Merged branch 'jetty-9.2.x' into 'jetty-9.3.x'.
diff --git a/.gitignore b/.gitignore
index 52260df..699c68b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,7 @@
 
 # merge tooling
 *.orig
+
+# test generated content
+*/src/test/*/WEB-INF/lib/test*.jar
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..3858e9b
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,54 @@
+Contributing to Jetty
+=====================
+Thanks for your interest in this project.
+
+Project description
+--------------------
+Jetty is a lightweight highly scalable java based web server and servlet engine.
+Our goal is to support web protocols like HTTP, HTTP/2, and WebSocket in a high
+volume low latency way that provides maximum performance while retaining the ease
+of use and compatibility with years of servlet development.
+Jetty is a modern fully async web server that has a long history as a component
+oriented technology easily embedded into applications while still offering a solid
+traditional distribution for webapp deployment.
+
+- [https://projects.eclipse.org/projects/rt.jetty](https://projects.eclipse.org/projects/rt.jetty)
+
+Developer resources
+--------------------
+Information regarding source code management, builds, coding standards, and more.
+
+- [https://www.eclipse.org/jetty/documentation/current/advanced-contributing.html](https://www.eclipse.org/jetty/documentation/current/advanced-contributing.html)
+
+The canonical Jetty git repository is located at GitHub.  Providing you have
+completed the contributors agreement mentioned below we will endeavor to pull
+your commit into Jetty proper.
+
+Contributor License Agreement
+------------------------------
+Before your contribution can be accepted by the project, you need to create and electronically sign the
+Eclipse Foundation [Contributor License Agreement](https://www.eclipse.org/legal/CLA.php) (CLA):
+
+1. Log in to the [Eclipse projects forge](https://projects.eclipse.org/user/login/sso). You will need to
+   create an account with the Eclipse Foundation if you have not already done so.
+2. Click on "Contributor License Agreement", and complete the form.
+
+Be sure to use the same email address in your Eclipse account that you intend to use when you commit to Git.
+
+Contact
+--------
+Contact the project developers via the project's "dev" list.
+
+- [https://dev.eclipse.org/mailman/listinfo/jetty-dev](https://dev.eclipse.org/mailman/listinfo/jetty-dev)
+
+Search for bugs
+----------------
+This project uses GitHub Issues to track ongoing development and issues.
+
+- [https://github.com/eclipse/jetty.project/issues](https://github.com/eclipse/jetty.project/issues)
+
+Create a new bug
+-----------------
+Be sure to search for existing bugs before you create another one. Remember that contributions are always welcome!
+
+- [https://github.com/eclipse/jetty.project/issues](https://github.com/eclipse/jetty.project/issues)
diff --git a/Jenkinsfile b/Jenkinsfile
index 9eb318b..b4c121d 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -3,11 +3,11 @@
 node {
   // System Dependent Locations
   def mvntool = tool name: 'maven3', type: 'hudson.tasks.Maven$MavenInstallation'
-  def jdktool = tool name: 'jdk7', type: 'hudson.model.JDK'
+  def jdktool = tool name: 'jdk8', type: 'hudson.model.JDK'
 
   // Environment
   List mvnEnv = ["PATH+MVN=${mvntool}/bin", "PATH+JDK=${jdktool}/bin", "JAVA_HOME=${jdktool}/", "MAVEN_HOME=${mvntool}"]
-  mvnEnv.add("MAVEN_OPTS=-Xms256m -Xmx1024m -XX:MaxPermSize=512m -Djava.awt.headless=true")
+  mvnEnv.add("MAVEN_OPTS=-Xms256m -Xmx1024m -Djava.awt.headless=true")
 
   try
   {
@@ -51,7 +51,7 @@
   {
     stage('Test') {
       withEnv(mvnEnv) {
-        timeout(time: 60, unit: 'MINUTES') {
+        timeout(time: 90, unit: 'MINUTES') {
           // Run test phase / ignore test failures
           sh "mvn -B install -Dmaven.test.failure.ignore=true"
           // Report failures in the jenkins UI
@@ -100,6 +100,20 @@
     notifyBuild("Test Failure")
     throw e
   }
+
+  try
+  {
+    stage 'Compact3'
+
+    dir("aggregates/jetty-all-compact3") {
+      withEnv(mvnEnv) {
+        sh "mvn -B -Pcompact3 clean install"
+      }
+    }
+  } catch(Exception e) {
+    notifyBuild("Compact3 Failure")
+    throw e
+  }
 }
 
 // True if this build is part of the "active" branches
@@ -146,8 +160,6 @@
     subject: summary,
     body: detail
   )
-
 }
 
-
 // vim: et:ts=2:sw=2:ft=groovy
diff --git a/KEYS.txt b/KEYS.txt
new file mode 100644
index 0000000..acf04a8
--- /dev/null
+++ b/KEYS.txt
@@ -0,0 +1,7 @@
+# GPG Release Key Fingerprints
+Jan Bartel      <janb@mortbay.com>          AED5 EE6C 45D0 FE8D 5D1B  164F 27DE D4BF 6216 DB8F
+Jesse McConnell <jesse.mcconnell@gmail.com> 2A68 4B57 436A 81FA 8706  B53C 61C3 351A 438A 3B7D
+Joakim Erdfelt  <joakim.erdfelt@gmail.com>  5989 BAF7 6217 B843 D66B  E55B 2D0E 1FB8 FE4B 68B4
+Joakim Erdfelt  <joakim@apache.org>         B59B 67FD 7904 9843 67F9  3180 0818 D9D6 8FB6 7BAC
+Joakim Erdfelt  <joakim@erdfelt.com>        BFBB 21C2 46D7 7768 3628  7A48 A04E 0C74 ABB3 5FEA
+Simone Bordet   <simone.bordet@gmail.com>   8B09 6546 B1A8 F026 56B1  5D3B 1677 D141 BCF3 584D
diff --git a/NOTICE.txt b/NOTICE.txt
index a0c9ce4..881d1c5 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -18,20 +18,54 @@
 
 Jetty may be distributed under either license.
 
+------
+Eclipse
+
+The following artifacts are EPL.
+ * org.eclipse.jetty.orbit:org.eclipse.jdt.core
+
+The following artifacts are EPL and ASL2.
+ * org.eclipse.jetty.orbit:javax.security.auth.message
+
+
+The following artifacts are EPL and CDDL 1.0.
+ * org.eclipse.jetty.orbit:javax.mail.glassfish
+
 
 ------
 Oracle
 
 The following artifacts are CDDL + GPLv2 with classpath exception.
-
 https://glassfish.dev.java.net/nonav/public/CDDL+GPL.html
 
-javax.servlet:javax.servlet-api
-javax.servlet.jsp:javax.servlet.jsp-api
-org.glassfish.web:javax.servlet.jsp
-org.glassfish.web:javax.servlet.jsp
-org.glassfish.web:javax.servlet.jsp.jstl
-org.eclipse.jetty.orbit.javax.servlet.jsp.jstl
+ * javax.servlet:javax.servlet-api
+ * javax.annotation:javax.annotation-api
+ * javax.transaction:javax.transaction-api
+ * javax.websocket:javax.websocket-api
+
+------
+Oracle OpenJDK
+
+If ALPN is used to negotiate HTTP/2 connections, then the following
+artifacts may be included in the distribution or downloaded when ALPN 
+module is selected. 
+
+ * java.sun.security.ssl
+
+These artifacts replace/modify OpenJDK classes.  The modififications
+are hosted at github and both modified and original are under GPL v2 with 
+classpath exceptions.
+http://openjdk.java.net/legal/gplv2+ce.html
+
+
+------
+OW2
+
+The following artifacts are licensed by the OW2 Foundation according to the
+terms of http://asm.ow2.org/license.html
+
+org.ow2.asm:asm-commons
+org.ow2.asm:asm
 
 
 ------
diff --git a/README.TXT b/README.TXT
deleted file mode 100644
index 9157b3e..0000000
--- a/README.TXT
+++ /dev/null
@@ -1,21 +0,0 @@
-This is a source checkout of the Jetty webserver.
-
-To build, use:
-
-  mvn clean install
-
-The jetty distribution will be built in
-
-  jetty-distribution/target/distribution
-
-The first build may take a long time as Maven downloads all the
-dependencies.
-
-The tests do a lot of stress testing, and on some machines it is
-necessary to set the file descriptor limit to greater than 2048
-for the tests to all pass successfully.
-
-Bypass tests by building with -Dmaven.test.skip=true but note
-that this will not produce some test jars that are leveraged
-in other places in the build.
-
diff --git a/README.md b/README.md
index 2e9bf5c..58208b8 100644
--- a/README.md
+++ b/README.md
@@ -2,23 +2,35 @@
 ============
 
 Jetty is a lightweight highly scalable java based web server and servlet engine.
-Our goal is to support web protocols like HTTP, SPDY and WebSocket in a high
-volume low latency way that provides maximum performance while retaining the ease
-of use and compatibility with years of servlet development. Jetty is a modern
-fully async web server that has a long history as a component oriented technology
-easily embedded into applications while still offering a solid traditional
-distribution for webapp deployment.
+Our goal is to support web protocols like HTTP, HTTP/2 and WebSocket in a high volume low latency way that provides maximum performance while retaining the ease of use and compatibility with years of servlet development. 
+Jetty is a modern fully async web server that has a long history as a component oriented technology easily embedded into applications while still offering a solid traditional distribution for webapp deployment.
 
 - [https://projects.eclipse.org/projects/rt.jetty](https://projects.eclipse.org/projects/rt.jetty)
 
 Documentation
 ============
 
-Project documentation is located on our Eclipse website.
+Project documentation is available on the Jetty Eclipse website.
 
 - [http://www.eclipse.org/jetty/documentation](http://www.eclipse.org/jetty/documentation)
 
+Building
+========
+
+To build, use:
+```shell
+  mvn clean install
+```
+
+The Jetty distribution will be built in `jetty-distribution/target/distribution`.
+
+The first build may take a longer than expected as Maven downloads all the dependencies.
+
+The build tests do a lot of stress testing, and on some machines it is necessary to set the file descriptor limit to greater than 2048 for the tests to all pass successfully.
+
+It is possible to bypass tests by building with `mvn -Dmaven.test.skip=true install` but note that this will not produce some of the test jars that are leveraged in other places in the build.
+
 Professional Services
 ============
 
-Expert advice and production support are available through [http://webtide.com](Webtide.com).
+Expert advice and production support are available through [Webtide.com](http://webtide.com).
diff --git a/VERSION.txt b/VERSION.txt
index b34e99c..654da00 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1,4 +1,120 @@
-jetty-9.2.22-SNAPSHOT
+jetty-9.3.19-SNAPSHOT
+
+jetty-9.3.18.v20170406 - 06 April 2017
+ + 877 Programmatic servlet mappings cannot override mappings from
+   webdefault.xml using quickstart
+ + 1201 X-Forwarded-For incorrectly set in jetty-http-forwarded.xml
+ + 1417 Improve classloader dumping
+ + 1439 Allow UNC paths to function as Resource bases
+
+jetty-9.3.17.v20170317 - 17 March 2017
+ + 329 Javadoc for HttpTester and ServletTester needs to reference limited HTTP
+   version scope
+ + 609 websocket ClientCloseTest testServerNoCloseHandshake is failing
+ + 1015 Ensure jetty-distribution excludes git / temp files
+ + 1047 ReadPendingException and then thread death
+ + 1049 test-jetty-osgi test exits/crashes the surefire forked JVM
+ + 1282 ByteArrayEndPointTest.testIdle() failure
+ + 1296 Introduce HTTP parser "content complete" event
+ + 1326 Jetty shutdown command got NullPointerException (http2 module added to
+   start)
+ + 1328 Response.setBufferSize(int) ISE should be more clear on reason
+ + 1340 PushCacheFilter question
+ + 1342 Improve ByteBufferPool scalability
+ + 1351 StringIndexOutOfBoundsException thrown on incomplete Accept-Language
+   header
+ + 1357 RolloverFileOutputStream: No rollout performed at midnight
+ + 1374 When `Server.start` fails, beans ought to be stopped
+ + 1375 Support pushed resources in HTTP client
+ + 1378 Slow TLS clients may hang the server
+ + 1383 javadoc build on JDK 8u121 fails due to scripts
+ + 1384 Expose StatisticsServlet to webapp
+ + 1387 Windows and paxexam failure due to "renaming bundle"
+ + 1389 Update to gcloud datastore-0.9.4-beta
+ + 1390 HashLoginService and "this.web-inf.url" property are incompatible
+ + 1394 Default OS Locale/Encoding/Charset can cause test failures
+ + 1396 Set-Cookie produced by Jetty is invalid for RFC6265 and Chrome
+ + 1399 SlowClientTest is failing on CI
+ + 1401 HttpOutput.recycle() does not clear the write listener
+
+jetty-9.3.16.v20170120 - 20 January 2017
+ + 486 JDK 9 ALPN implementation
+ + 592 Support no-value Host header in HttpParser
+ + 612 Support HTTP Trailer
+ + 1073 JDK9 support in Jetty 9.3.x
+ + 1195 Problem using STOP.PORT and STOP.KEY with --exec
+ + 1197 WebSocketClient not sending `Authorization` request header
+ + 1200 Context path not set for symlink from root
+ + 1202 NPE in JsrSession when dealing with a response missing the
+   `Sec-WebSocket-Extensions` header
+ + 1228 Internal error during SSL handshake
+ + 1229 ClassLoader constraint issue when using NativeWebSocketConfiguration
+   with WEB-INF/lib/jetty-http.jar present
+ + 1234 onBadMessage called from with handled message
+ + 1259 HostnameVerificationTest.simpleGetWithHostnameVerificationEnabledTest
+   is broken
+ + 1261 Intermittent H2C test failure AsyncIOServletTest.testAsyncReadEarlyEOF
+ + 1262 BufferUtil.isMappedBuffer() uses reflection on private JDK fields
+ + 1265 JAXB not available in JDK 9
+ + 1267 Request.getRemoteUser can throw undeclared IllegalStateException via
+   DeferredAuthentication & FormAuthenticator
+ + 1268 <jsp-file>incorrectly handled when the jsp is at the top directory
+ + 1269 Extensible assumed charset for mimetypes
+ + 1270 GzipHandler rework on dispatches
+ + 1272 Update ALPN versions for 8u111
+ + 1274 Distinguish no tlds vs no MetaInfConfiguration tld scanning for
+   quickstart
+ + 1275 Get rid of Mockito
+ + 1276 Remove org.eclipse.jetty.websocket.server.WebSocketServerFactory from
+   SPI
+ + 1277 http2 alpn test error
+
+jetty-9.3.15.v20161220 - 20 December 2016
+ + 240 Missing content for multipart request after upgrade to Jetty > 9.2.7
+ + 905 Jetty terminates SSL connections too early with Connection: close
+ + 1020 Java Util Logging properties in wrong location
+ + 1050 Add multiple FilterHolder to a ServletContextHandler may cause problems
+ + 1051 NCSARequestLog/RolloverFileOutputStream does not roll day after DST
+   ends
+ + 1054 Using WebSocketPingPongListener with empty PING payload results in
+   NullPointerException
+ + 1057 Improve WebSocketUpgradeFilter fast path performance
+ + 1062 Jetty allows requests to hang under PUT load
+ + 1063 HostPortHttpField should handle port-only values
+ + 1064 HttpClient sets chunked transfer-encoding
+ + 1065 Response.setBufferSize checks for written content
+ + 1069 Host header should be sent with HTTP/1.0
+ + 1072 InetAccessHandler needs InetAddress & Path based restrictions like
+   IPAccessHandler did
+ + 1078 DigestAuthentication should use realm from server, even if unknown in
+   advance
+ + 1081 DigestAuthenticator does not check the realm sent by the client
+ + 1090 Allow WebSocketUpgradeFilter to be used by WEB-INF/web.xml
+ + 1092 jetty-runner jstl support
+ + 1098 MimeTypes.getCharsetFromContentType() unable parse "application/pdf;;;
+   charset=UTF-8"
+ + 1099 PushCacheFilter pushes POST requests
+ + 1108 Please improve logging in SslContextFactory when there are no approved
+   cipher suites
+ + 1114 Add testcase for WSUF for stop/start of the Server
+ + 1118 Filter.destroy() conflicts with ContainerLifeCycle.destroy() in
+   WebSocketUpgradeFilter
+ + 1123 Broken lifecycle for WebSocket's mappings
+ + 1124 Allow configuration of WebSocket mappings from Spring
+ + 1130 PROXY protocol support reports incorrect remote address
+ + 1134 Jetty HTTP/2 client problems
+ + 1135 Avoid allocations from Method.getParameterTypes() if possible
+ + 1146 jetty.server.HttpInput deadlock
+ + 1161 HttpClient and WebSocketClient should not remove all cookies on stop
+ + 1169 HTTP/2 reset on a stalled write does not unblock writer thread
+ + 1171 jetty-client throws NPE for request to IDN hosts only when
+   `HttpClient#send(...)` is called
+ + 1175 Reading HttpServletRequest InputStream from a Filter then accessing
+   getParameterNames() results in java.io.IOException: Missing content for
+   multipart request
+ + 1181 Review buffer underflow cases in SslConnection
+ + 1186 Where can i find SocketConnector .java and
+   BlockingChannelConnector.java etc?
 
 jetty-9.2.21.v20170120 - 20 January 2017
  + 592 Support no-value Host header in HttpParser
@@ -7,16 +123,147 @@
  + 1267 Request.getRemoteUser can throw undeclared IllegalStateException via
    DeferredAuthentication & FormAuthenticator
 
-jetty-9.2.20.v20161216 - 16 December 2016
+jetty-9.3.14.v20161028 - 28 October 2016
+ + 292 NPE in SslConnectionFactory newConnection
  + 295 Ensure Jetty Client use of Deflater / Inflater calls .end() to avoid
    memory leak
- + Reset Response buffer size
- + 1051 NCSARequestLog/RolloverFileOutputStream does not roll day after DST
-   ends
- + 1057 Improve WebSocketUpgradeFilter fast path performance
- + 1090 Allow WebSocketUpgradeFilter to be used by WEB-INF/web.xml
- + 1124 Allow configuration of WebSocket mappings from Spring
- + 1130 PROXY protocol support reports incorrect remote address
+ + 989 InputStreamResponseListener.get() throws with HTTP/2 following redirect
+ + 1009 9.3.x] ThreadLimitHandler has no method setBlockForMs
+ + 1018 Remove dependency on asm types in oej.annotations.Util
+ + 1029 Restore Request.setHttpVersion()
+ + 1031 Improve HttpField pre-encoding
+ + 1032 Remove jetty dependencies in jetty jasper classes
+ + 1037 Don't execute AsyncListener.onTimeout events in spare Scheduler-Thread
+ + 1038 AttributeNormalizer does not favor ${WAR} over other attributes, like
+   ${jetty.base}
+ + 1039 AttributeNormalizer should not track attributes that are null
+ + 1046 Improve HTTP2Flusher error report
+ + 480764 Add extra tests for empty multipart
+
+jetty-9.3.13.v20161014 - 14 October 2016
+ + 295 Ensure Jetty Client use of Deflater / Inflater calls .end() to avoid
+   memory leak
+ + 926 No LSB Tags on jetty.sh script cause warning on Ubuntu 16.04
+ + 999 Create a Flight Recorder module
+ + 1000 Allow legacy behaviour if 2 servlets map to same path
+
+jetty-9.3.13.M0 - 30 September 2016
+ + 277 Proxy servlet does not handle HTTP status 100 correctly
+ + 870 TLS protocol exclusion broken for SslContextFactory(String)
+ + 915 The jetty-maven-plugin:stop goal doesn't stop everything completely
+ + 918 Support certificates hot reload
+ + 930 Add module instructions to SSL section
+ + 943 Docs: Error in 'Embedding Jetty' page - example 'FileServer'
+ + 948 9.4.0.RC0 jetty-distribution invalid config etc/jetty-http2c.xml
+ + 955 Response listeners not invoked when using Connection.send()
+ + 959 CompleteListener invoked twice for HTTP/2 transport and response content
+ + 960 Async I/O spin when reading early EOF
+ + 965 Link from High Load docs to Garbage Collection Tuning is broken
+ + 966 Remove usages of ConcurrentArrayQueue
+
+jetty-9.3.12.v20160915 - 15 September 2016
+ + 56 Fix authn issues in LdapLoginModule
+ + 131 Improve Connector Statistic names and values
+ + 185 Implement RFC 7239 (Forwarded header)
+ + 700 Bundle org.eclipse.jetty.http.spi not available via p2 repository
+ + 725 Provide a private way to report security issues
+ + 752 Implement support for HTTP2 SETTINGS_MAX_HEADER_LIST_SIZE
+ + 759 Ensure wrapped Responses will close and commit outputstream or writer
+ + 780 The moved websocket PathSpec is incompatible with cometd 3.0.x
+ + 783 Report name of broken jar file
+ + 784 JSP Session updated before sendRedirect() lose their information
+ + 786 Buffering Response Handler
+ + 790 AsyncContentListener semantic broken with HTTP/2 transport
+ + 792 HTTP/2] Socket seems to be not closed completely
+ + 797 MimeTypes resource loading incorrect on OSGi
+ + 798 async IO Write closed race
+ + 804 setting default Url Encoding broken in Jetty >= 9.3
+ + 806 Jetty HttpClient authentication - missing any realm option
+ + 817 NPE in jndi Resource
+ + 826 Better default for HTTP/2's max concurrent streams
+ + 827 HTTPClient fails connecting to HTTPS host through an HTTP proxy
+   w/authentication
+ + 830 Test webapp not properly copied to demo-base
+ + 832 ServerWithJNDI example uses wrong webapp
+ + 841 support reset in buffering interceptors
+ + 844 Implement a Thread Limit Handler
+ + 845 Improve blocking IO for data rate limiting
+ + 851 MBeanContainer no longer unregisters MBeans when "stopped"
+ + 854 If container.destroy() is called, calling container.start() again should
+   throw an IllegalStateException
+ + 855 JMXify MBeanContainer
+ + 860 Only TLS 1.2 Supported
+ + 868 ClassLoader leak with Jetty and Karaf - static instances of
+   java.lang.Throwable
+ + 880 Refactor jetty-http's HostPortHttpField logic into new jetty-util class
+ + 882 Add IPv6 support to IPAddressMap in jetty-util
+ + 889 ConstantThrowable.name can be removed
+ + 894 When adding servless class, preserve Class instead of going through
+   String
+ + 897 Remove GzipHandler interceptor when out of scope
+ + 898 GzipHandler adds multiple Vary header
+ + 902 Expect: 100-Continue does not work with HTTP/2
+ + 909 Path and Domain not properly matched in addCookie()
+ + 911 Request.getRequestURI() gets decoded after startAsync(req, resp) is
+   invoked
+ + 913 Unprotected debug in WebAppClassLoader
+ + 922 Implements methods Connection.getBytes[In|Out]()
+
+jetty-9.3.11.v20160721 - 21 July 2016
+ + 230 customize Content-Type in ErrorHandler's default error page
+ + 592 Support no-value Host header in HttpParser
+ + 631 SLOTH protection
+ + 643 NPE in passing websocket client test
+ + 649 LDAPLoginModule should disallow blank username and password
+ + 658 Add memcached option for gcloud-sessions in jetty-9.3
+ + 660 NullPointerException in Request.getParameter: _parameters is null
+ + 663 Update gcloud datastore to 0.2.3
+ + 667 Introduce optional `jetty.deploy.monitoredPath` for jetty-deploy paths
+   outside of ${jetty.base}
+ + 668 Introduce optional `jetty.deploy.defaultsDescriptorPath` for
+   jetty-deploy defaults descriptor outside of ${jetty.home}
+ + 669 Support UNC paths in PathResource
+ + 671 Incorrect ALPN default protocol
+ + 672 Allow logging configuration announcement to be programmatically disabled
+ + 673 ClasspathPattern needs a match all pattern
+ + 675 Slf4jLog.ignore() should produce at DEBUG level
+ + 676 JavaUtilLog.ignore() should produce at DEBUG level
+ + 677 Logging of .ignore() should indicate that it was an "Ignored Exception"
+ + 678 Log at less than DEBUG level when annotation scanning takes significant
+   time
+ + 682 Quickstart should not scan all container path jars
+ + 684 HttpClient proxies (HttpProxy and Socks4Proxy) do not support
+   authentication
+ + 685 SecureRequestCustomizer SSLSession attribute not set
+ + 687 AllowSymLinkAliasChecker not normalizing relative symlinks properly
+ + 690 jetty-maven-plugin does not configure AnnotationConfiguration for
+   jetty:effective-web-xml goal
+ + 693 QoSFilterTest failures are not capture by junit
+ + 694 http2.client.StreamResetTest.testServerExceptionConsumesQueuedData stack
+   not suppressed in test
+ + 695 Deprecate LocalConnector.getResponses() in favor of using .getResponse()
+ + 696 LocalConnector.getResponse() doesn't find close if using HTTP/1.1
+   w/Connection: close
+ + 701 Document CachingWebAppClassLoader
+ + 706 org.apache.jasper.compiler.disablejsr199 is no longer present in Jetty
+   9.3+
+ + 708 SslContextFactory: newSslServerSocket/newSslSocket customization
+ + 717 GzipHandler.minGzipSize still compresses small responses
+ + 718 Document HttpClient transports
+ + 720 asciiToLowerCase throws NullPointerException
+ + 721 HTTP Response header value encoding is invalid for RFC7230
+ + 723 Improve bad/missing mime.properties reporting
+ + 726 Http2 Client parse error
+ + 730 "Slow" client causes IllegalStateException
+ + 733 Allow setCharacterEncoding after getOutputStream
+ + 739 Illegal WindowUpdate frame with delta=0
+ + 742 Fixed link to webtide.com
+ + 745 Removed README.txt
+ + 747 Update documentation to reflect TLS and SSL support
+ + 751 Remove usages of ArrayQueue
+ + 752 Implement support for HTTP2 SETTINGS_MAX_HEADER_LIST_SIZE
+ + 755 NPE in HttpChannelOverHTTP2.requestContent()
+ + 756 Filter problematic headers from CGI and FastCGIProxy
 
 jetty-9.2.19.v20160908 - 08 September 2016
  + 817 NPE in jndi Resource
@@ -39,6 +286,79 @@
  + 661 JsrExtension is missing hashCode() and equals()
  + 756 Filter problematic headers from CGI and FastCGIProxy
 
+jetty-9.3.11.M0 - 22 June 2016
+ + 425 Incorrect @ServerEndpoint Encoder/Decoder lifecycle
+ + 624 AsyncContext.onCompleted called twice
+ + 645 jetty-requestlog.xml default log path
+ + 654 Jetty 9.3 ServletContext.getResourceAsStream("/") returns an unusable
+   stream
+ + 659 CONNECT request fails spuriously
+ + 660 NullPointerException in Request.getParameter: _parameters is null
+ + 661 JsrExtension is missing hashCode() and equals()
+
+jetty-9.3.10.v20160621 - 21 June 2016
+ + 388 Add methods to send text frames with pre-encoded strings
+ + 605 Guard concurrent calls to WebSocketSession.close()
+ + 608 reset encoding set from content type?
+ + 609 websocket ClientCloseTest testServerNoCloseHandshake is failing
+ + 610 HttpClientRedirectTest/testRedirectWithWrongScheme test failing in CI
+ + 620 Missing call to setPattern in RewritePatternRule constructor
+ + 622 NoSqlSessionManager test for expired session does not use
+   session.maxInactiveInterval
+ + 623 Add --gzip suffix to 304 responses with ETAGs
+ + 624 AsyncContext.onCompleted called twice
+ + 628 IOException: Unable to open root Jar file
+   MetaInfConfiguration.getTlds(MetaInfConfiguration.java:406) with Spring boot
+   loader + WebAppContext + non-expanded war
+ + 632 JMX tests rely on fixed port
+ + 633 If jmx and websocket is enabled, redploying a context produces a
+   NullPointerException
+ + 638 ConnectHandler responses should have Content-Length
+ + 639 ServerContainer stores WebSocket sessions twice
+ + 640 ClientContainer should store WebSocket sessions as beans
+ + 641 MongoSessionIdManager uses deprecated ensureIndex
+ + 647 HTTP/2 CONTINUATION frame parsing throws IllegalStateException
+ + 648 Problem using InputStreamResponseListener to handle HTTP/2 responses
+
+jetty-9.3.10.M0 - 26 May 2016
+ + 354 Spin loop in case of exception thrown during accept()
+ + 464 Improve reporting of SSLHandshakeException
+ + 542 Support Connection.Listener bean on clients
+ + 574 Introduce a TLS handshake completed listener
+ + 581 Initial session recv window setting not working
+ + 85 Expose TLS protocol used for connection in SecureRequestCustomizer
+
+jetty-9.3.9.v20160517 - 17 May 2016
+ + 436 Migrate Jetty Documentation
+ + 437 updates to NPE prevention
+ + 501 clear continuation initial on undispatch
+ + 510 Module [depend] property expansion should support eg
+   foo/${bar}/${bar}-xxx
+ + 514 Allow ExecutionStrategy to be configurable
+ + 518 jarfile fix for springboot
+ + 519 Disable SSL session caching
+ + 521 Separate usage of the Server and the ServerConnector Executors
+ + 525 Spin in HttpInputOverHttp.blockForContent with malformed HTTP-Request
+ + 526 Headers set from RequestDispatcher.include() not showing up in response
+ + 529 Start property for non standard JRE versions
+ + 533 Do not hide file resource exception
+ + 534 Deadlock in MongoSessionManager
+ + 546 Guard concurrent calls to ExecutionStrategy.execute()
+ + 547 ExecuteProduceConsume (EWYK) does not exit low threads mode
+ + 552 Improve HTTP/2 idle timeout handling
+ + 553 Abort HttpChannel if response has wrong content-length
+ + 556 Improve Resource.getAlias() checks on Windows
+ + 557 Review ThreadPool.isLowOnThreads()
+ + 558 HTTP/2 server hangs when thread pool is low on threads
+ + 560 Jetty Client Proxy Authentication does not work with HTTP Proxy
+   tunneling
+ + 561 Fixed test timer
+ + 567 NPE in ErrorPageErrorHandler debug
+ + 570 URIUtil.encodePath does not always encode utf8 chars
+ + 571 AbstractAuthentication.matchesURI() fails to match scheme
+ + 572 Don't reject HTTP/2 requests without body in low threads mode
+ + 486530 Handler added to WebAppContext prevents ServletContext initialization
+
 jetty-9.2.17.v20160517 - 17 May 2016
  + 560 Jetty Client Proxy Authentication does not work with HTTP Proxy
    tunneling
@@ -64,6 +384,123 @@
  + 510 Module [depend] property expansion should support eg
    foo/${bar}/${bar}-xxx
 
+jetty-9.3.9.M1 - 11 April 2016
+ + 481 Event response.success notified without waiting for content callback for
+   HTTP/2 transport
+ + 490 serverClasses set from jetty-web.xml
+ + 491 Do not assume gzip acceptable for HTTP/2
+ + 503 Wrong request-per-connection counting in MultiplexHttpDestination in
+   case of failures
+ + 504 HTTP/2 client transport cannot send request after idle timeout
+   jetty-9.3.9.M0 - 05 April 2016
+ + 184 Empty Realm for BasicAuthentication
+ + 371 update apache jsp to 8.0.27
+ + 418 Add osgi capability for endpoint configurator
+ + 424 Jetty impl. of Websocket ServerEndpointConfig.Configurator lifecycle out
+   of spec
+ + 427 Squelch intentional exceptions seen during websocket testing
+ + 434 RequestTest stack traces
+ + 435 adjust debug log message
+ + 437 Avoid NPE on receiving empty message though MessageHandler.Partial
+ + 438 File and Path Resources with control characters should be rejected
+ + 446 jetty-quickstart path normalization uses improper paths on Windows
+ + 448 RFC2616 Compliance Mode should track and report RFC7230 violations
+ + 450 Client AuthenticationProtocolHandler sends request failures to response
+   failure listener
+ + 451 RFC2616 Compliance mode should support empty headers
+ + 453 Change logging of setting session maxInactiveInterval to DEBUG from WARN
+ + 454 DoSFilter does not send an error status code when closing a connection
+   because of timeout
+ + 458 Improve Quality list handling
+ + 467 Compact // rule
+ + 469 Update to Apache Jasper 8.0.33
+ + 470 AsyncContextState NPE if called after reset
+ + 472 Use LongAdder for statistics
+ + 476 HttpClient should not send absolute-form target with non HttpProxy
+
+jetty-9.3.8.v20160314 - 14 March 2016
+ + 107 ResourceHandler range support testcase
+ + 124 Don't produce text/html if the request doesn't accept it
+ + 247 improving invalid buffer manipulation exception messages
+ + 258 Http request to origin server over https proxy contains absolute URL
+ + 266 jetty-client redirection process is aborted if redirect response have
+   corrupt body
+ + 305 NPE when notifying the session listener if the channel is closed before
+   a session has been opened
+ + 316 Add *.chm mimetype mapping
+ + 343 ensure release deployment of test-jetty-webapp:war and
+   test-proxy-webapp:war
+ + 346 HttpParser RFC2616 Compliance mode
+ + 353 Jetty Client doesn't forward authentication headers with redirects when
+   using proxy
+ + 356 Element error-page/location must start with a '/'
+ + 362 Very slow page load and missing resources when using HTTP/2 with Jetty
+   9.3.7
+ + 365 Potential connection leakage in case of aborted request
+ + 366 Avoid HTTP2Flusher reentrancy
+ + 367 Resolve remaining git.eclipse.org build references
+ + 372 Data race in HttpReceiverOverHTTP2
+ + 377 HttpClient - No supported cipher suites leads to stuck requests
+ + 378 Can't configure per nodes settings in start.ini
+ + 379 Insufficient information on asyncNotSupported
+ + 381 HttpClient does not send the Authorization header with authenticating
+   proxy
+ + 386 Explicit Authorization header is dropped when handling 407s
+ + 397 Multipart EOF handling
+ + 402 Don't use Thread.isAlive() in ShutdownMonitor
+ + 405 adding testcase for problematic HttpURI parsing of path params
+ + 406 GzipHandler: allow to override the Vary response header
+ + 407 JSR356 Server WebSocket Sessions no longer being tracked
+ + 408 Http client does not work on https with proxy
+ + 411 Add more debug log for mongosessionmanager and remove debug printlns
+ + 413 HotSwapHandler null handlers
+ + 416 Support HTTPS forward proxies
+ + 417 HttpClient: review support for OPTIONS *
+ + 423 Duplicate Content-Length header not handled correctly
+
+jetty-9.3.8.RC0 - 25 February 2016
+ + 81 Exception not always thrown in Jetty to application when upload part is
+   too big
+ + 82 Request.getPart() that results in Exception still allows other parts to
+   be fetched
+ + 251 Removing SSLEngine.beginHandshake() calls
+ + 285 PathContentProvider - Use of Direct buffers without pooling
+ + 298 qtp threads spin-locked in MBeanContainer.beanAdded
+ + 342 Reintroducing Response parameter to logExtended
+ + 344 init script does not properly display status of a non running service
+ + 346 HttpParser RFC2616 Compliance mode
+ + 347 Avoid sending request using a connection that is idle timing out
+ + 352 Integrate session idling for MongoSessionManager
+ + 354 Spin loop in case of exception thrown during accept()
+ + 355 Improve close behavior for failed pending writes
+ + 478918 Change javax.servlet.error,forward,include literals to
+   RequestDispatcher constants
+ + 484446 InputStreamResponseListener's InputStream uses default read (3) and
+   blocks early on never-ending response.
+ + 485306 HttpParser (HttpURI) mistaking basic auth password as a port number
+ + 485469 permessage-deflate extension causes protocol error in Firefox/Chrome
+ + 486394 Restore MultiPartFilter behavior with regards to temp file access
+ + 486497 NPE in MappedLoginService
+ + 486511 Server.getURI() returns wrong scheme on SSL/HTTPS
+ + 486530 Handler added to WebAppContext prevents ServletContext initialization
+ + 486589 HttpRequest has a wrong HTTP Version in HTTP/2
+ + 486604 Add debug logging of ErrorPageErrorHandler logic
+ + 486674 Quickstart path attribute normalization should be based on longest
+   path match
+ + 486829 Cancel stream error after a failed request with the HTTP/2.0 client
+ + 486877 Google Chrome flagging 'obsolete cipher suite' in Jetty and will soon
+   issue broken padlock
+ + 486930 Selector does not correctly handle rejected execution exception
+ + 487158 Switched SCM URIs to github
+ + 487197 Deflater/Inflater memory leak with WebSocket permessage-deflate
+   extension
+ + 487198 ContextScopeListener should be called on context start and stop
+ + 487277 Introduce http-forwarded module for X-Forwarded support
+ + 487354 Aborted request or response does not send RST_STREAM frame
+ + 487511 Jetty HTTP won't work on turkish systems
+ + 487714 Avoid NPE in close race for async write
+ + 487750 HTTP/2 push must not be recursive
+
 jetty-9.2.15.v20160210 - 10 February 2016
  + 482042 New API, Allow customization of ServletHandler path mapping
  + 482243 Fixed GzipHandler for Include
@@ -83,6 +520,101 @@
    exception.
  + 487511 Jetty HTTP won't work on turkish systems
 
+jetty-9.3.7.RC1 - 13 January 2016
+ + 481986 Dead JSR 356 Server Session still being tracked after
+   Session/Connection closure
+ + 484616 Outdated version of javaee_web_services_client_1_2.xsd
+ + 485031 two PathWatcher threads running after automatically restarting webapp
+ + 485063 After stopping JettyWebAppContext, it still contains reference to old
+   WebAppClassLoader via ServerContainer bean
+ + 485064 HashSessionManager leaks ScheduledExecutorScheduler with reference to
+   un-deployed webapp
+ + 485376 Multiple charset attributes in Content-Type
+ + 485535 jetty.sh results in FAILED when running service restart
+ + 485663 NullPointerException in WebSocketSession during upgrade with DEBUG
+   logging
+ + 485712 Quickstart web.xml is absolute
+
+jetty-9.3.7.RC0 - 05 January 2016
+ + 458745 Async ISE in async Echo
+ + 481567 permessage-deflate causing data-dependent ju.zip.DataFormatException:
+   invalid stored block lengths
+ + 482173 Track original Query string in Rewrite RuleContainer too
+ + 482243 Fixed GzipHandler for Include
+ + 482270 Expose upgrade request locales
+ + 482272 Fixed relative symlink checking
+ + 482506 HTTP/2 load test with h2load fails
+ + 482670 HttpURI wrongly parser URI paths starting with /@
+ + 482855 Content-Length omitted for POST requests with empty body
+ + 482959 Local stream count never decrements when closing a stream causing
+   IllegalStateException.
+ + 483009 MultiPartContentProvider may send wrong Content-Length
+ + 483039 HTTP2 Upgrade case sensitivity on Connection header
+ + 483344 text/csv Mime Type For CSV in mime properties File
+ + 483413 Warn on @Deprecated servlet/filter use
+ + 483422 Empty chunked body in 304 Response
+ + 483620 Servlet annotation mapping to "/" should override webdefault.xml
+   mapping
+ + 483857 jetty-client onComplete isn't called in case of exception in
+   GZIPContentDecoder.
+ + 483878 Parallel requests stuck via the http client transport over HTTP/2
+ + 484167 GOAWAY frames aren't handling disconnects appropriately on Client
+ + 484210 HttpClient over HTTP/2 should honor maxConcurrentStreams
+ + 484262 Race condition between GOAWAY disconnect and ability to make new
+   request.
+ + 484349 Promote WebSocket PathMappings / PathSpec to Jetty Http
+ + 484350 Allow GzipHandler path include/exclude to use regex
+ + 484397 Unavoidable NullPointerException in onMessage-Handler for
+   PongMessages
+ + 484440 Swap WebSocket PathMappings for new jetty-http PathMappings
+ + 484585 Avoid sending request using a connection that is idle timing out
+ + 484603 HashLoginService does not stop its PropertyUserStore
+ + 484612 Restore WebSocket Session.close() sending 1000/Normal status code
+ + 484621 Client hangs till timeout when Authentication.authenticate() throws
+   exception.
+ + 484622 Improve handling of Direct and Mapped buffers for static content
+ + 484624 Disable CachingWebAppClassLoader
+ + 484657 Support HSTS rfc6797
+ + 484683 FastCGI request idle timeout is handled incorrectly
+ + 484718 Review idle timeout handling
+ + 484801 Avoid non-cached memory mapped files
+ + 484818 Expose interesting HTTP/2 attributes and operations via JMX
+ + 484822 Jetty ThreadMonitor memory leak
+ + 484861 Improve FlowControlStrategy stall handling
+ + 484876 Make simpler to customize the FlowControlStrategy
+ + 484878 Make BufferingFlowControlStrategy.bufferRatio configurable via JMX
+
+jetty-9.3.6.v20151106 - 06 November 2015
+ + 419966 Add ContentProvider that submits multipart/form-data
+ + 472675 No main manifest attribute, in jetty-runner regression
+ + 476641 Proxy rewriteTarget() null return does not call error handler
+ + 478757 DebugHandler thread name is mangled
+ + 479179 Fixed NPE from debug
+ + 479378 Incorrect REQUEST_URI
+ + 479712 Documented --approve-all-licenses
+ + 479832 Use system properties for gcloud config for GCloudDatastore session
+   manager
+ + 479839 Regression when starting application with excessive scan times
+ + 479865 IllegalStateException: Multiple servlets map to path: *.jsp: jsp,jsp
+ + 480061 HTTP/2 server doesn't send GOAWAY frame when shutting down
+ + 480162 Continuations behavior differences due to HttpURI behavior
+ + 480260 HPack decode error for buffers with offset
+ + 480272 Update to newer jdt ecj version
+ + 480452 Large downloads via FastCGI proxy keep HttpClient connections active
+ + 480764 Error parsing empty multipart
+ + 481006 SSL requests intermittently fail with EOFException when SSL
+   renegotiation is disallowed.
+ + 481203 Add ability to set configurations to apply to WebAppContext for
+   jetty-maven-plugin
+ + 481225 Secondary resources with query parameters are not properly pushed
+ + 481236 Make ShutdownMonitor java security manager friendly
+ + 481355 Nested Symlinks
+ + 481373 Corner cases where session may remain in JDBCSessionManager memory
+ + 481385 Incorrect parsing of END_REQUEST frames
+ + 481418 ResourceHandler sets last modified
+ + 481437 Port ConnectHandler connect and context functionality from Jetty 8
+ + 481554 DispatcherType reset race
+
 jetty-9.2.14.v20151106 - 06 November 2015
  + 428474 Expose batch mode in the Jetty WebSocket API
  + 471055 Restore legacy/experimental WebSocket extensions (deflate-frame)
@@ -103,6 +635,115 @@
  + 481236 Make ShutdownMonitor java security manager friendly
  + 481437 Port ConnectHandler connect and context functionality from Jetty 8
 
+jetty-9.3.5.v20151012 - 12 October 2015
+ + 479343 calls to MetaData#orderFragments() with relative ordering adds
+   duplicate jars
+ + 479537 Server preface sent after client preface reply
+ + 479584 WS Session does not contain UpgradeRequest information in
+   WebSocketAdapter.onWebSocketConnect callback
+
+jetty-9.3.4.v20151007 - 07 October 2015
+ + 428474 Expose batch mode in the Jetty WebSocket API
+ + 472082 isOpen returns true on CLOSING Connection
+ + 474936 WebSocketSessions are not always cleaned out from openSessions
+ + 475209 WebSocketServerFactory should not hand null object to
+   DecoratedObjectFactory
+ + 476023 Incorrect trimming of WebSocket close reason
+ + 476049 When using WebSocket Session.close() there should be no status code
+   or reason sent
+ + 476170 Support servers that close connections without sending Connection:
+   close header.
+ + 476720 getTrustStoreResource fixed
+ + 477087 Enforce that the preface contains a SETTINGS frame
+ + 477123 AsyncListener callbacks need context scope
+ + 477270 Add ability to send a single PRIORITY frame
+ + 477278 Refactored DefaultServlet for cached Gzip & Etags
+ + 477385 Make jetty osgi manifests only resolve jetty packages against a
+   single distro version
+ + 477641 ALPN classes exposed to webapps - fixed typo
+ + 477680 Encode merged query parameters
+ + 477737 Improve handling of etags with dynamic and static gzip
+ + 477757 Null args in TypeUtil .call & .construct result in confusing
+   exceptions
+ + 477817 Fixed memory leak in QueuedThreadPool
+ + 477878 HttpClient over HTTP/2 doesn't close upload stream
+ + 477885 Jetty HTTP2 client fails to connect with Netty server - HTTP2 client
+   preface missing or corrupt.
+ + 477890 Overwhelmed HTTP/2 server discards data
+ + 477895 Prevent leak of handles to deleted files after redeploy
+ + 477900 Increase client authentication default max content size
+ + 478008 Do not reset current value of CounterStatistics
+ + 478021 Client sending Connection: close does not shutdown output
+ + 478105 prependFilterMapping check for null FilterHolder
+ + 478239 Remove pointless synchronize in infinispan scavenging
+ + 478247 WebappClassLoader pinned after redeploy
+ + 478275 Priority information in HEADERS frame is not sent
+ + 478280 property file in temp directory
+ + 478372 JavaUtilLog setSourceClass and setSourceMethod
+ + 478434 Priority weights should be between 1 and 256 inclusive
+ + 478752 Clarify support for HttpServletRequest.upgrade()
+ + 478757 DebugHandler thread name is mangled
+ + 478829 WebsocketSession not cleaned up / memory leak
+ + 478862 Update to jstl 1.2.5
+ + 478923 threads stuck at SharedBlockingCallback$Blocker.block
+ + 479026 Wrong CONNECT request idle timeout
+ + 479277 HttpClient with HTTP/2 transport does not work for "https" URLs
+
+jetty-9.3.3.v20150827 - 27 August 2015
+ + 470311 Introduce a proxy-protocol module
+ + 471055 Restore legacy/experimental WebSocket extensions (deflate-frame)
+ + 472411 PathResource.checkAliasPath() typo
+ + 473321 Overriding SSL context KeyStoreType requires explicit override of
+   TrustStoreType
+ + 474025 SslContextFactory does not work with JCEKS Keystore
+ + 474068 Update WebSocket Extension for permessage-deflate draft-22
+ + 474319 Reintroduce blocking connect()
+ + 474321 Allow synchronous address resolution
+ + 474344 apache-jstl includes test dependencies
+ + 474358 DefaultServlet bad Content-Type on compressed content
+ + 474361 Handle JVM version extensions like -internal
+ + 474453 Tiny buffers (under 7 bytes) fail to compress in permessage-deflate
+ + 474454 Backport permessage-deflate from Jetty 9.3.x to 9.2.x
+ + 474455 Enable permessage-deflate WebSocket extension
+ + 474558 Debug log ServletContainerInitializer @HandlesTypes contents
+ + 474617 AsyncListener.onError not called for errors
+ + 474618 AsyncListener.onComplete not called when error occurs
+ + 474634 AsyncListener.onError() handling
+ + 474685 GzipHandler configuration supports csv paths and mimetypes
+ + 474888 HttpClient JMX support
+ + 474936 WebSocketSessions are not always cleaned out from openSessions
+ + 474961 Close input stream for classes in AnnotationParser after scanning
+ + 475195 SNI matching fails when keystore does not contain wild certificates
+ + 475483 Starting Jetty with [exec] should use properties file
+ + 475546 ClosedChannelException when connecting to HTTPS over HTTP proxy with
+   CONNECT.
+ + 475605 Add support for multi-homed destinations
+ + 475927 SecureRequestCustomizer fails to match host
+
+jetty-9.3.2.v20150730 - 30 July 2015
+ + 470351 Fixed SNI matching of wildcard certificates
+ + 470727 Thread Starvation of selector wakeups
+ + 472601 org.eclipse.jetty.util.log.Log.setLog() does not work as before
+ + 472621 Unjustified timeout when serving static content
+ + 472781 GzipHandler isMimeTypeGzipable() bad logic
+ + 472859 ConcatServlet may expose protected resources
+ + 472931 HttpConfiguration copy constructor incomplete
+ + 472974 Improved StatisticsHandler 503 generation
+ + 473006 Encode addPath in URLResource
+ + 473118 HTTP/2 server does not retrieve Host header from client
+ + 473243 Delay resource close for async default content
+ + 473266 Better handling of MultiException
+ + 473294 Fixed include cipher suites support for wildcards
+ + 473307 Add 301 Moved Permanently Rules to jetty-rewrite
+ + 473309 Add special (non-replacement) Terminating rules to jetty-rewrite
+ + 473319 Parameterize status code on Redirect Rules for alternate use
+ + 473321 Overriding SSL context KeyStoreType requires explicit override of
+   TrustStoreType
+ + 473322 GatherWrite limit handling
+ + 473624 ProxyServlet.Transparent / TransparentDelegate add trailing slash
+   before query when using prefix.
+ + 473832 SslConnection flips back buffers on handshake exception
+
 jetty-9.2.13.v20150730 - 30 July 2015
  + 472859 ConcatServlet may expose protected resources
  + 473006 Encode addPath in URLResource
@@ -113,6 +754,225 @@
    before query when using prefix.
  + 473832 SslConnection flips back buffers on handshake exception
 
+jetty-9.3.1.v20150714 - 14 July 2015
+ + 441020 Support HEADERS followed by CONTINUATION+
+ + 460671 Rationalize property names (fix for jetty.sh)
+ + 462346 Change classesPattern to scanClassesPattern and testClassesPattern to
+   scanTestClassesPattern to clarify purpose
+ + 464294 AsyncNCSARequestLog blocks JVM exit after failure
+ + 464741 HttpFields declares IllegalArgumentException as checked exception
+ + 464745 Remove @org.apache.xbean.XBean references
+ + 469384 Improved javadoc for ClasspathPattern
+ + 470184 Send the proxy-to-server request more lazily
+ + 470327 Problem with scope provided dependencies with jspc plugin
+ + 470505 jetty-maven-plugin JettyWebAppContext#setQuickStartWebDescriptor
+   should accept a Maven-friendly type
+ + 470664 Handle multiple RequestLogHandler in chain
+ + 470727 Thread Starvation of selector wakeups
+ + 470803 If a webapp is not fully started do not fully stop it
+ + 470855 Only log warning for duplicate path mappings to same servlet in same
+   descriptor
+ + 470963 Update jetty-maven-plugin mojo annotations for maven 3
+ + 471071 jetty-infinispan.xml incorrect syntax for remote named cache
+ + 471076 Apache jspc ignores empty list of files to precompile and scans
+   anyway
+ + 471251 Improved debugging on async timeout
+ + 471272 ArrayIndexOutOfBoundsException in
+   org.eclipse.jetty.quickstart.PreconfigureQuickStartWar
+ + 471388 StringIndexOutOfBoundsException when using <c:url> with parameters
+ + 471464 Parsing issues with HttpURI
+ + 471604 Extend CrossOriginFilter to provide a Timing-Allow-Origin header
+ + 471623 Update to apache jsp 8.0.23 Use 8.0.23.M1 for jetty version of apache
+   jsp 8.0.23
+ + 471985 NPE in HttpFields.putField
+ + 472310 Improved logging when no supported included ciphers
+ + 472411 PathResource.checkAliasPath typo
+ + 472422 Custom status codes result in a NumberFormatException while using
+   http2.
+
+jetty-9.3.0.v20150612 - 12 June 2015
+ + 414479 Add WebSocketPingPongListener for those that want PING/PONG payload
+   data
+ + 420678 Add WebSocketPartialListener to support receiving partial WebSocket
+   TEXT/BINARY messages
+ + 420944 Hot Deployment of WAR when Context XML exists doesn't trigger
+   redeploy
+ + 423974 Optimize flow control
+ + 424368 Add CONTRIBUTING.md
+ + 430951 Support SNI with ExtendedSslContextFactory
+ + 436345 Refactor AbstractSession to minimize burden on subclasses to
+   implement behaviour
+ + 437303 Serving of static filenames with "unwise" characters causes 404 error
+ + 437395 Start / Properties in template sections should be default applied for
+   enabled modules
+ + 438204 getServerName returns IPv6 addresses wrapped in []
+ + 439369 Remove unused class CrossContextPsuedoSession
+ + 439374 Use utf-8 as default charset for html
+ + 439375 preferred rfc7231 format is mime;charset=lowercase-9
+ + 440106 Improve ProtocolHandler APIs
+ + 440506 Jetty OSGi boot bundle does not support OSGi framework Eclipse
+   Concierge
+ + 442083 Client resets stream, pending server data is failed, connection
+   closed.
+ + 442086 Review HttpOutput blocking writes
+ + 442477 Allow Symlink aliases by default
+ + 442495 Bad Context ClassLoader in JSR356 WebSocket onOpen
+ + 442950 Embedded Jetty client requests to localhost hangs with high cpu usage
+   (NIO OP_CONNECT Solaris/Sparc).
+ + 443652 Remove dependency on java.lang.management classes
+ + 443661 Rename manifest and service constants for jetty osgi resource
+   fragment code
+ + 443662 Consume buffer in write(ByteBuffer)
+ + 443713 Reduce number of SelectionKey.setInterestOps() calls
+ + 443893 Make a module for weld
+ + 444124 JSP include with <servlet><jsp-file> can cause infinite recursion
+ + 444214 Socks4Proxy fails when reading less than 8 bytes
+ + 444222 replace CRLF in header values with whitespace rather than ?
+ + 444416 AsyncProxyServlet recursion
+ + 444485 Client resets stream, pending server data is failed, write hangs
+ + 444517 Ensure WebSocketUpgradeFilter is always first in filter chain
+ + 444547 Format exception in ResourceCache.Content.toString()
+ + 444617 Expose local and remote socket address to applications
+ + 444721 PushCacheFilter cleanup/improvements
+ + 444748 WebSocketClient.stop() does not unregister from ShutdownThread
+ + 444764 HttpClient notifies callbacks for last chunk of content twice
+ + 444771 JSR356 / EndPointConfig.userProperties are not unique per endpoint
+   upgrade
+ + 445167 Allow configuration of dispatch after select
+ + 445823 Moved RequestLog calling to HttpChannel
+ + 446559 Avoid spin consuming extra data
+ + 446564 Refactored RequestLog Mechanism
+ + 446944 ServletTester and HttpTester should be in
+   <classifier>tests</classifier>
+ + 447216 putAll Properties in XmlConfiguration
+ + 447515 Remove GzipFilter
+ + 448156 Fixed INACTIVE race in IteratingCallback
+ + 448675 Impossible to set own Threadpool when using jetty-maven-plugin
+ + 449003 WARNING: Cannot enable requested module [protonego-impl]: not a valid
+   module name
+ + 449811 handle unquoted etags when gzipping
+ + 450467 Integer overflow in Session expiry calculation in MongoSessionManager
+ + 451973 Ambiguous module init location when mixing --add-to-start &
+   --add-to-startd in the same exec
+ + 451974 Combine multiple start license acknowledgement into one
+ + 452188 Delay dispatch until content optimisation
+ + 452322 Restore progress messages for --add-to-start(d) use
+ + 452323 Start --list-config makes no hint on transitive enabled modules
+ + 452329 Transitive modules in start.jar --add-to-start(d) are not added if
+   enabled already in tree
+ + 452465 100% CPU spin on page reload
+ + 452503 Start.jar --add-to-start=jstl results in GraphException: Unable to
+   expand property in name: jsp-impl/${jsp-impl}-jstl
+ + 453487 Recycle HttpChannelOverHTTP2
+ + 453627 Fixed FileSystem test for nanosecond filesystems
+ + 453636 Improved spin detection on test
+ + 453829 Added HeaderRegexRule
+ + 453834 CDI Support for WebSocket
+ + 454152 Remove mux remnants from WebSocketClient
+ + 454934 WebSocketClient / connectToServer can block indefinitely during
+   upgrade failure
+ + 454952 Allow Jetty to run in Java 8 compact 3 profile
+ + 456209 Bad ContextClassLoader in WebSocket onMessage
+ + 456956 Reduce ThreadLocal.remove() weak reference garbage
+ + 457130 HTTPS request with IP host and HTTP proxy throws
+   IllegalArgumentException.
+ + 457309 Add test to ensure GET and HEAD response headers same for gzip
+ + 457508 Add flag to scan exploded jars in jetty-jspc-maven-plugin
+ + 457788 Powered By in o.e.j.util.Jetty conditional on sendServerVersion
+ + 458478 JarFileResource improve performance of exist method
+ + 458527 Implement an async proxy servlet that can perform content
+   transformations.
+ + 458663 Handle null header values
+ + 459081 http2 push failures
+ + 459542 AsyncMiddleManServlet race condition on first download content
+ + 459655 Remove SPDY and NPN
+ + 459681 Remove dead code after removal of glassfish jasper support
+ + 459731 Update for drafts hpack-11 and http2-17
+ + 459734 Update to apache jsp 8.0.20
+ + 459845 Support upgrade from http1 to http2
+ + 460187 infinite recursion in sending error
+ + 460210 ExecutionStragegy producer for SelectManager calls onOpen from
+   produce method
+ + 460211 Fixed Idle race in ExecuteProduceRun
+ + 460297 Parameterize infinispan.mod
+ + 460670 Support multiple names in <Property> elements
+ + 460671 Rationalize property names
+ + 460746 HttpConfiguration#setPersistentConnectionsEnabled(boolean)
+ + 461052 Local streams created after INITIAL_WINDOW_SIZE setting have wrong
+   send window.
+ + 461350 Update HttpParser IllegalCharacter handling to RFC7230
+ + 461415 Maven Jetty Plugin ignores ZIP overlays
+ + 462040 reverted and deprecated getStringField methods
+ + 462098 Support setting ThreadGroup in ScheduledExecutorScheduler
+ + 462162 StackOverflowException when response commit fails
+ + 462193 Asynchronous HttpOutput.close()
+ + 463036 system properties to set ssl password and keypasword
+ + 463144 modules do not see pre-downloaded ALPN libs
+ + 464419 Removed xinetd support
+ + 464438 ClassFileTransformer support in
+   org.eclipse.jetty.webapp.WebAppClassLoader broken
+ + 464442 Enable parallel class loading
+ + 464528 NPE protection in getIncludedCipher suites
+ + 464537 Updated setuid dependency to 1.0.3
+ + 464555 ALPN module download attempts to download jar before dir exists
+ + 464556 Restrict start module downloads to ${jetty.base} paths only
+ + 464564 NoSql sessions created inside a forward not persisted correctly
+ + 464606 Support property expansion in "default" attribute of Property
+ + 464629 JDK8 Socket customization
+ + 464630 Cannot configure Configuration classlist in osgi
+ + 464633 Change Selection.how to Selection.criteria
+ + 464706 HTTP/2 and async I/O: onDataAvailable() not called
+ + 464708 Support HttpConfiguration.delayDispatchUntilContent in HTTP/2
+ + 464724 MultiPartInputStreamParser.parse ServletException never thrown
+ + 464727 Update Javadoc for Java 8 DocLint
+ + 464744 PathMap.match() never throws IllegalArgumentException
+ + 464837 Large META-INF/resources/ jars can significantly impact startup speed
+ + 464839 Add limit to MongoSessionIdManager purge queries
+ + 464869 org.eclipse.jetty.util.resource.PathResource do not work
+ + 465118 Fixed GzipHandler handling of multiple closes
+ + 465606 IteratingCallback.close() does not fail pending callback
+ + 465754 Unchecked PrintWriter errors
+ + 465854 Provide java.nio.file.WatchService alternative for Scanner
+ + 465857 Support HTTP/2 clear-text server-side upgrade
+ + 465867 Implement --skip-file-validation=<module>
+ + 466005 Use Files.move(src,trgt) instead of File.rename for
+   Part.write(filename)
+ + 466283 Support specifying ALPN protocols in HTTP2Client
+ + 466618 Partial WebSocket Text delivery does not like incomplete UTF8
+   sequences
+ + 466619 Add WebSocketFrameListener for receiving WebSocket Frame information
+ + 466628 Improve IllegalStateException on ServletInputStream.setReadListener()
+ + 466645 Allow XmlConfiguration Properties to use Elements or Attributes
+ + 466647 Add ${jetty.tag.version} property and expand URL properties
+ + 466648 jetty-ssl download of keystore should be from tags, not master
+ + 466669 Add nosql.mod into jetty distro
+ + 466678 Make a .mod file for jdbc session management
+ + 466774 Update jetty-all module for Jetty 9.3
+ + 467036 WebSocketClient fails to process immediate frames from server
+ + 467043 WebSocketClient close codes on protocol violation reported as policy
+   violation
+ + 467055 Mongodb session scavenging can result in very slow query
+ + 467165 Add --skip-file-validation to start.jar --help output
+ + 467281 Remove Java 1.7 support from Jetty 9.3
+ + 467289 Not possible to specify jmxrmi port value
+ + 467702 SslContextFactory not backward compatible
+ + 467730 HTTP2 requires enabled ciphers to be sorted by blacklist
+ + 467790 Update default etc files inside jetty-osgi-boot bundle
+ + 468313 PushCacheFilter wrongly associates primary resources to themselves
+ + 468347 Fix modules/debuglog.mod
+ + 469241 Use null WatchService as loop terminator for PathWatcher
+ + 469341 Not possible to use old/deprecated start properties
+ + 469414 Proxied redirects expose upstream server name
+ + 469633 Make SpinLock behavior pluggable
+ + 469799 Transitive module dependencies without ini templates are still added
+   to ini
+ + 469860 Add module metadata versioning to support backwards compat
+ + 469863 fixed setNeedClientAuth/setWantClientAuth
+ + 469936 Remove usages of SpinLock
+ + 469982 Produce warning for dynamic modules with ini-templates seen during
+   --add-to-start
+ + 469991 Fix logging levels in websocket client UpgradeConnection
+
 jetty-9.2.12.v20150709 - 09 July 2015
  + 469414 Proxied redirects expose upstream server name
  + 469936 Remove usages of SpinLock
@@ -148,6 +1008,118 @@
  + 468714 SelectorManager updateKey race without submit
  + 468747 XSS vulnerability in HttpSpiContextHandler
 
+jetty-9.3.0.RC1 - 22 May 2015
+ + 464839 Add limit to MongoSessionIdManager purge queries
+ + 465053 Prevent gzip buffer overflow on complete
+ + 466774 Update jetty-all module for Jetty 9.3
+ + 467055 Mongodb session scavenging can result in very slow query
+ + 467165 Add --skip-file-validation to start.jar --help output
+ + 467276 NPE protection in SslContextFactory
+ + 467281 Remove Java 1.7 support from Jetty 9.3
+ + 467289 Not possible to specify jmxrmi port value
+ + 467603 Response 401 from server hangs client
+ + 467702 SslContextFactory not backward compatible
+ + 467730 HTTP2 requires enabled ciphers to be sorted by blacklist
+ + 467790 Update default etc files inside jetty-osgi-boot bundle
+ + 467936 w Check HttpOutput aggregateSize is < bufferSize
+
+jetty-9.3.0.RC0 - 12 May 2015
+ + 414479 Add WebSocketPingPongListener for those that want PING/PONG payload
+   data
+ + 420678 Add WebSocketPartialListener to support receiving partial WebSocket
+   TEXT/BINARY messages
+ + 423974 Optimize flow control
+ + 430951 Support SNI with ExtendedSslContextFactory
+ + 436345 Refactor AbstractSession to minimize burden on subclasses to
+   implement behaviour
+ + 440106 Improve ProtocolHandler APIs
+ + 444721 PushCacheFilter cleanup/improvements
+ + 446564 Refactored RequestLog Mechanism
+ + 451973 Ambiguous module init location when mixing --add-to-start &
+   --add-to-startd in the same exec
+ + 453834 CDI Support for WebSocket
+ + 454934 WebSocketClient / connectToServer can block indefinitely during
+   upgrade failure
+ + 457309 Add test to ensure GET and HEAD response headers same for gzip
+ + 457508 Add flag to scan exploded jars in jetty-jspc-maven-plugin
+ + 457788 Powered By in o.e.j.util.Jetty conditional on sendServerVersion
+ + 458478 JarFileResource improve performance of exist method
+ + 459273 Redundant license notices
+ + 459734 Update to apache jsp 8.0.20
+ + 459845 Support upgrade from http1 to http2
+ + 460187 infinite recursion in sending error
+ + 460297 Parameterize infinispan.mod
+ + 460671 Rationalize property names
+ + 460746 HttpConfiguration#setPersistentConnectionsEnabled(boolean)
+ + 461415 Maven Jetty Plugin ignores ZIP overlays
+ + 461499 ConnectionPool may leak connections
+ + 461919 Use osgi-friendly serviceloader mechanism for WebSocketServletFactory
+ + 461941 JMX Remote host:port set from start properties
+ + 462040 reverted and deprecated getStringField methods
+ + 462098 Support setting ThreadGroup in ScheduledExecutorScheduler
+ + 462162 StackOverflowException when response commit fails
+ + 462193 Asynchronous HttpOutput.close()
+ + 462546 ShutdownMonitor should bind to jetty.host
+ + 462616 Race between finishing a connect and timing it out
+ + 463036 system properties to set ssl password and keypasword
+ + 463144 modules do not see pre-downloaded ALPN libs
+ + 463579 Add support for 308 status code
+ + 464292 Implement stream-based transformer for AsyncMiddleManServlet
+ + 464419 Removed xinetd support
+ + 464438 ClassFileTransformer support in
+   org.eclipse.jetty.webapp.WebAppClassLoader broken
+ + 464442 Enable parallel class loading
+ + 464528 NPE protection in getIncludedCipher suites
+ + 464537 Updated setuid dependency to 1.0.3
+ + 464555 ALPN module download attempts to download jar before dir exists
+ + 464556 Restrict start module downloads to ${jetty.base} paths only
+ + 464564 NoSql sessions created inside a forward not persisted correctly
+ + 464606 Support property expansion in "default" attribute of Property
+ + 464629 JDK8 Socket customization
+ + 464630 Cannot configure Configuration classlist in osgi
+ + 464633 Change Selection.how to Selection.criteria
+ + 464706 HTTP/2 and async I/O: onDataAvailable() not called
+ + 464708 Support HttpConfiguration.delayDispatchUntilContent in HTTP/2
+ + 464724 MultiPartInputStreamParser.parse ServletException never thrown
+ + 464727 Update Javadoc for Java 8 DocLint
+ + 464740 DosFilter whiteList check improvement
+ + 464744 PathMap.match() never throws IllegalArgumentException
+ + 464837 Large META-INF/resources/ jars can significantly impact startup speed
+ + 464869 org.eclipse.jetty.util.resource.PathResource do not work
+ + 464989 AbstractSessionManager.removeEventListener() should remove
+   HttpSessionIdListener
+ + 465181 HttpParser parse full end chunk
+ + 465202 Forked Mojo does not extract war overlays/dependencies
+ + 465359 Resource.newResource(String res, boolean useCache) does not use
+   useCache argument
+ + 465360 URLResource.addPath should use _useCaches setting to create new
+   Resource
+ + 465606 IteratingCallback.close() does not fail pending callback
+ + 465700 NullPointerException in ResourceHandler with welcome files
+ + 465734 DosFilter whitelist bit pattern fix
+ + 465747 Jetty is failing to process all HTTP OPTIONS requests
+ + 465754 Unchecked PrintWriter errors
+ + 465854 Provide java.nio.file.WatchService alternative for Scanner
+ + 465857 Support HTTP/2 clear-text server-side upgrade
+ + 465867 Implement --skip-file-validation=<module>
+ + 466005 Use Files.move(src,trgt) instead of File.rename for
+   Part.write(filename)
+ + 466283 Support specifying ALPN protocols in HTTP2Client
+ + 466329 Fixed local only TestFilter
+ + 466618 Partial WebSocket Text delivery does not like incomplete UTF8
+   sequences
+ + 466619 Add WebSocketFrameListener for receiving WebSocket Frame information
+ + 466628 Improve IllegalStateException on ServletInputStream.setReadListener()
+ + 466645 Allow XmlConfiguration Properties to use Elements or Attributes
+ + 466647 Add ${jetty.tag.version} property and expand URL properties
+ + 466648 jetty-ssl download of keystore should be from tags, not master
+ + 466669 Add nosql.mod into jetty distro
+ + 466678 Make a .mod file for jdbc session management
+ + 466774 Update jetty-all module for Jetty 9.3
+ + 467036 WebSocketClient fails to process immediate frames from server
+ + 467043 WebSocketClient close codes on protocol violation reported as policy
+   violation
+
 jetty-9.2.11.M0 - 25 March 2015
  + 454934 WebSocketClient / connectToServer can block indefinitely during
    upgrade failure
@@ -158,6 +1130,148 @@
  + 462546 ShutdownMonitor should bind to jetty.host
  + 462616 Race between finishing a connect and timing it out
 
+jetty-9.3.0.M2 - 11 March 2015
+ + 383207 Use BundleFileLocatorHelperFactory to obtain BundleFileLocatorHelper
+ + 420944 Hot Deployment of WAR when Context XML exists doesn't trigger
+   redeploy
+ + 423974 Optimize flow control
+ + 424368 Add CONTRIBUTING.md
+ + 430951 Improved ordering of SSL ciphers
+ + 439374 Use utf-8 as default charset for html
+ + 440506 Jetty OSGi boot bundle does not support OSGi framework Eclipse
+   Concierge
+ + 443652 Remove dependency on java.lang.management classes
+ + 445518 Provide different error callbacks to ProxyServlet
+ + 446564 Refactored RequestLog Mechanism
+ + 447472 Clear async context timeout on async static content
+ + 448446 org.eclipse.jetty.start.Main create classloader duplicate
+ + 448944 Provide m2e lifecycle mapping metadata for jetty-jspc-maven-plugin
+ + 449594 Handle ArrayTrie overflow with false return
+ + 449811 handle unquoted etags when gzipping
+ + 450467 Integer overflow in Session expiry calculation in MongoSessionManager
+ + 450483 Missing parameterization of etc/jetty-deploy.xml
+ + 450484 Missing parameterization of etc/jetty-http[s].xml
+ + 450855 GzipFilter MIGHT_COMPRESS exception
+ + 450873 Disable tests that downcaste wrapped GzipFilterResponses
+ + 450894 jetty.sh does not delete JETTY_STATE at start
+ + 451092 Connector will fail if HeaderListener return false
+ + 451529 Change sentinel class for finding jstl on classpath to
+   org.apache.taglibs.standard.tag.rt.core.WhenTag
+ + 451634 DefaultServlet: useFileMappedBuffer javadoc is misleading
+ + 451973 Ambiguous module init location when mixing --add-to-start &
+   --add-to-startd in the same exec
+ + 451974 Combine multiple start license acknowledgement into one
+ + 452188 Delay dispatch until content optimisation
+ + 452201 Set the container classloader for osgi during webbundle undeploy
+ + 452246 Fixed SSL hang on last chunk
+ + 452261 Ensure <jsp-file> works with new JettyJspServlet
+ + 452322 Restore progress messages for --add-to-start(d) use
+ + 452323 Start --list-config makes no hint on transitive enabled modules
+ + 452329 Transitive modules in start.jar --add-to-start(d) are not added if
+   enabled already in tree
+ + 452424 Do not add Date header if already set
+ + 452465 100% CPU spin on page reload
+ + 452503 Start.jar --add-to-start=jstl results in GraphException: Unable to
+   expand property in name: jsp-impl/${jsp-impl}-jstl
+ + 452516 Make HttpOutput aggregation size configurable
+ + 453386 Jetty not working when configuring QueuedThreadPool with
+   minThreads=0.
+ + 453487 Recycle HttpChannelOverHTTP2
+ + 453627 Fixed FileSystem test for nanosecond filesystems
+ + 453629 Fixed big write test
+ + 453636 Improved spin detection on test
+ + 453793 _maxHeaderBytes>0 is not verified in parseNext() when in
+   State.CLOSED.
+ + 453801 Jetty does not check for already registered services when
+   bootstrapping
+ + 453829 removed code with yahoo copyright
+ + 454152 Remove mux remnants from WebSocketClient
+ + 454157 HttpInput.consumeAll spins if input is in async mode
+ + 454291 Added busy threads JMX attribute to QueuedThreadPool
+ + 454773 SSLConnection use on Android client results in loop
+ + 454952 Allow Jetty to run in Java 8 compact 3 profile
+ + 454954 Jetty osgi should skip fragment and required bundles that are in the
+   uninstalled state
+ + 454955 OSGi AnnotationParser should skip resources that are not in the
+   classpath and close the class inputstream when done scanning it
+ + 454983 Source bundles should not be singleton
+ + 455047 Update JASPI
+ + 455174 jetty-plus JNDI tests should use unique JNDI paths
+ + 455330 Multiple Jetty-ContextFilePath entries separated by commas doesn't
+   work
+ + 455436 ProxyServlet sends two User-Agent values
+ + 455476 Persist updated session expiry time for MongoSessionManager
+ + 455655 ensure multipart form-data parsing exception thrown to servlet
+ + 455863 Fixed jetty.sh handling of multiple JETTY_ARGS
+ + 456209 Bad ContextClassLoader in WebSocket onMessage
+ + 456426 Exception on context undeploy from EnvConfiguration
+ + 456486 Jar containing ServiceContainerInitializer impl not found in TCCL in
+   osgi
+ + 456521 ShutdownHandler should shut down more gracefully
+ + 456956 Reduce ThreadLocal.remove() weak reference garbage
+ + 457017 Reflective call to websocket methods that fail have ambiguous
+   exceptions
+ + 457032 Request sent from a failed CompleteListener due to connect timeout is
+   failed immediately.
+ + 457130 HTTPS request with IP host and HTTP proxy throws
+   IllegalArgumentException.
+ + 457696 JMX implementation should not be overridden by WebApp classes
+ + 457893 Close temp jar resource
+ + 458101 added test for maxFormContentSize
+ + 458140 Added DispatcherType support to RewriteHandler
+ + 458174 Example Jar Server
+ + 458175 multipart annotation on lazily loaded servlet does not work
+ + 458209 Length check for HttpMethod MOVE lookahead
+ + 458354 ALPNServerConnection.select negotiation
+ + 458495 CompletableCallback may not notify failures
+ + 458527 Implement an async proxy servlet that can perform content
+   transformations.
+ + 458568 JDBCLoginService javadoc incorrectly references HashLoginService
+ + 458663 Handle null header values
+ + 458849 org.eclipse.jetty.util.Uptime.DefaultImpl() not available on GAE
+ + 459006 master branch does not build on norwegian locale
+ + 459081 http2 push failures
+ + 459125 GzipHandler default mimeType behavior incorrect
+ + 459273 Redundant license notices
+ + 459352 AsyncMiddleManServlet should set "Host:" header correctly in proxy to
+   remote request headers.
+ + 459490 Defining a duplicate error page in webdefault.xml and web.xml results
+   in an error
+ + 459542 AsyncMiddleManServlet race condition on first download content
+ + 459560 jetty.sh handles start.d and no start.ini
+ + 459655 Remove SPDY and NPN
+ + 459681 Remove dead code after removal of glassfish jasper support
+ + 459731 Update for drafts hpack-11 and http2-17
+ + 459769 AsyncMiddleManServlet race condition on last download content
+ + 459845 Support upgrade from http1 to http2/websocket
+ + 459963 Failure writing content of a committed request leaks connections
+ + 460176 When checking for precompiled jsp, ensure classname is present
+ + 460180 Jaas demo has wrong doco in html
+ + 460210 ExecutionStragegy producer for SelectManager calls onOpen from
+   produce method
+ + 460211 Fixed Idle race in ExecuteProduceRun
+ + 460291 AsyncGzipFilter Mappings
+ + 460371 AsyncMiddleManServlet.GZipContentTransformer fails if last transform
+   has no output
+ + 460372 if web.xml does not contain jspc maven plugin insertionMarker
+   behavior is wrong
+ + 460443 Race condition releasing the response buffer
+ + 460642 HttpParser error 400 can expose previous buffer contents in HTTP
+   status reason message
+ + 460670 Support multiple names in <Property> elements
+ + 460769 ClientUpgradeRequest sends cookies in the wrong format
+ + 460905 Make sure TimeoutCompleteListener is cancelled if the request cannot
+   be sent.
+ + 461052 Local streams created after INITIAL_WINDOW_SIZE setting have wrong
+   send window.
+ + 461070 Handle setReadListener on request with no content
+ + 461133 allow stop port to reuse address
+ + 461350 Update HttpParser IllegalCharacter handling to RFC7230
+ + 461452 Double release of buffer by HttpReceiverOverHTTP
+ + 461499 ConnectionPool may leak connections
+ + 461623 BufferUtil.writeTo does not update position consistently
+ + 461643 HttpContent.advance() race
+
 jetty-9.2.10.v20150310 - 10 March 2015
  + 445518 Provide different error callbacks to ProxyServlet
  + 456521 ShutdownHandler should shut down more gracefully
@@ -275,6 +1389,82 @@
  + 450873 Disable tests that downcaste wrapped GzipFilterResponses
  + 450894 jetty.sh does not delete JETTY_STATE at start
 
+jetty-9.3.0.M1 - 03 November 2014
+ + 376365 "jetty.sh start" returns 0 on failure
+ + 396569 'bin/jetty.sh stop' reports 'OK' even when jetty was not running
+ + 396572 Starting jetty from cygwin is not working properly
+ + 437303 Serving of static filenames with "unwise" characters causes 404 error
+ + 440729 SSL requests often fail with EOFException or IllegalStateException
+ + 440925 NPE when using relative paths for --start-log-file
+ + 442419 CrossOriginFilter javadoc says "exposeHeaders", but should be
+   "exposedHeaders"
+ + 442942 Content sent with status 204 (No Content)
+ + 443529 CrossOriginFilter does not accept wildcard for allowedHeaders
+ + 443530 CrossOriginFilter does not set the Vary header
+ + 443550 improved FileResource encoded alias checking
+ + 444031 Ensure exceptions do not reduce threadpool below minimum
+ + 444595 nosql/mongodb - Cleanup process/Refreshing does not respect encoding
+   of attribute keys
+ + 444676 Goal jetty:deploy-war produces errors with version 9.2.3
+ + 444722 Fixed order of setReuseAddress call
+ + 444896 Overriding of web-default servlet mapping in web.xml not working with
+   quickstart
+ + 445157 First redeployed servlet leaks WebAppContext
+ + 445167 Allow configuration of dispatch after select
+ + 445239 Rename weld.mod to cdi.mod to be consistent with past module namings
+ + 445258 STOP.WAIT is not really respected
+ + 445374 Reevaluate org.eclipse.jetty.websocket.jsr356 enablement concepts
+ + 445495 Improve Exception message when no jndi resource to bind for a name in
+   web.xml
+ + 445542 Add SecuredRedirectHandler for embedded jetty use to redirect to
+   secure port/scheme
+ + 445821 Error 400 should be logged with RequestLog
+ + 445823 Moved RequestLog calling to HttpChannel
+ + 445830 Support setting environment variables on forked jetty with
+   jetty:run-forked
+ + 445979 jetty.sh fails to start when start-stop-daemon does not exist and the
+   user is not root
+ + 446033 org.eclipse.jetty.websocket.server.WebSocketServerFactory not
+   available in OSGi
+ + 446063 ALPN Fail SSL Handshake if no supported Application Protocols
+ + 446107 NullPointerException in ProxyServlet when extended by Servlet without
+   a package
+ + 446425 Oracle Sql error on JettySessions table when this table do not exist
+   already
+ + 446506 getAsyncContext ISE before startAsync on async dispatches
+ + 446559 Avoid spin consuming extra data
+ + 446563 Null HttpChannel.getCurrentHttpChannel() in
+   ServletHandler.doFilter().
+ + 446564 Refactored RequestLog Mechanism
+ + 446672 NPN Specification issue in the case no protocols are selected
+ + 446923 SharedBlockingCallback does not handle connector max idle time of
+   Long.MAX_VALUE; BlockerTimeoutException not serializable
+ + 446944 ServletTester and HttpTester should be in
+   <classifier>tests</classifier>
+ + 447216 putAll Properties in XmlConfiguration
+ + 447381 Disable SSLv3 by default
+ + 447472 test harness for slow large writes
+ + 447515 Remove GzipFilter
+ + 447627 MultiPart file always created when "filename" set in
+   Content-Disposition
+ + 447629 getPart()/getParts() fails on Multipart request if getParameter is
+   called in a filter first
+ + 447746 HttpClient is always going to send User-Agent header even though I do
+   not want it to.
+ + 447979 Refactor to make MetaData responsible for progressively ordering
+   web-inf jars
+ + 448156 Fixed INACTIVE race in IteratingCallback
+ + 448225 Removed unnecessary synchronize on initParser
+ + 448675 Impossible to set own Threadpool when using jetty-maven-plugin
+ + 448841 Clarified selectors==0 javadoc 448840 Clarified ServerConnector
+   javadoc 448839 Fixed javadoc typo in ServerConnector
+ + 449001 Remove start.d directory from JETTY_HOME
+ + 449003 WARNING: Cannot enable requested module [protonego-impl]: not a valid
+   module name
+ + 449038 WebSocketUpgradeFilter must support async
+ + 449175 Removed extra space in NCSA log
+ + 449372 Make jvmArgs of jetty:run-forked configurable from command line
+
 jetty-9.2.4.v20141103 - 03 November 2014
  + 376365 "jetty.sh start" returns 0 on failure
  + 396569 'bin/jetty.sh stop' reports 'OK' even when jetty was not running
@@ -311,6 +1501,7 @@
  + 444896 Overriding of web-default servlet mapping in web.xml not working with
    quickstart
  + 445157 First redeployed servlet leaks WebAppContext
+ + 445167 Allow configuration of dispatch after select
  + 445239 Rename weld.mod to cdi.mod to be consistent with past module namings
  + 445258 STOP.WAIT is not really respected
  + 445374 Reevaluate org.eclipse.jetty.websocket.jsr356 enablement concepts
@@ -332,6 +1523,7 @@
  + 446425 Oracle Sql error on JettySessions table when this table do not exist
    already
  + 446506 getAsyncContext ISE before startAsync on async dispatches
+ + 446559 Avoid spin consuming extra data
  + 446563 Null HttpChannel.getCurrentHttpChannel() in
    ServletHandler.doFilter().
  + 446672 NPN Specification issue in the case no protocols are selected
@@ -339,6 +1531,7 @@
    Long.MAX_VALUE; BlockerTimeoutException not serializable
  + 447381 Disable SSLv3 by default
  + 447472 test harness for slow large writes
+ + 447515 Remove GzipFilter
  + 447627 MultiPart file always created when "filename" set in
    Content-Disposition
  + 447629 getPart()/getParts() fails on Multipart request if getParameter is
@@ -349,6 +1542,7 @@
    web-inf jars
  + 448156 Fixed INACTIVE race in IteratingCallback
  + 448225 Removed unnecessary synchronize on initParser
+ + 448675 Impossible to set own Threadpool when using jetty-maven-plugin
  + 448841 Clarified selectors==0 javadoc 448840 Clarified ServerConnector
    javadoc 448839 Fixed javadoc typo in ServerConnector
  + 449001 Remove start.d directory from JETTY_HOME
@@ -360,6 +1554,42 @@
  + 449372 Make jvmArgs of jetty:run-forked configurable from command line
  + 449603 OutputStreamContentProvider hangs when host is not available
 
+jetty-9.3.0.M0 - 24 September 2014
+ + 437395 Start / Properties in template sections should be default applied for
+   enabled modules
+ + 438204 getServerName returns IPv6 addresses wrapped in []
+ + 438387 NullPointerException after ServletUpgradeResponse.sendForbidden is
+   called during WebSocketCreator.createWebSocket
+ + 439369 Remove unused class CrossContextPsuedoSession
+ + 439375 preferred rfc7231 format is mime;charset=lowercase-9
+ + 442083 Client resets stream, pending server data is failed, connection
+   closed.
+ + 442086 Review HttpOutput blocking writes
+ + 442477 Allow Symlink aliases by default
+ + 442495 Bad Context ClassLoader in JSR356 WebSocket onOpen
+ + 442950 Embedded Jetty client requests to localhost hangs with high cpu usage
+   (NIO OP_CONNECT Solaris/Sparc).
+ + 443652 Remove dependency on java.lang.management classes
+ + 443661 Rename manifest and service constants for jetty osgi resource
+   fragment code
+ + 443662 Consume buffer in write(ByteBuffer)
+ + 443713 Reduce number of SelectionKey.setInterestOps() calls
+ + 443893 Make a module for weld
+ + 444124 JSP include with <servlet><jsp-file> can cause infinite recursion
+ + 444214 Socks4Proxy fails when reading less than 8 bytes
+ + 444222 replace CRLF in header values with whitespace rather than ?
+ + 444415 iterative WriteFlusher
+ + 444416 AsyncProxyServlet recursion
+ + 444485 Client resets stream, pending server data is failed, write hangs
+ + 444517 Ensure WebSocketUpgradeFilter is always first in filter chain
+ + 444547 Format exception in ResourceCache.Content.toString()
+ + 444617 Expose local and remote socket address to applications
+ + 444748 WebSocketClient.stop() does not unregister from ShutdownThread
+ + 444764 HttpClient notifies callbacks for last chunk of content twice
+ + 444771 JSR356 / EndPointConfig.userProperties are not unique per endpoint
+   upgrade
+ + 444863 ProxyServlet does not filter headers listed by the Connection header
+
 jetty-9.2.3.v20140905 - 05 September 2014
  + 347110 renamed class transformer methods
  + 411163 Add embedded jetty code example with JSP enabled
@@ -1133,8 +2363,8 @@
  + 412940 minor threadsafe fixes
  + 413018 ServletContext.addListener() should throw IllegalArgumentException if
    arg is not correct type of listener
- + 413020 Second call to HttpSession.invalidate() should throw exception 413019
-   HttpSession.getCreateTime() should throw exception after session is
+ + 413020 Second call to HttpSession.invalidate() should throw exception
+ + 413019 HttpSession.getCreateTime() should throw exception after session is
    invalidated
  + 413291 Avoid SPDY double dispatch
  + 413387 onResponseHeaders is not called multiple times when multiple
diff --git a/aggregates/jetty-all-compact3/pom.xml b/aggregates/jetty-all-compact3/pom.xml
new file mode 100644
index 0000000..4e39533
--- /dev/null
+++ b/aggregates/jetty-all-compact3/pom.xml
@@ -0,0 +1,237 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-project</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+    <relativePath>../../pom.xml</relativePath>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.eclipse.jetty.aggregate</groupId>
+  <artifactId>jetty-all-compact3</artifactId>
+  <name>Jetty :: Aggregate :: All core Jetty suitable for Java 8 compact 3 profile</name>
+  <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.all.compact3</bundle-symbolic-name>
+  </properties>
+  <build>
+    <sourceDirectory>${project.build.directory}/sources</sourceDirectory>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>8</source>
+          <target>8</target>
+          <compilerArgs>
+            <arg>-profile</arg>
+            <arg>compact3</arg>
+          </compilerArgs>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>unpack-dependencies</id>
+            <goals>
+              <goal>unpack-dependencies</goal>
+            </goals>
+            <configuration>
+              <excludes>**/MANIFEST.MF,javax/**</excludes>
+              <excludeArtifactIds>javax</excludeArtifactIds>
+              <excludeGroupIds>javax,org.eclipse.jetty.orbit,org.slf4j,org.ow2.asm</excludeGroupIds>
+              <outputDirectory>${project.build.directory}/classes</outputDirectory>
+              <overWriteReleases>false</overWriteReleases>
+              <overWriteSnapshots>true</overWriteSnapshots>
+            </configuration>
+          </execution>
+          <execution>
+            <id>unpack-source</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>unpack-dependencies</goal>
+            </goals>
+            <configuration>
+              <classifier>sources</classifier>
+              <includes>**/*</includes>
+              <excludes>
+                META-INF/**,
+                **/Servlet3Continuation*,
+                **/Jetty6Continuation*,
+                **/AppContextLeakPreventer*.java,
+                **/AWTLeakPreventer*.java,
+                **/IntrospectorCleaner*.java,
+                **/PostConstructAnnotationHandler*.java,
+                **/PreDestroyAnnotationHandler*.java,
+                **/ResourceAnnotationHandler*.java,
+                **/ResourcesAnnotationHandler*.java
+              </excludes>
+              <includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.websocket</includeGroupIds>
+              <excludeArtifactIds>javax</excludeArtifactIds>
+              <excludeGroupIds>javax,org.eclipse.jetty.orbit,org.slf4j,org.ow2.asm</excludeGroupIds>
+              <outputDirectory>${project.build.directory}/sources</outputDirectory>
+              <overWriteReleases>true</overWriteReleases>
+              <overWriteSnapshots>true</overWriteSnapshots>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifest>
+            </manifest>
+            <manifestEntries>
+              <mode>development</mode>
+              <url>http://eclipse.org/jetty</url>
+              <Built-By>${user.name}</Built-By>
+              <package>org.eclipse.jetty</package>
+              <Bundle-License>https://raw.githubusercontent.com/eclipse/jetty.project/master/NOTICE.txt</Bundle-License>
+              <Bundle-Name>Jetty</Bundle-Name>
+            </manifestEntries>
+          </archive>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <configuration>
+          <additionalparam>-Xdoclint:none</additionalparam>
+        </configuration>
+        <executions>
+          <execution>
+            <id>javadoc-jar</id>
+            <phase>compile</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-pmd-plugin</artifactId>
+          <configuration>
+            <skip>true</skip>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-client</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-deploy</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>websocket-servlet</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>javax-websocket-server-impl</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>websocket-client</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-plus</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-annotations</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-util</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-jaspi</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-jndi</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-rewrite</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlets</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-quickstart</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <!-- dependencies that jetty-all needs (some optional) -->
+    <dependency>
+      <groupId>javax.websocket</groupId>
+      <artifactId>javax.websocket-api</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.transaction</groupId>
+      <artifactId>javax.transaction-api</artifactId>
+      <scope>compile</scope>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.orbit</groupId>
+      <artifactId>javax.mail.glassfish</artifactId>
+      <scope>compile</scope>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <scope>compile</scope>
+      <optional>true</optional>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/aggregates/jetty-all/pom.xml b/aggregates/jetty-all/pom.xml
index 352b0f1..67a0795 100644
--- a/aggregates/jetty-all/pom.xml
+++ b/aggregates/jetty-all/pom.xml
@@ -2,108 +2,124 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.eclipse.jetty.aggregate</groupId>
   <artifactId>jetty-all</artifactId>
   <name>Jetty :: Aggregate :: All core Jetty</name>
+  <description>UberJar for Core Jetty features</description>
+  <packaging>pom</packaging>
   <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <uber-jar>${project.build.directory}/${project.artifactId}-${project.version}-uber.jar</uber-jar>
+    <gen-resources-dir>${project.build.directory}/gen-resources</gen-resources-dir>
+  </properties>
   <build>
-    <sourceDirectory>${project.build.directory}/sources</sourceDirectory>
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-dependency-plugin</artifactId>
+        <artifactId>maven-resources-plugin</artifactId>
+        <version>2.7</version>
         <executions>
           <execution>
-            <id>unpack-dependencies</id>
+            <id>massage-manifest</id>
+            <phase>generate-resources</phase>
             <goals>
-              <goal>unpack-dependencies</goal>
+              <goal>copy-resources</goal>
             </goals>
             <configuration>
-              <excludes>**/MANIFEST.MF,javax/**</excludes>
-              <excludeArtifactIds>javax</excludeArtifactIds>
-              <excludeGroupIds>javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn,org.slf4j,org.ow2.asm</excludeGroupIds>
-              <outputDirectory>${project.build.directory}/classes</outputDirectory>
-              <overWriteReleases>false</overWriteReleases>
-              <overWriteSnapshots>true</overWriteSnapshots>
-            </configuration>
-          </execution>
-          <execution>
-            <id>unpack-source</id>
-            <phase>generate-sources</phase>
-            <goals>
-              <goal>unpack-dependencies</goal>
-            </goals>
-            <configuration>
-              <classifier>sources</classifier>
-              <includes>**/*</includes>
-              <excludes>META-INF/**,**/Servlet3Continuation*,**/Jetty6Continuation*</excludes>
-              <includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.websocket</includeGroupIds>
-              <excludeArtifactIds>javax</excludeArtifactIds>
-              <excludeGroupIds>javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn,org.slf4j,org.ow2.asm</excludeGroupIds>
-              <outputDirectory>${project.build.directory}/sources</outputDirectory>
-              <overWriteReleases>true</overWriteReleases>
-              <overWriteSnapshots>true</overWriteSnapshots>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins
-        </groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>package</id>
-            <phase>package</phase>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-            <configuration>
-              <archive>
-                <manifest>
-                </manifest>
-                <manifestEntries>
-                  <mode>development</mode>
-                  <url>http://eclipse.org/jetty</url>
-                  <Built-By>${user.name}</Built-By>
-                  <package>org.eclipse.jetty</package>
-                  <Bundle-License>http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/NOTICE.txt</Bundle-License>
-                  <Bundle-Name>Jetty</Bundle-Name>
-                </manifestEntries>
-              </archive>
+              <outputDirectory>${gen-resources-dir}</outputDirectory>
+              <resources>
+                <resource>
+                  <directory>src/main/resources</directory>
+                  <filtering>true</filtering>
+                </resource>
+              </resources>
             </configuration>
           </execution>
         </executions>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-javadoc-plugin</artifactId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <version>2.3</version>
+        <dependencies>
+          <dependency>
+            <groupId>org.eclipse.jetty.toolchain</groupId>
+            <artifactId>jetty-build-support</artifactId>
+            <version>1.3</version>
+          </dependency>
+        </dependencies>
         <executions>
           <execution>
-            <id>javadoc-jar</id>
-            <phase>compile</phase>
+            <id>uberjar</id>
+            <phase>package</phase>
             <goals>
-              <goal>jar</goal>
+              <goal>shade</goal>
             </goals>
+            <configuration>
+              <shadedArtifactAttached>true</shadedArtifactAttached>
+              <shadedClassifierName>uber</shadedClassifierName>
+              <outputFile>${uber-jar}</outputFile>
+              <transformers>
+                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
+                <transformer implementation="org.eclipse.jetty.toolchain.shade.IncludeDirectoryTransformer">
+                  <directory>${gen-resources-dir}</directory>
+                </transformer>
+              </transformers>
+              <filters>
+                <filter>
+                  <artifact>*:*</artifact>
+                  <excludes>
+                    <exclude>META-INF/LICENSE.txt</exclude>
+                    <exclude>META-INF/*.MF</exclude>
+                    <exclude>META-INF/*.SF</exclude>
+                    <exclude>META-INF/*.DSA</exclude>
+                    <exclude>META-INF/*.RSA</exclude>
+                  </excludes>
+                </filter>
+              </filters>
+              <artifactSet>
+                <excludes>
+                  <exclude>javax:*</exclude>
+                  <exclude>org.eclipse.jetty.orbit:*</exclude>
+                  <exclude>org.mortbay.jetty:*</exclude>
+                  <exclude>org.mortbay.jetty.alpn:*</exclude>
+                  <exclude>org.slf4j:*</exclude>
+                  <exclude>org.ow2.asm:*</exclude>
+                  <exclude>*:javax</exclude>
+                </excludes>
+              </artifactSet>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>1.9.1</version>
+        <executions>
+          <execution>
+            <id>attach-artifacts</id>
+            <phase>package</phase>
+            <goals>
+              <goal>attach-artifact</goal>
+            </goals>
+            <configuration>
+              <artifacts>
+                <artifact>
+                  <file>${uber-jar}</file>
+                  <type>jar</type>
+                  <classifier>uber</classifier>
+                </artifact>
+              </artifacts>
+            </configuration>
           </execution>
         </executions>
       </plugin>
     </plugins>
-    <pluginManagement>
-      <plugins>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-pmd-plugin</artifactId>
-          <configuration>
-            <skip>true</skip>
-          </configuration>
-        </plugin>
-      </plugins>
-    </pluginManagement>
   </build>
 
   <dependencies>
@@ -111,120 +127,109 @@
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-client</artifactId>
       <version>${project.version}</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-deploy</artifactId>
       <version>${project.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.websocket</groupId>
-      <artifactId>websocket-servlet</artifactId>
-      <version>${project.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.websocket</groupId>
-      <artifactId>javax-websocket-server-impl</artifactId>
-      <version>${project.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.websocket</groupId>
-      <artifactId>websocket-client</artifactId>
-      <version>${project.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.spdy</groupId>
-      <artifactId>spdy-http-server</artifactId>
-      <version>${project.version}</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-jmx</artifactId>
       <version>${project.version}</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-plus</artifactId>
       <version>${project.version}</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-annotations</artifactId>
       <version>${project.version}</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-util</artifactId>
       <version>${project.version}</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-jaspi</artifactId>
       <version>${project.version}</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-jndi</artifactId>
       <version>${project.version}</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-rewrite</artifactId>
       <version>${project.version}</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-servlets</artifactId>
       <version>${project.version}</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-quickstart</artifactId>
       <version>${project.version}</version>
-      <scope>provided</scope>
+    </dependency>
+    <!-- websocket support -->
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>websocket-servlet</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>javax-websocket-server-impl</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>websocket-client</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <!-- http/2 support -->
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-alpn-client</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.http2</groupId>
+      <artifactId>http2-server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.http2</groupId>
+      <artifactId>http2-client</artifactId>
+      <version>${project.version}</version>
     </dependency>
     <!-- dependencies that jetty-all needs (some optional) -->
     <dependency>
       <groupId>javax.websocket</groupId>
       <artifactId>javax.websocket-api</artifactId>
-      <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>javax.servlet-api</artifactId>
-      <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>javax.transaction</groupId>
       <artifactId>javax.transaction-api</artifactId>
-      <scope>compile</scope>
-      <optional>true</optional>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty.orbit</groupId>
       <artifactId>javax.mail.glassfish</artifactId>
-      <scope>compile</scope>
-      <optional>true</optional>
     </dependency>
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
-      <scope>compile</scope>
-      <optional>true</optional>
     </dependency>
   </dependencies>
 </project>
diff --git a/aggregates/jetty-all/src/main/resources/META-INF/MANIFEST.MF b/aggregates/jetty-all/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..6ffd7d5
--- /dev/null
+++ b/aggregates/jetty-all/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,8 @@
+Manifest-Version: 1.0

+mode: development

+Implementation-Title: Jetty Uber All

+Implementation-Vendor: Eclipse Jetty Project

+Implementation-Vendor-Id: org.eclipse.jetty

+Implementation-Version: @project.version@

+X-License: https://www.eclipse.org/jetty/licenses.php

+

diff --git a/aggregates/jetty-all/src/main/resources/META-INF/javax.annotation-api-LICENSE.txt b/aggregates/jetty-all/src/main/resources/META-INF/javax.annotation-api-LICENSE.txt
new file mode 100644
index 0000000..a0ccc93
--- /dev/null
+++ b/aggregates/jetty-all/src/main/resources/META-INF/javax.annotation-api-LICENSE.txt
@@ -0,0 +1,263 @@
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+
+1. Definitions.
+
+   1.1. Contributor. means each individual or entity that creates or contributes to the creation of Modifications.
+
+   1.2. Contributor Version. means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor.
+
+   1.3. Covered Software. means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof.
+
+   1.4. Executable. means the Covered Software in any form other than Source Code.
+
+   1.5. Initial Developer. means the individual or entity that first makes Original Software available under this License.
+
+   1.6. Larger Work. means a work which combines Covered Software or portions thereof with code not governed by the terms of this License.
+
+   1.7. License. means this document.
+
+   1.8. Licensable. means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
+
+   1.9. Modifications. means the Source Code and Executable form of any of the following:
+
+        A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications;
+
+        B. Any new file that contains any part of the Original Software or previous Modification; or
+
+        C. Any new file that is contributed or otherwise made available under the terms of this License.
+
+   1.10. Original Software. means the Source Code and Executable form of computer software code that is originally released under this License.
+
+   1.11. Patent Claims. means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
+
+   1.12. Source Code. means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code.
+
+   1.13. You. (or .Your.) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, .You. includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, .control. means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
+
+2. License Grants.
+
+      2.1. The Initial Developer Grant.
+
+      Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license:
+
+         (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and
+
+         (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof).
+
+        (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License.
+
+        (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices.
+
+    2.2. Contributor Grant.
+
+    Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:
+
+        (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and
+
+        (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
+
+        (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party.
+
+        (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor.
+
+3. Distribution Obligations.
+
+      3.1. Availability of Source Code.
+      Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange.
+
+      3.2. Modifications.
+      The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License.
+
+      3.3. Required Notices.
+      You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer.
+
+      3.4. Application of Additional Terms.
+      You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients. rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
+
+      3.5. Distribution of Executable Versions.
+      You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient.s rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
+
+      3.6. Larger Works.
+      You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software.
+
+4. Versions of the License.
+
+      4.1. New Versions.
+      Sun Microsystems, Inc. is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License.
+
+      4.2. Effect of New Versions.
+      You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward.
+
+      4.3. Modified Versions.
+      When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License.
+
+5. DISCLAIMER OF WARRANTY.
+
+   COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN .AS IS. BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+6. TERMINATION.
+
+      6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
+
+      6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as .Participant.) alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant.
+
+      6.3. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+   UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY.S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+8. U.S. GOVERNMENT END USERS.
+
+   The Covered Software is a .commercial item,. as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of .commercial computer software. (as that term is defined at 48 C.F.R. ? 252.227-7014(a)(1)) and .commercial computer software documentation. as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License.
+
+9. MISCELLANEOUS.
+
+   This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction.s conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys. fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software.
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+   As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
+
+   NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)
+
+   The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California.
+
+
+The GNU General Public License (GPL) Version 2, June 1991
+
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification follow.
+
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+   a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
+
+   b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
+
+   c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
+
+   a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+   b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+   c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
+
+   One line to give the program's name and a brief idea of what it does.
+
+   Copyright (C)
+
+   This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
+
+   Gnomovision version 69, Copyright (C) year name of author
+   Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
+
+   Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+   signature of Ty Coon, 1 April 1989
+   Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.
+
+
+"CLASSPATH" EXCEPTION TO THE GPL VERSION 2
+
+Certain source files distributed by Sun Microsystems, Inc. are subject to the following clarification and special exception to the GPL Version 2, but only where Sun has expressly included in the particular source file's header the words
+
+"Sun designates this particular file as subject to the "Classpath" exception as provided by Sun in the License file that accompanied this code."
+
+Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License Version 2 cover the whole combination.
+
+As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module.? An independent module is a module which is not derived from or based on this library.? If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so.? If you do not wish to do so, delete this exception statement from your version.
diff --git a/aggregates/jetty-all/src/main/resources/META-INF/javax.servlet-api-LICENSE.txt b/aggregates/jetty-all/src/main/resources/META-INF/javax.servlet-api-LICENSE.txt
new file mode 100644
index 0000000..a0ccc93
--- /dev/null
+++ b/aggregates/jetty-all/src/main/resources/META-INF/javax.servlet-api-LICENSE.txt
@@ -0,0 +1,263 @@
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+
+1. Definitions.
+
+   1.1. Contributor. means each individual or entity that creates or contributes to the creation of Modifications.
+
+   1.2. Contributor Version. means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor.
+
+   1.3. Covered Software. means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof.
+
+   1.4. Executable. means the Covered Software in any form other than Source Code.
+
+   1.5. Initial Developer. means the individual or entity that first makes Original Software available under this License.
+
+   1.6. Larger Work. means a work which combines Covered Software or portions thereof with code not governed by the terms of this License.
+
+   1.7. License. means this document.
+
+   1.8. Licensable. means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
+
+   1.9. Modifications. means the Source Code and Executable form of any of the following:
+
+        A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications;
+
+        B. Any new file that contains any part of the Original Software or previous Modification; or
+
+        C. Any new file that is contributed or otherwise made available under the terms of this License.
+
+   1.10. Original Software. means the Source Code and Executable form of computer software code that is originally released under this License.
+
+   1.11. Patent Claims. means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
+
+   1.12. Source Code. means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code.
+
+   1.13. You. (or .Your.) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, .You. includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, .control. means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
+
+2. License Grants.
+
+      2.1. The Initial Developer Grant.
+
+      Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license:
+
+         (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and
+
+         (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof).
+
+        (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License.
+
+        (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices.
+
+    2.2. Contributor Grant.
+
+    Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:
+
+        (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and
+
+        (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
+
+        (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party.
+
+        (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor.
+
+3. Distribution Obligations.
+
+      3.1. Availability of Source Code.
+      Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange.
+
+      3.2. Modifications.
+      The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License.
+
+      3.3. Required Notices.
+      You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer.
+
+      3.4. Application of Additional Terms.
+      You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients. rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
+
+      3.5. Distribution of Executable Versions.
+      You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient.s rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
+
+      3.6. Larger Works.
+      You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software.
+
+4. Versions of the License.
+
+      4.1. New Versions.
+      Sun Microsystems, Inc. is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License.
+
+      4.2. Effect of New Versions.
+      You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward.
+
+      4.3. Modified Versions.
+      When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License.
+
+5. DISCLAIMER OF WARRANTY.
+
+   COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN .AS IS. BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+6. TERMINATION.
+
+      6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
+
+      6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as .Participant.) alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant.
+
+      6.3. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+   UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY.S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+8. U.S. GOVERNMENT END USERS.
+
+   The Covered Software is a .commercial item,. as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of .commercial computer software. (as that term is defined at 48 C.F.R. ? 252.227-7014(a)(1)) and .commercial computer software documentation. as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License.
+
+9. MISCELLANEOUS.
+
+   This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction.s conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys. fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software.
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+   As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
+
+   NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)
+
+   The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California.
+
+
+The GNU General Public License (GPL) Version 2, June 1991
+
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification follow.
+
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+   a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
+
+   b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
+
+   c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
+
+   a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+   b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+   c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
+
+   One line to give the program's name and a brief idea of what it does.
+
+   Copyright (C)
+
+   This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
+
+   Gnomovision version 69, Copyright (C) year name of author
+   Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
+
+   Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+   signature of Ty Coon, 1 April 1989
+   Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.
+
+
+"CLASSPATH" EXCEPTION TO THE GPL VERSION 2
+
+Certain source files distributed by Sun Microsystems, Inc. are subject to the following clarification and special exception to the GPL Version 2, but only where Sun has expressly included in the particular source file's header the words
+
+"Sun designates this particular file as subject to the "Classpath" exception as provided by Sun in the License file that accompanied this code."
+
+Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License Version 2 cover the whole combination.
+
+As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module.? An independent module is a module which is not derived from or based on this library.? If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so.? If you do not wish to do so, delete this exception statement from your version.
diff --git a/aggregates/jetty-all/src/main/resources/META-INF/javax.transaction-api-LICENSE.txt b/aggregates/jetty-all/src/main/resources/META-INF/javax.transaction-api-LICENSE.txt
new file mode 100644
index 0000000..a0ccc93
--- /dev/null
+++ b/aggregates/jetty-all/src/main/resources/META-INF/javax.transaction-api-LICENSE.txt
@@ -0,0 +1,263 @@
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+
+1. Definitions.
+
+   1.1. Contributor. means each individual or entity that creates or contributes to the creation of Modifications.
+
+   1.2. Contributor Version. means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor.
+
+   1.3. Covered Software. means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof.
+
+   1.4. Executable. means the Covered Software in any form other than Source Code.
+
+   1.5. Initial Developer. means the individual or entity that first makes Original Software available under this License.
+
+   1.6. Larger Work. means a work which combines Covered Software or portions thereof with code not governed by the terms of this License.
+
+   1.7. License. means this document.
+
+   1.8. Licensable. means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
+
+   1.9. Modifications. means the Source Code and Executable form of any of the following:
+
+        A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications;
+
+        B. Any new file that contains any part of the Original Software or previous Modification; or
+
+        C. Any new file that is contributed or otherwise made available under the terms of this License.
+
+   1.10. Original Software. means the Source Code and Executable form of computer software code that is originally released under this License.
+
+   1.11. Patent Claims. means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
+
+   1.12. Source Code. means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code.
+
+   1.13. You. (or .Your.) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, .You. includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, .control. means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
+
+2. License Grants.
+
+      2.1. The Initial Developer Grant.
+
+      Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license:
+
+         (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and
+
+         (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof).
+
+        (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License.
+
+        (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices.
+
+    2.2. Contributor Grant.
+
+    Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:
+
+        (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and
+
+        (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
+
+        (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party.
+
+        (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor.
+
+3. Distribution Obligations.
+
+      3.1. Availability of Source Code.
+      Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange.
+
+      3.2. Modifications.
+      The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License.
+
+      3.3. Required Notices.
+      You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer.
+
+      3.4. Application of Additional Terms.
+      You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients. rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
+
+      3.5. Distribution of Executable Versions.
+      You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient.s rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
+
+      3.6. Larger Works.
+      You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software.
+
+4. Versions of the License.
+
+      4.1. New Versions.
+      Sun Microsystems, Inc. is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License.
+
+      4.2. Effect of New Versions.
+      You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward.
+
+      4.3. Modified Versions.
+      When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License.
+
+5. DISCLAIMER OF WARRANTY.
+
+   COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN .AS IS. BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+6. TERMINATION.
+
+      6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
+
+      6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as .Participant.) alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant.
+
+      6.3. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+   UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY.S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+8. U.S. GOVERNMENT END USERS.
+
+   The Covered Software is a .commercial item,. as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of .commercial computer software. (as that term is defined at 48 C.F.R. ? 252.227-7014(a)(1)) and .commercial computer software documentation. as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License.
+
+9. MISCELLANEOUS.
+
+   This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction.s conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys. fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software.
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+   As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
+
+   NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)
+
+   The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California.
+
+
+The GNU General Public License (GPL) Version 2, June 1991
+
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification follow.
+
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+   a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
+
+   b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
+
+   c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
+
+   a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+   b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+   c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
+
+   One line to give the program's name and a brief idea of what it does.
+
+   Copyright (C)
+
+   This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
+
+   Gnomovision version 69, Copyright (C) year name of author
+   Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
+
+   Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+   signature of Ty Coon, 1 April 1989
+   Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.
+
+
+"CLASSPATH" EXCEPTION TO THE GPL VERSION 2
+
+Certain source files distributed by Sun Microsystems, Inc. are subject to the following clarification and special exception to the GPL Version 2, but only where Sun has expressly included in the particular source file's header the words
+
+"Sun designates this particular file as subject to the "Classpath" exception as provided by Sun in the License file that accompanied this code."
+
+Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License Version 2 cover the whole combination.
+
+As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module.? An independent module is a module which is not derived from or based on this library.? If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so.? If you do not wish to do so, delete this exception statement from your version.
diff --git a/aggregates/jetty-websocket-all/pom.xml b/aggregates/jetty-websocket-all/pom.xml
index 9631dd3..c3b3490 100644
--- a/aggregates/jetty-websocket-all/pom.xml
+++ b/aggregates/jetty-websocket-all/pom.xml
@@ -24,7 +24,7 @@
             </goals>
             <configuration>
               <excludes>**/MANIFEST.MF</excludes>
-              <excludeGroupIds>org.slf4j,org.eclipse.jetty.orbit,org.mortbay.jetty.npn</excludeGroupIds>
+              <excludeGroupIds>org.slf4j,org.eclipse.jetty.orbit,org.mortbay.jetty.alpn</excludeGroupIds>
               <outputDirectory>${project.build.directory}/classes</outputDirectory>
               <overWriteReleases>false</overWriteReleases>
               <overWriteSnapshots>true</overWriteSnapshots>
@@ -42,7 +42,7 @@
               <excludes>META-INF/**,**/Servlet3Continuation*,**/Jetty6Continuation*</excludes>
               <includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.websocket</includeGroupIds>
               <excludeArtifactIds>javax</excludeArtifactIds>
-              <excludeGroupIds>javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn</excludeGroupIds>
+              <excludeGroupIds>javax,org.eclipse.jetty.orbit,org.mortbay.jetty.alpn</excludeGroupIds>
               <outputDirectory>${project.build.directory}/sources</outputDirectory>
               <overWriteReleases>true</overWriteReleases>
               <overWriteSnapshots>true</overWriteSnapshots>
diff --git a/apache-jsp/pom.xml b/apache-jsp/pom.xml
index d32b7d7..8309bb4 100644
--- a/apache-jsp/pom.xml
+++ b/apache-jsp/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>apache-jsp</artifactId>
@@ -18,12 +18,6 @@
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
         <extensions>true</extensions>
-        <executions>
-          <execution>
-            <id>generate-manifest</id>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
             <configuration>
               <instructions>
                 <Bundle-Description>Jetty-specific ServletContainerInitializer for Jasper</Bundle-Description>
@@ -35,25 +29,29 @@
                 <_nouses>true</_nouses>
               </instructions>
             </configuration>
-          </execution>
-        </executions>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
         <executions>
           <execution>
-            <id>artifact-jar</id>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-          <execution>
             <id>test-jar</id>
             <goals>
               <goal>test-jar</goal>
             </goals>
           </execution>
+          <execution>
+            <id>nolog-jar</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <classifier>nolog</classifier>
+              <excludes>
+                <exclude>META-INF/services/org.apache.juli.logging.Log</exclude>
+              </excludes>
+            </configuration>
+          </execution>
         </executions>
         <configuration>
           <archive>
@@ -62,23 +60,6 @@
         </configuration>
       </plugin>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <groupId>org.jacoco</groupId>
         <artifactId>jacoco-maven-plugin</artifactId>
         <configuration>
@@ -94,11 +75,6 @@
         <artifactId>jetty-util</artifactId>
         <version>${project.version}</version>
     </dependency>
-    <dependency>
-        <groupId>org.eclipse.jetty</groupId>
-        <artifactId>jetty-server</artifactId>
-        <version>${project.version}</version>
-    </dependency>
     
     <!-- Schemas -->
     <dependency>
@@ -120,8 +96,28 @@
 
     <!-- Eclipse Java Compiler (for JSP Compilation) -->
     <dependency>
-      <groupId>org.eclipse.jetty.orbit</groupId>
-      <artifactId>org.eclipse.jdt.core</artifactId>
+      <groupId>org.eclipse.jdt.core.compiler</groupId>
+      <artifactId>ecj</artifactId>
+    </dependency>
+
+    <!-- tests -->
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlet</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlet</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
     </dependency>
   </dependencies>
 </project>
diff --git a/apache-jsp/src/main/config/modules/apache-jsp.mod b/apache-jsp/src/main/config/modules/apache-jsp.mod
new file mode 100644
index 0000000..5123670
--- /dev/null
+++ b/apache-jsp/src/main/config/modules/apache-jsp.mod
@@ -0,0 +1,10 @@
+#
+# Apache JSP Module
+#
+
+[name]
+apache-jsp
+
+[lib]
+lib/apache-jsp/*.jar
+
diff --git a/apache-jsp/src/main/config/modules/jsp-impl/apache-jsp.mod b/apache-jsp/src/main/config/modules/jsp-impl/apache-jsp.mod
deleted file mode 100644
index aed547c..0000000
--- a/apache-jsp/src/main/config/modules/jsp-impl/apache-jsp.mod
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# Apache JSP Module
-#
-
-[name]
-jsp-impl
-
-[lib]
-lib/apache-jsp/*.jar
-
diff --git a/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JettyJasperInitializer.java b/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JettyJasperInitializer.java
index 888bdeb..6a42085 100644
--- a/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JettyJasperInitializer.java
+++ b/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JettyJasperInitializer.java
@@ -29,18 +29,17 @@
 import org.apache.jasper.servlet.JasperInitializer;
 import org.apache.jasper.servlet.TldPreScanned;
 import org.apache.jasper.servlet.TldScanner;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
 import org.xml.sax.SAXException;
 
 /**
  * JettyJasperInitializer
- *
  */
 public class JettyJasperInitializer extends JasperInitializer
 {
-   private static final Logger LOG = Log.getLogger(JettyJasperInitializer.class);
-    
+   private static final Log LOG = LogFactory.getLog(JasperInitializer.class);
     /**
      * NullTldScanner
      *
@@ -91,8 +90,6 @@
     /**
      * Make a TldScanner, and prefeed it the tlds that have already been discovered in jar files
      * by the MetaInfConfiguration.
-     * 
-     * @see org.apache.jasper.servlet.JasperInitializer#prepareScanner(javax.servlet.ServletContext, boolean, boolean, boolean)
      */
     @Override
     public TldScanner newTldScanner(ServletContext context, boolean namespaceAware, boolean validate, boolean blockExternal)
@@ -114,6 +111,4 @@
         if (LOG.isDebugEnabled()) LOG.debug("Defaulting to jasper tld scanning");
         return super.newTldScanner(context, namespaceAware, validate, blockExternal);
     }
-    
-
 }
diff --git a/apache-jsp/src/main/java/org/eclipse/jetty/jsp/JettyJspServlet.java b/apache-jsp/src/main/java/org/eclipse/jetty/jsp/JettyJspServlet.java
index ca6b244..6205391 100644
--- a/apache-jsp/src/main/java/org/eclipse/jetty/jsp/JettyJspServlet.java
+++ b/apache-jsp/src/main/java/org/eclipse/jetty/jsp/JettyJspServlet.java
@@ -19,15 +19,17 @@
 package org.eclipse.jetty.jsp;
 
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 
+import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.jasper.servlet.JspServlet;
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.util.URIUtil;
-import org.eclipse.jetty.util.resource.Resource;
+
 
 /**
  * JettyJspServlet
@@ -59,10 +61,10 @@
 
         String servletPath=null;
         String pathInfo=null;
-        if (request.getAttribute("javax.servlet.include.request_uri")!=null)
+        if (request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI)!=null)
         {
-            servletPath=(String)request.getAttribute("javax.servlet.include.servlet_path");
-            pathInfo=(String)request.getAttribute("javax.servlet.include.path_info");
+            servletPath=(String)request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
+            pathInfo=(String)request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
             if (servletPath==null)
             {
                 servletPath=request.getServletPath();
@@ -75,7 +77,7 @@
             pathInfo = request.getPathInfo();
         }
         
-        String pathInContext = URIUtil.addPaths(servletPath,pathInfo);
+        String pathInContext = addPaths(servletPath,pathInfo);
     
         String jspFile = getInitParameter("jspFile");
 
@@ -83,7 +85,7 @@
         //otherwise the default servlet might handle it
         if (jspFile == null)
         {
-            if (pathInContext.endsWith("/"))
+            if (pathInContext != null && pathInContext.endsWith("/"))
             {
                 //dispatch via forward to the default servlet
                 getServletContext().getNamedDispatcher("default").forward(req, resp);
@@ -92,13 +94,16 @@
             else
             {      
                 //check if it resolves to a directory
-                Resource resource = ((ContextHandler.Context)getServletContext()).getContextHandler().getResource(pathInContext);    
-
-                if (resource!=null && resource.isDirectory())
+                String realPath = getServletContext().getRealPath(pathInContext);
+                if (realPath != null)
                 {
-                    //dispatch via forward to the default servlet
-                    getServletContext().getNamedDispatcher("default").forward(req, resp);
-                    return;
+                    Path asPath = Paths.get(realPath);
+                    if (Files.exists(asPath) && Files.isDirectory(asPath))
+                    {
+                        //dispatch via forward to the default servlet
+                        getServletContext().getNamedDispatcher("default").forward(req, resp);
+                        return;
+                    }
                 }
             }
         }
@@ -107,5 +112,19 @@
         super.service(req, resp);
     }
 
-    
+    /**
+     * @param servletPath the servletPath of the request
+     * @param pathInfo the pathInfo of the request
+     * @return servletPath with pathInfo appended
+     */
+    private String addPaths(String servletPath, String pathInfo)
+    {
+        if (servletPath.length()==0)
+            return pathInfo;
+       
+        if (pathInfo==null)
+            return servletPath;
+        
+        return servletPath+pathInfo;
+    }
 }
diff --git a/apache-jsp/src/test/java/org/eclipse/jetty/jsp/TestJettyJspServlet.java b/apache-jsp/src/test/java/org/eclipse/jetty/jsp/TestJettyJspServlet.java
new file mode 100644
index 0000000..b440f23
--- /dev/null
+++ b/apache-jsp/src/test/java/org/eclipse/jetty/jsp/TestJettyJspServlet.java
@@ -0,0 +1,123 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.jsp;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspFactory;
+
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.servlet.ServletTester;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.apache.jasper.runtime.JspFactoryImpl;
+import org.apache.tomcat.InstanceManager;
+import org.apache.tomcat.SimpleInstanceManager;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+
+public class TestJettyJspServlet
+{
+    File _dir;
+    ServletTester _tester;
+    
+    public static class DfltServlet extends HttpServlet
+    {
+
+        public DfltServlet()
+        {
+            super();
+        }
+        
+        /** 
+         * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+         */
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            resp.setContentType("html/text");
+            resp.getOutputStream().println("This.Is.The.Default.");
+        }
+        
+    }
+    
+    @Before
+    public void setUp () throws Exception
+    {
+        JspFactory.setDefaultFactory(new JspFactoryImpl());
+        _dir = MavenTestingUtils.getTestResourceDir("base");
+        _tester = new ServletTester("/context");
+        _tester.getContext().setClassLoader(new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()));
+        ServletHolder jspHolder = _tester.getContext().addServlet(JettyJspServlet.class, "/*");
+        jspHolder.setInitParameter("scratchdir", MavenTestingUtils.getTargetTestingDir().getAbsolutePath());
+        _tester.getContext().setResourceBase(_dir.getAbsolutePath());
+        _tester.getContext().setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager());
+        ServletHolder dfltHolder = new ServletHolder();
+        dfltHolder.setName("default");
+        dfltHolder.setHeldClass( DfltServlet.class);
+        _tester.getContext().addServlet(dfltHolder, "/");
+
+        _tester.start();
+    }
+    
+    @After
+    public void tearDown() throws Exception
+    {
+        if (_tester != null)
+            _tester.stop();
+    }
+
+    @Test
+    public void testWithJsp() throws Exception
+    {
+        //test that an ordinary jsp is served by jsp servlet
+        String request = "" +
+                "GET /context/foo.jsp HTTP/1.1\r\n" +
+                "Host: localhost\r\n" +
+                "Connection: close\r\n" +
+                "\r\n";
+        String response = _tester.getResponses(request);
+        assertTrue(!response.contains("This.Is.The.Default."));
+    }
+    
+    
+    @Test
+    public void testWithDirectory() throws Exception
+    {
+        //test that a dir is served by the default servlet
+        String request = "" +
+                "GET /context/dir HTTP/1.1\r\n" +
+                "Host: localhost\r\n" +
+                "Connection: close\r\n" +
+                "\r\n";
+        String response = _tester.getResponses(request);
+        assertTrue(response.contains("This.Is.The.Default."));
+    }
+
+}
diff --git a/apache-jsp/src/test/java/org/eclipse/jetty/jsp/TestJspFileNameToClass.java b/apache-jsp/src/test/java/org/eclipse/jetty/jsp/TestJspFileNameToClass.java
new file mode 100644
index 0000000..cb14e75
--- /dev/null
+++ b/apache-jsp/src/test/java/org/eclipse/jetty/jsp/TestJspFileNameToClass.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.jsp;
+
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestJspFileNameToClass
+{
+
+    @Test
+    public void testJspFileNameToClassName() throws Exception
+    {
+        ServletHolder h = new ServletHolder();
+        h.setName("test");
+
+
+        Assert.assertEquals(null,  h.getClassNameForJsp(null));
+
+        Assert.assertEquals(null,  h.getClassNameForJsp(""));
+
+        Assert.assertEquals(null,  h.getClassNameForJsp("/blah/"));
+
+        Assert.assertEquals(null,  h.getClassNameForJsp("//blah///"));
+
+        Assert.assertEquals(null,  h.getClassNameForJsp("/a/b/c/blah/"));
+
+        Assert.assertEquals("org.apache.jsp.a.b.c.blah",  h.getClassNameForJsp("/a/b/c/blah"));
+
+        Assert.assertEquals("org.apache.jsp.blah_jsp", h.getClassNameForJsp("/blah.jsp"));
+
+        Assert.assertEquals("org.apache.jsp.blah_jsp", h.getClassNameForJsp("//blah.jsp"));
+
+        Assert.assertEquals("org.apache.jsp.blah_jsp", h.getClassNameForJsp("blah.jsp"));
+
+        Assert.assertEquals("org.apache.jsp.a.b.c.blah_jsp", h.getClassNameForJsp("/a/b/c/blah.jsp"));
+
+        Assert.assertEquals("org.apache.jsp.a.b.c.blah_jsp", h.getClassNameForJsp("a/b/c/blah.jsp"));
+    }
+    
+}
diff --git a/jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml b/apache-jsp/src/test/resources/base/dir/empty.txt
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml
copy to apache-jsp/src/test/resources/base/dir/empty.txt
diff --git a/apache-jsp/src/test/resources/base/foo.jsp b/apache-jsp/src/test/resources/base/foo.jsp
new file mode 100644
index 0000000..fb73b0b
--- /dev/null
+++ b/apache-jsp/src/test/resources/base/foo.jsp
@@ -0,0 +1,23 @@
+<html><head>
+<%@ page import="java.util.Enumeration" %>
+</head><body>
+<h1>JSP Dump</h1>
+
+<table border="1">
+<tr><th>Request URI:</th><td><%= request.getRequestURI() %></td></tr>
+<tr><th>ServletPath:</th><td><%= request.getServletPath() %></td></tr>
+<tr><th>PathInfo:</th><td><%= request.getPathInfo() %></td></tr>
+
+<%
+   Enumeration e =request.getParameterNames();
+   while(e.hasMoreElements())
+   {
+       String name = (String)e.nextElement();
+%>
+<tr>
+  <th>getParameter("<%= name %>")</th>
+  <td><%= request.getParameter(name) %></td></tr>
+<% } %>
+
+</table>
+</body></html>
diff --git a/apache-jstl/pom.xml b/apache-jstl/pom.xml
index 4f34320..34f89a8 100644
--- a/apache-jstl/pom.xml
+++ b/apache-jstl/pom.xml
@@ -2,32 +2,25 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>apache-jstl</artifactId>
   <name>Apache :: JSTL module</name>
   <url>http://tomcat.apache.org/taglibs/standard/</url>
   <packaging>jar</packaging>
+  <properties>
+   <bundle-symbolic-name>${project.groupId}.apache.jstl</bundle-symbolic-name>
+  </properties>
 
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-plugin</artifactId>
+          <configuration>
+             <useSystemClassLoader>false</useSystemClassLoader>
+          </configuration>
       </plugin>
       <plugin>
         <groupId>org.jacoco</groupId>
@@ -51,6 +44,34 @@
        <groupId>org.apache.taglibs</groupId>
        <artifactId>taglibs-standard-impl</artifactId>
     </dependency>
+    
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+       <scope>test</scope>
+    </dependency>
+    
+    <dependency>
+       <groupId>org.eclipse.jetty</groupId>
+       <artifactId>apache-jsp</artifactId>
+       <version>${project.version}</version>
+       <scope>test</scope>
+    </dependency>
+
+    <dependency>
+       <groupId>org.eclipse.jetty</groupId>
+       <artifactId>jetty-annotations</artifactId>
+       <version>${project.version}</version>
+       <scope>test</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-webapp</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+
   </dependencies>
 
 </project>
diff --git a/apache-jstl/src/main/config/modules/apache-jstl.mod b/apache-jstl/src/main/config/modules/apache-jstl.mod
new file mode 100644
index 0000000..e4a9001
--- /dev/null
+++ b/apache-jstl/src/main/config/modules/apache-jstl.mod
@@ -0,0 +1,9 @@
+#
+# Apache JSTL 
+#
+
+[name]
+apache-jstl
+
+[lib]
+lib/apache-jstl/*.jar
diff --git a/apache-jstl/src/main/config/modules/jsp-impl/apache-jstl.mod b/apache-jstl/src/main/config/modules/jsp-impl/apache-jstl.mod
deleted file mode 100644
index 804b191..0000000
--- a/apache-jstl/src/main/config/modules/jsp-impl/apache-jstl.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Apache JSTL 
-#
-[name]
-jstl-impl
-
-[lib]
-lib/apache-jstl/*.jar
diff --git a/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JspConfig.java b/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JspConfig.java
new file mode 100644
index 0000000..bcbab98
--- /dev/null
+++ b/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JspConfig.java
@@ -0,0 +1,40 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.jstl;
+
+import java.io.File;
+import java.net.URI;
+
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * Attempt at collecting up all of the JSP specific configuration bits and pieces into a single place
+ * for WebAppContext users to utilize.
+ */
+public class JspConfig
+{
+    public static void init(WebAppContext context, URI baseUri, File scratchDir)
+    {
+        context.setAttribute("javax.servlet.context.tempdir", scratchDir);
+        context.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
+ ".*/javax.servlet-[^/]*\\.jar$|.*/servlet-api-[^/]*\\.jar$|.*javax.servlet.jsp.jstl-[^/]*\\.jar|.*taglibs-standard-impl-.*\\.jar");
+        context.setWar(baseUri.toASCIIString());
+        context.setResourceBase(baseUri.toASCIIString());
+    }
+}
diff --git a/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JspIncludeTest.java b/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JspIncludeTest.java
new file mode 100644
index 0000000..cd81563
--- /dev/null
+++ b/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JspIncludeTest.java
@@ -0,0 +1,174 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.jstl;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.net.URI;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.IO;
+import org.eclipse.jetty.toolchain.test.JAR;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.webapp.Configuration;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class JspIncludeTest
+{
+    private static Server server;
+    private static URI baseUri;
+    
+    @BeforeClass
+    public static void startServer() throws Exception
+    {
+        // Setup Server
+        server = new Server();
+        ServerConnector connector = new ServerConnector(server);
+        connector.setPort(0);
+        server.addConnector(connector);
+        
+        // Setup WebAppContext
+        File testWebAppDir = MavenTestingUtils.getProjectDir("src/test/webapp");
+        
+        // Prepare WebApp libs
+        File libDir = new File(testWebAppDir, "WEB-INF/lib");
+        FS.ensureDirExists(libDir);
+        File testTagLibDir = MavenTestingUtils.getProjectDir("src/test/taglibjar");
+        JAR.create(testTagLibDir, new File(libDir, "testtaglib.jar"));
+        
+        // Configure WebAppContext
+
+        Configuration.ClassList classlist = Configuration.ClassList
+                .setServerDefault(server);
+
+        classlist.addBefore(
+                "org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
+                "org.eclipse.jetty.annotations.AnnotationConfiguration");
+        
+        WebAppContext context = new WebAppContext();
+        context.setContextPath("/");
+        
+        File scratchDir = MavenTestingUtils.getTargetFile("tests/" + JspIncludeTest.class.getSimpleName() + "-scratch");
+        FS.ensureEmpty(scratchDir);
+        JspConfig.init(context, testWebAppDir.toURI(), scratchDir);
+        
+        server.setHandler(context);
+        
+        // Start Server
+        server.start();
+        
+        // Figure out Base URI
+        String host = connector.getHost();
+        if (host == null)
+        {
+            host = "localhost";
+        }
+        int port = connector.getLocalPort();
+        baseUri = new URI(String.format("http://%s:%d/", host, port));
+    }
+    
+    @AfterClass
+    public static void stopServer() throws Exception
+    {
+        server.stop();
+    }
+    
+    @Test
+    public void testTopWithIncluded() throws IOException
+    {
+        URI uri = baseUri.resolve("/top.jsp");
+        // System.out.println("GET (String): " + uri.toASCIIString());
+
+        InputStream in = null;
+        InputStreamReader reader = null;
+        HttpURLConnection connection = null;
+
+        try
+        {
+            connection = (HttpURLConnection) uri.toURL().openConnection();
+            connection.connect();
+            if (HttpURLConnection.HTTP_OK != connection.getResponseCode())
+            {
+                String body = getPotentialBody(connection);
+                String err = String.format("GET request failed (%d %s) %s%n%s", connection.getResponseCode(), connection.getResponseMessage(),
+                        uri.toASCIIString(), body);
+                throw new IOException(err);
+            }
+            in = connection.getInputStream();
+            reader = new InputStreamReader(in);
+            StringWriter writer = new StringWriter();
+            IO.copy(reader, writer);
+
+            String response = writer.toString();
+            // System.out.printf("Response%n%s",response);
+            assertThat("Response", response, containsString("<h2> Hello, this is the top page."));
+            assertThat("Response", response, containsString("<h3> This is the included page"));
+
+            assertThat("Response Header[main-page-key]", connection.getHeaderField("main-page-key"), is("main-page-value"));
+            assertThat("Response Header[included-page-key]", connection.getHeaderField("included-page-key"), is("included-page-value"));
+        }
+        finally
+        {
+            IO.close(reader);
+            IO.close(in);
+        }
+    }
+
+    /**
+     * Attempt to obtain the body text if available. Do not throw an exception if body is unable to be fetched.
+     *
+     * @param connection the connection to fetch the body content from.
+     * @return the body content, if present.
+     */
+    private String getPotentialBody(HttpURLConnection connection)
+    {
+        InputStream in = null;
+        InputStreamReader reader = null;
+        try
+        {
+            in = connection.getInputStream();
+            reader = new InputStreamReader(in);
+            StringWriter writer = new StringWriter();
+            IO.copy(reader, writer);
+            return writer.toString();
+        }
+        catch (IOException e)
+        {
+            return "<no body:" + e.getMessage() + ">";
+        }
+        finally
+        {
+            IO.close(reader);
+            IO.close(in);
+        }
+    }
+}
diff --git a/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JstlTest.java b/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JstlTest.java
new file mode 100644
index 0000000..43cfd15
--- /dev/null
+++ b/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JstlTest.java
@@ -0,0 +1,150 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.jstl;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+
+import javax.servlet.jsp.JspException;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.JAR;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.webapp.Configuration;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class JstlTest
+{
+    private static Server server;
+    private static URI baseUri;
+    
+    @BeforeClass
+    public static void startServer() throws Exception
+    {
+        // Setup Server
+        server = new Server();
+        ServerConnector connector = new ServerConnector(server);
+        connector.setPort(0);
+        server.addConnector(connector);
+        
+        // Setup WebAppContext
+        File testWebAppDir = MavenTestingUtils.getProjectDir("src/test/webapp");
+        
+        // Prepare WebApp libs
+        File libDir = new File(testWebAppDir, "WEB-INF/lib");
+        FS.ensureDirExists(libDir);
+        File testTagLibDir = MavenTestingUtils.getProjectDir("src/test/taglibjar");
+        JAR.create(testTagLibDir,new File(libDir, "testtaglib.jar"));
+        
+        // Configure WebAppContext
+ 
+        Configuration.ClassList classlist = Configuration.ClassList
+                .setServerDefault(server);
+
+        classlist.addBefore(
+                "org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
+                "org.eclipse.jetty.annotations.AnnotationConfiguration");
+        
+        WebAppContext context = new WebAppContext();
+        context.setContextPath("/");
+        
+        File scratchDir = MavenTestingUtils.getTargetFile("tests/" + JstlTest.class.getSimpleName() + "-scratch");
+        FS.ensureEmpty(scratchDir);
+        JspConfig.init(context,testWebAppDir.toURI(),scratchDir);
+        
+        server.setHandler(context);
+        
+        // Start Server
+        server.start();
+        
+        // Figure out Base URI
+        String host = connector.getHost();
+        if (host == null)
+        {
+            host = "localhost";
+        }
+        int port = connector.getLocalPort();
+        baseUri = new URI(String.format("http://%s:%d/",host,port));
+    }
+    
+    @AfterClass
+    public static void stopServer() throws Exception
+    {
+        server.stop();
+    }
+    
+    @Test
+    public void testUrlsBasic() throws IOException
+    {
+        HttpURLConnection http = (HttpURLConnection) baseUri.resolve("/urls.jsp").toURL().openConnection();
+        assertThat("http response", http.getResponseCode(), is(200));
+        try(InputStream input = http.getInputStream())
+        {
+            String resp = IO.toString(input, StandardCharsets.UTF_8);
+            assertThat("Response should be JSP processed", resp, not(containsString("<c:url")));
+            assertThat("Response", resp, containsString("[c:url value] = /ref.jsp;jsessionid="));
+            assertThat("Response", resp, containsString("[c:url param] = ref.jsp;key=value;jsessionid="));
+        }
+    }
+    
+    @Test
+    public void testCatchBasic() throws IOException
+    {
+        HttpURLConnection http = (HttpURLConnection) baseUri.resolve("/catch-basic.jsp").toURL().openConnection();
+        assertThat("http response", http.getResponseCode(), is(200));
+        try(InputStream input = http.getInputStream())
+        {
+            String resp = IO.toString(input, StandardCharsets.UTF_8);
+            assertThat("Response should be JSP processed", resp, not(containsString("<c:catch")));
+            assertThat("Response", resp, containsString("[c:catch] exception : " + JspException.class.getName()));
+            assertThat("Response", resp, containsString("[c:catch] exception.message : In &lt;parseNumber&gt;"));
+        }
+    }
+    
+    @Test
+    @Ignore
+    public void testCatchTaglib() throws IOException
+    {
+        HttpURLConnection http = (HttpURLConnection) baseUri.resolve("/catch-taglib.jsp").toURL().openConnection();
+        assertThat("http response", http.getResponseCode(), is(200));
+        try(InputStream input = http.getInputStream())
+        {
+            String resp = IO.toString(input, StandardCharsets.UTF_8);
+            assertThat("Response should be JSP processed", resp, not(containsString("<c:catch>")));
+            assertThat("Response should be JSP processed", resp, not(containsString("<jtest:errorhandler>")));
+            assertThat("Response", resp, not(containsString("[jtest:errorhandler] exception is null")));
+        }
+    }
+}
diff --git a/apache-jstl/src/test/resources/jetty-logging.properties b/apache-jstl/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..08befa5
--- /dev/null
+++ b/apache-jstl/src/test/resources/jetty-logging.properties
@@ -0,0 +1,3 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+# org.eclipse.jetty.LEVEL=INFO
+# org.eclipse.jetty.util.LEVEL=DEBUG
diff --git a/apache-jstl/src/test/taglibjar/META-INF/etag.tld b/apache-jstl/src/test/taglibjar/META-INF/etag.tld
new file mode 100644
index 0000000..de59ff0
--- /dev/null
+++ b/apache-jstl/src/test/taglibjar/META-INF/etag.tld
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
+  version="2.0">
+
+  <description>eclipse jetty test taglib</description>
+  <tlib-version>1.0</tlib-version>
+
+  <short-name>jtest</short-name>
+  <uri>org.eclipse.jetty.jstl.jtest</uri>
+
+  <tag-file>
+    <name>errorhandler</name>
+    <path>/META-INF/tags/errorhandler.tag</path>
+  </tag-file>
+</taglib>
\ No newline at end of file
diff --git a/apache-jstl/src/test/taglibjar/META-INF/tags/errorhandler.tag b/apache-jstl/src/test/taglibjar/META-INF/tags/errorhandler.tag
new file mode 100644
index 0000000..ddd44dd
--- /dev/null
+++ b/apache-jstl/src/test/taglibjar/META-INF/tags/errorhandler.tag
@@ -0,0 +1,12 @@
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
+
+<c:catch var="tossable">
+  <jsp:doBody />
+</c:catch>
+<c:if test="${tossable != null}">
+[jtest:errorhandler] exception : ${tossable}
+[jtest:errorhandler] exception.message : ${tossable.message}
+</c:if>
+<c:if test="${tossable == null}">
+[jtest:errorhandler] exception is null
+</c:if>
diff --git a/apache-jstl/src/test/webapp/WEB-INF/web.xml b/apache-jstl/src/test/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..b05e490
--- /dev/null
+++ b/apache-jstl/src/test/webapp/WEB-INF/web.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+         version="3.1">
+  <description>Test webapp for JSTL</description>
+</web-app>
\ No newline at end of file
diff --git a/apache-jstl/src/test/webapp/catch-basic.jsp b/apache-jstl/src/test/webapp/catch-basic.jsp
new file mode 100644
index 0000000..06e2bcf
--- /dev/null
+++ b/apache-jstl/src/test/webapp/catch-basic.jsp
@@ -0,0 +1,16 @@
+<%@ page contentType="text/plain; charset=UTF-8" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
+Title: JSTL c:catch test
+
+<c:catch var ="catchException">
+  <fmt:parseNumber var="parsedNum" value="aaa" />
+</c:catch>
+
+<c:if test = "${catchException != null}">
+[c:catch] exception : ${catchException}
+[c:catch] exception.message : ${catchException.message}
+</c:if>
+<c:if test = "${catchException == null}">
+[c:catch] exception is null
+</c:if>
diff --git a/apache-jstl/src/test/webapp/catch-taglib.jsp b/apache-jstl/src/test/webapp/catch-taglib.jsp
new file mode 100644
index 0000000..28f7488
--- /dev/null
+++ b/apache-jstl/src/test/webapp/catch-taglib.jsp
@@ -0,0 +1,11 @@
+<%@ page contentType="text/plain; charset=UTF-8" %>
+<%@ taglib uri="org.eclipse.jetty.jstl.jtest" prefix="jtest" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
+Title: JSTL c:catch test
+
+<jtest:errorhandler>
+  <fmt:parseNumber var="parsedNum" value="aaa" />
+</jtest:errorhandler>
+
+parsedNum = <c:out value="${parsedNum}"/>
\ No newline at end of file
diff --git a/apache-jstl/src/test/webapp/included.jsp b/apache-jstl/src/test/webapp/included.jsp
new file mode 100644
index 0000000..dde5f29
--- /dev/null
+++ b/apache-jstl/src/test/webapp/included.jsp
@@ -0,0 +1,8 @@
+<%
+  String headerPrefix = "";
+  if(request.getDispatcherType() == DispatcherType.INCLUDE)
+    headerPrefix = "org.eclipse.jetty.server.include.";
+
+  response.setHeader(headerPrefix + "included-page-key","included-page-value");
+%>
+<h3> This is the included page
\ No newline at end of file
diff --git a/apache-jstl/src/test/webapp/ref.jsp b/apache-jstl/src/test/webapp/ref.jsp
new file mode 100644
index 0000000..0debf75
--- /dev/null
+++ b/apache-jstl/src/test/webapp/ref.jsp
@@ -0,0 +1,2 @@
+<%@ page contentType="text/plain; charset=UTF-8" %>
+Reference Page: No useful content here, just used for other tests
\ No newline at end of file
diff --git a/apache-jstl/src/test/webapp/top.jsp b/apache-jstl/src/test/webapp/top.jsp
new file mode 100644
index 0000000..53d8d0c
--- /dev/null
+++ b/apache-jstl/src/test/webapp/top.jsp
@@ -0,0 +1,5 @@
+<%
+  application.getRequestDispatcher("/included.jsp").include(request,response);
+  response.setHeader("main-page-key", "main-page-value");
+%>
+<h2> Hello, this is the top page.
diff --git a/apache-jstl/src/test/webapp/urls.jsp b/apache-jstl/src/test/webapp/urls.jsp
new file mode 100644
index 0000000..bc18e9a
--- /dev/null
+++ b/apache-jstl/src/test/webapp/urls.jsp
@@ -0,0 +1,6 @@
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@ page contentType="text/plain; charset=UTF-8" %>
+Title: JSTL c:url Tests
+[c:url value] = <c:url value="/ref.jsp" />
+<c:set var="foo" value="ref.jsp;key=value"/>
+[c:url param] = <c:url value="${foo}"><c:param name="noframe" value="true"/></c:url>
diff --git a/dists/jetty-deb/pom.xml b/dists/jetty-deb/pom.xml
deleted file mode 100644
index 9ce00e1..0000000
--- a/dists/jetty-deb/pom.xml
+++ /dev/null
@@ -1,103 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.eclipse.jetty.dist</groupId>
-    <artifactId>dist-parent</artifactId>
-    <version>9.0.0-SNAPSHOT</version>
-  </parent>
-  <artifactId>jetty-deb</artifactId>
-  <name>Jetty :: Unix Distributions :: Debian</name>
-  <packaging>deb</packaging>
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.mortbay.jetty.toolchain</groupId>
-        <artifactId>unix-maven-plugin</artifactId>
-        <version>1.0-alpha-6.1</version>
-        <extensions>true</extensions>
-        <configuration>
-          <contact>Jetty Project</contact>
-          <contactEmail>jetty-dev@eclipse.org</contactEmail>
-          <name>Core Jetty ${project.version} Distribution</name>
-          <description>Jetty provides an Web server and javax.servlet
-            container, plus support for Web Sockets, OSGi, JMX, JNDI,
-            JASPI, AJP and many other integrations. These components are
-            open source and available for commercial use and
-            distribution.</description>
-          <deb>
-            <useFakeroot>false</useFakeroot>
-            <priority>optional</priority>
-            <section>java</section>
-          </deb>
-           <packages>
-            <package>
-              <id>jetty-server</id>
-              <assembly>
-                <extractArtifact>
-                  <artifact>org.eclipse.jetty:jetty-distribution:zip</artifact>
-                  <to>/usr/share/jetty9</to>
-                  <pattern>/jetty-distribution-${project.version}(.*)</pattern>
-                  <replacement>$1</replacement>
-                  <excludes>
-                    <exclude>jetty-distribution-*/javadoc</exclude>
-                    <exclude>jetty-distribution-*/javadoc/**</exclude>
-                    <exclude>jetty-distribution-*/logs/**</exclude>
-                    <exclude>jetty-distribution-*/bin/**</exclude>
-                    <exclude>jetty-distribution-*/etc/**</exclude>
-                    <exclude>jetty-distribution-*/webapps/**</exclude>                  
-                    <exclude>jetty-distribution-*/*.html</exclude>
-                    <exclude>jetty-distribution-*/*.txt</exclude>             
-                  </excludes>
-                 </extractArtifact>
-                 <extractArtifact>
-                  <artifact>org.eclipse.jetty:jetty-distribution:zip</artifact>
-                  <to>/usr/share/doc/jetty9</to>
-                  <pattern>/jetty-distribution-${project.version}(.*)</pattern>
-                  <replacement>$1</replacement>
-                  <excludes>
-                    <include>jetty-distribution-*/*.html</include>
-                    <include>jetty-distribution-*/*.txt</include> 
-                    <exclude>jetty-distribution-*/**</exclude>           
-                  </excludes>
-                 </extractArtifact>
-                 <extractArtifact>
-                  <artifact>org.eclipse.jetty:jetty-distribution:zip</artifact>
-                  <to>/etc/jetty9</to>
-                  <pattern>/jetty-distribution-${project.version}(.*)</pattern>
-                  <replacement>$1</replacement>
-                  <excludes>
-                    <include>jetty-distribution-*/etc/**</include>
-                    <!-- exclude>jetty-distribution-*/**</exclude-->           
-                  </excludes>
-                 </extractArtifact>
-              </assembly>
-            </package>
-            <package>
-              <id>jetty-test-webapp</id>
-              <classifier>test-webapp</classifier>
-              <assembly>
-                <extractArtifact>
-                  <artifact>org.eclipse.jetty:jetty-distribution:zip</artifact>
-                  <to>/var/lib/jetty9/webapps</to>
-                  <pattern>/jetty-distribution-${project.version}(.*)</pattern>
-                  <replacement>$1</replacement>
-                  <includes>
-                    <include>jetty-distribution-*/webapps/**</include>
-                  </includes>
-                </extractArtifact>
-              </assembly>
-            </package>
-          </packages>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-  <dependencies>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-distribution</artifactId>
-      <version>${project.version}</version>
-      <type>zip</type>
-    </dependency>
-  </dependencies>
-</project>
diff --git a/dists/jetty-deb/src/main/unix/scripts/postinst b/dists/jetty-deb/src/main/unix/scripts/postinst
deleted file mode 100644
index f6d7813..0000000
--- a/dists/jetty-deb/src/main/unix/scripts/postinst
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-
-LOG_DIR=/var/lib/jetty9/logs
-WEBAPP_DIR=/var/lib/jetty9/webapps
-
-# copy the jetty start script into place
-cp /usr/share/jetty9/bin/jetty.sh /etc/init.d/jetty
-
-# make it generally executable
-chmod 755 /etc/init.d/jetty
-
-# ensure we have a logging directory
-if [ ! -d "$LOG_DIR" ]; then
-  mkdir $LOG_DIR
-fi
-
-# ensure we have a webapps directory
-if [ ! -d "$WEBAPP_DIR" ]; then
-  mkdir $WEBAPP_DIR
-fi
diff --git a/dists/jetty-deb/src/main/unix/scripts/postrm b/dists/jetty-deb/src/main/unix/scripts/postrm
deleted file mode 100644
index 1f7f3e3..0000000
--- a/dists/jetty-deb/src/main/unix/scripts/postrm
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/bin/bash
-
-#rm -f /etc/init.d/jetty
-
- case "$1" in
-  purge)
- [...]
-    # find first and last SYSTEM_UID numbers
-    for LINE in `grep SYSTEM_UID /etc/adduser.conf | grep -v "^#"`; do
-       case $LINE in
-          FIRST_SYSTEM_UID*)
-            FIST_SYSTEM_UID=`echo $LINE | cut -f2 -d '='`
-            ;;
-          LAST_SYSTEM_UID*)
-            LAST_SYSTEM_UID=`echo $LINE | cut -f2 -d '='`
-            ;;
-          *)
-            ;;
-          esac
-    done
-    # Remove system account if necessary
-    CREATEDUSER="jetty"
-    if [ -n "$FIST_SYSTEM_UID" ] && [ -n "$LAST_SYSTEM_UID" ]; then
-     if USERID=`getent passwd $CREATEDUSER | cut -f 3 -d ':'`; then
-       if [ -n "$USERID" ]; then
-         if [ "$FIST_SYSTEM_UID" -le "$USERID" ] && \
-            [ "$USERID" -le "$LAST_SYSTEM_UID" ]; then
-               echo -n "Removing $CREATEDUSER system user.."
-               deluser --quiet $CREATEDUSER || true
-               echo "..done"
-         fi
-       fi
-     fi
-   fi
-   # Remove system group if necessary
-   CREATEDGROUP="jetty"
-   FIRST_USER_GID=`grep ^USERS_GID /etc/adduser.conf | cut -f2 -d '='`
-   if [ -n "$FIST_USER_GID" ] then
-     if GROUPGID=`getent group $CREATEDGROUP | cut -f 3 -d ':'`; then
-       if [ -n "$GROUPGID" ]; then
-         if [ "$FIST_USER_GID" -gt "$GROUPGID" ]; then
-           echo -n "Removing $CREATEDGROUP group.."
-           delgroup --only-if-empty $CREATEDGROUP || true
-           echo "..done"
-         fi
-       fi
-     fi
-   fi
\ No newline at end of file
diff --git a/dists/jetty-deb/src/main/unix/scripts/preinst b/dists/jetty-deb/src/main/unix/scripts/preinst
deleted file mode 100644
index 423b675..0000000
--- a/dists/jetty-deb/src/main/unix/scripts/preinst
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/bin/bash
-
- case "$1" in
-   install|upgrade)
- 
-   # If the package has default file it could be sourced, so that
-   # the local admin can overwrite the defaults
- 
-   [ -f "/etc/default/jetty9" ] && . /etc/default/jetty9
- 
-   # Sane defaults:
- 
-   [ -z "$SERVER_HOME" ] && SERVER_HOME=/usr/share/jetty9
-   [ -z "$SERVER_USER" ] && SERVER_USER=jetty
-   [ -z "$SERVER_NAME" ] && SERVER_NAME="Jetty-9 Http and Servlet Engine"
-   [ -z "$SERVER_GROUP" ] && SERVER_GROUP=jetty
- 
-   # Groups that the user will be added to, if undefined, then none.
-   ADDGROUP=""
- 
-   # create user to avoid running server as root
-   # 1. create group if not existing
-   if ! getent group | grep -q "^$SERVER_GROUP:" ; then
-      echo -n "Adding group $SERVER_GROUP.."
-      addgroup --quiet --system $SERVER_GROUP 2>/dev/null ||true
-      echo "..done"
-   fi
-   # 2. create homedir if not existing
-   test -d $SERVER_HOME || mkdir $SERVER_HOME
-   # 3. create user if not existing
-   if ! getent passwd | grep -q "^$SERVER_USER:"; then
-     echo -n "Adding system user $SERVER_USER.."
-     adduser --quiet \
-             --system \
-             --ingroup $SERVER_GROUP \
-             --no-create-home \
-             --disabled-password \
-             $SERVER_USER 2>/dev/null || true
-     echo "..done"
-   fi
-   # 4. adjust passwd entry
-   usermod -c "$SERVER_NAME" \
-           -d $SERVER_HOME   \
-           -g $SERVER_GROUP  \
-              $SERVER_USER
-   # 5. adjust file and directory permissions
-   if ! dpkg-statoverride --list $SERVER_HOME >/dev/null
-   then
-       chown -R $SERVER_USER:$SERVER_GROUP $SERVER_HOME
-       chmod u=rwx,g=rxs,o= $SERVER_HOME
-   fi
-   # 6. Add the user to the ADDGROUP group
-   if test -n $ADDGROUP
-   then
-       if ! groups $SERVER_USER | cut -d: -f2 | \
-          grep -qw $ADDGROUP; then
-            adduser $SERVER_USER $ADDGROUP
-       fi
-   fi
-   ;;
-   configure)
\ No newline at end of file
diff --git a/dists/pom.xml b/dists/pom.xml
deleted file mode 100644
index c6ad38dc..0000000
--- a/dists/pom.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-	<parent>
-		<artifactId>jetty-project</artifactId>
-		<groupId>org.eclipse.jetty</groupId>
-		<version>9.0.0-SNAPSHOT</version>
-	</parent>
-	<modelVersion>4.0.0</modelVersion>
-	<groupId>org.eclipse.jetty.dist</groupId>
-	<artifactId>dist-parent</artifactId>
-	<packaging>pom</packaging>
-	<name>Jetty :: Distribution :: Parent</name>
-	<profiles>
-	  <profile>
-	    <id>linux-packaging</id>
-	    <!-- activation>
-	      <os>
-	        <name>Linux</name>
-	      </os>
-	    </activation-->
-	    <modules>
-	      <module>jetty-deb</module>
-	      <!--module>jetty-rpm</module-->
-	    </modules>
-	   </profile>
-	</profiles>
-</project>
diff --git a/examples/async-rest/async-rest-jar/pom.xml b/examples/async-rest/async-rest-jar/pom.xml
index 0ce9d1a..84d358d 100644
--- a/examples/async-rest/async-rest-jar/pom.xml
+++ b/examples/async-rest/async-rest-jar/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>example-async-rest</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+       <version>9.3.19-SNAPSHOT</version>
   </parent>  
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.eclipse.jetty.example-async-rest</groupId>
@@ -10,6 +10,9 @@
   <packaging>jar</packaging>
   <name>Example Async Rest :: Jar</name>
   <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.examples.asyc.rest</bundle-symbolic-name>
+  </properties>
   <dependencies>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
diff --git a/examples/async-rest/async-rest-webapp/pom.xml b/examples/async-rest/async-rest-webapp/pom.xml
index bafb45b..bab4a8b 100644
--- a/examples/async-rest/async-rest-webapp/pom.xml
+++ b/examples/async-rest/async-rest-webapp/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>example-async-rest</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>  
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.eclipse.jetty.example-async-rest</groupId>
diff --git a/examples/async-rest/async-rest-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/examples/async-rest/async-rest-webapp/src/main/webapp/WEB-INF/jetty-web.xml
index 9376677..c15dc38 100644
--- a/examples/async-rest/async-rest-webapp/src/main/webapp/WEB-INF/jetty-web.xml
+++ b/examples/async-rest/async-rest-webapp/src/main/webapp/WEB-INF/jetty-web.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!--
 This is the jetty specific web application configuration file.  When starting
diff --git a/examples/async-rest/async-rest-webapp/src/test/java/org/eclipse/jetty/example/asyncrest/DemoServer.java b/examples/async-rest/async-rest-webapp/src/test/java/org/eclipse/jetty/example/asyncrest/DemoServer.java
index 5b79a56..0aef73a 100644
--- a/examples/async-rest/async-rest-webapp/src/test/java/org/eclipse/jetty/example/asyncrest/DemoServer.java
+++ b/examples/async-rest/async-rest-webapp/src/test/java/org/eclipse/jetty/example/asyncrest/DemoServer.java
@@ -28,7 +28,7 @@
     {
         String jetty_home = System.getProperty("jetty.home",".");
 
-        Server server = new Server(Integer.getInteger("jetty.port",8080).intValue());
+        Server server = new Server(Integer.getInteger("jetty.http.port",8080).intValue());
                 
         WebAppContext webapp = new WebAppContext();
         webapp.setContextPath("/");
diff --git a/examples/async-rest/pom.xml b/examples/async-rest/pom.xml
index a93434d..db336cf 100644
--- a/examples/async-rest/pom.xml
+++ b/examples/async-rest/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.examples</groupId>
     <artifactId>examples-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>  
   <modelVersion>4.0.0</modelVersion>
diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml
index b3d5ae7..ff520ef 100644
--- a/examples/embedded/pom.xml
+++ b/examples/embedded/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.examples</groupId>
     <artifactId>examples-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
@@ -11,7 +11,17 @@
   <name>Example :: Jetty Embedded</name>
   <description>Jetty Embedded Examples</description>
   <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.embedded</bundle-symbolic-name>
+  </properties>
   <dependencies>
+  
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>18.0</version>
+        </dependency>
+        
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-util-ajax</artifactId>
@@ -39,6 +49,11 @@
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-rewrite</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-jmx</artifactId>
       <version>${project.version}</version>
     </dependency>
@@ -53,8 +68,18 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
-      <groupId>org.eclipse.jetty.spdy</groupId>
-      <artifactId>spdy-http-server</artifactId>
+      <groupId>org.eclipse.jetty.http2</groupId>
+      <artifactId>http2-server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.alpn</groupId>
+      <artifactId>alpn-api</artifactId>
+      <version>${alpn.api.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-alpn-server</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
@@ -105,5 +130,12 @@
       <artifactId>jetty-test-helper</artifactId>
       <!-- scope>test</scope-->
     </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-http</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
diff --git a/examples/embedded/src/main/java/HelloWorld.java b/examples/embedded/src/main/java/HelloWorld.java
deleted file mode 100644
index 28cc26c..0000000
--- a/examples/embedded/src/main/java/HelloWorld.java
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-import java.io.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.server.handler.AbstractHandler;
-
-public class HelloWorld extends AbstractHandler
-{
-    @Override
-    public void handle( String target,
-                        Request baseRequest,
-                        HttpServletRequest request,
-                        HttpServletResponse response ) throws IOException,
-                                                      ServletException
-    {
-        // Declare response encoding and types
-        response.setContentType("text/html; charset=utf-8");
-
-        // Declare response status code
-        response.setStatus(HttpServletResponse.SC_OK);
-
-        // Write back response
-        response.getWriter().println("<h1>Hello World</h1>");
-
-        // Inform jetty that this request has now been handled
-        baseRequest.setHandled(true);
-    }
-
-    public static void main( String[] args ) throws Exception
-    {
-        Server server = new Server(8080);
-        server.setHandler(new HelloWorld());
-
-        server.start();
-        server.join();
-    }
-}
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java
index 1f1d746..c316670 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java
@@ -34,6 +34,8 @@
 
 public class AsyncEchoServlet extends HttpServlet
 {
+    private static final long serialVersionUID = 1L;
+
     @Override
     protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
     {
@@ -62,18 +64,23 @@
         @Override
         public void onDataAvailable() throws IOException
         {
-            onWritePossible();
+            handleAsyncIO();
         }
 
         @Override
         public void onAllDataRead() throws IOException
         {
-            onWritePossible();
+            handleAsyncIO();
         }
 
         @Override
         public void onWritePossible() throws IOException
         {
+            handleAsyncIO();
+        }
+        
+        private void handleAsyncIO() throws IOException
+        {
             // This method is called:
             //   1) after first registering a WriteListener (ready for first write)
             //   2) after first registering a ReadListener iff write is ready
@@ -81,8 +88,17 @@
             //   4) from an input callback 
            
             // We should try to read, only if we are able to write!
-            while (output.isReady() && input.isReady())
+            while (true)
             {
+                if (!output.isReady())
+                    // Don't even try to read anything until it is possible to write something,
+                    // when onWritePossible will be called
+                    break;
+
+                if (!input.isReady())
+                    // Nothing available to read, so wait for another call to onDataAvailable
+                    break;
+                
                 int read = input.read(buffer);
                 if (read<0)
                 {
@@ -100,7 +116,7 @@
         @Override
         public void onError(Throwable failure)
         {
-            failure.printStackTrace();
+            new Throwable("onError",failure).printStackTrace();
             asyncContext.complete();
         }
     }
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java
index dc0ebfe..14e5ca4 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java
@@ -39,6 +39,8 @@
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.server.handler.DefaultHandler;
 import org.eclipse.jetty.server.handler.HandlerList;
+import org.eclipse.jetty.server.handler.ResourceHandler;
+import org.eclipse.jetty.servlet.DefaultServlet;
 import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.URIUtil;
 import org.eclipse.jetty.util.resource.Resource;
@@ -109,8 +111,7 @@
             {
                 if (!request.getPathInfo().endsWith(URIUtil.SLASH))
                 {
-                    response.sendRedirect(response.encodeRedirectURL(URIUtil
-                            .addPaths(request.getRequestURI(), URIUtil.SLASH)));
+                    response.sendRedirect(response.encodeRedirectURL(request.getRequestURI()+URIUtil.SLASH));
                     return;
                 }
                 String listing = Resource.newResource(file).getListHTML(
@@ -126,7 +127,7 @@
             // Jetty DefaultServlet will cache formatted date strings, but we
             // will reformat for each request here
             response.setDateHeader("Last-Modified", file.lastModified());
-            response.setDateHeader("Content-Length", file.length());
+            response.setContentLengthLong(file.length());
             response.setContentType(mimeTypes.getMimeByExtension(file.getName()));
 
             // send "small" files blocking directly from an input stream
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java
index 9a2a3b1..98bf702 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java
@@ -40,6 +40,7 @@
         // Create the ResourceHandler. It is the object that will actually handle the request for a given file. It is
         // a Jetty Handler object so it is suitable for chaining with other handlers as you will see in other examples.
         ResourceHandler resource_handler = new ResourceHandler();
+        
         // Configure the ResourceHandler. Setting the resource base indicates where the files should be served out of.
         // In this example it is the current directory but it can be configured to anything that the jvm has access to.
         resource_handler.setDirectoriesListed(true);
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloSessionServlet.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloSessionServlet.java
new file mode 100644
index 0000000..0017fb3
--- /dev/null
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloSessionServlet.java
@@ -0,0 +1,84 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.embedded;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+@SuppressWarnings("serial")
+public class HelloSessionServlet extends HttpServlet
+{
+    public HelloSessionServlet() {}
+
+    @Override
+    protected void doGet( HttpServletRequest request,
+                          HttpServletResponse response ) throws ServletException,
+            IOException
+    {
+        response.setContentType("text/html");
+        response.setStatus(HttpServletResponse.SC_OK);
+        response.addHeader("Cache-Control","no-cache");
+        
+        HttpSession session = request.getSession();
+        String message;
+        String link;
+        
+        String greeting = request.getParameter("greeting");
+        if (greeting != null)
+        {
+            session.setAttribute("greeting", greeting);
+            message = "New greeting '" + greeting + "' set in session.";
+            link = "Click <a href=\"/\">here</a> to use the new greeting from the session.";
+        }
+        else
+        {
+            greeting = (String)session.getAttribute("greeting");
+
+            if (greeting != null) 
+            {
+                message = "Greeting '" + greeting + "' set from session.";
+            }
+            else
+            {
+                greeting = "Hello";
+                message = "Greeting '" + greeting + "' is default.";
+            }
+
+            link = "Click <a href=\"/?greeting=Hola\">here</a> to set a new greeting.";
+        }
+        
+        PrintWriter out = response.getWriter();
+        out.println("<h1>" + greeting + " from HelloSessionServlet</h1>");
+        out.println("<p>" + message + "</p>");
+        out.println("<pre>");
+        out.println("session.getId() = " +session.getId());
+        out.println("session.isNew() = " +session.isNew());
+        out.println("</pre>");
+        out.println("<p>" + link + "</p>");
+        
+    }
+
+}
+
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java
new file mode 100644
index 0000000..f2852af
--- /dev/null
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java
@@ -0,0 +1,61 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.embedded;
+
+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.server.handler.AbstractHandler;
+
+public class HelloWorld extends AbstractHandler
+{
+    @Override
+    public void handle( String target,
+                        Request baseRequest,
+                        HttpServletRequest request,
+                        HttpServletResponse response ) throws IOException,
+                                                      ServletException
+    {
+        // Declare response encoding and types
+        response.setContentType("text/html; charset=utf-8");
+
+        // Declare response status code
+        response.setStatus(HttpServletResponse.SC_OK);
+
+        // Write back response
+        response.getWriter().println("<h1>Hello World</h1>");
+
+        // Inform jetty that this request has now been handled
+        baseRequest.setHandled(true);
+    }
+
+    public static void main( String[] args ) throws Exception
+    {
+        Server server = new Server(8080);
+        server.setHandler(new HelloWorld());
+
+        server.start();
+        server.join();
+    }
+}
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java
new file mode 100644
index 0000000..d75498b
--- /dev/null
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java
@@ -0,0 +1,188 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.embedded;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.Date;
+import java.util.EnumSet;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.jmx.MBeanContainer;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.servlets.PushCacheFilter;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public class Http2Server
+{
+    public static void main(String... args) throws Exception
+    {
+        Server server = new Server();
+
+        MBeanContainer mbContainer = new MBeanContainer(
+                ManagementFactory.getPlatformMBeanServer());
+        server.addBean(mbContainer);
+
+        ServletContextHandler context = new ServletContextHandler(server, "/",ServletContextHandler.SESSIONS);
+        context.setResourceBase("src/main/resources/docroot");
+        context.addFilter(PushCacheFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
+        // context.addFilter(PushSessionCacheFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
+        context.addFilter(PushedTilesFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
+        context.addServlet(new ServletHolder(servlet), "/test/*");
+        context.addServlet(DefaultServlet.class, "/").setInitParameter("maxCacheSize","81920");
+        server.setHandler(context);
+
+        // HTTP Configuration
+        HttpConfiguration http_config = new HttpConfiguration();
+        http_config.setSecureScheme("https");
+        http_config.setSecurePort(8443);
+        http_config.setSendXPoweredBy(true);
+        http_config.setSendServerVersion(true);
+
+        // HTTP Connector
+        ServerConnector http = new ServerConnector(server,new HttpConnectionFactory(http_config), new HTTP2CServerConnectionFactory(http_config));
+        http.setPort(8080);
+        server.addConnector(http);
+
+        // SSL Context Factory for HTTPS and HTTP/2
+        String jetty_distro = System.getProperty("jetty.distro","../../jetty-distribution/target/distribution");
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath(jetty_distro + "/demo-base/etc/keystore");
+        sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
+        sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
+        sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
+
+        // HTTPS Configuration
+        HttpConfiguration https_config = new HttpConfiguration(http_config);
+        https_config.addCustomizer(new SecureRequestCustomizer());
+
+        // HTTP/2 Connection Factory
+        HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(https_config);
+
+        ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+        alpn.setDefaultProtocol(http.getDefaultProtocol());
+
+        // SSL Connection Factory
+        SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory,alpn.getProtocol());
+
+        // HTTP/2 Connector
+        ServerConnector http2Connector =
+            new ServerConnector(server,ssl,alpn,h2,new HttpConnectionFactory(https_config));
+        http2Connector.setPort(8443);
+        server.addConnector(http2Connector);
+
+        ALPN.debug=false;
+
+        server.start();
+        //server.dumpStdErr();
+        server.join();
+    }
+
+    public static class PushedTilesFilter implements Filter
+    {
+        @Override
+        public void init(FilterConfig filterConfig) throws ServletException
+        {
+        }
+
+        @Override
+        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+        {
+            Request baseRequest = Request.getBaseRequest(request);
+
+            if (baseRequest.isPush() && baseRequest.getRequestURI().contains("tiles") )
+            {
+                String uri = baseRequest.getRequestURI().replace("tiles","pushed").substring(baseRequest.getContextPath().length());
+                request.getRequestDispatcher(uri).forward(request,response);
+                return;
+            }
+
+            chain.doFilter(request,response);
+        }
+
+        @Override
+        public void destroy()
+        {
+        }
+    };
+
+    static Servlet servlet = new HttpServlet()
+    {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {
+            String code=request.getParameter("code");
+            if (code!=null)
+                response.setStatus(Integer.parseInt(code));
+
+            HttpSession session = request.getSession(true);
+            if (session.isNew())
+                response.addCookie(new Cookie("bigcookie",
+                "This is a test cookies that was created on "+new Date()+" and is used by the jetty http/2 test servlet."));
+            response.setHeader("Custom","Value");
+            response.setContentType("text/plain");
+            String content = "Hello from Jetty using "+request.getProtocol() +"\n";
+            content+="uri="+request.getRequestURI()+"\n";
+            content+="session="+session.getId()+(session.isNew()?"(New)\n":"\n");
+            content+="date="+new Date()+"\n";
+            
+	    Cookie[] cookies = request.getCookies();
+	    if (cookies!=null && cookies.length>0)
+		for (Cookie c : cookies)
+		    content+="cookie "+c.getName()+"="+c.getValue()+"\n";
+            
+            response.setContentLength(content.length());
+            response.getOutputStream().print(content);
+        }
+    };
+}
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java
index 091bcfa..45240b5 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java
@@ -24,9 +24,13 @@
 
 import org.eclipse.jetty.deploy.DeploymentManager;
 import org.eclipse.jetty.deploy.PropertiesConfigurationManager;
+import org.eclipse.jetty.deploy.bindings.DebugListenerBinding;
 import org.eclipse.jetty.deploy.providers.WebAppProvider;
+import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.jmx.MBeanContainer;
+import org.eclipse.jetty.rewrite.handler.RewriteHandler;
 import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.server.DebugListener;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -34,6 +38,7 @@
 import org.eclipse.jetty.server.NCSARequestLog;
 import org.eclipse.jetty.server.SecureRequestCustomizer;
 import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnectionStatistics;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.server.SslConnectionFactory;
 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
@@ -59,11 +64,17 @@
         
         // Find jetty home and base directories
         String homePath = System.getProperty("jetty.home", jettyHomeBuild);
-        File homeDir = new File(homePath);
-        if (!homeDir.exists())
+        File start_jar = new File(homePath,"start.jar");
+        if (!start_jar.exists())
         {
-            throw new FileNotFoundException(homeDir.getAbsolutePath());
+            homePath = jettyHomeBuild = "jetty-distribution/target/distribution";
+            start_jar = new File(homePath,"start.jar");
+            if (!start_jar.exists())
+                throw new FileNotFoundException(start_jar.toString());
         }
+
+        File homeDir = new File(homePath);
+
         String basePath = System.getProperty("jetty.base", homeDir + "/demo-base");
         File baseDir = new File(basePath);
         if(!baseDir.exists())
@@ -128,10 +139,10 @@
         // === jetty-https.xml ===
         // SSL Context Factory
         SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore");
+        sslContextFactory.setKeyStorePath(jetty_home + "/../../../jetty-server/src/test/config/etc/keystore");
         sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
         sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
-        sslContextFactory.setTrustStorePath(jetty_home + "/etc/keystore");
+        sslContextFactory.setTrustStorePath(jetty_home + "/../../../jetty-server/src/test/config/etc/keystore");
         sslContextFactory.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
         sslContextFactory.setExcludeCipherSuites("SSL_RSA_WITH_DES_CBC_SHA",
                 "SSL_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA",
@@ -146,7 +157,7 @@
 
         // SSL Connector
         ServerConnector sslConnector = new ServerConnector(server,
-            new SslConnectionFactory(sslContextFactory,"http/1.1"),
+            new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()),
             new HttpConnectionFactory(https_config));
         sslConnector.setPort(8443);
         server.addConnector(sslConnector);
@@ -154,6 +165,9 @@
 
         // === jetty-deploy.xml ===
         DeploymentManager deployer = new DeploymentManager();
+        DebugListener debug = new DebugListener(System.err,true,true,true);
+        server.addBean(debug);        
+        deployer.addLifeCycleBinding(new DebugListenerBinding(debug));
         deployer.setContexts(contexts);
         deployer.setContextAttribute(
                 "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
@@ -179,7 +193,12 @@
         StatisticsHandler stats = new StatisticsHandler();
         stats.setHandler(server.getHandler());
         server.setHandler(stats);
+        ServerConnectionStatistics.addToAllConnectors(server);
 
+        // === Rewrite Handler
+        RewriteHandler rewrite = new RewriteHandler();
+        rewrite.setHandler(server.getHandler());
+        server.setHandler(rewrite);
 
         // === jetty-requestlog.xml ===
         NCSARequestLog requestLog = new NCSARequestLog();
@@ -210,9 +229,8 @@
         HashLoginService login = new HashLoginService();
         login.setName("Test Realm");
         login.setConfig(jetty_base + "/etc/realm.properties");
-        login.setRefreshInterval(0);
+        login.setHotReload(false);
         server.addBean(login);
-
         
         // Start the server
         server.start();
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java
index ebd99c9..794d116 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java
@@ -21,6 +21,7 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 
+import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -42,7 +43,7 @@
         // to get access to a keystore that we use in many unit tests and should
         // probably be a direct path to your own keystore.
 
-        String jettyDistKeystore = "../../jetty-distribution/target/distribution/etc/keystore";
+        String jettyDistKeystore = "../../jetty-distribution/target/distribution/demo-base/etc/keystore";
         String keystorePath = System.getProperty(
                 "example.keystore", jettyDistKeystore);
         File keystoreFile = new File(keystorePath);
@@ -77,7 +78,7 @@
         http.setPort(8080);
         http.setIdleTimeout(30000);
 
-        // SSL Context Factory for HTTPS and SPDY
+        // SSL Context Factory for HTTPS
         // SSL requires a certificate so we configure a factory for ssl contents
         // with information pointing to what keystore the ssl connection needs
         // to know about. Much more configuration is available the ssl context,
@@ -96,14 +97,17 @@
         // resolve the https connection before handing control over to the Jetty
         // Server.
         HttpConfiguration https_config = new HttpConfiguration(http_config);
-        https_config.addCustomizer(new SecureRequestCustomizer());
+        SecureRequestCustomizer src = new SecureRequestCustomizer();
+        src.setStsMaxAge(2000);
+        src.setStsIncludeSubDomains(true);
+        https_config.addCustomizer(src);
 
         // HTTPS connector
         // We create a second ServerConnector, passing in the http configuration
         // we just made along with the previously created ssl context factory.
         // Next we set the port and a longer idle timeout.
         ServerConnector https = new ServerConnector(server,
-                new SslConnectionFactory(sslContextFactory, "http/1.1"),
+            new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()),
                 new HttpConnectionFactory(https_config));
         https.setPort(8443);
         https.setIdleTimeout(500000);
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java
index bbce944..2494427 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java
@@ -36,6 +36,7 @@
 import org.eclipse.jetty.server.handler.HandlerList;
 import org.eclipse.jetty.server.handler.HandlerWrapper;
 import org.eclipse.jetty.server.handler.RequestLogHandler;
+import org.eclipse.jetty.server.handler.gzip.GzipHandler;
 import org.eclipse.jetty.util.ajax.JSON;
 
 /**
@@ -127,7 +128,7 @@
 
         // link them all together
         wrapper.setHandler(hello);
-        list.setHandlers(new Handler[] { param, wrapper, dft });
+        list.setHandlers(new Handler[] { param, new GzipHandler(), dft });
         handlers.setHandlers(new Handler[] { list, requestLog });
 
         // Handler tree looks like the following
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java
index 8d44b67..773f0e5 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java
@@ -58,6 +58,7 @@
         other.addServlet(new ServletHolder(new HelloServlet("YO!")), "*.yo");
 
         server.start();
+        server.dumpStdErr();
         server.join();
     }
 }
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java
index 0ae32ef..81b3996 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java
@@ -18,6 +18,7 @@
 
 package org.eclipse.jetty.embedded;
 
+import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.Server;
 
 public class OneHandler
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java
index 682ce70..c5910c6 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java
@@ -21,8 +21,8 @@
 import java.lang.management.ManagementFactory;
 
 import org.eclipse.jetty.jmx.MBeanContainer;
-import org.eclipse.jetty.server.ConnectorStatistics;
 import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnectionStatistics;
 import org.eclipse.jetty.servlet.DefaultServlet;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 
@@ -44,7 +44,7 @@
         context.addServlet(DefaultServlet.class, "/");
 
         // Add Connector Statistics tracking to all connectors
-        ConnectorStatistics.addToAllConnectors(server);
+        ServerConnectionStatistics.addToAllConnectors(server);
 
         server.start();
         server.join();
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java
new file mode 100644
index 0000000..13bb503
--- /dev/null
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java
@@ -0,0 +1,62 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.embedded;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.session.AbstractSessionIdManager;
+import org.eclipse.jetty.server.session.HashSessionIdManager;
+import org.eclipse.jetty.server.session.HashSessionManager;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+
+
+public class OneServletContextWithSession
+{
+    public static void main(String[] args) throws Exception
+    {
+        Server server = new Server(8080);
+
+        // Create an ID manager for the server.  This is normally done
+        // by default, but is done explicitly here for demonstration.
+        AbstractSessionIdManager idManager = new HashSessionIdManager();
+        server.setSessionIdManager(idManager);
+
+        // Create a ServletContext, with a session handler enabled.
+        ServletContextHandler context = new ServletContextHandler(
+                ServletContextHandler.SESSIONS);
+        context.setContextPath("/");
+        context.setResourceBase(System.getProperty("java.io.tmpdir"));
+        server.setHandler(context);
+
+        // Access the SessionHandler from the context.
+        SessionHandler sessions = context.getSessionHandler();
+        
+        // Set a SessionManager.  This is normally done by default,
+        // but is done explicitly here for demonstration.
+        sessions.setSessionManager(new HashSessionManager());
+        
+
+        //Servlet to read/set the greeting stored in the session.
+        //Can be accessed using http://localhost:8080/hello
+        context.addServlet(HelloSessionServlet.class, "/");
+
+        server.start();
+        server.join();
+    }
+}
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java
index 382d56f..4061a9a 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java
@@ -22,8 +22,8 @@
 import java.lang.management.ManagementFactory;
 
 import org.eclipse.jetty.jmx.MBeanContainer;
-import org.eclipse.jetty.security.HashLoginService;
 import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker;
 import org.eclipse.jetty.webapp.WebAppContext;
 
 public class OneWebApp
@@ -51,29 +51,19 @@
         WebAppContext webapp = new WebAppContext();
         webapp.setContextPath("/");
         File warFile = new File(
-                "../../jetty-distribution/target/distribution/demo-base/webapps/test.war");
+                "../../jetty-distribution/target/distribution/test/webapps/test/");
         webapp.setWar(warFile.getAbsolutePath());
+        webapp.addAliasCheck(new AllowSymLinkAliasChecker());
 
         // A WebAppContext is a ContextHandler as well so it needs to be set to
         // the server so it is aware of where to send the appropriate requests.
         server.setHandler(webapp);
 
-        // Configure a LoginService
-        // Since this example is for our test webapp, we need to setup a
-        // LoginService so this shows how to create a very simple hashmap based
-        // one. The name of the LoginService needs to correspond to what is
-        // configured in the webapp's web.xml and since it has a lifecycle of
-        // its own we register it as a bean with the Jetty server object so it
-        // can be started and stopped according to the lifecycle of the server
-        // itself.
-        HashLoginService loginService = new HashLoginService();
-        loginService.setName("Test Realm");
-        loginService.setConfig("src/test/resources/realm.properties");
-        server.addBean(loginService);
-
         // Start things up! 
         server.start();
 
+        server.dumpStdErr();
+        
         // The use of server.join() the will make the current thread join and
         // wait until the server is done executing.
         // See http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join()
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java
new file mode 100644
index 0000000..3441974
--- /dev/null
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java
@@ -0,0 +1,52 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.embedded;
+
+import org.eclipse.jetty.rewrite.RewriteCustomizer;
+import org.eclipse.jetty.rewrite.handler.CompactPathRule;
+import org.eclipse.jetty.rewrite.handler.RewriteRegexRule;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+
+public class RewriteServer
+{
+    public static void main( String[] args ) throws Exception
+    {
+        Server server = new Server(8080);
+        
+        HttpConfiguration config=server.getConnectors()[0].getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration();
+
+        RewriteCustomizer rewrite = new RewriteCustomizer();
+        config.addCustomizer(rewrite);
+        rewrite.addRule(new CompactPathRule());
+        rewrite.addRule(new RewriteRegexRule("(.*)foo(.*)","$1FOO$2"));
+        
+        ServletContextHandler context = new ServletContextHandler(
+                ServletContextHandler.SESSIONS);
+        context.setContextPath("/");
+        server.setHandler(context);
+
+        context.addServlet(DumpServlet.class, "/*");
+
+        server.start();
+        server.join();
+    }
+}
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SpdyConnector.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SpdyConnector.java
deleted file mode 100644
index 41599d0..0000000
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SpdyConnector.java
+++ /dev/null
@@ -1,114 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.embedded;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.SecureRequestCustomizer;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.SslConnectionFactory;
-import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-
-/**
- * A Jetty server with HTTP and SPDY connectors.
- */
-public class SpdyConnector
-{
-    public static void main(String[] args) throws Exception
-    {
-        // Path to as-built jetty-distribution directory
-        String jettyHomeBuild = "../../jetty-distribution/target/distribution";
-        
-        // Find jetty home directories
-        String homePath = System.getProperty("jetty.home", jettyHomeBuild);
-        File homeDir = new File(homePath);
-        if (!homeDir.exists())
-        {
-            throw new FileNotFoundException(homeDir.getAbsolutePath());
-        }
-        String jetty_home = homeDir.getAbsolutePath();
-        System.setProperty("jetty.home", jetty_home);
-
-        // The Server
-        Server server = new Server();
-
-        // HTTP Configuration
-        HttpConfiguration http_config = new HttpConfiguration();
-        http_config.setSecureScheme("https");
-        http_config.setSecurePort(8443);
-
-        // HTTP connector
-        ServerConnector http = new ServerConnector(server,
-                new HttpConnectionFactory(http_config));        
-        http.setPort(8080);
-        server.addConnector(http);
- 
-        // SSL Context Factory for HTTPS and SPDY
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore");
-        sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
-        sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
-
-        // HTTPS Configuration
-        HttpConfiguration https_config = new HttpConfiguration(http_config);
-        https_config.addCustomizer(new SecureRequestCustomizer());
-        
-        // SPDY versions
-        HTTPSPDYServerConnectionFactory spdy2 = 
-                new HTTPSPDYServerConnectionFactory(2, https_config);
-
-        HTTPSPDYServerConnectionFactory spdy3 = 
-                new HTTPSPDYServerConnectionFactory(3, https_config, 
-                        new ReferrerPushStrategy());
-
-        // NPN Factory
-        SPDYServerConnectionFactory.checkProtocolNegotiationAvailable();
-        NPNServerConnectionFactory npn = new NPNServerConnectionFactory(
-                spdy3.getProtocol(), 
-                spdy2.getProtocol(),
-                http.getDefaultProtocol());
-        npn.setDefaultProtocol(http.getDefaultProtocol());
-
-        // SSL Factory
-        SslConnectionFactory ssl = new SslConnectionFactory(
-                sslContextFactory, npn.getProtocol());
-
-        // SPDY Connector
-        ServerConnector spdyConnector = new ServerConnector(server, ssl, 
-                npn, spdy3, spdy2, 
-                new HttpConnectionFactory(https_config));
-        spdyConnector.setPort(8443);
-        server.addConnector(spdyConnector);
-        
-        // Set a handler
-        server.setHandler(new HelloHandler());
-
-        // Start the server
-        server.start();
-        server.join();
-    }
-}
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SpdyServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SpdyServer.java
deleted file mode 100644
index 0bca7d6..0000000
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SpdyServer.java
+++ /dev/null
@@ -1,210 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.embedded;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.lang.management.ManagementFactory;
-
-import org.eclipse.jetty.deploy.DeploymentManager;
-import org.eclipse.jetty.deploy.providers.WebAppProvider;
-import org.eclipse.jetty.jmx.MBeanContainer;
-import org.eclipse.jetty.security.HashLoginService;
-import org.eclipse.jetty.server.AsyncNCSARequestLog;
-import org.eclipse.jetty.server.ForwardedRequestCustomizer;
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.NCSARequestLog;
-import org.eclipse.jetty.server.SecureRequestCustomizer;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.SslConnectionFactory;
-import org.eclipse.jetty.server.handler.ContextHandlerCollection;
-import org.eclipse.jetty.server.handler.DefaultHandler;
-import org.eclipse.jetty.server.handler.HandlerCollection;
-import org.eclipse.jetty.server.handler.RequestLogHandler;
-import org.eclipse.jetty.server.handler.StatisticsHandler;
-import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.http.PushStrategy;
-import org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-
-public class SpdyServer
-{
-    public static void main( String[] args ) throws Exception
-    {
-        // Path to as-built jetty-distribution directory
-        String jettyHomeBuild = "../../jetty-distribution/target/distribution";
-
-        // Find jetty home directories
-        String homePath = System.getProperty("jetty.home", jettyHomeBuild);
-        File homeDir = new File(homePath);
-        if (!homeDir.exists())
-        {
-            throw new FileNotFoundException(homeDir.getAbsolutePath());
-        }
-        String jetty_home = homeDir.getAbsolutePath();
-        System.setProperty("jetty.home", jetty_home);
-
-        // Setup Threadpool
-        QueuedThreadPool threadPool = new QueuedThreadPool(512);
-
-        // Setup Jetty Server instance
-        Server server = new Server(threadPool);
-        server.manage(threadPool);
-        server.setDumpAfterStart(false);
-        server.setDumpBeforeStop(false);
-
-        // Setup JMX
-        MBeanContainer mbContainer = new MBeanContainer(
-                ManagementFactory.getPlatformMBeanServer());
-        server.addBean(mbContainer);
-
-        // Common HTTP configuration
-        HttpConfiguration config = new HttpConfiguration();
-        config.setSecurePort(8443);
-        config.addCustomizer(new ForwardedRequestCustomizer());
-        config.addCustomizer(new SecureRequestCustomizer());
-        config.setSendServerVersion(true);
-
-        // Http Connector Setup
-
-        // A plain HTTP connector listening on port 8080. Note that it's also
-        // possible to have port 8080 configured as a non SSL SPDY connector.
-        // But the specification and most browsers do not allow to use SPDY
-        // without SSL encryption. However some browsers allow it to be
-        // configured.
-        HttpConnectionFactory http = new HttpConnectionFactory(config);
-        ServerConnector httpConnector = new ServerConnector(server, http);
-        httpConnector.setPort(8080);
-        httpConnector.setIdleTimeout(10000);
-        server.addConnector(httpConnector);
-
-        // SSL configurations
-
-        // We need a SSLContextFactory for the SSL encryption. That
-        // SSLContextFactory will be used by the SPDY
-        // connector.
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore");
-        sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
-        sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
-        sslContextFactory.setTrustStorePath(jetty_home + "/etc/keystore");
-        sslContextFactory.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
-        sslContextFactory.setExcludeCipherSuites(
-                "SSL_RSA_WITH_DES_CBC_SHA",
-                "SSL_DHE_RSA_WITH_DES_CBC_SHA", 
-                "SSL_DHE_DSS_WITH_DES_CBC_SHA",
-                "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
-                "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
-                "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
-                "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
-
-        // Spdy Connector
-
-        // Make sure that the required NPN implementations are available.
-        SPDYServerConnectionFactory.checkProtocolNegotiationAvailable();
-
-        // A ReferrerPushStrategy is being initialized.
-        // See:
-        // http://www.eclipse.org/jetty/documentation/current/spdy-configuring-push.html
-        // for more details.
-        PushStrategy push = new ReferrerPushStrategy();
-        HTTPSPDYServerConnectionFactory spdy2 = 
-                new HTTPSPDYServerConnectionFactory(2, config, push);
-        spdy2.setInputBufferSize(8192);
-        spdy2.setInitialWindowSize(32768);
-
-        // We need a connection factory per protocol that our server is supposed
-        // to support on the NPN port. We then
-        // create a ServerConnector and pass in the supported factories. NPN
-        // will then be used to negotiate the
-        // protocol with the client.
-        HTTPSPDYServerConnectionFactory spdy3 = 
-                new HTTPSPDYServerConnectionFactory(3, config, push);
-        spdy3.setInputBufferSize(8192);
-
-        NPNServerConnectionFactory npn = new NPNServerConnectionFactory(
-                spdy3.getProtocol(), spdy2.getProtocol(), http.getProtocol());
-        npn.setDefaultProtocol(http.getProtocol());
-        npn.setInputBufferSize(1024);
-
-        SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory,
-                npn.getProtocol());
-
-        // Setup the npn connector on port 8443
-        ServerConnector spdyConnector = new ServerConnector(server, ssl, 
-                npn, spdy3, spdy2, http);
-        spdyConnector.setPort(8443);
-
-        server.addConnector(spdyConnector);
-
-        // The following section adds some handlers, deployers and webapp
-        // providers. See
-        // http://www.eclipse.org/jetty/documentation/current/advanced-embedding.html
-        // for details.
-
-        // Setup handlers
-        HandlerCollection handlers = new HandlerCollection();
-        ContextHandlerCollection contexts = new ContextHandlerCollection();
-        RequestLogHandler requestLogHandler = new RequestLogHandler();
-
-        handlers.setHandlers(new Handler[] { contexts, new DefaultHandler(),
-                requestLogHandler });
-
-        StatisticsHandler stats = new StatisticsHandler();
-        stats.setHandler(handlers);
-
-        server.setHandler(stats);
-
-        // Setup deployers
-        DeploymentManager deployer = new DeploymentManager();
-        deployer.setContexts(contexts);
-        server.addBean(deployer);
-
-        WebAppProvider webapp_provider = new WebAppProvider();
-        webapp_provider.setMonitoredDirName(jetty_home + "/webapps");
-        webapp_provider.setParentLoaderPriority(false);
-        webapp_provider.setExtractWars(true);
-        webapp_provider.setScanInterval(2);
-        webapp_provider.setDefaultsDescriptor(jetty_home
-                + "/etc/webdefault.xml");
-        deployer.addAppProvider(webapp_provider);
-
-        HashLoginService login = new HashLoginService();
-        login.setName("Test Realm");
-        login.setConfig(jetty_home + "/etc/realm.properties");
-        server.addBean(login);
-
-        NCSARequestLog requestLog = new AsyncNCSARequestLog();
-        requestLog.setFilename(jetty_home + "/logs/jetty-yyyy_mm_dd.log");
-        requestLog.setExtended(false);
-        requestLogHandler.setRequestLog(requestLog);
-
-        server.setStopAtShutdown(true);
-
-        server.start();
-        server.dumpStdErr();
-        server.join();
-    }
-}
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java
index 595580e..744da77 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java
@@ -55,20 +55,22 @@
         // our jetty maven testing utilities to get the proper resource
         // directory, you needn't use these, you simply need to supply the paths
         // you are looking to serve content from.
+        ResourceHandler rh0 = new ResourceHandler();
+
         ContextHandler context0 = new ContextHandler();
         context0.setContextPath("/");
-        ResourceHandler rh0 = new ResourceHandler();
         File dir0 = MavenTestingUtils.getTestResourceDir("dir0");
-        rh0.setBaseResource(Resource.newResource(dir0));
+        context0.setBaseResource(Resource.newResource(dir0));
         context0.setHandler(rh0);
 
         // Rinse and repeat the previous item, only specifying a different
         // resource base.
+        ResourceHandler rh1 = new ResourceHandler();
+
         ContextHandler context1 = new ContextHandler();
         context1.setContextPath("/");
-        ResourceHandler rh1 = new ResourceHandler();
         File dir1 = MavenTestingUtils.getTestResourceDir("dir1");
-        rh1.setBaseResource(Resource.newResource(dir1));
+        context1.setBaseResource(Resource.newResource(dir1));
         context1.setHandler(rh1);
 
         // Create a ContextHandlerCollection and set the context handlers to it.
diff --git a/examples/embedded/src/main/resources/docroot/push.html b/examples/embedded/src/main/resources/docroot/push.html
new file mode 100644
index 0000000..f6807e7
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/push.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+<style>
+img {
+  width: 80px;
+  height: 80px;
+  border: 0;
+  display: block;
+}
+
+td {
+  width: 80px;
+  height: 80px;
+}
+
+tr {
+  height: 80px;
+}
+
+table {
+  border-collapse: collapse;
+}
+
+table, th, td {
+   border: 0 solid black;
+   margin: 0;
+   padding: 0;
+}
+</style>
+
+<body>
+<div id="pushtiles">
+<table>
+<tbody>
+<tr>
+<td><img src="tiles/tile00.jpg" alt="" /></td>
+<td><img src="tiles/tile01.jpg" alt="" /></td>
+<td><img src="tiles/tile02.jpg" alt="" /></td>
+<td><img src="tiles/tile03.jpg" alt="" /></td>
+<td><img src="tiles/tile04.jpg" alt="" /></td>
+<td><img src="tiles/tile05.jpg" alt="" /></td>
+<td><img src="tiles/tile06.jpg" alt="" /></td>
+<td><img src="tiles/tile07.jpg" alt="" /></td>
+<td><img src="tiles/tile08.jpg" alt="" /></td>
+<td><img src="tiles/tile09.jpg" alt="" /></td>
+</tr>
+<tr>
+<td><img src="tiles/tile10.jpg" alt="" /></td>
+<td><img src="tiles/tile11.jpg" alt="" /></td>
+<td><img src="tiles/tile12.jpg" alt="" /></td>
+<td><img src="tiles/tile13.jpg" alt="" /></td>
+<td><img src="tiles/tile14.jpg" alt="" /></td>
+<td><img src="tiles/tile15.jpg" alt="" /></td>
+<td><img src="tiles/tile16.jpg" alt="" /></td>
+<td><img src="tiles/tile17.jpg" alt="" /></td>
+<td><img src="tiles/tile18.jpg" alt="" /></td>
+<td><img src="tiles/tile19.jpg" alt="" /></td>
+</tr>
+<tr>
+<td><img src="tiles/tile20.jpg" alt="" /></td>
+<td><img src="tiles/tile21.jpg" alt="" /></td>
+<td><img src="tiles/tile22.jpg" alt="" /></td>
+<td><img src="tiles/tile23.jpg" alt="" /></td>
+<td><img src="tiles/tile24.jpg" alt="" /></td>
+<td><img src="tiles/tile25.jpg" alt="" /></td>
+<td><img src="tiles/tile26.jpg" alt="" /></td>
+<td><img src="tiles/tile27.jpg" alt="" /></td>
+<td><img src="tiles/tile28.jpg" alt="" /></td>
+<td><img src="tiles/tile29.jpg" alt="" /></td>
+</tr>
+<tr>
+<td><img src="tiles/tile30.jpg" alt="" /></td>
+<td><img src="tiles/tile31.jpg" alt="" /></td>
+<td><img src="tiles/tile32.jpg" alt="" /></td>
+<td><img src="tiles/tile33.jpg" alt="" /></td>
+<td><img src="tiles/tile34.jpg" alt="" /></td>
+<td><img src="tiles/tile35.jpg" alt="" /></td>
+<td><img src="tiles/tile36.jpg" alt="" /></td>
+<td><img src="tiles/tile37.jpg" alt="" /></td>
+<td><img src="tiles/tile38.jpg" alt="" /></td>
+<td><img src="tiles/tile39.jpg" alt="" /></td>
+</tr>
+<tr>
+<td><img src="tiles/tile40.jpg" alt="" /></td>
+<td><img src="tiles/tile41.jpg" alt="" /></td>
+<td><img src="tiles/tile42.jpg" alt="" /></td>
+<td><img src="tiles/tile43.jpg" alt="" /></td>
+<td><img src="tiles/tile44.jpg" alt="" /></td>
+<td><img src="tiles/tile45.jpg" alt="" /></td>
+<td><img src="tiles/tile46.jpg" alt="" /></td>
+<td><img src="tiles/tile47.jpg" alt="" /></td>
+<td><img src="tiles/tile48.jpg" alt="" /></td>
+<td><img src="tiles/tile49.jpg" alt="" /></td>
+</tr>
+</tbody>
+</table>
+</div>
+</body>
+</html>
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile00.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile00.jpg
new file mode 100644
index 0000000..dc30307
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile00.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile01.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile01.jpg
new file mode 100644
index 0000000..66c2e25
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile01.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile02.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile02.jpg
new file mode 100644
index 0000000..f281dfd
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile02.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile03.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile03.jpg
new file mode 100644
index 0000000..4787e69
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile03.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile04.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile04.jpg
new file mode 100644
index 0000000..64e6d10
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile04.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile05.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile05.jpg
new file mode 100644
index 0000000..1530c76
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile05.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile06.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile06.jpg
new file mode 100644
index 0000000..cd59c5f
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile06.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile07.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile07.jpg
new file mode 100644
index 0000000..7f85287
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile07.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile08.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile08.jpg
new file mode 100644
index 0000000..31ebcb0
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile08.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile09.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile09.jpg
new file mode 100644
index 0000000..b9cfb1a
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile09.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile10.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile10.jpg
new file mode 100644
index 0000000..11c6fae
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile10.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile11.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile11.jpg
new file mode 100644
index 0000000..fee6760
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile11.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile12.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile12.jpg
new file mode 100644
index 0000000..a503246
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile12.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile13.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile13.jpg
new file mode 100644
index 0000000..4227207
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile13.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile14.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile14.jpg
new file mode 100644
index 0000000..6071c0f
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile14.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile15.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile15.jpg
new file mode 100644
index 0000000..64a0bf9
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile15.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile16.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile16.jpg
new file mode 100644
index 0000000..f599516
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile16.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile17.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile17.jpg
new file mode 100644
index 0000000..b4183b3
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile17.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile18.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile18.jpg
new file mode 100644
index 0000000..5a568e2
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile18.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile19.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile19.jpg
new file mode 100644
index 0000000..126c3c6
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile19.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile20.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile20.jpg
new file mode 100644
index 0000000..5423ac1
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile20.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile21.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile21.jpg
new file mode 100644
index 0000000..0b6edb0
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile21.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile22.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile22.jpg
new file mode 100644
index 0000000..f788a48
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile22.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile23.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile23.jpg
new file mode 100644
index 0000000..1fbc64d
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile23.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile24.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile24.jpg
new file mode 100644
index 0000000..f565b8f
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile24.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile25.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile25.jpg
new file mode 100644
index 0000000..18a4d06
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile25.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile26.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile26.jpg
new file mode 100644
index 0000000..dde86ec
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile26.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile27.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile27.jpg
new file mode 100644
index 0000000..eaf0ce8
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile27.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile28.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile28.jpg
new file mode 100644
index 0000000..7e59630
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile28.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile29.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile29.jpg
new file mode 100644
index 0000000..1eb25b7
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile29.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile30.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile30.jpg
new file mode 100644
index 0000000..ea68292
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile30.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile31.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile31.jpg
new file mode 100644
index 0000000..31455ca
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile31.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile32.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile32.jpg
new file mode 100644
index 0000000..f6a2a29
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile32.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile33.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile33.jpg
new file mode 100644
index 0000000..8362cf4
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile33.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile34.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile34.jpg
new file mode 100644
index 0000000..2b856a0
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile34.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile35.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile35.jpg
new file mode 100644
index 0000000..4e59e21
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile35.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile36.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile36.jpg
new file mode 100644
index 0000000..7df53fd
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile36.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile37.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile37.jpg
new file mode 100644
index 0000000..3184411
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile37.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile38.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile38.jpg
new file mode 100644
index 0000000..52b44b8
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile38.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile39.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile39.jpg
new file mode 100644
index 0000000..2f7a636
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile39.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile40.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile40.jpg
new file mode 100644
index 0000000..61cfdfa
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile40.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile41.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile41.jpg
new file mode 100644
index 0000000..d25a4e4
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile41.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile42.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile42.jpg
new file mode 100644
index 0000000..029e0f6
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile42.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile43.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile43.jpg
new file mode 100644
index 0000000..5ea4f55
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile43.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile44.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile44.jpg
new file mode 100644
index 0000000..ba18b30
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile44.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile45.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile45.jpg
new file mode 100644
index 0000000..b856d66
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile45.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile46.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile46.jpg
new file mode 100644
index 0000000..f474cc2
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile46.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile47.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile47.jpg
new file mode 100644
index 0000000..6b2f58e
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile47.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile48.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile48.jpg
new file mode 100644
index 0000000..e5d1426
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile48.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/pushed/tile49.jpg b/examples/embedded/src/main/resources/docroot/pushed/tile49.jpg
new file mode 100644
index 0000000..6a33cf3
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/pushed/tile49.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/readme.txt b/examples/embedded/src/main/resources/docroot/readme.txt
new file mode 100644
index 0000000..e0df187
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/readme.txt
@@ -0,0 +1 @@
+This is a test resouce for the Http2Server example
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile00.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile00.jpg
new file mode 100644
index 0000000..b2e52fb
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile00.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile01.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile01.jpg
new file mode 100644
index 0000000..4d98637
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile01.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile02.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile02.jpg
new file mode 100644
index 0000000..06b289b
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile02.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile03.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile03.jpg
new file mode 100644
index 0000000..fcb7b19
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile03.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile04.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile04.jpg
new file mode 100644
index 0000000..0cc4933
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile04.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile05.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile05.jpg
new file mode 100644
index 0000000..6a6d66f
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile05.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile06.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile06.jpg
new file mode 100644
index 0000000..89ddd64
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile06.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile07.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile07.jpg
new file mode 100644
index 0000000..9679317
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile07.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile08.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile08.jpg
new file mode 100644
index 0000000..aa895c4
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile08.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile09.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile09.jpg
new file mode 100644
index 0000000..9c234ca
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile09.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile10.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile10.jpg
new file mode 100644
index 0000000..8bc306c
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile10.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile11.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile11.jpg
new file mode 100644
index 0000000..85b62b6
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile11.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile12.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile12.jpg
new file mode 100644
index 0000000..448faa2
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile12.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile13.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile13.jpg
new file mode 100644
index 0000000..4f4f901
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile13.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile14.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile14.jpg
new file mode 100644
index 0000000..e03120b
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile14.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile15.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile15.jpg
new file mode 100644
index 0000000..64bc238
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile15.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile16.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile16.jpg
new file mode 100644
index 0000000..5e858b9
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile16.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile17.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile17.jpg
new file mode 100644
index 0000000..2da3aa8
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile17.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile18.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile18.jpg
new file mode 100644
index 0000000..e934ce5
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile18.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile19.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile19.jpg
new file mode 100644
index 0000000..15707bc
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile19.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile20.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile20.jpg
new file mode 100644
index 0000000..4b940f1
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile20.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile21.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile21.jpg
new file mode 100644
index 0000000..b340585
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile21.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile22.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile22.jpg
new file mode 100644
index 0000000..39b0824
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile22.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile23.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile23.jpg
new file mode 100644
index 0000000..42adae1
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile23.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile24.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile24.jpg
new file mode 100644
index 0000000..a8281dd
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile24.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile25.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile25.jpg
new file mode 100644
index 0000000..43692f0
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile25.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile26.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile26.jpg
new file mode 100644
index 0000000..f8de0f7
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile26.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile27.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile27.jpg
new file mode 100644
index 0000000..f6c210a
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile27.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile28.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile28.jpg
new file mode 100644
index 0000000..39f2622
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile28.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile29.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile29.jpg
new file mode 100644
index 0000000..952ce0b
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile29.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile30.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile30.jpg
new file mode 100644
index 0000000..e699a53
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile30.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile31.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile31.jpg
new file mode 100644
index 0000000..d6256fe
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile31.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile32.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile32.jpg
new file mode 100644
index 0000000..fde5e8b
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile32.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile33.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile33.jpg
new file mode 100644
index 0000000..2ba66aa
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile33.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile34.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile34.jpg
new file mode 100644
index 0000000..37e24f0
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile34.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile35.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile35.jpg
new file mode 100644
index 0000000..b147697
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile35.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile36.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile36.jpg
new file mode 100644
index 0000000..6501650
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile36.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile37.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile37.jpg
new file mode 100644
index 0000000..e2d40ee
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile37.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile38.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile38.jpg
new file mode 100644
index 0000000..1363781
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile38.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile39.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile39.jpg
new file mode 100644
index 0000000..790cfd2
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile39.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile40.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile40.jpg
new file mode 100644
index 0000000..3027238
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile40.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile41.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile41.jpg
new file mode 100644
index 0000000..d085e38
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile41.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile42.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile42.jpg
new file mode 100644
index 0000000..b7ec60f
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile42.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile43.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile43.jpg
new file mode 100644
index 0000000..6230abd
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile43.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile44.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile44.jpg
new file mode 100644
index 0000000..46ad6a1
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile44.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile45.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile45.jpg
new file mode 100644
index 0000000..89d2d32
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile45.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile46.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile46.jpg
new file mode 100644
index 0000000..9f3451f
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile46.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile47.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile47.jpg
new file mode 100644
index 0000000..b702178
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile47.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile48.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile48.jpg
new file mode 100644
index 0000000..9defd9a
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile48.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/docroot/tiles/tile49.jpg b/examples/embedded/src/main/resources/docroot/tiles/tile49.jpg
new file mode 100644
index 0000000..26f2a44
--- /dev/null
+++ b/examples/embedded/src/main/resources/docroot/tiles/tile49.jpg
Binary files differ
diff --git a/examples/embedded/src/main/resources/exampleserver.xml b/examples/embedded/src/main/resources/exampleserver.xml
index a0fc811..deae1dd 100644
--- a/examples/embedded/src/main/resources/exampleserver.xml
+++ b/examples/embedded/src/main/resources/exampleserver.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="ExampleServer" class="org.eclipse.jetty.server.Server">
 
diff --git a/examples/embedded/src/main/resources/fileserver.xml b/examples/embedded/src/main/resources/fileserver.xml
index 0b4daa3..126f098 100644
--- a/examples/embedded/src/main/resources/fileserver.xml
+++ b/examples/embedded/src/main/resources/fileserver.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="FileServer" class="org.eclipse.jetty.server.Server">
 
diff --git a/examples/embedded/src/main/resources/java-util-logging.properties b/examples/embedded/src/main/resources/java-util-logging.properties
new file mode 100644
index 0000000..4aaa236
--- /dev/null
+++ b/examples/embedded/src/main/resources/java-util-logging.properties
@@ -0,0 +1,9 @@
+
+# Logging
+handlers = java.util.logging.ConsoleHandler
+.level = INFO
+
+java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$-6s %2$s %5$s%6$s%n
+
+# Console Logging
+java.util.logging.ConsoleHandler.level = ALL
\ No newline at end of file
diff --git a/examples/embedded/src/main/resources/jetty-logging.properties b/examples/embedded/src/main/resources/jetty-logging.properties
index d353093..7bffba8 100644
--- a/examples/embedded/src/main/resources/jetty-logging.properties
+++ b/examples/embedded/src/main/resources/jetty-logging.properties
@@ -1,12 +1,10 @@
-org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
-org.eclipse.jetty.LEVEL=INFO
-org.eclipse.jetty.STACKS=true
-org.eclipse.jetty.SOURCE=false
+#org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.JavaUtilLog
+#org.eclipse.jetty.util.log.javautil.PROPERTIES=java-util-logging.properties
+#org.eclipse.jetty.util.log.SOURCE=true
+#org.eclipse.jetty.LEVEL=INFO
+#org.eclipse.jetty.STACKS=true
 #org.eclipse.jetty.STACKS=false
-#org.eclipse.jetty.spdy.LEVEL=DEBUG
-#org.eclipse.jetty.server.LEVEL=DEBUG
 #org.eclipse.jetty.io.LEVEL=DEBUG
 #org.eclipse.jetty.io.ssl.LEVEL=DEBUG
-#org.eclipse.jetty.spdy.server.LEVEL=DEBUG
 #org.eclipse.jetty.server.LEVEL=DEBUG
 #org.eclipse.jetty.servlets.LEVEL=DEBUG
diff --git a/examples/embedded/src/main/resources/jetty-otherserver.xml b/examples/embedded/src/main/resources/jetty-otherserver.xml
index 4c8a5cd..21272bb 100644
--- a/examples/embedded/src/main/resources/jetty-otherserver.xml
+++ b/examples/embedded/src/main/resources/jetty-otherserver.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="OtherServer" class="org.eclipse.jetty.server.Server">
     <Set name="handler">
diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java
index 48b9b16..ecac0cb 100644
--- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java
+++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java
@@ -25,12 +25,7 @@
     public static void main(String[] args) throws Exception
     {
         System.setProperty("jetty.home","../jetty-distribution/target/distribution");
-        XmlConfiguration.main(new String[]
-            {
-            "../jetty-jmx/src/main/config/etc/jetty-jmx.xml",
-            "../jetty-server/src/main/config/etc/jetty.xml",
-            "../jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml"
-            }
-        );
+        XmlConfiguration.main("../jetty-jmx/src/main/config/etc/jetty-jmx.xml",
+                "../jetty-server/src/main/config/etc/jetty.xml");
     }
 }
diff --git a/examples/embedded/src/test/resources/realm.properties b/examples/embedded/src/test/resources/realm.properties
index 9d88b85..556117f 100644
--- a/examples/embedded/src/test/resources/realm.properties
+++ b/examples/embedded/src/test/resources/realm.properties
@@ -5,7 +5,7 @@
 #  <username>: <password>[,<rolename> ...]
 #
 # Passwords may be clear text, obfuscated or checksummed.  The class 
-# org.eclipse.util.Password should be used to generate obfuscated
+# org.eclipse.jetty.util.security.Password should be used to generate obfuscated
 # passwords or password checksums
 #
 # If DIGEST Authentication is used, the password must be in a recoverable
diff --git a/examples/pom.xml b/examples/pom.xml
index 008dcfa..79bb30b 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -1,27 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <groupId>org.eclipse.jetty.examples</groupId>
diff --git a/icon.jpg b/icon.jpg
deleted file mode 100644
index d2b7ab5..0000000
--- a/icon.jpg
+++ /dev/null
Binary files differ
diff --git a/jetty-alpn/jetty-alpn-client/pom.xml b/jetty-alpn/jetty-alpn-client/pom.xml
index d489c7f..9e5ff09 100644
--- a/jetty-alpn/jetty-alpn-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-client/pom.xml
@@ -2,13 +2,11 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-alpn-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-alpn-client</artifactId>
-  <name>Jetty :: ALPN Client</name>
-  <description>Jetty ALPN client services</description>
-  <url>http://www.eclipse.org/jetty</url>
+  <name>Jetty :: ALPN :: Client</name>
   <properties>
     <bundle-symbolic-name>${project.groupId}.alpn.client</bundle-symbolic-name>
   </properties>
@@ -31,15 +29,6 @@
           </execution>
         </executions>
       </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
       <!-- always include the sources to be able to prepare the eclipse-jetty-SDK feature
       with a snapshot. -->
       <plugin>
diff --git a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
index 70566ec..cc31f3b 100644
--- a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
+++ b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
@@ -18,10 +18,10 @@
 
 package org.eclipse.jetty.alpn.client;
 
-import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
+
 import javax.net.ssl.SSLEngine;
 
 import org.eclipse.jetty.alpn.ALPN;
@@ -35,12 +35,12 @@
 {
     private static final Logger LOG = Log.getLogger(ALPNClientConnection.class);
 
-    private final String protocol;
+    private final List<String> protocols;
 
-    public ALPNClientConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, SSLEngine sslEngine, Map<String, Object> context, String protocol)
+    public ALPNClientConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, SSLEngine sslEngine, Map<String, Object> context, List<String> protocols)
     {
         super(endPoint, executor, sslEngine, connectionFactory, context);
-        this.protocol = protocol;
+        this.protocols = protocols;
         ALPN.put(sslEngine, this);
     }
 
@@ -54,20 +54,20 @@
     @Override
     public List<String> protocols()
     {
-        return Arrays.asList(protocol);
+        return protocols;
     }
 
     @Override
     public void selected(String protocol)
     {
-        if (this.protocol.equals(protocol))
+        if (protocols.contains(protocol))
         {
             ALPN.remove(getSSLEngine());
             completed();
         }
         else
         {
-            LOG.info("Could not negotiate protocol: server {} - client {}", protocol, this.protocol);
+            LOG.info("Could not negotiate protocol: server [{}] - client {}", protocol, protocols);
             close();
         }
     }
diff --git a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
index d94fa8c..26657b5 100644
--- a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
+++ b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
@@ -19,7 +19,10 @@
 package org.eclipse.jetty.alpn.client;
 
 import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
+import java.util.ServiceLoader;
 import java.util.concurrent.Executor;
 
 import javax.net.ssl.SSLEngine;
@@ -28,24 +31,58 @@
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
+import org.eclipse.jetty.io.ssl.ALPNProcessor;
 import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
+import org.eclipse.jetty.io.ssl.SslHandshakeListener;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
 
-public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory
+public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory implements SslHandshakeListener
 {
+    private final SslHandshakeListener alpnListener = new ALPNListener();
     private final Executor executor;
-    private final String protocol;
+    private final List<String> protocols;
+    private final ALPNProcessor.Client alpnProcessor;
 
-    public ALPNClientConnectionFactory(Executor executor, ClientConnectionFactory connectionFactory, String protocol)
+    public ALPNClientConnectionFactory(Executor executor, ClientConnectionFactory connectionFactory, List<String> protocols)
     {
         super(connectionFactory);
+        if (protocols.isEmpty())
+            throw new IllegalArgumentException("ALPN protocol list cannot be empty");
         this.executor = executor;
-        this.protocol = protocol;
+        this.protocols = protocols;
+        Iterator<ALPNProcessor.Client> processors = ServiceLoader.load(ALPNProcessor.Client.class).iterator();
+        alpnProcessor = processors.hasNext() ? processors.next() : ALPNProcessor.Client.NOOP;
+    }
+
+    public ALPNProcessor.Client getALPNProcessor()
+    {
+        return alpnProcessor;
     }
 
     @Override
     public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
     {
-        return new ALPNClientConnection(endPoint, executor, getClientConnectionFactory(),
-                (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY), context, protocol);
+        SSLEngine sslEngine = (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY);
+        getALPNProcessor().configure(sslEngine, protocols);
+        ContainerLifeCycle connector = (ContainerLifeCycle)context.get(ClientConnectionFactory.CONNECTOR_CONTEXT_KEY);
+        // Method addBean() has set semantic, so the listener is added only once.
+        connector.addBean(alpnListener);
+        ALPNClientConnection connection = new ALPNClientConnection(endPoint, executor, getClientConnectionFactory(),
+                sslEngine, context, protocols);
+        return customize(connection, context);
+    }
+
+    private class ALPNListener implements SslHandshakeListener
+    {
+        @Override
+        public void handshakeSucceeded(Event event)
+        {
+            getALPNProcessor().process(event.getSSLEngine());
+        }
+
+        @Override
+        public void handshakeFailed(Event event, Throwable failure)
+        {
+        }
     }
 }
diff --git a/jetty-alpn/jetty-alpn-java-client/pom.xml b/jetty-alpn/jetty-alpn-java-client/pom.xml
new file mode 100644
index 0000000..9d6c727
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-java-client/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-alpn-parent</artifactId>
+        <version>9.3.19-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>jetty-alpn-java-client</artifactId>
+    <name>Jetty :: ALPN :: JDK9 Client Implementation</name>
+
+    <properties>
+        <bundle-symbolic-name>${project.groupId}.alpn.java.client</bundle-symbolic-name>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.9</source>
+                    <target>1.9</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-io</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.alpn</groupId>
+            <artifactId>alpn-api</artifactId>
+            <version>${alpn.api.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.eclipse.jetty.http2</groupId>
+            <artifactId>http2-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/jetty-alpn/jetty-alpn-java-client/src/main/java/org/eclipse/jetty/alpn/java/client/JDK9ClientALPNProcessor.java b/jetty-alpn/jetty-alpn-java-client/src/main/java/org/eclipse/jetty/alpn/java/client/JDK9ClientALPNProcessor.java
new file mode 100644
index 0000000..c9f7bb4
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-java-client/src/main/java/org/eclipse/jetty/alpn/java/client/JDK9ClientALPNProcessor.java
@@ -0,0 +1,55 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.alpn.java.client;
+
+import java.io.UncheckedIOException;
+import java.util.List;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLParameters;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.io.ssl.ALPNProcessor;
+
+public class JDK9ClientALPNProcessor implements ALPNProcessor.Client
+{
+    @Override
+    public void configure(SSLEngine sslEngine, List<String> protocols)
+    {
+        SSLParameters sslParameters = sslEngine.getSSLParameters();
+        sslParameters.setApplicationProtocols(protocols.toArray(new String[0]));
+        sslEngine.setSSLParameters(sslParameters);
+    }
+
+    @Override
+    public void process(SSLEngine sslEngine)
+    {
+        try
+        {
+            ALPN.ClientProvider provider = (ALPN.ClientProvider)ALPN.get(sslEngine);
+            if (provider != null)
+                provider.selected(sslEngine.getApplicationProtocol());
+        }
+        catch (SSLException x)
+        {
+            throw new UncheckedIOException(x);
+        }
+    }
+}
diff --git a/jetty-alpn/jetty-alpn-java-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client b/jetty-alpn/jetty-alpn-java-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client
new file mode 100644
index 0000000..29d8e8b
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-java-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client
@@ -0,0 +1 @@
+org.eclipse.jetty.alpn.java.client.JDK9ClientALPNProcessor
diff --git a/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2Client.java b/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2Client.java
new file mode 100644
index 0000000..3ff2219
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2Client.java
@@ -0,0 +1,85 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.alpn.java.client;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Jetty;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class JDK9HTTP2Client
+{
+    public static void main(String[] args) throws Exception
+    {
+        HTTP2Client client = new HTTP2Client();
+        SslContextFactory sslContextFactory = new SslContextFactory(true);
+        client.addBean(sslContextFactory);
+        client.start();
+
+        String host = "localhost";
+        int port = 8443;
+
+        FuturePromise<Session> sessionPromise = new FuturePromise<>();
+        client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
+        Session session = sessionPromise.get(555, TimeUnit.SECONDS);
+
+        HttpFields requestFields = new HttpFields();
+        requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
+        MetaData.Request metaData = new MetaData.Request("GET", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
+        HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
+        CountDownLatch latch = new CountDownLatch(1);
+        session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                System.err.println(frame);
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                System.err.println(frame);
+                callback.succeeded();
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+
+        latch.await(5, TimeUnit.SECONDS);
+
+        client.stop();
+    }
+}
diff --git a/jetty-alpn/jetty-alpn-java-client/src/test/resources/jetty-logging.properties b/jetty-alpn/jetty-alpn-java-client/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..d96a696
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-java-client/src/test/resources/jetty-logging.properties
@@ -0,0 +1,2 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.LEVEL=DEBUG
diff --git a/jetty-alpn/jetty-alpn-java-server/pom.xml b/jetty-alpn/jetty-alpn-java-server/pom.xml
new file mode 100644
index 0000000..f56ed3a
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-java-server/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-alpn-parent</artifactId>
+        <version>9.3.19-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>jetty-alpn-java-server</artifactId>
+    <name>Jetty :: ALPN :: JDK9 Server Implementation</name>
+
+    <properties>
+        <bundle-symbolic-name>${project.groupId}.alpn.java.server</bundle-symbolic-name>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.9</source>
+                    <target>1.9</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-io</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.alpn</groupId>
+            <artifactId>alpn-api</artifactId>
+            <version>${alpn.api.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.http2</groupId>
+            <artifactId>http2-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-alpn-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/jetty-alpn/jetty-alpn-java-server/src/main/java/org/eclipse/jetty/alpn/java/server/JDK9ServerALPNProcessor.java b/jetty-alpn/jetty-alpn-java-server/src/main/java/org/eclipse/jetty/alpn/java/server/JDK9ServerALPNProcessor.java
new file mode 100644
index 0000000..606ac5e
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-java-server/src/main/java/org/eclipse/jetty/alpn/java/server/JDK9ServerALPNProcessor.java
@@ -0,0 +1,73 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.alpn.java.server;
+
+import java.util.List;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.io.ssl.ALPNProcessor;
+import org.eclipse.jetty.io.ssl.SslHandshakeListener;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class JDK9ServerALPNProcessor implements ALPNProcessor.Server, SslHandshakeListener
+{
+    private static final Logger LOG = Log.getLogger(JDK9ServerALPNProcessor.class);
+
+    @Override
+    public void configure(SSLEngine sslEngine)
+    {
+        sslEngine.setHandshakeApplicationProtocolSelector(this::process);
+    }
+
+    private String process(SSLEngine sslEngine, List<String> protocols)
+    {
+        try
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("ALPN selecting among client{}", protocols);
+            ALPN.ServerProvider provider = (ALPN.ServerProvider)ALPN.remove(sslEngine);
+            return provider == null ? "" : provider.select(protocols);
+        }
+        catch (SSLException x)
+        {
+            return null;
+        }
+    }
+
+    @Override
+    public void handshakeSucceeded(Event event)
+    {
+        ALPN.ServerProvider provider = (ALPN.ServerProvider)ALPN.remove(event.getSSLEngine());
+        if (provider != null)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("ALPN unsupported by client");
+            provider.unsupported();
+        }
+    }
+
+    @Override
+    public void handshakeFailed(Event event, Throwable failure)
+    {
+    }
+}
diff --git a/jetty-alpn/jetty-alpn-java-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server b/jetty-alpn/jetty-alpn-java-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server
new file mode 100644
index 0000000..51e198c
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-java-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server
@@ -0,0 +1 @@
+org.eclipse.jetty.alpn.java.server.JDK9ServerALPNProcessor
diff --git a/jetty-alpn/jetty-alpn-java-server/src/test/java/org/eclipse/jetty/alpn/java/server/JDK9ALPNTest.java b/jetty-alpn/jetty-alpn-java-server/src/test/java/org/eclipse/jetty/alpn/java/server/JDK9ALPNTest.java
new file mode 100644
index 0000000..f482804
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-java-server/src/test/java/org/eclipse/jetty/alpn/java/server/JDK9ALPNTest.java
@@ -0,0 +1,166 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.alpn.java.server;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSocket;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class JDK9ALPNTest
+{
+    private Server server;
+    private ServerConnector connector;
+
+    public void startServer(Handler handler) throws Exception
+    {
+        server = new Server();
+        HttpConfiguration httpConfiguration = new HttpConfiguration();
+        HttpConnectionFactory h1 = new HttpConnectionFactory(httpConfiguration);
+        HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpConfiguration);
+        ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+        alpn.setDefaultProtocol(h1.getProtocol());
+        connector = new ServerConnector(server, newSslContextFactory(), alpn, h1, h2);
+        server.addConnector(connector);
+        server.setHandler(handler);
+        server.start();
+    }
+
+    private SslContextFactory newSslContextFactory()
+    {
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+        sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
+        sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
+        sslContextFactory.setIncludeProtocols("TLSv1.2");
+        // The mandatory HTTP/2 cipher.
+        sslContextFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
+        return sslContextFactory;
+    }
+
+    @Test
+    public void testClientNotSupportingALPNServerSpeaksDefaultProtocol() throws Exception
+    {
+        startServer(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+            }
+        });
+
+        SslContextFactory sslContextFactory = new SslContextFactory(true);
+        sslContextFactory.start();
+        SSLContext sslContext = sslContextFactory.getSslContext();
+        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", connector.getLocalPort()))
+        {
+            client.setUseClientMode(true);
+            client.setSoTimeout(5000);
+            client.startHandshake();
+
+            OutputStream output = client.getOutputStream();
+            output.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "Connection: close\r\n" +
+                    "\r\n" +
+                    "").getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            InputStream input = client.getInputStream();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
+            String line = reader.readLine();
+            Assert.assertTrue(line.contains(" 200 "));
+            while (true)
+            {
+                if (reader.readLine() == null)
+                    break;
+            }
+        }
+    }
+
+    @Test
+    public void testClientSupportingALPNServerSpeaksNegotiatedProtocol() throws Exception
+    {
+        startServer(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+            }
+        });
+
+        SslContextFactory sslContextFactory = new SslContextFactory(true);
+        sslContextFactory.start();
+        SSLContext sslContext = sslContextFactory.getSslContext();
+        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", connector.getLocalPort()))
+        {
+            client.setUseClientMode(true);
+            SSLParameters sslParameters = client.getSSLParameters();
+            sslParameters.setApplicationProtocols(new String[]{"unknown/1.0", "http/1.1"});
+            client.setSSLParameters(sslParameters);
+            client.setSoTimeout(5000);
+            client.startHandshake();
+
+            OutputStream output = client.getOutputStream();
+            output.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "Connection: close\r\n" +
+                    "\r\n" +
+                    "").getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            InputStream input = client.getInputStream();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
+            String line = reader.readLine();
+            Assert.assertTrue(line.contains(" 200 "));
+            while (true)
+            {
+                if (reader.readLine() == null)
+                    break;
+            }
+        }
+
+    }
+}
diff --git a/jetty-alpn/jetty-alpn-java-server/src/test/java/org/eclipse/jetty/alpn/java/server/JDK9HTTP2Server.java b/jetty-alpn/jetty-alpn-java-server/src/test/java/org/eclipse/jetty/alpn/java/server/JDK9HTTP2Server.java
new file mode 100644
index 0000000..f8a10d3
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-java-server/src/test/java/org/eclipse/jetty/alpn/java/server/JDK9HTTP2Server.java
@@ -0,0 +1,66 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.alpn.java.server;
+
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+/**
+ * Test server that verifies that the JDK 9 ALPN mechanism works.
+ */
+public class JDK9HTTP2Server
+{
+    public static void main(String... args) throws Exception
+    {
+        Server server = new Server();
+
+        HttpConfiguration httpsConfig = new HttpConfiguration();
+        httpsConfig.setSecureScheme("https");
+        httpsConfig.setSecurePort(8443);
+        httpsConfig.setSendXPoweredBy(true);
+        httpsConfig.setSendServerVersion(true);
+        httpsConfig.addCustomizer(new SecureRequestCustomizer());
+
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+        sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
+        sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
+        sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
+
+        HttpConnectionFactory http = new HttpConnectionFactory(httpsConfig);
+        HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
+        ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+        alpn.setDefaultProtocol(http.getProtocol());
+        SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
+
+        ServerConnector http2Connector = new ServerConnector(server, ssl, alpn, h2, http);
+        http2Connector.setPort(8443);
+        server.addConnector(http2Connector);
+
+        server.start();
+    }
+}
diff --git a/jetty-alpn/jetty-alpn-java-server/src/test/resources/jetty-logging.properties b/jetty-alpn/jetty-alpn-java-server/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..c391f84
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-java-server/src/test/resources/jetty-logging.properties
@@ -0,0 +1,3 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.LEVEL=DEBUG
+#org.eclipse.jetty.alpn.LEVEL=DEBUG
diff --git a/jetty-alpn/jetty-alpn-java-server/src/test/resources/keystore.jks b/jetty-alpn/jetty-alpn-java-server/src/test/resources/keystore.jks
new file mode 100644
index 0000000..d6592f9
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-java-server/src/test/resources/keystore.jks
Binary files differ
diff --git a/jetty-alpn/jetty-alpn-server/pom.xml b/jetty-alpn/jetty-alpn-server/pom.xml
index c397f58..b90ce5d 100644
--- a/jetty-alpn/jetty-alpn-server/pom.xml
+++ b/jetty-alpn/jetty-alpn-server/pom.xml
@@ -2,62 +2,16 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-alpn-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-alpn-server</artifactId>
-  <name>Jetty :: ALPN Server</name>
-  <description>Jetty ALPN server services</description>
-  <url>http://www.eclipse.org/jetty</url>
+  <name>Jetty :: ALPN :: Server</name>
   <properties>
     <bundle-symbolic-name>${project.groupId}.alpn.server</bundle-symbolic-name>
   </properties>
   <build>
     <plugins>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-               <Import-Package>org.eclipse.jetty.alpn,*</Import-Package>
-               <_nouses>true</_nouses>
-              </instructions>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
       <!-- always include the sources to be able to prepare the eclipse-jetty-SDK feature
       with a snapshot. -->
       <plugin>
@@ -71,6 +25,33 @@
           <onlyAnalyze>org.eclipse.jetty.alpn.*</onlyAnalyze>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>1.7</version>
+        <executions>
+          <execution>
+            <id>parse-version</id>
+            <goals>
+              <goal>parse-version</goal>
+            </goals>
+            <configuration>
+              <propertyPrefix>alpn</propertyPrefix>
+              <versionString>${alpn.api.version}</versionString>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+            <instructions>
+              <Bundle-SymbolicName>${bundle-symbolic-name};singleton:=true</Bundle-SymbolicName>
+              <Import-Package>org.eclipse.jetty.alpn;version="${alpn.majorVersion}.${alpn.minorVersion}.${alpn.incrementalVersion}",*</Import-Package>
+            </instructions>
+        </configuration>
+      </plugin>
     </plugins>
   </build>
   <dependencies>
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/etc/jetty-alpn.xml b/jetty-alpn/jetty-alpn-server/src/main/config/etc/jetty-alpn.xml
new file mode 100644
index 0000000..a8b0e35
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/etc/jetty-alpn.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure id="sslConnector" class="org.eclipse.jetty.server.ServerConnector">
+
+  <Call name="addConnectionFactory">
+    <Arg>
+      <New class="org.eclipse.jetty.server.SslConnectionFactory">
+        <Arg name="next">alpn</Arg>
+        <Arg name="sslContextFactory"><Ref refid="sslContextFactory"/></Arg>
+      </New>
+    </Arg>
+  </Call>
+  
+  <Call name="addConnectionFactory">
+    <Arg>
+      <New id="alpn" class="org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory">
+        <Arg type="String">
+          <Property name="jetty.alpn.protocols" deprecated="alpn.protocols" default="" />
+        </Arg>
+        <Set name="defaultProtocol">
+          <Property name="jetty.alpn.defaultProtocol" deprecated="alpn.defaultProtocol" />
+        </Set>
+      </New>
+    </Arg>
+  </Call>
+
+  <!-- ALPN debugging on System.err -->
+  <Set class="org.eclipse.jetty.alpn.ALPN" name="debug" type="boolean"><Property name="jetty.alpn.debug" default="false" /></Set>
+
+</Configure>
+
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/etc/protonego-alpn.xml b/jetty-alpn/jetty-alpn-server/src/main/config/etc/protonego-alpn.xml
deleted file mode 100644
index 5d2ac7a..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/etc/protonego-alpn.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<Configure id="protonego" class="org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory">
-    <Arg name="protocols">
-        <Array type="String">
-            <Item>spdy/3</Item>
-            <Item>spdy/2</Item>
-            <Item>http/1.1</Item>
-        </Array>
-    </Arg>
-
-    <Set name="defaultProtocol">http/1.1</Set>
-
-    <!-- Enables NPN debugging on System.err -->
-    <!--<Set class="org.eclipse.jetty.alpn.ALPN" name="debug" type="boolean">true</Set>-->
-
-</Configure>
-
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0.mod
new file mode 100644
index 0000000..cb91b4c
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+maven://org.mortbay.jetty.alpn/alpn-boot/8.1.0.v20141016|lib/alpn/alpn-boot-8.1.0.v20141016.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_05.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_05.mod
new file mode 100644
index 0000000..cb91b4c
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_05.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+maven://org.mortbay.jetty.alpn/alpn-boot/8.1.0.v20141016|lib/alpn/alpn-boot-8.1.0.v20141016.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_101.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_101.mod
new file mode 100644
index 0000000..8923d6a
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_101.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_102.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_102.mod
new file mode 100644
index 0000000..8923d6a
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_102.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_11.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_11.mod
new file mode 100644
index 0000000..cb91b4c
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_11.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+maven://org.mortbay.jetty.alpn/alpn-boot/8.1.0.v20141016|lib/alpn/alpn-boot-8.1.0.v20141016.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_111.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_111.mod
new file mode 100644
index 0000000..8923d6a
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_111.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_112.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_112.mod
new file mode 100644
index 0000000..cfdaff1
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_112.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.10.v20161026/alpn-boot-8.1.10.v20161026.jar|lib/alpn/alpn-boot-8.1.10.v20161026.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.10.v20161026.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_121.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_121.mod
new file mode 100644
index 0000000..4eb78ef
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_121.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.11.v20170118/alpn-boot-8.1.11.v20170118.jar|lib/alpn/alpn-boot-8.1.11.v20170118.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.11.v20170118.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_131.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_131.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_131.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_131.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_20.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_20.mod
new file mode 100644
index 0000000..cb91b4c
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_20.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+maven://org.mortbay.jetty.alpn/alpn-boot/8.1.0.v20141016|lib/alpn/alpn-boot-8.1.0.v20141016.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_25.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_25.mod
new file mode 100644
index 0000000..6d6d75e
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_25.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+maven://org.mortbay.jetty.alpn/alpn-boot/8.1.2.v20141202|lib/alpn/alpn-boot-8.1.2.v20141202.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.2.v20141202.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_31.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_31.mod
new file mode 100644
index 0000000..e52bd23
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_31.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+maven://org.mortbay.jetty.alpn/alpn-boot/8.1.3.v20150130|lib/alpn/alpn-boot-8.1.3.v20150130.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.3.v20150130.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_40.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_40.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_40.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_40.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_45.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_45.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_45.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_45.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_51.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_51.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_51.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_51.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_60.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_60.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_60.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_60.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_65.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_65.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_65.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_65.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_66.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_66.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_66.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_66.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_71.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_71.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_71.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_71.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_72.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_72.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_72.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_72.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_73.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_73.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_73.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_73.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_74.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_74.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_74.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_74.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_77.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_77.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_77.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_77.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_91.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_91.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_91.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_91.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_92.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_92.mod
similarity index 100%
rename from jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_92.mod
rename to jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_92.mod
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-8.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-8.mod
new file mode 100644
index 0000000..cc0befd
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-8.mod
@@ -0,0 +1,30 @@
+# ALPN is provided via a -Xbootclasspath that modifies the secure connections
+# in java to support the ALPN layer needed for HTTP/2.
+#
+# This modification has a tight dependency on specific recent updates of
+# Java 1.7 and Java 1.8 (Java versions prior to 1.7u40 are not supported).
+#
+# The alpn module will use an appropriate alpn-boot jar for your
+# specific version of Java.
+#
+# IMPORTANT: Versions of Java that exist after this module was created are
+#            not guaranteed to work with existing alpn-boot jars, and might
+#            need a new alpn-boot to be created / tested / deployed by the
+#            Jetty project in order to provide support for these future
+#            Java versions.
+#
+# All versions of the alpn-boot jar can be found at
+# http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/
+
+[depend]
+alpn-impl/alpn-${java.version}
+
+[files]
+lib/
+lib/alpn/
+
+[license]
+ALPN is a hosted at github under the GPL v2 with ClassPath Exception.
+ALPN replaces/modifies OpenJDK classes in the sun.security.ssl package.
+http://github.com/jetty-project/jetty-alpn
+http://openjdk.java.net/legal/gplv2+ce.html
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-9.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-9.mod
new file mode 100644
index 0000000..731b744
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-9.mod
@@ -0,0 +1,5 @@
+# Provides support for ALPN based on JDK 9.
+
+[lib]
+lib/jetty-alpn-java-server-${jetty.version}.jar
+lib/alpn-api-*.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn.mod
new file mode 100644
index 0000000..a16dbf5
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn.mod
@@ -0,0 +1,22 @@
+[depend]
+ssl
+alpn-impl/alpn-${java.version.platform}
+
+[lib]
+lib/jetty-alpn-client-${jetty.version}.jar
+lib/jetty-alpn-server-${jetty.version}.jar
+
+[xml]
+etc/jetty-alpn.xml
+
+[ini-template]
+## Overrides the order protocols are chosen by the server.
+## The default order is that specified by the order of the
+## modules declared in start.ini.
+# jetty.alpn.protocols=h2,http/1.1
+
+## Specifies what protocol to use when negotiation fails.
+# jetty.alpn.defaultProtocol=http/1.1
+
+## ALPN debug logging on System.err
+# jetty.alpn.debug=false
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_40.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_40.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_40.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_45.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_45.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_45.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_51.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_51.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_51.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_55.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_55.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_55.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_60.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_60.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_60.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_65.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_65.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_65.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_67.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_67.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_67.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_71.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_71.mod
deleted file mode 100644
index e9b4e2a..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_71.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.2.v20141202/alpn-boot-7.1.2.v20141202.jar|lib/alpn/alpn-boot-7.1.2.v20141202.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.2.v20141202.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_72.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_72.mod
deleted file mode 100644
index e9b4e2a..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_72.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.2.v20141202/alpn-boot-7.1.2.v20141202.jar|lib/alpn/alpn-boot-7.1.2.v20141202.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.2.v20141202.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_75.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_75.mod
deleted file mode 100644
index ac315d6..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_75.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.3.v20150130/alpn-boot-7.1.3.v20150130.jar|lib/alpn/alpn-boot-7.1.3.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.3.v20150130.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_76.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_76.mod
deleted file mode 100644
index ac315d6..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_76.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.3.v20150130/alpn-boot-7.1.3.v20150130.jar|lib/alpn/alpn-boot-7.1.3.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.3.v20150130.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_79.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_79.mod
deleted file mode 100644
index ac315d6..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_79.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.3.v20150130/alpn-boot-7.1.3.v20150130.jar|lib/alpn/alpn-boot-7.1.3.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.3.v20150130.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_80.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_80.mod
deleted file mode 100644
index ac315d6..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_80.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.3.v20150130/alpn-boot-7.1.3.v20150130.jar|lib/alpn/alpn-boot-7.1.3.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.3.v20150130.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0.mod
deleted file mode 100644
index a81732c..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_05.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_05.mod
deleted file mode 100644
index a81732c..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_05.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_101.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_101.mod
deleted file mode 100644
index a7f656b..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_101.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_102.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_102.mod
deleted file mode 100644
index a7f656b..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_102.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_11.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_11.mod
deleted file mode 100644
index a81732c..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_11.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_111.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_111.mod
deleted file mode 100644
index a7f656b..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_111.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_112.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_112.mod
deleted file mode 100644
index a09db39..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_112.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.10.v20161026/alpn-boot-8.1.10.v20161026.jar|lib/alpn/alpn-boot-8.1.10.v20161026.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.10.v20161026.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_121.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_121.mod
deleted file mode 100644
index fdd3868..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_121.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.11.v20170118/alpn-boot-8.1.11.v20170118.jar|lib/alpn/alpn-boot-8.1.11.v20170118.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.11.v20170118.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_20.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_20.mod
deleted file mode 100644
index a81732c..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_20.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_25.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_25.mod
deleted file mode 100644
index 8d13261..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_25.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.2.v20141202/alpn-boot-8.1.2.v20141202.jar|lib/alpn/alpn-boot-8.1.2.v20141202.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.2.v20141202.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_31.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_31.mod
deleted file mode 100644
index 4114979..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_31.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.3.v20150130/alpn-boot-8.1.3.v20150130.jar|lib/alpn/alpn-boot-8.1.3.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.3.v20150130.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn.mod
deleted file mode 100644
index d3e4118..0000000
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn.mod
+++ /dev/null
@@ -1,42 +0,0 @@
-# ALPN is provided via a -Xbootclasspath that modifies the secure connections
-# in java to support the ALPN layer needed for SPDY (and eventually HTTP/2)
-#
-# This modification has a tight dependency on specific recent updates of
-# Java 1.7 and Java 1.8
-# (Java versions prior to 1.7u40 are not supported)
-#
-# The alpn protonego module will use an appropriate alpn-boot jar for your
-# specific version of Java.
-#
-# IMPORTANT: Versions of Java that exist after this module was created are
-#            not guaranteed to work with existing alpn-boot jars, and might
-#            need a new alpn-boot to be created / tested / deployed by the
-#            Jetty project in order to provide support for these future
-#            Java versions.
-#
-# All versions of alpn-boot can be found at
-# http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/
-
-[name]
-protonego-impl
-
-[depend]
-protonego-impl/alpn-${java.version}
-
-[lib]
-lib/jetty-alpn-client-${jetty.version}.jar
-lib/jetty-alpn-server-${jetty.version}.jar
-
-[xml]
-etc/protonego-alpn.xml
-
-[files]
-lib/
-lib/alpn/
-
-[license]
-ALPN is a hosted at github under the GPL v2 with ClassPath Exception.
-ALPN replaces/modifies OpenJDK classes in the java.sun.security.ssl package.
-http://github.com/jetty-project/jetty-alpn
-http://openjdk.java.net/legal/gplv2+ce.html
-
diff --git a/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java
index 58f48ca..f3e5835 100644
--- a/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java
+++ b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java
@@ -20,10 +20,13 @@
 
 import java.util.Collections;
 import java.util.List;
+
 import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSession;
 
 import org.eclipse.jetty.alpn.ALPN;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.ConnectionFactory;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.NegotiatingServerConnection;
 import org.eclipse.jetty.util.log.Log;
@@ -42,13 +45,19 @@
     @Override
     public void unsupported()
     {
-        select(Collections.<String>emptyList());
+        select(Collections.emptyList());
     }
 
     @Override
     public String select(List<String> clientProtocols)
     {
+        SSLEngine sslEngine = getSSLEngine();
         List<String> serverProtocols = getProtocols();
+        SSLSession sslSession = sslEngine.getHandshakeSession();
+        if (sslSession == null)
+            sslSession = sslEngine.getSession();
+        String tlsProtocol = sslSession.getProtocol();
+        String tlsCipher = sslSession.getCipherSuite();
         String negotiated = null;
 
         // RFC 7301 states that the server picks the protocol
@@ -57,19 +66,35 @@
         {
             if (clientProtocols.contains(serverProtocol))
             {
+                ConnectionFactory factory = getConnector().getConnectionFactory(serverProtocol);
+                if (factory instanceof CipherDiscriminator && !((CipherDiscriminator)factory).isAcceptable(serverProtocol, tlsProtocol, tlsCipher))
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("{} protocol {} not acceptable to {} for {}/{}", this, serverProtocol, factory, tlsProtocol, tlsCipher);
+                    continue;
+                }
+
                 negotiated = serverProtocol;
                 break;
             }
         }
-
         if (negotiated == null)
         {
-            negotiated = getDefaultProtocol();
+            if (clientProtocols.isEmpty())
+            {
+                negotiated = getDefaultProtocol();
+            }
+            else
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} could not negotiate protocol among client{} and server{}", this, clientProtocols, serverProtocols);
+                throw new IllegalStateException();
+            }
         }
         if (LOG.isDebugEnabled())
-            LOG.debug("{} protocol selected {}", this, negotiated);
+            LOG.debug("{} protocol selected {} among client{} and server{}", this, negotiated, clientProtocols, serverProtocols);
         setProtocol(negotiated);
-        ALPN.remove(getSSLEngine());
+        ALPN.remove(sslEngine);
         return negotiated;
     }
 
diff --git a/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java
index c5480e7..ccf886a 100644
--- a/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java
+++ b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java
@@ -18,44 +18,60 @@
 
 package org.eclipse.jetty.alpn.server;
 
+import java.util.Iterator;
 import java.util.List;
+import java.util.ServiceLoader;
+
 import javax.net.ssl.SSLEngine;
 
-import org.eclipse.jetty.alpn.ALPN;
 import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ssl.ALPNProcessor;
+import org.eclipse.jetty.io.ssl.SslHandshakeListener;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
 import org.eclipse.jetty.util.annotation.Name;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
 
-public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFactory
+public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFactory implements SslHandshakeListener
 {
-    private static final Logger LOG = Log.getLogger(ALPNServerConnectionFactory.class);
+    private final ALPNProcessor.Server alpnProcessor;
+
+    public ALPNServerConnectionFactory(String protocols)
+    {
+        this(protocols.trim().split(",", 0));
+    }
 
     public ALPNServerConnectionFactory(@Name("protocols") String... protocols)
     {
         super("alpn", protocols);
-        try
-        {
-            ClassLoader alpnClassLoader = ALPN.class.getClassLoader();
-            if (alpnClassLoader != null)
-            {
-                LOG.warn("ALPN must be in the boot classloader, not in: " + alpnClassLoader);
-                throw new IllegalStateException("ALPN must be in the boot classloader");
-            }
-        }
-        catch (Throwable x)
-        {
-            LOG.warn("ALPN not available", x);
-            throw new IllegalStateException("ALPN not available", x);
-        }
+        checkProtocolNegotiationAvailable();
+        Iterator<ALPNProcessor.Server> processors = ServiceLoader.load(ALPNProcessor.Server.class).iterator();
+        alpnProcessor = processors.hasNext() ? processors.next() : ALPNProcessor.Server.NOOP;
+    }
+
+    public ALPNProcessor.Server getALPNProcessor()
+    {
+        return alpnProcessor;
     }
 
     @Override
     protected AbstractConnection newServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol)
     {
+        getALPNProcessor().configure(engine);
         return new ALPNServerConnection(connector, endPoint, engine, protocols, defaultProtocol);
     }
+
+    @Override
+    public void handshakeSucceeded(Event event)
+    {
+        if (alpnProcessor instanceof SslHandshakeListener)
+            ((SslHandshakeListener)alpnProcessor).handshakeSucceeded(event);
+    }
+
+    @Override
+    public void handshakeFailed(Event event, Throwable failure)
+    {
+        if (alpnProcessor instanceof SslHandshakeListener)
+            ((SslHandshakeListener)alpnProcessor).handshakeFailed(event, failure);
+    }
 }
diff --git a/jetty-alpn/pom.xml b/jetty-alpn/pom.xml
index d4740cc..94a7125 100644
--- a/jetty-alpn/pom.xml
+++ b/jetty-alpn/pom.xml
@@ -1,17 +1,29 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-alpn-parent</artifactId>
   <packaging>pom</packaging>
   <name>Jetty :: ALPN :: Parent</name>
-  <description>Jetty ALPN services parent</description>
-  <url>http://www.eclipse.org/jetty</url>
   <modules>
     <module>jetty-alpn-server</module>
     <module>jetty-alpn-client</module>
   </modules>
+  <profiles>
+    <profile>
+      <id>jdk9</id>
+      <activation>
+        <jdk>[1.9,)</jdk>
+      </activation>
+      <modules>
+        <module>jetty-alpn-java-client</module>
+        <module>jetty-alpn-java-server</module>
+      </modules>
+    </profile>
+  </profiles>
 </project>
diff --git a/jetty-annotations/pom.xml b/jetty-annotations/pom.xml
index e77c5ee..d075adc 100644
--- a/jetty-annotations/pom.xml
+++ b/jetty-annotations/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-annotations</artifactId>
@@ -15,52 +15,14 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
         <extensions>true</extensions>
-        <executions>
-          <execution>
-            <id>generate-manifest</id>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
             <configuration>
               <instructions>
-                <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",org.objectweb.asm.*;version=5,*</Import-Package>
                 <Require-Capability>osgi.serviceloader; filter:="(osgi.serviceloader=javax.servlet.ServletContainerInitializer)";resolution:=optional;cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)"</Require-Capability>
               </instructions>
             </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
       </plugin>
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
diff --git a/jetty-annotations/src/main/config/etc/jetty-annotations.xml b/jetty-annotations/src/main/config/etc/jetty-annotations.xml
index 637b511..3eb3f6b 100644
--- a/jetty-annotations/src/main/config/etc/jetty-annotations.xml
+++ b/jetty-annotations/src/main/config/etc/jetty-annotations.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
   <!-- =========================================================== -->
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java
index 4c0e575..3399bfd 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java
@@ -23,6 +23,7 @@
 import java.net.URI;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -59,8 +60,6 @@
 
 /**
  * Configuration for Annotations
- *
- *
  */
 public class AnnotationConfiguration extends AbstractConfiguration
 {
@@ -89,7 +88,7 @@
     protected CounterStatistic _webInfLibStats;
     protected CounterStatistic _webInfClassesStats;
     protected Pattern _sciExcludePattern;
-  
+    protected ServiceLoader<ServletContainerInitializer> _loadedInitializers = null;
     /**
      * TimeStatistic
      *
@@ -293,8 +292,7 @@
         }
         
         /**
-         * True if "*" is one of the values.
-         * @return
+         * @return true if "*" is one of the values.
          */
         public boolean hasWildcard()
         {
@@ -302,8 +300,7 @@
         }
         
         /**
-         * Get the index of the "*" element, if it is specified. -1 otherwise.
-         * @return
+         * @return the index of the "*" element, if it is specified. -1 otherwise.
          */
         public int getWildcardIndex()
         {
@@ -313,8 +310,7 @@
         }
         
         /**
-         * True if the ordering contains a single value of "*"
-         * @return
+         * @return true if the ordering contains a single value of "*"
          */
         public boolean isDefaultOrder ()
         {
@@ -323,8 +319,8 @@
         
         /**
          * Get the order index of the given classname
-         * @param name
-         * @return
+         * @param name the classname to look up
+         * @return the index of the class name (or -1 if not found)
          */
         public int getIndexOf (String name)
         {
@@ -336,7 +332,7 @@
         
         /**
          * Get the number of elements of the ordering
-         * @return
+         * @return the size of the index
          */
         public int getSize()
         {
@@ -417,6 +413,9 @@
             context.removeBean(starter);
             context.removeAttribute(CONTAINER_INITIALIZER_STARTER);
         }
+        
+        if (_loadedInitializers != null)
+            _loadedInitializers.reload();
     }
     
     /** 
@@ -425,7 +424,7 @@
     @Override
     public void configure(WebAppContext context) throws Exception
     {
-       context.addDecorator(new AnnotationDecorator(context));
+       context.getObjectFactory().addDecorator(new AnnotationDecorator(context));
 
        //Even if metadata is complete, we still need to scan for ServletContainerInitializers - if there are any
       
@@ -453,8 +452,6 @@
        if (initializers != null && initializers.size()>0)
        {
            Map<String, Set<String>> map = ( Map<String, Set<String>>) context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP);
-           if (map == null)
-               LOG.warn ("ServletContainerInitializers: detected. Class hierarchy: empty");
            for (ContainerInitializer i : initializers)
                    i.resolveClasses(context,map);
        }
@@ -499,8 +496,8 @@
     /**
      * Perform scanning of classes for annotations
      * 
-     * @param context
-     * @throws Exception
+     * @param context the context for the scan
+     * @throws Exception if unable to scan
      */
     protected void scanForAnnotations (WebAppContext context)
     throws Exception
@@ -562,6 +559,9 @@
         }
        
         boolean timeout = !latch.await(getMaxScanWait(context), TimeUnit.SECONDS);
+        long elapsedMs = TimeUnit.MILLISECONDS.convert(System.nanoTime()-start, TimeUnit.NANOSECONDS);
+        
+        LOG.info("Scanning elapsed time={}ms",elapsedMs);
           
         if (LOG.isDebugEnabled())
         {
@@ -570,7 +570,7 @@
 
             LOG.debug("Scanned {} container path jars, {} WEB-INF/lib jars, {} WEB-INF/classes dirs in {}ms for context {}",
                     _containerPathStats.getTotal(), _webInfLibStats.getTotal(), _webInfClassesStats.getTotal(),
-                    (TimeUnit.MILLISECONDS.convert(System.nanoTime()-start, TimeUnit.NANOSECONDS)),
+                    elapsedMs,
                     context);
         }
 
@@ -592,8 +592,9 @@
     
     /**
      * Check if we should use multiple threads to scan for annotations or not
-     * @param context
-     * @return
+     * @param context the context of the multi threaded setting
+     * @return true if multi threading is enabled on the context, server, or via a System property.
+     * @see #MULTI_THREADED
      */
     protected boolean isUseMultiThreading(WebAppContext context)
     {
@@ -618,8 +619,9 @@
     /**
      * Work out how long we should wait for the async scanning to occur.
      * 
-     * @param context
-     * @return
+     * @param context the context of the max scan wait setting
+     * @return the max scan wait setting on the context, or server, or via a System property.
+     * @see #MAX_SCAN_WAIT
      */
     protected int getMaxScanWait (WebAppContext context)
     {
@@ -639,24 +641,15 @@
         return Integer.getInteger(MAX_SCAN_WAIT, DEFAULT_MAX_SCAN_WAIT).intValue();
     }
     
-    
-    
     /** 
      * @see org.eclipse.jetty.webapp.AbstractConfiguration#cloneConfigure(org.eclipse.jetty.webapp.WebAppContext, org.eclipse.jetty.webapp.WebAppContext)
      */
     @Override
     public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception
     {
-        context.addDecorator(new AnnotationDecorator(context));
+        context.getObjectFactory().addDecorator(new AnnotationDecorator(context));
     }
 
-
-    
-    /**
-     * @param context
-     * @param scis
-     * @throws Exception
-     */
     public void createServletContainerInitializerAnnotationHandlers (WebAppContext context, List<ServletContainerInitializer> scis)
     throws Exception
     {
@@ -676,6 +669,9 @@
                 Class<?>[] classes = annotation.value();
                 if (classes != null)
                 {
+
+                    if (LOG.isDebugEnabled()){LOG.debug("HandlesTypes {} on initializer {}",Arrays.asList(classes),service.getClass());}
+                    
                     initializer = new ContainerInitializer(service, classes);
 
                     //If we haven't already done so, we need to register a handler that will
@@ -708,7 +704,7 @@
             else
             {
                 initializer = new ContainerInitializer(service, null);
-                if (LOG.isDebugEnabled()) LOG.debug("No annotation on initializer "+service.getClass());
+                if (LOG.isDebugEnabled()) LOG.debug("No HandlesTypes annotation on initializer "+service.getClass());
             }
             
             initializers.add(initializer);
@@ -724,7 +720,6 @@
         context.addBean(starter, true);
     }
 
-    
     public Resource getJarFor (ServletContainerInitializer service) 
     throws MalformedURLException, IOException
     {
@@ -747,13 +742,15 @@
         return Resource.newResource(loadingJarName);
     }
     
-
     /**
      * Check to see if the ServletContainerIntializer loaded via the ServiceLoader came
      * from a jar that is excluded by the fragment ordering. See ServletSpec 3.0 p.85.
-     * @param context
-     * @param sci
+     * 
+     * @param context the context for the jars
+     * @param sci the servlet container initializer
+     * @param sciResource  the resource for the servlet container initializer
      * @return true if excluded
+     * @throws Exception if unable to determine exclusion
      */
     public boolean isFromExcludedJar (WebAppContext context, ServletContainerInitializer sci, Resource sciResource)
     throws Exception
@@ -800,9 +797,9 @@
     /**
      * Test if the ServletContainerIntializer is excluded by the 
      * o.e.j.containerInitializerExclusionPattern
-     * @param context
-     * @param sci
-     * @return
+     * 
+     * @param sci the ServletContainerIntializer
+     * @return true if the ServletContainerIntializer is excluded
      */
     public boolean matchesExclusionPattern(ServletContainerInitializer sci)
     {
@@ -819,9 +816,9 @@
     /**
      * Test if the ServletContainerInitializer is from the container classpath
      * 
-     * @param context
-     * @param sci
-     * @return
+     * @param context the context for the webapp classpath
+     * @param sci the ServletContainerIntializer
+     * @return true if ServletContainerIntializer is from container classpath
      */
     public boolean isFromContainerClassPath (WebAppContext context, ServletContainerInitializer sci)
     {
@@ -831,26 +828,27 @@
     }
 
     /**
-     * @param context
-     * @return list of non-excluded {@link ServletContainerInitializer}s
-     * @throws Exception
+     * Get SCIs that are not excluded from consideration
+     * @param context the web app context 
+     * @return the list of non-excluded servlet container initializers
+     * @throws Exception if unable to get list 
      */
     public List<ServletContainerInitializer> getNonExcludedInitializers (WebAppContext context)
     throws Exception
     {
         ArrayList<ServletContainerInitializer> nonExcludedInitializers = new ArrayList<ServletContainerInitializer>();
-
+      
         //We use the ServiceLoader mechanism to find the ServletContainerInitializer classes to inspect
         long start = 0;
 
         ClassLoader old = Thread.currentThread().getContextClassLoader();
-        ServiceLoader<ServletContainerInitializer> loadedInitializers = null;
+       
         try
         {        
             if (LOG.isDebugEnabled())
                 start = System.nanoTime();
             Thread.currentThread().setContextClassLoader(context.getClassLoader());
-            loadedInitializers = ServiceLoader.load(ServletContainerInitializer.class);
+            _loadedInitializers = ServiceLoader.load(ServletContainerInitializer.class);
         }
         finally
         {
@@ -859,21 +857,22 @@
         
         if (LOG.isDebugEnabled())
             LOG.debug("Service loaders found in {}ms", (TimeUnit.MILLISECONDS.convert((System.nanoTime()-start), TimeUnit.NANOSECONDS)));
-        
+     
         
         Map<ServletContainerInitializer,Resource> sciResourceMap = new HashMap<ServletContainerInitializer,Resource>();
         ServletContainerInitializerOrdering initializerOrdering = getInitializerOrdering(context);
 
         //Get initial set of SCIs that aren't from excluded jars or excluded by the containerExclusionPattern, or excluded
         //because containerInitializerOrdering omits it
-        for (ServletContainerInitializer sci:loadedInitializers)
-        {       
+        for (ServletContainerInitializer sci:_loadedInitializers)
+        { 
+            
             if (matchesExclusionPattern(sci)) 
             {
                 if (LOG.isDebugEnabled()) LOG.debug("{} excluded by pattern", sci);
                 continue;
             }
-            
+
             Resource sciResource = getJarFor(sci);
             if (isFromExcludedJar(context, sci, sciResource)) 
             { 
@@ -933,7 +932,7 @@
                 {
                     for (Map.Entry<ServletContainerInitializer, Resource> entry:sciResourceMap.entrySet())
                     {
-                        if (webInfJar.equals(entry.getValue()))
+                      if (webInfJar.equals(entry.getValue()))
                             nonExcludedInitializers.add(entry.getKey());
                     }
                 }
@@ -944,8 +943,9 @@
         {
             int i=0;
             for (ServletContainerInitializer sci:nonExcludedInitializers)
-                LOG.debug("ServletContainerInitializer: {} {}",(++i), sci.getClass().getName());
-        }
+                LOG.debug("ServletContainerInitializer: {} {} from {}",(++i), sci.getClass().getName(), sciResourceMap.get(sci));
+        }    
+        
         return nonExcludedInitializers;
     }
 
@@ -953,7 +953,8 @@
     /**
      * Jetty-specific extension that allows an ordering to be applied across ALL ServletContainerInitializers.
      * 
-     * @return
+     * @param context the context for the initializer ordering configuration 
+     * @return the ordering of the ServletContainerIntializer's
      */
     public ServletContainerInitializerOrdering getInitializerOrdering (WebAppContext context)
     {
@@ -971,9 +972,9 @@
     /**
      * Scan jars on container path.
      * 
-     * @param context
-     * @param parser
-     * @throws Exception
+     * @param context the context for the scan
+     * @param parser the parser to scan with
+     * @throws Exception if unable to scan
      */
     public void parseContainerPath (final WebAppContext context, final AnnotationParser parser) throws Exception
     {
@@ -1004,9 +1005,9 @@
     /**
      * Scan jars in WEB-INF/lib
      * 
-     * @param context
-     * @param parser
-     * @throws Exception
+     * @param context the context for the scan
+     * @param parser the annotation parser to use
+     * @throws Exception if unable to scan and/or parse
      */
     public void parseWebInfLib (final WebAppContext context, final AnnotationParser parser) throws Exception
     {   
@@ -1067,9 +1068,9 @@
     /**
      * Scan classes in WEB-INF/classes
      * 
-     * @param context
-     * @param parser
-     * @throws Exception
+     * @param context the context for the scan
+     * @param parser the annotation parser to use
+     * @throws Exception if unable to scan and/or parse
      */
     public void parseWebInfClasses (final WebAppContext context, final AnnotationParser parser)
     throws Exception
@@ -1100,10 +1101,10 @@
     /**
      * Get the web-fragment.xml from a jar
      * 
-     * @param jar
-     * @param frags
-     * @return the fragment if found, or null of not found
-     * @throws Exception
+     * @param jar the jar to look in for a fragment
+     * @param frags the fragments previously found
+     * @return true if the fragment if found, or null of not found
+     * @throws Exception if unable to determine the the fragment contains
      */
     public FragmentDescriptor getFragmentFromJar (Resource jar,  List<FragmentDescriptor> frags)
     throws Exception
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationDecorator.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationDecorator.java
index c389c8e..5c9f714 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationDecorator.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationDecorator.java
@@ -18,21 +18,16 @@
 
 package org.eclipse.jetty.annotations;
 
-import org.eclipse.jetty.servlet.ServletContextHandler.Decorator;
+import org.eclipse.jetty.util.Decorator;
 import org.eclipse.jetty.webapp.WebAppContext;
 
 /**
  * AnnotationDecorator
- *
- *
  */
 public class AnnotationDecorator implements Decorator
 {
     protected AnnotationIntrospector _introspector = new AnnotationIntrospector();
 
-    /**
-     * @param context
-     */
     public AnnotationDecorator(WebAppContext context)
     {
        registerHandlers(context);
@@ -53,13 +48,13 @@
     /**
      * Look for annotations that can be discovered with introspection:
      * <ul>
-     * <li> Resource
-     * <li> Resources
-     * <li> PostConstruct
-     * <li> PreDestroy
-     * <li> ServletSecurity?
+     * <li> Resource </li>
+     * <li> Resources </li>
+     * <li> PostConstruct </li>
+     * <li> PreDestroy </li>
+     * <li> ServletSecurity? </li>
      * </ul>
-     * @param o
+     * @param o the object ot introspect
      */
     protected void introspect (Object o)
     {
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
index 78d704e..f3f4b58 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
@@ -48,18 +48,18 @@
 
 /**
  * AnnotationParser
- *
+ * <p>
  * Use asm to scan classes for annotations. A SAX-style parsing is done.
  * Handlers are registered which will be called back when various types of
  * entity are encountered, eg a class, a method, a field. 
- * 
+ * <p> 
  * Handlers are not called back in any particular order and are assumed
  * to be order-independent.
- * 
+ * <p>
  * As a registered Handler will be called back for each annotation discovered
  * on a class, a method, a field, the Handler should test to see if the annotation
  * is one that it is interested in.
- * 
+ * <p>
  * For the servlet spec, we are only interested in annotations on classes, methods and fields,
  * so the callbacks for handling finding a class, a method a field are themselves
  * not fully implemented.
@@ -76,8 +76,8 @@
     /**
      * Convert internal name to simple name
      * 
-     * @param name
-     * @return
+     * @param name the internal name
+     * @return the simple name
      */
     public static String normalize (String name)
     {
@@ -96,8 +96,8 @@
     /**
      * Convert internal names to simple names.
      * 
-     * @param list
-     * @return
+     * @param list the list of internal names
+     * @return the list of simple names
      */
     public static String[] normalize (String[] list)
     {
@@ -291,7 +291,6 @@
         }
     }
     
-    
     /**
      * Handler
      *
@@ -307,13 +306,10 @@
         public void handle (FieldInfo info, String annotationName);
     }
     
-    
-    
     /**
      * AbstractHandler
      *
      * Convenience base class to provide no-ops for all Handler methods.
-     * 
      */
     public static abstract class AbstractHandler implements Handler
     {
@@ -533,7 +529,8 @@
 
     /**
      * True if the class has already been processed, false otherwise
-     * @param className
+     * @param className the classname
+     * @return true if class was parsed, false if not
      */
     public boolean isParsed (String className)
     {
@@ -545,9 +542,10 @@
     /**
      * Parse a given class
      * 
-     * @param className
-     * @param resolver
-     * @throws Exception
+     * @param handlers the set of handlers to find class
+     * @param className the class name to parse
+     * @param resolver the class name resolver to use 
+     * @throws Exception if unable to parse
      */
     public void parse (Set<? extends Handler> handlers, String className, ClassNameResolver resolver)
     throws Exception
@@ -564,7 +562,10 @@
                 if (resource!= null)
                 {
                     Resource r = Resource.newResource(resource);
-                    scanClass(handlers, null, r.getInputStream());
+                    try (InputStream is = r.getInputStream())
+                    {
+                        scanClass(handlers, null, is);
+                    }
                 }
             }
         }
@@ -575,10 +576,11 @@
     /**
      * Parse the given class, optionally walking its inheritance hierarchy
      * 
-     * @param clazz
-     * @param resolver
-     * @param visitSuperClasses
-     * @throws Exception
+     * @param handlers the handlers to look for class in 
+     * @param clazz the class to look for
+     * @param resolver the resolver to look up class with
+     * @param visitSuperClasses if true, also visit super classes for parse 
+     * @throws Exception if unable to parse class
      */
     public void parse (Set<? extends Handler> handlers, Class<?> clazz, ClassNameResolver resolver, boolean visitSuperClasses)
     throws Exception
@@ -595,7 +597,10 @@
                     if (resource!= null)
                     {
                         Resource r = Resource.newResource(resource);
-                        scanClass(handlers, null, r.getInputStream());
+                        try (InputStream is =  r.getInputStream())
+                        {
+                            scanClass(handlers, null, is);
+                        }
                     }
                 }
             }
@@ -612,9 +617,10 @@
     /**
      * Parse the given classes
      * 
-     * @param classNames
-     * @param resolver
-     * @throws Exception
+     * @param handlers the set of handlers to look for class in 
+     * @param classNames the class name
+     * @param resolver the class name resolver
+     * @throws Exception if unable to parse
      */
     public void parse (Set<? extends Handler> handlers, String[] classNames, ClassNameResolver resolver)
     throws Exception
@@ -629,9 +635,10 @@
     /**
      * Parse the given classes
      * 
-     * @param classNames
-     * @param resolver
-     * @throws Exception
+     * @param handlers the set of handlers to look for class in 
+     * @param classNames the class names
+     * @param resolver the class name resolver
+     * @throws Exception if unable to parse
      */
     public void parse (Set<? extends Handler> handlers, List<String> classNames, ClassNameResolver resolver)
     throws Exception
@@ -649,7 +656,10 @@
                     if (resource!= null)
                     {
                         Resource r = Resource.newResource(resource);
-                        scanClass(handlers, null, r.getInputStream());
+                        try (InputStream is = r.getInputStream())
+                        {
+                            scanClass(handlers, null, is);
+                        }
                     }
                 }
             }
@@ -665,14 +675,15 @@
     /**
      * Parse all classes in a directory
      * 
-     * @param dir
-     * @param resolver
-     * @throws Exception
+     * @param handlers the set of handlers to look for classes in 
+     * @param dir the resource directory to look for classes
+     * @param resolver the class name resolver
+     * @throws Exception if unable to parse
      */
     protected void parseDir (Set<? extends Handler> handlers, Resource dir, ClassNameResolver resolver)
     throws Exception
     {
-        //skip dirs whose name start with . (ie hidden)
+        // skip dirs whose name start with . (ie hidden)
         if (!dir.isDirectory() || !dir.exists() || dir.getName().startsWith("."))
             return;
 
@@ -699,7 +710,10 @@
                         {
                             Resource r = Resource.newResource(res.getURL());
                             if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
-                            scanClass(handlers, dir, r.getInputStream());
+                            try (InputStream is=r.getInputStream())
+                            {
+                                scanClass(handlers, dir, is);
+                            }
                         }
                     }                  
                     catch (Exception ex)
@@ -723,11 +737,12 @@
      * Parse classes in the supplied classloader. 
      * Only class files in jar files will be scanned.
      * 
-     * @param loader
-     * @param visitParents
-     * @param nullInclusive
-     * @param resolver
-     * @throws Exception
+     * @param handlers the handlers to look for classes in 
+     * @param loader the classloader for the classes
+     * @param visitParents if true, visit parent classloaders too
+     * @param nullInclusive if true, an empty pattern means all names match, if false, none match
+     * @param resolver the class name resolver
+     * @throws Exception if unable to parse
      */
     public void parse (final Set<? extends Handler> handlers, ClassLoader loader, boolean visitParents, boolean nullInclusive, final ClassNameResolver resolver)
     throws Exception
@@ -765,9 +780,10 @@
     /**
      * Parse classes in the supplied uris.
      * 
-     * @param uris
-     * @param resolver
-     * @throws Exception
+     * @param handlers the handlers to look for classes in  
+     * @param uris the uris for the jars
+     * @param resolver the class name resolver
+     * @throws Exception if unable to parse
      */
     public void parse (final Set<? extends Handler> handlers, final URI[] uris, final ClassNameResolver resolver)
     throws Exception
@@ -793,9 +809,11 @@
 
     /**
      * Parse a particular uri
-     * @param uri
-     * @param resolver
-     * @throws Exception
+     * 
+     * @param handlers the handlers to look for classes in 
+     * @param uri the uri for the jar 
+     * @param resolver the class name resolver
+     * @throws Exception if unable to parse
      */
     public void parse (final Set<? extends Handler> handlers, URI uri, final ClassNameResolver resolver)
     throws Exception
@@ -809,9 +827,11 @@
     
     /**
      * Parse a resource
-     * @param r
-     * @param resolver
-     * @throws Exception
+     * 
+     * @param handlers the handlers to look for classes in  
+     * @param r the resource to parse
+     * @param resolver the class name resolver
+     * @throws Exception if unable to parse
      */
     public void parse (final Set<? extends Handler> handlers, Resource r, final ClassNameResolver resolver)
     throws Exception
@@ -834,8 +854,11 @@
 
         if (fullname.endsWith(".class"))
         {
-            scanClass(handlers, null, r.getInputStream());
-            return;
+            try (InputStream is=r.getInputStream())
+            {
+                scanClass(handlers, null, is);
+                return;
+            }
         }
         
         if (LOG.isDebugEnabled()) LOG.warn("Resource not scannable for classes: {}", r);
@@ -847,9 +870,10 @@
     /**
      * Parse a resource that is a jar file.
      * 
-     * @param jarResource
-     * @param resolver
-     * @throws Exception
+     * @param handlers the handlers to look for classes in  
+     * @param jarResource the jar resource to parse
+     * @param resolver the class name resolver
+     * @throws Exception if unable to parse
      */
     protected void parseJar (Set<? extends Handler> handlers, Resource jarResource,  final ClassNameResolver resolver)
     throws Exception
@@ -862,42 +886,11 @@
             if (LOG.isDebugEnabled()) {LOG.debug("Scanning jar {}", jarResource);};
 
             //treat it as a jar that we need to open and scan all entries from  
-            //TODO alternative impl
-            /*
-            long start = System.nanoTime();
-            Collection<Resource> resources = Resource.newResource("jar:"+jarResource+"!/").getAllResources();
-            System.err.println(jarResource+String.valueOf(resources.size())+" resources listed in "+ ((TimeUnit.MILLISECONDS.convert(System.nanoTime()-start, TimeUnit.NANOSECONDS))));
-            for (Resource r:resources)
-            {
-                //skip directories
-                if (r.isDirectory())
-                    continue;
-
-                String name = r.getName();
-                name = name.substring(name.indexOf("!/")+2);
-
-                //check file is a valid class file name
-                if (isValidClassFileName(name) && isValidClassFilePath(name))
-                {
-                    String shortName =  name.replace('/', '.').substring(0,name.length()-6);
-
-                    if ((resolver == null)
-                            ||
-                        (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
-                    {
-                        if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", r);};
-                        scanClass(handlers, jarResource, r.getInputStream());
-                    }
-                }
-            }
-            */
-
-           InputStream in = jarResource.getInputStream();
+            InputStream in = jarResource.getInputStream();
             if (in==null)
                 return;
 
             MultiException me = new MultiException();
-            
             JarInputStream jar_in = new JarInputStream(in);
             try
             { 
@@ -915,20 +908,27 @@
                     entry = jar_in.getNextJarEntry();
                 }
             }
+            catch (Exception e)
+            {
+                me.add(new RuntimeException("Error scanning jar "+jarResource, e));
+            }
             finally
             {
                 jar_in.close();
             }
+            
             me.ifExceptionThrow();
         }        
     }
 
     /**
      * Parse a single entry in a jar file
-     * @param jar
-     * @param entry
-     * @param resolver
-     * @throws Exception
+     * 
+     * @param handlers the handlers to look for classes in  
+     * @param jar the jar resource to parse
+     * @param entry the entry in the jar resource to parse
+     * @param resolver the class name resolver
+     * @throws Exception if unable to parse
      */
     protected void parseJarEntry (Set<? extends Handler> handlers, Resource jar, JarEntry entry, final ClassNameResolver resolver)
     throws Exception
@@ -949,11 +949,14 @@
 
             if ((resolver == null)
                     ||
-                (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
+               (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
             {
                 Resource clazz = Resource.newResource("jar:"+jar.getURI()+"!/"+name);
                 if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
-                scanClass(handlers, jar, clazz.getInputStream());
+                try (InputStream is = clazz.getInputStream())
+                {
+                    scanClass(handlers, jar, is);
+                }
             }
         }
     }
@@ -963,9 +966,10 @@
     /**
      * Use ASM on a class
      * 
+     * @param handlers the handlers to look for classes in  
      * @param containingResource the dir or jar that the class is contained within, can be null if not known
-     * @param is
-     * @throws IOException
+     * @param is the input stream to parse
+     * @throws IOException if unable to parse
      */
     protected void scanClass (Set<? extends Handler> handlers, Resource containingResource, InputStream is)
     throws IOException
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassNameResolver.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassNameResolver.java
index f57118e..10cc9af 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassNameResolver.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassNameResolver.java
@@ -25,7 +25,7 @@
     /**
      * Based on the execution context, should the class represented
      * by "name" be excluded from consideration?
-     * @param name
+     * @param name the name to test
      * @return true if classname is excluded
      */
     public boolean isExcluded (String name);
@@ -35,7 +35,7 @@
      * Based on the execution context, if a duplicate class 
      * represented by "name" is detected, should the existing
      * one be overridden or not?
-     * @param name
+     * @param name the name to test
      * @return true if name should be overridden
      */
     public boolean shouldOverride (String name);
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java
index 97a7ba7..9ad0087 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java
@@ -28,15 +28,11 @@
 
 /**
  * ContainerInitializerAnnotationHandler
- *
+ * <p>
  *  Discovers classes that contain the specified annotation, either at class or
- *  method level. The specified annotation is derived from an @HandlesTypes on
+ *  method level. The specified annotation is derived from an <code>&#064;HandlesTypes</code> on
  *  a ServletContainerInitializer class.
  */
-/**
- * @author janb
- *
- */
 public class ContainerInitializerAnnotationHandler extends AbstractHandler
 {
     final ContainerInitializer _initializer;
@@ -51,7 +47,7 @@
     /**
      * Handle finding a class that is annotated with the annotation we were constructed with.
      * 
-     * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(ClassInfo, String)
+     * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(org.eclipse.jetty.annotations.AnnotationParser.ClassInfo, String)
      */
     public void handle(ClassInfo info, String annotationName)
     {
@@ -64,7 +60,7 @@
     /**
      * Handle finding a field that is annotated with the annotation we were constructed with.
      * 
-     * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(FieldInfo, String)
+     * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(org.eclipse.jetty.annotations.AnnotationParser.FieldInfo, String)
      */
     public void handle(FieldInfo info, String annotationName)
     {        
@@ -76,7 +72,7 @@
     /**
      * Handle finding a method that is annotated with the annotation we were constructed with. 
      * 
-     * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(MethodInfo, String)
+     * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(org.eclipse.jetty.annotations.AnnotationParser.MethodInfo, String)
      */
     public void handle(MethodInfo info, String annotationName)
     {
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/DeclareRolesAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/DeclareRolesAnnotationHandler.java
index 774ddd4..10c9107 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/DeclareRolesAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/DeclareRolesAnnotationHandler.java
@@ -27,17 +27,12 @@
 
 /**
  * DeclaresRolesAnnotationHandler
- *
- *
  */
 public class DeclareRolesAnnotationHandler extends AbstractIntrospectableAnnotationHandler
 {
 
     protected WebAppContext _context;
 
-    /**
-     * @param context
-     */
     public DeclareRolesAnnotationHandler(WebAppContext context)
     {
         super(false);
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PostConstructAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PostConstructAnnotationHandler.java
index 3c54b9e..7af47d1 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PostConstructAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PostConstructAnnotationHandler.java
@@ -44,7 +44,7 @@
     public void doHandle(Class clazz)
     {
         //Check that the PostConstruct is on a class that we're interested in
-        if (Util.supportsPostConstructPreDestroy(clazz))
+        if (supportsPostConstruct(clazz))
         {
             Method[] methods = clazz.getDeclaredMethods();
             for (int i=0; i<methods.length; i++)
@@ -52,7 +52,7 @@
                 Method m = (Method)methods[i];
                 if (m.isAnnotationPresent(PostConstruct.class))
                 {
-                    if (m.getParameterTypes().length != 0)
+                    if (m.getParameterCount() != 0)
                         throw new IllegalStateException(m+" has parameters");
                     if (m.getReturnType() != Void.TYPE)
                         throw new IllegalStateException(m+" is not void");
@@ -84,4 +84,27 @@
             }
         }
     }
+    
+    /** 
+     * Check if the given class is permitted to have PostConstruct annotation.
+     * @param c the class
+     * @return true if the spec permits the class to have PostConstruct, false otherwise
+     */
+    public boolean supportsPostConstruct (Class c)
+    {
+        if (javax.servlet.Servlet.class.isAssignableFrom(c) ||
+                javax.servlet.Filter.class.isAssignableFrom(c) || 
+                javax.servlet.ServletContextListener.class.isAssignableFrom(c) ||
+                javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
+                javax.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
+                javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
+                javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
+                javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
+                javax.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) ||
+                javax.servlet.AsyncListener.class.isAssignableFrom(c) ||
+                javax.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c))
+            return true;
+        
+        return false;
+    }
 }
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PreDestroyAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PreDestroyAnnotationHandler.java
index bdaf726..47d635f 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PreDestroyAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PreDestroyAnnotationHandler.java
@@ -43,7 +43,7 @@
     public void doHandle(Class clazz)
     {
         //Check that the PreDestroy is on a class that we're interested in
-        if (Util.supportsPostConstructPreDestroy(clazz))
+        if (supportsPreDestroy(clazz))
         {
             Method[] methods = clazz.getDeclaredMethods();
             for (int i=0; i<methods.length; i++)
@@ -51,7 +51,7 @@
                 Method m = (Method)methods[i];
                 if (m.isAnnotationPresent(PreDestroy.class))
                 {
-                    if (m.getParameterTypes().length != 0)
+                    if (m.getParameterCount() != 0)
                         throw new IllegalStateException(m+" has parameters");
                     if (m.getReturnType() != Void.TYPE)
                         throw new IllegalStateException(m+" is not void");
@@ -85,4 +85,27 @@
             }
         }
     }
+    
+    /**
+     * Check if the spec permits the given class to use the PreDestroy annotation.
+     * @param c the class
+     * @return true if permitted, false otherwise
+     */
+    public boolean supportsPreDestroy (Class c)
+    {
+        if (javax.servlet.Servlet.class.isAssignableFrom(c) ||
+                javax.servlet.Filter.class.isAssignableFrom(c) || 
+                javax.servlet.ServletContextListener.class.isAssignableFrom(c) ||
+                javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
+                javax.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
+                javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
+                javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
+                javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
+                javax.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) ||
+                javax.servlet.AsyncListener.class.isAssignableFrom(c) ||
+                javax.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c))
+            return true;
+        
+        return false;
+    }
 }
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ResourceAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ResourceAnnotationHandler.java
index fa35c4c..178c732 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ResourceAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ResourceAnnotationHandler.java
@@ -21,6 +21,8 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 
 import javax.annotation.Resource;
@@ -39,6 +41,10 @@
 public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationHandler
 {
     private static final Logger LOG = Log.getLogger(ResourceAnnotationHandler.class);
+    
+    protected static final List<Class<?>> ENV_ENTRY_TYPES = 
+            Arrays.asList(new Class[] {String.class, Character.class, Integer.class, Boolean.class, Double.class, Byte.class, Short.class, Long.class, Float.class});
+            
 
     protected WebAppContext _context;
 
@@ -57,7 +63,7 @@
      */
     public void doHandle(Class<?> clazz)
     {
-        if (Util.supportsResourceInjection(clazz))
+        if (supportsResourceInjection(clazz))
         {
             handleClass(clazz);
 
@@ -182,7 +188,7 @@
                         //TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination
                         metaData.setOrigin("resource-ref."+name+".injection",resource,clazz);
                     }
-                    else if (!Util.isEnvEntryType(type))
+                    else if (!isEnvEntryType(type))
                     {
                         //if this is an env-entry type resource and there is no value bound for it, it isn't
                         //an error, it just means that perhaps the code will use a default value instead
@@ -196,7 +202,7 @@
                     //if this is an env-entry type resource and there is no value bound for it, it isn't
                     //an error, it just means that perhaps the code will use a default value instead
                     // JavaEE Spec. sec 5.4.1.3
-                    if (!Util.isEnvEntryType(type))
+                    if (!isEnvEntryType(type))
                         throw new IllegalStateException(e);
                 }
             }
@@ -206,9 +212,12 @@
 
     /**
      * Process a Resource annotation on a Method.
-     *
+     * <p>
      * This will generate a JNDI entry, and an Injection to be
      * processed when an instance of the class is created.
+     * 
+     * @param clazz the class to process 
+     * @param method the method to process
      */
     public void handleMethod(Class<?> clazz, Method method)
     {
@@ -248,7 +257,7 @@
                 return;
             }
 
-            if (method.getParameterTypes().length != 1)
+            if (method.getParameterCount() != 1)
             {
                 LOG.warn("Skipping Resource annotation on "+clazz.getName()+"."+method.getName()+": invalid java bean, not single argument to method");
                 return;
@@ -336,7 +345,7 @@
                         //TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination
                         metaData.setOrigin("resource-ref."+name+".injection",resource,clazz);
                     }
-                    else if (!Util.isEnvEntryType(paramType))
+                    else if (!isEnvEntryType(paramType))
                     {
 
                         //if this is an env-entry type resource and there is no value bound for it, it isn't
@@ -350,11 +359,47 @@
                     //if this is an env-entry type resource and there is no value bound for it, it isn't
                     //an error, it just means that perhaps the code will use a default value instead
                     // JavaEE Spec. sec 5.4.1.3
-                    if (!Util.isEnvEntryType(paramType))
+                    if (!isEnvEntryType(paramType))
                         throw new IllegalStateException(e);
                 }
             }
 
         }
     }
+    
+    /**
+     * Check if the given Class is one that the specification allows to have a Resource annotation.
+     * 
+     * @param c the class
+     * @return true if Resource annotation permitted, false otherwise
+     */
+    public boolean supportsResourceInjection (Class<?> c)
+    {
+        if (javax.servlet.Servlet.class.isAssignableFrom(c) ||
+                javax.servlet.Filter.class.isAssignableFrom(c) || 
+                javax.servlet.ServletContextListener.class.isAssignableFrom(c) ||
+                javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
+                javax.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
+                javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
+                javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
+                javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
+                javax.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) ||
+                javax.servlet.AsyncListener.class.isAssignableFrom(c) ||
+                javax.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c))
+            return true;
+        
+        return false;
+    }
+    
+    
+    /**
+     * Check if the class is one of the basic java types permitted as 
+     * env-entries.
+     * @param clazz the class to check
+     * @return true if class is permitted by the spec to be an env-entry value
+     */
+    public boolean isEnvEntryType (Class<?> clazz)
+    {
+        return ENV_ENTRY_TYPES.contains(clazz);
+    }
 }
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializersStarter.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializersStarter.java
index 7f7978f..0472492 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializersStarter.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializersStarter.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.annotations;
 
 import java.util.List;
+
 import org.eclipse.jetty.plus.annotation.ContainerInitializer;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.util.component.AbstractLifeCycle;
@@ -38,9 +39,6 @@
     private static final Logger LOG = Log.getLogger(ServletContainerInitializersStarter.class);
     WebAppContext _context;
     
-    /**
-     * @param context
-     */
     public ServletContainerInitializersStarter(WebAppContext context)
     {
         _context = context;
@@ -71,6 +69,4 @@
             }
         }
     }
-    
-    
 }
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java
index 0c30999..d2713fd 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java
@@ -40,15 +40,15 @@
 /**
  * ServletSecurityAnnotationHandler
  *
- * Inspect a class to see if it has an @ServletSecurity annotation on it,
- * setting up the <security-constraint>s.
+ * Inspect a class to see if it has an <code>&#064;ServletSecurity</code> annotation on it,
+ * setting up the <code>&lt;security-constraint&gt;s</code>.
  *
  * A servlet can be defined in:
  * <ul>
- * <li>web.xml
- * <li>web-fragment.xml
- * <li>@WebServlet annotation discovered
- * <li>ServletContext.createServlet
+ * <li>web.xml</li>
+ * <li>web-fragment.xml</li>
+ * <li>@WebServlet annotation discovered</li>
+ * <li>ServletContext.createServlet</li>
  * </ul>
  *
  * The ServletSecurity annotation for a servlet should only be processed
@@ -119,12 +119,14 @@
 
 
     /**
-     * Make a jetty Constraint object, which represents the <auth-constraint> and
-     * <user-data-constraint> elements, based on the security annotation.
-     * @param servlet
-     * @param rolesAllowed
-     * @param permitOrDeny
-     * @param transport
+     * Make a jetty Constraint object, which represents the <code>&lt;auth-constraint&gt;</code> and
+     * <code>&lt;user-data-constraint&gt;</code> elements, based on the security annotation.
+     * 
+     * @param servlet the servlet
+     * @param rolesAllowed the roles allowed
+     * @param permitOrDeny the role / permission semantic
+     * @param transport the transport guarantee
+     * @return the constraint
      */
     protected Constraint makeConstraint (Class servlet, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
     {
@@ -135,7 +137,8 @@
 
     /**
      * Get the ServletMappings for the servlet's class.
-     * @param className
+     * @param className the class name
+     * @return the servlet mappings for the class
      */
     protected List<ServletMapping> getServletMappings(String className)
     {
@@ -154,9 +157,12 @@
 
 
     /**
-     * Check if there are already <security-constraint> elements defined that match the url-patterns for
+     * Check if there are already <code>&lt;security-constraint&gt;</code> elements defined that match the url-patterns for
      * the servlet.
-     * @param servletMappings
+     * 
+     * @param servletMappings the servlet mappings
+     * @param constraintMappings the constraint mappings
+     * @return true if constraint exists
      */
     protected boolean constraintsExist (List<ServletMapping> servletMappings, List<ConstraintMapping> constraintMappings)
     {
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java
index 0509700..ec2a566 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java
@@ -20,12 +20,14 @@
 
 import java.lang.reflect.Array;
 
+import org.eclipse.jetty.http.pathmap.ServletPathSpec;
 import org.eclipse.jetty.util.Loader;
 import org.eclipse.jetty.util.TypeUtil;
 import org.objectweb.asm.Type;
 
 /**
- * Util
+ * Annotation Processing Utilities
+ * @deprecated and replaced by extra methods in {@link ResourceAnnotationHandler}, {@link PreDestroyAnnotationHandler}, {@link PostConstructAnnotationHandler} and {@link ServletPathSpec}
  */
 public class Util
 { 
@@ -40,7 +42,7 @@
     /**
      * Check if the presented method belongs to a class that is one
      * of the classes with which a servlet container should be concerned.
-     * @param c
+     * @param c the class
      * @return true if class is a type of one of the following: 
      *          ({@link javax.servlet.Servlet}, 
      *           {@link javax.servlet.Filter}, 
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java
index efcef9d..cf3296d 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java
@@ -26,6 +26,7 @@
 import javax.servlet.annotation.WebFilter;
 import javax.servlet.annotation.WebInitParam;
 
+import org.eclipse.jetty.http.pathmap.ServletPathSpec;
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.FilterMapping;
 import org.eclipse.jetty.servlet.Holder;
@@ -39,17 +40,11 @@
 
 /**
  * WebFilterAnnotation
- *
- *
  */
 public class WebFilterAnnotation extends DiscoveredAnnotation
 {
     private static final Logger LOG = Log.getLogger(WebFilterAnnotation.class);
 
-    /**
-     * @param context
-     * @param className
-     */
     public WebFilterAnnotation(WebAppContext context, String className)
     {
         super(context, className);
@@ -123,7 +118,7 @@
                 ArrayList<String> paths = new ArrayList<String>();
                 for (String s:urlPatterns)
                 {
-                    paths.add(Util.normalizePattern(s));
+                    paths.add(ServletPathSpec.normalize(s));
                 }
                 mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
             }
@@ -194,7 +189,7 @@
                     ArrayList<String> paths = new ArrayList<String>();
                     for (String s:urlPatterns)
                     {
-                        paths.add(Util.normalizePattern(s));
+                        paths.add(ServletPathSpec.normalize(s));
                     }
                     mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
                 }
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java
index c7bfa9b..bca9dc4 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java
@@ -40,17 +40,11 @@
 
 /**
  * WebListenerAnnotation
- *
- *
  */
 public class WebListenerAnnotation extends DiscoveredAnnotation
 {
     private static final Logger LOG = Log.getLogger(WebListenerAnnotation.class);
 
-    /**
-     * @param context
-     * @param className
-     */
     public WebListenerAnnotation(WebAppContext context, String className)
     {
         super(context, className);
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java
index a4e0587..e97693f 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java
@@ -34,10 +34,6 @@
        super(context);
     }
     
-  
-    /** 
-     * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(ClassInfo, String)
-     */
     public void handle(ClassInfo info, String annotationName)
     {
         if (annotationName == null || !"javax.servlet.annotation.WebListener".equals(annotationName))
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java
index eab3195..7fcb4cb 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.annotations;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -28,6 +27,7 @@
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServlet;
 
+import org.eclipse.jetty.http.pathmap.ServletPathSpec;
 import org.eclipse.jetty.servlet.Holder;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.servlet.ServletMapping;
@@ -54,13 +54,13 @@
     {
         super(context, className);
     }
-    
-    
+
+
     public WebServletAnnotation (WebAppContext context, String className, Resource resource)
     {
         super(context, className, resource);
     }
-    
+
     /**
      * @see DiscoveredAnnotation#apply()
      */
@@ -103,7 +103,7 @@
         //canonicalize the patterns
         ArrayList<String> urlPatternList = new ArrayList<String>();
         for (String p : urlPatterns)
-            urlPatternList.add(Util.normalizePattern(p));
+            urlPatternList.add(ServletPathSpec.normalize(p));
 
         String servletName = (annotation.name().equals("")?clazz.getName():annotation.name());
 
@@ -126,7 +126,7 @@
             }
         }
 
-        //handle creation/completion of a servlet 
+        //handle creation/completion of a servlet
         if (holder == null)
         {
             //No servlet of this name has already been defined, either by a descriptor
@@ -152,8 +152,8 @@
             }
 
             _context.getServletHandler().addServlet(holder);
-            
-            
+
+
             mapping = new ServletMapping();
             mapping.setServletName(holder.getName());
             mapping.setPathSpecs( LazyList.toStringArray(urlPatternList));
@@ -179,16 +179,16 @@
                     metaData.setOrigin(servletName+".servlet.init-param."+ip.name(),ip,clazz);
                 }
             }
-            
+
 
             //check the url-patterns
             //ServletSpec 3.0 p81 If a servlet already has url mappings from a
-            //webxml or fragment descriptor the annotation is ignored. 
-            //However, we want to be able to replace mappings that were given in webdefault.xml         
+            //webxml or fragment descriptor the annotation is ignored.
+            //However, we want to be able to replace mappings that were given in webdefault.xml
             List<ServletMapping> existingMappings = getServletMappingsForServlet(servletName);
-            
-            //if any mappings for this servlet already set by a descriptor that is not webdefault.xml forget 
-            //about processing these url mappings        
+
+            //if any mappings for this servlet already set by a descriptor that is not webdefault.xml forget
+            //about processing these url mappings
             if (existingMappings.isEmpty() || !containsNonDefaultMappings(existingMappings))
             {
                 mapping = new ServletMapping();
@@ -197,7 +197,7 @@
             }
         }
 
-        
+
         //We also want to be able to replace mappings that were defined in webdefault.xml
         //that were for a different servlet eg a mapping in webdefault.xml for / to the jetty
         //default servlet should be able to be replaced by an annotation for / to a different
@@ -209,7 +209,7 @@
             //take a copy of the existing servlet mappings that we can iterate over and remove from. This is
             //because the ServletHandler interface does not support removal of individual mappings.
             List<ServletMapping> allMappings = ArrayUtil.asMutableList(_context.getServletHandler().getServletMappings());
-            
+
             //for each of the urls in the annotation, check if a mapping to same/different servlet exists
             //  if mapping exists and is from a default descriptor, it can be replaced. NOTE: we do not
             //  guard against duplicate path mapping here: that is the job of the ServletHandler
@@ -221,11 +221,11 @@
                     String[] updatedPaths = ArrayUtil.removeFromArray(existingMapping.getPathSpecs(), p);
                     //if we removed the last path from a servletmapping, delete the servletmapping
                     if (updatedPaths == null || updatedPaths.length == 0)
-                    { 
+                    {
                         boolean success = allMappings.remove(existingMapping);
                         if (LOG.isDebugEnabled()) LOG.debug("Removed empty mapping {} from defaults descriptor success:{}",existingMapping, success);
                     }
-                    else 
+                    else
                     {
                         existingMapping.setPathSpecs(updatedPaths);
                         if (LOG.isDebugEnabled()) LOG.debug("Removed path {} from mapping {} from defaults descriptor ", p,existingMapping);
@@ -239,8 +239,8 @@
     }
 
 
-    
-    
+
+
     /**
      * @param name
      * @return
@@ -261,8 +261,8 @@
         }
         return mappings;
     }
-    
-    
+
+
     /**
      * @param mappings
      * @return
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java
index 1791e40..8623104 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java
@@ -29,7 +29,6 @@
  * WebServletAnnotationHandler
  *
  * Process a WebServlet annotation on a class.
- *
  */
 public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationHandler
 {
@@ -43,8 +42,6 @@
 
     /**
      * Handle discovering a WebServlet annotation.
-     *
-     * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(ClassInfo, String)
      */
     @Override
     public void handle(ClassInfo info, String annotationName)
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java
index 785e664..f4d77cd 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java
@@ -25,6 +25,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.webapp.FragmentDescriptor;
 import org.eclipse.jetty.webapp.WebAppContext;
@@ -40,7 +41,7 @@
     @Test
     public void testGetFragmentFromJar() throws Exception
     {
-        String dir = System.getProperty("basedir", ".");
+        String dir = MavenTestingUtils.getTargetTestingDir("getFragmentFromJar").getAbsolutePath();
         File file = new File(dir);
         file=new File(file.getCanonicalPath());
         URL url=file.toURL();
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
index f73aed1..53622f3 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
@@ -183,7 +183,7 @@
         // Intentionally using a base director name that starts with a "."
         // This mimics what you see in jenkins, hudson, hadoop, solr, camel, and selenium for their 
         // installed and/or managed webapps
-        File basedir = testdir.getFile(".base/workspace/classes");
+        File basedir = testdir.getPathFile(".base/workspace/classes").toFile();
         FS.ensureEmpty(basedir);
 
         // Copy in class that is known to have annotations.
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java
index df4ab8d..079a19d 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java
@@ -18,9 +18,6 @@
 
 package org.eclipse.jetty.annotations;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -34,6 +31,13 @@
 import org.eclipse.jetty.webapp.WebAppContext;
 import org.junit.Test;
 
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 /**
  * TestServletAnnotations
  */
@@ -56,7 +60,7 @@
             _list.add(a);
         }
     }
-    
+
     @Test
     public void testServletAnnotation() throws Exception
     {
@@ -66,9 +70,9 @@
 
         WebAppContext wac = new WebAppContext();
         List<DiscoveredAnnotation> results = new ArrayList<DiscoveredAnnotation>();
-        
+
         TestWebServletAnnotationHandler handler = new TestWebServletAnnotationHandler(wac, results);
-       
+
         parser.parse(Collections.singleton(handler), classes, new ClassNameResolver ()
         {
             public boolean isExcluded(String name)
@@ -82,7 +86,7 @@
             }
         });
 
-  
+
         assertEquals(1, results.size());
         assertTrue(results.get(0) instanceof WebServletAnnotation);
 
@@ -91,14 +95,14 @@
         ServletHolder[] holders = wac.getServletHandler().getServlets();
         assertNotNull(holders);
         assertEquals(1, holders.length);
-        
+
         // Verify servlet annotations
         ServletHolder cholder = holders[0];
         assertThat("Servlet Name", cholder.getName(), is("CServlet"));
         assertThat("InitParameter[x]", cholder.getInitParameter("x"), is("y"));
         assertThat("Init Order", cholder.getInitOrder(), is(2));
         assertThat("Async Supported", cholder.isAsyncSupported(), is(false));
-        
+
         // Verify mappings
         ServletMapping[] mappings = wac.getServletHandler().getServletMappings();
         assertNotNull(mappings);
@@ -107,33 +111,33 @@
         assertNotNull(paths);
         assertEquals(2, paths.length);
     }
-    
-    
+
+
     @Test
     public void testWebServletAnnotationOverrideDefault () throws Exception
     {
         //if the existing servlet mapping TO A DIFFERENT SERVLET IS from a default descriptor we
         //DO allow the annotation to replace the mapping.
-        
+
         WebAppContext wac = new WebAppContext();
         ServletHolder defaultServlet = new ServletHolder();
         defaultServlet.setClassName("org.eclipse.jetty.servlet.DefaultServlet");
-        defaultServlet.setName("default");      
+        defaultServlet.setName("default");
         wac.getServletHandler().addServlet(defaultServlet);
-        
+
         ServletMapping m = new ServletMapping();
         m.setPathSpec("/");
         m.setServletName("default");
         m.setDefault(true);  //this mapping will be from a default descriptor
         wac.getServletHandler().addServletMapping(m);
-        
+
         WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.annotations.ServletD", null);
         annotation.apply();
-        
+
         //test that as the original servlet mapping had only 1 pathspec, then the whole
         //servlet mapping should be deleted as that pathspec will be remapped to the DServlet
         ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
-        assertNotNull(resultMappings);    
+        assertNotNull(resultMappings);
         assertEquals(1, resultMappings.length);
         assertEquals(2, resultMappings[0].getPathSpecs().length);
         resultMappings[0].getServletName().equals("DServlet");
@@ -142,38 +146,38 @@
             assertTrue (s.equals("/") || s.equals("/bah/*"));
         }
     }
-    
-    
-    
+
+
+
     @Test
     public void testWebServletAnnotationReplaceDefault () throws Exception
     {
         //if the existing servlet mapping TO A DIFFERENT SERVLET IS from a default descriptor we
-        //DO allow the annotation to replace the mapping.    
+        //DO allow the annotation to replace the mapping.
         WebAppContext wac = new WebAppContext();
         ServletHolder defaultServlet = new ServletHolder();
         defaultServlet.setClassName("org.eclipse.jetty.servlet.DefaultServlet");
-        defaultServlet.setName("default");      
+        defaultServlet.setName("default");
         wac.getServletHandler().addServlet(defaultServlet);
-        
+
         ServletMapping m = new ServletMapping();
         m.setPathSpec("/");
         m.setServletName("default");
         m.setDefault(true);  //this mapping will be from a default descriptor
         wac.getServletHandler().addServletMapping(m);
-        
+
         ServletMapping m2 = new ServletMapping();
         m2.setPathSpec("/other");
         m2.setServletName("default");
         m2.setDefault(true);  //this mapping will be from a default descriptor
         wac.getServletHandler().addServletMapping(m2);
-        
+
         WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.annotations.ServletD", null);
         annotation.apply();
-        
+
         //test that only the mapping for "/" was removed from the mappings to the default servlet
         ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
-        assertNotNull(resultMappings);    
+        assertNotNull(resultMappings);
         assertEquals(2, resultMappings.length);
         for (ServletMapping r:resultMappings)
         {
@@ -194,10 +198,10 @@
            else
                fail("Unexpected servlet mapping");
         }
-        
+
     }
-    
-    
+
+
     @Test
     public void testWebServletAnnotationNotOverride () throws Exception
     {
@@ -232,7 +236,7 @@
                 fail("Unexpected servlet name");
         }
     }
-    
+
     @Test
     public void testWebServletAnnotationIgnore () throws Exception
     {
@@ -243,33 +247,33 @@
         servlet.setClassName("org.eclipse.jetty.servlet.OtherDServlet");
         servlet.setName("DServlet");
         wac.getServletHandler().addServlet(servlet);
-        
+
         ServletMapping m = new ServletMapping();
         m.setPathSpec("/default");
         m.setDefault(true);
         m.setServletName("DServlet");
         wac.getServletHandler().addServletMapping(m);
-        
+
         ServletMapping m2 = new ServletMapping();
         m2.setPathSpec("/other");
         m2.setServletName("DServlet");
         wac.getServletHandler().addServletMapping(m2);
-        
+
         WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.annotations.ServletD", null);
         annotation.apply();
 
         ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
         assertEquals(2, resultMappings.length);
-        
+
         for (ServletMapping r:resultMappings)
         {
             assertEquals(1, r.getPathSpecs().length);
             if (!r.getPathSpecs()[0].equals("/default") && !r.getPathSpecs()[0].equals("/other"))
                 fail("Unexpected path in mapping");
         }
-        
+
     }
-    
+
     @Test
     public void testWebServletAnnotationNoMappings () throws Exception
     {
@@ -293,8 +297,8 @@
                 fail("Unexpected path mapping");
         }
     }
-    
-    
+
+
 
     @Test
     public void testDeclareRoles ()
diff --git a/jetty-ant/pom.xml b/jetty-ant/pom.xml
index 72bbea2..2f56f74 100644
--- a/jetty-ant/pom.xml
+++ b/jetty-ant/pom.xml
@@ -2,13 +2,16 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-ant</artifactId>
   <packaging>jar</packaging>
   <name>Jetty :: Ant Plugin</name>
  
+  <properties>
+    <bundle-symbolic-name>org.eclipse.jetty.ant</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
@@ -22,8 +25,8 @@
             </goals>
             <configuration>
               <includeGroupIds>org.eclipse.jetty</includeGroupIds>
-              <excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.spdy,org.eclipse.jetty.websocket,org.eclipse.jetty.drafts</excludeGroupIds>
-              <excludeArtifactIds>jetty-all,jetty-start,jetty-monitor,jetty-jsp</excludeArtifactIds>
+              <excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.websocket,org.eclipse.jetty.drafts</excludeGroupIds>
+              <excludeArtifactIds>jetty-all,jetty-start,jetty-monitor</excludeArtifactIds>
               <includeTypes>jar</includeTypes>
               <outputDirectory>${project.build.directory}/test-lib</outputDirectory>
             </configuration>
diff --git a/jetty-ant/src/main/java/org/eclipse/jetty/ant/AntWebAppContext.java b/jetty-ant/src/main/java/org/eclipse/jetty/ant/AntWebAppContext.java
index a8752b3..7512581 100644
--- a/jetty-ant/src/main/java/org/eclipse/jetty/ant/AntWebAppContext.java
+++ b/jetty-ant/src/main/java/org/eclipse/jetty/ant/AntWebAppContext.java
@@ -16,7 +16,6 @@
 //  ========================================================================
 //
 
-
 package org.eclipse.jetty.ant;
 
 import java.io.File;
@@ -67,13 +66,8 @@
 import org.eclipse.jetty.webapp.WebXmlConfiguration;
 import org.eclipse.jetty.xml.XmlConfiguration;
 
-
-
 /**
- * AntWebAppContext
- * 
  * Extension of WebAppContext to allow configuration via Ant environment.
- *
  */
 public class AntWebAppContext extends WebAppContext
 {
@@ -432,6 +426,7 @@
      * Default constructor. Takes project as an argument
      *
      * @param project the project.
+     * @throws Exception if unable to create webapp context
      */
     public AntWebAppContext(Project project) throws Exception
     {
@@ -445,6 +440,7 @@
 
     /**
      * Adds a new Ant's attributes tag object if it have not been created yet.
+     * @param atts the attributes
      */
     public void addAttributes(Attributes atts)
     {
diff --git a/jetty-ant/src/main/java/org/eclipse/jetty/ant/JettyRunTask.java b/jetty-ant/src/main/java/org/eclipse/jetty/ant/JettyRunTask.java
index 626ea4d..cc2cb49 100644
--- a/jetty-ant/src/main/java/org/eclipse/jetty/ant/JettyRunTask.java
+++ b/jetty-ant/src/main/java/org/eclipse/jetty/ant/JettyRunTask.java
@@ -41,10 +41,8 @@
  */
 public class JettyRunTask extends Task
 {
-
     private int scanIntervalSeconds; 
     
-    
     /** Temporary files directory. */
     private File tempDirectory;
 
@@ -79,9 +77,6 @@
 
     private boolean daemon;
     
-  
-
-
     public JettyRunTask()
     {
         TaskLog.setTask(this);
@@ -89,17 +84,16 @@
 
     /**
      * Creates a new <code>WebApp</code> Ant object.
-     *
+     * @param webapp the webapp context 
      */
     public void addWebApp(AntWebAppContext webapp)
     {
        webapps.add(webapp);
     }
-    
-    
 
     /**
      * Adds a new Ant's connector tag object if it have not been created yet.
+     * @param connectors the connectors 
      */
     public void addConnectors(Connectors connectors)
     {
@@ -108,10 +102,6 @@
         this.connectors = connectors;
     }
 
-
-    /**
-     * @param services
-     */
     public void addLoginServices(LoginServices services)
     {        
         if (this.loginServices != null )
@@ -126,9 +116,6 @@
         this.systemProperties = systemProperties;
     }
     
-    /**
-     * @param handlers
-     */
     public void addContextHandlers (ContextHandlers handlers)
     {
         if (this.contextHandlers != null)
@@ -141,9 +128,6 @@
         return tempDirectory;
     }
 
-    /**
-     * @param tempDirectory
-     */
     public void setTempDirectory(File tempDirectory)
     {
         this.tempDirectory = tempDirectory;
@@ -154,17 +138,11 @@
         return jettyXml;
     }
 
-    /**
-     * @param jettyXml
-     */
     public void setJettyXml(File jettyXml)
     {
         this.jettyXml = jettyXml;
     }
 
-    /**
-     * @param className
-     */
     public void setRequestLog(String className)
     {
         try
@@ -209,7 +187,7 @@
      * Executes this Ant task. The build flow is being stopped until Jetty
      * server stops.
      *
-     * @throws BuildException
+     * @throws BuildException if unable to build
      */
     public void execute() throws BuildException
     {
@@ -297,17 +275,12 @@
         return scanIntervalSeconds;
     }
 
-    /**
-     * @param secs
-     */
     public void setScanIntervalSeconds(int secs)
     {
         scanIntervalSeconds = secs;
         TaskLog.log("scanIntervalSecs="+secs);
     }
     
-
-    
     /**
      * Sets the system properties.
      */
diff --git a/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/Connectors.java b/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/Connectors.java
index 9c425e1..6b25db1 100644
--- a/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/Connectors.java
+++ b/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/Connectors.java
@@ -23,11 +23,7 @@
 import java.util.List;
 
 /**
- * 
- * Connectors
- * 
- * Specifies a jetty configuration <connectors/> element for Ant build file.
- *
+ * Specifies a jetty configuration <code>&lt;connectors/&gt;</code> element for Ant build file.
  */
 public class Connectors
 {
diff --git a/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/ContextHandlers.java b/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/ContextHandlers.java
index bfbf49d..0c25058 100644
--- a/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/ContextHandlers.java
+++ b/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/ContextHandlers.java
@@ -25,8 +25,7 @@
 import org.eclipse.jetty.server.handler.ContextHandler;
 
 /**
- * Specifies <contextHandlers/> element in web app configuration.
- * 
+ * Specifies <code>&lt;contextHandlers/&gt;</code> element in web app configuration.
  */
 public class ContextHandlers
 {
diff --git a/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/FileMatchingConfiguration.java b/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/FileMatchingConfiguration.java
index 5576780..73749f9 100644
--- a/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/FileMatchingConfiguration.java
+++ b/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/FileMatchingConfiguration.java
@@ -27,10 +27,9 @@
 import org.apache.tools.ant.DirectoryScanner;
 
 /**
- * Describes set of files matched by <fileset/> elements in ant configuration
+ * Describes set of files matched by <code>&lt;fileset/&gt;</code> elements in ant configuration
  * file. It is used to group application classes, libraries, and scannedTargets
  * elements.
- * 
  */
 public class FileMatchingConfiguration
 {
@@ -44,7 +43,7 @@
 
     /**
      * @param directoryScanner new directory scanner retrieved from the
-     *            <fileset/> element.
+     *            <code>&lt;fileset/&gt;</code> element.
      */
     public void addDirectoryScanner(DirectoryScanner directoryScanner)
     {
diff --git a/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/LoginServices.java b/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/LoginServices.java
index f83a1d7..867f99b 100644
--- a/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/LoginServices.java
+++ b/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/LoginServices.java
@@ -25,14 +25,10 @@
 import org.eclipse.jetty.security.LoginService;
 
 /**
- * LoginServices
- * 
- * Specifies a jetty configuration <loginServices/> element for Ant build file.
- *
+ * Specifies a jetty configuration &lt;loginServices/&gt; element for Ant build file.
  */
 public class LoginServices
 {
-
     private List<LoginService> loginServices = new ArrayList<LoginService>();
 
     public void add(LoginService service)
diff --git a/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/SystemProperties.java b/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/SystemProperties.java
index 72bed64..3e2b38b 100644
--- a/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/SystemProperties.java
+++ b/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/SystemProperties.java
@@ -27,9 +27,8 @@
 
 /**
  * SystemProperties
- * 
- * Ant <systemProperties/> tag definition.
- * 
+ * <p> 
+ * Ant &lt;systemProperties/&gt; tag definition.
  */
 public class SystemProperties
 {
@@ -48,8 +47,8 @@
 
     /**
      * Set a System.property with this value if it is not already set.
-     * 
-     * @returns true if property has been set
+     * @param property the property to test 
+     * @return true if property has been set
      */
     public static boolean setIfNotSetAlready(Property property)
     {
diff --git a/jetty-ant/src/test/java/org/eclipse/jetty/ant/AntBuild.java b/jetty-ant/src/test/java/org/eclipse/jetty/ant/AntBuild.java
index 48a6edb..c16b459 100644
--- a/jetty-ant/src/test/java/org/eclipse/jetty/ant/AntBuild.java
+++ b/jetty-ant/src/test/java/org/eclipse/jetty/ant/AntBuild.java
@@ -64,7 +64,7 @@
             Project antProject = new Project();
             try
             {
-                antProject.setBaseDir(MavenTestingUtils.getBasedir());
+                antProject.setBaseDir(MavenTestingUtils.getBaseDir());
                 antProject.setUserProperty("ant.file",buildFile.getAbsolutePath());
                 DefaultLogger logger = new DefaultLogger();
 
diff --git a/jetty-bom/pom.xml b/jetty-bom/pom.xml
new file mode 100644
index 0000000..0fefd5e
--- /dev/null
+++ b/jetty-bom/pom.xml
@@ -0,0 +1,228 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-project</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>jetty-bom</artifactId>
+  <name>Jetty :: Bom</name>
+  <description>Jetty BOM artifact</description>
+  <packaging>pom</packaging>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-annotations</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>cdi-core</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>cdi-full-servlet</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>cdi-servlet</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>cdi-websocket</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-client</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-continuation</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-gcloud-memcached-session-manager</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-gcloud-session-manager</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-http</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-http-spi</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-infinispan</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-io</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-jaas</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-jaspi</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-jmx</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-jndi</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-monitor</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-nosql</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-osgi-boot</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-osgi-boot-jsp</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-osgi-boot-warurl</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-plus</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-proxy</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-quickstart</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-rewrite</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-security</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-server</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-servlet</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-servlets</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-spring</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-util</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-util-ajax</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-webapp</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>javax-websocket-client-impl</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>javax-websocket-server-impl</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>javax-websocket-api</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>javax-websocket-client</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>javax-websocket-common</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>javax-websocket-server</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>javax-websocket-servlet</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-xml</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+</project>
diff --git a/jetty-cdi/cdi-core/pom.xml b/jetty-cdi/cdi-core/pom.xml
new file mode 100644
index 0000000..5b1066a
--- /dev/null
+++ b/jetty-cdi/cdi-core/pom.xml
@@ -0,0 +1,71 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.cdi</groupId>
+    <artifactId>jetty-cdi-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cdi-core</artifactId>
+  <name>Jetty :: CDI :: Core</name>
+  <description>
+    Core CDI routines for Jetty (Also useful for CDI/SE applications that dont need a server)
+  </description>
+  <url>http://www.eclipse.org/jetty</url>
+  <packaging>jar</packaging>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.core</bundle-symbolic-name>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-util</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+       <groupId>javax.enterprise</groupId>
+       <artifactId>cdi-api</artifactId>
+       <version>1.2</version>
+       <exclusions>
+         <exclusion>
+           <groupId>javax.el</groupId>
+           <artifactId>javax.el-api</artifactId>
+         </exclusion>
+       </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.weld</groupId>
+      <artifactId>weld-core</artifactId>
+      <version>${weld.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.weld.se</groupId>
+      <artifactId>weld-se-core</artifactId>
+      <version>${weld.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>tests-jar</id>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/AnyLiteral.java b/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/AnyLiteral.java
new file mode 100644
index 0000000..600c931
--- /dev/null
+++ b/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/AnyLiteral.java
@@ -0,0 +1,28 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.core;
+
+import javax.enterprise.inject.Any;
+import javax.enterprise.util.AnnotationLiteral;
+
+@SuppressWarnings("serial")
+public class AnyLiteral extends AnnotationLiteral<Any>
+{
+    public static final AnyLiteral INSTANCE = new AnyLiteral();
+}
diff --git a/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/JettyLogFactory.java b/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/JettyLogFactory.java
new file mode 100644
index 0000000..8affb75
--- /dev/null
+++ b/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/JettyLogFactory.java
@@ -0,0 +1,37 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.core;
+
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.InjectionPoint;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * CDI Producer of Jetty Logging instances.
+ */
+public class JettyLogFactory
+{
+    @Produces
+    public Logger createLogger(InjectionPoint injectionPoint)
+    {
+        return Log.getLogger(injectionPoint.getMember().getDeclaringClass());
+    }
+}
diff --git a/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/NamedLiteral.java b/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/NamedLiteral.java
new file mode 100644
index 0000000..651ca6c
--- /dev/null
+++ b/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/NamedLiteral.java
@@ -0,0 +1,45 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.core;
+
+import javax.enterprise.util.AnnotationLiteral;
+import javax.inject.Named;
+
+@SuppressWarnings("serial")
+public class NamedLiteral extends AnnotationLiteral<Named> implements Named
+{
+    private final String value;
+
+    public String value()
+    {
+        return value;
+    }
+
+    public NamedLiteral(String name)
+    {
+        if (name == null)
+        {
+            this.value = "";
+        }
+        else
+        {
+            this.value = name;
+        }
+    }
+}
\ No newline at end of file
diff --git a/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/ScopedInstance.java b/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/ScopedInstance.java
new file mode 100644
index 0000000..322dfdb
--- /dev/null
+++ b/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/ScopedInstance.java
@@ -0,0 +1,45 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.core;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+
+public class ScopedInstance<T>
+{
+    public Bean<T> bean;
+    public CreationalContext<T> creationalContext;
+    public T instance;
+
+    public void destroy()
+    {
+        bean.destroy(instance,creationalContext);
+    }
+    
+    @Override
+    public String toString()
+    {
+        StringBuilder s = new StringBuilder();
+        s.append("ScopedInstance[");
+        s.append(bean);
+        s.append(',').append(creationalContext);
+        s.append(']');
+        return s.toString();
+    }
+}
diff --git a/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/SimpleBeanStore.java b/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/SimpleBeanStore.java
new file mode 100644
index 0000000..b4e8522
--- /dev/null
+++ b/jetty-cdi/cdi-core/src/main/java/org/eclipse/jetty/cdi/core/SimpleBeanStore.java
@@ -0,0 +1,94 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.core;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.enterprise.context.spi.Contextual;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class SimpleBeanStore
+{
+    private static final Logger LOG = Log.getLogger(SimpleBeanStore.class);
+
+    public Map<Contextual<?>, List<ScopedInstance<?>>> beans = new HashMap<>();
+
+    public void addBean(ScopedInstance<?> instance)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("addBean({})",instance);
+        }
+        List<ScopedInstance<?>> instances = getBeans(instance.bean);
+        if (instances == null)
+        {
+            instances = new ArrayList<>();
+            beans.put(instance.bean,instances);
+        }
+        instances.add(instance);
+    }
+
+    public void clear()
+    {
+        beans.clear();
+    }
+
+    public void destroy()
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("destroy() - {} beans",beans.size());
+        }
+        for (List<ScopedInstance<?>> instances : beans.values())
+        {
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("destroying - {} instance(s)",instances.size());
+            }
+            for (ScopedInstance<?> instance : instances)
+            {
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("destroying instance {}",instance);
+                }
+                instance.destroy();
+            }
+        }
+    }
+
+    public List<ScopedInstance<?>> getBeans(Contextual<?> contextual)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("getBeans({})",contextual);
+        }
+        return beans.get(contextual);
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%X[size=%d]",this.getClass().getSimpleName(),hashCode(),beans.size());
+    }
+}
\ No newline at end of file
diff --git a/jetty-cdi/cdi-core/src/main/resources/META-INF/beans.xml b/jetty-cdi/cdi-core/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..f158a71
--- /dev/null
+++ b/jetty-cdi/cdi-core/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,6 @@
+<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
+        http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
+       bean-discovery-mode="all">
+</beans>
\ No newline at end of file
diff --git a/jetty-cdi/cdi-core/src/test/java/org/eclipse/jetty/cdi/core/AbstractWeldTest.java b/jetty-cdi/cdi-core/src/test/java/org/eclipse/jetty/cdi/core/AbstractWeldTest.java
new file mode 100644
index 0000000..e5e5038
--- /dev/null
+++ b/jetty-cdi/cdi-core/src/test/java/org/eclipse/jetty/cdi/core/AbstractWeldTest.java
@@ -0,0 +1,78 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.core;
+
+import java.util.Set;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+
+import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.environment.se.WeldContainer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public abstract class AbstractWeldTest
+{
+    public static class TestBean<T>
+    {
+        public Bean<T> bean;
+        public CreationalContext<T> cCtx;
+        public T instance;
+
+        public void destroy()
+        {
+            bean.destroy(instance,cCtx);
+        }
+    }
+
+    @BeforeClass
+    public static void initWeld()
+    {
+        weld = new Weld();
+        container = weld.initialize();
+    }
+
+    @AfterClass
+    public static void shutdownWeld()
+    {
+        weld.shutdown();
+    }
+
+    private static WeldContainer container;
+    private static Weld weld;
+
+    @SuppressWarnings("unchecked")
+    public <T> TestBean<T> newInstance(Class<T> clazz) throws Exception
+    {
+        TestBean<T> testBean = new TestBean<>();
+        Set<Bean<?>> beans = container.getBeanManager().getBeans(clazz,AnyLiteral.INSTANCE);
+        if (beans.size() > 0)
+        {
+            testBean.bean = (Bean<T>)beans.iterator().next();
+            testBean.cCtx = container.getBeanManager().createCreationalContext(testBean.bean);
+            testBean.instance = (T)container.getBeanManager().getReference(testBean.bean,clazz,testBean.cCtx);
+            return testBean;
+        }
+        else
+        {
+            throw new Exception(String.format("Can't find class %s",clazz));
+        }
+    }
+}
diff --git a/jetty-cdi/cdi-core/src/test/java/org/eclipse/jetty/cdi/core/logging/LeanConsoleHandler.java b/jetty-cdi/cdi-core/src/test/java/org/eclipse/jetty/cdi/core/logging/LeanConsoleHandler.java
new file mode 100644
index 0000000..ffa0868
--- /dev/null
+++ b/jetty-cdi/cdi-core/src/test/java/org/eclipse/jetty/cdi/core/logging/LeanConsoleHandler.java
@@ -0,0 +1,112 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.core.logging;
+
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.regex.Pattern;
+
+public class LeanConsoleHandler extends Handler
+{
+    public static Handler createWithLevel(Level level)
+    {
+        LeanConsoleHandler handler = new LeanConsoleHandler();
+        handler.setLevel(level);
+        return handler;
+    }
+
+    @Override
+    public void close() throws SecurityException
+    {
+        /* nothing to do here */
+    }
+
+    @Override
+    public void flush()
+    {
+        /* nothing to do here */
+    }
+
+    public synchronized String formatMessage(LogRecord record)
+    {
+        String msg = getMessage(record);
+
+        try
+        {
+            Object params[] = record.getParameters();
+            if ((params == null) || (params.length == 0))
+            {
+                return msg;
+            }
+
+            if (Pattern.compile("\\{\\d+\\}").matcher(msg).find())
+            {
+                return MessageFormat.format(msg,params);
+            }
+
+            return msg;
+        }
+        catch (Exception ex)
+        {
+            return msg;
+        }
+    }
+
+    private String getMessage(LogRecord record)
+    {
+        ResourceBundle bundle = record.getResourceBundle();
+        if (bundle != null)
+        {
+            try
+            {
+                return bundle.getString(record.getMessage());
+            }
+            catch (java.util.MissingResourceException ex)
+            {
+            }
+        }
+
+        return record.getMessage();
+    }
+
+    @Override
+    public void publish(LogRecord record)
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append("[").append(record.getLevel().getName()).append("] ");
+        String logname = record.getLoggerName();
+        int idx = logname.lastIndexOf('.');
+        if (idx > 0)
+        {
+            logname = logname.substring(idx + 1);
+        }
+        buf.append(logname);
+        buf.append(": ");
+        buf.append(formatMessage(record));
+
+        System.out.println(buf.toString());
+        if (record.getThrown() != null)
+        {
+            record.getThrown().printStackTrace(System.out);
+        }
+    }
+}
diff --git a/jetty-cdi/cdi-core/src/test/java/org/eclipse/jetty/cdi/core/logging/LogFactory.java b/jetty-cdi/cdi-core/src/test/java/org/eclipse/jetty/cdi/core/logging/LogFactory.java
new file mode 100644
index 0000000..a5438c0
--- /dev/null
+++ b/jetty-cdi/cdi-core/src/test/java/org/eclipse/jetty/cdi/core/logging/LogFactory.java
@@ -0,0 +1,33 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.core.logging;
+
+import java.util.logging.Logger;
+
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.InjectionPoint;
+
+public class LogFactory
+{
+    @Produces
+    public Logger createLogger(InjectionPoint injectionPoint)
+    {
+        return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
+    }
+}
diff --git a/jetty-cdi/cdi-core/src/test/java/org/eclipse/jetty/cdi/core/logging/Logging.java b/jetty-cdi/cdi-core/src/test/java/org/eclipse/jetty/cdi/core/logging/Logging.java
new file mode 100644
index 0000000..6944e03
--- /dev/null
+++ b/jetty-cdi/cdi-core/src/test/java/org/eclipse/jetty/cdi/core/logging/Logging.java
@@ -0,0 +1,46 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.core.logging;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.logging.LogManager;
+
+public class Logging
+{
+    public static void config()
+    {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        URL url = cl.getResource("logging.properties");
+        if (url != null)
+        {
+            try (InputStream in = url.openStream())
+            {
+                LogManager.getLogManager().readConfiguration(in);
+            }
+            catch (IOException e)
+            {
+                e.printStackTrace(System.err);
+            }
+        }
+
+        System.setProperty("org.apache.commons.logging.Log","org.apache.commons.logging.impl.Jdk14Logger");
+    }
+}
diff --git a/jetty-cdi/cdi-core/src/test/resources/logging.properties b/jetty-cdi/cdi-core/src/test/resources/logging.properties
new file mode 100644
index 0000000..c0ff63e
--- /dev/null
+++ b/jetty-cdi/cdi-core/src/test/resources/logging.properties
@@ -0,0 +1,2 @@
+handlers = org.eclipse.jetty.cdi.core.logging.LeanConsoleHandler
+.level=INFO
diff --git a/jetty-cdi/cdi-full-servlet/pom.xml b/jetty-cdi/cdi-full-servlet/pom.xml
new file mode 100644
index 0000000..4b7292e
--- /dev/null
+++ b/jetty-cdi/cdi-full-servlet/pom.xml
@@ -0,0 +1,53 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.cdi</groupId>
+    <artifactId>jetty-cdi-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cdi-full-servlet</artifactId>
+  <name>Jetty :: CDI :: Dependencies</name>
+  <url>http://www.eclipse.org/jetty</url>
+  <packaging>pom</packaging>
+  <properties>
+    <weld.version>2.2.9.Final</weld.version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty.cdi</groupId>
+      <artifactId>cdi-servlet</artifactId>
+      <version>${project.version}</version>
+<!--
+      <exclusions>
+        <exclusion>
+          <groupId>javax.el</groupId>
+          <artifactId>javax.el-api</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.jboss.spec.javax.el</groupId>
+          <artifactId>jboss-el-api_3.0_spec</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.jboss.spec.javax.annotation</groupId>
+          <artifactId>jboss-annotations-api_1.2_spec</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.jboss.spec.javax.interceptor</groupId>
+          <artifactId>jboss-interceptors-api_1.2_spec</artifactId>
+        </exclusion>
+      </exclusions>
+-->
+    </dependency>
+    <dependency>
+       <groupId>javax.annotation</groupId>
+       <artifactId>javax.annotation-api</artifactId>
+       <version>1.2</version>
+    </dependency>
+    <dependency>
+       <groupId>org.mortbay.jasper</groupId>
+       <artifactId>apache-jsp</artifactId>
+       <version>${jsp.version}</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/jetty-cdi/cdi-servlet/pom.xml b/jetty-cdi/cdi-servlet/pom.xml
new file mode 100644
index 0000000..1d164f2
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/pom.xml
@@ -0,0 +1,90 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.cdi</groupId>
+    <artifactId>jetty-cdi-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cdi-servlet</artifactId>
+  <name>Jetty :: CDI :: Servlet</name>
+  <url>http://www.eclipse.org/jetty</url>
+  <packaging>jar</packaging>
+  <properties>
+    <weld.version>2.2.9.Final</weld.version>
+    <bundle-symbolic-name>${project.groupId}.servlet</bundle-symbolic-name>
+  </properties>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+            <configuration>
+              <descriptorRefs>
+                <descriptorRef>config</descriptorRef>
+              </descriptorRefs>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty.cdi</groupId>
+      <artifactId>cdi-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-plus</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-deploy</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.weld.servlet</groupId>
+      <artifactId>weld-servlet-core</artifactId>
+      <version>${weld.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>javax.el</groupId>
+          <artifactId>javax.el-api</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.jboss.spec.javax.el</groupId>
+          <artifactId>jboss-el-api_3.0_spec</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.jboss.spec.javax.annotation</groupId>
+          <artifactId>jboss-annotations-api_1.2_spec</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.jboss.spec.javax.interceptor</groupId>
+          <artifactId>jboss-interceptors-api_1.2_spec</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <!-- below here lie testing dragons -->
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>apache-jsp</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/jetty-cdi/cdi-servlet/src/main/config/etc/jetty-cdi.xml b/jetty-cdi/cdi-servlet/src/main/config/etc/jetty-cdi.xml
new file mode 100644
index 0000000..79ebdae
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/main/config/etc/jetty-cdi.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<!-- =============================================================== -->
+<!-- Mixin the Weld / CDI classes to the class loader                -->
+<!-- =============================================================== -->
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+  <Ref refid="DeploymentManager">
+    <Call name="addLifeCycleBinding">
+      <Arg>
+        <New
+          class="org.eclipse.jetty.cdi.servlet.WeldDeploymentBinding">
+        </New>
+      </Arg>
+    </Call>
+  </Ref>
+</Configure>
+
diff --git a/jetty-cdi/cdi-servlet/src/main/config/modules/cdi.mod b/jetty-cdi/cdi-servlet/src/main/config/modules/cdi.mod
new file mode 100644
index 0000000..ebffb55
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/main/config/modules/cdi.mod
@@ -0,0 +1,38 @@
+#
+# [EXPERIMENTAL] CDI / Weld Jetty module
+#
+
+[depend]
+deploy
+annotations
+plus
+# JSP (and EL) are requirements for CDI and Weld
+jsp
+
+[files]
+lib/cdi/
+maven://javax.enterprise/cdi-api/1.2|lib/cdi/javax.enterprise.cdi-api-1.2.jar
+maven://javax.interceptor/javax.interceptor-api/1.2|lib/cdi/javax.interceptor-api-1.2.jar
+maven://javax.inject/javax.inject/1|lib/cdi/javax.inject-1.0.jar
+maven://org.jboss.weld.servlet/weld-servlet-core/2.2.9.Final|lib/cdi/weld-servlet-core-2.2.9.Final.jar
+maven://org.jboss.weld.environment/weld-environment-common/2.2.9.Final|lib/cdi/weld-environment-common-2.2.9.Final.jar
+maven://org.jboss.weld/weld-core-impl/2.2.9.Final|lib/cdi/weld-core-impl-2.2.9.Final.jar
+maven://org.jboss.classfilewriter/jboss-classfilewriter/1.0.5.Final|lib/cdi/jboss-classfilewriter-1.0.5.Final.jar
+maven://com.google.guava/guava/13.0.1|lib/cdi/com.google.guava.guava-13.0.1.jar
+maven://org.jboss.weld/weld-spi/2.2.SP3|lib/cdi/weld-spi-2.2.SP3.jar
+maven://org.jboss.weld/weld-api/2.2.SP3|lib/cdi/weld-api-2.2.SP3.jar
+maven://org.jboss.logging/jboss-logging/3.1.3.GA|lib/cdi/jboss-logging-3.1.3.GA.jar
+
+
+[lib]
+lib/cdi/*.jar
+lib/cdi-core-${jetty.version}.jar
+lib/cdi-servlet-${jetty.version}.jar
+
+[xml]
+etc/jetty-cdi.xml
+
+[license]
+Weld is an open source project hosted on Github and released under the Apache 2.0 license.
+http://weld.cdi-spec.org/
+http://www.apache.org/licenses/LICENSE-2.0.html
diff --git a/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/EmbeddedCdiHandler.java b/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/EmbeddedCdiHandler.java
new file mode 100644
index 0000000..612eb58
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/EmbeddedCdiHandler.java
@@ -0,0 +1,131 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.servlet;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+import org.jboss.weld.environment.servlet.EnhancedListener;
+
+/**
+ * Handy {@link ServletContextHandler} implementation that hooks up
+ * all of the various CDI related components and listeners from Weld.
+ */
+public class EmbeddedCdiHandler extends ServletContextHandler
+{
+    private static final Logger LOG = Log.getLogger(EmbeddedCdiHandler.class);
+    
+    private static final String[] REQUIRED_BEANS_XML_PATHS = new String[] { 
+        "/WEB-INF/beans.xml", 
+        "/META-INF/beans.xml", 
+        "/WEB-INF/classes/META-INF/beans.xml" 
+    };
+
+    public EmbeddedCdiHandler()
+    {
+        super();
+    }
+
+    public EmbeddedCdiHandler(int options)
+    {
+        super(options);
+    }
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        // Required of CDI
+        Resource baseResource = getBaseResource();
+        if (baseResource == null)
+        {
+            throw new NullPointerException("baseResource must be set (to so it can find the beans.xml)");
+        }
+        
+        boolean foundBeansXml = false;
+
+        // Verify that beans.xml is present, otherwise weld will fail silently.
+        for(String beansXmlPath: REQUIRED_BEANS_XML_PATHS) {
+            Resource res = baseResource.addPath(beansXmlPath);
+            if (res == null)
+            {
+                // not found, skip it
+                continue;
+            }
+            
+            if (res.exists())
+            {
+                foundBeansXml = true;
+            }
+
+            if (res.isDirectory())
+            {
+                throw new IOException("Directory conflicts with expected file: " + res.getURI().toASCIIString());
+            }
+        }
+        
+        if (!foundBeansXml)
+        {
+            StringBuilder err = new StringBuilder();
+            err.append("Unable to find required beans.xml from the baseResource: ");
+            err.append(baseResource.getURI().toASCIIString()).append(System.lineSeparator());
+            err.append("Searched for: ");
+            for (String beansXmlPath : REQUIRED_BEANS_XML_PATHS)
+            {
+                err.append(System.lineSeparator());
+                err.append("  ").append(beansXmlPath);
+            }
+            LOG.warn("ERROR: {}",err.toString());
+            throw new IOException(err.toString());
+        }
+
+        // Initialize Weld
+        JettyWeldInitializer.initContext(this);
+
+        // Wire up Weld (what's usually done via the ServletContainerInitializer)
+        ServletContext ctx = getServletContext();
+        
+        // Fake the call to ServletContainerInitializer
+        ClassLoader orig = Thread.currentThread().getContextClassLoader();
+        try
+        {
+            Thread.currentThread().setContextClassLoader(ctx.getClassLoader());
+            
+            EnhancedListener weldListener = new EnhancedListener();
+            Set<Class<?>> classes = Collections.emptySet();
+            weldListener.onStartup(classes,ctx);
+            
+            // add the rest of the Weld Listeners
+            ctx.addListener(weldListener);
+        }
+        finally
+        {
+            Thread.currentThread().setContextClassLoader(orig);
+        }
+
+        // Let normal ServletContextHandler startup continue its merry way
+        super.doStart();
+    }
+}
diff --git a/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/JettyWeldInitializer.java b/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/JettyWeldInitializer.java
new file mode 100644
index 0000000..76100e1
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/JettyWeldInitializer.java
@@ -0,0 +1,77 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.servlet;
+
+import javax.naming.NamingException;
+import javax.naming.Reference;
+
+import org.eclipse.jetty.plus.jndi.Resource;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * Utility class suitable for initializing CDI/Weld on Embedded Jetty
+ */
+public class JettyWeldInitializer
+{
+    /**
+     * Initialize WebAppContext to support CDI/Weld.
+     * <p>
+     * Initializes Context, then sets up WebAppContext system and server classes to allow Weld to operate from Server
+     * level.
+     * <p>
+     * Includes {@link #initContext(ContextHandler)} behavior as well.
+     * @param webapp the webapp
+     * @throws NamingException if unable to bind BeanManager context
+     */
+    public static void initWebApp(WebAppContext webapp) throws NamingException
+    {
+        initContext(webapp);
+
+        // webapp cannot change / replace weld classes
+        webapp.addSystemClass("org.jboss.weld.");
+        webapp.addSystemClass("org.jboss.classfilewriter.");
+        webapp.addSystemClass("org.jboss.logging.");
+        webapp.addSystemClass("com.google.common.");
+        webapp.addSystemClass("org.eclipse.jetty.cdi.websocket.annotation.");
+        
+
+        // don't hide weld classes from webapps (allow webapp to use ones from system classloader)
+        webapp.prependServerClass("-org.eclipse.jetty.cdi.websocket.annotation.");
+        webapp.prependServerClass("-org.eclipse.jetty.cdi.core.");
+        webapp.prependServerClass("-org.eclipse.jetty.cdi.servlet.");
+        webapp.addServerClass("-org.jboss.weld.");
+        webapp.addServerClass("-org.jboss.classfilewriter.");
+        webapp.addServerClass("-org.jboss.logging.");
+        webapp.addServerClass("-com.google.common.");
+    
+    }
+
+    public static void initContext(ContextHandler handler) throws NamingException
+    {
+        // Add context specific weld container reference.
+        // See https://issues.jboss.org/browse/WELD-1710
+        // and https://github.com/weld/core/blob/2.2.5.Final/environments/servlet/core/src/main/java/org/jboss/weld/environment/servlet/WeldServletLifecycle.java#L244-L253
+        handler.setInitParameter("org.jboss.weld.environment.container.class","org.jboss.weld.environment.jetty.JettyContainer");
+
+        // Setup Weld BeanManager reference
+        Reference ref = new Reference("javax.enterprise.inject.spi.BeanManager","org.jboss.weld.resources.ManagerObjectFactory",null);
+        new Resource(handler,"BeanManager",ref);
+    }
+}
diff --git a/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/WeldDeploymentBinding.java b/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/WeldDeploymentBinding.java
new file mode 100644
index 0000000..b5fb912
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/WeldDeploymentBinding.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.servlet;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.AppLifeCycle;
+import org.eclipse.jetty.deploy.graph.Node;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * Perform some basic weld configuration of WebAppContext
+ */
+public class WeldDeploymentBinding implements AppLifeCycle.Binding
+{
+    public String[] getBindingTargets()
+    {
+        return new String[] { "deploying" };
+    }
+
+    public void processBinding(Node node, App app) throws Exception
+    {
+        ContextHandler handler = app.getContextHandler();
+        if (handler == null)
+        {
+            throw new NullPointerException("No Handler created for App: " + app);
+        }
+
+        if (handler instanceof WebAppContext)
+        {
+            // Do webapp specific init
+            WebAppContext webapp = (WebAppContext)handler;
+            JettyWeldInitializer.initWebApp(webapp);
+        }
+        else
+        {
+            // Do general init
+            JettyWeldInitializer.initContext(handler);
+        }
+    }
+}
diff --git a/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/Dumper.java b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/Dumper.java
new file mode 100644
index 0000000..1051861
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/Dumper.java
@@ -0,0 +1,27 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public interface Dumper
+{
+    public void dump(PrintWriter out) throws IOException;
+}
diff --git a/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/IsoTimeFormatter.java b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/IsoTimeFormatter.java
new file mode 100644
index 0000000..ccc83dd
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/IsoTimeFormatter.java
@@ -0,0 +1,39 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.servlet;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import javax.inject.Named;
+
+@Named("iso")
+public class IsoTimeFormatter implements TimeFormatter
+{
+    @Override
+    public String format(Calendar cal)
+    {
+        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'",Locale.US);
+        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+        return dateFormat.format(cal.getTime());
+    }
+}
diff --git a/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/LocaleTimeFormatter.java b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/LocaleTimeFormatter.java
new file mode 100644
index 0000000..ef04d5e
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/LocaleTimeFormatter.java
@@ -0,0 +1,38 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.servlet;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+import javax.enterprise.inject.Default;
+import javax.inject.Named;
+
+@Named("locale")
+@Default
+public class LocaleTimeFormatter implements TimeFormatter
+{
+    public static final TimeFormatter INSTANCE = new LocaleTimeFormatter();
+
+    @Override
+    public String format(Calendar cal)
+    {
+        return new SimpleDateFormat().format(cal.getTime());
+    }
+}
diff --git a/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/RequestInfoServlet.java b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/RequestInfoServlet.java
new file mode 100644
index 0000000..d9f93d6
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/RequestInfoServlet.java
@@ -0,0 +1,69 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.enterprise.inject.Any;
+import javax.enterprise.inject.Instance;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.cdi.core.NamedLiteral;
+
+@SuppressWarnings("serial")
+@WebServlet("/req-info")
+public class RequestInfoServlet extends HttpServlet
+{
+    @Inject
+    @Any
+    private Instance<Dumper> dumpers;
+    
+    @Inject
+    @Named("params")
+    private Dumper defaultDumper;
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+    {
+        resp.setContentType("text/plain");
+        PrintWriter out = resp.getWriter();
+        
+        Dumper dumper = defaultDumper;
+        
+        String dumperId = req.getParameter("dumperId");
+        
+        if (dumperId != null)
+        {
+            Instance<Dumper> inst = dumpers.select(new NamedLiteral(dumperId));
+            if (!inst.isAmbiguous() && !inst.isUnsatisfied())
+            {
+                dumper = inst.get();
+            }
+        }
+        
+        dumper.dump(out);
+    }
+}
diff --git a/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/RequestParamsDumper.java b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/RequestParamsDumper.java
new file mode 100644
index 0000000..52c7ec9
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/RequestParamsDumper.java
@@ -0,0 +1,71 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.servlet.http.HttpServletRequest;
+
+@Named("params")
+@RequestScoped
+public class RequestParamsDumper implements Dumper
+{
+    @Inject
+    private HttpServletRequest request;
+
+    @Override
+    public void dump(PrintWriter out) throws IOException
+    {
+        out.printf("request is %s%n",request == null ? "NULL" : "PRESENT");
+
+        if (request != null)
+        {
+            Map<String, String[]> params = request.getParameterMap();
+            List<String> paramNames = new ArrayList<>();
+            paramNames.addAll(params.keySet());
+            Collections.sort(paramNames);
+
+            out.printf("parameters.size = [%d]%n",params.size());
+
+            for (String name : paramNames)
+            {
+                out.printf(" param[%s] = [",name);
+                boolean delim = false;
+                for (String val : params.get(name))
+                {
+                    if (delim)
+                    {
+                        out.print(", ");
+                    }
+                    out.print(val);
+                    delim = true;
+                }
+                out.println("]");
+            }
+        }
+    }
+}
diff --git a/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/TimeFormatter.java b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/TimeFormatter.java
new file mode 100644
index 0000000..400343b
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/TimeFormatter.java
@@ -0,0 +1,26 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.servlet;
+
+import java.util.Calendar;
+
+public interface TimeFormatter
+{
+    public String format(Calendar cal);
+}
diff --git a/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/TimeServlet.java b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/TimeServlet.java
new file mode 100644
index 0000000..461eb17
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/TimeServlet.java
@@ -0,0 +1,59 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.servlet;
+
+import java.io.IOException;
+import java.util.Calendar;
+
+import javax.enterprise.inject.Any;
+import javax.enterprise.inject.Instance;
+import javax.inject.Inject;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.cdi.core.NamedLiteral;
+
+@SuppressWarnings("serial")
+@WebServlet(urlPatterns = "/time")
+public class TimeServlet extends HttpServlet
+{
+    @Inject
+    @Any
+    Instance<TimeFormatter> formatters;
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+    {
+        resp.setContentType("text/plain");
+
+        String timeType = req.getParameter("type");
+        TimeFormatter time = LocaleTimeFormatter.INSTANCE;
+
+        Instance<TimeFormatter> inst = formatters.select(new NamedLiteral(timeType));
+        if (!inst.isAmbiguous() && !inst.isUnsatisfied())
+        {
+            time = inst.get();
+        }
+
+        resp.getWriter().println(time.format(Calendar.getInstance()));
+    }
+}
diff --git a/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/WeldInitializationTest.java b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/WeldInitializationTest.java
new file mode 100644
index 0000000..1eff479
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/test/java/org/eclipse/jetty/cdi/servlet/WeldInitializationTest.java
@@ -0,0 +1,120 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.servlet;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.JettyLogHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class WeldInitializationTest
+{
+    private static final Logger LOG = Log.getLogger(WeldInitializationTest.class);
+    private static Server server;
+    private static URI serverHttpURI;
+
+    @BeforeClass
+    public static void startServer() throws Exception
+    {
+        JettyLogHandler.config();
+
+        server = new Server();
+        ServerConnector connector = new ServerConnector(server);
+        connector.setPort(0);
+        server.addConnector(connector);
+
+        EmbeddedCdiHandler context = new EmbeddedCdiHandler();
+
+        File baseDir = MavenTestingUtils.getTestResourcesDir();
+
+        context.setBaseResource(Resource.newResource(baseDir));
+        context.setContextPath("/");
+        server.setHandler(context);
+
+        // Add some servlets
+        context.addServlet(TimeServlet.class,"/time");
+        context.addServlet(RequestInfoServlet.class,"/req-info");
+
+        server.start();
+
+        String host = connector.getHost();
+        if (host == null)
+        {
+            host = "localhost";
+        }
+        int port = connector.getLocalPort();
+        serverHttpURI = new URI(String.format("http://%s:%d/",host,port));
+    }
+
+    @AfterClass
+    public static void stopServer()
+    {
+        try
+        {
+            server.stop();
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+        }
+    }
+
+    @Test
+    public void testRequestParamServletDefault() throws Exception
+    {
+        HttpURLConnection http = (HttpURLConnection) serverHttpURI.resolve("req-info").toURL().openConnection();
+        assertThat("response code", http.getResponseCode(), is(200));
+        try(InputStream inputStream = http.getInputStream())
+        {
+            String resp = IO.toString(inputStream);
+            assertThat("Response", resp, containsString("request is PRESENT"));
+            assertThat("Response", resp, containsString("parameters.size = [0]"));
+        }
+    }
+
+    @Test
+    public void testRequestParamServletAbc() throws Exception
+    {
+        HttpURLConnection http = (HttpURLConnection) serverHttpURI.resolve("req-info?abc=123").toURL().openConnection();
+        assertThat("response code", http.getResponseCode(), is(200));
+        try(InputStream inputStream = http.getInputStream())
+        {
+            String resp = IO.toString(inputStream);
+            assertThat("Response", resp, containsString("request is PRESENT"));
+            assertThat("Response", resp, containsString("parameters.size = [1]"));
+            assertThat("Response", resp, containsString(" param[abc] = [123]"));
+        }
+    }
+}
diff --git a/jetty-cdi/cdi-servlet/src/test/resources/META-INF/beans.xml b/jetty-cdi/cdi-servlet/src/test/resources/META-INF/beans.xml
new file mode 100644
index 0000000..f158a71
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/test/resources/META-INF/beans.xml
@@ -0,0 +1,6 @@
+<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
+        http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
+       bean-discovery-mode="all">
+</beans>
\ No newline at end of file
diff --git a/jetty-cdi/cdi-servlet/src/test/resources/jetty-logging.properties b/jetty-cdi/cdi-servlet/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..aaf03af
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/test/resources/jetty-logging.properties
@@ -0,0 +1,11 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+# org.jboss.LEVEL=DEBUG
+org.eclipse.jetty.LEVEL=INFO
+
+# org.eclipse.jetty.util.DecoratedObjectFactory.LEVEL=DEBUG
+# org.eclipse.jetty.cdi.LEVEL=DEBUG
+
+# org.eclipse.jetty.LEVEL=DEBUG
+# org.eclipse.jetty.websocket.LEVEL=DEBUG
+# org.eclipse.jetty.websocket.client.LEVEL=DEBUG
+
diff --git a/jetty-cdi/cdi-servlet/src/test/resources/logging.properties b/jetty-cdi/cdi-servlet/src/test/resources/logging.properties
new file mode 100644
index 0000000..cfec8c7
--- /dev/null
+++ b/jetty-cdi/cdi-servlet/src/test/resources/logging.properties
@@ -0,0 +1,2 @@
+handlers = org.eclipse.jetty.util.log.JettyLogHandler
+.level=FINE
diff --git a/jetty-cdi/cdi-websocket/pom.xml b/jetty-cdi/cdi-websocket/pom.xml
new file mode 100644
index 0000000..8db867b
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/pom.xml
@@ -0,0 +1,71 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.cdi</groupId>
+    <artifactId>jetty-cdi-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cdi-websocket</artifactId>
+  <name>Jetty :: CDI :: WebSocket</name>
+  <url>http://www.eclipse.org/jetty</url>
+  <packaging>jar</packaging>
+  <properties>
+    <weld.version>2.2.9.Final</weld.version>
+    <bundle-symbolic-name>${project.groupId}.websocket</bundle-symbolic-name>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty.cdi</groupId>
+      <artifactId>cdi-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>websocket-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <!-- below here lie testing dragons -->
+    <dependency>
+       <groupId>org.eclipse.jetty</groupId>
+       <artifactId>apache-jsp</artifactId>
+       <version>${project.version}</version>
+       <scope>test</scope>
+     </dependency>
+    <dependency>
+      <groupId>org.jboss.weld</groupId>
+      <artifactId>weld-core</artifactId>
+      <version>${weld.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.weld.se</groupId>
+      <artifactId>weld-se-core</artifactId>
+      <version>${weld.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.cdi</groupId>
+      <artifactId>cdi-core</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.cdi</groupId>
+      <artifactId>cdi-servlet</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>javax-websocket-server-impl</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/AbstractContainerListener.java b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/AbstractContainerListener.java
new file mode 100644
index 0000000..ff83850
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/AbstractContainerListener.java
@@ -0,0 +1,73 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket;
+
+import org.eclipse.jetty.util.component.Container;
+import org.eclipse.jetty.util.component.LifeCycle;
+
+/**
+ * Abstract implementation of listener that needs both Container events and LifeCycle events
+ */
+public abstract class AbstractContainerListener implements LifeCycle.Listener, Container.InheritedListener
+{
+    @Override
+    public void beanAdded(Container parent, Object child)
+    {
+        if (child instanceof LifeCycle)
+        {
+            ((LifeCycle)child).addLifeCycleListener(this);
+        }
+    }
+
+    @Override
+    public void beanRemoved(Container parent, Object child)
+    {
+        if (child instanceof LifeCycle)
+        {
+            ((LifeCycle)child).removeLifeCycleListener(this);
+        }
+    }
+
+    @Override
+    public void lifeCycleFailure(LifeCycle event, Throwable cause)
+    {
+    }
+
+    @Override
+    public void lifeCycleStarted(LifeCycle event)
+    {
+    }
+
+    @Override
+    public void lifeCycleStarting(LifeCycle event)
+    {
+    }
+
+    @Override
+    public void lifeCycleStopped(LifeCycle event)
+    {
+
+    }
+
+    @Override
+    public void lifeCycleStopping(LifeCycle event)
+    {
+    }
+
+}
diff --git a/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/JavaWebSocketSessionProducer.java b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/JavaWebSocketSessionProducer.java
new file mode 100644
index 0000000..d0f75b1
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/JavaWebSocketSessionProducer.java
@@ -0,0 +1,56 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket;
+
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.InjectionPoint;
+import javax.websocket.Session;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Producer of {@link javax.websocket.Session} instances
+ */
+public class JavaWebSocketSessionProducer
+{
+    private static final Logger LOG = Log.getLogger(JavaWebSocketSessionProducer.class);
+
+    @Produces
+    public Session getSession(InjectionPoint injectionPoint)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("getSession({})",injectionPoint);
+        }
+        org.eclipse.jetty.websocket.api.Session sess = WebSocketScopeContext.current().getSession();
+        if (sess == null)
+        {
+            throw new IllegalStateException("No Session Available");
+        }
+
+        if (sess instanceof javax.websocket.Session)
+        {
+            return (Session)sess;
+        }
+
+        throw new IllegalStateException("Incompatible Session, expected <" + javax.websocket.Session.class.getName() + ">, but got <"
+                + sess.getClass().getName() + "> instead");
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/JettyWebSocketSessionProducer.java b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/JettyWebSocketSessionProducer.java
new file mode 100644
index 0000000..7e4cb6e
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/JettyWebSocketSessionProducer.java
@@ -0,0 +1,55 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket;
+
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.InjectionPoint;
+
+import org.eclipse.jetty.cdi.websocket.annotation.WebSocketScope;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.Session;
+
+/**
+ * Producer of {@link org.eclipse.jetty.websocket.api.Session} instances
+ */
+public class JettyWebSocketSessionProducer
+{
+    private static final Logger LOG = Log.getLogger(JettyWebSocketSessionProducer.class);
+
+    @Produces
+    public Session getSession(InjectionPoint injectionPoint)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("getSession({})",injectionPoint);
+        }
+        WebSocketScopeContext ctx = WebSocketScopeContext.current();
+        if (ctx == null)
+        {
+            throw new IllegalStateException("Not in a " + WebSocketScope.class.getName());
+        }
+        org.eclipse.jetty.websocket.api.Session sess = ctx.getSession();
+        if (sess == null)
+        {
+            throw new IllegalStateException("No Session Available");
+        }
+        return sess;
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiInitializer.java b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiInitializer.java
new file mode 100644
index 0000000..2a40d58
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiInitializer.java
@@ -0,0 +1,70 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket;
+
+import java.util.Set;
+
+import javax.servlet.ServletContainerInitializer;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.thread.ThreadClassLoaderScope;
+
+public class WebSocketCdiInitializer implements ServletContainerInitializer
+{
+    public static void configureContext(ServletContextHandler context) throws ServletException
+    {
+        try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(context.getClassLoader()))
+        {
+            addListeners(context);
+        }
+    }
+
+    @Override
+    public void onStartup(Set<Class<?>> c, ServletContext context) throws ServletException
+    {
+        ContextHandler handler = ContextHandler.getContextHandler(context);
+
+        if (handler == null)
+        {
+            throw new ServletException("Not running on Jetty, WebSocket+CDI support unavailable");
+        }
+
+        if (!(handler instanceof ServletContextHandler))
+        {
+            throw new ServletException("Not running in Jetty ServletContextHandler, WebSocket+CDI support unavailable");
+        }
+
+        ServletContextHandler jettyContext = (ServletContextHandler)handler;
+        try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(context.getClassLoader()))
+        {
+            addListeners(jettyContext);
+        }
+    }
+    
+    private static void addListeners(ContainerLifeCycle container)
+    {
+        WebSocketCdiListener listener = new WebSocketCdiListener();
+        container.addLifeCycleListener(listener);
+        container.addEventListener(listener);
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiListener.java b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiListener.java
new file mode 100644
index 0000000..a50d969
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiListener.java
@@ -0,0 +1,137 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket;
+
+import java.util.Set;
+
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.CDI;
+
+import org.eclipse.jetty.cdi.core.AnyLiteral;
+import org.eclipse.jetty.cdi.core.ScopedInstance;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketSessionScope;
+
+public class WebSocketCdiListener extends AbstractContainerListener
+{
+    static final Logger LOG = Log.getLogger(WebSocketCdiListener.class);
+
+    @SuppressWarnings(
+    { "rawtypes", "unchecked" })
+    public static <T> ScopedInstance<T> newInstance(Class<T> clazz)
+    {
+        BeanManager bm = CDI.current().getBeanManager();
+
+        ScopedInstance sbean = new ScopedInstance();
+        Set<Bean<?>> beans = bm.getBeans(clazz,AnyLiteral.INSTANCE);
+        if (beans.size() > 0)
+        {
+            sbean.bean = beans.iterator().next();
+            sbean.creationalContext = bm.createCreationalContext(sbean.bean);
+            sbean.instance = bm.getReference(sbean.bean,clazz,sbean.creationalContext);
+            return sbean;
+        }
+        else
+        {
+            throw new RuntimeException(String.format("Can't find class %s",clazz));
+        }
+    }
+
+    public static class ContainerListener extends AbstractContainerListener
+    {
+        private static final Logger LOG = Log.getLogger(WebSocketCdiListener.ContainerListener.class);
+        private final WebSocketContainerScope container;
+        private final ScopedInstance<WebSocketScopeContext> wsScope;
+
+        public ContainerListener(WebSocketContainerScope container)
+        {
+            this.container = container;
+            this.wsScope = newInstance(WebSocketScopeContext.class);
+            this.wsScope.instance.create();
+        }
+
+        @Override
+        public void lifeCycleStarted(LifeCycle event)
+        {
+            if (event == container)
+            {
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("starting websocket container [{}]",event);
+                }
+                wsScope.instance.begin();
+                return;
+            }
+            
+            if (event instanceof WebSocketSessionScope)
+            {
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("starting websocket session [{}]",event);
+                }
+                wsScope.instance.setSession((Session)event);
+                return;
+            }
+        }
+
+        @Override
+        public void lifeCycleStopped(LifeCycle event)
+        {
+            if (event == container)
+            {
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("stopped websocket container [{}]",event);
+                }
+                this.wsScope.instance.end();
+                this.wsScope.instance.destroy();
+                this.wsScope.destroy();
+            }
+        }
+    }
+    
+    @Override
+    public void lifeCycleStarting(LifeCycle event)
+    {
+        if (event instanceof WebSocketContainerScope)
+        {
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("started websocket container [{}]",event);
+            }
+            ContainerListener listener = new ContainerListener((WebSocketContainerScope)event);
+            if (event instanceof ContainerLifeCycle)
+            {
+                ContainerLifeCycle container = (ContainerLifeCycle)event;
+                container.addLifeCycleListener(listener);
+                container.addEventListener(listener);
+            }
+            else
+            {
+                throw new RuntimeException("Unable to setup CDI against non-container: " + event.getClass().getName());
+            }
+        }
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketScopeContext.java b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketScopeContext.java
new file mode 100644
index 0000000..dcee925
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketScopeContext.java
@@ -0,0 +1,230 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Set;
+
+import javax.enterprise.context.spi.Context;
+import javax.enterprise.context.spi.Contextual;
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+
+import org.eclipse.jetty.cdi.core.AnyLiteral;
+import org.eclipse.jetty.cdi.core.ScopedInstance;
+import org.eclipse.jetty.cdi.core.SimpleBeanStore;
+import org.eclipse.jetty.cdi.websocket.annotation.WebSocketScope;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.Session;
+
+/**
+ * WebSocket Scope Context.
+ * <p>
+ * A CDI Context definition for how CDI will use objects defined to belong to the WebSocketScope
+ */
+public class WebSocketScopeContext implements Context
+{
+    private static final Logger LOG = Log.getLogger(WebSocketScopeContext.class);
+
+    private static ThreadLocal<WebSocketScopeContext> current = new ThreadLocal<>();
+
+    public static WebSocketScopeContext current()
+    {
+        return current.get();
+    }
+
+    private SimpleBeanStore beanStore;
+
+    @Inject
+    private BeanManager beanManager;
+
+    private ThreadLocal<org.eclipse.jetty.websocket.api.Session> session = new ThreadLocal<>();
+
+    public void begin()
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("{} begin()",this);
+        }
+        current.set(this);
+    }
+
+    public void create()
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("{} create()",this);
+        }
+        current.set(this);
+        beanStore = new SimpleBeanStore();
+    }
+
+    public void destroy()
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("{} destroy()",this);
+        }
+
+        beanStore.destroy();
+    }
+
+    public void end()
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("{} end()",this);
+        }
+        beanStore.clear();
+    }
+
+    @SuppressWarnings({ "unchecked" })
+    @Override
+    public <T> T get(Contextual<T> contextual)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("{} get({})",this,contextual);
+        }
+
+        Bean<T> bean = (Bean<T>)contextual;
+
+        if (bean.getBeanClass().isAssignableFrom(Session.class))
+        {
+            return (T)this.session;
+        }
+        
+        if (beanStore == null)
+        {
+            return null;
+        }
+
+        List<ScopedInstance<?>> beans = beanStore.getBeans(contextual);
+
+        if ((beans != null) && (!beans.isEmpty()))
+        {
+            return (T)beans.get(0).instance;
+        }
+
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("{} get({},{})",this,contextual,creationalContext);
+        }
+
+        Bean<T> bean = (Bean<T>)contextual;
+
+        if (bean.getBeanClass().isAssignableFrom(Session.class))
+        {
+            return (T)this.session;
+        }
+
+        if (beanStore == null)
+        {
+            beanStore = new SimpleBeanStore();
+        }
+
+        List<ScopedInstance<?>> beans = beanStore.getBeans(contextual);
+
+        if ((beans != null) && (!beans.isEmpty()))
+        {
+            for (ScopedInstance<?> instance : beans)
+            {
+                if (instance.bean.equals(bean))
+                {
+                    return (T)instance.instance;
+                }
+            }
+        }
+
+        // no bean found, create it
+        T t = bean.create(creationalContext);
+        ScopedInstance<T> customInstance = new ScopedInstance<>();
+        customInstance.bean = bean;
+        customInstance.creationalContext = creationalContext;
+        customInstance.instance = t;
+        beanStore.addBean(customInstance);
+        return t;
+    }
+
+    @Override
+    public Class<? extends Annotation> getScope()
+    {
+        return WebSocketScope.class;
+    }
+
+    @Override
+    public boolean isActive()
+    {
+        return true;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <T> T newInstance(Class<T> clazz)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("newInstance({})",clazz);
+        }
+        Set<Bean<?>> beans = beanManager.getBeans(clazz,AnyLiteral.INSTANCE);
+        if (beans.isEmpty())
+        {
+            return null;
+        }
+
+        Bean bean = beans.iterator().next();
+        CreationalContext cc = beanManager.createCreationalContext(bean);
+        return (T)beanManager.getReference(bean,clazz,cc);
+    }
+
+    public void setSession(org.eclipse.jetty.websocket.api.Session sess)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("{} setSession({})",this,sess);
+        }
+        current.set(this);
+        this.session.set(sess);
+    }
+
+    public org.eclipse.jetty.websocket.api.Session getSession()
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("{} getSession()",this);
+        }
+        return this.session.get();
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%X[%s]",this.getClass().getSimpleName(),hashCode(),beanStore);
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketScopeExtension.java b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketScopeExtension.java
new file mode 100644
index 0000000..dd075af
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketScopeExtension.java
@@ -0,0 +1,74 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket;
+
+import javax.enterprise.context.Destroyed;
+import javax.enterprise.context.Initialized;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.AfterBeanDiscovery;
+import javax.enterprise.inject.spi.BeforeBeanDiscovery;
+import javax.enterprise.inject.spi.Extension;
+
+import org.eclipse.jetty.cdi.websocket.annotation.WebSocketScope;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Register the various WebSocket specific components for CDI
+ */
+public class WebSocketScopeExtension implements Extension
+{
+    private static final Logger LOG = Log.getLogger(WebSocketScopeExtension.class);
+
+    public void addScope(@Observes final BeforeBeanDiscovery event)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("addScope()");
+        }
+        // Add our scope
+        event.addScope(WebSocketScope.class,true,false);
+    }
+
+    public void registerContext(@Observes final AfterBeanDiscovery event)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("registerContext()");
+        }
+        // Register our context
+        event.addContext(new WebSocketScopeContext());
+    }
+
+    public void logWsScopeInit(@Observes @Initialized(WebSocketScope.class) org.eclipse.jetty.websocket.api.Session sess)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("Initialized @WebSocketScope - {}",sess);
+        }
+    }
+
+    public void logWsScopeDestroyed(@Observes @Destroyed(WebSocketScope.class) org.eclipse.jetty.websocket.api.Session sess)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("Destroyed @WebSocketScope - {}",sess);
+        }
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/annotation/WebSocketScope.java b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/annotation/WebSocketScope.java
new file mode 100644
index 0000000..aa9b602
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/annotation/WebSocketScope.java
@@ -0,0 +1,45 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.inject.Scope;
+
+/**
+ * A CDI Context Scope for a WebSocket Session / Endpoint
+ * <p>
+ * <em>CAUTION: This is a highly speculative scope defined by Jetty.  One that will likely be replaced by a formal spec later</em>
+ * <p>
+ * At the time of implementation (of this scope), no standard scope exists for websocket session lifecycle.
+ */
+@Scope
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
+@Inherited
+@Documented
+public @interface WebSocketScope
+{
+
+}
diff --git a/jetty-cdi/cdi-websocket/src/main/resources/META-INF/beans.xml b/jetty-cdi/cdi-websocket/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..aeeef53
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,4 @@
+<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
+
+</beans>
\ No newline at end of file
diff --git a/jetty-cdi/cdi-websocket/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/jetty-cdi/cdi-websocket/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
new file mode 100644
index 0000000..93723bc
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
@@ -0,0 +1 @@
+org.eclipse.jetty.cdi.websocket.WebSocketScopeExtension
\ No newline at end of file
diff --git a/jetty-cdi/cdi-websocket/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer b/jetty-cdi/cdi-websocket/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
new file mode 100644
index 0000000..f527270
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
@@ -0,0 +1 @@
+org.eclipse.jetty.cdi.websocket.WebSocketCdiInitializer
\ No newline at end of file
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/CheckSocket.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/CheckSocket.java
new file mode 100644
index 0000000..12778c9
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/CheckSocket.java
@@ -0,0 +1,99 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.toolchain.test.EventQueue;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.WebSocketAdapter;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+
+@WebSocket
+public class CheckSocket extends WebSocketAdapter
+{
+    private static final Logger LOG = Log.getLogger(CheckSocket.class);
+    private CountDownLatch closeLatch = new CountDownLatch(1);
+    private CountDownLatch openLatch = new CountDownLatch(1);
+    private EventQueue<String> textMessages = new EventQueue<>();
+
+    public void awaitClose(int timeout, TimeUnit timeunit) throws InterruptedException
+    {
+        assertTrue("Timeout waiting for close",closeLatch.await(timeout,timeunit));
+    }
+
+    public void awaitOpen(int timeout, TimeUnit timeunit) throws InterruptedException
+    {
+        assertTrue("Timeout waiting for open",openLatch.await(timeout,timeunit));
+    }
+
+    public EventQueue<String> getTextMessages()
+    {
+        return textMessages;
+    }
+
+    @Override
+    public void onWebSocketClose(int statusCode, String reason)
+    {
+        LOG.debug("Close: {}, {}",statusCode,reason);
+        super.onWebSocketClose(statusCode,reason);
+        closeLatch.countDown();
+    }
+
+    @Override
+    public void onWebSocketConnect(Session sess)
+    {
+        LOG.debug("Open: {}",sess);
+        super.onWebSocketConnect(sess);
+        openLatch.countDown();
+    }
+    
+    @Override
+    public void onWebSocketError(Throwable cause)
+    {
+        LOG.warn("WebSocket Error",cause);
+        super.onWebSocketError(cause);
+    }
+
+    @Override
+    public void onWebSocketText(String message)
+    {
+        LOG.debug("TEXT: {}",message);
+        textMessages.add(message);
+    }
+
+    public void sendText(String msg) throws IOException
+    {
+        if (isConnected())
+        {
+            getRemote().sendString(msg);
+        }
+    }
+
+    public void close(int statusCode, String reason)
+    {
+        getSession().close(statusCode,reason);
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicapp/BasicAppTest.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicapp/BasicAppTest.java
new file mode 100644
index 0000000..40775fd
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicapp/BasicAppTest.java
@@ -0,0 +1,130 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.basicapp;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.io.File;
+import java.net.URI;
+import java.util.concurrent.TimeUnit;
+
+import javax.websocket.server.ServerContainer;
+
+import org.eclipse.jetty.cdi.servlet.EmbeddedCdiHandler;
+import org.eclipse.jetty.cdi.websocket.CheckSocket;
+import org.eclipse.jetty.cdi.websocket.cdiapp.InfoSocket;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.log.JettyLogHandler;
+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.websocket.api.StatusCode;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class BasicAppTest
+{
+    private static final Logger LOG = Log.getLogger(BasicAppTest.class);
+    
+    private static Server server;
+    @SuppressWarnings("unused")
+    private static URI serverHttpURI;
+    private static URI serverWebsocketURI;
+
+    @BeforeClass
+    public static void startServer() throws Exception
+    {
+        JettyLogHandler.config();
+
+        server = new Server();
+        ServerConnector connector = new ServerConnector(server);
+        connector.setPort(0);
+        server.addConnector(connector);
+
+        EmbeddedCdiHandler context = new EmbeddedCdiHandler();
+
+        File baseDir = MavenTestingUtils.getTestResourcesDir();
+
+        context.setBaseResource(Resource.newResource(baseDir));
+        context.setContextPath("/");
+        server.setHandler(context);
+        
+        // Add some websockets
+        ServerContainer container = WebSocketServerContainerInitializer.configureContext(context);
+        container.addEndpoint(EchoSocket.class);
+        container.addEndpoint(InfoSocket.class);
+
+        server.start();
+
+        String host = connector.getHost();
+        if (host == null)
+        {
+            host = "localhost";
+        }
+        int port = connector.getLocalPort();
+        serverHttpURI = new URI(String.format("http://%s:%d/",host,port));
+        serverWebsocketURI = new URI(String.format("ws://%s:%d/",host,port));
+    }
+
+    @AfterClass
+    public static void stopServer()
+    {
+        try
+        {
+            server.stop();
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+        }
+    }
+
+    @Test
+    public void testWebSocketEcho() throws Exception
+    {
+        WebSocketClient client = new WebSocketClient();
+        try
+        {
+            client.start();
+            CheckSocket socket = new CheckSocket();
+            client.connect(socket,serverWebsocketURI.resolve("/echo"));
+
+            socket.awaitOpen(2,TimeUnit.SECONDS);
+            socket.sendText("Hello World");
+            socket.close(StatusCode.NORMAL,"Test complete");
+            socket.awaitClose(2,TimeUnit.SECONDS);
+
+            assertThat("Messages received",socket.getTextMessages().size(),is(1));
+            String response = socket.getTextMessages().poll();
+            System.err.println(response);
+
+            assertThat("Message[0]",response,is("Hello World"));
+        }
+        finally
+        {
+            client.stop();
+        }
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicapp/EchoSocket.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicapp/EchoSocket.java
new file mode 100644
index 0000000..7e88f69
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicapp/EchoSocket.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.basicapp;
+
+import javax.websocket.CloseReason;
+import javax.websocket.OnClose;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.server.ServerEndpoint;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+@ServerEndpoint("/echo")
+public class EchoSocket
+{
+    private static final Logger LOG = Log.getLogger(EchoSocket.class);
+    @SuppressWarnings("unused")
+    private Session session;
+
+    @OnOpen
+    public void onOpen(Session session)
+    {
+        LOG.debug("onOpen(): {}",session);
+        this.session = session;
+    }
+
+    @OnClose
+    public void onClose(CloseReason close)
+    {
+        LOG.debug("onClose(): {}",close);
+        this.session = null;
+    }
+
+    @OnMessage
+    public String onMessage(String msg)
+    {
+        return msg;
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/Food.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/Food.java
new file mode 100644
index 0000000..47477f5
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/Food.java
@@ -0,0 +1,100 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.basicscope;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+public class Food
+{
+    private boolean constructed = false;
+    private boolean destroyed = false;
+    private String name;
+
+    @PreDestroy
+    void destroy()
+    {
+        destroyed = true;       
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+        {
+            return true;
+        }
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+        Food other = (Food)obj;
+        if (name == null)
+        {
+            if (other.name != null)
+            {
+                return false;
+            }
+        }
+        else if (!name.equals(other.name))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = (prime * result) + ((name == null) ? 0 : name.hashCode());
+        return result;
+    }
+
+    @PostConstruct
+    void init()
+    {
+        constructed = true;
+    }
+
+    public boolean isConstructed()
+    {
+        return constructed;
+    }
+
+    public boolean isDestroyed()
+    {
+        return destroyed;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/Meal.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/Meal.java
new file mode 100644
index 0000000..f8a42ef
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/Meal.java
@@ -0,0 +1,40 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.basicscope;
+
+import javax.inject.Inject;
+
+public class Meal
+{
+    @Inject
+    private Food entree;
+
+    @Inject
+    private Food side;
+
+    public Food getEntree()
+    {
+        return entree;
+    }
+
+    public Food getSide()
+    {
+        return side;
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/ScopeBasicsTest.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/ScopeBasicsTest.java
new file mode 100644
index 0000000..c594722
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/ScopeBasicsTest.java
@@ -0,0 +1,97 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.basicscope;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+
+import java.util.Set;
+
+import javax.enterprise.inject.spi.Bean;
+
+import org.eclipse.jetty.cdi.core.AnyLiteral;
+import org.eclipse.jetty.cdi.core.ScopedInstance;
+import org.eclipse.jetty.cdi.core.logging.Logging;
+import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.environment.se.WeldContainer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class ScopeBasicsTest
+{
+    private static Weld weld;
+    private static WeldContainer container;
+
+    @BeforeClass
+    public static void startWeld()
+    {
+        Logging.config();
+        weld = new Weld();
+        container = weld.initialize();
+    }
+
+    @AfterClass
+    public static void stopWeld()
+    {
+        weld.shutdown();
+    }
+
+    /**
+     * Validation of Scope / Inject logic on non-websocket-scoped classes
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testBasicBehavior() throws Exception
+    {
+        ScopedInstance<Meal> meal1Bean = newInstance(Meal.class);
+        Meal meal1 = meal1Bean.instance;
+        ScopedInstance<Meal> meal2Bean = newInstance(Meal.class);
+        Meal meal2 = meal2Bean.instance;
+
+        assertThat("Meals are not the same",meal1,not(sameInstance(meal2)));
+
+        assertThat("Meal 1 Entree Constructed",meal1.getEntree().isConstructed(),is(true));
+        assertThat("Meal 1 Side Constructed",meal1.getSide().isConstructed(),is(true));
+
+        assertThat("Meal parts not the same",meal1.getEntree(),not(sameInstance(meal1.getSide())));
+        assertThat("Meal entrees are the same",meal1.getEntree(),not(sameInstance(meal2.getEntree())));
+        assertThat("Meal sides are the same",meal1.getSide(),not(sameInstance(meal2.getSide())));
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public static <T> ScopedInstance<T> newInstance(Class<T> clazz) throws Exception
+    {
+        ScopedInstance sbean = new ScopedInstance();
+        Set<Bean<?>> beans = container.getBeanManager().getBeans(clazz,AnyLiteral.INSTANCE);
+        if (beans.size() > 0)
+        {
+            sbean.bean = beans.iterator().next();
+            sbean.creationalContext = container.getBeanManager().createCreationalContext(sbean.bean);
+            sbean.instance = container.getBeanManager().getReference(sbean.bean,clazz,sbean.creationalContext);
+            return sbean;
+        }
+        else
+        {
+            throw new Exception(String.format("Can't find class %s",clazz));
+        }
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/CdiAppTest.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/CdiAppTest.java
new file mode 100644
index 0000000..1e932f7
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/CdiAppTest.java
@@ -0,0 +1,185 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.cdiapp;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.io.File;
+import java.net.URI;
+import java.util.concurrent.TimeUnit;
+
+import javax.websocket.server.ServerContainer;
+
+import org.eclipse.jetty.cdi.servlet.EmbeddedCdiHandler;
+import org.eclipse.jetty.cdi.websocket.CheckSocket;
+import org.eclipse.jetty.cdi.websocket.WebSocketCdiInitializer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.log.JettyLogHandler;
+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.websocket.api.StatusCode;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class CdiAppTest
+{
+    private static final Logger LOG = Log.getLogger(CdiAppTest.class);
+    private static Server server;
+    private static URI serverWebsocketURI;
+
+    @BeforeClass
+    public static void startServer() throws Exception
+    {
+        JettyLogHandler.config();
+
+        server = new Server();
+        ServerConnector connector = new ServerConnector(server);
+        connector.setPort(0);
+        server.addConnector(connector);
+
+        EmbeddedCdiHandler context = new EmbeddedCdiHandler();
+        WebSocketCdiInitializer.configureContext(context);
+
+        File baseDir = MavenTestingUtils.getTestResourcesDir();
+
+        context.setBaseResource(Resource.newResource(baseDir));
+        context.setContextPath("/");
+        server.setHandler(context);
+        
+        // Add some websockets
+        ServerContainer container = WebSocketServerContainerInitializer.configureContext(context);
+        container.addEndpoint(EchoSocket.class);
+        container.addEndpoint(InfoSocket.class);
+
+        server.start();
+
+        String host = connector.getHost();
+        if (host == null)
+        {
+            host = "localhost";
+        }
+        int port = connector.getLocalPort();
+        serverWebsocketURI = new URI(String.format("ws://%s:%d/",host,port));
+    }
+
+    @AfterClass
+    public static void stopServer()
+    {
+        try
+        {
+            server.stop();
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+        }
+    }
+
+    @Test
+    public void testWebSocketActivated() throws Exception
+    {
+        WebSocketClient client = new WebSocketClient();
+        try
+        {
+            client.start();
+            CheckSocket socket = new CheckSocket();
+            client.connect(socket,serverWebsocketURI.resolve("/echo"));
+
+            socket.awaitOpen(2,TimeUnit.SECONDS);
+            socket.sendText("Hello");
+            socket.close(StatusCode.NORMAL,"Test complete");
+            socket.awaitClose(2,TimeUnit.SECONDS);
+
+            assertThat("Messages received",socket.getTextMessages().size(),is(1));
+            assertThat("Message[0]",socket.getTextMessages().poll(),is("Hello"));
+        }
+        finally
+        {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testWebSocket_Info_FieldPresence() throws Exception
+    {
+        WebSocketClient client = new WebSocketClient();
+        try
+        {
+            client.start();
+            CheckSocket socket = new CheckSocket();
+            client.connect(socket,serverWebsocketURI.resolve("/cdi-info"));
+
+            socket.awaitOpen(2,TimeUnit.SECONDS);
+            socket.sendText("info");
+            socket.close(StatusCode.NORMAL,"Test complete");
+            socket.awaitClose(2,TimeUnit.SECONDS);
+
+            assertThat("Messages received",socket.getTextMessages().size(),is(1));
+            String response = socket.getTextMessages().poll();
+            System.err.println(response);
+
+            assertThat("Message[0]",response,
+                    allOf(
+                            containsString("websocketSession is PRESENT"),
+                            containsString("httpSession is PRESENT"),
+                            containsString("servletContext is PRESENT")
+                    ));
+        }
+        finally
+        {
+            client.stop();
+        }
+    }
+    
+    @Test
+    public void testWebSocket_Info_DataFromCdi() throws Exception
+    {
+        WebSocketClient client = new WebSocketClient();
+        try
+        {
+            client.start();
+            CheckSocket socket = new CheckSocket();
+            client.connect(socket,serverWebsocketURI.resolve("/cdi-info"));
+
+            socket.awaitOpen(2,TimeUnit.SECONDS);
+            socket.sendText("data|stuff");
+            socket.close(StatusCode.NORMAL,"Test complete");
+            socket.awaitClose(2,TimeUnit.SECONDS);
+
+            assertThat("Messages received",socket.getTextMessages().size(),is(2));
+            String response = socket.getTextMessages().poll();
+            System.out.println("[0]" + response);
+            assertThat("Message[0]",response,containsString("Hello there stuff"));
+            System.out.println("[1]" + socket.getTextMessages().poll());
+        }
+        finally
+        {
+            client.stop();
+        }
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/DataMaker.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/DataMaker.java
new file mode 100644
index 0000000..85473a4
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/DataMaker.java
@@ -0,0 +1,43 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.cdiapp;
+
+import javax.inject.Inject;
+
+import org.eclipse.jetty.cdi.websocket.annotation.WebSocketScope;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.Session;
+
+public class DataMaker
+{
+    private static final Logger LOG = Log.getLogger(DataMaker.class);
+    
+    @Inject
+    @WebSocketScope
+    private Session session;
+
+    public void processMessage(String msg) 
+    {
+        LOG.debug(".processMessage({})",msg);
+        LOG.debug("session = {}",session);
+
+        session.getRemote().sendStringByFuture("Hello there " + msg);
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/EchoSocket.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/EchoSocket.java
new file mode 100644
index 0000000..2f67385
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/EchoSocket.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.cdiapp;
+
+import javax.websocket.CloseReason;
+import javax.websocket.OnClose;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.server.ServerEndpoint;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+@ServerEndpoint("/echo")
+public class EchoSocket
+{
+    private static final Logger LOG = Log.getLogger(EchoSocket.class);
+    @SuppressWarnings("unused")
+    private Session session;
+
+    @OnOpen
+    public void onOpen(Session session)
+    {
+        LOG.debug("onOpen(): {}",session);
+        this.session = session;
+    }
+
+    @OnClose
+    public void onClose(CloseReason close)
+    {
+        LOG.debug("onClose(): {}",close);
+        this.session = null;
+    }
+
+    @OnMessage
+    public String onMessage(String msg)
+    {
+        return msg;
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/InfoSocket.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/InfoSocket.java
new file mode 100644
index 0000000..8c31610
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/InfoSocket.java
@@ -0,0 +1,94 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.cdiapp;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.logging.Level;
+
+import javax.enterprise.context.SessionScoped;
+import javax.inject.Inject;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.websocket.CloseReason;
+import javax.websocket.OnClose;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.server.ServerEndpoint;
+
+@ServerEndpoint("/cdi-info")
+public class InfoSocket
+{
+    private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(InfoSocket.class.getName());
+
+    @SessionScoped
+    @Inject
+    private HttpSession httpSession;
+
+    @Inject
+    private ServletContext servletContext;
+    
+    @Inject
+    private DataMaker dataMaker;
+
+    private Session session;
+    
+    @OnOpen
+    public void onOpen(Session session)
+    {
+        LOG.log(Level.INFO,"onOpen(): {0}",session);
+        this.session = session;
+    }
+
+    @OnClose
+    public void onClose(CloseReason close)
+    {
+        LOG.log(Level.INFO,"onClose(): {}",close);
+        this.session = null;
+    }
+
+    @OnMessage
+    public String onMessage(String msg)
+    {
+        StringWriter str = new StringWriter();
+        PrintWriter out = new PrintWriter(str);
+        
+        String args[] = msg.split("\\|");
+
+        switch (args[0])
+        {
+            case "info":
+                out.printf("websocketSession is %s%n",asPresent(session));
+                out.printf("httpSession is %s%n",asPresent(httpSession));
+                out.printf("servletContext is %s%n",asPresent(servletContext));
+                break;
+            case "data":
+                dataMaker.processMessage(args[1]);
+                break;
+        }
+
+        return str.toString();
+    }
+
+    private String asPresent(Object obj)
+    {
+        return obj == null ? "NULL" : "PRESENT";
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/BogusSession.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/BogusSession.java
new file mode 100644
index 0000000..6c5f8eb
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/BogusSession.java
@@ -0,0 +1,150 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.wsscope;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import org.eclipse.jetty.websocket.api.CloseStatus;
+import org.eclipse.jetty.websocket.api.RemoteEndpoint;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.SuspendToken;
+import org.eclipse.jetty.websocket.api.UpgradeRequest;
+import org.eclipse.jetty.websocket.api.UpgradeResponse;
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+
+/**
+ * A bogus websocket Session concept object.
+ * <p>
+ * Used to test the scope @Inject of this kind of Session. This is important to test, as the BogusSession does not have
+ * a default constructor that CDI itself can use to create this object.
+ * <p>
+ * This object would need to be added to the beanstore for this scope for later @Inject to use.
+ */
+public class BogusSession implements Session
+{
+    private final String id;
+
+    public BogusSession(String id)
+    {
+        this.id = id;
+    }
+    
+    @Override
+    public String toString()
+    {
+        return String.format("BogusSession[id=%s]",id);
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+
+    @Override
+    public void close()
+    {
+    }
+
+    @Override
+    public void close(CloseStatus closeStatus)
+    {
+    }
+
+    @Override
+    public void close(int statusCode, String reason)
+    {
+    }
+
+    @Override
+    public void disconnect() throws IOException
+    {
+    }
+
+    @Override
+    public long getIdleTimeout()
+    {
+        return 0;
+    }
+
+    @Override
+    public InetSocketAddress getLocalAddress()
+    {
+        return null;
+    }
+
+    @Override
+    public WebSocketPolicy getPolicy()
+    {
+        return null;
+    }
+
+    @Override
+    public String getProtocolVersion()
+    {
+        return null;
+    }
+
+    @Override
+    public RemoteEndpoint getRemote()
+    {
+        return null;
+    }
+
+    @Override
+    public InetSocketAddress getRemoteAddress()
+    {
+        return null;
+    }
+
+    @Override
+    public UpgradeRequest getUpgradeRequest()
+    {
+        return null;
+    }
+
+    @Override
+    public UpgradeResponse getUpgradeResponse()
+    {
+        return null;
+    }
+
+    @Override
+    public boolean isOpen()
+    {
+        return false;
+    }
+
+    @Override
+    public boolean isSecure()
+    {
+        return false;
+    }
+
+    @Override
+    public void setIdleTimeout(long ms)
+    {
+    }
+
+    @Override
+    public SuspendToken suspend()
+    {
+        return null;
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/BogusSocket.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/BogusSocket.java
new file mode 100644
index 0000000..40bab2d
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/BogusSocket.java
@@ -0,0 +1,36 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.wsscope;
+
+import javax.inject.Inject;
+
+import org.eclipse.jetty.cdi.websocket.annotation.WebSocketScope;
+import org.eclipse.jetty.websocket.api.Session;
+
+public class BogusSocket
+{
+    @Inject
+    @WebSocketScope
+    private Session session;
+    
+    public Session getSession()
+    {
+        return session;
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/Food.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/Food.java
new file mode 100644
index 0000000..3e5bd40
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/Food.java
@@ -0,0 +1,119 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.wsscope;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+import org.eclipse.jetty.cdi.websocket.annotation.WebSocketScope;
+
+@WebSocketScope
+public class Food
+{
+    private boolean constructed = false;
+    private boolean destroyed = false;
+    private String name;
+
+    public Food()
+    {
+        // default constructor (for CDI use)
+    }
+
+    public Food(String name)
+    {
+        this.name = name;
+    }
+
+    @PreDestroy
+    void destroy()
+    {
+        destroyed = true;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+        {
+            return true;
+        }
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+        Food other = (Food)obj;
+        if (name == null)
+        {
+            if (other.name != null)
+            {
+                return false;
+            }
+        }
+        else if (!name.equals(other.name))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = (prime * result) + ((name == null) ? 0 : name.hashCode());
+        return result;
+    }
+
+    @PostConstruct
+    void init()
+    {
+        constructed = true;
+    }
+
+    public boolean isConstructed()
+    {
+        return constructed;
+    }
+
+    public boolean isDestroyed()
+    {
+        return destroyed;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%X[%s]",Food.class.getSimpleName(),hashCode(),name);
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/Meal.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/Meal.java
new file mode 100644
index 0000000..0236623
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/Meal.java
@@ -0,0 +1,40 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.wsscope;
+
+import javax.inject.Inject;
+
+public class Meal
+{
+    @Inject
+    private Food entree;
+
+    @Inject
+    private Food side;
+
+    public Food getEntree()
+    {
+        return entree;
+    }
+
+    public Food getSide()
+    {
+        return side;
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/WebSocketScopeBaselineTest.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/WebSocketScopeBaselineTest.java
new file mode 100644
index 0000000..22b3219
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/WebSocketScopeBaselineTest.java
@@ -0,0 +1,131 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.wsscope;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+
+import java.util.Set;
+
+import javax.enterprise.inject.spi.Bean;
+
+import org.eclipse.jetty.cdi.core.AnyLiteral;
+import org.eclipse.jetty.cdi.core.ScopedInstance;
+import org.eclipse.jetty.cdi.core.logging.Logging;
+import org.eclipse.jetty.cdi.websocket.WebSocketScopeContext;
+import org.eclipse.jetty.cdi.websocket.annotation.WebSocketScope;
+import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.environment.se.WeldContainer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class WebSocketScopeBaselineTest
+{
+    private static Weld weld;
+    private static WeldContainer container;
+
+    @BeforeClass
+    public static void startWeld()
+    {
+        Logging.config();
+        weld = new Weld();
+        container = weld.initialize();
+    }
+
+    @AfterClass
+    public static void stopWeld()
+    {
+        weld.shutdown();
+    }
+    
+    /**
+     * Test behavior of {@link WebSocketScope} in basic operation.
+     * <p>
+     * Food is declared as part of WebSocketScope, and as such, only 1 instance of it can exist.
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testScopeBehavior() throws Exception
+    {
+        ScopedInstance<WebSocketScopeContext> wsScopeBean = newInstance(WebSocketScopeContext.class);
+        WebSocketScopeContext wsScope = wsScopeBean.instance;
+
+        wsScope.create();
+        Meal meal1;
+        try
+        {
+            wsScope.begin();
+            ScopedInstance<Meal> meal1Bean = newInstance(Meal.class);
+            meal1 = meal1Bean.instance;
+            ScopedInstance<Meal> meal2Bean = newInstance(Meal.class);
+            Meal meal2 = meal2Bean.instance;
+            
+            assertThat("Meals are not the same",meal1,not(sameInstance(meal2)));
+            
+            assertThat("Meal 1 Entree Constructed",meal1.getEntree().isConstructed(),is(true));
+            assertThat("Meal 1 Side Constructed",meal1.getSide().isConstructed(),is(true));
+            
+            /* Since Food is annotated with @WebSocketScope, there can only be one instance of it
+             * in use with the 2 Meal objects.
+             */
+            assertThat("Meal parts not the same",meal1.getEntree(),sameInstance(meal1.getSide()));
+            assertThat("Meal entrees are the same",meal1.getEntree(),sameInstance(meal2.getEntree()));
+            assertThat("Meal sides are the same",meal1.getSide(),sameInstance(meal2.getSide()));
+
+            meal1Bean.destroy();
+            meal2Bean.destroy();
+        }
+        finally
+        {
+            wsScope.end();
+        }
+        
+        Food entree1 = meal1.getEntree();
+        Food side1 = meal1.getSide();
+
+        assertThat("Meal 1 entree destroyed",entree1.isDestroyed(),is(false));
+        assertThat("Meal 1 side destroyed",side1.isDestroyed(),is(false));
+        wsScope.destroy();
+
+        // assertThat("Meal 1 entree destroyed",entree1.isDestroyed(),is(true));
+        // assertThat("Meal 1 side destroyed",side1.isDestroyed(),is(true));
+        wsScopeBean.destroy();
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public static <T> ScopedInstance<T> newInstance(Class<T> clazz) throws Exception
+    {
+        ScopedInstance sbean = new ScopedInstance();
+        Set<Bean<?>> beans = container.getBeanManager().getBeans(clazz,AnyLiteral.INSTANCE);
+        if (beans.size() > 0)
+        {
+            sbean.bean = beans.iterator().next();
+            sbean.creationalContext = container.getBeanManager().createCreationalContext(sbean.bean);
+            sbean.instance = container.getBeanManager().getReference(sbean.bean,clazz,sbean.creationalContext);
+            return sbean;
+        }
+        else
+        {
+            throw new Exception(String.format("Can't find class %s",clazz));
+        }
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/WebSocketScopeSessionTest.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/WebSocketScopeSessionTest.java
new file mode 100644
index 0000000..140a55e
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/WebSocketScopeSessionTest.java
@@ -0,0 +1,253 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.cdi.websocket.wsscope;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import javax.enterprise.inject.spi.Bean;
+
+import org.eclipse.jetty.cdi.core.AnyLiteral;
+import org.eclipse.jetty.cdi.core.ScopedInstance;
+import org.eclipse.jetty.cdi.core.logging.Logging;
+import org.eclipse.jetty.cdi.websocket.WebSocketScopeContext;
+import org.eclipse.jetty.websocket.api.Session;
+import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.environment.se.WeldContainer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class WebSocketScopeSessionTest
+{
+    private static Weld weld;
+    private static WeldContainer container;
+
+    @BeforeClass
+    public static void startWeld()
+    {
+        Logging.config();
+        weld = new Weld();
+        container = weld.initialize();
+    }
+
+    @AfterClass
+    public static void stopWeld()
+    {
+        weld.shutdown();
+    }
+    
+    @Test
+    public void testSessionActivation() throws Exception
+    {
+        ScopedInstance<WebSocketScopeContext> wsScopeBean = newInstance(WebSocketScopeContext.class);
+        WebSocketScopeContext wsScope = wsScopeBean.instance;
+
+        wsScope.create();
+        try
+        {
+            // Scope 1
+            wsScope.begin();
+            BogusSession sess = new BogusSession("1");
+            wsScope.setSession(sess);
+            ScopedInstance<BogusSocket> sock1Bean = newInstance(BogusSocket.class);
+            BogusSocket sock1 = sock1Bean.instance;
+            assertThat("Socket 1 Session",sock1.getSession().toString(),is(sess.toString()));
+
+            sock1Bean.destroy();
+        }
+        finally
+        {
+            wsScope.end();
+        }
+
+        wsScope.destroy();
+        wsScopeBean.destroy();
+    }
+    
+    @Test
+    public void testMultiSession_Sequential() throws Exception
+    {
+        ScopedInstance<WebSocketScopeContext> wsScope1Bean = newInstance(WebSocketScopeContext.class);
+        WebSocketScopeContext wsScope1 = wsScope1Bean.instance;
+        
+        ScopedInstance<WebSocketScopeContext> wsScope2Bean = newInstance(WebSocketScopeContext.class);
+        WebSocketScopeContext wsScope2 = wsScope2Bean.instance;
+
+        wsScope1.create();
+        try
+        {
+            // Scope 1
+            wsScope1.begin();
+            BogusSession sess = new BogusSession("1");
+            wsScope1.setSession(sess);
+            ScopedInstance<BogusSocket> sock1Bean = newInstance(BogusSocket.class);
+            BogusSocket sock1 = sock1Bean.instance;
+            assertThat("Socket 1 Session",sock1.getSession(),sameInstance((Session)sess));
+            sock1Bean.destroy();
+        }
+        finally
+        {
+            wsScope1.end();
+        }
+        
+        wsScope1.destroy();
+        wsScope1Bean.destroy();
+
+        wsScope2.create();
+        try
+        {
+            // Scope 2
+            wsScope2.begin();
+            BogusSession sess = new BogusSession("2");
+            wsScope2.setSession(sess);
+            ScopedInstance<BogusSocket> sock2Bean = newInstance(BogusSocket.class);
+            BogusSocket sock2 = sock2Bean.instance;
+            assertThat("Socket 2 Session",sock2.getSession(),sameInstance((Session)sess));
+            sock2Bean.destroy();
+        }
+        finally
+        {
+            wsScope2.end();
+        }
+
+        wsScope2.destroy();
+        wsScope2Bean.destroy();
+    }
+    
+    @Test
+    public void testMultiSession_Overlapping() throws Exception
+    {
+        final CountDownLatch midLatch = new CountDownLatch(2);
+        final CountDownLatch end1Latch = new CountDownLatch(1);
+        
+        Callable<Session> call1 = new Callable<Session>() {
+            @Override
+            public Session call() throws Exception
+            {
+                Session ret = null;
+                ScopedInstance<WebSocketScopeContext> wsScope1Bean = newInstance(WebSocketScopeContext.class);
+                WebSocketScopeContext wsScope1 = wsScope1Bean.instance;
+                
+                wsScope1.create();
+                try
+                {
+                    // Scope 1
+                    wsScope1.begin();
+                    BogusSession sess = new BogusSession("1");
+                    wsScope1.setSession(sess);
+                    
+                    midLatch.countDown();
+                    midLatch.await(1, TimeUnit.SECONDS);
+                    
+                    ScopedInstance<BogusSocket> sock1Bean = newInstance(BogusSocket.class);
+                    BogusSocket sock1 = sock1Bean.instance;
+                    assertThat("Socket 1 Session",sock1.getSession(),sameInstance((Session)sess));
+                    ret = sock1.getSession();
+                    sock1Bean.destroy();
+                }
+                finally
+                {
+                    wsScope1.end();
+                }
+                
+                wsScope1.destroy();
+                wsScope1Bean.destroy();
+                end1Latch.countDown();
+                return ret;
+            }
+        };
+        
+        final CountDownLatch end2Latch = new CountDownLatch(1);
+        
+        Callable<Session> call2 = new Callable<Session>() {
+            @Override
+            public Session call() throws Exception
+            {
+                Session ret = null;
+                ScopedInstance<WebSocketScopeContext> wsScope2Bean = newInstance(WebSocketScopeContext.class);
+                WebSocketScopeContext wsScope2 = wsScope2Bean.instance;
+
+                wsScope2.create();
+                try
+                {
+                    // Scope 2
+                    wsScope2.begin();
+                    BogusSession sess = new BogusSession("2");
+                    wsScope2.setSession(sess);
+                    ScopedInstance<BogusSocket> sock2Bean = newInstance(BogusSocket.class);
+                    
+                    midLatch.countDown();
+                    midLatch.await(1, TimeUnit.SECONDS);
+                    
+                    BogusSocket sock2 = sock2Bean.instance;
+                    ret = sock2.getSession();
+                    assertThat("Socket 2 Session",sock2.getSession(),sameInstance((Session)sess)); 
+                    sock2Bean.destroy();
+                }
+                finally
+                {
+                    wsScope2.end();
+                }
+
+                wsScope2.destroy();
+                wsScope2Bean.destroy();
+                end2Latch.countDown();
+                return ret;
+            }
+        };
+        
+        ExecutorService svc = Executors.newFixedThreadPool(4);
+        Future<Session> fut1 = svc.submit(call1);
+        Future<Session> fut2 = svc.submit(call2);
+        
+        Session sess1 = fut1.get(1,TimeUnit.SECONDS);
+        Session sess2 = fut2.get(1,TimeUnit.SECONDS);
+        
+        assertThat("Sessions are different", sess1, not(sameInstance(sess2)));
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public static <T> ScopedInstance<T> newInstance(Class<T> clazz)
+    {
+        ScopedInstance sbean = new ScopedInstance();
+        Set<Bean<?>> beans = container.getBeanManager().getBeans(clazz,AnyLiteral.INSTANCE);
+        if (beans.size() > 0)
+        {
+            sbean.bean = beans.iterator().next();
+            sbean.creationalContext = container.getBeanManager().createCreationalContext(sbean.bean);
+            sbean.instance = container.getBeanManager().getReference(sbean.bean,clazz,sbean.creationalContext);
+            return sbean;
+        }
+        else
+        {
+            throw new RuntimeException(String.format("Can't find class %s",clazz));
+        }
+    }
+}
diff --git a/jetty-cdi/cdi-websocket/src/test/resources/META-INF/beans.xml b/jetty-cdi/cdi-websocket/src/test/resources/META-INF/beans.xml
new file mode 100644
index 0000000..f158a71
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/resources/META-INF/beans.xml
@@ -0,0 +1,6 @@
+<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
+        http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
+       bean-discovery-mode="all">
+</beans>
\ No newline at end of file
diff --git a/jetty-cdi/cdi-websocket/src/test/resources/jetty-logging.properties b/jetty-cdi/cdi-websocket/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..eeed11d
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/resources/jetty-logging.properties
@@ -0,0 +1,15 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+org.jetty.LEVEL=INFO
+org.jboss.LEVEL=INFO
+# org.eclipse.jetty.LEVEL=INFO
+ 
+# org.eclipse.jetty.util.component.LEVEL=DEBUG
+
+# org.eclipse.jetty.websocket.common.LEVEL=DEBUG
+# org.eclipse.jetty.util.DecoratedObjectFactory.LEVEL=DEBUG
+# org.eclipse.jetty.cdi.LEVEL=DEBUG
+
+# org.eclipse.jetty.LEVEL=DEBUG
+# org.eclipse.jetty.websocket.LEVEL=DEBUG
+# org.eclipse.jetty.websocket.client.LEVEL=DEBUG
+
diff --git a/jetty-cdi/cdi-websocket/src/test/resources/logging.properties b/jetty-cdi/cdi-websocket/src/test/resources/logging.properties
new file mode 100644
index 0000000..cfec8c7
--- /dev/null
+++ b/jetty-cdi/cdi-websocket/src/test/resources/logging.properties
@@ -0,0 +1,2 @@
+handlers = org.eclipse.jetty.util.log.JettyLogHandler
+.level=FINE
diff --git a/jetty-cdi/pom.xml b/jetty-cdi/pom.xml
index a958578..d44739c 100644
--- a/jetty-cdi/pom.xml
+++ b/jetty-cdi/pom.xml
@@ -2,48 +2,26 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
-  <artifactId>jetty-cdi</artifactId>
-  <name>Jetty :: CDI Configurations</name>
+  <groupId>org.eclipse.jetty.cdi</groupId>
+  <artifactId>jetty-cdi-parent</artifactId>
+  <name>Jetty :: CDI :: Parent</name>
   <url>http://www.eclipse.org/jetty</url>
-  <packaging>jar</packaging>
+  <packaging>pom</packaging>
   <properties>
-    <bundle-symbolic-name>${project.groupId}.${project.artifactId}</bundle-symbolic-name>
+    <weld.version>2.2.9.Final</weld.version>
   </properties>
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
 
-  <dependencies>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-plus</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-deploy</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-  </dependencies>
+  <modules>
+    <module>cdi-core</module>
+    <module>cdi-servlet</module>
+    <module>cdi-full-servlet</module>
+    <module>cdi-websocket</module>
+    <module>test-cdi-webapp</module>
+    <!-- needs to be fixed still 
+    <module>test-cdi-it</module>
+     -->
+  </modules>
 </project>
diff --git a/jetty-cdi/src/main/config/etc/jetty-cdi.xml b/jetty-cdi/src/main/config/etc/jetty-cdi.xml
deleted file mode 100644
index d364f4c..0000000
--- a/jetty-cdi/src/main/config/etc/jetty-cdi.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<!-- =============================================================== -->
-<!-- Mixin the Weld / CDI classes to the class loader                -->
-<!-- =============================================================== -->
-
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
-  <Ref refid="DeploymentManager">
-    <Call name="addLifeCycleBinding">
-      <Arg>
-        <New
-          class="org.eclipse.jetty.cdi.WeldDeploymentBinding">
-        </New>
-      </Arg>
-    </Call>
-  </Ref>
-</Configure>
-
diff --git a/jetty-cdi/src/main/config/modules/cdi.mod b/jetty-cdi/src/main/config/modules/cdi.mod
deleted file mode 100644
index 68dea58..0000000
--- a/jetty-cdi/src/main/config/modules/cdi.mod
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# CDI / Weld Jetty module
-#
-
-[depend]
-deploy
-annotations
-plus
-# JSP (and EL) are requirements for CDI and Weld
-jsp
-
-[files]
-lib/weld/
-http://central.maven.org/maven2/org/jboss/weld/servlet/weld-servlet/2.2.5.Final/weld-servlet-2.2.5.Final.jar|lib/weld/weld-servlet-2.2.5.Final.jar
-
-[lib]
-lib/weld/weld-servlet-2.2.5.Final.jar
-lib/jetty-cdi-${jetty.version}.jar
-
-[xml]
-etc/jetty-cdi.xml
-
-[license]
-Weld is an open source project hosted on Github and released under the Apache 2.0 license.
-http://weld.cdi-spec.org/
-http://www.apache.org/licenses/LICENSE-2.0.html
diff --git a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/WeldDeploymentBinding.java b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/WeldDeploymentBinding.java
deleted file mode 100644
index a536943..0000000
--- a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/WeldDeploymentBinding.java
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.cdi;
-
-import javax.naming.Reference;
-
-import org.eclipse.jetty.deploy.App;
-import org.eclipse.jetty.deploy.AppLifeCycle;
-import org.eclipse.jetty.deploy.graph.Node;
-import org.eclipse.jetty.plus.jndi.Resource;
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.webapp.WebAppContext;
-
-/**
- * Perform some basic weld configuration of WebAppContext
- */
-public class WeldDeploymentBinding implements AppLifeCycle.Binding
-{
-    public String[] getBindingTargets()
-    {
-        return new String[]
-        { "deploying" };
-    }
-
-    public void processBinding(Node node, App app) throws Exception
-    {
-        ContextHandler handler = app.getContextHandler();
-        if (handler == null)
-        {
-            throw new NullPointerException("No Handler created for App: " + app);
-        }
-
-        if (handler instanceof WebAppContext)
-        {
-            WebAppContext webapp = (WebAppContext)handler;
-
-            // Add context specific weld container reference.
-            // See https://issues.jboss.org/browse/WELD-1710
-            // and https://github.com/weld/core/blob/2.2.5.Final/environments/servlet/core/src/main/java/org/jboss/weld/environment/servlet/WeldServletLifecycle.java#L244-L253
-            webapp.setInitParameter("org.jboss.weld.environment.container.class",
-                    "org.jboss.weld.environment.jetty.JettyContainer");
-            
-            // Setup Weld BeanManager reference
-            Reference ref = new Reference("javax.enterprise.inject.spi.BeanManager",
-                    "org.jboss.weld.resources.ManagerObjectFactory", null);
-            new Resource(webapp,"BeanManager",ref);
-            
-            // webapp cannot change / replace weld classes
-            webapp.addSystemClass("org.jboss.weld.");
-            webapp.addSystemClass("org.jboss.classfilewriter.");
-            webapp.addSystemClass("org.jboss.logging.");
-            webapp.addSystemClass("com.google.common.");
-            
-            // don't hide weld classes from webapps (allow webapp to use ones from system classloader)
-            webapp.addServerClass("-org.jboss.weld.");
-            webapp.addServerClass("-org.jboss.classfilewriter.");
-            webapp.addServerClass("-org.jboss.logging.");
-            webapp.addServerClass("-com.google.common.");
-        }
-    }
-}
diff --git a/jetty-cdi/test-cdi-it/pom.xml b/jetty-cdi/test-cdi-it/pom.xml
new file mode 100644
index 0000000..5329541
--- /dev/null
+++ b/jetty-cdi/test-cdi-it/pom.xml
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.cdi</groupId>
+    <artifactId>jetty-cdi-parent</artifactId>
+    <version>9.3.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cdi-webapp-it</artifactId>
+  <packaging>jar</packaging>
+  <name>Jetty :: CDI :: Test :: WebApp Integration Tests</name>
+  <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <bundle-symbolic-name>${project.groupId}.cdi.webapp.it</bundle-symbolic-name>
+    <scripts-dir>${project.basedir}/src/test/scripts</scripts-dir>
+    <test-base-dir>${project.build.directory}/test-base</test-base-dir>
+    <test-home-dir>${project.build.directory}/test-home</test-home-dir>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-distribution</artifactId>
+      <version>${project.version}</version>
+      <type>zip</type>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.cdi</groupId>
+      <artifactId>test-cdi-webapp</artifactId>
+      <version>${project.version}</version>
+      <type>war</type>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <!-- DO NOT DEPLOY (or Release) -->
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>copy-apps-for-testing</id>
+            <phase>process-test-resources</phase>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <includeArtifactIds>test-cdi-webapp</includeArtifactIds>
+              <includeScope>runtime</includeScope>
+              <includeTypes>war</includeTypes>
+              <overwriteSnapshots>true</overwriteSnapshots>
+              <overwriteReleases>true</overwriteReleases>
+              <stripVersion>true</stripVersion>
+              <outputDirectory>${test-base-dir}/webapps</outputDirectory>
+            </configuration>
+          </execution>
+          <execution>
+            <id>unpack-jetty-distro</id>
+            <phase>process-test-resources</phase>
+            <goals>
+              <goal>unpack-dependencies</goal>
+            </goals>
+            <configuration>
+              <includeArtifactIds>jetty-distribution</includeArtifactIds>
+              <includeScope>runtime</includeScope>
+              <includeTypes>zip</includeTypes>
+              <outputAbsoluteArtifactFilename>true</outputAbsoluteArtifactFilename>
+              <outputDirectory>${test-home-dir}</outputDirectory>
+              <overWriteSnapshots>true</overWriteSnapshots>
+              <overWriteIfNewer>true</overWriteIfNewer>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+        <version>2.17</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>integration-test</goal>
+              <goal>verify</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <version>1.7</version>
+        <executions>
+          <execution>
+            <id>start-jetty</id>
+            <phase>pre-integration-test</phase>
+            <goals>
+              <goal>run</goal>
+            </goals>
+            <configuration>
+              <target>
+                <property name="jetty.cdi.home" location="${test-home-dir}/jetty-distribution-${project.version}"/>
+                <property name="jetty.cdi.base" location="${test-base-dir}"/>
+                <echo>Integration Test : Setup Jetty</echo>
+                <exec executable="${run.command}"
+                      dir="${scripts-dir}"
+                      spawn="false">
+                  <arg value="${run.command.xtra}"/>
+                  <arg value="${setup.script}"/>
+                  <arg file="${java.home}"/>
+                  <arg file="${jetty.cdi.home}"/>
+                  <arg file="${jetty.cdi.base}"/>
+                </exec>
+
+                <echo>Integration Test : Starting Jetty ...</echo>
+                <exec executable="${run.command}"
+                      dir="${scripts-dir}"
+                      spawn="true">
+                  <arg value="${run.command.xtra}"/>
+                  <arg value="${start.script}"/>
+                  <arg file="${java.home}"/>
+                  <arg file="${jetty.cdi.home}"/>
+                  <arg file="${jetty.cdi.base}"/>
+                </exec>
+                <waitfor maxwait="5" maxwaitunit="second"
+                  checkevery="100" checkeveryunit="millisecond">
+                  <http url="http://localhost:58080/cdi-webapp/"/>
+                </waitfor>
+                <echo>Integration Test : Jetty is now available</echo>
+              </target>
+            </configuration>
+          </execution>
+          <execution>
+            <id>stop-jetty</id>
+            <phase>post-integration-test</phase>
+            <goals>
+              <goal>run</goal>
+            </goals>
+            <configuration>
+              <target>
+                <property name="jetty.cdi.home" location="${test-home-dir}/jetty-distribution-${project.version}"/>
+                <property name="jetty.cdi.base" location="${test-base-dir}"/>
+                <echo>Integration Test : Stop Jetty</echo>
+                <exec executable="${run.command}"
+                      dir="${scripts-dir}"
+                      spawn="false">
+                  <arg value="${run.command.xtra}"/>
+                  <arg value="${stop.script}"/>
+                  <arg file="${java.home}"/>
+                  <arg file="${jetty.cdi.home}"/>
+                  <arg file="${jetty.cdi.base}"/>
+                </exec>
+              </target>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  <profiles>
+    <profile>
+      <id>it-windows</id>
+      <activation>
+        <os>
+          <family>Windows</family>
+        </os>
+      </activation>
+      <properties>
+        <run.command>cmd</run.command>
+        <run.command.xtra>/c</run.command.xtra>
+        <start.script>start-jetty.bat</start.script>
+        <stop.script>stop-jetty.bat</stop.script>
+      </properties>
+    </profile>
+    <profile>
+      <id>it-unix</id>
+      <activation>
+        <os>
+          <family>unix</family>
+        </os>
+      </activation>
+      <properties>
+        <run.command>sh</run.command>
+        <run.command.xtra>--</run.command.xtra>
+        <setup.script>setup-jetty.sh</setup.script>
+        <start.script>start-jetty.sh</start.script>
+        <stop.script>stop-jetty.sh</stop.script>
+      </properties>
+    </profile>
+  </profiles>
+</project>
diff --git a/jetty-cdi/test-cdi-it/src/test/java/org/eclipse/jetty/tests/HelloIT.java b/jetty-cdi/test-cdi-it/src/test/java/org/eclipse/jetty/tests/HelloIT.java
new file mode 100644
index 0000000..305532f
--- /dev/null
+++ b/jetty-cdi/test-cdi-it/src/test/java/org/eclipse/jetty/tests/HelloIT.java
@@ -0,0 +1,41 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.tests;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.net.URI;
+
+import org.eclipse.jetty.toolchain.test.SimpleRequest;
+import org.junit.Test;
+
+/**
+ * Basic tests for a simple @WebServlet with no CDI
+ */
+public class HelloIT
+{
+    @Test
+    public void testBasic() throws Exception
+    {
+        URI serverURI = new URI("http://localhost:58080/cdi-webapp/");
+        SimpleRequest req = new SimpleRequest(serverURI);
+        assertThat(req.getString("hello"),is("Hello World" + System.lineSeparator()));
+    }
+}
diff --git a/jetty-cdi/test-cdi-it/src/test/java/org/eclipse/jetty/tests/ServerInfoIT.java b/jetty-cdi/test-cdi-it/src/test/java/org/eclipse/jetty/tests/ServerInfoIT.java
new file mode 100644
index 0000000..9701275
--- /dev/null
+++ b/jetty-cdi/test-cdi-it/src/test/java/org/eclipse/jetty/tests/ServerInfoIT.java
@@ -0,0 +1,47 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.tests;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.net.URI;
+
+import org.eclipse.jetty.toolchain.test.SimpleRequest;
+import org.junit.Test;
+
+public class ServerInfoIT
+{
+    @Test
+    public void testGET() throws Exception {
+        URI serverURI = new URI("http://localhost:58080/cdi-webapp/");
+        SimpleRequest req = new SimpleRequest(serverURI);
+        
+        // Typical response:
+        // context = ServletContext@o.e.j.w.WebAppContext@37cb63fd{/cdi-webapp,
+        // file:///tmp/jetty-0.0.0.0-58080-cdi-webapp.war-_cdi-webapp-any-417759194514596377.dir/webapp/,AVAILABLE}
+        // {/cdi-webapp.war}\ncontext.contextPath = /cdi-webapp\ncontext.effective-version = 3.1\n
+        assertThat(req.getString("serverinfo"),
+                allOf(
+                containsString("context = ServletContext@"),
+                containsString("context.contextPath = /cdi-webapp"),
+                containsString("context.effective-version = 3.1")
+                ));
+    }
+}
diff --git a/jetty-cdi/test-cdi-it/src/test/java/org/eclipse/jetty/tests/ws/SessionInfoIT.java b/jetty-cdi/test-cdi-it/src/test/java/org/eclipse/jetty/tests/ws/SessionInfoIT.java
new file mode 100644
index 0000000..878f470
--- /dev/null
+++ b/jetty-cdi/test-cdi-it/src/test/java/org/eclipse/jetty/tests/ws/SessionInfoIT.java
@@ -0,0 +1,106 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.tests.ws;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.net.URI;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.websocket.ClientEndpoint;
+import javax.websocket.CloseReason;
+import javax.websocket.ContainerProvider;
+import javax.websocket.OnClose;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.WebSocketContainer;
+
+import org.eclipse.jetty.toolchain.test.EventQueue;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.junit.Test;
+
+public class SessionInfoIT
+{
+    @ClientEndpoint
+    public static class ClientSessionInfoSocket
+    {
+        private static final Logger LOG = Log.getLogger(SessionInfoIT.ClientSessionInfoSocket.class);
+        
+        public CountDownLatch openLatch = new CountDownLatch(1);
+        public CountDownLatch closeLatch = new CountDownLatch(1);
+        public Session session;
+        public EventQueue<String> messages = new EventQueue<>();
+        public CloseReason closeReason;
+        
+        @OnOpen
+        public void onOpen(Session session)
+        {
+            LOG.info("onOpen(): {}", session);
+            this.session = session;
+            this.openLatch.countDown();
+        }
+        
+        @OnClose
+        public void onClose(CloseReason close)
+        {
+            LOG.info("onClose(): {}", close);
+            this.session = null;
+            this.closeReason = close;
+            this.closeLatch.countDown();
+        }
+
+        @OnMessage
+        public void onMessage(String message)
+        {
+            LOG.info("onMessage(): {}", message);
+            this.messages.offer(message);
+        }
+    }
+
+    @Test
+    public void testSessionInfo() throws Exception
+    {
+        URI serverURI = new URI("ws://localhost:58080/cdi-webapp/");
+
+        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
+
+        ClientSessionInfoSocket socket = new ClientSessionInfoSocket();
+        
+        container.connectToServer(socket,serverURI.resolve("sessioninfo"));
+
+        assertThat("Await open", socket.openLatch.await(1,TimeUnit.SECONDS), is(true));
+        
+        socket.session.getBasicRemote().sendText("info");
+        socket.messages.awaitEventCount(1,2,TimeUnit.SECONDS);
+        
+        System.out.printf("socket.messages.size = %s%n",socket.messages.size());
+        
+        String msg = socket.messages.poll();
+        System.out.printf("Message is [%s]%n",msg);
+        
+        assertThat("Message", msg, containsString("HttpSession = HttpSession"));
+        
+        socket.session.getBasicRemote().sendText("close");
+        assertThat("Await close", socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
+    }
+}
diff --git a/jetty-cdi/test-cdi-it/src/test/resources/jetty-logging.properties b/jetty-cdi/test-cdi-it/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..a3e0efd
--- /dev/null
+++ b/jetty-cdi/test-cdi-it/src/test/resources/jetty-logging.properties
@@ -0,0 +1,10 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+org.jboss.LEVEL=DEBUG
+org.eclipse.jetty.LEVEL=INFO
+
+org.eclipse.jetty.util.DecoratedObjectFactory.LEVEL=DEBUG
+
+# org.eclipse.jetty.LEVEL=DEBUG
+org.eclipse.jetty.websocket.LEVEL=DEBUG
+# org.eclipse.jetty.websocket.client.LEVEL=DEBUG
+
diff --git a/jetty-cdi/test-cdi-it/src/test/scripts/setup-jetty.sh b/jetty-cdi/test-cdi-it/src/test/scripts/setup-jetty.sh
new file mode 100755
index 0000000..3dc240f
--- /dev/null
+++ b/jetty-cdi/test-cdi-it/src/test/scripts/setup-jetty.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+JAVA_HOME=$1
+JETTY_HOME=$2
+JETTY_BASE=$3
+
+echo \${java.home}  : $JAVA_HOME
+echo \${jetty.home} : $JETTY_HOME
+echo \${jetty.base} : $JETTY_BASE
+
+cd "$JETTY_BASE"
+
+"$JAVA_HOME/bin/java" -jar "$JETTY_HOME/start.jar" \
+    --approve-all-licenses \
+    --add-to-start=deploy,http,annotations,websocket,cdi,logging
+
+"$JAVA_HOME/bin/java" -jar "$JETTY_HOME/start.jar" \
+    --version
+
diff --git a/jetty-cdi/test-cdi-it/src/test/scripts/start-jetty.sh b/jetty-cdi/test-cdi-it/src/test/scripts/start-jetty.sh
new file mode 100755
index 0000000..ea7843b
--- /dev/null
+++ b/jetty-cdi/test-cdi-it/src/test/scripts/start-jetty.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+JAVA_HOME=$1
+JETTY_HOME=$2
+JETTY_BASE=$3
+
+echo \${java.home}  : $JAVA_HOME
+echo \${jetty.home} : $JETTY_HOME
+echo \${jetty.base} : $JETTY_BASE
+
+cd "$JETTY_BASE"
+
+"$JAVA_HOME/bin/java" -jar "$JETTY_HOME/start.jar" \
+    jetty.http.port=58080 \
+    STOP.PORT=58181 STOP.KEY=it
+
+
diff --git a/tests/test-cdi/cdi-webapp-it/src/test/scripts/stop-jetty.sh b/jetty-cdi/test-cdi-it/src/test/scripts/stop-jetty.sh
similarity index 100%
rename from tests/test-cdi/cdi-webapp-it/src/test/scripts/stop-jetty.sh
rename to jetty-cdi/test-cdi-it/src/test/scripts/stop-jetty.sh
diff --git a/jetty-cdi/test-cdi-webapp/pom.xml b/jetty-cdi/test-cdi-webapp/pom.xml
new file mode 100644
index 0000000..7756299
--- /dev/null
+++ b/jetty-cdi/test-cdi-webapp/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.cdi</groupId>
+    <artifactId>jetty-cdi-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>test-cdi-webapp</artifactId>
+  <packaging>war</packaging>
+  <name>Jetty :: CDI :: Test :: WebApp</name>
+  <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <bundle-symbolic-name>${project.groupId}.cdi.webapp.noweld</bundle-symbolic-name>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.websocket</groupId>
+      <artifactId>javax.websocket-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.enterprise</groupId>
+      <artifactId>cdi-api</artifactId>
+      <version>1.1</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.weld.servlet</groupId>
+      <artifactId>weld-servlet</artifactId>
+      <version>${weld.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <finalName>cdi-webapp</finalName>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <!-- DO NOT DEPLOY (or Release) -->
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>with-weld</id>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+            <configuration>
+              <descriptors>
+                <descriptor>src/assembly/with-weld.xml</descriptor>
+              </descriptors>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+         <groupId>org.eclipse.jetty</groupId>
+         <artifactId>jetty-maven-plugin</artifactId>
+         <version>${project.version}</version>
+         <configuration>
+         </configuration>
+         <dependencies>
+           <dependency>
+               <groupId>org.eclipse.jetty.cdi</groupId>
+               <artifactId>cdi-full-servlet</artifactId>
+               <version>${project.version}</version>
+               <type>pom</type>
+           </dependency>
+         </dependencies>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/jetty-cdi/test-cdi-webapp/src/assembly/with-weld.xml b/jetty-cdi/test-cdi-webapp/src/assembly/with-weld.xml
new file mode 100644
index 0000000..ca17471
--- /dev/null
+++ b/jetty-cdi/test-cdi-webapp/src/assembly/with-weld.xml
@@ -0,0 +1,31 @@
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" 
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+  <id>with-weld</id>
+  <formats>
+    <format>war</format>
+  </formats>
+  <includeBaseDirectory>false</includeBaseDirectory>
+  <fileSets>
+    <fileSet>
+      <directory>${project.basedir}/src/main/webapp</directory>
+      <outputDirectory>/</outputDirectory>
+    </fileSet>
+    <fileSet>
+      <directory>${project.build.outputDirectory}</directory>
+      <outputDirectory>/WEB-INF/classes</outputDirectory>
+    </fileSet>
+  </fileSets>
+  <dependencySets>
+    <dependencySet>
+      <outputDirectory>/WEB-INF/lib</outputDirectory>
+      <useProjectArtifact>true</useProjectArtifact>
+      <unpack>false</unpack>
+      <scope>test</scope>
+      <includes>
+        <include>*:cdi-api</include>
+        <include>*:weld-servlet</include>
+      </includes>
+    </dependencySet>
+  </dependencySets>
+</assembly>
diff --git a/tests/test-cdi/cdi-webapp/src/main/java/org/eclipse/jetty/tests/HelloServlet.java b/jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/HelloServlet.java
similarity index 100%
rename from tests/test-cdi/cdi-webapp/src/main/java/org/eclipse/jetty/tests/HelloServlet.java
rename to jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/HelloServlet.java
diff --git a/tests/test-cdi/cdi-webapp/src/main/java/org/eclipse/jetty/tests/ServerInfoServlet.java b/jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/ServerInfoServlet.java
similarity index 100%
rename from tests/test-cdi/cdi-webapp/src/main/java/org/eclipse/jetty/tests/ServerInfoServlet.java
rename to jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/ServerInfoServlet.java
diff --git a/jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/logging/JULog.java b/jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/logging/JULog.java
new file mode 100644
index 0000000..222465a
--- /dev/null
+++ b/jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/logging/JULog.java
@@ -0,0 +1,47 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.tests.logging;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class JULog
+{
+    private final Logger log;
+
+    public JULog(Class<?> clazz)
+    {
+        this.log = Logger.getLogger(clazz.getName());
+    }
+
+    public void info(String msg)
+    {
+        log.log(Level.INFO, msg);
+    }
+
+    public void info(String msg, Object ... args)
+    {
+        log.log(Level.INFO, msg, args);
+    }
+
+    public void warn(Throwable t)
+    {
+        log.log(Level.WARNING, "", t);
+    }
+}
diff --git a/jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/logging/JULogFactory.java b/jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/logging/JULogFactory.java
new file mode 100644
index 0000000..61f5d19
--- /dev/null
+++ b/jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/logging/JULogFactory.java
@@ -0,0 +1,31 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.tests.logging;
+
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.InjectionPoint;
+
+public class JULogFactory
+{
+    @Produces
+    public JULog createJULog(InjectionPoint injectionPoint)
+    {
+        return new JULog(injectionPoint.getMember().getDeclaringClass());
+    }
+}
diff --git a/jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/ws/SessionInfoSocket.java b/jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/ws/SessionInfoSocket.java
new file mode 100644
index 0000000..80113db
--- /dev/null
+++ b/jetty-cdi/test-cdi-webapp/src/main/java/org/eclipse/jetty/tests/ws/SessionInfoSocket.java
@@ -0,0 +1,98 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.tests.ws;
+
+import javax.inject.Inject;
+import javax.servlet.http.HttpSession;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
+import javax.websocket.RemoteEndpoint;
+import javax.websocket.Session;
+import javax.websocket.server.ServerEndpoint;
+
+import org.eclipse.jetty.tests.logging.JULog;
+
+@ServerEndpoint(value = "/sessioninfo")
+public class SessionInfoSocket
+{
+    @Inject
+    private JULog LOG;
+
+    @Inject
+    private HttpSession httpSession;
+
+    private Session wsSession;
+
+    @OnOpen
+    public void onOpen(Session session)
+    {
+        LOG.info("onOpen({0})",asClassId(session));
+        this.wsSession = session;
+    }
+
+    @OnMessage
+    public void onMessage(String message)
+    {
+        LOG.info("onMessage({0})",quoted(message));
+        
+        try
+        {
+            RemoteEndpoint.Basic remote = wsSession.getBasicRemote();
+            LOG.info("Remote.Basic: {0}", remote);
+            
+            if ("info".equalsIgnoreCase(message))
+            {
+                LOG.info("returning 'info' details");
+                remote.sendText("HttpSession = " + httpSession);
+            }
+            else if ("close".equalsIgnoreCase(message))
+            {
+                LOG.info("closing session");
+                wsSession.close();
+            }
+            else
+            {
+                LOG.info("echoing message as-is");
+                remote.sendText(message);
+            }
+        }
+        catch (Throwable t)
+        {
+            LOG.warn(t);
+        }
+    }
+
+    private String asClassId(Object obj)
+    {
+        if (obj == null)
+        {
+            return "<null>";
+        }
+        return String.format("%s@%X",obj.getClass().getName(),obj.hashCode());
+    }
+
+    private String quoted(String str)
+    {
+        if (str == null)
+        {
+            return "<null>";
+        }
+        return '"' + str + '"';
+    }
+}
diff --git a/tests/test-cdi/cdi-webapp/src/main/webapp/WEB-INF/beans.xml b/jetty-cdi/test-cdi-webapp/src/main/webapp/WEB-INF/beans.xml
similarity index 100%
rename from tests/test-cdi/cdi-webapp/src/main/webapp/WEB-INF/beans.xml
rename to jetty-cdi/test-cdi-webapp/src/main/webapp/WEB-INF/beans.xml
diff --git a/tests/test-cdi/cdi-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-cdi/test-cdi-webapp/src/main/webapp/WEB-INF/web.xml
similarity index 100%
rename from tests/test-cdi/cdi-webapp/src/main/webapp/WEB-INF/web.xml
rename to jetty-cdi/test-cdi-webapp/src/main/webapp/WEB-INF/web.xml
diff --git a/jetty-client/pom.xml b/jetty-client/pom.xml
index b8991e2..57002cc 100644
--- a/jetty-client/pom.xml
+++ b/jetty-client/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -16,50 +16,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.net.*,*</Import-Package>
-              </instructions>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <!-- Required for OSGI -->
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java
index c403652..b812abe 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java
@@ -19,21 +19,27 @@
 package org.eclipse.jetty.client;
 
 import java.io.IOException;
+import java.net.InetSocketAddress;
 import java.net.SocketAddress;
+import java.net.SocketException;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.SocketChannel;
 import java.util.Map;
 
 import org.eclipse.jetty.client.api.Connection;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ManagedSelector;
 import org.eclipse.jetty.io.SelectChannelEndPoint;
 import org.eclipse.jetty.io.SelectorManager;
 import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
 import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
+@ManagedObject
 public abstract class AbstractHttpClientTransport extends ContainerLifeCycle implements HttpClientTransport
 {
     protected static final Logger LOG = Log.getLogger(HttpClientTransport.class);
@@ -58,6 +64,12 @@
         this.client = client;
     }
 
+    @ManagedAttribute(value = "The number of selectors", readonly = true)
+    public int getSelectors()
+    {
+        return selectors;
+    }
+
     @Override
     protected void doStart() throws Exception
     {
@@ -75,7 +87,7 @@
     }
 
     @Override
-    public void connect(SocketAddress address, Map<String, Object> context)
+    public void connect(InetSocketAddress address, Map<String, Object> context)
     {
         SocketChannel channel = null;
         try
@@ -110,6 +122,11 @@
         // UnresolvedAddressException are not IOExceptions.
         catch (Throwable x)
         {
+            // If IPv6 is not deployed, a generic SocketException "Network is unreachable"
+            // exception is being thrown, so we attempt to provide a better error message.
+            if (x.getClass() == SocketException.class)
+                x = new SocketException("Could not connect to " + address).initCause(x);
+
             try
             {
                 if (channel != null)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java
index b5c646c..3fe68cc 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java
@@ -32,17 +32,16 @@
 import org.eclipse.jetty.client.api.Response;
 import org.eclipse.jetty.client.api.Result;
 import org.eclipse.jetty.client.util.BufferingResponseListener;
+import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
 public abstract class AuthenticationProtocolHandler implements ProtocolHandler
 {
-    public static final int DEFAULT_MAX_CONTENT_LENGTH = 4096;
+    public static final int DEFAULT_MAX_CONTENT_LENGTH = 16*1024;
     public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class);
-    private static final Pattern AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\"(.*)", Pattern.CASE_INSENSITIVE);
-    private static final String AUTHENTICATION_ATTRIBUTE = AuthenticationProtocolHandler.class.getName() + ".authentication";
+    private static final Pattern AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]*)\"(.*)", Pattern.CASE_INSENSITIVE);
 
     private final HttpClient client;
     private final int maxContentLength;
@@ -66,6 +65,8 @@
 
     protected abstract URI getAuthenticationURI(Request request);
 
+    protected abstract String getAuthenticationAttribute();
+
     @Override
     public Response.Listener getResponseListener()
     {
@@ -87,15 +88,15 @@
             ContentResponse response = new HttpContentResponse(result.getResponse(), getContent(), getMediaType(), getEncoding());
             if (result.isFailed())
             {
-                Throwable failure = result.getFailure();
                 if (LOG.isDebugEnabled())
-                    LOG.debug("Authentication challenge failed {}", failure);
+                    LOG.debug("Authentication challenge failed {}", result.getFailure());
                 forwardFailureComplete(request, result.getRequestFailure(), response, result.getResponseFailure());
                 return;
             }
 
+            String authenticationAttribute = getAuthenticationAttribute();
             HttpConversation conversation = request.getConversation();
-            if (conversation.getAttribute(AUTHENTICATION_ATTRIBUTE) != null)
+            if (conversation.getAttribute(authenticationAttribute) != null)
             {
                 // We have already tried to authenticate, but we failed again
                 if (LOG.isDebugEnabled())
@@ -116,7 +117,7 @@
 
             Authentication authentication = null;
             Authentication.HeaderInfo headerInfo = null;
-            URI authURI = getAuthenticationURI(request);
+            URI authURI = resolveURI(request, getAuthenticationURI(request));
             if (authURI != null)
             {
                 for (Authentication.HeaderInfo element : headerInfos)
@@ -148,17 +149,13 @@
                     return;
                 }
 
-                conversation.setAttribute(AUTHENTICATION_ATTRIBUTE, true);
+                conversation.setAttribute(authenticationAttribute, true);
 
                 URI requestURI = request.getURI();
                 String path = null;
-                if (HttpMethod.CONNECT.is(request.getMethod()))
+                if (requestURI == null)
                 {
-                    String uri = request.getScheme() + "://" + request.getHost();
-                    int port = request.getPort();
-                    if (port > 0)
-                        uri += ":" + port;
-                    requestURI = URI.create(uri);
+                    requestURI = resolveURI(request, null);
                     path = request.getPath();
                 }
                 Request newRequest = client.copyRequest(request, requestURI);
@@ -166,15 +163,11 @@
                     newRequest.path(path);
 
                 authnResult.apply(newRequest);
+                // Copy existing, explicitly set, authorization headers.
+                copyIfAbsent(request, newRequest, HttpHeader.AUTHORIZATION);
+                copyIfAbsent(request, newRequest, HttpHeader.PROXY_AUTHORIZATION);
 
-                newRequest.onResponseSuccess(new Response.SuccessListener()
-                {
-                    @Override
-                    public void onSuccess(Response response)
-                    {
-                        client.getAuthenticationStore().addAuthenticationResult(authnResult);
-                    }
-                });
+                newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult));
 
                 Connection connection = (Connection)request.getAttributes().get(Connection.class.getName());
                 if (connection != null)
@@ -190,6 +183,24 @@
             }
         }
 
+        private URI resolveURI(HttpRequest request, URI uri)
+        {
+            if (uri != null)
+                return uri;
+            String target = request.getScheme() + "://" + request.getHost();
+            int port = request.getPort();
+            if (port > 0)
+                target += ":" + port;
+            return URI.create(target);
+        }
+
+        private void copyIfAbsent(HttpRequest oldRequest, Request newRequest, HttpHeader header)
+        {
+            HttpField field = oldRequest.getHeaders().getField(header);
+            if (field != null && !newRequest.getHeaders().contains(header))
+                newRequest.getHeaders().put(field);
+        }
+
         private void forwardSuccessComplete(HttpRequest request, Response response)
         {
             HttpConversation conversation = request.getConversation();
@@ -201,7 +212,12 @@
         {
             HttpConversation conversation = request.getConversation();
             conversation.updateResponseListeners(null);
-            notifier.forwardFailureComplete(conversation.getResponseListeners(), request, requestFailure, response, responseFailure);
+            List<Response.ResponseListener> responseListeners = conversation.getResponseListeners();
+            if (responseFailure == null)
+                notifier.forwardSuccess(responseListeners, response);
+            else
+                notifier.forwardFailure(responseListeners, response, responseFailure);
+            notifier.notifyComplete(responseListeners, new Result(request, requestFailure, response, responseFailure));
         }
 
         private List<Authentication.HeaderInfo> parseAuthenticateHeader(Response response, HttpHeader header)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java
index b5a3dcd..a1df687 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java
@@ -18,393 +18,17 @@
 
 package org.eclipse.jetty.client;
 
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.BlockingDeque;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.ReentrantLock;
-
-import org.eclipse.jetty.client.api.Connection;
 import org.eclipse.jetty.client.api.Destination;
-import org.eclipse.jetty.util.BlockingArrayQueue;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.component.ContainerLifeCycle;
-import org.eclipse.jetty.util.component.Dumpable;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.thread.Sweeper;
+import org.eclipse.jetty.util.Callback;
 
-public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
+/**
+ * @deprecated use {@link DuplexConnectionPool} instead
+ */
+@Deprecated
+public class ConnectionPool extends DuplexConnectionPool
 {
-    protected static final Logger LOG = Log.getLogger(ConnectionPool.class);
-
-    private final AtomicInteger connectionCount = new AtomicInteger();
-    private final ReentrantLock lock = new ReentrantLock();
-    private final Destination destination;
-    private final int maxConnections;
-    private final Promise<Connection> requester;
-    private final BlockingDeque<Connection> idleConnections;
-    private final BlockingQueue<Connection> activeConnections;
-
-    public ConnectionPool(Destination destination, int maxConnections, Promise<Connection> requester)
+    public ConnectionPool(Destination destination, int maxConnections, Callback requester)
     {
-        this.destination = destination;
-        this.maxConnections = maxConnections;
-        this.requester = requester;
-        this.idleConnections = new LinkedBlockingDeque<>(maxConnections);
-        this.activeConnections = new BlockingArrayQueue<>(maxConnections);
-    }
-
-    public int getConnectionCount()
-    {
-        return connectionCount.get();
-    }
-
-    public BlockingQueue<Connection> getIdleConnections()
-    {
-        return idleConnections;
-    }
-
-    public BlockingQueue<Connection> getActiveConnections()
-    {
-        return activeConnections;
-    }
-
-    public Connection acquire()
-    {
-        Connection connection = activateIdle();
-        if (connection == null)
-            connection = tryCreate();
-        return connection;
-    }
-
-    private Connection tryCreate()
-    {
-        while (true)
-        {
-            int current = getConnectionCount();
-            final int next = current + 1;
-
-            if (next > maxConnections)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Max connections {}/{} reached", current, maxConnections);
-                // Try again the idle connections
-                return activateIdle();
-            }
-
-            if (connectionCount.compareAndSet(current, next))
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Connection {}/{} creation", next, maxConnections);
-
-                destination.newConnection(new Promise<Connection>()
-                {
-                    @Override
-                    public void succeeded(Connection connection)
-                    {
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("Connection {}/{} creation succeeded {}", next, maxConnections, connection);
-
-                        idleCreated(connection);
-
-                        requester.succeeded(connection);
-                    }
-
-                    @Override
-                    public void failed(Throwable x)
-                    {
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("Connection " + next + "/" + maxConnections + " creation failed", x);
-
-                        connectionCount.decrementAndGet();
-
-                        requester.failed(x);
-                    }
-                });
-
-                // Try again the idle connections
-                return activateIdle();
-            }
-        }
-    }
-
-    protected void idleCreated(Connection connection)
-    {
-        boolean idle;
-        final ReentrantLock lock = this.lock;
-        lock.lock();
-        try
-        {
-            // Use "cold" new connections as last.
-            idle = idleConnections.offerLast(connection);
-        }
-        finally
-        {
-            lock.unlock();
-        }
-
-        idle(connection, idle);
-    }
-
-    private Connection activateIdle()
-    {
-        boolean acquired;
-        Connection connection;
-        final ReentrantLock lock = this.lock;
-        lock.lock();
-        try
-        {
-            connection = idleConnections.pollFirst();
-            if (connection == null)
-                return null;
-            acquired = activeConnections.offer(connection);
-        }
-        finally
-        {
-            lock.unlock();
-        }
-
-        if (acquired)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Connection active {}", connection);
-            acquired(connection);
-            return connection;
-        }
-        else
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Connection active overflow {}", connection);
-            connection.close();
-            return null;
-        }
-    }
-
-    protected void acquired(Connection connection)
-    {
-    }
-
-    public boolean release(Connection connection)
-    {
-        boolean idle;
-        final ReentrantLock lock = this.lock;
-        lock.lock();
-        try
-        {
-            if (!activeConnections.remove(connection))
-                return false;
-            // Make sure we use "hot" connections first.
-            idle = idleConnections.offerFirst(connection);
-        }
-        finally
-        {
-            lock.unlock();
-        }
-
-        released(connection);
-        return idle(connection, idle);
-    }
-
-    protected boolean idle(Connection connection, boolean idle)
-    {
-        if (idle)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Connection idle {}", connection);
-            return true;
-        }
-        else
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Connection idle overflow {}", connection);
-            connection.close();
-            return false;
-        }
-    }
-
-    protected void released(Connection connection)
-    {
-    }
-
-    public boolean remove(Connection connection)
-    {
-        boolean activeRemoved;
-        boolean idleRemoved;
-        final ReentrantLock lock = this.lock;
-        lock.lock();
-        try
-        {
-            activeRemoved = activeConnections.remove(connection);
-            idleRemoved = idleConnections.remove(connection);
-        }
-        finally
-        {
-            lock.unlock();
-        }
-
-        if (activeRemoved)
-            released(connection);
-        boolean removed = activeRemoved || idleRemoved;
-        if (removed)
-        {
-            int pooled = connectionCount.decrementAndGet();
-            if (LOG.isDebugEnabled())
-                LOG.debug("Connection removed {} - pooled: {}", connection, pooled);
-        }
-        return removed;
-    }
-
-    public boolean isActive(Connection connection)
-    {
-        final ReentrantLock lock = this.lock;
-        lock.lock();
-        try
-        {
-            return activeConnections.contains(connection);
-        }
-        finally
-        {
-            lock.unlock();
-        }
-    }
-
-    public boolean isIdle(Connection connection)
-    {
-        final ReentrantLock lock = this.lock;
-        lock.lock();
-        try
-        {
-            return idleConnections.contains(connection);
-        }
-        finally
-        {
-            lock.unlock();
-        }
-    }
-
-    public boolean isEmpty()
-    {
-        return connectionCount.get() == 0;
-    }
-
-    public void close()
-    {
-        List<Connection> idles = new ArrayList<>();
-        List<Connection> actives = new ArrayList<>();
-        final ReentrantLock lock = this.lock;
-        lock.lock();
-        try
-        {
-            idles.addAll(idleConnections);
-            idleConnections.clear();
-            actives.addAll(activeConnections);
-            activeConnections.clear();
-        }
-        finally
-        {
-            lock.unlock();
-        }
-
-        connectionCount.set(0);
-
-        for (Connection connection : idles)
-            connection.close();
-
-        // A bit drastic, but we cannot wait for all requests to complete
-        for (Connection connection : actives)
-            connection.close();
-    }
-
-    @Override
-    public String dump()
-    {
-        return ContainerLifeCycle.dump(this);
-    }
-
-    @Override
-    public void dump(Appendable out, String indent) throws IOException
-    {
-        List<Connection> actives = new ArrayList<>();
-        List<Connection> idles = new ArrayList<>();
-        final ReentrantLock lock = this.lock;
-        lock.lock();
-        try
-        {
-            actives.addAll(activeConnections);
-            idles.addAll(idleConnections);
-        }
-        finally
-        {
-            lock.unlock();
-        }
-
-        ContainerLifeCycle.dumpObject(out, this);
-        ContainerLifeCycle.dump(out, indent, actives, idles);
-    }
-
-    @Override
-    public boolean sweep()
-    {
-        List<Sweeper.Sweepable> toSweep = new ArrayList<>();
-        final ReentrantLock lock = this.lock;
-        lock.lock();
-        try
-        {
-            for (Connection connection : getActiveConnections())
-            {
-                if (connection instanceof Sweeper.Sweepable)
-                    toSweep.add(((Sweeper.Sweepable)connection));
-            }
-        }
-        finally
-        {
-            lock.unlock();
-        }
-
-        for (Sweeper.Sweepable candidate : toSweep)
-        {
-            if (candidate.sweep())
-            {
-                boolean removed = getActiveConnections().remove(candidate);
-                LOG.warn("Connection swept: {}{}{} from active connections{}{}",
-                        candidate,
-                        System.lineSeparator(),
-                        removed ? "Removed" : "Not removed",
-                        System.lineSeparator(),
-                        dump());
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    public String toString()
-    {
-        int activeSize;
-        int idleSize;
-        final ReentrantLock lock = this.lock;
-        lock.lock();
-        try
-        {
-            activeSize = activeConnections.size();
-            idleSize = idleConnections.size();
-        }
-        finally
-        {
-            lock.unlock();
-        }
-
-        return String.format("%s[c=%d/%d,a=%d,i=%d]",
-                getClass().getSimpleName(),
-                connectionCount.get(),
-                maxConnections,
-                activeSize,
-                idleSize);
+        super(destination, maxConnections, requester);
     }
 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ContentDecoder.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ContentDecoder.java
index c547254..2a3a77e 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ContentDecoder.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ContentDecoder.java
@@ -37,10 +37,10 @@
 
     /**
      * Factory for {@link ContentDecoder}s; subclasses must implement {@link #newContentDecoder()}.
-     * <p />
+     * <p>
      * {@link Factory} have an {@link #getEncoding() encoding}, which is the string used in
      * {@code Accept-Encoding} request header and in {@code Content-Encoding} response headers.
-     * <p />
+     * <p>
      * {@link Factory} instances are configured in {@link HttpClient} via
      * {@link HttpClient#getContentDecoderFactories()}.
      */
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ContinueProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ContinueProtocolHandler.java
index 61fbaef..d815ec6 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ContinueProtocolHandler.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ContinueProtocolHandler.java
@@ -27,20 +27,28 @@
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpHeaderValue;
 
+/**
+ * <p>A protocol handler that handles the 100 response code.</p>
+ */
 public class ContinueProtocolHandler implements ProtocolHandler
 {
+    public static final String NAME = "continue";
     private static final String ATTRIBUTE = ContinueProtocolHandler.class.getName() + ".100continue";
 
-    private final HttpClient client;
     private final ResponseNotifier notifier;
 
-    public ContinueProtocolHandler(HttpClient client)
+    public ContinueProtocolHandler()
     {
-        this.client = client;
         this.notifier = new ResponseNotifier();
     }
 
     @Override
+    public String getName()
+    {
+        return NAME;
+    }
+
+    @Override
     public boolean accept(Request request, Response response)
     {
         boolean expect100 = request.getHeaders().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
@@ -56,6 +64,10 @@
         return new ContinueListener();
     }
 
+    protected void onContinue(Request request)
+    {
+    }
+
     protected class ContinueListener extends BufferingResponseListener
     {
         @Override
@@ -64,7 +76,8 @@
             // Handling of success must be done here and not from onComplete(),
             // since the onComplete() is not invoked because the request is not completed yet.
 
-            HttpConversation conversation = ((HttpRequest)response.getRequest()).getConversation();
+            Request request = response.getRequest();
+            HttpConversation conversation = ((HttpRequest)request).getConversation();
             // Mark the 100 Continue response as handled
             conversation.setAttribute(ATTRIBUTE, Boolean.TRUE);
 
@@ -80,6 +93,7 @@
                     // All good, continue
                     exchange.resetResponse();
                     exchange.proceed(null);
+                    onContinue(request);
                     break;
                 }
                 default:
@@ -90,7 +104,7 @@
                     List<Response.ResponseListener> listeners = exchange.getResponseListeners();
                     HttpContentResponse contentResponse = new HttpContentResponse(response, getContent(), getMediaType(), getEncoding());
                     notifier.forwardSuccess(listeners, contentResponse);
-                    exchange.proceed(new HttpRequestException("Expectation failed", exchange.getRequest()));
+                    exchange.proceed(new HttpRequestException("Expectation failed", request));
                     break;
                 }
             }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/DuplexConnectionPool.java b/jetty-client/src/main/java/org/eclipse/jetty/client/DuplexConnectionPool.java
new file mode 100644
index 0000000..dabdacc
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/DuplexConnectionPool.java
@@ -0,0 +1,458 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.client.api.Destination;
+import org.eclipse.jetty.util.BlockingArrayQueue;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Sweeper;
+
+@ManagedObject("The connection pool")
+public class DuplexConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
+{
+    private static final Logger LOG = Log.getLogger(DuplexConnectionPool.class);
+
+    private final AtomicInteger connectionCount = new AtomicInteger();
+    private final ReentrantLock lock = new ReentrantLock();
+    private final Destination destination;
+    private final int maxConnections;
+    private final Callback requester;
+    private final Deque<Connection> idleConnections;
+    private final Queue<Connection> activeConnections;
+
+    public DuplexConnectionPool(Destination destination, int maxConnections, Callback requester)
+    {
+        this.destination = destination;
+        this.maxConnections = maxConnections;
+        this.requester = requester;
+        this.idleConnections = new LinkedBlockingDeque<>(maxConnections);
+        this.activeConnections = new BlockingArrayQueue<>(maxConnections);
+    }
+
+    @ManagedAttribute(value = "The number of connections", readonly = true)
+    public int getConnectionCount()
+    {
+        return connectionCount.get();
+    }
+
+    @ManagedAttribute(value = "The number of idle connections", readonly = true)
+    public int getIdleConnectionCount()
+    {
+        lock();
+        try
+        {
+            return idleConnections.size();
+        }
+        finally
+        {
+            unlock();
+        }
+    }
+
+    @ManagedAttribute(value = "The number of active connections", readonly = true)
+    public int getActiveConnectionCount()
+    {
+        lock();
+        try
+        {
+            return activeConnections.size();
+        }
+        finally
+        {
+            unlock();
+        }
+    }
+
+    public Queue<Connection> getIdleConnections()
+    {
+        return idleConnections;
+    }
+
+    public Queue<Connection> getActiveConnections()
+    {
+        return activeConnections;
+    }
+
+    public Connection acquire()
+    {
+        Connection connection = activateIdle();
+        if (connection == null)
+            connection = tryCreate();
+        return connection;
+    }
+
+    private Connection tryCreate()
+    {
+        while (true)
+        {
+            int current = getConnectionCount();
+            final int next = current + 1;
+
+            if (next > maxConnections)
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Max connections {}/{} reached", current, maxConnections);
+                // Try again the idle connections
+                return activateIdle();
+            }
+
+            if (connectionCount.compareAndSet(current, next))
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Connection {}/{} creation", next, maxConnections);
+
+                destination.newConnection(new Promise<Connection>()
+                {
+                    @Override
+                    public void succeeded(Connection connection)
+                    {
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("Connection {}/{} creation succeeded {}", next, maxConnections, connection);
+
+                        idleCreated(connection);
+
+                        proceed();
+                    }
+
+                    @Override
+                    public void failed(Throwable x)
+                    {
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("Connection " + next + "/" + maxConnections + " creation failed", x);
+
+                        connectionCount.decrementAndGet();
+
+                        requester.failed(x);
+                    }
+                });
+
+                // Try again the idle connections
+                return activateIdle();
+            }
+        }
+    }
+
+    protected void proceed()
+    {
+        requester.succeeded();
+    }
+
+    protected void idleCreated(Connection connection)
+    {
+        boolean idle;
+        lock();
+        try
+        {
+            // Use "cold" new connections as last.
+            idle = idleConnections.offerLast(connection);
+        }
+        finally
+        {
+            unlock();
+        }
+
+        idle(connection, idle);
+    }
+
+    private Connection activateIdle()
+    {
+        boolean acquired;
+        Connection connection;
+        lock();
+        try
+        {
+            connection = idleConnections.pollFirst();
+            if (connection == null)
+                return null;
+            acquired = activeConnections.offer(connection);
+        }
+        finally
+        {
+            unlock();
+        }
+
+        if (acquired)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Connection active {}", connection);
+            acquired(connection);
+            return connection;
+        }
+        else
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Connection active overflow {}", connection);
+            connection.close();
+            return null;
+        }
+    }
+
+    protected void acquired(Connection connection)
+    {
+    }
+
+    public boolean release(Connection connection)
+    {
+        boolean idle;
+        lock();
+        try
+        {
+            if (!activeConnections.remove(connection))
+                return false;
+            // Make sure we use "hot" connections first.
+            idle = offerIdle(connection);
+        }
+        finally
+        {
+            unlock();
+        }
+
+        released(connection);
+        return idle(connection, idle);
+    }
+
+    protected boolean offerIdle(Connection connection)
+    {
+        return idleConnections.offerFirst(connection);
+    }
+
+    protected boolean idle(Connection connection, boolean idle)
+    {
+        if (idle)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Connection idle {}", connection);
+            return true;
+        }
+        else
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Connection idle overflow {}", connection);
+            connection.close();
+            return false;
+        }
+    }
+
+    protected void released(Connection connection)
+    {
+    }
+
+    public boolean remove(Connection connection)
+    {
+        return remove(connection, false);
+    }
+
+    protected boolean remove(Connection connection, boolean force)
+    {
+        boolean activeRemoved;
+        boolean idleRemoved;
+        lock();
+        try
+        {
+            activeRemoved = activeConnections.remove(connection);
+            idleRemoved = idleConnections.remove(connection);
+        }
+        finally
+        {
+            unlock();
+        }
+
+        if (activeRemoved || force)
+            released(connection);
+        boolean removed = activeRemoved || idleRemoved || force;
+        if (removed)
+        {
+            int pooled = connectionCount.decrementAndGet();
+            if (LOG.isDebugEnabled())
+                LOG.debug("Connection removed {} - pooled: {}", connection, pooled);
+        }
+        return removed;
+    }
+
+    public boolean isActive(Connection connection)
+    {
+        lock();
+        try
+        {
+            return activeConnections.contains(connection);
+        }
+        finally
+        {
+            unlock();
+        }
+    }
+
+    public boolean isIdle(Connection connection)
+    {
+        lock();
+        try
+        {
+            return idleConnections.contains(connection);
+        }
+        finally
+        {
+            unlock();
+        }
+    }
+
+    public boolean isEmpty()
+    {
+        return connectionCount.get() == 0;
+    }
+
+    public void close()
+    {
+        List<Connection> idles = new ArrayList<>();
+        List<Connection> actives = new ArrayList<>();
+        lock();
+        try
+        {
+            idles.addAll(idleConnections);
+            idleConnections.clear();
+            actives.addAll(activeConnections);
+            activeConnections.clear();
+        }
+        finally
+        {
+            unlock();
+        }
+
+        connectionCount.set(0);
+
+        for (Connection connection : idles)
+            connection.close();
+
+        // A bit drastic, but we cannot wait for all requests to complete
+        for (Connection connection : actives)
+            connection.close();
+    }
+
+    @Override
+    public String dump()
+    {
+        return ContainerLifeCycle.dump(this);
+    }
+
+    @Override
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        List<Connection> actives = new ArrayList<>();
+        List<Connection> idles = new ArrayList<>();
+        lock();
+        try
+        {
+            actives.addAll(activeConnections);
+            idles.addAll(idleConnections);
+        }
+        finally
+        {
+            unlock();
+        }
+
+        ContainerLifeCycle.dumpObject(out, this);
+        ContainerLifeCycle.dump(out, indent, actives, idles);
+    }
+
+    @Override
+    public boolean sweep()
+    {
+        List<Connection> toSweep = new ArrayList<>();
+        lock();
+        try
+        {
+            for (Connection connection : activeConnections)
+            {
+                if (connection instanceof Sweeper.Sweepable)
+                    toSweep.add(connection);
+            }
+        }
+        finally
+        {
+            unlock();
+        }
+
+        for (Connection connection : toSweep)
+        {
+            if (((Sweeper.Sweepable)connection).sweep())
+            {
+                boolean removed = remove(connection, true);
+                LOG.warn("Connection swept: {}{}{} from active connections{}{}",
+                        connection,
+                        System.lineSeparator(),
+                        removed ? "Removed" : "Not removed",
+                        System.lineSeparator(),
+                        dump());
+            }
+        }
+
+        return false;
+    }
+
+    protected void lock()
+    {
+        lock.lock();
+    }
+
+    protected void unlock()
+    {
+        lock.unlock();
+    }
+
+    @Override
+    public String toString()
+    {
+        int activeSize;
+        int idleSize;
+        lock();
+        try
+        {
+            activeSize = activeConnections.size();
+            idleSize = idleConnections.size();
+        }
+        finally
+        {
+            unlock();
+        }
+
+        return String.format("%s[c=%d/%d,a=%d,i=%d]",
+                getClass().getSimpleName(),
+                connectionCount.get(),
+                maxConnections,
+                activeSize,
+                idleSize);
+    }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
index 15d16bd..102e0ab 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
@@ -18,10 +18,10 @@
 
 package org.eclipse.jetty.client;
 
-import java.io.IOException;
 import java.net.CookieManager;
 import java.net.CookiePolicy;
 import java.net.CookieStore;
+import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.SocketAddress;
 import java.net.URI;
@@ -57,11 +57,14 @@
 import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpScheme;
 import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.ClientConnectionFactory;
 import org.eclipse.jetty.io.MappedByteBufferPool;
 import org.eclipse.jetty.util.Fields;
 import org.eclipse.jetty.util.Jetty;
 import org.eclipse.jetty.util.Promise;
 import org.eclipse.jetty.util.SocketAddressResolver;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -106,12 +109,13 @@
  * });
  * </pre>
  */
+@ManagedObject("The HTTP client")
 public class HttpClient extends ContainerLifeCycle
 {
     private static final Logger LOG = Log.getLogger(HttpClient.class);
 
     private final ConcurrentMap<Origin, HttpDestination> destinations = new ConcurrentHashMap<>();
-    private final List<ProtocolHandler> handlers = new ArrayList<>();
+    private final ProtocolHandlers handlers = new ProtocolHandlers();
     private final List<Request.Listener> requestListeners = new ArrayList<>();
     private final AuthenticationStore authenticationStore = new HttpAuthenticationStore();
     private final Set<ContentDecoder.Factory> decoderFactories = new ContentDecoderFactorySet();
@@ -136,7 +140,6 @@
     private volatile long addressResolutionTimeout = 15000;
     private volatile long idleTimeout;
     private volatile boolean tcpNoDelay = true;
-    private volatile boolean dispatchIO = true;
     private volatile boolean strictEventOrdering = false;
     private volatile HttpField encodingField;
     private volatile boolean removeIdleDestinations = false;
@@ -216,10 +219,10 @@
             resolver = new SocketAddressResolver.Async(executor, scheduler, getAddressResolutionTimeout());
         addBean(resolver);
 
-        handlers.add(new ContinueProtocolHandler(this));
-        handlers.add(new RedirectProtocolHandler(this));
-        handlers.add(new WWWAuthenticationProtocolHandler(this));
-        handlers.add(new ProxyAuthenticationProtocolHandler(this));
+        handlers.put(new ContinueProtocolHandler());
+        handlers.put(new RedirectProtocolHandler(this));
+        handlers.put(new WWWAuthenticationProtocolHandler(this));
+        handlers.put(new ProxyAuthenticationProtocolHandler(this));
 
         decoderFactories.add(new GZIPContentDecoder.Factory());
 
@@ -237,7 +240,6 @@
     @Override
     protected void doStop() throws Exception
     {
-        cookieStore.removeAll();
         decoderFactories.clear();
         handlers.clear();
 
@@ -315,6 +317,9 @@
      *
      * @param uri the URI to GET
      * @return the {@link ContentResponse} for the request
+     * @throws InterruptedException if send threading has been interrupted
+     * @throws ExecutionException the execution failed
+     * @throws TimeoutException the send timed out
      * @see #GET(URI)
      */
     public ContentResponse GET(String uri) throws InterruptedException, ExecutionException, TimeoutException
@@ -327,6 +332,9 @@
      *
      * @param uri the URI to GET
      * @return the {@link ContentResponse} for the request
+     * @throws InterruptedException if send threading has been interrupted
+     * @throws ExecutionException the execution failed
+     * @throws TimeoutException the send timed out
      * @see #newRequest(URI)
      */
     public ContentResponse GET(URI uri) throws InterruptedException, ExecutionException, TimeoutException
@@ -340,6 +348,9 @@
      * @param uri the URI to POST
      * @param fields the fields composing the form name/value pairs
      * @return the {@link ContentResponse} for the request
+     * @throws InterruptedException if send threading has been interrupted
+     * @throws ExecutionException the execution failed
+     * @throws TimeoutException the send timed out
      */
     public ContentResponse FORM(String uri, Fields fields) throws InterruptedException, ExecutionException, TimeoutException
     {
@@ -352,6 +363,9 @@
      * @param uri the URI to POST
      * @param fields the fields composing the form name/value pairs
      * @return the {@link ContentResponse} for the request
+     * @throws InterruptedException if send threading has been interrupted
+     * @throws ExecutionException the execution failed
+     * @throws TimeoutException the send timed out
      */
     public ContentResponse FORM(URI uri, Fields fields) throws InterruptedException, ExecutionException, TimeoutException
     {
@@ -394,9 +408,9 @@
     }
 
     /**
-     * Creates a new request with the specified URI.
+     * Creates a new request with the specified absolute URI in string format.
      *
-     * @param uri the URI to request
+     * @param uri the request absolute URI
      * @return the request just created
      */
     public Request newRequest(String uri)
@@ -405,9 +419,9 @@
     }
 
     /**
-     * Creates a new request with the specified URI.
+     * Creates a new request with the specified absolute URI.
      *
-     * @param uri the URI to request
+     * @param uri the request absolute URI
      * @return the request just created
      */
     public Request newRequest(URI uri)
@@ -444,16 +458,33 @@
                     HttpHeader.PROXY_AUTHORIZATION == header)
                 continue;
 
+            String name = field.getName();
             String value = field.getValue();
-            if (!newRequest.getHeaders().contains(header, value))
-                newRequest.header(field.getName(), value);
+            if (!newRequest.getHeaders().contains(name, value))
+                newRequest.header(name, value);
         }
         return newRequest;
     }
 
     protected HttpRequest newHttpRequest(HttpConversation conversation, URI uri)
     {
-        return new HttpRequest(this, conversation, uri);
+        return new HttpRequest(this, conversation, checkHost(uri));
+    }
+
+    /**
+     * <p>Checks {@code uri} for the host to be non-null host.</p>
+     * <p>URIs built from strings that have an internationalized domain name (IDN)
+     * are parsed without errors, but {@code uri.getHost()} returns null.</p>
+     *
+     * @param uri the URI to check for non-null host
+     * @return the same {@code uri} if the host is non-null
+     * @throws IllegalArgumentException if the host is null
+     */
+    private URI checkHost(URI uri)
+    {
+        if (uri.getHost() == null)
+            throw new IllegalArgumentException(String.format("Invalid URI host: null (authority: %s)", uri.getRawAuthority()));
+        return uri;
     }
 
     /**
@@ -476,6 +507,11 @@
 
     protected HttpDestination destinationFor(String scheme, String host, int port)
     {
+        if (!HttpScheme.HTTP.is(scheme) && !HttpScheme.HTTPS.is(scheme))
+            throw new IllegalArgumentException("Invalid protocol " + scheme);
+
+        scheme = scheme.toLowerCase(Locale.ENGLISH);
+        host = host.toLowerCase(Locale.ENGLISH);
         port = normalizePort(scheme, port);
 
         Origin origin = new Origin(scheme, host, port);
@@ -483,28 +519,25 @@
         if (destination == null)
         {
             destination = transport.newHttpDestination(origin);
-            if (isRunning())
+            addManaged(destination);
+            HttpDestination existing = destinations.putIfAbsent(origin, destination);
+            if (existing != null)
             {
-                HttpDestination existing = destinations.putIfAbsent(origin, destination);
-                if (existing != null)
-                {
-                    destination = existing;
-                }
-                else
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Created {}", destination);
-                }
-                if (!isRunning())
-                    destinations.remove(origin);
+                removeBean(destination);
+                destination = existing;
             }
-
+            else
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Created {}", destination);
+            }
         }
         return destination;
     }
 
     protected boolean removeDestination(HttpDestination destination)
     {
+        removeBean(destination);
         return destinations.remove(destination.getOrigin()) != null;
     }
 
@@ -513,32 +546,27 @@
      */
     public List<Destination> getDestinations()
     {
-        return new ArrayList<Destination>(destinations.values());
+        return new ArrayList<>(destinations.values());
     }
 
     protected void send(final HttpRequest request, List<Response.ResponseListener> listeners)
     {
-        String scheme = request.getScheme().toLowerCase(Locale.ENGLISH);
-        if (!HttpScheme.HTTP.is(scheme) && !HttpScheme.HTTPS.is(scheme))
-            throw new IllegalArgumentException("Invalid protocol " + scheme);
-
-        String host = request.getHost().toLowerCase(Locale.ENGLISH);
-        HttpDestination destination = destinationFor(scheme, host, request.getPort());
+        HttpDestination destination = destinationFor(request.getScheme(), request.getHost(), request.getPort());
         destination.send(request, listeners);
     }
 
     protected void newConnection(final HttpDestination destination, final Promise<Connection> promise)
     {
         Origin.Address address = destination.getConnectAddress();
-        resolver.resolve(address.getHost(), address.getPort(), new Promise<SocketAddress>()
+        resolver.resolve(address.getHost(), address.getPort(), new Promise<List<InetSocketAddress>>()
         {
             @Override
-            public void succeeded(SocketAddress socketAddress)
+            public void succeeded(List<InetSocketAddress> socketAddresses)
             {
                 Map<String, Object> context = new HashMap<>();
+                context.put(ClientConnectionFactory.CONNECTOR_CONTEXT_KEY, HttpClient.this);
                 context.put(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY, destination);
-                context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, promise);
-                transport.connect(socketAddress, context);
+                connect(socketAddresses, 0, context);
             }
 
             @Override
@@ -546,6 +574,23 @@
             {
                 promise.failed(x);
             }
+
+            private void connect(List<InetSocketAddress> socketAddresses, int index, Map<String, Object> context)
+            {
+                context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, new Promise.Wrapper<Connection>(promise)
+                {
+                    @Override
+                    public void failed(Throwable x)
+                    {
+                        int nextIndex = index + 1;
+                        if (nextIndex == socketAddresses.size())
+                            super.failed(x);
+                        else
+                            connect(socketAddresses, nextIndex, context);
+                    }
+                });
+                transport.connect(socketAddresses.get(index), context);
+            }
         });
     }
 
@@ -554,22 +599,14 @@
         return new HttpConversation();
     }
 
-    public List<ProtocolHandler> getProtocolHandlers()
+    public ProtocolHandlers getProtocolHandlers()
     {
         return handlers;
     }
 
     protected ProtocolHandler findProtocolHandler(Request request, Response response)
     {
-        // Optimized to avoid allocations of iterator instances
-        List<ProtocolHandler> protocolHandlers = getProtocolHandlers();
-        for (int i = 0; i < protocolHandlers.size(); ++i)
-        {
-            ProtocolHandler handler = protocolHandlers.get(i);
-            if (handler.accept(request, response))
-                return handler;
-        }
-        return null;
+        return handlers.find(request, response);
     }
 
     /**
@@ -591,6 +628,7 @@
     /**
      * @return the max time, in milliseconds, a connection can take to connect to destinations
      */
+    @ManagedAttribute("The timeout, in milliseconds, for connect() operations")
     public long getConnectTimeout()
     {
         return connectTimeout;
@@ -631,6 +669,7 @@
     /**
      * @return the max time, in milliseconds, a connection can be idle (that is, without traffic of bytes in either direction)
      */
+    @ManagedAttribute("The timeout, in milliseconds, to close idle connections")
     public long getIdleTimeout()
     {
         return idleTimeout;
@@ -685,6 +724,7 @@
      * @return whether this {@link HttpClient} follows HTTP redirects
      * @see Request#isFollowRedirects()
      */
+    @ManagedAttribute("Whether HTTP redirects are followed")
     public boolean isFollowRedirects()
     {
         return followRedirects;
@@ -750,6 +790,7 @@
     /**
      * @return the max number of connections that this {@link HttpClient} opens to {@link Destination}s
      */
+    @ManagedAttribute("The max number of connections per each destination")
     public int getMaxConnectionsPerDestination()
     {
         return maxConnectionsPerDestination;
@@ -757,7 +798,7 @@
 
     /**
      * Sets the max number of connections to open to each destinations.
-     * <p />
+     * <p>
      * RFC 2616 suggests that 2 connections should be opened per each destination,
      * but browsers commonly open 6.
      * If this {@link HttpClient} is used for load testing, it is common to have only one destination
@@ -774,6 +815,7 @@
     /**
      * @return the max number of requests that may be queued to a {@link Destination}.
      */
+    @ManagedAttribute("The max number of requests queued per each destination")
     public int getMaxRequestsQueuedPerDestination()
     {
         return maxRequestsQueuedPerDestination;
@@ -781,7 +823,7 @@
 
     /**
      * Sets the max number of requests that may be queued to a destination.
-     * <p />
+     * <p>
      * If this {@link HttpClient} performs a high rate of requests to a destination,
      * and all the connections managed by that destination are busy with other requests,
      * then new requests will be queued up in the destination.
@@ -800,6 +842,7 @@
     /**
      * @return the size of the buffer used to write requests
      */
+    @ManagedAttribute("The request buffer size")
     public int getRequestBufferSize()
     {
         return requestBufferSize;
@@ -816,6 +859,7 @@
     /**
      * @return the size of the buffer used to read responses
      */
+    @ManagedAttribute("The response buffer size")
     public int getResponseBufferSize()
     {
         return responseBufferSize;
@@ -850,6 +894,7 @@
     /**
      * @return whether TCP_NODELAY is enabled
      */
+    @ManagedAttribute(value = "Whether the TCP_NODELAY option is enabled", name = "tcpNoDelay")
     public boolean isTCPNoDelay()
     {
         return tcpNoDelay;
@@ -868,14 +913,16 @@
      * @return true to dispatch I/O operations in a different thread, false to execute them in the selector thread
      * @see #setDispatchIO(boolean)
      */
+    @Deprecated
     public boolean isDispatchIO()
     {
-        return dispatchIO;
+        // TODO this did default to true, so usage needs to be evaluated.
+        return false;
     }
 
     /**
      * Whether to dispatch I/O operations from the selector thread to a different thread.
-     * <p />
+     * <p>
      * This implementation never blocks on I/O operation, but invokes application callbacks that may
      * take time to execute or block on other I/O.
      * If application callbacks are known to take time or block on I/O, then parameter {@code dispatchIO}
@@ -886,15 +933,16 @@
      * @param dispatchIO true to dispatch I/O operations in a different thread,
      *                   false to execute them in the selector thread
      */
+    @Deprecated
     public void setDispatchIO(boolean dispatchIO)
     {
-        this.dispatchIO = dispatchIO;
     }
 
     /**
      * @return whether request events must be strictly ordered
      * @see #setStrictEventOrdering(boolean)
      */
+    @ManagedAttribute("Whether request/response events must be strictly ordered")
     public boolean isStrictEventOrdering()
     {
         return strictEventOrdering;
@@ -902,17 +950,17 @@
 
     /**
      * Whether request/response events must be strictly ordered with respect to connection usage.
-     * <p />
+     * <p>
      * From the point of view of connection usage, the connection can be reused just before the
      * "complete" event notified to {@link org.eclipse.jetty.client.api.Response.CompleteListener}s
      * (but after the "success" event).
-     * <p />
+     * <p>
      * When a request/response exchange is completing, the destination may have another request
      * queued to be sent to the server.
      * If the connection for that destination is reused for the second request before the "complete"
      * event of the first exchange, it may happen that the "begin" event of the second request
      * happens before the "complete" event of the first exchange.
-     * <p />
+     * <p>
      * Enforcing strict ordering of events so that a "begin" event of a request can never happen
      * before the "complete" event of the previous exchange comes with the cost of increased
      * connection usage.
@@ -921,7 +969,7 @@
      * when the connection cannot yet be reused.
      * When strict event ordering is not enforced, the redirect request will reuse the already
      * open connection making the system more efficient.
-     * <p />
+     * <p>
      * The default value for this property is {@code false}.
      *
      * @param strictEventOrdering whether request/response events must be strictly ordered
@@ -935,6 +983,7 @@
      * @return whether destinations that have no connections should be removed
      * @see #setRemoveIdleDestinations(boolean)
      */
+    @ManagedAttribute("Whether idle destinations are removed")
     public boolean isRemoveIdleDestinations()
     {
         return removeIdleDestinations;
@@ -942,7 +991,7 @@
 
     /**
      * Whether destinations that have no connections (nor active nor idle) should be removed.
-     * <p />
+     * <p>
      * Applications typically make request to a limited number of destinations so keeping
      * destinations around is not a problem for the memory or the GC.
      * However, for applications that hit millions of different destinations (e.g. a spider
@@ -950,7 +999,7 @@
      * anymore and leave space for new destinations.
      *
      * @param removeIdleDestinations whether destinations that have no connections should be removed
-     * @see org.eclipse.jetty.client.ConnectionPool
+     * @see org.eclipse.jetty.client.DuplexConnectionPool
      */
     public void setRemoveIdleDestinations(boolean removeIdleDestinations)
     {
@@ -960,6 +1009,7 @@
     /**
      * @return whether {@code connect()} operations are performed in blocking mode
      */
+    @ManagedAttribute("Whether the connect() operation is blocking")
     public boolean isConnectBlocking()
     {
         return connectBlocking;
@@ -1005,18 +1055,11 @@
         return port > 0 ? port : HttpScheme.HTTPS.is(scheme) ? 443 : 80;
     }
 
-    protected boolean isDefaultPort(String scheme, int port)
+    public boolean isDefaultPort(String scheme, int port)
     {
         return HttpScheme.HTTPS.is(scheme) ? port == 443 : port == 80;
     }
 
-    @Override
-    public void dump(Appendable out, String indent) throws IOException
-    {
-        dumpThis(out);
-        dump(out, indent, getBeans(), destinations.values());
-    }
-
     private class ContentDecoderFactorySet implements Set<ContentDecoder.Factory>
     {
         private final Set<ContentDecoder.Factory> set = new HashSet<>();
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClientTransport.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClientTransport.java
index 5fd218b..4538253 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClientTransport.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClientTransport.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.client;
 
-import java.net.SocketAddress;
+import java.net.InetSocketAddress;
 import java.util.Map;
 
 import org.eclipse.jetty.io.ClientConnectionFactory;
@@ -26,13 +26,13 @@
 /**
  * {@link HttpClientTransport} represents what transport implementations should provide
  * in order to plug-in a different transport for {@link HttpClient}.
- * <p/>
+ * <p>
  * While the {@link HttpClient} APIs define the HTTP semantic (request, response, headers, etc.)
  * <em>how</em> a HTTP exchange is carried over the network depends on implementations of this class.
- * <p/>
+ * <p>
  * The default implementation uses the HTTP protocol to carry over the network the HTTP exchange,
- * but the HTTP exchange may also be carried using the SPDY protocol or the FCGI protocol or, in future,
- * other protocols.
+ * but the HTTP exchange may also be carried using the FCGI protocol, the HTTP/2 protocol or,
+ * in future, other protocols.
  */
 public interface HttpClientTransport extends ClientConnectionFactory
 {
@@ -41,7 +41,7 @@
 
     /**
      * Sets the {@link HttpClient} instance on this transport.
-     * <p />
+     * <p>
      * This is needed because of a chicken-egg problem: in order to create the {@link HttpClient}
      * a {@link HttpClientTransport} is needed, that therefore cannot have a reference yet to the
      * {@link HttpClient}.
@@ -52,9 +52,9 @@
 
     /**
      * Creates a new, transport-specific, {@link HttpDestination} object.
-     * <p />
+     * <p>
      * {@link HttpDestination} controls the destination-connection cardinality: protocols like
-     * HTTP have 1-N cardinality, while multiplexed protocols like SPDY have a 1-1 cardinality.
+     * HTTP have 1-N cardinality, while multiplexed protocols like HTTP/2 have a 1-1 cardinality.
      *
      * @param origin the destination origin
      * @return a new, transport-specific, {@link HttpDestination} object
@@ -64,8 +64,8 @@
     /**
      * Establishes a physical connection to the given {@code address}.
      *
-     * @param address the address to connect to
+     *  @param address the address to connect to
      * @param context the context information to establish the connection
      */
-    public void connect(SocketAddress address, Map<String, Object> context);
+    public void connect(InetSocketAddress address, Map<String, Object> context);
 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
index 8aeab63..4ad0bc6 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
@@ -23,6 +23,8 @@
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import org.eclipse.jetty.client.api.Authentication;
 import org.eclipse.jetty.client.api.Connection;
@@ -33,18 +35,24 @@
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpHeaderValue;
-import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpScheme;
 import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
 
 public abstract class HttpConnection implements Connection
 {
+    private static final Logger LOG = Log.getLogger(HttpConnection.class);
     private static final HttpField CHUNKED_FIELD = new HttpField(HttpHeader.TRANSFER_ENCODING, HttpHeaderValue.CHUNKED);
 
     private final HttpDestination destination;
+    private int idleTimeoutGuard;
+    private long idleTimeoutStamp;
 
     protected HttpConnection(HttpDestination destination)
     {
         this.destination = destination;
+        this.idleTimeoutStamp = System.nanoTime();
     }
 
     public HttpClient getHttpClient()
@@ -60,26 +68,29 @@
     @Override
     public void send(Request request, Response.CompleteListener listener)
     {
-        ArrayList<Response.ResponseListener> listeners = new ArrayList<>(2);
-        if (request.getTimeout() > 0)
+        HttpRequest httpRequest = (HttpRequest)request;
+
+        ArrayList<Response.ResponseListener> listeners = new ArrayList<>(httpRequest.getResponseListeners());
+        if (httpRequest.getTimeout() > 0)
         {
-            TimeoutCompleteListener timeoutListener = new TimeoutCompleteListener(request);
+            TimeoutCompleteListener timeoutListener = new TimeoutCompleteListener(httpRequest);
             timeoutListener.schedule(getHttpClient().getScheduler());
             listeners.add(timeoutListener);
         }
         if (listener != null)
             listeners.add(listener);
 
-        HttpExchange exchange = new HttpExchange(getHttpDestination(), (HttpRequest)request, listeners);
+        HttpExchange exchange = new HttpExchange(getHttpDestination(), httpRequest, listeners);
 
-        send(exchange);
+        SendFailure result = send(exchange);
+        if (result != null)
+            httpRequest.abort(result.failure);
     }
 
-    protected abstract void send(HttpExchange exchange);
+    protected abstract SendFailure send(HttpExchange exchange);
 
     protected void normalizeRequest(Request request)
     {
-        String method = request.getMethod();
         HttpVersion version = request.getVersion();
         HttpFields headers = request.getHeaders();
         ContentProvider content = request.getContent();
@@ -92,14 +103,17 @@
             path = "/";
             request.path(path);
         }
-        if (proxy != null && !HttpMethod.CONNECT.is(method))
+
+        URI uri = request.getURI();
+
+        if (proxy instanceof HttpProxy && !HttpScheme.HTTPS.is(request.getScheme()) && uri != null)
         {
-            path = request.getURI().toString();
+            path = uri.toString();
             request.path(path);
         }
 
         // If we are HTTP 1.1, add the Host header
-        if (version.getVersion() > 10)
+        if (version.getVersion() <= 11)
         {
             if (!headers.containsKey(HttpHeader.HOST.asString()))
                 headers.put(getHttpDestination().getHostField());
@@ -108,14 +122,15 @@
         // Add content headers
         if (content != null)
         {
-            if (content instanceof ContentProvider.Typed)
+            if (!headers.containsKey(HttpHeader.CONTENT_TYPE.asString()))
             {
-                if (!headers.containsKey(HttpHeader.CONTENT_TYPE.asString()))
-                {
-                    String contentType = ((ContentProvider.Typed)content).getContentType();
-                    if (contentType != null)
-                        headers.put(HttpHeader.CONTENT_TYPE, contentType);
-                }
+                String contentType = null;
+                if (content instanceof ContentProvider.Typed)
+                    contentType = ((ContentProvider.Typed)content).getContentType();
+                if (contentType != null)
+                    headers.put(HttpHeader.CONTENT_TYPE, contentType);
+                else
+                    headers.put(HttpHeader.CONTENT_TYPE, "application/octet-stream");
             }
             long contentLength = content.getLength();
             if (contentLength >= 0)
@@ -123,18 +138,12 @@
                 if (!headers.containsKey(HttpHeader.CONTENT_LENGTH.asString()))
                     headers.put(HttpHeader.CONTENT_LENGTH, String.valueOf(contentLength));
             }
-            else
-            {
-                if (!headers.containsKey(HttpHeader.TRANSFER_ENCODING.asString()))
-                    headers.put(CHUNKED_FIELD);
-            }
         }
 
         // Cookies
         CookieStore cookieStore = getHttpClient().getCookieStore();
         if (cookieStore != null)
         {
-            URI uri = request.getURI();
             StringBuilder cookies = null;
             if (uri != null)
                 cookies = convertCookies(cookieStore.get(uri), null);
@@ -143,14 +152,9 @@
                 request.header(HttpHeader.COOKIE.asString(), cookies.toString());
         }
 
-        // Authorization
-        URI authenticationURI = proxy != null ? proxy.getURI() : request.getURI();
-        if (authenticationURI != null)
-        {
-            Authentication.Result authnResult = getHttpClient().getAuthenticationStore().findAuthenticationResult(authenticationURI);
-            if (authnResult != null)
-                authnResult.apply(request);
-        }
+        // Authentication
+        applyAuthentication(request, proxy != null ? proxy.getURI() : null);
+        applyAuthentication(request, uri);
     }
 
     private StringBuilder convertCookies(List<HttpCookie> cookies, StringBuilder builder)
@@ -167,6 +171,81 @@
         return builder;
     }
 
+    private void applyAuthentication(Request request, URI uri)
+    {
+        if (uri != null)
+        {
+            Authentication.Result result = getHttpClient().getAuthenticationStore().findAuthenticationResult(uri);
+            if (result != null)
+                result.apply(request);
+        }
+    }
+
+    protected SendFailure send(HttpChannel channel, HttpExchange exchange)
+    {
+        // Forbid idle timeouts for the time window where
+        // the request is associated to the channel and sent.
+        // Use a counter to support multiplexed requests.
+        boolean send;
+        synchronized (this)
+        {
+            send = idleTimeoutGuard >= 0;
+            if (send)
+                ++idleTimeoutGuard;
+        }
+
+        if (send)
+        {
+            HttpRequest request = exchange.getRequest();
+            SendFailure result;
+            if (channel.associate(exchange))
+            {
+                channel.send();
+                result = null;
+            }
+            else
+            {
+                channel.release();
+                result = new SendFailure(new HttpRequestException("Could not associate request to connection", request), false);
+            }
+
+            synchronized (this)
+            {
+                --idleTimeoutGuard;
+                idleTimeoutStamp = System.nanoTime();
+            }
+
+            return result;
+        }
+        else
+        {
+            return new SendFailure(new TimeoutException(), true);
+        }
+    }
+
+    public boolean onIdleTimeout(long idleTimeout)
+    {
+        synchronized (this)
+        {
+            if (idleTimeoutGuard == 0)
+            {
+                long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - idleTimeoutStamp);
+                boolean idle = elapsed > idleTimeout / 2;
+                if (idle)
+                    idleTimeoutGuard = -1;
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Idle timeout {}/{}ms - {}", elapsed, idleTimeout, this);
+                return idle;
+            }
+            else
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Idle timeout skipped - {}", this);
+                return false;
+            }
+        }
+    }
+
     @Override
     public String toString()
     {
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java
index 8fee88e..98c2da8 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java
@@ -33,7 +33,7 @@
  * {@link HttpContent} is a stateful, linear representation of the request content provided
  * by a {@link ContentProvider} that can be traversed one-way to obtain content buffers to
  * send to a HTTP server.
- * <p />
+ * <p>
  * {@link HttpContent} offers the notion of a one-way cursor to traverse the content.
  * The cursor starts in a virtual "before" position and can be advanced using {@link #advance()}
  * until it reaches a virtual "after" position where the content is fully consumed.
@@ -42,7 +42,7 @@
  *      |   |  |   |  |   |  |   |  |   |
  *      +---+  +---+  +---+  +---+  +---+
  *   ^           ^                    ^    ^
- *   |           | --> advance()      |    |
+ *   |           | --&gt; advance()      |    |
  *   |           |                  last   |
  *   |           |                         |
  * before        |                        after
@@ -57,7 +57,7 @@
  * </ul>
  * {@link HttpContent} may not have content, if the related {@link ContentProvider} is {@code null}, and this
  * is reflected by {@link #hasContent()}.
- * <p />
+ * <p>
  * {@link HttpContent} may have {@link AsyncContentProvider deferred content}, in which case {@link #advance()}
  * moves the cursor to a position that provides {@code null} {@link #getByteBuffer() buffer} and
  * {@link #getContent() content}. When the deferred content is available, a further call to {@link #advance()}
@@ -67,11 +67,13 @@
 {
     private static final Logger LOG = Log.getLogger(HttpContent.class);
     private static final ByteBuffer AFTER = ByteBuffer.allocate(0);
+    private static final ByteBuffer CLOSE = ByteBuffer.allocate(0);
 
     private final ContentProvider provider;
     private final Iterator<ByteBuffer> iterator;
     private ByteBuffer buffer;
-    private volatile ByteBuffer content;
+    private ByteBuffer content;
+    private boolean last;
 
     public HttpContent(ContentProvider provider)
     {
@@ -92,7 +94,7 @@
      */
     public boolean isLast()
     {
-        return !iterator.hasNext();
+        return last;
     }
 
     /**
@@ -113,52 +115,61 @@
 
     /**
      * Advances the cursor to the next block of content.
-     * <p />
+     * <p>
      * The next block of content may be valid (which yields a non-null buffer
      * returned by {@link #getByteBuffer()}), but may also be deferred
      * (which yields a null buffer returned by {@link #getByteBuffer()}).
-     * <p />
+     * <p>
      * If the block of content pointed by the new cursor position is valid, this method returns true.
      *
      * @return true if there is content at the new cursor's position, false otherwise.
      */
     public boolean advance()
     {
-        boolean advanced;
-        boolean hasNext;
-        ByteBuffer bytes;
         if (iterator instanceof Synchronizable)
         {
             synchronized (((Synchronizable)iterator).getLock())
             {
-                advanced = iterator.hasNext();
-                bytes = advanced ? iterator.next() : null;
-                hasNext = advanced && iterator.hasNext();
+                return advance(iterator);
             }
         }
         else
         {
-            advanced = iterator.hasNext();
-            bytes = advanced ? iterator.next() : null;
-            hasNext = advanced && iterator.hasNext();
+            return advance(iterator);
         }
+    }
 
-        if (advanced)
+    private boolean advance(Iterator<ByteBuffer> iterator)
+    {
+        boolean hasNext = iterator.hasNext();
+        ByteBuffer bytes = hasNext ? iterator.next() : null;
+        boolean hasMore = hasNext && iterator.hasNext();
+        boolean wasLast = last;
+        last = !hasMore;
+
+        if (hasNext)
         {
             buffer = bytes;
             content = bytes == null ? null : bytes.slice();
             if (LOG.isDebugEnabled())
-                LOG.debug("Advanced content to {} chunk {}", hasNext ? "next" : "last", bytes);
+                LOG.debug("Advanced content to {} chunk {}", hasMore ? "next" : "last", String.valueOf(bytes));
             return bytes != null;
         }
         else
         {
-            if (content != AFTER)
+            // No more content, but distinguish between last and consumed.
+            if (wasLast)
             {
-                content = buffer = AFTER;
+                buffer = content = AFTER;
                 if (LOG.isDebugEnabled())
                     LOG.debug("Advanced content past last chunk");
             }
+            else
+            {
+                buffer = content = CLOSE;
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Advanced content to last chunk");
+            }
             return false;
         }
     }
@@ -168,7 +179,7 @@
      */
     public boolean isConsumed()
     {
-        return content == AFTER;
+        return buffer == AFTER;
     }
 
     @Override
@@ -176,6 +187,8 @@
     {
         if (isConsumed())
             return;
+        if (buffer == CLOSE)
+            return;
         if (iterator instanceof Callback)
             ((Callback)iterator).succeeded();
     }
@@ -185,6 +198,8 @@
     {
         if (isConsumed())
             return;
+        if (buffer == CLOSE)
+            return;
         if (iterator instanceof Callback)
             ((Callback)iterator).failed(x);
     }
@@ -197,7 +212,7 @@
             if (iterator instanceof Closeable)
                 ((Closeable)iterator).close();
         }
-        catch (Exception x)
+        catch (Throwable x)
         {
             LOG.ignore(x);
         }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConversation.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConversation.java
index fc0f74b..51243e2 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConversation.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConversation.java
@@ -41,42 +41,42 @@
      * This list changes as the conversation proceeds, as follows:
      * <ol>
      * <li>
-     *     request R1 send => conversation.updateResponseListeners(null)
+     *     request R1 send =&gt; conversation.updateResponseListeners(null)
      *     <ul>
      *         <li>exchanges in conversation: E1</li>
      *         <li>listeners to be notified: E1.listeners</li>
      *     </ul>
      * </li>
      * <li>
-     *     response R1 arrived, 401 => conversation.updateResponseListeners(AuthenticationProtocolHandler.listener)
+     *     response R1 arrived, 401 =&gt; conversation.updateResponseListeners(AuthenticationProtocolHandler.listener)
      *     <ul>
      *         <li>exchanges in conversation: E1</li>
      *         <li>listeners to be notified: AuthenticationProtocolHandler.listener</li>
      *     </ul>
      * </li>
      * <li>
-     *     request R2 send => conversation.updateResponseListeners(null)
+     *     request R2 send =&gt; conversation.updateResponseListeners(null)
      *     <ul>
      *         <li>exchanges in conversation: E1 + E2</li>
      *         <li>listeners to be notified: E2.listeners + E1.listeners</li>
      *     </ul>
      * </li>
      * <li>
-     *     response R2 arrived, 302 => conversation.updateResponseListeners(RedirectProtocolHandler.listener)
+     *     response R2 arrived, 302 =&gt; conversation.updateResponseListeners(RedirectProtocolHandler.listener)
      *     <ul>
      *         <li>exchanges in conversation: E1 + E2</li>
      *         <li>listeners to be notified: E2.listeners + RedirectProtocolHandler.listener</li>
      *     </ul>
      * </li>
      * <li>
-     *     request R3 send => conversation.updateResponseListeners(null)
+     *     request R3 send =&gt; conversation.updateResponseListeners(null)
      *     <ul>
      *         <li>exchanges in conversation: E1 + E2 + E3</li>
      *         <li>listeners to be notified: E3.listeners + E1.listeners</li>
      *     </ul>
      * </li>
      * <li>
-     *     response R3 arrived, 200 => conversation.updateResponseListeners(null)
+     *     response R3 arrived, 200 =&gt; conversation.updateResponseListeners(null)
      *     <ul>
      *         <li>exchanges in conversation: E1 + E2 + E3</li>
      *         <li>listeners to be notified: E3.listeners + E1.listeners</li>
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
index f99ff66..54251e3 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
@@ -37,12 +37,15 @@
 import org.eclipse.jetty.util.BlockingArrayQueue;
 import org.eclipse.jetty.util.HostPort;
 import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.component.Dumpable;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-public abstract class HttpDestination implements Destination, Closeable, Dumpable
+@ManagedObject
+public abstract class HttpDestination extends ContainerLifeCycle implements Destination, Closeable, Dumpable
 {
     protected static final Logger LOG = Log.getLogger(HttpDestination.class);
 
@@ -71,6 +74,8 @@
         if (proxy != null)
         {
             connectionFactory = proxy.newClientConnectionFactory(connectionFactory);
+            if (proxy.isSecure())
+                connectionFactory = newSslClientConnectionFactory(connectionFactory);
         }
         else
         {
@@ -131,12 +136,14 @@
     }
 
     @Override
+    @ManagedAttribute(value = "The destination scheme", readonly = true)
     public String getScheme()
     {
         return origin.getScheme();
     }
 
     @Override
+    @ManagedAttribute(value = "The destination host", readonly = true)
     public String getHost()
     {
         // InetSocketAddress.getHostString() transforms the host string
@@ -145,11 +152,18 @@
     }
 
     @Override
+    @ManagedAttribute(value = "The destination port", readonly = true)
     public int getPort()
     {
         return origin.getAddress().getPort();
     }
 
+    @ManagedAttribute(value = "The number of queued requests", readonly = true)
+    public int getQueuedRequestCount()
+    {
+        return exchanges.size();
+    }
+
     public Origin.Address getConnectAddress()
     {
         return proxy == null ? origin.getAddress() : proxy.getAddress();
@@ -206,7 +220,7 @@
         return queue.offer(exchange);
     }
 
-    protected abstract void send();
+    public abstract void send();
 
     public void newConnection(Promise<Connection> promise)
     {
@@ -273,7 +287,7 @@
     @Override
     public String toString()
     {
-        return String.format("%s[%s]%x%s,queue=%d",
+        return String.format("%s[%s]@%x%s,queue=%d",
                 HttpDestination.class.getSimpleName(),
                 asString(),
                 hashCode(),
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java
index e326238..502b975 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java
@@ -27,7 +27,6 @@
 import org.eclipse.jetty.client.api.Destination;
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
 import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpMethod;
@@ -89,9 +88,12 @@
                 {
                     @SuppressWarnings("unchecked")
                     Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
-                    if (promise instanceof TunnelPromise)
+                    Promise<Connection> wrapped = promise;
+                    if (promise instanceof Promise.Wrapper)
+                        wrapped = ((Promise.Wrapper<Connection>)promise).unwrap();
+                    if (wrapped instanceof TunnelPromise)
                     {
-                        ((TunnelPromise)promise).setEndPoint(endPoint);
+                        ((TunnelPromise)wrapped).setEndPoint(endPoint);
                         return connectionFactory.newConnection(endPoint, context);
                     }
                     else
@@ -154,44 +156,44 @@
             String target = destination.getOrigin().getAddress().asString();
             Origin.Address proxyAddress = destination.getConnectAddress();
             HttpClient httpClient = destination.getHttpClient();
+            long connectTimeout = httpClient.getConnectTimeout();
             Request connect = httpClient.newRequest(proxyAddress.getHost(), proxyAddress.getPort())
-                    .scheme(HttpScheme.HTTP.asString())
                     .method(HttpMethod.CONNECT)
                     .path(target)
                     .header(HttpHeader.HOST, target)
-                    .timeout(httpClient.getConnectTimeout(), TimeUnit.MILLISECONDS);
+                    .idleTimeout(2 * connectTimeout, TimeUnit.MILLISECONDS)
+                    .timeout(connectTimeout, TimeUnit.MILLISECONDS);
+            ProxyConfiguration.Proxy proxy = destination.getProxy();
+            if (proxy != null && proxy.isSecure())
+                connect.scheme(HttpScheme.HTTPS.asString());
 
             final HttpConversation conversation = ((HttpRequest)connect).getConversation();
             conversation.setAttribute(EndPoint.class.getName(), endPoint);
 
             connect.attribute(Connection.class.getName(), new ProxyConnection(destination, connection, promise));
 
-            connection.send(connect, new Response.CompleteListener()
+            connection.send(connect, result ->
             {
-                @Override
-                public void onComplete(Result result)
+                // The EndPoint may have changed during the conversation, get the latest.
+                EndPoint endPoint = (EndPoint)conversation.getAttribute(EndPoint.class.getName());
+                if (result.isSucceeded())
                 {
-                    // The EndPoint may have changed during the conversation, get the latest.
-                    EndPoint endPoint = (EndPoint)conversation.getAttribute(EndPoint.class.getName());
-                    if (result.isSucceeded())
+                    Response response = result.getResponse();
+                    if (response.getStatus() == HttpStatus.OK_200)
                     {
-                        Response response = result.getResponse();
-                        if (response.getStatus() == HttpStatus.OK_200)
-                        {
-                            tunnelSucceeded(endPoint);
-                        }
-                        else
-                        {
-                            HttpResponseException failure = new HttpResponseException("Unexpected " + response +
-                                    " for " + result.getRequest(), response);
-                            tunnelFailed(endPoint, failure);
-                        }
+                        tunnelSucceeded(endPoint);
                     }
                     else
                     {
-                        tunnelFailed(endPoint, result.getFailure());
+                        HttpResponseException failure = new HttpResponseException("Unexpected " + response +
+                                " for " + result.getRequest(), response);
+                        tunnelFailed(endPoint, failure);
                     }
                 }
+                else
+                {
+                    tunnelFailed(endPoint, result.getFailure());
+                }
             });
         }
 
@@ -207,10 +209,10 @@
                         new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
                 HttpConnectionOverHTTP oldConnection = (HttpConnectionOverHTTP)endPoint.getConnection();
                 org.eclipse.jetty.io.Connection newConnection = sslConnectionFactory.newConnection(endPoint, context);
-                ClientConnectionFactory.Helper.replaceConnection(oldConnection, newConnection);
-                // Avoid setting fill interest in the old Connection,
-                // without closing the underlying EndPoint.
-                oldConnection.softClose();
+                // Creating the connection will link the new Connection the EndPoint,
+                // but we need the old Connection linked for the upgrade to do its job.
+                endPoint.setConnection(oldConnection);
+                endPoint.upgrade(newConnection);
                 if (LOG.isDebugEnabled())
                     LOG.debug("HTTP tunnel established: {} over {}", oldConnection, newConnection);
             }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
index 9da6136..4064c41 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
@@ -43,9 +43,9 @@
 
 /**
  * {@link HttpReceiver} provides the abstract code to implement the various steps of the receive of HTTP responses.
- * <p />
+ * <p>
  * {@link HttpReceiver} maintains a state machine that is updated when the steps of receiving a response are executed.
- * <p />
+ * <p>
  * Subclasses must handle the transport-specific details, for example how to read from the raw socket and how to parse
  * the bytes read from the socket. Then they have to call the methods defined in this class in the following order:
  * <ol>
@@ -60,7 +60,7 @@
  * (for example, because of I/O exceptions).
  * At any time, user threads may abort the response which will cause {@link #responseFailure(Throwable)} to be
  * invoked.
- * <p />
+ * <p>
  * The state machine maintained by this class ensures that the response steps are not executed by an I/O thread
  * if the response has already been failed.
  *
@@ -97,10 +97,10 @@
 
     /**
      * Method to be invoked when the response status code is available.
-     * <p />
+     * <p>
      * Subclasses must have set the response status code on the {@link Response} object of the {@link HttpExchange}
      * prior invoking this method.
-     * <p />
+     * <p>
      * This method takes case of notifying {@link org.eclipse.jetty.client.api.Response.BeginListener}s.
      *
      * @param exchange the HTTP exchange
@@ -140,10 +140,10 @@
 
     /**
      * Method to be invoked when a response HTTP header is available.
-     * <p />
+     * <p>
      * Subclasses must not have added the header to the {@link Response} object of the {@link HttpExchange}
      * prior invoking this method.
-     * <p />
+     * <p>
      * This method takes case of notifying {@link org.eclipse.jetty.client.api.Response.HeaderListener}s and storing cookies.
      *
      * @param exchange the HTTP exchange
@@ -185,7 +185,9 @@
                     case SET_COOKIE:
                     case SET_COOKIE2:
                     {
-                        storeCookie(exchange.getRequest().getURI(), field);
+                        URI uri = exchange.getRequest().getURI();
+                        if (uri != null)
+                            storeCookie(uri, field);
                         break;
                     }
                     default:
@@ -224,7 +226,7 @@
 
     /**
      * Method to be invoked after all response HTTP headers are available.
-     * <p />
+     * <p>
      * This method takes case of notifying {@link org.eclipse.jetty.client.api.Response.HeadersListener}s.
      *
      * @param exchange the HTTP exchange
@@ -282,11 +284,12 @@
 
     /**
      * Method to be invoked when response HTTP content is available.
-     * <p />
+     * <p>
      * This method takes case of decoding the content, if necessary, and notifying {@link org.eclipse.jetty.client.api.Response.ContentListener}s.
      *
      * @param exchange the HTTP exchange
      * @param buffer the response HTTP content buffer
+     * @param callback the callback
      * @return whether the processing should continue
      */
     protected boolean responseContent(HttpExchange exchange, ByteBuffer buffer, final Callback callback)
@@ -305,6 +308,7 @@
                 }
                 default:
                 {
+                    callback.failed(new IllegalStateException("Invalid response state " + current));
                     return false;
                 }
             }
@@ -364,7 +368,7 @@
 
     /**
      * Method to be invoked when the response is successful.
-     * <p />
+     * <p>
      * This method takes case of notifying {@link org.eclipse.jetty.client.api.Response.SuccessListener}s and possibly
      * {@link org.eclipse.jetty.client.api.Response.CompleteListener}s (if the exchange is completed).
      *
@@ -405,7 +409,7 @@
 
     /**
      * Method to be invoked when the response is failed.
-     * <p />
+     * <p>
      * This method takes care of notifying {@link org.eclipse.jetty.client.api.Response.FailureListener}s.
      *
      * @param failure the response failure
@@ -459,9 +463,9 @@
 
     /**
      * Resets this {@link HttpReceiver} state.
-     * <p />
+     * <p>
      * Subclasses should override (but remember to call {@code super}) to reset their own state.
-     * <p />
+     * <p>
      * Either this method or {@link #dispose()} is called.
      */
     protected void reset()
@@ -472,9 +476,9 @@
 
     /**
      * Disposes this {@link HttpReceiver} state.
-     * <p />
+     * <p>
      * Subclasses should override (but remember to call {@code super}) to dispose their own state.
-     * <p />
+     * <p>
      * Either this method or {@link #reset()} is called.
      */
     protected void dispose()
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRedirector.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRedirector.java
index 2a2b794..3b48ef2 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRedirector.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRedirector.java
@@ -37,7 +37,7 @@
 
 /**
  * Utility class that handles HTTP redirects.
- * <p />
+ * <p>
  * Applications can disable redirection via {@link Request#followRedirects(boolean)}
  * and then rely on this class to perform the redirect in a simpler way, for example:
  * <pre>
@@ -229,7 +229,18 @@
     private Request redirect(Request request, Response response, Response.CompleteListener listener, URI newURI)
     {
         if (!newURI.isAbsolute())
-            newURI = request.getURI().resolve(newURI);
+        {
+            URI requestURI = request.getURI();
+            if (requestURI == null)
+            {
+                String uri = request.getScheme() + "://" + request.getHost();
+                int port = request.getPort();
+                if (port > 0)
+                    uri += ":" + port;
+                requestURI = URI.create(uri);
+            }
+            newURI = requestURI.resolve(newURI);
+        }
 
         int status = response.getStatus();
         switch (status)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
index e3b0294..445f1d3 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
@@ -40,6 +40,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiFunction;
 
 import org.eclipse.jetty.client.api.ContentProvider;
 import org.eclipse.jetty.client.api.ContentResponse;
@@ -81,6 +82,7 @@
     private List<HttpCookie> cookies;
     private Map<String, Object> attributes;
     private List<RequestListener> requestListeners;
+    private BiFunction<Request, Request, Response.CompleteListener> pushListener;
 
     protected HttpRequest(HttpClient client, HttpConversation conversation, URI uri)
     {
@@ -92,6 +94,7 @@
         path = uri.getRawPath();
         query = uri.getRawQuery();
         extractParams(query);
+
         followRedirects(client.isFollowRedirects());
         idleTimeout = client.getIdleTimeout();
         HttpField acceptEncodingField = client.getAcceptEncodingField();
@@ -170,8 +173,6 @@
         else
         {
             String rawPath = uri.getRawPath();
-            if (uri.isOpaque())
-                rawPath = path;
             if (rawPath == null)
                 rawPath = "";
             this.path = rawPath;
@@ -503,7 +504,7 @@
                     listener.onContent(response, content);
                     callback.succeeded();
                 }
-                catch (Exception x)
+                catch (Throwable x)
                 {
                     callback.failed(x);
                 }
@@ -568,6 +569,26 @@
         return this;
     }
 
+    /**
+     * <p>Sets a listener for pushed resources.</p>
+     * <p>When resources are pushed from the server, the given {@code listener}
+     * is invoked for every pushed resource.
+     * The parameters to the {@code BiFunction} are this request and the
+     * synthesized request for the pushed resource.
+     * The {@code BiFunction} should return a {@code CompleteListener} that
+     * may also implement other listener interfaces to be notified of various
+     * response events, or {@code null} to signal that the pushed resource
+     * should be canceled.</p>
+     *
+     * @param listener a listener for pushed resource events
+     * @return this request object
+     */
+    public Request pushListener(BiFunction<Request, Request, Response.CompleteListener> listener)
+    {
+        this.pushListener = listener;
+        return this;
+    }
+
     @Override
     public ContentProvider getContent()
     {
@@ -694,6 +715,16 @@
         client.send(request, responseListeners);
     }
 
+    protected List<Response.ResponseListener> getResponseListeners()
+    {
+        return responseListeners;
+    }
+
+    public BiFunction<Request, Request, Response.CompleteListener> getPushListener()
+    {
+        return pushListener;
+    }
+
     @Override
     public boolean abort(Throwable cause)
     {
@@ -737,7 +768,7 @@
         if (value == null)
             return "";
 
-        String encoding = "UTF-8";
+        String encoding = "utf-8";
         try
         {
             return URLEncoder.encode(value, encoding);
@@ -768,7 +799,7 @@
 
     private String urlDecode(String value)
     {
-        String charset = "UTF-8";
+        String charset = "utf-8";
         try
         {
             return URLDecoder.decode(value, charset);
@@ -788,7 +819,7 @@
         URI result = newURI(path);
         if (result == null)
             return NULL_URI;
-        if (!result.isAbsolute() && !result.isOpaque())
+        if (!result.isAbsolute())
             result = URI.create(new Origin(getScheme(), getHost(), getPort()).asString() + path);
         return result;
     }
@@ -797,12 +828,16 @@
     {
         try
         {
-            return new URI(uri);
+            // Handle specially the "OPTIONS *" case, since it is possible to create a URI from "*" (!).
+            if ("*".equals(uri))
+                return null;
+            URI result = new URI(uri);
+            return result.isOpaque() ? null : result;
         }
         catch (URISyntaxException x)
         {
             // The "path" of a HTTP request may not be a URI,
-            // for example for CONNECT 127.0.0.1:8080 or OPTIONS *.
+            // for example for CONNECT 127.0.0.1:8080.
             return null;
         }
     }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java
index 56ff74e..fc94bc0 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java
@@ -20,7 +20,7 @@
 
 import org.eclipse.jetty.client.api.Request;
 
-public class HttpRequestException extends Throwable
+public class HttpRequestException extends RuntimeException
 {
     private final Request request;
 
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
index 9aae372..4b803d5 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
@@ -37,16 +37,16 @@
  * the transport-specific code to send requests over the wire, implementing
  * {@link #sendHeaders(HttpExchange, HttpContent, Callback)} and
  * {@link #sendContent(HttpExchange, HttpContent, Callback)}.
- * <p />
+ * <p>
  * {@link HttpSender} governs two state machines.
- * <p />
+ * <p>
  * The request state machine is updated by {@link HttpSender} as the various steps of sending a request
- * are executed, see {@link RequestState}.
+ * are executed, see <code>RequestState</code>.
  * At any point in time, a user thread may abort the request, which may (if the request has not been
- * completely sent yet) move the request state machine to {@link RequestState#FAILURE}.
+ * completely sent yet) move the request state machine to <code>RequestState#FAILURE</code>.
  * The request state machine guarantees that the request steps are executed (by I/O threads) only if
  * the request has not been failed already.
- * <p />
+ * <p>
  * The sender state machine is updated by {@link HttpSender} from three sources: deferred content notifications
  * (via {@link #onContent()}), 100-continue notifications (via {@link #proceed(HttpExchange, Throwable)})
  * and normal request send (via {@link #sendContent(HttpExchange, HttpContent, Callback)}).
@@ -392,7 +392,7 @@
     /**
      * Implementations should send the HTTP headers over the wire, possibly with some content,
      * in a single write, and notify the given {@code callback} of the result of this operation.
-     * <p />
+     * <p>
      * If there is more content to send, then {@link #sendContent(HttpExchange, HttpContent, Callback)}
      * will be invoked.
      *
@@ -404,11 +404,11 @@
 
     /**
      * Implementations should send the content at the {@link HttpContent} cursor position over the wire.
-     * <p />
+     * <p>
      * The {@link HttpContent} cursor is advanced by {@link HttpSender} at the right time, and if more
      * content needs to be sent, this method is invoked again; subclasses need only to send the content
      * at the {@link HttpContent} cursor position.
-     * <p />
+     * <p>
      * This method is invoked one last time when {@link HttpContent#isConsumed()} is true and therefore
      * there is no actual content to send.
      * This is done to allow subclasses to write "terminal" bytes (such as the terminal chunk when the
@@ -672,6 +672,13 @@
 
     private class CommitCallback implements Callback
     {
+
+        @Override
+        public boolean isNonBlocking()
+        {
+            return content.isNonBlocking();
+        }
+
         @Override
         public void succeeded()
         {
@@ -804,9 +811,9 @@
             while (true)
             {
                 boolean advanced = content.advance();
-                boolean consumed = content.isConsumed();
+                boolean lastContent = content.isLast();
                 if (LOG.isDebugEnabled())
-                    LOG.debug("Content {} consumed {} for {}", advanced, consumed, exchange.getRequest());
+                    LOG.debug("Content present {}, last {}, consumed {} for {}", advanced, lastContent, content.isConsumed(), exchange.getRequest());
 
                 if (advanced)
                 {
@@ -814,7 +821,7 @@
                     return Action.SCHEDULED;
                 }
 
-                if (consumed)
+                if (lastContent)
                 {
                     sendContent(exchange, content, lastCallback);
                     return Action.IDLE;
@@ -883,6 +890,12 @@
     private class LastContentCallback implements Callback
     {
         @Override
+        public boolean isNonBlocking()
+        {
+            return content.isNonBlocking();
+        }
+
+        @Override
         public void succeeded()
         {
             HttpExchange exchange = getHttpExchange();
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/LeakTrackingConnectionPool.java b/jetty-client/src/main/java/org/eclipse/jetty/client/LeakTrackingConnectionPool.java
index 22ad38b..eee58e6 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/LeakTrackingConnectionPool.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/LeakTrackingConnectionPool.java
@@ -20,11 +20,15 @@
 
 import org.eclipse.jetty.client.api.Connection;
 import org.eclipse.jetty.client.api.Destination;
+import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.LeakDetector;
-import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
 
 public class LeakTrackingConnectionPool extends ConnectionPool
 {
+    private static final Logger LOG = Log.getLogger(LeakTrackingConnectionPool.class);
+
     private final LeakDetector<Connection> leakDetector = new LeakDetector<Connection>()
     {
         @Override
@@ -34,9 +38,9 @@
         }
     };
 
-    public LeakTrackingConnectionPool(Destination destination, int maxConnections, Promise<Connection> connectionPromise)
+    public LeakTrackingConnectionPool(Destination destination, int maxConnections, Callback requester)
     {
-        super(destination, maxConnections, connectionPromise);
+        super(destination, maxConnections, requester);
         start();
     }
 
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java
index b7cc47d..b97c094 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java
@@ -18,6 +18,7 @@
 
 package org.eclipse.jetty.client;
 
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.eclipse.jetty.client.api.Connection;
@@ -27,6 +28,8 @@
 public abstract class MultiplexHttpDestination<C extends Connection> extends HttpDestination implements Promise<Connection>
 {
     private final AtomicReference<ConnectState> connect = new AtomicReference<>(ConnectState.DISCONNECTED);
+    private final AtomicInteger requestsPerConnection = new AtomicInteger();
+    private int maxRequestsPerConnection = 1024;
     private C connection;
 
     protected MultiplexHttpDestination(HttpClient client, Origin origin)
@@ -34,8 +37,18 @@
         super(client, origin);
     }
 
+    public int getMaxRequestsPerConnection()
+    {
+        return maxRequestsPerConnection;
+    }
+
+    public void setMaxRequestsPerConnection(int maxRequestsPerConnection)
+    {
+        this.maxRequestsPerConnection = maxRequestsPerConnection;
+    }
+
     @Override
-    protected void send()
+    public void send()
     {
         while (true)
         {
@@ -56,7 +69,7 @@
                 }
                 case CONNECTED:
                 {
-                    if (process(connection, false))
+                    if (process(connection))
                         break;
                     return;
                 }
@@ -76,12 +89,12 @@
         C connection = this.connection = (C)result;
         if (connect.compareAndSet(ConnectState.CONNECTING, ConnectState.CONNECTED))
         {
-            process(connection, true);
+            send();
         }
         else
         {
             connection.close();
-            failed(new IllegalStateException());
+            failed(new IllegalStateException("Invalid connection state " + connect));
         }
     }
 
@@ -89,47 +102,68 @@
     public void failed(Throwable x)
     {
         connect.set(ConnectState.DISCONNECTED);
+        abort(x);
     }
 
-    protected boolean process(final C connection, boolean dispatch)
+    protected boolean process(final C connection)
     {
-        HttpClient client = getHttpClient();
-        final HttpExchange exchange = getHttpExchanges().poll();
-        if (LOG.isDebugEnabled())
-            LOG.debug("Processing {} on {}", exchange, connection);
-        if (exchange == null)
-            return false;
+        while (true)
+        {
+            int max = getMaxRequestsPerConnection();
+            int count = requestsPerConnection.get();
+            int next = count + 1;
+            if (next > max)
+                return false;
 
-        final Request request = exchange.getRequest();
-        Throwable cause = request.getAbortCause();
-        if (cause != null)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Aborted before processing {}: {}", exchange, cause);
-            // It may happen that the request is aborted before the exchange
-            // is created. Aborting the exchange a second time will result in
-            // a no-operation, so we just abort here to cover that edge case.
-            exchange.abort(cause);
-        }
-        else
-        {
-            if (dispatch)
+            if (requestsPerConnection.compareAndSet(count, next))
             {
-                client.getExecutor().execute(new Runnable()
+                HttpExchange exchange = getHttpExchanges().poll();
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Processing {}/{} {} on {}", next, max, exchange, connection);
+                if (exchange == null)
                 {
-                    @Override
-                    public void run()
+                    requestsPerConnection.decrementAndGet();
+                    return false;
+                }
+
+                final Request request = exchange.getRequest();
+                Throwable cause = request.getAbortCause();
+                if (cause != null)
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Aborted before processing {}: {}", exchange, cause);
+                    requestsPerConnection.decrementAndGet();
+                    // It may happen that the request is aborted before the exchange
+                    // is created. Aborting the exchange a second time will result in
+                    // a no-operation, so we just abort here to cover that edge case.
+                    exchange.abort(cause);
+                }
+                else
+                {
+                    SendFailure result = send(connection, exchange);
+                    if (result != null)
                     {
-                        send(connection, exchange);
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("Send failed {} for {}", result, exchange);
+                        requestsPerConnection.decrementAndGet();
+                        if (result.retry)
+                        {
+                            if (enqueue(getHttpExchanges(), exchange))
+                                return true;
+                        }
+                        request.abort(result.failure);
                     }
-                });
-            }
-            else
-            {
-                send(connection, exchange);
+                }
+                return getHttpExchanges().peek() != null;
             }
         }
-        return true;
+    }
+
+    @Override
+    public void release(Connection connection)
+    {
+        requestsPerConnection.decrementAndGet();
+        send();
     }
 
     @Override
@@ -157,7 +191,7 @@
         }
     }
 
-    protected abstract void send(C connection, HttpExchange exchange);
+    protected abstract SendFailure send(C connection, HttpExchange exchange);
 
     private enum ConnectState
     {
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java
index 30992bc..3064e42 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java
@@ -19,70 +19,82 @@
 package org.eclipse.jetty.client;
 
 import java.io.IOException;
-import java.util.Arrays;
+import java.util.Collections;
 
 import org.eclipse.jetty.client.api.Connection;
 import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.thread.Sweeper;
 
-public abstract class PoolingHttpDestination<C extends Connection> extends HttpDestination implements Promise<Connection>
+@ManagedObject
+public abstract class PoolingHttpDestination<C extends Connection> extends HttpDestination implements Callback
 {
-    private final ConnectionPool connectionPool;
+    private DuplexConnectionPool connectionPool;
 
     public PoolingHttpDestination(HttpClient client, Origin origin)
     {
         super(client, origin);
         this.connectionPool = newConnectionPool(client);
+        addBean(connectionPool);
         Sweeper sweeper = client.getBean(Sweeper.class);
         if (sweeper != null)
             sweeper.offer(connectionPool);
     }
 
-    protected ConnectionPool newConnectionPool(HttpClient client)
+    @Override
+    protected void doStart() throws Exception
     {
-        return new ConnectionPool(this, client.getMaxConnectionsPerDestination(), this);
+        HttpClient client = getHttpClient();
+        this.connectionPool = newConnectionPool(client);
+        addBean(connectionPool);
+        super.doStart();
+        Sweeper sweeper = client.getBean(Sweeper.class);
+        if (sweeper != null)
+            sweeper.offer(connectionPool);
     }
 
-    public ConnectionPool getConnectionPool()
+    @Override
+    protected void doStop() throws Exception
+    {
+        HttpClient client = getHttpClient();
+        Sweeper sweeper = client.getBean(Sweeper.class);
+        if (sweeper != null)
+            sweeper.remove(connectionPool);
+        super.doStop();
+        removeBean(connectionPool);
+    }
+
+    protected DuplexConnectionPool newConnectionPool(HttpClient client)
+    {
+        return new DuplexConnectionPool(this, client.getMaxConnectionsPerDestination(), this);
+    }
+
+    @ManagedAttribute(value = "The connection pool", readonly = true)
+    public DuplexConnectionPool getConnectionPool()
     {
         return connectionPool;
     }
 
     @Override
-    @SuppressWarnings("unchecked")
-    public void succeeded(Connection connection)
+    public void succeeded()
     {
-        send(true);
+        send();
     }
 
     @Override
     public void failed(final Throwable x)
     {
-        getHttpClient().getExecutor().execute(new Runnable()
-        {
-            @Override
-            public void run()
-            {
-                abort(x);
-            }
-        });
+        abort(x);
     }
 
-    @Override
-    protected void send()
-    {
-        send(false);
-    }
-
-    private void send(boolean dispatch)
+    public void send()
     {
         if (getHttpExchanges().isEmpty())
             return;
-        C connection = acquire();
-        if (connection != null)
-            process(connection, dispatch);
+        process();
     }
 
     @SuppressWarnings("unchecked")
@@ -91,6 +103,19 @@
         return (C)connectionPool.acquire();
     }
 
+    private void process()
+    {
+        while (true)
+        {
+            C connection = acquire();
+            if (connection == null)
+                break;
+            boolean proceed = process(connection);
+            if (!proceed)
+                break;
+        }
+    }
+
     /**
      * <p>Processes a new connection making it idle or active depending on whether requests are waiting to be sent.</p>
      * <p>A new connection is created when a request needs to be executed; it is possible that the request that
@@ -99,9 +124,9 @@
      * <p>If a request is waiting to be executed, it will be dequeued and executed by the new connection.</p>
      *
      * @param connection the new connection
-     * @param dispatch whether to dispatch the processing to another thread
+     * @return whether to perform more processing
      */
-    public void process(final C connection, boolean dispatch)
+    public boolean process(final C connection)
     {
         HttpClient client = getHttpClient();
         final HttpExchange exchange = getHttpExchanges().poll();
@@ -111,13 +136,13 @@
         {
             if (!connectionPool.release(connection))
                 connection.close();
-
             if (!client.isRunning())
             {
                 if (LOG.isDebugEnabled())
                     LOG.debug("{} is stopping", client);
                 connection.close();
             }
+            return false;
         }
         else
         {
@@ -137,26 +162,25 @@
             }
             else
             {
-                if (dispatch)
+                SendFailure result = send(connection, exchange);
+                if (result != null)
                 {
-                    client.getExecutor().execute(new Runnable()
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Send failed {} for {}", result, exchange);
+                    if (result.retry)
                     {
-                        @Override
-                        public void run()
-                        {
-                            send(connection, exchange);
-                        }
-                    });
-                }
-                else
-                {
-                    send(connection, exchange);
+                        if (enqueue(getHttpExchanges(), exchange))
+                            return true;
+                    }
+
+                    request.abort(result.failure);
                 }
             }
+            return getHttpExchanges().peek() != null;
         }
     }
 
-    protected abstract void send(C connection, HttpExchange exchange);
+    protected abstract SendFailure send(C connection, HttpExchange exchange);
 
     @Override
     public void release(Connection c)
@@ -164,18 +188,21 @@
         @SuppressWarnings("unchecked")
         C connection = (C)c;
         if (LOG.isDebugEnabled())
-            LOG.debug("{} released", connection);
+            LOG.debug("Released {}", connection);
         HttpClient client = getHttpClient();
         if (client.isRunning())
         {
             if (connectionPool.isActive(connection))
             {
-                process(connection, false);
+                if (connectionPool.release(connection))
+                    send();
+                else
+                    connection.close();
             }
             else
             {
                 if (LOG.isDebugEnabled())
-                    LOG.debug("{} explicit", connection);
+                    LOG.debug("Released explicit {}", connection);
             }
         }
         else
@@ -187,11 +214,11 @@
     }
 
     @Override
-    public void close(Connection oldConnection)
+    public void close(Connection connection)
     {
-        super.close(oldConnection);
+        super.close(connection);
 
-        boolean removed = connectionPool.remove(oldConnection);
+        boolean removed = connectionPool.remove(connection);
 
         if (getHttpExchanges().isEmpty())
         {
@@ -206,14 +233,13 @@
                 getHttpClient().removeDestination(this);
             }
         }
-        else if (removed)
+        else
         {
             // We need to execute queued requests even if this connection failed.
             // We may create a connection that is not needed, but it will eventually
             // idle timeout, so no worries.
-            C newConnection = acquire();
-            if (newConnection != null)
-                process(newConnection, false);
+            if (removed)
+                process();
         }
     }
 
@@ -227,7 +253,7 @@
     public void dump(Appendable out, String indent) throws IOException
     {
         super.dump(out, indent);
-        ContainerLifeCycle.dump(out, indent, Arrays.asList(connectionPool));
+        ContainerLifeCycle.dump(out, indent, Collections.singletonList(connectionPool));
     }
 
     @Override
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ProtocolHandler.java
index b5e160e..2a724cf 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ProtocolHandler.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ProtocolHandler.java
@@ -21,9 +21,38 @@
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.client.api.Response;
 
+/**
+ * <p>A protocol handler performs HTTP protocol operations on
+ * behalf of the application, typically like a browser would.</p>
+ * <p>A typical example is handling HTTP redirects. {@link HttpClient}
+ * could just return the redirect response to the application,
+ * but the application would have to implement the redirect
+ * functionality (while browsers do this automatically).</p>
+ */
 public interface ProtocolHandler
 {
+    /**
+     * @return a unique name among protocol handlers
+     */
+    public String getName();
+
+    /**
+     * <p>Inspects the given {@code request} and {@code response}
+     * to detect whether this protocol handler should handle them.</p>
+     * <p>For example, a redirect protocol handler can inspect the
+     * response code and return true if it is a redirect response code.</p>
+     * <p>This method is being called just after the response line has
+     * been parsed, and before the response headers are available.</p>
+     *
+     * @param request  the request to accept
+     * @param response the response to accept
+     * @return true if this protocol handler can handle the given request and response
+     */
     public boolean accept(Request request, Response response);
 
+    /**
+     * @return a response listener that will handle the request and response
+     * on behalf of the application.
+     */
     public Response.Listener getResponseListener();
 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ProtocolHandlers.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ProtocolHandlers.java
new file mode 100644
index 0000000..6b131ba
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ProtocolHandlers.java
@@ -0,0 +1,94 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Response;
+
+/**
+ * <p>A container for {@link ProtocolHandler}s accessible from {@link HttpClient#getProtocolHandlers()}.</p>
+ */
+public class ProtocolHandlers
+{
+    private final Map<String, ProtocolHandler> handlers = new LinkedHashMap<>();
+
+    protected ProtocolHandlers()
+    {
+    }
+
+    /**
+     * <p>Stores the given {@code protocolHandler} in this container.</p>
+     * <p>If a protocol handler with the same name exists, it is
+     * replaced by the given one, and the existing returned.</p>
+     *
+     * @param protocolHandler the protocol handler to store
+     * @return the existing protocol handler with the same name,
+     * or null if no protocol handler with that name was already stored
+     * @see #remove(String)
+     */
+    public ProtocolHandler put(ProtocolHandler protocolHandler)
+    {
+        return handlers.put(protocolHandler.getName(), protocolHandler);
+    }
+
+    /**
+     * <p>Removes the protocol handler with the given name.</p>
+     *
+     * @param name the name of the protocol handler to remove
+     * @return the removed protocol handler, or null if no
+     * protocol handler with that name was already stored
+     * @see #put(ProtocolHandler)
+     * @see #clear()
+     */
+    public ProtocolHandler remove(String name)
+    {
+        return handlers.remove(name);
+    }
+
+    /**
+     * <p>Removes all protocol handlers from this container.</p>
+     */
+    public void clear()
+    {
+        handlers.clear();
+    }
+
+    /**
+     * <p>Finds the first protocol handler that
+     * {@link ProtocolHandler#accept(Request, Response) accepts}
+     * the given request and response.</p>
+     *
+     * @param request  the request to accept
+     * @param response the response to accept
+     * @return the protocol handler that accepted the request and response,
+     * or null if none of the protocol handlers accepted the request and response
+     */
+    public ProtocolHandler find(Request request, Response response)
+    {
+        for (ProtocolHandler handler : handlers.values())
+        {
+            if (handler.accept(request, response))
+                return handler;
+        }
+        return null;
+    }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java
index 7d27ba6..bd0866e 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java
@@ -25,8 +25,17 @@
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpStatus;
 
+/**
+ * <p>A protocol handler that handles the 401 response code
+ * in association with the {@code Proxy-Authenticate} header.</p>
+ *
+ * @see WWWAuthenticationProtocolHandler
+ */
 public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHandler
 {
+    public static final String NAME = "proxy-authenticate";
+    private static final String ATTRIBUTE = ProxyAuthenticationProtocolHandler.class.getName() + ".attribute";
+
     public ProxyAuthenticationProtocolHandler(HttpClient client)
     {
         this(client, DEFAULT_MAX_CONTENT_LENGTH);
@@ -38,6 +47,12 @@
     }
 
     @Override
+    public String getName()
+    {
+        return NAME;
+    }
+
+    @Override
     public boolean accept(Request request, Response response)
     {
         return response.getStatus() == HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407;
@@ -62,4 +77,10 @@
         ProxyConfiguration.Proxy proxy = destination.getProxy();
         return proxy != null ? proxy.getURI() : request.getURI();
     }
+
+    @Override
+    protected String getAuthenticationAttribute()
+    {
+        return ATTRIBUTE;
+    }
 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyConfiguration.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyConfiguration.java
index 209ea8f..50c0e35 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyConfiguration.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyConfiguration.java
@@ -29,7 +29,7 @@
 
 /**
  * The configuration of the forward proxy to use with {@link org.eclipse.jetty.client.HttpClient}.
- * <p />
+ * <p>
  * Applications add subclasses of {@link Proxy} to this configuration via:
  * <pre>
  * ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
@@ -124,6 +124,9 @@
          */
         public boolean matches(Origin origin)
         {
+            if (getAddress().equals(origin.getAddress()))
+                return false;
+
             boolean result = included.isEmpty();
             Origin.Address address = origin.getAddress();
             for (String included : this.included)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/RedirectProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/RedirectProtocolHandler.java
index 9bc7645..bd3b06c 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/RedirectProtocolHandler.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/RedirectProtocolHandler.java
@@ -21,9 +21,16 @@
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.client.api.Response;
 import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
 
+/**
+ * <p>A protocol handler that handles redirect status codes 301, 302, 303, 307 and 308.</p>
+ */
 public class RedirectProtocolHandler extends Response.Listener.Adapter implements ProtocolHandler
 {
+    public static final String NAME = "redirect";
+
     private final HttpRedirector redirector;
 
     public RedirectProtocolHandler(HttpClient client)
@@ -32,6 +39,12 @@
     }
 
     @Override
+    public String getName()
+    {
+        return NAME;
+    }
+
+    @Override
     public boolean accept(Request request, Response response)
     {
         return redirector.isRedirect(response) && request.isFollowRedirects();
@@ -44,6 +57,14 @@
     }
 
     @Override
+    public boolean onHeader(Response response, HttpField field)
+    {
+        // Avoid that the content is decoded, which could generate
+        // errors, since we are discarding the content anyway.
+        return field.getHeader() != HttpHeader.CONTENT_ENCODING;
+    }
+
+    @Override
     public void onComplete(Result result)
     {
         Request request = result.getRequest();
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java
index 5da8c24..fec25fa 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java
@@ -209,8 +209,7 @@
         }
         notifyHeaders(listeners, response);
         if (response instanceof ContentResponse)
-            // TODO: handle callback
-            notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), new Callback.Adapter());
+            notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), Callback.NOOP);
         notifySuccess(listeners, response);
     }
 
@@ -231,8 +230,7 @@
         }
         notifyHeaders(listeners, response);
         if (response instanceof ContentResponse)
-            // TODO: handle callback
-            notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), new Callback.Adapter());
+            notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), Callback.NOOP);
         notifyFailure(listeners, response, failure);
     }
 
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/SendFailure.java b/jetty-client/src/main/java/org/eclipse/jetty/client/SendFailure.java
new file mode 100644
index 0000000..f39f738
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/SendFailure.java
@@ -0,0 +1,37 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client;
+
+public class SendFailure
+{
+    public final Throwable failure;
+    public final boolean retry;
+
+    public SendFailure(Throwable failure, boolean retry)
+    {
+        this.failure = failure;
+        this.retry = retry;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s[failure=%s,retry=%b]", super.toString(), failure, retry);
+    }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java b/jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java
index 2841d6c..883b672 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java
@@ -70,7 +70,8 @@
         {
             HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
             Executor executor = destination.getHttpClient().getExecutor();
-            return new Socks4ProxyConnection(endPoint, executor, connectionFactory, context);
+            Socks4ProxyConnection connection = new Socks4ProxyConnection(endPoint, executor, connectionFactory, context);
+            return customize(connection, context);
         }
     }
 
@@ -198,10 +199,10 @@
                 ClientConnectionFactory connectionFactory = this.connectionFactory;
                 if (HttpScheme.HTTPS.is(destination.getScheme()))
                     connectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
-                org.eclipse.jetty.io.Connection connection = connectionFactory.newConnection(getEndPoint(), context);
-                ClientConnectionFactory.Helper.replaceConnection(this, connection);
+                org.eclipse.jetty.io.Connection newConnection = connectionFactory.newConnection(getEndPoint(), context);
+                getEndPoint().upgrade(newConnection);
                 if (LOG.isDebugEnabled())
-                    LOG.debug("SOCKS4 tunnel established: {} over {}", this, connection);
+                    LOG.debug("SOCKS4 tunnel established: {} over {}", this, newConnection);
             }
             catch (Throwable x)
             {
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/TimeoutCompleteListener.java b/jetty-client/src/main/java/org/eclipse/jetty/client/TimeoutCompleteListener.java
index caa308a..f5345e4 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/TimeoutCompleteListener.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/TimeoutCompleteListener.java
@@ -68,7 +68,7 @@
     {
         if (LOG.isDebugEnabled())
             LOG.debug("Executing timeout task {} for {}", task, request);
-        request.abort(new TimeoutException("Total timeout elapsed"));
+        request.abort(new TimeoutException("Total timeout " + request.getTimeout() + " ms elapsed"));
     }
 
     public void cancel()
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ValidatingConnectionPool.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ValidatingConnectionPool.java
new file mode 100644
index 0000000..a7b37ce
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ValidatingConnectionPool.java
@@ -0,0 +1,209 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.client.api.Destination;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Scheduler;
+
+/**
+ * <p>A connection pool that validates connections before
+ * making them available for use.</p>
+ * <p>Connections that have just been opened are not validated.
+ * Connections that are {@link #release(Connection) released} will
+ * be validated.</p>
+ * <p>Validation by reading from the EndPoint is not reliable,
+ * since the TCP FIN may arrive just after the validation read.</p>
+ * <p>This class validates connections by putting them in a
+ * "quarantine" for a configurable timeout, where they cannot
+ * be used to send requests. When the timeout expires, the
+ * quarantined connection is made idle and therefore available
+ * to send requests.</p>
+ * <p>The existing HttpClient mechanism to detect server closes
+ * will trigger and close quarantined connections, before they
+ * are made idle (and reusable) again.</p>
+ * <p>There still is a small chance that the timeout expires,
+ * the connection is made idle and available again, it is used
+ * to send a request exactly when the server decides to close.
+ * This case is however unavoidable and may be mitigated by
+ * tuning the idle timeout of the servers to be larger than
+ * that of the client.</p>
+ */
+public class ValidatingConnectionPool extends ConnectionPool
+{
+    private static final Logger LOG = Log.getLogger(ValidatingConnectionPool.class);
+
+    private final Scheduler scheduler;
+    private final long timeout;
+    private final Map<Connection, Holder> quarantine;
+
+    public ValidatingConnectionPool(Destination destination, int maxConnections, Callback requester, Scheduler scheduler, long timeout)
+    {
+        super(destination, maxConnections, requester);
+        this.scheduler = scheduler;
+        this.timeout = timeout;
+        this.quarantine = new HashMap<>(maxConnections);
+    }
+
+    @ManagedAttribute(value = "The number of validating connections", readonly = true)
+    public int getValidatingConnectionCount()
+    {
+        return quarantine.size();
+    }
+
+    @Override
+    public boolean release(Connection connection)
+    {
+        lock();
+        try
+        {
+            if (!getActiveConnections().remove(connection))
+                return false;
+            Holder holder = new Holder(connection);
+            holder.task = scheduler.schedule(holder, timeout, TimeUnit.MILLISECONDS);
+            quarantine.put(connection, holder);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Validating for {}ms {}", timeout, connection);
+        }
+        finally
+        {
+            unlock();
+        }
+
+        released(connection);
+        return true;
+    }
+
+    @Override
+    public boolean remove(Connection connection)
+    {
+        Holder holder;
+        lock();
+        try
+        {
+            holder = quarantine.remove(connection);
+        }
+        finally
+        {
+            unlock();
+        }
+
+        if (holder == null)
+            return super.remove(connection);
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("Removed while validating {}", connection);
+
+        boolean cancelled = holder.cancel();
+        if (cancelled)
+            return remove(connection, true);
+
+        return super.remove(connection);
+    }
+
+    @Override
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        super.dump(out, indent);
+        ContainerLifeCycle.dump(out, indent, quarantine.values());
+    }
+
+    @Override
+    public String toString()
+    {
+        int size;
+        lock();
+        try
+        {
+            size = quarantine.size();
+        }
+        finally
+        {
+            unlock();
+        }
+        return String.format("%s[v=%d]", super.toString(), size);
+    }
+
+    private class Holder implements Runnable
+    {
+        private final long timestamp = System.nanoTime();
+        private final AtomicBoolean latch = new AtomicBoolean();
+        private final Connection connection;
+        public Scheduler.Task task;
+
+        public Holder(Connection connection)
+        {
+            this.connection = connection;
+        }
+
+        @Override
+        public void run()
+        {
+            if (latch.compareAndSet(false, true))
+            {
+                boolean idle;
+                lock();
+                try
+                {
+                    quarantine.remove(connection);
+                    idle = offerIdle(connection);
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Validated {}", connection);
+                }
+                finally
+                {
+                    unlock();
+                }
+
+                if (idle(connection, idle))
+                    proceed();
+            }
+        }
+
+        public boolean cancel()
+        {
+            if (latch.compareAndSet(false, true))
+            {
+                task.cancel();
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("%s[validationLeft=%dms]",
+                    connection,
+                    timeout - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - timestamp)
+            );
+        }
+    }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java
index adbafac..1300c69 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java
@@ -25,8 +25,17 @@
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpStatus;
 
+/**
+ * <p>A protocol handler that handles the 401 response code
+ * in association with the {@code WWW-Authenticate} header.</p>
+ *
+ * @see ProxyAuthenticationProtocolHandler
+ */
 public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHandler
 {
+    public static final String NAME = "www-authenticate";
+    private static final String ATTRIBUTE = WWWAuthenticationProtocolHandler.class.getName() + ".attribute";
+
     public WWWAuthenticationProtocolHandler(HttpClient client)
     {
         this(client, DEFAULT_MAX_CONTENT_LENGTH);
@@ -38,6 +47,12 @@
     }
 
     @Override
+    public String getName()
+    {
+        return NAME;
+    }
+
+    @Override
     public boolean accept(Request request, Response response)
     {
         return response.getStatus() == HttpStatus.UNAUTHORIZED_401;
@@ -60,4 +75,10 @@
     {
         return request.getURI();
     }
+
+    @Override
+    protected String getAuthenticationAttribute()
+    {
+        return ATTRIBUTE;
+    }
 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Authentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Authentication.java
index f8314ce..685d6b3 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Authentication.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Authentication.java
@@ -25,12 +25,12 @@
 
 /**
  * {@link Authentication} represents a mechanism to authenticate requests for protected resources.
- * <p />
+ * <p>
  * {@link Authentication}s are added to an {@link AuthenticationStore}, which is then
  * {@link #matches(String, URI, String) queried} to find the right
  * {@link Authentication} mechanism to use based on its type, URI and realm, as returned by
  * {@code WWW-Authenticate} response headers.
- * <p />
+ * <p>
  * If an {@link Authentication} mechanism is found, it is then
  * {@link #authenticate(Request, ContentResponse, HeaderInfo, Attributes) executed} for the given request,
  * returning an {@link Authentication.Result}, which is then stored in the {@link AuthenticationStore}
@@ -39,6 +39,12 @@
 public interface Authentication
 {
     /**
+     * Constant used to indicate that any realm will match.
+     * @see #matches(String, URI, String)
+     */
+    public static final String ANY_REALM = "<<ANY_REALM>>";
+
+    /**
      * Matches {@link Authentication}s based on the given parameters
      * @param type the {@link Authentication} type such as "Basic" or "Digest"
      * @param uri the request URI
@@ -49,8 +55,8 @@
 
     /**
      * Executes the authentication mechanism for the given request, returning a {@link Result} that can be
-     * used to actually authenticate the request via {@link Result#apply(Request)}.
-     * <p />
+     * used to actually authenticate the request via {@link org.eclipse.jetty.client.api.Authentication.Result#apply(Request)}.
+     * <p>
      * If a request for {@code "/secure"} returns a {@link Result}, then the result may be used for other
      * requests such as {@code "/secure/foo"} or {@code "/secure/bar"}, unless those resources are protected
      * by other realms.
@@ -117,7 +123,7 @@
     }
 
     /**
-     * {@link Result} holds the information needed to authenticate a {@link Request} via {@link #apply(Request)}.
+     * {@link Result} holds the information needed to authenticate a {@link Request} via {@link org.eclipse.jetty.client.api.Authentication.Result#apply(org.eclipse.jetty.client.api.Request)}.
      */
     public static interface Result
     {
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Connection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Connection.java
index 4d23923..7731a0e 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Connection.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Connection.java
@@ -20,10 +20,12 @@
 
 import java.io.Closeable;
 
+import org.eclipse.jetty.util.Promise;
+
 /**
  * {@link Connection} represent a connection to a {@link Destination} and allow applications to send
  * requests via {@link #send(Request, Response.CompleteListener)}.
- * <p />
+ * <p>
  * {@link Connection}s are normally pooled by {@link Destination}s, but unpooled {@link Connection}s
  * may be created by applications that want to do their own connection management via
  * {@link Destination#newConnection(Promise)} and {@link Connection#close()}.
@@ -32,7 +34,7 @@
 {
     /**
      * Sends a request with an associated response listener.
-     * <p />
+     * <p>
      * {@link Request#send(Response.CompleteListener)} will eventually call this method to send the request.
      * It is exposed to allow applications to send requests via unpooled connections.
      *
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java
index 642a512..03754f7 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java
@@ -18,19 +18,28 @@
 
 package org.eclipse.jetty.client.api;
 
+import java.io.Closeable;
 import java.nio.ByteBuffer;
+import java.util.Iterator;
+
+import org.eclipse.jetty.client.util.ByteBufferContentProvider;
+import org.eclipse.jetty.client.util.PathContentProvider;
 
 /**
- * {@link ContentProvider} provides a source of request content.
- * <p/>
- * Implementations should return an {@link Iterator} over the request content.
+ * <p>{@link ContentProvider} provides a source of request content.</p>
+ * <p>Implementations should return an {@link Iterator} over the request content.
  * If the request content comes from a source that needs to be closed (for
- * example, an {@link InputStream}), then the iterator implementation class
+ * example, an {@link java.io.InputStream}), then the iterator implementation class
  * must implement {@link Closeable} and will be closed when the request is
- * completed (either successfully or failed).
- * <p/>
- * Applications should rely on utility classes such as {@link ByteBufferContentProvider}
- * or {@link PathContentProvider}.
+ * completed (either successfully or failed).</p>
+ * <p>Applications should rely on utility classes such as {@link ByteBufferContentProvider}
+ * or {@link PathContentProvider}.</p>
+ * <p>{@link ContentProvider} provides a {@link #getLength() length} of the content
+ * it represents.
+ * If the length is positive, it typically overrides any {@code Content-Length}
+ * header set by applications; if the length is negative, it typically removes
+ * any {@code Content-Length} header set by applications, resulting in chunked
+ * content (i.e. {@code Transfer-Encoding: chunked}) being sent to the server.</p>
  */
 public interface ContentProvider extends Iterable<ByteBuffer>
 {
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java
index 366a9e5..f9d1e21 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java
@@ -18,15 +18,17 @@
 
 package org.eclipse.jetty.client.api;
 
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.util.FuturePromise;
 import org.eclipse.jetty.util.Promise;
 
 /**
  * {@link Destination} represents the triple made of the {@link #getScheme}, the {@link #getHost}
  * and the {@link #getPort}.
- * <p />
+ * <p>
  * {@link Destination} holds a pool of {@link Connection}s, but allows to create unpooled
  * connections if the application wants full control over connection management via {@link #newConnection(Promise)}.
- * <p />
+ * <p>
  * {@link Destination}s may be obtained via {@link HttpClient#getDestination(String, String, int)}
  */
 public interface Destination
@@ -49,7 +51,7 @@
     /**
      * Creates asynchronously a new, unpooled, {@link Connection} that will be returned
      * at a later time through the given {@link Promise}.
-     * <p />
+     * <p>
      * Use {@link FuturePromise} to wait for the connection:
      * <pre>
      * Destination destination = ...;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java
index 50fa9a7..3dda9db 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.net.HttpCookie;
 import java.net.URI;
+import java.net.URLEncoder;
 import java.nio.ByteBuffer;
 import java.nio.file.Path;
 import java.util.EventListener;
@@ -30,6 +31,8 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.util.InputStreamResponseListener;
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpMethod;
@@ -202,6 +205,7 @@
 
     /**
      * @param content the content provider of this request
+     * @param contentType the content type
      * @return this request object
      */
     Request content(ContentProvider content, String contentType);
@@ -282,6 +286,7 @@
     /**
      * @param listenerClass the class of the listener, or null for all listeners classes
      * @return the listeners for request events of the given class
+     * @param <T> the type of listener class
      */
     <T extends RequestListener> List<T> getRequestListeners(Class<T> listenerClass);
 
@@ -383,17 +388,20 @@
 
     /**
      * Sends this request and returns the response.
-     * <p />
+     * <p>
      * This method should be used when a simple blocking semantic is needed, and when it is known
      * that the response content can be buffered without exceeding memory constraints.
-     * <p />
+     * <p>
      * For example, this method is not appropriate to download big files from a server; consider using
      * {@link #send(Response.CompleteListener)} instead, passing your own {@link Response.Listener} or a utility
      * listener such as {@link InputStreamResponseListener}.
-     * <p />
+     * <p>
      * The method returns when the {@link Response.CompleteListener complete event} is fired.
      *
      * @return a {@link ContentResponse} for this request
+     * @throws InterruptedException if send thread is interrupted
+     * @throws TimeoutException if send times out
+     * @throws ExecutionException if execution fails
      * @see Response.CompleteListener#onComplete(Result)
      */
     ContentResponse send() throws InterruptedException, TimeoutException, ExecutionException;
@@ -495,6 +503,7 @@
          * Callback method invoked when a chunk of request content has been sent successfully.
          * Changes to bytes in the given buffer have no effect, as the content has already been sent.
          * @param request the request that has been committed
+         * @param content the content
          */
         public void onContent(Request request, ByteBuffer content);
     }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java
index 989f879..0606698 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java
@@ -22,6 +22,7 @@
 import java.util.EventListener;
 import java.util.List;
 
+import org.eclipse.jetty.client.util.BufferingResponseListener;
 import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpVersion;
@@ -45,7 +46,9 @@
     Request getRequest();
 
     /**
-     * @return the response listener passed to {@link Request#send(CompleteListener)}
+     * @param listenerClass the listener class
+     * @return the response listener passed to {@link org.eclipse.jetty.client.api.Request#send(org.eclipse.jetty.client.api.Response.CompleteListener)}
+     * @param <T> the type of class
      */
     <T extends ResponseListener> List<T> getListeners(Class<T> listenerClass);
 
@@ -92,7 +95,7 @@
         /**
          * Callback method invoked when the response line containing HTTP version,
          * HTTP status code and reason has been received and parsed.
-         * <p />
+         * <p>
          * This method is the best approximation to detect when the first bytes of the response arrived to the client.
          *
          * @param response the response containing the response line data
@@ -192,9 +195,9 @@
         /**
          * Callback method invoked when the request <em><b>and</b></em> the response have been processed,
          * either successfully or not.
-         * <p/>
+         * <p>
          * The {@code result} parameter contains the request, the response, and eventual failures.
-         * <p/>
+         * <p>
          * Requests may complete <em>after</em> response, for example in case of big uploads that are
          * discarded or read asynchronously by the server.
          * This method is always invoked <em>after</em> {@link SuccessListener#onSuccess(Response)} or
@@ -245,7 +248,7 @@
                     onContent(response, content);
                     callback.succeeded();
                 }
-                catch (Exception x)
+                catch (Throwable x)
                 {
                     callback.failed(x);
                 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java
index 0a2a23f..cb4f4a4 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java
@@ -27,6 +27,7 @@
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpHeaderValue;
+import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpVersion;
 
 public class HttpChannelOverHTTP extends HttpChannel
@@ -96,26 +97,44 @@
 
         Response response = result.getResponse();
         HttpFields responseHeaders = response.getHeaders();
-        boolean close = result.isFailed() || receiver.isShutdown();
 
-        if (!close)
+        String closeReason = null;
+        if (result.isFailed())
+            closeReason = "failure";
+        else if (receiver.isShutdown())
+            closeReason = "server close";
+        else if (sender.isShutdown())
+            closeReason = "client close";
+
+        if (closeReason == null)
         {
             if (response.getVersion().compareTo(HttpVersion.HTTP_1_1) < 0)
             {
-                // HTTP 1.0 must close the connection unless it has an explicit keep alive.
-                close = !responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString());
+                // HTTP 1.0 must close the connection unless it has
+                // an explicit keep alive or it's a CONNECT method.
+                boolean keepAlive = responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString());
+                boolean connect = HttpMethod.CONNECT.is(exchange.getRequest().getMethod());
+                if (!keepAlive && !connect)
+                    closeReason = "http/1.0";
             }
             else
             {
-                // HTTP 1.1 or greater closes only if it has an explicit close.
-                close = responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
+                // HTTP 1.1 closes only if it has an explicit close.
+                if (responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()))
+                    closeReason = "http/1.1";
             }
         }
 
-        if (close)
+        if (closeReason != null)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Closing, reason: {} - {}", closeReason, connection);
             connection.close();
+        }
         else
+        {
             release();
+        }
     }
 
     @Override
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpClientTransportOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpClientTransportOverHTTP.java
index 22fa92f..931ca30 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpClientTransportOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpClientTransportOverHTTP.java
@@ -27,7 +27,9 @@
 import org.eclipse.jetty.client.api.Connection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.annotation.ManagedObject;
 
+@ManagedObject("The HTTP/1.1 client transport")
 public class HttpClientTransportOverHTTP extends AbstractHttpClientTransport
 {
     public HttpClientTransportOverHTTP()
@@ -55,20 +57,11 @@
         HttpConnectionOverHTTP connection = newHttpConnection(endPoint, destination, promise);
         if (LOG.isDebugEnabled())
             LOG.debug("Created {}", connection);
-        return connection;
+        return customize(connection, context);
     }
 
     protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
     {
         return new HttpConnectionOverHTTP(endPoint, destination, promise);
     }
-
-    /**
-     * @deprecated use {@link #newHttpConnection(EndPoint, HttpDestination, Promise)} instead
-     */
-    @Deprecated
-    protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination)
-    {
-        throw new UnsupportedOperationException("Deprecated, override newHttpConnection(EndPoint, HttpDestination, Promise<Connection>) instead");
-    }
 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
index f00a204..7e78aa8 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
@@ -26,6 +26,7 @@
 import org.eclipse.jetty.client.HttpConnection;
 import org.eclipse.jetty.client.HttpDestination;
 import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.SendFailure;
 import org.eclipse.jetty.client.api.Connection;
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.client.api.Response;
@@ -47,19 +48,9 @@
     private final HttpChannelOverHTTP channel;
     private long idleTimeout;
 
-    /**
-     * @deprecated use {@link #HttpConnectionOverHTTP(EndPoint, HttpDestination, Promise)} instead
-     */
-    @Deprecated
-    public HttpConnectionOverHTTP(EndPoint endPoint, HttpDestination destination)
-    {
-        this(endPoint, destination, new Promise.Adapter<Connection>());
-        throw new UnsupportedOperationException("Deprecated, use HttpConnectionOverHTTP(EndPoint, HttpDestination, Promise<Connection>) instead");
-    }
-
     public HttpConnectionOverHTTP(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
     {
-        super(endPoint, destination.getHttpClient().getExecutor(), destination.getHttpClient().isDispatchIO());
+        super(endPoint, destination.getHttpClient().getExecutor());
         this.promise = promise;
         this.delegate = new Delegate(destination);
         this.channel = newHttpChannel();
@@ -86,9 +77,9 @@
         delegate.send(request, listener);
     }
 
-    protected void send(HttpExchange exchange)
+    protected SendFailure send(HttpExchange exchange)
     {
-        delegate.send(exchange);
+        return delegate.send(exchange);
     }
 
     @Override
@@ -106,11 +97,12 @@
     }
 
     @Override
-    protected boolean onReadTimeout()
+    public boolean onIdleExpired()
     {
-        if (LOG.isDebugEnabled())
-            LOG.debug("{} idle timeout", this);
-        close(new TimeoutException());
+        long idleTimeout = getEndPoint().getIdleTimeout();
+        boolean close = delegate.onIdleTimeout(idleTimeout);
+        if (close)
+            close(new TimeoutException("Idle timeout " + idleTimeout + " ms"));
         return false;
     }
 
@@ -145,25 +137,19 @@
 
     protected void close(Throwable failure)
     {
-        if (softClose())
+        if (closed.compareAndSet(false, true))
         {
-            // First close then abort, to be sure that the connection cannot be reused
-            // from an onFailure() handler or by blocking code waiting for completion.
             getHttpDestination().close(this);
-            getEndPoint().shutdownOutput();
-            if (LOG.isDebugEnabled())
-                LOG.debug("{} oshut", this);
-            getEndPoint().close();
-            if (LOG.isDebugEnabled())
-                LOG.debug("{} closed", this);
 
             abort(failure);
-        }
-    }
 
-    public boolean softClose()
-    {
-        return closed.compareAndSet(false, true);
+            getEndPoint().shutdownOutput();
+            if (LOG.isDebugEnabled())
+                LOG.debug("Shutdown {}", this);
+            getEndPoint().close();
+            if (LOG.isDebugEnabled())
+                LOG.debug("Closed {}", this);
+        }
     }
 
     protected boolean abort(Throwable failure)
@@ -177,10 +163,8 @@
     {
         if (!closed.get())
             return false;
-
         if (sweeps.incrementAndGet() < 4)
             return false;
-
         return true;
     }
 
@@ -204,21 +188,18 @@
         }
 
         @Override
-        protected void send(HttpExchange exchange)
+        protected SendFailure send(HttpExchange exchange)
         {
             Request request = exchange.getRequest();
             normalizeRequest(request);
 
-            // Save the old idle timeout to restore it
+            // Save the old idle timeout to restore it.
             EndPoint endPoint = getEndPoint();
             idleTimeout = endPoint.getIdleTimeout();
             endPoint.setIdleTimeout(request.getIdleTimeout());
 
-            // One channel per connection, just delegate the send
-            if (channel.associate(exchange))
-                channel.send();
-            else
-                channel.release();
+            // One channel per connection, just delegate the send.
+            return send(channel, exchange);
         }
 
         @Override
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java
index 6177ece..cadc80d 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java
@@ -22,6 +22,7 @@
 import org.eclipse.jetty.client.HttpExchange;
 import org.eclipse.jetty.client.Origin;
 import org.eclipse.jetty.client.PoolingHttpDestination;
+import org.eclipse.jetty.client.SendFailure;
 
 public class HttpDestinationOverHTTP extends PoolingHttpDestination<HttpConnectionOverHTTP>
 {
@@ -31,8 +32,8 @@
     }
 
     @Override
-    protected void send(HttpConnectionOverHTTP connection, HttpExchange exchange)
+    protected SendFailure send(HttpConnectionOverHTTP connection, HttpExchange exchange)
     {
-        connection.send(exchange);
+        return connection.send(exchange);
     }
 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java
index ce39790..c006363 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java
@@ -29,13 +29,14 @@
 import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpParser;
+import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.CompletableCallback;
 
-public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
+public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.ResponseHandler
 {
     private final HttpParser parser = new HttpParser(this);
     private ByteBuffer buffer;
@@ -96,11 +97,13 @@
             EndPoint endPoint = connection.getEndPoint();
             while (true)
             {
-                // Connection may be closed in a parser callback.
-                if (connection.isClosed())
+                boolean upgraded = connection != endPoint.getConnection();
+
+                // Connection may be closed or upgraded in a parser callback.
+                if (connection.isClosed() || upgraded)
                 {
                     if (LOG.isDebugEnabled())
-                        LOG.debug("{} closed", connection);
+                        LOG.debug("{} {}", connection, upgraded ? "upgraded" : "closed");
                     releaseBuffer();
                     return;
                 }
@@ -163,7 +166,7 @@
 
     protected void fillInterested()
     {
-        getHttpChannel().getHttpConnection().fillInterested();
+        getHttpConnection().fillInterested();
     }
 
     private void shutdown()
@@ -202,20 +205,21 @@
             return false;
 
         String method = exchange.getRequest().getMethod();
-        parser.setHeadResponse(HttpMethod.HEAD.is(method) || HttpMethod.CONNECT.is(method));
+        parser.setHeadResponse(HttpMethod.HEAD.is(method) ||
+                (HttpMethod.CONNECT.is(method) && status == HttpStatus.OK_200));
         exchange.getResponse().version(version).status(status).reason(reason);
 
         return !responseBegin(exchange);
     }
 
     @Override
-    public boolean parsedHeader(HttpField field)
+    public void parsedHeader(HttpField field)
     {
         HttpExchange exchange = getHttpExchange();
         if (exchange == null)
-            return false;
+            return;
 
-        return !responseHeader(exchange, field);
+        responseHeader(exchange, field);
     }
 
     @Override
@@ -256,6 +260,13 @@
         return !proceed || async;
     }
 
+
+    @Override
+    public boolean contentComplete()
+    {
+        return false;
+    }
+    
     @Override
     public boolean messageComplete()
     {
@@ -263,7 +274,19 @@
         if (exchange == null)
             return false;
 
-        return !responseSuccess(exchange);
+        boolean proceed = responseSuccess(exchange);
+        if (!proceed)
+            return true;
+
+        int status = exchange.getResponse().getStatus();
+        if (status == HttpStatus.SWITCHING_PROTOCOLS_101)
+            return true;
+
+        if (HttpMethod.CONNECT.is(exchange.getRequest().getMethod()) &&
+                status == HttpStatus.OK_200)
+            return true;
+
+        return false;
     }
 
     @Override
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java
index 49fb84e..d397626 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java
@@ -28,13 +28,17 @@
 import org.eclipse.jetty.client.api.ContentProvider;
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.MetaData;
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IteratingCallback;
 
 public class HttpSenderOverHTTP extends HttpSender
 {
     private final HttpGenerator generator = new HttpGenerator();
+    private boolean shutdown;
 
     public HttpSenderOverHTTP(HttpChannelOverHTTP channel)
     {
@@ -50,77 +54,9 @@
     @Override
     protected void sendHeaders(HttpExchange exchange, HttpContent content, Callback callback)
     {
-        Request request = exchange.getRequest();
-        ContentProvider requestContent = request.getContent();
-        long contentLength = requestContent == null ? -1 : requestContent.getLength();
-        String path = request.getPath();
-        String query = request.getQuery();
-        if (query != null)
-            path += "?" + query;
-        HttpGenerator.RequestInfo requestInfo = new HttpGenerator.RequestInfo(request.getVersion(), request.getHeaders(), contentLength, request.getMethod(), path);
-
         try
         {
-            HttpClient client = getHttpChannel().getHttpDestination().getHttpClient();
-            ByteBufferPool bufferPool = client.getByteBufferPool();
-            ByteBuffer header = bufferPool.acquire(client.getRequestBufferSize(), false);
-            ByteBuffer chunk = null;
-
-            ByteBuffer contentBuffer = null;
-            boolean lastContent = false;
-            if (!expects100Continue(request))
-            {
-                content.advance();
-                contentBuffer = content.getByteBuffer();
-                lastContent = content.isLast();
-            }
-            while (true)
-            {
-                HttpGenerator.Result result = generator.generateRequest(requestInfo, header, chunk, contentBuffer, lastContent);
-                switch (result)
-                {
-                    case NEED_CHUNK:
-                    {
-                        chunk = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
-                        break;
-                    }
-                    case FLUSH:
-                    {
-                        int size = 1;
-                        boolean hasChunk = chunk != null;
-                        if (hasChunk)
-                            ++size;
-                        boolean hasContent = contentBuffer != null;
-                        if (hasContent)
-                            ++size;
-                        ByteBuffer[] toWrite = new ByteBuffer[size];
-                        ByteBuffer[] toRecycle = new ByteBuffer[hasChunk ? 2 : 1];
-                        toWrite[0] = header;
-                        toRecycle[0] = header;
-                        if (hasChunk)
-                        {
-                            toWrite[1] = chunk;
-                            toRecycle[1] = chunk;
-                        }
-                        if (hasContent)
-                            toWrite[toWrite.length - 1] = contentBuffer;
-                        EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint();
-                        endPoint.write(new ByteBufferRecyclerCallback(callback, bufferPool, toRecycle), toWrite);
-                        return;
-                    }
-                    case DONE:
-                    {
-                        // The headers have already been generated, perhaps by a concurrent abort.
-                        callback.failed(new HttpRequestException("Could not generate headers", request));
-                        return;
-                    }
-                    default:
-                    {
-                        callback.failed(new IllegalStateException(result.toString()));
-                        return;
-                    }
-                }
-            }
+            new HeadersCallback(exchange, content, callback).iterate();
         }
         catch (Throwable x)
         {
@@ -143,6 +79,10 @@
                 ByteBuffer contentBuffer = content.getByteBuffer();
                 boolean lastContent = content.isLast();
                 HttpGenerator.Result result = generator.generateRequest(null, null, chunk, contentBuffer, lastContent);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Generated content ({} bytes) - {}/{}",
+                            contentBuffer == null ? -1 : contentBuffer.remaining(),
+                            result, generator);
                 switch (result)
                 {
                     case NEED_CHUNK:
@@ -166,22 +106,24 @@
                     }
                     case CONTINUE:
                     {
-                        break;
+                        if (lastContent)
+                            break;
+                        callback.succeeded();
+                        return;
                     }
                     case DONE:
                     {
-                        assert generator.isEnd();
                         callback.succeeded();
                         return;
                     }
                     default:
                     {
-                        throw new IllegalStateException();
+                        throw new IllegalStateException(result.toString());
                     }
                 }
             }
         }
-        catch (Exception x)
+        catch (Throwable x)
         {
             if (LOG.isDebugEnabled())
                 LOG.debug(x);
@@ -206,7 +148,14 @@
 
     private void shutdownOutput()
     {
-        getHttpChannel().getHttpConnection().getEndPoint().shutdownOutput();
+        if (LOG.isDebugEnabled())
+            LOG.debug("Request shutdown output {}", getHttpExchange().getRequest());
+        shutdown = true;
+    }
+
+    protected boolean isShutdown()
+    {
+        return shutdown;
     }
 
     @Override
@@ -215,15 +164,156 @@
         return String.format("%s[%s]", super.toString(), generator);
     }
 
-    private class ByteBufferRecyclerCallback implements Callback
+    private class HeadersCallback extends IteratingCallback
     {
+        private final HttpExchange exchange;
         private final Callback callback;
+        private final MetaData.Request metaData;
+        private ByteBuffer headerBuffer;
+        private ByteBuffer chunkBuffer;
+        private ByteBuffer contentBuffer;
+        private boolean lastContent;
+        private boolean generated;
+
+        public HeadersCallback(HttpExchange exchange, HttpContent content, Callback callback)
+        {
+            super(false);
+            this.exchange = exchange;
+            this.callback = callback;
+
+            Request request = exchange.getRequest();
+            ContentProvider requestContent = request.getContent();
+            long contentLength = requestContent == null ? -1 : requestContent.getLength();
+            String path = request.getPath();
+            String query = request.getQuery();
+            if (query != null)
+                path += "?" + query;
+            metaData = new MetaData.Request(request.getMethod(), new HttpURI(path), request.getVersion(), request.getHeaders(), contentLength);
+
+            if (!expects100Continue(request))
+            {
+                content.advance();
+                contentBuffer = content.getByteBuffer();
+                lastContent = content.isLast();
+            }
+        }
+
+        @Override
+        protected Action process() throws Exception
+        {
+            HttpClient client = getHttpChannel().getHttpDestination().getHttpClient();
+            ByteBufferPool bufferPool = client.getByteBufferPool();
+
+            while (true)
+            {
+                HttpGenerator.Result result = generator.generateRequest(metaData, headerBuffer, chunkBuffer, contentBuffer, lastContent);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Generated headers ({} bytes), chunk ({} bytes), content ({} bytes) - {}/{}",
+                            headerBuffer == null ? -1 : headerBuffer.remaining(),
+                            chunkBuffer == null ? -1 : chunkBuffer.remaining(),
+                            contentBuffer == null ? -1 : contentBuffer.remaining(),
+                            result, generator);
+                switch (result)
+                {
+                    case NEED_HEADER:
+                    {
+                        headerBuffer = bufferPool.acquire(client.getRequestBufferSize(), false);
+                        break;
+                    }
+                    case NEED_CHUNK:
+                    {
+                        chunkBuffer = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
+                        break;
+                    }
+                    case FLUSH:
+                    {
+                        EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint();
+                        if (chunkBuffer == null)
+                        {
+                            if (contentBuffer == null)
+                                endPoint.write(this, headerBuffer);
+                            else
+                                endPoint.write(this, headerBuffer, contentBuffer);
+                        }
+                        else
+                        {
+                            if (contentBuffer == null)
+                                endPoint.write(this, headerBuffer, chunkBuffer);
+                            else
+                                endPoint.write(this, headerBuffer, chunkBuffer, contentBuffer);
+                        }
+                        generated = true;
+                        return Action.SCHEDULED;
+                    }
+                    case SHUTDOWN_OUT:
+                    {
+                        shutdownOutput();
+                        return Action.SUCCEEDED;
+                    }
+                    case CONTINUE:
+                    {
+                        if (generated)
+                            return Action.SUCCEEDED;
+                        break;
+                    }
+                    case DONE:
+                    {
+                        if (generated)
+                            return Action.SUCCEEDED;
+                        // The headers have already been generated by some
+                        // other thread, perhaps by a concurrent abort().
+                        throw new HttpRequestException("Could not generate headers", exchange.getRequest());
+                    }
+                    default:
+                    {
+                        throw new IllegalStateException(result.toString());
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void succeeded()
+        {
+            release();
+            super.succeeded();
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            release();
+            callback.failed(x);
+            super.failed(x);
+        }
+
+        @Override
+        protected void onCompleteSuccess()
+        {
+            super.onCompleteSuccess();
+            callback.succeeded();
+        }
+
+        private void release()
+        {
+            HttpClient client = getHttpChannel().getHttpDestination().getHttpClient();
+            ByteBufferPool bufferPool = client.getByteBufferPool();
+            bufferPool.release(headerBuffer);
+            headerBuffer = null;
+            if (chunkBuffer != null)
+                bufferPool.release(chunkBuffer);
+            chunkBuffer = null;
+        }
+    }
+
+    private class ByteBufferRecyclerCallback extends Callback.Nested
+    {
         private final ByteBufferPool pool;
         private final ByteBuffer[] buffers;
 
         private ByteBufferRecyclerCallback(Callback callback, ByteBufferPool pool, ByteBuffer... buffers)
         {
-            this.callback = callback;
+            super(callback);
             this.pool = pool;
             this.buffers = buffers;
         }
@@ -236,7 +326,7 @@
                 assert !buffer.hasRemaining();
                 pool.release(buffer);
             }
-            callback.succeeded();
+            super.succeeded();
         }
 
         @Override
@@ -244,7 +334,7 @@
         {
             for (ByteBuffer buffer : buffers)
                 pool.release(buffer);
-            callback.failed(x);
+            super.failed(x);
         }
     }
 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/package-info.java b/jetty-client/src/main/java/org/eclipse/jetty/client/package-info.java
index e746ad8..3754b3f 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/package-info.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/package-info.java
@@ -20,32 +20,34 @@
  * Jetty Client : Implementation and Core Classes
  * 
  * This package provides APIs, utility classes and an implementation of an asynchronous HTTP client.
- * <p />
- * The core class is {@link org.eclipse.jetty.client.api.HttpClient}, which acts as a central configuration object (for example
- * for {@link org.eclipse.jetty.client.api.HttpClient#setIdleTimeout(long) idle timeouts}, {@link org.eclipse.jetty.client.api.HttpClient#setMaxConnectionsPerDestination(int)
- * max connections per destination}, etc.) and as a factory for {@link Request} objects.
- * <p />
+ * <p>
+ * The core class is {@link org.eclipse.jetty.client.HttpClient}, which acts as a central configuration object (for example
+ * for {@link org.eclipse.jetty.client.HttpClient#setIdleTimeout(long) idle timeouts}, {@link org.eclipse.jetty.client.HttpClient#setMaxConnectionsPerDestination(int)
+ * max connections per destination}, etc.) and as a factory for {@link org.eclipse.jetty.client.api.Request} objects.
+ * <p>
  * The HTTP protocol is based on the request/response paradigm, a unit that in this implementation is called
- * <em>exchange</em> and is represented by {@link org.eclipse.jetty.client.api.HttpExchange}.
+ * <em>exchange</em> and is represented by {@link org.eclipse.jetty.client.HttpExchange}.
  * An initial request may trigger a sequence of exchanges with one or more servers, called a <em>conversation</em>
- * and represented by {@link org.eclipse.jetty.client.api.HttpConversation}. A typical example of a conversation is a redirect, where
+ * and represented by {@link org.eclipse.jetty.client.HttpConversation}. A typical example of a conversation is a redirect, where
  * upon a request for a resource URI, the server replies with a redirect (for example with the 303 status code)
  * to another URI. This conversation is made of a first exchange made of the original request and its 303 response,
  * and of a second exchange made of the request for the new URI and its 200 response.
- * <p />
- * {@link org.eclipse.jetty.client.api.HttpClient} holds a number of {@link org.eclipse.jetty.client.api.HttpDestination destinations}, which in turn hold a number of
- * pooled {@link org.eclipse.jetty.client.api.HttpConnection connections}.
- * <p />
+ * <p>
+ * {@link org.eclipse.jetty.client.HttpClient} holds a number of {@link org.eclipse.jetty.client.api.Destination destinations}, which in turn hold a number of
+ * pooled {@link org.eclipse.jetty.client.api.Connection connections}.
+ * <p>
  * When a request is sent, its exchange is associated to a connection, either taken from an idle queue or created
  * anew, and when both the request and response are completed, the exchange is disassociated from the connection.
  * Conversations may span multiple connections on different destinations, and therefore are maintained at the
- * {@link org.eclipse.jetty.client.api.HttpClient} level.
- * <p />
+ * {@link org.eclipse.jetty.client.HttpClient} level.
+ * <p>
  * Applications may decide to send the request and wait for the response in a blocking way, using
  * {@link org.eclipse.jetty.client.api.Request#send()}.
  * Alternatively, application may ask to be notified of response events asynchronously, using
- * {@link org.eclipse.jetty.client.api.Request#send(Response.Listener)}.
+ * {@link org.eclipse.jetty.client.api.Request#send(org.eclipse.jetty.client.api.Response.CompleteListener)}.
  */
 package org.eclipse.jetty.client;
 
+import org.eclipse.jetty.client.api.Response;
+
 
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractAuthentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractAuthentication.java
index 2cc3c7a..64eb215 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractAuthentication.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractAuthentication.java
@@ -52,7 +52,7 @@
         if (!getType().equalsIgnoreCase(type))
             return false;
 
-        if (!this.realm.equals(realm))
+        if (!this.realm.equals(ANY_REALM) && !this.realm.equals(realm))
             return false;
 
         return matchesURI(this.uri, uri);
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BasicAuthentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BasicAuthentication.java
index 7c816cb..93a184d 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BasicAuthentication.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BasicAuthentication.java
@@ -31,7 +31,7 @@
 
 /**
  * Implementation of the HTTP "Basic" authentication defined in RFC 2617.
- * <p />
+ * <p>
  * Applications should create objects of this class and add them to the
  * {@link AuthenticationStore} retrieved from the {@link HttpClient}
  * via {@link HttpClient#getAuthenticationStore()}.
@@ -63,25 +63,41 @@
     @Override
     public Result authenticate(Request request, ContentResponse response, HeaderInfo headerInfo, Attributes context)
     {
-        String value = "Basic " + B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1);
-        return new BasicResult(headerInfo.getHeader(), value);
+        return new BasicResult(getURI(), headerInfo.getHeader(), user, password);
     }
 
-    private class BasicResult implements Result
+    /**
+     * Basic authentication result.
+     * <p>
+     * Application may utilize this class directly via
+     * {@link AuthenticationStore#addAuthenticationResult(Result)}
+     * to perform preemptive authentication, that is immediately
+     * sending the authorization header based on the fact that the
+     * URI is known to require authentication and that username
+     * and password are known a priori.
+     */
+    public static class BasicResult implements Result
     {
+        private final URI uri;
         private final HttpHeader header;
         private final String value;
 
-        public BasicResult(HttpHeader header, String value)
+        public BasicResult(URI uri, String user, String password)
         {
+            this(uri, HttpHeader.AUTHORIZATION, user, password);
+        }
+
+        public BasicResult(URI uri, HttpHeader header, String user, String password)
+        {
+            this.uri = uri;
             this.header = header;
-            this.value = value;
+            this.value = "Basic " + B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1);
         }
 
         @Override
         public URI getURI()
         {
-            return BasicAuthentication.this.getURI();
+            return uri;
         }
 
         @Override
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferContentProvider.java
index 0784f45..6838675 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferContentProvider.java
@@ -22,9 +22,11 @@
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
+import org.eclipse.jetty.client.api.ContentProvider;
+
 /**
  * A {@link ContentProvider} for {@link ByteBuffer}s.
- * <p />
+ * <p>
  * The position and limit of the {@link ByteBuffer}s passed to the constructor are not modified,
  * and each invocation of the {@link #iterator()} method returns a {@link ByteBuffer#slice() slice}
  * of the original {@link ByteBuffer}.
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesContentProvider.java
index a54ef34..d2c080a 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesContentProvider.java
@@ -22,6 +22,8 @@
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
+import org.eclipse.jetty.client.api.ContentProvider;
+
 /**
  * A {@link ContentProvider} for byte arrays.
  */
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DeferredContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DeferredContentProvider.java
index b8223c3..7f40afd 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DeferredContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DeferredContentProvider.java
@@ -22,7 +22,9 @@
 import java.io.IOException;
 import java.io.InterruptedIOException;
 import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Deque;
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
@@ -35,18 +37,17 @@
 import org.eclipse.jetty.client.api.ContentProvider;
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.util.ArrayQueue;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.Callback;
 
 /**
  * A {@link ContentProvider} that allows to add content after {@link Request#send(Response.CompleteListener)}
  * has been called, therefore providing the request content at a later time.
- * <p />
+ * <p>
  * {@link DeferredContentProvider} can only be used in conjunction with
  * {@link Request#send(Response.CompleteListener)} (and not with its blocking counterpart {@link Request#send()})
  * because it provides content asynchronously.
- * <p />
+ * <p>
  * The deferred content is provided once and then fully consumed.
  * Invocations to the {@link #iterator()} method after the first will return an "empty" iterator
  * because the stream has been consumed on the first invocation.
@@ -55,13 +56,13 @@
  * of of {@link #iterator()} returning the iterator provided by this
   * class on the first invocation, and an iterator on the bytes copied to the other location
   * for subsequent invocations.
- * <p />
+ * <p>
  * Typical usage of {@link DeferredContentProvider} is in asynchronous proxies, where HTTP headers arrive
  * separately from HTTP content chunks.
- * <p />
+ * <p>
  * The deferred content must be provided through {@link #offer(ByteBuffer)}, which can be invoked multiple
  * times, and when all content has been provided it must be signaled with a call to {@link #close()}.
- * <p />
+ * <p>
  * Example usage:
  * <pre>
  * HttpClient httpClient = ...;
@@ -73,7 +74,7 @@
  *             .content(content)
  *             .send(new Response.CompleteListener()
  *             {
- *                 &#64Override
+ *                 &#64;Override
  *                 public void onComplete(Result result)
  *                 {
  *                     // Your logic here
@@ -87,10 +88,10 @@
  */
 public class DeferredContentProvider implements AsyncContentProvider, Callback, Closeable
 {
-    private static final Chunk CLOSE = new Chunk(BufferUtil.EMPTY_BUFFER, Callback.Adapter.INSTANCE);
+    private static final Chunk CLOSE = new Chunk(BufferUtil.EMPTY_BUFFER, Callback.NOOP);
 
     private final Object lock = this;
-    private final ArrayQueue<Chunk> chunks = new ArrayQueue<>(4, 64, lock);
+    private final Deque<Chunk> chunks = new ArrayDeque<>();
     private final AtomicReference<Listener> listener = new AtomicReference<>();
     private final DeferredContentProviderIterator iterator = new DeferredContentProviderIterator();
     private final AtomicBoolean closed = new AtomicBoolean();
@@ -143,7 +144,7 @@
      */
     public boolean offer(ByteBuffer buffer)
     {
-        return offer(buffer, Callback.Adapter.INSTANCE);
+        return offer(buffer, Callback.NOOP);
     }
 
     public boolean offer(ByteBuffer buffer, Callback callback)
@@ -218,11 +219,6 @@
     }
 
     @Override
-    public void succeeded()
-    {
-    }
-
-    @Override
     public void failed(Throwable failure)
     {
         iterator.failed(failure);
@@ -264,7 +260,7 @@
                 {
                     // Slow path: reinsert the CLOSE chunk
                     // so that hasNext() works correctly.
-                    chunks.add(0, CLOSE);
+                    chunks.offerFirst(CLOSE);
                     throw new NoSuchElementException();
                 }
                 return chunk == null ? null : chunk.buffer;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java
index ce9162a..3238cb3 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java
@@ -43,7 +43,7 @@
 
 /**
  * Implementation of the HTTP "Digest" authentication defined in RFC 2617.
- * <p />
+ * <p>
  * Applications should create objects of this class and add them to the
  * {@link AuthenticationStore} retrieved from the {@link HttpClient}
  * via {@link HttpClient#getAuthenticationStore()}.
@@ -99,7 +99,10 @@
                 clientQOP = "auth-int";
         }
 
-        return new DigestResult(headerInfo.getHeader(), response.getContent(), getRealm(), user, password, algorithm, nonce, clientQOP, opaque);
+        String realm = getRealm();
+        if (ANY_REALM.equals(realm))
+            realm = headerInfo.getRealm();
+        return new DigestResult(headerInfo.getHeader(), response.getContent(), realm, user, password, algorithm, nonce, clientQOP, opaque);
     }
 
     private Map<String, String> parseParameters(String wwwAuthenticate)
@@ -208,7 +211,8 @@
             String A1 = user + ":" + realm + ":" + password;
             String hashA1 = toHexString(digester.digest(A1.getBytes(StandardCharsets.ISO_8859_1)));
 
-            String A2 = request.getMethod() + ":" + request.getURI();
+            URI uri = request.getURI();
+            String A2 = request.getMethod() + ":" + uri;
             if ("auth-int".equals(qop))
                 A2 += ":" + toHexString(digester.digest(content));
             String hashA2 = toHexString(digester.digest(A2.getBytes(StandardCharsets.ISO_8859_1)));
@@ -237,7 +241,7 @@
             if (opaque != null)
                 value.append(", opaque=\"").append(opaque).append("\"");
             value.append(", algorithm=\"").append(algorithm).append("\"");
-            value.append(", uri=\"").append(request.getURI()).append("\"");
+            value.append(", uri=\"").append(uri).append("\"");
             if (qop != null)
             {
                 value.append(", qop=\"").append(qop).append("\"");
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/FormContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/FormContentProvider.java
index 955c916..5df04cf 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/FormContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/FormContentProvider.java
@@ -24,6 +24,7 @@
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.UnsupportedCharsetException;
 
+import org.eclipse.jetty.client.api.ContentProvider;
 import org.eclipse.jetty.util.Fields;
 
 /**
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/FutureResponseListener.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/FutureResponseListener.java
index 4b5314e..12d280a 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/FutureResponseListener.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/FutureResponseListener.java
@@ -34,7 +34,7 @@
  * A {@link BufferingResponseListener} that is also a {@link Future}, to allow applications
  * to block (indefinitely or for a timeout) until {@link #onComplete(Result)} is called,
  * or to {@link #cancel(boolean) abort} the request/response conversation.
- * <p />
+ * <p>
  * Typical usage is:
  * <pre>
  * Request request = httpClient.newRequest(...)...;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamContentProvider.java
index aee09fc..cdca1f8 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamContentProvider.java
@@ -33,20 +33,20 @@
 
 /**
  * A {@link ContentProvider} for an {@link InputStream}.
- * <p />
+ * <p>
  * The input stream is read once and therefore fully consumed.
  * Invocations to the {@link #iterator()} method after the first will return an "empty" iterator
  * because the stream has been consumed on the first invocation.
- * <p />
+ * <p>
  * However, it is possible for subclasses to override {@link #onRead(byte[], int, int)} to copy
  * the content read from the stream to another location (for example a file), and be able to
  * support multiple invocations of {@link #iterator()}, returning the iterator provided by this
  * class on the first invocation, and an iterator on the bytes copied to the other location
  * for subsequent invocations.
- * <p />
+ * <p>
  * It is possible to specify, at the constructor, a buffer size used to read content from the
  * stream, by default 4096 bytes.
- * <p />
+ * <p>
  * The {@link InputStream} passed to the constructor is by default closed when is it fully
  * consumed (or when an exception is thrown while reading it), unless otherwise specified
  * to the {@link #InputStreamContentProvider(java.io.InputStream, int, boolean) constructor}.
@@ -87,7 +87,7 @@
      * Callback method invoked just after having read from the stream,
      * but before returning the iteration element (a {@link ByteBuffer}
      * to the caller.
-     * <p />
+     * <p>
      * Subclasses may override this method to copy the content read from
      * the stream to another location (a file, or in memory if the content
      * is known to fit).
@@ -137,11 +137,6 @@
     }
 
     @Override
-    public void succeeded()
-    {
-    }
-
-    @Override
     public void failed(Throwable failure)
     {
         // TODO: forward the failure to the iterator.
@@ -154,12 +149,12 @@
      * means that stream reading must be performed by {@link #hasNext()}, which introduces a side-effect
      * on what is supposed to be a simple query method (with respect to the Query Command Separation
      * Principle).
-     * <p />
+     * <p>
      * Alternatively, we could return {@code true} from {@link #hasNext()} even if we don't know that
      * we will read -1, but then when {@link #next()} reads -1 it must return an empty buffer.
      * However this is problematic, since GETs with no content indication would become GET with chunked
      * content, and not understood by servers.
-     * <p />
+     * <p>
      * Therefore we need to make sure that {@link #hasNext()} does not perform any side effect (so that
      * it can be called multiple times) until {@link #next()} is called.
      */
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamResponseListener.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamResponseListener.java
index b9f2c53..5530b38 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamResponseListener.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamResponseListener.java
@@ -23,18 +23,23 @@
 import java.io.InterruptedIOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.AsynchronousCloseException;
-import java.util.concurrent.BlockingQueue;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Queue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.api.Response;
 import org.eclipse.jetty.client.api.Response.Listener;
 import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -42,7 +47,7 @@
 /**
  * Implementation of {@link Listener} that produces an {@link InputStream}
  * that allows applications to read the response content.
- * <p />
+ * <p>
  * Typical usage is:
  * <pre>
  * InputStreamResponseListener listener = new InputStreamResponseListener();
@@ -59,12 +64,10 @@
  *     }
  * }
  * </pre>
- * <p />
+ * <p>
  * The {@link HttpClient} implementation (the producer) will feed the input stream
  * asynchronously while the application (the consumer) is reading from it.
- * Chunks of content are maintained in a queue, and it is possible to specify a
- * maximum buffer size for the bytes held in the queue, by default 16384 bytes.
- * <p />
+ * <p>
  * If the consumer is faster than the producer, then the consumer will block
  * with the typical {@link InputStream#read()} semantic.
  * If the consumer is slower than the producer, then the producer will block
@@ -73,142 +76,137 @@
 public class InputStreamResponseListener extends Listener.Adapter
 {
     private static final Logger LOG = Log.getLogger(InputStreamResponseListener.class);
-    private static final byte[] EOF = new byte[0];
-    private static final byte[] CLOSED = new byte[0];
-    private static final byte[] FAILURE = new byte[0];
-    private final BlockingQueue<byte[]> queue = new LinkedBlockingQueue<>();
-    private final AtomicLong length = new AtomicLong();
+    private static final DeferredContentProvider.Chunk EOF = new DeferredContentProvider.Chunk(BufferUtil.EMPTY_BUFFER, Callback.NOOP);
+    private final Object lock = this;
     private final CountDownLatch responseLatch = new CountDownLatch(1);
     private final CountDownLatch resultLatch = new CountDownLatch(1);
     private final AtomicReference<InputStream> stream = new AtomicReference<>();
-    private final long maxBufferSize;
+    private final Queue<DeferredContentProvider.Chunk> chunks = new ArrayDeque<>();
     private Response response;
     private Result result;
-    private volatile Throwable failure;
-    private volatile boolean closed;
+    private Throwable failure;
+    private boolean closed;
 
     public InputStreamResponseListener()
     {
-        this(16 * 1024L);
     }
 
+    /**
+     * @deprecated response content is not buffered anymore, but handled asynchronously.
+     */
+    @Deprecated
     public InputStreamResponseListener(long maxBufferSize)
     {
-        this.maxBufferSize = maxBufferSize;
     }
 
     @Override
     public void onHeaders(Response response)
     {
-        this.response = response;
-        responseLatch.countDown();
+        synchronized (lock)
+        {
+            this.response = response;
+            responseLatch.countDown();
+        }
     }
 
     @Override
-    public void onContent(Response response, ByteBuffer content)
+    public void onContent(Response response, ByteBuffer content, Callback callback)
     {
-        if (!closed)
+        if (content.remaining() == 0)
         {
-            int remaining = content.remaining();
-            if (remaining > 0)
-            {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Skipped empty content {}", content);
+            callback.succeeded();
+            return;
+        }
 
-                byte[] bytes = new byte[remaining];
-                content.get(bytes);
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Queuing {}/{} bytes", bytes, remaining);
-                queue.offer(bytes);
-
-                long newLength = length.addAndGet(remaining);
-                while (newLength >= maxBufferSize)
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Queued bytes limit {}/{} exceeded, waiting", newLength, maxBufferSize);
-                    // Block to avoid infinite buffering
-                    if (!await())
-                        break;
-                    newLength = length.get();
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Queued bytes limit {}/{} exceeded, woken up", newLength, maxBufferSize);
-                }
-            }
-            else
+        boolean closed;
+        synchronized (lock)
+        {
+            closed = this.closed;
+            if (!closed)
             {
                 if (LOG.isDebugEnabled())
-                    LOG.debug("Queuing skipped, empty content {}", content);
+                    LOG.debug("Queueing content {}", content);
+                chunks.add(new DeferredContentProvider.Chunk(content, callback));
+                lock.notifyAll();
             }
         }
-        else
+
+        if (closed)
         {
-            LOG.debug("Queuing skipped, stream already closed");
+            if (LOG.isDebugEnabled())
+                LOG.debug("InputStream closed, ignored content {}", content);
+            callback.failed(new AsynchronousCloseException());
         }
     }
 
     @Override
     public void onSuccess(Response response)
     {
+        synchronized (lock)
+        {
+            if (!closed)
+                chunks.add(EOF);
+            lock.notifyAll();
+        }
+
         if (LOG.isDebugEnabled())
-            LOG.debug("Queuing end of content {}{}", EOF, "");
-        queue.offer(EOF);
-        signal();
+            LOG.debug("End of content");
     }
 
     @Override
     public void onFailure(Response response, Throwable failure)
     {
-        fail(failure);
-        signal();
+        List<Callback> callbacks;
+        synchronized (lock)
+        {
+            if (this.failure != null)
+                return;
+            this.failure = failure;
+            callbacks = drain();
+            lock.notifyAll();
+        }
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("Content failure", failure);
+
+        callbacks.forEach(callback -> callback.failed(failure));
     }
 
     @Override
     public void onComplete(Result result)
     {
-        if (result.isFailed() && failure == null)
-            fail(result.getFailure());
-        this.result = result;
-        resultLatch.countDown();
-        signal();
-    }
-
-    private void fail(Throwable failure)
-    {
-        if (LOG.isDebugEnabled())
-            LOG.debug("Queuing failure {} {}", FAILURE, failure);
-        queue.offer(FAILURE);
-        this.failure = failure;
-        responseLatch.countDown();
-    }
-
-    protected boolean await()
-    {
-        try
+        Throwable failure = result.getFailure();
+        List<Callback> callbacks = Collections.emptyList();
+        synchronized (lock)
         {
-            synchronized (this)
+            this.result = result;
+            if (result.isFailed() && this.failure == null)
             {
-                while (length.get() >= maxBufferSize && failure == null && !closed)
-                    wait();
-                // Re-read the values as they may have changed while waiting.
-                return failure == null && !closed;
+                this.failure = failure;
+                callbacks = drain();
             }
+            // Notify the response latch in case of request failures.
+            responseLatch.countDown();
+            resultLatch.countDown();
+            lock.notifyAll();
         }
-        catch (InterruptedException x)
-        {
-            Thread.currentThread().interrupt();
-            return false;
-        }
-    }
 
-    protected void signal()
-    {
-        synchronized (this)
+        if (LOG.isDebugEnabled())
         {
-            notifyAll();
+            if (failure == null)
+                LOG.debug("Result success");
+            else
+                LOG.debug("Result failure", failure);
         }
+
+        callbacks.forEach(callback -> callback.failed(failure));
     }
 
     /**
      * Waits for the given timeout for the response to be available, then returns it.
-     * <p />
+     * <p>
      * The wait ends as soon as all the HTTP headers have been received, without waiting for the content.
      * To wait for the whole content, see {@link #await(long, TimeUnit)}.
      *
@@ -224,15 +222,19 @@
         boolean expired = !responseLatch.await(timeout, unit);
         if (expired)
             throw new TimeoutException();
-        if (failure != null)
-            throw new ExecutionException(failure);
-        return response;
+        synchronized (lock)
+        {
+            // If the request failed there is no response.
+            if (response == null)
+                throw new ExecutionException(failure);
+            return response;
+        }
     }
 
     /**
      * Waits for the given timeout for the whole request/response cycle to be finished,
      * then returns the corresponding result.
-     * <p />
+     * <p>
      *
      * @param timeout the time to wait
      * @param unit the timeout unit
@@ -246,12 +248,15 @@
         boolean expired = !resultLatch.await(timeout, unit);
         if (expired)
             throw new TimeoutException();
-        return result;
+        synchronized (lock)
+        {
+            return result;
+        }
     }
 
     /**
      * Returns an {@link InputStream} providing the response content bytes.
-     * <p />
+     * <p>
      * The method may be invoked only once; subsequent invocations will return a closed {@link InputStream}.
      *
      * @return an input stream providing the response content
@@ -264,67 +269,75 @@
         return IO.getClosedStream();
     }
 
-    private class Input extends InputStream
+    private List<Callback> drain()
     {
-        private byte[] bytes;
-        private int index;
-
-        @Override
-        public int read() throws IOException
+        List<Callback> callbacks = new ArrayList<>();
+        synchronized (lock)
         {
             while (true)
             {
-                if (bytes == EOF)
-                {
-                    // Mark the fact that we saw -1,
-                    // so that in the close case we don't throw
-                    index = -1;
-                    return -1;
-                }
-                else if (bytes == FAILURE)
-                {
-                    throw failure();
-                }
-                else if (bytes == CLOSED)
-                {
-                    if (index < 0)
-                        return -1;
-                    throw new AsynchronousCloseException();
-                }
-                else if (bytes != null)
-                {
-                    int result = bytes[index] & 0xFF;
-                    if (++index == bytes.length)
-                    {
-                        length.addAndGet(-index);
-                        bytes = null;
-                        index = 0;
-                        signal();
-                    }
-                    return result;
-                }
-                else
-                {
-                    bytes = take();
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Dequeued {}/{} bytes", bytes, bytes.length);
-                }
+                DeferredContentProvider.Chunk chunk = chunks.peek();
+                if (chunk == null || chunk == EOF)
+                    break;
+                callbacks.add(chunk.callback);
+                chunks.poll();
             }
         }
+        return callbacks;
+    }
 
-        private IOException failure()
+    private class Input extends InputStream
+    {
+        @Override
+        public int read() throws IOException
         {
-            if (failure instanceof IOException)
-                return (IOException)failure;
-            else
-                return new IOException(failure);
+            byte[] tmp = new byte[1];
+            int read = read(tmp);
+            if (read < 0)
+                return read;
+            return tmp[0] & 0xFF;
         }
 
-        private byte[] take() throws IOException
+        @Override
+        public int read(byte[] b, int offset, int length) throws IOException
         {
             try
             {
-                return queue.take();
+                int result;
+                Callback callback = null;
+                synchronized (lock)
+                {
+                    DeferredContentProvider.Chunk chunk;
+                    while (true)
+                    {
+                        chunk = chunks.peek();
+                        if (chunk == EOF)
+                            return -1;
+
+                        if (chunk != null)
+                            break;
+
+                        if (failure != null)
+                            throw toIOException(failure);
+
+                        if (closed)
+                            throw new AsynchronousCloseException();
+
+                        lock.wait();
+                    }
+
+                    ByteBuffer buffer = chunk.buffer;
+                    result = Math.min(buffer.remaining(), length);
+                    buffer.get(b, offset, result);
+                    if (!buffer.hasRemaining())
+                    {
+                        callback = chunk.callback;
+                        chunks.poll();
+                    }
+                }
+                if (callback != null)
+                    callback.succeeded();
+                return result;
             }
             catch (InterruptedException x)
             {
@@ -332,18 +345,34 @@
             }
         }
 
+        private IOException toIOException(Throwable failure)
+        {
+            if (failure instanceof IOException)
+                return (IOException)failure;
+            else
+                return new IOException(failure);
+        }
+
         @Override
         public void close() throws IOException
         {
-            if (!closed)
+            List<Callback> callbacks;
+            synchronized (lock)
             {
-                super.close();
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Queuing close {}{}", CLOSED, "");
-                queue.offer(CLOSED);
+                if (closed)
+                    return;
                 closed = true;
-                signal();
+                callbacks = drain();
+                lock.notifyAll();
             }
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("InputStream close");
+
+            Throwable failure = new AsynchronousCloseException();
+            callbacks.forEach(callback -> callback.failed(failure));
+
+            super.close();
         }
     }
 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java
new file mode 100644
index 0000000..f699070
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java
@@ -0,0 +1,406 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.jetty.client.AsyncContentProvider;
+import org.eclipse.jetty.client.Synchronizable;
+import org.eclipse.jetty.client.api.ContentProvider;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * <p>A {@link ContentProvider} for form uploads with the {@code "multipart/form-data"}
+ * content type.</p>
+ * <p>Example usage:</p>
+ * <pre>
+ * MultiPartContentProvider multiPart = new MultiPartContentProvider();
+ * multiPart.addFieldPart("field", new StringContentProvider("foo"), null);
+ * multiPart.addFilePart("icon", "img.png", new PathContentProvider(Paths.get("/tmp/img.png")), null);
+ * multiPart.close();
+ * ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ *         .method(HttpMethod.POST)
+ *         .content(multiPart)
+ *         .send();
+ * </pre>
+ * <p>The above example would be the equivalent of submitting this form:</p>
+ * <pre>
+ * &lt;form method="POST" enctype="multipart/form-data"  accept-charset="UTF-8"&gt;
+ *     &lt;input type="text" name="field" value="foo" /&gt;
+ *     &lt;input type="file" name="icon" /&gt;
+ * &lt;/form&gt;
+ * </pre>
+ */
+public class MultiPartContentProvider extends AbstractTypedContentProvider implements AsyncContentProvider, Closeable
+{
+    private static final Logger LOG = Log.getLogger(MultiPartContentProvider.class);
+    private static final byte[] COLON_SPACE_BYTES = new byte[]{':', ' '};
+    private static final byte[] CR_LF_BYTES = new byte[]{'\r', '\n'};
+
+    private final List<Part> parts = new ArrayList<>();
+    private final ByteBuffer firstBoundary;
+    private final ByteBuffer middleBoundary;
+    private final ByteBuffer onlyBoundary;
+    private final ByteBuffer lastBoundary;
+    private final AtomicBoolean closed = new AtomicBoolean();
+    private Listener listener;
+    private long length = -1;
+
+    public MultiPartContentProvider()
+    {
+        this(makeBoundary());
+    }
+
+    public MultiPartContentProvider(String boundary)
+    {
+        super("multipart/form-data; boundary=" + boundary);
+        String firstBoundaryLine = "--" + boundary + "\r\n";
+        this.firstBoundary = ByteBuffer.wrap(firstBoundaryLine.getBytes(StandardCharsets.US_ASCII));
+        String middleBoundaryLine = "\r\n" + firstBoundaryLine;
+        this.middleBoundary = ByteBuffer.wrap(middleBoundaryLine.getBytes(StandardCharsets.US_ASCII));
+        String onlyBoundaryLine = "--" + boundary + "--\r\n";
+        this.onlyBoundary = ByteBuffer.wrap(onlyBoundaryLine.getBytes(StandardCharsets.US_ASCII));
+        String lastBoundaryLine = "\r\n" + onlyBoundaryLine;
+        this.lastBoundary = ByteBuffer.wrap(lastBoundaryLine.getBytes(StandardCharsets.US_ASCII));
+    }
+
+    private static String makeBoundary()
+    {
+        Random random = new Random();
+        StringBuilder builder = new StringBuilder("JettyHttpClientBoundary");
+        int length = builder.length();
+        while (builder.length() < length + 16)
+        {
+            long rnd = random.nextLong();
+            builder.append(Long.toString(rnd < 0 ? -rnd : rnd, 36));
+        }
+        builder.setLength(length + 16);
+        return builder.toString();
+    }
+
+    /**
+     * <p>Adds a field part with the given {@code name} as field name, and the given
+     * {@code content} as part content.</p>
+     * <p>The {@code Content-Type} of this part will be obtained from:</p>
+     * <ul>
+     *     <li>the {@code Content-Type} header in the {@code fields} parameter; otherwise</li>
+     *     <li>the {@link org.eclipse.jetty.client.api.ContentProvider.Typed#getContentType()} method if the {@code content} parameter
+     *     implements {@link org.eclipse.jetty.client.api.ContentProvider.Typed}; otherwise</li>
+     *     <li>"text/plain"</li>
+     * </ul>
+     *
+     * @param name the part name
+     * @param content the part content
+     * @param fields the headers associated with this part
+     */
+    public void addFieldPart(String name, ContentProvider content, HttpFields fields)
+    {
+        addPart(new Part(name, null, "text/plain", content, fields));
+    }
+
+    /**
+     * <p>Adds a file part with the given {@code name} as field name, the given
+     * {@code fileName} as file name, and the given {@code content} as part content.</p>
+     * <p>The {@code Content-Type} of this part will be obtained from:</p>
+     * <ul>
+     *     <li>the {@code Content-Type} header in the {@code fields} parameter; otherwise</li>
+     *     <li>the {@link org.eclipse.jetty.client.api.ContentProvider.Typed#getContentType()} method if the {@code content} parameter
+     *     implements {@link org.eclipse.jetty.client.api.ContentProvider.Typed}; otherwise</li>
+     *     <li>"application/octet-stream"</li>
+     * </ul>
+     *
+     * @param name the part name
+     * @param fileName the file name associated to this part
+     * @param content the part content
+     * @param fields the headers associated with this part
+     */
+    public void addFilePart(String name, String fileName, ContentProvider content, HttpFields fields)
+    {
+        addPart(new Part(name, fileName, "application/octet-stream", content, fields));
+    }
+
+    private void addPart(Part part)
+    {
+        parts.add(part);
+        if (LOG.isDebugEnabled())
+            LOG.debug("Added {}", part);
+    }
+
+    @Override
+    public void setListener(Listener listener)
+    {
+        this.listener = listener;
+        if (closed.get())
+            this.length = calculateLength();
+    }
+
+    private long calculateLength()
+    {
+        // Compute the length, if possible.
+        if (parts.isEmpty())
+        {
+            return onlyBoundary.remaining();
+        }
+        else
+        {
+            long result = 0;
+            for (int i = 0; i < parts.size(); ++i)
+            {
+                result += (i == 0) ? firstBoundary.remaining() : middleBoundary.remaining();
+                Part part = parts.get(i);
+                long partLength = part.length;
+                result += partLength;
+                if (partLength < 0)
+                {
+                    result = -1;
+                    break;
+                }
+            }
+            if (result > 0)
+                result += lastBoundary.remaining();
+            return result;
+        }
+    }
+
+    @Override
+    public long getLength()
+    {
+        return length;
+    }
+
+    @Override
+    public Iterator<ByteBuffer> iterator()
+    {
+        return new MultiPartIterator();
+    }
+
+    @Override
+    public void close()
+    {
+        closed.compareAndSet(false, true);
+    }
+
+    private static class Part
+    {
+        private final String name;
+        private final String fileName;
+        private final String contentType;
+        private final ContentProvider content;
+        private final HttpFields fields;
+        private final ByteBuffer headers;
+        private final long length;
+
+        private Part(String name, String fileName, String contentType, ContentProvider content, HttpFields fields)
+        {
+            this.name = name;
+            this.fileName = fileName;
+            this.contentType = contentType;
+            this.content = content;
+            this.fields = fields;
+            this.headers = headers();
+            this.length = content.getLength() < 0 ? -1 : headers.remaining() + content.getLength();
+        }
+
+        private ByteBuffer headers()
+        {
+            try
+            {
+                // Compute the Content-Disposition.
+                String contentDisposition = "Content-Disposition: form-data; name=\"" + name + "\"";
+                if (fileName != null)
+                    contentDisposition += "; filename=\"" + fileName + "\"";
+                contentDisposition += "\r\n";
+
+                // Compute the Content-Type.
+                String contentType = fields == null ? null : fields.get(HttpHeader.CONTENT_TYPE);
+                if (contentType == null)
+                {
+                    if (content instanceof Typed)
+                        contentType = ((Typed)content).getContentType();
+                    else
+                        contentType = this.contentType;
+                }
+                contentType = "Content-Type: " + contentType + "\r\n";
+
+                if (fields == null || fields.size() == 0)
+                {
+                    String headers = contentDisposition;
+                    headers += contentType;
+                    headers += "\r\n";
+                    return ByteBuffer.wrap(headers.getBytes(StandardCharsets.UTF_8));
+                }
+
+                ByteArrayOutputStream buffer = new ByteArrayOutputStream((fields.size() + 1) * contentDisposition.length());
+                buffer.write(contentDisposition.getBytes(StandardCharsets.UTF_8));
+                buffer.write(contentType.getBytes(StandardCharsets.UTF_8));
+                for (HttpField field : fields)
+                {
+                    if (HttpHeader.CONTENT_TYPE.equals(field.getHeader()))
+                        continue;
+                    buffer.write(field.getName().getBytes(StandardCharsets.US_ASCII));
+                    buffer.write(COLON_SPACE_BYTES);
+                    String value = field.getValue();
+                    if (value != null)
+                        buffer.write(value.getBytes(StandardCharsets.UTF_8));
+                    buffer.write(CR_LF_BYTES);
+                }
+                buffer.write(CR_LF_BYTES);
+                return ByteBuffer.wrap(buffer.toByteArray());
+            }
+            catch (IOException x)
+            {
+                throw new RuntimeIOException(x);
+            }
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("%s@%x[name=%s,fileName=%s,length=%d,headers=%s]",
+                    getClass().getSimpleName(),
+                    hashCode(),
+                    name,
+                    fileName,
+                    content.getLength(),
+                    fields);
+        }
+    }
+
+    private class MultiPartIterator implements Iterator<ByteBuffer>, Synchronizable, Callback, Closeable
+    {
+        private Iterator<ByteBuffer> iterator;
+        private int index;
+        private State state = State.FIRST_BOUNDARY;
+
+        @Override
+        public boolean hasNext()
+        {
+            return state != State.COMPLETE;
+        }
+
+        @Override
+        public ByteBuffer next()
+        {
+            while (true)
+            {
+                switch (state)
+                {
+                    case FIRST_BOUNDARY:
+                    {
+                        if (parts.isEmpty())
+                        {
+                            state = State.COMPLETE;
+                            return onlyBoundary.slice();
+                        }
+                        else
+                        {
+                            state = State.HEADERS;
+                            return firstBoundary.slice();
+                        }
+                    }
+                    case HEADERS:
+                    {
+                        Part part = parts.get(index);
+                        ContentProvider content = part.content;
+                        if (content instanceof AsyncContentProvider)
+                            ((AsyncContentProvider)content).setListener(listener);
+                        iterator = content.iterator();
+                        state = State.CONTENT;
+                        return part.headers.slice();
+                    }
+                    case CONTENT:
+                    {
+                        if (iterator.hasNext())
+                            return iterator.next();
+                        ++index;
+                        if (index == parts.size())
+                            state = State.LAST_BOUNDARY;
+                        else
+                            state = State.MIDDLE_BOUNDARY;
+                        break;
+                    }
+                    case MIDDLE_BOUNDARY:
+                    {
+                        state = State.HEADERS;
+                        return middleBoundary.slice();
+                    }
+                    case LAST_BOUNDARY:
+                    {
+                        state = State.COMPLETE;
+                        return lastBoundary.slice();
+                    }
+                    case COMPLETE:
+                    {
+                        throw new NoSuchElementException();
+                    }
+                }
+            }
+        }
+
+        @Override
+        public Object getLock()
+        {
+            if (iterator instanceof Synchronizable)
+                return ((Synchronizable)iterator).getLock();
+            return this;
+        }
+
+        @Override
+        public void succeeded()
+        {
+            if (iterator instanceof Callback)
+                ((Callback)iterator).succeeded();
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            if (iterator instanceof Callback)
+                ((Callback)iterator).failed(x);
+        }
+
+        @Override
+        public void close() throws IOException
+        {
+            if (iterator instanceof Closeable)
+                ((Closeable)iterator).close();
+        }
+    }
+
+    private enum State
+    {
+        FIRST_BOUNDARY, HEADERS, CONTENT, MIDDLE_BOUNDARY, LAST_BOUNDARY, COMPLETE
+    }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/OutputStreamContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/OutputStreamContentProvider.java
index c7f0f56..0d0bf00 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/OutputStreamContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/OutputStreamContentProvider.java
@@ -33,11 +33,11 @@
 /**
  * A {@link ContentProvider} that provides content asynchronously through an {@link OutputStream}
  * similar to {@link DeferredContentProvider}.
- * <p />
+ * <p>
  * {@link OutputStreamContentProvider} can only be used in conjunction with
  * {@link Request#send(Response.CompleteListener)} (and not with its blocking counterpart {@link Request#send()})
  * because it provides content asynchronously.
- * <p />
+ * <p>
  * The deferred content is provided once by writing to the {@link #getOutputStream() output stream}
  * and then fully consumed.
  * Invocations to the {@link #iterator()} method after the first will return an "empty" iterator
@@ -45,10 +45,10 @@
  * However, it is possible for subclasses to support multiple invocations of {@link #iterator()}
  * by overriding {@link #write(ByteBuffer)} and {@link #close()}, copying the bytes and making them
  * available for subsequent invocations.
- * <p />
+ * <p>
  * Content must be provided by writing to the {@link #getOutputStream() output stream}, that must be
  * {@link OutputStream#close() closed} when all content has been provided.
- * <p />
+ * <p>
  * Example usage:
  * <pre>
  * HttpClient httpClient = ...;
@@ -61,7 +61,7 @@
  *             .content(content)
  *             .send(new Response.CompleteListener()
  *             {
- *                 &#64Override
+ *                 &#64;Override
  *                 public void onComplete(Result result)
  *                 {
  *                     // Your logic here
@@ -79,6 +79,12 @@
     private final OutputStream output = new DeferredOutputStream();
 
     @Override
+    public boolean isNonBlocking()
+    {
+        return deferred.isNonBlocking();
+    }
+    
+    @Override
     public long getLength()
     {
         return deferred.getLength();
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathContentProvider.java
index 6b75d83..d1051d9 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathContentProvider.java
@@ -30,14 +30,18 @@
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
+import org.eclipse.jetty.client.api.ContentProvider;
+import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
 /**
- * A {@link ContentProvider} for files using JDK 7's {@code java.nio.file} APIs.
- * <p />
- * It is possible to specify, at the constructor, a buffer size used to read content from the
- * stream, by default 4096 bytes.
+ * <p>A {@link ContentProvider} for files using JDK 7's {@code java.nio.file} APIs.</p>
+ * <p>It is possible to specify, at the constructor, a buffer size used to read
+ * content from the stream, by default 4096 bytes.
+ * If a {@link ByteBufferPool} is provided via {@link #setByteBufferPool(ByteBufferPool)},
+ * the buffer will be allocated from that pool, otherwise one buffer will be
+ * allocated and used to read the file.</p>
  */
 public class PathContentProvider extends AbstractTypedContentProvider
 {
@@ -46,6 +50,7 @@
     private final Path filePath;
     private final long fileSize;
     private final int bufferSize;
+    private ByteBufferPool bufferPool;
 
     public PathContentProvider(Path filePath) throws IOException
     {
@@ -80,6 +85,16 @@
         return fileSize;
     }
 
+    public ByteBufferPool getByteBufferPool()
+    {
+        return bufferPool;
+    }
+
+    public void setByteBufferPool(ByteBufferPool byteBufferPool)
+    {
+        this.bufferPool = byteBufferPool;
+    }
+
     @Override
     public Iterator<ByteBuffer> iterator()
     {
@@ -88,7 +103,7 @@
 
     private class PathIterator implements Iterator<ByteBuffer>, Closeable
     {
-        private final ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize);
+        private ByteBuffer buffer;
         private SeekableByteChannel channel;
         private long position;
 
@@ -105,6 +120,9 @@
             {
                 if (channel == null)
                 {
+                    buffer = bufferPool == null ?
+                            ByteBuffer.allocateDirect(bufferSize) :
+                            bufferPool.acquire(bufferSize, true);
                     channel = Files.newByteChannel(filePath, StandardOpenOption.READ);
                     if (LOG.isDebugEnabled())
                         LOG.debug("Opened file {}", filePath);
@@ -120,9 +138,6 @@
 
                 position += read;
 
-                if (!hasNext())
-                    close();
-
                 buffer.flip();
                 return buffer;
             }
@@ -131,7 +146,7 @@
                 close();
                 throw x;
             }
-            catch (Exception x)
+            catch (Throwable x)
             {
                 close();
                 throw (NoSuchElementException)new NoSuchElementException().initCause(x);
@@ -139,20 +154,16 @@
         }
 
         @Override
-        public void remove()
-        {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
         public void close()
         {
             try
             {
+                if (bufferPool != null && buffer != null)
+                    bufferPool.release(buffer);
                 if (channel != null)
                     channel.close();
             }
-            catch (Exception x)
+            catch (Throwable x)
             {
                 LOG.ignore(x);
             }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/StringContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/StringContentProvider.java
index b310f06..bfd2be6 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/StringContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/StringContentProvider.java
@@ -21,9 +21,11 @@
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 
+import org.eclipse.jetty.client.api.ContentProvider;
+
 /**
  * A {@link ContentProvider} for strings.
- * <p />
+ * <p>
  * It is possible to specify, at the constructor, an encoding used to convert
  * the string into bytes, by default UTF-8.
  */
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java
index b4d6ac1..c6fdaff 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java
@@ -21,9 +21,9 @@
 import java.util.Arrays;
 import java.util.Collection;
 
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
 import org.eclipse.jetty.http.HttpScheme;
 import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.NetworkConnector;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.toolchain.test.TestTracker;
@@ -50,7 +50,7 @@
     protected String scheme;
     protected Server server;
     protected HttpClient client;
-    protected NetworkConnector connector;
+    protected ServerConnector connector;
 
     public AbstractHttpClientServerTest(SslContextFactory sslContextFactory)
     {
@@ -89,9 +89,14 @@
 
     protected void startClient() throws Exception
     {
+        startClient(new HttpClientTransportOverHTTP(1));
+    }
+
+    protected void startClient(HttpClientTransport transport) throws Exception
+    {
         QueuedThreadPool clientThreads = new QueuedThreadPool();
         clientThreads.setName("client");
-        client = new HttpClient(sslContextFactory);
+        client = new HttpClient(transport, sslContextFactory);
         client.setExecutor(clientThreads);
         client.start();
     }
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ClientConnectionCloseTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ClientConnectionCloseTest.java
new file mode 100644
index 0000000..fe44d1c
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ClientConnectionCloseTest.java
@@ -0,0 +1,257 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
+import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
+import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpHeaderValue;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
+{
+    public ClientConnectionCloseTest(SslContextFactory sslContextFactory)
+    {
+        super(sslContextFactory);
+    }
+
+    @Test
+    public void test_ClientConnectionClose_ServerConnectionClose_ClientClosesAfterExchange() throws Exception
+    {
+        byte[] data = new byte[128 * 1024];
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+
+                ServletInputStream input = request.getInputStream();
+                while (true)
+                {
+                    int read = input.read();
+                    if (read < 0)
+                        break;
+                }
+
+                response.setContentLength(data.length);
+                response.getOutputStream().write(data);
+
+                try
+                {
+                    // Delay the server from sending the TCP FIN.
+                    Thread.sleep(1000);
+                }
+                catch (InterruptedException x)
+                {
+                    throw new InterruptedIOException();
+                }
+            }
+        });
+
+        String host = "localhost";
+        int port = connector.getLocalPort();
+
+        HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
+
+        ContentResponse response = client.newRequest(host, port)
+                .scheme(scheme)
+                .header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
+                .content(new StringContentProvider("0"))
+                .onRequestSuccess(request ->
+                {
+                    HttpConnectionOverHTTP connection = (HttpConnectionOverHTTP)connectionPool.getActiveConnections().peek();
+                    Assert.assertFalse(connection.getEndPoint().isOutputShutdown());
+                })
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+        Assert.assertArrayEquals(data, response.getContent());
+        Assert.assertEquals(0, connectionPool.getConnectionCount());
+    }
+
+    @Test
+    public void test_ClientConnectionClose_ServerDoesNotRespond_ClientIdleTimeout() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                request.startAsync();
+                // Do not respond.
+            }
+        });
+
+        String host = "localhost";
+        int port = connector.getLocalPort();
+
+        HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
+
+        CountDownLatch resultLatch = new CountDownLatch(1);
+        long idleTimeout = 1000;
+        client.newRequest(host, port)
+                .scheme(scheme)
+                .header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
+                .idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
+                .onRequestSuccess(request ->
+                {
+                    HttpConnectionOverHTTP connection = (HttpConnectionOverHTTP)connectionPool.getActiveConnections().peek();
+                    Assert.assertFalse(connection.getEndPoint().isOutputShutdown());
+                })
+                .send(result ->
+                {
+                    if (result.isFailed())
+                        resultLatch.countDown();
+                });
+
+        Assert.assertTrue(resultLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+        Assert.assertEquals(0, connectionPool.getConnectionCount());
+    }
+
+    @Test
+    public void test_ClientConnectionClose_ServerPartialResponse_ClientIdleTimeout() throws Exception
+    {
+        long idleTimeout = 1000;
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+
+                ServletInputStream input = request.getInputStream();
+                while (true)
+                {
+                    int read = input.read();
+                    if (read < 0)
+                        break;
+                }
+
+                response.getOutputStream().print("Hello");
+                response.flushBuffer();
+
+                try
+                {
+                    Thread.sleep(2 * idleTimeout);
+                }
+                catch (InterruptedException x)
+                {
+                    throw new InterruptedIOException();
+                }
+            }
+        });
+
+        String host = "localhost";
+        int port = connector.getLocalPort();
+
+        HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
+
+        DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.allocate(8));
+        CountDownLatch resultLatch = new CountDownLatch(1);
+        client.newRequest(host, port)
+                .scheme(scheme)
+                .header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
+                .content(content)
+                .idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
+                .onRequestSuccess(request ->
+                {
+                    HttpConnectionOverHTTP connection = (HttpConnectionOverHTTP)connectionPool.getActiveConnections().peek();
+                    Assert.assertFalse(connection.getEndPoint().isOutputShutdown());
+                })
+                .send(result ->
+                {
+                    if (result.isFailed())
+                        resultLatch.countDown();
+                });
+        content.offer(ByteBuffer.allocate(8));
+        content.close();
+
+        Assert.assertTrue(resultLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+        Assert.assertEquals(0, connectionPool.getConnectionCount());
+    }
+
+    @Test
+    public void test_ClientConnectionClose_ServerNoConnectionClose_ClientCloses() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.setContentLength(0);
+                response.flushBuffer();
+
+                try
+                {
+                    // Delay the server from sending the TCP FIN.
+                    Thread.sleep(1000);
+                }
+                catch (InterruptedException x)
+                {
+                    throw new InterruptedIOException();
+                }
+            }
+        });
+
+        String host = "localhost";
+        int port = connector.getLocalPort();
+
+        HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
+
+        ContentResponse response = client.newRequest(host, port)
+                .scheme(scheme)
+                .header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
+                .onRequestSuccess(request ->
+                {
+                    HttpConnectionOverHTTP connection = (HttpConnectionOverHTTP)connectionPool.getActiveConnections().peek();
+                    Assert.assertFalse(connection.getEndPoint().isOutputShutdown());
+                })
+                .onResponseHeaders(r -> r.getHeaders().remove(HttpHeader.CONNECTION))
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+        Assert.assertEquals(0, connectionPool.getConnectionCount());
+    }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ContentResponseTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ContentResponseTest.java
index 65ea593..55cba2a 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/ContentResponseTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ContentResponseTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.util.Random;
 import java.util.concurrent.TimeUnit;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ExternalSiteTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ExternalSiteTest.java
index 0426801..598fe9e 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/ExternalSiteTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ExternalSiteTest.java
@@ -32,9 +32,11 @@
 import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
+@Ignore
 public class ExternalSiteTest
 {
     @Rule
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HostnameVerificationTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HostnameVerificationTest.java
index a5396e0..d86d72e 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HostnameVerificationTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HostnameVerificationTest.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.client;
 
 import java.io.IOException;
-import java.nio.channels.ClosedChannelException;
 import java.security.cert.CertificateException;
 import java.util.concurrent.ExecutionException;
 
@@ -48,13 +47,17 @@
 public class HostnameVerificationTest
 {
     private SslContextFactory clientSslContextFactory = new SslContextFactory();
-    private Server server = new Server();
+    private Server server;
     private HttpClient client;
     private NetworkConnector connector;
 
     @Before
     public void setUp() throws Exception
     {
+        QueuedThreadPool serverThreads = new QueuedThreadPool();
+        serverThreads.setName("server");
+        server = new Server(serverThreads);
+
         SslContextFactory serverSslContextFactory = new SslContextFactory();
         serverSslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
         serverSslContextFactory.setKeyStorePassword("storepwd");
@@ -75,10 +78,10 @@
         clientSslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
         clientSslContextFactory.setKeyStorePassword("storepwd");
 
-        QueuedThreadPool executor = new QueuedThreadPool();
-        executor.setName(executor.getName() + "-client");
+        QueuedThreadPool clientThreads = new QueuedThreadPool();
+        clientThreads.setName("client");
         client = new HttpClient(clientSslContextFactory);
-        client.setExecutor(executor);
+        client.setExecutor(clientThreads);
         client.start();
     }
 
@@ -95,7 +98,7 @@
      * http://www.ietf.org/rfc/rfc2818.txt section 3.1. It uses a certificate with a common name different to localhost
      * and sends a request to localhost. This should fail with a SSLHandshakeException.
      *
-     * @throws Exception
+     * @throws Exception on test failure
      */
     @Test
     public void simpleGetWithHostnameVerificationEnabledTest() throws Exception
@@ -109,23 +112,10 @@
         }
         catch (ExecutionException x)
         {
-            // The test may fail in 2 ways, since the CertificateException thrown because of the hostname
-            // verification failure is not rethrown immediately by the JDK SSL implementation, but only
-            // rethrown on the next read or write.
-            // Therefore this test may catch a SSLHandshakeException, or a ClosedChannelException.
-            // If it is the former, we verify that its cause is a CertificateException.
-
-            // ExecutionException wraps an SSLHandshakeException
             Throwable cause = x.getCause();
-            if (cause==null)
-            {
-                x.printStackTrace();
-                Assert.fail("No cause?");
-            }
-            if (cause instanceof SSLHandshakeException)
-                Assert.assertThat(cause.getCause().getCause(), Matchers.instanceOf(CertificateException.class));
-            else
-                Assert.assertThat(cause, Matchers.instanceOf(ClosedChannelException.class));
+            Assert.assertThat(cause, Matchers.instanceOf(SSLHandshakeException.class));
+            Throwable root = cause.getCause().getCause();
+            Assert.assertThat(root, Matchers.instanceOf(CertificateException.class));
         }
     }
 
@@ -133,7 +123,8 @@
      * This test has hostname verification disabled and connecting, ssl handshake and sending the request should just
      * work fine.
      *
-     * @throws Exception
+     * @throws Exception on test failure
+     *
      */
     @Test
     public void simpleGetWithHostnameVerificationDisabledTest() throws Exception
@@ -154,7 +145,7 @@
      * This test has hostname verification disabled by setting trustAll to true and connecting,
      * ssl handshake and sending the request should just work fine.
      *
-     * @throws Exception
+     * @throws Exception on test failure
      */
     @Test
     public void trustAllDisablesHostnameVerificationTest() throws Exception
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAsyncContentTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAsyncContentTest.java
index 4baae79..39c91d1 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAsyncContentTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAsyncContentTest.java
@@ -24,6 +24,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
+
 import javax.servlet.ServletException;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java
index 6122acb..6e1a5ca 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java
@@ -21,6 +21,8 @@
 import java.io.File;
 import java.io.IOException;
 import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -37,6 +39,7 @@
 import org.eclipse.jetty.client.api.Response;
 import org.eclipse.jetty.client.api.Result;
 import org.eclipse.jetty.client.util.BasicAuthentication;
+import org.eclipse.jetty.client.util.DeferredContentProvider;
 import org.eclipse.jetty.client.util.DigestAuthentication;
 import org.eclipse.jetty.security.Authenticator;
 import org.eclipse.jetty.security.ConstraintMapping;
@@ -108,6 +111,23 @@
     }
 
     @Test
+    public void test_BasicEmptyRealm() throws Exception
+    {
+        realm = "";
+        startBasic(new EmptyServerHandler());
+        URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort());
+        test_Authentication(new BasicAuthentication(uri, realm, "basic", "basic"));
+    }
+
+    @Test
+    public void test_BasicAnyRealm() throws Exception
+    {
+        startBasic(new EmptyServerHandler());
+        URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort());
+        test_Authentication(new BasicAuthentication(uri, Authentication.ANY_REALM, "basic", "basic"));
+    }
+
+    @Test
     public void test_DigestAuthentication() throws Exception
     {
         startDigest(new EmptyServerHandler());
@@ -115,6 +135,14 @@
         test_Authentication(new DigestAuthentication(uri, realm, "digest", "digest"));
     }
 
+    @Test
+    public void test_DigestAnyRealm() throws Exception
+    {
+        startDigest(new EmptyServerHandler());
+        URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort());
+        test_Authentication(new DigestAuthentication(uri, Authentication.ANY_REALM, "digest", "digest"));
+    }
+
     private void test_Authentication(Authentication authentication) throws Exception
     {
         AuthenticationStore authenticationStore = client.getAuthenticationStore();
@@ -366,4 +394,72 @@
 
         Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
     }
+
+    @Test
+    public void test_PreemptedAuthentication() throws Exception
+    {
+        startBasic(new EmptyServerHandler());
+
+        AuthenticationStore authenticationStore = client.getAuthenticationStore();
+        URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort());
+        authenticationStore.addAuthenticationResult(new BasicAuthentication.BasicResult(uri, "basic", "basic"));
+
+        AtomicInteger requests = new AtomicInteger();
+        client.getRequestListeners().add(new Request.Listener.Adapter()
+        {
+            @Override
+            public void onSuccess(Request request)
+            {
+                requests.incrementAndGet();
+            }
+        });
+
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .path("/secure")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(1, requests.get());
+    }
+
+    @Test
+    public void test_RequestFailsAfterResponse() throws Exception
+    {
+        startBasic(new EmptyServerHandler());
+
+        AuthenticationStore authenticationStore = client.getAuthenticationStore();
+        URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort());
+        BasicAuthentication authentication = new BasicAuthentication(uri, realm, "basic", "basic");
+        authenticationStore.addAuthentication(authentication);
+
+        CountDownLatch successLatch = new CountDownLatch(1);
+        CountDownLatch resultLatch = new CountDownLatch(1);
+        DeferredContentProvider content = new DeferredContentProvider();
+        Request request = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .path("/secure")
+                .content(content)
+                .onResponseSuccess(response -> successLatch.countDown());
+        request.send(result ->
+        {
+            if (result.isFailed() && result.getResponseFailure() == null)
+                resultLatch.countDown();
+        });
+
+        // Send some content to make sure the request is dispatched on the server.
+        content.offer(ByteBuffer.wrap("hello".getBytes(StandardCharsets.UTF_8)));
+
+        // Wait for the response to arrive to
+        // the authentication protocol handler.
+        Thread.sleep(1000);
+
+        // Trigger request failure.
+        request.abort(new Exception());
+
+        // Verify that the response was successful, it's the request that failed.
+        Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+    }
 }
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientContinueTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientContinueTest.java
deleted file mode 100644
index f756a50..0000000
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientContinueTest.java
+++ /dev/null
@@ -1,724 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.client;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import javax.servlet.ServletException;
-import javax.servlet.ServletInputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.client.util.BufferingResponseListener;
-import org.eclipse.jetty.client.util.BytesContentProvider;
-import org.eclipse.jetty.client.util.DeferredContentProvider;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpHeaderValue;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.toolchain.test.annotation.Slow;
-import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class HttpClientContinueTest extends AbstractHttpClientServerTest
-{
-    public HttpClientContinueTest(SslContextFactory sslContextFactory)
-    {
-        super(sslContextFactory);
-    }
-
-    @Test
-    public void test_Expect100Continue_WithOneContent_Respond100Continue() throws Exception
-    {
-        test_Expect100Continue_Respond100Continue("data1".getBytes(StandardCharsets.UTF_8));
-    }
-
-    @Test
-    public void test_Expect100Continue_WithMultipleContents_Respond100Continue() throws Exception
-    {
-        test_Expect100Continue_Respond100Continue("data1".getBytes(StandardCharsets.UTF_8), "data2".getBytes(StandardCharsets.UTF_8), "data3".getBytes(StandardCharsets.UTF_8));
-    }
-
-    private void test_Expect100Continue_Respond100Continue(byte[]... contents) throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                // Send 100-Continue and copy the content back
-                IO.copy(request.getInputStream(), response.getOutputStream());
-            }
-        });
-
-        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-                .content(new BytesContentProvider(contents))
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-
-        int index = 0;
-        byte[] responseContent = response.getContent();
-        for (byte[] content : contents)
-        {
-            for (byte b : content)
-            {
-                Assert.assertEquals(b, responseContent[index++]);
-            }
-        }
-    }
-
-    @Test
-    public void test_Expect100Continue_WithChunkedContent_Respond100Continue() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                // Send 100-Continue and copy the content back
-                ServletInputStream input = request.getInputStream();
-                // Make sure we chunk the response too
-                response.flushBuffer();
-                IO.copy(input, response.getOutputStream());
-            }
-        });
-
-        byte[] content1 = new byte[10240];
-        byte[] content2 = new byte[16384];
-        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-                .content(new BytesContentProvider(content1, content2)
-                {
-                    @Override
-                    public long getLength()
-                    {
-                        return -1;
-                    }
-                })
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-
-        int index = 0;
-        byte[] responseContent = response.getContent();
-        for (byte b : content1)
-            Assert.assertEquals(b, responseContent[index++]);
-        for (byte b : content2)
-            Assert.assertEquals(b, responseContent[index++]);
-    }
-
-    @Test
-    public void test_Expect100Continue_WithContent_Respond417ExpectationFailed() throws Exception
-    {
-        test_Expect100Continue_WithContent_RespondError(417);
-    }
-
-    @Test
-    public void test_Expect100Continue_WithContent_Respond413RequestEntityTooLarge() throws Exception
-    {
-        test_Expect100Continue_WithContent_RespondError(413);
-    }
-
-    private void test_Expect100Continue_WithContent_RespondError(final int error) throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                response.sendError(error);
-            }
-        });
-
-        byte[] content1 = new byte[10240];
-        byte[] content2 = new byte[16384];
-        final CountDownLatch latch = new CountDownLatch(1);
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-                .content(new BytesContentProvider(content1, content2))
-                .send(new BufferingResponseListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertTrue(result.isFailed());
-                        Assert.assertNotNull(result.getRequestFailure());
-                        Assert.assertNull(result.getResponseFailure());
-                        byte[] content = getContent();
-                        Assert.assertNotNull(content);
-                        Assert.assertTrue(content.length > 0);
-                        Assert.assertEquals(error, result.getResponse().getStatus());
-                        latch.countDown();
-                    }
-                });
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void test_Expect100Continue_WithContent_WithRedirect() throws Exception
-    {
-        final String data = "success";
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                if (request.getRequestURI().endsWith("/done"))
-                {
-                    response.getOutputStream().print(data);
-                }
-                else
-                {
-                    // Send 100-Continue and consume the content
-                    IO.copy(request.getInputStream(), new ByteArrayOutputStream());
-                    // Send a redirect
-                    response.sendRedirect("/done");
-                }
-            }
-        });
-
-        byte[] content = new byte[10240];
-        final CountDownLatch latch = new CountDownLatch(1);
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .method(HttpMethod.POST)
-                .path("/continue")
-                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-                .content(new BytesContentProvider(content))
-                .send(new BufferingResponseListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertFalse(result.isFailed());
-                        Assert.assertEquals(200, result.getResponse().getStatus());
-                        Assert.assertEquals(data, getContentAsString());
-                        latch.countDown();
-                    }
-                });
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void test_Redirect_WithExpect100Continue_WithContent() throws Exception
-    {
-        // A request with Expect: 100-Continue cannot receive non-final responses like 3xx
-
-        final String data = "success";
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                if (request.getRequestURI().endsWith("/done"))
-                {
-                    // Send 100-Continue and consume the content
-                    IO.copy(request.getInputStream(), new ByteArrayOutputStream());
-                    response.getOutputStream().print(data);
-                }
-                else
-                {
-                    // Send a redirect
-                    response.sendRedirect("/done");
-                }
-            }
-        });
-
-        byte[] content = new byte[10240];
-        final CountDownLatch latch = new CountDownLatch(1);
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .method(HttpMethod.POST)
-                .path("/redirect")
-                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-                .content(new BytesContentProvider(content))
-                .send(new BufferingResponseListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertTrue(result.isFailed());
-                        Assert.assertNotNull(result.getRequestFailure());
-                        Assert.assertNull(result.getResponseFailure());
-                        Assert.assertEquals(302, result.getResponse().getStatus());
-                        latch.countDown();
-                    }
-                });
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Slow
-    @Test
-    public void test_Expect100Continue_WithContent_WithResponseFailure_Before100Continue() throws Exception
-    {
-        final long idleTimeout = 1000;
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                try
-                {
-                    TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
-                }
-                catch (InterruptedException x)
-                {
-                    throw new ServletException(x);
-                }
-            }
-        });
-
-        client.setIdleTimeout(idleTimeout);
-
-        byte[] content = new byte[1024];
-        final CountDownLatch latch = new CountDownLatch(1);
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-                .content(new BytesContentProvider(content))
-                .send(new BufferingResponseListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertTrue(result.isFailed());
-                        Assert.assertNotNull(result.getRequestFailure());
-                        Assert.assertNotNull(result.getResponseFailure());
-                        latch.countDown();
-                    }
-                });
-
-        Assert.assertTrue(latch.await(3 * idleTimeout, TimeUnit.MILLISECONDS));
-    }
-
-    @Slow
-    @Test
-    public void test_Expect100Continue_WithContent_WithResponseFailure_After100Continue() throws Exception
-    {
-        final long idleTimeout = 1000;
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                // Send 100-Continue and consume the content
-                IO.copy(request.getInputStream(), new ByteArrayOutputStream());
-                try
-                {
-                    TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
-                }
-                catch (InterruptedException x)
-                {
-                    throw new ServletException(x);
-                }
-            }
-        });
-
-        client.setIdleTimeout(idleTimeout);
-
-        byte[] content = new byte[1024];
-        final CountDownLatch latch = new CountDownLatch(1);
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-                .content(new BytesContentProvider(content))
-                .send(new BufferingResponseListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertTrue(result.isFailed());
-                        Assert.assertNull(result.getRequestFailure());
-                        Assert.assertNotNull(result.getResponseFailure());
-                        latch.countDown();
-                    }
-                });
-
-        Assert.assertTrue(latch.await(3 * idleTimeout, TimeUnit.MILLISECONDS));
-    }
-
-    @Test
-    public void test_Expect100Continue_WithContent_WithResponseFailure_During100Continue() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                // Send 100-Continue and consume the content
-                IO.copy(request.getInputStream(), new ByteArrayOutputStream());
-            }
-        });
-
-        client.getProtocolHandlers().clear();
-        client.getProtocolHandlers().add(new ContinueProtocolHandler(client)
-        {
-            @Override
-            public Response.Listener getResponseListener()
-            {
-                final Response.Listener listener = super.getResponseListener();
-                return new Response.Listener.Adapter()
-                {
-                    @Override
-                    public void onBegin(Response response)
-                    {
-                        response.abort(new Exception());
-                    }
-
-                    @Override
-                    public void onFailure(Response response, Throwable failure)
-                    {
-                        listener.onFailure(response, failure);
-                    }
-                };
-            }
-        });
-
-        byte[] content = new byte[1024];
-        final CountDownLatch latch = new CountDownLatch(1);
-        client.newRequest("localhost", connector.getLocalPort())
-        .scheme(scheme)
-        .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-        .content(new BytesContentProvider(content))
-        .send(new BufferingResponseListener()
-        {
-            @Override
-            public void onComplete(Result result)
-            {
-                Assert.assertTrue(result.isFailed());
-                Assert.assertNotNull(result.getRequestFailure());
-                Assert.assertNotNull(result.getResponseFailure());
-                latch.countDown();
-            }
-        });
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Slow
-    @Test
-    public void test_Expect100Continue_WithDeferredContent_Respond100Continue() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                // Send 100-Continue and echo the content
-                IO.copy(request.getInputStream(), response.getOutputStream());
-            }
-        });
-
-        final byte[] chunk1 = new byte[]{0, 1, 2, 3};
-        final byte[] chunk2 = new byte[]{4, 5, 6, 7};
-        final byte[] data = new byte[chunk1.length + chunk2.length];
-        System.arraycopy(chunk1, 0, data, 0, chunk1.length);
-        System.arraycopy(chunk2, 0, data, chunk1.length, chunk2.length);
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        DeferredContentProvider content = new DeferredContentProvider();
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-                .content(content)
-                .send(new BufferingResponseListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertArrayEquals(data, getContent());
-                        latch.countDown();
-                    }
-                });
-
-        Thread.sleep(1000);
-
-        content.offer(ByteBuffer.wrap(chunk1));
-
-        Thread.sleep(1000);
-
-        content.offer(ByteBuffer.wrap(chunk2));
-        content.close();
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Slow
-    @Test
-    public void test_Expect100Continue_WithInitialAndDeferredContent_Respond100Continue() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                // Send 100-Continue and echo the content
-                IO.copy(request.getInputStream(), response.getOutputStream());
-            }
-        });
-
-        final byte[] chunk1 = new byte[]{0, 1, 2, 3};
-        final byte[] chunk2 = new byte[]{4, 5, 6, 7};
-        final byte[] data = new byte[chunk1.length + chunk2.length];
-        System.arraycopy(chunk1, 0, data, 0, chunk1.length);
-        System.arraycopy(chunk2, 0, data, chunk1.length, chunk2.length);
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(chunk1));
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-                .content(content)
-                .send(new BufferingResponseListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertArrayEquals(data, getContent());
-                        latch.countDown();
-                    }
-                });
-
-        Thread.sleep(1000);
-
-        content.offer(ByteBuffer.wrap(chunk2));
-        content.close();
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void test_Expect100Continue_WithConcurrentDeferredContent_Respond100Continue() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                // Send 100-Continue and echo the content
-                IO.copy(request.getInputStream(), response.getOutputStream());
-            }
-        });
-
-        final byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
-        final DeferredContentProvider content = new DeferredContentProvider();
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-                .onRequestHeaders(new org.eclipse.jetty.client.api.Request.HeadersListener()
-                {
-                    @Override
-                    public void onHeaders(org.eclipse.jetty.client.api.Request request)
-                    {
-                        content.offer(ByteBuffer.wrap(data));
-                        content.close();
-                    }
-                })
-                .content(content)
-                .send(new BufferingResponseListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertArrayEquals(data, getContent());
-                        latch.countDown();
-                    }
-                });
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void test_Expect100Continue_WithInitialAndConcurrentDeferredContent_Respond100Continue() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                // Send 100-Continue and echo the content
-                IO.copy(request.getInputStream(), response.getOutputStream());
-            }
-        });
-
-        final byte[] chunk1 = new byte[]{0, 1, 2, 3};
-        final byte[] chunk2 = new byte[]{4, 5, 6};
-        final byte[] data = new byte[chunk1.length + chunk2.length];
-        System.arraycopy(chunk1, 0, data, 0, chunk1.length);
-        System.arraycopy(chunk2, 0, data, chunk1.length, chunk2.length);
-
-        final DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(chunk1));
-
-        List<ProtocolHandler> protocolHandlers = client.getProtocolHandlers();
-        for (Iterator<ProtocolHandler> iterator = protocolHandlers.iterator(); iterator.hasNext();)
-        {
-            ProtocolHandler protocolHandler = iterator.next();
-            if (protocolHandler instanceof ContinueProtocolHandler)
-                iterator.remove();
-        }
-        protocolHandlers.add(new ContinueProtocolHandler(client)
-        {
-            @Override
-            public Response.Listener getResponseListener()
-            {
-                return new ContinueListener()
-                {
-                    @Override
-                    public void onHeaders(Response response)
-                    {
-                        super.onHeaders(response);
-                        content.offer(ByteBuffer.wrap(chunk2));
-                        content.close();
-                    }
-                };
-            }
-        });
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-                .content(content)
-                .send(new BufferingResponseListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertArrayEquals(data, getContent());
-                        latch.countDown();
-                    }
-                });
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void test_Expect100Continue_WithTwoResponsesInOneRead() throws Exception
-    {
-        // There is a chance that the server replies with the 100 Continue response
-        // and immediately after with the "normal" response, say a 200 OK.
-        // These may be read by the client in a single read, and must be handled correctly.
-
-        startClient();
-
-        try (ServerSocket server = new ServerSocket())
-        {
-            server.bind(new InetSocketAddress("localhost", 0));
-
-            final CountDownLatch latch = new CountDownLatch(1);
-            client.newRequest("localhost", server.getLocalPort())
-                    .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
-                    .content(new BytesContentProvider(new byte[]{0}))
-                    .send(new Response.CompleteListener()
-                    {
-                        @Override
-                        public void onComplete(Result result)
-                        {
-                            Assert.assertTrue(result.toString(), result.isSucceeded());
-                            Assert.assertEquals(200, result.getResponse().getStatus());
-                            latch.countDown();
-                        }
-                    });
-
-            try (Socket socket = server.accept())
-            {
-                // Read the request headers.
-                InputStream input = socket.getInputStream();
-                int crlfs = 0;
-                while (true)
-                {
-                    int read = input.read();
-                    if (read == '\r' || read == '\n')
-                        ++crlfs;
-                    else
-                        crlfs = 0;
-                    if (crlfs == 4)
-                        break;
-                }
-
-                OutputStream output = socket.getOutputStream();
-                String responses = "" +
-                        "HTTP/1.1 100 Continue\r\n" +
-                        "\r\n" +
-                        "HTTP/1.1 200 OK\r\n" +
-                        "Transfer-Encoding: chunked\r\n" +
-                        "\r\n" +
-                        "10\r\n" +
-                        "0123456789ABCDEF\r\n";
-                output.write(responses.getBytes(StandardCharsets.UTF_8));
-                output.flush();
-
-                Thread.sleep(1000);
-
-                String content = "" +
-                        "10\r\n" +
-                        "0123456789ABCDEF\r\n" +
-                        "0\r\n" +
-                        "\r\n";
-                output.write(content.getBytes(StandardCharsets.UTF_8));
-                output.flush();
-
-                Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-            }
-        }
-    }
-}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java
index d2bd818..1089d7f 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.client;
 
 import java.io.IOException;
-import java.net.URI;
 import java.nio.ByteBuffer;
 import java.util.Map;
 import java.util.concurrent.Executor;
@@ -93,9 +92,7 @@
             public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
             {
                 baseRequest.setHandled(true);
-                if (!URI.create(baseRequest.getUri().toString()).isAbsolute())
-                    response.setStatus(HttpServletResponse.SC_USE_PROXY);
-                else if (serverHost.equals(request.getServerName()))
+                if (serverHost.equals(request.getServerName()))
                     response.setStatus(status);
                 else
                     response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
@@ -142,11 +139,12 @@
         {
             HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
             Executor executor = destination.getHttpClient().getExecutor();
-            return new CAFEBABEConnection(endPoint, executor, connectionFactory, context);
+            CAFEBABEConnection connection = new CAFEBABEConnection(endPoint, executor, connectionFactory, context);
+            return customize(connection, context);
         }
     }
 
-    private class CAFEBABEConnection extends AbstractConnection
+    private class CAFEBABEConnection extends AbstractConnection implements Callback
     {
         private final ClientConnectionFactory connectionFactory;
         private final Map<String, Object> context;
@@ -162,8 +160,19 @@
         public void onOpen()
         {
             super.onOpen();
+            getEndPoint().write(this, ByteBuffer.wrap(CAFE_BABE));
+        }
+
+        @Override
+        public void succeeded()
+        {
             fillInterested();
-            getEndPoint().write(new Callback.Adapter(), ByteBuffer.wrap(CAFE_BABE));
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            close();
         }
 
         @Override
@@ -177,7 +186,7 @@
                 Assert.assertArrayEquals(CAFE_BABE, buffer.array());
 
                 // We are good, upgrade the connection
-                ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(getEndPoint(), context));
+                getEndPoint().upgrade(connectionFactory.newConnection(getEndPoint(), context));
             }
             catch (Throwable x)
             {
@@ -206,7 +215,7 @@
         }
     }
 
-    private class CAFEBABEServerConnection extends AbstractConnection
+    private class CAFEBABEServerConnection extends AbstractConnection implements Callback
     {
         private final org.eclipse.jetty.server.ConnectionFactory connectionFactory;
 
@@ -232,15 +241,25 @@
                 int filled = getEndPoint().fill(buffer);
                 Assert.assertEquals(4, filled);
                 Assert.assertArrayEquals(CAFE_BABE, buffer.array());
-                getEndPoint().write(new Callback.Adapter(), buffer);
-
-                // We are good, upgrade the connection
-                ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(connector, getEndPoint()));
+                getEndPoint().write(this, buffer);
             }
             catch (Throwable x)
             {
                 close();
             }
         }
+
+        @Override
+        public void succeeded()
+        {
+            // We are good, upgrade the connection
+            getEndPoint().upgrade(connectionFactory.newConnection(connector, getEndPoint()));
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            close();
+        }
     }
 }
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java
index debfba7..153ea85 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java
@@ -18,6 +18,7 @@
 
 package org.eclipse.jetty.client;
 
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jetty.client.api.Connection;
@@ -27,7 +28,7 @@
 import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
 import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
 import org.eclipse.jetty.client.util.FutureResponseListener;
-import org.eclipse.jetty.toolchain.test.annotation.Slow;
+import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.util.FuturePromise;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.junit.Assert;
@@ -59,13 +60,12 @@
             Assert.assertEquals(200, response.getStatus());
 
             HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination;
-            ConnectionPool connectionPool = httpDestination.getConnectionPool();
+            DuplexConnectionPool connectionPool = httpDestination.getConnectionPool();
             Assert.assertTrue(connectionPool.getActiveConnections().isEmpty());
             Assert.assertTrue(connectionPool.getIdleConnections().isEmpty());
         }
     }
 
-    @Slow
     @Test
     public void testExplicitConnectionIsClosedOnRemoteClose() throws Exception
     {
@@ -94,8 +94,30 @@
         Assert.assertFalse(httpConnection.getEndPoint().isOpen());
 
         HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination;
-        ConnectionPool connectionPool = httpDestination.getConnectionPool();
+        DuplexConnectionPool connectionPool = httpDestination.getConnectionPool();
         Assert.assertTrue(connectionPool.getActiveConnections().isEmpty());
         Assert.assertTrue(connectionPool.getIdleConnections().isEmpty());
     }
+
+    @Test
+    public void testExplicitConnectionResponseListeners() throws Exception
+    {
+        start(new EmptyServerHandler());
+
+        Destination destination = client.getDestination(scheme, "localhost", connector.getLocalPort());
+        FuturePromise<Connection> futureConnection = new FuturePromise<>();
+        destination.newConnection(futureConnection);
+        Connection connection = futureConnection.get(5, TimeUnit.SECONDS);
+        CountDownLatch responseLatch = new CountDownLatch(1);
+        Request request = client.newRequest(destination.getHost(), destination.getPort())
+                .scheme(scheme)
+                .onResponseSuccess(response -> responseLatch.countDown());
+
+        FutureResponseListener listener = new FutureResponseListener(request);
+        connection.send(request, listener);
+        ContentResponse response = listener.get(5, TimeUnit.SECONDS);
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+        Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+    }
 }
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java
index 0620db8..5deb8d5 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java
@@ -106,7 +106,7 @@
             // Expected.
         }
 
-        ConnectionPool connectionPool = connectionRef.get().getHttpDestination().getConnectionPool();
+        DuplexConnectionPool connectionPool = connectionRef.get().getHttpDestination().getConnectionPool();
         Assert.assertEquals(0, connectionPool.getConnectionCount());
         Assert.assertEquals(0, connectionPool.getActiveConnections().size());
         Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -157,7 +157,7 @@
 
         Assert.assertTrue(commitLatch.await(5, TimeUnit.SECONDS));
         final CountDownLatch contentLatch = new CountDownLatch(1);
-        content.offer(ByteBuffer.allocate(1024), new Callback.Adapter()
+        content.offer(ByteBuffer.allocate(1024), new Callback()
         {
             @Override
             public void failed(Throwable x)
@@ -170,7 +170,7 @@
         Assert.assertTrue(contentLatch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
 
-        ConnectionPool connectionPool = connectionRef.get().getHttpDestination().getConnectionPool();
+        DuplexConnectionPool connectionPool = connectionRef.get().getHttpDestination().getConnectionPool();
         Assert.assertEquals(0, connectionPool.getConnectionCount());
         Assert.assertEquals(0, connectionPool.getActiveConnections().size());
         Assert.assertEquals(0, connectionPool.getIdleConnections().size());
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientGZIPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientGZIPTest.java
index 1ad479a..e11dc7d 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientGZIPTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientGZIPTest.java
@@ -32,8 +32,6 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -217,14 +215,10 @@
         final CountDownLatch latch = new CountDownLatch(1);
         client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
-                .send(new Response.CompleteListener()
+                .send(result ->
                 {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        if (result.isFailed())
-                            latch.countDown();
-                    }
+                    if (result.isFailed())
+                        latch.countDown();
                 });
 
         Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientLoadTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientLoadTest.java
deleted file mode 100644
index 682c429..0000000
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientLoadTest.java
+++ /dev/null
@@ -1,320 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.client;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-import java.util.Random;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-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.client.api.Connection;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
-import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
-import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
-import org.eclipse.jetty.client.util.BytesContentProvider;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.HttpScheme;
-import org.eclipse.jetty.io.LeakTrackingByteBufferPool;
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.server.AbstractConnectionFactory;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.LeakDetector;
-import org.eclipse.jetty.util.SocketAddressResolver;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.Scheduler;
-import org.junit.Assert;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-public class HttpClientLoadTest extends AbstractHttpClientServerTest
-{
-    private final Logger logger = Log.getLogger(HttpClientLoadTest.class);
-
-    public HttpClientLoadTest(SslContextFactory sslContextFactory)
-    {
-        super(sslContextFactory);
-    }
-
-    @Test
-    public void testIterative() throws Exception
-    {
-        int cores = Runtime.getRuntime().availableProcessors();
-
-        final AtomicLong connectionLeaks = new AtomicLong();
-
-        start(new LoadHandler());
-        server.stop();
-        server.removeConnector(connector);
-        LeakTrackingByteBufferPool serverBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged());
-        connector = new ServerConnector(server, connector.getExecutor(), connector.getScheduler(),
-                serverBufferPool , 1, Math.min(1, cores / 2),
-                AbstractConnectionFactory.getFactories(sslContextFactory, new HttpConnectionFactory()));
-        server.addConnector(connector);
-        server.start();
-
-        client.stop();
-
-        HttpClient newClient = new HttpClient(new HttpClientTransportOverHTTP()
-        {
-            @Override
-            public HttpDestination newHttpDestination(Origin origin)
-            {
-                return new HttpDestinationOverHTTP(getHttpClient(), origin)
-                {
-                    @Override
-                    protected ConnectionPool newConnectionPool(HttpClient client)
-                    {
-                        return new LeakTrackingConnectionPool(this, client.getMaxConnectionsPerDestination(), this)
-                        {
-                            @Override
-                            protected void leaked(LeakDetector.LeakInfo resource)
-                            {
-                                connectionLeaks.incrementAndGet();
-                            }
-                        };
-                    }
-                };
-            }
-        }, sslContextFactory);
-        newClient.setExecutor(client.getExecutor());
-        newClient.setSocketAddressResolver(new SocketAddressResolver.Sync());
-        client = newClient;
-        LeakTrackingByteBufferPool clientBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged());
-        client.setByteBufferPool(clientBufferPool);
-        client.setMaxConnectionsPerDestination(32768);
-        client.setMaxRequestsQueuedPerDestination(1024 * 1024);
-        client.setDispatchIO(false);
-        client.setStrictEventOrdering(false);
-        client.start();
-
-        Random random = new Random();
-        // At least 25k requests to warmup properly (use -XX:+PrintCompilation to verify JIT activity)
-        int runs = 1;
-        int iterations = 500;
-        for (int i = 0; i < runs; ++i)
-        {
-            run(random, iterations);
-        }
-
-        // Re-run after warmup
-        iterations = 5_000;
-        for (int i = 0; i < runs; ++i)
-        {
-            run(random, iterations);
-        }
-
-        System.gc();
-
-        assertThat("Server BufferPool - leaked acquires", serverBufferPool.getLeakedAcquires(), is(0L));
-        assertThat("Server BufferPool - leaked releases", serverBufferPool.getLeakedReleases(), is(0L));
-        assertThat("Server BufferPool - unreleased", serverBufferPool.getLeakedResources(), is(0L));
-
-        assertThat("Client BufferPool - leaked acquires", clientBufferPool.getLeakedAcquires(), is(0L));
-        assertThat("Client BufferPool - leaked releases", clientBufferPool.getLeakedReleases(), is(0L));
-        assertThat("Client BufferPool - unreleased", clientBufferPool.getLeakedResources(), is(0L));
-
-        assertThat("Connection Leaks", connectionLeaks.get(), is(0L));
-    }
-
-    private void run(Random random, int iterations) throws InterruptedException
-    {
-        CountDownLatch latch = new CountDownLatch(iterations);
-        List<String> failures = new ArrayList<>();
-
-        int factor = logger.isDebugEnabled() ? 25 : 1;
-        factor *= "http".equalsIgnoreCase(scheme) ? 10 : 1000;
-
-        // Dumps the state of the client if the test takes too long
-        final Thread testThread = Thread.currentThread();
-        Scheduler.Task task = client.getScheduler().schedule(new Runnable()
-        {
-            @Override
-            public void run()
-            {
-                logger.warn("Interrupting test, it is taking too long");
-                for (String host : Arrays.asList("localhost", "127.0.0.1"))
-                {
-                    HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, connector.getLocalPort());
-                    ConnectionPool connectionPool = destination.getConnectionPool();
-                    for (Connection connection : new ArrayList<>(connectionPool.getActiveConnections()))
-                    {
-                        HttpConnectionOverHTTP active = (HttpConnectionOverHTTP)connection;
-                        logger.warn(active.getEndPoint() + " exchange " + active.getHttpChannel().getHttpExchange());
-                    }
-                }
-                testThread.interrupt();
-            }
-        }, iterations * factor, TimeUnit.MILLISECONDS);
-
-        long begin = System.nanoTime();
-        for (int i = 0; i < iterations; ++i)
-        {
-            test(random, latch, failures);
-//            test("http", "localhost", "GET", false, false, 64 * 1024, false, latch, failures);
-        }
-        Assert.assertTrue(latch.await(iterations, TimeUnit.SECONDS));
-        long end = System.nanoTime();
-        task.cancel();
-        long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin);
-        logger.info("{} requests in {} ms, {} req/s", iterations, elapsed, elapsed > 0 ? iterations * 1000 / elapsed : -1);
-
-        for (String failure : failures)
-            System.err.println("FAILED: "+failure);
-
-        Assert.assertTrue(failures.toString(), failures.isEmpty());
-    }
-
-    private void test(Random random, final CountDownLatch latch, final List<String> failures) throws InterruptedException
-    {
-        // Choose a random destination
-        String host = random.nextBoolean() ? "localhost" : "127.0.0.1";
-        // Choose a random method
-        HttpMethod method = random.nextBoolean() ? HttpMethod.GET : HttpMethod.POST;
-
-        boolean ssl = HttpScheme.HTTPS.is(scheme);
-
-        // Choose randomly whether to close the connection on the client or on the server
-        boolean clientClose = false;
-        if (!ssl && random.nextBoolean())
-            clientClose = true;
-        boolean serverClose = false;
-        if (!ssl && random.nextBoolean())
-            serverClose = true;
-
-        int maxContentLength = 64 * 1024;
-        int contentLength = random.nextInt(maxContentLength) + 1;
-
-        test(scheme, host, method.asString(), clientClose, serverClose, contentLength, true, latch, failures);
-    }
-
-    private void test(String scheme, String host, String method, boolean clientClose, boolean serverClose, int contentLength, final boolean checkContentLength, final CountDownLatch latch, final List<String> failures) throws InterruptedException
-    {
-        Request request = client.newRequest(host, connector.getLocalPort())
-                .scheme(scheme)
-                .method(method);
-
-        if (clientClose)
-            request.header(HttpHeader.CONNECTION, "close");
-        else if (serverClose)
-            request.header("X-Close", "true");
-
-        switch (method)
-        {
-            case "GET":
-                request.header("X-Download", String.valueOf(contentLength));
-                break;
-            case "POST":
-                request.header("X-Upload", String.valueOf(contentLength));
-                request.content(new BytesContentProvider(new byte[contentLength]));
-                break;
-        }
-
-        final CountDownLatch requestLatch = new CountDownLatch(1);
-        request.send(new Response.Listener.Adapter()
-        {
-            private final AtomicInteger contentLength = new AtomicInteger();
-
-            @Override
-            public void onHeaders(Response response)
-            {
-                if (checkContentLength)
-                {
-                    String content = response.getHeaders().get("X-Content");
-                    if (content != null)
-                        contentLength.set(Integer.parseInt(content));
-                }
-            }
-
-            @Override
-            public void onContent(Response response, ByteBuffer content)
-            {
-                if (checkContentLength)
-                    contentLength.addAndGet(-content.remaining());
-            }
-
-            @Override
-            public void onComplete(Result result)
-            {
-                if (result.isFailed())
-                {
-                    result.getFailure().printStackTrace();
-                    failures.add("Result failed " + result);
-                }
-
-                if (checkContentLength && contentLength.get() != 0)
-                    failures.add("Content length mismatch " + contentLength);
-
-                requestLatch.countDown();
-                latch.countDown();
-            }
-        });
-        requestLatch.await(5, TimeUnit.SECONDS);
-    }
-
-    private class LoadHandler extends AbstractHandler
-    {
-        @Override
-        public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-        {
-            String method = request.getMethod().toUpperCase(Locale.ENGLISH);
-            switch (method)
-            {
-                case "GET":
-                    int contentLength = request.getIntHeader("X-Download");
-                    if (contentLength > 0)
-                    {
-                        response.setHeader("X-Content", String.valueOf(contentLength));
-                        response.getOutputStream().write(new byte[contentLength]);
-                    }
-                    break;
-                case "POST":
-                    response.setHeader("X-Content", request.getHeader("X-Upload"));
-                    IO.copy(request.getInputStream(), response.getOutputStream());
-                    break;
-            }
-
-            if (Boolean.parseBoolean(request.getHeader("X-Close")))
-                response.setHeader("Connection", "close");
-
-            baseRequest.setHandled(true);
-        }
-    }
-}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java
index 5cb8c77..4992e26 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java
@@ -32,6 +32,7 @@
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.client.util.BasicAuthentication;
 import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpScheme;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.util.B64Code;
@@ -58,7 +59,7 @@
             public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
             {
                 baseRequest.setHandled(true);
-                if (!URI.create(baseRequest.getUri().toString()).isAbsolute())
+                if (!URI.create(baseRequest.getHttpURI().toString()).isAbsolute())
                     response.setStatus(HttpServletResponse.SC_USE_PROXY);
                 else if (serverHost.equals(request.getServerName()))
                     response.setStatus(status);
@@ -80,7 +81,7 @@
     }
 
     @Test
-    public void testAuthenticatedProxiedRequest() throws Exception
+    public void testProxyAuthentication() throws Exception
     {
         final String user = "foo";
         final String password = "bar";
@@ -157,4 +158,244 @@
         Assert.assertEquals(status, response3.getStatus());
         Assert.assertEquals(1, requests.get());
     }
+
+    @Test
+    public void testProxyAuthenticationWithRedirect() throws Exception
+    {
+        String user = "foo";
+        String password = "bar";
+        String credentials = B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1);
+        String proxyHost = "localhost";
+        String serverHost = "server";
+        int serverPort = HttpScheme.HTTP.is(scheme) ? 80 : 443;
+        String realm = "test_realm";
+        int status = HttpStatus.NO_CONTENT_204;
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                if (target.startsWith("/proxy"))
+                {
+                    String authorization = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString());
+                    if (authorization == null)
+                    {
+                        response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407);
+                        response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + realm + "\"");
+                    }
+                    else
+                    {
+                        String prefix = "Basic ";
+                        if (authorization.startsWith(prefix))
+                        {
+                            String attempt = authorization.substring(prefix.length());
+                            if (credentials.equals(attempt))
+                            {
+                                // Change also the host, to verify that proxy authentication works in this case too.
+                                response.sendRedirect(scheme + "://127.0.0.1:" + serverPort + "/server");
+                            }
+                        }
+                    }
+                }
+                else if (target.startsWith("/server"))
+                {
+                    response.setStatus(status);
+                }
+                else
+                {
+                    response.sendError(HttpStatus.INTERNAL_SERVER_ERROR_500);
+                }
+            }
+        });
+
+        int proxyPort = connector.getLocalPort();
+        client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
+
+        ContentResponse response1 = client.newRequest(serverHost, serverPort)
+                .scheme(scheme)
+                .path("/proxy")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        // No Authentication available => 407.
+        Assert.assertEquals(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407, response1.getStatus());
+
+        // Add authentication...
+        URI uri = URI.create(scheme + "://" + proxyHost + ":" + proxyPort);
+        client.getAuthenticationStore().addAuthentication(new BasicAuthentication(uri, realm, user, password));
+        final AtomicInteger requests = new AtomicInteger();
+        client.getRequestListeners().add(new Request.Listener.Adapter()
+        {
+            @Override
+            public void onSuccess(Request request)
+            {
+                requests.incrementAndGet();
+            }
+        });
+        // ...and perform the request again => 407 + 302 + 204.
+        ContentResponse response2 = client.newRequest(serverHost, serverPort)
+                .scheme(scheme)
+                .path("/proxy")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(status, response2.getStatus());
+        Assert.assertEquals(3, requests.get());
+
+        // Now the authentication result is cached => 204.
+        requests.set(0);
+        ContentResponse response3 = client.newRequest(serverHost, serverPort)
+                .scheme(scheme)
+                .path("/server")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(status, response3.getStatus());
+        Assert.assertEquals(1, requests.get());
+    }
+
+    @Test
+    public void testProxyAuthenticationWithServerAuthentication() throws Exception
+    {
+        String proxyRealm = "proxyRealm";
+        String serverRealm = "serverRealm";
+        int status = HttpStatus.NO_CONTENT_204;
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                String authorization = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString());
+                if (authorization == null)
+                {
+                    response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407);
+                    response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + proxyRealm + "\"");
+                }
+                else
+                {
+                    authorization = request.getHeader(HttpHeader.AUTHORIZATION.asString());
+                    if (authorization == null)
+                    {
+                        response.setStatus(HttpStatus.UNAUTHORIZED_401);
+                        response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), "Basic realm=\"" + serverRealm + "\"");
+                    }
+                    else
+                    {
+                        response.setStatus(status);
+                    }
+                }
+            }
+        });
+
+        String proxyHost = "localhost";
+        int proxyPort = connector.getLocalPort();
+        String serverHost = "server";
+        int serverPort = proxyPort + 1;
+        URI proxyURI = URI.create(scheme + "://" + proxyHost + ":" + proxyPort);
+        client.getAuthenticationStore().addAuthentication(new BasicAuthentication(proxyURI, proxyRealm, "proxyUser", "proxyPassword"));
+        URI serverURI = URI.create(scheme + "://" + serverHost + ":" + serverPort);
+        client.getAuthenticationStore().addAuthentication(new BasicAuthentication(serverURI, serverRealm, "serverUser", "serverPassword"));
+        client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
+        final AtomicInteger requests = new AtomicInteger();
+        client.getRequestListeners().add(new Request.Listener.Adapter()
+        {
+            @Override
+            public void onSuccess(Request request)
+            {
+                requests.incrementAndGet();
+            }
+        });
+        // Make a request, expect 407 + 401 + 204.
+        ContentResponse response1 = client.newRequest(serverHost, serverPort)
+                .scheme(scheme)
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(status, response1.getStatus());
+        Assert.assertEquals(3, requests.get());
+
+        // Make again the request, authentication is cached, expect 204.
+        requests.set(0);
+        ContentResponse response2 = client.newRequest(serverHost, serverPort)
+                .scheme(scheme)
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(status, response2.getStatus());
+        Assert.assertEquals(1, requests.get());
+    }
+
+    @Test
+    public void testProxyAuthenticationWithExplicitAuthorizationHeader() throws Exception
+    {
+        String proxyRealm = "proxyRealm";
+        String serverRealm = "serverRealm";
+        int status = HttpStatus.NO_CONTENT_204;
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                String authorization = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString());
+                if (authorization == null)
+                {
+                    response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407);
+                    response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + proxyRealm + "\"");
+                }
+                else
+                {
+                    authorization = request.getHeader(HttpHeader.AUTHORIZATION.asString());
+                    if (authorization == null)
+                    {
+                        response.setStatus(HttpStatus.UNAUTHORIZED_401);
+                        response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), "Basic realm=\"" + serverRealm + "\"");
+                    }
+                    else
+                    {
+                        response.setStatus(status);
+                    }
+                }
+            }
+        });
+
+        String proxyHost = "localhost";
+        int proxyPort = connector.getLocalPort();
+        String serverHost = "server";
+        int serverPort = proxyPort + 1;
+        URI proxyURI = URI.create(scheme + "://" + proxyHost + ":" + proxyPort);
+        client.getAuthenticationStore().addAuthentication(new BasicAuthentication(proxyURI, proxyRealm, "proxyUser", "proxyPassword"));
+        client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
+        final AtomicInteger requests = new AtomicInteger();
+        client.getRequestListeners().add(new Request.Listener.Adapter()
+        {
+            @Override
+            public void onSuccess(Request request)
+            {
+                requests.incrementAndGet();
+            }
+        });
+        // Make a request, expect 407 + 204.
+        ContentResponse response1 = client.newRequest(serverHost, serverPort)
+                .scheme(scheme)
+                .header(HttpHeader.AUTHORIZATION, "Basic foobar")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(status, response1.getStatus());
+        Assert.assertEquals(2, requests.get());
+
+        // Make again the request, authentication is cached, expect 204.
+        requests.set(0);
+        ContentResponse response2 = client.newRequest(serverHost, serverPort)
+                .scheme(scheme)
+                .header(HttpHeader.AUTHORIZATION, "Basic foobar")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(status, response2.getStatus());
+        Assert.assertEquals(1, requests.get());
+    }
 }
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java
index 1534800..3aa0510 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java
@@ -22,10 +22,12 @@
 import java.net.URLDecoder;
 import java.nio.ByteBuffer;
 import java.nio.channels.UnresolvedAddressException;
+import java.nio.charset.StandardCharsets;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -43,7 +45,6 @@
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.hamcrest.Matchers;
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -54,15 +55,11 @@
         super(sslContextFactory);
     }
 
-    @Before
-    public void prepare() throws Exception
-    {
-        start(new RedirectHandler());
-    }
-
     @Test
     public void test_303() throws Exception
     {
+        start(new RedirectHandler());
+
         Response response = client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
                 .path("/303/localhost/done")
@@ -76,6 +73,8 @@
     @Test
     public void test_303_302() throws Exception
     {
+        start(new RedirectHandler());
+
         Response response = client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
                 .path("/303/localhost/302/localhost/done")
@@ -89,6 +88,8 @@
     @Test
     public void test_303_302_OnDifferentDestinations() throws Exception
     {
+        start(new RedirectHandler());
+
         Response response = client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
                 .path("/303/127.0.0.1/302/localhost/done")
@@ -102,6 +103,8 @@
     @Test
     public void test_301() throws Exception
     {
+        start(new RedirectHandler());
+
         Response response = client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
                 .method(HttpMethod.HEAD)
@@ -116,6 +119,8 @@
     @Test
     public void test_301_WithWrongMethod() throws Exception
     {
+        start(new RedirectHandler());
+
         try
         {
             client.newRequest("localhost", connector.getLocalPort())
@@ -139,6 +144,8 @@
     @Test
     public void test_307_WithRequestContent() throws Exception
     {
+        start(new RedirectHandler());
+
         byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
         ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
@@ -156,6 +163,7 @@
     @Test
     public void testMaxRedirections() throws Exception
     {
+        start(new RedirectHandler());
         client.setMaxRedirects(1);
 
         try
@@ -180,6 +188,8 @@
     @Test
     public void test_303_WithConnectionClose_WithBigRequest() throws Exception
     {
+        start(new RedirectHandler());
+
         Response response = client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
                 .path("/303/localhost/done?close=true")
@@ -193,6 +203,8 @@
     @Test
     public void testDontFollowRedirects() throws Exception
     {
+        start(new RedirectHandler());
+
         Response response = client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
                 .followRedirects(false)
@@ -207,6 +219,8 @@
     @Test
     public void testRelativeLocation() throws Exception
     {
+        start(new RedirectHandler());
+
         Response response = client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
                 .path("/303/localhost/done?relative=true")
@@ -220,6 +234,8 @@
     @Test
     public void testAbsoluteURIPathWithSpaces() throws Exception
     {
+        start(new RedirectHandler());
+
         Response response = client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
                 .path("/303/localhost/a+space?decode=true")
@@ -233,6 +249,8 @@
     @Test
     public void testRelativeURIPathWithSpaces() throws Exception
     {
+        start(new RedirectHandler());
+
         Response response = client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
                 .path("/303/localhost/a+space?relative=true&decode=true")
@@ -246,7 +264,6 @@
     @Test
     public void testRedirectWithWrongScheme() throws Exception
     {
-        dispose();
         start(new AbstractHandler()
         {
             @Override
@@ -254,7 +271,7 @@
             {
                 baseRequest.setHandled(true);
                 response.setStatus(303);
-                response.setHeader("Location", "ssh://localhost/path");
+                response.setHeader("Location", "ssh://localhost:" + connector.getLocalPort() + "/path");
             }
         });
 
@@ -263,14 +280,10 @@
                 .scheme(scheme)
                 .path("/path")
                 .timeout(5, TimeUnit.SECONDS)
-                .send(new Response.CompleteListener()
+                .send(result ->
                 {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertTrue(result.isFailed());
-                        latch.countDown();
-                    }
+                    Assert.assertTrue(result.isFailed());
+                    latch.countDown();
                 });
         Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
     }
@@ -280,6 +293,8 @@
     public void testRedirectFailed() throws Exception
     {
         // TODO this test is failing with timout after an ISP upgrade??  DNS dependent?
+        start(new RedirectHandler());
+
         try
         {
             client.newRequest("localhost", connector.getLocalPort())
@@ -369,6 +384,7 @@
     @Test
     public void testHttpRedirector() throws Exception
     {
+        start(new RedirectHandler());
         final HttpRedirector redirector = new HttpRedirector(client);
 
         org.eclipse.jetty.client.api.Request request1 = client.newRequest("localhost", connector.getLocalPort())
@@ -389,20 +405,52 @@
         Assert.assertTrue(redirector.isRedirect(response2));
 
         final CountDownLatch latch = new CountDownLatch(1);
-        redirector.redirect(request2, response2, new Response.CompleteListener()
+        redirector.redirect(request2, response2, r ->
         {
-            @Override
-            public void onComplete(Result result)
-            {
-                Response response3 = result.getResponse();
-                Assert.assertEquals(200, response3.getStatus());
-                Assert.assertFalse(redirector.isRedirect(response3));
-                latch.countDown();
-            }
+            Response response3 = r.getResponse();
+            Assert.assertEquals(200, response3.getStatus());
+            Assert.assertFalse(redirector.isRedirect(response3));
+            latch.countDown();
         });
         Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
     }
 
+    @Test
+    public void testRedirectWithCorruptedBody() throws Exception
+    {
+        byte[] bytes = "ok".getBytes(StandardCharsets.UTF_8);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                if (target.startsWith("/redirect"))
+                {
+                    response.setStatus(HttpStatus.SEE_OTHER_303);
+                    response.setHeader(HttpHeader.LOCATION.asString(), scheme + "://localhost:" + connector.getLocalPort() + "/ok");
+                    // Say that we send gzipped content, but actually don't.
+                    response.setHeader(HttpHeader.CONTENT_ENCODING.asString(), "gzip");
+                    response.getOutputStream().write("redirect".getBytes(StandardCharsets.UTF_8));
+                }
+                else
+                {
+                    response.setStatus(HttpStatus.OK_200);
+                    response.getOutputStream().write(bytes);
+                }
+            }
+        });
+
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .path("/redirect")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertArrayEquals(bytes, response.getContent());
+    }
+
     private void testSameMethodRedirect(final HttpMethod method, int redirectCode) throws Exception
     {
         testMethodRedirect(method, method, redirectCode);
@@ -415,6 +463,8 @@
 
     private void testMethodRedirect(final HttpMethod requestMethod, final HttpMethod redirectMethod, int redirectCode) throws Exception
     {
+        start(new RedirectHandler());
+
         final AtomicInteger passes = new AtomicInteger();
         client.getRequestListeners().add(new org.eclipse.jetty.client.api.Request.Listener.Adapter()
         {
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientStreamTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientStreamTest.java
deleted file mode 100644
index 5182c1d..0000000
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientStreamTest.java
+++ /dev/null
@@ -1,1127 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.client;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.AsynchronousCloseException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.Random;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.client.util.BufferingResponseListener;
-import org.eclipse.jetty.client.util.BytesContentProvider;
-import org.eclipse.jetty.client.util.DeferredContentProvider;
-import org.eclipse.jetty.client.util.InputStreamContentProvider;
-import org.eclipse.jetty.client.util.InputStreamResponseListener;
-import org.eclipse.jetty.client.util.OutputStreamContentProvider;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
-import org.eclipse.jetty.toolchain.test.annotation.Slow;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.Assert;
-import org.junit.Test;
-
-import static java.nio.file.StandardOpenOption.CREATE;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-public class HttpClientStreamTest extends AbstractHttpClientServerTest
-{
-    public HttpClientStreamTest(SslContextFactory sslContextFactory)
-    {
-        super(sslContextFactory);
-    }
-
-    @Test
-    public void testFileUpload() throws Exception
-    {
-        // Prepare a big file to upload
-        Path targetTestsDir = MavenTestingUtils.getTargetTestingDir().toPath();
-        Files.createDirectories(targetTestsDir);
-        Path upload = Paths.get(targetTestsDir.toString(), "http_client_upload.big");
-        try (OutputStream output = Files.newOutputStream(upload, CREATE))
-        {
-            byte[] kb = new byte[1024];
-            for (int i = 0; i < 10 * 1024; ++i)
-                output.write(kb);
-        }
-
-        start(new EmptyServerHandler());
-
-        final AtomicLong requestTime = new AtomicLong();
-        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .file(upload)
-                .onRequestSuccess(new Request.SuccessListener()
-                {
-                    @Override
-                    public void onSuccess(Request request)
-                    {
-                        requestTime.set(System.nanoTime());
-                    }
-                })
-                .timeout(10, TimeUnit.SECONDS)
-                .send();
-        long responseTime = System.nanoTime();
-
-        Assert.assertEquals(200, response.getStatus());
-        Assert.assertTrue(requestTime.get() <= responseTime);
-
-        // Give some time to the server to consume the request content
-        // This is just to avoid exception traces in the test output
-        Thread.sleep(1000);
-    }
-
-    @Test
-    public void testDownload() throws Exception
-    {
-        final byte[] data = new byte[128 * 1024];
-        byte value = 1;
-        Arrays.fill(data, value);
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                response.getOutputStream().write(data);
-            }
-        });
-
-        InputStreamResponseListener listener = new InputStreamResponseListener();
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .send(listener);
-        Response response = listener.get(5, TimeUnit.SECONDS);
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-
-        InputStream input = listener.getInputStream();
-        Assert.assertNotNull(input);
-
-        int length = 0;
-        while (input.read() == value)
-        {
-            if (length % 100 == 0)
-                Thread.sleep(1);
-            ++length;
-        }
-
-        Assert.assertEquals(data.length, length);
-
-        Result result = listener.await(5, TimeUnit.SECONDS);
-        Assert.assertNotNull(result);
-        Assert.assertFalse(result.isFailed());
-        Assert.assertSame(response, result.getResponse());
-    }
-
-    @Test
-    public void testDownloadOfUTF8Content() throws Exception
-    {
-        final byte[] data = new byte[]{(byte)0xC3, (byte)0xA8}; // UTF-8 representation of &egrave;
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                response.getOutputStream().write(data);
-            }
-        });
-
-        InputStreamResponseListener listener = new InputStreamResponseListener();
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .send(listener);
-        Response response = listener.get(5, TimeUnit.SECONDS);
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-
-        InputStream input = listener.getInputStream();
-        Assert.assertNotNull(input);
-
-        for (byte b : data)
-        {
-            int read = input.read();
-            assertTrue(read >= 0);
-            Assert.assertEquals(b & 0xFF, read);
-        }
-
-        Assert.assertEquals(-1, input.read());
-
-        Result result = listener.await(5, TimeUnit.SECONDS);
-        Assert.assertNotNull(result);
-        Assert.assertFalse(result.isFailed());
-        Assert.assertSame(response, result.getResponse());
-    }
-
-    @Test
-    public void testDownloadWithFailure() throws Exception
-    {
-        final byte[] data = new byte[64 * 1024];
-        byte value = 1;
-        Arrays.fill(data, value);
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                // Say we want to send this much...
-                response.setContentLength(2 * data.length);
-                // ...but write only half...
-                response.getOutputStream().write(data);
-                // ...then shutdown output
-                baseRequest.getHttpChannel().getEndPoint().shutdownOutput();
-            }
-        });
-
-        InputStreamResponseListener listener = new InputStreamResponseListener();
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .send(listener);
-        Response response = listener.get(5, TimeUnit.SECONDS);
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-
-        InputStream input = listener.getInputStream();
-        Assert.assertNotNull(input);
-
-        int length = 0;
-        try
-        {
-            length = 0;
-            while (input.read() == value)
-            {
-                if (length % 100 == 0)
-                    Thread.sleep(1);
-                ++length;
-            }
-            fail();
-        }
-        catch (IOException expected)
-        {
-        }
-
-        Assert.assertEquals(data.length, length);
-
-        Result result = listener.await(5, TimeUnit.SECONDS);
-        Assert.assertNotNull(result);
-        Assert.assertTrue(result.isFailed());
-    }
-
-    @Test(expected = AsynchronousCloseException.class)
-    public void testInputStreamResponseListenerClosedBeforeReading() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                IO.copy(request.getInputStream(), response.getOutputStream());
-            }
-        });
-
-        InputStreamResponseListener listener = new InputStreamResponseListener();
-        InputStream stream = listener.getInputStream();
-        // Close the stream immediately
-        stream.close();
-
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .content(new BytesContentProvider(new byte[]{0, 1, 2, 3}))
-                .send(listener);
-        Response response = listener.get(5, TimeUnit.SECONDS);
-        Assert.assertEquals(200, response.getStatus());
-
-        stream.read(); // Throws
-    }
-
-    @Test
-    public void testInputStreamResponseListenerClosedWhileWaiting() throws Exception
-    {
-        final byte[] chunk1 = new byte[]{0, 1};
-        final byte[] chunk2 = new byte[]{2, 3};
-        final CountDownLatch closeLatch = new CountDownLatch(1);
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                response.setContentLength(chunk1.length + chunk2.length);
-                ServletOutputStream output = response.getOutputStream();
-                output.write(chunk1);
-                output.flush();
-                try
-                {
-                    closeLatch.await(5, TimeUnit.SECONDS);
-                    output.write(chunk2);
-                    output.flush();
-                }
-                catch (InterruptedException x)
-                {
-                    throw new InterruptedIOException();
-                }
-            }
-        });
-
-        final CountDownLatch waitLatch = new CountDownLatch(1);
-        final CountDownLatch waitedLatch = new CountDownLatch(1);
-        InputStreamResponseListener listener = new InputStreamResponseListener(1)
-        {
-            @Override
-            protected boolean await()
-            {
-                waitLatch.countDown();
-                boolean result = super.await();
-                waitedLatch.countDown();
-                return result;
-            }
-        };
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .send(listener);
-        Response response = listener.get(5, TimeUnit.SECONDS);
-        Assert.assertEquals(200, response.getStatus());
-
-        InputStream stream = listener.getInputStream();
-        // Wait until we block
-        Assert.assertTrue(waitLatch.await(5, TimeUnit.SECONDS));
-        // Close the stream
-        stream.close();
-        closeLatch.countDown();
-
-        // Be sure we're not stuck waiting
-        Assert.assertTrue(waitedLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testInputStreamResponseListenerFailedWhileWaiting() throws Exception
-    {
-        final byte[] chunk1 = new byte[]{0, 1};
-        final byte[] chunk2 = new byte[]{2, 3};
-        final CountDownLatch closeLatch = new CountDownLatch(1);
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                response.setContentLength(chunk1.length + chunk2.length);
-                ServletOutputStream output = response.getOutputStream();
-                output.write(chunk1);
-                output.flush();
-                try
-                {
-                    closeLatch.await(5, TimeUnit.SECONDS);
-                    output.write(chunk2);
-                    output.flush();
-                }
-                catch (InterruptedException x)
-                {
-                    throw new InterruptedIOException();
-                }
-            }
-        });
-
-        final CountDownLatch waitLatch = new CountDownLatch(1);
-        final CountDownLatch waitedLatch = new CountDownLatch(1);
-        InputStreamResponseListener listener = new InputStreamResponseListener(1)
-        {
-            @Override
-            protected boolean await()
-            {
-                waitLatch.countDown();
-                boolean result = super.await();
-                waitedLatch.countDown();
-                return result;
-            }
-        };
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .send(listener);
-        Response response = listener.get(5, TimeUnit.SECONDS);
-        Assert.assertEquals(200, response.getStatus());
-
-        // Wait until we block
-        Assert.assertTrue(waitLatch.await(5, TimeUnit.SECONDS));
-        // Fail the response
-        response.abort(new Exception());
-        closeLatch.countDown();
-
-        // Be sure we're not stuck waiting
-        Assert.assertTrue(waitedLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testInputStreamResponseListenerConsumingBeforeWaiting() throws Exception
-    {
-        final byte[] data = new byte[]{0, 1};
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                response.setContentLength(data.length);
-                ServletOutputStream output = response.getOutputStream();
-                output.write(data);
-                output.flush();
-            }
-        });
-
-        final AtomicReference<Throwable> failure = new AtomicReference<>();
-        InputStreamResponseListener listener = new InputStreamResponseListener(1)
-        {
-            @Override
-            protected boolean await()
-            {
-                // Consume everything just before waiting
-                InputStream stream = getInputStream();
-                consume(stream, data);
-                return super.await();
-            }
-
-            private void consume(InputStream stream, byte[] data)
-            {
-                try
-                {
-                    for (byte datum : data)
-                        Assert.assertEquals(datum, stream.read());
-                }
-                catch (IOException x)
-                {
-                    failure.compareAndSet(null, x);
-                }
-            }
-        };
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .send(listener);
-        Result result = listener.await(5, TimeUnit.SECONDS);
-        Assert.assertEquals(200, result.getResponse().getStatus());
-        Assert.assertNull(failure.get());
-    }
-
-    @Test
-    public void testInputStreamResponseListenerFailedBeforeResponse() throws Exception
-    {
-        start(new EmptyServerHandler());
-        int port = connector.getLocalPort();
-        server.stop();
-
-        InputStreamResponseListener listener = new InputStreamResponseListener();
-        // Connect to the wrong port
-        client.newRequest("localhost", port)
-                .scheme(scheme)
-                .send(listener);
-        Result result = listener.await(5, TimeUnit.SECONDS);
-        Assert.assertNotNull(result);
-    }
-
-    @Test(expected = ExecutionException.class)
-    public void testInputStreamContentProviderThrowingWhileReading() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                IO.copy(request.getInputStream(), response.getOutputStream());
-            }
-        });
-
-        final byte[] data = new byte[]{0, 1, 2, 3};
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .content(new InputStreamContentProvider(new InputStream()
-                {
-                    private int index = 0;
-
-                    @Override
-                    public int read() throws IOException
-                    {
-                        // Will eventually throw ArrayIndexOutOfBounds
-                        return data[index++];
-                    }
-                }, data.length / 2))
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-    }
-
-    @Test(expected = AsynchronousCloseException.class)
-    public void testDownloadWithCloseBeforeContent() throws Exception
-    {
-        final byte[] data = new byte[128 * 1024];
-        byte value = 3;
-        Arrays.fill(data, value);
-        final CountDownLatch latch = new CountDownLatch(1);
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                response.flushBuffer();
-
-                try
-                {
-                    Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-                }
-                catch (InterruptedException e)
-                {
-                    throw new InterruptedIOException();
-                }
-
-                response.getOutputStream().write(data);
-            }
-        });
-
-        InputStreamResponseListener listener = new InputStreamResponseListener();
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .send(listener);
-        Response response = listener.get(5, TimeUnit.SECONDS);
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-
-        InputStream input = listener.getInputStream();
-        Assert.assertNotNull(input);
-        input.close();
-
-        latch.countDown();
-
-        input.read(); // Throws
-    }
-
-    @Test(expected = AsynchronousCloseException.class)
-    public void testDownloadWithCloseMiddleOfContent() throws Exception
-    {
-        final byte[] data1 = new byte[1024];
-        final byte[] data2 = new byte[1024];
-        final CountDownLatch latch = new CountDownLatch(1);
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                response.getOutputStream().write(data1);
-                response.flushBuffer();
-
-                try
-                {
-                    Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-                }
-                catch (InterruptedException e)
-                {
-                    throw new InterruptedIOException();
-                }
-
-                response.getOutputStream().write(data2);
-            }
-        });
-
-        InputStreamResponseListener listener = new InputStreamResponseListener();
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .send(listener);
-        Response response = listener.get(5, TimeUnit.SECONDS);
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-
-        InputStream input = listener.getInputStream();
-        Assert.assertNotNull(input);
-
-        for (byte datum1 : data1)
-            Assert.assertEquals(datum1, input.read());
-
-        input.close();
-
-        latch.countDown();
-
-        input.read(); // Throws
-    }
-
-    @Test
-    public void testDownloadWithCloseEndOfContent() throws Exception
-    {
-        final byte[] data = new byte[1024];
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                response.getOutputStream().write(data);
-                response.flushBuffer();
-            }
-        });
-
-        InputStreamResponseListener listener = new InputStreamResponseListener();
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .send(listener);
-        Response response = listener.get(5, TimeUnit.SECONDS);
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-
-        InputStream input = listener.getInputStream();
-        Assert.assertNotNull(input);
-
-        for (byte datum : data)
-            Assert.assertEquals(datum, input.read());
-
-        // Read EOF
-        Assert.assertEquals(-1, input.read());
-
-        input.close();
-
-        // Must not throw
-        Assert.assertEquals(-1, input.read());
-    }
-
-    @Slow
-    @Test
-    public void testUploadWithDeferredContentProviderFromInputStream() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                IO.copy(request.getInputStream(), new ByteArrayOutputStream());
-            }
-        });
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        try (DeferredContentProvider content = new DeferredContentProvider())
-        {
-            client.newRequest("localhost", connector.getLocalPort())
-                    .scheme(scheme)
-                    .content(content)
-                    .send(new Response.CompleteListener()
-                    {
-                        @Override
-                        public void onComplete(Result result)
-                        {
-                            if (result.isSucceeded() && result.getResponse().getStatus() == 200)
-                                latch.countDown();
-                        }
-                    });
-
-            // Make sure we provide the content *after* the request has been "sent".
-            Thread.sleep(1000);
-
-            try (ByteArrayInputStream input = new ByteArrayInputStream(new byte[1024]))
-            {
-                byte[] buffer = new byte[200];
-                int read;
-                while ((read = input.read(buffer)) >= 0)
-                    content.offer(ByteBuffer.wrap(buffer, 0, read));
-            }
-        }
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testUploadWithDeferredContentAvailableCallbacksNotifiedOnce() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                IO.copy(request.getInputStream(), new ByteArrayOutputStream());
-            }
-        });
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        final AtomicInteger succeeds = new AtomicInteger();
-        try (DeferredContentProvider content = new DeferredContentProvider())
-        {
-            // Make the content immediately available.
-            content.offer(ByteBuffer.allocate(1024), new Callback.Adapter()
-            {
-                @Override
-                public void succeeded()
-                {
-                    succeeds.incrementAndGet();
-                }
-            });
-
-            client.newRequest("localhost", connector.getLocalPort())
-                    .scheme(scheme)
-                    .content(content)
-                    .send(new Response.CompleteListener()
-                    {
-                        @Override
-                        public void onComplete(Result result)
-                        {
-                            if (result.isSucceeded() && result.getResponse().getStatus() == 200)
-                                latch.countDown();
-                        }
-                    });
-        }
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-        Assert.assertEquals(1, succeeds.get());
-    }
-
-    @Test
-    public void testUploadWithDeferredContentProviderRacingWithSend() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                IO.copy(request.getInputStream(), response.getOutputStream());
-            }
-        });
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        final byte[] data = new byte[512];
-        final DeferredContentProvider content = new DeferredContentProvider()
-        {
-            @Override
-            public void setListener(Listener listener)
-            {
-                super.setListener(listener);
-                // Simulate a concurrent call
-                offer(ByteBuffer.wrap(data));
-                close();
-            }
-        };
-
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .content(content)
-                .send(new BufferingResponseListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        if (result.isSucceeded() &&
-                                result.getResponse().getStatus() == 200 &&
-                                Arrays.equals(data, getContent()))
-                            latch.countDown();
-                    }
-                });
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testUploadWithDeferredContentProviderRacingWithIterator() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                IO.copy(request.getInputStream(), response.getOutputStream());
-            }
-        });
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        final byte[] data = new byte[512];
-        final AtomicReference<DeferredContentProvider> contentRef = new AtomicReference<>();
-        final DeferredContentProvider content = new DeferredContentProvider()
-        {
-            @Override
-            public Iterator<ByteBuffer> iterator()
-            {
-                return new Iterator<ByteBuffer>()
-                {
-                    // Data for the deferred content iterator:
-                    // [0] => deferred
-                    // [1] => deferred
-                    // [2] => data
-                    private final byte[][] iteratorData = new byte[3][];
-                    private final AtomicInteger index = new AtomicInteger();
-
-                    {
-                        iteratorData[0] = null;
-                        iteratorData[1] = null;
-                        iteratorData[2] = data;
-                    }
-
-                    @Override
-                    public boolean hasNext()
-                    {
-                        return index.get() < iteratorData.length;
-                    }
-
-                    @Override
-                    public ByteBuffer next()
-                    {
-                        byte[] chunk = iteratorData[index.getAndIncrement()];
-                        ByteBuffer result = chunk == null ? null : ByteBuffer.wrap(chunk);
-                        if (index.get() == 2)
-                        {
-                            contentRef.get().offer(result == null ? BufferUtil.EMPTY_BUFFER : result);
-                            contentRef.get().close();
-                        }
-                        return result;
-                    }
-
-                    @Override
-                    public void remove()
-                    {
-                    }
-                };
-            }
-        };
-        contentRef.set(content);
-
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .content(content)
-                .send(new BufferingResponseListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        if (result.isSucceeded() &&
-                                result.getResponse().getStatus() == 200 &&
-                                Arrays.equals(data, getContent()))
-                            latch.countDown();
-                    }
-                });
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testUploadWithOutputStream() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                IO.copy(request.getInputStream(), response.getOutputStream());
-            }
-        });
-
-        final byte[] data = new byte[512];
-        final CountDownLatch latch = new CountDownLatch(1);
-        OutputStreamContentProvider content = new OutputStreamContentProvider();
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .content(content)
-                .send(new BufferingResponseListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        if (result.isSucceeded() &&
-                                result.getResponse().getStatus() == 200 &&
-                                Arrays.equals(data, getContent()))
-                            latch.countDown();
-                    }
-                });
-
-        // Make sure we provide the content *after* the request has been "sent".
-        Thread.sleep(1000);
-
-        try (OutputStream output = content.getOutputStream())
-        {
-            output.write(data);
-        }
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testBigUploadWithOutputStreamFromInputStream() throws Exception
-    {
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                IO.copy(request.getInputStream(), response.getOutputStream());
-            }
-        });
-
-        final byte[] data = new byte[16 * 1024 * 1024];
-        new Random().nextBytes(data);
-        final CountDownLatch latch = new CountDownLatch(1);
-        OutputStreamContentProvider content = new OutputStreamContentProvider();
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .content(content)
-                .send(new BufferingResponseListener(data.length)
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertTrue(result.isSucceeded());
-                        Assert.assertEquals(200, result.getResponse().getStatus());
-                        Assert.assertArrayEquals(data, getContent());
-                        latch.countDown();
-                    }
-                });
-
-        // Make sure we provide the content *after* the request has been "sent".
-        Thread.sleep(1000);
-
-        try (InputStream input = new ByteArrayInputStream(data); OutputStream output = content.getOutputStream())
-        {
-            byte[] buffer = new byte[1024];
-            while (true)
-            {
-                int read = input.read(buffer);
-                if (read < 0)
-                    break;
-                output.write(buffer, 0, read);
-            }
-        }
-
-        Assert.assertTrue(latch.await(30, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testUploadWithOutputStreamFailureToConnect() throws Exception
-    {
-        start(new EmptyServerHandler());
-
-        final byte[] data = new byte[512];
-        final CountDownLatch latch = new CountDownLatch(1);
-        OutputStreamContentProvider content = new OutputStreamContentProvider();
-        client.newRequest("0.0.0.1", connector.getLocalPort())
-                .scheme(scheme)
-                .content(content)
-                .send(new Response.CompleteListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        if (result.isFailed())
-                            latch.countDown();
-                    }
-                });
-
-        try (OutputStream output = content.getOutputStream())
-        {
-            output.write(data);
-            Assert.fail();
-        }
-        catch (IOException x)
-        {
-            // Expected
-        }
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testUploadWithDeferredContentProviderFailsMultipleOffers() throws Exception
-    {
-        start(new EmptyServerHandler());
-
-        final CountDownLatch failLatch = new CountDownLatch(2);
-        final Callback.Adapter callback = new Callback.Adapter()
-        {
-            @Override
-            public void failed(Throwable x)
-            {
-                failLatch.countDown();
-            }
-        };
-
-        final CountDownLatch completeLatch = new CountDownLatch(1);
-        final DeferredContentProvider content = new DeferredContentProvider();
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .content(content)
-                .onRequestBegin(new Request.BeginListener()
-                {
-                    @Override
-                    public void onBegin(Request request)
-                    {
-                        content.offer(ByteBuffer.wrap(new byte[256]), callback);
-                        content.offer(ByteBuffer.wrap(new byte[256]), callback);
-                        request.abort(new Exception("explicitly_thrown_by_test"));
-                    }
-                })
-                .send(new Response.CompleteListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        if (result.isFailed())
-                            completeLatch.countDown();
-                    }
-                });
-        Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(failLatch.await(5, TimeUnit.SECONDS));
-
-        // Make sure that adding more content results in the callback to be failed.
-        final CountDownLatch latch = new CountDownLatch(1);
-        content.offer(ByteBuffer.wrap(new byte[128]), new Callback.Adapter()
-        {
-            @Override
-            public void failed(Throwable x)
-            {
-                latch.countDown();
-            }
-        });
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testUploadWithConnectFailureClosesStream() throws Exception
-    {
-        start(new EmptyServerHandler());
-
-        final CountDownLatch closeLatch = new CountDownLatch(1);
-        InputStream stream = new ByteArrayInputStream("test".getBytes(StandardCharsets.UTF_8))
-        {
-            @Override
-            public void close() throws IOException
-            {
-                super.close();
-                closeLatch.countDown();
-            }
-        };
-        InputStreamContentProvider content = new InputStreamContentProvider(stream);
-
-        final CountDownLatch completeLatch = new CountDownLatch(1);
-        client.newRequest("0.0.0.1", connector.getLocalPort())
-                .scheme(scheme)
-                .content(content)
-                .send(new Response.CompleteListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertTrue(result.isFailed());
-                        completeLatch.countDown();
-                    }
-                });
-
-        Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testUploadWithWriteFailureClosesStream() throws Exception
-    {
-        start(new EmptyServerHandler());
-
-        final AtomicInteger bytes = new AtomicInteger();
-        final CountDownLatch closeLatch = new CountDownLatch(1);
-        InputStream stream = new InputStream()
-        {
-            @Override
-            public int read() throws IOException
-            {
-                int result = bytes.incrementAndGet();
-                switch (result)
-                {
-                    case 1:
-                    {
-                        break;
-                    }
-                    case 2:
-                    {
-                        try
-                        {
-                            connector.stop();
-                        }
-                        catch (Exception x)
-                        {
-                            throw new IOException(x);
-                        }
-                        break;
-                    }
-                    default:
-                    {
-                        result = -1;
-                        break;
-                    }
-                }
-                return result;
-            }
-
-            @Override
-            public void close() throws IOException
-            {
-                super.close();
-                closeLatch.countDown();
-            }
-        };
-        InputStreamContentProvider provider = new InputStreamContentProvider(stream, 1);
-
-        final CountDownLatch completeLatch = new CountDownLatch(1);
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .content(provider)
-                .send(new Response.CompleteListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertTrue(result.isFailed());
-                        completeLatch.countDown();
-                    }
-                });
-
-        Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
-    }
-}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java
index 2122f37..5b1e53a 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java
@@ -18,10 +18,17 @@
 
 package org.eclipse.jetty.client;
 
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.ssl.SslHandshakeListener;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
@@ -86,10 +93,30 @@
         serverTLSFactory.setIncludeProtocols("TLSv1.2");
         startServer(serverTLSFactory, new EmptyServerHandler());
 
+        CountDownLatch serverLatch = new CountDownLatch(1);
+        connector.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeFailed(Event event, Throwable failure)
+            {
+                serverLatch.countDown();
+            }
+        });
+
         SslContextFactory clientTLSFactory = createSslContextFactory();
         clientTLSFactory.setIncludeProtocols("TLSv1.1");
         startClient(clientTLSFactory);
 
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeFailed(Event event, Throwable failure)
+            {
+                clientLatch.countDown();
+            }
+        });
+
         try
         {
             client.newRequest("localhost", connector.getLocalPort())
@@ -102,6 +129,9 @@
         {
             // Expected.
         }
+
+        Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
     }
 
     @Test
@@ -111,10 +141,30 @@
         serverTLSFactory.setIncludeCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA");
         startServer(serverTLSFactory, new EmptyServerHandler());
 
+        CountDownLatch serverLatch = new CountDownLatch(1);
+        connector.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeFailed(Event event, Throwable failure)
+            {
+                serverLatch.countDown();
+            }
+        });
+
         SslContextFactory clientTLSFactory = createSslContextFactory();
         clientTLSFactory.setExcludeCipherSuites(".*_SHA$");
         startClient(clientTLSFactory);
 
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeFailed(Event event, Throwable failure)
+            {
+                clientLatch.countDown();
+            }
+        });
+
         try
         {
             client.newRequest("localhost", connector.getLocalPort())
@@ -127,6 +177,9 @@
         {
             // Expected.
         }
+
+        Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
     }
 
     @Test
@@ -138,9 +191,29 @@
         serverTLSFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
         startServer(serverTLSFactory, new EmptyServerHandler());
 
+        CountDownLatch serverLatch = new CountDownLatch(1);
+        connector.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeFailed(Event event, Throwable failure)
+            {
+                serverLatch.countDown();
+            }
+        });
+
         SslContextFactory clientTLSFactory = createSslContextFactory();
         startClient(clientTLSFactory);
 
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeFailed(Event event, Throwable failure)
+            {
+                clientLatch.countDown();
+            }
+        });
+
         try
         {
             client.newRequest("localhost", connector.getLocalPort())
@@ -153,6 +226,9 @@
         {
             // Expected.
         }
+
+        Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
     }
 
     @Test
@@ -161,12 +237,32 @@
         SslContextFactory serverTLSFactory = createSslContextFactory();
         startServer(serverTLSFactory, new EmptyServerHandler());
 
+        CountDownLatch serverLatch = new CountDownLatch(1);
+        connector.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeFailed(Event event, Throwable failure)
+            {
+                serverLatch.countDown();
+            }
+        });
+
         SslContextFactory clientTLSFactory = createSslContextFactory();
         // TLS 1.1 protocol, but only TLS 1.2 ciphers.
         clientTLSFactory.setIncludeProtocols("TLSv1.1");
         clientTLSFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
         startClient(clientTLSFactory);
 
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeFailed(Event event, Throwable failure)
+            {
+                clientLatch.countDown();
+            }
+        });
+
         try
         {
             client.newRequest("localhost", connector.getLocalPort())
@@ -179,5 +275,121 @@
         {
             // Expected.
         }
+
+        Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testHandshakeSucceeded() throws Exception
+    {
+        SslContextFactory serverTLSFactory = createSslContextFactory();
+        startServer(serverTLSFactory, new EmptyServerHandler());
+
+        CountDownLatch serverLatch = new CountDownLatch(1);
+        connector.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeSucceeded(Event event)
+            {
+                serverLatch.countDown();
+            }
+        });
+
+        SslContextFactory clientTLSFactory = createSslContextFactory();
+        startClient(clientTLSFactory);
+
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeSucceeded(Event event)
+            {
+                clientLatch.countDown();
+            }
+        });
+
+        ContentResponse response = client.GET("https://localhost:" + connector.getLocalPort());
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testHandshakeSucceededWithSessionResumption() throws Exception
+    {
+        SslContextFactory serverTLSFactory = createSslContextFactory();
+        startServer(serverTLSFactory, new EmptyServerHandler());
+
+        AtomicReference<byte[]> serverSession = new AtomicReference<>();
+        connector.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeSucceeded(Event event)
+            {
+                serverSession.set(event.getSSLEngine().getSession().getId());
+            }
+        });
+
+        SslContextFactory clientTLSFactory = createSslContextFactory();
+        startClient(clientTLSFactory);
+
+        AtomicReference<byte[]> clientSession = new AtomicReference<>();
+        client.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeSucceeded(Event event)
+            {
+                clientSession.set(event.getSSLEngine().getSession().getId());
+            }
+        });
+
+        // First request primes the TLS session.
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(HttpScheme.HTTPS.asString())
+                .header(HttpHeader.CONNECTION, "close")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        Assert.assertNotNull(serverSession.get());
+        Assert.assertNotNull(clientSession.get());
+
+        connector.removeBean(connector.getBean(SslHandshakeListener.class));
+        client.removeBean(client.getBean(SslHandshakeListener.class));
+
+        CountDownLatch serverLatch = new CountDownLatch(1);
+        connector.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeSucceeded(Event event)
+            {
+                if (Arrays.equals(serverSession.get(), event.getSSLEngine().getSession().getId()))
+                    serverLatch.countDown();
+            }
+        });
+
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.addBean(new SslHandshakeListener()
+        {
+            @Override
+            public void handshakeSucceeded(Event event)
+            {
+                if (Arrays.equals(clientSession.get(), event.getSSLEngine().getSession().getId()))
+                    clientLatch.countDown();
+            }
+        });
+
+        // Second request should have the same session ID.
+        response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(HttpScheme.HTTPS.asString())
+                .header(HttpHeader.CONNECTION, "close")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
     }
 }
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
index edc0776..539a66b 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
@@ -22,14 +22,18 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.HttpCookie;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
 import java.net.URI;
 import java.net.URLEncoder;
+import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
-import java.nio.channels.UnresolvedAddressException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -72,6 +76,7 @@
 import org.eclipse.jetty.http.HttpHeaderValue;
 import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.toolchain.test.TestingDir;
@@ -80,6 +85,8 @@
 import org.eclipse.jetty.util.FuturePromise;
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.SocketAddressResolver;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.hamcrest.Matchers;
 import org.junit.Assert;
@@ -87,8 +94,6 @@
 import org.junit.Rule;
 import org.junit.Test;
 
-import static java.nio.file.StandardOpenOption.CREATE;
-
 public class HttpClientTest extends AbstractHttpClientServerTest
 {
     @Rule
@@ -111,7 +116,7 @@
         Assert.assertEquals(200, response.getStatus());
 
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
 
         long start = System.nanoTime();
         HttpConnectionOverHTTP connection = null;
@@ -128,8 +133,8 @@
         client.stop();
 
         Assert.assertEquals(0, client.getDestinations().size());
-        Assert.assertEquals(0, connectionPool.getIdleConnections().size());
-        Assert.assertEquals(0, connectionPool.getActiveConnections().size());
+        Assert.assertEquals(0, connectionPool.getIdleConnectionCount());
+        Assert.assertEquals(0, connectionPool.getActiveConnectionCount());
         Assert.assertFalse(connection.getEndPoint().isOpen());
     }
 
@@ -330,7 +335,7 @@
             public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
             {
                 baseRequest.setHandled(true);
-                consume(request.getInputStream());
+                consume(request.getInputStream(), true);
                 String value = request.getParameter(paramName);
                 if (paramValue.equals(value))
                 {
@@ -361,22 +366,18 @@
             public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
             {
                 baseRequest.setHandled(true);
-                consume(request.getInputStream());
+                consume(request.getInputStream(), true);
             }
         });
 
         final byte[] content = {0, 1, 2, 3};
         ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
-                .onRequestContent(new Request.ContentListener()
+                .onRequestContent((request, buffer) ->
                 {
-                    @Override
-                    public void onContent(Request request, ByteBuffer buffer)
-                    {
-                        byte[] bytes = new byte[buffer.remaining()];
-                        buffer.get(bytes);
-                        if (!Arrays.equals(content, bytes))
-                            request.abort(new Exception());
-                    }
+                    byte[] bytes = new byte[buffer.remaining()];
+                    buffer.get(bytes);
+                    if (!Arrays.equals(content, bytes))
+                        request.abort(new Exception());
                 })
                 .content(new BytesContentProvider(content))
                 .timeout(5, TimeUnit.SECONDS)
@@ -395,22 +396,18 @@
             public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
             {
                 baseRequest.setHandled(true);
-                consume(request.getInputStream());
+                consume(request.getInputStream(), true);
             }
         });
 
         final AtomicInteger progress = new AtomicInteger();
         ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
-                .onRequestContent(new Request.ContentListener()
+                .onRequestContent((request, buffer) ->
                 {
-                    @Override
-                    public void onContent(Request request, ByteBuffer buffer)
-                    {
-                        byte[] bytes = new byte[buffer.remaining()];
-                        Assert.assertEquals(1, bytes.length);
-                        buffer.get(bytes);
-                        Assert.assertEquals(bytes[0], progress.getAndIncrement());
-                    }
+                    byte[] bytes = new byte[buffer.remaining()];
+                    Assert.assertEquals(1, bytes.length);
+                    buffer.get(bytes);
+                    Assert.assertEquals(bytes[0], progress.getAndIncrement());
                 })
                 .content(new BytesContentProvider(new byte[]{0}, new byte[]{1}, new byte[]{2}, new byte[]{3}, new byte[]{4}))
                 .timeout(5, TimeUnit.SECONDS)
@@ -432,19 +429,15 @@
         final CountDownLatch successLatch = new CountDownLatch(2);
         client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
-                .onRequestBegin(new Request.BeginListener()
+                .onRequestBegin(request ->
                 {
-                    @Override
-                    public void onBegin(Request request)
+                    try
                     {
-                        try
-                        {
-                            latch.await();
-                        }
-                        catch (InterruptedException x)
-                        {
-                            x.printStackTrace();
-                        }
+                        latch.await();
+                    }
+                    catch (InterruptedException x)
+                    {
+                        x.printStackTrace();
                     }
                 })
                 .send(new Response.Listener.Adapter()
@@ -459,14 +452,7 @@
 
         client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
-                .onRequestQueued(new Request.QueuedListener()
-                {
-                    @Override
-                    public void onQueued(Request request)
-                    {
-                        latch.countDown();
-                    }
-                })
+                .onRequestQueued(request -> latch.countDown())
                 .send(new Response.Listener.Adapter()
                 {
                     @Override
@@ -483,74 +469,53 @@
     @Test
     public void test_QueuedRequest_IsSent_WhenPreviousRequestClosedConnection() throws Exception
     {
-        start(new EmptyServerHandler());
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                if (target.endsWith("/one"))
+                    baseRequest.getHttpChannel().getEndPoint().close();
+                else
+                    baseRequest.setHandled(true);
+            }
+        });
 
         client.setMaxConnectionsPerDestination(1);
-        final long idleTimeout = 1000;
-        client.setIdleTimeout(idleTimeout);
 
-        final CountDownLatch latch = new CountDownLatch(3);
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .path("/one")
-                .listener(new Request.Listener.Adapter()
-                {
-                    @Override
-                    public void onBegin(Request request)
-                    {
-                        try
-                        {
-                            TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
-                        }
-                        catch (InterruptedException x)
-                        {
-                            x.printStackTrace();
-                        }
-                    }
+        try (StacklessLogging stackless = new StacklessLogging(org.eclipse.jetty.server.HttpChannel.class))
+        {
+            final CountDownLatch latch = new CountDownLatch(2);
+            client.newRequest("localhost", connector.getLocalPort())
+            .scheme(scheme)
+            .path("/one")
+            .onResponseFailure((response, failure) -> latch.countDown())
+            .send(null);
 
-                    @Override
-                    public void onFailure(Request request, Throwable failure)
-                    {
-                        latch.countDown();
-                    }
-                })
-                .onResponseFailure(new Response.FailureListener()
-                {
-                    @Override
-                    public void onFailure(Response response, Throwable failure)
-                    {
-                        latch.countDown();
-                    }
-                })
-                .send(null);
+            client.newRequest("localhost", connector.getLocalPort())
+            .scheme(scheme)
+            .path("/two")
+            .onResponseSuccess(response ->
+            {
+                Assert.assertEquals(200, response.getStatus());
+                latch.countDown();
+            })
+            .send(null);
 
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .path("/two")
-                .onResponseSuccess(new Response.SuccessListener()
-                {
-                    @Override
-                    public void onSuccess(Response response)
-                    {
-                        Assert.assertEquals(200, response.getStatus());
-                        latch.countDown();
-                    }
-                })
-                .send(null);
-
-        Assert.assertTrue(latch.await(5 * idleTimeout, TimeUnit.MILLISECONDS));
+            Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        }
     }
 
     @Test
     public void test_ExchangeIsComplete_OnlyWhenBothRequestAndResponseAreComplete() throws Exception
     {
-        start(new EmptyServerHandler());
+        start(new RespondThenConsumeHandler());
 
         // Prepare a big file to upload
-        Path targetTestsDir = testdir.getEmptyDir().toPath();
+        Path targetTestsDir = testdir.getEmptyPathDir();
         Files.createDirectories(targetTestsDir);
         Path file = Paths.get(targetTestsDir.toString(), "http_client_conversation.big");
-        try (OutputStream output = Files.newOutputStream(file, CREATE))
+        try (OutputStream output = Files.newOutputStream(file, StandardOpenOption.CREATE))
         {
             byte[] kb = new byte[1024];
             for (int i = 0; i < 10 * 1024; ++i)
@@ -564,14 +529,10 @@
         client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
                 .file(file)
-                .onRequestSuccess(new Request.SuccessListener()
+                .onRequestSuccess(request ->
                 {
-                    @Override
-                    public void onSuccess(Request request)
-                    {
-                        requestTime.set(System.nanoTime());
-                        latch.countDown();
-                    }
+                    requestTime.set(System.nanoTime());
+                    latch.countDown();
                 })
                 .send(new Response.Listener.Adapter()
                 {
@@ -674,14 +635,10 @@
         final int port = connector.getLocalPort();
         client.newRequest(host, port)
                 .scheme(scheme)
-                .onRequestBegin(new Request.BeginListener()
+                .onRequestBegin(request ->
                 {
-                    @Override
-                    public void onBegin(Request request)
-                    {
-                        HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
-                        destination.getConnectionPool().getActiveConnections().peek().close();
-                    }
+                    HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
+                    destination.getConnectionPool().getActiveConnections().peek().close();
                 })
                 .send(new Response.Listener.Adapter()
                 {
@@ -773,14 +730,7 @@
 
         ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
-                .onResponseHeader(new Response.HeaderListener()
-                {
-                    @Override
-                    public boolean onHeader(Response response, HttpField field)
-                    {
-                        return !field.getName().equals(headerName);
-                    }
-                })
+                .onResponseHeader((response1, field) -> !field.getName().equals(headerName))
                 .timeout(5, TimeUnit.SECONDS)
                 .send();
 
@@ -858,26 +808,73 @@
     }
 
     @Test
-    public void testConnectThrowsUnresolvedAddressException() throws Exception
+    public void testConnectThrowsUnknownHostException() throws Exception
     {
+        String host = "idontexist";
+        int port = 80;
+
+        try
+        {
+            Socket socket = new Socket();
+            socket.connect(new InetSocketAddress(host, port), 1000);
+            Assume.assumeTrue("Host must not be resolvable", false);
+        }
+        catch (IOException ignored)
+        {
+        }
+
         start(new EmptyServerHandler());
 
         final CountDownLatch latch = new CountDownLatch(1);
-        client.newRequest("idontexist", 80)
-                .send(new Response.CompleteListener()
+        client.newRequest(host, port)
+                .send(result ->
                 {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        Assert.assertTrue(result.isFailed());
-                        Assert.assertTrue(result.getFailure() instanceof UnresolvedAddressException);
-                        latch.countDown();
-                    }
+                    Assert.assertTrue(result.isFailed());
+                    Throwable failure = result.getFailure();
+                    Assert.assertTrue(failure instanceof UnknownHostException);
+                    latch.countDown();
                 });
         Assert.assertTrue(latch.await(10, TimeUnit.SECONDS));
     }
 
     @Test
+    public void testConnectHostWithMultipleAddresses() throws Exception
+    {
+        start(new EmptyServerHandler());
+
+        client.setSocketAddressResolver(new SocketAddressResolver.Async(client.getExecutor(), client.getScheduler(), client.getConnectTimeout())
+        {
+            @Override
+            public void resolve(String host, int port, Promise<List<InetSocketAddress>> promise)
+            {
+                super.resolve(host, port, new Promise<List<InetSocketAddress>>()
+                {
+                    @Override
+                    public void succeeded(List<InetSocketAddress> result)
+                    {
+                        // Add as first address an invalid address so that we test
+                        // that the connect operation iterates over the addresses.
+                        result.add(0, new InetSocketAddress("idontexist", port));
+                        promise.succeeded(result);
+                    }
+
+                    @Override
+                    public void failed(Throwable x)
+                    {
+                        promise.failed(x);
+                    }
+                });
+            }
+        });
+
+        // If no exceptions the test passes.
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .header(HttpHeader.CONNECTION, "close")
+                .send();
+    }
+
+    @Test
     public void testCustomUserAgent() throws Exception
     {
         final String userAgent = "Test/1.0";
@@ -1123,7 +1120,7 @@
             }
         });
 
-        final Exchanger<Response> ex = new Exchanger<Response>();
+        final Exchanger<Response> ex = new Exchanger<>();
         BufferingResponseListener listener = new BufferingResponseListener()
         {
             @Override
@@ -1236,7 +1233,7 @@
             // before closing the connection, so we need to wait before checking
             // that the connection is closed to avoid races.
             Thread.sleep(1000);
-            Assert.assertTrue(((HttpConnectionOverHTTP)connection).isClosed());
+            Assert.assertTrue(connection.isClosed());
         }
     }
 
@@ -1274,14 +1271,10 @@
         final CountDownLatch completeLatch = new CountDownLatch(1);
         client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
-                .send(new Response.CompleteListener()
+                .send(result ->
                 {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        if (result.isFailed())
-                            completeLatch.countDown();
-                    }
+                    if (result.isFailed())
+                        completeLatch.countDown();
                 });
 
         Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
@@ -1370,7 +1363,7 @@
                 int count = requests.incrementAndGet();
                 if (count == maxRetries)
                     baseRequest.setHandled(true);
-                consume(request.getInputStream());
+                consume(request.getInputStream(), true);
             }
         });
 
@@ -1470,28 +1463,78 @@
         final CountDownLatch latch = new CountDownLatch(2);
         client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
-                .onRequestBegin(new Request.BeginListener()
+                .onRequestBegin(request ->
                 {
-                    @Override
-                    public void onBegin(Request request)
-                    {
-                        Assert.assertTrue(open.get());
-                        latch.countDown();
-                    }
+                    Assert.assertTrue(open.get());
+                    latch.countDown();
                 })
-                .send(new Response.CompleteListener()
+                .send(result ->
                 {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        if (result.isSucceeded())
-                            latch.countDown();
-                    }
+                    if (result.isSucceeded())
+                        latch.countDown();
                 });
 
         Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
     }
 
+    @Test
+    public void testCONNECTWithHTTP10() throws Exception
+    {
+        try (ServerSocket server = new ServerSocket(0))
+        {
+            startClient();
+
+            String host = "localhost";
+            int port = server.getLocalPort();
+
+            Request request = client.newRequest(host, port)
+                    .method(HttpMethod.CONNECT)
+                    .version(HttpVersion.HTTP_1_0);
+            FuturePromise<Connection> promise = new FuturePromise<>();
+            client.getDestination("http", host, port).newConnection(promise);
+            Connection connection = promise.get(5, TimeUnit.SECONDS);
+            FutureResponseListener listener = new FutureResponseListener(request);
+            connection.send(request, listener);
+
+            try (Socket socket = server.accept())
+            {
+                InputStream input = socket.getInputStream();
+                consume(input, false);
+
+                // HTTP/1.0 response, the client must not close the connection.
+                String httpResponse = "" +
+                        "HTTP/1.0 200 OK\r\n" +
+                        "\r\n";
+                OutputStream output = socket.getOutputStream();
+                output.write(httpResponse.getBytes(StandardCharsets.UTF_8));
+                output.flush();
+
+                ContentResponse response = listener.get(5, TimeUnit.SECONDS);
+                Assert.assertEquals(200, response.getStatus());
+
+                // Because the tunnel was successful, this connection will be
+                // upgraded to an SslConnection, so it will not be fill interested.
+                // This test doesn't upgrade, so it needs to restore the fill interest.
+                ((AbstractConnection)connection).fillInterested();
+
+                // Test that I can send another request on the same connection.
+                request = client.newRequest(host, port);
+                listener = new FutureResponseListener(request);
+                connection.send(request, listener);
+
+                consume(input, false);
+
+                httpResponse = "" +
+                        "HTTP/1.1 200 OK\r\n" +
+                        "Content-Length: 0\r\n" +
+                        "\r\n";
+                output.write(httpResponse.getBytes(StandardCharsets.UTF_8));
+                output.flush();
+
+                listener.get(5, TimeUnit.SECONDS);
+            }
+        }
+    }
 
     @Test
     public void test_IPv6_Host() throws Exception
@@ -1509,21 +1552,111 @@
 
         URI uri = URI.create(scheme + "://[::1]:" + connector.getLocalPort() + "/path");
         ContentResponse response = client.newRequest(uri)
-                .method(HttpMethod.PUT)
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
+            .method(HttpMethod.PUT)
+            .timeout(5, TimeUnit.SECONDS)
+            .send();
 
         Assert.assertNotNull(response);
         Assert.assertEquals(200, response.getStatus());
         Assert.assertThat(new String(response.getContent(), StandardCharsets.ISO_8859_1),Matchers.startsWith("[::1]:"));
     }
-    
-    
-    private void consume(InputStream input) throws IOException
+
+    @Test
+    public void testCopyRequest() throws Exception
     {
+        startClient();
+
+        assertCopyRequest(client.newRequest("http://example.com/some/url")
+                .method(HttpMethod.HEAD)
+                .version(HttpVersion.HTTP_2)
+                .content(new StringContentProvider("some string"))
+                .timeout(321, TimeUnit.SECONDS)
+                .idleTimeout(2221, TimeUnit.SECONDS)
+                .followRedirects(true)
+                .header(HttpHeader.CONTENT_TYPE, "application/json")
+                .header("X-Some-Custom-Header", "some-value"));
+
+        assertCopyRequest(client.newRequest("https://example.com")
+                .method(HttpMethod.POST)
+                .version(HttpVersion.HTTP_1_0)
+                .content(new StringContentProvider("some other string"))
+                .timeout(123231, TimeUnit.SECONDS)
+                .idleTimeout(232342, TimeUnit.SECONDS)
+                .followRedirects(false)
+                .header(HttpHeader.ACCEPT, "application/json")
+                .header("X-Some-Other-Custom-Header", "some-other-value"));
+
+        assertCopyRequest(client.newRequest("https://example.com")
+                .header(HttpHeader.ACCEPT, "application/json")
+                .header(HttpHeader.ACCEPT, "application/xml")
+                .header("x-same-name", "value1")
+                .header("x-same-name", "value2"));
+
+        assertCopyRequest(client.newRequest("https://example.com")
+                .header(HttpHeader.ACCEPT, "application/json")
+                .header(HttpHeader.CONTENT_TYPE, "application/json"));
+
+        assertCopyRequest(client.newRequest("https://example.com")
+                .header("Accept", "application/json")
+                .header("Content-Type", "application/json"));
+
+        assertCopyRequest(client.newRequest("https://example.com")
+                .header("X-Custom-Header-1", "value1")
+                .header("X-Custom-Header-2", "value2"));
+
+        assertCopyRequest(client.newRequest("https://example.com")
+                .header("X-Custom-Header-1", "value")
+                .header("X-Custom-Header-2", "value"));
+    }
+
+    @Test
+    public void testHostWithHTTP10() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                Assert.assertThat(request.getHeader("Host"), Matchers.notNullValue());
+            }
+        });
+
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .version(HttpVersion.HTTP_1_0)
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(200, response.getStatus());
+    }
+
+    private void assertCopyRequest(Request original)
+    {
+        Request copy = client.copyRequest((HttpRequest) original, original.getURI());
+        Assert.assertEquals(original.getURI(), copy.getURI());
+        Assert.assertEquals(original.getMethod(), copy.getMethod());
+        Assert.assertEquals(original.getVersion(), copy.getVersion());
+        Assert.assertEquals(original.getContent(), copy.getContent());
+        Assert.assertEquals(original.getIdleTimeout(), copy.getIdleTimeout());
+        Assert.assertEquals(original.getTimeout(), copy.getTimeout());
+        Assert.assertEquals(original.isFollowRedirects(), copy.isFollowRedirects());
+        Assert.assertEquals(original.getHeaders(), copy.getHeaders());
+    }
+
+    private void consume(InputStream input, boolean eof) throws IOException
+    {
+        int crlfs = 0;
         while (true)
         {
-            if (input.read() < 0)
+            int read = input.read();
+            if (read == '\r' || read == '\n')
+                ++crlfs;
+            else
+                crlfs = 0;
+            if (!eof && crlfs == 4)
+                break;
+            if (read < 0)
                 break;
         }
     }
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java
index a4ea3ec..c0a4853 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java
@@ -18,9 +18,20 @@
 
 package org.eclipse.jetty.client;
 
+import static org.hamcrest.CoreMatchers.instanceOf;
+
+import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
 import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.Locale;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
 import javax.servlet.ServletException;
@@ -29,16 +40,24 @@
 
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.util.Fields;
 import org.eclipse.jetty.util.URIUtil;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.junit.Assert;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 public class HttpClientURITest extends AbstractHttpClientServerTest
 {
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+    
     public HttpClientURITest(SslContextFactory sslContextFactory)
     {
         super(sslContextFactory);
@@ -63,6 +82,51 @@
     }
 
     @Test
+    public void testIDNHost() throws Exception
+    {
+        startClient();
+        expectedException.expect(IllegalArgumentException.class);
+        client.newRequest(scheme + "://пример.рф"); // example.com-like host in IDN domain
+    }
+
+    @Test
+    public void testIDNRedirect() throws Exception
+    {
+        // Internationalized Domain Name.
+        // String exampleHost = scheme + "://пример.рф";
+        String exampleHost = scheme + "://\uD0BF\uD180\uD0B8\uD0BC\uD0B5\uD180.\uD180\uD184";
+        String incorrectlyDecoded = new String(exampleHost.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
+
+        // Simple server that only parses clear-text HTTP/1.1.
+        IDNRedirectServer server = new IDNRedirectServer(exampleHost);
+        server.start();
+
+        try
+        {
+            startClient();
+
+            ContentResponse response = client.newRequest("localhost", server.getLocalPort())
+                    .timeout(5, TimeUnit.SECONDS)
+                    .followRedirects(false)
+                    .send();
+
+            HttpField location = response.getHeaders().getField(HttpHeader.LOCATION);
+            Assert.assertEquals(incorrectlyDecoded, location.getValue());
+
+            expectedException.expect(ExecutionException.class);
+            expectedException.expectCause(instanceOf(IllegalArgumentException.class));
+            client.newRequest("localhost", server.getLocalPort())
+                    .timeout(5, TimeUnit.SECONDS)
+                    .followRedirects(true)
+                    .send();
+        }
+        finally
+        {
+            server.stop();
+        }
+    }
+
+    @Test
     public void testPath() throws Exception
     {
         final String path = "/path";
@@ -472,4 +536,103 @@
 
         Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
     }
+
+    @Test
+    public void testAsteriskFormTarget() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                Assert.assertEquals("*", target);
+                Assert.assertEquals("*", request.getPathInfo());
+            }
+        });
+
+        Request request = client.newRequest("localhost", connector.getLocalPort())
+                .method(HttpMethod.OPTIONS)
+                .scheme(scheme)
+                .path("*")
+                .timeout(5, TimeUnit.SECONDS);
+
+        Assert.assertEquals("*", request.getPath());
+        Assert.assertNull(request.getQuery());
+        Fields params = request.getParams();
+        Assert.assertEquals(0, params.getSize());
+        Assert.assertNull(request.getURI());
+
+        ContentResponse response = request.send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    private static class IDNRedirectServer implements Runnable
+    {
+        private final String location;
+        private ServerSocket serverSocket;
+        private int port;
+
+        private IDNRedirectServer(final String location) throws IOException
+        {
+            this.location = location;
+        }
+
+        @Override
+        public void run()
+        {
+            try
+            {
+                while (!Thread.currentThread().isInterrupted())
+                {
+                    try (Socket clientSocket = serverSocket.accept();
+                         BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), StandardCharsets.UTF_8));
+                         PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), false))
+                    {
+                        // Ignore the request.
+                        while (true)
+                        {
+                            String line = reader.readLine();
+                            if (line == null || line.isEmpty())
+                                break;
+                        }
+
+                        writer.append("HTTP/1.1 302 Found\r\n")
+                                .append("Location: ").append(location).append("\r\n")
+                                .append("Content-Length: 0\r\n")
+                                .append("Connection: close\r\n")
+                                .append("\r\n");
+                        writer.flush();
+                    }
+                }
+            }
+            catch (SocketException x)
+            {
+                // ServerSocket has been closed.
+            }
+            catch (Throwable x)
+            {
+                x.printStackTrace();
+            }
+        }
+
+        private void start() throws Exception
+        {
+            serverSocket = new ServerSocket();
+            serverSocket.bind(new InetSocketAddress("localhost", 0));
+            port = serverSocket.getLocalPort();
+            new Thread(this).start();
+        }
+
+        private void stop() throws Exception
+        {
+            serverSocket.close();
+        }
+
+        private int getLocalPort()
+        {
+            return port;
+        }
+    }
 }
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdown.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdown.java
index 26f1669..386c2b1 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdown.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdown.java
@@ -272,7 +272,7 @@
         Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
 
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", connector.getLocalPort());
-        ConnectionPool pool = destination.getConnectionPool();
+        DuplexConnectionPool pool = destination.getConnectionPool();
         Assert.assertEquals(0, pool.getConnectionCount());
         Assert.assertEquals(0, pool.getIdleConnections().size());
         Assert.assertEquals(0, pool.getActiveConnections().size());
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java
index 9e333c9..83c8abb 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java
@@ -21,9 +21,10 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
-import java.util.concurrent.BlockingQueue;
+import java.util.Queue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -41,7 +42,7 @@
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.toolchain.test.annotation.Slow;
 import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.junit.Assert;
 import org.junit.Test;
@@ -68,12 +69,12 @@
         String host = "localhost";
         int port = connector.getLocalPort();
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
 
-        final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
+        final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
         Assert.assertEquals(0, idleConnections.size());
 
-        final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections();
+        final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
         Assert.assertEquals(0, activeConnections.size());
 
         final CountDownLatch headersLatch = new CountDownLatch(1);
@@ -114,8 +115,8 @@
                     }
                 });
 
-        Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(headersLatch.await(30, TimeUnit.SECONDS));
+        Assert.assertTrue(successLatch.await(30, TimeUnit.SECONDS));
 
         Assert.assertEquals(1, idleConnections.size());
         Assert.assertEquals(0, activeConnections.size());
@@ -129,12 +130,12 @@
         String host = "localhost";
         int port = connector.getLocalPort();
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
 
-        final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
+        final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
         Assert.assertEquals(0, idleConnections.size());
 
-        final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections();
+        final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
         Assert.assertEquals(0, activeConnections.size());
 
         final CountDownLatch beginLatch = new CountDownLatch(1);
@@ -165,8 +166,8 @@
             }
         });
 
-        Assert.assertTrue(beginLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(beginLatch.await(30, TimeUnit.SECONDS));
+        Assert.assertTrue(failureLatch.await(30, TimeUnit.SECONDS));
 
         Assert.assertEquals(0, idleConnections.size());
         Assert.assertEquals(0, activeConnections.size());
@@ -180,12 +181,12 @@
         String host = "localhost";
         int port = connector.getLocalPort();
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
 
-        final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
+        final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
         Assert.assertEquals(0, idleConnections.size());
 
-        final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections();
+        final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
         Assert.assertEquals(0, activeConnections.size());
 
         final CountDownLatch successLatch = new CountDownLatch(3);
@@ -225,7 +226,7 @@
                     }
                 });
 
-        Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(successLatch.await(30, TimeUnit.SECONDS));
 
         Assert.assertEquals(0, idleConnections.size());
         Assert.assertEquals(0, activeConnections.size());
@@ -240,12 +241,12 @@
         String host = "localhost";
         int port = connector.getLocalPort();
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
 
-        final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
+        final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
         Assert.assertEquals(0, idleConnections.size());
 
-        final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections();
+        final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
         Assert.assertEquals(0, activeConnections.size());
 
         final long delay = 1000;
@@ -299,7 +300,7 @@
                     }
                 });
 
-        Assert.assertTrue(successLatch.await(delay * 5, TimeUnit.MILLISECONDS));
+        Assert.assertTrue(successLatch.await(delay * 30, TimeUnit.MILLISECONDS));
 
         Assert.assertEquals(0, idleConnections.size());
         Assert.assertEquals(0, activeConnections.size());
@@ -313,12 +314,12 @@
         String host = "localhost";
         int port = connector.getLocalPort();
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
 
-        final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
+        final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
         Assert.assertEquals(0, idleConnections.size());
 
-        final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections();
+        final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
         Assert.assertEquals(0, activeConnections.size());
 
         server.stop();
@@ -344,7 +345,7 @@
                     }
                 });
 
-        Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(failureLatch.await(30, TimeUnit.SECONDS));
 
         Assert.assertEquals(0, idleConnections.size());
         Assert.assertEquals(0, activeConnections.size());
@@ -366,12 +367,12 @@
         String host = "localhost";
         int port = connector.getLocalPort();
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
 
-        final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
+        final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
         Assert.assertEquals(0, idleConnections.size());
 
-        final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections();
+        final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
         Assert.assertEquals(0, activeConnections.size());
 
         final CountDownLatch latch = new CountDownLatch(1);
@@ -389,7 +390,7 @@
                     }
                 });
 
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(latch.await(30, TimeUnit.SECONDS));
 
         Assert.assertEquals(0, idleConnections.size());
         Assert.assertEquals(0, activeConnections.size());
@@ -398,9 +399,7 @@
     @Test
     public void test_BigRequestContent_ResponseWithConnectionCloseHeader_RemovesConnection() throws Exception
     {
-        StdErrLog logger = StdErrLog.getLogger(org.eclipse.jetty.server.HttpConnection.class);
-        logger.setHideStacks(true);
-        try
+        try (StacklessLogging stackless = new StacklessLogging(HttpConnection.class))
         {
             start(new AbstractHandler()
             {
@@ -416,12 +415,12 @@
             String host = "localhost";
             int port = connector.getLocalPort();
             HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
-            ConnectionPool connectionPool = destination.getConnectionPool();
+            DuplexConnectionPool connectionPool = destination.getConnectionPool();
 
-            final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
+            final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
             Assert.assertEquals(0, idleConnections.size());
 
-            final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections();
+            final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
             Assert.assertEquals(0, activeConnections.size());
 
             Log.getLogger(HttpConnection.class).info("Expecting java.lang.IllegalStateException: HttpParser{s=CLOSED,...");
@@ -444,17 +443,13 @@
                         }
                     });
 
-            Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+            Assert.assertTrue(latch.await(30, TimeUnit.SECONDS));
 
             Assert.assertEquals(0, idleConnections.size());
             Assert.assertEquals(0, activeConnections.size());
-            
+
             server.stop();
         }
-        finally
-        {
-            logger.setHideStacks(false);
-        }
     }
 
     @Slow
@@ -466,17 +461,17 @@
         String host = "localhost";
         int port = connector.getLocalPort();
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
 
-        final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
+        final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
         Assert.assertEquals(0, idleConnections.size());
 
-        final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections();
+        final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
         Assert.assertEquals(0, activeConnections.size());
 
         ContentResponse response = client.newRequest(host, port)
                 .scheme(scheme)
-                .timeout(5, TimeUnit.SECONDS)
+                .timeout(30, TimeUnit.SECONDS)
                 .send();
 
         Assert.assertEquals(200, response.getStatus());
@@ -498,12 +493,12 @@
         String host = "localhost";
         int port = connector.getLocalPort();
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
 
-        final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
+        final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
         Assert.assertEquals(0, idleConnections.size());
 
-        final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections();
+        final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
         Assert.assertEquals(0, activeConnections.size());
 
         client.setStrictEventOrdering(false);
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieTest.java
index 38de87a..15d9290 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieTest.java
@@ -23,6 +23,7 @@
 import java.net.URI;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java
index 489a01b..9a1e14a 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java
@@ -68,7 +68,7 @@
             Assert.assertSame(failure, x.getCause());
             // Make sure the pool is in a sane state.
             HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
-            ConnectionPool connectionPool = destination.getConnectionPool();
+            DuplexConnectionPool connectionPool = destination.getConnectionPool();
             Assert.assertEquals(1, connectionPool.getConnectionCount());
             Assert.assertEquals(0, connectionPool.getActiveConnections().size());
             Assert.assertEquals(1, connectionPool.getIdleConnections().size());
@@ -116,7 +116,7 @@
         }
 
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
         Assert.assertEquals(0, connectionPool.getConnectionCount());
         Assert.assertEquals(0, connectionPool.getActiveConnections().size());
         Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -163,7 +163,7 @@
         }
 
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
         Assert.assertEquals(0, connectionPool.getConnectionCount());
         Assert.assertEquals(0, connectionPool.getActiveConnections().size());
         Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -210,7 +210,7 @@
         }
 
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
         Assert.assertEquals(0, connectionPool.getConnectionCount());
         Assert.assertEquals(0, connectionPool.getActiveConnections().size());
         Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -253,7 +253,7 @@
         }
 
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
         Assert.assertEquals(0, connectionPool.getConnectionCount());
         Assert.assertEquals(0, connectionPool.getActiveConnections().size());
         Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -317,7 +317,7 @@
         }
 
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
         Assert.assertEquals(0, connectionPool.getConnectionCount());
         Assert.assertEquals(0, connectionPool.getActiveConnections().size());
         Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -372,7 +372,7 @@
         }
 
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
         Assert.assertEquals(0, connectionPool.getConnectionCount());
         Assert.assertEquals(0, connectionPool.getActiveConnections().size());
         Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -482,7 +482,7 @@
         }
 
         HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
-        ConnectionPool connectionPool = destination.getConnectionPool();
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
         Assert.assertEquals(0, connectionPool.getConnectionCount());
         Assert.assertEquals(0, connectionPool.getActiveConnections().size());
         Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -555,7 +555,7 @@
         final AtomicBoolean aborted = new AtomicBoolean();
         final CountDownLatch latch = new CountDownLatch(1);
         client.getProtocolHandlers().clear();
-        client.getProtocolHandlers().add(new RedirectProtocolHandler(client)
+        client.getProtocolHandlers().put(new RedirectProtocolHandler(client)
         {
             @Override
             public void onComplete(Result result)
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java
index 6b3477f..cb2fa71 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java
@@ -24,6 +24,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseConcurrentAbortTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseConcurrentAbortTest.java
index 7bcc187..818db65 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseConcurrentAbortTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseConcurrentAbortTest.java
@@ -24,6 +24,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -42,7 +43,8 @@
     private final CountDownLatch callbackLatch = new CountDownLatch(1);
     private final CountDownLatch failureLatch = new CountDownLatch(1);
     private final CountDownLatch completeLatch = new CountDownLatch(1);
-    private final AtomicBoolean success = new AtomicBoolean();
+    private final AtomicBoolean failureWasAsync = new AtomicBoolean();
+    private final AtomicBoolean completeWasSync = new AtomicBoolean();
 
     public HttpResponseConcurrentAbortTest(SslContextFactory sslContextFactory)
     {
@@ -66,8 +68,9 @@
                 })
                 .send(new TestResponseListener());
         Assert.assertTrue(callbackLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(completeLatch.await(6, TimeUnit.SECONDS));
-        Assert.assertTrue(success.get());
+        Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(failureWasAsync.get());
+        Assert.assertTrue(completeWasSync.get());
     }
 
     @Test
@@ -89,7 +92,8 @@
                 .send(new TestResponseListener());
         Assert.assertTrue(callbackLatch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(success.get());
+        Assert.assertTrue(failureWasAsync.get());
+        Assert.assertTrue(completeWasSync.get());
     }
 
     @Test
@@ -110,7 +114,8 @@
                 .send(new TestResponseListener());
         Assert.assertTrue(callbackLatch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(success.get());
+        Assert.assertTrue(failureWasAsync.get());
+        Assert.assertTrue(completeWasSync.get());
     }
 
     @Test
@@ -141,7 +146,8 @@
                 .send(new TestResponseListener());
         Assert.assertTrue(callbackLatch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(success.get());
+        Assert.assertTrue(failureWasAsync.get());
+        Assert.assertTrue(completeWasSync.get());
     }
 
     private void abort(final Response response)
@@ -157,14 +163,15 @@
 
         try
         {
-            // The failure callback must be executed asynchronously.
-            boolean latched = failureLatch.await(4, TimeUnit.SECONDS);
-            success.set(latched);
+            // The failure callback is executed asynchronously, but
+            // here we are within the context of another response
+            // callback, which should detect that a failure happened
+            // and therefore this thread should complete the response.
+            failureWasAsync.set(failureLatch.await(2, TimeUnit.SECONDS));
 
-            // The complete callback must not be executed
-            // until we return from this callback.
-            latched = completeLatch.await(1, TimeUnit.SECONDS);
-            success.set(!latched);
+            // The complete callback must be executed by this thread,
+            // after we return from this response callback.
+            completeWasSync.set(!completeLatch.await(1, TimeUnit.SECONDS));
 
             callbackLatch.countDown();
         }
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/RespondThenConsumeHandler.java b/jetty-client/src/test/java/org/eclipse/jetty/client/RespondThenConsumeHandler.java
new file mode 100644
index 0000000..89bc0dc
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/RespondThenConsumeHandler.java
@@ -0,0 +1,45 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.handler.AbstractHandler;
+
+class RespondThenConsumeHandler extends AbstractHandler
+{
+    @Override
+    public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+            throws IOException, ServletException
+    {
+        baseRequest.setHandled(true);
+        response.setContentLength(0);
+        response.setStatus(200);
+        response.flushBuffer();
+        
+        InputStream in = request.getInputStream();
+        while(in.read()>=0);
+    }
+    
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ServerConnectionCloseTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ServerConnectionCloseTest.java
new file mode 100644
index 0000000..4df94d0
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ServerConnectionCloseTest.java
@@ -0,0 +1,176 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
+import org.eclipse.jetty.client.util.FutureResponseListener;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class ServerConnectionCloseTest
+{
+    @Rule
+    public final TestTracker tracker = new TestTracker();
+    private HttpClient client;
+
+    private void startClient() throws Exception
+    {
+        QueuedThreadPool clientThreads = new QueuedThreadPool();
+        clientThreads.setName("client");
+        client = new HttpClient(new HttpClientTransportOverHTTP(1), null);
+        client.setExecutor(clientThreads);
+        client.start();
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        if (client != null)
+            client.stop();
+    }
+
+    @Test
+    public void testServerSendsConnectionCloseWithoutContent() throws Exception
+    {
+        testServerSendsConnectionClose(true, false, "");
+    }
+
+    @Test
+    public void testServerSendsConnectionCloseWithContent() throws Exception
+    {
+        testServerSendsConnectionClose(true, false, "data");
+    }
+
+    @Test
+    public void testServerSendsConnectionCloseWithChunkedContent() throws Exception
+    {
+        testServerSendsConnectionClose(true, true, "data");
+    }
+
+    @Test
+    public void testServerSendsConnectionCloseWithoutContentButDoesNotClose() throws Exception
+    {
+        testServerSendsConnectionClose(false, false, "");
+    }
+
+    @Test
+    public void testServerSendsConnectionCloseWithContentButDoesNotClose() throws Exception
+    {
+        testServerSendsConnectionClose(false, false, "data");
+    }
+
+    @Test
+    public void testServerSendsConnectionCloseWithChunkedContentButDoesNotClose() throws Exception
+    {
+        testServerSendsConnectionClose(false, true, "data");
+    }
+
+    private void testServerSendsConnectionClose(boolean shutdownOutput, boolean chunked, String content) throws Exception
+    {
+        ServerSocket server = new ServerSocket(0);
+        int port = server.getLocalPort();
+
+        startClient();
+
+        Request request = client.newRequest("localhost", port).path("/ctx/path");
+        FutureResponseListener listener = new FutureResponseListener(request);
+        request.send(listener);
+
+        Socket socket = server.accept();
+
+        InputStream input = socket.getInputStream();
+        consumeRequest(input);
+
+        OutputStream output = socket.getOutputStream();
+        String serverResponse = "" +
+                "HTTP/1.1 200 OK\r\n" +
+                "Connection: close\r\n";
+        if (chunked)
+        {
+            serverResponse += "" +
+                    "Transfer-Encoding: chunked\r\n" +
+                    "\r\n";
+                    for (int i = 0; i < 2; ++i)
+                    {
+                        serverResponse +=
+                                Integer.toHexString(content.length()) + "\r\n" +
+                                content + "\r\n";
+                    }
+            serverResponse += "" +
+                    "0\r\n" +
+                    "\r\n";
+        }
+        else
+        {
+            serverResponse += "Content-Length: " + content.length() + "\r\n";
+            serverResponse += "\r\n";
+            serverResponse += content;
+        }
+
+        output.write(serverResponse.getBytes("UTF-8"));
+        output.flush();
+        if (shutdownOutput)
+            socket.shutdownOutput();
+
+        ContentResponse response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        // Give some time to process the connection.
+        Thread.sleep(1000);
+
+        // Connection should have been removed from pool.
+        HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", port);
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
+        Assert.assertEquals(0, connectionPool.getConnectionCount());
+        Assert.assertEquals(0, connectionPool.getIdleConnectionCount());
+        Assert.assertEquals(0, connectionPool.getActiveConnectionCount());
+    }
+
+    private boolean consumeRequest(InputStream input) throws IOException
+    {
+        int crlfs = 0;
+        while (true)
+        {
+            int read = input.read();
+            if (read < 0)
+                return true;
+            if (read == '\r' || read == '\n')
+                ++crlfs;
+            else
+                crlfs = 0;
+            if (crlfs == 4)
+                return false;
+        }
+    }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/Socks4ProxyTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/Socks4ProxyTest.java
index 3ce20f1..e75e66c 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/Socks4ProxyTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/Socks4ProxyTest.java
@@ -26,8 +26,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -69,17 +67,16 @@
         byte ip4 = 13;
         String serverHost = ip1 + "." + ip2 + "." + ip3 + "." + ip4;
         int serverPort = proxyPort + 1; // Any port will do
+        String method = "GET";
+        String path = "/path";
         client.newRequest(serverHost, serverPort)
-                .path("/path")
+                .method(method)
+                .path(path)
                 .timeout(5, TimeUnit.SECONDS)
-                .send(new Response.CompleteListener()
+                .send(result ->
                 {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        if (result.isSucceeded())
-                            latch.countDown();
-                    }
+                    if (result.isSucceeded())
+                        latch.countDown();
                 });
 
         SocketChannel channel = server.accept();
@@ -100,11 +97,11 @@
         // Socks4 response.
         channel.write(ByteBuffer.wrap(new byte[]{0, 0x5A, 0, 0, 0, 0, 0, 0}));
 
-        buffer = ByteBuffer.allocate(3);
+        buffer = ByteBuffer.allocate(method.length() + 1 + path.length());
         read = channel.read(buffer);
-        Assert.assertEquals(3, read);
+        Assert.assertEquals(buffer.capacity(), read);
         buffer.flip();
-        Assert.assertEquals("GET", StandardCharsets.UTF_8.decode(buffer).toString());
+        Assert.assertEquals(method + " " + path, StandardCharsets.UTF_8.decode(buffer).toString());
 
         // Response
         String response = "" +
@@ -129,19 +126,17 @@
 
         String serverHost = "127.0.0.13"; // Test expects an IP address.
         int serverPort = proxyPort + 1; // Any port will do
+        String method = "GET";
         client.newRequest(serverHost, serverPort)
+                .method(method)
                 .path("/path")
                 .timeout(5, TimeUnit.SECONDS)
-                .send(new Response.CompleteListener()
+                .send(result ->
                 {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        if (result.isSucceeded())
-                            latch.countDown();
-                        else
-                            result.getFailure().printStackTrace();
-                    }
+                    if (result.isSucceeded())
+                        latch.countDown();
+                    else
+                        result.getFailure().printStackTrace();
                 });
 
         SocketChannel channel = server.accept();
@@ -161,11 +156,11 @@
 
         channel.write(ByteBuffer.wrap(chunk2));
 
-        buffer = ByteBuffer.allocate(3);
+        buffer = ByteBuffer.allocate(method.length());
         read = channel.read(buffer);
-        Assert.assertEquals(3, read);
+        Assert.assertEquals(buffer.capacity(), read);
         buffer.flip();
-        Assert.assertEquals("GET", StandardCharsets.UTF_8.decode(buffer).toString());
+        Assert.assertEquals(method, StandardCharsets.UTF_8.decode(buffer).toString());
 
         // Response
         String response = "" +
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/TLSServerConnectionCloseTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/TLSServerConnectionCloseTest.java
new file mode 100644
index 0000000..38fa326
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/TLSServerConnectionCloseTest.java
@@ -0,0 +1,213 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
+import org.eclipse.jetty.client.util.FutureResponseListener;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TLSServerConnectionCloseTest
+{
+    @Parameterized.Parameters(name = "CloseMode: {0}")
+    public static Object[] parameters()
+    {
+        return new Object[]{CloseMode.NONE, CloseMode.CLOSE, CloseMode.ABRUPT};
+    }
+
+    @Rule
+    public final TestTracker tracker = new TestTracker();
+    private HttpClient client;
+    private final CloseMode closeMode;
+
+    public TLSServerConnectionCloseTest(CloseMode closeMode)
+    {
+        this.closeMode = closeMode;
+    }
+
+    private void startClient() throws Exception
+    {
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setEndpointIdentificationAlgorithm("");
+        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
+        sslContextFactory.setTrustStorePassword("storepwd");
+
+        QueuedThreadPool clientThreads = new QueuedThreadPool();
+        clientThreads.setName("client");
+        client = new HttpClient(new HttpClientTransportOverHTTP(1), sslContextFactory);
+        client.setExecutor(clientThreads);
+        client.start();
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        if (client != null)
+            client.stop();
+    }
+
+    @Test
+    public void testServerSendsConnectionCloseWithoutContent() throws Exception
+    {
+        testServerSendsConnectionClose(false, "");
+    }
+
+    @Test
+    public void testServerSendsConnectionCloseWithContent() throws Exception
+    {
+        testServerSendsConnectionClose(false, "data");
+    }
+
+    @Test
+    public void testServerSendsConnectionCloseWithChunkedContent() throws Exception
+    {
+        testServerSendsConnectionClose(true, "data");
+    }
+
+    private void testServerSendsConnectionClose(boolean chunked, String content) throws Exception
+    {
+        ServerSocket server = new ServerSocket(0);
+        int port = server.getLocalPort();
+
+        startClient();
+
+        Request request = client.newRequest("localhost", port).scheme("https").path("/ctx/path");
+        FutureResponseListener listener = new FutureResponseListener(request);
+        request.send(listener);
+
+        Socket socket = server.accept();
+        SSLContext sslContext = client.getSslContextFactory().getSslContext();
+        SSLSocket sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(socket, "localhost", port, false);
+        sslSocket.setUseClientMode(false);
+        sslSocket.startHandshake();
+
+        InputStream input = sslSocket.getInputStream();
+        consumeRequest(input);
+
+        OutputStream output = sslSocket.getOutputStream();
+        String serverResponse = "" +
+                "HTTP/1.1 200 OK\r\n" +
+                "Connection: close\r\n";
+        if (chunked)
+        {
+            serverResponse += "" +
+                    "Transfer-Encoding: chunked\r\n" +
+                    "\r\n";
+                    for (int i = 0; i < 2; ++i)
+                    {
+                        serverResponse +=
+                                Integer.toHexString(content.length()) + "\r\n" +
+                                content + "\r\n";
+                    }
+            serverResponse += "" +
+                    "0\r\n" +
+                    "\r\n";
+        }
+        else
+        {
+            serverResponse += "Content-Length: " + content.length() + "\r\n";
+            serverResponse += "\r\n";
+            serverResponse += content;
+        }
+
+        output.write(serverResponse.getBytes("UTF-8"));
+        output.flush();
+
+        switch (closeMode)
+        {
+            case NONE:
+            {
+                break;
+            }
+            case CLOSE:
+            {
+                sslSocket.close();
+                break;
+            }
+            case ABRUPT:
+            {
+                socket.shutdownOutput();
+                break;
+            }
+            default:
+            {
+                throw new IllegalStateException();
+            }
+        }
+
+        ContentResponse response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        // Give some time to process the connection.
+        Thread.sleep(1000);
+
+        // Connection should have been removed from pool.
+        HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", port);
+        DuplexConnectionPool connectionPool = destination.getConnectionPool();
+        Assert.assertEquals(0, connectionPool.getConnectionCount());
+        Assert.assertEquals(0, connectionPool.getIdleConnectionCount());
+        Assert.assertEquals(0, connectionPool.getActiveConnectionCount());
+    }
+
+    private boolean consumeRequest(InputStream input) throws IOException
+    {
+        int crlfs = 0;
+        while (true)
+        {
+            int read = input.read();
+            if (read < 0)
+                return true;
+            if (read == '\r' || read == '\n')
+                ++crlfs;
+            else
+                crlfs = 0;
+            if (crlfs == 4)
+                return false;
+        }
+    }
+
+    private enum CloseMode
+    {
+        NONE, CLOSE, ABRUPT
+    }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ValidatingConnectionPoolTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ValidatingConnectionPoolTest.java
new file mode 100644
index 0000000..5beda29
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ValidatingConnectionPoolTest.java
@@ -0,0 +1,204 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client;
+
+import java.io.IOException;
+import java.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.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
+import org.eclipse.jetty.client.util.FutureResponseListener;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpHeaderValue;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ValidatingConnectionPoolTest extends AbstractHttpClientServerTest
+{
+    public ValidatingConnectionPoolTest(SslContextFactory sslContextFactory)
+    {
+        super(sslContextFactory);
+    }
+
+    @Override
+    protected void startClient() throws Exception
+    {
+        startClient(new ValidatingHttpClientTransportOverHTTP(1000));
+    }
+
+    @Test
+    public void testRequestAfterValidation() throws Exception
+    {
+        start(new EmptyServerHandler());
+
+        client.setMaxConnectionsPerDestination(1);
+
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .send();
+        Assert.assertEquals(200, response.getStatus());
+
+        // The second request should be sent after the validating timeout.
+        response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .send();
+        Assert.assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testServerClosesConnectionAfterRedirectWithoutConnectionCloseHeader() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                if (target.endsWith("/redirect"))
+                {
+                    response.setStatus(HttpStatus.TEMPORARY_REDIRECT_307);
+                    response.setContentLength(0);
+                    response.setHeader(HttpHeader.LOCATION.asString(), scheme + "://localhost:" + connector.getLocalPort() + "/");
+                    response.flushBuffer();
+                    baseRequest.getHttpChannel().getEndPoint().shutdownOutput();
+                }
+                else
+                {
+                    response.setStatus(HttpStatus.OK_200);
+                    response.setContentLength(0);
+                    response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
+                }
+            }
+        });
+
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .path("/redirect")
+                .send();
+        Assert.assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testServerClosesConnectionAfterResponseWithQueuedRequestWithMaxConnectionsWithConnectionCloseHeader() throws Exception
+    {
+        testServerClosesConnectionAfterResponseWithQueuedRequestWithMaxConnections(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.setStatus(HttpStatus.OK_200);
+                response.setContentLength(0);
+                response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
+            }
+        });
+    }
+
+    @Test
+    public void testServerClosesConnectionAfterResponseWithQueuedRequestWithMaxConnectionsWithoutConnectionCloseHeader() throws Exception
+    {
+        testServerClosesConnectionAfterResponseWithQueuedRequestWithMaxConnections(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.setStatus(HttpStatus.OK_200);
+                response.setContentLength(0);
+                response.flushBuffer();
+                baseRequest.getHttpChannel().getEndPoint().shutdownOutput();
+            }
+        });
+    }
+
+    private void testServerClosesConnectionAfterResponseWithQueuedRequestWithMaxConnections(Handler handler) throws Exception
+    {
+        start(handler);
+        client.setMaxConnectionsPerDestination(1);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Request request1 = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .path("/one")
+                .onRequestBegin(r ->
+                {
+                    try
+                    {
+                        latch.await();
+                    }
+                    catch (InterruptedException x)
+                    {
+                        r.abort(x);
+                    }
+                });
+        FutureResponseListener listener1 = new FutureResponseListener(request1);
+        request1.send(listener1);
+
+        Request request2 = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .path("/two");
+        FutureResponseListener listener2 = new FutureResponseListener(request2);
+        request2.send(listener2);
+
+        // Now we have one request about to be sent, and one queued.
+
+        latch.countDown();
+
+        ContentResponse response1 = listener1.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(200, response1.getStatus());
+
+        ContentResponse response2 = listener2.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(200, response2.getStatus());
+    }
+
+    private static class ValidatingHttpClientTransportOverHTTP extends HttpClientTransportOverHTTP
+    {
+        private final long timeout;
+
+        public ValidatingHttpClientTransportOverHTTP(long timeout)
+        {
+            super(1);
+            this.timeout = timeout;
+        }
+
+        @Override
+        public HttpDestination newHttpDestination(Origin origin)
+        {
+            return new HttpDestinationOverHTTP(getHttpClient(), origin)
+            {
+                @Override
+                protected DuplexConnectionPool newConnectionPool(HttpClient client)
+                {
+                    return new ValidatingConnectionPool(this, client.getMaxConnectionsPerDestination(), this, client.getScheduler(), timeout);
+                }
+            };
+        }
+    }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java
index 5f526fd..9f1cd0d 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java
@@ -18,12 +18,13 @@
 
 package org.eclipse.jetty.client.http;
 
+import java.util.Queue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jetty.client.AbstractHttpClientServerTest;
-import org.eclipse.jetty.client.ConnectionPool;
+import org.eclipse.jetty.client.DuplexConnectionPool;
 import org.eclipse.jetty.client.EmptyServerHandler;
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.Origin;
@@ -62,7 +63,7 @@
         if (connection == null)
         {
             // There are no queued requests, so the newly created connection will be idle
-            connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
+            connection = timedPoll(destination.getConnectionPool().getIdleConnections(), 5, TimeUnit.SECONDS);
         }
         Assert.assertNotNull(connection);
     }
@@ -96,9 +97,9 @@
         HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()))
         {
             @Override
-            protected ConnectionPool newConnectionPool(HttpClient client)
+            protected DuplexConnectionPool newConnectionPool(HttpClient client)
             {
-                return new ConnectionPool(this, client.getMaxConnectionsPerDestination(), this)
+                return new DuplexConnectionPool(this, client.getMaxConnectionsPerDestination(), this)
                 {
                     @Override
                     protected void idleCreated(Connection connection)
@@ -132,10 +133,11 @@
 
         latch.countDown();
 
-        // There must be 2 idle connections
-        Connection connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
+        // There must be 2 idle connections.
+        Queue<Connection> idleConnections = destination.getConnectionPool().getIdleConnections();
+        Connection connection = timedPoll(idleConnections, 5, TimeUnit.SECONDS);
         Assert.assertNotNull(connection);
-        connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
+        connection = timedPoll(idleConnections, 5, TimeUnit.SECONDS);
         Assert.assertNotNull(connection);
     }
 
@@ -156,7 +158,7 @@
         // Acquire the connection to make it active
         Assert.assertSame(connection1, destination.acquire());
 
-        destination.process(connection1, false);
+        destination.process(connection1);
         destination.release(connection1);
 
         Connection connection2 = destination.acquire();
@@ -272,4 +274,17 @@
         destinationAfter = client.getDestination(scheme, host, port);
         Assert.assertNotSame(destinationBefore, destinationAfter);
     }
+
+    private Connection timedPoll(Queue<Connection> connections, long time, TimeUnit unit) throws InterruptedException
+    {
+        long start = System.nanoTime();
+        while (unit.toNanos(time) > System.nanoTime() - start)
+        {
+            Connection connection = connections.poll();
+            if (connection != null)
+                return connection;
+            TimeUnit.MILLISECONDS.sleep(5);
+        }
+        return connections.poll();
+    }
 }
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java
index d25b593..693f5af 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java
@@ -30,7 +30,6 @@
 import org.eclipse.jetty.client.HttpRequest;
 import org.eclipse.jetty.client.HttpResponseException;
 import org.eclipse.jetty.client.Origin;
-import org.eclipse.jetty.client.api.Connection;
 import org.eclipse.jetty.client.api.Response;
 import org.eclipse.jetty.client.util.FutureResponseListener;
 import org.eclipse.jetty.http.HttpFields;
@@ -62,7 +61,8 @@
         client.start();
         destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
         endPoint = new ByteArrayEndPoint();
-        connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
+        connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<>());
+        endPoint.setConnection(connection);
     }
 
     @After
@@ -86,7 +86,7 @@
     @Test
     public void test_Receive_NoResponseContent() throws Exception
     {
-        endPoint.setInput("" +
+        endPoint.addInput("" +
                 "HTTP/1.1 200 OK\r\n" +
                 "Content-length: 0\r\n" +
                 "\r\n");
@@ -109,7 +109,7 @@
     public void test_Receive_ResponseContent() throws Exception
     {
         String content = "0123456789ABCDEF";
-        endPoint.setInput("" +
+        endPoint.addInput("" +
                 "HTTP/1.1 200 OK\r\n" +
                 "Content-length: " + content.length() + "\r\n" +
                 "\r\n" +
@@ -136,7 +136,7 @@
     {
         String content1 = "0123456789";
         String content2 = "ABCDEF";
-        endPoint.setInput("" +
+        endPoint.addInput("" +
                 "HTTP/1.1 200 OK\r\n" +
                 "Content-length: " + (content1.length() + content2.length()) + "\r\n" +
                 "\r\n" +
@@ -144,7 +144,7 @@
         HttpExchange exchange = newExchange();
         FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
         connection.getHttpChannel().receive();
-        endPoint.setInputEOF();
+        endPoint.addInputEOF();
         connection.getHttpChannel().receive();
 
         try
@@ -161,15 +161,17 @@
     @Test
     public void test_Receive_ResponseContent_IdleTimeout() throws Exception
     {
-        endPoint.setInput("" +
+        endPoint.addInput("" +
                 "HTTP/1.1 200 OK\r\n" +
                 "Content-length: 1\r\n" +
                 "\r\n");
         HttpExchange exchange = newExchange();
         FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
         connection.getHttpChannel().receive();
-        // Simulate an idle timeout
-        connection.onReadTimeout();
+        // ByteArrayEndPoint has an idle timeout of 0 by default,
+        // so to simulate an idle timeout is enough to wait a bit.
+        Thread.sleep(100);
+        connection.onIdleExpired();
 
         try
         {
@@ -185,7 +187,7 @@
     @Test
     public void test_Receive_BadResponse() throws Exception
     {
-        endPoint.setInput("" +
+        endPoint.addInput("" +
                 "HTTP/1.1 200 OK\r\n" +
                 "Content-length: A\r\n" +
                 "\r\n");
@@ -207,7 +209,7 @@
     @Test
     public void test_FillInterested_RacingWith_BufferRelease() throws Exception
     {
-        connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>())
+        connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<>())
         {
             @Override
             protected HttpChannelOverHTTP newHttpChannel()
@@ -226,7 +228,7 @@
                                 // before fillInterested() is called.
                                 Assert.assertNull(getResponseBuffer());
                                 // Fill the endpoint so receive is called again.
-                                endPoint.setInput("X");
+                                endPoint.addInput("X");
                                 super.fillInterested();
                             }
                         };
@@ -234,9 +236,10 @@
                 };
             }
         };
+        endPoint.setConnection(connection);
 
         // Partial response to trigger the call to fillInterested().
-        endPoint.setInput("" +
+        endPoint.addInput("" +
                 "HTTP/1.1 200 OK\r\n" +
                 "Content-Length: 1\r\n" +
                 "\r\n");
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesClientTest.java
index 517c41b..b592b94 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesClientTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesClientTest.java
@@ -26,7 +26,6 @@
 import java.net.SocketTimeoutException;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
@@ -105,13 +104,10 @@
         final SSLSocket server = (SSLSocket)acceptor.accept();
         server.setUseClientMode(false);
 
-        Future<Object> handshake = threadPool.submit(new Callable<Object>()
+        Future<Object> handshake = threadPool.submit(() ->
         {
-            public Object call() throws Exception
-            {
-                server.startHandshake();
-                return null;
-            }
+            server.startHandshake();
+            return null;
         });
 
         // Client Hello
@@ -185,13 +181,10 @@
         final SSLSocket server = (SSLSocket)acceptor.accept();
         server.setUseClientMode(false);
 
-        Future<Object> handshake = threadPool.submit(new Callable<Object>()
+        Future<Object> handshake = threadPool.submit(() ->
         {
-            public Object call() throws Exception
-            {
-                server.startHandshake();
-                return null;
-            }
+            server.startHandshake();
+            return null;
         });
 
         SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
@@ -222,13 +215,10 @@
         Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
 
         // Renegotiate
-        Future<Object> renegotiation = threadPool.submit(new Callable<Object>()
+        Future<Object> renegotiation = threadPool.submit(() ->
         {
-            public Object call() throws Exception
-            {
-                server.startHandshake();
-                return null;
-            }
+            server.startHandshake();
+            return null;
         });
 
         // Renegotiation Handshake
@@ -307,13 +297,10 @@
         final SSLSocket server = (SSLSocket)acceptor.accept();
         server.setUseClientMode(false);
 
-        Future<Object> handshake = threadPool.submit(new Callable<Object>()
+        Future<Object> handshake = threadPool.submit(() ->
         {
-            public Object call() throws Exception
-            {
-                server.startHandshake();
-                return null;
-            }
+            server.startHandshake();
+            return null;
         });
 
         SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
@@ -344,13 +331,10 @@
         Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
 
         // Renegotiate
-        threadPool.submit(new Callable<Object>()
+        threadPool.submit(() ->
         {
-            public Object call() throws Exception
-            {
-                server.startHandshake();
-                return null;
-            }
+            server.startHandshake();
+            return null;
         });
 
         // Renegotiation Handshake
@@ -358,6 +342,9 @@
         Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
         proxy.flushToClient(record);
 
+        // Client sends close alert.
+        record = proxy.readFromClient();
+        Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
         record = proxy.readFromClient();
         Assert.assertNull(record);
 
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java
index f67afc5..d4c8ce8 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java
@@ -18,8 +18,6 @@
 
 package org.eclipse.jetty.client.ssl;
 
-import static org.hamcrest.Matchers.nullValue;
-
 import java.io.BufferedReader;
 import java.io.EOFException;
 import java.io.File;
@@ -32,7 +30,6 @@
 import java.nio.channels.SocketChannel;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
-import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -53,11 +50,12 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.client.ssl.SslBytesTest.TLSRecord.Type;
+import org.eclipse.jetty.http.HttpCompliance;
 import org.eclipse.jetty.http.HttpParser;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ManagedSelector;
 import org.eclipse.jetty.io.SelectChannelEndPoint;
-import org.eclipse.jetty.io.SelectorManager.ManagedSelector;
 import org.eclipse.jetty.io.ssl.SslConnection;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.HttpConnection;
@@ -111,12 +109,12 @@
             @Override
             public Connection newConnection(Connector connector, EndPoint endPoint)
             {
-                return configure(new HttpConnection(getHttpConfiguration(), connector, endPoint)
+                return configure(new HttpConnection(getHttpConfiguration(), connector, endPoint,getHttpCompliance(),isRecordHttpComplianceViolations())
                 {
                     @Override
-                    protected HttpParser newHttpParser()
+                    protected HttpParser newHttpParser(HttpCompliance compliance)
                     {
-                        return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize())
+                        return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize(),compliance)
                         {
                             @Override
                             public boolean parseNext(ByteBuffer buffer)
@@ -243,14 +241,10 @@
     {
         final SSLSocket client = newClient();
 
-        Future<Object> handshake = threadPool.submit(new Callable<Object>()
+        Future<Object> handshake = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                client.startHandshake();
-                return null;
-            }
+            client.startHandshake();
+            return null;
         });
 
         // Client Hello
@@ -299,7 +293,7 @@
         closeClient(client);
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testHandshakeWithResumedSessionThenClose() throws Exception
     {
         // First socket will establish the SSL session
@@ -317,14 +311,10 @@
 
         final SSLSocket client2 = newClient(proxy);
 
-        Future<Object> handshake = threadPool.submit(new Callable<Object>()
+        threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                client2.startHandshake();
-                return null;
-            }
+            client2.startHandshake();
+            return null;
         });
 
         // Client Hello with SessionID
@@ -370,7 +360,10 @@
         // Close the raw socket
         proxy.flushToServer(null);
 
-        // Expect the server to send a FIN as well
+        // Expect the server to send a TLS Alert.
+        record = proxy.readFromServer();
+        Assert.assertNotNull(record);
+        Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
         record = proxy.readFromServer();
         Assert.assertNull(record);
 
@@ -381,19 +374,15 @@
         Assert.assertThat(httpParses.get(), Matchers.lessThan(20));
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testHandshakeWithSplitBoundary() throws Exception
     {
         final SSLSocket client = newClient();
 
-        Future<Object> handshake = threadPool.submit(new Callable<Object>()
+        Future<Object> handshake = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                client.startHandshake();
-                return null;
-            }
+            client.startHandshake();
+            return null;
         });
 
         // Client Hello
@@ -479,26 +468,22 @@
         if (record!=null)
         {
             Assert.assertEquals(record.getType(),Type.ALERT);
-            
+
             // Now should be a raw close
             record = proxy.readFromServer();
             Assert.assertNull(String.valueOf(record), record);
         }
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testClientHelloIncompleteThenReset() throws Exception
     {
         final SSLSocket client = newClient();
 
-        threadPool.submit(new Callable<Object>()
+        threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                client.startHandshake();
-                return null;
-            }
+            client.startHandshake();
+            return null;
         });
 
         // Client Hello
@@ -519,19 +504,15 @@
         client.close();
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testClientHelloThenReset() throws Exception
     {
         final SSLSocket client = newClient();
 
-        threadPool.submit(new Callable<Object>()
+        threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                client.startHandshake();
-                return null;
-            }
+            client.startHandshake();
+            return null;
         });
 
         // Client Hello
@@ -550,7 +531,7 @@
         client.close();
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testHandshakeThenReset() throws Exception
     {
         final SSLSocket client = newClient();
@@ -570,7 +551,7 @@
         client.close();
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestIncompleteThenReset() throws Exception
     {
         final SSLSocket client = newClient();
@@ -579,19 +560,15 @@
         client.startHandshake();
         Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
 
-        threadPool.submit(new Callable<Object>()
+        threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                OutputStream clientOutput = client.getOutputStream();
-                clientOutput.write(("" +
-                        "GET / HTTP/1.1\r\n" +
-                        "Host: localhost\r\n" +
-                        "\r\n").getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            OutputStream clientOutput = client.getOutputStream();
+            clientOutput.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "\r\n").getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Application data
@@ -612,7 +589,7 @@
         client.close();
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestResponse() throws Exception
     {
         final SSLSocket client = newClient();
@@ -621,19 +598,15 @@
         client.startHandshake();
         Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
 
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                OutputStream clientOutput = client.getOutputStream();
-                clientOutput.write(("" +
-                        "GET / HTTP/1.1\r\n" +
-                        "Host: localhost\r\n" +
-                        "\r\n").getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            OutputStream clientOutput = client.getOutputStream();
+            clientOutput.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "\r\n").getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Application data
@@ -665,19 +638,15 @@
         closeClient(client);
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testHandshakeAndRequestOneByteAtATime() throws Exception
     {
         final SSLSocket client = newClient();
 
-        Future<Object> handshake = threadPool.submit(new Callable<Object>()
+        Future<Object> handshake = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                client.startHandshake();
-                return null;
-            }
+            client.startHandshake();
+            return null;
         });
 
         // Client Hello
@@ -714,19 +683,15 @@
 
         Assert.assertNull(handshake.get(1, TimeUnit.SECONDS));
 
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                OutputStream clientOutput = client.getOutputStream();
-                clientOutput.write(("" +
-                        "GET / HTTP/1.1\r\n" +
-                        "Host: localhost\r\n" +
-                        "\r\n").getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            OutputStream clientOutput = client.getOutputStream();
+            clientOutput.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "\r\n").getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Application data
@@ -752,11 +717,11 @@
 
         // Check that we did not spin
         TimeUnit.MILLISECONDS.sleep(1000);
-        Assert.assertThat(sslFills.get(), Matchers.lessThan(1000));
+        Assert.assertThat(sslFills.get(), Matchers.lessThan(2000));
         Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20));
         // An average of 958 httpParses is seen in standard Oracle JDK's
         // An average of 1183 httpParses is seen in OpenJDK JVMs.
-        Assert.assertThat(httpParses.get(), Matchers.lessThan(500));
+        Assert.assertThat(httpParses.get(), Matchers.lessThan(2000));
 
         client.close();
 
@@ -775,14 +740,14 @@
         if (record!=null)
         {
             Assert.assertEquals(record.getType(),Type.ALERT);
-            
+
             // Now should be a raw close
             record = proxy.readFromServer();
             Assert.assertNull(String.valueOf(record), record);
         }
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestWithCloseAlertAndShutdown() throws Exception
     {
         // See next test on why we only run in Linux
@@ -794,19 +759,15 @@
         client.startHandshake();
         Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
 
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                OutputStream clientOutput = client.getOutputStream();
-                clientOutput.write(("" +
-                        "GET / HTTP/1.1\r\n" +
-                        "Host: localhost\r\n" +
-                        "\r\n").getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            OutputStream clientOutput = client.getOutputStream();
+            clientOutput.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "\r\n").getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Application data
@@ -837,7 +798,7 @@
         if (record!=null)
         {
             Assert.assertEquals(record.getType(),Type.ALERT);
-            
+
             // Now should be a raw close
             record = proxy.readFromServer();
             Assert.assertNull(String.valueOf(record), record);
@@ -850,7 +811,7 @@
         Assert.assertThat(httpParses.get(), Matchers.lessThan(20));
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestWithCloseAlert() throws Exception
     {
         // Currently we are ignoring this test on anything other then linux
@@ -869,19 +830,15 @@
         client.startHandshake();
         Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
 
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                OutputStream clientOutput = client.getOutputStream();
-                clientOutput.write(("" +
-                        "GET / HTTP/1.1\r\n" +
-                        "Host: localhost\r\n" +
-                        "\r\n").getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            OutputStream clientOutput = client.getOutputStream();
+            clientOutput.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "\r\n").getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Application data
@@ -912,7 +869,7 @@
         if (record!=null)
         {
             Assert.assertEquals(record.getType(),Type.ALERT);
-            
+
             // Now should be a raw close
             record = proxy.readFromServer();
             Assert.assertNull(String.valueOf(record), record);
@@ -930,7 +887,7 @@
         proxy.flushToServer(record);
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestWithRawClose() throws Exception
     {
         final SSLSocket client = newClient();
@@ -939,19 +896,15 @@
         client.startHandshake();
         Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
 
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                OutputStream clientOutput = client.getOutputStream();
-                clientOutput.write(("" +
-                        "GET / HTTP/1.1\r\n" +
-                        "Host: localhost\r\n" +
-                        "\r\n").getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            OutputStream clientOutput = client.getOutputStream();
+            clientOutput.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "\r\n").getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Application data
@@ -974,7 +927,7 @@
         if (record!=null)
         {
             Assert.assertEquals(record.getType(),Type.ALERT);
-            
+
             // Now should be a raw close
             record = proxy.readFromServer();
             Assert.assertNull(String.valueOf(record), record);
@@ -989,7 +942,7 @@
         client.close();
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestWithImmediateRawClose() throws Exception
     {
         final SSLSocket client = newClient();
@@ -998,19 +951,15 @@
         client.startHandshake();
         Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
 
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                OutputStream clientOutput = client.getOutputStream();
-                clientOutput.write(("" +
-                        "GET / HTTP/1.1\r\n" +
-                        "Host: localhost\r\n" +
-                        "\r\n").getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            OutputStream clientOutput = client.getOutputStream();
+            clientOutput.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "\r\n").getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Application data
@@ -1031,7 +980,7 @@
         if (record!=null)
         {
             Assert.assertEquals(record.getType(),Type.ALERT);
-            
+
             // Now should be a raw close
             record = proxy.readFromServer();
             Assert.assertNull(String.valueOf(record), record);
@@ -1046,12 +995,12 @@
         client.close();
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestWithBigContentWriteBlockedThenReset() throws Exception
     {
         // Don't run on Windows (buggy JVM)
         Assume.assumeTrue(!OS.IS_WINDOWS);
-        
+
         final SSLSocket client = newClient();
 
         SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
@@ -1061,21 +1010,17 @@
         byte[] data = new byte[128 * 1024];
         Arrays.fill(data, (byte)'X');
         final String content = new String(data, StandardCharsets.UTF_8);
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                OutputStream clientOutput = client.getOutputStream();
-                clientOutput.write(("" +
-                        "GET /echo HTTP/1.1\r\n" +
-                        "Host: localhost\r\n" +
-                        "Content-Length: " + content.length() + "\r\n" +
-                        "\r\n" +
-                        content).getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            OutputStream clientOutput = client.getOutputStream();
+            clientOutput.write(("" +
+                    "GET /echo HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "Content-Length: " + content.length() + "\r\n" +
+                    "\r\n" +
+                    content).getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Nine TLSRecords will be generated for the request
@@ -1107,12 +1052,12 @@
         client.close();
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestWithBigContentReadBlockedThenReset() throws Exception
     {
         // Don't run on Windows (buggy JVM)
         Assume.assumeTrue(!OS.IS_WINDOWS);
-        
+
         final SSLSocket client = newClient();
 
         SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
@@ -1122,21 +1067,17 @@
         byte[] data = new byte[128 * 1024];
         Arrays.fill(data, (byte)'X');
         final String content = new String(data, StandardCharsets.UTF_8);
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                OutputStream clientOutput = client.getOutputStream();
-                clientOutput.write(("" +
-                        "GET /echo_suppress_exception HTTP/1.1\r\n" +
-                        "Host: localhost\r\n" +
-                        "Content-Length: " + content.length() + "\r\n" +
-                        "\r\n" +
-                        content).getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            OutputStream clientOutput = client.getOutputStream();
+            clientOutput.write(("" +
+                    "GET /echo_suppress_exception HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "Content-Length: " + content.length() + "\r\n" +
+                    "\r\n" +
+                    content).getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Nine TLSRecords will be generated for the request,
@@ -1163,7 +1104,7 @@
         client.close();
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestWithCloseAlertWithSplitBoundary() throws Exception
     {
         if (!OS.IS_LINUX)
@@ -1186,19 +1127,15 @@
         client.startHandshake();
         Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
 
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                OutputStream clientOutput = client.getOutputStream();
-                clientOutput.write(("" +
-                        "GET / HTTP/1.1\r\n" +
-                        "Host: localhost\r\n" +
-                        "\r\n").getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            OutputStream clientOutput = client.getOutputStream();
+            clientOutput.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "\r\n").getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Application data
@@ -1238,7 +1175,7 @@
         if (record!=null)
         {
             Assert.assertEquals(record.getType(),Type.ALERT);
-            
+
             // Now should be a raw close
             record = proxy.readFromServer();
             Assert.assertNull(String.valueOf(record), record);
@@ -1251,7 +1188,7 @@
         Assert.assertThat(httpParses.get(), Matchers.lessThan(20));
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestWithContentWithSplitBoundary() throws Exception
     {
         final SSLSocket client = newClient();
@@ -1262,22 +1199,18 @@
 
         final String content = "0123456789ABCDEF";
 
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                OutputStream clientOutput = client.getOutputStream();
-                clientOutput.write(("" +
-                        "POST / HTTP/1.1\r\n" +
-                        "Host: localhost\r\n" +
-                        "Content-Type: text/plain\r\n" +
-                        "Content-Length: " + content.length() + "\r\n" +
-                        "\r\n" +
-                        content).getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            OutputStream clientOutput = client.getOutputStream();
+            clientOutput.write(("" +
+                    "POST / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "Content-Type: text/plain\r\n" +
+                    "Content-Length: " + content.length() + "\r\n" +
+                    "\r\n" +
+                    content).getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Application data
@@ -1314,7 +1247,7 @@
         closeClient(client);
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestWithBigContentWithSplitBoundary() throws Exception
     {
         final SSLSocket client = newClient();
@@ -1328,22 +1261,18 @@
         Arrays.fill(data, (byte)'X');
         final String content = new String(data, StandardCharsets.UTF_8);
 
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                OutputStream clientOutput = client.getOutputStream();
-                clientOutput.write(("" +
-                        "POST / HTTP/1.1\r\n" +
-                        "Host: localhost\r\n" +
-                        "Content-Type: text/plain\r\n" +
-                        "Content-Length: " + content.length() + "\r\n" +
-                        "\r\n" +
-                        content).getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            OutputStream clientOutput = client.getOutputStream();
+            clientOutput.write(("" +
+                    "POST / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "Content-Type: text/plain\r\n" +
+                    "Content-Length: " + content.length() + "\r\n" +
+                    "\r\n" +
+                    content).getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Nine TLSRecords will be generated for the request
@@ -1362,8 +1291,8 @@
 
         // Check that we did not spin
         TimeUnit.MILLISECONDS.sleep(500);
-        Assert.assertThat(sslFills.get(), Matchers.lessThan(50));
-        Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20));
+        Assert.assertThat(sslFills.get(), Matchers.lessThan(100));
+        Assert.assertThat(sslFlushes.get(), Matchers.lessThan(50));
         Assert.assertThat(httpParses.get(), Matchers.lessThan(100));
 
         Assert.assertNull(request.get(5, TimeUnit.SECONDS));
@@ -1384,8 +1313,8 @@
 
         // Check that we did not spin
         TimeUnit.MILLISECONDS.sleep(500);
-        Assert.assertThat(sslFills.get(), Matchers.lessThan(50));
-        Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20));
+        Assert.assertThat(sslFills.get(), Matchers.lessThan(100));
+        Assert.assertThat(sslFlushes.get(), Matchers.lessThan(50));
         Assert.assertThat(httpParses.get(), Matchers.lessThan(100));
 
         closeClient(client);
@@ -1427,14 +1356,10 @@
         Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
 
         // Renegotiate
-        threadPool.submit(new Callable<Object>()
+        threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                client.startHandshake();
-                return null;
-            }
+            client.startHandshake();
+            return null;
         });
 
         // Renegotiation Handshake
@@ -1451,15 +1376,11 @@
         Assert.assertNull(record);
 
         // Write the rest of the request
-        threadPool.submit(new Callable<Object>()
+        threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                clientOutput.write(content2.getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            clientOutput.write(content2.getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Trying to write more application data results in an exception since the server closed
@@ -1474,6 +1395,7 @@
         }
         catch (IOException expected)
         {
+            // Expected
         }
 
         // Check that we did not spin
@@ -1485,7 +1407,7 @@
         client.close();
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestWithBigContentWithRenegotiationInMiddleOfContent() throws Exception
     {
         assumeJavaVersionSupportsTLSRenegotiations();
@@ -1518,14 +1440,10 @@
         Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
 
         // Renegotiate
-        Future<Object> renegotiation = threadPool.submit(new Callable<Object>()
+        Future<Object> renegotiation = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                client.startHandshake();
-                return null;
-            }
+            client.startHandshake();
+            return null;
         });
 
         // Renegotiation Handshake
@@ -1573,15 +1491,11 @@
         Assert.assertNull(renegotiation.get(5, TimeUnit.SECONDS));
 
         // Write the rest of the request
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                clientOutput.write(content2.getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            clientOutput.write(content2.getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Three TLSRecords will be generated for the remainder of the content
@@ -1652,14 +1566,10 @@
         Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
 
         // Renegotiate
-        Future<Object> renegotiation = threadPool.submit(new Callable<Object>()
+        Future<Object> renegotiation = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                client.startHandshake();
-                return null;
-            }
+            client.startHandshake();
+            return null;
         });
 
         // Renegotiation Handshake
@@ -1725,15 +1635,11 @@
         Assert.assertNull(renegotiation.get(5, TimeUnit.SECONDS));
 
         // Write the rest of the request
-        Future<Object> request = threadPool.submit(new Callable<Object>()
+        Future<Object> request = threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                clientOutput.write(content2.getBytes(StandardCharsets.UTF_8));
-                clientOutput.flush();
-                return null;
-            }
+            clientOutput.write(content2.getBytes(StandardCharsets.UTF_8));
+            clientOutput.flush();
+            return null;
         });
 
         // Three TLSRecords will be generated for the remainder of the content
@@ -1781,7 +1687,7 @@
         closeClient(client);
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testServerShutdownOutputClientDoesNotCloseServerCloses() throws Exception
     {
         final SSLSocket client = newClient();
@@ -1835,27 +1741,26 @@
         Assert.assertFalse(serverEndPoint.get().isOpen());
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testPlainText() throws Exception
     {
         final SSLSocket client = newClient();
 
-        threadPool.submit(new Callable<Object>()
+        threadPool.submit(() ->
         {
-            @Override
-            public Object call() throws Exception
-            {
-                client.startHandshake();
-                return null;
-            }
+            client.startHandshake();
+            return null;
         });
 
         // Instead of passing the Client Hello, we simulate plain text was passed in
         proxy.flushToServer(0, "GET / HTTP/1.1\r\n".getBytes(StandardCharsets.UTF_8));
 
-        // We expect that the server closes the connection immediately
+        // We expect that the server sends the TLS Alert.
         TLSRecord record = proxy.readFromServer();
-        Assert.assertNull(String.valueOf(record), record);
+        Assert.assertNotNull(record);
+        Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
+        record = proxy.readFromServer();
+        Assert.assertNull(record);
 
         // Check that we did not spin
         TimeUnit.MILLISECONDS.sleep(500);
@@ -1866,34 +1771,31 @@
         client.close();
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testRequestConcurrentWithIdleExpiration() throws Exception
     {
         final SSLSocket client = newClient();
         final OutputStream clientOutput = client.getOutputStream();
         final CountDownLatch latch = new CountDownLatch(1);
 
-        idleHook = new Runnable()
+        idleHook = () ->
         {
-            public void run()
+            if (latch.getCount()==0)
+                return;
+            try
             {
-                if (latch.getCount()==0)
-                    return;
-                try
-                {
-                    // Send request
-                    clientOutput.write(("" +
-                            "GET / HTTP/1.1\r\n" +
-                            "Host: localhost\r\n" +
-                            "\r\n").getBytes(StandardCharsets.UTF_8));
-                    clientOutput.flush();
-                    latch.countDown();
-                }
-                catch (Exception x)
-                {
-                    // Latch won't trigger and test will fail
-                    x.printStackTrace();
-                }
+                // Send request
+                clientOutput.write(("" +
+                        "GET / HTTP/1.1\r\n" +
+                        "Host: localhost\r\n" +
+                        "\r\n").getBytes(StandardCharsets.UTF_8));
+                clientOutput.flush();
+                latch.countDown();
+            }
+            catch (Exception x)
+            {
+                // Latch won't trigger and test will fail
+                x.printStackTrace();
             }
         };
 
@@ -1968,11 +1870,11 @@
 
         // Socket close
         record = proxy.readFromServer();
-        if (record!=null)
+        if (record != null)
         {
-            Assert.assertEquals(record.getType(),Type.ALERT);
+            Assert.assertEquals(record.getType(), Type.ALERT);
             record = proxy.readFromServer();
         }
-        Assert.assertThat(record,nullValue());
+        Assert.assertNull(record);
     }
 }
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesTest.java
index 39347de..12b9d51 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesTest.java
@@ -28,7 +28,6 @@
 import java.net.SocketTimeoutException;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
@@ -49,16 +48,16 @@
 
     public static class TLSRecord
     {
-        private final SslBytesServerTest.TLSRecord.Type type;
+        private final Type type;
         private final byte[] bytes;
 
-        public TLSRecord(SslBytesServerTest.TLSRecord.Type type, byte[] bytes)
+        public TLSRecord(Type type, byte[] bytes)
         {
             this.type = type;
             this.bytes = bytes;
         }
 
-        public SslBytesServerTest.TLSRecord.Type getType()
+        public Type getType()
         {
             return type;
         }
@@ -80,15 +79,15 @@
 
             private int code;
 
-            private Type(int code)
+            Type(int code)
             {
                 this.code = code;
-                SslBytesServerTest.TLSRecord.Type.Mapper.codes.put(this.code, this);
+                Mapper.codes.put(this.code, this);
             }
 
-            public static SslBytesServerTest.TLSRecord.Type from(int code)
+            public static Type from(int code)
             {
-                SslBytesServerTest.TLSRecord.Type result = SslBytesServerTest.TLSRecord.Type.Mapper.codes.get(code);
+                Type result = Mapper.codes.get(code);
                 if (result == null)
                     throw new IllegalArgumentException("Invalid TLSRecord.Type " + code);
                 return result;
@@ -96,7 +95,7 @@
 
             private static class Mapper
             {
-                private static final Map<Integer, SslBytesServerTest.TLSRecord.Type> codes = new HashMap<>();
+                private static final Map<Integer, Type> codes = new HashMap<>();
             }
         }
     }
@@ -218,7 +217,7 @@
             }
         }
 
-        private TLSRecord read(SslBytesServerTest.TLSRecord.Type type, InputStream input, byte[] bytes, int offset, int length) throws IOException
+        private TLSRecord read(TLSRecord.Type type, InputStream input, byte[] bytes, int offset, int length) throws IOException
         {
             while (length > 0)
             {
@@ -291,56 +290,50 @@
             }
         }
 
-        public SslBytesServerTest.SimpleProxy.AutomaticFlow startAutomaticFlow() throws InterruptedException
+        public SslBytesTest.SimpleProxy.AutomaticFlow startAutomaticFlow() throws InterruptedException
         {
             final CountDownLatch startLatch = new CountDownLatch(2);
             final CountDownLatch stopLatch = new CountDownLatch(2);
-            Future<Object> clientToServer = threadPool.submit(new Callable<Object>()
+            Future<Object> clientToServer = threadPool.submit(() ->
             {
-                public Object call() throws Exception
+                startLatch.countDown();
+                logger.debug("Automatic flow C --> S started");
+                try
                 {
-                    startLatch.countDown();
-                    logger.debug("Automatic flow C --> S started");
-                    try
+                    while (true)
                     {
-                        while (true)
-                        {
-                            flushToServer(readFromClient(), 0);
-                        }
-                    }
-                    catch (InterruptedIOException x)
-                    {
-                        return null;
-                    }
-                    finally
-                    {
-                        stopLatch.countDown();
-                        logger.debug("Automatic flow C --> S finished");
+                        flushToServer(readFromClient(), 0);
                     }
                 }
-            });
-            Future<Object> serverToClient = threadPool.submit(new Callable<Object>()
-            {
-                public Object call() throws Exception
+                catch (InterruptedIOException x)
                 {
-                    startLatch.countDown();
-                    logger.debug("Automatic flow C <-- S started");
-                    try
+                    return null;
+                }
+                finally
+                {
+                    stopLatch.countDown();
+                    logger.debug("Automatic flow C --> S finished");
+                }
+            });
+            Future<Object> serverToClient = threadPool.submit(() ->
+            {
+                startLatch.countDown();
+                logger.debug("Automatic flow C <-- S started");
+                try
+                {
+                    while (true)
                     {
-                        while (true)
-                        {
-                            flushToClient(readFromServer());
-                        }
+                        flushToClient(readFromServer());
                     }
-                    catch (InterruptedIOException x)
-                    {
-                        return null;
-                    }
-                    finally
-                    {
-                        stopLatch.countDown();
-                        logger.debug("Automatic flow C <-- S finished");
-                    }
+                }
+                catch (InterruptedIOException x)
+                {
+                    return null;
+                }
+                finally
+                {
+                    stopLatch.countDown();
+                    logger.debug("Automatic flow C <-- S finished");
                 }
             });
             Assert.assertTrue(startLatch.await(5, TimeUnit.SECONDS));
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslConnectionTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslConnectionTest.java
new file mode 100644
index 0000000..dee20c4
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslConnectionTest.java
@@ -0,0 +1,94 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client.ssl;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLHandshakeException;
+
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.ByteArrayEndPoint;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.io.ssl.SslConnection;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SslConnectionTest
+{
+    @Test
+    public void testSslConnectionClosedBeforeFill() throws Exception
+    {
+        File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks");
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath());
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.start();
+
+        ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+        QueuedThreadPool threadPool = new QueuedThreadPool();
+        threadPool.start();
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
+        SSLEngine sslEngine = sslContextFactory.newSSLEngine();
+        sslEngine.setUseClientMode(false);
+        SslConnection sslConnection = new SslConnection(byteBufferPool, threadPool, endPoint, sslEngine);
+        EndPoint sslEndPoint = sslConnection.getDecryptedEndPoint();
+        sslEndPoint.setConnection(new AbstractConnection(sslEndPoint, threadPool)
+        {
+            @Override
+            public void onFillable()
+            {
+            }
+        });
+
+        // There are no bytes in the endPoint, so we fill zero.
+        // However, this will trigger state changes in SSLEngine
+        // that will later cause it to throw ISE("Internal error").
+        sslEndPoint.fill(BufferUtil.EMPTY_BUFFER);
+
+        // Close the connection before filling.
+        sslEndPoint.shutdownOutput();
+
+        // Put some bytes in the endPoint to trigger
+        // the required state changes in SSLEngine.
+        byte[] bytes = new byte[]{0x16, 0x03, 0x03, 0x00, 0x00};
+        endPoint.addInput(ByteBuffer.wrap(bytes));
+
+        // This attempt to read, if not guarded, throws ISE("Internal error").
+        // We want SSLHandshakeException to be thrown instead, because it is
+        // handled better (it is an IOException) by the Connection code that
+        // reads from the EndPoint.
+        try
+        {
+            sslEndPoint.fill(BufferUtil.EMPTY_BUFFER);
+            Assert.fail();
+        }
+        catch (SSLHandshakeException x)
+        {
+            // Expected.
+        }
+    }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java
new file mode 100644
index 0000000..44cea0c
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java
@@ -0,0 +1,444 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client.util;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.MultipartConfigElement;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.Part;
+
+import org.eclipse.jetty.client.AbstractHttpClientServerTest;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MultiPartContentProviderTest extends AbstractHttpClientServerTest
+{
+    public MultiPartContentProviderTest(SslContextFactory sslContextFactory)
+    {
+        super(sslContextFactory);
+    }
+
+    @Test
+    public void testEmptyMultiPart() throws Exception
+    {
+        start(new AbstractMultiPartHandler()
+        {
+            @Override
+            protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                Collection<Part> parts = request.getParts();
+                Assert.assertEquals(0, parts.size());
+            }
+        });
+
+        MultiPartContentProvider multiPart = new MultiPartContentProvider();
+        multiPart.close();
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .method(HttpMethod.POST)
+                .content(multiPart)
+                .send();
+
+        Assert.assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testSimpleField() throws Exception
+    {
+        String name = "field";
+        String value = "value";
+        start(new AbstractMultiPartHandler()
+        {
+            @Override
+            protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                Collection<Part> parts = request.getParts();
+                Assert.assertEquals(1, parts.size());
+                Part part = parts.iterator().next();
+                Assert.assertEquals(name, part.getName());
+                Assert.assertEquals(value, IO.toString(part.getInputStream()));
+            }
+        });
+
+        MultiPartContentProvider multiPart = new MultiPartContentProvider();
+        multiPart.addFieldPart(name, new StringContentProvider(value), null);
+        multiPart.close();
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .method(HttpMethod.POST)
+                .content(multiPart)
+                .send();
+
+        Assert.assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testFieldWithOverridenContentType() throws Exception
+    {
+        String name = "field";
+        String value = "\u00e8";
+        Charset encoding = StandardCharsets.ISO_8859_1;
+        start(new AbstractMultiPartHandler()
+        {
+            @Override
+            protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                Collection<Part> parts = request.getParts();
+                Assert.assertEquals(1, parts.size());
+                Part part = parts.iterator().next();
+                Assert.assertEquals(name, part.getName());
+                String contentType = part.getContentType();
+                Assert.assertNotNull(contentType);
+                int equal = contentType.lastIndexOf('=');
+                Charset charset = Charset.forName(contentType.substring(equal + 1));
+                Assert.assertEquals(encoding, charset);
+                Assert.assertEquals(value, IO.toString(part.getInputStream(), charset));
+            }
+        });
+
+        MultiPartContentProvider multiPart = new MultiPartContentProvider();
+        HttpFields fields = new HttpFields();
+        fields.put(HttpHeader.CONTENT_TYPE, "text/plain;charset=" + encoding.name());
+        BytesContentProvider content = new BytesContentProvider(value.getBytes(encoding));
+        multiPart.addFieldPart(name, content, fields);
+        multiPart.close();
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .method(HttpMethod.POST)
+                .content(multiPart)
+                .send();
+
+        Assert.assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testFieldDeferred() throws Exception
+    {
+        String name = "field";
+        byte[] data = "Hello, World".getBytes(StandardCharsets.US_ASCII);
+        start(new AbstractMultiPartHandler()
+        {
+            @Override
+            protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                Collection<Part> parts = request.getParts();
+                Assert.assertEquals(1, parts.size());
+                Part part = parts.iterator().next();
+                Assert.assertEquals(name, part.getName());
+                Assert.assertEquals("text/plain", part.getContentType());
+                Assert.assertArrayEquals(data, IO.readBytes(part.getInputStream()));
+            }
+        });
+
+        MultiPartContentProvider multiPart = new MultiPartContentProvider();
+        DeferredContentProvider content = new DeferredContentProvider();
+        multiPart.addFieldPart(name, content, null);
+        multiPart.close();
+        CountDownLatch responseLatch = new CountDownLatch(1);
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .method(HttpMethod.POST)
+                .content(multiPart)
+                .send(result ->
+                {
+                    Assert.assertTrue(String.valueOf(result.getFailure()), result.isSucceeded());
+                    Assert.assertEquals(200, result.getResponse().getStatus());
+                    responseLatch.countDown();
+                });
+
+        // Wait until the request has been sent.
+        Thread.sleep(1000);
+
+        // Provide the content.
+        content.offer(ByteBuffer.wrap(data));
+        content.close();
+
+        Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testFileFromInputStream() throws Exception
+    {
+        String name = "file";
+        String fileName = "upload.png";
+        String contentType = "image/png";
+        byte[] data = new byte[512];
+        new Random().nextBytes(data);
+        start(new AbstractMultiPartHandler()
+        {
+            @Override
+            protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                Collection<Part> parts = request.getParts();
+                Assert.assertEquals(1, parts.size());
+                Part part = parts.iterator().next();
+                Assert.assertEquals(name, part.getName());
+                Assert.assertEquals(contentType, part.getContentType());
+                Assert.assertEquals(fileName, part.getSubmittedFileName());
+                Assert.assertEquals(data.length, part.getSize());
+                Assert.assertArrayEquals(data, IO.readBytes(part.getInputStream()));
+            }
+        });
+
+        CountDownLatch closeLatch = new CountDownLatch(1);
+        MultiPartContentProvider multiPart = new MultiPartContentProvider();
+        InputStreamContentProvider content = new InputStreamContentProvider(new ByteArrayInputStream(data)
+        {
+            @Override
+            public void close() throws IOException
+            {
+                super.close();
+                closeLatch.countDown();
+            }
+        });
+        HttpFields fields = new HttpFields();
+        fields.put(HttpHeader.CONTENT_TYPE, contentType);
+        multiPart.addFilePart(name, fileName, content, fields);
+        multiPart.close();
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .method(HttpMethod.POST)
+                .content(multiPart)
+                .send();
+
+        Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testFileFromPath() throws Exception
+    {
+        // Prepare a file to upload.
+        String data = "multipart_test_\u20ac";
+        Path tmpDir = MavenTestingUtils.getTargetTestingPath();
+        Path tmpPath = Files.createTempFile(tmpDir, "multipart_", ".txt");
+        Charset encoding = StandardCharsets.UTF_8;
+        try (BufferedWriter writer = Files.newBufferedWriter(tmpPath, encoding, StandardOpenOption.CREATE))
+        {
+            writer.write(data);
+        }
+
+        String name = "file";
+        String contentType = "text/plain; charset=" + encoding.name();
+        start(new AbstractMultiPartHandler()
+        {
+            @Override
+            protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                Collection<Part> parts = request.getParts();
+                Assert.assertEquals(1, parts.size());
+                Part part = parts.iterator().next();
+                Assert.assertEquals(name, part.getName());
+                Assert.assertEquals(contentType, part.getContentType());
+                Assert.assertEquals(tmpPath.getFileName().toString(), part.getSubmittedFileName());
+                Assert.assertEquals(Files.size(tmpPath), part.getSize());
+                Assert.assertEquals(data, IO.toString(part.getInputStream(), encoding));
+            }
+        });
+
+        MultiPartContentProvider multiPart = new MultiPartContentProvider();
+        PathContentProvider content = new PathContentProvider(contentType, tmpPath);
+        content.setByteBufferPool(client.getByteBufferPool());
+        multiPart.addFilePart(name, tmpPath.getFileName().toString(), content, null);
+        multiPart.close();
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .method(HttpMethod.POST)
+                .content(multiPart)
+                .send();
+
+        Assert.assertEquals(200, response.getStatus());
+
+        Files.delete(tmpPath);
+    }
+
+    @Test
+    public void testFieldWithFile() throws Exception
+    {
+        // Prepare a file to upload.
+        byte[] data = new byte[1024];
+        new Random().nextBytes(data);
+        Path tmpDir = MavenTestingUtils.getTargetTestingPath();
+        Path tmpPath = Files.createTempFile(tmpDir, "multipart_", ".txt");
+        try (OutputStream output = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE))
+        {
+            output.write(data);
+        }
+
+        String field = "field";
+        String value = "\u20ac";
+        String fileField = "file";
+        Charset encoding = StandardCharsets.UTF_8;
+        String contentType = "text/plain;charset=" + encoding.name();
+        String headerName = "foo";
+        String headerValue = "bar";
+        start(new AbstractMultiPartHandler()
+        {
+            @Override
+            protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                List<Part> parts = new ArrayList<>(request.getParts());
+                Assert.assertEquals(2, parts.size());
+                Part fieldPart = parts.get(0);
+                Part filePart = parts.get(1);
+                if (!field.equals(fieldPart.getName()))
+                {
+                    Part swap = filePart;
+                    filePart = fieldPart;
+                    fieldPart = swap;
+                }
+
+                Assert.assertEquals(field, fieldPart.getName());
+                Assert.assertEquals(contentType, fieldPart.getContentType());
+                Assert.assertEquals(value, IO.toString(fieldPart.getInputStream(), encoding));
+                Assert.assertEquals(headerValue, fieldPart.getHeader(headerName));
+
+                Assert.assertEquals(fileField, filePart.getName());
+                Assert.assertEquals("application/octet-stream", filePart.getContentType());
+                Assert.assertEquals(tmpPath.getFileName().toString(), filePart.getSubmittedFileName());
+                Assert.assertEquals(Files.size(tmpPath), filePart.getSize());
+                Assert.assertArrayEquals(data, IO.readBytes(filePart.getInputStream()));
+            }
+        });
+
+        MultiPartContentProvider multiPart = new MultiPartContentProvider();
+        HttpFields fields = new HttpFields();
+        fields.put(headerName, headerValue);
+        multiPart.addFieldPart(field, new StringContentProvider(value, encoding), fields);
+        multiPart.addFilePart(fileField, tmpPath.getFileName().toString(), new PathContentProvider(tmpPath), null);
+        multiPart.close();
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .method(HttpMethod.POST)
+                .content(multiPart)
+                .send();
+
+        Assert.assertEquals(200, response.getStatus());
+
+        Files.delete(tmpPath);
+    }
+
+    @Test
+    public void testFieldDeferredAndFileDeferred() throws Exception
+    {
+        String value = "text";
+        Charset encoding = StandardCharsets.US_ASCII;
+        byte[] fileData = new byte[1024];
+        new Random().nextBytes(fileData);
+        start(new AbstractMultiPartHandler()
+        {
+            @Override
+            protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                List<Part> parts = new ArrayList<>(request.getParts());
+                Assert.assertEquals(2, parts.size());
+                Part fieldPart = parts.get(0);
+                Part filePart = parts.get(1);
+                if (!"field".equals(fieldPart.getName()))
+                {
+                    Part swap = filePart;
+                    filePart = fieldPart;
+                    fieldPart = swap;
+                }
+
+                Assert.assertEquals(value, IO.toString(fieldPart.getInputStream(), encoding));
+
+                Assert.assertEquals("file", filePart.getName());
+                Assert.assertEquals("application/octet-stream", filePart.getContentType());
+                Assert.assertEquals("fileName", filePart.getSubmittedFileName());
+                Assert.assertArrayEquals(fileData, IO.readBytes(filePart.getInputStream()));
+            }
+        });
+
+        MultiPartContentProvider multiPart = new MultiPartContentProvider();
+        DeferredContentProvider fieldContent = new DeferredContentProvider();
+        multiPart.addFieldPart("field", fieldContent, null);
+        DeferredContentProvider fileContent = new DeferredContentProvider();
+        multiPart.addFilePart("file", "fileName", fileContent, null);
+        CountDownLatch responseLatch = new CountDownLatch(1);
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .method(HttpMethod.POST)
+                .content(multiPart)
+                .send(result ->
+                {
+                    Assert.assertTrue(String.valueOf(result.getFailure()), result.isSucceeded());
+                    Assert.assertEquals(200, result.getResponse().getStatus());
+                    responseLatch.countDown();
+                });
+
+        // Wait until the request has been sent.
+        Thread.sleep(1000);
+
+        // Provide the content, in reversed part order.
+        fileContent.offer(ByteBuffer.wrap(fileData));
+        fileContent.close();
+
+        Thread.sleep(1000);
+
+        fieldContent.offer(encoding.encode(value));
+        fieldContent.close();
+
+        multiPart.close();
+
+        Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    private static abstract class AbstractMultiPartHandler extends AbstractHandler
+    {
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            File tmpDir = MavenTestingUtils.getTargetTestingDir();
+            request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, new MultipartConfigElement(tmpDir.getAbsolutePath()));
+            handle(request, response);
+        }
+
+        protected abstract void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
+    }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/util/TypedContentProviderTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/util/TypedContentProviderTest.java
index 71c22f0..1d80fcc 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/util/TypedContentProviderTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/util/TypedContentProviderTest.java
@@ -20,6 +20,7 @@
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -126,7 +127,7 @@
             {
                 baseRequest.setHandled(true);
                 Assert.assertEquals("GET", request.getMethod());
-                Assert.assertNull(request.getContentType());
+                Assert.assertNotNull(request.getContentType());
                 Assert.assertEquals(content, IO.toString(request.getInputStream()));
             }
         });
diff --git a/jetty-continuation/pom.xml b/jetty-continuation/pom.xml
index 1482a8b..3886b0f 100644
--- a/jetty-continuation/pom.xml
+++ b/jetty-continuation/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-continuation</artifactId>
@@ -15,35 +15,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>artifact-jar</id>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-        </executions>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Continuation.java b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Continuation.java
index 123aba4..8eb512b 100644
--- a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Continuation.java
+++ b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Continuation.java
@@ -18,12 +18,15 @@
 
 package org.eclipse.jetty.continuation;
 
+import javax.servlet.FilterChain;
+import javax.servlet.Servlet;
+import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
+import javax.servlet.ServletResponseWrapper;
 
-/* ------------------------------------------------------------ */
 /**
  * Continuation.
- * 
+ * <p> 
  * 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>
@@ -48,7 +51,7 @@
  * <p>
  * There are two distinct style of operation of the continuation API.
  * </p>
- * <h3>Suspend/Resume Usage</h3> 
+ * <h1>Suspend/Resume Usage</h1> 
  * <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.
@@ -83,7 +86,7 @@
  *     response.getOutputStream().write(process(results));
  *   }
  * </pre> 
- * <h3>Suspend/Complete Usage</h3> 
+ * <h1>Suspend/Complete Usage</h1> 
  * <p>
  * The suspend/complete style is used when an asynchronous handler is used to 
  * generate the response:
@@ -148,7 +151,7 @@
      * @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.
+     *            A timeout of &lt;=0 means the continuation will never expire.
      */
     void setTimeout(long timeoutMs);
 
@@ -160,8 +163,8 @@
      * <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)}
+     * {@link javax.servlet.Servlet#service(ServletRequest, ServletResponse)} method and
+     * {@link javax.servlet.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).
@@ -196,8 +199,8 @@
      * <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)}
+     * {@link javax.servlet.Servlet#service(ServletRequest, ServletResponse)} method and
+     * {@link javax.servlet.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).
@@ -361,7 +364,7 @@
     /** 
      * Add a ContinuationListener.
      * 
-     * @param listener
+     * @param listener the listener
      */
     void addContinuationListener(ContinuationListener listener);
     
diff --git a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationListener.java b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationListener.java
index 8193fbf..4c9574e 100644
--- a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationListener.java
+++ b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationListener.java
@@ -20,13 +20,13 @@
 
 import java.util.EventListener;
 
+import javax.servlet.ServletRequestListener;
 
-/* ------------------------------------------------------------ */
-/** A Continuation Listener
+/** 
+ * A Continuation Listener
  * <p>
  * A ContinuationListener may be registered with a call to
  * {@link Continuation#addContinuationListener(ContinuationListener)}.
- *
  */
 public interface ContinuationListener extends EventListener
 {
@@ -36,7 +36,7 @@
      * any calls to {@link ServletRequestListener#requestDestroyed(javax.servlet.ServletRequestEvent)}
      * The response may still be written to during the call.
      *
-     * @param continuation
+     * @param continuation the continuation
      */
     public void onComplete(Continuation continuation);
 
@@ -46,7 +46,7 @@
      * 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
+     * @param continuation the continuation
      */
     public void onTimeout(Continuation continuation);
 
diff --git a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Servlet3Continuation.java b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Servlet3Continuation.java
index 5cbab9f..10ee475 100644
--- a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Servlet3Continuation.java
+++ b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Servlet3Continuation.java
@@ -218,6 +218,7 @@
     {
         if (isSuspended())
         {
+            _initial=false;
             if (ContinuationFilter.__debug)
                 throw new ContinuationThrowable();
             throw __exception;
@@ -244,14 +245,12 @@
     @Override
     public void onStartAsync(AsyncEvent event) throws IOException
     {
-        _initial=false;
     }
 
     /* ------------------------------------------------------------ */
     @Override
     public void onTimeout(AsyncEvent event) throws IOException
     {
-        _initial=false;
         _expired=true;
         for (ContinuationListener listener:_listeners)
             listener.onTimeout(this);
diff --git a/jetty-deploy/pom.xml b/jetty-deploy/pom.xml
index 031bbe3..272990f 100644
--- a/jetty-deploy/pom.xml
+++ b/jetty-deploy/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-deploy</artifactId>
@@ -15,53 +15,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>org.eclipse.jetty.jmx.*;version="9.1";resolution:=optional,*</Import-Package>
-                <_nouses>true</_nouses>
-              </instructions>
-            </configuration>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-deploy/src/main/config/etc/jetty-deploy.xml
index 6dfc7e5..c27fb3e 100644
--- a/jetty-deploy/src/main/config/etc/jetty-deploy.xml
+++ b/jetty-deploy/src/main/config/etc/jetty-deploy.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Create the deployment manager                                   -->
@@ -39,8 +39,22 @@
         <Call id="webappprovider" name="addAppProvider">
           <Arg>
             <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
-              <Set name="monitoredDirName"><Property name="jetty.base" default="." />/<Property name="jetty.deploy.monitoredDirName" default="webapps"/></Set>
-              <Set name="defaultsDescriptor"><Property name="jetty.home" default="." />/etc/webdefault.xml</Set>
+              <Set name="monitoredDirName">
+                <Property>
+                  <Name>jetty.deploy.monitoredPath</Name>
+                  <Default>
+                    <Property name="jetty.base" default="." />/<Property name="jetty.deploy.monitoredDir" deprecated="jetty.deploy.monitoredDirName" default="webapps"/>
+                  </Default>
+                </Property>
+              </Set>
+              <Set name="defaultsDescriptor">
+                <Property>
+                  <Name>jetty.deploy.defaultsDescriptorPath</Name>
+                  <Default>
+                    <Property name="jetty.home" default="." />/etc/webdefault.xml
+                  </Default>
+                </Property>
+              </Set>
               <Set name="scanInterval"><Property name="jetty.deploy.scanInterval" default="1"/></Set>
               <Set name="extractWars"><Property name="jetty.deploy.extractWars" default="true"/></Set>
               <Set name="configurationManager">
diff --git a/jetty-deploy/src/main/config/modules/deploy.mod b/jetty-deploy/src/main/config/modules/deploy.mod
index f16b3f2..788b9e7 100644
--- a/jetty-deploy/src/main/config/modules/deploy.mod
+++ b/jetty-deploy/src/main/config/modules/deploy.mod
@@ -15,7 +15,17 @@
 etc/jetty-deploy.xml
 
 [ini-template]
-## DeployManager configuration
-# Monitored Directory name (relative to jetty.base)
-# jetty.deploy.monitoredDirName=webapps
+# Monitored directory name (relative to $jetty.base)
+# jetty.deploy.monitoredDir=webapps
+# - OR -
+# Monitored directory path (fully qualified)
+# jetty.deploy.monitoredPath=/var/www/webapps
 
+# Defaults Descriptor for all deployed webapps
+# jetty.deploy.defaultsDescriptorPath=${jetty.base}/etc/webdefault.xml
+
+# Monitored directory scan period (seconds)
+# jetty.deploy.scanInterval=1
+
+# Whether to extract *.war files
+# jetty.deploy.extractWars=true
diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java
index 9fc5628..0341329 100644
--- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java
+++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java
@@ -34,6 +34,8 @@
     /**
      * Create an App with specified Origin ID and archivePath
      * 
+     * @param manager the deployment manager 
+     * @param provider the app provider
      * @param originId
      *            the origin ID (The ID that the {@link AppProvider} knows
      *            about)
@@ -50,6 +52,8 @@
     /**
      * Create an App with specified Origin ID and archivePath
      * 
+     * @param manager the deployment manager 
+     * @param provider the app provider
      * @param originId
      *            the origin ID (The ID that the {@link AppProvider} knows
      *            about)
@@ -92,7 +96,7 @@
      * @return the {@link ContextHandler} to use for the App when fully started.
      *         (Portions of which might be ignored when App is not yet 
      *         {@link AppLifeCycle#DEPLOYED} or {@link AppLifeCycle#STARTED})
-     * @throws Exception
+     * @throws Exception if unable to get the context handler
      */
     public ContextHandler getContextHandler() throws Exception
     {
diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppLifeCycle.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppLifeCycle.java
index 1f3ae09..39749e8 100644
--- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppLifeCycle.java
+++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppLifeCycle.java
@@ -32,11 +32,11 @@
 
 /**
  * The lifecycle of an App in the {@link DeploymentManager}.
- * 
+ * <p>
  * Setups a the default {@link Graph}, and manages the bindings to the life cycle via the {@link AppLifeCycle.Binding}
  * annotation.
  * <p>
- * <img src="doc-files/AppLifeCycle.png">
+ * <img alt="app lifecycle graph" src="doc-files/AppLifeCycle.png">
  */
 public class AppLifeCycle extends Graph
 {
@@ -144,6 +144,7 @@
     /**
      * Get all objects bound to a specific {@link Node}
      * 
+     * @param node the deployment graph node 
      * @return Set of Object(s) for specific lifecycle bindings. never null.
      */
     public Set<AppLifeCycle.Binding> getBindings(Node node)
@@ -154,6 +155,7 @@
     /**
      * Get all objects bound to a specific {@link Node}
      * 
+     * @param nodeName the node name 
      * @return Set of Object(s) for specific lifecycle bindings. never null.
      */
     public Set<AppLifeCycle.Binding> getBindings(String nodeName)
diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java
index c93da9f..272cc95 100644
--- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java
+++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.deploy;
 
+import java.io.IOException;
+
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.util.component.LifeCycle;
 
@@ -29,7 +31,7 @@
     /**
      * Set the Deployment Manager
      * 
-     * @param deploymentManager
+     * @param deploymentManager the deployment manager
      * @throws IllegalStateException
      *             if the provider {@link #isRunning()}.
      */
@@ -39,8 +41,8 @@
     /** Create a ContextHandler for an App
      * @param app The App
      * @return A ContextHandler
-     * @throws IOException
-     * @throws Exception 
+     * @throws IOException if unable to create context
+     * @throws Exception if unable to create context
      */
     ContextHandler createContextHandler(App app) throws Exception;
 }
diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java
index 39e7023..d7963ce 100644
--- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java
+++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java
@@ -51,14 +51,14 @@
  * <p>
  * Responsibilities:
  * <p>
- * <img src="doc-files/DeploymentManager_Roles.png">
+ * <img alt="deployment manager roles graph" src="doc-files/DeploymentManager_Roles.png">
  * <ol>
  * <li>Tracking Apps and their LifeCycle Location</li>
  * <li>Managing AppProviders and the Apps that they provide.</li>
  * <li>Executing AppLifeCycle on App based on current and desired LifeCycle Location.</li>
  * </ol>
  * <p>
- * <img src="doc-files/DeploymentManager.png">
+ * <img alt="deployment manager graph" src="doc-files/DeploymentManager.png">
  */
 @ManagedObject("Deployment Manager")
 public class DeploymentManager extends ContainerLifeCycle
@@ -131,6 +131,7 @@
      * Receive an app for processing.
      * 
      * Most commonly used by the various {@link AppProvider} implementations.
+     * @param app the app
      */
     public void addApp(App app)
     {
@@ -151,7 +152,7 @@
     /** Set the AppProviders.
      * The providers passed are added via {@link #addBean(Object)} so that 
      * their lifecycles may be managed as a {@link ContainerLifeCycle}.
-     * @param providers
+     * @param providers the app provider list
      */
     public void setAppProviders(Collection<AppProvider> providers)
     {
@@ -202,9 +203,9 @@
     /**
      * Convenience method to allow for insertion of nodes into the lifecycle.
      * 
-     * @param existingFromNodeName
-     * @param existingToNodeName
-     * @param insertedNodeName
+     * @param existingFromNodeName the existing node start
+     * @param existingToNodeName the existing node end
+     * @param insertedNodeName the new node to create between the existing nodes
      */
     public void insertLifeCycleNode(String existingFromNodeName, String existingToNodeName, String insertedNodeName)
     {
@@ -353,7 +354,7 @@
     /**
      * Get a contextAttribute that will be set for every Context deployed by this provider.
      * 
-     * @param name
+     * @param name context attribute name
      * @return the context attribute value
      */
     public Object getContextAttribute(String name)
@@ -431,7 +432,7 @@
     /**
      * Remove a contextAttribute that will be set for every Context deployed by this provider.
      * 
-     * @param name
+     * @param name the context attribute name
      */
     public void removeContextAttribute(String name)
     {
@@ -529,8 +530,8 @@
     /**
      * Set a contextAttribute that will be set for every Context deployed by this provider.
      * 
-     * @param name
-     * @param value
+     * @param name the context attribute name
+     * @param value the context attribute value
      */
     public void setContextAttribute(String name, Object value)
     {
diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugListenerBinding.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugListenerBinding.java
new file mode 100644
index 0000000..f7fba98
--- /dev/null
+++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugListenerBinding.java
@@ -0,0 +1,53 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.deploy.bindings;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.graph.Node;
+import org.eclipse.jetty.server.DebugListener;
+
+
+/** A Deployment binding that installs a DebugListener in all deployed contexts
+ */
+public class DebugListenerBinding extends DebugBinding
+{
+    final DebugListener _debugListener;
+
+    public DebugListenerBinding()
+    {
+        this(new DebugListener());
+    }
+    
+    public DebugListenerBinding(DebugListener debugListener)
+    {
+        super(new String[]{"deploying"});
+        _debugListener=debugListener;
+    }
+    
+    public DebugListener getDebugListener()
+    {
+        return _debugListener;
+    }
+    
+    public void processBinding(Node node, App app) throws Exception
+    {
+        app.getContextHandler().addEventListener(_debugListener);
+    }
+
+}
diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java
index b90d9aa..5a50e27 100644
--- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java
+++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java
@@ -97,6 +97,8 @@
                 Resource resource = Resource.newResource(app.getOriginId());
                 File file = resource.getFile();
                 jettyXmlConfig.getIdMap().put("Server",app.getDeploymentManager().getServer());
+                jettyXmlConfig.getProperties().put("jetty.home",System.getProperty("jetty.home","."));
+                jettyXmlConfig.getProperties().put("jetty.base",System.getProperty("jetty.base","."));
                 jettyXmlConfig.getProperties().put("jetty.webapp",file.getCanonicalPath());
                 jettyXmlConfig.getProperties().put("jetty.webapps",file.getParentFile().getCanonicalPath());
                 
diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java
index 836d214..b076eff 100644
--- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java
+++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java
@@ -25,6 +25,7 @@
 import org.eclipse.jetty.deploy.App;
 import org.eclipse.jetty.deploy.ConfigurationManager;
 import org.eclipse.jetty.deploy.util.FileID;
+import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.util.URIUtil;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
@@ -33,8 +34,8 @@
 import org.eclipse.jetty.webapp.WebAppContext;
 import org.eclipse.jetty.xml.XmlConfiguration;
 
-/* ------------------------------------------------------------ */
-/** The webapps directory scanning provider.
+/** 
+ * The webapps directory scanning provider.
  * <p>
  * This provider scans one or more directories (typically "webapps") for contexts to
  * deploy, which may be:<ul>
@@ -213,9 +214,6 @@
     }  
     
     /* ------------------------------------------------------------ */
-    /**
-     *
-     */
     @ManagedAttribute("configuration classes for webapps to be processed through")
     public String[] getConfigurationClasses()
     {
@@ -247,6 +245,28 @@
     }
 
     /* ------------------------------------------------------------ */
+    protected void initializeWebAppContextDefaults(WebAppContext webapp)
+    {
+        if (_defaultsDescriptor != null)
+            webapp.setDefaultsDescriptor(_defaultsDescriptor);
+        webapp.setExtractWAR(_extractWars);
+        webapp.setParentLoaderPriority(_parentLoaderPriority);
+        if (_configurationClasses != null)
+            webapp.setConfigurationClasses(_configurationClasses);
+
+        if (_tempDirectory != null)
+        {
+            /* Since the Temp Dir is really a context base temp directory,
+             * Lets set the Temp Directory in a way similar to how WebInfConfiguration does it,
+             * instead of setting the WebAppContext.setTempDirectory(File).  
+             * If we used .setTempDirectory(File) all webapps will wind up in the
+             * same temp / work directory, overwriting each others work.
+             */
+            webapp.setAttribute(WebAppContext.BASETEMPDIR, _tempDirectory);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
     @Override
     public ContextHandler createContextHandler(final App app) throws Exception
     {
@@ -259,7 +279,7 @@
 
         if (resource.exists() && FileID.isXmlFile(file))
         {
-            XmlConfiguration xmlc = new XmlConfiguration(resource.getURL())
+            XmlConfiguration xmlc = new XmlConfiguration(resource.getURI().toURL())
             {
                 @Override
                 public void initializeDefaults(Object context)
@@ -269,9 +289,7 @@
                     if (context instanceof WebAppContext)
                     {
                         WebAppContext webapp = (WebAppContext)context;
-                        webapp.setParentLoaderPriority(_parentLoaderPriority);
-                        if (_defaultsDescriptor != null)
-                            webapp.setDefaultsDescriptor(_defaultsDescriptor);
+                        initializeWebAppContextDefaults(webapp);
                     }
                 }
             };
@@ -329,31 +347,10 @@
             context = "/" + context;
         }
 
-
         webAppContext.setContextPath(context);
         webAppContext.setWar(file.getAbsolutePath());
-        if (_defaultsDescriptor != null)
-        {
-            webAppContext.setDefaultsDescriptor(_defaultsDescriptor);
-        }
-        webAppContext.setExtractWAR(_extractWars);
-        webAppContext.setParentLoaderPriority(_parentLoaderPriority);
-        if (_configurationClasses != null)
-        {
-            webAppContext.setConfigurationClasses(_configurationClasses);
-        }
+        initializeWebAppContextDefaults(webAppContext);
 
-        if (_tempDirectory != null)
-        {
-            /* Since the Temp Dir is really a context base temp directory,
-             * Lets set the Temp Directory in a way similar to how WebInfConfiguration does it,
-             * instead of setting the
-             * WebAppContext.setTempDirectory(File).  
-             * If we used .setTempDirectory(File) all webapps will wind up in the
-             * same temp / work directory, overwriting each others work.
-             */
-            webAppContext.setAttribute(WebAppContext.BASETEMPDIR, _tempDirectory);
-        }
         return webAppContext;
     }
     
diff --git a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AppLifeCycleTest.java b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AppLifeCycleTest.java
index 1baf750..a39ab36 100644
--- a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AppLifeCycleTest.java
+++ b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AppLifeCycleTest.java
@@ -36,10 +36,10 @@
  */
 public class AppLifeCycleTest
 {
-        @Rule
-        public TestingDir testdir = new TestingDir();
+    @Rule
+    public TestingDir testdir = new TestingDir();
 
-        private void assertNoPath(String from, String to)
+    private void assertNoPath(String from, String to)
     {
         assertPath(from,to,new ArrayList<String>());
     }
@@ -168,7 +168,7 @@
      * Request multiple lifecycle paths with a single lifecycle instance. Just to ensure that there is no state
      * maintained between {@link AppLifeCycle#getPath(Node, Node)} requests.
      * 
-     * @throws IOException
+     * @throws IOException on test failure
      */
     @Test
     public void testFindPathMultiple() throws IOException
@@ -176,7 +176,7 @@
         AppLifeCycle lifecycle = new AppLifeCycle();
         List<String> expected = new ArrayList<String>();
 
-        File outputDir = testdir.getEmptyDir();
+        File outputDir = testdir.getEmptyPathDir().toFile();
 
         // Modify graph to add new 'staging' -> 'staged' between 'deployed' and 'started'
         GraphOutputDot.write(lifecycle,new File(outputDir,"multiple-1.dot")); // before change
diff --git a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBindingTest.java b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBindingTest.java
index 10aa4d7..f95b0a8 100644
--- a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBindingTest.java
+++ b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBindingTest.java
@@ -26,6 +26,7 @@
 import java.io.File;
 import java.util.List;
 
+import org.eclipse.jetty.deploy.providers.ScanningAppProvider;
 import org.eclipse.jetty.deploy.test.XmlConfiguredJetty;
 import org.eclipse.jetty.toolchain.test.IO;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
diff --git a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ScanningAppProviderRuntimeUpdatesTest.java b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ScanningAppProviderRuntimeUpdatesTest.java
index edce918..87bebf9 100644
--- a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ScanningAppProviderRuntimeUpdatesTest.java
+++ b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ScanningAppProviderRuntimeUpdatesTest.java
@@ -116,6 +116,7 @@
 
     /**
      * Simple webapp deployment after startup of server.
+     * @throws IOException on test failure
      */
     @Test
     public void testAfterStartupContext() throws IOException
@@ -131,6 +132,7 @@
 
     /**
      * Simple webapp deployment after startup of server, and then removal of the webapp.
+     * @throws IOException on test failure
      */
     @Test
     public void testAfterStartupThenRemoveContext() throws IOException
@@ -154,6 +156,7 @@
 
     /**
      * Simple webapp deployment after startup of server, and then removal of the webapp.
+     * @throws Exception on test failure
      */
     @Test
     public void testAfterStartupThenUpdateContext() throws Exception
diff --git a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/WebAppProviderTest.java b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/WebAppProviderTest.java
index f9f8622..cee2c6a 100644
--- a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/WebAppProviderTest.java
+++ b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/WebAppProviderTest.java
@@ -18,23 +18,33 @@
 
 package org.eclipse.jetty.deploy.providers;
 
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
 import java.io.File;
+import java.nio.file.FileSystemException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Arrays;
 
 import org.eclipse.jetty.deploy.test.XmlConfiguredJetty;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.toolchain.test.TestingDir;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
+@Ignore("See issue #1200")
 public class WebAppProviderTest
 {
     @Rule
     public TestingDir testdir = new TestingDir();
     private static XmlConfiguredJetty jetty;
-
+    private boolean symlinkSupported = false;
+    
     @Before
     public void setupEnvironment() throws Exception
     {
@@ -45,6 +55,20 @@
 
         // Setup initial context
         jetty.copyWebapp("foo-webapp-1.war","foo.war");
+        
+        // Make symlink
+        Path pathWar3 = MavenTestingUtils.getTestResourcePathFile("webapps/foo-webapp-3.war");
+        Path pathBar = jetty.getJettyDir("webapps/bar.war").toPath();
+        try
+        {
+            Files.createSymbolicLink(pathBar, pathWar3);
+            symlinkSupported = true;
+        } catch (UnsupportedOperationException | FileSystemException e)
+        {
+            // if unable to create symlink, no point testing that feature
+            // this is the path that Microsoft Windows takes.
+            symlinkSupported = false;
+        }
 
         // Should not throw an Exception
         jetty.load();
@@ -64,18 +88,34 @@
     public void testStartupContext()
     {
         // Check Server for Handlers
-        jetty.assertWebAppContextsExists("/foo");
+        jetty.assertWebAppContextsExists("/bar", "/foo");
 
         File workDir = jetty.getJettyDir("workish");
 
-        System.err.println("workDir="+workDir);
-
         // Test for regressions
         assertDirNotExists("root of work directory",workDir,"webinf");
         assertDirNotExists("root of work directory",workDir,"jsp");
 
         // Test for correct behaviour
-        Assert.assertTrue("Should have generated directory in work directory: " + workDir,hasJettyGeneratedPath(workDir,"foo.war"));
+        assertTrue("Should have generated directory in work directory: " + workDir,hasJettyGeneratedPath(workDir,"foo.war"));
+    }
+    
+    @Test
+    public void testStartupSymlinkContext()
+    {
+        assumeTrue(symlinkSupported);
+        
+        // Check for path
+        File barLink = jetty.getJettyDir("webapps/bar.war");
+        assertTrue("bar.war link exists: " + barLink.toString(), barLink.exists());
+        assertTrue("bar.war link isFile: " + barLink.toString(), barLink.isFile());
+        
+        // Check Server for expected Handlers
+        jetty.assertWebAppContextsExists("/bar", "/foo");
+        
+        // Test for expected work/temp directory behaviour
+        File workDir = jetty.getJettyDir("workish");
+        assertTrue("Should have generated directory in work directory: " + workDir,hasJettyGeneratedPath(workDir,"bar.war"));
     }
 
     private static boolean hasJettyGeneratedPath(File basedir, String expectedWarFilename)
diff --git a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java
index 95b4bef..6f9b7ce 100644
--- a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java
+++ b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java
@@ -70,7 +70,7 @@
         _xmlConfigurations = new ArrayList<>();
         Properties properties = new Properties();
 
-        String jettyHomeBase = testdir.getDir().getAbsolutePath();
+        String jettyHomeBase = testdir.getPath().toString();
         // Ensure we have a new (pristene) directory to work with.
         int idx = 0;
         _jettyHome = new File(jettyHomeBase + "#" + idx);
@@ -116,7 +116,7 @@
         System.setProperty("java.io.tmpdir",tmpDir.getAbsolutePath());
         properties.setProperty("jetty.home",_jettyHome.getAbsolutePath());
         System.setProperty("jetty.home",_jettyHome.getAbsolutePath());
-        properties.setProperty("test.basedir",MavenTestingUtils.getBasedir().getAbsolutePath());
+        properties.setProperty("test.basedir",MavenTestingUtils.getBaseDir().getAbsolutePath());
         properties.setProperty("test.resourcesdir",MavenTestingUtils.getTestResourcesDir().getAbsolutePath());
         properties.setProperty("test.webapps",webappsDir.getAbsolutePath());
         properties.setProperty("test.targetdir",MavenTestingUtils.getTargetDir().getAbsolutePath());
diff --git a/jetty-deploy/src/test/resources/binding-test-contexts-1.xml b/jetty-deploy/src/test/resources/binding-test-contexts-1.xml
index 3c6a950..d49cf70 100644
--- a/jetty-deploy/src/test/resources/binding-test-contexts-1.xml
+++ b/jetty-deploy/src/test/resources/binding-test-contexts-1.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
diff --git a/jetty-deploy/src/test/resources/etc/webdefault.xml b/jetty-deploy/src/test/resources/etc/webdefault.xml
index 13a96e9..6a58d98 100644
--- a/jetty-deploy/src/test/resources/etc/webdefault.xml
+++ b/jetty-deploy/src/test/resources/etc/webdefault.xml
@@ -1,113 +1,142 @@
 <?xml version="1.0" encoding="UTF-8"?>
-
-<!-- ===================================================================== -->
-<!-- This file contains the default descriptor for web applications.       -->
-<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
-<!-- The intent of this descriptor is to include jetty specific or common  -->
-<!-- configuration for all webapps.   If a context has a webdefault.xml    -->
-<!-- descriptor, it is applied before the contexts own web.xml file        -->
-<!--                                                                       -->
-<!-- A context may be assigned a default descriptor by:                    -->
-<!--  + Calling WebApplicationContext.setDefaultsDescriptor                -->
-<!--  + Passed an arg to addWebApplications                                -->
-<!--                                                                       -->
-<!-- This file is used both as the resource within the jetty.jar (which is -->
-<!-- used as the default if no explicit defaults descriptor is set) and it -->
-<!-- is copied to the etc directory of the Jetty distro and explicitly     -->
-<!-- by the jetty.xml file.                                                -->
-<!--                                                                       -->
-<!-- ===================================================================== -->
 <web-app 
-   xmlns="http://java.sun.com/xml/ns/javaee" 
+   xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
-   metadata-complete="true"
-   version="2.5"> 
+   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+   metadata-complete="false"
+   version="3.1"> 
+
+  <!-- ===================================================================== -->
+  <!-- This file contains the default descriptor for web applications.       -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+  <!-- The intent of this descriptor is to include jetty specific or common  -->
+  <!-- configuration for all webapps.   If a context has a webdefault.xml    -->
+  <!-- descriptor, it is applied before the context's own web.xml file       -->
+  <!--                                                                       -->
+  <!-- A context may be assigned a default descriptor by calling             -->
+  <!-- WebAppContext.setDefaultsDescriptor(String).                          -->
+  <!--                                                                       -->
+  <!-- This file is present in the jetty-webapp.jar, and is used as the      -->
+  <!-- defaults descriptor if no other is explicitly set on a context.       -->
+  <!--                                                                       -->
+  <!-- A copy of this file is also placed into the $JETTY_HOME/etc dir of    -->
+  <!-- the  distribution, and is referenced by some of the other xml files,  -->
+  <!-- eg the jetty-deploy.xml file.                                         -->
+  <!-- ===================================================================== -->
 
   <description>
     Default web.xml file.  
     This file is applied to a Web application before it's own WEB_INF/web.xml file
   </description>
 
+  <!-- ==================================================================== -->
+  <!-- Removes static references to beans from javax.el.BeanELResolver to   -->
+  <!-- ensure webapp classloader can be released on undeploy                -->
+  <!-- ==================================================================== -->
+  <listener>
+   <listener-class>org.eclipse.jetty.servlet.listener.ELContextCleaner</listener-class>
+  </listener>
+  
+  <!-- ==================================================================== -->
+  <!-- Removes static cache of Methods from java.beans.Introspector to      -->
+  <!-- ensure webapp classloader can be released on undeploy                -->
+  <!-- ==================================================================== -->  
+  <listener>
+   <listener-class>org.eclipse.jetty.servlet.listener.IntrospectorCleaner</listener-class>
+  </listener>
+  
 
   <!-- ==================================================================== -->
   <!-- Context params to control Session Cookies                            -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
-  <!-- UNCOMMENT TO ACTIVATE
-  <context-param>
-    <param-name>org.eclipse.jetty.servlet.SessionDomain</param-name>
-    <param-value>127.0.0.1</param-value>
-  </context-param>
-
-  <context-param>
-    <param-name>org.eclipse.jetty.servlet.SessionPath</param-name>
-    <param-value>/</param-value>
-  </context-param>
-
-  <context-param>
-    <param-name>org.eclipse.jetty.servlet.MaxAge</param-name>
-    <param-value>-1</param-value>
-  </context-param>
+  <!--
+    UNCOMMENT TO ACTIVATE 
+    <context-param> 
+      <param-name>org.eclipse.jetty.servlet.SessionDomain</param-name> 
+      <param-value>127.0.0.1</param-value> 
+    </context-param> 
+    <context-param>
+      <param-name>org.eclipse.jetty.servlet.SessionPath</param-name>
+      <param-value>/</param-value>
+    </context-param>
+    <context-param>
+      <param-name>org.eclipse.jetty.servlet.MaxAge</param-name>
+      <param-value>-1</param-value>
+    </context-param>
   -->
 
-
   <!-- ==================================================================== -->
   <!-- The default servlet.                                                 -->
   <!-- This servlet, normally mapped to /, provides the handling for static -->
   <!-- content, OPTIONS and TRACE methods for the context.                  -->
   <!-- The following initParameters are supported:                          -->
-  <!--                                                                      -->
-  <!--   acceptRanges     If true, range requests and responses are         -->
-  <!--                    supported                                         -->
-  <!--                                                                      -->
-  <!--   dirAllowed       If true, directory listings are returned if no    -->
-  <!--                    welcome file is found. Else 403 Forbidden.        -->
-  <!--                                                                      -->
-  <!--   welcomeServlets  If true, attempt to dispatch to welcome files     -->
-  <!--                    that are servlets, if no matching static          -->
-  <!--                    resources can be found.                           -->
-  <!--                                                                      -->
-  <!--   redirectWelcome  If true, redirect welcome file requests           -->
-  <!--                    else use request dispatcher forwards              -->
-  <!--                                                                      -->
-  <!--   gzip             If set to true, then static content will be served--> 
-  <!--                    as gzip content encoded if a matching resource is -->
-  <!--                    found ending with ".gz"                           -->
-  <!--                                                                      -->
-  <!--   resoureBase      Can be set to replace the context resource base   -->
-  <!--                                                                      -->
-  <!--   relativeResourceBase                                               -->
-  <!--                    Set with a pathname relative to the base of the   -->
-  <!--                    servlet context root. Useful for only serving     -->
-  <!--                    static content from only specific subdirectories. -->
-  <!--                                                                      -->
-  <!--   useFileMappedBuffer                                                -->
-  <!--                    If set to true (the default), a  memory mapped    -->
-  <!--                    file buffer will be used to serve static content  -->
-  <!--                    when using an NIO connector. Setting this value   -->
-  <!--                    to false means that a direct buffer will be used  -->
-  <!--                    instead. If you are having trouble with Windows   -->
-  <!--                    file locking, set this to false.                  -->
-  <!--                                                                      -->
-  <!--  cacheControl      If set, all static content will have this value   -->
-  <!--                    set as the cache-control header.                  -->
-  <!--                                                                      -->
-  <!--  maxCacheSize      Maximum size of the static resource cache         -->
-  <!--                                                                      -->
-  <!--  maxCachedFileSize Maximum size of any single file in the cache      -->
-  <!--                                                                      -->
-  <!--  maxCachedFiles    Maximum number of files in the cache              -->
-  <!--                                                                      -->
-  <!--  cacheType         "nio", "bio" or "both" to determine the type(s)   -->
-  <!--                    of resource cache. A bio cached buffer may be used-->
-  <!--                    by nio but is not as efficient as a nio buffer.   -->
-  <!--                    An nio cached buffer may not be used by bio.      -->
-  <!--                                                                      -->
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+  <!--  
+ *  acceptRanges      If true, range requests and responses are
+ *                    supported
+ *
+ *  dirAllowed        If true, directory listings are returned if no
+ *                    welcome file is found. Else 403 Forbidden.
+ *
+ *  welcomeServlets   If true, attempt to dispatch to welcome files
+ *                    that are servlets, but only after no matching static
+ *                    resources could be found. If false, then a welcome
+ *                    file must exist on disk. If "exact", then exact
+ *                    servlet matches are supported without an existing file.
+ *                    Default is true.
+ *
+ *                    This must be false if you want directory listings,
+ *                    but have index.jsp in your welcome file list.
+ *
+ *  redirectWelcome   If true, welcome files are redirected rather than
+ *                    forwarded to.
+ *
+ *  gzip              If set to true, then static content will be served as
+ *                    gzip content encoded if a matching resource is
+ *                    found ending with ".gz"
+ *
+ *  resourceBase      Set to replace the context resource base
+ *
+ *  resourceCache     If set, this is a context attribute name, which the servlet
+ *                    will use to look for a shared ResourceCache instance.
+ *
+ *  relativeResourceBase
+ *                    Set with a pathname relative to the base of the
+ *                    servlet context root. Useful for only serving static content out
+ *                    of only specific subdirectories.
+ *
+ *  pathInfoOnly      If true, only the path info will be applied to the resourceBase
+ *
+ *  stylesheet        Set with the location of an optional stylesheet that will be used
+ *                    to decorate the directory listing html.
+ *
+ *  aliases           If True, aliases of resources are allowed (eg. symbolic
+ *                    links and caps variations). May bypass security constraints.
+ *                    
+ *  etags             If True, weak etags will be generated and handled.
+ *
+ *  maxCacheSize      The maximum total size of the cache or 0 for no cache.
+ *  maxCachedFileSize The maximum size of a file to cache
+ *  maxCachedFiles    The maximum number of files to cache
+ *
+ *  useFileMappedBuffer
+ *                    If set to true, it will use mapped file buffers to serve static content
+ *                    when using an NIO connector. Setting this value to false means that
+ *                    a direct buffer will be used instead of a mapped file buffer.
+ *                    This file sets the value to true.
+ *
+ *  cacheControl      If set, all static content will have this value set as the cache-control
+ *                    header.
+ *
+ -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <servlet>
     <servlet-name>default</servlet-name>
     <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
     <init-param>
+      <param-name>aliases</param-name>
+      <param-value>false</param-value>
+    </init-param>
+    <init-param>
       <param-name>acceptRanges</param-name>
       <param-value>true</param-value>
     </init-param>
@@ -117,8 +146,8 @@
     </init-param>
     <init-param>
       <param-name>welcomeServlets</param-name>
- 	    <param-value>false</param-value>
- 	  </init-param>
+      <param-value>false</param-value>
+    </init-param>
     <init-param>
       <param-name>redirectWelcome</param-name>
       <param-value>false</param-value>
@@ -129,24 +158,30 @@
     </init-param>
     <init-param>
       <param-name>maxCachedFileSize</param-name>
-      <param-value>10000000</param-value>
+      <param-value>200000000</param-value>
     </init-param>
     <init-param>
       <param-name>maxCachedFiles</param-name>
-      <param-value>1000</param-value>
-    </init-param>
-    <init-param>
-      <param-name>cacheType</param-name>
-      <param-value>both</param-value>
+      <param-value>2048</param-value>
     </init-param>
     <init-param>
       <param-name>gzip</param-name>
       <param-value>true</param-value>
     </init-param>
     <init-param>
+      <param-name>etags</param-name>
+      <param-value>false</param-value>
+    </init-param>
+    <init-param>
       <param-name>useFileMappedBuffer</param-name>
       <param-value>true</param-value>
-    </init-param>  
+    </init-param>
+    <!--
+    <init-param>
+      <param-name>resourceCache</param-name>
+      <param-value>resourceCache</param-value>
+    </init-param>
+    -->
     <!--
     <init-param>
       <param-name>cacheControl</param-name>
@@ -154,20 +189,23 @@
     </init-param>
     -->
     <load-on-startup>0</load-on-startup>
-  </servlet> 
+  </servlet>
 
-  <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
-  
+  <servlet-mapping>
+    <servlet-name>default</servlet-name>
+    <url-pattern>/</url-pattern>
+  </servlet-mapping>
+
 
   <!-- ==================================================================== -->
   <!-- JSP Servlet                                                          -->
-  <!-- This is the jasper JSP servlet from the jakarta project              -->
+  <!-- This is the jasper JSP servlet.                                      -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <!-- The JSP page compiler and execution servlet, which is the mechanism  -->
-  <!-- used by Glassfish to support JSP pages.  Traditionally, this servlet -->
-  <!-- is mapped to URL patterh "*.jsp".  This servlet supports the         -->
-  <!-- following initialization parameters (default values are in square    -->
-  <!-- brackets):                                                           -->
+  <!-- used by the jsp container to support JSP pages.  Traditionally,      -->
+  <!-- this servlet is mapped to URL pattern "*.jsp".  This servlet         -->
+  <!-- supports the following initialization parameters (default values     -->
+  <!-- are in square brackets):                                             -->
   <!--                                                                      -->
   <!--   checkInterval       If development is false and reloading is true, -->
   <!--                       background compiles are enabled. checkInterval -->
@@ -175,7 +213,7 @@
   <!--                       if a JSP page needs to be recompiled. [300]    -->
   <!--                                                                      -->
   <!--   compiler            Which compiler Ant should use to compile JSP   -->
-  <!--                       pages.  See the Ant documenation for more      -->
+  <!--                       pages.  See the Ant documentation for more     -->
   <!--                       information. [javac]                           -->
   <!--                                                                      -->
   <!--   classdebuginfo      Should the class file be compiled with         -->
@@ -236,28 +274,29 @@
   <!--   xpoweredBy          Determines whether X-Powered-By response       -->
   <!--                       header is added by generated servlet  [false]  -->
   <!--                                                                      -->
-  <!-- If you wish to use Jikes to compile JSP pages:                       -->
-  <!--   Set the init parameter "compiler" to "jikes".  Define              -->
-  <!--   the property "-Dbuild.compiler.emacs=true" when starting Jetty     -->
-  <!--   to cause Jikes to emit error messages in a format compatible with  -->
-  <!--   Jasper.                                                            -->
-  <!--   If you get an error reporting that jikes can't use UTF-8 encoding, -->
-  <!--   try setting the init parameter "javaEncoding" to "ISO-8859-1".     -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <servlet id="jsp">
     <servlet-name>jsp</servlet-name>
-    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+    <servlet-class>org.eclipse.jetty.jsp.JettyJspServlet</servlet-class>
     <init-param>
-        <param-name>logVerbosityLevel</param-name>
-        <param-value>DEBUG</param-value>
+      <param-name>logVerbosityLevel</param-name>
+      <param-value>DEBUG</param-value>
     </init-param>
     <init-param>
-        <param-name>fork</param-name>
-        <param-value>false</param-value>
+      <param-name>fork</param-name>
+      <param-value>false</param-value>
     </init-param>
     <init-param>
-        <param-name>xpoweredBy</param-name>
-        <param-value>false</param-value>
+      <param-name>xpoweredBy</param-name>
+      <param-value>false</param-value>
+    </init-param>
+    <init-param>
+      <param-name>compilerTargetVM</param-name>
+      <param-value>1.7</param-value>
+    </init-param>
+    <init-param>
+      <param-name>compilerSourceVM</param-name>
+      <param-value>1.7</param-value>
     </init-param>
     <!--  
     <init-param>
@@ -268,62 +307,22 @@
     <load-on-startup>0</load-on-startup>
   </servlet>
 
-  <servlet-mapping> 
-    <servlet-name>jsp</servlet-name> 
-    <url-pattern>*.jsp</url-pattern> 
+  <servlet-mapping>
+    <servlet-name>jsp</servlet-name>
+    <url-pattern>*.jsp</url-pattern>
     <url-pattern>*.jspf</url-pattern>
     <url-pattern>*.jspx</url-pattern>
     <url-pattern>*.xsp</url-pattern>
-    <url-pattern>*.JSP</url-pattern> 
+    <url-pattern>*.JSP</url-pattern>
     <url-pattern>*.JSPF</url-pattern>
     <url-pattern>*.JSPX</url-pattern>
     <url-pattern>*.XSP</url-pattern>
   </servlet-mapping>
-  
+
+
   <!-- ==================================================================== -->
-  <!-- Dynamic Servlet Invoker.                                             -->
-  <!-- This servlet invokes anonymous servlets that have not been defined   -->
-  <!-- in the web.xml or by other means. The first element of the pathInfo  -->
-  <!-- of a request passed to the envoker is treated as a servlet name for  -->
-  <!-- an existing servlet, or as a class name of a new servlet.            -->
-  <!-- This servlet is normally mapped to /servlet/*                        -->
-  <!-- This servlet support the following initParams:                       -->
-  <!--                                                                      -->
-  <!--  nonContextServlets       If false, the invoker can only load        -->
-  <!--                           servlets from the contexts classloader.    -->
-  <!--                           This is false by default and setting this  -->
-  <!--                           to true may have security implications.    -->
-  <!--                                                                      -->
-  <!--  verbose                  If true, log dynamic loads                 -->
-  <!--                                                                      -->
-  <!--  *                        All other parameters are copied to the     -->
-  <!--                           each dynamic servlet as init parameters    -->
+  <!-- Default session configuration                                        -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
-  <!-- Uncomment for dynamic invocation
-  <servlet>
-    <servlet-name>invoker</servlet-name>
-    <servlet-class>org.eclipse.jetty.servlet.Invoker</servlet-class>
-    <init-param>
-      <param-name>verbose</param-name>
-      <param-value>false</param-value>
-    </init-param>
-    <init-param>
-      <param-name>nonContextServlets</param-name>
-      <param-value>false</param-value>
-    </init-param>
-    <init-param>
-      <param-name>dynamicParam</param-name>
-      <param-value>anyValue</param-value>
-    </init-param>
-    <load-on-startup>0</load-on-startup>
-  </servlet>
-
-  <servlet-mapping> <servlet-name>invoker</servlet-name> <url-pattern>/servlet/*</url-pattern> </servlet-mapping>
-  -->
-
-
-
-  <!-- ==================================================================== -->
   <session-config>
     <session-timeout>30</session-timeout>
   </session-config>
@@ -342,6 +341,8 @@
   -->
 
   <!-- ==================================================================== -->
+  <!-- Default welcome files                                                -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <welcome-file-list>
     <welcome-file>index.html</welcome-file>
     <welcome-file>index.htm</welcome-file>
@@ -349,48 +350,170 @@
   </welcome-file-list>
 
   <!-- ==================================================================== -->
+  <!-- Default locale encodings                                             -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <locale-encoding-mapping-list>
-    <locale-encoding-mapping><locale>ar</locale><encoding>ISO-8859-6</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>be</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>bg</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ca</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>cs</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>da</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>de</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>el</locale><encoding>ISO-8859-7</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>en</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>es</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>et</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>fi</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>fr</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>hr</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>hu</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>is</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>it</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>iw</locale><encoding>ISO-8859-8</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ja</locale><encoding>Shift_JIS</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ko</locale><encoding>EUC-KR</encoding></locale-encoding-mapping>     
-    <locale-encoding-mapping><locale>lt</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>lv</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>mk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>nl</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>no</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>pl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>pt</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ro</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ru</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sh</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sk</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sq</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sr</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sv</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>tr</locale><encoding>ISO-8859-9</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>uk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>zh</locale><encoding>GB2312</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>zh_TW</locale><encoding>Big5</encoding></locale-encoding-mapping>   
+    <locale-encoding-mapping>
+      <locale>ar</locale>
+      <encoding>ISO-8859-6</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>be</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>bg</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ca</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>cs</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>da</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>de</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>el</locale>
+      <encoding>ISO-8859-7</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>en</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>es</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>et</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>fi</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>fr</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>hr</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>hu</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>is</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>it</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>iw</locale>
+      <encoding>ISO-8859-8</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ja</locale>
+      <encoding>Shift_JIS</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ko</locale>
+      <encoding>EUC-KR</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>lt</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>lv</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>mk</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>nl</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>no</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>pl</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>pt</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ro</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ru</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sh</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sk</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sl</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sq</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sr</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sv</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>tr</locale>
+      <encoding>ISO-8859-9</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>uk</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>zh</locale>
+      <encoding>GB2312</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>zh_TW</locale>
+      <encoding>Big5</encoding>
+    </locale-encoding-mapping>
   </locale-encoding-mapping-list>
-  
+
+  <!-- ==================================================================== -->
+  <!-- Disable TRACE method with security constraint                        -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <security-constraint>
     <web-resource-collection>
       <web-resource-name>Disable TRACE</web-resource-name>
@@ -399,6 +522,13 @@
     </web-resource-collection>
     <auth-constraint/>
   </security-constraint>
-  
+  <security-constraint>
+    <web-resource-collection>
+      <web-resource-name>Enable everything but TRACE</web-resource-name>
+      <url-pattern>/</url-pattern>
+      <http-method-omission>TRACE</http-method-omission>
+    </web-resource-collection>
+  </security-constraint>
+
 </web-app>
 
diff --git a/jetty-deploy/src/test/resources/jetty-deploy-wars.xml b/jetty-deploy/src/test/resources/jetty-deploy-wars.xml
index 9933cb6..1760a67 100644
--- a/jetty-deploy/src/test/resources/jetty-deploy-wars.xml
+++ b/jetty-deploy/src/test/resources/jetty-deploy-wars.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
diff --git a/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml b/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml
index 8d55d22..b16fbd7 100644
--- a/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml
+++ b/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
   <Call name="addBean">
diff --git a/jetty-deploy/src/test/resources/jetty-http.xml b/jetty-deploy/src/test/resources/jetty-http.xml
index 42b889b..7fa6def 100644
--- a/jetty-deploy/src/test/resources/jetty-http.xml
+++ b/jetty-deploy/src/test/resources/jetty-http.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- ============================================================= -->
 <!-- Configure the Jetty Server instance with an ID "Server"       -->
diff --git a/jetty-deploy/src/test/resources/jetty.xml b/jetty-deploy/src/test/resources/jetty.xml
index 25332f6..a08e270 100644
--- a/jetty-deploy/src/test/resources/jetty.xml
+++ b/jetty-deploy/src/test/resources/jetty.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Documentation of this file format can be found at:              -->
@@ -46,9 +46,9 @@
     <Arg name="threadpool"><New id="threadpool" class="org.eclipse.jetty.util.thread.QueuedThreadPool"/></Arg>
     -->
     <Get name="ThreadPool">
-      <Set name="minThreads" type="int"><Property name="threads.min" default="10"/></Set>
-      <Set name="maxThreads" type="int"><Property name="threads.max" default="200"/></Set>
-      <Set name="idleTimeout" type="int"><Property name="threads.timeout" default="60000"/></Set>
+      <Set name="minThreads" type="int"><Property name="jetty.threadPool.minThreads" default="10"/></Set>
+      <Set name="maxThreads" type="int"><Property name="jetty.threadPool.maxThreads" default="200"/></Set>
+      <Set name="idleTimeout" type="int"><Property name="jetty.threadPool.idleTimeout" default="60000"/></Set>
       <Set name="detailedDump">false</Set>
     </Get>
 
@@ -64,21 +64,21 @@
     <!-- =========================================================== -->
     <!-- Http Configuration.                                         -->
     <!-- This is a common configuration instance used by all         -->
-    <!-- connectors that can carry HTTP semantics (HTTP, HTTPS, SPDY)-->
+    <!-- connectors that can carry HTTP semantics (HTTP, HTTPS, etc.)-->
     <!-- It configures the non wire protocol aspects of the HTTP     -->
     <!-- semantic.                                                   -->
     <!--                                                             -->
     <!-- This configuration is only defined here and is used by      -->
-    <!-- reference from the jetty-http.xml, jetty-https.xml and      -->
-    <!-- jetty-spdy.xml configuration files which instantiate the    -->
-    <!-- connectors.                                                 -->
+    <!-- reference from other XML files such as jetty-http.xml,      -->
+    <!-- jetty-https.xml and other configuration files which         -->
+    <!-- instantiate the connectors.                                 -->
     <!--                                                             -->
     <!-- Consult the javadoc of o.e.j.server.HttpConfiguration       -->
     <!-- for all configuration that may be set here.                 -->
     <!-- =========================================================== -->
     <New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
       <Set name="secureScheme">https</Set>
-      <Set name="securePort" type="java.lang.Integer"><Property name="jetty.secure.port" default="8443" /></Set>
+      <Set name="securePort" type="java.lang.Integer"><Property name="jetty.httpConfig.securePort" default="8443" /></Set>
       <Set name="outputBufferSize">32768</Set>
       <Set name="requestHeaderSize">8192</Set>
       <Set name="responseHeaderSize">8192</Set>
@@ -125,7 +125,7 @@
     <!-- =========================================================== -->
     <Set name="stopAtShutdown">true</Set>
     <Set name="stopTimeout">5000</Set>
-    <Set name="dumpAfterStart"><Property name="jetty.dump.start" default="false"/></Set>
-    <Set name="dumpBeforeStop"><Property name="jetty.dump.stop" default="false"/></Set>
+    <Set name="dumpAfterStart"><Property name="jetty.server.dumpAfterStart" default="false"/></Set>
+    <Set name="dumpBeforeStop"><Property name="jetty.server.dumpBeforeStop" default="false"/></Set>
 
 </Configure>
diff --git a/jetty-deploy/src/test/resources/webapps/foo.xml b/jetty-deploy/src/test/resources/webapps/foo.xml
index e7c6e9d..f0a9611 100644
--- a/jetty-deploy/src/test/resources/webapps/foo.xml
+++ b/jetty-deploy/src/test/resources/webapps/foo.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
     <Set name="contextPath">/foo</Set>
     <Set name="war">
diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml
index c9edfcc..8716142 100644
--- a/jetty-distribution/pom.xml
+++ b/jetty-distribution/pom.xml
@@ -1,18 +1,21 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
+  <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-distribution</artifactId>
   <name>Jetty :: Distribution Assemblies</name>
-  <url>http://www.eclipse.org/jetty</url>
   <packaging>pom</packaging>
+
   <properties>
     <assembly-directory>${basedir}/target/distribution</assembly-directory>
-    <jetty-setuid-version>1.0.1</jetty-setuid-version>
+    <jetty-setuid-version>1.0.3</jetty-setuid-version>
   </properties>
+
   <build>
     <plugins>
       <plugin>
@@ -53,6 +56,18 @@
               </tasks>
             </configuration>
           </execution>
+          <execution>
+            <id>removeKeystore</id>
+            <phase>process-resources</phase>
+            <goals>
+              <goal>run</goal>
+            </goals>
+            <configuration>
+              <tasks>
+                <delete file="${assembly-directory}/etc/keystore" />
+              </tasks>
+            </configuration>
+          </execution>
         </executions>
       </plugin>
       <plugin>
@@ -204,30 +219,7 @@
               </artifactItems>
             </configuration>
           </execution>
-
           <execution>
-            <id>unpack-setuid-config</id>
-            <phase>process-resources</phase>
-            <goals>
-              <goal>unpack</goal>
-            </goals>
-            <configuration>
-              <artifactItems>
-                <artifactItem>
-                  <groupId>org.eclipse.jetty.toolchain.setuid</groupId>
-                  <artifactId>jetty-setuid-java</artifactId>
-                  <version>${jetty-setuid-version}</version>
-                  <classifier>config</classifier>
-                  <type>jar</type>
-                  <overWrite>true</overWrite>
-                  <outputDirectory>${assembly-directory}</outputDirectory>
-                </artifactItem>
-              </artifactItems>
-              <excludes>META-INF/**</excludes>
-            </configuration>
-          </execution>
-
-           <execution>
             <id>unpack-test-webapp-config</id>
             <phase>process-resources</phase>
             <goals>
@@ -314,7 +306,26 @@
               <excludes>META-INF/**</excludes>
             </configuration>
           </execution>
-
+          <execution>
+            <id>unpack-documentation</id>
+            <phase>process-resources</phase>
+            <goals>
+              <goal>unpack</goal>
+            </goals>
+            <configuration>
+              <artifactItems>
+                <artifactItem>
+                  <groupId>org.eclipse.jetty</groupId>
+                  <artifactId>jetty-documentation</artifactId>
+                  <version>${project.version}</version>
+                  <classifier>html</classifier>
+                  <type>zip</type>
+                  <overWrite>true</overWrite>
+                  <outputDirectory>${assembly-directory}/demo-base/webapps/doc</outputDirectory>
+                </artifactItem>
+              </artifactItems>
+            </configuration>
+          </execution>
           <execution>
             <id>copy-lib-deps</id>
             <phase>generate-resources</phase>
@@ -323,8 +334,8 @@
             </goals>
             <configuration>
               <includeGroupIds>org.eclipse.jetty</includeGroupIds>
-              <excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.spdy,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs</excludeGroupIds>
-              <excludeArtifactIds>jetty-all,jetty-jsp,apache-jsp,apache-jstl,jetty-start,jetty-monitor,jetty-spring</excludeArtifactIds>
+              <excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.http2,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs</excludeGroupIds>
+              <excludeArtifactIds>jetty-all,apache-jsp,apache-jstl,jetty-start,jetty-monitor,jetty-spring</excludeArtifactIds>
               <includeTypes>jar</includeTypes>
               <outputDirectory>${assembly-directory}/lib</outputDirectory>
             </configuration>
@@ -343,6 +354,19 @@
             </configuration>
           </execution>
           <execution>
+            <id>copy-lib-http2-deps</id>
+            <phase>generate-resources</phase>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <includeGroupIds>org.eclipse.jetty.http2</includeGroupIds>
+              <includeArtifactIds>http2-hpack,http2-common,http2-server</includeArtifactIds>
+              <includeTypes>jar</includeTypes>
+              <outputDirectory>${assembly-directory}/lib/http2</outputDirectory>
+            </configuration>
+          </execution>
+          <execution>
             <id>copy-lib-fcgi-deps</id>
             <phase>generate-resources</phase>
             <goals>
@@ -414,32 +438,6 @@
             </configuration>
           </execution>
           <execution>
-            <id>unpack-spdy</id>
-            <phase>process-resources</phase>
-            <goals>
-              <goal>unpack-dependencies</goal>
-            </goals>
-            <configuration>
-              <includeGroupIds>org.eclipse.jetty.spdy</includeGroupIds>
-              <classifier>config</classifier>
-              <failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact>
-              <excludes>META-INF/**</excludes>
-              <outputDirectory>${assembly-directory}</outputDirectory>
-            </configuration>
-          </execution>
-          <execution>
-            <id>copy-lib-spdy-deps</id>
-            <phase>process-resources</phase>
-            <goals>
-              <goal>copy-dependencies</goal>
-            </goals>
-            <configuration>
-              <includeGroupIds>org.eclipse.jetty.spdy</includeGroupIds>
-              <includeTypes>jar</includeTypes>
-              <outputDirectory>${assembly-directory}/lib/spdy</outputDirectory>
-            </configuration>
-          </execution>
-          <execution>
             <id>copy-annotations-deps</id>
             <phase>generate-resources</phase>
             <goals>
@@ -480,27 +478,14 @@
             </configuration>
           </execution>
           <execution>
-            <id>copy-glassfish-jsp-deps</id>
-            <phase>generate-resources</phase>
-            <goals>
-              <goal>copy-dependencies</goal>
-            </goals>
-            <configuration>
-              <includeGroupIds>org.eclipse.jetty.orbit,org.glassfish.web, org.glassfish, javax.el, javax.servlet.jsp, org.eclipse.jetty.toolchain, org.eclipse.jetty</includeGroupIds>
-              <includeArtifactIds>org.eclipse.jdt.core, javax.servlet.jsp-api, javax.servlet.jsp, jetty-jsp-jdt, javax.el-api, javax.el, jetty-jsp</includeArtifactIds>
-              <includeTypes>jar</includeTypes>
-              <outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
-            </configuration>
-          </execution>
-          <execution>
             <id>copy-apache-jsp-deps</id>
             <phase>generate-resources</phase>
             <goals>
               <goal>copy-dependencies</goal>
             </goals>
             <configuration>
-              <includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.toolchain,org.mortbay.jasper,org.eclipse.jetty.orbit</includeGroupIds>
-              <includeArtifactIds>apache-jsp,apache-el,org.eclipse.jdt.core</includeArtifactIds>
+              <includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.toolchain,org.mortbay.jasper,org.eclipse.jetty.orbit,org.eclipse.jdt.core.compiler</includeGroupIds>
+              <includeArtifactIds>apache-jsp,apache-el,ecj</includeArtifactIds>
               <includeTypes>jar</includeTypes>
               <prependGroupId>true</prependGroupId>
               <outputDirectory>${assembly-directory}/lib/apache-jsp</outputDirectory>
@@ -521,19 +506,6 @@
            </configuration>
           </execution>
           <execution>
-           <id>copy-jstl-impl</id>
-           <phase>generate-resources</phase>
-           <goals>
-             <goal>copy-dependencies</goal>
-           </goals>
-           <configuration>
-              <includeGroupIds>org.glassfish.web</includeGroupIds>
-              <includeArtifactIds>javax.servlet.jsp.jstl</includeArtifactIds>
-              <includeTypes>jar</includeTypes>
-              <outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
-           </configuration>
-          </execution>
-          <execution>
            <id>copy-apache-jstl-deps</id>
            <phase>generate-resources</phase>
            <goals>
@@ -588,7 +560,7 @@
               <arguments>
                 <argument>jetty.home=${assembly-directory}</argument>
                 <argument>jetty.base=${assembly-directory}</argument>
-                <argument>--add-to-start=server,deploy,websocket,ext,resources,jsp,jstl,http</argument>
+                <argument>--add-to-start=deploy,websocket,ext,resources,jsp,jstl,http</argument>
               </arguments>
             </configuration>
             <goals>
@@ -596,14 +568,28 @@
             </goals>
           </execution>
           <execution>
-            <id>setup demo-base</id>
+            <id>setup demo-base-ini</id>
             <phase>process-classes</phase>
             <configuration>
               <mainClass>org.eclipse.jetty.start.Main</mainClass>
               <arguments>
                 <argument>jetty.home=${assembly-directory}</argument>
                 <argument>jetty.base=${assembly-directory}/demo-base</argument>
-                <argument>--add-to-start=server,continuation,deploy,websocket,ext,resources,client,annotations,jndi,servlets</argument>
+                <argument>--add-to-start=continuation,deploy,websocket,ext,resources,client,annotations,jndi,servlets</argument>
+              </arguments>
+            </configuration>
+            <goals>
+              <goal>java</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>setup demo-base-startd</id>
+            <phase>process-classes</phase>
+            <configuration>
+              <mainClass>org.eclipse.jetty.start.Main</mainClass>
+              <arguments>
+                <argument>jetty.home=${assembly-directory}</argument>
+                <argument>jetty.base=${assembly-directory}/demo-base</argument>
                 <argument>--add-to-startd=jsp,jstl,http,https</argument>
               </arguments>
             </configuration>
@@ -650,6 +636,7 @@
       </plugin>
     </plugins>
   </build>
+
   <dependencies>
     <!-- Orbit Deps -->
     <dependency>
@@ -671,28 +658,6 @@
     </dependency>
 
     <dependency>
-      <groupId>org.glassfish.web</groupId>
-      <artifactId>javax.servlet.jsp.jstl</artifactId>
-    </dependency>
-
-    <dependency>
-      <groupId>org.glassfish.web</groupId>
-      <artifactId>javax.servlet.jsp</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.toolchain</groupId>
-      <artifactId>jetty-jsp-jdt</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>javax.servlet.jsp</groupId>
-      <artifactId>javax.servlet.jsp-api</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.glassfish</groupId>
-      <artifactId>javax.el</artifactId>
-    </dependency>
-
-    <dependency>
       <groupId>org.ow2.asm</groupId>
       <artifactId>asm</artifactId>
     </dependency>
@@ -761,11 +726,6 @@
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-jsp</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
       <artifactId>apache-jsp</artifactId>
       <version>${project.version}</version>
     </dependency>
@@ -812,8 +772,13 @@
     </dependency>
 -->
     <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-cdi</artifactId>
+      <groupId>org.eclipse.jetty.cdi</groupId>
+      <artifactId>cdi-servlet</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.cdi</groupId>
+      <artifactId>cdi-websocket</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
@@ -832,27 +797,11 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
-      <groupId>org.eclipse.jetty.spdy</groupId>
-      <artifactId>spdy-core</artifactId>
+      <groupId>org.eclipse.jetty.http2</groupId>
+      <artifactId>http2-server</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
-      <groupId>org.eclipse.jetty.spdy</groupId>
-      <artifactId>spdy-server</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.spdy</groupId>
-      <artifactId>spdy-http-server</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.spdy</groupId>
-      <artifactId>spdy-example-webapp</artifactId>
-      <version>${project.version}</version>
-      <type>war</type>
-    </dependency>
-    <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-alpn-server</artifactId>
       <version>${project.version}</version>
@@ -868,5 +817,49 @@
       <artifactId>jetty-jaspi</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-infinispan</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.gcloud</groupId>
+      <artifactId>jetty-gcloud-session-manager</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.gcloud</groupId>
+      <artifactId>jetty-gcloud-memcached-session-manager</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-nosql</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-documentation</artifactId>
+      <version>${project.version}</version>
+      <classifier>html</classifier>
+      <type>zip</type>
+    </dependency>
   </dependencies>
+
+  <profiles>
+    <profile>
+      <id>jdk9</id>
+      <activation>
+        <jdk>[1.9,)</jdk>
+      </activation>
+      <dependencies>
+        <dependency>
+          <groupId>org.eclipse.jetty</groupId>
+          <artifactId>jetty-alpn-java-server</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+      </dependencies>
+    </profile>
+  </profiles>
+
 </project>
diff --git a/jetty-distribution/src/main/assembly/jetty-assembly.xml b/jetty-distribution/src/main/assembly/jetty-assembly.xml
index c0198eb..7a3fe1c 100644
--- a/jetty-distribution/src/main/assembly/jetty-assembly.xml
+++ b/jetty-distribution/src/main/assembly/jetty-assembly.xml
@@ -14,6 +14,36 @@
       <excludes>
         <exclude>**/META-INF/**</exclude>
         <exclude>*-config.jar</exclude>
+        <!-- common OS detritus -->
+        <exclude>**/.DS_Store</exclude>
+        <exclude>**/Thumbs.db</exclude>
+        <exclude>**/desktop.ini</exclude>
+        <!-- common temp files -->
+        <exclude>**/*~</exclude>
+        <exclude>**/*.bak</exclude>
+        <exclude>**/*.backup</exclude>
+        <exclude>**/*.old</exclude>
+        <exclude>**/*.swp</exclude>
+        <exclude>**/*.debug</exclude>
+        <exclude>**/*.dump</exclude>
+        <exclude>**/*.log</exclude>
+        <exclude>**/~*</exclude>
+        <!-- common git/scm files -->
+        <exclude>**/*.orig</exclude>
+        <exclude>**/*.diff</exclude>
+        <exclude>**/*.patch</exclude>
+        <exclude>**/.gitignore</exclude>
+        <!-- various editor files -->
+        <exclude>**/*.iml</exclude>
+        <exclude>**/*.ipr</exclude>
+        <exclude>**/*.iws</exclude>
+        <exclude>**/*.idea</exclude>
+        <exclude>**/.classpath</exclude>
+        <exclude>**/.project</exclude>
+        <exclude>**/.settings</exclude>
+        <!-- maven dust -->
+        <exclude>**/*.versionsBackup</exclude>
+        <exclude>**/*.releaseBackup</exclude>
       </excludes>
     </fileSet>
   </fileSets>
diff --git a/jetty-distribution/src/main/assembly/jetty-src.xml b/jetty-distribution/src/main/assembly/jetty-src.xml
index f908b1e..37657e3 100644
--- a/jetty-distribution/src/main/assembly/jetty-src.xml
+++ b/jetty-distribution/src/main/assembly/jetty-src.xml
@@ -12,6 +12,36 @@
       </includes>
       <excludes>
         <exclude>**/target/**</exclude>
+        <!-- common OS detritus -->
+        <exclude>**/.DS_Store</exclude>
+        <exclude>**/Thumbs.db</exclude>
+        <exclude>**/desktop.ini</exclude>
+        <!-- common temp files -->
+        <exclude>**/*~</exclude>
+        <exclude>**/*.bak</exclude>
+        <exclude>**/*.backup</exclude>
+        <exclude>**/*.old</exclude>
+        <exclude>**/*.swp</exclude>
+        <exclude>**/*.debug</exclude>
+        <exclude>**/*.dump</exclude>
+        <exclude>**/*.log</exclude>
+        <exclude>**/~*</exclude>
+        <!-- common git/scm files -->
+        <exclude>**/*.orig</exclude>
+        <exclude>**/*.diff</exclude>
+        <exclude>**/*.patch</exclude>
+        <exclude>**/.gitignore</exclude>
+        <!-- various editor files -->
+        <exclude>**/*.iml</exclude>
+        <exclude>**/*.ipr</exclude>
+        <exclude>**/*.iws</exclude>
+        <exclude>**/*.idea</exclude>
+        <exclude>**/.classpath</exclude>
+        <exclude>**/.project</exclude>
+        <exclude>**/.settings</exclude>
+        <!-- maven dust -->
+        <exclude>**/*.versionsBackup</exclude>
+        <exclude>**/*.releaseBackup</exclude>
       </excludes>
     </fileSet>
   </fileSets>
diff --git a/jetty-distribution/src/main/resources/bin/jetty.sh b/jetty-distribution/src/main/resources/bin/jetty.sh
index da6f571..2f6ba41 100755
--- a/jetty-distribution/src/main/resources/bin/jetty.sh
+++ b/jetty-distribution/src/main/resources/bin/jetty.sh
@@ -1,5 +1,16 @@
 #!/usr/bin/env bash
-#
+
+# LSB Tags
+### BEGIN INIT INFO
+# Provides:          jetty
+# Required-Start:    $local_fs $network
+# Required-Stop:     $local_fs $network
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: Jetty start script.
+# Description:       Start Jetty web server.
+### END INIT INFO
+
 # Startup script for jetty under *nix systems (it works under NT/cygwin too).
 
 ##################################################
@@ -18,19 +29,19 @@
 # Configuration files
 #
 # /etc/default/$NAME
-#   If it exists, this is read at the start of script. It may perform any 
+#   If it exists, this is read at the start of script. It may perform any
 #   sequence of shell commands, like setting relevant environment variables.
 #
 # $HOME/.$NAMErc (e.g. $HOME/.jettyrc)
-#   If it exists, this is read at the start of script. It may perform any 
+#   If it exists, this is read at the start of script. It may perform any
 #   sequence of shell commands, like setting relevant environment variables.
 #
 # /etc/$NAME.conf
 #   If found, and no configurations were given on the command line,
-#   the file will be used as this script's configuration. 
+#   the file will be used as this script's configuration.
 #   Each line in the file may contain:
 #     - A comment denoted by the pound (#) sign as first non-blank character.
-#     - The path to a regular file, which will be passed to jetty as a 
+#     - The path to a regular file, which will be passed to jetty as a
 #       config.xml file.
 #     - The path to a directory. Each *.xml file in the directory will be
 #       passed to jetty as a config.xml file.
@@ -62,14 +73,14 @@
 #   Where the $NAME.pid file should be stored. It defaults to the
 #   first available of /var/run, /usr/var/run, JETTY_BASE and /tmp
 #   if not set.
-#  
+#
 # JETTY_PID
 #   The Jetty PID file, defaults to $JETTY_RUN/$NAME.pid
-#   
+#
 # JETTY_ARGS
 #   The default arguments to pass to jetty.
 #   For example
-#      JETTY_ARGS=jetty.port=8080 jetty.spdy.port=8443 jetty.secure.port=443
+#      JETTY_ARGS=jetty.http.port=8080 jetty.ssl.port=8443
 #
 # JETTY_USER
 #   if set, then used as a username to run the server as
@@ -97,10 +108,10 @@
   local L OP=$1
   shift
   for L in "$@"; do
-    [ "$OP" "$L" ] || continue 
+    [ "$OP" "$L" ] || continue
     printf %s "$L"
     break
-  done 
+  done
 }
 
 running()
@@ -118,7 +129,7 @@
 started()
 {
   # wait for 60s to see "STARTED" in PID file, needs jetty-started.xml as argument
-  for T in 1 2 3 4 5 6 7 9 10 11 12 13 14 15 
+  for T in 1 2 3 4 5 6 7 9 10 11 12 13 14 15
   do
     sleep 4
     [ -z "$(grep STARTED $1 2>/dev/null)" ] || return 0
@@ -162,12 +173,12 @@
 ##################################################
 ETC=/etc
 if [ $UID != 0 ]
-then 
+then
   ETC=$HOME/etc
 fi
 
-for CONFIG in $ETC/default/${NAME}{,9} $HOME/.${NAME}rc; do
-  if [ -f "$CONFIG" ] ; then 
+for CONFIG in {/etc,~/etc}/default/${NAME}{,9} $HOME/.${NAME}rc; do
+  if [ -f "$CONFIG" ] ; then
     readConfig "$CONFIG"
   fi
 done
@@ -187,7 +198,7 @@
 ##################################################
 # Try to determine JETTY_HOME if not set
 ##################################################
-if [ -z "$JETTY_HOME" ] 
+if [ -z "$JETTY_HOME" ]
 then
   JETTY_SH=$0
   case "$JETTY_SH" in
@@ -200,7 +211,7 @@
   esac
 
   if [ ! -f "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ]
-  then 
+  then
     JETTY_HOME=
   fi
 fi
@@ -210,7 +221,7 @@
 # No JETTY_HOME yet? We're out of luck!
 ##################################################
 if [ -z "$JETTY_HOME" ]; then
-  echo "** ERROR: JETTY_HOME not set, you need to set it or install in a standard location" 
+  echo "** ERROR: JETTY_HOME not set, you need to set it or install in a standard location"
   exit 1
 fi
 
@@ -219,7 +230,7 @@
 
 
 ##################################################
-# Set JETTY_BASE 
+# Set JETTY_BASE
 ##################################################
 if [ -z "$JETTY_BASE" ]; then
   JETTY_BASE=$JETTY_HOME
@@ -232,7 +243,7 @@
 #####################################################
 # Check that jetty is where we think it is
 #####################################################
-if [ ! -r "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ] 
+if [ ! -r "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ]
 then
   echo "** ERROR: Oops! Jetty doesn't appear to be installed in $JETTY_HOME"
   echo "** ERROR:  $JETTY_HOME/$JETTY_INSTALL_TRACE_FILE is not readable!"
@@ -244,7 +255,7 @@
 # but only if no configurations were given on the
 # command line.
 ##################################################
-if [ -z "$JETTY_CONF" ] 
+if [ -z "$JETTY_CONF" ]
 then
   if [ -f $ETC/${NAME}.conf ]
   then
@@ -261,7 +272,7 @@
 #####################################################
 # Find a location for the pid file
 #####################################################
-if [ -z "$JETTY_RUN" ] 
+if [ -z "$JETTY_RUN" ]
 then
   JETTY_RUN=$(findDirectory -w /var/run /usr/var/run $JETTY_BASE /tmp)
 fi
@@ -269,12 +280,12 @@
 #####################################################
 # Find a pid and state file
 #####################################################
-if [ -z "$JETTY_PID" ] 
+if [ -z "$JETTY_PID" ]
 then
   JETTY_PID="$JETTY_RUN/${NAME}.pid"
 fi
 
-if [ -z "$JETTY_STATE" ] 
+if [ -z "$JETTY_STATE" ]
 then
   JETTY_STATE=$JETTY_BASE/${NAME}.state
 fi
@@ -289,7 +300,7 @@
 ##################################################
 # Get the list of config.xml files from jetty.conf
 ##################################################
-if [ -f "$JETTY_CONF" ] && [ -r "$JETTY_CONF" ] 
+if [ -f "$JETTY_CONF" ] && [ -r "$JETTY_CONF" ]
 then
   while read -r CONF
   do
@@ -297,18 +308,18 @@
       continue
     fi
 
-    if [ -d "$CONF" ] 
+    if [ -d "$CONF" ]
     then
       # assume it's a directory with configure.xml files
       # for example: /etc/jetty.d/
       # sort the files before adding them to the list of JETTY_ARGS
       for XMLFILE in "$CONF/"*.xml
       do
-        if [ -r "$XMLFILE" ] && [ -f "$XMLFILE" ] 
+        if [ -r "$XMLFILE" ] && [ -f "$XMLFILE" ]
         then
           JETTY_ARGS=(${JETTY_ARGS[*]} "$XMLFILE")
         else
-          echo "** WARNING: Cannot read '$XMLFILE' specified in '$JETTY_CONF'" 
+          echo "** WARNING: Cannot read '$XMLFILE' specified in '$JETTY_CONF'"
         fi
       done
     else
@@ -335,11 +346,11 @@
 #####################################################
 # See if JETTY_LOGS is defined
 #####################################################
-if [ -z "$JETTY_LOGS" ] && [ -d $JETTY_BASE/logs ] 
+if [ -z "$JETTY_LOGS" ] && [ -d $JETTY_BASE/logs ]
 then
   JETTY_LOGS=$JETTY_BASE/logs
 fi
-if [ -z "$JETTY_LOGS" ] && [ -d $JETTY_HOME/logs ] 
+if [ -z "$JETTY_LOGS" ] && [ -d $JETTY_HOME/logs ]
 then
   JETTY_LOGS=$JETTY_HOME/logs
 fi
@@ -350,7 +361,7 @@
   CYGWIN*) JETTY_LOGS="`cygpath -w $JETTY_LOGS`";;
   esac
 
-  JAVA_OPTIONS=(${JAVA_OPTIONS[*]} "-Djetty.logs=$JETTY_LOGS")
+  JAVA_OPTIONS=(${JAVA_OPTIONS[*]} "-Djetty.logging.dir=$JETTY_LOGS")
 fi
 
 #####################################################
@@ -367,7 +378,7 @@
 #####################################################
 
 case "`uname`" in
-CYGWIN*) 
+CYGWIN*)
 JETTY_HOME="`cygpath -w $JETTY_HOME`"
 JETTY_BASE="`cygpath -w $JETTY_BASE`"
 TMPDIR="`cygpath -w $TMPDIR`"
@@ -397,7 +408,7 @@
 RUN_CMD=("$JAVA" ${RUN_ARGS[@]})
 
 #####################################################
-# Comment these out after you're happy with what 
+# Comment these out after you're happy with what
 # the script is doing.
 #####################################################
 if (( DEBUG ))
@@ -422,12 +433,12 @@
   start)
     echo -n "Starting Jetty: "
 
-    if (( NO_START )); then 
+    if (( NO_START )); then
       echo "Not starting ${NAME} - NO_START=1";
       exit
     fi
 
-    if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1 
+    if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1
     then
       unset CH_USER
       if [ -n "$JETTY_USER" ]
@@ -445,7 +456,7 @@
         exit 1
       fi
 
-      if [ -n "$JETTY_USER" ] 
+      if [ -n "$JETTY_USER" ] && [ `whoami` != "$JETTY_USER" ]
       then
         unset SU_SHELL
         if [ "$JETTY_SHELL" ]
@@ -457,11 +468,11 @@
         chown "$JETTY_USER" "$JETTY_PID"
         # FIXME: Broken solution: wordsplitting, pathname expansion, arbitrary command execution, etc.
         su - "$JETTY_USER" $SU_SHELL -c "
-          exec ${RUN_CMD[*]} start-log-file="$JETTY_LOGS/start.log" &
+          exec ${RUN_CMD[*]} start-log-file="$JETTY_LOGS/start.log" > /dev/null &
           disown \$!
           echo \$! > '$JETTY_PID'"
       else
-        "${RUN_CMD[@]}" &
+        "${RUN_CMD[@]}" > /dev/null &
         disown $!
         echo $! > "$JETTY_PID"
       fi
@@ -487,7 +498,7 @@
     echo -n "Stopping Jetty: "
     if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1; then
       start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s HUP
-      
+
       TIMEOUT=30
       while running "$JETTY_PID"; do
         if (( TIMEOUT-- == 0 )); then
@@ -508,7 +519,7 @@
         exit 1
       fi
       kill "$PID" 2>/dev/null
-      
+
       TIMEOUT=30
       while running $JETTY_PID; do
         if (( TIMEOUT-- == 0 )); then
@@ -527,6 +538,7 @@
 
   restart)
     JETTY_SH=$0
+    > "$JETTY_STATE"
     if [ ! -f $JETTY_SH ]; then
       if [ ! -f $JETTY_HOME/bin/jetty.sh ]; then
         echo "$JETTY_HOME/bin/jetty.sh does not exist."
@@ -562,7 +574,13 @@
     ;;
 
   check|status)
-    echo "Checking arguments to Jetty: "
+    if running "$JETTY_PID"
+    then
+      echo "Jetty running pid=$(< "$JETTY_PID")"
+    else
+      echo "Jetty NOT running"
+    fi
+    echo
     echo "START_INI      =  $START_INI"
     echo "START_D        =  $START_D"
     echo "JETTY_HOME     =  $JETTY_HOME"
@@ -578,10 +596,9 @@
     echo "JETTY_ARGS     =  ${JETTY_ARGS[*]}"
     echo "RUN_CMD        =  ${RUN_CMD[*]}"
     echo
-    
+
     if running "$JETTY_PID"
     then
-      echo "Jetty running pid=$(< "$JETTY_PID")"
       exit 0
     fi
     exit 1
diff --git a/jetty-distribution/src/main/resources/demo-base/etc/keystore b/jetty-distribution/src/main/resources/demo-base/etc/keystore
deleted file mode 100644
index 08f6cda..0000000
--- a/jetty-distribution/src/main/resources/demo-base/etc/keystore
+++ /dev/null
Binary files differ
diff --git a/jetty-distribution/src/main/resources/demo-base/webapps/ROOT/index.html b/jetty-distribution/src/main/resources/demo-base/webapps/ROOT/index.html
index ce03053..5b13252 100644
--- a/jetty-distribution/src/main/resources/demo-base/webapps/ROOT/index.html
+++ b/jetty-distribution/src/main/resources/demo-base/webapps/ROOT/index.html
@@ -21,7 +21,7 @@
       Container which supports asynchronous server and client
       implementations of the <a href="http://en.wikipedia.org/wiki/HTTP">HTTP</a>,
       <a href="http://en.wikipedia.org/wiki/WebSocket">Websocket</a> and <a
-        href="http://en.wikipedia.org/wiki/SPDY">SPDY</a> protocols. The
+        href="http://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> protocols. The
       project is 100% <a href="http://en.wikipedia.org/wiki/Open_source">Open Source</a> and hosted by the <a href="http://www.eclipse.org">Eclipse Foundation</a> at <a href="http://www.eclipse.org/jetty/">http://www.eclipse.org/jetty</a>.
     </p>
   </div>
@@ -44,7 +44,7 @@
           <h2>information ...</h2>
           <ul>
             <li><a href="http://www.eclipse.org/jetty/">Jetty Homepage</a></li>
-            <li><a href="http://www.eclipse.org/jetty/documentation/current">Jetty Documentation</a></li>
+            <li><a href="http://www.eclipse.org/jetty/documentation/current">Jetty Documentation</a> (<a href="/doc/">local</a>)</li>
             <li><a href="/proxy/apidocs/">Javadoc</a> (via transparent proxy)</li>
             <li><a href="/proxy/xref/">Xref</a> (via transparent proxy)</li>
             <li><a
@@ -66,7 +66,7 @@
 
   <div id='blog'>
     <h1>Jetty Blog</h1>
-    <iframe src="http://www.webtide.com/blog.jsp" />
+    <iframe src="https://webtide.com/blog.jsp" />
   </div>
 </body>
 </html>
diff --git a/jetty-distribution/src/main/resources/demo-base/webapps/example-moved.xml b/jetty-distribution/src/main/resources/demo-base/webapps/example-moved.xml
index 53d6bfd..e9ad124 100644
--- a/jetty-distribution/src/main/resources/demo-base/webapps/example-moved.xml
+++ b/jetty-distribution/src/main/resources/demo-base/webapps/example-moved.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- Simple handler to redirect from old path to new -->
 <Configure class="org.eclipse.jetty.server.handler.MovedContextHandler">
diff --git a/jetty-distribution/src/main/resources/etc/hawtio.xml b/jetty-distribution/src/main/resources/etc/hawtio.xml
index 228adc1..cc87fce 100644
--- a/jetty-distribution/src/main/resources/etc/hawtio.xml
+++ b/jetty-distribution/src/main/resources/etc/hawtio.xml
@@ -1,15 +1,15 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection">
   <Call name="addHandler">
     <Arg>
       <New class="org.eclipse.jetty.webapp.WebAppContext">
-	<Set name="contextPath">/hawtio</Set>
-	<Set name="war"><Property name="jetty.base" default="."/>/lib/hawtio/hawtio.war</Set>
-	<Set name="extractWAR">true</Set>
-	<Set name="copyWebDir">false</Set>
-	<Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
+        <Set name="contextPath">/hawtio</Set>
+        <Set name="war"><Property name="jetty.base" default="."/>/lib/hawtio/hawtio.war</Set>
+        <Set name="extractWAR">true</Set>
+        <Set name="copyWebDir">false</Set>
+        <Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
       </New>
     </Arg>
   </Call>
diff --git a/jetty-distribution/src/main/resources/etc/jamon.xml b/jetty-distribution/src/main/resources/etc/jamon.xml
index d00d9c7..2ec2d89 100644
--- a/jetty-distribution/src/main/resources/etc/jamon.xml
+++ b/jetty-distribution/src/main/resources/etc/jamon.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Mixin the Jamon Handler                                         -->
@@ -7,24 +7,24 @@
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
-  <Get id="oldhandler" name="handler" />
-  <Set name="handler">
-    <New id="JamonHandler" class="com.jamonapi.http.JAMonJettyHandlerNew">
-      <Set name="handler"><Ref refid="oldhandler" /></Set>
-      <Set name="summaryLabels"><Property name="jamon.summaryLabels" /></Set>
-    </New>
-  </Set>
+  <Call name="insertHandler">
+    <Arg>
+      <New id="JamonHandler" class="com.jamonapi.http.JAMonJettyHandlerNew">
+        <Set name="summaryLabels"><Property name="jamon.summaryLabels" /></Set>
+      </New>
+    </Arg>
+  </Call>
 
   <Ref refid="Contexts">
     <Call name="addHandler">
       <Arg>
-	<New class="org.eclipse.jetty.webapp.WebAppContext">
-	  <Set name="contextPath">/jamon</Set>
-	  <Set name="war"><Property name="jetty.base" default="."/>/lib/jamon/jamon.war</Set>
-	  <Set name="extractWAR">true</Set>
-	  <Set name="copyWebDir">false</Set>
-	  <Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
-	</New>
+        <New class="org.eclipse.jetty.webapp.WebAppContext">
+          <Set name="contextPath">/jamon</Set>
+          <Set name="war"><Property name="jetty.base" default="."/>/lib/jamon/jamon.war</Set>
+          <Set name="extractWAR">true</Set>
+          <Set name="copyWebDir">false</Set>
+          <Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
+        </New>
       </Arg>
     </Call>
   </Ref>
diff --git a/jetty-distribution/src/main/resources/etc/jetty-setuid.xml b/jetty-distribution/src/main/resources/etc/jetty-setuid.xml
new file mode 100644
index 0000000..cb33a1a
--- /dev/null
+++ b/jetty-distribution/src/main/resources/etc/jetty-setuid.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<!-- ================================================================ -->
+<!-- Configure the Jetty SetUIDListener                                -->
+<!-- ================================================================ -->
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <Call name="addLifeCycleListener">
+    <Arg>
+      <New class="org.eclipse.jetty.setuid.SetUIDListener">
+        <Set name="startServerAsPrivileged"><Property name="jetty.setuid.startServerAsPrivileged" deprecated="jetty.startServerAsPrivileged" default="false"/></Set>
+        <Set name="umaskOctal"><Property name="jetty.setuid.umask" deprecated="jetty.umask" default="002"/></Set>
+        <Set name="username"><Property name="jetty.setuid.userName" deprecated="jetty.username" default="jetty"/></Set>
+        <Set name="groupname"><Property name="jetty.setuid.groupName" deprecated="jetty.groupname" default="jetty"/></Set>
+        <!-- uncomment to change the limits on number of open file descriptors for root -->
+        <!--
+        <Call name="setRLimitNoFiles">
+          <Arg>
+            <New class="org.eclipse.jetty.setuid.RLimit">
+              <Set name="soft">20000</Set>
+              <Set name="hard">40000</Set>
+            </New>
+          </Arg>
+        </Call>
+        -->
+      </New>
+    </Arg>
+  </Call>
+</Configure>
diff --git a/jetty-distribution/src/main/resources/etc/jetty-started.xml b/jetty-distribution/src/main/resources/etc/jetty-started.xml
index b958074..faa2839 100644
--- a/jetty-distribution/src/main/resources/etc/jetty-started.xml
+++ b/jetty-distribution/src/main/resources/etc/jetty-started.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Mixin the Start FileNoticeLifeCycleListener                     -->
diff --git a/jetty-distribution/src/main/resources/etc/jminix.xml b/jetty-distribution/src/main/resources/etc/jminix.xml
index 1be4757..15c699e 100644
--- a/jetty-distribution/src/main/resources/etc/jminix.xml
+++ b/jetty-distribution/src/main/resources/etc/jminix.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
  
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
   <Call name="addBean">
diff --git a/jetty-distribution/src/main/resources/etc/jolokia.xml b/jetty-distribution/src/main/resources/etc/jolokia.xml
index 575912f..b6e2404 100644
--- a/jetty-distribution/src/main/resources/etc/jolokia.xml
+++ b/jetty-distribution/src/main/resources/etc/jolokia.xml
@@ -1,15 +1,15 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection">
   <Call name="addHandler">
     <Arg>
       <New class="org.eclipse.jetty.webapp.WebAppContext">
-	<Set name="contextPath">/jolokia</Set>
-	<Set name="war"><Property name="jetty.base" default="."/>/lib/jolokia/jolokia.war</Set>
-	<Set name="extractWAR">true</Set>
-	<Set name="copyWebDir">false</Set>
-	<Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
+        <Set name="contextPath">/jolokia</Set>
+        <Set name="war"><Property name="jetty.base" default="."/>/lib/jolokia/jolokia.war</Set>
+        <Set name="extractWAR">true</Set>
+        <Set name="copyWebDir">false</Set>
+        <Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
       </New>
     </Arg>
   </Call>
diff --git a/jetty-distribution/src/main/resources/modules/hawtio.mod b/jetty-distribution/src/main/resources/modules/hawtio.mod
index 2dfb31b..f6d0d9d 100644
--- a/jetty-distribution/src/main/resources/modules/hawtio.mod
+++ b/jetty-distribution/src/main/resources/modules/hawtio.mod
@@ -22,7 +22,7 @@
 http://www.apache.org/licenses/LICENSE-2.0.html
 
 [ini-template]
-
+## Hawt.io configuration
 -Dhawtio.authenticationEnabled=false
 -Dhawtio.dirname=/dirname
 -Dhawtio.config.dir=${jetty.base}/etc/hawtio
diff --git a/jetty-distribution/src/main/resources/modules/jamon.mod b/jetty-distribution/src/main/resources/modules/jamon.mod
index 2aeb2ad..2d1f144 100644
--- a/jetty-distribution/src/main/resources/modules/jamon.mod
+++ b/jetty-distribution/src/main/resources/modules/jamon.mod
@@ -13,8 +13,8 @@
 
 [files]
 lib/jamon/
-http://central.maven.org/maven2/com/jamonapi/jamon/2.79/jamon-2.79.jar|lib/jamon/jamon-2.79.jar
-http://central.maven.org/maven2/com/jamonapi/jamon_war/2.79/jamon_war-2.79.war|lib/jamon/jamon.war
+maven://com.jamonapi/jamon/2.79|lib/jamon/jamon-2.79.jar
+maven://com.jamonapi/jamon_war/2.79/war|lib/jamon/jamon.war
 
 [lib]
 lib/jamon/**.jar
@@ -25,6 +25,7 @@
 http://jamonapi.sourceforge.net/JAMonLicense.html
 
 [ini-template]
+## Jamon Configuration
+# jamon.summaryLabels=demo
 jamon.summaryLabels=default, request.getStatus().contextpath.value.ms
-#jamon.summaryLabels=demo
 
diff --git a/jetty-distribution/src/main/resources/modules/jminix.mod b/jetty-distribution/src/main/resources/modules/jminix.mod
index b35d702..05788f0 100644
--- a/jetty-distribution/src/main/resources/modules/jminix.mod
+++ b/jetty-distribution/src/main/resources/modules/jminix.mod
@@ -11,21 +11,21 @@
 
 [files]
 lib/jminix/
-http://central.maven.org/maven2/org/jminix/jminix/1.1.0/jminix-1.1.0.jar|lib/jminix/jminix-1.1.0.jar
+maven://org.jminix/jminix/1.1.0|lib/jminix/jminix-1.1.0.jar
 http://maven.restlet.com/org/restlet/org.restlet/1.1.5/org.restlet-1.1.5.jar|lib/jminix/org.restlet-1.1.5.jar
 http://maven.restlet.com/org/restlet/org.restlet.ext.velocity/1.1.5/org.restlet.ext.velocity-1.1.5.jar|lib/jminix/org.restlet.ext.velocity-1.1.5.jar
-http://central.maven.org/maven2/org/apache/velocity/velocity/1.5/velocity-1.5.jar|lib/jminix/velocity-1.5.jar
-http://central.maven.org/maven2/oro/oro/2.0.8/oro-2.0.8.jar|lib/jminix/oro-2.0.8.jar
+maven://org.apache.velocity/velocity/1.5|lib/jminix/velocity-1.5.jar
+maven://oro/oro/2.0.8|lib/jminix/oro-2.0.8.jar
 http://maven.restlet.com/com/noelios/restlet/com.noelios.restlet/1.1.5/com.noelios.restlet-1.1.5.jar|lib/jminix/com.noelios.restlet-1.1.5.jar
 http://maven.restlet.com/com/noelios/restlet/com.noelios.restlet.ext.servlet/1.1.5/com.noelios.restlet.ext.servlet-1.1.5.jar|lib/jminix/com.noelios.restlet.ext.servlet-1.1.5.jar
-http://central.maven.org/maven2/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar|lib/jminix/commons-logging-1.1.1.jar
-http://repo2.maven.org/maven2/net/sf/json-lib/json-lib/2.2.3/json-lib-2.2.3-jdk15.jar|lib/jminix/json-lib-2.2.3-jdk15.jar
-http://central.maven.org/maven2/commons-lang/commons-lang/2.4/commons-lang-2.4.jar|lib/jminix/commons-lang-2.4.jar
-http://central.maven.org/maven2/commons-beanutils/commons-beanutils/1.7.0/commons-beanutils-1.7.0.jar|lib/jminix/commons-beanutils-1.7.0.jar
-http://central.maven.org/maven2/commons-collections/commons-collections/3.2/commons-collections-3.2.jar|lib/jminix/commons-collections-3.2.jar
-http://central.maven.org/maven2/net/sf/ezmorph/ezmorph/1.0.6/ezmorph-1.0.6.jar|lib/jminix/ezmorph-1.0.6.jar
-http://central.maven.org/maven2/org/jgroups/jgroups/2.12.1.3.Final/jgroups-2.12.1.3.Final.jar|lib/jminix/jgroups-2.12.1.3.Final.jar
-http://central.maven.org/maven2/org/jasypt/jasypt/1.8/jasypt-1.8.jar|lib/jminix/jasypt-1.8.jar
+maven://commons-logging/commons-logging/1.1.1|lib/jminix/commons-logging-1.1.1.jar
+maven://net.sf.json-lib/json-lib/2.2.3/jar/jdk15|lib/jminix/json-lib-2.2.3-jdk15.jar
+maven://commons-lang/commons-lang/2.4|lib/jminix/commons-lang-2.4.jar
+maven://commons-beanutils/commons-beanutils/1.7.0|lib/jminix/commons-beanutils-1.7.0.jar
+maven://commons-collections/commons-collections/3.2|lib/jminix/commons-collections-3.2.jar
+maven://net.sf.ezmorph/ezmorph/1.0.6|lib/jminix/ezmorph-1.0.6.jar
+maven://org.jgroups/jgroups/2.12.1.3.Final|lib/jminix/jgroups-2.12.1.3.Final.jar
+maven://org.jasypt/jasypt/1.8|lib/jminix/jasypt-1.8.jar
 
 [lib]
 lib/jminix/**.jar
@@ -36,6 +36,6 @@
 http://www.apache.org/licenses/LICENSE-2.0
 
 [ini-template]
-# Jminix Configuration
+## Jminix Configuration
 jminix.port=8088
 
diff --git a/jetty-distribution/src/main/resources/modules/jolokia.mod b/jetty-distribution/src/main/resources/modules/jolokia.mod
index 876c2fc..da8ac8f 100644
--- a/jetty-distribution/src/main/resources/modules/jolokia.mod
+++ b/jetty-distribution/src/main/resources/modules/jolokia.mod
@@ -11,7 +11,7 @@
 etc/jolokia.xml
 
 [files]
-http://repo1.maven.org/maven2/org/jolokia/jolokia-war/1.2.2/jolokia-war-1.2.2.war|lib/jolokia/jolokia.war
+maven://org.jolokia/jolokia-war/1.2.2/war|lib/jolokia/jolokia.war
 
 [license]
 Jolokia is released under the Apache License 2.0
diff --git a/jetty-distribution/src/main/resources/modules/jsp.mod b/jetty-distribution/src/main/resources/modules/jsp.mod
index bb31ca7..a16cc93 100644
--- a/jetty-distribution/src/main/resources/modules/jsp.mod
+++ b/jetty-distribution/src/main/resources/modules/jsp.mod
@@ -5,17 +5,5 @@
 [depend]
 servlet
 annotations
-jsp-impl/${jsp-impl}-jsp
+apache-jsp
 
-[ini-template]
-# JSP Configuration
-
-# Select JSP implementation, choices are
-#   glassfish : The reference implementation 
-#               default in jetty <= 9.1
-#   apache    : The apache version 
-#               default jetty >= 9.2
-jsp-impl=apache
-
-# To use a non-jdk compiler for JSP compilation when using glassfish uncomment next line
-# -Dorg.apache.jasper.compiler.disablejsr199=true
diff --git a/jetty-distribution/src/main/resources/modules/jstl.mod b/jetty-distribution/src/main/resources/modules/jstl.mod
index cb06244..efc310a 100644
--- a/jetty-distribution/src/main/resources/modules/jstl.mod
+++ b/jetty-distribution/src/main/resources/modules/jstl.mod
@@ -1,14 +1,8 @@
 #
-# Jetty JSP Module
+# Jetty JSTL Module
 #
 
 [depend]
 jsp
-jsp-impl/${jsp-impl}-jstl
+apache-jstl
 
-[ini-template]
-# JSTL Configuration
-# The glassfish jsp-impl includes JSTL by default and this module
-# is not required to activate it.
-# The apache jsp-impl does not include JSTL by default and this module
-# is required to put JSTL on the container classpath
diff --git a/jetty-distribution/src/main/resources/modules/protonego.mod b/jetty-distribution/src/main/resources/modules/protonego.mod
deleted file mode 100644
index fbf4d08..0000000
--- a/jetty-distribution/src/main/resources/modules/protonego.mod
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# Protocol Negotiatin Selection Module
-#
-
-[depend]
-protonego-impl/${protonego}
-
-[ini-template]
-# Protocol Negotiation Implementation Selection
-#  choices are:
-#    'npn'  : original implementation for SPDY (now deprecated)
-#    'alpn' : replacement for NPN, in use by current SPDY implementations
-#             and the future HTTP/2 spec
-#  Note: java 1.8+ are ALPN only.
-protonego=alpn
-
-# Configuration for NPN
-# npn.protocols=spdy/3,http/1.1
-# npn.defaultProtocol=http/1.1
-
-# Configuration for ALPN
-# alpn.protocols=h2-14,http/1.1
-# alpn.defaultProtocol=http/1.1
-
diff --git a/jetty-distribution/src/main/resources/modules/setuid.mod b/jetty-distribution/src/main/resources/modules/setuid.mod
index 64c9e23..41ef757 100644
--- a/jetty-distribution/src/main/resources/modules/setuid.mod
+++ b/jetty-distribution/src/main/resources/modules/setuid.mod
@@ -6,14 +6,14 @@
 server
 
 [lib]
-lib/setuid/jetty-setuid-java-1.0.1.jar
+lib/setuid/jetty-setuid-java-1.0.3.jar
 
 [xml]
 etc/jetty-setuid.xml
 
 [ini-template]
 ## SetUID Configuration
-# jetty.startServerAsPrivileged=false
-# jetty.username=jetty
-# jetty.groupname=jetty
-# jetty.umask=002
+# jetty.setuid.startServerAsPrivileged=false
+# jetty.setuid.userName=jetty
+# jetty.setuid.groupName=jetty
+# jetty.setuid.umask=002
diff --git a/jetty-documentation/pom.xml b/jetty-documentation/pom.xml
new file mode 100644
index 0000000..427b4ce
--- /dev/null
+++ b/jetty-documentation/pom.xml
@@ -0,0 +1,258 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+ <parent>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-project</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+ </parent>
+  <artifactId>jetty-documentation</artifactId>
+  <name>Jetty :: Documentation</name>
+  <packaging>pom</packaging>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <asciidoctor.version>1.5.3</asciidoctor.version>
+    <html.directory>${project.build.directory}/current</html.directory>
+  </properties>
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.felix</groupId>
+          <artifactId>maven-bundle-plugin</artifactId>
+          <version>3.0.1</version>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+    <plugins>
+      <plugin>
+        <artifactId>maven-resources-plugin</artifactId>
+        <version>2.6</version>
+        <executions>
+          <execution>
+            <id>copy-assets</id>
+            <phase>process-resources</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <resources>
+                <resource>
+                  <directory>src/main/resources</directory>
+                  <includes>
+                    <include>**</include>
+                  </includes>
+                </resource>
+              </resources>
+              <outputDirectory>${html.directory}</outputDirectory>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.asciidoctor</groupId>
+        <artifactId>asciidoctor-maven-plugin</artifactId>
+        <version>${asciidoctor.version}</version>
+        <executions>
+          <execution>
+            <id>output-html</id>
+            <phase>compile</phase>
+            <goals>
+              <goal>process-asciidoc</goal>
+            </goals>
+            <configuration>
+              <backend>docbook</backend>
+              <doctype>book</doctype>
+              <sourceDocumentName>index.adoc</sourceDocumentName>
+              <attributes>
+                <sub-order>attributes+</sub-order>
+                <imagesdir />
+                <linkcss>true</linkcss>
+                <allow-uri-read>true</allow-uri-read>
+                <toc>true</toc>
+                <revnumber>${project.version}</revnumber>
+                <JDURL>http://www.eclipse.org/jetty/javadoc/${project.version}</JDURL>
+                <JXURL>http://download.eclipse.org/jetty/stable-9/xref</JXURL>
+                <SRCDIR>${basedir}/..</SRCDIR>
+                <GITBROWSEURL>https://github.com/eclipse/jetty.project/tree/jetty-9.3.x</GITBROWSEURL>
+                <MVNCENTRAL>http://central.maven.org/maven2</MVNCENTRAL>
+                <VERSION>${project.version}</VERSION>
+              </attributes>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>com.agilejava.docbkx</groupId>
+        <artifactId>docbkx-maven-plugin</artifactId>
+        <version>2.0.17</version>
+        <executions>
+          <execution>
+            <id>html</id>
+            <phase>compile</phase>
+            <goals>
+              <goal>generate-html</goal>
+            </goals>
+            <configuration>
+              <htmlStylesheet>css/docbook.css</htmlStylesheet>
+              <htmlCustomization>${project.basedir}/src/main/docbkx-stylesheet/html/docbook.xsl</htmlCustomization>
+              <preProcess>
+                <!-- pull over the images from the source material -->
+                <copy todir="target/docbkx/html/images" flatten="true">
+                  <fileset dir="src/main/asciidoc">
+                    <include name="**/*.png" />
+                    <include name="**/*.jpg" />
+                    <include name="**/*.svg" />
+                    <include name="**/*.dot" />
+                  </fileset>
+                </copy>
+                <copy todir="target/docbkx/html/images">
+                  <fileset dir="src/main/docbkx-resources/images" />
+                </copy>
+                <copy todir="target/docbkx/html/css">
+                  <fileset dir="src/main/docbkx-resources/css" />
+                </copy>
+                <copy todir="target/docbkx/html/fonts">
+                  <fileset dir="src/main/docbkx-resources/fonts" />
+                </copy>
+                <copy todir="target/docbkx/html/js">
+                  <fileset dir="src/main/docbkx-resources/js" />
+                </copy>
+              </preProcess>
+            </configuration>
+          </execution>
+        </executions>
+        <configuration>
+          <!-- shared configuration -->
+          <sourceDirectory>${project.build.directory}/generated-docs</sourceDirectory>
+          <includes>index.xml</includes>
+          <generatedSourceDirectory>${project.build.directory}/docbkx/generated</generatedSourceDirectory>
+          <chunkedOutput>true</chunkedOutput>
+          <highlightSource>true</highlightSource>
+        </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>net.sf.docbook</groupId>
+            <artifactId>docbook-xml</artifactId>
+            <!--version>5.0-all</version-->
+            <version>5.1b4-all</version>
+            <classifier>resources</classifier>
+            <type>zip</type>
+            <scope>runtime</scope>
+          </dependency>
+          <dependency>
+            <groupId>net.sf.xslthl</groupId>
+            <artifactId>xslthl</artifactId>
+            <version>2.0.1</version>
+            <scope>runtime</scope>
+          </dependency>
+          <dependency>
+            <groupId>org.eclipse.jetty.toolchain</groupId>
+            <artifactId>jetty-xslt-tools</artifactId>
+            <version>1.3</version>
+            <scope>runtime</scope>
+          </dependency>
+        </dependencies>
+      </plugin>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <descriptors>
+            <descriptor>src/main/assembly/html.xml</descriptor>
+          </descriptors>
+        </configuration>
+        <executions>
+          <execution>
+            <id>make-assembly</id>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  <profiles>
+    <!--
+      Couple of different approaches to generating pdf's
+    -->
+    <profile>
+      <id>generate-pdf</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>com.agilejava.docbkx</groupId>
+            <artifactId>docbkx-maven-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>generate-pdf</id>
+                <phase>compile</phase>
+                <goals>
+                  <goal>generate-pdf</goal>
+                </goals>
+                <configuration>
+                  <includes>index.xml</includes>
+                  <fop1Extensions>1</fop1Extensions>
+                  <paperType>A4</paperType>
+                  <foCustomization>src/main/docbkx-stylesheet/fo/docbook.xsl</foCustomization>
+                </configuration>
+              </execution>
+            </executions>
+            <dependencies>
+              <dependency>
+                <groupId>net.sf.offo</groupId>
+                <artifactId>fop-hyph</artifactId>
+                <version>1.2</version>
+                <scope>runtime</scope>
+              </dependency>
+            </dependencies>
+          </plugin>
+          <plugin>
+            <groupId>org.asciidoctor</groupId>
+            <artifactId>asciidoctor-maven-plugin</artifactId>
+            <version>${asciidoctor.version}</version>
+            <dependencies>
+              <dependency>
+                <groupId>org.asciidoctor</groupId>
+                <artifactId>asciidoctorj-pdf</artifactId>
+                <version>1.5.0-alpha.11</version>
+              </dependency>
+            </dependencies>
+            <executions>
+              <execution>
+                <id>output-pdf</id>
+                <phase>generate-sources</phase>
+                <goals>
+                  <goal>process-asciidoc</goal>
+                </goals>
+                <configuration>
+                  <backend>pdf</backend>
+                  <sourceHighlighter>rouge</sourceHighlighter>
+                  <sourceDocumentName>index.adoc</sourceDocumentName>
+                  <attributes>
+                    <imagesdir />
+                    <version>${project.version}</version>
+                    <linkcss>true</linkcss>
+                    <allow-uri-read>true</allow-uri-read>
+                    <toc>true</toc>
+                    <revnumber>${project.version}</revnumber>
+                    <JDURL>http://download.eclipse.org/jetty/stable-9/apidocs</JDURL>
+                    <JXURL>http://download.eclipse.org/jetty/stable-9/xref</JXURL>
+                    <SRCDIR>${basedir}/../jetty.project/</SRCDIR>
+                    <GITBROWSEURL>https://github.com/eclipse/jetty.project/master</GITBROWSEURL>
+                    <icons>font</icons>
+                    <pagenums />
+                    <toc />
+                    <idprefix />
+                    <idseparator>-</idseparator>
+                  </attributes>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>
diff --git a/jetty-documentation/src/main/asciidoc/administration/alpn/alpn.adoc b/jetty-documentation/src/main/asciidoc/administration/alpn/alpn.adoc
new file mode 100644
index 0000000..0a65b98
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/alpn/alpn.adoc
@@ -0,0 +1,275 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[alpn]]
+=== Introducing ALPN
+
+The Jetty project provides an implementation of the TLS extension for ALPN for OpenJDK 7 and OpenJDK 8.
+ALPN allows the application layer to negotiate which protocol to use over the secure connection.
+
+Any protocol can be negotiated by ALPN within a TLS connection.
+The protocols that are most commonly negotiated are HTTP/2 (for browsers that support it) and, historically, SPDY.
+The ALPN implementation is therefore not HTTP/2 or SPDY specific in any way.
+Jetty's ALPN implementation, although hosted under the umbrella of the Jetty project, is independent of Jetty (the Servlet Container); you can use the ALPN implementation in any other Java network server.
+
+The Jetty distribution will automatically enable ALPN when it is needed to by a HTTP/2 connector, so for the most part ALPN is transparent to the average deployer.
+This section provides the detail required for non-standard deployments or developing to the ALPN API.
+
+[[alpn-starting]]
+==== Starting the JVM
+
+To enable ALPN support, start the JVM as follows:
+
+[source, plain, subs="{sub-order}"]
+----
+java -Xbootclasspath/p:<path_to_alpn_boot_jar> ...
+----
+
+Where `path_to_alpn_boot_jar` is the path on the file system for the ALPN Boot Jar file,such as the one at the Maven coordinates `org.mortbay.jetty.alpn:alpn-boot`.
+
+Be certain link:#alpn-versions[to get the ALPN Boot Jar version which matches the version of your JRE].
+
+[[alpn-osgi]]
+===== Starting in OSGi
+
+To use ALPN in an OSGi environment, in addition to putting the ALPN jar on the boot classpath for the container, you will also need to deploy the `jetty-osgi-alpn` jar.
+This jar contains a Fragment-Host directive that ensures the ALPN classes will be available from the system bundle.
+
+You can download the http://central.maven.org/maven2/org/eclipse/jetty/osgi/jetty-osgi-alpn/[jetty-osgi-alpn jar] from Maven Central.
+
+[[alpn-understanding]]
+==== Understanding the ALPN API
+
+Applications need to interact with ALPN TLS extension protocol negotiations.
+For example, server applications need to know whether the client supports ALPN, and client applications needs to know whether the server supports ALPN.
+
+To implement this interaction, Jetty's ALPN implementation provides an API to applications, hosted at Maven coordinates
+`org.eclipse.jetty.alpn:alpn-api`.
+You need to declare this dependency as provided, because the `alpn-boot` jar already includes it (see the previous section), and it is therefore available from the boot classpath.
+
+The API consists of a single class, `org.eclipse.jetty.alpn.ALPN`, and applications need to register instances of `SSLSocket` or `SSLEngine` with a `ClientProvider` or `ServerProvider` (depending on whether the application is a client application or server application).
+Refer to `ALPN` Javadocs and to the examples below for further details about client and server provider methods.
+
+[[alpn-client-example]]
+==== Client Example
+
+[source, java, subs="{sub-order}"]
+----
+SSLContext sslContext = ...;
+final SSLSocket sslSocket = (SSLSocket)context.getSocketFactory().createSocket("localhost", server.getLocalPort());
+
+ALPN.put(sslSocket, new ALPN.ClientProvider()
+{
+    @Override
+    public boolean supports()
+    {
+        return true;
+    }
+
+    @Override
+    public List<String> protocols()
+    {
+        return Arrays.asList("h2", "http/1.1");
+    }
+
+    @Override
+    public void unsupported()
+    {
+        ALPN.remove(sslSocket);
+    }
+
+    @Override
+    public void selected(String protocol)
+    {
+        ALPN.remove(sslSocket);
+        System.out.println("Protocol Selected is: " + protocol);
+    }
+});
+----
+
+The ALPN implementation calls `ALPN.ClientProvider` methods `supports()`, `protocols()`, `unsupported()` and `selected(String)`, so that the client application can:
+
+* Decide whether to support ALPN
+* Provide the protocols supported
+* Know whether the server supports ALPN
+* Know the protocol chosen by the server
+
+[[alpn-server-example]]
+==== Server Example
+
+The example for SSLEngine is identical, and you just need to replace the SSLSocket instance with an SSLEngine instance.
+
+[source, java, subs="{sub-order}"]
+----
+final SSLSocket sslSocket = ...;
+ALPN.put(sslSocket, new ALPN.ServerProvider()
+{
+    @Override
+    public void unsupported()
+    {
+        ALPN.remove(sslSocket);
+    }
+
+    @Override
+    public String select(List<String> protocols);
+    {
+        ALPN.remove(sslSocket);
+        return protocols.get(0);
+    }
+});
+----
+
+The ALPN implementation calls `ALPN.ServerProvider` methods `unsupported()`, and `select(List<String>),` so that the server application can:
+
+* know whether the client supports ALPN.
+* select one of the protocols the client supports.
+
+[[alpn-implementation]]
+==== Implementation Details
+
+It is important that implementations of `ALPN.ServerProvider` and `ALPN.ClientProvider` remove the `sslSocket` or `sslEngine` when the negotiation is complete, like shown in the examples above.
+
+Failing to do so will cause a memory leak.
+
+[[alpn-tests]]
+==== Unit Tests
+
+You can write and run unit tests that use the ALPN implementation.
+The solution that we use with Maven is to specify an additional command line argument to the Surefire plugin:
+
+[source, xml, subs="{sub-order}"]
+----
+<project>
+
+<properties>
+    <alpn-boot-version>8.1.4.v20150727</alpn-boot-version>
+</properties>
+
+<build>
+    <plugins>
+        <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+                <argLine>
+                    -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn-boot-version}/alpn-boot-${alpn-boot-version}.jar
+                </argLine>
+            </configuration>
+        </plugin>
+
+        ...
+
+    </plugins>
+</build>
+
+...
+
+</project>
+----
+
+[[alpn-debugging]]
+==== Debugging
+
+You can enable debug logging for the ALPN implementation in this way:
+
+....
+ALPN.debug = true;
+....
+
+Since the ALPN class is in the boot classpath, we chose not to use logging libraries because we do not want to override application logging library choices; therefore the logging is performed directly on `System.err`.
+
+[[alpn-license-details]]
+==== License Details
+
+The ALPN implementation relies on modification of a few OpenJDK classes and on a few new classes that need to live in the `sun.security.ssl` package.
+These classes are released under the same GPLv2+exception license of OpenJDK.
+
+The ALPN class and its nested classes are released under same license as the classes of the Jetty project.
+
+[[alpn-versions]]
+==== Versions
+
+The ALPN implementation, relying on modifications of OpenJDK classes, updates every time there are updates to the modified OpenJDK classes.
+
+.ALPN vs. OpenJDK versions
+[cols=",",options="header",]
+|=============================
+|OpenJDK version |ALPN version
+|1.7.0u40 |7.1.0.v20141016
+|1.7.0u45 |7.1.0.v20141016
+|1.7.0u51 |7.1.0.v20141016
+|1.7.0u55 |7.1.0.v20141016
+|1.7.0u60 |7.1.0.v20141016
+|1.7.0u65 |7.1.0.v20141016
+|1.7.0u67 |7.1.0.v20141016
+|1.7.0u71 |7.1.2.v20141202
+|1.7.0u72 |7.1.2.v20141202
+|1.7.0u75 |7.1.3.v20150130
+|1.7.0u76 |7.1.3.v20150130
+|1.7.0u79 |7.1.3.v20150130
+|1.7.0u80 |7.1.3.v20150130
+|1.8.0 |8.1.0.v20141016
+|1.8.0u05 |8.1.0.v20141016
+|1.8.0u11 |8.1.0.v20141016
+|1.8.0u20 |8.1.0.v20141016
+|1.8.0u25 |8.1.2.v20141202
+|1.8.0u31 |8.1.3.v20150130
+|1.8.0u40 |8.1.3.v20150130
+|1.8.0u45 |8.1.3.v20150130
+|1.8.0u51 |8.1.4.v20150727
+|1.8.0u60 |8.1.5.v20150921
+|1.8.0u65 |8.1.6.v20151105
+|1.8.0u66 |8.1.6.v20151105
+|1.8.0u71 |8.1.7.v20160121
+|1.8.0u72 |8.1.7.v20160121
+|1.8.0u73 |8.1.7.v20160121
+|1.8.0u74 |8.1.7.v20160121
+|1.8.0u77 |8.1.7.v20160121
+|1.8.0u91 |8.1.7.v20160121
+|1.8.0u92 |8.1.8.v20160420
+|1.8.0u101 |8.1.9.v20160720
+|1.8.0u102 |8.1.9.v20160720
+|1.8.0u111 |8.1.9.v20160720
+|1.8.0u112 |8.1.10.v20161026
+|1.8.0u121 |8.1.11.v20170118
+|1.8.0u131 |8.1.11.v20170118
+|=============================
+
+[[alpn-build]]
+==== How to build ALPN
+
+This section is for Jetty developers that need to update the ALPN implementation with the OpenJDK versions.
+
+Clone the OpenJDK repository with the following command:
+
+[source, screen, subs="{sub-order}"]
+....
+$ hg clone http://hg.openjdk.java.net/jdk7u/jdk7u jdk7u # OpenJDK 7
+$ hg clone http://hg.openjdk.java.net/jdk8u/jdk8u jdk8u # OpenJDK 8
+$ cd !$
+$ ./get_source.sh
+....
+
+To update the source to a specific tag, use the following command:
+
+[source, screen, subs="{sub-order}"]
+....
+$ ./make/scripts/hgforest.sh update <tag-name>
+
+....
+
+The list of OpenJDK tags can be obtained from these pages: http://hg.openjdk.java.net/jdk7u/jdk7u/tags[OpenJDK 7] / http://hg.openjdk.java.net/jdk8u/jdk8u/tags[OpenJDK 8].
+
+You will then need to compare and incorporate the OpenJDK source changes into the modified OpenJDK classes at the https://github.com/jetty-project/jetty-alpn[ALPN GitHub Repository], branch `openjdk7` for OpenJDK 7 and branch `master` for OpenJDK 8.
diff --git a/jetty-documentation/src/main/asciidoc/administration/alpn/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/alpn/chapter.adoc
new file mode 100644
index 0000000..af21846
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/alpn/chapter.adoc
@@ -0,0 +1,28 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[alpn-chapter]]
+== Application Layer Protocol Negotiation (ALPN)
+
+The development of new web protocols such as HTTP/2 raised the need of protocol negotiation within a Transport Layer Security (TLS) handshake.
+A protocol negotiation called https://tools.ietf.org/html/rfc7301[ALPN] (Application Layer Protocol Negotiation) RFC7301 has been defined to accomplish this.
+
+ALPN has now replaced the older (and now fully deprecated) NPN in the general Web of 2016.
+
+For those browsers that support HTTP/2, they all now support the ALPN negotiation layers for TLS.
+Starting with Jetty 9.3.0, only ALPN is supported by Jetty.
+
+include::alpn.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/administration/annotations/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/annotations/chapter.adoc
new file mode 100644
index 0000000..5efaf22
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/annotations/chapter.adoc
@@ -0,0 +1,25 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[annotations]]
+== Annotations
+
+Jetty supports the servlet specification annotations. 
+It is not enable by default, so the following sections show you how to enable it, and how to use them.
+
+include::quick-annotations-setup.adoc[]
+include::using-annotations.adoc[]
+include::using-annotations-embedded.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/administration/annotations/quick-annotations-setup.adoc b/jetty-documentation/src/main/asciidoc/administration/annotations/quick-annotations-setup.adoc
new file mode 100644
index 0000000..192f1d2
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/annotations/quick-annotations-setup.adoc
@@ -0,0 +1,43 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[annotations-quick-setup]]
+=== Quick Setup
+
+==== Jetty Distribution
+
+If you are using the jetty distribution, then annotations are enabled by default.
+The annotations link:#startup-modules[module] and its transitive dependencies are responsible for making annotation processing available.
+
+Note that annotations that relate to link:#jndi[JNDI], such as @Resource and @Resources are enabled via the JNDI module, which is a transitive dependency on the annotations module.
+
+==== Jetty Maven Plugin
+
+Annotations and JNDI are pre-enabled for the Maven plugin.
+
+==== Embedding
+
+To use annotations in an embedded scenario, you will need to include the `jetty-annotations` jar and all its dependencies onto your classpath.
+You will also need to include the `org.eclipse.jetty.annotations.AnnotationConfiguration` class into the list of link:#webapp-configurations[Configuration classes] applied to the `org.eclipse.jetty.webapp.WebAppContext` class representing your webapp.
+
+Below is an example application that sets up the standard `test-spec.war` webapp from the distribution in embedded fashion.
+It can also be found in the Jetty GitHub repository on the examples/embedded page as link:{GITBROWSEURL}/examples/embedded/src/main/java/org/eclipse/jetty/embedded[`ServerWithAnnotations.java`.]
+Note that the `test-spec.war` uses not only annotations, but also link:#jndi[JNDI], so this example also enables their processing (via the link:#jndi-configuration-classes[org.eclipse.jetty.plus.webapp.EnvConfiguration], link:#jndi-configuration-classes[org.eclipse.jetty.plus.webapp.PlusConfiguration] and their related jars).
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java[]
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/annotations/using-annotations-embedded.adoc b/jetty-documentation/src/main/asciidoc/administration/annotations/using-annotations-embedded.adoc
new file mode 100644
index 0000000..893a634
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/annotations/using-annotations-embedded.adoc
@@ -0,0 +1,192 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[using-annotations-embedded]]
+=== Using Annotations with Jetty Embedded
+
+==== Setting up the Classpath
+
+You will need to place the following Jetty jar files onto the classpath of your application.
+You can obtain them from the http://download.eclipse.org/jetty/stable-9/dist/[Jetty distribution], or the http://central.maven.org/maven2/org/eclipse/jetty/jetty-annotations[Maven repository]:
+
+....
+jetty-plus.jar
+jetty-annotations.jar
+....
+
+You will also need the http://asm.ow2.org/[asm] jar, which you can obtain from link:{MVNCENTRAL}/org/eclipse/jetty/orbit/org.objectweb.asm/3.3.1.v201105211655/org.objectweb.asm-3.3.1.v201105211655.jar[this link.]
+
+==== Example
+
+Here's an example application that sets up a Jetty server, performs some setup to ensure that annotations are scanned, and then deploys a webapp that uses annotations.
+This example also uses the @Resource annotation which involves JNDI, so we would also link:#jndi-embedded[add the necessary JNDI jars to the classpath].
+The example also adds in the configuration classes that are responsible for JNDI (see line 19).
+
+The code is as follows:
+
+[source, java, subs="{sub-order}"]
+----
+import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * ServerWithAnnotations
+ *
+ *
+ */
+public class ServerWithAnnotations
+{
+    public static final void main(String args[]) throws Exception
+    {
+        //Create the server
+        Server server = new Server(8080);
+
+        //Enable parsing of jndi-related parts of web.xml and jetty-env.xml
+        org.eclipse.jetty.webapp.Configuration.ClassList classlist = org.eclipse.jetty.webapp.Configuration.ClassList.setServerDefault(server);
+        classlist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration", "org.eclipse.jetty.plus.webapp.EnvConfiguration", "org.eclipse.jetty.plus.webapp.PlusConfiguration");
+        classlist.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration", "org.eclipse.jetty.annotations.AnnotationConfiguration");
+
+        //Create a WebApp
+        WebAppContext webapp = new WebAppContext();
+        webapp.setContextPath("/");
+        webapp.setWar("../../tests/test-webapps/test-servlet-spec/test-spec-webapp/target/test-spec-webapp-9.0.4-SNAPSHOT.war");
+        server.setHandler(webapp);
+
+        //Register new transaction manager in JNDI
+        //At runtime, the webapp accesses this as java:comp/UserTransaction
+        org.eclipse.jetty.plus.jndi.Transaction transactionMgr = new org.eclipse.jetty.plus.jndi.Transaction(new com.acme.MockUserTransaction());
+
+        //Define an env entry with webapp scope.
+        org.eclipse.jetty.plus.jndi.EnvEntry maxAmount = new org.eclipse.jetty.plus.jndi.EnvEntry (webapp, "maxAmount", new Double(100), true);
+
+
+        // Register a  mock DataSource scoped to the webapp
+        org.eclipse.jetty.plus.jndi.Resource mydatasource = new org.eclipse.jetty.plus.jndi.Resource(webapp, "jdbc/mydatasource", new com.acme.MockDataSource());
+
+        // Configure a LoginService
+        HashLoginService loginService = new HashLoginService();
+        loginService.setName("Test Realm");
+        loginService.setConfig("src/test/resources/realm.properties");
+        server.addBean(loginService);
+
+
+        server.start();
+        server.join();
+    }
+
+}
+----
+
+On line 19 the configuration classes responsible for setting up JNDI and `java:comp/env` are added.
+
+On line 20 we add in the configuration class that ensures annotations are inspected.
+
+On lines 30, 33 and 37 JNDI resources that we will be able to reference with @Resource annotations are configured.
+
+With the setup above, a servlet that uses annotations and Jetty will honour the annotations when the webapp is deployed can be created:
+
+[source, java, subs="{sub-order}"]
+----
+import javax.annotation.security.DeclareRoles;
+import javax.annotation.security.RunAs;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.annotation.WebInitParam;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.sql.DataSource;
+import javax.transaction.UserTransaction;
+
+/**
+ * AnnotationTest
+ *
+ * Use servlet 3.0 annotations from within Jetty.
+ *
+ * Also uses servlet 2.5 resource injection and lifecycle callbacks
+ */
+
+@RunAs("special")
+@WebServlet(urlPatterns = {"/","/test/*"}, name="AnnotationTest", initParams={@WebInitParam(name="fromAnnotation", value="xyz")})
+@DeclareRoles({"user","client"})
+public class AnnotationTest extends HttpServlet
+{
+    private DataSource myDS;
+
+    @Resource(mappedName="UserTransaction")
+    private UserTransaction myUserTransaction;
+
+    @Resource(mappedName="maxAmount")
+    private Double maxAmount;
+
+
+    @Resource(mappedName="jdbc/mydatasource")
+    public void setMyDatasource(DataSource ds)
+    {
+        myDS=ds;
+    }
+
+
+    @PostConstruct
+    private void myPostConstructMethod ()
+    {
+        System.err.println("PostConstruct called");
+    }
+
+
+    @PreDestroy
+    private void myPreDestroyMethod()
+    {
+        System.err.println("PreDestroy called");
+    }
+
+    public void init(ServletConfig config) throws ServletException
+    {
+        super.init(config);
+    }
+
+
+    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        doGet(request, response);
+    }
+
+    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        try
+        {
+            response.setContentType("text/html");
+            ServletOutputStream out = response.getOutputStream();
+            out.println("<html>");
+            out.println("<body>");
+            out.println("<h1>Results</h1>");
+            out.println(myDS.toString());
+            out.println("<br/>");
+            out.println(maxAmount.toString());
+            out.println("</body>");
+            out.println("</html>");
+            out.flush();
+        }
+        catch (Exception e)
+        {
+            throw new ServletException(e);
+        }
+    }
+}
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/annotations/using-annotations.adoc b/jetty-documentation/src/main/asciidoc/administration/annotations/using-annotations.adoc
new file mode 100644
index 0000000..32a0fcb
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/annotations/using-annotations.adoc
@@ -0,0 +1,157 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[using-annotations]]
+=== Working with Annotations
+
+==== Which Annotations Are Supported
+
+Jetty supports interpretation and application of the following annotations:
+
+* @Resource
+* @Resources
+* @PostConstruct
+* @PreDestroy
+* @DeclaredRoles
+* @RunAs
+* @MultipartConfig
+* @WebServlet
+* @WebFilter
+* @WebListener
+* @WebInitParam
+* @ServletSecurity, @HttpConstraint, @HttpMethodConstraint
+* @HandlesTypes (on ServletContainerInitializers)
+
+[[discoverable_introspectable_annotations]]
+==== Discovered vs Introspected Annotations
+
+Some types of annotation can be placed on any class, not necessarily just those with which the container interacts directly.
+These type of annotations are refered to as "discovered" to indicate that the container must take proactive action to go out and find them.
+The other type of annotation is call "introspected", meaning that they occur on classes with which the container interacts during their lifecycle (e.g. `javax.servlet.Servlet`, `javax.servlet.Filter`, ...etc.), and hence can be found by simple inspection of the class at that point.
+
+Some examples of discovered annotations are:
+
+* @WebServlet
+* @WebFilter
+* @WebListener
+
+Some examples of introspected annotations are:
+
+* @PostConstruct
+* @PreDestroy
+* @Resource
+
+[[jars-scanned-for-annotations]]
+==== Which Jar Files Are Scanned For Discovered Annotations
+
+The web.xml file can contain the attribute `metadata-complete`.
+If this is set to `true`, then _no_ scanning of discoverable annotations takes place.
+However, scanning of classes may _still_ occur because of http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html[javax.servlet.ServletContainerInitializer]s.
+Classes implementing this interface are found by Jetty using the http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html[javax.util.ServiceLoader] mechanism, and if one is present _and_ it includes the @HandlesTypes annotation, then Jetty must scan the class hierarchy of the web application.
+This may be very time-consuming if you have many jars in the container's path or in the webapp's WEB-INF/lib.
+
+If scanning is to take place - because either `metadata-complete` is `false` or missing, or because there are one or more http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html[javax.servlet.ServletContainerIntializer]s with @HandlesTypes - then Jetty must consider both the container's classpath and the webapp's classpath.
+
+By default, Jetty will _not_ scan any classes that are on the container's classpath.
+If you need to cause jars and classes that are on the container's classpath to be scanned, then you can use the link:#container-include-jar-pattern[`org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern`] link:#context_attributes[context attribute] to specify a pattern for jars and directories from the container's classpath to scan.
+
+By default Jetty will scan __all__classes from `WEB-INF/classes` and all jars from `WEB-INF/lib` according to the order, if any, established by absolute or relative ordering clauses in web.xml.
+If your webapp contains many jar files, you can significantly speed up deployment by omitting them from scanning.
+To do this, use the link:#web-inf-include-jar-pattern[org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern] link:#context_attributes[context attribute] to define the patterns of jars that you specifically want to be scanned.
+
+Note that if you have configured an link:#using-extra-classpath-method[extraClasspath] for the webapp, then it participates in the scanning process too.
+Any classes dirs are treated the same for scanning purposes as if they were in WEB-INF/classes and jars are treated as if they were in WEB-INF/lib.
+
+See also the next section on link:#servlet-container-initializers[ServletContainerInitializers] if you need to link:#servlet-container-initializers[control the order in which they are applied].
+
+==== Multi-threaded Annotation Scanning
+
+link:#jars-scanned-for-annotations[If annotation scanning is to be performed], by default Jetty will do it in a multi-threaded manner in order to complete it in the minimum amount of time.
+
+If for some reason you don't want multi-threaded scanning, you can configure Jetty to revert to single-threaded scanning.
+There are several ways to configure this:
+
+1.  Set the link:#context_attributes[context attribute] `org.eclipse.jetty.annotations.multiThreaded` to `false`
+2.  Set the link:#server_attributes[Server attribute] `org.eclipse.jetty.annotations.multiThreaded` to `false`
+3.  Set the System property `org.eclipse.jetty.annotations.multiThreaded` to `false`
+
+Method 1 will only affect the current webapp.
+Method 2 will affect all webapps deployed to the same Server instance.
+Method 3 will affect all webapps deployed in the same JVM.
+
+By default, Jetty will wait a maximum of 60 seconds for all of the scanning threads to complete.
+You can set this to a higher or lower number of seconds by doing one of the following:
+
+1.  Set the link:#context_attributes[context attribute] `org.eclipse.jetty.annotations.maxWait`
+2.  Set the link:#server_attributes[Server attribute] `org.eclipse.jetty.annotations.maxWait`
+3.  Set the System property `org.eclipse.jetty.annotations.maxWait`
+
+Method 1 will only affect the current webapp.
+Method 2 will affect all webapps deployed to the same Server instance.
+Method 3 will affect all webapps deployed in the same JVM.
+
+[[servlet-container-initializers]]
+==== ServletContainerInitializers
+
+The http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html[javax.servlet.ServletContainerInitializer] class can exist in: the container's classpath, the webapp's `WEB-INF/classes` directory, the webapp's `WEB-INF/lib` jars, or any external link:#using-extra-classpath-method[extraClasspath] that you have configured on the webapp.
+
+The http://jcp.org/aboutJava/communityprocess/final/jsr340/[Servlet Specification] does not define any order in which a `ServletContainerInitializer` must be called when the webapp starts.
+By default Jetty will call them in the following order:
+
+1.  ServletContainerInitializers from the container's classpath
+2.  ServletContainerInitializers from WEB-INF/classes
+3.  ServletContainerInitializers from WEB-INF/lib jars __in the order established in web.xml__, or in the order that the SCI is returned by the http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html[javax.util.ServiceLoader] if there is _no_ ordering
+
+As is the case with annotation scanning, the link:#using-extra-classpath-method[extraClasspath] is fully considered for `ServletContainerInitializer` callbacks. `ServletContainerInitializer` derived from a classes directory on the `extraClasspath` and jars from an `extraClasspath` for the webapp are called in step 2 and 3, respectively.
+
+===== Controlling the order of ServletContainerInitializer invocation
+
+If you need `ServletContainerInitializer` classes called in a specific order that is different from that outlined above, you can use the link:#context_attributes[context attribute] `org.eclipse.jetty.containerInitializerOrder`.
+Set them to a list of comma separated class names of `ServletContainerInitializers` in the order that you want them applied.
+You may optionally use the wildcard character "*" *once* in the list.
+It will match all `ServletContainerInitializer` classed not explicitly named in the list.
+
+Here is an example, setting the context attribute in code (although you can also do the link:#intro-jetty-configuration-webapps[same in xml]):
+
+[source, java, subs="{sub-order}"]
+----
+WebAppContext context = new WebAppContext();
+context.setAttribute("org.eclipse.jetty.containerInitializerOrder",
+                     "org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer, com.acme.Foo.MySCI, *");
+----
+
+In this example, we ensure that the `WebSocketServerContainerInitializer` is the very first `ServletContainerInitializer` that is called, followed by MySCI and then any other `ServletContainerInitializer` instances that were discovered but not yet called.
+
+[[excluding-scis]]
+===== Excluding ServletContainerInitializers
+
+By default, as according to the Servlet Specification, all `ServletContainerInitializer` that are discovered are invoked (see above for how to control the invocation order).
+Sometimes, depending on your requirements, you may need to prevent some being called at all.
+
+In this case, you can define the `org.eclipse.jetty.containerInitializerExclusionPattern` link:#context_attributes[context attribute].
+This is a regular expression that defines http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html[patterns] of classnames that you want to exclude.
+Here's an example, setting the context attribute in code, although you may do exactly the link:#intro-jetty-configuration-webapps[same in xml]:
+
+[source, java, subs="{sub-order}"]
+----
+WebAppContext context = new WebAppContext();
+context.setAttribute("org.eclipse.jetty.containerInitializerExclusionPattern",
+                     "com.acme.*|com.corp.SlowContainerInitializer");
+----
+
+In this example we exclude *all* `ServletContainerInitializer` instances in the com.acme package, and the `SlowContainerInitializer`.
+
+It is possible to use exclusion and ordering together to control `ServletContainerInitializer` invocation - the exclusions will be applied before the ordering.
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/balancer-servlet.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/balancer-servlet.adoc
new file mode 100644
index 0000000..b13c049
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/balancer-servlet.adoc
@@ -0,0 +1,38 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[balancer-servlet]]
+=== Balancer Servlet
+
+[[balancer-servlet-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.proxy.BalancerServlet`
+* Maven Artifact: org.eclipse.jetty:jetty-proxy
+* Javadoc: {JDURL}/org/eclipse/jetty/proxy/BalancerServlet.html
+* Xref: {JXURL}/org/eclipse/jetty/proxy/BalancerServlet.html
+
+[[balancer-servlet-usage]]
+==== Usage
+
+The Balancer servlet allows for simple, sticky round robin load balancing leveraging the `ProxyServlet` that is distributed with Jetty.
+
+In addition to the parameters for `ProxyServlet`, the following are available for the balancer servlet:
+
+stickySessions::
+True if sessions should be sticky for subsequent requests
+balancerMember.<name>.proxyTo::
+One of more of these are required and will be the locations that are used to proxy traffic to.
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/cgi-servlet.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/cgi-servlet.adoc
new file mode 100644
index 0000000..5a55a96
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/cgi-servlet.adoc
@@ -0,0 +1,43 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[cgi-servlet]]
+=== CGI Servlet
+
+[[cgi-servlet-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.servlets.CGI`
+* Maven Artifact: org.eclipse.jetty:jetty-servlets
+* Javadoc: {JDURL}/org/eclipse/jetty/servlets/CGI.html
+* Xref: {JXURL}/org/eclipse/jetty/servlets/CGI.html
+
+[[cgi-servlet-usage]]
+==== Usage
+
+The CGI servlet class extends the abstract HttpServlet class.
+When the init parameter is called, the cgi bin directory is set with the `cgibinResourceBase`.
+Otherwise, it defaults to the resource base of the context.
+
+The cgi bin uses three parameters:
+
+commandPrefix::
+The init parameter obtained when there is a prefix set to all commands directed to the method exec.
+Path::
+An init parameter passed to the exec environment as a PATH.
+This must be run unpacked somewhere in the filesystem.
+ENV_::
+An init parameter that points to an environment variable with the name stripped of the leading ENV_ and using the init parameter value.
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/chapter.adoc
new file mode 100644
index 0000000..9e3a200
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/chapter.adoc
@@ -0,0 +1,44 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[advanced-extras]]
+== Provided Servlets, Filters, and Handlers
+
+Jetty ships with a bundle of servlets that interact with the key classes. 
+Most are in the org.eclipse.jetty.servlets package. 
+These servlets and filters are among the principle elements of Jetty as a component-based infrastructure that holds and runs J2EE applications. 
+As described, they play a major role in running and maintaining the Jetty server.
+
+Also included are a number of Jetty specific handlers that allow access to internals of jetty that would not normally be exposed and are very useful testing environments and many production scenarios.
+
+include::default-servlet.adoc[]
+include::proxy-servlet.adoc[]
+include::balancer-servlet.adoc[]
+include::cgi-servlet.adoc[]
+include::qos-filter.adoc[]
+include::dos-filter.adoc[]
+include::gzip-filter.adoc[]
+include::cross-origin-filter.adoc[]
+include::resource-handler.adoc[]
+include::debug-handler.adoc[]
+include::statistics-handler.adoc[]
+include::ipaccess-handler.adoc[]
+include::moved-context-handler.adoc[]
+include::shutdown-handler.adoc[]
+include::default-handler.adoc[]
+include::error-handler.adoc[]
+include::rewrite-handler.adoc[]
+
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/cross-origin-filter.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/cross-origin-filter.adoc
new file mode 100644
index 0000000..318557b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/cross-origin-filter.adoc
@@ -0,0 +1,95 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[cross-origin-filter]]
+=== Cross Origin Filter
+
+[[cross-origin-filter-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.servlets.CrossOriginFilter`
+* Maven Artifact: org.eclipse.jetty:jetty-servlets
+* Javadoc: {JDURL}/org/eclipse/jetty/servlets/CrossOriginFilter.html
+* Xref: {JXURL}/org/eclipse/jetty/servlets/CrossOriginFilter.html
+
+[[cross-origin-filter-usage]]
+==== Usage
+
+HTTP requests made from a script are subject to well known restrictions, the most prominent being the same domain policy.
+
+Firefox 3.5 introduced support for W3C's Access Control for Cross-Site Requests specification, which requires a compliant client (for example, Firefox 3.5) and a compliant server (via this servlet filter).
+
+This filter implements the required bits to support the server-side contract of the specification, and will allow a compliant client to perform cross-domain requests via the standard XMLHttpRequest object.
+If the client does not issue a compliant cross-domain request, this filter does nothing, and its overhead is the check of the presence of the cross-domain HTTP header.
+
+This is extremely useful in CometD web applications where it is now possible to perform cross-domain long polling without using script injection (also known as the JSONP transport), and therefore removing all the downsides that the JSONP transport has (it's chattier, does not react quickly to failures, has a message size limit, uses GET instead of POST, etc.).
+
+[[cross-origin-setup]]
+==== Setup
+
+You will need to put the `jetty-servlets.jar` file onto your classpath.
+If you are creating a webapp, ensure that this jar is included in your webapp's `WEB-INF/lib`.
+Or, if you are running Jetty embedded you will need to ensure that `jetty-servlets.jar` is on the execution classpath.
+You can download the `jetty-servlets.jar` from the Maven Central Repository at http://central.maven.org/maven2/org/eclipse/jetty/jetty-servlets/.
+It is also available as part of the Jetty distribution in the `$JETTY_HOME/lib` directory.
+
+[[cross-origin-config]]
+==== Configuration
+
+This is a regular servlet filter that must be configured in `web.xml`.
+
+It supports the following configuration parameters:
+
+allowedOrigins::
+A comma separated list of origins that are allowed to access the resources.
+Default value is: * (all origins)
+allowedMethods::
+A comma separated list of HTTP methods that are allowed to be used when accessing the resources.
+Default value is: GET,POST,HEAD
+allowedHeaders::
+A comma separated list of HTTP headers that are allowed to be specified when accessing the resources.
+Default value is: X-Requested-With,Content-Type,Accept,Origin
+allowCredentials::
+A boolean indicating if the resource allows requests with credentials.
+Default value is: true
+preflightMaxAge::
+The number of seconds that preflight requests can be cached by the client.
+Default value is 1800 seconds (30 minutes)
+chainPreflight::
+If true preflight requests are chained to their target resource for normal handling (as an OPTION request).
+Otherwise the filter will response to the preflight.
+Default is true.
+exposedHeaders::
+A comma separated list of HTTP headers that are allowed to be exposed on the client.
+Default value is the empty list.
+
+A typical configuration could be:
+
+[source, xml, subs="{sub-order}"]
+----
+<web-app>
+
+    <filter>
+        <filter-name>cross-origin</filter-name>
+        <filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
+    </filter>
+    <filter-mapping>
+        <filter-name>cross-origin</filter-name>
+        <url-pattern>/cometd/*</url-pattern>
+    </filter-mapping>
+
+</web-app>
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/debug-handler.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/debug-handler.adoc
new file mode 100644
index 0000000..b084c40
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/debug-handler.adoc
@@ -0,0 +1,67 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[debug-handler]]
+=== Debug Handler
+
+[[debug-handler-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.server.handler.DebugHandler`
+* Maven Artifact: org.eclipse.jetty:jetty-server
+* Javadoc: {JDURL}/org/eclipse/jetty/server/handler/DebugHandler.html
+* Xref: {JXURL}/org/eclipse/jetty/server/handler/DebugHandler.html
+
+[[debug-handler-usage]]
+==== Usage
+
+A simple handler that is useful to debug incoming traffic.
+It will log entry and exit points of HTTP requests as well as the response code.
+
+==== Usage in standard distribution
+
+The debug handler can be added to Jetty by activating the debug module.
+
+==== Embedded usage
+
+[source, java, subs="{sub-order}"]
+----
+Server server = new Server(8080);
+RolloverFileOutputStream outputStream = new RolloverFileOutputStream("MeinLogPfad/yyyy_mm_dd.request.log", true,10);
+
+DebugHandler debugHandler = new DebugHandler();
+debugHandler.setOutputStream(outputStream);
+debugHandler.setHandler(server.getHandler());
+
+server.setHandler(debugHandler);
+server.start();
+----
+
+==== Example output
+
+[source,bash]
+----
+15:14:05.838:qtp551889550-13-selector-0 OPENED HttpConnection@e910ee4{IDLE},g=HttpGenerator{s=START},p=HttpParser{s=START,0 of 0}
+15:14:05.846:qtp551889550-57:http://0:0:0:0:0:0:0:1:8080/ REQUEST 0:0:0:0:0:0:0:1 GET __utma=111872281.10102721.1321534299.1369833564.1370447492.35; __utmz=111872281.1321534299.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _opt_vi_RPY720HZ=75E12E63-0CD0-4D6F-8383-C90D5C8397C7; Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:22.0) Gecko/20100101 Firefox/22.0
+15:14:05.894:qtp551889550-57:http://0:0:0:0:0:0:0:1:8080/ RESPONSE 200 null
+15:14:05.959:qtp551889550-59:http://0:0:0:0:0:0:0:1:8080/jetty.css REQUEST 0:0:0:0:0:0:0:1 GET __utma=111872281.10102721.1321534299.1369833564.1370447492.35; __utmz=111872281.1321534299.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _opt_vi_RPY720HZ=75E12E63-0CD0-4D6F-8383-C90D5C8397C7; visited=yes; Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:22.0) Gecko/20100101 Firefox/22.0
+15:14:05.962:qtp551889550-59:http://0:0:0:0:0:0:0:1:8080/jetty.css RESPONSE 200 null
+15:14:06.052:qtp551889550-57:http://0:0:0:0:0:0:0:1:8080/images/jetty-header.jpg REQUEST 0:0:0:0:0:0:0:1 GET __utma=111872281.10102721.1321534299.1369833564.1370447492.35; __utmz=111872281.1321534299.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _opt_vi_RPY720HZ=75E12E63-0CD0-4D6F-8383-C90D5C8397C7; visited=yes; Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:22.0) Gecko/20100101 Firefox/22.0
+15:14:06.055:qtp551889550-57:http://0:0:0:0:0:0:0:1:8080/images/jetty-header.jpg RESPONSE 200 null
+15:14:07.248:qtp551889550-59:http://0:0:0:0:0:0:0:1:8080/favicon.ico REQUEST 0:0:0:0:0:0:0:1 GET __utma=111872281.10102721.1321534299.1369833564.1370447492.35; __utmz=111872281.1321534299.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _opt_vi_RPY720HZ=75E12E63-0CD0-4D6F-8383-C90D5C8397C7; visited=yes; Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:22.0) Gecko/20100101 Firefox/22.0
+15:14:07.251:qtp551889550-59:http://0:0:0:0:0:0:0:1:8080/favicon.ico RESPONSE 404 text/html;charset=ISO-8859-1
+15:14:09.330:qtp551889550-57 CLOSED HttpConnection@e910ee4{INTERESTED},g=HttpGenerator{s=START},p=HttpParser{s=START,0 of -1}
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/default-handler.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/default-handler.adoc
new file mode 100644
index 0000000..a0efcd4
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/default-handler.adoc
@@ -0,0 +1,51 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[default-handler]]
+=== Default Handler
+
+[[default-handler-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.server.handler.DefaultHandler`
+* Maven Artifact: org.eclipse.jetty:jetty-server
+* Javadoc: {JDURL}/org/eclipse/jetty/server/handler/DefaultHandler.html
+* Xref: {JXURL}/org/eclipse/jetty/server/handler/DefaultHandler.html
+
+[[default-handler-usage]]
+==== Usage
+
+A simple handler that is useful to terminate handler chains with a clean fashion.
+As in the example below, if a resource to be served is not matched within the resource handler the `DefaultHandler` will take care of producing a 404 page.
+This class is a useful template to either extend and embrace or simply provide a similar implementation for customizing to your needs.
+There is also an link:#error-handler[Error Handler] that services errors related to the servlet api specification, so it is best to not get the two confused.
+
+_____
+[NOTE]
+The `DefaultHandler` will also handle serving out the `flav.ico` file should a request make it through all of the other handlers without being resolved.
+_____
+
+[source, java, subs="{sub-order}"]
+----
+    Server server = new Server(8080);
+    HandlerList handlers = new HandlerList();
+    ResourceHandler resourceHandler = new ResourceHandler();
+    resourceHandler.setBaseResource(Resource.newResource("."));
+    handlers.setHandlers(new Handler[]
+    { resourceHandler, new DefaultHandler() });
+    server.setHandler(handlers);
+    server.start();
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/default-servlet.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/default-servlet.adoc
new file mode 100644
index 0000000..fb64444
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/default-servlet.adoc
@@ -0,0 +1,66 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[default-servlet]]
+=== Default Servlet
+
+[[default-servlet-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.servlet.DefaultServlet`
+* Maven Artifact: org.eclipse.jetty:jetty-servlet
+* Javadoc: {JDURL}/org/eclipse/jetty/servlet/DefaultServlet.html
+* Xref: {JXURL}/org/eclipse/jetty/servlet/DefaultServlet.html
+
+[[default-servlet-usage]]
+==== Usage
+
+The `DefaultServlet` implements the `ResourceFactory` interface and extends the `HttpServlet` abstract class.
+It is usually mapped to "/" and provides handling for static content, `OPTION` and `TRACE` methods for the context.
+The `MOVE` method is allowed if `PUT` and `DELETE` are allowed.
+See the `DefaultServlet` link:{JDURL}/org/eclipse/jetty/servlet/DefaultServlet.html[javadoc].
+
+[[default-servlet-init]]
+==== Init Parameters
+
+Jetty supports the following `initParameters`:
+
+acceptRanges::
+If true, range requests and responses are supported.
+dirAllowed::
+If true, directory listings are returned if no welcome file is found.
+Otherwise 403 Forbidden displays.
+redirectWelcome::
+If true, welcome files are redirected rather that forwarded.
+gzip::
+If set to true, then static content is served as gzip content encoded if a matching resource is found ending with ".gz".
+resourceBase::
+Set to replace the context resource base.
+aliases::
+If true, aliases of resources are allowed (that is, symbolic links and caps variations) and may bypass security constraints.
+maxCacheSize::
+Maximum total size of the cache or 0 for no cache.
+maxCachedFileSize::
+Maximum size of a file to cache.
+maxCachedFiles::
+Maximum number of files to cache.
+useFileMappedBuffer::
+If set to true, mapped file buffer serves static content.
+Setting this value to false means that a direct buffer is used instead of a mapped file buffer.
+By default, this is set to true.
+otherGzipFileExtensions::
+A comma separated list of other file extensions that signify that a file is gzip compressed.
+If you don't explicitly set this, it defaults to ".svgz".
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/dos-filter.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/dos-filter.adoc
new file mode 100644
index 0000000..c7ec4d3
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/dos-filter.adoc
@@ -0,0 +1,112 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[dos-filter]]
+=== Denial of Service Filter
+
+[[dos-filter-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.servlets.DoSFilter`
+* Maven Artifact: org.eclipse.jetty:jetty-servlets
+* Javadoc: {JDURL}/org/eclipse/jetty/servlets/DoSFilter.html
+* Xref: {JXURL}/org/eclipse/jetty/servlets/DoSFilter.html
+
+[[dos-filter-usage]]
+==== Usage
+
+The Denial of Service (DoS) filter limits exposure to request flooding, whether malicious, or as a result of a misconfigured client.
+The DoS filter keeps track of the number of requests from a connection per second.
+If the requests exceed the limit, Jetty rejects, delays, or throttles the request, and sends a warning message.
+The filter works on the assumption that the attacker might be written in simple blocking style, so by suspending requests you are hopefully consuming the attacker's resources.
+The DoS filter is related to the QoS filter, using Continuations to prioritize requests and avoid thread starvation.
+
+[[dos-filter-using]]
+==== Using the DoS Filter
+
+Jetty places throttled requests in a priority queue, giving priority first to authenticated users and users with an HttpSession, then to connections identified by their IP addresses.
+Connections with no way to identify them have lowest priority.
+To uniquely identify authenticated users, you should implement the The extractUserId(ServletRequest request) function.
+
+===== Required JARs
+
+To use the DoS Filter, these JAR files must be available in WEB-INF/lib:
+
+* $JETTY_HOME/lib/jetty-util.jar
+* $JETTY_HOME/lib/jetty-servlets.jar
+
+===== Sample Configuration
+
+Place the configuration in a webapp's `web.xml` or `jetty-web.xml`.
+The default configuration allows 25 requests per connection at a time, servicing more important requests first, and queuing up the rest.
+This example allow 30 requests at a time:
+
+[source, xml, subs="{sub-order}"]
+----
+<filter>
+   <filter-name>DoSFilter</filter-name>
+   <filter-class>org.eclipse.jetty.servlets.DoSFilter</filter-class>
+   <init-param>
+     <param-name>maxRequestsPerSec</param-name>
+     <param-value>30</param-value>
+   </init-param>
+ </filter>
+----
+
+[[dos-filter-init]]
+===== Configuring DoS Filter Parameters
+
+The following `init` parameters control the behavior of the filter:
+
+maxRequestsPerSec::
+Maximum number of requests from a connection per second.
+Requests in excess of this are first delayed, then throttled.
+Default is 25.
+
+delayMs::
+Delay imposed on all requests over the rate limit, before they are considered at all:
+* 100 (ms) = Default
+* -1 = Reject request
+* 0 = No delay
+* any other value = Delay in ms
+
+maxWaitMs::
+Length of time, in ms, to blocking wait for the throttle semaphore.
+Default is 50 ms.
+throttledRequests::
+Number of requests over the rate limit able to be considered at once.
+Default is 5.
+throttleMs::
+Length of time, in ms, to async wait for semaphore. Default is 30000L.
+maxRequestMs::
+Length of time, in ms, to allow the request to run. Default is 30000L.
+maxIdleTrackerMs::
+Length of time, in ms, to keep track of request rates for a connection, before deciding that the user has gone away, and discarding it.
+Default is 30000L.
+insertHeaders::
+If true, insert the DoSFilter headers into the response.
+Defaults to true.
+trackSessions::
+If true, usage rate is tracked by session if a session exists.
+Defaults to true.
+remotePort::
+If true and session tracking is not used, then rate is tracked by IP and port (effectively connection).
+Defaults to false.
+ipWhitelist::
+A comma-separated list of IP addresses that will not be rate limited.
+managedAttr::
+If set to true, then this servlet is set as a ServletContext attribute with the filter name as the attribute name.
+This allows a context external mechanism (for example, JMX via `ContextHandler.MANAGED_ATTRIBUTES`) to manage the configuration of the filter.
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/error-handler.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/error-handler.adoc
new file mode 100644
index 0000000..31b34b7
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/error-handler.adoc
@@ -0,0 +1,33 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[error-handler]]
+=== Error Handler
+
+[[error-handler-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.server.handler.ErrorHandler`
+* Maven Artifact: org.eclipse.jetty:jetty-server
+* Javadoc: {JDURL}/org/eclipse/jetty/server/handler/ErrorHandler.html
+* Xref: {JXURL}/org/eclipse/jetty/server/handler/ErrorHandler.html
+
+[[error-handler-usage]]
+==== Usage
+
+A handler that is used to report errors from servlet contexts and webapp contexts to report error conditions.
+Primarily handles setting the various servlet spec specific response headers for error conditions.
+Can be customized by extending; for more information on this see xref:custom-error-pages[].
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/gzip-filter.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/gzip-filter.adoc
new file mode 100644
index 0000000..9d4dff7
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/gzip-filter.adoc
@@ -0,0 +1,87 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[gzip-filter]]
+=== Gzip Handler
+
+[[gzip-filter-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.server.handler.gzip.GzipHandler`
+* Maven Artifact: org.eclipse.jetty:jetty-servlets
+* Javadoc:
+{JDURL}/org/eclipse/jetty/server/handler/gzip/GzipHandler.html
+* Xref: {JXURL}/org/eclipse/jetty/server/handler/gzip/GzipHandler.html
+
+[[gzip-filter-usage]]
+==== Usage
+
+The Jetty `GzipHandler` is a compression handler that you can apply to any dynamic resource (servlet).
+It fixes many of the bugs in commonly available compression filters: it works with asynchronous servlets; it handles all ways to set content length.
+It has been tested with Jetty continuations and suspending requests.
+Some user-agents might be excluded from compression to avoid common browser bugs (yes, this means IE!).
+
+The `GzipHandler` is added to the entire server by the `etc/jetty-gzip.xml` file from the `gzip.mod` module.
+It may also be added to individual contexts in a context xml file.
+
+[[gzip-filter-rules]]
+==== Gzip Rules
+
+`GzipHandler` will gzip or deflate the content of a response if:
+
+* It is mapped to a matching path
+* The request method is configured to support gzip
+* The request is not from an excluded User-Agent
+* accept-encoding header is set to either gzip, deflate or a combination of those
+* The response status code is >=200 and <300
+* The content length is unknown or more than the minGzipSize initParameter or the minGzipSize is 0(default)
+* The content-type does not match an excluded mime-type
+* No content-encoding is specified by the resource
+
+If both gzip and deflate are specified in the accept-encoding header, then gzip will be used.
+
+Compressing the content can greatly improve the network bandwidth usage, but at the cost of memory and CPU cycles.
+The link:#default-servlet[DefaultServlet] is capable of serving pre-compressed static content, which saves memory and CPU.
+By default, the `GzipHandler` will check to see if pre-compressed content exists, and pass the request through to be handled by the `DefaultServlet`.
+
+[[gzip-filter-init]]
+==== Gzip Configuration
+
+minGzipSize::
+Content will only be compressed if content length is either unknown or greater than `minGzipSize`.
+checkGzExists::
+True by default.
+If set to false, the handler will not check for pre-compressed content.
+compressionLevel::
+The compression level used for deflate compression. (0-9).
+includedMethods::
+List of HTTP methods to compress.
+If not set, only `GET` requests are compressed.
+includedMimeTypes::
+List of MIME types to compress.
+excludedMimeTypes::
+List of MIME types not to compress.
+excludedAgentPatterns::
+A list of regex patterns for User-Agent names from which requests should not be compressed.
+excludedPaths::
+List of paths to exclude from compression.
+Performs a `String.startsWith(String)` comparison to check if the path matches.
+If it does match then there is no compression.
+To match subpaths use excludePathPatterns instead.
+includedPaths::
+List of paths to consider for compression.
+includePaths::
+List of paths to definitely consider for compression.
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/ipaccess-handler.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/ipaccess-handler.adoc
new file mode 100644
index 0000000..489f119
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/ipaccess-handler.adoc
@@ -0,0 +1,73 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[ipaccess-handler]]
+=== IP Access Handler
+
+[[ipaccess-handler-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.server.handler.IPAccessHandler`
+* Maven Artifact: org.eclipse.jetty:jetty-server
+* Javadoc: {JDURL}/org/eclipse/jetty/server/handler/IPAccessHandler.html
+* Xref: {JXURL}/org/eclipse/jetty/server/handler/IPAccessHandler.html
+
+[[ipaccess-handler-usage]]
+==== Usage
+
+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.
+
+Typically, the black/white lists will be used in one of three modes:
+
+* Blocking a few specific IPs/URLs by specifying several black list entries.
+* Allowing only some specific IPs/URLs by specifying several white lists entries.
+* 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.
+
+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.
+
+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.
+
+* 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
+
+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).
+
+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.
+
+Examples of the entry specifications are:
+
+* 10.10.1.2 - all requests from IP 10.10.1.2
+* 10.10.1.2|/foo/bar - all requests from IP 10.10.1.2 to URI /foo/bar
+* 10.10.1.2|/foo/* - all requests from IP 10.10.1.2 to URIs starting with /foo/
+* 10.10.1.2|*.html - all requests from IP 10.10.1.2 to URIs ending with .html
+* 10.10.0-255.0-255 - all requests from IPs within 10.10.0.0/16 subnet
+* 10.10.0-.-255|/foo/bar - all requests from IPs within 10.10.0.0/16 subnet to URI /foo/bar
+* 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/
+
+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.
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/moved-context-handler.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/moved-context-handler.adoc
new file mode 100644
index 0000000..313c553
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/moved-context-handler.adoc
@@ -0,0 +1,71 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[moved-context-handler]]
+=== Moved Context Handler
+
+[[moved-context-handler-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.server.handler.MovedContextHandler`
+* Maven Artifact: org.eclipse.jetty:jetty-server
+* Javadoc:
+{JDURL}/org/eclipse/jetty/server/handler/MovedContextHandler.html
+* Xref:
+{JXURL}/org/eclipse/jetty/server/handler/MovedContextHandler.html
+
+[[moved-context-handler-usage]]
+==== Usage
+
+You can use the `MovedContextHandler` to relocate or redirect a context that has changed context path and/or virtual hosts.
+
+You can configure it to _permanently_ redirect the old URL to the new URL, in which case Jetty sends a Http Status code of 301 to the browser with the new URL.
+Alternatively, you can make it non-permanent, in which case Jetty sends a 302 Http Status code along with the new URL.
+
+In addition, as with any other context, you can configure a list of virtual hosts, meaning that this context responds only to requests to one of the listed host names.
+
+Suppose you have a context deployed at `/foo`, but that now you want to deploy at the root context `/` instead.
+
+* First you reconfigure and redeploy the context on Jetty.
+* Next you need a way to redirect all the browsers who have bookmarked `/foo` to the new path.
+You create a new xref:configuring-contexts[context xml] file in `{$jetty/.base}/webapps` and configure the `MovedContextHandler` to do the redirection from `/foo` to `/`.
+
+Below is an example.
+This is a permanent redirection, which also preserves `pathinfo` and query strings on the redirect:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.server.handler.MovedContextHandler">
+  <Set name="contextPath">/foo</Set>
+  <Set name="newContextURL">/</Set>
+  <Set name="permanent">true</Set>
+  <Set name="discardPathInfo">false</Set>
+  <Set name="discardQuery">false</Set>
+
+  <Set name="virtualHosts">
+    <Array type="String">
+          <Item>209.235.245.73</Item>
+          <Item>127.0.0.73</Item>
+          <Item>acme.org</Item>
+          <Item>www.acme.org</Item>
+          <Item>server.acme.org</Item>
+    </Array>
+  </Set>
+</Configure>
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/proxy-servlet.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/proxy-servlet.adoc
new file mode 100644
index 0000000..b2edcdb
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/proxy-servlet.adoc
@@ -0,0 +1,77 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[proxy-servlet]]
+=== Proxy Servlet
+
+[[proxy-servlet-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.proxy.ProxyServlet`
+* Maven Artifact: org.eclipse.jetty:jetty-proxy
+* Javadoc: {JDURL}/org/eclipse/jetty/proxy/ProxyServlet.html
+* Xref: {JXURL}/org/eclipse/jetty/proxy/ProxyServlet.html
+
+[[proxy-servlet-usage]]
+==== Usage
+
+An asynchronous servlet that forwards requests to another server either as a standard web reverse proxy (as defined by RFC2616) or as a transparent reverse proxy.
+Internally it uses the async jetty-client.
+
+To facilitate JMX monitoring, the `HttpClient` instance is set as context attribute, prefixed with the servlet's name and exposed by the mechanism provided by `ContextHandler.MANAGED_ATTRIBUTES`.
+
+[[proxy-servlet-init]]
+==== Init Parameters
+
+The following init parameters may be used to configure the servlet:
+
+hostHeader::
+  forces the host header to a particular value
+viaHost::
+  the name to use in the Via header: Via: http/1.1 <viaHost>
+whiteList::
+  comma-separated list of allowed proxy hosts
+blackList::
+  comma-separated list of forbidden proxy hosts
+
+
+In addition, there are a number of init parameters that can be used to configure the `HttpClient` instance used internally for the proxy.
+
+maxThreads::
+Default Value: 256
+The max number of threads of HttpClient's Executor
+
+maxConnections::
+Default Value: 32768
+The max number of connections per destination.
+RFC 2616 suggests that 2 connections should be opened per each destination, but browsers commonly open 6 or more.
+If this `HttpClient` is used for load testing, it is common to have only one destination (the server to load test), and it is recommended to set this value to a high value (at least as much as the threads present in the executor).
+
+idleTimeout::
+Default Value: 30000
+The idle timeout in milliseconds that a connection can be idle, that is without traffic of bytes in either direction.
+
+timeout::
+Default Value: 60000
+The total timeout in milliseconds for the request/response conversation.
+
+requestBufferSize::
+Default Value: 4096
+The size of the request buffer the request is written into.
+
+responseBufferSize::
+Default Value: 4096
+The size of the response buffer the response is written into.
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/qos-filter.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/qos-filter.adoc
new file mode 100644
index 0000000..f6f4385
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/qos-filter.adoc
@@ -0,0 +1,161 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[qos-filter]]
+=== Quality of Service Filter
+
+[[qos-filter-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.servlets.QoSFilter`
+* Maven Artifact: org.eclipse.jetty:jetty-servlets
+* Javadoc: {JDURL}/org/eclipse/jetty/servlets/QoSFilter.html
+* Xref: {JXURL}/org/eclipse/jetty/servlets/QoSFilter.html
+
+[[qos-filter-usage]]
+==== Usage
+
+Jetty supports Continuations, which allow non-blocking handling of HTTP requests, so that threads can be allocated in a managed way to provide application specific Quality of Service (QoS).
+The `QoSFilter` is a utility servlet filter that implements some QoS features.
+
+[[qos-understanding]]
+==== Understanding the Problem
+
+===== Waiting for Resources
+
+Web applications frequently use JDBC Connection pools to limit the simultaneous load on the database.
+This protects the database from peak loads, but makes the web application vulnerable to thread starvation.
+Consider a thread pool with 20 connections, being used by a web application that that typically receives 200 requests per second and each request holds a JDBC connection for 50ms.
+Such a pool can service on average 200*20*1000/50 = 400 requests per second.
+
+However, if the request rate rises above 400 per second, or if the database slows down (due to a large query) or becomes momentarily unavailable, the thread pool can very quickly accumulate many waiting requests.
+If, for example, the website is "slashdotted" or experiences some other temporary burst of traffic and the request rate rises from 400 to 500 requests per second, then 100 requests per second join those waiting for a JDBC connection.
+Typically, a web server's thread pool contains only a few hundred threads, so a burst or slow DB need only persist for a few seconds to consume the entire web server's thread pool; this is called thread starvation.
+The key issue with thread starvation is that it effects the entire web application, and potentially the entire web server.
+Even if the requests using the database are only a small proportion of the total requests on the web server, all requests are blocked because all the available threads are waiting on the JDBC connection pool.
+This represents non-graceful degradation under load and provides a very poor quality of service.
+
+===== Prioritizing Resources
+
+Consider a web application that is under extreme load.
+This load might be due to a popularity spike (slashdot), usage burst (Christmas or close of business), or even a denial of service attack.
+During such periods of load, it is often desirable not to treat all requests as equals, and to give priority to high value customers or administrative users.
+
+The typical behavior of a web server under extreme load is to use all its threads to service requests and to build up a backlog of unserviced requests.
+If the backlog grows deep enough, then requests start to timeout and users experience failures as well as delays.
+
+Ideally, the web application should be able to examine the requests in the backlog, and give priority to high value customers and administrative users.
+But with the standard blocking servlet API, it is not possible to examine a request without allocating a thread to that request for the duration of its handling.
+There is no way to delay the handling of low priority requests, so if the resources are to be reallocated, then the low priority requests must all be failed.
+
+[[qos-applying]]
+==== Applying the QoSFilter
+
+The Quality of Service Filter (QoSFilter) uses Continuations to avoid thread starvation, prioritize requests and give graceful degradation under load, to provide a high quality of service.
+When you apply the filter to specific URLs within a web application, it limits the number of active requests being handled for those URLs.
+Any requests in excess of the limit are suspended. When a request completes handling the limited URL, one of the waiting requests resumes and can be handled.
+You can assign priorities to each suspended request, so that high priority requests resume before lower priority requests.
+
+===== Required JARs
+
+To use the QoS Filter, these JAR files must be available in `WEB-INF/lib`:
+
+* $JETTY_HOME/lib/jetty-util.jar
+* $JETTY_HOME/lib/jetty-servlets.jar – contains QoSFilter
+
+===== Sample Configuration
+
+Place the configuration in a webapp's `web.xml` or `jetty-web.xml`.
+The default configuration processes ten requests at a time, servicing more important requests first and queuing up the rest.
+This example processes fifty requests at a time:
+
+[source, xml, subs="{sub-order}"]
+----
+<filter>
+   <filter-name>QoSFilter</filter-name>
+   <filter-class>org.eclipse.jetty.servlets.QoSFilter</filter-class>
+   <init-param>
+     <param-name>maxRequests</param-name>
+     <param-value>50</param-value>
+   </init-param>
+ </filter>
+----
+
+[[qos-filter-init]]
+===== Configuring QoS Filter Parameters
+
+A semaphore polices the `maxRequests` limit.
+The filter waits a short time while attempting to acquire the semaphore.
+The `waitMs` init parameter controls the wait, avoiding the expense of a suspend if the semaphore is shortly available.
+If the semaphore cannot be obtained, Jetty suspends the request for the default suspend period of the container or the value set as the `suspendMs` init parameter.
+
+The QoS filter uses the following init parameters:
+
+maxRequests::
+The maximum number of requests to be serviced at a time. The default is 10.
+maxPriority::
+The maximum valid priority that can be assigned to a request.
+A request with a high priority value is more important than a request with a low priority value. The default is 10.
+waitMS::
+The length of time, in milliseconds, to wait while trying to accept a new request.
+Used when the maxRequests limit is reached.
+Default is 50 ms.
+suspendMS::
+Length of time, in milliseconds, that the request will be suspended if it is not accepted immediately.
+If not set, the container's default suspend period applies. Default is -1 ms.
+managedAttr::
+If set to true, then this servlet is set as a `ServletContext` attribute with the filter name as the attribute name.
+This allows a context external mechanism (for example, JMX via `ContextHandler.MANAGED_ATTRIBUTES`) to manage the configuration of the filter.
+
+===== Mapping to URLs
+
+You can use the `<filter-mapping>` syntax to map the QoSFilter to a servlet, either by using the servlet name, or by using a URL pattern.
+In this example, a URL pattern applies the QoSFilter to every request within the web application context:
+
+[source, xml, subs="{sub-order}"]
+----
+<filter-mapping>
+   <filter-name>QoSFilter</filter-name>
+   <url-pattern>/*</url-pattern>
+ </filter-mapping>
+----
+
+===== Setting the Request Priority
+
+Requests with higher values have a higher priority.
+The default request priorities assigned by the QoSFilter are:
+
+* 2 -- For any authenticated request
+* 1 -- For any request with a non-new valid session
+* 0 -- For all other requests
+
+To customize the priority, subclass QoSFilter and then override the `getPriority(ServletRequest request)` method to return an appropriate priority for the request.
+You can then use this subclass as your QoS filter.
+Here's an example:
+
+[source, java, subs="{sub-order}"]
+----
+public class ParsePriorityQoSFilter extends QoSFilter
+ {
+     protected int getPriority(ServletRequest request)
+     {
+         String p = ((HttpServletRequest)request).getParameter("priority");
+         if (p!=null)
+             return Integer.parseInt(p);
+         return 0;
+     }
+ }
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/resource-handler.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/resource-handler.adoc
new file mode 100644
index 0000000..7a1de4d
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/resource-handler.adoc
@@ -0,0 +1,56 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[resource-handler]]
+=== Resource Handler
+
+[[resource-handler-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.server.handler.ResourceHandler`
+* Maven Artifact: org.eclipse.jetty:jetty-server
+* Javadoc: {JDURL}/org/eclipse/jetty/server/handler/ResourceHandler.html
+* Xref: {JXURL}/org/eclipse/jetty/server/handler/ResourceHandler.html
+
+[[resource-handler-usage]]
+==== Usage
+
+This handler will serve static content and handle If-Modified-Since headers and is suitable for simple serving of static content.
+
+____
+[IMPORTANT]
+There is no caching done with this handler, so if you are looking for a more fully featured way of serving static content look to the xref:default-servlet[].
+____
+
+____
+[NOTE]
+Requests for resources that do not exist are let pass (Eg no 404's).
+____
+
+==== Improving the Look and Feel
+
+The resource handler has a default stylesheet which you can change by calling `setStyleSheet(String location)` with the location of a file on the system that it can locate through the resource loading system.
+The default css is called `jetty-dir.css` and is located in the `jetty-util` package, pulled as a classpath resource from the `jetty-util` jar when requested through the `ResourceHandler`.
+
+==== Embedded Example
+
+The following is an example of a split fileserver, able to serve static content from multiple directory locations.
+Since this handler does not return 404's on content you are able to iteratively try multiple resource handlers to resolve content.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java[]
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/rewrite-handler.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/rewrite-handler.adoc
new file mode 100644
index 0000000..14ff266
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/rewrite-handler.adoc
@@ -0,0 +1,144 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[rewrite-handler]]
+=== Rewrite Handler
+
+The `RewriteHandler` matches a request against a set of rules, and modifies the request accordingly for any rules that match.
+The most common use is to rewrite request URIs, but it is capable of much more: rules can also be configured to redirect the response, set a cookie or response code on the response, modify the header, etc.
+
+[[rewrite-handler-metadata]]
+==== Info
+
+* Classname: org.eclipse.jetty.rewrite.handler.RewriteHandler
+* Maven artifact: org.eclipse.jetty:jetty-rewrite
+* Javadoc: {JDURL}/org/eclipse/jetty/rewrite/handler/RewriteHandler.html
+* Xref: {JXURL}/org/eclipse/jetty/rewrite/handler/RewriteHandler.html
+
+The standard Jetty distribution bundle contains the `jetty-rewrite` link:#startup-modules[module], so all you need to do is to enable it using one of the link:#start-jar[module commands], eg:
+
+[source, screen, subs="{sub-order}"]
+....
+$ java -jar start.jar --add-to-start=rewrite
+....
+
+_____
+[NOTE]
+If you are running the standard Jetty distribution with the sample test webapp, there will be a demo of the rewrite module at http://localhost:8080/test/rewrite/
+_____
+
+==== Usage
+
+The rewrite module enables the following Jetty xml config file on the execution path:
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-rewrite/src/main/config/etc/jetty-rewrite.xml[]
+----
+
+As the commented out code shows, you configure the `RewriteHandler` by adding various rules.
+
+There is an example of link:#rewrite-rules[rules] configuration in the standard distribution in the `demo-base/etc/demo-rewrite-rules.xml` file:
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/demo-rewrite-rules.xml[]
+----
+
+===== Embedded Example
+
+This is an example for embedded Jetty, which does something similar to the configuration file example above:
+
+[source, java, subs="{sub-order}"]
+----
+  Server server = new Server();
+
+  RewriteHandler rewrite = new RewriteHandler();
+  rewrite.setRewriteRequestURI(true);
+  rewrite.setRewritePathInfo(false);
+  rewrite.originalPathAttribute("requestedPath");
+
+  RedirectPatternRule redirect = new RedirectPatternRule();
+  redirect.setPattern("/redirect/*");
+  redirect.setReplacement("/redirected");
+  rewrite.addRule(redirect);
+
+  RewritePatternRule oldToNew = new RewritePatternRule();
+  oldToNew.setPattern("/some/old/context");
+  oldToNew.setReplacement("/some/new/context");
+  rewrite.addRule(oldToNew);
+
+  RewriteRegexRule reverse = new RewriteRegexRule();
+  reverse.setRegex("/reverse/([^/]*)/(.*)");
+  reverse.setReplacement("/reverse/$2/$1");
+  rewrite.addRule(reverse);
+
+  server.setHandler(rewrite);
+----
+
+[[rewrite-rules]]
+==== Rules
+
+There are several types of rules that are written extending useful base rule classes.
+
+===== PatternRule
+
+Matches against the request URI using the servlet pattern syntax.
+
+link:{JXURL}/org/eclipse/jetty/rewrite/handler/CookiePatternRule.html[CookiePatternRule]::
+Adds a cookie to the response.
+link:{JXURL}/org/eclipse/jetty/rewrite/handler/HeaderPatternRule.html[HeaderPatternRule]::
+Adds/modifies a header in the response.
+link:{JXURL}/org/eclipse/jetty/rewrite/handler/RedirectPatternRule.html[RedirectPatternRule]::
+Redirects the response.
+link:{JXURL}/org/eclipse/jetty/rewrite/handler/ResponsePatternRule.html[ResponsePatternRule]::
+Sends the response code (status or error).
+link:{JXURL}/org/eclipse/jetty/rewrite/handler/RewritePatternRule.html[RewritePatternRule]::
+Rewrite the URI by replacing the matched request path with a fixed string.
+
+===== RegexRule
+
+Matches against the request URI using regular expressions.
+
+link:{JXURL}/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.html[RedirectRegexRule]::
+Redirect the response.
+link:{JXURL}/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.html[RewriteRegexRule]::
+Rewrite the URI by matching with a regular expression.
+(The replacement string may use `Template:$n` to replace the nth capture group.)
+
+===== HeaderRule
+
+Match against request headers. Match either on a header name and specific value, or on the presence of a header (with any value).
+
+link:{JXURL}/org/eclipse/jetty/rewrite/handler/ForwardedSchemeHeaderRule.html[ForwardedSchemaHeaderRule]::
+Set the scheme on the request (defaulting to HTTPS).
+
+===== Others
+
+Extra rules that defy standard classification.
+
+link:{JXURL}/org/eclipse/jetty/rewrite/handler/MsieSslRule.html[MsieSslRule]::
+Disables the keep alive for SSL from IE5 or IE6.
+link:{JXURL}/org/eclipse/jetty/rewrite/handler/LegacyRule.html[LegacyRule]::
+Implements the legacy API of RewriteHandler
+
+===== RuleContainer
+
+Groups rules together.
+The contained rules will only be processed if the conditions for the `RuleContainer` evaluate to true.
+
+link:{JXURL}/org/eclipse/jetty/rewrite/handler/VirtualHostRuleContainer.html[VirtualHostRuleContainer]::
+Groups rules that apply only to a specific virtual host or a set of virtual hosts
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/shutdown-handler.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/shutdown-handler.adoc
new file mode 100644
index 0000000..4557f5c
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/shutdown-handler.adoc
@@ -0,0 +1,66 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[shutdown-handler]]
+=== Shutdown Handler
+
+[[shutdown-handler-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.server.handler.ShutdownHandler`
+* Maven Artifact: org.eclipse.jetty:jetty-server
+* Javadoc: {JDURL}/org/eclipse/jetty/server/handler/ShutdownHandler.html
+* Xref: {JXURL}/org/eclipse/jetty/server/handler/ShutdownHandler.html
+
+[[shutdown-handler-usage]]
+==== Usage
+
+A handler that shuts the server down on a valid request.
+This is used to perform "soft" restarts from Java.
+If `_exitJvm` is set to true a hard `System.exit()` call is being made.
+
+This is an example of how you can setup this handler directly with the Server.
+It can also be added as a part of handler chain or collection.
+
+[source, java, subs="{sub-order}"]
+----
+    Server server = new Server(8080);
+    HandlerList handlers = new HandlerList();
+    handlers.setHandlers(new Handler[]
+    { someOtherHandler, new ShutdownHandler(server,"secret password") });
+    server.setHandler(handlers);
+    server.start();
+----
+
+This is an example that you can use to call the shutdown handler from within java.
+
+[source, java, subs="{sub-order}"]
+----
+   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);
+        }
+    }
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/statistics-handler.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/statistics-handler.adoc
new file mode 100644
index 0000000..1961540
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/extras/statistics-handler.adoc
@@ -0,0 +1,117 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[statistics-handler]]
+=== Statistics Handler
+
+[[statistics-handler-metadata]]
+==== Info
+
+* Classname: `org.eclipse.jetty.server.handler.StatisticsHandler`
+* Maven Artifact: org.eclipse.jetty:jetty-server
+* Javadoc:
+{JDURL}/org/eclipse/jetty/server/handler/StatisticsHandler.html
+* Xref: {JXURL}/org/eclipse/jetty/server/handler/StatisticsHandler.html
+
+[[statistics-handler-usage]]
+==== Usage
+
+Jetty currently has two levels of request statistic collection:
+
+* Subclasses of `AbstractConnector` class optionally can collect statistics about connections as well as number of requests.
+* The `StatisticsHandler` class may be used to collect request statistics.
+
+In addition to these, subclasses of the `AbstractSessionHandler` class optionally can collect session statistics.
+
+`AbstractConnector` and `AbstractSessionHandler` statistics are turned off by default and must either be configured manually for each instance or turned on via JMX interface.
+The `StatisticsHandler` is not included in default Jetty configuration, and needs to be configured manually.
+
+_____
+[NOTE]
+To view statistics, you have to be able to connect to Jetty using either JConsole or some other JMX agent. See xref:using-jmx[] for more information.
+_____
+
+[[connector-statistics]]
+==== Connector statistics
+
+Detailed statistics on connection duration and number of requests are only collated when a connection is closed.
+The current and maximum number of connections are the only "live" statistics.
+//To learn how to turn on connector statistics please see the Jetty Statistics tutorial, although this is not recommended and it is best to use a JMX agent to select statistics only when needed.
+
+The following example shows how to turn on connector statistics in Jetty xml.
+This example comes from within `jetty-http.xml`.
+
+[source, xml, subs="{sub-order}"]
+----
+  <Call name="addConnector">
+    <Arg>
+      <New class="org.eclipse.jetty.server.ServerConnector">
+        <Arg name="server"><Ref refid="Server" /></Arg>
+        <Arg name="factories">
+          <Array type="org.eclipse.jetty.server.ConnectionFactory">
+            <Item>
+              <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+                <Arg name="config"><Ref refid="httpConfig" /></Arg>
+              </New>
+            </Item>
+          </Array>
+        </Arg>
+        <Set name="host"><Property name="jetty.host" /></Set>
+        <Set name="port"><Property name="jetty.http.port" default="8080" /></Set>
+        <Set name="idleTimeout">30000</Set>
+        <!-- Enable Connection Statistics -->
+        <Call name="addBean">
+          <Arg>
+              <New id="ConnectionStatistics" class="org.eclipse.jetty.io.ConnectionStatistics"/>
+          </Arg>
+        </Call>
+      </New>
+    </Arg>
+  </Call>
+----
+
+[[request-statistics]]
+==== Request Statistics
+
+To collect request statistics a `StatisticsHandler` must be configured as one of the handlers of the server.
+Typically this can be done as the top level handler, but you may choose to configure a statistics handler for just one context by creating a context configuration file.
+You can enable the `StatisticsHandler` by activating the `stats` modules on the command line.
+
+[source, screen, subs="{sub-order}"]
+....
+$ java -jar {$jetty.home}/start.jar --add-to-start=stats
+....
+
+Alternately, if you are making multiple changes to the Jetty configuration, you could include statistics handler configuration into your own Jetty xml configuration.
+The following fragment shows how to configure a top level statistics handler:
+
+[source, xml, subs="{sub-order}"]
+----
+    <Get id="oldhandler" name="handler" />
+  <Set name="handler">
+    <New id="StatsHandler" class="org.eclipse.jetty.server.handler.StatisticsHandler">
+      <Set name="handler"><Ref refid="oldhandler" /></Set>
+    </New>
+  </Set>
+----
+
+[[session-statistics]]
+==== Session Statistics
+
+Session handling is built into Jetty for any servlet or webapp context.
+Detailed statistics on session duration are only collated when a session is closed.
+The current, minimum, and maximum number of sessions are the only "live" statistics.
+The session statistics are enabled by default and do not need to be configured.
diff --git a/jetty-documentation/src/main/asciidoc/administration/fastcgi/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/fastcgi/chapter.adoc
new file mode 100644
index 0000000..1c034f8
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/fastcgi/chapter.adoc
@@ -0,0 +1,21 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[fastcgi]]
+== FastCGI Support
+
+include::fastcgi-intro.adoc[]
+include::configuring-fastcgi.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/administration/fastcgi/configuring-fastcgi.adoc b/jetty-documentation/src/main/asciidoc/administration/fastcgi/configuring-fastcgi.adoc
new file mode 100644
index 0000000..0580e61
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/fastcgi/configuring-fastcgi.adoc
@@ -0,0 +1,178 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-fastcgi]]
+=== Configuring Jetty for FastCGI
+
+In this section you will see how to configure Jetty to serve WordPress via FastCGI.
+
+The first step is to have WordPress installed on your server machine, for example under `/var/www/wordpress`.
+For more information about how to install WordPress, please refer to the https://codex.wordpress.org/Installing_WordPress[WordPress Installation Guide].
+
+The second step is to install `php-fpm` and make sure it is configured to listen on a TCP socket; typically it is configured to listen to `localhost:9000`.
+
+The third step is to install Jetty, for example under `/opt/jetty`, called in the following `$JETTY_HOME`.
+Refer to xref:jetty-downloading[] for more information about how to install Jetty.
+
+The fourth step is to create a Jetty base directory (see xref:startup-base-and-home[]), called in the following `$JETTY_BASE`, where you setup the configuration needed to support FastCGI in Jetty, and configure the `fcgi`, `http` and `deploy` modules, so that Jetty will be able to accept HTTP requests from browsers, convert them in FastCGI, and proxy them to `php-fpm`:
+
+[source, screen, subs="{sub-order}"]
+....
+$ mkdir -p /usr/jetty/wordpress
+$ cd /usr/jetty/wordpress
+$ java -jar $JETTY_HOME/start.jar --add-to-startd=fcgi,http,deploy
+....
+
+Therefore `$JETTY_BASE=/usr/jetty/wordpress`.
+
+The fifth step is to deploy the web application that provides the proxying of client requests to the FastCGI server, `php-fpm`.
+Typically this is done by deploying a `*.war` file in the `$JETTY_BASE/webapps` directory.
+For FastCGI there is no web application that needs developed - all the work has already been done for you by Jetty.
+As such you only need to deploy a Jetty context XML file that configures the web application directly.
+Copy and paste the following content as `$JETTY_BASE/webapps/jetty-wordpress.xml`:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+<Configure class="org.eclipse.jetty.servlet.ServletContextHandler">
+
+    <New id="root" class="java.lang.String">
+        <Arg>/var/www/wordpress</Arg>
+    </New>
+
+    <Set name="contextPath">/</Set>
+    <Set name="resourceBase"><Ref refid="root" /></Set>
+    <Set name="welcomeFiles">
+        <Array type="string"><Item>index.php</Item></Array>
+    </Set>
+
+    <Call name="addFilter">
+        <Arg>org.eclipse.jetty.fcgi.server.proxy.TryFilesFilter</Arg>
+        <Arg>/*</Arg>
+        <Arg>
+            <Call name="of" class="java.util.EnumSet">
+                <Arg><Get name="REQUEST" class="javax.servlet.DispatcherType" /></Arg>
+            </Call>
+        </Arg>
+        <Call name="setInitParameter">
+            <Arg>files</Arg>
+            <Arg>$path /index.php?p=$path</Arg>
+        </Call>
+    </Call>
+
+    <Call name="addServlet">
+        <Arg>
+            <New class="org.eclipse.jetty.servlet.ServletHolder">
+                <Arg>default</Arg>
+                <Arg>
+                    <Call name="forName" class="java.lang.Class">
+                        <Arg>org.eclipse.jetty.servlet.DefaultServlet</Arg>
+                    </Call>
+                </Arg>
+                <Call name="setInitParameter">
+                    <Arg>dirAllowed</Arg>
+                    <Arg>false</Arg>
+                </Call>
+            </New>
+        </Arg>
+        <Arg>/</Arg>
+    </Call>
+
+    <Call name="addServlet">
+        <Arg>org.eclipse.jetty.fcgi.server.proxy.FastCGIProxyServlet</Arg>
+        <Arg>*.php</Arg>
+        <Call name="setInitParameter">
+            <Arg>proxyTo</Arg>
+            <Arg>http://localhost:9000</Arg>
+        </Call>
+        <Call name="setInitParameter">
+            <Arg>prefix</Arg>
+            <Arg>/</Arg>
+        </Call>
+        <Call name="setInitParameter">
+            <Arg>scriptRoot</Arg>
+            <Arg><Ref refid="root" /></Arg>
+        </Call>
+        <Call name="setInitParameter">
+            <Arg>scriptPattern</Arg>
+            <Arg>(.+?\\.php)</Arg>
+        </Call>
+    </Call>
+
+</Configure>
+
+----
+
+An explanation of the above contents:
+
+* Linne 6 specifies the WordPress installation directory, in this example `/var/www/wordpress` (as defined in the first step).
+* Line 9 it is specified the context path at which WordPress will be served, in this example at the root context path `/`.
+* Line 10 specifies the resource base of the context, also set to the WordPress installation directory.
+This allows Jetty to serve static resources directly from the WordPress installation directory.
+* Line 12 specifies the welcome file as `index.php`, so that Jetty can perform the proper redirects in case of URIs ending with the `/` character.
+* Line 15 specifies the `TryFilesFilter`, a Servlet Filter that has been inspired by the http://wiki.nginx.org/HttpCoreModule#try_files[try_files] functionality offered by Nginx.
+This filter tries to serve the resource from the file system first, and if the resource is not found it forwards the request as `index.php?p=$path`, which will match the proxy servlet defined below.
+Refer to the link:{JDURL}/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.html[TryFilesFilter] documentation for further information.
+* Line 29specifies Jetty's `DefaultServlet` to serve static content such as CSS files, JavaScript files, etc. `DefaultServlet` will serve these files by looking in the resource base of the context, defined at line 10 (see above).
+* Line 47 specifies the `FastCGIProxyServlet`, a Servlet that proxies HTTP requests arriving from clients to FastCGI requests to the FastCGI server.
+* Line 52 specifies the TCP address of the FastCGI server (`php-fpm`), where HTTP requests are forwarded as FastCGI requests.
+* Line 60 specifies once again the WordPress installation directory, so that the `FastCGIProxyServlet` can pass this information to the FastCGI server.
+* Line 64 specifies a regular expression that matches request URIs performed to this servlet, in addition to the standard URL mapping defined by Servlet at line 49.
+Refer to the link:{JDURL}/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.html[FastCGIProxyServlet] documentation for further information.
+
+The last step is to start Jetty (see xref:startup[]) and navigate to `http://localhost:8080` with your browser and enjoy WordPress:
+
+[source, screen, subs="{sub-order}"]
+....
+$ cd $JETTY_BASE
+$ java -jar /opt/jetty/start.jar
+....
+
+[[configuring-fastcgi-http2]]
+==== Configuring Jetty to Proxy HTTP/2 to FastCGI
+
+In order to configure Jetty to listen for HTTP/2 requests from clients that are HTTP/2 enabled and forward them to the FastCGI server as FastCGI requests, you need to enable the `http2` module, which in turn will require a TLS connector and consequently a keystore to read the key material required by TLS.
+
+Enabling the `http2` is easy; in additions to the modules you have enabled above, add the `http2` module:
+
+[source, screen, subs="{sub-order}"]
+....
+$ cd $JETTY_BASE
+$ java -jar $JETTY_HOME/start.jar --add-to-startd=http2
+....
+
+The command above adds the `http2` module (and its dependencies) to the existing modules and uses the default Jetty keystore to provide the key material required by TLS.
+You will want to use your own keystore with your own private key and certificate for your own domain.
+
+Remember that by adding the `http2` module, you will start two JVMs: one that reads the configuration, and one that has the ALPN boot boot jar in the boot classpath, as explained in xref:http2-configuring[].
+
+Since now your site will run over TLS, you need to make sure that the WordPress URL is also configured so.
+If you have followed the steps of the link:#configuring-fastcgi[previous section], your WordPress site is served at `http://localhost:8080`.
+You will need to change that to be `https://localhost:8443` from the WordPress administration web interface, or follow the http://codex.wordpress.org/Changing_The_Site_URL[WordPress instructions] to do so without using the administration web interface.
+
+The minimal modules required to run WordPress with Jetty on HTTP/2 are therefore: `http2`, `http`, `fcgi` and `deploy`.
+These will setup a clear text connector on port 8080 for HTTP/1.1 and a TLS connector on port 8443 for HTTP/2 and HTTP/1.1.
+
+At this point, you can start Jetty (see xref:startup[]), hit `http://localhost:8080` with your browser and enjoy WordPress via HTTP/2 using a HTTP/2 enabled browser:
+
+[source, screen, subs="{sub-order}"]
+....
+$ cd $JETTY_BASE
+$ java -jar $JETTY_HOME/start.jar
+....
+
+If you don't have a HTTP/2 enabled browser, WordPress will still be available over HTTP/1.1.
diff --git a/jetty-documentation/src/main/asciidoc/administration/fastcgi/fastcgi-intro.adoc b/jetty-documentation/src/main/asciidoc/administration/fastcgi/fastcgi-intro.adoc
new file mode 100644
index 0000000..db2685a
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/fastcgi/fastcgi-intro.adoc
@@ -0,0 +1,34 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[fastcgi-intro]]
+=== FastCGI Introduction
+
+FastCGI is a network protocol primarily used by a _web server_ to communicate to a __FastCGI server__.
+FastCGI servers are typically used to serve web content generated by dynamic web languages, primarily http://www.php.net/[PHP], but also Python, Ruby, Perl and others.
+
+Web servers that supports FastCGI are, among others, http://httpd.apache.org/[Apache], http://nginx.org/[Nginx], and Jetty.
+Web servers typically act as proxies, converting HTTP requests that they receive from clients (browsers) to FastCGI requests that are forwarded to the FastCGI server.
+The FastCGI server spawns the dynamic web language interpreter, passing it the information contained in the FastCGI request and a dynamic web language script is executed, producing web content, typically HTML.
+The web content is then formatted into a FastCGI response that is returned to the web server, which converts it to a HTTP response that is then returned to the client.
+
+The most well known FastCGI server is the http://php-fpm.org/[PHP FastCGI Process Manager], or `php-fpm`.
+In the following we will assume that `php-fpm` is used as FastCGI server.
+
+Jetty can be configured to act as a web server that supports FastCGI, replacing the functionality that is normally provided by Apache or Nginx.
+This allows users to leverage Jetty features such as HTTP/2, the unique support that Jetty provides for HTTP/2 Push, Jetty's scalability, and of course Jetty's native support for Java Web Standards such as Servlets, JSPs, etc.
+
+With such configuration, users can not only deploy their Java Web Applications in Jetty, but also serve their http://wordpress.com/[WordPress] site or blog or their https://drupal.org/[Drupal] site without having to install and manage multiple web servers.
diff --git a/jetty-documentation/src/main/asciidoc/administration/http2/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/http2/chapter.adoc
new file mode 100644
index 0000000..22690bc
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/http2/chapter.adoc
@@ -0,0 +1,24 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http2]]
+== HTTP/2
+
+include::introduction.adoc[]
+include::enabling-http2.adoc[]
+include::configuring-http2.adoc[]
+include::configuring-push.adoc[]
+include::configuring-haproxy.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/administration/http2/configuring-haproxy.adoc b/jetty-documentation/src/main/asciidoc/administration/http2/configuring-haproxy.adoc
new file mode 100644
index 0000000..aa494ee
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/http2/configuring-haproxy.adoc
@@ -0,0 +1,193 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http2-configuring-haproxy]]
+=== Configuring HAProxy and Jetty
+
+Typical website deployments have Apache (or Nginx) configured as reverse proxy to talk to one or more backend Jetty instances.
+This configuration cannot be used for HTTP/2 because Apache does not yet support HTTP/2 (nor does Nginx).
+
+http://haproxy.org[HAProxy] is an open source solution that offers load balancing and proxying for TCP and HTTP based application, and can be used as a replacement for Apache or Nginx when these are used as reverse proxies and has the major benefit that supports HTTP/2.
+It also offers load balancing and several other features which can position it as a complete replacement for Apache or Nginx.
+
+The deployment proposed here will have HAProxy play the role that Apache and Nginx usually do: to perform the TLS offloading (that is, decrypt and encrypt TLS) and then forwarding the now clear-text traffic to a backend Jetty server, speaking either HTTP/1.1 or HTTP/2.
+
+The instructions that follow are for Linux.
+
+[[http2-haproxy-install]]
+==== Installing HAProxy
+
+You will need HAProxy 1.5 or later, because it provides support for SSL and ALPN, both required by HTTP/2. Most Linux distributions have the HAProxy package available to be installed out of the box. For example on Ubuntu 15.04:
+
+[source,screen, subs="{sub-order}"]
+....
+$ sudo apt-get install haproxy
+....
+
+Alternatively you can download the HAProxy source code and build it on your environment by following the README bundled with the HAProxy source code tarball.
+
+____
+[NOTE]
+HAProxy supports ALPN only if built with OpenSSL 1.0.2 or greater.
+Alternatively, HAProxy supports NPN when built with OpenSSL 1.0.1 or greater.
+You must upgrade OpenSSL if you have a version earlier than 1.0.1.
+Use `haproxy -vv` to know with which OpenSSL version HAProxy has been built.
+____
+
+[[http2-haproxy-ssl]]
+==== Setup SSL for HAProxy
+
+HAProxy will perform the TLS decryption and encryption much more efficiently than a Java implementation.
+
+HAProxy will need a single file containing the X509 certificates and the private key, all in https://en.wikipedia.org/wiki/X.509[PEM format], with the following order:
+
+1.  The site certificate; this certificate's Common Name refers to the site domain (for example: CN=*.webtide.com) and is signed by Certificate Authority #1.
+2.  The Certificate Authority #1 certificate; this certificate may be signed by Certificate Authority #2.
+3.  The Certificate Authority #2 certificate; this certificate may be signed by Certificate Authority #3; and so on until the Root Certificate Authority.
+4.  The Root Certificate Authority certificate.
+5.  The private key corresponding to the site certificate.
+
+Let's use `keytool` to generate a self signed certificate:
+
+[source,screen, subs="{sub-order}"]
+....
+$ keytool -genkeypair -keyalg RSA -keystore keystore.p12 -storetype pkcs12 -storepass storepwd -ext SAN=DNS:domain.com
+What is your first and last name?
+[Unknown]:  *.domain.com
+What is the name of your organizational unit?
+[Unknown]:  Unit
+What is the name of your organization?
+[Unknown]:  Domain
+What is the name of your City or Locality?
+[Unknown]:  Torino
+What is the name of your State or Province?
+[Unknown]:  TO
+What is the two-letter country code for this unit?
+[Unknown]:  IT
+Is CN=*.domain.com, OU=Unit, O=Domain, L=Torino, ST=TO, C=IT correct?
+[no]:  yes
+....
+
+The above command will generate a self signed certificate and private key for `domain.com` and subdomains, stored in the `keystore.p12` file in PKCS#12 format.
+We need to extract the certificate and the private key in PEM format.
+
+To extract the certificate into `certificate.pem`:
+
+[source,screen, subs="{sub-order}"]
+....
+$ keytool -exportcert -keystore keystore.p12 -storetype pkcs12 -storepass storepwd -rfc -file certificate.pem
+....
+
+To export the private key into `private_key.pem`:
+
+[source,screen, subs="{sub-order}"]
+....
+$ openssl pkcs12 -in keystore.p12 -nodes -nocerts -out private_key.pem -passin pass:storepwd
+....
+
+At this point you just need to concatenate the two files into one, in the correct order:
+
+[source,screen, subs="{sub-order}"]
+....
+$ cat certificate.pem private_key.pem > domain.pem
+....
+
+The `domain.pem` file will be used later by HAProxy.
+
+[[http2-haproxy-cfg]]
+==== HAProxy Configuration File
+
+Now we can setup `haproxy.cfg` to configure HAProxy.
+This is a minimal configuration:
+
+[source, ,subs="{sub-order}"]
+....
+global
+tune.ssl.default-dh-param 1024
+
+defaults
+timeout connect 10000ms
+timeout client 60000ms
+timeout server 60000ms
+
+frontend fe_http
+mode http
+bind *:80
+# Redirect to https
+redirect scheme https code 301
+
+frontend fe_https
+mode tcp
+bind *:443 ssl no-sslv3 crt domain.pem ciphers TLSv1.2 alpn h2,http/1.1
+default_backend be_http
+
+backend be_http
+mode tcp
+server domain 127.0.0.1:8282
+....
+
+The HAProxy configuration file works in the following way.
+The `fe_http` front-end accepts connections on port 80 and redirects them to use the `https` scheme.
+
+The `fe_https` front-end accepts connections on port 443 and it is where the TLS decryption/encryption happens.
+You must specify the path to the PEM file containing the TLS key material (the `crt domain.pem` part), the ciphers that are suitable for HTTP/2 (the `ciphers TLSv1.2`), and the ALPN protocols supported (the `alpn h2,http/1.1` ).
+This front-end then forwards the now decrypted bytes to the back-end in `mode tcp`. The `mode tcp` says that HAProxy will not try to interpret the bytes as HTTP/1.1 but instead opaquely forward them to the back-end.
+
+The `be_http` back-end will forward (again in `mode tcp`) the clear-text bytes to a Jetty connector that talks clear-text HTTP/2 and HTTP/1.1 on port 8282.
+
+[[http2-haproxy-jetty]]
+==== Setup Jetty for HTTP/2 and HTTP/1.1
+
+The Jetty setup follows the steps of having Jetty installed in the `JETTY_HOME` directory, creating a `JETTY_BASE` directory and initializing it using Jetty's command line tools.
+You must enable the `http2c` module, that is the module that speaks clear-text HTTP/2.
+Since the `http2c` module depends on the `http` module, the `http` module will be enabled transitively, and the final setup will therefore support both HTTP/2 and HTTP/1.1 in clear text.
+
+Additionally, you will also enable the `deploy` module to be able to deploy a sample web application:
+
+[source,screen, subs="{sub-order}"]
+....
+$ JETTY_BASE=haproxy-jetty-http2
+$ mkdir $JETTY_BASE
+$ cd $JETTY_BASE
+$ java -jar $JETTY_HOME/start.jar --add-to-start=http2c,deploy
+....
+
+Now let's deploy a demo web application and start Jetty:
+
+[source,screen, subs="{sub-order}"]
+....
+$ cd $JETTY_BASE
+$ cp $JETTY_HOME/demo-base/webapps/async-rest.war $JETTY_BASE/webapps/
+$ java -jar $JETTY_HOME/start.jar jetty.http.host=127.0.0.1 jetty.http.port=8282
+....
+
+Now you can browse https://domain.com/async-rest (replace `domain.com` with your own domain, or with `localhost`, to make this example work).
+
+____
+[NOTE]
+You want the Jetty connector that listens on port 8282 to be available only to HAProxy, and not to remote clients.
+For this reason, you want to specify the `jetty.http.host` property on the command line (or in `start.ini`/ `start.d/http.ini` to make this setting persistent) to bind the Jetty connector only on the loopback interface (127.0.0.1), making it available to HAProxy but not to remote clients.
+If your Jetty instance runs on a different machine and/or on a different (sub)network, you may want to adjust both the back-end section of the HAProxy configuration file and the `jetty.http.host` property to match accordingly.
+____
+
+Browsers supporting HTTP/2 will connect to HAProxy, which will decrypt the traffic and send it to Jetty.
+Likewise, HTTP/1.1 clients will connect to HAProxy, which will decrypt the traffic and send it to Jetty.
+
+The Jetty connector, configured with the `http2c` module (and therefore transitively with the `http` module) is able to distinguish whether the incoming bytes are HTTP/2 or HTTP/1.1 and will handle the request accordingly.
+
+The response is relayed back to HAProxy, which will encrypt it and send it back to the remote client.
+
+This configuration offers you efficient TLS offloading, HTTP/2 support and transparent fallback to HTTP/1.1 for clients that don't support HTTP/1.1.
diff --git a/jetty-documentation/src/main/asciidoc/administration/http2/configuring-http2.adoc b/jetty-documentation/src/main/asciidoc/administration/http2/configuring-http2.adoc
new file mode 100644
index 0000000..3219ac1
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/http2/configuring-http2.adoc
@@ -0,0 +1,69 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http2-configuring]]
+=== Configuring HTTP/2
+
+Enabling the HTTP/2 module in the Jetty server does not create a HTTP/2 specific connector, but rather it adds a HTTP/2 Connection factory to an
+existing connector.
+Thus configuring HTTP/2 is a combination of configuring common properties on the connector and HTTP/2 specific properties on the connection factory.
+The modules and XML files involved can be seen with the following commands:
+
+[source,screen, subs="{sub-order}"]
+....
+$ java -jar $JETTY_HOME/start.jar --list-modules
+    ...
+    1) protonego-boot  <transitive>
+    ...
+    2) http            ${jetty.base}/start.d/http.ini
+    2) ssl             ${jetty.base}/start.d/ssl.ini
+    3) alpn            ${jetty.base}/start.d/alpn.ini
+    3) http2c          ${jetty.base}/start.d/http2c.ini
+    ...
+    4) http2           ${jetty.base}/start.d/http2.ini
+    5) https           ${jetty.base}/start.d/https.ini
+
+$ java -jar $JETTY_HOME/start.jar --list-config
+ ...
+ ${jetty.home}/etc/jetty-ssl.xml
+ ${jetty.home}/etc/jetty-ssl-context.xml
+ ${jetty.home}/etc/jetty-alpn.xml
+ ${jetty.home}/etc/jetty-http2c.xml
+ ${jetty.home}/etc/jetty-http.xml
+ ...
+ ${jetty.home}/etc/jetty-http2.xml
+ ${jetty.home}/etc/jetty-https.xml
+....
+
+The common properties associated with connectors (host,port, timeouts, etc.) can be set in the module ini files (or `start.ini` if `--add-to-start` was used): `${jetty.base}/start.d/http.ini` and `${jetty.base}/start.d/ssl.ini`.
+These properties are instantiated in the associated XML files: `${jetty.home}/etc/jetty-http.xml`; `${jetty.home}/etc/jetty-ssl.xml`, plus the SSL keystore is instantiated in `${jetty.home}/etc/jetty-ssl-context.xml`.
+
+____
+[NOTE]
+If you are planning to edit XML files, make sure to copy them to your `{$jetty.base}/etc/` directory before doing so.
+The XML files that come with the Jetty distribution should *not* be modified directly.
+____
+
+HTTP/2 specific properties can be set in the module ini files: `${jetty.base}/start.d/http2.ini` and `${jetty.base}/start.d/http2c.ini`, which are instantiated in the associated XML files: `${jetty.home}/etc/jetty-http2.xml`; `${jetty.home}/etc/jetty-http2c.xml`, respectively.
+Currently there are very few HTTP/2 configuration properties and the default values are reasonable:
+
+.HTTP/2 Configuration Properties
+[cols=",",options="header",]
+|=======================================================================
+|Property |Description
+|jetty.http2.maxConcurrentStreams |The maximum number of concurrently open streams allowed on a single HTTP/2 connection (default 1024). Larger values increase parallelism but cost a memory commitment.
+|jetty.http2.initialStreamRecvWindow |The initial receive flow control window size for a new stream (default 65535). Larger values may allow greater throughput but also risk head of line blocking if TCP/IP flow control is triggered.
+|=======================================================================
diff --git a/jetty-documentation/src/main/asciidoc/administration/http2/configuring-push.adoc b/jetty-documentation/src/main/asciidoc/administration/http2/configuring-push.adoc
new file mode 100644
index 0000000..3e59d9b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/http2/configuring-push.adoc
@@ -0,0 +1,59 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http2-configuring-push]]
+=== Configuring HTTP/2 Push
+
+HTTP/2 Push is a mechanism that allows the server to send multiple resources to the client for a single client request.
+This will reduce the amount of round-trips necessary to retrieve all the resources that make up a web page and can significantly improve the page load time.
+
+HTTP/2 Push can be automated in your application by configuring a link:{JDURL}/org/eclipse/jetty/servlets/PushCacheFilter.html[`PushCacheFilter`] in the `web.xml`, in this way:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app
+    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+    metadata-complete="true"
+    version="3.1">
+
+    ...
+    <filter>
+        <filter-name>PushFilter</filter-name>
+        <filter-class>org.eclipse.jetty.servlets.PushCacheFilter</filter-class>
+        <async-supported>true</async-supported>
+    </filter>
+    <filter-mapping>
+        <filter-name>PushFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+    ...
+
+</web-app>
+----
+
+`PushCacheFilter` analyzes the HTTP requests for resources that arrive to your web application.
+Some of these requests contain the HTTP `Referrer` header that points to a resource that has been requested previously.
+This allows the `PushCacheFilter` to organize resources in to two categories: _primary_ resources (those referenced by the `Referrer` header) and _secondary_ resources (those that have the `Referer` header).
+
+`PushCacheFilter` associates secondary resources to primary resources.
+Only secondary resources that have been requested within a time window from the request of the primary resource are associated with the primary resource.
+
+`PushCacheFilter` can be configured with the following `init-params`::
+* `associatePeriod`: the time window, in milliseconds, within which a request for a secondary resource will be associated to a primary resource.
+* `maxAssociations`: the max number of secondary resources that may be associated to a primary resource.
diff --git a/jetty-documentation/src/main/asciidoc/administration/http2/enabling-http2.adoc b/jetty-documentation/src/main/asciidoc/administration/http2/enabling-http2.adoc
new file mode 100644
index 0000000..2b1ee94
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/http2/enabling-http2.adoc
@@ -0,0 +1,72 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http2-enabling]]
+=== Enabling HTTP/2
+
+This section is written assuming that a link:#startup-base-and-home[Jetty base directory] is being used.
+A demo Jetty base that supports HTTP/1, HTTPS/1 and deployment from a webapps directory can be created with the commands:
+
+[source, screen, subs="{sub-order}"]
+....
+$ JETTY_BASE=http2-demo
+$ mkdir $JETTY_BASE
+$ cd $JETTY_BASE
+$ java -jar $JETTY_HOME/start.jar --add-to-startd=http,https,deploy
+....
+
+The commands above create a `$JETTY_BASE` directory called `http2-demo`, and initializes the `http,` `https` and `deploy` modules (and their dependencies) to run a typical Jetty Server on port 8080 (for HTTP/1) and 8443 (for HTTPS/1).
+Note that the HTTPS module downloads a demo keystore file with a self signed certificate, which needs to be replaced by a Certificate Authority issued certificate for real deployment.
+
+To add HTTP/2 to this demo base, it is just a matter of enabling the `http2` module with the following command:
+
+[source, screen, subs="{sub-order}"]
+....
+$ java -jar $JETTY_HOME/start.jar --add-to-startd=http2
+....
+
+This command does not create a new connector, but instead simply adds the HTTP/2 protocol to the existing HTTPS/1 connector, so that it now supports both protocols on port 8443.
+To do this, it also transitively enables the ALPN module for protocol negotiation.
+The support for each protocol can be seen in the info logging when the server is started:
+
+[source,screen, subs="{sub-order}"]
+----
+$ java -jar $JETTY_HOME/start.jar
+...
+2015-06-17 14:16:12.549:INFO:oejs.ServerConnector:main: Started ServerConnector@34c9c77f{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
+2015-06-17 14:16:12.782:INFO:oejs.ServerConnector:main: Started ServerConnector@711f39f9{SSL,[ssl, alpn, h2, h2-17, http/1.1]}{0.0.0.0:8443}
+...
+----
+
+This log shows that port 8080 supports only HTTP/1.1 (which by specification includes HTTP/1.0 support), while port 8443 supports the SSL protocol, with ALPN negotiation to select between several versions of HTTP/2 (h2 & the draft h2-17) and HTTP/1.1.
+What is not shown is that HTTP/1.1 is the default ALPN protocol, so that if a client connects that does not speak ALPN, then HTTP/1.1 will be assumed.
+
+A browser can now be pointed at `https://localhost:8443/` and if it supports HTTP/2 then it will be used (often indicated by a lightening bolt icon in the address bar).
+Note that a browser pointed at this server with URL starting with `http://localhost:8080/` will still talk HTTP/1.1, as HTTP/2 has not been enabled on the plain text connector.
+
+HTTP/2 can be enabled on the plain text connector and the server restarted with the following command:
+
+[source,screen]
+....
+$ java -jar $JETTY_HOME/start.jar --add-to-startd=http2c
+$ java -jar $JETTY_HOME/start.jar
+..
+2015-06-17 14:16:12.549:INFO:oejs.ServerConnector:main: Started ServerConnector@6f32cd1e{HTTP/1.1,[http/1.1, h2c, h2c-17]}{0.0.0.0:8080}
+2015-06-17 14:16:12.782:INFO:oejs.ServerConnector:main: Started ServerConnector@711f39f9{SSL,[ssl, alpn, h2, h2-17, http/1.1]}{0.0.0.0:8443}
+..
+....
+
+No major browser currently supports plain text HTTP/2, so the 8080 port will only be able to use HTTP/2 with specific clients (eg `curl`) that use the upgrade mechanism or assume HTTP/2.
diff --git a/jetty-documentation/src/main/asciidoc/administration/http2/introduction.adoc b/jetty-documentation/src/main/asciidoc/administration/http2/introduction.adoc
new file mode 100644
index 0000000..1ee4a06
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/http2/introduction.adoc
@@ -0,0 +1,42 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http2-introduction]]
+=== Introducing HTTP/2
+
+Jetty supports both a client and a server implementation for the HTTP/2 protocol as defined by http://tools.ietf.org/html/rfc7540[RFC 7540].
+
+The requirements for running HTTP/2 are JDK 8 or greater, and typically also ALPN support (see xref:alpn-chapter[]).
+
+A server deployed over TLS (SSL) normally advertises the HTTP/2 protocol via the TLS extension Application Layer Protocol Negotiation link:#alpn[(ALPN)].
+
+____
+[IMPORTANT]
+To use HTTP/2 in Jetty via a TLS connector you need to add the link:#alpn-starting[ALPN boot jar] in the boot classpath.
+This is done automatically when using the Jetty distribution's start.jar link:#startup-modules[module system], but must be configured directly otherwise.
+____
+
+[[http2-modules]]
+==== Jetty HTTP/2 Sub Projects
+
+The Jetty HTTP/2 implementation consists of the following sub-projects (each producing a jar file):
+
+1.  `http2-common`: Contains the HTTP/2 API and a partial implementation shared across other modules.
+2.  `http2-hpack`: Contains the HTTP/2 HPACK implementation for HTTP header compression.
+3.  `http2-server`: Provides the server-side implementation of HTTP/2.
+4.  `http2-client`: Provides the implementation of HTTP/2 client with a low level HTTP/2 API, dealing with HTTP/2 streams, frames, etc.
+5.  `http2-http-client-transport`: Provides the implementation of the HTTP/2 transport for `HttpClient` (see xref:http-client[]).
+Applications can use the higher level API provided by `HttpClient` to send HTTP requests and receive HTTP responses, and the HTTP/2 transport will take care of converting them in HTTP/2 format (see also https://webtide.com/http2-support-for-httpclient/[this blog entry]).
diff --git a/jetty-documentation/src/main/asciidoc/administration/jmx/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/jmx/chapter.adoc
new file mode 100644
index 0000000..bb86883
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jmx/chapter.adoc
@@ -0,0 +1,33 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jmx-chapter]]
+== Java Management Extensions (JMX)
+
+
+The http://java.sun.com/products/JavaManagement/[Java Management Extensions (JMX) API] is a standard API for managing and monitoring resources such as applications, devices, services, and the Java virtual machine.
+
+Typical uses of the JMX technology include:
+
+* Consulting and changing application configuration
+* Accumulating and making available statistics about application behavior
+* Notifying of state changes and erroneous conditions
+
+The JMX API includes remote access, so a remote management program can interact with a running application for these purposes.
+
+include::using-jmx.adoc[]
+include::jetty-jmx-annotations.adoc[]
+include::jetty-jconsole.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/administration/jmx/images/jconsole1.jpg b/jetty-documentation/src/main/asciidoc/administration/jmx/images/jconsole1.jpg
new file mode 100644
index 0000000..332d603
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jmx/images/jconsole1.jpg
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/administration/jmx/images/jconsole2.jpg b/jetty-documentation/src/main/asciidoc/administration/jmx/images/jconsole2.jpg
new file mode 100644
index 0000000..aaac114
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jmx/images/jconsole2.jpg
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/administration/jmx/images/jconsole3.png b/jetty-documentation/src/main/asciidoc/administration/jmx/images/jconsole3.png
new file mode 100644
index 0000000..2300a30
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jmx/images/jconsole3.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/administration/jmx/images/jmc1.png b/jetty-documentation/src/main/asciidoc/administration/jmx/images/jmc1.png
new file mode 100644
index 0000000..77a2164
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jmx/images/jmc1.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/administration/jmx/images/jmc2.png b/jetty-documentation/src/main/asciidoc/administration/jmx/images/jmc2.png
new file mode 100644
index 0000000..28a9f7e
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jmx/images/jmc2.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/administration/jmx/images/jmc3.png b/jetty-documentation/src/main/asciidoc/administration/jmx/images/jmc3.png
new file mode 100644
index 0000000..7087909
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jmx/images/jmc3.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/administration/jmx/jetty-jconsole.adoc b/jetty-documentation/src/main/asciidoc/administration/jmx/jetty-jconsole.adoc
new file mode 100644
index 0000000..e48750c
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jmx/jetty-jconsole.adoc
@@ -0,0 +1,116 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-jconsole]]
+=== Managing Jetty with JConsole and JMC
+
+JConsole and the Java Mission Control (JMX) are graphical tools; they allow you to remotely manage and monitor your server and web application status using JMX.
+When following the instructions given below, please also ensure that you make any necessary changes to any anti-virus software you may be using which may prevent JConsole or JMC from running.
+
+==== Starting Jetty Standalone
+
+The simplest way to enable support is to add the JMX-Remote support module to your `{$jetty.base}`.
+
+[source, screen, subs="{sub-order}"]
+....
+[mybase]$ java /opt/jetty-dist/start.jar --add-to-start=jmx-remote, jmx
+INFO: jmx-remote      initialised in ${jetty.base}/start.ini
+INFO: jmx             initialised in ${jetty.base}/start.ini
+....
+
+Then open the `{$jetty.base}/start.ini` (or `{$jetty.base}/start.d/jmx-remote.ini`) file and edit the properties to suit your needs:
+
+[source, screen, subs="{sub-order}"]
+....
+#
+# Initialize module jmx-remote
+#
+--module=jmx-remote
+## JMX Configuration
+## Enable for an open port accessible by remote machines
+jetty.jmxrmihost=localhost
+jetty.jmxrmiport=1099
+....
+
+[[jetty-jconsole-monitoring]]
+==== Monitoring Jetty with JConsole
+
+To monitor Jetty's server status with JConsole, start Jetty and then start JConsole by typing `jconsole` on the command line.
+
+===== Connecting to your server process
+
+After you start Jetty, you will see a dialog box in JConsole with a list of running processes to which you can connect.
+It should look something like so:
+
+image:images/jconsole1.jpg[image,width=576]
+
+____
+[IMPORTANT]
+If you don't see your Jetty process in the list of processes you can connect to, quickly switch tabs, or close and reopen a new "New Connection" dialog window.
+This forces JConsole to refresh the list, and recognize your newly-started Jetty process.
+____
+
+Select the start.jar entry and click the "Connect" button.
+A new JConsole window opens:
+
+image:images/jconsole2.jpg[image,width=576]
+
+From this window you can monitor memory usage, thread usage, classloading and VM statistics.
+You can also perform operations such as a manual garbage collect.
+JConsole is an extremely powerful and useful tool.
+
+==== Managing Jetty Objects with JConsole
+
+The MBean tab of JConsole allows access to managed objects within the Java application, including MBeans the JVM provides.
+If you also want to interact with the Jetty JMX implementation via JConsole, you need to start Jetty JMX in a form that JConsole can access.
+See xref:using-jmx[] for more information.
+
+image:images/jconsole3.png[image,width=576]
+
+[[jetty-jmc-monitoring]]
+==== Monitoring Jetty with JMC
+
+To monitor Jetty's server status with JMC, start Jetty and then start JMC by typing `jmc` on the command line.
+
+===== Connecting to your server process
+
+After you start Jetty, you will see a dialog box in JMC with a list of running processes to which you can connect.
+It should look something like so:
+
+image:images/jmc1.png[image,width=576]
+
+____
+[IMPORTANT]
+If you don't see your Jetty process in the list of processes you can connect to, quickly switch tabs, or close and reopen a new "New Connection" dialog window.
+This forces JMC to refresh the list, and recognize your newly-started Jetty process.
+____
+
+Double-click the start.jar entry or right-click the start.jar entry and select "Start JMX Console".
+A new JMC window opens on the right:
+
+image:images/jmc2.png[image,width=576]
+
+From this window you can monitor memory usage, thread usage, classloading and VM statistics.
+You can also perform operations such as a manual garbage collect.
+JMC is an extremely powerful and useful tool.
+
+==== Managing Jetty Objects with JConsole
+
+The MBean tab of JMC allows access to managed objects within the Java application, including MBeans the JVM provides.
+If you also want to interact with the Jetty JMX implementation via JMC, you need to start Jetty JMX in a form that JMC can access.
+See xref:using-jmx[] for more information.
+
+image:images/jmc3.png[image,width=576]
diff --git a/jetty-documentation/src/main/asciidoc/administration/jmx/jetty-jmx-annotations.adoc b/jetty-documentation/src/main/asciidoc/administration/jmx/jetty-jmx-annotations.adoc
new file mode 100644
index 0000000..49f41c9
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jmx/jetty-jmx-annotations.adoc
@@ -0,0 +1,138 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-jmx-annotations]]
+=== Jetty JMX Annotations
+
+When the `jetty-jmx` libraries are present on startup and the wiring is enabled for exposing Jetty MBeans to JMX, there are three annotations that govern when and how MBeans are created and exposed.
+
+[[jmx-annotation-introspection]]
+==== Annotation Introspection
+
+When JMX is configured and enabled in Jetty, any time an object is registered with the Server it is introspected as a potential MBean to be exposed.
+This introspection proceeds as follows assuming the class is named `com.acme.Foo`:
+
+1.  All influences for `com.acme.Foo` determined.
+These include each class in the chain of super classes, and by convention each of these classes following a form of `com.acme.jmx.FooMBean`.
+All super classes and their corresponding MBean representations are then used in the next step.
+2.  Each potential influencing class is checked for the `@ManagedObject` annotation.
+Should this annotation exist at any point in the chain of influencers then an MBran is created with the description of the version `@ManagedObject` discovered.
+3.  Once a MBean has been created for an object then each potential influencing object is introspected for `@ManagedAttribute` and `@ManagedOperation` annotations and the corresponding type is exposed to the MBean.
+
+The convention of looking for `@ManagedObject` annotations on `.jmx.ClassMBean` allows for a normal POJOs to be wrapped in an MBean without itself without requiring it being marked up with annotations.
+Since the POJO is passed to these wrapped derived Mbean instances and is an internal variable then the MBean can be used to better expose a set of attributes and operations that may not have been anticipated when the original object was created.
+
+[[jmx-managed-object]]
+==== @ManagedObject
+
+The `@ManagedObject` annotation is used on a class at the top level to indicate that it should be exposed as an MBean.
+It has only one attribute to it which is used as the description of the MBean.
+Should multiple `@ManagedObject` annotations be found in the chain of influence then the first description is used.
+
+The list of attributes available are:
+
+value::
+  The description of the Managed Object.
+
+[[jmx-managed-attribute]]
+==== @ManagedAttribute
+
+The `@ManagedAttribute` annotation is used to indicate that a given method exposes a JMX attribute.
+This annotation is placed always on the reader method of a given attribute.
+Unless it is marked as read-only in the configuration of the annotation a corresponding setter is looked for following normal naming conventions.
+For example if this annotation is on a method called `getFoo()` then a method called `setFoo()` would be looked for and if found wired automatically into the JMX attribute.
+
+The list of attributes available are:
+
+value::
+  The description of the Managed Attribute.
+name::
+  The name of the Managed Attribute.
+proxied::
+  Value is true if the corresponding MBean for this object contains the method of this JMX attribute in question.
+readonly::
+  By default this value is false which means that a corresponding setter will be looked for an wired into the attribute should one be found.
+  Setting this to true make the JMX attribute read only.
+setter::
+  This attribute can be used when the corresponding setter for a JMX attribute follows a non-standard naming convention and it should still be exposed as the setter for the attribute.
+
+[[jmx-managed-operation]]
+==== @ManagedOperation
+
+The `@ManagedOperation` annotation is used to indicate that a given method should be considered a JMX operation.
+
+The list of attributes available are:
+
+value::
+  The description of the Managed Operation.
+impact::
+  The impact of an operation.
+  By default this value is "UNKNOWN" and acceptable values are "ACTION", "INFO", "ACTION_INFO" and should be used according to their definitions with JMX.
+proxied::
+  Value is true if the corresponding MBean for this object contains the method of this JMX operation in question.
+
+[[jmx-name-annotation]]
+==== @Name
+
+A fourth annotation is often used in conjunction with the JMX annotations mentioned above.
+This annotation is used to describe variables in method signatures so that when rendered into tools like JConsole it is clear what the parameters are.
+For example:
+
+The list of attributes available are:
+
+value::
+  The name of the parameter.
+description::
+  The description of the parameter.
+
+[[jmx-annotation-example]]
+==== Example
+
+The following is an example of each of the annotations mentioned above in practice.
+
+[source, java, subs="{sub-order}"]
+----
+
+package com.acme;
+
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.annotation.ManagedOperation;
+import org.eclipse.jetty.util.annotation.Name;
+
+@ManagedObject("Test MBean Annotations")
+public class Derived extends Base implements Signature
+{
+    String fname="Full Name";
+
+    @ManagedAttribute(value="The full name of something", name="fname")
+    public String getFullName()
+    {
+        return fname;
+    }
+
+    public void setFullName(String name)
+    {
+        fname=name;
+    }
+
+    @ManagedOperation("Doodle something")
+    public void doodle(@Name(value="doodle", description="A description of the argument") String doodle)
+    {
+        System.err.println("doodle "+doodle);
+    }
+}
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/jmx/using-jmx.adoc b/jetty-documentation/src/main/asciidoc/administration/jmx/using-jmx.adoc
new file mode 100644
index 0000000..4113599
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jmx/using-jmx.adoc
@@ -0,0 +1,203 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[using-jmx]]
+=== Using JMX with Jetty
+
+Jetty JMX integration uses the platform MBean server implementation that Java VM provides.
+The integration is based on the `ObjectMBean` implementation of `DynamicMBean`.
+This implementation allows you to wrap an arbitrary POJO in an MBean and annotate it appropriately to expose it via JMX.
+See xref:jetty-jmx-annotations[].
+
+The `MBeanContainer` implementation of the `Container.Listener` interface coordinates creation of MBeans.
+The Jetty Server and it's components use a link:{JDURL}/org/eclipse/jetty/util/component/Container.html[Container] to maintain a containment tree of components and to support notification of changes to that tree.
+The `MBeanContainer` class listens for Container events and creates and destroys MBeans as required to wrap all Jetty components.
+
+You can access the MBeans that Jetty publishes both through built-in Java VM connector via JConsole or JMC, or by registering a remote JMX connector and using a remote JMX agent to monitor Jetty.
+
+[[configuring-jmx]]
+==== Configuring JMX
+
+This guide describes how to initialize and configure the Jetty JMX integration.
+
+To monitor an application using JMX, perform the following steps:
+
+* Configure the application to instantiate an MBean container.
+* Instrument objects to be MBeans.
+* Provide access for JMX agents to MBeans.
+
+[[accessing-jetty-mbeans]]
+===== Using JConsole to Access Jetty MBeans
+
+The simplest way to access the MBeans that Jetty publishes is to use the http://java.sun.com/developer/technicalArticles/J2SE/jconsole.html[JConsole utility] the Java Virtual Machine supplies.
+See xref:jetty-jconsole[] for instructions on how to configure JVM for use with JConsole or JMC.
+
+To access Jetty MBeans via JConsole or JMC, you must:
+
+* Enable the registration of Jetty MBeans into the platform MBeanServer.
+* Enable a `JMXConnectorServer` so that JConsole/JMC can connect and visualize the MBeans.
+
+[[registering-jetty-mbeans]]
+===== Registering Jetty MBeans
+
+Configuring Jetty JMX integration differs for standalone and embedded Jetty.
+
+[[jmx-standalone-jetty]]
+====== Standalone Jetty
+
+JMX is not enabled by default in the Jetty distribution.
+To enable JMX in the Jetty distribution, run the following, where `{$jetty.home}` is the directory where you have the Jetty distribution located (see link:#startup-base-and-home[the documentation for Jetty base vs. home examples]):
+
+[source, screen, subs="{sub-order}"]
+....
+$ java -jar {$jetty.home}/start.jar --add-to-start=jmx
+....
+
+Running the above command will append the available configurable elements of the JMX module to the `{$jetty.base}/start.ini` file.
+If you are managing separate ini files for your modules in the distribution, use `--add-to-start.d=jmx` instead.
+
+If you wish to add remote access for JMX, you will also need to enable the JMX-Remote module:
+
+[source, screen, subs="{sub-order}"]
+....
+$ java -jar {$jetty.home}/start.jar --add-to-start=jmx-remote
+....
+
+[[jmx-embedded-jetty]]
+====== Embedded Jetty
+
+When running Jetty embedded into an application, create and configure an MBeanContainer instance as follows:
+
+[source, java]
+----
+
+Server server = new Server();
+
+// Setup JMX
+MBeanContainer mbContainer=new MBeanContainer(ManagementFactory.getPlatformMBeanServer());
+server.addEventListener(mbContainer);
+server.addBean(mbContainer);
+
+// Add loggers MBean to server (will be picked up by MBeanContainer above)
+server.addBean(Log.getLog());
+
+----
+
+Notice that Jetty creates the `MBeanContainer` immediately after creating the Server, and immediately after registering it as an `EventListener` of the Server object (which is also a Container object).
+
+Because logging is initialized prior to the `MBeanContainer` (even before the Server itself), it is necessary to register the logger manually via `server.addBean()` so that the loggers may show up in the JMX tree.
+
+[[jmx-using-jetty-maven-plugin]]
+===== Using the Jetty Maven Plugin with JMX
+
+If you are using the link:#jetty-maven-plugin[Jetty Maven plugin] you should copy the `/etc/jetty-jmx.xml` file into your webapp project somewhere, such as `/src/etc,` then add a `<jettyconfig>` element to the plugin `<configuration>`:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupid>org.eclipse.jetty</groupid>
+  <artifactid>jetty-maven-plugin</artifactid>
+  <version>{VERSION}</version>
+  <configuration>
+    <scanintervalseconds>10</scanintervalseconds>
+    <jettyXml>src/etc/jetty-jmx.xml</jettyXml>
+  </configuration>
+</plugin>
+----
+
+
+[[enabling-jmxconnectorserver-for-remote-access]]
+==== Enabling JMXConnectorServer for Remote Access
+
+There are two ways of enabling remote connectivity so that JConsole or JMC can connect to visualize MBeans.
+
+* Use the `com.sun.management.jmxremote` system property on the command line.
+Unfortunately, this solution does not work well with firewalls and is not flexible.
+* Use Jetty's `ConnectorServer` class.
+To enable use of this class, uncomment the correspondent portion in `/etc/jetty-jmx.xml,` like this:
+
+[source, xml, subs="{sub-order}"]
+----
+<New id="ConnectorServer" class="org.eclipse.jetty.jmx.ConnectorServer">
+  <Arg>
+    <New class="javax.management.remote.JMXServiceURL">
+      <Arg type="java.lang.String">rmi</Arg>
+      <Arg type="java.lang.String" />
+      <Arg type="java.lang.Integer"><SystemProperty name="jetty.jmxrmiport" default="1099"/></Arg>
+      <Arg type="java.lang.String">/jndi/rmi://<SystemProperty name="jetty.jmxrmihost" default="localhost"/>:<SystemProperty name="jetty.jmxrmiport" default="1099"/>/jmxrmi</Arg>
+    </New>
+  </Arg>
+  <Arg>org.eclipse.jetty.jmx:name=rmiconnectorserver</Arg>
+  <Call name="start" />
+</New>
+
+----
+
+This configuration snippet starts an `RMIRegistry` and a `JMXConnectorServer` both on port 1099 (by default), so that firewalls should open just that one port to allow connections from JConsole or JMC.
+
+[[securing-remote-access]]
+==== Securing Remote Access
+
+`JMXConnectorServer` several options to restrict access.
+For a complete guide to controlling authentication and authorization in JMX, see https://blogs.oracle.com/lmalventosa/entry/jmx_authentication_authorization[Authentication and Authorization in JMX RMI connectors] in Luis-Miguel Alventosa's blog.
+
+To restrict access to the `JMXConnectorServer`, you can use this configuration, where the `jmx.password` and `jmx.access` files have the format specified in the blog entry above:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<New id="ConnectorServer" class="org.eclipse.jetty.jmx.ConnectorServer">
+  <Arg>
+    <New class="javax.management.remote.JMXServiceURL">
+      <Arg type="java.lang.String">rmi</Arg>
+      <Arg type="java.lang.String" />
+      <Arg type="java.lang.Integer"><SystemProperty name="jetty.jmxrmiport" default="1099"/></Arg>
+      <Arg type="java.lang.String">/jndi/rmi://<SystemProperty name="jetty.jmxrmihost" default="localhost"/>:<SystemProperty name="jetty.jmxrmiport" default="1099"/>/jmxrmi</Arg>
+    </New>
+  </Arg>
+  <Arg>
+    <Map>
+      <Entry>
+        <Item>jmx.remote.x.password.file</Item>
+        <Item>
+          <New class="java.lang.String"><Arg><Property name="jetty.home" default="." />/resources/jmx.password</Arg></New>
+        </Item>
+      </Entry>
+      <Entry>
+        <Item>jmx.remote.x.access.file</Item>
+        <Item>
+          <New class="java.lang.String"><Arg><Property name="jetty.home" default="." />/resources/jmx.access</Arg></New>
+        </Item>
+      </Entry>
+    </Map>
+  </Arg>
+  <Arg>org.eclipse.jetty.jmx:name=rmiconnectorserver</Arg>
+  <Call name="start" />
+</New>
+
+
+----
+
+[[custom-monitor-applcation]]
+==== Custom Monitor Application
+
+Using the JMX API, you can also write a custom application to monitor your Jetty server.
+To allow this application to connect to your Jetty server, you need to uncomment the last section of the `/etc/jetty-jmx.xml` configuration file and optionally modify the endpoint name.
+Doing so creates a JMX HTTP connector and registers a JMX URL that outputs to the `Stderr` log.
+
+You should provide the URL that appears in the log to your monitor application in order to create an `MBeanServerConnection.`
+You can use the same URL to connect to your Jetty instance from a remote machine using JConsole or JMC.
+See the link:{GITBROWSEURL}/jetty-jmx/src/main/config/etc/jetty-jmx.xml[configuration file] for more details.
diff --git a/jetty-documentation/src/main/asciidoc/administration/jndi/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/jndi/chapter.adoc
new file mode 100644
index 0000000..62f8ea7
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jndi/chapter.adoc
@@ -0,0 +1,27 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jndi]]
+== Configuring JNDI
+
+Jetty supports `java:comp/env` lookups in webapps.
+This is an optional feature for which some configuration is required.
+
+include::quick-jndi-setup.adoc[]
+include::using-jndi.adoc[]
+include::jndi-configuration.adoc[]
+include::jndi-embedded.adoc[]
+include::jndi-datasources.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/administration/jndi/jndi-configuration.adoc b/jetty-documentation/src/main/asciidoc/administration/jndi/jndi-configuration.adoc
new file mode 100644
index 0000000..9dc0659
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jndi/jndi-configuration.adoc
@@ -0,0 +1,337 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jndi-configuration]]
+=== Configuring JNDI
+
+[[configuring-jndi-env-entries]]
+==== Configuring JNDI _env-entries_
+
+Sometimes it is useful to pass configuration information to a webapp at runtime that you either cannot or cannot conveniently code into a `web.xml` env-entry.
+In such cases, you can use the `org.eclipse.jetty.plus.jndi.EnvEntry` class, and even override an entry of the same name in `web.xml`.
+
+[source, xml, subs="{sub-order}"]
+----
+<New class="org.eclipse.jetty.plus.jndi.EnvEntry">
+  <Arg></Arg>
+  <Arg>mySpecialValue</Arg>
+  <Arg type="java.lang.Integer">4000</Arg>
+  <Arg type="boolean">true</Arg>
+</New>
+----
+
+This example defines a virtual `env-entry` called `mySpecialValue` with value `4000` that is xref:jndi-name-scope[scoped] to the JVM.
+It is put into JNDI at `java:comp/env/mySpecialValue` for _every_ web app deployed.
+Moreover, the boolean argument indicates that this value overrides an `env-entry` of the same name in `web.xml`.
+If you don't want to override, omit this argument, or set it to `false`.
+
+The Servlet Specification allows binding only the following object types to an `env-entry`:
+
+* java.lang.String
+* java.lang.Integer
+* java.lang.Float
+* java.lang.Double
+* java.lang.Long
+* java.lang.Short
+* java.lang.Character
+* java.lang.Byte
+* java.lang.Boolean
+
+That being said, Jetty is a little more flexible and allows you to also bind custom POJOs, http://docs.oracle.com/javase/1.5.0/docs/api/javax/naming/Reference.html[`javax.naming.References`] and http://docs.oracle.com/javase/1.5.0/docs/api/javax/naming/Referenceable.html[`javax.naming.Referenceables`].
+Be aware that if you take advantage of this feature, your web application is __not portable__.
+
+To use the `env-entry` configured above, use code in your `servlet/filter/etc.`, such as:
+
+[source, java, subs="{sub-order}"]
+----
+import javax.naming.InitialContext;
+
+public class MyClass {
+
+  public void myMethod() {
+
+    InitialContext ic = new InitialContext();
+    Integer mySpecialValue = (Integer)ic.lookup("java:comp/env/mySpecialValue");
+    ...
+  }
+}
+----
+
+[[configuring-resource-refs-and-resource-env-refs]]
+==== Configuring _resource-refs_ and _resource-env-refs_
+
+You can configure any type of resource that you want to refer to in a `web.xml` file as a `resource-ref` or `resource-env-ref`, using the `org.eclipse.jetty.plus.jndi.Resource` type of naming entry.
+You provide the scope, the name of the object (relative to `java:comp/env`) and a POJO instance or a `javax.naming.Reference` instance or `javax.naming.Referenceable` instance.
+
+The http://jcp.org/aboutJava/communityprocess/pr/jsr244/index.html[J2EE Specification] recommends storing DataSources in `java:comp/env/jdbc`, JMS connection factories under `java:comp/env/jms`, JavaMail connection factories under `java:comp/env/mail` and URL connection factories under `java:comp/env/url`.
+
+For example:
+
+.DataSource Declaration Conventions
+[cols=",,",options="header",]
+|=======================================================================
+|Resource Type |Name in `jetty.xml` |Environment Lookup
+|javax.sql.DataSource |jdbc/myDB |java:comp/env/jdbc/myDB
+
+|javax.jms.QueueConnectionFactory |jms/myQueue
+|java:comp/env/jms/myQueue
+
+|javax.mail.Session |mail/myMailService
+|java:comp/env/mail/myMailService
+|=======================================================================
+
+[[configuring-datasources]]
+==== Configuring DataSources
+
+Here is an example of configuring a `javax.sql.DataSource`.
+Jetty can use any DataSource implementation available on its classpath.
+In this example, the DataSource is from the http://db.apache.org/derby[Derby] relational database, but you can use any implementation of a `javax.sql.DataSource`.
+This example configures it as scoped to a web app with the id of __wac__:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
+  <New id="myds" class="org.eclipse.jetty.plus.jndi.Resource">
+    <Arg><Ref refid="wac"/></Arg>
+    <Arg>jdbc/myds</Arg>
+    <Arg>
+      <New class="org.apache.derby.jdbc.EmbeddedDataSource">
+        <Set name="DatabaseName">test</Set>
+        <Set name="createDatabase">create</Set>
+      </New>
+    </Arg>
+  </New>
+</Configure>
+----
+
+The code above creates an instance of `org.apache.derby.jdbc.EmbeddedDataSource`, calls the two setter methods `setDatabaseName("test"),` and `setCreateDatabase("create"),` and binds it into the JNDI scope for the web app.
+If you do not have the appropriate `resource-ref` set up in your `web.xml`, it is available from application lookups as `java:comp/env/jdbc/myds`.
+
+Here's an example `web.xml` declaration for the datasource above:
+
+[source, xml, subs="{sub-order}"]
+----
+<resource-ref>
+  <res-ref-name>jdbc/myds</res-ref-name>
+  <res-type>javax.sql.DataSource</res-type>
+  <res-auth>Container</res-auth>
+</resource-ref>
+----
+
+To look up your DataSource in your `servlet/filter/etc.`:
+
+[source, java, subs="{sub-order}"]
+----
+import javax.naming.InitialContext;
+import javax.sql.DataSource;
+
+public class MyClass {
+
+  public void myMethod() {
+
+    InitialContext ic = new InitialContext();
+    DataSource myDS = (DataSource)ic.lookup("java:comp/env/jdbc/myds");
+
+    ...
+  }
+}
+----
+
+____
+[NOTE]
+Careful! When configuring Resources, ensure that the type of object you configure matches the type of object you expect to look up in `java:comp/env`.
+For database connection factories, this means that the object you register as a Resource _must_ implement the `javax.sql.DataSource` interface.
+____
+
+For more examples of datasource configurations, see xref:jndi-datasource-examples[].
+
+[[configuring-jms-queues-topics-connectionfactories]]
+==== Configuring JMS Queues, Topics and ConnectionFactories
+
+Jetty can bind any implementation of the JMS destinations and connection factories.
+You just need to ensure the implementation Jars are available on Jetty's classpath.
+Here is an example of binding an http://activemq.apache.org[ActiveMQ] in-JVM connection factory:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
+  <New id="cf" class="org.eclipse.jetty.plus.jndi.Resource">
+    <Arg><Ref refid='wac'/></Arg>
+    <Arg>jms/connectionFactory</Arg>
+    <Arg>
+      <New class="org.apache.activemq.ActiveMQConnectionFactory">
+        <Arg>vm://localhost?broker.persistent=false</Arg>
+      </New>
+    </Arg>
+  </New>
+</Configure>
+----
+
+The entry in `web.xml` would be:
+
+[source, xml, subs="{sub-order}"]
+----
+<resource-ref>
+  <res-ref-name>jms/connectionFactory</res-ref-name>
+  <res-type>javax.jms.ConnectionFactory</res-type>
+  <res-auth>Container</res-auth>
+</resource-ref>
+----
+
+TODO: put in an example of a QUEUE from progress demo
+
+[[configuring-mail-with-jndi]]
+==== Configuring Mail
+
+Jetty also provides infrastructure for access to `javax.mail.Sessions` from within an application:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
+  <New id="mail" class="org.eclipse.jetty.plus.jndi.Resource">
+    <Arg><Ref refid="wac"/></Arg>
+    <Arg>mail/Session</Arg>
+    <Arg>
+      <New class="org.eclipse.jetty.jndi.factories.MailSessionReference">
+        <Set name="user">fred</Set>
+        <Set name="password">OBF:1xmk1w261z0f1w1c1xmq</Set>
+        <Set name="properties">
+          <New class="java.util.Properties">
+            <Put name="mail.smtp.host">XXX</Put>
+            <Put name="mail.from">me@me</Put>
+            <Put name="mail.debug">true</Put>
+          </New>
+        </Set>
+      </New>
+    </Arg>
+  </New>
+</Configure>
+----
+
+This setup creates an instance of the `org.eclipse.jetty.jndi.factories.MailSessionReference` class, calls it's setter methods to set up the authentication for the mail system, and populates a set of Properties, setting them on the `MailSessionReference` instance.
+The result is that an application can look up `java:comp/env/mail/Session` at runtime and obtain access to a `javax.mail.Session` that has the necessary configuration to permit it to send email via SMTP.
+
+____
+[TIP]
+You can set the password to be plain text, or use Jetty's link:#configuring-security-secure-passwords[Secure Password Obfuscation] (OBF:) mechanism to make the config file a little more secure from prying eyes.
+Remember that you cannot use the other Jetty encryption mechanisms of MD5 and Crypt because they do not allow you to recover the original password, which the mail system requires.
+____
+
+[[configuring-xa-transactions]]
+==== Configuring XA Transactions
+
+If you want to perform distributed transactions with your resources, you need a _transaction manager_ that supports the JTA interfaces, and that you can look up as `java:comp/UserTransaction` in your webapp.
+Jetty does not ship with one as standard, but you can plug in the one you prefer.
+You can configure a transaction manager using the link:{JDURL}/org/eclipse/jetty/plus/jndi/Transaction.html[JNDI Transaction] object in a Jetty config file.
+The following example configures the http://www.atomikos.com/[Atomikos] transaction manager:
+
+[source, xml, subs="{sub-order}"]
+----
+<New id="tx" class="org.eclipse.jetty.plus.jndi.Transaction">
+  <Arg>
+    <New class="com.atomikos.icatch.jta.J2eeUserTransaction"/>
+  </Arg>
+</New>
+----
+
+[[configuring-links]]
+==== Configuring Links
+
+Generally, the name you set for your `Resource` should be the same name you use for it in `web.xml`.
+For example:
+
+In a context xml file:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
+  <New id="myds" class="org.eclipse.jetty.plus.jndi.Resource">
+    <Arg><Ref refid="wac"/></Arg>
+    <Arg>jdbc/mydatasource</Arg>
+    <Arg>
+      <New class="org.apache.derby.jdbc.EmbeddedDataSource">
+        <Set name="DatabaseName">test</Set>
+        <Set name="createDatabase">create</Set>
+      </New>
+    </Arg>
+  </New>
+</Configure>
+----
+
+In a `web.xml` file:
+
+[source, xml, subs="{sub-order}"]
+----
+<resource-ref>
+  <res-ref-name>jdbc/mydatasource</res-ref-name>
+  <res-type>javax.sql.DataSource</res-type>
+  <res-auth>Container</res-auth>
+  <injection-target>
+    <injection-target-class>com.acme.JNDITest</injection-target-class>
+    <injection-target-name>myDatasource</injection-target-name>
+  </injection-target>
+</resource-ref>
+----
+
+However, you can refer to it in `web.xml` by a different name, and link it to the name in your `org.eclipse.jetty.plus.jndi.Resource` by using an `org.eclipse.jetty.plus.jndi.Link`.
+For the example above, you can refer to the `jdbc/mydatasource` resource as `jdbc/mydatasource1` as follows:
+
+In a context xml file declare `jdbc/mydatasource`:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
+  <New id="myds" class="org.eclipse.jetty.plus.jndi.Resource">
+    <Arg><Ref refid="wac"/></Arg>
+    <Arg>jdbc/mydatasource</Arg>
+    <Arg>
+      <New class="org.apache.derby.jdbc.EmbeddedDataSource">
+        <Set name="DatabaseName">test</Set>
+        <Set name="createDatabase">create</Set>
+      </New>
+    </Arg>
+  </New>
+</Configure>
+----
+
+Then in a `WEB-INF/jetty-env.xml` file, link the name `jdbc/mydatasource` to the name you want to reference it as in
+`web.xml`, which in this case is `jdbc/mydatasource1`:
+
+[source, xml, subs="{sub-order}"]
+----
+<New id="map1" class="org.eclipse.jetty.plus.jndi.Link">
+  <Arg><Ref refid='wac'/></Arg>
+  <Arg>jdbc/mydatasource1</Arg> <!-- name in web.xml -->
+  <Arg>jdbc/mydatasource</Arg>  <!-- name in container environment -->
+</New>
+----
+
+Now you can refer to `jdbc/mydatasource1` in the `web.xml` like this:
+
+[source, xml, subs="{sub-order}"]
+----
+<resource-ref>
+  <res-ref-name>jdbc/mydatasource1</res-ref-name>
+  <res-type>javax.sql.DataSource</res-type>
+  <res-auth>Container</res-auth>
+  <injection-target>
+    <injection-target-class>com.acme.JNDITest</injection-target-class>
+    <injection-target-name>myDatasource</injection-target-name>
+  </injection-target>
+</resource-ref>
+----
+
+This can be useful when you cannot change a JNDI resource directly in the `web.xml` but need to link it to a specific resource in your deployment environment.
diff --git a/jetty-documentation/src/main/asciidoc/administration/jndi/jndi-datasources.adoc b/jetty-documentation/src/main/asciidoc/administration/jndi/jndi-datasources.adoc
new file mode 100644
index 0000000..4165b3d
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jndi/jndi-datasources.adoc
@@ -0,0 +1,403 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jndi-datasource-examples]]
+=== Datasource Examples
+
+Here are examples of configuring a JNDI datasource for various databases.
+
+____
+[NOTE]
+Read xref:configuring-datasources[] in xref:jndi-configuration[] for more information about configuring datasources.
+____
+
+All of these examples correspond to a `resource-ref` in `web.xml`.
+
+[source, xml, subs="{sub-order}"]
+----
+  <resource-ref>
+     <description>My DataSource Reference</description>
+     <res-ref-name>jdbc/DSTest</res-ref-name>
+     <res-type>javax.sql.DataSource</res-type>
+     <res-auth>Container</res-auth>
+  </resource-ref>
+----
+
+These examples assume that all of the datasources are declared at the JVM scope, but you can use other scopes if desired.
+You can configure all JNDI resources in a `jetty.xml` file, a `WEB-INF/jetty-env.xml` file, or a context XML file.
+See the section xref:jndi-where-to-declare[] for more information.
+
+____
+[IMPORTANT]
+You must provide Jetty with the libraries necessary to instantiate the datasource you have configured by putting the corresponding Jar in `JETTY_HOME/lib/ext`.
+____
+
+[[pooling-datasources]]
+==== Pooling DataSources
+
+Pooling datasources enables connection pooling, which lets you reuse an existing connection instead of creating a new connection to the database.
+This is highly efficient in terms of memory allocation and speed of the request to the database.
+We highly recommend this option for production environments.
+
+The following is a list of the pooled datasource examples we have worked with in the past:
+
+* xref:hikaricp-datasource[]
+* xref:bonecp-datasource[]
+* xref:c3p0-datasource[]
+* xref:dbcp-datasource[]
+* xref:atomikos-datasource[]
+* xref:mysql-pooled-datasource[]
+* xref:postgreSQL-pooled-datasource[]
+* xref:DB2-pooled-datasource[]
+
+[[hikaricp-datasource]]
+===== HikariCP
+
+Connection pooling, available at http://search.maven.org/remotecontent?filepath=com/zaxxer/HikariCP/1.4.0/HikariCP-1.4.0.jar[HikariCP Download].
+All configuration options for HikariCP are described here: https://github.com/brettwooldridge/HikariCP[HikariCP documentation].
+
+[source, xml, subs="{sub-order}"]
+----
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg></Arg>
+     <Arg>jdbc/DSTest</Arg>
+     <Arg>
+       <New class="com.zaxxer.hikari.HikariDataSource">
+         <Arg>
+            <New class="com.zaxxer.hikari.HikariConfig">
+               <Set name="minimumPoolSize">5</Set>
+               <Set name="maximumPoolSize">20</Set>
+               <Set name="dataSourceClassName">com.mysql.jdbc.jdbc2.optional.MysqlDataSource</Set>
+               <Set name="username">jdbc.user</Set>
+               <Set name="password">jdbc.pass</Set>
+               <Call name="addDataSourceProperty">
+                  <Arg>url</Arg>
+                  <Arg>jdbc.url</Arg>
+               </Call>
+            </New>
+         </Arg>
+      </New>
+    </Arg>
+  </New>
+----
+
+[[bonecp-datasource]]
+===== BoneCP
+
+Connection pooling, available at http://jolbox.com/index.html?page=http://jolbox.com/download.html[BoneCP Download].
+All configuration options for BoneCP are described here: http://jolbox.com/bonecp/downloads/site/apidocs/com/jolbox/bonecp/BoneCPDataSource.html[BoneCP API].
+
+[source, xml, subs="{sub-order}"]
+----
+
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg></Arg>
+     <Arg>jdbc/DSTest</Arg>
+     <Arg>
+       <New class="com.jolbox.bonecp.BoneCPDataSource">
+         <Set name="driverClass">com.mysql.jdbc.Driver</Set>
+         <Set name="jdbcUrl">jdbc.url</Set>
+         <Set name="username">jdbc.user</Set>
+         <Set name="password">jdbc.pass</Set>
+         <Set name="minConnectionsPerPartition">5</Set>
+         <Set name="maxConnectionsPerPartition">50</Set>
+         <Set name="acquireIncrement">5</Set>
+         <Set name="idleConnectionTestPeriod">30</Set>
+      </New>
+    </Arg>
+  </New>
+----
+
+[[c3p0-datasource]]
+===== c3p0
+
+Connection pooling, available at http://central.maven.org/maven2/c3p0/c3p0/0.9.1.2/c3p0-0.9.1.2.jar[c3p0 Jar].
+
+[source, xml, subs="{sub-order}"]
+----
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg></Arg>
+     <Arg>jdbc/DSTest</Arg>
+     <Arg>
+      <New class="com.mchange.v2.c3p0.ComboPooledDataSource">
+         <Set name="driverClass">org.some.Driver</Set>
+         <Set name="jdbcUrl">jdbc.url</Set>
+         <Set name="user">jdbc.user</Set>
+         <Set name="password">jdbc.pass</Set>
+      </New>
+     </Arg>
+    </New>
+----
+
+[[dbcp-datasource]]
+===== DBCP
+
+Connection pooling, available at http://central.maven.org/maven2/commons-dbcp/commons-dbcp/1.2/commons-dbcp-1.2.jar[dbcp Jar].
+
+[source, xml, subs="{sub-order}"]
+----
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg></Arg>
+     <Arg>jdbc/DSTest</Arg>
+     <Arg>
+         <New class="org.apache.commons.dbcp.BasicDataSource">
+            <Set name="driverClassName">org.some.Driver</Set>
+            <Set name="url">jdbc.url</Set>
+            <Set name="username">jdbc.user</Set>
+            <Set name="password">jdbc.pass</Set>
+         </New>
+         </New>
+     </Arg>
+    </New>
+----
+
+[[atomikos-datasource]]
+===== Atomikos 3.3.2+
+
+Connection pooling + XA transactions.
+
+[source, xml, subs="{sub-order}"]
+----
+   <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+      <Arg></Arg>
+      <Arg>jdbc/DSTest</Arg>
+      <Arg>
+         <New class="com.atomikos.jdbc.AtomikosDataSourceBean">
+            <Set name="minPoolSize">2</Set>
+            <Set name="maxPoolSize">50</Set>
+            <Set name="xaDataSourceClassName">com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</Set>
+            <Set name="UniqueResourceName">DSTest</Set>
+            <Get name="xaProperties">
+               <Call name="setProperty">
+                  <Arg>url</Arg>
+                  <Arg>jdbc:mysql://localhost:3306/databasename</Arg>
+               </Call>
+               <Call name="setProperty">
+                  <Arg>user</Arg>
+                  <Arg>some_username</Arg>
+               </Call>
+               <Call name="setProperty">
+                  <Arg>password</Arg>
+                  <Arg>some_password</Arg>
+               </Call>
+            </Get>
+         </New>
+      </Arg>
+    </New>
+----
+
+[[mysql-pooled-datasource]]
+===== MySQL
+
+Implements `javax.sql.DataSource` and `javax.sql.ConnectionPoolDataSource`.
+
+[source, xml, subs="{sub-order}"]
+----
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg></Arg>
+     <Arg>jdbc/DSTest</Arg>
+     <Arg>
+        <New class="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource">
+           <Set name="Url">jdbc:mysql://localhost:3306/databasename</Set>
+           <Set name="User">user</Set>
+           <Set name="Password">pass</Set>
+        </New>
+     </Arg>
+    </New>
+----
+
+[[postgreSQL-pooled-datasource]]
+===== PostgreSQL
+
+Implements `javax.sql.ConnectionPoolDataSource`.
+
+[source, xml, subs="{sub-order}"]
+----
+
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg></Arg>
+     <Arg>jdbc/DSTest</Arg>
+     <Arg>
+        <New class="org.postgresql.ds.PGConnectionPoolDataSource">
+           <Set name="User">user</Set>
+           <Set name="Password">pass</Set>
+           <Set name="DatabaseName">dbname</Set>
+           <Set name="ServerName">localhost</Set>
+           <Set name="PortNumber">5432</Set>
+
+        </New>
+     </Arg>
+  </New>
+
+
+----
+
+[[DB2-pooled-datasource]]
+===== DB2
+
+Implements `javax.sql.ConnectionPoolDataSource`.
+
+[source, xml, subs="{sub-order}"]
+----
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg></Arg>
+     <Arg>jdbc/DSTest</Arg>
+     <Arg>
+        <New class="com.ibm.db2.jcc.DB2ConnectionPoolDataSource">
+           <Set name="DatabaseName">dbname</Set>
+           <Set name="User">user</Set>
+           <Set name="Password">pass</Set>
+           <Set name="ServerName">servername</Set>
+           <Set name="PortNumber">50000</Set>
+        </New>
+     </Arg>
+  </New>
+----
+
+[[non-pooling-datasources]]
+==== Non-pooling DataSources
+
+If you are deploying in a production environment, we highly recommend using a Pooling DataSource.
+Since that is not always an option we have a handful of examples for non-pooling datasources listed here as well.
+
+The following is a list of the non-pooled datasource examples:
+
+* xref:sql-server-2000-datasource[]
+* xref:oracle-9i10g-datasource[]
+* xref:postgreSQL-datasource[]
+* xref:sybase-datasource[]
+* xref:DB2-datasource[]
+
+[[sql-server-2000-datasource]]
+===== SQL Server 2000
+
+Implements `javax.sql.DataSource` and `javax.sql.ConnectionPoolDataSource`.
+
+[source, xml, subs="{sub-order}"]
+----
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg></Arg>
+     <Arg>jdbc/DSTest</Arg>
+     <Arg>
+        <New class="net.sourceforge.jtds.jdbcx.JtdsDataSource">
+           <Set name="User">user</Set>
+           <Set name="Password">pass</Set>
+           <Set name="DatabaseName">dbname</Set>
+           <Set name="ServerName">localhost</Set>
+           <Set name="PortNumber">1433</Set>
+        </New>
+     </Arg>
+    </New>
+----
+
+[[oracle-9i10g-datasource]]
+===== Oracle 9i/10g
+
+Implements `javax.sql.DataSource` and `javax.sql.ConnectionPoolDataSource`.
+
+[source, xml, subs="{sub-order}"]
+----
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+    <Arg></Arg>
+    <Arg>jdbc/DSTest</Arg>
+    <Arg>
+      <New class="oracle.jdbc.pool.OracleDataSource">
+        <Set name="DriverType">thin</Set>
+        <Set name="URL">jdbc:oracle:thin:@fmsswdb1:10017:otcd</Set>
+        <Set name="User">xxxx</Set>
+        <Set name="Password">xxxx</Set>
+        <Set name="connectionCachingEnabled">true</Set>
+        <Set name="connectionCacheProperties">
+          <New class="java.util.Properties">
+            <Call name="setProperty">
+              <Arg>MinLimit</Arg>
+              <Arg>5</Arg>
+            </Call>
+            <!-- put the other properties in here too -->
+          </New>
+        </Set>
+      </New>
+    </Arg>
+  </New>
+----
+
+For more information, refer to: http://docs.oracle.com/cd/B14117_01/java.101/b10979/conncache.htm[Oracle Database JDBC documentation].
+
+[[postgreSQL-datasource]]
+===== PostgreSQL
+
+Implements `javax.sql.DataSource`.
+
+[source, xml, subs="{sub-order}"]
+----
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg></Arg>
+     <Arg>jdbc/DSTest</Arg>
+     <Arg>
+        <New class="org.postgresql.ds.PGSimpleDataSource">
+           <Set name="User">user</Set>
+           <Set name="Password">pass</Set>
+           <Set name="DatabaseName">dbname</Set>
+           <Set name="ServerName">localhost</Set>
+           <Set name="PortNumber">5432</Set>
+        </New>
+     </Arg>
+  </New>
+----
+
+[[sybase-datasource]]
+===== Sybase
+
+Implements `javax.sql.DataSource`.
+
+[source, xml, subs="{sub-order}"]
+----
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg></Arg>
+     <Arg>jdbc/DSTest</Arg>
+     <Arg>
+        <New class="com.sybase.jdbc2.jdbc.SybDataSource">
+           <Set name="DatabaseName">dbname</Set>
+           <Set name="User">user</Set>
+           <Set name="Password">pass</Set>
+           <Set name="ServerName">servername</Set>
+           <Set name="PortNumber">5000</Set>
+        </New>
+     </Arg>
+  </New>
+----
+
+[[DB2-datasource]]
+===== DB2
+
+Implements `javax.sql.DataSource`.
+
+[source, xml, subs="{sub-order}"]
+----
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg></Arg>
+     <Arg>jdbc/DSTest</Arg>
+     <Arg>
+        <New class="com.ibm.db2.jcc.DB2SimpleDataSource">
+           <Set name="DatabaseName">dbname</Set>
+           <Set name="User">user</Set>
+           <Set name="Password">pass</Set>
+           <Set name="ServerName">servername</Set>
+           <Set name="PortNumber">50000</Set>
+        </New>
+     </Arg>
+  </New>
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/jndi/jndi-embedded.adoc b/jetty-documentation/src/main/asciidoc/administration/jndi/jndi-embedded.adoc
new file mode 100644
index 0000000..3504217
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jndi/jndi-embedded.adoc
@@ -0,0 +1,131 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jndi-embedded]]
+=== Using JNDI with Jetty Embedded
+
+==== Setting up the Classpath
+
+In addition to the jars that you require for your application, and the jars needed for core Jetty, you will need to place the following jars onto your classpath:
+
+....
+jetty-jndi.jar
+jetty-plus.jar
+....
+
+If you are using transactions, you will also need the `javax.transaction` api.
+You can obtain this jar link:{MVNCENTRAL}/org/eclipse/jetty/orbit/javax.transaction/1.1.1.v201105210645/javax.transaction-1.1.1.v201105210645.jar[here.]
+
+If you wish to use mail, you will also need the `javax.mail` api and implementation which link:{MVNCENTRAL/org/eclipse/jetty/orbit/javax.mail.glassfish/1.4.1.v201005082020/javax.mail.glassfish-1.4.1.v201005082020.jar[you can download here.]
+Note that this jar also requires the `javax.activation` classes, which is available link:{MVCENTRAL}/org/eclipse/jetty/orbit/javax.activation/1.1.0.v201105071233/javax.activation-1.1.0.v201105071233.jar[at this link.]
+
+==== Example Code
+
+Here is an example class that sets up some JNDI entries and deploys a webapp that references these JNDI entries in code.
+We'll use some mocked up classes for the transaction manager and the DataSource in this example for simplicity:
+
+[source, java, subs="{sub-order}"]
+----
+import java.util.Properties;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * ServerWithJNDI
+ *
+ *
+ */
+public class ServerWithJNDI
+{
+    public static void main(String[] args) throws Exception
+    {
+
+        //Create the server
+        Server server = new Server(8080);
+
+        //Enable parsing of jndi-related parts of web.xml and jetty-env.xml
+        org.eclipse.jetty.webapp.Configuration.ClassList classlist = org.eclipse.jetty.webapp.Configuration.ClassList.setServerDefault(server);
+        classlist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration", "org.eclipse.jetty.plus.webapp.EnvConfiguration", "org.eclipse.jetty.plus.webapp.PlusConfiguration");
+
+        //Create a WebApp
+        WebAppContext webapp = new WebAppContext();
+        webapp.setContextPath("/");
+        webapp.setWar("./my-foo-webapp.war");
+        server.setHandler(webapp);
+
+        //Register new transaction manager in JNDI
+        //At runtime, the webapp accesses this as java:comp/UserTransaction
+        org.eclipse.jetty.plus.jndi.Transaction transactionMgr = new org.eclipse.jetty.plus.jndi.Transaction(new com.acme.MockUserTransaction());
+
+        //Define an env entry with Server scope.
+        //At runtime, the webapp accesses this as java:comp/env/woggle
+        //This is equivalent to putting an env-entry in web.xml:
+        //<env-entry>
+        //  <env-entry-name>woggle</env-entry-name>
+        //  <env-entry-type>java.lang.Integer</env-entry-type>
+        //  <env-entry-value>4000</env-entry-value>
+        //</env-entry>
+        org.eclipse.jetty.plus.jndi.EnvEntry woggle = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "woggle", new Integer(4000), false);
+
+
+        //Define an env entry with webapp scope.
+        //At runtime, the webapp accesses this as java:comp/env/wiggle
+        //This is equivalent to putting a web.xml entry in web.xml:
+        //<env-entry>
+        //  <env-entry-name>wiggle</env-entry-name>
+        //  <env-entry-value>100</env-entry-value>
+        //  <env-entry-type>java.lang.Double</env-entry-type>
+        //</env-entry>
+        //Note that the last arg of "true" means that this definition for "wiggle" would override an entry of the
+        //same name in web.xml
+        org.eclipse.jetty.plus.jndi.EnvEntry wiggle = new org.eclipse.jetty.plus.jndi.EnvEntry(webapp, "wiggle", new Double(100), true);
+
+        //Register a reference to a mail service scoped to the webapp.
+        //This must be linked to the webapp by an entry in web.xml:
+        // <resource-ref>
+        //  <res-ref-name>mail/Session</res-ref-name>
+        //  <res-type>javax.mail.Session</res-type>
+        //  <res-auth>Container</res-auth>
+        // </resource-ref>
+        //At runtime the webapp accesses this as java:comp/env/mail/Session
+        org.eclipse.jetty.jndi.factories.MailSessionReference mailref = new org.eclipse.jetty.jndi.factories.MailSessionReference();
+        mailref.setUser("CHANGE-ME");
+        mailref.setPassword("CHANGE-ME");
+        Properties props = new Properties();
+        props.put("mail.smtp.auth", "false");
+        props.put("mail.smtp.host","CHANGE-ME");
+        props.put("mail.from","CHANGE-ME");
+        props.put("mail.debug", "false");
+        mailref.setProperties(props);
+        org.eclipse.jetty.plus.jndi.Resource xxxmail = new org.eclipse.jetty.plus.jndi.Resource(webapp, "mail/Session", mailref);
+
+
+        // Register a  mock DataSource scoped to the webapp
+        //This must be linked to the webapp via an entry in web.xml:
+        //<resource-ref>
+        //  <res-ref-name>jdbc/mydatasource</res-ref-name>
+        //  <res-type>javax.sql.DataSource</res-type>
+        //  <res-auth>Container</res-auth>
+        //</resource-ref>
+        //At runtime the webapp accesses this as java:comp/env/jdbc/mydatasource
+        org.eclipse.jetty.plus.jndi.Resource mydatasource = new org.eclipse.jetty.plus.jndi.Resource(webapp, "jdbc/mydatasource",
+                                                                                                     new com.acme.MockDataSource());
+
+        server.start();
+        server.join();
+    }
+}
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/jndi/quick-jndi-setup.adoc b/jetty-documentation/src/main/asciidoc/administration/jndi/quick-jndi-setup.adoc
new file mode 100644
index 0000000..3e38d4d
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jndi/quick-jndi-setup.adoc
@@ -0,0 +1,45 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jndi-quick-setup]]
+=== Quick Setup
+
+If you are using the standard distribution of Jetty, you must enable the _JNDI_ link:#startup-modules[module] to obtain Jetty's JNDI implementation, and the *plus* link:#startup-modules[module] which provides classes for interacting with JNDI.
+As the _plus_ module depends on the _JNDI_ module, you only need to enable the _plus_ module to enable both.
+Assuming you have Jetty installed in `/opt/jetty`, and you have made a link:#startup-base-and-home[jetty base] in `/opt/jetty/my-base`, do:
+
+[source,bash]
+----
+cd /opt/jetty
+cd my-base
+java -jar $JETTY_HOME/start.jar --add-to-startd=plus
+
+----
+
+You can now start Jetty and use JNDI within your webapps.
+See link:#using-jndi[Using JNDI] for information on how to add entries to the JNDI environment that Jetty can look up within webapps.
+
+If you have extra jars associated with your JNDI resources, for example a database driver jar, and you haven't made a custom link:#startup-modules[module] for it, you can put the jars into your `{$jetty base}ext/` directory.
+You will then need to enable the _ext_ module to ensure the jars in the `ext/` directory are on the classpath.
+Assuming you have Jetty installed in `/opt/jetty`, and you have made a link:#startup-base-and-home[jetty base] in `/opt/jetty/my-base`, do:
+
+[source,bash]
+----
+cd /opt/jetty
+cd my-base
+java -jar $JETTY_HOME/start.jar --add-to-startd=ext
+
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/jndi/using-jndi.adoc b/jetty-documentation/src/main/asciidoc/administration/jndi/using-jndi.adoc
new file mode 100644
index 0000000..a5a49ed
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/jndi/using-jndi.adoc
@@ -0,0 +1,152 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[using-jetty-jndi]]
+=== Working with Jetty JNDI
+
+==== Defining the web.xml
+
+You can configure naming resources to reference in a `web.xml` file and access from within the `java:comp/env` naming environment of the webapp during execution.
+Specifically, you can configure support for the following `web.xml` elements:
+
+[source, xml, subs="{sub-order}"]
+----
+<env-entry/>
+<resource-ref/>
+<resource-env-ref/>
+----
+
+link:#configuring-jndi-env-entries[Configuring env-entries] shows you how to set up overrides for `env-entry` elements in `web.xml`, while link:#configuring-resource-refs-and-resource-env-refs[Configuring `resource-refs` and `resource-env-refs`] discusses how to configure support resources such as `javax.sql.DataSource`.
+
+You can also plug a JTA `javax.transaction.UserTransaction` implementation into Jetty so that webapps can look up `java:comp/UserTransaction` to obtain a distributed transaction manager: see link:#configuring-xa-transactions[Configuring XA Transactions].
+
+[[defining-jndi-naming-entries]]
+==== Declaring Resources
+
+You must declare the objects you want bound into the Jetty environment so that you can then hook into your webapp via `env-entry`, `resource-ref` and `resource-env-refs` in `web.xml`.
+You create these bindings by using declarations of the following types:
+
+`org.eclipse.jetty.plus.jndi.EnvEntry`::
+For `env-entry` type of entries
+`org.eclipse.jetty.plus.jndi.Resource`::
+For all other type of resources
+`org.eclipse.jetty.plus.jndi.Transaction`::
+For a JTA manager
+`org.eclipse.jetty.plus.jndi.Link`::
+For the link between a `web.xml` resource name and a naming entry
+
+Declarations of each of these types follow the same general pattern:
+
+[source, xml, subs="{sub-order}"]
+----
+<New class="org.eclipse.jetty.plus.jndi.xxxx">
+  <Arg><!-- scope --></Arg>
+  <Arg><!-- name --></Arg>
+  <Arg><!-- value --></Arg>
+</New>
+----
+
+You can place these declarations into three different files, depending on your needs and the link:#jndi-name-scope[scope] of the resources being declared.
+
+[[jndi-where-to-declare]]
+==== Deciding Where to Declare Resources
+
+You can define naming resources in three places:
+
+_jetty.xml_::
+Naming resources defined in a `jetty.xml` file are link:#jndi-name-scope[scoped] at either the JVM level or the Server level.
+The classes for the resource must be visible at the Jetty container level.
+If the classes for the resource only exist inside your webapp, you must declare it in a `WEB-INF/jetty-env.xml` file.
+WEB-INF/jetty-env.xml::
+Naming resources in a `WEB-INF/jetty-env.xml` file are link:#jndi-name-scope[scoped] to the web app in which the file resides.
+While you can enter JVM or Server scopes if you choose, we do not recommend doing so.
+The resources defined here may use classes from inside your webapp.
+This is a Jetty-specific mechanism.
+Context xml file::
+Entries in a context xml file should be link:#jndi-name-scope[scoped] at the level of the webapp to which they apply, although you can supply a less strict scoping level of Server or JVM if you choose.
+As with resources declared in a `jetty.xml` file, classes associated with the resource must be visible on the container's classpath.
+
+[[jndi-name-scope]]
+==== Scope of Resource Names
+
+Naming resources within Jetty belong to one of three different scopes, in increasing order of restrictiveness:
+
+JVM scope::
+The name is unique across the JVM instance, and is visible to all application code.
+You represent this scope by a `null` first parameter to the resource declaration.
+For example:
++
+[source, xml, subs="{sub-order}"]
+----
+
+<New id="cf" class="org.eclipse.jetty.plus.jndi.Resource">
+  <Arg></Arg>  <!-- empty arg -->
+  <Arg>jms/connectionFactory</Arg>
+  <Arg>
+    <New class="org.apache.activemq.ActiveMQConnectionFactory">
+       <Arg>vm://localhost?broker.persistent=false</Arg>
+    </New>
+  </Arg>
+</New>
+----
+Server scope::
+The name is unique to a Server instance, and is only visible to code associated with that instance.
+You represent this scope by referencing the Server instance as the first parameter to the resource declaration.
+For example:
++
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="Server" class="org.eclipse.jetty.Server">
+  <New id="cf" class="org.eclipse.jetty.plus.jndi.Resource">
+    <Arg><Ref refid="Server"/></Arg>  <!-- reference to Server instance -->
+    <Arg>jms/connectionFactory</Arg>
+    <Arg>
+      <New class="org.apache.activemq.ActiveMQConnectionFactory">
+        <Arg>vm://localhost?broker.persistent=false</Arg>
+      </New>
+    </Arg>
+  </New>
+</Configure>
+----
+Webapp scope::
+The name is unique to the WebAppContext instance, and is only visible to code associated with that instance.
+You represent this scope by referencing the `WebAppContext` instance as the first parameter to the resource declaration.
+For example:
++
+[source, xml, subs="{sub-order}"]
+----
+<Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
+  <New id="cf" class="org.eclipse.jetty.plus.jndi.Resource">
+    <Arg><Ref refid='wac'/></Arg> <!-- reference to WebAppContext -->
+    <Arg>jms/connectionFactory</Arg>
+    <Arg>
+      <New class="org.apache.activemq.ActiveMQConnectionFactory">
+        <Arg>vm://localhost?broker.persistent=false</Arg>
+      </New>
+    </Arg>
+  </New>
+</Configure>
+----
+
+[[binding-objects-into-jetty-jndi]]
+==== What Can Be Bound as a Resource?
+
+You can bind four types of objects into a Jetty JNDI reference:
+
+* An ordinary POJO instance.
+* A http://docs.oracle.com/javase/1.5.0/docs/api/javax/naming/Reference.html[javax.naming.Reference] instance.
+* An object instance that implements the http://docs.oracle.com/javase/1.5.0/docs/api/javax/naming/Referenceable.html[javax.naming.Referenceable] interface.
+* A link between a name as referenced in `web.xml` and as referenced in the Jetty environment.
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/chapter.adoc
new file mode 100644
index 0000000..738f010
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/chapter.adoc
@@ -0,0 +1,31 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-logging]]
+== Jetty Logging
+
+This chapter discusses various options for configuring logging.
+
+include::configuring-jetty-logging.adoc[]
+include::default-logging-with-stderrlog.adoc[]
+include::configuring-jetty-request-logs.adoc[]
+include::example-apache-log4j.adoc[]
+include::example-java-util-logging.adoc[]
+include::example-java-util-logging-native.adoc[]
+include::example-logback.adoc[]
+include::example-slf4j-multiple-loggers.adoc[]
+include::example-logback-centralized-logging.adoc[]
+include::dump-tool.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/configuring-jetty-logging.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/configuring-jetty-logging.adoc
new file mode 100644
index 0000000..576e7fb
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/configuring-jetty-logging.adoc
@@ -0,0 +1,72 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-jetty-logging]]
+=== Configuring Jetty Logging
+
+Jetty provides logging via its own `org.eclipse.jetty.util.log.Logger` layer, and does not natively use any existing Java logging framework.
+All logging events, produced via the Jetty logging layer, have a name, a level, and a message.
+The name is a FQCN (fully qualified class name) similar to how all existing Java logging frameworks operate.
+
+Jetty logging, however, has a slightly different set of levels that it uses internally:
+
+WARN::
+  For events serious enough to inform and log, but not fatal.
+INFO::
+  Informational events.
+DEBUG::
+  Debugging events (very noisy).
+IGNORE::
+  Exception events that you can safely ignore, but useful for some people.
+  You might see this level as DEBUG under some Java logging framework configurations, where it retains the _ignore_ phrase somewhere in the logging.
+____
+[NOTE]
+Jetty logging produces no FATAL or SEVERE events.
+____
+
+[[selecting-log-framework]]
+==== Selecting the Log Framework
+
+Configure the Jetty logging layer via the `org.eclipse.jetty.util.log.Log` class, following link:{GITBROWSEURL}/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java[these rules].
+
+1.  Load Properties
+* First from a Classpath Resource called `jetty-logging.properties` (if found).
+* Then from the `System.getProperties()`.
+2.  Determine the log implementation.
+* If property `org.eclipse.jetty.util.log.class` is defined, load the class it defines as the logger implementation from the server `classpath`.
+* If the class `org.slf4j.Logger` exists in server classpath, the Jetty implementation becomes `org.eclipse.jetty.util.log.Slf4jLog`.
+* If no logger implementation is specified, default to `org.eclipse.jetty.util.log.StdErrLog`.
+____
+[NOTE]
+You can create your own custom logging by providing an implementation of the link:{JDURL}org/eclipse/jetty/util/log/Logger.html[Jetty Logger API]. 
+For an example of a custom logger, see link:{GITBROWSEURL}/jetty-util/src/main/java/org/eclipse/jetty/util/log/JavaUtilLog.java[JavaUtilLog.java].
+____
+
+[[configuring-jetty-stderrlog]]
+==== The jetty-logging.properties file
+
+By default, the internal Jetty Logging discovery mechanism will load logging specific properties from a classpath resource called `jetty-logging.properties` and then initialize the Logging from a combination of properties found in that file, along with any System Properties.
+A typical jetty-logging.properties file will include at least the declaration of which logging implementation you want to use by defining a value for the `org.eclipse.jetty.util.log.class` property.
+  
+Examples for various logging frameworks can be found later in this documentation.
+
+* Default Logging with link:#default-logging-with-stderrlog[Jetty's StdErrLog]
+* Using link:#example-logging-log4j[Log4j via Slf4jLog]
+* Using link:#example-logging-logback[Logback via Slf4jLog]
+* Using java.util.logging via Slf4jLog
+* Using java.util.logging via Jetty's JavaUtilLog
+* Capturing link:#example-slf4j-multiple-loggers[Multiple Logging Frameworks via Slf4jLog]
+* link:#example-logging-logback-centralized[Centralized Logging with Logback and Sfl4jLog]
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/configuring-jetty-request-logs.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/configuring-jetty-request-logs.adoc
new file mode 100644
index 0000000..a96a4a4
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/configuring-jetty-request-logs.adoc
@@ -0,0 +1,116 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-jetty-request-logs]]
+=== Configuring Jetty Request Logs
+
+Request logs are a record of the requests that the server has processed.
+There is one entry per request received, and commonly in the standard NCSA format, so you can use tools like http://en.wikipedia.org/wiki/Webalizer[Webalizer] to analyze them conveniently.
+
+[[constructing-request-log-entry]]
+==== Constructing a Request Log Entry
+
+A standard request log entry includes the client IP address, date, method, URL, result, size, referrer, user agent and latency.
+For example:
+
+....
+123.4.5.6 - - [20/Jul/2016:10:16:17 +0000]
+  "GET /jetty/tut/XmlConfiguration.html HTTP/1.1"
+  200 76793 "http://localhost:8080/jetty/tut/logging.html"
+  "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6) Gecko/20040614 Firefox/0.8" 342
+....
+
+[[implementing-request-log]]
+==== Implementing a Request Log
+
+Jetty provides an implementation called `NCSARequestLog` which supports the NCSA format in files that will roll over on a daily basis.
+
+The http://logback.qos.ch/[Logback Project] offers http://logback.qos.ch/access.html[another implementation] of a `RequestLog` interface, providing rich and powerful HTTP-access log functionality.
+
+If neither of these options meets your needs, you can implement a custom request logger by implementing Jetty's link:{JDURL}/org/eclipse/jetty/server/RequestLog.html[`RequestLog.java`] interface and plugging it in similar to the `NCSARequestLog`, as shown below.
+
+[[configuring-request-log]]
+==== Configuring the Request Log module
+
+To enable the Request Log module for the entire server via the Jetty distribution, it first needs to be enabled on the command line:
+
+[source, screen, subs="{sub-order}"]
+----
+$ java -jar ../start.jar --add-to-startd=requestlog
+
+INFO: requestlog      initialised in ${jetty.base}/start.d/requestlog.ini
+MKDIR: ${jetty.base}/logs
+INFO: Base directory was modified
+----
+
+The above command will add a new `requestlog.ini` file to your `{$jetty.base}/start.d` directory.
+If you used `--add-to-start` it will append the configuration options for the module to the `start.ini` file located in your `{$jetty.base}` directory.
+
+The equivalent code for embedded usages of Jetty is:
+
+[source, java, subs="{sub-order}"]
+----
+NCSARequestLog requestLog = new NCSARequestLog("/var/logs/jetty/jetty-yyyy_mm_dd.request.log");
+requestLog.setAppend(true);
+requestLog.setExtended(false);
+requestLog.setLogTimeZone("GMT");
+requestLog.setLogLatency(true);
+requestLog.setRetainDays("90");
+
+server.setRequestLog(requestLog);
+----
+
+This configures a request log in `{$jetty.home}/logs` with filenames including the date.
+Existing log files are appended to and the extended NCSA format is used in the GMT time zone.
+
+The above configuration enables Log Latency, which is the amount of time it took the server to handle the request.
+This value is measured in milliseconds and is appended to the the log file for each request.
+
+You can also customize the number of days you wish to keep request logs.
+By default, log files are kept for 90 days before being deleted.
+The value for `retainDays` (xml) or `setRetainDays` (Java) should be configured as _1 + n_ days.
+For example, if you wanted to keep the logs for the current day and the day prior you would set the `retainDays` (or `setRetainDays`) value to 2.
+
+To examine more configuration options, see link:{JDURL}/org/eclipse/jetty/server/NCSARequestLog.html[NCSARequestLog.java].
+
+[[configuring-separate-request-log-for-web-application]]
+==== Configuring a Separate Request Log For a Web Application
+
+To configure a separate request log for specific a web application, add the following to the context XML file.
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  ...
+  <Call name="insertHandler">
+    <Arg>
+      <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler">
+        <Set name="requestLog">
+          <New id="RequestLogImpl" class="org.eclipse.jetty.server.NCSARequestLog">
+            <Set name="filename"><Property name="jetty.logs" default="./logs"/>/test-yyyy_mm_dd.request.log</Set>
+            <Set name="filenameDateFormat">yyyy_MM_dd</Set>
+            <Set name="LogTimeZone">GMT</Set>
+            <Set name="retainDays">90</Set>
+            <Set name="append">true</Set>
+            <Set name="LogLatency">true</Set>
+          </New>
+        </Set>
+      </New>
+    </Arg>
+  </Call>
+  ...
+</Configure>
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/default-logging-with-stderrlog.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/default-logging-with-stderrlog.adoc
new file mode 100644
index 0000000..ff68b10
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/default-logging-with-stderrlog.adoc
@@ -0,0 +1,125 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[default-logging-with-stderrlog]]
+=== Default Logging with Jetty's StdErrLog
+
+[[stderrlog-configuration]]
+==== StdErrLog Configuration
+
+If you do nothing to configure a separate logging framework, Jetty will default to using an internal `org.eclipse.jetty.util.log.StdErrLog` implementation.
+This will output all logging events to STDERR (aka `System.err`).
+
+Simply use Jetty and `StdErrLog` based logging is output.
+
+Included in the Jetty distribution is a logging module that is capable of performing simple capturing of all STDOUT and STDERR output to a file that is rotated daily.
+
+To enable on this feature via the command line:
+
+[source,bash]
+----
+[my-base]$ java -jar /opt/jetty/start.jar --module=logging
+----
+
+You can also include the `--module=logging` command in your `${jetty.base}/start.ini`.
+
+[source,bash]
+----
+[my-base]$ java -jar /opt/jetty/start.jar --add-to-start=logging
+----
+
+The default configuration for logging output will create a file `${jetty.logs}/yyyy_mm_dd.stderrout.log` which allows configuration of the output directory by setting the `jetty.logs` property.
+
+For more advanced logging configurations, please consider use of a separate logging library.
+
+[[stderr-properties]]
+==== The jetty-logging.properties File
+The recommended way to configure `StdErrLog` is to create a `${jetty.home}/resources/jetty-logging.properties` file, specify the log implementation to `StdErrLog` and then setup logging levels.
+
+[source,properties]
+----
+# Configure Jetty for StdErrLog Logging
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StrErrLog
+# Overall Logging Level is INFO
+org.eclipse.jetty.LEVEL=INFO
+# Detail Logging for WebSocket
+org.eclipse.jetty.websocket.LEVEL=DEBUG
+----
+
+There are a number of properties that can be defined in the configuration that will affect the behavior of `StdErrLog`.
+
+`<name>.LEVEL=<level>`::
+  Sets the logging level for all loggers within the `name` specified to the level, which can be (in increasing order of restriction) `ALL`, `DEBUG`, `INFO`, `WARN`, `OFF`.
+  The name (or hierarchy) can be a specific fully qualified class or a package namespace.
+  For example, `org.eclipse.jetty.http.LEVEL=DEBUG` is a package namespace approach to turn all loggers in the Jetty HTTP package to DEBUG level, and  `org.eclipse.jetty.io.ChanelEndPoint.LEVEL=ALL` turns on all logging events for the specific class, including `DEBUG`, `INFO`, `WARN` (and even special internally ignored exception classes).
+  If more than one system property specifies a logging level, the most specific one applies.
+`<name>.SOURCE=<boolean>`::
+  Named Logger specific, attempts to print the Java source file name and line number from where the logging event originated.
+  Name must be a fully qualified class name (this configurable does not support package name hierarchy).
+  Default is false.
+  Be aware that this is a slow operation and has an impact on performance.
+`<name>.STACKS=<boolean>`::
+  Named Logger specific, controls the display of stacktraces.
+  Name must be a fully qualified class name (this configurable does not support package name hierarchy).
+  Default is true.
+`org.eclipse.jetty.util.log.stderr.SOURCE=<boolean>`::
+  Special Global Configuration.
+  Attempts to print the Java source file name and line number from where the logging event originated.
+  Default is false.
+`org.eclipse.jetty.util.log.stderr.LONG=<boolean>`::
+  Special Global Configuration.
+  When true, outputs logging events to `STDERR` using long form, fully qualified class names.
+  When false, uses abbreviated package names.
+  Default is false.
++
+  * Example when set to false:
++
+[source, screen, subs="{sub-order}"]
+....
+2014-06-03 14:36:16.013:INFO:oejs.Server:main: jetty-9.2.0.v20140526
+2014-06-03 14:36:16.028:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:/opt/jetty/demo-base/webapps/] at interval 1
+2014-06-03 14:36:16.051:INFO:oejsh.ContextHandler:main: Started o.e.j.s.h.MovedContextHandler@7d256e50{/oldContextPath,null,AVAILABLE}
+2014-06-03 14:36:17.880:INFO:oejs.ServerConnector:main: Started ServerConnector@34f2d11a{HTTP/1.1}{0.0.0.0:8080}
+2014-06-03 14:36:17.888:INFO:oejs.Server:main: Started @257ms
+....
++
+  * Example when set to true:
++
+[source, screen, subs="{sub-order}"]
+....
+2014-06-03 14:38:19.019:INFO:org.eclipse.jetty.server.Server:main: jetty-9.2.0.v20140526
+2014-06-03 14:38:19.032:INFO:org.eclipse.jetty.deploy.providers.ScanningAppProvider:main: Deployment monitor [file:/opt/jetty/demo-base/webapps/] at interval 1
+2014-06-03 14:38:19.054:INFO:org.eclipse.jetty.server.handler.ContextHandler:main: Started o.e.j.s.h.MovedContextHandler@246d8660{/oldContextPath,null,AVAILABLE}
+2014-06-03 14:38:20.715:INFO:org.eclipse.jetty.server.ServerConnector:main: Started ServerConnector@59f625be{HTTP/1.1}{0.0.0.0:8080}
+2014-06-03 14:38:20.723:INFO:org.eclipse.jetty.server.Server:main: Started @243ms
+....
+
+[[deprecated-parameters]]
+==== Deprecated Parameters
+
+These parameters existed in prior versions of Jetty, and are no longer supported.
+They are included here for historical (and search engine) reasons.
+
+`org.eclipse.jetty.util.log.DEBUG`::
+  Formerly used to enable DEBUG level logging on any logger used within Jetty (not just Jetty's own logger).
+  * Replaced with using the logger implementation specific configuration and level filtering.
+`org.eclipse.jetty.util.log.stderr.DEBUG`::
+  Formerly used to enable DEBUG level logging on the internal Jetty `StdErrLog` implementation.
+  * Replaced with level specific equivalent.
+    Example: `org.eclipse.jetty.LEVEL=DEBUG`
+`DEBUG`::
+  Ancient debugging flag that turned on all debugging, even non-logging debugging.
+  * Jetty no longer uses because many third party libraries employ this overly simple property name, which would generate far too much console output.
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/dump-tool.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/dump-tool.adoc
new file mode 100644
index 0000000..f2b8085
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/dump-tool.adoc
@@ -0,0 +1,835 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-dump-tool]]
+=== Jetty Dump Tool
+
+The dump feature in Jetty provides a good snapshot of the status of the threadpool, select sets, classloaders, and so forth. 
+To get maximum detail from the dump, you need to `setDetailDump(true)` on any `QueuedThreadPools` you are using. 
+You can do this by a direct call if you are embedding Jetty, or in `jetty.xml`.
+
+[[configuring-dump-feature]]
+==== Configuring the Dump Feature in jetty.xml
+
+You can request that Jetty do a dump immediately after starting and just before stopping by calling the appropriate setters on the `Server` instance. 
+This can be accomplished in `jetty.xml` with:
+
+[source, xml, subs="{sub-order}"]
+----
+<Set name="dumpAfterStart">true</Set>
+<Set name="dumpBeforeStop">true</Set>
+----
+
+[[extra-threadpool-info]]
+==== Extra ThreadPool Information
+
+You can get additional detail from the `QueuedThreadPool` if `setDetailedDump(true)` is called on the thread pool instance. 
+Do this in `jetty.xml` as follows:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+  <!-- ==================================== -->
+  <!-- Server Thread Pool                   --> 
+  <!-- ==================================== -->
+  <Set name="ThreadPool">
+     <!-- Default queued blocking threadpool -->
+     <New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
+       <Set name="minThreads">10</Set>
+       <Set name="maxThreads">200</Set>
+       <Set name="detailedDump">true</Set>
+     </New>
+  </Set>
+----
+
+[[dump-tool-via-jmx]]
+==== Using the Dump Feature via JMX
+
+The `dump` method is on the Server instance and many of its nested components (Handlers, Connectors, and so forth). 
+Dumps may be obtained by calling these methods either in code or via JMX (see xref:using-jmx[]).
+
+The Server MBean has a `dump()` method, which dumps everything, plus a `dumpStdErr()` operation that dumps to StdErr rather than replying to JConsole.
+
+[[examing-jetty-distro-dump]]
+==== Examining a Jetty Distribution Dump
+
+This is a dump of the stock jetty-distribution with extra threadpool information:
+
+....
+org.eclipse.jetty.server.Server@76f08fe1 - STARTING
+ += qtp1062680061{STARTED,10<=13<=200,i=1,q=0} - STARTED
+ |   +- 12 qtp1062680061-12-selector-0 RUNNABLE
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:159)
+ |   |   +- sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103)
+ |   |   +- sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:102)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.run(SelectorManager.java:435)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
+ |   |   +- java.lang.Thread.run(Thread.java:722)
+ |   +- 13 qtp1062680061-13-selector-6 RUNNABLE
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:159)
+ |   |   +- sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103)
+ |   |   +- sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:102)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.run(SelectorManager.java:435)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
+ |   |   +- java.lang.Thread.run(Thread.java:722)
+ |   +- 14 qtp1062680061-14-selector-5 RUNNABLE
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:159)
+ |   |   +- sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103)
+ |   |   +- sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:102)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.run(SelectorManager.java:435)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
+ |   |   +- java.lang.Thread.run(Thread.java:722)
+ |   +- 15 qtp1062680061-15-acceptor-0-ServerConnector@3d0f282{HTTP/1.1}{0.0.0.0:9090} BLOCKED
+ |   |   +- sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:210)
+ |   |   +- org.eclipse.jetty.server.ServerConnector.accept(ServerConnector.java:284)
+ |   |   +- org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:460)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
+ |   |   +- java.lang.Thread.run(Thread.java:722)
+ |   +- 16 qtp1062680061-16-selector-1 RUNNABLE
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:159)
+ |   |   +- sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103)
+ |   |   +- sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:102)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.run(SelectorManager.java:435)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
+ |   |   +- java.lang.Thread.run(Thread.java:722)
+ |   +- 17 qtp1062680061-17-selector-2 RUNNABLE
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:159)
+ |   |   +- sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103)
+ |   |   +- sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:102)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.run(SelectorManager.java:435)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
+ |   |   +- java.lang.Thread.run(Thread.java:722)
+ |   +- 18 qtp1062680061-18-selector-3 RUNNABLE
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:159)
+ |   |   +- sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103)
+ |   |   +- sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:102)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.run(SelectorManager.java:435)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
+ |   |   +- java.lang.Thread.run(Thread.java:722)
+ |   +- 19 qtp1062680061-19-selector-4 RUNNABLE
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:159)
+ |   |   +- sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103)
+ |   |   +- sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:102)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.run(SelectorManager.java:435)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
+ |   |   +- java.lang.Thread.run(Thread.java:722)
+ |   +- 20 qtp1062680061-20-selector-7 RUNNABLE
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
+ |   |   +- sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:159)
+ |   |   +- sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103)
+ |   |   +- sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
+ |   |   +- sun.nio.ch.SelectorImpl.select(SelectorImpl.java:102)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.run(SelectorManager.java:435)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
+ |   |   +- java.lang.Thread.run(Thread.java:722)
+ |   +- 21 qtp1062680061-21-acceptor-1-ServerConnector@3d0f282{HTTP/1.1}{0.0.0.0:9090} RUNNABLE
+ |   |   +- sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
+ |   |   +- sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:226)
+ |   |   +- org.eclipse.jetty.server.ServerConnector.accept(ServerConnector.java:284)
+ |   |   +- org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:460)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
+ |   |   +- java.lang.Thread.run(Thread.java:722)
+ |   +- 49 qtp1062680061-49-acceptor-2-ServerConnector@3d0f282{HTTP/1.1}{0.0.0.0:9090} BLOCKED
+ |   |   +- sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:210)
+ |   |   +- org.eclipse.jetty.server.ServerConnector.accept(ServerConnector.java:284)
+ |   |   +- org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:460)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
+ |   |   +- java.lang.Thread.run(Thread.java:722)
+ |   +- 50 qtp1062680061-50-acceptor-3-ServerConnector@3d0f282{HTTP/1.1}{0.0.0.0:9090} BLOCKED
+ |   |   +- sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:210)
+ |   |   +- org.eclipse.jetty.server.ServerConnector.accept(ServerConnector.java:284)
+ |   |   +- org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:460)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
+ |   |   +- org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
+ |   |   +- java.lang.Thread.run(Thread.java:722)
+ |   +- 52 qtp1062680061-52 TIMED_WAITING IDLE
+ += org.eclipse.jetty.util.thread.ScheduledExecutorScheduler@725f5 - STARTED
+ += org.eclipse.jetty.server.handler.HandlerCollection@58b37561 - STARTED
+ |   += org.eclipse.jetty.server.handler.ContextHandlerCollection@64c6e290 - STARTED
+ |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   += o.e.j.w.WebAppContext@7ea88b1c{/async-rest,[file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/, jar:file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/example-async-rest-jar-9.0.2.v20130417.jar!/META-INF/resources/],AVAILABLE}{/async-rest.war} - STARTED
+ |   |   |   += org.eclipse.jetty.server.session.SessionHandler@6dfb8d2e - STARTED
+ |   |   |   |   += org.eclipse.jetty.server.session.HashSessionManager@6cb83869 - STARTED
+ |   |   |   |   += org.eclipse.jetty.security.ConstraintSecurityHandler@2848c90e - STARTED
+ |   |   |   |   |   +- org.eclipse.jetty.security.DefaultAuthenticatorFactory@52b12fef
+ |   |   |   |   |   += org.eclipse.jetty.servlet.ServletHandler@46bac287 - STARTED
+ |   |   |   |   |   |   += default@5c13d641==org.eclipse.jetty.servlet.DefaultServlet,0,true - STARTED
+ |   |   |   |   |   |   |   +- maxCacheSize=256000000
+ |   |   |   |   |   |   |   +- etags=true
+ |   |   |   |   |   |   |   +- dirAllowed=true
+ |   |   |   |   |   |   |   +- gzip=true
+ |   |   |   |   |   |   |   +- maxCachedFileSize=200000000
+ |   |   |   |   |   |   |   +- redirectWelcome=false
+ |   |   |   |   |   |   |   +- acceptRanges=true
+ |   |   |   |   |   |   |   +- welcomeServlets=false
+ |   |   |   |   |   |   |   +- aliases=false
+ |   |   |   |   |   |   |   +- useFileMappedBuffer=true
+ |   |   |   |   |   |   |   +- maxCachedFiles=2048
+ |   |   |   |   |   |   +- [/]=>default
+ |   |   |   |   |   |   += jsp@19c47==org.apache.jasper.servlet.JspServlet,0,true - STARTED
+ |   |   |   |   |   |   |   +- logVerbosityLevel=DEBUG
+ |   |   |   |   |   |   |   +- fork=false
+ |   |   |   |   |   |   |   +- com.sun.appserv.jsp.classpath=/home/user/jetty-distribution-{VERSION}/lib/jetty-xml-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/servlet-api-3.0.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-http-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-continuation-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-server-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-security-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-servlet-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-webapp-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-deploy-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-client-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-jmx-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/com.sun.el-2.2.0.v201303151357.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.el-2.2.0.v201303151357.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp-2.2.0.v201112011158.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.jasper.glassfish-2.2.2.v201112011158.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar:/home/user/jetty-distribution-{VERSION}/resources:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-api-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-common-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-server-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-servlet-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-util-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-io-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/start.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/opt/local/lib/libsvnjavahl-1.0.dylib:/System/Library/Java/Extensions/AppleScriptEngine.jar:/System/Library/Java/Extensions/dns_sd.jar:/System/Library/Java/Extensions/j3daudio.jar:/System/Library/Java/Extensions/j3dcore.jar:/System/Library/Java/Extensions/j3dutils.jar:/System/Library/Java/Extensions/jai_codec.jar:/System/Library/Java/Extensions/jai_core.jar:/System/Library/Java/Extensions/libAppleScriptEngine.jnilib:/System/Library/Java/Extensions/libJ3D.jnilib:/System/Library/Java/Extensions/libJ3DAudio.jnilib:/System/Library/Java/Extensions/libJ3DUtils.jnilib:/System/Library/Java/Extensions/libmlib_jai.jnilib:/System/Library/Java/Extensions/libQTJNative.jnilib:/System/Library/Java/Extensions/mlibwrapper_jai.jar:/System/Library/Java/Extensions/MRJToolkit.jar:/System/Library/Java/Extensions/QTJava.zip:/System/Library/Java/Extensions/vecmath.jar:/usr/lib/java/libjdns_sd.jnilib
+ |   |   |   |   |   |   |   +- scratchdir=/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/jsp
+ |   |   |   |   |   |   |   +- xpoweredBy=false
+ |   |   |   |   |   |   +- [*.jsp, *.jspf, *.jspx, *.xsp, *.JSP, *.JSPF, *.JSPX, *.XSP]=>jsp
+ |   |   |   |   |   |   += SerialRestServlet@461411d==org.eclipse.jetty.example.asyncrest.SerialRestServlet,-1,false - STARTED
+ |   |   |   |   |   |   +- [/testSerial]=>SerialRestServlet
+ |   |   |   |   |   |   += AsyncRestServlet@73eb9bd5==org.eclipse.jetty.example.asyncrest.AsyncRestServlet,-1,false - STARTED
+ |   |   |   |   |   |   +- [/testAsync]=>AsyncRestServlet
+ |   |   |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   |   |   += HashLoginService[Test Realm] - STARTED
+ |   |   |   |   |   +- org.eclipse.jetty.security.DefaultIdentityService@d2539a6
+ |   |   |   |   |   +- org.eclipse.jetty.security.authentication.BasicAuthenticator@7b239469
+ |   |   |   |   |   |
+ |   |   |   |   |   +> HashLoginService[Test Realm] - STARTED
+ |   |   |   |   |   +> org.eclipse.jetty.security.DefaultIdentityService@d2539a6
+ |   |   |   |   |   +> org.eclipse.jetty.security.authentication.BasicAuthenticator@7b239469
+ |   |   |   |   |   +> []
+ |   |   |   |   |   +> /={TRACE={RoleInfo,F,C[]}}
+ |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   += org.eclipse.jetty.servlet.ErrorPageErrorHandler@3c121009 - STARTED
+ |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   |
+ |   |   |   +> WebAppClassLoader=Async REST Webservice Example@52934ea0
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/classes/
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/example-async-rest-jar-9.0.2.v20130417.jar
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/jetty-client-{VERSION}.jar
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/jetty-http-{VERSION}.jar
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/jetty-io-{VERSION}.jar
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/jetty-util-{VERSION}.jar
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/jetty-util-ajax-{VERSION}.jar
+ |   |   |   |   +- startJarLoader@7194b34a
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-xml-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/servlet-api-3.0.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-http-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-continuation-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-server-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-security-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-servlet-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-webapp-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-deploy-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-client-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-jmx-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/com.sun.el-2.2.0.v201303151357.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.el-2.2.0.v201303151357.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp-2.2.0.v201112011158.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.jasper.glassfish-2.2.2.v201112011158.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/resources/
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-api-9.0.2.v20130417.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-common-9.0.2.v20130417.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-server-9.0.2.v20130417.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-servlet-9.0.2.v20130417.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-util-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-io-{VERSION}.jar
+ |   |   |   |       +- sun.misc.Launcher$AppClassLoader@19d1b44b
+ |   |   |   |           +- file:/home/user/jetty-distribution-{VERSION}/start.jar
+ |   |   |   |           +- sun.misc.Launcher$ExtClassLoader@1693b52b
+ |   |   |   +> javax.servlet.context.tempdir=/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-
+ |   |   |   +> org.apache.catalina.jsp_classpath=/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/classes:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/example-async-rest-jar-9.0.2.v20130417.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/jetty-client-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/jetty-http-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/jetty-io-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/jetty-util-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/jetty-util-ajax-{VERSION}.jar
+ |   |   |   +> org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern=.*/servlet-api-[^/]*\.jar$
+ |   |   |   +> com.sun.jsp.taglibraryCache={}
+ |   |   |   +> com.sun.jsp.tagFileJarUrlsCache={}
+ |   |   += o.e.j.s.h.MovedContextHandler@5e0c8d24{/oldContextPath,null,AVAILABLE} - STARTED
+ |   |   |   += org.eclipse.jetty.server.handler.MovedContextHandler$Redirector@2a4200d3 - STARTED
+ |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   |
+ |   |   |   +> No ClassLoader
+ |   |   |   +> org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern=.*/servlet-api-[^/]*\.jar$
+ |   |   += o.e.j.w.WebAppContext@6f01ba6f{/,file:/home/user/jetty-distribution-{VERSION}/webapps/ROOT/,AVAILABLE}{/ROOT} - STARTED
+ |   |   |   += org.eclipse.jetty.server.session.SessionHandler@5a770658 - STARTED
+ |   |   |   |   += org.eclipse.jetty.server.session.HashSessionManager@746a95ae - STARTED
+ |   |   |   |   += org.eclipse.jetty.security.ConstraintSecurityHandler@1890e38 - STARTED
+ |   |   |   |   |   +- org.eclipse.jetty.security.DefaultAuthenticatorFactory@6242c657
+ |   |   |   |   |   += org.eclipse.jetty.servlet.ServletHandler@debac27 - STARTED
+ |   |   |   |   |   |   += default@5c13d641==org.eclipse.jetty.servlet.DefaultServlet,0,true - STARTED
+ |   |   |   |   |   |   |   +- maxCacheSize=256000000
+ |   |   |   |   |   |   |   +- etags=true
+ |   |   |   |   |   |   |   +- dirAllowed=true
+ |   |   |   |   |   |   |   +- gzip=true
+ |   |   |   |   |   |   |   +- maxCachedFileSize=200000000
+ |   |   |   |   |   |   |   +- redirectWelcome=false
+ |   |   |   |   |   |   |   +- acceptRanges=true
+ |   |   |   |   |   |   |   +- welcomeServlets=false
+ |   |   |   |   |   |   |   +- aliases=false
+ |   |   |   |   |   |   |   +- useFileMappedBuffer=true
+ |   |   |   |   |   |   |   +- maxCachedFiles=2048
+ |   |   |   |   |   |   +- [/]=>default
+ |   |   |   |   |   |   += jsp@19c47==org.apache.jasper.servlet.JspServlet,0,true - STARTED
+ |   |   |   |   |   |   |   +- logVerbosityLevel=DEBUG
+ |   |   |   |   |   |   |   +- fork=false
+ |   |   |   |   |   |   |   +- com.sun.appserv.jsp.classpath=/home/user/jetty-distribution-{VERSION}/lib/jetty-xml-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/servlet-api-3.0.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-http-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-continuation-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-server-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-security-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-servlet-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-webapp-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-deploy-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-client-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-jmx-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/com.sun.el-2.2.0.v201303151357.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.el-2.2.0.v201303151357.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp-2.2.0.v201112011158.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.jasper.glassfish-2.2.2.v201112011158.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar:/home/user/jetty-distribution-{VERSION}/resources:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-api-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-common-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-server-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-servlet-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-util-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-io-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/start.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/opt/local/lib/libsvnjavahl-1.0.dylib:/System/Library/Java/Extensions/AppleScriptEngine.jar:/System/Library/Java/Extensions/dns_sd.jar:/System/Library/Java/Extensions/j3daudio.jar:/System/Library/Java/Extensions/j3dcore.jar:/System/Library/Java/Extensions/j3dutils.jar:/System/Library/Java/Extensions/jai_codec.jar:/System/Library/Java/Extensions/jai_core.jar:/System/Library/Java/Extensions/libAppleScriptEngine.jnilib:/System/Library/Java/Extensions/libJ3D.jnilib:/System/Library/Java/Extensions/libJ3DAudio.jnilib:/System/Library/Java/Extensions/libJ3DUtils.jnilib:/System/Library/Java/Extensions/libmlib_jai.jnilib:/System/Library/Java/Extensions/libQTJNative.jnilib:/System/Library/Java/Extensions/mlibwrapper_jai.jar:/System/Library/Java/Extensions/MRJToolkit.jar:/System/Library/Java/Extensions/QTJava.zip:/System/Library/Java/Extensions/vecmath.jar:/usr/lib/java/libjdns_sd.jnilib
+ |   |   |   |   |   |   |   +- scratchdir=/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-ROOT-_-any-/jsp
+ |   |   |   |   |   |   |   +- xpoweredBy=false
+ |   |   |   |   |   |   +- [*.jsp, *.jspf, *.jspx, *.xsp, *.JSP, *.JSPF, *.JSPX, *.XSP]=>jsp
+ |   |   |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   |   |   +~ HashLoginService[Test Realm] - STARTED
+ |   |   |   |   |   +- org.eclipse.jetty.security.DefaultIdentityService@d2539a6
+ |   |   |   |   |   +- org.eclipse.jetty.security.authentication.BasicAuthenticator@6b733b94
+ |   |   |   |   |   |
+ |   |   |   |   |   +> HashLoginService[Test Realm] - STARTED
+ |   |   |   |   |   +> org.eclipse.jetty.security.DefaultIdentityService@d2539a6
+ |   |   |   |   |   +> org.eclipse.jetty.security.authentication.BasicAuthenticator@6b733b94
+ |   |   |   |   |   +> []
+ |   |   |   |   |   +> /={TRACE={RoleInfo,F,C[]}}
+ |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   += org.eclipse.jetty.servlet.ErrorPageErrorHandler@3c41a9ce - STARTED
+ |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   |
+ |   |   |   +> WebAppClassLoader=ROOT@7af33249
+ |   |   |   |   +- startJarLoader@7194b34a
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-xml-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/servlet-api-3.0.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-http-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-continuation-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-server-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-security-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-servlet-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-webapp-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-deploy-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-client-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-jmx-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/com.sun.el-2.2.0.v201303151357.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.el-2.2.0.v201303151357.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp-2.2.0.v201112011158.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.jasper.glassfish-2.2.2.v201112011158.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/resources/
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-api-9.0.2.v20130417.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-common-9.0.2.v20130417.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-server-9.0.2.v20130417.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-servlet-9.0.2.v20130417.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-util-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-io-{VERSION}.jar
+ |   |   |   |       +- sun.misc.Launcher$AppClassLoader@19d1b44b
+ |   |   |   |           +- file:/home/user/jetty-distribution-{VERSION}/start.jar
+ |   |   |   |           +- sun.misc.Launcher$ExtClassLoader@1693b52b
+ |   |   |   +> javax.servlet.context.tempdir=/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-ROOT-_-any-
+ |   |   |   +> org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern=.*/servlet-api-[^/]*\.jar$
+ |   |   |   +> com.sun.jsp.taglibraryCache={}
+ |   |   |   +> com.sun.jsp.tagFileJarUrlsCache={}
+ |   |   += o.e.j.s.h.ContextHandler@7b2dffdf{/javadoc,file:/home/user/jetty-distribution-{VERSION}/javadoc,AVAILABLE} - STARTED
+ |   |   |   += org.eclipse.jetty.server.handler.ResourceHandler@8f9c8a7 - STARTED
+ |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   |
+ |   |   |   +> No ClassLoader
+ |   |   |   +> org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern=.*/servlet-api-[^/]*\.jar$
+ |   |   += o.e.j.w.WebAppContext@716d9094{/test,file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/,AVAILABLE}{/test.war} - STARTED
+ |   |   |   += org.eclipse.jetty.server.session.SessionHandler@336abd81 - STARTED
+ |   |   |   |   += org.eclipse.jetty.server.session.HashSessionManager@1246f8d0 - STARTED
+ |   |   |   |   += org.eclipse.jetty.security.ConstraintSecurityHandler@7179290f - STARTED
+ |   |   |   |   |   +- org.eclipse.jetty.security.DefaultAuthenticatorFactory@17d41d12
+ |   |   |   |   |   += org.eclipse.jetty.servlet.ServletHandler@5034037e - STARTED
+ |   |   |   |   |   |   += default@5c13d641==org.eclipse.jetty.servlet.DefaultServlet,0,true - STARTED
+ |   |   |   |   |   |   |   +- maxCacheSize=256000000
+ |   |   |   |   |   |   |   +- etags=true
+ |   |   |   |   |   |   |   +- dirAllowed=true
+ |   |   |   |   |   |   |   +- gzip=true
+ |   |   |   |   |   |   |   +- maxCachedFileSize=200000000
+ |   |   |   |   |   |   |   +- redirectWelcome=false
+ |   |   |   |   |   |   |   +- acceptRanges=true
+ |   |   |   |   |   |   |   +- welcomeServlets=false
+ |   |   |   |   |   |   |   +- aliases=false
+ |   |   |   |   |   |   |   +- useFileMappedBuffer=true
+ |   |   |   |   |   |   |   +- maxCachedFiles=2048
+ |   |   |   |   |   |   +- [/]=>default
+ |   |   |   |   |   |   += jsp@19c47==org.apache.jasper.servlet.JspServlet,0,true - STARTED
+ |   |   |   |   |   |   |   +- logVerbosityLevel=DEBUG
+ |   |   |   |   |   |   |   +- fork=false
+ |   |   |   |   |   |   |   +- com.sun.appserv.jsp.classpath=/home/user/jetty-distribution-{VERSION}/lib/jetty-xml-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/servlet-api-3.0.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-http-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-continuation-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-server-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-security-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-servlet-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-webapp-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-deploy-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-client-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-jmx-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/com.sun.el-2.2.0.v201303151357.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.el-2.2.0.v201303151357.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp-2.2.0.v201112011158.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.jasper.glassfish-2.2.2.v201112011158.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar:/home/user/jetty-distribution-{VERSION}/resources:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-api-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-common-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-server-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-servlet-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-util-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-io-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/start.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/opt/local/lib/libsvnjavahl-1.0.dylib:/System/Library/Java/Extensions/AppleScriptEngine.jar:/System/Library/Java/Extensions/dns_sd.jar:/System/Library/Java/Extensions/j3daudio.jar:/System/Library/Java/Extensions/j3dcore.jar:/System/Library/Java/Extensions/j3dutils.jar:/System/Library/Java/Extensions/jai_codec.jar:/System/Library/Java/Extensions/jai_core.jar:/System/Library/Java/Extensions/libAppleScriptEngine.jnilib:/System/Library/Java/Extensions/libJ3D.jnilib:/System/Library/Java/Extensions/libJ3DAudio.jnilib:/System/Library/Java/Extensions/libJ3DUtils.jnilib:/System/Library/Java/Extensions/libmlib_jai.jnilib:/System/Library/Java/Extensions/libQTJNative.jnilib:/System/Library/Java/Extensions/mlibwrapper_jai.jar:/System/Library/Java/Extensions/MRJToolkit.jar:/System/Library/Java/Extensions/QTJava.zip:/System/Library/Java/Extensions/vecmath.jar:/usr/lib/java/libjdns_sd.jnilib
+ |   |   |   |   |   |   |   +- scratchdir=/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/jsp
+ |   |   |   |   |   |   |   +- xpoweredBy=false
+ |   |   |   |   |   |   +- [*.jsp, *.jspf, *.jspx, *.xsp, *.JSP, *.JSPF, *.JSPX, *.XSP]=>jsp
+ |   |   |   |   |   |   += QoSFilter - STARTED
+ |   |   |   |   |   |   |   +- managedAttr=true
+ |   |   |   |   |   |   |   +- maxRequests=10000
+ |   |   |   |   |   |   +- [/*]/[]==0=>QoSFilter
+ |   |   |   |   |   |   += MultiPart - STARTED
+ |   |   |   |   |   |   |   +- deleteFiles=true
+ |   |   |   |   |   |   +- [/dump/*]/[]==0=>MultiPart
+ |   |   |   |   |   |   += GzipFilter - STARTED
+ |   |   |   |   |   |   |   +- bufferSize=8192
+ |   |   |   |   |   |   |   +- excludedAgents=MSIE 6.0
+ |   |   |   |   |   |   |   +- userAgent=(?:Mozilla[^\(]*\(compatible;\s*+([^;]*);.*)|(?:.*?([^\s]+/[^\s]+).*)
+ |   |   |   |   |   |   |   +- mimeTypes=text/plain,application/xml
+ |   |   |   |   |   |   |   +- uncheckedPrintWriter=true
+ |   |   |   |   |   |   |   +- cacheSize=1024
+ |   |   |   |   |   |   |   +- minGzipSize=2048
+ |   |   |   |   |   |   +- [/dump/gzip/*, *.txt]/[]==0=>GzipFilter
+ |   |   |   |   |   |   += Login@462ff49==com.acme.LoginServlet,1,true - STARTED
+ |   |   |   |   |   |   +- [/login/*]=>Login
+ |   |   |   |   |   |   += Hello@42628b2==com.acme.HelloWorld,1,true - STARTED
+ |   |   |   |   |   |   +- [/hello/*]=>Hello
+ |   |   |   |   |   |   += Dump@20ae14==com.acme.Dump,1,true - STARTED
+ |   |   |   |   |   |   |   +- servlet-override-example=a servlet value
+ |   |   |   |   |   |   +- [/dump/*, *.dump]=>Dump
+ |   |   |   |   |   |   += Session@d9891a76==com.acme.SessionDump,5,true - STARTED
+ |   |   |   |   |   |   +- [/session/*]=>Session
+ |   |   |   |   |   |   += Cookie@78a4f684==com.acme.CookieDump,1,true - STARTED
+ |   |   |   |   |   |   +- [/cookie/*]=>Cookie
+ |   |   |   |   |   |   += Dispatch@14d3a89a==com.acme.DispatchServlet,1,true - STARTED
+ |   |   |   |   |   |   +- [/dispatch/*]=>Dispatch
+ |   |   |   |   |   |   += CGI@10465==org.eclipse.jetty.servlets.CGI,1,true - STARTED
+ |   |   |   |   |   |   +- [/cgi-bin/*]=>CGI
+ |   |   |   |   |   |   += Chat@200778==com.acme.ChatServlet,1,true - STARTED
+ |   |   |   |   |   |   +- [/chat/*]=>Chat
+ |   |   |   |   |   |   += WSChat@99274454==com.acme.WebSocketChatServlet,1,true - STARTED
+ |   |   |   |   |   |   +- [/ws/*]=>WSChat
+ |   |   |   |   |   |   += Rewrite@a4dac96c==com.acme.RewriteServlet,-1,false - STARTED
+ |   |   |   |   |   |   +- [/rewritten/*, /redirected/*]=>Rewrite
+ |   |   |   |   |   |   += SecureMode@d45951da==com.acme.SecureModeServlet,1,true - STARTED
+ |   |   |   |   |   |   +- [/secureMode/*]=>SecureMode
+ |   |   |   |   |   |   += foo.jsp@d7583f1f==org.apache.jasper.servlet.JspServlet,-1,false - STARTED
+ |   |   |   |   |   |   +- [/jsp/foo/]=>foo.jsp
+ |   |   |   |   |   |   +- [*.more]=>Dump
+ |   |   |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   |   |   |   += RegoTest@dafcd1ad==com.acme.RegTest,-1,false - STARTED
+ |   |   |   |   |   |   +- [/rego/*]=>RegoTest
+ |   |   |   |   |   |   += RegoTest2@849d6425==com.acme.RegTest,-1,false - STARTED
+ |   |   |   |   |   |   +- [/rego2/*]=>RegoTest2
+ |   |   |   |   |   |   += TestFilter - STARTED
+ |   |   |   |   |   |   |   +- remote=false
+ |   |   |   |   |   |   +- [/*]/[]==31=>TestFilter
+ |   |   |   |   |   += HashLoginService[Test Realm] - STARTED
+ |   |   |   |   |   +- org.eclipse.jetty.security.authentication.FormAuthenticator@1fa291f2
+ |   |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   |   |   +- org.eclipse.jetty.security.DefaultIdentityService@41917d6d
+ |   |   |   |   |   |
+ |   |   |   |   |   +> HashLoginService[Test Realm] - STARTED
+ |   |   |   |   |   +> org.eclipse.jetty.security.DefaultIdentityService@41917d6d
+ |   |   |   |   |   +> org.eclipse.jetty.security.authentication.FormAuthenticator@1fa291f2
+ |   |   |   |   |   +> [server-administrator, *, admin, user]
+ |   |   |   |   |   +> /rego2/*={*={RoleInfo,C[server-administrator]}}
+ |   |   |   |   |   +> *.htm={*={RoleInfo,C[server-administrator, *, admin, user]}}
+ |   |   |   |   |   +> /dump/auth/ssl/*={*={RoleInfo[]}}
+ |   |   |   |   |   +> /dump/auth/noaccess/*={*={RoleInfo,F,C[]}}
+ |   |   |   |   |   +> /auth/*={*={RoleInfo,F,C[]}}
+ |   |   |   |   |   +> /dump/auth/admin/*={*={RoleInfo,C[admin]}}
+ |   |   |   |   |   +> /dump/auth/relax/*={GET={RoleInfo[]}, HEAD={RoleInfo[]}}
+ |   |   |   |   |   +> /rego/*={*={RoleInfo,C[admin]}}
+ |   |   |   |   |   +> /dump/auth/*={*={RoleInfo,C[server-administrator, *, admin, user]}}
+ |   |   |   |   |   +> /={TRACE={RoleInfo,F,C[]}}
+ |   |   |   |   |   +> /auth/relax.txt={GET={RoleInfo[]}, HEAD={RoleInfo[]}}
+ |   |   |   |   |   +> /auth2/*={*={RoleInfo,C[server-administrator, *, admin, user]}}
+ |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   += org.eclipse.jetty.servlet.ErrorPageErrorHandler@24bf7a86 - STARTED
+ |   |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |   |   +- org.eclipse.jetty.servlets.QoSFilter@6df3d1f5
+ |   |   |   |
+ |   |   |   +> WebAppClassLoader=Test WebApp@3e2f3adb
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/classes/
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/jetty-continuation-{VERSION}.jar
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/jetty-http-{VERSION}.jar
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/jetty-io-{VERSION}.jar
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/jetty-servlets-{VERSION}.jar
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/jetty-util-{VERSION}.jar
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/websocket-api-9.0.2.v20130417.jar
+ |   |   |   |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/websocket-servlet-9.0.2.v20130417.jar
+ |   |   |   |   +- startJarLoader@7194b34a
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-xml-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/servlet-api-3.0.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-http-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-continuation-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-server-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-security-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-servlet-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-webapp-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-deploy-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-client-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-jmx-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/com.sun.el-2.2.0.v201303151357.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.el-2.2.0.v201303151357.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp-2.2.0.v201112011158.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.jasper.glassfish-2.2.2.v201112011158.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/resources/
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-api-9.0.2.v20130417.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-common-9.0.2.v20130417.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-server-9.0.2.v20130417.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-servlet-9.0.2.v20130417.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-util-{VERSION}.jar
+ |   |   |   |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-io-{VERSION}.jar
+ |   |   |   |       +- sun.misc.Launcher$AppClassLoader@19d1b44b
+ |   |   |   |           +- file:/home/user/jetty-distribution-{VERSION}/start.jar
+ |   |   |   |           +- sun.misc.Launcher$ExtClassLoader@1693b52b
+ |   |   |   +> org.eclipse.jetty.server.context.ManagedAttributes=QoSFilter,TransparentProxy.ThreadPool,TransparentProxy.HttpClient
+ |   |   |   +> context-override-example=a context value
+ |   |   |   +> javax.servlet.context.tempdir=/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-
+ |   |   |   +> org.apache.catalina.jsp_classpath=/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/classes:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/jetty-continuation-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/jetty-http-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/jetty-io-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/jetty-servlets-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/jetty-util-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/websocket-api-9.0.2.v20130417.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/WEB-INF/lib/websocket-servlet-9.0.2.v20130417.jar
+ |   |   |   +> org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern=.*/servlet-api-[^/]*\.jar$
+ |   |   |   +> QoSFilter=org.eclipse.jetty.servlets.QoSFilter@6df3d1f5
+ |   |   |   +> com.sun.jsp.taglibraryCache={}
+ |   |   |   +> com.sun.jsp.tagFileJarUrlsCache={}
+ |   |   += o.e.j.w.WebAppContext@4ac92718{/proxy,file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/,AVAILABLE}{/xref-proxy.war} - STARTED
+ |   |       += org.eclipse.jetty.server.session.SessionHandler@5c25bf03 - STARTED
+ |   |       |   += org.eclipse.jetty.server.session.HashSessionManager@33053093 - STARTED
+ |   |       |   += org.eclipse.jetty.security.ConstraintSecurityHandler@3bab0b5a - STARTED
+ |   |       |   |   +- org.eclipse.jetty.security.DefaultAuthenticatorFactory@11ad5296
+ |   |       |   |   += org.eclipse.jetty.servlet.ServletHandler@a08feeb - STARTED
+ |   |       |   |   |   += default@5c13d641==org.eclipse.jetty.servlet.DefaultServlet,0,true - STARTED
+ |   |       |   |   |   |   +- maxCacheSize=256000000
+ |   |       |   |   |   |   +- etags=true
+ |   |       |   |   |   |   +- dirAllowed=true
+ |   |       |   |   |   |   +- gzip=true
+ |   |       |   |   |   |   +- maxCachedFileSize=200000000
+ |   |       |   |   |   |   +- redirectWelcome=false
+ |   |       |   |   |   |   +- acceptRanges=true
+ |   |       |   |   |   |   +- welcomeServlets=false
+ |   |       |   |   |   |   +- aliases=false
+ |   |       |   |   |   |   +- useFileMappedBuffer=true
+ |   |       |   |   |   |   +- maxCachedFiles=2048
+ |   |       |   |   |   +- [/]=>default
+ |   |       |   |   |   += jsp@19c47==org.apache.jasper.servlet.JspServlet,0,true - STARTED
+ |   |       |   |   |   |   +- logVerbosityLevel=DEBUG
+ |   |       |   |   |   |   +- fork=false
+ |   |       |   |   |   |   +- com.sun.appserv.jsp.classpath=/home/user/jetty-distribution-{VERSION}/lib/jetty-xml-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/servlet-api-3.0.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-http-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-continuation-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-server-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-security-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-servlet-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-webapp-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-deploy-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-client-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-jmx-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/com.sun.el-2.2.0.v201303151357.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.el-2.2.0.v201303151357.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp-2.2.0.v201112011158.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.jasper.glassfish-2.2.2.v201112011158.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar:/home/user/jetty-distribution-{VERSION}/resources:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-api-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-common-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-server-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-servlet-9.0.2.v20130417.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-util-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/lib/jetty-io-{VERSION}.jar:/home/user/jetty-distribution-{VERSION}/start.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/opt/local/lib/libsvnjavahl-1.0.dylib:/System/Library/Java/Extensions/AppleScriptEngine.jar:/System/Library/Java/Extensions/dns_sd.jar:/System/Library/Java/Extensions/j3daudio.jar:/System/Library/Java/Extensions/j3dcore.jar:/System/Library/Java/Extensions/j3dutils.jar:/System/Library/Java/Extensions/jai_codec.jar:/System/Library/Java/Extensions/jai_core.jar:/System/Library/Java/Extensions/libAppleScriptEngine.jnilib:/System/Library/Java/Extensions/libJ3D.jnilib:/System/Library/Java/Extensions/libJ3DAudio.jnilib:/System/Library/Java/Extensions/libJ3DUtils.jnilib:/System/Library/Java/Extensions/libmlib_jai.jnilib:/System/Library/Java/Extensions/libQTJNative.jnilib:/System/Library/Java/Extensions/mlibwrapper_jai.jar:/System/Library/Java/Extensions/MRJToolkit.jar:/System/Library/Java/Extensions/QTJava.zip:/System/Library/Java/Extensions/vecmath.jar:/usr/lib/java/libjdns_sd.jnilib
+ |   |       |   |   |   |   +- scratchdir=/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/jsp
+ |   |       |   |   |   |   +- xpoweredBy=false
+ |   |       |   |   |   +- [*.jsp, *.jspf, *.jspx, *.xsp, *.JSP, *.JSPF, *.JSPX, *.XSP]=>jsp
+ |   |       |   |   |   += XrefTransparentProxy@b0222797==org.eclipse.jetty.proxy.ProxyServlet$Transparent,1,true - STARTED
+ |   |       |   |   |   |   +- proxyTo=http://download.eclipse.org/jetty/stable-9
+ |   |       |   |   |   |   +- hostHeader=download.eclipse.org
+ |   |       |   |   |   +- [/xref/*]=>XrefTransparentProxy
+ |   |       |   |   |   += JavadocTransparentProxy@8ab9c012==org.eclipse.jetty.proxy.ProxyServlet$Transparent,1,true - STARTED
+ |   |       |   |   |   |   +- proxyTo=http://download.eclipse.org/jetty/stable-9
+ |   |       |   |   |   |   +- hostHeader=download.eclipse.org
+ |   |       |   |   |   +- [/apidocs/*]=>JavadocTransparentProxy
+ |   |       |   |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |       |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |       |   |   +~ HashLoginService[Test Realm] - STARTED
+ |   |       |   |   +- org.eclipse.jetty.security.DefaultIdentityService@d2539a6
+ |   |       |   |   +- org.eclipse.jetty.security.authentication.BasicAuthenticator@5497fb72
+ |   |       |   |   |
+ |   |       |   |   +> HashLoginService[Test Realm] - STARTED
+ |   |       |   |   +> org.eclipse.jetty.security.DefaultIdentityService@d2539a6
+ |   |       |   |   +> org.eclipse.jetty.security.authentication.BasicAuthenticator@5497fb72
+ |   |       |   |   +> []
+ |   |       |   |   +> /={TRACE={RoleInfo,F,C[]}}
+ |   |       |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |       += org.eclipse.jetty.servlet.ErrorPageErrorHandler@321f8d38 - STARTED
+ |   |       |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |       +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   |       |
+ |   |       +> WebAppClassLoader=Transparent Proxy WebApp@3570713d
+ |   |       |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/WEB-INF/classes/
+ |   |       |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/WEB-INF/lib/jetty-client-{VERSION}.jar
+ |   |       |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/WEB-INF/lib/jetty-http-{VERSION}.jar
+ |   |       |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/WEB-INF/lib/jetty-io-{VERSION}.jar
+ |   |       |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/WEB-INF/lib/jetty-proxy-{VERSION}.jar
+ |   |       |   +- file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/WEB-INF/lib/jetty-util-{VERSION}.jar
+ |   |       |   +- startJarLoader@7194b34a
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-xml-{VERSION}.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/servlet-api-3.0.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-http-{VERSION}.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-continuation-{VERSION}.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-server-{VERSION}.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-security-{VERSION}.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-servlet-{VERSION}.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-webapp-{VERSION}.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-deploy-{VERSION}.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-client-{VERSION}.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-jmx-{VERSION}.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/com.sun.el-2.2.0.v201303151357.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.el-2.2.0.v201303151357.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp-2.2.0.v201112011158.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.jasper.glassfish-2.2.2.v201112011158.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/resources/
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-api-9.0.2.v20130417.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-common-9.0.2.v20130417.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-server-9.0.2.v20130417.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-servlet-9.0.2.v20130417.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-util-{VERSION}.jar
+ |   |       |       +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-io-{VERSION}.jar
+ |   |       |       +- sun.misc.Launcher$AppClassLoader@19d1b44b
+ |   |       |           +- file:/home/user/jetty-distribution-{VERSION}/start.jar
+ |   |       |           +- sun.misc.Launcher$ExtClassLoader@1693b52b
+ |   |       +> javax.servlet.context.tempdir=/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-
+ |   |       +> org.apache.catalina.jsp_classpath=/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/WEB-INF/classes:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/WEB-INF/lib/jetty-client-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/WEB-INF/lib/jetty-http-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/WEB-INF/lib/jetty-io-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/WEB-INF/lib/jetty-proxy-{VERSION}.jar:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/WEB-INF/lib/jetty-util-{VERSION}.jar
+ |   |       +> org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern=.*/servlet-api-[^/]*\.jar$
+ |   |       +> JavadocTransparentProxy.HttpClient=org.eclipse.jetty.client.HttpClient@580f016d
+ |   |       +> XrefTransparentProxy.HttpClient=org.eclipse.jetty.client.HttpClient@70c7e52b
+ |   |       +> com.sun.jsp.taglibraryCache={}
+ |   |       +> com.sun.jsp.tagFileJarUrlsCache={}
+ |   += org.eclipse.jetty.server.handler.DefaultHandler@4de4926a - STARTED
+ |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   += org.eclipse.jetty.server.handler.RequestLogHandler@3dc087a2 - STARTED
+ |   |   += org.eclipse.jetty.server.AsyncNCSARequestLog@108a1cf6 - STARTED
+ |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ +- org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   +- [/rego/*]=>RegoTest=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=RegoTest,id=0
+ |   +- org.eclipse.jetty.security.DefaultAuthenticatorFactory@17d41d12=org.eclipse.jetty.security:context=test,type=defaultauthenticatorfactory,id=0
+ |   +- org.eclipse.jetty.server.session.HashSessionManager@1246f8d0=org.eclipse.jetty.server.session:context=test,type=hashsessionmanager,id=0
+ |   +- org.eclipse.jetty.security.ConstraintSecurityHandler@1890e38=org.eclipse.jetty.security:context=ROOT,type=constraintsecurityhandler,id=0
+ |   +- WSChat@99274454==com.acme.WebSocketChatServlet,1,true=org.eclipse.jetty.servlet:context=test,type=servletholder,name=WSChat,id=0
+ |   +- org.eclipse.jetty.deploy.DeploymentManager@c8e4be2=org.eclipse.jetty.deploy:type=deploymentmanager,id=0
+ |   +- org.eclipse.jetty.jmx.MBeanContainer@644a5ddd=org.eclipse.jetty.jmx:type=mbeancontainer,id=0
+ |   +- [/dump/gzip/*, *.txt]/[]==0=>GzipFilter=org.eclipse.jetty.servlet:context=test,type=filtermapping,name=GzipFilter,id=0
+ |   +- Hello@42628b2==com.acme.HelloWorld,1,true=org.eclipse.jetty.servlet:context=test,type=servletholder,name=Hello,id=0
+ |   +- [/]=>default=org.eclipse.jetty.servlet:context=xref-proxy,type=servletmapping,name=default,id=0
+ |   +- [/login/*]=>Login=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=Login,id=0
+ |   +- org.eclipse.jetty.server.handler.DefaultHandler@4de4926a=org.eclipse.jetty.server.handler:type=defaulthandler,id=0
+ |   +- org.eclipse.jetty.server.session.SessionHandler@5c25bf03=org.eclipse.jetty.server.session:context=xref-proxy,type=sessionhandler,id=0
+ |   +- [/ws/*]=>WSChat=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=WSChat,id=0
+ |   +- o.e.j.w.WebAppContext@6f01ba6f{/,file:/home/user/jetty-distribution-{VERSION}/webapps/ROOT/,AVAILABLE}{/ROOT}=org.eclipse.jetty.webapp:context=ROOT,type=webappcontext,id=0
+ |   +- o.e.j.w.WebAppContext@7ea88b1c{/async-rest,[file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/, jar:file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/example-async-rest-jar-9.0.2.v20130417.jar!/META-INF/resources/],AVAILABLE}{/async-rest.war}=org.eclipse.jetty.webapp:context=async-rest,type=webappcontext,id=0
+ |   +- ServerConnector@3d0f282{HTTP/1.1}{0.0.0.0:9090}=org.eclipse.jetty.server:context=HTTP/1.1@3d0f282,type=serverconnector,id=0
+ |   +- org.eclipse.jetty.security.DefaultAuthenticatorFactory@6242c657=org.eclipse.jetty.security:context=ROOT,type=defaultauthenticatorfactory,id=0
+ |   +- JavadocTransparentProxy@8ab9c012==org.eclipse.jetty.proxy.ProxyServlet$Transparent,1,true=org.eclipse.jetty.servlet:context=xref-proxy,type=servletholder,name=JavadocTransparentProxy,id=0
+ |   +- [/dump/*, *.dump]=>Dump=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=Dump,id=0
+ |   +- [/jsp/foo/]=>foo.jsp=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=foo.jsp,id=0
+ |   +- org.eclipse.jetty.servlet.ServletHandler@46bac287=org.eclipse.jetty.servlet:context=async-rest,type=servlethandler,id=0
+ |   +- GzipFilter=org.eclipse.jetty.servlet:context=test,type=filterholder,name=GzipFilter,id=0
+ |   +- o.e.j.w.WebAppContext@4ac92718{/proxy,file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/,AVAILABLE}{/xref-proxy.war}=org.eclipse.jetty.webapp:context=xref-proxy,type=webappcontext,id=0
+ |   +- qtp1062680061{STARTED,10<=13<=200,i=1,q=0}=org.eclipse.jetty.util.thread:type=queuedthreadpool,id=0
+ |   +- org.eclipse.jetty.server.session.HashSessionManager@33053093=org.eclipse.jetty.server.session:context=xref-proxy,type=hashsessionmanager,id=0
+ |   +- org.eclipse.jetty.security.DefaultAuthenticatorFactory@52b12fef=org.eclipse.jetty.security:context=async-rest,type=defaultauthenticatorfactory,id=0
+ |   +- Login@462ff49==com.acme.LoginServlet,1,true=org.eclipse.jetty.servlet:context=test,type=servletholder,name=Login,id=0
+ |   +- org.eclipse.jetty.security.authentication.BasicAuthenticator@7b239469=org.eclipse.jetty.security.authentication:context=async-rest,type=basicauthenticator,id=0
+ |   +- MultiPart=org.eclipse.jetty.servlet:context=test,type=filterholder,name=MultiPart,id=0
+ |   +- default@5c13d641==org.eclipse.jetty.servlet.DefaultServlet,0,true=org.eclipse.jetty.servlet:context=xref-proxy,type=servletholder,name=default,id=0
+ |   +- default@5c13d641==org.eclipse.jetty.servlet.DefaultServlet,0,true=org.eclipse.jetty.servlet:context=ROOT,type=servletholder,name=default,id=0
+ |   +- default@5c13d641==org.eclipse.jetty.servlet.DefaultServlet,0,true=org.eclipse.jetty.servlet:context=async-rest,type=servletholder,name=default,id=0
+ |   +- default@5c13d641==org.eclipse.jetty.servlet.DefaultServlet,0,true=org.eclipse.jetty.servlet:context=test,type=servletholder,name=default,id=0
+ |   +- org.eclipse.jetty.server.session.HashSessionManager@746a95ae=org.eclipse.jetty.server.session:context=ROOT,type=hashsessionmanager,id=0
+ |   +- RegoTest2@849d6425==com.acme.RegTest,-1,false=org.eclipse.jetty.servlet:context=test,type=servletholder,name=RegoTest2,id=0
+ |   +- org.eclipse.jetty.server.ServerConnector$ServerConnectorManager@6f0ac4be=org.eclipse.jetty.server:context=HTTP/1.1@3d0f282,type=serverconnector$serverconnectormanager,id=0
+ |   +- [/]=>default=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=default,id=0
+ |   +- SecureMode@d45951da==com.acme.SecureModeServlet,1,true=org.eclipse.jetty.servlet:context=test,type=servletholder,name=SecureMode,id=0
+ |   +- org.eclipse.jetty.security.authentication.BasicAuthenticator@6b733b94=org.eclipse.jetty.security.authentication:context=ROOT,type=basicauthenticator,id=0
+ |   +- org.eclipse.jetty.server.session.SessionHandler@6dfb8d2e=org.eclipse.jetty.server.session:context=async-rest,type=sessionhandler,id=0
+ |   +- org.eclipse.jetty.security.DefaultIdentityService@41917d6d=org.eclipse.jetty.security:context=test,type=defaultidentityservice,id=0
+ |   +- jsp@19c47==org.apache.jasper.servlet.JspServlet,0,true=org.eclipse.jetty.servlet:context=xref-proxy,type=servletholder,name=jsp,id=0
+ |   +- jsp@19c47==org.apache.jasper.servlet.JspServlet,0,true=org.eclipse.jetty.servlet:context=ROOT,type=servletholder,name=jsp,id=0
+ |   +- jsp@19c47==org.apache.jasper.servlet.JspServlet,0,true=org.eclipse.jetty.servlet:context=async-rest,type=servletholder,name=jsp,id=0
+ |   +- jsp@19c47==org.apache.jasper.servlet.JspServlet,0,true=org.eclipse.jetty.servlet:context=test,type=servletholder,name=jsp,id=0
+ |   +- [/*]/[]==31=>TestFilter=org.eclipse.jetty.servlet:context=test,type=filtermapping,name=TestFilter,id=0
+ |   +- org.eclipse.jetty.server.session.HashSessionManager@6cb83869=org.eclipse.jetty.server.session:context=async-rest,type=hashsessionmanager,id=0
+ |   +- org.eclipse.jetty.io.ArrayByteBufferPool@30ad8942=org.eclipse.jetty.io:context=HTTP/1.1@3d0f282,type=arraybytebufferpool,id=0
+ |   +- [/cgi-bin/*]=>CGI=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=CGI,id=0
+ |   +- org.eclipse.jetty.server.handler.HandlerCollection@58b37561=org.eclipse.jetty.server.handler:type=handlercollection,id=0
+ |   +- Session@d9891a76==com.acme.SessionDump,5,true=org.eclipse.jetty.servlet:context=test,type=servletholder,name=Session,id=0
+ |   +- org.eclipse.jetty.servlet.ServletHandler@a08feeb=org.eclipse.jetty.servlet:context=xref-proxy,type=servlethandler,id=0
+ |   +- org.eclipse.jetty.util.thread.ScheduledExecutorScheduler@725f5=org.eclipse.jetty.util.thread:type=scheduledexecutorscheduler,id=0
+ |   +- [/*]/[]==0=>QoSFilter=org.eclipse.jetty.servlet:context=test,type=filtermapping,name=QoSFilter,id=0
+ |   +- org.eclipse.jetty.server.session.SessionHandler@5a770658=org.eclipse.jetty.server.session:context=ROOT,type=sessionhandler,id=0
+ |   +- org.eclipse.jetty.server.session.SessionHandler@336abd81=org.eclipse.jetty.server.session:context=test,type=sessionhandler,id=0
+ |   +- o.e.j.s.h.ContextHandler@7b2dffdf{/javadoc,file:/home/user/jetty-distribution-{VERSION}/javadoc,AVAILABLE}=org.eclipse.jetty.server.handler:context=javadoc,type=contexthandler,id=0
+ |   +- org.eclipse.jetty.servlets.QoSFilter@6df3d1f5=org.eclipse.jetty.servlets:context=test,type=qosfilter,id=0
+ |   +- [*.more]=>Dump=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=Dump,id=1
+ |   +- Dump@20ae14==com.acme.Dump,1,true=org.eclipse.jetty.servlet:context=test,type=servletholder,name=Dump,id=0
+ |   +- HttpConnectionFactory@5e47b1b9{HTTP/1.1}=org.eclipse.jetty.server:context=HTTP/1.1@3d0f282,type=httpconnectionfactory,id=0
+ |   +- org.eclipse.jetty.servlet.ServletHandler@debac27=org.eclipse.jetty.servlet:context=ROOT,type=servlethandler,id=0
+ |   +- [*.jsp, *.jspf, *.jspx, *.xsp, *.JSP, *.JSPF, *.JSPX, *.XSP]=>jsp=org.eclipse.jetty.servlet:context=xref-proxy,type=servletmapping,name=jsp,id=0
+ |   +- org.eclipse.jetty.server.handler.MovedContextHandler$Redirector@2a4200d3=org.eclipse.jetty.server.handler:context=oldContextPath,type=movedcontexthandler$redirector,id=0
+ |   +- TestFilter=org.eclipse.jetty.servlet:context=test,type=filterholder,name=TestFilter,id=0
+ |   +- Rewrite@a4dac96c==com.acme.RewriteServlet,-1,false=org.eclipse.jetty.servlet:context=test,type=servletholder,name=Rewrite,id=0
+ |   +- [/dispatch/*]=>Dispatch=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=Dispatch,id=0
+ |   +- [/testSerial]=>SerialRestServlet=org.eclipse.jetty.servlet:context=async-rest,type=servletmapping,name=SerialRestServlet,id=0
+ |   +- org.eclipse.jetty.servlet.ErrorPageErrorHandler@24bf7a86=org.eclipse.jetty.servlet:context=test,type=errorpageerrorhandler,id=0
+ |   +- [/secureMode/*]=>SecureMode=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=SecureMode,id=0
+ |   +- [/]=>default=org.eclipse.jetty.servlet:context=async-rest,type=servletmapping,name=default,id=0
+ |   +- Dispatch@14d3a89a==com.acme.DispatchServlet,1,true=org.eclipse.jetty.servlet:context=test,type=servletholder,name=Dispatch,id=0
+ |   +- org.eclipse.jetty.server.handler.ContextHandlerCollection@64c6e290=org.eclipse.jetty.server.handler:type=contexthandlercollection,id=0
+ |   +- org.eclipse.jetty.security.ConstraintSecurityHandler@2848c90e=org.eclipse.jetty.security:context=async-rest,type=constraintsecurityhandler,id=0
+ |   +- [/rego2/*]=>RegoTest2=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=RegoTest2,id=0
+ |   +- [/rewritten/*, /redirected/*]=>Rewrite=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=Rewrite,id=0
+ |   +- org.eclipse.jetty.servlet.ServletHandler@5034037e=org.eclipse.jetty.servlet:context=test,type=servlethandler,id=0
+ |   +- org.eclipse.jetty.servlet.ErrorPageErrorHandler@3c121009=org.eclipse.jetty.servlet:context=async-rest,type=errorpageerrorhandler,id=0
+ |   +- sun.nio.ch.ServerSocketChannelImpl[/0:0:0:0:0:0:0:0:9090]=sun.nio.ch:context=HTTP/1.1@3d0f282,type=serversocketchannelimpl,id=0
+ |   +- org.eclipse.jetty.security.ConstraintSecurityHandler@7179290f=org.eclipse.jetty.security:context=test,type=constraintsecurityhandler,id=0
+ |   +- org.eclipse.jetty.server.session.HashSessionIdManager@289eb857=org.eclipse.jetty.server.session:type=hashsessionidmanager,id=0
+ |   +- org.eclipse.jetty.security.authentication.BasicAuthenticator@5497fb72=org.eclipse.jetty.security.authentication:context=xref-proxy,type=basicauthenticator,id=0
+ |   +- org.eclipse.jetty.security.DefaultAuthenticatorFactory@11ad5296=org.eclipse.jetty.security:context=xref-proxy,type=defaultauthenticatorfactory,id=0
+ |   +- [/dump/*]/[]==0=>MultiPart=org.eclipse.jetty.servlet:context=test,type=filtermapping,name=MultiPart,id=0
+ |   +- o.e.j.s.h.MovedContextHandler@5e0c8d24{/oldContextPath,null,AVAILABLE}=org.eclipse.jetty.server.handler:context=oldContextPath,type=movedcontexthandler,id=0
+ |   +- QoSFilter=org.eclipse.jetty.servlet:context=test,type=filterholder,name=QoSFilter,id=0
+ |   +- org.eclipse.jetty.security.authentication.FormAuthenticator@1fa291f2=org.eclipse.jetty.security.authentication:context=test,type=formauthenticator,id=0
+ |   +- o.e.j.w.WebAppContext@716d9094{/test,file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/,AVAILABLE}{/test.war}=org.eclipse.jetty.webapp:context=test,type=webappcontext,id=0
+ |   +- [/]=>default=org.eclipse.jetty.servlet:context=ROOT,type=servletmapping,name=default,id=0
+ |   +- [/hello/*]=>Hello=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=Hello,id=0
+ |   +- [/chat/*]=>Chat=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=Chat,id=0
+ |   +- [/testAsync]=>AsyncRestServlet=org.eclipse.jetty.servlet:context=async-rest,type=servletmapping,name=AsyncRestServlet,id=0
+ |   +- org.eclipse.jetty.security.DefaultIdentityService@d2539a6=org.eclipse.jetty.security:context=async-rest,type=defaultidentityservice,id=0
+ |   +- org.eclipse.jetty.server.handler.RequestLogHandler@3dc087a2=org.eclipse.jetty.server.handler:type=requestloghandler,id=0
+ |   +- org.eclipse.jetty.servlet.ErrorPageErrorHandler@321f8d38=org.eclipse.jetty.servlet:context=xref-proxy,type=errorpageerrorhandler,id=0
+ |   +- org.eclipse.jetty.server.handler.ResourceHandler@8f9c8a7=org.eclipse.jetty.server.handler:context=javadoc,type=resourcehandler,id=0
+ |   +- CGI@10465==org.eclipse.jetty.servlets.CGI,1,true=org.eclipse.jetty.servlet:context=test,type=servletholder,name=CGI,id=0
+ |   +- SerialRestServlet@461411d==org.eclipse.jetty.example.asyncrest.SerialRestServlet,-1,false=org.eclipse.jetty.servlet:context=async-rest,type=servletholder,name=SerialRestServlet,id=0
+ |   +- HashLoginService[Test Realm]=org.eclipse.jetty.security:type=hashloginservice,id=0
+ |   +- AsyncRestServlet@73eb9bd5==org.eclipse.jetty.example.asyncrest.AsyncRestServlet,-1,false=org.eclipse.jetty.servlet:context=async-rest,type=servletholder,name=AsyncRestServlet,id=0
+ |   +- org.eclipse.jetty.server.Server@76f08fe1=org.eclipse.jetty.server:type=server,id=0
+ |   +- org.eclipse.jetty.servlet.ErrorPageErrorHandler@3c41a9ce=org.eclipse.jetty.servlet:context=ROOT,type=errorpageerrorhandler,id=0
+ |   +- [/apidocs/*]=>JavadocTransparentProxy=org.eclipse.jetty.servlet:context=xref-proxy,type=servletmapping,name=JavadocTransparentProxy,id=0
+ |   +- Chat@200778==com.acme.ChatServlet,1,true=org.eclipse.jetty.servlet:context=test,type=servletholder,name=Chat,id=0
+ |   +- [/cookie/*]=>Cookie=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=Cookie,id=0
+ |   +- [/session/*]=>Session=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=Session,id=0
+ |   +- org.eclipse.jetty.deploy.providers.WebAppProvider@7b26b7df=org.eclipse.jetty.deploy.providers:type=webappprovider,id=0
+ |   +- org.eclipse.jetty.server.AsyncNCSARequestLog@108a1cf6=org.eclipse.jetty.server:type=asyncncsarequestlog,id=0
+ |   +- [*.jsp, *.jspf, *.jspx, *.xsp, *.JSP, *.JSPF, *.JSPX, *.XSP]=>jsp=org.eclipse.jetty.servlet:context=test,type=servletmapping,name=jsp,id=0
+ |   +- [*.jsp, *.jspf, *.jspx, *.xsp, *.JSP, *.JSPF, *.JSPX, *.XSP]=>jsp=org.eclipse.jetty.servlet:context=ROOT,type=servletmapping,name=jsp,id=0
+ |   +- HashLoginService[Test Realm]=org.eclipse.jetty.security:context=test,type=hashloginservice,id=0
+ |   +- [*.jsp, *.jspf, *.jspx, *.xsp, *.JSP, *.JSPF, *.JSPX, *.XSP]=>jsp=org.eclipse.jetty.servlet:context=async-rest,type=servletmapping,name=jsp,id=0
+ |   +- foo.jsp@d7583f1f==org.apache.jasper.servlet.JspServlet,-1,false=org.eclipse.jetty.servlet:context=test,type=servletholder,name=foo.jsp,id=0
+ |   +- RegoTest@dafcd1ad==com.acme.RegTest,-1,false=org.eclipse.jetty.servlet:context=test,type=servletholder,name=RegoTest,id=0
+ |   +- [/xref/*]=>XrefTransparentProxy=org.eclipse.jetty.servlet:context=xref-proxy,type=servletmapping,name=XrefTransparentProxy,id=0
+ |   +- org.eclipse.jetty.security.ConstraintSecurityHandler@3bab0b5a=org.eclipse.jetty.security:context=xref-proxy,type=constraintsecurityhandler,id=0
+ |   +- HttpConfiguration@703b16bb{32768,8192/8192,https://:8443,[]}=org.eclipse.jetty.server:context=HTTP/1.1@3d0f282,type=httpconfiguration,id=0
+ |   +- org.eclipse.jetty.util.log.Log@dda4f7b=org.eclipse.jetty.util.log:type=log,id=0
+ |   +- Cookie@78a4f684==com.acme.CookieDump,1,true=org.eclipse.jetty.servlet:context=test,type=servletholder,name=Cookie,id=0
+ |   +- XrefTransparentProxy@b0222797==org.eclipse.jetty.proxy.ProxyServlet$Transparent,1,true=org.eclipse.jetty.servlet:context=xref-proxy,type=servletholder,name=XrefTransparentProxy,id=0
+ +- org.eclipse.jetty.util.log.Log@dda4f7b
+ += ServerConnector@3d0f282{HTTP/1.1}{0.0.0.0:9090} - STARTED
+ |   +~ org.eclipse.jetty.server.Server@76f08fe1 - STARTING
+ |   +~ qtp1062680061{STARTED,10<=13<=200,i=1,q=0} - STARTED
+ |   +~ org.eclipse.jetty.util.thread.ScheduledExecutorScheduler@725f5 - STARTED
+ |   +- org.eclipse.jetty.io.ArrayByteBufferPool@30ad8942
+ |   += HttpConnectionFactory@5e47b1b9{HTTP/1.1} - STARTED
+ |   |   +- HttpConfiguration@703b16bb{32768,8192/8192,https://:8443,[]}
+ |   |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   += org.eclipse.jetty.server.ServerConnector$ServerConnectorManager@6f0ac4be - STARTED
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector@61454787 keys=0 selected=0 id=0
+ |   |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   |   +- sun.nio.ch.KQueueSelectorImpl@a0c508b keys=0
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector@2e7bdad4 keys=0 selected=0 id=1
+ |   |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   |   +- sun.nio.ch.KQueueSelectorImpl@5825168 keys=0
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector@2eae85ab keys=0 selected=0 id=2
+ |   |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   |   +- sun.nio.ch.KQueueSelectorImpl@6faa85f6 keys=0
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector@244112c0 keys=0 selected=0 id=3
+ |   |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   |   +- sun.nio.ch.KQueueSelectorImpl@10c6f695 keys=0
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector@7666b8cd keys=0 selected=0 id=4
+ |   |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   |   +- sun.nio.ch.KQueueSelectorImpl@17836c59 keys=0
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector@353e531e keys=0 selected=0 id=5
+ |   |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   |   +- sun.nio.ch.KQueueSelectorImpl@2095f259 keys=0
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector@5459c1c5 keys=0 selected=0 id=6
+ |   |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |   |   +- sun.nio.ch.KQueueSelectorImpl@142c7195 keys=0
+ |   |   +- org.eclipse.jetty.io.SelectorManager$ManagedSelector@71d4f78b keys=0 selected=0 id=7
+ |   |       +- org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:459)
+ |   |       +- sun.nio.ch.KQueueSelectorImpl@16bdab45 keys=0
+ |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ |   +- sun.nio.ch.ServerSocketChannelImpl[/0:0:0:0:0:0:0:0:9090]
+ += org.eclipse.jetty.deploy.DeploymentManager@c8e4be2 - STARTED
+ |   +~ org.eclipse.jetty.deploy.providers.WebAppProvider@7b26b7df - STARTED
+ |   +~ org.eclipse.jetty.jmx.MBeanContainer@644a5ddd
+ +~ HashLoginService[Test Realm] - STARTED
+ += org.eclipse.jetty.server.session.HashSessionIdManager@289eb857 - STARTED
+ |
+ +> startJarLoader@7194b34a
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-xml-{VERSION}.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/servlet-api-3.0.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-http-{VERSION}.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-continuation-{VERSION}.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-server-{VERSION}.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-security-{VERSION}.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-servlet-{VERSION}.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-webapp-{VERSION}.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-deploy-{VERSION}.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-client-{VERSION}.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-jmx-{VERSION}.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/com.sun.el-2.2.0.v201303151357.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.el-2.2.0.v201303151357.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/javax.servlet.jsp-2.2.0.v201112011158.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.jasper.glassfish-2.2.2.v201112011158.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/resources/
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-api-9.0.2.v20130417.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-common-9.0.2.v20130417.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-server-9.0.2.v20130417.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/websocket/websocket-servlet-9.0.2.v20130417.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-util-{VERSION}.jar
+     +- file:/home/user/jetty-distribution-{VERSION}/lib/jetty-io-{VERSION}.jar
+     +- sun.misc.Launcher$AppClassLoader@19d1b44b
+         +- file:/home/user/jetty-distribution-{VERSION}/start.jar
+         +- sun.misc.Launcher$ExtClassLoader@1693b52b
+2013-04-29 14:38:39.422:INFO:oejs.Server:Thread-2: Graceful shutdown org.eclipse.jetty.server.Server@76f08fe1 by  Mon Apr 29 14:38:44 CDT 2013
+2013-04-29 14:38:39.429:INFO:oejs.ServerConnector:Thread-2: Stopped ServerConnector@3d0f282{HTTP/1.1}{0.0.0.0:9090}
+2013-04-29 14:38:39.444:INFO:oejsl.ELContextCleaner:Thread-2: javax.el.BeanELResolver purged
+2013-04-29 14:38:39.444:INFO:oejsh.ContextHandler:Thread-2: stopped o.e.j.w.WebAppContext@4ac92718{/proxy,file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-xref-proxy.war-_xref-proxy-any-/webapp/,UNAVAILABLE}{/xref-proxy.war}
+2013-04-29 14:38:39.447:INFO:oejsl.ELContextCleaner:Thread-2: javax.el.BeanELResolver purged
+2013-04-29 14:38:39.447:INFO:oejsh.ContextHandler:Thread-2: stopped o.e.j.w.WebAppContext@716d9094{/test,file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-test.war-_test-any-/webapp/,UNAVAILABLE}{/test.war}
+2013-04-29 14:38:39.455:INFO:oejsh.ContextHandler:Thread-2: stopped o.e.j.s.h.ContextHandler@7b2dffdf{/javadoc,file:/home/user/jetty-distribution-{VERSION}/javadoc,UNAVAILABLE}
+2013-04-29 14:38:39.456:INFO:oejsl.ELContextCleaner:Thread-2: javax.el.BeanELResolver purged
+2013-04-29 14:38:39.456:INFO:oejsh.ContextHandler:Thread-2: stopped o.e.j.w.WebAppContext@6f01ba6f{/,file:/home/user/jetty-distribution-{VERSION}/webapps/ROOT/,UNAVAILABLE}{/ROOT}
+2013-04-29 14:38:39.456:INFO:oejsh.ContextHandler:Thread-2: stopped o.e.j.s.h.MovedContextHandler@5e0c8d24{/oldContextPath,null,UNAVAILABLE}
+2013-04-29 14:38:39.457:INFO:oejsl.ELContextCleaner:Thread-2: javax.el.BeanELResolver purged
+2013-04-29 14:38:39.457:INFO:oejsh.ContextHandler:Thread-2: stopped o.e.j.w.WebAppContext@7ea88b1c{/async-rest,[file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/, jar:file:/private/var/folders/br/kbs2g3753c54wmv4j31pnw5r0000gn/T/jetty-0.0.0.0-9090-async-rest.war-_async-rest-any-/webapp/WEB-INF/lib/example-async-rest-jar-9.0.2.v20130417.jar!/META-INF/resources/],UNAVAILABLE}{/async-rest.war}
+.... 
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/example-apache-log4j.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/example-apache-log4j.adoc
new file mode 100644
index 0000000..f309ad9
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/example-apache-log4j.adoc
@@ -0,0 +1,67 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[example-logging-log4j]]
+=== Example: Logging with Apache Log4j
+
+It is possible to have the Jetty Server logging configured so that Log4j controls the output of logging events produced by Jetty. 
+This is accomplished by configuring Jetty for logging to http://logging.apache.org/log4j/[Apache Log4j] via http://slf4j.org/manual.html[Slf4j] and the http://slf4j.org/manual.html#swapping[Slf4j binding layer for Log4j].
+
+A convenient replacement `logging` module has been created to bootstrap your `${jetty.base}` directory for logging with log4j.
+
+[source, screen, subs="{sub-order}"]
+....
+[mybase]$ mkdir modules
+[mybase]$ cd modules
+
+[modules]$ curl -O https://raw.githubusercontent.com/jetty-project/logging-modules/master/log4j-1.2/logging.mod
+  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
+                                 Dload  Upload   Total   Spent    Left  Speed
+100   720  100   720    0     0   2188      0 --:--:-- --:--:-- --:--:--  2188
+[modules]$ cd ..
+
+[mybase]$ java -jar /opt/jetty-dist/start.jar --add-to-start=logging
+INFO: logging         initialised in ${jetty.base}/start.ini (appended)
+MKDIR: ${jetty.base}/logs
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.6.6/slf4j-api-1.6.6.jar to lib/logging/slf4j-api-1.6.6.jar
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-log4j12/1.6.6/slf4j-log4j12-1.6.6.jar to lib/logging/slf4j-log4j12-1.6.6.jar
+DOWNLOAD: http://central.maven.org/maven2/log4j/log4j/1.2.17/log4j-1.2.17.jar to lib/logging/log4j-1.2.17.jar
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/log4j-1.2/log4j.properties to resources/log4j.properties
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/log4j-1.2/jetty-logging.properties to resources/jetty-logging.properties
+INFO: resources       initialised transitively
+INFO: resources       enabled in     ${jetty.base}/start.ini
+
+[mybase]$ java -jar /opt/jetty-dist/start.jar
+....
+
+The replacement `logging.mod` performs a number of tasks.
+
+.  `mybase` is a `${jetty.base}` directory.
+.  The jetty-distribution is unpacked (and untouched) into `/opt/jetty-dist/` and becomes the `${jetty.home}` directory for this demonstration.
+.  The `curl` command downloads the replacement `logging.mod` and puts it into the `${jetty.base}/modules/` directory for use by mybase only.
+.  The `start.jar --add-to-start=logging` command performs a number of steps to make the logging module available to the `${jetty.base}` configuration.
+..  The `--module=logging` command is added to the `${jetty.base}/start.ini` configuration.
+..  Required `${jetty.base}` directories are created: `${jetty.base}/logs` and `${jetty.base}/resources`
+..  Required libraries are downloaded (if not present already): slf4j-api, slf4j-log4j, and log4j itself.
+* The libraries are put in the `${jetty.base}/lib/logging/` directory.
+..  Required configuration files are downloaded (if not present already): `jetty-logging.properties`, and `log4j.properties`
+* The configuration files are placed in the `${jetty.base}/resources/` directory.
+
+At this point the Jetty `mybase` is configured so that the Jetty server itself will log using log4j, using the log4j configuration found in `mybase/resources/log4j.properties`.
+
+The server classpath can be verified by using the `start.jar --list-config` command.
+
+In essence, Jetty is now configured to emit its own logging events to slf4j, and slf4j itself is using the static log binder found in `slf4j-log4j12.jar`, making all Jetty + Slf4j + Log4j events emitted by the Jetty server go to Log4j for routing (to console, file, syslog, etc...).
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/example-java-util-logging-native.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/example-java-util-logging-native.adoc
new file mode 100644
index 0000000..445a154
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/example-java-util-logging-native.adoc
@@ -0,0 +1,90 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[example-logging-java-util-logging-native]]
+=== Example: Logging with Java's java.util.logging via JavaUtilLog
+
+It is possible to have the Jetty Server logging configured so that
+`java.util.logging` controls the output of logging events produced by
+Jetty.
+
+This example demonstrates how to configuring Jetty for logging to
+`java.util.logging` via Jetty's own JavaUtilLog implementation.
+
+____
+[IMPORTANT]
+While this is a valid setup, the Jetty project recommends always using the link:#example-logging-java-util-logging[slf4j to java.util.logging configuration] for memory and performance reasons. 
+This native implementation is very non-performant and is not guaranteed to exist in the future.
+____
+
+A convenient replacement `logging` module has been created to bootstrap your `${jetty.base}` directory for logging with `java.util.logging`.
+
+[source, screen, subs="{sub-order}"]
+....
+[mybase]$ mkdir modules
+[mybase]$ cd modules
+
+[modules]$ curl -O https://raw.githubusercontent.com/jetty-project/logging-modules/master/java.util.logging-native/logging.mod
+  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
+                                 Dload  Upload   Total   Spent    Left  Speed
+100   623  100   623    0     0   1879      0 --:--:-- --:--:-- --:--:--  1876
+[modules]$ cd ..
+
+[mybase]$ java -jar /opt/jetty-dist/start.jar --add-to-start=logging
+INFO: logging         initialised in ${jetty.base}/start.ini (appended)
+MKDIR: ${jetty.base}/logs
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/java.util.logging-native/jetty-logging.xml to etc/jetty-logging.xml
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/java.util.logging-native/logging.properties to resources/logging.properties
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/java.util.logging-native/jetty-logging.properties to resources/jetty-logging.properties
+INFO: resources       initialised transitively
+INFO: resources       enabled in     ${jetty.base}/${jetty.base}
+
+[mybase]$ java -jar /opt/jetty-dist/start.jar
+....
+
+The replacement `logging.mod` performs a number of tasks.
+
+.  `mybase` is a `${jetty.base}` directory.
+.  The jetty-distribution is unpacked (and untouched) into `/opt/jetty-dist/` and becomes the `${jetty.home}` directory for this demonstration.
+.  The `curl` command downloads the replacement `logging.mod` and puts it into the `${jetty.base}/modules/` directory for use by `mybase` only.
+.  The `start.jar --add-to-start=logging` command performs a number of steps to make the logging module available to the `${jetty.base}`
+configuration.
+..  The `--module=logging` command is added to the `${jetty.base}/start.ini` configuration.
+..  Required `${jetty.base}` directories are created: `${jetty.base}/logs` and `${jetty.base}/resources`.
+..  Required configuration files are downloaded (if not present already): `jetty-logging.properties`, and `logging.properties`
+* The configuration files are put in the `${jetty.base}/resources/` directory.
+..  Required `java.util.logging` initialization commands are downloaded (if not present already): `jetty-logging.xml`.
+* The xml file is put in the `${jetty.base}/etc/` directory.
+
+At this point the Jetty `mybase` is configured so that the Jetty server itself will log using `java.util.logging`, using the `java.util.logging` configuration found in `mybase/resources/logging.properties`.
+
+The server classpath can be verified by using the `start.jar --list-config` command.
+
+In essence, Jetty is now configured to use `org.eclipse.jetty.util.log.JavaUtilLog`, which emit its own logging events to `java.util.logging`, making all Jetty and `java.util.logging` events emitted by the Jetty server go to `java.util.logging` for routing (to console, file, etc...).
+
+If there any custom `java.util.logging` handlers to be used, put the implementation jar in the `${jetty.base}/lib/logging/` directory and reference them in the `${jetty.base}/resources/logging.properties` file.
+
+____
+[NOTE]
+`java.util.logging` is configured via the `${jetty.base}/resources/logging.properties` file during a valid startup of Jetty.
+
+This means that if there is any startup errors that occur before `java.util.logging` is configured, they will likely be lost and/or not routed through your configuration.
+
+Other logging frameworks are more reliable in that they always initialize and configure on first use, unlike `java.util.logging`.
+
+* While it is possible to configure `java.util.logging` sooner, even at JVM startup, the example demonstrated here does not show this technique.
+For more information consult the official `java.util.logging.LogManager` javadoc http://docs.oracle.com/javase/7/docs/api/java/util/logging/LogManager.html[documentation from Oracle].
+____
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/example-java-util-logging.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/example-java-util-logging.adoc
new file mode 100644
index 0000000..5f205af
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/example-java-util-logging.adoc
@@ -0,0 +1,85 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[example-logging-java-util-logging]]
+=== Example: Logging with Java's java.util.logging via Slf4j
+
+It is possible to have the Jetty Server logging configured so that `java.util.logging` controls the output of logging events produced by Jetty.
+
+This example demonstrates how to configuring Jetty for logging to `java.util.logging` via http://slf4j.org/manual.html[Slf4j] and the http://slf4j.org/manual.html#swapping[Slf4j binding layer for java.util.logging].
+If you want to use the built-in native `JavaUtilLog` implementation, see #example-logging-java-util-logging-native .
+
+A convenient replacement `logging` module has been created to bootstrap your `${jetty.base}` directory for logging with `java.util.logging`.
+
+[source, screen, subs="{sub-order}"]
+....
+[mybase]$ mkdir modules
+[mybase]$ cd modules
+
+[modules]$ curl -O https://raw.githubusercontent.com/jetty-project/logging-modules/master/java.util.logging-slf4j/logging.mod
+  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
+                                 Dload  Upload   Total   Spent    Left  Speed
+100   826  100   826    0     0   2468      0 --:--:-- --:--:-- --:--:--  2473
+[modules]$ cd ..
+
+[mybase]$ java -jar /opt/jetty-dist/start.jar --add-to-start=logging
+INFO: logging         initialised in ${jetty.base}/start.ini (appended)
+MKDIR: ${jetty.base}/logs
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-jdk14/1.6.6/slf4j-jdk14-1.6.6.jar to lib/logging/slf4j-jdk14-1.6.6.jar
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.6.6/slf4j-api-1.6.6.jar to lib/logging/slf4j-api-1.6.6.jar
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/java.util.logging-slf4j/jetty-logging.xml to etc/jetty-logging.xml
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/java.util.logging-slf4j/logging.properties to resources/logging.properties
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/java.util.logging-slf4j/jetty-logging.properties to resources/jetty-logging.properties
+INFO: resources       initialised transitively
+INFO: resources       enabled in     ${jetty.base}/start.ini
+
+[mybase]$ java -jar /opt/jetty-dist/start.jar
+....
+
+The replacement `logging.mod` performs a number of tasks.
+
+.  `mybase` is a `${jetty.base}` directory
+.  The jetty-distribution is unpacked (and untouched) into `/opt/jetty-dist/` and becomes the `${jetty.home}` directory for this demonstration.
+.  The `curl` command downloads the replacement `logging.mod` and puts it into the `${jetty.base}/modules/` directory for use by `mybase` only.
+.  The `start.jar --add-to-start=logging` command performs a number of steps to make the logging module available to the `${jetty.base}` configuration.
+..  The `--module=logging` command is added to the `${jetty.base}/start.ini` configuration.
+..  Required `${jetty.base}` directories are created: `${jetty.base}/logs` and `${jetty.base}/resources`.
+..  Required libraries are downloaded (if not present already): slf4j-api, and slf4j-jdk14.
+* The libraries are put in the `${jetty.base}/lib/logging/` directory.
+..  Required configuration files are downloaded (if not present already): `jetty-logging.properties`, and `logging.properties`.
+* The configuration files are put in the `${jetty.base}/resources/` directory.
+..  Required `java.util.logging` initialization commands are downloaded (if not present already): `jetty-logging.xml`.
+* The xml file is put in the `${jetty.base}/etc/` directory.
+
+At this point the Jetty `mybase` is configured so that the Jetty server itself will log using `java.util.logging`, using the `java.util.logging` configuration found in `mybase/resources/logging.properties`.
+
+The server classpath can be verified by using the `start.jar --list-config` command.
+
+In essence, Jetty is now configured to emit its own logging events to slf4j, and slf4j itself is using the static log binder found in `slf4j-jdk14.jar`, making all Jetty + Slf4j + `java.util.logging` events emitted by the Jetty server go to `java.util.logging` for routing (to console, file, etc...).
+
+If there any custom `java.util.logging` handlers to be used, put the implementation jar in the `${jetty.base}/lib/logging/` directory and reference them in the `${jetty.base}/resources/logging.properties` file.
+
+____
+[NOTE]
+`java.util.logging` is configured via the `${jetty.base}/resources/logging.properties` file during a valid startup of Jetty.
+
+This means that if there is any startup errors that occur before `java.util.logging` is configured, they will likely be lost and/or not routed through your configuration.
+
+Other logging frameworks are more reliable in that they always initialize and configure on first use, unlike `java.util.logging`.
+
+* While it is possible to configure `java.util.logging` sooner, even at JVM startup, the example demonstrated here does not show this technique.
+For more information consult the official `java.util.logging.LogManager` javadoc http://docs.oracle.com/javase/7/docs/api/java/util/logging/LogManager.html[documentation from Oracle].
+____
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/example-logback-centralized-logging.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/example-logback-centralized-logging.adoc
new file mode 100644
index 0000000..069f345
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/example-logback-centralized-logging.adoc
@@ -0,0 +1,109 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[example-logging-logback-centralized]]
+=== Example: Centralized Logging with Logback
+
+The term _Centralized Logging_ refers to a forced logging configuration for the Jetty Server and all web applications that are deployed on the server.
+It routes all logging events from the web applications to a single configuration on the Server side.
+
+The example below shows how to accomplish this with Jetty and Slf4j, using `Logback` to manage the final writing of logs to disk.
+
+____
+[IMPORTANT]
+This mechanism forces all webapps to use the server's configuration for logging, something that isn't 100% appropriate for all webapps.
+An example would be having Jenkins-CI deployed as an webapp, if you force its logging configuration to the server side, you lose the ability on http://jenkins-ci.org/[Jenkins-CI] to see the logs from the various builds (as now those logs are actually going to the main server log).
+____
+
+This configuration is essentially the multiple logger configuration with added configuration to the deployers to force a `WebAppClassLoader` change to use the server classpath over the webapps classpath for the logger specific classes.
+
+The technique used by this configuration is to provide an link:{JDURL}org/eclipse/jetty/deploy/AppLifeCycle.Binding.html[AppLifeCycle.Binding] against the link:{JDURL}/org/eclipse/jetty/deploy/AppLifeCycle.html[`"deploying"`node] that modifies the link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#addSystemClass(java.lang.String)[WebAppContext.addSystemClass(String)] for the common logging classes.
+See https://github.com/jetty-project/jetty-webapp-logging/blob/master/src/main/java/org/eclipse/jetty/webapp/logging/CentralizedWebAppLoggingBinding.java[org.eclipse.jetty.logging.CentralizedWebAppLoggingBinding] for actual implementation.
+
+A convenient replacement `logging` module has been created to bootstrap your `${jetty.base}` directory for capturing all Jetty server logging from multiple logging frameworks into a single logging output file managed by Logback.
+
+[source, screen, subs="{sub-order}"]
+....
+[mybase]$ mkdir modules
+[mybase]$ cd modules
+
+[modules]$ curl -O https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/logging.mod
+  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
+                                 Dload  Upload   Total   Spent    Left  Speed
+100  1416  100  1416    0     0   4241      0 --:--:-- --:--:-- --:--:--  4252
+
+[master]$ curl -O https://raw.githubusercontent.com/jetty-project/logging-modules/master/centralized/webapp-logging.mod
+  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
+                                 Dload  Upload   Total   Spent    Left  Speed
+100   660  100   660    0     0   2032      0 --:--:-- --:--:-- --:--:--  2037
+[modules]$ cd ..
+
+[mybase]$ java -jar /opt/jetty-dist/start.jar --add-to-start=logging,webapp-logging
+INFO: logging         initialised in ${jetty.base}/start.ini (appended)
+MKDIR: ${jetty.base}/logs
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.6.6/slf4j-api-1.6.6.jar to lib/logging/slf4j-api-1.6.6.jar
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/log4j-over-slf4j/1.6.6/log4j-over-slf4j-1.6.6.jar to lib/logging/log4j-over-slf4j-1.6.6.jar
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/jul-to-slf4j/1.6.6/jul-to-slf4j-1.6.6.jar to lib/logging/jul-to-slf4j-1.6.6.jar
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/jcl-over-slf4j/1.6.6/jcl-over-slf4j-1.6.6.jar to lib/logging/jcl-over-slf4j-1.6.6.jar
+DOWNLOAD: http://central.maven.org/maven2/ch/qos/logback/logback-core/1.0.7/logback-core-1.0.7.jar to lib/logging/logback-core-1.0.7.jar
+DOWNLOAD: http://central.maven.org/maven2/ch/qos/logback/logback-classic/1.0.7/logback-classic-1.0.7.jar to lib/logging/logback-classic-1.0.7.jar
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/logback.xml to resources/logback.xml
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/jetty-logging.properties to resources/jetty-logging.properties
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/jetty-logging.xml to etc/jetty-logging.xml
+INFO: resources       initialised transitively
+INFO: resources       enabled in     ${jetty.base}/start.ini
+INFO: webapp-logging  initialised in ${jetty.base}/start.ini (appended)
+DOWNLOAD: http://central.maven.org/maven2/org/eclipse/jetty/jetty-webapp-logging/9.0.0/jetty-webapp-logging-9.0.0.jar to lib/webapp-logging/jetty-webapp-logging-9.0.0.jar
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/jetty-webapp-logging/master/src/main/config/etc/jetty-webapp-logging.xml to etc/jetty-webapp-logging.xml
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/jetty-webapp-logging/master/src/main/config/etc/jetty-mdc-handler.xml to etc/jetty-mdc-handler.xml
+INFO: deploy          initialised transitively
+INFO: deploy          enabled in     ${jetty.base}/start.ini
+INFO: logging         initialised transitively
+INFO: resources       initialised transitively
+INFO: resources       enabled in     ${jetty.base}/start.ini
+
+[mybase]$ java -jar /opt/jetty-dist/start.jar
+....
+
+The replacement `logging.mod` performs a number of tasks.
+
+.  `mybase` is a `${jetty.base}` directory.
+.  The jetty-distribution is unpacked (and untouched) into `/opt/jetty-dist/` and becomes the `${jetty.home}` directory for this demonstration.
+.  The `curl` command downloads the replacement `logging.mod` and puts it into the `${jetty.base}/modules/` directory for use by mybase only.
+.  The `start.jar --add-to-start=logging,webapp-logging` command performs a number of steps to make the logging module available to the `${jetty.base}` configuration.
+..  Several entries are added to the `${jetty.base}/start.ini` configuration.
+*  `--module=logging` is added to enable the logging module.
+*  `--module=webapp-logging` is added to enable the webapp-logging module.
+..  Required `${jetty.base}` directories are created: `${jetty.base}/logs` and `${jetty.base}/resources`.
+..  Required logging libraries are downloaded (if not present already) to the `${jetty.base}/lib/logging/` directory:
+* `slf4j-api.jar` - API jar for Slf4j (used by most of the rest of the jars)
+* `log4j-over-slf4j.jar` - Slf4j jar that captures all log4j emitted logging events
+* `jul-to-slf4j.jar` - Slf4j jar that captures all java.util.logging events
+* `jcl-over-slf4j.jar` - Slf4j jar that captures all commons-logging events
+* `logback-classic.jar` - the Slf4j adapter jar that routes all of the captured logging events to logback itself.
+* `logback-core.jar` - the logback implementation jar, that handles all of the filtering and output of the logging events.
+..  Required webapp-logging library is downloaded (if not present already) to the `${jetty.base}/lib/webapp-logging/` directory:
+* `jetty-webapp-logging.jar` - the Jetty side deployment manger app-lifecycle bindings for modifying the `WebAppClassloaders` of deployed webapps.
+..  Required configuration files are downloaded (if not present already) to the `${jetty.base}/resources/` directory: `jetty-logging.properties`, and `logback.xml`.
+..  Required initialization commands are downloaded (if not present already) to the `${jetty.base}/etc/` directory: `jetty-logging.xml`, `jetty-webapp-logging.xml`, and `jetty-mdc-handler.xml`.
+
+At this point the Jetty `mybase` is configured so that the jetty server itself will log using slf4j, and all other logging events from other Jetty Server components (such as database drivers, security layers, jsp, mail, and other 3rd party server components) are routed to logback for filtering and output.
+
+All webapps deployed via the `DeploymentManager` have their `WebAppClassLoader` modified to use server side classes and configuration for all logging implementations.
+
+The server classpath can be verified by using the `start.jar --list-config` command.
+
+In essence, Jetty is now configured to emit its own logging events to slf4j, and various slf4j bridge jars are acting on behalf of log4j, `java.util.logging`, and `commons-logging`, routing all of the logging events to logback (a slf4j adapter) for routing (to console, file, etc...).
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/example-logback-sifting.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/example-logback-sifting.adoc
new file mode 100644
index 0000000..17459f3
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/example-logback-sifting.adoc
@@ -0,0 +1,39 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[example-logging-logback-sifting]]
+=== Example: MDC Based Sifting of Logs with Logback
+
+This page describes how to create log files at the server level and name them based on an arbitrary context. 
+This can be done with SLF4J + Logback + Jetty Webapp Logging in the mix. 
+Find example projects for this feature at github:
+
+....
+https://github.com/jetty-project/jetty-and-logback-example
+....
+
+.GitHub Example Project
+[cols=",",options="header",]
+|=======================================================================
+|Modules |Description
+|/jetty-distro-with-logback-basic/ |Configures the jetty distribution with logback enabled at the server level with an example logback configuration.
+
+|/jetty-distro-with-logback-sifting/ |Configures the jetty distribution with logback, centralized webapp logging, an MDC handler, and a sample logback configuration that performs sifting based on the incoming Host header on the requests.
+
+|/jetty-slf4j-mdc-handler/ |Provides the SLF4J MDC key/value pairs that Jetty needs to perform the sample sifting.
+
+|/jetty-slf4j-test-webapp/ |A sample webapp+servlet that accepts arbitrary values on a form POST and logs them via SLF4J, so that you can see the results of this example.
+|=======================================================================
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/example-logback.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/example-logback.adoc
new file mode 100644
index 0000000..a939037
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/example-logback.adoc
@@ -0,0 +1,63 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[example-logging-logback]]
+=== Example: Logging with Logback
+
+It is possible to have the Jetty Server logging configured so that Logback controls the output of logging events produced by Jetty.
+This is accomplished by configuring Jetty for logging to `Logback`, which uses http://slf4j.org/manual.html[Slf4j] and the http://logback.qos.ch/[Logback Implementation for Slf4j].
+
+A convenient replacement `logging` module has been created to bootstrap the `${jetty.base}` directory for logging with logback.
+
+[source, screen, subs="{sub-order}"]
+....
+[mybase]$ mkdir modules
+[mybase]$ cd modules
+
+[modules]$ curl -O https://raw.githubusercontent.com/jetty-project/logging-modules/master/logback/logging.mod
+  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
+                                 Dload  Upload   Total   Spent    Left  Speed
+100   742  100   742    0     0   2196      0 --:--:-- --:--:-- --:--:--  2201
+[modules]$ cd ..
+
+[mybase]$ java -jar /opt/jetty-dist/start.jar --add-to-start=logging
+INFO: logging         initialised in ${jetty.base}/start.ini (appended)
+MKDIR: ${jetty.base}/logs
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.6.6/slf4j-api-1.6.6.jar to lib/logging/slf4j-api-1.6.6.jar
+DOWNLOAD: http://central.maven.org/maven2/ch/qos/logback/logback-core/1.0.7/logback-core-1.0.7.jar to lib/logging/logback-core-1.0.7.jar
+DOWNLOAD: http://central.maven.org/maven2/ch/qos/logback/logback-classic/1.0.7/logback-classic-1.0.7.jar to lib/logging/logback-classic-1.0.7.jar
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/logback/logback.xml to resources/logback.xml
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/logback/jetty-logging.properties to resources/jetty-logging.properties
+
+[mybase]$ java -jar /opt/jetty-dist/start.jar
+....
+
+The replacement `logging.mod` performs a number of tasks.
+
+.  `mybase` is a `${jetty.base}` directory.
+.  The jetty-distribution is unpacked (and untouched) into `/opt/jetty-dist/` and becomes the `${jetty.home}` directory for this demonstration.
+.  The `curl` command downloads the replacement `logging.mod` and puts it into the `${jetty.base}/modules/` directory for use by `mybase` only.
+.  The `start.jar --add-to-start=logging` command performs a number of steps to make the logging module available to the `${jetty.base}` configuration.
+..  The `--module=logging` command is added to the `${jetty.base}/start.ini` configuration.
+..  Required `${jetty.base}` directories are created: `${jetty.base}/logs` and `${jetty.base}/resources`.
+..  Required libraries are downloaded (if not present already) to the `${jetty.base}/lib/logging/` directory: slf4j-api, logback-core, and logback-classic.
+..  Required configuration files are downloaded (if not present already) to the `${jetty.base}/lib/resources/` directory.: `jetty-logging.properties`, and `logback.xml`.
+
+At this point the Jetty `mybase` is configured so that the Jetty server itself will log using Logback, using the Logback configuration found in `mybase/resources/logback.xml`
+
+The server classpath can be verified by using the `start.jar --list-config` command.
+
+In essence, Jetty is now configured to emit its own logging events to slf4j, and slf4j itself is using the static log binder found in logback-classic.jar, making all Jetty + Slf4j + Logback events emitted by the Jetty server go to Logback for routing (to console, file, syslog, etc...).
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/example-slf4j-multiple-loggers.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/example-slf4j-multiple-loggers.adoc
new file mode 100644
index 0000000..32a7ece
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/example-slf4j-multiple-loggers.adoc
@@ -0,0 +1,140 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[example-slf4j-multiple-loggers]]
+=== Example: Capturing Multiple Logging Frameworks with Slf4j
+
+This page describes how to configure Jetty for capturing multiple logging frameworks logging events into a single logging implementation handled by Slf4j.
+
+A single logging solution can be configured for the variety of logging libraries available in common use when using Slf4j. With careful setup, all of the following logging APIs can be supported at the same time, with a single configuration file to control the output of events produced.
+
+Logging APIs that Slf4j supports:
+
+* Slf4j API
+* Logback API
+* Apache Log4j 1.2
+* JDK 1.4 Logging (aka `java.util.logging`)
+* Apache Commons Logging
+
+To accomplish this configuration a single underlying logging framework should first be chosen.
+This decision guides the rest of the choices about jar files to place on the server classpath.
+
+____
+[CAUTION]
+There MUST NOT be multiple underlying logging frameworks on the classpath.
+If there are, the Slf4j framework fails to load.
+____
+
+____
+[NOTE]
+Some third party libraries provide their own implementations of common logging APIs; be careful not to accidentally include an underlying logging framework.
+For example, if you are using SpringSource you likely have a `com.springsource.org.apache.log4j.jar` along with a `log4j.jar`, which have the same classes in them.
+In this example, use the `com.springsource.org.apache.log4j.jar` version and exclude the `log4j.jar`, as the SpringSource version includes extra metadata suitable for using SpringSource.
+____
+
+.Slf4j Logging Grid
+[width="100%",cols="25%,25%,25%,25%",options="header",]
+|=======================================================================
+|Logging API |Slf4j Binding Jar |Slf4j Adapter Jar |Underlying Logging Framework
+
+|Logback API |n/a |logback-classic.jar |logback-core.jar
+
+|Log4j
+|http://slf4j.org/legacy.html#log4j-over-slf4j[log4j-over-slf4j.jar]
+|slf4j-log4j12.jar |log4j.jar
+
+|JDK 1.4 Logging
+|http://slf4j.org/legacy.html#jul-to-slf4j[jul-to-slf4j.jar]
+|slf4j-jdk14.jar |(Core Java Classlib)
+
+|Commons Logging
+|http://slf4j.org/legacy.html#jcl-over-slf4j[jcl-over-slf4j.jar]
+|slf4j-jcl.jar |commons-logging.jar
+|=======================================================================
+
+Logging API::
+  * The Logging API that you are either capturing events from and/or using to write out those events (for example, to disk).
+Slf4j Binding JAR::
+  * Special JARs, created and maintained by the Slf4j project, that pretend to be the various Logging API implementation classes, but instead just route that Logging API's events to Slf4j to handle.
+  * There MAY be multiple Slf4j binding JARs present on the classpath at the same time.
+
+  * For a single logging API, if you choose to use the Slf4j binding JAR, then you MUST NOT include the SLf4j adapter JAR or underlying logging  framework in the classpath as well.
+Slf4j Adapter Jar::
+  * These JARs are created and maintained by the Slf4j project and route Slf4j logging events to a specific underlying logging framework.
+  * There MUST NOT be multiple Slf4j adapter JARs present on the classpath at the same time.
+  * Logging events that these adapter JARs capture can come from direct use of the Slf4j API or via one of the Slf4j binding JAR implementations.
+Underlying Logging Framework::
+  * This is the last leg of the configuration, the implementation that processes, filters, and outputs the logging events to the console, logging directory on disk, or whatever else the underlying logging framework supports (like Socket, SMTP, Database, or even SysLog in the case of Logback).
+
+The following sections use Logback as the underlying Logging framework.
+This requires using `logback-classic.jar` and `logback-core.jar`, and excluding any other Slf4j adapter JAR or underlying logging framework.
+
+It also requires including the other Slf4j binding JARs in the classpath, along with some special initialization for `java.util.logging`.
+
+A convenient replacement `logging` module has been created to bootstrap the `${jetty.base}` directory for capturing all Jetty server logging from multiple logging frameworks into a single logging output file managed by logback.
+
+[source, screen, subs="{sub-order}"]
+....
+[mybase]$ mkdir modules
+[mybase]$ cd modules
+
+[modules]$ curl -O https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/logging.mod
+  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
+                                 Dload  Upload   Total   Spent    Left  Speed
+100  1293  100  1293    0     0   3693      0 --:--:-- --:--:-- --:--:--  3694
+[modules]$ cd ..
+
+[mybase]$ java -jar /opt/jetty-dist/start.jar --add-to-start=logging
+INFO: logging         initialised in ${jetty.base}/start.ini (appended)
+MKDIR: ${jetty.base}/logs
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.6.6/slf4j-api-1.6.6.jar to lib/logging/slf4j-api-1.6.6.jar
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/log4j-over-slf4j/1.6.6/log4j-over-slf4j-1.6.6.jar to lib/logging/log4j-over-slf4j-1.6.6.jar
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/jul-to-slf4j/1.6.6/jul-to-slf4j-1.6.6.jar to lib/logging/jul-to-slf4j-1.6.6.jar
+DOWNLOAD: http://central.maven.org/maven2/org/slf4j/jcl-over-slf4j/1.6.6/jcl-over-slf4j-1.6.6.jar to lib/logging/jcl-over-slf4j-1.6.6.jar
+DOWNLOAD: http://central.maven.org/maven2/ch/qos/logback/logback-core/1.0.7/logback-core-1.0.7.jar to lib/logging/logback-core-1.0.7.jar
+DOWNLOAD: http://central.maven.org/maven2/ch/qos/logback/logback-classic/1.0.7/logback-classic-1.0.7.jar to lib/logging/logback-classic-1.0.7.jar
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/logback.xml to resources/logback.xml
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/jetty-logging.properties to resources/jetty-logging.properties
+DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/jetty-logging.xml to etc/jetty-logging.xml
+INFO: resources       initialised transitively
+INFO: resources       enabled in     ${jetty.base}/start.ini
+
+[mybase]$ java -jar /opt/jetty-dist/start.jar
+....
+
+The replacement `logging.mod` performs a number of tasks.
+
+.  `mybase` is a `${jetty.base}` directory.
+.  The jetty-distribution is unpacked (and untouched) into `/opt/jetty-dist/` and becomes the `${jetty.home}` directory for this demonstration.
+.  The `curl` command downloads the replacement `logging.mod` and puts it into the `${jetty.base}/modules/` directory for use by `mybase` only.
+.  The `start.jar --add-to-start=logging` command performs a number of steps to make the logging module available to the `${jetty.base}` configuration.
+..  The `--module=logging` command is added to the `${jetty.base}/start.ini` configuration.
+..  Required `${jetty.base}` directories are created: `${jetty.base}/logs` and `${jetty.base}/resources`.
+..  Required libraries are downloaded (if not present already) to the the `${jetty.base}/lib/logging/` directory:
+* `slf4j-api.jar` - API jar for Slf4j (used by most of the rest of the jars)
+* `log4j-over-slf4j.jar` - Slf4j jar that captures all log4j emitted logging events
+* `jul-to-slf4j.jar` - Slf4j jar that captures all `java.util.logging` events
+* `jcl-over-slf4j.jar` - Slf4j jar that captures all `commons-logging` events
+* `logback-classic.jar` - the Slf4j adapter jar that routes all of the captured logging events to logback itself.
+* `logback-core.jar` - the logback implementation jar, that handles all of the filtering and output of the logging events.
+..  Required configuration files are downloaded (if not present already) to the `${jetty.base}/resources/` directory: `jetty-logging.properties`, and `logback.xml`
+..  Required `java.util.logging` initialization commands are downloaded (if not present already) to the `${jetty.base}/etc/` directory: `jetty-logging.xml`
+
+At this point the Jetty `mybase` is configured so that the jetty server itself will log using slf4j, and all other logging events from other Jetty server components (such as database drivers, security layers, jsp, mail, and other 3rd party server components) are routed to logback for filtering and output.
+
+The server classpath can be verified by using the `start.jar --list-config` command.
+
+In essence, Jetty is now configured to emit its own logging events to slf4j, and various slf4j bridge jars are acting on behalf of log4j, `java.util.logging`, and `commons-logging`, routing all of the logging events to logback (a Slf4j adapter) for routing (to console, file, etc...).
diff --git a/jetty-documentation/src/main/asciidoc/administration/npn/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/npn/chapter.adoc
new file mode 100644
index 0000000..b589fce
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/npn/chapter.adoc
@@ -0,0 +1,18 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[npn-chapter]]
+== NPN
diff --git a/jetty-documentation/src/main/asciidoc/administration/npn/npn.adoc b/jetty-documentation/src/main/asciidoc/administration/npn/npn.adoc
new file mode 100644
index 0000000..34ee58a
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/npn/npn.adoc
@@ -0,0 +1,305 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+How to build NPN
+================
+
+[[npn]]
+=
+
+[[npn-introduction]]
+== Configuring NPN
+
+The Jetty project provides an implementation of the Transport Layer
+Security (TLS) extension for Next Protocol Negotiation (NPN) for OpenJDK
+7 (but not for OpenJDK 8 or greater - see xref:alpn-chapter[] for
+using a TLS protocol negotiation extension with OpenJDK 8 or greater).
+NPN allows the application layer to negotiate which protocol to use over
+the secure connection.
+
+NPN currently negotiates using SPDY as an application level protocol on
+port 443, and also negotiates the SPDY version. However, NPN is not
+SPDY-specific in any way. Jetty's NPN implementation, although hosted
+under the umbrella of the Jetty project, is independent of Jetty (the
+servlet container); you can use it in any other Java network server.
+
+[[npn-starting]]
+== Starting the JVM
+
+To enable NPN support, start the JVM as follows:
+
+[source, plain, subs="{sub-order}"]
+----
+java -Xbootclasspath/p:<path_to_npn_boot_jar> ...            
+----
+
+where `path_to_npn_boot_jar` is the path on the file system for the NPN
+Boot Jar file, for example, one at the Maven coordinates
+org.mortbay.jetty.npn:npn-boot.
+
+Be aware that the current versions of the npn packages no longer align
+with Jetty versions. Look at the dates in those file paths before
+looking at the version number.
+
+[[npn-osgi]]
+=== Starting in OSGi
+
+To use NPN in an OSGi environment, in addition to putting the NPN jar on
+the boot classpath for the container, you will also need to deploy the
+jetty-osgi-npn jar. This jar contains a Fragment-Host directive that
+ensures the NPN classes will be available from the system bundle.
+
+You can download the jetty-osgi-npn jar from maven central:
+http://central.maven.org/maven2/org/eclipse/jetty/osgi/jetty-osgi-npn/
+
+[[npn-understanding]]
+== Understanding the NPN API
+
+Applications need to interact with NPN TLS extension protocol
+negotiations. For example, server applications need to know whether the
+client supports NPN, and client applications needs to know the list of
+protocols the server supports, and so on.
+
+To implement this interaction, Jetty's NPN implementation provides an
+API to applications, hosted at Maven coordinates
+org.eclipse.jetty.npn:npn-api. You need to declare this dependency as
+provided, because the npn-boot Jar already includes it (see the previous
+section), and it is therefore available in the boot classpath.
+
+The API consists of a single class,
+`org.eclipse.jetty.npn.NextProtoNego`, and applications need to register
+instances of SSLSocket or SSLEngine with a ClientProvider or
+ServerProvider (depending on whether the application is a client or
+server application). Refer to NextProtoNego Javadocs and to the examples
+below for further details about client and server provider methods.
+
+[[client-example]]
+== Client Example
+
+[source, java, subs="{sub-order}"]
+----
+SSLContext sslContext = ...;
+final SSLSocket sslSocket = (SSLSocket)context.getSocketFactory().createSocket("localhost", server.getLocalPort());
+
+NextProtoNego.put(sslSocket, new NextProtoNego.ClientProvider()
+{
+    @Override
+    public boolean supports()
+    {
+        return true;
+    }
+
+    @Override
+    public void unsupported()
+    {
+        NextProtoNego.remove(sslSocket);
+    }
+
+    @Override
+    public String selectProtocol(List<String> protocols)
+    {
+        NextProtoNego.remove(sslSocket);
+        return protocols.get(0);
+    }
+});
+----
+
+The NPN implementation calls `NextProtoNego.ClientProvider` methods
+`supports()`, `unsupported()` and `selectProtocol(List<String>)`, so
+that the client application can:
+
+* decide whether to support NPN.
+* know whether the server supports NPN.
+* select one of the protocols the server supports.
+
+[[server-example]]
+== Server Example
+
+The example for SSLEngine is identical, and you just need to replace the
+SSLSocket instance with an SSLEngine instance.
+
+[source, java, subs="{sub-order}"]
+----
+final SSLSocket sslSocket = ...;
+NextProtoNego.put(sslSocket, new NextProtoNego.ServerProvider()
+{
+    @Override
+    public void unsupported()
+    {
+        NextProtoNego.remove(sslSocket);
+    }
+
+    @Override
+    public List<String> protocols()
+    {
+        return Arrays.asList("http/1.1");
+    }
+
+    @Override
+    public void protocolSelected(String protocol)
+    {
+        NextProtoNego.remove(sslSocket);
+        System.out.println("Protocol Selected is: " + protocol);
+    }
+});
+----
+
+The NPN implementation calls `NextProtoNego.ServerProvider` methods
+`unsupported()`, `protocols()` and `protocolSelected(String),` so that
+the server application can:
+
+* know whether the client supports NPN.
+* provide the list of protocols the server supports.
+* know which protocol the client chooses.
+
+[[npn-implementation]]
+== Implementation Details
+
+It is common that the NextProtoNego.ServerProvider and the
+NextProtoNego.ClientProvider are implemented as (anonymous) inner
+classes, and that their methods' implementations require references to
+the the sslSocket (or sslEngine), either directly or indirectly.
+
+Since the NextProtoNego class holds [sslSocket/sslEngine, provider]
+pairs in a `WeakHashMap`, if the value (that is, the provider
+implementation) holds a strong (even indirect) reference to the key,
+then the `WeakHashMap` entries are never removed, leading to a memory
+leak.
+
+It is important that implementations of `NextProtoNego.ServerProvider`
+and `NextProtoNego.ClientProvider` remove the `sslSocket` or `sslEngine`
+when the negotiation is complete, like shown in the examples above.
+
+Be aware that declaring the SslConnection as a final local variable and
+referencing it from within the anonymous NextProtoNego.ServerProvider
+class generates a hidden field in the anonymous inner class, that may
+cause a memory leak if the implementation does not call
+`NextProtoNego.remove()`.
+
+[[npn-tests]]
+== Unit Tests
+
+You can write and run unit tests that use the NPN implementation. The
+solution that we use with Maven is to specify an additional command line
+argument to the Surefire plugin:
+
+[source, xml, subs="{sub-order}"]
+----
+<project>
+
+<properties>
+    <npn-version>1.1.1.v20121030</npn-version>
+</properties>
+
+<build>
+    <plugins>
+        <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+                <argLine>
+                    -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn-version}/npn-boot-${npn-version}.jar
+                </argLine>
+            </configuration>
+        </plugin>
+
+        ...
+
+    </plugins>
+</build>
+
+...
+
+</project>
+----
+
+[[npn-debugging]]
+== Debugging
+
+You can enable debug logging for the NPN implementation in this way:
+
+....
+NextProtoNego.debug = true;
+....
+
+Since the NextProtoNego class is in the boot classpath, we chose not to
+use logging libraries because we do not want to override application
+logging library choices; therefore the logging is performed directly on
+`System.err.`
+
+[[npn-license-details]]
+== License Details
+
+The NPN implementation relies on modification of a few OpenJDK classes
+and on a few new classes that need to live in the `sun.security.ssl`
+package. These classes are released under the same GPLv2+exception
+license of OpenJDK.
+
+The NextProtoNego class is released under same license as the classes of
+the Jetty project.
+
+[[npn-versions]]
+== Versions
+
+The NPN implementation, relying on modifications of OpenJDK classes,
+updates every time there are updates to the modified OpenJDK classes.
+
+.NPN vs. OpenJDK versions
+[cols=",",options="header",]
+|==========================================================
+|NPN version |OpenJDK version
+|1.0.0.v20120402 |1.7.0 - 1.7.0u2 - 1.7.0u3
+|1.1.0.v20120525 |1.7.0u4 - 1.7.0u5
+|1.1.1.v20121030 |1.7.0u6 - 1.7.0u7
+|1.1.3.v20130313 |1.7.0u9 - 1.7.0u10 - 1.7.0u11
+|1.1.4.v20130313 |1.7.0u13
+|1.1.5.v20130313 |1.7.0u15 - 1.7.0u17 - 1.7.0u21 - 1.7.0u25
+|1.1.6.v20130911 |1.7.0u40 - 1.7.0u45 - 1.7.0u51
+|1.1.8.v20141013 |1.7.0u55 - 1.7.0u60 - 1.7.0u65 - 1.7.0u67
+|1.1.9.v20141016 |1.7.0u71 - 1.7.0u72
+|1.1.10.v20150130 |1.7.0u75 - 1.7.0u76 - 1.7.0u79
+|1.1.11.v20150415 |1.7.0u80
+|==========================================================
+
+[[npn-build]]
+== How to build NPN
+
+This section is for Jetty developers that need to update the NPN
+implementation with the OpenJDK versions.
+
+Clone the OpenJDK repository with the following command:
+
+[source, screen, subs="{sub-order}"]
+....
+$ hg clone http://hg.openjdk.java.net/jdk7u/jdk7u jdk7u
+$ cd jdk7u
+$ ./get_source.sh
+  
+....
+
+To update the source to a specific tag, use the following command:
+
+[source, screen, subs="{sub-order}"]
+....
+$ ./make/scripts/hgforest.sh update <tag-name>
+  
+....
+
+The list of OpenJDK tags can be obtained from
+http://hg.openjdk.java.net/jdk7u/jdk7u/tags[this page].
+
+Then you need to compare and incorporate the OpenJDK source changes into
+the modified OpenJDK classes at the
+https://github.com/jetty-project/jetty-npn[NPN GitHub Repository].
diff --git a/jetty-documentation/src/main/asciidoc/administration/part.adoc b/jetty-documentation/src/main/asciidoc/administration/part.adoc
new file mode 100644
index 0000000..839b1c1
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/part.adoc
@@ -0,0 +1,31 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+[[jetty-admin-guide]]
+
+= Jetty Administration Guide
+
+include::startup/chapter.adoc[]
+include::sessions/chapter.adoc[]
+include::logging/chapter.adoc[]
+include::jndi/chapter.adoc[]
+include::annotations/chapter.adoc[]
+include::jmx/chapter.adoc[]
+include::alpn/chapter.adoc[]
+include::http2/chapter.adoc[]
+include::fastcgi/chapter.adoc[]
+include::extras/chapter.adoc[]
+include::runner/chapter.adoc[]
+include::tuning/chapter.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/administration/runner/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/runner/chapter.adoc
new file mode 100644
index 0000000..b933efa
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/runner/chapter.adoc
@@ -0,0 +1,22 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[runner]]
+== Jetty Runner
+
+This chapter explains how to use the `jetty-runner` to run your webapps without needing an installation of Jetty.
+
+include::jetty-runner.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/administration/runner/jetty-runner.adoc b/jetty-documentation/src/main/asciidoc/administration/runner/jetty-runner.adoc
new file mode 100644
index 0000000..1943faf
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/runner/jetty-runner.adoc
@@ -0,0 +1,323 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-runner]]
+=== Use Jetty without an installed distribution
+
+The idea of the `jetty-runner` is extremely simple – run a webapp directly from the command line using a single jar file and as much default configuration as possible.
+Of course, if your webapp is not so straightforward, the `jetty-runner` has command line options which allow you to customize the execution environment.
+
+[[jetty-runner-preparation]]
+==== Preparation
+
+You will need the `jetty-runner` jar:
+
+1.  http://central.maven.org/maven2/org/eclipse/jetty/jetty-runner/[Get] the `jetty-runner` jar available at http://search.maven.org/#browse[maven central].
+
+==== Deploying a simple context
+
+Let's assume we have a very simple webapp that does not need any resources from its environment, nor any configuration apart from the defaults.
+Starting it is as simple as performing the following:
+
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar simple.war
+....
+
+This will start Jetty on port 8080, and deploy the webapp to `/`.
+
+Your webapp does not have to be packed into a war, you can deploy a webapp that is a directory instead in the same way:
+
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar simple
+....
+
+In fact, the webapp does not have to be a war or even a directory, it can simply be a Jetty link:#using-context-provider[context xml] file that describes your webapp:
+
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar simple-context.xml
+....
+
+____
+[NOTE]
+When using a context xml file, the application being deployed is not even required to be a fully-fledged webapp. It can simply be a Jetty link:#what-is-a-context[context].
+____
+
+==== Deploying multiple contexts
+
+If you have more than one webapp that must be deployed, simply provide them all on the command line.
+You can control the context paths for them using the `--path` parameter.
+Here's an example of deploying 2 wars (although either or both of them could be unpacked directories instead):
+
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --path /one my1.war --path /two my2.war
+....
+
+If you have context xml files that describe your webapps, you can fully configure your webapps in them and hence you don't need to use the command line switches.
+Just provide the list of context files like so:
+
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar my-first-context.xml my-second-context.xml my-third-context.xml
+....
+
+____
+[NOTE]
+Switched used on the command line override configuration file settings.
+So, for example, you could set the context path for the webapp inside the context xml file, and use the `--path` switch to override it on the command line.
+____
+
+
+===== Changing the default port
+
+By default the `jetty-runner` will listen on port 8080.
+You can easily change this on the command line using the `--port` command.
+Here's an example that runs our simple.war on port 9090:
+
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --port 9090 simple.war
+....
+
+===== Using jetty.xml files
+
+Instead of, or in addition to, using command line switches, you can use one or more `jetty.xml` files to configure the environment for your webapps.
+Here's an example where we apply two different `jetty.xml` files:
+
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --config jetty.xml --config jetty-https.xml simple.war
+....
+
+===== Full configuration reference
+
+You can see the fill set of configuration options using the `--help` switch:
+
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --help
+....
+
+Here's what the output will look like:
+
+[source, plain, subs="{sub-order}"]
+----
+
+Usage: java [-Djetty.home=dir] -jar jetty-runner.jar [--help|--version] [ server opts] [[ context opts] context ...]
+Server opts:
+ --version                           - display version and exit
+ --log file                          - request log filename (with optional 'yyyy_mm_dd' wildcard
+ --out file                          - info/warn/debug log filename (with optional 'yyyy_mm_dd' wildcard
+ --host name|ip                      - interface to listen on (default is all interfaces)
+ --port n                            - port to listen on (default 8080)
+ --stop-port n                       - port to listen for stop command
+ --stop-key n                        - security string for stop command (required if --stop-port is present)
+ [--jar file]*n                      - each tuple specifies an extra jar to be added to the classloader
+ [--lib dir]*n                       - each tuple specifies an extra directory of jars to be added to the classloader
+ [--classes dir]*n                   - each tuple specifies an extra directory of classes to be added to the classloader
+ --stats [unsecure|realm.properties] - enable stats gathering servlet context
+ [--config file]*n                   - each tuple specifies the name of a jetty xml config file to apply (in the order defined)
+Context opts:
+ [[--path /path] context]*n          - WAR file, web app dir or context xml file, optionally with a context path
+----
+
+Printing the version:::
+Print out the version of Jetty and then exit immediately.
++
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --version
+....
+
+Configuring a request log:::
+Cause Jetty to write a request log with the given name.
+If the file is prefixed with `yyyy_mm_dd` then the file will be automatically rolled over.
+Note that for finer grained configuration of the link:{JDURL}/org/eclipse/jetty/server/NCSARequestLog.html[request log], you will need to use a Jetty xml file instead.
++
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --log yyyy_mm_dd-requests.log  my.war
+....
+
+Configuring the output log:::
+Redirect the output of jetty logging to the named file.
+If the file is prefixed with `yyyy_mm_dd` then the file will be automatically rolled over.
++
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --out yyyy_mm_dd-output.log my.war
+....
+
+Configuring the interface for http:::
+Like Jetty standalone, the default is for the connectors to listen on all interfaces on a machine.
+You can control that by specifying the name or ip address of the particular interface you wish to use with the `--host` argument:
++
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --host 192.168.22.19 my.war
+....
+
+Configuring the port for http:::
+The default port number is 8080.
+To link:#how-to-configure-connectors[configure a https connector], use a Jetty xml config file instead.
++
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --port 9090  my.war
+....
+
+Configuring stop:::
+You can configure a port number for jetty to listen on for a stop command, so you are able to stop it from a different terminal.
+This requires the use of a "secret" key, to prevent malicious or accidental termination.
+Use the `--stop-port` and `--stop-key` parameters as arguments to the `jetty-runner`:
++
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --stop-port 8181 --stop-key abc123
+
+....
++
+Then, to stop Jetty from a different terminal, you need to supply the same port and key information.
+For this you'll either need a local installation of Jetty, the link:#jetty-maven-plugin[jetty-maven-plugin], the link:#jetty-ant[jetty-ant plugin], or a custom class.
+Here's how to use a Jetty installation to perform a stop:
++
+[source, screen, subs="{sub-order}"]
+....
+> java -jar start.jar --stop-port 8181 --stop-key abc123 --stop
+....
+
+Configuring the container classpath:::
+With a local installation of Jetty, you add jars and classes to the container's classpath by putting them in the `{$jetty.base}/lib` directory.
+With the `jetty-runner`, you can use the `--lib`, `--jar` and `--classes` arguments instead to achieve the same thing.
++
+`--lib` adds the location of a directory which contains jars to add to the container classpath.
+You can add 1 or more.
+Here's an example of configuring 2 directories:
++
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --lib /usr/local/external/lib --lib $HOME/external-other/lib my.war
+....
++
+`--jar` adds a single jar file to the container classpath.
+You can add 1 or more.
+Here's an example of configuring 3 extra jars:
++
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --jar /opt/stuff/jars/jar1.jar --jar $HOME/jars/jar2.jar --jar /usr/local/proj/jars/jar3.jar  my.war
+....
++
+`--classes` add the location of a directory containing classes to add to the container classpath.
+You can add 1 or more.
+Here's an example of configuring a single extra classes dir:
++
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --classes /opt/stuff/classes my.war
+....
+
+Gathering statistics:::
+If statistics gathering is enabled, then they are viewable by surfing to the context `/stats`.
+You may optionally protect access to that context with a password.
+Here's an example of enabling statistics, with no password protection:
++
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --stats unsecure my.war
+....
++
+If we wished to protect access to the `/stats` context, we would provide the location of a Jetty realm configuration file containing authentication and authorization information.
+For example, we could use the following example realm file from the Jetty distribution:
++
+[source, screen, subs="{sub-order}"]
+....
+jetty: MD5:164c88b302622e17050af52c89945d44,user
+admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin
+other: OBF:1xmk1w261u9r1w1c1xmq,user
+plain: plain,user
+user: password,user
+# This entry is for digest auth.  The credential is a MD5 hash of username:realmname:password
+digest: MD5:6e120743ad67abfbc385bc2bb754e297,user
+....
++
+Assuming we've copied it into the local directory, we would apply it like so
++
+[source, screen, subs="{sub-order}"]
+....
+> java -jar jetty-runner.jar --stats realm.properties my.war
+....
++
+After navigating to http://localhost:8080/ a few times, we can point to the stats servlet on http://localhost:8080/stats to see the output:
++
+....
+Statistics:
+Statistics gathering started 1490627ms ago
+
+Requests:
+Total requests: 9
+Active requests: 1
+Max active requests: 1
+Total requests time: 63
+Mean request time: 7.875
+Max request time: 26
+Request time standard deviation: 8.349764752888037
+
+
+Dispatches:
+Total dispatched: 9
+Active dispatched: 1
+Max active dispatched: 1
+Total dispatched time: 63
+Mean dispatched time: 7.875
+Max dispatched time: 26
+Dispatched time standard deviation: 8.349764752888037
+Total requests suspended: 0
+Total requests expired: 0
+Total requests resumed: 0
+
+
+Responses:
+1xx responses: 0
+2xx responses: 7
+3xx responses: 1
+4xx responses: 0
+5xx responses: 0
+Bytes sent total: 1453
+
+
+Connections:
+org.eclipse.jetty.server.ServerConnector@203822411
+Protocols:http/1.1
+Statistics gathering started 1490606ms ago
+Total connections: 7
+Current connections open: 1
+Max concurrent connections open: 2
+Total connections duration: 72883
+Mean connection duration: 12147.166666666666
+Max connection duration: 65591
+Connection duration standard deviation: 23912.40292977684
+Total messages in: 7
+Total messages out: 7
+
+
+Memory:
+Heap memory usage: 49194840 bytes
+Non-heap memory usage: 12611696 bytes
+....
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/chapter.adoc
new file mode 100644
index 0000000..5f0ca5c
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/chapter.adoc
@@ -0,0 +1,25 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[session-management]]
+== Session Management
+
+include::setting-session-characteristics.adoc[]
+include::using-persistent-sessions.adoc[]
+include::session-clustering-jdbc.adoc[]
+include::session-clustering-mongodb.adoc[]
+include::session-clustering-infinispan.adoc[]
+include::session-clustering-gcloud-datastore.adoc[]
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-clustering-gcloud-datastore.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-clustering-gcloud-datastore.adoc
new file mode 100644
index 0000000..06cc9d1
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-clustering-gcloud-datastore.adoc
@@ -0,0 +1,274 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[session-clustering-gcloud-datastore]]
+=== Session Clustering with Google Cloud Datastore
+
+Jetty can support session clustering by persisting sessions to https://cloud.google.com/datastore/docs/concepts/overview[Google Cloud Datastore]. 
+Each Jetty instance locally caches sessions for which it has received requests, writing any changes to the session through to the Datastore as the request exits the server. 
+Sessions must obey the Serialization contract, and servlets must call the `Session.setAttribute()` method to ensure that changes are persisted.
+
+The persistent session mechanism works in conjunction with a load balancer that supports stickiness. 
+Stickiness can be based on various data items, such as source IP address or characteristics of the session ID or a load-balancer specific mechanism. 
+For those load balancers that examine the session ID, the Jetty persistent session mechanism appends a node ID to the session ID, which can be used for routing.
+
+==== Configuration
+
+There are two components to session management in Jetty: a session ID manager and a session manager.
+
+- The session ID manager ensures that session IDs are unique across all webapps hosted on a Jetty instance, and thus there can only be one session ID manager per Jetty instance.
+- The session manager handles the session lifecycle (create/update/invalidate/expire) on behalf of a web application, so there is one session manager per web application instance.
+
+These managers also cooperate and collaborate with the `org.eclipse.jetty.server.session.SessionHandler` to enable cross-context dispatch.
+
+==== The gcloud-sessions Module
+
+When using the jetty distribution, to enable Cloud Datastore session persistence, you will first need to enable the `gcloud-sessions` link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` or `--add-to-startd` argument to the link:#startup-overview[start.jar].
+
+As part of the module installation, the necessary jars will be dynamically downloaded and installed to your `${jetty.base}/lib/gcloud` directory. 
+If you need to up or downgrade the version of the jars, then you can delete the jars that were automatically installed and replace them. 
+Once you've done that, you will need to prevent jetty's startup checks from detecting the missing jars. 
+To do that, you can use `--skip-file-validation=glcoud-sessions` argument to start.jar on the command line, or place that line inside `${jetty.base}/start.ini` to ensure it is used for every start.
+
+===== Configuring the GCloudSessionIdManager
+
+The gcloud-sessions module will have installed file called `${jetty.home}/etc/jetty-gcloud-sessions.xml`. 
+This file configures an instance of the `GCloudSessionIdManager` that will be shared across all webapps deployed on that server. It looks like this:
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml[]
+----
+
+You configure it by setting values for properties. 
+The properties will either be inserted as commented out in your `start.ini`, or your `start.d/gcloud-sessions.ini` file, depending on how you enabled the module.
+
+The only property you always need to set is the name of the node in the cluster:
+
+jetty.gcloudSession.workerName::
+  The name that uniquely identifies this node in the cluster. 
+  This value will also be used by the sticky load balancer to identify the node.
+  Don't forget to change the value of this property on *each* node on which you enable gcloud datastore session clustering.
+
+
+===== Configuring GCloud Datastore
+
+Things that you will need:
+
+- a local installation of the https://cloud.google.com/sdk/[Google Cloud SDK]
+- a project id referred to below as [YOUR PROJECT ID]
+- to have https://cloud.google.com/datastore/docs/activate[enabled your project id] to use GCloud Datastore
+
+====== Using GCloud Datastore from Compute/AppEngine
+
+If you are running your webapp from within ComputeEngine or AppEngine, you do not need to do anything else in order to configure your GCloud setup. All necessary information will be inferred from the environment by the infrastrcture.
+
+====== Using GCloud Datastore from an external server
+
+If you are running your webapp externally to Google infrastructure, you can still interact with the remote GCloud Datastore service.
+
+Execute the following commands:
+
+- gcloud config set project [YOUR PROJECT ID].
+- gcloud auth login
+
+This will populate your environment with the necessary authentication information to allow you to contact the remote GCloud Datastore instance.
+
+====== Using GCloud Datastore local development server
+
+If you would like to locally test your application, you can use the Google Cloud SDK's https://cloud.google.com/datastore/docs/tools/datastore-emulator[GCloud Datastore emulator].
+
+Follow the instructions on the https://cloud.google.com/datastore/docs/tools/datastore-emulator[GCloud Datastore emulator page] to set up your environment.
+
+===== Configuring the GCloudSessionManager
+
+As mentioned elsewhere, there must be one `SessionManager` per context (e.g. webapp). 
+Each SessionManager needs to reference the single `GCloudSessionIdManager`.
+
+The way you configure a `GCloudSessionManager` depends on whether you're configuring from a context xml file, a `jetty-web.xml` file or code.
+The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
+
+From a context xml file, you reference the Server instance as a Ref:
+
+[source, xml, subs="{sub-order}"]
+----  
+  <!-- Get a reference to the GCloudSessionIdManager -->
+  <Ref id="Server">
+    <Call id="idMgr" name="getSessionIdManager"/>
+  </Ref>
+
+  <!-- Use the GCloudSessionIdManager to set up the GCloudSessionManager -->
+  <Set name="sessionHandler">
+    <New class="org.eclipse.jetty.server.session.SessionHandler">
+      <Arg>
+        <New id="mgr" class="org.eclipse.jetty.gcloud.session.GCloudSessionManager">
+          <Set name="sessionIdManager">
+            <Ref id="idMgr"/>
+          </Set>
+          <Set name="scavengeIntervalSec">600</Set>
+        </New>
+      </Arg>
+    </New>
+  </Set>
+----
+
+From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
+
+[source, xml, subs="{sub-order}"]
+----
+<!-- Reference the server directly -->
+<Get name="server">
+  <Get id="idMgr" name="sessionIdManager"/>
+</Get>
+
+<!-- Apply the SessionIdManager to the GCloudSessionManager -->
+<Set name="sessionHandler">
+  <New class="org.eclipse.jetty.server.session.SessionHandler">
+     <Arg>
+        <New id="mgr" class="org.eclipse.jetty.gcloud.session.GCloudSessionManager">
+          <Set name="sessionIdManager">
+            <Ref id="idMgr"/>
+          </Set>
+          <Set name="scavengeIntervalSec">600</Set>
+        </New>
+      </Arg>
+  </New>
+</Set>
+----
+
+The `GCloudSessionManager` supports the following configuration setters:
+
+scavengeIntervalSec::
+  Time in seconds between runs of a scavenger task that looks for expired old sessions to delete. 
+  The default is 10 minutes. 
+  If set to 0, no scavenging is done.
+staleIntervalSec::
+  The length of time a session can be in memory without being checked against the cluster.
+  A value of 0 indicates that the session is never checked against the cluster - the current node is considered to be the master for the session.
+maxQueryResults::
+  The maximum number of results to return for a query to find expired sessions. 
+  For efficiency it is important to limit the size of the result. 
+  The default is 100. 
+  If 0 or negative numbers are set, the default is used instead.
+
+===== The gcloud-memcached-sessions module
+
+As an optimization, you can have Jetty store your session data into GCloud Datastore but also cache it into memcached. This serves two purposes: faster read-accesses and also better support for non-sticky load balancers (although using a non-sticky load balancer is highly undesirable and not recommended).
+
+You will need to enable the `gcloud-memcached-sessions` link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` or `--add-to-startd` argument to the link:#startup-overview[start.jar].
+
+If you already enabled the gcloud-sessions module, that's fine as the gcloud-memcached-sessions module depends on it anyway.
+
+Jetty uses the https://github.com/killme2008/xmemcached[Xmemcached] java client.
+It depends on http://www.slf4j.org/[slf4j], so you will need to choose an http://www.slf4j.org/[slf4j logging implementation]. You can copy the chosen implementation jars into your $jetty.base/lib/ext directory.
+
+====== Configuring the GCloudSessionIdManager
+
+The instructions here are exactly the same as for the gcloud-sessions module.
+
+====== Configuring GCloud Datastore
+
+The instructions here are exactly the same as for the gcloud-sessions module.
+
+====== Configuring Memcached
+
+If you have installed memcached on a host and port other than the defaults of `localhost` and `11211`, then you will need to take note of these values and supply them to the configuration of the `GCloudMemcachedSessionManager`.
+
+====== Configuring the GCloudMemcachedSessionManager
+
+*Note that* you will be configuring a `GCloudMemcachedSessionManager` 'instead of' a `GCloudSessionManager`.
+
+As usual, there must be only one per context (e.g. webapp). 
+Each GCloudMemcachedSessionManager needs to reference the single `GCloudSessionIdManager`.
+
+
+The way you configure a `GCloudMemcachedSessionManager` depends on whether you're configuring from a context xml file, a `jetty-web.xml` file or code.
+The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
+
+From a context xml file, you reference the Server instance as a Ref:
+
+[source, xml, subs="{sub-order}"]
+----  
+  <!-- Get a reference to the GCloudSessionIdManager -->
+  <Ref id="Server">
+    <Call id="idMgr" name="getSessionIdManager"/>
+  </Ref>
+
+  <!-- Use the GCloudSessionIdManager to set up the GCloudMemcachedSessionManager -->
+  <Set name="sessionHandler">
+    <New class="org.eclipse.jetty.server.session.SessionHandler">
+      <Arg>
+        <New id="mgr" class="org.eclipse.jetty.gcloud.memcached.session.GCloudMemcachedSessionManager">
+          <Set name="sessionIdManager">
+            <Ref id="idMgr"/>
+          </Set>
+          <Set name="scavengeIntervalSec">600</Set>
+          <Set name="host">myhost</Set>
+          <Set name="port">11211</Set>
+          <Set name="expirySec">0</Set>
+        </New>
+      </Arg>
+    </New>
+  </Set>
+----
+
+From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
+
+[source, xml, subs="{sub-order}"]
+----
+<!-- Reference the server directly -->
+<Get name="server">
+  <Get id="idMgr" name="sessionIdManager"/>
+</Get>
+
+<!-- Apply the SessionIdManager to the GCloudMemcachedSessionManager -->
+<Set name="sessionHandler">
+  <New class="org.eclipse.jetty.server.session.SessionHandler">
+     <Arg>
+        <New id="mgr" class="org.eclipse.jetty.gcloud..memcached.session.GCloudMemcachedSessionManager">
+          <Set name="sessionIdManager">
+            <Ref id="idMgr"/>
+          </Set>
+          <Set name="scavengeIntervalSec">600</Set>
+          <Set name="host">myhost</Set>
+          <Set name="port">11211</Set>
+          <Set name="expirySec">0</Set>
+        </New>
+      </Arg>
+  </New>
+</Set>
+----
+
+The `GCloudMemcachedSessionManager` supports the following configuration setters:
+
+scavengeIntervalSec::
+  Time in seconds between runs of a scavenger task that looks for expired old sessions to delete. 
+  The default is 10 minutes. 
+  If set to 0, no scavenging is done.
+staleIntervalSec::
+  The length of time a session can be in memory without being checked against the cluster.
+  A value of 0 indicates that the session is never checked against the cluster - the current node is considered to be the master for the session.
+maxQueryResults::
+  The maximum number of results to return for a query to find expired sessions. 
+  For efficiency it is important to limit the size of the result. 
+  The default is 100. 
+  If 0 or negative numbers are set, the default is used instead.
+host::
+  The address of the host where the memcached server is running. Defaults to "localhost".
+port::
+  The port on the host where the memcached serer is running. Defaults to "11211".
+expirySec::
+  The time in seconds that an entry in the memcached cache is considered valid. By default, entries are are not aged out of the cached, however they may be evicted due to memory constraints.
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-clustering-infinispan.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-clustering-infinispan.adoc
new file mode 100644
index 0000000..d1020cf
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-clustering-infinispan.adoc
@@ -0,0 +1,186 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[session-clustering-infinispan]]
+=== Session Clustering with Infinispan
+
+Jetty can support session clustering by persisting sessions to http://www.infinispan.org[Infinispan]. 
+Each Jetty instance locally caches sessions for which it has received requests, writing any changes to the session through to Infinispan as the request exits the server.
+Sessions must obey the Serialization contract, and servlets must call the `Session.setAttribute()` method to ensure that changes are persisted.
+
+The persistent session mechanism works in conjunction with a load balancer that supports stickiness. 
+Stickiness can be based on various data items, such as source IP address or characteristics of the session ID or a load-balancer specific mechanism. 
+For those load balancers that examine the session ID, the Jetty persistent session mechanism appends a node ID to the session ID, which can be used for routing.
+
+==== Configuration
+
+There are two components to session management in Jetty: a session ID manager and a session manager.
+
+* The session ID manager ensures that session IDs are unique across all webapps hosted on a Jetty instance, and thus there can only be one session ID manager per Jetty instance.
+* The session manager handles the session lifecycle (create/update/invalidate/expire) on behalf of a web application, so there is one session manager per web application instance.
+
+These managers also cooperate and collaborate with the `org.eclipse.jetty.server.session.SessionHandler` to enable cross-context dispatch.
+
+==== The Infinispan Module
+
+When using the jetty distribution, to enable Infinispan session persistence, you will first need to enable the Infinispan link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` or `--add-to-startd` argument to the link:#startup-overview[start.jar].
+
+As part of the module installation, the necessary Infinispan jars will be dynamically downloaded and installed to your `${jetty.base}/lib/infinispan` directory. 
+If you need to up or downgrade the version of the Infinispan jars, then you can delete the jars that were automatically installed and replace them. 
+Once you've done that, you will need to prevent Jetty's startup checks from detecting the missing jars. 
+To do that, you can use `--skip-file-validation=infinispan` argument to start.jar on the command line, or place that line inside `${jetty.base}/start.ini` to ensure it is used for every start.
+
+You will also find the following properties, either in your base's `start.d/infinispan.ini` file or appended to your `start.ini`, depending on how you enabled the module:
+
+....
+## Unique identifier for this node in the cluster
+jetty.infinispanSession.workerName=node1
+....
+
+jetty.infinispanSession.workerName::
+  The name that uniquely identifies this node in the cluster. 
+  This value   will also be used by the sticky load balancer to identify the node.
+  Don't forget to change the value of this property on *each* node on which you enable Infinispan session clustering.
+
+These properties are applied to the `InfinispanSessionIdManager` described below.
+
+===== Configuring the InfinispanSessionIdManager
+
+The Infinispan module will have installed file called `$\{jetty.home}/etc/jetty-infinispan.xml`. 
+This file configures an instance of the `InfinispanSessionIdManager` that will be shared across all webapps deployed on that server. 
+It looks like this:
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-infinispan/src/main/config/etc/jetty-infinispan.xml[]
+----
+
+As you can see, you configure the Infinispan http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_the_cache_apis[Cache] instance that the `InfinispanSessionIdManager` should use in this file. 
+By default, the Infinispan http://infinispan.org/docs/7.1.x/getting_started/getting_started.html#_running_infinispan_on_a_single_node[Default cache] instance is used (e.g. on the local node). 
+You can instead use a custom Cache setup - the `jetty-infinispan.xml` file shows you how to configure a remote Cache (using the http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_using_hot_rod_server[hotrod java client]).
+
+The `InfinispanSessionIdManager` can be configured by calling setters:
+
+idleExpiryMultiple::
+  Sessions that are not immortal, e.g. they have an expiry time, have   their ids stored into Infinispan with an http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_expiration[idle  expiry timeout] equivalent to double the session's timeout. 
+  This should be sufficient to ensure that a session id that is in-use by a session is never accidentally removed. 
+  However, should you wish to, you can configure this to any integral value to effectively increase the http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_expiration[idle expiry] timeout.
+
+===== Configuring the InfinispanSessionManager
+
+As mentioned elsewhere, there should be one `InfinispanSessionManager` per context (e.g. webapp). 
+It will need to reference the single `InfinispanSessionIdManager` configured previously for the Server.
+
+The way you configure a `InfinispanSessionManager` depends on whether you're configuring from a context xml file, a `jetty-web.xml` file or code. 
+The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
+
+From a context xml file, you reference the Server instance as a Ref:
+
+[source, xml, subs="{sub-order}"]
+----
+  <!-- Expose the jetty infinispan classes for session serialization -->
+  <Call name="prependServerClass">
+    <Arg>-org.eclipse.jetty.session.infinispan.</Arg>
+  </Call>
+
+  
+  <!-- Get a reference to the InfinispanSessionIdManager -->
+  <Ref id="Server">
+    <Call id="idMgr" name="getSessionIdManager"/>
+  </Ref>
+
+  <!-- Get a referencee to the Cache from the InfinispanSessionIdManager -->
+  <Ref id="idMgr">
+    <Get id="cache" name="cache"/>
+  </Ref>
+
+  <!-- Use the InfinispanSessionIdManager and Cache to setup up the InfinispanSessionManager -->
+  <Set name="sessionHandler">
+    <New class="org.eclipse.jetty.server.session.SessionHandler">
+      <Arg>
+        <New id="mgr" class="org.eclipse.jetty.session.infinispan.InfinispanSessionManager">
+          <Set name="sessionIdManager">
+            <Ref id="idMgr"/>
+          </Set>
+          <Set name="cache">
+            <Ref id="cache">
+            </Ref>
+          </Set>
+          <Set name="scavengeInterval">60</Set>
+        </New>
+      </Arg>
+    </New>
+  </Set>
+----
+
+From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
+
+[source, xml, subs="{sub-order}"]
+----
+  <!-- Expose the jetty infinispan classes for session serialization -->
+  <Call name="prependServerClass">
+    <Arg>-org.eclipse.jetty.session.infinispan.</Arg>
+  </Call>
+
+<!-- Reference the server directly -->
+<Get name="server">
+  <Get id="idMgr" name="sessionIdManager"/>
+</Get>
+
+<!-- Get a reference to the Cache via the InfinispanSessionIdManager -->
+ <Ref id="idMgr">
+    <Get id="cache" name="cache"/>
+  </Ref>
+
+<!-- Apply the SessionIdManager and Cache to the InfinispanSessionManager -->
+<Set name="sessionHandler">
+  <New class="org.eclipse.jetty.server.session.SessionHandler">
+     <Arg>
+        <New id="mgr" class="org.eclipse.jetty.session.infinispan.InfinispanSessionManager">
+          <Set name="sessionIdManager">
+            <Ref id="idMgr"/>
+          </Set>
+          <Set name="cache">
+            <Ref id="cache">
+            </Ref>
+          </Set>
+          <Set name="scavengeInterval">600</Set>
+        </New>
+      </Arg>
+  </New>
+</Set>
+----
+
+The InfinispanSessionManager can be provided by calling setters:
+
+scavengeInterval::
+  Time in seconds between runs of a scavenger task that looks for expired old sessions to delete. 
+  The default is 10 minutes.
+staleIntervalSec::
+  The length of time a session can be in memory without being checked against the cluster. 
+  A value of 0 indicates that the session is never checked against the cluster - the current node is considered to be the master for the session.
+
+===== Using HotRod
+
+If you're using the hotrod client - where serialization will be required - you will need to ensure that the hotrod marshalling software works with Jetty classloading. 
+To do this, firstly ensure that you have included the lines containing the `prependServerClass` to your context xml file as shown above.
+
+Then, create the file `${jetty.base}/resources/hotrod-client.properties`. 
+Add the following line to this file:
+
+....
+infinispan.client.hotrod.marshaller=org.eclipse.jetty.session.infinispan.WebAppMarshaller
+....
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-clustering-jdbc.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-clustering-jdbc.adoc
new file mode 100644
index 0000000..88258ba
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-clustering-jdbc.adoc
@@ -0,0 +1,226 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[session-clustering-jdbc]]
+=== Session Clustering with a Database
+
+Jetty can support session clustering by persisting sessions to a shared database. 
+Each Jetty instance locally caches sessions for which it has received requests, writing any changes to the session through to the database as the request exits the server. 
+Sessions must obey the Serialization contract, and servlets must call the `Session.setAttribute()` method to ensure that changes are persisted.
+
+The persistent session mechanism works in conjunction with a load balancer that supports stickiness. 
+Stickiness can be based on various data items, such as source IP address or characteristics of the session ID or a load-balancer specific mechanism. 
+For those load balancers that examine the session ID, the Jetty persistent session mechanism appends a node ID to the session ID, which can be used for routing.
+
+In this type of solution, the database can become both a bottleneck and a single point of failure. 
+Jetty takes steps to reduce the load on the database (discussed below), but in a heavily loaded environment you might need to investigate other optimization strategies such as local caching and database replication. 
+You should also consult your database vendor's documentation for information on how to ensure high availability and failover of your database.
+
+==== Configuration
+
+There are two components to session management in Jetty: a session ID manager and a session manager.
+
+* The session ID manager ensures that session IDs are unique across all webapps hosted on a Jetty instance, and thus there can only be one session ID manager per Jetty instance.
+* The session manager handles the session lifecycle (create/update/invalidate/expire) on behalf of a web application, so there is one session manager per web application instance.
+
+These managers also cooperate and collaborate with the `org.eclipse.jetty.server.session.SessionHandler` to enable cross-context dispatch.
+
+==== The jdbc-session Module
+
+When using the jetty distribution, to enable jdbc session persistence, you will first need to enable the jdbc-session link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` or `--add-to-startd` argument to the link:#startup-overview[start.jar].
+
+You will also find the following properties, either in your base's start.d/jdbc-session.ini file or appended to your start.ini, depending on how you enabled the module:
+
+[source, java, subs="{sub-order}"]
+----
+## Unique identifier for this node in the cluster
+jetty.jdbcSession.workerName=node1
+
+##Uncomment either the datasource name or driverClass and connectionURL
+#jetty.jdbcSession.datasource=sessions
+jetty.jdbcSession.driverClass=org.apache.derby.jdbc.EmbeddedDriver
+jetty.jdbcSession.connectionURL=jdbc:derby:sessions;create=true
+----
+
+jetty.jdbcSession.workerName::
+  The name that uniquely identifies this node in the cluster. 
+  This value will also be used by the sticky load balancer to identify the node.
+  Don't forget to change the value of this property on *each* node on which you enable jdbc session clustering.
+jetty.jdbcSession.scavenge::
+  The time in seconds between sweeps of a task which scavenges old expired sessions. 
+  The default is 10 minutess. 
+  Increasing the frequency is not recommended as doing so increases the load on the database with very little gain.
+jetty.jdbcSession.datasource::
+  The name of a `javax.sql.DataSource` that gives access to the database that holds the session information. 
+  You should configure *either* this or the jdbc driver information described next.
+jetty.jdbcSession.datasource and jetty.jdbcSession.connectionURL::
+  This is the name of the jdbc driver class, and a jdbc connection url suitable for that driver. 
+  You should configure *either* this or the jdbc datasource name described above.
+
+These properties are applied to the `JDBCSessionIdManager` described below.
+
+===== Configuring the JDBCSessionIdManager
+
+The jdbc-session module will have installed file called `$\{jetty.home}/etc/jetty-jdbc-sessions.xml`. 
+This file configures an instance of the `JDBCSessionIdManager` that will be shared across all webapps deployed on that server. 
+It looks like this:
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-server/src/main/config/etc/jetty-jdbc-sessions.xml[]
+----
+
+As well as uncommenting and setting up appropriate values for the properties discussed above, you will also need to edit this file and uncomment *either* the data source or the driver info elements.
+
+As Jetty configuration files are direct mappings of XML to Java, it is straight forward to do this in code:
+
+[source, java, subs="{sub-order}"]
+----    
+Server server = new Server();
+     ...
+JDBCSessionIdManager idMgr = new JDBCSessionIdManager(server);
+idMgr.setWorkerName("node1");
+idMgr.setDriverInfo("com.mysql.jdbc.Driver", "jdbc:mysql://127.0.0.1:3306/sessions?user=janb");
+idMgr.setScavengeInterval(600);
+server.setSessionIdManager(idMgr);
+ 
+----
+
+====== Configuring the Database Schema
+
+You may find it necessary to change the names of the tables and columns that the JDBC Session management uses to store the session information.
+The defaults used are:
+
+.Default Values for Session Id Table
+[options="header"]
+|===========================
+|table name |JettySessionIds
+|columns |id 
+|===========================
+
+.Default Values for Session Table
+[options="header"]
+|=======================================================================
+|table name |JettySessions
+
+|columns |rowId, sessionId, contextPath, virtualHost, lastNode,
+accessTime, lastAccessTime, createTime, cookieTime, lastSavedTime,
+expiryTime, maxInterval, map
+|=======================================================================
+
+To change these values, use the link:{JDURL}/org/eclipse/jetty/server/session/SessionIdTableSchema.html[org.eclipse.jetty.server.session.SessionIdTableSchema] and link:{JDURL}/org/eclipse/jetty/server/session/SessionTableSchema.html[org.eclipse.jetty.server.session.SessionTableSchema] classes. 
+These classes have getter/setter methods for the table name and all columns.
+
+Here's an example of changing the name of `JettySessionsId` table and its single column. 
+This example will use java code, but as explained above, you may also do this via a Jetty xml configuration file:
+
+[source, java, subs="{sub-order}"]
+----
+JDBCSessionIdManager idManager = new JDBCSessionIdManager(server);
+
+SessionIdTableSchema idTableSchema = new SessionIdTableSchema();
+idTableSchema.setTableName("mysessionids");
+idTableSchema.setIdColumn("theid");
+idManager.setSessionIdTableSchema(idTableSchema);
+----
+
+In a similar fashion, you can change the names of the table and columns for the `JettySessions` table. 
+*Note* that both the `SessionIdTableSchema` and the `SessionTableSchema` instances are set on the `JDBCSessionIdManager` class.
+
+[source, java, subs="{sub-order}"]
+----
+JDBCSessionIdManager idManager = new JDBCSessionIdManager(server);
+
+SessionTableSchema sessionTableSchema = new SessionTableSchema();
+sessionTableSchema.setTableName("mysessions");
+sessionTableSchema.setIdColumn("mysessionid");
+sessionTableSchema.setAccessTimeColumn("atime");
+sessionTableSchema.setContextPathColumn("cpath");
+sessionTableSchema.setCookieTimeColumn("cooktime");
+sessionTableSchema.setCreateTimeColumn("ctime");
+sessionTableSchema.setExpiryTimeColumn("extime");
+sessionTableSchema.setLastAccessTimeColumn("latime");
+sessionTableSchema.setLastNodeColumn("lnode");
+sessionTableSchema.setLastSavedTimeColumn("lstime");
+sessionTableSchema.setMapColumn("mo");
+sessionTableSchema.setMaxIntervalColumn("mi");           
+idManager.setSessionTableSchema(sessionTableSchema);
+----
+
+===== Configuring the JDBCSessionManager
+
+As mentioned elsewhere, there should be one `JDBCSessionManager` per context (e.g. webapp). 
+It will need to reference the single `JDBCSessionIdManager` configured previously for the Server.
+
+The way you configure a `JDBCSessionManager` depends on whether you're configuring from a context xml file, a `jetty-web.xml` file or code.
+The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
+
+From a context xml file, you reference the Server instance as a Ref:
+
+[source, xml, subs="{sub-order}"]
+----
+  <Ref id="Server">
+    <Call id="idMgr" name="getSessionIdManager"/>
+  </Ref>
+  <Set name="sessionHandler">
+    <New class="org.eclipse.jetty.server.session.SessionHandler">
+      <Arg>
+        <New id="jdbcmgr" class="org.eclipse.jetty.server.session.JDBCSessionManager">
+          <Set name="sessionIdManager">
+            <Ref id="idMgr"/>
+          </Set>
+        </New>
+      </Arg>
+    </New>
+  </Set>
+----
+
+From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
+
+[source, xml, subs="{sub-order}"]
+----
+  
+<Get name="server">
+  <Get id="idMgr" name="sessionIdManager"/>
+</Get>
+<Set name="sessionHandler">
+  <New class="org.eclipse.jetty.server.session.SessionHandler">
+    <Arg>
+      <New class="org.eclipse.jetty.server.session.JDBCSessionManager">
+        <Set name="sessionIdManager">
+          <Ref id="idMgr"/>
+        </Set>
+      </New>
+    </Arg>
+  </New>
+</Set>
+----
+
+If you're embedding this in code:
+
+[source, java, subs="{sub-order}"]
+----
+
+//assuming you have already set up the JDBCSessionIdManager as shown earlier
+//and have a reference to the Server instance:
+ 
+WebAppContext wac = new WebAppContext();
+ ... //configure your webapp context
+JDBCSessionManager jdbcMgr = new JDBCSessionManager();
+jdbcMgr.setSessionIdManager(server.getSessionIdManager());
+SessionHandler sessionHandler = new SessionHandler(jdbcMgr);
+wac.setSessionHandler(sessionHandler);
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-clustering-mongodb.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-clustering-mongodb.adoc
new file mode 100644
index 0000000..c8aeef7
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-clustering-mongodb.adoc
@@ -0,0 +1,238 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[session-clustering-mongodb]]
+=== Session Clustering with MongoDB
+
+Jetty can support session clustering by persisting sessions into http://www.mogodb.org[MongoDB]. 
+Each Jetty instance locally caches sessions for which it has received requests, writing any changes to the session through to the cluster as the request exits the server. 
+Sessions must obey the Serialization contract, and servlets must call the `Session.setAttribute()` method to ensure that changes are persisted.
+
+The session persistence mechanism works in conjunction with a load balancer that supports stickiness. 
+Stickiness can be based on various data items, such as source IP address or characteristics of the session ID or a load-balancer specific mechanism. 
+For those load balancers that examine the session ID, the Jetty persistent session mechanism appends a node ID to the session ID, which can be used for routing.
+
+In this type of solution, the traffic on the network needs to be carefully watched and tends to be the bottleneck. 
+You are probably investigating this solution in order to scale to large amount of users and sessions, so careful attention should be paid to your usage scenario. 
+Applications with a heavy write profile to their sessions will consume more network bandwidth than profiles that are predominately read oriented. 
+We recommend using this session manager with largely read based session scenarios.
+
+==== Configuration
+
+There are two components to session management in Jetty: a session ID manager and a session manager.
+
+* The session ID manager ensures that session IDs are unique across all webapps hosted on a Jetty instance, and thus there can only be one session ID manager per Jetty instance.
+* The session manager handles the session lifecycle (create/update/invalidate/expire) on behalf of a web application, so there is one session manager per web application instance.
+
+These managers also cooperate and collaborate with the `org.eclipse.jetty.server.session.SessionHandler` to enable cross-context dispatch.
+
+==== The nosql Module
+
+When using the jetty distribution, to enable the MongoDB session persistence mechanism, you will first need to enable the nosql link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` or `--add-to-startd` argument to the link:#startup-overview[start.jar]. 
+This module will automatically download the `mongodb-java-driver` and install it to your base's `lib/nosql` directory.
+
+As part of the module installation, the necessary mongo java driver jars will be dynamically downloaded and installed to your `${jetty.base}/lib/nosql` directory. 
+If you need to up or downgrade the version of these jars, then you can delete the jars that were automatically installed and replace them. 
+Once you've done that, you will need to prevent Jetty's startup checks from detecting the missing jars. 
+To do that, you can use `--skip-file-validation=nosql` argument to start.jar on the command line, or place that line inside `${jetty.base}/start.ini` to ensure it is used for every start.
+
+You will also find the following properties, either in your base's `start.d/nosql.ini` file or appended to your `start.ini`, depending on how you enabled the module:
+
+[source, xml, subs="{sub-order}"]
+----
+## Unique identifier for this node in the cluster
+jetty.nosqlSession.workerName=node1
+
+
+## Interval in seconds between scavenging expired sessions
+jetty.nosqlSession.scavenge=1800
+----
+
+The `jetty.nosqlSession.workerName` is the unique name for this Jetty Server instance. 
+It will be used by the sticky load balancer to uniquely identify the node. 
+You should change this value on *each* node to which you install MongoDB session management.
+
+The `jetty.nosqlSession.scavenge` property defines the time in seconds between runs of the scavenger: the scavenger is a task which runs periodically to clean out sessions that have expired but become stranded in the database for whatever reason.
+
+These properties are substituted into the configuration of the `MongoDBSessionIdManager` and `MongoSessionManager`.
+
+===== Configuring the MongoSessionIdManager
+
+The nosql module will have installed file called `$\{jetty.home}/etc/jetty-nosql.xml`. 
+This file configures an instance of the `MongoSessionIdManager` that will be shared across all webapps deployed on that server. 
+It looks like this:
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-nosql/src/main/config/etc/jetty-nosql.xml[]
+----
+
+The `MongoSessionIdManager` needs access to a MongoDB cluster, and the `jetty-nosql.xml` file assumes the defaults of localhost and default MongoDB port. 
+If you need to configure something else, you will need to edit this file. 
+Here's an example of a more complex setup to use a remote MongoDB instance:
+
+[source, xml, subs="{sub-order}"]
+----
+    <New id="mongodb" class="com.mongodb.Mongo">
+      <Arg>
+        <New class="java.util.ArrayList">
+          <Call name="add">
+            <Arg>
+              <New class="com.mongodb.ServerAddress">
+                <Arg type="java.lang.String">foo.example.com</Arg>
+                <Arg type="int">27017</Arg>
+              </New>
+            </Arg>
+          </Call>
+          <!-- Add more Call statements here as desired --> </New>
+      </Arg>
+
+      <Call name="getDB">
+        <Arg>HttpSessions</Arg>
+        <Call id="sessionDocument" name="getCollection">
+          <Arg>sessions</Arg>
+        </Call>
+      </Call>
+      <!-- If you want to configure Jetty to be able to read through the slaves, call the following: -->
+      <Call name="slaveOk"/>
+    </New>
+
+    <Set name="sessionIdManager">
+      <New id="mongoIdMgr" class="org.eclipse.jetty.nosql.mongodb.MongoSessionIdManager">
+        <Arg>
+          <Ref id="Server"/>
+        </Arg>
+        <Arg>
+          <Ref id="sessionDocument"/>
+        </Arg>
+        <Set name="workerName"><Property name="jetty.nosqlSession.workerName" default="node1"/></Set>
+        <Set name="scavengePeriod"><Property name="jetty.nosqlSession.scavenge" default="1800"/></Set>
+      </New>
+    </Set>
+    
+  
+----
+
+As Jetty configuration files are direct mappings of XML to Java, it is straight forward to do this in code:
+
+[source, java, subs="{sub-order}"]
+----
+    
+ Server server = new Server();
+     ...
+ MongoSessionIdManager idMgr = newMongoSessionIdManager(server);
+ idMgr.setWorkerName("node1");
+ idMgr.setScavengePeriod(1800);
+ server.setSessionIdManager(idMgr);
+  
+----
+
+The MongoSessionIdManager has slightly different options than some of our more traditional session options. 
+The `MongoDBSessionIdManager` has the same scavenge timers which govern the setting of a valid session to invalid after a certain period of inactivity. 
+New to this session id manager is the extra purge setting which governs removal from the MongoDB cluster. 
+This can be configured through the 'purge' option. Purge is by default set to true and by default runs daily for each node on the cluster. 
+Also able to be configured is the age in which an invalid session will be retained which is set to 1 day by default. 
+This means that invalid sessions will be removed after lingering in the MongoDB instance for a day. 
+There is also an option for purging valid sessions that have not been used recently. 
+The default time for this is 1 week. You can disable these behaviors by setting purge to false.
+
+scavengeDelay::
+  How long to delay before periodically looking for sessions to scavenge?
+scavengePeriod::
+  How much time after a scavenge has completed should you wait before doing it again?
+scavengeBlockSize::
+  Number of session ids to which to limit each scavenge query. 
+  If you have a very large number of sessions in memory then setting this to a non 0 value may help speed up scavenging by breaking the scavenge into multiple, queries. 
+  The default is 0, which means that all session ids are considered in a single query.
+purge (Boolean)::
+  Do you want to purge (delete) sessions that are invalid from the session store completely?
+purgeDelay::
+  How often do you want to perform this purge operation?
+purgeInvalidAge::
+  How old should an invalid session be before it is eligible to be purged?
+purgeValidAge::
+  How old should a valid session be before it is eligible to be marked invalid and purged? 
+  Should this occur at all?
+purgeLimit::
+  Integer value that represents how many items to return from a purge query. 
+  The default is 0, which is unlimited. 
+  If you have a lot of old  expired orphaned sessions then setting this value may speed up the purge process.
+preserveOnStop::
+  Whether or not to retain all sessions when the session manager stops.
+  Default is `true`.
+
+===== Configuring a MongoSessionManager
+
+As mentioned elsewhere, there should be one `MongoSessionManager` per context (e.g. webapp). 
+It will need to reference the single `MongoSessionIdManager` configured previously for the Server.
+
+The way you configure a link:{JDURL}/org/eclipse/jetty/nosql/MongoSessionManager.html[org.eclipse.jetty.nosql.mongodb.MongoSessionManager] depends on whether you're configuring from a link:#deployable-descriptor-file[context xml] file or a link:#jetty-web-xml-config[jetty-web.xml] file or code. 
+The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
+
+From a context xml file, you reference the Server instance as a Ref:
+
+[source, xml, subs="{sub-order}"]
+----  
+<Ref name="Server" id="Server">
+    <Call id="mongoIdMgr" name="getSessionIdManager"/>
+  </Ref>
+
+  <Set name="sessionHandler">
+    <New class="org.eclipse.jetty.server.session.SessionHandler">
+      <Arg>
+        <New id="mongoMgr" class="org.eclipse.jetty.nosql.mongodb.MongoSessionManager">
+          <Set name="sessionIdManager">
+            <Ref id="mongoIdMgr"/>
+          </Set>
+        </New>
+      </Arg>
+    </New>
+  </Set>
+----
+
+From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
+
+[source, xml, subs="{sub-order}"]
+----
+<Get name="server">
+  <Get id="mongoIdMgr" name="sessionIdManager"/>
+</Get>
+<Set name="sessionHandler">
+  <New class="org.eclipse.jetty.server.session.SessionHandler">
+    <Arg>
+      <New class="org.eclipse.jetty.nosql.mongodb.MongoSessionManager">
+        <Set name="sessionIdManager">
+          <Ref id="mongoIdMgr"/>
+        </Set>
+      </New>
+    </Arg>
+  </New>
+</Set>
+----
+
+If you're embedding this in code:
+
+[source, java, subs="{sub-order}"]
+----
+//assuming you have already set up the MongoSessionIdManager as shown earlier
+ //and have a reference to the Server instance:
+ 
+ WebAppContext wac = new WebAppContext();
+  ... //configure your webapp context
+ MongoSessionManager mongoMgr = new MongoSessionManager();
+ mongoMgr.setSessionIdManager(server.getSessionIdManager());
+ wac.setSessionHandler(new SessionHandler(mongoMgr));
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/setting-session-characteristics.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/setting-session-characteristics.adoc
new file mode 100644
index 0000000..1da8971
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/setting-session-characteristics.adoc
@@ -0,0 +1,279 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[setting-session-characteristics]]
+=== Setting Session Characteristics
+
+Sessions are a concept within the Servlet api which allow requests to store and retrieve information across the time a user spends in an application.  
+Choosing the correct session manager implementation is an important consideration for every application as each can fit and perform optimally in different situations.
+If you need a simple in-memory session manager that can persist to disk then the `HashSessionManager` can be a good place to start.  
+If you need a session manager that can work in a clustered scenario with multiple instances of Jetty, then the JDBC session manager can be an excellent option.
+Jetty also offers more niche session managers that leverage backends such as MongoDB, Inifinispan, or even Google's Cloud Data Store.
+
+To modify the session characteristics of a web application, you can use the following parameters, applying them as in one of the example configurations:
+
+[[using-init-parameters]]
+==== Using Init Parameters
+
+Use these parameters to set session characteristics.
+
+.Init Parameters
+[cols=",,",options="header",]
+|=======================================================================
+|Context Parameter |Default Value |Description
+|org.eclipse.jetty.servlet.SessionCookie |JSESSIONID |Session cookie
+name defaults to JSESSIONID, but can be set for a particular webapp with
+this context param.
+
+|org.eclipse.jetty.servlet.SessionIdPathParameterName |jsessionid
+|Session URL parameter name. Defaults to jsessionid, but can be set for
+a particular webapp with this context param. Set to "none" to disable
+URL rewriting.
+
+|org.eclipse.jetty.servlet.SessionDomain |- |Session Domain. If this
+property is set as a ServletContext param, then it is used as the domain
+for session cookies.If it is not set, then no domain is specified for
+the session cookie.
+
+|org.eclipse.jetty.servlet.SessionPath |- |Session Path. If this
+property is set as a ServletContext param, 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.
+
+|org.eclipse.jetty.servlet.MaxAge |-1 |Session Max Age. If this property
+is set as a ServletContext param, 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.
+
+|org.eclipse.jetty.servlet.CheckingRemoteSessionIdEncoding |false |If
+true, Jetty will add JSESSIONID parameter even when encoding external
+urls with calls to encodeURL(). False by default.
+|=======================================================================
+
+[[applying-init-parameters]]
+===== Applying Init Parameters
+
+The following sections provide examples of how to apply the init parameters.
+
+[[context-parameter-example]]
+====== Context Parameter Example
+
+You can set these parameters as context parameters in a web application's `WEB-INF/web.xml` file:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app
+  xmlns="http://java.sun.com/xml/ns/javaee"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+  version="2.5">
+  ...
+  <context-param>
+    <param-name>org.eclipse.jetty.servlet.SessionCookie</param-name>
+    <param-value>XSESSIONID</param-value>
+  </context-param>
+  <context-param>
+    <param-name>org.eclipse.jetty.servlet.SessionIdPathParameterName</param-name>
+    <param-value>xsessionid</param-value>
+  </context-param>
+  ...
+</web-app>
+
+        
+----
+
+[[web-application-examples]]
+====== Web Application Examples
+
+You can configure init parameters on a web application, either in code, or in a Jetty context xml file equivalent:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/test</Set>
+  <Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/test</Set>
+ 
+  ...
+ 
+  <Call name="setInitParameter">
+        <Arg>org.eclipse.jetty.servlet.SessionCookie</Arg>
+        <Arg>XSESSIONID</Arg>
+  </Call>
+  <Call name="setInitParameter">
+        <Arg>org.eclipse.jetty.servlet.SessionIdPathParameterName</Arg>
+        <Arg>xsessionid</Arg>
+  </Call>
+</Configure>
+
+        
+----
+
+[[init-parameter-examples]]
+====== SessionManager Examples
+
+You can configure init parameters directly on a `SessionManager` instance, either in code or the equivalent in xml:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/test</Set>
+  <Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/test</Set>
+ 
+   ...
+
+  <Get name="sessionHandler">
+     <Set name="sessionManager">
+         <New class="org.eclipse.jetty.server.session.HashSessionManager">
+            <Set name="sessionCookie">XSESSIONID</Set>
+            <Set name="sessionIdPathParameterName">xsessionid</Set>
+         </New>
+     </Set>
+  </Get>
+</Configure>
+
+        
+----
+
+==== Using Servlet 3.0 Session Configuration
+
+With the advent of http://jcp.org/en/jsr/detail?id=315[Servlet Specification 3.0] there are new APIs for configuring session handling characteristics. 
+What was achievable before only via Jetty-specific link:#session-init-params[init-parameters] can now be achieved in a container-agnostic manner either in code, or via `web.xml`.
+
+[[session-cookie-configuration]]
+===== SessionCookieConfiguration
+
+The http://docs.oracle.com/javaee/6/api/javax/servlet/SessionCookieConfig.html[javax.servlet.SessionCookieConfig] class can be used to set up session handling characteristics. 
+For full details, consult the http://docs.oracle.com/javaee/6/api/javax/servlet/SessionCookieConfig.html[javadoc].
+
+Below is an example of this implementation: a `ServletContextListener` retrieves the `SessionCookieConfig` and sets up some new values when the context is being initialized:
+
+[source, java, subs="{sub-order}"]
+----
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+public class TestListener implements ServletContextListener 
+{
+
+    public void contextInitialized(ServletContextEvent sce) 
+    {
+        String comment = "This is my special cookie configuration";
+        String domain = "foo.com";
+        String path = "/my/special/path";
+        boolean isSecure = true;
+        boolean httpOnly = false;
+        int maxAge = 30000;
+        String cookieName = "FOO_SESSION";
+
+
+        SessionCookieConfig scf = sce.getServletContext().getSessionCookieConfig();
+
+        scf.setComment(comment);
+        scf.setDomain(domain);
+        scf.setHttpOnly(httpOnly);
+        scf.setMaxAge(maxAge);
+        scf.setPath(path);
+        scf.setSecure(isSecure);
+        scf.setName(cookieName);
+    }
+
+    public void contextDestroyed(ServletContextEvent sce) 
+    {
+
+    }
+}
+----
+
+You can also use `web.xml` to configure the session handling characteristics instead: here's an example doing exactly the same as above instead of using code:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app
+   xmlns="http://java.sun.com/xml/ns/javaee"
+   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+   metadata-complete="true"
+   version="3.0">
+
+   <session-config>
+      <cookie-config>
+         <comment>This is my special cookie configuration</comment>
+         <domain>foo.com</domain>
+         <http-only>false</http-only>
+         <max-age>30000</max-age>
+         <path>/my/special/path</path>
+         <secure>true</secure>
+         <name>FOO_SESSION</name>
+      </cookie-config>
+   </session-config>
+</web-app>
+----
+
+[[session-tracking-modes]]
+===== SessionTrackingModes
+
+In addition to the configuration of link:#session-cookie-configuration[session cookies], since Servlet 3.0 you can also use the http://docs.oracle.com/javaee/6/api/javax/servlet/SessionTrackingMode.html[javax.servlet.SessionTrackingMode] to configure session tracking.
+
+To determine what are the _default_ session tracking characteristics used by the container, call:
+
+[source, java, subs="{sub-order}"]
+----
+javax.servlet.SessionContext.getDefaultSessionTrackingModes();
+----
+
+This returns a java.util.Set of javax.servlet.SessionTrackingMode. The
+_default_ session tracking modes for Jetty are:
+
+* http://docs.oracle.com/javaee/6/api/javax/servlet/SessionTrackingMode.html#COOKIE[SessionTrackingMode.COOKIE]
+* http://docs.oracle.com/javaee/6/api/javax/servlet/SessionTrackingMode.html#URL[SessionTrackingMode.URL]
+
+To see which session tracking modes are actually in effect for this Context, the following call returns a `java.util.Set` of `javax.servlet.SessionTrackingMode`:
+
+[source, java, subs="{sub-order}"]
+----
+javax.servlet.SessionContext.getEffectiveSessionTrackingModes();
+----
+
+To change the session tracking modes, call:
+
+[source, java, subs="{sub-order}"]
+----
+javax.servlet.SessionContext.setSessionTrackingModes(Set<SessionTrackingMode>);
+----
+
+You may also set the tracking mode in `web.xml`, e.g.:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app
+   xmlns="http://java.sun.com/xml/ns/javaee"
+   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+   metadata-complete="true"
+   version="3.0">
+
+   <session-config>
+      <tracking-mode>URL</tracking-mode>
+      <tracking-mode>COOKIE</tracking-mode>
+   </session-config>
+</web-app>
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/using-persistent-sessions.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/using-persistent-sessions.adoc
new file mode 100644
index 0000000..35b88be
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/using-persistent-sessions.adoc
@@ -0,0 +1,115 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[using-persistent-sessions]]
+=== Using Persistent Sessions
+
+It is sometimes useful to preserve existing Sessions across restarts of Jetty. 
+The link:{JDURL}/org/eclipse/jetty/server/session/HashSessionManager.html[`HashSessionManager`] supports this feature. 
+If you enable persistence, the `HashSessionManager` saves all existing, valid Sessions to disk before shutdown completes. 
+On restart, Jetty restores the saved Sessions.
+
+[[enabling-persistence]]
+==== Enabling Persistence
+
+A `SessionManager` does just what its name suggests – it manages the lifecycle and state of sessions on behalf of a webapp. 
+Each webapp must have its own unique `SessionManager` instance. 
+Enabling persistence is as simple as configuring the `HashSessionManager` as the `SessionManager` for a webapp and telling it where on disk to store the sessions:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  .
+  .
+  .
+  <Set name="sessionHandler">
+    <New class="org.eclipse.jetty.server.session.SessionHandler">
+      <Arg>
+        <New class="org.eclipse.jetty.server.session.HashSessionManager">
+          <Set name="storeDirectory">your/chosen/directory/goes/here</Set>
+        </New>
+      </Arg>
+    </New>
+  </Set>
+  .
+  .
+  .
+</Configure>      
+
+      
+----
+
+The above uses an example of a xref:intro-jetty-configuration-contexts[context configuration file].
+
+[TIP]
+====
+If you want to persist the sessions from multiple webapps: 
+
+1.  Configure a separate `HashSessionManager` for each.
+  
+2.  Assign to each a different value for `storeDirectory`.
+====
+
+[[delaying-session-load]]
+==== Delaying Session Load
+
+You might need to ensure that the sessions are loaded AFTER the servlet environment starts up (by default, Jetty eagerly loads sessions as part of the container startup, but before it initializes the servlet environment). 
+For example, the Wicket web framework requires the servlet environment to be available when sessions are activated.
+
+Using `SessionManager.setLazyLoad(true)`, Jetty loads sessions lazily either when it receives the first request for a session, or the session scavenger runs for the first time, whichever happens first. 
+Here's how the configuration looks in XML:
+
+[source, xml, subs="{sub-order}"]
+----
+<Set name="sessionHandler">
+  <New class="org.eclipse.jetty.server.session.SessionHandler">
+    <Arg>
+      <New class="org.eclipse.jetty.server.session.HashSessionManager">
+        <Set name="lazyLoad">true</Set>
+      </New>
+    </Arg>
+  </New>
+</Set>      
+----
+
+[[enabling-persistence-for-jetty-maven-plugin]]
+==== Enabling Persistence for the Jetty Maven Plugin
+
+To enable session persistence for the Jetty Maven plugin, set up the `HashSessionManager` in the configuration section like so:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <version>9.0.0.RC2 (or current version)</version>
+  <configuration>
+    <!-- ... -->
+    <webAppConfig implementation="org.eclipse.jetty.maven.plugin.JettyWebAppContext">
+      <defaultsDescriptor>${project.build.outputDirectory}/META-INF/webdefault.xml</defaultsDescriptor>
+      <contextPath>${jetty.contextRoot}</contextPath>
+      <sessionHandler implementation="org.eclipse.jetty.server.session.SessionHandler">
+        <sessionManager implementation="org.eclipse.jetty.server.session.HashSessionManager">
+          <storeDirectory>${project.basedir}/target/jetty-sessions</storeDirectory>
+          <idleSavePeriod>1</idleSavePeriod>
+        </sessionManager>
+      </sessionHandler>
+    </webAppConfig>
+    <!-- ... -->
+  </configuration>
+</plugin>      
+----
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/chapter.adoc
new file mode 100644
index 0000000..afe0226
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/chapter.adoc
@@ -0,0 +1,27 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[startup]]
+== Starting Jetty
+
+include::startup-overview.adoc[]
+include::start-jar.adoc[]
+include::startup-base-vs-home.adoc[]
+include::startup-xml-config.adoc[]
+include::startup-classpath.adoc[]
+include::startup-modules.adoc[]
+include::startup-unix-service.adoc[]
+include::startup-windows-service.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/images/modules-9.3-simplified.dot b/jetty-documentation/src/main/asciidoc/administration/startup/images/modules-9.3-simplified.dot
new file mode 100644
index 0000000..ecc2962
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/images/modules-9.3-simplified.dot
@@ -0,0 +1,254 @@
+/*
+ * GraphViz Graph of Jetty Modules
+ * 
+ * Jetty: http://eclipse.org/jetty/
+ * GraphViz: http://graphviz.org/
+ * 
+ * To Generate Graph image using graphviz:
+ *   $ dot -Tpng -Goverlap=false -o modules-9.png modules-9.3.dot
+ */
+
+digraph modules {
+  node [color=gray, style=filled, shape=rectangle];
+  node [fontname="Verdana", size="20,20"];
+  graph [
+    concentrate=false,
+    fontname="Verdana",
+    fontsize = 20,
+    rankdir = LR,
+    ranksep = 1.5,
+    nodesep = .5,
+    style = bold,
+    labeljust = l,
+    label = "Jetty Modules",
+    ssize = "20,40"
+  ];
+
+  /* Modules */
+
+  node [ labeljust = l ];
+
+  /* Level 0 */
+  { rank = same;
+  "server" [ color="#66FFCC" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>server</B></TD></TR>
+</TABLE>>];
+  "ext" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>ext</B></TD></TR>
+</TABLE>>];
+  "jvm" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>jvm</B></TD></TR>
+</TABLE>>];
+  "apache-jstl" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>apache-jstl</B></TD></TR>
+</TABLE>>];
+  "client" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>client</B></TD></TR>
+</TABLE>>];
+  "logging" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>logging</B></TD></TR>
+</TABLE>>];
+  "resources" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>resources</B></TD></TR>
+</TABLE>>];
+  "apache-jsp" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>apache-jsp</B></TD></TR>
+</TABLE>>];
+  "protonego-boot" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>protonego-boot</B></TD></TR>
+</TABLE>>];
+  }
+
+  /* Level 1 */
+  { rank = same;
+  "requestlog" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>requestlog</B></TD></TR>
+</TABLE>>];
+  "servlet" [ color="#66FFCC" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>servlet</B></TD></TR>
+</TABLE>>];
+  "gzip" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>gzip</B></TD></TR>
+</TABLE>>];
+  "monitor" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>monitor</B></TD></TR>
+</TABLE>>];
+  "rewrite" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>rewrite</B></TD></TR>
+</TABLE>>];
+  "ssl" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>ssl</B></TD></TR>
+</TABLE>>];
+  "security" [ color="#66FFCC" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>security</B></TD></TR>
+</TABLE>>];
+  "setuid" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>setuid</B></TD></TR>
+</TABLE>>];
+  "spring" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>spring</B></TD></TR>
+</TABLE>>];
+  "stats" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>stats</B></TD></TR>
+</TABLE>>];
+  "jmx" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>jmx</B></TD></TR>
+</TABLE>>];
+  "http" [ color="#66FFCC" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>http</B></TD></TR>
+</TABLE>>];
+  "debuglog" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>debuglog</B></TD></TR>
+</TABLE>>];
+  "ipaccess" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>ipaccess</B></TD></TR>
+</TABLE>>];
+  "jaas" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>jaas</B></TD></TR>
+</TABLE>>];
+  "jndi" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>jndi</B></TD></TR>
+</TABLE>>];
+  "lowresources" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>lowresources</B></TD></TR>
+</TABLE>>];
+  }
+
+  /* Level 2 */
+  { rank = same;
+  "fcgi" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>fcgi</B></TD></TR>
+</TABLE>>];
+  "jmx-remote" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>jmx-remote</B></TD></TR>
+</TABLE>>];
+  "webapp" [ color="#66FFCC" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>webapp</B></TD></TR>
+</TABLE>>];
+  "proxy" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>proxy</B></TD></TR>
+</TABLE>>];
+  "alpn" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>alpn</B></TD></TR>
+</TABLE>>];
+  "jaspi" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>jaspi</B></TD></TR>
+</TABLE>>];
+  "http2c" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>http2c</B></TD></TR>
+</TABLE>>];
+  "https" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>https</B></TD></TR>
+</TABLE>>];
+  "servlets" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>servlets</B></TD></TR>
+</TABLE>>];
+  }
+
+  /* Level 3 */
+  { rank = same;
+  "http2" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>http2</B></TD></TR>
+</TABLE>>];
+  "plus" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>plus</B></TD></TR>
+</TABLE>>];
+  "deploy" [ color="#66FFCC" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>deploy</B></TD></TR>
+</TABLE>>];
+  "nosql" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>nosql</B></TD></TR>
+</TABLE>>];
+  }
+
+  /* Level 4 */
+  { rank = same;
+  "annotations" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>annotations</B></TD></TR>
+</TABLE>>];
+  }
+
+  /* Level 5 */
+  { rank = same;
+  "jdbc-sessions" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>jdbc-sessions</B></TD></TR>
+</TABLE>>];
+  "infinispan" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>infinispan</B></TD></TR>
+</TABLE>>];
+  "quickstart" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>quickstart</B></TD></TR>
+</TABLE>>];
+  "jsp" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>jsp</B></TD></TR>
+</TABLE>>];
+  "websocket" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>websocket</B></TD></TR>
+</TABLE>>];
+  }
+
+  /* Level 6 */
+  { rank = same;
+  "jstl" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>jstl</B></TD></TR>
+</TABLE>>];
+  "cdi" [ color="#B8FFB8" label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2">
+  <TR><TD ALIGN="LEFT"><B>cdi</B></TD></TR>
+  <TR><TD ALIGN="LEFT">(experimental)</TD></TR>
+</TABLE>>];
+  }
+    "alpn" -> "protonego-boot";
+    "annotations" -> "plus";
+    "cdi" -> "deploy";
+    "cdi" -> "annotations";
+    "cdi" -> "jsp";
+    "debuglog" -> "server";
+    "deploy" -> "webapp";
+    "fcgi" -> "servlet";
+    "fcgi" -> "client";
+    "gzip" -> "server";
+    "http" -> "server";
+    "http2" -> "ssl";
+    "http2" -> "alpn";
+    "http2c" -> "http";
+    "https" -> "ssl";
+    "infinispan" -> "annotations";
+    "infinispan" -> "webapp";
+    "ipaccess" -> "server";
+    "jaas" -> "server";
+    "jaspi" -> "security";
+    "jdbc-sessions" -> "annotations";
+    "jdbc-sessions" -> "webapp";
+    "jmx" -> "server";
+    "jmx-remote" -> "jmx";
+    "jndi" -> "server";
+    "jsp" -> "servlet";
+    "jsp" -> "annotations";
+    "jsp" -> "apache-jsp";
+    "jstl" -> "jsp";
+    "jstl" -> "apache-jstl";
+    "lowresources" -> "server";
+    "monitor" -> "server";
+    "monitor" -> "client";
+    "nosql" -> "webapp";
+    "plus" -> "security";
+    "plus" -> "jndi";
+    "plus" -> "webapp";
+    "proxy" -> "servlet";
+    "proxy" -> "client";
+    "quickstart" -> "plus";
+    "quickstart" -> "annotations";
+    "requestlog" -> "server";
+    "rewrite" -> "server";
+    "security" -> "server";
+    "servlet" -> "server";
+    "servlets" -> "servlet";
+    "setuid" -> "server";
+    "spring" -> "server";
+    "ssl" -> "server";
+    "stats" -> "server";
+    "webapp" -> "servlet";
+    "webapp" -> "security";
+    "websocket" -> "annotations";
+}
+
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/images/modules-9.3-simplified.png b/jetty-documentation/src/main/asciidoc/administration/startup/images/modules-9.3-simplified.png
new file mode 100644
index 0000000..bf38325
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/images/modules-9.3-simplified.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/images/windows-service-jetty.png b/jetty-documentation/src/main/asciidoc/administration/startup/images/windows-service-jetty.png
new file mode 100644
index 0000000..c78557b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/images/windows-service-jetty.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/screen-empty-base-listconfig.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/screen-empty-base-listconfig.adoc
new file mode 100644
index 0000000..777484f
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/screen-empty-base-listconfig.adoc
@@ -0,0 +1,67 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[source, screen, subs="{sub-order}"]
+....
+[mybase]$ java -jar /opt/jetty-distribution/start.jar --list-config
+
+Java Environment:
+-----------------
+ java.home = /lib/jvm/jdk-8u92/jre
+ java.vm.vendor = Oracle Corporation
+ java.vm.version = 25.45-b14
+ java.vm.name = Java HotSpot(TM) 64-Bit Server VM
+ java.vm.info = mixed mode
+ java.runtime.name = Java(TM) SE Runtime Environment
+ java.runtime.version = 1.8.0_92-b14
+ java.io.tmpdir = /tmp
+ user.dir = /home/jetty/mybase
+ user.language = en
+ user.country = US
+
+Jetty Environment:
+-----------------
+ jetty.version = {VERSION}
+ jetty.home = /opt/jetty-distribution
+ jetty.base = /home/jetty/mybase
+
+Config Search Order:
+--------------------
+ <command-line>
+ ${jetty.base} -> /home/jetty/mybase
+ ${jetty.home} -> /opt/jetty-distribution
+
+
+JVM Arguments:
+--------------
+ (no jvm args specified)
+
+System Properties:
+------------------
+ (no system properties specified)
+
+Properties:
+-----------
+ (no properties specified)
+
+Jetty Server Classpath:
+-----------------------
+No classpath entries and/or version information available show.
+
+Jetty Active XMLs:
+------------------
+ (no xml files specified)
+....
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/screen-empty-base.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/screen-empty-base.adoc
new file mode 100644
index 0000000..2abc44a
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/screen-empty-base.adoc
@@ -0,0 +1,31 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[source, screen, subs="{sub-order}"]
+....
+[jetty]$ mkdir mybase
+[jetty]$ cd mybase
+[mybase]$ ls -la
+total 0
+drwxr-xr-x   2 staff  staff   68 Jul 12 17:29 .
+drwxr-xr-x  20 staff  staff  680 Jul 12 17:29 ..
+
+[mybase]$ java -jar $JETTY_HOME/start.jar
+WARNING: Nothing to start, exiting ...
+
+Usage: java -jar start.jar [options] [properties] [configs]
+       java -jar start.jar --help  # for more information
+....
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/screen-http-webapp-deploy-listconfig.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/screen-http-webapp-deploy-listconfig.adoc
new file mode 100644
index 0000000..be46f0c
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/screen-http-webapp-deploy-listconfig.adoc
@@ -0,0 +1,82 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[source, screen, subs="{sub-order}"]
+....
+[mybase]$ java -jar /opt/jetty-distribution/start.jar --list-config
+
+Java Environment:
+-----------------
+ java.home = /lib/jvm/jdk-8u31-x64/jre
+ java.vm.vendor = Oracle Corporation
+ java.vm.version = 25.31-b07
+ java.vm.name = Java HotSpot(TM) 64-Bit Server VM
+ java.vm.info = mixed mode
+ java.runtime.name = Java(TM) SE Runtime Environment
+ java.runtime.version = 1.8.0_31-b13
+ java.io.tmpdir = /tmp
+ user.dir = /home/jetty/mybase
+ user.language = en
+ user.country = US
+
+Jetty Environment:
+-----------------
+ jetty.version = {VERSION}
+ jetty.home = /opt/jetty-distribution
+ jetty.base = /home/jetty/mybase
+
+Config Search Order:
+--------------------
+ <command-line>
+ ${jetty.base} -> /home/jetty/mybase
+ ${jetty.home} -> /opt/jetty-distribution
+
+
+JVM Arguments:
+--------------
+ (no jvm args specified)
+
+System Properties:
+------------------
+ (no system properties specified)
+
+Properties:
+-----------
+ jetty.http.port = 8080
+
+Jetty Server Classpath:
+-----------------------
+Version Information on 11 entries in the classpath.
+Note: order presented here is how they would appear on the classpath.
+      changes to the --module=name command line options will be reflected here.
+ 0:                    3.1.0 | ${jetty.home}/lib/servlet-api-3.1.jar
+ 1:                 3.1.0.M0 | ${jetty.home}/lib/jetty-schemas-3.1.jar
+ 2:           {VERSION} | ${jetty.home}/lib/jetty-http-{VERSION}.jar
+ 3:           {VERSION} | ${jetty.home}/lib/jetty-server-{VERSION}.jar
+ 4:           {VERSION} | ${jetty.home}/lib/jetty-xml-{VERSION}.jar
+ 5:           {VERSION} | ${jetty.home}/lib/jetty-util-{VERSION}.jar
+ 6:           {VERSION} | ${jetty.home}/lib/jetty-io-{VERSION}.jar
+ 7:           {VERSION} | ${jetty.home}/lib/jetty-security-{VERSION}.jar
+ 8:           {VERSION} | ${jetty.home}/lib/jetty-servlet-{VERSION}.jar
+ 9:           {VERSION} | ${jetty.home}/lib/jetty-webapp-{VERSION}.jar
+10:           {VERSION} | ${jetty.home}/lib/jetty-deploy-{VERSION}.jar
+
+Jetty Active XMLs:
+------------------
+ ${jetty.home}/etc/jetty.xml
+ ${jetty.home}/etc/jetty-http.xml
+ ${jetty.home}/etc/jetty-deploy.xml
+....
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/screen-http-webapp-deploy.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/screen-http-webapp-deploy.adoc
new file mode 100644
index 0000000..8c12e51
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/screen-http-webapp-deploy.adoc
@@ -0,0 +1,29 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[source, screen, subs="{sub-order}"]
+....
+[mybase]$ java -jar /opt/jetty-distribution/start.jar --add-to-start=http,webapp,deploy
+
+INFO: server          initialised (transitively) in ${jetty.base}/start.ini
+INFO: http            initialised in ${jetty.base}/start.ini
+INFO: security        initialised (transitively) in ${jetty.base}/start.ini
+INFO: servlet         initialised (transitively) in ${jetty.base}/start.ini
+INFO: webapp          initialised in ${jetty.base}/start.ini
+INFO: deploy          initialised in ${jetty.base}/start.ini
+MKDIR: ${jetty.base}/webapps
+INFO: Base directory was modified
+....
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/screen-list-modules.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/screen-list-modules.adoc
new file mode 100644
index 0000000..af6b301
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/screen-list-modules.adoc
@@ -0,0 +1,350 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[source, screen, subs="{sub-order}"]
+....
+[mybase]$ java -jar /opt/jetty-distribution/start.jar --list-modules
+
+Jetty All Available Modules:
+----------------------------
+
+ [ ] Module: alpn
+     Depend: ssl
+     Depend: alpn-impl/alpn-${java.version}
+        LIB: lib/jetty-alpn-client-${jetty.version}.jar
+        LIB: lib/jetty-alpn-server-${jetty.version}.jar
+        XML: etc/jetty-alpn.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: protonego-boot
+        Ref: alpn-impl/alpn-1.8.0_45
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: annotations
+     Depend: plus
+        LIB: lib/jetty-annotations-${jetty.version}.jar
+        LIB: lib/annotations/*.jar
+        XML: etc/jetty-annotations.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: apache-jsp
+        LIB: lib/apache-jsp/*.jar
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: apache-jstl
+        LIB: lib/apache-jstl/*.jar
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: cdi
+     Depend: deploy
+     Depend: annotations
+     Depend: plus
+     Depend: jsp
+        LIB: lib/cdi/*.jar
+        LIB: lib/cdi-core-${jetty.version}.jar
+        LIB: lib/cdi-servlet-${jetty.version}.jar
+        XML: etc/jetty-cdi.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: client
+        LIB: lib/jetty-client-${jetty.version}.jar
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: continuation
+        LIB: lib/jetty-continuation-${jetty.version}.jar
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: debuglog
+     Depend: server
+        XML: etc/jetty-debug.xml
+    Enabled: <not enabled in this configuration>
+
+ [x] Module: deploy
+     Depend: webapp
+        LIB: lib/jetty-deploy-${jetty.version}.jar
+        XML: etc/jetty-deploy.xml
+    Enabled: <via> ${jetty.base}/start.ini
+
+ [ ] Module: ext
+        LIB: lib/ext/**.jar
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: fcgi
+     Depend: servlet
+     Depend: client
+        LIB: lib/jetty-proxy-${jetty.version}.jar
+        LIB: lib/fcgi/*.jar
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: gzip
+     Depend: server
+        XML: etc/jetty-gzip.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: hawtio
+     Depend: stats
+     Depend: deploy
+     Depend: jmx
+        XML: etc/hawtio.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: home-base-warning
+        XML: etc/home-base-warning.xml
+    Enabled: <not enabled in this configuration>
+
+ [x] Module: http
+     Depend: server
+        XML: etc/jetty-http.xml
+    Enabled: <via> ${jetty.base}/start.ini
+
+ [ ] Module: http2
+     Depend: ssl
+     Depend: alpn
+        LIB: lib/http2/*.jar
+        XML: etc/jetty-http2.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: http2c
+     Depend: http
+        LIB: lib/http2/*.jar
+        XML: etc/jetty-http2c.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: https
+     Depend: ssl
+        XML: etc/jetty-https.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: infinispan
+     Depend: annotations
+     Depend: webapp
+        LIB: lib/jetty-infinispan-${jetty.version}.jar
+        LIB: lib/infinispan/*.jar
+        XML: etc/jetty-infinispan.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: ipaccess
+     Depend: server
+        XML: etc/jetty-ipaccess.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: jaas
+     Depend: server
+        LIB: lib/jetty-jaas-${jetty.version}.jar
+        XML: etc/jetty-jaas.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: jamon
+     Depend: stats
+     Depend: deploy
+     Depend: jmx
+     Depend: jsp
+        LIB: lib/jamon/**.jar
+        XML: etc/jamon.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: jaspi
+     Depend: security
+        LIB: lib/jetty-jaspi-${jetty.version}.jar
+        LIB: lib/jaspi/*.jar
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: jdbc-sessions
+     Depend: annotations
+     Depend: webapp
+        XML: etc/jetty-jdbc-sessions.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: jminix
+     Depend: stats
+     Depend: jmx
+        LIB: lib/jminix/**.jar
+        XML: etc/jminix.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: jmx
+     Depend: server
+        LIB: lib/jetty-jmx-${jetty.version}.jar
+        XML: etc/jetty-jmx.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: jmx-remote
+     Depend: jmx
+        XML: etc/jetty-jmx-remote.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: jndi
+     Depend: server
+        LIB: lib/jetty-jndi-${jetty.version}.jar
+        LIB: lib/jndi/*.jar
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: jolokia
+     Depend: stats
+     Depend: deploy
+     Depend: jmx
+        XML: etc/jolokia.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: jsp
+     Depend: servlet
+     Depend: annotations
+     Depend: apache-jsp
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: jstl
+     Depend: jsp
+     Depend: apache-jstl
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: jvm
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: logging
+        LIB: lib/logging/**.jar
+        LIB: resources/
+        XML: etc/jetty-logging.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: lowresources
+     Depend: server
+        XML: etc/jetty-lowresources.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: monitor
+     Depend: server
+     Depend: client
+        LIB: lib/monitor/jetty-monitor-${jetty.version}.jar
+        XML: etc/jetty-monitor.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: nosql
+     Depend: webapp
+        LIB: lib/jetty-nosql-${jetty.version}.jar
+        LIB: lib/nosql/*.jar
+        XML: etc/jetty-nosql.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: plus
+     Depend: server
+     Depend: security
+     Depend: jndi
+     Depend: webapp
+        LIB: lib/jetty-plus-${jetty.version}.jar
+        XML: etc/jetty-plus.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: proxy
+     Depend: servlet
+     Depend: client
+        LIB: lib/jetty-proxy-${jetty.version}.jar
+        XML: etc/jetty-proxy.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: quickstart
+     Depend: server
+     Depend: plus
+     Depend: annotations
+        LIB: lib/jetty-quickstart-${jetty.version}.jar
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: requestlog
+     Depend: server
+        XML: etc/jetty-requestlog.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: resources
+        LIB: resources/
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: rewrite
+     Depend: server
+        LIB: lib/jetty-rewrite-${jetty.version}.jar
+        XML: etc/jetty-rewrite.xml
+    Enabled: <not enabled in this configuration>
+
+ [x] Module: security
+     Depend: server
+        LIB: lib/jetty-security-${jetty.version}.jar
+    Enabled: <via> <transitive from> ${jetty.base}/start.ini
+    Enabled: <via> ${jetty.base}/start.ini
+
+ [x] Module: server
+        LIB: lib/servlet-api-3.1.jar
+        LIB: lib/jetty-schemas-3.1.jar
+        LIB: lib/jetty-http-${jetty.version}.jar
+        LIB: lib/jetty-server-${jetty.version}.jar
+        LIB: lib/jetty-xml-${jetty.version}.jar
+        LIB: lib/jetty-util-${jetty.version}.jar
+        LIB: lib/jetty-io-${jetty.version}.jar
+        XML: etc/jetty.xml
+    Enabled: <via> <transitive from> ${jetty.base}/start.ini
+    Enabled: <via> ${jetty.base}/start.ini
+
+ [x] Module: servlet
+     Depend: server
+        LIB: lib/jetty-servlet-${jetty.version}.jar
+    Enabled: <via> <transitive from> ${jetty.base}/start.ini
+    Enabled: <via> ${jetty.base}/start.ini
+
+ [ ] Module: servlets
+     Depend: servlet
+        LIB: lib/jetty-servlets-${jetty.version}.jar
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: setuid
+     Depend: server
+        LIB: lib/setuid/jetty-setuid-java-1.0.3.jar
+        XML: etc/jetty-setuid.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: spring
+     Depend: server
+        LIB: lib/spring/*.jar
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: ssl
+     Depend: server
+        XML: etc/jetty-ssl.xml
+        XML: etc/jetty-ssl-context.xml
+    Enabled: <not enabled in this configuration>
+
+ [ ] Module: stats
+     Depend: server
+        XML: etc/jetty-stats.xml
+    Enabled: <not enabled in this configuration>
+
+ [x] Module: webapp
+     Depend: servlet
+     Depend: security
+        LIB: lib/jetty-webapp-${jetty.version}.jar
+    Enabled: <via> ${jetty.base}/start.ini
+    Enabled: <via> <transitive from> ${jetty.base}/start.ini
+
+ [ ] Module: websocket
+     Depend: annotations
+        LIB: lib/websocket/*.jar
+    Enabled: <not enabled in this configuration>
+
+Jetty Selected Module Ordering:
+-------------------------------
+    1) server          ${jetty.base}/start.ini
+    2) http            ${jetty.base}/start.ini
+    2) security        ${jetty.base}/start.ini
+    2) servlet         ${jetty.base}/start.ini
+    3) webapp          ${jetty.base}/start.ini
+    4) deploy          ${jetty.base}/start.ini
+....
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/start-jar.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/start-jar.adoc
new file mode 100644
index 0000000..eb6bb69
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/start-jar.adoc
@@ -0,0 +1,201 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[start-jar]]
+=== Using start.jar
+
+The most basic way of starting the Jetty standalone server is to execute the `start.jar`, which is a bootstrap for starting Jetty with the configuration you want.
+
+[source, screen, subs="{sub-order}"]
+....
+[jetty-distribution-{VERSION}]$ java -jar start.jar
+2013-09-23 11:27:06.654:INFO:oejs.Server:main: jetty-{VERSION}
+...
+....
+
+Jetty is a highly modularized web server container.
+Very little is mandatory and required, and most components are optional; you enable or disable them according to the needs of your environment.
+
+At its most basic, you configure Jetty from two elements:
+
+1.  A set of libraries and directories that make up the server classpath.
+2.  A set of Jetty XML configuration files (IoC style) that establish how to build the Jetty server and its components.
+
+Instead of editing these directly, Jetty 9.1 introduced more options on how to configure Jetty (these are merely syntactic sugar that eventually resolve into the two basic configuration components).
+
+Jetty Startup Features include:
+
+* A separation of the Jetty distribution binaries in `${jetty.home}` and the environment specific configurations (and binaries) found in `${jetty.base}` (detailed in link:#startup-jetty-base-and-jetty-home[Managing Jetty Base and Jetty Home.])
+* You can enable a set of libraries and XML configuration files via the newly introduced link:#startup-modules[module system.]
+* All of the pre-built XML configuration files shipped in Jetty are now parameterized with properties that you can specify in your `${jetty.base}/start.ini` (demonstrated in link:#quick-start-configure[Quick Start Configuration]).
+
+These are powerful new features, made to support a variety of styles of configuring Jetty, from a simple property based configuration, to handling multiple installations on a server, to customized stacks of technology on top of Jetty, and even the classic, custom XML configurations of old.
+
+For example, if you use the `${jetty.base}` concepts properly, you can upgrade the Jetty distribution without having to remake your entire tree of modifications to Jetty.
+Simply separate out your specific modifications to the `${jetty.base}`, and in the future, just upgrade your `${jetty.home}` directory with a new Jetty distribution.
+
+[[executing-startjar]]
+
+==== Executing start.jar
+
+When executed `start.jar` performs the following actions:
+
+* Loads and parses all INIs found in `${jetty.base}/start.d/*.ini` as command line arguments.
+* Loads and parses `${jetty.base}/start.ini` as command line arguments.
+* Parses actual command line arguments used to execute `start.jar` itself.
+* Resolves any XML configuration files, modules, and libraries using base vs. home resolution steps:
+1.  Checks whether file exists as relative reference to `${jetty.base}.`
+2.  Checks whether file exists as relative reference to `${jetty.home}.`
+3.  Uses default behavior of `java.io.File` (Relative to `System.getProperty` ("user.dir") and then as absolute file system path).
+* Loads any dependent modules (merges XXNK, library, and properties results with active command line).
+* Builds out server classpath.
+* Determines run mode:
+** Shows informational command line options and exit.
+** Executes Jetty normally, waits for Jetty to stop.
+** Executes a forked JVM to run Jetty in, waits for forked JVM to exit.
+
+==== start.jar Command Line Options
+
+--help::
+Obtains the current list of command line options and some basic usage help.
+--version::
+Shows the list of server classpath entries, and prints version information found for each entry.
+--list-classpath::
+Similar to --version, shows the server classpath.
+--list-config::
+Lists the resolved configuration that will start Jetty.
+
++
+* Java environment
+* Jetty environment
+* JVM arguments
+* Properties
+* Server classpath
+* Server XML configuration files
+--dry-run::
+Prints the resolved command line that `start.jar` should use to start a forked instance of Jetty.
+--exec::
+Starts a forked instance of Jetty.
+--debug::
+Enables debugging output of the startup procedure.
++
+*Note*: This does not set up debug logging for Jetty itself.
+For information on logging, please see the section on <<configuring-jetty-logging, Configuring Jetty Logging.>>
+--start-log-file=<filename>::
+  Sends all startup output to the filename specified.
++
+Filename is relative to `${jetty.base}`.
+This is useful for capturing startup issues where the Jetty-specific logger has not yet kicked in due to a possible startup configuration error.
+--list-modules::
+Lists all the modules defined by the system.
++
+Looks for module files using the link:#startup-base-and-home[normal `${jetty.base}` and `${jetty.home}` resolution logic].
++
+Also lists enabled state based on information present on the command line, and all active startup INI files.
+--module=<name>,(<name>)*::
+Enables one or more modules by name (use `--list-modules` to see the list of available modules).
++
+This enables all transitive (dependent) modules from the module system as well.
++
+If you use this from the shell command line, it is considered a temporary effect, useful for testing out a scenario.
+If you want this module to always be enabled, add this command to your `${jetty.base}/start.ini.`
+--add-to-start=<name>,(<name>)*::
+Enables a module by appending lines to the `${jetty.base}/start.ini` file.
++
+The lines that are added are provided by the module-defined INI templates.
++
+Note: Transitive modules are also appended.
+--add-to-startd=<name>,(<name>)*::
+Enables a module via creation of a module-specific INI file in the `${jetty.base}/start.d/` directory.
++
+The content of the new INI is provided by the module-defined ini templates.
++
+Note: Transitive modules are also created in the same directory as their own INI files.
+
+[NOTE]
+--
+With respect to `start.ini` and `start.d/*.ini` files, only *one* of these methods should be implemented.
+Mixing a `start.ini` with module specific ini files in the `{$jetty.base}/start.d` directory can lead to server issues unless great care is taken.
+--
+
+--write-module-graph=<filename>::
+Advanced feature: Creates a graphviz http://graphviz.org/content/dot-language[dot file] of the module graph as it exists for the active `${jetty.base}`.
++
+[source, screen, subs="{sub-order}"]
+....
+# generate module.dot
+$ java -jar start.jar --module=websocket --write-module-graph=modules.dot
+
+# post process to a PNG file
+$ dot -Tpng -o modules.png modules.dot
+....
++
+See http://graphviz.org/[graphviz.org] for details on http://graphviz.org/content/command-line-invocation[how to post-process this dotty file] into the output best suited for your needs.
+
+--create-files::
+Create any missing files that are required by initialized modules.
+This may download a file from the network if the module provides a URL.
+
+--skip-file-validation=<modulename>(,<modulename)*::
+Disable the [files] section validation of content in the `${jetty.base}` directory for a specific module.
+Useful for modules that have downloadable content that is being overridden with alternatives in the `${jetty.base}`` directory.
+
+____
+[CAUTION]
+This advanced option is for administrators that fully understand the configuration of their `${jetty.base}` and are willing to forego some of the safety checks built into the jetty-start mechanism.
+____
+
+--approve-all-licenses::
+Approve all license questions.
+Useful for enabling modules from a script that does not require user interaction.
+
+===== Startup / Shutdown Command Line
+
+--stop::
+Sends a stop signal to the running Jetty instance.
++
+Note: The server must have been started with various stop properties for this to work.
+
+STOP.PORT=<number>;;
+The port to use to stop the running Jetty server.
+This is an internal port, opened on localhost, used solely for stopping the running Jetty server.
+Choose a port that you do not use to serve web traffic.
++
+Required for --stop to function.
+STOP.KEY=<alphanumeric>;;
+The passphrase defined to stop the server.
++
+Required for --stop to function.
+STOP.WAIT=<number>;;
+The time (in seconds) to wait for confirmation that the running Jetty server has stopped.
+If not specified, the stopper waits indefinitely for the server to stop.
++
+If the time specified elapses, without a confirmation of server stop, then the `--stop` command exits with a non-zero return code.
+
+===== Advanced Commands
+
+--lib=<classpath>::
+Add arbitrary classpath entries to the the server classpath.
+
+--include-jetty-dir=<path>::
+Include an extra Jetty directory to use as a source for configuration details.
+This directory behaves similarly to `${jetty.base}` but sits at a layer between `${jetty.base}` and `${jetty.home}`.
+This allows for some complex hierarchies of configuration details.
+
+--download=<http-uri>|<location>::
+If the file does not exist at the given location, download it from the given http URI.
+Note: location is always relative to `${jetty.base}`.
+You might need to escape the slash "\|" to use this on some environments.
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/start-matrix.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/start-matrix.adoc
new file mode 100644
index 0000000..85065da
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/start-matrix.adoc
@@ -0,0 +1,20 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[advanced-start-matrix]]
+=== Start Option Matrix [deprecated]
+
+Content from: http://wiki.eclipse.org/Jetty/Reference/Start_Options
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/startup-base-vs-home.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/startup-base-vs-home.adoc
new file mode 100644
index 0000000..f60de74
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/startup-base-vs-home.adoc
@@ -0,0 +1,311 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[startup-base-and-home]]
+=== Managing Jetty Base and Jetty Home
+
+Starting with Jetty 9.1, it is possible to maintain a separation between the binary installation of the standalone Jetty (known as `${jetty.home}`), and the customizations for your specific environment (known as `${jetty.base}`).
+
+Jetty Base::
+  * Also known as the `${jetty.base}` property.
+  * This is the location for your configurations and customizations to the Jetty distribution.
+Jetty Home::
+  * Also known as the `${jetty.home}` property.
+  * This is the location for the Jetty distribution binaries, default XML IoC configurations, and default module definitions.
+
+Potential configuration is resolved from these 2 directory locations.
+
+Check Jetty Base::
+  If the referenced configuration exists, relative to the defined Jetty base, use it.
+Check Jetty Home::
+  If the referenced configuration exists, relative to the defined Jetty home, use it.
+Use java.io.File(String pathname) Logic::
+  Lastly, use the reference as a `java.io.File(String pathname)` reference, following the default resolution rules outlined by that constructor.
+
++
+In brief, the reference will be used as-is, be it relative (to current working directory, aka $\{user.dir}) or absolute path, or even network reference (such as on Windows and use of UNC paths).
+
+For more details on how startup with start.jar works, see link:#execute-start-jar[Using start.jar: Executing]
+
+[[demo-base]]
+==== Demo-Base in the Jetty Distribution
+
+The Jetty Distribution comes with an example `${jetty.base}` which enables the various demonstration webapps and server configurations.
+
+How to use the demo-base directory as a Jetty Base directory.
+
+[source, screen, subs="{sub-order}"]
+....
+[jetty-distribution-{VERSION}]$ ls -la
+
+total 496
+drwxrwxr-x 11 user group   4096 Oct  8 15:23 ./
+drwxr-xr-x 14 user group   4096 Oct  8 13:04 ../
+drwxrwxr-x  2 user group   4096 Oct  8 06:54 bin/
+drwxrwxr-x  6 user group   4096 Oct  8 06:54 demo-base/
+drwxrwxr-x  2 user group   4096 Oct 11 15:14 etc/
+drwxrwxr-x 11 user group   4096 Oct  8 06:54 lib/
+-rw-rw-r--  1 user group  30012 Sep 30 19:55 license-eplv10-aslv20.html
+drwxrwxr-x  2 user group   4096 Oct  8 06:54 logs/
+drwxrwxr-x  2 user group   4096 Oct  8 06:54 modules/
+-rw-rw-r--  1 user group   6262 Sep 30 19:55 notice.html
+-rw-rw-r--  1 user group   1249 Sep 30 19:55 README.TXT
+drwxrwxr-x  2 user group   4096 Oct  8 06:54 resources/
+drwxrwxr-x  2 user group   4096 Oct  8 06:54 start.d/
+-rw-rw-r--  1 user group   1780 Sep 30 19:55 start.ini
+-rw-rw-r--  1 user group  71921 Sep 30 19:55 start.jar
+-rw-rw-r--  1 user group 336468 Sep 30 19:55 VERSION.txt
+drwxrwxr-x  2 user group   4096 Oct  8 06:54 webapps/
+
+[jetty-distribution-{VERSION}]$ cd demo-base
+[demo-base]$ java -jar $JETTY_HOME/start.jar
+
+2013-10-16 09:08:47.800:WARN::main: demo test-realm is deployed. DO NOT USE IN PRODUCTION!
+2013-10-16 09:08:47.802:INFO:oejs.Server:main: jetty-{VERSION}
+2013-10-16 09:08:47.817:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:/home/user/jetty-distribution-{VERSION}/demo-base/webapps/] at interval 1
+2013-10-16 09:08:48.072:WARN::main: async-rest webapp is deployed. DO NOT USE IN PRODUCTION!
+...
+....
+
+As you can see above, you are executing the demo-base configuration using the Jetty base concepts.
+
+If you want to see what the Jetty base looks like without executing Jetty, you can simply list the configuration
+
+[source, screen, subs="{sub-order}"]
+....
+[my-base]$ java -jar $JETTY_HOME/start.jar --list-config
+
+Java Environment:
+-----------------
+ java.home=/usr/lib/jvm/jdk-7u21-x64/jre
+ java.vm.vendor=Oracle Corporation
+ java.vm.version=23.21-b01
+ java.vm.name=Java HotSpot(TM) 64-Bit Server VM
+ java.vm.info=mixed mode
+ java.runtime.name=Java(TM) SE Runtime Environment
+ java.runtime.version=1.7.0_21-b11
+ java.io.tmpdir=/tmp
+
+Jetty Environment:
+-----------------
+ jetty.home=/home/user/jetty-distribution-{VERSION}
+ jetty.base=/home/user/jetty-distribution-{VERSION}/demo-base
+ jetty.version={VERSION}
+
+JVM Arguments:
+--------------
+ (no jvm args specified)
+
+System Properties:
+------------------
+ jetty.base = /home/user/jetty-distribution-{VERSION}/demo-base
+ jetty.home = /home/user/jetty-distribution-{VERSION}
+
+Properties:
+-----------
+ demo.realm = etc/realm.properties
+ https.port = 8443
+ https.timeout = 30000
+ jaas.login.conf = etc/login.conf
+ jetty.dump.start = false
+ jetty.dump.stop = false
+ jetty.keymanager.password = OBF:1u2u1wml1z7s1z7a1wnl1u2g
+ jetty.keystore = etc/keystore
+ jetty.keystore.password = OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
+ jetty.http.port = 8080
+ jetty.secure.port = 8443
+ jetty.truststore = etc/keystore
+ jetty.truststore.password = OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
+ org.eclipse.jetty.websocket.jsr356 = false
+ threads.max = 200
+ threads.min = 10
+ threads.timeout = 60000
+
+Jetty Server Classpath:
+-----------------------
+Version Information on 42 entries in the classpath.
+Note: order presented here is how they would appear on the classpath.
+      changes to the --module=name command line options will be reflected here.
+ 0:               {VERSION} | ${jetty.home}/lib/jetty-client-{VERSION}.jar
+ 1:      1.4.1.v201005082020 | ${jetty.base}/lib/ext/javax.mail.glassfish-1.4.1.v201005082020.jar
+ 2:               {VERSION} | ${jetty.base}/lib/ext/test-mock-resources-{VERSION}.jar
+ 3:                    (dir) | ${jetty.home}/resources
+ 4:                    3.1.0 | ${jetty.home}/lib/servlet-api-3.1.jar
+ 5:                  3.1.RC0 | ${jetty.home}/lib/jetty-schemas-3.1.jar
+ 6:               {VERSION} | ${jetty.home}/lib/jetty-http-{VERSION}.jar
+ 7:               {VERSION} | ${jetty.home}/lib/jetty-continuation-{VERSION}.jar
+ 8:               {VERSION} | ${jetty.home}/lib/jetty-server-{VERSION}.jar
+ 9:               {VERSION} | ${jetty.home}/lib/jetty-xml-{VERSION}.jar
+10:               {VERSION} | ${jetty.home}/lib/jetty-util-{VERSION}.jar
+11:               {VERSION} | ${jetty.home}/lib/jetty-io-{VERSION}.jar
+12:               {VERSION} | ${jetty.home}/lib/jetty-jaas-{VERSION}.jar
+13:               {VERSION} | ${jetty.home}/lib/jetty-jndi-{VERSION}.jar
+14:      1.1.0.v201105071233 | ${jetty.home}/lib/jndi/javax.activation-1.1.0.v201105071233.jar
+15:      1.4.1.v201005082020 | ${jetty.home}/lib/jndi/javax.mail.glassfish-1.4.1.v201005082020.jar
+16:                      1.2 | ${jetty.home}/lib/jndi/javax.transaction-api-1.2.jar
+17:               {VERSION} | ${jetty.home}/lib/jetty-rewrite-{VERSION}.jar
+18:               {VERSION} | ${jetty.home}/lib/jetty-security-{VERSION}.jar
+19:               {VERSION} | ${jetty.home}/lib/jetty-servlet-{VERSION}.jar
+20:                    3.0.0 | ${jetty.home}/lib/jsp/javax.el-3.0.0.jar
+21:      1.2.0.v201105211821 | ${jetty.home}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar
+22:                    2.3.2 | ${jetty.home}/lib/jsp/javax.servlet.jsp-2.3.2.jar
+23:                    2.3.1 | ${jetty.home}/lib/jsp/javax.servlet.jsp-api-2.3.1.jar
+24:                    2.3.3 | ${jetty.home}/lib/jsp/jetty-jsp-jdt-2.3.3.jar
+25:      1.2.0.v201112081803 | ${jetty.home}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar
+26:   3.8.2.v20130121-145325 | ${jetty.home}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar
+27:               {VERSION} | ${jetty.home}/lib/jetty-plus-{VERSION}.jar
+28:               {VERSION} | ${jetty.home}/lib/jetty-webapp-{VERSION}.jar
+29:               {VERSION} | ${jetty.home}/lib/jetty-annotations-{VERSION}.jar
+30:                      4.1 | ${jetty.home}/lib/annotations/asm-4.1.jar
+31:                      4.1 | ${jetty.home}/lib/annotations/asm-commons-4.1.jar
+32:                      1.2 | ${jetty.home}/lib/annotations/javax.annotation-api-1.2.jar
+33:               {VERSION} | ${jetty.home}/lib/jetty-deploy-{VERSION}.jar
+34:                      1.0 | ${jetty.home}/lib/websocket/javax.websocket-api-1.0.jar
+35:               {VERSION} | ${jetty.home}/lib/websocket/javax-websocket-client-impl-{VERSION}.jar
+36:               {VERSION} | ${jetty.home}/lib/websocket/javax-websocket-server-impl-{VERSION}.jar
+37:               {VERSION} | ${jetty.home}/lib/websocket/websocket-api-{VERSION}.jar
+38:               {VERSION} | ${jetty.home}/lib/websocket/websocket-client-{VERSION}.jar
+39:               {VERSION} | ${jetty.home}/lib/websocket/websocket-common-{VERSION}.jar
+40:               {VERSION} | ${jetty.home}/lib/websocket/websocket-server-{VERSION}.jar
+41:               {VERSION} | ${jetty.home}/lib/websocket/websocket-servlet-{VERSION}.jar
+
+Jetty Active XMLs:
+------------------
+ ${jetty.home}/etc/jetty.xml
+ ${jetty.home}/etc/jetty-http.xml
+ ${jetty.home}/etc/jetty-jaas.xml
+ ${jetty.home}/etc/jetty-rewrite.xml
+ ${jetty.home}/etc/jetty-ssl.xml
+ ${jetty.home}/etc/jetty-https.xml
+ ${jetty.home}/etc/jetty-plus.xml
+ ${jetty.home}/etc/jetty-annotations.xml
+ ${jetty.home}/etc/jetty-deploy.xml
+ ${jetty.base}/etc/demo-rewrite-rules.xml
+ ${jetty.base}/etc/test-realm.xml
+....
+
+This demonstrates the powerful `--list-config` command line option and how you can use it to see what the configuration will look like when starting Jetty.
+From the Java environment, to the system properties, to the classpath, and finally the Active Jetty IoC XML used to build up the Jetty server configuration.
+
+Of note, is that the output will make it known where the configuration elements came from, be it in either in `${jetty.home}` or `${jetty.base}`.
+
+If you look at the `${jetty.base}/start.ini` you will see a layout similar to below.
+
+[source, screen, subs="{sub-order}"]
+....
+[my-base]$ cat start.ini
+
+# Enable security via jaas, and configure it
+--module=jaas
+jaas.login.conf=etc/login.conf
+
+# Enable rewrite examples
+--module=rewrite
+etc/demo-rewrite-rules.xml
+
+# Websocket chat examples needs websocket enabled
+# Don't start for all contexts (set to true in test.xml context)
+org.eclipse.jetty.websocket.jsr356=false
+--module=websocket
+
+# Create and configure the test realm
+etc/test-realm.xml
+demo.realm=etc/realm.properties
+
+# Initialize module server
+--module=server
+threads.min=10
+threads.max=200
+threads.timeout=60000
+jetty.dump.start=false
+jetty.dump.stop=false
+
+--module=deploy
+--module=jsp
+--module=ext
+--module=resources
+--module=client
+--module=annotations
+....
+
+The `${jetty.base}/start.ini` is the main startup configuration entry point for Jetty.
+In this example you will see that we are enabling a few modules for Jetty, specifying some properties, and also referencing some Jetty IoC XML files (namely the `etc/demo-rewrite-rules.xml` and `etc/test-realm.xml` files)
+
+When Jetty's `start.jar` resolves the entries in the `start.ini`, it will follow the link:#base-vs-home-resolution[resolution rules above].
+
+For example, the reference to `etc/demo-rewrite-rules.xml` was found in `${jetty.base}/etc/demo-rewrite-rules.xml`.
+
+==== Declaring Jetty Base
+
+The Jetty distribution's `start.jar` is the component that manages the behavior of this separation.
+
+The Jetty `start.jar` and XML files always assume that both `${jetty.home}` and `${jetty.base}` are defined when starting Jetty.
+
+You can opt to manually define the `${jetty.home}` and `${jetty.base}` directories, such as this:
+
+[source, screen, subs="{sub-order}"]
+....
+[jetty-distribution-{VERSION}]$ pwd
+/home/user/jetty-distribution-{VERSION}
+
+[jetty-distribution-{VERSION}]$ java -jar start.jar \
+    jetty.home=/home/user/jetty-distribution-{VERSION} \
+    jetty.base=/home/user/my-base
+
+2013-10-16 09:08:47.802:INFO:oejs.Server:main: jetty-{VERSION}
+2013-10-16 09:08:47.817:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:/home/user/my-base/webapps/] at interval 1
+...
+....
+
+Alternately, you can declare one directory and let the other one be discovered.
+
+The following example uses default discovery of `${jetty.home}` by using the parent directory of wherever `start.jar` itself is, and a manual declaration of `${jetty.base}`.
+
+[source, screen, subs="{sub-order}"]
+....
+[jetty-distribution-{VERSION}]$ pwd
+/home/user/jetty-distribution-{VERSION}
+
+[jetty-distribution-{VERSION}]$ java -jar start.jar jetty.base=/home/user/my-base
+
+2013-10-16 09:08:47.802:INFO:oejs.Server:main: jetty-{VERSION}
+2013-10-16 09:08:47.817:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:/home/user/my-base/webapps/] at interval 1
+...
+....
+
+But Jetty recommends that you always start Jetty from the directory that is your `${jetty.base}` and starting Jetty by referencing
+the `start.jar` in your `{$jetty.home}` remotely.
+
+The following demonstrates this by allowing default discovery of `${jetty.home}` via locating the `start.jar`, and using the `user.dir` System Property for `${jetty.base}`.
+
+[source, screen, subs="{sub-order}"]
+....
+[jetty-distribution-{VERSION}]$ pwd
+/home/user/jetty-distribution-{VERSION}
+
+[jetty-distribution-{VERSION}]$ cd /home/user/my-base
+[my-base]$ java -jar /home/user/jetty-distribution-{VERSION}/start.jar
+
+2013-10-16 09:08:47.802:INFO:oejs.Server:main: jetty-{VERSION}
+2013-10-16 09:08:47.817:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:/home/user/my-base/webapps/] at interval 1
+...
+....
+
+____
+[IMPORTANT]
+Be aware of the `user.dir` system property, as it can only be safely set when the JVM starts and many 3rd party libraries (especially logging) use this system property.
+It is strongly recommended that you sit in the directory that is your desired `${jetty.base}` when starting Jetty to have consistent behavior and use of the `user.dir` system property.
+____
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/startup-classpath.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/startup-classpath.adoc
new file mode 100644
index 0000000..1d07231
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/startup-classpath.adoc
@@ -0,0 +1,109 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[startup-classpath]]
+=== Managing Server Classpath
+
+Jetty Server Classpath is determined by a combination of factors.
+
+The java.class.path System Property::
+  If you start Jetty with a JVM specified classpath, then Jetty will use the java.class.path System Property to populate the initial classpath.
+Module specified Libraries::
+  The module system declares various libraries that are required for that module to operate.
+  These module defined libraries are added to the Jetty Server classpath when any module is activated with library declarations.
+Command Line Libraries::
+  The command line option `--lib=<path>` can be used as a final means to add arbitrary entries to the Jetty Server classpath.
+
+Of special note, there are 2 structural modules defined to ease some of this for you.
+
+--module=ext::
+  The `ext` module will enable the `lib/ext/*.jar` logic.
+  +
+  If this module is activated, then all jar files found in the lib/ext/ paths will be automatically added to the Jetty Server Classpath.
+--module=resources::
+  The `resources` module will add the `resources/` directory the classpath.
+  +
+  If you have 3rd party libraries that lookup resources from the classpath, put your files in here.
+  +
+  Logging libraries often have classpath lookup of their configuration files (eg: `log4j.properties`, `log4j.xml`, `logging.properties`, and `logback.xml`), so this would be the ideal setup for this sort of configuration demand.
+
+____
+[NOTE]
+Both the `ext` and `resources` modules declare relative paths that follow link:#base-vs-home-resolution[Jetty Base and Jetty Home path resolution rules].
+____
+
+==== Interrogating the Server Classpath
+
+The Jetty `start.jar` has the ability to resolve the classpath from the command line, modules and configuration, and to list the classpath entries it will use to start jetty.
+
+The `--list-classpath` command line option is used as such.
+
+(Demonstrated with the link:#demo-base[demo-base from the Jetty Distribution])
+
+[source, screen, subs="{sub-order}"]
+....
+[demo-base]$ java -jar $JETTY_HOME/start.jar --list-classpath
+
+Jetty Server Classpath:
+-----------------------
+Version Information on 42 entries in the classpath.
+Note: order presented here is how they would appear on the classpath.
+      changes to the --module=name command line options will be reflected here.
+ 0:               {VERSION} | ${jetty.home}/lib/jetty-client-{VERSION}.jar
+ 1:      1.4.1.v201005082020 | ${jetty.base}/lib/ext/javax.mail.glassfish-1.4.1.v201005082020.jar
+ 2:               {VERSION} | ${jetty.base}/lib/ext/test-mock-resources-{VERSION}.jar
+ 3:                    (dir) | ${jetty.home}/resources
+ 4:                    3.1.0 | ${jetty.home}/lib/servlet-api-3.1.jar
+ 5:                  3.1.RC0 | ${jetty.home}/lib/jetty-schemas-3.1.jar
+ 6:               {VERSION} | ${jetty.home}/lib/jetty-http-{VERSION}.jar
+ 7:               {VERSION} | ${jetty.home}/lib/jetty-continuation-{VERSION}.jar
+ 8:               {VERSION} | ${jetty.home}/lib/jetty-server-{VERSION}.jar
+ 9:               {VERSION} | ${jetty.home}/lib/jetty-xml-{VERSION}.jar
+10:               {VERSION} | ${jetty.home}/lib/jetty-util-{VERSION}.jar
+11:               {VERSION} | ${jetty.home}/lib/jetty-io-{VERSION}.jar
+12:               {VERSION} | ${jetty.home}/lib/jetty-jaas-{VERSION}.jar
+13:               {VERSION} | ${jetty.home}/lib/jetty-jndi-{VERSION}.jar
+14:      1.1.0.v201105071233 | ${jetty.home}/lib/jndi/javax.activation-1.1.0.v201105071233.jar
+15:      1.4.1.v201005082020 | ${jetty.home}/lib/jndi/javax.mail.glassfish-1.4.1.v201005082020.jar
+16:                      1.2 | ${jetty.home}/lib/jndi/javax.transaction-api-1.2.jar
+17:               {VERSION} | ${jetty.home}/lib/jetty-rewrite-{VERSION}.jar
+18:               {VERSION} | ${jetty.home}/lib/jetty-security-{VERSION}.jar
+19:               {VERSION} | ${jetty.home}/lib/jetty-servlet-{VERSION}.jar
+20:                    3.0.0 | ${jetty.home}/lib/jsp/javax.el-3.0.0.jar
+21:      1.2.0.v201105211821 | ${jetty.home}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar
+22:                    2.3.2 | ${jetty.home}/lib/jsp/javax.servlet.jsp-2.3.2.jar
+23:                    2.3.1 | ${jetty.home}/lib/jsp/javax.servlet.jsp-api-2.3.1.jar
+24:                    2.3.3 | ${jetty.home}/lib/jsp/jetty-jsp-jdt-2.3.3.jar
+25:      1.2.0.v201112081803 | ${jetty.home}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar
+26:   3.8.2.v20130121-145325 | ${jetty.home}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar
+27:               {VERSION} | ${jetty.home}/lib/jetty-plus-{VERSION}.jar
+28:               {VERSION} | ${jetty.home}/lib/jetty-webapp-{VERSION}.jar
+29:               {VERSION} | ${jetty.home}/lib/jetty-annotations-{VERSION}.jar
+30:                      4.1 | ${jetty.home}/lib/annotations/asm-4.1.jar
+31:                      4.1 | ${jetty.home}/lib/annotations/asm-commons-4.1.jar
+32:                      1.2 | ${jetty.home}/lib/annotations/javax.annotation-api-1.2.jar
+33:               {VERSION} | ${jetty.home}/lib/jetty-deploy-{VERSION}.jar
+34:                      1.0 | ${jetty.home}/lib/websocket/javax.websocket-api-1.0.jar
+35:               {VERSION} | ${jetty.home}/lib/websocket/javax-websocket-client-impl-{VERSION}.jar
+36:               {VERSION} | ${jetty.home}/lib/websocket/javax-websocket-server-impl-{VERSION}.jar
+37:               {VERSION} | ${jetty.home}/lib/websocket/websocket-api-{VERSION}.jar
+38:               {VERSION} | ${jetty.home}/lib/websocket/websocket-client-{VERSION}.jar
+39:               {VERSION} | ${jetty.home}/lib/websocket/websocket-common-{VERSION}.jar
+40:               {VERSION} | ${jetty.home}/lib/websocket/websocket-server-{VERSION}.jar
+41:               {VERSION} | ${jetty.home}/lib/websocket/websocket-servlet-{VERSION}.jar
+....
+
+Of note is that an attempt is made to list the internally declared version of each artifact on the Server Classpath, which can potentially help when diagnosing classpath issues.
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/startup-modules.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/startup-modules.adoc
new file mode 100644
index 0000000..f56d5f0
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/startup-modules.adoc
@@ -0,0 +1,170 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[startup-modules]]
+=== Managing Startup Modules
+
+Starting with Jetty 9.1, a new Module system was introduced, replacing the previous `start.config` + `OPTIONS` techniques from past Jetty Distributions.
+
+The standard Jetty Distribution ships with several modules defined in `${jetty.home}/modules/`.
+
+What a Jetty Startup Module Defines:
+
+A Module Name::
+  The name of the module is the keyword used by the `--module=<name>` command line argument to activate/enable modules, and also find dependent modules.
+  The filename of the module defines its name (eg: server.mod becomes the module named "server").
+List of Dependant Modules::
+  All modules can declare that they depend on other modules with the `[depend]` section.
+  The list of dependencies is used to transitively resolve other modules that are deemed to be required based on the modules that you activate.
+  The order of modules defined in the graph of active modules is used to determine various execution order for configuration, such as Jetty IoC XML configurations, and to resolve conflicting property declarations.
+  Of note: there is a special section `[optional]` used to describe structurally dependent modules that are not technically required, but might be of use to your specific configuration.
+List of Libraries::
+  Module can optionally declare that they have libraries that they need to function properly.
+  The `[lib]` section declares a set of pathnames that follow the link:#base-vs-home-resolution[Jetty Base and Jetty Home path resolution rules].
+List of Jetty IoC XML Configurations::
+  A Module can optionally declare a list of Jetty IoC XML configurations used to wire up the functionality that this module defines.
+  The `[xml]` section declares a set of pathnames that follow the link:#base-vs-home-resolution[Jetty Base and Jetty Home path resolution rules].
+  Ideally, all XML files are parameterized to accept properties to configure the various elements of the standard configuration.
+  Allowing for a simplified configuration of Jetty for the vast majority of deployments.
+  The execution order of the Jetty IoC XML configurations is determined by the graph of active module dependencies resolved via the `[depend]` sections.
+  If the default XML is not sufficient to satisfy your needs, you can override this XML by making your own in the `${jetty.base}/etc/` directory, with the same name.
+  The resolution steps for Jetty Base and Jetty Home will ensure that your copy from `${jetty.base}` will be picked up over the default one in `${jetty.home}`.
+Jetty INI Template::
+  Each module can optionally declare a startup ini template that is used to insert/append/inject sample configuration elements into the `start.ini` or `start.d/*.ini` files when using the `--add-to-start=<name>` or `--add-to-startd=<name>` command line arguments in `start.jar`.
+  Commonly used to present some of the parameterized property options from the Jetty IoC XML configuration files also referenced in the same module.
+  The `[ini-template]` section declares this section of sample configuration.
+Required Files and Directories::
+  If the activation of a module requires some paths to exist, the `[files]` section defines them.
+  There are 2 modes of operation of the entries in this section.
+Ensure Directory Exists;;
+    If you add a pathname that ends in `"/"` (slash), such as `"webapps/"`, then that directory will be created if it does not yet exist in `${jetty.base}/<pathname>` (eg: `"webapps/"` will result in `${jetty.base}/webapps/` being created).
+Download File;;
+    There is a special syntax to allow you to download a file into a specific location if it doesn't exist yet: `<url>:<pathname>`.
+    Currently, the `<url>` must be a `http://` scheme URL (please link:#bugs[let us know] if you need more schemes supported).
+    The `<pathname>` portion follows the link:#base-vs-home-resolution[Jetty Base and Jetty Home path resolution rules].
+    Example: `http://repo.corp.com/maven/corp-security-policy-1.0.jar:lib/corp-security-policy.jar`
+    This will check for the existence of `lib/corp-security-policy.jar`, and if it doesn't exist, it will download the jar file from
+    `http://repo.corp.com/maven/corp-security-policy-1.0.jar`
+
+[[enabling-modules]]
+==== Enabling Modules
+
+Jetty ships with many modules defined, and a small subset predefined in the `start.ini` found in the jetty distribution.
+
+____
+[TIP]
+The default distribution has a co-mingled `${jetty.home}` and `${jetty.base}`. Where the directories for `${jetty.home}` and `${jetty.base}` point to the same location.
+It is highly encouraged that you learn about the differences in link:#startup-base-and-home[Jetty Base vs Jetty Home] and take full advantage of this setup.
+____
+
+When you want enable a module, you can use the `--module=<modulename>` syntax on the command line to enable that module and all of its dependent modules.
+
+An example of this, with a new, empty, base directory.
+We can see from this output, that the directory is new.
+
+include::screen-empty-base.adoc[]
+
+Lets see what the configuration looks like so far:
+
+include::screen-empty-base-listconfig.adoc[]
+
+Lets try adding some basic support for webapps, with automatic deploy (hot deploy), and a single basic HTTP/1.1 connector.
+
+include::screen-http-webapp-deploy.adoc[]
+
+This created the webapps directory in our `mybase` directory and appended the `start.ini` file with the ini template arguments from the associated module files.
+Additionally, where needed, Jetty enabled any module dependencies and added their module ini template properties.
+
+Lets see what it looks like configuration wise.
+
+include::screen-http-webapp-deploy-listconfig.adoc[]
+
+You now have a configured and functional server, albeit with no webapps deployed.
+At this point you can place a webapp (war file) in the `mybase/webapps/` directory and and start Jetty.
+
+[[startup-configuring-modules]]
+
+==== Configuring Modules
+
+Once a module has been enabled for the server, it can be further configured to meet your needs.
+This is done by editing the associated ini file for the module.
+If your server setup is using a centralized ini configuration, you will edit the `{$jetty.base}/server.ini` file.
+If you have elected to manage each module within it's own ini file, you can find these files in the `{$jetty.base}/start.d` directory.
+
+When a module is activated, a number of properties are set by default.
+To view these defaults, open up the associated ini file.
+Listed in the ini file is the associated module file and any properties that can be set.
+
+Below is an example of the `requestlog.ini` file:
+
+[source, screen, subs="{sub-order}"]
+....
+# ---------------------------------------
+# Module: requestlog
+--module=requestlog
+
+## Logging directory (relative to $jetty.base)
+# jetty.requestlog.dir=logs
+
+## File path
+# jetty.requestlog.filePath=${jetty.requestlog.dir}/yyyy_mm_dd.request.log
+
+## Date format for rollovered files (uses SimpleDateFormat syntax)
+# jetty.requestlog.filenameDateFormat=yyyy_MM_dd
+
+## How many days to retain old log files
+# jetty.requestlog.retainDays=90
+
+## Whether to append to existing file
+# jetty.requestlog.append=true
+
+## Whether to use the extended log output
+# jetty.requestlog.extended=true
+
+## Whether to log http cookie information
+# jetty.requestlog.cookies=true
+
+## Timezone of the log entries
+# jetty.requestlog.timezone=GMT
+
+## Whether to log LogLatency
+# jetty.requestlog.loglatency=false
+....
+
+The first lines name the module file being called (located in `{$jetty.home/modules}`).
+Subsequent lines list properties that can be changed as well as a description for each property.
+To edit a property, first un-comment the line by deleting the `#` at the start of the line, then make the change after `=` sign (such as changing a `true` value to `false`).
+
+[[startup-disable-module]]
+==== Disabling Modules
+
+Disabling a module is an easy process.
+To disable a module, comment out the `--module=` line in the associated ini file.
+Deleting the ini file associated with module is another option, but may not be practical in all situations.
+
+[[startup-listing-modules]]
+==== Listing Available and Active Modules
+
+To see which modules are __available__, use the `--list-modules` command line argument.
+This command will also show you which modules are __enabled__.
+Here's an example:
+
+include::screen-list-modules.adoc[]
+
+Since being introduced in Jetty 9.1 the modules provided in the Jetty distribution has expanded greatly.
+Below is a graphical representation of the standard modules and their dependencies.
+
+image:images/modules-9.3-simplified.png[image,width=768]
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/startup-overview.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/startup-overview.adoc
new file mode 100644
index 0000000..9581094
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/startup-overview.adoc
@@ -0,0 +1,239 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[startup-overview]]
+=== Startup Overview
+
+The `start.jar` bootstrap manages the startup of standalone Jetty.
+It is responsible for:
+
+Building the classpath::
+  The `start.jar` bootstrap builds a classpath for all the required Jetty features and their dependencies.
+  It builds the classpath using  either the `--lib` option to `start.jar` to add an individual  classpath entry, or with the `--module` option that includes all the  libs and their dependencies for a module (a named Jetty feature).
+Instantiating the Server Components::
+  The server and its components are instantiated using either Jetty IoC XML or Spring.
+  The Jetty server is a collection of POJOs for the server, connectors, session managers and others.
+  These are instantiated, injected, and wired up together in XML files, commonly one per module/feature, that are passed as arguments to `start.jar`.
+Resolving Server Filesystem Locations::
+  The `start.jar` mechanism resolves canonical locations for the `${jetty.home}` and the `${jetty.base}` directories.
+  The `${jetty.home}` directory is the location of the standard distribution of Jetty.
+  The `${jetty.base}` directory is the location of the local server customization and configurations.
+  +
+  If you want to modify the Jetty distribution, base and home can be the same directory.
+  Separating the base and home directories allows the distribution to remain unmodified, with all customizations in the base directory, and thus simplifies subsequent server version upgrades.
+Parameterizing the Server Configuration::
+  XML files primarily determine the server configuration.
+  Many of these files are parameterized to allow simple injection of host names, ports, passwords and more.
+  The `start.jar` mechanism allows you to set parameters on the command line or in properties files.
+
+To achieve these start up mechanisms, the `start.jar` uses:
+
+Command line arguments::
+  You can configure the entire server with command line arguments that specify libraries, properties and XML files.
+  However in practice the   INI and modules mechanisms (below) reduce the verbosity of the command line.
+INI files::
+  The `start.jar` mechanism uses the contents of the `${jetty.base}/start.ini` and `${jetty.base}/start.d/*.ini` files with each line equivalent to a `start.jar` command line argument.
+  This means that either a global `start.ini` file or multiple `start.d/feature.ini` files control the configuration of the server.
+[NOTE]
+--
+It is important to chose *either* `${jetty.base}/start.ini` or `${jetty.base}/start.d/*.ini` to manage configuration.
+Using both is not recommended and can lead to issues with your server.
+--
+Modules::
+  Instead of explicitly listing all the libraries, properties and XML files for a feature, the `start.jar` mechanism allows you to create  modules.
+  A module is defined in a `modules/*.mod` file, including the libraries, dependencies, XML, and template INI files for a Jetty feature.
+  Thus you can use a single `--module=name` command line option as the equivalent of specifying  `--lib=location`, `feature.xml` or `name=value` arguments for a feature and all its dependencies.
+  Modules also use their dependencies to control the ordering of libraries and XML files.
+  There are several module files included with the Jetty distribution that cover the most common server features, such as HTTP, HTTPS, SSL, Logging, Annotations...etc.
+  These module files should *only* be edited if you are making structural changes to the way the feature will perform.
+  For more information, refer to the section on <<startup-modules,managing startup modules>> later in this chapter.
+XML Files::
+  XML files in either Jetty IoC or Spring format instantiate the actual POJO components of the server.
+  This includes all major components such as connectors, keystores, session managers, and data sources.
+  Typically there are one or more XML files per module, and these are  defined and activated in the corresponding module.
+
+==== Startup Example
+
+The simplest way to start Jetty is via the `start.jar` mechanism using the following Java command line:
+
+[source, screen, subs="{sub-order}"]
+....
+[user]$ cd jetty-distribution-{VERSION}
+[jetty-distribution-{VERSION}]$ java -jar start.jar --module=http jetty.http.port=8080
+....
+
+This command uses the `start.jar` mechanism to bootstrap the classpath, properties, and XML files with the metadata obtained from the `http` module.
+Specifically the `http` module is defined in the `${jetty.home}/modules/http.mod` file, and includes the following:
+
+[source, screen, subs="{sub-order}"]
+....
+[jetty-distribution-{VERSION}]$ cat modules/http.mod
+[depend]
+server
+
+[xml]
+etc/jetty-http.xml
+
+[ini-template]
+jetty.http.port=8080
+http.timeout=30000
+....
+
+The `http` module declares that `http` depends on the server module, uses the `jetty-http.xml` file, and can be parameterized with `jetty.http.port` and `http.timeout` parameters.
+The INI-template section is not actually used by the command above, so the `jetty.http.port` must still be defined on the command line.
+
+Following the server dependency, the `${jetty.home}/modules/server.mod` file includes:
+
+[source, screen, subs="{sub-order}"]
+....
+[jetty-distribution-{VERSION}]$ cat modules/server.mod
+[lib]
+lib/servlet-api-3.1.jar
+lib/jetty-http-${jetty.version}.jar
+lib/jetty-server-${jetty.version}.jar
+lib/jetty-xml-${jetty.version}.jar
+lib/jetty-util-${jetty.version}.jar
+lib/jetty-io-${jetty.version}.jar
+
+[xml]
+etc/jetty.xml
+
+[ini-template]
+threads.min=10
+threads.max=200
+....
+
+The `server` module declares the libraries the server needs and to use `jetty.xml` file.
+The combined metadata of the `http` and `server` modules results in `start.jar` generating the effective Java command line required to start Jetty.
+
+Another way to see this is by asking Jetty what its configuration looks like by appending --list-config to the command line:
+
+[source, screen, subs="{sub-order}"]
+....
+[jetty-distribution-{VERSION}]$ java -jar start.jar --module=http jetty.http.port=9099 --list-config
+
+Java Environment:
+-----------------
+ java.home=/user/lib/jvm/jdk-7u21-x64/jre
+ java.vm.vendor=Oracle Corporation
+ java.vm.version=23.25-b01
+ java.vm.name=Java HotSpot(TM) 64-Bit Server VM
+ java.vm.info=mixed mode
+ java.runtime.name=Java(TM) SE Runtime Environment
+ java.runtime.version=1.7.0_25-b15
+ java.io.tmpdir=/tmp
+
+Jetty Environment:
+-----------------
+ jetty.home=/opt/jetty/jetty-distribution-{VERSION}
+ jetty.base=/opt/jetty/jetty-distribution-{VERSION}
+ jetty.version={VERSION}
+
+JVM Arguments:
+--------------
+ (no jvm args specified)
+
+System Properties:
+------------------
+ jetty.home = /opt/jetty/jetty-distribution-{VERSION}
+ jetty.base = /opt/jetty/jetty-distribution-{VERSION}
+
+Properties:
+-----------
+ jetty.http.port = 9099
+
+Jetty Server Classpath:
+-----------------------
+Version Information on 7 entries in the classpath.
+Note: order presented here is how they would appear on the classpath.
+      changes to the --module=name command line options will be reflected here.
+ 0:                    3.1.0 | ${jetty.home}/lib/servlet-api-3.1.jar
+ 1:                  3.1.RC0 | ${jetty.home}/lib/jetty-schemas-3.1.jar
+ 2:               {VERSION} | ${jetty.home}/lib/jetty-http-{VERSION}.jar
+ 3:               {VERSION} | ${jetty.home}/lib/jetty-server-{VERSION}.jar
+ 4:               {VERSION} | ${jetty.home}/lib/jetty-xml-{VERSION}.jar
+ 5:               {VERSION} | ${jetty.home}/lib/jetty-util-{VERSION}.jar
+ 6:               {VERSION} | ${jetty.home}/lib/jetty-io-{VERSION}.jar
+
+Jetty Active XMLs:
+------------------
+ ${jetty.home}/etc/jetty.xml
+ ${jetty.home}/etc/jetty-http.xml
+....
+
+This represents the entirety of the configuration that is applied to start Jetty.
+
+If you don't want to use the `start.jar` bootstrap, you can start Jetty using a traditional Java command line.
+
+The following is the equivalent Java command line for what the `start.jar` bootstrap above performs.
+
+[source, screen, subs="{sub-order}"]
+....
+[user]$ cd jetty-distribution-{VERSION}
+[jetty-distribution-{VERSION}]$ echo jetty.http.port=8080 > /tmp/jetty.properties
+[jetty-distribution-{VERSION}]$ export JETTY_HOME=`pwd`
+[jetty-distribution-{VERSION}]$ export JETTY_BASE=`pwd`
+[jetty-distribution-{VERSION}]$ export JETTY_VERSION="${project.version}"
+[jetty-distribution-{VERSION}]$ java -Djetty.home=$JETTY_HOME \
+-Djetty.base=$JETTY_BASE \
+-cp \
+ $JETTY_HOME/lib/servlet-api-3.1.jar\
+:$JETTY_HOME/lib/jetty-schemas-3.1.jar\
+:$JETTY_HOME/lib/jetty-http-$JETTY_VERSION.jar\
+:$JETTY_HOME/lib/jetty-server-$JETTY_VERSION.jar \
+:$JETTY_HOME/lib/jetty-xml-$JETTY_VERSION.jar\
+:$JETTY_HOME/lib/jetty-util-$JETTY_VERSION.jar\
+:$JETTY_HOME/lib/jetty-io-$JETTY_VERSION.jar\
+org.eclipse.jetty.xml.XmlConfiguration \
+/tmp/jetty.properties \
+$JETTY_HOME/etc/jetty.xml \
+$JETTY_HOME/etc/jetty-http.xml
+....
+
+The Java command line sets up the classpath with the core Jetty jars and the servlet API, executes the XmlConfiguration class and passes it some XML files that define the server and an HTTP connector running on the port defined in the `jetty.properties` file.
+
+You can further simplify the startup of this server by using the INI template defined by the modules to create a `start.ini` file with the command:
+
+[source, screen, subs="{sub-order}"]
+....
+[user]$ cd jetty-distribution-{VERSION}
+[jetty-distribution-{VERSION}]$ mkdir example-base
+[example-base]$ cd example-base
+[example-base]$ ls -la
+total 8
+drwxrwxr-x  2 user webgroup 4096 Oct  4 11:49 ./
+drwxrwxr-x 12 user webgroup 4096 Oct  4 11:49 ../
+
+[example-base]$ java -jar $JETTY_HOME/start.jar --add-to-start=http
+
+WARNING: http            initialised in ${jetty.base}/start.ini (appended)
+WARNING: http            enabled in     ${jetty.base}/start.ini
+WARNING: server          initialised in ${jetty.base}/start.ini (appended)
+WARNING: server          enabled in     ${jetty.base}/start.ini
+
+[example-base]$ ls -la
+total 12
+drwxrwxr-x  2 user webgroup 4096 Oct  4 11:55 ./
+drwxrwxr-x 12 user webgroup 4096 Oct  4 11:49 ../
+-rw-rw-r--  1 user webgroup  250 Oct  4 11:55 start.ini
+....
+
+Once complete, you can edit the `start.ini` file to modify any parameters and you can run the server with the simple command:
+
+[source, screen, subs="{sub-order}"]
+....
+[example-base]$ java -jar $JETTY_HOME/start.jar
+....
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/startup-troubleshooting.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/startup-troubleshooting.adoc
new file mode 100644
index 0000000..f87a3c5
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/startup-troubleshooting.adoc
@@ -0,0 +1,20 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[startup-troubleshooting]]
+=== Startup Troubleshooting
+
+[troubleshooting startup]
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/startup-unix-service.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/startup-unix-service.adoc
new file mode 100644
index 0000000..da0ae4f
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/startup-unix-service.adoc
@@ -0,0 +1,272 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[startup-unix-service]]
+=== Startup a Unix Service using jetty.sh
+
+The standalone Jetty distribution ships with a `bin/jetty.sh` script that can be used by various Unix distros (including OSX) to manage Jetty as a startup service.
+
+This script is suitable for setting up Jetty as a service in Unix.
+
+==== Quick-Start a Jetty Service
+
+The minimum steps to get Jetty to run as a Service include:
+
+[source, screen, subs="{sub-order}"]
+....
+[/opt/jetty]# tar -zxf /home/user/downloads/jetty-distribution-{VERSION}.tar.gz
+[/opt/jetty]# cd jetty-distribution-{VERSION}/
+[/opt/jetty/jetty-distribution-{VERSION}]# ls
+bin        lib                         modules      resources  start.jar
+demo-base  license-eplv10-aslv20.html  notice.html  start.d    VERSION.txt
+etc        logs                        README.TXT   start.ini  webapps
+
+[/opt/jetty/jetty-distribution-{VERSION}]# cp bin/jetty.sh /etc/init.d/jetty
+[/opt/jetty/jetty-distribution-{VERSION}]# echo JETTY_HOME=`pwd` > /etc/default/jetty
+[/opt/jetty/jetty-distribution-{VERSION}]# cat /etc/default/jetty
+JETTY_HOME=/opt/jetty/jetty-distribution-{VERSION}
+
+[/opt/jetty/jetty-distribution-{VERSION}]# service jetty start
+Starting Jetty: OK Wed Nov 20 10:26:53 MST 2013
+....
+
+From this demonstration we can see that Jetty started successfully as a Unix Service from the `/opt/jetty/jetty-distribution-{VERSION}` directory.
+
+This configuration works well but it is running Jetty as the root user.
+
+==== Practical Setup of a Jetty Service
+
+There are various ways this can be accomplished, mostly depending on your Unix environment (and possibly corporate policies).
+
+The techniques outlined here assume an installation on Linux (demonstrated on Ubuntu 12.04.3 LTS).
+
+Prepare some empty directories to work with.
+
+[source, screen, subs="{sub-order}"]
+....
+# mkdir -p /opt/jetty
+# mkdir -p /opt/web/mybase
+# mkdir -p /opt/jetty/temp
+....
+
+The directory purposes are as follows:
+
+/opt/jetty::
+Where the Jetty Distribution will be unpacked into
+/opt/web/mybase::
+Where your specific set of webapps will be located, including all of the configuration required of the server to make them operational.
+/opt/jetty/temp::
+This is the temporary directory assigned to Java by the Service Layer (this is what Java sees as the `java.io.tmpdir` System Property).
++
+This is intentionally kept separate from the standard temp directory of `/tmp`, as this location doubles as the Servlet Spec work directory.
+It is our experience that the standard temp directory is often managed by various cleanup scripts that wreak havoc on a long running Jetty server.
+
+Jetty 9.3 requires Java 8 (or greater) to run.
+Make sure you have it installed.
+
+[source, screen, subs="{sub-order}"]
+....
+# apt-get install openjdk-7-jdk
+....
+
+Or download Java 7 from: http://www.oracle.com/technetwork/java/javase/downloads/index.html
+
+[source, screen, subs="{sub-order}"]
+....
+# java -version
+java version "1.6.0_27"
+OpenJDK Runtime Environment (IcedTea6 1.12.6) (6b27-1.12.6-1ubuntu0.12.04.2)
+OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)
+
+# update-alternatives --list java
+/usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java
+/usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java
+
+# update-alternatives --config java
+There are 2 choices for the alternative java (providing /usr/bin/java).
+
+  Selection    Path                                            Priority   Status
+------------------------------------------------------------
+* 0            /usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java   1061      auto mode
+  1            /usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java   1061      manual mode
+  2            /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java   1051      manual mode
+
+Press enter to keep the current choice[*], or type selection number: 2
+update-alternatives: using /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java to provide /usr/bin/java (java) in manual mode.
+
+# java -version
+java version "1.7.0_25"
+OpenJDK Runtime Environment (IcedTea 2.3.10) (7u25-2.3.10-1ubuntu0.12.04.2)
+OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)
+....
+
+It is recommended that you create a user to specifically run Jetty.
+This user should have the minimum set of privileges needed to run Jetty.
+
+[source, screen, subs="{sub-order}"]
+....
+# useradd --user-group --shell /bin/false --home-dir /opt/jetty/temp jetty
+....
+
+This will create a user called `jetty`, belonging to the group called `jetty`, with no shell access (aka `/bin/false`), and home directory at `/opt/jetty/temp`.
+
+Download a copy of the Jetty distribution from the link:#jetty-downloading[Official Eclipse Download Site]
+
+Unpack it into place.
+
+[source, screen, subs="{sub-order}"]
+....
+[/opt/jetty]# tar -zxf /home/user/Downloads/jetty-distribution-{VERSION}.tar.gz
+[/opt/jetty]# ls -F
+jetty-distribution-{VERSION}/
+[/opt/jetty]# mkdir /opt/jetty/temp
+....
+
+It might seem strange or undesirable to unpack the first portion of the jetty-distribution directory name too.
+But starting with Jetty 9.1 the split between `${jetty.home}` and `${jetty.base}` allows for easier upgrades of Jetty itself while isolating your webapp specific configuration.
+For more information on the Jetty home and base concepts see the section on managing a Jetty installation <<startup-base-and-home, earlier in this Chapter.>>
+
+The `/opt/jetty/temp` directory is created as a durable place for Jetty to use for temp and working directories.
+Many Unix systems will periodically clean out the /tmp directory, this behavior is undesired in a Servlet container and has been known to cause problems.
+This durable directory at `/opt/jetty/temp` solves for that behavior.
+
+The directory at `/opt/web/mybase` is going to be a `${jetty.base}`, so lets configure it to hold your webapp and its configuration.
+
+[TIP]
+--
+In past versions of Jetty, you would configure / modify / add to the `jetty-distribution` directory directly.
+While this is still supported, we encourage you to setup a proper `${jetty.base}` directory, as it will benefit you with easier `jetty-distribution` upgrades in the future.
+--
+
+[source, screen, subs="{sub-order}"]
+....
+# cd /opt/web/mybase/
+[/opt/web/mybase]# ls
+[/opt/web/mybase]# java -jar /opt/jetty/jetty-distribution-{VERSION}/start.jar \
+   --add-to-start=deploy,http,logging
+WARNING: deploy          initialised in ${jetty.base}/start.ini (appended)
+WARNING: deploy          enabled in     ${jetty.base}/start.ini
+WARNING: server          initialised in ${jetty.base}/start.ini (appended)
+WARNING: server          enabled in     ${jetty.base}/start.ini
+WARNING: http            initialised in ${jetty.base}/start.ini (appended)
+WARNING: http            enabled in     ${jetty.base}/start.ini
+WARNING: server          enabled in     ${jetty.base}/start.ini
+WARNING: logging         initialised in ${jetty.base}/start.ini (appended)
+WARNING: logging         enabled in     ${jetty.base}/start.ini
+[/opt/web/mybase]# ls -F
+start.ini  webapps/
+....
+
+At this point you have configured your `/opt/web/mybase` to enable the following modules:
+
+deploy::
+This is the module that will perform deployment of web applications (WAR files or exploded directories), or Jetty IoC XML context deployables, from the `/opt/web/mybase/webapps` directory.
+http::
+This sets up a single Connector that listens for basic HTTP requests.
++
+See the created `start.ini` for configuring this connector.
+logging::
+When running Jetty as a service it is very important to have logging enabled.
+This module will enable the basic STDOUT and STDERR capture logging to the `/opt/web/mybase/logs/` directory.
+
+See xref:start-jar[] for more details and options on setting up and configuring a `${jetty.base}` directory.
+
+Copy your war file into place.
+
+[source, screen, subs="{sub-order}"]
+....
+# cp /home/user/projects/mywebsite.war /opt/web/mybase/webapps/
+....
+
+Most service installations will want Jetty to run on port 80, now is the opportunity to change this from the default value of `8080` to `80`.
+
+Edit the `/opt/web/mybase/start.ini` and change the `jetty.http.port` value.
+
+[source, screen, subs="{sub-order}"]
+....
+# grep jetty.http.port /opt/web/mybase/start.ini
+jetty.port=80
+....
+
+Change the permissions on the Jetty distribution and webapp directories so that the user you created can access it.
+
+[source, screen, subs="{sub-order}"]
+....
+# chown --recursive jetty /opt/jetty
+# chown --recursive jetty /opt/web/mybase
+....
+
+Next we need to make the Unix System aware that we have a new Jetty Service that can be managed by the standard `service` calls.
+
+[source, screen, subs="{sub-order}"]
+....
+# cp /opt/jetty/jetty-distribution-{VERSION}/bin/jetty.sh /etc/init.d/jetty
+# echo "JETTY_HOME=/opt/jetty/jetty-distribution-{VERSION}" > /etc/default/jetty
+# echo "JETTY_BASE=/opt/web/mybase" >> /etc/default/jetty
+# echo "TMPDIR=/opt/jetty/temp" >> /etc/default/jetty
+....
+
+Test out the configuration:
+
+[source, screen, subs="{sub-order}"]
+....
+# service jetty status
+Checking arguments to Jetty:
+START_INI      =  /opt/web/mybase/start.ini
+JETTY_HOME     =  /opt/jetty/jetty-distribution-{VERSION}
+JETTY_BASE     =  /opt/web/mybase
+JETTY_CONF     =  /opt/jetty/jetty-distribution-{VERSION}/etc/jetty.conf
+JETTY_PID      =  /var/run/jetty.pid
+JETTY_START    =  /opt/jetty/jetty-distribution-{VERSION}/start.jar
+JETTY_LOGS     =  /opt/web/mybase/logs
+CLASSPATH      =
+JAVA           =  /usr/bin/java
+JAVA_OPTIONS   =  -Djetty.state=/opt/web/mybase/jetty.state
+       -Djetty.logs=/opt/web/mybase/logs
+       -Djetty.home=/opt/jetty/jetty-distribution-{VERSION}
+       -Djetty.base=/opt/web/mybase
+       -Djava.io.tmpdir=/opt/jetty/temp
+JETTY_ARGS     =  jetty-logging.xml jetty-started.xml
+RUN_CMD        =  /usr/bin/java
+       -Djetty.state=/opt/web/mybase/jetty.state
+       -Djetty.logs=/opt/web/mybase/logs
+       -Djetty.home=/opt/jetty/jetty-distribution-{VERSION}
+       -Djetty.base=/opt/web/mybase
+       -Djava.io.tmpdir=/opt/jetty/temp
+       -jar /opt/jetty/jetty-distribution-{VERSION}/start.jar
+       jetty-logging.xml
+       jetty-started.xml
+....
+
+You now have a configured `${jetty.base}` in `/opt/web/mybase` and a jetty-distribution in `/opt/jetty/jetty-distribution-{VERSION}`, along with the service level files necessary to start the service.
+
+Test the service to make sure it starts up and runs successfully.
+
+[source, screen, subs="{sub-order}"]
+....
+# service jetty start
+Starting Jetty: OK Wed Nov 20 12:35:28 MST 2013
+
+# service jetty check
+..(snip)..
+Jetty running pid=2958
+
+[/opt/web/mybase]# ps u 2958
+USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
+jetty     2958  5.3  0.1 11179176 53984 ?      Sl   12:46   0:00 /usr/bin/java -Djetty...
+....
+
+You should now have your server running. Try it out
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/startup-windows-service.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/startup-windows-service.adoc
new file mode 100644
index 0000000..662b6bc
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/startup-windows-service.adoc
@@ -0,0 +1,296 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[startup-windows-service]]
+=== Startup via Windows Service
+
+There are no components that ship with the Jetty Distribution to make it a formal Windows Service.
+
+However, we recommend the use of https://commons.apache.org/proper/commons-daemon/procrun.html[Apache ProcRun's Daemon].
+
+The techniques outlined here are based on Windows 7 (64-bit), using JDK 8 (64-bit), running on an Intel i7 architecture machine.
+
+Prepare some empty directories to work with.
+
+[source, screen, subs="{sub-order}"]
+....
+C:\> mkdir opt
+C:\> cd opt
+C:\opt> mkdir jetty
+C:\opt> mkdir logs
+C:\opt> mkdir myappbase
+C:\opt> mkdir temp
+C:\opt> dir
+ Volume in drive C has no label.
+ Volume Serial Number is DEAD-BEEF
+
+ Directory of C:\opt
+
+11/21/2013  04:06 PM    <DIR>          .
+11/21/2013  04:06 PM    <DIR>          ..
+11/21/2013  04:06 PM    <DIR>          jetty
+11/21/2013  04:06 PM    <DIR>          logs
+11/21/2013  04:06 PM    <DIR>          myappbase
+11/21/2013  04:06 PM    <DIR>          temp
+               0 File(s)              0 bytes
+....
+
+The directory purposes are as follows:
+
+C:\opt::
+Where the service layer utilities, scripts, and binaries will eventually be.
+C:\opt\logs::
+Where the logs for the service layer will put its own logs.
++
+Typically you will see the audit logs (install/update/delete), StdOutput, and StdError logs here.
+C:\opt\jetty::
+Where the Jetty Distribution will be unpacked into.
+C:\opt\myappbase::
+Where your specific set of webapps will be located, including all of the configuration required of the server to make them operational.
+C:\opt\temp::
+This is the temporary directory assigned to Java by the Service Layer (this is what Java sees as the `java.io.tmpdir` System Property).
++
+This is intentionally kept separate from the standard temp directories of Windows, as this location doubles as the Servlet Spec work directory.
+
+Or download Java 8 from: http://www.oracle.com/technetwork/java/javase/downloads/index.html
+
+[source, screen, subs="{sub-order}"]
+....
+C:\opt>java -version
+java version "1.7.0_45"
+Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
+Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
+....
+
+Download a copy of the ZIP distribution from the link:#jetty-downloading[Official Eclipse Download Site]
+
+Extract the contents of the `jetty-distribution-{VERSION}` directory to `C:\opt\jetty`
+
+Once complete, the contents of the `C:\opt\jetty` directory should look like this:
+
+[source, screen, subs="{sub-order}"]
+....
+C:\opt\jetty>dir
+ Volume in drive C has no label.
+ Volume Serial Number is C8CF-820B
+
+ Directory of C:\opt\jetty
+
+11/21/2013  12:13 PM    <DIR>          .
+11/21/2013  12:13 PM    <DIR>          ..
+11/21/2013  12:13 PM    <DIR>          bin
+11/21/2013  12:13 PM    <DIR>          demo-base
+11/21/2013  12:13 PM    <DIR>          etc
+11/21/2013  12:13 PM    <DIR>          lib
+11/21/2013  12:13 PM            30,012 license-eplv10-aslv20.html
+11/21/2013  12:13 PM    <DIR>          logs
+11/21/2013  12:13 PM    <DIR>          modules
+11/21/2013  12:13 PM             6,262 notice.html
+11/21/2013  12:13 PM             1,249 README.TXT
+11/21/2013  12:13 PM    <DIR>          resources
+11/21/2013  12:13 PM    <DIR>          start.d
+11/21/2013  12:13 PM             2,126 start.ini
+11/21/2013  12:13 PM            72,226 start.jar
+11/21/2013  12:13 PM           341,784 VERSION.txt
+11/21/2013  12:13 PM    <DIR>          webapps
+               6 File(s)        453,659 bytes
+              11 Dir(s)  306,711,420,928 bytes free
+....
+
+Download a copy of the https://commons.apache.org/proper/commons-daemon/binaries.html[Apache ProcRun] native binaries.
+
+You should have downloaded a file named `commons-daemon-1.0.15-bin-windows.zip` (the version might be different).
+Open the ZIP file and extract the `prunmgr.exe` and `prunsrv.exe` files into the `C:\opt` directory.
+
+Make sure to get the right version of `prunsrv.exe` for your environment.
+The ZIP file has both 32 bit and 64 bit versions of this file.
+
+Once you are complete, the contents of `C:\opt` directory should look like this:
+
+[source, screen, subs="{sub-order}"]
+....
+C:\opt> dir
+ Volume in drive C has no label.
+ Volume Serial Number is DEAD-BEEF
+
+ Directory of C:\opt
+
+11/21/2013  04:06 PM    <DIR>          .
+11/21/2013  04:06 PM    <DIR>          ..
+11/21/2013  04:06 PM    <DIR>          jetty
+11/21/2013  04:06 PM    <DIR>          logs
+11/21/2013  04:06 PM    <DIR>          myappbase
+11/21/2013  04:06 PM    <DIR>          temp
+11/21/2013  04:11 PM           104,448 prunmgr.exe
+11/21/2013  04:11 PM            80,896 prunsrv.exe
+               2 File(s)        185,344 bytes
+....
+
+Now it's time to setup your new `${jetty.base}` directory to have all of your WebApps and the configurations that they need.
+
+We'll start by specifying which modules we want to use (this will create a start.ini file and also create a few empty directories for you)
+
+[source, screen, subs="{sub-order}"]
+....
+C:\opt\myappbase>java -jar ..\jetty\start.jar --add-to-start=deploy,http,logging
+
+WARNING: deploy          initialised in ${jetty.base}\start.ini (appended)
+WARNING: deploy          enabled in     ${jetty.base}\start.ini
+MKDIR: ${jetty.base}\webapps
+WARNING: server          initialised in ${jetty.base}\start.ini (appended)
+WARNING: server          enabled in     ${jetty.base}\start.ini
+WARNING: http            initialised in ${jetty.base}\start.ini (appended)
+WARNING: http            enabled in     ${jetty.base}\start.ini
+WARNING: server          enabled in     ${jetty.base}\start.ini
+WARNING: logging         initialised in ${jetty.base}\start.ini (appended)
+WARNING: logging         enabled in     ${jetty.base}\start.ini
+MKDIR: ${jetty.base}\logs
+
+C:\opt\myappbase>dir
+ Volume in drive C has no label.
+ Volume Serial Number is C8CF-820B
+
+ Directory of C:\opt\myappbase
+
+11/21/2013  12:49 PM    <DIR>          .
+11/21/2013  12:49 PM    <DIR>          ..
+11/21/2013  12:49 PM    <DIR>          logs
+11/21/2013  12:49 PM             1,355 start.ini
+11/21/2013  12:49 PM    <DIR>          webapps
+               1 File(s)          1,355 bytes
+               4 Dir(s)  306,711,064,576 bytes free
+....
+
+At this point you have configured your `C:\opt\myappbase` to enable the following modules:
+
+deploy::
+This is the module that will perform deployment of web applications (WAR files or exploded directories), or Jetty IoC XML context deployables, from the `C:\opt\myappbase\webapps` directory.
+http::
+This sets up a single Connector that listens for basic HTTP requests.
++
+See the created `start.ini` for configuring this connector.
+logging::
+When running Jetty as a service it is very important to have logging enabled.
+This module will enable the basic STDOUT and STDERR capture logging to the `C:\opt\myappbase\logs` directory.
+
+See the section on xref:start-jar[] for more details and options on setting up and configuring a `${jetty.base}` directory.
+
+At this point you merely have to copy your WAR files into the `{$jetty.base}/webapps` directory.
+
+[source, screen, subs="{sub-order}"]
+....
+C:\opt\myappbase> copy C:\projects\mywebsite.war webapps\
+....
+
+At this point you should have your directories, Java, the Jetty distribution, and your webapp specifics setup and ready for operation.
+
+We will use the https://commons.apache.org/proper/commons-daemon/binaries.html[Apache ProcRun's prunsrv.exe] to install a Jetty Service.
+
+The basic command line syntax is outlined in the link above.
+
+A example `install-jetty-service.bat` is provided here as an example, based on the above directories.
+
+[source,bat]
+----
+@echo off
+set SERVICE_NAME=JettyService
+set JETTY_HOME=C:\opt\jetty
+set JETTY_BASE=C:\opt\myappbase
+set STOPKEY=secret
+set STOPPORT=50001
+
+set PR_INSTALL=C:\opt\prunsrv.exe
+
+@REM Service Log Configuration
+set PR_LOGPREFIX=%SERVICE_NAME%
+set PR_LOGPATH=C:\opt\logs
+set PR_STDOUTPUT=auto
+set PR_STDERROR=auto
+set PR_LOGLEVEL=Debug
+
+@REM Path to Java Installation
+set JAVA_HOME=C:\Program Files\Java\jdk1.7.0_45
+set PR_JVM=%JAVA_HOME%\jre\bin\server\jvm.dll
+set PR_CLASSPATH=%JETTY_HOME%\start.jar;%JAVA_HOME%\lib\tools.jar
+
+@REM JVM Configuration
+set PR_JVMMS=128
+set PR_JVMMX=512
+set PR_JVMSS=4000
+set PR_JVMOPTIONS=-Duser.dir="%JETTY_BASE%";-Djava.io.tmpdir="C:\opt\temp";-Djetty.home="%JETTY_HOME%";-Djetty.base="%JETTY_BASE%"
+@REM Startup Configuration
+set JETTY_START_CLASS=org.eclipse.jetty.start.Main
+
+set PR_STARTUP=auto
+set PR_STARTMODE=java
+set PR_STARTCLASS=%JETTY_START_CLASS%
+set PR_STARTPARAMS=STOP.KEY="%STOPKEY%";STOP.PORT=%STOPPORT%
+
+@REM Shutdown Configuration
+set PR_STOPMODE=java
+set PR_STOPCLASS=%JETTY_START_CLASS%
+set PR_STOPPARAMS=--stop;STOP.KEY="%STOPKEY%";STOP.PORT=%STOPPORT%;STOP.WAIT=10
+
+"%PR_INSTALL%" //IS/%SERVICE_NAME% ^
+  --DisplayName="%SERVICE_NAME%" ^
+  --Install="%PR_INSTALL%" ^
+  --Startup="%PR_STARTUP%" ^
+  --LogPath="%PR_LOGPATH%" ^
+  --LogPrefix="%PR_LOGPREFIX%" ^
+  --LogLevel="%PR_LOGLEVEL%" ^
+  --StdOutput="%PR_STDOUTPUT%" ^
+  --StdError="%PR_STDERROR%" ^
+  --JavaHome="%JAVA_HOME%" ^
+  --Jvm="%PR_JVM%" ^
+  --JvmMs="%PR_JVMMS%" ^
+  --JvmMx="%PR_JVMMX%" ^
+  --JvmSs="%PR_JVMSS%" ^
+  --JvmOptions="%PR_JVMOPTIONS%" ^
+  --Classpath="%PR_CLASSPATH%" ^
+  --StartMode="%PR_STARTMODE%" ^
+  --StartClass="%JETTY_START_CLASS%" ^
+  --StartParams="%PR_STARTPARAMS%" ^
+  --StopMode="%PR_STOPMODE%" ^
+  --StopClass="%PR_STOPCLASS%" ^
+  --StopParams="%PR_STOPPARAMS%"
+
+if not errorlevel 1 goto installed
+echo Failed to install "%SERVICE_NAME%" service.  Refer to log in %PR_LOGPATH%
+goto end
+
+:installed
+echo The Service "%SERVICE_NAME%" has been installed
+
+:end
+----
+
+Configuration's of note in this batch file:
+
+SERVICE_NAME::
+This is the name of the service that Windows sees.
+The name in the Services window will show this name.
+STOPKEY::
+This is the secret key (password) for the ShutdownMonitor, used to issue a formal command to stop the server.
+STOPPORT::
+The port that the Shutdown Monitor listens on for the stop command.
++
+If you have multiple Jetty servers on the same machine, this port will need to be different for each Service.
+
+Once you have run `prunsrv.exe //IS/<service-name>` (done for you in the above batch file) to install the service, you can use the standard Windows utilities to manage (start/stop/restart) the Jetty service.
+
+Open the Service View and start your service.
+
+image:images/windows-service-jetty.png[image,width=576]
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/startup-xml-config.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/startup-xml-config.adoc
new file mode 100644
index 0000000..9c428f7
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/startup-xml-config.adoc
@@ -0,0 +1,25 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[startup-xml-config]]
+=== Managing XML Based Startup Configuration
+
+When you see XML files on the command line for startup of Jetty, they are always part of the Jetty IoC Configuration mechanism.
+
+Internally, Jetty uses these XML files to build up Jetty with the features that you wan to use.
+
+The module mechanism present in Jetty determines the load order of the XML files.
+The Jetty Base and Jetty Home resolution logic also applies, which allows you to override a XML file declared by a module with your XML by simply having the same named XML in your `${jetty.base}/etc` directory location.
diff --git a/jetty-documentation/src/main/asciidoc/administration/tuning/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/tuning/chapter.adoc
new file mode 100644
index 0000000..87f637b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/tuning/chapter.adoc
@@ -0,0 +1,28 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[optimizing]]
+== Optimizing Jetty
+
+There are many ways to optimize Jetty which vary depending on the situation.
+Are you trying to optimize for number of requests within a given amount of time?
+Are you trying to optimize the serving of static content?
+Do you have a large bit of hardware that you want to give entirely over to Jetty to use to its heart's delight?
+This chapter examines a few of the many different ways to optimize Jetty.
+
+include::garbage-collection.adoc[]
+include::high-load.adoc[]
+include::limit-load.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/administration/tuning/garbage-collection.adoc b/jetty-documentation/src/main/asciidoc/administration/tuning/garbage-collection.adoc
new file mode 100644
index 0000000..d438181
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/tuning/garbage-collection.adoc
@@ -0,0 +1,73 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[garbage-collection]]
+=== Garbage Collection
+
+Tuning the JVM garbage collection (GC) can greatly improve Jetty performance.
+Specifically, you can avoid pauses while the system performs full garbage collections.
+Optimal tuning of the GC depends on the behavior of the application and requires detailed analysis, however there are general recommendations.
+
+See official https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/[Java 8 Garbage Collection documentation] for further assistance.
+
+[[tuning-examples]]
+==== Tuning Examples
+
+These options are general to the Oracle JVM, and work in a Java 8 installation.
+They provide good information about how your JVM is running; based on that initial information, you can then tune more finely.
+
+The most important thing you can do for yourself when considering GC is to capture the GC activity so that you can analyze what is happening and how often it happens.
+
+[source,screen, subs="{sub-order}"]
+....
+-verbose:gc
+-Xloggc:/path/to/myjettybase/logs/gc.log
+-XX:+PrintGCDateStamps
+-XX:+PrintGCTimeStamps
+-XX:+PrintGCDetails
+-XX:+PrintTenuringDistribution
+-XX:+PrintCommandLineFlags
+-XX:+PrintReferenceGC
+-XX:+PrintAdaptiveSizePolicy
+....
+
+There are not many recommended options for GC that can apply to nearly all users.
+
+However, the most obvious one is to disable explicit GC (this is performed regularly by RMI and can introduce an abnormal amount of GC pauses).
+
+[source,screen, subs="{sub-order}"]
+....
+-XX:+DisableExplicitGC
+....
+
+Before you apply any other GC tuning options, monitor your GC logs to see if https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html[tuning the CMS] makes sense for your environment.
+
+A common setup for those just starting out with GC tuning is included below for reference.
+
+____
+[CAUTION]
+This example configuration below could have a negative effect on your application performance, so continued monitoring of your GC log before and after is highly recommended to know if the configuration was beneficial or not.
+____
+
+[source,screen, subs="{sub-order}"]
+....
+-XX:MaxGCPauseMillis=250
+-XX:+UseConcMarkSweepGC
+-XX:ParallelCMSThreads=2
+-XX:+CMSClassUnloadingEnabled
+-XX:+UseCMSCompactAtFullCollection
+-XX:CMSInitiatingOccupancyFraction=80
+....
diff --git a/jetty-documentation/src/main/asciidoc/administration/tuning/high-load.adoc b/jetty-documentation/src/main/asciidoc/administration/tuning/high-load.adoc
new file mode 100644
index 0000000..2159d43
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/tuning/high-load.adoc
@@ -0,0 +1,157 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[high-load]]
+=== High Load
+
+Configuring Jetty for high load, whether for load testing or for production, requires that the operating system, the JVM, Jetty, the application, the network and the load generation all be tuned.
+
+==== Load Generation for Load Testing
+
+Machines handling load generation must have their OS, JVM, etc., tuned just as much as the server machines.
+
+The load generation should not be over the local network on the server machine, as this has unrealistic performance and latency as well as different packet sizes and transport characteristics.
+
+The load generator should generate a realistic load.
+Avoid the following pitfalls:
+
+* A common mistake is that load generators often open relatively few connections that are extremely busy sending as many requests as possible over each connection.
+This causes the measured throughput to be limited by request latency (see http://blogs.webtide.com/gregw/entry/lies_damned_lies_and_benchmarks[Lies, Damned Lies and Benchmarks] for an analysis of such an issue).
+* Another common mistake is to use TCP/IP for a single request, and to open many, many short-lived connections.
+This often results in accept queues filling and limitations due to file descriptor and/or port starvation.
+* A load generator should model the traffic profile from the normal clients of the server.
+For browsers, this is often between two and six connections that are mostly idle and that are used in sporadic bursts with read times in between.
+The connections are typically long held HTTP/1.1 connections.
+* Load generators should be written in asynchronously so that a limited number of threads does not restrict the maximum number of users that can be simulated.
+If the generator is not asynchronous, a thread pool of 2000 may only be able to simulate 500 or fewer users.
+The Jetty `HttpClient` is an ideal choice for building a load generator as it is asynchronous and can simulate many thousands of connections (see the CometD Load Tester for a good example of a realistic load generator).
+
+==== Operating System Tuning
+
+Both the server machine and any load generating machines need to be tuned to support many TCP/IP connections and high throughput.
+
+===== Linux
+
+Linux does a reasonable job of self-configuring TCP/IP, but there are a few limits and defaults that you should increase.
+You can configure most of these in `/etc/security/limits.conf` or via `sysctl`.
+
+====== TCP Buffer Sizes
+
+You should increase TCP buffer sizes to at least 16MB for 10G paths and tune the auto-tuning (keep in mind that you need to consider buffer bloat).
+
+[source, screen, subs="{sub-order}"]
+....
+$ sysctl -w net.core.rmem_max=16777216
+$ sysctl -w net.core.wmem_max=16777216
+$ sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
+$ sysctl -w net.ipv4.tcp_wmem="4096 16384 16777216"
+....
+
+====== Queue Sizes
+
+`net.core.somaxconn` controls the size of the connection listening queue.
+The default value is 128.
+If you are running a high-volume server and connections are getting refused at a TCP level, you need to increase this value.
+This setting can take a bit of finesse to get correct: if you set it too high, resource problems occur as it tries to notify a server of a large number of connections, and many remain pending, but if you set it too low, refused connections occur.
+
+[source, screen, subs="{sub-order}"]
+....
+ $ sysctl -w net.core.somaxconn=4096
+....
+
+The `net.core.netdev_max_backlog` controls the size of the incoming packet queue for upper-layer (Java) processing.
+The default (2048) may be increased and other related parameters adjusted with:
+
+[source, screen, subs="{sub-order}"]
+....
+$ sysctl -w net.core.netdev_max_backlog=16384
+$ sysctl -w net.ipv4.tcp_max_syn_backlog=8192
+$ sysctl -w net.ipv4.tcp_syncookies=1
+....
+
+====== Ports
+
+If many outgoing connections are made (for example, on load generators), the operating system might run low on ports.
+Thus it is best to increase the port range, and allow reuse of sockets in `TIME_WAIT`:
+
+[source, screen, subs="{sub-order}"]
+....
+$ sysctl -w net.ipv4.ip_local_port_range="1024 65535"
+$ sysctl -w net.ipv4.tcp_tw_recycle=1
+....
+
+====== File Descriptors
+
+Busy servers and load generators may run out of file descriptors as the system defaults are normally low.
+These can be increased for a specific user in `/etc/security/limits.conf`:
+
+....
+theusername            hard nofile     40000
+theusername            soft nofile     40000
+....
+
+====== Congestion Control
+
+Linux supports pluggable congestion control algorithms.
+To get a list of congestion control algorithms that are available in your kernel run:
+
+[source, screen, subs="{sub-order}"]
+....
+$ sysctl net.ipv4.tcp_available_congestion_control
+....
+
+If cubic and/or htcp are not listed, you need to research the control algorithms for your kernel.
+You can try setting the control to cubic with:
+
+[source, screen, subs="{sub-order}"]
+....
+$ sysctl -w net.ipv4.tcp_congestion_control=cubic
+....
+
+====== Mac OS
+
+Tips welcome.
+
+====== Windows
+
+Tips welcome.
+
+====== Network Tuning
+
+Intermediaries such as nginx can use a non-persistent HTTP/1.0 connection.
+Make sure to use persistent HTTP/1.1 connections.
+
+====== JVM Tuning
+
+* Tune the link:#tuning-examples[Garbage Collection]
+* Allocate sufficient memory
+* Use the -server option
+* Jetty Tuning
+
+//====== Connectors
+
+====== Acceptors
+
+The standard rule of thumb for the number of Accepters to configure is one per CPU on a given machine.
+
+====== Low Resource Limits
+
+Must not be configured for less than the number of expected connections.
+
+====== Thread Pool
+
+Configure with goal of limiting memory usage maximum available.
+Typically this is >50 and <500
diff --git a/jetty-documentation/src/main/asciidoc/administration/tuning/limit-load.adoc b/jetty-documentation/src/main/asciidoc/administration/tuning/limit-load.adoc
new file mode 100644
index 0000000..e42b6e7
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/tuning/limit-load.adoc
@@ -0,0 +1,41 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[limit-load]]
+=== Limiting Load
+
+To achieve optimal fair handling for all users of a server, it can be necessary to limit the resources that each user/connection can utilize so as to maximize throughput for the server or to ensure that the entire server runs within the limitations of it's runtime.
+
+==== Low Resources Monitor
+
+An instance of link:{JDURL}/org/eclipse/jetty/server/LowResourcesMonitor.html[LowResourcesMonitor] may be added to a Jetty server to monitor for low resources situations and to take action to limit the number of idle connections on the server.
+To configure the low resources monitor, you can enable the the `lowresources.mod` on the command line, which has the effect of including the following XML configuration:
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-server/src/main/config/etc/jetty-lowresources.xml[]
+----
+
+The monitor is configured with a period in milliseconds at which it will scan the server looking for a low resources condition, which may be one of:
+
+* If `monitorThreads` is configured as true and a connectors Executor is an instance of link:{JDURL}/org/eclipse/jetty/util/thread/ThreadPool.html[ThreadPool], then its `isLowOnThreads()` method is used to detect low resources.
+* If `maxConnections` is configured to a number >0 then if the total number of connections from all monitored connectors exceeds this value, then low resources state is entered.
+* If the `maxMemory` field is configured to a number of bytes >0 then if the JVMs total memory minus its idle memory exceeds this value, then low resources state is entered.
+
+Once low resources state is detected, then the monitor will iterate over all existing connections and set their `IdleTimeout` to its configured `lowResourcesIdleTimeout` in milliseconds.
+This allows the idle time of existing connections to be reduced so that the connection is quickly closed if no further request are received.
+
+If the low resources state persists longer than the time in milliseconds configured for the `maxLowResourcesTime` field, the the `lowResourcesIdleTimeout` is repeatedly applied so that new connections as well as existing connections will be limited.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/connectors/chapter.adoc b/jetty-documentation/src/main/asciidoc/configuring/connectors/chapter.adoc
new file mode 100644
index 0000000..13369de
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/connectors/chapter.adoc
@@ -0,0 +1,24 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-connectors]]
+== Configuring Jetty Connectors
+
+This chapter discusses various options for configuring Jetty connectors.
+
+include::configuring-connectors.adoc[]
+include::configuring-ssl.adoc[]
+include::setting-port80-access-for-non-root-user.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-connectors.adoc b/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-connectors.adoc
new file mode 100644
index 0000000..95b73372
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-connectors.adoc
@@ -0,0 +1,261 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-connectors]]
+=== Connector Configuration Overview
+
+Connectors are the mechanism through which Jetty accepts network connections for various protocols.
+Configuring a connector is a combination of configuring the following:
+
+* Network parameters on the connector itself (for example: the listening port).
+* Services the connector uses (for example: executors, schedulers).
+* Connection factories that instantiate and configure the protocol for an accepted connection.
+
+Jetty primarily uses a single connector type called link:{JDURL}/org/eclipse/jetty/server/ServerConnector.html[ServerConnector].
+
+____
+[NOTE]
+Prior to Jetty 9, the type of the connector specified both the protocol and the implementation used; for example, selector-based non blocking I/O vs blocking I/O, or SSL connector vs non-SSL connector.
+Jetty 9 has a single selector-based non-blocking I/O connector, and a collection of link:{JDURL}/org/eclipse/jetty/server/ConnectionFactory.html[`ConnectionFactories`] now configure the protocol on the connector.
+____
+
+The standard Jetty distribution comes with the following Jetty XML files that create and configure connectors; you should examine them as you read this section:
+
+link:{GITBROWSEURL}/jetty-server/src/main/config/etc/jetty-http.xml[`jetty-http.xml`]::
+  Instantiates a link:{JDURL}/org/eclipse/jetty/server/ServerConnector.html[`ServerConnector`] that accepts HTTP connections (that may be upgraded to WebSocket connections).
+link:{GITBROWSEURL}/jetty-server/src/main/config/etc/jetty-ssl.xml[`jetty-ssl.xml`]::
+  Instantiates a link:{JDURL}/org/eclipse/jetty/server/ServerConnector.html[`ServerConnector`] that accepts SSL/TLS connections.
+  On it's own, this connector is not functional and requires one or more of the following files to also be configured to add  link:{JDURL}/org/eclipse/jetty/server/ConnectionFactory.html[`ConnectionFactories`] to make the connector functional.
+link:{GITBROWSEURL}/jetty-server/src/main/config/etc/jetty-https.xml[`jetty-https.xml`]::
+  Adds a link:{JDURL}/org/eclipse/jetty/server/HttpConnectionFactory.html[`HttpConnectionFactory`] to the link:{JDURL}/org/eclipse/jetty/server/ServerConnector.html[`ServerConnector`]  configured by `jetty-ssl.xml` which combine to provide support for HTTPS.
+link:{GITBROWSEURL}/jetty-http2/http2-server/src/main/config/etc/jetty-http2.xml[`jetty-http2.xml`]::
+  Adds a link:{JDURL}/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.html[`Http2ServerConnectionFactory`] to the  link:{JDURL}/org/eclipse/jetty/server/ServerConnector.html[`ServerConnector`] configured by `jetty-ssl.xml` to support the http2 protocol. Also prepends either `protonego-alpn.xml` or `protonego-npn.xml` so that the next protocol can be negotiated, which allows the same SSL port to handle multiple protocols.
+link:{GITBROWSEURL}/jetty-alpn/jetty-alpn-server/src/main/config/etc/jetty-alpn.xml[`jetty-alpn.xml`]::
+  Adds an link:{JDURL}/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.html[`ALPNServerConnectionFactory`] to the link:{JDURL}/org/eclipse/jetty/server/ServerConnector.html[`ServerConnector`] configured by `jetty-ssl.xml` which allows the one SSL connector to support multiple protocols with the ALPN extension used to select the protocol to be used for each connection.
+
+Typically connectors require very little configuration aside from setting the listening port (see link:#jetty-connectors-network-settings[Network Settings]), and enabling `X-Forwarded-For` customization when applicable. (see link:#jetty-connectors-http-configuration[HTTP Configuration]).
+Additional settings are for expert configuration only.
+
+==== Constructing a ServerConnector
+
+The services a link:{JDURL}/org/eclipse/jetty/server/ServerConnector.html[`ServerConnector`] instance uses are set by constructor injection and once instantiated cannot be changed.
+Many of the services may be defaulted with null or 0 values so that a reasonable default is used, thus for most purposes only the Server and the connection factories need to be passed to the connector constructor. In Jetty XML (that is, in link:{SRCDIR}/jetty-server/src/main/config/etc/jetty-http.xml[`jetty-http.xml`]) you can do this by:
+
+[source, xml, subs="{sub-order}"]
+----
+<New class="org.eclipse.jetty.server.ServerConnector">
+  <Arg name="server"><Ref refid="Server" /></Arg>
+  <Arg name="factories">
+    <Array type="org.eclipse.jetty.server.ConnectionFactory">
+      <!-- insert one or more factories here -->
+    </Array>
+  </Arg>
+  <!-- set connector fields here -->
+</New>
+----
+
+You can see the other arguments that can be passed when constructing a `ServerConnector` in the link:{JDURL}/org/eclipse/jetty/server/ServerConnector.html#ServerConnector%28org.eclipse.jetty.server.Server,%20java.util.concurrent.Executor,%20org.eclipse.jetty.util.thread.Scheduler,%20org.eclipse.jetty.io.ByteBufferPool,%20int,%20int,%20org.eclipse.jetty.server.ConnectionFactory...%29[Javadoc].
+Typically the defaults are sufficient for almost all deployments.
+
+[[jetty-connectors-network-settings]]
+==== Network Settings.
+
+You configure connector network settings by calling setters on the connector before it is started.
+For example, you can set the port with the Jetty XML:
+
+[source, xml, subs="{sub-order}"]
+----
+<New class="org.eclipse.jetty.server.ServerConnector">
+  <Arg name="server"><Ref refid="Server" /></Arg>
+  <Arg name="factories"><!-- insert one or more factories here --></Arg>
+
+  <Set name="port">8080</Set>
+</New>
+----
+
+Values in Jetty XML can also be parameterized so that they may be passed from property files or set on the command line.
+Thus typically the port is set within Jetty XML, but uses the `Property` element to be customizable:
+
+[source, xml, subs="{sub-order}"]
+----
+<New class="org.eclipse.jetty.server.ServerConnector">
+  <Arg name="server"><Ref refid="Server" /></Arg>
+  <Arg name="factories"><!-- insert one or more factories here --></Arg>
+
+  <Set name="port"><Property name="jetty.http.port" default="8080"/></Set>
+</New>
+----
+
+The network settings that you can set on the link:{JDURL}/org/eclipse/jetty/server/ServerConnector.html[`ServerConnector`] include:
+
+.Connector Configuration
+[width="100%",cols="22%,78%",options="header",]
+|=======================================================================
+|Field |Description
+|host |The network interface this connector binds to as an IP address or a hostname.
+If null or 0.0.0.0, bind to all interfaces.
+
+|port |The configured port for the connector or 0 a random available port may be used (selected port available via `getLocalPort()`).
+
+|idleTimeout |The time in milliseconds that the connection can be idle before it is closed.
+
+|defaultProtocol |The name of the default protocol used to select a `ConnectionFactory` instance. This defaults to the first `ConnectionFactory` added to the connector.
+
+|stopTimeout |The time in milliseconds to wait before gently stopping a connector.
+
+|acceptQueueSize |The size of the pending connection backlog.
+The exact interpretation is JVM and operating system specific and you can ignore it.
+Higher values allow more connections to wait pending an acceptor thread.
+Because the exact interpretation is deployment dependent, it is best to keep this value as the default unless there is a specific connection issue for a specific OS that you need to address.
+
+|reuseAddress |Allow the server socket to be rebound even if in http://www.ssfnet.org/Exchange/tcp/tcpTutorialNotes.html[TIME_WAIT].
+For servers it is typically OK to leave this as the default true.
+
+|soLingerTime |A value greater than zero sets the socket http://stackoverflow.com/questions/3757289/tcp-option-so-linger-zero-when-its-required[SO_LINGER] value in milliseconds.
+Jetty attempts to gently close all TCP/IP connections with proper half close semantics, so a linger timeout should not be required and thus the default is -1.
+|=======================================================================
+
+[[jetty-connectors-http-configuration]]
+==== HTTP Configuration
+
+The link:{JDURL}/org/eclipse/jetty/server/HttpConfiguration.html[HttpConfiguration] class holds the configuration for link:{JDURL}/org/eclipse/jetty/server/HttpChannel.html[`HTTPChannel`]s, which you can create 1:1 with each HTTP connection or 1:n on a multiplexed HTTP/2 connection.
+Thus a `HTTPConfiguration` object is injected into both the HTTP and HTTP/2 connection factories.
+To avoid duplicate configuration, the standard Jetty distribution creates the common `HttpConfiguration` instance in link:{SRCDIR}/jetty-server/src/main/config/etc/jetty.xml[`jetty.xml`], which is a `Ref` element then used in link:{SRCDIR}/jetty-server/src/main/config/etc/jetty-http.xml[`jetty-http.xml`], link:{SRCDIR}/jetty-server/src/main/config/etc/jetty-https.xml[`jetty-https.xml`] and in link:{SRCDIR}/jetty-http2/http2-server/src/main/config/etc/jetty-http2.xml[`jetty-http2.xml`].
+
+A typical configuration of link:{JDURL}/org/eclipse/jetty/server/HttpConfiguration.html[HttpConfiguration] is:
+
+[source, xml, subs="{sub-order}"]
+----
+<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
+  <Set name="secureScheme">https</Set>
+  <Set name="securePort"><Property name="jetty.ssl.port" default="8443" /></Set>
+  <Set name="outputBufferSize">32768</Set>
+  <Set name="requestHeaderSize">8192</Set>
+  <Set name="responseHeaderSize">8192</Set>
+</New>
+----
+
+This example HttpConfiguration may be used by reference to the ID "`httpConfig`":
+
+[source, xml, subs="{sub-order}"]
+----
+<Call name="addConnector">
+  <Arg>
+    <New class="org.eclipse.jetty.server.ServerConnector">
+      <Arg name="server"><Ref refid="Server" /></Arg>
+      <Arg name="factories">
+        <Array type="org.eclipse.jetty.server.ConnectionFactory">
+          <Item>
+            <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+              <Arg name="config"><Ref refid="httpConfig" /></Arg>
+            </New>
+          </Item>
+        </Array>
+      </Arg>
+      <!-- ... -->
+    </New>
+  </Arg>
+</Call>
+----
+
+For SSL based connectors (in `jetty-https.xml` and `jetty-http2.xml`), the common "`httpConfig`" instance is used as the basis to create an SSL specific configuration with ID "`sslHttpConfig`":
+
+[source, xml, subs="{sub-order}"]
+----
+<New id="sslHttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
+  <Arg><Ref refid="httpConfig"/></Arg>
+  <Call name="addCustomizer">
+    <Arg><New class="org.eclipse.jetty.server.SecureRequestCustomizer"/></Arg>
+  </Call>
+</New>
+----
+
+This adds a `SecureRequestCustomizer` which adds SSL Session IDs and certificate information as request attributes.
+
+==== SSL Context Configuration
+
+The SSL/TLS connectors for HTTPS and HTTP/2 require a certificate to establish a secure connection.
+Jetty holds certificates in standard JVM keystores and are configured as keystore and truststores on a link:{JDURL}/org/eclipse/jetty/util/ssl/SslContextFactory.html[`SslContextFactory`] instance that is injected into an link:{JDURL}/org/eclipse/jetty/server/SslConnectionFactory.html[`SslConnectionFactory`] instance.
+An example using the keystore distributed with Jetty (containing a self signed test certificate) is in link:{SRCDIR}/jetty-server/src/main/config/etc/jetty-https.xml[`jetty-https.xml`].
+Read more about SSL keystores in link:#configuring-ssl[Configuring SSL].
+
+==== Proxy / Load Balancer Connection Configuration
+
+Often a Connector needs to be configured to accept connections from an intermediary such as a Reverse Proxy and/or Load Balancer deployed in front of the server.
+In such environments, the TCP/IP connection terminating on the server does not originate from the client, but from the intermediary, so that the Remote IP and port number can be reported incorrectly in logs and in some circumstances the incorrect server address and port may be used.
+
+Thus Intermediaries typically implement one of several de facto standards to communicate to the server information about the orginal client connection terminating on the intermediary.
+Jetty supports the `X-Forwarded-For` header and the http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt[Proxy Protocol] mechanisms as described below.
+
+____
+[NOTE]
+The XML files in the Jetty distribution contain commented out examples of both the `X-Forwarded-For` and http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt[Proxy Protocol] mechanisms.
+When using those examples, it is recommended that the XML in the Jetty distribution is not edited.
+Rather the files should be copied into a Jetty base directory and then modified.
+____
+
+===== X-Forward-for Configuration
+
+The `X-Forwarded-for` header and associated headers are a de facto standard where intermediaries add HTTP headers to each request they forward to describe the originating connection.
+These headers can be interpreted by an instance of link:{JDURL}/org/eclipse/jetty/server/ForwardedRequestCustomizer.html[`ForwardedRequestCustomizer`] which can be added to a `HttpConfiguration` as follows:
+
+[source, xml, subs="{sub-order}"]
+----
+<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
+  <Set name="outputBufferSize">32768</Set>
+  <Set name="requestHeaderSize">8192</Set>
+  <Set name="responseHeaderSize">8192</Set>
+
+  <Call name="addCustomizer">
+    <Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
+  </Call>
+</New>
+----
+
+===== Proxy Protocol
+
+The http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt[Proxy Protocol] is a de facto standard created by HAProxy and used by environments such as Amazon Elastic Cloud.
+This mechanism is independent of any protocol, so it can be used for HTTP2, TLS etc.
+The information about the client connection is sent as a small data frame on each newly established connection.
+In Jetty, this protocol can be handled by the link:{JDURL}/org/eclipse/jetty/server/ProxyConnectionFactory.html[`ProxyConnectionFactory`] which parses the data frame and then instantiates the next `ConnectionFactory` on the connection with an end point that has been customized with the data obtained about the original client connection.
+The connection factory can be added to any link:{JDURL}/org/eclipse/jetty/server/ServerConnector.html[`ServerConnector`] and should be the first link:{JDURL}/org/eclipse/jetty/server/ConnectionFactory.html[`ConnectionFactory`].
+
+An example of adding the factory to a HTTP connector is:
+
+[source, xml, subs="{sub-order}"]
+----
+<Call name="addConnector">
+  <Arg>
+    <New class="org.eclipse.jetty.server.ServerConnector">
+      <Arg name="server"><Ref refid="Server" /></Arg>
+      <Arg name="factories">
+        <Array type="org.eclipse.jetty.server.ConnectionFactory">
+          <Item>
+            <New class="org.eclipse.jetty.server.ProxyConnectionFactory"/>
+          </Item>
+          <Item>
+            <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+              <Arg name="config"><Ref refid="httpConfig" /></Arg>
+            </New>
+          </Item>
+        </Array>
+      </Arg>
+      <Set name="host"><Property name="jetty.host" /></Set>
+      <Set name="port"><Property name="jetty.http.port" default="80" /></Set>
+    </New>
+  </Arg>
+</Call>
+----
diff --git a/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-ssl.adoc b/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-ssl.adoc
new file mode 100644
index 0000000..ff9cfca
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-ssl.adoc
@@ -0,0 +1,932 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-ssl]]
+=== Configuring SSL/TLS
+
+This document provides an overview of how to configure SSL and TLS for Jetty.
+
+[[tls-and-ssl-versions]]
+==== TLS and SSL versions
+
+Which browser/OS supports which protocols can be https://en.wikipedia.org/wiki/Transport_Layer_Security#Web_browsers[found on Wikipedia].
+
+* TLS v1.2: The protocol which should be used wherever possible.
+All CBC based ciphers are supported since Java 7, the new GCM modes are supported since Java 8.
+
+===== Older Protocols
+
+TLS v1.0, v1.1 and SSL v3 are no longer supported by default. If your Jetty implementation requires these protocols for legacy support, they can be enabled manually.
+
+____
+[NOTE]
+Once TLS v1.3 is released, there will be no workaround available for TLS v1.0 or v1.1.
+Plans for TLS v1.3 include banning ciphers with known vulnerabilities from being present at any level.
+It is recommended to upgrade any clients using these ciphers as soon as possible or face being locked into a outdated version of Jetty, Java or even OS.
+____
+
+By default, Jetty excludes these ciphers in the link:{GITBROWSEURL}/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java#L249-L256[`SslContextFactory`.]
+You can re-enable these by re-declaring the ciphers you want excluded in code:
+
+[source, java, subs="{sub-order}"]
+----
+SslContextFactory sslContextFactory = new SslContextFactory();
+sslContextFactory.setExcludeCipherSuites(
+            "^.*_(MD5|SHA|SHA1)$");
+----
+
+If, after making these changes, you still have issues using these ciphers they are likely being blocked at the JVM level.
+Locate the `$JAVA_HOME/jre/lib/security/` directory for the `java.security` file and examine it for any configuration that is excluding _ciphers_ or _algorithms_ (depending on the version of the JVM you are using the nomenclature may be different).
+
+[[understanding-certificates-and-keys]]
+==== Understanding Certificates and Keys
+
+Configuring SSL can be a confusing experience of keys, certificates, protocols and formats, thus it helps to have a reasonable understanding of the basics.
+The following links provide some good starting points:
+
+* Certificates:
+** http://en.tldp.org/HOWTO/SSL-Certificates-HOWTO/index.html[SSL Certificates HOWTO]
+** http://mindprod.com/jgloss/certificate.html[Mindprod Java Glossary: Certificates]
+* Keytool:
+** http://docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html[Keytool for Unix]
+** http://docs.oracle.com/javase/8/docs/technotes/tools/windows/keytool.html[Keytool for Windows]
+* Other tools:
+** https://www.ibm.com/developerworks/mydeveloperworks/groups/service/html/communityview?communityUuid=6fb00498-f6ea-4f65-bf0c-adc5bd0c5fcc[IBM Keyman]
+* OpenSSL:
+** http://www.openssl.org/support/faq.html[OpenSSL FAQ]
+
+[[openssl-vs-keytool]]
+===== OpenSSL vs. Keytool
+
+For testing, the `keytool` utility bundled with the JDK provides the simplest way to generate the key and certificate you need.
+
+You can also use the OpenSSL tools to generate keys and certificates, or to convert those that you have used with Apache or other servers.
+Since Apache and other servers commonly use the OpenSSL tool suite to generate and manipulate keys and certificates, you might already have some keys and certificates created by OpenSSL, or you might also prefer the formats OpenSSL produces.
+
+If you want the option of using the same certificate with Jetty or a web server such as Apache not written in Java, you might prefer to generate your private key and certificate with OpenSSL.
+
+[[configuring-jetty-for-ssl]]
+===== Configuring Jetty for SSL
+
+To configure Jetty for SSL, complete the tasks in the following sections:
+
+* xref:generating-key-pairs-and-certificates[]
+* xref:requesting-trusted-certificate[]
+* xref:loading-keys-and-certificates[]
+* xref:configuring-sslcontextfactory[]
+
+[[generating-key-pairs-and-certificates]]
+===== Generating Key Pairs and Certificates
+
+The simplest way to generate keys and certificates is to use the `keytool` application that comes with the JDK, as it generates keys and certificates directly into the keystore.
+See xref:generating-key-pairs-and-certificates-JDK-keytool[].
+
+If you already have keys and certificates, see xref:loading-keys-and-certificates[] to load them into a JSSE keystore.
+This section also applies if you have a renewal certificate to replace one that is expiring.
+
+The examples below generate only basic keys and certificates.
+You should read the full manuals of the tools you are using if you want to specify:
+
+* The key size
+* The certificate expiration date
+* Alternate security providers
+
+[[generating-key-pairs-and-certificates-JDK-keytool]]
+====== Generating Keys and Certificates with JDK's keytool
+
+The following command generates a key pair and certificate directly into file `keystore`:
+
+[source, screen, subs="{sub-order}"]
+----
+$ keytool -keystore keystore -alias jetty -genkey -keyalg RSA
+----
+
+____
+[NOTE]
+The DSA key algorithm certificate produces an error after loading several pages.
+In a browser, it displays a message "Could not establish an encrypted connection because certificate presented by localhost as an invalid signature."
+The solution is to use RSA for the key algorithm.
+____
+
+This command prompts for information about the certificate and for passwords to protect both the keystore and the keys within it.
+The only mandatory response is to provide the fully qualified host name of the server at the "first and last name" prompt.
+For example:
+
+[source, screen, subs="{sub-order}"]
+----
+$ keytool -keystore keystore -alias jetty -genkey -keyalg RSA -sigalg SHA256withRSA
+ Enter keystore password:  password
+ What is your first and last name?
+   [Unknown]:  jetty.eclipse.org
+ What is the name of your organizational unit?
+   [Unknown]:  Jetty
+ What is the name of your organization?
+   [Unknown]:  Mort Bay Consulting Pty. Ltd.
+ What is the name of your City or Locality?
+   [Unknown]:
+ What is the name of your State or Province?
+   [Unknown]:
+ What is the two-letter country code for this unit?
+   [Unknown]:
+ Is CN=jetty.eclipse.org, OU=Jetty, O=Mort Bay Consulting Pty. Ltd.,
+ L=Unknown, ST=Unknown, C=Unknown correct?
+   [no]:  yes
+
+ Enter key password for <jetty>
+         (RETURN if same as keystore password):
+ $
+----
+
+You now have the minimal requirements to run an SSL connection and could proceed directly to link:#configuring-sslcontextfactory[configure an SSL connector].
+However, the browser _will not_ trust the certificate you have generated, and prompts the user to this effect.
+While what you have at this point is often sufficient for testing, most public sites need a trusted certificate, which is demonstrated in the section link:#generating-csr-from-keytool[generating a CSR with keytool].
+
+If you want to use only a self signed certificate for some kind of internal admin panel add -validity <days> to the keytool call above, otherwise your certificate is only valid for one month.
+
+If you are using Java 8 or later, then you may also use the SAN extension to set one or more names that the certificate applies to:
+
+[source, screen, subs="{sub-order}"]
+----
+$ keytool -keystore keystore -alias jetty -genkey -keyalg RSA -sigalg SHA256withRSA -ext 'SAN=dns:jetty.eclipse.org,dns:*.jetty.org'
+ ...
+----
+
+[[generating-keys-and-certificates-openssl]]
+====== Generating Keys and Certificates with OpenSSL
+
+The following command generates a key pair in the file `jetty.key`:
+
+[source, screen, subs="{sub-order}"]
+----
+$ openssl genrsa -aes128 -out jetty.key
+----
+
+You might also want to use the `-rand` file argument to provide an arbitrary file that helps seed the random number generator.
+
+The following command generates a certificate for the key into the file `jetty.crt`:
+
+[source, screen, subs="{sub-order}"]
+----
+$ openssl req -new -x509 -newkey rsa:2048 -sha256 -key jetty.key -out jetty.crt
+----
+
+Adding -sha256 ensures to get a certificate with the now recommended SHA-256 signature algorithm.
+For the those with heightened security in mind, add -b4096 to get a 4069 bit key.
+
+The next command prompts for information about the certificate and for passwords to protect both the keystore and the keys within it.
+The only mandatory response is to provide the fully qualified host name of the server at the "Common Name" prompt. For example:
+
+[source, screen, subs="{sub-order}"]
+----
+$ openssl genrsa -aes128 -out jetty.key
+Generating RSA private key, 2048 bit long modulus
+..............+++
+......................................................................+++
+e is 65537 (0x10001)
+Enter pass phrase for jetty.key:
+Verifying - Enter pass phrase for jetty.key:
+
+$ openssl req -new -x509 -newkey rsa:2048 -sha256 -key jetty.key -out jetty.crt
+Enter pass phrase for jetty.key:
+You are about to be asked to enter information that will be incorporated into your certificate request.
+What you are about to enter is what is called a Distinguished Name or a DN.
+There are quite a few fields but you can leave some blank.
+For some fields there will be a default value.
+If you enter '.', the field will be left blank.
+
+Country Name (2 letter code) [AU]:
+State or Province Name (full name) [Some-State]:
+Locality Name (eg, city) []:
+Organization Name (eg, company) [Internet Widgits Pty Ltd]:Mort Bay Consulting Pty. Ltd.
+Organizational Unit Name (eg, section) []:Jetty
+Common Name (e.g. server FQDN or YOUR name) []:jetty.eclipse.org
+Email Address []:
+
+$
+----
+
+You now have the minimal requirements to run an SSL connection and could proceed directly to xref:loading-keys-and-certificates[] to load these keys and certificates into a JSSE keystore.
+However the browser _will not_ trust the certificate you have generated, and prompts the user to this effect.
+While what you have at this point is often sufficient for testing, most public sites need a trusted certificate, which is demonstrated in the section, xref:generating-csr-from-openssl[] to obtain a certificate.
+
+[[using-keys-and-certificates-from-other-sources]]
+====== Using Keys and Certificates from Other Sources
+
+If you have keys and certificates from other sources, you can proceed directly to xref:loading-keys-and-certificates[].
+
+[[requesting-trusted-certificate]]
+===== Requesting a Trusted Certificate
+
+The keys and certificates generated with JDK's `keytool` and OpenSSL are sufficient to run an SSL connector.
+However the browser will not trust the certificate you have generated, and it will prompt the user to this effect.
+
+To obtain a certificate that most common browsers will trust, you need to request a well-known certificate authority (CA) to sign your key/certificate.
+Such trusted CAs include: AddTrust, Entrust, GeoTrust, RSA Data Security, Thawte, VISA, ValiCert, Verisign, and beTRUSTed, among others.
+Each CA has its own instructions (look for JSSE or OpenSSL sections), but all involve a step that generates a certificate signing request (CSR).
+
+[[generating-csr-from-keytool]]
+====== Generating a CSR with keytool
+
+The following command generates the file `jetty.csr` using `keytool` for a key/cert already in the keystore:
+
+[source, screen, subs="{sub-order}"]
+----
+$ keytool -certreq -alias jetty -keystore keystore -file jetty.csr
+----
+
+[[generating-csr-from-openssl]]
+====== Generating a CSR from OpenSSL
+
+The following command generates the file `jetty.csr` using OpenSSL for a key in the file `jetty.key`:
+
+[source, screen, subs="{sub-order}"]
+----
+$ openssl req -new -key jetty.key -out jetty.csr
+----
+
+Notice that this command uses only the existing key from `jetty.key` file, and not a certificate in `jetty.crt` as generated with OpenSSL.
+You need to enter the details for the certificate again.
+
+[[loading-keys-and-certificates]]
+===== Loading Keys and Certificates
+
+Once a CA has sent you a certificate, or if you generated your own certificate without `keytool`, you need to load it into a JSSE keystore.
+
+____
+[NOTE]
+You need both the private key and the certificate in the JSSE keystore.
+You should load the certificate into the keystore used to generate the CSR with `keytool`.
+If your key pair is not already in a keystore (for example, because it has been generated with OpenSSL), you need to use the PKCS12 format to load both key and certificate (see link:#loading-keys-and-certificates-via-pkcks12[PKCKS12 Keys &Certificates]).
+____
+
+[[loading-certificates-with-keytool]]
+====== Loading Certificates with keytool
+
+You can use `keytool` to load a certificate in PEM form directly into a keystore.
+The PEM format is a text encoding of certificates; it is produced by OpenSSL, and is returned by some CAs.
+An example PEM file is:
+
+[source, screen, subs="{sub-order}"]
+----
+jetty.crt
+-----BEGIN CERTIFICATE-----
+MIICSDCCAfKgAwIBAgIBADANBgkqhkiG9w0BAQQFADBUMSYwJAYDVQQKEx1Nb3J0
+IEJheSBDb25zdWx0aW5nIFB0eS4gTHRkLjEOMAwGA1UECxMFSmV0dHkxGjAYBgNV
+BAMTEWpldHR5Lm1vcnRiYXkub3JnMB4XDTAzMDQwNjEzMTk1MFoXDTAzMDUwNjEz
+MTk1MFowVDEmMCQGA1UEChMdTW9ydCBCYXkgQ29uc3VsdGluZyBQdHkuIEx0ZC4x
+DjAMBgNVBAsTBUpldHR5MRowGAYDVQQDExFqZXR0eS5tb3J0YmF5Lm9yZzBcMA0G
+CSqGSIb3DQEBAQUAA0sAMEgCQQC5V4oZeVdhdhHqa9L2/ZnKySPWUqqy81riNfAJ
+7uALW0kEv/LtlG34dOOcVVt/PK8/bU4dlolnJx1SpiMZbKsFAgMBAAGjga4wgasw
+HQYDVR0OBBYEFFV1gbB1XRvUx1UofmifQJS/MCYwMHwGA1UdIwR1MHOAFFV1gbB1
+XRvUx1UofmifQJS/MCYwoVikVjBUMSYwJAYDVQQKEx1Nb3J0IEJheSBDb25zdWx0
+aW5nIFB0eS4gTHRkLjEOMAwGA1UECxMFSmV0dHkxGjAYBgNVBAMTEWpldHR5Lm1v
+cnRiYXkub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQA6NkaV
+OtXzP4ayzBcgK/qSCmF44jdcARmrXhiXUcXzjxsLjSJeYPJojhUdC2LQKy+p4ki8
+Rcz6oCRvCGCe5kDB
+-----END CERTIFICATE-----
+----
+
+The following command loads a PEM encoded certificate in the `jetty.crt` file into a JSSE keystore:
+
+[source, screen, subs="{sub-order}"]
+----
+$ keytool -keystore keystore -import -alias jetty -file jetty.crt -trustcacerts
+----
+
+If the certificate you receive from the CA is not in a format that `keytool` understands, you can use the `openssl` command to convert formats:
+
+[source, screen, subs="{sub-order}"]
+----
+$ openssl x509 -in jetty.der -inform DER -outform PEM -out jetty.crt
+----
+
+[[loading-keys-and-certificates-via-pkcks12]]
+====== Loading Keys and Certificates via PKCS12
+
+If you have a key and certificate in separate files, you need to combine them into a PKCS12 format file to load into a new keystore.
+The certificate can be one you generated yourself or one returned from a CA in response to your CSR.
+
+The following OpenSSL command combines the keys in `jetty.key` and the certificate in the `jetty.crt` file into the `jetty.pkcs12` file:
+
+[source, screen, subs="{sub-order}"]
+----
+$ openssl pkcs12 -inkey jetty.key -in jetty.crt -export -out jetty.pkcs12
+----
+
+If you have a chain of certificates, because your CA is an intermediary, build the PKCS12 file as follows:
+
+[source, screen, subs="{sub-order}"]
+----
+$ cat example.crt intermediate.crt [intermediate2.crt] ... rootCA.crt > cert-chain.txt
+$ openssl pkcs12 -export -inkey example.key -in cert-chain.txt -out example.pkcs12
+----
+
+____
+[NOTE]
+The order of certificates must be from server to rootCA, as per link:https://www.ietf.org/rfc/rfc2246.txt[RFC2246 section 7.4.2.]
+____
+
+OpenSSL asks for an __export password__.
+A non-empty password is required to make the next step work.
+Load the resulting PKCS12 file into a JSSE keystore with `keytool`:
+
+[source, screen, subs="{sub-order}"]
+----
+$ keytool -importkeystore -srckeystore jetty.pkcs12 -srcstoretype PKCS12 -destkeystore keystore
+----
+
+[[renewing-certificates]]
+===== Renewing Certificates
+
+If you are updating your configuration to use a newer certificate, as when the old one is expiring, just load the newer certificate as described in the section, xref:loading-keys-and-certificates[].
+If you imported the key and certificate originally using the PKCS12 method, use an alias of "1" rather than "jetty", because that is the alias the PKCS12 process enters into the keystore.
+
+==== Configuring SSL in Jetty Distribution
+
+For those of you using the Jetty Distribution, enabling SSL support is as easy as activating the `ssl` module.
+
+An example of this setup:
+
+[source, plain, subs="{sub-order}"]
+----
+$ cd /path/to/mybase
+$ java -jar /path/to/jetty-dist/start.jar --add-to-start=ssl
+INFO : server          initialised (transitively) in ${jetty.base}/start.d/server.ini
+INFO : ssl             initialised in ${jetty.base}/start.d/ssl.ini
+INFO : Base directory was modified
+$ tree
+.
+├── etc
+│   └── keystore
+└── start.d
+    ├── server.ini
+    └── ssl.ini
+----
+
+When you open `start.d/ssl.ini`, you will see many commented properties ready for you to configure the `SslContextFactory` basics.
+
+To highlight some of the more commonly used properties:
+
+jetty.ssl.host::
+  Configures which interfaces the SSL/TLS Connector should listen on.
+jetty.ssl.port::
+  Configures which port the SSL/TLS Connector should listen on.
+jetty.httpConfig.securePort::
+  If a webapp needs to redirect to a secure version of the same resource, then this is the port reported back on the response `location` line (having this be separate is useful if you have something sitting in front of Jetty, such as a Load Balancer or proxy).
+jetty.sslContext.keyStorePath::
+  Sets the location of the `keystore` that you configured with your certificates.
+jetty.sslContext.keyStorePassword::
+  Sets the Password for the `keystore`.
+
+[[two-way-authentication]]
+==== Two Way Authentication
+
+To enable two-way authentication, you first need to activate the ssl module as shown in the previous section.
+
+First you need load the `ssl` module and `https` module.
+[source%nowrap,ini,linenums]
+.$JETTY_BASE/start.d/ssl.ini
+----
+# Module: ssl
+--module=ssl
+
+jetty.ssl.host=0.0.0.0
+jetty.ssl.port=8583
+jetty.sslContext.keyStorePath=etc/keystore
+jetty.sslContext.trustStorePath=etc/keystore
+jetty.sslContext.keyStorePassword=OBF:
+jetty.sslContext.keyManagerPassword=OBF:
+jetty.sslContext.trustStorePassword=OBF:
+jetty.sslContext.trustStoreType=JKS
+# enable two way authentication
+jetty.sslContext.needClientAuth=true
+----
+
+[source%nowrap,ini,linenums]
+.$JETTY_BASE/start.d/https.ini
+----
+# Module: https
+--module=https
+----
+
+[[layout-of-keystore-and-truststore]]
+===== Layout of `keystore` and `truststore`
+
+`keystore` only contains the server's private key and certificate.
+
+[[img-certificate-chain]]
+image::images/certificate-chain.png[title="Certificate chain", alt="Certificate chain"]
+
+[literal]
+.The structure of KeyStore file
+....
+├── PrivateKeyEntry
+│   ├── PrivateKey
+│   ├── Certificate chain
+│   │   ├── Server certificate (end entity)
+│   │   ├── Intermediary CA certificate
+│   │   └── Root CA certificate
+├── TrustedCertEntry
+│   └── Intermediary CA certificate
+└── TrustedCertEntry
+    └── Root CA certificate
+....
+
+[TIP]
+====
+└── PrivateKeyEntry +
+    └── Certificate chain +
+        ├── Intermediary CA certificate +
+        └── Root CA certificate +
+are optional
+====
+
+[source%nowrap,plain,linenums]
+----
+$ cd $JETTY_BASE
+$ keytool -list -keystore etc/keystore -storetype jks -storepass '' -v
+
+Keystore type: JKS
+Keystore provider: SUN
+
+Your keystore contains 3 entries
+
+Alias name: *.example.com
+Creation date: Sep 20, 2016
+Entry type: PrivateKeyEntry
+Certificate chain length: 3
+Certificate[1]:
+Owner: CN=*.example.com, OU=Web Servers, O="Example.com Co.,Ltd.", C=CN
+Issuer: CN="Example.com Co.,Ltd. ETP CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
+Serial number: b63af619ff0b4c368735113ba5db8997
+Valid from: Mon Sep 12 15:09:49 CST 2016 until: Wed Sep 12 15:09:49 CST 2018
+Certificate fingerprints:
+	 MD5:  D9:26:CC:27:77:9D:26:FE:67:4C:BE:FF:E3:95:1E:97
+	 SHA1: AF:DC:D2:65:6A:33:42:E3:81:9E:4D:19:0D:22:20:C7:6F:2F:11:D0
+	 SHA256: 43:E8:21:5D:C6:FB:A0:7D:5D:7B:9C:8B:8D:E9:4B:52:BF:50:0D:90:4F:61:C2:18:9E:89:AA:4C:C2:93:BD:32
+	 Signature algorithm name: SHA256withRSA
+	 Version: 3
+
+Extensions:
+
+#1: ObjectId: 2.5.29.35 Criticality=false
+AuthorityKeyIdentifier [
+KeyIdentifier [
+0000: 44 9B AD 31 E7 FE CA D5   5A 8E 17 55 F9 F0 1D 6B  D..1....Z..U...k
+0010: F5 A5 8F C1                                        ....
+]
+]
+
+#2: ObjectId: 2.5.29.19 Criticality=true
+BasicConstraints:[
+  CA:false
+  PathLen: undefined
+]
+
+#3: ObjectId: 2.5.29.37 Criticality=true
+ExtendedKeyUsages [
+  serverAuth
+  clientAuth
+]
+
+#4: ObjectId: 2.5.29.15 Criticality=true
+KeyUsage [
+  DigitalSignature
+  Key_Encipherment
+  Data_Encipherment
+]
+
+#5: ObjectId: 2.5.29.14 Criticality=false
+SubjectKeyIdentifier [
+KeyIdentifier [
+0000: 7D 26 36 73 61 5E 08 94   AD 25 13 46 DB DB 95 25  .&6sa^...%.F...%
+0010: BF 82 5A CA                                        ..Z.
+]
+]
+
+Certificate[2]:
+Owner: CN="Example.com Co.,Ltd. ETP CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
+Issuer: CN="Example.com Co.,Ltd. Root CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
+Serial number: f6e7b86f6fdb467f9498fb599310198f
+Valid from: Wed Nov 18 00:00:00 CST 2015 until: Sun Nov 18 00:00:00 CST 2035
+Certificate fingerprints:
+	 MD5:  ED:A3:91:57:D8:B8:6E:B1:01:58:55:5C:33:14:F5:99
+	 SHA1: D9:A4:93:9D:A6:F8:A3:F9:FD:85:51:E2:C5:2E:0B:EE:80:E7:D0:22
+	 SHA256: BF:54:7A:F6:CA:0C:FA:EF:93:B6:6B:6E:2E:D7:44:A8:40:00:EC:69:3A:2C:CC:9A:F7:FE:8E:6F:C0:FA:22:38
+	 Signature algorithm name: SHA256withRSA
+	 Version: 3
+
+Extensions: 
+
+#1: ObjectId: 2.5.29.35 Criticality=false
+AuthorityKeyIdentifier [
+KeyIdentifier [
+0000: A6 BD 5F B3 E8 7D 74 3D   20 44 66 1A 16 3B 1B DF  .._...t= Df..;..
+0010: E6 E6 04 46                                        ...F
+]
+]
+
+#2: ObjectId: 2.5.29.19 Criticality=true
+BasicConstraints:[
+  CA:true
+  PathLen:2147483647
+]
+
+#3: ObjectId: 2.5.29.15 Criticality=true
+KeyUsage [
+  Key_CertSign
+  Crl_Sign
+]
+
+#4: ObjectId: 2.5.29.14 Criticality=false
+SubjectKeyIdentifier [
+KeyIdentifier [
+0000: 44 9B AD 31 E7 FE CA D5   5A 8E 17 55 F9 F0 1D 6B  D..1....Z..U...k
+0010: F5 A5 8F C1                                        ....
+]
+]
+
+Certificate[3]:
+Owner: CN="Example.com Co.,Ltd. Root CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
+Issuer: CN="Example.com Co.,Ltd. Root CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
+Serial number: f0a45bc9972c458cbeae3f723055f1ac
+Valid from: Wed Nov 18 00:00:00 CST 2015 until: Sun Nov 18 00:00:00 CST 2114
+Certificate fingerprints:
+	 MD5:  50:61:62:22:71:60:F7:69:2E:27:42:6B:62:31:82:79
+	 SHA1: 7A:6D:A6:48:B1:43:03:3B:EA:A0:29:2F:19:65:9C:9B:0E:B1:03:1A
+	 SHA256: 05:3B:9C:5B:8E:18:61:61:D1:9C:AA:0E:8C:B1:EA:44:C2:6E:67:5D:96:30:EC:8C:F6:6F:E1:EC:AD:00:60:F1
+	 Signature algorithm name: SHA256withRSA
+	 Version: 3
+
+Extensions: 
+
+#1: ObjectId: 2.5.29.35 Criticality=false
+AuthorityKeyIdentifier [
+KeyIdentifier [
+0000: A6 BD 5F B3 E8 7D 74 3D   20 44 66 1A 16 3B 1B DF  .._...t= Df..;..
+0010: E6 E6 04 46                                        ...F
+]
+]
+
+#2: ObjectId: 2.5.29.19 Criticality=true
+BasicConstraints:[
+  CA:true
+  PathLen:2147483647
+]
+
+#3: ObjectId: 2.5.29.15 Criticality=true
+KeyUsage [
+  Key_CertSign
+  Crl_Sign
+]
+
+#4: ObjectId: 2.5.29.14 Criticality=false
+SubjectKeyIdentifier [
+KeyIdentifier [
+0000: A6 BD 5F B3 E8 7D 74 3D   20 44 66 1A 16 3B 1B DF  .._...t= Df..;..
+0010: E6 E6 04 46                                        ...F
+]
+]
+
+
+
+*******************************************
+*******************************************
+
+
+Alias name: example.com co.,ltd. etp ca
+Creation date: Sep 20, 2016
+Entry type: trustedCertEntry
+
+Owner: CN="Example.com Co.,Ltd. ETP CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
+Issuer: CN="Example.com Co.,Ltd. Root CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
+Serial number: f6e7b86f6fdb467f9498fb599310198f
+Valid from: Wed Nov 18 00:00:00 CST 2015 until: Sun Nov 18 00:00:00 CST 2035
+Certificate fingerprints:
+	 MD5:  ED:A3:91:57:D8:B8:6E:B1:01:58:55:5C:33:14:F5:99
+	 SHA1: D9:A4:93:9D:A6:F8:A3:F9:FD:85:51:E2:C5:2E:0B:EE:80:E7:D0:22
+	 SHA256: BF:54:7A:F6:CA:0C:FA:EF:93:B6:6B:6E:2E:D7:44:A8:40:00:EC:69:3A:2C:CC:9A:F7:FE:8E:6F:C0:FA:22:38
+	 Signature algorithm name: SHA256withRSA
+	 Version: 3
+
+Extensions:
+
+#1: ObjectId: 2.5.29.35 Criticality=false
+AuthorityKeyIdentifier [
+KeyIdentifier [
+0000: A6 BD 5F B3 E8 7D 74 3D   20 44 66 1A 16 3B 1B DF  .._...t= Df..;..
+0010: E6 E6 04 46                                        ...F
+]
+]
+
+#2: ObjectId: 2.5.29.19 Criticality=true
+BasicConstraints:[
+  CA:true
+  PathLen:2147483647
+]
+
+#3: ObjectId: 2.5.29.15 Criticality=true
+KeyUsage [
+  Key_CertSign
+  Crl_Sign
+]
+
+#4: ObjectId: 2.5.29.14 Criticality=false
+SubjectKeyIdentifier [
+KeyIdentifier [
+0000: 44 9B AD 31 E7 FE CA D5   5A 8E 17 55 F9 F0 1D 6B  D..1....Z..U...k
+0010: F5 A5 8F C1                                        ....
+]
+]
+
+
+
+*******************************************
+*******************************************
+
+
+Alias name: example.com co.,ltd. root ca
+Creation date: Sep 20, 2016
+Entry type: trustedCertEntry
+
+Owner: CN="Example.com Co.,Ltd. Root CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
+Issuer: CN="Example.com Co.,Ltd. Root CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
+Serial number: f0a45bc9972c458cbeae3f723055f1ac
+Valid from: Wed Nov 18 00:00:00 CST 2015 until: Sun Nov 18 00:00:00 CST 2114
+Certificate fingerprints:
+	 MD5:  50:61:62:22:71:60:F7:69:2E:27:42:6B:62:31:82:79
+	 SHA1: 7A:6D:A6:48:B1:43:03:3B:EA:A0:29:2F:19:65:9C:9B:0E:B1:03:1A
+	 SHA256: 05:3B:9C:5B:8E:18:61:61:D1:9C:AA:0E:8C:B1:EA:44:C2:6E:67:5D:96:30:EC:8C:F6:6F:E1:EC:AD:00:60:F1
+	 Signature algorithm name: SHA256withRSA
+	 Version: 3
+
+Extensions:
+
+#1: ObjectId: 2.5.29.35 Criticality=false
+AuthorityKeyIdentifier [
+KeyIdentifier [
+0000: A6 BD 5F B3 E8 7D 74 3D   20 44 66 1A 16 3B 1B DF  .._...t= Df..;..
+0010: E6 E6 04 46                                        ...F
+]
+]
+
+#2: ObjectId: 2.5.29.19 Criticality=true
+BasicConstraints:[
+  CA:true
+  PathLen:2147483647
+]
+
+#3: ObjectId: 2.5.29.15 Criticality=true
+KeyUsage [
+  Key_CertSign
+  Crl_Sign
+]
+
+#4: ObjectId: 2.5.29.14 Criticality=false
+SubjectKeyIdentifier [
+KeyIdentifier [
+0000: A6 BD 5F B3 E8 7D 74 3D   20 44 66 1A 16 3B 1B DF  .._...t= Df..;..
+0010: E6 E6 04 46                                        ...F
+]
+]
+
+
+
+*******************************************
+*******************************************
+----
+
+In addition, you can split `$JETTY/etc/keystore` as two files.
+One is `$JETTY/etc/keystore` which only contains the server’s private key and certificate,
+the other is `$JETTY/etc/truststore` which contains intermediary CA and root CA.
+
+[literal]
+.The structure of `$JETTY/etc/keystore`
+....
+└── PrivateKeyEntry
+    ├── PrivateKey
+    └── Certificate chain
+        └── Server certificate (end entity)
+....
+
+[literal]
+.The structure of `$JETTY/etc/truststore`
+....
+├── TrustedCertEntry
+│   └── Intermediary CA certificate
+└── TrustedCertEntry
+    └── Root CA certificate
+....
+
+[[configuring-sslcontextfactory]]
+==== Configuring the Jetty SslContextFactory
+
+The generated SSL certificates from above are held in the key store are configured in an instance of link:{JDURL}/org/eclipse/jetty/util/ssl/SslContextFactory.html[SslContextFactory] object.
+
+The `SslContextFactory` is responsible for:
+
+* Creating the Java `SslEngine` used by Jetty's Connectors and Jetty's Clients (HTTP/1, HTTP/2, and WebSocket).
+* Managing Keystore Access
+* Managing Truststore Access
+* Managing Protocol selection via Excludes / Includes list
+* Managing Cipher Suite selection via Excludes / Includes list
+* Managing order of Ciphers offered (important for TLS/1.2 and HTTP/2 support)
+* SSL Session Caching options
+* Certificate https://en.wikipedia.org/wiki/Revocation_list[Revocation Lists] and Distribution Points (CRLDP)
+* https://en.wikipedia.org/wiki/Online_Certificate_Status_Protocol[OCSP] Support
+* Client Authentication Support
+
+For Jetty Connectors, the configured `SslContextFactory` is injected into a specific ServerConnector `SslConnectionFactory`.
+
+For Jetty Clients, the various constructors support using a configured `SslContextFactory`.
+
+While the `SslContextFactory` can operate without a keystore (this mode is most suitable for the various Jetty Clients) it is best practice to at least configure the keystore being used.
+
+setKeyStorePath::
+  The configured keystore to use for all SSL/TLS in configured Jetty Connector (or Client).
+____
+[NOTE]
+As a keystore is vital security information, it can be desirable to locate the file in a directory with *very* restricted access.
+____
+
+setKeyStorePassword::
+  The keystore password may be set here in plain text, or as some measure of protection from casual observation, it may be obfuscated using the link:{JDURL}/org/eclipse/jetty/util/security/Password.html[Password] class.
+setTrustStorePath::
+  This is used if validating client certificates and is typically set to the same path as the keystore.
+setKeyManagerPassword::
+  The password that is passed to the `KeyManagerFactory.init(...)`.
+  If there is no `keymanagerpassword`, then the `keystorepassword` is used instead.
+  If there is no `trustmanager` set, then the keystore is used as the trust store and the `keystorepassword` is used as the truststore password.
+setExcludeCipherSuites / setIncludeCipherSuites::
+  This allows for the customization of the selected Cipher Suites that will be used by SSL/TLS.
+setExcludeProtocols / setIncludeProtocols::
+  This allows for the customization of the selected Protocols that will be used by SSL/TLS.
+
+____
+[NOTE]
+When working with Includes / Excludes, it is important to know that *Excludes will always win.*
+The selection process is to process the JVM list of available Cipher Suites or Protocols against the include list, then remove the excluded ones.
+Be aware that each Include / Exclude list has a Set method (replace the list) or Add method (append the list).
+____
+
+____
+[CAUTION]
+The keystore and truststore passwords may also be set using the system properties: `org.eclipse.jetty.ssl.keypassword` `org.eclipse.jetty.ssl.password`.
+This is _not_ a recommended usage.
+____
+
+==== Configuring SNI
+
+From Java 8, the JVM contains support for the http://en.wikipedia.org/wiki/Server_Name_Indication[Server Name Indicator (SNI)] extension, which allows a SSL connection handshake to indicate one or more DNS names that it applies to.
+To support this, the `ExtendedSslContextFactory` is used that will look for multiple X509 certificates within the keystore, each of which may have multiple DNS names (including wildcards) associated with the http://en.wikipedia.org/wiki/SubjectAltName[Subject Alternate Name] extension.
+When using the `ExtendedSSlContextFactory`, the correct certificate is automatically selected if the SNI extension is present in the handshake.
+
+[[configuring-sslcontextfactory-cipherSuites]]
+==== Disabling/Enabling Specific Cipher Suites
+
+As an example, to avoid the BEAST attack it is necessary to configure a specific set of cipher suites. This can either be done via link:{JDURL}/org/eclipse/jetty/util/ssl/SslContextFactory.html#setIncludeCipherSuites(java.lang.String...)[SslContext.setIncludeCipherSuites(java.lang.String...)] or vialink:{JDURL}/org/eclipse/jetty/util/ssl/SslContextFactory.html#setExcludeCipherSuites(java.lang.String...)[SslContext.setExcludeCipherSuites(java.lang.String...)].
+
+____
+[NOTE]
+It's crucial that you use the _exact_ names of the cipher suites as used/known by the JDK.
+You can get them by obtaining an instance of SSLEngine and call `getSupportedCipherSuites()`.
+Tools like ssllabs.com might report slightly different names which will be ignored.
+____
+
+____
+[NOTE]
+It's recommended to install the Java Cryptography Extension (JCE) Unlimited Strength policy files in your JRE to get full strength ciphers such as AES-256.
+The files can be found on the http://www.oracle.com/technetwork/java/javase/downloads/index.html[Java download page].
+Just overwrite the two present JAR files in `<JRE_HOME>/lib/security/`.
+____
+
+Both `setIncludeCipherSuites` and `setExcludeCipherSuites` can be fed by the exact cipher suite name used in the JDK or by using regular expressions.
+
+If you have a need to adjust the Includes or Excludes, then this is best done with a custom blow-in XML that configures the `SslContextFactory` to suit your needs.
+
+To do this, first create a new `${jetty.base}/etc/tweak-ssl.xml` file (this can be any name, just avoid prefixing it with "jetty-").
+
+[source, xml, subs="{sub-order}"]
+----
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN"
+          "http://www.eclipse.org/jetty/configure_9_3.dtd">
+<!-- Tweak SsslContextFactory Includes / Excludes -->
+<Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
+  <!-- Mitigate SLOTH Attack -->
+  <Call name="addExcludeCipherSuites">
+    <Arg>
+      <Array type="String">
+        <Item>.*_RSA_.*SHA1$</Item>
+        <Item>.*_RSA_.*SHA$</Item>
+        <Item>.*_RSA_.*MD5$</Item>
+      </Array>
+    </Arg>
+  </Call>
+</Configure>
+----
+
+This new XML will configure the id `sslContextFactory` further (this id is first created by the `ssl` module and its associated `${jetty.home}/etc/jetty-ssl-context.xml`).
+You can do anything you want with the `SslContextFactory` in use by the Jetty Distribution from this tweaked XML.
+
+To make sure that your `${jetty.base}` uses this new XML, add it to the end of your `${jetty.base}/start.ini` or `${jetty.base}/start.d/server.ini`.
+
+[source, plain, subs="{sub-order}"]
+----
+$ cd /path/to/mybase
+$ ls -l
+drwxrwxr-x.  2 user group  4096 Feb  2 11:47 etc/
+-rw-rw-r--.  1 user group  4259 Feb  2 11:47 start.ini
+$ tail start.ini
+# Module: https
+--module=https
+etc/tweak-ssl.xml
+$
+----
+
+____
+[NOTE]
+The default `SslContextFactory` implementation applies the latest SSL/TLS recommendations surrounding vulnerabilities in SSL/TLS.
+Check the release notes (the `VERSION.txt` found in the root of the Jetty Distribution, or the http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.eclipse.jetty%22%20AND%20a%3A%22jetty-project%22[alternate (classified 'version') artifacts for the `jetty-project` component]on Maven Central) for updates.
+The Java JVM is also applying exclusions at the JVM level and as such if you have a need to enable something that is generally accepted by the industry as being insecure or vulnerable you will likely have to enable it in BOTH the Java JVM and the Jetty configuration.
+____
+
+____
+[TIP]
+You can enable the `org.eclipse.jetty.util.ssl` named logger at DEBUG level to see what the list of selected Protocols and Cipher suites are at startup of Jetty.
+____
+
+Additional Include / Exclude examples:
+
+*Example*: Include all ciphers which support https://en.wikipedia.org/wiki/Forward_secrecy[Forward Secrecy] using regex:
+
+[source, xml, subs="{sub-order}"]
+----
+  <!-- Enable Forward Secrecy Ciphers.
+       Note: this replaces the default Include Cipher list -->
+  <Set name="IncludeCipherSuites">
+    <Array type="String">
+      <Item>TLS_DHE_RSA.*</Item>
+      <Item>TLS_ECDHE.*</Item>
+    </Array>
+  </Set>
+----
+
+*Example*: Exclude all old, insecure or anonymous cipher suites:
+
+[source, xml, subs="{sub-order}"]
+----
+  <!-- Eliminate Old / Insecure / Anonymous Ciphers -->
+  <Call name="addExcludeCipherSuites">
+    <Arg>
+      <Array type="String">
+        <Item>.*NULL.*</Item>
+        <Item>.*RC4.*</Item>
+        <Item>.*MD5.*</Item>
+        <Item>.*DES.*</Item>
+        <Item>.*DSS.*</Item>
+      </Array>
+    </Arg>
+  </Call>
+----
+
+*Example*: Since 2014 SSLv3 is considered insecure and should be disabled.
+
+[source, xml, subs="{sub-order}"]
+----
+  <!-- Eliminate Insecure Protocols -->
+  <Call name="addExcludeProtocols">
+    <Arg>
+     <Array type="java.lang.String">
+       <Item>SSL</Item>
+       <Item>SSLv2</Item>
+       <Item>SSLv2Hello</Item>
+       <Item>SSLv3</Item>
+     </Array>
+    </Arg>
+  </Call>
+----
+
+____
+[NOTE]
+Note that disabling SSLv3 prevents very old browsers like Internet Explorer 6 on Windows XP from connecting.
+____
+
+*Example*: TLS renegotiation could be disabled too to prevent an attack based on this feature.
+
+[source, xml, subs="{sub-order}"]
+----
+  <Set name="renegotiationAllowed">FALSE</Set>
+----
diff --git a/jetty-documentation/src/main/asciidoc/configuring/connectors/images/certificate-chain.png b/jetty-documentation/src/main/asciidoc/configuring/connectors/images/certificate-chain.png
new file mode 100644
index 0000000..6d2a614
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/connectors/images/certificate-chain.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/configuring/connectors/setting-port80-access-for-non-root-user.adoc b/jetty-documentation/src/main/asciidoc/configuring/connectors/setting-port80-access-for-non-root-user.adoc
new file mode 100644
index 0000000..eb2b563
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/connectors/setting-port80-access-for-non-root-user.adoc
@@ -0,0 +1,137 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[setting-port80-access]]
+=== Setting Port 80 Access for a Non-Root User
+
+On Unix-based systems, port 80 is protected; typically only the superuser `root` can open it. For security reasons, it is not desirable to run the server as `root`.
+This page presents several options to access port 80 as a non-root user, including using `ipchains`, `iptables`, Jetty's SetUID feature, `xinetd`, and the Solaris 10 User Rights Management Framework.
+
+[[using-ipchains]]
+==== Using ipchains
+
+On some Linux systems you can use the _ipchains REDIRECT_ mechanism to redirect from one port to another inside the kernel (if `ipchains` is not available, then `iptables` usually is):
+
+[source, screen, subs="{sub-order}"]
+----
+# /sbin/ipchains -I input --proto TCP --dport 80 -j REDIRECT 8080
+----
+
+This command instructs the system as follows: "Insert into the kernel's packet filtering the following as the first rule to check on incoming packets: if the protocol is TCP and the destination port is 80, redirect the packet to port 8080".
+Be aware that your kernel must be compiled with support for `ipchains` (virtually all stock kernels are).
+You must also have the `ipchains` command-line utility installed.
+You can run this command at any time, preferably just once, since it inserts another copy of the rule every time you run it.
+
+[[using-iptables]]
+==== Using iptables
+
+On many Linux systems you can use the `iptables` REDIRECT mechanism to redirect from one port to another inside the kernel (if `iptables` is not available, then usually `ipchains` is).
+
+You need to add something like the following to the startup scripts or your firewall rules:
+
+[source, screen, subs="{sub-order}"]
+----
+# /sbin/iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
+----
+
+The underlying model of `iptables` is different from `ipchains`, so the forwarding normally happens only to packets originating outside of the server itself.
+You also need to allow incoming packets to port 8080 if you use `iptables` as a local firewall.
+
+Be careful to place rules like this one early in your _input_ chain.
+Such rules must precede any rule that accepts the packet, otherwise the redirection won't occur.
+You can insert as many rules as required if your server needs to listen on multiple ports, as for HTTPS.
+
+[[configuring-jetty-setuid-feature]]
+==== Configuring Jetty's SetUID Feature
+
+http://en.wikipedia.org/wiki/Setuid[SetUID] is a technique that uses Unix-like file system access rights to allow users to run an executable that would otherwise require higher privileges.
+
+Jetty's `SetUID` module allows you to run Jetty as a normal user even when you need to run Jetty on port 80 or 443.
+
+To use it with the Jetty distribution:
+
+.  Ensure that you have the `http.mod` (and link:#quickstart-starting-https[https.mod] if you are using SSL) link:#startup-modules[modules enabled] for the link:#creating-jetty-base[base] you are using.
+The `http.mod` is enabled by default in the distribution, while the link:#quickstart-starting-https[https.mod] is only enabled in the link:#demo-webapps-base[demo-base] directory.
+.  Ensure that you have link:#quickstart-changing-jetty-port[changed the http port] to 80 (and link:#quickstart-changing-https-port[changed the https port] to 443 if you are using SSL).
+.  Enable the `setuid.mod` module:
++
+[source, screen, subs="{sub-order}"]
+----
+# java -jar start.jar --add-to-start=setuid
+----
++
+____
+[NOTE]
+The --add-to-start command will enable the setuid module for this and all subsequent executions of jetty.
+There are other ways to enable the module, such as for a single execution.
+For more information on the alternatives see the section on link:#startup-modules[Managing Startup Modules].
+____
+
+.  Edit the configuration for the `setuid` module to substitute the `userid` and `groupid` of the user to switch to after starting.
+If you used the `--add-to-start` command, this configuration is in the `start.ini` file.
+If you used the `--add-to-startd` command instead, this configuration is in the `start.d/setuid.ini` file instead.
+
+  Below are the lines to configure:
++
+[source, text, subs="{sub-order}"]]
+----
+jetty.startServerAsPrivileged=false
+jetty.username=foo
+jetty.groupname=bar
+jetty.umask=002
+----
++
+____
+[NOTE]
+As well as opening the connectors as `root`, you can also have Jetty start the Server as `root` before changing to the non-`root` user.
+____
+
+.  A native code library is required to perform user switching.
+This code is hosted as part of the Jetty ToolChain project and is released independently from Jetty itself.
+You can find the source code https://github.com/eclipsejetty.toolchain[here] in the https://github.com/eclipse/jetty.toolchain/jetty-setuid[jetty-setuid] project.
+Build it locally, which will produce a native library appropriate for the operating system:
++
+[source, screen, subs="{sub-order}"]
+----
+# mvn clean install
+----
++
+If you built on a linux machine you will find the native library in `jetty-setuid/libsetuid-linux/target/libsetuid-linux.so`.
+If you built on a different operating system you will find the library in a different subdirectory, with the name containing the name of the operating system.
+You may want copy this file into your Jetty distribution's lib directory.
+
+.  Start Jetty as the `root` user in your base directory, providing the location of the native library to Java.
+Below is an example of how to do it from the command line, assuming you are in the link:#demo-webapps-base[demo-base] directory:
++
+[source, screen, subs="{sub-order}"]
+----
+# sudo java -Djava.library.path=libsetuid-linux -jar $JETTY_HOME/start.jar
+----
+
+[[using-solaris10-user-rights-management-framework]]
+
+==== Using the Solaris 10 User Rights Management Framework
+
+Solaris 10 provides a User Rights Management framework that can permit users and processes superuser-like abilities:
+
+[source, screen, subs="{sub-order}"]
+----
+usermod -K defaultpriv=basic,net_privaddr myself
+----
+
+Now the `myself` user can bind to port 80.
+
+Refer to the http://docs.oracle.com/cd/E23823_01/html/816-4557/prbactm-1.html#scrolltoc[Solaris 10] and http://docs.oracle.com/cd/E23824_01/html/821-1456/prbactm-1.html#scrolltoc[Solaris 11 Security Services documentation] for more information.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/contexts/chapter.adoc b/jetty-documentation/src/main/asciidoc/configuring/contexts/chapter.adoc
new file mode 100644
index 0000000..8b2b4ea
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/contexts/chapter.adoc
@@ -0,0 +1,27 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-contexts]]
+== Configuring Contexts
+
+This chapter discusses various options for configuring Jetty contexts.
+
+include::setting-context-path.adoc[]
+include::configuring-virtual-hosts.adoc[]
+include::temporary-directories.adoc[]
+include::serving-webapp-from-particular-port.adoc[]
+include::custom-error-pages.adoc[]
+include::setting-form-size.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/configuring/contexts/configuring-virtual-hosts.adoc b/jetty-documentation/src/main/asciidoc/configuring/contexts/configuring-virtual-hosts.adoc
new file mode 100644
index 0000000..1d13c02
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/contexts/configuring-virtual-hosts.adoc
@@ -0,0 +1,214 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-virtual-hosts]]
+=== Configuring Virtual Hosts
+
+A virtual host is an alternative name, registered in DNS, for an IP address such that multiple domain names will resolve to the same IP of a shared server instance.
+If the content to be served to the aliases names is link:#different-virtual-hosts-different-contexts[different], then a virtual host needs to be configured for each deployed link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandler.html[context] to indicate which names a context will respond to.
+
+Virtual hosts are set on a link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandler.html[context] by calling the link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandler.html#setVirtualHosts-java.lang.String:A-[`setVirtualHosts`] or link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandler.html#addVirtualHosts-java.lang.String:A-[`addVirtualHost`] method which can be done in several ways:
+
+* Using a link:#deployable-descriptor-file[context XML] file in the webapps directory (see the example in link:{SRCDIR}/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml[test.xml] in the Jetty distribution).
+* Creating a link:#deployment-architecture[custom deployer] with a binding to configure virtual hosts for all contexts found in the same `webapps` directory.
+* Calling the link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandler.html#setVirtualHosts-java.lang.String:A-[API] directly on an link:#advanced-embedding[embedded] usage.
+* Using a `WEB-INF/jetty-web.xml` file (now deprecated).
+
+[[configuring-a-virtual-host]]
+==== Virtual Host Names
+
+Jetty supports the following styles of virtual host name:
+
+www.hostname.com::
+  A fully qualified host name. It is important to list all variants as a site may receive traffic from both www.hostname.com and just hostname.com
+*.hostname.com::
+  A wildcard qualified host which will match only one level of arbitrary names.
+  *.foo.com will match www.foo.com and m.foo.com, but not www.other.foo.com
+10.0.0.2::
+  An IP address may be given as a virtual host name to indicate that a context should handle requests received on that server port that do not have a host name specified (HTTP/0.9 or HTTP/1.0).
+@ConnectorName::
+  A connector name, which is not strictly a virtual host, but instead will only match requests that are received on connectors that have a matching name set with  link:{JDURL}/org/eclipse/jetty/server/AbstractConnector.html#setName(java.lang.String)[Connector.setName(String)].
+www.√integral.com::
+  Non-ASCII and  http://en.wikipedia.org/wiki/Internationalized_domain_name[IDN] domain names can be set as virtual hosts using http://en.wikipedia.org/wiki/Punycode[Puny Code] equivalents that may be obtained from a http://network-tools.com/idn-convert.asp[Punycode/IDN converters].
+  For example if the non-ASCII domain name `www.√integral.com` is given to a browser, then it will make a request that uses the domain name `www.xn--integral-7g7d.com`, which is the name that should be added as the virtual host name.
+
+==== Example Virtual Host Configuration
+
+Virtual hosts can be used with any context that is a subclass of link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandler.html[ContextHandler].
+Lets look at an example where we configure a web application - represented by the link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html[WebAppContext] class - with virtual hosts.
+You supply a list of IP addresses and names at which the web application is reachable, such as the following:
+
+* `333.444.555.666`
+* `127.0.0.1`
+* `www.blah.com`
+* `www.blah.net`
+* `www.blah.org`
+
+Suppose you have a webapp called `blah.war`, that you want all of the above names and addresses to be served at path "`/blah`".
+Here's how you would configure the virtual hosts with a link:#deployable-descriptor-file[context XML] file:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/blah</Set>
+  <Set name="war"><Property name="jetty.webapps"/>/webapps/blah.war</Set>
+  <Set name="virtualHosts">
+    <Array type="java.lang.String">
+      <Item>333.444.555.666</Item>
+      <Item>127.0.0.1</Item>
+      <Item>www.blah.com</Item>
+      <Item>www.blah.net</Item>
+      <Item>www.blah.org</Item>
+    </Array>
+  </Set>
+</Configure>
+----
+
+[[different-virtual-hosts-different-contexts]]
+==== Using Different Sets of Virtual Hosts to Select Different Contexts
+
+You can configure different contexts to respond on different virtual hosts by supplying a specific list of virtual hosts for each one.
+
+For example, suppose your imaginary machine has these DNS names:
+
+* `www.blah.com`
+* `www.blah.net`
+* `www.blah.org`
+* `www.other.com`
+* `www.other.net`
+* `www.other.org`
+
+Suppose also you have 2 webapps, one called `blah.war` that you would like served from the `*.blah.*` names, and one called `other.war` that you want served only from the `*.other.*` names.
+
+Using the method of preparing link:#deployable-descriptor-file[contextXML] files, one for each webapp yields the following:
+
+For `blah` webapp:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/blah</Set>
+  <Set name="war"><Property name="jetty.webapps"/>/webapps/blah.war</Set>
+  <Set name="virtualHosts">
+    <Array type="java.lang.String">
+      <Item>www.blah.com</Item>
+      <Item>www.blah.net</Item>
+      <Item>www.blah.org</Item>
+    </Array>
+  </Set>
+</Configure>
+----
+
+These URLs now resolve to the blah context (ie `blah.war`):
+
+* `http://www.blah.com/blah`
+* `http://www.blah.net/blah`
+* `http://www.blah.org/blah`
+
+For `other` webapp:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/other</Set>
+  <Set name="war"><Property name="jetty.webapps"/>/webapps/other.war</Set>
+  <Set name="virtualHosts">
+    <Array type="java.lang.String">
+      <Item>www.other.com</Item>
+      <Item>www.other.net</Item>
+      <Item>www.other.org</Item>
+    </Array>
+  </Set>
+</Configure>
+----
+
+These URLs now resolve to the other context (i.e. `other.war`):
+
+* `http://www.other.com/other`
+* `http://www.other.net/other`
+* `http://www.other.org/other`
+
+[[different-virtual-hosts-different-context-same-path]]
+==== Using Different Sets of Virtual Hosts to Select Different Contexts at the Same Context Path
+
+In the previous section we setup 2 different contexts to be served from different virtual hosts at _different_ context paths.
+However, there is no requirement that the context paths must be distinct: you may use the same context path for multiple contexts, and use virtual hosts to determine which one is served for a given request.
+
+Consider an example where we have the same set of DNS names as before, and the same webapps `blah.war` and `other.war`. We still want `blah.war` to be served in response to hostnames of `*.blah.*`, and we still want `other.war` to be served in response to `*.other.*` names.
+However, we would like__all__ of our clients to use the `"/"` context path, no matter which context is being targeted.
+
+In other words, we want all of the following URLs to map to `blah.war`:
+
+* `http://www.blah.com/`
+* `http://www.blah.net/`
+* `http://www.blah.org/`
+
+Similarly, we want the following URLs to map to `other.war`:
+
+* `http://www.other.com/`
+* `http://www.other.net/`
+* `http://www.other.org/`
+
+To achieve this, we simply use the same context path of `/` for each of our webapps, while still applying our different set of virtual host names.
+
+For foo webapp:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/</Set>
+  <Set name="war"><Property name="jetty.webapps"/>/webapps/foo.war</Set>
+  <Set name="virtualHosts">
+    <Array type="java.lang.String">
+      <Item>www.blah.com</Item>
+      <Item>www.blah.net</Item>
+      <Item>www.blah.org</Item>
+    </Array>
+  </Set>
+</Configure>
+----
+
+For bar webapp:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/</Set>
+  <Set name="war"><Property name="jetty.webapps"/>/webapps/bar.war</Set>
+  <Set name="virtualHosts">
+    <Array type="java.lang.String">
+      <Item>www.other.com</Item>
+      <Item>www.other.net</Item>
+      <Item>www.other.org</Item>
+    </Array>
+  </Set>
+</Configure>
+----
diff --git a/jetty-documentation/src/main/asciidoc/configuring/contexts/custom-error-pages.adoc b/jetty-documentation/src/main/asciidoc/configuring/contexts/custom-error-pages.adoc
new file mode 100644
index 0000000..5201ef8
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/contexts/custom-error-pages.adoc
@@ -0,0 +1,171 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[custom-error-pages]]
+=== Creating Custom Error Pages
+
+The following sections describe several ways to create custom error pages in Jetty.
+
+==== Defining error pages in web.xml
+
+You can use the standard webapp configuration file located in `webapp/WEB-INF/web.xml` to map errors to specific URLs with the `error-page` element.
+This element creates a mapping between the error-code or exception-type to the location of a resource in the web application.
+
+* `error-code` - an integer value
+* `exception-type` - a fully qualified class name of a Java Exception type
+* `location` - location of the resource in the webapp relative to the root of the web application. Value should start with `/`.
+
+Error code example:
+
+[source, xml, subs="{sub-order}"]
+----
+<error-page>
+  <error-code>404</error-code>
+  <location>/jspsnoop/ERROR/404</location>
+</error-page>
+
+----
+
+Exception example:
+
+[source, xml, subs="{sub-order}"]
+----
+<error-page>
+  <exception-type>java.io.IOException</exception-type>
+  <location>/jspsnoop/IOException</location>
+</error-page>
+
+----
+
+The error page mappings created with the error-page element will redirect to a normal URL within the web application and will be handled as a normal request to that location and thus may be static content, a JSP or a filter and/or servlet.
+When handling a request generated by an error redirection, the following request attributes are set and are available to generate dynamic content:
+
+javax.servlet.error.exception::
+  The exception instance that caused the error (or null).
+javax.servlet.error.exception_type::
+  The class name of the exception instance that caused the error (or null).
+javax.servlet.error.message::
+  The error message.
+javax.servlet.error.request_uri::
+  The URI of the request with an error.
+javax.servlet.error.servlet_name::
+  The Servlet name of the servlet that the request was
+  dispatched to.
+javax.servlet.error.status_code::
+  The status code of the error (e.g. 404, 500 etc.).
+
+==== Configuring error pages context files
+
+You can use context IoC XML files to configure the default error page mappings with more flexibility than is available with `web.xml`, specifically with the support of error code ranges.
+Context files are normally located in `${jetty.base}/webapps/` (see `DeployerManager` for more details) and an example of more flexible error page mapping is below:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.eclipse.org/configure.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/test</Set>
+  <Set name="war">
+    <SystemProperty name="jetty.base" default="."/>/webapps/test
+  </Set>
+
+  <!-- by Code -->
+  <Get name="errorHandler">
+    <Call name="addErrorPage">
+      <Arg type="int">404</Arg>
+      <Arg type="String">/jspsnoop/ERROR/404</Arg>
+    </Call>
+  </Get>
+
+  <!-- by Exception -->
+  <Get name="errorHandler">
+    <Call name="addErrorPage">
+      <Arg>
+        <Call class="java.lang.Class" name="forName">
+          <Arg type="String">java.io.IOException</Arg>
+        </Call>
+      </Arg>
+      <Arg type="String">/jspsnoop/IOException</Arg>
+    </Call>
+  </Get>
+
+  <!-- by Code Range -->
+  <Get name="errorHandler">
+    <Call name="addErrorPage">
+      <Arg type="int">500</Arg>
+      <Arg type="int">599</Arg>
+      <Arg type="String">/dump/errorCodeRangeMapping</Arg>
+    </Call>
+  </Get>
+</Configure>
+
+----
+
+==== Custom ErrorHandler class
+
+If no error page mapping is defined, or if the error page resource itself has an error, then the error page will be generated by an instance of `ErrorHandler` configured either the Context or the Server.
+An `ErrorHandler` may extend the `ErrorHandler` class and may totally replace the handle method to generate an error page, or it can implement some or all of the following methods to partially modify the error pages:
+
+[source, java, subs="{sub-order}"]
+----
+void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
+void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message) throws IOException
+void writeErrorPage(HttpServletRequest request, Writer writer, int code, String message, boolean showStacks) throws IOException
+void writeErrorPageHead(HttpServletRequest request, Writer writer, int code, String message) throws IOException
+void writeErrorPageBody(HttpServletRequest request, Writer writer, int code, String message, boolean showStacks) throws IOException
+void writeErrorPageMessage(HttpServletRequest request, Writer writer, int code, String message, String uri) throws IOException
+void writeErrorPageStacks(HttpServletRequest request, Writer writer) throws IOException
+----
+
+An `ErrorHandler` instance may be set on a Context by calling the `ContextHandler.setErrorHandler` method. This can be done by embedded code or via context IoC XML.
+For example:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
+  ...
+  <Set name="errorHandler">
+    <New class="com.acme.handler.MyErrorHandler"/>
+  </Set>
+  ...
+</Configure>
+----
+
+An `ErrorHandler` instance may be set on the entire server by setting it as a dependent bean on the Server instance.
+This can be done by calling `Server.addBean(Object)` via embedded code or in `jetty.xml` IoC XML:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+  ...
+  <Call name="addBean">
+    <Arg>
+      <New class="com.acme.handler.MyErrorHandler"/>
+    </Arg>
+  </Call>
+  ...
+</Configure>
+----
+
+==== Server level 404 error
+
+It is possible to get a 'page not found' when a request is made to the server for a resource that is outside of any registered contexts.
+As an example, you have a domain name pointing to your public server IP, yet no context is registered with Jetty to serve pages for that domain.
+As a consequence, the server, by default, gives a listing of all contexts running on the server.
+
+One of the quickest ways to avoid this behavior is to create a catch all context.
+Create a "root" web app mapped to the "/" URI, and use the `index.html` redirect to whatever place with a header directive.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/contexts/serving-webapp-from-particular-port.adoc b/jetty-documentation/src/main/asciidoc/configuring/contexts/serving-webapp-from-particular-port.adoc
new file mode 100644
index 0000000..ab8482b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/contexts/serving-webapp-from-particular-port.adoc
@@ -0,0 +1,72 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[serving-webapp-from-particular-port]]
+=== Serving a WebApp from a Particular Port/Connector
+
+Sometimes it is required to serve different web applications from different ports/connectors.
+The simplest way to do this is to create multiple `Server` instances.
+However, if contexts need to share resources (eg data sources, authentication), or if the mapping of ports to web applications is not cleanly divided, then the named connector mechanism can be used.
+
+[[creating-server-instances]]
+==== Creating Multiple Server Instances
+
+Creating multiple server instances is a straight forward process that includes embedding Jetty code by creating multiples instances of the Server class and configuring them as needed.
+This is also easy to achieve if you are configuring Jetty servers via XML.
+The `id` field in the Configure element of `jetty.xml` files is used to identify the instance that the configuration applies to, so to run two instances of the Server, you can copy the `jetty.xml`, jetty-http.xml and other jetty configuration files used and change the "Server" id to a new name.
+This can be done in the same style and layout as the existing `jetty.xml` files or the multiple XML files may be combined to a single file.
+
+When creating new configurations for alternative server:
+
+* Change all `id="Server"` to the new server name:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="OtherServer" class="org.eclipse.jetty.server.Server">
+----
+
+* For all connectors for the new server change the `refid` in the server argument:
+
+[source, xml, subs="{sub-order}"]
+----
+<Arg name="server"><Ref refid="OtherServer" /></Arg>
+----
+
+* Make sure that any references to properties like `jetty.http.port` are either renamed or replaced with absolute values.
+* Make sure that any deployers `AppProviders` refer to a different "webapps" directory so that a different set of applications are deployed.
+
+[[jetty-otherserver.xml]]
+===== Example Other Server XML
+
+The following example creates another server instance and configures it with a connector and deployer:
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/resources/jetty-otherserver.xml[]
+----
+
+To run the other server, add the extra configuration file(s) to the command line:
+
+[source, screen, subs="{sub-order}"]
+----
+java -jar start.jar jetty-otherserver.xml
+----
+
+[[alternative]]
+==== Named Connectors
+
+It is also possible to use an extension to the virtual host mechanism with named to connectors to make some web applications only accessible by specific connectors.
+If a connector has a name "MyConnector" set using the `setName` method, then this can be referenced with the special virtual host name "@MyConnector".
diff --git a/jetty-documentation/src/main/asciidoc/configuring/contexts/setting-context-path.adoc b/jetty-documentation/src/main/asciidoc/configuring/contexts/setting-context-path.adoc
new file mode 100644
index 0000000..ad119f4
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/contexts/setting-context-path.adoc
@@ -0,0 +1,58 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[setting-context-path]]
+=== Setting a Context Path
+
+The context path is the prefix of a URL path that is used to select the context(s) to which an incoming request is passed. Typically a URL in a Java servlet server is of the format `http://hostname.com/contextPath/servletPath/pathInfo`, where each of the path elements can be zero or more / separated elements. 
+If there is no context path, the context is referred to as the _root_ context. 
+The root context must be configured as `/` but is reported as the empty string by the servlet API `getContextPath()` method.
+
+How you set the context path depends on how you deploy the web application (or `ContextHandler`).
+
+[[using-embedded-deployment]]
+==== Using Embedded Deployment
+
+If you run Jetty from code as an embedded server (see link:#advanced-embedding[Embedding]), setting the context path is a matter of calling the `setContextPath` method on the `ContextHandler` instance (or `WebAppContext` instance).
+
+[[usng-the-context-provider]]
+==== By naming convention
+
+If a web application is deployed using the WebAppProvider of the DeploymentManager without an XML IoC file, then the name of the WAR file is used to set the context path:
+
+* If the WAR file is named myapp.war, then the context will be deployed with a context path of `/myapp`
+* If the WAR file is named ROOT.WAR (or any case insensitive variation), then the context will be deployed with a context path of `/`
+* If the WAR file is named ROOT-foobar.war ( or any case insensitive variation), then the context will be deployed with a context path of `/` and a virtual host of "foobar"
+
+[[using-the-webapp-provider]]
+==== By Deployer configuration
+
+If a web application is deployed using the `WebAppProvider` of the `DeploymentManager` with an XML IoC file to configure the context, then the `setContextPath` method can be called within that file. 
+For example:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/test</Set>
+  ...
+</Configure>
+----
+
+[[embedding-web-inf-jetty-web.xml-file]]
+==== Embedding a WEB-INF/jetty-web.xml File
+
+You can also set the context path for webapps by embedding a `WEB-INF/jetty-web.xml` file in the WAR, which uses the same XML IoC format as the deployer example above. 
+However this is not the preferred method as it requires the web application to be modified.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/contexts/setting-form-size.adoc b/jetty-documentation/src/main/asciidoc/configuring/contexts/setting-form-size.adoc
new file mode 100644
index 0000000..094c214
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/contexts/setting-form-size.adoc
@@ -0,0 +1,60 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[setting-form-size]]
+=== Setting Max Form Size
+
+Jetty limits the amount of data that can post back from a browser or other client to the server.
+This helps protect the server against denial of service attacks by malicious clients sending huge amounts of data.
+The default maximum size Jetty permits is 200000 bytes.
+You can change this default for a particular webapp, for all webapps on a particular Server instance, or all webapps within the same JVM.
+
+==== For a Single Webapp
+
+The method to invoke is: `ContextHandler.setMaxFormContentSize(int maxSize);`
+
+This can be done either in a context XML deployment descriptor external to the webapp, or in a `jetty-web.xml` file in the webapp's `WEB-INF` directory.
+
+In either case the syntax of the XML file is the same:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+  <!-- Max Form Size                                                   -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+  <Set name="maxFormContentSize">200000</Set>
+</Configure>
+----
+
+==== For All Apps on a Server
+
+Set an attribute on the Server instance for which you want to modify the maximum form content size:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.server.Server">
+  <Call name="setAttribute">
+    <Arg>org.eclipse.jetty.server.Request.maxFormContentSize</Arg>
+    <Arg>200000</Arg>
+  </Call>
+</Configure>
+----
+
+==== For All Apps in the JVM
+
+Use the system property `org.eclipse.jetty.server.Request.maxFormContentSize`.
+This can be set on the command line or in the `start.ini` or `start.d\server.ini` file.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/contexts/temporary-directories.adoc b/jetty-documentation/src/main/asciidoc/configuring/contexts/temporary-directories.adoc
new file mode 100644
index 0000000..2ab6e7d
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/contexts/temporary-directories.adoc
@@ -0,0 +1,158 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[ref-temporary-directories]]
+=== Temporary Directories
+
+Jetty itself has no temporary directories, but you can assign a directory for each web application, into which the WAR is unpacked, JSPs compiled on-the-fly, etc.
+If you do not assign a specific temporary directory, Jetty will create one as needed when your web application starts.
+Whether you set the location of the temporary directory - or you let Jetty create one for you - you also have a choice to either keep or delete the temporary directory when the web application stops.
+
+==== The default temp directory
+
+By default, Jetty will create a temporary directory for each web application. The name of the directory will be of the form:
+
+....
+"jetty-"+host+"-"+port+"-"+resourceBase+"-_"+context+"-"+virtualhost+"-"+randomdigits+".dir"
+....
+
+For example:
+
+....
+jetty-0.0.0.0-8080-test.war-_test-any-8900275691885214790.dir
+....
+
+Where `0.0.0.0` is the host address, `8080` is the port, `test.war` is the resourceBase, `test` is the context path (with / converted to _), `any` is the virtual host, and `randomdigits` are a string of digits guaranteed to be unique.
+
+Once the temp directory is created, it is retrievable as the value (as a File) of the context attribute `javax.servlet.context.tempdir.`
+
+===== The location of the temp directory
+
+By default, Jetty will create this directory inside the directory named by the `java.io.tmpdir` System property.
+You can instruct Jetty to use a different parent directory by setting the context attribute `org.eclipse.jetty.webapp.basetempdir` to the name of the desired parent directory.
+The directory named by this attribute _must_ exist and be __writeable__.
+
+As usual with Jetty, you can either set this attribute in a context xml file, or you can do it in code.
+
+Here's an example of setting it in an xml file:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+  <Set name="contextPath">/test</Set>
+  <Set name="war">foo.war</Set>
+
+  <Call name="setAttribute">
+    <Arg>org.eclipse.jetty.webapp.basetempdir</Arg>
+    <Arg>/home/my/foo</Arg>
+  </Call>
+</Configure>
+----
+
+The equivalent in code is:
+
+[source, java, subs="{sub-order}"]
+----
+WebAppContext context = new WebAppContext();
+context.setContextPath("/test");
+context.setWar("foo.war");
+context.setAttribute("org.eclipse.jetty.webapp.basetempdir", "/tmp/foo");
+----
+
+==== Setting a specific temp directory
+
+There are several ways to use a particular directory as the temporary directory:
+
+===== Call WebAppContext.setTempDirectory(String dir)
+As before this can be accomplished with an xml file or directly in code. Here's an example of setting the temp directory in xml:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+  <Set name="contextPath">/test</Set>
+  <Set name="war">foo.war</Set>
+
+  <Set name="tempDirectory">/some/dir/foo</Set>
+</Configure>
+----
+
+Here's an example of doing it with java code:
+
+[source, java, subs="{sub-order}"]
+----
+WebAppContext context = new WebAppContext();
+context.setContextPath("/test");
+context.setWar("foo.war");
+context.setTempDirectory(new File("/some/dir/foo"));
+----
+
+===== Set the javax.servlet.context.tempdir context attribute
+You should set this context attribute with the name of directory you want to use as the temp directory. Again, you can do this in xml:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+  <Set name="contextPath">/test</Set>
+  <Set name="war">foo.war</Set>
+
+  <Call name="setAttribute">
+    <Arg>javax.servlet.context.tempdir</Arg>
+    <Arg>/some/dir/foo</Arg>
+  </Call>
+
+</Configure>
+----
+
+Or in java:
+
+[source, java, subs="{sub-order}"]
+----
+WebAppContext context = new WebAppContext();
+context.setContextPath("/test");
+context.setWar("foo.war");
+context.setAttribute("javax.servlet.context.tempdir", "/some/dir/foo");
+----
+
+Once a temporary directory has been created by either of these methods, a file instance for it is set as the value of the `javax.servlet.context.tempdir` attribute of the web application.
+
+____
+[NOTE]
+Be wary of setting an explicit temp directory if you are likely to change the jars in WEB-INF/lib between redeployments.
+There is a JVM bug concerning link:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4774421[caching of jar contents.]
+____
+
+==== The "work" directory
+
+Mostly for backward compatibility, from Jetty 9.1.1 onwards, it is possible to create a directory named "work" in the `$\{jetty.base}` directory.
+If such a directory is found, it is assumed you want to use it as the parent directory for all of the temporary directories of the webapps in `$\{jetty.base}`.
+Moreover, as has historically been the case, these temp directories inside the work directory are not cleaned up when Jetty exits (or more correctly speaking, the `temp` directory corresponding to a context is not cleaned up when that context stops).
+
+When a work directory is used, the algorithm for generating the name of the context-specific temp directories omits the random digit string.
+This ensures the name of the directory remains consistent across context restarts.
+
+==== Persisting the temp directory
+
+Sometimes it is useful to keep the contents of the temporary directory between restarts of the web application.
+By default, Jetty will *not* persist the temp directory.
+To configure Jetty to keep it, use link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html[WebAppContext.setPersistTempDirectory(true)].
+
+____
+[NOTE]
+Be aware that if you call `setPersistTempDirectory(true)`, but let Jetty create a new temp directory each time (i.e. you do NOT set an explicit temp directory), then you will accumulate temp directories in your chosen temp directory location.
+____
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/anatomy-of-a-webapp.adoc b/jetty-documentation/src/main/asciidoc/configuring/deploying/anatomy-of-a-webapp.adoc
new file mode 100644
index 0000000..71099d0
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/anatomy-of-a-webapp.adoc
@@ -0,0 +1,40 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[anatomy-of-a-webapp]]
+=== Anatomy of a Web Application
+
+The standard Jetty distribution is capable of deploying standard Servlet Spec Web Applications and Jetty internal ContextHandler deployment descriptors, or even a mix of the two.
+
+Web Applications are deployable collections of dynamic (servlets, filters, jsps, etc..) and static content, support libraries, and descriptive metadata that are bound to a specific context path on Jetty.
+
+Ultimately the format and layout are defined by the Servlet Spec, and the official Servlet Spec documentation should be consulted for a more detailed understanding of Web Application layout and structure; however, this will outline basics about how Jetty views these requirements.
+
+Web Applications can be bundled into a single Web Archive (WAR file) or as a directory tree.
+
+`/WEB-INF/`::
+  Special Servlet API defined directory used to store anything related to the Web Application that are not part of the public access of the Web Application.
+  If there is content that is accessed by a Web Application internally, but that should also never be accessed directly by a web browser, this is the directory it would placed in.
+  
+`/WEB-INF/web.xml`::
+  *Required* deployment descriptor defining various behavior of the Web Application.
+  
+`/WEB-INF/classes/`::
+  Location for Web Application specific compiled java classes
+`/WEB-INF/lib/`::
+  Directory for JAR files (libraries)
+
+The Jetty internal `WebAppClassloader` will load classes from `/WEB-INF/classes/` first, then from jar files found in `/WEB-INF/lib/`.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/automatic-webapp-deployment.adoc b/jetty-documentation/src/main/asciidoc/configuring/deploying/automatic-webapp-deployment.adoc
new file mode 100644
index 0000000..fec871a
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/automatic-webapp-deployment.adoc
@@ -0,0 +1,40 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[automatic-webapp-deployment]]
+=== Automatic Web Application Deployment
+
+The most basic technique for deploying Web Applications is to put a WAR file or Exploded WAR directory into the `${jetty.base}/webapps/` directory and let Jetty's deployment scanner find it and deploy it under a Context path of the same name.
+
+Only Web Applications that follow the Web Application Layout will be detected by Jetty and deployed this way.
+
+The Context Path assigned to this automatic deployment is based the filename (or directory name) of the WAR.
+
+[cols=",",options="header",]
+|=======================================================================
+|File or Directory Name |Assigned Context Path
+|`/webapps/footrope.war` |http://host/footrope/
+
+|`/webapps/baggywrinkle-1.0.war` |http://host/baggywrinkle-1.0/
+
+|`/webapps/lazaret-2.1.3-SNAPSHOT.war` |http://host/lazaret-2.1.3-SNAPSHOT/
+
+|`/webapps/belaying-pins/WEB-INF/web.xml` |http://host/belaying-pins/
+
+|`/webapps/root.war` _(reserved name)_ |http://host/
+
+|`/webapps/root/WEB-INF/web.xml` _(reserved name)_ |http://host/
+|=======================================================================
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/chapter.adoc b/jetty-documentation/src/main/asciidoc/configuring/deploying/chapter.adoc
new file mode 100644
index 0000000..e9c9bc1
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/chapter.adoc
@@ -0,0 +1,32 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-deployment]]
+== Deploying to Jetty
+
+This chapter discusses various ways to deploy applications with Jetty.
+Topics range from deployment bindings to deploying third party products.
+It also includes information about the Deployment Manager, WebApp Provider, and Overlay Deployer.
+
+include::anatomy-of-a-webapp.adoc[]
+include::automatic-webapp-deployment.adoc[]
+include::configuring-specific-webapp-deployment.adoc[]
+include::deployment-processing-webapps.adoc[]
+include::static-content-deployment.adoc[]
+include::hot-deployment.adoc[]
+include::deployment-architecture.adoc[]
+include::quickstart-webapp.adoc[]
+//include::overlay-deployer.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/configuring-specific-webapp-deployment.adoc b/jetty-documentation/src/main/asciidoc/configuring/deploying/configuring-specific-webapp-deployment.adoc
new file mode 100644
index 0000000..41d27a1
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/configuring-specific-webapp-deployment.adoc
@@ -0,0 +1,162 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-specific-webapp-deployment]]
+=== Configuring a Specific Web Application Deployment
+
+Using the Automatic Web Application Deployment model is quick and easy, but sometimes you might need to tune certain deployment properties (for example, you want to deploy with a context path that is not based on the file name, or you want to define a special database connection pool just for this web application).
+You can use a xref:deployable-descriptor-file[] to accomplish such tuning.
+
+[[deployable-descriptor-file]]
+==== Jetty Deployable Descriptor XML File
+
+Jetty supports deploying Web Applications via XML files which will build an instance of a link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandler.html[ContextHandler] that Jetty can then deploy.
+
+[[using-basic-descriptor-files]]
+==== Using Basic Descriptor Files
+
+In a default Jetty installation, Jetty scans its `$JETTY_HOME/webapps` directory for context deployment descriptor files.
+To deploy a web application using such a file, simply place the file in that directory.
+
+The deployment descriptor file itself is an xml file that configures a link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html[`WebAppContext`] class.
+For a basic installation only two properties need configured:
+
+war::
+  The filesystem path to the web application file (or directory)
+contextPath::
+  The context path to use for the web application
+
+For example, here is a descriptor file that deploys the file `/opt/myapp/myapp.war` to the context path `/wiki`:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/wiki</Set>
+  <Set name="war">/opt/myapp/myapp.war</Set>
+</Configure>
+----
+
+____
+[NOTE]
+Both `SystemProperty` and `Property` elements can be used in the descriptor file.
+For example, if the system property is set to `myapp.home=/opt/myapp`, the previous example can be rewritten as:
+____
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/wiki</Set>
+  <Set name="war"><SystemProperty name="myapp.home"/>/myapp.war</Set>
+</Configure>
+----
+
+If the home path for an application needs altered, only the system property needs changed.
+This is useful if the version of an app is frequently changed.
+
+[[configuring-advanced-descriptor-files]]
+==== Configuring Advanced Descriptor Files
+
+Official documentation for the for the link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html[`WebAppContext`] class lists all the properties that can be set.
+Here are some examples that configure advanced options in the descriptor file.
+
+This first example tells Jetty not to expand the WAR file when deploying it.
+This can help make it clear that users should not make changes to the temporary unpacked WAR because such changes do not persist, and therefore do not apply the next time the web application deploys.
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/wiki</Set>
+  <Set name="war"><SystemProperty name="myapp.home"/>/myapp.war</Set>
+  <Set name="extractWAR">false</Set>
+</Configure>
+----
+
+The next example retrieves the JavaEE Servlet context and sets an initialization parameter on it.
+The `setAttribute` method can also be used to set a Servlet context attribute.
+However, since the `web.xml` for the web application is processed after the deployment descriptor, the `web.xml` values overwrite identically named attributes from the deployment descriptor.
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/wiki</Set>
+  <Set name="war"><SystemProperty name="myapp.home"/>/myapp.war</Set>
+  <Get name="ServletContext">
+     <Call name="setInitParameter">
+       <Arg>myapp.config</Arg>
+       <Arg><SystemProperty name="myapp.home">/config/app-config.xml</Arg>
+    </Call>
+  </Get>
+</Configure>
+----
+
+The following example sets a special `web.xml` override descriptor.
+This descriptor is processed after the web application's `web.xml`, so it may override identically named attributes.
+This feature is useful when adding parameters or additional Servlet mappings without breaking open a packed WAR file.
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/wiki</Set>
+  <Set name="war"><SystemProperty name="myapp.home"/>/myapp.war</Set>
+  <Set name="overrideDescriptor">/opt/myapp/overlay-web.xml</Set>
+</Configure>
+----
+
+The next example configures not only the web application context, but also a database connection pool (see xref:jndi-datasource-examples[]) that the application can then use. 
+If the `web.xml` does not include a reference to this data source, an override descriptor mechanism (as shown in the previous example) can be used to include it.
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/wiki</Set>
+  <Set name="war"><SystemProperty name="myapp.home"/>/myapp.war</Set>
+
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+    <Arg></Arg>
+    <Arg>jdbc/DSTest</Arg>
+    <Arg>
+      <New class="org.apache.commons.dbcp.BasicDataSource">
+        <Set name="driverClassName">org.some.Driver</Set>
+        <Set name="url">jdbc.url</Set>
+        <Set name="username">jdbc.user</Set>
+        <Set name="password">jdbc.pass</Set>
+      </New>
+    </Arg>
+  </New>
+</Configure>
+----
+
+There are many other settings that can be changed in a link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html[`WebAppContext`].
+The link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html[javadoc] for `WebAppContext` is a good source of information.
+Also see the documentation on link:#troubleshooting-zip-exceptions[avoiding zip file exceptions] for a description of `WebAppContext` settings that determine such things as whether or not the war is automatically unpacked during deployment, or whether certain sections of a webapp are copied to a temporary location.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/deployment-architecture.adoc b/jetty-documentation/src/main/asciidoc/configuring/deploying/deployment-architecture.adoc
new file mode 100644
index 0000000..3e7fd4e
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/deployment-architecture.adoc
@@ -0,0 +1,149 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[deployment-architecture]]
+=== Deployment Architecture
+
+Jetty is built around an extensible Deployment Manager architecture complete with formal LifeCycle for Web Applications going through it.
+
+For Jetty to serve content (static or dynamic), a link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandler.html[ContextHandler] needs to be configured and added to Jetty in the appropriate location.
+A pluggable `DeploymentManager` exists to make this process easier.
+The Jetty distribution contains example `DeploymentManager` configurations to deploy WAR files found in a directory to Jetty, and to deploy Jetty context xml files into Jetty as well.
+
+The `DeploymentManager` is the heart of the typical webapp deployment mechanism; it operates as a combination of an Application LifeCycle Graph, Application Providers that find and provide Applications into the Application LifeCycle Graph, and a set of bindings in the graph that control the deployment process.
+
+image:images/Jetty_DeployManager_DeploymentManager_Roles.png[image,width=195]
+
+[[udm-application-providers]]
+==== Application Providers
+
+Before Jetty deploys an application, an link:{JDURL}/org/eclipse/jetty/deploy/AppProvider.html[AppProvider] identifies the App and then provides it to the `DeploymentManager`.
+The main `AppProvider` with the Jetty distribution is the link:{JDURL}/org/eclipse/jetty/deploy/providers/WebAppProvider.html[WebAppProvider.]
+
+[[udm-application-lifecycle-graph]]
+==== Application LifeCycle Graph
+
+The core feature of the `DeploymentManager` is the link:{JDURL}/org/eclipse/jetty/deploy/AppLifeCycle.html[Application LifeCycle Graph].
+
+image:images/Jetty_DeployManager_AppLifeCycle-1.png[image,width=340]
+
+The nodes and edges of this graph are pre-defined in Jetty along the most common actions and states found.
+These nodes and edges are not hardcoded; they can be adjusted and added to depending on need (for example, any complex requirements for added workflow, approvals, staging, distribution, coordinated deploys for a cluster or cloud, etc.).
+
+New applications enter this graph at the Undeployed node, and the link:{JDURL}/org/eclipse/jetty/deploy/DeploymentManager.html#requestAppGoal(org.eclipse.jetty.deploy.App[`java.lang.String DeploymentManager.requestAppGoal(App,String)`] method pushes them through the graph.
+
+[[udm-lifecycle-bindings]]
+==== LifeCycle Bindings
+
+A set of default link:{JDURL}/org/eclipse/jetty/deploy/AppLifeCycle.Binding.html[`AppLifeCycle.Bindings`] defines standard behavior, and handles deploying, starting, stopping, and undeploying applications.
+If desired, custom `AppLifeCycle.Bindings` can be written and assigned anywhere on the Application LifeCycle graph.
+
+Examples of new `AppLifeCycle.Binding` implementations that can be developed include:
+
+* Validating the incoming application.
+* Preventing the deployment of known forbidden applications.
+* Submitting the installation to an application auditing service in a corporate environment.
+* Distributing the application to other nodes in the cluster or cloud.
+* Emailing owner/admin of change of state of the application.
+
+There are four default bindings:
+
+* link:{JDURL}/org/eclipse/jetty/deploy/bindings/StandardDeployer.html[StandardDeployer] — Deploys the ContextHandler into Jetty in the appropriate place.
+* link:{JDURL}/org/eclipse/jetty/deploy/bindings/StandardStarter.html[StandardStarter] — Sets the ContextHandler to started and start accepting incoming requests.
+* link:{JDURL}/org/eclipse/jetty/deploy/bindings/StandardStopper.html[StandardStopper] — Stops the ContextHandler and stops accepting incoming requests.
+* link:{JDURL}/org/eclipse/jetty/deploy/bindings/StandardUndeployer.html[StandardUndeployer] — Removes the ContextHandler from Jetty.
+
+image:images/Jetty_DeployManager_DefaultAppLifeCycleBindings.png[image,width=851]
+
+A fifth, non-standard binding, called link:{JDURL}/org/eclipse/jetty/deploy/bindings/DebugBinding.html[DebugBinding], is also available for debugging reasons; it logs the various transitions through the Application LifeCycle.
+
+[[default-web-app-provider]]
+==== Understanding the Default WebAppProvider
+
+The link:{JDURL}/org/eclipse/jetty/deploy/providers/WebAppProvider.html[WebAppProvider] is used for the deployment of Web Applications packaged as WAR files, expanded as a directory, or declared in a xref:deployable-descriptor-file[].
+It supports hot (re)deployment.
+
+The basic operation of the `WebAppProvider` is to periodically scan a directory for deployables.
+In the standard Jetty Distribution, this is configured in the `${jetty.home}/etc/jetty-deploy.xml` file.
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <Call name="addBean">
+    <Arg>
+      <New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
+        <Set name="contexts">
+          <Ref refid="Contexts" />
+        </Set>
+        <Call id="webappprovider" name="addAppProvider">
+          <Arg>
+            <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
+              <Set name="monitoredDirName"><Property name="jetty.home" default="." />/webapps</Set>
+              <Set name="defaultsDescriptor"><Property name="jetty.home" default="." />/etc/webdefault.xml</Set>
+              <Set name="scanInterval">1</Set>
+              <Set name="extractWars">true</Set>
+            </New>
+          </Arg>
+        </Call>
+      </New>
+    </Arg>
+  </Call>
+</Configure>
+----
+
+The above configuration will create a `DeploymentManager` tracked as a Server LifeCycle Bean, with the following configuration.
+
+contexts::
+  A passed in reference to the HandlerContainer into which the discovered webapps are deployed.
+  This is normally a reference that points to the `id="Contexts"` found in the `${jetty.home}/etc/jetty.xml` file, which itself is an instance of `ContextHandlerCollection`.
+
+monitoredDirName::
+  The file path or URL to the directory to scan for web applications.
+
+  Scanning follows these rules:
+  
+  1.  A base directory must exist.
+  2.  Hidden Files (starting with `"."`) are ignored.
+  3.  Directories with names ending in `".d"` are ignored.
+  4.  Common CVS directories `"CVS"` and `"CVSROOT"` are ignored.
+  5.  Any `*.war` files are considered link:#automatic-webapp-deployment[automatic deployables].
+  6.  Any `*.xml` files are considered link:#deployable-descriptor-file[context descriptor deployables].
+  7.  In the special case where both a WAR file and XML file exists for same base name, the XML file is assumed to configure and reference the WAR file (see xref:configuring-specific-webapp-deployment[]).
+  Since jetty-9.2.7, if either the WAR file or its corresponding XML file changes, the webapp will be redeployed.
+  8.  A directory is considered to be deployable.
+  9.  In the special case where both a Directory and WAR file of the same name exists, the WAR file is assumed to be an automatic deployable.
+  10. In the special case where both a Directory and XML file of the same name exists, the XML file is assumed to configure and reference the Directory.
+  11. All other directories are subject to automatic deployment.
+  12. If automatic deployment is used, and the special filename `root.war/ROOT.war` or directory name `root/ROOT` will result in a deployment to the `"/"` context path.
+
+defaultsDescriptor::
+  Specifies the default Servlet web descriptor to use for all Web Applications.
+  The intent of this descriptor is to include common configuration for the Web Application before the Web Application's own `/WEB-INF/web.xml` is applied.
+  The `${jetty.home}/etc/webdefault.xml` that comes with the Jetty distribution controls the configuration of the JSP and Default servlets, along with MIME-types and other basic metadata.
+
+scanInterval::
+  The period in seconds between sweeps of the `monitoredDirName` for changes: new contexts to deploy, changed contexts to redeploy, or removed contexts to undeploy.
+
+extractWars::
+  If parameter is true, any packed WAR or zip files are first extracted to a temporary directory before being deployed.
+  This is advisable if there are uncompiled JSPs in the web apps.
+
+parentLoaderPriority::
+  Parameter is a boolean that selects whether the standard Java link:#jetty-classloading[parent first delegation] is used or the link:#jetty-classloading[servlet specification webapp classloading priority].
+  The latter is the default.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/deployment-processing-webapps.adoc b/jetty-documentation/src/main/asciidoc/configuring/deploying/deployment-processing-webapps.adoc
new file mode 100644
index 0000000..52c22c9
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/deployment-processing-webapps.adoc
@@ -0,0 +1,300 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-webapps]]
+=== Deployment Processing of WebAppContexts
+
+Web applications require a certain amount of processing before they can go into service: they may need to be unpacked, a special classloader created for their jar files, `web.xml` and `web-fragment.xml` descriptors processed, and classes scanned for annotations amongst other things.
+As web applications have become more complex, Jetty has added ways to assist with customization by either broadening or lessening the amount of processing that is done at deployment time.
+This section will examine this processing and it can be tailored to fit individual needs.
+
+If instead you're looking for information on how to configure a specific `WebAppContext` - such as its context path, whether it should be unpacked or not - then you can find that in the section entitled link:#configuring-specific-webapp-deployment[Configuring a Specific WebApp Deployment].
+
+[[webapp-configurations]]
+==== Configuration Classes
+
+As a webapp is being deployed, a series of link:{JDURL}/org/eclipse/jetty/webapp/Configuration.html[org.eclipse.jetty.webapp.Configuration] classes are applied to it, each one performing a specific function.
+The ordering of these Configurations is significant as subsequent Configurations tend to build on information extracted or setup in foregoing Configurations.
+These are the default list, in order, of Configurations that are applied to each link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html[org.eclipse.jetty.webapp.WebAppContex]t:
+
+.Default Configuration classes
+[cols=",",]
+|=======================================================================
+|link:{JDURL}/org/eclipse/jetty/webapp/WebInfConfiguration.html[org.eclipse.jetty.webapp.WebInfConfiguration]
+|Extracts war, orders jars and defines classpath
+
+|link:{JDURL}/org/eclipse/jetty/webapp/WebXmlConfiguration.html[org.eclipse.jetty.webapp.WebXmlConfiguration]
+|Processes a WEB-INF/web.xml file
+
+|link:{JDURL}/org/eclipse/jetty/webapp/MetaInfConfiguration.html[org.eclipse.jetty.webapp.MetaInfConfiguration]
+|Looks in container and webapp jars for META-INF/resources and
+META-INF/web-fragment.xml
+
+|link:{JDURL}/org/eclipse/jetty/webapp/FragmentConfiguration.html[org.eclipse.jetty.webapp.FragmentConfiguration]
+|Processes all discovered META-INF/web-fragment.xml files
+
+|link:{JDURL}/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.html[org.eclipse.jetty.webapp.JettyWebXmlConfiguration]
+|Processes a WEB-INF/jetty-web.xml file
+|=======================================================================
+
+===== Anatomy of a Configuration Class
+
+A Configuration class is called 5 times in different phases of the link:http://download.eclipse.org/jetty/stable-9/apidocs/org/eclipse/jetty/webapp/WebAppContext.html[`WebAppContext's`] lifecycle:
+
+preConfigure::
+  As the `WebAppContext` is starting up this phase is executed.
+  The `Configuration` should discover any of the resources it will need during the subsequent phases.
+configure::
+  This phase is where the work of the class is done, usually using the resources discovered during the `preConfigure` phase.
+postConfigure::
+  This phase allows the `Configuration` to clear down any resources that may have been created during the previous 2 phases that are not needed for the lifetime of the `WebAppContext`.
+deconfigure::
+  This phase occurs whenever a `WebAppContext` is being stopped and allows the Configuration to undo any resources/metadata that it created.
+  A `WebAppContext` should be able to be cleanly start/stopped multiple times without resources being held.
+destroy::
+  This phase is called when a `WebAppContext` is actually removed from service.
+  For example, the war file associated with it is deleted from the $JETTY_HOME/webapps directory.
+
+Each phase is called on each `Configuration` class in the order in which the `Configuration` class is listed.
+Using the default `Configuration` classes as an example, `preConfigure()` will be called on `WebInfConfiguration`, `WebXmlConfiguration`, `MetaInfConfiguration`, `FragmentConfiguration` and then `JettyWebXmlConfiguration`.
+The cycle begins again for the `configure()` phase and again for the `postConfigure()` phases.
+The cycle is repeated _in reverse order_ for the `deconfigure()` and eventually the `destroy()` phases.
+
+===== Extending Container Support by Creating Extra Configurations
+
+As shown, there is a default set of Configurations that support basic deployment of a webapp.
+JavaEE features such as JNDI and advanced servlet spec features such as annotations have not been mentioned.
+Jetty's philosophy is to allow the user to tailor the container exactly to their needs.
+If these features are not needed, then Jetty does not pay the price for them - an important consideration because features such as annotations require extensive and time-consuming scanning of `WEB-INF/lib` jars.
+As modern webapps may have scores of these jars, it can be a source of significant deployment delay.
+We will see in the section link:#webapp-context-attributes[Other Configuration] another helpful webapp facility that Jetty provides for cutting down the time spent analyzing jars.
+
+Jetty makes use of the flexibility of Configurations to make JNDI and annotation support pluggable.
+
+Firstly, lets look at how Configurations help enable JNDI.
+
+[[jndi-configuration-classes]]
+====== Example: JNDI Configurations
+
+JNDI lookups within web applications require the container to hookup resources defined in the container's environment to that of the web application.
+To achieve that, we use 2 extra Configurations:
+
+.JNDI Configuration classes
+[cols=",",]
+|=======================================================================
+|link:{JDURL}/org/eclipse/jetty/plus/webapp/EnvConfiguration.html[org.eclipse.jetty.plus.webapp.EnvConfiguration]
+|Creates `java:comp/env` for the webapp, applies a `WEB-INF/jetty-env.xml` file
+
+|link:{JDURL}/org/eclipse/jetty/plus/webapp/PlusConfiguration.html[org.eclipse.jetty.plus.webapp.PlusConfiguration]
+|Processes JNDI related aspects of `WEB-INF/web.xml` and hooks up naming entries
+|=======================================================================
+
+These configurations must be added in _exactly_ the order shown above and should be inserted _immediately before_ the link:{JDURL}/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.html[org.eclipse.jetty.webapp.JettyWebXmlConfiguration] class in the list of configurations.
+To fully support JNDI additional configuration is required, full details of which can be found link:#jndi[here].
+
+====== Example: Annotation Configurations
+
+We need just one extra Configuration class to help provide servlet annotation scanning:
+
+.Annotation Configuration classes
+[cols=",",]
+|=======================================================================
+|link:{JDURL}/org/eclipse/jetty/annotations.AnnotationConfiguration.html[org.eclipse.jetty.annotations.AnnotationConfiguration]
+|Scan container and web app jars looking for @WebServlet, @WebFilter,
+@WebListener etc
+|=======================================================================
+
+The above configuration class must be _inserted immediately before_ the link:{JDURL}/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.html[org.eclipse.jetty.webapp.JettyWebXmlConfiguration] class in the list of configurations.
+To fully support annotations additional configuration is require, details of which can be found link:#webapp-context-attributes[below.]
+
+===== How to Set the List of Configurations
+
+You have a number of options for how to make Jetty use a different list of Configurations.
+
+====== Setting the list directly on the WebAppContext
+
+If you have only one webapp that you wish to affect, this may be the easiest option.
+You will, however, either need to have a context xml file that represents your web app, or you need to call the equivalent in code.
+Let's see an example of how we would add in the Configurations for both JNDI _and_ annotations:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+  <Set name="war"><SystemProperty name="jetty.base" default="."/>/webapps/my-cool-webapp</Set>
+
+  <Set name="configurationClasses">
+    <Array type="java.lang.String">
+      <Item>org.eclipse.jetty.webapp.WebInfConfiguration</Item>
+      <Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
+      <Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
+      <Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
+      <Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
+      <Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
+      <Item>org.eclipse.jetty.annotations.AnnotationConfiguration</Item>
+      <Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
+    </Array>
+  </Set>
+
+</Configure>
+----
+
+Of course, you can also use this method to reduce the Configurations applied to a specific `WebAppContext`.
+
+====== Setting the list for all webapps via the Deployer
+
+If you use the link:#deployment-architecture[deployer], you can set up the list of Configuration classes on the link:#default-web-app-provider[WebAppProvider].
+They will then be applied to each `WebAppContext` deployed by the deployer:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <Call name="addBean">
+    <Arg>
+      <New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
+        <Set name="contexts">
+          <Ref refid="Contexts" />
+        </Set>
+        <Call id="webappprovider" name="addAppProvider">
+          <Arg>
+            <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
+              <Set name="monitoredDirName"><Property name="jetty.base" default="." />/webapps</Set>
+              <Set name="configurationClasses">
+                <Array type="java.lang.String">
+                  <Item>org.eclipse.jetty.webapp.WebInfConfiguration</Item>
+                  <Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
+                  <Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
+                  <Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
+                  <Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
+                  <Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
+                  <Item>org.eclipse.jetty.annotations.AnnotationConfiguration</Item>
+                  <Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
+                </Array>
+              </Set>
+            </New>
+          </Arg>
+        </Call>
+      </New>
+    </Arg>
+  </Call>
+</Configure>
+----
+
+====== Adding or inserting to an existing list
+
+Instead of having to enumerate the list in its entirety, you can simply nominate classes that you want to add, and indicate whereabouts in the list you want them inserted.
+Let's look at an example of using this method to add in Configuration support for JNDI - as usual you can either do this in an xml file, or via equivalent code.
+This example uses an xml file, in fact it is the `$JETTY_HOME/etc/jetty-plus.xml` file from the Jetty distribution:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <!-- =========================================================== -->
+  <!-- Add plus Configuring classes to all webapps for this Server -->
+  <!-- =========================================================== -->
+  <Call class="org.eclipse.jetty.webapp.Configuration$ClassList" name="setServerDefault">
+    <Arg><Ref refid="Server" /></Arg>
+    <Call name="addAfter">
+      <Arg name="afterClass">org.eclipse.jetty.webapp.FragmentConfiguration</Arg>
+      <Arg>
+        <Array type="String">
+          <Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
+          <Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
+        </Array>
+      </Arg>
+    </Call>
+  </Call>
+
+</Configure>
+----
+
+The link:{JDURL}/org/eclipse/jetty/webapp/Configuration.html[org.eclipse.jetty.webapp.Configuration.ClassList] class provides these methods for insertion:
+
+addAfter::
+  Inserts the supplied list of `Configuration` class names after the given Configuration class name.
+addBefore::
+  Inserts the supplied list of `Configuration` class names before the given Configuration class name.
+
+[[webapp-context-attributes]]
+==== Other Configuration
+
+[[container-include-jar-pattern]]
+===== org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern
+
+This is a link:#context_attributes[context attribute] that can be set on link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html[an org.eclipse.jetty.webapp.WebAppContext] to control which parts of the _container's_ classpath should be processed for things like annotations, `META-INF/resources`, `META-INF/web-fragment.xml` and `tlds` inside `META-INF`.
+
+Normally, nothing from the container classpath will be included for processing.
+However, sometimes you will need to include some.
+For example, you may have some libraries that are shared amongst your webapps and thus you have put them into a `$JETTY_HOME/lib` directory.
+The libraries contain annotations and therefore must be scanned.
+
+The value of this attribute is a regexp that defines which _jars_ and _class directories_ from the container's classpath should be examined.
+
+Here's an example from a context xml file (although as always, you could have accomplished the same in code), which would match any jar whose name starts with "foo-" or "bar-", or a directory named "classes":
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+    <Call name="setAttribute">
+      <Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
+      <Arg>.*/foo-[^/]*\.jar$|.*/bar-[^/]*\.jar$|.*/classes/.*</Arg>
+    </Call>
+
+</Configure>
+----
+
+Note that the order of the patterns defines the ordering of the scanning of the jars or class directories.
+
+[[web-inf-include-jar-pattern]]
+===== org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern
+
+Similarly to the previous link:#context_attributes[context attribute], this attribute controls which jars are processed for things like annotations, `META-INF/resources`, `META-INF/web-fragment.xml` and `tlds` in `META-INF`.
+However, this attribute controls which jars from the _webapp's_ classpath (usually `WEB-INF/lib`) are processed.
+This can be particularly useful when you have dozens of jars in `WEB-INF/lib`, but you know that only a few need to be scanned.
+
+Here's an example in a xml file of a pattern that matches any jar that starts with `spring-`:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+    <Call name="setAttribute">
+      <Arg>org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern</Arg>
+      <Arg>.*/spring-[^/]*\.jar$</Arg>
+    </Call>
+
+</Configure>
+----
+
+Note that the order of the patterns defines the ordering of the scanning of jar files.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/hot-deployment.adoc b/jetty-documentation/src/main/asciidoc/configuring/deploying/hot-deployment.adoc
new file mode 100644
index 0000000..8a007b0
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/hot-deployment.adoc
@@ -0,0 +1,71 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[hot-deployment]]
+=== Hot Deployment
+
+Jetty allows for deploying an arbitrary context or web application by monitoring a directory for changes.
+If a web application or a context descriptor is added to the directory, Jetty's DeploymentManager (DM) deploys a new context.
+If a context descriptor is touched or updated, the DM stops, reconfigures, and redeploys its context.
+If a context is removed, the DM stops it and removes it from the server.
+
+This behavior can be controlled by configuring `WebAppProvider` properties.
+
+monitoredDirName::
+  The directory to scan for possible deployable Web Applications (or Deployment Descriptor XML files).
+scanInterval::
+  Number of seconds between scans of the provided `monitoredDirName`.
+  A value of `0` disables the continuous hot deployment scan, Web Applications will be deployed on startup only.
+
+The default location for this configuration is in the `${jetty.home}/etc/jetty-deploy.xml` file.
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <Call name="addBean">
+    <Arg>
+      <New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
+        <Set name="contexts">
+          <Ref refid="Contexts" />
+        </Set>
+        <Call name="setContextAttribute">
+          <Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
+          <Arg>.*/[^/]*servlet-api-[^/]*\.jar$|.*/javax.servlet.jsp.jstl-.*\.jar$|.*/org.apache.taglibs.taglibs-standard-impl-.*\.jar$</Arg>
+        </Call>
+
+        <Call id="webappprovider" name="addAppProvider">
+          <Arg>
+            <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
+              <Set name="monitoredDirName"><Property name="jetty.base" default="." />/<Property name="jetty.deploy.monitoredDir" deprecated="jetty.deploy.monitoredDirName" default="webapps"/></Set>
+              <Set name="defaultsDescriptor"><Property name="jetty.home" default="." />/etc/webdefault.xml</Set>
+              <Set name="scanInterval"><Property name="jetty.deploy.scanInterval" default="1"/></Set>
+              <Set name="extractWars"><Property name="jetty.deploy.extractWars" default="true"/></Set>
+            </New>
+          </Arg>
+        </Call>
+      </New>
+    </Arg>
+  </Call>
+</Configure>
+----
+
+See xref:default-web-app-provider[] for more configuration details.
+
+See also xref:deployment-architecture[] for detailed conceptual information.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/images/Jetty_DeployManager_AppLifeCycle-1.png b/jetty-documentation/src/main/asciidoc/configuring/deploying/images/Jetty_DeployManager_AppLifeCycle-1.png
new file mode 100644
index 0000000..522551a
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/images/Jetty_DeployManager_AppLifeCycle-1.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/images/Jetty_DeployManager_DefaultAppLifeCycleBindings.png b/jetty-documentation/src/main/asciidoc/configuring/deploying/images/Jetty_DeployManager_DefaultAppLifeCycleBindings.png
new file mode 100644
index 0000000..9436817
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/images/Jetty_DeployManager_DefaultAppLifeCycleBindings.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/images/Jetty_DeployManager_DeploymentManager_Roles.png b/jetty-documentation/src/main/asciidoc/configuring/deploying/images/Jetty_DeployManager_DeploymentManager_Roles.png
new file mode 100644
index 0000000..d37aefe
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/images/Jetty_DeployManager_DeploymentManager_Roles.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/overlay-deployer.adoc b/jetty-documentation/src/main/asciidoc/configuring/deploying/overlay-deployer.adoc
new file mode 100644
index 0000000..9a6e60f
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/overlay-deployer.adoc
@@ -0,0 +1,290 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[overlay-deployer]]
+=== Overlay WebApp Deployer
+
+____
+[NOTE]
+This feature was reintroduced in Jetty 9.0.4
+____
+
+The Jetty Overlay Deployer allows multiple WAR files to be overlaid so that a web application can be customized, configured, and deployed without unpacking, modifying and repacking the WAR file.
+This has the following benefits:
+
+* WAR files can be kept immutable, even signed, so that it is clear which version is deployed.
+* All modifications made to customize/configure the web application are separate WARs, and thus are easily identifiable for review and migration to new versions.
+* A parameterized template overlay can be created that contains common customizations and configuration that apply to many instances of the web application (for example, for multi-tenant deployment).
+* Because the layered deployment clearly identifies the common and instance specific components, Jetty is able to share classloaders and static resource caches for the template, greatly reducing the memory footprint of multiple instances.
+
+This tutorial describes how to configure Jetty to use the Overlay Deployer, and how to deploy multiple instances of a web application using the JTrac application in the example.
+
+[[overlay-overview]]
+==== Overview
+
+Customizing, configuring and deploying a web application bundled as a WAR file frequently includes some or all of these steps:
+
+* Editing the `WEB-INF/web.xml` file to set init parameters, add filters/servlets or to configure JNDI resources.
+* Editing other application specific configuration files under `WEB-INF/`.
+* Editing container specific configuration files under `WEB-INF/` (for example, `jetty-web.xml` or `jboss-web.xml`).
+* Adding/modifying static content such as images and CSS to create a style or themes for the web application.
+* Adding Jars to the container classpath for Datasource and other resources.
+* Modifying the container configuration to provide JNDI resources.
+
+The result is that the customizations and configurations blend into both the container and the WAR file.
+If either the container or the base WAR file is upgraded to a new version, it can be a very difficult and error prone task to identify all the changes that have been made and to reapply them to a new version.
+
+[[overlay-overlays]]
+==== Overlays
+
+To solve the problems highlighted above, Jetty introduced WAR overlays (a concept borrowed from the Maven WAR plugin).
+An overlay is basically just another WAR file, whose contents merge on top of the original WAR so that filed can be added or replaced.
+Jetty overlays also allow fragments of `web.xml` to be mixed in, which means the configuration can be modified without replacing it.
+
+[[overlay-jtrac]]
+==== JTrac Overlay Example
+
+The JTrac issue tracking web application is a good example of a typical web application, as it uses the usual suspects of libs: spring, hibernate, dom4j, commons-*, wicket, etc.
+The files for this demonstration are available in overlays-demo.tar.gz.
+The demonstration can be expanded on top of the Jetty distribution; this tutorial expands it to /tmp and installs the components step-by-step:
+
+[source, screen, subs="{sub-order}"]
+----
+$ cd /tmp
+$ wget http://webtide.com/wp-content/uploads/2011/05/overlays-demo.tar.gz
+$ tar xfvz overlays-demo.tar.gz
+$ export OVERLAYS=/tmp/overlays
+----
+
+[[overlay-configure]]
+==== Configuring Jetty for Overlays
+
+Overlays support is included in jetty distributions from 7.4.1-SNAPSHOT onwards, which can be downloaded from oss.sonatype.org or Maven Central and unpack into a directory.
+The `start.ini` file needs edited so that it includes the overlay option and configuration file.
+The resulting file should look like:
+
+[source, plain, subs="{sub-order}"]
+----
+OPTIONS=Server,jsp,jmx,resources,websocket,ext,overlay
+etc/jetty.xml
+etc/jetty-deploy.xml
+etc/jetty-overlay.xml
+----
+
+The mechanics of this are in etc/jetty-deploy.xml, which installs the `OverlayedAppProvider` into the `DeploymentManager`.
+Jetty can then be started normally:
+
+[source, screen, subs="{sub-order}"]
+----
+$ java -jar start.jar
+----
+
+Jetty is now listening on port 8080, but with no webapp deployed.
+
+____
+[IMPORTANT]
+You should conduct the rest of the tutorial in another window with the JETTY_HOME environmental variable set to the Jetty distribution directory.
+____
+
+[[overlay-install]]
+==== Installing the WebApp
+
+The WAR file for this demo can be downloaded and deployed the using the following commands, which downloads and extracts the WAR file to the $JETTY_HOME/overlays/webapps directory.
+
+[source, screen, subs="{sub-order}"]
+----
+$ cd /tmp
+$ wget -O jtrac.zip http://sourceforge.net/projects/j-trac/files/jtrac/2.1.0/jtrac-2.1.0.zip/download
+$ jar xfv jtrac.zip jtrac/jtrac.war
+$ mv jtrac/jtrac.war $JETTY_HOME/overlays/webapps
+----
+
+When these commands (or equivalent) have been executed, a message that the `OverlayedAppProvider` has extracted and loaded the WAR file will be displayed in the Jetty server window:
+
+[source, plain, subs="{sub-order}"]
+----
+2011-05-06 10:31:54.678:INFO:OverlayedAppProvider:Extract jar:file:/tmp/jetty-distribution-7.4.1-SNAPSHOT/overlays/webapps/jtrac-2.1.0.war!/ to /tmp/jtrac-2.1.0_236811420856825222.extract
+2011-05-06 10:31:55.235:INFO:OverlayedAppProvider:loaded jtrac-2.1.0@1304641914666
+----
+
+Unlike the normal webapps dir, loading a WAR file from the overlays/webapp dir does not deploy the web application, it simply makes it available to use as the basis for templates and overlays.
+
+==== Installing a Template Overlay
+
+A template overlay is a WAR structured directory/archive that contains the files that have been added or modified to customize/configure the web application for all instances planned for deployment.
+
+The demo template can be installed from the downloaded files with the command:
+
+[source, screen, subs="{sub-order}"]
+----
+$ mv $OVERLAYS/jtracTemplate\=jtrac-2.1.0 $JETTY_HOME/overlays/templates/
+----
+
+In the Jetty server window, a message similar to this will be displayed confirmed that the template is loaded:
+
+[source, plain, subs="{sub-order}"]
+----
+2011-05-06 11:00:08.716:INFO:OverlayedAppProvider:loaded jtracTemplate=jtrac-2.1.0@1304643608715
+----
+
+The contents of the loaded template are as follows:
+
+[source, plain, subs="{sub-order}"]
+----
+templates/jtracTemplate=jtrac-2.1.0
+|__ WEB-INF
+    |__ classes
+    |   |__ jtrac-init.properties
+    |__ log4j.properties
+    |__ overlay.xml
+    |__ template.xml
+    |__ web-overlay.xml
+----
+
+name of the template directory (or WAR)::
+  Uses the ‘=’ character in jtracTemplate=jtrac-2.1.0 to separate the name of the template from the name of the WAR file in webapps that it applies to.
+  If = is a problem, use -- instead.
+WEB-INF/classes/jtrac-init.properties::
+  Replaces the JTrac properties file with an empty file, as the properties it contains are configured elsewhere.
+WEB-INF/log4j.properties::
+  Configures the logging for all instances of the template.
+WEB-INF/overlay.xml::
+  A Jetty XML formatted IoC file that injects/configures the `ContextHandler` for each instance. \
+  In this case it sets up the context path:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/</Set>
+</Configure>
+----
+
+WEB-INF/template.xml::
+  A Jetty XML formatted IoC file that injects/configures the resource cache and classloader that all instances of the template share.
+  It runs only once per load of the template:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<Configure class="org.eclipse.jetty.overlays.TemplateContext">
+  <Get name="resourceCache">
+    <Set name="useFileMappedBuffer">true</Set>
+    <Set name="maxCachedFileSize">10000000</Set>
+    <Set name="maxCachedFiles">1000</Set>
+    <Set name="maxCacheSize">64000000</Set>
+  </Get>
+</Configure>
+----
+
+WEB-INF/web-overlay.xml::
+  A `web.xml` fragment that Jetty overlays on top of the `web.xml` from the base WAR file; it can set init parameters and add/modify filters and
+  servlets.
+  In this example it sets the application home and springs `webAppRootKey`:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+  <context-param>
+    <param-name>jtrac.home</param-name>
+    <param-value>/tmp/jtrac-${overlay.instance.classifier}</param-value>
+  </context-param>
+  <context-param>
+    <param-name>webAppRootKey</param-name>
+    <param-value>jtrac-${overlay.instance.classifier}</param-value>
+  </context-param>
+  <filter>
+</web-app>
+----
+
+Notice the parameterization of values such as `${overlays.instance.classifier}`, as this allows the configuration to be in the template, and not customized for each instance.
+
+Without the Overlay Deployer, all of the above would still need to have configure , but rather than being in a single clear structure the configuration elements would have been either in the server's common directory, the server's `webdefaults.xml` (aka `server.xml`), or baked into the WAR file of each application instance using copied/modified files from the original.
+The Overlay Deployer allows all these changes to be made in one structure; moreover it allows for the parameterization of some of the configuration, which facilitates easy multi-tenant deployment.
+
+==== Installing an Instance Overlay
+
+Now that the template is installed, one or more instance overlays can be implemented to deploy the actual web applications:
+
+[source, screen, subs="{sub-order}"]
+----
+$ mv /tmp/overlays/instances/jtracTemplate\=blue $JETTY_HOME/overlays/instances/
+$ mv /tmp/overlays/instances/jtracTemplate\=red $JETTY_HOME/overlays/instances/
+$ mv /tmp/overlays/instances/jtracTemplate\=blue $JETTY_HOME/overlays/instances/
+----
+
+As each instance moves into place, the Jetty server window reacts and deploys the instance.
+Within each instance, there is the structure:
+
+[source, plain, subs="{sub-order}"]
+----
+instances/jtracTemplate=red/
+|__ WEB-INF
+|   |__ overlay.xml
+|__ favicon.ico
+|__ resources
+    |__ jtrac.css
+----
+
+WEB-INF/overlay.xml::
+  A Jetty XML format IoC file that injects/configures the context for the instance.
+  In this case it sets up a virtual host for the instance:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="virtualHosts">
+    <Array type="String">
+      <Item>127.0.0.2</Item>
+      <Item>red.myVirtualDomain.com</Item>
+    </Array>
+  </Set>
+</Configure>
+----
+
+favicon.ico::
+  Replaces the icon in the base WAR with one that has a theme for the instance; in this case red, blue, or green.
+resources/jtrac.css::
+  Replaces the style sheet from the base WAR with one that has a theme for the instance.
+
+Deployed instances can be vied by pointing a browser at http://127.0.0.1:8080, http://127.0.0.2:8080 and http://127.0.0.3:8080.
+The default username/password for JTrac is admin/admin.
+
+[[overlay-tips]]
+==== Things to Know and Notice
+
+* Each instance has themes with images and style sheets from the instance overlay.
+* Each instance is running with its own application directory (that is, /tmp/jtrac-red), set in templates web-overlay.xml.
+* A virtual host set in the instance overlay.xml distinguishes the instances.
+* All instances share static content from the base WAR and template.
+Specifically there is a shared `ResourceCache` so only a single instance of each static content is loaded into memory.
+* All instances share the classloader at the base WAR and template level, so that only a single instance of common classes is loaded into memory.
+Classes with non shared statics can be configured to load in the instances classloader.
+* Jetty hot deploys all overlays and tracks dependencies.
+** If an XML changes in an instance, Jetty redeploys it.
+** If an XML changes in a template, then Jetty redeploys all instances using it.
+** If a WAR file changes, then Jetty redeploys all templates and all instances dependent on it.
+* New versions can be easily deployed.
+For example, when JTrac-2.2.0.war becomes available, it can be placed into `overlays/webapps` and then rename `jtracTemplate\=jtrac-2.1.0` to `jtracTemplate\=jtrac-2.2.0`.
+* There is a fuller version of this demo in overlays-demo-jndi.tar.gz, that uses JNDI (needs `options=jndi`, annotations and `jetty-plus.xml` in `start.ini`) and shows how additional JARs can be added in the overlays.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/quickstart-webapp.adoc b/jetty-documentation/src/main/asciidoc/configuring/deploying/quickstart-webapp.adoc
new file mode 100644
index 0000000..a3d563c
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/quickstart-webapp.adoc
@@ -0,0 +1,120 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[quickstart-webapp]]
+=== Quickstart Webapps
+
+The auto discovery features of the Servlet specification can make deployments slow and uncertain.
+Auto discovery of Web Application configuration can be useful during the development of a webapp as it allows new features and frameworks to be enabled simply by dropping in a jar file.
+However, for deployment, the need to scan the contents of many jars can have a significant impact of the start time of a webapp.
+
+With the release of Jetty 9.2, a quickstart module was included which allows a webapp to be pre-scanned and preconfigured.
+This means that all the scanning is done prior to deployment and all configuration is encoded into an effective `web.xml`, called `WEB-INF/quickstart-web.xml`, which can be inspected to understand what will be deployed before deploying.
+Not only does the `quickstart-web.xml` contain all the discovered Servlets, Filters and Constraints, but it also encodes as context parameters all discovered:
+
+* ServletContainerInitializers
+* HandlesTypes classes
+* Taglib Descriptors
+
+With the quickstart mechanism, Jetty is able to entirely bypass all scanning and discovery modes and start a webapp in a predictable and fast way.
+Tests have shown that webapps that took many seconds to scan and deploy can now be deployed in a few hundred milliseconds.
+
+==== Setting up Quickstart
+
+To use quickstart the module has to be available to the Jetty instance.
+In a standard Jetty distribution it can be configured with the following command:
+
+[source, screen, subs="{sub-order}"]
+----
+$ java -jar $JETTY_HOME/start.jar --add-to-startd=quickstart
+----
+
+In a Maven project this is done by adding a dependency on the artifact ID `jetty-quickstart`.
+
+[source, xml, subs="{sub-order}"]
+----
+<dependency>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-quickstart</artifactId>
+    <version>{VERSION}</version>
+</dependency>
+----
+
+Additionally, for those using Maven, the link:#get-up-and-running[Jetty Maven Plugin] has a goal, link:#jetty-effective-web-xml[`jetty:effective-web-xml`], which performs quickstart operations.
+It should be noted, however, that the Jetty Maven Plugin also includes additional items on it's classpath which may not be needed by the webapp.
+
+Deployed webapps need to be instances of link:{JDURL}/org/eclipse/jetty/quickstart/QuickStartWebApp.html[`org.eclipse.jetty.quickstart.QuickStartWebApp`] rather than the normal `org.eclipse.jetty.webapp.WebAppContext`.
+If a web application already has a `webapps/myapp.xml` file, simply change the class in the `Configure` element.
+Otherwise, create a `webapps/myapp.xml` file as follows:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<Configure class="org.eclipse.jetty.quickstart.QuickStartWebApp">
+  <Set name="war"><Property name="jetty.webapps" default="."/>/benchmark.war</Set>
+  <Set name="contextPath">/benchmark</Set>
+  <Set name="autoPreconfigure">true</Set>
+</Configure>
+----
+
+For embedded implementations of Jetty, invoking the link:{JDURL}/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.html[`org.eclipse.jetty.quickstart.PreconfigureQuickStartWar`] class can be used to configure war files for quickstart deployment.
+This will create the `quickstart-web.xml` before the first deployment.
+
+// ==== Preconfiguring the web application
+//
+// If the `QuickStateWebApp` method `setAutoPreconfigure(true)` is called (see example in myapp.xml above), then the first time the webapp is deployed a `WEB-INF/quickstart-web.xml` file will be generated that contains the effective `web.xml` for all the discovered configuration.
+// On subsequent deployments, all the discovery steps are skipped and the `quickstart-web.xml` is used directly to configure the web application.
+//
+// It is also possible to preconfigure a war file manually by running the class link:{JDURL}/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.html[org.eclipse.jetty.quickstart.PreconfigureQuickStartWar] with the jetty-all-uber (aggregate) jar:
+//
+// [source, screen, subs="{sub-order}"]
+// ----
+// $ java -cp jetty-all-{VERSION}-uber.jar org.eclipse.jetty.quickstart.PreconfigureQuickStartWar myapp.war
+// ----
+//
+// This will create the `quickstart-web.xml` file before the first deployment.
+// Note that this can also be a good debugging tool for discovered configuration and if run with debug turned on the origin of every element is included in the `quickstart-web.xml` file.
+// Run the class with no arguments to see other runtime options.
+
+==== Avoiding TLD Scans with precompiled JSPs
+
+Of course precompiling JSPs is an excellent way to improve the start time of a web application.
+As of Jetty 9.2 the Apache Jasper JSP implementation has been used and has been augmented to allow the TLD scan to be skipped.
+This can be done by adding a `context-param` to the `web.xml` file (this is done automatically by the Jetty Maven JSPC plugin):
+
+[source, xml, subs="{sub-order}"]
+----
+<context-param>
+  <param-name>org.eclipse.jetty.jsp.precompiled</param-name>
+  <param-value>true</param-value>
+</context-param>
+----
+
+==== Bypassing start.jar
+
+The Jetty `start.jar` mechanism is a very powerful and flexible mechanism for constructing a `classpath` and executing a configuration encoded in Jetty XML format.
+However, this mechanism does take some time to build the `classpath`.
+The start.jar mechanism can be bypassed by using the `–dry-run` option to generate and reuse a complete command line to start Jetty at a later time:
+
+[source, screen, subs="{sub-order}"]
+----
+$ RUN=$(java -jar $JETTY_HOME/start.jar --dry-run)
+$ eval $RUN
+----
+
+Note that `--dry-run` may create a properties file in the temp directory and include it on the generated command line.
+If so, then a copy of the temporary properties file should be taken and the command line updated with it's new persistent location.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/setting-deployment-bindings.adoc b/jetty-documentation/src/main/asciidoc/configuring/deploying/setting-deployment-bindings.adoc
new file mode 100644
index 0000000..cb3b327
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/setting-deployment-bindings.adoc
@@ -0,0 +1,42 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[setting-deployment-bindings]]
+=== Setting Deployment Bindings
+
+Jetty provides __deployment bindings__, which allows access to the application lifecycle of `DeploymentManager`.
+
+There are a handful of bindings that exist within the core distribution of Jetty:
+
+* `StandardDeployer` (deploying)–Standard binding that deals with deploying a webapp.
+* `StandardStarter` (starting)–Standard binding that deals with starting a webapp.
+* `StandardStopper` (stopping)–Standard binding that deals with stopping a webapp.
+* `StandardUndeployer` (undeploying)–Standard undeployer that deals with undeploying a webapp.
+* `DebugBinding` (any specified)–Attaches a binding and prints logging information of a context going through the specified binding target.
+* `GlobalWebappConfigBinding` (deploying)–Allows the user to override various settings of a webapp's context globally for all contexts.
++
+[source, xml, subs="{sub-order}"]
+----
+        <Call name="addLifeCycleBinding">
+          <Arg>
+            <New class="org.eclipse.jetty.deploy.bindings.GlobalWebappConfigBinding" >
+              <Set name="jettyXml">file://<Property name="jetty.home" default="." />/etc/global-webapp-context-config.xml</Set>
+            </New>
+          </Arg>
+        </Call>
+
+----
+* OrderedGroupBinding (any specified)–Allows the user to set a specific order for bindings to process in a given binding target.
diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/static-content-deployment.adoc b/jetty-documentation/src/main/asciidoc/configuring/deploying/static-content-deployment.adoc
new file mode 100644
index 0000000..4461e6c
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/static-content-deployment.adoc
@@ -0,0 +1,42 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[static-content-deployment]]
+=== Configuring Static Content Deployment
+
+To serve purely static content, the Jetty Deployment Descriptor XML concepts and the internal `ResourceHandler` can be used.
+Create a file called `scratch.xml` in the `${jetty.base}/webapps` directory and paste the following file contents in it.
+
+[source, xml, subs="{sub-order}"]
+----
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
+  <Set name="contextPath">/scratch</Set>
+  <Set name="handler">
+    <New class="org.eclipse.jetty.server.handler.ResourceHandler">
+      <Set name="resourceBase">/home/jesse/scratch</Set>
+      <Set name="directoriesListed">true</Set>
+    </New>
+  </Set>
+</Configure>
+
+
+----
+
+This is a very basic setup for serving static files.
+For advanced static file serving, use the link:{JXURL}/org/eclipse/jetty/servlet/DefaultServlet.html[DefaultServlet].
diff --git a/jetty-documentation/src/main/asciidoc/configuring/jsp/chapter.adoc b/jetty-documentation/src/main/asciidoc/configuring/jsp/chapter.adoc
new file mode 100644
index 0000000..4964940
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/jsp/chapter.adoc
@@ -0,0 +1,20 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-jsp]]
+== Configuring JSP Support
+
+include::configuring-jsp.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/configuring/jsp/configuring-jsp.adoc b/jetty-documentation/src/main/asciidoc/configuring/jsp/configuring-jsp.adoc
new file mode 100644
index 0000000..7985d4a
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/jsp/configuring-jsp.adoc
@@ -0,0 +1,331 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jsp-support]]
+=== Configuring JSP
+
+This document provides information about configuring Java Server Pages (JSP) for Jetty.
+
+[[which-jsp-implementation]]
+==== Which JSP Implementation
+
+As of Jetty 9.2, Jetty is using Jasper from http://tomcat.apache.org/tomcat-8.0-doc/jasper-howto.html[Apache] as the default JSP container implementation.
+
+By default the Jetty distribution enables the JSP link:#startup-modules[module], and by default, this link:#startup-modules[module] is set to Apache Jasper.
+
+[source, plain, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-distribution/src/main/resources/modules/jsp.mod[]
+----
+
+Note that the availability of some JSP features may depend on which JSP container implementation you are using.
+Note also that it may not be possible to precompile your JSPs with one container and deploy to the other.
+
+===== JSPs and Embedding
+
+If you have an embedded setup for your webapp and wish to use JSPs, you will need to ensure that a JSP engine is correctly initialized.
+
+For Apache, a Servlet Specification 3.1 style link:#servlet-container-initializers[ServletContainerInitializer] is used to accomplish this.
+You will need to ensure that this ServletContainerInitializer is run by jetty. Perhaps the easiest way to do this is to enable annotations processing so that Jetty automatically discovers and runs it.
+The link:#embedded-examples[Embedded Examples] section includes a link:#embedded-webapp-jsp[worked code example] of how to do this.
+
+Alternatively, you can manually wire in the appropriate ServletContainerInitializer as shown in the https://github.com/jetty-project/embedded-jetty-jsp/blob/master/src/main/java/org/eclipse/jetty/demo/Main.java[embedded-jetty-jsp] example on https://github.com/jetty-project[GitHub], in which case you will not need the jetty-annotations jar on your classpath, nor include the AnnotationConfiguration in the list of link:#webapp-configurations[configuration classes].
+
+==== Precompiling JSPs
+
+You can either follow the instructions on precompilation provided by Apache, or if you are using Maven for your builds, you can use the link:#jetty-jspc-maven-plugin[jetty-jspc-maven] plugin to do it for you.
+
+If you have precompiled your JSPs, and have customized the output package prefix (which is `org.apache.jsp` by default), you should configure your webapp context to tell Jetty about this custom package name.
+You can do this using a servlet context init-param called `org.eclipse.jetty.servlet.jspPackagePrefix`.
+
+For example, suppose you have precompiled your JSPs with the custom package prefix of `com.acme`, then you would add the following lines to your `web.xml` file:
+
+[source, xml, subs="{sub-order}"]
+----
+  <context-param>
+    <param-name>org.eclipse.jetty.servlet.jspPackagePrefix</param-name>
+    <param-value>com.acme</param-value>
+  </context-param>
+----
+
+____
+[NOTE]
+Both Jetty Maven plugins - link:#jetty-jspc-maven-plugin[jetty-jspc-maven-plugin] and the link:#jetty-maven-plugin[jetty-maven-plugin] - will only use Apache Jasper.
+____
+
+[[compiling-jsps]]
+===== Apache JSP Container
+
+By default, the Apache JSP container will look for the Eclipse Java Compiler (jdt).
+The Jetty distribution ships a copy of this in `{$jetty.home}/lib/apache-jsp`.
+If you wish to use a different compiler, you will need to configure the `compilerClassName` init-param on the `JspServlet` with the name of the class.
+
+.Understanding Apache JspServlet Parameters
+[cols=",,,",options="header",]
+|=======================================================================
+|init param |Description |Default |`webdefault.xml`
+|classpath |`Classpath used for jsp compilation. Only used if
+                org.apache.catalina.jsp_classpath context attribute is not
+                set, which it is in Jetty.` |- |–
+
+|classdebuginfo |Include debugging info in class file. |TRUE |–
+
+|checkInterval |Interval in seconds between background recompile checks.
+Only relevant if `
+                development=false`. |0 |–
+
+|development |`development=true`, recompilation checks occur on each
+request. See also `
+                modificationTestInterval`. |TRUE |–
+
+|displaySourceFragment |Should a source fragment be included in
+exception messages |TRUE |–
+
+|errorOnUseBeanInvalidClassAttribute |Should Jasper issue an error when
+the value of the class attribute in an useBean action is not a valid
+bean class |TRUE |–
+
+|fork |Should Ant fork its Java compiles of JSP pages? |TRUE |FALSE
+
+|keepgenerated |Do you want to keep the generated Java files around?
+|TRUE |–
+
+|trimSpaces |Should white spaces between directives or actions be
+trimmed? |FALSE |–
+
+|enablePooling |Determines whether tag handler pooling is enabled. |TRUE
+|–
+
+|engineOptionsClass |Allows specifying the Options class used to
+configure Jasper. If not present, the default EmbeddedServletOptions
+will be used. |–
+
+|mappedFile |Support for mapped Files. Generates a servlet that has a
+print statement per line of the JSP file  |TRUE |–
+
+|suppressSmap |Generation of SMAP info for JSR45 debugging. |FALSE |–
+
+|dumpSmap |Dump SMAP JSR45 info to a file. |FALSE |–
+
+|genStrAsCharArray |Option for generating Strings. |FALSE |–
+
+|ieClassId |The class-id value to be sent to Internet Explorer when
+using <jsp:plugin> tags. |clsid:8AD9C840-044E-11D1-B3E9-00805F499D93 |–
+
+|maxLoadedJsps |The maximum number of JSPs that will be loaded for a web
+application. If more than this number of JSPs are loaded, the least
+recently used JSPs will be unloaded so that the number of JSPs loaded at
+any one time does not exceed this limit. A value of zero or less
+indicates no limit. |-1 |–
+
+|jspIdleTimeout |The amount of time in seconds a JSP can be idle before
+it is unloaded. A value of zero or less indicates never unload. |-1 |–
+
+|scratchDir |Directory where servlets are generated. See |– |–
+
+|compilerClassName |If not set, defaults to the Eclipse jdt compiler. |–
+
+|compiler |Used if the Eclipse jdt compiler cannot be found on the
+classpath. It is the classname of a compiler that Ant should invoke. |–
+|–
+
+|compilerTargetVM |Target vm to compile for. |1.7 |–
+
+|compilerSourceVM |Sets source compliance level for the jdt compiler.
+|1.7 |–
+
+|javaEncoding |Pass through the encoding to use for the compilation.
+|UTF8 |–
+
+|modificationTestInterval |If `development=true`, interval between
+recompilation checks, triggered by a request. |4 |–
+
+|xpoweredBy |Generate an X-Powered-By response header. |FALSE |FALSE
+
+|recompileOnFail |If a JSP compilation fails should the
+modificationTestInterval be ignored and the next access trigger a
+re-compilation attempt? Used in development mode only and is disabled by
+default as compilation may be expensive and could lead to excessive
+resource usage. |- |–
+|=======================================================================
+
+[[configuring-jsp-for-jetty]]
+===== Configuration
+
+The JSP engine has many configuration parameters.
+Some parameters affect only precompilation, and some affect runtime recompilation checking.
+Parameters also differ among the various versions of the JSP engine.
+This page lists the configuration parameters, their meanings, and their default settings.
+Set all parameters on the `org.apache.jasper.JspServlet` instance defined in the link:#webdefault-xml[`webdefault.xml`] file.
+
+____
+[NOTE]
+Be careful: for all of these parameters, if the value you set doesn't take effect, try using all lower case instead of camel case, or capitalizing only some of the words in the name, as JSP is inconsistent in its parameter naming strategy.
+____
+
+[[modifying-configuration]]
+==== Modifying Configuration
+
+[[overriding-webdefault.xml]]
+===== Overriding `webdefault.xml`
+
+You can make a copy of the link:#webdefault-xml[{$jetty.home}/etc/webdefault.xml] that ships with Jetty, apply your changes, and use it instead of the shipped version.
+The example below shows how to do this when using the Jetty Maven plugin.
+
+[source, xml, subs="{sub-order}"]
+----
+  <plugin>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-maven-plugin</artifactId>
+    <configuration>
+      <webApp>
+        <defaultsDescriptor>src/main/resources/webdefault.xml</defaultsDescriptor>
+      </webApp>
+  </plugin>
+----
+
+If you are using the Jetty distribution, and you want to change the JSP settings for just one or a few of your webapps, copy the `{$jetty.home}/etc/webdefault.xml` file somewhere, modify it, and then use a link:#intro-jetty-configuration-contexts[context xml] file to set this file as the `defaultsDescriptor` for your webapp. Here's a snippet:
+
+[source, xml, subs="{sub-order}"]
+----
+ <Configure class=>"org.eclipse.jetty.webapp.WebAppContext">
+
+   <Set name=>"contextPath">/foo</Set>
+   <Set name=>"war"><SystemProperty name=>"jetty.home" >default=>"."/>/webapps/foobar.war</Set>
+   <Set name=>"defaultsDescriptor">/home/smith/dev/webdefault.xml</Set>
+
+ </Configure>
+----
+
+If you want to change the JSP settings for all webapps, edit the `{$jetty.home}/etc/webdefaults.xml` file directly instead.
+
+[[configuring-jsp-servlet-in-web.xml]]
+===== Configuring the JSP Servlet in `web.xml`
+
+Another option is to add an entry for the JSPServlet to the `WEB-INF/web.xml` file of your webapp and change or add init-params.
+You may also add (but not remove) servlet-mappings.
+You can use the entry in link:#webdefault-xml[{$jetty.home}/etc/webdefault.xml] as a starting point.
+
+[source, xml, subs="{sub-order}"]
+----
+ <servlet id="jsp">
+     <servlet-name>jsp</servlet-name>
+     <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+     <init-param>
+         <param-name>logVerbosityLevel</param-name>
+         <param-value>DEBUG</param-value>
+     </init-param>
+     <init-param>
+         <param-name>fork</param-name>
+         <param-value>>false</param-value>
+     </init-param>
+     <init-param>
+         <param-name>keepgenerated</param-name>
+         <param-value>>true</param-value>
+     </init-param>
+     ...
+
+     <load-on-startup>0</load-on-startup>
+   </servlet>
+
+   <servlet-mapping>
+     <servlet-name>jsp</servlet-name>
+     <url-pattern>*.jsp</url-pattern>
+     <url-pattern>*.jspf</url-pattern>
+     <url-pattern>*.jspx</url-pattern>
+     <url-pattern>*.xsp</url-pattern>
+     <url-pattern>*.JSP</url-pattern>
+     <url-pattern>*.JSPF</url-pattern>
+     <url-pattern>*.JSPX</url-pattern>
+     <url-pattern>*.XSP</url-pattern>
+   </servlet-mapping>
+
+   <servlet id="my-servlet">
+     <servlet-name>myServlet</servlet-name>
+     <servlet-class>com.acme.servlet.MyServlet</servlet-class>
+      ...
+----
+
+[[using-jstl-taglibs-for-jetty7-jetty8]]
+==== Using JSTL Taglibs
+
+The JavaServer Pages Standlard Tag Library (JSTL) is part of the Jetty distribution and is automatically put on the classpath when you link:#which-jsp-implementation[select your flavour of JSP].
+It is also automatically on the classpath for the Jetty Maven plugin, which uses the Apache JSP engine as of Jetty 9.2.
+
+===== Embedding
+
+If you are using Jetty in an embedded scenario, and you need to use JSTL, then you must ensure that the JSTL jars are included on the _container's_ classpath - that is the classpath that is the _parent_ of the webapp's classpath.
+This is a restriction that arises from the JavaEE specification.
+
+====== Apache JSP
+
+You will need to put the jars that are present in the `{$jetty.home}/lib/apache-jstl` directory onto the _container's_ classpath.
+The Apache JSP engine will find the JSTL tag definitions inside these jars during startup.
+
+As an efficiency enhancement, you can have jetty examine the JSTL jars to find the tags, and pre-feed them into the Apache JSP engine.
+This is more efficient, because jetty will only scan the jars you tell it to, whereas the Apache JSP engine will scan every jar, which can be time-consuming in applications with a lot of jars on the container classpath.
+
+To take advantage of this efficiency enhancement, set up the link:#container-include-jar-pattern[org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern] to include a http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html[pattern] that will match the names of the JSTL jars.
+The link:#embedded-examples[Embedded Examples] section includes a link:#embedded-webapp-jsp[worked code example] of how to do this.
+Below is a snippet from the example:
+
+[source, java, subs="{sub-order}"]
+----
+  webapp.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",".*/[^/]*taglibs.*\\.jar$");
+----
+
+[[using-jsf-taglibs]]
+==== Using JSF Taglibs
+
+The following sections provide information about using JSF TagLibs with Jetty Standalone and the Jetty Maven Plugin.
+
+[[using-jsf-taglibs-with-jetty-standalone]]
+===== Using JSF Taglibs with Jetty Distribution
+
+If you want to use JSF with your webapp, you need to copy the JSF implementation Jar (whichever Jar contains the `META-INF/*.tld` files from your chosen JSF implementation) into Jetty's shared container lib directory.
+You can either put them into the lib directory for Apache `{$jetty.home}/lib/apache-jsp` or put them into `{$jetty.home}/lib/ext`.
+
+[[using-jsf-taglibs-with-jetty-maven-plugin]]
+===== Using JSF Taglibs with Jetty Maven Plugin
+
+You should make your JSF jars dependencies of the plugin and _not_ the webapp itself.
+For example:
+
+[source, xml, subs="{sub-order}"]
+----
+   <plugin>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-maven-plugin</artifactId>
+    <configuration>
+       <webApp>
+         <contextPath>/${artifactId}</contextPath>
+       </webApp>
+       <scanIntervalSeconds>5</scanIntervalSeconds>
+    </configuration>
+    <dependencies>
+      <dependency>
+        <groupId>com.sun.faces</groupId>
+        <artifactId>jsf-api</artifactId>
+        <version>2.0.8</version>
+      </dependency>
+      <dependency>
+        <groupId>com.sun.faces</groupId>
+        <artifactId>jsf-impl</artifactId>
+        <version>2.0.8</version>
+     </dependency>
+    </dependencies>
+  </plugin>
+----
diff --git a/jetty-documentation/src/main/asciidoc/configuring/part.adoc b/jetty-documentation/src/main/asciidoc/configuring/part.adoc
new file mode 100644
index 0000000..bad646e
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/part.adoc
@@ -0,0 +1,24 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+[[jetty-config-guide]]
+
+= Jetty Configuration Guide
+
+include::deploying/chapter.adoc[]
+include::contexts/chapter.adoc[]
+include::connectors/chapter.adoc[]
+include::security/chapter.adoc[]
+include::jsp/chapter.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/configuring/security/authentication.adoc b/jetty-documentation/src/main/asciidoc/configuring/security/authentication.adoc
new file mode 100644
index 0000000..c58453c
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/security/authentication.adoc
@@ -0,0 +1,389 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-security-authentication]]
+=== Authentication
+
+There are two aspects to securing a web application(or context) within the Jetty server:
+
+Authentication::
+  The web application can be configured with a mechanism to determine the identity of the user.
+  This is configured by a mix of standard declarations and jetty specific mechanisms and is covered in this section.
+Authorization::
+  Once the identify of the user is known (or not known), the web application can be configured via standard descriptors with security constraints that declare what resources that user may access.
+
+==== Configuring an Authentication mechanism
+
+The jetty server supports several standard authentication mechanisms: http://en.wikipedia.org/wiki/Basic_access_authentication[BASIC]; http://en.wikipedia.org/wiki/Digest_authentication[DIGEST]; http://en.wikipedia.org/wiki/Form-based_authentication[FORM]; CLIENT-CERT; and other mechanisms can be plugged in using the extensible http://docs.oracle.com/cd/E19462-01/819-6717/gcszc/index.html[JASPI] or http://en.wikipedia.org/wiki/SPNEGO[SPNEGO] mechanisms.
+
+Internally, configuring an authentication mechanism is done by setting an instance of a the link:{JDURL}/org/eclipse/jetty/security/Authenticator.html[Authenticator] interface onto the link:{JDURL}/org/eclipse/jetty/security/SecurityHandler.html[SecurityHandler] of the context, but in most cases it is done by declaring a `<    login-config>` element in the standard web.xml descriptor or via annotations.
+
+Below is an example taken from the link:{GITBROWSEURL}/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml?h=release-9[jetty-test-webapp web.xml] that configures BASIC authentication:
+
+[source, xml, subs="{sub-order}"]
+----
+  <login-config>
+    <auth-method>BASIC</auth-method>
+    <realm-name>Test Realm</realm-name>
+  </login-config>
+
+----
+
+The link:{GITBROWSEURL}/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml?h=release-9[jetty-test-webapp web.xml] also includes commented out examples of other DIGEST and FORM configuration:
+
+[source, xml, subs="{sub-order}"]
+----
+  <login-config>
+    <auth-method>FORM</auth-method>
+    <realm-name>Test Realm</realm-name>
+    <form-login-config>
+       <form-login-page>/logon.html?param=test</form-login-page>
+       <form-error-page>/logonError.html?param=test</form-error-page>
+    </form-login-config>
+  </login-config>
+
+----
+
+With FORM Authentication, you must also configure URLs of pages to generate a login form and handle errors.
+Below is a simple HTML form from the link:{GITBROWSEURL}/tests/test-webapps/test-jetty-webapp/src/main/webapp/logon.html?h=release-9[test webapp logon.html]:
+
+[source, xml, subs="{sub-order}"]
+----
+<HTML>
+<H1>FORM Authentication demo</H1>
+<form method="POST" action="j_security_check">
+<table border="0" cellspacing="2" cellpadding="1">
+<tr>
+  <td>Username:</td>
+  <td><input size="12" value="" name="j_username" maxlength="25" type="text"></td>
+</tr>
+<tr>
+  <td>Password:</td>
+  <td><input size="12" value="" name="j_password" maxlength="25" type="password"></td>
+</tr>
+<tr>
+  <td colspan="2" align="center">
+    <input name="submit" type="submit" value="Login">
+  </td>
+</tr>
+</table>
+</form>
+</HTML>
+
+----
+
+The Authentication mechanism declared for a context / web application defines how the server obtain authentication credentials from the
+client, but it does not define how the server checks if those credentials are valid.
+To check credentials, the server and/or context also need to be configured with a link:{JDURL}/org/eclipse/jetty/security/LoginService.html[LoginService] instance, which may be matched by the declared realm-name.
+
+[[security-realms]]
+==== Security Realms
+
+Security realms allow you to secure your web applications against unauthorized access.
+Protection is based on authentication that identifies who is requesting access to the webapp and access control that restricts what can be accessed and how it is accessed within the webapp.
+
+A webapp statically declares its security requirements in its web.xml file.
+Authentication is controlled by the `<login-config>` element.
+Access controls are specified by `<security-constraint>` and `<security-role-ref>` elements.
+When a request is received for a protected resource, the web container checks if the user performing the request is authenticated, and if the user has a role assignment that permits access to the requested resource.
+
+The Servlet Specification does not address how the static security information in the `WEB-INF/web.xml` file is mapped to the runtime environment of the container.
+For Jetty, the link:{JDURL}/org/eclipse/jetty/security/LoginService.html[LoginService] performs this function.
+
+A LoginService has a unique name, and gives access to information about a set of users.
+Each user has authentication information (e.g. a password) and a set of roles associated with him/herself.
+
+You may configure one or many different LoginServices depending on your needs.
+A single realm would indicate that you wish to share common security information across all of your web applications.
+Distinct realms allow you to partition your security information webapp by webapp.
+
+When a request to a web application requires authentication or authorization, Jetty will use the `<realm-name>` sub-element inside `<login-config>` element in the web.xml file to perform an _exact match_ to a LoginService.
+
+==== Scoping Security Realms
+
+A LoginService has a unique name, and is composed of a set of users. Each user has authentication information (for example, a password) and a set of roles associated with him/herself.
+You can configure one or many different realms depending on your needs.
+
+* Configure a single LoginService to share common security information across all of your web applications.
+* Configure distinct LoginServices to partition your security information webapp by webapp.
+
+===== Globally Scoped
+
+A LoginService is available to all web applications on a Server instance if you add it as a bean to the Server.
+Such a definition would go into an xml file in your `${jetty.base}/etc` directory, e.g. `${jetty.base}/etc/my-realm.xml` and you would add this xml file to the execution path via `start.ini` or `start.d` (you may want to review the material in the link:#startup[Starting Jetty] chapter).
+Here's an example of an xml file that defines an in-memory type of LoginService called the link:{JDURL}/org/eclipse/jetty/security/HashLoginService.html[HashLoginService]:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+  <Call name="addBean">
+    <Arg>
+      <New class="org.eclipse.jetty.security.HashLoginService">
+        <Set name="name">Test Realm</Set>
+        <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
+        <Set name="refreshInterval">0</Set>
+      </New>
+    </Arg>
+  </Call>
+</Configure>
+
+
+----
+
+If you define more than one LoginService on a Server, you will need to specify which one you want used for each context.
+You can do that by telling the context the name of the LoginService, or passing it the LoginService instance.
+Here's an example of doing both of these, using a link:#deployable-descriptor-file[context xml file]:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+ <Get name="securityHandler">
+   <!-- Either: -->
+   <Set name="loginService">
+     <New class="org.eclipse.jetty.security.HashLoginService">
+           <Set name="name">Test Realm</Set>
+     </New>
+   </Set>
+
+   <!-- or if you defined a LoginService called "Test Realm" in jetty.xml : -->
+   <Set name="realmName">Test Realm</Set>
+
+ </Get>
+
+
+----
+
+===== Per-Webapp Scoped
+
+Alternatively, you can define a LoginService for just a single web application.
+Here's how to define the same HashLoginService, but inside a link:#deployable-descriptor-file[context xml file]:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/test</Set>
+  <Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/test</Set>
+  <Get name="securityHandler">
+    <Set name="loginService">
+      <New class="org.eclipse.jetty.security.HashLoginService">
+            <Set name="name">Test Realm</Set>
+            <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
+      </New>
+    </Set>
+  </Get>
+</Configure>
+
+
+----
+
+Jetty provides a number of different LoginService types which can be seen in the next section.
+
+[[configuring-login-service]]
+==== Configuring a LoginService
+
+A link:{JDURL}/org/eclipse/jetty/security/LoginService.html[LoginService] instance is required by each context/webapp that has a authentication mechanism, which is used to check the validity of the username and credentials collected by the authentication mechanism. Jetty provides the following implementations of LoginService:
+
+link:{JDURL}/org/eclipse/jetty/security/HashLoginService.html[HashLoginService]::
+  A user realm that is backed by a hash map that is filled either programatically or from a Java properties file.
+link:{JDURL}/org/eclipse/jetty/security/JDBCLoginService.html[JDBCLoginService]::
+  Uses a JDBC connection to an SQL database for authentication
+link:{JDURL}/org/eclipse/jetty/plus/security/DataSourceLoginService.html[DataSourceLoginService]::
+  Uses a JNDI defined   http://docs.oracle.com/javase/7/docs/api/javax/sql/DataSource.html[DataSource] for authentication
+link:{JDURL}/org/eclipse/jetty/jaas/JAASLoginService.html[JAASLoginService]::
+  Uses a http://en.wikipedia.org/wiki/Java_Authentication_and_Authorization_Service[JAAS] provider for authentication; see the section on
+  link:#jaas-support[JAAS support] for more information
+link:{JDURL}/org/eclipse/jetty/security/SpnegoLoginService.html[SpnegoLoginService]::
+  http://en.wikipedia.org/wiki/SPNEGO[SPNEGO] Authentication; see the section on link:#spnego-support[SPNEGO support] for more information.
+
+An instance of a LoginService can be matched to a context/webapp by:
+
+* A LoginService instance may be set directly on the SecurityHandler instance via embedded code or IoC XML
+* Matching the realm-name defined in web.xml with the name of a LoginService instance that has been added to the Server instance as a dependent bean
+* If only a single LoginService instance has been set on the Server then it is used as the login service for the context
+
+[[hash-login-service]]
+===== HashLoginService
+
+The HashLoginService is a simple and efficient login service that loads usernames, credentials and roles from a Java properties file in the format:
+
+[source,properties]
+----
+
+username: password[,rolename ...]
+
+----
+
+Where:
+
+username::
+  is the user's unique identity
+password::
+  is the user's (possibly obfuscated or MD5 encrypted) password;
+rolename::
+  is a role of the user
+
+For example:
+
+[source,properties]
+----
+
+admin: CRYPT:ad1ks..kc.1Ug,server-administrator,content-administrator,admin
+other: OBF:1xmk1w261u9r1w1c1xmq
+guest: guest,read-only
+
+----
+
+You configure the HashLoginService with a name and a reference to the location of the properties file:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<Item>
+<New class="org.eclipse.jetty.security.HashLoginService">
+  <Set name="name">Test Realm</Set>
+  <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
+</New>
+</Item>
+
+----
+
+You can also configure it to check the properties file regularly for changes and reload when changes are detected.
+The `reloadInterval` is in seconds:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<New class="org.eclipse.jetty.security.HashLoginService">
+    <Set name="name">Test Realm</Set>
+    <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
+    <Set name="reloadInterval">5</Set>
+    <Call name="start"></Call>
+  </New>
+
+----
+
+[[jdbc-login-service]]
+===== JDBCLoginService
+
+In this implementation, authentication and role information is stored in a database accessed via JDBC.
+A properties file defines the JDBC connection and database table information.
+Here is an example of a properties file for this realm implementation:
+
+[source,properties]
+----
+
+jdbcdriver = org.gjt.mm.mysql.Driver
+url = jdbc:mysql://localhost/jetty
+username = jetty
+password = jetty
+usertable = users
+usertablekey = id
+usertableuserfield = username
+usertablepasswordfield = pwd
+roletable = roles
+roletablekey = id
+roletablerolefield = role
+userroletable = user_roles
+userroletableuserkey = user_id
+userroletablerolekey = role_id
+cachetime = 300
+
+----
+
+The format of the database tables is (pseudo-sql):
+
+[source,sql]
+----
+
+users
+(
+  id integer PRIMARY KEY,
+  username varchar(100) NOT NULL UNIQUE KEY,
+  pwd varchar(50) NOT NULL
+);
+user_roles
+(
+  user_id integer NOT NULL,
+  role_id integer NOT NULL,
+  UNIQUE KEY (user_id, role_id),
+  INDEX(user_id)
+);
+roles
+(
+  id integer PRIMARY KEY,
+  role varchar(100) NOT NULL UNIQUE KEY
+);
+
+----
+
+Where:
+
+* *users* is a table containing one entry for every user consisting of:
++
+id::
+  the unique identity of a user
+user::
+  the name of the user
+pwd::
+  the user's password (possibily obfuscated or MD5 encrypted)
+* *user-roles* is a table containing one row for every role granted to a
+user:
++
+user_id::
+  the unique identity of the user
+role_id::
+  the role for a user
+* *roles* is a a table containing one role for every role in the system:
++
+id::
+  the unique identifier of a role
+role::
+  a human-readable name for a role
+
+If you want to use obfuscated, MD5 hashed or encrypted passwords the `pwd` column of the `users` table must be large enough to hold the obfuscated, hashed or encrypted password text plus the appropriate prefix.
+
+You define a `JDBCLoginService` with the name of the realm and the location of the properties file describing the database:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<New class="org.eclipse.jetty.security.JDBCLoginService">
+  <Set name="name">Test JDBC Realm</Set>
+  <Set name="config">etc/jdbcRealm.properties</Set>
+</New>
+
+----
+
+==== Authorization
+
+As far as the http://jcp.org/aboutJava/communityprocess/final/jsr340/[Servlet Specification] is concerned, authorization is based on roles.
+As we have seen, a LoginService associates a user with a set of roles.
+When a user requests a resource that is access protected, the LoginService will be asked to authenticate the user if they are not already, and then asked to confirm if that user possesses one of the roles permitted access to the resource.
+
+Until Servlet 3.1, role-based authorization could define:
+
+* access granted to a set of named roles
+* access totally forbidden, regardless of role
+* access granted to a user in any of the roles defined in the effective web.xml.
+This is indicated by the special value of "*" for the `<role-name>` of a `<auth-constraint> `in the `<security-constraint>`
+
+With the advent of Servlet 3.1, there is now another authorization:
+
+* access granted to any user who is authenticated, regardless of roles.
+This is indicated by the special value of "**" for the `<role-name>` of a `<auth-constraint>` in the `<security-constraint>`
diff --git a/jetty-documentation/src/main/asciidoc/configuring/security/authorization.adoc b/jetty-documentation/src/main/asciidoc/configuring/security/authorization.adoc
new file mode 100644
index 0000000..02fb0ea
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/security/authorization.adoc
@@ -0,0 +1,39 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-security-authorization]]
+=== Authorization
+
+There are two aspects to securing a web application(or context) within
+Jetty:
+
+Authentication::
+  The web application can be configured with a mechanism to determine
+  the identity of the user. See
+  link:#configuring-security-authentication[Configurating Security -
+  Authentication].
+Authorization::
+  Once the identify of the user is known (or not known), the web
+  application can be configured with security constraints that declare
+  what resources that user may access. This is covered in this section.
+
+==== Blah blah blah
+
+blah blah blah
+
+==== Blah blah blah
+
+blah blah blah
diff --git a/jetty-documentation/src/main/asciidoc/configuring/security/chapter.adoc b/jetty-documentation/src/main/asciidoc/configuring/security/chapter.adoc
new file mode 100644
index 0000000..7807df1
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/security/chapter.adoc
@@ -0,0 +1,26 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-security]]
+== Configuring Security
+
+include::jetty-home-and-jetty-base.adoc[]
+include::authentication.adoc[]
+include::configuring-form-size.adoc[]
+include::serving-aliased-files.adoc[]
+include::secure-passwords.adoc[]
+include::jaas-support.adoc[]
+include::spnego-support.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/configuring/security/configuring-form-size.adoc b/jetty-documentation/src/main/asciidoc/configuring/security/configuring-form-size.adoc
new file mode 100644
index 0000000..f60166b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/security/configuring-form-size.adoc
@@ -0,0 +1,73 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-form-size]]
+=== Limiting Form Content
+
+Form content sent to the server is processed by Jetty into a map of parameters to be used by the web application.
+This can be vulnerable to denial of service (DOS) attacks since significant memory and CPU can be consumed if a malicious clients sends very large form content or large number of form keys.
+Thus Jetty limits the amount of data and keys that can be in a form posted to Jetty.
+
+The default maximum size Jetty permits is 200000 bytes and 1000 keys.
+You can change this default for a particular webapp or for all webapps on a particular Server instance.
+
+==== Configuring Form Limits for a Webapp
+
+To configure the form limits for a single web application, the context handler (or webappContext) instance must be configured using the following methods:
+
+[source, java, subs="{sub-order}"]
+----
+ContextHandler.setMaxFormContentSize(int maxSizeInBytes);
+ContextHandler.setMaxFormKeys(int formKeys);
+
+----
+
+These methods may be called directly when embedding Jetty, but more commonly are configured from a context XML file or WEB-INF/jetty-web.xml file:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+  ...
+
+  <Set name="maxFormContentSize">200000</Set>
+  <Set name="maxFormKeys">200</Set>
+</Configure>
+
+----
+
+==== Configuring Form Limits for the Server
+
+If a context does not have specific form limits configured, then the server attributes are inspected to see if a server wide limit has been set on the size or keys.
+The following XML shows how these attributes can be set in `jetty.xml`:
+
+[source, xml, subs="{sub-order}"]
+----
+<configure class="org.eclipse.jetty.server.Server">
+
+  ...
+
+  <Call name="setAttribute">
+    <Arg>org.eclipse.jetty.server.Request.maxFormContentSize</Arg>
+    <Arg>100000</Arg>
+   </Call>
+  <Call name="setAttribute">
+    <Arg>org.eclipse.jetty.server.Request.maxFormKeys</Arg>
+    <Arg>2000</Arg>
+   </Call>
+</configure>
+
+----
diff --git a/jetty-documentation/src/main/asciidoc/configuring/security/jaas-support.adoc b/jetty-documentation/src/main/asciidoc/configuring/security/jaas-support.adoc
new file mode 100644
index 0000000..7bfb90e
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/security/jaas-support.adoc
@@ -0,0 +1,421 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jaas-support]]
+=== JAAS Support
+
+JAAS implements a Java version of the standard Pluggable Authentication Module (PAM) framework.
+
+JAAS can be used for two purposes:
+
+* for authentication of users, to reliably and securely determine who is currently executing Java code, regardless of whether the code is running as an application, an applet, a bean, or a servlet
+* for authorization of users to ensure they have the access control rights (permissions) required to do the actions performed
+
+JAAS authentication is performed in a pluggable fashion.
+This permits applications to remain independent from underlying authentication technologies.
+New or updated authentication technologies can be plugged under an application without requiring modifications to the application itself.
+Applications enable the authentication process by instantiating a `LoginContext` object, which in turn references a configuration to determine the authentication technology(ies), or `LoginModule`(s), to be used in performing the authentication.
+Typical `LoginModules` may prompt for and verify a username and password.
+Others may read and verify a voice or fingerprint sample.
+
+See Java Authentication and Authorization Service (JAAS) http://java.sun.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html[Reference Guide] for more information about JAAS.
+
+[[jetty-jaas]]
+==== Jetty and JAAS
+
+Many application servers support JAAS as a means of bringing greater flexibility to the declarative security models of the J2EE (now known as the JavaEE) http://java.sun.com/javaee/index.jsp[specification].
+Jetty support for JAAS provides greater alternatives for servlet security, and increases the portability of web applications.
+
+The JAAS support aims to dictate as little as possible whilst providing a sufficiently flexible infrastructure to allow users to drop in their
+own custom http://java.sun.com/j2se/1.4.2/docs/guide/security/jaas/JAASLMDevGuide.html[LoginModules].
+
+[[jaas-configuration]]
+==== Configuration
+
+Using JAAS with Jetty is very simply a matter of declaring a `org.eclipse.jetty.jaas.JAASLoginService`, creating a JAAS login module configuration file and specifying it on the Jetty run line.
+Let's look at an example.
+
+===== Step 1
+
+Configure a Jetty `org.eclipse.jetty.jaas.JAASLoginService` to match the `<realm-name>` in your `web.xml` file. For example, if the `web.xml` contains a realm called "xyz" like so:
+
+[source, xml, subs="{sub-order}"]
+----
+<login-config>
+  <auth-method>FORM</auth-method>
+  <realm-name>xyz</realm-name>
+  <form-login-config>
+    <form-login-page>/login/login</form-login-page>
+    <form-error-page>/login/error</form-error-page>
+  </form-login-config>
+</login-config>
+----
+
+Then you need to create a `JAASLoginService` with the matching name of "xyz":
+
+[source, xml, subs="{sub-order}"]
+----
+<New class="org.eclipse.jetty.jaas.JAASLoginService">
+  <Set name="Name">Test JAAS Realm</Set>
+  <Set name="LoginModuleName">xyz</Set>
+</New>
+----
+
+____
+[CAUTION]
+The name of the realm-name that you declare in `web.xml` must match exactly the name of your `JAASLoginService`.
+____
+
+You can declare your `JAASLoginService` in a couple of different ways:
+
+1.  If you have more than one webapp that you would like to use the same security infrastructure, then you can declare your `JAASLoginService` in a top-level Jetty xml file as a bean that is added to the `org.eclipse.jetty.server.Server`.
+An example:
++
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <Call name="addBean">
+    <Arg>
+      <New class="org.eclipse.jetty.jaas.JAASLoginService">
+        <Set name="name">Test JAAS Realm</Set>
+        <Set name="LoginModuleName">xyz</Set>
+      </New>
+    </Arg>
+  </Call>
+
+</Configure>
+----
+2.  Alternatively, you can use a `JAASLoginService` with just a specific webapp by creating a link:#deployable-descriptor-file[context xml] file for the webapp, and specifying the `JAASLoginService` in it:
++
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+  <Set name="securityHandler">
+    <New class="org.eclipse.jetty.security.ConstraintSecurityHandler">
+     <Set name="loginService">
+       <New class="org.eclipse.jetty.jaas.JAASLoginService">
+         <Set name="name">Test JAAS Realm</Set>
+         <Set name="loginModuleName">xyz</Set>
+       </New>
+     </Set>
+    </New>
+  </Set>
+
+</Configure>
+----
+
+[[jaas-step-2]]
+===== Step 2
+
+Set up your `LoginModule` in a configuration file, following the http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/login/Configuration.html[syntax rules] :
+
+[source,ini]
+----
+xyz {
+       com.acme.SomeLoginModule required debug=true;
+    };
+----
+
+____
+[CAUTION]
+It is imperative that the application name on the first line is exactly the same as the `LoginModuleName` of your `JAASLoginService`.
+____
+
+You may find it convenient to name this configuration file as `etc/login.conf` because, as we will see below, some of the wiring up for JAAS has been done for you.
+
+===== Step 3
+
+You now need to invoke Jetty with support for JAAS.
+There are 2 aspects to this:
+
+* adding JAAS-related jars to the Jetty container classpath
+* setting the System property `java.security.auth.login.config`
+
+To accomplish the above, use the Jetty link:#startup-overview[startup] link:#startup-modules[modules mechanism] to add the JAAS link:#startup-modules[module]:
+
+[source,bash]
+----
+java -jar start.jar --add-to-startd=jaas
+----
+
+____
+[NOTE]
+The top level of the distribution does not have the JAAS module enabled by default.
+However, there are several link:#demo-webapps-base[demo webapps] - including a JAAS webapp - available in the `demo-base` directory of the distribution which has pre-enabled the JAAS module.
+____
+
+Now you will have a file named `start.d/jaas.ini`, which contains:
+
+[source,ini]
+----
+--module=jaas
+jaas.login.conf=etc/login.conf
+----
+
+The `jaas.login.conf` property refers to the location of your `LoginModule` configuration file that you established in link:#jaas-step-2[Step 2].
+If you called it `etc/login.conf`, then your work is done. Otherwise, change the value of the `jaas.login.conf` property to be the location of your LoginModule configuration file.
+Jetty will automatically use this property to set the value of the System property `java.security.auth.login.config.`
+
+==== A Closer Look at JAASLoginService
+
+To allow the greatest degree of flexibility in using JAAS with web applications, the `JAASLoginService` supports a couple of configuration options.
+Note that you don't ordinarily need to set these explicitly, as Jetty has defaults which will work in 99% of cases.
+However, should you need to, you can configure:
+
+* a policy for role-based authorization (Default: `org.eclipse.jetty.jaas.StrictRoleCheckPolicy`)
+* a CallbackHandler (Default: `org.eclipse.jetty.jaas.callback.DefaultCallbackHandler`)
+* a list of classnames for the Principal implementation that equate to a user role (Default: `org.eclipse.jetty.jaas.JAASRole`)
+
+Here's an example of setting each of these (to their default values):
+
+[source, xml, subs="{sub-order}"]
+----
+<New class="org.eclipse.jetty.jaas.JAASLoginService">
+  <Set name="Name">Test JAAS Realm</Set>
+  <Set name="LoginModuleName">xyz</Set>
+  <Set name="RoleCheckPolicy">
+    <New class="org.eclipse.jetty.jaas.StrictRoleCheckPolicy"/>
+  </Set>
+  <Set name="CallbackHandlerClass">
+       org.eclipse.jetty.jaas.callback.DefaultCallbackHandler
+  </Set>
+  <Set name="roleClassNames">
+    <Array type="java.lang.String">
+      <Item>org.eclipse.jetty.jaas.JAASRole</Item>
+    </Array>
+  </Set>
+</New>
+----
+
+===== RoleCheckPolicy
+
+The `RoleCheckPolicy` must be an implementation of the `org.eclipse.jetty.jaas.RoleCheckPolicy` interface and its purpose is to help answer the question "is User X in Role Y" for role-based authorization requests.
+The default implementation distributed with Jetty is the `org.eclipse.jetty.jaas.StrictRoleCheckPolicy`, which will assess a user as having a particular role if that role is at the top of the stack of roles that have been temporarily pushed onto the user.
+If the user has no temporarily assigned roles, the role is amongst those configured for the user.
+
+Roles can be temporarily assigned to a user programmatically by using the `pushRole(String rolename)` method of the `org.eclipse.jetty.jaas.JAASUserPrincipal` class.
+
+For the majority of webapps, the default `StrictRoleCheckPolicy` will be quite adequate, however you may provide your own implementation and set it on your `JAASLoginService` instance.
+
+===== CallbackHandler
+
+A CallbackHandler is responsible for interfacing with the user to obtain usernames and credentials to be authenticated.
+
+Jetty ships with the `org.eclipse.jetty.jaas.DefaultCallbackHandler` which interfaces the information contained in the request to the Callbacks that are requested by `LoginModules`.
+You can replace this default with your own implementation if you have specific requirements not covered by the default.
+
+===== Role Principal Implementation Class
+
+When `LoginModules` authenticate a user, they usually also gather all of the roles that a user has and place them inside the JAAS Subject.
+As `LoginModules` are free to use their own implementation of the JAAS Principal to put into the Subject, Jetty needs to know which Principals represent the user and which represent his/her roles when performing authorization checks on `<security-constraint>`. The example `LoginModules` that ship with Jetty all use the `org.eclipse.jetty.jaas.JAASRole` class. However, if you have plugged in other `LoginModules`, you must configure the classnames of their role Principal implementations.
+
+===== Sample LoginModules
+
+* link:{JXURL}/org/eclipse/jetty/jaas/spi/JDBCLoginModule.html[`org.eclipse.jetty.jaas.spi.JDBCLoginModule`]
+* link:{JXURL}/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.html[`org.eclipse.jetty.jaas.spi.PropertyFileLoginModule`]
+* link:{JXURL}/org/eclipse/jetty/jaas/spi/DataSourceLoginModule.html[`org.eclipse.jetty.jaas.spi.DataSourceLoginModule`]
+* link:{JXURL}/org/eclipse/jetty/jaas/spi/LdapLoginModule.html[`org.eclipse.jetty.jaas.ldap.LdapLoginModule`]
+
+____
+[NOTE]
+Passwords can be stored in clear text, obfuscated or checksummed.
+The class link:{JDURL}/org/eclipse/jetty/util/security/Password.html[`org.eclipse.jetty.util.security.Password`] should be used to generate all varieties of passwords,the output from which can be put in to property files or entered into database tables.
+See more on this under the Configuration section on link:#configuring-security-secure-passwords[securing passwords].
+____
+
+===== JDBCLoginModule
+
+The `JDBCLoginModule` stores user passwords and roles in a database that are accessed via JDBC calls.
+You can configure the JDBC connection information, as well as the names of the table and columns storing the username and credential, and the names of the table and columns storing the roles.
+
+Here is an example login module configuration file entry for it using an HSQLDB driver:
+
+[source,ini]
+----
+
+jdbc {
+      org.eclipse.jetty.jaas.spi.JDBCLoginModule required
+      debug="true"
+      dbUrl="jdbc:hsqldb:."
+      dbUserName="sa"
+      dbDriver="org.hsqldb.jdbcDriver"
+      userTable="myusers"
+      userField="myuser"
+      credentialField="mypassword"
+      userRoleTable="myuserroles"
+      userRoleUserField="myuser"
+      userRoleRoleField="myrole";
+      };
+----
+
+There is no particular schema required for the database tables storing the authentication and role information.
+The properties `userTable`, `userField`, `credentialField`, `userRoleTable`, `userRoleUserField`, `userRoleRoleField` configure the names of the tables and the columns within them that are used to format the following queries:
+
+[source,sql]
+----
+  select <credentialField> from <userTable>
+          where <userField> =?
+  select <userRoleRoleField> from <userRoleTable>
+          where <userRoleUserField> =?
+----
+
+Credential and role information is lazily read from the database when a previously unauthenticated user requests authentication.
+Note that this information is _only_ cached for the length of the authenticated session.
+When the user logs out or the session expires, the information is flushed from memory.
+
+Note that passwords can be stored in the database in plain text or encoded formats - see the note on "Passwords/Credentials" above.
+
+===== DataSourceLoginModule
+
+Similar to the `JDBCLoginModule`, but this `LoginModule` uses a `DataSource` to connect to the database instead of a JDBC driver. The `DataSource` is obtained by performing a JNDI lookup on `java:comp/env/${dnJNDIName}`.
+
+A sample login module configuration using this method:
+
+[source,ini]
+----
+
+ds {
+     org.eclipse.jetty.jaas.spi.DataSourceLoginModule required
+     debug="true"
+     dbJNDIName="ds"
+     userTable="myusers"
+     userField="myuser"
+     credentialField="mypassword"
+     userRoleTable="myuserroles"
+     userRoleUserField="myuser"
+     userRoleRoleField="myrole";
+    };
+----
+
+===== PropertyFileLoginModule
+
+With this login module implementation, the authentication and role information is read from a property file.
+
+[source,ini]
+----
+props {
+        org.eclipse.jetty.jaas.spi.PropertyFileLoginModule required
+        debug="true"
+        file="/somewhere/somefile.props";
+      };
+----
+
+The file parameter is the location of a properties file of the same format as the `etc/realm.properties` example file.
+The format is:
+
+[source,text]
+----
+<username>: <password>[,<rolename> ...]
+----
+
+Here's an example:
+
+[source,ini]
+----
+fred: OBF:1xmk1w261u9r1w1c1xmq,user,admin
+harry: changeme,user,developer
+tom: MD5:164c88b302622e17050af52c89945d44,user
+dick: CRYPT:adpexzg3FUZAk,admin
+----
+
+The contents of the file are fully read in and cached in memory the first time a user requests authentication.
+
+===== LdapLoginModule
+
+Here's an example:
+
+[source,ini]
+----
+
+ldaploginmodule {
+   org.eclipse.jetty.jaas.spi.LdapLoginModule required
+   debug="true"
+   contextFactory="com.sun.jndi.ldap.LdapCtxFactory"
+   hostname="ldap.example.com"
+   port="389"
+   bindDn="cn=Directory Manager"
+   bindPassword="directory"
+   authenticationMethod="simple"
+   forceBindingLogin="false"
+   userBaseDn="ou=people,dc=alcatel"
+   userRdnAttribute="uid"
+   userIdAttribute="uid"
+   userPasswordAttribute="userPassword"
+   userObjectClass="inetOrgPerson"
+   roleBaseDn="ou=groups,dc=example,dc=com"
+   roleNameAttribute="cn"
+   roleMemberAttribute="uniqueMember"
+   roleObjectClass="groupOfUniqueNames";
+   };
+----
+
+==== Writing your Own LoginModule
+
+If you want to implement your own custom `LoginModule`, there are two classes to be familiar with:  `org.eclipse.jetty.jaas.spi.AbstractLoginModule` and `org.eclipse.jetty.jaas.spi.UserInfo`.
+
+The `org.eclipse.jetty.jaas.spi.AbstractLoginModule` implements all of the `javax.security.auth.spi.LoginModule` methods.
+All you need to do is to implement the `getUserInfo` method to return a `org.eclipse.jetty.jaas.UserInfo` instance which encapsulates the username, password and role names (note: as `java.lang.Strings`) for a user.
+
+The `AbstractLoginModule` does not support any caching, so if you want to cache UserInfo (eg as does the `org.eclipse.jetty.jaas.spi.PropertyFileLoginModule`) then you must provide this yourself.
+
+==== Other Goodies
+
+===== RequestParameterCallback
+
+As all servlet containers intercept and process a form submission with action `j_security_check`, it is usually not possible to insert any extra input fields onto a login form with which to perform authentication: you may only pass `j_username` and `j_password`.
+For those rare occasions when this is not good enough, and you require more information from the user in order to authenticate them, you can use the JAAS callback handler `org.eclipse.jetty.jaas.callback.RequestParameterCallback`.
+This callback handler gives you access to all parameters that were passed in the form submission.
+To use it, in the `login()` method of your custom login module, add the `RequestParameterCallback` to the list of callback handlers the login module uses, tell it which params you are interested in, and then get the value of the parameter back.
+Here is an example:
+
+[source, java, subs="{sub-order}"]
+----
+
+public class FooLoginModule extends AbstractLoginModule
+{
+     public boolean login()
+        throws LoginException
+     {
+
+        Callback[] callbacks = new Callback[3];
+        callbacks[0] = new NameCallback();
+        callbacks[1] = new ObjectCallback();
+
+        //as an example, look for a param named "extrainfo" in the request
+        //use one RequestParameterCallback() instance for each param you want to access
+        callbacks[2] = new RequestParameterCallback ();
+        ((RequestParameterCallback)callbacks[2]).setParameterName ("extrainfo");
+
+
+        callbackHandler.handle(callbacks);
+        String userName = ((NameCallback)callbacks[0]).getName();
+        Object pwd = ((ObjectCallback)callbacks[1]).getObject();
+        List paramValues = ((RequestParameterCallback)callbacks[2]).getParameterValues();
+
+        //use the userName, pwd and the value(s) of the parameter named "extrainfo" to
+        //authenticate the user
+
+     }
+}
+----
+
+===== Example JAAS WebApp
+
+An example webapp using JAAS can be found in the Jetty GitHub repository:
+
+* link:{GITBROWSEURL}/tests/test-webapps/test-jaas-webapp[https://github.com/eclipse/jetty.project/tree/master/tests/test-webapps/test-jaas-webapp]
diff --git a/jetty-documentation/src/main/asciidoc/configuring/security/jetty-home-and-jetty-base.adoc b/jetty-documentation/src/main/asciidoc/configuring/security/jetty-home-and-jetty-base.adoc
new file mode 100644
index 0000000..174d2f5
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/security/jetty-home-and-jetty-base.adoc
@@ -0,0 +1,520 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-home-and-jetty-base]]
+=== Using the $\{jetty.home} and $\{jetty.base} Concepts to Configure
+Security
+
+Jetty 9.1 introduced `${jetty.base}` and `${jetty.home}`.
+
+* `${jetty.home}` is the directory location for the jetty distribution (the binaries).
+* `${jetty.base}` is the directory location for your customizations to the distribution.
+
+This separation:
+
+* Allows you to manage multiple Jetty installations.
+* Makes it simple to retain your current configuration when you upgrade your Jetty distribution.
+
+For more information, see xref:startup-base-and-home[].
+
+Further, Jetty 9.1 parameterized all of the standard configuration XMLs.
+For SSL, parameters are now properties in the `start.ini` or `start.d\ssl.ini`, reducing to eliminating the need to edit XML files.
+
+Instead of explicitly listing all the libraries, properties, and XML files for a feature, Jetty 9.1 introduced a new module system.
+A module is defined in a `modules/*.mod` file, including the libraries, dependencies, XML, and template INI files for a Jetty feature.
+Thus you can use a single `--module=name` command line option as the equivalent of specifying many `--lib=location, feature.xml, name=value` arguments for a feature and all its dependencies.
+Modules use their dependencies to control the ordering of libraries and XML files.
+For more information, see xref:startup-modules[].
+
+[[configuring-security-jetty91]]
+==== Configuring SSL in with modules
+
+This page describes how to configure SSL in Jetty with modules.
+It provides an example of using the `${jetty.home}` and `${jetty.base}` to maximum effect.
+It also includes a detailed explanation of how modules work.
+
+This example assumes you have the jetty-distribution unpacked in `/home/user/jetty-distribution-{VERSION}`.
+It also assumes you are using `start.ini` to configure your server features.
+
+1.  Create a base directory anywhere.
++
+[source, screen, subs="{sub-order}"]
+....
+[/home/user]$ mkdir my-base
+[/home/user]$ cd my-base
+....
+2.  Add the modules for SSL, HTTP, and webapp deployment.
+Adding modules in this way will append the associated module properties to the `${jetty.base}/start.ini` file.
++
+[source, screen, subs="{sub-order}"]
+....
+[my-base]$ java -jar /home/user/jetty-distribution-{VERSION}/start.jar --add-to-start=http,https,deploy
+
+ssl             initialised in ${jetty.base}/start.ini (appended)
+ssl             enabled in     ${jetty.base}/start.ini
+DOWNLOAD: https://github.com/eclipse/jetty.project/raw/master/jetty-server/src/main/config/etc/keystore to etc/keystore
+server          initialised in ${jetty.base}/start.ini (appended)
+server          enabled in     ${jetty.base}/start.ini
+http            initialised in ${jetty.base}/start.ini (appended)
+http            enabled in     ${jetty.base}/start.ini
+server          enabled in     ${jetty.base}/start.ini
+deploy          initialised in ${jetty.base}/start.ini (appended)
+deploy          enabled in     ${jetty.base}/start.ini
+MKDIR: ${jetty.base}/webapps
+server          enabled in     ${jetty.base}/start.ini
+....
+3.  Look at your directory.
++
+[source, screen, subs="{sub-order}"]
+....
+[my-base]$ ls -la
+total 20
+drwxrwxr-x   4 user group 4096 Oct  8 06:55 ./
+drwxr-xr-x 103 user group 4096 Oct  8 06:53 ../
+drwxrwxr-x   2 user group 4096 Oct  8 06:55 etc/
+-rw-rw-r--   1 user group  815 Oct  8 06:55 start.ini
+drwxrwxr-x   2 user group 4096 Oct  8 06:55 webapps/
+....
+4.  Copy your WAR files into webapps.
++
+[source, screen, subs="{sub-order}"]
+....
+[my-base]$ ls -la
+[my-base]$ cp ~/code/project/target/gadget.war webapps/
+....
+5.  Copy your keystore into place.
++
+[source, screen, subs="{sub-order}"]
+....
+[my-base]$ cp ~/code/project/keystore etc/keystore
+....
+6.  Edit the `start.ini` to configure your SSL settings.
++
+[source, screen, subs="{sub-order}"]
+....
+[my-base]$ cat start.ini
+....
+7.  Initialize module ssl.
++
+....
+--module=ssl
+....
+8.  Define the port to use for secure redirection.
++
+....
+jetty.secure.port=8443
+....
+9.  Set up a demonstration keystore and truststore.
++
+....
+jetty.keystore=etc/keystore
+jetty.truststore=etc/keystore
+....
+10. Set the demonstration passwords.
++
+....
+jetty.keystore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
+jetty.keymanager.password=OBF:1u2u1wml1z7s1z7a1wnl1u2g
+jetty.truststore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
+....
+11. Initialize the module server.
++
+....
+--module=server
+threads.min=10
+threads.max=200
+threads.timeout=60000
+#jetty.host=myhost.com
+jetty.dump.start=false
+jetty.dump.stop=false
+....
+12. Initialize module http.
++
+....
+--module=http
+jetty.http.port=8080
+http.timeout=30000
+....
+13. Initialize module deploy.
++
+....
+--module=deploy
+....
+
+Look at the configuration you have at this point.
+
+[source, screen, subs="{sub-order}"]
+....
+[my-base]$ java -jar /home/user/jetty-distribution-{VERSION}/start.jar --list-config
+
+Java Environment:
+-----------------
+ java.home=/usr/lib/jvm/jdk-7u21-x64/jre
+ java.vm.vendor=Oracle Corporation
+ java.vm.version=23.21-b01
+ java.vm.name=Java HotSpot(TM) 64-Bit Server VM
+ java.vm.info=mixed mode
+ java.runtime.name=Java(TM) SE Runtime Environment
+ java.runtime.version=1.7.0_21-b11
+ java.io.tmpdir=/tmp
+
+Jetty Environment:
+-----------------
+ jetty.home=/home/user/jetty-distribution-{VERSION}
+ jetty.base=/home/user/my-base
+ jetty.version={VERSION}
+
+JVM Arguments:
+--------------
+ (no jvm args specified)
+
+System Properties:
+------------------
+ jetty.base = /home/user/my-base
+ jetty.home = /home/user/jetty-distribution-{VERSION}
+
+Properties:
+-----------
+ http.timeout = 30000
+ jetty.dump.start = false
+ jetty.dump.stop = false
+ jetty.keymanager.password = OBF:1u2u1wml1z7s1z7a1wnl1u2g
+ jetty.keystore = etc/keystore
+ jetty.keystore.password = OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
+ jetty.http.port = 8080
+ jetty.secure.port = 8443
+ jetty.truststore = etc/keystore
+ jetty.truststore.password = OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
+ threads.max = 200
+ threads.min = 10
+ threads.timeout = 60000
+
+Jetty Server Classpath:
+-----------------------
+Version Information on 11 entries in the classpath.
+: order presented here is how they would appear on the classpath.
+      changes to the --module=name command line options will be reflected here.
+ 0:                    3.1.0 | ${jetty.home}/lib/servlet-api-3.1.jar
+ 1:                  3.1.RC0 | ${jetty.home}/lib/jetty-schemas-3.1.jar
+ 2:                {VERSION} | ${jetty.home}/lib/jetty-http-{VERSION}.jar
+ 3:                {VERSION} | ${jetty.home}/lib/jetty-continuation-{VERSION}.jar
+ 4:                {VERSION} | ${jetty.home}/lib/jetty-server-{VERSION}.jar
+ 5:                {VERSION} | ${jetty.home}/lib/jetty-xml-{VERSION}.jar
+ 6:                {VERSION} | ${jetty.home}/lib/jetty-util-{VERSION}.jar
+ 7:                {VERSION} | ${jetty.home}/lib/jetty-io-{VERSION}.jar
+ 8:                {VERSION} | ${jetty.home}/lib/jetty-servlet-{VERSION}.jar
+ 9:                {VERSION} | ${jetty.home}/lib/jetty-webapp-{VERSION}.jar
+10:                {VERSION} | ${jetty.home}/lib/jetty-deploy-{VERSION}.jar
+
+Jetty Active XMLs:
+------------------
+ ${jetty.home}/etc/jetty.xml
+ ${jetty.home}/etc/jetty-http.xml
+ ${jetty.home}/etc/jetty-ssl.xml
+ ${jetty.home}/etc/jetty-deploy.xml
+....
+
+Now start Jetty.
+
+[source, screen, subs="{sub-order}"]
+....
+[my-base]$ java -jar /home/user/jetty-distribution-{VERSION}/start.jar
+2013-10-08 07:06:55.837:INFO:oejs.Server:main: jetty-{VERSION}
+2013-10-08 07:06:55.853:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:/home/user/my-base/webapps/] at interval 1
+2013-10-08 07:06:55.872:INFO:oejs.ServerConnector:main: Started ServerConnector@72974691{HTTP/1.1}{0.0.0.0:8080}
+....
+
+[[reviewing-ssl-config]]
+==== Reviewing the Configuration
+
+The following sections review this configuration.
+
+[[jetty-base-jetty-home]]
+===== $\{jetty.base} and $\{jetty.home}
+
+First notice the separation of `${jetty.base}` and `${jetty.home}`.
+
+* `${jetty.home}` is where your distribution lies, unchanged, unedited.
+* `${jetty.base}` is where your customizations are.
+
+[[modules]]
+===== Modules
+
+Notice that you have `--module=<name>` here and there; you have wrapped up the goal of a module (libs, configuration XMLs, and properties) into a single unit, with dependencies on other modules.
+
+You can see the list of modules:
+
+[source, screen, subs="{sub-order}"]
+....
+[my-base]$ java -jar /home/user/jetty-distribution-{VERSION}/start.jar --list-modules
+
+Jetty All Available Modules:
+----------------------------
+
+Module: annotations
+      LIB: lib/jetty-annotations-${jetty.version}.jar
+      LIB: lib/annotations/*.jar
+      XML: etc/jetty-annotations.xml
+  depends: [plus]
+
+Module: client
+      LIB: lib/jetty-client-${jetty.version}.jar
+  depends: []
+
+Module: debug
+      XML: etc/jetty-debug.xml
+  depends: [server]
+
+Module: deploy
+      LIB: lib/jetty-deploy-${jetty.version}.jar
+      XML: etc/jetty-deploy.xml
+  depends: [webapp]
+  enabled: ${jetty.base}/start.ini
+
+Module: ext
+      LIB: lib/ext/*.jar
+  depends: []
+
+Module: http
+      XML: etc/jetty-http.xml
+  depends: [server]
+  enabled: ${jetty.base}/start.ini
+
+Module: http2
+      LIB: lib/http2/*.jar
+      XML: etc/jetty-http2.xml
+  depends: [ssl, alpn]
+
+Module: http2c
+     LIB: lib/http2/*.jar
+     XML: etc/jetty-http2c.xml
+ depends: [http]
+
+Module: https
+      XML: etc/jetty-https.xml
+  depends: [ssl]
+
+Module: ipaccess
+      XML: etc/jetty-ipaccess.xml
+  depends: [server]
+
+Module: jaas
+      LIB: lib/jetty-jaas-${jetty.version}.jar
+      XML: etc/jetty-jaas.xml
+  depends: [server]
+
+Module: jaspi
+      LIB: lib/jetty-jaspi-${jetty.version}.jar
+      LIB: lib/jaspi/*.jar
+  depends: [security]
+
+Module: jmx
+      LIB: lib/jetty-jmx-${jetty.version}.jar
+      XML: etc/jetty-jmx.xml
+  depends: []
+
+Module: jndi
+      LIB: lib/jetty-jndi-${jetty.version}.jar
+      LIB: lib/jndi/*.jar
+  depends: [server]
+
+Module: jsp
+      LIB: lib/jsp/*.jar
+  depends: [servlet]
+
+Module: jvm
+  depends: []
+
+Module: logging
+      XML: etc/jetty-logging.xml
+  depends: []
+
+Module: lowresources
+      XML: etc/jetty-lowresources.xml
+  depends: [server]
+
+Module: monitor
+      LIB: lib/jetty-monitor-${jetty.version}.jar
+      XML: etc/jetty-monitor.xml
+  depends: [client, server]
+
+Module: npn
+  depends: []
+
+Module: plus
+      LIB: lib/jetty-plus-${jetty.version}.jar
+      XML: etc/jetty-plus.xml
+  depends: [server, security, jndi]
+
+Module: proxy
+      LIB: lib/jetty-proxy-${jetty.version}.jar
+      XML: etc/jetty-proxy.xml
+  depends: [client, server]
+
+Module: requestlog
+      XML: etc/jetty-requestlog.xml
+  depends: [server]
+
+Module: resources
+      LIB: resources
+  depends: []
+
+Module: rewrite
+      LIB: lib/jetty-rewrite-${jetty.version}.jar
+      XML: etc/jetty-rewrite.xml
+  depends: [server]
+
+Module: security
+      LIB: lib/jetty-security-${jetty.version}.jar
+  depends: [server]
+
+Module: server
+      LIB: lib/servlet-api-3.1.jar
+      LIB: lib/jetty-schemas-3.1.jar
+      LIB: lib/jetty-http-${jetty.version}.jar
+      LIB: lib/jetty-continuation-${jetty.version}.jar
+      LIB: lib/jetty-server-${jetty.version}.jar
+      LIB: lib/jetty-xml-${jetty.version}.jar
+      LIB: lib/jetty-util-${jetty.version}.jar
+      LIB: lib/jetty-io-${jetty.version}.jar
+      XML: etc/jetty.xml
+  depends: []
+  enabled: ${jetty.base}/start.ini
+
+Module: servlet
+      LIB: lib/jetty-servlet-${jetty.version}.jar
+  depends: [server]
+
+Module: servlets
+      LIB: lib/jetty-servlets-${jetty.version}.jar
+  depends: [servlet]
+
+Module: setuid
+      LIB: lib/setuid/jetty-setuid-java-1.0.1.jar
+      XML: etc/jetty-setuid.xml
+  depends: [server]
+
+Module: ssl
+      XML: etc/jetty-ssl.xml
+  depends: [server]
+  enabled: ${jetty.base}/start.ini
+
+Module: stats
+      XML: etc/jetty-stats.xml
+  depends: [server]
+
+Module: webapp
+      LIB: lib/jetty-webapp-${jetty.version}.jar
+  depends: [servlet]
+
+Module: websocket
+      LIB: lib/websocket/*.jar
+  depends: [annotations]
+
+Module: xinetd
+      XML: etc/jetty-xinetd.xml
+  depends: [server]
+
+Jetty Active Module Tree:
+-------------------------
+ + Module: server [enabled]
+   + Module: http [enabled]
+   + Module: servlet [transitive]
+   + Module: ssl [enabled]
+     + Module: webapp [transitive]
+       + Module: deploy [enabled]
+....
+
+These are the modules by name, the libraries they bring in, the XML configurations they use, the other modules they depend on (even optional ones), and if the module is in use, where it was enabled.
+
+While you can manage the list of active modules yourself, it is much easier to edit the `${jetty.base}/start.ini`.
+
+If you want to start using a new module:
+
+[source, screen, subs="{sub-order}"]
+....
+[my-base] $ java -jar ../jetty-distribution-{VERSION}/start.jar --add-to-start=https
+....
+
+This adds the `--module=` lines and associated properties (the parameterized values mentioned above), to your `start.ini`.
+
+____
+[IMPORTANT]
+Do not edit the modules and XML files in the `${jetty.home}` directory; there is no need to be moving or copying them unless you want to make your own modules or override the behavior of an existing module.
+____
+
+Notice that your `${jetty.base}/start.ini` has no references to the XML files.
+That's because the module system and its graph of dependencies now dictate all of the XML files, and their load order.
+
+[[parameterizing]]
+===== Parameters
+
+Next is parameterizing all of the standard configuration XMLs.
+In this example all of the SSL parameters are now just properties in the `start.ini`, reducing or eliminating the need to edit XML files.
+
+[[override-jetty.home]]
+===== Overriding $\{jetty.home} in $\{jetty.base}
+
+Finally, you can override anything you see in `${jetty.home}` in `${jetty.base}`, even XML configurations and libraries.
+
+For more information on the `start.jar` in 9.1, see xref:start-jar[].
+
+[[summary-configuring-SSL-Jetty]]
+==== Summary of Configuring SSL
+
+1.  Download and unpack Jetty into `/home/user/jetty-distribution-{VERSION}`.
+2.  Go to your base directory and just use the distribution, no editing.
++
+[source, screen, subs="{sub-order}"]
+....
+[my-base]$ java -jar /home/user/jetty-distribution-{VERSION}/start.jar
+....
+* The Jetty distribution provides, out of the box, the XML configuration files, in this case `jetty-http.xml` and `jetty-ssl.xml`.
+These can be found in the `${jetty.home}/etc/` directory.
+* We have parameterized all of the configurable values in those XMLs.
+You can now set the values using simple properties, either on the command line, or within the `${jetty.base}/start.ini`.
+* When you activate the module for HTTP or HTTPs, Jetty automatically adds the appropriate libraries and XML to start Jetty.
+Unless you have a highly custom setup (such as listening on two different ports, using SSL on each, each with its own keystore and configuration), there is no need to muck around in XML files.
+3.  Use modules to configure HTTPS:
+* http -> server
+* https -> ssl -> server
++
+You can find the details about the modules in `${jetty.home}/modules/`.
+For SSL they include `modules/http.mod`, `modules/https.mod`, `modules/ssl.mod`, and `modules/server.mod`.
++
+Ideally, this level of detail is not important to you.
+What is important is that you want to use HTTPS and want to configure it.
+You accomplish that by adding the `--module=https` to your `start.ini`.
+By default, the module system keeps things sane, and transitively includes all dependent modules as well.
+
+You can see what the configuration looks like, after all of the modules are resolved, without starting Jetty via:
+
+[source, screen, subs="{sub-order}"]
+....
+[my-base] $ java -jar ../jetty-distribution-{VERSION}/start.jar --list-config
+....
+
+Just because the JARs exist on disk does not mean that they are in use.
+The configuration controls what is used.
+
+Use the `--list-config` to see the configuration.
+Notice that only a subset of the JARs from the distribution are in use.
+The modules you have enabled determine that subset.
+
+[source, screen, subs="{sub-order}"]
+....
+[my-base]$ java -jar ~/jetty-distribution-{VERSION}/start.jar --list-config
+....
diff --git a/jetty-documentation/src/main/asciidoc/configuring/security/secure-passwords.adoc b/jetty-documentation/src/main/asciidoc/configuring/security/secure-passwords.adoc
new file mode 100644
index 0000000..b4c341c
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/security/secure-passwords.adoc
@@ -0,0 +1,104 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[configuring-security-secure-passwords]]
+=== Secure Password Obfuscation
+
+There are many places where you might want to use and store a password, for example for the SSL connectors and user passwords in realms.
+
+Passwords can be stored in clear text, obfuscated, checksummed or encrypted in order of increasing security.
+The choice of method to secure a password depends on where you are using the password.
+In some cases such as keystore passwords and digest authentication, the system must retrieve the original password, which requires the obfuscation method.
+The drawback of the obfuscation algorithm is that it protects passwords from casual viewing only.
+
+When the stored password is compared to one a user enters, the handling code can apply the same algorithm that secures the stored password to the user input and compare results, making password authentication more secure.
+
+The class `org.eclipse.jetty.util.security.Password` can be used to generate all varieties of passwords.
+
+Run it without arguments to see usage instructions:
+
+[source, screen, subs="{sub-order}"]
+....
+
+$ export JETTY_VERSION=9.0.0-SNAPSHOT
+$ java -cp lib/jetty-util-$JETTY_VERSION.jar org.eclipse.jetty.util.security.Password
+
+Usage - java org.eclipse.jetty.util.security.Password [<user>] <password>
+If the password is ?, the user will be prompted for the password
+
+....
+
+For example, to generate a secured version of the password "blah" for the user "me":
+
+[source, screen, subs="{sub-order}"]
+....
+
+$ export JETTY_VERSION=9.0.0.RC0
+$ java -cp lib/jetty-util-$JETTY_VERSION.jar org.eclipse.jetty.util.security.Password me blah
+blah
+OBF:20771x1b206z
+MD5:639bae9ac6b3e1a84cebb7b403297b79
+CRYPT:me/ks90E221EY
+
+....
+
+You can now cut and paste whichever secure version you choose into your configuration file or Java code.
+
+For example, the last line below shows how you would implement the encrypted password generated above into the properties file for a `LoginService`:
+
+[source,bash]
+----
+
+admin: CRYPT:ad1ks..kc.1Ug,server-administrator,content-administrator,admin
+other: OBF:1xmk1w261u9r1w1c1xmq
+guest: guest,read-only
+me:CRYPT:me/ks90E221EY
+
+----
+
+____
+[TIP]
+Don't forget to also copy the OBF:, MD5: or CRYPT: prefix on the generated password. It will not be usable by Jetty without it.
+____
+
+You can also use obfuscated passwords in jetty xml files where a plain text password is usually needed.
+Here's an example setting the password for a JDBC Datasource with obfuscation:
+
+[source, xml, subs="{sub-order}"]
+----
+
+  <New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg></Arg>
+     <Arg>jdbc/DSTest</Arg>
+     <Arg>
+       <New class="com.jolbox.bonecp.BoneCPDataSource">
+         <Set name="driverClass">com.mysql.jdbc.Driver</Set>
+         <Set name="jdbcUrl">jdbc:mysql://localhost:3306/foo</Set>
+         <Set name="username">dbuser</Set>
+         <Set name="password">
+            <Call class="org.eclipse.jetty.util.security.Password" name="deobfuscate">
+                  <Arg>OBF:1ri71v1r1v2n1ri71shq1ri71shs1ri71v1r1v2n1ri7</Arg>
+            </Call>
+         </Set>
+         <Set name="minConnectionsPerPartition">5</Set>
+         <Set name="maxConnectionsPerPartition">50</Set>
+         <Set name="acquireIncrement">5</Set>
+         <Set name="idleConnectionTestPeriod">30</Set>
+      </New>
+    </Arg>
+  </New>
+
+----
diff --git a/jetty-documentation/src/main/asciidoc/configuring/security/serving-aliased-files.adoc b/jetty-documentation/src/main/asciidoc/configuring/security/serving-aliased-files.adoc
new file mode 100644
index 0000000..3503047
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/security/serving-aliased-files.adoc
@@ -0,0 +1,83 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[serving-aliased-files]]
+=== Aliased Files and Symbolic links
+
+Web applications will often server static content from the file system provided by the operating system running underneath the JVM.
+However because file systems often implement multiple aliased names for the same file, then security constraints and other servlet URI space mappings my inadvertently be bypassed by aliases.
+
+A key example of this is case insensitivity and 8.3 filenames implemented by the Windows file system.
+If a file within a web application called `/mysecretfile.txt` is protected by a security constraint on the URI `/mysecretfile.txt`, then a request to `/MySecretFile.TXT` will not match the URI constraint because URIs are case sensitive, but the Windows file system will report that a file does exist at that name and it will be served despite the security constraint.
+Less well known than case insensitivity is that Windows files systems also support http://en.wikipedia.org/wiki/8.3_filename[8.3 filenames] for compatibility with legacy programs.
+Thus a request to a URI like `/MYSECR~1.TXT` will again not match the security constraint, but will be reported as an existing file by the file system and served.
+
+There are many examples of aliases, not just on Windows:
+
+* NTFS Alternate stream names like `c:\test\file.txt::$DATA:name`
+* OpenVMS support file versionig so that `/mysecret.txt;N` refers to version N of `/mysecret.txt` and is essentially an alias.
+* The clearcase software configuration management system provides a file system where `@@` in a file name is an alias to a specific version.
+* The Unix files system supports `/./foo.txt` as and alias for `/foo.txt`
+* Many JVM implementations incorrectly assume the null character is a string terminator, so that a file name resulting from `/foobar.txt%00` is an alias for `/foobar.txt`
+* Unix symbolic links and hard links are a form of aliases that allow the same file or directory to have multiple names.
+
+In addition, it is not just URI security constraints that can be bypassed. For example the mapping of the URI pattern `*.jsp` to the JSP
+Servlet may be bypassed by an a request to an alias like `/foobar.jsp%00`, thus rather than execute the JSP, the source code of the JSP is returned by the file system.
+
+==== Good Security Practise
+
+Part of the problem with aliases is that the standard web application security model is to allow all requests except the ones that are specifically denied by security constraints.
+A best practice for security is to deny all requests and to permit only those that are specifically identified as allowable.
+While it is possible to design web application security constraints in this style, it can be difficult in all circumstances and it is not the default. T
+hus it is important for Jetty to be able to detect and deny requests to aliased static content.
+
+[[file-alias-detection]]
+==== Alias detection
+
+It is impossible for Jetty to know of all the aliases that may be implemented by the file system running beneath it, thus it does not attempt to make any specific checks for any know aliases.
+Instead Jetty detects aliases by using the canonical path of a file.
+If a file resource handled by jetty has a canonical name that differs from the name used to request the resource, then Jetty determines that the resource is an aliased request and it will not be returned by the `ServletContext.getResource(String)` method (or similar) and thus will not be served as static content nor used as the basis of a JSP.
+
+This if Jetty is running on a Windows operating system, then a file called `/MySecret.TXT` will have a canonical name that exactly matches that case.
+So while a request to `/mysecret.txt` or `/MYSECR~1.TXT` will result in a File Resource that matches the file, the different canonical name will indicate that those requests are aliases and they will not be served as static content and instead a 404 response returned.
+
+Unfortunately this approach denies all aliases, including symbolic links, which can be useful in assembling complex web applications.
+
+[[file-alias-serving]]
+==== Serving Aliases and Symbolic Links
+
+Not all aliases are bad nor should be seen as attempts to subvert security constraints.
+Specifically symbolic links can be very useful when assembling complex web applications, yet by default Jetty will not serve them.
+Thus Jetty contexts support an extensible `AliasCheck` mechanism to allow aliases resources to be inspected an conditionally served.
+In this way, "good" aliases can be detected and served.
+Jetty provides several utility implementations of the `AliasCheck` interface as nested classes with `ContextHandler`:
+
+ApproveAliases::
+  Approve all aliases (*Use with caution!*).
+AllowSymLinkAliasChecker::
+  Approve Aliases using the java-7 `Files.readSymbolicLink(path)` and `Path.toRealPath(...)` APIs to check that aliases are valid symbolic links.
+
+An application is free to implement its own Alias checking.
+Alias Checkers can be installed in a context via the following XML used in a context deployer file or `WEB-INF/jetty-web.xml`:
+
+[source, xml, subs="{sub-order}"]
+----
+  <!-- Allow symbolic links  -->
+  <Call name="addAliasCheck">
+    <Arg><New class="org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker"/></Arg>
+  </Call>
+
+----
diff --git a/jetty-documentation/src/main/asciidoc/configuring/security/spnego-support.adoc b/jetty-documentation/src/main/asciidoc/configuring/security/spnego-support.adoc
new file mode 100644
index 0000000..73f8781
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/configuring/security/spnego-support.adoc
@@ -0,0 +1,171 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[spnego-support]]
+=== Spnego Support
+
+Simple and Protected GSSAPI Negotiation Mechanism (Spnego) is a way for users to be seamlessly authenticated when running on a Windows or Active Directory based network.
+Jetty supports this type of authentication and authorization through the JDK (which has been enabled since the later versions of Java 6 and 7).
+Also important to note is that this is an _incredibly_ fragile setup where everything needs to be configured just right for things to work, otherwise it can fail in fun and exciting, not to mention obscure, ways.
+
+There is a substantial amount of configuration and testing required to enable this feature as well as knowledge and access to central systems on a Windows network such as the Active Domain Controller and the ability to create and maintain service users.
+
+==== Configuring Jetty and Spnego
+
+To run with Spengo enabled the following command line options are required:
+
+[source,screen, subs="{sub-order}"]
+----
+-Djava.security.krb5.conf=/path/to/jetty/etc/krb5.ini \
+-Djava.security.auth.login.config=/path/to/jetty/etc/spnego.conf \
+-Djavax.security.auth.useSubjectCredsOnly=false
+----
+
+For debugging the Spengo authentication the following options are very helpful:
+
+[source,screen, subs="{sub-order}"]
+----
+-Dorg.eclipse.jetty.LEVEL=debug \
+-Dsun.security.spnego.debug=all
+----
+
+Spengo Authentication must be enabled in the webapp in the following way.
+The name of the role will be different for your network.
+
+[source, xml, subs="{sub-order}"]
+----
+
+ <security-constraint>
+   <web-resource-collection>
+     <web-resource-name>Secure Area</web-resource-name>
+     <url-pattern>/secure/me/*</url-pattern>
+   </web-resource-collection>
+   <auth-constraint>
+     <!-- this is the domain that the user is a member of -->
+     <role-name>MORTBAY.ORG</role-name>
+   </auth-constraint>
+ </security-constraint>
+ <login-config>
+   <auth-method>SPNEGO</auth-method>
+   <realm-name>Test Realm</realm-name>
+   <!-- optionally to add custom error page -->
+   <spnego-login-config>
+     <spengo-error-page>/loginError.html?param=foo</spnego-error-page>
+   </spnego-login-config>
+ </login-config>
+
+----
+
+A corresponding `UserRealm` needs to be created either programmatically if embedded, via the `jetty.xml` or in a context file for the webapp.
+
+This is what the configuration within a Jetty xml file would look like.
+
+[source, xml, subs="{sub-order}"]
+----
+
+  <Call name="addBean">
+     <Arg>
+       <New class="org.eclipse.jetty.security.SpnegoLoginService">
+         <Set name="name">Test Realm</Set>
+         <Set name="config"><Property name="jetty.home" default="."/>/etc/spnego.properties</Set>
+       </New>
+     </Arg>
+   </Call>
+
+----
+
+This is what the configuration within a context xml file would look like.
+
+[source, xml, subs="{sub-order}"]
+----
+
+ <Get name="securityHandler">
+   <Set name="loginService">
+     <New class="org.eclipse.jetty.security.SpnegoLoginService">
+       <Set name="name">Test Realm</Set>
+       <Set name="config">
+        <SystemProperty name="jetty.home" default="."/>/etc/spnego.properties
+      </Set>
+     </New>
+   </Set>
+   <Set name="checkWelcomeFiles">true</Set>
+ </Get>
+
+
+----
+
+There are a number of important configuration files with S3pnego that are required. The default values for these configuration files from this
+test example are found in the `/etc` folder of the Jetty distribution.
+
+spnego.properties::
+  configures the user realm with runtime properties
+krb5.ini::
+  configures the underlying kerberos setup
+spnego.conf::
+  configures the glue between gssapi and kerberos
+
+It is important to note that the keytab file referenced in the `krb5.ini` and the `spengo.conf` files needs to contain the keytab for the `targetName` for the http server.
+To do this use a process similar to this:
+
+On the Windows Active Domain Controller run:
+
+[source, screen, subs="{sub-order}"]
+----
+$ setspn -A HTTP/linux.mortbay.org ADUser
+----
+
+To create the keytab file use the following process:
+
+[source, screen, subs="{sub-order}"]
+----
+$ ktpass -out c:\dir\krb5.keytab -princ HTTP/linux.mortbay.org@MORTBAY.ORG -mapUser ADUser -mapOp set -pass ADUserPWD -crypto RC4-HMAC-NT -pType KRB5_NT_PRINCIPAL
+----
+
+This step will give you the keytab file which should then be copied to the machine running the http server and referenced from the configuration files.
+For our testing we put the keytab into the `/etc` directory of Jetty and referenced it from there.
+
+==== Configuring Firefox
+
+The follows steps have been required to inform Firefox that it should use a negotiation dialog to authenticate.
+
+1.  Browse to about:config and agree to the warnings
+2.  Search through to find the 'network' settings
+3.  Set `network.negotiate-auth.delegation-uris` to http://,https://
+4.  Set `network.negotiate-auth.trusted-uris` to http://,https://
+
+==== Configuring Internet Explorer
+
+The follows steps have been required to inform Internet Explorer that it should use a negotiation dialog to authenticate.
+
+1.  Tools -> Options -> Security -> Local Intranet -> Sites (everything should be checked here)
+2.  Tools -> Options -> Security -> Local Intranet -> Sites -> Advanced (add url to server (http:// and/or https:// use the hostname!)
+3.  Tools -> Options -> Security -> Local Intranet -> Sites -> Advanced -> Close
+4.  Tools -> Options -> Security -> Local Intranet -> Sites -> Ok
+5.  Tools -> Options -> Advanced -> Security (in the checkbox list)
+6.  Locate and check 'Enable Integrated Windows Authentication'
+7.  Tools -> Options -> Advanced -> Security -> Ok
+8.  Close IE then reopen and browse to your Spengo protected resource
+
+____
+[NOTE]
+You must go to the hostname and not the IP.
+If you go to the IP it will default to NTLM authentication...the following conditions must be true for Spnego authentication to work:
+* You must be within the Intranet Zone of the network
+* Accessing the server using a Hostname rather than IP
+* Integrated Windows Authentication in IE is enabled and the host is trusted in Firefox
+* The server is not local to the browser, it can't be running on localhost
+* The client's Kerberos system is authenticated to a domain controller
+____
diff --git a/jetty-documentation/src/main/asciidoc/development/ant/chapter.adoc b/jetty-documentation/src/main/asciidoc/development/ant/chapter.adoc
new file mode 100644
index 0000000..5526124
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/ant/chapter.adoc
@@ -0,0 +1,22 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[ant-and-jetty]]
+== Ant and Jetty
+
+This chapter explains how to use Jetty with Ant and the Jetty Ant tasks.
+
+include::jetty-ant.adoc[]
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/development/ant/jetty-ant.adoc b/jetty-documentation/src/main/asciidoc/development/ant/jetty-ant.adoc
new file mode 100644
index 0000000..b4663c3
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/ant/jetty-ant.adoc
@@ -0,0 +1,620 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-ant]]
+=== Ant Jetty Plugin
+
+The Ant Jetty plugin is a part of Jetty 9 under the `jetty-ant` module.
+This plugin makes it possible to start a Jetty web server directly from the Ant build script, and to embed the Jetty web server inside your build process.
+Its purpose is to provide almost the same functionality as the Jetty plugin for Maven: dynamic application reloading, working directly on web application sources, and tightly integrating with the build system.
+
+[source, xml, subs="{sub-order}"]
+----
+<dependency>
+   <groupId>org.eclipse.jetty</groupId>
+   <artifactId>jetty-ant</artifactId>
+ </dependency>
+    
+----
+
+[[jetty-ant-preparation]]
+==== Preparing Your Project
+
+To set up your project for Ant to run Jetty, you need a Jetty distribution and the jetty-ant Jar:
+
+1.  http://download.eclipse.org/jetty/[Download] a Jetty distribution and unpack it in the local filesystem.
+2.  http://central.maven.org/maven2/org/eclipse/jetty/jetty-ant/[Get] the jetty-ant Jar.
+3.  Make a directory in your project called `jetty-lib/`.
+4.  Copy all of the Jars in your Jetty distribution's `lib` directory, and all its subdirectories, into your new `jetty-lib` dir.
+When copying the Jars, _don't_ preserve the Jetty distribution's lib dir hierarchy – all the jars should be directly inside your ` jetty-lib` dir.
+5.  Also copy the jetty-ant Jar you downloaded earlier into the `jetty-lib` dir.
+6.  Make a directory in your project called `jetty-temp`.
+
+Now you're ready to edit or create your Ant `build.xml` file.
+
+==== Preparing the `build.xml` file
+
+Begin with an empty `build.xml`:
+
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+</project>  
+      
+----
+
+Add a `<taskdef>` that imports all available Jetty tasks:
+
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+     <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+  <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+</project> 
+      
+----
+
+Now you are ready to add a new target for running Jetty:
+
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+  <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run />
+  </target>
+
+</project>
+      
+----
+
+This is the minimal configuration you need. You can now start Jetty on the default port of 8080.
+
+==== Starting Jetty via Ant
+
+At the command line enter:
+
+[source, screen, subs="{sub-order}"]
+....
+> ant jetty.run
+....
+
+==== Configuring the Jetty Container
+
+A number of configuration options can help you set up the Jetty environment so that your web application has all the resources it needs:
+
+ports and connectors:::
+  To configure the port that Jetty starts on you need to define a connector.
+  First you need to configure a `<typedef>` for the Connector class and then define the connector in the Jetty tags:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+  <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+  <typedef name="connector" classname="org.eclipse.jetty.ant.types.Connector"
+           classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run>
+      <connectors>
+        <connector port="8090"/>
+      </connectors>
+    </jetty.run>
+  </target>
+
+</project>
+            
+----
++
+____
+[TIP]
+You can set the port to 0, which starts the Jetty server connector on an arbitrary available port.
+You can then access these values from system properties `jetty.ant.server.port` and `jetty.ant.server.host`.
+____
+
+login services:::
+  If your web application requires authentication and authorization services, you can configure these on the Jetty container.
+  Here's an example of how to set up an link:{JDURL}/org/eclipse/jetty/security/HashLoginService.html[org.eclipse.jetty.security.HashLoginService]:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+ <typedef name="hashLoginService" classname="org.eclipse.jetty.security.HashLoginService"
+          classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run>
+      <loginServices>
+        <hashLoginService name="Test Realm" config="${basedir}/realm.properties"/>
+      </loginServices>
+    </jetty.run>
+  </target>
+
+</project>
+            
+----
+request log:::
+  The `requestLog` option allows you to specify a request logger for the Jetty instance.
+  You can either use the link:{JDURL}/org/eclipse/jetty/server/NCSARequestLog.html[org.eclipse.jetty.server.NCSARequestLog] class, or supply the name of your custom class:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run requestLog="com.acme.MyFancyRequestLog">
+    </jetty.run>
+  </target>
+
+</project>
+            
+----
+temporary directory:::
+  You can configure a directory as a temporary file store for uses such as expanding files and compiling JSPs by supplying the `tempDirectory` option:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run tempDirectory="${basedir}/jetty-temp">
+    </jetty.run>
+  </target>
+
+</project>
+            
+----
+other context handlers:::
+  You may need to configure some other context handlers to run at the same time as your web application.
+  You can specify these other context handlers using the `<contextHandlers>` element.
+  You need to supply a `<typedef>` for it before you can use it:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath"
+          resource="tasks.properties" loaderref="jetty.loader" />
+
+ <typedef name="contextHandlers" classname="org.eclipse.jetty.ant.types.ContextHandlers"
+          classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run>
+     <contextHandlers>
+       <contextHandler resourceBase="${basedir}/stuff" contextPath="/stuff"/>
+     </contextHandlers>
+    </jetty.run>
+  </target>
+
+</project>
+            
+----
+system properties:::
+  As a convenience, you can configure system properties by using the `<systemProperties>` element.
+  Be aware that, depending on the purpose of the system property, setting it from within the Ant execution may mean that it is evaluated too late, as the JVM evaluates some system properties on entry.
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run>
+      <systemProperties>
+        <systemProperty name="foo" value="bar"/>
+      </systemProperties>
+    </jetty.run>
+  </target>
+
+</project>
+            
+----
+jetty XML file:::
+  If you have a lot of configuration to apply to the Jetty container, it can be more convenient to put it into a standard Jetty XML configuration file and have the Ant plugin apply it before starting Jetty:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run jettyXml="${basedir}/jetty.xml">
+    </jetty.run>
+  </target>
+
+</project>
+            
+----
+scanning for changes:::
+  The most useful mode in which to run the Ant plugin is for it to continue to execute Jetty and automatically restart your web application if any part of it changes (for example, your IDE
+  recompiles the classes of the web application).
+  The `scanIntervalSeconds` option controls how frequently the `<jetty.run>` task scans your web application/WAR file for changes.
+  The default value of `0` disables scanning. Here's an example where Jetty checks for changes every five seconds:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run scanIntervalSeconds="5">
+    </jetty.run>
+  </target>
+
+</project>
+            
+----
+stopping:::
+  In normal mode (`daemon="false"`), the `<jetty.run>` task runs until you `cntrl-c` it. It may be useful to script both the stop AND the start of Jetty.
+  For such a case, we provide the `<jetty.stop>` task.
+  +
+  To use it, you need to provide a port and an identifying string to both the ` <jetty.run>` and the `<jetty.stop>` tasks, where `<jetty.run>` listens on the given port for a stop message containing the given string, and cleanly stops Jetty when it is received.
+  The `<jetty.stop>` task sends this stop message.
+  You can also optionally provide a `stopWait` value (in seconds), which is the length of time the `<jetty.stop>` task waits for confirmation that the stop succeeded:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run stopPort="9999" stopKey="9999">
+    </jetty.run>
+  </target>
+
+  <target name="jetty.stop">
+   <jetty.stop stopPort="9999" stopKey="9999" stopWait="10"/>
+  </target>
+
+</project>
+            
+----
++
+To stop jetty via Ant, enter:
++
+[source, screen, subs="{sub-order}"]
+....
+> ant jetty.stop
+....
+
+
+execution without pausing ant:::
+  Usually, the `<jetty.run>` task runs until you `cntrl-c` it, pausing the execution of Ant as it does so. In some cases, it may be useful to let Ant continue executing.
+  For example, to run your unit tests you may need other tasks to execute while Jetty is running.
+  For this case, we provide the `daemon` option.
+  This defaults to `false`. For `true`, Ant continues to execute after starting Jetty.
+  If Ant exits, so does Jetty. Understand that this option does _not_ fork a new process for Jetty.
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run daemon="true">
+    </jetty.run>
+  </target>
+
+</project>
+            
+----
+
+==== Deploying a Web Application
+
+Add a `<typedef>` for the `org.eclipse.jetty.ant.AntWebAppContext` class with name __webApp__, then add a `<webApp>` element to `<jetty.run>` to describe your web application.
+The following example deploys a web application that is expanded in the local directory `foo/` to context path ` / `:
+
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+ <typedef name="webApp" classname="org.eclipse.jetty.ant.AntWebAppContext"
+          classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run>
+      <webApp war="${basedir}/foo" contextPath="/"/>
+    </jetty.run>
+  </target>
+
+</project>
+      
+----
+
+deploying a WAR file:::
+  It is not necessary to expand the web application into a directory.
+  It is fine to deploy it as a WAR file:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+ <typedef name="webApp" classname="org.eclipse.jetty.ant.AntWebAppContext"
+          classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run>
+      <webApp war="${basedir}/foo.war" contextPath="/"/>
+    </jetty.run>
+  </target>
+
+</project>
+            
+----
+
+deploying more than one web application:::
+  You can also deploy more than one web application:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+ <typedef name="webApp" classname="org.eclipse.jetty.ant.AntWebAppContext"
+          classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run>
+      <webApp war="${basedir}/foo.war" contextPath="/"/>
+      <webApp war="${basedir}/other    contextPath="/other"/>
+      <webApp war="${basedir}/bar.war" contextPath="/bar"/>
+    </jetty.run>
+  </target>
+
+</project>
+            
+----
+
+===== Configuring the Web Application
+
+As the `org.eclipse.jetty.ant.AntWebAppContext` class is an extension of
+the
+link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html[`org.eclipse.jetty.webapp.WebAppContext`]
+class, you can configure it by adding attributes of the same name
+(without the `set` or `add` prefix) as the setter methods.
+
+Here's an example that specifies the location of the `web.xml` file (equivalent to method link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setDescriptor%28java.lang.String%29[`AntWebAppContext.setDescriptor()`]) and the web application's temporary directory (equivalent to method link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setTempDirectory%28java.io.File%29[`AntWebAppContext.setTempDirectory()`]):
+
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+ <typedef name="webApp" classname="org.eclipse.jetty.ant.AntWebAppContext"
+          classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run>
+      <webApp descriptor="${basedir}/web.xml" tempDirectory="${basedir}/my-temp" war="${basedir}/foo" contextPath="/"/>
+    </jetty.run>
+  </target>
+
+</project>
+        
+----
+
+Other extra configuration options for the AntWebAppContext include:
+
+extra classes and Jars:::
+  If your web application's classes and Jars do not reside inside `WEB-INF` of the resource base directory, you can use the <classes> and <jar> elements to tell Ant where to find them. Here's an example:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+ <typedef name="webApp" classname="org.eclipse.jetty.ant.AntWebAppContext"
+          classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run>
+      <webApp descriptor="${basedir}/web.xml" tempDirectory="${basedir}/my-temp" war="${basedir}/foo" contextPath="/">
+        <classes dir="${basedir}/classes">
+          <include name="**/*.class"/>
+          <include name="**/*.properties"/>
+        </classes>
+        <lib dir="${basedir}/jars">
+          <include name="**/*.jar"/>
+          <exclude name="**/*.dll"/>
+        </lib>
+      </webApp>
+    </jetty.run>
+  </target>
+
+</project>
+              
+----
+context attributes:::
+  Jetty allows you to set up ServletContext attributes on your web application.
+  You configure them in a context XML file that is applied to your WebAppContext instance prior to starting it.
+  For convenience, the Ant plugin permits you to configure these directly in the build file.
+  Here's an example:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+ <typedef name="webApp" classname="org.eclipse.jetty.ant.AntWebAppContext"
+          classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run>
+      <webApp war="${basedir}/foo" contextPath="/">
+        <attributes>
+          <attribute name="my.param" value="123"/>
+        </attributes>
+      </webApp>
+    </jetty.run>
+  </target>
+
+</project>
+              
+----
+`jetty-env.xml` file:::
+  If you are using features such as link:#configuring_jndi[JNDI] with your web application, you may need to configure a link:#using_jndi[`WEB-INF/jetty-env.xml`] file to define resources. If the structure of your web application project is such that the source of `jetty-env.xml` file resides somewhere other than `WEB-INF`, you can use the `jettyEnvXml` attribute to tell Ant where to find it:
++
+[source, xml, subs="{sub-order}"]
+----
+<project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+ <typedef name="webApp" classname="org.eclipse.jetty.ant.AntWebAppContext"
+          classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run>
+      <webApp war="${basedir}/foo" contextPath="/" jettyEnvXml="${basedir}/jetty-env.xml">
+        <attributes>
+      </webApp>
+    </jetty.run>
+  </target>
+
+</project>
+              
+----
+context XML file:::
+  You may prefer or even require to do some advanced configuration of your web application outside of the Ant build file.
+  In this case, you can use a standard context XML configuration file which the Ant plugin applies to your web application before it is deployed.
+  Be aware that the settings from the context XML file _override_ those of the attributes and nested elements you defined in the build file.
++
+[source, xml, subs="{sub-order}"]
+----
+project name="Jetty-Ant integration test" basedir=".">
+
+  <path id="jetty.plugin.classpath">
+    <fileset dir="jetty-lib" includes="*.jar"/>
+  </path>
+
+ <taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
+
+ <typedef name="webApp" classname="org.eclipse.jetty.ant.AntWebAppContext"
+          classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
+
+  <target name="jetty.run">
+    <jetty.run>
+      <webApp war="${basedir}/foo" contextPath="/" contextXml="${basedir}/jetty-env.xml">
+        <attributes>
+      </webApp>
+    </jetty.run>
+  </target>
+
+</project>
+              
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/clients/http/chapter.adoc b/jetty-documentation/src/main/asciidoc/development/clients/http/chapter.adoc
new file mode 100644
index 0000000..048119e
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/clients/http/chapter.adoc
@@ -0,0 +1,25 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http-client]]
+== HTTP Client
+
+include::http-client-intro.adoc[]
+include::http-client-api.adoc[]
+include::http-client-cookie.adoc[]
+include::http-client-authentication.adoc[]
+include::http-client-proxy.adoc[]
+include::http-client-transport.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-api.adoc b/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-api.adoc
new file mode 100644
index 0000000..8ea908d
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-api.adoc
@@ -0,0 +1,380 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http-client-api]]
+=== API Usage
+
+[[http-client-blocking]]
+==== Blocking APIs
+
+The simple way to perform a HTTP request is the following:
+
+[source, java, subs="{sub-order}"]
+----
+ContentResponse response = httpClient.GET("http://domain.com/path?query");
+----
+
+The method `HttpClient.GET(...)` performs a HTTP `GET` request to the given URI and returns a `ContentResponse` when the request/response conversation completes successfully.
+
+The `ContentResponse` object contains the HTTP response information: status code, headers and possibly content.
+The content length is limited by default to 2 MiB; for larger content see xref:http-client-response-content[].
+
+If you want to customize the request, for example by issuing a `HEAD` request instead of a `GET`, and simulating a browser user agent, you can do it in this way:
+
+[source, java, subs="{sub-order}"]
+----
+ContentResponse response = httpClient.newRequest("http://domain.com/path?query")
+        .method(HttpMethod.HEAD)
+        .agent("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0")
+        .send();
+----
+
+This is a shorthand for:
+
+[source, java, subs="{sub-order}"]
+----
+Request request = httpClient.newRequest("http://domain.com/path?query");
+request.method(HttpMethod.HEAD);
+request.agent("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0");
+ContentResponse response = request.send();
+----
+
+You first create a request object using `httpClient.newRequest(...)`, and then you customize it using the fluent API style (that is, a chained invocation of methods on the request object).
+When the request object is customized, you call `request.send()` that produces the `ContentResponse` when the request/response conversation is complete.
+
+Simple `POST` requests also have a shortcut method:
+
+[source, java, subs="{sub-order}"]
+----
+ContentResponse response = httpClient.POST("http://domain.com/entity/1")
+        .param("p", "value")
+        .send();
+----
+
+The `POST` parameter values added via the `param()` method are automatically URL-encoded.
+
+Jetty's HTTP client automatically follows redirects, so it handles the typical web pattern http://en.wikipedia.org/wiki/Post/Redirect/Get[POST/Redirect/GET], and the response object contains the content of the response of the `GET` request.
+Following redirects is a feature that you can enable/disable on a per-request basis or globally.
+
+File uploads also require one line, and make use of JDK 7′s `java.nio.file` classes:
+
+[source, java, subs="{sub-order}"]
+----
+ContentResponse response = httpClient.newRequest("http://domain.com/upload")
+        .method(HttpMethod.POST)
+        .file(Paths.get("file_to_upload.txt"), "text/plain")
+        .send();
+----
+
+It is possible to impose a total timeout for the request/response conversation using the `Request.timeout(...)` method as follows:
+
+[source, java, subs="{sub-order}"]
+----
+ContentResponse response = httpClient.newRequest("http://domain.com/path?query")
+        .timeout(5, TimeUnit.SECONDS)
+        .send();
+----
+
+In the example above, when the 5 seconds expire, the request is aborted and a `java.util.concurrent.TimeoutException` is thrown.
+
+[[http-client-async]]
+==== Non-Blocking APIs
+
+So far we have shown how to use Jetty HTTP client in a blocking style - that is, the thread that issues the request blocks until the request/response conversation is complete.
+
+This section will look at Jetty's HTTP client non-blocking, asynchronous APIs that are perfectly suited for large content downloads, for parallel processing of requests/responses and in cases where performance and efficient thread and resource utilization is a key factor.
+
+The asynchronous APIs rely heavily on listeners that are invoked at various stages of request and response processing.
+These listeners are implemented by applications and may perform any kind of logic.
+The implementation invokes these listeners in the same thread that is used to process the request or response.
+Therefore, if the application code in these listeners takes a long time to execute, the request or response processing is delayed until the listener returns.
+
+If you need to execute application code that takes long time inside a listener, you must spawn your own thread and remember to deep copy any data provided by the listener that you will need in your code, because when the listener returns the data it provides may be recycled/cleared/destroyed.
+
+Request and response processing are executed by two different threads and therefore may happen concurrently.
+A typical example of this concurrent processing is an echo server, where a large upload may be concurrent with the large download echoed back.
+As a side note, remember that responses may be processed and completed _before_ requests; a typical example is a large upload that triggers a quick response - for example an error - by the server: the response may arrive and be completed while the request content is still being uploaded.
+
+The application thread that calls `Request.send(Response.CompleteListener)` performs the processing of the request until either the request is fully processed or until it would block on I/O, then it returns (and therefore never blocks).
+If it would block on I/O, the thread asks the I/O system to emit an event when the I/O will be ready to continue, then returns.
+When such an event is fired, a thread taken from the `HttpClient` thread pool will resume the processing of the request.
+
+Response are processed from the I/O thread that fires the event that bytes are ready to be read.
+Response processing continues until either the response is fully processed or until it would block for I/O.
+If it would block for I/O, the thread asks the I/O system to emit an event when the I/O will be ready to continue, then returns.
+When such an event is fired, a thread taken from the `HttpClient` thread pool will resume the processing of the response.
+
+When the request and the response are both fully processed, the thread that finished the last processing (usually the thread that processes the response, but may also be the thread that processes the request - if the request takes more time than the response to be processed) is used to de-queue the next request for the same destination and processes it.
+
+A simple asynchronous `GET` request that discards the response content can be written in this way:
+
+[source, java, subs="{sub-order}"]
+----
+httpClient.newRequest("http://domain.com/path")
+        .send(new Response.CompleteListener()
+        {
+            @Override
+            public void onComplete(Result result)
+            {
+                // Your logic here
+            }
+        });
+----
+
+Method `Request.send(Response.CompleteListener)` returns `void` and does not block; the `Response.CompleteListener` provided as a parameter is notified when the request/response conversation is complete, and the `Result` parameter allows you to access the response object.
+
+You can write the same code using JDK 8′s lambda expressions:
+
+[source, java, subs="{sub-order}"]
+----
+httpClient.newRequest("http://domain.com/path")
+        .send(result -> { /* Your logic here */ });
+----
+
+You can impose a total timeout for the request/response conversation in the same way used by the synchronous API:
+
+[source, java, subs="{sub-order}"]
+----
+Request request = httpClient.newRequest("http://domain.com/path")
+        .timeout(3, TimeUnit.SECONDS)
+        .send(result -> { /* Your logic here */ });
+----
+
+The example above will impose a total timeout of 3 seconds on the request/response conversation.
+
+The HTTP client APIs use listeners extensively to provide hooks for all possible request and response events, and with JDK 8′s lambda expressions they are even more fun to use:
+
+[source, java, subs="{sub-order}"]
+----
+httpClient.newRequest("http://domain.com/path")
+        // Add request hooks
+        .onRequestQueued(request -> { ... })
+        .onRequestBegin(request -> { ... })
+        ... // More request hooks available
+
+        // Add response hooks
+        .onResponseBegin(response -> { ... })
+        .onResponseHeaders(response -> { ... })
+        .onResponseContent((response, buffer) -> { ... })
+        ... // More response hooks available
+
+        .send(result -> { ... });
+----
+
+This makes Jetty HTTP client suitable for HTTP load testing because, for example, you can accurately time every step of the request/response conversation (thus knowing where the request/response time is really spent).
+
+Have a look at the link:{JDURL}/org/eclipse/jetty/client/api/Request.Listener.html[`Request.Listener`] class to know about request events, and to the link:{JDURL}/org/eclipse/jetty/client/api/Response.Listener.html[`Response.Listener`] class to know about response events.
+
+[[http-client-content]]
+==== Content Handling
+
+[[http-client-request-content]]
+===== Request Content Handling
+
+Jetty's HTTP client provides a number of utility classes off the shelf to handle request content.
+
+You can provide request content as `String`, `byte[]`, `ByteBuffer`, `java.nio.file.Path`, `InputStream`, and provide your own implementation of `org.eclipse.jetty.client.api.ContentProvider`.
+Here’s an example that provides the request content using `java.nio.file.Paths`:
+
+[source, java, subs="{sub-order}"]
+----
+ContentResponse response = httpClient.newRequest("http://domain.com/upload")
+        .method(HttpMethod.POST)
+        .file(Paths.get("file_to_upload.txt"), "text/plain")
+        .send();
+----
+
+This is equivalent to using the `PathContentProvider` utility class:
+
+[source, java, subs="{sub-order}"]
+----
+ContentResponse response = httpClient.newRequest("http://domain.com/upload")
+        .method(HttpMethod.POST)
+        .content(new PathContentProvider(Paths.get("file_to_upload.txt")), "text/plain")
+        .send();
+----
+
+Alternatively, you can use `FileInputStream` via the `InputStreamContentProvider` utility class:
+
+[source, java, subs="{sub-order}"]
+----
+ContentResponse response = httpClient.newRequest("http://domain.com/upload")
+        .method(HttpMethod.POST)
+        .content(new InputStreamContentProvider(new FileInputStream("file_to_upload.txt")), "text/plain")
+        .send();
+----
+
+Since `InputStream` is blocking, then also the send of the request will block if the input stream blocks, even in case of usage of the asynchronous `HttpClient` APIs.
+
+If you have already read the content in memory, you can pass it as a `byte[]` using the `BytesContentProvider` utility class:
+
+[source, java, subs="{sub-order}"]
+----
+byte[] bytes = ...;
+ContentResponse response = httpClient.newRequest("http://domain.com/upload")
+        .method(HttpMethod.POST)
+        .content(new BytesContentProvider(bytes), "text/plain")
+        .send();
+----
+
+If the request content is not immediately available, but your application will be notified of the content to send, you can use `DeferredContentProvider` in this way:
+
+[source, java, subs="{sub-order}"]
+----
+DeferredContentProvider content = new DeferredContentProvider();
+httpClient.newRequest("http://domain.com/upload")
+        .method(HttpMethod.POST)
+        .content(content)
+        .send(new Response.CompleteListener()
+        {
+            @Override
+            public void onComplete(Result result)
+            {
+                // Your logic here
+            }
+        });
+
+// Content not available yet here
+
+...
+
+// An event happens, now content is available
+byte[] bytes = ...;
+content.offer(ByteBuffer.wrap(bytes));
+
+...
+
+// All content has arrived
+content.close();
+----
+
+While the request content is awaited and consequently uploaded by the client application, the server may be able to respond (at least with the response headers) completely asynchronously.
+In this case, `Response.Listener` callbacks will be invoked before the request is fully sent.
+This allows fine-grained control of the request/response conversation: for example the server may reject contents that are too big, send a response to the client, which in turn may stop the content upload.
+
+Another way to provide request content is by using an `OutputStreamContentProvider`,
+which allows applications to write request content when it is available to the `OutputStream` provided by `OutputStreamContentProvider`:
+
+[source, java, subs="{sub-order}"]
+----
+OutputStreamContentProvider content = new OutputStreamContentProvider();
+
+// Use try-with-resources to close the OutputStream when all content is written
+try (OutputStream output = content.getOutputStream())
+{
+    client.newRequest("localhost", 8080)
+            .method(HttpMethod.POST)
+            .content(content)
+            .send(new Response.CompleteListener()
+            {
+                @Override
+                public void onComplete(Result result)
+                {
+                    // Your logic here
+                }
+            });
+
+    ...
+
+    // Write content
+    writeContent(output);
+}
+// End of try-with-resource, output.close() called automatically to signal end of content
+----
+
+[[http-client-response-content]]
+===== Response Content Handling
+
+Jetty HTTP client allows applications to handle response content in different ways.
+
+The first way is to buffer the response content in memory; this is done when using the blocking APIs (see xref:http-client-blocking[]) and the content is buffered within a `ContentResponse` up to 2 MiB.
+
+If you want to control the length of the response content (for example limiting to values smaller than the default of 2 MiB), then you can use a `org.eclipse.jetty.client.util.FutureResponseListener` in this way:
+
+[source, java, subs="{sub-order}"]
+----
+Request request = httpClient.newRequest("http://domain.com/path");
+
+// Limit response content buffer to 512 KiB
+FutureResponseListener listener = new FutureResponseListener(request, 512 * 1024);
+
+request.send(listener);
+
+ContentResponse response = listener.get(5, TimeUnit.SECONDS);
+----
+
+If the response content length is exceeded, the response will be aborted, and an exception will be thrown by method `get()`.
+
+If you are using the asynchronous APIs (see xref:http-client-async[]), you can use the `BufferingResponseListener` utility class:
+
+[source, java, subs="{sub-order}"]
+----
+httpClient.newRequest("http://domain.com/path")
+        // Buffer response content up to 8 MiB
+        .send(new BufferingResponseListener(8 * 1024 * 1024)
+        {
+            @Override
+            public void onComplete(Result result)
+            {
+                if (!result.isFailed())
+                {
+                    byte[] responseContent = getContent();
+                    // Your logic here
+                }
+            }
+        });
+----
+
+The second way is the most efficient (because it avoids content copies) and allows you to specify a `Response.ContentListener`, or a subclass, to handle the content as soon as it arrives.
+In the example below, `Response.Listener.Adapter` is a class that implements both `Response.ContentListener` and `Response.CompleteListener` and can be passed to `Request.send()`.
+Jetty's HTTP client will invoke the `onContent()` method zero or more times (until there is content), and finally invoke the `onComplete()` method.
+
+[source, java, subs="{sub-order}"]
+----
+ContentResponse response = httpClient
+        .newRequest("http://domain.com/path")
+        .send(new Response.Listener.Adapter()
+        {
+            @Override
+            public void onContent(Response response, ByteBuffer buffer)
+            {
+                // Your logic here
+            }
+        });
+----
+
+The third way allows you to wait for the response and then stream the content using the `InputStreamResponseListener` utility class:
+
+[source, java, subs="{sub-order}"]
+----
+
+InputStreamResponseListener listener = new InputStreamResponseListener();
+httpClient.newRequest("http://domain.com/path")
+        .send(listener);
+
+// Wait for the response headers to arrive
+Response response = listener.get(5, TimeUnit.SECONDS);
+
+// Look at the response
+if (response.getStatus() == HttpStatus.OK_200)
+{
+    // Use try-with-resources to close input stream.
+    try (InputStream responseContent = listener.getInputStream())
+    {
+        // Your logic here
+    }
+}
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-authentication.adoc b/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-authentication.adoc
new file mode 100644
index 0000000..cf89bea
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-authentication.adoc
@@ -0,0 +1,78 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http-client-authentication]]
+=== Authentication Support
+
+Jetty's HTTP client supports the "Basic" and "Digest" authentication mechanisms defined by link:https://tools.ietf.org/html/rfc7235[RFC 7235].
+
+You can configure authentication credentials in the HTTP client instance as follows:
+
+[source, java, subs="{sub-order}"]
+----
+URI uri = new URI("http://domain.com/secure");
+String realm = "MyRealm";
+String user = "username";
+String pass = "password";
+
+// Add authentication credentials
+AuthenticationStore auth = httpClient.getAuthenticationStore();
+auth.addAuthentication(new BasicAuthentication(uri, realm, user, pass));
+
+ContentResponse response = httpClient
+        .newRequest(uri)
+        .send()
+        .get(5, TimeUnit.SECONDS);
+----
+
+Jetty's HTTP client tests authentication credentials against the challenge(s) the server issues, and if they match it automatically sends the right authentication headers to the server for authentication.
+If the authentication is successful, it caches the result and reuses it for subsequent requests for the same domain and matching URIs.
+
+The HTTP conversation for a successful match is the following:
+
+----
+Application  HttpClient                     Server
+     |           |                             |
+     |--- GET ---|------------ GET ----------->|
+     |           |                             |
+     |           |<-- 401 + WWW-Authenticate --|
+     |           |                             |
+     |           |--- GET + Authentication --->|
+     |           |                             |
+     |<-- 200 ---|------------ 200 ------------|
+----
+
+The application does not receive events related to the response with code 401, they are handled internally by `HttpClient` which produces a request similar to the original but with the correct `Authorization` header, and then relays the response with code 200 to the application.
+
+Successful authentications are cached, but it is possible to clear them in order to force authentication again:
+
+[source, java, subs="{sub-order}"]
+----
+httpClient.getAuthenticationStore().clearAuthenticationResults();
+----
+
+Authentications may be preempted to avoid the additional roundtrip due to the server challenge in this way:
+
+[source, java, subs="{sub-order}"]
+----
+AuthenticationStore auth = httpClient.getAuthenticationStore();
+URI uri = URI.create("http://domain.com/secure");
+auth.addAuthenticationResult(new BasicAuthentication.BasicResult(uri, "username", "password"));
+----
+
+In this way, the original request is enriched by `HttpClient` immediately with the `Authorization` header, and the server should respond with a 200 and the resource content rather than with the 401 and the challenge.
+
+See also the link:#http-client-proxy-authentication[proxy authentication section] for further information about how authentication works with HTTP proxies.
diff --git a/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-cookie.adoc b/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-cookie.adoc
new file mode 100644
index 0000000..a2ab7b3
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-cookie.adoc
@@ -0,0 +1,88 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http-client-cookie]]
+=== Cookies Support
+
+Jetty HTTP client supports cookies out of the box.
+The `HttpClient` instance receives cookies from HTTP responses and stores them in a `java.net.CookieStore`, a class that is part of the JDK.
+When new requests are made, the cookie store is consulted and if there are matching cookies (that is, cookies that are not expired and that match domain and path of the request) then they are added to the requests.
+
+Applications can programmatically access the cookie store to find the cookies that have been set:
+
+[source, java, subs="{sub-order}"]
+----
+CookieStore cookieStore = httpClient.getCookieStore();
+List<HttpCookie> cookies = cookieStore.get(URI.create("http://domain.com/path"));
+----
+
+Applications can also programmatically set cookies as if they were returned from a HTTP response:
+
+[source, java, subs="{sub-order}"]
+----
+CookieStore cookieStore = httpClient.getCookieStore();
+HttpCookie cookie = new HttpCookie("foo", "bar");
+cookie.setDomain("domain.com");
+cookie.setPath("/");
+cookie.setMaxAge(TimeUnit.DAYS.toSeconds(1));
+cookieStore.add(URI.create("http://domain.com"), cookie);
+----
+
+Cookies may be added only for a particular request:
+
+[source, java, subs="{sub-order}"]
+----
+ContentResponse response = httpClient.newRequest("http://domain.com/path")
+        .cookie(new HttpCookie("foo", "bar"))
+        .send();
+----
+
+You can remove cookies that you do not want to be sent in future HTTP requests:
+
+[source, java, subs="{sub-order}"]
+----
+CookieStore cookieStore = httpClient.getCookieStore();
+URI uri = URI.create("http://domain.com");
+List<HttpCookie> cookies = cookieStore.get(uri);
+for (HttpCookie cookie : cookies)
+    cookieStore.remove(uri, cookie);
+----
+
+If you want to totally disable cookie handling, you can install a `HttpCookieStore.Empty` instance in this way:
+
+[source, java, subs="{sub-order}"]
+----
+httpClient.setCookieStore(new HttpCookieStore.Empty());
+----
+
+You can enable cookie filtering by installing a cookie store that performs the filtering logic in this way:
+
+[source, java, subs="{sub-order}"]
+----
+httpClient.setCookieStore(new GoogleOnlyCookieStore());
+
+public class GoogleOnlyCookieStore extends HttpCookieStore
+{
+    @Override
+    public void add(URI uri, HttpCookie cookie)
+    {
+        if (uri.getHost().endsWith("google.com"))
+            super.add(uri, cookie);
+    }
+}
+----
+
+The example above will retain only cookies that come from the `google.com` domain or sub-domains.
diff --git a/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-intro.adoc b/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-intro.adoc
new file mode 100644
index 0000000..23e1d50
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-intro.adoc
@@ -0,0 +1,103 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http-client-intro]]
+=== Introduction
+
+The Jetty HTTP client module provides easy-to-use APIs and utility classes to perform HTTP (or HTTPS) requests.
+
+Jetty's HTTP client is non-blocking and asynchronous.
+It offers an asynchronous API that never blocks for I/O, making it very efficient in thread utilization and well suited for high performance scenarios such as load testing or parallel computation.
+
+However, when all you need to do is to perform a `GET` request to a resource, Jetty's HTTP client offers also a synchronous API; a programming interface
+where the thread that issued the request blocks until the request/response conversation is complete.
+
+Jetty's HTTP client supports different link:#http-client-transport[transports]: HTTP/1.1, FastCGI and HTTP/2.
+This means that the semantic of a HTTP request (that is, " `GET` me the resource `/index.html` ") can be carried over the network in different formats.
+The most common and default format is HTTP/1.1.
+That said, Jetty's HTTP client can carry the same request using the FastCGI format or the new HTTP/2 format.
+
+The FastCGI transport is heavily used in Jetty's link:#fastcgi[FastCGI support] that allows Jetty to work as a reverse proxy to PHP (exactly like Apache or Nginx do) and therefore be able to serve - for example - WordPress websites.
+
+The HTTP/2 transport allows Jetty's HTTP client to perform requests using HTTP/2 to HTTP/2 enabled web sites, see also Jetty's link:#http2[HTTP/2 support].
+
+Out of the box features that you get with the Jetty HTTP client include:
+
+* Redirect support - redirect codes such as 302 or 303 are automatically followed.
+* Cookies support - cookies sent by servers are stored and sent back to servers in matching requests.
+* Authentication support - HTTP "Basic" and "Digest" authentications are supported, others are pluggable.
+* Forward proxy support - HTTP proxying and SOCKS4 proxying.
+
+[[http-client-init]]
+==== Starting HttpClient
+
+The main class is named `org.eclipse.jetty.client.HttpClient`.
+
+You can think of a `HttpClient` instance as a browser instance.
+Like a browser it can make requests to different domains, it manages redirects, cookies and authentication, you can configure it with a proxy, and
+it provides you with the responses to the requests you make.
+
+In order to use `HttpClient`, you must instantiate it, configure it, and then start it:
+
+[source, java, subs="{sub-order}"]
+----
+// Instantiate HttpClient
+HttpClient httpClient = new HttpClient();
+
+// Configure HttpClient, for example:
+httpClient.setFollowRedirects(false);
+
+// Start HttpClient
+httpClient.start();
+----
+
+You may create multiple instances of `HttpClient`, but typically one instance is enough for an application.
+There are several reasons for having multiple `HttpClient` instances including, but not limited to:
+
+* You want to specify different configuration parameters (for example, one instance is configured with a forward proxy while another is not)
+* You want the two instances to behave like two different browsers and hence have different cookies, different authentication credentials...etc.
+* You want to use different transports
+
+When you create a `HttpClient` instance using the parameterless constructor, you will only be able to perform plain HTTP requests and you will not be able to perform HTTPS requests.
+
+In order to perform HTTPS requests, you should create first a link:{JDURL}/org/eclipse/jetty/util/ssl/SslContextFactory.html[`SslContextFactory`], configure it, and pass it to the `HttpClient` constructor.
+When created with a `SslContextFactory`, the `HttpClient` will be able to perform both HTTP and HTTPS requests to any domain.
+
+[source, java, subs="{sub-order}"]
+----
+// Instantiate and configure the SslContextFactory
+SslContextFactory sslContextFactory = new SslContextFactory();
+
+// Instantiate HttpClient with the SslContextFactory
+HttpClient httpClient = new HttpClient(sslContextFactory);
+
+// Configure HttpClient, for example:
+httpClient.setFollowRedirects(false);
+
+// Start HttpClient
+httpClient.start();
+----
+
+==== Stopping HttpClient
+
+It is recommended that when your application stops, you also stop the `HttpClient` instance (or instances) that you are using.
+
+[source, java, subs="{sub-order}"]
+----
+httpClient.stop();
+----
+
+Stopping `HttpClient` makes sure that the memory it holds (for example, authentication credentials, cookies, etc.) is released, and that the thread pool and scheduler are properly stopped allowing all threads used by `HttpClient` to exit.
diff --git a/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-proxy.adoc b/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-proxy.adoc
new file mode 100644
index 0000000..3f3335b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-proxy.adoc
@@ -0,0 +1,101 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http-client-proxy]]
+=== Proxy Support
+
+Jetty's HTTP client can be configured to use proxies to connect to destinations.
+
+Two types of proxies are available out of the box: a HTTP proxy (provided by class `org.eclipse.jetty.client.HttpProxy`) and a SOCKS 4 proxy (provided by class `org.eclipse.jetty.client.Socks4Proxy`).
+Other implementations may be written by subclassing `ProxyConfiguration.Proxy`.
+
+The following is a typical configuration:
+
+[source, java, subs="{sub-order}"]
+----
+ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
+HttpProxy proxy = new HttpProxy("proxyHost", proxyPort);
+
+// Do not proxy requests for localhost:8080
+proxy.getExcludedAddresses().add("localhost:8080");
+
+// add the new proxy to the list of proxies already registered
+proxyConfig.getProxies().add(proxy);
+
+ContentResponse response = httpClient.GET(uri);
+----
+
+You specify the proxy host and port, and optionally also the addresses that you do not want to be proxied, and then add the proxy configuration on the `ProxyConfiguration` instance.
+
+Configured in this way, `HttpClient` makes requests to the HTTP proxy (for plain-text HTTP requests) or establishes a tunnel via `HTTP CONNECT` (for encrypted HTTPS requests).
+
+[[http-client-proxy-authentication]]
+==== Proxy Authentication Support
+
+Jetty's HTTP client support proxy authentication in the same way it supports link:#http-client-authentication[server authentication].
+
+In the example below, the proxy requires Basic authentication, but the server requires Digest authentication, and therefore:
+
+[source, java, subs="{sub-order}"]
+----
+URI proxyURI = new URI("http://proxy.net:8080");
+URI serverURI = new URI("http://domain.com/secure");
+
+AuthenticationStore auth = httpClient.getAuthenticationStore();
+
+// Proxy credentials.
+auth.addAuthentication(new BasicAuthentication(proxyURI, "ProxyRealm", "proxyUser", "proxyPass"));
+
+// Server credentials.
+auth.addAuthentication(new DigestAuthentication(serverURI, "ServerRealm", "serverUser", "serverPass"));
+
+// Proxy configuration.
+ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
+HttpProxy proxy = new HttpProxy("proxy.net", 8080);
+proxyConfig.getProxies().add(proxy);
+
+ContentResponse response = httpClient.newRequest(serverURI)
+        .send()
+        .get(5, TimeUnit.SECONDS);
+----
+
+The HTTP conversation for successful authentications on both the proxy and the server is the following:
+
+----
+Application  HttpClient                         Proxy                    Server
+     |           |                                |                         |
+     |--- GET -->|------------- GET ------------->|                         |
+     |           |                                |                         |
+     |           |<----- 407 + Proxy-Authn -------|                         |
+     |           |                                |                         |
+     |           |------ GET + Proxy-Authz ------>|                         |
+     |           |                                |                         |
+     |           |                                |---------- GET --------->|
+     |           |                                |                         |
+     |           |                                |<--- 401 + WWW-Authn ----|
+     |           |                                |                         |
+     |           |<------ 401 + WWW-Authn --------|                         |
+     |           |                                |                         |
+     |           |-- GET + Proxy-Authz + Authz -->|                         |
+     |           |                                |                         |
+     |           |                                |------ GET + Authz ----->|
+     |           |                                |                         |
+     |<-- 200 ---|<------------ 200 --------------|<--------- 200 ----------|
+----
+
+The application does not receive events related to the responses with code 407 and 401 since they are handled internally by `HttpClient`.
+
+Similarly to the link:#http-client-authentication[authentication section], the proxy authentication result and the server authentication result can be preempted to avoid, respectively, the 407 and 401 roundtrips.
diff --git a/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-transport.adoc b/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-transport.adoc
new file mode 100644
index 0000000..9b2112d
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/clients/http/http-client-transport.adoc
@@ -0,0 +1,113 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[http-client-transport]]
+=== Pluggable Transports
+
+Jetty's HTTP client can be configured to use different transports to carry the semantic of HTTP requests and responses.
+
+This means that the intention of a client to request resource `/index.html` using the `GET` method can be carried over the network in different formats.
+
+A HTTP client transport is the component that is in charge of converting a high-level, semantic, HTTP requests such as "GET resource /index.html" into the specific format understood by the server (for example, HTTP/2), and to convert the server response from the specific format (HTTP/2) into high-level, semantic objects that can be used by applications.
+
+In this way, applications are not aware of the actual protocol being used.
+This allows them to write their logic against a high-level API that hides the details of the specific protocol being used over the network.
+
+The most common protocol format is HTTP/1.1, a text-based protocol with lines separated by `\r\n`:
+
+[source, screen, subs="{sub-order}"]
+----
+GET /index.html HTTP/1.1\r\n
+Host: domain.com\r\n
+...
+\r\n
+----
+
+However, the same request can be made using FastCGI, a binary protocol:
+
+[source, screen, subs="{sub-order}"]
+----
+x01 x01 x00 x01 x00 x08 x00 x00
+x00 x01 x01 x00 x00 x00 x00 x00
+x01 x04 x00 x01 xLL xLL x00 x00
+x0C x0B  D   O   C   U   M   E
+ N   T   _   U   R   I   /   i
+ n   d   e   x   .   h   t   m
+ l
+...
+----
+
+Similarly, HTTP/2 is a binary protocol that transports the same information in a yet different format.
+
+==== HTTP/1.1 Transport
+
+HTTP/1.1 is the default transport.
+
+[source, java, subs="{sub-order}"]
+----
+// No transport specified, using default.
+HttpClient client = new HttpClient();
+client.start();
+----
+
+If you want to customize the HTTP/1.1 transport, you can explicitly configure `HttpClient` in this way:
+
+[source, java, subs="{sub-order}"]
+----
+int selectors = 1;
+HttpClientTransportOverHTTP transport = new HttpClientTransportOverHTTP(selectors);
+
+HttpClient client = new HttpClient(transport, null);
+client.start();
+----
+
+The example above allows you to customize the number of NIO selectors that `HttpClient` will be using.
+
+==== HTTP/2 Transport
+
+The HTTP/2 transport can be configured in this way:
+
+[source, java, subs="{sub-order}"]
+----
+HTTP2Client h2Client = new HTTP2Client();
+h2Client.setSelectors(1);
+HttpClientTransportOverHTTP2 transport = new HttpClientTransportOverHTTP2(h2Client);
+
+HttpClient client = new HttpClient(transport, null);
+client.start();
+----
+
+`HTTP2Client` is the lower-level client that provides an API based on HTTP/2 concepts such as _sessions_, _streams_ and _frames_ that are specific to HTTP/2.
+
+`HttpClientTransportOverHTTP2` uses `HTTP2Client` to format high-level semantic HTTP requests ("GET resource /index.html") into the HTTP/2 specific format.
+
+==== FastCGI Transport
+
+The FastCGI transport can be configured in this way:
+
+[source, java, subs="{sub-order}"]
+----
+int selectors = 1;
+String scriptRoot = "/var/www/wordpress";
+HttpClientTransportOverFCGI transport = new HttpClientTransportOverFCGI(selectors, false, scriptRoot);
+
+HttpClient client = new HttpClient(transport, null);
+client.start();
+----
+
+In order to make requests using the FastCGI transport, you need to have a FastCGI server such as https://en.wikipedia.org/wiki/PHP#PHPFPM[PHP-FPM] (see also http://php.net/manual/en/install.fpm.php).
+
+The FastCGI transport is primarily used by Jetty's link:#fastcgi[FastCGI support] to serve PHP pages (WordPress for example).
diff --git a/jetty-documentation/src/main/asciidoc/development/continuations/chapter.adoc b/jetty-documentation/src/main/asciidoc/development/continuations/chapter.adoc
new file mode 100644
index 0000000..32ecf81
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/continuations/chapter.adoc
@@ -0,0 +1,22 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[continuations]]
+== Continuations
+
+include::continuations-intro.adoc[]
+include::continuations-using.adoc[]
+include::continuations-patterns.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/development/continuations/continuations-intro.adoc b/jetty-documentation/src/main/asciidoc/development/continuations/continuations-intro.adoc
new file mode 100644
index 0000000..8e14840
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/continuations/continuations-intro.adoc
@@ -0,0 +1,127 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[continuations-intro]]
+=== Introduction
+
+Continuations are a mechanism to implement Asynchronous servlets similar to asynchronous features in Servlet 3.0, but provides a simpler and portable interface.
+
+==== Why Asynchronous Servlets ?
+
+===== Not Asynchronous IO
+
+The concept of Asynchronous Servlets is often confused with Asynchronous IO or the use of NIO.
+However, Asynchronous Servlets are not primarily motivated by asynchronous IO, since:
+
+* HTTP Requests are mostly small and arrive in a single packet. Servlets rarely block on requests.
+
+* Many responses are small and fit within the server buffers, so servlets often do not block writing responses.
+
+* Even if we could expose asynchronous IO in a servlet, it is a hard paradigm to program. For example what would an application do if it read 2 bytes of a 3 byte UTF-8 character?
+It would have to buffer and wait for more bytes.
+This is best done by the container rather than the application.
+
+===== Asynchronous Waiting
+
+The main use-case for asynchronous servlets is waiting for non-IO events or resources.
+Many web applications need to wait at some stage during the processing of a HTTP request, for example:
+
+* Waiting for a resource to be available before processing the request (e.g., thread, JDBC Connection).
+
+* Waiting for an application event in an AJAX Comet application (e.g., chat message, price change).
+
+* Waiting for a response from a remote service (e.g., RESTful or SOAP call to a web service).
+
+The servlet API (pre 2.5) supports only a synchronous call style, so that any waiting that a servlet needs to do must be with blocking.
+Unfortunately this means that the thread allocated to the request must be held during that wait along with all its resources: kernel thread, stack memory and often pooled buffers, character converters, EE authentication context, etc.
+It is wasteful of system resources to hold these resources while waiting. Significantly better scalability and quality of service can be achieved if waiting is done asynchronously.
+
+==== Asynchronous Servlet Examples
+
+===== AJAX Comet Server Push
+
+Web 2.0 applications can use the http://en.wikipedia.org/wiki/Comet_(programming)[comet] technique (aka AJAX Push, Server Push, Long Polling) to dynamically update a web page without refreshing the entire page.
+
+Consider a stock portfolio web application. Each browser will send a long poll request to the server asking for any of the user's stock prices that have changed. The server will receive the long poll requests from all its clients, but will not immediately respond.
+Instead the server waits until a stock price changes, at which time it will send a response to each of the clients with that stock in their portfolio.
+The clients that receive the long poll response will immediately send another long poll request so they may obtain future price changes.
+
+Thus the server will typically hold a long poll request for every connected user, so if the servlet is not asynchronous, there would need more than 1000 threads available to handle 1000 simultaneous users.
+1000 threads can consume over 256MB of memory; that would be better used for the application rather than idly waiting for a price to change.
+
+If the servlet is asynchronous, then the number of threads needed is governed by the time to generate each response and the frequency of price changes.
+If every user receives a price every 10 seconds and the response takes 10ms to generate, then 1000 users can be serviced with just 1 thread, and the 256MB of stack be freed for other purposes.
+
+For more on comet see the http://cometd.org/[cometd] project that works asynchronously with Jetty.
+
+===== Asynchronous RESTful Web Service
+
+Consider a web application that accesses a remote web service (e.g., SOAP service or RESTful service).
+Typically a remote web service can take hundreds of milliseconds to produce a response -- eBay's RESTful web service frequently takes 350ms to respond with a list of auctions matching a given keyword -- while only a few 10s of milliseconds of CPU time are needed to locally process a request and generate a response.
+
+To handle 1000 requests per second, which each perform a 200ms web service call, a webapp would needs 1000*(200+20)/1000 = 220 threads and 110MB of stack memory.
+It would also be vulnerable to thread starvation if bursts occurred or the web service became slower. If handled asynchronously, the web application would not need to hold a thread while waiting for web service response.
+Even if the asynchronous mechanism cost 10ms (which it doesn't), then this webapp would need 1000*(20+10)/1000 = 30 threads and 15MB of stack memory.
+This is a 86% reduction in the resources required and 95MB more memory would be available for the application.
+Furthermore, if multiple web services request are required, the asynchronous approach allows these to be made in parallel rather than serially, without allocating additional threads.
+
+For an example of Jetty's solution, see the https://webtide.com/async-rest-jetty-9/[Asynchronous REST example]
+
+===== Quality of Service (e.g., JDBC Connection Pool)
+
+Consider a web application handling on average 400 requests per second, with each request interacting with the database for 50ms.
+To handle this load, 400*50/1000 = 20 JDBC connections are need on average.
+However, requests do not come at an even rate and there are often bursts and pauses.
+To protect a database from bursts, often a JDBC connection pool is applied to limit the simultaneous requests made on the database.
+So for this application, it would be reasonable to apply a JDBC pool of 30 connections, to provide for a 50% margin.
+
+If momentarily the request rate doubled, then the 30 connections would only be able to handle 600 requests per second, and 200 requests per second would join those waiting on the JDBC Connection pool.
+Then if the servlet container had a thread pool with 200 threads, that would be entirely consumed by threads waiting for JDBC connections in 1 second of this request rate.
+After 1s, the web application would be unable to process any requests at all because no threads would be available.
+Even requests that do not use the database would be blocked due to thread starvation.
+To double the thread pool would require an additional 100MB of stack memory and would only give the application another 1s of grace under load!
+
+This thread starvation situation can also occur if the database runs slowly or is momentarily unavailable.
+Thread starvation is a very frequently reported problem, and causes the entire web service to lock up and become unresponsive.
+If the web container was able to suspend the requests waiting for a JDBC connection without threads, then thread starvation would not occur, as only 30 threads would be consumed by requests accessing the database and the other 470 threads would be available to process the request that do not access the database.
+
+For an example of Jetty's solution, see the Quality of Service Filter.
+
+==== Servlet Threading Model
+
+The scalability issues of Java servlets are caused mainly by the server threading model:
+
+===== Thread per connection
+
+The traditional IO model of Java associated a thread with every TCP/IP connection.
+If you have a few very active threads, this model can scale to a very high number of requests per second.
+
+However, the traffic profile typical of many web applications is many persistent HTTP connections that are mostly idle while users read pages or search for the next link to click. With such profiles, the thread-per-connection model can have problems scaling to the thousands of threads required to support thousands of users on large scale deployments.
+
+===== Thread per request
+
+The Java NIO libraries support asynchronous IO, so that threads no longer need to be allocated to every connection.
+When the connection is idle (between requests), then the connection is added to an NIO select set, which allows one thread to scan many connections for activity.
+Only when IO is detected on a connection is a thread allocated to it.
+However, the servlet 2.5 API model still requires a thread to be allocated for the duration of the request handling.
+
+This thread-per-request model allows much greater scaling of connections (users) at the expense of a small reduction to maximum requests per second due to extra scheduling latency.
+
+===== Asynchronous Request handling
+
+The Jetty Continuation (and the servlet 3.0 asynchronous) API introduce a change in the servlet API that allows a request to be dispatched multiple times to a servlet.
+If the servlet does not have the resources required on a dispatch, then the request is suspended (or put into asynchronous mode), so that the servlet may return from the dispatch without a response being sent.
+When the waited-for resources become available, the request is re-dispatched to the servlet, with a new thread, and a response is generated.
diff --git a/jetty-documentation/src/main/asciidoc/development/continuations/continuations-patterns.adoc b/jetty-documentation/src/main/asciidoc/development/continuations/continuations-patterns.adoc
new file mode 100644
index 0000000..48b3b00
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/continuations/continuations-patterns.adoc
@@ -0,0 +1,124 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[continuations-patterns]]
+=== Common Continuation Patterns
+
+==== Suspend Resume Pattern
+
+The suspend/resume style is used when a servlet and/or filter is used to generate the response after an asynchronous wait that is terminated by an asynchronous handler.
+Typically a request attribute is used to pass results and to indicate if the request has already been suspended.
+
+[source, java, subs="{sub-order}"]
+----
+void doGet(HttpServletRequest request, HttpServletResponse response)
+{
+     // if we need to get asynchronous results
+     Object results = request.getAttribute("results");
+     if (results==null)
+     {
+       final Continuation continuation = ContinuationSupport.getContinuation(request);
+
+       // if this is not a timeout
+       if (continuation.isExpired())
+       {
+         sendMyTimeoutResponse(response);
+         return;
+       }
+
+       // suspend the request
+       continuation.suspend(); // always suspend before registration
+
+       // register with async service.  The code here will depend on the
+       // the service used (see Jetty HttpClient for example)
+       myAsyncHandler.register(new MyHandler()
+       {
+          public void onMyEvent(Object result)
+          {
+            continuation.setAttribute("results",results);
+            continuation.resume();
+          }
+       });
+       return; // or continuation.undispatch();
+     }
+
+     // Send the results
+     sendMyResultResponse(response,results);
+}
+
+----
+
+This style is very good when the response needs the facilities of the servlet container (e.g., it uses a web framework) or if one event may resume many requests so the container's thread pool can be used to handle each of them.
+
+==== Suspend Continue Pattern
+
+The suspend/complete style is used when an asynchronous handler is used to generate the response:
+
+[source, java, subs="{sub-order}"]
+----
+void doGet(HttpServletRequest request, HttpServletResponse response)
+{
+     final Continuation continuation = ContinuationSupport.getContinuation(request);
+
+     // if this is not a timeout
+     if (continuation.isExpired())
+     {
+       sendMyTimeoutResponse(request,response);
+       return;
+     }
+
+     // suspend the request
+     continuation.suspend(); // response may be wrapped.
+
+     // register with async service.  The code here will depend on the
+     // the service used (see Jetty HttpClient for example)
+     myAsyncHandler.register(new MyHandler()
+     {
+       public void onMyEvent(Object result)
+       {
+         sendMyResultResponse(continuation.getServletResponse(),results);
+         continuation.complete();
+       }
+     });
+}
+
+----
+
+This style is very good when the response does not need the facilities of the servlet container (e.g., it does not use a web framework) and if an event will resume only one continuation.
+If many responses are to be sent (e.g., a chat room), then writing one response may block and cause a DOS on the other responses.
+
+==== Examples
+
+* The https://github.com/eclipse/jetty.project/blob/jetty-8/test-jetty-webapp/src/main/java/com/acme/ChatServlet.java[ChatServlet example] shows how the suspend/resume style can be used to directly code a chat room (See similar https://github.com/eclipse/jetty.project/blob/master/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/ChatServlet.java[example] using Async Servlets).
+The same principles are applied to frameworks like http://cometd.org/[cometd] which provide an richer environment for such applications, based on Continuations.
+
+* The link:{JDURL}/org/eclipse/jetty/servlets/QoSFilter.html[QoSFilter] uses suspend/resume style to limit the number of requests simultaneously within the filter.
+This can be used to protect a JDBC connection pool or other limited resource from too many simultaneous requests.
+
++
+If too many requests are received, the extra requests wait for a short time on a semaphore, before being suspended.
+As requests within the filter return, they use a priority queue to resume the suspended requests.
+This allows your authenticated or priority users to get a better share of your server's resources when the machine is under load.
++
+
+* The link:{JDURL}/org/eclipse/jetty/servlets/DoSFilter.html[DosFilter] is similar to the QoSFilter, but protects a web application from a denial of service attack, as much as is possible from within a web application.
+
++
+If too many requests are detected coming from one source, then those requests are suspended and a warning generated.
+This works on the assumption that the attacker may be written in simple blocking style, so by suspending you are hopefully consuming their resources. True protection from DOS can only be achieved by network devices (or eugenics :)).
++
+
+* The link:{JDURL}/org/eclipse/jetty/proxy/ProxyServlet.html[ProxyServlet] uses the suspend/complete style and the Jetty asynchronous HTTP client to implement a scalable Proxy server (or transparent proxy).
diff --git a/jetty-documentation/src/main/asciidoc/development/continuations/continuations-using.adoc b/jetty-documentation/src/main/asciidoc/development/continuations/continuations-using.adoc
new file mode 100644
index 0000000..48009c3
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/continuations/continuations-using.adoc
@@ -0,0 +1,114 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[continuations-using]]
+=== Using Continuations
+
+Asynchronous servlets were originally introduced with Jetty 6 Continuations, which were a Jetty specific mechanism.
+From Jetty 7 onwards, the Continuations API has been extended to be a general purpose API that will work asynchronously on any servlet-3.0 container, as well as on Jetty 6, 7, or 8.
+Continuations will also work in blocking mode with any servlet 2.5 container.
+
+==== Obtaining a Continuation
+
+The link:{JDURL}/org/eclipse/jetty/continuation/ContinuationSupport.html[ContinuationSupport] factory class can be used to obtain a continuation instance associated with a request:
+
+`Continuation continuation = ContinuationSupport.getContinuation(request);`
+
+==== Suspending a Request
+
+To suspend a request, the suspend method can be called on the continuation:
+
+[source, java, subs="{sub-order}"]
+----
+    void doGet(HttpServletRequest request, HttpServletResponse response)
+      {
+      ...
+      // optionally:
+      // continuation.setTimeout(long);
+      continuation.suspend();
+      ...
+      }
+
+----
+
+The lifecycle of the request will be extended beyond the return to the  container from the `Servlet.service(...)` method and `Filter.doFilter(...)` calls. When these dispatch methods return, the suspended request will not yet be committed and a response will not yet be sent to the HTTP client.
+
+Once the request has been suspended, the continuation should be registered with an asynchronous service so that it may be used by an asynchronous callback when the waited-for event happens.
+
+The request will be suspended until either `continuation.resume()` or `continuation.complete()` is called. If neither is called then the continuation will timeout.
+The timeout should be set before the suspend, by a call to `continuation.setTimeout(long)` if no timeout is set, then the default period is used.
+If no timeout listeners resume or complete the continuation, then the continuation is resumed with `continuation.isExpired()` true.
+
+Suspension is analogous to the servlet 3.0 `request.startAsync()` method. Unlike jetty 6 continuations, an exception is not thrown by suspend and the method should return normally.
+This allows the registration of the continuation to occur after suspension and avoids the need for a mutex.
+If an exception is desirable (to bypass code that is unaware of continuations and may try to commit the response), then `continuation.undispatch()` may be called to exit the current thread from the current dispatch by throwing a `ContinuationThrowable`.
+
+==== Resuming a Request
+
+Once an asynchronous event has occurred, the continuation can be resumed:
+
+[source, java, subs="{sub-order}"]
+----
+    void myAsyncCallback(Object results)
+    {
+    continuation.setAttribute("results",results);
+    continuation.resume();
+    }
+----
+
+When a continuation is resumed, the request is re-dispatched to the servlet container, almost as if the request had been received again.
+However during the re-dispatch, the `continuation.isInitial()` method returns false and any attributes set by the asynchronous handler are available.
+
+Continuation resume is analogous to Servlet 3.0 `AsyncContext.dispatch()`.
+
+==== Completing a Request
+
+As an alternative to resuming a request, an asynchronous handler may  write the response itself. After writing the response, the handler must indicate the request handling is complete by calling the complete method:
+
+[source, java, subs="{sub-order}"]
+----
+    void myAsyncCallback(Object results)
+    {
+      writeResults(continuation.getServletResponse(),results);
+      continuation.complete();
+    }
+----
+
+After complete is called, the container schedules the response to be  committed and flushed. Continuation complete is analogous to Servlet 3.0 `AsyncContext.complete()`.
+
+==== Continuation Listeners
+
+An application may monitor the status of a continuation by using a ContinuationListener:
+
+[source, java, subs="{sub-order}"]
+----
+    void doGet(HttpServletRequest request, HttpServletResponse response)
+    {
+      ...
+
+      Continuation continuation = ContinuationSupport.getContinuation(request);
+      continuation.addContinuationListener(new ContinuationListener()
+      {
+        public void onTimeout(Continuation continuation) { ... }
+        public void onComplete(Continuation continuation) { ... }
+      });
+
+      continuation.suspend();
+      ...
+    }
+----
+
+Continuation listeners are analogous to Servlet 3.0 AsyncListeners.
diff --git a/jetty-documentation/src/main/asciidoc/development/embedding/chapter.adoc b/jetty-documentation/src/main/asciidoc/development/embedding/chapter.adoc
new file mode 100644
index 0000000..287bb60
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/embedding/chapter.adoc
@@ -0,0 +1,22 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[advanced-embedding]]
+== Embedding
+
+include::jetty-helloworld.adoc[]
+include::embedding-jetty.adoc[]
+include::embedded-examples.adoc[]
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/development/embedding/embedded-examples.adoc b/jetty-documentation/src/main/asciidoc/development/embedding/embedded-examples.adoc
new file mode 100644
index 0000000..6ae14da
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/embedding/embedded-examples.adoc
@@ -0,0 +1,84 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[embedded-examples]]
+=== Embedded Examples
+
+Jetty has a rich history of being embedded into a wide variety of applications.
+In this section we will walk you through a number of our simple examples under our embedded-jetty-examples project in our git repository.
+
+____
+[IMPORTANT]
+These files are pulled directly from our git repository when this document is generated.
+If the line numbers do not line up feel free to fix this documentation in github and give us a pull request, or at least open an issue to notify us of the discrepancy.
+____
+
+include::examples/embedded-file-server.adoc[]
+include::examples/embedded-split-file-server.adoc[]
+include::examples/embedded-many-connectors.adoc[]
+include::examples/embedded-secured-hello-handler.adoc[]
+include::examples/embedded-minimal-servlet.adoc[]
+include::examples/embedded-one-webapp.adoc[]
+
+[[embedded-webapp-jsp]]
+==== Web Application with JSP
+
+This example is very similar to the one in the previous section, although it enables the embedded webapp to use JSPs.
+As of jetty-9.2, we use the JSP engine from Apache, which relies on a Servlet Specification 3.1 style `ServletContainerInitializer` to initialize itself.
+To get this to work with Jetty, you need to enable annotations processing, as shown in this example code:
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java[]
+
+----
+
+===== Run it!
+
+After you have started things up you should be able to navigate to http://localhost:8080/jsp/ and click on any of the links to jsps.
+
+===== Maven Coordinates
+
+To use this example in your project, you will need the following Maven dependencies declared, in addition to those from the previous section:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<dependency>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-annotations</artifactId>
+  <version>${project.version}</version>
+</dependency>
+<dependency>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>apache-jsp</artifactId>
+  <version>${project.version}</version>
+</dependency>
+<dependency>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>apache-jstl</artifactId>
+  <version>${project.version}</version>
+</dependency>     
+----
+
+[[adding-embedded-examples]]
+==== Adding Examples
+
+If you would like to add an example to this list, fork the documentation project from github (see the blue bar at the bottom of this page) and add the new page.
+Feel free to add the example contents directly as a `[source.java]` and we will take it from there.
+
+If you feel and example is missing, feel free to open a bug to ask for it.
+No guarantees, but the more helpful and demonstrative it is the better.
diff --git a/jetty-documentation/src/main/asciidoc/development/embedding/embedding-jetty.adoc b/jetty-documentation/src/main/asciidoc/development/embedding/embedding-jetty.adoc
new file mode 100644
index 0000000..d9f60a8
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/embedding/embedding-jetty.adoc
@@ -0,0 +1,245 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[embedding-jetty]]
+=== Embedding Jetty
+
+Jetty has a slogan, "__Don't deploy your application in Jetty, deploy Jetty in your application!__"
+What this means is that as an alternative to bundling your application as a standard WAR to be deployed in Jetty, Jetty is designed to be a software component that can be instantiated and used in a Java program just like any POJO.
+Put another way, running Jetty in embedded mode means putting an HTTP module into your application, rather than putting your application into an HTTP server.
+
+This tutorial takes you step-by-step from the simplest Jetty server instantiation to running multiple web applications with standards-based deployment descriptors.
+The source for most of these examples is part of the standard Jetty project.
+
+==== Overview
+
+To embed a Jetty server the following steps are typical and are illustrated by the examples in this tutorial:
+
+1.  Create a link:{JDURL}/org/eclipse/jetty/server/Server.html[Server] instance.
+2.  Add/Configure link:{JDURL}/org/eclipse/jetty/server/Connector.html[Connectors].
+3.  Add/Configure link:{JDURL}/org/eclipse/jetty/server/Handler.html[Handlers] and/or link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandler.html[Contexts] and/or http://docs.oracle.com/javaee/6/api/javax/servlet/Servlet.html[Servlets].
+4.  Start the Server.
+5.  Wait on the server or do something else with your thread.
+
+==== Creating the Server
+
+The following code from SimplestServer.java instantiates and runs the simplest possible Jetty server:
+
+[source, java]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java[]
+----
+
+This runs an HTTP server on port 8080. It is not a very useful server as it has no handlers, and thus returns a 404 error for every request.
+
+==== Using Handlers
+
+To produce a response to a request, Jetty requires that you set a link:{JDURL}/org/eclipse/jetty/server/Handler.html[Handler] on the server.
+A handler may:
+
+* Examine/modify the HTTP request.
+* Generate the complete HTTP response.
+* Call another Handler (see link:{JDURL}/org/eclipse/jetty/server/handler/HandlerWrapper.html[`HandlerWrapper`]).
+* Select one or many Handlers to call (see link:{JDURL}/org/eclipse/jetty/server/handler/HandlerCollection.html[`HandlerCollection`]).
+
+===== HelloWorld Handler
+
+The following code based on HelloHandler.java shows a simple hello world handler:
+
+[source, java]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloHandler.java[]
+----
+
+The parameters passed to the handle method are:
+
+* `target` – the target of the request, which is either a URI or a name from a named dispatcher.
+* `baseRequest` – the Jetty mutable request object, which is always unwrapped.
+* `request` – the immutable request object, which may have been wrapped by a filter or servlet.
+* `response` – the response, which may have been wrapped by a filter or servlet.
+
+The handler sets the response status, content-type, and marks the request as handled before it generates the body of the response using a writer.
+
+===== Running HelloWorldHandler
+
+To allow a Handler to handle HTTP requests, you must add it to a Server instance.
+The following code from OneHandler.java shows how a Jetty server can use the HelloWorld handler:
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java[]
+----
+
+One or more handlers do all request handling in Jetty.
+Some handlers select other specific handlers (for example, a `ContextHandlerCollection` uses the context path to select a `ContextHandler`); others use application logic to generate a response (for example, the `ServletHandler` passes the request to an application Servlet), while others do tasks unrelated to generating the response (for example, `RequestLogHandler` or `StatisticsHandler`).
+
+Later sections describe how you can combine handlers like aspects.
+You can see some of the handlers available in Jetty in the link:{JXURL}/org/eclipse/jetty/server/handler/package-summary.html[org.eclipse.jetty.server.handler] package.
+
+===== Handler Collections and Wrappers
+
+Complex request handling is typically built from multiple Handlers that you can combine in various ways.
+Jetty has several implementations of the link:{JDURL}/org/eclipse/jetty/server/HandlerContainer.html[`HandlerContainer`] interface:
+
+link:{JDURL}/org/eclipse/jetty/server/handler/HandlerCollection.html[`HandlerCollection`]::
+  Holds a collection of other handlers and calls each handler in order.
+  This is useful for combining statistics and logging handlers with the handler that generates the response.
+link:{JDURL}/org/eclipse/jetty/server/handler/HandlerList.html[`HandlerList`]::
+  A Handler Collection that calls each handler in turn until either an exception is thrown, the response is committed or the `request.isHandled()` returns true.
+  You can use it to combine handlers that conditionally handle a request, such as calling multiple contexts until one matches a virtual host.
+link:{JDURL}/org/eclipse/jetty/server/handler/HandlerWrapper.html[`HandlerWrapper`]::
+  A Handler base class that you can use to daisy chain handlers together in the style of aspect-oriented programming.
+  For example, a standard web application is implemented by a chain of a context, session, security and servlet handlers.
+link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandlerCollection.html[`ContextHandlerCollection`]::
+  A specialized `HandlerCollection` that uses the longest prefix of the request URI (the `contextPath`) to select a contained `ContextHandler` to handle the request.
+
+===== Scoped Handlers
+
+Much of the standard Servlet container in Jetty is implemented with `HandlerWrappers` that daisy chain handlers together: `ContextHandler` to `SessionHandler` to `SecurityHandler` to `ServletHandler`.
+However, because of the nature of the servlet specification, this chaining cannot be a pure nesting of handlers as the outer handlers sometimes need information that the inner handlers process.
+For example, when a `ContextHandler` calls some application listeners to inform them of a request entering the context, it must already know which servlet the `ServletHandler` will dispatch the request to so that the `servletPath` method returns the correct value.
+
+The `HandlerWrapper` is specialized to the link:{JXURL}/org/eclipse/jetty/server/handler/ScopedHandler.html[`ScopedHandler`] abstract class, which supports a daisy chain of scopes.
+For example if a `ServletHandler` is nested within a `ContextHandler`, the order and nesting of execution of methods is:
+
+....
+Server.handle(...)
+  ContextHandler.doScope(...)
+    ServletHandler.doScope(...)
+      ContextHandler.doHandle(...)
+        ServletHandler.doHandle(...)
+          SomeServlet.service(...)
+....
+
+Thus when the `ContextHandler` handles the request, it does so within the scope the `ServletHandler` has established.
+
+===== Resource Handler
+
+The link:{JXURL}/org/eclipse/jetty/embedded/FileServer.html[FileServer example] shows how you can use a `ResourceHandler` to serve static content from the current working directory:
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java[]
+----
+
+Notice that a `HandlerList` is used with the `ResourceHandler` and a `DefaultHandler`, so that the `DefaultHandler` generates a good 404 response for any requests that do not match a static resource.
+
+==== Embedding Connectors
+
+In the previous examples, the Server instance is passed a port number and it internally creates a default instance of a Connector that listens for requests on that port.
+However, often when embedding Jetty it is desirable to explicitly instantiate and configure one or more Connectors for a Server instance.
+
+===== One Connector
+
+The following example, link:{JXURL}/org/eclipse/jetty/embedded/OneConnector.html[OneConnector.java],
+instantiates, configures, and adds a single HTTP connector instance to the server:
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java[]
+----
+
+In this example the connector handles the HTTP protocol, as that is the default for the link:{JXURL}/org/eclipse/jetty/server/ServerConnector.html[`ServerConnector`] class.
+
+===== Many Connectors
+
+When configuring multiple connectors (for example, HTTP and HTTPS), it may be desirable to share configuration of common parameters for HTTP.
+To achieve this you need to explicitly configure the `ServerConnector` class with `ConnectionFactory` instances, and provide them with common HTTP configuration.
+
+The link:{JXURL}/org/eclipse/jetty/embedded/ManyConnectors.html[ManyConnectors example], configures a server with two `ServerConnector` instances: the http connector has a link:{JXURL}/org/eclipse/jetty/server/HttpConnectionFactory.html[`HTTPConnectionFactory`] instance; the https connector has a `SslConnectionFactory` chained to a `HttpConnectionFactory`.
+Both `HttpConnectionFactory` are configured based on the same link:{JXURL}/org/eclipse/jetty/server/HttpConfiguration.html[`HttpConfiguration`] instance, however the HTTPS factory uses a wrapped configuration so that a link:{JXURL}/org/eclipse/jetty/server/SecureRequestCustomizer.html[`SecureRequestCustomizer`] can be added.
+
+==== Embedding Servlets
+
+http://en.wikipedia.org/wiki/Java_Servlet[Servlets] are the standard way to provide application logic that handles HTTP requests.
+Servlets are similar to a Jetty Handler except that the request object is not mutable and thus cannot be modified.
+Servlets are handled in Jetty by a link:{JXURL}/org/eclipse/jetty/embedded/MinimalServlets.html[`ServletHandler`].
+It uses standard path mappings to match a Servlet to a request; sets the requests `servletPath` and `pathInfo`; passes the request to the servlet, possibly via Filters to produce a response.
+
+The link:{JXURL}/org/eclipse/jetty/embedded/MinimalServlets.html[MinimalServlets example] creates a `ServletHandler` instance and configures a single HelloServlet:
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java[]
+----
+
+==== Embedding Contexts
+
+A link:{JXURL}/org/eclipse/jetty/embedded/OneContext.html[`ContextHandler`] is a `ScopedHandler` that responds only to requests that have a URI prefix that matches the configured context path.
+Requests that match the context path have their path methods updated accordingly and the contexts scope is available, which optionally may include:
+
+* A `Classloader` that is set as the Thread context `classloader` while request handling is in scope.
+* A set of attributes that is available via the http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContext.html[`ServletContext`] API.
+* A set of init parameters that is available via the http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContext.html[`ServletContext`] API.
+* A base Resource which is used as the document root for static resource requests via the http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContext.html[`ServletContext`] API.
+* A set of virtual host names.
+
+The following link:{JXURL}/org/eclipse/jetty/embedded/OneContext.html[OneContext example] shows a context being established that wraps the link:{JXURL}/org/eclipse/jetty/embedded/HelloHandler.html[HelloHandler]:
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java[]
+----
+
+When many contexts are present, you can embed a `ContextHandlerCollection` to efficiently examine a request URI to then select the matching `ContextHandler`(s) for the request.
+The link:{JXURL}/org/eclipse/jetty/embedded/ManyContexts.html[ManyContexts example] shows how many such contexts you can configure:
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java[]
+----
+
+==== Embedding ServletContexts
+
+A link:{JXURL}/org/eclipse/jetty/servlet/ServletContextHandler.html[`ServletContextHandler`] is a specialization of `ContextHandler` with support for standard sessions and Servlets.
+The following link:{JXURL}/org/eclipse/jetty/embedded/OneServletContext.html[OneServletContext example] instantiates a link:{JXURL}/org/eclipse/jetty/servlet/DefaultServlet.html[`DefaultServlet`] to server static content from /tmp/ and a `DumpServlet` that creates a session and dumps basic details about the request:
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java[]
+----
+
+==== Embedding Web Applications
+
+A link:{JXURL}/org/eclipse/jetty/webapp/WebAppContext.html[`WebAppContext`] is an extension of a `ServletContextHandler` that uses the http://en.wikipedia.org/wiki/WAR_%28Sun_file_format%29[standard layout] and web.xml to configure the servlets, filters and other features from a web.xml and/or annotations.
+The following link:{JXURL}/org/eclipse/jetty/embedded/OneWebApp.html[OneWebApp example] configures the Jetty test webapp.
+Web applications can use resources the container provides, and in this case a `LoginService` is needed and also configured:
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java[]
+----
+
+==== Like Jetty XML
+
+The typical way to configure an instance of the Jetty server is via `jetty.xml` and associated configuration files.
+However the Jetty XML configuration format is just a simple rendering of what you can do in code; it is very simple to write embedded code that does precisely what the jetty.xml configuration does.
+The link:{JXURL}/org/eclipse/jetty/embedded/LikeJettyXml.html[LikeJettyXml example] following renders in code the behavior obtained from the configuration files:
+
+* link:{GITBROWSEURL}/jetty-server/src/main/config/etc/jetty.xml[jetty.xml]
+* link:{GITBROWSEURL}/jetty-jmx/src/main/config/etc/jetty-jmx.xml[jetty-jmx.xml]
+* link:{GITBROWSEURL}/jetty-server/src/main/config/etc/jetty-http.xml[jetty-http.xml]
+* link:{GITBROWSEURL}/jetty-server/src/main/config/etc/jetty-https.xml[jetty-https.xml]
+* link:{GITBROWSEURL}/jetty-deploy/src/main/config/etc/jetty-deploy.xml[jetty-deploy.xml]
+* link:{GITBROWSEURL}/jetty-server/src/main/config/etc/jetty-stats.xml[jetty-stats.xml]
+* link:{GITBROWSEURL}/jetty-server/src/main/config/etc/jetty-requestlog.xml[jetty-requestlog.xml]
+* link:{GITBROWSEURL}/jetty-server/src/main/config/etc/jetty-lowresources.xml[jetty-lowresources.xml]
+* link:{GITBROWSEURL}/tests/test-webapps/test-jetty-webapp/src/main/config/etc/test-realm.xml[test-realm.xml]
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java[]
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-file-server.adoc b/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-file-server.adoc
new file mode 100644
index 0000000..0ab2e0a
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-file-server.adoc
@@ -0,0 +1,47 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[embedded-file-server]]
+==== Simple File Server
+
+This example shows how to create a simple file server in Jetty.
+It is perfectly suitable for test cases where you need an actual web server to obtain a file from, it could easily be configured to serve files from a directory under `src/test/resources`.
+Note that this does not have any logic for caching of files, either within the server or setting the appropriate headers on the response.
+It is simply a few lines that illustrate how easy it is to serve out some files.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java[]
+
+----
+
+===== Run it!
+
+After you have started things up you should be able to navigate to http://localhost:8080/index.html (assuming one is in the resource base directory) and you are good to go.
+
+===== Maven Coordinates
+
+To use this example in your project you will need the following Maven dependencies declared.
+
+[source, xml, subs="{sub-order}"]
+----
+<dependency>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-server</artifactId>
+  <version>${project.version}</version>
+</dependency>
+
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-many-connectors.adoc b/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-many-connectors.adoc
new file mode 100644
index 0000000..579b2a0
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-many-connectors.adoc
@@ -0,0 +1,54 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[embedded-many-connectors]]
+==== Multiple Connectors
+
+This example shows how to configure Jetty to use multiple connectors, specifically so it can process both http and https requests.
+Since the meat of this example is the server and connector configuration it only uses a simple HelloHandler but this example should be easily merged with other examples like those deploying servlets or webapps.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java[]
+
+----
+
+===== Walkthrough
+
+Start things up!
+By using the `server.join()` the server thread will join with the current thread.
+See link:http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#join()[`Thread.join()`] for more details.
+
+===== Maven Coordinates
+
+To use this example in your project you will need the following Maven dependencies declared.
+
+[source, xml, subs="{sub-order}"]
+----
+                
+<dependency>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-server</artifactId>
+  <version>${project.version}</version>
+</dependency>
+<dependency>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-security</artifactId>
+  <version>${project.version}</version>
+</dependency>
+
+            
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-minimal-servlet.adoc b/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-minimal-servlet.adoc
new file mode 100644
index 0000000..3b7ae42
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-minimal-servlet.adoc
@@ -0,0 +1,54 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[embedded-minimal-servlet]]
+==== Minimal Servlet
+
+This example shows the bare minimum required for deploying a servlet into Jetty.
+Note that this is strictly a servlet, not a servlet in the context of a web application, that example comes later.
+This is purely just a servlet deployed and mounted on a context and able to process requests.
+This example is excellent for situations where you have a simple servlet that you need to unit test, just mount it on a context and issue requests using your favorite http client library (like our Jetty client found in xref:http-client[]).
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java[]
+
+----
+
+===== Walkthrough
+
+Start things up! By using the `server.join()` the server thread will join with the current thread.
+See link:http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#join()[`Thread.join()`] for more details.
+
+It is really simple to create useful servlets for testing behaviors. Sometimes you need a http server to run a unit test against that will return test content and wiring up a servlet like this makes it trivial.
+
+After you have started things up you should be able to navigate to http://localhost:8080/ and you are good to go.
+
+===== Maven Coordinates
+
+To use this example in your project you will need the following Maven dependencies declared.
+
+[source, xml, subs="{sub-order}"]
+----
+                
+<dependency>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-servlet</artifactId>
+  <version>${project.version}</version>
+</dependency>
+
+            
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-one-webapp.adoc b/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-one-webapp.adoc
new file mode 100644
index 0000000..28259bf
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-one-webapp.adoc
@@ -0,0 +1,48 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[embedded-one-webapp]]
+==== Web Application
+
+This example shows how to deploy a simple webapp with an embedded instance of Jetty.
+This is useful when you want to manage the lifecycle of a server programmatically, either within a production application or as a simple way to deploying and debugging a full scale application deployment.
+In many ways it is easier then traditional deployment since you control the classpath yourself, making this easy to wire up in a test case in Maven  and issue requests using your favorite http client library (like our Jetty client found in xref:http-client[]).
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java[]
+
+----
+
+===== Run it!
+
+After you have started things up you should be able to navigate to http://localhost:8080/ and you are good to go.
+
+===== Maven Coordinates
+
+To use this example in your project you will need the following Maven dependencies declared.
+
+[source, xml, subs="{sub-order}"]
+----
+
+<dependency>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-webapp</artifactId>
+  <version>${project.version}</version>
+</dependency>
+
+      
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-secured-hello-handler.adoc b/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-secured-hello-handler.adoc
new file mode 100644
index 0000000..1a4ec85
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-secured-hello-handler.adoc
@@ -0,0 +1,57 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[embedded-secured-hello-handler]]
+==== Secured Hello Handler
+
+This example shows how to wrap one handler with another one that handles security.
+We have a simple Hello Handler that just return a greeting but add on the restriction that to get this greeting you must authenticate.
+Another thing to remember is that this example uses the `ConstraintSecurityHandler` which is what supports the security mappings inside of the servlet api, it could be easier to show just the `SecurityHandler` usage, but the constraint provides more configuration power.
+If you don't need that you can drop the Constraint bits and use just the `SecurityHandler`.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java[]
+
+----
+
+===== Run it!
+
+After you have started things up you should be able to navigate to http://localhost:8080/index.html (assuming one is in the resource base directory) and you are good to go.
+
+===== The Realm Properties File
+
+[source,properties]
+----
+include::{SRCDIR}/examples/embedded/src/test/resources/realm.properties[]
+      
+----
+
+===== Maven Coordinates
+
+To use this example in your project you will need the following Maven dependencies declared.
+
+[source, xml, subs="{sub-order}"]
+----
+
+<dependency>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-server</artifactId>
+  <version>${project.version}</version>
+</dependency>
+
+      
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-split-file-server.adoc b/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-split-file-server.adoc
new file mode 100644
index 0000000..aa64ad6
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/embedding/examples/embedded-split-file-server.adoc
@@ -0,0 +1,54 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[embedded-split-file-server]]
+==== Split File Server
+
+This example builds on the link:#emebedded-file-server[Simple File Server] to show how chaining multiple `ResourceHandlers` together can let you aggregate multiple directories to serve content on a single path and how you can link these together with `ContextHandlers`.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java[]
+      
+    
+----
+
+===== Run it!
+
+After you have started things up you should be able to navigate to http://localhost:8090/index.html (assuming one is in the resource base directory) and you are good to go.
+Any requests for files will be looked for in the first resource handler, then the second, and so on and so forth.
+
+===== Maven Coordinates
+
+To use this example as is in your project you will need the following maven dependencies declared.
+We would recommend not using the toolchain dependency in your actual application.
+
+[source, xml, subs="{sub-order}"]
+----
+
+<dependency>  
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-server</artifactId>
+  <version>${project.version}</version>
+</dependency>
+<dependency>
+  <groupId>org.eclipse.jetty.toolchain</groupId>
+  <artifactId>jetty-test-helper</artifactId>
+  <version>2.2</version>
+</dependency>
+
+      
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/embedding/jetty-helloworld.adoc b/jetty-documentation/src/main/asciidoc/development/embedding/jetty-helloworld.adoc
new file mode 100644
index 0000000..89699ef
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/embedding/jetty-helloworld.adoc
@@ -0,0 +1,86 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-helloworld]]
+=== Jetty Embedded HelloWorld
+
+This section provides a tutorial that shows how you can quickly develop embedded code against the Jetty API.
+
+[[downloading-jars]]
+==== Downloading the Jars
+
+Jetty is decomposed into many jars and dependencies to achieve a minimal footprint by selecting the minimal set of jars.
+Typically it is best to use something like Maven to manage jars, however this tutorial uses an aggregate Jar that contains all of the Jetty classes in one Jar.
+You can manually download the aggregate link:http://central.maven.org/maven2/org/eclipse/jetty/aggregate/jetty-all/{VERSION}/jetty-all-{VERSION}-uber.jar[`jetty-all.jar`] using `curl`) or a browser.
+
+____
+[NOTE]
+The central Maven repository has started to aggressively reject/deny access to the repository from the `wget` command line tool (due to abusive use of the tool by some groups).
+The administrators of the central maven repository have stated that the recommended command line download tool is now curl.
+____
+
+Use curl as follows:
+
+[source, screen, subs="{sub-order}"]
+....
+> mkdir Demo
+> cd Demo
+> curl -o jetty-all-uber.jar http://central.maven.org/maven2/org/eclipse/jetty/aggregate/jetty-all/{VERSION}/jetty-all-{VERSION}-uber.jar
+....
+
+[[writing-helloworld-example]]
+==== Writing a HelloWorld Example
+
+The link:#embedding[Embedding Jetty] section contains many examples of writing against the Jetty API.
+This tutorial uses a simple HelloWorld handler with a main method to run the server.
+You can either link:{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java[download] or create in an editor the file `HelloWorld.java` with the following content:
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java[]
+----
+
+[[compiling-helloworld-example]]
+==== Compiling the HelloWord example
+
+The following command compiles the HelloWorld class:
+
+[source, screen, subs="{sub-order}"]
+....
+> mkdir classes
+> javac -d classes -cp jetty-all-uber.jar HelloWorld.java
+....
+
+[[running-handler-and-server]]
+==== Running the Handler and Server
+
+The following command runs the HelloWorld example:
+
+[source, screen, subs="{sub-order}"]
+....
+> java -cp classes:jetty-all-uber.jar org.eclipse.jetty.embedded.HelloWorld
+....
+
+You can now point your browser at http://localhost:8080/[http://localhost:8080] to see your hello world page.
+
+[[next-steps]]
+==== Next Steps
+
+To learn more about Jetty, take these next steps:
+
+* Follow the examples in link:#embedding-jetty[Embedding Jetty] to better understand the jetty APIs.
+* Explore the complete link:{JDURL}/[Jetty javadoc]
+* Consider using link:#maven-and-jetty[Jetty and Maven] to manage your Jars and dependencies.
diff --git a/jetty-documentation/src/main/asciidoc/development/frameworks/chapter.adoc b/jetty-documentation/src/main/asciidoc/development/frameworks/chapter.adoc
new file mode 100644
index 0000000..e30caa1
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/frameworks/chapter.adoc
@@ -0,0 +1,23 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[frameworks]]
+== Frameworks
+
+include::spring-usage.adoc[]
+include::osgi.adoc[]
+include::weld.adoc[]
+include::metro.adoc[]
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/development/frameworks/metro.adoc b/jetty-documentation/src/main/asciidoc/development/frameworks/metro.adoc
new file mode 100644
index 0000000..f24efc3
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/frameworks/metro.adoc
@@ -0,0 +1,60 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[framework-metro]]
+=== Metro
+
+https://metro.java.net/[Metro] is the reference implementation for http://jcp.org/en/jsr/detail?id=109[web services].
+You can easily use Metro with Jetty to integrate web services with your web applications.
+
+[[metro-setup-distro]]
+==== Metro Setup
+
+1.  https://metro.java.net/latest/download.html[Download] the Metro distribution and unpack it to your disk.
+We'll refer to the unpacked location as `$metro.home`.
+2.  Create the directory `$jetty.home/lib/metro`
+3.  Copy the jars from $metro.home/lib to `$jetty.home/lib/metro`
+4.  Edit the start.ini file and add an OPTION line for metro near the end.
++
+[source, plain, subs="{sub-order}"]
+----
+OPTIONS=metro
+----
+
+That's all the setup you need to do to integrate Jetty and Metro.
+
+Now read the https://metro.java.net/discover/[Metro documentation] on https://metro.java.net/getting-started/[how to create web services].
+The Metro distribution you downloaded should also contain several example web applications in the $metro.home/samples directory that you can build and deploy to Jetty (simply by copying the war file produced by the build).
+
+Here's an example of the log output from Jetty when one of the sample Metro wars (from `$metro.home/samples/async`) is deployed to Jetty:
+
+[source, screen, subs="{sub-order}"]
+....
+[2093] java -jar start.jar
+
+2013-07-26 15:47:53.480:INFO:oejs.Server:main: jetty-9.0.4.v20130625
+2013-07-26 15:47:53.549:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:/home/user/jetty-distribution-{VERSION}/webapps/] at interval 1
+Jul 26, 2013 3:47:53 PM com.sun.xml.ws.transport.http.servlet.WSServletContextListener contextInitialized
+INFO: WSSERVLET12: JAX-WS context listener initializing
+Jul 26, 2013 3:47:56 PM com.sun.xml.ws.server.MonitorBase createRoot
+INFO: Metro monitoring rootname successfully set to: com.sun.metro:pp=/,type=WSEndpoint,name=/metro-async-AddNumbersService-AddNumbersImplPort
+Jul 26, 2013 3:47:56 PM com.sun.xml.ws.transport.http.servlet.WSServletDelegate <init>
+INFO: WSSERVLET14: JAX-WS servlet initializing
+2013-07-26 15:47:56.800:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@75707c77{/metro-async,file:/tmp/jetty-0.0.0.0-8080-metro-async.war-_metro-async-any-/webapp/,AVAILABLE}{/metro-async.war}
+2013-07-26 15:47:56.853:INFO:oejs.ServerConnector:main: Started ServerConnector@47dce809{HTTP/1.1}{0.0.0.0:8080}
+
+        
+....
diff --git a/jetty-documentation/src/main/asciidoc/development/frameworks/osgi.adoc b/jetty-documentation/src/main/asciidoc/development/frameworks/osgi.adoc
new file mode 100644
index 0000000..08fb3e1
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/frameworks/osgi.adoc
@@ -0,0 +1,1428 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[framework-jetty-osgi]]
+=== OSGI
+
+==== Introduction
+
+The Jetty OSGi infrastructure provides a Jetty container inside an OSGi
+container. Traditional JavaEE webapps can be deployed, in addition to
+Jetty ContextHandlers, along with OSGi web bundles. In addition, the
+infrastructure also supports the OSGi HttpService interface.
+
+==== General Setup
+
+All of the Jetty jars contain manifest entries appropriate to ensure
+that they can be deployed into an OSGi container as bundles. You will
+need to install some jetty jars into your OSGi container. You can always
+find the jetty jars either in the maven central repository, or you can
+download a distribution of jetty. Here's the absolute minimal set of
+jetty jars:
+
+.Bundle Name Mapping
+[cols=",",options="header",]
+|===================================================
+|Jar |Bundle Symbolic Name
+|jetty-util |org.eclipse.jetty.util
+|jetty-http |org.eclipse.jetty.http
+|jetty-io |org.eclipse.jetty.io
+|jetty-security |org.eclipse.jetty.security
+|jetty-server |org.eclipse.jetty.server
+|jetty-servlet |org.eclipse.jetty.servlet
+|jetty-webapp |org.eclipse.jetty.webapp
+|jetty-deploy |org.eclipse.jetty.deploy
+|jetty-xml |org.eclipse.jetty.xml
+|jetty-osgi-servlet-api |org.eclipse.jetty.toolchain
+|===================================================
+
+____
+[NOTE]
+
+We recommend that you also deploy the
+link:#osgi-annotations[annotation-related] jars also, as increasingly
+the Servlet Specification relies on annotations for functionality.
+____
+
+You will also need the**OSGi Event Management service** and the **OSGi
+Configuration Management service**. If your OSGi container does not
+automatically make these available, you will need to add them in a way
+appropriate to your container.
+
+==== The Jetty OSGi Container
+
+===== The jetty-osgi-boot jar
+
+Now that you have the basic set of Jetty jars installed, you can install
+the
+http://central.maven.org/maven2/org/eclipse/jetty/osgi/jetty-osgi-boot/[jetty-osgi-boot.jar]
+bundle, downloadable from the maven central repo
+http://central.maven.org/maven2/org/eclipse/jetty/osgi/jetty-osgi-boot/[here.]
+
+This bundle will instantiate and make available the Jetty OSGi container
+when it is started. If this bundle is not auto-started upon installation
+into your OSGi container, you should start it manually using a command
+appropriate for your container.
+
+[[customize-jetty-container]]
+===== Customizing the Jetty Container
+
+Before going ahead with the install, you may want to customize the Jetty
+container. In general this is done by a combination of System properties
+and the usual jetty xml configuration files. The way you define the
+System properties will depend on which OSGi container you are using, so
+ensure that you are familiar with how to set them for your environment.
+In the following examples, we will assume that the OSGi container allows
+us to set System properties as simple name=value pairs.
+
+The available System properties are:
+
+jetty.http.port::
+  If not specified, this defaults to the usual jetty port of 8080.
+jetty.home::
+  Either this property _or_ the *jetty.home.bundle* _must_ be specified.
+  This property should point to a file system location that has an
+  `etc/` directory containing xml files to configure the Jetty container
+  on startup. For example:
+  +
+[source, plain, subs="{sub-order}"]
+----
+jetty.home=/opt/custom/jetty
+              
+----
+  +
+  Where `/opt/custom/jetty` contains:
+  +
+[source, plain, subs="{sub-order}"]
+----
+
+etc/jetty.xml
+etc/jetty-selector.xml
+etc/jetty-deployer.xml
+etc/jetty-special.xml
+
+              
+----
+jetty.home.bundle::
+  Either this property _or_ the *jetty.home* property must be specified.
+  This property should specify the symbolic name of a bundle which
+  contains a directory called `jettyhome/`. The`
+              jettyhome/` directory should have a subdirectory called
+  `etc/` that contains the xml files to be applied to Jetty on startup.
+  The jetty-osgi-boot.jar contains a`
+              jettyhome/` directory with a default set of xml
+  configuration files. Here's how you would specify it:
+  +
+[source, plain, subs="{sub-order}"]
+----
+jetty.home.bundle=org.eclipse.jetty.osgi.boot
+----
+  +
+  Here's a partial listing of that jar that shows you the names of the
+  xml files contained within it:
+  +
+[source, plain, subs="{sub-order}"]
+----
+META-INF/MANIFEST.MF
+jettyhome/etc/jetty.xml
+jettyhome/etc/jetty-deployer.xml
+jettyhome/etc/jetty-http.xml
+----
+jetty.etc.config.urls::
+  This specifies the paths of the xml files that are to be used. If not
+  specified, they default to:
+  +
+[source, plain, subs="{sub-order}"]
+----
+etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml
+----
+  +
+  Note that the paths can either be relative or absolute, or a mixture.
+  If the path is relative, it is resolved against either *jetty.home* or
+  **jetty.home.bundle**, whichever was specified. You can use this
+  ability to mix and match jetty configuration files to add
+  functionality, such as adding in a https connector. Here's an example
+  of adding a https connector, using the relevant files from the
+  jetty-distribution:
+  +
+....
+etc/jetty.xml, etc/jetty-http.xml, /opt/jetty/etc/jetty-ssl.xml, /opt/jetty/etc/jetty-https.xml, etc/jetty-deployer.xml
+....
+  +
+  Note that regardless of whether you set the *jetty.home* or
+  *jetty.home.bundle* property, when Jetty executes the configuration
+  files, it will set an appropriate value for *jetty.home* so that
+  references in xml files to <property name="jetty.home"> will work. Be
+  careful, however, if you are mixing and matching relative and absolute
+  configuration file paths: the value of *jetty.home* is determined from
+  the resolved location of the _relative_ files only.
+
+===== The Jetty Container as an OSGi Service
+
+You can now go ahead and deploy the jetty-osgi-boot.jar into your OSGi
+container. A Jetty Server instance will be created, the xml config files
+applied to it, and then published as an OSGi service. Normally, you will
+not need to interact with this service instance, however you can
+retrieve a reference to it using the usual OSGi API:
+
+[source, java, subs="{sub-order}"]
+----
+
+org.osgi.framework.BundleContext bc;
+org.osgi.framework.ServiceReference ref = bc.getServiceReference("org.eclipse.jetty.server.Server");
+
+        
+----
+
+The Server service has a couple of properties associated with it that
+you can retrieve using the
+org.osgi.framework.ServiceReference.getProperty(String) method:
+
+managedServerName::
+  The Jetty Server instance created by the jetty-osgi-boot.jar will be
+  called "defaultJettyServer"
+jetty.etc.config.urls::
+  The list of xml files resolved from either *jetty.home*
+  or**jetty.home.bundle**/jettyhome
+
+===== Adding More Jetty Servers
+
+As we have seen in the previous section, the jetty-osgi-boot code will
+create an org.eclipse.jetty.server.Server instance, apply the xml
+configuration files specified by *jetty.etc.config.urls* System property
+to it, and then register it as an OSGi Service. The name associated with
+this default instance is "defaultJettyServer".
+
+You can create other Server instances, register them as OSGi Services,
+and the jetty-osgi-boot code will notice them, and configure them so
+that they can deploy ContextHandlers and webapp bundles. When you deploy
+webapps or ContextHandlers as bundles or Services (see sections below)
+you can target them to be deployed to a particular server instance via
+the Server's name.
+
+Here's an example of how to create a new Server instance and register it
+with OSGi so that the jetty-osgi-boot code will find it and configure it
+so it can be a deployment target:
+
+[source, java, subs="{sub-order}"]
+----
+public class Activator implements BundleActivator
+{
+
+    public void start(BundleContext context) throws Exception
+    {
+        
+        Server server = new Server();
+        //do any setup on Server in here
+        String serverName = "fooServer";
+        Dictionary serverProps = new Hashtable();
+        //define the unique name of the server instance
+        serverProps.put("managedServerName", serverName);
+        serverProps.put("jetty.http.port", "9999");
+        //let Jetty apply some configuration files to the Server instance
+        serverProps.put("jetty.etc.config.urls", "file:/opt/jetty/etc/jetty.xml,file:/opt/jetty/etc/jetty-selector.xml,file:/opt/jetty/etc/jetty-deployer.xml");
+        //register as an OSGi Service for Jetty to find 
+        context.registerService(Server.class.getName(), server, serverProps);
+       
+    }
+}
+----
+
+Now that we have created a new Server called "fooServer", we can deploy
+webapps and ContextHandlers as Bundles or Services to it (see below for
+more information on this). Here's an example of deploying a webapp as a
+Service and targetting it to the "fooServer" Server we created above:
+
+[source, java, subs="{sub-order}"]
+----
+public class Activator implements BundleActivator
+{
+
+    public void start(BundleContext context) throws Exception
+    {
+        
+        //Create a webapp context as a Service and target it at the "fooServer" Server instance
+        WebAppContext webapp = new WebAppContext();
+        Dictionary props = new Hashtable();
+        props.put("war",".");
+        props.put("contextPath","/acme");
+        props.put("managedServerName", "fooServer");
+        context.registerService(ContextHandler.class.getName(),webapp,props);
+    }
+}
+----
+
+==== Deploying Bundles as Webapps
+
+The Jetty OSGi container listens for the installation of bundles, and
+will automatically attempt to deploy any that appear to be webapps.
+
+Any of the following criteria are sufficient for Jetty to deploy the
+bundle as a webapp:
+
+Bundle contains a WEB-INF/web.xml file::
+  If the bundle contains a web descriptor, then it is automatically
+  deployed. This is an easy way to deploy classic JavaEE webapps.
+Bundle MANIFEST contains Jetty-WarFolderPath (for releases prior to
+jetty-9.3) or Jetty-WarResourcePath::
+  This is the location within the bundle of the webapp resources.
+  Typically this would be used if the bundle is not a pure webapp, but
+  rather the webapp is a component of the bundle. Here's an example of a
+  bundle where the resources of the webapp are not located at the root
+  of the bundle, but rather inside the subdirectory `web/` :
+  +
+  `MANIFEST`:
+  +
+[source, plain, subs="{sub-order}"]
+----
+
+Bundle-Name: Web
+Jetty-WarResourcePath: web
+Import-Package: javax.servlet;version="3.1",
+ javax.servlet.resources;version="3.1"
+Bundle-SymbolicName: com.acme.sample.web
+
+            
+----
+  +
+  Bundle contents:
+  +
+[source, plain, subs="{sub-order}"]
+----
+
+META-INF/MANIFEST.MF
+web/index.html
+web/foo.html
+web/WEB-INF/web.xml
+com/acme/sample/web/MyStuff.class
+com/acme/sample/web/MyOtherStuff.class
+
+            
+----
+Bundle MANIFEST contains Web-ContextPath::
+  This header can be used in conjunction with either of the two
+  preceding headers to control the context path to which the webapp is
+  deployed, or alone to identify that the bundle's contents should be
+  published as a webapp. This header is part of the RFC-66 specification
+  for using webapps with OSGi. Here's an eample based on the previous
+  one where we use the Web-ContextPath header to set its deployment
+  context path to be "/sample" :
+  +
+  `MANIFEST`:
+  +
+[source, plain, subs="{sub-order}"]
+----
+
+Bundle-Name: Web
+Jetty-WarResourcePath: web
+Web-ContextPath: /sample
+Import-Package: javax.servlet;version="3.1",
+ javax.servlet.resources;version="3.1"
+Bundle-SymbolicName: com.acme.sample.web
+
+            
+----
+
+You can also define extra headers in your bundle MANIFEST that help
+customize the web app to be deployed:
+
+Jetty-defaultWebXmlFilePath::
+  The location of a webdefault.xml file to apply to the webapp. The
+  location can be either absolute (either absolute path or file: url),
+  or relative (in which case it is interpreted as relative to the bundle
+  root). Defaults to the webdefault.xml file built into the Jetty OSGi
+  container.
+Jetty-WebXmlFilePath::
+  The location of the web.xml file. The location can be either absolute
+  (either absolute path or file: url), or relative (in which case it is
+  interpreted as relative to the bundle root). Defaults to
+  WEB-INF/web.xml
+Jetty-extraClassPath::
+  A classpath of additional items to add to the webapp's classloader.
+Jetty-bundleInstall::
+  The path to the base folder that overrides the computed bundle
+  installation - mostly useful for those OSGi frameworks that unpack
+  bundles by default.
+Require-TldBundle::
+  A comma separated list of bundle symbolic names of bundles containing
+  TLDs that this webapp depends upon.
+managedServerName::
+  The name of the Server instance to which to deploy this webapp bundle.
+  If not specified, defaults to the default Server instance called
+  "defaultJettyServer".
+Jetty-WarFragmentResourcePath::
+  The path within a fragment hosted by the web-bundle that contains
+  static resources for the webapp. The path is appended to the base
+  resource for the webapp (see Jetty-WarResourcePath).
+Jetty-WarPrependFragmentResourcePath::
+  The path within a fragment hosted by the web-bundle that contains
+  static resources for the webapp.The path is prepended to the base
+  resource for the webapp (see Jetty-WarResourcePath).
+Jetty-ContextFilePath::
+  A comma separated list of paths within the webapp bundle to Jetty
+  context files that will be applied to the webapp. Alternatively you
+  may include a single Jetty context file called
+  "jetty-webapp-context.xml" in the webapp bundle's META-INF directory
+  and it will be automatically applied to the webapp.
+
+===== Determining the Context Path for a Webapp Bundle
+
+As we have seen in the previous section, if the bundle `MANIFEST`
+contains the RFC-66 header **Web-ContextPath**, Jetty will use that as
+the context path. If the MANIFEST does not contain that header, then
+Jetty will concoct a context path based on the last element of the
+bundle's location (by calling Bundle.getLocation()) after stripping off
+any file extensions.
+
+For example, suppose we have a bundle whose location is:
+
+[source, plain, subs="{sub-order}"]
+----
+file://some/where/over/the/rainbow/oz.war
+----
+
+The corresponding synthesized context path would be:
+
+[source, plain, subs="{sub-order}"]
+----
+/oz
+----
+
+===== Extra Properties Available for Webapp Bundles
+
+You can further customize your webapp by including a jetty context xml
+file that is applied to the webapp. This xml file must be placed in
+`META-INF` of the bundle, and must be called `jetty-webapp-context.xml`.
+
+Here's an example of a webapp bundle listing containing such a file:
+
+[source, plain, subs="{sub-order}"]
+----
+
+META-INF/MANIFEST.MF
+META-INF/jetty-webapp-context.xml
+web/index.html
+web/foo.html
+web/WEB-INF/web.xml
+com/acme/sample/web/MyStuff.class
+com/acme/sample/web/MyOtherStuff.class
+
+            
+----
+
+Here's an example of the contents of a META-INF/jetty-webapp-context.xml
+file:
+
+....
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="defaultsDescriptor"><Property name="bundle.root"/>META-INF/webdefault.xml</Set>
+</Configure>
+....
+
+As you can see, it is a normal context xml file used to set up a webapp.
+There are, however, some additional useful properties that can be
+referenced
+
+Server::
+  This is a reference to the Jetty org.eclipse.jetty.server.Server
+  instance to which the webapp being configured in the context xml file
+  will be deployed.
+bundle.root::
+  This is a reference to the org.eclipse.jetty.util.resource.Resource
+  that represents the location of the Bundle. Note that this could be
+  either a directory in the file system if the OSGi container
+  automatically unpacks bundles, or it may be a jar:file: url if the
+  bundle remains packed.
+
+==== Deploying Bundles as Jetty ContextHandlers
+
+In addition to deploying webapps, the Jetty OSGi container listens for
+the installation of bundles that are not heavyweight webapps, but rather
+use the flexible Jetty-specific concept of ContextHandlers.
+
+The following is the criteria used to decide if a bundle can be deployed
+as a ContextHandler:
+
+Bundle MANIFEST contains Jetty-ContextFilePath::
+  A comma separated list of names of context files - each one of which
+  represents a ContextHandler that should be deployed by Jetty. The
+  context files can be inside the bundle, external to the bundle
+  somewhere on the file system, or external to the bundle in the
+  *jetty.home* directory.
+  +
+  A context file that is inside the bundle:
+  +
+[source, plain, subs="{sub-order}"]
+----
+Jetty-ContextFilePath: ./a/b/c/d/foo.xml
+----
+  +
+  A context file that is on the file system:
+  +
+[source, plain, subs="{sub-order}"]
+----
+Jetty-ContextFilePath: /opt/app/contexts/foo.xml
+----
+  +
+  A context file that is relative to jetty.home:
+  +
+[source, plain, subs="{sub-order}"]
+----
+Jetty-ContextFilePath: contexts/foo.xml
+----
+  +
+  A number of different context files:
+  +
+[source, plain, subs="{sub-order}"]
+----
+Jetty-ContextFilePath: ./a/b/c/d/foo.xml,/opt/app/contexts/foo.xml,contexts/foo.xml
+----
+
+Other MANIFEST properties that can be used to configure the deployment
+of the ContextHandler:
+
+managedServerName::
+  The name of the Server instance to which to deploy this webapp bundle.
+  If not specified, defaults to the default Server instance called
+  "defaultJettyServer".
+
+===== Determining the Context Path for a ContextHandler Bundle
+
+Usually, the context path for the ContextHandler will be set by the
+context xml file. However, you can override any path set in the context
+xml file by using the *Web-ContextPath* header in the `MANIFEST`.
+
+===== Extra Properties Available for Context Xml Files
+
+Before the Jetty OSGi container applies a context xml file found in a
+Jetty-ContextFilePath MANIFEST header, it sets a few useful properties
+that can be referred to within the xml file:
+
+Server::
+  This is a reference to the Jetty org.eclipse.jetty.server.Server
+  instance to which the ContextHandler being configured in the context
+  xml file will be deployed.
+bundle.root::
+  This is a reference to the org.eclipse.jetty.util.resource.Resource
+  that represents the location of the Bundle (obtained by calling
+  Bundle.getLocation()). Note that this could be either a directory in
+  the file system if the OSGi container automatically unpacks bundles,
+  or it may be a jar:file: url if the bundle remains packed.
+
+Here's an example of a context xml file that makes use of these
+properties:
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-osgi/test-jetty-osgi-context/src/main/context/acme.xml[]
+----
+
+[[services-as-webapps]]
+==== Deploying Services as Webapps
+
+In addition to listening for bundles whose format or `MANIFEST` entries
+define a webapp or ContextHandler for to be deployed, the Jetty OSGi
+container also listens for the registration of OSGi services that are
+instances of org.eclipse.jetty.webapp.WebAppContext. So you may
+programmatically create a WebAppContext, register it as a service, and
+have Jetty pick it up and deploy it.
+
+Here's an example of doing that with a simple bundle that serves static
+content, and an org.osgi.framework.BundleActivator that instantiates the
+WebAppContext:
+
+The bundle contents:
+
+[source, plain, subs="{sub-order}"]
+----
+
+META-INF/MANIFEST.MF
+index.html
+com/acme/osgi/Activator.class
+
+      
+----
+
+The `MANIFEST.MF`:
+
+[source, plain, subs="{sub-order}"]
+----
+
+Bundle-Classpath: .
+Bundle-Name: Jetty OSGi Test WebApp
+DynamicImport-Package: org.eclipse.jetty.*;version="[9.0,10.0)"
+Bundle-Activator: com.acme.osgi.Activator
+Import-Package: org.eclipse.jetty.server.handler;version="[9.0,10)",
+ org.eclipse.jetty.webapp;version="[9.0,10)",
+ org.osgi.framework;version= "[1.5,2)",
+ org.osgi.service.cm;version="1.2.0",
+ org.osgi.service.packag eadmin;version="[1.2,2)",
+ org.osgi.service.startlevel;version="1.0.0",
+ org.osgi.service.url;version="1.0.0",
+ org.osgi.util.tracker;version= "1.3.0",
+ org.xml.sax,org.xml.sax.helpers
+Bundle-SymbolicName: com.acme.testwebapp
+
+      
+----
+
+The Activator code:
+
+[source, java, subs="{sub-order}"]
+----
+
+public void start(BundleContext context) throws Exception
+{
+    WebAppContext webapp = new WebAppContext();
+    Dictionary props = new Hashtable();
+    props.put("Jetty-WarResourcePath",".");
+    props.put("contextPath","/acme");
+    context.registerService(ContextHandler.class.getName(),webapp,props);
+}
+
+      
+----
+
+The above setup is sufficient for Jetty to recognize and deploy the
+WebAppContext at /acme.
+
+As the example shows, you can use OSGi Service properties in order to
+communicate extra configuration information to Jetty:
+
+Jetty-WarFolderPath (for releases prior to 9.3) or
+Jetty-WarResourcePath::
+  The location within the bundle of the root of the static resources for
+  the webapp
+Web-ContextPath::
+  The context path at which to deploy the webapp.
+Jetty-defaultWebXmlFilePath::
+  The location within the bundle of a webdefault.xml file to apply to
+  the webapp. Defaults to that of the Jetty OSGi container.
+Jetty-WebXmlFilePath::
+  The location within the bundle of the web.xml file. Defaults to
+  WEB-INF/web.xml
+Jetty-extraClassPath::
+  A classpath of additional items to add to the webapp's classloader.
+Jetty-bundleInstall::
+  The path to the base folder that overrides the computed bundle
+  installation - mostly useful for those OSGi frameworks that unpack
+  bundles by default.
+Require-TldBundle::
+  A comma separated list of bundle symbolic names of bundles containing
+  TLDs that this webapp depends upon.
+managedServerName::
+  The name of the Server instance to which to deploy this webapp. If not
+  specified, defaults to the default Server instance called
+  "defaultJettyServer".
+Jetty-WarFragmentResourcePath::
+  The path within a fragment hosted by the web-bundle that contains
+  static resources for the webapp. The path is appended to the base
+  resource for the webapp (see Jetty-WarResourcePath).
+Jetty-WarPrependFragmentResourcePath::
+  The path within a fragment hosted by the web-bundle that contains
+  static resources for the webapp.The path is prepended to the base
+  resource for the webapp (see Jetty-WarResourcePath).
+
+==== Deploying Services as ContextHandlers
+
+Similarly to WebAppContexts, the Jetty OSGi container can detect the
+registration of an OSGi Service that represents a ContextHandler and
+ensure that it is deployed. The ContextHandler can either be fully
+configured before it is registered as an OSGi service - in which case
+the Jetty OSGi container will merely deploy it - or the ContextHandler
+can be partially configured, with the Jetty OSGi container completing
+the configuration via a context xml file and properties associated with
+the Service.
+
+Here's an example of doing that with a simple bundle that serves static
+content with an org.osgi.framework.BundleActivator that instantiates a
+ContextHandler and registers it as an OSGi Service, passing in
+properties that define a context xml file and context path for Jetty to
+apply upon deployment:
+
+The bundle contents:
+
+[source, plain, subs="{sub-order}"]
+----
+
+META-INF/MANIFEST.MF
+static/index.html
+acme.xml
+com/acme/osgi/Activator.class
+com/acme/osgi/Activator$1.class
+
+      
+----
+
+The `MANIFEST`:
+
+[source, plain, subs="{sub-order}"]
+----
+
+Bundle-Classpath: .
+Bundle-Name: Jetty OSGi Test Context
+DynamicImport-Package: org.eclipse.jetty.*;version="[9.0,10.0)"
+Bundle-Activator: com.acme.osgi.Activator
+Import-Package: javax.servlet;version="2.6.0",
+ javax.servlet.resources;version="2.6.0",
+ org.eclipse.jetty.server.handler;version="[9.0,10)",
+ org.osgi.framework;version="[1.5,2)",
+ org.osgi.service.cm;version="1.2.0",
+ org.osgi.service.packageadmin;version="[1.2,2)",
+ org.osgi.service.startlevel;version="1.0.0.o",
+ org.osgi.service.url;version="1.0.0",
+ org.osgi.util.tracker;version="1.3.0",
+ org.xml.sax,org.xml.sax.helpers
+Bundle-SymbolicName: com.acme.testcontext
+
+      
+----
+
+The Activator code:
+
+[source, java, subs="{sub-order}"]
+----
+
+public void start(final BundleContext context) throws Exception
+{
+    ContextHandler ch = new ContextHandler();
+    ch.addEventListener(new ServletContextListener () {
+
+            @Override
+            public void contextInitialized(ServletContextEvent sce)
+            {
+               System.err.println("Context is initialized");
+            }
+
+            @Override
+            public void contextDestroyed(ServletContextEvent sce)
+            {
+                System.err.println("Context is destroyed!");                
+            }
+            
+    });
+    Dictionary props = new Hashtable();
+    props.put("Web-ContextPath","/acme");
+    props.put("Jetty-ContextFilePath", "acme.xml");
+    context.registerService(ContextHandler.class.getName(),ch,props);
+}
+
+      
+----
+
+The contents of the `acme.xml` context file:
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-osgi/test-jetty-osgi-context/src/main/context/acme.xml[]
+----
+
+You may also use the following OSGi Service properties:
+
+managedServerName::
+  The name of the Server instance to which to deploy this webapp. If not
+  specified, defaults to the default Server instance called
+  "defaultJettyServer".
+
+===== Extra Properties Available for Context Xml Files
+
+Before the Jetty OSGi container applies a context xml file found in a
+Jetty-ContextFilePath property, it sets a few useful properties that can
+be referred to within the xml file:
+
+Server::
+  This is a reference to the Jetty org.eclipse.jetty.server.Server
+  instance to which the ContextHandler being configured in the context
+  xml file will be deployed.
+bundle.root::
+  This is a reference to the org.eclipse.jetty.util.resource.Resource
+  that represents the location of the Bundle publishing the
+  ContextHandler as a Service(obtained by calling Bundle.getLocation()).
+  Note that this could be either a directory in the file system if the
+  OSGi container automatically unpacks bundles, or it may be a jar:file:
+  url if the bundle remains packed.
+
+In the example above, you can see both of these properties being used in
+the context xml file.
+
+==== Support for the OSGi Service Platform Enterprise Specification
+
+The Jetty OSGi container implements several aspects of the Enterprise
+Specification v4.2 for the WebAppContexts and ContextHandlers that it
+deploys from either bundles or OSGi services as outlined in foregoing
+sections.
+
+===== Context Attributes
+
+For each WebAppContext or ContextHandler, the following context
+attribute is set, as required by section__128.6.1 Bundle Context__ pg
+427:
+
+osgi-bundleContext::
+  The value of this attribute is the BundleContext representing the
+  Bundle associated with the WebAppContext or ContextHandler.
+
+===== Service Attributes
+
+As required by the specification section _128.3.4 Publishing the Servlet
+Context_ pg 421, each WebAppContext and ContextHandler deployed by the
+Jetty OSGi container is also published as an OSGi service (unless it has
+been already - see sections 1.6 and 1.7). The following properties are
+associated with these services:
+
+osgi.web.symbolicname::
+  The symbolic name of the Bundle associated with the WebAppContext or
+  ContextHandler
+osgi.web.version::
+  The Bundle-Version header from the Bundle associated with the
+  WebAppContext or ContextHandler
+osgi.web.contextpath::
+  The context path of the WebAppContext or ContextHandler
+
+===== OSGi Events
+
+As required by the specification section _128.5 Events_ pg 426, the
+following OSGi Event Admin events will be posted:
+
+org/osgi/service/web/DEPLOYING::
+  The Jetty OSGi container is about to deploy a WebAppContext or
+  ContextHandler
+org/osgi/service/web/DEPLOYED::
+  The Jetty OSGi container has finished deploying a WebAppContext or
+  ContextHandler and it is in service
+org/osgi/service/web/UNDEPLOYING::
+  The Jetty OSGi container is about to undeploy a WebAppContext or
+  ContextHandler
+org/osgi/service/web/UNDEPLOYED::
+  The Jetty OSGi container has finished undeploying a WebAppContext or
+  ContextHandler and it is no longer in service
+org/osgi/service/web/FAILED::
+  The Jetty OSGi container failed to deploy a WebAppContext or
+  ContextHandler
+
+==== Using JSPs
+
+===== Setup
+
+In order to use JSPs with your webapps and bundles you will need to
+install the JSP and JSTL jars and their dependencies into your OSGi
+container. Some you will find in the Jetty distribution, whereas others
+you will need to download from
+http://central.maven.org/maven2/org/eclipse/jetty/orbit/[Maven central].
+Here is the list of recommended jars (NOTE the version numbers may
+change in future):
+
+.Jars Required for JSP
+[cols=",,",options="header",]
+|=======================================================================
+|Jar |Bundle Symbolic Name |Location
+|The link:#osgi-annotations[annotation jars] | |
+
+|org.mortbay.jasper:apache-el |org.mortbay.jasper.apache-el
+|Distribution lib/apache-jsp
+
+|org.mortbay.jasper:apache-jsp |org.mortbay.jasper.apache-jsp
+|Distribution lib/apache-jsp
+
+|org.eclipse.jetty:apache-jsp |org.eclipse.jetty.apache-jsp
+|Distribution lib/apache-jsp
+
+|org.eclipse.jdt.core-3.8.2.v20130121.jar
+|org.eclipse.jdt.core.compiler.batch |Distribution lib/apache-jsp
+
+|org.eclipse.jetty.osgi:jetty-osgi-boot-jsp
+|org.eclipse.jetty.osgi.boot.jsp
+|http://central.maven.org/maven2/org/eclipse/jetty/osgi/jetty-osgi-boot-jsp[Maven
+central]
+|=======================================================================
+
+____
+[NOTE]
+1.  As of jetty-9.2.3 the jetty-osgi-boot-jsp bundle changed to using
+Apache Jasper as the JSP implementation. Prior to this the Glassfish
+Jasper implementation was used, which had a different set of
+dependencies - pay careful attention to the jars listed both at the top
+of this page and in this section, as deployment of other jars can cause
+incomplete or incorrect package resolution in the OSGi container.
+2.  The order of deployment is important. Deploy these bundles in the
+order shown or you may experience strange failures in the compilation of
+jsps. This can be hard to diagnose but is almost always caused by the
+ServletContainerInitializer in the org.eclipse.jetty.apache-jsp bundle
+for the jsp container not being invoked due to incorrect startup of the
+annotation jars.
+____
+
+For the JSTL library, we recommend the use of the implementation from
+Glassfish, as it has fewer dependencies:
+
+.Jars Required for Glassfish JSTL
+[cols=",,",options="header",]
+|=======================================================================
+|Jar |Bundle Symbolic Name |Location
+|The jsp jars | |
+
+|org.eclipse.jetty.orbit:javax.servlet.jsp.jstl-1.2.0.v201105211821.jar
+|javax.servlet.jsp.jstl |Distribution lib/jsp
+
+|org.glassfish.web:javax.servlet.jsp.jstl-1.2.2.jar
+|org.glassfish.web.javax.servlet.jsp.jstl |Distribution lib/jsp
+|=======================================================================
+
+However, if you wish, you may use the JSTL implementation from Apache
+instead, although you will need to source some dependency jars with
+suitable OSGi manifests:
+
+.Jars Required for Apache JSTL
+[cols=",,",options="header",]
+|=======================================================================
+|Jar |Bundle Symbolic Name |Location
+|The jsp jars | |
+
+|org.apache.taglibs:taglibs-standard-spec:jar:1.2.1
+|org.apache.taglibs.taglibs-standard-spec |Distribution lib/apache-jstl
+
+|org.apache.taglibs:taglibs-standard-spec:jar:1.2.1
+|org.apache.taglibs.standard-impl |Distribution lib/apache-jstl
+
+|org.apache.xalan 2.7.1 | |Try
+http://download.eclipse.org/tools/orbit/downloads/drops/R20140525021250/repository/plugins/org.apache.xalan_2.7.1.v201005080400.jar[Eclipse
+Orbit]
+
+|org.apache.xml.serializer 2.7.1 | |Try
+http://download.eclipse.org/tools/orbit/downloads/drops/R20140525021250/repository/plugins/org.apache.xml.serializer_2.7.1.v201005080400.jar[Eclipse
+Orbit]
+|=======================================================================
+
+===== The jetty-osgi-boot-jsp jar
+
+To be able to use JSPs you will need to also install the
+http://central.maven.org/maven2/org/eclipse/jetty/osgi/jetty-osgi-boot-jsp/[jetty-osgi-boot-jsp.jar]
+into your OSGi container. This jar can be obtained from maven central
+http://central.maven.org/maven2/org/eclipse/jetty/osgi/jetty-osgi-boot-jsp/[here].
+
+This bundle acts as a fragment extension to the jetty-osgi-boot.jar and
+adds in support for using JSP.
+
+====== Using TagLibs
+
+The Jetty JSP OSGi container will make available the JSTL tag library to
+all webapps. If you only use this tag library, then your webapp will
+work without any further modification.
+
+However, if you make use of other taglibs, you will need to ensure that
+they are installed into the OSGi container, and also define some System
+properties and/or MANIFEST headers in your webapp. This is necessary
+because the classloading regime used by the OSGi container is very
+different than that used by JSP containers, and the MANIFEST of a normal
+webapp does not contain enough information for the OSGi environment to
+allow a JSP container to find and resolve TLDs referenced in the
+webapp's .jsp files.
+
+Firstly, lets look at an example of a web bundle's modified MANIFEST
+file so you get an idea of what is required. This example is a web
+bundle that uses the Spring servlet framework:
+
+[source, plain, subs="{sub-order}"]
+----
+
+Bundle-SymbolicName: com.acme.sample
+Bundle-Name: WebSample
+Web-ContextPath: taglibs
+Import-Bundle: org.springframework.web.servlet
+Require-TldBundle: org.springframework.web.servlet
+Bundle-Version: 1.0.0
+Import-Package: org.eclipse.virgo.web.dm;version="[3.0.0,4.0.0)",org.s
+ pringframework.context.config;version="[2.5.6,4.0.0)",org.springframe
+ work.stereotype;version="[2.5.6,4.0.0)",org.springframework.web.bind.
+ annotation;version="[2.5.6,4.0.0)",org.springframework.web.context;ve
+ rsion="[2.5.6,4.0.0)",org.springframework.web.servlet;version="[2.5.6
+ ,4.0.0)",org.springframework.web.servlet.view;version="[2.5.6,4.0.0)"
+ 
+          
+----
+
+The *Require-TldBundle* header tells the Jetty OSGi container that this
+bundle contains TLDs that need to be passed over to the JSP container
+for processing. The *Import-Bundle* header ensures that the
+implementation classes for these TLDs will be available to the webapp on
+the OSGi classpath.
+
+The format of the *Require-TldBundle* header is a comma separated list
+of one or more symbolic names of bundles containing TLDs.
+
+====== Container Path Taglibs
+
+Some TLD jars are required to be found on the Jetty OSGi container's
+classpath, rather than considered part of the web bundle's classpath.
+For example, this is true of JSTL and Java Server Faces. The Jetty OSGi
+container takes care of JSTL for you, but you can control which other
+jars are considered as part of the container's classpath by using the
+System property **org.eclipse.jetty.osgi.tldbundles**:
+
+org.eclipse.jetty.osgi.tldbundles::
+  System property defined on the OSGi environment that is a comma
+  separated list of symbolic names of bundles containing taglibs that
+  will be treated as if they are on the container's classpath for web
+  bundles. For example:
+  +
+[source, plain, subs="{sub-order}"]
+----
+org.eclipse.jetty.osgi.tldbundles=com.acme.special.tags,com.foo.web,org.bar.web.framework
+----
+  +
+  You will still need to define the *Import-Bundle* header in the
+  MANIFEST file for the web bundle to ensure that the TLD bundles are on
+  the OSGi classpath.
+
+Alternatively or additionally, you can define a pattern as a context
+attribute that will match symbolic bundle names in the OSGi environment
+containing TLDs that should be considered as discovered from the
+container's classpath.
+
+org.eclipse.jetty.server.webapp.containerIncludeBundlePattern::
+  This pattern must be specified as a context attribute of the
+  WebAppContext representing the web bundle. Unless you are deploying
+  your own WebAppContext (see link:#services-as-webapps[Deploying
+  Services as Webapps]), you won't have a reference to the WebAppContext
+  to do this. In that case, it can be specified on the
+  org.eclipse.jetty.deploy.DeploymentManager, where it will be applied
+  to _every_ webapp deployed by the Jetty OSGi container. The
+  jetty-osgi-boot.jar contains the default
+  jettyhome/etc/jetty-deploy.xml file where the DeploymentManager is
+  defined. To set the pattern, you will need to provide your own etc
+  files - see the section on link:#customize-jetty-container[customizing
+  the jetty container] for how to do this. Here's how the
+  jetty-deploy.xml file would look if we defined a pattern that matched
+  all bundle symbolic names ending in "tag" and "web":
+  +
+[source, xml, subs="{sub-order}"]
+----
+
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+    <Call name="addBean">
+      <Arg>
+        <New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
+          <Set name="contexts">
+            <Ref refid="Contexts" />
+          </Set>
+          <Call name="setContextAttribute">
+            <Arg>org.eclipse.jetty.server.webapp.ContainerIncludeBundlePattern</Arg>
+            <Arg>.*\.tag$|.*\.web$</Arg>
+          </Call>
+        </New>
+      </Arg>
+    </Call>
+</Configure>
+
+                
+----
+  +
+  Again, you will still need to define suitable *Import-Bundle* headers
+  in your web bundle MANIFEST to ensure that bundles matching the
+  pattern are available on the OSGi class path.
+
+[[osgi-annotations]]
+==== Using Annotations/ServletContainerInitializers
+
+Annotations are very much part of the Servlet 3.0 and 3.1
+specifications. In order to use them with Jetty in OSGi, you will need
+to deploy some extra jars into your OSGi container:
+
+.Jars Required for Annotations
+[cols=",,",options="header",]
+|=======================================================================
+|Jar |Bundle Symbolic Name |Location
+|org.ow2.asm:asm-5.0.1.jar |org.objectweb.asm
+|http://central.maven.org/maven2/org/ow2/asm/asm[Maven central]
+
+|org.ow2.asm:asm-commons-5.0.1.jar |org.objectweb.asm.commons
+|http://central.maven.org/maven2/org/ow2/asm/asm-commons[Maven central]
+
+|org.ow2.asm:asm-tree-5.0.1.jar |org.objectweb.asm.tree
+|http://central.maven.org/maven2/org/ow2/asm/asm-tree[Maven central]
+
+|org.apache.aries:org.apache.aries.util-1.0.1.jar |org.apache.aries.util
+|http://central.maven.org/maven2/org/apache/aries/org.apache.aries.util/[Maven
+central]
+
+|org.apache.aries.spifly:org.apache.aries.spifly.dynamic.bundle-1.0.1.jar
+|org.apache.aries.spifly.dynamic.bundle
+|http://central.maven.org/maven2/org/apache/aries/spifly/org.apache.aries.spifly.dynamic.bundle/[Maven
+central]
+
+|javax.annotation:javax.annotation-api-1.2.jar |javax.annotation-api
+|http://central.maven.org/maven2/javax/annotation/javax.annotation-api/[Maven
+central]
+
+|jta api version 1.1.1 (eg
+org.apache.geronimo.specs:geronimo-jta_1.1_spec-1.1.1.jar)^*^ | |Maven
+central
+
+|javax mail api version 1.4.1 (eg
+org.eclipse.jetty.orbit:javax.mail.glassfish-1.4.1.v201005082020.jar)^*^
+| |Maven central
+
+|jetty-jndi |org.eclipse.jetty.jndi |Distribution lib/
+
+|jetty-plus |org.eclipse.jetty.plus |Distribution lib/
+
+|jetty-annotations |org.eclipse.jetty.annotations |Distribution lib/
+|=======================================================================
+
+____
+[IMPORTANT]
+If you wish to use JSPs you will need to deploy these annotation-related
+jars.
+____
+
+____
+[NOTE]
+You may be able to deploy later versions or other providers of these
+specifications, however these particular versions are known to have
+correct manifests and have been tested and known to work with OSGi
+____
+
+Even if your webapp itself does not not use annotations, you may need to
+deploy these jars because your webapp depends on a Jetty module or a 3rd
+party library that uses a
+http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html[javax.servlet.ServletContainerInitializer].
+This interface requires annotation support. It is implemented by
+providers of code that extend the capabilities of the container. An
+example of this is the Jetty JSR356 Websocket implementation, although
+it is being used increasingly commonly in popular libraries like
+link:http://projects.spring.io/spring-framework/[Spring], link:https://jersey.java.net/[Jersey]
+and JSP containers.
+
+To find ServletContainerInitializers on the classpath, Jetty uses the
+Java
+http://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html[ServiceLoader]
+mechanism. For this to function in OSGi, you will need an OSGi R5
+compatible container, and have support for the
+http://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html[Service
+Loader Mediator]. Jetty has been tested with
+thehttp://aries.apache.org/modules/spi-fly.html[Aries SpiFly]module,
+which is the reference implementation of the Service Loader Mediator,
+and is listed in the jars above.
+
+==== OSGi Containers
+
+===== Felix
+
+The Jetty OSGi integration has been successfully tested against
+http://felix.apache.org/[Felix] 5.0.0.
+
+You will require the following extra Felix services, available as
+separately downloadable jars:
+
+* http://felix.apache.org/documentation/subprojects/apache-felix-config-admin.html[Felix
+Configuration Admin Service]
+* http://felix.apache.org/documentation/subprojects/apache-felix-event-admin.html[Felix
+Event Admin Service]
+
+Unfortunately, as of Felix 4.x there is a difficultly with the
+resolution of the javax.transaction package.
+A link:http://mail-archives.apache.org/mod_mbox/felix-users/201211.mbox/%3CCAPr=90M+5vYjPqAvyTU+gYHr64y_FosBYELeUYcU_rFEJF3Cxw&#64;mail.gmail.com%3E[description of the problem] and hint to solving it is described [http://mail-archives.apache.org/mod_mbox/felix-users/201211.mbox/%3CCAPr=90M+5vYjPqAvyTU+gYHr64y_FosBYELeUYcU_rFEJF3Cxw&#64;mail.gmail.com%3E[here]].
+
+The simplest solution for this is to extract the `default.properties`
+file from the `felix.jar, change the declaration of the javax.sql and
+      javax.transaction packages` and set the changed lines as the value
+of the `org.osgi.framework.system.packages` property in the
+`conf/config.properties` file.
+
+The `default.properties` file defines the default
+`org.osgi.framework.system.packages` property like this:
+
+[source,properties]
+----
+# Default packages exported by system bundle.
+org.osgi.framework.system.packages=org.osgi.framework; version=1.7.0, \
+ org.osgi.framework.hooks.bundle; version=1.1.0, \
+ org.osgi.framework.hooks.resolver; version=1.0.0, \
+ org.osgi.framework.hooks.service; version=1.1.0, \
+ org.osgi.framework.hooks.weaving; version=1.0.0, \
+ org.osgi.framework.launch; version=1.1.0, \
+ org.osgi.framework.namespace; version=1.0.0, \
+ org.osgi.framework.startlevel; version=1.0.0, \
+ org.osgi.framework.wiring; version=1.1.0, \
+ org.osgi.resource; version=1.0.0, \
+ org.osgi.service.packageadmin; version=1.2.0, \
+ org.osgi.service.startlevel; version=1.1.0, \
+ org.osgi.service.url; version=1.0.0, \
+ org.osgi.util.tracker; version=1.5.1 \
+ ${jre-${java.specification.version}}
+----
+
+The last line must be substituted for one of the definitions further
+down in the file that is suitable for the jvm you are using.
+
+You will take these lines and copy them into the
+`conf/config.properties` file, after having replaced the line
+$\{jre-$\{java.specification.version}} with all of the lines relevant to
+your version of the jvm.
+
+For example, for a 1.7 jvm, you will find this property definition:
+
+[source,properties]
+----
+jre-1.7=, \
+ javax.accessibility;uses:="javax.swing.text";version="0.0.0.1_007_JavaSE", \
+ javax.activation;version="0.0.0.1_007_JavaSE", \
+ javax.activity;version="0.0.0.1_007_JavaSE", \
+ javax.annotation.processing;uses:="javax.tools,javax.lang.model,javax.lang.model.element,javax.lang.model.util";version="0.0.0.1_007_JavaSE", \
+ javax.annotation;version="0.0.0.1_007_JavaSE", \
+ javax.crypto.interfaces;uses:="javax.crypto.spec,javax.crypto";version="0.0.0.1_007_JavaSE", \
+ javax.crypto.spec;uses:="javax.crypto";version="0.0.0.1_007_JavaSE", \
+ javax.crypto;uses:="javax.crypto.spec";version="0.0.0.1_007_JavaSE", \
+ javax.imageio.event;uses:="javax.imageio";version="0.0.0.1_007_JavaSE", \
+ javax.imageio.metadata;uses:="org.w3c.dom,javax.imageio";version="0.0.0.1_007_JavaSE", \
+ javax.imageio.plugins.bmp;uses:="javax.imageio";version="0.0.0.1_007_JavaSE", \
+ javax.imageio.plugins.jpeg;uses:="javax.imageio";version="0.0.0.1_007_JavaSE", \
+ javax.imageio.spi;uses:="javax.imageio.stream,javax.imageio,javax.imageio.metadata";version="0.0.0.1_007_JavaSE", \
+ javax.imageio.stream;uses:="javax.imageio";version="0.0.0.1_007_JavaSE", \
+ javax.imageio;uses:="javax.imageio.metadata,javax.imageio.stream,javax.imageio.spi,javax.imageio.event";version="0.0.0.1_007_JavaSE", \
+ javax.jws.soap;version="0.0.0.1_007_JavaSE", \
+ javax.jws;version="0.0.0.1_007_JavaSE", \
+ javax.lang.model.element;uses:="javax.lang.model.type,javax.lang.model";version="0.0.0.1_007_JavaSE", \
+ javax.lang.model.type;uses:="javax.lang.model.element,javax.lang.model";version="0.0.0.1_007_JavaSE", \
+ javax.lang.model.util;uses:="javax.lang.model,javax.lang.model.element,javax.annotation.processing,javax.lang.model.type";version="0.0.0.1_007_JavaSE", \
+ javax.lang.model;version="0.0.0.1_007_JavaSE", \
+ javax.management.loading;uses:="javax.management";version="0.0.0.1_007_JavaSE", \
+ javax.management.modelmbean;uses:="javax.management,javax.management.loading";version="0.0.0.1_007_JavaSE", \
+ javax.management.monitor;uses:="javax.management";version="0.0.0.1_007_JavaSE", \
+ javax.management.openmbean;uses:="javax.management";version="0.0.0.1_007_JavaSE", \
+ javax.management.relation;uses:="javax.management";version="0.0.0.1_007_JavaSE", \
+ javax.management.remote.rmi;uses:="javax.management.remote,javax.security.auth,javax.management,javax.management.loading,javax.naming,javax.rmi.ssl,org.omg.CORBA,org.omg.CORBA_2_3.portable,org.omg.CORBA.portable,javax.rmi.CORBA,javax.rmi";version="0.0.0.1_007_JavaSE", \
+ javax.management.remote;uses:="javax.security.auth,javax.management";version="0.0.0.1_007_JavaSE", \
+ javax.management.timer;uses:="javax.management";version="0.0.0.1_007_JavaSE", \
+ javax.management;uses:="javax.management.loading,javax.management.openmbean";version="0.0.0.1_007_JavaSE", \
+ javax.naming.directory;uses:="javax.naming";version="0.0.0.1_007_JavaSE", \
+ javax.naming.event;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_007_JavaSE", \
+ javax.naming.ldap;uses:="javax.naming,javax.naming.directory,javax.net.ssl,javax.naming.event";version="0.0.0.1_007_JavaSE", \
+ javax.naming.spi;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_007_JavaSE", \
+ javax.naming;uses:="javax.naming.spi";version="0.0.0.1_007_JavaSE", \
+ javax.net.ssl;uses:="javax.security.cert,javax.security.auth.x500,javax.net";version="0.0.0.1_007_JavaSE", \
+ javax.net;version="0.0.0.1_007_JavaSE", \
+ javax.print.attribute.standard;uses:="javax.print.attribute";version="0.0.0.1_007_JavaSE", \
+ javax.print.attribute;version="0.0.0.1_007_JavaSE", \
+ javax.print.event;uses:="javax.print,javax.print.attribute";version="0.0.0.1_007_JavaSE", \
+ javax.print;uses:="javax.print.attribute,javax.print.event,javax.print.attribute.standard";version="0.0.0.1_007_JavaSE", \
+ javax.rmi.CORBA;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable,org.omg.CORBA.portable,org.omg.SendingContext";version="0.0.0.1_007_JavaSE", \
+ javax.rmi.ssl;uses:="javax.net,javax.net.ssl";version="0.0.0.1_007_JavaSE", \
+ javax.rmi;uses:="org.omg.CORBA,javax.rmi.CORBA";version="0.0.0.1_007_JavaSE", \
+ javax.script;version="0.0.0.1_007_JavaSE", \
+ javax.security.auth.callback;version="0.0.0.1_007_JavaSE", \
+ javax.security.auth.kerberos;uses:="javax.security.auth,javax.crypto";version="0.0.0.1_007_JavaSE", \
+ javax.security.auth.login;uses:="javax.security.auth,javax.security.auth.callback";version="0.0.0.1_007_JavaSE", \
+ javax.security.auth.spi;uses:="javax.security.auth.callback,javax.security.auth.login,javax.security.auth";version="0.0.0.1_007_JavaSE", \
+ javax.security.auth.x500;uses:="javax.security.auth";version="0.0.0.1_007_JavaSE", \
+ javax.security.auth;version="0.0.0.1_007_JavaSE", \
+ javax.security.cert;version="0.0.0.1_007_JavaSE", \
+ javax.security.sasl;uses:="javax.security.auth.callback";version="0.0.0.1_007_JavaSE", \
+ javax.sound.midi.spi;uses:="javax.sound.midi";version="0.0.0.1_007_JavaSE", \
+ javax.sound.midi;uses:="javax.sound.midi.spi";version="0.0.0.1_007_JavaSE", \
+ javax.sound.sampled.spi;uses:="javax.sound.sampled";version="0.0.0.1_007_JavaSE", \
+ javax.sound.sampled;uses:="javax.sound.sampled.spi";version="0.0.0.1_007_JavaSE", \
+ javax.sql.rowset.serial;uses:="javax.sql.rowset";version="0.0.0.1_007_JavaSE", \
+ javax.sql.rowset.spi;uses:="javax.sql,javax.naming,javax.sql.rowset";version="0.0.0.1_007_JavaSE", \
+ javax.sql.rowset;uses:="javax.sql,javax.sql.rowset.serial,javax.sql.rowset.spi";version="0.0.0.1_007_JavaSE", \
+ javax.sql;uses:="javax.transaction.xa";version="0.0.0.1_007_JavaSE", \
+ javax.swing.border;uses:="javax.swing";version="0.0.0.1_007_JavaSE", \
+ javax.swing.colorchooser;uses:="javax.swing,javax.swing.border,javax.swing.event,javax.swing.text";version="0.0.0.1_007_JavaSE", \
+ javax.swing.event;uses:="javax.swing,javax.swing.text,javax.swing.table,javax.swing.tree,javax.swing.undo";version="0.0.0.1_007_JavaSE", \
+ javax.swing.filechooser;uses:="javax.swing";version="0.0.0.1_007_JavaSE", \
+ javax.swing.plaf.basic;uses:="javax.swing.border,javax.swing,javax.swing.plaf,javax.swing.text,javax.swing.event,javax.swing.colorchooser,javax.accessibility,javax.swing.filechooser,javax.swing.text.html,javax.sound.sampled,javax.swing.table,javax.swing.plaf.synth,javax.swing.tree";version="0.0.0.1_007_JavaSE", \
+ javax.swing.plaf.metal;uses:="javax.swing.plaf,javax.swing,javax.swing.border,javax.swing.text,javax.swing.plaf.basic,javax.swing.filechooser,javax.swing.event,javax.swing.tree";version="0.0.0.1_007_JavaSE", \
+ javax.swing.plaf.multi;uses:="javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_007_JavaSE", \
+ javax.swing.plaf.nimbus;uses:="javax.swing,javax.swing.plaf,javax.swing.border,javax.swing.plaf.synth";version="0.0.0.1_007_JavaSE", \
+ javax.swing.plaf.synth;uses:="javax.swing,javax.swing.plaf,javax.swing.text,javax.swing.border,javax.swing.plaf.basic,javax.swing.colorchooser,javax.swing.event,javax.xml.parsers,org.xml.sax,org.xml.sax.helpers,javax.swing.table,javax.swing.tree";version="0.0.0.1_007_JavaSE", \
+ javax.swing.plaf;uses:="javax.swing,javax.swing.border,javax.accessibility,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_007_JavaSE", \
+ javax.swing.table;uses:="javax.swing.event,javax.swing.plaf,javax.swing.border,javax.swing,javax.accessibility";version="0.0.0.1_007_JavaSE", \
+ javax.swing.text.html.parser;uses:="javax.swing.text,javax.swing.text.html";version="0.0.0.1_007_JavaSE", \
+ javax.swing.text.html;uses:="javax.swing.event,javax.swing.text,javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.border,javax.swing.undo";version="0.0.0.1_007_JavaSE", \
+ javax.swing.text.rtf;uses:="javax.swing.text";version="0.0.0.1_007_JavaSE", \
+ javax.swing.text;uses:="javax.swing.event,javax.swing.tree,javax.swing.undo,javax.swing,javax.swing.plaf,javax.swing.plaf.basic,javax.print,javax.print.attribute,javax.accessibility,javax.swing.text.html";version="0.0.0.1_007_JavaSE", \
+ javax.swing.tree;uses:="javax.swing.event,javax.swing,javax.swing.border,javax.swing.plaf,javax.swing.plaf.basic";version="0.0.0.1_007_JavaSE", \
+ javax.swing.undo;uses:="javax.swing,javax.swing.event";version="0.0.0.1_007_JavaSE", \
+ javax.swing;uses:="javax.swing.event,javax.accessibility,javax.swing.text,javax.swing.plaf,javax.swing.border,javax.swing.tree,javax.swing.table,javax.swing.colorchooser,javax.swing.plaf.basic,javax.swing.text.html,javax.swing.filechooser,javax.print,javax.print.attribute,javax.swing.plaf.metal";version="0.0.0.1_007_JavaSE", \
+ javax.tools;uses:="javax.lang.model.element,javax.annotation.processing,javax.lang.model";version="0.0.0.1_007_JavaSE", \
+ javax.transaction.xa;version="0.0.0.1_007_JavaSE", \
+ javax.transaction;version="0.0.0.1_007_JavaSE", \
+ javax.xml.bind.annotation.adapters;uses:="javax.xml.bind";version="0.0.0.1_007_JavaSE", \
+ javax.xml.bind.annotation;uses:="javax.xml.transform,javax.xml.bind,javax.xml.parsers,javax.xml.transform.dom,org.w3c.dom";version="0.0.0.1_007_JavaSE", \
+ javax.xml.bind.attachment;uses:="javax.activation";version="0.0.0.1_007_JavaSE", \
+ javax.xml.bind.helpers;uses:="javax.xml.bind.annotation.adapters,javax.xml.transform.dom,org.w3c.dom,org.xml.sax,javax.xml.bind.attachment,javax.xml.stream,javax.xml.transform,javax.xml.transform.stream,javax.xml.validation,javax.xml.transform.sax,javax.xml.bind,javax.xml.parsers";version="0.0.0.1_007_JavaSE", \
+ javax.xml.bind.util;uses:="javax.xml.transform.sax,javax.xml.bind,org.xml.sax,org.xml.sax.ext,org.xml.sax.helpers";version="0.0.0.1_007_JavaSE", \
+ javax.xml.bind;uses:="javax.xml.validation,javax.xml.namespace,javax.xml.datatype,javax.xml.transform,javax.xml.bind.annotation,javax.xml.transform.stream,org.w3c.dom,javax.xml.bind.attachment,javax.xml.stream,javax.xml.bind.annotation.adapters,org.xml.sax";version="0.0.0.1_007_JavaSE", \
+ javax.xml.crypto.dom;uses:="javax.xml.crypto,org.w3c.dom";version="0.0.0.1_007_JavaSE", \
+ javax.xml.crypto.dsig.dom;uses:="javax.xml.crypto.dsig,javax.xml.crypto,org.w3c.dom,javax.xml.crypto.dom";version="0.0.0.1_007_JavaSE", \
+ javax.xml.crypto.dsig.keyinfo;uses:="javax.xml.crypto";version="0.0.0.1_007_JavaSE", \
+ javax.xml.crypto.dsig.spec;uses:="javax.xml.crypto";version="0.0.0.1_007_JavaSE", \
+ javax.xml.crypto.dsig;uses:="javax.xml.crypto,javax.xml.crypto.dsig.spec,javax.xml.crypto.dsig.keyinfo";version="0.0.0.1_007_JavaSE", \
+ javax.xml.crypto;uses:="javax.xml.crypto.dsig.keyinfo";version="0.0.0.1_007_JavaSE", \
+ javax.xml.datatype;uses:="javax.xml.namespace";version="0.0.0.1_007_JavaSE", \
+ javax.xml.namespace;version="0.0.0.1_007_JavaSE", \
+ javax.xml.parsers;uses:="javax.xml.validation,org.w3c.dom,org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_007_JavaSE", \
+ javax.xml.soap;uses:="javax.activation,javax.xml.namespace,org.w3c.dom,javax.xml.transform.dom,javax.xml.transform";version="0.0.0.1_007_JavaSE", \
+ javax.xml.stream.events;uses:="javax.xml.namespace,javax.xml.stream";version="0.0.0.1_007_JavaSE", \
+ javax.xml.stream.util;uses:="javax.xml.stream,javax.xml.stream.events,javax.xml.namespace";version="0.0.0.1_007_JavaSE", \
+ javax.xml.stream;uses:="javax.xml.stream.events,javax.xml.namespace,javax.xml.stream.util,javax.xml.transform";version="0.0.0.1_007_JavaSE", \
+ javax.xml.transform.dom;uses:="javax.xml.transform,org.w3c.dom";version="0.0.0.1_007_JavaSE", \
+ javax.xml.transform.sax;uses:="org.xml.sax.ext,javax.xml.transform,org.xml.sax,javax.xml.transform.stream";version="0.0.0.1_007_JavaSE", \
+ javax.xml.transform.stax;uses:="javax.xml.stream,javax.xml.transform,javax.xml.stream.events";version="0.0.0.1_007_JavaSE", \
+ javax.xml.transform.stream;uses:="javax.xml.transform";version="0.0.0.1_007_JavaSE", \
+ javax.xml.transform;version="0.0.0.1_007_JavaSE", \
+ javax.xml.validation;uses:="org.w3c.dom.ls,javax.xml.transform,javax.xml.transform.stream,org.xml.sax,org.w3c.dom";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.handler.soap;uses:="javax.xml.ws.handler,javax.xml.namespace,javax.xml.soap,javax.xml.bind";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.handler;uses:="javax.xml.ws,javax.xml.namespace";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.http;uses:="javax.xml.ws";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.soap;uses:="javax.xml.ws.spi,javax.xml.ws,javax.xml.soap";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.spi.http;version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.spi;uses:="javax.xml.ws,javax.xml.ws.wsaddressing,javax.xml.transform,org.w3c.dom,javax.xml.namespace,javax.xml.ws.handler,javax.xml.bind";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.wsaddressing;uses:="javax.xml.bind.annotation,javax.xml.namespace,org.w3c.dom,javax.xml.transform,javax.xml.bind,javax.xml.ws,javax.xml.ws.spi";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws;uses:="javax.xml.ws.handler,javax.xml.ws.spi,javax.xml.ws.spi.http,javax.xml.transform,org.w3c.dom,javax.xml.bind.annotation,javax.xml.transform.stream,javax.xml.bind,javax.xml.namespace";version="0.0.0.1_007_JavaSE", \
+ javax.xml.xpath;uses:="org.xml.sax,javax.xml.namespace";version="0.0.0.1_007_JavaSE", \
+ javax.xml;version="0.0.0.1_007_JavaSE", \
+ org.ietf.jgss;version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA.DynAnyPackage;uses:="org.omg.CORBA";version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA.ORBPackage;uses:="org.omg.CORBA";version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA.TypeCodePackage;uses:="org.omg.CORBA";version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA.portable;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA;uses:="org.omg.CORBA.portable,org.omg.CORBA.DynAnyPackage,org.omg.CORBA.ORBPackage,org.omg.CORBA_2_3.portable,org.omg.CORBA.TypeCodePackage";version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA_2_3.portable;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA_2_3;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.CosNaming.NamingContextExtPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.CosNaming.NamingContextPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CosNaming";version="0.0.0.1_007_JavaSE", \
+ org.omg.CosNaming;uses:="org.omg.CORBA.portable,org.omg.CORBA,org.omg.PortableServer,org.omg.CosNaming.NamingContextPackage,org.omg.CosNaming.NamingContextExtPackage";version="0.0.0.1_007_JavaSE", \
+ org.omg.Dynamic;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.DynamicAny.DynAnyFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.DynamicAny.DynAnyPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.DynamicAny;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.DynamicAny.DynAnyFactoryPackage,org.omg.DynamicAny.DynAnyPackage";version="0.0.0.1_007_JavaSE", \
+ org.omg.IOP.CodecFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.IOP.CodecPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.IOP;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP.CodecFactoryPackage,org.omg.IOP.CodecPackage";version="0.0.0.1_007_JavaSE", \
+ org.omg.Messaging;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableInterceptor.ORBInitInfoPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableInterceptor;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP,org.omg.PortableInterceptor.ORBInitInfoPackage,org.omg.CORBA_2_3.portable,org.omg.Dynamic";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableServer.CurrentPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableServer.POAManagerPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableServer.POAPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableServer.ServantLocatorPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableServer.portable;uses:="org.omg.CORBA,org.omg.PortableServer";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableServer;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.PortableServer.CurrentPackage,org.omg.PortableServer.POAManagerPackage,org.omg.PortableServer.POAPackage,org.omg.PortableServer.portable,org.omg.CORBA_2_3,org.omg.PortableServer.ServantLocatorPackage";version="0.0.0.1_007_JavaSE", \
+ org.omg.SendingContext;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.stub.java.rmi;uses:="javax.rmi.CORBA";version="0.0.0.1_007_JavaSE", \
+ org.w3c.dom.bootstrap;uses:="org.w3c.dom";version="0.0.0.1_007_JavaSE", \
+ org.w3c.dom.events;uses:="org.w3c.dom,org.w3c.dom.views";version="0.0.0.1_007_JavaSE", \
+ org.w3c.dom.ls;uses:="org.w3c.dom,org.w3c.dom.events,org.w3c.dom.traversal";version="0.0.0.1_007_JavaSE", \
+ org.w3c.dom;version="0.0.0.1_007_JavaSE", \
+ org.xml.sax.ext;uses:="org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_007_JavaSE", \
+ org.xml.sax.helpers;uses:="org.xml.sax";version="0.0.0.1_007_JavaSE", \
+ org.xml.sax;version="0.0.0.1_007_JavaSE"
+----
+
+Remove the definition for the `javax.transaction` packages, and remove
+the `uses:=` clause for the `javax.sql` packages (but leaving the
+`version` clause). Concatenate all the lines together. You'll wind up
+with something like this in your `conf/config.properties` file:
+
+[source,properties]
+----
+org.osgi.framework.system.packages=org.osgi.framework;version=1.7.0, org.osgi.framework.hooks.bundle;version=1.1.0, org.osgi.framework.hooks.resolver;version=1.0.0, org.osgi.framework.hooks.service;version=1.1.0, org.osgi.framework.hooks.weaving;version=1.0.0, org.osgi.framework.launch;version=1.1.0, org.osgi.framework.namespace;version=1.0.0, org.osgi.framework.startlevel;version=1.0.0, org.osgi.framework.wiring;version=1.1.0, org.osgi.resource;version=1.0.0, org.osgi.service.packageadmin; version=1.2.0, org.osgi.service.startlevel; version=1.1.0, org.osgi.service.url;version=1.0.0, org.osgi.util.tracker;version=1.5.1 javax.accessibility;uses:="javax.swing.text";version="0.0.0.1_007_JavaSE", javax.activation;version="0.0.0.1_007_JavaSE", javax.activity;version="0.0.0.1_007_JavaSE", javax.annotation.processing;uses:="javax.tools,javax.lang.model,javax.lang.model.element,javax.lang.model.util";version="0.0.0.1_007_JavaSE", javax.annotation;version="0.0.0.1_007_JavaSE", javax.crypto.interfaces;uses:="javax.crypto.spec,javax.crypto";version="0.0.0.1_007_JavaSE", javax.crypto.spec;uses:="javax.crypto";version="0.0.0.1_007_JavaSE", javax.crypto;uses:="javax.crypto.spec";version="0.0.0.1_007_JavaSE", javax.imageio.event;uses:="javax.imageio";version="0.0.0.1_007_JavaSE", javax.imageio.metadata;uses:="org.w3c.dom,javax.imageio";version="0.0.0.1_007_JavaSE", javax.imageio.plugins.bmp;uses:="javax.imageio";version="0.0.0.1_007_JavaSE", javax.imageio.plugins.jpeg;uses:="javax.imageio";version="0.0.0.1_007_JavaSE", javax.imageio.spi;uses:="javax.imageio.stream,javax.imageio,javax.imageio.metadata";version="0.0.0.1_007_JavaSE", javax.imageio.stream;uses:="javax.imageio";version="0.0.0.1_007_JavaSE", javax.imageio;uses:="javax.imageio.metadata,javax.imageio.stream,javax.imageio.spi,javax.imageio.event";version="0.0.0.1_007_JavaSE", javax.jws.soap;version="0.0.0.1_007_JavaSE", javax.jws;version="0.0.0.1_007_JavaSE", javax.lang.model.element;uses:="javax.lang.model.type,javax.lang.model";version="0.0.0.1_007_JavaSE", javax.lang.model.type;uses:="javax.lang.model.element,javax.lang.model";version="0.0.0.1_007_JavaSE", javax.lang.model.util;uses:="javax.lang.model,javax.lang.model.element,javax.annotation.processing,javax.lang.model.type";version="0.0.0.1_007_JavaSE", javax.lang.model;version="0.0.0.1_007_JavaSE", javax.management.loading;uses:="javax.management";version="0.0.0.1_007_JavaSE", javax.management.modelmbean;uses:="javax.management,javax.management.loading";version="0.0.0.1_007_JavaSE", javax.management.monitor;uses:="javax.management";version="0.0.0.1_007_JavaSE", javax.management.openmbean;uses:="javax.management";version="0.0.0.1_007_JavaSE", javax.management.relation;uses:="javax.management";version="0.0.0.1_007_JavaSE", javax.management.remote.rmi;uses:="javax.management.remote,javax.security.auth,javax.management,javax.management.loading,javax.naming,javax.rmi.ssl,org.omg.CORBA,org.omg.CORBA_2_3.portable,org.omg.CORBA.portable,javax.rmi.CORBA,javax.rmi";version="0.0.0.1_007_JavaSE", javax.management.remote;uses:="javax.security.auth,javax.management";version="0.0.0.1_007_JavaSE", javax.management.timer;uses:="javax.management";version="0.0.0.1_007_JavaSE", javax.management;uses:="javax.management.loading,javax.management.openmbean";version="0.0.0.1_007_JavaSE", javax.naming.directory;uses:="javax.naming";version="0.0.0.1_007_JavaSE", javax.naming.event;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_007_JavaSE", javax.naming.ldap;uses:="javax.naming,javax.naming.directory,javax.net.ssl,javax.naming.event";version="0.0.0.1_007_JavaSE", javax.naming.spi;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_007_JavaSE", javax.naming;uses:="javax.naming.spi";version="0.0.0.1_007_JavaSE", javax.net.ssl;uses:="javax.security.cert,javax.security.auth.x500,javax.net";version="0.0.0.1_007_JavaSE", javax.net;version="0.0.0.1_007_JavaSE", javax.print.attribute.standard;uses:="javax.print.attribute";version="0.0.0.1_007_JavaSE", javax.print.attribute;version="0.0.0.1_007_JavaSE", javax.print.event;uses:="javax.print,javax.print.attribute";version="0.0.0.1_007_JavaSE", javax.print;uses:="javax.print.attribute,javax.print.event,javax.print.attribute.standard";version="0.0.0.1_007_JavaSE", javax.rmi.CORBA;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable,org.omg.CORBA.portable,org.omg.SendingContext";version="0.0.0.1_007_JavaSE", javax.rmi.ssl;uses:="javax.net,javax.net.ssl";version="0.0.0.1_007_JavaSE", javax.rmi;uses:="org.omg.CORBA,javax.rmi.CORBA";version="0.0.0.1_007_JavaSE", javax.script;version="0.0.0.1_007_JavaSE", javax.security.auth.callback;version="0.0.0.1_007_JavaSE", javax.security.auth.kerberos;uses:="javax.security.auth,javax.crypto";version="0.0.0.1_007_JavaSE", javax.security.auth.login;uses:="javax.security.auth,javax.security.auth.callback";version="0.0.0.1_007_JavaSE", javax.security.auth.spi;uses:="javax.security.auth.callback,javax.security.auth.login,javax.security.auth";version="0.0.0.1_007_JavaSE", javax.security.auth.x500;uses:="javax.security.auth";version="0.0.0.1_007_JavaSE", javax.security.auth;version="0.0.0.1_007_JavaSE", javax.security.cert;version="0.0.0.1_007_JavaSE", javax.security.sasl;uses:="javax.security.auth.callback";version="0.0.0.1_007_JavaSE", javax.sound.midi.spi;uses:="javax.sound.midi";version="0.0.0.1_007_JavaSE", javax.sound.midi;uses:="javax.sound.midi.spi";version="0.0.0.1_007_JavaSE", javax.sound.sampled.spi;uses:="javax.sound.sampled";version="0.0.0.1_007_JavaSE", javax.sound.sampled;uses:="javax.sound.sampled.spi";version="0.0.0.1_007_JavaSE", javax.sql.rowset.serial;version="0.0.0.1_007_JavaSE", javax.sql.rowset.spi;version="0.0.0.1_007_JavaSE", javax.sql.rowset;version="0.0.0.1_007_JavaSE", javax.sql;version="0.0.0.1_007_JavaSE", javax.swing.border;uses:="javax.swing";version="0.0.0.1_007_JavaSE", javax.swing.colorchooser;uses:="javax.swing,javax.swing.border,javax.swing.event,javax.swing.text";version="0.0.0.1_007_JavaSE", javax.swing.event;uses:="javax.swing,javax.swing.text,javax.swing.table,javax.swing.tree,javax.swing.undo";version="0.0.0.1_007_JavaSE", javax.swing.filechooser;uses:="javax.swing";version="0.0.0.1_007_JavaSE", javax.swing.plaf.basic;uses:="javax.swing.border,javax.swing,javax.swing.plaf,javax.swing.text,javax.swing.event,javax.swing.colorchooser,javax.accessibility,javax.swing.filechooser,javax.swing.text.html,javax.sound.sampled,javax.swing.table,javax.swing.plaf.synth,javax.swing.tree";version="0.0.0.1_007_JavaSE", javax.swing.plaf.metal;uses:="javax.swing.plaf,javax.swing,javax.swing.border,javax.swing.text,javax.swing.plaf.basic,javax.swing.filechooser,javax.swing.event,javax.swing.tree";version="0.0.0.1_007_JavaSE", javax.swing.plaf.multi;uses:="javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_007_JavaSE", javax.swing.plaf.nimbus;uses:="javax.swing,javax.swing.plaf,javax.swing.border,javax.swing.plaf.synth";version="0.0.0.1_007_JavaSE", javax.swing.plaf.synth;uses:="javax.swing,javax.swing.plaf,javax.swing.text,javax.swing.border,javax.swing.plaf.basic,javax.swing.colorchooser,javax.swing.event,javax.xml.parsers,org.xml.sax,org.xml.sax.helpers,javax.swing.table,javax.swing.tree";version="0.0.0.1_007_JavaSE", javax.swing.plaf;uses:="javax.swing,javax.swing.border,javax.accessibility,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_007_JavaSE", javax.swing.table;uses:="javax.swing.event,javax.swing.plaf,javax.swing.border,javax.swing,javax.accessibility";version="0.0.0.1_007_JavaSE", javax.swing.text.html.parser;uses:="javax.swing.text,javax.swing.text.html";version="0.0.0.1_007_JavaSE", javax.swing.text.html;uses:="javax.swing.event,javax.swing.text,javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.border,javax.swing.undo";version="0.0.0.1_007_JavaSE", javax.swing.text.rtf;uses:="javax.swing.text";version="0.0.0.1_007_JavaSE", javax.swing.text;uses:="javax.swing.event,javax.swing.tree,javax.swing.undo,javax.swing,javax.swing.plaf,javax.swing.plaf.basic,javax.print,javax.print.attribute,javax.accessibility,javax.swing.text.html";version="0.0.0.1_007_JavaSE", javax.swing.tree;uses:="javax.swing.event,javax.swing,javax.swing.border,javax.swing.plaf,javax.swing.plaf.basic";version="0.0.0.1_007_JavaSE", javax.swing.undo;uses:="javax.swing,javax.swing.event";version="0.0.0.1_007_JavaSE", javax.swing;uses:="javax.swing.event,javax.accessibility,javax.swing.text,javax.swing.plaf,javax.swing.border,javax.swing.tree,javax.swing.table,javax.swing.colorchooser,javax.swing.plaf.basic,javax.swing.text.html,javax.swing.filechooser,javax.print,javax.print.attribute,javax.swing.plaf.metal";version="0.0.0.1_007_JavaSE", javax.tools;uses:="javax.lang.model.element,javax.annotation.processing,javax.lang.model";version="0.0.0.1_007_JavaSE", javax.xml.bind.annotation.adapters;uses:="javax.xml.bind";version="0.0.0.1_007_JavaSE", javax.xml.bind.annotation;uses:="javax.xml.transform,javax.xml.bind,javax.xml.parsers,javax.xml.transform.dom,org.w3c.dom";version="0.0.0.1_007_JavaSE", javax.xml.bind.attachment;uses:="javax.activation";version="0.0.0.1_007_JavaSE", javax.xml.bind.helpers;uses:="javax.xml.bind.annotation.adapters,javax.xml.transform.dom,org.w3c.dom,org.xml.sax,javax.xml.bind.attachment,javax.xml.stream,javax.xml.transform,javax.xml.transform.stream,javax.xml.validation,javax.xml.transform.sax,javax.xml.bind,javax.xml.parsers";version="0.0.0.1_007_JavaSE", javax.xml.bind.util;uses:="javax.xml.transform.sax,javax.xml.bind,org.xml.sax,org.xml.sax.ext,org.xml.sax.helpers";version="0.0.0.1_007_JavaSE", javax.xml.bind;uses:="javax.xml.validation,javax.xml.namespace,javax.xml.datatype,javax.xml.transform,javax.xml.bind.annotation,javax.xml.transform.stream,org.w3c.dom,javax.xml.bind.attachment,javax.xml.stream,javax.xml.bind.annotation.adapters,org.xml.sax";version="0.0.0.1_007_JavaSE", javax.xml.crypto.dom;uses:="javax.xml.crypto,org.w3c.dom";version="0.0.0.1_007_JavaSE", javax.xml.crypto.dsig.dom;uses:="javax.xml.crypto.dsig,javax.xml.crypto,org.w3c.dom,javax.xml.crypto.dom";version="0.0.0.1_007_JavaSE", javax.xml.crypto.dsig.keyinfo;uses:="javax.xml.crypto";version="0.0.0.1_007_JavaSE", javax.xml.crypto.dsig.spec;uses:="javax.xml.crypto";version="0.0.0.1_007_JavaSE", javax.xml.crypto.dsig;uses:="javax.xml.crypto,javax.xml.crypto.dsig.spec,javax.xml.crypto.dsig.keyinfo";version="0.0.0.1_007_JavaSE", javax.xml.crypto;uses:="javax.xml.crypto.dsig.keyinfo";version="0.0.0.1_007_JavaSE", javax.xml.datatype;uses:="javax.xml.namespace";version="0.0.0.1_007_JavaSE", javax.xml.namespace;version="0.0.0.1_007_JavaSE", javax.xml.parsers;uses:="javax.xml.validation,org.w3c.dom,org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_007_JavaSE", javax.xml.soap;uses:="javax.activation,javax.xml.namespace,org.w3c.dom,javax.xml.transform.dom,javax.xml.transform";version="0.0.0.1_007_JavaSE", javax.xml.stream.events;uses:="javax.xml.namespace,javax.xml.stream";version="0.0.0.1_007_JavaSE", javax.xml.stream.util;uses:="javax.xml.stream,javax.xml.stream.events,javax.xml.namespace";version="0.0.0.1_007_JavaSE", javax.xml.stream;uses:="javax.xml.stream.events,javax.xml.namespace,javax.xml.stream.util,javax.xml.transform";version="0.0.0.1_007_JavaSE", javax.xml.transform.dom;uses:="javax.xml.transform,org.w3c.dom";version="0.0.0.1_007_JavaSE", javax.xml.transform.sax;uses:="org.xml.sax.ext,javax.xml.transform,org.xml.sax,javax.xml.transform.stream";version="0.0.0.1_007_JavaSE", javax.xml.transform.stax;uses:="javax.xml.stream,javax.xml.transform,javax.xml.stream.events";version="0.0.0.1_007_JavaSE", javax.xml.transform.stream;uses:="javax.xml.transform";version="0.0.0.1_007_JavaSE", javax.xml.transform;version="0.0.0.1_007_JavaSE", javax.xml.validation;uses:="org.w3c.dom.ls,javax.xml.transform,javax.xml.transform.stream,org.xml.sax,org.w3c.dom";version="0.0.0.1_007_JavaSE", javax.xml.ws.handler.soap;uses:="javax.xml.ws.handler,javax.xml.namespace,javax.xml.soap,javax.xml.bind";version="0.0.0.1_007_JavaSE", javax.xml.ws.handler;uses:="javax.xml.ws,javax.xml.namespace";version="0.0.0.1_007_JavaSE", javax.xml.ws.http;uses:="javax.xml.ws";version="0.0.0.1_007_JavaSE", javax.xml.ws.soap;uses:="javax.xml.ws.spi,javax.xml.ws,javax.xml.soap";version="0.0.0.1_007_JavaSE", javax.xml.ws.spi.http;version="0.0.0.1_007_JavaSE", javax.xml.ws.spi;uses:="javax.xml.ws,javax.xml.ws.wsaddressing,javax.xml.transform,org.w3c.dom,javax.xml.namespace,javax.xml.ws.handler,javax.xml.bind";version="0.0.0.1_007_JavaSE", javax.xml.ws.wsaddressing;uses:="javax.xml.bind.annotation,javax.xml.namespace,org.w3c.dom,javax.xml.transform,javax.xml.bind,javax.xml.ws,javax.xml.ws.spi";version="0.0.0.1_007_JavaSE", javax.xml.ws;uses:="javax.xml.ws.handler,javax.xml.ws.spi,javax.xml.ws.spi.http,javax.xml.transform,org.w3c.dom,javax.xml.bind.annotation,javax.xml.transform.stream,javax.xml.bind,javax.xml.namespace";version="0.0.0.1_007_JavaSE", javax.xml.xpath;uses:="org.xml.sax,javax.xml.namespace";version="0.0.0.1_007_JavaSE", javax.xml;version="0.0.0.1_007_JavaSE", org.ietf.jgss;version="0.0.0.1_007_JavaSE", org.omg.CORBA.DynAnyPackage;uses:="org.omg.CORBA";version="0.0.0.1_007_JavaSE", org.omg.CORBA.ORBPackage;uses:="org.omg.CORBA";version="0.0.0.1_007_JavaSE", org.omg.CORBA.TypeCodePackage;uses:="org.omg.CORBA";version="0.0.0.1_007_JavaSE", org.omg.CORBA.portable;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable";version="0.0.0.1_007_JavaSE", org.omg.CORBA;uses:="org.omg.CORBA.portable,org.omg.CORBA.DynAnyPackage,org.omg.CORBA.ORBPackage,org.omg.CORBA_2_3.portable,org.omg.CORBA.TypeCodePackage";version="0.0.0.1_007_JavaSE", org.omg.CORBA_2_3.portable;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.CORBA_2_3;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.CosNaming.NamingContextExtPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.CosNaming.NamingContextPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CosNaming";version="0.0.0.1_007_JavaSE", org.omg.CosNaming;uses:="org.omg.CORBA.portable,org.omg.CORBA,org.omg.PortableServer,org.omg.CosNaming.NamingContextPackage,org.omg.CosNaming.NamingContextExtPackage";version="0.0.0.1_007_JavaSE", org.omg.Dynamic;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.DynamicAny.DynAnyFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.DynamicAny.DynAnyPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.DynamicAny;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.DynamicAny.DynAnyFactoryPackage,org.omg.DynamicAny.DynAnyPackage";version="0.0.0.1_007_JavaSE", org.omg.IOP.CodecFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.IOP.CodecPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.IOP;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP.CodecFactoryPackage,org.omg.IOP.CodecPackage";version="0.0.0.1_007_JavaSE", org.omg.Messaging;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.PortableInterceptor.ORBInitInfoPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.PortableInterceptor;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP,org.omg.PortableInterceptor.ORBInitInfoPackage,org.omg.CORBA_2_3.portable,org.omg.Dynamic";version="0.0.0.1_007_JavaSE", org.omg.PortableServer.CurrentPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.PortableServer.POAManagerPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.PortableServer.POAPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.PortableServer.ServantLocatorPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.PortableServer.portable;uses:="org.omg.CORBA,org.omg.PortableServer";version="0.0.0.1_007_JavaSE", org.omg.PortableServer;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.PortableServer.CurrentPackage,org.omg.PortableServer.POAManagerPackage,org.omg.PortableServer.POAPackage,org.omg.PortableServer.portable,org.omg.CORBA_2_3,org.omg.PortableServer.ServantLocatorPackage";version="0.0.0.1_007_JavaSE", org.omg.SendingContext;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", org.omg.stub.java.rmi;uses:="javax.rmi.CORBA";version="0.0.0.1_007_JavaSE", org.w3c.dom.bootstrap;uses:="org.w3c.dom";version="0.0.0.1_007_JavaSE", org.w3c.dom.events;uses:="org.w3c.dom,org.w3c.dom.views";version="0.0.0.1_007_JavaSE", org.w3c.dom.ls;uses:="org.w3c.dom,org.w3c.dom.events,org.w3c.dom.traversal";version="0.0.0.1_007_JavaSE", org.w3c.dom;version="0.0.0.1_007_JavaSE", org.xml.sax.ext;uses:="org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_007_JavaSE", org.xml.sax.helpers;uses:="org.xml.sax";version="0.0.0.1_007_JavaSE", org.xml.sax;version="0.0.0.1_007_JavaSE"
+----
+
+You should now be able to start Felix, and deploy all the jars listed on
+this page. You should see output similar to this on the console, using
+the `felix:lb` command:
+
+....
+    ID|State      |Level|Name
+    0|Active     |    0|System Bundle (4.4.1)
+    1|Active     |    1|ASM (5.0.1)
+    2|Active     |    1|ASM commons classes (5.0.1)
+    3|Active     |    1|ASM Tree class visitor (5.0.1)
+    4|Active     |    1|geronimo-jta_1.1_spec (1.1.1)
+    5|Active     |    1|javax.annotation API (1.2.0)
+    6|Active     |    1|javax.mail bundle from Glassfish (1.4.1.v201005082020)
+    7|Active     |    1|Java Server Pages Standard Tag Library API Bundle (1.2.0.v201105211821)
+    8|Active     |    1|JavaServer Pages (TM) TagLib Implementation (1.2.2)
+    9|Active     |    1|Jetty :: Servlet Annotations (9.2.4.SNAPSHOT)
+   10|Active     |    1|Jetty :: Deployers (9.2.4.SNAPSHOT)
+   11|Active     |    1|Jetty :: Http Utility (9.2.4.SNAPSHOT)
+   12|Active     |    1|Jetty :: IO Utility (9.2.4.SNAPSHOT)
+   13|Active     |    1|Jetty :: JNDI Naming (9.2.4.SNAPSHOT)
+   14|Active     |    1|Jetty :: OSGi :: Boot (9.2.4.SNAPSHOT)
+   15|Resolved   |    1|Jetty-OSGi-Jasper Integration (9.2.4.SNAPSHOT)
+   16|Active     |    1|Jetty Servlet API and Schemas for OSGi (3.1.0.SNAPSHOT)
+   17|Active     |    1|Jetty :: Plus (9.2.4.SNAPSHOT)
+   18|Active     |    1|Jetty :: Security (9.2.4.SNAPSHOT)
+   19|Active     |    1|Jetty :: Server Core (9.2.4.SNAPSHOT)
+   20|Active     |    1|Jetty :: Servlet Handling (9.2.4.SNAPSHOT)
+   21|Active     |    1|Jetty :: Utility Servlets and Filters (9.2.4.SNAPSHOT)
+   22|Active     |    1|Jetty :: Utilities (9.2.4.SNAPSHOT)
+   23|Active     |    1|Jetty :: Webapp Application Support (9.2.4.SNAPSHOT)
+   24|Active     |    1|Jetty :: XML utilities (9.2.4.SNAPSHOT)
+   25|Active     |    1|Apache Aries SPI Fly Dynamic Weaving Bundle (1.0.1)
+   26|Active     |    1|Apache Aries Util (1.0.0)
+   27|Active     |    1|Apache Felix Bundle Repository (2.0.2)
+   28|Active     |    1|Apache Felix Configuration Admin Service (1.8.0)
+   29|Active     |    1|Apache Felix EventAdmin (1.3.2)
+   30|Active     |    1|Apache Felix Gogo Command (0.14.0)
+   31|Active     |    1|Apache Felix Gogo Runtime (0.12.1)
+   32|Active     |    1|Apache Felix Gogo Shell (0.10.0)
+   33|Active     |    1|Apache Felix Log Service (1.0.1)
+   34|Active     |    1|Jetty :: Apache JSP (9.2.4.SNAPSHOT)
+   35|Active     |    1|Eclipse Compiler for Java(TM) (3.8.2.v20130121-145325)
+   36|Active     |    1|Mortbay EL API and Implementation (8.0.9)
+   37|Active     |    1|Mortbay Jasper (8.0.9)
+....
+
+===== Eclipse
+
+The jetty OSGi integration has been successfully tested against
+https://www.eclipse.org/equinox/[Equinox] Mars RC1.
+
+Ensure that these services are present:
+
+* https://www.eclipse.org/equinox/bundles/[Configuration Admin]
+* https://www.eclipse.org/equinox/bundles/[Event Admin]
+
+====== Eclipse Update Site
+
+There is a list of Eclipse P2 sites for the jetty releases maintained at
+http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/
+
+Each P2 repo has one big feature group that defines most of the jetty
+jars. *Beware: No 3rd party dependency jars are included, so you will
+need to have installed the dependencies listed previously in this
+document.*
+
+In addition, as the feature group includes websocket, you will need to
+download and have installed the javax.websocket-api jar:
+
+.Extra Jars Required for Websocket
+[cols=",,",options="header",]
+|=======================================================================
+|Jar |Bundle Symbolic Name |Location
+|javax.websocket-api |javax.websocket-api
+|http://central.maven.org/maven2/javax/websocket/websocket-api[Maven
+central]
+|=======================================================================
diff --git a/jetty-documentation/src/main/asciidoc/development/frameworks/spring-usage.adoc b/jetty-documentation/src/main/asciidoc/development/frameworks/spring-usage.adoc
new file mode 100644
index 0000000..660e199
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/frameworks/spring-usage.adoc
@@ -0,0 +1,102 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[framework-jetty-spring]]
+=== Spring Setup
+
+You can assemble and configure Jetty in code or with almost any IoC style framework including Spring.
+If all you want to do is setup a Jetty server in your stock Spring usage, simply look at the xml snippet below as an example.
+If you want to replace the jetty-xml being used to start the normal Jetty distribution with spring, you may do so however currently it will not leverage the rest of the module system.
+
+==== Jetty-Spring Module
+
+The skeleton of a jetty spring module can be enabled from the jetty-distribution via the link:#startup-modules[module mechanism].
+For example:
+
+[source, screen, subs="{sub-order}"]
+....
+$ java -jar start.jar --add-to-startd=spring
+....
+
+This (or the alternative link:#start-jar[--add-to-start]=spring command) creates a `${jetty.home}/lib/spring` directory and populates it with the jetty-spring integration jar.
+It does NOT supply the spring jars and their dependencies.
+You will need to download these and place them into jetty's classpath - you can use the `${jetty.home}/lib/spring` directory created by spring.mod for this purpose.
+
+==== Using Spring to Configure Jetty
+
+Configuring Jetty via Spring is simply a matter of calling the API as Spring beans.
+The following is an example mimicking the default jetty startup configuration.
+
+[source, xml, subs="{sub-order}"]
+----
+            
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+
+<!-- =============================================================== -->
+<!-- Configure the Jetty Server with Spring                          -->
+<!-- This file is the similar to jetty.xml, but written in spring    -->
+<!-- XmlBeanFactory format.                                          -->
+<!-- =============================================================== -->
+
+<beans>
+    <bean id="contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
+    <bean id="server" name="Main" class="org.eclipse.jetty.server.Server" init-method="start" destroy-method="stop">
+        <constructor-arg>
+            <bean id="threadPool" class="org.eclipse.jetty.util.thread.QueuedThreadPool">
+                <property name="minThreads" value="10"/>
+                <property name="maxThreads" value="50"/>
+            </bean>
+        </constructor-arg>
+        <property name="connectors">
+            <list>
+                <bean id="connector" class="org.eclipse.jetty.server.ServerConnector">
+                    <constructor-arg ref="server"/>
+                    <property name="port" value="8080"/>
+                </bean>
+            </list>
+        </property>
+        <property name="handler">
+            <bean id="handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
+            <property name="handlers">
+                    <list>
+                        <ref bean="contexts"/>
+                        <bean id="defaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
+                    </list>
+                </property>
+            </bean>
+        </property>
+        <property name="beans">
+            <list>
+                <bean id="deploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
+                    <property name="contexts" ref="contexts"/>
+                    <property name="appProviders">
+                        <list>
+                            <bean id="webAppProvider" class="org.eclipse.jetty.deploy.providers.WebAppProvider">
+                                <property name="monitoredDirName" value="webapps"/>
+                                <property name="scanInterval" value="1"/>
+                                <property name="extractWars" value="true"/>
+                            </bean>
+                        </list>
+                    </property>
+                </bean>
+            </list>
+        </property>
+    </bean>
+</beans>
+
+        
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc b/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc
new file mode 100644
index 0000000..1211e96
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc
@@ -0,0 +1,81 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[framework-weld]]
+=== Weld
+
+http://seamframework.org/Weld[Weld] can be used to add support for CDI (Contexts and Dependency Injection) to Servlets, Listeners and Filters.
+It is easily configured with Jetty 9.
+
+[[weld-setup-distro]]
+==== Weld Setup
+
+The easiest way to configure weld is within the jetty distribution itself:
+
+1.  Enable the startup link:#startup-modules[module] called "cdi".
+2.  Ensure your `WEB-INF/web.xml` contains the following:
++
+[source, xml, subs="{sub-order}"]
+----
+  <listener>
+    <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
+  </listener>
+
+  <resource-env-ref>
+    <description>Object factory for the CDI Bean Manager</description>
+    <resource-env-ref-name>BeanManager</resource-env-ref-name>
+    <resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
+  </resource-env-ref>
+----
+
+That should be it so when you start up your jetty distribution with the webapp you should see output similar to the following (providing your logging is the default configuration):
+
+[source, screen, subs="{sub-order}"]
+....
+2015-06-18 12:13:54.924:INFO::main: Logging initialized @485ms
+2015-06-18 12:13:55.231:INFO:oejs.Server:main: jetty-9.3.1-SNAPSHOT
+2015-06-18 12:13:55.264:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///tmp/cdi-demo/webapps/] at interval 1
+2015-06-18 12:13:55.607:WARN:oeja.AnnotationConfiguration:main: ServletContainerInitializers: detected. Class hierarchy: empty
+Jun 18, 2015 12:13:55 PM org.jboss.weld.environment.servlet.EnhancedListener onStartup
+INFO: WELD-ENV-001008: Initialize Weld using ServletContainerInitializer
+Jun 18, 2015 12:13:55 PM org.jboss.weld.bootstrap.WeldStartup <clinit>
+INFO: WELD-000900: 2.2.9 (Final)
+Jun 18, 2015 12:13:55 PM org.jboss.weld.environment.servlet.deployment.WebAppBeanArchiveScanner scan
+WARN: WELD-ENV-001004: Found both WEB-INF/beans.xml and WEB-INF/classes/META-INF/beans.xml. It's not portable to use both locations at the same time. Weld is going to use file:/tmp/jetty-0.0.0.0-8080-cdi-webapp.war-_cdi-webapp-any-8161614308407422636.dir/webapp/WEB-INF/beans.xml.
+Jun 18, 2015 12:13:55 PM org.jboss.weld.bootstrap.WeldStartup startContainer
+INFO: WELD-000101: Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously.
+Jun 18, 2015 12:13:55 PM org.jboss.weld.interceptor.util.InterceptionTypeRegistry <clinit>
+WARN: WELD-001700: Interceptor annotation class javax.ejb.PostActivate not found, interception based on it is not enabled
+Jun 18, 2015 12:13:55 PM org.jboss.weld.interceptor.util.InterceptionTypeRegistry <clinit>
+WARN: WELD-001700: Interceptor annotation class javax.ejb.PrePassivate not found, interception based on it is not enabled
+Jun 18, 2015 12:13:56 PM org.jboss.weld.bootstrap.MissingDependenciesRegistry handleResourceLoadingException
+Jun 18, 2015 12:13:56 PM org.jboss.weld.environment.servlet.WeldServletLifecycle findContainer
+INFO: WELD-ENV-001002: Container detection skipped - custom container class loaded: org.jboss.weld.environment.jetty.JettyContainer.
+Jun 18, 2015 12:13:56 PM org.jboss.weld.environment.jetty.JettyContainer initialize
+INFO: WELD-ENV-001200: Jetty 7.2+ detected, CDI injection will be available in Servlets and Filters. Injection into Listeners should work on Jetty 9.1.1 and newer.
+Jun 18, 2015 12:13:56 PM org.jboss.weld.environment.servlet.Listener contextInitialized
+INFO: WELD-ENV-001006: org.jboss.weld.environment.servlet.EnhancedListener used for ServletContext notifications
+Jun 18, 2015 12:13:56 PM org.jboss.weld.environment.servlet.EnhancedListener contextInitialized
+INFO: WELD-ENV-001009: org.jboss.weld.environment.servlet.Listener used for ServletRequest and HttpSession notifications
+2015-06-18 12:13:56.535:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@6574b225{/cdi-webapp,file:///tmp/jetty-0.0.0.0-8080-cdi-webapp.war-_cdi-webapp-any-8161614308407422636.dir/webapp/,AVAILABLE}{/cdi-webapp.war}
+2015-06-18 12:13:56.554:INFO:oejs.ServerConnector:main: Started ServerConnector@7112f81c{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
+2015-06-18 12:13:56.587:INFO:oejus.SslContextFactory:main: x509={jetty.eclipse.org=jetty} wild={} alias=null for SslContextFactory@3214ee6(file:///tmp/cdi-demo/etc/keystore,file:///tmp/cdi-demo/etc/keystore)
+2015-06-18 12:13:56.821:INFO:oejs.ServerConnector:main: Started ServerConnector@69176a9b{SSL,[ssl, http/1.1]}{0.0.0.0:8443}
+2015-06-18 12:13:56.822:INFO:oejs.Server:main: Started @2383ms
+
+....
+
+For use with the jetty-maven-plugin, the best idea is to make the org.jboss.weld.servlet:weld-servlet artifact a _plugin_ dependency (__not__ a webapp dependency), then follow step 2 above.
diff --git a/jetty-documentation/src/main/asciidoc/development/handlers/chapter.adoc b/jetty-documentation/src/main/asciidoc/development/handlers/chapter.adoc
new file mode 100644
index 0000000..4f03f30
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/handlers/chapter.adoc
@@ -0,0 +1,20 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-handlers]]
+== Handlers
+
+include::writing-custom-handlers.adoc[]
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/development/handlers/writing-custom-handlers.adoc b/jetty-documentation/src/main/asciidoc/development/handlers/writing-custom-handlers.adoc
new file mode 100644
index 0000000..8ceff45
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/handlers/writing-custom-handlers.adoc
@@ -0,0 +1,174 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[writing-custom-handlers]]
+=== Writing Custom Handlers
+
+The Handler is the Jetty component that deals with received requests.
+
+Many users of Jetty never need to write a Jetty Handler, but instead use the link:{JXURL}/org/eclipse/jetty/servlet/package-summary.html[Servlet API.]
+You can reuse the existing Jetty handlers for context, security, sessions and servlets without the need for extension.
+However, some users might have special requirements or footprint concerns that prohibit the use of the full servlet API.
+For them implementing a Jetty handler is a straight forward way to provide dynamic web content with a minimum of fuss.
+
+See the section on xref:basic-architecture[] to understand more about Handlers vs. Servlets.
+
+[[handler-api]]
+==== The Handler API
+
+The link:{JDURL}/org/eclipse/jetty/server/Handler.html[Handler] interface provides Jetty's core of content generation or manipulation.
+Classes that implement this interface are used to coordinate requests, filter requests and generate content.
+
+The core API of the Handler interface is:
+
+[source, java, subs="{sub-order}"]
+----
+public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+    throws IOException, ServletException
+----
+
+An implementation of this method can handle a request and pass the request onto another handler (or servlet), or it can modify and/or wrap the request before passing it on.
+This gives three styles of handler:
+
+* Coordinating Handlers - Handlers that route requests to other handlers (`HandlerCollection`, `ContextHandlerCollection`)
+* Filtering Handlers - Handlers that augment a request and pass it on to other handlers (`HandlerWrapper`, `ContextHandler`, `SessionHandler`)
+* Generating Handlers - Handlers that produce content (`ResourceHandler` and `ServletHandler`)
+
+[[target]]
+===== The Target
+
+The target of a handler is an identifier for the resource that should handle the passed request.
+This is normally the URI that is parsed from an HTTP Request.
+However, in two key circumstances the target may differ from the URI of the passed request:
+
+* If the request has been dispatched to a named resource, such as a named servlet, the target is the name of that resource.
+* If the request is being made by a call to link:http://docs.oracle.com/javaee/7/api/javax/servlet/RequestDispatcher.html[`RequestDispatcher`], the target is the URI of the included resource and is different to the URI of the actual request.
+
+[[request-and-response]]
+===== The Request and Response
+
+The request and response objects used in the signature of the handle method are
+link:http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html[`ServletRequest`] and link:http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html[`ServletResponse`].
+These are the standard APIs and are moderately restricted in what they can do to the request and response.
+More often than not, access to the Jetty implementations of these classes is required: link:{JDURL}/org/eclipse/jetty/server/Request.html[`Request`] and link:{JDURL}/org/eclipse/jetty/server/Response.html[`Response`].
+However, as the request and response may be wrapped by handlers, filters and servlets, it is not possible to pass the implementation directly.
+The following mantra retrieves the core implementation objects from under any wrappers:
+
+[source, java, subs="{sub-order}"]
+----
+Request base_request = request instanceof Request ? (Request)request : HttpConnection.getCurrentConnection().getHttpChannel().getRequest();
+Response base_response = response instanceof Response ? (Response)response : HttpConnection.getCurrentConnection().getHttpChannel().getResponse();
+----
+
+Notice that if the handler passes the request on to another handler, it should use the Request/Response objects passed in, and not the base objects.
+This is to preserve any wrapping done by up stream handlers.
+
+[[dispatch]]
+===== The Dispatch
+
+The dispatch argument indicates the state of the handling of the call and may be:
+
+* `REQUEST == 1` - An original request received from a connector.
+* `FORWARD == 2` - A request being forwarded by a RequestDispatcher.
+* `INCLUDE == 4` - A request being included by a RequestDispatcher.
+* `ERROR == 8` - A request being forwarded to a error handler by the container.
+
+These mostly have significance for servlet and related handlers.
+For example, the security handler only applies authentication and authorization to REQUEST dispatches.
+
+[[handling-requests]]
+==== Handling Requests
+
+A Handler may handle a request by:
+
+* xref:generating-response[]
+* xref:filtering-request-or-response[]
+* xref:passing-request-and-response[]
+
+[[generating-response]]
+===== Generating a Response
+
+The link:{JDURL}/org/eclipse/jetty/embedded/OneHandler.html[`OneHandler`] embedded example shows how a simple handler can generate a response.
+
+You can use the standard servlet response API, which will typically set some status, content headers and then write out the content:
+
+[source, java, subs="{sub-order}"]
+----
+ response.setContentType("text/html");
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.getWriter().println("<h1>Hello OneHandler</h1>");
+----
+
+It is also very important that a handler indicate that it has completed handling the request and that the request should not be passed to other handlers:
+
+[source, java, subs="{sub-order}"]
+----
+ Request base_request = (request instanceof Request) ? (Request)request:HttpConnection.getCurrentConnection().getHttpChannel().getRequest();
+ base_request.setHandled(true);
+----
+
+[[filtering-request-or-response]]
+===== Filtering the Request and/or Response
+
+Once the base request or response object is obtained, you can modify it.
+Typically you would make modifications to accomplish:
+
+* Breaking the URI into contextPath, servletPath and pathInfo components.
+* Associating a resource base with a request for static content.
+* Associating a session with a request.
+* Associating a security principal with a request.
+* Changing the URI and paths during a request dispatch forward to another resource.
+
+You can also update the context of the request:
+
+* Setting the current threads context classloader.
+* Setting thread locals to identify the current `ServletContext`.
+
+Typically Jetty passes a modified request to another handler and undoes modifications in a finally block afterwards:
+
+[source, java, subs="{sub-order}"]
+----
+ try
+ {
+    base_request.setSession(a_session);
+    next_handler.handle(target,request,response,dispatch);
+ }
+ finally
+ {
+    base_request.setSession(old_session);
+ }
+----
+
+The classes that implement the link:{JDURL}/org/eclipse/jetty/server/handler/HandlerWrapper.html[`HandlerWrapper`] class are typically handler filters of this style.
+
+[[passing-request-and-response]]
+===== Passing the Request and Response to Another Handler
+
+A handler might simply inspect the request and use the target, request URI or other information to select another handler to pass the request to.
+These handlers typically implement the link:{JDURL}/org/eclipse/jetty/server/HandlerContainer.html[`HandlerContainer`] interface.
+
+Examples include:
+
+* link:{JDURL}/org/eclipse/jetty/server/handler/HandlerCollection.html[Class `HandlerCollection`] -
+A collection of handlers, where each handler is called regardless of the state of the request.
+This is typically used to pass a request to a link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandlerCollection.html[`ContextHandlerCollection`,] and then the link:{JDURL}/org/eclipse/jetty/server/handler/RequestLogHandler.html[`RequestLogHandler`.]
+* link:{JDURL}/org/eclipse/jetty/server/handler/HandlerList.html[`HandlerList`] - A list of handlers that are called in turn until the request state is set as handled.
+* link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandlerCollection.html[`ContextHandlerCollection`] - A collection of Handlers, of which one is selected by best match for the context path.
+
+[[more-about-handlers]]
+==== More About Handlers
+
+See the link:{JXURL}/[latest Jetty Source XRef] and the link:{JDURL}/[latest Jetty JavaDoc] for detailed information on each Jetty handler.
diff --git a/jetty-documentation/src/main/asciidoc/development/maven/chapter.adoc b/jetty-documentation/src/main/asciidoc/development/maven/chapter.adoc
new file mode 100644
index 0000000..beda6d1
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/maven/chapter.adoc
@@ -0,0 +1,25 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[maven-and-jetty]]
+== Maven and Jetty
+
+This chapter explains how to use Jetty with Maven and the Jetty Maven plugin.
+
+include::jetty-maven-helloworld.adoc[]
+include::jetty-maven-plugin.adoc[]
+include::jetty-maven-scanning.adoc[]
+include::jetty-jspc-maven-plugin.adoc[]
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/development/maven/jetty-jspc-maven-plugin.adoc b/jetty-documentation/src/main/asciidoc/development/maven/jetty-jspc-maven-plugin.adoc
new file mode 100644
index 0000000..07c2df8
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/maven/jetty-jspc-maven-plugin.adoc
@@ -0,0 +1,239 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-jspc-maven-plugin]]
+=== Jetty Jspc Maven Plugin
+
+This plugin will help you pre-compile your jsps and works in conjunction with the Maven war plugin to put them inside an assembled war.
+
+[[jspc-config]]
+==== Configuration
+
+Here's the basic setup required to put the jspc plugin into your build:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+   <artifactId>jetty-jspc-maven-plugin</artifactId>
+   <version>{VERSION}</version>
+   <executions>
+     <execution>
+       <id>jspc</id>
+       <goals>
+         <goal>jspc</goal>
+       </goals>
+       <configuration>
+       </configuration>
+     </execution>
+   </executions>
+ </plugin>
+----
+
+The configurable parameters are as follows:
+
+webXmlFragment::
+Default value: `$\{project.basedir}/target/webfrag.xml`
++
+File into which to generate the servlet declarations.
+Will be merged with an existing `web.xml`.
+webAppSourceDirectory::
+Default value: `$\{project.basedir}/src/main/webapp`
++
+Root of resources directory where jsps, tags etc are located.
+webXml::
+Default value: `$\{project.basedir}/src/main/webapp/WEB-INF/web.xml`
++
+The web.xml file to use to merge with the generated fragments.
+includes::
+Default value: `**\/*.jsp, **\/*.jspx`
++
+The comma separated list of patterns for file extensions to be processed.
+excludes::
+Default value: `**\/.svn\/**`
++
+The comma separated list of patterns for file extensions to be skipped.
+classesDirectory::
+Default value: `$\{project.build.outputDirectory}`
++
+Location of classes for the webapp.
+generatedClasses::
+Default value: `$\{project.build.outputDirectory}`
++
+Location to put the generated classes for the jsps.
+insertionMarker::
+Default value: _none_
++
+A marker string in the src `web.xml` file which indicates where to merge in the generated web.xml fragment.
+Note that the marker string will NOT be preserved during the insertion.
+Can be left blank, in which case the generated fragment is inserted just before the line containing `</web-app>`.
+useProvidedScope::
+Default value: false
++
+If true, jars of dependencies marked with <scope>provided</scope> will be placed on the compilation classpath.
+mergeFragment::
+Default value: true
++
+Whether or not to merge the generated fragment file with the source web.xml.
+The merged file will go into the same directory as the webXmlFragment.
+keepSources::
+Default value: false
++
+If true, the generated .java files are not deleted at the end of processing.
+sourceVersion::
+Introduced in Jetty 9.3.6.
+Java version of jsp source files.
+Defaults to 1.7.
+targetVersion::
+Introduced in Jetty 9.3.6.
+Java version of class files generated from jsps.
+Defaults to 1.7.
+tldJarNamePatterns::
+Default value: `.*taglibs[^/]*\.jar|.*jstl-impl[^/]*\.jar$`
++
+Patterns of jars on the 'system' (ie container) path that contain tlds.
+Use | to separate each pattern.
+jspc::
+Default value: the `org.apache.jasper.JspC` instance being configured.
++
+The JspC class actually performs the pre-compilation.
+All setters on the JspC class are available.
+You can download the javadoc http://central.maven.org/maven2/org/glassfish/web/javax.servlet.jsp/2.3.2/javax.servlet.jsp-2.3.2-javadoc.jar[here].
+
+Taking all the default settings, here's how to configure the war plugin to use the generated `web.xml` that includes all of the jsp servlet declarations:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.apache.maven.plugins</groupId>
+  <artifactId>maven-war-plugin</artifactId>
+  <configuration>
+    <webXml>${project.basedir}/target/web.xml</webXml>
+  </configuration>
+</plugin>
+----
+
+[[jspc-production-precompile]]
+==== Precompiling only for Production Build
+
+As compiling jsps is usually done during preparation for a production release and not usually done during development, it is more convenient to put the plugin setup inside a <profile> which which can be deliberately invoked during prep for production.
+
+For example, the following profile will only be invoked if the flag `-Dprod` is present on the run line:
+
+[source, xml, subs="{sub-order}"]
+----
+<profiles>
+    <profile>
+      <id>prod</id>
+      <activation>
+        <property><name>prod</name></property>
+      </activation>
+      <build>
+      <plugins>
+        <plugin>
+          <groupId>org.eclipse.jetty</groupId>
+          <artifactId>jetty-jspc-maven-plugin</artifactId>
+          <version>{VERSION}</version>
+          <!-- put your configuration in here -->
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-war-plugin</artifactId>
+          <!-- put your configuration in here -->
+        </plugin>
+      </plugins>
+      </build>
+    </profile>
+  </profiles>
+----
+
+The following invocation would cause your code to be compiled, the jsps to be compiled, the <servlet> and <servlet-mapping>s inserted in the `web.xml` and your webapp assembled into a war:
+
+[source, screen, subs="{sub-order}"]
+....
+$ mvn -Dprod package
+....
+
+[[jspc-overlay-precompile]]
+==== Precompiling Jsps with Overlaid Wars
+
+Precompiling jsps with an overlaid war requires a bit more configuration.
+This is because you need to separate the steps of unpacking the overlaid war and then repacking the final target war so the jetty-jspc-maven-plugin has the opportunity to access the overlaid resources.
+
+In the example we'll show, we will use an overlaid war.
+The overlaid war will provide the `web.xml` file but the jsps will be in `src/main/webapp` (i.e. part of the project that uses the overlay).
+We will unpack the overlaid war file, compile the jsps and merge their servlet definitions into the extracted `web.xml`, then pack everything into a war.
+
+Here's an example configuration of the war plugin that separate those phases into an unpack phase, and then a packing phase:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+    <artifactId>maven-war-plugin</artifactId>
+    <executions>
+      <execution>
+        <id>unpack</id>
+        <goals><goal>exploded</goal></goals>
+        <phase>generate-resources</phase>
+        <configuration>
+          <webappDirectory>target/foo</webappDirectory>
+          <overlays>
+            <overlay />
+            <overlay>
+              <groupId>org.eclipse.jetty</groupId>
+              <artifactId>test-jetty-webapp</artifactId>
+            </overlay>
+          </overlays>
+        </configuration>
+      </execution>
+      <execution>
+        <id>pack</id>
+        <goals><goal>war</goal></goals>
+        <phase>package</phase>
+        <configuration>
+          <warSourceDirectory>target/foo</warSourceDirectory>
+          <webXml>target/web.xml</webXml>
+        </configuration>
+      </execution>
+    </executions>
+</plugin>
+----
+
+Now you also need to configure the `jetty-jspc-maven-plugin` so that it can use the web.xml that was extracted by the war unpacking and merge in the generated definitions of the servlets.
+This is in `target/foo/WEB-INF/web.xml`.
+Using the default settings, the `web.xml` merged with the jsp servlet definitions will be put into `target/web.xml`.
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+    <groupId>org.eclipse.jetty</groupId>
+     <artifactId>jetty-jspc-maven-plugin</artifactId>
+     <version>{VERSION}</version>
+     <executions>
+       <execution>
+         <id>jspc</id>
+         <goals>
+           <goal>jspc</goal>
+         </goals>
+         <configuration>
+            <webXml>target/foo/WEB-INF/web.xml</webXml>
+            <includes>**/*.foo</includes>
+            <excludes>**/*.fff</excludes>
+        </configuration>
+      </execution>
+    </executions>
+</plugin>
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/maven/jetty-maven-helloworld.adoc b/jetty-documentation/src/main/asciidoc/development/maven/jetty-maven-helloworld.adoc
new file mode 100644
index 0000000..c6f2cce
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/maven/jetty-maven-helloworld.adoc
@@ -0,0 +1,323 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-maven-helloworld]]
+=== Using Maven
+
+http://maven.apache.org/[Apache Maven] is a software project management and comprehension tool.
+Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.
+
+It is an ideal tool to build a web application project, and such projects can use the link:#jetty-maven-plugin[jetty-maven-plugin] to easily run the web application and save time in development.
+You can also use Maven to build, test and run a project which embeds Jetty.
+
+____
+[NOTE]
+Use of Maven and the jetty-maven-plugin is *not* required.
+Using Maven for Jetty implementations is a popular choice, but users encouraged to manage their projects in whatever way suits their needs.
+Other popular tools include Ant and Gradle. 
+____
+
+First we'll have a look at a very simple HelloWorld java application that embeds Jetty, then a simple webapp which makes use of the link:#jetty-maven-plugin[jetty-maven-plugin] to speed up the development cycle.
+
+[[configuring-embedded-jetty-with-maven]]
+==== Using Embedded Jetty with Maven
+
+To understand the basic operations of building and running against Jetty, first review:
+
+* link:#advanced-embedding[Embedding with Jetty]
+* link:#jetty-helloworld[Jetty HelloWorld example]
+
+Maven uses convention over configuration, so it is best to use the project structure Maven recommends.
+You can use _link:#archetypes[http://maven.apache.org/guides/introduction/introduction-to-archetypes.html[archetypes]]_ to quickly setup Maven projects, but we will set up the structure manually for this simple tutorial example:
+
+[source, screen, subs="{sub-order}"]
+....
+> mkdir JettyMavenHelloWorld
+> cd JettyMavenHelloWorld
+> mkdir -p src/main/java/org/example
+....
+
+[[creating-helloworld-class]]
+===== Creating the HelloWorld Class
+
+Use an editor to create the file `src/main/java/org/example/HelloWorld.java` with the following contents:
+
+[source, java, subs="{sub-order}"]
+----
+package org.example;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+import java.io.IOException;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+
+public class HelloWorld extends AbstractHandler
+{
+    public void handle(String target,
+                       Request baseRequest,
+                       HttpServletRequest request,
+                       HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        response.setContentType("text/html;charset=utf-8");
+        response.setStatus(HttpServletResponse.SC_OK);
+        baseRequest.setHandled(true);
+        response.getWriter().println("<h1>Hello World</h1>");
+    }
+
+    public static void main(String[] args) throws Exception
+    {
+        Server server = new Server(8080);
+        server.setHandler(new HelloWorld());
+
+        server.start();
+        server.join();
+    }
+}
+----
+
+[[creating-embedded-pom-descriptor]]
+===== Creating the POM Descriptor
+
+The `pom.xml` file declares the project name and its dependencies.
+Use an editor to create the file `pom.xml` in the `JettyMavenHelloWorld` directory with the following contents:
+
+[source, xml, subs="{sub-order}"]
+----
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.example</groupId>
+  <artifactId>hello-world</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>jar</packaging>
+  <name>Jetty HelloWorld</name>
+
+  <properties>
+      <!-- Adapt this to a version found on
+           http://central.maven.org/maven2/org/eclipse/jetty/jetty-maven-plugin/
+        -->
+      <jettyVersion>9.3.9.v20160517</jettyVersion>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-server</artifactId>
+      <version>${jettyVersion}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>exec-maven-plugin</artifactId>
+        <version>1.1</version>
+        <executions>
+          <execution><goals><goal>java</goal></goals></execution>
+        </executions>
+        <configuration>
+          <mainClass>org.example.HelloWorld</mainClass>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
+----
+
+[[buildng-and-running-embedded-helloworld]]
+===== Building and Running Embedded HelloWorld
+
+You can now compile and execute the HelloWorld class by using these commands:
+
+[source, screen, subs="{sub-order}"]
+....
+> mvn clean compile exec:java
+....
+
+You can point your browser to `http://localhost:8080` to see the _Hello World_ page.
+You can observe what Maven is doing for you behind the scenes by using the `mvn dependency:tree` command, which reveals the transitive dependency resolved and downloaded as:
+
+[source, screen, subs="{sub-order}"]
+....
+> mvn dependency:tree
+[INFO] Scanning for projects...
+...
+[INFO]
+[INFO] ------------------------------------------------------------------------
+[INFO] Building Jetty HelloWorld 0.1-SNAPSHOT
+[INFO] ------------------------------------------------------------------------
+[INFO]
+[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ hello-world ---
+...
+[INFO] org.example:hello-world:jar:0.1-SNAPSHOT
+[INFO] \- org.eclipse.jetty:jetty-server:jar:9.3.9.v20160517:compile
+[INFO]    +- javax.servlet:javax.servlet-api:jar:3.1.0:compile
+[INFO]    +- org.eclipse.jetty:jetty-http:jar:9.3.9.v20160517:compile
+[INFO]    |  \- org.eclipse.jetty:jetty-util:jar:9.3.9.v20160517:compile
+[INFO]    \- org.eclipse.jetty:jetty-io:jar:9.3.9.v20160517:compile
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD SUCCESS
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time: 4.145 s
+[INFO] Finished at: 2016-08-01T13:46:42-04:00
+[INFO] Final Memory: 15M/209M
+[INFO] ------------------------------------------------------------------------
+....
+
+[[developing-standard-webapp-with-jetty-and-maven]]
+==== Developing a Standard WebApp with Jetty and Maven
+
+The previous section demonstrated how to use Maven with an application that embeds Jetty.
+Now we will examine instead how to develop a standard webapp with Maven and Jetty.
+First create the Maven structure (you can use the maven webapp archetype instead if you prefer):
+
+[source, screen, subs="{sub-order}"]
+....
+> mkdir JettyMavenHelloWarApp
+> cd JettyMavenHelloWebApp
+> mkdir -p src/main/java/org/example
+> mkdir -p src/main/webapp/WEB-INF
+....
+
+[[creating-servlet]]
+===== Creating a Servlet
+
+Use an editor to create the file `src/main/java/org/example/HelloServlet.java` with the following contents:
+
+[source, java, subs="{sub-order}"]
+----
+package org.example;
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class HelloServlet extends HttpServlet
+{
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        response.setContentType("text/html");
+        response.setStatus(HttpServletResponse.SC_OK);
+        response.getWriter().println("<h1>Hello Servlet</h1>");
+        response.getWriter().println("session=" + request.getSession(true).getId());
+    }
+}
+----
+
+You need to declare this servlet in the deployment descriptor, so create the file `src/main/webapp/WEB-INF/web.xml` and add the following contents:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app
+   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+   metadata-complete="false"
+   version="3.1">
+
+  <servlet>
+    <servlet-name>Hello</servlet-name>
+    <servlet-class>org.example.HelloServlet</servlet-class>
+  </servlet>
+  <servlet-mapping>
+    <servlet-name>Hello</servlet-name>
+    <url-pattern>/hello/*</url-pattern>
+  </servlet-mapping>
+
+</web-app>
+----
+
+[[creating-plugin-pom-descriptor]]
+===== Creating the POM Descriptor
+
+The `pom.xml` file declares the project name and its dependencies.
+Use an editor to create the file `pom.xml` with the following contents in the `JettyMavenHelloWarApp` directory, noting particularly the declaration of the link:#jetty-maven-plugin[jetty-maven-plugin]:
+
+[source, xml, subs="{sub-order}"]
+----
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.example</groupId>
+  <artifactId>hello-world</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>war</packaging>
+  <name>Jetty HelloWorld WebApp</name>
+
+  <properties>
+      <jettyVersion>{VERSION}</jettyVersion>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <version>3.1.0</version>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-maven-plugin</artifactId>
+        <version>${jettyVersion}</version>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
+----
+
+[[building-and-running-web-application]]
+===== Building and Running the Web Application
+
+Now you can both build and run the web application without needing to assemble it into a war by using the link:#jetty-maven-plugin[jetty-maven-plugin] via the command:
+
+[source, screen, subs="{sub-order}"]
+....
+> mvn jetty:run
+....
+
+You can see the static and dynamic content at `http://localhost:8080/hello`
+
+There are a great deal of configuration options available for the jetty-maven-plugin to help you build and run your webapp.
+The full reference is at link:#jetty-maven-plugin[Configuring the Jetty Maven Plugin].
+
+[[building-war-file]]
+===== Building a WAR file
+
+You can create a Web Application Archive (WAR) file from the project with the command:
+
+[source, screen, subs="{sub-order}"]
+....
+> mvn package
+....
+
+The resulting war file is in the `target` directory and may be deployed on any standard servlet server, including link:#configuring-deployment[Jetty].
diff --git a/jetty-documentation/src/main/asciidoc/development/maven/jetty-maven-plugin.adoc b/jetty-documentation/src/main/asciidoc/development/maven/jetty-maven-plugin.adoc
new file mode 100644
index 0000000..5551ab0
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/maven/jetty-maven-plugin.adoc
@@ -0,0 +1,1129 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-maven-plugin]]
+=== Configuring the Jetty Maven Plugin
+
+The Jetty Maven plugin is useful for rapid development and testing.
+You can add it to any webapp project that is structured according to the Maven defaults.
+The plugin can then periodically scan your project for changes and automatically redeploy the webapp if any are found.
+This makes the development cycle more productive by eliminating the build and deploy steps: you use your IDE to make changes to the project, and the running web container automatically picks them up, allowing you to test them straight away.
+
+____
+[IMPORTANT]
+You should use Maven 3.3+ for this plugin.
+____
+
+While the Jetty Maven Plugin can be very useful for development we do not recommend its use in a production capacity.
+In order for the plugin to work it needs to leverage many internal Maven apis and Maven itself it not a production deployment tool.
+We recommend either the traditional distribution deployment approach or using link:#advanced-embedding[embedded Jetty.]
+
+[[get-up-and-running]]
+==== Quick Start: Get Up and Running
+
+First, add `jetty-maven-plugin` to your `pom.xml` definition:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <version>{VERSION}</version>
+</plugin>
+----
+
+Then, from the same directory as your root `pom.xml`, type:
+
+[source, screen, subs="{sub-order}"]
+....
+mvn jetty:run
+....
+
+This starts Jetty and serves up your project on http://localhost:8080/.
+
+Jetty will continue to run until you stop it.
+While it runs it periodically scans for changes to your project files
+If you save changes and recompile your class files, Jetty redeploys your webapp, and you can instantly test the changes that were just made.
+
+You can terminate the plugin with a `ctrl-c` in the terminal window where it is running.
+
+____
+[NOTE]
+The classpath of the running Jetty instance and its deployed webapp are managed by Maven, and may not be exactly what you expect.
+For example: a webapp's dependent jars might be referenced via the local repository, not the `WEB-INF/lib` directory.
+____
+
+[[running-and-deploying]]
+==== Supported Goals
+
+The Jetty Maven plugin has a number of distinct Maven goals.
+Arguably the most useful is the `run` goal which runs Jetty on an unassembled webapp.
+There are other goals which help you accomplish different tasks.
+For example, you might need to run your webapp in a forked instance of Jetty rather than within the process running Maven; or you may need finer grained control over the maven lifecycle stage in which you wish to deploy your webapp.
+There are different goals to accomplish these tasks, as well as several others.
+
+To see a list of all goals supported by the Jetty Maven plugin, do:
+
+[source, screen, subs="{sub-order}"]
+....
+mvn jetty:help
+....
+
+To see the detailed list of parameters that can be configured for a particular goal, in addition to its description, do:
+
+[source, screen, subs="{sub-order}"]
+....
+mvn jetty:help -Ddetail=true -Dgoal= <goal name>
+....
+
+[[configuring-jetty-container]]
+==== Configuring the Jetty Container
+
+These configuration elements set up the Jetty environment in which your webapp executes.
+They are common to most goals:
+
+httpConnector::
+Optional.
+If not specified, Jetty will create a link:{JDURL}/org/eclipse/jetty/server/ServerConnector.html[ServerConnector] instance listening on port 8080.
+You can change this default port number by using the system property `jetty.http.port` on the command line, for example, `mvn -Djetty.http.port=9999 jetty:run`.
+Alternatively, you can use this configuration element to set up the information for the ServerConnector.
+The following are the valid configuration sub-elements:
++
+port;;
+The port number for the connector to listen on.
+By default it is 8080.
+host;;
+The particular interface for the connector to listen on.
+By default, all interfaces.
+name;;
+The name of the connector, which is useful for link:#serving-webapp-from-particular-port[configuring contexts to respond only on particular connectors].
+idleTimeout;;
+Maximum idle time for a connection.
+soLinger;;
+The socket linger time.
++
+You could instead configure the connectors in a standard link:#jetty-xml-config[jetty xml config file] and put its location into the ` jettyXml` parameter.
+Note that since jetty-9.0 it is no longer possible to configure a link:#maven-config-https[https connector] directly in the pom.xml: you need to link:#maven-config-https[use jetty xml config files to do it].
+jettyXml::
+Optional.
+A comma separated list of locations of `jetty xml` files to apply in addition to any plugin configuration parameters.
+You might use it if you have other webapps, handlers, specific types of connectors etc., to deploy, or if you have other Jetty objects that you cannot configure from the plugin.
+scanIntervalSeconds::
+The pause in seconds between sweeps of the webapp to check for changes and automatically hot redeploy if any are detected.
+*By default this is 0, which disables hot deployment scanning.*
+A number greater than 0 enables it.
+reload::
+Default value is "automatic", used in conjunction with a non-zero *_`scanIntervalSeconds`_* causes automatic hot redeploy when changes are detected.
+Set to "manual" instead to trigger scanning by typing a linefeed in the console running the plugin.
+This might be useful when you are doing a series of changes that you want to ignore until you're done.
+In that use, use the `reload` parameter.
+dumpOnStart::
+Optional.
+Default value is false.
+If true, then jetty will dump out the server structure on start.
+loginServices::
+Optional.
+A list of `org.eclipse.jetty.security.LoginService` implementations. Note that there is no default realm.
+If you use a realm in your `web.xml` you can specify a corresponding realm here.
+You could instead configure the login services in a jetty xml file and add its location to the `jettyXml` parameter.
+requestLog::
+Optional.
+An implementation of the `org.eclipse.jetty.server.RequestLog` request log interface.
+An implementation that respects the NCSA format is available as `org.eclipse.jetty.server.NCSARequestLog`.
+There are three other ways to configure the RequestLog:
++
+  * In a jetty xml config file, as specified in the `jettyXml` parameter.
+  * In a context xml config file, as specified in the `contextXml` parameter.
+  * In the `webApp` element.
++
+See link:#configuring-jetty-request-logs[Configuring Request Logs] for more information.
+server::
+Optional as of Jetty 9.3.1.
+This would configure an instance of the link:{SRCDIR}/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java[`org.eclipse.jetty.server.Server`] for the plugin to use, however it is usually NOT necessary to configure this, as the plugin will automatically configure one for you.
+In particular, if you use the jettyXml element, then you generally DON'T want to define this element, as you are probably using the jettyXml file to configure up a Server with a special constructor argument, such as a custom threadpool.
+If you define both a server element AND use a jetty xml element which points to a config file that has a line like `<Configure id="Server" class="org.eclipse.jetty.server.Server">` then the the xml configuration will override what you configure for the server in the `pom.xml`.
+stopPort::
+Optional.
+Port to listen on for stop commands.
+Useful to use in conjunction with the link:#jetty-stop-goal[stop] or link:#jetty-run-forked-goal[run-forked] goals.
+stopKey::
+Optional.
+Used in conjunction with stopPort for stopping jetty.
+Useful when used in conjunction with the stop or run-forked goals.
+systemProperties::
+Optional.
+Allows you to configure System properties for the execution of the plugin.
+For more information, see link:#sys_props[Setting System Properties].
+systemPropertiesFile::
+Optional.
+A file containing System properties to set for the execution of the plugin.
+By default, settings that you make here *do not* override any system properties already set on the command line, by the JVM, or in the POM via `systemProperties`.
+Read link:#sys_props[Setting System Properties] for how to force overrides.
+skip::
+Default is false.
+If true, the execution of the plugin exits.
+Same as setting the SystemProperty `-Djetty.skip` on the command line.
+This is most useful when configuring Jetty for execution during integration testing and you want to skip the tests.
+useProvidedScope::
+Default value is `false`.
+If true, the dependencies with `<scope>provided</scope>` are placed onto the __container classpath__.
+Be aware that this is NOT the webapp classpath, as "provided" indicates that these dependencies would normally be expected to be provided by the container.
+You should very rarely ever need to use this.
+Instead, you should copy the provided dependencies as explicit dependencies of the `plugin` instead.
+excludedGoals::
+Optional.
+A list of Jetty plugin goal names that will cause the plugin to print an informative message and exit.
+Useful if you want to prevent users from executing goals that you know cannot work with your project.
+
+[[maven-config-https]]
+===== Configuring a Https Connector
+
+In order to configure an HTTPS connector, you need to use jetty xml configuration files.
+This example uses files copied directly from the jetty distribution etc/ directory, although you can of course make up your own xml file or files.
+We will use the following files:
+
+jetty.xml::
+Sets up various characteristics of the link:{SRCDIR}/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java[`org.eclipse.jetty.server.Server`] instance for the plugin to use.
+Importantly, it sets up the link:{SRCDIR}/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java[`org.eclipse.jetty.server.HttpConfiguration`] element that we can refer to in subsequent xml files that configure the connectors.
+Here's the relevant section:
++
+[source, xml, subs="{sub-order}"]
+----
+    <New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
+      <Set name="secureScheme">https</Set>
+      <Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
+      <Set name="outputBufferSize">32768</Set>
+      <Set name="requestHeaderSize">8192</Set>
+      <Set name="responseHeaderSize">8192</Set>
+      <Set name="sendServerVersion">true</Set>
+      <Set name="sendDateHeader">false</Set>
+      <Set name="headerCacheSize">512</Set>
+
+      <!-- Uncomment to enable handling of X-Forwarded- style headers
+      <Call name="addCustomizer">
+        <Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
+      </Call>
+      -->
+    </New>
+----
+jetty-ssl.xml::
+Set up ssl which will be used by the https connector.
+Here's the `jetty-ssl.xml` file from the jetty-distribution:
++
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-server/src/main/config/etc/jetty-ssl.xml[]
+----
+jetty-https.xml::
+Set up the https connector using the HttpConfiguration from `jetty.xml` and the ssl configuration from `jetty-ssl.xml`:
++
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-server/src/main/config/etc/jetty-https.xml[]
+----
+
+Now you need to let the plugin know to apply the files above:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <version>{VERSION}</version>
+  <configuration>
+    <jettyXml>jetty.xml,jetty-ssl.xml,jetty-https.xml</jettyXml>
+  </configuration>
+</plugin>
+----
+
+____
+[CAUTION]
+Just like with an installed distribution of Jetty, the ordering of the xml files is significant.
+____
+
+You can also use Jetty xml files to configure a http connector for the plugin to use.
+Here we use the same `jetty-http.xml` file from the Jetty distribution:
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-server/src/main/config/etc/jetty-http.xml[]
+----
+
+Now we add it to the list of configs for the plugin to apply:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <version>{VERSION}</version>
+  <configuration>
+    <jettyXml>jetty.xml,jetty-http.xml,jetty-ssl.xml,jetty-https.xml</jettyXml>
+  </configuration>
+</plugin>
+----
+
+Alternatively, you can use the link:#maven-http-connector[*httpConnector*] configuration element inside the pom instead as described above.
+
+[[configuring-your-webapp]]
+==== Configuring Your WebApp
+
+These configuration parameters apply to your webapp.
+They are common to almost all goals.
+
+webApp::
+This is an instance of link:{JDURL}/org/eclipse/jetty/maven/plugin/JettyWebAppContext.html[org.eclipse.jetty.maven.plugin.JettyWebAppContext], which is an extension to the class  link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.hml[`org.eclipse.jetty.webapp.WebAppContext`].
+You can use any of the setter methods on this object to configure your webapp.
+Here are a few of the most useful ones:
++
+contextPath;;
+The context path for your webapp. By default, this is set to `/`.
+If using a custom value for this parameter, you probably want to include the leading `/`, example `/mycontext`.
+descriptor;;
+The path to the `web.xml` file for your webapp.
+defaultsDescriptor;;
+The path to a `webdefault.xml` file that will be applied to your webapp before the `web.xml`.
+If you don't supply one, Jetty uses a default file baked into the `jetty-webapp.jar`.
+overrideDescriptor;;
+The path to a `web.xml` file that Jetty applies after reading your `web.xml`.
+You can use this to replace or add configuration.
+tempDirectory;;
+The path to a dir that Jetty can use to expand or copy jars and jsp compiles when your webapp is running.
+The default is `${project.build.outputDirectory}/tmp`.
+baseResource;;
+The path from which Jetty serves static resources.
+Defaults to `src/main/webapp`.
+resourceBases;;
+Use instead of `baseResource` if you have multiple directories from which you want to serve static content.
+This is an array of directory names.
+baseAppFirst;;
+Defaults to "true".
+Controls whether any overlaid wars are added before or after the original base resource(s) of the webapp.
+See the section on link:#using-overlaid-wars[overlaid wars] for more information.
+containerIncludeJarPattern;;
+Defaults to `.*/javax.servlet-[^/]*\.jar$|.*/servlet-api-[^/]*\.jar$|.*javax.servlet.jsp.jstl-[^/]*\.jar|.*taglibs-standard-impl-.*\.jar`.
+This is a pattern that is applied to the names of the jars on the container's classpath (ie the classpath of the plugin, not that of the webapp) that should be scanned for fragments, tlds, annotations etc.
+This is analogous to the context attribute link:#container-include-jar-pattern[org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern] that is documented link:#container-include-jar-pattern[here].
+You can define extra patterns of jars that will be included in the scan.
+webInfIncludeJarPattern;;
+Defaults to matching _all_ of the dependency jars for the webapp (ie the equivalent of WEB-INF/lib).
+You can make this pattern more restrictive to only match certain jars by using this setter.
+This is analogous to the context attribute link:#web-inf-include-jar-pattern[org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern] that is documented link:#web-inf-include-jar-pattern[here].
+contextXml::
+The path to a context xml file that is applied to your webapp AFTER the `webApp` element.
+
+[[jetty-run-goal]]
+==== jetty:run
+
+The `run` goal runs on a webapp that does not have to be built into a WAR.
+Instead, Jetty deploys the webapp from its sources.
+It looks for the constituent parts of a webapp in the Maven default project locations, although you can override these in the plugin configuration.
+For example, by default it looks for:
+
+* resources in `${project.basedir}/src/main/webapp`
+* classes in `${project.build.outputDirectory}`
+* `web.xml` in `${project.basedir}/src/main/webapp/WEB-INF/`
+
+The plugin automatically ensures the classes are rebuilt and up-to-date before deployment.
+If you change the source of a class and your IDE automatically compiles it in the background, the plugin picks up the changed class.
+
+You do not need to assemble the webapp into a WAR, saving time during the development cycle.
+Once invoked, you can configure the plugin to run continuously, scanning for changes in the project and automatically performing a hot redeploy when necessary.
+Any changes you make are immediately reflected in the running instance of Jetty, letting you quickly jump from coding to testing, rather than going through the cycle of: code, compile, reassemble, redeploy, test.
+
+Here is an example, which turns on scanning for changes every ten seconds, and sets the webapp context path to `/test`:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <version>{VERSION}</version>
+  <configuration>
+    <scanIntervalSeconds>10</scanIntervalSeconds>
+    <webApp>
+      <contextPath>/test</contextPath>
+    </webApp>
+  </configuration>
+</plugin>
+----
+
+[[configuring-additional-parameters]]
+===== Configuration
+
+In addition to the `webApp` element that is common to most goals, the `jetty:run` goal supports:
+
+classesDirectory::
+Location of your compiled classes for the webapp.
+You should rarely need to set this parameter.
+Instead, you should set `build outputDirectory` in your `pom.xml`.
+testClassesDirectory::
+Location of the compiled test classes for your webapp. By default this is `${project.build.testOutputDirectory}`.
+useTestScope::
+If true, the classes from `testClassesDirectory` and dependencies of scope "test" are placed first on the classpath.
+By default this is false.
+webAppSourceDirectory::
+By default, this is set to `${project.basedir}/src/main/webapp`.
+If your static sources are in a different location, set this parameter accordingly.
+jettyEnvXml::
+Optional.
+Location of a `jetty-env.xml` file, which allows you to make JNDI bindings that satisfy `env-entry`, `resource-env-ref`, and `resource-ref` linkages in the `web.xml` that are scoped  only to the webapp and not shared with other webapps that you might be deploying at the same time (for example, by using a ` jettyConfig` file).
+scanTargets::
+Optional.
+A list of files and directories to periodically scan in addition to those the plugin automatically scans.
+scanTargetPatterns::
+Optional.
+If you have a long list of extra files you want scanned, it is more convenient to use pattern matching expressions to specify them instead of enumerating them with the `scanTargetsList` of `scanTargetPatterns`, each consisting of a directory, and including and/or excluding parameters to specify the file matching patterns.
+scanClassesPattern::
+Since 9.3.0.
+Optional.
+Include and exclude patterns that can be applied to the classesDirectory for the purposes of scanning, it does *not* affect the classpath.
+If a file or directory is excluded by the patterns then a change in that file (or subtree in the case of a directory) is ignored and will not cause the webapp to redeploy.
+Patterns are specified as a relative path using a glob-like syntax as described in the http://docs.oracle.com/javase/8/docs/api/java/nio/file/FileSystem.html#getPathMatcher-java.lang.String-[javadoc] for http://docs.oracle.com/javase/8/docs/api/java/nio/file/FileSystem.html#getPathMatcher-java.lang.String-[FileSystem.getPathMatcher].
+scanTestClassesPattern::
+Since 9.3.0.
+Optional.
+Include and exclude patterns that can be applied to the testClassesDirectory for the purposes of scanning, it does *not* affect the classpath.
+If a file or directory is excluded by the patterns then a change in that file (or subtree in the case of a directory) is ignored and will not cause the webapp to redeploy.
+Patterns are specified as a relative path using a glob-like syntax as described in the http://docs.oracle.com/javase/8/docs/api/java/nio/file/FileSystem.html#getPathMatcher-java.lang.String-[javadoc] for http://docs.oracle.com/javase/8/docs/api/java/nio/file/FileSystem.html#getPathMatcher-java.lang.String-[FileSystem.getPathMatcher].
+
+Here's an example:
+
+[source, xml, subs="{sub-order}"]
+----
+<project>
+...
+  <plugins>
+...
+    <plugin>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-maven-plugin</artifactId>
+      <version>{VERSION}</version>
+      <configuration>
+        <webAppSourceDirectory>${project.basedir}/src/staticfiles</webAppSourceDirectory>
+        <webApp>
+          <contextPath>/</contextPath>
+          <descriptor>${project.basedir}/src/over/here/web.xml</descriptor>
+          <jettyEnvXml>${project.basedir}/src/over/here/jetty-env.xml</jettyEnvXml>
+        </webApp>
+        <classesDirectory>${project.basedir}/somewhere/else</classesDirectory>
+        <scanClassesPattern>
+          <excludes>
+             <exclude>**/Foo.class</exclude>
+          </excludes>
+        </scanClassesPattern>
+        <scanTargets>
+          <scanTarget>src/mydir</scanTarget>
+          <scanTarget>src/myfile.txt</scanTarget>
+        </scanTargets>
+        <scanTargetPatterns>
+          <scanTargetPattern>
+            <directory>src/other-resources</directory>
+            <includes>
+              <include>**/*.xml</include>
+              <include>**/*.properties</include>
+            </includes>
+            <excludes>
+              <exclude>**/myspecial.xml</exclude>
+              <exclude>**/myspecial.properties</exclude>
+            </excludes>
+          </scanTargetPattern>
+        </scanTargetPatterns>
+      </configuration>
+    </plugin>
+  </plugins>
+</project>
+----
+
+If, for whatever reason, you cannot run on an unassembled webapp, the goals `run-war` and `run-exploded` work on unassembled webapps.
+
+[[running-assembled-webapp-as-war]]
+==== jetty:run-war
+
+This goal first packages your webapp as a WAR file and then deploys it to Jetty.
+If you set a non-zero `scanInterval`, Jetty watches your `pom.xml` and the WAR file; if either changes, it redeploys the war.
+
+[[configuring-war]]
+===== Configuration
+
+war::
+The location of the built WAR file. This defaults to `${project.build.directory}/${project.build.finalName}.war`.
+If this is not sufficient, set it to your custom location.
+
+Here's how to set it:
+
+[source, xml, subs="{sub-order}"]
+----
+<project>
+...
+  <plugins>
+...
+    <plugin>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-maven-plugin</artifactId>
+      <version>{VERSION}</version>
+      <configuration>
+        <war>${project.basedir}/target/mycustom.war</war>
+      </configuration>
+    </plugin>
+  </plugins>
+</project>
+----
+
+[[running-assembled-webapp-as-expanded-war]]
+==== jetty:run-exploded
+
+The run-exploded goal first assembles your webapp into an exploded WAR file and then deploys it to Jetty.
+If you set a non-zero `scanInterval`, Jetty watches your `pom.xml,`WEB-INF/lib`, `WEB-INF/` and `WEB-INF/web.xml` for changes and redeploys when necessary.
+
+[[configuring-exploded-war]]
+===== Configuration
+
+war::
+The location of the exploded WAR.
+This defaults to `${project.build.directory}/${project.build.finalName}`, but you can override the default by setting this parameter.
+
+Here's how to set it:
+
+[source, xml, subs="{sub-order}"]
+----
+<project>
+...
+  <plugins>
+...
+    <plugin>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>maven-jetty-plugin</artifactId>
+      <version>{VERSION}</version>
+      <configuration>
+        <war>${project.basedir}/target/myfunkywebapp</war>
+      </configuration>
+    </plugin>
+  </plugins>
+</project>
+----
+
+[[deploy-war-running-pre-assembled-war]]
+==== jetty:deploy-war
+
+This is basically the same as `jetty:run-war`, but without assembling the WAR of the current module - you can nominate the location of any war to run.
+Unlike `run-war`, the phase in which this plugin executes is not bound to the "package" phase - you may bind it to any phase to use it.
+
+===== Configuration
+
+war::
+The location of the WAR file. This defaults to `${project.build.directory}/${project.build.finalName}`, but you can override the default by setting this parameter.
+daemon::
+If true, this plugin will start Jetty but let the build continue.
+This is useful if you want to start jetty as an execution binding in a particular phase and then stop it in another.
+Alternatively, you can set this parameter to false, in which case Jetty will block and you will need to use a ctrl-c to stop it.
+
+Here's the configuration:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<project>
+...
+  <plugins>
+...
+  <plugin>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-maven-plugin</artifactId>
+    <version>{VERSION}</version>
+    <configuration>
+      <war>/opt/special/some-app.war</war>
+      <stopKey>alpha</stopKey>
+      <stopPort>9099</stopPort>
+    </configuration>
+    <executions>
+      <execution>
+        <id>start-jetty</id>
+        <phase>test-compile</phase>
+        <goals>
+          <goal>deploy-war</goal>
+        </goals>
+      </execution>
+      <execution>
+        <id>stop-jetty</id>
+        <phase>test</phase>
+        <goals>
+          <goal>stop</goal>
+        </goals>
+      </execution>
+     </executions>
+    </plugin>
+  </plugins>
+</project>
+
+
+----
+
+[[jetty-run-forked-goal]]
+==== jetty:run-forked
+
+This goal allows you to start the webapp in a new JVM, optionally passing arguments to that new JVM.
+This goal supports the same configuration parameters as the `jetty:run` goal with a couple of extras to help configure the forked process.
+
+===== Configuration
+
+The available configuration parameters - in addition to those for the `jetty:run` goal - are:
+
+jvmArgs::
+Optional.
+A string representing arbitrary arguments to pass to the forked JVM.
+waitForChild::
+Default is `true`.
+This causes the parent process to wait for the forked process to exit.
+In this case you can use `ctrl-c` to terminate both processes.
+It is more useful to set it to `false`, in which case the parent process terminates whilst leaving the child process running.
+You use the `jetty:stop` goal to stop the child process.
+maxStartupLines::
+Default is `50`.
+This is maximum number of lines the parent process reads from the child process to receive an indication that the child has started.
+If the child process produces an excessive amount of output on stdout you may need to increase this number.
+
+Some of the container configuration parameters are *NOT* available with this goal:
+
+scanIntervalSeconds::
+Not supported.
+The forked jetty will not monitor and redeploy the webapp.
+reload::
+Not supported.
+The forked jetty will not redeploy the webapp.
+httpConnector::
+Not supported.
+To define custom connectors use a jetty xml file instead.
+loginServices::
+Not supported.
+To define LoginServices use a jetty xml or context xml file instead.
+requestLog::
+Not supported.
+To define a RequestLog setup, use a jetty xml or context xml file instead.
+systemProperties::
+Not supported.
+Use the `jvmArgs` parameter to pass system properties to the forked process.
+
+To deploy your unassembled web app to Jetty running in a new JVM:
+
+[source, screen, subs="{sub-order}"]
+....
+mvn jetty:run-forked
+....
+
+Jetty continues to execute until you either:
+
+* Press `ctrl-c` in the terminal window to stop the plugin, which also stops the forked JVM (only if you started with `waitForChild=true`)
+* Use `jetty:stop` to stop the forked JVM, which also stops the plugin.
+
+____
+[NOTE]
+If you want to set a custom port for the Jetty connector you need to specify it in a `jetty xml` file rather than setting the connector and port tags.
+You can specify the location of the `jetty.xml` using the `jettyXml` parameter.
+____
+
+[[jetty-start-goal]]
+==== jetty:start
+
+This goal is for use with an execution binding in your `pom.xml`.
+It is similar to the `jetty:run` goal, however it does NOT first execute the build up until the `test-compile` phase to ensure that all necessary classes and files of the webapp have been generated.
+This is most useful when you want to control the start and stop of Jetty via execution bindings in your `pom.xml`.
+
+For example, you can configure the plugin to start your webapp at the beginning of your unit tests and stop at the end.
+To do this, you need to set up a couple of `execution` scenarios for the Jetty plugin.
+You use the `pre-integration-test` and `post-integration-test` Maven build phases to trigger the execution and termination of Jetty:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <version>{VERSION}</version>
+  <configuration>
+    <scanIntervalSeconds>10</scanIntervalSeconds>
+    <stopKey>foo</stopKey>
+    <stopPort>9999</stopPort>
+  </configuration>
+  <executions>
+    <execution>
+      <id>start-jetty</id>
+      <phase>pre-integration-test</phase>
+      <goals>
+        <goal>start</goal>
+      </goals>
+      <configuration>
+        <scanIntervalSeconds>0</scanIntervalSeconds>
+      </configuration>
+    </execution>
+    <execution>
+      <id>stop-jetty</id>
+      <phase>post-integration-test</phase>
+       <goals>
+         <goal>stop</goal>
+       </goals>
+     </execution>
+  </executions>
+</plugin>
+----
+
+[[jetty-stop-goal]]
+==== jetty:stop
+
+The stop goal stops a running instance of Jetty.
+To use it, you need to configure the plugin with a special port number and key.
+That same port number and key will also be used by the other goals that start jetty.
+
+stopPort::
+A port number for Jetty to listen on to receive a stop command to cause it to shutdown.
+stopKey::
+A string value sent to the `stopPort` to validate the stop command.
+stopWait::
+The maximum time in seconds that the plugin will wait for confirmation that Jetty has stopped.
+If false or not specified, the plugin does not wait for confirmation but exits after issuing the stop command.
+
+Here's a configuration example:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <version>{VERSION}</version>
+  <configuration>
+    <stopPort>9966</stopPort>
+    <stopKey>foo</stopKey>
+    <stopWait>10</stopWait>
+  </configuration>
+</plugin>
+
+
+----
+
+Then, while Jetty is running (in another window), type:
+
+[source, screen, subs="{sub-order}"]
+....
+mvn jetty:stop
+....
+
+The `stopPort` must be free on the machine you are running on.
+If this is not the case, you will get an "Address already in use" error message after the "Started ServerConnector ..." message.
+
+[[jetty-effective-web-xml]]
+==== jetty:effective-web-xml
+
+This goal calculates a synthetic `web.xml` (the "effective web.xml") according to the rules of the Servlet Specification taking into account all sources of discoverable configuration of web components in your application: descriptors (`webdefault.xml`, `web.xml`, `web-fragment.xml`s, `web-override.xml`) and discovered annotations (`@WebServlet`, `@WebFilter`, `@WebListener`).
+The effective `web.xml` from these combined sources is generated and displayed as maven log output.
+Other useful information about your webapp that is produced as part of the analysis is also stored as context parameters in the effective-web.xml.
+The effective-web.xml can be used in conjunction with the link:#quickstart-webapp[Quickstart] feature to quickly start your webapp (note that Quickstart is not appropriate for the mvn jetty goals).
+
+The following configuration parameters allow you to save the file:
+
+deleteOnExit::
+By default this is `true`.
+If set to `false`, the effective web.xml is generated into a file called `effective-web.xml` in the build `target` directory.
+effectiveWebXml::
+The full path name of a file into which you would like the effective web xml generated.
+
+Note that no programmatic declarations of servlets, filters and listeners will be taken into account.
+
+[[using-overlaid-wars]]
+==== Using Overlaid wars
+
+If your webapp depends on other war files, thelink:#jetty-run-goal[jetty:run] and link:#jetty-run-forked-goal[jetty:run-forked] goals are able to merge resources from all of them.
+It can do so based on the settings of the http://maven.apache.org/plugins/maven-war-plugin/[maven-war-plugin], or if your project does not use the http://maven.apache.org/plugins/maven-war-plugin/[maven-war-plugin] to handle the overlays, it can fall back to a simple algorithm to determine the ordering of resources.
+
+===== With maven-war-plugin
+
+The maven-war-plugin has a rich set of capabilities for merging resources.
+The `jetty:run` and `jetty:run-forked` goals are able to interpret most of them and apply them during execution of your unassembled webapp.
+This is probably best seen by looking at a concrete example.
+
+Suppose your webapp depends on the following wars:
+
+[source, xml, subs="{sub-order}"]
+----
+<dependency>
+  <groupId>com.acme</groupId>
+  <artifactId>X</artifactId>
+  <type>war</type>
+</dependency>
+<dependency>
+  <groupId>com.acme</groupId>
+  <artifactId>Y</artifactId>
+  <type>war</type>
+</dependency>
+----
+
+Containing:
+
+[source,text]
+----
+WebAppX:
+
+ /foo.jsp
+ /bar.jsp
+ /WEB-INF/web.xml
+
+WebAppY:
+
+ /bar.jsp
+ /baz.jsp
+ /WEB-INF/web.xml
+ /WEB-INF/special.xml
+----
+
+They are configured for the http://maven.apache.org/plugins/maven-war-plugin/overlays.html[maven-war-plugin]:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.apache.maven.plugins</groupId>
+  <artifactId>maven-war-plugin</artifactId>
+  <version>{VERSION}</version>
+  <configuration>
+    <overlays>
+      <overlay>
+        <groupId>com.acme</groupId>
+        <artifactId>X</artifactId>
+        <excludes>
+          <exclude>bar.jsp</exclude>
+        </excludes>
+      </overlay>
+      <overlay>
+        <groupId>com.acme</groupId>
+        <artifactId>Y</artifactId>
+        <excludes>
+          <exclude>baz.jsp</exclude>
+        </excludes>
+      </overlay>
+      <overlay>
+      </overlay>
+    </overlays>
+  </configuration>
+</plugin>
+----
+
+Then executing jetty:run would yield the following ordering of resources: `com.acme.X.war : com.acme.Y.war: ${project.basedir}/src/main/webapp`.
+Note that the current project's resources are placed last in the ordering due to the empty <overlay/> element in the maven-war-plugin.
+You can either use that, or specify the `<baseAppFirst>false</baseAppFirst>` parameter to the jetty-maven-plugin.
+
+Moreover, due to the `exclusions` specified above, a request for the resource ` bar.jsp` would only be satisfied from `com.acme.Y.war.`
+Similarly as `baz.jsp` is excluded, a request for it would result in a 404 error.
+
+===== Without maven-war-plugin
+
+The algorithm is fairly simple, is based on the ordering of declaration of the dependent wars, and does not support exclusions.
+The configuration parameter `<baseAppFirst>` (see the section on link:#configuring-your-webapp[Configuring Your Webapp] for more information) can be used to control whether your webapp's resources are placed first or last on the resource path at runtime.
+
+For example, suppose our webapp depends on these two wars:
+
+[source, xml, subs="{sub-order}"]
+----
+<dependency>
+  <groupId>com.acme</groupId>
+  <artifactId>X</artifactId>
+  <type>war</type>
+</dependency>
+<dependency>
+  <groupId>com.acme</groupId>
+  <artifactId>Y</artifactId>
+  <type>war</type>
+</dependency>
+----
+
+Suppose the webapps contain:
+
+[source,text]
+----
+WebAppX:
+
+ /foo.jsp
+ /bar.jsp
+ /WEB-INF/web.xml
+
+WebAppY:
+
+ /bar.jsp
+ /baz.jsp
+ /WEB-INF/web.xml
+ /WEB-INF/special.xml
+
+----
+
+Then our webapp has available these additional resources:
+
+[source,text]
+----
+/foo.jsp (X)
+/bar.jsp (X)
+/baz.jsp (Y)
+/WEB-INF/web.xml (X)
+/WEB-INF/special.xml (Y)
+
+----
+
+[[configuring-security-settings]]
+==== Configuring Security Settings
+
+You can configure LoginServices in the plugin.
+Here's an example of setting up the HashLoginService for a webapp:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <version>{VERSION}</version>
+  <configuration>
+    <scanIntervalSeconds>10</scanIntervalSeconds>
+    <webApp>
+      <contextPath>/test</contextPath>
+    </webApp>
+    <loginServices>
+      <loginService implementation="org.eclipse.jetty.security.HashLoginService">
+        <name>Test Realm</name>
+        <config>${project.basedir}/src/etc/realm.properties</config>
+      </loginService>
+    </loginServices>
+  </configuration>
+</plugin>
+
+----
+
+[[using-multiple-webapp-root-directories]]
+==== Using Multiple Webapp Root Directories
+
+If you have external resources that you want to incorporate in the execution of a webapp, but which are not assembled into war files, you can't use the overlaid wars method described above, but you can tell Jetty the directories in which these external resources are located.
+At runtime, when Jetty receives a request for a resource, it searches all the locations to retrieve the resource.
+It's a lot like the overlaid war situation, but without the war.
+
+Here is a configuration example:
+
+[source, xml, subs="{sub-order}"]
+----
+<configuration>
+  <webApp>
+    <contextPath>/${build.finalName}</contextPath>
+    <baseResource implementation="org.eclipse.jetty.util.resource.ResourceCollection">
+      <resourcesAsCSV>src/main/webapp,/home/johndoe/path/to/my/other/source,/yet/another/folder</resourcesAsCSV>
+    </baseResource>
+  </webApp>
+</configuration>
+----
+
+[[running-more-than-one-webapp]]
+==== Running More than One Webapp
+
+You can use either a `jetty.xml` file to configure extra (pre-compiled) webapps that you want to deploy, or you can use the `<contextHandlers>` configuration element to do so.
+If you want to deploy webapp A, and webapps B and C in the same Jetty instance:
+
+Putting the configuration in webapp A's `pom.xml`:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <version>{VERSION}</version>
+  <configuration>
+    <scanIntervalSeconds>10</scanIntervalSeconds>
+    <webApp>
+      <contextPath>/test</contextPath>
+    </webApp>
+    <contextHandlers>
+      <contextHandler implementation="org.eclipse.jetty.maven.plugin.JettyWebAppContext">
+        <war>${project.basedir}../../B.war</war>
+        <contextPath>/B</contextPath>
+      </contextHandler>
+      <contextHandler implementation="org.eclipse.jetty.maven.plugin.JettyWebAppContext">
+        <war>${project.basedir}../../C.war</war>
+        <contextPath>/C</contextPath>
+      </contextHandler>
+    </contextHandlers>
+  </configuration>
+</plugin>
+----
+
+____
+[IMPORTANT]
+If the `ContextHandler` you are deploying is a webapp, it is *essential* that you use an `org.eclipse.jetty.maven.plugin.JettyWebAppContext` instance rather than a standard `org.eclipse.jetty.webapp.WebAppContext` instance.
+Only the former will allow the webapp to function correctly in the maven environment.
+____
+
+Alternatively, add a `jetty.xml` file to webapp A.
+Copy the `jetty.xml` file from the Jetty distribution, and then add WebAppContexts for the other 2 webapps:
+
+[source, xml, subs="{sub-order}"]
+----
+<Ref refid="Contexts">
+  <Call name="addHandler">
+    <Arg>
+      <New class="org.eclipse.jetty.maven.plugin.JettyWebAppContext">
+        <Set name="contextPath">/B</Set>
+        <Set name="war">../../B.war</Set>
+      </New>
+    </Arg>
+  </Call>
+  <Call>
+    <Arg>
+      <New class="org.eclipse.jetty.maven.plugin.JettyWebAppContext">
+        <Set name="contextPath">/C</Set>
+        <Set name="war">../../C.war</Set>
+      </New>
+    </Arg>
+  </Call>
+</Ref>
+----
+
+Then configure the location of this `jetty.xml` file into webapp A's jetty plugin:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <version>{VERSION}</version>
+  <configuration>
+    <scanIntervalSeconds>10</scanIntervalSeconds>
+    <webApp>
+      <contextPath>/test</contextPath>
+    </webApp>
+    <jettyXml>src/main/etc/jetty.xml</jettyXml>
+  </configuration>
+</plugin>
+
+----
+
+For either of these solutions, the other webapps must already have been built, and they are not automatically monitored for changes.
+You can refer either to the packed WAR file of the pre-built webapps or to their expanded equivalents.
+
+[[setting-system-properties]]
+==== Setting System Properties
+
+You can specify property name/value pairs that Jetty sets as System properties for the execution of the plugin.
+This feature is useful to tidy up the command line and save a lot of typing.
+
+However, *sometimes it is not possible to use this feature to set System properties* - sometimes the software component using the System property is already initialized by the time that maven runs (in which case you will need to provide the System property on the command line), or by the time that Jetty runs.
+In the latter case, you can use the link:http://www.mojohaus.org/[maven properties plugin] to define the system properties instead. Here's an example that configures the logback logging system as the Jetty logger:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.codehaus.mojo</groupId>
+  <artifactId>properties-maven-plugin</artifactId>
+  <version>1.0-alpha-2</version>
+  <executions>
+    <execution>
+      <goals>
+        <goal>set-system-properties</goal>
+      </goals>
+      <configuration>
+        <properties>
+          <property>
+            <name>logback.configurationFile</name>
+            <value>${project.baseUri}/resources/logback.xml</value>
+          </property>
+        </properties>
+      </configuration>
+    </execution>
+  </executions>
+</plugin>
+----
+
+____
+[NOTE]
+If a System property is already set (for example, from the command line or by the JVM itself), then by default these configured properties *DO NOT* override them (see below for use of the <force> parameter).
+____
+
+[[specifying-properties-in-pom]]
+===== Specifying System Properties in the POM
+
+Here's an example of how to specify System properties in the POM:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <configuration>
+    <systemProperties>
+      <systemProperty>
+        <name>fooprop</name>
+        <value>222</value>
+      </systemProperty>
+    </systemProperties>
+    <webApp>
+      <contextPath>/test</contextPath>
+    </webApp>
+  </configuration>
+</plugin>
+
+----
+
+To change the default behavior so that these system properties override those on the command line, use the `<force>` parameter:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <configuration>
+    <systemProperties>
+      <force>true</force>
+      <systemProperty>
+       <name>fooprop</name>
+       <value>222</value>
+     </systemProperty>
+    </systemProperties>
+    <webApp>
+      <contextPath>/test</contextPath>
+    </webApp>
+  </configuration>
+</plugin>
+----
+
+[[specifying-properties-in-file]]
+===== Specifying System Properties in a File
+
+You can also specify your System properties in a file.
+System properties you specify in this way *do not* override System properties that set on the command line, by the JVM, or directly in the POM via `systemProperties`.
+
+Suppose we have a file called `mysys.props` which contains the following:
+
+[source,text]
+----
+fooprop=222
+----
+
+This can be configured on the plugin like so:
+
+[source, xml, subs="{sub-order}"]
+----
+<plugin>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-maven-plugin</artifactId>
+  <configuration>
+    <systemPropertiesFile>${project.basedir}/mysys.props</systemPropertiesFile>
+    <webApp>
+      <contextPath>/test</contextPath>
+    </webApp>
+  </configuration>
+</plugin>
+----
+
+You can instead specify the file by setting the System property `jetty.systemPropertiesFile` on the command line.
diff --git a/jetty-documentation/src/main/asciidoc/development/maven/jetty-maven-scanning.adoc b/jetty-documentation/src/main/asciidoc/development/maven/jetty-maven-scanning.adoc
new file mode 100644
index 0000000..d0915c6
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/maven/jetty-maven-scanning.adoc
@@ -0,0 +1,37 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-maven-scanning]]
+=== Files Scanned by the Jetty Maven Plugin
+
+If you set a non zero `scanInterval` link:#jetty-maven-plugin[configuration parameter], the `jetty-maven-plugin` will scan certain files every `scanInterval` seconds for changes, and redeploy the webapp if necessary.
+The files that are scanned depend on the goal being executed.
+
+[[scanner-matrix]]
+==== Scanner Matrix
+
+[width="100%",cols="2",options="header"]
+|=======================================================================
+|Goal                            |Files
+|link:#jetty-run-goal[jetty:run] |pom.xml, <dependencies>, <classesDirectory>, <testClassesDirectory>, <webXml> or <webAppSourceDirectory>/WEB-INF/web.xml, <jettyEnvXml> or <webAppSourceDirectory>/WEB-INF/jetty-web.xml, <webAppSourceDirectory>/WEB-INF/jetty-web.xml, <scanTargets>, <scanTargetPatterns>, any link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setDefaultsDescriptor%28java.lang.String%29[defaultsDescriptor] for the webapp, any link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setOverrideDescriptor%28java.lang.String%29[overrideDescriptor] for the webapp
+|link:#running-assembled-webapp-as-war[jetty:run-war] |pom.xml, <war>
+|link:#running-assembled-webapp-as-expanded-war[jetty:run-exploded]
+|pom.xml, <war>/WEB-INF/web.xml, <war>/WEB-INF/jetty-web.xml, <war>/WEB-INF/jetty-env.xml,<war>/WEB-INF/classes, <war>/WEB-INF/lib
+|link:#deploy-war-running-pre-assembled-war[jetty:deploy-war] |pom.xml, <war>
+|link:#jetty-run-forked-goal[jetty:run-forked] |
+|link:#jetty-start-goal[jetty:start] |pom.xml, <dependencies> from the pom, <classesDirectory>, <testClassesDirectory>, <webXml> or <webAppSourceDirectory>/WEB-INF/web.xml, <jettyEnvXml> or <webAppSourceDirectory>/WEB-INF/jetty-web.xml, <webAppSourceDirectory>/WEB-INF/jetty-web.xml, <scanTargets>, <scanTargetPatterns>, any link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setDefaultsDescriptor%28java.lang.String%29[defaultsDescriptor] for the webapp, any link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setOverrideDescriptor%28java.lang.String%29[overrideDescriptor] for the webapp
+|link:#jetty-stop-goal[jetty:stop] |
+|=======================================================================
diff --git a/jetty-documentation/src/main/asciidoc/development/part.adoc b/jetty-documentation/src/main/asciidoc/development/part.adoc
new file mode 100644
index 0000000..df19af4
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/part.adoc
@@ -0,0 +1,29 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+[[jetty-dev-guide]]
+
+= Jetty Development Guide
+
+include::embedding/chapter.adoc[]
+include::clients/http/chapter.adoc[]
+include::maven/chapter.adoc[]
+include::ant/chapter.adoc[]
+include::handlers/chapter.adoc[]
+include::websockets/intro/chapter.adoc[]
+include::websockets/jetty/chapter.adoc[]
+//include::websockets/java/chapter.adoc[]
+include::continuations/chapter.adoc[]
+include::frameworks/chapter.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/intro/chapter.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/intro/chapter.adoc
new file mode 100644
index 0000000..343eae5
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/intro/chapter.adoc
@@ -0,0 +1,128 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[websocket-intro]]
+== WebSocket Introduction
+
+WebSocket is a new protocol for bidirectional communications over HTTP.
+
+It is based on a low level framing protocol that delivers messages in either UTF-8 TEXT or BINARY format.
+
+A single message in WebSocket can be of any size (the underlying framing however does have a single frame limit of http://en.wikipedia.org/wiki/9223372036854775807[63-bits])
+
+There can be an unlimited number of messages sent.
+
+Messages are sent sequentially, the base protocol does not support interleaved messages.
+
+A WebSocket connection goes through some basic state changes:
+
+.WebSocket connection states
+[width="50%",cols=",",options="header",]
+|=======================================================================
+|State |Description
+|CONNECTING |A HTTP Upgrade to WebSocket is in progress
+|OPEN |The HTTP Upgrade succeeded and the socket is now open and ready to read / write
+|CLOSING |A WebSocket Close Handshake has been started
+|CLOSED |WebSocket is now closed, no more read/write possible
+|=======================================================================
+
+When a WebSocket is closed, a link:{JDURL}/org/eclipse/jetty/websocket/api/StatusCode.html[status code] and short reason string is provided.
+
+[[ws-intro-provides]]
+=== What Jetty provides
+
+Jetty provides an implementation of the following standards and specs.
+
+http://tools.ietf.org/html/rfc6455[RFC-6455]::
+  The WebSocket Protocol
++
+We support the version 13 of the released and final spec.
++
+Jetty tests its WebSocket protocol implementation using the http://autobahn.ws/testsuite[autobahn testsuite].
+
+____
+[IMPORTANT]
+The early drafts of WebSocket were supported in Jetty 7 and Jetty 8,   but this support has been removed in Jetty 9.
+This means that Jetty 9 will not support the old browsers that implemented the early drafts of WebSocket. (such as Safari 5.0 or Opera 12)
+____
+
+____
+[TIP]
+Want to know if the browser you are targeting supports WebSocket?
+Use http://caniuse.com/websockets[caniuse.com/websockets] to find out.
+____
+
+http://www.jcp.org/en/jsr/detail?id=356[JSR-356]::
+  The Java WebSocket API (`javax.websocket`)
++
+This is the official Java API for working with WebSockets.
+
+Unstable standards and specs:
+
+https://datatracker.ietf.org/doc/draft-ietf-hybi-websocket-perframe-compression/[perframe-compression]::
+  Per Frame Compression Extension.
++
+An early extension draft from the Google/Chromium team that would provide WebSocket frame compression.
++
+perframe-compression using deflate algorithm is present on many versions of Chrome/Chromium.
++
+Jetty's support for perframe-compression is based on the draft-04 spec.
++
+This standard is being replaced with permessage-compression.
+
+https://datatracker.ietf.org/doc/draft-tyoshino-hybi-permessage-compression/[permessage-compression]::
+  Per Frame Compression Extension.
++
+This is the replacement for perframe-compression, switching the compression to being based on the entire message, not the individual frames.
+
+[[ws-intro-api]]
+=== WebSocket APIs
+
+APIs and libraries to implement your WebSockets using Jetty.
+
+Jetty WebSocket API::
+  The basic common API for creating and working with WebSockets using Jetty.
+Jetty WebSocket Server API::
+  Write WebSocket Server Endpoints for Jetty.
+Jetty WebSocket Client API::
+  Connect to WebSocket servers with Jetty.
+Java WebSocket Client API::
+  The new standard Java WebSocket Client API (`javax.websocket`) [JSR-356]
+Java WebSocket Server API::
+  The new standard Java WebSocket Server API (`javax.websocket.server`) [JSR-356]
+
+=== Enabling WebSocket
+
+To enable websocket, you need to link:#enabling-modules[enable] the `websocket` link:#enabling-modules[module].
+
+Once this module is enabled for your jetty base, it will apply to all webapps deployed to that base.
+If you want to be more selective about which webapps use websocket, then you can:
+
+Disable jsr-356 for a particular webapp:::
+  You can disable jsr-356 for a particular webapp by setting the link:#context_attributes[context attribute] `org.eclipse.jetty.websocket.jsr356` to `false`.
+  This will mean that websockets are not available to your webapp, however deployment time   scanning for websocket-related classes such as endpoints will still occur.
+  This can be a significant impost if your webapp contains a lot of classes and/or jar files.
+  To completely disable websockets and avoid all setup costs associated with it for a particular webapp, use instead the context attribute `org.eclipse.jetty.containerInitializerExclusionPattern`, described next, which allows you to exclude the websocket ServletContainerInitializer that causes the scanning.
+Completely disable jsr-356 for a particular webapp:::
+  Set the `org.eclipse.jetty.containerInitializerExclusionPattern` link:#context_attributes[context attribute] to include `org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer`.
+  Here's an example of doing this in code, although you can do the link:#intro-jetty-configuration-webapps[same in xml]:
++
+[source, java, subs="{sub-order}"]
+----
+WebAppContext context = new WebAppContext();
+context.setAttribute("org.eclipse.jetty.containerInitializerExclusionPattern", 
+                     "org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer|com.acme.*");
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/java/chapter.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/java/chapter.adoc
new file mode 100644
index 0000000..d551ff1
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/java/chapter.adoc
@@ -0,0 +1,21 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[websocket-java]]
+== Java Websocket API
+
+JSR-356 These pages are works in progress that have not been moved to
+their respective sections yet.
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/java/java-websocket-client-api.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/java/java-websocket-client-api.adoc
new file mode 100644
index 0000000..b37d4ab
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/java/java-websocket-client-api.adoc
@@ -0,0 +1,22 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[java-websocket-client-api]]
+= Java WebSocket Client API Usage
+
+== Java WebSocket Client API
+
+The simpler way to perform a websocket request is the following:
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/java/java-websocket-server-api.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/java/java-websocket-server-api.adoc
new file mode 100644
index 0000000..418c941
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/java/java-websocket-server-api.adoc
@@ -0,0 +1,22 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[java-websocket-server-api]]
+= Java WebSocket Server API
+
+== Java WebSocket Server API
+
+The simpler way to perform a websocket request is the following:
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/jetty/chapter.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/chapter.adoc
new file mode 100644
index 0000000..a93e154
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/chapter.adoc
@@ -0,0 +1,30 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[websocket-jetty]]
+== Jetty Websocket API
+
+These pages are works in progress that have not been moved to their respective sections yet.
+
+include::jetty-websocket-api.adoc[]
+include::jetty-websocket-api-events.adoc[]
+include::jetty-websocket-api-session.adoc[]
+include::jetty-websocket-api-send-message.adoc[]
+include::jetty-websocket-api-annotations.adoc[]
+include::jetty-websocket-api-listener.adoc[]
+include::jetty-websocket-api-adapter.adoc[]
+include::jetty-websocket-server-api.adoc[]
+include::jetty-websocket-client-api.adoc[]
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-adapter.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-adapter.adoc
new file mode 100644
index 0000000..50e93fd
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-adapter.adoc
@@ -0,0 +1,27 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-websocket-api-adapter]]
+=== Using the WebSocketAdapter
+
+A basic adapter for managing the Session object on the WebSocketListener.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-websocket/websocket-common/src/test/java/examples/echo/AdapterEchoSocket.java[]
+----
+
+This is a convenience class to make using the WebSocketListener easier, and provides some useful methods to check the state of the Session.
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-annotations.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-annotations.adoc
new file mode 100644
index 0000000..c81175e
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-annotations.adoc
@@ -0,0 +1,107 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-websocket-api-annotations]]
+=== Using WebSocket Annotations
+
+The most basic form of WebSocket is a marked up POJO with annotations
+provided by the Jetty WebSocket API.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-websocket/websocket-common/src/test/java/examples/echo/AnnotatedEchoSocket.java[]
+----
+
+The above example is a simple WebSocket echo endpoint that will echo back any TEXT messages it receives.
+
+This implementation is using a stateless approach to a Echo socket, as the Session is being passed into the Message event as the event occurs.
+This would allow you to reuse the single instance of the AnnotatedEchoSocket for working with multiple endpoints.
+
+The annotations you have available:
+
+link:{JDURL}/org/eclipse/jetty/websocket/api/annotations/WebSocket.html[@WebSocket]::
+  A required class level annotation.
++
+Flags this POJO as being a WebSocket.
++
+The class must be not abstract and public.
+link:{JDURL}/org/eclipse/jetty/websocket/api/annotations/OnWebSocketConnect.html[@OnWebSocketConnect]::
+  An optional method level annotation.
++
+Flags one method in the class as receiving the On Connect event.
++
+Method must be public, not abstract, return void, and have a single link:{JDURL}/org/eclipse/jetty/websocket/api/Session.html[Session] parameter.
+
+link:{JDURL}/org/eclipse/jetty/websocket/api/annotations/OnWebSocketClose.html[@OnWebSocketClose]::
+  An optional method level annotation.
++
+Flags one method in the class as receiving the On Close event.
++
+Method signature must be public, not abstract, and return void.
++
+The method parameters:
++
+. link:{JDURL}/org/eclipse/jetty/websocket/api/Session.html[`Session`] (optional)
+. `int closeCode` (required)
+. `String closeReason` (required)
+
+link:{JDURL}/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.html[@OnWebSocketMessage]::
+  An optional method level annotation.
++
+Flags up to 2 methods in the class as receiving On Message events.
++
+You can have 1 method for TEXT messages, and 1 method for BINARY messages.
++
+Method signature must be public, not abstract, and return void.
++
+The method parameters for Text messages:
++
+* link:{JDURL}/org/eclipse/jetty/websocket/api/Session.html[`Session`] (optional)
+* `String text` (required)
++
+The method parameters for Binary messages:
++
+* link:{JDURL}/org/eclipse/jetty/websocket/api/Session.html[`Session`] (optional)
+* `byte buf[]` (required)
+* `int offset` (required)
+* `int length` (required)
+
+link:{JDURL}/org/eclipse/jetty/websocket/api/annotations/OnWebSocketError.html[@OnWebSocketError]::
+  An optional method level annotation.
++
+Flags one method in the class as receiving Error events from the WebSocket implementation.
++
+Method signatures must be public, not abstract, and return void.
++
+The method parameters:
++
+1.  link:{JDURL}/org/eclipse/jetty/websocket/api/Session.html[`Session`] (optional)
+2.  `Throwable cause` (required)
+
+link:{JDURL}/org/eclipse/jetty/websocket/api/annotations/OnWebSocketFrame.html[@OnWebSocketFrame]::
+  An optional method level annotation.
++
+Flags one method in the class as receiving Frame events from the WebSocket implementation after they have been processed by any extensions declared during the Upgrade handshake.
++
+Method signatures must be public, not abstract, and return void.
++
+The method parameters:
++
+1.  link:{JDURL}/org/eclipse/jetty/websocket/api/Session.html[`Session`] (optional)
+2.  link:{JDURL}/org/eclipse/jetty/websocket/api/extensions/Frame.html[`Frame`] (required)
++
+The Frame received will be notified on this method, then be processed by Jetty, possibly resulting in another event, such as On Close, or On Message.
+Changes to the Frame will not be seen by Jetty.
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-events.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-events.adoc
new file mode 100644
index 0000000..7565ba2
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-events.adoc
@@ -0,0 +1,47 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-websocket-api-events]]
+=== WebSocket Events
+
+Every WebSocket can receive various events:
+
+On Connect Event::
+  An indication to the WebSocket that the Upgrade has succeeded and the WebSocket is now open.
++
+You will receive a link:{JDURL}/org/eclipse/jetty/websocket/api/Session.html[`org.eclipse.jetty.websocket.api.Session`] object that references the specific session for this Open Event.
++
+For normal WebSockets, it is important to hold onto this Session and use it for communicating with the Remote Endpoint.
++
+For Stateless WebSockets, the Session will be passed into each event as it occurs, allowing you to only have 1 instance of a WebSocket serving many Remote Endpoints.
+
+On Close Event::
+  An indication that the WebSocket is now closed.
++
+Every Close Event will have a link:{JDURL}/org/eclipse/jetty/websocket/api/StatusCode.html[Status   Code] (and an optional Closure Reason Message)
++
+A normal WebSocket closure will go through a Close Handshake where both the Local Endpoint and the Remote Endpoint both send a Close frame to indicate that the connection is closed.
++
+It is possible for the Local WebSocket to indicate its desire to Close by issuing a Close frame to the Remote Endpoint, but the Remote Endpoint can continue to send messages until it sends a Close Frame.
+This is known as a Half-Open connection, and it is important to note that once the Local Endpoint has send the Close Frame it cannot write anymore WebSocket traffic.
++
+On an abnormal closure, such as a connection disconnect or a connection timeout, the low level connection will be terminated without going through a Close Handshake, this will still result in an On Close Event (and likely a corresponding On Error Event).
+On Error Event::
+  If an error occurred, during the implementation, the WebSocket will be notified via this event handler.
+On Message Event::
+  An indication that a complete message has been received and is ready for handling by your WebSocket.
++
+This can be a (UTF8) TEXT message or a raw BINARY message.
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-listener.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-listener.adoc
new file mode 100644
index 0000000..dc22643
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-listener.adoc
@@ -0,0 +1,28 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-websocket-api-listener]]
+=== Using WebSocketListener
+
+The basic form of a WebSocket using the link:{JDURL}/org/eclipse/jetty/websocket/api/WebSocketListener.html[`org.eclipse.jetty.websocket.api.WebSocketListener`] for incoming events.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-websocket/websocket-common/src/test/java/examples/echo/ListenerEchoSocket.java[]
+----
+
+This is by far the most basic and best performing (speed and memory wise) WebSocket implementation you can create.
+If the listener is too much work for you, you can instead opt for the WebSocketAdapter
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-send-message.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-send-message.adoc
new file mode 100644
index 0000000..d89a77b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-send-message.adoc
@@ -0,0 +1,298 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-websocket-api-send-message]]
+=== Send Messages to Remote Endpoint
+
+The most important feature of the Session is access to the link:{JDURL}/org/eclipse/jetty/websocket/api/RemoteEndpoint.html[`org.eclipse.jetty.websocket.api.RemoteEndpoint`]needed to send messages.
+
+With RemoteEndpoint you can choose to send TEXT or BINARY WebSocket messages, or the WebSocket PING and PONG control frames.
+
+[[blocking]]
+==== Blocking Send Message
+
+Most calls are blocking in nature, and will not return until the send has completed (or has thrown an exception).
+
+[source, java, subs="{sub-order}"]
+----
+RemoteEndpoint remote = session.getRemote();
+
+// Blocking Send of a BINARY message to remote endpoint
+ByteBuffer buf = ByteBuffer.wrap(new byte[] { 0x11, 0x22, 0x33, 0x44 });
+try
+{
+    remote.sendBytes(buf);
+}
+catch (IOException e)
+{
+    e.printStackTrace(System.err);
+}
+----
+
+How to send a simple Binary message using the RemoteEndpoint.
+This will block until the message is sent, possibly throwing an IOException if unable to send the message.
+
+[source, java, subs="{sub-order}"]
+----
+RemoteEndpoint remote = session.getRemote();
+
+// Blocking Send of a TEXT message to remote endpoint
+try
+{
+    remote.sendString("Hello World");
+}
+catch (IOException e)
+{
+    e.printStackTrace(System.err);
+}
+----
+
+How to send a simple Text message using the RemoteEndpoint.
+This will block until the message is sent, possibly throwing an IOException if unable to send the message.
+
+[[partial]]
+==== Send Partial Message
+
+If you have a large message to send, and want to send it in pieces and parts, you can utilize the partial message sending methods of RemoteEndpoint.
+Just be sure you finish sending your message (`isLast == true`).
+
+[source, java, subs="{sub-order}"]
+----
+RemoteEndpoint remote = session.getRemote();
+
+// Blocking Send of a BINARY message to remote endpoint
+// Part 1
+ByteBuffer buf1 = ByteBuffer.wrap(new byte[] { 0x11, 0x22 });
+// Part 2 (last part)
+ByteBuffer buf2 = ByteBuffer.wrap(new byte[] { 0x33, 0x44 });
+try
+{
+    remote.sendPartialBytes(buf1,false);
+    remote.sendPartialBytes(buf2,true); // isLast is true
+}
+catch (IOException e)
+{
+    e.printStackTrace(System.err);
+}
+----
+
+How to send a Binary message in 2 parts, using the partial message support in RemoteEndpoint.
+This will block until each part of the message is sent, possibly throwing an IOException if unable to send the partial message.
+
+[source, java, subs="{sub-order}"]
+----
+RemoteEndpoint remote = session.getRemote();
+
+// Blocking Send of a TEXT message to remote endpoint
+String part1 = "Hello";
+String part2 = " World";
+try
+{
+    remote.sendPartialString(part1,false);
+    remote.sendPartialString(part2,true); // last part
+}
+catch (IOException e)
+{
+    e.printStackTrace(System.err);
+}
+----
+
+How to send a Text message in 2 parts, using the partial message support in RemoteEndpoint.
+This will block until each part of the message is sent, possibly throwing an IOException if unable to send the partial message.
+
+[[pingpong]]
+==== Send Ping / Pong Control Frame
+
+You can also send Ping and Pong control frames using the RemoteEndpoint.
+
+[source, java, subs="{sub-order}"]
+----
+RemoteEndpoint remote = session.getRemote();
+
+// Blocking Send of a PING to remote endpoint
+String data = "You There?";
+ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
+try
+{
+    remote.sendPing(payload);
+}
+catch (IOException e)
+{
+    e.printStackTrace(System.err);
+}
+----
+
+How to send a Ping control frame, with a payload of `"You There?"` (arriving at Remote Endpoint as a byte array payload).
+This will block until the message is sent, possibly throwing an IOException if unable to send the ping frame.
+
+[source, java, subs="{sub-order}"]
+----
+RemoteEndpoint remote = session.getRemote();
+
+// Blocking Send of a PONG to remote endpoint
+String data = "Yup, I'm here";
+ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
+try
+{
+    remote.sendPong(payload);
+}
+catch (IOException e)
+{
+    e.printStackTrace(System.err);
+}
+----
+
+How to send a Pong control frame, with a payload of `"Yup I'm here"` (arriving at Remote Endpoint as a byte array payload).
+This will block until the message is sent, possibly throwing an IOException if unable to send the pong frame.
+
+To be correct in your usage of Pong frames, you should return the same byte array data that you received in the Ping frame.
+
+[[async]]
+==== Async Send Message
+
+However there are also 2 Async send message methods available:
+
+* link:{JDURL}/org/eclipse/jetty/websocket/api/RemoteEndpoint.html#sendBytesByFuture(java.nio.ByteBuffer)[`RemoteEndpoint.sendBytesByFuture(ByteBuffer message)`]
+* link:{JDURL}/org/eclipse/jetty/websocket/api/RemoteEndpoint.html#sendStringByFuture(java.lang.String)[`RemoteEndpoint.sendStringByFuture(String message)`]
+
+Both return a `Future<Void>` that can be used to test for success and failure of the message send using standard http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html[`java.util.concurrent.Future`] behavior.
+
+[source, java, subs="{sub-order}"]
+----
+RemoteEndpoint remote = session.getRemote();
+
+// Async Send of a BINARY message to remote endpoint
+ByteBuffer buf = ByteBuffer.wrap(new byte[] { 0x11, 0x22, 0x33, 0x44 });
+remote.sendBytesByFuture(buf);
+----
+
+How to send a simple Binary message using the RemoteEndpoint.
+The message will be enqueued for outgoing write, but you will not know if it succeeded or failed.
+
+[source, java, subs="{sub-order}"]
+----
+RemoteEndpoint remote = session.getRemote();
+
+// Async Send of a BINARY message to remote endpoint
+ByteBuffer buf = ByteBuffer.wrap(new byte[] { 0x11, 0x22, 0x33, 0x44 });
+try
+{
+    Future<Void> fut = remote.sendBytesByFuture(buf);
+    // wait for completion (forever)
+    fut.get();
+}
+catch (ExecutionException | InterruptedException e)
+{
+    // Send failed
+    e.printStackTrace();
+}
+----
+
+How to send a simple Binary message using the RemoteEndpoint, tracking the `Future<Void>` to know if the send succeeded or failed.
+
+[source, java, subs="{sub-order}"]
+----
+RemoteEndpoint remote = session.getRemote();
+
+// Async Send of a BINARY message to remote endpoint
+ByteBuffer buf = ByteBuffer.wrap(new byte[] { 0x11, 0x22, 0x33, 0x44 });
+Future<Void> fut = null;
+try
+{
+    fut = remote.sendBytesByFuture(buf);
+    // wait for completion (timeout)
+    fut.get(2,TimeUnit.SECONDS);
+}
+catch (ExecutionException | InterruptedException e)
+{
+    // Send failed
+    e.printStackTrace();
+}
+catch (TimeoutException e)
+{
+    // timeout
+    e.printStackTrace();
+    if (fut != null)
+    {
+        // cancel the message
+        fut.cancel(true);
+    }
+}
+----
+
+How to send a simple Binary message using the RemoteEndpoint, tracking the `Future<Void>` and waiting only prescribed amount of time for the send to complete, cancelling the message if the timeout occurs.
+
+[source, java, subs="{sub-order}"]
+----
+RemoteEndpoint remote = session.getRemote();
+
+// Async Send of a TEXT message to remote endpoint
+remote.sendStringByFuture("Hello World");
+----
+
+How to send a simple Text message using the RemoteEndpoint.
+The message will be enqueued for outgoing write, but you will not know if it succeeded or failed.
+
+[source, java, subs="{sub-order}"]
+----
+RemoteEndpoint remote = session.getRemote();
+
+// Async Send of a TEXT message to remote endpoint
+try
+{
+    Future<Void> fut = remote.sendStringByFuture("Hello World");
+    // wait for completion (forever)
+    fut.get();
+}
+catch (ExecutionException | InterruptedException e)
+{
+    // Send failed
+    e.printStackTrace();
+}
+----
+
+How to send a simple Binary message using the RemoteEndpoint, tracking the `Future<Void>` to know if the send succeeded or failed.
+
+[source, java, subs="{sub-order}"]
+----
+RemoteEndpoint remote = session.getRemote();
+
+// Async Send of a TEXT message to remote endpoint
+Future<Void> fut = null;
+try
+{
+    fut = remote.sendStringByFuture("Hello World");
+    // wait for completion (timeout)
+    fut.get(2,TimeUnit.SECONDS);
+}
+catch (ExecutionException | InterruptedException e)
+{
+    // Send failed
+    e.printStackTrace();
+}
+catch (TimeoutException e)
+{
+    // timeout
+    e.printStackTrace();
+    if (fut != null)
+    {
+        // cancel the message
+        fut.cancel(true);
+    }
+}
+----
+
+How to send a simple Binary message using the RemoteEndpoint, tracking the `Future<Void>` and waiting only prescribed amount of time for the send to complete, cancelling the message if the timeout occurs.
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-session.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-session.adoc
new file mode 100644
index 0000000..abcee12
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api-session.adoc
@@ -0,0 +1,63 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-websocket-api-session]]
+=== WebSocket Session
+
+The link:{JDURL}/org/eclipse/jetty/websocket/api/Session.html[Session] object can be used to:
+
+The Connection State (is it open or not).
+
+[source, java, subs="{sub-order}"]
+----
+if(session.isOpen()) {
+  // send message
+}
+----
+
+Is the Connection Secure.
+
+[source, java, subs="{sub-order}"]
+----
+if(session.isSecure()) {
+  // connection is using 'wss://'
+}
+----
+
+What was in the Upgrade Request and Response.
+
+[source, java, subs="{sub-order}"]
+----
+UpgradeRequest req = session.getUpgradeRequest();
+String channelName = req.getParameterMap().get("channelName");
+
+UpgradeRespons resp = session.getUpgradeResponse();
+String subprotocol = resp.getAcceptedSubProtocol();
+----
+
+What is the Local and Remote Address.
+
+[source, java, subs="{sub-order}"]
+----
+InetSocketAddress remoteAddr = session.getRemoteAddress();
+----
+
+Get and Set the Idle Timeout
+
+[source, java, subs="{sub-order}"]
+----
+session.setIdleTimeout(2000); // 2 second timeout
+----
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api.adoc
new file mode 100644
index 0000000..723e146
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-api.adoc
@@ -0,0 +1,22 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-websocket-api]]
+=== Jetty WebSocket API Usage
+
+Jetty provides its own more powerful WebSocket API, with a common core API for both server and client use of WebSockets.
+
+It is an event driven API based on WebSocket Messages.
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-client-api.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-client-api.adoc
new file mode 100644
index 0000000..8bc4433
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-client-api.adoc
@@ -0,0 +1,51 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-websocket-client-api]]
+=== Jetty WebSocket Client API
+
+Jetty also provides a Jetty WebSocket Client Library to write make talking to WebSocket servers easier.
+
+To use the Jetty WebSocket Client on your own Java project you will need the following maven artifacts.
+
+[source, xml, subs="{sub-order}"]
+----
+<dependency>
+  <groupId>org.eclipse.jetty.websocket</groupId>
+  <artifactId>websocket-client</artifactId>
+  <version>${project.version}</version>
+</dependency>
+----
+
+==== The WebSocketClient
+
+To use the WebSocketClient you will need to hook up a WebSocket object instance to a specific destination WebSocket URI.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-websocket/websocket-client/src/test/java/examples/SimpleEchoClient.java[]
+----
+
+The above example connects to a remote WebSocket server and hands off a SimpleEchoSocket to perform the logic on the websocket once connected, waiting for the socket to register that it has closed.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-websocket/websocket-client/src/test/java/examples/SimpleEchoSocket.java[]
+----
+
+When the SimpleEchoSocket connects, it sends 2 Text messages and then closes the socket.
+
+The onMessage(String msg) receives the responses from the remote server WebSocket and outputs them to the console.
diff --git a/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-server-api.adoc b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-server-api.adoc
new file mode 100644
index 0000000..24c80b9
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/development/websockets/jetty/jetty-websocket-server-api.adoc
@@ -0,0 +1,79 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-websocket-server-api]]
+=== Jetty WebSocket Server API
+
+Jetty provides the ability to wire up WebSocket endpoints to Servlet Path Specs via the use of a WebSocketServlet bridge servlet.
+
+Internally, Jetty manages the HTTP Upgrade to WebSocket and migration from a HTTP Connection to a WebSocket Connection.
+
+This will only work when running within the Jetty Container (unlike past Jetty technologies, you cannot get Jetty WebSocket server functionality running Jetty within other containers like JBoss, Tomcat, or WebLogic).
+
+==== The Jetty WebSocketServlet
+
+To wire up your WebSocket to a specific path via the WebSocketServlet, you will need to extend org.eclipse.jetty.websocket.servlet.WebSocketServlet and specify what WebSocket object should be created with incoming Upgrade requests.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-websocket/websocket-servlet/src/test/java/examples/MyEchoServlet.java[]
+----
+
+This example will create a Servlet mapped via the http://docs.oracle.com/javaee/6/api/javax/servlet/annotation/WebServlet.html[@WebServlet] annotation to the Servlet path spec of `"/echo"` (or you can do this manually in the `WEB-INF/web.xml` of your web application) which will create MyEchoSocket instances when encountering HTTP Upgrade requests.
+
+The link:{JDURL}/org/eclipse/jetty/websocket/servlet/WebSocketServlet.html#configure(org.eclipse.jetty.websocket.servlet.WebSocketServletFactory)[`WebSocketServlet.configure(WebSocketServletFactory factory)`] is where you put your specific configuration for your WebSocket.
+In the example we specify a 10 second idle timeout and register MyEchoSocket with the default WebSocketCreator the WebSocket class we want to be created on Upgrade.
+
+____
+[NOTE]
+It is important that you take in account any firewall or router timeouts
+when configuring websockets. Be sure the websocket configuration is
+lower than your firewall or router.
+____
+
+==== Using the WebSocketCreator
+
+All WebSocket's are created via whatever link:{JDURL}/org/eclipse/jetty/websocket/servlet/WebSocketCreator.html[WebSocketCreator] you have registered with the link:{JDURL}/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.html[WebSocketServletFactory].
+
+By default, the WebSocketServletFactory is a simple WebSocketCreator capable of creating a single WebSocket object.
+Use link:{JDURL}/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.html#register(java.lang.Class)[`WebSocketCreator.register(Class<?> websocket)`] to tell the WebSocketServletFactory which class it should instantiate (make sure it has a default constructor).
+
+If you have a more complicated creation scenario, you might want to provide your own WebSocketCreator that bases the WebSocket it creates off of information present in the UpgradeRequest object.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-websocket/websocket-servlet/src/test/java/examples/MyAdvancedEchoCreator.java[]
+----
+
+Here we show a WebSocketCreator that will utilize the http://tools.ietf.org/html/rfc6455#section-1.9[WebSocket subprotocol] information from request to determine what WebSocket type should be
+created.
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/jetty-websocket/websocket-servlet/src/test/java/examples/MyAdvancedEchoServlet.java[]
+----
+
+When you want a custom WebSocketCreator, use link:{JDURL}/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.html#setCreator(org.eclipse.jetty.websocket.servlet.WebSocketCreator)[`WebSocketServletFactory.setCreator(WebSocketCreator creator)`] and the WebSocketServletFactory will use your creator for all incoming Upgrade requests on this servlet.
+
+Other uses for a WebSocketCreator:
+
+* Controlling the selection of WebSocket subprotocol
+* Performing any WebSocket origin you deem important.
+* Obtaining the HTTP headers from incoming request
+* Obtaining the Servlet HttpSession object (if it exists)
+* Specifying a response status code and reason
+
+If you don't want to accept the upgrade, simply return null from the link:{JDURL}/org/eclipse/jetty/websocket/servlet/WebSocketCreator.html#createWebSocket(org.eclipse.jetty.websocket.api.UpgradeRequest, org.eclipse.jetty.websocket.api.UpgradeResponse)[`WebSocketCreator.createWebSocket(UpgradeRequest req, UpgradeResponse resp)`] method.
diff --git a/jetty-documentation/src/main/asciidoc/index-docinfo.xml b/jetty-documentation/src/main/asciidoc/index-docinfo.xml
new file mode 100644
index 0000000..8fcb9cf
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/index-docinfo.xml
@@ -0,0 +1,96 @@
+<copyright>
+  <year>1995-2017</year>
+  <holder>Mort Bay Consulting Pty. Ltd.</holder>
+</copyright>

+<revhistory>

+  <revision>

+    <revnumber>{revnumber}</revnumber>

+    <date>

+      <?dbtimestamp format="Y-m-d H:M:S"?>

+    </date>

+    <revdescription>

+      <para>This documentation is produced and contributed to under the Eclipse Public License v1.0.</para>
+    </revdescription>

+  </revision>

+</revhistory>

+<keywordset>

+  <keyword>jetty</keyword>

+  <keyword>servlet</keyword>

+  <keyword>servlet-api</keyword>

+  <keyword>cometd</keyword>

+  <keyword>http</keyword>

+  <keyword>websocket</keyword>

+  <keyword>eclipse</keyword>

+  <keyword>maven</keyword>

+  <keyword>java</keyword>

+  <keyword>server</keyword>

+  <keyword>software</keyword>

+</keywordset>

+<authorgroup>

+  <author>

+    <personname>

+      <firstname>Jan</firstname>

+      <surname>Bartel</surname>

+    </personname>

+    <affiliation>

+      <shortaffil>Jetty</shortaffil>

+      <jobtitle>Project Lead</jobtitle>

+    </affiliation>

+  </author>

+  <author>

+    <personname>

+      <firstname>Thomas</firstname>

+      <surname>Becker</surname>

+    </personname>

+    <affiliation>

+      <shortaffil>Jetty</shortaffil>

+      <jobtitle>Committer</jobtitle>

+    </affiliation>

+  </author>

+  <author>

+    <personname>

+      <firstname>Simone</firstname>

+      <surname>Bordet</surname>

+    </personname>

+    <affiliation>

+      <shortaffil>Jetty</shortaffil>

+      <jobtitle>Committer</jobtitle>

+    </affiliation>

+  </author>

+  <author>

+    <personname>

+      <firstname>Joakim</firstname>

+      <surname>Erdfelt</surname>

+    </personname>

+    <affiliation>

+      <shortaffil>Jetty</shortaffil>

+      <jobtitle>Committer</jobtitle>

+    </affiliation>

+  </author>

+  <author>

+    <personname>

+      <firstname>Jesse</firstname>

+      <surname>McConnell</surname>

+    </personname>

+    <affiliation>

+      <shortaffil>Jetty</shortaffil>

+      <jobtitle>Committer</jobtitle>

+    </affiliation>

+  </author>

+  <author>

+    <personname>

+      <firstname>Greg</firstname>

+      <surname>Wilkins</surname>

+    </personname>

+    <affiliation>

+      <shortaffil>Jetty</shortaffil>

+      <jobtitle>Project Lead</jobtitle>

+    </affiliation>

+  </author>

+  <editor>

+    <personname>

+      <firstname>Shirley</firstname>

+      <surname>Boulay</surname>

+    </personname>

+  </editor>

+</authorgroup>

diff --git a/jetty-documentation/src/main/asciidoc/index.adoc b/jetty-documentation/src/main/asciidoc/index.adoc
new file mode 100644
index 0000000..5356fac
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/index.adoc
@@ -0,0 +1,28 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+Jetty : The Definitive Reference
+================================
+:docinfo:
+:revnumber: {VERSION}
+
+:toc:
+
+include::quick-start/part.adoc[]
+include::configuring/part.adoc[]
+include::administration/part.adoc[]
+include::development/part.adoc[]
+include::reference/part.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/configuring/chapter.adoc b/jetty-documentation/src/main/asciidoc/quick-start/configuring/chapter.adoc
new file mode 100644
index 0000000..7379a70
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/configuring/chapter.adoc
@@ -0,0 +1,21 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[quick-start-configure]]
+== An Introduction to Jetty Configuration
+
+include::how-to-configure.adoc[]
+include::what-to-configure.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/configuring/how-to-configure.adoc b/jetty-documentation/src/main/asciidoc/quick-start/configuring/how-to-configure.adoc
new file mode 100644
index 0000000..2cbc490
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/configuring/how-to-configure.adoc
@@ -0,0 +1,128 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[quickstart-config-how]]
+=== How to Configure Jetty
+
+To understand Jetty configuration, you need to understand the "How" and the "What". 
+This section covers how to configure Jetty in terms of what mechanisms exist to perform configuration. 
+The link:#quickstart-config-what[next section] gives an overview of the action components and fields that you can configure with these mechanisms.
+
+==== Jetty POJO Configuration
+
+The core components of Jetty are Plain Old Java Objects (http://en.wikipedia.org/wiki/Plain_Old_Java_Object[POJOs])
+The process of configuring Jetty is mostly the process of instantiating, assembling and setting fields on the Jetty POJOs. 
+This can be achieved by:
+
+* Writing Java code to directly instantiate and assemble Jetty objects. 
+This is referred to as xref:embedding-jetty[].
+* Using Jetty XML configuration, which is an http://en.wikipedia.org/wiki/Inversion_of_Control[Inversion of Control (IoC)] framework, to instantiate and assemble Jetty objects as XML objects. 
+The `etc/jetty.xml` file is the main Jetty XML configuration file, but there are many other `etc/jetty-__feature__.xml` files included in the Jetty distribution.
+* Using a third party http://en.wikipedia.org/wiki/Inversion_of_Control[IoC] framework like http://en.wikipedia.org/wiki/Spring_Framework[Spring], to instantiate and assemble Jetty objects as Spring beans.
+
+Because the main Jetty configuration is done by IoC, the link:{JDURL}/[Jetty API documentation] is the ultimate configuration reference.
+
+==== Jetty Start Configuration Files
+
+The Jetty distribution uses the following configuration files to instantiate, inject and start server via the `start.jar` mechanism.
+
+`ini` files::
+  The Jetty Start mechanism uses the command line, the `$JETTY_BASE/start.ini` and/or `$JETTY_BASE/start.d/*.ini` files to create an effective command line of arguments. 
+  Arguments may be:
+  
+  * Module activations in the form `--module=name`
+  * Properties in the form of `name=value`, used to parameterize Jetty IoC XML
+  * XML files in Jetty IoC (or Spring) XML format
+  * A standard http://en.wikipedia.org/wiki/Java_properties[Java property file] containing additional start properties
+  * Other start.jar options (see `java -jar start.jar --help`)
+  * Some JVM options in combination with `--exec`, such as `-Xbootclasspath`.
+
+____
+[NOTE]
+--
+As of Jetty 9 it is the `ini` files located in the Jetty base directory (if different from Jetty home) that are typically edited to change the configuration (e.g. change ports). 
+--
+____
+  
+`mod` files::
+  The `$JETTY_HOME/modules/*.mod` files contain the definition of modules that can be activated by `--module=name`. 
+  Each `mod` file defines:
+  
+  * Module dependencies for ordering and activation
+  * The libraries needed by the module to be added to the classpath
+  * The XML files needed by the module to be added to the effective command line
+  * Files needed by the activated module
+  * A template `ini` file to be used when activating the `--add-to-start=name` option
++  
+Typically module files are rarely edited and only then for significant structural changes. 
+The `*.mod` files are normally located in `$JETTY_HOME/modules/`, but extra or edited modules may be added to `$JETTY_BASE/module`. 
+If module changes are required, it is best practice to copy the particular `*.mod` file from `$JETTY_HOME/modules/` to `$JETTY_BASE/modules/` before being modified.
+
+XML files::
+  XML files in link:#jetty-xml-syntax[Jetty IoC XML format] or Spring IoC format are listed either on the command line, in `ini` files, or are added to the effective command line by a module definition. 
+  The XML files instantiate and inject the actual Java objects that comprise the server, connectors and contexts. 
+  Because Jetty IoC XML files use properties, most common configuration tasks can be accomplished without editing these XML files and can instead be achieved by editing the property in the corresponding `ini` files.
+  XML files are normally located in `$JETTY_HOME/etc/`, but extra or edited XML files may be added to `$JETTY_BASE/etc/`. 
+  *Note* If XML configuration changes are required, it is best practice to copy the XML file from `$JETTY_HOME/etc/` to `$JETTY_BASE/etc/` before being modified.
+  
+Below is an illustration of how the various Jetty configuration files (`ini`, `mod` and XML) are related:
+
+image:images/Jetty_Configuration_File_Relationships.png[image,width=693]
+
+==== Other Configuration Files
+
+In addition to the configuration files described above, the configuration of the server can use the following file types:
+
+Context XML files::
+  Any XML files in link:#jetty-xml-syntax[Jetty IoC XML format] or Spring IoC format that is discovered in the `/webapps` directory are used by the deploy module to instantiate and inject `HttpContext` instances to create a specific context. 
+  These may be standard web applications or bespoke contexts created from special purpose handlers.
+
+web.xml::
+  The http://en.wikipedia.org/wiki/Servlet[Servlet] Specification defines the http://en.wikipedia.org/wiki/Web.xml[`web.xml`] deployment descriptor that defines and configures the filters, servlets and resources a http://en.wikipedia.org/wiki/Web_application[web application] uses. 
+  The Jetty `WebAppContext` component uses this XML format to:
+
+  * Set up the default configuration of a web application context.
+  * Interpret the application-specific configuration supplied with a web application in the `WEB-INF/web.xml` file.
+  * Interpret descriptor fragments included in the `META-INF` directory of Jar files within `WEB-INF/lib.`
++
+Normally the `web.xml` file for a web application is found in the `WEB-INF/web.xml` location within the war file/directory or as `web.xml` fragments with `.jar` files found in `WEB-INF/lib`. 
+Jetty also supports multiple `web.xml` files so that a default descriptor may be applied before `WEB-INF/web.xml` (typically set to `etc/webdefault.xml` by the deploy module) and an override descriptor may be applied after `WEB-INF/web.xml` (typically set by a context XML file see `test.xml`)
+
+Property Files::
+  Standard http://en.wikipedia.org/wiki/Java_properties[Java property files] are also used for Jetty configuration in several ways:
++  
+  * To parameterize Jetty IoC XML via the use of the `Property` element.
+  * To configure the default logging mechanism (`StdErrLog`). Other logging frameworks can be utilized and also use property files (for example, `log4j`).
+  * As a simple database for login usernames and credentials.
+
+==== Jetty IoC XML format
+
+To understand the link:#jetty-xml-syntax[Jetty IoC XML format], consider the following example of an embedded Jetty server instantiated and configured in Java:
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java[]
+----
+
+link:#jetty-xml-syntax[Jetty IoC XML format] allows you to instantiate and configure the exact same server in XML without writing any java code:
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/resources/exampleserver.xml[]
+----
+
+//In practice, most commonly used Jetty features have XML files that are included in the standard distribution in the `/etc` directory. 
+//Thus configuring Jetty is often a matter of just editing existing XML files and altering the property values injected into them.
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/configuring/images/Jetty_Configuration_File_Relationships.png b/jetty-documentation/src/main/asciidoc/quick-start/configuring/images/Jetty_Configuration_File_Relationships.png
new file mode 100644
index 0000000..d327793
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/configuring/images/Jetty_Configuration_File_Relationships.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/configuring/what-to-configure.adoc b/jetty-documentation/src/main/asciidoc/quick-start/configuring/what-to-configure.adoc
new file mode 100644
index 0000000..1d0d568
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/configuring/what-to-configure.adoc
@@ -0,0 +1,290 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[quickstart-config-what]]
+=== What to Configure in Jetty
+
+This section gives an overview of the components of Jetty you typically configure using the mechanisms outlined in the previous section.
+xref:basic-architecture[] describes the structure of a Jetty server, which is good background reading to understand configuration, and is vital if you want to change the structure of the server as set up by the default configurations in the Jetty distribution. 
+However, for most purposes, configuration is a matter of identifying the correct configuration file and modifying existing configuration values.
+
+[[intro-jetty-configuration-server]]
+==== Configuring the Server
+
+The Server instance is the central coordination object of a Jetty server; it provides services and life cycle management for all other Jetty server components. 
+In the standard Jetty distribution, the core server configuration is in `etc/jetty.xml` file, but you can mix in other server configurations which can include:
+
+ThreadPool::
+  The Server instance provides a ThreadPool instance that is the default Executor service other Jetty server components use. 
+  The prime configuration of the thread pool is the maximum and minimum size and is set in `start.ini` or `start.d/server.ini`.
+Handlers::
+  A Jetty server can have only a single Handler instance to handle incoming HTTP requests. 
+  However a handler may be a container or wrapper of other handlers forming a tree of handlers that typically
+  handle a request as a collaboration between the handlers from a branch of the tree from root to leaf. 
+  The default handler tree set up in the `etc/jetty.xml` file is a Handler Collection containing a Context Handler Collection and the Default Handler. 
+  The Context Handler Collection selects the next handler by context path and is where deployed Context Handler and Web Application Contexts are added to the handler tree. 
+  The Default Handler handles any requests not already handled and generates the standard 404 page. 
+  Other configuration files may add handlers to this tree (for example, `jetty-rewrite.xml`, `jetty-requestlog.xml`) or configure components to hot deploy handlers (for example, `jetty-deploy.xml`).
+Server Attributes::
+  The server holds a generic attribute map of strings to objects so that other Jetty components can associate named objects with the server, and if the value objects implement the LifeCycle interface, they are started and stopped with the server. 
+  Typically server attributes hold server-wide default values.
+Server fields::
+  The server also has some specific configuration fields that you set in `start.ini`  or `start.d/server.ini` for controlling, among other things, the sending of dates and versions in HTTP responses.
+Connectors::
+  The server holds a collection of connectors that receive connections for HTTP and the other protocols that Jetty supports. 
+  The next section, xref:intro-jetty-configuration-connectors[] describes configuration of the connectors themselves. 
+  For the server you can either set the collection of all connectors or add/remove individual connectors.
+Services::
+  The server can hold additional service objects, sometimes as attributes, but often as aggregated LifeCycle beans.
+  Examples of services are Login Services and DataSources, which you configure at the server level and then inject into the web applications that use them.
+
+[[intro-jetty-configuration-connectors]]
+==== Configuring Connectors
+
+A Jetty Server Connector is a network end point that accepts connections for one or more protocols which produce requests and/or messages for the Jetty server. 
+In the standard Jetty server distribution, several provided configuration files add connectors to the server for various protocols and combinations of protocols: `http.ini`, `https.ini` and `jetty-http2.xml`.
+The configuration needed for connectors is typically:
+
+Port::
+  The TCP/IP port on which the connector listens for connections is set using the the XML Property element which looks up the `jetty.http.port` (or `jetty.ssl.port`) property, and if not found defaults to 8080 (or 8443 for TLS).
+Host::
+  You can configure a host either as a host name or IP address to identify a specific network interface on which to listen.
+  If not set, or set to the value of 0.0.0.0, the connector listens on all local interfaces. 
+  The XML Property element is used to look up the host value from the `jetty.host` property.
+Idle Timeout::
+  The time in milliseconds that a connection can be idle before the connector takes action to close the connection.
+HTTP Configuration::
+  Connector types that accept HTTP semantics (including HTTP, HTTPS and HTTP2) are configured with a `HttpConfiguration` instance that contains common HTTP configuration that is independent of the specific wire protocol used. 
+  Because these values are often common to multiple connector types, the standard Jetty Server distribution creates a single `HttpConfiguration` in the `jetty.xml` file which is used via the XML Ref element in the specific connector files.
+SSL Context Factory::
+  The TLS connector types (HTTPS and HTTP2) configure an SSL Context Factory with the location of the server keystore and truststore for obtaining server certificates.
+
+
+____
+[NOTE]
+Virtual hosts are not configured on connectors. You must configure individual contexts with the virtual hosts to which they respond.
+____
+
+____
+[NOTE]
+Prior to Jetty 9, the type of the connector reflected both the protocol supported (HTTP, HTTPS, AJP, SPDY), and the nature of the implementation (NIO or BIO). 
+From Jetty 9 onwards there is only one prime Connector type (`ServerConnector`), which is NIO based and uses Connection Factories to handle one or more protocols.
+____
+
+[[intro-jetty-configuration-contexts]]
+==== Configuring Contexts
+
+A Jetty context is a handler that groups other handlers under a context path together with associated resources and is roughly equivalent to the standard ServletContext API. 
+A context may contain either standard Jetty handlers or a custom application handler.
+
+____
+[NOTE]
+The servlet specification defines a web application. 
+In Jetty a standard web application is a specialized context that uses a standard layout and `WEB-INF/web.xml` to instantiate and configure classpath, resource base and handlers for sessions, security, and servlets, plus servlets for JSPs and static content. 
+Standard web applications often need little or no additional configuration, but you can also use the techniques for arbitrary contexts to refine or modify the configuration of standard web applications.
+____
+
+Configuration values that are common to all contexts are:
+
+contextPath::
+  The contextPath is a URL prefix that identifies which context a HTTP request is destined for. 
+  For example, if a context has a context path `/foo`, it handles requests to `/foo`, `/foo/index.html`,
+  `/foo/bar/`, and `/foo/bar/image.png` but it does not handle requests like `/`, `/other/`, or `/favicon.ico`. 
+  A context with a context path of / is called the root context.
++
+The context path can be set by default from the deployer (which uses the filename as the basis for the context path); or in code; or it can be set by a Jetty IoC XML that is either applied by the deployer or found in the `WEB-INF/jetty-web.xml` file of a standard web app context.
+
+virtualHost::
+  A context may optionally have one or more virtual hosts set. 
+  Unlike the host set on a connector (which selects the network interface on which to listen), a virtual host does not set any network parameters.
+  Instead a virtual host represents an alias assigned by a name service to an IP address, which may have many aliases. 
+  To determine which virtual host a request is intended for, the HTTP client (browser) includes in the request the name used to look up the network address.
+  A context with a virtual host set only handles requests that have a matching virtual host in their request headers.
+classPath::
+  A context may optionally have a classpath, so that any thread that executes a handler within the context has a thread context classloader set with the classpath. 
+  A standard web application has the classpath initialized by the `WEB-INF/lib` and `WEB-INF/classes` directory and
+  has additional rules about delegating classloading to the parent classloader. 
+  All contexts may have additional classpath entries added.
+attributes::
+  Attributes are arbitrary named objects that are associated with a context and are frequently used to pass entities between a web application and its container. 
+  For example the attribute `javax.servlet.context.tempdir` is used to pass the File instance that represents the assigned temporary directory for a web application.
+resourceBase::
+  The resource base is a directory (or collection of directories or URL) that contains the static resources for the context. 
+  These can be images and HTML files ready to serve or JSP source files ready to be compiled. 
+  In traditional web servers this value is often called the docroot.
+
+===== Context Configuration by API
+
+In an embedded server, you configure contexts by directly calling the link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandler.html[ContextHandler] API as in the following example:
+
+[source, java, subs="{sub-order}"]
+----
+include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java[]
+----
+
+===== Context Configuration by IoC XML
+
+You can create and configure a context entirely by IoC XML (either Jetty's or Spring). 
+The deployer discovers and hot deploys context IoC descriptors like the following which creates a context to serve the Javadoc from the Jetty distribution:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0"  encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC
+    "-//Mort Bay Consulting//DTD Configure//EN"
+    "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<!--
+  Configure a custom context for serving javadoc as static resources
+-->
+
+<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
+  <Set name="contextPath">/javadoc</Set>
+  <Set name="resourceBase"><SystemProperty name="jetty.home" default="."/>/javadoc/</Set>
+  <Set name="handler">
+    <New class="org.eclipse.jetty.server.handler.ResourceHandler">
+      <Set name="welcomeFiles">
+        <Array type="String">
+          <Item>index.html</Item>
+        </Array>
+      </Set>
+      <Set name="cacheControl">max-age=3600,public</Set>
+    </New>
+  </Set>
+</Configure>
+----
+
+[[intro-jetty-configuration-webapps]]
+===== Configuring Web Applications
+
+The servlet specification defines a web application, which when packaged as a zip is called WAR file (Web application ARchive). 
+Jetty implements both WAR files and unpacked web applications as a specialized context that is configured by means of:
+
+* A standard layout which sets the location of the resourceBase (the root of the WAR) and initializes the classpath from jars found in `WEB-INF/lib` and classes found in `WEB-INF/classes`.
+* The standard `WEB-INF/web.xml` deployment descriptor which is parsed to define and configure init parameters, filters, servlets, listeners, security constraints, welcome files and resources to be injected.
+* A default `web.xml` format deployment descriptor provided either by Jetty or in configuration configures the JSP servlet and the default servlet for handling static content. 
+The standard `web.xml` may override the default `web.xml`.
+* Annotations discovered on classes in Jars contained in `WEB-INF/lib` can declare additional filters, servlets and listeners.
+* Standard deployment descriptor fragments discovered in Jars contained in `WEB-INF/lib` can declare additional init parameters, filters, servlets, listeners, security constraints, welcome files and resources to be injected.
+* An optional `WEB-INF/jetty-web.xml` file may contain Jetty IoC configuration to configure the Jetty specific APIs of the context and handlers.
+
+Because these configuration mechanisms are contained within the WAR file (or unpacked web application), typically a web application contains much of its own configuration and deploying a WAR is often just a matter of dropping the WAR file in to the webapps directory that is scanned by the link:#quickstart-config-deployer[Jetty deployer].
+
+If you need to configure something within a web application, often you do so by unpacking the WAR file and editing the `web.xml` and other configuration files. 
+However, both the servlet standard and some Jetty features allow for other configuration to be applied to a web application externally from the WAR:
+
+* Configured data sources and security realms in the server can be injected into a web application either explicitly or by name matching.
+* Jetty allows one or more override deployment descriptors, in `web.xml` format, to be set on a context (via code or IoC XML) to amend the configuration set by the default and standard `web.xml`.
+* The normal Jetty Java API may be called by code or IoC XML to amend the configuration of a web application.
+
+===== Setting the Context Path
+
+The web application standard provides no configuration mechanism for a web application or WAR file to set its own `contextPath`. 
+By default the deployer uses conventions to set the context path: 
+If you deploy a WAR file called `foobar.WAR`, the context path is `/foobar`; if you deploy a WAR file called `ROOT.WAR` the context path is `/`. 
+
+However, it is often desirable to explicitly set the context path so that information (for example, version numbers) may be included in the filename of the WAR.
+Jetty allows the context Path of a WAR file to be set internally (by the WAR itself) or externally (by the deployer of the WAR).
+
+To set the contextPath from within the WAR file, you can include a `WEB-INF/jetty-web.xml` file which contains IoC XML to set the context path:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0"  encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC
+    "-//Mort Bay Consulting//DTD Configure//EN"
+    "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+    <Set name="contextPath">/contextpath</Set>
+</Configure>
+----
+
+Alternately, you can configure the classpath externally without the need to modify the WAR file itself.
+Instead of allowing the WAR file to be discovered by the deployer, an IoC XML file may be deployed that both sets the context path and declares the WAR file that it applies to:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0"  encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC
+    "-//Mort Bay Consulting//DTD Configure//EN"
+    "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/test.war</Set>
+  <Set name="contextPath">/test</Set>
+</Configure>
+----
+
+An example of setting the context path is included with the Jetty distribution in `$JETTY_HOME/webapps/test.xml`.
+
+[[quickstart-config-deployer]]
+===== Web Application Deployment
+
+Jetty is capable of deploying a variety of Web Application formats. 
+This is accomplished via scans of the `${jetty.base}/webapps` directory for contexts to deploy.
+
+A Context can be any of the following:
+
+* A standard WAR file. (must in "`.war`").
+* A directory containing an expanded WAR file. (must contain `{dir}/WEB-INF/web.xml` file).
+* A directory containing static content.
+* A XML descriptor in xref:jetty-xml-syntax[] that configures a link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandler.html[ContextHandler] instance (Such as a
+link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html[WebAppContext]).
+
+The new WebAppProvider will attempt to avoid double deployments during the directory scan with the following heuristics:
+
+* Hidden files (starting with `"."`) are ignored
+* Directories with names ending in `".d"` are ignored
+* If a directory and matching WAR file exist with the same base name (eg: `foo/` and `foo.war`), then the directory is assumed to be the unpacked WAR and only the WAR is deployed (which may reuse the unpacked directory)
+* If a directory and matching XML file exists (eg: `foo/` and `foo.xml`), then the directory is assumed to be an unpacked WAR and only the XML is deployed (which may use the directory in its own configuration)
+* If a WAR file and matching XML file exist (eg: `foo.war` and `foo.xml`), then the WAR is assumed to be configured by the XML and only the XML is deployed.
+
+____
+[NOTE]
+In prior versions of Jetty there was a separate ContextDeployer that provided XML-based deployment. As of Jetty 9 the ContextDeployer no longer exists and its functionality has been merged with the new link:{JDURL}/org/eclipse/jetty/deploy/providers/WebAppProvider.html[WebAppProvider] to avoid double deployment scenarios.
+____
+
+//A Context is an instance of ContextHandler that aggregates other handlers with common resources for handling HTTP requests (such as resource base, class loader, configuration attributes). 
+//A standard web application is a specialized instance of a context (called a WebAppContext) that uses standard layouts and `web.xml` deployment descriptors to configure the context.
+
+===== Setting an Authentication Realm
+
+The authentication method and realm name for a standard web application may be set in the `web.xml` deployment descriptor with elements like:
+
+[source, xml, subs="{sub-order}"]
+----
+...
+<login-config>
+  <auth-method>BASIC</auth-method>
+  <realm-name>Test Realm</realm-name>
+</login-config>
+...
+----
+
+This example declares that the BASIC authentication mechanism will be used with credentials validated against a realm called "Test Realm."
+However the standard does not describe how the realm itself is implemented or configured. 
+In Jetty, there are several realm implementations (called LoginServices) and the simplest of these is the HashLoginService, which can read usernames and credentials from a Java properties file.
+
+To configure an instance of HashLoginService that matches the "Test Realm" configured above, the following `$JETTY_BASE/etc/test-realm.xml` IoC XML file should be passed on the command line or set in `start.ini` or `start.d/server.ini`.
+
+[source, xml, subs="{sub-order}"]
+----
+include::{SRCDIR}/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/test-realm.xml[]
+----
+
+This creates and configures the LoginService as an aggregate bean on the server. 
+When a web application is deployed that declares a realm called "Test Realm," the server beans are searched for a matching Login Service.
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/getting-started/chapter.adoc b/jetty-documentation/src/main/asciidoc/quick-start/getting-started/chapter.adoc
new file mode 100644
index 0000000..985f41b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/getting-started/chapter.adoc
@@ -0,0 +1,25 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[quick-start-getting-started]]
+== Using Jetty
+
+You can use Jetty in many different ways ranging from embedding Jetty in applications, launching it from different build systems, from different JVM-based languages, or as a standalone distribution.
+This guide covers the latter, a standalone distribution suitable for deploying web applications.
+
+include::jetty-installing.adoc[]
+include::jetty-running.adoc[]
+include::jetty-deploying.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-deploying.adoc b/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-deploying.adoc
new file mode 100644
index 0000000..0888725
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-deploying.adoc
@@ -0,0 +1,70 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[quickstart-deploying-webapps]]
+=== Deploying Web Applications
+
+Jetty server instances that configure the deploy module will have a web application deployer that link:#hot-deployment[hot deploys] files found in the `webapps` directory. 
+Standard WAR files and Jetty configuration files that are placed in the `webapps` directory are hot deployed to the server with the following conventions:
+
+* A directory called `example/` is deployed as a standard web application if it contains a `WEB-INF/` subdirectory, otherwise it is deployed as context of static content. 
+The context path is `/example` (that is, `http://localhost:8080/example/`) unless the base name is ROOT (case insensitive), in which case the context path is /. 
+If the directory name ends with ".d" it is ignored (but may be used by explicit configuration).
+* A file called `example.war` is deployed as a standard web application with the context path `/example` (that is,
+`http://localhost:8080/example/`). 
+If the base name is `ROOT` (case insensitive), the context path is `/`. 
+If `example.war` and `example/` exist, only the WAR is deployed (which may use the directory as an unpack location).
+* An XML file like `example.xml` is deployed as a context whose configuration is defined by the XML. 
+The configuration itself must set the context path. 
+If `example.xml` and `example.war` exists, only the XML is deployed (which may use the WAR in its configuration).
+
+If you have a standard web application, you can hot deploy it into Jetty by copying it into the `webapps` directory.
+
+==== Jetty Demonstration Web Applications
+
+The demo-base/webapps directory contains the following deployable and auxiliary files:
+
+`ROOT/`::
+  A directory of static content that is deployed to the root context / due to it's name. 
+  Contains the Jetty demo welcome page.
+`test.d`::
+  A directory containing additional configuration files used by `test.xml` to inject extra configuration into `test.war`.  
+`test.xml`::
+  A context configuration file that configures and deploys `test.war.`
+  The additional configuration includes the context path as well as setting additional descriptors found in the `test.d` directory.
+`test.war`::
+  The demonstration web application that is configured and deployed by `test.xml`.
+`async-rest.war`::
+  A web application demonstration of asynchronous REST to eBay, automatically deployed to /async-rest based on the file name.
+`test-jaas.war`::
+  A demonstration web application utilizing link:#jaas-support[JAAS] for authentication.
+`test-jaas.xml`::
+  A context configuration file that configures `test-jaas.war`.
+  Additional configuration includes setting up the link:#configuring-login-service[LoginService] for authentication and authorization.
+`test-jndi.war`::
+  A demonstration web application showing the use of link:#jndi[JNDI].
+`test-jndi.xml`::
+  A context configuration file that configures `test-jndi.war`. 
+  Additional configuration includes defining objects in the naming space that can be referenced from the webapp.
+`test-spec.war`::
+  A demonstration web application that shows the use of annotations, fragments, `ServletContainerInitializers` and other Servlet Specification 3.0/3.1 features.
+`test-spec.xml`::
+  A context configuration file that configures `test-spec.war`. 
+  Additional configuration includes setting up some objects in the naming space that can be referenced by annotations.
+`xref-proxy.war`::
+  A demonstration web application that uses a transparent proxy to serve the Jetty source link:{JXURL}/[xref] from the http://www.eclipse.org/jetty[Eclipse Jetty website].
+`example-moved.xml`::
+  A demonstration context configuration file that shows how to use the link:#moved-context-handler[`MovedContextHandler`] to redirect from one path to another.
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-installing.adoc b/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-installing.adoc
new file mode 100644
index 0000000..bfe2e7d
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-installing.adoc
@@ -0,0 +1,57 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-downloading]]
+=== Downloading Jetty
+
+==== Downloading the Jetty Distribution
+
+The standalone Jetty distribution is available for download from the Eclipse Foundation:
+____
+*Jetty*
+http://download.eclipse.org/jetty
+____
+
+It is available in both zip and gzip formats; download the one most appropriate for your system. 
+Notice that there are a number of other files with extensions of .sha or .md5 which are checksum files. 
+When you download and unpack the binary, it is extracted into a directory called `jetty-distribution-VERSION.` 
+Put this directory in a convenient location. 
+The rest of the instructions in this documentation refer to this location as either `JETTY_HOME` or as `$(jetty.home).`
+
+[[distribution-content]]
+==== Distribution Content
+
+A quick rundown of the distribution's contents follows. The top-level directory contains:
+
+.Contents
+[width="80%",cols="40%,60%",options="header"]
+|=======================================================================
+|Location |Description |license-eplv10-aslv20.html |License file for Jetty
+|README.txt |Useful getting started information
+|VERSION.txt |Release information
+|bin/ |Utility shell scripts to help run Jetty on Unix systems
+|demo-base/ |A Jetty base directory to run a Jetty server with demonstration webapps
+|etc/ |Directory for Jetty XML configuration files
+|lib/ |All the JAR files necessary to run Jetty
+|logs/ |Directory for request logs
+|modules/ |Directory of module definitions
+|notice.html |License information and exceptions
+|resources/ |Directory containing additional resources for classpath, activated via configuration
+|start.d/ |Directory of *.ini files containing arguments that are added to the effective command line (see start.ini)
+|start.ini |File containing the arguments that are added to the effective command line (modules, properties and XML configuration files)
+|start.jar |Jar that invokes Jetty (see also xref:quickstart-running-jetty[])
+|webapps/ |Directory containing webapps that run under the default configuration of Jetty
+|=======================================================================
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-running.adoc b/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-running.adoc
new file mode 100644
index 0000000..87d4e09
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-running.adoc
@@ -0,0 +1,235 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[quickstart-running-jetty]]
+=== Running Jetty
+
+To start Jetty on the default port of 8080, run the following command:
+
+[source, screen, subs="{sub-order}"]
+----
+> cd $JETTY_HOME
+> java -jar start.jar
+
+2015-06-04 10:50:44.806:INFO::main: Logging initialized @334ms
+2015-06-04 10:50:44.858:WARN:oejs.HomeBaseWarning:main: This instance of Jetty is not running from a separate {jetty.base} directory, this is not recommended.  See documentation at http://www.eclipse.org/jetty/documentation/current/startup.html
+2015-06-04 10:50:44.995:INFO:oejs.Server:main: jetty-9.3.0.v20150601
+2015-06-04 10:50:45.012:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///opt/jetty-distribution-9.3.0.v20150601/webapps/] at interval 1
+2015-06-04 10:50:45.030:INFO:oejs.ServerConnector:main: Started ServerConnector@19dfb72a{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
+2015-06-04 10:50:45.030:INFO:oejs.Server:main: Started @558ms
+----
+
+You can point a browser at this server at link:http://localhost:8080[]. 
+However, as there are no webapps deployed in the $JETTY_HOME directory, you will see a 404 error page served by Jetty. 
+*Note* the HomeBase warning - it is _not_ recommended to run Jetty from the $JETTY_HOME directory. 
+Instead, see how to link:#creating-jetty-base[create a Jetty Base] below.
+
+[[demo-webapps-base]]
+==== Demo Base
+
+Within the standard jetty distribution there is the `demo-base` directory, which demonstrates the recommended way to run Jetty in a directory separately from $JETTY_HOME:
+
+[source, screen, subs="{sub-order}"]
+----
+> cd $JETTY_HOME/demo-base/
+> java -jar $JETTY_HOME/start.jar
+
+2015-06-04 10:55:24.161:INFO::main: Logging initialized @308ms
+2015-06-04 10:55:24.431:WARN::main: demo test-realm is deployed. DO NOT USE IN PRODUCTION!
+2015-06-04 10:55:24.434:INFO:oejs.Server:main: jetty-9.3.0.v20150601
+2015-06-04 10:55:24.457:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///opt/jetty-distribution-9.3.0.v20150601/demo-base/webapps/] at interval 1
+2015-06-04 10:55:24.826:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@c038203{/,file:///opt/jetty-distribution-9.3.0.v20150601/demo-base/webapps/ROOT/,AVAILABLE}{/ROOT}
+2015-06-04 10:55:24.929:WARN::main: test-jaas webapp is deployed. DO NOT USE IN PRODUCTION!
+2015-06-04 10:55:24.978:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@46238e3f{/test-jaas,file:///tmp/jetty-0.0.0.0-8080-test-jaas.war-_test-jaas-any-9105214562680121772.dir/webapp/,AVAILABLE}{/test-jaas.war}
+2015-06-04 10:55:25.162:WARN::main: async-rest webapp is deployed. DO NOT USE IN PRODUCTION!
+2015-06-04 10:55:25.208:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@6b67034{/async-rest,[file:///tmp/jetty-0.0.0.0-8080-async-rest.war-_async-rest-any-1023939491558622183.dir/webapp/, jar:file:///tmp/jetty-0.0.0.0-8080-async-rest.war-_async-rest-any-1023939491558622183.dir/webapp/WEB-INF/lib/example-async-rest-jar-9.3.0.v20150601.jar!/META-INF/resources],AVAILABLE}{/async-rest.war}
+2015-06-04 10:55:25.311:WARN::main: test-jndi webapp is deployed. DO NOT USE IN PRODUCTION!
+2015-06-04 10:55:25.386:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@8b96fde{/test-jndi,file:///tmp/jetty-0.0.0.0-8080-test-jndi.war-_test-jndi-any-1692053319754270133.dir/webapp/,AVAILABLE}{/test-jndi.war}
+2015-06-04 10:55:25.508:WARN::main: test-spec webapp is deployed. DO NOT USE IN PRODUCTION!
+2015-06-04 10:55:25.594:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@69930714{/test-spec,[file:///tmp/jetty-0.0.0.0-8080-test-spec.war-_test-spec-any-5518740932795802823.dir/webapp/, jar:file:///tmp/jetty-0.0.0.0-8080-test-spec.war-_test-spec-any-5518740932795802823.dir/webapp/WEB-INF/lib/test-web-fragment-9.3.0.v20150601.jar!/META-INF/resources],AVAILABLE}{/test-spec.war}
+2015-06-04 10:55:25.781:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@3eb7fc54{/proxy,file:///tmp/jetty-0.0.0.0-8080-xref-proxy.war-_xref-proxy-any-3068657547009829038.dir/webapp/,AVAILABLE}{/xref-proxy.war}
+2015-06-04 10:55:25.786:INFO:oejsh.ContextHandler:main: Started o.e.j.s.h.MovedContextHandler@59662a0b{/oldContextPath,null,AVAILABLE}
+2015-06-04 10:55:25.951:WARN::main: test webapp is deployed. DO NOT USE IN PRODUCTION!
+2015-06-04 10:55:26.248:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@4f83df68{/test,file:///tmp/jetty-0.0.0.0-8080-test.war-_test-any-5238659347611323540.dir/webapp/,AVAILABLE}{/test.war}
+2015-06-04 10:55:26.255:INFO:oejs.ServerConnector:main: Started ServerConnector@5a9c4ad9{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
+2015-06-04 10:55:26.259:INFO:oejus.SslContextFactory:main: x509={jetty.eclipse.org=jetty} wild={} alias=null for SslContextFactory@23941fb4(file:///opt/jetty-distribution-9.3.0.v20150601/demo-base/etc/keystore,file:///opt/jetty-distribution-9.3.0.v20150601/demo-base/etc/keystore)
+2015-06-04 10:55:26.269:INFO:oejs.ServerConnector:main: Started ServerConnector@5d908d47{SSL,[ssl, http/1.1]}{0.0.0.0:8443}
+2015-06-04 10:55:26.270:INFO:oejs.Server:main: Started @2417ms
+----
+
+You can visit this demo server by pointing a browser at link:http://localhost:8080[], which will now show a welcome page and several demo/test web applications.
+
+____
+[WARNING]
+The demonstration web applications are not necessarily secure and should not be deployed in production web servers.
+____
+
+You can see the configuration of the demo-base by using the following commands:
+
+[source, screen, subs="{sub-order}"]
+----
+> cd $JETTY_HOME/demo-base/
+> java -jar $JETTY_HOME/start.jar --list-modules
+...
+
+> java -jar %JETTY_HOME/start.jar --list-config
+...
+----
+
+ The `--list-modules` command will return a complete list of available and enabled modules for the server. 
+ It will also display the location of the modules, how and in what order they are implemented, dependent modules, and associated jar files. 
+ The `--list-config` command displays a trove of  information about the server including the Java and Jetty environments, the configuration order, any JVM arguments or System Properties set, general server properties, a full listing of the Jetty server class path, and active Jetty XML files.
+
+[[creating-jetty-base]]
+==== Creating a new Jetty Base
+
+The `demo-base` directory described above is an example of the link:#startup-base-and-home[jetty.base] mechanism added in Jetty 9.1. 
+A Jetty base directory allows the configuration and web applications of a server instance to be stored separately from the Jetty distribution, so that upgrades can be done with minimal disruption. 
+Jetty's default configuration is based on two properties:
+
+jetty.home::
+  The property that defines the location of the jetty distribution, its libs, default modules and default XML files (typically start.jar, lib, etc)
+jetty.base::
+  The property that defines the location of a specific instance of a jetty server, its configuration, logs and web applications (typically start.ini, start.d, logs and webapps)
+
+The `jetty.home` and `jetty.base` properties may be explicitly set on the command line, or they can be inferred from the environment if used with commands like:
+
+[source, screen, subs="{sub-order}"]
+----
+> cd $JETTY_BASE
+> java -jar $JETTY_HOME/start.jar
+----
+
+The following commands create a new base directory, enables both the HTTP connector and the web application deployer modules, and copies a demo webapp to be deployed:
+
+[source, screen, subs="{sub-order}"]
+----
+> JETTY_BASE=/tmp/mybase
+> mkdir $JETTY_BASE
+> cd $JETTY_BASE
+> java -jar $JETTY_HOME/start.jar
+
+WARNING: Nothing to start, exiting ...
+
+Usage: java -jar start.jar [options] [properties] [configs]
+       java -jar start.jar --help  # for more information
+
+> java -jar $JETTY_HOME/start.jar --add-to-startd=http,deploy
+
+INFO: server          initialised (transitively) in ${jetty.base}/start.d/server.ini
+INFO: http            initialised in ${jetty.base}/start.d/http.ini
+INFO: security        initialised (transitively) in ${jetty.base}/start.d/security.ini
+INFO: servlet         initialised (transitively) in ${jetty.base}/start.d/servlet.ini
+INFO: webapp          initialised (transitively) in ${jetty.base}/start.d/webapp.ini
+INFO: deploy          initialised in ${jetty.base}/start.d/deploy.ini
+MKDIR: ${jetty.base}/webapps
+INFO: Base directory was modified
+
+> cp $JETTY_HOME/demo-base/webapps/async-rest.war webapps/ROOT.war
+> java -jar $JETTY_HOME/start.jar
+
+2015-06-04 11:10:16.286:INFO::main: Logging initialized @274ms
+2015-06-04 11:10:16.440:INFO:oejs.Server:main: jetty-9.3.0.v20150601
+2015-06-04 11:10:16.460:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///tmp/mybase/webapps/] at interval 1
+2015-06-04 11:10:16.581:WARN::main: async-rest webapp is deployed. DO NOT USE IN PRODUCTION!
+2015-06-04 11:10:16.589:INFO:oejw.StandardDescriptorProcessor:main: NO JSP Support for /, did not find org.eclipse.jetty.jsp.JettyJspServlet
+2015-06-04 11:10:16.628:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@1a407d53{/,[file:///tmp/jetty-0.0.0.0-8080-ROOT.war-_-any-4510228025526425427.dir/webapp/, jar:file:///tmp/jetty-0.0.0.0-8080-ROOT.war-_-any-4510228025526425427.dir/webapp/WEB-INF/lib/example-async-rest-jar-9.3.0.v20150601.jar!/META-INF/resources],AVAILABLE}{/ROOT.war}
+2015-06-04 11:10:16.645:INFO:oejs.ServerConnector:main: Started ServerConnector@3abbfa04{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
+2015-06-04 11:10:16.646:INFO:oejs.Server:main: Started @634ms
+----
+
+[[quickstart-changing-jetty-port]]
+==== Changing the Jetty Port
+
+You can configure Jetty to run on a different port by setting the `jetty.http.port` Property on the command line:
+
+[source, screen, subs="{sub-order}"]
+----
+> cd $JETTY_BASE
+> java -jar $JETTY_HOME/start.jar jetty.http.port=8081
+...
+----
+
+Alternatively, property values can be added to the effective command line built from either the `start.ini` file or `start.d/http.ini` files. 
+By default, the Jetty distribution defines the `jetty.http.port` property in the `start.d/http.ini` file, which may be edited to set another value.
+
+____
+[NOTE]
+--
+The configuration by properties works via the following chain:
+
+* The start.d/http.ini file is part of the effective command line and contains the --module=http argument which activates the http module.
+* The modules/http.mod file defines the http module which specifies the etc/jetty-http.xml configuration file and the template ini properties it uses.
+* The jetty.http.port property is used by the Property XML element in etc/jetty.http.xml to inject the ServerConnector instance with the port.
+
+For more information see the link:#quick-start-configure[Quickstart Configuration Guide] and link:#configuring-connectors[Configuring Connectors].
+--
+____
+
+[[quickstart-starting-https]]
+==== Adding SSL for HTTPS & HTTP2
+
+To add HTTPS and HTTP2 connectors to a Jetty configuration, the modules can be activated by the following command:
+
+[source, screen, subs="{sub-order}"]
+----
+> java -jar $JETTY_HOME/start.jar --add-to-startd=https,http2
+[...]
+
+> java -jar $JETTY_HOME/start.jar
+[...]
+
+2015-06-04 13:52:01.933:INFO:oejs.ServerConnector:main: Started ServerConnector@6f1fba17{SSL,[ssl, alpn, h2, http/1.1]}{0.0.0.0:8443}
+[...]
+----
+
+The `--add-to-startd` command sets up the effective command line in the ini files to run an ssl connection that supports the HTTPS and HTTP2 protocols as follows:
+
+* creates `start.d/ssl.ini` that configures an SSL connector (eg port, keystore etc.) by adding `etc/jetty-ssl.xml` and `etc/jetty-ssl-context.xml` to the effective command line.
+* creates `start.d/alpn.ini` that configures protocol negotiation on the SSL connector by adding `etc/jetty-alpn.xml` to the effective command line.
+* creates `start.d/https.ini` that configures the HTTPS protocol on the SSL connector by adding `etc/jetty-https.xml` to the effective command line.
+* creates `start.d/http2.ini` that configures the HTTP/2 protocol on the SSL connector by adding `etc/jetty-http2.xml` to the effective command line.
+* checks for the existence of a `etc/keystore` file and if not present, downloads a demonstration keystore file.
+
+____
+[NOTE]
+If a single `start.ini` file is preferred over individual `start.d/*.ini` files, then the option --add-to-start=module may be used to append the module activation to the start.ini file rather than create a file in start.d
+____
+
+[[quickstart-changing-https-port]]
+===== Changing the Jetty HTTPS Port
+
+You can configure the SSL connector to run on a different port by setting the `jetty.ssl.port` property on the command line:
+
+[source, screen, subs="{sub-order}"]
+----
+> cd $JETTY_BASE
+> java -jar $JETTY_HOME/start.jar jetty.ssl.port=8444
+----
+
+Alternatively, property values can be added to the effective command line built from the `start.ini` file and `start.d/*.ini` files. 
+If you used the `--add-to-startd` command to enable HTTPS , then you can edit this property in the `start.d/https.ini` file. 
+If you used `--add-to-start` command, then you can edit this property in the `start.ini` file.
+
+==== More start.jar options
+
+The job of the `start.jar` is to interpret the command line, `start.ini` and `start.d` directory (and associated .ini files) to build a Java classpath and list of properties and configuration files to pass to the main class of the Jetty XML configuration mechanism. 
+The `start.jar` mechanism has many options which are documented in the xref:startup[] administration section and you can see them in summary by using the command:
+
+[source, screen, subs="{sub-order}"]
+----
+> java -jar $JETTY_HOME/start.jar --help
+----
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/introduction/chapter.adoc b/jetty-documentation/src/main/asciidoc/quick-start/introduction/chapter.adoc
new file mode 100644
index 0000000..20fc03b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/introduction/chapter.adoc
@@ -0,0 +1,23 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[introduction]]
+== Introducing Jetty
+
+include::what-is-jetty.adoc[]
+include::what-version.adoc[]
+include::jetty-javaee.adoc[]
+include::jetty-coordinates.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/introduction/jetty-coordinates.adoc b/jetty-documentation/src/main/asciidoc/quick-start/introduction/jetty-coordinates.adoc
new file mode 100644
index 0000000..5570f18
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/introduction/jetty-coordinates.adoc
@@ -0,0 +1,53 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[quickstart-jetty-coordinates]]
+=== Finding Jetty in Maven
+
+==== Maven Coordinates
+
+Jetty has existed in Maven Central almost since its inception, though the coordinates have changed over the years.
+When Jetty was based at SourceForge and then The Codehaus it was located under the `groupId` of `org.mortbay.jetty`.
+With Jetty 7 the project moved to the Eclipse foundation and to a new `groupId` at that time to reflect its new home.
+
+The top level Project Object Model (POM) for the Jetty project is located under the following coordinates.
+
+[source, xml, subs="{sub-order}"]
+----
+<dependency>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-project</artifactId>
+  <version>${project.version}</version>
+</dependency>
+----
+
+==== Changelogs in Central
+
+The changes between versions of Jetty are tracked in a file called VERSIONS.txt, which is under source control and is generated on release.
+Those generated files are also uploaded into Maven Central during the release of the top level POM. You can find them as a classifier marked artifact.
+
+http://central.maven.org/maven2/org/eclipse/jetty/jetty-project/
+
+[source, xml, subs="{sub-order}"]
+----
+<dependency>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-project</artifactId>
+  <version>${project.version}</version>
+  <classifier>version</classifier>
+  <type>txt</type>
+</dependency>
+----
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/introduction/jetty-javaee.adoc b/jetty-documentation/src/main/asciidoc/quick-start/introduction/jetty-javaee.adoc
new file mode 100644
index 0000000..6e2866d
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/introduction/jetty-javaee.adoc
@@ -0,0 +1,107 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-javaee]]
+=== Jetty and Java EE Web Profile
+
+Jetty implements aspects of the Java EE specification, primarily the Servlet Specification. 
+Recent releases of the Java EE platform have introduced a Web Profile, recognizing that many developers need only a subset of the many technologies under the Java EE umbrella.
+
+While Jetty itself does not ship all of the Web Profile technologies, Jetty architecture is such that you can plug in third party implementations to produce a container customized to your exact needs.
+
+[[jetty-javaee-7]]
+==== Java EE 7 Web Profile
+
+In the forthcoming Java EE-7 specification, the Web Profile reflects updates in its component specifications and adds some new ones:
+
+.JavaEE7 Web Profile
+[cols=",,,",options="header",]
+|=======================================================================
+|JSR |Name |Included with jetty-9.1.x |Pluggable
+
+|http://jcp.org/en/jsr/detail?id=340[JSR 340] |Servlet Specification API 3.1 |Yes |
+
+|http://jcp.org/en/jsr/detail?id=344[JSR 344] |Java Server Faces 2.2 (JSF) |No |Yes, https://javaserverfaces.java.net/[Mojarra] or http://myfaces.apache.org/[MyFaces]
+
+|http://jcp.org/en/jsr/detail?id=245[JSR 245] / http://jcp.org/en/jsr/detail?id=341[JSR 341] |Java Server Pages 2.3/Java Expression Language 3.0 (JSP/EL) |Yes |Yes
+
+|http://jcp.org/en/jsr/detail?id=52[JSR 52] |Java Standard Tag Library 1.2 (JSTL) |Yes |Yes
+
+|http://jcp.org/en/jsr/detail?id=45[JSR 45] |Debugging Support for Other Languages 1.0 |Yes (via JSP) |Yes (via JSP)
+
+|http://jcp.org/en/jsr/detail?id=346[JSR 346] |Contexts and Dependency Injection for the JavaEE Platform 1.1 (Web Beans) |No |Yes, http://seamframework.org/Weld[Weld]
+
+|http://jcp.org/en/jsr/detail?id=330[JSR 330] |Dependency Injection for Java 1.0 |No |Yes as part of a CDI implementation, http://seamframework.org/Weld[Weld]
+
+|http://jcp.org/en/jsr/detail?id=316[JSR 316] |Managed Beans 1.0 |No |Yes, as part of another technology
+
+|http://jcp.org/en/jsr/detail?id=345[JSR 345] |Enterprise JavaBeans 3.2 Lite |No |
+
+|http://jcp.org/en/jsr/detail?id=338[JSR 338] |Java Persistance 2.1 (JPA) |No |Yes, eg http://www.hibernate.org/[Hibernate]
+
+|http://jcp.org/en/jsr/detail?id=250[JSR 250] |Common Annotations for the Java Platform 1.2 |Yes |Partially (for non-core Servlet Spec annotations)
+
+|http://jcp.org/en/jsr/detail?id=907[JSR 907] |Java Transaction API 1.2 (JTA) |Yes |Yes
+
+|http://jcp.org/en/jsr/detail?id=349[JSR 349] |Bean Validation 1.1 |No |Yes as part of another technology eg JSF, or a stand-alone implementation such as http://www.hibernate.org/subprojects/validator/docs.html[Hiberate
+Validator]
+
+|http://jcp.org/en/jsr/detail?id=339[JSR 339] |Java API for RESTful Web Services 2.0 (JAX-RS) |No |
+
+|http://jcp.org/en/jsr/detail?id=356[JSR 356] |Java API for Websocket 1.0 |Yes |No
+
+|http://jcp.org/en/jsr/detail?id=353[JSR 353] |Java API for JSON Processing 1.0 (JSON-P) |No |Yes, eg JSON-P https://java.net/projects/jsonp/[reference implementation]
+
+|link:jcp.org/en/jsr/detail?id=318[JSR 318] |Interceptors 1.2 |No |Yes as part of a CDI implementation
+|=======================================================================
+
+[[jetty-javaee-6]]
+==== Jetty EE 6 Web Profile
+
+Here is the matrix of JSRs for Java EE 6 Web Profile, and how they relate to Jetty:
+
+.Java EE 6 Web Profile
+[cols=",,,",options="header",]
+|=======================================================================
+|JSR |Name |Included with jetty-9.0.x |Pluggable
+
+|http://jcp.org/en/jsr/detail?id=315[JSR 315] |Servlet Specification API 3.0 |Yes |
+
+|http://jcp.org/en/jsr/detail?id=314[JSR 314] |JavaServer Faces 2.0 (JSF) |No |Yes, for example, https://javaserverfaces.java.net/[Mojarra] or http://myfaces.apache.org/[MyFaces]
+
+|http://jcp.org/en/jsr/detail?id=245[JSR 245] |JavaServer Pages 2.2/Java Expression Language 2.2 (JSP/EL) |Yes |Yes
+
+|http://jcp.org/en/jsr/detail?id=52[JSR 52] |Java Standard Tag Library 1.2 (JSTL) |Yes |Yes
+
+|http://jcp.org/en/jsr/detail?id=45[JSR 45] |Debugging Support for Other Languages 1.0 |Yes (via JSP) |Yes (via JSP)
+
+|http://jcp.org/en/jsr/detail?id=299[JSR 299] |Contexts and Dependency Injection for the Java EE Platform 1.0 (Web Beans) |No |Yes, http://seamframework.org/Weld[Weld] or http://openwebbeans.apache.org/[OpenWebBeans]
+
+|http://jcp.org/en/jsr/detail?id=330[JSR 330] |Dependency Injection for Java 1.0 |No |Yes as part of a CDI implementation, http://seamframework.org/Weld[Weld]
+
+|http://jcp.org/en/jsr/detail?id=316[JSR 316] |Managed Beans 1.0 |No |Yes, as part of another technology.
+
+|http://jcp.org/en/jsr/detail?id=318[JSR 318] |Enterprise JavaBeans 3.1 |No |Yes, OpenEJB
+
+|http://jcp.org/en/jsr/detail?id=317[JSR 317] |Java Persistance 2.0 (JPA) |No |Yes, http://www.hibernate.org/[Hibernate]
+
+|http://jcp.org/en/jsr/detail?id=250[JSR 250] |Common Annotations for the Java Platform |Yes |Partially (for non-core Servlet Spec annotations)
+
+|http://jcp.org/en/jsr/detail?id=907[JSR 907] |Java Transaction API (JTA) |Yes |Implementations are pluggable, such as http://www.atomikos.com/[Atomikos], http://jotm.ow2.org/xwiki/bin/view/Main/WebHome[JOTM], http://jencks.codehaus.org/Transaction+Manager[Jencks (Geronimo Transaction Manager)]
+
+|http://jcp.org/en/jsr/detail?id=303[JSR 303] |Bean Validation 1.0 |No |Yes as part of another technology (JSF), or a stand-alone implementation such as http://www.hibernate.org/subprojects/validator/docs.html[Hiberate
+Validator]
+|=======================================================================
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-is-jetty.adoc b/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-is-jetty.adoc
new file mode 100644
index 0000000..241665f
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-is-jetty.adoc
@@ -0,0 +1,45 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[what-is-jetty]]
+=== What is Jetty?
+
+Jetty is an open-source project providing an HTTP server, HTTP client, and javax.servlet container.
+
+This guide is broken up in to five parts:
+
+* The link:#quick-start[first section] emphasizes beginning to use Jetty.
+It provides information about what Jetty is and where you can download it, and where to find Jetty in repositories like Central Maven.
+It also provides a Quick Start guide on how to get Jetty up and running as well as an overview of how and what to configure in Jetty.
+
+* The link:#jetty-config-guide[second section] of the guide deals with configuring Jetty at a more granular level.
+It explains how to use Jetty to deploy web applications, configure contexts and connects, and how to implement SSL and other security measures.
+
+* Administration of Jetty is the focus of the link:#jetty-admin-guide[third section] of the guide.
+From server startup to session management, logging, HTTP/2 support and Jetty optimization, these chapters will help administrators get the most out of their Jetty server instances.
+This section also covers configuring many of the most common servlet container features such as JNDI and JMX.
+
+* Aimed at advanced users of Jetty, the link:#jetty-dev-guide[fourth section] of the guide focuses on Jetty development.
+A large portion of this section is focused on using Jetty as an embedded server in existing applications.
+It contains several examples and how-to guides for making the most out of the Jetty framework.
+This section also includes a guide on using the Jetty Maven plugin as well as information on debugging Jetty.
+
+* The link:#jetty-ref-guide[final section] of the guide is a reference section.
+Included there are guides on Jetty architecture and Jetty XML syntax, alternate distributions of Jetty and even troubleshooting of common issues.
+There is also a chapter on getting involved in the Jetty community including information on how to contribute code and how to find help.
+
+Feedback is always welcome!
+Additionally, if you are interested in how to contribute to the open source project there is a link:#community[section on that as well!]
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc b/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc
new file mode 100644
index 0000000..9310066
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc
@@ -0,0 +1,39 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[what-jetty-version]]
+=== What Version Do I Use?
+
+Jetty 9 is the most recent version of Jetty and has a great many improvements over previous versions.
+This documentation which focuses on Jetty 9.
+While many people continue to use older versions of Jetty, we generally recommend using Jetty 9 as it represents the version of Jetty that we will actively maintain and improve over the next few years.
+
+.Jetty Versions
+[width="100%",cols="12%,9%,15%,6%,21%,10%,6%,21%",options="header",]
+|=======================================================================
+|Version |Year |Home |JVM |Protocols |Servlet |JSP |Status
+|9.4 |2016 |Eclipse |1.8 |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |3.1 |2.3 |Stable
+|9.3 |2015 |Eclipse |1.8 |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |3.1 |2.3 |Stable
+|9.2 |2014 |Eclipse |1.7 |HTTP/1.1 RFC2616, javax.websocket, SPDY v3 |3.1 |2.3 |Stable
+|8 |2009-2014 |Eclipse/Codehaus |1.6 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |3.0 |2.2 |Deprecated
+|7 |2008-2014 |Eclipse/Codehaus |1.5 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |2.5 |2.1 |Deprecated
+|6 |2006-2010 |Codehaus |1.4-1.5 |HTTP/1.1 RFC2616 |2.5 |2.0 |Deprecated
+|5 |2003-2009 |Sourceforge |1.2-1.5 |HTTP/1.1 RFC2616 |2.4 |2.0 |Deprecated
+|4 |2001-2006 |Sourceforge |1.2, J2ME |HTTP/1.1 RFC2616 |2.3 |1.2 |Ancient
+|3 |1999-2002 |Sourceforge |1.2 |HTTP/1.1 RFC2068 |2.2 |1.1 |Fossilized
+|2 |1998-2000 |Mortbay |1.1 |HTTP/1.0 RFC1945 |2.1 |1.0 |Legendary
+|1 |1995-1998 |Mortbay |1.0 |HTTP/1.0 RFC1945 |- |- |Mythical
+|=======================================================================
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/part.adoc b/jetty-documentation/src/main/asciidoc/quick-start/part.adoc
new file mode 100644
index 0000000..2c87e4a
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/quick-start/part.adoc
@@ -0,0 +1,22 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+[[quick-start]]
+
+= Getting Started With Jetty
+
+include::introduction/chapter.adoc[]
+include::getting-started/chapter.adoc[]
+include::configuring/chapter.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/reference/architecture/1xx-responses.adoc b/jetty-documentation/src/main/asciidoc/reference/architecture/1xx-responses.adoc
new file mode 100644
index 0000000..7cb776d
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/architecture/1xx-responses.adoc
@@ -0,0 +1,39 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[1xx-responses]]
+=== Managing 1xx Responses
+
+The http://www.ietf.org/rfc/rfc2616.txt[HTTP RFC] allows for 1xx informational responses to be sent before a real content response. 
+Unfortunately the servlet specification does not provide a way for these to be sent, so Jetty has had to provide non-standard handling of these headers.
+
+[[100-continue]]
+==== 100 Continue
+
+The 100 Continue response should be sent by the server when a client sends a request with a Expect: 100-continue header, as the client will not send the body of the request until the 100 continue response has been sent.
+
+The intent of this feature is to allow a server to inspect the headers and to tell the client to not send a request body that might be too large or insufficiently private or otherwise unable to be handled.
+
+Jetty achieves this by waiting until the input stream or reader is obtained by the filter/servlet, before sending the 100 continues response. 
+Thus a filter/servlet may inspect the headers of a request before getting the input stream and send an error response (or redirect etc.) rather than the 100 continues.
+
+[[102-processing]]
+==== 102 Processing
+
+http://www.ietf.org/rfc/rfc2518.txt[RFC 2518] defines the 102 processing response that can be sent "when the server has a reasonable expectation that the request will take significant time to complete.
+As guidance, if a method is taking longer than 20 seconds (a reasonable, but arbitrary value) to process the server SHOULD return a 102 (Processing) response".
+
+So if a request is received with the Expect: 102-processing header, then a filter/servlet may send a 102 response (without terminating further processing) by calling `servletResponse.sendError(102);`.
diff --git a/jetty-documentation/src/main/asciidoc/reference/architecture/basic-architecture.adoc b/jetty-documentation/src/main/asciidoc/reference/architecture/basic-architecture.adoc
new file mode 100644
index 0000000..04c6920
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/architecture/basic-architecture.adoc
@@ -0,0 +1,176 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[basic-architecture]]
+=== Jetty Architecture
+
+==== View from 20,000 feet 
+
+The Jetty link:{JDURL}/org/eclipse/jetty/server/Server.html[Server] is the plumbing between
+a collection of `Connector`s that accept connections and a collection of `Handler`s that
+service requests from the connections and produce responses, with threads from a thread pool doing the work.
+
+image:images/jetty-high-level-architecture.png[image,width=576]
+
+While the Jetty request/responses are derived from the Servlet API, the full features of the Servlet API
+are only available if you configure the appropriate handlers.
+For example, the session API on the request is inactive unless the request has been passed to a `SessionHandler`.
+The concept of a Servlet itself is implemented by a `ServletHandler`.
+If Servlets are not required, there is very little overhead in the use of the servlet request/response APIs.
+Thus you can build a Jetty server using only connectors and handlers, without using Servlets.
+
+The job of configuring Jetty is building a tree of connectors and handlers and providing their individual configurations.
+As Jetty components are simply Plain Old Java Objects (POJOs), you can accomplish this assembly
+and configuration of components by a variety of techniques:
+
+* In code, see the examples in the Jetty Source XRef.
+* Using Jetty XML, a dependency injection style in XML format.
+* With your dependency injection framework of choice, Spring or XBean.
+* Using Jetty WebApp and Context Deployers.
+
+==== Patterns
+
+The implementation of Jetty follows some fairly standard patterns. 
+Most abstract concepts such as `Connector`s and `Handler`s are captured by interfaces.
+Generic handling for those interfaces is then provided in an abstract implementation
+such as `AbstractConnector` and `AbstractHandler`.
+
+image:images/basic-architecture-patterns.png[image,width=576]
+
+The JSR77 inspired life cycle of most Jetty components is represented by the `LifeCycle`
+interface and the `AbstractLifeCycle` implementation used as the base of many Jetty components.
+
+==== Connectors
+
+A `Connector` is the component that accepts TCP connections.
+For each accepted TCP connection, the `Connector` asks a `ConnectionFactory` to create
+a `Connection` object that handles the network traffic on that TCP connection, parsing
+and generating bytes for a specific protocol.
+
+A `ServerConnector` can therefore be configured with one or more `ConnectionFactory`.
+
+The simplest case is a single `ConnectionFactory` such as `HttpConnectionFactory`, that
+creates `HttpConnection` objects that parse and generate bytes for the HTTP/1.1 protocol.
+
+A more complex case can be a `ServerConnector` configured with three factories:
+`ProxyConnectionFactory`, `SslConnectionFactory` and `HttpConnectionFactory`.
+Such connector will be able to handle PROXY protocol bytes coming from a load balancer
+such as HAProxy (with the `ProxyConnectionFactory`), then handle TLS bytes (with
+`SslConnectionFactory`) and therefore decrypting/encrypting the bytes from/to a remote
+client, and finally handling HTTP/1.1 bytes (with `HttpConnectionFactory`).
+Each `ConnectionFactory` is asked to create a `Connection` object for each TCP connection;
+the `Connection` objects will be chained together to handle the bytes, each for its
+own protocol.
+Therefore the `ProxyConnection` will handle the PROXY protocol bytes, `SslConnection`
+will handle the encryption/decryption of the bytes, and `HttpConnection` will handle
+the HTTP/1.1 bytes producing a request and response object that will be processed by
+applications.
+
+Advanced usages of Jetty will allow users to write their own `ConnectionFactory` to
+handle custom protocols that are not implemented directly by the Jetty project,
+therefore using Jetty as a generic network server.
+
+==== Handlers
+
+A `Handler` is the component that deals with HTTP requests and responses.
+The core API of a handler is the handle method:
+
+image:images/basic-architecture-handlers.png[image,width=576]
+
+[source, java, subs="{sub-order}"]
+----
+public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+----
+
+Parameters:
+
+* `target` – the target of the request, either a URI or a name.
+* `baseRequest` – the original unwrapped request object.
+* `request` – the request object, either as the `baseRequest` object or a wrapper of `baseRequest`.
+You can use the HttpConnection.getCurrentConnection() method to access the Request object if required.
+* response – the response object, either unwrapped as `Response` or a wrapper of that response.
+You can use the HttpConnection.getCurrentConnection() method to access the `Response` object if required.
+
+An implementation of this method can handle the request, pass the request onto another handler (or servlet)
+or it might modify and/or wrap the request and then pass it on.
+This gives three styles of Handler:
+
+* Coordinating handlers – handlers that route requests to other handlers (`HandlerCollection`, `ContextHandlerCollection`)
+* Filtering handlers – handlers that augment a request and pass it on to other handlers (`HandlerWrapper`, `ContextHandler`, `SessionHandler`)
+* Generating handlers – handlers that produce content (`ResourceHandler` and `ServletHandler`)
+
+===== Nested Handlers and Handlers Called Sequentially
+
+You can combine handlers to handle different aspects of a request by nesting them,
+calling them in sequence, or by combining the two models.
+
+image:images/basic-architecture-nested-handlers.png[image,width=576]
+
+Handlers called in sequence perform actions that do not depend on the next invocation, nor on the handler order. 
+They handle a request and generate the response without interacting with other handlers. 
+The main class for this model is `HandlerCollection`.
+
+Nested handlers are called according to a before/invokeNext/after pattern. 
+The main class for nested handlers is `HandlerWrapper`.
+Nested handlers are much more common than those called in sequence.
+
+See also xref:writing-custom-handlers[].
+
+===== Servlet Handler
+
+The `ServletHandler` is a `Handler` that generates content by passing the request to any
+configured Servlet Filters and then to a Servlet mapped by a URI pattern.
+
+image:images/basic-architecture-servlet-handler.png[image,width=576]
+
+A `ServletHandler` is normally deployed within the scope of a `ServletContext`, which is a
+`ContextHandler` that provides convenience methods for mapping URIs to servlets.
+
+Filters and Servlets can also use a `RequestDispatcher` to reroute a request to another context
+or another Servlet in the current context.
+
+[[what-is-a-context]]
+==== Contexts
+
+Contexts are handlers that group other handlers below a particular URI context path or a virtual host.
+Typically a context can have:
+
+* A context path that defines which requests are handled by the context (e.g. `/myapp`)
+* A resource base for static content (a document root)
+* A class loader to obtain classes specific to the context (typically from `/WEB-INF/classes` and `/WEB-INF/lib`)
+* Virtual host names
+
+Contexts implementations include:
+
+* `ContextHandler`
+* `ServletContextHandler`
+* `WebAppContext`
+
+A web application context combines handlers for security, session and servlets in a single unit
+that you can configure with a `web.xml` descriptor.
+
+==== Web Application
+
+A `WebAppContext` is a derivation of `ServletContextHandler` that supports the standardized layout
+of a web application and configuration of session, security, listeners, filter, servlets, and JSP
+via a `web.xml` descriptor normally found in the `/WEB-INF` directory of a web application.
+
+image:images/basic-architecture-web-application.png[image,width=576]
+
+Essentially `WebAppContext` is a convenience class that assists the construction and configuration
+of other handlers to achieve a standard web application configuration.
+Configuration is actually done by pluggable implementations of the Configuration class and the
+prime among these is `WebXmlConfiguration.`
diff --git a/jetty-documentation/src/main/asciidoc/reference/architecture/chapter.adoc b/jetty-documentation/src/main/asciidoc/reference/architecture/chapter.adoc
new file mode 100644
index 0000000..ac74770
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/architecture/chapter.adoc
@@ -0,0 +1,25 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[architecture]]
+== Architecture
+
+General items related to the architecture of jetty and how it deals with certain design decisions.
+
+include::basic-architecture.adoc[]
+include::jetty-classloading.adoc[]
+include::1xx-responses.adoc[]
+include::server-side-architecture.adoc[]
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-handlers.png b/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-handlers.png
new file mode 100644
index 0000000..47dd0e6
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-handlers.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-nested-handlers.png b/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-nested-handlers.png
new file mode 100644
index 0000000..8774001
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-nested-handlers.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-patterns.png b/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-patterns.png
new file mode 100644
index 0000000..777eed9
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-patterns.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-servlet-handler.png b/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-servlet-handler.png
new file mode 100644
index 0000000..d8f2ea8
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-servlet-handler.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-web-application.png b/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-web-application.png
new file mode 100644
index 0000000..e5232a2
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/architecture/images/basic-architecture-web-application.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/architecture/images/jetty-high-level-architecture.png b/jetty-documentation/src/main/asciidoc/reference/architecture/images/jetty-high-level-architecture.png
new file mode 100644
index 0000000..02bf647
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/architecture/images/jetty-high-level-architecture.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/architecture/jetty-classloading.adoc b/jetty-documentation/src/main/asciidoc/reference/architecture/jetty-classloading.adoc
new file mode 100644
index 0000000..5fcec47
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/architecture/jetty-classloading.adoc
@@ -0,0 +1,192 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-classloading]]
+=== Jetty Classloading
+
+Class loading in a web container is slightly more complex than a normal Java application.
+The normal configuration is that each web context (web application or WAR file) has its own classloader, which has the system classloader as its parent.
+Such a classloader hierarchy is normal in Java, however the servlet specification complicates the hierarchy because it requires the following:
+
+* Classes contained within WEB-INF/lib or WEB-INF/classes have priority over classes on the parent classloader.
+This is the opposite of the normal behaviour of a Java 2 classloader.
+* System classes such as `java.lang.String` are excluded from the webapp priority, and you may not replace them with classes in `WEB-INF/lib` or `WEB-INF/` classes.
+Unfortunately the specification does not clearly state what classes are _System_ classes, and it is unclear if all javax classes should be treated as System classes.
+* Server implementation classes like link:{JDURL}/org/eclipse/jetty/server/Server.html[Server] should be hidden from the web application and should not be available in any classloader.
+Unfortunately the specification does not state what classes are _Server_ classes, and it is unclear if common libraries like the Xerces parser should be treated as Implementation classes.
+
+[[configuring-webapp-classloading]]
+==== Configuring Webapp Classloading
+
+Jetty provides configuration options to control the three webapp class loading issues identified above.
+
+You can configure webapp classloading by several methods on the link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html[WebAppContext].
+You can call these methods directly if you are working with the Jetty API, or you can inject methods from a context XML file if you are using the Context Provider (xref:using-context-provider[]).
+You CANNOT set these methods from a `jetty-web.xml` file, as it executes after the classloader configuration is set.
+
+[[controlling-webapp-classloader-priority]]
+===== Controlling Webapp Classloader Priority
+
+The method link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#isParentLoaderPriority()[org.eclipse.jett .webapp.WebAppContext.setParentLoaderPriority(boolean)] allows control over the priority given to webapp classes over system classes.
+If you set it to false (the default), Jetty uses standard webapp classloading priority.
+However, if in this mode some classes that are dependencies of other classes are loaded from the parent classloader (due to settings of system classes below), ambiguities might arise as both the webapp and system classloader versions can end up being loaded.
+
+If set to true, Jetty uses normal JavaSE classloading priority, and gives priority to the parent/system classloader.
+This avoids the issues of multiple versions of a class within a webapp, but the version the parent/system loader provides must be the right version for all webapps you configure in this way.
+
+[[configuring-webapp-caching]]
+===== Configuring Webapp Classloader Caching
+
+Introduced in Jetty 9.3.6, the link:{JDURL}/org/eclipse/jetty/webapp/CachingWebAppClassLoader.html[CachingWebAppClassLoader] can be used to cache `getResource(String)` results.
+For webapps that search for classes and resources regularly, this can increase speed and performance.
+This is an optional feature and it should be noted that it can conflict with several other libraries such as JSP, JSTL, JSF and CDI.
+As such, this feature must be manually enabled for each webapp you want to use it in.
+
+Below is an example of implementing this feature using Jetty IoC XML format:
+
+[source, xml, options="header"]
+----
+<Configure id="mywebapp" class="org.eclipse.jetty.webapp.WebAppContext">
+
+...
+
+  <Set name="classLoader">
+    <New class="org.eclipse.jetty.webapp.CachingWebAppClassLoader">
+      <Arg><Ref refid="mywebapp"/></Arg>
+    </New>
+  </Set>
+
+...
+</Configure>
+----
+
+[[classloading-setting-system-classes]]
+===== Setting System Classes
+
+You can call the methods link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setSystemClasses%28java.lang.String%5B%5D%29[org.eclipse.jetty.webapp.WebAppContext.setSystemClasses(String Array)] or link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#addSystemClass(java.lang.String)[org.eclipse.jetty.webapp.WebAppContext.addSystemClass(String)] to allow fine control over which classes are considered System classes.
+
+* A web application can see a System class.
+* A WEB-INF class cannot replace a System class.
+
+The default system classes are:
+
+.Default System Classes
+[width="100%",cols="8%,92%",options="header",]
+|=======================================================================
+|System Classes
+|java. |Java SE classes (per servlet spec v2.5 / SRV.9.7.2).
+|javax. |Java SE classes (per servlet spec v2.5 / SRV.9.7.2).
+|org.xml. |Needed by javax.xml.
+|org.w3c. |Needed by javax.xml.
+|org.eclipse.jetty.continuation. |Webapp can see and not change continuation classes.
+|org.eclipse.jetty.jndi. |Webapp can see and not change naming classes.
+|org.eclipse.jetty.jaas. |Webapp can see and not change JAAS classes.
+|org.eclipse.jetty.websocket. |WebSocket is a Jetty extension.
+|org.eclipse.jetty.servlet.DefaultServlet |Webapp can see and not change default servlet.
+|=======================================================================
+
+Absolute classname can be passed, names ending with . are treated as packages names, and names starting with - are treated as negative matches and must be listed before any enclosing packages.
+
+[[setting-server-classes]]
+===== Setting Server Classes
+
+You can call the methods link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setServerClasses%28java.lang.String%5B%5D%29[org.eclipse.jetty.webapp.WebAppContext.setServerClasses(String Array)] or
+link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#addServerClass(java.lang.String)[org.eclipse.jetty.webapp.WebAppContext.addServerClass(String)] to allow fine control over which classes are considered Server classes.
+
+* A web application cannot see a Server class.
+* A WEB-INF class can replace a Server class.
+
+The default server classes are:
+
+.Default Server Classes
+[width="100%",cols="8%,92%",options="header",]
+|=======================================================================
+|Server Classes
+|-org.eclipse.jetty.continuation. |Don't hide continuation classes.
+|-org.eclipse.jetty.jndi. |Don't hide naming classes.
+|-org.eclipse.jetty.jaas. |Don't hide jaas classes.
+|-org.eclipse.jetty.servlets. |Don't hide utility servlet classes if provided.
+|-org.eclipse.jetty.servlet.DefaultServlet |Don't hide default servlet.
+|-org.eclipse.jetty.servlet.listener. |Don't hide utility listeners
+|-org.eclipse.jetty.websocket. |Don't hide websocket extension.
+| org.eclipse.jetty. |Do hide all other Jetty classes.
+|=======================================================================
+
+[[adding-extra-classpaths]]
+==== Adding Extra Classpaths to Jetty
+
+You can add extra classpaths to Jetty in several ways.
+
+[[classpaths-using-start-jar]]
+===== Using `start.jar`
+
+If you are using xref:advanced-start-features[], at startup the jetty runtime automatically loads option Jars from the top level `$jetty.home/lib` directory. The default settings include:
+
+* Adding Jars under `$jetty.home/lib/ext` to the system classpath.
+You can place additional Jars here.
+* Adding the directory `$jetty.home/resources` to the classpath (may contain classes or other resources).
+* Adding a single path defined by the command line parameter __path__.
+
+[[using-extra-classpath-method]]
+===== Using the extraClasspath() method
+
+You can add an additional classpath to a context classloader by calling link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setExtraClasspath(java.lang.String)[org.eclipse.jetty.webapp.WebAppContext.setExtraClasspath(String)] with a comma-separated list of paths.
+You can do so directly to the API via a context XML file such as the following:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+ ...
+ <Set name="extraClasspath>../my/classes,../my/jars/special.jar,../my/jars/other.jar</Set>
+ ...
+----
+
+[[using-custom-webappclassloader]]
+==== Using a Custom WebAppClassLoader
+
+If none of the alternatives already described meet your needs, you can always provide a custom classloader for your webapp.
+We recommend, but do not require, that your custom loader subclasses link:{JDURL}/org/eclipse/jetty/webapp/WebAppClassLoader.html[WebAppClassLoader].
+You configure the classloader for the webapp like so:
+
+[source, java, subs="{sub-order}"]
+----
+MyCleverClassLoader myCleverClassLoader = new MyCleverClassLoader();
+ ...
+   WebAppContext webapp = new WebAppContext();
+ ...
+   webapp.setClassLoader(myCleverClassLoader);
+
+----
+
+You can also accomplish this in a context xml file.
+
+[[starting-jetty-custom-classloader]]
+==== Starting Jetty with a Custom ClassLoader
+
+If you start a Jetty server using a custom class loader–consider the Jetty classes not being available to the system class loader, only your custom class loader–you may run into class loading issues when the WebAppClassLoader kicks in.
+By default the WebAppClassLoader uses the system class loader as its parent, hence the problem. This is easy to fix, like so:
+
+[source, java, subs="{sub-order}"]
+----
+context.setClassLoader(new WebAppClassLoader(this.getClass().getClassLoader(), context));
+----
+
+or
+
+[source, java, subs="{sub-order}"]
+----
+context.setClassLoader(new WebAppClassLoader(new MyCustomClassLoader(), context));
+----
diff --git a/jetty-documentation/src/main/asciidoc/reference/architecture/server-side-architecture.adoc b/jetty-documentation/src/main/asciidoc/reference/architecture/server-side-architecture.adoc
new file mode 100644
index 0000000..6918bed
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/architecture/server-side-architecture.adoc
@@ -0,0 +1,97 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[creating-custom-protocol]]
+=== Creating a Custom Protocol
+
+You can create custom protocols with Jetty. This page provides an example of how to do so, with Telnet as the protocol.
+
+To create a custom Telnet protocol, complete the following tasks:
+
+* Implement a `TelnetServerConnectionFactory`.
+* Implement a `TelnetServerConnection` by extending `o.e.j.io.AbstractConnection`.
+* Create a parser/interpreter for the bytes you receive (this is totally independent from Jetty).
+* If needed, design an API for the application to use to process the bytes received (also independent from Jetty). 
+The API likely has a _respond back_ primitive that uses a Jetty provided `EndPoint` and `EndPoint.write(Callback, Buffer...)` to write the response bytes.
+
+[[server-connection-factory]]
+==== Implementing a TelnetServerConnectionFactory
+
+Begin with an `org.eclipse.jetty.server.ServerConnector`, which you can use as is. `ServerConnector` takes a `o.e.j.server.ConnectionFactory`, which creates `o.e.j.io.Connection` objects that interpret the bytes the connector receives. 
+You must implement `ConnectionFactory` with a `TelnetServerConnectionFactory`, where you return a Connection implementation (for example, `TelnetServerConnection`).
+
+[[telnet-server-connection]]
+==== Implementing the TelnetServerConnection
+
+For the Connection implementation you need to extend from `o.e.j.io.AbstractConnection` because it provides many facilities that you would otherwise need to re-implement from scratch.
+
+For each Connection instance there is associated an `o.e.j.io.EndPoint` instance. 
+Think of `EndPoint` as a specialized version of JDK’s `SocketChannel`. 
+You use the `EndPoint` to read, write, and close. 
+You don’t need to implement `EndPoint`, because Jetty provides concrete
+classes for you to use.
+
+The Connection is the _passive_ side (that is, Jetty calls it when there is data to read), while the `EndPoint` is the active part (that is, applications call it to write data to the other end). 
+When there is data to read, Jetty calls `AbstractConnection.onFillable()`, which you must implement in your `TelnetServerConnection`.
+
+A typical implementation reads bytes from the `EndPoint` by calling `EndPoint.fill(ByteBuffer)`. 
+For examples, look at both the simpler `SPDYConnection` (in the SPDY client package, but server also uses it), and the slightly more complex `HttpConnection`.
+
+[[parser-interpreter]]
+==== Parsing the Bytes Received
+
+After you read the bytes, you need to parse them. 
+For the Telnet protocol there is not much to parse, but perhaps you have your own commands that you want to interpret and execute. 
+Therefore typically every connection has an associated parser instance. 
+In turn, a parser usually emits parse events that a parser listener interprets, as the following examples illustrate:
+
+* In HTTP, the Jetty HTTP parser parses the request line (and emits a parser event), then parses the headers (and emits a parser event for each) until it recognizes the end of the headers (and emits another parser event). 
+At that point, the _interpreter_ or parser listener (which for HTTP is `o.e.j.server.HttpChannel`) has all the information necessary to build a `HttpServletRequest` object and can call the user code (the web application, that is, servlets/filters). 
+* In SPDY, the Jetty SPDY parser parses a SPDY frame (and emits a parser event), and the parser listener (an instance of o.e.j.spdy.StandardSession) interprets the parser events and calls user code (application-provided listeners).
+
+With `ConnectionFactory`, Connection, parser, and parser listeners in place, you have configured the read side.
+
+[[api-byte-processor]]
+==== Designing an API to Process Bytes
+
+At this point, server applications typically write data back to the client.
+
+The Servlet API (for HTTP) or application-provided listeners (for SPDY) expose an interface to web applications so that they can write data back to the client. 
+The implementation of those interfaces must link back to the `EndPoint` instance associated with the Connection instance so that it can write data via `EndPoint.write(Callback, ByteBuffer...)`. 
+This is an asynchronous call, and it notifies the callback when all the buffers have been fully written.
+
+For example, in the Servlet API, applications use a `ServletOutputStream` to write the response content. 
+`ServletOutputStream` is an abstract class that Jetty implements, enabling Jetty to handle the writes from the web application; the writes eventually end up in an `EndPoint.write(...)` call.
+
+[[api-tips]]
+===== Tips for Designing an API
+
+If you want to write a completely asynchronous implementation, your API to write data to the client must have a callback/promise concept: “Call me back when you are done, and (possibly) give me the result of the computation."
+
+SPDY’s Stream class is a typical example. 
+Notice how the methods there exist in two versions, a synchronous (blocking) one, and an asynchronous one that takes as last parameter a Callback (if no result is needed), or a Promise (if a result is needed). 
+It is trivial to write the synchronous version in terms of the asynchronous version.
+
+You can use `EndPoint.write(Callback, ByteBuffer...)` in a blocking way as follows:
+
+[source, java, subs="{sub-order}"]
+----
+FutureCallback callback = new FutureCallback();
+endPoint.write(callback, buffers);
+callback.get();
+----
+
+With the snippet above your API can be synchronous or asynchronous (your choice), but implemented synchronously.
diff --git a/jetty-documentation/src/main/asciidoc/reference/contributing/bugs.adoc b/jetty-documentation/src/main/asciidoc/reference/contributing/bugs.adoc
new file mode 100644
index 0000000..88b52fb
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/contributing/bugs.adoc
@@ -0,0 +1,24 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[bugs]]
+=== Issues, Features, and Bugs
+
+As with any constantly evolving software project, there will be issues, features, and bugs.
+We want to know whats bugging you!
+
+File bugs as Issues in our Github repository http://github.com/eclipse/jetty.project[Issues at Github]
+
diff --git a/jetty-documentation/src/main/asciidoc/reference/contributing/chapter.adoc b/jetty-documentation/src/main/asciidoc/reference/contributing/chapter.adoc
new file mode 100644
index 0000000..cc19329
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/contributing/chapter.adoc
@@ -0,0 +1,31 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[advanced-contributing]]
+== Contributing to Jetty
+
+There are many ways to contribute to Jetty, from hardened developers looking to sharpen their skills to neophytes interested in working with an open source project for the first time. 
+Here we show ways to interact with the Jetty community, give feedback to the developers and contribute patches to both core Jetty code and even this document.
+
+include::community.adoc[]
+include::documentation.adoc[]
+include::source-build.adoc[]
+include::coding-standards.adoc[]
+include::bugs.adoc[]
+include::patches.adoc[]
+include::security.adoc[]
+include::releasing-jetty.adoc[]
+include::release-testing.adoc[]
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/reference/contributing/coding-standards.adoc b/jetty-documentation/src/main/asciidoc/reference/contributing/coding-standards.adoc
new file mode 100644
index 0000000..58bba75
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/contributing/coding-standards.adoc
@@ -0,0 +1,87 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[coding-standards]]
+=== Coding Standards
+
+Jetty uses number of conventions for its source code.
+
+==== Code Formatting
+
+Jetty uses the code formatting the following project specifies.
+
+http://git.eclipse.org/c/jetty/org.eclipse.jetty.admin.git/tree/jetty-eclipse-java-format.xml[Eclipse Java Formatting]
+
+==== Code Templates
+
+Jetty specifies the following code templates for use by the project developers.
+
+http://git.eclipse.org/c/jetty/org.eclipse.jetty.admin.git/tree/jetty-eclipse-codetemplates.xml[Eclipse Code Templates]
+
+==== Code Conventions
+
+The following is an example of the Java formatting and naming styles to apply to Jetty:
+
+[source, java, subs="{sub-order}"]
+----
+
+import some.exact.ClassName;      // GOOD
+import some.wildcard.package.*;   // BAD!
+
+package org.always.have.a.package;
+
+/* --------------------------------------------------------- */
+/** Always have some javadoc
+ */
+class MyClassName
+{
+    // indent by 4 spaces.
+    // use spaced to indent
+    // The code must format OK with default tabsize of 8.
+
+    private static final int ALL_CAPS_FOR_PUBLIC_CONSTANTS=1;
+
+    // Field prefixed with __ for static of _ for normal fields.
+    // This convention is no longer mandatory, but any given
+    // class should either consistently use this style or not.
+    private static String __staticField;
+    private Object _privateField;
+
+
+    // use getters and setters rather than public fields.
+    public void setPrivateField(Object privateField)
+    {
+        _privateField=privateField;
+    }
+
+    public Object getPrivateField()
+    {
+        return _privateField;
+    }
+
+    public void doSomething()
+        throws SomeException
+    {
+        Object local_variable = _privateField;
+        if (local_variable==null)
+        {
+             // do Something
+        }
+    }
+}
+
+              
+----
diff --git a/jetty-documentation/src/main/asciidoc/reference/contributing/community.adoc b/jetty-documentation/src/main/asciidoc/reference/contributing/community.adoc
new file mode 100644
index 0000000..5488b4d
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/contributing/community.adoc
@@ -0,0 +1,53 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[community]]
+=== Community
+
+Developers and users alike are welcome to engage the Jetty community.
+We all have day jobs here so don't just ask questions on IRC and frustrated if no one answers right away.
+Stick around to hear an answer, one is likely coming at some point!
+
+[[community-mailing-lists]]
+==== Mailing Lists
+
+We have a number of options for people that wish to subscribe to our mailing lists.
+
+* Jetty Developers List
+** Join - https://dev.eclipse.org/mailman/listinfo/jetty-dev
+** Archives - http://dev.eclipse.org/mhonarc/lists/jetty-dev/
+* Jetty Users List
+** Join - https://dev.eclipse.org/mailman/listinfo/jetty-users
+** Archives - http://dev.eclipse.org/mhonarc/lists/jetty-users/
+* Jetty Announcements List
+** Join - https://dev.eclipse.org/mailman/listinfo/jetty-announce
+** Archives - http://dev.eclipse.org/mhonarc/lists/jetty-announce/
+* Jetty Commit List
+** Join - https://dev.eclipse.org/mailman/listinfo/jetty-commit
+** Archives - http://dev.eclipse.org/mhonarc/lists/jetty-commit/
+
+[[community-irc]]
+==== Internet Relay Chat - IRC
+
+Much of our conversations about Jetty occur on IRC in one location or another.
+Users are always welcome to come join our IRC channels and talk with us, other users, or just lurk.
+
+irc.freenode.org - #jetty::
+  Our primary location, we recommend that if your looking to find folks on IRC you try here.
+  We also have commit notifications coming to this channel on the bottom and top of the hour.
+irc.codehaus.org - #jetty::
+  Our prior location before the move to the eclipse foundation.
+  We are idle on here.
diff --git a/jetty-documentation/src/main/asciidoc/reference/contributing/documentation.adoc b/jetty-documentation/src/main/asciidoc/reference/contributing/documentation.adoc
new file mode 100644
index 0000000..0c0d871
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/contributing/documentation.adoc
@@ -0,0 +1,240 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[contributing-documentation]]
+=== Documentation
+
+This document is produced using a combination of maven, git, and asciidoc. 
+We welcome anyone and everyone to contribute to the content of this book. 
+Below is the information on how to obtain the source of this book and to build it as well as information on how to contribute back to it.
+
+Note: All contributions to this documentation are under the EPLv1 and the copyright is assigned to Mort Bay.
+
+==== Tools
+
+You will need:
+
+git::
+  This documentation is part of the Jetty project so all contributions must be through the normal Jetty contribution process.
++
+You can go one of two ways for using git, if you are familiar with SCM's and the command line interface feel free to install and use git from there. 
+Otherwise we would recommend you use the github client itself as it will help with some of the workflow involved with working with git.
+All contributions much be signed and can be pulled into Jetty through the normal pull request process.
+
+maven 3::
+  We build the documentation with maven 3 which can be found at http://maven.apache.org[Apache Maven].
+
+==== Render Chain
+
+The Jetty documentation is all written in asciidoc which is used as the origin format.  
+The maven build uses the asciidoctor-maven-plugin to process all of the .adoc files into a single docbook file which is then used to produce the final output.
+We use this intermediary step in order to primarily produce chunked html which we then deploy to the Eclipse Jetty website.
+However we can also use this docbook output to produce pdf files, epub books or even Eclipse Help files should we so desire.
+
+==== Getting Started (cli)
+
+First you need to obtain the source of the documentation project.
+
+Clone the repository:
+
+[source, screen, subs="{sub-order}"]
+....
+$ git clone https://github.com/eclipse/jetty.project.git
+....
+
+You will now have a local directory with all of jetty, including the jetty-documentation.
+Now we move on to building it.
+
+[source, screen, subs="{sub-order}"]
+....
+$ cd jetty.project/jetty-documentation 
+$ mvn install
+....
+
+While maven is running you may see a lot of files being downloaded. 
+If you are not familiar with maven, then what you are seeing is maven setting up the execution environment for generating the documentation.
+This build will first produce docbook xml and then through the docbkx-maven-plugin generate the chunked html output.
+The downloads are all of the java dependencies that are required to make this build work. 
+After a while the downloading will stop and you should see the execution of the asciidoctor-maven-plugin followed by the docbkx-maven-plugin.
+
+[source, screen, subs="{sub-order}"]
+....
+[INFO] --- asciidoctor-maven-plugin:1.5.3:process-asciidoc (output-html) @ jetty-documentation ---
+[INFO] Rendered /Users/jesse/src/projects/jetty/jetty-docs/src/main/asciidoc/index.adoc
+[INFO]
+
+[INFO] Processing input file: index.xml
+[INFO] Applying customization parameters
+[INFO] Chunking output.
+[INFO] See /Users/jesse/src/projects/jetty/jetty-docs/target/docbkx/html/index for generated file(s)
+....
+
+The build is finished once you see a message akin to this:
+
+[source, screen, subs="{sub-order}"]
+....
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD SUCCESS
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time: 7.014s
+[INFO] Finished at: Tue Oct 25 14:15:37 CDT 2011
+[INFO] Final Memory: 14M/229M
+[INFO] ------------------------------------------------------------------------  
+....
+
+You may now open your web browser and browse to the first page of the html output to see what you have produced! 
+Generally you can do this with File -> Open File -> which will open a file system browsing screen, navigate to your jetty-documentation directory and then further into target/docbkx/html/index/index.html which is the first page of the produced documentation.
+
+____
+[TIP]
+If the build is broken, feel free to notify us.
+____
+
+==== Making Changes
+
+Now that you have built the documentation, you want to edit it and make some changes. 
+We'll now have to take a bit of as step back and look at how git and github works. 
+In the above example you have cloned directly from our canonical documentation repository. 
+Obviously we can not allow anyone immediate access to this repository so you must make a fork of it for your own and then issue back pull requests to build up documentation karma. 
+In English that means that you would go to the url of the documentation in github:
+
+....
+https://github.com/eclipse/jetty.project
+....
+
+When you are on this page you will see a little button called 'Fork' which you can click and you will be taken back to your main page on github where you have a new repository. 
+When you checkout this repository you are free to commit to your heart's delight all the changes you so direly wish to see in the Jetty documentation. 
+You can clone it to your local machine and build it the same way as above. 
+So let's start small with a little example. 
+Find some paragraph in the documentation that you think needs changed. Locate that in the local checkout and make the change. 
+Now follow the process to push that change back into Jetty proper. 
+Do make sure the change works and the build isn't broken though so make sure you run maven and check the output.
+Then commit the change.
+
+[source, screen, subs="{sub-order}"]
+....
+$ git commit -s -m "Tweaked the introduction to fix a horrid misspelled word." src/main/asciidoc/quickstart/introduction/topic.xml  
+....
+
+____
+[NOTE]
+In order for us to accept your commit into the Jetty repository you must have an Eclipse CLA on file and sign your commit.
+Please check out the link:#contributing-cla[patches] section for more information.
+____
+
+This will commit the change in your local repository. 
+You can then push the change up to your repository on github.
+
+[source, screen, subs="{sub-order}"]
+....
+$ git push
+....
+
+Now you'll see some output showing that your change has been propagated to your repository on github. 
+In fact if you navigate to that repository at the top of the files list you should see your comment there. 
+Success, your change is now positioned for notifying us about it! 
+If you click on the commit message itself you'll be taken to a screen that shows what files were changed in that commit. In the upper right corner is a button for 'Pull Request'. 
+When you select this and follow the workflow we will then be notified of your contribution and will be able to apply it to our git repository upon review.
+
+Thats it! You have successfully contributed to the documentation efforts of the Jetty project. 
+After enough of these sorts of contributions and building up good community karma, you may be asked to join us as a committer on the documentation.
+
+==== Conventions
+
+Below is list of conventions that should be followed when developing documentation within this framework. 
+These are not set in stone and should be updated as we learn more.
+
+ventilated prose::
+  Each sentence should be on its own line with a hard return at the end of the line.
+  Asciidoc rendering does not treat this style as separate lines and will produce paragraphs correctly.
+  The primary benefit is that you can easily see changes between scm versions of files, and it makes it trivial to quickly look through a pull request.
+  Additional benefits are the ability to comment out a sentence mid paragraph or move sentences around within a paragraph.
+  Enabling Soft Line Wrap in your favorite editor can make this a bit easier to stomach.
+
+id's::
+  Critically important for being able to generate url's that can be used in a persistent fashion. 
+  Without sane id's the chapters and sections will have generated id which are rooted in some obscure location based
+  voodoo. 
+  A url using these 'e12c8673' type links will not be durable across generations of the documentation. 
+  These id's need to be used on chapters and sections two deep, and anywhere that you intend to cross link deeper.
++
+The id values go into a global namespace so they must be unique across the entire document or the last example will win and any cross links will go there. 
+Below is an example of an id.
+
+....
+[[this-id-an-id]]
+....
+
+link vs xref::
+  The `link:` item should be generally used for linking around the document when you want to choose the text that will be rendered in the link itself.
+  However if you are linking to a section and want to use the title itself as the link text, use the `xref:` tag without the hashmark in front of the link id.
+
+version differences::
+  In general differences in functionality within a release should go into nested sections and use titles like 'Prior to: ##' or 'In version: ##'.
+  
+license blocks::
+  Each adoc file should contain the license block that exists in the index.adoc file and a copy has been added to the bottom of this page as well for reference. 
+
+....
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+....
+
+Some admonition examples:
+
+______________________________________________
+[NOTE]
+A note about the previous case to be aware of.
+______________________________________________
+
+________________________________________
+[IMPORTANT]
+Important notes are marked with an icon.
+________________________________________
+
+________________________________
+[TIP]
+Tips that make your life easier.
+________________________________
+
+_______________________________________________________
+[CAUTION]
+Places where you have to be careful what you are doing.
+_______________________________________________________
+
+__________________________________________________________________________________________________________________
+[WARNING]
+Where extreme care has to be taken. Data corruption or other nasty
+things may occur if these warnings are ignored.
+__________________________________________________________________________________________________________________
+
+==== Oddities
+
+* If an included file ends with a list entry, it needs to have two empty lines at the end of the file in order for the section rendering to work correctly. 
+
diff --git a/jetty-documentation/src/main/asciidoc/reference/contributing/patches.adoc b/jetty-documentation/src/main/asciidoc/reference/contributing/patches.adoc
new file mode 100644
index 0000000..cd471d7
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/contributing/patches.adoc
@@ -0,0 +1,105 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[contributing-patches]]
+=== Contributing Patches
+
+We love seeing people contribute patches to the Jetty project and the process is relatively simple.
+The requirements to commit are modest but very important to the Eclipse Foundation and the intellectual property of the open source project.
+The following is the general process by which we operate.
+
+* You must have a signed Eclipse Contributor Agreement.
+* This agreement must be under the _same_ email address as the Git pull request originates from.
+* The commit must be signed.
+* When the pull request is made, a git-hook will validate the email address.
+** If the result is a green checkbox then the Jetty committers can review the pull request.
+** If the result is a red X then there is absolutely nothing the Jetty committers can do to accept the commit at this point.
+* This may not be the final form a commit will take, there may be some back and forth and you may be asked to re-issue a pull request.
+
+
+
+Not everything is specifically relevant since we are at GitHub but the crux of things are detailed there.  The CLA is critically important to the process.
+
+[[contributing-cla]]
+==== Sign a CLA
+
+The Eclipse Foundation has a strong Intellectual Property policy which tracks contributions in detail to ensure that:
+
+1.  Did the contributor author 100% of the content?
+2.  Does the contributor have the rights to contribute this content to Eclipse?
+3.  Is the contribution under the project’s license(s) (e.g. EPL)
+
+A contributor needs to e-sign a Contributor Licence Agreement (for more explanation see the http://www.eclipse.org/legal/clafaq.php[Eclipse CLA FAQ] ) regardless of how their contribution patch is provided.
+You can familiarize yourself with the Eclipse wiki page at http://wiki.eclipse.org/Development_Resources/Contributing_via_Git[Contributing via Git].
+In order to have a pull request accepted by any Eclipse project you must complete this agreement.
+____
+[TIP]
+Log into the https://projects.eclipse.org/user/login/sso[Eclipse projects forge] (you will need to create an account with the Eclipse Foundation if you have not already done so); click on "Contributor License Agreement"; and Complete the form.
+Be sure to use the _same email address_ when you create any Git commit records.
+____
+
+[[contributing-git-config]]
+==== Configuring Git
+
+GitHub has copious amounts of quality documentation on how to interact with the system and you will minimally need to configure the user.email property.
+Check out the following link:https://help.github.com/articles/setting-your-email-in-git[guide on GitHub] for more information.
+
+[[contributing-making-the-commit]]
+==== Making the Commit
+
+When making the commit for the pull request it is  _vital_ that you "sign-off" on the commit using "git commit -s" option.
+Without this sign-off, your patch cannot be applied to the Jetty repository because it will be rejected.
+
+You can check out the link:https://help.github.com/articles/signing-tags-using-gpg[guide at Github] for more information.
+____
+[TIP]
+One way to think of this is that when you sign the CLA you are indicating that you are free to contribute to eclipse, but that doesn't mean everything you ever do can be contributed.
+Using the commit signing mechanism indicates that your commit is under the auspices of your agreement.
+____
+
+If a pull request is for a particular issue in our repository then the format of the commit message is important.
+The message should follow the form "Issue #123 <description of the commit>".
+When the Jetty project runs releases we have an automated process that scans for commits with this format for inclusion in our VERSION.txt file.
+
+____
+> git commit -s -m "Issue #123 resolving the issue by adding widget"
+____
+
+[[contributing-the-pull-request]]
+==== The Pull Request
+
+Pull requests are very much a GitHub process so best link:https://help.github.com/articles/creating-a-pull-request[explained by Github].
+
+[[contributing-our-policies]]
+==== Our Policies
+
+We wholeheartedly welcome contributions to Jetty and will do our best to process them in a timely fashion.
+While not every contribution will be accepted as is our commitment is to work with interested parties on the things they care about.
+With that in mind, short of some simple contributions we can only handle pull requests with actively engaged parties.
+We reserve the right to abandon pull requests whose authors do no respond in a timely fashion.
+We will generally adhere to the following time frames for contributions.
+
+* Invalid Pull Requests - 1 week
+** These pull requests do not follow the contribution requirements for some reason, be it missing contributor agreement or the wrong email.
+** We will try and follow up with the pull request author to resolve the issue but much of this is out of our hands and are between committer and the Eclipse Foundation.
+** If we do not hear from the contributor after a week we will close the pull request.
+
+* Valid Pull Requests - 2 weeks
+** These pull requests have a green check mark after the commit title.
+** If the pull request can be immediately applied we will do so.
+** There may need to be some conversation on the issue in which case a committer will follow up with the author in the pull request.
+** If the original contributor does not respond within 2 weeks we may close the commit.
+** If we see value in the commit yet the author has not responded after 2 weeks we may make some variation of the commit ourselves.
diff --git a/jetty-documentation/src/main/asciidoc/reference/contributing/release-testing.adoc b/jetty-documentation/src/main/asciidoc/reference/contributing/release-testing.adoc
new file mode 100644
index 0000000..f086857
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/contributing/release-testing.adoc
@@ -0,0 +1,228 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[release-testing]]
+=== Testing a Jetty Release
+
+To test a Jetty release, complete the following steps for each release you want to test:
+
+1.  Download the staged release:
++
+[source, screen, subs="{sub-order}"]
+....
+
+ wget https://oss.sonatype.org/content/repositories/jetty-[reponumber]/org/eclipse/jetty/jetty-distribution/[jetty-version]/jetty-distribution-9.[jetty-minor-version].tar.gz
+
+      
+....
+2.  Extract to a directory of your choice.
+3.  Start jetty:
++
+[source, screen, subs="{sub-order}"]
+....
+
+ cd [installdir] ; java -jar start.jar
+ 
+      
+....
+4.  If there are no exceptions, proceed. Otherwise, investigate.
+5.  Open http://localhost:8080/ in a browser. In the examples section click "Test Jetty Webapp". You should see the `test.war` webapp.
+6.  Go through ALL the tests and verify that everything works as expected.
+7.  In the examples section click "JAAS Test" and verify that everything works as expected.
+8.  In the examples section click "JNDI Test" and verify that everything works as expected.
+9.  In the examples section click "Servlet 3.1 Test" and verify that everything works as expected.
+10. Verify that hot deployment works.
++
+[source, screen, subs="{sub-order}"]
+....
+
+ cd [installdir] ;
+ touch [pathToJettyDistribution]/webapps.demo/test.xml
+ 
+      
+....
+11. Verify that `test.war` gets redeployed in `STDOUT`.
+12. Verify that the spdy example webapp and spdy-proxy do work
++
+[source, screen, subs="{sub-order}"]
+....
+
+ cd jetty_src/jetty-spdy/spdy-example-webapp
+ mvn jetty:run-forked
+ 
+      
+....
+13. Browse to https://localhost:8443 and verify that all looks ok
+14. Stop the server with CTRL+C and restart it in proxy mode:
++
+[source, screen, subs="{sub-order}"]
+....
+
+ mvn -Pproxy jetty:run-forked
+ 
+      
+....
+15. Browse to http://localhost:8080 and https://localhost:8443 and verify that all looks ok
+
+[[testing-cometd]]
+==== Testing CometD
+
+1.  Clone CometD.
++
+[source, screen, subs="{sub-order}"]
+....
+
+ clone git://github.com/cometd/cometd.git
+ git clone git://github.com/cometd/cometd.git
+ 
+        
+....
+2.  Edit `pom.xml` and update `jetty-version.`
++
+....
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <jetty-version>7.6.2.v20120308</jetty-version>
+        <jetty-plugin-version>${jetty-version}</jetty-plugin-version>
+        <slf4j-version>1.6.4</slf4j-version>
+        <spring-version>3.1.0.RELEASE</spring-version>
+    </properties>
+    <repositories>
+    <repository>
+      <id>Jetty Staging</id>
+      <url>https://oss.sonatype.org/content/repositories/jetty-988/</url>
+    </repository>
+  </repositories>
+ 
+        
+....
+3.  Build Cometd:
++
+[source, screen, subs="{sub-order}"]
+....
+
+ mvn clean install
+  
+        
+....
+4.  Be patient.
+5.  Run the loadtest as it is described here: http://cometd.org/documentation/2.x/howtos/loadtesting.
+Keep the default values, but make sure that you raise the clients setting to 1000.
+Run the loadtest until ''JIT compilation time'' is close to a value of zero (about 10k calls).
+6.  Make sure that the performance results are reasonably fast.
+On a MacBook Pro i7 2.6ghz dualcore produces the following:
++
+[source, screen, subs="{sub-order}"]
+....
+
+ ========================================
+Statistics Started at Fri Mar 09 13:44:35 CET 2012
+Operative System: Mac OS X 10.7.3 amd64
+JVM : Oracle Corporation Java HotSpot(TM) 64-Bit Server VM runtime 23.0-b16 1.7.0_04-ea-b14
+Processors: 4
+System Memory: 99.583336% used of 30.0 GiB
+Used Heap Size: 36.490677 MiB
+Max Heap Size: 1920.0 MiB
+Young Generation Heap Size: 896.0 MiB
+- - - - - - - - - - - - - - - - - - - -
+Testing 1000 clients in 100 rooms, 10 rooms/client
+Sending 1000 batches of 10x50 bytes messages every 10000 ?s
+[GC [PSYoungGen: 786432K->8736K(917504K)] 823650K->45954K(1966080K), 0.0309940 secs] [Times: user=0.09 sys=0.00, real=0.03 secs]
+[GC [PSYoungGen: 795168K->11424K(917504K)] 832386K->48642K(1966080K), 0.0513360 secs] [Times: user=0.13 sys=0.00, real=0.05 secs]
+[GC [PSYoungGen: 797856K->14560K(917504K)] 835074K->51778K(1966080K), 0.0432940 secs] [Times: user=0.12 sys=0.00, real=0.05 secs]
+[GC [PSYoungGen: 800992K->15680K(917504K)] 838210K->52898K(1966080K), 0.0491200 secs] [Times: user=0.14 sys=0.00, real=0.05 secs]
+[GC [PSYoungGen: 802112K->17568K(917504K)] 839330K->54786K(1966080K), 0.0484950 secs] [Times: user=0.14 sys=0.00, real=0.05 secs]
+[GC [PSYoungGen: 804000K->17600K(917504K)] 841218K->54818K(1966080K), 0.0456460 secs] [Times: user=0.14 sys=0.01, real=0.05 secs]
+[GC [PSYoungGen: 804032K->19488K(917504K)] 841250K->56706K(1966080K), 0.0542000 secs] [Times: user=0.15 sys=0.00, real=0.05 secs]
+[GC [PSYoungGen: 805920K->20224K(917504K)] 843138K->57442K(1966080K), 0.0486350 secs] [Times: user=0.16 sys=0.00, real=0.05 secs]
+[GC [PSYoungGen: 806656K->20192K(917504K)] 843874K->57410K(1966080K), 0.0566690 secs] [Times: user=0.15 sys=0.00, real=0.06 secs]
+[GC [PSYoungGen: 806624K->21152K(917504K)] 843842K->58370K(1966080K), 0.0536740 secs] [Times: user=0.16 sys=0.00, real=0.06 secs]
+[GC [PSYoungGen: 807584K->21088K(917504K)] 844802K->58306K(1966080K), 0.0576060 secs] [Times: user=0.18 sys=0.00, real=0.06 secs]
+[GC [PSYoungGen: 807520K->22080K(917504K)] 844738K->59298K(1966080K), 0.0663300 secs] [Times: user=0.19 sys=0.01, real=0.06 secs]
+- - - - - - - - - - - - - - - - - - - -
+Statistics Ended at Fri Mar 09 13:45:21 CET 2012
+Elapsed time: 45826 ms
+    Time in JIT compilation: 52 ms
+    Time in Young Generation GC: 606 ms (12 collections)
+    Time in Old Generation GC: 0 ms (0 collections)
+Garbage Generated in Young Generation: 9036.513 MiB
+Garbage Generated in Survivor Generation: 21.65625 MiB
+Garbage Generated in Old Generation: 0.0 MiB
+Average CPU Load: 156.54865/400
+----------------------------------------
+
+Outgoing: Elapsed = 45820 ms | Rate = 218 messages/s - 21 requests/s - ~0.083 Mib/s
+Waiting for messages to arrive 996960/999045
+All messages arrived 999045/999045
+Messages - Success/Expected = 999045/999045
+Incoming - Elapsed = 45945 ms | Rate = 21743 messages/s - 9496 responses/s(43.68%) - ~8.295 Mib/s
+Messages - Wall Latency Distribution Curve (X axis: Frequency, Y axis: Latency):
+ @                    _  24 ms (8765, 0.88%)
+       @              _  45 ms (58952, 5.90%)
+          @           _  67 ms (87065, 8.71%)
+             @        _  88 ms (113786, 11.39%)
+                   @  _  109 ms (167426, 16.76%)
+                   @  _  131 ms (176163, 17.63%) ^50%
+              @       _  152 ms (123182, 12.33%)
+          @           _  174 ms (90918, 9.10%)
+        @             _  195 ms (67209, 6.73%) ^85%
+     @                _  216 ms (46989, 4.70%)
+   @                  _  238 ms (24975, 2.50%) ^95%
+  @                   _  259 ms (16509, 1.65%)
+ @                    _  281 ms (8454, 0.85%) ^99%
+@                     _  302 ms (4324, 0.43%)
+@                     _  323 ms (2955, 0.30%)
+@                     _  345 ms (957, 0.10%) ^99.9%
+@                     _  366 ms (204, 0.02%)
+@                     _  388 ms (144, 0.01%)
+@                     _  409 ms (25, 0.00%)
+@                     _  430 ms (43, 0.00%)
+Messages - Wall Latency 50th%/99th% = 117/275 ms
+Messages - Wall Latency Min/Ave/Max = 2/123/430 ms
+Messages - Network Latency Min/Ave/Max = 1/114/417 ms
+Thread Pool - Concurrent Threads max = 239 | Queue Size max = 1002 | Queue Latency avg/max = 12/101 ms
+
+        
+....
+7.  Deploy `cometd.war` to the `webapps` directory of the jetty-distribution tested above.
++
+[source, screen, subs="{sub-order}"]
+....
+
+ cp cometd-demo/target/cometd-demo-[version].war [pathToJetty]/jetty-distribution-[jetty-version]/webapps/
+ 
+        
+....
+8.  Start jetty and make sure there are no exceptions.
++
+[source, screen, subs="{sub-order}"]
+....
+
+ cd [pathToJetty] && java -jar start.jar
+ 
+        
+....
+9.  Go through all pages of the demo and test them:
++
+....
+
+ http://localhost:8080/cometd-demo-2.4.1-SNAPSHOT/
+
+        
+....
+
+If all tests are green, you are done!
diff --git a/jetty-documentation/src/main/asciidoc/reference/contributing/releasing-jetty.adoc b/jetty-documentation/src/main/asciidoc/reference/contributing/releasing-jetty.adoc
new file mode 100644
index 0000000..c8b21ac
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/contributing/releasing-jetty.adoc
@@ -0,0 +1,277 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[releasing-jetty]]
+=== Releasing Jetty
+
+There are a number of steps to releasing jetty.
+It is not just limited to running a couple of maven commands and then moving onto bigger and better things.
+There are a number of process related issues once the fun maven bits have been completed.
+
+[[releasing-process]]
+==== Building and Staging a Releasable Version
+
+This release script is for jetty-9 (to release jetty-7 or jetty-8 see older documentation).
+
+1.  Pick your version identification strings.
++
+These follow a strict format and will be used when prompted during step link:#prepare-release-step[listitem_title] below.
++
+....
+
+Release Version                : 9.0.0.v20130322  (v[year][month][day])
+Next Development Version       : 9.0.1-SNAPSHOT
+Tag Name                       : jetty-9.9.0.v20130322
+
+        
+....
+2.  We use the 'release-9' branch to avoid problems with other developers actively working on the master branch.
++
+[source, screen, subs="{sub-order}"]
+....
+
+// Get all of the remotes
+$ git pull origin
+// Create a local tracking branch (if you haven't already)
+$ git branch --track release-9 refs/remotes/origin/release-9
+// Check out your local tracking branch.
+$ git checkout release-9
+// Merge from master into the branch (this becomes your point in time
+// from master that you will be releasing from)
+$ git merge --no-ff master
+
+        
+....
+3.  Update the VERSION.txt with changes from the git logs, this populates the resolves issues since the last release.
++
+[source, screen, subs="{sub-order}"]
+....
+
+$ mvn -N -Pupdate-version        
+
+        
+....
+4.  Edit the VERSION.txt file to set the 'Release Version' at the top alongside the Date of this release.
++
+[source, screen, subs="{sub-order}"]
+....
+
+$ vi VERSION.txt        
+
+        
+....
+5.  Make sure everything is commit'd and pushed to github.com/eclipse/jetty.project
++
+[source, screen, subs="{sub-order}"]
+....
+
+$ git commit -m "Updating VERSION.txt top section" VERSION.txt
+$ git push origin release-9        
+
+        
+....
+6.  Prepare the Release
++
+NOTE: This step updates the <version> elements in the pom.xml files, does a test build with these new versions, and then commits the pom.xml changes to your local git repo.
+The `eclipse-release` profile is required on the prepare in order to bring in the jetty aggregates as that profile defines a module which is ignored otherwise.
++
+[source, screen, subs="{sub-order}"]
+....
+
+$ mvn release:prepare -DreleaseVersion=9.0.0.v20130322 \
+                      -DdevelopmentVersion=9.0.1-SNAPSHOT \
+                      -Dtag=jetty-9.0.0.v20130322 \
+                      -Peclipse-release        
+
+        
+....
+7.  Perform the Release
++
+NOTE: This step performs the release and deploys it to a oss.sonatype.org staging repository.
++
+[source, screen, subs="{sub-order}"]
+....
+
+$ mvn release:perform
+
+        
+....
+8.  Set up files for next development versions.
++
+Edit `VERSION.txt` for 'Next Development Version' at the top.
+Do not date this line.
++
+Make sure everything is commit'd and pushed to github.com/eclipse/jetty.project
++
+[source, screen, subs="{sub-order}"]
+....
+
+$ vi VERSION.txt
+$ git commit -m "Updating VERSION.txt top section" VERSION.txt
+$ git push origin release-9
+
+        
+....
+9.  Close the staging repository on oss.sonatype.org
+10. Announce stage to the mailing list for testing.
+11. Once the staged repository has been approved by the rest of the committers.
++
+* Release the staging repository to maven central on oss.sonatype.org
+* Merge back the changes in release-9 to master
++
+[source, screen, subs="{sub-order}"]
+....
+
+$ git checkout master
+$ git merge --no-ff release-9
+$ git push origin master
+
+            
+....
+
+[[releasing-aggregates]]
+==== Building and Deploying Aggregate Javadoc and Xref
+
+Define the jetty.eclipse.website server entry in your .m2/settings.xml file.
+You'll need to have access to the dev.eclipse.org machine to perform these actions.
+If you don't know if you have access to this then you probably don't and will need to ask a project leader for help.
+
+To build and deploy the aggregate javadoc and jxr bits:
+
+[source, screen, subs="{sub-order}"]
+....
+
+$ cd target/checkout
+$ mvn -Paggregate-site javadoc:aggregate jxr:jxr
+$ mvn -N site:deploy   
+
+    
+....
+
+This will generate the aggregate docs and deploy them to the `/home/www/jetty/<project version>/jetty-project` directory on download.eclipse.org.
+
+[[releasing-distributions]]
+==== Deploying Distribution Files
+
+Since we also provide alternative locations to download jetty distributions we need to copy these into place.
+There are a couple of scripts that will take care of this although they need to be localized to your particular execution environment and you need to have authorization to put stuff where it needs to go.
+These scripts are located at:
+
+* http://git.eclipse.org/c/jetty/org.eclipse.jetty.admin.git/tree/release-scripts.
+
+To localize the scripts to your environment:
+
+* ensure you have "curl" installed
+* edit the scripts and replace all ssh login lines with your own login id
+
+Once these are setup you can deploy a release to eclipse with the following incantation:
+
+[source, screen, subs="{sub-order}"]
+....
+
+$ ./promote-to-eclipse.sh 9.0.0.v20130322
+    
+....
+
+Each of these scripts will download all of the relevant files from maven central and then copy them into the correct location on eclipse infrastructure.
+On the eclipse side of it they will also adjust the xref and javadoc documentation links if they remain broken as well as regenerate all of the html files on the eclipse download site.
+
+[[releasing-stable-links]]
+==== Updating Stable Links
+
+Since we are not allowed to have symbolic links on the download site we have to log into the machine manually and remove the previous stable directory and update it with a new release.
+Maintaining the conventions we use on the site will allow all 'stable' links to be stable and not needed to update to the latest major Jetty build version:
+
+[source, screen, subs="{sub-order}"]
+....
+
+$ ssh <user>@build.eclipse.org
+$ cd ~downloads/jetty/
+$ rm -Rf stable-9
+$ cp -r <version> stable-9
+$ ./index.sh   
+
+    
+....
+
+This needs to be done for all Eclipse Jetty releases (regardless of version). In addition we have to work to reduce the footprint of jetty on the primary eclipse download resources so we want to move older releases to the eclipse archive site.
+
+[source, screen, subs="{sub-order}"]
+....
+
+$ cd ~/downloads/jetty
+$ mv <old release> /home/data/httpd/archive.eclipse.org/jetty/    
+$ ./index.sh   
+
+    
+....
+
+Periodically we need to do the same for the osgi P2 repositories to keep the size of our downloads directory at a reasonable size.
+
+==== Building an OSGi P2 Repository
+
+Most of the jetty jars are also osgi bundles, plus we release some specific bundles that link:#framework-jetty-osgi[integrate jetty closely with osgi].
+To do this, we use a Hudson job on the eclipse infrastructure. You will need to have permission to access https://hudson.eclipse.org/hudson/view/Jetty-RT/
+
+There are Hudson jobs that build osgi p2 repos for each of the major releases of jetty:7 (jetty-rt-bundles-7), 8 (jetty-rt-bundles-8) and 9 (jetty-rt-bundles-9).
+You will need to start a manual build of the job that matches the version of jetty that you are releasing.
+You will be prompted to supply some parameters to the build:
+
+pack_and_sign::
+  By default, this is ticked. Leave it ticked.
+jetty_release-version::
+  Enter the version number of the release, eg 9.2.6.v20141205
+force_context_qualifier::
+  Leave this blank.
+set_pom_version::
+  Enter the major.minor.point release number, eg 9.2.6
+delete_tycho_meta::
+  This is ticked by default. Leave it ticked
+BRANCH_NAME::
+  This is not the branch of the jetty release. Rather it refers to the branch structure of the project that drives the jetty p2 release.
+  It will already be set correctly for the selected job, so don't change it unless you have an extremely good reason.
+
+Once you have supplied the necessary parameters, the build job will commence and the bundles and update site zips will generated and automatically placed in the `/home/data/httpd/downlaod.eclipse.org/jetty/updates/jetty-bundles-[MAJOR.VERSION].x`, where [MAJOR.VERSION] matches the major version number of the jetty release you are doing.
+These files will then be visible from http://download.eclipse.org/jetty/updates/jetty-bundles-[MAJOR.VERSION].x, where [MAJOR.VERSION] corresponds to the major version of the jetty release you are doing.
+
+[[releasing-documentation]]
+==== Release Documentation
+
+There are two git repositories you need to be aware of for releasing jetty-documentation. The jetty-documentation is located in our github repository and the jetty-website is located at eclipse.
+
+jetty-documentation::
+  https://github.com/jetty-project/jetty-documentation
+jetty-website::
+  http://git.eclipse.org/c/www.eclipse.org/jetty.git
+
+Do the following steps to publish documentation for the release:
+
+1.  Checkout the jetty-documentation repository.
+2.  Edit the <version> of the jetty-documentation pom.xml and change it _locally_ to be the release number, eg 9.2.6.v20141205
+3.  Build the documentation with mvn clean install
+4.  Checkout the jetty-website
+5.  Inside the documentation/ directory, make a directory named the same as the release number, eg 9.2.6.v20141205/
+6.  Copy the built `documentation` from jetty-documentation/target/docbkx/html/jetty into the new directory
+7.  Edit the `index.html` file in the `documentation` directory and add the newly released documentation url.
+Make sure you follow the other examples and include the `rel="nofollow"` attribute on the link so that search engines do not crawl newly created documentation, otherwise we are subject to duplicate content penalties in SEO.
+8.  Commit the changes to the jetty-website project
+
+____
+[NOTE]
+There is a separate Jenkins build job that publishes documentation to http://www.eclipse.org/jetty/documentation/current triggered by a push of changed files to the jetty-documentation project.
+If you commit your change to the <version> number from step 2, then these builds will use the same release version number.
+It is preferable if you _don't_ commit that version number change, or better yet, ensure that it is set to the next -SNAPSHOT version number for your jetty major release number.
+____
diff --git a/jetty-documentation/src/main/asciidoc/reference/contributing/security.adoc b/jetty-documentation/src/main/asciidoc/reference/contributing/security.adoc
new file mode 100644
index 0000000..a786460
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/contributing/security.adoc
@@ -0,0 +1,30 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[security-reporting]]
+=== Reporting Security Issues
+
+There are a number of avenues for reporting security issues to the Jetty project available.
+If the issue is directly related to Jetty itself then reporting to the Jetty developers is encouraged.
+The most direct method is to mail _security@webtide.com_.
+Since Webtide is comprised of the active committers of the Jetty project this is our preferred reporting method.
+We are generally flexible in how we work with reporters of security issues but we reserve the right to act in the interests of the Jetty project in all circumstances.
+
+If the issue is related to Eclipse or its Jetty integration then we encourage you to reach out to _security@eclipse.org_.
+
+If the issue is related to integrations with Jetty we are happy to work with you to identify the proper entity and either of the approaches above is fine.
+
+We prefer that security issues are reported directly to Jetty developers as opposed through GitHub Issues since it has no facility to tag issues as _private_.
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/reference/contributing/source-build.adoc b/jetty-documentation/src/main/asciidoc/reference/contributing/source-build.adoc
new file mode 100644
index 0000000..81ddb84
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/contributing/source-build.adoc
@@ -0,0 +1,96 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[contributing-source-build]]
+=== Source Control and Building
+
+If you want to contribute to the development of jetty, you will need to work with a handful of technologies.
+
+[[contributing-source]]
+==== Source Control
+
+Jetty uses several development trunks for its artifacts.
+They are mirrored on github through http://github.com/eclipse, or you can look through them via the Eclipse setup at the URLs below.
+
+===== Primary Interest SCM URLs
+
+These are the URLs to the GIT repositories for the Jetty code.
+They are for people who are working on the Jetty project, as well as for people who are interested in examining or modifying the Jetty code for their own projects.
+
+Jetty Project Repository::
+  https://github.com/eclipse/jetty.project
+
+===== Build and Project Infrastructure SCM URLs
+
+These are the URLs for Jetty-related code and metadata.
+These are not needed to use Jetty; these are primarily of use for people who are working with Jetty-the-project (as opposed to using Jetty-the-server in their own projects).
+
+Administrative pom.xml file::
+  https://github.com/eclipse/jetty.parent
+Build related artifacts that release separately, common assembly descriptors, remote resources, etc.::
+  https://github.com/eclipse/jetty.toolchain
+Files associated with the development of Jetty -- code styles, formatting, iplogs, etc.::
+  http://git.eclipse.org/c/jetty/org.eclipse.jetty.admin.git
+
+==== Build
+
+Jetty requires the use of Java 7 and the latest releases are always recommended to build.
+
+Jetty uses http://maven.apache.org/[Apache Maven 3] for managing its build and primary project metadata.
+
+Building Jetty should simply be a matter of changing into the relevant directory and executing the following commands:
+
+[source, screen, subs="{sub-order}"]
+....
+
+$ git clone https://github.com/eclipse/jetty.project.git
+$ cd jetty.project
+$ mvn install
+
+    
+....
+
+All relevant dependencies will be downloaded into your local repository automatically.
+
+____
+[NOTE]
+Jetty has a great many test cases that run through the course of its build.
+Periodically we find some test cases to be more time dependent than they should be and this results in intermittent test failures.
+You can help track these down by opening a bug report.
+____
+
+==== Import into Eclipse
+
+Jetty is a Maven project. To develop Jetty in Eclipse, follow these directions:
+
+===== Install m2e plugin
+
+1.  From the Eclipse menu at the top of the screen, select _Help > Eclipse Marketplace._
+2.  Search for __m2e__.
+3.  Install the _Maven Integration for Eclipse_
+
+===== Clone the git repository
+
+Using either the egit plugin or git on the commandline (as in the build section above), obtain the jetty source.
+
+===== Import the Maven Projects
+
+1.  From the Eclipse menu, select _File > Import_
+2.  From the Maven folder, select _Existing Maven Projects._
+3.  Click __Next__.
+4.  In the Import Maven projects pane, click _Browse_ and select the top folder of the jetty source tree.
+5.  Click _Next/Finish_ to import all of jetty into Eclipse.
+6.  Wait for Eclipse and m2e to compilie and set up the project.
diff --git a/jetty-documentation/src/main/asciidoc/reference/debugging/chapter.adoc b/jetty-documentation/src/main/asciidoc/reference/debugging/chapter.adoc
new file mode 100644
index 0000000..4a9e58c
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/debugging/chapter.adoc
@@ -0,0 +1,28 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[advanced-debugging]]
+== Debugging
+
+=== Options
+
+Given how flexible Jetty is in how it can be configured and deployed into development and production, there exists a wealth of different options for debugging your application in you favorite environment.
+In this section we will gather up some of these different options available and explain how you can use them.
+If you would like to contribute to this section simply fork the repository and contribute the information, or open a github issue with the information and we'll bring it over.
+
+include::enable-remote-debugging.adoc[]
+include::debugging-with-intellij.adoc[]
+include::debugging-with-eclipse.adoc[]
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/reference/debugging/debugging-with-eclipse.adoc b/jetty-documentation/src/main/asciidoc/reference/debugging/debugging-with-eclipse.adoc
new file mode 100644
index 0000000..785b689
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/debugging/debugging-with-eclipse.adoc
@@ -0,0 +1,57 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[debugging-with-eclipse]]
+=== Debugging With Eclipse
+
+There are a number of options available to debug your application in Eclipse.
+
+If not done already prepare your application for remote debugging as described here: xref:enable-remote-debugging[]
+
+==== Linking with Eclipse
+
+Next we need to link the Eclipse project with the deployed webapp.
+
+1.  Within Eclipse, right-click on the project containing the webapp deployed into jetty and select *Debug -> Debug Configurations* and create a new configuration of **Remote Java Application**.
+Make sure the port you choose is the same as the one you added in xref:enable-remote-debugging[].
++
+image:images/debug-eclipse-1.png[image,width=576]
+
+2.  Next in your webapp you can set a breakpoint within a servlet which when it is tripped will halt the remote jvm's processing thread to await for debugging commands from your Eclipse instance.
++
+image:images/debug-eclipse-2.png[image,width=576]
+
+3.  Accessing that servlet within your browser, pointed at your remote debug configurated jetty-distribution, should transition your Eclipse instance to the standard Debug view.
++
+image:images/debug-eclipse-3.png[image,width=576]
+
+[[eclipse-within-eclipse]]
+==== Within Eclipse
+
+Since Jetty can be incredibly simple to embed, many people choose to create a small `main` method which they can launch directly within Eclipse in order to more easily debug their entire application.
+The best place to get started on this approach is to look through xref:embedding-jetty[] and the xref:embedded-examples[] sections.
+
+Once you have a main method defined in order to launch your application, right-click on the source file and select**Debug As -> Java Application**.
+In your *Console* tab within Eclipse you should see your application startup and once it has completed startup you should be able to configure breakpoints and hit the Jetty instance as normal via your web browser.
+
+____
+[TIP]
+You can easily configure logging through a `jetty-logging.properties`
+file. If this file is on your classpath then Jetty will use it for
+configuring logging, we use this approach extensively throughout Jetty
+development and it makes life ever so much easier. You can see this in
+action in the xref:configuring-jetty-stderrlog[] section.
+____
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/reference/debugging/debugging-with-intellij.adoc b/jetty-documentation/src/main/asciidoc/reference/debugging/debugging-with-intellij.adoc
new file mode 100644
index 0000000..cb66535
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/debugging/debugging-with-intellij.adoc
@@ -0,0 +1,67 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[debugging-with-intellij]]
+=== Debugging With IntelliJ
+
+There are a number of options available to debug your application in IntelliJ.
+
+If not done already prepare your application for remote debugging as described here: xref:enable-remote-debugging[]
+
+==== Linking with IntelliJ
+
+Next we need to link the IntelliJ project with the deployed webapp.
+
+1.  Within IntelliJ, open the project containing the webapp deployed into jetty that you want to debug. Select**Run -> Edit Configurations**.
+Add a new configuration by clicking the "+" icon. Choose **Remote**.
+Make sure the port you choose is the same as the one you added in xref:enable-remote-debugging[].
++
+image:images/intellij_new_remote_config.png[image,width=800]
+
+2.  Next in your webapp you can set a breakpoint within a servlet which when it is tripped will halt the remote jvm's processing thread to await for debugging commands from your IntelliJ instance.
+To set a breakpoint, simply open the servlet or any other class you want to debug and click left to the line you want to set the breakpoint at (where the red dot is on the next screenshot).
+The red dot and red background on the line mark the breakpoint.
++
+image:images/intellij_set_breakpoint.png[image,width=800]
+
+3.  Accessing that servlet within your browser, pointed at your remote debug configured jetty-distribution, should transition your IntelliJ instance to the standard debugger view.
++
+image:images/intellij_debug_view.png[image,width=800]
+
+[[intellij-within-intellij]]
+==== Within IntelliJ
+
+Since Jetty can be incredibly simple to embed, many people choose to create a small `main` method which they can launch directly within IntelliJ in order to more easily debug their entire application.
+The best place to get started on this approach is to look through xref:embedding-jetty[] and the xref:embedded-examples[] sections.
+
+Once you have a main method defined in order to launch your application, open the source file and right-click the main method.
+Select *Debug* or simply hit CTRL+SHIFT+D.
+In your *Console* tab within IntelliJ you should see your application startup and once it has completed startup you should be able to configure breakpoints and hit the Jetty instance as normal via your web browser.
+The same thing works for unit tests.
+Instead of the main method run debug on the test method you want to debug.
+
+image:images/intellij_select_debug.png[image,width=800]
+
+Debugging in IntelliJ is extremely powerful.
+For example it's possible to have conditional breakpoints that only trigger a break if the configured conditions are met.
+Have a look at the various tutorials in the internet or the http://www.jetbrains.com/idea/webhelp/getting-help.html[IntelliJ documentation] for further details.
+
+____
+[TIP]
+You can easily configure logging through a `jetty-logging.properties` file.
+If this file is on your classpath then Jetty will use it for configuring logging, we use this approach extensively throughout Jetty development and it makes life ever so much easier.
+You can see this in action in the xref:configuring-jetty-stderrlog[] section.
+____
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/reference/debugging/enable-remote-debugging.adoc b/jetty-documentation/src/main/asciidoc/reference/debugging/enable-remote-debugging.adoc
new file mode 100644
index 0000000..c151368
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/debugging/enable-remote-debugging.adoc
@@ -0,0 +1,98 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[enable-remote-debugging]]
+=== Enable remote debugging
+
+[[remote-debugging]]
+==== Remote Debugging
+
+If you have a web application deployed into Jetty you can interact with it remotely from a debugging perspective easily.
+The basics are that you must start up the remote JVM with additional parameters and then start up a remote debugging session in Eclipse for the webapp in question.
+This is easily accomplished.
+
+____
+[NOTE]
+This example assumes you are deploying your web application into the jetty-distribution.
+____
+
+===== Starting Jetty
+
+Assuming you have your webapp deployed into jetty, there are two different ways to approach this:
+
+Via command line::
+  Add the required parameters on the commandline like so.
++
+[source, screen, subs="{sub-order}"]
+....
+                            
+$ java -Xdebug -agentlib:jdwp=transport=dt_socket,address=9999,server=y,suspend=n -jar start.jar
+
+                        
+....
+
+Via `start.ini`::
+  This approach is best used if you want to debug a particular jetty-distribution and not have to remember the commandline incantations.
++
+1.  Edit the `start.ini` and uncomment the --exec line, this is required if you are adding jvm options to the start.ini file as jetty-start must generate the classpath required and fork a new jvm.
+2.  Add the parameters mentioned above in the Command Line option so your start.ini looks like this:
++
+[source, plain, subs="{sub-order}"]
+----
+#===========================================================
+# Configure JVM arguments.
+# If JVM args are include in an ini file then --exec is needed
+# to start a new JVM from start.jar with the extra args.
+# If you wish to avoid an extra JVM running, place JVM args
+# on the normal command line and do not use --exec
+#-----------------------------------------------------------
+--exec
+-Xdebug
+-agentlib:jdwp=transport=dt_socket,address=9999,server=y,suspend=n
+# -Xmx2000m
+# -Xmn512m
+# -XX:+UseConcMarkSweepGC
+# -XX:ParallelCMSThreads=2
+# -XX:+CMSClassUnloadingEnabled
+# -XX:+UseCMSCompactAtFullCollection
+# -XX:CMSInitiatingOccupancyFraction=80
+# -verbose:gc
+# -XX:+PrintGCDateStamps
+# -XX:+PrintGCTimeStamps
+# -XX:+PrintGCDetails
+# -XX:+PrintTenuringDistribution
+# -XX:+PrintCommandLineFlags
+# -XX:+DisableExplicitGC
+
+                                    
+----
++
+Uncomment any other jvm environmental options you so desire for your debugging session.
+
+3.  Regardless of the option chosen, you should see the following lines at the top of your jetty-distribution startup.
++
+[source, plain, subs="{sub-order}"]
+----
+Listening for transport dt_socket at address: 9999
+
+----
+
+===== Linking with your IDE
+
+Refer to the documentation for your ide:
+
+* xref:debugging-with-intellij[]
+* xref:debugging-with-eclipse[]
diff --git a/jetty-documentation/src/main/asciidoc/reference/debugging/images/debug-eclipse-1.png b/jetty-documentation/src/main/asciidoc/reference/debugging/images/debug-eclipse-1.png
new file mode 100644
index 0000000..f6ebb05
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/debugging/images/debug-eclipse-1.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/debugging/images/debug-eclipse-2.png b/jetty-documentation/src/main/asciidoc/reference/debugging/images/debug-eclipse-2.png
new file mode 100644
index 0000000..c90e163
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/debugging/images/debug-eclipse-2.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/debugging/images/debug-eclipse-3.png b/jetty-documentation/src/main/asciidoc/reference/debugging/images/debug-eclipse-3.png
new file mode 100644
index 0000000..df7f728
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/debugging/images/debug-eclipse-3.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/debugging/images/intellij_debug_view.png b/jetty-documentation/src/main/asciidoc/reference/debugging/images/intellij_debug_view.png
new file mode 100644
index 0000000..ed034e4
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/debugging/images/intellij_debug_view.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/debugging/images/intellij_new_remote_config.png b/jetty-documentation/src/main/asciidoc/reference/debugging/images/intellij_new_remote_config.png
new file mode 100644
index 0000000..0e031ee
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/debugging/images/intellij_new_remote_config.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/debugging/images/intellij_select_debug.png b/jetty-documentation/src/main/asciidoc/reference/debugging/images/intellij_select_debug.png
new file mode 100644
index 0000000..d53984c
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/debugging/images/intellij_select_debug.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/debugging/images/intellij_set_breakpoint.png b/jetty-documentation/src/main/asciidoc/reference/debugging/images/intellij_set_breakpoint.png
new file mode 100644
index 0000000..fc4eb95
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/debugging/images/intellij_set_breakpoint.png
Binary files differ
diff --git a/jetty-documentation/src/main/asciidoc/reference/faq/chapter.adoc b/jetty-documentation/src/main/asciidoc/reference/faq/chapter.adoc
new file mode 100644
index 0000000..2d9768f
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/faq/chapter.adoc
@@ -0,0 +1,19 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[faq]]
+== Frequently Asked Questions
+
diff --git a/jetty-documentation/src/main/asciidoc/reference/jetty-xml/chapter.adoc b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/chapter.adoc
new file mode 100644
index 0000000..7b8bfad
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/chapter.adoc
@@ -0,0 +1,26 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[reference-section]]
+== Jetty XML Reference
+
+include::jetty-xml-syntax.adoc[]
+include::jetty-xml-usage.adoc[]
+include::jetty-xml-config.adoc[]
+include::jetty-web-xml-config.adoc[]
+include::jetty-env-xml.adoc[]
+include::webdefault-xml.adoc[]
+include::override-web-xml.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-env-xml.adoc b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-env-xml.adoc
new file mode 100644
index 0000000..a082f67
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-env-xml.adoc
@@ -0,0 +1,97 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-env-xml]]
+=== `jetty-env.xml`
+
+`jetty-env.xml` is an optional Jetty file that configures JNDI resources for an individual webapp.
+The format of `jetty-env.xml` is the same as xref:jetty-xml-config[] –it is an XML mapping of the Jetty API.
+
+When Jetty deploys a web application, it automatically looks for a file called ` WEB-INF/jetty-env.xml` within the web application (or WAR), and sets up the webapp naming environment so that naming references in the `WEB-INF/web.xml` file can be resolved from the information provided in the `WEB-INF/jetty-env.xml` and xref:jetty-xml-config[] files.
+You define global naming resources on the server via `jetty.xml`.
+
+[[jetty-env-root-element]]
+==== `jetty-env.xml` Root Element
+
+Jetty applies `jetty-env.xml` on a per-webapp basis, and configures an instance of `org.eclipse.jetty.webapp.WebAppContext.`
+
+[source, xml, subs="{sub-order}"]
+----
+ 
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+ ..
+</Configure>
+
+      
+----
+
+____
+[CAUTION]
+Make sure you are applying the configuration to an instance of the proper class. `jetty-env.xml` configures an instance of WebAppContext, and not an instance of Server.
+____
+
+[[using-jetty-env-xml]]
+==== Using `jetty-env.xml`
+
+Place the `jetty-env.xml` file in your web application's WEB-INF folder.
+
+[source, xml, subs="{sub-order}"]
+----
+ 
+ <?xml version="1.0"?>
+ <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
+ 
+ <Configure class="org.eclipse.jetty.webapp.WebAppContext">
+ 
+   <!-- Add an EnvEntry only valid for this webapp               -->
+   <New id="gargle"  class="org.eclipse.jetty.plus.jndi.EnvEntry">
+     <Arg>gargle</Arg>
+     <Arg type="java.lang.Double">100</Arg>
+     <Arg type="boolean">true</Arg>
+   </New>
+ 
+  <!-- Add an override for a global EnvEntry                           -->
+   <New id="wiggle"  class="org.eclipse.jetty.plus.jndi.EnvEntry">
+     <Arg>wiggle</Arg>
+     <Arg type="java.lang.Double">55.0</Arg>
+     <Arg type="boolean">true</Arg>
+   </New>
+ 
+   <!-- an XADataSource                                                -->
+   <New id="mydatasource99" class="org.eclipse.jetty.plus.jndi.Resource">
+     <Arg>jdbc/mydatasource99</Arg>
+     <Arg>
+       <New class="com.atomikos.jdbc.SimpleDataSourceBean">
+         <Set name="xaDataSourceClassName">org.apache.derby.jdbc.EmbeddedXADataSource</Set>
+         <Set name="xaDataSourceProperties">databaseName=testdb99;createDatabase=create</Set>
+         <Set name="UniqueResourceName">mydatasource99</Set>
+       </New>
+     </Arg>
+   </New>
+
+ </Configure>
+
+      
+----
+
+[[additional-jetty-env-xml-resources]]
+==== Additional `jetty-env.xml` Resources
+
+* xref:jetty-xml-syntax[] –In-depth reference for Jetty-specific configuration XML syntax.
+* xref:jetty-xml-config[] –Configuration file for configuring the entire server.
diff --git a/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-web-xml-config.adoc b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-web-xml-config.adoc
new file mode 100644
index 0000000..30cdfb1
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-web-xml-config.adoc
@@ -0,0 +1,62 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-web-xml-config]]
+=== `jetty-web.xml`
+
+`jetty-web.xml` is a Jetty configuration file that you can bundle with a specific web application.
+The format of `jetty-web.xml` is the same as xref:jetty-xml-config[] – it is an XML mapping of the Jetty API.
+
+This document offers an overview for using the `jetty-web.xml` configuration file.
+For a more in-depth look at the syntax, see xref:jetty-xml-syntax[].
+
+[[root-element-jetty-web-xml]]
+==== Root Element
+
+`jetty-web.xml` applies on a per-webapp basis, and configures an instance of `org.eclipse.jetty.webapp.WebAppContext`.
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+ ..
+</Configure>
+----
+
+____
+[CAUTION]
+Make sure you are applying the configuration to an instance of the proper class. `jetty-web.xml` configures an instance of WebAppContext; `jetty.xml` configures an instance of Server.
+____
+
+[[using-jetty-web-xml]]
+==== Using `jetty-web.xml`
+
+Place the `jetty-web.xml` into your web application's `WEB-INF` folder.
+When Jetty deploys a web application, it looks for a file called `WEB-INF/jetty-web.xml` or `WEB-INF/web-jetty.xml` within the web application (or WAR) and applies the configuration found there.
+Be aware that `jetty-web.xml` is called _after_ all other configuration has been applied to the web application.
+
+[[jetty-web-xml-examples]]
+==== `jetty-web.xml` Examples
+
+The distribution contains an example of `jetty-web.xml` inside the WEB-INF folder of the test webapp war (`$JETTY_HOME/webapps/test.war/WEB-INF/jetty-web.xml`).
+
+[[additional-jetty-web-xml-resources]]
+==== Additional `jetty-web.xml` Resources
+
+* xref:jetty-xml-syntax[] –in-depth reference for Jetty-specific configuration XML syntax.
+* xref:jetty-xml-config[] –configuration file for configuring the entire server
diff --git a/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-xml-config.adoc b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-xml-config.adoc
new file mode 100644
index 0000000..fed25aa
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-xml-config.adoc
@@ -0,0 +1,62 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-xml-config]]
+=== `jetty.xml`
+
+`jetty.xml` is the default configuration file for Jetty, typically located at ` $JETTY_HOME/etc/jetty.xml`. Usually the `jetty.xml` configures:
+
+* The Server class (or subclass if extended) and global options.
+* A ThreadPool (min and max thread).
+* Connectors (ports, timeouts, buffer sizes, protocol).
+* The handler structure (default handlers and/or a contextHandlerCollections).
+* The deployment manager that scans for and deploys webapps and contexts.
+* Login services that provide authentication checking.
+* A request log.
+
+Not all Jetty features are configured in `jetty.xml`.
+There are several optional configuration files that share the same format as `jetty.xml` and, if specified, concatenate to it.
+These configuration files are also stored in `$JETTY_HOME/etc/`, and examples of them are in http://github.com/eclipse/jetty.project/jetty-server/src/main/config/etc/[Github Repository].
+The selection of which configuration files to use is controlled by xref:advanced-start-features[] and the process of merging configuration is described in xref:jetty-xml-usage[].
+
+[[root-element-jetty-xml]]
+==== Root Element
+
+`jetty.xml` configures an instance of the `Jetty org.eclipse.jetty.server.Server.`
+
+[source, xml, subs="{sub-order}"]
+----
+
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+ ...
+</Configure>
+
+      
+----
+
+[[jetty-xml-examples]]
+==== Examples
+
+`$JETTY_HOME/etc` contains the default `jetty.xml`, as well as other sample configuration files (`jetty-*.xml`) which can be passed to the server via the command line.
+
+[[jetty-xml-additional-resources]]
+==== Additional Resources
+
+* xref:jetty-xml-syntax[] –in-depth reference for Jetty-specific configuration XML syntax.
+* xref:jetty-xml-config[] –configuration file for configuring the entire server
diff --git a/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-xml-syntax.adoc b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-xml-syntax.adoc
new file mode 100644
index 0000000..4b371b8
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-xml-syntax.adoc
@@ -0,0 +1,986 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-xml-syntax]]
+=== Jetty XML Syntax
+
+The Jetty XML syntax is a straightforward mapping of XML elements to a Java API so that POJOs can be instantiated and getters, setters, and methods called.
+It is very similar to Inversion Of Control (IOC) or Dependency Injection (DI) frameworks like Spring or Plexus (but it predates all of them).
+Typically Jetty XML is used by `jetty.xml` to configure a Jetty server or by a `context.xml` file to configure a ContextHandler or subclass, but you can also use the mechanism to configure arbitrary POJOs.
+
+This page describes the basic syntax of Jetty XML configuration. See xref:jetty-xml-usage[] for information on how you can use and combine Jetty XML.
+See configuration files for specific examples.
+
+[[basic-xml-configuration-file-example]]
+==== Basic XML Configuration File Example
+
+The following XML configuration file creates some Java objects and sets some attributes:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<Configure id="foo" class="com.acme.Foo">
+  <Set name="name">demo</Set>
+  <Set name="nested">
+    <New id="bar" class="com.acme.Bar">
+      <Arg>true</Arg>
+      <Set name="wibble">10</Set>
+      <Set name="wobble">xyz</Set>
+      <Set name="parent"><Ref refid="foo"/></Set>
+      <Call name="init">
+         <Arg>false</Arg>
+      </Call>
+    </New>
+  </Set>
+
+  <Ref refid="bar">
+    <Set name="wibble">20</Set>
+    <Get name="parent">
+      <Set name="name">demo2</Set>
+    </Get>
+  </Ref>
+</Configure>
+----
+
+The XML above is equivalent to the following Java code:
+
+[source, java, subs="{sub-order}"]
+----
+com.acme.Foo foo = new com.acme.Foo();
+foo.setName("demo");
+
+com.acme.Bar bar = new com.acme.Bar(true);
+bar.setWibble(10);
+bar.setWobble("xyz");
+bar.setParent(foo);
+bar.init(false);
+
+foo.setNested(bar);
+
+bar.setWibble(20);
+bar.getParent().setName("demo2");
+----
+
+==== Overview
+
+===== Understanding DTD and Parsing
+
+The document type descriptor
+(link:{GITBROWSEURL}/jetty-xml/src/main/resources/org/eclipse/jetty/xml/configure_9_0.dtd?h=release-9[configure.dtd]) describes all valid elements in a Jetty XML configuration file using the Jetty IoC format.
+The first two lines of an XML must reference the DTD to be used to validate the XML like:
+
+[source, xml, subs="{sub-order}"]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+...
+----
+
+Typcically a good XML editor will fetch the DTD from the URL and use it to give syntax highlighting and validation while a configuration file is being edited.
+Some editors also allows DTD files to be locally cached.
+The URL may point to configure.dtd if you want the latest current version, or to a specific version like configure_9_0.dtd if you want a particular validation feature set.
+
+Files that conform to the configure.dtd format are processed in Jetty by the `XmlConfiguration` class which may also validate the XML (using a version of the DTD from the classes jar file), but is by default run in a forgiving mode that tries to work around validation failures.
+
+===== Jetty XML Configuration Scope
+
+The configuration of object instances with Jetty IoC XML is done on a scoped basis, so that for any given XML element there is a corresponding Object in scope and the nested XML elements apply to that.
+The outer most scope is given by a Configure element and elements like Call, New and Get establish new scopes.
+The following example uses the name fields to explain the scope.
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="com.example.Foo">
+  <Set name="fieldOnFoo">value</Set>
+  <Set name="fieldOnFoo">
+    <New class="com.example.Bar">
+      <Set name=fieldOnBar>value</Set>
+      <Call name="methodOnBarWithNoArgs"/>
+    </New>
+  </Set>
+
+  <Call name="methodOnFoo">
+    <Arg>value for first arg of methodOnFoo</Arg>
+    <Arg><New class="com.example.Bar"/></Arg>
+    <Set name="fieldOnObjectReturnedByMethodOnFoo">value</Set>
+    <Call name="methodOnObjectReturnedByMethodOnFooWithNoArgs"/>
+  </Call>
+</Configure>
+----
+
+===== Coercing Arguments to a Type
+
+When trying to match XML elements to java elements, Jetty `XmlConfiguration` may need to coerces values to match method arguments.
+By default it does so on a best effort basis, but you can also specify explicit types with the `type` attribute.
+Supported values for type are: `String`, `Character`, `Short`, `Byte`, `Integer`, `Long`, `Boolean`, `Float`, `Double`, `char`, `short`, `byte`, `int`, `long`, `boolean`, `float`, `double`, `URL`, `InetAddress`, `InetAddrPort`, and `void`.
+
+===== Referring to a Class
+
+If you do not specify the classname, Jetty assumes you are calling the method on the object that is current in scope (eg the object of the surrounding `Configure`, `New` or `Get` clause).
+If the class attribute is specified to a fully-qualified class name, then it is either used to create a new instance (`Configure` and `New` elements) or is used to access a static (`Call`, `Set` or `Get` elements).
+
+===== Referring to an Object
+
+You can use the id attribute to store a reference to the current object when first creating or referring to this object.
+You can then use the link:#jetty-xml-ref[Ref element] to reference the object later.
+The ID must be unique for each object you create.
+
+===== Attribute vs Element Style
+
+For XML elements that contain only other XML Elements, there is a choice of using attributes or elements style.
+The following is an example of attribute style:
+
+....
+  <Call id="result" class="org.example.SomeClass" name="someMethod" arg="value0,value1"/>
+....
+
+Attribute style has the benefit of brevity, but is limited by: values can only be Strings; multivalued items can not contain ','; values may not be subject to property expansion or other elements that return values.
+Thus, the more verbose element style is available and the following is semantically equivalent to the attribute style above:
+
+....
+  <Call>
+    <Id>result</Id>
+    <Class>org.example.SomeClass</Class>
+    <Name>someMethod</Name>
+    <Arg>value0</Arg>
+    <Arg>value1</Arg>
+  </Call>
+....
+
+Note that multivalued elements like `Arg` must be repeated and may not be comma-separated like they are when provided as attributes.
+It is possible to use a mix of styles and the following example shows a moretypical example that uses property expansion as the reason for element style:
+
+....
+  <Call id="result" name="someMethod">
+    <Class><Property name="which.class">
+      <Default><Property name="default.class" default="org.example.SomeClass"/>
+    </Property></Class>
+    <Arg>value0</Arg>
+    <Arg>value1</Arg>
+  </Call>
+....
+
+Attributes may not be expressed as elements when their parent element is one that contains data.
+Thus `Arg`, `Item`, `Set`, `Put` and `Get` elements may not have their attributes expressed as elements.
+
+[[jetty-xml-configure]]
+==== <Configure>
+
+This is the root element that specifies the class of object that is to be configured.
+It is usually either the Server, in `jetty.xml`, or a `WebAppContext` in `jetty-web.xml`.
+
+[cols=",,",options="header",]
+|=======================================================================
+|Attribute |Required |Description
+|id |no |A reference to the object that was created. If you define
+multiple link:#jetty-xml-configure[Configure element]s with the same id,
+they will be treated as one object, even if they're in different files.
+You can use this to break up configuration of an object (such as the
+Server) across multiple files.
+
+|class |no |The fully qualified classname of the object to be
+configured. Could be `org.eclipse.jetty.server.Server`,
+`org.eclipse.jetty.webapp.WebAppContext`, a handler, etc.
+|=======================================================================
+
+===== Can Contain
+
+link:#jetty-xml-set[Set element], link:#jetty-xml-get[Get element],
+link:#jetty-xml-put[Put element], link:#jetty-xml-call[Call element],
+link:#jetty-xml-new[New element], link:#jetty-xml-ref[Ref element],
+link:#jetty-xml-array[Array element], link:#jetty-xml-map[Map element],
+link:#jetty-xml-property[Property element]
+
+===== Examples
+
+====== Basic Example
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.server.Server">
+  <Set name="port">8080</Set>
+</Configure>
+----
+
+This is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+org.eclipse.jetty.server.Server server = new org.eclipse.jetty.server.Server();
+server.setPort(8080);
+----
+
+====== Using id to break up configuration of one object across multiple files
+
+In `etc/jetty.xml`:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+  <!-- basic configuration here -->
+</Configure>
+----
+
+In `etc/jetty-logging.xml`:
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+  <!-- assumes that you have the basic server configuration set up; this file only contains additional configuration for logging -->
+</Configure>
+----
+
+Then run the combined configuration using:
+
+....
+java -jar start.jar etc/jetty.xml jetty-logging.xml
+....
+
+[[jetty-xml-set]]
+==== <Set>
+
+A Set element maps to a call to a setter method or field on the current object.
+It can contain text and/or elements such as `Call`, `New`, `SystemProperty`, etc., as values.
+The name and optional type attributes are used to select the setter method.
+If you do not specify a value type, white space is trimmed out of the value.
+If it contains multiple elements as values, they are added as strings before being converted to any specified type.
+
+[cols=",,",options="header",]
+|=======================================================================
+|Attribute |Required |Description
+|name |yes |the name of the setter method to call, or the field to set.
+If the name given is xxx, then a setXxx method is used. If the setXxx
+method cannot be found, then the xxx field is used.
+
+|type |no |the declared type of the argument. See also discussion of
+type for Arg for how to define null and empty string values.
+
+|class |no |if present, then this Set is treated as a static set method
+invocation
+|=======================================================================
+
+===== Can Contain
+
+value text, link:#jetty-xml-get[Get element], link:#jetty-xml-call[Call
+element], link:#jetty-xml-new[New element], link:#jetty-xml-ref[Ref
+element], link:#jetty-xml-array[Array element], link:#jetty-xml-map[Map
+element], link:#jetty-xml-system-property[System Property element],
+link:#jetty-xml-property[Property element]
+
+===== Examples
+
+====== Basic Example
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="server" class="org.eclipse.jetty.server.Server">
+  <Set name="port">8080</Set>
+</Configure>
+----
+
+====== Set via a System Property
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="server" class="org.eclipse.jetty.server.Server">
+  <Set name="port"><SystemProperty name="jetty.http.port" /></Set>
+</Configure>
+----
+
+====== Creating a NewObject and Setting It on the Server
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="server" class="org.eclipse.jetty.server.Server">
+  <Set name="threadPool">
+    <New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
+      <Set name="minThreads">10</Set>
+      <Set name="maxThreads">1000</Set>
+    </New>
+  </Set>
+</Configure>
+----
+
+This is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+org.eclipse.jetty.server.Server server = new org.eclipse.jetty.server.Server();
+
+org.eclipse.jetty.util.thread.QueuedThreadPool threadPool = new org.eclipse.jetty.util.thread.QueuedThreadPool();
+threadPool.setMinThreads(10);
+threadPool.setMaxThreads(1000);
+
+server.setThreadPool(threadPool);
+----
+
+====== Invoking a Static Setter
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="server" class="org.eclipse.jetty.server.Server">
+  <Set class="org.eclipse.jetty.util.log.Log" name="logToParent">loggerName</Set>
+</Configure>
+----
+
+[[jetty-xml-get]]
+==== <Get>
+
+A Get element maps to a call to a getter method or field on the current object.
+It can contain nested elements such as `Set`, `Put`, `Call`, etc.; these act on the object returned by the `Get` call.
+
+[cols=",,",options="header",]
+|=======================================================================
+|Attribute |Required |Description
+|name |yes |the name of the getter method to call, or the field to get.
+If the name given is xxx, then a getXxx method is used. If the getXxx
+method cannot be found, then the xxx field is used.
+
+|class |no |f present, then this Get is treated as a static getter or
+field.
+
+|id |no |if present, then you can use this id to refer to the returned
+object later.
+|=======================================================================
+
+===== Can Contain
+
+link:#jetty-xml-set[Set element], link:#jetty-xml-get[Get element],
+link:#jetty-xml-put[Put element], link:#jetty-xml-call[Call element],
+link:#jetty-xml-new[New element], link:#jetty-xml-ref[Ref element],
+link:#jetty-xml-array[Array element], link:#jetty-xml-map[Map element],
+link:#jetty-xml-property[Property element]
+
+===== Examples
+
+====== Basic Example
+
+This simple example doesn't do much on its own.
+You would normally use this in conjunction with a `<Ref id="Logger" />`.
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="server" class="org.eclipse.jetty.server.Server">
+  <Get id="Logger" class="org.eclipse.jetty.util.log.Log" name="log"/>
+</Configure>
+----
+
+====== Invoking a Static Getter and Call Methods on the Returned Object
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="server" class="org.eclipse.jetty.server.Server">
+    <Get class="java.lang.System" name="out">
+      <Call name="println">
+        <Arg>Server version is: <Get class="org.eclipse.jetty.server.Server" name="version"/></Arg>
+      </Call>
+    </Get>
+</Configure>
+----
+
+[[jetty-xml-put]]
+==== <Put>
+
+A Put element maps to a call to a put method on the current object, which must implement the Map interface.
+It can contain text and/or elements such as `Call`, `New`, `SystemProperty`, etc. as values.
+If you do not specify a no value type, white space is trimmed out of the value.
+If it contains multiple elements as values, they are added as strings before being converted to any specified type.
+
+[cols=",,",options="header",]
+|=======================================================================
+|Attribute |Required |Description
+|name |yes |used as the put key
+
+|type |no |forces the type of the value. See also discussion of type for
+Arg for how to define null and empty string values.
+|=======================================================================
+
+===== Can Contain
+
+value text, link:#jetty-xml-get[Get element], link:#jetty-xml-call[Call
+element], link:#jetty-xml-new[New element], link:#jetty-xml-ref[Ref
+element], link:#jetty-xml-array[Array element], link:#jetty-xml-map[Map
+element], link:#jetty-xml-system-property[System Property element],
+link:#jetty-xml-property[Property element]
+
+===== Example
+
+[source, xml, subs="{sub-order}"]
+----
+<Get name="someKindOfMap">
+   <Put name="keyName">objectValue</Put>
+</Get>
+----
+
+[[jetty-xml-call]]
+==== <Call>
+
+A `Call` element maps to an arbitrary call to a method on the current object.
+It can contain a sequence of Arg elements followed by a sequence of configuration elements, such as Set, Put, Call.
+The <Arg>s are passed as arguments to the method; the sequence of configuration elements act on the object returned by the original call.
+
+[cols=",,",options="header",]
+|=======================================================================
+|Attribute |Required |Description
+|name |yes |the name of the arbitrary method to call. The method called
+will use the exact name you provide it.
+
+|class |no |if present, then this Call is treated as a static method.
+
+|id |no |if present, you can use this id to refer to any object returned
+by the call, for later use.
+
+|arg |no |comma separated list of arguments may be used for simple
+string values rather than Arg elements
+|=======================================================================
+
+===== Can Contain
+
+Attributes as elements (Id, Name, Class) plus link:#jetty-xml-arg[Arg
+element], link:#jetty-xml-set[Set element], link:#jetty-xml-get[Get
+element], link:#jetty-xml-put[Put element], link:#jetty-xml-call[Call
+element], link:#jetty-xml-new[New element], link:#jetty-xml-ref[Ref
+element], link:#jetty-xml-array[Array element], link:#jetty-xml-map[Map
+element], link:#jetty-xml-property[Property element]
+
+===== Examples
+
+====== Basic example
+
+[source, xml, subs="{sub-order}"]
+----
+<Call name="doFoo">
+  <Arg>bar</Arg>
+  <Set name="test">1, 2, 3</Set>
+</Call>
+----
+
+This is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+Object o2 = o1.doFoo("bar");
+o2.setTest("1, 2, 3");
+----
+
+====== Invoking a static method
+
+[source, xml, subs="{sub-order}"]
+----
+<Call class="com.acme.Foo" name="setString">
+  <Arg>somestring</Arg>
+</Call>
+----
+
+Which is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+com.acme.Foo.setString("somestring");
+----
+
+====== Invoking the Actual MethodInstead of Relying on Getter/Setter Magic
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+  <Call name="getPort" id="port" />
+  <Call class="com.acme.Environment" name="setPort">
+    <Arg>
+      <Ref refid="port"/>
+    </Arg>
+  </Call>
+</Configure>
+----
+
+Which is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+org.mortbay.jetty.Server server = new org.mortbay.jetty.Server();
+com.acme.Environment.setPort( server.getPort() );
+----
+
+[[jetty-xml-arg]]
+==== <Arg>
+
+An Arg element can be an argument of either a method or a constructor.
+Use it within xref:jetty-syntax-call[] and xref:jetty-syntax-new[].
+
+It can contain text and/or elements, such as `Call`, `New`, `SystemProperty`, etc., as values.
+The optional type attribute can force the type of the value.
+If you don't specify a type, white space is trimmed out of the value.
+If it contains multiple elements as values, they are added as strings before being converted to any specified type.
+Simple `String` arguments can also be specified as a string separated arg attribute on the parent element.
+
+[cols=",,",options="header",]
+|=======================================================================
+|Attribute |Required |Description
+|type |no |force the type of the argument. If you do not provide a value
+for the element, if you use type of "String", the value will be the
+empty string (""), otherwise it is null.
+|=======================================================================
+
+===== Can Contain
+
+value text, link:#jetty-xml-get[Get element], link:#jetty-xml-call[Call
+element], link:#jetty-xml-new[New element], link:#jetty-xml-ref[Ref
+element], link:#jetty-xml-array[Array element], link:#jetty-xml-map[Map
+element], link:#jetty-xml-system-property[System Property element],
+link:#jetty-xml-property[Property element]
+
+===== Examples
+
+====== Basic examples
+
+[source, xml, subs="{sub-order}"]
+----
+<Arg>foo</Arg> <!-- String -->
+<Arg>true</Arg> <!-- Boolean -->
+<Arg>1</Arg> <!-- int, long, short, float, double -->
+<Arg><Ref refid="foo" /></Arg>  <!-- any object; reference a previously created object with id "foo", and pass it as a parameter -->
+<Arg></Arg> <!-- null value -->
+<Arg type="String"></Arg> <!-- empty string "" -->
+----
+
+====== Coercing Type
+
+This explicitly coerces the type to a boolean:
+
+[source, xml, subs="{sub-order}"]
+----
+<Arg type="boolean">False</Arg>
+----
+
+====== As a Parameter
+
+Here are a couple of examples of link:#jetty-xml-arg[Arg element] being used as a parameter to methods and to constructors:
+
+[source, xml, subs="{sub-order}"]
+----
+<Call class="com.acme.Environment" name="setFoo">
+  <Arg>
+    <New class="com.acme.Foo">
+      <Arg>bar</Arg>
+    </New>
+  </Arg>
+</Call>
+----
+
+This is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+com.acme.Environment.setFoo(new com.acme.Foo("bar"));
+----
+
+[source, xml, subs="{sub-order}"]
+----
+<New class="com.acme.Baz">
+  <Arg>
+    <Call id="bar" class="com.acme.MyStaticObjectFactory" name="createObject">
+      <Arg>2</Arg>
+    </Call>
+  </Arg>
+</New>
+----
+
+This is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+new com.acme.Baz(com.acme.MyStaticObjectFactory.createObject(2));
+----
+
+[[jetty-xml-new]]
+==== <New>
+
+Instantiates an object.
+Equivalent to `new` in Java, and allows the creation of a new object.
+A `New` element can contain a sequence of link:#jetty-xml-arg[`Arg` element]'s, followed by a sequence of configuration elements (`Set`, `Put`, etc).
+link:#jetty-xml-arg[`Arg` element]'s are used to select a constructor for the object to be created.
+The sequence of configuration elements then acts on the newly-created object.
+
+[cols=",,",options="header",]
+|=======================================================================
+|Attribute |Required |Description
+|class |yes |fully qualified classname, which determines the type of the
+new object that is instantiated.
+
+|id |no |gives a unique name to the object which can be referenced later
+by Ref elements.
+
+|arg |no |comma separated list of arguments may be used for simple
+string values rather than Arg elements
+|=======================================================================
+
+===== Can Contain
+
+Attributes as elements (Id, Class) plus link:#jetty-xml-arg[Arg
+element], link:#jetty-xml-set[Set element], link:#jetty-xml-get[Get
+element], link:#jetty-xml-put[Put element], link:#jetty-xml-call[Call
+element], link:#jetty-xml-new[New element], link:#jetty-xml-ref[Ref
+element], link:#jetty-xml-array[Array element], link:#jetty-xml-map[Map
+element], link:#jetty-xml-property[Property element]
+
+===== Examples
+
+====== Basic example
+
+[source, xml, subs="{sub-order}"]
+----
+<New class="com.acme.Foo">
+  <Arg>bar</Arg>
+</New>
+----
+
+Which is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+com.acme.Foo foo = new com.acme.Foo("bar");
+----
+
+====== Instantiate with the Default Constructor
+
+[source, xml, subs="{sub-order}"]
+----
+<New class="com.acme.Foo" />
+----
+
+Which is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+com.acme.Foo foo = new com.acme.Foo();
+----
+
+====== Instantiate with Multiple Arguments, Then Configuring Further
+
+[source, xml, subs="{sub-order}"]
+----
+<New id="foo" class="com.acme.Foo">
+   <Arg>bar</Arg>
+   <Arg>baz</Arg>
+   <Set name="test">1, 2, 3</Set>
+ </New>
+----
+
+Which is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+Object foo = new com.acme.Foo("bar", "baz");
+foo.setTest("1, 2, 3");
+----
+
+[[jetty-xml-ref]]
+==== <Ref>
+
+A `Ref` element allows a previously created object to be referenced by a unique id.
+It can contain a sequence of elements, such as `Set` or `Put` which then act on the referenced object.
+You can also use a `Ref` element as a value for other elements such as `Set` and `Arg`.
+
+The `Ref` element provides convenience and eases readability.
+You can usually achieve the effect of the `Ref` by nesting elements (method calls), but this can get complicated very easily.
+The Ref element makes it possible to refer to the same object if you're using it multiple times, or passing it into multiple methods.
+It also makes it possible to split up configuration across multiple files.
+
+[cols=",,",options="header",]
+|=======================================================================
+|Attribute |Required |Description
+|refid |yes |the unique identifier used to name a previously created
+object.
+|=======================================================================
+
+===== Can Contain
+
+link:#jetty-xml-set[Set element], link:#jetty-xml-get[Get element],
+link:#jetty-xml-put[Put element], link:#jetty-xml-call[Call element],
+link:#jetty-xml-new[New element], link:#jetty-xml-ref[Ref element],
+link:#jetty-xml-array[Array element], link:#jetty-xml-map[Map element],
+link:#jetty-xml-property[Property element]
+
+===== Examples
+
+====== Basic example
+
+Use the referenced object as an argument to a method call or constructor:
+
+[source, xml, subs="{sub-order}"]
+----
+<Get id="foo" name="xFoo" />
+<Set name="test"><Ref refid="foo"/></Set>
+----
+
+This is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+foo = getXFoo();
+setSomeMethod(foo);
+----
+
+====== Manipulating the Object Returned by Ref
+
+[source, xml, subs="{sub-order}"]
+----
+<Get id="foo" name="xFoo" />
+<Ref refid="foo">
+  <Set name="test">1, 2, 3</Set>
+</Ref>
+----
+
+This is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+foo = getXFoo();
+foo.setTest("1, 2, 3");
+----
+
+====== Ref vs. Nested Elements
+
+Here is an example of the difference in syntax between using the `Ref` element, and nesting method calls.
+They are exactly equivalent:
+
+[source, xml, subs="{sub-order}"]
+----
+<!-- using Ref in conjunction with Get -->
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+  <Get id="Logger" class="org.eclipse.jetty.util.log.Log" name="log"/>
+  <Ref refid="Logger">
+    <Set name="debugEnabled">true</Set>
+  </Ref>
+</Configure>
+<!-- calling the setter directly on the object returned by Get -->
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+  <Get class="org.eclipse.jetty.util.log.Log" name="log">
+    <Set name="debugEnabled">true</Set>
+  </Get>
+</Configure>
+----
+
+Here is a more practical example, taken from the handler configuration section in `etc/jetty.xml`:
+
+[source, xml, subs="{sub-order}"]
+----
+<Set name="handler">
+  <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
+    <Set name="handlers">
+      <Array type="org.eclipse.jetty.server.Handler">
+        <Item>
+          <!-- create a new instance of a ContextHandlerCollection named "Contexts" -->
+          <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
+        </Item>
+        <Item>
+          <New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
+        </Item>
+        <Item>
+          <!-- create a new instance of a RequestLogHandler named "RequestLog" -->
+          <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
+        </Item>
+      </Array>
+    </Set>
+  </New>
+</Set>
+
+<Call name="addBean">
+  <Arg>
+    <New class="org.eclipse.jetty.deploy.ContextDeployer">
+      <!-- pass in the ContextHandlerCollection object ("Contexts") that was created earlier, as an argument -->
+      <Set name="contexts"><Ref refid="Contexts"/></Set>
+    </New>
+  </Arg>
+</Call>
+
+<!-- configure the RequestLogHandler object ("RequestLog") that we created earlier -->
+<Ref refid="RequestLog">
+  ....
+</Ref>
+----
+
+[[jetty-xml-array]]
+==== <Array>
+
+An `Array` element allows the creation of a new array.
+
+[cols=",,",options="header",]
+|==================================================================
+|Attribute |Required |Description
+|type |no |specify what types of items the array can contain.
+|id |no |unique identifier you can use to refer to the array later.
+|==================================================================
+
+===== Can Contain
+
+link:#jetty-xml-item[Item element]
+
+===== Example
+
+[source, xml, subs="{sub-order}"]
+----
+<Array type="java.lang.String">
+   <Item>value0</Item>
+   <Item><New class="java.lang.String"><Arg>value1</Arg></New></Item>
+</Array>
+----
+
+This is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+String[] a = new String[] { "value0", new String("value1") };
+----
+
+[[jetty-xml-item]]
+==== <Item>
+
+An `Item` element defines an entry for Array and Map elements.
+
+[cols=",,",options="header",]
+|=======================================================================
+|Attribute |Required |Description
+|type |no |force the types of value.
+|id |no |unique identifier that you can use to refer to the array later.
+|=======================================================================
+
+===== Can Contain
+
+link:#jetty-xml-get[Get element], link:#jetty-xml-call[Call element],
+link:#jetty-xml-new[New element], link:#jetty-xml-ref[Ref element],
+link:#jetty-xml-array[Array element], link:#jetty-xml-map[Map element],
+link:#jetty-xml-system-property[System Property element],
+link:#jetty-xml-property[Property element]
+
+[[jetty-xml-map]]
+==== <Map>
+
+A `Map` element allows the creation of a new HashMap and to populate it with `(key, value)` pairs.
+
+[cols=",,",options="header",]
+|================================================================
+|Attribute |Required |Description
+|id |no |unique identifier you can use to refer to the map later.
+|================================================================
+
+===== Can Contain
+
+link:#jetty-xml-entry[Entry element]
+
+===== Example
+
+[source, xml, subs="{sub-order}"]
+----
+<Map>
+  <Entry>
+    <Item>keyName</Item>
+    <Item><New class="java.lang.String"><Arg>value1</Arg></New></Item>
+  </Entry>
+</Map>
+----
+
+This is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+Map m = new HashMap();
+m.put("keyName", new String("value1"));
+----
+
+[[jetty-xml-entry]]
+==== <Entry>
+
+An `Entry` element contains a key-value link:#jetty-xml-item[Item element] pair for a `Map`.
+
+===== Can Contain
+
+link:#jetty-xml-item[Item element]
+
+[[jetty-xml-system-property]]
+==== <SystemProperty>
+
+A `SystemProperty` element gets the value of a JVM system property.
+It can be used within elements that accept values, such as `Set`, `Put`, `Arg`.
+
+[cols=",,",options="header",]
+|=======================================================================
+|Attribute |Required |Description
+|name |yes |property name
+
+|default |no |a default value as a fallback
+
+|id |no |unique identifier which you can use to refer to the array
+later.
+|=======================================================================
+
+===== Can Contain
+
+Only attributes as Elements (`Id`, `Name`, `Default`).
+
+===== Example
+
+[source, xml, subs="{sub-order}"]
+----
+<SystemProperty name="jetty.http.port" default="8080"/>
+----
+
+That is equivalent to:
+
+[source, java, subs="{sub-order}"]
+----
+System.getProperty("jetty.http.port", "8080");
+----
+
+Both try to retrieve the value of `jetty.http.port`.
+If `jetty.http.port` is not set, then 8080 is used.
+
+[[jetty-xml-property]]
+==== <Property>
+
+A `Property` element allows arbitrary properties to be retrieved by name.
+It can contain a sequence of elements, such as `Set`, `Put`, `Call` that act on the retrieved object.
+
+[cols=",,",options="header",]
+|=======================================================================
+|Attribute |Required |Description
+|name |yes |property name
+
+|default |no |a default value as a fallback
+
+|id |no |unique identifier which you can use to refer to the array
+later.
+|=======================================================================
+
+The `Name` attribute may be a comma separated list of property names, with the first property name being the "official" name, and the others names being old, deprecated property names that are kept for backward compatibility.
+A warning log is issued when deprecated property names are used.
+The `Default` attribute contains the value to use in case none of the property names is found.
+
+===== Can Contain
+
+The attributes may be expressed as contained Elements (`Id`, `Name`, `Default`).
+
+===== Example
+
+[source, xml, subs="{sub-order}"]
+----
+<Property name="Server">
+  <Call id="jdbcIdMgr" name="getAttribute">
+    <Arg>jdbcIdMgr</Arg>
+  </Call>
+</Property>
+----
diff --git a/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-xml-usage.adoc b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-xml-usage.adoc
new file mode 100644
index 0000000..767a656
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/jetty-xml-usage.adoc
@@ -0,0 +1,71 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jetty-xml-usage]]
+=== Jetty XML Usage
+
+Jetty provides an XML-based configuration.
+It is grounded in Java's Reflection API. Classes in the `java.lang.reflect` represent Java methods and classes, such that you can instantiate objects and invoke their methods based on their names and argument types.
+Behind the scenes, Jetty's XML config parser translates the XML elements and attributes into Reflection calls.
+
+[[using-jettyxml]]
+==== Using `jetty.xml`
+
+To use `jetty.xml`, specify it as a configuration file when running Jetty.
+
+[source, java, subs="{sub-order}"]
+----
+ java -jar start.jar etc/jetty.xml
+----
+
+____
+[NOTE]
+If you start Jetty without specifying a configuration file, Jetty automatically locates and uses the default installation `jetty.xml` file.
+Therefore `java -jar start.jar` is equivalent to `java -jar start.jar etc/jetty.xml` .
+____
+
+[[using-multiple-configuration-files]]
+==== Using Multiple Configuration Files
+
+You are not limited to one configuration file; you can use multiple configuration files when running Jetty, and Jetty will configure the appropriate server instance.
+The ID of the server in the `<Configure>` tag specifies the instance you want to configure.
+Each server ID in a configuration file creates a new server instance within the same JVM.
+If you use the same ID across multiple configuration files, those configurations are all applied to the same server.
+
+[[setting-parameters-in-configuration-files]]
+==== Setting Parameters in Configuration Files
+
+You can set parameters in configuration files either with system properties (using `<SystemProperty>`) or properties files (using `<Property>`) passed via the command line.
+For example, this code in `jetty.xml` allows the port to be defined on the command line, falling back onto `8080`if the port is not specified:
+
+[source, xml, subs="{sub-order}"]
+----
+  <Set name="port"><SystemProperty name="jetty.http.port" default="8080"/></Set>
+----
+
+Then you modify the port while running Jetty by using this command:
+
+[source, java, subs="{sub-order}"]
+----
+ java -Djetty.http.port=8888 -jar start.jar etc/jetty.xml
+----
+
+An example of defining both system properties and properties files from the command line:
+
+[source, java, subs="{sub-order}"]
+----
+ java -Djetty.http.port=8888 -jar start.jar myjetty.properties etc/jetty.xml etc/other.xml
+----
diff --git a/jetty-documentation/src/main/asciidoc/reference/jetty-xml/override-web-xml.adoc b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/override-web-xml.adoc
new file mode 100644
index 0000000..e3f0be6
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/override-web-xml.adoc
@@ -0,0 +1,93 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[override-web-xml]]
+=== Jetty `override-web.xml`
+
+To deploy a web application or WAR into different environments, most likely you will need to customize the webapp for compatibility with each environment.
+The challenge is to do so without changing the webapp itself. You can use a `jetty.xml` file for some of this work since it is not part of the webapp.
+But there are some changes that `jetty.xml` cannot accomplish, for example, modifications to servlet init-params and context init-params.
+Using `webdefault.xml` is not an option because Jetty applies `webdefault.xml` to a web application _before_ the application's own `WEB-INF/web.xml`, which means that it cannot override values inside the webapp's ` web.xml`.
+
+The solution is `override-web.xml`.
+It is a `web.xml` file that Jetty applies to a web application _after_ the application's own `WEB-INF/web.xml`, which means that it can override values or add new elements.
+This is defined on a per-webapp basis, using the xref:jetty-xml-syntax[].
+
+[[using-override-web-xml]]
+==== Using override-web.xml
+
+You can specify the `override-web.xml` to use for an individual web application in a deployable xml file located in Jetty webapps folder .
+For example, if you had a webapp named MyApp, you would place a deployable xml file named `myapp.xml` in `${jetty.base}/webapps` which includes an `overrideDescriptor` entry for the `override-web.xml` file.
+
+[source, xml, subs="{sub-order}"]
+----
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  ...
+  <!-- Set up the path to the custom override descriptor,
+  relative to your $(jetty.home) directory or to the current directory -->
+  <Set name="overrideDescriptor"><SystemProperty name="jetty.home" default="."/>/my/path/to/override-web.xml</Set>
+  ...
+</Configure>
+----
+
+The equivalent in code is:
+
+[source, java, subs="{sub-order}"]
+----
+import org.eclipse.jetty.webapp.WebAppContext;
+
+    ...
+
+    WebAppContext wac = new WebAppContext();
+    ...
+    //Set the path to the override descriptor, based on your $(jetty.home) directory
+    wac.setOverrideDescriptor(System.getProperty("jetty.home")+"/my/path/to/override-web.xml");
+    ...
+----
+
+Alternatively, you can use the classloader (xref:jetty-classloading[]) to get the path to the override descriptor as a resource.
+
+[[override-using-jetty-maven-plugin]]
+==== Using the Jetty Maven Plugin
+
+Use the `<overrideDescriptor>` tag as follows:
+
+[source, xml, subs="{sub-order}"]
+----
+<project>
+    ...
+    <plugins>
+        <plugin>
+            ...
+            <artifactId>jetty-maven-plugin</artifactId>
+            <configuration>
+                <webAppConfig>
+                  ...
+                  <overrideDescriptor>src/main/resources/override-web.xml</overrideDescriptor>
+                </webAppConfig>
+            </configuration>
+        </plugin>
+        ...
+    </plugins>
+    ...
+</project>
+----
+
+[[override-web-xml-additional-resources]]
+==== Additional Resources
+
+* xref:webdefault-xml[] –Information for this `web.xml` -formatted file, applied before the webapp's `web.xml` webapp.
+* xref:jetty-xml-config[] –Reference for `jetty.xml` files
diff --git a/jetty-documentation/src/main/asciidoc/reference/jetty-xml/webdefault-xml.adoc b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/webdefault-xml.adoc
new file mode 100644
index 0000000..6a785c6
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/jetty-xml/webdefault-xml.adoc
@@ -0,0 +1,116 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[webdefault-xml]]
+=== `webdefault.xml`
+
+The `webdefault.xml` file saves web applications from having to define a lot of house-keeping and container-specific elements in their own `web.xml` files.
+For example, you can use it to set up mime-type mappings and JSP servlet-mappings.
+Jetty applies `webdefault.xml` to a web application _before_ the application's own `WEB-INF/web.xml`, which means that it cannot override values inside the webapp's `web.xml`.
+It uses the xref:jetty-xml-config[] syntax.
+Generally, it is convenient for all webapps in a Jetty instance to share the same `webdefault.xml` file.
+However, it is certainly possible to provide differentiated ` webdefault.xml` files for individual web applications.
+
+The `webdefault.xml` file is located in `$(jetty.home)/etc/webdefault.xml`.
+
+[[using-webdefault-xml]]
+==== Using `webdefault.xml`
+
+You can specify a custom configuration file to use for specific webapps, or for all webapps. If you do not specify an alternate defaults descriptor, the $JETTY-HOME/etc/jetty-deploy.xml file will configure jetty to automatically use $JETTY_HOME/etc/`webdefault.xml`.
+
+[[creating-custom-webdefault-xml-one-webapp]]
+===== Creating a Custom `webdefault.xml` for One WebApp
+
+You can specify a custom `webdefault.xml` for an individual web application in that webapp's xref:jetty-xml-config[] as follows:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+  ...
+  <!-- Set up the absolute path to the custom webdefault.xml -->
+  <Set name="defaultsDescriptor">/my/path/to/webdefault.xml</Set>
+  ...
+</Configure>
+
+
+----
+
+The equivalent in code is:
+
+[source, java, subs="{sub-order}"]
+----
+
+import org.eclipse.jetty.webapp.WebAppContext;
+
+    ...
+
+    WebAppContext wac = new WebAppContext();
+    ...
+    //Set up the absolute path to the custom webdefault.xml.
+    wac.setDefaultsDescriptor("/my/path/to/webdefault.xml");
+    ...
+
+
+----
+
+Alternatively, you can use a xref:jetty-classloading[] to find the resource representing your custom `webdefault.xml`.
+
+[[creating-custom-webdefault-xml-multiple-webapps]]
+===== Creating a Custom `webdefault.xml` for Multiple WebApps
+
+If you want to apply the same custom `webdefault.xml` to a number of webapps, provide the path to the file in xref:jetty-xml-config[] in the $JETTY_HOME/etc/jetty-deploy.xml file:
+
+[source, xml, subs="{sub-order}"]
+----
+   <Set name="defaultsDescriptor"><Property name="jetty.home" default="." />/other/path/to/another/webdefault.xml</Set>
+----
+
+[[webdefault-xml-using-jetty-maven-plugin]]
+===== Using the Jetty Maven Plugin
+
+Similarly, when using the link:#jetty-maven-plugin[Jetty Maven Plugin] you provide a customized `webdefault.xml` file for your webapp as follows:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<project>
+    ...
+    <plugins>
+        <plugin>
+            ...
+            <artifactId>jetty-maven-plugin</artifactId>
+            <configuration>
+                <webApp>
+                  ...
+                  <defaultsDescriptor>/my/path/to/webdefault.xml</defaultsDescriptor>
+                </webApp>
+            </configuration>
+        </plugin>
+        ...
+    </plugins>
+    ...
+</project>
+
+
+----
+
+[[webdefault-xml-additional-resources]]
+===== Additional Resources
+
+* xref:jetty-web-xml-config[] –Reference for `web.xml` files
+* xref:override-web-xml[] –Information for this `web.xml` -formatted file, applied after the webapp's `web.xml` webapp.
+* xref:jetty-xml-config[] –Reference for `jetty.xml` files
diff --git a/jetty-documentation/src/main/asciidoc/reference/part.adoc b/jetty-documentation/src/main/asciidoc/reference/part.adoc
new file mode 100644
index 0000000..d55cbd4
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/part.adoc
@@ -0,0 +1,26 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+// #TODO Remove chapter in Jetty 10
+[[jetty-ref-guide]]
+
+= Jetty Reference Guide
+
+include::architecture/chapter.adoc[]
+include::platforms/chapter.adoc[]
+include::jetty-xml/chapter.adoc[]
+include::troubleshooting/chapter.adoc[]
+include::debugging/chapter.adoc[]
+include::contributing/chapter.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/reference/platforms/chapter.adoc b/jetty-documentation/src/main/asciidoc/reference/platforms/chapter.adoc
new file mode 100644
index 0000000..44a5dc5
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/platforms/chapter.adoc
@@ -0,0 +1,34 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[platforms]]
+== Platforms, Stacks and Alternative Distributions
+
+=== Many many options...
+
+In addition to using Jetty in its distribution form and its multiple embedded forms, there are a number of alternative ways to use Jetty.
+Many products and open source projects out there distribute Jetty themselves, in both distribution and embedded forms, not to mention different operating systems bundling Jetty in other installable forms.
+
+If your platform supports Jetty from a distribution or deployment perspective and want to be included on this list just fork the documentation and submit a pull request, or contact us.
+Check out our list of http://www.eclipse.org/jetty/powered[Powered By] page for software that makes use of Jetty, often in novel and exciting ways.
+
+include::jelastic.adoc[]
+include::cloudfoundry.adoc[]
+include::elastic-beanstalk.adoc[]
+include::fedora.adoc[]
+include::ubuntu.adoc[]
+
+
diff --git a/jetty-documentation/src/main/asciidoc/reference/platforms/cloudfoundry.adoc b/jetty-documentation/src/main/asciidoc/reference/platforms/cloudfoundry.adoc
new file mode 100644
index 0000000..767e7af
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/platforms/cloudfoundry.adoc
@@ -0,0 +1,141 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[cloudfoundry]]
+=== CloudFoundry
+
+____
+[WARNING]
+This is an increasingly aged integration, things like likely changed enough this is not directly useful but may serve as a useful starting point should someone want to look into it.
+____
+
+[[cloudfoundry-overview]]
+==== Overview
+
+http://www.cloudfoundry.com[CloudFoundry] is an open platform intended as a place to deploy end user applications in a manner which is both simple and eminently scalable to fit the needs of the application.
+With the release of their V2 framework the Jetty project has created a buildpack which allows you to deploy your java based web application onto Jetty and still make use of the remainder of the CloudFoundry platform.
+
+This buildpack itself is quite simple to use.
+A collection of ruby scripting and the buildpack conventions will allow Jetty to be downloaded, configured and customized to your needs and then have your web application deployed onto it.
+While the default buildpack we have created is useful to deploy a stock configuration of jetty, it is quite likely that you will want to fork the buildpack and tweak it to fit your immediate needs.
+This process is made trivial since buildpacks install from a github repository.
+For example, to change the jetty version simply fork it in GitHub and tweak the `JETTY_VERSION` string in the `jetty_web.rb` file.
+
+If you have additional modifications to make to the Jetty server, like perhaps configuring additional static contexts, setting up a proxy servlet, adding jar files to the jetty home/lib/ext directory, etc you can either adapt the ruby scripting directly or place them under the appropriate location in the `/resources` directory of this buildpack and they will be copied into the correct location.
+
+For the time being I'll leave this buildpack under my personal github account and should there be interest expressed I am more then happy to push it over to https://github.com/jetty-project down the road for proper contributions, etc.
+
+[[cloudfoundry-usage]]
+==== Usage
+
+To show how incredibly easy it is to use the Jetty buildpack with cloudfoundry, this is all the more you need to do to deploy your application.
+Refer to the CloudFoundry http://docs.cloudfoundry.com/[documentation] to get started, get the `cf` utilities installed and an environment configured.
+
+[source, screen, subs="{sub-order}"]
+....
+$ cf push snifftest --buildpack=git://github.com/jmcc0nn3ll/jetty-buildpack.git
+
+....
+
+____
+[TIP]
+In this example the web application is uploaded from the *current* directory so make sure you have changed directory into the root of your web application.
+The `snifftest` on the commandline referrs to what you are calling the application, not the directory to deploy.
+Also note that the webapplication is installed into the `ROOT` context of Jetty as is available at the root context of the server.
+Any additional web applications will have to be configured within the buildpack as mentioned above.
+____
+
+You will be prompted to answer a series of questions describing the execution environment and any additional services you need enabled (databases, etc).
+
+[source, plain, subs="{sub-order}"]
+----
+
+Instances> 1
+
+Custom startup command> none
+
+1: 64M
+2: 128M
+3: 256M
+4: 512M
+5: 1G
+Memory Limit> 256M
+
+Creating snifftest... OK
+
+1: snifftest
+2: none
+Subdomain> snifftest
+
+1: a1-app.cf-app.com
+2: none
+Domain> a1-app.cf-app.com
+
+Binding snifftest.a1-app.cf-app.com to snifftest... OK
+
+Create services for application?> n
+
+Save configuration?> n
+
+      
+----
+
+Once answered you will see the installation process of your application.
+
+[source, plain, subs="{sub-order}"]
+----
+
+Uploading snifftest... OK
+Starting snifftest... OK
+-> Downloaded app package (4.0K)
+Initialized empty Git repository in /tmp/buildpacks/jetty-buildpack.git/.git/
+Installing jetty-buildpack.git.
+Downloading JDK...
+Copying openjdk-1.7.0_21.tar.gz from the buildpack cache ...
+Unpacking JDK to .jdk
+Downloading Jetty: jetty-distribution-{VERSION}.tar.gz
+Downloading jetty-distribution-{VERSION}.tar.gz from http://repo2.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.0.3.v20130506/ ...
+Unpacking Jetty to .jetty
+-> Uploading staged droplet (36M)
+-> Uploaded droplet
+Checking snifftest...
+Staging in progress...
+Staging in progress...
+Staging in progress...
+Staging in progress...
+Staging in progress...
+Staging in progress...
+  0/1 instances: 1 starting
+  0/1 instances: 1 starting
+  0/1 instances: 1 starting
+  0/1 instances: 1 starting
+  1/1 instances: 1 running
+OK
+
+----
+
+The application is now available at the configured location! Under the url `http://snifftest.a1-app.cf-app.com/` in this particular example.
+
+[[cloudfoundry-acknowledgements]]
+==== Acknowledgements
+
+The Jetty buildpack was forked from the CloudFoundry Java buildpack. The Virgo Buildpack that Glyn worked on was used as a sanity check.
+
+* http://github.com/cloudfoundry/cloudfoundry-buildpack-java
+* http://github.com/glyn/virgo-buildpack
+
+CloudFoundry buildpacks were modelled on Heroku buildpacks.
+
diff --git a/jetty-documentation/src/main/asciidoc/reference/platforms/elastic-beanstalk.adoc b/jetty-documentation/src/main/asciidoc/reference/platforms/elastic-beanstalk.adoc
new file mode 100644
index 0000000..07b9f4d
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/platforms/elastic-beanstalk.adoc
@@ -0,0 +1,86 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[elastic-beanstalk]]
+=== Amazon Elastic Beanstalk
+
+____
+[WARNING]
+This is an increasingly aged integration, things like likely changed enough this is not directly useful but may serve as a useful starting point should someone want to look into it.
+____
+
+
+http://aws.amazon.com/elasticbeanstalk/[Elastic Beanstalk] is a component with the http://aws.amazon.com[Amazon Web Services] offering that allows you to configure an entire virtual machine based on one of several available baseline configurations and then customize it through a powerful configuration system. While the default offerings currently available are based on Tomcat for for the java community, we worked out the basics using that configuration system to enable the usage of Jetty instead.
+
+[[elastic-beanstalk-overview]]
+==== Overview
+
+Elastic beanstalk has a very http://aws.amazon.com/about-aws/whats-new/2012/10/02/introducing-aws-elastic-beanstalk-configuration-files/[powerful configuration mechanism] so this integration taps into that to effectively rework the tomcat configuration and replace it with the bits required to make jetty run in its place. Below is a walk through of what the various configuration files are doing and how the general flow of configuration on beanstalk happens.
+
+There is an `.ebextensions` directory in your beanstalk application which contains all of the files requires to configure and customize your beanstalk and application combo.
+Files that end in .config in this directory are processed in alphabetical order.
+
+00-java7.config::
+  installs java 7 onto the beanstalk environment and makes it the   default
+10-tweak.config::
+  not required, but changes the `/opt/elasticbeanstalk` directory to be readable making debugging easier
+11-jetty.config::
+  installs jetty9 into `/opt/jetty-9` and removes unneeded distribution files
+12-beanstalk.config::
+  handles replacing tomcat with jetty in many configuration files, configures logging and wires up system startup processes.
+  Some files in your `.ebextensions` directory are moved to replace files under   /opt/elasticbeanstalk.
+
+If you look in the `.ebextensions` directory of your application you should also see other jetty specific xml and ini files.
+The final config file handles these as they are largely customization for your application.
+
+20-testapp.config::
+  layers application specific configuration files into the jetty installation
+
+The files in our example test webapp here enable various OPTIONS for libraries that need to be loaded, customize the root application being deployed and even deploy additional contexts like we do in our jetty distribution demo.
+This is also the mechanism that you would use to wire up application specific things, for example if you needed additional software installed, customized directories made, etc.
+
+[[elastic-beanstalk-maven]]
+==== Maven Bits
+
+Support for this feature leverages Maven to make things easy and is composed of three different modules.
+
+jetty-beanstalk-overlay::
+  This is the collection of scripts that are required to wedge jetty into the normal beanstalk setup.
+  This module is intended to extract into an webapp to enable it for beanstalk usage with jetty.
+jetty-beanstalk-resources::
+  This generates an artifact of files that are downloaded by the configuration process and contains replacements for certain beanstalk files as well as various system level jetty configuration files like an updated `jetty.sh` script for the `/etc/init.d` setup.
+jetty-beanstalk-testapp::
+  An example webapp that shows both how to combine the war file from another maven module with the jetty-beanstalk-overlay to produce a beanstalk enabled application bundle.
+  Also included is examples of how to alter the jetty configuration for things like a customized
+  `start.ini` file.
+
+____
+[NOTE]
+The test webapps needs access to a snapshot version of the test-jetty-webapp so it really serves as more of an example of how to layer your webapp with the bits required to customize your app for beanstalk and jetty.
+____
+
+To actually make use of these artifacts you currently must clone this git repository and build it locally.
+Once you have the artifacts you simply need to copy the approach in the jetty-beanstalk-testapp to apply the configuration to your webapp.
+
+* https://github.com/jmcc0nn3ll/jetty-beanstalk
+
+____
+[IMPORTANT]
+Bluepill is used to manage the start and stop process of the app server.
+This seems to be a problematic bit of software with a colored history and the version in use at the time of this writing is old.
+When starting and stopping (or restarting) the appserver you may see error messages show up that the Server timed out getting a response or things like that.
+These are red herrings and my experience is that jetty has started and stopped just fine, the pid file required shows up in a very timely fashion (under `/var/run/jetty.pid`) so do check that the app server has started, but please be aware there is a strangeness here that hasn't been sorted out yet.
+____
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/reference/platforms/fedora.adoc b/jetty-documentation/src/main/asciidoc/reference/platforms/fedora.adoc
new file mode 100644
index 0000000..e194c69
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/platforms/fedora.adoc
@@ -0,0 +1,24 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[fedora]]
+=== Fedora
+
+As of Fedora 19, Jetty 9 is the version of Jetty available.
+This distribution of Jetty is not created or maintained by the Jetty project though we have had a fair amount of communication with the folks behind it and we are very pleased with how this Linux distribution has stayed current.
+Releases are kept largely in sync with our releases as there is a wonderful automatic notification mechanism in place for Fedora that detects our releases and immediately opens an issue for them to update.
+
+* https://admin.fedoraproject.org/pkgdb/acls/name/jetty[Jetty on Fedora]
diff --git a/jetty-documentation/src/main/asciidoc/reference/platforms/jelastic.adoc b/jetty-documentation/src/main/asciidoc/reference/platforms/jelastic.adoc
new file mode 100644
index 0000000..2b43c4b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/platforms/jelastic.adoc
@@ -0,0 +1,24 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[jelastic]]
+=== Jelastic
+
+Jelastic is a wonderful place to host your applications with solid support for Jetty.
+As a cloud hosting platform they take the majority of configuration and installation details out of the picture and focus on letting you focus on your web application.
+
+* http://jelastic.com/why[Why Jelastic?]
+* http://jelastic.com/jetty-hosting[Jetty Hosting]
diff --git a/jetty-documentation/src/main/asciidoc/reference/platforms/ubuntu.adoc b/jetty-documentation/src/main/asciidoc/reference/platforms/ubuntu.adoc
new file mode 100644
index 0000000..413b243
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/platforms/ubuntu.adoc
@@ -0,0 +1,23 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[ubuntu]]
+=== Ubuntu
+
+Currently there are no actual `.deb` files available for installing on Debian based Linux machines but there is a handy blog that as been largely been kept up to date on the steps involved through the comments.
+
+* http://pietervogelaar.nl/ubuntu-12-04-install-jetty-9/[Install Jetty9
+on Ubuntu]
diff --git a/jetty-documentation/src/main/asciidoc/reference/troubleshooting/chapter.adoc b/jetty-documentation/src/main/asciidoc/reference/troubleshooting/chapter.adoc
new file mode 100644
index 0000000..7739464
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/troubleshooting/chapter.adoc
@@ -0,0 +1,26 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[troubleshooting]]
+== Troubleshooting
+
+This is a collection of helpful tricks and tips that we have come across to address odd issues that might arise.
+
+include::troubleshooting-zip-exceptions.adoc[]
+include::troubleshooting-locked-files.adoc[]
+include::preventing-memory-leaks.adoc[]
+include::slow-deployment.adoc[]
+include::security-reports.adoc[]
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/reference/troubleshooting/preventing-memory-leaks.adoc b/jetty-documentation/src/main/asciidoc/reference/troubleshooting/preventing-memory-leaks.adoc
new file mode 100644
index 0000000..eca61ff
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/troubleshooting/preventing-memory-leaks.adoc
@@ -0,0 +1,161 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[preventing-memory-leaks]]
+=== Preventing Memory Leaks
+
+If you have memory leaks, and you have thoroughly investigated tools like jconsole, yourkit, jprofiler, jvisualvm or any of the other profiling and analysis tools, and you can eliminate your code as the source of the problem, read the following sections about how to prevent memory leaks in your application.
+
+[[preventing-webapp-classloader-pinning]]
+==== Preventing WebApp Classloader Pinning
+
+____
+[NOTE]
+This feature is available for Jetty 7.6.6 and later.
+____
+
+Code that keeps references to a webapp classloader can cause memory leaks.
+These leaks fall generally into two categories: static fields and daemon threads.
+
+* A static field is initialized with the value of the classloader, which happens to be a webapp classloader; as Jetty undeploys and redeploys the webapp, the static reference lives on, meaning garbage collecting cannot occur for the webapp classloader.
+* When Jetty starts as a daemon thread and is outside the lifecycle of the webapp, threads have references to the context classloader that created them, leading to a memory leak if that classloader belongs to a webapp.
+For a good discussion of the issue see http://cdivilly.wordpress.com/tag/sun-awt-appcontext/[Anatomy of a PermGen Memory Leak.]
+
+We provide a number of link:{JDURL}//org/eclipse/jetty/util/preventers/package-summary.html[workaround classes] that preemptively invoke the problematic code with the Jetty classloader, thereby ensuring the webapp classloader is not pinned.
+Be aware that since some of the problematic code creates threads, you should be selective about which preventers you enable, and use only those that are specific to your application.
+
+[[preventers-table]]
+===== Preventers
+
+Jetty includes the following preventers.
+
+[cols=",",options="header",]
+|=======================================================================
+|Preventer Name |Problem Addressed
+|AppContextLeakPreventer |The call to `AppContext.getAppContext()` keeps a static reference to the context classloader. The JRE can invoke AppContext in many different places.
+
+|AWTLeakPreventer |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 with the thread context class loader.
+See https://issues.jboss.org/browse/AS7-3733[JBoss bug AS7-3733.]
+
+|DOMLeakPreventer |DOM parsing can cause the webapp classloader to be pinned, due to the static field ` RuntimeException` of `com.sun.org.apache.xerces.internal.parsers.AbstractDOMParser.` http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6916498[Oracle bug 6916498] specifically mentions that a heap dump might not identify the GCRoot as the uncollected loader, making it difficult to identify the cause of the leak.
+
+|DriverManagerLeakPreventer |The number of threads dedicated to accepting incoming connections.
+
+|GCThreadLeakPreventer |Calls to `sun.misc.GC.requestLatency` create a daemon thread that keeps a reference to 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[Stackoverflow: Does java garbage collection log entry 'Full GC system' mean some class
+called System.gc()?]
+
+|Java2DLeakPreventer |`sun.java2d.Disposer` keeps a reference to the classloader.
+See https://issues.apache.org/bugzilla/show_bug.cgi?id=51687[ASF bug 51687.]
+
+|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 starts and keeps a reference to the context classloader.
+
+|LoginConfigurationLeakPreventer |The `javax.security.auth.login.Configuration` class keeps a static reference to the thread context classloader.
+
+|SecurityProviderLeakPreventer |Some security providers, such as `sun.security.pkcs11.SunPKCS11` start a deamon thread that traps the thread context classloader.
+|=======================================================================
+
+[[configuring-preventers]]
+===== Configuring Preventers
+
+You can individually enable each preventer by adding an instance to a Server with the ` addBean(Object)` call. Here's an example of how to do it in code with the `org.eclipse.jetty.util.preventers.AppContextLeakPreventer`:
+
+[source, java, subs="{sub-order}"]
+----
+
+Server server = new Server();
+server.addBean(new AppContextLeakPreventer());
+
+        
+----
+
+You can add the equivalent in code to the `$JETTY_HOME/etc/jetty.xml` file or any jetty xml file that is configuring a Server instance.
+Be aware that if you have more than one Server instance in your JVM, you should configure these preventers on just _one_ of them.
+Here's the example from code put into xml:
+
+[source, xml, subs="{sub-order}"]
+----
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+   <Call name="addBean">
+    <Arg>
+      <New class="org.eclipse.jetty.util.preventers.AppContextLeakPreventer"/>
+    </Arg>
+   </Call>
+
+</Configure>
+
+        
+----
+
+[[jsp-bugs]]
+==== JSP Bugs: Permgen Problems
+
+The JSP engine in Jetty is Jasper.
+This was originally developed under the Apache Tomcat project, but over time many different project have forked it.
+All Jetty versions up to 6 used Apache-based Jasper exclusively, with Jetty 6 using Apache Jasper only for JSP 2.0.
+With the advent of JSP 2.1, Jetty 6 switched to using Jasper from Sun's https://glassfish.java.net/[Glassfish] project, which is now the reference implementation.
+
+All forks of Jasper suffer from a problem whereby using JSP tag files puts the permgen space under pressure.
+This is because of the classloading architecture of the JSP implementation.
+Each JSP file is effectively compiled and its class loaded in its own classloader to allow for hot replacement.
+Each JSP that contains references to a tag file compiles the tag if necessary and then loads it using its own classloader.
+If you have many JSPs that refer to the same tag file, the tag's class is loaded over and over again into permgen space, once for each JSP.
+See http://java.net/jira/browse/GLASSFISH-3963[Glassfish bug 3963] and https://issues.apache.org/bugzilla/show_bug.cgi?id=43878[Apache bug 43878.]
+The Apache Tomcat project has already closed this bug with status WON'T FIX, however the Glassfish folks still have the bug open and have scheduled it to be fixed.
+When the fix becomes available, the Jetty project will pick it up and incorporate into our release program.
+
+[[jvm-bugs]]
+==== JVM Bugs
+
+This section describes garbage collection and direct ByteBuffer problems.
+
+[[jvm-garbage-collection-problems]]
+===== Garbage Collection Problems
+
+One symptom of a cluster of JVM related memory issues is the OOM exception accompanied by a message such as `java.lang.OutOfMemoryError: requested xxxx bytes for xxx.
+Out of swap space?`
+
+http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4697804[Oracle bug 4697804] describes how this can happen in the scenario when the garbage collector needs to allocate a bit more space during its run and tries to resize the heap, but fails because the machine is out of swap space.
+One suggested work around is to ensure that the JVM never tries to resize the heap, by setting min heap size to max heap size:
+
+[source,text]
+----
+
+java -Xmx1024m -Xms1024m
+
+        
+----
+
+Another workaround is to ensure you have configured sufficient swap space on your device to accommodate all programs you are running concurrently.
+
+[[direct-byte-buffers]]
+===== Direct ByteBuffers
+
+Exhausting native memory is another issue related to JVM bugs.
+The symptoms to look out for are the process size growing, but heap use remaining relatively constant.
+Both the JIT compiler and nio ByteBuffers can consume native memory.
+http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6210541[Oracle bug 6210541] discusses a still-unsolved problem whereby the JVM itself allocates a direct ByteBuffer in some circumstances while the system never garbage collects, effectively eating native memory.
+Guy Korland's blog discusses this problem http://www.jroller.com/gkorland/entry/java_s_memory_isn_t[here] and http://www.jroller.com/gkorland/entry/java_s_memory_managment_is[here.]
+As the JIT compiler consumes native memory, the lack of available memory may manifest itself in the JIT as OutOfMemory exceptions such as `Exception in thread "CompilerThread0" java.lang.OutOfMemoryError: requested xxx bytes for ChunkPool::allocate. Out of swap
+      space?`
+
+By default, Jetty allocates and manages its own pool of direct ByteBuffers for io if you configure the nio SelectChannelConnector.
+It also allocates MappedByteBuffers to memory-map static files via the DefaultServlet settings.
+However, you could be vulnerable to this JVM ByteBuffer allocation problem if you have disabled either of these options.
+For example, if you're on Windows, you may have disabled the use of memory-mapped buffers for the static file cache on the DefaultServlet to avoid the file-locking problem.
diff --git a/jetty-documentation/src/main/asciidoc/reference/troubleshooting/security-reports.adoc b/jetty-documentation/src/main/asciidoc/reference/troubleshooting/security-reports.adoc
new file mode 100644
index 0000000..39d340c
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/troubleshooting/security-reports.adoc
@@ -0,0 +1,120 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[security-reports]]
+=== Jetty Security Reports
+
+The following sections provide information about Jetty security issues.
+
+If you would like to report a security issue please follow these link:#security-reporting[instructions].
+
+.Resolved Issues
+[width="99%",cols="11%,19%,14%,9%,14%,14%,19%",options="header",]
+|=======================================================================
+|yyyy/mm/dd |ID |Exploitable |Severity |Affects |Fixed Version |Comment
+|2016/05/31 |CVE-2016-4800 |high |high |>= 9.3.0, < = 9.3.8 |9.3.9
+|http://www.ocert.org/advisories/ocert-2016-001.html[Alias vulnerability allowing access to protected resources within a webapp on Windows.]
+
+|2015/02/24 |CVE-2015-2080 |high |high |>=9.2.3 <9.2.9 |9.2.9
+|http://blog.gdssecurity.com/labs/2015/2/25/jetleak-vulnerability-remote-leakage-of-shared-buffers-in-je.html[JetLeak exposure of past buffers during HttpParser error]
+
+|2013/11/27 |http://en.securitylab.ru/lab/PT-2013-65[PT-2013-65] |medium
+|high |>=9.0.0 <9.0.5 |9.0.6
+https://bugs.eclipse.org/bugs/show_bug.cgi?id=418014[418014] |Alias checking disabled by NTFS errors on Windows.
+
+|2013/07/24
+|https://bugs.eclipse.org/bugs/show_bug.cgi?id=413684[413684] |low
+|medium |>=7.6.9 <9.0.5 |7.6.13,8.1.13,9.0.5
+https://bugs.eclipse.org/bugs/show_bug.cgi?id=413684[413684]
+|Constraints bypassed if Unix symlink alias checker used on Windows.
+
+|2011/12/29
+|http://www.ocert.org/advisories/ocert-2011-003.html[CERT2011-003] http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2011-4461[CVE-2011-4461]
+|high |medium |All versions |7.6.0.RCO
+https://bugs.eclipse.org/bugs/show_bug.cgi?id=367638[Jetty-367638]
+|Added ContextHandler.setMaxFormKeys (intkeys) to limit the number of parameters (default 1000).
+
+|2009/11/05
+|http://www.kb.cert.org/vuls/id/120541[CERT2011-003] http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3555[CERT2011-003]
+|medium |high |JVM<1.6u19 |jetty-7.01.v20091125, jetty-6.1.22 |Work
+around by turning off SSL renegotiation in Jetty. If using JVM > 1.6u19
+setAllowRenegotiate(true) may be called on connectors.
+
+|2009/06/18 |http://jira.codehaus.org/browse/JETTY-1042[Jetty-1042] |low
+|high |< = 6.1.18, < = 7.0.0.M4 |6.1.19, 7.0.0.Rc0 |Cookie leak between
+requests sharing a connection.
+
+|2009/04/30 |http://www.kb.cert.org/vuls/id/402580[CERT402580] |medium
+|high |< = 6.1.16, < = 7.0.0.M2 a|
+5.1.15, 6.1.18, 7.0.0.M2
+
+http://jira.codehaus.org/browse/JETTY-1004[Jetty-1004]
+
+ |View arbitrary disk content in some specific configurations.
+
+|2007/12/22
+|http://www.kb.cert.org/vuls/id/553235[CERT553235] http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2007-6672[CVE-2007-6672]
+|high |medium |6.1.rrc0-6.1.6 a|
+6.1.7
+
+http://jira.codehaus.org/browse/JETTY-386[CERT553235]
+
+ |Static content visible in WEB-INF and past security constraints.
+
+|2007/11/05
+|http://www.kb.cert.org/vuls/id/438616[CERT438616] http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-5614[CVE-2007-5614]
+|low |low |<6.1.6 |6.1.6rc1 (patch in CVS for jetty5) |Single quote in
+cookie name.
+
+|2007/11/05
+|http://www.kb.cert.org/vuls/id/237888[CERT237888>] http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-5613[CVE-2007-5613]
+|low |low |<6.1.6 |6.1.6rc0 (patch in CVS for jetty5) |XSS in demo dup
+servlet.
+
+|2007/11/03 |http://www.kb.cert.org/vuls/id/212984[CERT212984
+>] http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-5615[CVE-2007-5615]
+|medium |medium |<6.1.6 |6.1.6rc0 (patch in CVS for jetty5) |CRLF
+Response splitting.
+
+|2006/11/22
+|http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-6969[CVE-2006-6969]
+|low |high |<6.1.0, <6.0.2, <5.1.12, <4.2.27 |6.1.0pre3, 6.0.2, 5.1.12,
+4.2.27 |Session ID predictability.
+
+|2006/06/01
+|http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-2759[CVE-2006-2759]
+|medium |medium |<6.0.*, <6.0.0Beta17 |6.0.0Beta17 |JSP source
+visibility.
+
+|2006/01/05 | |medium |medium |<5.1.10 |5.1.10 |Fixed //security
+constraint bypass on Windows.
+
+|2005/11/18
+|http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-2758[CVE-2006-2758]
+|medium |medium |<5.1.6 |5.1.6, 6.0.0Beta4 |JSP source visibility.
+
+|2004/02/04 |JSSE 1.0.3_01 |medium |medium |<4.2.7 |4.2.7 |Upgraded JSSE
+to obtain downstream security fix.
+
+|2002/09/22 | |high |high |<4.1.0 |4.1.0 |Fixed CGI servlet remove
+exploit.
+
+|2002/03/12 | |medium | |<3.1.7 |4.0.RC2, 3.1.7 |Fixed // security
+constraint bypass.
+
+|2001/10/21 |medium | |high |<3.1.3 |3.1.3 |Fixed trailing null security
+constraint bypass.
+|=======================================================================
diff --git a/jetty-documentation/src/main/asciidoc/reference/troubleshooting/slow-deployment.adoc b/jetty-documentation/src/main/asciidoc/reference/troubleshooting/slow-deployment.adoc
new file mode 100644
index 0000000..e3b4009
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/troubleshooting/slow-deployment.adoc
@@ -0,0 +1,49 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[troubleshooting-slow-deployment]]
+=== Troubleshooting Slow Deployment
+
+After upgrading to a version of Jetty that supports Servlet Spec 3.0 or above, enabling some new modules, or introducing some new jars to your webapp, you notice that your deployment time is increased.
+This could be due to scanning for classes caused by a ServletContainerInitializer.
+
+As documented in the section on link:#using-annotations[Using Annotations], even if your webapp has set `metadata-complete=true` in web.xml, all jars within your webapp may still be scanned due to one or more ServletContainerInitializers that have a http://docs.oracle.com/javaee/6/api/javax/servlet/annotation/HandlesTypes.html[&#64;HandlesTypes] annotation listing the names of classes in which it is interested.
+
+There are 3 ways to speed up deployment time:
+
+* limit which ServletContainerInitializers to include
+* limit which jars to scan
+* limit the scan to the first deployment only
+
+==== Remedies
+
+===== Limit Which ServletContainerInitializers to Execute
+
+As documented in the section link:#excluding-scis[Excluding ServletContainerInitializers], you can provide a context attribute that defines a pattern of ServletContainerInitializer (SCI) class names to ignore.
+These SCIs will not be examined for http://docs.oracle.com/javaee/6/api/javax/servlet/annotation/HandlesTypes.html[&#64;HandlesTypes] and will not be executed.
+This is useful if you have included a 3rd party jar that has a SCI on which your code does not rely.
+
+===== Limit Which Jars to Scan
+
+As documented in the section link:#jars-scanned-for-annotations[Jars Scanned for Annotations], you can explicitly define which jars to include in the scanning process.
+This is helpful if you have a lot of jars in your webapp, and you know that they do not contain any classes referenced by an @HandlesTypes annotation on a ServletContainerInitializer that will be executed.
+
+===== Limit Scanning to First Deployment Only (Quickstart)
+
+The link:#quickstart-webapp[quickstart mechanism] will do a normal deployment - obeying any limits on SCIs and jars to scan as documented here - the first time the webapp is deployed only.
+Subsequent deployments will re-use the information discovered during the first deployment.
+This is useful if you cannot limit the scan significantly by using any of the mechanisms described here, but you don't want to incur the cost of scanning on every redeployment.
+The link:#quickstart-webapp[quickstart mechanism] and how to use it is described link:#quickstart-webapp[here].
diff --git a/jetty-documentation/src/main/asciidoc/reference/troubleshooting/troubleshooting-locked-files.adoc b/jetty-documentation/src/main/asciidoc/reference/troubleshooting/troubleshooting-locked-files.adoc
new file mode 100644
index 0000000..495f29a
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/troubleshooting/troubleshooting-locked-files.adoc
@@ -0,0 +1,124 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[troubleshooting-locked-files-on-windows]]
+=== Troubleshooting Locked Files on Windows
+
+Jetty buffers static content for webapps such as HTML files, CSS files, images, etc.
+If you are using NIO connectors, Jetty uses memory-mapped files to do this.
+The problem is that on Windows, memory mapping a file causes the file to lock, so that you cannot update or replace the file.
+Effectively this means that you have to stop Jetty to update a file.
+
+==== Remedy
+
+Jetty provides a configuration switch in the `webdefault.xml` file for the DefaultServlet that enables or disables the use of memory-mapped files.
+If you are running on Windows and are having file-locking problems, you should set this switch to disable memory-mapped file buffers.
+
+The default `webdefault.xml` file is found in the jetty distribution under the `etc/` directory or in the `jetty-webapp-${VERSION}.jar` artifact at `org/eclipse/jetty/webapp/webdefault.xml`.
+Edit the file in the distribution or extract it to a convenient disk location and edit it to change `useFileMappedBuffer` to false.
+The easiest option is to simply edit the default file contained in the jetty distribution itself.
+
+[source, xml, subs="{sub-order}"]
+----
+<init-param>
+   <param-name>useFileMappedBuffer</param-name>
+   <param-value>true</param-value> <!-- change to false -->
+ </init-param>
+ 
+    
+----
+
+Make sure to apply your custom `webdefault.xml` file to all of your webapps.
+You can do that by changing the configuration of the Deployment Manager in `etc/jetty-deploy.xml`.
+
+[source, xml, subs="{sub-order}"]
+----
+<Call id="webappprovider" name="addAppProvider">
+  <Arg>
+    <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
+      .
+      .
+      <!-- this should be the new custom webdefault.xml or change should be made in this file -->
+      <Set name="defaultsDescriptor"><Property name="jetty.home" default="." />/etc/webdefault.xml</Set>
+      <Set name="scanInterval">1</Set>
+      <Set name="extractWars">true</Set>
+      .
+      .
+    </New>
+  </Arg>
+</Call>
+
+    
+----
+
+Alternatively, if you have individually configured your webapps with context xml files, you need to call the `WebAppContext.setDefaultsDescriptor(String path)` method:
+
+[source, xml, subs="{sub-order}"]
+----
+<New id="myWebAppContext"  class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/</Set>
+  <Set name="war">./webapps/fredapp</Set>
+  <Set name="defaultsDescriptor">/home/fred/jetty/mywebdefaults.xml</Set>
+  .
+  .
+</New>
+
+    
+----
+
+Instead, you could redefine the DefaultServlet in your web.xml file, making sure to set useFileMappedBuffer to false:
+
+[source, xml, subs="{sub-order}"]
+----
+<web-app ...>
+ ...
+ <servlet>
+     <servlet-name>Default</servlet-name>
+     <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
+     <init-param>
+       <param-name>useFileMappedBuffer</param-name>
+       <param-value>false</param-value>
+     </init-param>
+     <load-on-startup>0</load-on-startup>
+   </servlet>
+ ...
+ </web-app>
+ 
+    
+----
+
+==== Alternate Remedy
+
+You can force a `WebAppContext` to always copy a web app directory on deployment.
+The base directory of your web app (ie the root directory where your static content exists) will be copied to the link:#ref-temporary-directories[temp directory].
+Configure this in an xml file like so:
+
+[source, xml, subs="{sub-order}"]
+----
+<New id="myWebAppContext"  class="org.eclipse.jetty.webapp.WebAppContext">
+  <Set name="contextPath">/</Set>
+  <Set name="war">./webapps/fredapp</Set>
+  <Set name="copyWebDir">true</Set>
+  .
+  .
+</New>
+    
+----
+
+____
+[NOTE]
+Be careful with this option when using an explicitly setlink:#ref-temp-directories[temp directory] name - as the name of the temp directory will not unique across redeployments, copying the static content into the same directory name each time may not avoid the locking problem.
+____
\ No newline at end of file
diff --git a/jetty-documentation/src/main/asciidoc/reference/troubleshooting/troubleshooting-zip-exceptions.adoc b/jetty-documentation/src/main/asciidoc/reference/troubleshooting/troubleshooting-zip-exceptions.adoc
new file mode 100644
index 0000000..93006bc
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/troubleshooting/troubleshooting-zip-exceptions.adoc
@@ -0,0 +1,42 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[troubleshooting-zip-exceptions]]
+=== Troubleshooting Zip Exceptions
+
+A Zip exception occurs when Jetty rereads a Jar or WAR file.
+
+The JVM maintains a cache of zip file indexes, and does not support hot replacement of zip files.
+Thus if you redeploy a web application using the same WAR or Jar files, exceptions occur when Jetty rereads the jars.
+See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4774421[Oracle Bug 4774421] for more information.
+
+[[remedy]]
+==== Remedy
+
+The remedy is to avoid hot replacing Jar or WAR files, which can be difficult if you are using the
+link:#configuring-specific-webapp-deployment[Webapp Provider].
+You can use the following techniques to reduce exposure to this issue:
+
+* Deploy unpacked classes in the `WEB-INF/classes` directory rather than as a Jar file under `WEB-INF/lib`.
+* Deploy all WAR and Jar files with a version number in their filename or path.
+If the code changes, a new version number applies, avoiding the cache problem.
+* Deploy a packed WAR file with the link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setExtractWAR(boolean)[setExtractWAR] option set to true.
+This causes the WAR to be extracted to a link:#ref-temporary-directories[temporary directory] and thus to a new location.
+This might not be sufficient if you want to hot-replace and re-extract the WAR, so you might also need to use link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setCopyWebInf(boolean)[WebAppContext.setCopyWebInf(true)], which (re)copies just the WEB-INF directory to a different location.
+* Deploy an unpacked WAR file with the link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setCopyWebDir(boolean)[setCopyWebDir] option set to true.
+This causes the directory to be extracted to a new location.
+
+If you have problems with link:#troubleshooting-locked-files-on-windows[Windows file-locking] preventing static file editing (such as JSP or HTML), use the link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#setCopyWebDir(boolean)[WebAppContext .setCopyWebDir(true)] option.
diff --git a/jetty-documentation/src/main/asciidoc/reference/upgrading/chapter.adoc b/jetty-documentation/src/main/asciidoc/reference/upgrading/chapter.adoc
new file mode 100644
index 0000000..8910b6e
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/upgrading/chapter.adoc
@@ -0,0 +1,20 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+[[upgrading-jetty]]
+== Upgrading Jetty
+
+=== TBD
diff --git a/jetty-documentation/src/main/asciidoc/reference/upgrading/sample.adoc b/jetty-documentation/src/main/asciidoc/reference/upgrading/sample.adoc
new file mode 100644
index 0000000..9e4d932
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/reference/upgrading/sample.adoc
@@ -0,0 +1,23 @@
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ========================================================================
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+
+= Sample Title
+
+This is a sample paragraph.
+
+== Sample Subsection
+
+More sample text.
diff --git a/jetty-documentation/src/main/assembly/html.xml b/jetty-documentation/src/main/assembly/html.xml
new file mode 100644
index 0000000..6c0dcf7
--- /dev/null
+++ b/jetty-documentation/src/main/assembly/html.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly>
+  <id>html</id>
+  <formats>
+    <format>zip</format>
+  </formats>
+  <baseDirectory>${project.version}</baseDirectory>
+  <fileSets>
+    <fileSet>
+      <directory>${project.basedir}/target/docbkx/html</directory>
+      <outputDirectory></outputDirectory>
+      <includes>
+        <include>**</include>
+      </includes>
+    </fileSet>
+  </fileSets>
+</assembly>
diff --git a/jetty-documentation/src/main/docbkx-resources/css/docbook.css b/jetty-documentation/src/main/docbkx-resources/css/docbook.css
new file mode 100644
index 0000000..9245481
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/css/docbook.css
@@ -0,0 +1,310 @@
+a {
+  text-decoration: none;
+  color: #069;
+}
+
+a:hover {
+  text-decoration: underline;
+}
+
+a:visited {
+  color: #036;
+}
+
+body {
+  font-family: Verdana, Helvetica, arial, freesans, clean, sans-serif;
+  font-size: 10pt;
+  width: 960px;
+  margin-left: auto;
+  margin-right: auto;
+  background-color: #FFF;
+  color: #444;
+}
+
+code {
+  font-size: 10pt;
+}
+
+.table-contents table td,
+.table-contents table th,
+.navheader table td,
+.navfooter table td {
+  font-size: 10pt;
+}
+
+.navfooter hr, 
+.navheader hr {
+  border: 0px;
+  border-bottom: 1px solid #fc390e; /* jetty orange */
+}
+
+.navfooter a,
+.navheader a {
+  font-weight: bold;
+  color: #fc390e;
+}
+
+h1, h2, h3, h4, h5, h6 {
+  font-weight: bold;
+  margin-bottom: 12pt;
+}
+
+h1 { font-size: 18pt; }
+h2 { font-size: 16pt; }
+h3 { font-size: 14pt; }
+h4 { font-size: 12pt; }
+h5 { font-size: 10pt; }
+h6 { font-size: 10pt; }
+
+p, ul {
+  margin: 1.286em 0;
+  line-height: 120%;
+}
+
+dl {
+  margin: 0;
+  line-height: 120%;
+}
+
+pre {
+  font-family: "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
+  font-weight: normal !important;
+  font-style: normal !important;
+  font-size: 10pt !important;
+  margin: 0px;
+  overflow-x: auto;
+}
+
+strong {
+  color: #000;
+}
+
+/* Pretty Tables */
+
+.table table {
+  border: 1px solid #036;
+  padding: 0;
+  border-collapse: collapse;
+}
+
+.table tr {
+  border: 0;
+  padding: 0;
+}
+
+.table tr:nth-child(even) {
+  background-color: #EEE;
+}
+
+.table th {
+  padding: 5px;
+  background: #036;
+  color: white;
+  text-align: center;
+  font-weight: bold;
+}
+
+.table td {
+  border: 1px solid #CCC;
+  padding: 5px;
+}
+
+h2.subtitle {
+  font-variant: normal;
+}
+
+h3.author {
+  font-variant: normal;
+  margin-top: 2em;
+  margin-bottom: 0;
+}
+
+.email {
+}
+
+.informalfigure {
+  text-align: center;
+}
+
+.mediaobject {
+  display: inline-block;
+}
+
+div.variablelist dd p {
+  margin: 0;
+  margin-top: 0;
+  margin-bottom: 1em;
+  margin-left: 1em;
+}
+
+span.term {
+  font-weight:bold;
+  padding-left:3px;
+  padding-right:3px;
+}
+
+.caption p {
+ margin: 0;
+ text-align: right;
+}
+
+.navfooter table tr td {
+  width: 33.33%;
+}
+
+.navfooter {
+  margin-bottom: 1.0em;
+}
+
+.example .title {
+  color: #248;
+}
+
+div .screenexample {
+  background-color: #EEE;
+  border:solid 1px #CCC;
+  padding: 1.5em;
+  color: black;
+  border-radius: 15px;
+}
+
+pre.screen {
+  white-space: pre;
+  width: 100%;
+  color: black;
+  overflow-x: initial;
+  white-space: pre-wrap;       /* css-3 */
+  white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
+  white-space: -pre-wrap;      /* Opera 4-6 */
+  white-space: -o-pre-wrap;    /* Opera 7 */
+  word-wrap: break-word;       /* Internet Explorer 5.5+ */
+}
+
+div.variablelist {
+  padding-left: 20px;
+}
+
+.programlisting {
+  color: black;
+  font-weight: normal;
+}
+
+.programlisting .hl-tag {
+  color: #881280;
+  font-weight: normal;
+}
+
+.programlisting .hl-string {
+  color: green;
+  font-weight: bold;
+}
+
+.programlisting .hl-keyword {
+  color: #009;
+  font-weight: bold;
+}
+
+.programlisting .hl-attribute {
+  color: #fc390e;
+}
+
+.programlisting .hl-value {
+  color: #009;
+}
+
+.programlisting .hl-annotation {
+  color: #880;
+}
+
+.programlisting .hl-comment {
+  color: gray;
+  font-style: italic;
+}
+
+.caution, .important, .note, .tip, .warning {
+  padding: 10px;
+  padding-left: 2.0em;
+  margin: 10px;
+  border-style: solid;
+  border-width: 1px;
+  border-top-width: 4px;
+}
+
+.caution { border-color: #c60; }
+.caution h3 { color: #c60; }
+
+.warning { border-color: #900; }
+.warning h3 { color: #900; }
+
+.important { border-color: #069; }
+.important h3 { color: #069; }
+
+.note { border-color: #909; }
+.note h3 { color: #909; }
+
+.tip { border-color: #090; }
+.tip h3 { color: #090; }
+
+.caution h3, .important h3, .note h3, .tip h3, .warning h3 {
+  margin: 0px;
+  margin-bottom: 0.5em;
+}
+
+.jetty-callout {
+  background-color: #DEF;
+  text-align: left; 
+  border: thin solid #CCF; 
+  padding: 4px; 
+}
+
+.jetty-callout h5 {
+  font-size: 10pt;
+  margin: 0;
+  margin-bottom: 5px;
+}
+
+.jetty-callout .callout {
+  font-weight: bold;
+  font-style: normal;
+}
+
+.jetty-callout a {
+  text-decoration: none;
+}
+
+.jetty-callout .website {
+  color: #48f;
+}
+
+.jetty-callout p {
+  font-style: oblique;
+  margin: 0;
+}
+
+div.draft {
+  background-color: #FF3300; 
+  text-align: left; 
+  font-size: 110%; 
+  border:thin dotted blue; 
+  padding: 4px;
+  margin-top: 2.0em;
+  margin-bottom: 2.0em;
+  padding: 2.0em;
+  color: white;
+}
+
+div.draft p {
+  font-weight: bold;
+  margin: 0;
+}
+
+div.draft h5 {
+  margin: 0;
+  margin-bottom: 1.0em;
+  font-size: 150%;
+}
+
+div.draft a {
+  color: yellow;
+}
+
diff --git a/jetty-documentation/src/main/docbkx-resources/css/font-awesome/font-awesome-ie7.min.css b/jetty-documentation/src/main/docbkx-resources/css/font-awesome/font-awesome-ie7.min.css
new file mode 100644
index 0000000..ae30160
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/css/font-awesome/font-awesome-ie7.min.css
@@ -0,0 +1,22 @@
+/*!
+ *  Font Awesome 3.0.2
+ *  the iconic font designed for use with Twitter Bootstrap
+ *  -------------------------------------------------------
+ *  The full suite of pictographic icons, examples, and documentation
+ *  can be found at: http://fortawesome.github.com/Font-Awesome/
+ *
+ *  License
+ *  -------------------------------------------------------
+ *  - The Font Awesome font is licensed under the SIL Open Font License - http://scripts.sil.org/OFL
+ *  - Font Awesome CSS, LESS, and SASS files are licensed under the MIT License -
+ *    http://opensource.org/licenses/mit-license.html
+ *  - The Font Awesome pictograms are licensed under the CC BY 3.0 License - http://creativecommons.org/licenses/by/3.0/
+ *  - Attribution is no longer required in Font Awesome 3.0, but much appreciated:
+ *    "Font Awesome by Dave Gandy - http://fortawesome.github.com/Font-Awesome"
+
+ *  Contact
+ *  -------------------------------------------------------
+ *  Email: dave@davegandy.com
+ *  Twitter: http://twitter.com/fortaweso_me
+ *  Work: Lead Product Designer @ http://kyruus.com
+ */.icon-large{font-size:1.3333333333333333em;margin-top:-4px;padding-top:3px;margin-bottom:-4px;padding-bottom:3px;vertical-align:middle}.nav [class^="icon-"],.nav [class*=" icon-"]{vertical-align:inherit;margin-top:-4px;padding-top:3px;margin-bottom:-4px;padding-bottom:3px}.nav [class^="icon-"].icon-large,.nav [class*=" icon-"].icon-large{vertical-align:-25%}.nav-pills [class^="icon-"].icon-large,.nav-tabs [class^="icon-"].icon-large,.nav-pills [class*=" icon-"].icon-large,.nav-tabs [class*=" icon-"].icon-large{line-height:.75em;margin-top:-7px;padding-top:5px;margin-bottom:-5px;padding-bottom:4px}.btn [class^="icon-"].pull-left,.btn [class*=" icon-"].pull-left,.btn [class^="icon-"].pull-right,.btn [class*=" icon-"].pull-right{vertical-align:inherit}.btn [class^="icon-"].icon-large,.btn [class*=" icon-"].icon-large{margin-top:-0.5em}a [class^="icon-"],a [class*=" icon-"]{cursor:pointer}ul.icons{text-indent:-1.5em;margin-left:3em}.icon-glass{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf000;')}.icon-music{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf001;')}.icon-search{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf002;')}.icon-envelope{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf003;')}.icon-heart{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf004;')}.icon-star{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf005;')}.icon-star-empty{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf006;')}.icon-user{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf007;')}.icon-film{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf008;')}.icon-th-large{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf009;')}.icon-th{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf00a;')}.icon-th-list{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf00b;')}.icon-ok{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf00c;')}.icon-remove{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf00d;')}.icon-zoom-in{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf00e;')}.icon-zoom-out{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf010;')}.icon-off{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf011;')}.icon-signal{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf012;')}.icon-cog{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf013;')}.icon-trash{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf014;')}.icon-home{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf015;')}.icon-file{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf016;')}.icon-time{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf017;')}.icon-road{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf018;')}.icon-download-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf019;')}.icon-download{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf01a;')}.icon-upload{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf01b;')}.icon-inbox{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf01c;')}.icon-play-circle{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf01d;')}.icon-repeat{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf01e;')}.icon-refresh{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf021;')}.icon-list-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf022;')}.icon-lock{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf023;')}.icon-flag{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf024;')}.icon-headphones{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf025;')}.icon-volume-off{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf026;')}.icon-volume-down{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf027;')}.icon-volume-up{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf028;')}.icon-qrcode{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf029;')}.icon-barcode{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf02a;')}.icon-tag{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf02b;')}.icon-tags{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf02c;')}.icon-book{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf02d;')}.icon-bookmark{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf02e;')}.icon-print{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf02f;')}.icon-camera{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf030;')}.icon-font{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf031;')}.icon-bold{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf032;')}.icon-italic{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf033;')}.icon-text-height{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf034;')}.icon-text-width{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf035;')}.icon-align-left{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf036;')}.icon-align-center{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf037;')}.icon-align-right{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf038;')}.icon-align-justify{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf039;')}.icon-list{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf03a;')}.icon-indent-left{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf03b;')}.icon-indent-right{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf03c;')}.icon-facetime-video{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf03d;')}.icon-picture{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf03e;')}.icon-pencil{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf040;')}.icon-map-marker{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf041;')}.icon-adjust{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf042;')}.icon-tint{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf043;')}.icon-edit{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf044;')}.icon-share{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf045;')}.icon-check{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf046;')}.icon-move{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf047;')}.icon-step-backward{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf048;')}.icon-fast-backward{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf049;')}.icon-backward{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf04a;')}.icon-play{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf04b;')}.icon-pause{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf04c;')}.icon-stop{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf04d;')}.icon-forward{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf04e;')}.icon-fast-forward{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf050;')}.icon-step-forward{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf051;')}.icon-eject{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf052;')}.icon-chevron-left{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf053;')}.icon-chevron-right{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf054;')}.icon-plus-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf055;')}.icon-minus-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf056;')}.icon-remove-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf057;')}.icon-ok-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf058;')}.icon-question-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf059;')}.icon-info-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf05a;')}.icon-screenshot{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf05b;')}.icon-remove-circle{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf05c;')}.icon-ok-circle{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf05d;')}.icon-ban-circle{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf05e;')}.icon-arrow-left{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf060;')}.icon-arrow-right{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf061;')}.icon-arrow-up{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf062;')}.icon-arrow-down{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf063;')}.icon-share-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf064;')}.icon-resize-full{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf065;')}.icon-resize-small{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf066;')}.icon-plus{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf067;')}.icon-minus{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf068;')}.icon-asterisk{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf069;')}.icon-exclamation-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf06a;')}.icon-gift{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf06b;')}.icon-leaf{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf06c;')}.icon-fire{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf06d;')}.icon-eye-open{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf06e;')}.icon-eye-close{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf070;')}.icon-warning-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf071;')}.icon-plane{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf072;')}.icon-calendar{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf073;')}.icon-random{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf074;')}.icon-comment{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf075;')}.icon-magnet{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf076;')}.icon-chevron-up{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf077;')}.icon-chevron-down{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf078;')}.icon-retweet{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf079;')}.icon-shopping-cart{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf07a;')}.icon-folder-close{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf07b;')}.icon-folder-open{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf07c;')}.icon-resize-vertical{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf07d;')}.icon-resize-horizontal{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf07e;')}.icon-bar-chart{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf080;')}.icon-twitter-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf081;')}.icon-facebook-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf082;')}.icon-camera-retro{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf083;')}.icon-key{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf084;')}.icon-cogs{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf085;')}.icon-comments{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf086;')}.icon-thumbs-up{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf087;')}.icon-thumbs-down{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf088;')}.icon-star-half{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf089;')}.icon-heart-empty{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf08a;')}.icon-signout{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf08b;')}.icon-linkedin-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf08c;')}.icon-pushpin{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf08d;')}.icon-external-link{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf08e;')}.icon-signin{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf090;')}.icon-trophy{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf091;')}.icon-github-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf092;')}.icon-upload-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf093;')}.icon-lemon{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf094;')}.icon-phone{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf095;')}.icon-check-empty{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf096;')}.icon-bookmark-empty{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf097;')}.icon-phone-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf098;')}.icon-twitter{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf099;')}.icon-facebook{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf09a;')}.icon-github{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf09b;')}.icon-unlock{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf09c;')}.icon-credit-card{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf09d;')}.icon-rss{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf09e;')}.icon-hdd{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0a0;')}.icon-bullhorn{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0a1;')}.icon-bell{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0a2;')}.icon-certificate{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0a3;')}.icon-hand-right{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0a4;')}.icon-hand-left{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0a5;')}.icon-hand-up{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0a6;')}.icon-hand-down{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0a7;')}.icon-circle-arrow-left{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0a8;')}.icon-circle-arrow-right{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0a9;')}.icon-circle-arrow-up{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0aa;')}.icon-circle-arrow-down{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0ab;')}.icon-globe{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0ac;')}.icon-wrench{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0ad;')}.icon-tasks{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0ae;')}.icon-filter{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0b0;')}.icon-briefcase{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0b1;')}.icon-fullscreen{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0b2;')}.icon-group{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0c0;')}.icon-link{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0c1;')}.icon-cloud{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0c2;')}.icon-beaker{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0c3;')}.icon-cut{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0c4;')}.icon-copy{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0c5;')}.icon-paper-clip{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0c6;')}.icon-save{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0c7;')}.icon-sign-blank{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0c8;')}.icon-reorder{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0c9;')}.icon-list-ul{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0ca;')}.icon-list-ol{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0cb;')}.icon-strikethrough{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0cc;')}.icon-underline{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0cd;')}.icon-table{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0ce;')}.icon-magic{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0d0;')}.icon-truck{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0d1;')}.icon-pinterest{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0d2;')}.icon-pinterest-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0d3;')}.icon-google-plus-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0d4;')}.icon-google-plus{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0d5;')}.icon-money{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0d6;')}.icon-caret-down{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0d7;')}.icon-caret-up{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0d8;')}.icon-caret-left{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0d9;')}.icon-caret-right{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0da;')}.icon-columns{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0db;')}.icon-sort{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0dc;')}.icon-sort-down{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0dd;')}.icon-sort-up{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0de;')}.icon-envelope-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0e0;')}.icon-linkedin{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0e1;')}.icon-undo{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0e2;')}.icon-legal{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0e3;')}.icon-dashboard{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0e4;')}.icon-comment-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0e5;')}.icon-comments-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0e6;')}.icon-bolt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0e7;')}.icon-sitemap{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0e8;')}.icon-umbrella{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0e9;')}.icon-paste{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0ea;')}.icon-lightbulb{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0eb;')}.icon-exchange{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0ec;')}.icon-cloud-download{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0ed;')}.icon-cloud-upload{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0ee;')}.icon-user-md{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0f0;')}.icon-stethoscope{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0f1;')}.icon-suitcase{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0f2;')}.icon-bell-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0f3;')}.icon-coffee{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0f4;')}.icon-food{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0f5;')}.icon-file-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0f6;')}.icon-building{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0f7;')}.icon-hospital{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0f8;')}.icon-ambulance{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0f9;')}.icon-medkit{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0fa;')}.icon-fighter-jet{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0fb;')}.icon-beer{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0fc;')}.icon-h-sign{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0fd;')}.icon-plus-sign-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf0fe;')}.icon-double-angle-left{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf100;')}.icon-double-angle-right{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf101;')}.icon-double-angle-up{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf102;')}.icon-double-angle-down{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf103;')}.icon-angle-left{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf104;')}.icon-angle-right{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf105;')}.icon-angle-up{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf106;')}.icon-angle-down{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf107;')}.icon-desktop{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf108;')}.icon-laptop{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf109;')}.icon-tablet{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf10a;')}.icon-mobile-phone{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf10b;')}.icon-circle-blank{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf10c;')}.icon-quote-left{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf10d;')}.icon-quote-right{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf10e;')}.icon-spinner{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf110;')}.icon-circle{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf111;')}.icon-reply{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf112;')}.icon-github-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf113;')}.icon-folder-close-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf114;')}.icon-folder-open-alt{*zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = '&#xf115;')}
\ No newline at end of file
diff --git a/jetty-documentation/src/main/docbkx-resources/css/font-awesome/font-awesome.css b/jetty-documentation/src/main/docbkx-resources/css/font-awesome/font-awesome.css
new file mode 100755
index 0000000..ace5a08
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/css/font-awesome/font-awesome.css
@@ -0,0 +1,2086 @@
+/*!
+ *  Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
+ *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */
+/* FONT PATH
+ * -------------------------- */
+@font-face {
+  font-family: 'FontAwesome';
+  src: url('../../fonts/fontawesome-webfont.eot?v=4.5.0');
+  src: url('../../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'), url('../../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'), url('../../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'), url('../../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'), url('../../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
+.fa {
+  display: inline-block;
+  font: normal normal normal 14px/1 FontAwesome;
+  font-size: inherit;
+  text-rendering: auto;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+/* makes the font 33% larger relative to the icon container */
+.fa-lg {
+  font-size: 1.33333333em;
+  line-height: 0.75em;
+  vertical-align: -15%;
+}
+.fa-2x {
+  font-size: 2em;
+}
+.fa-3x {
+  font-size: 3em;
+}
+.fa-4x {
+  font-size: 4em;
+}
+.fa-5x {
+  font-size: 5em;
+}
+.fa-fw {
+  width: 1.28571429em;
+  text-align: center;
+}
+.fa-ul {
+  padding-left: 0;
+  margin-left: 2.14285714em;
+  list-style-type: none;
+}
+.fa-ul > li {
+  position: relative;
+}
+.fa-li {
+  position: absolute;
+  left: -2.14285714em;
+  width: 2.14285714em;
+  top: 0.14285714em;
+  text-align: center;
+}
+.fa-li.fa-lg {
+  left: -1.85714286em;
+}
+.fa-border {
+  padding: .2em .25em .15em;
+  border: solid 0.08em #eeeeee;
+  border-radius: .1em;
+}
+.fa-pull-left {
+  float: left;
+}
+.fa-pull-right {
+  float: right;
+}
+.fa.fa-pull-left {
+  margin-right: .3em;
+}
+.fa.fa-pull-right {
+  margin-left: .3em;
+}
+/* Deprecated as of 4.4.0 */
+.pull-right {
+  float: right;
+}
+.pull-left {
+  float: left;
+}
+.fa.pull-left {
+  margin-right: .3em;
+}
+.fa.pull-right {
+  margin-left: .3em;
+}
+.fa-spin {
+  -webkit-animation: fa-spin 2s infinite linear;
+  animation: fa-spin 2s infinite linear;
+}
+.fa-pulse {
+  -webkit-animation: fa-spin 1s infinite steps(8);
+  animation: fa-spin 1s infinite steps(8);
+}
+@-webkit-keyframes fa-spin {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  100% {
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+@keyframes fa-spin {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  100% {
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+.fa-rotate-90 {
+  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
+  -webkit-transform: rotate(90deg);
+  -ms-transform: rotate(90deg);
+  transform: rotate(90deg);
+}
+.fa-rotate-180 {
+  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
+  -webkit-transform: rotate(180deg);
+  -ms-transform: rotate(180deg);
+  transform: rotate(180deg);
+}
+.fa-rotate-270 {
+  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
+  -webkit-transform: rotate(270deg);
+  -ms-transform: rotate(270deg);
+  transform: rotate(270deg);
+}
+.fa-flip-horizontal {
+  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
+  -webkit-transform: scale(-1, 1);
+  -ms-transform: scale(-1, 1);
+  transform: scale(-1, 1);
+}
+.fa-flip-vertical {
+  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
+  -webkit-transform: scale(1, -1);
+  -ms-transform: scale(1, -1);
+  transform: scale(1, -1);
+}
+:root .fa-rotate-90,
+:root .fa-rotate-180,
+:root .fa-rotate-270,
+:root .fa-flip-horizontal,
+:root .fa-flip-vertical {
+  filter: none;
+}
+.fa-stack {
+  position: relative;
+  display: inline-block;
+  width: 2em;
+  height: 2em;
+  line-height: 2em;
+  vertical-align: middle;
+}
+.fa-stack-1x,
+.fa-stack-2x {
+  position: absolute;
+  left: 0;
+  width: 100%;
+  text-align: center;
+}
+.fa-stack-1x {
+  line-height: inherit;
+}
+.fa-stack-2x {
+  font-size: 2em;
+}
+.fa-inverse {
+  color: #ffffff;
+}
+/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
+   readers do not read off random characters that represent icons */
+.fa-glass:before {
+  content: "\f000";
+}
+.fa-music:before {
+  content: "\f001";
+}
+.fa-search:before {
+  content: "\f002";
+}
+.fa-envelope-o:before {
+  content: "\f003";
+}
+.fa-heart:before {
+  content: "\f004";
+}
+.fa-star:before {
+  content: "\f005";
+}
+.fa-star-o:before {
+  content: "\f006";
+}
+.fa-user:before {
+  content: "\f007";
+}
+.fa-film:before {
+  content: "\f008";
+}
+.fa-th-large:before {
+  content: "\f009";
+}
+.fa-th:before {
+  content: "\f00a";
+}
+.fa-th-list:before {
+  content: "\f00b";
+}
+.fa-check:before {
+  content: "\f00c";
+}
+.fa-remove:before,
+.fa-close:before,
+.fa-times:before {
+  content: "\f00d";
+}
+.fa-search-plus:before {
+  content: "\f00e";
+}
+.fa-search-minus:before {
+  content: "\f010";
+}
+.fa-power-off:before {
+  content: "\f011";
+}
+.fa-signal:before {
+  content: "\f012";
+}
+.fa-gear:before,
+.fa-cog:before {
+  content: "\f013";
+}
+.fa-trash-o:before {
+  content: "\f014";
+}
+.fa-home:before {
+  content: "\f015";
+}
+.fa-file-o:before {
+  content: "\f016";
+}
+.fa-clock-o:before {
+  content: "\f017";
+}
+.fa-road:before {
+  content: "\f018";
+}
+.fa-download:before {
+  content: "\f019";
+}
+.fa-arrow-circle-o-down:before {
+  content: "\f01a";
+}
+.fa-arrow-circle-o-up:before {
+  content: "\f01b";
+}
+.fa-inbox:before {
+  content: "\f01c";
+}
+.fa-play-circle-o:before {
+  content: "\f01d";
+}
+.fa-rotate-right:before,
+.fa-repeat:before {
+  content: "\f01e";
+}
+.fa-refresh:before {
+  content: "\f021";
+}
+.fa-list-alt:before {
+  content: "\f022";
+}
+.fa-lock:before {
+  content: "\f023";
+}
+.fa-flag:before {
+  content: "\f024";
+}
+.fa-headphones:before {
+  content: "\f025";
+}
+.fa-volume-off:before {
+  content: "\f026";
+}
+.fa-volume-down:before {
+  content: "\f027";
+}
+.fa-volume-up:before {
+  content: "\f028";
+}
+.fa-qrcode:before {
+  content: "\f029";
+}
+.fa-barcode:before {
+  content: "\f02a";
+}
+.fa-tag:before {
+  content: "\f02b";
+}
+.fa-tags:before {
+  content: "\f02c";
+}
+.fa-book:before {
+  content: "\f02d";
+}
+.fa-bookmark:before {
+  content: "\f02e";
+}
+.fa-print:before {
+  content: "\f02f";
+}
+.fa-camera:before {
+  content: "\f030";
+}
+.fa-font:before {
+  content: "\f031";
+}
+.fa-bold:before {
+  content: "\f032";
+}
+.fa-italic:before {
+  content: "\f033";
+}
+.fa-text-height:before {
+  content: "\f034";
+}
+.fa-text-width:before {
+  content: "\f035";
+}
+.fa-align-left:before {
+  content: "\f036";
+}
+.fa-align-center:before {
+  content: "\f037";
+}
+.fa-align-right:before {
+  content: "\f038";
+}
+.fa-align-justify:before {
+  content: "\f039";
+}
+.fa-list:before {
+  content: "\f03a";
+}
+.fa-dedent:before,
+.fa-outdent:before {
+  content: "\f03b";
+}
+.fa-indent:before {
+  content: "\f03c";
+}
+.fa-video-camera:before {
+  content: "\f03d";
+}
+.fa-photo:before,
+.fa-image:before,
+.fa-picture-o:before {
+  content: "\f03e";
+}
+.fa-pencil:before {
+  content: "\f040";
+}
+.fa-map-marker:before {
+  content: "\f041";
+}
+.fa-adjust:before {
+  content: "\f042";
+}
+.fa-tint:before {
+  content: "\f043";
+}
+.fa-edit:before,
+.fa-pencil-square-o:before {
+  content: "\f044";
+}
+.fa-share-square-o:before {
+  content: "\f045";
+}
+.fa-check-square-o:before {
+  content: "\f046";
+}
+.fa-arrows:before {
+  content: "\f047";
+}
+.fa-step-backward:before {
+  content: "\f048";
+}
+.fa-fast-backward:before {
+  content: "\f049";
+}
+.fa-backward:before {
+  content: "\f04a";
+}
+.fa-play:before {
+  content: "\f04b";
+}
+.fa-pause:before {
+  content: "\f04c";
+}
+.fa-stop:before {
+  content: "\f04d";
+}
+.fa-forward:before {
+  content: "\f04e";
+}
+.fa-fast-forward:before {
+  content: "\f050";
+}
+.fa-step-forward:before {
+  content: "\f051";
+}
+.fa-eject:before {
+  content: "\f052";
+}
+.fa-chevron-left:before {
+  content: "\f053";
+}
+.fa-chevron-right:before {
+  content: "\f054";
+}
+.fa-plus-circle:before {
+  content: "\f055";
+}
+.fa-minus-circle:before {
+  content: "\f056";
+}
+.fa-times-circle:before {
+  content: "\f057";
+}
+.fa-check-circle:before {
+  content: "\f058";
+}
+.fa-question-circle:before {
+  content: "\f059";
+}
+.fa-info-circle:before {
+  content: "\f05a";
+}
+.fa-crosshairs:before {
+  content: "\f05b";
+}
+.fa-times-circle-o:before {
+  content: "\f05c";
+}
+.fa-check-circle-o:before {
+  content: "\f05d";
+}
+.fa-ban:before {
+  content: "\f05e";
+}
+.fa-arrow-left:before {
+  content: "\f060";
+}
+.fa-arrow-right:before {
+  content: "\f061";
+}
+.fa-arrow-up:before {
+  content: "\f062";
+}
+.fa-arrow-down:before {
+  content: "\f063";
+}
+.fa-mail-forward:before,
+.fa-share:before {
+  content: "\f064";
+}
+.fa-expand:before {
+  content: "\f065";
+}
+.fa-compress:before {
+  content: "\f066";
+}
+.fa-plus:before {
+  content: "\f067";
+}
+.fa-minus:before {
+  content: "\f068";
+}
+.fa-asterisk:before {
+  content: "\f069";
+}
+.fa-exclamation-circle:before {
+  content: "\f06a";
+}
+.fa-gift:before {
+  content: "\f06b";
+}
+.fa-leaf:before {
+  content: "\f06c";
+}
+.fa-fire:before {
+  content: "\f06d";
+}
+.fa-eye:before {
+  content: "\f06e";
+}
+.fa-eye-slash:before {
+  content: "\f070";
+}
+.fa-warning:before,
+.fa-exclamation-triangle:before {
+  content: "\f071";
+}
+.fa-plane:before {
+  content: "\f072";
+}
+.fa-calendar:before {
+  content: "\f073";
+}
+.fa-random:before {
+  content: "\f074";
+}
+.fa-comment:before {
+  content: "\f075";
+}
+.fa-magnet:before {
+  content: "\f076";
+}
+.fa-chevron-up:before {
+  content: "\f077";
+}
+.fa-chevron-down:before {
+  content: "\f078";
+}
+.fa-retweet:before {
+  content: "\f079";
+}
+.fa-shopping-cart:before {
+  content: "\f07a";
+}
+.fa-folder:before {
+  content: "\f07b";
+}
+.fa-folder-open:before {
+  content: "\f07c";
+}
+.fa-arrows-v:before {
+  content: "\f07d";
+}
+.fa-arrows-h:before {
+  content: "\f07e";
+}
+.fa-bar-chart-o:before,
+.fa-bar-chart:before {
+  content: "\f080";
+}
+.fa-twitter-square:before {
+  content: "\f081";
+}
+.fa-facebook-square:before {
+  content: "\f082";
+}
+.fa-camera-retro:before {
+  content: "\f083";
+}
+.fa-key:before {
+  content: "\f084";
+}
+.fa-gears:before,
+.fa-cogs:before {
+  content: "\f085";
+}
+.fa-comments:before {
+  content: "\f086";
+}
+.fa-thumbs-o-up:before {
+  content: "\f087";
+}
+.fa-thumbs-o-down:before {
+  content: "\f088";
+}
+.fa-star-half:before {
+  content: "\f089";
+}
+.fa-heart-o:before {
+  content: "\f08a";
+}
+.fa-sign-out:before {
+  content: "\f08b";
+}
+.fa-linkedin-square:before {
+  content: "\f08c";
+}
+.fa-thumb-tack:before {
+  content: "\f08d";
+}
+.fa-external-link:before {
+  content: "\f08e";
+}
+.fa-sign-in:before {
+  content: "\f090";
+}
+.fa-trophy:before {
+  content: "\f091";
+}
+.fa-github-square:before {
+  content: "\f092";
+}
+.fa-upload:before {
+  content: "\f093";
+}
+.fa-lemon-o:before {
+  content: "\f094";
+}
+.fa-phone:before {
+  content: "\f095";
+}
+.fa-square-o:before {
+  content: "\f096";
+}
+.fa-bookmark-o:before {
+  content: "\f097";
+}
+.fa-phone-square:before {
+  content: "\f098";
+}
+.fa-twitter:before {
+  content: "\f099";
+}
+.fa-facebook-f:before,
+.fa-facebook:before {
+  content: "\f09a";
+}
+.fa-github:before {
+  content: "\f09b";
+}
+.fa-unlock:before {
+  content: "\f09c";
+}
+.fa-credit-card:before {
+  content: "\f09d";
+}
+.fa-feed:before,
+.fa-rss:before {
+  content: "\f09e";
+}
+.fa-hdd-o:before {
+  content: "\f0a0";
+}
+.fa-bullhorn:before {
+  content: "\f0a1";
+}
+.fa-bell:before {
+  content: "\f0f3";
+}
+.fa-certificate:before {
+  content: "\f0a3";
+}
+.fa-hand-o-right:before {
+  content: "\f0a4";
+}
+.fa-hand-o-left:before {
+  content: "\f0a5";
+}
+.fa-hand-o-up:before {
+  content: "\f0a6";
+}
+.fa-hand-o-down:before {
+  content: "\f0a7";
+}
+.fa-arrow-circle-left:before {
+  content: "\f0a8";
+}
+.fa-arrow-circle-right:before {
+  content: "\f0a9";
+}
+.fa-arrow-circle-up:before {
+  content: "\f0aa";
+}
+.fa-arrow-circle-down:before {
+  content: "\f0ab";
+}
+.fa-globe:before {
+  content: "\f0ac";
+}
+.fa-wrench:before {
+  content: "\f0ad";
+}
+.fa-tasks:before {
+  content: "\f0ae";
+}
+.fa-filter:before {
+  content: "\f0b0";
+}
+.fa-briefcase:before {
+  content: "\f0b1";
+}
+.fa-arrows-alt:before {
+  content: "\f0b2";
+}
+.fa-group:before,
+.fa-users:before {
+  content: "\f0c0";
+}
+.fa-chain:before,
+.fa-link:before {
+  content: "\f0c1";
+}
+.fa-cloud:before {
+  content: "\f0c2";
+}
+.fa-flask:before {
+  content: "\f0c3";
+}
+.fa-cut:before,
+.fa-scissors:before {
+  content: "\f0c4";
+}
+.fa-copy:before,
+.fa-files-o:before {
+  content: "\f0c5";
+}
+.fa-paperclip:before {
+  content: "\f0c6";
+}
+.fa-save:before,
+.fa-floppy-o:before {
+  content: "\f0c7";
+}
+.fa-square:before {
+  content: "\f0c8";
+}
+.fa-navicon:before,
+.fa-reorder:before,
+.fa-bars:before {
+  content: "\f0c9";
+}
+.fa-list-ul:before {
+  content: "\f0ca";
+}
+.fa-list-ol:before {
+  content: "\f0cb";
+}
+.fa-strikethrough:before {
+  content: "\f0cc";
+}
+.fa-underline:before {
+  content: "\f0cd";
+}
+.fa-table:before {
+  content: "\f0ce";
+}
+.fa-magic:before {
+  content: "\f0d0";
+}
+.fa-truck:before {
+  content: "\f0d1";
+}
+.fa-pinterest:before {
+  content: "\f0d2";
+}
+.fa-pinterest-square:before {
+  content: "\f0d3";
+}
+.fa-google-plus-square:before {
+  content: "\f0d4";
+}
+.fa-google-plus:before {
+  content: "\f0d5";
+}
+.fa-money:before {
+  content: "\f0d6";
+}
+.fa-caret-down:before {
+  content: "\f0d7";
+}
+.fa-caret-up:before {
+  content: "\f0d8";
+}
+.fa-caret-left:before {
+  content: "\f0d9";
+}
+.fa-caret-right:before {
+  content: "\f0da";
+}
+.fa-columns:before {
+  content: "\f0db";
+}
+.fa-unsorted:before,
+.fa-sort:before {
+  content: "\f0dc";
+}
+.fa-sort-down:before,
+.fa-sort-desc:before {
+  content: "\f0dd";
+}
+.fa-sort-up:before,
+.fa-sort-asc:before {
+  content: "\f0de";
+}
+.fa-envelope:before {
+  content: "\f0e0";
+}
+.fa-linkedin:before {
+  content: "\f0e1";
+}
+.fa-rotate-left:before,
+.fa-undo:before {
+  content: "\f0e2";
+}
+.fa-legal:before,
+.fa-gavel:before {
+  content: "\f0e3";
+}
+.fa-dashboard:before,
+.fa-tachometer:before {
+  content: "\f0e4";
+}
+.fa-comment-o:before {
+  content: "\f0e5";
+}
+.fa-comments-o:before {
+  content: "\f0e6";
+}
+.fa-flash:before,
+.fa-bolt:before {
+  content: "\f0e7";
+}
+.fa-sitemap:before {
+  content: "\f0e8";
+}
+.fa-umbrella:before {
+  content: "\f0e9";
+}
+.fa-paste:before,
+.fa-clipboard:before {
+  content: "\f0ea";
+}
+.fa-lightbulb-o:before {
+  content: "\f0eb";
+}
+.fa-exchange:before {
+  content: "\f0ec";
+}
+.fa-cloud-download:before {
+  content: "\f0ed";
+}
+.fa-cloud-upload:before {
+  content: "\f0ee";
+}
+.fa-user-md:before {
+  content: "\f0f0";
+}
+.fa-stethoscope:before {
+  content: "\f0f1";
+}
+.fa-suitcase:before {
+  content: "\f0f2";
+}
+.fa-bell-o:before {
+  content: "\f0a2";
+}
+.fa-coffee:before {
+  content: "\f0f4";
+}
+.fa-cutlery:before {
+  content: "\f0f5";
+}
+.fa-file-text-o:before {
+  content: "\f0f6";
+}
+.fa-building-o:before {
+  content: "\f0f7";
+}
+.fa-hospital-o:before {
+  content: "\f0f8";
+}
+.fa-ambulance:before {
+  content: "\f0f9";
+}
+.fa-medkit:before {
+  content: "\f0fa";
+}
+.fa-fighter-jet:before {
+  content: "\f0fb";
+}
+.fa-beer:before {
+  content: "\f0fc";
+}
+.fa-h-square:before {
+  content: "\f0fd";
+}
+.fa-plus-square:before {
+  content: "\f0fe";
+}
+.fa-angle-double-left:before {
+  content: "\f100";
+}
+.fa-angle-double-right:before {
+  content: "\f101";
+}
+.fa-angle-double-up:before {
+  content: "\f102";
+}
+.fa-angle-double-down:before {
+  content: "\f103";
+}
+.fa-angle-left:before {
+  content: "\f104";
+}
+.fa-angle-right:before {
+  content: "\f105";
+}
+.fa-angle-up:before {
+  content: "\f106";
+}
+.fa-angle-down:before {
+  content: "\f107";
+}
+.fa-desktop:before {
+  content: "\f108";
+}
+.fa-laptop:before {
+  content: "\f109";
+}
+.fa-tablet:before {
+  content: "\f10a";
+}
+.fa-mobile-phone:before,
+.fa-mobile:before {
+  content: "\f10b";
+}
+.fa-circle-o:before {
+  content: "\f10c";
+}
+.fa-quote-left:before {
+  content: "\f10d";
+}
+.fa-quote-right:before {
+  content: "\f10e";
+}
+.fa-spinner:before {
+  content: "\f110";
+}
+.fa-circle:before {
+  content: "\f111";
+}
+.fa-mail-reply:before,
+.fa-reply:before {
+  content: "\f112";
+}
+.fa-github-alt:before {
+  content: "\f113";
+}
+.fa-folder-o:before {
+  content: "\f114";
+}
+.fa-folder-open-o:before {
+  content: "\f115";
+}
+.fa-smile-o:before {
+  content: "\f118";
+}
+.fa-frown-o:before {
+  content: "\f119";
+}
+.fa-meh-o:before {
+  content: "\f11a";
+}
+.fa-gamepad:before {
+  content: "\f11b";
+}
+.fa-keyboard-o:before {
+  content: "\f11c";
+}
+.fa-flag-o:before {
+  content: "\f11d";
+}
+.fa-flag-checkered:before {
+  content: "\f11e";
+}
+.fa-terminal:before {
+  content: "\f120";
+}
+.fa-code:before {
+  content: "\f121";
+}
+.fa-mail-reply-all:before,
+.fa-reply-all:before {
+  content: "\f122";
+}
+.fa-star-half-empty:before,
+.fa-star-half-full:before,
+.fa-star-half-o:before {
+  content: "\f123";
+}
+.fa-location-arrow:before {
+  content: "\f124";
+}
+.fa-crop:before {
+  content: "\f125";
+}
+.fa-code-fork:before {
+  content: "\f126";
+}
+.fa-unlink:before,
+.fa-chain-broken:before {
+  content: "\f127";
+}
+.fa-question:before {
+  content: "\f128";
+}
+.fa-info:before {
+  content: "\f129";
+}
+.fa-exclamation:before {
+  content: "\f12a";
+}
+.fa-superscript:before {
+  content: "\f12b";
+}
+.fa-subscript:before {
+  content: "\f12c";
+}
+.fa-eraser:before {
+  content: "\f12d";
+}
+.fa-puzzle-piece:before {
+  content: "\f12e";
+}
+.fa-microphone:before {
+  content: "\f130";
+}
+.fa-microphone-slash:before {
+  content: "\f131";
+}
+.fa-shield:before {
+  content: "\f132";
+}
+.fa-calendar-o:before {
+  content: "\f133";
+}
+.fa-fire-extinguisher:before {
+  content: "\f134";
+}
+.fa-rocket:before {
+  content: "\f135";
+}
+.fa-maxcdn:before {
+  content: "\f136";
+}
+.fa-chevron-circle-left:before {
+  content: "\f137";
+}
+.fa-chevron-circle-right:before {
+  content: "\f138";
+}
+.fa-chevron-circle-up:before {
+  content: "\f139";
+}
+.fa-chevron-circle-down:before {
+  content: "\f13a";
+}
+.fa-html5:before {
+  content: "\f13b";
+}
+.fa-css3:before {
+  content: "\f13c";
+}
+.fa-anchor:before {
+  content: "\f13d";
+}
+.fa-unlock-alt:before {
+  content: "\f13e";
+}
+.fa-bullseye:before {
+  content: "\f140";
+}
+.fa-ellipsis-h:before {
+  content: "\f141";
+}
+.fa-ellipsis-v:before {
+  content: "\f142";
+}
+.fa-rss-square:before {
+  content: "\f143";
+}
+.fa-play-circle:before {
+  content: "\f144";
+}
+.fa-ticket:before {
+  content: "\f145";
+}
+.fa-minus-square:before {
+  content: "\f146";
+}
+.fa-minus-square-o:before {
+  content: "\f147";
+}
+.fa-level-up:before {
+  content: "\f148";
+}
+.fa-level-down:before {
+  content: "\f149";
+}
+.fa-check-square:before {
+  content: "\f14a";
+}
+.fa-pencil-square:before {
+  content: "\f14b";
+}
+.fa-external-link-square:before {
+  content: "\f14c";
+}
+.fa-share-square:before {
+  content: "\f14d";
+}
+.fa-compass:before {
+  content: "\f14e";
+}
+.fa-toggle-down:before,
+.fa-caret-square-o-down:before {
+  content: "\f150";
+}
+.fa-toggle-up:before,
+.fa-caret-square-o-up:before {
+  content: "\f151";
+}
+.fa-toggle-right:before,
+.fa-caret-square-o-right:before {
+  content: "\f152";
+}
+.fa-euro:before,
+.fa-eur:before {
+  content: "\f153";
+}
+.fa-gbp:before {
+  content: "\f154";
+}
+.fa-dollar:before,
+.fa-usd:before {
+  content: "\f155";
+}
+.fa-rupee:before,
+.fa-inr:before {
+  content: "\f156";
+}
+.fa-cny:before,
+.fa-rmb:before,
+.fa-yen:before,
+.fa-jpy:before {
+  content: "\f157";
+}
+.fa-ruble:before,
+.fa-rouble:before,
+.fa-rub:before {
+  content: "\f158";
+}
+.fa-won:before,
+.fa-krw:before {
+  content: "\f159";
+}
+.fa-bitcoin:before,
+.fa-btc:before {
+  content: "\f15a";
+}
+.fa-file:before {
+  content: "\f15b";
+}
+.fa-file-text:before {
+  content: "\f15c";
+}
+.fa-sort-alpha-asc:before {
+  content: "\f15d";
+}
+.fa-sort-alpha-desc:before {
+  content: "\f15e";
+}
+.fa-sort-amount-asc:before {
+  content: "\f160";
+}
+.fa-sort-amount-desc:before {
+  content: "\f161";
+}
+.fa-sort-numeric-asc:before {
+  content: "\f162";
+}
+.fa-sort-numeric-desc:before {
+  content: "\f163";
+}
+.fa-thumbs-up:before {
+  content: "\f164";
+}
+.fa-thumbs-down:before {
+  content: "\f165";
+}
+.fa-youtube-square:before {
+  content: "\f166";
+}
+.fa-youtube:before {
+  content: "\f167";
+}
+.fa-xing:before {
+  content: "\f168";
+}
+.fa-xing-square:before {
+  content: "\f169";
+}
+.fa-youtube-play:before {
+  content: "\f16a";
+}
+.fa-dropbox:before {
+  content: "\f16b";
+}
+.fa-stack-overflow:before {
+  content: "\f16c";
+}
+.fa-instagram:before {
+  content: "\f16d";
+}
+.fa-flickr:before {
+  content: "\f16e";
+}
+.fa-adn:before {
+  content: "\f170";
+}
+.fa-bitbucket:before {
+  content: "\f171";
+}
+.fa-bitbucket-square:before {
+  content: "\f172";
+}
+.fa-tumblr:before {
+  content: "\f173";
+}
+.fa-tumblr-square:before {
+  content: "\f174";
+}
+.fa-long-arrow-down:before {
+  content: "\f175";
+}
+.fa-long-arrow-up:before {
+  content: "\f176";
+}
+.fa-long-arrow-left:before {
+  content: "\f177";
+}
+.fa-long-arrow-right:before {
+  content: "\f178";
+}
+.fa-apple:before {
+  content: "\f179";
+}
+.fa-windows:before {
+  content: "\f17a";
+}
+.fa-android:before {
+  content: "\f17b";
+}
+.fa-linux:before {
+  content: "\f17c";
+}
+.fa-dribbble:before {
+  content: "\f17d";
+}
+.fa-skype:before {
+  content: "\f17e";
+}
+.fa-foursquare:before {
+  content: "\f180";
+}
+.fa-trello:before {
+  content: "\f181";
+}
+.fa-female:before {
+  content: "\f182";
+}
+.fa-male:before {
+  content: "\f183";
+}
+.fa-gittip:before,
+.fa-gratipay:before {
+  content: "\f184";
+}
+.fa-sun-o:before {
+  content: "\f185";
+}
+.fa-moon-o:before {
+  content: "\f186";
+}
+.fa-archive:before {
+  content: "\f187";
+}
+.fa-bug:before {
+  content: "\f188";
+}
+.fa-vk:before {
+  content: "\f189";
+}
+.fa-weibo:before {
+  content: "\f18a";
+}
+.fa-renren:before {
+  content: "\f18b";
+}
+.fa-pagelines:before {
+  content: "\f18c";
+}
+.fa-stack-exchange:before {
+  content: "\f18d";
+}
+.fa-arrow-circle-o-right:before {
+  content: "\f18e";
+}
+.fa-arrow-circle-o-left:before {
+  content: "\f190";
+}
+.fa-toggle-left:before,
+.fa-caret-square-o-left:before {
+  content: "\f191";
+}
+.fa-dot-circle-o:before {
+  content: "\f192";
+}
+.fa-wheelchair:before {
+  content: "\f193";
+}
+.fa-vimeo-square:before {
+  content: "\f194";
+}
+.fa-turkish-lira:before,
+.fa-try:before {
+  content: "\f195";
+}
+.fa-plus-square-o:before {
+  content: "\f196";
+}
+.fa-space-shuttle:before {
+  content: "\f197";
+}
+.fa-slack:before {
+  content: "\f198";
+}
+.fa-envelope-square:before {
+  content: "\f199";
+}
+.fa-wordpress:before {
+  content: "\f19a";
+}
+.fa-openid:before {
+  content: "\f19b";
+}
+.fa-institution:before,
+.fa-bank:before,
+.fa-university:before {
+  content: "\f19c";
+}
+.fa-mortar-board:before,
+.fa-graduation-cap:before {
+  content: "\f19d";
+}
+.fa-yahoo:before {
+  content: "\f19e";
+}
+.fa-google:before {
+  content: "\f1a0";
+}
+.fa-reddit:before {
+  content: "\f1a1";
+}
+.fa-reddit-square:before {
+  content: "\f1a2";
+}
+.fa-stumbleupon-circle:before {
+  content: "\f1a3";
+}
+.fa-stumbleupon:before {
+  content: "\f1a4";
+}
+.fa-delicious:before {
+  content: "\f1a5";
+}
+.fa-digg:before {
+  content: "\f1a6";
+}
+.fa-pied-piper:before {
+  content: "\f1a7";
+}
+.fa-pied-piper-alt:before {
+  content: "\f1a8";
+}
+.fa-drupal:before {
+  content: "\f1a9";
+}
+.fa-joomla:before {
+  content: "\f1aa";
+}
+.fa-language:before {
+  content: "\f1ab";
+}
+.fa-fax:before {
+  content: "\f1ac";
+}
+.fa-building:before {
+  content: "\f1ad";
+}
+.fa-child:before {
+  content: "\f1ae";
+}
+.fa-paw:before {
+  content: "\f1b0";
+}
+.fa-spoon:before {
+  content: "\f1b1";
+}
+.fa-cube:before {
+  content: "\f1b2";
+}
+.fa-cubes:before {
+  content: "\f1b3";
+}
+.fa-behance:before {
+  content: "\f1b4";
+}
+.fa-behance-square:before {
+  content: "\f1b5";
+}
+.fa-steam:before {
+  content: "\f1b6";
+}
+.fa-steam-square:before {
+  content: "\f1b7";
+}
+.fa-recycle:before {
+  content: "\f1b8";
+}
+.fa-automobile:before,
+.fa-car:before {
+  content: "\f1b9";
+}
+.fa-cab:before,
+.fa-taxi:before {
+  content: "\f1ba";
+}
+.fa-tree:before {
+  content: "\f1bb";
+}
+.fa-spotify:before {
+  content: "\f1bc";
+}
+.fa-deviantart:before {
+  content: "\f1bd";
+}
+.fa-soundcloud:before {
+  content: "\f1be";
+}
+.fa-database:before {
+  content: "\f1c0";
+}
+.fa-file-pdf-o:before {
+  content: "\f1c1";
+}
+.fa-file-word-o:before {
+  content: "\f1c2";
+}
+.fa-file-excel-o:before {
+  content: "\f1c3";
+}
+.fa-file-powerpoint-o:before {
+  content: "\f1c4";
+}
+.fa-file-photo-o:before,
+.fa-file-picture-o:before,
+.fa-file-image-o:before {
+  content: "\f1c5";
+}
+.fa-file-zip-o:before,
+.fa-file-archive-o:before {
+  content: "\f1c6";
+}
+.fa-file-sound-o:before,
+.fa-file-audio-o:before {
+  content: "\f1c7";
+}
+.fa-file-movie-o:before,
+.fa-file-video-o:before {
+  content: "\f1c8";
+}
+.fa-file-code-o:before {
+  content: "\f1c9";
+}
+.fa-vine:before {
+  content: "\f1ca";
+}
+.fa-codepen:before {
+  content: "\f1cb";
+}
+.fa-jsfiddle:before {
+  content: "\f1cc";
+}
+.fa-life-bouy:before,
+.fa-life-buoy:before,
+.fa-life-saver:before,
+.fa-support:before,
+.fa-life-ring:before {
+  content: "\f1cd";
+}
+.fa-circle-o-notch:before {
+  content: "\f1ce";
+}
+.fa-ra:before,
+.fa-rebel:before {
+  content: "\f1d0";
+}
+.fa-ge:before,
+.fa-empire:before {
+  content: "\f1d1";
+}
+.fa-git-square:before {
+  content: "\f1d2";
+}
+.fa-git:before {
+  content: "\f1d3";
+}
+.fa-y-combinator-square:before,
+.fa-yc-square:before,
+.fa-hacker-news:before {
+  content: "\f1d4";
+}
+.fa-tencent-weibo:before {
+  content: "\f1d5";
+}
+.fa-qq:before {
+  content: "\f1d6";
+}
+.fa-wechat:before,
+.fa-weixin:before {
+  content: "\f1d7";
+}
+.fa-send:before,
+.fa-paper-plane:before {
+  content: "\f1d8";
+}
+.fa-send-o:before,
+.fa-paper-plane-o:before {
+  content: "\f1d9";
+}
+.fa-history:before {
+  content: "\f1da";
+}
+.fa-circle-thin:before {
+  content: "\f1db";
+}
+.fa-header:before {
+  content: "\f1dc";
+}
+.fa-paragraph:before {
+  content: "\f1dd";
+}
+.fa-sliders:before {
+  content: "\f1de";
+}
+.fa-share-alt:before {
+  content: "\f1e0";
+}
+.fa-share-alt-square:before {
+  content: "\f1e1";
+}
+.fa-bomb:before {
+  content: "\f1e2";
+}
+.fa-soccer-ball-o:before,
+.fa-futbol-o:before {
+  content: "\f1e3";
+}
+.fa-tty:before {
+  content: "\f1e4";
+}
+.fa-binoculars:before {
+  content: "\f1e5";
+}
+.fa-plug:before {
+  content: "\f1e6";
+}
+.fa-slideshare:before {
+  content: "\f1e7";
+}
+.fa-twitch:before {
+  content: "\f1e8";
+}
+.fa-yelp:before {
+  content: "\f1e9";
+}
+.fa-newspaper-o:before {
+  content: "\f1ea";
+}
+.fa-wifi:before {
+  content: "\f1eb";
+}
+.fa-calculator:before {
+  content: "\f1ec";
+}
+.fa-paypal:before {
+  content: "\f1ed";
+}
+.fa-google-wallet:before {
+  content: "\f1ee";
+}
+.fa-cc-visa:before {
+  content: "\f1f0";
+}
+.fa-cc-mastercard:before {
+  content: "\f1f1";
+}
+.fa-cc-discover:before {
+  content: "\f1f2";
+}
+.fa-cc-amex:before {
+  content: "\f1f3";
+}
+.fa-cc-paypal:before {
+  content: "\f1f4";
+}
+.fa-cc-stripe:before {
+  content: "\f1f5";
+}
+.fa-bell-slash:before {
+  content: "\f1f6";
+}
+.fa-bell-slash-o:before {
+  content: "\f1f7";
+}
+.fa-trash:before {
+  content: "\f1f8";
+}
+.fa-copyright:before {
+  content: "\f1f9";
+}
+.fa-at:before {
+  content: "\f1fa";
+}
+.fa-eyedropper:before {
+  content: "\f1fb";
+}
+.fa-paint-brush:before {
+  content: "\f1fc";
+}
+.fa-birthday-cake:before {
+  content: "\f1fd";
+}
+.fa-area-chart:before {
+  content: "\f1fe";
+}
+.fa-pie-chart:before {
+  content: "\f200";
+}
+.fa-line-chart:before {
+  content: "\f201";
+}
+.fa-lastfm:before {
+  content: "\f202";
+}
+.fa-lastfm-square:before {
+  content: "\f203";
+}
+.fa-toggle-off:before {
+  content: "\f204";
+}
+.fa-toggle-on:before {
+  content: "\f205";
+}
+.fa-bicycle:before {
+  content: "\f206";
+}
+.fa-bus:before {
+  content: "\f207";
+}
+.fa-ioxhost:before {
+  content: "\f208";
+}
+.fa-angellist:before {
+  content: "\f209";
+}
+.fa-cc:before {
+  content: "\f20a";
+}
+.fa-shekel:before,
+.fa-sheqel:before,
+.fa-ils:before {
+  content: "\f20b";
+}
+.fa-meanpath:before {
+  content: "\f20c";
+}
+.fa-buysellads:before {
+  content: "\f20d";
+}
+.fa-connectdevelop:before {
+  content: "\f20e";
+}
+.fa-dashcube:before {
+  content: "\f210";
+}
+.fa-forumbee:before {
+  content: "\f211";
+}
+.fa-leanpub:before {
+  content: "\f212";
+}
+.fa-sellsy:before {
+  content: "\f213";
+}
+.fa-shirtsinbulk:before {
+  content: "\f214";
+}
+.fa-simplybuilt:before {
+  content: "\f215";
+}
+.fa-skyatlas:before {
+  content: "\f216";
+}
+.fa-cart-plus:before {
+  content: "\f217";
+}
+.fa-cart-arrow-down:before {
+  content: "\f218";
+}
+.fa-diamond:before {
+  content: "\f219";
+}
+.fa-ship:before {
+  content: "\f21a";
+}
+.fa-user-secret:before {
+  content: "\f21b";
+}
+.fa-motorcycle:before {
+  content: "\f21c";
+}
+.fa-street-view:before {
+  content: "\f21d";
+}
+.fa-heartbeat:before {
+  content: "\f21e";
+}
+.fa-venus:before {
+  content: "\f221";
+}
+.fa-mars:before {
+  content: "\f222";
+}
+.fa-mercury:before {
+  content: "\f223";
+}
+.fa-intersex:before,
+.fa-transgender:before {
+  content: "\f224";
+}
+.fa-transgender-alt:before {
+  content: "\f225";
+}
+.fa-venus-double:before {
+  content: "\f226";
+}
+.fa-mars-double:before {
+  content: "\f227";
+}
+.fa-venus-mars:before {
+  content: "\f228";
+}
+.fa-mars-stroke:before {
+  content: "\f229";
+}
+.fa-mars-stroke-v:before {
+  content: "\f22a";
+}
+.fa-mars-stroke-h:before {
+  content: "\f22b";
+}
+.fa-neuter:before {
+  content: "\f22c";
+}
+.fa-genderless:before {
+  content: "\f22d";
+}
+.fa-facebook-official:before {
+  content: "\f230";
+}
+.fa-pinterest-p:before {
+  content: "\f231";
+}
+.fa-whatsapp:before {
+  content: "\f232";
+}
+.fa-server:before {
+  content: "\f233";
+}
+.fa-user-plus:before {
+  content: "\f234";
+}
+.fa-user-times:before {
+  content: "\f235";
+}
+.fa-hotel:before,
+.fa-bed:before {
+  content: "\f236";
+}
+.fa-viacoin:before {
+  content: "\f237";
+}
+.fa-train:before {
+  content: "\f238";
+}
+.fa-subway:before {
+  content: "\f239";
+}
+.fa-medium:before {
+  content: "\f23a";
+}
+.fa-yc:before,
+.fa-y-combinator:before {
+  content: "\f23b";
+}
+.fa-optin-monster:before {
+  content: "\f23c";
+}
+.fa-opencart:before {
+  content: "\f23d";
+}
+.fa-expeditedssl:before {
+  content: "\f23e";
+}
+.fa-battery-4:before,
+.fa-battery-full:before {
+  content: "\f240";
+}
+.fa-battery-3:before,
+.fa-battery-three-quarters:before {
+  content: "\f241";
+}
+.fa-battery-2:before,
+.fa-battery-half:before {
+  content: "\f242";
+}
+.fa-battery-1:before,
+.fa-battery-quarter:before {
+  content: "\f243";
+}
+.fa-battery-0:before,
+.fa-battery-empty:before {
+  content: "\f244";
+}
+.fa-mouse-pointer:before {
+  content: "\f245";
+}
+.fa-i-cursor:before {
+  content: "\f246";
+}
+.fa-object-group:before {
+  content: "\f247";
+}
+.fa-object-ungroup:before {
+  content: "\f248";
+}
+.fa-sticky-note:before {
+  content: "\f249";
+}
+.fa-sticky-note-o:before {
+  content: "\f24a";
+}
+.fa-cc-jcb:before {
+  content: "\f24b";
+}
+.fa-cc-diners-club:before {
+  content: "\f24c";
+}
+.fa-clone:before {
+  content: "\f24d";
+}
+.fa-balance-scale:before {
+  content: "\f24e";
+}
+.fa-hourglass-o:before {
+  content: "\f250";
+}
+.fa-hourglass-1:before,
+.fa-hourglass-start:before {
+  content: "\f251";
+}
+.fa-hourglass-2:before,
+.fa-hourglass-half:before {
+  content: "\f252";
+}
+.fa-hourglass-3:before,
+.fa-hourglass-end:before {
+  content: "\f253";
+}
+.fa-hourglass:before {
+  content: "\f254";
+}
+.fa-hand-grab-o:before,
+.fa-hand-rock-o:before {
+  content: "\f255";
+}
+.fa-hand-stop-o:before,
+.fa-hand-paper-o:before {
+  content: "\f256";
+}
+.fa-hand-scissors-o:before {
+  content: "\f257";
+}
+.fa-hand-lizard-o:before {
+  content: "\f258";
+}
+.fa-hand-spock-o:before {
+  content: "\f259";
+}
+.fa-hand-pointer-o:before {
+  content: "\f25a";
+}
+.fa-hand-peace-o:before {
+  content: "\f25b";
+}
+.fa-trademark:before {
+  content: "\f25c";
+}
+.fa-registered:before {
+  content: "\f25d";
+}
+.fa-creative-commons:before {
+  content: "\f25e";
+}
+.fa-gg:before {
+  content: "\f260";
+}
+.fa-gg-circle:before {
+  content: "\f261";
+}
+.fa-tripadvisor:before {
+  content: "\f262";
+}
+.fa-odnoklassniki:before {
+  content: "\f263";
+}
+.fa-odnoklassniki-square:before {
+  content: "\f264";
+}
+.fa-get-pocket:before {
+  content: "\f265";
+}
+.fa-wikipedia-w:before {
+  content: "\f266";
+}
+.fa-safari:before {
+  content: "\f267";
+}
+.fa-chrome:before {
+  content: "\f268";
+}
+.fa-firefox:before {
+  content: "\f269";
+}
+.fa-opera:before {
+  content: "\f26a";
+}
+.fa-internet-explorer:before {
+  content: "\f26b";
+}
+.fa-tv:before,
+.fa-television:before {
+  content: "\f26c";
+}
+.fa-contao:before {
+  content: "\f26d";
+}
+.fa-500px:before {
+  content: "\f26e";
+}
+.fa-amazon:before {
+  content: "\f270";
+}
+.fa-calendar-plus-o:before {
+  content: "\f271";
+}
+.fa-calendar-minus-o:before {
+  content: "\f272";
+}
+.fa-calendar-times-o:before {
+  content: "\f273";
+}
+.fa-calendar-check-o:before {
+  content: "\f274";
+}
+.fa-industry:before {
+  content: "\f275";
+}
+.fa-map-pin:before {
+  content: "\f276";
+}
+.fa-map-signs:before {
+  content: "\f277";
+}
+.fa-map-o:before {
+  content: "\f278";
+}
+.fa-map:before {
+  content: "\f279";
+}
+.fa-commenting:before {
+  content: "\f27a";
+}
+.fa-commenting-o:before {
+  content: "\f27b";
+}
+.fa-houzz:before {
+  content: "\f27c";
+}
+.fa-vimeo:before {
+  content: "\f27d";
+}
+.fa-black-tie:before {
+  content: "\f27e";
+}
+.fa-fonticons:before {
+  content: "\f280";
+}
+.fa-reddit-alien:before {
+  content: "\f281";
+}
+.fa-edge:before {
+  content: "\f282";
+}
+.fa-credit-card-alt:before {
+  content: "\f283";
+}
+.fa-codiepie:before {
+  content: "\f284";
+}
+.fa-modx:before {
+  content: "\f285";
+}
+.fa-fort-awesome:before {
+  content: "\f286";
+}
+.fa-usb:before {
+  content: "\f287";
+}
+.fa-product-hunt:before {
+  content: "\f288";
+}
+.fa-mixcloud:before {
+  content: "\f289";
+}
+.fa-scribd:before {
+  content: "\f28a";
+}
+.fa-pause-circle:before {
+  content: "\f28b";
+}
+.fa-pause-circle-o:before {
+  content: "\f28c";
+}
+.fa-stop-circle:before {
+  content: "\f28d";
+}
+.fa-stop-circle-o:before {
+  content: "\f28e";
+}
+.fa-shopping-bag:before {
+  content: "\f290";
+}
+.fa-shopping-basket:before {
+  content: "\f291";
+}
+.fa-hashtag:before {
+  content: "\f292";
+}
+.fa-bluetooth:before {
+  content: "\f293";
+}
+.fa-bluetooth-b:before {
+  content: "\f294";
+}
+.fa-percent:before {
+  content: "\f295";
+}
diff --git a/jetty-documentation/src/main/docbkx-resources/css/font-awesome/font-awesome.css.map b/jetty-documentation/src/main/docbkx-resources/css/font-awesome/font-awesome.css.map
new file mode 100755
index 0000000..60763a8
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/css/font-awesome/font-awesome.css.map
@@ -0,0 +1,7 @@
+{
+"version": 3,
+"mappings": ";;;;;;;AAGA,UAUC;EATC,WAAW,EAAE,aAAa;EAC1B,GAAG,EAAE,+CAAgE;EACrE,GAAG,EAAE,ySAAmG;EAKxG,WAAW,EAAE,MAAM;EACnB,UAAU,EAAE,MAAM;ACTpB,GAAmB;EACjB,OAAO,EAAE,YAAY;EACrB,IAAI,EAAE,uCAAwD;EAC9D,SAAS,EAAE,OAAO;EAClB,cAAc,EAAE,IAAI;EACpB,sBAAsB,EAAE,WAAW;EACnC,uBAAuB,EAAE,SAAS;EAClC,SAAS,EAAE,eAAe;;;ACN5B,MAAsB;EACpB,SAAS,EAAE,SAAS;EACpB,WAAW,EAAE,MAAS;EACtB,cAAc,EAAE,IAAI;;AAEtB,MAAsB;EAAE,SAAS,EAAE,GAAG;;AACtC,MAAsB;EAAE,SAAS,EAAE,GAAG;;AACtC,MAAsB;EAAE,SAAS,EAAE,GAAG;;AACtC,MAAsB;EAAE,SAAS,EAAE,GAAG;;ACVtC,MAAsB;EACpB,KAAK,EAAE,SAAW;EAClB,UAAU,EAAE,MAAM;;ACDpB,MAAsB;EACpB,YAAY,EAAE,CAAC;EACf,WAAW,ECKU,SAAS;EDJ9B,eAAe,EAAE,IAAI;EACrB,WAAK;IAAE,QAAQ,EAAE,QAAQ;;AAE3B,MAAsB;EACpB,QAAQ,EAAE,QAAQ;EAClB,IAAI,EAAE,UAAa;EACnB,KAAK,ECFgB,SAAS;EDG9B,GAAG,EAAE,SAAU;EACf,UAAU,EAAE,MAAM;EAClB,YAAuB;IACrB,IAAI,EAAE,UAA0B;;AEbpC,UAA0B;EACxB,OAAO,EAAE,gBAAgB;EACzB,MAAM,EAAE,iBAA4B;EACpC,aAAa,EAAE,IAAI;;AAGrB,WAAY;EAAE,KAAK,EAAE,KAAK;;AAC1B,UAAW;EAAE,KAAK,EAAE,IAAI;;AAGtB,aAAY;EAAE,YAAY,EAAE,IAAI;AAChC,cAAa;EAAE,WAAW,EAAE,IAAI;;ACXlC,QAAwB;EACtB,iBAAiB,EAAE,0BAA0B;EACrC,SAAS,EAAE,0BAA0B;;AAG/C,SAAyB;EACvB,iBAAiB,EAAE,4BAA4B;EACvC,SAAS,EAAE,4BAA4B;;AAGjD,0BASC;EARC,EAAG;IACD,iBAAiB,EAAE,YAAY;IACvB,SAAS,EAAE,YAAY;EAEjC,IAAK;IACH,iBAAiB,EAAE,cAAc;IACzB,SAAS,EAAE,cAAc;AAIrC,kBASC;EARC,EAAG;IACD,iBAAiB,EAAE,YAAY;IACvB,SAAS,EAAE,YAAY;EAEjC,IAAK;IACH,iBAAiB,EAAE,cAAc;IACzB,SAAS,EAAE,cAAc;AC5BrC,aAA8B;ECY5B,MAAM,EAAE,wDAAmE;EAC3E,iBAAiB,EAAE,aAAgB;EAC/B,aAAa,EAAE,aAAgB;EAC3B,SAAS,EAAE,aAAgB;;ADdrC,cAA8B;ECW5B,MAAM,EAAE,wDAAmE;EAC3E,iBAAiB,EAAE,cAAgB;EAC/B,aAAa,EAAE,cAAgB;EAC3B,SAAS,EAAE,cAAgB;;ADbrC,cAA8B;ECU5B,MAAM,EAAE,wDAAmE;EAC3E,iBAAiB,EAAE,cAAgB;EAC/B,aAAa,EAAE,cAAgB;EAC3B,SAAS,EAAE,cAAgB;;ADXrC,mBAAmC;ECejC,MAAM,EAAE,wDAAmE;EAC3E,iBAAiB,EAAE,YAAoB;EACnC,aAAa,EAAE,YAAoB;EAC/B,SAAS,EAAE,YAAoB;;ADjBzC,iBAAmC;ECcjC,MAAM,EAAE,wDAAmE;EAC3E,iBAAiB,EAAE,YAAoB;EACnC,aAAa,EAAE,YAAoB;EAC/B,SAAS,EAAE,YAAoB;;ADZzC;;;;uBAIuC;EACrC,MAAM,EAAE,IAAI;;AEfd,SAAyB;EACvB,QAAQ,EAAE,QAAQ;EAClB,OAAO,EAAE,YAAY;EACrB,KAAK,EAAE,GAAG;EACV,MAAM,EAAE,GAAG;EACX,WAAW,EAAE,GAAG;EAChB,cAAc,EAAE,MAAM;;AAExB,0BAAyD;EACvD,QAAQ,EAAE,QAAQ;EAClB,IAAI,EAAE,CAAC;EACP,KAAK,EAAE,IAAI;EACX,UAAU,EAAE,MAAM;;AAEpB,YAA4B;EAAE,WAAW,EAAE,OAAO;;AAClD,YAA4B;EAAE,SAAS,EAAE,GAAG;;AAC5C,WAA2B;EAAE,KAAK,ELVZ,IAAI;;;;AMN1B,gBAAgC;EAAE,OAAO,ENoQ1B,GAAO;;AMnQtB,gBAAgC;EAAE,OAAO,EN0W1B,GAAO;;AMzWtB,iBAAiC;EAAE,OAAO,ENmb1B,GAAO;;AMlbvB,qBAAqC;EAAE,OAAO,ENmL1B,GAAO;;AMlL3B,gBAAgC;EAAE,OAAO,ENkR1B,GAAO;;AMjRtB,eAA+B;EAAE,OAAO,ENke1B,GAAO;;AMjerB,iBAAiC;EAAE,OAAO,ENse1B,GAAO;;AMrevB,eAA+B;EAAE,OAAO,EN+iB1B,GAAO;;AM9iBrB,eAA+B;EAAE,OAAO,ENyN1B,GAAO;;AMxNrB,mBAAmC;EAAE,OAAO,ENggB1B,GAAO;;AM/fzB,aAA6B;EAAE,OAAO,EN8f1B,GAAO;;AM7fnB,kBAAkC;EAAE,OAAO,EN+f1B,GAAO;;AM9fxB,gBAAgC;EAAE,OAAO,ENoG1B,GAAO;;AMnGtB;;gBAEgC;EAAE,OAAO,ENkgB1B,GAAO;;AMjgBtB,sBAAsC;EAAE,OAAO,ENua1B,GAAO;;AMta5B,uBAAuC;EAAE,OAAO,ENqa1B,GAAO;;AMpa7B,oBAAoC;EAAE,OAAO,EN+X1B,GAAO;;AM9X1B,iBAAiC;EAAE,OAAO,ENsb1B,GAAO;;AMrbvB;cAC8B;EAAE,OAAO,ENwH1B,GAAO;;AMvHpB,kBAAkC;EAAE,OAAO,ENygB1B,GAAO;;AMxgBxB,eAA+B;EAAE,OAAO,ENmQ1B,GAAO;;AMlQrB,iBAAiC;EAAE,OAAO,EN6L1B,GAAO;;AM5LvB,kBAAkC;EAAE,OAAO,EN0G1B,GAAO;;AMzGxB,eAA+B;EAAE,OAAO,EN+Y1B,GAAO;;AM9YrB,mBAAmC;EAAE,OAAO,ENiJ1B,GAAO;;AMhJzB,8BAA8C;EAAE,OAAO,ENI1B,GAAO;;AMHpC,4BAA4C;EAAE,OAAO,ENM1B,GAAO;;AMLlC,gBAAgC;EAAE,OAAO,ENkQ1B,GAAO;;AMjQtB,wBAAwC;EAAE,OAAO,EN4W1B,GAAO;;AM3W9B;iBACiC;EAAE,OAAO,ENmY1B,GAAO;;AMlYvB,kBAAkC;EAAE,OAAO,EN8X1B,GAAO;;AM7XxB,mBAAmC;EAAE,OAAO,ENiS1B,GAAO;;AMhSzB,eAA+B;EAAE,OAAO,ENoS1B,GAAO;;AMnSrB,eAA+B;EAAE,OAAO,ENgM1B,GAAO;;AM/LrB,qBAAqC;EAAE,OAAO,EN+O1B,GAAO;;AM9O3B,qBAAqC;EAAE,OAAO,EN8hB1B,GAAO;;AM7hB3B,sBAAsC;EAAE,OAAO,EN4hB1B,GAAO;;AM3hB5B,oBAAoC;EAAE,OAAO,EN6hB1B,GAAO;;AM5hB1B,iBAAiC;EAAE,OAAO,EN2W1B,GAAO;;AM1WvB,kBAAkC;EAAE,OAAO,ENW1B,GAAO;;AMVxB,cAA8B;EAAE,OAAO,ENod1B,GAAO;;AMndpB,eAA+B;EAAE,OAAO,ENod1B,GAAO;;AMndrB,eAA+B;EAAE,OAAO,EN2B1B,GAAO;;AM1BrB,mBAAmC;EAAE,OAAO,EN2B1B,GAAO;;AM1BzB,gBAAgC;EAAE,OAAO,ENkW1B,GAAO;;AMjWtB,iBAAiC;EAAE,OAAO,ENwC1B,GAAO;;AMvCvB,eAA+B;EAAE,OAAO,EN8L1B,GAAO;;AM7LrB,eAA+B;EAAE,OAAO,ENmB1B,GAAO;;AMlBrB,iBAAiC;EAAE,OAAO,ENoP1B,GAAO;;AMnPvB,sBAAsC;EAAE,OAAO,ENid1B,GAAO;;AMhd5B,qBAAqC;EAAE,OAAO,ENid1B,GAAO;;AMhd3B,qBAAqC;EAAE,OAAO,EN1C1B,GAAO;;AM2C3B,uBAAuC;EAAE,OAAO,EN7C1B,GAAO;;AM8C7B,sBAAsC;EAAE,OAAO,EN3C1B,GAAO;;AM4C5B,wBAAwC;EAAE,OAAO,EN9C1B,GAAO;;AM+C9B,eAA+B;EAAE,OAAO,ENwQ1B,GAAO;;AMvQrB;kBACkC;EAAE,OAAO,ENmT1B,GAAO;;AMlTxB,iBAAiC;EAAE,OAAO,ENmO1B,GAAO;;AMlOvB,uBAAuC;EAAE,OAAO,ENigB1B,GAAO;;AMhgB7B;;oBAEoC;EAAE,OAAO,EN+T1B,GAAO;;AM9T1B,iBAAiC;EAAE,OAAO,ENwT1B,GAAO;;AMvTvB,qBAAqC;EAAE,OAAO,EN+Q1B,GAAO;;AM9Q3B,iBAAiC;EAAE,OAAO,EN5D1B,GAAO;;AM6DvB,eAA+B;EAAE,OAAO,EN8c1B,GAAO;;AM7crB;0BAC0C;EAAE,OAAO,ENqT1B,GAAO;;AMpThC,yBAAyC;EAAE,OAAO,ENuX1B,GAAO;;AMtX/B,yBAAyC;EAAE,OAAO,EN0C1B,GAAO;;AMzC/B,iBAAiC;EAAE,OAAO,ENjC1B,GAAO;;AMkCvB,wBAAwC;EAAE,OAAO,ENma1B,GAAO;;AMla9B,wBAAwC;EAAE,OAAO,EN4H1B,GAAO;;AM3H9B,mBAAmC;EAAE,OAAO,EN7B1B,GAAO;;AM8BzB,eAA+B;EAAE,OAAO,EN0T1B,GAAO;;AMzTrB,gBAAgC;EAAE,OAAO,ENwS1B,GAAO;;AMvStB,eAA+B;EAAE,OAAO,ENia1B,GAAO;;AMharB,kBAAkC;EAAE,OAAO,ENgK1B,GAAO;;AM/JxB,uBAAuC;EAAE,OAAO,ENuH1B,GAAO;;AMtH7B,uBAAuC;EAAE,OAAO,EN4Z1B,GAAO;;AM3Z7B,gBAAgC;EAAE,OAAO,EN4F1B,GAAO;;AM3FtB,uBAAuC;EAAE,OAAO,ENoC1B,GAAO;;AMnC7B,wBAAwC;EAAE,OAAO,ENoC1B,GAAO;;AMnC9B,sBAAsC;EAAE,OAAO,ENsT1B,GAAO;;AMrT5B,uBAAuC;EAAE,OAAO,ENyQ1B,GAAO;;AMxQ7B,uBAAuC;EAAE,OAAO,ENwb1B,GAAO;;AMvb7B,uBAAuC;EAAE,OAAO,ENsB1B,GAAO;;AMrB7B,0BAA0C;EAAE,OAAO,EN2T1B,GAAO;;AM1ThC,sBAAsC;EAAE,OAAO,ENsM1B,GAAO;;AMrM5B,qBAAqC;EAAE,OAAO,EN6D1B,GAAO;;AM5D3B,yBAAyC;EAAE,OAAO,ENob1B,GAAO;;AMnb/B,yBAAyC;EAAE,OAAO,ENkB1B,GAAO;;AMjB/B,cAA8B;EAAE,OAAO,EN/C1B,GAAO;;AMgDpB,qBAAqC;EAAE,OAAO,EN3D1B,GAAO;;AM4D3B,sBAAsC;EAAE,OAAO,EN3D1B,GAAO;;AM4D5B,mBAAmC;EAAE,OAAO,EN3D1B,GAAO;;AM4DzB,qBAAqC;EAAE,OAAO,EN/D1B,GAAO;;AMgE3B;gBACgC;EAAE,OAAO,ENqV1B,GAAO;;AMpVtB,iBAAiC;EAAE,OAAO,ENuF1B,GAAO;;AMtFvB,mBAAmC;EAAE,OAAO,EN4C1B,GAAO;;AM3CzB,eAA+B;EAAE,OAAO,ENmS1B,GAAO;;AMlSrB,gBAAgC;EAAE,OAAO,ENsP1B,GAAO;;AMrPtB,mBAAmC;EAAE,OAAO,EN9D1B,GAAO;;AM+DzB,6BAA6C;EAAE,OAAO,ENgF1B,GAAO;;AM/EnC,eAA+B;EAAE,OAAO,EN+I1B,GAAO;;AM9IrB,eAA+B;EAAE,OAAO,ENoM1B,GAAO;;AMnMrB,eAA+B;EAAE,OAAO,ENmH1B,GAAO;;AMlHrB,cAA8B;EAAE,OAAO,ENiF1B,GAAO;;AMhFpB,oBAAoC;EAAE,OAAO,ENiF1B,GAAO;;AMhF1B;+BAC+C;EAAE,OAAO,EN0E1B,GAAO;;AMzErC,gBAAgC;EAAE,OAAO,ENmR1B,GAAO;;AMlRtB,mBAAmC;EAAE,OAAO,EN/B1B,GAAO;;AMgCzB,iBAAiC;EAAE,OAAO,ENoS1B,GAAO;;AMnSvB,kBAAkC;EAAE,OAAO,ENwB1B,GAAO;;AMvBxB,iBAAiC;EAAE,OAAO,ENqN1B,GAAO;;AMpNvB,qBAAqC;EAAE,OAAO,ENE1B,GAAO;;AMD3B,uBAAuC;EAAE,OAAO,ENF1B,GAAO;;AMG7B,kBAAkC;EAAE,OAAO,EN2S1B,GAAO;;AM1SxB,wBAAwC;EAAE,OAAO,ENyU1B,GAAO;;AMxU9B,iBAAiC;EAAE,OAAO,EN8G1B,GAAO;;AM7GvB,sBAAsC;EAAE,OAAO,EN+G1B,GAAO;;AM9G5B,mBAAmC;EAAE,OAAO,ENnF1B,GAAO;;AMoFzB,mBAAmC;EAAE,OAAO,ENrF1B,GAAO;;AMsFzB;oBACoC;EAAE,OAAO,EN/E1B,GAAO;;AMgF1B,yBAAyC;EAAE,OAAO,ENua1B,GAAO;;AMta/B,0BAA0C;EAAE,OAAO,ENmE1B,GAAO;;AMlEhC,uBAAuC;EAAE,OAAO,EN5C1B,GAAO;;AM6C7B,cAA8B;EAAE,OAAO,ENqK1B,GAAO;;AMpKpB;eAC+B;EAAE,OAAO,ENK1B,GAAO;;AMJrB,mBAAmC;EAAE,OAAO,ENQ1B,GAAO;;AMPzB,sBAAsC;EAAE,OAAO,ENmY1B,GAAO;;AMlY5B,wBAAwC;EAAE,OAAO,ENiY1B,GAAO;;AMhY9B,oBAAoC;EAAE,OAAO,EN2V1B,GAAO;;AM1V1B,kBAAkC;EAAE,OAAO,ENyI1B,GAAO;;AMxIxB,mBAAmC;EAAE,OAAO,ENyT1B,GAAO;;AMxTzB,0BAA0C;EAAE,OAAO,ENiL1B,GAAO;;AMhLhC,qBAAqC;EAAE,OAAO,EN0X1B,GAAO;;AMzX3B,wBAAwC;EAAE,OAAO,EN8C1B,GAAO;;AM7C9B,kBAAkC;EAAE,OAAO,ENoT1B,GAAO;;AMnTxB,iBAAiC;EAAE,OAAO,EN8Y1B,GAAO;;AM7YvB,wBAAwC;EAAE,OAAO,EN6G1B,GAAO;;AM5G9B,iBAAiC;EAAE,OAAO,EN8Z1B,GAAO;;AM7ZvB,kBAAkC;EAAE,OAAO,EN+J1B,GAAO;;AM9JxB,gBAAgC;EAAE,OAAO,ENsO1B,GAAO;;AMrOtB,mBAAmC;EAAE,OAAO,EN2U1B,GAAO;;AM1UzB,qBAAqC;EAAE,OAAO,EN/E1B,GAAO;;AMgF3B,uBAAuC;EAAE,OAAO,ENoO1B,GAAO;;AMnO7B,kBAAkC;EAAE,OAAO,EN8Y1B,GAAO;;AM7YxB;mBACmC;EAAE,OAAO,ENuC1B,GAAO;;AMtCzB,iBAAiC;EAAE,OAAO,ENiG1B,GAAO;;AMhGvB,iBAAiC;EAAE,OAAO,ENiZ1B,GAAO;;AMhZvB,sBAAsC;EAAE,OAAO,ENR1B,GAAO;;AMS5B,cAA8B;EAAE,OAAO,EN4Q1B,GAAO;;AM3QpB,gBAAgC;EAAE,OAAO,ENgH1B,GAAO;;AM/GtB,mBAAmC;EAAE,OAAO,ENnF1B,GAAO;;AMoFzB,eAA+B;EAAE,OAAO,ENzG1B,GAAO;;AM0GrB,sBAAsC;EAAE,OAAO,ENzD1B,GAAO;;AM0D5B,uBAAuC;EAAE,OAAO,EN0G1B,GAAO;;AMzG7B,sBAAsC;EAAE,OAAO,ENwG1B,GAAO;;AMvG5B,oBAAoC;EAAE,OAAO,ENyG1B,GAAO;;AMxG1B,sBAAsC;EAAE,OAAO,ENqG1B,GAAO;;AMpG5B,4BAA4C;EAAE,OAAO,EN5I1B,GAAO;;AM6IlC,6BAA6C;EAAE,OAAO,ENxI1B,GAAO;;AMyInC,0BAA0C;EAAE,OAAO,ENxI1B,GAAO;;AMyIhC,4BAA4C;EAAE,OAAO,ENhJ1B,GAAO;;AMiJlC,gBAAgC;EAAE,OAAO,ENsF1B,GAAO;;AMrFtB,iBAAiC;EAAE,OAAO,ENia1B,GAAO;;AMhavB,gBAAgC;EAAE,OAAO,ENiV1B,GAAO;;AMhVtB,iBAAiC;EAAE,OAAO,ENgD1B,GAAO;;AM/CvB,oBAAoC;EAAE,OAAO,ENvG1B,GAAO;;AMwG1B,qBAAqC;EAAE,OAAO,ENzI1B,GAAO;;AM0I3B;gBACgC;EAAE,OAAO,ENqY1B,GAAO;;AMpYtB;eAC+B;EAAE,OAAO,ENuI1B,GAAO;;AMtIrB,gBAAgC;EAAE,OAAO,ENpD1B,GAAO;;AMqDtB,gBAAgC;EAAE,OAAO,EN+C1B,GAAO;;AM9CtB;mBACmC;EAAE,OAAO,ENwP1B,GAAO;;AMvPzB;kBACkC;EAAE,OAAO,ENkC1B,GAAO;;AMjCxB,oBAAoC;EAAE,OAAO,ENsL1B,GAAO;;AMrL1B;mBACmC;EAAE,OAAO,EN0C1B,GAAO;;AMzCzB,iBAAiC;EAAE,OAAO,ENiS1B,GAAO;;AMhSvB;;eAE+B;EAAE,OAAO,EN9I1B,GAAO;;AM+IrB,kBAAkC;EAAE,OAAO,ENgI1B,GAAO;;AM/HxB,kBAAkC;EAAE,OAAO,EN8H1B,GAAO;;AM7HxB,wBAAwC;EAAE,OAAO,EN4S1B,GAAO;;AM3S9B,oBAAoC;EAAE,OAAO,ENoW1B,GAAO;;AMnW1B,gBAAgC;EAAE,OAAO,ENmT1B,GAAO;;AMlTtB,gBAAgC;EAAE,OAAO,ENkI1B,GAAO;;AMjItB,gBAAgC;EAAE,OAAO,ENuV1B,GAAO;;AMtVtB,oBAAoC;EAAE,OAAO,ENwL1B,GAAO;;AMvL1B,2BAA2C;EAAE,OAAO,ENyL1B,GAAO;;AMxLjC,6BAA6C;EAAE,OAAO,ENyD1B,GAAO;;AMxDnC,sBAAsC;EAAE,OAAO,ENuD1B,GAAO;;AMtD5B,gBAAgC;EAAE,OAAO,ENsJ1B,GAAO;;AMrJtB,qBAAqC;EAAE,OAAO,ENtH1B,GAAO;;AMuH3B,mBAAmC;EAAE,OAAO,ENhH1B,GAAO;;AMiHzB,qBAAqC;EAAE,OAAO,ENvH1B,GAAO;;AMwH3B,sBAAsC;EAAE,OAAO,ENvH1B,GAAO;;AMwH5B,kBAAkC;EAAE,OAAO,ENvE1B,GAAO;;AMwExB;eAC+B;EAAE,OAAO,EN2P1B,GAAO;;AM1PrB;oBACoC;EAAE,OAAO,EN+P1B,GAAO;;AM9P1B;mBACmC;EAAE,OAAO,EN4P1B,GAAO;;AM3PzB,mBAAmC;EAAE,OAAO,ENxC1B,GAAO;;AMyCzB,mBAAmC;EAAE,OAAO,ENkG1B,GAAO;;AMjGzB;eAC+B;EAAE,OAAO,EN8U1B,GAAO;;AM7UrB;gBACgC;EAAE,OAAO,ENqB1B,GAAO;;AMpBtB;qBACqC;EAAE,OAAO,EN2R1B,GAAO;;AM1R3B,oBAAoC;EAAE,OAAO,ENpF1B,GAAO;;AMqF1B,qBAAqC;EAAE,OAAO,ENnF1B,GAAO;;AMoF3B;eAC+B;EAAE,OAAO,ENjK1B,GAAO;;AMkKrB,kBAAkC;EAAE,OAAO,ENkO1B,GAAO;;AMjOxB,mBAAmC;EAAE,OAAO,ENkU1B,GAAO;;AMjUzB;oBACoC;EAAE,OAAO,EN1G1B,GAAO;;AM2G1B,sBAAsC;EAAE,OAAO,ENgF1B,GAAO;;AM/E5B,mBAAmC;EAAE,OAAO,ENnD1B,GAAO;;AMoDzB,yBAAyC;EAAE,OAAO,ENzG1B,GAAO;;AM0G/B,uBAAuC;EAAE,OAAO,ENzG1B,GAAO;;AM0G7B,kBAAkC;EAAE,OAAO,ENsU1B,GAAO;;AMrUxB,sBAAsC;EAAE,OAAO,EN+P1B,GAAO;;AM9P5B,mBAAmC;EAAE,OAAO,ENsQ1B,GAAO;;AMrQzB,iBAAiC;EAAE,OAAO,ENvL1B,GAAO;;AMwLvB,iBAAiC;EAAE,OAAO,ENzG1B,GAAO;;AM0GvB,kBAAkC;EAAE,OAAO,ENtF1B,GAAO;;AMuFxB,sBAAsC;EAAE,OAAO,EN3B1B,GAAO;;AM4B5B,qBAAqC;EAAE,OAAO,ENxK1B,GAAO;;AMyK3B,qBAAqC;EAAE,OAAO,ENkC1B,GAAO;;AMjC3B,oBAAoC;EAAE,OAAO,EN3O1B,GAAO;;AM4O1B,iBAAiC;EAAE,OAAO,ENiG1B,GAAO;;AMhGvB,sBAAsC;EAAE,OAAO,EN/C1B,GAAO;;AMgD5B,eAA+B;EAAE,OAAO,ENpM1B,GAAO;;AMqMrB,mBAAmC;EAAE,OAAO,ENe1B,GAAO;;AMdzB,sBAAsC;EAAE,OAAO,ENgJ1B,GAAO;;AM/I5B,4BAA4C;EAAE,OAAO,EN5O1B,GAAO;;AM6OlC,6BAA6C;EAAE,OAAO,EN5O1B,GAAO;;AM6OnC,0BAA0C;EAAE,OAAO,EN5O1B,GAAO;;AM6OhC,4BAA4C;EAAE,OAAO,ENhP1B,GAAO;;AMiPlC,qBAAqC;EAAE,OAAO,EN5O1B,GAAO;;AM6O3B,sBAAsC;EAAE,OAAO,EN5O1B,GAAO;;AM6O5B,mBAAmC;EAAE,OAAO,EN5O1B,GAAO;;AM6OzB,qBAAqC;EAAE,OAAO,ENhP1B,GAAO;;AMiP3B,kBAAkC;EAAE,OAAO,ENlG1B,GAAO;;AMmGxB,iBAAiC;EAAE,OAAO,ENuC1B,GAAO;;AMtCvB,iBAAiC;EAAE,OAAO,ENoP1B,GAAO;;AMnPvB;iBACiC;EAAE,OAAO,ENyF1B,GAAO;;AMxFvB,mBAAmC;EAAE,OAAO,EN9I1B,GAAO;;AM+IzB,qBAAqC;EAAE,OAAO,EN0I1B,GAAO;;AMzI3B,sBAAsC;EAAE,OAAO,EN0I1B,GAAO;;AMzI5B,kBAAkC;EAAE,OAAO,ENgN1B,GAAO;;AM/MxB,iBAAiC;EAAE,OAAO,ENnJ1B,GAAO;;AMoJvB;gBACgC;EAAE,OAAO,ENkJ1B,GAAO;;AMjJtB,qBAAqC;EAAE,OAAO,ENnB1B,GAAO;;AMoB3B,mBAAmC;EAAE,OAAO,ENxC1B,GAAO;;AMyCzB,wBAAwC;EAAE,OAAO,ENvC1B,GAAO;;AMwC9B,kBAAkC;EAAE,OAAO,EN0L1B,GAAO;;AMzLxB,kBAAkC;EAAE,OAAO,ENpC1B,GAAO;;AMqCxB,gBAAgC;EAAE,OAAO,ENoE1B,GAAO;;AMnEtB,kBAAkC;EAAE,OAAO,ENpC1B,GAAO;;AMqCxB,qBAAqC;EAAE,OAAO,ENkB1B,GAAO;;AMjB3B,iBAAiC;EAAE,OAAO,ENrD1B,GAAO;;AMsDvB,yBAAyC;EAAE,OAAO,ENvD1B,GAAO;;AMwD/B,mBAAmC;EAAE,OAAO,ENuO1B,GAAO;;AMtOzB,eAA+B;EAAE,OAAO,ENtJ1B,GAAO;;AMuJrB;oBACoC;EAAE,OAAO,ENqI1B,GAAO;;AMpI1B;;sBAEsC;EAAE,OAAO,ENuM1B,GAAO;;AMtM5B,yBAAyC;EAAE,OAAO,ENkC1B,GAAO;;AMjC/B,eAA+B;EAAE,OAAO,EN5I1B,GAAO;;AM6IrB,oBAAoC;EAAE,OAAO,EN7J1B,GAAO;;AM8J1B;uBACuC;EAAE,OAAO,EN1L1B,GAAO;;AM2L7B,mBAAmC;EAAE,OAAO,EN4G1B,GAAO;;AM3GzB,eAA+B;EAAE,OAAO,ENT1B,GAAO;;AMUrB,sBAAsC;EAAE,OAAO,ENhH1B,GAAO;;AMiH5B,sBAAsC;EAAE,OAAO,EN8M1B,GAAO;;AM7M5B,oBAAoC;EAAE,OAAO,ENyM1B,GAAO;;AMxM1B,iBAAiC;EAAE,OAAO,ENvH1B,GAAO;;AMwHvB,uBAAuC;EAAE,OAAO,ENmG1B,GAAO;;AMlG7B,qBAAqC;EAAE,OAAO,EN8C1B,GAAO;;AM7C3B,2BAA2C;EAAE,OAAO,EN8C1B,GAAO;;AM7CjC,iBAAiC;EAAE,OAAO,ENgJ1B,GAAO;;AM/IvB,qBAAqC;EAAE,OAAO,EN5N1B,GAAO;;AM6N3B,4BAA4C;EAAE,OAAO,ENjF1B,GAAO;;AMkFlC,iBAAiC;EAAE,OAAO,ENoH1B,GAAO;;AMnHvB,iBAAiC;EAAE,OAAO,ENkC1B,GAAO;;AMjCvB,8BAA8C;EAAE,OAAO,ENlM1B,GAAO;;AMmMpC,+BAA+C;EAAE,OAAO,ENlM1B,GAAO;;AMmMrC,4BAA4C;EAAE,OAAO,ENlM1B,GAAO;;AMmMlC,8BAA8C;EAAE,OAAO,ENtM1B,GAAO;;AMuMpC,gBAAgC;EAAE,OAAO,EN/B1B,GAAO;;AMgCtB,eAA+B;EAAE,OAAO,ENjK1B,GAAO;;AMkKrB,iBAAiC;EAAE,OAAO,EN9S1B,GAAO;;AM+SvB,qBAAqC;EAAE,OAAO,ENmP1B,GAAO;;AMlP3B,mBAAmC;EAAE,OAAO,EN9O1B,GAAO;;AM+OzB,qBAAqC;EAAE,OAAO,EN/I1B,GAAO;;AMgJ3B,qBAAqC;EAAE,OAAO,EN/I1B,GAAO;;AMgJ3B,qBAAqC;EAAE,OAAO,EN4G1B,GAAO;;AM3G3B,sBAAsC;EAAE,OAAO,ENsE1B,GAAO;;AMrE5B,iBAAiC;EAAE,OAAO,EN2M1B,GAAO;;AM1MvB,uBAAuC;EAAE,OAAO,EN6B1B,GAAO;;AM5B7B,yBAAyC;EAAE,OAAO,EN6B1B,GAAO;;AM5B/B,mBAAmC;EAAE,OAAO,ENhB1B,GAAO;;AMiBzB,qBAAqC;EAAE,OAAO,ENlB1B,GAAO;;AMmB3B,uBAAuC;EAAE,OAAO,ENvN1B,GAAO;;AMwN7B,wBAAwC;EAAE,OAAO,ENiD1B,GAAO;;AMhD9B,+BAA+C;EAAE,OAAO,EN3I1B,GAAO;;AM4IrC,uBAAuC;EAAE,OAAO,ENkH1B,GAAO;;AMjH7B,kBAAkC;EAAE,OAAO,EN1L1B,GAAO;;AM2LxB;8BAC8C;EAAE,OAAO,ENjP1B,GAAO;;AMkPpC;4BAC4C;EAAE,OAAO,ENhP1B,GAAO;;AMiPlC;+BAC+C;EAAE,OAAO,ENnP1B,GAAO;;AMoPrC;cAC8B;EAAE,OAAO,EN7J1B,GAAO;;AM8JpB,cAA8B;EAAE,OAAO,EN/F1B,GAAO;;AMgGpB;cAC8B;EAAE,OAAO,EN4N1B,GAAO;;AM3NpB;cAC8B;EAAE,OAAO,ENvD1B,GAAO;;AMwDpB;;;cAG8B;EAAE,OAAO,ENrD1B,GAAO;;AMsDpB;;cAE8B;EAAE,OAAO,EN8E1B,GAAO;;AM7EpB;cAC8B;EAAE,OAAO,ENtD1B,GAAO;;AMuDpB;cAC8B;EAAE,OAAO,ENzR1B,GAAO;;AM0RpB,eAA+B;EAAE,OAAO,ENzJ1B,GAAO;;AM0JrB,oBAAoC;EAAE,OAAO,EN7I1B,GAAO;;AM8I1B,yBAAyC;EAAE,OAAO,EN2G1B,GAAO;;AM1G/B,0BAA0C;EAAE,OAAO,EN2G1B,GAAO;;AM1GhC,0BAA0C;EAAE,OAAO,EN2G1B,GAAO;;AM1GhC,2BAA2C;EAAE,OAAO,EN2G1B,GAAO;;AM1GjC,2BAA2C;EAAE,OAAO,EN8G1B,GAAO;;AM7GjC,4BAA4C;EAAE,OAAO,EN8G1B,GAAO;;AM7GlC,oBAAoC;EAAE,OAAO,ENgK1B,GAAO;;AM/J1B,sBAAsC;EAAE,OAAO,EN4J1B,GAAO;;AM3J5B,yBAAyC;EAAE,OAAO,ENwO1B,GAAO;;AMvO/B,kBAAkC;EAAE,OAAO,ENqO1B,GAAO;;AMpOxB,eAA+B;EAAE,OAAO,EN+N1B,GAAO;;AM9NrB,sBAAsC;EAAE,OAAO,EN+N1B,GAAO;;AM9N5B,uBAAuC;EAAE,OAAO,ENmO1B,GAAO;;AMlO7B,kBAAkC;EAAE,OAAO,ENxM1B,GAAO;;AMyMxB,yBAAyC;EAAE,OAAO,EN+G1B,GAAO;;AM9G/B,oBAAoC;EAAE,OAAO,ENnF1B,GAAO;;AMoF1B,iBAAiC;EAAE,OAAO,EN/I1B,GAAO;;AMgJvB,cAA8B;EAAE,OAAO,ENhX1B,GAAO;;AMiXpB,oBAAoC;EAAE,OAAO,ENxT1B,GAAO;;AMyT1B,2BAA2C;EAAE,OAAO,ENxT1B,GAAO;;AMyTjC,iBAAiC;EAAE,OAAO,ENyK1B,GAAO;;AMxKvB,wBAAwC;EAAE,OAAO,ENyK1B,GAAO;;AMxK9B,0BAA0C;EAAE,OAAO,ENtD1B,GAAO;;AMuDhC,wBAAwC;EAAE,OAAO,ENpD1B,GAAO;;AMqD9B,0BAA0C;EAAE,OAAO,ENvD1B,GAAO;;AMwDhC,2BAA2C;EAAE,OAAO,ENvD1B,GAAO;;AMwDjC,gBAAgC;EAAE,OAAO,ENxW1B,GAAO;;AMyWtB,kBAAkC;EAAE,OAAO,EN0M1B,GAAO;;AMzMxB,kBAAkC;EAAE,OAAO,ENpX1B,GAAO;;AMqXxB,gBAAgC;EAAE,OAAO,ENpE1B,GAAO;;AMqEtB,mBAAmC;EAAE,OAAO,EN1N1B,GAAO;;AM2NzB,gBAAgC;EAAE,OAAO,ENqE1B,GAAO;;AMpEtB,qBAAqC;EAAE,OAAO,ENtJ1B,GAAO;;AMuJ3B,iBAAiC;EAAE,OAAO,ENuJ1B,GAAO;;AMtJvB,iBAAiC;EAAE,OAAO,EN/L1B,GAAO;;AMgMvB,eAA+B;EAAE,OAAO,EN1D1B,GAAO;;AM2DrB;mBACmC;EAAE,OAAO,ENnI1B,GAAO;;AMoIzB,gBAAgC;EAAE,OAAO,EN2G1B,GAAO;;AM1GtB,iBAAiC;EAAE,OAAO,ENxC1B,GAAO;;AMyCvB,kBAAkC;EAAE,OAAO,ENrX1B,GAAO;;AMsXxB,cAA8B;EAAE,OAAO,ENpU1B,GAAO;;AMqUpB,aAA6B;EAAE,OAAO,ENgL1B,GAAO;;AM/KnB,gBAAgC;EAAE,OAAO,ENqL1B,GAAO;;AMpLtB,iBAAiC;EAAE,OAAO,ENa1B,GAAO;;AMZvB,oBAAoC;EAAE,OAAO,ENrC1B,GAAO;;AMsC1B,yBAAyC;EAAE,OAAO,EN8E1B,GAAO;;AM7E/B,+BAA+C;EAAE,OAAO,ENtX1B,GAAO;;AMuXrC,8BAA8C;EAAE,OAAO,ENxX1B,GAAO;;AMyXpC;8BAC8C;EAAE,OAAO,EN3T1B,GAAO;;AM4TpC,uBAAuC;EAAE,OAAO,ENjP1B,GAAO;;AMkP7B,qBAAqC;EAAE,OAAO,EN+K1B,GAAO;;AM9K3B,uBAAuC;EAAE,OAAO,ENmK1B,GAAO;;AMlK7B;cAC8B;EAAE,OAAO,ENoI1B,GAAO;;AMnIpB,wBAAwC;EAAE,OAAO,ENjB1B,GAAO;;AMkB9B,wBAAwC;EAAE,OAAO,EN6D1B,GAAO;;AM5D9B,gBAAgC;EAAE,OAAO,EN2C1B,GAAO;;AM1CtB,0BAA0C;EAAE,OAAO,EN7O1B,GAAO;;AM8OhC,oBAAoC;EAAE,OAAO,EN2K1B,GAAO;;AM1K1B,iBAAiC;EAAE,OAAO,ENvD1B,GAAO;;AMwDvB;;qBAEqC;EAAE,OAAO,ENsI1B,GAAO;;AMrI3B;yBACyC;EAAE,OAAO,ENjK1B,GAAO;;AMkK/B,gBAAgC;EAAE,OAAO,ENwK1B,GAAO;;AMvKtB,iBAAiC;EAAE,OAAO,ENvK1B,GAAO;;AMwKvB,iBAAiC;EAAE,OAAO,ENhB1B,GAAO;;AMiBvB,wBAAwC;EAAE,OAAO,ENhB1B,GAAO;;AMiB9B,6BAA6C;EAAE,OAAO,ENsE1B,GAAO;;AMrEnC,sBAAsC;EAAE,OAAO,ENoE1B,GAAO;;AMnE5B,oBAAoC;EAAE,OAAO,EN7Q1B,GAAO;;AM8Q1B,eAA+B;EAAE,OAAO,EN1Q1B,GAAO;;AM2QrB,qBAAqC;EAAE,OAAO,ENjD1B,GAAO;;AMkD3B,yBAAyC;EAAE,OAAO,ENjD1B,GAAO;;AMkD/B,iBAAiC;EAAE,OAAO,ENvQ1B,GAAO;;AMwQvB,iBAAiC;EAAE,OAAO,EN9I1B,GAAO;;AM+IvB,mBAAmC;EAAE,OAAO,ENzI1B,GAAO;;AM0IzB,cAA8B;EAAE,OAAO,EN9O1B,GAAO;;AM+OpB,mBAAmC;EAAE,OAAO,EN3W1B,GAAO;;AM4WzB,gBAAgC;EAAE,OAAO,EN9T1B,GAAO;;AM+TtB,cAA8B;EAAE,OAAO,ENnE1B,GAAO;;AMoEpB,gBAAgC;EAAE,OAAO,ENoC1B,GAAO;;AMnCtB,eAA+B;EAAE,OAAO,ENjS1B,GAAO;;AMkSrB,gBAAgC;EAAE,OAAO,ENjS1B,GAAO;;AMkStB,kBAAkC;EAAE,OAAO,ENtY1B,GAAO;;AMuYxB,yBAAyC;EAAE,OAAO,ENtY1B,GAAO;;AMuY/B,gBAAgC;EAAE,OAAO,EN2C1B,GAAO;;AM1CtB,uBAAuC;EAAE,OAAO,EN2C1B,GAAO;;AM1C7B,kBAAkC;EAAE,OAAO,ENvC1B,GAAO;;AMwCxB;cAC8B;EAAE,OAAO,EN3W1B,GAAO;;AM4WpB;eAC+B;EAAE,OAAO,EN2D1B,GAAO;;AM1DrB,eAA+B;EAAE,OAAO,ENuF1B,GAAO;;AMtFrB,kBAAkC;EAAE,OAAO,ENwB1B,GAAO;;AMvBxB,qBAAqC;EAAE,OAAO,ENpS1B,GAAO;;AMqS3B,qBAAqC;EAAE,OAAO,ENkB1B,GAAO;;AMjB3B,mBAAmC;EAAE,OAAO,EN1S1B,GAAO;;AM2SzB,qBAAqC;EAAE,OAAO,ENxP1B,GAAO;;AMyP3B,sBAAsC;EAAE,OAAO,ENjP1B,GAAO;;AMkP5B,uBAAuC;EAAE,OAAO,EN9P1B,GAAO;;AM+P7B,4BAA4C;EAAE,OAAO,ENxP1B,GAAO;;AMyPlC;;uBAEuC;EAAE,OAAO,ENjQ1B,GAAO;;AMkQ7B;yBACyC;EAAE,OAAO,ENvQ1B,GAAO;;AMwQ/B;uBACuC;EAAE,OAAO,ENxQ1B,GAAO;;AMyQ7B;uBACuC;EAAE,OAAO,EN7P1B,GAAO;;AM8P7B,sBAAsC;EAAE,OAAO,EN1Q1B,GAAO;;AM2Q5B,eAA+B;EAAE,OAAO,ENsG1B,GAAO;;AMrGrB,kBAAkC;EAAE,OAAO,ENlV1B,GAAO;;AMmVxB,mBAAmC;EAAE,OAAO,ENnL1B,GAAO;;AMoLzB;;;;oBAIoC;EAAE,OAAO,ENxK1B,GAAO;;AMyK1B,yBAAyC;EAAE,OAAO,ENpW1B,GAAO;;AMqW/B;gBACgC;EAAE,OAAO,EN1E1B,GAAO;;AM2EtB;iBACiC;EAAE,OAAO,ENpT1B,GAAO;;AMqTvB,qBAAqC;EAAE,OAAO,EN1O1B,GAAO;;AM2O3B,cAA8B;EAAE,OAAO,EN5O1B,GAAO;;AM6OpB,sBAAsC;EAAE,OAAO,EN7N1B,GAAO;;AM8N5B,wBAAwC;EAAE,OAAO,ENwB1B,GAAO;;AMvB9B,aAA6B;EAAE,OAAO,ENzF1B,GAAO;;AM0FnB;iBACiC;EAAE,OAAO,EN2F1B,GAAO;;AM1FvB;sBACsC;EAAE,OAAO,EN9H1B,GAAO;;AM+H5B;wBACwC;EAAE,OAAO,EN/H1B,GAAO;;AMgI9B,kBAAkC;EAAE,OAAO,EN3N1B,GAAO;;AM4NxB;sBACsC;EAAE,OAAO,ENrX1B,GAAO;;AMsX5B,iBAAiC;EAAE,OAAO,ENnO1B,GAAO;;AMoOvB,oBAAoC;EAAE,OAAO,ENlI1B,GAAO;;AMmI1B,kBAAkC;EAAE,OAAO,EN1C1B,GAAO;;AM2CxB,oBAAoC;EAAE,OAAO,EN7D1B,GAAO;;AM8D1B,2BAA2C;EAAE,OAAO,EN7D1B,GAAO;;AM8DjC,eAA+B;EAAE,OAAO,ENpb1B,GAAO;;AMqbrB;mBACmC;EAAE,OAAO,ENzQ1B,GAAO;;AM0QzB,cAA8B;EAAE,OAAO,ENsC1B,GAAO;;AMrCpB,qBAAqC;EAAE,OAAO,EN/b1B,GAAO;;AMgc3B,eAA+B;EAAE,OAAO,ENrH1B,GAAO;;AMsHrB,qBAAqC;EAAE,OAAO,ENlD1B,GAAO;;AMmD3B,iBAAiC;EAAE,OAAO,ENsC1B,GAAO;;AMrCvB,eAA+B;EAAE,OAAO,ENiF1B,GAAO;;AMhFrB,sBAAsC;EAAE,OAAO,ENvJ1B,GAAO;;AMwJ5B,eAA+B;EAAE,OAAO,ENuE1B,GAAO;;AMtErB,qBAAqC;EAAE,OAAO,ENjb1B,GAAO;;AMkb3B,iBAAiC;EAAE,OAAO,EN9I1B,GAAO;;AM+IvB,wBAAwC;EAAE,OAAO,ENhQ1B,GAAO;;AMiQ9B,kBAAkC;EAAE,OAAO,EN9Z1B,GAAO;;AM+ZxB,wBAAwC;EAAE,OAAO,ENla1B,GAAO;;AMma9B,sBAAsC;EAAE,OAAO,ENpa1B,GAAO;;AMqa5B,kBAAkC;EAAE,OAAO,ENta1B,GAAO;;AMuaxB,oBAAoC;EAAE,OAAO,ENpa1B,GAAO;;AMqa1B,oBAAoC;EAAE,OAAO,ENpa1B,GAAO;;AMqa1B,qBAAqC;EAAE,OAAO,ENld1B,GAAO;;AMmd3B,uBAAuC;EAAE,OAAO,ENld1B,GAAO;;AMmd7B,gBAAgC;EAAE,OAAO,ENY1B,GAAO;;AMXtB,oBAAoC;EAAE,OAAO,EN3X1B,GAAO;;AM4X1B,aAA6B;EAAE,OAAO,ENre1B,GAAO;;AMsenB,qBAAqC;EAAE,OAAO,ENjV1B,GAAO;;AMkV3B,sBAAsC;EAAE,OAAO,ENpK1B,GAAO;;AMqK5B,wBAAwC;EAAE,OAAO,ENrd1B,GAAO;;AMsd9B,qBAAqC;EAAE,OAAO,EN3f1B,GAAO;;AM4f3B,oBAAoC;EAAE,OAAO,ENvJ1B,GAAO;;AMwJ1B,qBAAqC;EAAE,OAAO,EN5N1B,GAAO;;AM6N3B,iBAAiC;EAAE,OAAO,EN1O1B,GAAO;;AM2OvB,wBAAwC;EAAE,OAAO,EN1O1B,GAAO;;AM2O9B,qBAAqC;EAAE,OAAO,ENN1B,GAAO;;AMO3B,oBAAoC;EAAE,OAAO,ENN1B,GAAO;;AMO1B,kBAAkC;EAAE,OAAO,EN/d1B,GAAO;;AMgexB,cAA8B;EAAE,OAAO,EN7c1B,GAAO;;AM8cpB,kBAAkC;EAAE,OAAO,EN1P1B,GAAO;;AM2PxB,oBAAoC;EAAE,OAAO,ENhhB1B,GAAO;;AMihB1B,aAA6B;EAAE,OAAO,EN7b1B,GAAO;;AM8bnB;;cAE8B;EAAE,OAAO,ENxQ1B,GAAO;;AMyQpB,mBAAmC;EAAE,OAAO,EN7M1B,GAAO;;AM8MzB,qBAAqC;EAAE,OAAO,ENpd1B,GAAO;;AMqd3B,yBAAyC;EAAE,OAAO,ENnZ1B,GAAO;;AMoZ/B,mBAAmC;EAAE,OAAO,ENxY1B,GAAO;;AMyYzB,mBAAmC;EAAE,OAAO,EN1T1B,GAAO;;AM2TzB,kBAAkC;EAAE,OAAO,ENxP1B,GAAO;;AMyPxB,iBAAiC;EAAE,OAAO,ENrH1B,GAAO;;AMsHvB,uBAAuC;EAAE,OAAO,ENzG1B,GAAO;;AM0G7B,sBAAsC;EAAE,OAAO,ENrG1B,GAAO;;AMsG5B,mBAAmC;EAAE,OAAO,ENpG1B,GAAO;;AMqGzB,oBAAoC;EAAE,OAAO,EN5c1B,GAAO;;AM6c1B,0BAA0C;EAAE,OAAO,EN9c1B,GAAO;;AM+chC,kBAAkC;EAAE,OAAO,EN3Y1B,GAAO;;AM4YxB,eAA+B;EAAE,OAAO,ENhH1B,GAAO;;AMiHrB,sBAAsC;EAAE,OAAO,ENI1B,GAAO;;AMH5B,qBAAqC;EAAE,OAAO,EN5M1B,GAAO;;AM6M3B,sBAAsC;EAAE,OAAO,ENpE1B,GAAO;;AMqE5B,oBAAoC;EAAE,OAAO,ENhS1B,GAAO;;AMiS1B,gBAAgC;EAAE,OAAO,ENG1B,GAAO;;AMFtB,eAA+B;EAAE,OAAO,ENtO1B,GAAO;;AMuOrB,kBAAkC;EAAE,OAAO,EN7N1B,GAAO;;AM8NxB,sBAAsC;EAAE,OAAO,ENhC1B,GAAO;;AMiC5B,0BAA0C;EAAE,OAAO,ENhC1B,GAAO;;AMiChC,uBAAuC;EAAE,OAAO,END1B,GAAO;;AME7B,sBAAsC;EAAE,OAAO,EN1O1B,GAAO;;AM2O5B,qBAAqC;EAAE,OAAO,ENF1B,GAAO;;AMG3B,sBAAsC;EAAE,OAAO,EN3O1B,GAAO;;AM4O5B,wBAAwC;EAAE,OAAO,EN1O1B,GAAO;;AM2O9B,wBAAwC;EAAE,OAAO,EN5O1B,GAAO;;AM6O9B,iBAAiC;EAAE,OAAO,ENvN1B,GAAO;;AMwNvB,4BAA4C;EAAE,OAAO,EN9X1B,GAAO;;AM+XlC,sBAAsC;EAAE,OAAO,ENhM1B,GAAO;;AMiM5B,mBAAmC;EAAE,OAAO,ENI1B,GAAO;;AMHzB,iBAAiC;EAAE,OAAO,EN7I1B,GAAO;;AM8IvB,oBAAoC;EAAE,OAAO,ENjB1B,GAAO;;AMkB1B,qBAAqC;EAAE,OAAO,ENhB1B,GAAO;;AMiB3B;cAC8B;EAAE,OAAO,ENphB1B,GAAO;;AMqhBpB,kBAAkC;EAAE,OAAO,ENd1B,GAAO;;AMexB,gBAAgC;EAAE,OAAO,ENnD1B,GAAO;;AMoDtB,iBAAiC;EAAE,OAAO,ENvF1B,GAAO;;AMwFvB,iBAAiC;EAAE,OAAO,ENrP1B,GAAO",
+"sources": ["../scss/_path.scss","../scss/_core.scss","../scss/_larger.scss","../scss/_fixed-width.scss","../scss/_list.scss","../scss/_variables.scss","../scss/_bordered-pulled.scss","../scss/_animated.scss","../scss/_rotated-flipped.scss","../scss/_mixins.scss","../scss/_stacked.scss","../scss/_icons.scss"],
+"names": [],
+"file": "font-awesome.css"
+}
diff --git a/jetty-documentation/src/main/docbkx-resources/css/font-awesome/font-awesome.min.css b/jetty-documentation/src/main/docbkx-resources/css/font-awesome/font-awesome.min.css
new file mode 100755
index 0000000..92f6e0e
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/css/font-awesome/font-awesome.min.css
@@ -0,0 +1,4 @@
+/*!
+ *  Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
+ *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */@font-face{font-family:'FontAwesome';src:url('../../fonts/fontawesome-webfont.eot?v=4.5.0');src:url('../../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('../../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('../../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('../../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('../../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}  .fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}  .fa-2x{font-size:2em}  .fa-3x{font-size:3em}  .fa-4x{font-size:4em}  .fa-5x{font-size:5em}  .fa-fw{width:1.28571429em;text-align:center}  .fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}  .fa-ul>li{position:relative}  .fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}  .fa-li.fa-lg{left:-1.85714286em}  .fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}  .fa-pull-left{float:left}  .fa-pull-right{float:right}  .fa.fa-pull-left{margin-right:.3em}  .fa.fa-pull-right{margin-left:.3em}  .pull-right{float:right}  .pull-left{float:left}  .fa.pull-left{margin-right:.3em}  .fa.pull-right{margin-left:.3em}  .fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}  .fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}  @-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}  @keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}  .fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}  .fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}  .fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}  .fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}  .fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}  :root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}  .fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}  .fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}  .fa-stack-1x{line-height:inherit}  .fa-stack-2x{font-size:2em}  .fa-inverse{color:#fff}  .fa-glass:before{content:"\f000"}  .fa-music:before{content:"\f001"}  .fa-search:before{content:"\f002"}  .fa-envelope-o:before{content:"\f003"}  .fa-heart:before{content:"\f004"}  .fa-star:before{content:"\f005"}  .fa-star-o:before{content:"\f006"}  .fa-user:before{content:"\f007"}  .fa-film:before{content:"\f008"}  .fa-th-large:before{content:"\f009"}  .fa-th:before{content:"\f00a"}  .fa-th-list:before{content:"\f00b"}  .fa-check:before{content:"\f00c"}  .fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}  .fa-search-plus:before{content:"\f00e"}  .fa-search-minus:before{content:"\f010"}  .fa-power-off:before{content:"\f011"}  .fa-signal:before{content:"\f012"}  .fa-gear:before,.fa-cog:before{content:"\f013"}  .fa-trash-o:before{content:"\f014"}  .fa-home:before{content:"\f015"}  .fa-file-o:before{content:"\f016"}  .fa-clock-o:before{content:"\f017"}  .fa-road:before{content:"\f018"}  .fa-download:before{content:"\f019"}  .fa-arrow-circle-o-down:before{content:"\f01a"}  .fa-arrow-circle-o-up:before{content:"\f01b"}  .fa-inbox:before{content:"\f01c"}  .fa-play-circle-o:before{content:"\f01d"}  .fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}  .fa-refresh:before{content:"\f021"}  .fa-list-alt:before{content:"\f022"}  .fa-lock:before{content:"\f023"}  .fa-flag:before{content:"\f024"}  .fa-headphones:before{content:"\f025"}  .fa-volume-off:before{content:"\f026"}  .fa-volume-down:before{content:"\f027"}  .fa-volume-up:before{content:"\f028"}  .fa-qrcode:before{content:"\f029"}  .fa-barcode:before{content:"\f02a"}  .fa-tag:before{content:"\f02b"}  .fa-tags:before{content:"\f02c"}  .fa-book:before{content:"\f02d"}  .fa-bookmark:before{content:"\f02e"}  .fa-print:before{content:"\f02f"}  .fa-camera:before{content:"\f030"}  .fa-font:before{content:"\f031"}  .fa-bold:before{content:"\f032"}  .fa-italic:before{content:"\f033"}  .fa-text-height:before{content:"\f034"}  .fa-text-width:before{content:"\f035"}  .fa-align-left:before{content:"\f036"}  .fa-align-center:before{content:"\f037"}  .fa-align-right:before{content:"\f038"}  .fa-align-justify:before{content:"\f039"}  .fa-list:before{content:"\f03a"}  .fa-dedent:before,.fa-outdent:before{content:"\f03b"}  .fa-indent:before{content:"\f03c"}  .fa-video-camera:before{content:"\f03d"}  .fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}  .fa-pencil:before{content:"\f040"}  .fa-map-marker:before{content:"\f041"}  .fa-adjust:before{content:"\f042"}  .fa-tint:before{content:"\f043"}  .fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}  .fa-share-square-o:before{content:"\f045"}  .fa-check-square-o:before{content:"\f046"}  .fa-arrows:before{content:"\f047"}  .fa-step-backward:before{content:"\f048"}  .fa-fast-backward:before{content:"\f049"}  .fa-backward:before{content:"\f04a"}  .fa-play:before{content:"\f04b"}  .fa-pause:before{content:"\f04c"}  .fa-stop:before{content:"\f04d"}  .fa-forward:before{content:"\f04e"}  .fa-fast-forward:before{content:"\f050"}  .fa-step-forward:before{content:"\f051"}  .fa-eject:before{content:"\f052"}  .fa-chevron-left:before{content:"\f053"}  .fa-chevron-right:before{content:"\f054"}  .fa-plus-circle:before{content:"\f055"}  .fa-minus-circle:before{content:"\f056"}  .fa-times-circle:before{content:"\f057"}  .fa-check-circle:before{content:"\f058"}  .fa-question-circle:before{content:"\f059"}  .fa-info-circle:before{content:"\f05a"}  .fa-crosshairs:before{content:"\f05b"}  .fa-times-circle-o:before{content:"\f05c"}  .fa-check-circle-o:before{content:"\f05d"}  .fa-ban:before{content:"\f05e"}  .fa-arrow-left:before{content:"\f060"}  .fa-arrow-right:before{content:"\f061"}  .fa-arrow-up:before{content:"\f062"}  .fa-arrow-down:before{content:"\f063"}  .fa-mail-forward:before,.fa-share:before{content:"\f064"}  .fa-expand:before{content:"\f065"}  .fa-compress:before{content:"\f066"}  .fa-plus:before{content:"\f067"}  .fa-minus:before{content:"\f068"}  .fa-asterisk:before{content:"\f069"}  .fa-exclamation-circle:before{content:"\f06a"}  .fa-gift:before{content:"\f06b"}  .fa-leaf:before{content:"\f06c"}  .fa-fire:before{content:"\f06d"}  .fa-eye:before{content:"\f06e"}  .fa-eye-slash:before{content:"\f070"}  .fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}  .fa-plane:before{content:"\f072"}  .fa-calendar:before{content:"\f073"}  .fa-random:before{content:"\f074"}  .fa-comment:before{content:"\f075"}  .fa-magnet:before{content:"\f076"}  .fa-chevron-up:before{content:"\f077"}  .fa-chevron-down:before{content:"\f078"}  .fa-retweet:before{content:"\f079"}  .fa-shopping-cart:before{content:"\f07a"}  .fa-folder:before{content:"\f07b"}  .fa-folder-open:before{content:"\f07c"}  .fa-arrows-v:before{content:"\f07d"}  .fa-arrows-h:before{content:"\f07e"}  .fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}  .fa-twitter-square:before{content:"\f081"}  .fa-facebook-square:before{content:"\f082"}  .fa-camera-retro:before{content:"\f083"}  .fa-key:before{content:"\f084"}  .fa-gears:before,.fa-cogs:before{content:"\f085"}  .fa-comments:before{content:"\f086"}  .fa-thumbs-o-up:before{content:"\f087"}  .fa-thumbs-o-down:before{content:"\f088"}  .fa-star-half:before{content:"\f089"}  .fa-heart-o:before{content:"\f08a"}  .fa-sign-out:before{content:"\f08b"}  .fa-linkedin-square:before{content:"\f08c"}  .fa-thumb-tack:before{content:"\f08d"}  .fa-external-link:before{content:"\f08e"}  .fa-sign-in:before{content:"\f090"}  .fa-trophy:before{content:"\f091"}  .fa-github-square:before{content:"\f092"}  .fa-upload:before{content:"\f093"}  .fa-lemon-o:before{content:"\f094"}  .fa-phone:before{content:"\f095"}  .fa-square-o:before{content:"\f096"}  .fa-bookmark-o:before{content:"\f097"}  .fa-phone-square:before{content:"\f098"}  .fa-twitter:before{content:"\f099"}  .fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}  .fa-github:before{content:"\f09b"}  .fa-unlock:before{content:"\f09c"}  .fa-credit-card:before{content:"\f09d"}  .fa-feed:before,.fa-rss:before{content:"\f09e"}  .fa-hdd-o:before{content:"\f0a0"}  .fa-bullhorn:before{content:"\f0a1"}  .fa-bell:before{content:"\f0f3"}  .fa-certificate:before{content:"\f0a3"}  .fa-hand-o-right:before{content:"\f0a4"}  .fa-hand-o-left:before{content:"\f0a5"}  .fa-hand-o-up:before{content:"\f0a6"}  .fa-hand-o-down:before{content:"\f0a7"}  .fa-arrow-circle-left:before{content:"\f0a8"}  .fa-arrow-circle-right:before{content:"\f0a9"}  .fa-arrow-circle-up:before{content:"\f0aa"}  .fa-arrow-circle-down:before{content:"\f0ab"}  .fa-globe:before{content:"\f0ac"}  .fa-wrench:before{content:"\f0ad"}  .fa-tasks:before{content:"\f0ae"}  .fa-filter:before{content:"\f0b0"}  .fa-briefcase:before{content:"\f0b1"}  .fa-arrows-alt:before{content:"\f0b2"}  .fa-group:before,.fa-users:before{content:"\f0c0"}  .fa-chain:before,.fa-link:before{content:"\f0c1"}  .fa-cloud:before{content:"\f0c2"}  .fa-flask:before{content:"\f0c3"}  .fa-cut:before,.fa-scissors:before{content:"\f0c4"}  .fa-copy:before,.fa-files-o:before{content:"\f0c5"}  .fa-paperclip:before{content:"\f0c6"}  .fa-save:before,.fa-floppy-o:before{content:"\f0c7"}  .fa-square:before{content:"\f0c8"}  .fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}  .fa-list-ul:before{content:"\f0ca"}  .fa-list-ol:before{content:"\f0cb"}  .fa-strikethrough:before{content:"\f0cc"}  .fa-underline:before{content:"\f0cd"}  .fa-table:before{content:"\f0ce"}  .fa-magic:before{content:"\f0d0"}  .fa-truck:before{content:"\f0d1"}  .fa-pinterest:before{content:"\f0d2"}  .fa-pinterest-square:before{content:"\f0d3"}  .fa-google-plus-square:before{content:"\f0d4"}  .fa-google-plus:before{content:"\f0d5"}  .fa-money:before{content:"\f0d6"}  .fa-caret-down:before{content:"\f0d7"}  .fa-caret-up:before{content:"\f0d8"}  .fa-caret-left:before{content:"\f0d9"}  .fa-caret-right:before{content:"\f0da"}  .fa-columns:before{content:"\f0db"}  .fa-unsorted:before,.fa-sort:before{content:"\f0dc"}  .fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}  .fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}  .fa-envelope:before{content:"\f0e0"}  .fa-linkedin:before{content:"\f0e1"}  .fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}  .fa-legal:before,.fa-gavel:before{content:"\f0e3"}  .fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}  .fa-comment-o:before{content:"\f0e5"}  .fa-comments-o:before{content:"\f0e6"}  .fa-flash:before,.fa-bolt:before{content:"\f0e7"}  .fa-sitemap:before{content:"\f0e8"}  .fa-umbrella:before{content:"\f0e9"}  .fa-paste:before,.fa-clipboard:before{content:"\f0ea"}  .fa-lightbulb-o:before{content:"\f0eb"}  .fa-exchange:before{content:"\f0ec"}  .fa-cloud-download:before{content:"\f0ed"}  .fa-cloud-upload:before{content:"\f0ee"}  .fa-user-md:before{content:"\f0f0"}  .fa-stethoscope:before{content:"\f0f1"}  .fa-suitcase:before{content:"\f0f2"}  .fa-bell-o:before{content:"\f0a2"}  .fa-coffee:before{content:"\f0f4"}  .fa-cutlery:before{content:"\f0f5"}  .fa-file-text-o:before{content:"\f0f6"}  .fa-building-o:before{content:"\f0f7"}  .fa-hospital-o:before{content:"\f0f8"}  .fa-ambulance:before{content:"\f0f9"}  .fa-medkit:before{content:"\f0fa"}  .fa-fighter-jet:before{content:"\f0fb"}  .fa-beer:before{content:"\f0fc"}  .fa-h-square:before{content:"\f0fd"}  .fa-plus-square:before{content:"\f0fe"}  .fa-angle-double-left:before{content:"\f100"}  .fa-angle-double-right:before{content:"\f101"}  .fa-angle-double-up:before{content:"\f102"}  .fa-angle-double-down:before{content:"\f103"}  .fa-angle-left:before{content:"\f104"}  .fa-angle-right:before{content:"\f105"}  .fa-angle-up:before{content:"\f106"}  .fa-angle-down:before{content:"\f107"}  .fa-desktop:before{content:"\f108"}  .fa-laptop:before{content:"\f109"}  .fa-tablet:before{content:"\f10a"}  .fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}  .fa-circle-o:before{content:"\f10c"}  .fa-quote-left:before{content:"\f10d"}  .fa-quote-right:before{content:"\f10e"}  .fa-spinner:before{content:"\f110"}  .fa-circle:before{content:"\f111"}  .fa-mail-reply:before,.fa-reply:before{content:"\f112"}  .fa-github-alt:before{content:"\f113"}  .fa-folder-o:before{content:"\f114"}  .fa-folder-open-o:before{content:"\f115"}  .fa-smile-o:before{content:"\f118"}  .fa-frown-o:before{content:"\f119"}  .fa-meh-o:before{content:"\f11a"}  .fa-gamepad:before{content:"\f11b"}  .fa-keyboard-o:before{content:"\f11c"}  .fa-flag-o:before{content:"\f11d"}  .fa-flag-checkered:before{content:"\f11e"}  .fa-terminal:before{content:"\f120"}  .fa-code:before{content:"\f121"}  .fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}  .fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}  .fa-location-arrow:before{content:"\f124"}  .fa-crop:before{content:"\f125"}  .fa-code-fork:before{content:"\f126"}  .fa-unlink:before,.fa-chain-broken:before{content:"\f127"}  .fa-question:before{content:"\f128"}  .fa-info:before{content:"\f129"}  .fa-exclamation:before{content:"\f12a"}  .fa-superscript:before{content:"\f12b"}  .fa-subscript:before{content:"\f12c"}  .fa-eraser:before{content:"\f12d"}  .fa-puzzle-piece:before{content:"\f12e"}  .fa-microphone:before{content:"\f130"}  .fa-microphone-slash:before{content:"\f131"}  .fa-shield:before{content:"\f132"}  .fa-calendar-o:before{content:"\f133"}  .fa-fire-extinguisher:before{content:"\f134"}  .fa-rocket:before{content:"\f135"}  .fa-maxcdn:before{content:"\f136"}  .fa-chevron-circle-left:before{content:"\f137"}  .fa-chevron-circle-right:before{content:"\f138"}  .fa-chevron-circle-up:before{content:"\f139"}  .fa-chevron-circle-down:before{content:"\f13a"}  .fa-html5:before{content:"\f13b"}  .fa-css3:before{content:"\f13c"}  .fa-anchor:before{content:"\f13d"}  .fa-unlock-alt:before{content:"\f13e"}  .fa-bullseye:before{content:"\f140"}  .fa-ellipsis-h:before{content:"\f141"}  .fa-ellipsis-v:before{content:"\f142"}  .fa-rss-square:before{content:"\f143"}  .fa-play-circle:before{content:"\f144"}  .fa-ticket:before{content:"\f145"}  .fa-minus-square:before{content:"\f146"}  .fa-minus-square-o:before{content:"\f147"}  .fa-level-up:before{content:"\f148"}  .fa-level-down:before{content:"\f149"}  .fa-check-square:before{content:"\f14a"}  .fa-pencil-square:before{content:"\f14b"}  .fa-external-link-square:before{content:"\f14c"}  .fa-share-square:before{content:"\f14d"}  .fa-compass:before{content:"\f14e"}  .fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}  .fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}  .fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}  .fa-euro:before,.fa-eur:before{content:"\f153"}  .fa-gbp:before{content:"\f154"}  .fa-dollar:before,.fa-usd:before{content:"\f155"}  .fa-rupee:before,.fa-inr:before{content:"\f156"}  .fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}  .fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}  .fa-won:before,.fa-krw:before{content:"\f159"}  .fa-bitcoin:before,.fa-btc:before{content:"\f15a"}  .fa-file:before{content:"\f15b"}  .fa-file-text:before{content:"\f15c"}  .fa-sort-alpha-asc:before{content:"\f15d"}  .fa-sort-alpha-desc:before{content:"\f15e"}  .fa-sort-amount-asc:before{content:"\f160"}  .fa-sort-amount-desc:before{content:"\f161"}  .fa-sort-numeric-asc:before{content:"\f162"}  .fa-sort-numeric-desc:before{content:"\f163"}  .fa-thumbs-up:before{content:"\f164"}  .fa-thumbs-down:before{content:"\f165"}  .fa-youtube-square:before{content:"\f166"}  .fa-youtube:before{content:"\f167"}  .fa-xing:before{content:"\f168"}  .fa-xing-square:before{content:"\f169"}  .fa-youtube-play:before{content:"\f16a"}  .fa-dropbox:before{content:"\f16b"}  .fa-stack-overflow:before{content:"\f16c"}  .fa-instagram:before{content:"\f16d"}  .fa-flickr:before{content:"\f16e"}  .fa-adn:before{content:"\f170"}  .fa-bitbucket:before{content:"\f171"}  .fa-bitbucket-square:before{content:"\f172"}  .fa-tumblr:before{content:"\f173"}  .fa-tumblr-square:before{content:"\f174"}  .fa-long-arrow-down:before{content:"\f175"}  .fa-long-arrow-up:before{content:"\f176"}  .fa-long-arrow-left:before{content:"\f177"}  .fa-long-arrow-right:before{content:"\f178"}  .fa-apple:before{content:"\f179"}  .fa-windows:before{content:"\f17a"}  .fa-android:before{content:"\f17b"}  .fa-linux:before{content:"\f17c"}  .fa-dribbble:before{content:"\f17d"}  .fa-skype:before{content:"\f17e"}  .fa-foursquare:before{content:"\f180"}  .fa-trello:before{content:"\f181"}  .fa-female:before{content:"\f182"}  .fa-male:before{content:"\f183"}  .fa-gittip:before,.fa-gratipay:before{content:"\f184"}  .fa-sun-o:before{content:"\f185"}  .fa-moon-o:before{content:"\f186"}  .fa-archive:before{content:"\f187"}  .fa-bug:before{content:"\f188"}  .fa-vk:before{content:"\f189"}  .fa-weibo:before{content:"\f18a"}  .fa-renren:before{content:"\f18b"}  .fa-pagelines:before{content:"\f18c"}  .fa-stack-exchange:before{content:"\f18d"}  .fa-arrow-circle-o-right:before{content:"\f18e"}  .fa-arrow-circle-o-left:before{content:"\f190"}  .fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}  .fa-dot-circle-o:before{content:"\f192"}  .fa-wheelchair:before{content:"\f193"}  .fa-vimeo-square:before{content:"\f194"}  .fa-turkish-lira:before,.fa-try:before{content:"\f195"}  .fa-plus-square-o:before{content:"\f196"}  .fa-space-shuttle:before{content:"\f197"}  .fa-slack:before{content:"\f198"}  .fa-envelope-square:before{content:"\f199"}  .fa-wordpress:before{content:"\f19a"}  .fa-openid:before{content:"\f19b"}  .fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}  .fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}  .fa-yahoo:before{content:"\f19e"}  .fa-google:before{content:"\f1a0"}  .fa-reddit:before{content:"\f1a1"}  .fa-reddit-square:before{content:"\f1a2"}  .fa-stumbleupon-circle:before{content:"\f1a3"}  .fa-stumbleupon:before{content:"\f1a4"}  .fa-delicious:before{content:"\f1a5"}  .fa-digg:before{content:"\f1a6"}  .fa-pied-piper:before{content:"\f1a7"}  .fa-pied-piper-alt:before{content:"\f1a8"}  .fa-drupal:before{content:"\f1a9"}  .fa-joomla:before{content:"\f1aa"}  .fa-language:before{content:"\f1ab"}  .fa-fax:before{content:"\f1ac"}  .fa-building:before{content:"\f1ad"}  .fa-child:before{content:"\f1ae"}  .fa-paw:before{content:"\f1b0"}  .fa-spoon:before{content:"\f1b1"}  .fa-cube:before{content:"\f1b2"}  .fa-cubes:before{content:"\f1b3"}  .fa-behance:before{content:"\f1b4"}  .fa-behance-square:before{content:"\f1b5"}  .fa-steam:before{content:"\f1b6"}  .fa-steam-square:before{content:"\f1b7"}  .fa-recycle:before{content:"\f1b8"}  .fa-automobile:before,.fa-car:before{content:"\f1b9"}  .fa-cab:before,.fa-taxi:before{content:"\f1ba"}  .fa-tree:before{content:"\f1bb"}  .fa-spotify:before{content:"\f1bc"}  .fa-deviantart:before{content:"\f1bd"}  .fa-soundcloud:before{content:"\f1be"}  .fa-database:before{content:"\f1c0"}  .fa-file-pdf-o:before{content:"\f1c1"}  .fa-file-word-o:before{content:"\f1c2"}  .fa-file-excel-o:before{content:"\f1c3"}  .fa-file-powerpoint-o:before{content:"\f1c4"}  .fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}  .fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}  .fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}  .fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}  .fa-file-code-o:before{content:"\f1c9"}  .fa-vine:before{content:"\f1ca"}  .fa-codepen:before{content:"\f1cb"}  .fa-jsfiddle:before{content:"\f1cc"}  .fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}  .fa-circle-o-notch:before{content:"\f1ce"}  .fa-ra:before,.fa-rebel:before{content:"\f1d0"}  .fa-ge:before,.fa-empire:before{content:"\f1d1"}  .fa-git-square:before{content:"\f1d2"}  .fa-git:before{content:"\f1d3"}  .fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}  .fa-tencent-weibo:before{content:"\f1d5"}  .fa-qq:before{content:"\f1d6"}  .fa-wechat:before,.fa-weixin:before{content:"\f1d7"}  .fa-send:before,.fa-paper-plane:before{content:"\f1d8"}  .fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}  .fa-history:before{content:"\f1da"}  .fa-circle-thin:before{content:"\f1db"}  .fa-header:before{content:"\f1dc"}  .fa-paragraph:before{content:"\f1dd"}  .fa-sliders:before{content:"\f1de"}  .fa-share-alt:before{content:"\f1e0"}  .fa-share-alt-square:before{content:"\f1e1"}  .fa-bomb:before{content:"\f1e2"}  .fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}  .fa-tty:before{content:"\f1e4"}  .fa-binoculars:before{content:"\f1e5"}  .fa-plug:before{content:"\f1e6"}  .fa-slideshare:before{content:"\f1e7"}  .fa-twitch:before{content:"\f1e8"}  .fa-yelp:before{content:"\f1e9"}  .fa-newspaper-o:before{content:"\f1ea"}  .fa-wifi:before{content:"\f1eb"}  .fa-calculator:before{content:"\f1ec"}  .fa-paypal:before{content:"\f1ed"}  .fa-google-wallet:before{content:"\f1ee"}  .fa-cc-visa:before{content:"\f1f0"}  .fa-cc-mastercard:before{content:"\f1f1"}  .fa-cc-discover:before{content:"\f1f2"}  .fa-cc-amex:before{content:"\f1f3"}  .fa-cc-paypal:before{content:"\f1f4"}  .fa-cc-stripe:before{content:"\f1f5"}  .fa-bell-slash:before{content:"\f1f6"}  .fa-bell-slash-o:before{content:"\f1f7"}  .fa-trash:before{content:"\f1f8"}  .fa-copyright:before{content:"\f1f9"}  .fa-at:before{content:"\f1fa"}  .fa-eyedropper:before{content:"\f1fb"}  .fa-paint-brush:before{content:"\f1fc"}  .fa-birthday-cake:before{content:"\f1fd"}  .fa-area-chart:before{content:"\f1fe"}  .fa-pie-chart:before{content:"\f200"}  .fa-line-chart:before{content:"\f201"}  .fa-lastfm:before{content:"\f202"}  .fa-lastfm-square:before{content:"\f203"}  .fa-toggle-off:before{content:"\f204"}  .fa-toggle-on:before{content:"\f205"}  .fa-bicycle:before{content:"\f206"}  .fa-bus:before{content:"\f207"}  .fa-ioxhost:before{content:"\f208"}  .fa-angellist:before{content:"\f209"}  .fa-cc:before{content:"\f20a"}  .fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}  .fa-meanpath:before{content:"\f20c"}  .fa-buysellads:before{content:"\f20d"}  .fa-connectdevelop:before{content:"\f20e"}  .fa-dashcube:before{content:"\f210"}  .fa-forumbee:before{content:"\f211"}  .fa-leanpub:before{content:"\f212"}  .fa-sellsy:before{content:"\f213"}  .fa-shirtsinbulk:before{content:"\f214"}  .fa-simplybuilt:before{content:"\f215"}  .fa-skyatlas:before{content:"\f216"}  .fa-cart-plus:before{content:"\f217"}  .fa-cart-arrow-down:before{content:"\f218"}  .fa-diamond:before{content:"\f219"}  .fa-ship:before{content:"\f21a"}  .fa-user-secret:before{content:"\f21b"}  .fa-motorcycle:before{content:"\f21c"}  .fa-street-view:before{content:"\f21d"}  .fa-heartbeat:before{content:"\f21e"}  .fa-venus:before{content:"\f221"}  .fa-mars:before{content:"\f222"}  .fa-mercury:before{content:"\f223"}  .fa-intersex:before,.fa-transgender:before{content:"\f224"}  .fa-transgender-alt:before{content:"\f225"}  .fa-venus-double:before{content:"\f226"}  .fa-mars-double:before{content:"\f227"}  .fa-venus-mars:before{content:"\f228"}  .fa-mars-stroke:before{content:"\f229"}  .fa-mars-stroke-v:before{content:"\f22a"}  .fa-mars-stroke-h:before{content:"\f22b"}  .fa-neuter:before{content:"\f22c"}  .fa-genderless:before{content:"\f22d"}  .fa-facebook-official:before{content:"\f230"}  .fa-pinterest-p:before{content:"\f231"}  .fa-whatsapp:before{content:"\f232"}  .fa-server:before{content:"\f233"}  .fa-user-plus:before{content:"\f234"}  .fa-user-times:before{content:"\f235"}  .fa-hotel:before,.fa-bed:before{content:"\f236"}  .fa-viacoin:before{content:"\f237"}  .fa-train:before{content:"\f238"}  .fa-subway:before{content:"\f239"}  .fa-medium:before{content:"\f23a"}  .fa-yc:before,.fa-y-combinator:before{content:"\f23b"}  .fa-optin-monster:before{content:"\f23c"}  .fa-opencart:before{content:"\f23d"}  .fa-expeditedssl:before{content:"\f23e"}  .fa-battery-4:before,.fa-battery-full:before{content:"\f240"}  .fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}  .fa-battery-2:before,.fa-battery-half:before{content:"\f242"}  .fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}  .fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}  .fa-mouse-pointer:before{content:"\f245"}  .fa-i-cursor:before{content:"\f246"}  .fa-object-group:before{content:"\f247"}  .fa-object-ungroup:before{content:"\f248"}  .fa-sticky-note:before{content:"\f249"}  .fa-sticky-note-o:before{content:"\f24a"}  .fa-cc-jcb:before{content:"\f24b"}  .fa-cc-diners-club:before{content:"\f24c"}  .fa-clone:before{content:"\f24d"}  .fa-balance-scale:before{content:"\f24e"}  .fa-hourglass-o:before{content:"\f250"}  .fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}  .fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}  .fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}  .fa-hourglass:before{content:"\f254"}  .fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}  .fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}  .fa-hand-scissors-o:before{content:"\f257"}  .fa-hand-lizard-o:before{content:"\f258"}  .fa-hand-spock-o:before{content:"\f259"}  .fa-hand-pointer-o:before{content:"\f25a"}  .fa-hand-peace-o:before{content:"\f25b"}  .fa-trademark:before{content:"\f25c"}  .fa-registered:before{content:"\f25d"}  .fa-creative-commons:before{content:"\f25e"}  .fa-gg:before{content:"\f260"}  .fa-gg-circle:before{content:"\f261"}  .fa-tripadvisor:before{content:"\f262"}  .fa-odnoklassniki:before{content:"\f263"}  .fa-odnoklassniki-square:before{content:"\f264"}  .fa-get-pocket:before{content:"\f265"}  .fa-wikipedia-w:before{content:"\f266"}  .fa-safari:before{content:"\f267"}  .fa-chrome:before{content:"\f268"}  .fa-firefox:before{content:"\f269"}  .fa-opera:before{content:"\f26a"}  .fa-internet-explorer:before{content:"\f26b"}  .fa-tv:before,.fa-television:before{content:"\f26c"}  .fa-contao:before{content:"\f26d"}  .fa-500px:before{content:"\f26e"}  .fa-amazon:before{content:"\f270"}  .fa-calendar-plus-o:before{content:"\f271"}  .fa-calendar-minus-o:before{content:"\f272"}  .fa-calendar-times-o:before{content:"\f273"}  .fa-calendar-check-o:before{content:"\f274"}  .fa-industry:before{content:"\f275"}  .fa-map-pin:before{content:"\f276"}  .fa-map-signs:before{content:"\f277"}  .fa-map-o:before{content:"\f278"}  .fa-map:before{content:"\f279"}  .fa-commenting:before{content:"\f27a"}  .fa-commenting-o:before{content:"\f27b"}  .fa-houzz:before{content:"\f27c"}  .fa-vimeo:before{content:"\f27d"}  .fa-black-tie:before{content:"\f27e"}  .fa-fonticons:before{content:"\f280"}  .fa-reddit-alien:before{content:"\f281"}  .fa-edge:before{content:"\f282"}  .fa-credit-card-alt:before{content:"\f283"}  .fa-codiepie:before{content:"\f284"}  .fa-modx:before{content:"\f285"}  .fa-fort-awesome:before{content:"\f286"}  .fa-usb:before{content:"\f287"}  .fa-product-hunt:before{content:"\f288"}  .fa-mixcloud:before{content:"\f289"}  .fa-scribd:before{content:"\f28a"}  .fa-pause-circle:before{content:"\f28b"}  .fa-pause-circle-o:before{content:"\f28c"}  .fa-stop-circle:before{content:"\f28d"}  .fa-stop-circle-o:before{content:"\f28e"}  .fa-shopping-bag:before{content:"\f290"}  .fa-shopping-basket:before{content:"\f291"}  .fa-hashtag:before{content:"\f292"}  .fa-bluetooth:before{content:"\f293"}  .fa-bluetooth-b:before{content:"\f294"}  .fa-percent:before{content:"\f295"}
diff --git a/jetty-documentation/src/main/docbkx-resources/css/highlighter/darkula.css b/jetty-documentation/src/main/docbkx-resources/css/highlighter/darkula.css
new file mode 100644
index 0000000..1c6853b
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/css/highlighter/darkula.css
@@ -0,0 +1,152 @@
+/*
+
+Darkula color scheme from the JetBrains family of IDEs
+
+*/
+
+
+.hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+  background: #2b2b2b;
+  -webkit-text-size-adjust: none;
+}
+
+.hljs,
+.hljs-tag,
+.hljs-title,
+.css .hljs-rule,
+.css .hljs-value,
+.aspectj .hljs-function,
+.css .hljs-function .hljs-preprocessor,
+.hljs-pragma {
+  color: #bababa;
+}
+
+.hljs-strongemphasis,
+.hljs-strong,
+.hljs-emphasis {
+  color: #a8a8a2;
+}
+
+.hljs-bullet,
+.hljs-blockquote,
+.hljs-horizontal_rule,
+.hljs-number,
+.hljs-regexp,
+.alias .hljs-keyword,
+.hljs-literal,
+.hljs-hexcolor {
+  color: #6896ba;
+}
+
+.hljs-tag .hljs-value,
+.hljs-code,
+.css .hljs-class,
+.hljs-class .hljs-title:last-child {
+  color: #a6e22e;
+}
+
+.hljs-link_url {
+  font-size: 80%;
+}
+
+.hljs-emphasis,
+.hljs-strongemphasis,
+.hljs-class .hljs-title:last-child,
+.hljs-typename {
+  font-style: italic;
+}
+
+.hljs-keyword,
+.ruby .hljs-class .hljs-keyword:first-child,
+.ruby .hljs-function .hljs-keyword,
+.hljs-function,
+.hljs-change,
+.hljs-winutils,
+.hljs-flow,
+.nginx .hljs-title,
+.tex .hljs-special,
+.hljs-header,
+.hljs-attribute,
+.hljs-symbol,
+.hljs-symbol .hljs-string,
+.hljs-tag .hljs-title,
+.hljs-value,
+.alias .hljs-keyword:first-child,
+.css .hljs-tag,
+.css .unit,
+.css .hljs-important {
+  color: #cb7832;
+}
+
+.hljs-function .hljs-keyword,
+.hljs-class .hljs-keyword:first-child,
+.hljs-aspect .hljs-keyword:first-child,
+.hljs-constant,
+.hljs-typename,
+.css .hljs-attribute {
+  color: #cb7832;
+}
+
+.hljs-variable,
+.hljs-params,
+.hljs-class .hljs-title,
+.hljs-aspect .hljs-title {
+  color: #b9b9b9;
+}
+
+.hljs-string,
+.css .hljs-id,
+.hljs-subst,
+.hljs-type,
+.ruby .hljs-class .hljs-parent,
+.hljs-built_in,
+.django .hljs-template_tag,
+.django .hljs-variable,
+.smalltalk .hljs-class,
+.django .hljs-filter .hljs-argument,
+.smalltalk .hljs-localvars,
+.smalltalk .hljs-array,
+.hljs-attr_selector,
+.hljs-pseudo,
+.hljs-addition,
+.hljs-stream,
+.hljs-envvar,
+.apache .hljs-tag,
+.apache .hljs-cbracket,
+.tex .hljs-command,
+.hljs-prompt,
+.hljs-link_label,
+.hljs-link_url,
+.hljs-name {
+  color: #e0c46c;
+}
+
+.hljs-comment,
+.hljs-annotation,
+.hljs-pi,
+.hljs-doctype,
+.hljs-deletion,
+.hljs-shebang,
+.apache .hljs-sqbracket,
+.tex .hljs-formula {
+  color: #7f7f7f;
+}
+
+.hljs-decorator {
+  color: #bab429;
+}
+
+.coffeescript .javascript,
+.javascript .xml,
+.tex .hljs-formula,
+.xml .javascript,
+.xml .vbscript,
+.xml .css,
+.xml .hljs-cdata,
+.xml .php,
+.php .xml {
+  opacity: 0.5;
+}
diff --git a/jetty-documentation/src/main/docbkx-resources/css/highlighter/default.css b/jetty-documentation/src/main/docbkx-resources/css/highlighter/default.css
new file mode 100644
index 0000000..b0ac8b8
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/css/highlighter/default.css
@@ -0,0 +1,155 @@
+/*
+
+Original style from softwaremaniacs.org (c) Ivan Sagalaev <Maniac@SoftwareManiacs.Org>
+
+*/
+
+.hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+  background: #f0f0f0;
+  -webkit-text-size-adjust: none;
+}
+
+.hljs,
+.hljs-subst,
+.hljs-tag .hljs-title,
+.nginx .hljs-title {
+  color: black;
+}
+
+.hljs-string,
+.hljs-title,
+.hljs-constant,
+.hljs-parent,
+.hljs-tag .hljs-value,
+.hljs-rule .hljs-value,
+.hljs-preprocessor,
+.hljs-pragma,
+.hljs-name,
+.haml .hljs-symbol,
+.ruby .hljs-symbol,
+.ruby .hljs-symbol .hljs-string,
+.hljs-template_tag,
+.django .hljs-variable,
+.smalltalk .hljs-class,
+.hljs-addition,
+.hljs-flow,
+.hljs-stream,
+.bash .hljs-variable,
+.pf .hljs-variable,
+.apache .hljs-tag,
+.apache .hljs-cbracket,
+.tex .hljs-command,
+.tex .hljs-special,
+.erlang_repl .hljs-function_or_atom,
+.asciidoc .hljs-header,
+.markdown .hljs-header,
+.coffeescript .hljs-attribute,
+.tp .hljs-variable {
+  color: #800;
+}
+
+.smartquote,
+.hljs-comment,
+.hljs-annotation,
+.diff .hljs-header,
+.hljs-chunk,
+.asciidoc .hljs-blockquote,
+.markdown .hljs-blockquote {
+  color: #888;
+}
+
+.hljs-number,
+.hljs-date,
+.hljs-regexp,
+.hljs-literal,
+.hljs-hexcolor,
+.smalltalk .hljs-symbol,
+.smalltalk .hljs-char,
+.go .hljs-constant,
+.hljs-change,
+.lasso .hljs-variable,
+.makefile .hljs-variable,
+.asciidoc .hljs-bullet,
+.markdown .hljs-bullet,
+.asciidoc .hljs-link_url,
+.markdown .hljs-link_url {
+  color: #080;
+}
+
+.hljs-label,
+.ruby .hljs-string,
+.hljs-decorator,
+.hljs-filter .hljs-argument,
+.hljs-localvars,
+.hljs-array,
+.hljs-attr_selector,
+.hljs-important,
+.hljs-pseudo,
+.hljs-pi,
+.haml .hljs-bullet,
+.hljs-doctype,
+.hljs-deletion,
+.hljs-envvar,
+.hljs-shebang,
+.apache .hljs-sqbracket,
+.nginx .hljs-built_in,
+.tex .hljs-formula,
+.erlang_repl .hljs-reserved,
+.hljs-prompt,
+.asciidoc .hljs-link_label,
+.markdown .hljs-link_label,
+.vhdl .hljs-attribute,
+.clojure .hljs-attribute,
+.asciidoc .hljs-attribute,
+.lasso .hljs-attribute,
+.coffeescript .hljs-property,
+.hljs-phony {
+  color: #88f;
+}
+
+.hljs-keyword,
+.hljs-id,
+.hljs-title,
+.hljs-built_in,
+.css .hljs-tag,
+.hljs-doctag,
+.smalltalk .hljs-class,
+.hljs-winutils,
+.bash .hljs-variable,
+.pf .hljs-variable,
+.apache .hljs-tag,
+.hljs-type,
+.hljs-typename,
+.tex .hljs-command,
+.asciidoc .hljs-strong,
+.markdown .hljs-strong,
+.hljs-request,
+.hljs-status,
+.tp .hljs-data,
+.tp .hljs-io {
+  font-weight: bold;
+}
+
+.asciidoc .hljs-emphasis,
+.markdown .hljs-emphasis,
+.tp .hljs-units {
+  font-style: italic;
+}
+
+.nginx .hljs-built_in {
+  font-weight: normal;
+}
+
+.coffeescript .javascript,
+.javascript .xml,
+.lasso .markup,
+.tex .hljs-formula,
+.xml .javascript,
+.xml .vbscript,
+.xml .css,
+.xml .hljs-cdata {
+  opacity: 0.5;
+}
diff --git a/jetty-documentation/src/main/docbkx-resources/css/highlighter/foundation.css b/jetty-documentation/src/main/docbkx-resources/css/highlighter/foundation.css
new file mode 100644
index 0000000..09c2ff2
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/css/highlighter/foundation.css
@@ -0,0 +1,135 @@
+/*
+Description: Foundation 4 docs style for highlight.js
+Author: Dan Allen <dan.j.allen@gmail.com>
+Website: http://foundation.zurb.com/docs/
+Version: 1.0
+Date: 2013-04-02
+*/
+
+.hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+  background: #eee;
+  -webkit-text-size-adjust: none;
+}
+
+.hljs-header,
+.hljs-decorator,
+.hljs-annotation {
+  color: #000077;
+}
+
+.hljs-horizontal_rule,
+.hljs-link_url,
+.hljs-emphasis,
+.hljs-attribute {
+  color: #070;
+}
+
+.hljs-emphasis {
+  font-style: italic;
+}
+
+.hljs-link_label,
+.hljs-strong,
+.hljs-value,
+.hljs-string,
+.scss .hljs-value .hljs-string {
+  color: #d14;
+}
+
+.hljs-strong {
+  font-weight: bold;
+}
+
+.hljs-blockquote,
+.hljs-comment {
+  color: #998;
+  font-style: italic;
+}
+
+.asciidoc .hljs-title,
+.hljs-function .hljs-title {
+  color: #900;
+}
+
+.hljs-class {
+  color: #458;
+}
+
+.hljs-id,
+.hljs-pseudo,
+.hljs-constant,
+.hljs-hexcolor {
+  color: teal;
+}
+
+.hljs-variable {
+  color: #336699;
+}
+
+.hljs-bullet {
+  color: #997700;
+}
+
+.hljs-pi,
+.hljs-doctype {
+  color: #3344bb;
+}
+
+.hljs-code,
+.hljs-number {
+  color: #099;
+}
+
+.hljs-important {
+  color: #f00;
+}
+
+.smartquote,
+.hljs-label {
+  color: #970;
+}
+
+.hljs-preprocessor,
+.hljs-pragma {
+  color: #579;
+}
+
+.hljs-reserved,
+.hljs-keyword,
+.scss .hljs-value {
+  color: #000;
+}
+
+.hljs-regexp {
+  background-color: #fff0ff;
+  color: #880088;
+}
+
+.hljs-symbol {
+  color: #990073;
+}
+
+.hljs-symbol .hljs-string {
+  color: #a60;
+}
+
+.hljs-tag {
+  color: #007700;
+}
+
+.hljs-at_rule,
+.hljs-at_rule .hljs-keyword {
+  color: #088;
+}
+
+.hljs-at_rule .hljs-preprocessor {
+  color: #808;
+}
+
+.scss .hljs-tag,
+.scss .hljs-attribute {
+  color: #339;
+}
diff --git a/jetty-documentation/src/main/docbkx-resources/css/highlighter/github.css b/jetty-documentation/src/main/docbkx-resources/css/highlighter/github.css
new file mode 100644
index 0000000..791537e
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/css/highlighter/github.css
@@ -0,0 +1,123 @@
+/*
+
+github.com style (c) Vasily Polovnyov <vast@whiteants.net>
+
+*/
+
+.hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+  color: #333;
+  background: #f8f8f8;
+  -webkit-text-size-adjust: none;
+}
+
+.hljs-comment,
+.diff .hljs-header {
+  color: #998;
+  font-style: italic;
+}
+
+.hljs-keyword,
+.css .rule .hljs-keyword,
+.hljs-winutils,
+.nginx .hljs-title,
+.hljs-subst,
+.hljs-request,
+.hljs-status {
+  color: #333;
+  font-weight: bold;
+}
+
+.hljs-number,
+.hljs-hexcolor,
+.ruby .hljs-constant {
+  color: #008080;
+}
+
+.hljs-string,
+.hljs-tag .hljs-value,
+.hljs-doctag,
+.tex .hljs-formula {
+  color: #d14;
+}
+
+.hljs-title,
+.hljs-id,
+.scss .hljs-preprocessor {
+  color: #900;
+  font-weight: bold;
+}
+
+.hljs-list .hljs-keyword,
+.hljs-subst {
+  font-weight: normal;
+}
+
+.hljs-class .hljs-title,
+.hljs-type,
+.vhdl .hljs-literal,
+.tex .hljs-command {
+  color: #458;
+  font-weight: bold;
+}
+
+.hljs-tag,
+.hljs-tag .hljs-title,
+.hljs-rule .hljs-property,
+.django .hljs-tag .hljs-keyword {
+  color: #000080;
+  font-weight: normal;
+}
+
+.hljs-attribute,
+.hljs-variable,
+.lisp .hljs-body,
+.hljs-name {
+  color: #008080;
+}
+
+.hljs-regexp {
+  color: #009926;
+}
+
+.hljs-symbol,
+.ruby .hljs-symbol .hljs-string,
+.lisp .hljs-keyword,
+.clojure .hljs-keyword,
+.scheme .hljs-keyword,
+.tex .hljs-special,
+.hljs-prompt {
+  color: #990073;
+}
+
+.hljs-built_in {
+  color: #0086b3;
+}
+
+.hljs-preprocessor,
+.hljs-pragma,
+.hljs-pi,
+.hljs-doctype,
+.hljs-shebang,
+.hljs-cdata {
+  color: #999;
+  font-weight: bold;
+}
+
+.hljs-deletion {
+  background: #fdd;
+}
+
+.hljs-addition {
+  background: #dfd;
+}
+
+.diff .hljs-change {
+  background: #0086b3;
+}
+
+.hljs-chunk {
+  color: #aaa;
+}
diff --git a/jetty-documentation/src/main/docbkx-resources/css/highlighter/googlecode.css b/jetty-documentation/src/main/docbkx-resources/css/highlighter/googlecode.css
new file mode 100644
index 0000000..dad5bb0
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/css/highlighter/googlecode.css
@@ -0,0 +1,144 @@
+/*
+
+Google Code style (c) Aahan Krish <geekpanth3r@gmail.com>
+
+*/
+
+.hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+  background: white;
+  color: black;
+  -webkit-text-size-adjust: none;
+}
+
+.hljs-comment {
+  color: #800;
+}
+
+.hljs-keyword,
+.method,
+.hljs-list .hljs-keyword,
+.nginx .hljs-title,
+.hljs-tag .hljs-title,
+.setting .hljs-value,
+.hljs-winutils,
+.tex .hljs-command,
+.http .hljs-title,
+.hljs-request,
+.hljs-status {
+  color: #008;
+}
+
+.hljs-envvar,
+.tex .hljs-special {
+  color: #660;
+}
+
+.hljs-string,
+.hljs-tag .hljs-value,
+.hljs-cdata,
+.hljs-filter .hljs-argument,
+.hljs-attr_selector,
+.apache .hljs-cbracket,
+.hljs-date,
+.hljs-regexp,
+.coffeescript .hljs-attribute {
+  color: #080;
+}
+
+.hljs-sub .hljs-identifier,
+.hljs-pi,
+.hljs-tag,
+.hljs-tag .hljs-keyword,
+.hljs-decorator,
+.ini .hljs-title,
+.hljs-shebang,
+.hljs-prompt,
+.hljs-hexcolor,
+.hljs-rule .hljs-value,
+.hljs-literal,
+.hljs-symbol,
+.ruby .hljs-symbol .hljs-string,
+.hljs-number,
+.css .hljs-function,
+.clojure .hljs-attribute {
+  color: #066;
+}
+
+.hljs-class .hljs-title,
+.smalltalk .hljs-class,
+.hljs-doctag,
+.hljs-type,
+.hljs-typename,
+.hljs-tag .hljs-attribute,
+.hljs-doctype,
+.hljs-class .hljs-id,
+.hljs-built_in,
+.setting,
+.hljs-params,
+.hljs-variable,
+.hljs-name {
+  color: #606;
+}
+
+.css .hljs-tag,
+.hljs-rule .hljs-property,
+.hljs-pseudo,
+.hljs-subst {
+  color: #000;
+}
+
+.css .hljs-class,
+.css .hljs-id {
+  color: #9b703f;
+}
+
+.hljs-value .hljs-important {
+  color: #ff7700;
+  font-weight: bold;
+}
+
+.hljs-rule .hljs-keyword {
+  color: #c5af75;
+}
+
+.hljs-annotation,
+.apache .hljs-sqbracket,
+.nginx .hljs-built_in {
+  color: #9b859d;
+}
+
+.hljs-preprocessor,
+.hljs-preprocessor *,
+.hljs-pragma {
+  color: #444;
+}
+
+.tex .hljs-formula {
+  background-color: #eee;
+  font-style: italic;
+}
+
+.diff .hljs-header,
+.hljs-chunk {
+  color: #808080;
+  font-weight: bold;
+}
+
+.diff .hljs-change {
+  background-color: #bccff9;
+}
+
+.hljs-addition {
+  background-color: #baeeba;
+}
+
+.hljs-deletion {
+  background-color: #ffc8bd;
+}
+
+.hljs-comment .hljs-doctag {
+  font-weight: bold;
+}
diff --git a/jetty-documentation/src/main/docbkx-resources/css/highlighter/zenburn.css b/jetty-documentation/src/main/docbkx-resources/css/highlighter/zenburn.css
new file mode 100644
index 0000000..c7f5bc5
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/css/highlighter/zenburn.css
@@ -0,0 +1,118 @@
+/*
+
+Zenburn style from voldmar.ru (c) Vladimir Epifanov <voldmar@voldmar.ru>
+based on dark.css by Ivan Sagalaev
+
+*/
+
+.hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+  background: #3f3f3f;
+  color: #dcdcdc;
+  -webkit-text-size-adjust: none;
+}
+
+.hljs-keyword,
+.hljs-tag,
+.css .hljs-class,
+.css .hljs-id,
+.lisp .hljs-title,
+.nginx .hljs-title,
+.hljs-request,
+.hljs-status,
+.clojure .hljs-attribute {
+  color: #e3ceab;
+}
+
+.django .hljs-template_tag,
+.django .hljs-variable,
+.django .hljs-filter .hljs-argument {
+  color: #dcdcdc;
+}
+
+.hljs-number,
+.hljs-date {
+  color: #8cd0d3;
+}
+
+.dos .hljs-envvar,
+.dos .hljs-stream,
+.hljs-variable,
+.apache .hljs-sqbracket,
+.hljs-name {
+  color: #efdcbc;
+}
+
+.dos .hljs-flow,
+.diff .hljs-change,
+.python .exception,
+.python .hljs-built_in,
+.hljs-literal,
+.tex .hljs-special {
+  color: #efefaf;
+}
+
+.diff .hljs-chunk,
+.hljs-subst {
+  color: #8f8f8f;
+}
+
+.dos .hljs-keyword,
+.hljs-decorator,
+.hljs-title,
+.hljs-type,
+.diff .hljs-header,
+.ruby .hljs-class .hljs-parent,
+.apache .hljs-tag,
+.nginx .hljs-built_in,
+.tex .hljs-command,
+.hljs-prompt {
+  color: #efef8f;
+}
+
+.dos .hljs-winutils,
+.ruby .hljs-symbol,
+.ruby .hljs-symbol .hljs-string,
+.ruby .hljs-string {
+  color: #dca3a3;
+}
+
+.diff .hljs-deletion,
+.hljs-string,
+.hljs-tag .hljs-value,
+.hljs-preprocessor,
+.hljs-pragma,
+.hljs-built_in,
+.smalltalk .hljs-class,
+.smalltalk .hljs-localvars,
+.smalltalk .hljs-array,
+.css .hljs-rule .hljs-value,
+.hljs-attr_selector,
+.hljs-pseudo,
+.apache .hljs-cbracket,
+.tex .hljs-formula,
+.coffeescript .hljs-attribute {
+  color: #cc9393;
+}
+
+.hljs-shebang,
+.diff .hljs-addition,
+.hljs-comment,
+.hljs-annotation,
+.hljs-pi,
+.hljs-doctype {
+  color: #7f9f7f;
+}
+
+.coffeescript .javascript,
+.javascript .xml,
+.tex .hljs-formula,
+.xml .javascript,
+.xml .vbscript,
+.xml .css,
+.xml .hljs-cdata {
+  opacity: 0.5;
+}
+
diff --git a/jetty-documentation/src/main/docbkx-resources/fonts/FontAwesome.otf b/jetty-documentation/src/main/docbkx-resources/fonts/FontAwesome.otf
new file mode 100755
index 0000000..3ed7f8b
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/fonts/FontAwesome.otf
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.eot b/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.eot
new file mode 100755
index 0000000..9b6afae
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.eot
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.svg b/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.svg
new file mode 100755
index 0000000..d05688e
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.svg
@@ -0,0 +1,655 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="fontawesomeregular" horiz-adv-x="1536" >
+<font-face units-per-em="1792" ascent="1536" descent="-256" />
+<missing-glyph horiz-adv-x="448" />
+<glyph unicode=" "  horiz-adv-x="448" />
+<glyph unicode="&#x09;" horiz-adv-x="448" />
+<glyph unicode="&#xa0;" horiz-adv-x="448" />
+<glyph unicode="&#xa8;" horiz-adv-x="1792" />
+<glyph unicode="&#xa9;" horiz-adv-x="1792" />
+<glyph unicode="&#xae;" horiz-adv-x="1792" />
+<glyph unicode="&#xb4;" horiz-adv-x="1792" />
+<glyph unicode="&#xc6;" horiz-adv-x="1792" />
+<glyph unicode="&#xd8;" horiz-adv-x="1792" />
+<glyph unicode="&#x2000;" horiz-adv-x="768" />
+<glyph unicode="&#x2001;" horiz-adv-x="1537" />
+<glyph unicode="&#x2002;" horiz-adv-x="768" />
+<glyph unicode="&#x2003;" horiz-adv-x="1537" />
+<glyph unicode="&#x2004;" horiz-adv-x="512" />
+<glyph unicode="&#x2005;" horiz-adv-x="384" />
+<glyph unicode="&#x2006;" horiz-adv-x="256" />
+<glyph unicode="&#x2007;" horiz-adv-x="256" />
+<glyph unicode="&#x2008;" horiz-adv-x="192" />
+<glyph unicode="&#x2009;" horiz-adv-x="307" />
+<glyph unicode="&#x200a;" horiz-adv-x="85" />
+<glyph unicode="&#x202f;" horiz-adv-x="307" />
+<glyph unicode="&#x205f;" horiz-adv-x="384" />
+<glyph unicode="&#x2122;" horiz-adv-x="1792" />
+<glyph unicode="&#x221e;" horiz-adv-x="1792" />
+<glyph unicode="&#x2260;" horiz-adv-x="1792" />
+<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="&#xf000;" horiz-adv-x="1792" d="M1699 1350q0 -35 -43 -78l-632 -632v-768h320q26 0 45 -19t19 -45t-19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45t45 19h320v768l-632 632q-43 43 -43 78q0 23 18 36.5t38 17.5t43 4h1408q23 0 43 -4t38 -17.5t18 -36.5z" />
+<glyph unicode="&#xf001;" d="M1536 1312v-1120q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v537l-768 -237v-709q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89 t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v967q0 31 19 56.5t49 35.5l832 256q12 4 28 4q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf002;" horiz-adv-x="1664" d="M1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -52 -38 -90t-90 -38q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5 t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
+<glyph unicode="&#xf003;" horiz-adv-x="1792" d="M1664 32v768q-32 -36 -69 -66q-268 -206 -426 -338q-51 -43 -83 -67t-86.5 -48.5t-102.5 -24.5h-1h-1q-48 0 -102.5 24.5t-86.5 48.5t-83 67q-158 132 -426 338q-37 30 -69 66v-768q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1664 1083v11v13.5t-0.5 13 t-3 12.5t-5.5 9t-9 7.5t-14 2.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5q0 -168 147 -284q193 -152 401 -317q6 -5 35 -29.5t46 -37.5t44.5 -31.5t50.5 -27.5t43 -9h1h1q20 0 43 9t50.5 27.5t44.5 31.5t46 37.5t35 29.5q208 165 401 317q54 43 100.5 115.5t46.5 131.5z M1792 1120v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf004;" horiz-adv-x="1792" d="M896 -128q-26 0 -44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124t127 -344q0 -221 -229 -450l-623 -600 q-18 -18 -44 -18z" />
+<glyph unicode="&#xf005;" horiz-adv-x="1664" d="M1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -21 -10.5 -35.5t-30.5 -14.5q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455 l502 -73q56 -9 56 -46z" />
+<glyph unicode="&#xf006;" horiz-adv-x="1664" d="M1137 532l306 297l-422 62l-189 382l-189 -382l-422 -62l306 -297l-73 -421l378 199l377 -199zM1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -50 -41 -50q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500 l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455l502 -73q56 -9 56 -46z" />
+<glyph unicode="&#xf007;" horiz-adv-x="1408" d="M1408 131q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q9 0 42 -21.5t74.5 -48t108 -48t133.5 -21.5t133.5 21.5t108 48t74.5 48t42 21.5q61 0 111.5 -20t85.5 -53.5t62 -81 t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
+<glyph unicode="&#xf008;" horiz-adv-x="1920" d="M384 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 320v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 704v128q0 26 -19 45t-45 19h-128 q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 -64v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM384 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45 t45 -19h128q26 0 45 19t19 45zM1792 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 704v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1792 320v128 q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 704v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19 t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1920 1248v-1344q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1344q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf009;" horiz-adv-x="1664" d="M768 512v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM768 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 512v-384q0 -52 -38 -90t-90 -38 h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf00a;" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 288v-192q0 -40 -28 -68t-68 -28h-320 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf00b;" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-960 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h960q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf00c;" horiz-adv-x="1792" d="M1671 970q0 -40 -28 -68l-724 -724l-136 -136q-28 -28 -68 -28t-68 28l-136 136l-362 362q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -295l656 657q28 28 68 28t68 -28l136 -136q28 -28 28 -68z" />
+<glyph unicode="&#xf00d;" horiz-adv-x="1408" d="M1298 214q0 -40 -28 -68l-136 -136q-28 -28 -68 -28t-68 28l-294 294l-294 -294q-28 -28 -68 -28t-68 28l-136 136q-28 28 -28 68t28 68l294 294l-294 294q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -294l294 294q28 28 68 28t68 -28l136 -136q28 -28 28 -68 t-28 -68l-294 -294l294 -294q28 -28 28 -68z" />
+<glyph unicode="&#xf00e;" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-224q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v224h-224q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h224v224q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5v-224h224 q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5 t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
+<glyph unicode="&#xf010;" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-576q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h576q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5z M1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z " />
+<glyph unicode="&#xf011;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61t-298 61t-245 164t-164 245t-61 298q0 182 80.5 343t226.5 270q43 32 95.5 25t83.5 -50q32 -42 24.5 -94.5t-49.5 -84.5q-98 -74 -151.5 -181t-53.5 -228q0 -104 40.5 -198.5t109.5 -163.5t163.5 -109.5 t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5q0 121 -53.5 228t-151.5 181q-42 32 -49.5 84.5t24.5 94.5q31 43 84 50t95 -25q146 -109 226.5 -270t80.5 -343zM896 1408v-640q0 -52 -38 -90t-90 -38t-90 38t-38 90v640q0 52 38 90t90 38t90 -38t38 -90z" />
+<glyph unicode="&#xf012;" horiz-adv-x="1792" d="M256 96v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 224v-320q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 480v-576q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1408 864v-960q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1376v-1472q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1472q0 14 9 23t23 9h192q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf013;" d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1536 749v-222q0 -12 -8 -23t-20 -13l-185 -28q-19 -54 -39 -91q35 -50 107 -138q10 -12 10 -25t-9 -23q-27 -37 -99 -108t-94 -71q-12 0 -26 9l-138 108q-44 -23 -91 -38 q-16 -136 -29 -186q-7 -28 -36 -28h-222q-14 0 -24.5 8.5t-11.5 21.5l-28 184q-49 16 -90 37l-141 -107q-10 -9 -25 -9q-14 0 -25 11q-126 114 -165 168q-7 10 -7 23q0 12 8 23q15 21 51 66.5t54 70.5q-27 50 -41 99l-183 27q-13 2 -21 12.5t-8 23.5v222q0 12 8 23t19 13 l186 28q14 46 39 92q-40 57 -107 138q-10 12 -10 24q0 10 9 23q26 36 98.5 107.5t94.5 71.5q13 0 26 -10l138 -107q44 23 91 38q16 136 29 186q7 28 36 28h222q14 0 24.5 -8.5t11.5 -21.5l28 -184q49 -16 90 -37l142 107q9 9 24 9q13 0 25 -10q129 -119 165 -170q7 -8 7 -22 q0 -12 -8 -23q-15 -21 -51 -66.5t-54 -70.5q26 -50 41 -98l183 -28q13 -2 21 -12.5t8 -23.5z" />
+<glyph unicode="&#xf014;" horiz-adv-x="1408" d="M512 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM768 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1024 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1152 76v948h-896v-948q0 -22 7 -40.5t14.5 -27t10.5 -8.5h832q3 0 10.5 8.5t14.5 27t7 40.5zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832 q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf015;" horiz-adv-x="1664" d="M1408 544v-480q0 -26 -19 -45t-45 -19h-384v384h-256v-384h-384q-26 0 -45 19t-19 45v480q0 1 0.5 3t0.5 3l575 474l575 -474q1 -2 1 -6zM1631 613l-62 -74q-8 -9 -21 -11h-3q-13 0 -21 7l-692 577l-692 -577q-12 -8 -24 -7q-13 2 -21 11l-62 74q-8 10 -7 23.5t11 21.5 l719 599q32 26 76 26t76 -26l244 -204v195q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-408l219 -182q10 -8 11 -21.5t-7 -23.5z" />
+<glyph unicode="&#xf016;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z " />
+<glyph unicode="&#xf017;" d="M896 992v-448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf018;" horiz-adv-x="1920" d="M1111 540v4l-24 320q-1 13 -11 22.5t-23 9.5h-186q-13 0 -23 -9.5t-11 -22.5l-24 -320v-4q-1 -12 8 -20t21 -8h244q12 0 21 8t8 20zM1870 73q0 -73 -46 -73h-704q13 0 22 9.5t8 22.5l-20 256q-1 13 -11 22.5t-23 9.5h-272q-13 0 -23 -9.5t-11 -22.5l-20 -256 q-1 -13 8 -22.5t22 -9.5h-704q-46 0 -46 73q0 54 26 116l417 1044q8 19 26 33t38 14h339q-13 0 -23 -9.5t-11 -22.5l-15 -192q-1 -14 8 -23t22 -9h166q13 0 22 9t8 23l-15 192q-1 13 -11 22.5t-23 9.5h339q20 0 38 -14t26 -33l417 -1044q26 -62 26 -116z" />
+<glyph unicode="&#xf019;" horiz-adv-x="1664" d="M1280 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 416v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h465l135 -136 q58 -56 136 -56t136 56l136 136h464q40 0 68 -28t28 -68zM1339 985q17 -41 -14 -70l-448 -448q-18 -19 -45 -19t-45 19l-448 448q-31 29 -14 70q17 39 59 39h256v448q0 26 19 45t45 19h256q26 0 45 -19t19 -45v-448h256q42 0 59 -39z" />
+<glyph unicode="&#xf01a;" d="M1120 608q0 -12 -10 -24l-319 -319q-11 -9 -23 -9t-23 9l-320 320q-15 16 -7 35q8 20 30 20h192v352q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-352h192q14 0 23 -9t9 -23zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273 t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf01b;" d="M1118 660q-8 -20 -30 -20h-192v-352q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v352h-192q-14 0 -23 9t-9 23q0 12 10 24l319 319q11 9 23 9t23 -9l320 -320q15 -16 7 -35zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198 t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf01c;" d="M1023 576h316q-1 3 -2.5 8t-2.5 8l-212 496h-708l-212 -496q-1 -2 -2.5 -8t-2.5 -8h316l95 -192h320zM1536 546v-482q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v482q0 62 25 123l238 552q10 25 36.5 42t52.5 17h832q26 0 52.5 -17t36.5 -42l238 -552 q25 -61 25 -123z" />
+<glyph unicode="&#xf01d;" d="M1184 640q0 -37 -32 -55l-544 -320q-15 -9 -32 -9q-16 0 -32 8q-32 19 -32 56v640q0 37 32 56q33 18 64 -1l544 -320q32 -18 32 -55zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf01e;" d="M1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l138 138q-148 137 -349 137q-104 0 -198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5q119 0 225 52t179 147q7 10 23 12q14 0 25 -9 l137 -138q9 -8 9.5 -20.5t-7.5 -22.5q-109 -132 -264 -204.5t-327 -72.5q-156 0 -298 61t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q147 0 284.5 -55.5t244.5 -156.5l130 129q29 31 70 14q39 -17 39 -59z" />
+<glyph unicode="&#xf021;" d="M1511 480q0 -5 -1 -7q-64 -268 -268 -434.5t-478 -166.5q-146 0 -282.5 55t-243.5 157l-129 -129q-19 -19 -45 -19t-45 19t-19 45v448q0 26 19 45t45 19h448q26 0 45 -19t19 -45t-19 -45l-137 -137q71 -66 161 -102t187 -36q134 0 250 65t186 179q11 17 53 117 q8 23 30 23h192q13 0 22.5 -9.5t9.5 -22.5zM1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-26 0 -45 19t-19 45t19 45l138 138q-148 137 -349 137q-134 0 -250 -65t-186 -179q-11 -17 -53 -117q-8 -23 -30 -23h-199q-13 0 -22.5 9.5t-9.5 22.5v7q65 268 270 434.5t480 166.5 q146 0 284 -55.5t245 -156.5l130 129q19 19 45 19t45 -19t19 -45z" />
+<glyph unicode="&#xf022;" horiz-adv-x="1792" d="M384 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M384 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1536 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5z M1536 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5zM1536 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5 t9.5 -22.5zM1664 160v832q0 13 -9.5 22.5t-22.5 9.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1792 1248v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47 t47 -113z" />
+<glyph unicode="&#xf023;" horiz-adv-x="1152" d="M320 768h512v192q0 106 -75 181t-181 75t-181 -75t-75 -181v-192zM1152 672v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v192q0 184 132 316t316 132t316 -132t132 -316v-192h32q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf024;" horiz-adv-x="1792" d="M320 1280q0 -72 -64 -110v-1266q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v1266q-64 38 -64 110q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -25 -12.5 -38.5t-39.5 -27.5q-215 -116 -369 -116q-61 0 -123.5 22t-108.5 48 t-115.5 48t-142.5 22q-192 0 -464 -146q-17 -9 -33 -9q-26 0 -45 19t-19 45v742q0 32 31 55q21 14 79 43q236 120 421 120q107 0 200 -29t219 -88q38 -19 88 -19q54 0 117.5 21t110 47t88 47t54.5 21q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf025;" horiz-adv-x="1664" d="M1664 650q0 -166 -60 -314l-20 -49l-185 -33q-22 -83 -90.5 -136.5t-156.5 -53.5v-32q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-32q71 0 130 -35.5t93 -95.5l68 12q29 95 29 193q0 148 -88 279t-236.5 209t-315.5 78 t-315.5 -78t-236.5 -209t-88 -279q0 -98 29 -193l68 -12q34 60 93 95.5t130 35.5v32q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v32q-88 0 -156.5 53.5t-90.5 136.5l-185 33l-20 49q-60 148 -60 314q0 151 67 291t179 242.5 t266 163.5t320 61t320 -61t266 -163.5t179 -242.5t67 -291z" />
+<glyph unicode="&#xf026;" horiz-adv-x="768" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45z" />
+<glyph unicode="&#xf027;" horiz-adv-x="1152" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142z" />
+<glyph unicode="&#xf028;" horiz-adv-x="1664" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142zM1408 640q0 -153 -85 -282.5t-225 -188.5q-13 -5 -25 -5q-27 0 -46 19t-19 45q0 39 39 59q56 29 76 44q74 54 115.5 135.5t41.5 173.5t-41.5 173.5 t-115.5 135.5q-20 15 -76 44q-39 20 -39 59q0 26 19 45t45 19q13 0 26 -5q140 -59 225 -188.5t85 -282.5zM1664 640q0 -230 -127 -422.5t-338 -283.5q-13 -5 -26 -5q-26 0 -45 19t-19 45q0 36 39 59q7 4 22.5 10.5t22.5 10.5q46 25 82 51q123 91 192 227t69 289t-69 289 t-192 227q-36 26 -82 51q-7 4 -22.5 10.5t-22.5 10.5q-39 23 -39 59q0 26 19 45t45 19q13 0 26 -5q211 -91 338 -283.5t127 -422.5z" />
+<glyph unicode="&#xf029;" horiz-adv-x="1408" d="M384 384v-128h-128v128h128zM384 1152v-128h-128v128h128zM1152 1152v-128h-128v128h128zM128 129h384v383h-384v-383zM128 896h384v384h-384v-384zM896 896h384v384h-384v-384zM640 640v-640h-640v640h640zM1152 128v-128h-128v128h128zM1408 128v-128h-128v128h128z M1408 640v-384h-384v128h-128v-384h-128v640h384v-128h128v128h128zM640 1408v-640h-640v640h640zM1408 1408v-640h-640v640h640z" />
+<glyph unicode="&#xf02a;" horiz-adv-x="1792" d="M63 0h-63v1408h63v-1408zM126 1h-32v1407h32v-1407zM220 1h-31v1407h31v-1407zM377 1h-31v1407h31v-1407zM534 1h-62v1407h62v-1407zM660 1h-31v1407h31v-1407zM723 1h-31v1407h31v-1407zM786 1h-31v1407h31v-1407zM943 1h-63v1407h63v-1407zM1100 1h-63v1407h63v-1407z M1226 1h-63v1407h63v-1407zM1352 1h-63v1407h63v-1407zM1446 1h-63v1407h63v-1407zM1635 1h-94v1407h94v-1407zM1698 1h-32v1407h32v-1407zM1792 0h-63v1408h63v-1408z" />
+<glyph unicode="&#xf02b;" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91z" />
+<glyph unicode="&#xf02c;" horiz-adv-x="1920" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91zM1899 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-36 0 -59 14t-53 45l470 470q37 37 37 90q0 52 -37 91l-715 714q-38 38 -102 64.5t-117 26.5h224q53 0 117 -26.5t102 -64.5l715 -714q37 -39 37 -91z" />
+<glyph unicode="&#xf02d;" horiz-adv-x="1664" d="M1639 1058q40 -57 18 -129l-275 -906q-19 -64 -76.5 -107.5t-122.5 -43.5h-923q-77 0 -148.5 53.5t-99.5 131.5q-24 67 -2 127q0 4 3 27t4 37q1 8 -3 21.5t-3 19.5q2 11 8 21t16.5 23.5t16.5 23.5q23 38 45 91.5t30 91.5q3 10 0.5 30t-0.5 28q3 11 17 28t17 23 q21 36 42 92t25 90q1 9 -2.5 32t0.5 28q4 13 22 30.5t22 22.5q19 26 42.5 84.5t27.5 96.5q1 8 -3 25.5t-2 26.5q2 8 9 18t18 23t17 21q8 12 16.5 30.5t15 35t16 36t19.5 32t26.5 23.5t36 11.5t47.5 -5.5l-1 -3q38 9 51 9h761q74 0 114 -56t18 -130l-274 -906 q-36 -119 -71.5 -153.5t-128.5 -34.5h-869q-27 0 -38 -15q-11 -16 -1 -43q24 -70 144 -70h923q29 0 56 15.5t35 41.5l300 987q7 22 5 57q38 -15 59 -43zM575 1056q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5 t-16.5 -22.5zM492 800q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5t-16.5 -22.5z" />
+<glyph unicode="&#xf02e;" horiz-adv-x="1280" d="M1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
+<glyph unicode="&#xf02f;" horiz-adv-x="1664" d="M384 0h896v256h-896v-256zM384 640h896v384h-160q-40 0 -68 28t-28 68v160h-640v-640zM1536 576q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 576v-416q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-160q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68 v160h-224q-13 0 -22.5 9.5t-9.5 22.5v416q0 79 56.5 135.5t135.5 56.5h64v544q0 40 28 68t68 28h672q40 0 88 -20t76 -48l152 -152q28 -28 48 -76t20 -88v-256h64q79 0 135.5 -56.5t56.5 -135.5z" />
+<glyph unicode="&#xf030;" horiz-adv-x="1920" d="M960 864q119 0 203.5 -84.5t84.5 -203.5t-84.5 -203.5t-203.5 -84.5t-203.5 84.5t-84.5 203.5t84.5 203.5t203.5 84.5zM1664 1280q106 0 181 -75t75 -181v-896q0 -106 -75 -181t-181 -75h-1408q-106 0 -181 75t-75 181v896q0 106 75 181t181 75h224l51 136 q19 49 69.5 84.5t103.5 35.5h512q53 0 103.5 -35.5t69.5 -84.5l51 -136h224zM960 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf031;" horiz-adv-x="1664" d="M725 977l-170 -450q33 0 136.5 -2t160.5 -2q19 0 57 2q-87 253 -184 452zM0 -128l2 79q23 7 56 12.5t57 10.5t49.5 14.5t44.5 29t31 50.5l237 616l280 724h75h53q8 -14 11 -21l205 -480q33 -78 106 -257.5t114 -274.5q15 -34 58 -144.5t72 -168.5q20 -45 35 -57 q19 -15 88 -29.5t84 -20.5q6 -38 6 -57q0 -4 -0.5 -13t-0.5 -13q-63 0 -190 8t-191 8q-76 0 -215 -7t-178 -8q0 43 4 78l131 28q1 0 12.5 2.5t15.5 3.5t14.5 4.5t15 6.5t11 8t9 11t2.5 14q0 16 -31 96.5t-72 177.5t-42 100l-450 2q-26 -58 -76.5 -195.5t-50.5 -162.5 q0 -22 14 -37.5t43.5 -24.5t48.5 -13.5t57 -8.5t41 -4q1 -19 1 -58q0 -9 -2 -27q-58 0 -174.5 10t-174.5 10q-8 0 -26.5 -4t-21.5 -4q-80 -14 -188 -14z" />
+<glyph unicode="&#xf032;" horiz-adv-x="1408" d="M555 15q74 -32 140 -32q376 0 376 335q0 114 -41 180q-27 44 -61.5 74t-67.5 46.5t-80.5 25t-84 10.5t-94.5 2q-73 0 -101 -10q0 -53 -0.5 -159t-0.5 -158q0 -8 -1 -67.5t-0.5 -96.5t4.5 -83.5t12 -66.5zM541 761q42 -7 109 -7q82 0 143 13t110 44.5t74.5 89.5t25.5 142 q0 70 -29 122.5t-79 82t-108 43.5t-124 14q-50 0 -130 -13q0 -50 4 -151t4 -152q0 -27 -0.5 -80t-0.5 -79q0 -46 1 -69zM0 -128l2 94q15 4 85 16t106 27q7 12 12.5 27t8.5 33.5t5.5 32.5t3 37.5t0.5 34v35.5v30q0 982 -22 1025q-4 8 -22 14.5t-44.5 11t-49.5 7t-48.5 4.5 t-30.5 3l-4 83q98 2 340 11.5t373 9.5q23 0 68.5 -0.5t67.5 -0.5q70 0 136.5 -13t128.5 -42t108 -71t74 -104.5t28 -137.5q0 -52 -16.5 -95.5t-39 -72t-64.5 -57.5t-73 -45t-84 -40q154 -35 256.5 -134t102.5 -248q0 -100 -35 -179.5t-93.5 -130.5t-138 -85.5t-163.5 -48.5 t-176 -14q-44 0 -132 3t-132 3q-106 0 -307 -11t-231 -12z" />
+<glyph unicode="&#xf033;" horiz-adv-x="1024" d="M0 -126l17 85q6 2 81.5 21.5t111.5 37.5q28 35 41 101q1 7 62 289t114 543.5t52 296.5v25q-24 13 -54.5 18.5t-69.5 8t-58 5.5l19 103q33 -2 120 -6.5t149.5 -7t120.5 -2.5q48 0 98.5 2.5t121 7t98.5 6.5q-5 -39 -19 -89q-30 -10 -101.5 -28.5t-108.5 -33.5 q-8 -19 -14 -42.5t-9 -40t-7.5 -45.5t-6.5 -42q-27 -148 -87.5 -419.5t-77.5 -355.5q-2 -9 -13 -58t-20 -90t-16 -83.5t-6 -57.5l1 -18q17 -4 185 -31q-3 -44 -16 -99q-11 0 -32.5 -1.5t-32.5 -1.5q-29 0 -87 10t-86 10q-138 2 -206 2q-51 0 -143 -9t-121 -11z" />
+<glyph unicode="&#xf034;" horiz-adv-x="1792" d="M1744 128q33 0 42 -18.5t-11 -44.5l-126 -162q-20 -26 -49 -26t-49 26l-126 162q-20 26 -11 44.5t42 18.5h80v1024h-80q-33 0 -42 18.5t11 44.5l126 162q20 26 49 26t49 -26l126 -162q20 -26 11 -44.5t-42 -18.5h-80v-1024h80zM81 1407l54 -27q12 -5 211 -5q44 0 132 2 t132 2q36 0 107.5 -0.5t107.5 -0.5h293q6 0 21 -0.5t20.5 0t16 3t17.5 9t15 17.5l42 1q4 0 14 -0.5t14 -0.5q2 -112 2 -336q0 -80 -5 -109q-39 -14 -68 -18q-25 44 -54 128q-3 9 -11 48t-14.5 73.5t-7.5 35.5q-6 8 -12 12.5t-15.5 6t-13 2.5t-18 0.5t-16.5 -0.5 q-17 0 -66.5 0.5t-74.5 0.5t-64 -2t-71 -6q-9 -81 -8 -136q0 -94 2 -388t2 -455q0 -16 -2.5 -71.5t0 -91.5t12.5 -69q40 -21 124 -42.5t120 -37.5q5 -40 5 -50q0 -14 -3 -29l-34 -1q-76 -2 -218 8t-207 10q-50 0 -151 -9t-152 -9q-3 51 -3 52v9q17 27 61.5 43t98.5 29t78 27 q19 42 19 383q0 101 -3 303t-3 303v117q0 2 0.5 15.5t0.5 25t-1 25.5t-3 24t-5 14q-11 12 -162 12q-33 0 -93 -12t-80 -26q-19 -13 -34 -72.5t-31.5 -111t-42.5 -53.5q-42 26 -56 44v383z" />
+<glyph unicode="&#xf035;" d="M81 1407l54 -27q12 -5 211 -5q44 0 132 2t132 2q70 0 246.5 1t304.5 0.5t247 -4.5q33 -1 56 31l42 1q4 0 14 -0.5t14 -0.5q2 -112 2 -336q0 -80 -5 -109q-39 -14 -68 -18q-25 44 -54 128q-3 9 -11 47.5t-15 73.5t-7 36q-10 13 -27 19q-5 2 -66 2q-30 0 -93 1t-103 1 t-94 -2t-96 -7q-9 -81 -8 -136l1 -152v52q0 -55 1 -154t1.5 -180t0.5 -153q0 -16 -2.5 -71.5t0 -91.5t12.5 -69q40 -21 124 -42.5t120 -37.5q5 -40 5 -50q0 -14 -3 -29l-34 -1q-76 -2 -218 8t-207 10q-50 0 -151 -9t-152 -9q-3 51 -3 52v9q17 27 61.5 43t98.5 29t78 27 q7 16 11.5 74t6 145.5t1.5 155t-0.5 153.5t-0.5 89q0 7 -2.5 21.5t-2.5 22.5q0 7 0.5 44t1 73t0 76.5t-3 67.5t-6.5 32q-11 12 -162 12q-41 0 -163 -13.5t-138 -24.5q-19 -12 -34 -71.5t-31.5 -111.5t-42.5 -54q-42 26 -56 44v383zM1310 125q12 0 42 -19.5t57.5 -41.5 t59.5 -49t36 -30q26 -21 26 -49t-26 -49q-4 -3 -36 -30t-59.5 -49t-57.5 -41.5t-42 -19.5q-13 0 -20.5 10.5t-10 28.5t-2.5 33.5t1.5 33t1.5 19.5h-1024q0 -2 1.5 -19.5t1.5 -33t-2.5 -33.5t-10 -28.5t-20.5 -10.5q-12 0 -42 19.5t-57.5 41.5t-59.5 49t-36 30q-26 21 -26 49 t26 49q4 3 36 30t59.5 49t57.5 41.5t42 19.5q13 0 20.5 -10.5t10 -28.5t2.5 -33.5t-1.5 -33t-1.5 -19.5h1024q0 2 -1.5 19.5t-1.5 33t2.5 33.5t10 28.5t20.5 10.5z" />
+<glyph unicode="&#xf036;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf037;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h896q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45t-45 -19 h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h640q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf038;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf039;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf03a;" horiz-adv-x="1792" d="M256 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM256 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5 t9.5 -22.5zM256 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344 q13 0 22.5 -9.5t9.5 -22.5zM256 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192 q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5z" />
+<glyph unicode="&#xf03b;" horiz-adv-x="1792" d="M384 992v-576q0 -13 -9.5 -22.5t-22.5 -9.5q-14 0 -23 9l-288 288q-9 9 -9 23t9 23l288 288q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
+<glyph unicode="&#xf03c;" horiz-adv-x="1792" d="M352 704q0 -14 -9 -23l-288 -288q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v576q0 13 9.5 22.5t22.5 9.5q14 0 23 -9l288 -288q9 -9 9 -23zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
+<glyph unicode="&#xf03d;" horiz-adv-x="1792" d="M1792 1184v-1088q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-403 403v-166q0 -119 -84.5 -203.5t-203.5 -84.5h-704q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h704q119 0 203.5 -84.5t84.5 -203.5v-165l403 402q18 19 45 19q12 0 25 -5 q39 -17 39 -59z" />
+<glyph unicode="&#xf03e;" horiz-adv-x="1920" d="M640 960q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 576v-448h-1408v192l320 320l160 -160l512 512zM1760 1280h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5v1216 q0 13 -9.5 22.5t-22.5 9.5zM1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf040;" d="M363 0l91 91l-235 235l-91 -91v-107h128v-128h107zM886 928q0 22 -22 22q-10 0 -17 -7l-542 -542q-7 -7 -7 -17q0 -22 22 -22q10 0 17 7l542 542q7 7 7 17zM832 1120l416 -416l-832 -832h-416v416zM1515 1024q0 -53 -37 -90l-166 -166l-416 416l166 165q36 38 90 38 q53 0 91 -38l235 -234q37 -39 37 -91z" />
+<glyph unicode="&#xf041;" horiz-adv-x="1024" d="M768 896q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1024 896q0 -109 -33 -179l-364 -774q-16 -33 -47.5 -52t-67.5 -19t-67.5 19t-46.5 52l-365 774q-33 70 -33 179q0 212 150 362t362 150t362 -150t150 -362z" />
+<glyph unicode="&#xf042;" d="M768 96v1088q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf043;" horiz-adv-x="1024" d="M512 384q0 36 -20 69q-1 1 -15.5 22.5t-25.5 38t-25 44t-21 50.5q-4 16 -21 16t-21 -16q-7 -23 -21 -50.5t-25 -44t-25.5 -38t-15.5 -22.5q-20 -33 -20 -69q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 512q0 -212 -150 -362t-362 -150t-362 150t-150 362 q0 145 81 275q6 9 62.5 90.5t101 151t99.5 178t83 201.5q9 30 34 47t51 17t51.5 -17t33.5 -47q28 -93 83 -201.5t99.5 -178t101 -151t62.5 -90.5q81 -127 81 -275z" />
+<glyph unicode="&#xf044;" horiz-adv-x="1792" d="M888 352l116 116l-152 152l-116 -116v-56h96v-96h56zM1328 1072q-16 16 -33 -1l-350 -350q-17 -17 -1 -33t33 1l350 350q17 17 1 33zM1408 478v-190q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-14 -14 -32 -8q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v126q0 13 9 22l64 64q15 15 35 7t20 -29zM1312 1216l288 -288l-672 -672h-288v288zM1756 1084l-92 -92 l-288 288l92 92q28 28 68 28t68 -28l152 -152q28 -28 28 -68t-28 -68z" />
+<glyph unicode="&#xf045;" horiz-adv-x="1664" d="M1408 547v-259q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h255v0q13 0 22.5 -9.5t9.5 -22.5q0 -27 -26 -32q-77 -26 -133 -60q-10 -4 -16 -4h-112q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832 q66 0 113 47t47 113v214q0 19 18 29q28 13 54 37q16 16 35 8q21 -9 21 -29zM1645 1043l-384 -384q-18 -19 -45 -19q-12 0 -25 5q-39 17 -39 59v192h-160q-323 0 -438 -131q-119 -137 -74 -473q3 -23 -20 -34q-8 -2 -12 -2q-16 0 -26 13q-10 14 -21 31t-39.5 68.5t-49.5 99.5 t-38.5 114t-17.5 122q0 49 3.5 91t14 90t28 88t47 81.5t68.5 74t94.5 61.5t124.5 48.5t159.5 30.5t196.5 11h160v192q0 42 39 59q13 5 25 5q26 0 45 -19l384 -384q19 -19 19 -45t-19 -45z" />
+<glyph unicode="&#xf046;" horiz-adv-x="1664" d="M1408 606v-318q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-10 -10 -23 -10q-3 0 -9 2q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832 q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v254q0 13 9 22l64 64q10 10 23 10q6 0 12 -3q20 -8 20 -29zM1639 1095l-814 -814q-24 -24 -57 -24t-57 24l-430 430q-24 24 -24 57t24 57l110 110q24 24 57 24t57 -24l263 -263l647 647q24 24 57 24t57 -24l110 -110 q24 -24 24 -57t-24 -57z" />
+<glyph unicode="&#xf047;" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-384v-384h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v384h-384v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45 t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h384v384h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45t-19 -45t-45 -19h-128v-384h384v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
+<glyph unicode="&#xf048;" horiz-adv-x="1024" d="M979 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19z" />
+<glyph unicode="&#xf049;" horiz-adv-x="1792" d="M1747 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19l710 710 q19 19 32 13t13 -32v-710q4 11 13 19z" />
+<glyph unicode="&#xf04a;" horiz-adv-x="1664" d="M1619 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-8 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-19 19 -19 45t19 45l710 710q19 19 32 13t13 -32v-710q5 11 13 19z" />
+<glyph unicode="&#xf04b;" horiz-adv-x="1408" d="M1384 609l-1328 -738q-23 -13 -39.5 -3t-16.5 36v1472q0 26 16.5 36t39.5 -3l1328 -738q23 -13 23 -31t-23 -31z" />
+<glyph unicode="&#xf04c;" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45zM640 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf04d;" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf04e;" horiz-adv-x="1664" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q19 -19 19 -45t-19 -45l-710 -710q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
+<glyph unicode="&#xf050;" horiz-adv-x="1792" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19l-710 -710 q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
+<glyph unicode="&#xf051;" horiz-adv-x="1024" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19z" />
+<glyph unicode="&#xf052;" horiz-adv-x="1538" d="M14 557l710 710q19 19 45 19t45 -19l710 -710q19 -19 13 -32t-32 -13h-1472q-26 0 -32 13t13 32zM1473 0h-1408q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1408q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19z" />
+<glyph unicode="&#xf053;" horiz-adv-x="1280" d="M1171 1235l-531 -531l531 -531q19 -19 19 -45t-19 -45l-166 -166q-19 -19 -45 -19t-45 19l-742 742q-19 19 -19 45t19 45l742 742q19 19 45 19t45 -19l166 -166q19 -19 19 -45t-19 -45z" />
+<glyph unicode="&#xf054;" horiz-adv-x="1280" d="M1107 659l-742 -742q-19 -19 -45 -19t-45 19l-166 166q-19 19 -19 45t19 45l531 531l-531 531q-19 19 -19 45t19 45l166 166q19 19 45 19t45 -19l742 -742q19 -19 19 -45t-19 -45z" />
+<glyph unicode="&#xf055;" d="M1216 576v128q0 26 -19 45t-45 19h-256v256q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-256h-256q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h256v-256q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v256h256q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5 t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf056;" d="M1216 576v128q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
+<glyph unicode="&#xf057;" d="M1149 414q0 26 -19 45l-181 181l181 181q19 19 19 45q0 27 -19 46l-90 90q-19 19 -46 19q-26 0 -45 -19l-181 -181l-181 181q-19 19 -45 19q-27 0 -46 -19l-90 -90q-19 -19 -19 -46q0 -26 19 -45l181 -181l-181 -181q-19 -19 -19 -45q0 -27 19 -46l90 -90q19 -19 46 -19 q26 0 45 19l181 181l181 -181q19 -19 45 -19q27 0 46 19l90 90q19 19 19 46zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf058;" d="M1284 802q0 28 -18 46l-91 90q-19 19 -45 19t-45 -19l-408 -407l-226 226q-19 19 -45 19t-45 -19l-91 -90q-18 -18 -18 -46q0 -27 18 -45l362 -362q19 -19 45 -19q27 0 46 19l543 543q18 18 18 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf059;" d="M896 160v192q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h192q14 0 23 9t9 23zM1152 832q0 88 -55.5 163t-138.5 116t-170 41q-243 0 -371 -213q-15 -24 8 -42l132 -100q7 -6 19 -6q16 0 25 12q53 68 86 92q34 24 86 24q48 0 85.5 -26t37.5 -59 q0 -38 -20 -61t-68 -45q-63 -28 -115.5 -86.5t-52.5 -125.5v-36q0 -14 9 -23t23 -9h192q14 0 23 9t9 23q0 19 21.5 49.5t54.5 49.5q32 18 49 28.5t46 35t44.5 48t28 60.5t12.5 81zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf05a;" d="M1024 160v160q0 14 -9 23t-23 9h-96v512q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h96v-320h-96q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h448q14 0 23 9t9 23zM896 1056v160q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23 t23 -9h192q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf05b;" d="M1197 512h-109q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h109q-32 108 -112.5 188.5t-188.5 112.5v-109q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v109q-108 -32 -188.5 -112.5t-112.5 -188.5h109q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-109 q32 -108 112.5 -188.5t188.5 -112.5v109q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-109q108 32 188.5 112.5t112.5 188.5zM1536 704v-128q0 -26 -19 -45t-45 -19h-143q-37 -161 -154.5 -278.5t-278.5 -154.5v-143q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v143 q-161 37 -278.5 154.5t-154.5 278.5h-143q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h143q37 161 154.5 278.5t278.5 154.5v143q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-143q161 -37 278.5 -154.5t154.5 -278.5h143q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf05c;" d="M1097 457l-146 -146q-10 -10 -23 -10t-23 10l-137 137l-137 -137q-10 -10 -23 -10t-23 10l-146 146q-10 10 -10 23t10 23l137 137l-137 137q-10 10 -10 23t10 23l146 146q10 10 23 10t23 -10l137 -137l137 137q10 10 23 10t23 -10l146 -146q10 -10 10 -23t-10 -23 l-137 -137l137 -137q10 -10 10 -23t-10 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5 t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf05d;" d="M1171 723l-422 -422q-19 -19 -45 -19t-45 19l-294 294q-19 19 -19 45t19 45l102 102q19 19 45 19t45 -19l147 -147l275 275q19 19 45 19t45 -19l102 -102q19 -19 19 -45t-19 -45zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198 t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf05e;" d="M1312 643q0 161 -87 295l-754 -753q137 -89 297 -89q111 0 211.5 43.5t173.5 116.5t116 174.5t43 212.5zM313 344l755 754q-135 91 -300 91q-148 0 -273 -73t-198 -199t-73 -274q0 -162 89 -299zM1536 643q0 -157 -61 -300t-163.5 -246t-245 -164t-298.5 -61t-298.5 61 t-245 164t-163.5 246t-61 300t61 299.5t163.5 245.5t245 164t298.5 61t298.5 -61t245 -164t163.5 -245.5t61 -299.5z" />
+<glyph unicode="&#xf060;" d="M1536 640v-128q0 -53 -32.5 -90.5t-84.5 -37.5h-704l293 -294q38 -36 38 -90t-38 -90l-75 -76q-37 -37 -90 -37q-52 0 -91 37l-651 652q-37 37 -37 90q0 52 37 91l651 650q38 38 91 38q52 0 90 -38l75 -74q38 -38 38 -91t-38 -91l-293 -293h704q52 0 84.5 -37.5 t32.5 -90.5z" />
+<glyph unicode="&#xf061;" d="M1472 576q0 -54 -37 -91l-651 -651q-39 -37 -91 -37q-51 0 -90 37l-75 75q-38 38 -38 91t38 91l293 293h-704q-52 0 -84.5 37.5t-32.5 90.5v128q0 53 32.5 90.5t84.5 37.5h704l-293 294q-38 36 -38 90t38 90l75 75q38 38 90 38q53 0 91 -38l651 -651q37 -35 37 -90z" />
+<glyph unicode="&#xf062;" horiz-adv-x="1664" d="M1611 565q0 -51 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-294 293v-704q0 -52 -37.5 -84.5t-90.5 -32.5h-128q-53 0 -90.5 32.5t-37.5 84.5v704l-294 -293q-36 -38 -90 -38t-90 38l-75 75q-38 38 -38 90q0 53 38 91l651 651q35 37 90 37q54 0 91 -37l651 -651 q37 -39 37 -91z" />
+<glyph unicode="&#xf063;" horiz-adv-x="1664" d="M1611 704q0 -53 -37 -90l-651 -652q-39 -37 -91 -37q-53 0 -90 37l-651 652q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l294 -294v704q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-704l294 294q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" />
+<glyph unicode="&#xf064;" horiz-adv-x="1792" d="M1792 896q0 -26 -19 -45l-512 -512q-19 -19 -45 -19t-45 19t-19 45v256h-224q-98 0 -175.5 -6t-154 -21.5t-133 -42.5t-105.5 -69.5t-80 -101t-48.5 -138.5t-17.5 -181q0 -55 5 -123q0 -6 2.5 -23.5t2.5 -26.5q0 -15 -8.5 -25t-23.5 -10q-16 0 -28 17q-7 9 -13 22 t-13.5 30t-10.5 24q-127 285 -127 451q0 199 53 333q162 403 875 403h224v256q0 26 19 45t45 19t45 -19l512 -512q19 -19 19 -45z" />
+<glyph unicode="&#xf065;" d="M755 480q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23zM1536 1344v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332 q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf066;" d="M768 576v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45zM1523 1248q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45 t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23z" />
+<glyph unicode="&#xf067;" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-416v-416q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v416h-416q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h416v416q0 40 28 68t68 28h192q40 0 68 -28t28 -68v-416h416q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf068;" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-1216q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h1216q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf069;" horiz-adv-x="1664" d="M1482 486q46 -26 59.5 -77.5t-12.5 -97.5l-64 -110q-26 -46 -77.5 -59.5t-97.5 12.5l-266 153v-307q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v307l-266 -153q-46 -26 -97.5 -12.5t-77.5 59.5l-64 110q-26 46 -12.5 97.5t59.5 77.5l266 154l-266 154 q-46 26 -59.5 77.5t12.5 97.5l64 110q26 46 77.5 59.5t97.5 -12.5l266 -153v307q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-307l266 153q46 26 97.5 12.5t77.5 -59.5l64 -110q26 -46 12.5 -97.5t-59.5 -77.5l-266 -154z" />
+<glyph unicode="&#xf06a;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM896 161v190q0 14 -9 23.5t-22 9.5h-192q-13 0 -23 -10t-10 -23v-190q0 -13 10 -23t23 -10h192 q13 0 22 9.5t9 23.5zM894 505l18 621q0 12 -10 18q-10 8 -24 8h-220q-14 0 -24 -8q-10 -6 -10 -18l17 -621q0 -10 10 -17.5t24 -7.5h185q14 0 23.5 7.5t10.5 17.5z" />
+<glyph unicode="&#xf06b;" d="M928 180v56v468v192h-320v-192v-468v-56q0 -25 18 -38.5t46 -13.5h192q28 0 46 13.5t18 38.5zM472 1024h195l-126 161q-26 31 -69 31q-40 0 -68 -28t-28 -68t28 -68t68 -28zM1160 1120q0 40 -28 68t-68 28q-43 0 -69 -31l-125 -161h194q40 0 68 28t28 68zM1536 864v-320 q0 -14 -9 -23t-23 -9h-96v-416q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v416h-96q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h440q-93 0 -158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5q107 0 168 -77l128 -165l128 165q61 77 168 77q93 0 158.5 -65.5t65.5 -158.5 t-65.5 -158.5t-158.5 -65.5h440q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf06c;" horiz-adv-x="1792" d="M1280 832q0 26 -19 45t-45 19q-172 0 -318 -49.5t-259.5 -134t-235.5 -219.5q-19 -21 -19 -45q0 -26 19 -45t45 -19q24 0 45 19q27 24 74 71t67 66q137 124 268.5 176t313.5 52q26 0 45 19t19 45zM1792 1030q0 -95 -20 -193q-46 -224 -184.5 -383t-357.5 -268 q-214 -108 -438 -108q-148 0 -286 47q-15 5 -88 42t-96 37q-16 0 -39.5 -32t-45 -70t-52.5 -70t-60 -32q-30 0 -51 11t-31 24t-27 42q-2 4 -6 11t-5.5 10t-3 9.5t-1.5 13.5q0 35 31 73.5t68 65.5t68 56t31 48q0 4 -14 38t-16 44q-9 51 -9 104q0 115 43.5 220t119 184.5 t170.5 139t204 95.5q55 18 145 25.5t179.5 9t178.5 6t163.5 24t113.5 56.5l29.5 29.5t29.5 28t27 20t36.5 16t43.5 4.5q39 0 70.5 -46t47.5 -112t24 -124t8 -96z" />
+<glyph unicode="&#xf06d;" horiz-adv-x="1408" d="M1408 -160v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1152 896q0 -78 -24.5 -144t-64 -112.5t-87.5 -88t-96 -77.5t-87.5 -72t-64 -81.5t-24.5 -96.5q0 -96 67 -224l-4 1l1 -1 q-90 41 -160 83t-138.5 100t-113.5 122.5t-72.5 150.5t-27.5 184q0 78 24.5 144t64 112.5t87.5 88t96 77.5t87.5 72t64 81.5t24.5 96.5q0 94 -66 224l3 -1l-1 1q90 -41 160 -83t138.5 -100t113.5 -122.5t72.5 -150.5t27.5 -184z" />
+<glyph unicode="&#xf06e;" horiz-adv-x="1792" d="M1664 576q-152 236 -381 353q61 -104 61 -225q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 121 61 225q-229 -117 -381 -353q133 -205 333.5 -326.5t434.5 -121.5t434.5 121.5t333.5 326.5zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5 t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1792 576q0 -34 -20 -69q-140 -230 -376.5 -368.5t-499.5 -138.5t-499.5 139t-376.5 368q-20 35 -20 69t20 69q140 229 376.5 368t499.5 139t499.5 -139t376.5 -368q20 -35 20 -69z" />
+<glyph unicode="&#xf070;" horiz-adv-x="1792" d="M555 201l78 141q-87 63 -136 159t-49 203q0 121 61 225q-229 -117 -381 -353q167 -258 427 -375zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1307 1151q0 -7 -1 -9 q-105 -188 -315 -566t-316 -567l-49 -89q-10 -16 -28 -16q-12 0 -134 70q-16 10 -16 28q0 12 44 87q-143 65 -263.5 173t-208.5 245q-20 31 -20 69t20 69q153 235 380 371t496 136q89 0 180 -17l54 97q10 16 28 16q5 0 18 -6t31 -15.5t33 -18.5t31.5 -18.5t19.5 -11.5 q16 -10 16 -27zM1344 704q0 -139 -79 -253.5t-209 -164.5l280 502q8 -45 8 -84zM1792 576q0 -35 -20 -69q-39 -64 -109 -145q-150 -172 -347.5 -267t-419.5 -95l74 132q212 18 392.5 137t301.5 307q-115 179 -282 294l63 112q95 -64 182.5 -153t144.5 -184q20 -34 20 -69z " />
+<glyph unicode="&#xf071;" horiz-adv-x="1792" d="M1024 161v190q0 14 -9.5 23.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -23.5v-190q0 -14 9.5 -23.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 23.5zM1022 535l18 459q0 12 -10 19q-13 11 -24 11h-220q-11 0 -24 -11q-10 -7 -10 -21l17 -457q0 -10 10 -16.5t24 -6.5h185 q14 0 23.5 6.5t10.5 16.5zM1008 1469l768 -1408q35 -63 -2 -126q-17 -29 -46.5 -46t-63.5 -17h-1536q-34 0 -63.5 17t-46.5 46q-37 63 -2 126l768 1408q17 31 47 49t65 18t65 -18t47 -49z" />
+<glyph unicode="&#xf072;" horiz-adv-x="1408" d="M1376 1376q44 -52 12 -148t-108 -172l-161 -161l160 -696q5 -19 -12 -33l-128 -96q-7 -6 -19 -6q-4 0 -7 1q-15 3 -21 16l-279 508l-259 -259l53 -194q5 -17 -8 -31l-96 -96q-9 -9 -23 -9h-2q-15 2 -24 13l-189 252l-252 189q-11 7 -13 23q-1 13 9 25l96 97q9 9 23 9 q6 0 8 -1l194 -53l259 259l-508 279q-14 8 -17 24q-2 16 9 27l128 128q14 13 30 8l665 -159l160 160q76 76 172 108t148 -12z" />
+<glyph unicode="&#xf073;" horiz-adv-x="1664" d="M128 -128h288v288h-288v-288zM480 -128h320v288h-320v-288zM128 224h288v320h-288v-320zM480 224h320v320h-320v-320zM128 608h288v288h-288v-288zM864 -128h320v288h-320v-288zM480 608h320v288h-320v-288zM1248 -128h288v288h-288v-288zM864 224h320v320h-320v-320z M512 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1248 224h288v320h-288v-320zM864 608h320v288h-320v-288zM1248 608h288v288h-288v-288zM1280 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64 q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47 h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf074;" horiz-adv-x="1792" d="M666 1055q-60 -92 -137 -273q-22 45 -37 72.5t-40.5 63.5t-51 56.5t-63 35t-81.5 14.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q250 0 410 -225zM1792 256q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192q-32 0 -85 -0.5t-81 -1t-73 1 t-71 5t-64 10.5t-63 18.5t-58 28.5t-59 40t-55 53.5t-56 69.5q59 93 136 273q22 -45 37 -72.5t40.5 -63.5t51 -56.5t63 -35t81.5 -14.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1792 1152q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5 v192h-256q-48 0 -87 -15t-69 -45t-51 -61.5t-45 -77.5q-32 -62 -78 -171q-29 -66 -49.5 -111t-54 -105t-64 -100t-74 -83t-90 -68.5t-106.5 -42t-128 -16.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q48 0 87 15t69 45t51 61.5t45 77.5q32 62 78 171q29 66 49.5 111 t54 105t64 100t74 83t90 68.5t106.5 42t128 16.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
+<glyph unicode="&#xf075;" horiz-adv-x="1792" d="M1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22q-17 -2 -30.5 9t-17.5 29v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281 q0 130 71 248.5t191 204.5t286 136.5t348 50.5q244 0 450 -85.5t326 -233t120 -321.5z" />
+<glyph unicode="&#xf076;" d="M1536 704v-128q0 -201 -98.5 -362t-274 -251.5t-395.5 -90.5t-395.5 90.5t-274 251.5t-98.5 362v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-128q0 -52 23.5 -90t53.5 -57t71 -30t64 -13t44 -2t44 2t64 13t71 30t53.5 57t23.5 90v128q0 26 19 45t45 19h384 q26 0 45 -19t19 -45zM512 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45zM1536 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf077;" horiz-adv-x="1792" d="M1683 205l-166 -165q-19 -19 -45 -19t-45 19l-531 531l-531 -531q-19 -19 -45 -19t-45 19l-166 165q-19 19 -19 45.5t19 45.5l742 741q19 19 45 19t45 -19l742 -741q19 -19 19 -45.5t-19 -45.5z" />
+<glyph unicode="&#xf078;" horiz-adv-x="1792" d="M1683 728l-742 -741q-19 -19 -45 -19t-45 19l-742 741q-19 19 -19 45.5t19 45.5l166 165q19 19 45 19t45 -19l531 -531l531 531q19 19 45 19t45 -19l166 -165q19 -19 19 -45.5t-19 -45.5z" />
+<glyph unicode="&#xf079;" horiz-adv-x="1920" d="M1280 32q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-8 0 -13.5 2t-9 7t-5.5 8t-3 11.5t-1 11.5v13v11v160v416h-192q-26 0 -45 19t-19 45q0 24 15 41l320 384q19 22 49 22t49 -22l320 -384q15 -17 15 -41q0 -26 -19 -45t-45 -19h-192v-384h576q16 0 25 -11l160 -192q7 -11 7 -21 zM1920 448q0 -24 -15 -41l-320 -384q-20 -23 -49 -23t-49 23l-320 384q-15 17 -15 41q0 26 19 45t45 19h192v384h-576q-16 0 -25 12l-160 192q-7 9 -7 20q0 13 9.5 22.5t22.5 9.5h960q8 0 13.5 -2t9 -7t5.5 -8t3 -11.5t1 -11.5v-13v-11v-160v-416h192q26 0 45 -19t19 -45z " />
+<glyph unicode="&#xf07a;" horiz-adv-x="1664" d="M640 0q0 -52 -38 -90t-90 -38t-90 38t-38 90t38 90t90 38t90 -38t38 -90zM1536 0q0 -52 -38 -90t-90 -38t-90 38t-38 90t38 90t90 38t90 -38t38 -90zM1664 1088v-512q0 -24 -16.5 -42.5t-40.5 -21.5l-1044 -122q13 -60 13 -70q0 -16 -24 -64h920q26 0 45 -19t19 -45 t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 11 8 31.5t16 36t21.5 40t15.5 29.5l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t19.5 -15.5t13 -24.5t8 -26t5.5 -29.5t4.5 -26h1201q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf07b;" horiz-adv-x="1664" d="M1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
+<glyph unicode="&#xf07c;" horiz-adv-x="1920" d="M1879 584q0 -31 -31 -66l-336 -396q-43 -51 -120.5 -86.5t-143.5 -35.5h-1088q-34 0 -60.5 13t-26.5 43q0 31 31 66l336 396q43 51 120.5 86.5t143.5 35.5h1088q34 0 60.5 -13t26.5 -43zM1536 928v-160h-832q-94 0 -197 -47.5t-164 -119.5l-337 -396l-5 -6q0 4 -0.5 12.5 t-0.5 12.5v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158z" />
+<glyph unicode="&#xf07d;" horiz-adv-x="768" d="M704 1216q0 -26 -19 -45t-45 -19h-128v-1024h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v1024h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45z" />
+<glyph unicode="&#xf07e;" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-1024v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h1024v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
+<glyph unicode="&#xf080;" horiz-adv-x="2048" d="M640 640v-512h-256v512h256zM1024 1152v-1024h-256v1024h256zM2048 0v-128h-2048v1536h128v-1408h1920zM1408 896v-768h-256v768h256zM1792 1280v-1152h-256v1152h256z" />
+<glyph unicode="&#xf081;" d="M1280 926q-56 -25 -121 -34q68 40 93 117q-65 -38 -134 -51q-61 66 -153 66q-87 0 -148.5 -61.5t-61.5 -148.5q0 -29 5 -48q-129 7 -242 65t-192 155q-29 -50 -29 -106q0 -114 91 -175q-47 1 -100 26v-2q0 -75 50 -133.5t123 -72.5q-29 -8 -51 -8q-13 0 -39 4 q21 -63 74.5 -104t121.5 -42q-116 -90 -261 -90q-26 0 -50 3q148 -94 322 -94q112 0 210 35.5t168 95t120.5 137t75 162t24.5 168.5q0 18 -1 27q63 45 105 109zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5 t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf082;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-188v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-532q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960z" />
+<glyph unicode="&#xf083;" horiz-adv-x="1792" d="M928 704q0 14 -9 23t-23 9q-66 0 -113 -47t-47 -113q0 -14 9 -23t23 -9t23 9t9 23q0 40 28 68t68 28q14 0 23 9t9 23zM1152 574q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM128 0h1536v128h-1536v-128zM1280 574q0 159 -112.5 271.5 t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM256 1216h384v128h-384v-128zM128 1024h1536v118v138h-828l-64 -128h-644v-128zM1792 1280v-1280q0 -53 -37.5 -90.5t-90.5 -37.5h-1536q-53 0 -90.5 37.5t-37.5 90.5v1280 q0 53 37.5 90.5t90.5 37.5h1536q53 0 90.5 -37.5t37.5 -90.5z" />
+<glyph unicode="&#xf084;" horiz-adv-x="1792" d="M832 1024q0 80 -56 136t-136 56t-136 -56t-56 -136q0 -42 19 -83q-41 19 -83 19q-80 0 -136 -56t-56 -136t56 -136t136 -56t136 56t56 136q0 42 -19 83q41 -19 83 -19q80 0 136 56t56 136zM1683 320q0 -17 -49 -66t-66 -49q-9 0 -28.5 16t-36.5 33t-38.5 40t-24.5 26 l-96 -96l220 -220q28 -28 28 -68q0 -42 -39 -81t-81 -39q-40 0 -68 28l-671 671q-176 -131 -365 -131q-163 0 -265.5 102.5t-102.5 265.5q0 160 95 313t248 248t313 95q163 0 265.5 -102.5t102.5 -265.5q0 -189 -131 -365l355 -355l96 96q-3 3 -26 24.5t-40 38.5t-33 36.5 t-16 28.5q0 17 49 66t66 49q13 0 23 -10q6 -6 46 -44.5t82 -79.5t86.5 -86t73 -78t28.5 -41z" />
+<glyph unicode="&#xf085;" horiz-adv-x="1920" d="M896 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1664 128q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1152q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1280 731v-185q0 -10 -7 -19.5t-16 -10.5l-155 -24q-11 -35 -32 -76q34 -48 90 -115q7 -10 7 -20q0 -12 -7 -19q-23 -30 -82.5 -89.5t-78.5 -59.5q-11 0 -21 7l-115 90q-37 -19 -77 -31q-11 -108 -23 -155q-7 -24 -30 -24h-186q-11 0 -20 7.5t-10 17.5 l-23 153q-34 10 -75 31l-118 -89q-7 -7 -20 -7q-11 0 -21 8q-144 133 -144 160q0 9 7 19q10 14 41 53t47 61q-23 44 -35 82l-152 24q-10 1 -17 9.5t-7 19.5v185q0 10 7 19.5t16 10.5l155 24q11 35 32 76q-34 48 -90 115q-7 11 -7 20q0 12 7 20q22 30 82 89t79 59q11 0 21 -7 l115 -90q34 18 77 32q11 108 23 154q7 24 30 24h186q11 0 20 -7.5t10 -17.5l23 -153q34 -10 75 -31l118 89q8 7 20 7q11 0 21 -8q144 -133 144 -160q0 -9 -7 -19q-12 -16 -42 -54t-45 -60q23 -48 34 -82l152 -23q10 -2 17 -10.5t7 -19.5zM1920 198v-140q0 -16 -149 -31 q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20 t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31zM1920 1222v-140q0 -16 -149 -31q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68 q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70 q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31z" />
+<glyph unicode="&#xf086;" horiz-adv-x="1792" d="M1408 768q0 -139 -94 -257t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224 q0 139 94 257t256.5 186.5t353.5 68.5t353.5 -68.5t256.5 -186.5t94 -257zM1792 512q0 -120 -71 -224.5t-195 -176.5q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7 q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230z" />
+<glyph unicode="&#xf087;" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 768q0 51 -39 89.5t-89 38.5h-352q0 58 48 159.5t48 160.5q0 98 -32 145t-128 47q-26 -26 -38 -85t-30.5 -125.5t-59.5 -109.5q-22 -23 -77 -91q-4 -5 -23 -30t-31.5 -41t-34.5 -42.5 t-40 -44t-38.5 -35.5t-40 -27t-35.5 -9h-32v-640h32q13 0 31.5 -3t33 -6.5t38 -11t35 -11.5t35.5 -12.5t29 -10.5q211 -73 342 -73h121q192 0 192 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5q32 1 53.5 47t21.5 81zM1536 769 q0 -89 -49 -163q9 -33 9 -69q0 -77 -38 -144q3 -21 3 -43q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5h-36h-93q-96 0 -189.5 22.5t-216.5 65.5q-116 40 -138 40h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h274q36 24 137 155q58 75 107 128 q24 25 35.5 85.5t30.5 126.5t62 108q39 37 90 37q84 0 151 -32.5t102 -101.5t35 -186q0 -93 -48 -192h176q104 0 180 -76t76 -179z" />
+<glyph unicode="&#xf088;" d="M256 1088q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 512q0 35 -21.5 81t-53.5 47q15 17 25 47.5t10 55.5q0 69 -53 119q18 32 18 69t-17.5 73.5t-47.5 52.5q5 30 5 56q0 85 -49 126t-136 41h-128q-131 0 -342 -73q-5 -2 -29 -10.5 t-35.5 -12.5t-35 -11.5t-38 -11t-33 -6.5t-31.5 -3h-32v-640h32q16 0 35.5 -9t40 -27t38.5 -35.5t40 -44t34.5 -42.5t31.5 -41t23 -30q55 -68 77 -91q41 -43 59.5 -109.5t30.5 -125.5t38 -85q96 0 128 47t32 145q0 59 -48 160.5t-48 159.5h352q50 0 89 38.5t39 89.5z M1536 511q0 -103 -76 -179t-180 -76h-176q48 -99 48 -192q0 -118 -35 -186q-35 -69 -102 -101.5t-151 -32.5q-51 0 -90 37q-34 33 -54 82t-25.5 90.5t-17.5 84.5t-31 64q-48 50 -107 127q-101 131 -137 155h-274q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5 h288q22 0 138 40q128 44 223 66t200 22h112q140 0 226.5 -79t85.5 -216v-5q60 -77 60 -178q0 -22 -3 -43q38 -67 38 -144q0 -36 -9 -69q49 -74 49 -163z" />
+<glyph unicode="&#xf089;" horiz-adv-x="896" d="M832 1504v-1339l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41z" />
+<glyph unicode="&#xf08a;" horiz-adv-x="1792" d="M1664 940q0 81 -21.5 143t-55 98.5t-81.5 59.5t-94 31t-98 8t-112 -25.5t-110.5 -64t-86.5 -72t-60 -61.5q-18 -22 -49 -22t-49 22q-24 28 -60 61.5t-86.5 72t-110.5 64t-112 25.5t-98 -8t-94 -31t-81.5 -59.5t-55 -98.5t-21.5 -143q0 -168 187 -355l581 -560l580 559 q188 188 188 356zM1792 940q0 -221 -229 -450l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5 q224 0 351 -124t127 -344z" />
+<glyph unicode="&#xf08b;" horiz-adv-x="1664" d="M640 96q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h320q13 0 22.5 -9.5t9.5 -22.5q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-66 0 -113 -47t-47 -113v-704 q0 -66 47 -113t113 -47h288h11h13t11.5 -1t11.5 -3t8 -5.5t7 -9t2 -13.5zM1568 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45z" />
+<glyph unicode="&#xf08c;" d="M237 122h231v694h-231v-694zM483 1030q-1 52 -36 86t-93 34t-94.5 -34t-36.5 -86q0 -51 35.5 -85.5t92.5 -34.5h1q59 0 95 34.5t36 85.5zM1068 122h231v398q0 154 -73 233t-193 79q-136 0 -209 -117h2v101h-231q3 -66 0 -694h231v388q0 38 7 56q15 35 45 59.5t74 24.5 q116 0 116 -157v-371zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf08d;" horiz-adv-x="1152" d="M480 672v448q0 14 -9 23t-23 9t-23 -9t-9 -23v-448q0 -14 9 -23t23 -9t23 9t9 23zM1152 320q0 -26 -19 -45t-45 -19h-429l-51 -483q-2 -12 -10.5 -20.5t-20.5 -8.5h-1q-27 0 -32 27l-76 485h-404q-26 0 -45 19t-19 45q0 123 78.5 221.5t177.5 98.5v512q-52 0 -90 38 t-38 90t38 90t90 38h640q52 0 90 -38t38 -90t-38 -90t-90 -38v-512q99 0 177.5 -98.5t78.5 -221.5z" />
+<glyph unicode="&#xf08e;" horiz-adv-x="1792" d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf090;" d="M1184 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45zM1536 992v-704q0 -119 -84.5 -203.5t-203.5 -84.5h-320q-13 0 -22.5 9.5t-9.5 22.5 q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q66 0 113 47t47 113v704q0 66 -47 113t-113 47h-288h-11h-13t-11.5 1t-11.5 3t-8 5.5t-7 9t-2 13.5q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf091;" horiz-adv-x="1664" d="M458 653q-74 162 -74 371h-256v-96q0 -78 94.5 -162t235.5 -113zM1536 928v96h-256q0 -209 -74 -371q141 29 235.5 113t94.5 162zM1664 1056v-128q0 -71 -41.5 -143t-112 -130t-173 -97.5t-215.5 -44.5q-42 -54 -95 -95q-38 -34 -52.5 -72.5t-14.5 -89.5q0 -54 30.5 -91 t97.5 -37q75 0 133.5 -45.5t58.5 -114.5v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 69 58.5 114.5t133.5 45.5q67 0 97.5 37t30.5 91q0 51 -14.5 89.5t-52.5 72.5q-53 41 -95 95q-113 5 -215.5 44.5t-173 97.5t-112 130t-41.5 143v128q0 40 28 68t68 28h288v96 q0 66 47 113t113 47h576q66 0 113 -47t47 -113v-96h288q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf092;" d="M394 184q-8 -9 -20 3q-13 11 -4 19q8 9 20 -3q12 -11 4 -19zM352 245q9 -12 0 -19q-8 -6 -17 7t0 18q9 7 17 -6zM291 305q-5 -7 -13 -2q-10 5 -7 12q3 5 13 2q10 -5 7 -12zM322 271q-6 -7 -16 3q-9 11 -2 16q6 6 16 -3q9 -11 2 -16zM451 159q-4 -12 -19 -6q-17 4 -13 15 t19 7q16 -5 13 -16zM514 154q0 -11 -16 -11q-17 -2 -17 11q0 11 16 11q17 2 17 -11zM572 164q2 -10 -14 -14t-18 8t14 15q16 2 18 -9zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-224q-16 0 -24.5 1t-19.5 5t-16 14.5t-5 27.5v239q0 97 -52 142q57 6 102.5 18t94 39 t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103 q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -103t0.5 -68q0 -22 -11 -33.5t-22 -13t-33 -1.5 h-224q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf093;" horiz-adv-x="1664" d="M1280 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 288v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h427q21 -56 70.5 -92 t110.5 -36h256q61 0 110.5 36t70.5 92h427q40 0 68 -28t28 -68zM1339 936q-17 -40 -59 -40h-256v-448q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v448h-256q-42 0 -59 40q-17 39 14 69l448 448q18 19 45 19t45 -19l448 -448q31 -30 14 -69z" />
+<glyph unicode="&#xf094;" d="M1407 710q0 44 -7 113.5t-18 96.5q-12 30 -17 44t-9 36.5t-4 48.5q0 23 5 68.5t5 67.5q0 37 -10 55q-4 1 -13 1q-19 0 -58 -4.5t-59 -4.5q-60 0 -176 24t-175 24q-43 0 -94.5 -11.5t-85 -23.5t-89.5 -34q-137 -54 -202 -103q-96 -73 -159.5 -189.5t-88 -236t-24.5 -248.5 q0 -40 12.5 -120t12.5 -121q0 -23 -11 -66.5t-11 -65.5t12 -36.5t34 -14.5q24 0 72.5 11t73.5 11q57 0 169.5 -15.5t169.5 -15.5q181 0 284 36q129 45 235.5 152.5t166 245.5t59.5 275zM1535 712q0 -165 -70 -327.5t-196 -288t-281 -180.5q-124 -44 -326 -44 q-57 0 -170 14.5t-169 14.5q-24 0 -72.5 -14.5t-73.5 -14.5q-73 0 -123.5 55.5t-50.5 128.5q0 24 11 68t11 67q0 40 -12.5 120.5t-12.5 121.5q0 111 18 217.5t54.5 209.5t100.5 194t150 156q78 59 232 120q194 78 316 78q60 0 175.5 -24t173.5 -24q19 0 57 5t58 5 q81 0 118 -50.5t37 -134.5q0 -23 -5 -68t-5 -68q0 -10 1 -18.5t3 -17t4 -13.5t6.5 -16t6.5 -17q16 -40 25 -118.5t9 -136.5z" />
+<glyph unicode="&#xf095;" horiz-adv-x="1408" d="M1408 296q0 -27 -10 -70.5t-21 -68.5q-21 -50 -122 -106q-94 -51 -186 -51q-27 0 -52.5 3.5t-57.5 12.5t-47.5 14.5t-55.5 20.5t-49 18q-98 35 -175 83q-128 79 -264.5 215.5t-215.5 264.5q-48 77 -83 175q-3 9 -18 49t-20.5 55.5t-14.5 47.5t-12.5 57.5t-3.5 52.5 q0 92 51 186q56 101 106 122q25 11 68.5 21t70.5 10q14 0 21 -3q18 -6 53 -76q11 -19 30 -54t35 -63.5t31 -53.5q3 -4 17.5 -25t21.5 -35.5t7 -28.5q0 -20 -28.5 -50t-62 -55t-62 -53t-28.5 -46q0 -9 5 -22.5t8.5 -20.5t14 -24t11.5 -19q76 -137 174 -235t235 -174 q2 -1 19 -11.5t24 -14t20.5 -8.5t22.5 -5q18 0 46 28.5t53 62t55 62t50 28.5q14 0 28.5 -7t35.5 -21.5t25 -17.5q25 -15 53.5 -31t63.5 -35t54 -30q70 -35 76 -53q3 -7 3 -21z" />
+<glyph unicode="&#xf096;" horiz-adv-x="1408" d="M1120 1280h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v832q0 66 -47 113t-113 47zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf097;" horiz-adv-x="1280" d="M1152 1280h-1024v-1242l423 406l89 85l89 -85l423 -406v1242zM1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289 q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
+<glyph unicode="&#xf098;" d="M1280 343q0 11 -2 16q-3 8 -38.5 29.5t-88.5 49.5l-53 29q-5 3 -19 13t-25 15t-21 5q-18 0 -47 -32.5t-57 -65.5t-44 -33q-7 0 -16.5 3.5t-15.5 6.5t-17 9.5t-14 8.5q-99 55 -170.5 126.5t-126.5 170.5q-2 3 -8.5 14t-9.5 17t-6.5 15.5t-3.5 16.5q0 13 20.5 33.5t45 38.5 t45 39.5t20.5 36.5q0 10 -5 21t-15 25t-13 19q-3 6 -15 28.5t-25 45.5t-26.5 47.5t-25 40.5t-16.5 18t-16 2q-48 0 -101 -22q-46 -21 -80 -94.5t-34 -130.5q0 -16 2.5 -34t5 -30.5t9 -33t10 -29.5t12.5 -33t11 -30q60 -164 216.5 -320.5t320.5 -216.5q6 -2 30 -11t33 -12.5 t29.5 -10t33 -9t30.5 -5t34 -2.5q57 0 130.5 34t94.5 80q22 53 22 101zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf099;" horiz-adv-x="1664" d="M1620 1128q-67 -98 -162 -167q1 -14 1 -42q0 -130 -38 -259.5t-115.5 -248.5t-184.5 -210.5t-258 -146t-323 -54.5q-271 0 -496 145q35 -4 78 -4q225 0 401 138q-105 2 -188 64.5t-114 159.5q33 -5 61 -5q43 0 85 11q-112 23 -185.5 111.5t-73.5 205.5v4q68 -38 146 -41 q-66 44 -105 115t-39 154q0 88 44 163q121 -149 294.5 -238.5t371.5 -99.5q-8 38 -8 74q0 134 94.5 228.5t228.5 94.5q140 0 236 -102q109 21 205 78q-37 -115 -142 -178q93 10 186 50z" />
+<glyph unicode="&#xf09a;" horiz-adv-x="1024" d="M959 1524v-264h-157q-86 0 -116 -36t-30 -108v-189h293l-39 -296h-254v-759h-306v759h-255v296h255v218q0 186 104 288.5t277 102.5q147 0 228 -12z" />
+<glyph unicode="&#xf09b;" d="M1536 640q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -39.5 7t-12.5 30v211q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5 q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23 q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -89t0.5 -54q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf09c;" horiz-adv-x="1664" d="M1664 960v-256q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-192h96q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h672v192q0 185 131.5 316.5t316.5 131.5 t316.5 -131.5t131.5 -316.5z" />
+<glyph unicode="&#xf09d;" horiz-adv-x="1920" d="M1760 1408q66 0 113 -47t47 -113v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600zM160 1280q-13 0 -22.5 -9.5t-9.5 -22.5v-224h1664v224q0 13 -9.5 22.5t-22.5 9.5h-1600zM1760 0q13 0 22.5 9.5t9.5 22.5v608h-1664v-608 q0 -13 9.5 -22.5t22.5 -9.5h1600zM256 128v128h256v-128h-256zM640 128v128h384v-128h-384z" />
+<glyph unicode="&#xf09e;" horiz-adv-x="1408" d="M384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 69q2 -28 -17 -48q-18 -21 -47 -21h-135q-25 0 -43 16.5t-20 41.5q-22 229 -184.5 391.5t-391.5 184.5q-25 2 -41.5 20t-16.5 43v135q0 29 21 47q17 17 43 17h5q160 -13 306 -80.5 t259 -181.5q114 -113 181.5 -259t80.5 -306zM1408 67q2 -27 -18 -47q-18 -20 -46 -20h-143q-26 0 -44.5 17.5t-19.5 42.5q-12 215 -101 408.5t-231.5 336t-336 231.5t-408.5 102q-25 1 -42.5 19.5t-17.5 43.5v143q0 28 20 46q18 18 44 18h3q262 -13 501.5 -120t425.5 -294 q187 -186 294 -425.5t120 -501.5z" />
+<glyph unicode="&#xf0a0;" d="M1040 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1296 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1408 160v320q0 13 -9.5 22.5t-22.5 9.5 h-1216q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h1216q13 0 22.5 9.5t9.5 22.5zM178 640h1180l-157 482q-4 13 -16 21.5t-26 8.5h-782q-14 0 -26 -8.5t-16 -21.5zM1536 480v-320q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v320q0 25 16 75 l197 606q17 53 63 86t101 33h782q55 0 101 -33t63 -86l197 -606q16 -50 16 -75z" />
+<glyph unicode="&#xf0a1;" horiz-adv-x="1792" d="M1664 896q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5v-384q0 -52 -38 -90t-90 -38q-417 347 -812 380q-58 -19 -91 -66t-31 -100.5t40 -92.5q-20 -33 -23 -65.5t6 -58t33.5 -55t48 -50t61.5 -50.5q-29 -58 -111.5 -83t-168.5 -11.5t-132 55.5q-7 23 -29.5 87.5 t-32 94.5t-23 89t-15 101t3.5 98.5t22 110.5h-122q-66 0 -113 47t-47 113v192q0 66 47 113t113 47h480q435 0 896 384q52 0 90 -38t38 -90v-384zM1536 292v954q-394 -302 -768 -343v-270q377 -42 768 -341z" />
+<glyph unicode="&#xf0a2;" horiz-adv-x="1792" d="M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM246 128h1300q-266 300 -266 832q0 51 -24 105t-69 103t-121.5 80.5t-169.5 31.5t-169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -532 -266 -832z M1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5 t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z" />
+<glyph unicode="&#xf0a3;" d="M1376 640l138 -135q30 -28 20 -70q-12 -41 -52 -51l-188 -48l53 -186q12 -41 -19 -70q-29 -31 -70 -19l-186 53l-48 -188q-10 -40 -51 -52q-12 -2 -19 -2q-31 0 -51 22l-135 138l-135 -138q-28 -30 -70 -20q-41 11 -51 52l-48 188l-186 -53q-41 -12 -70 19q-31 29 -19 70 l53 186l-188 48q-40 10 -52 51q-10 42 20 70l138 135l-138 135q-30 28 -20 70q12 41 52 51l188 48l-53 186q-12 41 19 70q29 31 70 19l186 -53l48 188q10 41 51 51q41 12 70 -19l135 -139l135 139q29 30 70 19q41 -10 51 -51l48 -188l186 53q41 12 70 -19q31 -29 19 -70 l-53 -186l188 -48q40 -10 52 -51q10 -42 -20 -70z" />
+<glyph unicode="&#xf0a4;" horiz-adv-x="1792" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 768q0 51 -39 89.5t-89 38.5h-576q0 20 15 48.5t33 55t33 68t15 84.5q0 67 -44.5 97.5t-115.5 30.5q-24 0 -90 -139q-24 -44 -37 -65q-40 -64 -112 -145q-71 -81 -101 -106 q-69 -57 -140 -57h-32v-640h32q72 0 167 -32t193.5 -64t179.5 -32q189 0 189 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5h331q52 0 90 38t38 90zM1792 769q0 -105 -75.5 -181t-180.5 -76h-169q-4 -62 -37 -119q3 -21 3 -43 q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5q-133 0 -322 69q-164 59 -223 59h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h288q10 0 21.5 4.5t23.5 14t22.5 18t24 22.5t20.5 21.5t19 21.5t14 17q65 74 100 129q13 21 33 62t37 72t40.5 63t55 49.5 t69.5 17.5q125 0 206.5 -67t81.5 -189q0 -68 -22 -128h374q104 0 180 -76t76 -179z" />
+<glyph unicode="&#xf0a5;" horiz-adv-x="1792" d="M1376 128h32v640h-32q-35 0 -67.5 12t-62.5 37t-50 46t-49 54q-2 3 -3.5 4.5t-4 4.5t-4.5 5q-72 81 -112 145q-14 22 -38 68q-1 3 -10.5 22.5t-18.5 36t-20 35.5t-21.5 30.5t-18.5 11.5q-71 0 -115.5 -30.5t-44.5 -97.5q0 -43 15 -84.5t33 -68t33 -55t15 -48.5h-576 q-50 0 -89 -38.5t-39 -89.5q0 -52 38 -90t90 -38h331q-15 -17 -25 -47.5t-10 -55.5q0 -69 53 -119q-18 -32 -18 -69t17.5 -73.5t47.5 -52.5q-4 -24 -4 -56q0 -85 48.5 -126t135.5 -41q84 0 183 32t194 64t167 32zM1664 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45 t45 -19t45 19t19 45zM1792 768v-640q0 -53 -37.5 -90.5t-90.5 -37.5h-288q-59 0 -223 -59q-190 -69 -317 -69q-142 0 -230 77.5t-87 217.5l1 5q-61 76 -61 178q0 22 3 43q-33 57 -37 119h-169q-105 0 -180.5 76t-75.5 181q0 103 76 179t180 76h374q-22 60 -22 128 q0 122 81.5 189t206.5 67q38 0 69.5 -17.5t55 -49.5t40.5 -63t37 -72t33 -62q35 -55 100 -129q2 -3 14 -17t19 -21.5t20.5 -21.5t24 -22.5t22.5 -18t23.5 -14t21.5 -4.5h288q53 0 90.5 -37.5t37.5 -90.5z" />
+<glyph unicode="&#xf0a6;" d="M1280 -64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 700q0 189 -167 189q-26 0 -56 -5q-16 30 -52.5 47.5t-73.5 17.5t-69 -18q-50 53 -119 53q-25 0 -55.5 -10t-47.5 -25v331q0 52 -38 90t-90 38q-51 0 -89.5 -39t-38.5 -89v-576 q-20 0 -48.5 15t-55 33t-68 33t-84.5 15q-67 0 -97.5 -44.5t-30.5 -115.5q0 -24 139 -90q44 -24 65 -37q64 -40 145 -112q81 -71 106 -101q57 -69 57 -140v-32h640v32q0 72 32 167t64 193.5t32 179.5zM1536 705q0 -133 -69 -322q-59 -164 -59 -223v-288q0 -53 -37.5 -90.5 t-90.5 -37.5h-640q-53 0 -90.5 37.5t-37.5 90.5v288q0 10 -4.5 21.5t-14 23.5t-18 22.5t-22.5 24t-21.5 20.5t-21.5 19t-17 14q-74 65 -129 100q-21 13 -62 33t-72 37t-63 40.5t-49.5 55t-17.5 69.5q0 125 67 206.5t189 81.5q68 0 128 -22v374q0 104 76 180t179 76 q105 0 181 -75.5t76 -180.5v-169q62 -4 119 -37q21 3 43 3q101 0 178 -60q139 1 219.5 -85t80.5 -227z" />
+<glyph unicode="&#xf0a7;" d="M1408 576q0 84 -32 183t-64 194t-32 167v32h-640v-32q0 -35 -12 -67.5t-37 -62.5t-46 -50t-54 -49q-9 -8 -14 -12q-81 -72 -145 -112q-22 -14 -68 -38q-3 -1 -22.5 -10.5t-36 -18.5t-35.5 -20t-30.5 -21.5t-11.5 -18.5q0 -71 30.5 -115.5t97.5 -44.5q43 0 84.5 15t68 33 t55 33t48.5 15v-576q0 -50 38.5 -89t89.5 -39q52 0 90 38t38 90v331q46 -35 103 -35q69 0 119 53q32 -18 69 -18t73.5 17.5t52.5 47.5q24 -4 56 -4q85 0 126 48.5t41 135.5zM1280 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 580 q0 -142 -77.5 -230t-217.5 -87l-5 1q-76 -61 -178 -61q-22 0 -43 3q-54 -30 -119 -37v-169q0 -105 -76 -180.5t-181 -75.5q-103 0 -179 76t-76 180v374q-54 -22 -128 -22q-121 0 -188.5 81.5t-67.5 206.5q0 38 17.5 69.5t49.5 55t63 40.5t72 37t62 33q55 35 129 100 q3 2 17 14t21.5 19t21.5 20.5t22.5 24t18 22.5t14 23.5t4.5 21.5v288q0 53 37.5 90.5t90.5 37.5h640q53 0 90.5 -37.5t37.5 -90.5v-288q0 -59 59 -223q69 -190 69 -317z" />
+<glyph unicode="&#xf0a8;" d="M1280 576v128q0 26 -19 45t-45 19h-502l189 189q19 19 19 45t-19 45l-91 91q-18 18 -45 18t-45 -18l-362 -362l-91 -91q-18 -18 -18 -45t18 -45l91 -91l362 -362q18 -18 45 -18t45 18l91 91q18 18 18 45t-18 45l-189 189h502q26 0 45 19t19 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf0a9;" d="M1285 640q0 27 -18 45l-91 91l-362 362q-18 18 -45 18t-45 -18l-91 -91q-18 -18 -18 -45t18 -45l189 -189h-502q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h502l-189 -189q-19 -19 -19 -45t19 -45l91 -91q18 -18 45 -18t45 18l362 362l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf0aa;" d="M1284 641q0 27 -18 45l-362 362l-91 91q-18 18 -45 18t-45 -18l-91 -91l-362 -362q-18 -18 -18 -45t18 -45l91 -91q18 -18 45 -18t45 18l189 189v-502q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v502l189 -189q19 -19 45 -19t45 19l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf0ab;" d="M1284 639q0 27 -18 45l-91 91q-18 18 -45 18t-45 -18l-189 -189v502q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-502l-189 189q-19 19 -45 19t-45 -19l-91 -91q-18 -18 -18 -45t18 -45l362 -362l91 -91q18 -18 45 -18t45 18l91 91l362 362q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf0ac;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1042 887q-2 -1 -9.5 -9.5t-13.5 -9.5q2 0 4.5 5t5 11t3.5 7q6 7 22 15q14 6 52 12q34 8 51 -11 q-2 2 9.5 13t14.5 12q3 2 15 4.5t15 7.5l2 22q-12 -1 -17.5 7t-6.5 21q0 -2 -6 -8q0 7 -4.5 8t-11.5 -1t-9 -1q-10 3 -15 7.5t-8 16.5t-4 15q-2 5 -9.5 10.5t-9.5 10.5q-1 2 -2.5 5.5t-3 6.5t-4 5.5t-5.5 2.5t-7 -5t-7.5 -10t-4.5 -5q-3 2 -6 1.5t-4.5 -1t-4.5 -3t-5 -3.5 q-3 -2 -8.5 -3t-8.5 -2q15 5 -1 11q-10 4 -16 3q9 4 7.5 12t-8.5 14h5q-1 4 -8.5 8.5t-17.5 8.5t-13 6q-8 5 -34 9.5t-33 0.5q-5 -6 -4.5 -10.5t4 -14t3.5 -12.5q1 -6 -5.5 -13t-6.5 -12q0 -7 14 -15.5t10 -21.5q-3 -8 -16 -16t-16 -12q-5 -8 -1.5 -18.5t10.5 -16.5 q2 -2 1.5 -4t-3.5 -4.5t-5.5 -4t-6.5 -3.5l-3 -2q-11 -5 -20.5 6t-13.5 26q-7 25 -16 30q-23 8 -29 -1q-5 13 -41 26q-25 9 -58 4q6 1 0 15q-7 15 -19 12q3 6 4 17.5t1 13.5q3 13 12 23q1 1 7 8.5t9.5 13.5t0.5 6q35 -4 50 11q5 5 11.5 17t10.5 17q9 6 14 5.5t14.5 -5.5 t14.5 -5q14 -1 15.5 11t-7.5 20q12 -1 3 17q-5 7 -8 9q-12 4 -27 -5q-8 -4 2 -8q-1 1 -9.5 -10.5t-16.5 -17.5t-16 5q-1 1 -5.5 13.5t-9.5 13.5q-8 0 -16 -15q3 8 -11 15t-24 8q19 12 -8 27q-7 4 -20.5 5t-19.5 -4q-5 -7 -5.5 -11.5t5 -8t10.5 -5.5t11.5 -4t8.5 -3 q14 -10 8 -14q-2 -1 -8.5 -3.5t-11.5 -4.5t-6 -4q-3 -4 0 -14t-2 -14q-5 5 -9 17.5t-7 16.5q7 -9 -25 -6l-10 1q-4 0 -16 -2t-20.5 -1t-13.5 8q-4 8 0 20q1 4 4 2q-4 3 -11 9.5t-10 8.5q-46 -15 -94 -41q6 -1 12 1q5 2 13 6.5t10 5.5q34 14 42 7l5 5q14 -16 20 -25 q-7 4 -30 1q-20 -6 -22 -12q7 -12 5 -18q-4 3 -11.5 10t-14.5 11t-15 5q-16 0 -22 -1q-146 -80 -235 -222q7 -7 12 -8q4 -1 5 -9t2.5 -11t11.5 3q9 -8 3 -19q1 1 44 -27q19 -17 21 -21q3 -11 -10 -18q-1 2 -9 9t-9 4q-3 -5 0.5 -18.5t10.5 -12.5q-7 0 -9.5 -16t-2.5 -35.5 t-1 -23.5l2 -1q-3 -12 5.5 -34.5t21.5 -19.5q-13 -3 20 -43q6 -8 8 -9q3 -2 12 -7.5t15 -10t10 -10.5q4 -5 10 -22.5t14 -23.5q-2 -6 9.5 -20t10.5 -23q-1 0 -2.5 -1t-2.5 -1q3 -7 15.5 -14t15.5 -13q1 -3 2 -10t3 -11t8 -2q2 20 -24 62q-15 25 -17 29q-3 5 -5.5 15.5 t-4.5 14.5q2 0 6 -1.5t8.5 -3.5t7.5 -4t2 -3q-3 -7 2 -17.5t12 -18.5t17 -19t12 -13q6 -6 14 -19.5t0 -13.5q9 0 20 -10t17 -20q5 -8 8 -26t5 -24q2 -7 8.5 -13.5t12.5 -9.5l16 -8t13 -7q5 -2 18.5 -10.5t21.5 -11.5q10 -4 16 -4t14.5 2.5t13.5 3.5q15 2 29 -15t21 -21 q36 -19 55 -11q-2 -1 0.5 -7.5t8 -15.5t9 -14.5t5.5 -8.5q5 -6 18 -15t18 -15q6 4 7 9q-3 -8 7 -20t18 -10q14 3 14 32q-31 -15 -49 18q0 1 -2.5 5.5t-4 8.5t-2.5 8.5t0 7.5t5 3q9 0 10 3.5t-2 12.5t-4 13q-1 8 -11 20t-12 15q-5 -9 -16 -8t-16 9q0 -1 -1.5 -5.5t-1.5 -6.5 q-13 0 -15 1q1 3 2.5 17.5t3.5 22.5q1 4 5.5 12t7.5 14.5t4 12.5t-4.5 9.5t-17.5 2.5q-19 -1 -26 -20q-1 -3 -3 -10.5t-5 -11.5t-9 -7q-7 -3 -24 -2t-24 5q-13 8 -22.5 29t-9.5 37q0 10 2.5 26.5t3 25t-5.5 24.5q3 2 9 9.5t10 10.5q2 1 4.5 1.5t4.5 0t4 1.5t3 6q-1 1 -4 3 q-3 3 -4 3q7 -3 28.5 1.5t27.5 -1.5q15 -11 22 2q0 1 -2.5 9.5t-0.5 13.5q5 -27 29 -9q3 -3 15.5 -5t17.5 -5q3 -2 7 -5.5t5.5 -4.5t5 0.5t8.5 6.5q10 -14 12 -24q11 -40 19 -44q7 -3 11 -2t4.5 9.5t0 14t-1.5 12.5l-1 8v18l-1 8q-15 3 -18.5 12t1.5 18.5t15 18.5q1 1 8 3.5 t15.5 6.5t12.5 8q21 19 15 35q7 0 11 9q-1 0 -5 3t-7.5 5t-4.5 2q9 5 2 16q5 3 7.5 11t7.5 10q9 -12 21 -2q7 8 1 16q5 7 20.5 10.5t18.5 9.5q7 -2 8 2t1 12t3 12q4 5 15 9t13 5l17 11q3 4 0 4q18 -2 31 11q10 11 -6 20q3 6 -3 9.5t-15 5.5q3 1 11.5 0.5t10.5 1.5 q15 10 -7 16q-17 5 -43 -12zM879 10q206 36 351 189q-3 3 -12.5 4.5t-12.5 3.5q-18 7 -24 8q1 7 -2.5 13t-8 9t-12.5 8t-11 7q-2 2 -7 6t-7 5.5t-7.5 4.5t-8.5 2t-10 -1l-3 -1q-3 -1 -5.5 -2.5t-5.5 -3t-4 -3t0 -2.5q-21 17 -36 22q-5 1 -11 5.5t-10.5 7t-10 1.5t-11.5 -7 q-5 -5 -6 -15t-2 -13q-7 5 0 17.5t2 18.5q-3 6 -10.5 4.5t-12 -4.5t-11.5 -8.5t-9 -6.5t-8.5 -5.5t-8.5 -7.5q-3 -4 -6 -12t-5 -11q-2 4 -11.5 6.5t-9.5 5.5q2 -10 4 -35t5 -38q7 -31 -12 -48q-27 -25 -29 -40q-4 -22 12 -26q0 -7 -8 -20.5t-7 -21.5q0 -6 2 -16z" />
+<glyph unicode="&#xf0ad;" horiz-adv-x="1664" d="M384 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1028 484l-682 -682q-37 -37 -90 -37q-52 0 -91 37l-106 108q-38 36 -38 90q0 53 38 91l681 681q39 -98 114.5 -173.5t173.5 -114.5zM1662 919q0 -39 -23 -106q-47 -134 -164.5 -217.5 t-258.5 -83.5q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q58 0 121.5 -16.5t107.5 -46.5q16 -11 16 -28t-16 -28l-293 -169v-224l193 -107q5 3 79 48.5t135.5 81t70.5 35.5q15 0 23.5 -10t8.5 -25z" />
+<glyph unicode="&#xf0ae;" horiz-adv-x="1792" d="M1024 128h640v128h-640v-128zM640 640h1024v128h-1024v-128zM1280 1152h384v128h-384v-128zM1792 320v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 832v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19 t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0b0;" horiz-adv-x="1408" d="M1403 1241q17 -41 -14 -70l-493 -493v-742q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-256 256q-19 19 -19 45v486l-493 493q-31 29 -14 70q17 39 59 39h1280q42 0 59 -39z" />
+<glyph unicode="&#xf0b1;" horiz-adv-x="1792" d="M640 1280h512v128h-512v-128zM1792 640v-480q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v480h672v-160q0 -26 19 -45t45 -19h320q26 0 45 19t19 45v160h672zM1024 640v-128h-256v128h256zM1792 1120v-384h-1792v384q0 66 47 113t113 47h352v160q0 40 28 68 t68 28h576q40 0 68 -28t28 -68v-160h352q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf0b2;" d="M1283 995l-355 -355l355 -355l144 144q29 31 70 14q39 -17 39 -59v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l144 144l-355 355l-355 -355l144 -144q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l144 -144 l355 355l-355 355l-144 -144q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v448q0 26 19 45t45 19h448q42 0 59 -40q17 -39 -14 -69l-144 -144l355 -355l355 355l-144 144q-31 30 -14 69q17 40 59 40h448q26 0 45 -19t19 -45v-448q0 -42 -39 -59q-13 -5 -25 -5q-26 0 -45 19z " />
+<glyph unicode="&#xf0c0;" horiz-adv-x="1920" d="M593 640q-162 -5 -265 -128h-134q-82 0 -138 40.5t-56 118.5q0 353 124 353q6 0 43.5 -21t97.5 -42.5t119 -21.5q67 0 133 23q-5 -37 -5 -66q0 -139 81 -256zM1664 3q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5 t43 97.5t62 81t85.5 53.5t111.5 20q10 0 43 -21.5t73 -48t107 -48t135 -21.5t135 21.5t107 48t73 48t43 21.5q61 0 111.5 -20t85.5 -53.5t62 -81t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM640 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75 t75 -181zM1344 896q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5zM1920 671q0 -78 -56 -118.5t-138 -40.5h-134q-103 123 -265 128q81 117 81 256q0 29 -5 66q66 -23 133 -23q59 0 119 21.5t97.5 42.5 t43.5 21q124 0 124 -353zM1792 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181z" />
+<glyph unicode="&#xf0c1;" horiz-adv-x="1664" d="M1456 320q0 40 -28 68l-208 208q-28 28 -68 28q-42 0 -72 -32q3 -3 19 -18.5t21.5 -21.5t15 -19t13 -25.5t3.5 -27.5q0 -40 -28 -68t-68 -28q-15 0 -27.5 3.5t-25.5 13t-19 15t-21.5 21.5t-18.5 19q-33 -31 -33 -73q0 -40 28 -68l206 -207q27 -27 68 -27q40 0 68 26 l147 146q28 28 28 67zM753 1025q0 40 -28 68l-206 207q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l208 -208q27 -27 68 -27q42 0 72 31q-3 3 -19 18.5t-21.5 21.5t-15 19t-13 25.5t-3.5 27.5q0 40 28 68t68 28q15 0 27.5 -3.5t25.5 -13t19 -15 t21.5 -21.5t18.5 -19q33 31 33 73zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-206 207q-83 83 -83 203q0 123 88 209l-88 88q-86 -88 -208 -88q-120 0 -204 84l-208 208q-84 84 -84 204t85 203l147 146q83 83 203 83q121 0 204 -85l206 -207 q83 -83 83 -203q0 -123 -88 -209l88 -88q86 88 208 88q120 0 204 -84l208 -208q84 -84 84 -204z" />
+<glyph unicode="&#xf0c2;" horiz-adv-x="1920" d="M1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088q-185 0 -316.5 131.5t-131.5 316.5q0 132 71 241.5t187 163.5q-2 28 -2 43q0 212 150 362t362 150q158 0 286.5 -88t187.5 -230q70 62 166 62q106 0 181 -75t75 -181q0 -75 -41 -138q129 -30 213 -134.5t84 -239.5z " />
+<glyph unicode="&#xf0c3;" horiz-adv-x="1664" d="M1527 88q56 -89 21.5 -152.5t-140.5 -63.5h-1152q-106 0 -140.5 63.5t21.5 152.5l503 793v399h-64q-26 0 -45 19t-19 45t19 45t45 19h512q26 0 45 -19t19 -45t-19 -45t-45 -19h-64v-399zM748 813l-272 -429h712l-272 429l-20 31v37v399h-128v-399v-37z" />
+<glyph unicode="&#xf0c4;" horiz-adv-x="1792" d="M960 640q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1260 576l507 -398q28 -20 25 -56q-5 -35 -35 -51l-128 -64q-13 -7 -29 -7q-17 0 -31 8l-690 387l-110 -66q-8 -4 -12 -5q14 -49 10 -97q-7 -77 -56 -147.5t-132 -123.5q-132 -84 -277 -84 q-136 0 -222 78q-90 84 -79 207q7 76 56 147t131 124q132 84 278 84q83 0 151 -31q9 13 22 22l122 73l-122 73q-13 9 -22 22q-68 -31 -151 -31q-146 0 -278 84q-82 53 -131 124t-56 147q-5 59 15.5 113t63.5 93q85 79 222 79q145 0 277 -84q83 -52 132 -123t56 -148 q4 -48 -10 -97q4 -1 12 -5l110 -66l690 387q14 8 31 8q16 0 29 -7l128 -64q30 -16 35 -51q3 -36 -25 -56zM579 836q46 42 21 108t-106 117q-92 59 -192 59q-74 0 -113 -36q-46 -42 -21 -108t106 -117q92 -59 192 -59q74 0 113 36zM494 91q81 51 106 117t-21 108 q-39 36 -113 36q-100 0 -192 -59q-81 -51 -106 -117t21 -108q39 -36 113 -36q100 0 192 59zM672 704l96 -58v11q0 36 33 56l14 8l-79 47l-26 -26q-3 -3 -10 -11t-12 -12q-2 -2 -4 -3.5t-3 -2.5zM896 480l96 -32l736 576l-128 64l-768 -431v-113l-160 -96l9 -8q2 -2 7 -6 q4 -4 11 -12t11 -12l26 -26zM1600 64l128 64l-520 408l-177 -138q-2 -3 -13 -7z" />
+<glyph unicode="&#xf0c5;" horiz-adv-x="1792" d="M1696 1152q40 0 68 -28t28 -68v-1216q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v288h-544q-40 0 -68 28t-28 68v672q0 40 20 88t48 76l408 408q28 28 76 48t88 20h416q40 0 68 -28t28 -68v-328q68 40 128 40h416zM1152 939l-299 -299h299v299zM512 1323l-299 -299 h299v299zM708 676l316 316v416h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h512v256q0 40 20 88t48 76zM1664 -128v1152h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h896z" />
+<glyph unicode="&#xf0c6;" horiz-adv-x="1408" d="M1404 151q0 -117 -79 -196t-196 -79q-135 0 -235 100l-777 776q-113 115 -113 271q0 159 110 270t269 111q158 0 273 -113l605 -606q10 -10 10 -22q0 -16 -30.5 -46.5t-46.5 -30.5q-13 0 -23 10l-606 607q-79 77 -181 77q-106 0 -179 -75t-73 -181q0 -105 76 -181 l776 -777q63 -63 145 -63q64 0 106 42t42 106q0 82 -63 145l-581 581q-26 24 -60 24q-29 0 -48 -19t-19 -48q0 -32 25 -59l410 -410q10 -10 10 -22q0 -16 -31 -47t-47 -31q-12 0 -22 10l-410 410q-63 61 -63 149q0 82 57 139t139 57q88 0 149 -63l581 -581q100 -98 100 -235 z" />
+<glyph unicode="&#xf0c7;" d="M384 0h768v384h-768v-384zM1280 0h128v896q0 14 -10 38.5t-20 34.5l-281 281q-10 10 -34 20t-39 10v-416q0 -40 -28 -68t-68 -28h-576q-40 0 -68 28t-28 68v416h-128v-1280h128v416q0 40 28 68t68 28h832q40 0 68 -28t28 -68v-416zM896 928v320q0 13 -9.5 22.5t-22.5 9.5 h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5zM1536 896v-928q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h928q40 0 88 -20t76 -48l280 -280q28 -28 48 -76t20 -88z" />
+<glyph unicode="&#xf0c8;" d="M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf0c9;" d="M1536 192v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 704v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 1216v-128q0 -26 -19 -45 t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0ca;" horiz-adv-x="1792" d="M384 128q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 640q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1152q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z M1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
+<glyph unicode="&#xf0cb;" horiz-adv-x="1792" d="M381 -84q0 -80 -54.5 -126t-135.5 -46q-106 0 -172 66l57 88q49 -45 106 -45q29 0 50.5 14.5t21.5 42.5q0 64 -105 56l-26 56q8 10 32.5 43.5t42.5 54t37 38.5v1q-16 0 -48.5 -1t-48.5 -1v-53h-106v152h333v-88l-95 -115q51 -12 81 -49t30 -88zM383 543v-159h-362 q-6 36 -6 54q0 51 23.5 93t56.5 68t66 47.5t56.5 43.5t23.5 45q0 25 -14.5 38.5t-39.5 13.5q-46 0 -81 -58l-85 59q24 51 71.5 79.5t105.5 28.5q73 0 123 -41.5t50 -112.5q0 -50 -34 -91.5t-75 -64.5t-75.5 -50.5t-35.5 -52.5h127v60h105zM1792 224v-192q0 -13 -9.5 -22.5 t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1123v-99h-335v99h107q0 41 0.5 122t0.5 121v12h-2q-8 -17 -50 -54l-71 76l136 127h106v-404h108zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5 t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
+<glyph unicode="&#xf0cc;" horiz-adv-x="1792" d="M1760 640q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1728q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h1728zM483 704q-28 35 -51 80q-48 97 -48 188q0 181 134 309q133 127 393 127q50 0 167 -19q66 -12 177 -48q10 -38 21 -118q14 -123 14 -183q0 -18 -5 -45l-12 -3l-84 6 l-14 2q-50 149 -103 205q-88 91 -210 91q-114 0 -182 -59q-67 -58 -67 -146q0 -73 66 -140t279 -129q69 -20 173 -66q58 -28 95 -52h-743zM990 448h411q7 -39 7 -92q0 -111 -41 -212q-23 -55 -71 -104q-37 -35 -109 -81q-80 -48 -153 -66q-80 -21 -203 -21q-114 0 -195 23 l-140 40q-57 16 -72 28q-8 8 -8 22v13q0 108 -2 156q-1 30 0 68l2 37v44l102 2q15 -34 30 -71t22.5 -56t12.5 -27q35 -57 80 -94q43 -36 105 -57q59 -22 132 -22q64 0 139 27q77 26 122 86q47 61 47 129q0 84 -81 157q-34 29 -137 71z" />
+<glyph unicode="&#xf0cd;" d="M48 1313q-37 2 -45 4l-3 88q13 1 40 1q60 0 112 -4q132 -7 166 -7q86 0 168 3q116 4 146 5q56 0 86 2l-1 -14l2 -64v-9q-60 -9 -124 -9q-60 0 -79 -25q-13 -14 -13 -132q0 -13 0.5 -32.5t0.5 -25.5l1 -229l14 -280q6 -124 51 -202q35 -59 96 -92q88 -47 177 -47 q104 0 191 28q56 18 99 51q48 36 65 64q36 56 53 114q21 73 21 229q0 79 -3.5 128t-11 122.5t-13.5 159.5l-4 59q-5 67 -24 88q-34 35 -77 34l-100 -2l-14 3l2 86h84l205 -10q76 -3 196 10l18 -2q6 -38 6 -51q0 -7 -4 -31q-45 -12 -84 -13q-73 -11 -79 -17q-15 -15 -15 -41 q0 -7 1.5 -27t1.5 -31q8 -19 22 -396q6 -195 -15 -304q-15 -76 -41 -122q-38 -65 -112 -123q-75 -57 -182 -89q-109 -33 -255 -33q-167 0 -284 46q-119 47 -179 122q-61 76 -83 195q-16 80 -16 237v333q0 188 -17 213q-25 36 -147 39zM1536 -96v64q0 14 -9 23t-23 9h-1472 q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h1472q14 0 23 9t9 23z" />
+<glyph unicode="&#xf0ce;" horiz-adv-x="1664" d="M512 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23 v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 160v192 q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192 q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1664 1248v-1088q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1344q66 0 113 -47t47 -113 z" />
+<glyph unicode="&#xf0d0;" horiz-adv-x="1664" d="M1190 955l293 293l-107 107l-293 -293zM1637 1248q0 -27 -18 -45l-1286 -1286q-18 -18 -45 -18t-45 18l-198 198q-18 18 -18 45t18 45l1286 1286q18 18 45 18t45 -18l198 -198q18 -18 18 -45zM286 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM636 1276 l196 -60l-196 -60l-60 -196l-60 196l-196 60l196 60l60 196zM1566 798l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM926 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98z" />
+<glyph unicode="&#xf0d1;" horiz-adv-x="1792" d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5 t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38 t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0d2;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134 q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33 q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf0d3;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5 t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5 t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" />
+<glyph unicode="&#xf0d4;" d="M917 631q0 26 -6 64h-362v-132h217q-3 -24 -16.5 -50t-37.5 -53t-66.5 -44.5t-96.5 -17.5q-99 0 -169 71t-70 171t70 171t169 71q92 0 153 -59l104 101q-108 100 -257 100q-160 0 -272 -112.5t-112 -271.5t112 -271.5t272 -112.5q165 0 266.5 105t101.5 270zM1262 585 h109v110h-109v110h-110v-110h-110v-110h110v-110h110v110zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf0d5;" horiz-adv-x="2304" d="M1437 623q0 -208 -87 -370.5t-248 -254t-369 -91.5q-149 0 -285 58t-234 156t-156 234t-58 285t58 285t156 234t234 156t285 58q286 0 491 -192l-199 -191q-117 113 -292 113q-123 0 -227.5 -62t-165.5 -168.5t-61 -232.5t61 -232.5t165.5 -168.5t227.5 -62 q83 0 152.5 23t114.5 57.5t78.5 78.5t49 83t21.5 74h-416v252h692q12 -63 12 -122zM2304 745v-210h-209v-209h-210v209h-209v210h209v209h210v-209h209z" />
+<glyph unicode="&#xf0d6;" horiz-adv-x="1920" d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384 v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0d7;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0d8;" horiz-adv-x="1024" d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
+<glyph unicode="&#xf0d9;" horiz-adv-x="640" d="M640 1088v-896q0 -26 -19 -45t-45 -19t-45 19l-448 448q-19 19 -19 45t19 45l448 448q19 19 45 19t45 -19t19 -45z" />
+<glyph unicode="&#xf0da;" horiz-adv-x="640" d="M576 640q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19t-19 45v896q0 26 19 45t45 19t45 -19l448 -448q19 -19 19 -45z" />
+<glyph unicode="&#xf0db;" horiz-adv-x="1664" d="M160 0h608v1152h-640v-1120q0 -13 9.5 -22.5t22.5 -9.5zM1536 32v1120h-640v-1152h608q13 0 22.5 9.5t9.5 22.5zM1664 1248v-1216q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1344q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf0dc;" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45zM1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
+<glyph unicode="&#xf0dd;" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0de;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
+<glyph unicode="&#xf0e0;" horiz-adv-x="1792" d="M1792 826v-794q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v794q44 -49 101 -87q362 -246 497 -345q57 -42 92.5 -65.5t94.5 -48t110 -24.5h1h1q51 0 110 24.5t94.5 48t92.5 65.5q170 123 498 345q57 39 100 87zM1792 1120q0 -79 -49 -151t-122 -123 q-376 -261 -468 -325q-10 -7 -42.5 -30.5t-54 -38t-52 -32.5t-57.5 -27t-50 -9h-1h-1q-23 0 -50 9t-57.5 27t-52 32.5t-54 38t-42.5 30.5q-91 64 -262 182.5t-205 142.5q-62 42 -117 115.5t-55 136.5q0 78 41.5 130t118.5 52h1472q65 0 112.5 -47t47.5 -113z" />
+<glyph unicode="&#xf0e1;" d="M349 911v-991h-330v991h330zM370 1217q1 -73 -50.5 -122t-135.5 -49h-2q-82 0 -132 49t-50 122q0 74 51.5 122.5t134.5 48.5t133 -48.5t51 -122.5zM1536 488v-568h-329v530q0 105 -40.5 164.5t-126.5 59.5q-63 0 -105.5 -34.5t-63.5 -85.5q-11 -30 -11 -81v-553h-329 q2 399 2 647t-1 296l-1 48h329v-144h-2q20 32 41 56t56.5 52t87 43.5t114.5 15.5q171 0 275 -113.5t104 -332.5z" />
+<glyph unicode="&#xf0e2;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298z" />
+<glyph unicode="&#xf0e3;" horiz-adv-x="1792" d="M1771 0q0 -53 -37 -90l-107 -108q-39 -37 -91 -37q-53 0 -90 37l-363 364q-38 36 -38 90q0 53 43 96l-256 256l-126 -126q-14 -14 -34 -14t-34 14q2 -2 12.5 -12t12.5 -13t10 -11.5t10 -13.5t6 -13.5t5.5 -16.5t1.5 -18q0 -38 -28 -68q-3 -3 -16.5 -18t-19 -20.5 t-18.5 -16.5t-22 -15.5t-22 -9t-26 -4.5q-40 0 -68 28l-408 408q-28 28 -28 68q0 13 4.5 26t9 22t15.5 22t16.5 18.5t20.5 19t18 16.5q30 28 68 28q10 0 18 -1.5t16.5 -5.5t13.5 -6t13.5 -10t11.5 -10t13 -12.5t12 -12.5q-14 14 -14 34t14 34l348 348q14 14 34 14t34 -14 q-2 2 -12.5 12t-12.5 13t-10 11.5t-10 13.5t-6 13.5t-5.5 16.5t-1.5 18q0 38 28 68q3 3 16.5 18t19 20.5t18.5 16.5t22 15.5t22 9t26 4.5q40 0 68 -28l408 -408q28 -28 28 -68q0 -13 -4.5 -26t-9 -22t-15.5 -22t-16.5 -18.5t-20.5 -19t-18 -16.5q-30 -28 -68 -28 q-10 0 -18 1.5t-16.5 5.5t-13.5 6t-13.5 10t-11.5 10t-13 12.5t-12 12.5q14 -14 14 -34t-14 -34l-126 -126l256 -256q43 43 96 43q52 0 91 -37l363 -363q37 -39 37 -91z" />
+<glyph unicode="&#xf0e4;" horiz-adv-x="1792" d="M384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM576 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1004 351l101 382q6 26 -7.5 48.5t-38.5 29.5 t-48 -6.5t-30 -39.5l-101 -382q-60 -5 -107 -43.5t-63 -98.5q-20 -77 20 -146t117 -89t146 20t89 117q16 60 -6 117t-72 91zM1664 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 1024q0 53 -37.5 90.5 t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1472 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 384q0 -261 -141 -483q-19 -29 -54 -29h-1402q-35 0 -54 29 q-141 221 -141 483q0 182 71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf0e5;" horiz-adv-x="1792" d="M896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640 q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 174 120 321.5 t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
+<glyph unicode="&#xf0e6;" horiz-adv-x="1792" d="M704 1152q-153 0 -286 -52t-211.5 -141t-78.5 -191q0 -82 53 -158t149 -132l97 -56l-35 -84q34 20 62 39l44 31l53 -10q78 -14 153 -14q153 0 286 52t211.5 141t78.5 191t-78.5 191t-211.5 141t-286 52zM704 1280q191 0 353.5 -68.5t256.5 -186.5t94 -257t-94 -257 t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224q0 139 94 257t256.5 186.5 t353.5 68.5zM1526 111q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129 q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230q0 -120 -71 -224.5t-195 -176.5z" />
+<glyph unicode="&#xf0e7;" horiz-adv-x="896" d="M885 970q18 -20 7 -44l-540 -1157q-13 -25 -42 -25q-4 0 -14 2q-17 5 -25.5 19t-4.5 30l197 808l-406 -101q-4 -1 -12 -1q-18 0 -31 11q-18 15 -13 39l201 825q4 14 16 23t28 9h328q19 0 32 -12.5t13 -29.5q0 -8 -5 -18l-171 -463l396 98q8 2 12 2q19 0 34 -15z" />
+<glyph unicode="&#xf0e8;" horiz-adv-x="1792" d="M1792 288v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192q0 52 38 90t90 38h512v192h-96q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h320q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-96v-192h512q52 0 90 -38t38 -90v-192h96q40 0 68 -28t28 -68 z" />
+<glyph unicode="&#xf0e9;" horiz-adv-x="1664" d="M896 708v-580q0 -104 -76 -180t-180 -76t-180 76t-76 180q0 26 19 45t45 19t45 -19t19 -45q0 -50 39 -89t89 -39t89 39t39 89v580q33 11 64 11t64 -11zM1664 681q0 -13 -9.5 -22.5t-22.5 -9.5q-11 0 -23 10q-49 46 -93 69t-102 23q-68 0 -128 -37t-103 -97 q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -28 -17q-18 0 -29 17q-4 6 -14.5 24t-17.5 28q-43 60 -102.5 97t-127.5 37t-127.5 -37t-102.5 -97q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -29 -17q-17 0 -28 17q-4 6 -14.5 24t-17.5 28q-43 60 -103 97t-128 37q-58 0 -102 -23t-93 -69 q-12 -10 -23 -10q-13 0 -22.5 9.5t-9.5 22.5q0 5 1 7q45 183 172.5 319.5t298 204.5t360.5 68q140 0 274.5 -40t246.5 -113.5t194.5 -187t115.5 -251.5q1 -2 1 -7zM896 1408v-98q-42 2 -64 2t-64 -2v98q0 26 19 45t45 19t45 -19t19 -45z" />
+<glyph unicode="&#xf0ea;" horiz-adv-x="1792" d="M768 -128h896v640h-416q-40 0 -68 28t-28 68v416h-384v-1152zM1024 1312v64q0 13 -9.5 22.5t-22.5 9.5h-704q-13 0 -22.5 -9.5t-9.5 -22.5v-64q0 -13 9.5 -22.5t22.5 -9.5h704q13 0 22.5 9.5t9.5 22.5zM1280 640h299l-299 299v-299zM1792 512v-672q0 -40 -28 -68t-68 -28 h-960q-40 0 -68 28t-28 68v160h-544q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1088q40 0 68 -28t28 -68v-328q21 -13 36 -28l408 -408q28 -28 48 -76t20 -88z" />
+<glyph unicode="&#xf0eb;" horiz-adv-x="1024" d="M736 960q0 -13 -9.5 -22.5t-22.5 -9.5t-22.5 9.5t-9.5 22.5q0 46 -54 71t-106 25q-13 0 -22.5 9.5t-9.5 22.5t9.5 22.5t22.5 9.5q50 0 99.5 -16t87 -54t37.5 -90zM896 960q0 72 -34.5 134t-90 101.5t-123 62t-136.5 22.5t-136.5 -22.5t-123 -62t-90 -101.5t-34.5 -134 q0 -101 68 -180q10 -11 30.5 -33t30.5 -33q128 -153 141 -298h228q13 145 141 298q10 11 30.5 33t30.5 33q68 79 68 180zM1024 960q0 -155 -103 -268q-45 -49 -74.5 -87t-59.5 -95.5t-34 -107.5q47 -28 47 -82q0 -37 -25 -64q25 -27 25 -64q0 -52 -45 -81q13 -23 13 -47 q0 -46 -31.5 -71t-77.5 -25q-20 -44 -60 -70t-87 -26t-87 26t-60 70q-46 0 -77.5 25t-31.5 71q0 24 13 47q-45 29 -45 81q0 37 25 64q-25 27 -25 64q0 54 47 82q-4 50 -34 107.5t-59.5 95.5t-74.5 87q-103 113 -103 268q0 99 44.5 184.5t117 142t164 89t186.5 32.5 t186.5 -32.5t164 -89t117 -142t44.5 -184.5z" />
+<glyph unicode="&#xf0ec;" horiz-adv-x="1792" d="M1792 352v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5q-12 0 -24 10l-319 320q-9 9 -9 22q0 14 9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h1376q13 0 22.5 -9.5t9.5 -22.5zM1792 896q0 -14 -9 -23l-320 -320q-9 -9 -23 -9 q-13 0 -22.5 9.5t-9.5 22.5v192h-1376q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1376v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
+<glyph unicode="&#xf0ed;" horiz-adv-x="1920" d="M1280 608q0 14 -9 23t-23 9h-224v352q0 13 -9.5 22.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-352h-224q-13 0 -22.5 -9.5t-9.5 -22.5q0 -14 9 -23l352 -352q9 -9 23 -9t23 9l351 351q10 12 10 24zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
+<glyph unicode="&#xf0ee;" horiz-adv-x="1920" d="M1280 672q0 14 -9 23l-352 352q-9 9 -23 9t-23 -9l-351 -351q-10 -12 -10 -24q0 -14 9 -23t23 -9h224v-352q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5v352h224q13 0 22.5 9.5t9.5 22.5zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
+<glyph unicode="&#xf0f0;" horiz-adv-x="1408" d="M384 192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 68 5.5 131t24 138t47.5 132.5t81 103t120 60.5q-22 -52 -22 -120v-203q-58 -20 -93 -70t-35 -111q0 -80 56 -136t136 -56 t136 56t56 136q0 61 -35.5 111t-92.5 70v203q0 62 25 93q132 -104 295 -104t295 104q25 -31 25 -93v-64q-106 0 -181 -75t-75 -181v-89q-32 -29 -32 -71q0 -40 28 -68t68 -28t68 28t28 68q0 42 -32 71v89q0 52 38 90t90 38t90 -38t38 -90v-89q-32 -29 -32 -71q0 -40 28 -68 t68 -28t68 28t28 68q0 42 -32 71v89q0 68 -34.5 127.5t-93.5 93.5q0 10 0.5 42.5t0 48t-2.5 41.5t-7 47t-13 40q68 -15 120 -60.5t81 -103t47.5 -132.5t24 -138t5.5 -131zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
+<glyph unicode="&#xf0f1;" horiz-adv-x="1408" d="M1280 832q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 832q0 -62 -35.5 -111t-92.5 -70v-395q0 -159 -131.5 -271.5t-316.5 -112.5t-316.5 112.5t-131.5 271.5v132q-164 20 -274 128t-110 252v512q0 26 19 45t45 19q6 0 16 -2q17 30 47 48 t65 18q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5q-33 0 -64 18v-402q0 -106 94 -181t226 -75t226 75t94 181v402q-31 -18 -64 -18q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5q35 0 65 -18t47 -48q10 2 16 2q26 0 45 -19t19 -45v-512q0 -144 -110 -252 t-274 -128v-132q0 -106 94 -181t226 -75t226 75t94 181v395q-57 21 -92.5 70t-35.5 111q0 80 56 136t136 56t136 -56t56 -136z" />
+<glyph unicode="&#xf0f2;" horiz-adv-x="1792" d="M640 1152h512v128h-512v-128zM288 1152v-1280h-64q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h64zM1408 1152v-1280h-1024v1280h128v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h128zM1792 928v-832q0 -92 -66 -158t-158 -66h-64v1280h64q92 0 158 -66 t66 -158z" />
+<glyph unicode="&#xf0f3;" horiz-adv-x="1792" d="M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5 t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z" />
+<glyph unicode="&#xf0f4;" horiz-adv-x="1920" d="M1664 896q0 80 -56 136t-136 56h-64v-384h64q80 0 136 56t56 136zM0 128h1792q0 -106 -75 -181t-181 -75h-1280q-106 0 -181 75t-75 181zM1856 896q0 -159 -112.5 -271.5t-271.5 -112.5h-64v-32q0 -92 -66 -158t-158 -66h-704q-92 0 -158 66t-66 158v736q0 26 19 45 t45 19h1152q159 0 271.5 -112.5t112.5 -271.5z" />
+<glyph unicode="&#xf0f5;" horiz-adv-x="1408" d="M640 1472v-640q0 -61 -35.5 -111t-92.5 -70v-779q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v779q-57 20 -92.5 70t-35.5 111v640q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45 t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45zM1408 1472v-1600q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v512h-224q-13 0 -22.5 9.5t-9.5 22.5v800q0 132 94 226t226 94h256q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0f6;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M384 736q0 14 9 23t23 9h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64zM1120 512q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704zM1120 256q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704 q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704z" />
+<glyph unicode="&#xf0f7;" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1536h-1152v-1536h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM1408 1472v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0f8;" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1152h-256v-32q0 -40 -28 -68t-68 -28h-448q-40 0 -68 28t-28 68v32h-256v-1152h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM896 1056v320q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-96h-128v96q0 13 -9.5 22.5 t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5v96h128v-96q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1408 1088v-1280q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1280q0 26 19 45t45 19h320 v288q0 40 28 68t68 28h448q40 0 68 -28t28 -68v-288h320q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0f9;" horiz-adv-x="1920" d="M640 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM256 640h384v256h-158q-14 -2 -22 -9l-195 -195q-7 -12 -9 -22v-30zM1536 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1664 800v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM1920 1344v-1152 q0 -26 -19 -45t-45 -19h-192q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-128q-26 0 -45 19t-19 45t19 45t45 19v416q0 26 13 58t32 51l198 198q19 19 51 32t58 13h160v320q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf0fa;" horiz-adv-x="1792" d="M1280 416v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM640 1152h512v128h-512v-128zM256 1152v-1280h-32 q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h32zM1440 1152v-1280h-1088v1280h160v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h160zM1792 928v-832q0 -92 -66 -158t-158 -66h-32v1280h32q92 0 158 -66t66 -158z" />
+<glyph unicode="&#xf0fb;" horiz-adv-x="1920" d="M1920 576q-1 -32 -288 -96l-352 -32l-224 -64h-64l-293 -352h69q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-96h-160h-64v32h64v416h-160l-192 -224h-96l-32 32v192h32v32h128v8l-192 24v128l192 24v8h-128v32h-32v192l32 32h96l192 -224h160v416h-64v32h64h160h96 q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-69l293 -352h64l224 -64l352 -32q261 -58 287 -93z" />
+<glyph unicode="&#xf0fc;" horiz-adv-x="1664" d="M640 640v384h-256v-256q0 -53 37.5 -90.5t90.5 -37.5h128zM1664 192v-192h-1152v192l128 192h-128q-159 0 -271.5 112.5t-112.5 271.5v320l-64 64l32 128h480l32 128h960l32 -192l-64 -32v-800z" />
+<glyph unicode="&#xf0fd;" d="M1280 192v896q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-512v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-896q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h512v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf0fe;" d="M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf100;" horiz-adv-x="1024" d="M627 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23zM1011 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23z" />
+<glyph unicode="&#xf101;" horiz-adv-x="1024" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM979 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23 l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
+<glyph unicode="&#xf102;" horiz-adv-x="1152" d="M1075 224q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM1075 608q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393 q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
+<glyph unicode="&#xf103;" horiz-adv-x="1152" d="M1075 672q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23zM1075 1056q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
+<glyph unicode="&#xf104;" horiz-adv-x="640" d="M627 992q0 -13 -10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
+<glyph unicode="&#xf105;" horiz-adv-x="640" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
+<glyph unicode="&#xf106;" horiz-adv-x="1152" d="M1075 352q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
+<glyph unicode="&#xf107;" horiz-adv-x="1152" d="M1075 800q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
+<glyph unicode="&#xf108;" horiz-adv-x="1920" d="M1792 544v832q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1376v-1088q0 -66 -47 -113t-113 -47h-544q0 -37 16 -77.5t32 -71t16 -43.5q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19 t-19 45q0 14 16 44t32 70t16 78h-544q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf109;" horiz-adv-x="1920" d="M416 256q-66 0 -113 47t-47 113v704q0 66 47 113t113 47h1088q66 0 113 -47t47 -113v-704q0 -66 -47 -113t-113 -47h-1088zM384 1120v-704q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5v704q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5z M1760 192h160v-96q0 -40 -47 -68t-113 -28h-1600q-66 0 -113 28t-47 68v96h160h1600zM1040 96q16 0 16 16t-16 16h-160q-16 0 -16 -16t16 -16h160z" />
+<glyph unicode="&#xf10a;" horiz-adv-x="1152" d="M640 128q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1024 288v960q0 13 -9.5 22.5t-22.5 9.5h-832q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h832q13 0 22.5 9.5t9.5 22.5zM1152 1248v-1088q0 -66 -47 -113t-113 -47h-832 q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h832q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf10b;" horiz-adv-x="768" d="M464 128q0 33 -23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5t56.5 23.5t23.5 56.5zM672 288v704q0 13 -9.5 22.5t-22.5 9.5h-512q-13 0 -22.5 -9.5t-9.5 -22.5v-704q0 -13 9.5 -22.5t22.5 -9.5h512q13 0 22.5 9.5t9.5 22.5zM480 1136 q0 16 -16 16h-160q-16 0 -16 -16t16 -16h160q16 0 16 16zM768 1152v-1024q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v1024q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf10c;" d="M768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103 t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf10d;" horiz-adv-x="1664" d="M768 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z M1664 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z" />
+<glyph unicode="&#xf10e;" horiz-adv-x="1664" d="M768 1216v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136zM1664 1216 v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136z" />
+<glyph unicode="&#xf110;" horiz-adv-x="1792" d="M526 142q0 -53 -37.5 -90.5t-90.5 -37.5q-52 0 -90 38t-38 90q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 -64q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -53 -37.5 -90.5t-90.5 -37.5 t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1522 142q0 -52 -38 -90t-90 -38q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM558 1138q0 -66 -47 -113t-113 -47t-113 47t-47 113t47 113t113 47t113 -47t47 -113z M1728 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1088 1344q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1618 1138q0 -93 -66 -158.5t-158 -65.5q-93 0 -158.5 65.5t-65.5 158.5 q0 92 65.5 158t158.5 66q92 0 158 -66t66 -158z" />
+<glyph unicode="&#xf111;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf112;" horiz-adv-x="1792" d="M1792 416q0 -166 -127 -451q-3 -7 -10.5 -24t-13.5 -30t-13 -22q-12 -17 -28 -17q-15 0 -23.5 10t-8.5 25q0 9 2.5 26.5t2.5 23.5q5 68 5 123q0 101 -17.5 181t-48.5 138.5t-80 101t-105.5 69.5t-133 42.5t-154 21.5t-175.5 6h-224v-256q0 -26 -19 -45t-45 -19t-45 19 l-512 512q-19 19 -19 45t19 45l512 512q19 19 45 19t45 -19t19 -45v-256h224q713 0 875 -403q53 -134 53 -333z" />
+<glyph unicode="&#xf113;" horiz-adv-x="1664" d="M640 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1280 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1440 320 q0 120 -69 204t-187 84q-41 0 -195 -21q-71 -11 -157 -11t-157 11q-152 21 -195 21q-118 0 -187 -84t-69 -204q0 -88 32 -153.5t81 -103t122 -60t140 -29.5t149 -7h168q82 0 149 7t140 29.5t122 60t81 103t32 153.5zM1664 496q0 -207 -61 -331q-38 -77 -105.5 -133t-141 -86 t-170 -47.5t-171.5 -22t-167 -4.5q-78 0 -142 3t-147.5 12.5t-152.5 30t-137 51.5t-121 81t-86 115q-62 123 -62 331q0 237 136 396q-27 82 -27 170q0 116 51 218q108 0 190 -39.5t189 -123.5q147 35 309 35q148 0 280 -32q105 82 187 121t189 39q51 -102 51 -218 q0 -87 -27 -168q136 -160 136 -398z" />
+<glyph unicode="&#xf114;" horiz-adv-x="1664" d="M1536 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68v-960q0 -40 28 -68t68 -28h1216q40 0 68 28t28 68zM1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320 q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
+<glyph unicode="&#xf115;" horiz-adv-x="1920" d="M1781 605q0 35 -53 35h-1088q-40 0 -85.5 -21.5t-71.5 -52.5l-294 -363q-18 -24 -18 -40q0 -35 53 -35h1088q40 0 86 22t71 53l294 363q18 22 18 39zM640 768h768v160q0 40 -28 68t-68 28h-576q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68 v-853l256 315q44 53 116 87.5t140 34.5zM1909 605q0 -62 -46 -120l-295 -363q-43 -53 -116 -87.5t-140 -34.5h-1088q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158v-160h192q54 0 99 -24.5t67 -70.5q15 -32 15 -68z " />
+<glyph unicode="&#xf116;" horiz-adv-x="1792" />
+<glyph unicode="&#xf117;" horiz-adv-x="1792" />
+<glyph unicode="&#xf118;" d="M1134 461q-37 -121 -138 -195t-228 -74t-228 74t-138 195q-8 25 4 48.5t38 31.5q25 8 48.5 -4t31.5 -38q25 -80 92.5 -129.5t151.5 -49.5t151.5 49.5t92.5 129.5q8 26 32 38t49 4t37 -31.5t4 -48.5zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5 t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf119;" d="M1134 307q8 -25 -4 -48.5t-37 -31.5t-49 4t-32 38q-25 80 -92.5 129.5t-151.5 49.5t-151.5 -49.5t-92.5 -129.5q-8 -26 -31.5 -38t-48.5 -4q-26 8 -38 31.5t-4 48.5q37 121 138 195t228 74t228 -74t138 -195zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204 t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf11a;" d="M1152 448q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h640q26 0 45 -19t19 -45zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf11b;" horiz-adv-x="1920" d="M832 448v128q0 14 -9 23t-23 9h-192v192q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-192h-192q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h192v-192q0 -14 9 -23t23 -9h128q14 0 23 9t9 23v192h192q14 0 23 9t9 23zM1408 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1920 512q0 -212 -150 -362t-362 -150q-192 0 -338 128h-220q-146 -128 -338 -128q-212 0 -362 150 t-150 362t150 362t362 150h896q212 0 362 -150t150 -362z" />
+<glyph unicode="&#xf11c;" horiz-adv-x="1920" d="M384 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM512 624v-96q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h224q16 0 16 -16zM384 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 368v-96q0 -16 -16 -16 h-864q-16 0 -16 16v96q0 16 16 16h864q16 0 16 -16zM768 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM640 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1024 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16 h96q16 0 16 -16zM896 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1280 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1152 880v-96 q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 880v-352q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h112v240q0 16 16 16h96q16 0 16 -16zM1792 128v896h-1664v-896 h1664zM1920 1024v-896q0 -53 -37.5 -90.5t-90.5 -37.5h-1664q-53 0 -90.5 37.5t-37.5 90.5v896q0 53 37.5 90.5t90.5 37.5h1664q53 0 90.5 -37.5t37.5 -90.5z" />
+<glyph unicode="&#xf11d;" horiz-adv-x="1792" d="M1664 491v616q-169 -91 -306 -91q-82 0 -145 32q-100 49 -184 76.5t-178 27.5q-173 0 -403 -127v-599q245 113 433 113q55 0 103.5 -7.5t98 -26t77 -31t82.5 -39.5l28 -14q44 -22 101 -22q120 0 293 92zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9 h-64q-14 0 -23 9t-9 23v1266q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102 q-15 -9 -33 -9q-16 0 -32 8q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
+<glyph unicode="&#xf11e;" horiz-adv-x="1792" d="M832 536v192q-181 -16 -384 -117v-185q205 96 384 110zM832 954v197q-172 -8 -384 -126v-189q215 111 384 118zM1664 491v184q-235 -116 -384 -71v224q-20 6 -39 15q-5 3 -33 17t-34.5 17t-31.5 15t-34.5 15.5t-32.5 13t-36 12.5t-35 8.5t-39.5 7.5t-39.5 4t-44 2 q-23 0 -49 -3v-222h19q102 0 192.5 -29t197.5 -82q19 -9 39 -15v-188q42 -17 91 -17q120 0 293 92zM1664 918v189q-169 -91 -306 -91q-45 0 -78 8v-196q148 -42 384 90zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v1266 q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102q-15 -9 -33 -9q-16 0 -32 8 q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
+<glyph unicode="&#xf120;" horiz-adv-x="1664" d="M585 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23zM1664 96v-64q0 -14 -9 -23t-23 -9h-960q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h960q14 0 23 -9 t9 -23z" />
+<glyph unicode="&#xf121;" horiz-adv-x="1920" d="M617 137l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23zM1208 1204l-373 -1291q-4 -13 -15.5 -19.5t-23.5 -2.5l-62 17q-13 4 -19.5 15.5t-2.5 24.5 l373 1291q4 13 15.5 19.5t23.5 2.5l62 -17q13 -4 19.5 -15.5t2.5 -24.5zM1865 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23z" />
+<glyph unicode="&#xf122;" horiz-adv-x="1792" d="M640 454v-70q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-69l-397 -398q-19 -19 -19 -45t19 -45zM1792 416q0 -58 -17 -133.5t-38.5 -138t-48 -125t-40.5 -90.5l-20 -40q-8 -17 -28 -17q-6 0 -9 1 q-25 8 -23 34q43 400 -106 565q-64 71 -170.5 110.5t-267.5 52.5v-251q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-262q411 -28 599 -221q169 -173 169 -509z" />
+<glyph unicode="&#xf123;" horiz-adv-x="1664" d="M1186 579l257 250l-356 52l-66 10l-30 60l-159 322v-963l59 -31l318 -168l-60 355l-12 66zM1638 841l-363 -354l86 -500q5 -33 -6 -51.5t-34 -18.5q-17 0 -40 12l-449 236l-449 -236q-23 -12 -40 -12q-23 0 -34 18.5t-6 51.5l86 500l-364 354q-32 32 -23 59.5t54 34.5 l502 73l225 455q20 41 49 41q28 0 49 -41l225 -455l502 -73q45 -7 54 -34.5t-24 -59.5z" />
+<glyph unicode="&#xf124;" horiz-adv-x="1408" d="M1401 1187l-640 -1280q-17 -35 -57 -35q-5 0 -15 2q-22 5 -35.5 22.5t-13.5 39.5v576h-576q-22 0 -39.5 13.5t-22.5 35.5t4 42t29 30l1280 640q13 7 29 7q27 0 45 -19q15 -14 18.5 -34.5t-6.5 -39.5z" />
+<glyph unicode="&#xf125;" horiz-adv-x="1664" d="M557 256h595v595zM512 301l595 595h-595v-595zM1664 224v-192q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v224h-864q-14 0 -23 9t-9 23v864h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224v224q0 14 9 23t23 9h192q14 0 23 -9t9 -23 v-224h851l246 247q10 9 23 9t23 -9q9 -10 9 -23t-9 -23l-247 -246v-851h224q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf126;" horiz-adv-x="1024" d="M288 64q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM288 1216q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM928 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1024 1088q0 -52 -26 -96.5t-70 -69.5 q-2 -287 -226 -414q-68 -38 -203 -81q-128 -40 -169.5 -71t-41.5 -100v-26q44 -25 70 -69.5t26 -96.5q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 52 26 96.5t70 69.5v820q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136q0 -52 -26 -96.5t-70 -69.5v-497 q54 26 154 57q55 17 87.5 29.5t70.5 31t59 39.5t40.5 51t28 69.5t8.5 91.5q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136z" />
+<glyph unicode="&#xf127;" horiz-adv-x="1664" d="M439 265l-256 -256q-10 -9 -23 -9q-12 0 -23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23zM608 224v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM384 448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23t9 23t23 9h320 q14 0 23 -9t9 -23zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-334 335q-21 21 -42 56l239 18l273 -274q27 -27 68 -27.5t68 26.5l147 146q28 28 28 67q0 40 -28 68l-274 275l18 239q35 -21 56 -42l336 -336q84 -86 84 -204zM1031 1044l-239 -18 l-273 274q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l274 -274l-18 -240q-35 21 -56 42l-336 336q-84 86 -84 204q0 120 85 203l147 146q83 83 203 83q121 0 204 -85l334 -335q21 -21 42 -56zM1664 960q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9 t-9 23t9 23t23 9h320q14 0 23 -9t9 -23zM1120 1504v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM1527 1353l-256 -256q-11 -9 -23 -9t-23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
+<glyph unicode="&#xf128;" horiz-adv-x="1024" d="M704 280v-240q0 -16 -12 -28t-28 -12h-240q-16 0 -28 12t-12 28v240q0 16 12 28t28 12h240q16 0 28 -12t12 -28zM1020 880q0 -54 -15.5 -101t-35 -76.5t-55 -59.5t-57.5 -43.5t-61 -35.5q-41 -23 -68.5 -65t-27.5 -67q0 -17 -12 -32.5t-28 -15.5h-240q-15 0 -25.5 18.5 t-10.5 37.5v45q0 83 65 156.5t143 108.5q59 27 84 56t25 76q0 42 -46.5 74t-107.5 32q-65 0 -108 -29q-35 -25 -107 -115q-13 -16 -31 -16q-12 0 -25 8l-164 125q-13 10 -15.5 25t5.5 28q160 266 464 266q80 0 161 -31t146 -83t106 -127.5t41 -158.5z" />
+<glyph unicode="&#xf129;" horiz-adv-x="640" d="M640 192v-128q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64v384h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-576h64q26 0 45 -19t19 -45zM512 1344v-192q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v192 q0 26 19 45t45 19h256q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf12a;" horiz-adv-x="640" d="M512 288v-224q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v224q0 26 19 45t45 19h256q26 0 45 -19t19 -45zM542 1344l-28 -768q-1 -26 -20.5 -45t-45.5 -19h-256q-26 0 -45.5 19t-20.5 45l-28 768q-1 26 17.5 45t44.5 19h320q26 0 44.5 -19t17.5 -45z" />
+<glyph unicode="&#xf12b;" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1534 846v-206h-514l-3 27 q-4 28 -4 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q83 65 188 65q110 0 178 -59.5t68 -158.5q0 -56 -24.5 -103t-62 -76.5t-81.5 -58.5t-82 -50.5t-65.5 -51.5t-30.5 -63h232v80 h126z" />
+<glyph unicode="&#xf12c;" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1536 -50v-206h-514l-4 27 q-3 45 -3 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q80 65 188 65q110 0 178 -59.5t68 -158.5q0 -66 -34.5 -118.5t-84 -86t-99.5 -62.5t-87 -63t-41 -73h232v80h126z" />
+<glyph unicode="&#xf12d;" horiz-adv-x="1920" d="M896 128l336 384h-768l-336 -384h768zM1909 1205q15 -34 9.5 -71.5t-30.5 -65.5l-896 -1024q-38 -44 -96 -44h-768q-38 0 -69.5 20.5t-47.5 54.5q-15 34 -9.5 71.5t30.5 65.5l896 1024q38 44 96 44h768q38 0 69.5 -20.5t47.5 -54.5z" />
+<glyph unicode="&#xf12e;" horiz-adv-x="1664" d="M1664 438q0 -81 -44.5 -135t-123.5 -54q-41 0 -77.5 17.5t-59 38t-56.5 38t-71 17.5q-110 0 -110 -124q0 -39 16 -115t15 -115v-5q-22 0 -33 -1q-34 -3 -97.5 -11.5t-115.5 -13.5t-98 -5q-61 0 -103 26.5t-42 83.5q0 37 17.5 71t38 56.5t38 59t17.5 77.5q0 79 -54 123.5 t-135 44.5q-84 0 -143 -45.5t-59 -127.5q0 -43 15 -83t33.5 -64.5t33.5 -53t15 -50.5q0 -45 -46 -89q-37 -35 -117 -35q-95 0 -245 24q-9 2 -27.5 4t-27.5 4l-13 2q-1 0 -3 1q-2 0 -2 1v1024q2 -1 17.5 -3.5t34 -5t21.5 -3.5q150 -24 245 -24q80 0 117 35q46 44 46 89 q0 22 -15 50.5t-33.5 53t-33.5 64.5t-15 83q0 82 59 127.5t144 45.5q80 0 134 -44.5t54 -123.5q0 -41 -17.5 -77.5t-38 -59t-38 -56.5t-17.5 -71q0 -57 42 -83.5t103 -26.5q64 0 180 15t163 17v-2q-1 -2 -3.5 -17.5t-5 -34t-3.5 -21.5q-24 -150 -24 -245q0 -80 35 -117 q44 -46 89 -46q22 0 50.5 15t53 33.5t64.5 33.5t83 15q82 0 127.5 -59t45.5 -143z" />
+<glyph unicode="&#xf130;" horiz-adv-x="1152" d="M1152 832v-128q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-217 24 -364.5 187.5t-147.5 384.5v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -185 131.5 -316.5t316.5 -131.5 t316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45zM896 1216v-512q0 -132 -94 -226t-226 -94t-226 94t-94 226v512q0 132 94 226t226 94t226 -94t94 -226z" />
+<glyph unicode="&#xf131;" horiz-adv-x="1408" d="M271 591l-101 -101q-42 103 -42 214v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -53 15 -113zM1385 1193l-361 -361v-128q0 -132 -94 -226t-226 -94q-55 0 -109 19l-96 -96q97 -51 205 -51q185 0 316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45v-128 q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-125 13 -235 81l-254 -254q-10 -10 -23 -10t-23 10l-82 82q-10 10 -10 23t10 23l1234 1234q10 10 23 10t23 -10l82 -82q10 -10 10 -23 t-10 -23zM1005 1325l-621 -621v512q0 132 94 226t226 94q102 0 184.5 -59t116.5 -152z" />
+<glyph unicode="&#xf132;" horiz-adv-x="1280" d="M1088 576v640h-448v-1137q119 63 213 137q235 184 235 360zM1280 1344v-768q0 -86 -33.5 -170.5t-83 -150t-118 -127.5t-126.5 -103t-121 -77.5t-89.5 -49.5t-42.5 -20q-12 -6 -26 -6t-26 6q-16 7 -42.5 20t-89.5 49.5t-121 77.5t-126.5 103t-118 127.5t-83 150 t-33.5 170.5v768q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf133;" horiz-adv-x="1664" d="M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf134;" horiz-adv-x="1408" d="M512 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 1376v-320q0 -16 -12 -25q-8 -7 -20 -7q-4 0 -7 1l-448 96q-11 2 -18 11t-7 20h-256v-102q111 -23 183.5 -111t72.5 -203v-800q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v800 q0 106 62.5 190.5t161.5 114.5v111h-32q-59 0 -115 -23.5t-91.5 -53t-66 -66.5t-40.5 -53.5t-14 -24.5q-17 -35 -57 -35q-16 0 -29 7q-23 12 -31.5 37t3.5 49q5 10 14.5 26t37.5 53.5t60.5 70t85 67t108.5 52.5q-25 42 -25 86q0 66 47 113t113 47t113 -47t47 -113 q0 -33 -14 -64h302q0 11 7 20t18 11l448 96q3 1 7 1q12 0 20 -7q12 -9 12 -25z" />
+<glyph unicode="&#xf135;" horiz-adv-x="1664" d="M1440 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1664 1376q0 -249 -75.5 -430.5t-253.5 -360.5q-81 -80 -195 -176l-20 -379q-2 -16 -16 -26l-384 -224q-7 -4 -16 -4q-12 0 -23 9l-64 64q-13 14 -8 32l85 276l-281 281l-276 -85q-3 -1 -9 -1 q-14 0 -23 9l-64 64q-17 19 -5 39l224 384q10 14 26 16l379 20q96 114 176 195q188 187 358 258t431 71q14 0 24 -9.5t10 -22.5z" />
+<glyph unicode="&#xf136;" horiz-adv-x="1792" d="M1745 763l-164 -763h-334l178 832q13 56 -15 88q-27 33 -83 33h-169l-204 -953h-334l204 953h-286l-204 -953h-334l204 953l-153 327h1276q101 0 189.5 -40.5t147.5 -113.5q60 -73 81 -168.5t0 -194.5z" />
+<glyph unicode="&#xf137;" d="M909 141l102 102q19 19 19 45t-19 45l-307 307l307 307q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf138;" d="M717 141l454 454q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l307 -307l-307 -307q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf139;" d="M1165 397l102 102q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l307 307l307 -307q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf13a;" d="M813 237l454 454q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-307 -307l-307 307q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf13b;" horiz-adv-x="1408" d="M1130 939l16 175h-884l47 -534h612l-22 -228l-197 -53l-196 53l-13 140h-175l22 -278l362 -100h4v1l359 99l50 544h-644l-15 181h674zM0 1408h1408l-128 -1438l-578 -162l-574 162z" />
+<glyph unicode="&#xf13c;" horiz-adv-x="1792" d="M275 1408h1505l-266 -1333l-804 -267l-698 267l71 356h297l-29 -147l422 -161l486 161l68 339h-1208l58 297h1209l38 191h-1208z" />
+<glyph unicode="&#xf13d;" horiz-adv-x="1792" d="M960 1280q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1792 352v-352q0 -22 -20 -30q-8 -2 -12 -2q-13 0 -23 9l-93 93q-119 -143 -318.5 -226.5t-429.5 -83.5t-429.5 83.5t-318.5 226.5l-93 -93q-9 -9 -23 -9q-4 0 -12 2q-20 8 -20 30v352 q0 14 9 23t23 9h352q22 0 30 -20q8 -19 -7 -35l-100 -100q67 -91 189.5 -153.5t271.5 -82.5v647h-192q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h192v163q-58 34 -93 92.5t-35 128.5q0 106 75 181t181 75t181 -75t75 -181q0 -70 -35 -128.5t-93 -92.5v-163h192q26 0 45 -19 t19 -45v-128q0 -26 -19 -45t-45 -19h-192v-647q149 20 271.5 82.5t189.5 153.5l-100 100q-15 16 -7 35q8 20 30 20h352q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf13e;" horiz-adv-x="1152" d="M1056 768q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v320q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45q0 106 -75 181t-181 75t-181 -75t-75 -181 v-320h736z" />
+<glyph unicode="&#xf140;" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM1152 640q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM1280 640q0 -212 -150 -362t-362 -150t-362 150 t-150 362t150 362t362 150t362 -150t150 -362zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf141;" horiz-adv-x="1408" d="M384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM896 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM1408 800v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf142;" horiz-adv-x="384" d="M384 288v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 1312v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
+<glyph unicode="&#xf143;" d="M512 256q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM863 162q-13 232 -177 396t-396 177q-14 1 -24 -9t-10 -23v-128q0 -13 8.5 -22t21.5 -10q154 -11 264 -121t121 -264q1 -13 10 -21.5t22 -8.5h128q13 0 23 10 t9 24zM1247 161q-5 154 -56 297.5t-139.5 260t-205 205t-260 139.5t-297.5 56q-14 1 -23 -9q-10 -10 -10 -23v-128q0 -13 9 -22t22 -10q204 -7 378 -111.5t278.5 -278.5t111.5 -378q1 -13 10 -22t22 -9h128q13 0 23 10q11 9 9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf144;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1152 585q32 18 32 55t-32 55l-544 320q-31 19 -64 1q-32 -19 -32 -56v-640q0 -37 32 -56 q16 -8 32 -8q17 0 32 9z" />
+<glyph unicode="&#xf145;" horiz-adv-x="1792" d="M1024 1084l316 -316l-572 -572l-316 316zM813 105l618 618q19 19 19 45t-19 45l-362 362q-18 18 -45 18t-45 -18l-618 -618q-19 -19 -19 -45t19 -45l362 -362q18 -18 45 -18t45 18zM1702 742l-907 -908q-37 -37 -90.5 -37t-90.5 37l-126 126q56 56 56 136t-56 136 t-136 56t-136 -56l-125 126q-37 37 -37 90.5t37 90.5l907 906q37 37 90.5 37t90.5 -37l125 -125q-56 -56 -56 -136t56 -136t136 -56t136 56l126 -125q37 -37 37 -90.5t-37 -90.5z" />
+<glyph unicode="&#xf146;" d="M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
+<glyph unicode="&#xf147;" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h832q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5 t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf148;" horiz-adv-x="1024" d="M1018 933q-18 -37 -58 -37h-192v-864q0 -14 -9 -23t-23 -9h-704q-21 0 -29 18q-8 20 4 35l160 192q9 11 25 11h320v640h-192q-40 0 -58 37q-17 37 9 68l320 384q18 22 49 22t49 -22l320 -384q27 -32 9 -68z" />
+<glyph unicode="&#xf149;" horiz-adv-x="1024" d="M32 1280h704q13 0 22.5 -9.5t9.5 -23.5v-863h192q40 0 58 -37t-9 -69l-320 -384q-18 -22 -49 -22t-49 22l-320 384q-26 31 -9 69q18 37 58 37h192v640h-320q-14 0 -25 11l-160 192q-13 14 -4 34q9 19 29 19z" />
+<glyph unicode="&#xf14a;" d="M685 237l614 614q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-467 -467l-211 211q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l358 -358q19 -19 45 -19t45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5 t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf14b;" d="M404 428l152 -152l-52 -52h-56v96h-96v56zM818 818q14 -13 -3 -30l-291 -291q-17 -17 -30 -3q-14 13 3 30l291 291q17 17 30 3zM544 128l544 544l-288 288l-544 -544v-288h288zM1152 736l92 92q28 28 28 68t-28 68l-152 152q-28 28 -68 28t-68 -28l-92 -92zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf14c;" d="M1280 608v480q0 26 -19 45t-45 19h-480q-42 0 -59 -39q-17 -41 14 -70l144 -144l-534 -534q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l534 534l144 -144q18 -19 45 -19q12 0 25 5q39 17 39 59zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf14d;" d="M1005 435l352 352q19 19 19 45t-19 45l-352 352q-30 31 -69 14q-40 -17 -40 -59v-160q-119 0 -216 -19.5t-162.5 -51t-114 -79t-76.5 -95.5t-44.5 -109t-21.5 -111.5t-5 -110.5q0 -181 167 -404q10 -12 25 -12q7 0 13 3q22 9 19 33q-44 354 62 473q46 52 130 75.5 t224 23.5v-160q0 -42 40 -59q12 -5 24 -5q26 0 45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf14e;" d="M640 448l256 128l-256 128v-256zM1024 1039v-542l-512 -256v542zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf150;" d="M1145 861q18 -35 -5 -66l-320 -448q-19 -27 -52 -27t-52 27l-320 448q-23 31 -5 66q17 35 57 35h640q40 0 57 -35zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf151;" d="M1145 419q-17 -35 -57 -35h-640q-40 0 -57 35q-18 35 5 66l320 448q19 27 52 27t52 -27l320 -448q23 -31 5 -66zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf152;" d="M1088 640q0 -33 -27 -52l-448 -320q-31 -23 -66 -5q-35 17 -35 57v640q0 40 35 57q35 18 66 -5l448 -320q27 -19 27 -52zM1280 160v960q0 14 -9 23t-23 9h-960q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h960q14 0 23 9t9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf153;" horiz-adv-x="1024" d="M976 229l35 -159q3 -12 -3 -22.5t-17 -14.5l-5 -1q-4 -2 -10.5 -3.5t-16 -4.5t-21.5 -5.5t-25.5 -5t-30 -5t-33.5 -4.5t-36.5 -3t-38.5 -1q-234 0 -409 130.5t-238 351.5h-95q-13 0 -22.5 9.5t-9.5 22.5v113q0 13 9.5 22.5t22.5 9.5h66q-2 57 1 105h-67q-14 0 -23 9 t-9 23v114q0 14 9 23t23 9h98q67 210 243.5 338t400.5 128q102 0 194 -23q11 -3 20 -15q6 -11 3 -24l-43 -159q-3 -13 -14 -19.5t-24 -2.5l-4 1q-4 1 -11.5 2.5l-17.5 3.5t-22.5 3.5t-26 3t-29 2.5t-29.5 1q-126 0 -226 -64t-150 -176h468q16 0 25 -12q10 -12 7 -26 l-24 -114q-5 -26 -32 -26h-488q-3 -37 0 -105h459q15 0 25 -12q9 -12 6 -27l-24 -112q-2 -11 -11 -18.5t-20 -7.5h-387q48 -117 149.5 -185.5t228.5 -68.5q18 0 36 1.5t33.5 3.5t29.5 4.5t24.5 5t18.5 4.5l12 3l5 2q13 5 26 -2q12 -7 15 -21z" />
+<glyph unicode="&#xf154;" horiz-adv-x="1024" d="M1020 399v-367q0 -14 -9 -23t-23 -9h-956q-14 0 -23 9t-9 23v150q0 13 9.5 22.5t22.5 9.5h97v383h-95q-14 0 -23 9.5t-9 22.5v131q0 14 9 23t23 9h95v223q0 171 123.5 282t314.5 111q185 0 335 -125q9 -8 10 -20.5t-7 -22.5l-103 -127q-9 -11 -22 -12q-13 -2 -23 7 q-5 5 -26 19t-69 32t-93 18q-85 0 -137 -47t-52 -123v-215h305q13 0 22.5 -9t9.5 -23v-131q0 -13 -9.5 -22.5t-22.5 -9.5h-305v-379h414v181q0 13 9 22.5t23 9.5h162q14 0 23 -9.5t9 -22.5z" />
+<glyph unicode="&#xf155;" horiz-adv-x="1024" d="M978 351q0 -153 -99.5 -263.5t-258.5 -136.5v-175q0 -14 -9 -23t-23 -9h-135q-13 0 -22.5 9.5t-9.5 22.5v175q-66 9 -127.5 31t-101.5 44.5t-74 48t-46.5 37.5t-17.5 18q-17 21 -2 41l103 135q7 10 23 12q15 2 24 -9l2 -2q113 -99 243 -125q37 -8 74 -8q81 0 142.5 43 t61.5 122q0 28 -15 53t-33.5 42t-58.5 37.5t-66 32t-80 32.5q-39 16 -61.5 25t-61.5 26.5t-62.5 31t-56.5 35.5t-53.5 42.5t-43.5 49t-35.5 58t-21 66.5t-8.5 78q0 138 98 242t255 134v180q0 13 9.5 22.5t22.5 9.5h135q14 0 23 -9t9 -23v-176q57 -6 110.5 -23t87 -33.5 t63.5 -37.5t39 -29t15 -14q17 -18 5 -38l-81 -146q-8 -15 -23 -16q-14 -3 -27 7q-3 3 -14.5 12t-39 26.5t-58.5 32t-74.5 26t-85.5 11.5q-95 0 -155 -43t-60 -111q0 -26 8.5 -48t29.5 -41.5t39.5 -33t56 -31t60.5 -27t70 -27.5q53 -20 81 -31.5t76 -35t75.5 -42.5t62 -50 t53 -63.5t31.5 -76.5t13 -94z" />
+<glyph unicode="&#xf156;" horiz-adv-x="898" d="M898 1066v-102q0 -14 -9 -23t-23 -9h-168q-23 -144 -129 -234t-276 -110q167 -178 459 -536q14 -16 4 -34q-8 -18 -29 -18h-195q-16 0 -25 12q-306 367 -498 571q-9 9 -9 22v127q0 13 9.5 22.5t22.5 9.5h112q132 0 212.5 43t102.5 125h-427q-14 0 -23 9t-9 23v102 q0 14 9 23t23 9h413q-57 113 -268 113h-145q-13 0 -22.5 9.5t-9.5 22.5v133q0 14 9 23t23 9h832q14 0 23 -9t9 -23v-102q0 -14 -9 -23t-23 -9h-233q47 -61 64 -144h171q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf157;" horiz-adv-x="1027" d="M603 0h-172q-13 0 -22.5 9t-9.5 23v330h-288q-13 0 -22.5 9t-9.5 23v103q0 13 9.5 22.5t22.5 9.5h288v85h-288q-13 0 -22.5 9t-9.5 23v104q0 13 9.5 22.5t22.5 9.5h214l-321 578q-8 16 0 32q10 16 28 16h194q19 0 29 -18l215 -425q19 -38 56 -125q10 24 30.5 68t27.5 61 l191 420q8 19 29 19h191q17 0 27 -16q9 -14 1 -31l-313 -579h215q13 0 22.5 -9.5t9.5 -22.5v-104q0 -14 -9.5 -23t-22.5 -9h-290v-85h290q13 0 22.5 -9.5t9.5 -22.5v-103q0 -14 -9.5 -23t-22.5 -9h-290v-330q0 -13 -9.5 -22.5t-22.5 -9.5z" />
+<glyph unicode="&#xf158;" horiz-adv-x="1280" d="M1043 971q0 100 -65 162t-171 62h-320v-448h320q106 0 171 62t65 162zM1280 971q0 -193 -126.5 -315t-326.5 -122h-340v-118h505q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-505v-192q0 -14 -9.5 -23t-22.5 -9h-167q-14 0 -23 9t-9 23v192h-224q-14 0 -23 9t-9 23v128 q0 14 9 23t23 9h224v118h-224q-14 0 -23 9t-9 23v149q0 13 9 22.5t23 9.5h224v629q0 14 9 23t23 9h539q200 0 326.5 -122t126.5 -315z" />
+<glyph unicode="&#xf159;" horiz-adv-x="1792" d="M514 341l81 299h-159l75 -300q1 -1 1 -3t1 -3q0 1 0.5 3.5t0.5 3.5zM630 768l35 128h-292l32 -128h225zM822 768h139l-35 128h-70zM1271 340l78 300h-162l81 -299q0 -1 0.5 -3.5t1.5 -3.5q0 1 0.5 3t0.5 3zM1382 768l33 128h-297l34 -128h230zM1792 736v-64q0 -14 -9 -23 t-23 -9h-213l-164 -616q-7 -24 -31 -24h-159q-24 0 -31 24l-166 616h-209l-167 -616q-7 -24 -31 -24h-159q-11 0 -19.5 7t-10.5 17l-160 616h-208q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h175l-33 128h-142q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h109l-89 344q-5 15 5 28 q10 12 26 12h137q26 0 31 -24l90 -360h359l97 360q7 24 31 24h126q24 0 31 -24l98 -360h365l93 360q5 24 31 24h137q16 0 26 -12q10 -13 5 -28l-91 -344h111q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-145l-34 -128h179q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf15a;" horiz-adv-x="1280" d="M1167 896q18 -182 -131 -258q117 -28 175 -103t45 -214q-7 -71 -32.5 -125t-64.5 -89t-97 -58.5t-121.5 -34.5t-145.5 -15v-255h-154v251q-80 0 -122 1v-252h-154v255q-18 0 -54 0.5t-55 0.5h-200l31 183h111q50 0 58 51v402h16q-6 1 -16 1v287q-13 68 -89 68h-111v164 l212 -1q64 0 97 1v252h154v-247q82 2 122 2v245h154v-252q79 -7 140 -22.5t113 -45t82.5 -78t36.5 -114.5zM952 351q0 36 -15 64t-37 46t-57.5 30.5t-65.5 18.5t-74 9t-69 3t-64.5 -1t-47.5 -1v-338q8 0 37 -0.5t48 -0.5t53 1.5t58.5 4t57 8.5t55.5 14t47.5 21t39.5 30 t24.5 40t9.5 51zM881 827q0 33 -12.5 58.5t-30.5 42t-48 28t-55 16.5t-61.5 8t-58 2.5t-54 -1t-39.5 -0.5v-307q5 0 34.5 -0.5t46.5 0t50 2t55 5.5t51.5 11t48.5 18.5t37 27t27 38.5t9 51z" />
+<glyph unicode="&#xf15b;" d="M1024 1024v472q22 -14 36 -28l408 -408q14 -14 28 -36h-472zM896 992q0 -40 28 -68t68 -28h544v-1056q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h800v-544z" />
+<glyph unicode="&#xf15c;" d="M1468 1060q14 -14 28 -36h-472v472q22 -14 36 -28zM992 896h544v-1056q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h800v-544q0 -40 28 -68t68 -28zM1152 160v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704 q14 0 23 9t9 23zM1152 416v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1152 672v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23z" />
+<glyph unicode="&#xf15d;" horiz-adv-x="1664" d="M1191 1128h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1572 -23 v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -11v-2l14 2q9 2 30 2h248v119h121zM1661 874v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162 l230 -662h70z" />
+<glyph unicode="&#xf15e;" horiz-adv-x="1664" d="M1191 104h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1661 -150 v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162l230 -662h70zM1572 1001v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -10v-3l14 3q9 1 30 1h248 v119h121z" />
+<glyph unicode="&#xf160;" horiz-adv-x="1792" d="M736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1792 -32v-192q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832 q14 0 23 -9t9 -23zM1600 480v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1408 992v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1216 1504v-192q0 -14 -9 -23t-23 -9h-256 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf161;" horiz-adv-x="1792" d="M1216 -32v-192q0 -14 -9 -23t-23 -9h-256q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192 q14 0 23 -9t9 -23zM1408 480v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1600 992v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1792 1504v-192q0 -14 -9 -23t-23 -9h-832 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf162;" d="M1346 223q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23 zM1486 165q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5 t82 -252.5zM1456 882v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165z" />
+<glyph unicode="&#xf163;" d="M1346 1247q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9 t9 -23zM1456 -142v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165zM1486 1189q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13 q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5t82 -252.5z" />
+<glyph unicode="&#xf164;" horiz-adv-x="1664" d="M256 192q0 26 -19 45t-45 19q-27 0 -45.5 -19t-18.5 -45q0 -27 18.5 -45.5t45.5 -18.5q26 0 45 18.5t19 45.5zM416 704v-640q0 -26 -19 -45t-45 -19h-288q-26 0 -45 19t-19 45v640q0 26 19 45t45 19h288q26 0 45 -19t19 -45zM1600 704q0 -86 -55 -149q15 -44 15 -76 q3 -76 -43 -137q17 -56 0 -117q-15 -57 -54 -94q9 -112 -49 -181q-64 -76 -197 -78h-36h-76h-17q-66 0 -144 15.5t-121.5 29t-120.5 39.5q-123 43 -158 44q-26 1 -45 19.5t-19 44.5v641q0 25 18 43.5t43 20.5q24 2 76 59t101 121q68 87 101 120q18 18 31 48t17.5 48.5 t13.5 60.5q7 39 12.5 61t19.5 52t34 50q19 19 45 19q46 0 82.5 -10.5t60 -26t40 -40.5t24 -45t12 -50t5 -45t0.5 -39q0 -38 -9.5 -76t-19 -60t-27.5 -56q-3 -6 -10 -18t-11 -22t-8 -24h277q78 0 135 -57t57 -135z" />
+<glyph unicode="&#xf165;" horiz-adv-x="1664" d="M256 960q0 -26 -19 -45t-45 -19q-27 0 -45.5 19t-18.5 45q0 27 18.5 45.5t45.5 18.5q26 0 45 -18.5t19 -45.5zM416 448v640q0 26 -19 45t-45 19h-288q-26 0 -45 -19t-19 -45v-640q0 -26 19 -45t45 -19h288q26 0 45 19t19 45zM1545 597q55 -61 55 -149q-1 -78 -57.5 -135 t-134.5 -57h-277q4 -14 8 -24t11 -22t10 -18q18 -37 27 -57t19 -58.5t10 -76.5q0 -24 -0.5 -39t-5 -45t-12 -50t-24 -45t-40 -40.5t-60 -26t-82.5 -10.5q-26 0 -45 19q-20 20 -34 50t-19.5 52t-12.5 61q-9 42 -13.5 60.5t-17.5 48.5t-31 48q-33 33 -101 120q-49 64 -101 121 t-76 59q-25 2 -43 20.5t-18 43.5v641q0 26 19 44.5t45 19.5q35 1 158 44q77 26 120.5 39.5t121.5 29t144 15.5h17h76h36q133 -2 197 -78q58 -69 49 -181q39 -37 54 -94q17 -61 0 -117q46 -61 43 -137q0 -32 -15 -76z" />
+<glyph unicode="&#xf166;" d="M919 233v157q0 50 -29 50q-17 0 -33 -16v-224q16 -16 33 -16q29 0 29 49zM1103 355h66v34q0 51 -33 51t-33 -51v-34zM532 621v-70h-80v-423h-74v423h-78v70h232zM733 495v-367h-67v40q-39 -45 -76 -45q-33 0 -42 28q-6 16 -6 54v290h66v-270q0 -24 1 -26q1 -15 15 -15 q20 0 42 31v280h67zM985 384v-146q0 -52 -7 -73q-12 -42 -53 -42q-35 0 -68 41v-36h-67v493h67v-161q32 40 68 40q41 0 53 -42q7 -21 7 -74zM1236 255v-9q0 -29 -2 -43q-3 -22 -15 -40q-27 -40 -80 -40q-52 0 -81 38q-21 27 -21 86v129q0 59 20 86q29 38 80 38t78 -38 q21 -28 21 -86v-76h-133v-65q0 -51 34 -51q24 0 30 26q0 1 0.5 7t0.5 16.5v21.5h68zM785 1079v-156q0 -51 -32 -51t-32 51v156q0 52 32 52t32 -52zM1318 366q0 177 -19 260q-10 44 -43 73.5t-76 34.5q-136 15 -412 15q-275 0 -411 -15q-44 -5 -76.5 -34.5t-42.5 -73.5 q-20 -87 -20 -260q0 -176 20 -260q10 -43 42.5 -73t75.5 -35q137 -15 412 -15t412 15q43 5 75.5 35t42.5 73q20 84 20 260zM563 1017l90 296h-75l-51 -195l-53 195h-78l24 -69t23 -69q35 -103 46 -158v-201h74v201zM852 936v130q0 58 -21 87q-29 38 -78 38q-51 0 -78 -38 q-21 -29 -21 -87v-130q0 -58 21 -87q27 -38 78 -38q49 0 78 38q21 27 21 87zM1033 816h67v370h-67v-283q-22 -31 -42 -31q-15 0 -16 16q-1 2 -1 26v272h-67v-293q0 -37 6 -55q11 -27 43 -27q36 0 77 45v-40zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf167;" d="M971 292v-211q0 -67 -39 -67q-23 0 -45 22v301q22 22 45 22q39 0 39 -67zM1309 291v-46h-90v46q0 68 45 68t45 -68zM343 509h107v94h-312v-94h105v-569h100v569zM631 -60h89v494h-89v-378q-30 -42 -57 -42q-18 0 -21 21q-1 3 -1 35v364h-89v-391q0 -49 8 -73 q12 -37 58 -37q48 0 102 61v-54zM1060 88v197q0 73 -9 99q-17 56 -71 56q-50 0 -93 -54v217h-89v-663h89v48q45 -55 93 -55q54 0 71 55q9 27 9 100zM1398 98v13h-91q0 -51 -2 -61q-7 -36 -40 -36q-46 0 -46 69v87h179v103q0 79 -27 116q-39 51 -106 51q-68 0 -107 -51 q-28 -37 -28 -116v-173q0 -79 29 -116q39 -51 108 -51q72 0 108 53q18 27 21 54q2 9 2 58zM790 1011v210q0 69 -43 69t-43 -69v-210q0 -70 43 -70t43 70zM1509 260q0 -234 -26 -350q-14 -59 -58 -99t-102 -46q-184 -21 -555 -21t-555 21q-58 6 -102.5 46t-57.5 99 q-26 112 -26 350q0 234 26 350q14 59 58 99t103 47q183 20 554 20t555 -20q58 -7 102.5 -47t57.5 -99q26 -112 26 -350zM511 1536h102l-121 -399v-271h-100v271q-14 74 -61 212q-37 103 -65 187h106l71 -263zM881 1203v-175q0 -81 -28 -118q-37 -51 -106 -51q-67 0 -105 51 q-28 38 -28 118v175q0 80 28 117q38 51 105 51q69 0 106 -51q28 -37 28 -117zM1216 1365v-499h-91v55q-53 -62 -103 -62q-46 0 -59 37q-8 24 -8 75v394h91v-367q0 -33 1 -35q3 -22 21 -22q27 0 57 43v381h91z" />
+<glyph unicode="&#xf168;" horiz-adv-x="1408" d="M597 869q-10 -18 -257 -456q-27 -46 -65 -46h-239q-21 0 -31 17t0 36l253 448q1 0 0 1l-161 279q-12 22 -1 37q9 15 32 15h239q40 0 66 -45zM1403 1511q11 -16 0 -37l-528 -934v-1l336 -615q11 -20 1 -37q-10 -15 -32 -15h-239q-42 0 -66 45l-339 622q18 32 531 942 q25 45 64 45h241q22 0 31 -15z" />
+<glyph unicode="&#xf169;" d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1 l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf16a;" horiz-adv-x="1792" d="M1280 640q0 37 -30 54l-512 320q-31 20 -65 2q-33 -18 -33 -56v-640q0 -38 33 -56q16 -8 31 -8q20 0 34 10l512 320q30 17 30 54zM1792 640q0 -96 -1 -150t-8.5 -136.5t-22.5 -147.5q-16 -73 -69 -123t-124 -58q-222 -25 -671 -25t-671 25q-71 8 -124.5 58t-69.5 123 q-14 65 -21.5 147.5t-8.5 136.5t-1 150t1 150t8.5 136.5t22.5 147.5q16 73 69 123t124 58q222 25 671 25t671 -25q71 -8 124.5 -58t69.5 -123q14 -65 21.5 -147.5t8.5 -136.5t1 -150z" />
+<glyph unicode="&#xf16b;" horiz-adv-x="1792" d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" />
+<glyph unicode="&#xf16c;" d="M1289 -96h-1118v480h-160v-640h1438v640h-160v-480zM347 428l33 157l783 -165l-33 -156zM450 802l67 146l725 -339l-67 -145zM651 1158l102 123l614 -513l-102 -123zM1048 1536l477 -641l-128 -96l-477 641zM330 65v159h800v-159h-800z" />
+<glyph unicode="&#xf16d;" d="M1362 110v648h-135q20 -63 20 -131q0 -126 -64 -232.5t-174 -168.5t-240 -62q-197 0 -337 135.5t-140 327.5q0 68 20 131h-141v-648q0 -26 17.5 -43.5t43.5 -17.5h1069q25 0 43 17.5t18 43.5zM1078 643q0 124 -90.5 211.5t-218.5 87.5q-127 0 -217.5 -87.5t-90.5 -211.5 t90.5 -211.5t217.5 -87.5q128 0 218.5 87.5t90.5 211.5zM1362 1003v165q0 28 -20 48.5t-49 20.5h-174q-29 0 -49 -20.5t-20 -48.5v-165q0 -29 20 -49t49 -20h174q29 0 49 20t20 49zM1536 1211v-1142q0 -81 -58 -139t-139 -58h-1142q-81 0 -139 58t-58 139v1142q0 81 58 139 t139 58h1142q81 0 139 -58t58 -139z" />
+<glyph unicode="&#xf16e;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" />
+<glyph unicode="&#xf170;" d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf171;" horiz-adv-x="1408" d="M815 677q8 -63 -50.5 -101t-111.5 -6q-39 17 -53.5 58t-0.5 82t52 58q36 18 72.5 12t64 -35.5t27.5 -67.5zM926 698q-14 107 -113 164t-197 13q-63 -28 -100.5 -88.5t-34.5 -129.5q4 -91 77.5 -155t165.5 -56q91 8 152 84t50 168zM1165 1240q-20 27 -56 44.5t-58 22 t-71 12.5q-291 47 -566 -2q-43 -7 -66 -12t-55 -22t-50 -43q30 -28 76 -45.5t73.5 -22t87.5 -11.5q228 -29 448 -1q63 8 89.5 12t72.5 21.5t75 46.5zM1222 205q-8 -26 -15.5 -76.5t-14 -84t-28.5 -70t-58 -56.5q-86 -48 -189.5 -71.5t-202 -22t-201.5 18.5q-46 8 -81.5 18 t-76.5 27t-73 43.5t-52 61.5q-25 96 -57 292l6 16l18 9q223 -148 506.5 -148t507.5 148q21 -6 24 -23t-5 -45t-8 -37zM1403 1166q-26 -167 -111 -655q-5 -30 -27 -56t-43.5 -40t-54.5 -31q-252 -126 -610 -88q-248 27 -394 139q-15 12 -25.5 26.5t-17 35t-9 34t-6 39.5 t-5.5 35q-9 50 -26.5 150t-28 161.5t-23.5 147.5t-22 158q3 26 17.5 48.5t31.5 37.5t45 30t46 22.5t48 18.5q125 46 313 64q379 37 676 -50q155 -46 215 -122q16 -20 16.5 -51t-5.5 -54z" />
+<glyph unicode="&#xf172;" d="M848 666q0 43 -41 66t-77 1q-43 -20 -42.5 -72.5t43.5 -70.5q39 -23 81 4t36 72zM928 682q8 -66 -36 -121t-110 -61t-119 40t-56 113q-2 49 25.5 93t72.5 64q70 31 141.5 -10t81.5 -118zM1100 1073q-20 -21 -53.5 -34t-53 -16t-63.5 -8q-155 -20 -324 0q-44 6 -63 9.5 t-52.5 16t-54.5 32.5q13 19 36 31t40 15.5t47 8.5q198 35 408 1q33 -5 51 -8.5t43 -16t39 -31.5zM1142 327q0 7 5.5 26.5t3 32t-17.5 16.5q-161 -106 -365 -106t-366 106l-12 -6l-5 -12q26 -154 41 -210q47 -81 204 -108q249 -46 428 53q34 19 49 51.5t22.5 85.5t12.5 71z M1272 1020q9 53 -8 75q-43 55 -155 88q-216 63 -487 36q-132 -12 -226 -46q-38 -15 -59.5 -25t-47 -34t-29.5 -54q8 -68 19 -138t29 -171t24 -137q1 -5 5 -31t7 -36t12 -27t22 -28q105 -80 284 -100q259 -28 440 63q24 13 39.5 23t31 29t19.5 40q48 267 80 473zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf173;" horiz-adv-x="1024" d="M944 207l80 -237q-23 -35 -111 -66t-177 -32q-104 -2 -190.5 26t-142.5 74t-95 106t-55.5 120t-16.5 118v544h-168v215q72 26 129 69.5t91 90t58 102t34 99t15 88.5q1 5 4.5 8.5t7.5 3.5h244v-424h333v-252h-334v-518q0 -30 6.5 -56t22.5 -52.5t49.5 -41.5t81.5 -14 q78 2 134 29z" />
+<glyph unicode="&#xf174;" d="M1136 75l-62 183q-44 -22 -103 -22q-36 -1 -62 10.5t-38.5 31.5t-17.5 40.5t-5 43.5v398h257v194h-256v326h-188q-8 0 -9 -10q-5 -44 -17.5 -87t-39 -95t-77 -95t-118.5 -68v-165h130v-418q0 -57 21.5 -115t65 -111t121 -85.5t176.5 -30.5q69 1 136.5 25t85.5 50z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf175;" horiz-adv-x="768" d="M765 237q8 -19 -5 -35l-350 -384q-10 -10 -23 -10q-14 0 -24 10l-355 384q-13 16 -5 35q9 19 29 19h224v1248q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1248h224q21 0 29 -19z" />
+<glyph unicode="&#xf176;" horiz-adv-x="768" d="M765 1043q-9 -19 -29 -19h-224v-1248q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1248h-224q-21 0 -29 19t5 35l350 384q10 10 23 10q14 0 24 -10l355 -384q13 -16 5 -35z" />
+<glyph unicode="&#xf177;" horiz-adv-x="1792" d="M1792 736v-192q0 -14 -9 -23t-23 -9h-1248v-224q0 -21 -19 -29t-35 5l-384 350q-10 10 -10 23q0 14 10 24l384 354q16 14 35 6q19 -9 19 -29v-224h1248q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf178;" horiz-adv-x="1792" d="M1728 643q0 -14 -10 -24l-384 -354q-16 -14 -35 -6q-19 9 -19 29v224h-1248q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h1248v224q0 21 19 29t35 -5l384 -350q10 -10 10 -23z" />
+<glyph unicode="&#xf179;" horiz-adv-x="1408" d="M1393 321q-39 -125 -123 -250q-129 -196 -257 -196q-49 0 -140 32q-86 32 -151 32q-61 0 -142 -33q-81 -34 -132 -34q-152 0 -301 259q-147 261 -147 503q0 228 113 374q112 144 284 144q72 0 177 -30q104 -30 138 -30q45 0 143 34q102 34 173 34q119 0 213 -65 q52 -36 104 -100q-79 -67 -114 -118q-65 -94 -65 -207q0 -124 69 -223t158 -126zM1017 1494q0 -61 -29 -136q-30 -75 -93 -138q-54 -54 -108 -72q-37 -11 -104 -17q3 149 78 257q74 107 250 148q1 -3 2.5 -11t2.5 -11q0 -4 0.5 -10t0.5 -10z" />
+<glyph unicode="&#xf17a;" horiz-adv-x="1664" d="M682 530v-651l-682 94v557h682zM682 1273v-659h-682v565zM1664 530v-786l-907 125v661h907zM1664 1408v-794h-907v669z" />
+<glyph unicode="&#xf17b;" horiz-adv-x="1408" d="M493 1053q16 0 27.5 11.5t11.5 27.5t-11.5 27.5t-27.5 11.5t-27 -11.5t-11 -27.5t11 -27.5t27 -11.5zM915 1053q16 0 27 11.5t11 27.5t-11 27.5t-27 11.5t-27.5 -11.5t-11.5 -27.5t11.5 -27.5t27.5 -11.5zM103 869q42 0 72 -30t30 -72v-430q0 -43 -29.5 -73t-72.5 -30 t-73 30t-30 73v430q0 42 30 72t73 30zM1163 850v-666q0 -46 -32 -78t-77 -32h-75v-227q0 -43 -30 -73t-73 -30t-73 30t-30 73v227h-138v-227q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73l-1 227h-74q-46 0 -78 32t-32 78v666h918zM931 1255q107 -55 171 -153.5t64 -215.5 h-925q0 117 64 215.5t172 153.5l-71 131q-7 13 5 20q13 6 20 -6l72 -132q95 42 201 42t201 -42l72 132q7 12 20 6q12 -7 5 -20zM1408 767v-430q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73v430q0 43 30 72.5t72 29.5q43 0 73 -29.5t30 -72.5z" />
+<glyph unicode="&#xf17c;" d="M663 1125q-11 -1 -15.5 -10.5t-8.5 -9.5q-5 -1 -5 5q0 12 19 15h10zM750 1111q-4 -1 -11.5 6.5t-17.5 4.5q24 11 32 -2q3 -6 -3 -9zM399 684q-4 1 -6 -3t-4.5 -12.5t-5.5 -13.5t-10 -13q-7 -10 -1 -12q4 -1 12.5 7t12.5 18q1 3 2 7t2 6t1.5 4.5t0.5 4v3t-1 2.5t-3 2z M1254 325q0 18 -55 42q4 15 7.5 27.5t5 26t3 21.5t0.5 22.5t-1 19.5t-3.5 22t-4 20.5t-5 25t-5.5 26.5q-10 48 -47 103t-72 75q24 -20 57 -83q87 -162 54 -278q-11 -40 -50 -42q-31 -4 -38.5 18.5t-8 83.5t-11.5 107q-9 39 -19.5 69t-19.5 45.5t-15.5 24.5t-13 15t-7.5 7 q-14 62 -31 103t-29.5 56t-23.5 33t-15 40q-4 21 6 53.5t4.5 49.5t-44.5 25q-15 3 -44.5 18t-35.5 16q-8 1 -11 26t8 51t36 27q37 3 51 -30t4 -58q-11 -19 -2 -26.5t30 -0.5q13 4 13 36v37q-5 30 -13.5 50t-21 30.5t-23.5 15t-27 7.5q-107 -8 -89 -134q0 -15 -1 -15 q-9 9 -29.5 10.5t-33 -0.5t-15.5 5q1 57 -16 90t-45 34q-27 1 -41.5 -27.5t-16.5 -59.5q-1 -15 3.5 -37t13 -37.5t15.5 -13.5q10 3 16 14q4 9 -7 8q-7 0 -15.5 14.5t-9.5 33.5q-1 22 9 37t34 14q17 0 27 -21t9.5 -39t-1.5 -22q-22 -15 -31 -29q-8 -12 -27.5 -23.5 t-20.5 -12.5q-13 -14 -15.5 -27t7.5 -18q14 -8 25 -19.5t16 -19t18.5 -13t35.5 -6.5q47 -2 102 15q2 1 23 7t34.5 10.5t29.5 13t21 17.5q9 14 20 8q5 -3 6.5 -8.5t-3 -12t-16.5 -9.5q-20 -6 -56.5 -21.5t-45.5 -19.5q-44 -19 -70 -23q-25 -5 -79 2q-10 2 -9 -2t17 -19 q25 -23 67 -22q17 1 36 7t36 14t33.5 17.5t30 17t24.5 12t17.5 2.5t8.5 -11q0 -2 -1 -4.5t-4 -5t-6 -4.5t-8.5 -5t-9 -4.5t-10 -5t-9.5 -4.5q-28 -14 -67.5 -44t-66.5 -43t-49 -1q-21 11 -63 73q-22 31 -25 22q-1 -3 -1 -10q0 -25 -15 -56.5t-29.5 -55.5t-21 -58t11.5 -63 q-23 -6 -62.5 -90t-47.5 -141q-2 -18 -1.5 -69t-5.5 -59q-8 -24 -29 -3q-32 31 -36 94q-2 28 4 56q4 19 -1 18l-4 -5q-36 -65 10 -166q5 -12 25 -28t24 -20q20 -23 104 -90.5t93 -76.5q16 -15 17.5 -38t-14 -43t-45.5 -23q8 -15 29 -44.5t28 -54t7 -70.5q46 24 7 92 q-4 8 -10.5 16t-9.5 12t-2 6q3 5 13 9.5t20 -2.5q46 -52 166 -36q133 15 177 87q23 38 34 30q12 -6 10 -52q-1 -25 -23 -92q-9 -23 -6 -37.5t24 -15.5q3 19 14.5 77t13.5 90q2 21 -6.5 73.5t-7.5 97t23 70.5q15 18 51 18q1 37 34.5 53t72.5 10.5t60 -22.5zM626 1152 q3 17 -2.5 30t-11.5 15q-9 2 -9 -7q2 -5 5 -6q10 0 7 -15q-3 -20 8 -20q3 0 3 3zM1045 955q-2 8 -6.5 11.5t-13 5t-14.5 5.5q-5 3 -9.5 8t-7 8t-5.5 6.5t-4 4t-4 -1.5q-14 -16 7 -43.5t39 -31.5q9 -1 14.5 8t3.5 20zM867 1168q0 11 -5 19.5t-11 12.5t-9 3q-14 -1 -7 -7l4 -2 q14 -4 18 -31q0 -3 8 2zM921 1401q0 2 -2.5 5t-9 7t-9.5 6q-15 15 -24 15q-9 -1 -11.5 -7.5t-1 -13t-0.5 -12.5q-1 -4 -6 -10.5t-6 -9t3 -8.5q4 -3 8 0t11 9t15 9q1 1 9 1t15 2t9 7zM1486 60q20 -12 31 -24.5t12 -24t-2.5 -22.5t-15.5 -22t-23.5 -19.5t-30 -18.5 t-31.5 -16.5t-32 -15.5t-27 -13q-38 -19 -85.5 -56t-75.5 -64q-17 -16 -68 -19.5t-89 14.5q-18 9 -29.5 23.5t-16.5 25.5t-22 19.5t-47 9.5q-44 1 -130 1q-19 0 -57 -1.5t-58 -2.5q-44 -1 -79.5 -15t-53.5 -30t-43.5 -28.5t-53.5 -11.5q-29 1 -111 31t-146 43q-19 4 -51 9.5 t-50 9t-39.5 9.5t-33.5 14.5t-17 19.5q-10 23 7 66.5t18 54.5q1 16 -4 40t-10 42.5t-4.5 36.5t10.5 27q14 12 57 14t60 12q30 18 42 35t12 51q21 -73 -32 -106q-32 -20 -83 -15q-34 3 -43 -10q-13 -15 5 -57q2 -6 8 -18t8.5 -18t4.5 -17t1 -22q0 -15 -17 -49t-14 -48 q3 -17 37 -26q20 -6 84.5 -18.5t99.5 -20.5q24 -6 74 -22t82.5 -23t55.5 -4q43 6 64.5 28t23 48t-7.5 58.5t-19 52t-20 36.5q-121 190 -169 242q-68 74 -113 40q-11 -9 -15 15q-3 16 -2 38q1 29 10 52t24 47t22 42q8 21 26.5 72t29.5 78t30 61t39 54q110 143 124 195 q-12 112 -16 310q-2 90 24 151.5t106 104.5q39 21 104 21q53 1 106 -13.5t89 -41.5q57 -42 91.5 -121.5t29.5 -147.5q-5 -95 30 -214q34 -113 133 -218q55 -59 99.5 -163t59.5 -191q8 -49 5 -84.5t-12 -55.5t-20 -22q-10 -2 -23.5 -19t-27 -35.5t-40.5 -33.5t-61 -14 q-18 1 -31.5 5t-22.5 13.5t-13.5 15.5t-11.5 20.5t-9 19.5q-22 37 -41 30t-28 -49t7 -97q20 -70 1 -195q-10 -65 18 -100.5t73 -33t85 35.5q59 49 89.5 66.5t103.5 42.5q53 18 77 36.5t18.5 34.5t-25 28.5t-51.5 23.5q-33 11 -49.5 48t-15 72.5t15.5 47.5q1 -31 8 -56.5 t14.5 -40.5t20.5 -28.5t21 -19t21.5 -13t16.5 -9.5z" />
+<glyph unicode="&#xf17d;" d="M1024 36q-42 241 -140 498h-2l-2 -1q-16 -6 -43 -16.5t-101 -49t-137 -82t-131 -114.5t-103 -148l-15 11q184 -150 418 -150q132 0 256 52zM839 643q-21 49 -53 111q-311 -93 -673 -93q-1 -7 -1 -21q0 -124 44 -236.5t124 -201.5q50 89 123.5 166.5t142.5 124.5t130.5 81 t99.5 48l37 13q4 1 13 3.5t13 4.5zM732 855q-120 213 -244 378q-138 -65 -234 -186t-128 -272q302 0 606 80zM1416 536q-210 60 -409 29q87 -239 128 -469q111 75 185 189.5t96 250.5zM611 1277q-1 0 -2 -1q1 1 2 1zM1201 1132q-185 164 -433 164q-76 0 -155 -19 q131 -170 246 -382q69 26 130 60.5t96.5 61.5t65.5 57t37.5 40.5zM1424 647q-3 232 -149 410l-1 -1q-9 -12 -19 -24.5t-43.5 -44.5t-71 -60.5t-100 -65t-131.5 -64.5q25 -53 44 -95q2 -6 6.5 -17.5t7.5 -16.5q36 5 74.5 7t73.5 2t69 -1.5t64 -4t56.5 -5.5t48 -6.5t36.5 -6 t25 -4.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf17e;" d="M1173 473q0 50 -19.5 91.5t-48.5 68.5t-73 49t-82.5 34t-87.5 23l-104 24q-30 7 -44 10.5t-35 11.5t-30 16t-16.5 21t-7.5 30q0 77 144 77q43 0 77 -12t54 -28.5t38 -33.5t40 -29t48 -12q47 0 75.5 32t28.5 77q0 55 -56 99.5t-142 67.5t-182 23q-68 0 -132 -15.5 t-119.5 -47t-89 -87t-33.5 -128.5q0 -61 19 -106.5t56 -75.5t80 -48.5t103 -32.5l146 -36q90 -22 112 -36q32 -20 32 -60q0 -39 -40 -64.5t-105 -25.5q-51 0 -91.5 16t-65 38.5t-45.5 45t-46 38.5t-54 16q-50 0 -75.5 -30t-25.5 -75q0 -92 122 -157.5t291 -65.5 q73 0 140 18.5t122.5 53.5t88.5 93.5t33 131.5zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5q-130 0 -234 80q-77 -16 -150 -16q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5q0 73 16 150q-80 104 -80 234q0 159 112.5 271.5t271.5 112.5q130 0 234 -80 q77 16 150 16q143 0 273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -73 -16 -150q80 -104 80 -234z" />
+<glyph unicode="&#xf180;" horiz-adv-x="1280" d="M1000 1102l37 194q5 23 -9 40t-35 17h-712q-23 0 -38.5 -17t-15.5 -37v-1101q0 -7 6 -1l291 352q23 26 38 33.5t48 7.5h239q22 0 37 14.5t18 29.5q24 130 37 191q4 21 -11.5 40t-36.5 19h-294q-29 0 -48 19t-19 48v42q0 29 19 47.5t48 18.5h346q18 0 35 13.5t20 29.5z M1227 1324q-15 -73 -53.5 -266.5t-69.5 -350t-35 -173.5q-6 -22 -9 -32.5t-14 -32.5t-24.5 -33t-38.5 -21t-58 -10h-271q-13 0 -22 -10q-8 -9 -426 -494q-22 -25 -58.5 -28.5t-48.5 5.5q-55 22 -55 98v1410q0 55 38 102.5t120 47.5h888q95 0 127 -53t10 -159zM1227 1324 l-158 -790q4 17 35 173.5t69.5 350t53.5 266.5z" />
+<glyph unicode="&#xf181;" d="M704 192v1024q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-1024q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1376 576v640q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-640q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408 q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf182;" horiz-adv-x="1280" d="M1280 480q0 -40 -28 -68t-68 -28q-51 0 -80 43l-227 341h-45v-132l247 -411q9 -15 9 -33q0 -26 -19 -45t-45 -19h-192v-272q0 -46 -33 -79t-79 -33h-160q-46 0 -79 33t-33 79v272h-192q-26 0 -45 19t-19 45q0 18 9 33l247 411v132h-45l-227 -341q-29 -43 -80 -43 q-40 0 -68 28t-28 68q0 29 16 53l256 384q73 107 176 107h384q103 0 176 -107l256 -384q16 -24 16 -53zM864 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
+<glyph unicode="&#xf183;" horiz-adv-x="1024" d="M1024 832v-416q0 -40 -28 -68t-68 -28t-68 28t-28 68v352h-64v-912q0 -46 -33 -79t-79 -33t-79 33t-33 79v464h-64v-464q0 -46 -33 -79t-79 -33t-79 33t-33 79v912h-64v-352q0 -40 -28 -68t-68 -28t-68 28t-28 68v416q0 80 56 136t136 56h640q80 0 136 -56t56 -136z M736 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
+<glyph unicode="&#xf184;" d="M773 234l350 473q16 22 24.5 59t-6 85t-61.5 79q-40 26 -83 25.5t-73.5 -17.5t-54.5 -45q-36 -40 -96 -40q-59 0 -95 40q-24 28 -54.5 45t-73.5 17.5t-84 -25.5q-46 -31 -60.5 -79t-6 -85t24.5 -59zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf185;" horiz-adv-x="1792" d="M1472 640q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5t-223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5t45.5 -223.5t123 -184t184 -123t223.5 -45.5t223.5 45.5t184 123t123 184t45.5 223.5zM1748 363q-4 -15 -20 -20l-292 -96v-306q0 -16 -13 -26q-15 -10 -29 -4 l-292 94l-180 -248q-10 -13 -26 -13t-26 13l-180 248l-292 -94q-14 -6 -29 4q-13 10 -13 26v306l-292 96q-16 5 -20 20q-5 17 4 29l180 248l-180 248q-9 13 -4 29q4 15 20 20l292 96v306q0 16 13 26q15 10 29 4l292 -94l180 248q9 12 26 12t26 -12l180 -248l292 94 q14 6 29 -4q13 -10 13 -26v-306l292 -96q16 -5 20 -20q5 -16 -4 -29l-180 -248l180 -248q9 -12 4 -29z" />
+<glyph unicode="&#xf186;" d="M1262 233q-54 -9 -110 -9q-182 0 -337 90t-245 245t-90 337q0 192 104 357q-201 -60 -328.5 -229t-127.5 -384q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51q144 0 273.5 61.5t220.5 171.5zM1465 318q-94 -203 -283.5 -324.5t-413.5 -121.5q-156 0 -298 61 t-245 164t-164 245t-61 298q0 153 57.5 292.5t156 241.5t235.5 164.5t290 68.5q44 2 61 -39q18 -41 -15 -72q-86 -78 -131.5 -181.5t-45.5 -218.5q0 -148 73 -273t198 -198t273 -73q118 0 228 51q41 18 72 -13q14 -14 17.5 -34t-4.5 -38z" />
+<glyph unicode="&#xf187;" horiz-adv-x="1792" d="M1088 704q0 26 -19 45t-45 19h-256q-26 0 -45 -19t-19 -45t19 -45t45 -19h256q26 0 45 19t19 45zM1664 896v-960q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v960q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1728 1344v-256q0 -26 -19 -45t-45 -19h-1536 q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1536q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf188;" horiz-adv-x="1664" d="M1632 576q0 -26 -19 -45t-45 -19h-224q0 -171 -67 -290l208 -209q19 -19 19 -45t-19 -45q-18 -19 -45 -19t-45 19l-198 197q-5 -5 -15 -13t-42 -28.5t-65 -36.5t-82 -29t-97 -13v896h-128v-896q-51 0 -101.5 13.5t-87 33t-66 39t-43.5 32.5l-15 14l-183 -207 q-20 -21 -48 -21q-24 0 -43 16q-19 18 -20.5 44.5t15.5 46.5l202 227q-58 114 -58 274h-224q-26 0 -45 19t-19 45t19 45t45 19h224v294l-173 173q-19 19 -19 45t19 45t45 19t45 -19l173 -173h844l173 173q19 19 45 19t45 -19t19 -45t-19 -45l-173 -173v-294h224q26 0 45 -19 t19 -45zM1152 1152h-640q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5z" />
+<glyph unicode="&#xf189;" horiz-adv-x="1920" d="M1917 1016q23 -64 -150 -294q-24 -32 -65 -85q-78 -100 -90 -131q-17 -41 14 -81q17 -21 81 -82h1l1 -1l1 -1l2 -2q141 -131 191 -221q3 -5 6.5 -12.5t7 -26.5t-0.5 -34t-25 -27.5t-59 -12.5l-256 -4q-24 -5 -56 5t-52 22l-20 12q-30 21 -70 64t-68.5 77.5t-61 58 t-56.5 15.5q-3 -1 -8 -3.5t-17 -14.5t-21.5 -29.5t-17 -52t-6.5 -77.5q0 -15 -3.5 -27.5t-7.5 -18.5l-4 -5q-18 -19 -53 -22h-115q-71 -4 -146 16.5t-131.5 53t-103 66t-70.5 57.5l-25 24q-10 10 -27.5 30t-71.5 91t-106 151t-122.5 211t-130.5 272q-6 16 -6 27t3 16l4 6 q15 19 57 19l274 2q12 -2 23 -6.5t16 -8.5l5 -3q16 -11 24 -32q20 -50 46 -103.5t41 -81.5l16 -29q29 -60 56 -104t48.5 -68.5t41.5 -38.5t34 -14t27 5q2 1 5 5t12 22t13.5 47t9.5 81t0 125q-2 40 -9 73t-14 46l-6 12q-25 34 -85 43q-13 2 5 24q17 19 38 30q53 26 239 24 q82 -1 135 -13q20 -5 33.5 -13.5t20.5 -24t10.5 -32t3.5 -45.5t-1 -55t-2.5 -70.5t-1.5 -82.5q0 -11 -1 -42t-0.5 -48t3.5 -40.5t11.5 -39t22.5 -24.5q8 -2 17 -4t26 11t38 34.5t52 67t68 107.5q60 104 107 225q4 10 10 17.5t11 10.5l4 3l5 2.5t13 3t20 0.5l288 2 q39 5 64 -2.5t31 -16.5z" />
+<glyph unicode="&#xf18a;" horiz-adv-x="1792" d="M675 252q21 34 11 69t-45 50q-34 14 -73 1t-60 -46q-22 -34 -13 -68.5t43 -50.5t74.5 -2.5t62.5 47.5zM769 373q8 13 3.5 26.5t-17.5 18.5q-14 5 -28.5 -0.5t-21.5 -18.5q-17 -31 13 -45q14 -5 29 0.5t22 18.5zM943 266q-45 -102 -158 -150t-224 -12 q-107 34 -147.5 126.5t6.5 187.5q47 93 151.5 139t210.5 19q111 -29 158.5 -119.5t2.5 -190.5zM1255 426q-9 96 -89 170t-208.5 109t-274.5 21q-223 -23 -369.5 -141.5t-132.5 -264.5q9 -96 89 -170t208.5 -109t274.5 -21q223 23 369.5 141.5t132.5 264.5zM1563 422 q0 -68 -37 -139.5t-109 -137t-168.5 -117.5t-226 -83t-270.5 -31t-275 33.5t-240.5 93t-171.5 151t-65 199.5q0 115 69.5 245t197.5 258q169 169 341.5 236t246.5 -7q65 -64 20 -209q-4 -14 -1 -20t10 -7t14.5 0.5t13.5 3.5l6 2q139 59 246 59t153 -61q45 -63 0 -178 q-2 -13 -4.5 -20t4.5 -12.5t12 -7.5t17 -6q57 -18 103 -47t80 -81.5t34 -116.5zM1489 1046q42 -47 54.5 -108.5t-6.5 -117.5q-8 -23 -29.5 -34t-44.5 -4q-23 8 -34 29.5t-4 44.5q20 63 -24 111t-107 35q-24 -5 -45 8t-25 37q-5 24 8 44.5t37 25.5q60 13 119 -5.5t101 -65.5z M1670 1209q87 -96 112.5 -222.5t-13.5 -241.5q-9 -27 -34 -40t-52 -4t-40 34t-5 52q28 82 10 172t-80 158q-62 69 -148 95.5t-173 8.5q-28 -6 -52 9.5t-30 43.5t9.5 51.5t43.5 29.5q123 26 244 -11.5t208 -134.5z" />
+<glyph unicode="&#xf18b;" d="M1133 -34q-171 -94 -368 -94q-196 0 -367 94q138 87 235.5 211t131.5 268q35 -144 132.5 -268t235.5 -211zM638 1394v-485q0 -252 -126.5 -459.5t-330.5 -306.5q-181 215 -181 495q0 187 83.5 349.5t229.5 269.5t325 137zM1536 638q0 -280 -181 -495 q-204 99 -330.5 306.5t-126.5 459.5v485q179 -30 325 -137t229.5 -269.5t83.5 -349.5z" />
+<glyph unicode="&#xf18c;" horiz-adv-x="1408" d="M1402 433q-32 -80 -76 -138t-91 -88.5t-99 -46.5t-101.5 -14.5t-96.5 8.5t-86.5 22t-69.5 27.5t-46 22.5l-17 10q-113 -228 -289.5 -359.5t-384.5 -132.5q-19 0 -32 13t-13 32t13 31.5t32 12.5q173 1 322.5 107.5t251.5 294.5q-36 -14 -72 -23t-83 -13t-91 2.5t-93 28.5 t-92 59t-84.5 100t-74.5 146q114 47 214 57t167.5 -7.5t124.5 -56.5t88.5 -77t56.5 -82q53 131 79 291q-7 -1 -18 -2.5t-46.5 -2.5t-69.5 0.5t-81.5 10t-88.5 23t-84 42.5t-75 65t-54.5 94.5t-28.5 127.5q70 28 133.5 36.5t112.5 -1t92 -30t73.5 -50t56 -61t42 -63t27.5 -56 t16 -39.5l4 -16q12 122 12 195q-8 6 -21.5 16t-49 44.5t-63.5 71.5t-54 93t-33 112.5t12 127t70 138.5q73 -25 127.5 -61.5t84.5 -76.5t48 -85t20.5 -89t-0.5 -85.5t-13 -76.5t-19 -62t-17 -42l-7 -15q1 -5 1 -50.5t-1 -71.5q3 7 10 18.5t30.5 43t50.5 58t71 55.5t91.5 44.5 t112 14.5t132.5 -24q-2 -78 -21.5 -141.5t-50 -104.5t-69.5 -71.5t-81.5 -45.5t-84.5 -24t-80 -9.5t-67.5 1t-46.5 4.5l-17 3q-23 -147 -73 -283q6 7 18 18.5t49.5 41t77.5 52.5t99.5 42t117.5 20t129 -23.5t137 -77.5z" />
+<glyph unicode="&#xf18d;" horiz-adv-x="1280" d="M1259 283v-66q0 -85 -57.5 -144.5t-138.5 -59.5h-57l-260 -269v269h-529q-81 0 -138.5 59.5t-57.5 144.5v66h1238zM1259 609v-255h-1238v255h1238zM1259 937v-255h-1238v255h1238zM1259 1077v-67h-1238v67q0 84 57.5 143.5t138.5 59.5h846q81 0 138.5 -59.5t57.5 -143.5z " />
+<glyph unicode="&#xf18e;" d="M1152 640q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf190;" d="M1152 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-192q0 -14 -9 -23t-23 -9q-12 0 -24 10l-319 319q-9 9 -9 23t9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h352q13 0 22.5 -9.5t9.5 -22.5zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf191;" d="M1024 960v-640q0 -26 -19 -45t-45 -19q-20 0 -37 12l-448 320q-27 19 -27 52t27 52l448 320q17 12 37 12q26 0 45 -19t19 -45zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf192;" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5 t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf193;" horiz-adv-x="1664" d="M1023 349l102 -204q-58 -179 -210 -290t-339 -111q-156 0 -288.5 77.5t-210 210t-77.5 288.5q0 181 104.5 330t274.5 211l17 -131q-122 -54 -195 -165.5t-73 -244.5q0 -185 131.5 -316.5t316.5 -131.5q126 0 232.5 65t165 175.5t49.5 236.5zM1571 249l58 -114l-256 -128 q-13 -7 -29 -7q-40 0 -57 35l-239 477h-472q-24 0 -42.5 16.5t-21.5 40.5l-96 779q-2 16 6 42q14 51 57 82.5t97 31.5q66 0 113 -47t47 -113q0 -69 -52 -117.5t-120 -41.5l37 -289h423v-128h-407l16 -128h455q40 0 57 -35l228 -455z" />
+<glyph unicode="&#xf194;" d="M1292 898q10 216 -161 222q-231 8 -312 -261q44 19 82 19q85 0 74 -96q-4 -57 -74 -167t-105 -110q-43 0 -82 169q-13 54 -45 255q-30 189 -160 177q-59 -7 -164 -100l-81 -72l-81 -72l52 -67q76 52 87 52q57 0 107 -179q15 -55 45 -164.5t45 -164.5q68 -179 164 -179 q157 0 383 294q220 283 226 444zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf195;" horiz-adv-x="1152" d="M1152 704q0 -191 -94.5 -353t-256.5 -256.5t-353 -94.5h-160q-14 0 -23 9t-9 23v611l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v93l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v250q0 14 9 23t23 9h160 q14 0 23 -9t9 -23v-181l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-93l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-487q188 13 318 151t130 328q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf196;" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-352v-352q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v352h-352q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h352v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-352h352q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832 q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf197;" horiz-adv-x="2176" d="M620 416q-110 -64 -268 -64h-128v64h-64q-13 0 -22.5 23.5t-9.5 56.5q0 24 7 49q-58 2 -96.5 10.5t-38.5 20.5t38.5 20.5t96.5 10.5q-7 25 -7 49q0 33 9.5 56.5t22.5 23.5h64v64h128q158 0 268 -64h1113q42 -7 106.5 -18t80.5 -14q89 -15 150 -40.5t83.5 -47.5t22.5 -40 t-22.5 -40t-83.5 -47.5t-150 -40.5q-16 -3 -80.5 -14t-106.5 -18h-1113zM1739 668q53 -36 53 -92t-53 -92l81 -30q68 48 68 122t-68 122zM625 400h1015q-217 -38 -456 -80q-57 0 -113 -24t-83 -48l-28 -24l-288 -288q-26 -26 -70.5 -45t-89.5 -19h-96l-93 464h29 q157 0 273 64zM352 816h-29l93 464h96q46 0 90 -19t70 -45l288 -288q4 -4 11 -10.5t30.5 -23t48.5 -29t61.5 -23t72.5 -10.5l456 -80h-1015q-116 64 -273 64z" />
+<glyph unicode="&#xf198;" horiz-adv-x="1664" d="M1519 760q62 0 103.5 -40.5t41.5 -101.5q0 -97 -93 -130l-172 -59l56 -167q7 -21 7 -47q0 -59 -42 -102t-101 -43q-47 0 -85.5 27t-53.5 72l-55 165l-310 -106l55 -164q8 -24 8 -47q0 -59 -42 -102t-102 -43q-47 0 -85 27t-53 72l-55 163l-153 -53q-29 -9 -50 -9 q-61 0 -101.5 40t-40.5 101q0 47 27.5 85t71.5 53l156 53l-105 313l-156 -54q-26 -8 -48 -8q-60 0 -101 40.5t-41 100.5q0 47 27.5 85t71.5 53l157 53l-53 159q-8 24 -8 47q0 60 42 102.5t102 42.5q47 0 85 -27t53 -72l54 -160l310 105l-54 160q-8 24 -8 47q0 59 42.5 102 t101.5 43q47 0 85.5 -27.5t53.5 -71.5l53 -161l162 55q21 6 43 6q60 0 102.5 -39.5t42.5 -98.5q0 -45 -30 -81.5t-74 -51.5l-157 -54l105 -316l164 56q24 8 46 8zM725 498l310 105l-105 315l-310 -107z" />
+<glyph unicode="&#xf199;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM1280 352v436q-31 -35 -64 -55q-34 -22 -132.5 -85t-151.5 -99q-98 -69 -164 -69v0v0q-66 0 -164 69 q-46 32 -141.5 92.5t-142.5 92.5q-12 8 -33 27t-31 27v-436q0 -40 28 -68t68 -28h832q40 0 68 28t28 68zM1280 925q0 41 -27.5 70t-68.5 29h-832q-40 0 -68 -28t-28 -68q0 -37 30.5 -76.5t67.5 -64.5q47 -32 137.5 -89t129.5 -83q3 -2 17 -11.5t21 -14t21 -13t23.5 -13 t21.5 -9.5t22.5 -7.5t20.5 -2.5t20.5 2.5t22.5 7.5t21.5 9.5t23.5 13t21 13t21 14t17 11.5l267 174q35 23 66.5 62.5t31.5 73.5z" />
+<glyph unicode="&#xf19a;" horiz-adv-x="1792" d="M127 640q0 163 67 313l367 -1005q-196 95 -315 281t-119 411zM1415 679q0 -19 -2.5 -38.5t-10 -49.5t-11.5 -44t-17.5 -59t-17.5 -58l-76 -256l-278 826q46 3 88 8q19 2 26 18.5t-2.5 31t-28.5 13.5l-205 -10q-75 1 -202 10q-12 1 -20.5 -5t-11.5 -15t-1.5 -18.5t9 -16.5 t19.5 -8l80 -8l120 -328l-168 -504l-280 832q46 3 88 8q19 2 26 18.5t-2.5 31t-28.5 13.5l-205 -10q-7 0 -23 0.5t-26 0.5q105 160 274.5 253.5t367.5 93.5q147 0 280.5 -53t238.5 -149h-10q-55 0 -92 -40.5t-37 -95.5q0 -12 2 -24t4 -21.5t8 -23t9 -21t12 -22.5t12.5 -21 t14.5 -24t14 -23q63 -107 63 -212zM909 573l237 -647q1 -6 5 -11q-126 -44 -255 -44q-112 0 -217 32zM1570 1009q95 -174 95 -369q0 -209 -104 -385.5t-279 -278.5l235 678q59 169 59 276q0 42 -6 79zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286 t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 -215q173 0 331.5 68t273 182.5t182.5 273t68 331.5t-68 331.5t-182.5 273t-273 182.5t-331.5 68t-331.5 -68t-273 -182.5t-182.5 -273t-68 -331.5t68 -331.5t182.5 -273 t273 -182.5t331.5 -68z" />
+<glyph unicode="&#xf19b;" horiz-adv-x="1792" d="M1086 1536v-1536l-272 -128q-228 20 -414 102t-293 208.5t-107 272.5q0 140 100.5 263.5t275 205.5t391.5 108v-172q-217 -38 -356.5 -150t-139.5 -255q0 -152 154.5 -267t388.5 -145v1360zM1755 954l37 -390l-525 114l147 83q-119 70 -280 99v172q277 -33 481 -157z" />
+<glyph unicode="&#xf19c;" horiz-adv-x="2048" d="M960 1536l960 -384v-128h-128q0 -26 -20.5 -45t-48.5 -19h-1526q-28 0 -48.5 19t-20.5 45h-128v128zM256 896h256v-768h128v768h256v-768h128v768h256v-768h128v768h256v-768h59q28 0 48.5 -19t20.5 -45v-64h-1664v64q0 26 20.5 45t48.5 19h59v768zM1851 -64 q28 0 48.5 -19t20.5 -45v-128h-1920v128q0 26 20.5 45t48.5 19h1782z" />
+<glyph unicode="&#xf19d;" horiz-adv-x="2304" d="M1774 700l18 -316q4 -69 -82 -128t-235 -93.5t-323 -34.5t-323 34.5t-235 93.5t-82 128l18 316l574 -181q22 -7 48 -7t48 7zM2304 1024q0 -23 -22 -31l-1120 -352q-4 -1 -10 -1t-10 1l-652 206q-43 -34 -71 -111.5t-34 -178.5q63 -36 63 -109q0 -69 -58 -107l58 -433 q2 -14 -8 -25q-9 -11 -24 -11h-192q-15 0 -24 11q-10 11 -8 25l58 433q-58 38 -58 107q0 73 65 111q11 207 98 330l-333 104q-22 8 -22 31t22 31l1120 352q4 1 10 1t10 -1l1120 -352q22 -8 22 -31z" />
+<glyph unicode="&#xf19e;" d="M859 579l13 -707q-62 11 -105 11q-41 0 -105 -11l13 707q-40 69 -168.5 295.5t-216.5 374.5t-181 287q58 -15 108 -15q43 0 111 15q63 -111 133.5 -229.5t167 -276.5t138.5 -227q37 61 109.5 177.5t117.5 190t105 176t107 189.5q54 -14 107 -14q56 0 114 14v0 q-28 -39 -60 -88.5t-49.5 -78.5t-56.5 -96t-49 -84q-146 -248 -353 -610z" />
+<glyph unicode="&#xf1a0;" d="M768 750h725q12 -67 12 -128q0 -217 -91 -387.5t-259.5 -266.5t-386.5 -96q-157 0 -299 60.5t-245 163.5t-163.5 245t-60.5 299t60.5 299t163.5 245t245 163.5t299 60.5q300 0 515 -201l-209 -201q-123 119 -306 119q-129 0 -238.5 -65t-173.5 -176.5t-64 -243.5 t64 -243.5t173.5 -176.5t238.5 -65q87 0 160 24t120 60t82 82t51.5 87t22.5 78h-436v264z" />
+<glyph unicode="&#xf1a1;" horiz-adv-x="1792" d="M1095 369q16 -16 0 -31q-62 -62 -199 -62t-199 62q-16 15 0 31q6 6 15 6t15 -6q48 -49 169 -49q120 0 169 49q6 6 15 6t15 -6zM788 550q0 -37 -26 -63t-63 -26t-63.5 26t-26.5 63q0 38 26.5 64t63.5 26t63 -26.5t26 -63.5zM1183 550q0 -37 -26.5 -63t-63.5 -26t-63 26 t-26 63t26 63.5t63 26.5t63.5 -26t26.5 -64zM1434 670q0 49 -35 84t-85 35t-86 -36q-130 90 -311 96l63 283l200 -45q0 -37 26 -63t63 -26t63.5 26.5t26.5 63.5t-26.5 63.5t-63.5 26.5q-54 0 -80 -50l-221 49q-19 5 -25 -16l-69 -312q-180 -7 -309 -97q-35 37 -87 37 q-50 0 -85 -35t-35 -84q0 -35 18.5 -64t49.5 -44q-6 -27 -6 -56q0 -142 140 -243t337 -101q198 0 338 101t140 243q0 32 -7 57q30 15 48 43.5t18 63.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191 t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf1a2;" d="M939 407q13 -13 0 -26q-53 -53 -171 -53t-171 53q-13 13 0 26q5 6 13 6t13 -6q42 -42 145 -42t145 42q5 6 13 6t13 -6zM676 563q0 -31 -23 -54t-54 -23t-54 23t-23 54q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1014 563q0 -31 -23 -54t-54 -23t-54 23t-23 54 q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1229 666q0 42 -30 72t-73 30q-42 0 -73 -31q-113 78 -267 82l54 243l171 -39q1 -32 23.5 -54t53.5 -22q32 0 54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5q-48 0 -69 -43l-189 42q-17 5 -21 -13l-60 -268q-154 -6 -265 -83 q-30 32 -74 32q-43 0 -73 -30t-30 -72q0 -30 16 -55t42 -38q-5 -25 -5 -48q0 -122 120 -208.5t289 -86.5q170 0 290 86.5t120 208.5q0 25 -6 49q25 13 40.5 37.5t15.5 54.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf1a3;" d="M866 697l90 27v62q0 79 -58 135t-138 56t-138 -55.5t-58 -134.5v-283q0 -20 -14 -33.5t-33 -13.5t-32.5 13.5t-13.5 33.5v120h-151v-122q0 -82 57.5 -139t139.5 -57q81 0 138.5 56.5t57.5 136.5v280q0 19 13.5 33t33.5 14q19 0 32.5 -14t13.5 -33v-54zM1199 502v122h-150 v-126q0 -20 -13.5 -33.5t-33.5 -13.5q-19 0 -32.5 14t-13.5 33v123l-90 -26l-60 28v-123q0 -80 58 -137t139 -57t138.5 57t57.5 139zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103 t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf1a4;" horiz-adv-x="1920" d="M1062 824v118q0 42 -30 72t-72 30t-72 -30t-30 -72v-612q0 -175 -126 -299t-303 -124q-178 0 -303.5 125.5t-125.5 303.5v266h328v-262q0 -43 30 -72.5t72 -29.5t72 29.5t30 72.5v620q0 171 126.5 292t301.5 121q176 0 302 -122t126 -294v-136l-195 -58zM1592 602h328 v-266q0 -178 -125.5 -303.5t-303.5 -125.5q-177 0 -303 124.5t-126 300.5v268l131 -61l195 58v-270q0 -42 30 -71.5t72 -29.5t72 29.5t30 71.5v275z" />
+<glyph unicode="&#xf1a5;" d="M1472 160v480h-704v704h-480q-93 0 -158.5 -65.5t-65.5 -158.5v-480h704v-704h480q93 0 158.5 65.5t65.5 158.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
+<glyph unicode="&#xf1a6;" horiz-adv-x="2048" d="M328 1254h204v-983h-532v697h328v286zM328 435v369h-123v-369h123zM614 968v-697h205v697h-205zM614 1254v-204h205v204h-205zM901 968h533v-942h-533v163h328v82h-328v697zM1229 435v369h-123v-369h123zM1516 968h532v-942h-532v163h327v82h-327v697zM1843 435v369h-123 v-369h123z" />
+<glyph unicode="&#xf1a7;" d="M1046 516q0 -64 -38 -109t-91 -45q-43 0 -70 15v277q28 17 70 17q53 0 91 -45.5t38 -109.5zM703 944q0 -64 -38 -109.5t-91 -45.5q-43 0 -70 15v277q28 17 70 17q53 0 91 -45t38 -109zM1265 513q0 134 -88 229t-213 95q-20 0 -39 -3q-23 -78 -78 -136q-87 -95 -211 -101 v-636l211 41v206q51 -19 117 -19q125 0 213 95t88 229zM922 940q0 134 -88.5 229t-213.5 95q-74 0 -141 -36h-186v-840l211 41v206q55 -19 116 -19q125 0 213.5 95t88.5 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf1a8;" horiz-adv-x="2038" d="M1222 607q75 3 143.5 -20.5t118 -58.5t101 -94.5t84 -108t75.5 -120.5q33 -56 78.5 -109t75.5 -80.5t99 -88.5q-48 -30 -108.5 -57.5t-138.5 -59t-114 -47.5q-44 37 -74 115t-43.5 164.5t-33 180.5t-42.5 168.5t-72.5 123t-122.5 48.5l-10 -2l-6 -4q4 -5 13 -14 q6 -5 28 -23.5t25.5 -22t19 -18t18 -20.5t11.5 -21t10.5 -27.5t4.5 -31t4 -40.5l1 -33q1 -26 -2.5 -57.5t-7.5 -52t-12.5 -58.5t-11.5 -53q-35 1 -101 -9.5t-98 -10.5q-39 0 -72 10q-2 16 -2 47q0 74 3 96q2 13 31.5 41.5t57 59t26.5 51.5q-24 2 -43 -24 q-36 -53 -111.5 -99.5t-136.5 -46.5q-25 0 -75.5 63t-106.5 139.5t-84 96.5q-6 4 -27 30q-482 -112 -513 -112q-16 0 -28 11t-12 27q0 15 8.5 26.5t22.5 14.5l486 106q-8 14 -8 25t5.5 17.5t16 11.5t20 7t23 4.5t18.5 4.5q4 1 15.5 7.5t17.5 6.5q15 0 28 -16t20 -33 q163 37 172 37q17 0 29.5 -11t12.5 -28q0 -15 -8.5 -26t-23.5 -14l-182 -40l-1 -16q-1 -26 81.5 -117.5t104.5 -91.5q47 0 119 80t72 129q0 36 -23.5 53t-51 18.5t-51 11.5t-23.5 34q0 16 10 34l-68 19q43 44 43 117q0 26 -5 58q82 16 144 16q44 0 71.5 -1.5t48.5 -8.5 t31 -13.5t20.5 -24.5t15.5 -33.5t17 -47.5t24 -60l50 25q-3 -40 -23 -60t-42.5 -21t-40 -6.5t-16.5 -20.5zM1282 842q-5 5 -13.5 15.5t-12 14.5t-10.5 11.5t-10 10.5l-8 8t-8.5 7.5t-8 5t-8.5 4.5q-7 3 -14.5 5t-20.5 2.5t-22 0.5h-32.5h-37.5q-126 0 -217 -43 q16 30 36 46.5t54 29.5t65.5 36t46 36.5t50 55t43.5 50.5q12 -9 28 -31.5t32 -36.5t38 -13l12 1v-76l22 -1q247 95 371 190q28 21 50 39t42.5 37.5t33 31t29.5 34t24 31t24.5 37t23 38t27 47.5t29.5 53l7 9q-2 -53 -43 -139q-79 -165 -205 -264t-306 -142q-14 -3 -42 -7.5 t-50 -9.5t-39 -14q3 -19 24.5 -46t21.5 -34q0 -11 -26 -30zM1061 -79q39 26 131.5 47.5t146.5 21.5q9 0 22.5 -15.5t28 -42.5t26 -50t24 -51t14.5 -33q-121 -45 -244 -45q-61 0 -125 11zM822 568l48 12l109 -177l-73 -48zM1323 51q3 -15 3 -16q0 -7 -17.5 -14.5t-46 -13 t-54 -9.5t-53.5 -7.5t-32 -4.5l-7 43q21 2 60.5 8.5t72 10t60.5 3.5h14zM866 679l-96 -20l-6 17q10 1 32.5 7t34.5 6q19 0 35 -10zM1061 45h31l10 -83l-41 -12v95zM1950 1535v1v-1zM1950 1535l-1 -5l-2 -2l1 3zM1950 1535l1 1z" />
+<glyph unicode="&#xf1a9;" d="M1167 -50q-5 19 -24 5q-30 -22 -87 -39t-131 -17q-129 0 -193 49q-5 4 -13 4q-11 0 -26 -12q-7 -6 -7.5 -16t7.5 -20q34 -32 87.5 -46t102.5 -12.5t99 4.5q41 4 84.5 20.5t65 30t28.5 20.5q12 12 7 29zM1128 65q-19 47 -39 61q-23 15 -76 15q-47 0 -71 -10 q-29 -12 -78 -56q-26 -24 -12 -44q9 -8 17.5 -4.5t31.5 23.5q3 2 10.5 8.5t10.5 8.5t10 7t11.5 7t12.5 5t15 4.5t16.5 2.5t20.5 1q27 0 44.5 -7.5t23 -14.5t13.5 -22q10 -17 12.5 -20t12.5 1q23 12 14 34zM1483 346q0 22 -5 44.5t-16.5 45t-34 36.5t-52.5 14 q-33 0 -97 -41.5t-129 -83.5t-101 -42q-27 -1 -63.5 19t-76 49t-83.5 58t-100 49t-111 19q-115 -1 -197 -78.5t-84 -178.5q-2 -112 74 -164q29 -20 62.5 -28.5t103.5 -8.5q57 0 132 32.5t134 71t120 70.5t93 31q26 -1 65 -31.5t71.5 -67t68 -67.5t55.5 -32q35 -3 58.5 14 t55.5 63q28 41 42.5 101t14.5 106zM1536 506q0 -164 -62 -304.5t-166 -236t-242.5 -149.5t-290.5 -54t-293 57.5t-247.5 157t-170.5 241.5t-64 302q0 89 19.5 172.5t49 145.5t70.5 118.5t78.5 94t78.5 69.5t64.5 46.5t42.5 24.5q14 8 51 26.5t54.5 28.5t48 30t60.5 44 q36 28 58 72.5t30 125.5q129 -155 186 -193q44 -29 130 -68t129 -66q21 -13 39 -25t60.5 -46.5t76 -70.5t75 -95t69 -122t47 -148.5t19.5 -177.5z" />
+<glyph unicode="&#xf1aa;" d="M1070 463l-160 -160l-151 -152l-30 -30q-65 -64 -151.5 -87t-171.5 -2q-16 -70 -72 -115t-129 -45q-85 0 -145 60.5t-60 145.5q0 72 44.5 128t113.5 72q-22 86 1 173t88 152l12 12l151 -152l-11 -11q-37 -37 -37 -89t37 -90q37 -37 89 -37t89 37l30 30l151 152l161 160z M729 1145l12 -12l-152 -152l-12 12q-37 37 -89 37t-89 -37t-37 -89.5t37 -89.5l29 -29l152 -152l160 -160l-151 -152l-161 160l-151 152l-30 30q-68 67 -90 159.5t5 179.5q-70 15 -115 71t-45 129q0 85 60 145.5t145 60.5q76 0 133.5 -49t69.5 -123q84 20 169.5 -3.5 t149.5 -87.5zM1536 78q0 -85 -60 -145.5t-145 -60.5q-74 0 -131 47t-71 118q-86 -28 -179.5 -6t-161.5 90l-11 12l151 152l12 -12q37 -37 89 -37t89 37t37 89t-37 89l-30 30l-152 152l-160 160l152 152l160 -160l152 -152l29 -30q64 -64 87.5 -150.5t2.5 -171.5 q76 -11 126.5 -68.5t50.5 -134.5zM1534 1202q0 -77 -51 -135t-127 -69q26 -85 3 -176.5t-90 -158.5l-12 -12l-151 152l12 12q37 37 37 89t-37 89t-89 37t-89 -37l-30 -30l-152 -152l-160 -160l-152 152l161 160l152 152l29 30q67 67 159 89.5t178 -3.5q11 75 68.5 126 t135.5 51q85 0 145 -60.5t60 -145.5z" />
+<glyph unicode="&#xf1ab;" d="M654 458q-1 -3 -12.5 0.5t-31.5 11.5l-20 9q-44 20 -87 49q-7 5 -41 31.5t-38 28.5q-67 -103 -134 -181q-81 -95 -105 -110q-4 -2 -19.5 -4t-18.5 0q6 4 82 92q21 24 85.5 115t78.5 118q17 30 51 98.5t36 77.5q-8 1 -110 -33q-8 -2 -27.5 -7.5t-34.5 -9.5t-17 -5 q-2 -2 -2 -10.5t-1 -9.5q-5 -10 -31 -15q-23 -7 -47 0q-18 4 -28 21q-4 6 -5 23q6 2 24.5 5t29.5 6q58 16 105 32q100 35 102 35q10 2 43 19.5t44 21.5q9 3 21.5 8t14.5 5.5t6 -0.5q2 -12 -1 -33q0 -2 -12.5 -27t-26.5 -53.5t-17 -33.5q-25 -50 -77 -131l64 -28 q12 -6 74.5 -32t67.5 -28q4 -1 10.5 -25.5t4.5 -30.5zM449 944q3 -15 -4 -28q-12 -23 -50 -38q-30 -12 -60 -12q-26 3 -49 26q-14 15 -18 41l1 3q3 -3 19.5 -5t26.5 0t58 16q36 12 55 14q17 0 21 -17zM1147 815l63 -227l-139 42zM39 15l694 232v1032l-694 -233v-1031z M1280 332l102 -31l-181 657l-100 31l-216 -536l102 -31l45 110l211 -65zM777 1294l573 -184v380zM1088 -29l158 -13l-54 -160l-40 66q-130 -83 -276 -108q-58 -12 -91 -12h-84q-79 0 -199.5 39t-183.5 85q-8 7 -8 16q0 8 5 13.5t13 5.5q4 0 18 -7.5t30.5 -16.5t20.5 -11 q73 -37 159.5 -61.5t157.5 -24.5q95 0 167 14.5t157 50.5q15 7 30.5 15.5t34 19t28.5 16.5zM1536 1050v-1079l-774 246q-14 -6 -375 -127.5t-368 -121.5q-13 0 -18 13q0 1 -1 3v1078q3 9 4 10q5 6 20 11q106 35 149 50v384l558 -198q2 0 160.5 55t316 108.5t161.5 53.5 q20 0 20 -21v-418z" />
+<glyph unicode="&#xf1ac;" horiz-adv-x="1792" d="M288 1152q66 0 113 -47t47 -113v-1088q0 -66 -47 -113t-113 -47h-128q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h128zM1664 989q58 -34 93 -93t35 -128v-768q0 -106 -75 -181t-181 -75h-864q-66 0 -113 47t-47 113v1536q0 40 28 68t68 28h672q40 0 88 -20t76 -48 l152 -152q28 -28 48 -76t20 -88v-163zM928 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM928 256v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM928 512v128q0 14 -9 23 t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1184 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1184 256v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128 q14 0 23 9t9 23zM1184 512v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 256v128q0 14 -9 23t-23 9h-128 q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 512v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1536 896v256h-160q-40 0 -68 28t-28 68v160h-640v-512h896z" />
+<glyph unicode="&#xf1ad;" d="M1344 1536q26 0 45 -19t19 -45v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280zM512 1248v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 992v-64q0 -14 9 -23t23 -9h64q14 0 23 9 t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 736v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 480v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 160v64 q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64 q14 0 23 9t9 23zM384 928v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 -96v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9 t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM896 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 928v64 q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 160v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64 q14 0 23 9t9 23zM1152 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 928v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9 t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23z" />
+<glyph unicode="&#xf1ae;" horiz-adv-x="1280" d="M1188 988l-292 -292v-824q0 -46 -33 -79t-79 -33t-79 33t-33 79v384h-64v-384q0 -46 -33 -79t-79 -33t-79 33t-33 79v824l-292 292q-28 28 -28 68t28 68t68 28t68 -28l228 -228h368l228 228q28 28 68 28t68 -28t28 -68t-28 -68zM864 1152q0 -93 -65.5 -158.5 t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
+<glyph unicode="&#xf1b0;" horiz-adv-x="1664" d="M780 1064q0 -60 -19 -113.5t-63 -92.5t-105 -39q-76 0 -138 57.5t-92 135.5t-30 151q0 60 19 113.5t63 92.5t105 39q77 0 138.5 -57.5t91.5 -135t30 -151.5zM438 581q0 -80 -42 -139t-119 -59q-76 0 -141.5 55.5t-100.5 133.5t-35 152q0 80 42 139.5t119 59.5 q76 0 141.5 -55.5t100.5 -134t35 -152.5zM832 608q118 0 255 -97.5t229 -237t92 -254.5q0 -46 -17 -76.5t-48.5 -45t-64.5 -20t-76 -5.5q-68 0 -187.5 45t-182.5 45q-66 0 -192.5 -44.5t-200.5 -44.5q-183 0 -183 146q0 86 56 191.5t139.5 192.5t187.5 146t193 59zM1071 819 q-61 0 -105 39t-63 92.5t-19 113.5q0 74 30 151.5t91.5 135t138.5 57.5q61 0 105 -39t63 -92.5t19 -113.5q0 -73 -30 -151t-92 -135.5t-138 -57.5zM1503 923q77 0 119 -59.5t42 -139.5q0 -74 -35 -152t-100.5 -133.5t-141.5 -55.5q-77 0 -119 59t-42 139q0 74 35 152.5 t100.5 134t141.5 55.5z" />
+<glyph unicode="&#xf1b1;" horiz-adv-x="768" d="M704 1008q0 -145 -57 -243.5t-152 -135.5l45 -821q2 -26 -16 -45t-44 -19h-192q-26 0 -44 19t-16 45l45 821q-95 37 -152 135.5t-57 243.5q0 128 42.5 249.5t117.5 200t160 78.5t160 -78.5t117.5 -200t42.5 -249.5z" />
+<glyph unicode="&#xf1b2;" horiz-adv-x="1792" d="M896 -93l640 349v636l-640 -233v-752zM832 772l698 254l-698 254l-698 -254zM1664 1024v-768q0 -35 -18 -65t-49 -47l-704 -384q-28 -16 -61 -16t-61 16l-704 384q-31 17 -49 47t-18 65v768q0 40 23 73t61 47l704 256q22 8 44 8t44 -8l704 -256q38 -14 61 -47t23 -73z " />
+<glyph unicode="&#xf1b3;" horiz-adv-x="2304" d="M640 -96l384 192v314l-384 -164v-342zM576 358l404 173l-404 173l-404 -173zM1664 -96l384 192v314l-384 -164v-342zM1600 358l404 173l-404 173l-404 -173zM1152 651l384 165v266l-384 -164v-267zM1088 1030l441 189l-441 189l-441 -189zM2176 512v-416q0 -36 -19 -67 t-52 -47l-448 -224q-25 -14 -57 -14t-57 14l-448 224q-5 2 -7 4q-2 -2 -7 -4l-448 -224q-25 -14 -57 -14t-57 14l-448 224q-33 16 -52 47t-19 67v416q0 38 21.5 70t56.5 48l434 186v400q0 38 21.5 70t56.5 48l448 192q23 10 50 10t50 -10l448 -192q35 -16 56.5 -48t21.5 -70 v-400l434 -186q36 -16 57 -48t21 -70z" />
+<glyph unicode="&#xf1b4;" horiz-adv-x="2048" d="M1848 1197h-511v-124h511v124zM1596 771q-90 0 -146 -52.5t-62 -142.5h408q-18 195 -200 195zM1612 186q63 0 122 32t76 87h221q-100 -307 -427 -307q-214 0 -340.5 132t-126.5 347q0 208 130.5 345.5t336.5 137.5q138 0 240.5 -68t153 -179t50.5 -248q0 -17 -2 -47h-658 q0 -111 57.5 -171.5t166.5 -60.5zM277 236h296q205 0 205 167q0 180 -199 180h-302v-347zM277 773h281q78 0 123.5 36.5t45.5 113.5q0 144 -190 144h-260v-294zM0 1282h594q87 0 155 -14t126.5 -47.5t90 -96.5t31.5 -154q0 -181 -172 -263q114 -32 172 -115t58 -204 q0 -75 -24.5 -136.5t-66 -103.5t-98.5 -71t-121 -42t-134 -13h-611v1260z" />
+<glyph unicode="&#xf1b5;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM499 1041h-371v-787h382q117 0 197 57.5t80 170.5q0 158 -143 200q107 52 107 164q0 57 -19.5 96.5 t-56.5 60.5t-79 29.5t-97 8.5zM477 723h-176v184h163q119 0 119 -90q0 -94 -106 -94zM486 388h-185v217h189q124 0 124 -113q0 -104 -128 -104zM1136 356q-68 0 -104 38t-36 107h411q1 10 1 30q0 132 -74.5 220.5t-203.5 88.5q-128 0 -210 -86t-82 -216q0 -135 79 -217 t213 -82q205 0 267 191h-138q-11 -34 -47.5 -54t-75.5 -20zM1126 722q113 0 124 -122h-254q4 56 39 89t91 33zM964 988h319v-77h-319v77z" />
+<glyph unicode="&#xf1b6;" horiz-adv-x="1792" d="M1582 954q0 -101 -71.5 -172.5t-172.5 -71.5t-172.5 71.5t-71.5 172.5t71.5 172.5t172.5 71.5t172.5 -71.5t71.5 -172.5zM812 212q0 104 -73 177t-177 73q-27 0 -54 -6l104 -42q77 -31 109.5 -106.5t1.5 -151.5q-31 -77 -107 -109t-152 -1q-21 8 -62 24.5t-61 24.5 q32 -60 91 -96.5t130 -36.5q104 0 177 73t73 177zM1642 953q0 126 -89.5 215.5t-215.5 89.5q-127 0 -216.5 -89.5t-89.5 -215.5q0 -127 89.5 -216t216.5 -89q126 0 215.5 89t89.5 216zM1792 953q0 -189 -133.5 -322t-321.5 -133l-437 -319q-12 -129 -109 -218t-229 -89 q-121 0 -214 76t-118 192l-230 92v429l389 -157q79 48 173 48q13 0 35 -2l284 407q2 187 135.5 319t320.5 132q188 0 321.5 -133.5t133.5 -321.5z" />
+<glyph unicode="&#xf1b7;" d="M1242 889q0 80 -57 136.5t-137 56.5t-136.5 -57t-56.5 -136q0 -80 56.5 -136.5t136.5 -56.5t137 56.5t57 136.5zM632 301q0 -83 -58 -140.5t-140 -57.5q-56 0 -103 29t-72 77q52 -20 98 -40q60 -24 120 1.5t85 86.5q24 60 -1.5 120t-86.5 84l-82 33q22 5 42 5 q82 0 140 -57.5t58 -140.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v153l172 -69q20 -92 93.5 -152t168.5 -60q104 0 181 70t87 173l345 252q150 0 255.5 105.5t105.5 254.5q0 150 -105.5 255.5t-255.5 105.5 q-148 0 -253 -104.5t-107 -252.5l-225 -322q-9 1 -28 1q-75 0 -137 -37l-297 119v468q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5zM1289 887q0 -100 -71 -170.5t-171 -70.5t-170.5 70.5t-70.5 170.5t70.5 171t170.5 71q101 0 171.5 -70.5t70.5 -171.5z " />
+<glyph unicode="&#xf1b8;" horiz-adv-x="1792" d="M836 367l-15 -368l-2 -22l-420 29q-36 3 -67 31.5t-47 65.5q-11 27 -14.5 55t4 65t12 55t21.5 64t19 53q78 -12 509 -28zM449 953l180 -379l-147 92q-63 -72 -111.5 -144.5t-72.5 -125t-39.5 -94.5t-18.5 -63l-4 -21l-190 357q-17 26 -18 56t6 47l8 18q35 63 114 188 l-140 86zM1680 436l-188 -359q-12 -29 -36.5 -46.5t-43.5 -20.5l-18 -4q-71 -7 -219 -12l8 -164l-230 367l211 362l7 -173q170 -16 283 -5t170 33zM895 1360q-47 -63 -265 -435l-317 187l-19 12l225 356q20 31 60 45t80 10q24 -2 48.5 -12t42 -21t41.5 -33t36 -34.5 t36 -39.5t32 -35zM1550 1053l212 -363q18 -37 12.5 -76t-27.5 -74q-13 -20 -33 -37t-38 -28t-48.5 -22t-47 -16t-51.5 -14t-46 -12q-34 72 -265 436l313 195zM1407 1279l142 83l-220 -373l-419 20l151 86q-34 89 -75 166t-75.5 123.5t-64.5 80t-47 46.5l-17 13l405 -1 q31 3 58 -10.5t39 -28.5l11 -15q39 -61 112 -190z" />
+<glyph unicode="&#xf1b9;" horiz-adv-x="2048" d="M480 448q0 66 -47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47t113 47t47 113zM516 768h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5zM1888 448q0 66 -47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47t113 47t47 113zM2048 544v-384 q0 -14 -9 -23t-23 -9h-96v-128q0 -80 -56 -136t-136 -56t-136 56t-56 136v128h-1024v-128q0 -80 -56 -136t-136 -56t-136 56t-56 136v128h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5t179 63.5h768q98 0 179 -63.5t104 -157.5 l105 -419h28q93 0 158.5 -65.5t65.5 -158.5z" />
+<glyph unicode="&#xf1ba;" horiz-adv-x="2048" d="M1824 640q93 0 158.5 -65.5t65.5 -158.5v-384q0 -14 -9 -23t-23 -9h-96v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-1024v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5 t179 63.5h128v224q0 14 9 23t23 9h448q14 0 23 -9t9 -23v-224h128q98 0 179 -63.5t104 -157.5l105 -419h28zM320 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM516 640h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5z M1728 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47z" />
+<glyph unicode="&#xf1bb;" d="M1504 64q0 -26 -19 -45t-45 -19h-462q1 -17 6 -87.5t5 -108.5q0 -25 -18 -42.5t-43 -17.5h-320q-25 0 -43 17.5t-18 42.5q0 38 5 108.5t6 87.5h-462q-26 0 -45 19t-19 45t19 45l402 403h-229q-26 0 -45 19t-19 45t19 45l402 403h-197q-26 0 -45 19t-19 45t19 45l384 384 q19 19 45 19t45 -19l384 -384q19 -19 19 -45t-19 -45t-45 -19h-197l402 -403q19 -19 19 -45t-19 -45t-45 -19h-229l402 -403q19 -19 19 -45z" />
+<glyph unicode="&#xf1bc;" d="M1127 326q0 32 -30 51q-193 115 -447 115q-133 0 -287 -34q-42 -9 -42 -52q0 -20 13.5 -34.5t35.5 -14.5q5 0 37 8q132 27 243 27q226 0 397 -103q19 -11 33 -11q19 0 33 13.5t14 34.5zM1223 541q0 40 -35 61q-237 141 -548 141q-153 0 -303 -42q-48 -13 -48 -64 q0 -25 17.5 -42.5t42.5 -17.5q7 0 37 8q122 33 251 33q279 0 488 -124q24 -13 38 -13q25 0 42.5 17.5t17.5 42.5zM1331 789q0 47 -40 70q-126 73 -293 110.5t-343 37.5q-204 0 -364 -47q-23 -7 -38.5 -25.5t-15.5 -48.5q0 -31 20.5 -52t51.5 -21q11 0 40 8q133 37 307 37 q159 0 309.5 -34t253.5 -95q21 -12 40 -12q29 0 50.5 20.5t21.5 51.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf1bd;" horiz-adv-x="1024" d="M1024 1233l-303 -582l24 -31h279v-415h-507l-44 -30l-142 -273l-30 -30h-301v303l303 583l-24 30h-279v415h507l44 30l142 273l30 30h301v-303z" />
+<glyph unicode="&#xf1be;" horiz-adv-x="2304" d="M784 164l16 241l-16 523q-1 10 -7.5 17t-16.5 7q-9 0 -16 -7t-7 -17l-14 -523l14 -241q1 -10 7.5 -16.5t15.5 -6.5q22 0 24 23zM1080 193l11 211l-12 586q0 16 -13 24q-8 5 -16 5t-16 -5q-13 -8 -13 -24l-1 -6l-10 -579q0 -1 11 -236v-1q0 -10 6 -17q9 -11 23 -11 q11 0 20 9q9 7 9 20zM35 533l20 -128l-20 -126q-2 -9 -9 -9t-9 9l-17 126l17 128q2 9 9 9t9 -9zM121 612l26 -207l-26 -203q-2 -9 -10 -9q-9 0 -9 10l-23 202l23 207q0 9 9 9q8 0 10 -9zM401 159zM213 650l25 -245l-25 -237q0 -11 -11 -11q-10 0 -12 11l-21 237l21 245 q2 12 12 12q11 0 11 -12zM307 657l23 -252l-23 -244q-2 -13 -14 -13q-13 0 -13 13l-21 244l21 252q0 13 13 13q12 0 14 -13zM401 639l21 -234l-21 -246q-2 -16 -16 -16q-6 0 -10.5 4.5t-4.5 11.5l-20 246l20 234q0 6 4.5 10.5t10.5 4.5q14 0 16 -15zM784 164zM495 785 l21 -380l-21 -246q0 -7 -5 -12.5t-12 -5.5q-16 0 -18 18l-18 246l18 380q2 18 18 18q7 0 12 -5.5t5 -12.5zM589 871l19 -468l-19 -244q0 -8 -5.5 -13.5t-13.5 -5.5q-18 0 -20 19l-16 244l16 468q2 19 20 19q8 0 13.5 -5.5t5.5 -13.5zM687 911l18 -506l-18 -242 q-2 -21 -22 -21q-19 0 -21 21l-16 242l16 506q0 9 6.5 15.5t14.5 6.5q9 0 15 -6.5t7 -15.5zM1079 169v0v0zM881 915l15 -510l-15 -239q0 -10 -7.5 -17.5t-17.5 -7.5t-17 7t-8 18l-14 239l14 510q0 11 7.5 18t17.5 7t17.5 -7t7.5 -18zM980 896l14 -492l-14 -236q0 -11 -8 -19 t-19 -8t-19 8t-9 19l-12 236l12 492q1 12 9 20t19 8t18.5 -8t8.5 -20zM1192 404l-14 -231v0q0 -13 -9 -22t-22 -9t-22 9t-10 22l-6 114l-6 117l12 636v3q2 15 12 24q9 7 20 7q8 0 15 -5q14 -8 16 -26zM2304 423q0 -117 -83 -199.5t-200 -82.5h-786q-13 2 -22 11t-9 22v899 q0 23 28 33q85 34 181 34q195 0 338 -131.5t160 -323.5q53 22 110 22q117 0 200 -83t83 -201z" />
+<glyph unicode="&#xf1c0;" d="M768 768q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 0q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127 t443 -43zM768 384q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 1536q208 0 385 -34.5t280 -93.5t103 -128v-128q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5 t-103 128v128q0 69 103 128t280 93.5t385 34.5z" />
+<glyph unicode="&#xf1c1;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M894 465q33 -26 84 -56q59 7 117 7q147 0 177 -49q16 -22 2 -52q0 -1 -1 -2l-2 -2v-1q-6 -38 -71 -38q-48 0 -115 20t-130 53q-221 -24 -392 -83q-153 -262 -242 -262q-15 0 -28 7l-24 12q-1 1 -6 5q-10 10 -6 36q9 40 56 91.5t132 96.5q14 9 23 -6q2 -2 2 -4q52 85 107 197 q68 136 104 262q-24 82 -30.5 159.5t6.5 127.5q11 40 42 40h21h1q23 0 35 -15q18 -21 9 -68q-2 -6 -4 -8q1 -3 1 -8v-30q-2 -123 -14 -192q55 -164 146 -238zM318 54q52 24 137 158q-51 -40 -87.5 -84t-49.5 -74zM716 974q-15 -42 -2 -132q1 7 7 44q0 3 7 43q1 4 4 8 q-1 1 -1 2t-0.5 1.5t-0.5 1.5q-1 22 -13 36q0 -1 -1 -2v-2zM592 313q135 54 284 81q-2 1 -13 9.5t-16 13.5q-76 67 -127 176q-27 -86 -83 -197q-30 -56 -45 -83zM1238 329q-24 24 -140 24q76 -28 124 -28q14 0 18 1q0 1 -2 3z" />
+<glyph unicode="&#xf1c2;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M233 768v-107h70l164 -661h159l128 485q7 20 10 46q2 16 2 24h4l3 -24q1 -3 3.5 -20t5.5 -26l128 -485h159l164 661h70v107h-300v-107h90l-99 -438q-5 -20 -7 -46l-2 -21h-4l-3 21q-1 5 -4 21t-5 25l-144 545h-114l-144 -545q-2 -9 -4.5 -24.5t-3.5 -21.5l-4 -21h-4l-2 21 q-2 26 -7 46l-99 438h90v107h-300z" />
+<glyph unicode="&#xf1c3;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M429 106v-106h281v106h-75l103 161q5 7 10 16.5t7.5 13.5t3.5 4h2q1 -4 5 -10q2 -4 4.5 -7.5t6 -8t6.5 -8.5l107 -161h-76v-106h291v106h-68l-192 273l195 282h67v107h-279v-107h74l-103 -159q-4 -7 -10 -16.5t-9 -13.5l-2 -3h-2q-1 4 -5 10q-6 11 -17 23l-106 159h76v107 h-290v-107h68l189 -272l-194 -283h-68z" />
+<glyph unicode="&#xf1c4;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M416 106v-106h327v106h-93v167h137q76 0 118 15q67 23 106.5 87t39.5 146q0 81 -37 141t-100 87q-48 19 -130 19h-368v-107h92v-555h-92zM769 386h-119v268h120q52 0 83 -18q56 -33 56 -115q0 -89 -62 -120q-31 -15 -78 -15z" />
+<glyph unicode="&#xf1c5;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M1280 320v-320h-1024v192l192 192l128 -128l384 384zM448 512q-80 0 -136 56t-56 136t56 136t136 56t136 -56t56 -136t-56 -136t-136 -56z" />
+<glyph unicode="&#xf1c6;" d="M640 1152v128h-128v-128h128zM768 1024v128h-128v-128h128zM640 896v128h-128v-128h128zM768 768v128h-128v-128h128zM1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400 v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-128v-128h-128v128h-512v-1536h1280zM781 593l107 -349q8 -27 8 -52q0 -83 -72.5 -137.5t-183.5 -54.5t-183.5 54.5t-72.5 137.5q0 25 8 52q21 63 120 396v128h128v-128h79 q22 0 39 -13t23 -34zM640 128q53 0 90.5 19t37.5 45t-37.5 45t-90.5 19t-90.5 -19t-37.5 -45t37.5 -45t90.5 -19z" />
+<glyph unicode="&#xf1c7;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M620 686q20 -8 20 -30v-544q0 -22 -20 -30q-8 -2 -12 -2q-12 0 -23 9l-166 167h-131q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h131l166 167q16 15 35 7zM1037 -3q31 0 50 24q129 159 129 363t-129 363q-16 21 -43 24t-47 -14q-21 -17 -23.5 -43.5t14.5 -47.5 q100 -123 100 -282t-100 -282q-17 -21 -14.5 -47.5t23.5 -42.5q18 -15 40 -15zM826 145q27 0 47 20q87 93 87 219t-87 219q-18 19 -45 20t-46 -17t-20 -44.5t18 -46.5q52 -57 52 -131t-52 -131q-19 -20 -18 -46.5t20 -44.5q20 -17 44 -17z" />
+<glyph unicode="&#xf1c8;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M768 768q52 0 90 -38t38 -90v-384q0 -52 -38 -90t-90 -38h-384q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h384zM1260 766q20 -8 20 -30v-576q0 -22 -20 -30q-8 -2 -12 -2q-14 0 -23 9l-265 266v90l265 266q9 9 23 9q4 0 12 -2z" />
+<glyph unicode="&#xf1c9;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M480 768q8 11 21 12.5t24 -6.5l51 -38q11 -8 12.5 -21t-6.5 -24l-182 -243l182 -243q8 -11 6.5 -24t-12.5 -21l-51 -38q-11 -8 -24 -6.5t-21 12.5l-226 301q-14 19 0 38zM1282 467q14 -19 0 -38l-226 -301q-8 -11 -21 -12.5t-24 6.5l-51 38q-11 8 -12.5 21t6.5 24l182 243 l-182 243q-8 11 -6.5 24t12.5 21l51 38q11 8 24 6.5t21 -12.5zM662 6q-13 2 -20.5 13t-5.5 24l138 831q2 13 13 20.5t24 5.5l63 -10q13 -2 20.5 -13t5.5 -24l-138 -831q-2 -13 -13 -20.5t-24 -5.5z" />
+<glyph unicode="&#xf1ca;" d="M1497 709v-198q-101 -23 -198 -23q-65 -136 -165.5 -271t-181.5 -215.5t-128 -106.5q-80 -45 -162 3q-28 17 -60.5 43.5t-85 83.5t-102.5 128.5t-107.5 184t-105.5 244t-91.5 314.5t-70.5 390h283q26 -218 70 -398.5t104.5 -317t121.5 -235.5t140 -195q169 169 287 406 q-142 72 -223 220t-81 333q0 192 104 314.5t284 122.5q178 0 273 -105.5t95 -297.5q0 -159 -58 -286q-7 -1 -19.5 -3t-46 -2t-63 6t-62 25.5t-50.5 51.5q31 103 31 184q0 87 -29 132t-79 45q-53 0 -85 -49.5t-32 -140.5q0 -186 105 -293.5t267 -107.5q62 0 121 14z" />
+<glyph unicode="&#xf1cb;" horiz-adv-x="1792" d="M216 367l603 -402v359l-334 223zM154 511l193 129l-193 129v-258zM973 -35l603 402l-269 180l-334 -223v-359zM896 458l272 182l-272 182l-272 -182zM485 733l334 223v359l-603 -402zM1445 640l193 -129v258zM1307 733l269 180l-603 402v-359zM1792 913v-546 q0 -41 -34 -64l-819 -546q-21 -13 -43 -13t-43 13l-819 546q-34 23 -34 64v546q0 41 34 64l819 546q21 13 43 13t43 -13l819 -546q34 -23 34 -64z" />
+<glyph unicode="&#xf1cc;" horiz-adv-x="2048" d="M1800 764q111 -46 179.5 -145.5t68.5 -221.5q0 -164 -118 -280.5t-285 -116.5q-4 0 -11.5 0.5t-10.5 0.5h-1209h-1h-2h-5q-170 10 -288 125.5t-118 280.5q0 110 55 203t147 147q-12 39 -12 82q0 115 82 196t199 81q95 0 172 -58q75 154 222.5 248t326.5 94 q166 0 306 -80.5t221.5 -218.5t81.5 -301q0 -6 -0.5 -18t-0.5 -18zM468 498q0 -122 84 -193t208 -71q137 0 240 99q-16 20 -47.5 56.5t-43.5 50.5q-67 -65 -144 -65q-55 0 -93.5 33.5t-38.5 87.5q0 53 38.5 87t91.5 34q44 0 84.5 -21t73 -55t65 -75t69 -82t77 -75t97 -55 t121.5 -21q121 0 204.5 71.5t83.5 190.5q0 121 -84 192t-207 71q-143 0 -241 -97q14 -16 29.5 -34t34.5 -40t29 -34q66 64 142 64q52 0 92 -33t40 -84q0 -57 -37 -91.5t-94 -34.5q-43 0 -82.5 21t-72 55t-65.5 75t-69.5 82t-77.5 75t-96.5 55t-118.5 21q-122 0 -207 -70.5 t-85 -189.5z" />
+<glyph unicode="&#xf1cd;" horiz-adv-x="1792" d="M896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 1408q-190 0 -361 -90l194 -194q82 28 167 28t167 -28l194 194q-171 90 -361 90zM218 279l194 194 q-28 82 -28 167t28 167l-194 194q-90 -171 -90 -361t90 -361zM896 -128q190 0 361 90l-194 194q-82 -28 -167 -28t-167 28l-194 -194q171 -90 361 -90zM896 256q159 0 271.5 112.5t112.5 271.5t-112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5 t271.5 -112.5zM1380 473l194 -194q90 171 90 361t-90 361l-194 -194q28 -82 28 -167t-28 -167z" />
+<glyph unicode="&#xf1ce;" horiz-adv-x="1792" d="M1760 640q0 -176 -68.5 -336t-184 -275.5t-275.5 -184t-336 -68.5t-336 68.5t-275.5 184t-184 275.5t-68.5 336q0 213 97 398.5t265 305.5t374 151v-228q-221 -45 -366.5 -221t-145.5 -406q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5 t136.5 204t51 248.5q0 230 -145.5 406t-366.5 221v228q206 -31 374 -151t265 -305.5t97 -398.5z" />
+<glyph unicode="&#xf1d0;" horiz-adv-x="1792" d="M19 662q8 217 116 406t305 318h5q0 -1 -1 -3q-8 -8 -28 -33.5t-52 -76.5t-60 -110.5t-44.5 -135.5t-14 -150.5t39 -157.5t108.5 -154q50 -50 102 -69.5t90.5 -11.5t69.5 23.5t47 32.5l16 16q39 51 53 116.5t6.5 122.5t-21 107t-26.5 80l-14 29q-10 25 -30.5 49.5t-43 41 t-43.5 29.5t-35 19l-13 6l104 115q39 -17 78 -52t59 -61l19 -27q1 48 -18.5 103.5t-40.5 87.5l-20 31l161 183l160 -181q-33 -46 -52.5 -102.5t-22.5 -90.5l-4 -33q22 37 61.5 72.5t67.5 52.5l28 17l103 -115q-44 -14 -85 -50t-60 -65l-19 -29q-31 -56 -48 -133.5t-7 -170 t57 -156.5q33 -45 77.5 -60.5t85 -5.5t76 26.5t57.5 33.5l21 16q60 53 96.5 115t48.5 121.5t10 121.5t-18 118t-37 107.5t-45.5 93t-45 72t-34.5 47.5l-13 17q-14 13 -7 13l10 -3q40 -29 62.5 -46t62 -50t64 -58t58.5 -65t55.5 -77t45.5 -88t38 -103t23.5 -117t10.5 -136 q3 -259 -108 -465t-312 -321t-456 -115q-185 0 -351 74t-283.5 198t-184 293t-60.5 353z" />
+<glyph unicode="&#xf1d1;" horiz-adv-x="1792" d="M874 -102v-66q-208 6 -385 109.5t-283 275.5l58 34q29 -49 73 -99l65 57q148 -168 368 -212l-17 -86q65 -12 121 -13zM276 428l-83 -28q22 -60 49 -112l-57 -33q-98 180 -98 385t98 385l57 -33q-30 -56 -49 -112l82 -28q-35 -100 -35 -212q0 -109 36 -212zM1528 251 l58 -34q-106 -172 -283 -275.5t-385 -109.5v66q56 1 121 13l-17 86q220 44 368 212l65 -57q44 50 73 99zM1377 805l-233 -80q14 -42 14 -85t-14 -85l232 -80q-31 -92 -98 -169l-185 162q-57 -67 -147 -85l48 -241q-52 -10 -98 -10t-98 10l48 241q-90 18 -147 85l-185 -162 q-67 77 -98 169l232 80q-14 42 -14 85t14 85l-233 80q33 93 99 169l185 -162q59 68 147 86l-48 240q44 10 98 10t98 -10l-48 -240q88 -18 147 -86l185 162q66 -76 99 -169zM874 1448v-66q-65 -2 -121 -13l17 -86q-220 -42 -368 -211l-65 56q-38 -42 -73 -98l-57 33 q106 172 282 275.5t385 109.5zM1705 640q0 -205 -98 -385l-57 33q27 52 49 112l-83 28q36 103 36 212q0 112 -35 212l82 28q-19 56 -49 112l57 33q98 -180 98 -385zM1585 1063l-57 -33q-35 56 -73 98l-65 -56q-148 169 -368 211l17 86q-56 11 -121 13v66q209 -6 385 -109.5 t282 -275.5zM1748 640q0 173 -67.5 331t-181.5 272t-272 181.5t-331 67.5t-331 -67.5t-272 -181.5t-181.5 -272t-67.5 -331t67.5 -331t181.5 -272t272 -181.5t331 -67.5t331 67.5t272 181.5t181.5 272t67.5 331zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71 t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf1d2;" d="M582 228q0 -66 -93 -66q-107 0 -107 63q0 64 98 64q102 0 102 -61zM546 694q0 -85 -74 -85q-77 0 -77 84q0 90 77 90q36 0 55 -25.5t19 -63.5zM712 769v125q-78 -29 -135 -29q-50 29 -110 29q-86 0 -145 -57t-59 -143q0 -50 29.5 -102t73.5 -67v-3q-38 -17 -38 -85 q0 -53 41 -77v-3q-113 -37 -113 -139q0 -45 20 -78.5t54 -51t72 -25.5t81 -8q224 0 224 188q0 67 -48 99t-126 46q-27 5 -51.5 20.5t-24.5 39.5q0 44 49 52q77 15 122 70t45 134q0 24 -10 52q37 9 49 13zM771 350h137q-2 27 -2 82v387q0 46 2 69h-137q3 -23 3 -71v-392 q0 -50 -3 -75zM1280 366v121q-30 -21 -68 -21q-53 0 -53 82v225h52q9 0 26.5 -1t26.5 -1v117h-105q0 82 3 102h-140q4 -24 4 -55v-47h-60v-117q36 3 37 3q3 0 11 -0.5t12 -0.5v-2h-2v-217q0 -37 2.5 -64t11.5 -56.5t24.5 -48.5t43.5 -31t66 -12q64 0 108 24zM924 1072 q0 36 -24 63.5t-60 27.5t-60.5 -27t-24.5 -64q0 -36 25 -62.5t60 -26.5t59.5 27t24.5 62zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf1d3;" horiz-adv-x="1792" d="M595 22q0 100 -165 100q-158 0 -158 -104q0 -101 172 -101q151 0 151 105zM536 777q0 61 -30 102t-89 41q-124 0 -124 -145q0 -135 124 -135q119 0 119 137zM805 1101v-202q-36 -12 -79 -22q16 -43 16 -84q0 -127 -73 -216.5t-197 -112.5q-40 -8 -59.5 -27t-19.5 -58 q0 -31 22.5 -51.5t58 -32t78.5 -22t86 -25.5t78.5 -37.5t58 -64t22.5 -98.5q0 -304 -363 -304q-69 0 -130 12.5t-116 41t-87.5 82t-32.5 127.5q0 165 182 225v4q-67 41 -67 126q0 109 63 137v4q-72 24 -119.5 108.5t-47.5 165.5q0 139 95 231.5t235 92.5q96 0 178 -47 q98 0 218 47zM1123 220h-222q4 45 4 134v609q0 94 -4 128h222q-4 -33 -4 -124v-613q0 -89 4 -134zM1724 442v-196q-71 -39 -174 -39q-62 0 -107 20t-70 50t-39.5 78t-18.5 92t-4 103v351h2v4q-7 0 -19 1t-18 1q-21 0 -59 -6v190h96v76q0 54 -6 89h227q-6 -41 -6 -165h171 v-190q-15 0 -43.5 2t-42.5 2h-85v-365q0 -131 87 -131q61 0 109 33zM1148 1389q0 -58 -39 -101.5t-96 -43.5q-58 0 -98 43.5t-40 101.5q0 59 39.5 103t98.5 44q58 0 96.5 -44.5t38.5 -102.5z" />
+<glyph unicode="&#xf1d4;" d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf1d5;" horiz-adv-x="1280" d="M842 964q0 -80 -57 -136.5t-136 -56.5q-60 0 -111 35q-62 -67 -115 -146q-247 -371 -202 -859q1 -22 -12.5 -38.5t-34.5 -18.5h-5q-20 0 -35 13.5t-17 33.5q-14 126 -3.5 247.5t29.5 217t54 186t69 155.5t74 125q61 90 132 165q-16 35 -16 77q0 80 56.5 136.5t136.5 56.5 t136.5 -56.5t56.5 -136.5zM1223 953q0 -158 -78 -292t-212.5 -212t-292.5 -78q-64 0 -131 14q-21 5 -32.5 23.5t-6.5 39.5q5 20 23 31.5t39 7.5q51 -13 108 -13q97 0 186 38t153 102t102 153t38 186t-38 186t-102 153t-153 102t-186 38t-186 -38t-153 -102t-102 -153 t-38 -186q0 -114 52 -218q10 -20 3.5 -40t-25.5 -30t-39.5 -3t-30.5 26q-64 123 -64 265q0 119 46.5 227t124.5 186t186 124t226 46q158 0 292.5 -78t212.5 -212.5t78 -292.5z" />
+<glyph unicode="&#xf1d6;" horiz-adv-x="1792" d="M270 730q-8 19 -8 52q0 20 11 49t24 45q-1 22 7.5 53t22.5 43q0 139 92.5 288.5t217.5 209.5q139 66 324 66q133 0 266 -55q49 -21 90 -48t71 -56t55 -68t42 -74t32.5 -84.5t25.5 -89.5t22 -98l1 -5q55 -83 55 -150q0 -14 -9 -40t-9 -38q0 -1 1.5 -3.5t3.5 -5t2 -3.5 q77 -114 120.5 -214.5t43.5 -208.5q0 -43 -19.5 -100t-55.5 -57q-9 0 -19.5 7.5t-19 17.5t-19 26t-16 26.5t-13.5 26t-9 17.5q-1 1 -3 1l-5 -4q-59 -154 -132 -223q20 -20 61.5 -38.5t69 -41.5t35.5 -65q-2 -4 -4 -16t-7 -18q-64 -97 -302 -97q-53 0 -110.5 9t-98 20 t-104.5 30q-15 5 -23 7q-14 4 -46 4.5t-40 1.5q-41 -45 -127.5 -65t-168.5 -20q-35 0 -69 1.5t-93 9t-101 20.5t-74.5 40t-32.5 64q0 40 10 59.5t41 48.5q11 2 40.5 13t49.5 12q4 0 14 2q2 2 2 4l-2 3q-48 11 -108 105.5t-73 156.5l-5 3q-4 0 -12 -20q-18 -41 -54.5 -74.5 t-77.5 -37.5h-1q-4 0 -6 4.5t-5 5.5q-23 54 -23 100q0 275 252 466z" />
+<glyph unicode="&#xf1d7;" horiz-adv-x="2048" d="M580 1075q0 41 -25 66t-66 25q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 66 24.5t25 65.5zM1323 568q0 28 -25.5 50t-65.5 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q40 0 65.5 22t25.5 51zM1087 1075q0 41 -24.5 66t-65.5 25 q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 65.5 24.5t24.5 65.5zM1722 568q0 28 -26 50t-65 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q39 0 65 22t26 51zM1456 965q-31 4 -70 4q-169 0 -311 -77t-223.5 -208.5t-81.5 -287.5 q0 -78 23 -152q-35 -3 -68 -3q-26 0 -50 1.5t-55 6.5t-44.5 7t-54.5 10.5t-50 10.5l-253 -127l72 218q-290 203 -290 490q0 169 97.5 311t264 223.5t363.5 81.5q176 0 332.5 -66t262 -182.5t136.5 -260.5zM2048 404q0 -117 -68.5 -223.5t-185.5 -193.5l55 -181l-199 109 q-150 -37 -218 -37q-169 0 -311 70.5t-223.5 191.5t-81.5 264t81.5 264t223.5 191.5t311 70.5q161 0 303 -70.5t227.5 -192t85.5 -263.5z" />
+<glyph unicode="&#xf1d8;" horiz-adv-x="1792" d="M1764 1525q33 -24 27 -64l-256 -1536q-5 -29 -32 -45q-14 -8 -31 -8q-11 0 -24 5l-453 185l-242 -295q-18 -23 -49 -23q-13 0 -22 4q-19 7 -30.5 23.5t-11.5 36.5v349l864 1059l-1069 -925l-395 162q-37 14 -40 55q-2 40 32 59l1664 960q15 9 32 9q20 0 36 -11z" />
+<glyph unicode="&#xf1d9;" horiz-adv-x="1792" d="M1764 1525q33 -24 27 -64l-256 -1536q-5 -29 -32 -45q-14 -8 -31 -8q-11 0 -24 5l-527 215l-298 -327q-18 -21 -47 -21q-14 0 -23 4q-19 7 -30 23.5t-11 36.5v452l-472 193q-37 14 -40 55q-3 39 32 59l1664 960q35 21 68 -2zM1422 26l221 1323l-1434 -827l336 -137 l863 639l-478 -797z" />
+<glyph unicode="&#xf1da;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298zM896 928v-448q0 -14 -9 -23 t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf1db;" d="M768 1280q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf1dc;" horiz-adv-x="1792" d="M1682 -128q-44 0 -132.5 3.5t-133.5 3.5q-44 0 -132 -3.5t-132 -3.5q-24 0 -37 20.5t-13 45.5q0 31 17 46t39 17t51 7t45 15q33 21 33 140l-1 391q0 21 -1 31q-13 4 -50 4h-675q-38 0 -51 -4q-1 -10 -1 -31l-1 -371q0 -142 37 -164q16 -10 48 -13t57 -3.5t45 -15 t20 -45.5q0 -26 -12.5 -48t-36.5 -22q-47 0 -139.5 3.5t-138.5 3.5q-43 0 -128 -3.5t-127 -3.5q-23 0 -35.5 21t-12.5 45q0 30 15.5 45t36 17.5t47.5 7.5t42 15q33 23 33 143l-1 57v813q0 3 0.5 26t0 36.5t-1.5 38.5t-3.5 42t-6.5 36.5t-11 31.5t-16 18q-15 10 -45 12t-53 2 t-41 14t-18 45q0 26 12 48t36 22q46 0 138.5 -3.5t138.5 -3.5q42 0 126.5 3.5t126.5 3.5q25 0 37.5 -22t12.5 -48q0 -30 -17 -43.5t-38.5 -14.5t-49.5 -4t-43 -13q-35 -21 -35 -160l1 -320q0 -21 1 -32q13 -3 39 -3h699q25 0 38 3q1 11 1 32l1 320q0 139 -35 160 q-18 11 -58.5 12.5t-66 13t-25.5 49.5q0 26 12.5 48t37.5 22q44 0 132 -3.5t132 -3.5q43 0 129 3.5t129 3.5q25 0 37.5 -22t12.5 -48q0 -30 -17.5 -44t-40 -14.5t-51.5 -3t-44 -12.5q-35 -23 -35 -161l1 -943q0 -119 34 -140q16 -10 46 -13.5t53.5 -4.5t41.5 -15.5t18 -44.5 q0 -26 -12 -48t-36 -22z" />
+<glyph unicode="&#xf1dd;" horiz-adv-x="1280" d="M1278 1347v-73q0 -29 -18.5 -61t-42.5 -32q-50 0 -54 -1q-26 -6 -32 -31q-3 -11 -3 -64v-1152q0 -25 -18 -43t-43 -18h-108q-25 0 -43 18t-18 43v1218h-143v-1218q0 -25 -17.5 -43t-43.5 -18h-108q-26 0 -43.5 18t-17.5 43v496q-147 12 -245 59q-126 58 -192 179 q-64 117 -64 259q0 166 88 286q88 118 209 159q111 37 417 37h479q25 0 43 -18t18 -43z" />
+<glyph unicode="&#xf1de;" d="M352 128v-128h-352v128h352zM704 256q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM864 640v-128h-864v128h864zM224 1152v-128h-224v128h224zM1536 128v-128h-736v128h736zM576 1280q26 0 45 -19t19 -45v-256 q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM1216 768q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM1536 640v-128h-224v128h224zM1536 1152v-128h-864v128h864z" />
+<glyph unicode="&#xf1e0;" d="M1216 512q133 0 226.5 -93.5t93.5 -226.5t-93.5 -226.5t-226.5 -93.5t-226.5 93.5t-93.5 226.5q0 12 2 34l-360 180q-92 -86 -218 -86q-133 0 -226.5 93.5t-93.5 226.5t93.5 226.5t226.5 93.5q126 0 218 -86l360 180q-2 22 -2 34q0 133 93.5 226.5t226.5 93.5 t226.5 -93.5t93.5 -226.5t-93.5 -226.5t-226.5 -93.5q-126 0 -218 86l-360 -180q2 -22 2 -34t-2 -34l360 -180q92 86 218 86z" />
+<glyph unicode="&#xf1e1;" d="M1280 341q0 88 -62.5 151t-150.5 63q-84 0 -145 -58l-241 120q2 16 2 23t-2 23l241 120q61 -58 145 -58q88 0 150.5 63t62.5 151t-62.5 150.5t-150.5 62.5t-151 -62.5t-63 -150.5q0 -7 2 -23l-241 -120q-62 57 -145 57q-88 0 -150.5 -62.5t-62.5 -150.5t62.5 -150.5 t150.5 -62.5q83 0 145 57l241 -120q-2 -16 -2 -23q0 -88 63 -150.5t151 -62.5t150.5 62.5t62.5 150.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf1e2;" horiz-adv-x="1792" d="M571 947q-10 25 -34 35t-49 0q-108 -44 -191 -127t-127 -191q-10 -25 0 -49t35 -34q13 -5 24 -5q42 0 60 40q34 84 98.5 148.5t148.5 98.5q25 11 35 35t0 49zM1513 1303l46 -46l-244 -243l68 -68q19 -19 19 -45.5t-19 -45.5l-64 -64q89 -161 89 -343q0 -143 -55.5 -273.5 t-150 -225t-225 -150t-273.5 -55.5t-273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5q182 0 343 -89l64 64q19 19 45.5 19t45.5 -19l68 -68zM1521 1359q-10 -10 -22 -10q-13 0 -23 10l-91 90q-9 10 -9 23t9 23q10 9 23 9t23 -9l90 -91 q10 -9 10 -22.5t-10 -22.5zM1751 1129q-11 -9 -23 -9t-23 9l-90 91q-10 9 -10 22.5t10 22.5q9 10 22.5 10t22.5 -10l91 -90q9 -10 9 -23t-9 -23zM1792 1312q0 -14 -9 -23t-23 -9h-96q-14 0 -23 9t-9 23t9 23t23 9h96q14 0 23 -9t9 -23zM1600 1504v-96q0 -14 -9 -23t-23 -9 t-23 9t-9 23v96q0 14 9 23t23 9t23 -9t9 -23zM1751 1449l-91 -90q-10 -10 -22 -10q-13 0 -23 10q-10 9 -10 22.5t10 22.5l90 91q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
+<glyph unicode="&#xf1e3;" horiz-adv-x="1792" d="M609 720l287 208l287 -208l-109 -336h-355zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM1515 186q149 203 149 454v3l-102 -89l-240 224l63 323 l134 -12q-150 206 -389 282l53 -124l-287 -159l-287 159l53 124q-239 -76 -389 -282l135 12l62 -323l-240 -224l-102 89v-3q0 -251 149 -454l30 132l326 -40l139 -298l-116 -69q117 -39 240 -39t240 39l-116 69l139 298l326 40z" />
+<glyph unicode="&#xf1e4;" horiz-adv-x="1792" d="M448 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM256 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM832 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM66 768q-28 0 -47 19t-19 46v129h514v-129q0 -27 -19 -46t-46 -19h-383zM1216 224v-192q0 -14 -9 -23t-23 -9h-192 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1600 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23 zM1408 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1016v-13h-514v10q0 104 -382 102q-382 -1 -382 -102v-10h-514v13q0 17 8.5 43t34 64t65.5 75.5t110.5 76t160 67.5t224 47.5t293.5 18.5t293 -18.5t224 -47.5 t160.5 -67.5t110.5 -76t65.5 -75.5t34 -64t8.5 -43zM1792 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 962v-129q0 -27 -19 -46t-46 -19h-384q-27 0 -46 19t-19 46v129h514z" />
+<glyph unicode="&#xf1e5;" horiz-adv-x="1792" d="M704 1216v-768q0 -26 -19 -45t-45 -19v-576q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v512l249 873q7 23 31 23h424zM1024 1216v-704h-256v704h256zM1792 320v-512q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v576q-26 0 -45 19t-19 45v768h424q24 0 31 -23z M736 1504v-224h-352v224q0 14 9 23t23 9h288q14 0 23 -9t9 -23zM1408 1504v-224h-352v224q0 14 9 23t23 9h288q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf1e6;" horiz-adv-x="1792" d="M1755 1083q37 -37 37 -90t-37 -91l-401 -400l150 -150l-160 -160q-163 -163 -389.5 -186.5t-411.5 100.5l-362 -362h-181v181l362 362q-124 185 -100.5 411.5t186.5 389.5l160 160l150 -150l400 401q38 37 91 37t90 -37t37 -90.5t-37 -90.5l-400 -401l234 -234l401 400 q38 37 91 37t90 -37z" />
+<glyph unicode="&#xf1e7;" horiz-adv-x="1792" d="M873 796q0 -83 -63.5 -142.5t-152.5 -59.5t-152.5 59.5t-63.5 142.5q0 84 63.5 143t152.5 59t152.5 -59t63.5 -143zM1375 796q0 -83 -63 -142.5t-153 -59.5q-89 0 -152.5 59.5t-63.5 142.5q0 84 63.5 143t152.5 59q90 0 153 -59t63 -143zM1600 616v667q0 87 -32 123.5 t-111 36.5h-1112q-83 0 -112.5 -34t-29.5 -126v-673q43 -23 88.5 -40t81 -28t81 -18.5t71 -11t70 -4t58.5 -0.5t56.5 2t44.5 2q68 1 95 -27q6 -6 10 -9q26 -25 61 -51q7 91 118 87q5 0 36.5 -1.5t43 -2t45.5 -1t53 1t54.5 4.5t61 8.5t62 13.5t67 19.5t67.5 27t72 34.5z M1763 621q-121 -149 -372 -252q84 -285 -23 -465q-66 -113 -183 -148q-104 -32 -182 15q-86 51 -82 164l-1 326v1q-8 2 -24.5 6t-23.5 5l-1 -338q4 -114 -83 -164q-79 -47 -183 -15q-117 36 -182 150q-105 180 -22 463q-251 103 -372 252q-25 37 -4 63t60 -1q3 -2 11 -7 t11 -8v694q0 72 47 123t114 51h1257q67 0 114 -51t47 -123v-694l21 15q39 27 60 1t-4 -63z" />
+<glyph unicode="&#xf1e8;" horiz-adv-x="1792" d="M896 1102v-434h-145v434h145zM1294 1102v-434h-145v434h145zM1294 342l253 254v795h-1194v-1049h326v-217l217 217h398zM1692 1536v-1013l-434 -434h-326l-217 -217h-217v217h-398v1158l109 289h1483z" />
+<glyph unicode="&#xf1e9;" d="M773 217v-127q-1 -292 -6 -305q-12 -32 -51 -40q-54 -9 -181.5 38t-162.5 89q-13 15 -17 36q-1 12 4 26q4 10 34 47t181 216q1 0 60 70q15 19 39.5 24.5t49.5 -3.5q24 -10 37.5 -29t12.5 -42zM624 468q-3 -55 -52 -70l-120 -39q-275 -88 -292 -88q-35 2 -54 36 q-12 25 -17 75q-8 76 1 166.5t30 124.5t56 32q13 0 202 -77q70 -29 115 -47l84 -34q23 -9 35.5 -30.5t11.5 -48.5zM1450 171q-7 -54 -91.5 -161t-135.5 -127q-37 -14 -63 7q-14 10 -184 287l-47 77q-14 21 -11.5 46t19.5 46q35 43 83 26q1 -1 119 -40q203 -66 242 -79.5 t47 -20.5q28 -22 22 -61zM778 803q5 -102 -54 -122q-58 -17 -114 71l-378 598q-8 35 19 62q41 43 207.5 89.5t224.5 31.5q40 -10 49 -45q3 -18 22 -305.5t24 -379.5zM1440 695q3 -39 -26 -59q-15 -10 -329 -86q-67 -15 -91 -23l1 2q-23 -6 -46 4t-37 32q-30 47 0 87 q1 1 75 102q125 171 150 204t34 39q28 19 65 2q48 -23 123 -133.5t81 -167.5v-3z" />
+<glyph unicode="&#xf1ea;" horiz-adv-x="2048" d="M1024 1024h-384v-384h384v384zM1152 384v-128h-640v128h640zM1152 1152v-640h-640v640h640zM1792 384v-128h-512v128h512zM1792 640v-128h-512v128h512zM1792 896v-128h-512v128h512zM1792 1152v-128h-512v128h512zM256 192v960h-128v-960q0 -26 19 -45t45 -19t45 19 t19 45zM1920 192v1088h-1536v-1088q0 -33 -11 -64h1483q26 0 45 19t19 45zM2048 1408v-1216q0 -80 -56 -136t-136 -56h-1664q-80 0 -136 56t-56 136v1088h256v128h1792z" />
+<glyph unicode="&#xf1eb;" horiz-adv-x="2048" d="M1024 13q-20 0 -93 73.5t-73 93.5q0 32 62.5 54t103.5 22t103.5 -22t62.5 -54q0 -20 -73 -93.5t-93 -73.5zM1294 284q-2 0 -40 25t-101.5 50t-128.5 25t-128.5 -25t-101 -50t-40.5 -25q-18 0 -93.5 75t-75.5 93q0 13 10 23q78 77 196 121t233 44t233 -44t196 -121 q10 -10 10 -23q0 -18 -75.5 -93t-93.5 -75zM1567 556q-11 0 -23 8q-136 105 -252 154.5t-268 49.5q-85 0 -170.5 -22t-149 -53t-113.5 -62t-79 -53t-31 -22q-17 0 -92 75t-75 93q0 12 10 22q132 132 320 205t380 73t380 -73t320 -205q10 -10 10 -22q0 -18 -75 -93t-92 -75z M1838 827q-11 0 -22 9q-179 157 -371.5 236.5t-420.5 79.5t-420.5 -79.5t-371.5 -236.5q-11 -9 -22 -9q-17 0 -92.5 75t-75.5 93q0 13 10 23q187 186 445 288t527 102t527 -102t445 -288q10 -10 10 -23q0 -18 -75.5 -93t-92.5 -75z" />
+<glyph unicode="&#xf1ec;" horiz-adv-x="1792" d="M384 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5 t37.5 90.5zM384 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 768q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1536 0v384q0 52 -38 90t-90 38t-90 -38t-38 -90v-384q0 -52 38 -90t90 -38t90 38t38 90zM1152 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z M1536 1088v256q0 26 -19 45t-45 19h-1280q-26 0 -45 -19t-19 -45v-256q0 -26 19 -45t45 -19h1280q26 0 45 19t19 45zM1536 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1408v-1536q0 -52 -38 -90t-90 -38 h-1408q-52 0 -90 38t-38 90v1536q0 52 38 90t90 38h1408q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf1ed;" d="M1519 890q18 -84 -4 -204q-87 -444 -565 -444h-44q-25 0 -44 -16.5t-24 -42.5l-4 -19l-55 -346l-2 -15q-5 -26 -24.5 -42.5t-44.5 -16.5h-251q-21 0 -33 15t-9 36q9 56 26.5 168t26.5 168t27 167.5t27 167.5q5 37 43 37h131q133 -2 236 21q175 39 287 144q102 95 155 246 q24 70 35 133q1 6 2.5 7.5t3.5 1t6 -3.5q79 -59 98 -162zM1347 1172q0 -107 -46 -236q-80 -233 -302 -315q-113 -40 -252 -42q0 -1 -90 -1l-90 1q-100 0 -118 -96q-2 -8 -85 -530q-1 -10 -12 -10h-295q-22 0 -36.5 16.5t-11.5 38.5l232 1471q5 29 27.5 48t51.5 19h598 q34 0 97.5 -13t111.5 -32q107 -41 163.5 -123t56.5 -196z" />
+<glyph unicode="&#xf1ee;" horiz-adv-x="1792" d="M602 949q19 -61 31 -123.5t17 -141.5t-14 -159t-62 -145q-21 81 -67 157t-95.5 127t-99 90.5t-78.5 57.5t-33 19q-62 34 -81.5 100t14.5 128t101 81.5t129 -14.5q138 -83 238 -177zM927 1236q11 -25 20.5 -46t36.5 -100.5t42.5 -150.5t25.5 -179.5t0 -205.5t-47.5 -209.5 t-105.5 -208.5q-51 -72 -138 -72q-54 0 -98 31q-57 40 -69 109t28 127q60 85 81 195t13 199.5t-32 180.5t-39 128t-22 52q-31 63 -8.5 129.5t85.5 97.5q34 17 75 17q47 0 88.5 -25t63.5 -69zM1248 567q-17 -160 -72 -311q-17 131 -63 246q25 174 -5 361q-27 178 -94 342 q114 -90 212 -211q9 -37 15 -80q26 -179 7 -347zM1520 1440q9 -17 23.5 -49.5t43.5 -117.5t50.5 -178t34 -227.5t5 -269t-47 -300t-112.5 -323.5q-22 -48 -66 -75.5t-95 -27.5q-39 0 -74 16q-67 31 -92.5 100t4.5 136q58 126 90 257.5t37.5 239.5t-3.5 213.5t-26.5 180.5 t-38.5 138.5t-32.5 90t-15.5 32.5q-34 65 -11.5 135.5t87.5 104.5q37 20 81 20q49 0 91.5 -25.5t66.5 -70.5z" />
+<glyph unicode="&#xf1f0;" horiz-adv-x="2304" d="M1975 546h-138q14 37 66 179l3 9q4 10 10 26t9 26l12 -55zM531 611l-58 295q-11 54 -75 54h-268l-2 -13q311 -79 403 -336zM710 960l-162 -438l-17 89q-26 70 -85 129.5t-131 88.5l135 -510h175l261 641h-176zM849 318h166l104 642h-166zM1617 944q-69 27 -149 27 q-123 0 -201 -59t-79 -153q-1 -102 145 -174q48 -23 67 -41t19 -39q0 -30 -30 -46t-69 -16q-86 0 -156 33l-22 11l-23 -144q74 -34 185 -34q130 -1 208.5 59t80.5 160q0 106 -140 174q-49 25 -71 42t-22 38q0 22 24.5 38.5t70.5 16.5q70 1 124 -24l15 -8zM2042 960h-128 q-65 0 -87 -54l-246 -588h174l35 96h212q5 -22 20 -96h154zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf1f1;" horiz-adv-x="2304" d="M671 603h-13q-47 0 -47 -32q0 -22 20 -22q17 0 28 15t12 39zM1066 639h62v3q1 4 0.5 6.5t-1 7t-2 8t-4.5 6.5t-7.5 5t-11.5 2q-28 0 -36 -38zM1606 603h-12q-48 0 -48 -32q0 -22 20 -22q17 0 28 15t12 39zM1925 629q0 41 -30 41q-19 0 -31 -20t-12 -51q0 -42 28 -42 q20 0 32.5 20t12.5 52zM480 770h87l-44 -262h-56l32 201l-71 -201h-39l-4 200l-34 -200h-53l44 262h81l2 -163zM733 663q0 -6 -4 -42q-16 -101 -17 -113h-47l1 22q-20 -26 -58 -26q-23 0 -37.5 16t-14.5 42q0 39 26 60.5t73 21.5q14 0 23 -1q0 3 0.5 5.5t1 4.5t0.5 3 q0 20 -36 20q-29 0 -59 -10q0 4 7 48q38 11 67 11q74 0 74 -62zM889 721l-8 -49q-22 3 -41 3q-27 0 -27 -17q0 -8 4.5 -12t21.5 -11q40 -19 40 -60q0 -72 -87 -71q-34 0 -58 6q0 2 7 49q29 -8 51 -8q32 0 32 19q0 7 -4.5 11.5t-21.5 12.5q-43 20 -43 59q0 72 84 72 q30 0 50 -4zM977 721h28l-7 -52h-29q-2 -17 -6.5 -40.5t-7 -38.5t-2.5 -18q0 -16 19 -16q8 0 16 2l-8 -47q-21 -7 -40 -7q-43 0 -45 47q0 12 8 56q3 20 25 146h55zM1180 648q0 -23 -7 -52h-111q-3 -22 10 -33t38 -11q30 0 58 14l-9 -54q-30 -8 -57 -8q-95 0 -95 95 q0 55 27.5 90.5t69.5 35.5q35 0 55.5 -21t20.5 -56zM1319 722q-13 -23 -22 -62q-22 2 -31 -24t-25 -128h-56l3 14q22 130 29 199h51l-3 -33q14 21 25.5 29.5t28.5 4.5zM1506 763l-9 -57q-28 14 -50 14q-31 0 -51 -27.5t-20 -70.5q0 -30 13.5 -47t38.5 -17q21 0 48 13 l-10 -59q-28 -8 -50 -8q-45 0 -71.5 30.5t-26.5 82.5q0 70 35.5 114.5t91.5 44.5q26 0 61 -13zM1668 663q0 -18 -4 -42q-13 -79 -17 -113h-46l1 22q-20 -26 -59 -26q-23 0 -37 16t-14 42q0 39 25.5 60.5t72.5 21.5q15 0 23 -1q2 7 2 13q0 20 -36 20q-29 0 -59 -10q0 4 8 48 q38 11 67 11q73 0 73 -62zM1809 722q-14 -24 -21 -62q-23 2 -31.5 -23t-25.5 -129h-56l3 14q19 104 29 199h52q0 -11 -4 -33q15 21 26.5 29.5t27.5 4.5zM1950 770h56l-43 -262h-53l3 19q-23 -23 -52 -23q-31 0 -49.5 24t-18.5 64q0 53 27.5 92t64.5 39q31 0 53 -29z M2061 640q0 148 -72.5 273t-198 198t-273.5 73q-181 0 -328 -110q127 -116 171 -284h-50q-44 150 -158 253q-114 -103 -158 -253h-50q44 168 171 284q-147 110 -328 110q-148 0 -273.5 -73t-198 -198t-72.5 -273t72.5 -273t198 -198t273.5 -73q181 0 328 110 q-120 111 -165 264h50q46 -138 152 -233q106 95 152 233h50q-45 -153 -165 -264q147 -110 328 -110q148 0 273.5 73t198 198t72.5 273zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf1f2;" horiz-adv-x="2304" d="M313 759q0 -51 -36 -84q-29 -26 -89 -26h-17v220h17q61 0 89 -27q36 -31 36 -83zM2089 824q0 -52 -64 -52h-19v101h20q63 0 63 -49zM380 759q0 74 -50 120.5t-129 46.5h-95v-333h95q74 0 119 38q60 51 60 128zM410 593h65v333h-65v-333zM730 694q0 40 -20.5 62t-75.5 42 q-29 10 -39.5 19t-10.5 23q0 16 13.5 26.5t34.5 10.5q29 0 53 -27l34 44q-41 37 -98 37q-44 0 -74 -27.5t-30 -67.5q0 -35 18 -55.5t64 -36.5q37 -13 45 -19q19 -12 19 -34q0 -20 -14 -33.5t-36 -13.5q-48 0 -71 44l-42 -40q44 -64 115 -64q51 0 83 30.5t32 79.5zM1008 604 v77q-37 -37 -78 -37q-49 0 -80.5 32.5t-31.5 82.5q0 48 31.5 81.5t77.5 33.5q43 0 81 -38v77q-40 20 -80 20q-74 0 -125.5 -50.5t-51.5 -123.5t51 -123.5t125 -50.5q42 0 81 19zM2240 0v527q-65 -40 -144.5 -84t-237.5 -117t-329.5 -137.5t-417.5 -134.5t-504 -118h1569 q26 0 45 19t19 45zM1389 757q0 75 -53 128t-128 53t-128 -53t-53 -128t53 -128t128 -53t128 53t53 128zM1541 584l144 342h-71l-90 -224l-89 224h-71l142 -342h35zM1714 593h184v56h-119v90h115v56h-115v74h119v57h-184v-333zM2105 593h80l-105 140q76 16 76 94q0 47 -31 73 t-87 26h-97v-333h65v133h9zM2304 1274v-1268q0 -56 -38.5 -95t-93.5 -39h-2040q-55 0 -93.5 39t-38.5 95v1268q0 56 38.5 95t93.5 39h2040q55 0 93.5 -39t38.5 -95z" />
+<glyph unicode="&#xf1f3;" horiz-adv-x="2304" d="M119 854h89l-45 108zM740 328l74 79l-70 79h-163v-49h142v-55h-142v-54h159zM898 406l99 -110v217zM1186 453q0 33 -40 33h-84v-69h83q41 0 41 36zM1475 457q0 29 -42 29h-82v-61h81q43 0 43 32zM1197 923q0 29 -42 29h-82v-60h81q43 0 43 31zM1656 854h89l-44 108z M699 1009v-271h-66v212l-94 -212h-57l-94 212v-212h-132l-25 60h-135l-25 -60h-70l116 271h96l110 -257v257h106l85 -184l77 184h108zM1255 453q0 -20 -5.5 -35t-14 -25t-22.5 -16.5t-26 -10t-31.5 -4.5t-31.5 -1t-32.5 0.5t-29.5 0.5v-91h-126l-80 90l-83 -90h-256v271h260 l80 -89l82 89h207q109 0 109 -89zM964 794v-56h-217v271h217v-57h-152v-49h148v-55h-148v-54h152zM2304 235v-229q0 -55 -38.5 -94.5t-93.5 -39.5h-2040q-55 0 -93.5 39.5t-38.5 94.5v678h111l25 61h55l25 -61h218v46l19 -46h113l20 47v-47h541v99l10 1q10 0 10 -14v-86h279 v23q23 -12 55 -18t52.5 -6.5t63 0.5t51.5 1l25 61h56l25 -61h227v58l34 -58h182v378h-180v-44l-25 44h-185v-44l-23 44h-249q-69 0 -109 -22v22h-172v-22q-24 22 -73 22h-628l-43 -97l-43 97h-198v-44l-22 44h-169l-78 -179v391q0 55 38.5 94.5t93.5 39.5h2040 q55 0 93.5 -39.5t38.5 -94.5v-678h-120q-51 0 -81 -22v22h-177q-55 0 -78 -22v22h-316v-22q-31 22 -87 22h-209v-22q-23 22 -91 22h-234l-54 -58l-50 58h-349v-378h343l55 59l52 -59h211v89h21q59 0 90 13v-102h174v99h8q8 0 10 -2t2 -10v-87h529q57 0 88 24v-24h168 q60 0 95 17zM1546 469q0 -23 -12 -43t-34 -29q25 -9 34 -26t9 -46v-54h-65v45q0 33 -12 43.5t-46 10.5h-69v-99h-65v271h154q48 0 77 -15t29 -58zM1269 936q0 -24 -12.5 -44t-33.5 -29q26 -9 34.5 -25.5t8.5 -46.5v-53h-65q0 9 0.5 26.5t0 25t-3 18.5t-8.5 16t-17.5 8.5 t-29.5 3.5h-70v-98h-64v271l153 -1q49 0 78 -14.5t29 -57.5zM1798 327v-56h-216v271h216v-56h-151v-49h148v-55h-148v-54zM1372 1009v-271h-66v271h66zM2065 357q0 -86 -102 -86h-126v58h126q34 0 34 25q0 16 -17 21t-41.5 5t-49.5 3.5t-42 22.5t-17 55q0 39 26 60t66 21 h130v-57h-119q-36 0 -36 -25q0 -16 17.5 -20.5t42 -4t49 -2.5t42 -21.5t17.5 -54.5zM2304 407v-101q-24 -35 -88 -35h-125v58h125q33 0 33 25q0 13 -12.5 19t-31 5.5t-40 2t-40 8t-31 24t-12.5 48.5q0 39 26.5 60t66.5 21h129v-57h-118q-36 0 -36 -25q0 -20 29 -22t68.5 -5 t56.5 -26zM2139 1008v-270h-92l-122 203v-203h-132l-26 60h-134l-25 -60h-75q-129 0 -129 133q0 138 133 138h63v-59q-7 0 -28 1t-28.5 0.5t-23 -2t-21.5 -6.5t-14.5 -13.5t-11.5 -23t-3 -33.5q0 -38 13.5 -58t49.5 -20h29l92 213h97l109 -256v256h99l114 -188v188h66z" />
+<glyph unicode="&#xf1f4;" horiz-adv-x="2304" d="M745 630q0 -37 -25.5 -61.5t-62.5 -24.5q-29 0 -46.5 16t-17.5 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM1530 779q0 -42 -22 -57t-66 -15l-32 -1l17 107q2 11 13 11h18q22 0 35 -2t25 -12.5t12 -30.5zM1881 630q0 -36 -25.5 -61t-61.5 -25q-29 0 -47 16 t-18 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM513 801q0 59 -38.5 85.5t-100.5 26.5h-160q-19 0 -21 -19l-65 -408q-1 -6 3 -11t10 -5h76q20 0 22 19l18 110q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM822 489l41 261q1 6 -3 11t-10 5h-76 q-14 0 -17 -33q-27 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q28 0 58 12t48 32q-4 -12 -4 -21q0 -16 13 -16h69q19 0 22 19zM1269 752q0 5 -4 9.5t-9 4.5h-77q-11 0 -18 -10l-106 -156l-44 150q-5 16 -22 16h-75q-5 0 -9 -4.5t-4 -9.5q0 -2 19.5 -59 t42 -123t23.5 -70q-82 -112 -82 -120q0 -13 13 -13h77q11 0 18 10l255 368q2 2 2 7zM1649 801q0 59 -38.5 85.5t-100.5 26.5h-159q-20 0 -22 -19l-65 -408q-1 -6 3 -11t10 -5h82q12 0 16 13l18 116q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM1958 489 l41 261q1 6 -3 11t-10 5h-76q-14 0 -17 -33q-26 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q29 0 59 12t47 32q0 -1 -2 -9t-2 -12q0 -16 13 -16h69q19 0 22 19zM2176 898v1q0 14 -13 14h-74q-11 0 -13 -11l-65 -416l-1 -2q0 -5 4 -9.5t10 -4.5h66 q19 0 21 19zM392 764q-5 -35 -26 -46t-60 -11l-33 -1l17 107q2 11 13 11h19q40 0 58 -11.5t12 -48.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf1f5;" horiz-adv-x="2304" d="M1597 633q0 -69 -21 -106q-19 -35 -52 -35q-23 0 -41 9v224q29 30 57 30q57 0 57 -122zM2035 669h-110q6 98 56 98q51 0 54 -98zM476 534q0 59 -33 91.5t-101 57.5q-36 13 -52 24t-16 25q0 26 38 26q58 0 124 -33l18 112q-67 32 -149 32q-77 0 -123 -38q-48 -39 -48 -109 q0 -58 32.5 -90.5t99.5 -56.5q39 -14 54.5 -25.5t15.5 -27.5q0 -31 -48 -31q-29 0 -70 12.5t-72 30.5l-18 -113q72 -41 168 -41q81 0 129 37q51 41 51 117zM771 749l19 111h-96v135l-129 -21l-18 -114l-46 -8l-17 -103h62v-219q0 -84 44 -120q38 -30 111 -30q32 0 79 11v118 q-32 -7 -44 -7q-42 0 -42 50v197h77zM1087 724v139q-15 3 -28 3q-32 0 -55.5 -16t-33.5 -46l-10 56h-131v-471h150v306q26 31 82 31q16 0 26 -2zM1124 389h150v471h-150v-471zM1746 638q0 122 -45 179q-40 52 -111 52q-64 0 -117 -56l-8 47h-132v-645l150 25v151 q36 -11 68 -11q83 0 134 56q61 65 61 202zM1278 986q0 33 -23 56t-56 23t-56 -23t-23 -56t23 -56.5t56 -23.5t56 23.5t23 56.5zM2176 629q0 113 -48 176q-50 64 -144 64q-96 0 -151.5 -66t-55.5 -180q0 -128 63 -188q55 -55 161 -55q101 0 160 40l-16 103q-57 -31 -128 -31 q-43 0 -63 19q-23 19 -28 66h248q2 14 2 52zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf1f6;" horiz-adv-x="2048" d="M1558 684q61 -356 298 -556q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5zM1024 -176q16 0 16 16t-16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5zM2026 1424q8 -10 7.5 -23.5t-10.5 -22.5 l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5 l418 363q10 8 23.5 7t21.5 -11z" />
+<glyph unicode="&#xf1f7;" horiz-adv-x="2048" d="M1040 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM503 315l877 760q-42 88 -132.5 146.5t-223.5 58.5q-93 0 -169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -384 -137 -645zM1856 128 q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5l149 129h757q-166 187 -227 459l111 97q61 -356 298 -556zM1942 1520l84 -96q8 -10 7.5 -23.5t-10.5 -22.5l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161 q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5l418 363q10 8 23.5 7t21.5 -11z" />
+<glyph unicode="&#xf1f8;" horiz-adv-x="1408" d="M512 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM768 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1024 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704 q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167 q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf1f9;" d="M1150 462v-109q0 -50 -36.5 -89t-94 -60.5t-118 -32.5t-117.5 -11q-205 0 -342.5 139t-137.5 346q0 203 136 339t339 136q34 0 75.5 -4.5t93 -18t92.5 -34t69 -56.5t28 -81v-109q0 -16 -16 -16h-118q-16 0 -16 16v70q0 43 -65.5 67.5t-137.5 24.5q-140 0 -228.5 -91.5 t-88.5 -237.5q0 -151 91.5 -249.5t233.5 -98.5q68 0 138 24t70 66v70q0 7 4.5 11.5t10.5 4.5h119q6 0 11 -4.5t5 -11.5zM768 1280q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5 t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf1fa;" d="M972 761q0 108 -53.5 169t-147.5 61q-63 0 -124 -30.5t-110 -84.5t-79.5 -137t-30.5 -180q0 -112 53.5 -173t150.5 -61q96 0 176 66.5t122.5 166t42.5 203.5zM1536 640q0 -111 -37 -197t-98.5 -135t-131.5 -74.5t-145 -27.5q-6 0 -15.5 -0.5t-16.5 -0.5q-95 0 -142 53 q-28 33 -33 83q-52 -66 -131.5 -110t-173.5 -44q-161 0 -249.5 95.5t-88.5 269.5q0 157 66 290t179 210.5t246 77.5q87 0 155 -35.5t106 -99.5l2 19l11 56q1 6 5.5 12t9.5 6h118q5 0 13 -11q5 -5 3 -16l-120 -614q-5 -24 -5 -48q0 -39 12.5 -52t44.5 -13q28 1 57 5.5t73 24 t77 50t57 89.5t24 137q0 292 -174 466t-466 174q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51q228 0 405 144q11 9 24 8t21 -12l41 -49q8 -12 7 -24q-2 -13 -12 -22q-102 -83 -227.5 -128t-258.5 -45q-156 0 -298 61 t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q344 0 556 -212t212 -556z" />
+<glyph unicode="&#xf1fb;" horiz-adv-x="1792" d="M1698 1442q94 -94 94 -226.5t-94 -225.5l-225 -223l104 -104q10 -10 10 -23t-10 -23l-210 -210q-10 -10 -23 -10t-23 10l-105 105l-603 -603q-37 -37 -90 -37h-203l-256 -128l-64 64l128 256v203q0 53 37 90l603 603l-105 105q-10 10 -10 23t10 23l210 210q10 10 23 10 t23 -10l104 -104l223 225q93 94 225.5 94t226.5 -94zM512 64l576 576l-192 192l-576 -576v-192h192z" />
+<glyph unicode="&#xf1fc;" horiz-adv-x="1792" d="M1615 1536q70 0 122.5 -46.5t52.5 -116.5q0 -63 -45 -151q-332 -629 -465 -752q-97 -91 -218 -91q-126 0 -216.5 92.5t-90.5 219.5q0 128 92 212l638 579q59 54 130 54zM706 502q39 -76 106.5 -130t150.5 -76l1 -71q4 -213 -129.5 -347t-348.5 -134q-123 0 -218 46.5 t-152.5 127.5t-86.5 183t-29 220q7 -5 41 -30t62 -44.5t59 -36.5t46 -17q41 0 55 37q25 66 57.5 112.5t69.5 76t88 47.5t103 25.5t125 10.5z" />
+<glyph unicode="&#xf1fd;" horiz-adv-x="1792" d="M1792 128v-384h-1792v384q45 0 85 14t59 27.5t47 37.5q30 27 51.5 38t56.5 11t55.5 -11t52.5 -38q29 -25 47 -38t58 -27t86 -14q45 0 85 14.5t58 27t48 37.5q21 19 32.5 27t31 15t43.5 7q35 0 56.5 -11t51.5 -38q28 -24 47 -37.5t59 -27.5t85 -14t85 14t59 27.5t47 37.5 q30 27 51.5 38t56.5 11q34 0 55.5 -11t51.5 -38q28 -24 47 -37.5t59 -27.5t85 -14zM1792 448v-192q-35 0 -55.5 11t-52.5 38q-29 25 -47 38t-58 27t-85 14q-46 0 -86 -14t-58 -27t-47 -38q-22 -19 -33 -27t-31 -15t-44 -7q-35 0 -56.5 11t-51.5 38q-29 25 -47 38t-58 27 t-86 14q-45 0 -85 -14.5t-58 -27t-48 -37.5q-21 -19 -32.5 -27t-31 -15t-43.5 -7q-35 0 -56.5 11t-51.5 38q-28 24 -47 37.5t-59 27.5t-85 14q-46 0 -86 -14t-58 -27t-47 -38q-30 -27 -51.5 -38t-56.5 -11v192q0 80 56 136t136 56h64v448h256v-448h256v448h256v-448h256v448 h256v-448h64q80 0 136 -56t56 -136zM512 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150zM1024 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51 t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150zM1536 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150z" />
+<glyph unicode="&#xf1fe;" horiz-adv-x="2048" d="M2048 0v-128h-2048v1536h128v-1408h1920zM1664 1024l256 -896h-1664v576l448 576l576 -576z" />
+<glyph unicode="&#xf200;" horiz-adv-x="1792" d="M768 646l546 -546q-106 -108 -247.5 -168t-298.5 -60q-209 0 -385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103v-762zM955 640h773q0 -157 -60 -298.5t-168 -247.5zM1664 768h-768v768q209 0 385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf201;" horiz-adv-x="2048" d="M2048 0v-128h-2048v1536h128v-1408h1920zM1920 1248v-435q0 -21 -19.5 -29.5t-35.5 7.5l-121 121l-633 -633q-10 -10 -23 -10t-23 10l-233 233l-416 -416l-192 192l585 585q10 10 23 10t23 -10l233 -233l464 464l-121 121q-16 16 -7.5 35.5t29.5 19.5h435q14 0 23 -9 t9 -23z" />
+<glyph unicode="&#xf202;" horiz-adv-x="1792" d="M1292 832q0 -6 10 -41q10 -29 25 -49.5t41 -34t44 -20t55 -16.5q325 -91 325 -332q0 -146 -105.5 -242.5t-254.5 -96.5q-59 0 -111.5 18.5t-91.5 45.5t-77 74.5t-63 87.5t-53.5 103.5t-43.5 103t-39.5 106.5t-35.5 95q-32 81 -61.5 133.5t-73.5 96.5t-104 64t-142 20 q-96 0 -183 -55.5t-138 -144.5t-51 -185q0 -160 106.5 -279.5t263.5 -119.5q177 0 258 95q56 63 83 116l84 -152q-15 -34 -44 -70l1 -1q-131 -152 -388 -152q-147 0 -269.5 79t-190.5 207.5t-68 274.5q0 105 43.5 206t116 176.5t172 121.5t204.5 46q87 0 159 -19t123.5 -50 t95 -80t72.5 -99t58.5 -117t50.5 -124.5t50 -130.5t55 -127q96 -200 233 -200q81 0 138.5 48.5t57.5 128.5q0 42 -19 72t-50.5 46t-72.5 31.5t-84.5 27t-87.5 34t-81 52t-65 82t-39 122.5q-3 16 -3 33q0 110 87.5 192t198.5 78q78 -3 120.5 -14.5t90.5 -53.5h-1 q12 -11 23 -24.5t26 -36t19 -27.5l-129 -99q-26 49 -54 70v1q-23 21 -97 21q-49 0 -84 -33t-35 -83z" />
+<glyph unicode="&#xf203;" d="M1432 484q0 173 -234 239q-35 10 -53 16.5t-38 25t-29 46.5q0 2 -2 8.5t-3 12t-1 7.5q0 36 24.5 59.5t60.5 23.5q54 0 71 -15h-1q20 -15 39 -51l93 71q-39 54 -49 64q-33 29 -67.5 39t-85.5 10q-80 0 -142 -57.5t-62 -137.5q0 -7 2 -23q16 -96 64.5 -140t148.5 -73 q29 -8 49 -15.5t45 -21.5t38.5 -34.5t13.5 -46.5v-5q1 -58 -40.5 -93t-100.5 -35q-97 0 -167 144q-23 47 -51.5 121.5t-48 125.5t-54 110.5t-74 95.5t-103.5 60.5t-147 24.5q-101 0 -192 -56t-144 -148t-50 -192v-1q4 -108 50.5 -199t133.5 -147.5t196 -56.5q186 0 279 110 q20 27 31 51l-60 109q-42 -80 -99 -116t-146 -36q-115 0 -191 87t-76 204q0 105 82 189t186 84q112 0 170 -53.5t104 -172.5q8 -21 25.5 -68.5t28.5 -76.5t31.5 -74.5t38.5 -74t45.5 -62.5t55.5 -53.5t66 -33t80 -13.5q107 0 183 69.5t76 174.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf204;" horiz-adv-x="2048" d="M1152 640q0 104 -40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5zM1920 640q0 104 -40.5 198.5 t-109.5 163.5t-163.5 109.5t-198.5 40.5h-386q119 -90 188.5 -224t69.5 -288t-69.5 -288t-188.5 -224h386q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5zM2048 640q0 -130 -51 -248.5t-136.5 -204t-204 -136.5t-248.5 -51h-768q-130 0 -248.5 51t-204 136.5 t-136.5 204t-51 248.5t51 248.5t136.5 204t204 136.5t248.5 51h768q130 0 248.5 -51t204 -136.5t136.5 -204t51 -248.5z" />
+<glyph unicode="&#xf205;" horiz-adv-x="2048" d="M0 640q0 130 51 248.5t136.5 204t204 136.5t248.5 51h768q130 0 248.5 -51t204 -136.5t136.5 -204t51 -248.5t-51 -248.5t-136.5 -204t-204 -136.5t-248.5 -51h-768q-130 0 -248.5 51t-204 136.5t-136.5 204t-51 248.5zM1408 128q104 0 198.5 40.5t163.5 109.5 t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5z" />
+<glyph unicode="&#xf206;" horiz-adv-x="2304" d="M762 384h-314q-40 0 -57.5 35t6.5 67l188 251q-65 31 -137 31q-132 0 -226 -94t-94 -226t94 -226t226 -94q115 0 203 72.5t111 183.5zM576 512h186q-18 85 -75 148zM1056 512l288 384h-480l-99 -132q105 -103 126 -252h165zM2176 448q0 132 -94 226t-226 94 q-60 0 -121 -24l174 -260q15 -23 10 -49t-27 -40q-15 -11 -36 -11q-35 0 -53 29l-174 260q-93 -95 -93 -225q0 -132 94 -226t226 -94t226 94t94 226zM2304 448q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 97 39.5 183.5t109.5 149.5l-65 98l-353 -469 q-18 -26 -51 -26h-197q-23 -164 -149 -274t-294 -110q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q114 0 215 -55l137 183h-224q-26 0 -45 19t-19 45t19 45t45 19h384v-128h435l-85 128h-222q-26 0 -45 19t-19 45t19 45t45 19h256q33 0 53 -28l267 -400 q91 44 192 44q185 0 316.5 -131.5t131.5 -316.5z" />
+<glyph unicode="&#xf207;" d="M384 320q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 320q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1362 716l-72 384q-5 23 -22.5 37.5t-40.5 14.5 h-918q-23 0 -40.5 -14.5t-22.5 -37.5l-72 -384q-5 -30 14 -53t49 -23h1062q30 0 49 23t14 53zM1136 1328q0 20 -14 34t-34 14h-640q-20 0 -34 -14t-14 -34t14 -34t34 -14h640q20 0 34 14t14 34zM1536 603v-603h-128v-128q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5v128h-768v-128q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5v128h-128v603q0 112 25 223l103 454q9 78 97.5 137t230 89t312.5 30t312.5 -30t230 -89t97.5 -137l105 -454q23 -102 23 -223z" />
+<glyph unicode="&#xf208;" horiz-adv-x="2048" d="M1463 704q0 -35 -25 -60.5t-61 -25.5h-702q-36 0 -61 25.5t-25 60.5t25 60.5t61 25.5h702q36 0 61 -25.5t25 -60.5zM1677 704q0 86 -23 170h-982q-36 0 -61 25t-25 60q0 36 25 61t61 25h908q-88 143 -235 227t-320 84q-177 0 -327.5 -87.5t-238 -237.5t-87.5 -327 q0 -86 23 -170h982q36 0 61 -25t25 -60q0 -36 -25 -61t-61 -25h-908q88 -143 235.5 -227t320.5 -84q132 0 253 51.5t208 139t139 208t52 253.5zM2048 959q0 -35 -25 -60t-61 -25h-131q17 -85 17 -170q0 -167 -65.5 -319.5t-175.5 -263t-262.5 -176t-319.5 -65.5 q-246 0 -448.5 133t-301.5 350h-189q-36 0 -61 25t-25 61q0 35 25 60t61 25h132q-17 85 -17 170q0 167 65.5 319.5t175.5 263t262.5 176t320.5 65.5q245 0 447.5 -133t301.5 -350h188q36 0 61 -25t25 -61z" />
+<glyph unicode="&#xf209;" horiz-adv-x="1280" d="M953 1158l-114 -328l117 -21q165 451 165 518q0 56 -38 56q-57 0 -130 -225zM654 471l33 -88q37 42 71 67l-33 5.5t-38.5 7t-32.5 8.5zM362 1367q0 -98 159 -521q18 10 49 10q15 0 75 -5l-121 351q-75 220 -123 220q-19 0 -29 -17.5t-10 -37.5zM283 608q0 -36 51.5 -119 t117.5 -153t100 -70q14 0 25.5 13t11.5 27q0 24 -32 102q-13 32 -32 72t-47.5 89t-61.5 81t-62 32q-20 0 -45.5 -27t-25.5 -47zM125 273q0 -41 25 -104q59 -145 183.5 -227t281.5 -82q227 0 382 170q152 169 152 427q0 43 -1 67t-11.5 62t-30.5 56q-56 49 -211.5 75.5 t-270.5 26.5q-37 0 -49 -11q-12 -5 -12 -35q0 -34 21.5 -60t55.5 -40t77.5 -23.5t87.5 -11.5t85 -4t70 0h23q24 0 40 -19q15 -19 19 -55q-28 -28 -96 -54q-61 -22 -93 -46q-64 -46 -108.5 -114t-44.5 -137q0 -31 18.5 -88.5t18.5 -87.5l-3 -12q-4 -12 -4 -14 q-137 10 -146 216q-8 -2 -41 -2q2 -7 2 -21q0 -53 -40.5 -89.5t-94.5 -36.5q-82 0 -166.5 78t-84.5 159q0 34 33 67q52 -64 60 -76q77 -104 133 -104q12 0 26.5 8.5t14.5 20.5q0 34 -87.5 145t-116.5 111q-43 0 -70 -44.5t-27 -90.5zM11 264q0 101 42.5 163t136.5 88 q-28 74 -28 104q0 62 61 123t122 61q29 0 70 -15q-163 462 -163 567q0 80 41 130.5t119 50.5q131 0 325 -581q6 -17 8 -23q6 16 29 79.5t43.5 118.5t54 127.5t64.5 123t70.5 86.5t76.5 36q71 0 112 -49t41 -122q0 -108 -159 -550q61 -15 100.5 -46t58.5 -78t26 -93.5 t7 -110.5q0 -150 -47 -280t-132 -225t-211 -150t-278 -55q-111 0 -223 42q-149 57 -258 191.5t-109 286.5z" />
+<glyph unicode="&#xf20a;" horiz-adv-x="2048" d="M785 528h207q-14 -158 -98.5 -248.5t-214.5 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-203q-5 64 -35.5 99t-81.5 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t40 -51.5t66 -18q95 0 109 139zM1497 528h206 q-14 -158 -98 -248.5t-214 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-204q-4 64 -35 99t-81 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t39.5 -51.5t65.5 -18q49 0 76.5 38t33.5 101zM1856 647q0 207 -15.5 307 t-60.5 161q-6 8 -13.5 14t-21.5 15t-16 11q-86 63 -697 63q-625 0 -710 -63q-5 -4 -17.5 -11.5t-21 -14t-14.5 -14.5q-45 -60 -60 -159.5t-15 -308.5q0 -208 15 -307.5t60 -160.5q6 -8 15 -15t20.5 -14t17.5 -12q44 -33 239.5 -49t470.5 -16q610 0 697 65q5 4 17 11t20.5 14 t13.5 16q46 60 61 159t15 309zM2048 1408v-1536h-2048v1536h2048z" />
+<glyph unicode="&#xf20b;" d="M992 912v-496q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v496q0 112 -80 192t-192 80h-272v-1152q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v1344q0 14 9 23t23 9h464q135 0 249 -66.5t180.5 -180.5t66.5 -249zM1376 1376v-880q0 -135 -66.5 -249t-180.5 -180.5 t-249 -66.5h-464q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h160q14 0 23 -9t9 -23v-768h272q112 0 192 80t80 192v880q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
+<glyph unicode="&#xf20c;" d="M1311 694v-114q0 -24 -13.5 -38t-37.5 -14h-202q-24 0 -38 14t-14 38v114q0 24 14 38t38 14h202q24 0 37.5 -14t13.5 -38zM821 464v250q0 53 -32.5 85.5t-85.5 32.5h-133q-68 0 -96 -52q-28 52 -96 52h-130q-53 0 -85.5 -32.5t-32.5 -85.5v-250q0 -22 21 -22h55 q22 0 22 22v230q0 24 13.5 38t38.5 14h94q24 0 38 -14t14 -38v-230q0 -22 21 -22h54q22 0 22 22v230q0 24 14 38t38 14h97q24 0 37.5 -14t13.5 -38v-230q0 -22 22 -22h55q21 0 21 22zM1410 560v154q0 53 -33 85.5t-86 32.5h-264q-53 0 -86 -32.5t-33 -85.5v-410 q0 -21 22 -21h55q21 0 21 21v180q31 -42 94 -42h191q53 0 86 32.5t33 85.5zM1536 1176v-1072q0 -96 -68 -164t-164 -68h-1072q-96 0 -164 68t-68 164v1072q0 96 68 164t164 68h1072q96 0 164 -68t68 -164z" />
+<glyph unicode="&#xf20d;" d="M915 450h-294l147 551zM1001 128h311l-324 1024h-440l-324 -1024h311l383 314zM1536 1120v-960q0 -118 -85 -203t-203 -85h-960q-118 0 -203 85t-85 203v960q0 118 85 203t203 85h960q118 0 203 -85t85 -203z" />
+<glyph unicode="&#xf20e;" horiz-adv-x="2048" d="M2048 641q0 -21 -13 -36.5t-33 -19.5l-205 -356q3 -9 3 -18q0 -20 -12.5 -35.5t-32.5 -19.5l-193 -337q3 -8 3 -16q0 -23 -16.5 -40t-40.5 -17q-25 0 -41 18h-400q-17 -20 -43 -20t-43 20h-399q-17 -20 -43 -20q-23 0 -40 16.5t-17 40.5q0 8 4 20l-193 335 q-20 4 -32.5 19.5t-12.5 35.5q0 9 3 18l-206 356q-20 5 -32.5 20.5t-12.5 35.5q0 21 13.5 36.5t33.5 19.5l199 344q0 1 -0.5 3t-0.5 3q0 36 34 51l209 363q-4 10 -4 18q0 24 17 40.5t40 16.5q26 0 44 -21h396q16 21 43 21t43 -21h398q18 21 44 21q23 0 40 -16.5t17 -40.5 q0 -6 -4 -18l207 -358q23 -1 39 -17.5t16 -38.5q0 -13 -7 -27l187 -324q19 -4 31.5 -19.5t12.5 -35.5zM1063 -158h389l-342 354h-143l-342 -354h360q18 16 39 16t39 -16zM112 654q1 -4 1 -13q0 -10 -2 -15l208 -360q2 0 4.5 -1t5.5 -2.5l5 -2.5l188 199v347l-187 194 q-13 -8 -29 -10zM986 1438h-388l190 -200l554 200h-280q-16 -16 -38 -16t-38 16zM1689 226q1 6 5 11l-64 68l-17 -79h76zM1583 226l22 105l-252 266l-296 -307l63 -64h463zM1495 -142l16 28l65 310h-427l333 -343q8 4 13 5zM578 -158h5l342 354h-373v-335l4 -6q14 -5 22 -13 zM552 226h402l64 66l-309 321l-157 -166v-221zM359 226h163v189l-168 -177q4 -8 5 -12zM358 1051q0 -1 0.5 -2t0.5 -2q0 -16 -8 -29l171 -177v269zM552 1121v-311l153 -157l297 314l-223 236zM556 1425l-4 -8v-264l205 74l-191 201q-6 -2 -10 -3zM1447 1438h-16l-621 -224 l213 -225zM1023 946l-297 -315l311 -319l296 307zM688 634l-136 141v-284zM1038 270l-42 -44h85zM1374 618l238 -251l132 624l-3 5l-1 1zM1718 1018q-8 13 -8 29v2l-216 376q-5 1 -13 5l-437 -463l310 -327zM522 1142v223l-163 -282zM522 196h-163l163 -283v283zM1607 196 l-48 -227l130 227h-82zM1729 266l207 361q-2 10 -2 14q0 1 3 16l-171 296l-129 -612l77 -82q5 3 15 7z" />
+<glyph unicode="&#xf210;" d="M0 856q0 131 91.5 226.5t222.5 95.5h742l352 358v-1470q0 -132 -91.5 -227t-222.5 -95h-780q-131 0 -222.5 95t-91.5 227v790zM1232 102l-176 180v425q0 46 -32 79t-78 33h-484q-46 0 -78 -33t-32 -79v-492q0 -46 32.5 -79.5t77.5 -33.5h770z" />
+<glyph unicode="&#xf211;" d="M934 1386q-317 -121 -556 -362.5t-358 -560.5q-20 89 -20 176q0 208 102.5 384.5t278.5 279t384 102.5q82 0 169 -19zM1203 1267q93 -65 164 -155q-389 -113 -674.5 -400.5t-396.5 -676.5q-93 72 -155 162q112 386 395 671t667 399zM470 -67q115 356 379.5 622t619.5 384 q40 -92 54 -195q-292 -120 -516 -345t-343 -518q-103 14 -194 52zM1536 -125q-193 50 -367 115q-135 -84 -290 -107q109 205 274 370.5t369 275.5q-21 -152 -101 -284q65 -175 115 -370z" />
+<glyph unicode="&#xf212;" horiz-adv-x="2048" d="M1893 1144l155 -1272q-131 0 -257 57q-200 91 -393 91q-226 0 -374 -148q-148 148 -374 148q-193 0 -393 -91q-128 -57 -252 -57h-5l155 1272q224 127 482 127q233 0 387 -106q154 106 387 106q258 0 482 -127zM1398 157q129 0 232 -28.5t260 -93.5l-124 1021 q-171 78 -368 78q-224 0 -374 -141q-150 141 -374 141q-197 0 -368 -78l-124 -1021q105 43 165.5 65t148.5 39.5t178 17.5q202 0 374 -108q172 108 374 108zM1438 191l-55 907q-211 -4 -359 -155q-152 155 -374 155q-176 0 -336 -66l-114 -941q124 51 228.5 76t221.5 25 q209 0 374 -102q172 107 374 102z" />
+<glyph unicode="&#xf213;" horiz-adv-x="2048" d="M1500 165v733q0 21 -15 36t-35 15h-93q-20 0 -35 -15t-15 -36v-733q0 -20 15 -35t35 -15h93q20 0 35 15t15 35zM1216 165v531q0 20 -15 35t-35 15h-101q-20 0 -35 -15t-15 -35v-531q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM924 165v429q0 20 -15 35t-35 15h-101 q-20 0 -35 -15t-15 -35v-429q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM632 165v362q0 20 -15 35t-35 15h-101q-20 0 -35 -15t-15 -35v-362q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM2048 311q0 -166 -118 -284t-284 -118h-1244q-166 0 -284 118t-118 284 q0 116 63 214.5t168 148.5q-10 34 -10 73q0 113 80.5 193.5t193.5 80.5q102 0 180 -67q45 183 194 300t338 117q149 0 275 -73.5t199.5 -199.5t73.5 -275q0 -66 -14 -122q135 -33 221 -142.5t86 -247.5z" />
+<glyph unicode="&#xf214;" d="M0 1536h1536v-1392l-776 -338l-760 338v1392zM1436 209v926h-1336v-926l661 -294zM1436 1235v201h-1336v-201h1336zM181 937v-115h-37v115h37zM181 789v-115h-37v115h37zM181 641v-115h-37v115h37zM181 493v-115h-37v115h37zM181 345v-115h-37v115h37zM207 202l15 34 l105 -47l-15 -33zM343 142l15 34l105 -46l-15 -34zM478 82l15 34l105 -46l-15 -34zM614 23l15 33l104 -46l-15 -34zM797 10l105 46l15 -33l-105 -47zM932 70l105 46l15 -34l-105 -46zM1068 130l105 46l15 -34l-105 -46zM1203 189l105 47l15 -34l-105 -46zM259 1389v-36h-114 v36h114zM421 1389v-36h-115v36h115zM583 1389v-36h-115v36h115zM744 1389v-36h-114v36h114zM906 1389v-36h-114v36h114zM1068 1389v-36h-115v36h115zM1230 1389v-36h-115v36h115zM1391 1389v-36h-114v36h114zM181 1049v-79h-37v115h115v-36h-78zM421 1085v-36h-115v36h115z M583 1085v-36h-115v36h115zM744 1085v-36h-114v36h114zM906 1085v-36h-114v36h114zM1068 1085v-36h-115v36h115zM1230 1085v-36h-115v36h115zM1355 970v79h-78v36h115v-115h-37zM1355 822v115h37v-115h-37zM1355 674v115h37v-115h-37zM1355 526v115h37v-115h-37zM1355 378 v115h37v-115h-37zM1355 230v115h37v-115h-37zM760 265q-129 0 -221 91.5t-92 221.5q0 129 92 221t221 92q130 0 221.5 -92t91.5 -221q0 -130 -91.5 -221.5t-221.5 -91.5zM595 646q0 -36 19.5 -56.5t49.5 -25t64 -7t64 -2t49.5 -9t19.5 -30.5q0 -49 -112 -49q-97 0 -123 51 h-3l-31 -63q67 -42 162 -42q29 0 56.5 5t55.5 16t45.5 33t17.5 53q0 46 -27.5 69.5t-67.5 27t-79.5 3t-67 5t-27.5 25.5q0 21 20.5 33t40.5 15t41 3q34 0 70.5 -11t51.5 -34h3l30 58q-3 1 -21 8.5t-22.5 9t-19.5 7t-22 7t-20 4.5t-24 4t-23 1q-29 0 -56.5 -5t-54 -16.5 t-43 -34t-16.5 -53.5z" />
+<glyph unicode="&#xf215;" horiz-adv-x="2048" d="M863 504q0 112 -79.5 191.5t-191.5 79.5t-191 -79.5t-79 -191.5t79 -191t191 -79t191.5 79t79.5 191zM1726 505q0 112 -79 191t-191 79t-191.5 -79t-79.5 -191q0 -113 79.5 -192t191.5 -79t191 79.5t79 191.5zM2048 1314v-1348q0 -44 -31.5 -75.5t-76.5 -31.5h-1832 q-45 0 -76.5 31.5t-31.5 75.5v1348q0 44 31.5 75.5t76.5 31.5h431q44 0 76 -31.5t32 -75.5v-161h754v161q0 44 32 75.5t76 31.5h431q45 0 76.5 -31.5t31.5 -75.5z" />
+<glyph unicode="&#xf216;" horiz-adv-x="2048" d="M1430 953zM1690 749q148 0 253 -98.5t105 -244.5q0 -157 -109 -261.5t-267 -104.5q-85 0 -162 27.5t-138 73.5t-118 106t-109 126.5t-103.5 132.5t-108.5 126t-117 106t-136 73.5t-159 27.5q-154 0 -251.5 -91.5t-97.5 -244.5q0 -157 104 -250t263 -93q100 0 208 37.5 t193 98.5q5 4 21 18.5t30 24t22 9.5q14 0 24.5 -10.5t10.5 -24.5q0 -24 -60 -77q-101 -88 -234.5 -142t-260.5 -54q-133 0 -245.5 58t-180 165t-67.5 241q0 205 141.5 341t347.5 136q120 0 226.5 -43.5t185.5 -113t151.5 -153t139 -167.5t133.5 -153.5t149.5 -113 t172.5 -43.5q102 0 168.5 61.5t66.5 162.5q0 95 -64.5 159t-159.5 64q-30 0 -81.5 -18.5t-68.5 -18.5q-20 0 -35.5 15t-15.5 35q0 18 8.5 57t8.5 59q0 159 -107.5 263t-266.5 104q-58 0 -111.5 -18.5t-84 -40.5t-55.5 -40.5t-33 -18.5q-15 0 -25.5 10.5t-10.5 25.5 q0 19 25 46q59 67 147 103.5t182 36.5q191 0 318 -125.5t127 -315.5q0 -37 -4 -66q57 15 115 15z" />
+<glyph unicode="&#xf217;" horiz-adv-x="1664" d="M1216 832q0 26 -19 45t-45 19h-128v128q0 26 -19 45t-45 19t-45 -19t-19 -45v-128h-128q-26 0 -45 -19t-19 -45t19 -45t45 -19h128v-128q0 -26 19 -45t45 -19t45 19t19 45v128h128q26 0 45 19t19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf218;" horiz-adv-x="1664" d="M1280 832q0 26 -19 45t-45 19t-45 -19l-147 -146v293q0 26 -19 45t-45 19t-45 -19t-19 -45v-293l-147 146q-19 19 -45 19t-45 -19t-19 -45t19 -45l256 -256q19 -19 45 -19t45 19l256 256q19 19 19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf219;" horiz-adv-x="2048" d="M212 768l623 -665l-300 665h-323zM1024 -4l349 772h-698zM538 896l204 384h-262l-288 -384h346zM1213 103l623 665h-323zM683 896h682l-204 384h-274zM1510 896h346l-288 384h-262zM1651 1382l384 -512q14 -18 13 -41.5t-17 -40.5l-960 -1024q-18 -20 -47 -20t-47 20 l-960 1024q-16 17 -17 40.5t13 41.5l384 512q18 26 51 26h1152q33 0 51 -26z" />
+<glyph unicode="&#xf21a;" horiz-adv-x="2048" d="M1811 -19q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83 q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83 q19 19 45 19t45 -19l83 -83zM237 19q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -82l83 82q19 19 45 19t45 -19l83 -82l64 64v293l-210 314q-17 26 -7 56.5t40 40.5l177 58v299h128v128h256v128h256v-128h256v-128h128v-299l177 -58q30 -10 40 -40.5t-7 -56.5l-210 -314 v-293l19 18q19 19 45 19t45 -19l83 -82l83 82q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83 q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83zM640 1152v-128l384 128l384 -128v128h-128v128h-512v-128h-128z" />
+<glyph unicode="&#xf21b;" d="M576 0l96 448l-96 128l-128 64zM832 0l128 640l-128 -64l-96 -128zM992 1010q-2 4 -4 6q-10 8 -96 8q-70 0 -167 -19q-7 -2 -21 -2t-21 2q-97 19 -167 19q-86 0 -96 -8q-2 -2 -4 -6q2 -18 4 -27q2 -3 7.5 -6.5t7.5 -10.5q2 -4 7.5 -20.5t7 -20.5t7.5 -17t8.5 -17t9 -14 t12 -13.5t14 -9.5t17.5 -8t20.5 -4t24.5 -2q36 0 59 12.5t32.5 30t14.5 34.5t11.5 29.5t17.5 12.5h12q11 0 17.5 -12.5t11.5 -29.5t14.5 -34.5t32.5 -30t59 -12.5q13 0 24.5 2t20.5 4t17.5 8t14 9.5t12 13.5t9 14t8.5 17t7.5 17t7 20.5t7.5 20.5q2 7 7.5 10.5t7.5 6.5 q2 9 4 27zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 61 4.5 118t19 125.5t37.5 123.5t63.5 103.5t93.5 74.5l-90 220h214q-22 64 -22 128q0 12 2 32q-194 40 -194 96q0 57 210 99q17 62 51.5 134t70.5 114q32 37 76 37q30 0 84 -31t84 -31t84 31 t84 31q44 0 76 -37q36 -42 70.5 -114t51.5 -134q210 -42 210 -99q0 -56 -194 -96q7 -81 -20 -160h214l-82 -225q63 -33 107.5 -96.5t65.5 -143.5t29 -151.5t8 -148.5z" />
+<glyph unicode="&#xf21c;" horiz-adv-x="2304" d="M2301 500q12 -103 -22 -198.5t-99 -163.5t-158.5 -106t-196.5 -31q-161 11 -279.5 125t-134.5 274q-12 111 27.5 210.5t118.5 170.5l-71 107q-96 -80 -151 -194t-55 -244q0 -27 -18.5 -46.5t-45.5 -19.5h-256h-69q-23 -164 -149 -274t-294 -110q-185 0 -316.5 131.5 t-131.5 316.5t131.5 316.5t316.5 131.5q76 0 152 -27l24 45q-123 110 -304 110h-64q-26 0 -45 19t-19 45t19 45t45 19h128q78 0 145 -13.5t116.5 -38.5t71.5 -39.5t51 -36.5h512h115l-85 128h-222q-30 0 -49 22.5t-14 52.5q4 23 23 38t43 15h253q33 0 53 -28l70 -105 l114 114q19 19 46 19h101q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-179l115 -172q131 63 275 36q143 -26 244 -134.5t118 -253.5zM448 128q115 0 203 72.5t111 183.5h-314q-35 0 -55 31q-18 32 -1 63l147 277q-47 13 -91 13q-132 0 -226 -94t-94 -226t94 -226 t226 -94zM1856 128q132 0 226 94t94 226t-94 226t-226 94q-60 0 -121 -24l174 -260q15 -23 10 -49t-27 -40q-15 -11 -36 -11q-35 0 -53 29l-174 260q-93 -95 -93 -225q0 -132 94 -226t226 -94z" />
+<glyph unicode="&#xf21d;" d="M1408 0q0 -63 -61.5 -113.5t-164 -81t-225 -46t-253.5 -15.5t-253.5 15.5t-225 46t-164 81t-61.5 113.5q0 49 33 88.5t91 66.5t118 44.5t131 29.5q26 5 48 -10.5t26 -41.5q5 -26 -10.5 -48t-41.5 -26q-58 -10 -106 -23.5t-76.5 -25.5t-48.5 -23.5t-27.5 -19.5t-8.5 -12 q3 -11 27 -26.5t73 -33t114 -32.5t160.5 -25t201.5 -10t201.5 10t160.5 25t114 33t73 33.5t27 27.5q-1 4 -8.5 11t-27.5 19t-48.5 23.5t-76.5 25t-106 23.5q-26 4 -41.5 26t-10.5 48q4 26 26 41.5t48 10.5q71 -12 131 -29.5t118 -44.5t91 -66.5t33 -88.5zM1024 896v-384 q0 -26 -19 -45t-45 -19h-64v-384q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v384h-64q-26 0 -45 19t-19 45v384q0 53 37.5 90.5t90.5 37.5h384q53 0 90.5 -37.5t37.5 -90.5zM928 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5 t158.5 -65.5t65.5 -158.5z" />
+<glyph unicode="&#xf21e;" horiz-adv-x="1792" d="M1280 512h305q-5 -6 -10 -10.5t-9 -7.5l-3 -4l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-5 2 -21 20h369q22 0 39.5 13.5t22.5 34.5l70 281l190 -667q6 -20 23 -33t39 -13q21 0 38 13t23 33l146 485l56 -112q18 -35 57 -35zM1792 940q0 -145 -103 -300h-369l-111 221 q-8 17 -25.5 27t-36.5 8q-45 -5 -56 -46l-129 -430l-196 686q-6 20 -23.5 33t-39.5 13t-39 -13.5t-22 -34.5l-116 -464h-423q-103 155 -103 300q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124 t127 -344z" />
+<glyph unicode="&#xf221;" horiz-adv-x="1280" d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292 q11 134 80.5 249t182 188t245.5 88q170 19 319 -54t236 -212t87 -306zM128 960q0 -185 131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5z" />
+<glyph unicode="&#xf222;" d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-382 -383q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5 q203 0 359 -126l382 382h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf223;" horiz-adv-x="1280" d="M830 1220q145 -72 233.5 -210.5t88.5 -305.5q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5 t-147.5 384.5q0 167 88.5 305.5t233.5 210.5q-165 96 -228 273q-6 16 3.5 29.5t26.5 13.5h69q21 0 29 -20q44 -106 140 -171t214 -65t214 65t140 171q8 20 37 20h61q17 0 26.5 -13.5t3.5 -29.5q-63 -177 -228 -273zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf224;" d="M1024 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64 q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-149 16 -270.5 103t-186.5 223.5t-53 291.5q16 204 160 353.5t347 172.5q118 14 228 -19t198 -103l255 254h-134q-14 0 -23 9t-9 23v64zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf225;" horiz-adv-x="1792" d="M1280 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64 q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5t-147.5 384.5q0 201 126 359l-52 53l-101 -111q-9 -10 -22 -10.5t-23 7.5l-48 44q-10 8 -10.5 21.5t8.5 23.5l105 115l-111 112v-134q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9 t-9 23v288q0 26 19 45t45 19h288q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-133l106 -107l86 94q9 10 22 10.5t23 -7.5l48 -44q10 -8 10.5 -21.5t-8.5 -23.5l-90 -99l57 -56q158 126 359 126t359 -126l255 254h-134q-14 0 -23 9t-9 23v64zM832 256q185 0 316.5 131.5 t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf226;" horiz-adv-x="1792" d="M1790 1007q12 -155 -52.5 -292t-186 -224t-271.5 -103v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-512v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23 t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292q17 206 164.5 356.5t352.5 169.5q206 21 377 -94q171 115 377 94q205 -19 352.5 -169.5t164.5 -356.5zM896 647q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM576 512q115 0 218 57q-154 165 -154 391 q0 224 154 391q-103 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5zM1152 128v260q-137 15 -256 94q-119 -79 -256 -94v-260h512zM1216 512q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5q-115 0 -218 -57q154 -167 154 -391 q0 -226 -154 -391q103 -57 218 -57z" />
+<glyph unicode="&#xf227;" horiz-adv-x="1920" d="M1536 1120q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-31 -182 -166 -312t-318 -156q-210 -29 -384.5 80t-241.5 300q-117 6 -221 57.5t-177.5 133t-113.5 192.5t-32 230 q9 135 78 252t182 191.5t248 89.5q118 14 227.5 -19t198.5 -103l255 254h-134q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q59 -74 93 -169q182 -9 328 -124l255 254h-134q-14 0 -23 9 t-9 23v64zM1024 704q0 20 -4 58q-162 -25 -271 -150t-109 -292q0 -20 4 -58q162 25 271 150t109 292zM128 704q0 -168 111 -294t276 -149q-3 29 -3 59q0 210 135 369.5t338 196.5q-53 120 -163.5 193t-245.5 73q-185 0 -316.5 -131.5t-131.5 -316.5zM1088 -128 q185 0 316.5 131.5t131.5 316.5q0 168 -111 294t-276 149q3 -29 3 -59q0 -210 -135 -369.5t-338 -196.5q53 -120 163.5 -193t245.5 -73z" />
+<glyph unicode="&#xf228;" horiz-adv-x="2048" d="M1664 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-32 -180 -164.5 -310t-313.5 -157q-223 -34 -409 90q-117 -78 -256 -93v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23 t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-155 17 -279.5 109.5t-187 237.5t-39.5 307q25 187 159.5 322.5t320.5 164.5q224 34 410 -90q146 97 320 97q201 0 359 -126l255 254h-134q-14 0 -23 9 t-9 23v64zM896 391q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM128 704q0 -185 131.5 -316.5t316.5 -131.5q117 0 218 57q-154 167 -154 391t154 391q-101 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5zM1216 256q185 0 316.5 131.5t131.5 316.5 t-131.5 316.5t-316.5 131.5q-117 0 -218 -57q154 -167 154 -391t-154 -391q101 -57 218 -57z" />
+<glyph unicode="&#xf229;" d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-213 -214l140 -140q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-140 141l-78 -79q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5 t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5q203 0 359 -126l78 78l-172 172q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l172 -172l213 213h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf22a;" horiz-adv-x="1280" d="M640 892q217 -24 364.5 -187.5t147.5 -384.5q0 -167 -87 -306t-236 -212t-319 -54q-133 15 -245.5 88t-182 188t-80.5 249q-12 155 52.5 292t186 224t271.5 103v132h-160q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h160v165l-92 -92q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22 t9 23l202 201q19 19 45 19t45 -19l202 -201q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-92 92v-165h160q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-160v-132zM576 -128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5 t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf22b;" horiz-adv-x="2048" d="M1901 621q19 -19 19 -45t-19 -45l-294 -294q-9 -10 -22.5 -10t-22.5 10l-45 45q-10 9 -10 22.5t10 22.5l185 185h-294v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-132q-24 -217 -187.5 -364.5t-384.5 -147.5q-167 0 -306 87t-212 236t-54 319q15 133 88 245.5 t188 182t249 80.5q155 12 292 -52.5t224 -186t103 -271.5h132v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224h294l-185 185q-10 9 -10 22.5t10 22.5l45 45q9 10 22.5 10t22.5 -10zM576 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5 t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf22c;" horiz-adv-x="1280" d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-612q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v612q-217 24 -364.5 187.5t-147.5 384.5q0 117 45.5 223.5t123 184t184 123t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5zM576 512q185 0 316.5 131.5 t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf22d;" horiz-adv-x="1280" d="M1024 576q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1152 576q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123 t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5z" />
+<glyph unicode="&#xf22e;" horiz-adv-x="1792" />
+<glyph unicode="&#xf22f;" horiz-adv-x="1792" />
+<glyph unicode="&#xf230;" d="M1451 1408q35 0 60 -25t25 -60v-1366q0 -35 -25 -60t-60 -25h-391v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-735q-35 0 -60 25t-25 60v1366q0 35 25 60t60 25h1366z" />
+<glyph unicode="&#xf231;" horiz-adv-x="1280" d="M0 939q0 108 37.5 203.5t103.5 166.5t152 123t185 78t202 26q158 0 294 -66.5t221 -193.5t85 -287q0 -96 -19 -188t-60 -177t-100 -149.5t-145 -103t-189 -38.5q-68 0 -135 32t-96 88q-10 -39 -28 -112.5t-23.5 -95t-20.5 -71t-26 -71t-32 -62.5t-46 -77.5t-62 -86.5 l-14 -5l-9 10q-15 157 -15 188q0 92 21.5 206.5t66.5 287.5t52 203q-32 65 -32 169q0 83 52 156t132 73q61 0 95 -40.5t34 -102.5q0 -66 -44 -191t-44 -187q0 -63 45 -104.5t109 -41.5q55 0 102 25t78.5 68t56 95t38 110.5t20 111t6.5 99.5q0 173 -109.5 269.5t-285.5 96.5 q-200 0 -334 -129.5t-134 -328.5q0 -44 12.5 -85t27 -65t27 -45.5t12.5 -30.5q0 -28 -15 -73t-37 -45q-2 0 -17 3q-51 15 -90.5 56t-61 94.5t-32.5 108t-11 106.5z" />
+<glyph unicode="&#xf232;" d="M985 562q13 0 97.5 -44t89.5 -53q2 -5 2 -15q0 -33 -17 -76q-16 -39 -71 -65.5t-102 -26.5q-57 0 -190 62q-98 45 -170 118t-148 185q-72 107 -71 194v8q3 91 74 158q24 22 52 22q6 0 18 -1.5t19 -1.5q19 0 26.5 -6.5t15.5 -27.5q8 -20 33 -88t25 -75q0 -21 -34.5 -57.5 t-34.5 -46.5q0 -7 5 -15q34 -73 102 -137q56 -53 151 -101q12 -7 22 -7q15 0 54 48.5t52 48.5zM782 32q127 0 243.5 50t200.5 134t134 200.5t50 243.5t-50 243.5t-134 200.5t-200.5 134t-243.5 50t-243.5 -50t-200.5 -134t-134 -200.5t-50 -243.5q0 -203 120 -368l-79 -233 l242 77q158 -104 345 -104zM782 1414q153 0 292.5 -60t240.5 -161t161 -240.5t60 -292.5t-60 -292.5t-161 -240.5t-240.5 -161t-292.5 -60q-195 0 -365 94l-417 -134l136 405q-108 178 -108 389q0 153 60 292.5t161 240.5t240.5 161t292.5 60z" />
+<glyph unicode="&#xf233;" horiz-adv-x="1792" d="M128 128h1024v128h-1024v-128zM128 640h1024v128h-1024v-128zM1696 192q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM128 1152h1024v128h-1024v-128zM1696 704q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1696 1216 q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1792 384v-384h-1792v384h1792zM1792 896v-384h-1792v384h1792zM1792 1408v-384h-1792v384h1792z" />
+<glyph unicode="&#xf234;" horiz-adv-x="2048" d="M704 640q-159 0 -271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5t-112.5 -271.5t-271.5 -112.5zM1664 512h352q13 0 22.5 -9.5t9.5 -22.5v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-352q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5 t-9.5 22.5v352h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v352q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5v-352zM928 288q0 -52 38 -90t90 -38h256v-238q-68 -50 -171 -50h-874q-121 0 -194 69t-73 190q0 53 3.5 103.5t14 109t26.5 108.5 t43 97.5t62 81t85.5 53.5t111.5 20q19 0 39 -17q79 -61 154.5 -91.5t164.5 -30.5t164.5 30.5t154.5 91.5q20 17 39 17q132 0 217 -96h-223q-52 0 -90 -38t-38 -90v-192z" />
+<glyph unicode="&#xf235;" horiz-adv-x="2048" d="M704 640q-159 0 -271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5t-112.5 -271.5t-271.5 -112.5zM1781 320l249 -249q9 -9 9 -23q0 -13 -9 -22l-136 -136q-9 -9 -22 -9q-14 0 -23 9l-249 249l-249 -249q-9 -9 -23 -9q-13 0 -22 9l-136 136 q-9 9 -9 22q0 14 9 23l249 249l-249 249q-9 9 -9 23q0 13 9 22l136 136q9 9 22 9q14 0 23 -9l249 -249l249 249q9 9 23 9q13 0 22 -9l136 -136q9 -9 9 -22q0 -14 -9 -23zM1283 320l-181 -181q-37 -37 -37 -91q0 -53 37 -90l83 -83q-21 -3 -44 -3h-874q-121 0 -194 69 t-73 190q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q19 0 39 -17q154 -122 319 -122t319 122q20 17 39 17q28 0 57 -6q-28 -27 -41 -50t-13 -56q0 -54 37 -91z" />
+<glyph unicode="&#xf236;" horiz-adv-x="2048" d="M256 512h1728q26 0 45 -19t19 -45v-448h-256v256h-1536v-256h-256v1216q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-704zM832 832q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM2048 576v64q0 159 -112.5 271.5t-271.5 112.5h-704 q-26 0 -45 -19t-19 -45v-384h1152z" />
+<glyph unicode="&#xf237;" d="M1536 1536l-192 -448h192v-192h-274l-55 -128h329v-192h-411l-357 -832l-357 832h-411v192h329l-55 128h-274v192h192l-192 448h256l323 -768h378l323 768h256zM768 320l108 256h-216z" />
+<glyph unicode="&#xf238;" d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM768 192q80 0 136 56t56 136t-56 136t-136 56 t-136 -56t-56 -136t56 -136t136 -56zM1344 768v512h-1152v-512h1152z" />
+<glyph unicode="&#xf239;" d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM288 224q66 0 113 47t47 113t-47 113t-113 47 t-113 -47t-47 -113t47 -113t113 -47zM704 768v512h-544v-512h544zM1248 224q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM1408 768v512h-576v-512h576z" />
+<glyph unicode="&#xf23a;" horiz-adv-x="1792" d="M597 1115v-1173q0 -25 -12.5 -42.5t-36.5 -17.5q-17 0 -33 8l-465 233q-21 10 -35.5 33.5t-14.5 46.5v1140q0 20 10 34t29 14q14 0 44 -15l511 -256q3 -3 3 -5zM661 1014l534 -866l-534 266v600zM1792 996v-1054q0 -25 -14 -40.5t-38 -15.5t-47 13l-441 220zM1789 1116 q0 -3 -256.5 -419.5t-300.5 -487.5l-390 634l324 527q17 28 52 28q14 0 26 -6l541 -270q4 -2 4 -6z" />
+<glyph unicode="&#xf23b;" d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1408v-1536h-1536v1536h1536z" />
+<glyph unicode="&#xf23c;" horiz-adv-x="2296" d="M478 -139q-8 -16 -27 -34.5t-37 -25.5q-25 -9 -51.5 3.5t-28.5 31.5q-1 22 40 55t68 38q23 4 34 -21.5t2 -46.5zM1819 -139q7 -16 26 -34.5t38 -25.5q25 -9 51.5 3.5t27.5 31.5q2 22 -39.5 55t-68.5 38q-22 4 -33 -21.5t-2 -46.5zM1867 -30q13 -27 56.5 -59.5t77.5 -41.5 q45 -13 82 4.5t37 50.5q0 46 -67.5 100.5t-115.5 59.5q-40 5 -63.5 -37.5t-6.5 -76.5zM428 -30q-13 -27 -56 -59.5t-77 -41.5q-45 -13 -82 4.5t-37 50.5q0 46 67.5 100.5t115.5 59.5q40 5 63 -37.5t6 -76.5zM1158 1094h1q-41 0 -76 -15q27 -8 44 -30.5t17 -49.5 q0 -35 -27 -60t-65 -25q-52 0 -80 43q-5 -23 -5 -42q0 -74 56 -126.5t135 -52.5q80 0 136 52.5t56 126.5t-56 126.5t-136 52.5zM1462 1312q-99 109 -220.5 131.5t-245.5 -44.5q27 60 82.5 96.5t118 39.5t121.5 -17t99.5 -74.5t44.5 -131.5zM2212 73q8 -11 -11 -42 q7 -23 7 -40q1 -56 -44.5 -112.5t-109.5 -91.5t-118 -37q-48 -2 -92 21.5t-66 65.5q-687 -25 -1259 0q-23 -41 -66.5 -65t-92.5 -22q-86 3 -179.5 80.5t-92.5 160.5q2 22 7 40q-19 31 -11 42q6 10 31 1q14 22 41 51q-7 29 2 38q11 10 39 -4q29 20 59 34q0 29 13 37 q23 12 51 -16q35 5 61 -2q18 -4 38 -19v73q-11 0 -18 2q-53 10 -97 44.5t-55 87.5q-9 38 0 81q15 62 93 95q2 17 19 35.5t36 23.5t33 -7.5t19 -30.5h13q46 -5 60 -23q3 -3 5 -7q10 1 30.5 3.5t30.5 3.5q-15 11 -30 17q-23 40 -91 43q0 6 1 10q-62 2 -118.5 18.5t-84.5 47.5 q-32 36 -42.5 92t-2.5 112q16 126 90 179q23 16 52 4.5t32 -40.5q0 -1 1.5 -14t2.5 -21t3 -20t5.5 -19t8.5 -10q27 -14 76 -12q48 46 98 74q-40 4 -162 -14l47 46q61 58 163 111q145 73 282 86q-20 8 -41 15.5t-47 14t-42.5 10.5t-47.5 11t-43 10q595 126 904 -139 q98 -84 158 -222q85 -10 121 9h1q5 3 8.5 10t5.5 19t3 19.5t3 21.5l1 14q3 28 32 40t52 -5q73 -52 91 -178q7 -57 -3.5 -113t-42.5 -91q-28 -32 -83.5 -48.5t-115.5 -18.5v-10q-71 -2 -95 -43q-14 -5 -31 -17q11 -1 32 -3.5t30 -3.5q1 4 5 8q16 18 60 23h13q5 18 19 30t33 8 t36 -23t19 -36q79 -32 93 -95q9 -40 1 -81q-12 -53 -56 -88t-97 -44q-10 -2 -17 -2q0 -49 -1 -73q20 15 38 19q26 7 61 2q28 28 51 16q14 -9 14 -37q33 -16 59 -34q27 13 38 4q10 -10 2 -38q28 -30 41 -51q23 8 31 -1zM1937 1025q0 -29 -9 -54q82 -32 112 -132 q4 37 -9.5 98.5t-41.5 90.5q-20 19 -36 17t-16 -20zM1859 925q35 -42 47.5 -108.5t-0.5 -124.5q67 13 97 45q13 14 18 28q-3 64 -31 114.5t-79 66.5q-15 -15 -52 -21zM1822 921q-30 0 -44 1q42 -115 53 -239q21 0 43 3q16 68 1 135t-53 100zM258 839q30 100 112 132 q-9 25 -9 54q0 18 -16.5 20t-35.5 -17q-28 -29 -41.5 -90.5t-9.5 -98.5zM294 737q29 -31 97 -45q-13 58 -0.5 124.5t47.5 108.5v0q-37 6 -52 21q-51 -16 -78.5 -66t-31.5 -115q9 -17 18 -28zM471 683q14 124 73 235q-19 -4 -55 -18l-45 -19v1q-46 -89 -20 -196q25 -3 47 -3z M1434 644q8 -38 16.5 -108.5t11.5 -89.5q3 -18 9.5 -21.5t23.5 4.5q40 20 62 85.5t23 125.5q-24 2 -146 4zM1152 1285q-116 0 -199 -82.5t-83 -198.5q0 -117 83 -199.5t199 -82.5t199 82.5t83 199.5q0 116 -83 198.5t-199 82.5zM1380 646q-106 2 -211 0v1q-1 -27 2.5 -86 t13.5 -66q29 -14 93.5 -14.5t95.5 10.5q9 3 11 39t-0.5 69.5t-4.5 46.5zM1112 447q8 4 9.5 48t-0.5 88t-4 63v1q-212 -3 -214 -3q-4 -20 -7 -62t0 -83t14 -46q34 -15 101 -16t101 10zM718 636q-16 -59 4.5 -118.5t77.5 -84.5q15 -8 24 -5t12 21q3 16 8 90t10 103 q-69 -2 -136 -6zM591 510q3 -23 -34 -36q132 -141 271.5 -240t305.5 -154q172 49 310.5 146t293.5 250q-33 13 -30 34l3 9v1v-1q-17 2 -50 5.5t-48 4.5q-26 -90 -82 -132q-51 -38 -82 1q-5 6 -9 14q-7 13 -17 62q-2 -5 -5 -9t-7.5 -7t-8 -5.5t-9.5 -4l-10 -2.5t-12 -2 l-12 -1.5t-13.5 -1t-13.5 -0.5q-106 -9 -163 11q-4 -17 -10 -26.5t-21 -15t-23 -7t-36 -3.5q-2 0 -3 -0.5t-3 -0.5h-3q-179 -17 -203 40q-2 -63 -56 -54q-47 8 -91 54q-12 13 -20 26q-17 29 -26 65q-58 -6 -87 -10q1 -2 4 -10zM507 -118q3 14 3 30q-17 71 -51 130t-73 70 q-41 12 -101.5 -14.5t-104.5 -80t-39 -107.5q35 -53 100 -93t119 -42q51 -2 94 28t53 79zM510 53q23 -63 27 -119q195 113 392 174q-98 52 -180.5 120t-179.5 165q-6 -4 -29 -13q0 -2 -1 -5t-1 -4q31 -18 22 -37q-12 -23 -56 -34q-10 -13 -29 -24h-1q-2 -83 1 -150 q19 -34 35 -73zM579 -113q532 -21 1145 0q-254 147 -428 196q-76 -35 -156 -57q-8 -3 -16 0q-65 21 -129 49q-208 -60 -416 -188h-1v-1q1 0 1 1zM1763 -67q4 54 28 120q14 38 33 71l-1 -1q3 77 3 153q-15 8 -30 25q-42 9 -56 33q-9 20 22 38q-2 4 -2 9q-16 4 -28 12 q-204 -190 -383 -284q198 -59 414 -176zM2155 -90q5 54 -39 107.5t-104 80t-102 14.5q-38 -11 -72.5 -70.5t-51.5 -129.5q0 -16 3 -30q10 -49 53 -79t94 -28q54 2 119 42t100 93z" />
+<glyph unicode="&#xf23d;" horiz-adv-x="2304" d="M1524 -25q0 -68 -48 -116t-116 -48t-116.5 48t-48.5 116t48.5 116.5t116.5 48.5t116 -48.5t48 -116.5zM775 -25q0 -68 -48.5 -116t-116.5 -48t-116 48t-48 116t48 116.5t116 48.5t116.5 -48.5t48.5 -116.5zM0 1469q57 -60 110.5 -104.5t121 -82t136 -63t166 -45.5 t200 -31.5t250 -18.5t304 -9.5t372.5 -2.5q139 0 244.5 -5t181 -16.5t124 -27.5t71 -39.5t24 -51.5t-19.5 -64t-56.5 -76.5t-89.5 -91t-116 -104.5t-139 -119q-185 -157 -286 -247q29 51 76.5 109t94 105.5t94.5 98.5t83 91.5t54 80.5t13 70t-45.5 55.5t-116.5 41t-204 23.5 t-304 5q-168 -2 -314 6t-256 23t-204.5 41t-159.5 51.5t-122.5 62.5t-91.5 66.5t-68 71.5t-50.5 69.5t-40 68t-36.5 59.5z" />
+<glyph unicode="&#xf23e;" horiz-adv-x="1792" d="M896 1472q-169 0 -323 -66t-265.5 -177.5t-177.5 -265.5t-66 -323t66 -323t177.5 -265.5t265.5 -177.5t323 -66t323 66t265.5 177.5t177.5 265.5t66 323t-66 323t-177.5 265.5t-265.5 177.5t-323 66zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348 t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM496 704q16 0 16 -16v-480q0 -16 -16 -16h-32q-16 0 -16 16v480q0 16 16 16h32zM896 640q53 0 90.5 -37.5t37.5 -90.5q0 -35 -17.5 -64t-46.5 -46v-114q0 -14 -9 -23 t-23 -9h-64q-14 0 -23 9t-9 23v114q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5zM896 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM544 928v-96 q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 93 65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5v-96q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 146 -103 249t-249 103t-249 -103t-103 -249zM1408 192v512q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-512 q0 -26 19 -45t45 -19h896q26 0 45 19t19 45z" />
+<glyph unicode="&#xf240;" horiz-adv-x="2304" d="M1920 1024v-768h-1664v768h1664zM2048 448h128v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288zM2304 832v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113 v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160q53 0 90.5 -37.5t37.5 -90.5z" />
+<glyph unicode="&#xf241;" horiz-adv-x="2304" d="M256 256v768h1280v-768h-1280zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+<glyph unicode="&#xf242;" horiz-adv-x="2304" d="M256 256v768h896v-768h-896zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+<glyph unicode="&#xf243;" horiz-adv-x="2304" d="M256 256v768h512v-768h-512zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+<glyph unicode="&#xf244;" horiz-adv-x="2304" d="M2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23 v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+<glyph unicode="&#xf245;" horiz-adv-x="1280" d="M1133 493q31 -30 14 -69q-17 -40 -59 -40h-382l201 -476q10 -25 0 -49t-34 -35l-177 -75q-25 -10 -49 0t-35 34l-191 452l-312 -312q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v1504q0 42 40 59q12 5 24 5q27 0 45 -19z" />
+<glyph unicode="&#xf246;" horiz-adv-x="1024" d="M832 1408q-320 0 -320 -224v-416h128v-128h-128v-544q0 -224 320 -224h64v-128h-64q-272 0 -384 146q-112 -146 -384 -146h-64v128h64q320 0 320 224v544h-128v128h128v416q0 224 -320 224h-64v128h64q272 0 384 -146q112 146 384 146h64v-128h-64z" />
+<glyph unicode="&#xf247;" horiz-adv-x="2048" d="M2048 1152h-128v-1024h128v-384h-384v128h-1280v-128h-384v384h128v1024h-128v384h384v-128h1280v128h384v-384zM1792 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 -128v128h-128v-128h128zM1664 0v128h128v1024h-128v128h-1280v-128h-128v-1024h128v-128 h1280zM1920 -128v128h-128v-128h128zM1280 896h384v-768h-896v256h-384v768h896v-256zM512 512h640v512h-640v-512zM1536 256v512h-256v-384h-384v-128h640z" />
+<glyph unicode="&#xf248;" horiz-adv-x="2304" d="M2304 768h-128v-640h128v-384h-384v128h-896v-128h-384v384h128v128h-384v-128h-384v384h128v640h-128v384h384v-128h896v128h384v-384h-128v-128h384v128h384v-384zM2048 1024v-128h128v128h-128zM1408 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 256 v128h-128v-128h128zM1536 384h-128v-128h128v128zM384 384h896v128h128v640h-128v128h-896v-128h-128v-640h128v-128zM896 -128v128h-128v-128h128zM2176 -128v128h-128v-128h128zM2048 128v640h-128v128h-384v-384h128v-384h-384v128h-384v-128h128v-128h896v128h128z" />
+<glyph unicode="&#xf249;" d="M1024 288v-416h-928q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68v-928h-416q-40 0 -68 -28t-28 -68zM1152 256h381q-15 -82 -65 -132l-184 -184q-50 -50 -132 -65v381z" />
+<glyph unicode="&#xf24a;" d="M1400 256h-248v-248q29 10 41 22l185 185q12 12 22 41zM1120 384h288v896h-1280v-1280h896v288q0 40 28 68t68 28zM1536 1312v-1024q0 -40 -20 -88t-48 -76l-184 -184q-28 -28 -76 -48t-88 -20h-1024q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68 z" />
+<glyph unicode="&#xf24b;" horiz-adv-x="2304" d="M1951 538q0 -26 -15.5 -44.5t-38.5 -23.5q-8 -2 -18 -2h-153v140h153q10 0 18 -2q23 -5 38.5 -23.5t15.5 -44.5zM1933 751q0 -25 -15 -42t-38 -21q-3 -1 -15 -1h-139v129h139q3 0 8.5 -0.5t6.5 -0.5q23 -4 38 -21.5t15 -42.5zM728 587v308h-228v-308q0 -58 -38 -94.5 t-105 -36.5q-108 0 -229 59v-112q53 -15 121 -23t109 -9l42 -1q328 0 328 217zM1442 403v113q-99 -52 -200 -59q-108 -8 -169 41t-61 142t61 142t169 41q101 -7 200 -58v112q-48 12 -100 19.5t-80 9.5l-28 2q-127 6 -218.5 -14t-140.5 -60t-71 -88t-22 -106t22 -106t71 -88 t140.5 -60t218.5 -14q101 4 208 31zM2176 518q0 54 -43 88.5t-109 39.5v3q57 8 89 41.5t32 79.5q0 55 -41 88t-107 36q-3 0 -12 0.5t-14 0.5h-455v-510h491q74 0 121.5 36.5t47.5 96.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90 t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf24c;" horiz-adv-x="2304" d="M858 295v693q-106 -41 -172 -135.5t-66 -211.5t66 -211.5t172 -134.5zM1362 641q0 117 -66 211.5t-172 135.5v-694q106 41 172 135.5t66 211.5zM1577 641q0 -159 -78.5 -294t-213.5 -213.5t-294 -78.5q-119 0 -227.5 46.5t-187 125t-125 187t-46.5 227.5q0 159 78.5 294 t213.5 213.5t294 78.5t294 -78.5t213.5 -213.5t78.5 -294zM1960 634q0 139 -55.5 261.5t-147.5 205.5t-213.5 131t-252.5 48h-301q-176 0 -323.5 -81t-235 -230t-87.5 -335q0 -171 87 -317.5t236 -231.5t323 -85h301q129 0 251.5 50.5t214.5 135t147.5 202.5t55.5 246z M2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf24d;" horiz-adv-x="1792" d="M1664 -96v1088q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5zM1792 992v-1088q0 -66 -47 -113t-113 -47h-1088q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113 zM1408 1376v-160h-128v160q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h160v-128h-160q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf24e;" horiz-adv-x="2304" d="M1728 1088l-384 -704h768zM448 1088l-384 -704h768zM1269 1280q-14 -40 -45.5 -71.5t-71.5 -45.5v-1291h608q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1344q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h608v1291q-40 14 -71.5 45.5t-45.5 71.5h-491q-14 0 -23 9t-9 23v64 q0 14 9 23t23 9h491q21 57 70 92.5t111 35.5t111 -35.5t70 -92.5h491q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-491zM1088 1264q33 0 56.5 23.5t23.5 56.5t-23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5zM2176 384q0 -73 -46.5 -131t-117.5 -91 t-144.5 -49.5t-139.5 -16.5t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81zM896 384q0 -73 -46.5 -131t-117.5 -91t-144.5 -49.5t-139.5 -16.5 t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81z" />
+<glyph unicode="&#xf250;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-77 -29 -149 -92.5 t-129.5 -152.5t-92.5 -210t-35 -253h1024q0 132 -35 253t-92.5 210t-129.5 152.5t-149 92.5q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" />
+<glyph unicode="&#xf251;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -66 9 -128h1006q9 61 9 128zM1280 -128q0 130 -34 249.5t-90.5 208t-126.5 152t-146 94.5h-230q-76 -31 -146 -94.5t-126.5 -152t-90.5 -208t-34 -249.5h1024z" />
+<glyph unicode="&#xf252;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -206 85 -384h854q85 178 85 384zM1223 192q-54 141 -145.5 241.5t-194.5 142.5h-230q-103 -42 -194.5 -142.5t-145.5 -241.5h910z" />
+<glyph unicode="&#xf253;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-137 -51 -244 -196 h700q-107 145 -244 196q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" />
+<glyph unicode="&#xf254;" d="M1504 -64q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472zM130 0q3 55 16 107t30 95t46 87t53.5 76t64.5 69.5t66 60t70.5 55t66.5 47.5t65 43q-43 28 -65 43t-66.5 47.5t-70.5 55t-66 60t-64.5 69.5t-53.5 76t-46 87 t-30 95t-16 107h1276q-3 -55 -16 -107t-30 -95t-46 -87t-53.5 -76t-64.5 -69.5t-66 -60t-70.5 -55t-66.5 -47.5t-65 -43q43 -28 65 -43t66.5 -47.5t70.5 -55t66 -60t64.5 -69.5t53.5 -76t46 -87t30 -95t16 -107h-1276zM1504 1536q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9 h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472z" />
+<glyph unicode="&#xf255;" d="M768 1152q-53 0 -90.5 -37.5t-37.5 -90.5v-128h-32v93q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-429l-32 30v172q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-224q0 -47 35 -82l310 -296q39 -39 39 -102q0 -26 19 -45t45 -19h640q26 0 45 19t19 45v25 q0 41 10 77l108 436q10 36 10 77v246q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-32h-32v125q0 40 -25 72.5t-64 40.5q-14 2 -23 2q-46 0 -79 -33t-33 -79v-128h-32v122q0 51 -32.5 89.5t-82.5 43.5q-5 1 -13 1zM768 1280q84 0 149 -50q57 34 123 34q59 0 111 -27 t86 -76q27 7 59 7q100 0 170 -71.5t70 -171.5v-246q0 -51 -13 -108l-109 -436q-6 -24 -6 -71q0 -80 -56 -136t-136 -56h-640q-84 0 -138 58.5t-54 142.5l-308 296q-76 73 -76 175v224q0 99 70.5 169.5t169.5 70.5q11 0 16 -1q6 95 75.5 160t164.5 65q52 0 98 -21 q72 69 174 69z" />
+<glyph unicode="&#xf256;" horiz-adv-x="1792" d="M880 1408q-46 0 -79 -33t-33 -79v-656h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528v-256l-154 205q-38 51 -102 51q-53 0 -90.5 -37.5t-37.5 -90.5q0 -43 26 -77l384 -512q38 -51 102 -51h688q34 0 61 22t34 56l76 405q5 32 5 59v498q0 46 -33 79t-79 33t-79 -33 t-33 -79v-272h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528h-32v656q0 46 -33 79t-79 33zM880 1536q68 0 125.5 -35.5t88.5 -96.5q19 4 42 4q99 0 169.5 -70.5t70.5 -169.5v-17q105 6 180.5 -64t75.5 -175v-498q0 -40 -8 -83l-76 -404q-14 -79 -76.5 -131t-143.5 -52 h-688q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 106 75 181t181 75q78 0 128 -34v434q0 99 70.5 169.5t169.5 70.5q23 0 42 -4q31 61 88.5 96.5t125.5 35.5z" />
+<glyph unicode="&#xf257;" horiz-adv-x="1792" d="M1073 -128h-177q-163 0 -226 141q-23 49 -23 102v5q-62 30 -98.5 88.5t-36.5 127.5q0 38 5 48h-261q-106 0 -181 75t-75 181t75 181t181 75h113l-44 17q-74 28 -119.5 93.5t-45.5 145.5q0 106 75 181t181 75q46 0 91 -17l628 -239h401q106 0 181 -75t75 -181v-668 q0 -88 -54 -157.5t-140 -90.5l-339 -85q-92 -23 -186 -23zM1024 583l-155 -71l-163 -74q-30 -14 -48 -41.5t-18 -60.5q0 -46 33 -79t79 -33q26 0 46 10l338 154q-49 10 -80.5 50t-31.5 90v55zM1344 272q0 46 -33 79t-79 33q-26 0 -46 -10l-290 -132q-28 -13 -37 -17 t-30.5 -17t-29.5 -23.5t-16 -29t-8 -40.5q0 -50 31.5 -82t81.5 -32q20 0 38 9l352 160q30 14 48 41.5t18 60.5zM1112 1024l-650 248q-24 8 -46 8q-53 0 -90.5 -37.5t-37.5 -90.5q0 -40 22.5 -73t59.5 -47l526 -200v-64h-640q-53 0 -90.5 -37.5t-37.5 -90.5t37.5 -90.5 t90.5 -37.5h535l233 106v198q0 63 46 106l111 102h-69zM1073 0q82 0 155 19l339 85q43 11 70 45.5t27 78.5v668q0 53 -37.5 90.5t-90.5 37.5h-308l-136 -126q-36 -33 -36 -82v-296q0 -46 33 -77t79 -31t79 35t33 81v208h32v-208q0 -70 -57 -114q52 -8 86.5 -48.5t34.5 -93.5 q0 -42 -23 -78t-61 -53l-310 -141h91z" />
+<glyph unicode="&#xf258;" horiz-adv-x="2048" d="M1151 1536q61 0 116 -28t91 -77l572 -781q118 -159 118 -359v-355q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v177l-286 143h-546q-80 0 -136 56t-56 136v32q0 119 84.5 203.5t203.5 84.5h420l42 128h-686q-100 0 -173.5 67.5t-81.5 166.5q-65 79 -65 182v32 q0 80 56 136t136 56h959zM1920 -64v355q0 157 -93 284l-573 781q-39 52 -103 52h-959q-26 0 -45 -19t-19 -45q0 -32 1.5 -49.5t9.5 -40.5t25 -43q10 31 35.5 50t56.5 19h832v-32h-832q-26 0 -45 -19t-19 -45q0 -44 3 -58q8 -44 44 -73t81 -29h640h91q40 0 68 -28t28 -68 q0 -15 -5 -30l-64 -192q-10 -29 -35 -47.5t-56 -18.5h-443q-66 0 -113 -47t-47 -113v-32q0 -26 19 -45t45 -19h561q16 0 29 -7l317 -158q24 -13 38.5 -36t14.5 -50v-197q0 -26 19 -45t45 -19h384q26 0 45 19t19 45z" />
+<glyph unicode="&#xf259;" horiz-adv-x="2048" d="M816 1408q-48 0 -79.5 -34t-31.5 -82q0 -14 3 -28l150 -624h-26l-116 482q-9 38 -39.5 62t-69.5 24q-47 0 -79 -34t-32 -81q0 -11 4 -29q3 -13 39 -161t68 -282t32 -138v-227l-307 230q-34 26 -77 26q-52 0 -89.5 -36.5t-37.5 -88.5q0 -67 56 -110l507 -379 q34 -26 76 -26h694q33 0 59 20.5t34 52.5l100 401q8 30 10 88t9 86l116 478q3 12 3 26q0 46 -33 79t-80 33q-38 0 -69 -25.5t-40 -62.5l-99 -408h-26l132 547q3 14 3 28q0 47 -32 80t-80 33q-38 0 -68.5 -24t-39.5 -62l-145 -602h-127l-164 682q-9 38 -39.5 62t-68.5 24z M1461 -256h-694q-85 0 -153 51l-507 380q-50 38 -78.5 94t-28.5 118q0 105 75 179t180 74q25 0 49.5 -5.5t41.5 -11t41 -20.5t35 -23t38.5 -29.5t37.5 -28.5l-123 512q-7 35 -7 59q0 93 60 162t152 79q14 87 80.5 144.5t155.5 57.5q83 0 148 -51.5t85 -132.5l103 -428 l83 348q20 81 85 132.5t148 51.5q87 0 152.5 -54t82.5 -139q93 -10 155 -78t62 -161q0 -30 -7 -57l-116 -477q-5 -22 -5 -67q0 -51 -13 -108l-101 -401q-19 -75 -79.5 -122.5t-137.5 -47.5z" />
+<glyph unicode="&#xf25a;" horiz-adv-x="1792" d="M640 1408q-53 0 -90.5 -37.5t-37.5 -90.5v-512v-384l-151 202q-41 54 -107 54q-52 0 -89 -38t-37 -90q0 -43 26 -77l384 -512q38 -51 102 -51h718q22 0 39.5 13.5t22.5 34.5l92 368q24 96 24 194v217q0 41 -28 71t-68 30t-68 -28t-28 -68h-32v61q0 48 -32 81.5t-80 33.5 q-46 0 -79 -33t-33 -79v-64h-32v90q0 55 -37 94.5t-91 39.5q-53 0 -90.5 -37.5t-37.5 -90.5v-96h-32v570q0 55 -37 94.5t-91 39.5zM640 1536q107 0 181.5 -77.5t74.5 -184.5v-220q22 2 32 2q99 0 173 -69q47 21 99 21q113 0 184 -87q27 7 56 7q94 0 159 -67.5t65 -161.5 v-217q0 -116 -28 -225l-92 -368q-16 -64 -68 -104.5t-118 -40.5h-718q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 105 74.5 180.5t179.5 75.5q71 0 130 -35v547q0 106 75 181t181 75zM768 128v384h-32v-384h32zM1024 128v384h-32v-384h32zM1280 128v384h-32 v-384h32z" />
+<glyph unicode="&#xf25b;" d="M1288 889q60 0 107 -23q141 -63 141 -226v-177q0 -94 -23 -186l-85 -339q-21 -86 -90.5 -140t-157.5 -54h-668q-106 0 -181 75t-75 181v401l-239 628q-17 45 -17 91q0 106 75 181t181 75q80 0 145.5 -45.5t93.5 -119.5l17 -44v113q0 106 75 181t181 75t181 -75t75 -181 v-261q27 5 48 5q69 0 127.5 -36.5t88.5 -98.5zM1072 896q-33 0 -60.5 -18t-41.5 -48l-74 -163l-71 -155h55q50 0 90 -31.5t50 -80.5l154 338q10 20 10 46q0 46 -33 79t-79 33zM1293 761q-22 0 -40.5 -8t-29 -16t-23.5 -29.5t-17 -30.5t-17 -37l-132 -290q-10 -20 -10 -46 q0 -46 33 -79t79 -33q33 0 60.5 18t41.5 48l160 352q9 18 9 38q0 50 -32 81.5t-82 31.5zM128 1120q0 -22 8 -46l248 -650v-69l102 111q43 46 106 46h198l106 233v535q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5v-640h-64l-200 526q-14 37 -47 59.5t-73 22.5 q-53 0 -90.5 -37.5t-37.5 -90.5zM1180 -128q44 0 78.5 27t45.5 70l85 339q19 73 19 155v91l-141 -310q-17 -38 -53 -61t-78 -23q-53 0 -93.5 34.5t-48.5 86.5q-44 -57 -114 -57h-208v32h208q46 0 81 33t35 79t-31 79t-77 33h-296q-49 0 -82 -36l-126 -136v-308 q0 -53 37.5 -90.5t90.5 -37.5h668z" />
+<glyph unicode="&#xf25c;" horiz-adv-x="1973" d="M857 992v-117q0 -13 -9.5 -22t-22.5 -9h-298v-812q0 -13 -9 -22.5t-22 -9.5h-135q-13 0 -22.5 9t-9.5 23v812h-297q-13 0 -22.5 9t-9.5 22v117q0 14 9 23t23 9h793q13 0 22.5 -9.5t9.5 -22.5zM1895 995l77 -961q1 -13 -8 -24q-10 -10 -23 -10h-134q-12 0 -21 8.5 t-10 20.5l-46 588l-189 -425q-8 -19 -29 -19h-120q-20 0 -29 19l-188 427l-45 -590q-1 -12 -10 -20.5t-21 -8.5h-135q-13 0 -23 10q-9 10 -9 24l78 961q1 12 10 20.5t21 8.5h142q20 0 29 -19l220 -520q10 -24 20 -51q3 7 9.5 24.5t10.5 26.5l221 520q9 19 29 19h141 q13 0 22 -8.5t10 -20.5z" />
+<glyph unicode="&#xf25d;" horiz-adv-x="1792" d="M1042 833q0 88 -60 121q-33 18 -117 18h-123v-281h162q66 0 102 37t36 105zM1094 548l205 -373q8 -17 -1 -31q-8 -16 -27 -16h-152q-20 0 -28 17l-194 365h-155v-350q0 -14 -9 -23t-23 -9h-134q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h294q128 0 190 -24q85 -31 134 -109 t49 -180q0 -92 -42.5 -165.5t-115.5 -109.5q6 -10 9 -16zM896 1376q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM1792 640 q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf25e;" horiz-adv-x="1792" d="M605 303q153 0 257 104q14 18 3 36l-45 82q-6 13 -24 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13t-23.5 -14.5t-28.5 -13.5t-33.5 -9.5t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78 q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-148 0 -246 -96.5t-98 -240.5q0 -146 97 -241.5t247 -95.5zM1235 303q153 0 257 104q14 18 4 36l-45 82q-8 14 -25 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13t-23.5 -14.5t-28.5 -13.5t-33.5 -9.5 t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-147 0 -245.5 -96.5t-98.5 -240.5q0 -146 97 -241.5t247 -95.5zM896 1376 q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191 t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71z" />
+<glyph unicode="&#xf260;" horiz-adv-x="2048" d="M736 736l384 -384l-384 -384l-672 672l672 672l168 -168l-96 -96l-72 72l-480 -480l480 -480l193 193l-289 287zM1312 1312l672 -672l-672 -672l-168 168l96 96l72 -72l480 480l-480 480l-193 -193l289 -287l-96 -96l-384 384z" />
+<glyph unicode="&#xf261;" horiz-adv-x="1792" d="M717 182l271 271l-279 279l-88 -88l192 -191l-96 -96l-279 279l279 279l40 -40l87 87l-127 128l-454 -454zM1075 190l454 454l-454 454l-271 -271l279 -279l88 88l-192 191l96 96l279 -279l-279 -279l-40 40l-87 -88zM1792 640q0 -182 -71 -348t-191 -286t-286 -191 t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf262;" horiz-adv-x="2304" d="M651 539q0 -39 -27.5 -66.5t-65.5 -27.5q-39 0 -66.5 27.5t-27.5 66.5q0 38 27.5 65.5t66.5 27.5q38 0 65.5 -27.5t27.5 -65.5zM1805 540q0 -39 -27.5 -66.5t-66.5 -27.5t-66.5 27.5t-27.5 66.5t27.5 66t66.5 27t66.5 -27t27.5 -66zM765 539q0 79 -56.5 136t-136.5 57 t-136.5 -56.5t-56.5 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM1918 540q0 80 -56.5 136.5t-136.5 56.5q-79 0 -136 -56.5t-57 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM850 539q0 -116 -81.5 -197.5t-196.5 -81.5q-116 0 -197.5 82t-81.5 197 t82 196.5t197 81.5t196.5 -81.5t81.5 -196.5zM2004 540q0 -115 -81.5 -196.5t-197.5 -81.5q-115 0 -196.5 81.5t-81.5 196.5t81.5 196.5t196.5 81.5q116 0 197.5 -81.5t81.5 -196.5zM1040 537q0 191 -135.5 326.5t-326.5 135.5q-125 0 -231 -62t-168 -168.5t-62 -231.5 t62 -231.5t168 -168.5t231 -62q191 0 326.5 135.5t135.5 326.5zM1708 1110q-254 111 -556 111q-319 0 -573 -110q117 0 223 -45.5t182.5 -122.5t122 -183t45.5 -223q0 115 43.5 219.5t118 180.5t177.5 123t217 50zM2187 537q0 191 -135 326.5t-326 135.5t-326.5 -135.5 t-135.5 -326.5t135.5 -326.5t326.5 -135.5t326 135.5t135 326.5zM1921 1103h383q-44 -51 -75 -114.5t-40 -114.5q110 -151 110 -337q0 -156 -77 -288t-209 -208.5t-287 -76.5q-133 0 -249 56t-196 155q-47 -56 -129 -179q-11 22 -53.5 82.5t-74.5 97.5 q-80 -99 -196.5 -155.5t-249.5 -56.5q-155 0 -287 76.5t-209 208.5t-77 288q0 186 110 337q-9 51 -40 114.5t-75 114.5h365q149 100 355 156.5t432 56.5q224 0 421 -56t348 -157z" />
+<glyph unicode="&#xf263;" horiz-adv-x="1280" d="M640 629q-188 0 -321 133t-133 320q0 188 133 321t321 133t321 -133t133 -321q0 -187 -133 -320t-321 -133zM640 1306q-92 0 -157.5 -65.5t-65.5 -158.5q0 -92 65.5 -157.5t157.5 -65.5t157.5 65.5t65.5 157.5q0 93 -65.5 158.5t-157.5 65.5zM1163 574q13 -27 15 -49.5 t-4.5 -40.5t-26.5 -38.5t-42.5 -37t-61.5 -41.5q-115 -73 -315 -94l73 -72l267 -267q30 -31 30 -74t-30 -73l-12 -13q-31 -30 -74 -30t-74 30q-67 68 -267 268l-267 -268q-31 -30 -74 -30t-73 30l-12 13q-31 30 -31 73t31 74l267 267l72 72q-203 21 -317 94 q-39 25 -61.5 41.5t-42.5 37t-26.5 38.5t-4.5 40.5t15 49.5q10 20 28 35t42 22t56 -2t65 -35q5 -4 15 -11t43 -24.5t69 -30.5t92 -24t113 -11q91 0 174 25.5t120 50.5l38 25q33 26 65 35t56 2t42 -22t28 -35z" />
+<glyph unicode="&#xf264;" d="M927 956q0 -66 -46.5 -112.5t-112.5 -46.5t-112.5 46.5t-46.5 112.5t46.5 112.5t112.5 46.5t112.5 -46.5t46.5 -112.5zM1141 593q-10 20 -28 32t-47.5 9.5t-60.5 -27.5q-10 -8 -29 -20t-81 -32t-127 -20t-124 18t-86 36l-27 18q-31 25 -60.5 27.5t-47.5 -9.5t-28 -32 q-22 -45 -2 -74.5t87 -73.5q83 -53 226 -67l-51 -52q-142 -142 -191 -190q-22 -22 -22 -52.5t22 -52.5l9 -9q22 -22 52.5 -22t52.5 22l191 191q114 -115 191 -191q22 -22 52.5 -22t52.5 22l9 9q22 22 22 52.5t-22 52.5l-191 190l-52 52q141 14 225 67q67 44 87 73.5t-2 74.5 zM1092 956q0 134 -95 229t-229 95t-229 -95t-95 -229t95 -229t229 -95t229 95t95 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf265;" horiz-adv-x="1720" d="M1565 1408q65 0 110 -45.5t45 -110.5v-519q0 -176 -68 -336t-182.5 -275t-274 -182.5t-334.5 -67.5q-176 0 -335.5 67.5t-274.5 182.5t-183 275t-68 336v519q0 64 46 110t110 46h1409zM861 344q47 0 82 33l404 388q37 35 37 85q0 49 -34.5 83.5t-83.5 34.5q-47 0 -82 -33 l-323 -310l-323 310q-35 33 -81 33q-49 0 -83.5 -34.5t-34.5 -83.5q0 -51 36 -85l405 -388q33 -33 81 -33z" />
+<glyph unicode="&#xf266;" horiz-adv-x="2304" d="M1494 -103l-295 695q-25 -49 -158.5 -305.5t-198.5 -389.5q-1 -1 -27.5 -0.5t-26.5 1.5q-82 193 -255.5 587t-259.5 596q-21 50 -66.5 107.5t-103.5 100.5t-102 43q0 5 -0.5 24t-0.5 27h583v-50q-39 -2 -79.5 -16t-66.5 -43t-10 -64q26 -59 216.5 -499t235.5 -540 q31 61 140 266.5t131 247.5q-19 39 -126 281t-136 295q-38 69 -201 71v50l513 -1v-47q-60 -2 -93.5 -25t-12.5 -69q33 -70 87 -189.5t86 -187.5q110 214 173 363q24 55 -10 79.5t-129 26.5q1 7 1 25v24q64 0 170.5 0.5t180 1t92.5 0.5v-49q-62 -2 -119 -33t-90 -81 l-213 -442q13 -33 127.5 -290t121.5 -274l441 1017q-14 38 -49.5 62.5t-65 31.5t-55.5 8v50l460 -4l1 -2l-1 -44q-139 -4 -201 -145q-526 -1216 -559 -1291h-49z" />
+<glyph unicode="&#xf267;" horiz-adv-x="1792" d="M949 643q0 -26 -16.5 -45t-41.5 -19q-26 0 -45 16.5t-19 41.5q0 26 17 45t42 19t44 -16.5t19 -41.5zM964 585l350 581q-9 -8 -67.5 -62.5t-125.5 -116.5t-136.5 -127t-117 -110.5t-50.5 -51.5l-349 -580q7 7 67 62t126 116.5t136 127t117 111t50 50.5zM1611 640 q0 -201 -104 -371q-3 2 -17 11t-26.5 16.5t-16.5 7.5q-13 0 -13 -13q0 -10 59 -44q-74 -112 -184.5 -190.5t-241.5 -110.5l-16 67q-1 10 -15 10q-5 0 -8 -5.5t-2 -9.5l16 -68q-72 -15 -146 -15q-199 0 -372 105q1 2 13 20.5t21.5 33.5t9.5 19q0 13 -13 13q-6 0 -17 -14.5 t-22.5 -34.5t-13.5 -23q-113 75 -192 187.5t-110 244.5l69 15q10 3 10 15q0 5 -5.5 8t-10.5 2l-68 -15q-14 72 -14 139q0 206 109 379q2 -1 18.5 -12t30 -19t17.5 -8q13 0 13 12q0 6 -12.5 15.5t-32.5 21.5l-20 12q77 112 189 189t244 107l15 -67q2 -10 15 -10q5 0 8 5.5 t2 10.5l-15 66q71 13 134 13q204 0 379 -109q-39 -56 -39 -65q0 -13 12 -13q11 0 48 64q111 -75 187.5 -186t107.5 -241l-56 -12q-10 -2 -10 -16q0 -5 5.5 -8t9.5 -2l57 13q14 -72 14 -140zM1696 640q0 163 -63.5 311t-170.5 255t-255 170.5t-311 63.5t-311 -63.5 t-255 -170.5t-170.5 -255t-63.5 -311t63.5 -311t170.5 -255t255 -170.5t311 -63.5t311 63.5t255 170.5t170.5 255t63.5 311zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191 t191 -286t71 -348z" />
+<glyph unicode="&#xf268;" horiz-adv-x="1792" d="M893 1536q240 2 451 -120q232 -134 352 -372l-742 39q-160 9 -294 -74.5t-185 -229.5l-276 424q128 159 311 245.5t383 87.5zM146 1131l337 -663q72 -143 211 -217t293 -45l-230 -451q-212 33 -385 157.5t-272.5 316t-99.5 411.5q0 267 146 491zM1732 962 q58 -150 59.5 -310.5t-48.5 -306t-153 -272t-246 -209.5q-230 -133 -498 -119l405 623q88 131 82.5 290.5t-106.5 277.5zM896 942q125 0 213.5 -88.5t88.5 -213.5t-88.5 -213.5t-213.5 -88.5t-213.5 88.5t-88.5 213.5t88.5 213.5t213.5 88.5z" />
+<glyph unicode="&#xf269;" horiz-adv-x="1792" d="M903 -256q-283 0 -504.5 150.5t-329.5 398.5q-58 131 -67 301t26 332.5t111 312t179 242.5l-11 -281q11 14 68 15.5t70 -15.5q42 81 160.5 138t234.5 59q-54 -45 -119.5 -148.5t-58.5 -163.5q25 -8 62.5 -13.5t63 -7.5t68 -4t50.5 -3q15 -5 9.5 -45.5t-30.5 -75.5 q-5 -7 -16.5 -18.5t-56.5 -35.5t-101 -34l15 -189l-139 67q-18 -43 -7.5 -81.5t36 -66.5t65.5 -41.5t81 -6.5q51 9 98 34.5t83.5 45t73.5 17.5q61 -4 89.5 -33t19.5 -65q-1 -2 -2.5 -5.5t-8.5 -12.5t-18 -15.5t-31.5 -10.5t-46.5 -1q-60 -95 -144.5 -135.5t-209.5 -29.5 q74 -61 162.5 -82.5t168.5 -6t154.5 52t128 87.5t80.5 104q43 91 39 192.5t-37.5 188.5t-78.5 125q87 -38 137 -79.5t77 -112.5q15 170 -57.5 343t-209.5 284q265 -77 412 -279.5t151 -517.5q2 -127 -40.5 -255t-123.5 -238t-189 -196t-247.5 -135.5t-288.5 -49.5z" />
+<glyph unicode="&#xf26a;" horiz-adv-x="1792" d="M1493 1308q-165 110 -359 110q-155 0 -293 -73t-240 -200q-75 -93 -119.5 -218t-48.5 -266v-42q4 -141 48.5 -266t119.5 -218q102 -127 240 -200t293 -73q194 0 359 110q-121 -108 -274.5 -168t-322.5 -60q-29 0 -43 1q-175 8 -333 82t-272 193t-181 281t-67 339 q0 182 71 348t191 286t286 191t348 71h3q168 -1 320.5 -60.5t273.5 -167.5zM1792 640q0 -192 -77 -362.5t-213 -296.5q-104 -63 -222 -63q-137 0 -255 84q154 56 253.5 233t99.5 405q0 227 -99 404t-253 234q119 83 254 83q119 0 226 -65q135 -125 210.5 -295t75.5 -361z " />
+<glyph unicode="&#xf26b;" horiz-adv-x="1792" d="M1792 599q0 -56 -7 -104h-1151q0 -146 109.5 -244.5t257.5 -98.5q99 0 185.5 46.5t136.5 130.5h423q-56 -159 -170.5 -281t-267.5 -188.5t-321 -66.5q-187 0 -356 83q-228 -116 -394 -116q-237 0 -237 263q0 115 45 275q17 60 109 229q199 360 475 606 q-184 -79 -427 -354q63 274 283.5 449.5t501.5 175.5q30 0 45 -1q255 117 433 117q64 0 116 -13t94.5 -40.5t66.5 -76.5t24 -115q0 -116 -75 -286q101 -182 101 -390zM1722 1239q0 83 -53 132t-137 49q-108 0 -254 -70q121 -47 222.5 -131.5t170.5 -195.5q51 135 51 216z M128 2q0 -86 48.5 -132.5t134.5 -46.5q115 0 266 83q-122 72 -213.5 183t-137.5 245q-98 -205 -98 -332zM632 715h728q-5 142 -113 237t-251 95q-144 0 -251.5 -95t-112.5 -237z" />
+<glyph unicode="&#xf26c;" horiz-adv-x="2048" d="M1792 288v960q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1248v-960q0 -66 -47 -113t-113 -47h-736v-128h352q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23 v64q0 14 9 23t23 9h352v128h-736q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf26d;" horiz-adv-x="1792" d="M138 1408h197q-70 -64 -126 -149q-36 -56 -59 -115t-30 -125.5t-8.5 -120t10.5 -132t21 -126t28 -136.5q4 -19 6 -28q51 -238 81 -329q57 -171 152 -275h-272q-48 0 -82 34t-34 82v1304q0 48 34 82t82 34zM1346 1408h308q48 0 82 -34t34 -82v-1304q0 -48 -34 -82t-82 -34 h-178q212 210 196 565l-469 -101q-2 -45 -12 -82t-31 -72t-59.5 -59.5t-93.5 -36.5q-123 -26 -199 40q-32 27 -53 61t-51.5 129t-64.5 258q-35 163 -45.5 263t-5.5 139t23 77q20 41 62.5 73t102.5 45q45 12 83.5 6.5t67 -17t54 -35t43 -48t34.5 -56.5l468 100 q-68 175 -180 287z" />
+<glyph unicode="&#xf26e;" d="M1401 -11l-6 -6q-113 -114 -259 -175q-154 -64 -317 -64q-165 0 -317 64q-148 63 -259 175q-113 112 -175 258q-42 103 -54 189q-4 28 48 36q51 8 56 -20q1 -1 1 -4q18 -90 46 -159q50 -124 152 -226q98 -98 226 -152q132 -56 276 -56q143 0 276 56q128 55 225 152l6 6 q10 10 25 6q12 -3 33 -22q36 -37 17 -58zM929 604l-66 -66l63 -63q21 -21 -7 -49q-17 -17 -32 -17q-10 0 -19 10l-62 61l-66 -66q-5 -5 -15 -5q-15 0 -31 16l-2 2q-18 15 -18 29q0 7 8 17l66 65l-66 66q-16 16 14 45q18 18 31 18q6 0 13 -5l65 -66l65 65q18 17 48 -13 q27 -27 11 -44zM1400 547q0 -118 -46 -228q-45 -105 -126 -186q-80 -80 -187 -126t-228 -46t-228 46t-187 126q-82 82 -125 186q-15 32 -15 40h-1q-9 27 43 44q50 16 60 -12q37 -99 97 -167h1v339v2q3 136 102 232q105 103 253 103q147 0 251 -103t104 -249 q0 -147 -104.5 -251t-250.5 -104q-58 0 -112 16q-28 11 -13 61q16 51 44 43l14 -3q14 -3 32.5 -6t30.5 -3q104 0 176 71.5t72 174.5q0 101 -72 171q-71 71 -175 71q-107 0 -178 -80q-64 -72 -64 -160v-413q110 -67 242 -67q96 0 185 36.5t156 103.5t103.5 155t36.5 183 q0 198 -141 339q-140 140 -339 140q-200 0 -340 -140q-53 -53 -77 -87l-2 -2q-8 -11 -13 -15.5t-21.5 -9.5t-38.5 3q-21 5 -36.5 16.5t-15.5 26.5v680q0 15 10.5 26.5t27.5 11.5h877q30 0 30 -55t-30 -55h-811v-483h1q40 42 102 84t108 61q109 46 231 46q121 0 228 -46 t187 -126q81 -81 126 -186q46 -112 46 -229zM1369 1128q9 -8 9 -18t-5.5 -18t-16.5 -21q-26 -26 -39 -26q-9 0 -16 7q-106 91 -207 133q-128 56 -276 56q-133 0 -262 -49q-27 -10 -45 37q-9 25 -8 38q3 16 16 20q130 57 299 57q164 0 316 -64q137 -58 235 -152z" />
+<glyph unicode="&#xf270;" horiz-adv-x="1792" d="M1551 60q15 6 26 3t11 -17.5t-15 -33.5q-13 -16 -44 -43.5t-95.5 -68t-141 -74t-188 -58t-229.5 -24.5q-119 0 -238 31t-209 76.5t-172.5 104t-132.5 105t-84 87.5q-8 9 -10 16.5t1 12t8 7t11.5 2t11.5 -4.5q192 -117 300 -166q389 -176 799 -90q190 40 391 135z M1758 175q11 -16 2.5 -69.5t-28.5 -102.5q-34 -83 -85 -124q-17 -14 -26 -9t0 24q21 45 44.5 121.5t6.5 98.5q-5 7 -15.5 11.5t-27 6t-29.5 2.5t-35 0t-31.5 -2t-31 -3t-22.5 -2q-6 -1 -13 -1.5t-11 -1t-8.5 -1t-7 -0.5h-5.5h-4.5t-3 0.5t-2 1.5l-1.5 3q-6 16 47 40t103 30 q46 7 108 1t76 -24zM1364 618q0 -31 13.5 -64t32 -58t37.5 -46t33 -32l13 -11l-227 -224q-40 37 -79 75.5t-58 58.5l-19 20q-11 11 -25 33q-38 -59 -97.5 -102.5t-127.5 -63.5t-140 -23t-137.5 21t-117.5 65.5t-83 113t-31 162.5q0 84 28 154t72 116.5t106.5 83t122.5 57 t130 34.5t119.5 18.5t99.5 6.5v127q0 65 -21 97q-34 53 -121 53q-6 0 -16.5 -1t-40.5 -12t-56 -29.5t-56 -59.5t-48 -96l-294 27q0 60 22 119t67 113t108 95t151.5 65.5t190.5 24.5q100 0 181 -25t129.5 -61.5t81 -83t45 -86t12.5 -73.5v-589zM692 597q0 -86 70 -133 q66 -44 139 -22q84 25 114 123q14 45 14 101v162q-59 -2 -111 -12t-106.5 -33.5t-87 -71t-32.5 -114.5z" />
+<glyph unicode="&#xf271;" horiz-adv-x="1792" d="M1536 1280q52 0 90 -38t38 -90v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128zM1152 1376v-288q0 -14 9 -23t23 -9 h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 1376v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM1536 -128v1024h-1408v-1024h1408zM896 448h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224 v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224z" />
+<glyph unicode="&#xf272;" horiz-adv-x="1792" d="M1152 416v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23 -9t9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23 t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47 t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf273;" horiz-adv-x="1792" d="M1111 151l-46 -46q-9 -9 -22 -9t-23 9l-188 189l-188 -189q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22t9 23l189 188l-189 188q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l188 -188l188 188q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23l-188 -188l188 -188q9 -10 9 -23t-9 -22z M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf274;" horiz-adv-x="1792" d="M1303 572l-512 -512q-10 -9 -23 -9t-23 9l-288 288q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l220 -220l444 444q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23 t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47 t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf275;" horiz-adv-x="1792" d="M448 1536q26 0 45 -19t19 -45v-891l536 429q17 14 40 14q26 0 45 -19t19 -45v-379l536 429q17 14 40 14q26 0 45 -19t19 -45v-1152q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h384z" />
+<glyph unicode="&#xf276;" horiz-adv-x="1024" d="M512 448q66 0 128 15v-655q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v655q61 -15 128 -15zM512 1536q212 0 362 -150t150 -362t-150 -362t-362 -150t-362 150t-150 362t150 362t362 150zM512 1312q14 0 23 9t9 23t-9 23t-23 9q-146 0 -249 -103t-103 -249 q0 -14 9 -23t23 -9t23 9t9 23q0 119 84.5 203.5t203.5 84.5z" />
+<glyph unicode="&#xf277;" horiz-adv-x="1792" d="M1745 1239q10 -10 10 -23t-10 -23l-141 -141q-28 -28 -68 -28h-1344q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h576v64q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-64h512q40 0 68 -28zM768 320h256v-512q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v512zM1600 768 q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-1344q-40 0 -68 28l-141 141q-10 10 -10 23t10 23l141 141q28 28 68 28h512v192h256v-192h576z" />
+<glyph unicode="&#xf278;" horiz-adv-x="2048" d="M2020 1525q28 -20 28 -53v-1408q0 -20 -11 -36t-29 -23l-640 -256q-24 -11 -48 0l-616 246l-616 -246q-10 -5 -24 -5q-19 0 -36 11q-28 20 -28 53v1408q0 20 11 36t29 23l640 256q24 11 48 0l616 -246l616 246q32 13 60 -6zM736 1390v-1270l576 -230v1270zM128 1173 v-1270l544 217v1270zM1920 107v1270l-544 -217v-1270z" />
+<glyph unicode="&#xf279;" horiz-adv-x="1792" d="M512 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472q0 20 17 28l480 256q7 4 15 4zM1760 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472 q0 20 17 28l480 256q7 4 15 4zM640 1536q8 0 14 -3l512 -256q18 -10 18 -29v-1472q0 -13 -9.5 -22.5t-22.5 -9.5q-8 0 -14 3l-512 256q-18 10 -18 29v1472q0 13 9.5 22.5t22.5 9.5z" />
+<glyph unicode="&#xf27a;" horiz-adv-x="1792" d="M640 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 640q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-110 0 -211 18q-173 -173 -435 -229q-52 -10 -86 -13q-12 -1 -22 6t-13 18q-4 15 20 37q5 5 23.5 21.5t25.5 23.5t23.5 25.5t24 31.5t20.5 37 t20 48t14.5 57.5t12.5 72.5q-146 90 -229.5 216.5t-83.5 269.5q0 174 120 321.5t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
+<glyph unicode="&#xf27b;" horiz-adv-x="1792" d="M640 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 -53 -37.5 -90.5t-90.5 -37.5 t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5 t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51 t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 130 71 248.5t191 204.5t286 136.5t348 50.5t348 -50.5t286 -136.5t191 -204.5t71 -248.5z" />
+<glyph unicode="&#xf27c;" horiz-adv-x="1024" d="M512 345l512 295v-591l-512 -296v592zM0 640v-591l512 296zM512 1527v-591l-512 -296v591zM512 936l512 295v-591z" />
+<glyph unicode="&#xf27d;" horiz-adv-x="1792" d="M1709 1018q-10 -236 -332 -651q-333 -431 -562 -431q-142 0 -240 263q-44 160 -132 482q-72 262 -157 262q-18 0 -127 -76l-77 98q24 21 108 96.5t130 115.5q156 138 241 146q95 9 153 -55.5t81 -203.5q44 -287 66 -373q55 -249 120 -249q51 0 154 161q101 161 109 246 q13 139 -109 139q-57 0 -121 -26q120 393 459 382q251 -8 236 -326z" />
+<glyph unicode="&#xf27e;" d="M0 1408h1536v-1536h-1536v1536zM1085 293l-221 631l221 297h-634l221 -297l-221 -631l317 -304z" />
+<glyph unicode="&#xf280;" d="M0 1408h1536v-1536h-1536v1536zM908 1088l-12 -33l75 -83l-31 -114l25 -25l107 57l107 -57l25 25l-31 114l75 83l-12 33h-95l-53 96h-32l-53 -96h-95zM641 925q32 0 44.5 -16t11.5 -63l174 21q0 55 -17.5 92.5t-50.5 56t-69 25.5t-85 7q-133 0 -199 -57.5t-66 -182.5v-72 h-96v-128h76q20 0 20 -8v-382q0 -14 -5 -20t-18 -7l-73 -7v-88h448v86l-149 14q-6 1 -8.5 1.5t-3.5 2.5t-0.5 4t1 7t0.5 10v387h191l38 128h-231q-6 0 -2 6t4 9v80q0 27 1.5 40.5t7.5 28t19.5 20t36.5 5.5zM1248 96v86l-54 9q-7 1 -9.5 2.5t-2.5 3t1 7.5t1 12v520h-275 l-23 -101l83 -22q23 -7 23 -27v-370q0 -14 -6 -18.5t-20 -6.5l-70 -9v-86h352z" />
+<glyph unicode="&#xf281;" horiz-adv-x="1792" d="M1792 690q0 -58 -29.5 -105.5t-79.5 -72.5q12 -46 12 -96q0 -155 -106.5 -287t-290.5 -208.5t-400 -76.5t-399.5 76.5t-290 208.5t-106.5 287q0 47 11 94q-51 25 -82 73.5t-31 106.5q0 82 58 140.5t141 58.5q85 0 145 -63q218 152 515 162l116 521q3 13 15 21t26 5 l369 -81q18 37 54 59.5t79 22.5q62 0 106 -43.5t44 -105.5t-44 -106t-106 -44t-105.5 43.5t-43.5 105.5l-334 74l-104 -472q300 -9 519 -160q58 61 143 61q83 0 141 -58.5t58 -140.5zM418 491q0 -62 43.5 -106t105.5 -44t106 44t44 106t-44 105.5t-106 43.5q-61 0 -105 -44 t-44 -105zM1228 136q11 11 11 26t-11 26q-10 10 -25 10t-26 -10q-41 -42 -121 -62t-160 -20t-160 20t-121 62q-11 10 -26 10t-25 -10q-11 -10 -11 -25.5t11 -26.5q43 -43 118.5 -68t122.5 -29.5t91 -4.5t91 4.5t122.5 29.5t118.5 68zM1225 341q62 0 105.5 44t43.5 106 q0 61 -44 105t-105 44q-62 0 -106 -43.5t-44 -105.5t44 -106t106 -44z" />
+<glyph unicode="&#xf282;" horiz-adv-x="1792" d="M69 741h1q16 126 58.5 241.5t115 217t167.5 176t223.5 117.5t276.5 43q231 0 414 -105.5t294 -303.5q104 -187 104 -442v-188h-1125q1 -111 53.5 -192.5t136.5 -122.5t189.5 -57t213 -3t208 46.5t173.5 84.5v-377q-92 -55 -229.5 -92t-312.5 -38t-316 53 q-189 73 -311.5 249t-124.5 372q-3 242 111 412t325 268q-48 -60 -78 -125.5t-46 -159.5h635q8 77 -8 140t-47 101.5t-70.5 66.5t-80.5 41t-75 20.5t-56 8.5l-22 1q-135 -5 -259.5 -44.5t-223.5 -104.5t-176 -140.5t-138 -163.5z" />
+<glyph unicode="&#xf283;" horiz-adv-x="2304" d="M0 32v608h2304v-608q0 -66 -47 -113t-113 -47h-1984q-66 0 -113 47t-47 113zM640 256v-128h384v128h-384zM256 256v-128h256v128h-256zM2144 1408q66 0 113 -47t47 -113v-224h-2304v224q0 66 47 113t113 47h1984z" />
+<glyph unicode="&#xf284;" horiz-adv-x="1792" d="M1549 857q55 0 85.5 -28.5t30.5 -83.5t-34 -82t-91 -27h-136v-177h-25v398h170zM1710 267l-4 -11l-5 -10q-113 -230 -330.5 -366t-474.5 -136q-182 0 -348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71q244 0 454.5 -124t329.5 -338l2 -4l8 -16 q-30 -15 -136.5 -68.5t-163.5 -84.5q-6 -3 -479 -268q384 -183 799 -366zM896 -234q250 0 462.5 132.5t322.5 357.5l-287 129q-72 -140 -206 -222t-292 -82q-151 0 -280 75t-204 204t-75 280t75 280t204 204t280 75t280 -73.5t204 -204.5l280 143q-116 208 -321 329 t-443 121q-119 0 -232.5 -31.5t-209 -87.5t-176.5 -137t-137 -176.5t-87.5 -209t-31.5 -232.5t31.5 -232.5t87.5 -209t137 -176.5t176.5 -137t209 -87.5t232.5 -31.5z" />
+<glyph unicode="&#xf285;" horiz-adv-x="1792" d="M1427 827l-614 386l92 151h855zM405 562l-184 116v858l1183 -743zM1424 697l147 -95v-858l-532 335zM1387 718l-500 -802h-855l356 571z" />
+<glyph unicode="&#xf286;" horiz-adv-x="1792" d="M640 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1152 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1664 496v-752h-640v320q0 80 -56 136t-136 56t-136 -56t-56 -136v-320h-640v752q0 16 16 16h96 q16 0 16 -16v-112h128v624q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h16v393q-32 19 -32 55q0 26 19 45t45 19t45 -19t19 -45q0 -36 -32 -55v-9h272q16 0 16 -16v-224q0 -16 -16 -16h-272v-128h16q16 0 16 -16v-112h128 v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-624h128v112q0 16 16 16h96q16 0 16 -16z" />
+<glyph unicode="&#xf287;" horiz-adv-x="2304" d="M2288 731q16 -8 16 -27t-16 -27l-320 -192q-8 -5 -16 -5q-9 0 -16 4q-16 10 -16 28v128h-858q37 -58 83 -165q16 -37 24.5 -55t24 -49t27 -47t27 -34t31.5 -26t33 -8h96v96q0 14 9 23t23 9h320q14 0 23 -9t9 -23v-320q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v96h-96 q-32 0 -61 10t-51 23.5t-45 40.5t-37 46t-33.5 57t-28.5 57.5t-28 60.5q-23 53 -37 81.5t-36 65t-44.5 53.5t-46.5 17h-360q-22 -84 -91 -138t-157 -54q-106 0 -181 75t-75 181t75 181t181 75q88 0 157 -54t91 -138h104q24 0 46.5 17t44.5 53.5t36 65t37 81.5q19 41 28 60.5 t28.5 57.5t33.5 57t37 46t45 40.5t51 23.5t61 10h107q21 57 70 92.5t111 35.5q80 0 136 -56t56 -136t-56 -136t-136 -56q-62 0 -111 35.5t-70 92.5h-107q-17 0 -33 -8t-31.5 -26t-27 -34t-27 -47t-24 -49t-24.5 -55q-46 -107 -83 -165h1114v128q0 18 16 28t32 -1z" />
+<glyph unicode="&#xf288;" horiz-adv-x="1792" d="M1150 774q0 -56 -39.5 -95t-95.5 -39h-253v269h253q56 0 95.5 -39.5t39.5 -95.5zM1329 774q0 130 -91.5 222t-222.5 92h-433v-896h180v269h253q130 0 222 91.5t92 221.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348 t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf289;" horiz-adv-x="2304" d="M1645 438q0 59 -34 106.5t-87 68.5q-7 -45 -23 -92q-7 -24 -27.5 -38t-44.5 -14q-12 0 -24 3q-31 10 -45 38.5t-4 58.5q23 71 23 143q0 123 -61 227.5t-166 165.5t-228 61q-134 0 -247 -73t-167 -194q108 -28 188 -106q22 -23 22 -55t-22 -54t-54 -22t-55 22 q-75 75 -180 75q-106 0 -181 -74.5t-75 -180.5t75 -180.5t181 -74.5h1046q79 0 134.5 55.5t55.5 133.5zM1798 438q0 -142 -100.5 -242t-242.5 -100h-1046q-169 0 -289 119.5t-120 288.5q0 153 100 267t249 136q62 184 221 298t354 114q235 0 408.5 -158.5t196.5 -389.5 q116 -25 192.5 -118.5t76.5 -214.5zM2048 438q0 -175 -97 -319q-23 -33 -64 -33q-24 0 -43 13q-26 17 -32 48.5t12 57.5q71 104 71 233t-71 233q-18 26 -12 57t32 49t57.5 11.5t49.5 -32.5q97 -142 97 -318zM2304 438q0 -244 -134 -443q-23 -34 -64 -34q-23 0 -42 13 q-26 18 -32.5 49t11.5 57q108 164 108 358q0 195 -108 357q-18 26 -11.5 57.5t32.5 48.5q26 18 57 12t49 -33q134 -198 134 -442z" />
+<glyph unicode="&#xf28a;" d="M1500 -13q0 -89 -63 -152.5t-153 -63.5t-153.5 63.5t-63.5 152.5q0 90 63.5 153.5t153.5 63.5t153 -63.5t63 -153.5zM1267 268q-115 -15 -192.5 -102.5t-77.5 -205.5q0 -74 33 -138q-146 -78 -379 -78q-109 0 -201 21t-153.5 54.5t-110.5 76.5t-76 85t-44.5 83 t-23.5 66.5t-6 39.5q0 19 4.5 42.5t18.5 56t36.5 58t64 43.5t94.5 18t94 -17.5t63 -41t35.5 -53t17.5 -49t4 -33.5q0 -34 -23 -81q28 -27 82 -42t93 -17l40 -1q115 0 190 51t75 133q0 26 -9 48.5t-31.5 44.5t-49.5 41t-74 44t-93.5 47.5t-119.5 56.5q-28 13 -43 20 q-116 55 -187 100t-122.5 102t-72 125.5t-20.5 162.5q0 78 20.5 150t66 137.5t112.5 114t166.5 77t221.5 28.5q120 0 220 -26t164.5 -67t109.5 -94t64 -105.5t19 -103.5q0 -46 -15 -82.5t-36.5 -58t-48.5 -36t-49 -19.5t-39 -5h-8h-32t-39 5t-44 14t-41 28t-37 46t-24 70.5 t-10 97.5q-15 16 -59 25.5t-81 10.5l-37 1q-68 0 -117.5 -31t-70.5 -70t-21 -76q0 -24 5 -43t24 -46t53 -51t97 -53.5t150 -58.5q76 -25 138.5 -53.5t109 -55.5t83 -59t60.5 -59.5t41 -62.5t26.5 -62t14.5 -63.5t6 -62t1 -62.5z" />
+<glyph unicode="&#xf28b;" d="M704 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1152 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103 t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf28c;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273 t73 -273t198 -198t273 -73zM864 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192z" />
+<glyph unicode="&#xf28d;" d="M1088 352v576q0 14 -9 23t-23 9h-576q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h576q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
+<glyph unicode="&#xf28e;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273 t73 -273t198 -198t273 -73zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h576q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-576z" />
+<glyph unicode="&#xf290;" horiz-adv-x="1792" d="M1757 128l35 -313q3 -28 -16 -50q-19 -21 -48 -21h-1664q-29 0 -48 21q-19 22 -16 50l35 313h1722zM1664 967l86 -775h-1708l86 775q3 24 21 40.5t43 16.5h256v-128q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5v128h384v-128q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5v128h256q25 0 43 -16.5t21 -40.5zM1280 1152v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 159 112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
+<glyph unicode="&#xf291;" horiz-adv-x="2048" d="M1920 768q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5h-15l-115 -662q-8 -46 -44 -76t-82 -30h-1280q-46 0 -82 30t-44 76l-115 662h-15q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5h1792zM485 -32q26 2 43.5 22.5t15.5 46.5l-32 416q-2 26 -22.5 43.5 t-46.5 15.5t-43.5 -22.5t-15.5 -46.5l32 -416q2 -25 20.5 -42t43.5 -17h5zM896 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1280 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1632 27l32 416 q2 26 -15.5 46.5t-43.5 22.5t-46.5 -15.5t-22.5 -43.5l-32 -416q-2 -26 15.5 -46.5t43.5 -22.5h5q25 0 43.5 17t20.5 42zM476 1244l-93 -412h-132l101 441q19 88 89 143.5t160 55.5h167q0 26 19 45t45 19h384q26 0 45 -19t19 -45h167q90 0 160 -55.5t89 -143.5l101 -441 h-132l-93 412q-11 44 -45.5 72t-79.5 28h-167q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45h-167q-45 0 -79.5 -28t-45.5 -72z" />
+<glyph unicode="&#xf292;" horiz-adv-x="1792" d="M991 512l64 256h-254l-64 -256h254zM1759 1016l-56 -224q-7 -24 -31 -24h-327l-64 -256h311q15 0 25 -12q10 -14 6 -28l-56 -224q-5 -24 -31 -24h-327l-81 -328q-7 -24 -31 -24h-224q-16 0 -26 12q-9 12 -6 28l78 312h-254l-81 -328q-7 -24 -31 -24h-225q-15 0 -25 12 q-9 12 -6 28l78 312h-311q-15 0 -25 12q-9 12 -6 28l56 224q7 24 31 24h327l64 256h-311q-15 0 -25 12q-10 14 -6 28l56 224q5 24 31 24h327l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h254l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h311 q15 0 25 -12q9 -12 6 -28z" />
+<glyph unicode="&#xf293;" d="M841 483l148 -148l-149 -149zM840 1094l149 -149l-148 -148zM710 -130l464 464l-306 306l306 306l-464 464v-611l-255 255l-93 -93l320 -321l-320 -321l93 -93l255 255v-611zM1429 640q0 -209 -32 -365.5t-87.5 -257t-140.5 -162.5t-181.5 -86.5t-219.5 -24.5 t-219.5 24.5t-181.5 86.5t-140.5 162.5t-87.5 257t-32 365.5t32 365.5t87.5 257t140.5 162.5t181.5 86.5t219.5 24.5t219.5 -24.5t181.5 -86.5t140.5 -162.5t87.5 -257t32 -365.5z" />
+<glyph unicode="&#xf294;" horiz-adv-x="1024" d="M596 113l173 172l-173 172v-344zM596 823l173 172l-173 172v-344zM628 640l356 -356l-539 -540v711l-297 -296l-108 108l372 373l-372 373l108 108l297 -296v711l539 -540z" />
+<glyph unicode="&#xf295;" d="M1280 256q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM512 1024q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5 t112.5 -271.5zM1440 1344q0 -20 -13 -38l-1056 -1408q-19 -26 -51 -26h-160q-26 0 -45 19t-19 45q0 20 13 38l1056 1408q19 26 51 26h160q26 0 45 -19t19 -45zM768 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
+<glyph unicode="&#xf296;" horiz-adv-x="1792" />
+<glyph unicode="&#xf297;" horiz-adv-x="1792" />
+<glyph unicode="&#xf298;" horiz-adv-x="1792" />
+<glyph unicode="&#xf299;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29a;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29b;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29c;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29d;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29e;" horiz-adv-x="1792" />
+<glyph unicode="&#xf500;" horiz-adv-x="1792" />
+</font>
+</defs></svg> 
\ No newline at end of file
diff --git a/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.ttf b/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.ttf
new file mode 100755
index 0000000..26dea79
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.ttf
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.woff b/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.woff
new file mode 100755
index 0000000..dc35ce3
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.woff
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.woff2 b/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.woff2
new file mode 100755
index 0000000..500e517
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/fonts/fontawesome-webfont.woff2
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/images/caution.png b/jetty-documentation/src/main/docbkx-resources/images/caution.png
new file mode 100644
index 0000000..5b7809c
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/caution.png
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/images/caution.svg b/jetty-documentation/src/main/docbkx-resources/images/caution.svg
new file mode 100644
index 0000000..dd84f3f
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/caution.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In  -->
+<!DOCTYPE svg [
+	<!ENTITY st0 "fill:#FFFFFF;stroke:none;">
+	<!ENTITY st1 "fill:#FFFFFF;stroke-width:6.6112;stroke-linecap:round;stroke-linejoin:round;">
+	<!ENTITY st2 "stroke:#FFFFFF;stroke-width:6.6112;">
+	<!ENTITY st3 "fill:none;stroke:none;">
+	<!ENTITY st4 "fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
+	<!ENTITY st5 "stroke:none;">
+]>
+<svg  width="48pt" height="48pt" viewBox="0 0 48 48" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
+	<g id="Layer_x0020_3" style="&st4;">
+		<g>
+			<path style="&st2;" d="M41.7,35.3L26.6,9.4c-0.6-1-1.7-1.7-2.9-1.6c-1.2,0-2.3,0.7-2.9,1.7L6.3,35.4c-0.6,1-0.6,2.3,0,3.3c0.6,1,1.7,1.6,2.9,1.6h29.6c1.2,0,2.3-0.6,2.9-1.7c0.6-1,0.6-2.3,0-3.3z"/>
+			<path style="&st1;" d="M23.7,11L9.2,37h29.6L23.7,11z"/>
+			<path style="&st0;" d="M23.7,11.9L10.3,36.1h27.5l-14-24.1z"/>
+			<g>
+				<path style="&st5;" d="M24.1,34c-1.1,0-1.8-0.8-1.8-1.8c0-1.1,0.7-1.8,1.8-1.8c1.1,0,1.8,0.7,1.8,1.8c0,1-0.7,1.8-1.8,1.8h0z M22.9,29.3l-0.4-9.1h3.2l-0.4,9.1h-2.3z"/>
+			</g>
+		</g>
+	</g>
+	<g id="crop_x0020_marks" style="&st4;">
+		<path style="&st3;" d="M48,48H0V0h48v48z"/>
+	</g>
+</svg>
diff --git a/jetty-documentation/src/main/docbkx-resources/images/draft-ribbon.png b/jetty-documentation/src/main/docbkx-resources/images/draft-ribbon.png
new file mode 100644
index 0000000..98d5640
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/draft-ribbon.png
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/images/favicon.ico b/jetty-documentation/src/main/docbkx-resources/images/favicon.ico
new file mode 100644
index 0000000..ea9e174
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/favicon.ico
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/images/important.png b/jetty-documentation/src/main/docbkx-resources/images/important.png
new file mode 100644
index 0000000..12c90f6
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/important.png
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/images/important.svg b/jetty-documentation/src/main/docbkx-resources/images/important.svg
new file mode 100644
index 0000000..dd84f3f
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/important.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In  -->
+<!DOCTYPE svg [
+	<!ENTITY st0 "fill:#FFFFFF;stroke:none;">
+	<!ENTITY st1 "fill:#FFFFFF;stroke-width:6.6112;stroke-linecap:round;stroke-linejoin:round;">
+	<!ENTITY st2 "stroke:#FFFFFF;stroke-width:6.6112;">
+	<!ENTITY st3 "fill:none;stroke:none;">
+	<!ENTITY st4 "fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
+	<!ENTITY st5 "stroke:none;">
+]>
+<svg  width="48pt" height="48pt" viewBox="0 0 48 48" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
+	<g id="Layer_x0020_3" style="&st4;">
+		<g>
+			<path style="&st2;" d="M41.7,35.3L26.6,9.4c-0.6-1-1.7-1.7-2.9-1.6c-1.2,0-2.3,0.7-2.9,1.7L6.3,35.4c-0.6,1-0.6,2.3,0,3.3c0.6,1,1.7,1.6,2.9,1.6h29.6c1.2,0,2.3-0.6,2.9-1.7c0.6-1,0.6-2.3,0-3.3z"/>
+			<path style="&st1;" d="M23.7,11L9.2,37h29.6L23.7,11z"/>
+			<path style="&st0;" d="M23.7,11.9L10.3,36.1h27.5l-14-24.1z"/>
+			<g>
+				<path style="&st5;" d="M24.1,34c-1.1,0-1.8-0.8-1.8-1.8c0-1.1,0.7-1.8,1.8-1.8c1.1,0,1.8,0.7,1.8,1.8c0,1-0.7,1.8-1.8,1.8h0z M22.9,29.3l-0.4-9.1h3.2l-0.4,9.1h-2.3z"/>
+			</g>
+		</g>
+	</g>
+	<g id="crop_x0020_marks" style="&st4;">
+		<path style="&st3;" d="M48,48H0V0h48v48z"/>
+	</g>
+</svg>
diff --git a/jetty-documentation/src/main/docbkx-resources/images/jetty-avatar.svg b/jetty-documentation/src/main/docbkx-resources/images/jetty-avatar.svg
new file mode 100644
index 0000000..eef377e
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/jetty-avatar.svg
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="400"
+   height="400"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.48.1 r9760"
+   version="1.0"
+   sodipodi:docname="jetty-avatar.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/joakim/code/intalio/logos/jetty-avatar.png"
+   inkscape:export-xdpi="28.799999"
+   inkscape:export-ydpi="28.799999">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+    <inkscape:perspective
+       id="perspective2390"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <filter
+       inkscape:collect="always"
+       id="filter3853"
+       x="-0.15826963"
+       width="1.3165393"
+       y="-0.20584013"
+       height="1.4116803">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="26.292865"
+         id="feGaussianBlur3855" />
+    </filter>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#525252"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.87428571"
+     inkscape:cx="368.87255"
+     inkscape:cy="200"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer2"
+     showgrid="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1056"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     showborder="true"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Jetty"
+     style="display:inline"
+     transform="translate(-1.845606,-221.31978)">
+    <g
+       id="g3857"
+       transform="matrix(0.88669095,0,0,0.88669095,25.158515,48.38446)">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3004"
+         d="m 84.37204,268.03851 a 12.50125,12.50125 0 0 0 -12.1875,9.6875 l -10.3125,44.75 a 12.50125,12.50125 0 0 0 0.71875,7.75 12.50125,12.50125 0 0 0 -4.0625,6.6875 L 18.68454,509.44476 c -0.93999,0.0793 -1.75306,0.16127 -3.21875,0.21875 a 12.50125,12.50125 0 0 0 -12,12.1875 l -0.96875,39.9375 a 12.50125,12.50125 0 0 0 12.53125,12.8125 l 67.40625,-0.25 a 12.50125,12.50125 0 0 0 0.625,0 c 8.74629,-0.48292 17.66461,-3.77402 24.21875,-10.15625 6.55414,-6.38223 10.64721,-14.90394 13.6875,-25.25 a 12.50125,12.50125 0 0 0 0.1875,-0.6875 l 5.625,-24.3125 3.75,0 -4.0625,8.6875 a 12.50125,12.50125 0 0 0 11.34375,17.78125 l 42.15625,0 a 12.50125,12.50125 0 0 0 11.34375,-7.21875 l 115.34375,-247.375 a 12.50125,12.50125 0 0 0 -11.3125,-17.78125 l -42.1875,0 a 12.50125,12.50125 0 0 0 -11.3125,7.21875 l -24.59375,52.75 a 12.50125,12.50125 0 0 0 -4.375,-0.78125 l -43.03125,0 10.15625,-43.875 a 12.50125,12.50125 0 0 0 -12.1875,-15.3125 l -83.4375,0 z m 262.15625,0 a 12.50125,12.50125 0 0 0 -11.3125,7.21875 l -115.375,247.375 a 12.50125,12.50125 0 0 0 11.34375,17.78125 l 42.15625,0 a 12.50125,12.50125 0 0 0 11.34375,-7.21875 l 115.34375,-247.375 a 12.50125,12.50125 0 0 0 -11.3125,-17.78125 l -42.1875,0 z m -196.75,146.28125 27.21875,0 -5.84375,12.53125 -24.28125,0 2.90625,-12.53125 z"
+         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:25;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3853);enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
+      <g
+         id="g3058">
+        <g
+           id="g3032"
+           transform="translate(-33.169935,0)">
+          <path
+             id="path2996"
+             d="M 106.32205,339.73892 65.825288,515.14967 c -2.406515,7.05911 -6.072322,6.68223 -14.254473,7.0031 l -0.962606,39.94815 67.382421,-0.24066 c 13.0754,-0.72195 21.22428,-8.13437 26.59883,-26.42388 l 45.18031,-195.69746 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:28.19471741;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path2994"
+             d="m 119.98951,280.53864 -10.33393,44.76119 83.44772,0 10.33393,-44.76119 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:28.19471741;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3020"
+             d="M 106.32205,339.73892 65.825288,515.14967 c -2.406515,7.05911 -6.072322,6.68223 -14.254473,7.0031 l -0.962606,39.94815 67.382421,-0.24066 c 13.0754,-0.72195 21.22428,-8.13437 26.59883,-26.42388 l 45.18031,-195.69746 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3018"
+             d="m 119.98951,280.53864 -10.33393,44.76119 83.44772,0 10.33393,-44.76119 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none"
+             inkscape:connector-curvature="0" />
+        </g>
+        <g
+           id="g3046"
+           transform="translate(-287.0915,0)">
+          <path
+             id="path2988"
+             d="m 542.7018,280.53864 -115.3597,247.38976 42.1718,0 115.3597,-247.38976 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:28.19471741;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3193"
+             d="m 636.0746,280.53864 -115.3597,247.38976 42.1718,0 115.3597,-247.38976 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:28.19471741;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3012"
+             d="m 542.7018,280.53864 -115.3597,247.38976 42.1718,0 115.3597,-247.38976 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3316"
+             d="m 636.0746,280.53864 -115.3597,247.38976 42.1718,0 115.3597,-247.38976 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none"
+             inkscape:connector-curvature="0" />
+        </g>
+        <g
+           id="g3052"
+           transform="translate(-239.05228,0)">
+          <path
+             id="path2992"
+             d="m 370.6225,339.73892 -14.3342,62.08809 83.7467,0 14.3342,-62.08809 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:28.19471741;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path2990"
+             d="m 347.6212,439.36864 -14.3342,62.08809 83.7466,0 14.3342,-62.08809 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:28.19471741;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3016"
+             d="m 370.6225,339.73892 -14.3342,62.08809 83.7467,0 14.3342,-62.08809 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3014"
+             d="m 347.6212,439.36864 -14.3342,62.08809 83.7466,0 14.3342,-62.08809 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none"
+             inkscape:connector-curvature="0" />
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/jetty-documentation/src/main/docbkx-resources/images/jetty-header-logo.png b/jetty-documentation/src/main/docbkx-resources/images/jetty-header-logo.png
new file mode 100644
index 0000000..5f1b1e0
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/jetty-header-logo.png
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/images/jetty-logo-shadow.png b/jetty-documentation/src/main/docbkx-resources/images/jetty-logo-shadow.png
new file mode 100644
index 0000000..c61f70b
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/jetty-logo-shadow.png
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/images/jetty-logo-shadow.svg b/jetty-documentation/src/main/docbkx-resources/images/jetty-logo-shadow.svg
new file mode 100644
index 0000000..a93014b
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/jetty-logo-shadow.svg
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="1400"
+   height="400"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.48.3.1 r9886"
+   version="1.0"
+   sodipodi:docname="jetty-logo-shadow.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/joakim/code/intalio/jetty-documentation/src/docbkx/images/jetty-logo-shadow.png"
+   inkscape:export-xdpi="34.09"
+   inkscape:export-ydpi="34.09">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+    <inkscape:perspective
+       id="perspective2390"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <filter
+       inkscape:collect="always"
+       id="filter3295"
+       x="-0.036928206"
+       width="1.0738564"
+       y="-0.15990376"
+       height="1.3198075">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="20.425209"
+         id="feGaussianBlur3297" />
+    </filter>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.61821336"
+     inkscape:cx="638.23248"
+     inkscape:cy="439.84756"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1679"
+     inkscape:window-height="1001"
+     inkscape:window-x="1"
+     inkscape:window-y="48"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     showborder="true"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer1"
+     inkscape:label="Drop Shadow">
+    <path
+       style="fill:#121212;fill-opacity:0.76888889000000005;fill-rule:evenodd;stroke:none;stroke-width:25;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline;filter:url(#filter3295)"
+       d="M 133.14892,74.210753 C 127.32606,74.207483 122.27201,78.224803 120.96142,83.898253 L 110.61767,128.6795 C 110.03629,131.2768 110.29904,133.99187 111.36767,136.4295 C 109.33278,138.16652 107.90884,140.51055 107.30517,143.117 L 67.46142,315.617 C 66.521262,315.6963 65.709017,315.77825 64.24267,315.83575 C 57.642403,316.0821 52.37259,321.42036 52.21142,328.02325 L 51.27392,367.992 C 51.198431,371.36099 52.486258,374.61778 54.845444,377.02403 C 57.20463,379.43028 60.435344,380.78218 63.80517,380.77325 L 131.18017,380.5545 C 131.39916,380.54984 131.61798,380.53942 131.83642,380.52325 C 140.58271,380.04033 149.50103,376.74923 156.05517,370.367 C 162.60931,363.98478 166.70238,355.49431 169.74267,345.14825 C 169.81228,344.91058 169.8748,344.67089 169.93017,344.4295 L 185.05517,278.83575 C 185.38711,284.40834 186.31145,289.70084 188.05517,294.71075 C 190.72151,302.37149 195.75549,309.41129 202.83642,313.867 C 209.91735,318.32271 218.43366,320.14825 227.64892,320.14825 L 398.49267,320.14825 C 404.30384,320.13699 409.34096,316.12283 410.64892,310.46075 L 418.64892,275.8045 C 419.50468,272.09658 418.62495,268.20076 416.25888,265.22037 C 413.89281,262.23997 410.29806,260.49955 406.49267,260.492 L 294.14892,260.492 L 295.27392,255.64825 L 413.36767,255.64825 C 419.17884,255.63699 424.21596,251.62283 425.52392,245.96075 C 425.994,243.92461 428.7761,237.60241 431.96142,227.0545 C 434.57148,218.41152 437.29104,206.4887 439.49267,190.1795 L 450.58642,190.1795 L 436.61767,250.58575 C 433.83028,262.65929 433.25054,278.51751 440.52392,293.367 C 447.7973,308.21649 464.78839,320.14825 488.77392,320.14825 L 587.55517,320.14825 C 593.36634,320.13699 598.40346,316.12283 599.71142,310.46075 L 607.71142,275.8045 C 608.56718,272.09658 607.68745,268.20076 605.32138,265.22037 C 602.95531,262.23997 599.36056,260.49955 595.55517,260.492 C 566.44463,260.49201 549.53568,259.6817 543.21142,258.867 C 543.42812,257.44728 543.42232,257.233 544.14892,254.08575 L 558.89892,190.1795 L 611.43017,190.1795 L 617.55517,190.1795 L 625.30517,190.1795 L 611.33642,250.58575 C 608.54904,262.65928 607.96929,278.51751 615.24267,293.367 C 622.51605,308.21649 639.50714,320.14825 663.49267,320.14825 L 762.27392,320.14825 C 768.08509,320.13699 773.12221,316.12283 774.43017,310.46075 L 776.71142,300.5545 C 784.37997,311.40324 799.92403,320.14825 823.74267,320.14825 L 876.71142,320.14825 C 870.84247,320.97 863.04125,321.617 852.30517,321.617 L 768.93017,321.617 C 763.10731,321.61373 758.05326,325.63105 756.74267,331.3045 L 749.30517,363.5545 C 748.44941,367.26243 749.32914,371.15825 751.69521,374.13864 C 754.06128,377.11903 757.65603,378.85945 761.46142,378.867 L 892.93017,378.867 C 923.24425,378.867 944.16687,379.27677 961.27392,374.33575 C 978.38097,369.39473 990.66904,355.65737 994.99267,336.9295 L 1038.4302,148.742 C 1039.2872,145.02873 1038.4036,141.12735 1036.0304,138.14563 C 1033.6572,135.16391 1030.0535,133.42752 1026.2427,133.4295 L 943.83642,133.4295 C 938.01356,133.42623 932.95951,137.44355 931.64892,143.117 L 904.55517,260.492 L 882.11767,260.492 L 907.93017,148.742 C 908.78715,145.02874 907.9036,141.12736 905.5304,138.14565 C 903.15719,135.16393 899.55354,133.42754 895.74267,133.4295 L 812.36767,133.4295 C 810.12122,133.43967 807.91903,134.05499 805.99267,135.21075 C 804.06631,134.05499 801.86412,133.43967 799.61767,133.4295 L 746.71142,133.4295 L 751.30517,113.58575 C 752.16093,109.87782 751.2812,105.98201 748.91513,103.00161 C 746.54906,100.02122 742.95431,98.280803 739.14892,98.273253 L 656.46142,98.273253 C 650.63856,98.269983 645.58451,102.2873 644.27392,107.96075 L 638.39892,133.4295 L 624.89892,133.4295 L 618.74267,133.4295 L 571.99267,133.4295 L 576.58642,113.58575 C 577.44218,109.87782 576.56245,105.98201 574.19638,103.00161 C 571.83031,100.02122 568.23556,98.280803 564.43017,98.273253 L 481.74267,98.273253 C 475.91981,98.269983 470.86576,102.2873 469.55517,107.96075 L 463.68017,133.4295 L 444.02392,133.4295 C 438.20106,133.42623 433.14701,137.44355 431.83642,143.117 L 430.02392,151.0545 C 421.62416,141.56409 407.22229,133.4295 387.68017,133.4295 L 252.46142,133.4295 C 237.21293,133.42952 222.49219,141.92039 213.46142,155.77325 L 215.08642,148.742 C 215.69431,146.1414 215.45338,143.41448 214.39892,140.96075 C 216.4166,139.22739 217.8288,136.89563 218.43017,134.3045 L 228.77392,89.523253 C 229.6309,85.809993 228.74735,81.908613 226.37415,78.926903 C 224.00094,75.945183 220.39729,74.208793 216.58642,74.210753 L 133.14892,74.210753 z M 1230.6802,74.210753 C 1225.8279,74.217643 1221.418,77.031713 1219.3676,81.429503 L 1121.7426,290.742 L 1131.5239,248.367 C 1132.3797,244.65907 1131.4999,240.76324 1129.1338,237.78285 C 1126.7678,234.80245 1123.173,233.06204 1119.3676,233.0545 L 1035.6176,233.0545 C 1029.7948,233.05126 1024.7408,237.06857 1023.4302,242.742 L 1009.0864,304.83575 C 1008.2294,308.54901 1009.113,312.45039 1011.4862,315.43211 C 1013.8594,318.41382 1017.463,320.15022 1021.2739,320.14825 L 1105.0239,320.14825 C 1106.1113,320.14302 1107.1933,319.99595 1108.2426,319.71075 L 1103.9926,328.83575 C 1102.1877,332.70937 1102.4865,337.2366 1104.7849,340.83938 C 1107.0834,344.44215 1111.0629,346.62121 1115.3364,346.617 L 1157.4926,346.617 C 1162.3561,346.62224 1166.7813,343.80626 1168.8364,339.39825 L 1284.1802,91.992003 C 1285.9828,88.123403 1285.6872,83.602493 1283.3963,80.001533 C 1281.1053,76.400563 1277.1356,74.217203 1272.8676,74.210753 L 1230.6802,74.210753 z M 1324.0552,74.210753 C 1319.2029,74.217643 1314.793,77.031713 1312.7426,81.429503 L 1197.3676,328.83575 C 1195.5627,332.70937 1195.8615,337.2366 1198.1599,340.83938 C 1200.4584,344.44215 1204.4379,346.62121 1208.7114,346.617 L 1250.8676,346.617 C 1255.7311,346.62224 1260.1563,343.80626 1262.2114,339.39825 L 1377.5552,91.992003 C 1379.3578,88.123403 1379.0622,83.602493 1376.7713,80.001533 C 1374.4803,76.400563 1370.5106,74.217203 1366.2426,74.210753 L 1324.0552,74.210753 z M 1058.6176,133.4295 C 1052.7948,133.42628 1047.7408,137.44358 1046.4302,143.117 L 1032.0864,205.21075 C 1031.2294,208.92401 1032.113,212.82539 1034.4862,215.80711 C 1036.8594,218.78882 1040.463,220.52522 1044.2739,220.52325 L 1128.0239,220.52325 C 1133.8561,220.52318 1138.9133,216.49037 1140.2114,210.8045 L 1154.5239,148.742 C 1155.3797,145.03407 1154.4999,141.13824 1152.1338,138.15784 C 1149.7678,135.17745 1146.173,133.43703 1142.3676,133.4295 L 1058.6176,133.4295 z M 321.14892,190.1795 C 324.49626,190.1795 326.64455,190.50681 327.77392,190.77325 C 327.79548,191.54482 327.76376,193.22107 327.61767,195.492 L 310.96142,195.492 C 311.68961,194.18337 312.37493,193.13181 312.93017,192.5545 C 314.73432,190.67863 315.72887,190.1795 321.14892,190.1795 z M 733.61767,190.1795 L 789.30517,190.1795 L 772.99267,260.8045 C 772.10059,260.60116 771.18888,260.49636 770.27392,260.492 C 741.16337,260.492 724.25443,259.6817 717.93017,258.867 C 718.14687,257.44728 718.14107,257.233 718.86767,254.08575 L 733.61767,190.1795 z"
+       id="path3181"
+       sodipodi:nodetypes="cccccccccscccssccssccccsccccsscccsccccsccccccccsccccccccccsccsccsccccccsccccccsccccccccsccccccccccccccsccccccsccccsccccsccccscccccsccccscccccsccccsccccccscccccccc" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Jetty"
+     style="display:inline"
+     transform="translate(-1.845606,-221.31978)">
+    <path
+       id="path3193"
+       style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:25;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 468.5751,304.59386 L 460.4501,339.75011 L 430.85635,339.75011 L 423.54385,371.50011 L 453.1376,371.50011 L 435.6376,447.21886 C 431.11514,466.80783 434.69559,501.46886 475.60635,501.46886 L 574.3876,501.46886 L 582.3876,466.81261 C 513.82035,466.81262 513.81207,466.80768 518.8251,445.09386 L 535.8251,371.50011 L 598.2626,371.50011 L 604.3876,371.50011 L 627.85635,371.50011 L 610.35635,447.21886 C 605.83388,466.80782 609.41434,501.46886 650.3251,501.46886 L 749.10635,501.46886 L 757.10635,466.81261 C 688.53909,466.81261 688.53082,466.80768 693.54385,445.09386 L 710.54385,371.50011 L 779.10635,371.50011 L 786.4501,339.75011 L 717.85635,339.75011 L 725.98135,304.59386 L 643.29385,304.59386 L 635.16885,339.75011 L 611.73135,339.75011 L 605.5751,339.75011 L 543.1376,339.75011 L 551.2626,304.59386 L 468.5751,304.59386 z M 106.32205,339.73892 L 65.825288,515.14967 C 63.418773,522.20878 59.752966,521.8319 51.570815,522.15277 L 50.608209,562.10092 L 117.99063,561.86026 C 131.06603,561.13831 139.21491,553.72589 144.58946,535.43638 L 189.76977,339.73892 L 106.32205,339.73892 z M 119.98951,280.53864 L 109.65558,325.29983 L 193.1033,325.29983 L 203.43723,280.53864 L 119.98951,280.53864 z M 239.29385,339.75011 C 224.85477,339.75012 208.26802,352.04925 204.6376,371.50011 L 188.6376,436.96886 C 179.27284,477.53211 184.20976,501.46886 214.48135,501.46886 L 385.3251,501.46886 L 393.3251,466.81261 L 265.29385,466.81261 L 272.16885,436.96886 L 400.2001,436.96886 C 402.2537,428.07372 410.80305,415.14706 415.3251,371.50011 C 416.6053,365.955 407.76262,339.75011 374.5126,339.75011 L 239.29385,339.75011 z M 307.98135,371.50011 C 325.75108,371.50011 330.91518,377.05789 325.2001,401.81261 L 280.29385,401.81261 C 285.80848,377.9261 293.45487,371.5001 307.98135,371.50011 z M 799.21238,339.73892 L 769.87735,466.80292 C 769.87735,466.80292 761.87689,501.45673 810.59962,501.45673 L 893.34691,501.45673 C 889.21672,519.34655 887.23544,527.9284 839.14197,527.9284 L 755.76542,527.9284 L 748.32055,560.1757 L 879.79056,560.1757 C 940.41876,560.1757 962.20636,560.1757 969.65123,527.9284 L 1013.0982,339.73892 L 930.6824,339.73892 L 901.34737,466.80292 L 853.2539,466.80292 L 882.58893,339.73892 L 799.21238,339.73892 z M 1045.4591,339.73892 L 1031.1249,401.82701 L 1114.8716,401.82701 L 1129.2058,339.73892 L 1045.4591,339.73892 z M 1022.4578,439.36864 L 1008.1236,501.45673 L 1091.8702,501.45673 L 1106.2044,439.36864 L 1022.4578,439.36864 z M 1217.5384,280.53864 L 1102.1787,527.9284 L 1144.3505,527.9284 L 1259.7102,280.53864 L 1217.5384,280.53864 z M 1310.9112,280.53864 L 1195.5515,527.9284 L 1237.7233,527.9284 L 1353.083,280.53864 L 1310.9112,280.53864 z" />
+    <path
+       d="M 468.5751,304.59386 L 460.4501,339.75011 L 430.85635,339.75011 L 423.54385,371.50011 L 453.1376,371.50011 L 435.6376,447.21886 C 431.11514,466.80783 434.69559,501.46886 475.60635,501.46886 L 574.3876,501.46886 L 582.3876,466.81261 C 513.82035,466.81262 513.81207,466.80768 518.8251,445.09386 L 535.8251,371.50011 L 598.2626,371.50011 L 604.3876,371.50011 L 627.85635,371.50011 L 610.35635,447.21886 C 605.83388,466.80782 609.41434,501.46886 650.3251,501.46886 L 749.10635,501.46886 L 757.10635,466.81261 C 688.53909,466.81261 688.53082,466.80768 693.54385,445.09386 L 710.54385,371.50011 L 779.10635,371.50011 L 786.4501,339.75011 L 717.85635,339.75011 L 725.98135,304.59386 L 643.29385,304.59386 L 635.16885,339.75011 L 611.73135,339.75011 L 605.5751,339.75011 L 543.1376,339.75011 L 551.2626,304.59386 L 468.5751,304.59386 z M 106.32205,339.73892 L 65.825288,515.14967 C 63.418773,522.20878 59.752966,521.8319 51.570815,522.15277 L 50.608209,562.10092 L 117.99063,561.86026 C 131.06603,561.13831 139.21491,553.72589 144.58946,535.43638 L 189.76977,339.73892 L 106.32205,339.73892 z M 119.98951,280.53864 L 109.65558,325.29983 L 193.1033,325.29983 L 203.43723,280.53864 L 119.98951,280.53864 z M 239.29385,339.75011 C 224.85477,339.75012 208.26802,352.04925 204.6376,371.50011 L 188.6376,436.96886 C 179.27284,477.53211 184.20976,501.46886 214.48135,501.46886 L 385.3251,501.46886 L 393.3251,466.81261 L 265.29385,466.81261 L 272.16885,436.96886 L 400.2001,436.96886 C 402.2537,428.07372 410.80305,415.14706 415.3251,371.50011 C 416.6053,365.955 407.76262,339.75011 374.5126,339.75011 L 239.29385,339.75011 z M 307.98135,371.50011 C 325.75108,371.50011 330.91518,377.05789 325.2001,401.81261 L 280.29385,401.81261 C 285.80848,377.9261 293.45487,371.5001 307.98135,371.50011 z M 799.21238,339.73892 L 769.87735,466.80292 C 769.87735,466.80292 761.87689,501.45673 810.59962,501.45673 L 893.34691,501.45673 C 889.21672,519.34655 887.23544,527.9284 839.14197,527.9284 L 755.76542,527.9284 L 748.32055,560.1757 L 879.79056,560.1757 C 940.41876,560.1757 962.20636,560.1757 969.65123,527.9284 L 1013.0982,339.73892 L 930.6824,339.73892 L 901.34737,466.80292 L 853.2539,466.80292 L 882.58893,339.73892 L 799.21238,339.73892 z M 1045.4591,339.73892 L 1031.1249,401.82701 L 1114.8716,401.82701 L 1129.2058,339.73892 L 1045.4591,339.73892 z M 1022.4578,439.36864 L 1008.1236,501.45673 L 1091.8702,501.45673 L 1106.2044,439.36864 L 1022.4578,439.36864 z M 1217.5384,280.53864 L 1102.1787,527.9284 L 1144.3505,527.9284 L 1259.7102,280.53864 L 1217.5384,280.53864 z M 1310.9112,280.53864 L 1195.5515,527.9284 L 1237.7233,527.9284 L 1353.083,280.53864 L 1310.9112,280.53864 z"
+       style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:30;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3316" />
+  </g>
+</svg>
diff --git a/jetty-documentation/src/main/docbkx-resources/images/jetty-logo.svg b/jetty-documentation/src/main/docbkx-resources/images/jetty-logo.svg
new file mode 100644
index 0000000..5f8d6fd
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/jetty-logo.svg
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="1400"
+   height="400"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="jetty-logo.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/joakim/documents/Webtide/jetty-logo.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+    <inkscape:perspective
+       id="perspective2390"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       sodipodi:type="inkscape:persp3d" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.87428571"
+     inkscape:cx="700"
+     inkscape:cy="200"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer2"
+     showgrid="false"
+     inkscape:window-width="1679"
+     inkscape:window-height="1001"
+     inkscape:window-x="1"
+     inkscape:window-y="48"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     showborder="true" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Jetty"
+     style="display:inline"
+     transform="translate(-1.845606,-221.31978)">
+    <path
+       id="path3193"
+       style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:25;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 468.5751,304.59386 L 460.4501,339.75011 L 430.85635,339.75011 L 423.54385,371.50011 L 453.1376,371.50011 L 435.6376,447.21886 C 431.11514,466.80783 434.69559,501.46886 475.60635,501.46886 L 574.3876,501.46886 L 582.3876,466.81261 C 513.82035,466.81262 513.81207,466.80768 518.8251,445.09386 L 535.8251,371.50011 L 598.2626,371.50011 L 604.3876,371.50011 L 627.85635,371.50011 L 610.35635,447.21886 C 605.83388,466.80782 609.41434,501.46886 650.3251,501.46886 L 749.10635,501.46886 L 757.10635,466.81261 C 688.53909,466.81261 688.53082,466.80768 693.54385,445.09386 L 710.54385,371.50011 L 779.10635,371.50011 L 786.4501,339.75011 L 717.85635,339.75011 L 725.98135,304.59386 L 643.29385,304.59386 L 635.16885,339.75011 L 611.73135,339.75011 L 605.5751,339.75011 L 543.1376,339.75011 L 551.2626,304.59386 L 468.5751,304.59386 z M 106.32205,339.73892 L 65.825288,515.14967 C 63.418773,522.20878 59.752966,521.8319 51.570815,522.15277 L 50.608209,562.10092 L 117.99063,561.86026 C 131.06603,561.13831 139.21491,553.72589 144.58946,535.43638 L 189.76977,339.73892 L 106.32205,339.73892 z M 119.98951,280.53864 L 109.65558,325.29983 L 193.1033,325.29983 L 203.43723,280.53864 L 119.98951,280.53864 z M 239.29385,339.75011 C 224.85477,339.75012 208.26802,352.04925 204.6376,371.50011 L 188.6376,436.96886 C 179.27284,477.53211 184.20976,501.46886 214.48135,501.46886 L 385.3251,501.46886 L 393.3251,466.81261 L 265.29385,466.81261 L 272.16885,436.96886 L 400.2001,436.96886 C 402.2537,428.07372 410.80305,415.14706 415.3251,371.50011 C 416.6053,365.955 407.76262,339.75011 374.5126,339.75011 L 239.29385,339.75011 z M 307.98135,371.50011 C 325.75108,371.50011 330.91518,377.05789 325.2001,401.81261 L 280.29385,401.81261 C 285.80848,377.9261 293.45487,371.5001 307.98135,371.50011 z M 799.21238,339.73892 L 769.87735,466.80292 C 769.87735,466.80292 761.87689,501.45673 810.59962,501.45673 L 893.34691,501.45673 C 889.21672,519.34655 887.23544,527.9284 839.14197,527.9284 L 755.76542,527.9284 L 748.32055,560.1757 L 879.79056,560.1757 C 940.41876,560.1757 962.20636,560.1757 969.65123,527.9284 L 1013.0982,339.73892 L 930.6824,339.73892 L 901.34737,466.80292 L 853.2539,466.80292 L 882.58893,339.73892 L 799.21238,339.73892 z M 1045.4591,339.73892 L 1031.1249,401.82701 L 1114.8716,401.82701 L 1129.2058,339.73892 L 1045.4591,339.73892 z M 1022.4578,439.36864 L 1008.1236,501.45673 L 1091.8702,501.45673 L 1106.2044,439.36864 L 1022.4578,439.36864 z M 1217.5384,280.53864 L 1102.1787,527.9284 L 1144.3505,527.9284 L 1259.7102,280.53864 L 1217.5384,280.53864 z M 1310.9112,280.53864 L 1195.5515,527.9284 L 1237.7233,527.9284 L 1353.083,280.53864 L 1310.9112,280.53864 z" />
+    <path
+       d="M 468.5751,304.59386 L 460.4501,339.75011 L 430.85635,339.75011 L 423.54385,371.50011 L 453.1376,371.50011 L 435.6376,447.21886 C 431.11514,466.80783 434.69559,501.46886 475.60635,501.46886 L 574.3876,501.46886 L 582.3876,466.81261 C 513.82035,466.81262 513.81207,466.80768 518.8251,445.09386 L 535.8251,371.50011 L 598.2626,371.50011 L 604.3876,371.50011 L 627.85635,371.50011 L 610.35635,447.21886 C 605.83388,466.80782 609.41434,501.46886 650.3251,501.46886 L 749.10635,501.46886 L 757.10635,466.81261 C 688.53909,466.81261 688.53082,466.80768 693.54385,445.09386 L 710.54385,371.50011 L 779.10635,371.50011 L 786.4501,339.75011 L 717.85635,339.75011 L 725.98135,304.59386 L 643.29385,304.59386 L 635.16885,339.75011 L 611.73135,339.75011 L 605.5751,339.75011 L 543.1376,339.75011 L 551.2626,304.59386 L 468.5751,304.59386 z M 106.32205,339.73892 L 65.825288,515.14967 C 63.418773,522.20878 59.752966,521.8319 51.570815,522.15277 L 50.608209,562.10092 L 117.99063,561.86026 C 131.06603,561.13831 139.21491,553.72589 144.58946,535.43638 L 189.76977,339.73892 L 106.32205,339.73892 z M 119.98951,280.53864 L 109.65558,325.29983 L 193.1033,325.29983 L 203.43723,280.53864 L 119.98951,280.53864 z M 239.29385,339.75011 C 224.85477,339.75012 208.26802,352.04925 204.6376,371.50011 L 188.6376,436.96886 C 179.27284,477.53211 184.20976,501.46886 214.48135,501.46886 L 385.3251,501.46886 L 393.3251,466.81261 L 265.29385,466.81261 L 272.16885,436.96886 L 400.2001,436.96886 C 402.2537,428.07372 410.80305,415.14706 415.3251,371.50011 C 416.6053,365.955 407.76262,339.75011 374.5126,339.75011 L 239.29385,339.75011 z M 307.98135,371.50011 C 325.75108,371.50011 330.91518,377.05789 325.2001,401.81261 L 280.29385,401.81261 C 285.80848,377.9261 293.45487,371.5001 307.98135,371.50011 z M 799.21238,339.73892 L 769.87735,466.80292 C 769.87735,466.80292 761.87689,501.45673 810.59962,501.45673 L 893.34691,501.45673 C 889.21672,519.34655 887.23544,527.9284 839.14197,527.9284 L 755.76542,527.9284 L 748.32055,560.1757 L 879.79056,560.1757 C 940.41876,560.1757 962.20636,560.1757 969.65123,527.9284 L 1013.0982,339.73892 L 930.6824,339.73892 L 901.34737,466.80292 L 853.2539,466.80292 L 882.58893,339.73892 L 799.21238,339.73892 z M 1045.4591,339.73892 L 1031.1249,401.82701 L 1114.8716,401.82701 L 1129.2058,339.73892 L 1045.4591,339.73892 z M 1022.4578,439.36864 L 1008.1236,501.45673 L 1091.8702,501.45673 L 1106.2044,439.36864 L 1022.4578,439.36864 z M 1217.5384,280.53864 L 1102.1787,527.9284 L 1144.3505,527.9284 L 1259.7102,280.53864 L 1217.5384,280.53864 z M 1310.9112,280.53864 L 1195.5515,527.9284 L 1237.7233,527.9284 L 1353.083,280.53864 L 1310.9112,280.53864 z"
+       style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:30;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3316" />
+  </g>
+</svg>
diff --git a/jetty-documentation/src/main/docbkx-resources/images/jetty.gif b/jetty-documentation/src/main/docbkx-resources/images/jetty.gif
new file mode 100644
index 0000000..9c767e0
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/jetty.gif
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/images/note.png b/jetty-documentation/src/main/docbkx-resources/images/note.png
new file mode 100644
index 0000000..d0c3c64
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/note.png
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/images/note.svg b/jetty-documentation/src/main/docbkx-resources/images/note.svg
new file mode 100644
index 0000000..648299d
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/note.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In  -->
+<!DOCTYPE svg [
+	<!ENTITY st0 "fill:none;stroke:#FFFFFF;stroke-width:12.1438;stroke-linejoin:round;">
+	<!ENTITY st1 "fill:none;stroke-width:1.2429;">
+	<!ENTITY st2 "fill:#FFFFFF;stroke:none;">
+	<!ENTITY st3 "fill:none;stroke:#FFFFFF;stroke-width:12.7649;stroke-linejoin:round;">
+	<!ENTITY st4 "fill:#FFFFFF;stroke-width:6.3824;stroke-linejoin:round;">
+	<!ENTITY st5 "fill:none;stroke:none;">
+	<!ENTITY st6 "fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
+	<!ENTITY st7 "fill:#FFFFFF;stroke:#FFFFFF;stroke-width:12.7649;stroke-linejoin:round;">
+	<!ENTITY st8 "stroke:none;">
+	<!ENTITY st9 "fill:none;stroke-width:4.9715;stroke-linejoin:round;">
+]>
+<svg  xmlns="http://www.w3.org/2000/svg" width="48pt" height="48pt" viewBox="0 0 48 48" xml:space="preserve">
+	<g id="Layer_x0020_1" style="&st6;">
+		<path style="&st0;" d="M35.7,19.8v18.9H11V8.8h13.9l10.8,11z"/>
+		<path style="&st3;" d="M38.7,30.4L25,16.7l-7.7-3l2.7,8.7l13.3,13.4l5.4-5.4z"/>
+		<path style="&st7;" d="M35.7,8.8H11v29.9h24.7V8.8z"/>
+		<path style="&st4;" d="M35.7,8.8H11v29.9h24.7V8.8z"/>
+		<path style="&st2;" d="M35.7,8.8H11v29.9h24.7V8.8z"/>
+	</g>
+	<g id="Layer_x0020_4" style="&st6;">
+		<path style="&st9;" d="M38.7,30.4L25,16.7l-7.7-3l2.7,8.7l13.3,13.4l5.4-5.4z"/>
+		<path style="&st8;" d="M38.7,30.4L25,16.7l-7.7-3l2.7,8.7l13.3,13.4l5.4-5.4z"/>
+		<path style="&st8;" d="M20.6,14.7l-2.5,2.5L17,13.4l3.6,1.3z"/>
+		<path style="&st1;" d="M19.6,22.2l3-0.3l2.4-2.4l0.4-2.8"/>
+		<path style="&st2;" d="M20.4,14.9L18.3,17l1.6,5.2l2.7-0.3l2.4-2.4l0.3-2.4l-5-2.2z"/>
+	</g>
+	<g id="crop" style="&st6;">
+		<path style="&st5;" d="M48,48H0V0h48v48z"/>
+	</g>
+</svg>
diff --git a/jetty-documentation/src/main/docbkx-resources/images/tip.png b/jetty-documentation/src/main/docbkx-resources/images/tip.png
new file mode 100644
index 0000000..5c4aab3
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/tip.png
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/images/tip.svg b/jetty-documentation/src/main/docbkx-resources/images/tip.svg
new file mode 100644
index 0000000..4a64a15
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/tip.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In  -->
+<!DOCTYPE svg [
+	<!ENTITY st0 "fill:none;stroke:#000000;stroke-width:1.0944;">
+	<!ENTITY st1 "fill:#FFFFFF;stroke:none;">
+	<!ENTITY st2 "fill-rule:nonzero;clip-rule:nonzero;stroke:#FFFFFF;stroke-width:5.6139;stroke-miterlimit:4;">
+	<!ENTITY st3 "fill:none;stroke:none;">
+	<!ENTITY st4 "fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
+	<!ENTITY st5 "stroke:none;">
+]>
+<svg  width="48pt" height="48pt" viewBox="0 0 48 48" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
+	<g id="Layer_x0020_3" style="&st2;">
+		<g>
+			<path d="M9.5,18.6c0,8,6.5,14.4,14.4,14.4c8,0,14.4-6.5,14.4-14.4c0-8-6.5-14.4-14.4-14.4c-8,0-14.4,6.5-14.4,14.4z M12.8,18.6c0-6.2,5-11.2,11.2-11.2c6.2,0,11.2,5,11.2,11.2c0,6.2-5,11.2-11.2,11.2c-6.2,0-11.2-5-11.2-11.2z"/>
+			<path d="M28.1,37.9l-7.6,0.8c-0.9,0.1-1.5,0.9-1.4,1.8c0.1,0.9,0.9,1.5,1.8,1.4l7.6-0.8c0.9-0.1,1.5-0.9,1.4-1.8c-0.1-0.9-0.9-1.5-1.8-1.4z"/>
+			<path d="M28.1,34.8l-7.6,0.8c-0.9,0.1-1.5,0.9-1.4,1.8c0.1,0.9,0.9,1.5,1.8,1.4l7.6-0.8c0.9-0.1,1.5-0.9,1.4-1.8c-0.1-0.9-0.9-1.5-1.8-1.4z"/>
+			<path d="M28.1,31.6l-7.6,0.8c-0.9,0.1-1.5,0.9-1.4,1.8s0.9,1.5,1.8,1.4l7.6-0.8c0.9-0.1,1.5-0.9,1.4-1.8s-0.9-1.5-1.8-1.4z"/>
+			<path d="M23.1,41.3v0.9c0,0.9,0.7,1.6,1.6,1.6c0.9,0,1.6-0.7,1.6-1.6v-0.9h-3.3z"/>
+			<path style="&st1;" d="M35.9,18.7c0,6.6-5.4,12-12,12c-6.6,0-12-5.4-12-12s5.4-12,12-12c6.6,0,12,5.4,12,12z"/>
+			<path style="&st5;" d="M9.6,18.6c0,8,6.5,14.4,14.4,14.4c8,0,14.4-6.5,14.4-14.4c0-8-6.5-14.4-14.4-14.4c-8,0-14.4,6.5-14.4,14.4z M12.9,18.6c0-6.2,5-11.2,11.2-11.2c6.2,0,11.2,5,11.2,11.2c0,6.2-5,11.2-11.2,11.2c-6.2,0-11.2-5-11.2-11.2z"/>
+			<path style="&st5;" d="M28.2,37.9l-7.6,0.8c-0.9,0.1-1.5,0.9-1.4,1.8c0.1,0.9,0.9,1.5,1.8,1.4l7.6-0.8c0.9-0.1,1.5-0.9,1.4-1.8c-0.1-0.9-0.9-1.5-1.8-1.4z"/>
+			<path style="&st5;" d="M28.2,34.7l-7.6,0.8c-0.9,0.1-1.5,0.9-1.4,1.8c0.1,0.9,0.9,1.5,1.8,1.4l7.6-0.8c0.9-0.1,1.5-0.9,1.4-1.8c-0.1-0.9-0.9-1.5-1.8-1.4z"/>
+			<path style="&st5;" d="M28.2,31.6l-7.6,0.8c-0.9,0.1-1.5,0.9-1.4,1.8c0.1,0.9,0.9,1.5,1.8,1.4l7.6-0.8c0.9-0.1,1.5-0.9,1.4-1.8c-0.1-0.9-0.9-1.5-1.8-1.4z"/>
+			<path style="&st5;" d="M23.1,41.3v0.9c0,0.9,0.7,1.6,1.6,1.6s1.6-0.7,1.6-1.6v-0.9h-3.3z"/>
+			<path style="&st0;" d="M22.3,28.3l-3.5-10.7c0,0,6.6,3.9,10.5,0"/>
+		</g>
+	</g>
+	<g id="crop_x0020_marks" style="&st4;">
+		<path style="&st3;" d="M48,48H0V0h48v48z"/>
+	</g>
+</svg>
diff --git a/jetty-documentation/src/main/docbkx-resources/images/warning.png b/jetty-documentation/src/main/docbkx-resources/images/warning.png
new file mode 100644
index 0000000..1c33db8
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/warning.png
Binary files differ
diff --git a/jetty-documentation/src/main/docbkx-resources/images/warning.svg b/jetty-documentation/src/main/docbkx-resources/images/warning.svg
new file mode 100644
index 0000000..fc8d748
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/images/warning.svg
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In  -->
+<!DOCTYPE svg [
+	<!ENTITY st0 "fill:#000000;stroke:#FFFFFF;stroke-width:7.9139;stroke-linejoin:round;">
+	<!ENTITY st1 "fill-rule:nonzero;clip-rule:nonzero;fill:#FFFFFF;stroke:#000000;stroke-miterlimit:4;">
+	<!ENTITY st2 "fill:none;stroke:none;">
+	<!ENTITY st3 "fill:#000000;">
+	<!ENTITY st4 "fill-rule:evenodd;clip-rule:evenodd;stroke:none;">
+	<!ENTITY st5 "fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
+]>
+<svg  width="48pt" height="48pt" viewBox="0 0 48 48" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
+	<g id="Layer_x0020_4" style="&st1;">
+		<g style="&st4;">
+			<path style="&st0;" d="M16.4,42.3L5.7,31.6V16.4L16.4,5.7h15.2l10.7,10.7v15.2L31.6,42.3H16.4z"/>
+			<path style="&st3;" d="M16.4,42.3L5.7,31.6V16.4L16.4,5.7h15.2l10.7,10.7v15.2L31.6,42.3H16.4z"/>
+			<path d="M11.7,17.7l18.7,18.7l5.9-5.9L17.6,11.7l-5.9,5.9z"/>
+			<path d="M11.7,30.5l5.9,5.9l18.7-18.7l-5.9-5.9L11.7,30.5z"/>
+		</g>
+	</g>
+	<g id="crop_x0020_marks" style="&st5;">
+		<path style="&st2;" d="M48,48H0V0h48v48z"/>
+	</g>
+</svg>
diff --git a/jetty-documentation/src/main/docbkx-resources/js/highlight.pack.js b/jetty-documentation/src/main/docbkx-resources/js/highlight.pack.js
new file mode 100644
index 0000000..a4ea792
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-resources/js/highlight.pack.js
@@ -0,0 +1 @@
+!function(e){"undefined"!=typeof exports?e(exports):(window.hljs=e({}),"function"==typeof define&&define.amd&&define("hljs",[],function(){return window.hljs}))}(function(e){function n(e){return e.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0==t.index}function a(e){return/^(no-?highlight|plain|text)$/i.test(e)}function i(e){var n,t,r,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",t=/\blang(?:uage)?-([\w-]+)\b/i.exec(i))return w(t[1])?t[1]:"no-highlight";for(i=i.split(/\s+/),n=0,r=i.length;r>n;n++)if(w(i[n])||a(i[n]))return i[n]}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3==i.nodeType?a+=i.nodeValue.length:1==i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!=r[0].offset?e[0].offset<r[0].offset?e:r:"start"==r[0].event?e:r:e.length?e:r}function o(e){function r(e){return" "+e.nodeName+'="'+n(e.value)+'"'}l+="<"+t(e)+Array.prototype.map.call(e.attributes,r).join("")+">"}function u(e){l+="</"+t(e)+">"}function c(e){("start"==e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g==e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g==e&&g.length&&g[0].offset==s);f.reverse().forEach(o)}else"start"==g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):Object.keys(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\b\w+\b/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),void 0===a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"==e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,a,i){function o(e,n){for(var t=0;t<n.c.length;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function h(e,n,t,r){var a=r?"":E.classPrefix,i='<span class="'+a,o=t?"":"</span>";return i+=e+'">',i+n+o}function p(){if(!L.k)return n(M);var e="",t=0;L.lR.lastIndex=0;for(var r=L.lR.exec(M);r;){e+=n(M.substr(t,r.index-t));var a=g(L,r);a?(B+=a[1],e+=h(a[0],n(r[0]))):e+=n(r[0]),t=L.lR.lastIndex,r=L.lR.exec(M)}return e+n(M.substr(t))}function d(){var e="string"==typeof L.sL;if(e&&!x[L.sL])return n(M);var t=e?l(L.sL,M,!0,y[L.sL]):f(M,L.sL.length?L.sL:void 0);return L.r>0&&(B+=t.r),e&&(y[L.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){return void 0!==L.sL?d():p()}function v(e,t){var r=e.cN?h(e.cN,"",!0):"";e.rB?(k+=r,M=""):e.eB?(k+=n(t)+r,M=""):(k+=r,M=t),L=Object.create(e,{parent:{value:L}})}function m(e,t){if(M+=e,void 0===t)return k+=b(),0;var r=o(t,L);if(r)return k+=b(),v(r,t),r.rB?0:t.length;var a=u(L,t);if(a){var i=L;i.rE||i.eE||(M+=t),k+=b();do L.cN&&(k+="</span>"),B+=L.r,L=L.parent;while(L!=a.parent);return i.eE&&(k+=n(t)),M="",a.starts&&v(a.starts,""),i.rE?0:t.length}if(c(t,L))throw new Error('Illegal lexeme "'+t+'" for mode "'+(L.cN||"<unnamed>")+'"');return M+=t,t.length||1}var N=w(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,L=i||N,y={},k="";for(R=L;R!=N;R=R.parent)R.cN&&(k=h(R.cN,"",!0)+k);var M="",B=0;try{for(var C,j,I=0;;){if(L.t.lastIndex=I,C=L.t.exec(t),!C)break;j=m(t.substr(I,C.index-I),C[0]),I=C.index+j}for(m(t.substr(I)),R=L;R.parent;R=R.parent)R.cN&&(k+="</span>");return{r:B,value:k,language:e,top:L}}catch(O){if(-1!=O.message.indexOf("Illegal"))return{r:0,value:n(t)};throw O}}function f(e,t){t=t||E.languages||Object.keys(x);var r={r:0,value:n(e)},a=r;return t.forEach(function(n){if(w(n)){var t=l(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}}),a.language&&(r.second_best=a),r}function g(e){return E.tabReplace&&(e=e.replace(/^((<[^>]+>|\t)+)/gm,function(e,n){return n.replace(/\t/g,E.tabReplace)})),E.useBR&&(e=e.replace(/\n/g,"<br>")),e}function h(e,n,t){var r=n?R[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n=i(e);if(!a(n)){var t;E.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n")):t=e;var r=t.textContent,o=n?l(n,r,!0):f(r),s=u(t);if(s.length){var p=document.createElementNS("http://www.w3.org/1999/xhtml","div");p.innerHTML=o.value,o.value=c(s,u(p),r)}o.value=g(o.value),e.innerHTML=o.value,e.className=h(e.className,n,o.language),e.result={language:o.language,re:o.r},o.second_best&&(e.second_best={language:o.second_best.language,re:o.second_best.r})}}function d(e){E=o(E,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");Array.prototype.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){R[e]=n})}function N(){return Object.keys(x)}function w(e){return e=(e||"").toLowerCase(),x[e]||x[R[e]]}var E={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},x={},R={};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=w,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e});hljs.registerLanguage("nginx",function(e){var r={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},b={eW:!0,l:"[a-z/_]+",k:{built_in:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,r],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{cN:"url",b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[r]},{cN:"regexp",c:[e.BE,r],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},r]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"title",b:e.UIR,starts:b}],r:0}],i:"[^\\s\\}]"}});hljs.registerLanguage("cs",function(e){var r="abstract as base bool break byte case catch char checked const continue decimal dynamic default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long null when object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while async protected public private internal ascending descending from get group into join let orderby partial select set value var where yield",t=e.IR+"(<"+e.IR+">)?";return{aliases:["csharp"],k:r,i:/::/,c:[e.C("///","$",{rB:!0,c:[{cN:"xmlDocTag",v:[{b:"///",r:0},{b:"<!--|-->"},{b:"</?",e:">"}]}]}),e.CLCM,e.CBCM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line region endregion pragma checksum"},{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},e.ASM,e.QSM,e.CNM,{bK:"class interface",e:/[{;=]/,i:/[^\s:]/,c:[e.TM,e.CLCM,e.CBCM]},{bK:"namespace",e:/[{;=]/,i:/[^\s:]/,c:[{cN:"title",b:"[a-zA-Z](\\.?\\w)*",r:0},e.CLCM,e.CBCM]},{bK:"new return throw await",r:0},{cN:"function",b:"("+t+"\\s+)+"+e.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:r,c:[{b:e.IR+"\\s*\\(",rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,k:r,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]}]}});hljs.registerLanguage("diff",function(e){return{aliases:["patch"],c:[{cN:"chunk",r:10,v:[{b:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"header",v:[{b:/Index: /,e:/$/},{b:/=====/,e:/=====$/},{b:/^\-\-\-/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+\+\+/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"change",b:"^\\!",e:"$"}]}});hljs.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)}/}]},s={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/-?[a-z\.]+/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",operator:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"shebang",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,e.NM,s,a,t]}});hljs.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"header",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"blockquote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"`.+?`"},{b:"^( {4}|	)",e:"$",r:0}]},{cN:"horizontal_rule",b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"link_label",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link_url",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"link_reference",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:"^\\[.+\\]:",rB:!0,c:[{cN:"link_reference",b:"\\[",e:"\\]:",eB:!0,eE:!0,starts:{cN:"link_url",e:"$"}}]}]}});hljs.registerLanguage("javascript",function(e){return{aliases:["js"],k:{keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},c:[{cN:"pi",r:10,b:/^\s*['"]use (strict|asm)['"]/},e.ASM,e.QSM,{cN:"string",b:"`",e:"`",c:[e.BE,{cN:"subst",b:"\\$\\{",e:"\\}"}]},e.CLCM,e.CBCM,{cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{b:/</,e:/>\s*[);\]]/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:[e.CLCM,e.CBCM]}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+e.IR,r:0},{bK:"import",e:"[;$]",k:"import from as",c:[e.ASM,e.QSM]},{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]}],i:/#/}});hljs.registerLanguage("ini",function(e){var c={cN:"string",c:[e.BE],v:[{b:"'''",e:"'''",r:10},{b:'"""',e:'"""',r:10},{b:'"',e:'"'},{b:"'",e:"'"}]};return{aliases:["toml"],cI:!0,i:/\S/,c:[e.C(";","$"),e.HCM,{cN:"title",b:/^\s*\[+/,e:/\]+/},{cN:"setting",b:/^[a-z0-9\[\]_-]+\s*=\s*/,e:"$",c:[{cN:"value",eW:!0,k:"on off true false yes no",c:[{cN:"variable",v:[{b:/\$[\w\d"][\w\d_]*/},{b:/\$\{(.*?)}/}]},c,{cN:"number",b:/([\+\-]+)?[\d]+_[\d_]+/},e.NM],r:0}]}]}});hljs.registerLanguage("apache",function(e){var r={cN:"number",b:"[\\$%]\\d+"};return{aliases:["apacheconf"],cI:!0,c:[e.HCM,{cN:"tag",b:"</?",e:">"},{cN:"keyword",b:/\w+/,r:0,k:{common:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{e:/$/,r:0,k:{literal:"on off all"},c:[{cN:"sqbracket",b:"\\s\\[",e:"\\]$"},{cN:"cbracket",b:"[\\$%]\\{",e:"\\}",c:["self",r]},r,e.QSM]}}],i:/\S/}});hljs.registerLanguage("sql",function(e){var t=e.C("--","$");return{cI:!0,i:/[<>{}*]/,c:[{cN:"operator",bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke",e:/;/,eW:!0,k:{keyword:"abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias allocate allow alter always analyze ancillary and any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound buffer_cache buffer_pool build bulk by byte byteordermark bytes c cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle d data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration e each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain export export_set extended extent external external_1 external_2 externally extract f failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function g general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour http i id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists k keep keep_duplicates key keys kill l language large last last_day last_insert_id last_value lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim m main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex n name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding p package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second section securefile security seed segment select self sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime t table tables tablespace tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text varchar varying void"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]}});hljs.registerLanguage("makefile",function(e){var a={cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]};return{aliases:["mk","mak"],c:[e.HCM,{b:/^\w+\s*\W*=/,rB:!0,r:0,starts:{cN:"constant",e:/\s*\W*=/,eE:!0,starts:{e:/$/,r:0,c:[a]}}},{cN:"title",b:/^[\w]+:\s*$/},{cN:"phony",b:/^\.PHONY:/,e:/$/,k:".PHONY",l:/[\.\w]+/},{b:/^\t+/,e:/$/,r:0,c:[e.QSM,a]}]}});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",a={cN:"function",b:c+"\\(",rB:!0,eE:!0,e:"\\("},r={cN:"rule",b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{cN:"value",eW:!0,eE:!0,c:[a,e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"hexcolor",b:"#[0-9A-Fa-f]+"},{cN:"important",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,{cN:"id",b:/\#[A-Za-z0-9_-]+/},{cN:"class",b:/\.[A-Za-z0-9_-]+/},{cN:"attr_selector",b:/\[/,e:/\]/,i:"$"},{cN:"pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"']+/},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{cN:"at_rule",b:"@",e:"[{;]",c:[{cN:"keyword",b:/\S+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[a,e.ASM,e.QSM,e.CSSNM]}]},{cN:"tag",b:c,r:0},{cN:"rules",b:"{",e:"}",i:/\S/,c:[e.CBCM,r]}]}});hljs.registerLanguage("xml",function(t){var s="[A-Za-z0-9\\._:-]+",c={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php"},e={eW:!0,i:/</,r:0,c:[c,{cN:"attribute",b:s,r:0},{b:"=",r:0,c:[{cN:"value",c:[c],v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s\/>]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xsl","plist"],cI:!0,c:[{cN:"doctype",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},t.C("<!--","-->",{r:10}),{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{title:"style"},c:[e],starts:{e:"</style>",rE:!0,sL:"css"}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{title:"script"},c:[e],starts:{e:"</script>",rE:!0,sL:["actionscript","javascript","handlebars"]}},c,{cN:"pi",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"title",b:/[^ \/><\n\t]+/,r:0},e]}]}});hljs.registerLanguage("ruby",function(e){var c="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",r="and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",b={cN:"doctag",b:"@[A-Za-z]+"},a={cN:"value",b:"#<",e:">"},n=[e.C("#","$",{c:[b]}),e.C("^\\=begin","^\\=end",{c:[b],r:10}),e.C("^__END__","\\n$")],s={cN:"subst",b:"#\\{",e:"}",k:r},t={cN:"string",c:[e.BE,s],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/}]},i={cN:"params",b:"\\(",e:"\\)",k:r},d=[t,a,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+e.IR+"::)?"+e.IR}]}].concat(n)},{cN:"function",bK:"def",e:"$|;",c:[e.inherit(e.TM,{b:c}),i].concat(n)},{cN:"constant",b:"(::)?(\\b[A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":",c:[t,{b:c}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"("+e.RSR+")\\s*",c:[a,{cN:"regexp",c:[e.BE,s],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}].concat(n),r:0}].concat(n);s.c=d,i.c=d;var o="[>?]>",l="[\\w#]+\\(\\w+\\):\\d+:\\d+>",u="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",N=[{b:/^\s*=>/,cN:"status",starts:{e:"$",c:d}},{cN:"prompt",b:"^("+o+"|"+l+"|"+u+")",starts:{e:"$",c:d}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,i:/\/\*/,c:n.concat(N).concat(d)}});hljs.registerLanguage("objectivec",function(e){var t={cN:"built_in",b:"(AV|CA|CF|CG|CI|MK|MP|NS|UI)\\w+"},i={keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},o=/[a-zA-Z@][a-zA-Z0-9_]*/,n="@interface @class @protocol @implementation";return{aliases:["mm","objc","obj-c"],k:i,l:o,i:"</",c:[t,e.CLCM,e.CBCM,e.CNM,e.QSM,{cN:"string",v:[{b:'@"',e:'"',i:"\\n",c:[e.BE]},{b:"'",e:"[^\\\\]'",i:"[^\\\\][^']"}]},{cN:"preprocessor",b:"#",e:"$",c:[{cN:"title",v:[{b:'"',e:'"'},{b:"<",e:">"}]}]},{cN:"class",b:"("+n.split(" ").join("|")+")\\b",e:"({|$)",eE:!0,k:n,l:o,c:[e.UTM]},{cN:"variable",b:"\\."+e.UIR,r:0}]}});hljs.registerLanguage("python",function(e){var r={cN:"prompt",b:/^(>>>|\.\.\.) /},b={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[r],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[r],r:10},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},e.ASM,e.QSM]},a={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},l={cN:"params",b:/\(/,e:/\)/,c:["self",r,a,b]};return{aliases:["py","gyp"],k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},i:/(<\/|->|\?)/,c:[r,a,b,e.HCM,{v:[{cN:"function",bK:"def",r:10},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n,]/,c:[e.UTM,l]},{cN:"decorator",b:/^[\t ]*@/,e:/$/},{b:/\b(print|exec)\(/}]}});hljs.registerLanguage("golo",function(e){return{k:{keyword:"println readln print import module function local return let var while for foreach times in case when match with break continue augment augmentation each find filter reduce if then else otherwise try catch finally raise throw orIfNull",typename:"DynamicObject|10 DynamicVariable struct Observable map set vector list array",literal:"true false null"},c:[e.HCM,e.QSM,e.CNM,{cN:"annotation",b:"@[A-Za-z]+"}]}});hljs.registerLanguage("coffeescript",function(e){var c={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger super then unless until loop of by when and or is isnt not",literal:"true false null undefined yes no on off",built_in:"npm require console print module global window document"},n="[A-Za-z$_][0-9A-Za-z$_]*",r={cN:"subst",b:/#\{/,e:/}/,k:c},t=[e.BNM,e.inherit(e.CNM,{starts:{e:"(\\s*/)?",r:0}}),{cN:"string",v:[{b:/'''/,e:/'''/,c:[e.BE]},{b:/'/,e:/'/,c:[e.BE]},{b:/"""/,e:/"""/,c:[e.BE,r]},{b:/"/,e:/"/,c:[e.BE,r]}]},{cN:"regexp",v:[{b:"///",e:"///",c:[r,e.HCM]},{b:"//[gim]*",r:0},{b:/\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/}]},{cN:"property",b:"@"+n},{b:"`",e:"`",eB:!0,eE:!0,sL:"javascript"}];r.c=t;var s=e.inherit(e.TM,{b:n}),i="(\\(.*\\))?\\s*\\B[-=]>",o={cN:"params",b:"\\([^\\(]",rB:!0,c:[{b:/\(/,e:/\)/,k:c,c:["self"].concat(t)}]};return{aliases:["coffee","cson","iced"],k:c,i:/\/\*/,c:t.concat([e.C("###","###"),e.HCM,{cN:"function",b:"^\\s*"+n+"\\s*=\\s*"+i,e:"[-=]>",rB:!0,c:[s,o]},{b:/[:\(,=]\s*/,r:0,c:[{cN:"function",b:i,e:"[-=]>",rB:!0,c:[o]}]},{cN:"class",bK:"class",e:"$",i:/[:="\[\]]/,c:[{bK:"extends",eW:!0,i:/[:="\[\]]/,c:[s]},s]},{cN:"attribute",b:n+":",e:":",rB:!0,rE:!0,r:0}])}});hljs.registerLanguage("perl",function(e){var t="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",r={cN:"subst",b:"[$@]\\{",e:"\\}",k:t},s={b:"->{",e:"}"},n={cN:"variable",v:[{b:/\$\d/},{b:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{b:/[\$%@][^\s\w{]/,r:0}]},o=[e.BE,r,n],i=[n,e.HCM,e.C("^\\=\\w","\\=cut",{eW:!0}),s,{cN:"string",c:o,v:[{b:"q[qwxr]?\\s*\\(",e:"\\)",r:5},{b:"q[qwxr]?\\s*\\[",e:"\\]",r:5},{b:"q[qwxr]?\\s*\\{",e:"\\}",r:5},{b:"q[qwxr]?\\s*\\|",e:"\\|",r:5},{b:"q[qwxr]?\\s*\\<",e:"\\>",r:5},{b:"qw\\s+q",e:"q",r:5},{b:"'",e:"'",c:[e.BE]},{b:'"',e:'"'},{b:"`",e:"`",c:[e.BE]},{b:"{\\w+}",c:[],r:0},{b:"-?\\w+\\s*\\=\\>",c:[],r:0}]},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\/\\/|"+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"sub",bK:"sub",e:"(\\s*\\(.*?\\))?[;{]",r:5},{cN:"operator",b:"-\\w\\b",r:0},{b:"^__DATA__$",e:"^__END__$",sL:"mojolicious",c:[{b:"^@@.*",e:"$",cN:"comment"}]}];return r.c=i,s.c=i,{aliases:["pl"],k:t,c:i}});hljs.registerLanguage("cpp",function(t){var e={cN:"keyword",b:"\\b[a-z\\d_]*_t\\b"},r={cN:"string",v:[t.inherit(t.QSM,{b:'((u8?|U)|L)?"'}),{b:'(u8?|U)?R"',e:'"',c:[t.BE]},{b:"'\\\\?.",e:"'",i:"."}]},s={cN:"number",v:[{b:"\\b(\\d+(\\.\\d*)?|\\.\\d+)(u|U|l|L|ul|UL|f|F)"},{b:t.CNR}]},i={cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line pragma ifdef ifndef",c:[{b:/\\\n/,r:0},{bK:"include",e:"$",c:[r,{cN:"string",b:"<",e:">",i:"\\n"}]},r,s,t.CLCM,t.CBCM]},a=t.IR+"\\s*\\(",c={keyword:"int float while private char catch export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using class asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignof constexpr decltype noexcept static_assert thread_local restrict _Bool complex _Complex _Imaginary atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong",built_in:"std string cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf",literal:"true false nullptr NULL"};return{aliases:["c","cc","h","c++","h++","hpp"],k:c,i:"</",c:[e,t.CLCM,t.CBCM,s,r,i,{b:"\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",e:">",k:c,c:["self",e]},{b:t.IR+"::",k:c},{bK:"new throw return else",r:0},{cN:"function",b:"("+t.IR+"[\\*&\\s]+)+"+a,rB:!0,e:/[{;=]/,eE:!0,k:c,i:/[^\w\s\*&]/,c:[{b:a,rB:!0,c:[t.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:c,r:0,c:[t.CLCM,t.CBCM,r,s]},t.CLCM,t.CBCM,i]}]}});hljs.registerLanguage("http",function(t){return{aliases:["https"],i:"\\S",c:[{cN:"status",b:"^HTTP/[0-9\\.]+",e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{cN:"request",b:"^[A-Z]+ (.*?) HTTP/[0-9\\.]+$",rB:!0,e:"$",c:[{cN:"string",b:" ",e:" ",eB:!0,eE:!0}]},{cN:"attribute",b:"^\\w",e:": ",eE:!0,i:"\\n|\\s|=",starts:{cN:"string",e:"$"}},{b:"\\n\\n",starts:{sL:[],eW:!0}}]}});hljs.registerLanguage("json",function(e){var t={literal:"true false null"},i=[e.QSM,e.CNM],l={cN:"value",e:",",eW:!0,eE:!0,c:i,k:t},c={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:!0,eE:!0,c:[e.BE],i:"\\n",starts:l}],i:"\\S"},n={b:"\\[",e:"\\]",c:[e.inherit(l,{cN:null})],i:"\\S"};return i.splice(i.length,0,c,n),{c:i,k:t,i:"\\S"}});hljs.registerLanguage("java",function(e){var a=e.UIR+"(<"+e.UIR+">)?",t="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private",c="\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",r={cN:"number",b:c,r:0};return{aliases:["jsp"],k:t,i:/<\/|#/,c:[e.C("/\\*\\*","\\*/",{r:0,c:[{cN:"doctag",b:"@[A-Za-z]+"}]}),e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"new throw return else",r:0},{cN:"function",b:"("+a+"\\s+)+"+e.UIR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:t,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,k:t,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},r,{cN:"annotation",b:"@[A-Za-z]+"}]}});hljs.registerLanguage("php",function(e){var c={cN:"variable",b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},a={cN:"preprocessor",b:/<\?(php)?|\?>/},i={cN:"string",c:[e.BE,a],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},t={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.CLCM,e.HCM,e.C("/\\*","\\*/",{c:[{cN:"doctag",b:"@[A-Za-z]+"},a]}),e.C("__halt_compiler.+?;",!1,{eW:!0,k:"__halt_compiler",l:e.UIR}),{cN:"string",b:/<<<['"]?\w+['"]?$/,e:/^\w+;?$/,c:[e.BE,{cN:"subst",v:[{b:/\$\w+/},{b:/\{\$/,e:/\}/}]}]},a,c,{b:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",c,e.CBCM,i,t]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},i,t]}});
\ No newline at end of file
diff --git a/jetty-documentation/src/main/docbkx-stylesheet/fo/docbook.xsl b/jetty-documentation/src/main/docbkx-stylesheet/fo/docbook.xsl
new file mode 100644
index 0000000..b36ab89
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-stylesheet/fo/docbook.xsl
@@ -0,0 +1,288 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+  xmlns:jfetch="java:org.eclipse.jetty.xslt.tools.JavaSourceFetchExtension"
+  xmlns:fetch="java:org.eclipse.jetty.xslt.tools.SourceFetchExtension"
+  xmlns:d="http://docbook.org/ns/docbook"
+  xmlns:fo="http://www.w3.org/1999/XSL/Format"
+  xmlns:sverb="http://nwalsh.com/xslt/ext/com.nwalsh.saxon.Verbatim"
+  xmlns:xverb="com.nwalsh.xalan.Verbatim"
+  xmlns:lxslt="http://xml.apache.org/xslt"
+  xmlns:exsl="http://exslt.org/common"
+  xmlns:xl="http://www.w3.org/1999/xlink"
+  exclude-result-prefixes="sverb xverb lxslt exsl d"
+>
+
+  <!-- imports the original docbook stylesheet -->
+  <xsl:import href="urn:docbkx:stylesheet"/>
+
+  <!-- set bellow all your custom xsl configuration -->
+  <xsl:import href="urn:docbkx:stylesheet/highlight.xsl"/>
+  <xsl:param name="highlight.source" select="1"/>
+
+  <xsl:param name="img.src.path">target/docbkx/pdf/jetty/</xsl:param>
+  <xsl:param name="keep.relative.image.uris">false</xsl:param>
+  
+  <xsl:param name="admon.graphics.extension">.svg</xsl:param>
+
+  <xsl:attribute-set name="table.table.properties">
+    <xsl:attribute name="hyphenate">true</xsl:attribute>
+  </xsl:attribute-set>
+
+  <xsl:param name="hyphenate.verbatim" select="0"></xsl:param>
+  <xsl:param name="hyphenate.verbatim.characters">.,: </xsl:param>
+
+  <xsl:attribute-set name="monospace.verbatim.properties" use-attribute-sets="verbatim.properties monospace.properties">
+    <xsl:attribute name="wrap-option">wrap</xsl:attribute>
+    <xsl:attribute name="hyphenation-character">\</xsl:attribute>
+    <xsl:attribute name="font-size">
+      <xsl:choose>
+        <xsl:when test="processing-instruction('db-font-size')">
+          <xsl:value-of
+           select="processing-instruction('db-font-size')"/>
+        </xsl:when>
+        <xsl:otherwise>75%</xsl:otherwise>
+      </xsl:choose>
+    </xsl:attribute>
+  </xsl:attribute-set>
+
+  <xsl:param name="shade.verbatim" select="1"/>
+
+  <xsl:attribute-set name="shade.verbatim.style">
+    <xsl:attribute name="background-color">#E0E0E0</xsl:attribute>
+    <xsl:attribute name="border-width">0.5pt</xsl:attribute>
+    <xsl:attribute name="border-style">solid</xsl:attribute>
+    <xsl:attribute name="border-color">#575757</xsl:attribute>
+    <xsl:attribute name="padding">3pt</xsl:attribute>
+  </xsl:attribute-set>
+
+ <xsl:template match="d:programlisting">
+        <fo:block language="en" 
+            wrap-option="wrap" 
+            linefeed-treatment="preserve"
+            font-family="monospace" 
+            white-space-collapse="false" 
+            white-space-treatment="preserve">
+            <xsl:apply-templates></xsl:apply-templates>
+        </fo:block>
+    </xsl:template>
+
+  <!--
+  this settings lets our variable lists render out with term stacking on top of the listitems
+  -->
+  <xsl:param name="variablelist.as.blocks">1</xsl:param>
+
+  <!--
+  Override the default term presentation to make terms bold
+  -->
+<xsl:template match="d:varlistentry" mode="vl.as.blocks">
+  <xsl:variable name="id"><xsl:call-template name="object.id"/></xsl:variable>
+
+  <fo:block id="{$id}" xsl:use-attribute-sets="list.item.spacing"  
+      keep-together.within-column="always" 
+      keep-with-next.within-column="always">
+      <fo:inline font-weight="bold">
+        <xsl:apply-templates select="d:term"/>
+      </fo:inline>
+  </fo:block>
+
+  <fo:block margin-left="0.25in">
+    <xsl:apply-templates select="d:listitem"/>
+  </fo:block>
+</xsl:template>
+
+<xsl:attribute-set name="formal.object.properties">
+<xsl:attribute name="keep-together.within-column">auto</xsl:attribute>
+</xsl:attribute-set>
+
+<!-- tweak the generation of toc generation -->
+
+  <xsl:param name="generate.section.toc.level" select="1"/>
+  <xsl:param name="toc.section.depth" select="1"/>
+  <xsl:param name="generate.toc">
+    appendix  toc,title
+    article/appendix  nop
+    article   toc,title
+    book      toc,title,figure,table,example,equation
+    chapter   toc,title
+    part      toc,title
+    preface   toc,title
+    qandadiv  toc
+    qandaset  toc
+    reference toc,title
+    sect1     toc
+    sect2     toc
+    sect3     toc
+    sect4     toc
+    sect5     toc
+    section   toc
+    set       toc,title
+  </xsl:param>
+
+<xsl:param name="ulink.hyphenate.chars">., </xsl:param>
+<xsl:param name="ulink.hyphenate">&#x200B;</xsl:param>
+
+<xsl:template match="entry//text()">
+ <xsl:call-template name="hyphenate-url">
+   <xsl:with-param name="url" select="."/>
+ </xsl:call-template>
+</xsl:template>
+
+<xsl:template match="programlisting//text()">
+ <xsl:call-template name="hyphenate-url">
+   <xsl:with-param name="url" select="."/>
+ </xsl:call-template>
+</xsl:template>
+
+<xsl:template match="screen//text()">
+ <xsl:call-template name="hyphenate-url">
+   <xsl:with-param name="url" select="."/>
+ </xsl:call-template>
+</xsl:template>
+
+
+<xsl:template name="book.titlepage.recto">
+  
+
+<fo:block text-align="center"><fo:external-graphic src="url(target/docbkx/pdf/jetty/images/jetty-logo-shadow.png)" width="540px" height="auto" content-width="scale-to-fit" content-height="scale-to-fit" content-type="content-type:image/png" text-align="center"/></fo:block>
+<fo:block text-align="center" font-size="24pt" border-width="10mm">The Definitive Reference</fo:block>
+
+
+  <xsl:apply-templates mode="book.titlepage.recto.auto.mode" select="d:bookinfo/d:authorgroup"/>
+  <xsl:apply-templates mode="book.titlepage.recto.auto.mode" select="d:info/d:authorgroup"/>
+  
+  <xsl:apply-templates mode="book.titlepage.mode" select="d:info/d:revhistory"/>
+
+</xsl:template>
+
+<!--
+<xsl:param name="page.height.portrait">9in</xsl:param>
+<xsl:param name="page.width.portrait">7in</xsl:param>
+<xsl:param name="page.margin.inner">0.75in</xsl:param>
+<xsl:param name="page.margin.outer">0.50in</xsl:param>
+<xsl:param name="page.margin.top">0.17in</xsl:param>
+<xsl:param name="page.margin.bottom">0.50in</xsl:param>
+-->
+<xsl:template name="book.titlepage.before.verso"/>
+<xsl:template name="book.titlepage.verso"/>
+
+  <xsl:template match="d:programlisting">
+    <xsl:variable name="id">
+      <xsl:call-template name="object.id"/>
+    </xsl:variable>
+
+    <xsl:variable name="content">
+      <xsl:choose>
+        <xsl:when test="@language='rjava'">
+          <xsl:variable name="filename" select="./d:filename"/>
+          <xsl:variable name="methodname" select="./d:methodname"/>
+          <xsl:value-of select="jfetch:fetch($filename,$methodname)"/>
+        </xsl:when>
+        <xsl:when test="@language='rxml'">
+          <xsl:variable name="filename" select="./d:filename"/>
+          <xsl:value-of select="fetch:fetch($filename)"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="apply-highlighting"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+
+    <fo:block id="{$id}"
+                xsl:use-attribute-sets="monospace.verbatim.properties shade.verbatim.style">
+      <xsl:choose>
+        <xsl:when test="$hyphenate.verbatim != 0 and function-available('exsl:node-set')">
+          <xsl:apply-templates select="exsl:node-set($content)" mode="hyphenate.verbatim"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:copy-of select="$content"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </fo:block>
+  </xsl:template>
+
+<xsl:template match="d:revhistory" mode="book.titlepage.mode">
+
+  <xsl:variable name="explicit.table.width">
+    <xsl:call-template name="pi.dbfo_table-width"/>
+  </xsl:variable>
+
+  <xsl:variable name="table.width">
+    <xsl:choose>
+      <xsl:when test="$explicit.table.width != ''">
+        <xsl:value-of select="$explicit.table.width"/>
+      </xsl:when>
+      <xsl:when test="$default.table.width = ''">
+        <xsl:text>100%</xsl:text>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$default.table.width"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <fo:table table-layout="fixed" width="{$table.width}" margin-top="40mm" xsl:use-attribute-sets="revhistory.table.properties">
+    <fo:table-column column-number="1" column-width="proportional-column-width(1)"/>
+    <fo:table-column column-number="2" column-width="proportional-column-width(1)"/>
+    <fo:table-column column-number="3" column-width="proportional-column-width(1)"/>
+    <fo:table-body start-indent="0pt" end-indent="0pt">
+      <fo:table-row>
+        <fo:table-cell number-columns-spanned="3" xsl:use-attribute-sets="revhistory.table.cell.properties">
+          <fo:block xsl:use-attribute-sets="revhistory.title.properties">
+            <xsl:call-template name="gentext">
+              <xsl:with-param name="key" select="'RevHistory'"/>
+            </xsl:call-template>
+          </fo:block>
+        </fo:table-cell>
+      </fo:table-row>
+      <xsl:apply-templates mode="titlepage.mode"/>
+    </fo:table-body>
+  </fo:table>
+</xsl:template>
+
+<xsl:template match="d:editor[1]" priority="2" mode="titlepage.mode">
+   <fo:block text-align="center"  margin-top="5mm"></fo:block>
+  <xsl:call-template name="gentext.edited.by"/>
+  <xsl:call-template name="gentext.space"/>
+
+  <xsl:call-template name="person.name.list">
+    <xsl:with-param name="person.list" select="../d:editor"/>
+  </xsl:call-template>
+</xsl:template>
+
+  <xsl:template match="d:link|d:term">
+    <xsl:choose>
+      <xsl:when test="@linkend">
+        <fo:basic-link internal-destination="{@linkend}"
+                xsl:use-attribute-sets="xref.properties"
+                color="blue">
+            <xsl:choose>
+                <xsl:when test="count(child::node())=0">
+                    <xsl:value-of select="@linkend"/>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:apply-templates/>
+                </xsl:otherwise>
+            </xsl:choose>
+        </fo:basic-link>
+      </xsl:when>
+      <xsl:when test="@xl:href">
+        <fo:basic-link external-destination="{@xl:href}"
+                xsl:use-attribute-sets="xref.properties"
+                text-decoration="underline"
+                color="blue">
+            <xsl:choose>
+                <xsl:when test="count(child::node())=0">
+                    <xsl:value-of select="@linkend"/>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:apply-templates/>
+                </xsl:otherwise>
+            </xsl:choose>
+        </fo:basic-link>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:apply-templates/>
+      </xsl:otherwise>
+    </xsl:choose>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/jetty-documentation/src/main/docbkx-stylesheet/html/docbook.xsl b/jetty-documentation/src/main/docbkx-stylesheet/html/docbook.xsl
new file mode 100644
index 0000000..eeadaa8
--- /dev/null
+++ b/jetty-documentation/src/main/docbkx-stylesheet/html/docbook.xsl
@@ -0,0 +1,451 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+xmlns:jfetch="java:org.eclipse.jetty.xslt.tools.JavaSourceFetchExtension"
+xmlns:fetch="java:org.eclipse.jetty.xslt.tools.SourceFetchExtension"
+xmlns:d="http://docbook.org/ns/docbook"
+xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0"
+xmlns:xslthl="http://xslthl.sf.net"
+xmlns:gcse="http://www.google.com"
+xmlns:date="http://exslt.org/dates-and-times"
+>
+
+  <!-- imports the original docbook stylesheet -->
+  <xsl:import href="urn:docbkx:stylesheet"/>
+
+  <!-- set bellow all your custom xsl configuration -->
+  <xsl:import href="urn:docbkx:stylesheet/highlight.xsl"/>
+  <xsl:param name="highlight.source" select="1"/>
+
+  <!-- use the xml:id on the chapter and sections when rendering chunked output" -->
+  <xsl:param name="use.id.as.filename" select="1"/>
+
+  <!--<xsl:param name="draft.mode">maybe</xsl:param>
+  <xsl:param name="draft.watermark.image">images/draft-ribbon.png</xsl:param>
+-->
+  <!-- tweak the generation of toc generation -->
+  <xsl:param name="generate.section.toc.level" select="1"/>
+  <xsl:param name="toc.section.depth" select="1"/>
+  <!--xsl:param name="chunk.tocs.and.lots" select="1"/-->
+  <xsl:param name="generate.toc">
+    appendix  toc,title
+    article/appendix  nop
+    article   toc,title
+    book      toc,title,figure,table,example,equation
+    chapter   toc,title
+    part      toc,title
+    preface   toc,title
+    qandadiv  toc
+    qandaset  toc
+    reference toc,title
+    sect1     toc
+    sect2     toc
+    sect3     toc
+    sect4     toc
+    sect5     toc
+    section   toc
+    set       toc,title
+  </xsl:param>
+
+  <!--
+    Important links:
+    - http://www.sagehill.net/docbookxsl/
+    - http://docbkx-tools.sourceforge.net/
+  -->
+
+  <!-- This addresses the issue where 'the section called "foo"' is rendered when we really only want 'foo'
+       Note: we should still be able to use xrefstyle on xrefs -->
+  <xsl:param name="local.l10n.xml" select="document('')"/>
+  <l:i18n xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0">
+    <l:l10n language="en">
+          <l:context name="xref">
+        <l:template name="section" text="%t"/>
+      </l:context>
+    </l:l10n>
+  </l:i18n>
+
+  <xsl:template match="d:authorgroup" mode="titlepage.mode"/>
+
+  <!-- squash the generation of title attributes -->
+  <xsl:template name="generate.html.title"/>
+
+  <!-- allow sections to be excluded from toc generation -->
+  <xsl:template match="d:section[@role = 'NotInToc']" mode="toc" />
+
+  <xsl:template name="user.head.content">
+    <link rel="shortcut icon" href="images/favicon.ico" />
+    <!--
+      - syntax highlighting bits and pieces
+    -->
+    <xsl:element name="link">
+      <xsl:attribute name="rel">stylesheet</xsl:attribute>
+      <xsl:attribute name="href">css/highlighter/foundation.css</xsl:attribute>
+    </xsl:element>
+    <xsl:element name="script">
+      <xsl:attribute name="src">js/highlight.pack.js</xsl:attribute>
+    </xsl:element>
+    <xsl:element name="script">
+      hljs.initHighlightingOnLoad();
+    </xsl:element>
+
+    <xsl:element name="link">
+      <xsl:attribute name="type">text/css</xsl:attribute>
+      <xsl:attribute name="rel">stylesheet</xsl:attribute>
+      <xsl:attribute name="href">css/font-awesome/font-awesome.min.css</xsl:attribute>
+    </xsl:element>
+
+    <xsl:if test="ancestor-or-self::*[@status][1]/@status = 'draft'">
+      <style type="text/css">
+        <xsl:text>
+          body { 
+            background-image: url('images/draft-ribbon.png');
+            background-repeat: no-repeat;
+            background-position: top left;
+          }
+        </xsl:text>
+      </style>
+    </xsl:if>
+    <xsl:if test="ancestor-or-self::*[@status][1]/@status = 'migrate'">
+      <style type="text/css">
+        <xsl:text>
+          body { 
+            background-image: url('images/draft-ribbon.png');
+            background-repeat: no-repeat;
+            background-position: top left;
+          }
+        </xsl:text>
+      </style>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template name="user.header.navigation">
+    <table>
+      <tr>
+        <td style="width: 25%">
+          <a href="http://www.eclipse.org/jetty"><img src="images/jetty-header-logo.png" alt="Jetty Logo"></img></a>
+          <br/>
+         
+          <span style="font-size: small">
+            Version: <xsl:value-of select="/d:book/d:info/d:revhistory/d:revision[1]/d:revnumber"/>
+          </span>
+        
+        </td>
+        <td style="width: 50%">
+          <script type="text/javascript">  (function() {
+            var cx = '016459005284625897022:obd4lsai2ds';
+            var gcse = document.createElement('script');
+            gcse.type = 'text/javascript';
+            gcse.async = true;
+            gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
+            '//www.google.com/cse/cse.js?cx=' + cx;
+            var s = document.getElementsByTagName('script')[0];
+            s.parentNode.insertBefore(gcse, s);
+            })();
+          </script>
+          <gcse:search></gcse:search>
+        </td>
+      </tr>
+    </table>
+  </xsl:template>
+
+  <xsl:template name="user.header.content">
+    <!-- Include required JS files -->
+
+    <div class="jetty-callout">
+      <h5 class="callout">
+        <a href="http://www.webtide.com/">Contact the core Jetty developers at
+          <span class="website">www.webtide.com</span>
+        </a>
+      </h5>
+      <p>
+ private support for your internal/customer projects ... custom extensions and distributions ... versioned snapshots for indefinite support ...
+ scalability guidance for your apps and Ajax/Comet projects ... development services for sponsored feature development
+      </p>
+   </div>
+
+     <xsl:if test="ancestor-or-self::*[@status][1]/@status = 'draft'">
+        <div class="draft">
+          <h5>DRAFT</h5>
+          <p>
+            This page has content that is not yet available in a released version of Jetty.  Watch for notification of a new release on our <a href="http://www.twitter.com/JettyProject">Twitter feed</a>.
+          </p>
+        </div>
+    </xsl:if>
+
+    <xsl:if test="ancestor-or-self::*[@status][1]/@status = 'migrate'">
+        <div class="draft">
+          <h5>DRAFT</h5>
+          <p>
+          This page contains content that we have migrated from Jetty 7 or Jetty 8 documentation into the correct format, but we have not yet audited it for technical accuracy in Jetty 9.  Be aware that examples or information contained on this page may be incorrect.  Please check back soon as we continue improving the documentation, or submit corrections yourself to this page through <a href="http://github.com/jetty-project/jetty-documentation" style="text-decoration:none"><i class="icon-github"></i> Github</a>. Thank you.
+          </p>
+        </div>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template name="user.footer.content">
+    <!-- content here is in a custom footer text -->
+    <xsl:apply-templates select="//copyright[1]" mode="titlepage.mode"/>
+    
+    <xsl:element name="script">
+      <xsl:attribute name="type">text/javascript</xsl:attribute>
+      SyntaxHighlighter.all()
+    </xsl:element>
+
+  </xsl:template>
+
+  <xsl:template name="user.footer.navigation">
+    
+      <p>
+            <div class="jetty-callout">
+            See an error or something missing?
+            <span class="callout">
+              <a href="http://github.com/eclipse/jetty.project">Contribute to this documentation at
+                <span class="website"><i class="fa fa-github" aria-hidden="true"></i> Github!</span>
+              </a>
+            </span>
+            <span style="float: right">
+              <i>(Generated: <xsl:value-of  select="date:date()"/>)</i>
+            </span>
+          </div>
+      </p>
+
+    <script type="text/javascript">
+  var _gaq = _gaq || [];
+  _gaq.push(['_setAccount', 'UA-1149868-7']);
+  _gaq.push(['_trackPageview']);
+
+  (function() {
+    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+  })();
+    </script>
+  </xsl:template>
+
+ <!-- 
+   - synxtax highlighting 
+   -->
+  <xsl:template match="d:programlisting">
+    <xsl:choose>
+      <xsl:when test="@language = 'screen'">
+      <xsl:element name="div">
+        <xsl:attribute name="class">screenexample</xsl:attribute>
+        <xsl:element name="pre">
+          <xsl:attribute name="class">screen</xsl:attribute>
+            <xsl:value-of select="text()"/>
+        </xsl:element>
+      </xsl:element>
+      </xsl:when>
+      <xsl:otherwise>
+        <pre>
+          <code>
+            <xsl:value-of select="text()"/>
+          </code>
+        </pre>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  
+
+  <!-- By default, DocBook surrounds highlighted elements with one or more HTML elements
+  that already have an explicit style, which makes difficult to customize them via CSS.
+  Here we override the surrounding using a span element with the right class, easily
+  customizable in the CSS. -->
+    <xsl:template match="xslthl:comment" mode="xslthl">
+        <span class="hl-comment">
+            <xsl:apply-templates />
+        </span>
+    </xsl:template>
+    <xsl:template match="xslthl:string" mode="xslthl">
+        <span class="hl-string">
+            <xsl:apply-templates />
+        </span>
+    </xsl:template>
+    <xsl:template match="xslthl:annotation" mode="xslthl">
+        <span class="hl-annotation">
+            <xsl:apply-templates />
+        </span>
+    </xsl:template>
+    <xsl:template match="xslthl:keyword" mode="xslthl">
+        <span class="hl-keyword">
+            <xsl:apply-templates />
+        </span>
+    </xsl:template>
+
+
+<xsl:template name="nongraphical.admonition">
+  <xsl:variable name="admon.icon">
+    <xsl:choose>
+      <xsl:when test="local-name(.)='note'">fa fa-asterisk</xsl:when>
+      <xsl:when test="local-name(.)='warning'">fa fa-exclamation-triangle</xsl:when>
+      <xsl:when test="local-name(.)='caution'">fa fa-exclamation</xsl:when>
+      <xsl:when test="local-name(.)='tip'">fa fa-lightbulb-o</xsl:when>
+      <xsl:when test="local-name(.)='important'">fa fa-plus</xsl:when>
+      <xsl:otherwise>fa fa-asterisk</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <div>
+    <xsl:apply-templates select="." mode="class.attribute"/>
+    <xsl:if test="$admon.style">
+      <xsl:attribute name="style">
+        <xsl:value-of select="$admon.style"/>
+      </xsl:attribute>
+    </xsl:if>
+
+    <xsl:if test="$admon.textlabel != 0 or d:title or d:info/d:title">
+      <h3 class="title">
+        <xsl:call-template name="anchor"/>
+        <i>
+          <xsl:attribute name="class">
+            <xsl:value-of select="$admon.icon"/>
+          </xsl:attribute>
+          <xsl:attribute name="aria-hidden">true</xsl:attribute>
+        </i>
+        <xsl:text> </xsl:text>
+        <xsl:apply-templates select="." mode="object.title.markup"/>
+      </h3>
+    </xsl:if>
+
+    <xsl:apply-templates/>
+  </div>
+</xsl:template>
+
+
+<xsl:template name="navig.content">
+    <xsl:param name="direction" select="d:next"/>
+        <xsl:choose>
+            <xsl:when test="$direction = 'prev'">
+              <xsl:element name="i">
+                <xsl:attribute name="class">fa fa-chevron-left</xsl:attribute>
+                <xsl:attribute name="aria-hidden">true</xsl:attribute>
+              </xsl:element>
+              <xsl:text> Previous</xsl:text>
+            </xsl:when>
+            <xsl:when test="$direction = 'next'">
+              <xsl:text>Next </xsl:text>
+              <xsl:element name="i">
+                <xsl:attribute name="class">fa fa-chevron-right</xsl:attribute>
+                <xsl:attribute name="aria-hidden">true</xsl:attribute>
+              </xsl:element>
+            </xsl:when>
+            <xsl:when test="$direction = 'up'">
+                <xsl:element name="i">
+                <xsl:attribute name="class">fa fa-chevron-up</xsl:attribute>
+                <xsl:attribute name="aria-hidden">true</xsl:attribute>
+              </xsl:element>
+              <xsl:text> Top</xsl:text>
+            </xsl:when>
+            <xsl:when test="$direction = 'home'">
+              <xsl:element name="i">
+                <xsl:attribute name="class">fa fa-home</xsl:attribute>
+                <xsl:attribute name="aria-hidden">true</xsl:attribute>
+              </xsl:element>
+              <xsl:text> Home</xsl:text>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:text>xxx</xsl:text>
+            </xsl:otherwise>
+        </xsl:choose>
+</xsl:template>
+
+<!--
+Override the default header navigation to insert a home button on the top.
+-->
+<xsl:template name="header.navigation">
+  <xsl:param name="prev" select="/d:foo"/>
+  <xsl:param name="next" select="/d:foo"/>
+  <xsl:param name="nav.context"/>
+
+  <xsl:variable name="home" select="/*[1]"/>
+  <xsl:variable name="up" select="parent::*"/>
+
+  <xsl:variable name="row1" select="$navig.showtitles != 0"/>
+  <xsl:variable name="row2" select="count($prev) &gt; 0
+                                    or (count($up) &gt; 0 
+                                        and generate-id($up) != generate-id($home)
+                                        and $navig.showtitles != 0)
+                                    or count($next) &gt; 0"/>
+
+  <xsl:if test="$suppress.navigation = '0' and $suppress.header.navigation = '0'">
+    <div class="navheader">
+      <xsl:if test="$row1 or $row2">
+        <table width="100%" summary="Navigation header">
+          <xsl:if test="$row1">
+            <tr>
+              <th colspan="3" align="center">
+                <xsl:apply-templates select="." mode="object.title.markup"/>
+              </th>
+            </tr>
+          </xsl:if>
+
+          <xsl:if test="$row2">
+            <tr>
+              <td width="20%" align="left">
+                <xsl:if test="count($prev)>0">
+                  <a accesskey="p">
+                    <xsl:attribute name="href">
+                      <xsl:call-template name="href.target">
+                        <xsl:with-param name="object" select="$prev"/>
+                      </xsl:call-template>
+                    </xsl:attribute>
+                    <xsl:call-template name="navig.content">
+                      <xsl:with-param name="direction" select="'prev'"/>
+                    </xsl:call-template>
+                  </a>
+                </xsl:if>
+                <xsl:text>&#160;</xsl:text>
+              </td>
+              <th width="60%" align="center">
+                <xsl:choose>
+                  <xsl:when test="count($up) > 0
+                                  and generate-id($up) != generate-id($home)
+                                  and $navig.showtitles != 0">
+                    <xsl:apply-templates select="$up" mode="object.title.markup"/>
+                  </xsl:when>
+                  <xsl:otherwise>&#160;</xsl:otherwise>
+                </xsl:choose>
+                <br/>
+                <!-- really conjested with it here
+                <span style="font-size: 8pt; font-weight: normal">
+                  <xsl:value-of select="/d:book/d:info/d:revhistory/d:revision[1]/d:revnumber"/>
+                </span>
+                <br/>
+                -->
+                <a accesskey="p">
+                    <xsl:attribute name="href">
+                      <xsl:call-template name="href.target">
+                        <xsl:with-param name="object" select="$home"/>
+                      </xsl:call-template>
+                    </xsl:attribute>
+                    <xsl:call-template name="navig.content">
+                      <xsl:with-param name="direction" select="'home'"/>
+                    </xsl:call-template>
+                </a>
+              </th>
+              <td width="20%" align="right">
+                <xsl:text>&#160;</xsl:text>
+                <xsl:if test="count($next)>0">
+                  <a accesskey="n">
+                    <xsl:attribute name="href">
+                      <xsl:call-template name="href.target">
+                        <xsl:with-param name="object" select="$next"/>
+                      </xsl:call-template>
+                    </xsl:attribute>
+                    <xsl:call-template name="navig.content">
+                      <xsl:with-param name="direction" select="'next'"/>
+                    </xsl:call-template>
+                  </a>
+                </xsl:if>
+              </td>
+            </tr>
+          </xsl:if>
+        </table>
+      </xsl:if>
+      <xsl:if test="$header.rule != 0">
+        <hr/>
+      </xsl:if>
+    </div>
+  </xsl:if>
+</xsl:template>
+
+
+</xsl:stylesheet>
diff --git a/jetty-fcgi/fcgi-client/pom.xml b/jetty-fcgi/fcgi-client/pom.xml
index d9daf08..9dab830 100644
--- a/jetty-fcgi/fcgi-client/pom.xml
+++ b/jetty-fcgi/fcgi-client/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.fcgi</groupId>
         <artifactId>fcgi-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
+        <version>9.3.19-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java
index 1934bb0..267bf24 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java
@@ -79,6 +79,7 @@
         if (exchange != null)
         {
             version = exchange.getRequest().getVersion();
+            idle.onOpen();
             sender.send(exchange);
         }
     }
@@ -91,6 +92,7 @@
 
     protected boolean responseBegin(int code, String reason)
     {
+        idle.notIdle();
         HttpExchange exchange = getHttpExchange();
         if (exchange == null)
             return false;
@@ -106,12 +108,14 @@
 
     protected boolean responseHeaders()
     {
+       idle.notIdle();
         HttpExchange exchange = getHttpExchange();
         return exchange != null && receiver.responseHeaders(exchange);
     }
 
     protected boolean content(ByteBuffer buffer, Callback callback)
     {
+        idle.notIdle();
         HttpExchange exchange = getHttpExchange();
         if (exchange != null)
             return receiver.responseContent(exchange, buffer, callback);
@@ -151,6 +155,7 @@
     private class FCGIIdleTimeout extends IdleTimeout
     {
         private final HttpConnectionOverFCGI connection;
+        private boolean open;
 
         public FCGIIdleTimeout(HttpConnectionOverFCGI connection, long idleTimeout)
         {
@@ -160,6 +165,21 @@
         }
 
         @Override
+        public void onOpen()
+        {
+            open = true;
+            notIdle();
+            super.onOpen();
+        }
+
+        @Override
+        public void onClose()
+        {
+            super.onClose();
+            open = false;
+        }
+
+        @Override
         protected void onIdleExpired(TimeoutException timeout)
         {
             if (LOG.isDebugEnabled())
@@ -170,7 +190,7 @@
         @Override
         public boolean isOpen()
         {
-            return connection.getEndPoint().isOpen();
+            return open;
         }
     }
 }
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpClientTransportOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpClientTransportOverFCGI.java
index edc8e0a..aded21e 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpClientTransportOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpClientTransportOverFCGI.java
@@ -30,7 +30,10 @@
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
 
+@ManagedObject("The FastCGI/1.0 client transport")
 public class HttpClientTransportOverFCGI extends AbstractHttpClientTransport
 {
     private final boolean multiplexed;
@@ -53,6 +56,7 @@
         return multiplexed;
     }
 
+    @ManagedAttribute(value = "The scripts root directory", readonly = true)
     public String getScriptRoot()
     {
         return scriptRoot;
@@ -71,10 +75,15 @@
         HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
         @SuppressWarnings("unchecked")
         Promise<Connection> promise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
-        HttpConnectionOverFCGI connection = new HttpConnectionOverFCGI(endPoint, destination, promise, isMultiplexed());
+        HttpConnectionOverFCGI connection = newHttpConnection(endPoint, destination, promise);
         if (LOG.isDebugEnabled())
             LOG.debug("Created {}", connection);
-        return connection;
+        return customize(connection, context);
+    }
+
+    protected HttpConnectionOverFCGI newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
+    {
+        return new HttpConnectionOverFCGI(endPoint, destination, promise, isMultiplexed());
     }
 
     protected void customize(Request request, HttpFields fastCGIHeaders)
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java
index dd4f9a3..8e81a03 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java
@@ -31,6 +31,7 @@
 import org.eclipse.jetty.client.HttpConnection;
 import org.eclipse.jetty.client.HttpDestination;
 import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.SendFailure;
 import org.eclipse.jetty.client.api.Connection;
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.client.api.Response;
@@ -57,27 +58,17 @@
     private final LinkedList<Integer> requests = new LinkedList<>();
     private final Map<Integer, HttpChannelOverFCGI> channels = new ConcurrentHashMap<>();
     private final AtomicBoolean closed = new AtomicBoolean();
-    private final Flusher flusher;
     private final HttpDestination destination;
     private final Promise<Connection> promise;
     private final boolean multiplexed;
+    private final Flusher flusher;
     private final Delegate delegate;
     private final ClientParser parser;
     private ByteBuffer buffer;
 
-    /**
-     * @deprecated use {@link #HttpConnectionOverFCGI(EndPoint, HttpDestination, Promise, boolean)} instead
-     */
-    @Deprecated
-    public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination, boolean multiplexed)
-    {
-        this(endPoint, destination, new Promise.Adapter<Connection>(), multiplexed);
-        throw new UnsupportedOperationException("Deprecated, use HttpConnectionOverFCGI(EndPoint, HttpDestination, Promise<Connection>, boolean) instead");
-    }
-
     public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise, boolean multiplexed)
     {
-        super(endPoint, destination.getHttpClient().getExecutor(), destination.getHttpClient().isDispatchIO());
+        super(endPoint, destination.getHttpClient().getExecutor());
         this.destination = destination;
         this.promise = promise;
         this.multiplexed = multiplexed;
@@ -92,15 +83,20 @@
         return destination;
     }
 
+    protected Flusher getFlusher()
+    {
+        return flusher;
+    }
+
     @Override
     public void send(Request request, Response.CompleteListener listener)
     {
         delegate.send(request, listener);
     }
 
-    protected void send(HttpExchange exchange)
+    protected SendFailure send(HttpExchange exchange)
     {
-        delegate.send(exchange);
+        return delegate.send(exchange);
     }
 
     @Override
@@ -114,61 +110,68 @@
     @Override
     public void onFillable()
     {
+        buffer = acquireBuffer();
+        process(buffer);
+    }
+
+    private ByteBuffer acquireBuffer()
+    {
         HttpClient client = destination.getHttpClient();
         ByteBufferPool bufferPool = client.getByteBufferPool();
-        buffer = bufferPool.acquire(client.getResponseBufferSize(), true);
-        process();
+        return bufferPool.acquire(client.getResponseBufferSize(), true);
     }
 
-    private void process()
+    private void releaseBuffer(ByteBuffer buffer)
     {
-        if (readAndParse())
-        {
-            HttpClient client = destination.getHttpClient();
-            ByteBufferPool bufferPool = client.getByteBufferPool();
-            bufferPool.release(buffer);
-            // Don't linger the buffer around if we are idle.
-            buffer = null;
-        }
+        assert this.buffer == buffer;
+        HttpClient client = destination.getHttpClient();
+        ByteBufferPool bufferPool = client.getByteBufferPool();
+        bufferPool.release(buffer);
+        this.buffer = null;
     }
 
-    private boolean readAndParse()
+    private void process(ByteBuffer buffer)
     {
-        EndPoint endPoint = getEndPoint();
-        ByteBuffer buffer = this.buffer;
-        while (true)
+        try
         {
-            try
+            EndPoint endPoint = getEndPoint();
+            boolean looping = false;
+            while (true)
             {
-                if (parse(buffer))
-                    return false;
+                if (!looping && parse(buffer))
+                    return;
 
                 int read = endPoint.fill(buffer);
-                if (LOG.isDebugEnabled()) // Avoid boxing of variable 'read'.
+                if (LOG.isDebugEnabled())
                     LOG.debug("Read {} bytes from {}", read, endPoint);
+
                 if (read > 0)
                 {
                     if (parse(buffer))
-                        return false;
+                        return;
                 }
                 else if (read == 0)
                 {
+                    releaseBuffer(buffer);
                     fillInterested();
-                    return true;
+                    return;
                 }
                 else
                 {
+                    releaseBuffer(buffer);
                     shutdown();
-                    return true;
+                    return;
                 }
+
+                looping = true;
             }
-            catch (Exception x)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug(x);
-                close(x);
-                return false;
-            }
+        }
+        catch (Exception x)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug(x);
+            releaseBuffer(buffer);
+            close(x);
         }
     }
 
@@ -184,13 +187,18 @@
         if (channels.isEmpty())
             close();
         else
-            failAndClose(new EOFException());
+            failAndClose(new EOFException(String.valueOf(getEndPoint())));
     }
 
     @Override
-    protected boolean onReadTimeout()
+    public boolean onIdleExpired()
     {
-        close(new TimeoutException());
+        long idleTimeout = getEndPoint().getIdleTimeout();
+        boolean close = delegate.onIdleTimeout(idleTimeout);
+        if (multiplexed)
+            close &= isFillInterested();
+        if (close)
+            close(new TimeoutException("Idle timeout " + idleTimeout + " ms"));
         return false;
     }
 
@@ -210,17 +218,16 @@
     {
         if (closed.compareAndSet(false, true))
         {
-            // First close then abort, to be sure that the connection cannot be reused
-            // from an onFailure() handler or by blocking code waiting for completion.
             getHttpDestination().close(this);
-            getEndPoint().shutdownOutput();
-            if (LOG.isDebugEnabled())
-                LOG.debug("{} oshut", this);
-            getEndPoint().close();
-            if (LOG.isDebugEnabled())
-                LOG.debug("{} closed", this);
 
             abort(failure);
+
+            getEndPoint().shutdownOutput();
+            if (LOG.isDebugEnabled())
+                LOG.debug("Shutdown {}", this);
+            getEndPoint().close();
+            if (LOG.isDebugEnabled())
+                LOG.debug("Closed {}", this);
         }
     }
 
@@ -279,6 +286,11 @@
         }
     }
 
+    protected HttpChannelOverFCGI newHttpChannel(int id, Request request)
+    {
+        return new HttpChannelOverFCGI(this, getFlusher(), id, request.getIdleTimeout());
+    }
+
     @Override
     public String toString()
     {
@@ -297,19 +309,17 @@
         }
 
         @Override
-        protected void send(HttpExchange exchange)
+        protected SendFailure send(HttpExchange exchange)
         {
             Request request = exchange.getRequest();
             normalizeRequest(request);
 
             // FCGI may be multiplexed, so create one channel for each request.
             int id = acquireRequest();
-            HttpChannelOverFCGI channel = new HttpChannelOverFCGI(HttpConnectionOverFCGI.this, flusher, id, request.getIdleTimeout());
+            HttpChannelOverFCGI channel = newHttpChannel(id, request);
             channels.put(id, channel);
-            if (channel.associate(exchange))
-                channel.send();
-            else
-                channel.release();
+
+            return send(channel, exchange);
         }
 
         @Override
@@ -318,6 +328,11 @@
             HttpConnectionOverFCGI.this.close();
         }
 
+        protected void close(Throwable failure)
+        {
+            HttpConnectionOverFCGI.this.close(failure);
+        }
+
         @Override
         public boolean isClosed()
         {
@@ -380,7 +395,7 @@
                             {
                                 if (LOG.isDebugEnabled())
                                     LOG.debug("Content consumed asynchronously, resuming processing");
-                                process();
+                                process(HttpConnectionOverFCGI.this.buffer);
                             }
 
                             @Override
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java
index 1a81750..9c96293 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java
@@ -22,6 +22,7 @@
 import org.eclipse.jetty.client.HttpExchange;
 import org.eclipse.jetty.client.Origin;
 import org.eclipse.jetty.client.PoolingHttpDestination;
+import org.eclipse.jetty.client.SendFailure;
 
 public class HttpDestinationOverFCGI extends PoolingHttpDestination<HttpConnectionOverFCGI>
 {
@@ -31,8 +32,8 @@
     }
 
     @Override
-    protected void send(HttpConnectionOverFCGI connection, HttpExchange exchange)
+    protected SendFailure send(HttpConnectionOverFCGI connection, HttpExchange exchange)
     {
-        connection.send(exchange);
+        return connection.send(exchange);
     }
 }
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java
index a5724ab..6598cc7 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java
@@ -64,9 +64,9 @@
 
         // FastCGI headers based on the URI
         URI uri = request.getURI();
-        String path = uri.getRawPath();
+        String path = uri == null ? request.getPath() : uri.getRawPath();
         fcgiHeaders.put(FCGI.Headers.DOCUMENT_URI, path);
-        String query = uri.getRawQuery();
+        String query = uri == null ? null : uri.getRawQuery();
         fcgiHeaders.put(FCGI.Headers.QUERY_STRING, query == null ? "" : query);
 
         // FastCGI headers based on HTTP headers
@@ -99,7 +99,7 @@
         int id = getHttpChannel().getRequest();
         boolean hasContent = content.hasContent();
         Generator.Result headersResult = generator.generateRequestHeaders(id, fcgiHeaders,
-                hasContent ? callback : Callback.Adapter.INSTANCE);
+                hasContent ? callback : Callback.NOOP);
         if (hasContent)
         {
             getHttpChannel().flush(headersResult);
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java
index 2036e39..f813bc2 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java
@@ -22,6 +22,7 @@
 import org.eclipse.jetty.client.HttpExchange;
 import org.eclipse.jetty.client.MultiplexHttpDestination;
 import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.SendFailure;
 
 public class MultiplexHttpDestinationOverFCGI extends MultiplexHttpDestination<HttpConnectionOverFCGI>
 {
@@ -31,8 +32,8 @@
     }
 
     @Override
-    protected void send(HttpConnectionOverFCGI connection, HttpExchange exchange)
+    protected SendFailure send(HttpConnectionOverFCGI connection, HttpExchange exchange)
     {
-        connection.send(exchange);
+        return connection.send(exchange);
     }
 }
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ClientGenerator.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ClientGenerator.java
index 1c6d470..d923f6f 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ClientGenerator.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ClientGenerator.java
@@ -20,6 +20,7 @@
 
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -46,7 +47,7 @@
     {
         request &= 0xFF_FF;
 
-        Charset utf8 = Charset.forName("UTF-8");
+        final Charset utf8 = StandardCharsets.UTF_8;
         List<byte[]> bytes = new ArrayList<>(fields.size() * 2);
         int fieldsLength = 0;
         for (HttpField field : fields)
@@ -88,7 +89,7 @@
         beginRequestBuffer.putInt(0x00_08_00_00);
         // Hardcode RESPONDER role and KEEP_ALIVE flag
         beginRequestBuffer.putLong(0x00_01_01_00_00_00_00_00L);
-        beginRequestBuffer.flip();
+        BufferUtil.flipToFlush(beginRequestBuffer, 0);
 
         int index = 0;
         while (fieldsLength > 0)
@@ -128,7 +129,7 @@
             }
 
             buffer.putShort(4, (short)length);
-            buffer.flip();
+            BufferUtil.flipToFlush(buffer, 0);
         }
 
 
@@ -139,7 +140,7 @@
         // Generate the last FCGI_PARAMS frame
         lastParamsBuffer.putInt(0x01_04_00_00 + request);
         lastParamsBuffer.putInt(0x00_00_00_00);
-        lastParamsBuffer.flip();
+        BufferUtil.flipToFlush(lastParamsBuffer, 0);
 
         return result;
     }
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Flusher.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Flusher.java
index c78b8a0..a040ae0 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Flusher.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Flusher.java
@@ -19,10 +19,10 @@
 package org.eclipse.jetty.fcgi.generator;
 
 import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
 import java.util.Queue;
 
 import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.util.ConcurrentArrayQueue;
 import org.eclipse.jetty.util.IteratingCallback;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -31,7 +31,7 @@
 {
     private static final Logger LOG = Log.getLogger(Flusher.class);
 
-    private final Queue<Generator.Result> queue = new ConcurrentArrayQueue<>();
+    private final Queue<Generator.Result> queue = new ArrayDeque<>();
     private final IteratingCallback flushCallback = new FlushCallback();
     private final EndPoint endPoint;
 
@@ -43,10 +43,26 @@
     public void flush(Generator.Result... results)
     {
         for (Generator.Result result : results)
-            queue.offer(result);
+            offer(result);
         flushCallback.iterate();
     }
 
+    private void offer(Generator.Result result)
+    {
+        synchronized (this)
+        {
+            queue.offer(result);
+        }
+    }
+
+    private Generator.Result poll()
+    {
+        synchronized (this)
+        {
+            return queue.poll();
+        }
+    }
+
     public void shutdown()
     {
         flush(new ShutdownResult());
@@ -60,7 +76,7 @@
         protected Action process() throws Exception
         {
             // Look if other writes are needed.
-            Generator.Result result = queue.poll();
+            Generator.Result result = poll();
             if (result == null)
             {
                 // No more writes to do, return.
@@ -71,7 +87,7 @@
             // Most often there is another result in the
             // queue so this is a real optimization because
             // it sends both results in just one TCP packet.
-            Generator.Result other = queue.poll();
+            Generator.Result other = poll();
             if (other != null)
                 result = result.join(other);
 
@@ -106,7 +122,7 @@
 
             while (true)
             {
-                Generator.Result result = queue.poll();
+                Generator.Result result = poll();
                 if (result == null)
                     break;
                 result.failed(x);
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Generator.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Generator.java
index 04f9dce..75b79c9 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Generator.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/Generator.java
@@ -58,7 +58,7 @@
             int length = Math.min(MAX_CONTENT_LENGTH, contentLength);
             buffer.putShort((short)length);
             buffer.putShort((short)0);
-            buffer.flip();
+            BufferUtil.flipToFlush(buffer, 0);
 
             if (contentLength == 0)
                 break;
@@ -80,6 +80,7 @@
         return result;
     }
 
+    // TODO: rewrite this class in light of ByteBufferPool.Lease.
     public static class Result implements Callback
     {
         private final List<Callback> callbacks = new ArrayList<>(2);
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ServerGenerator.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ServerGenerator.java
index 127b656..f3227c5 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ServerGenerator.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ServerGenerator.java
@@ -20,6 +20,7 @@
 
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -54,7 +55,7 @@
     {
         request &= 0xFF_FF;
 
-        Charset utf8 = Charset.forName("UTF-8");
+        final Charset utf8 = StandardCharsets.UTF_8;
         List<byte[]> bytes = new ArrayList<>(fields.size() * 2);
         int length = 0;
 
@@ -94,7 +95,7 @@
             buffer.put(bytes.get(i)).put(COLON).put(bytes.get(i + 1)).put(EOL);
         buffer.put(EOL);
 
-        buffer.flip();
+        BufferUtil.flipToFlush(buffer, 0);
 
         return generateContent(request, buffer, true, false, callback, FCGI.FrameType.STDOUT);
     }
@@ -128,7 +129,7 @@
         endRequestBuffer.putInt(0x00_08_00_00);
         endRequestBuffer.putInt(aborted ? 1 : 0);
         endRequestBuffer.putInt(0);
-        endRequestBuffer.flip();
+        BufferUtil.flipToFlush(endRequestBuffer, 0);
         return endRequestBuffer;
     }
 }
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/BeginRequestContentParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/BeginRequestContentParser.java
index ca7d784..9b4d921 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/BeginRequestContentParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/BeginRequestContentParser.java
@@ -22,6 +22,16 @@
 
 import org.eclipse.jetty.fcgi.FCGI;
 
+/**
+ * <p>Parser for the BEGIN_REQUEST frame body.</p>
+ * <pre>
+ * struct begin_request_body {
+ *     ushort role;
+ *     ubyte flags;
+ *     ubyte[5] reserved;
+ * }
+ * </pre>
+ */
 public class BeginRequestContentParser extends ContentParser
 {
     private final ServerParser.Listener listener;
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java
index 7654c89..f7b5a18 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java
@@ -20,6 +20,16 @@
 
 import java.nio.ByteBuffer;
 
+/**
+ * <p>Parser for the END_REQUEST frame body.</p>
+ * <pre>
+ * struct end_request_body {
+ *     uint applicationStatus;
+ *     ubyte protocolStatus;
+ *     ubyte[3] reserved;
+ * }
+ * </pre>
+ */
 public class EndRequestContentParser extends ContentParser
 {
     private final Parser.Listener listener;
@@ -80,7 +90,7 @@
                     }
                     else
                     {
-                        state = State.APPLICATION_BYTES;
+                        state = State.RESERVED_BYTES;
                         cursor = 0;
                         break;
                     }
@@ -88,7 +98,7 @@
                 case RESERVED_BYTES:
                 {
                     buffer.get();
-                    if (++cursor == 0)
+                    if (++cursor == 3)
                     {
                         onEnd();
                         reset();
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/HeaderParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/HeaderParser.java
index 9d9d191..028f2ea 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/HeaderParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/HeaderParser.java
@@ -21,9 +21,28 @@
 import java.nio.ByteBuffer;
 
 import org.eclipse.jetty.fcgi.FCGI;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
 
+/**
+ * <p>Parser for FastCGI frame headers.</p>
+ * <pre>
+ * struct frame_header {
+ *     ubyte version;
+ *     ubyte type;
+ *     ushort requestId;
+ *     ushort contentLength;
+ *     ubyte paddingLength;
+ *     ubyte reserved;
+ * }
+ * </pre>
+ *
+ * @see Parser
+ */
 public class HeaderParser
 {
+    private static final Logger LOG = Log.getLogger(Parser.class);
+
     private State state = State.VERSION;
     private int cursor;
     private int version;
@@ -109,6 +128,8 @@
                 case RESERVED:
                 {
                     buffer.get();
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Parsed request {} header {} length={}", getRequest(), getFrameType(), getContentLength());
                     return true;
                 }
                 default:
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ParamsContentParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ParamsContentParser.java
index b48f1f1..2610c94 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ParamsContentParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ParamsContentParser.java
@@ -20,11 +20,44 @@
 
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 
 import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
+/**
+ * <p>Parser for the PARAMS frame body.</p>
+ * <pre>
+ * struct small_name_small_value_params_body {
+ *     ubyte nameLength;
+ *     ubyte valueLength;
+ *     ubyte[] nameBytes;
+ *     ubyte[] valueBytes;
+ * }
+ *
+ * struct small_name_large_value_params_body {
+ *     ubyte nameLength;
+ *     uint valueLength;
+ *     ubyte[] nameBytes;
+ *     ubyte[] valueBytes;
+ * }
+ *
+ * struct large_name_small_value_params_body {
+ *     uint nameLength;
+ *     ubyte valueLength;
+ *     ubyte[] nameBytes;
+ *     ubyte[] valueBytes;
+ * }
+ *
+ * struct large_name_large_value_params_body {
+ *     uint nameLength;
+ *     uint valueLength;
+ *     ubyte[] nameBytes;
+ *     ubyte[] valueBytes;
+ * }
+ * </pre>
+ */
 public class ParamsContentParser extends ContentParser
 {
     private static final Logger LOG = Log.getLogger(ParamsContentParser.class);
@@ -179,7 +212,7 @@
                 }
                 case PARAM:
                 {
-                    Charset utf8 = Charset.forName("UTF-8");
+                    Charset utf8 = StandardCharsets.UTF_8;
                     onParam(new String(nameBytes, utf8), new String(valueBytes, utf8));
                     partialReset();
                     if (length == 0)
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java
index ff9c7ae..9b13498 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java
@@ -22,9 +22,33 @@
 
 import org.eclipse.jetty.fcgi.FCGI;
 import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
 
+/**
+ * <p>The FastCGI protocol exchanges <em>frames</em>.</p>
+ * <pre>
+ * struct frame {
+ *     ubyte version;
+ *     ubyte type;
+ *     ushort requestId;
+ *     ushort contentLength;
+ *     ubyte paddingLength;
+ *     ubyte reserved;
+ *     ubyte[] content;
+ *     ubyte[] padding;
+ * }
+ * </pre>
+ * <p>Depending on the {@code type}, the content may have a different format,
+ * so there are specialized content parsers.</p>
+ *
+ * @see HeaderParser
+ * @see ContentParser
+ */
 public abstract class Parser
 {
+    private static final Logger LOG = Log.getLogger(Parser.class);
+
     protected final HeaderParser headerParser = new HeaderParser();
     private State state = State.HEADER;
     private int padding;
@@ -56,6 +80,9 @@
                     else
                     {
                         ContentParser.Result result = contentParser.parse(buffer);
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("Parsed request {} content {} result={}", headerParser.getRequest(), headerParser.getFrameType(), result);
+
                         if (result == ContentParser.Result.PENDING)
                         {
                             // Not enough data, signal to read/parse more.
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java
index 3e15fcf..8a186b7 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java
@@ -18,11 +18,13 @@
 
 package org.eclipse.jetty.fcgi.parser;
 
+import java.io.EOFException;
 import java.nio.ByteBuffer;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.eclipse.jetty.fcgi.FCGI;
+import org.eclipse.jetty.http.BadMessageException;
 import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpHeader;
@@ -32,6 +34,14 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
+/**
+ * <p>The parser for STDOUT type frame bodies.</p>
+ * <p>STDOUT frame bodies contain both the HTTP headers (but not the response line)
+ * and the HTTP content (either Content-Length delimited or chunked).</p>
+ * <p>For this reason, a special HTTP parser is used to parse the frames body.
+ * This special HTTP parser is configured to skip the response line, and to
+ * parse HTTP headers and HTTP content.</p>
+ */
 public class ResponseContentParser extends StreamContentParser
 {
     private static final Logger LOG = Log.getLogger(ResponseContentParser.class);
@@ -71,7 +81,7 @@
         parsers.remove(request);
     }
 
-    private class ResponseParser implements HttpParser.ResponseHandler<ByteBuffer>
+    private class ResponseParser implements HttpParser.ResponseHandler
     {
         private final HttpFields fields = new HttpFields();
         private ClientParser.Listener listener;
@@ -89,12 +99,12 @@
 
         public boolean parse(ByteBuffer buffer)
         {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Response {} {} content {} {}", request, FCGI.StreamType.STD_OUT, state, buffer);
-
             int remaining = buffer.remaining();
             while (remaining > 0)
             {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Response {} {}, state {} {}", request, FCGI.StreamType.STD_OUT, state, buffer);
+
                 switch (state)
                 {
                     case HEADERS:
@@ -154,7 +164,7 @@
         }
 
         @Override
-        public boolean parsedHeader(HttpField httpField)
+        public void parsedHeader(HttpField httpField)
         {
             try
             {
@@ -190,7 +200,6 @@
                 if (LOG.isDebugEnabled())
                     LOG.debug("Exception while invoking listener " + listener, x);
             }
-            return false;
         }
 
         private void notifyBegin(int code, String reason)
@@ -246,12 +255,12 @@
         {
             if (!seenResponseCode)
             {
-                // No Status header but we have other headers, assume 200 OK
+                // No Status header but we have other headers, assume 200 OK.
                 notifyBegin(200, "OK");
                 notifyHeaders(fields);
             }
             notifyHeaders();
-            // Return from parsing so that we can parse the content
+            // Return from HTTP parsing so that we can parse the content.
             return true;
         }
 
@@ -276,30 +285,49 @@
         }
 
         @Override
+        public boolean contentComplete()
+        {
+            return false;
+        }
+        
+        @Override
         public boolean messageComplete()
         {
-            // Return from parsing so that we can parse the next headers or the raw content.
-            // No need to notify the listener because it will be done by FCGI_END_REQUEST.
-            return true;
+            // No need to notify the end of the response to the
+            // listener because it will be done by FCGI_END_REQUEST.
+            return false;
         }
 
         @Override
         public void earlyEOF()
         {
-            // TODO
+            fail(new EOFException());
         }
 
         @Override
         public void badMessage(int status, String reason)
         {
-            // TODO
+            fail(new BadMessageException(status, reason));
+        }
+
+        protected void fail(Throwable failure)
+        {
+            try
+            {
+                listener.onFailure(request, failure);
+            }
+            catch (Throwable x)
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Exception while invoking listener " + listener, x);
+            }
         }
     }
 
     // Methods overridden to make them visible here
     private static class FCGIHttpParser extends HttpParser
     {
-        private FCGIHttpParser(ResponseHandler<ByteBuffer> handler)
+        private FCGIHttpParser(ResponseHandler handler)
         {
             super(handler, 65 * 1024, true);
             reset();
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/StreamContentParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/StreamContentParser.java
index 1b1af44..97bdc87 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/StreamContentParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/StreamContentParser.java
@@ -24,6 +24,10 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
+/**
+ * <p>A stream content parser parses frame bodies of type STDIN, STDOUT and STDERR.</p>
+ * <p>STDOUT frame bodies are handled specially by {@link ResponseContentParser}.
+ */
 public class StreamContentParser extends ContentParser
 {
     private static final Logger LOG = Log.getLogger(StreamContentParser.class);
diff --git a/jetty-fcgi/fcgi-server/pom.xml b/jetty-fcgi/fcgi-server/pom.xml
index 713e4ad..aa1d650 100644
--- a/jetty-fcgi/fcgi-server/pom.xml
+++ b/jetty-fcgi/fcgi-server/pom.xml
@@ -1,73 +1,67 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.fcgi</groupId>
-        <artifactId>fcgi-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
-    </parent>
+  <parent>
+    <groupId>org.eclipse.jetty.fcgi</groupId>
+    <artifactId>fcgi-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
 
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>fcgi-server</artifactId>
-    <name>Jetty :: FastCGI :: Server</name>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>fcgi-server</artifactId>
+  <name>Jetty :: FastCGI :: Server</name>
 
-    <properties>
-        <bundle-symbolic-name>${project.groupId}.server</bundle-symbolic-name>
-    </properties>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.server</bundle-symbolic-name>
+  </properties>
 
-    <build>
-        <plugins>
-            <plugin>
-                <artifactId>maven-assembly-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>single</goal>
-                        </goals>
-                        <configuration>
-                            <descriptorRefs>
-                                <descriptorRef>config</descriptorRef>
-                            </descriptorRefs>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
+  <build>
+    <plugins>
+    </plugins>
+  </build>
 
-    <dependencies>
-        <dependency>
-            <groupId>javax.servlet</groupId>
-            <artifactId>javax.servlet-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.fcgi</groupId>
-            <artifactId>fcgi-client</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-proxy</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-server</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-servlet</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-http-server</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-
+  <dependencies>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.fcgi</groupId>
+      <artifactId>fcgi-client</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-proxy</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlet</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.http2</groupId>
+      <artifactId>http2-server</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-alpn-server</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-annotations</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
 </project>
diff --git a/jetty-fcgi/fcgi-server/src/main/config/modules/fcgi.mod b/jetty-fcgi/fcgi-server/src/main/config/modules/fcgi.mod
index e837b00..14152d5 100644
--- a/jetty-fcgi/fcgi-server/src/main/config/modules/fcgi.mod
+++ b/jetty-fcgi/fcgi-server/src/main/config/modules/fcgi.mod
@@ -12,4 +12,4 @@
 
 [ini-template]
 ## For configuration of FastCGI contexts, see
-## TODO: documentation url here
+## https://www.eclipse.org/jetty/documentation/current/fastcgi.html
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java
index 7af7474..65ad9de 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java
@@ -18,75 +18,79 @@
 
 package org.eclipse.jetty.fcgi.server;
 
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.eclipse.jetty.fcgi.FCGI;
+import org.eclipse.jetty.http.HostPortHttpField;
 import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpScheme;
 import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.HttpChannel;
 import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpInput;
 import org.eclipse.jetty.server.HttpTransport;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-public class HttpChannelOverFCGI extends HttpChannel<ByteBuffer>
+public class HttpChannelOverFCGI extends HttpChannel
 {
     private static final Logger LOG = Log.getLogger(HttpChannelOverFCGI.class);
 
-    private final List<HttpField> fields = new ArrayList<>();
+    private final HttpFields fields = new HttpFields();
     private final Dispatcher dispatcher;
     private String method;
     private String path;
     private String query;
     private String version;
+    private HostPortHttpField hostPort;
 
-    public HttpChannelOverFCGI(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
+    public HttpChannelOverFCGI(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport)
     {
-        super(connector, configuration, endPoint, transport, input);
-        this.dispatcher = new Dispatcher(connector.getExecutor(), this);
+        super(connector, configuration, endPoint, transport);
+        this.dispatcher = new Dispatcher(connector.getServer().getThreadPool(), this);
     }
 
     protected void header(HttpField field)
     {
-        if (FCGI.Headers.REQUEST_METHOD.equalsIgnoreCase(field.getName()))
-            method = field.getValue();
-        else if (FCGI.Headers.DOCUMENT_URI.equalsIgnoreCase(field.getName()))
-            path = field.getValue();
-        else if (FCGI.Headers.QUERY_STRING.equalsIgnoreCase(field.getName()))
-            query = field.getValue();
-        else if (FCGI.Headers.SERVER_PROTOCOL.equalsIgnoreCase(field.getName()))
-            version = field.getValue();
+        String name = field.getName();
+        String value = field.getValue();
+        getRequest().setAttribute(name, value);
+        if (FCGI.Headers.REQUEST_METHOD.equalsIgnoreCase(name))
+            method = value;
+        else if (FCGI.Headers.DOCUMENT_URI.equalsIgnoreCase(name))
+            path = value;
+        else if (FCGI.Headers.QUERY_STRING.equalsIgnoreCase(name))
+            query = value;
+        else if (FCGI.Headers.SERVER_PROTOCOL.equalsIgnoreCase(name))
+            version = value;
         else
-            fields.add(field);
+            processField(field);
     }
 
-    @Override
-    public boolean headerComplete()
+    private void processField(HttpField field)
+    {
+        HttpField httpField = convertHeader(field);
+        if (httpField != null)
+        {
+            fields.add(httpField);
+            if (HttpHeader.HOST.is(httpField.getName()))
+                hostPort = (HostPortHttpField)httpField;
+        }
+    }
+
+    public void onRequest()
     {
         String uri = path;
         if (query != null && query.length() > 0)
             uri += "?" + query;
-        startRequest(HttpMethod.fromString(method), method, ByteBuffer.wrap(uri.getBytes(StandardCharsets.UTF_8)),
-                HttpVersion.fromString(version));
-
-        for (HttpField fcgiField : fields)
-        {
-            HttpField httpField = convertHeader(fcgiField);
-            if (httpField != null)
-                parsedHeader(httpField);
-        }
-
-        return super.headerComplete();
+        // TODO https?
+        onRequest(new MetaData.Request(method, HttpScheme.HTTP.asString(), hostPort, uri, HttpVersion.fromString(version), fields,Long.MIN_VALUE));
     }
 
     private HttpField convertHeader(HttpField field)
@@ -105,7 +109,12 @@
                 httpName.append(Character.toUpperCase(part.charAt(0)));
                 httpName.append(part.substring(1).toLowerCase(Locale.ENGLISH));
             }
-            return new HttpField(httpName.toString(), field.getValue());
+            String headerName = httpName.toString();
+            String value = field.getValue();
+            if (HttpHeader.HOST.is(headerName))
+                return new HostPortHttpField(value);
+            else
+                return new HttpField(headerName, value);
         }
         return null;
     }
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java
index d7d3e54..2fb5fe0 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java
@@ -23,9 +23,9 @@
 import org.eclipse.jetty.fcgi.generator.Flusher;
 import org.eclipse.jetty.fcgi.generator.Generator;
 import org.eclipse.jetty.fcgi.generator.ServerGenerator;
-import org.eclipse.jetty.http.HttpGenerator;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpHeaderValue;
+import org.eclipse.jetty.http.MetaData;
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.server.HttpTransport;
 import org.eclipse.jetty.util.BufferUtil;
@@ -36,7 +36,6 @@
     private final ServerGenerator generator;
     private final Flusher flusher;
     private final int request;
-    private volatile boolean head;
     private volatile boolean shutdown;
     private volatile boolean aborted;
 
@@ -48,16 +47,63 @@
     }
 
     @Override
-    public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
+    public boolean isOptimizedForDirectBuffers()
     {
-        boolean head = this.head = info.isHead();
-        boolean shutdown = this.shutdown = info.getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
+        return false;
+    }
+
+    @Override
+    public void send(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback)
+    {
+        if (info!=null)
+            commit(info,head,content,lastContent,callback);
+        else
+        {
+            if (head)
+            {
+                if (lastContent)
+                {
+                    Generator.Result result = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback);
+                    flusher.flush(result);
+                }
+                else
+                {
+                    // Skip content generation
+                    callback.succeeded();
+                }
+            }
+            else
+            {
+                Generator.Result result = generateResponseContent(content, lastContent, callback);
+                flusher.flush(result);
+            }
+
+            if (lastContent && shutdown)
+                flusher.shutdown();
+        }
+    }
+
+    @Override
+    public boolean isPushSupported()
+    {
+        return false;
+    }
+
+    @Override
+    public void push(org.eclipse.jetty.http.MetaData.Request request)
+    {
+        // LOG.debug("ignore push in {}",this);
+    }
+
+    private void commit(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback)
+    {
+        boolean shutdown = this.shutdown = info.getFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
 
         if (head)
         {
             if (lastContent)
             {
-                Generator.Result headersResult = generateResponseHeaders(info, Callback.Adapter.INSTANCE);
+                Generator.Result headersResult = generateResponseHeaders(info, Callback.NOOP);
                 Generator.Result contentResult = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback);
                 flusher.flush(headersResult, contentResult);
             }
@@ -69,7 +115,7 @@
         }
         else
         {
-            Generator.Result headersResult = generateResponseHeaders(info, Callback.Adapter.INSTANCE);
+            Generator.Result headersResult = generateResponseHeaders(info, Callback.NOOP);
             Generator.Result contentResult = generateResponseContent(content, lastContent, callback);
             flusher.flush(headersResult, contentResult);
         }
@@ -78,35 +124,9 @@
             flusher.shutdown();
     }
 
-    @Override
-    public void send(ByteBuffer content, boolean lastContent, Callback callback)
+    protected Generator.Result generateResponseHeaders(MetaData.Response info, Callback callback)
     {
-        if (head)
-        {
-            if (lastContent)
-            {
-                Generator.Result result = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback);
-                flusher.flush(result);
-            }
-            else
-            {
-                // Skip content generation
-                callback.succeeded();
-            }
-        }
-        else
-        {
-            Generator.Result result = generateResponseContent(content, lastContent, callback);
-            flusher.flush(result);
-        }
-
-        if (lastContent && shutdown)
-            flusher.shutdown();
-    }
-
-    protected Generator.Result generateResponseHeaders(HttpGenerator.ResponseInfo info, Callback callback)
-    {
-        return generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), info.getHttpFields(), callback);
+        return generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), info.getFields(), callback);
     }
 
     protected Generator.Result generateResponseContent(ByteBuffer buffer, boolean lastContent, Callback callback)
@@ -115,13 +135,13 @@
     }
 
     @Override
-    public void abort()
+    public void abort(Throwable failure)
     {
         aborted = true;
     }
 
     @Override
-    public void completed()
+    public void onCompleted()
     {
     }
 }
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java
index 3e3a018..19e5701 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java
@@ -29,9 +29,9 @@
 import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.server.ByteBufferQueuedHttpInput;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpInput;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
@@ -82,11 +82,13 @@
                 }
                 else if (read == 0)
                 {
+                    bufferPool.release(buffer);
                     fillInterested();
                     break;
                 }
                 else
                 {
+                    bufferPool.release(buffer);
                     shutdown();
                     break;
                 }
@@ -96,11 +98,8 @@
         {
             if (LOG.isDebugEnabled())
                 LOG.debug(x);
-            // TODO: fail and close ?
-        }
-        finally
-        {
             bufferPool.release(buffer);
+            // TODO: fail and close ?
         }
     }
 
@@ -122,8 +121,7 @@
         {
             // TODO: handle flags
             HttpChannelOverFCGI channel = new HttpChannelOverFCGI(connector, configuration, getEndPoint(),
-                    new HttpTransportOverFCGI(connector.getByteBufferPool(), flusher, request, sendStatus200),
-                    new ByteBufferQueuedHttpInput());
+                    new HttpTransportOverFCGI(connector.getByteBufferPool(), flusher, request, sendStatus200));
             HttpChannelOverFCGI existing = channels.putIfAbsent(request, channel);
             if (existing != null)
                 throw new IllegalStateException();
@@ -149,8 +147,8 @@
                 LOG.debug("Request {} headers on {}", request, channel);
             if (channel != null)
             {
-                if (channel.headerComplete())
-                    channel.dispatch();
+                channel.onRequest();
+                channel.dispatch();
             }
         }
 
@@ -162,8 +160,9 @@
                 LOG.debug("Request {} {} content {} on {}", request, stream, buffer, channel);
             if (channel != null)
             {
-                if (channel.content(buffer))
-                    channel.dispatch();
+                ByteBuffer copy = ByteBuffer.allocate(buffer.remaining());
+                copy.put(buffer).flip();
+                channel.onContent(new HttpInput.Content(copy));
             }
             return false;
         }
@@ -176,8 +175,8 @@
                 LOG.debug("Request {} end on {}", request, channel);
             if (channel != null)
             {
-                if (channel.messageComplete())
-                    channel.dispatch();
+                channel.onContentComplete();
+                channel.onRequestComplete();
             }
         }
 
@@ -189,7 +188,7 @@
                 LOG.debug("Request {} failure on {}: {}", request, channel, failure);
             if (channel != null)
             {
-                channel.badMessage(400, failure.toString());
+                channel.onBadMessage(400, failure.toString());
             }
         }
     }
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
index a2daed4..b2cc9ff 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
@@ -19,30 +19,35 @@
 package org.eclipse.jetty.fcgi.server.proxy;
 
 import java.net.URI;
+import java.util.List;
+import java.util.TreeMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.fcgi.FCGI;
 import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
+import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpScheme;
 import org.eclipse.jetty.proxy.AsyncProxyServlet;
-import org.eclipse.jetty.proxy.ProxyServlet;
 
 /**
- * Specific implementation of {@link ProxyServlet.Transparent} for FastCGI.
- * <p />
+ * Specific implementation of {@link org.eclipse.jetty.proxy.AsyncProxyServlet.Transparent} for FastCGI.
+ * <p>
  * This servlet accepts a HTTP request and transforms it into a FastCGI request
  * that is sent to the FastCGI server specified in the <code>proxyTo</code>
  * init-param.
- * <p />
+ * <p>
  * This servlet accepts two additional init-params:
  * <ul>
  *     <li><code>scriptRoot</code>, mandatory, that must be set to the directory where
@@ -65,6 +70,8 @@
 {
     public static final String SCRIPT_ROOT_INIT_PARAM = "scriptRoot";
     public static final String SCRIPT_PATTERN_INIT_PARAM = "scriptPattern";
+    public static final String ORIGINAL_URI_ATTRIBUTE_INIT_PARAM = "originalURIAttribute";
+    public static final String ORIGINAL_QUERY_ATTRIBUTE_INIT_PARAM = "originalQueryAttribute";
     public static final String FASTCGI_HTTPS_INIT_PARAM = "fastCGI.HTTPS";
 
     private static final String REMOTE_ADDR_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".remoteAddr";
@@ -74,8 +81,11 @@
     private static final String SERVER_PORT_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".serverPort";
     private static final String SCHEME_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".scheme";
     private static final String REQUEST_URI_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".requestURI";
+    private static final String REQUEST_QUERY_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".requestQuery";
 
     private Pattern scriptPattern;
+    private String originalURIAttribute;
+    private String originalQueryAttribute;
     private boolean fcgiHTTPS;
 
     @Override
@@ -88,6 +98,9 @@
             value = "(.+?\\.php)";
         scriptPattern = Pattern.compile(value);
 
+        originalURIAttribute = getInitParameter(ORIGINAL_URI_ATTRIBUTE_INIT_PARAM);
+        originalQueryAttribute = getInitParameter(ORIGINAL_QUERY_ATTRIBUTE_INIT_PARAM);
+
         fcgiHTTPS = Boolean.parseBoolean(getInitParameter(FASTCGI_HTTPS_INIT_PARAM));
     }
 
@@ -102,33 +115,78 @@
     }
 
     @Override
-    protected void customizeProxyRequest(Request proxyRequest, HttpServletRequest request)
+    protected void sendProxyRequest(HttpServletRequest request, HttpServletResponse proxyResponse, Request proxyRequest)
     {
         proxyRequest.attribute(REMOTE_ADDR_ATTRIBUTE, request.getRemoteAddr());
         proxyRequest.attribute(REMOTE_PORT_ATTRIBUTE, String.valueOf(request.getRemotePort()));
         proxyRequest.attribute(SERVER_NAME_ATTRIBUTE, request.getServerName());
         proxyRequest.attribute(SERVER_ADDR_ATTRIBUTE, request.getLocalAddr());
         proxyRequest.attribute(SERVER_PORT_ATTRIBUTE, String.valueOf(request.getLocalPort()));
-
         proxyRequest.attribute(SCHEME_ATTRIBUTE, request.getScheme());
 
-        // If we are forwarded or included, retain the original request URI.
-        String originalPath = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
-        String originalQuery = (String)request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING);
-        if (originalPath == null)
+        // Has the original URI been rewritten ?
+        String originalURI = null;
+        String originalQuery = null;
+        if (originalURIAttribute != null)
+            originalURI = (String)request.getAttribute(originalURIAttribute);
+        if (originalURI != null && originalQueryAttribute != null)
         {
-            originalPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
-            originalQuery = (String)request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING);
-        }
-        if (originalPath != null)
-        {
-            String originalURI = originalPath;
+            originalQuery = (String)request.getAttribute(originalQueryAttribute);
             if (originalQuery != null)
                 originalURI += "?" + originalQuery;
-            proxyRequest.attribute(REQUEST_URI_ATTRIBUTE, originalURI);
         }
 
-        super.customizeProxyRequest(proxyRequest, request);
+        if (originalURI == null)
+        {
+            // If we are forwarded or included, retain the original request URI.
+            String originalPath = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
+            originalQuery = (String)request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING);
+            if (originalPath == null)
+            {
+                originalPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
+                originalQuery = (String)request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING);
+            }
+            if (originalPath != null)
+            {
+                originalURI = originalPath;
+                if (originalQuery != null)
+                    originalURI += "?" + originalQuery;
+            }
+        }
+
+        if (originalURI != null)
+            proxyRequest.attribute(REQUEST_URI_ATTRIBUTE, originalURI);
+        if (originalQuery != null)
+            proxyRequest.attribute(REQUEST_QUERY_ATTRIBUTE, originalQuery);
+
+        // If the Host header is missing, add it.
+        if (!proxyRequest.getHeaders().containsKey(HttpHeader.HOST.asString()))
+        {
+            String host = request.getServerName();
+            int port = request.getServerPort();
+            if (!getHttpClient().isDefaultPort(request.getScheme(), port))
+                host += ":" + port;
+            proxyRequest.header(HttpHeader.HOST, host);
+            proxyRequest.header(HttpHeader.X_FORWARDED_HOST, host);
+        }
+
+        // PHP does not like multiple Cookie headers, coalesce into one.
+        List<String> cookies = proxyRequest.getHeaders().getValuesList(HttpHeader.COOKIE);
+        if (cookies.size() > 1)
+        {
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < cookies.size(); ++i)
+            {
+                if (i > 0)
+                    builder.append("; ");
+                String cookie = cookies.get(i);
+                builder.append(cookie);
+            }
+            proxyRequest.header(HttpHeader.COOKIE, null);
+            proxyRequest.header(HttpHeader.COOKIE, builder.toString());
+        }
+
+        super.sendProxyRequest(request, proxyResponse, proxyRequest);
     }
 
     protected void customizeFastCGIHeaders(Request proxyRequest, HttpFields fastCGIHeaders)
@@ -145,8 +203,8 @@
             fastCGIHeaders.put(FCGI.Headers.HTTPS, "on");
 
         URI proxyRequestURI = proxyRequest.getURI();
-        String rawPath = proxyRequestURI.getRawPath();
-        String rawQuery = proxyRequestURI.getRawQuery();
+        String rawPath = proxyRequestURI == null ? proxyRequest.getPath() : proxyRequestURI.getRawPath();
+        String rawQuery = proxyRequestURI == null ? null : proxyRequestURI.getRawQuery();
 
         String requestURI = (String)proxyRequest.getAttributes().get(REQUEST_URI_ATTRIBUTE);
         if (requestURI == null)
@@ -157,6 +215,10 @@
         }
         fastCGIHeaders.put(FCGI.Headers.REQUEST_URI, requestURI);
 
+        String requestQuery = (String)proxyRequest.getAttributes().get(REQUEST_QUERY_ATTRIBUTE);
+        if (requestQuery != null)
+            fastCGIHeaders.put(FCGI.Headers.QUERY_STRING, requestQuery);
+
         String scriptName = rawPath;
         Matcher matcher = scriptPattern.matcher(rawPath);
         if (matcher.matches())
@@ -186,6 +248,16 @@
         {
             super.customize(request, fastCGIHeaders);
             customizeFastCGIHeaders(request, fastCGIHeaders);
+            if (_log.isDebugEnabled())
+            {
+                TreeMap<String, String> fcgi = new TreeMap<>();
+                for (HttpField field : fastCGIHeaders)
+                    fcgi.put(field.getName(), field.getValue());
+                String eol = System.lineSeparator();
+                _log.debug("FastCGI variables{}{}", eol, fcgi.entrySet().stream()
+                        .map(entry -> String.format("%s: %s", entry.getKey(), entry.getValue()))
+                        .collect(Collectors.joining(eol)));
+            }
         }
     }
 }
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java
index 4ec36a1..2ce1c84 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java
@@ -24,6 +24,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
@@ -36,11 +37,11 @@
 
 /**
  * Inspired by nginx's try_files functionality.
- * <p />
+ * <p>
  * This filter accepts the <code>files</code> init-param as a list of space-separated
  * file URIs. The special token <code>$path</code> represents the current request URL's
  * path (the portion after the context path).
- * <p />
+ * <p>
  * Typical example of how this filter can be configured is the following:
  * <pre>
  * &lt;filter&gt;
@@ -58,7 +59,7 @@
  * failing that it will forward the request to <code>index.php?p=/path/to/resource.ext</code>.
  * The last file URI specified in the list is therefore the "fallback" to which the request
  * is forwarded to in case no previous files can be found.
- * <p />
+ * <p>
  * The files are resolved using {@link ServletContext#getResource(String)} to make sure
  * that only files visible to the application are served.
  *
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java
index 54cf987..b9a8346 100644
--- a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java
+++ b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java
@@ -20,7 +20,7 @@
 
 import java.util.concurrent.atomic.AtomicLong;
 
-import org.eclipse.jetty.client.ConnectionPool;
+import org.eclipse.jetty.client.DuplexConnectionPool;
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.HttpDestination;
 import org.eclipse.jetty.client.LeakTrackingConnectionPool;
@@ -37,10 +37,10 @@
 import org.eclipse.jetty.toolchain.test.TestTracker;
 import org.eclipse.jetty.util.LeakDetector;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Rule;
 
-import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertThat;
 
 public abstract class AbstractHttpClientServerTest
@@ -71,7 +71,7 @@
 
         QueuedThreadPool executor = new QueuedThreadPool();
         executor.setName(executor.getName() + "-client");
-        
+
         client = new HttpClient(new HttpClientTransportOverFCGI(1, false, "")
         {
             @Override
@@ -80,7 +80,7 @@
                 return new HttpDestinationOverFCGI(client, origin)
                 {
                     @Override
-                    protected ConnectionPool newConnectionPool(HttpClient client)
+                    protected DuplexConnectionPool newConnectionPool(HttpClient client)
                     {
                         return new LeakTrackingConnectionPool(this, client.getMaxConnectionsPerDestination(), this)
                         {
@@ -105,15 +105,15 @@
     {
         System.gc();
 
-        assertThat("Server BufferPool - leaked acquires", serverBufferPool.getLeakedAcquires(), is(0L));
-        assertThat("Server BufferPool - leaked releases", serverBufferPool.getLeakedReleases(), is(0L));
-        assertThat("Server BufferPool - unreleased", serverBufferPool.getLeakedResources(), is(0L));
-        
-        assertThat("Client BufferPool - leaked acquires", clientBufferPool.getLeakedAcquires(), is(0L));
-        assertThat("Client BufferPool - leaked releases", clientBufferPool.getLeakedReleases(), is(0L));
-        assertThat("Client BufferPool - unreleased", clientBufferPool.getLeakedResources(), is(0L));
-        
-        assertThat("Connection Leaks", connectionLeaks.get(), is(0L));
+        assertThat("Server BufferPool - leaked acquires", serverBufferPool.getLeakedAcquires(), Matchers.is(0L));
+        assertThat("Server BufferPool - leaked releases", serverBufferPool.getLeakedReleases(), Matchers.is(0L));
+        assertThat("Server BufferPool - unreleased", serverBufferPool.getLeakedResources(), Matchers.is(0L));
+
+        assertThat("Client BufferPool - leaked acquires", clientBufferPool.getLeakedAcquires(), Matchers.is(0L));
+        assertThat("Client BufferPool - leaked releases", clientBufferPool.getLeakedReleases(), Matchers.is(0L));
+        assertThat("Client BufferPool - unreleased", clientBufferPool.getLeakedResources(), Matchers.is(0L));
+
+        assertThat("Connection Leaks", connectionLeaks.get(), Matchers.is(0L));
 
         if (client != null)
             client.stop();
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/EmptyServerHandler.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/EmptyServerHandler.java
index d9d5d47..2161f46 100644
--- a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/EmptyServerHandler.java
+++ b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/EmptyServerHandler.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.fcgi.server;
 
 import java.io.IOException;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java
index 73c364b..554f655 100644
--- a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java
+++ b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java
@@ -32,6 +32,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.zip.GZIPOutputStream;
+
 import javax.servlet.ServletException;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
@@ -49,6 +50,7 @@
 import org.eclipse.jetty.toolchain.test.IO;
 import org.eclipse.jetty.toolchain.test.annotation.Slow;
 import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -95,6 +97,35 @@
     }
 
     @Test
+    public void testGETResponseWithBigContent() throws Exception
+    {
+        final byte[] data = new byte[16 * 1024 * 1024];
+        new Random().nextBytes(data);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                // Setting the Content-Length triggers the HTTP
+                // content mode for response content parsing,
+                // otherwise the RAW content mode is used.
+                response.setContentLength(data.length);
+                response.getOutputStream().write(data);
+                baseRequest.setHandled(true);
+            }
+        });
+
+        Request request = client.newRequest(scheme + "://localhost:" + connector.getLocalPort());
+        FutureResponseListener listener = new FutureResponseListener(request, data.length);
+        request.send(listener);
+        ContentResponse response = listener.get(15, TimeUnit.SECONDS);
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatus());
+        byte[] content = response.getContent();
+        Assert.assertArrayEquals(data, content);
+    }
+
+    @Test
     public void testGETWithParametersResponseWithContent() throws Exception
     {
         final String paramName1 = "a";
@@ -419,7 +450,7 @@
         Assert.assertNotNull(response);
         Assert.assertEquals(200, response.getStatus());
     }
-    
+
     @Test
     public void testConnectionIdleTimeout() throws Exception
     {
@@ -573,7 +604,7 @@
             }
         });
 
-        try
+        try (StacklessLogging stackless = new StacklessLogging(org.eclipse.jetty.server.HttpChannel.class))
         {
             client.newRequest("localhost", connector.getLocalPort())
                     .scheme(scheme)
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/DrupalHTTP2FastCGIProxyServer.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/DrupalHTTP2FastCGIProxyServer.java
new file mode 100644
index 0000000..7237adc
--- /dev/null
+++ b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/DrupalHTTP2FastCGIProxyServer.java
@@ -0,0 +1,93 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.fcgi.server.proxy;
+
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class DrupalHTTP2FastCGIProxyServer
+{
+    public static void main(String[] args) throws Exception
+    {
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setEndpointIdentificationAlgorithm("");
+        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
+        sslContextFactory.setTrustStorePassword("storepwd");
+        sslContextFactory.setCipherComparator(new HTTP2Cipher.CipherComparator());
+
+        Server server = new Server();
+
+        // HTTP(S) Configuration
+        HttpConfiguration config = new HttpConfiguration();
+        HttpConfiguration https_config = new HttpConfiguration(config);
+        https_config.addCustomizer(new SecureRequestCustomizer());
+        
+        // HTTP2 factory
+        HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(https_config);
+        ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+        alpn.setDefaultProtocol(h2.getProtocol());
+        
+        // SSL Factory
+        SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory,alpn.getProtocol());
+        
+        // HTTP2 Connector
+        ServerConnector http2Connector = 
+            new ServerConnector(server,ssl,alpn,h2,new HttpConnectionFactory(https_config));
+        http2Connector.setPort(8443);
+        http2Connector.setIdleTimeout(15000);
+        server.addConnector(http2Connector);
+
+        // Drupal seems to only work on the root context,
+        // at least out of the box without additional plugins
+
+        String root = "/home/simon/programs/drupal-7.23";
+
+        ServletContextHandler context = new ServletContextHandler(server, "/");
+        context.setResourceBase(root);
+        context.setWelcomeFiles(new String[]{"index.php"});
+
+        // Serve static resources
+        ServletHolder defaultServlet = new ServletHolder(DefaultServlet.class);
+        defaultServlet.setName("default");
+        context.addServlet(defaultServlet, "/");
+
+        // FastCGI
+        ServletHolder fcgiServlet = new ServletHolder(FastCGIProxyServlet.class);
+        fcgiServlet.setInitParameter(FastCGIProxyServlet.SCRIPT_ROOT_INIT_PARAM, root);
+        fcgiServlet.setInitParameter("proxyTo", "http://localhost:9000");
+        fcgiServlet.setInitParameter("prefix", "/");
+        fcgiServlet.setInitParameter(FastCGIProxyServlet.SCRIPT_PATTERN_INIT_PARAM, "(.+\\.php)");
+        context.addServlet(fcgiServlet, "*.php");
+
+        server.start();
+    }
+}
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/DrupalSPDYFastCGIProxyServer.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/DrupalSPDYFastCGIProxyServer.java
deleted file mode 100644
index 75ce29d..0000000
--- a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/DrupalSPDYFastCGIProxyServer.java
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.fcgi.server.proxy;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.servlet.DefaultServlet;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnector;
-import org.eclipse.jetty.spdy.server.http.PushStrategy;
-import org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-
-public class DrupalSPDYFastCGIProxyServer
-{
-    public static void main(String[] args) throws Exception
-    {
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.setEndpointIdentificationAlgorithm("");
-        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
-        sslContextFactory.setKeyStorePassword("storepwd");
-        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
-        sslContextFactory.setTrustStorePassword("storepwd");
-
-        Server server = new Server();
-
-        Map<Short, PushStrategy> pushStrategies = new HashMap<>();
-        pushStrategies.put(SPDY.V3, new ReferrerPushStrategy());
-        HTTPSPDYServerConnector connector = new HTTPSPDYServerConnector(server, sslContextFactory, pushStrategies);
-        connector.setPort(8443);
-        server.addConnector(connector);
-
-        // Drupal seems to only work on the root context,
-        // at least out of the box without additional plugins
-
-        String root = "/home/simon/programs/drupal-7.23";
-
-        ServletContextHandler context = new ServletContextHandler(server, "/");
-        context.setResourceBase(root);
-        context.setWelcomeFiles(new String[]{"index.php"});
-
-        // Serve static resources
-        ServletHolder defaultServlet = new ServletHolder(DefaultServlet.class);
-        defaultServlet.setName("default");
-        context.addServlet(defaultServlet, "/");
-
-        // FastCGI
-        ServletHolder fcgiServlet = new ServletHolder(FastCGIProxyServlet.class);
-        fcgiServlet.setInitParameter(FastCGIProxyServlet.SCRIPT_ROOT_INIT_PARAM, root);
-        fcgiServlet.setInitParameter("proxyTo", "http://localhost:9000");
-        fcgiServlet.setInitParameter("prefix", "/");
-        fcgiServlet.setInitParameter(FastCGIProxyServlet.SCRIPT_PATTERN_INIT_PARAM, "(.+\\.php)");
-        context.addServlet(fcgiServlet, "*.php");
-
-        server.start();
-    }
-}
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java
index df2e067..6aca3a6 100644
--- a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java
+++ b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java
@@ -19,12 +19,9 @@
 package org.eclipse.jetty.fcgi.server.proxy;
 
 import java.io.IOException;
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Random;
 import java.util.concurrent.TimeUnit;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -33,15 +30,18 @@
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.api.Response;
 import org.eclipse.jetty.client.util.FutureResponseListener;
+import org.eclipse.jetty.fcgi.FCGI;
 import org.eclipse.jetty.fcgi.server.ServerFCGIConnectionFactory;
+import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Test;
@@ -52,15 +52,16 @@
 public class FastCGIProxyServletTest
 {
     @Parameterized.Parameters
-    public static Collection<Object[]> parameters()
+    public static Object[] parameters()
     {
-        return Arrays.asList(new Object[]{true}, new Object[]{false});
+        return new Object[]{true, false};
     }
 
     private final boolean sendStatus200;
     private Server server;
     private ServerConnector httpConnector;
     private ServerConnector fcgiConnector;
+    private ServletContextHandler context;
     private HttpClient client;
 
     public FastCGIProxyServletTest(boolean sendStatus200)
@@ -70,7 +71,9 @@
 
     public void prepare(HttpServlet servlet) throws Exception
     {
-        server = new Server();
+        QueuedThreadPool serverThreads = new QueuedThreadPool();
+        serverThreads.setName("server");
+        server = new Server(serverThreads);
         httpConnector = new ServerConnector(server);
         server.addConnector(httpConnector);
 
@@ -78,26 +81,30 @@
         server.addConnector(fcgiConnector);
 
         final String contextPath = "/";
-        ServletContextHandler context = new ServletContextHandler(server, contextPath);
+        context = new ServletContextHandler(server, contextPath);
 
         final String servletPath = "/script";
         FastCGIProxyServlet fcgiServlet = new FastCGIProxyServlet()
         {
             @Override
-            protected URI rewriteURI(HttpServletRequest request)
+            protected String rewriteTarget(HttpServletRequest request)
             {
-                return URI.create("http://localhost:" + fcgiConnector.getLocalPort() + servletPath + request.getServletPath());
+                return "http://localhost:" + fcgiConnector.getLocalPort() + servletPath + request.getServletPath();
             }
         };
         ServletHolder fcgiServletHolder = new ServletHolder(fcgiServlet);
-        context.addServlet(fcgiServletHolder, "*.php");
+        fcgiServletHolder.setName("fcgi");
         fcgiServletHolder.setInitParameter(FastCGIProxyServlet.SCRIPT_ROOT_INIT_PARAM, "/scriptRoot");
         fcgiServletHolder.setInitParameter("proxyTo", "http://localhost");
         fcgiServletHolder.setInitParameter(FastCGIProxyServlet.SCRIPT_PATTERN_INIT_PARAM, "(.+?\\.php)");
+        context.addServlet(fcgiServletHolder, "*.php");
 
         context.addServlet(new ServletHolder(servlet), servletPath + "/*");
 
+        QueuedThreadPool clientThreads = new QueuedThreadPool();
+        clientThreads.setName("client");
         client = new HttpClient();
+        client.setExecutor(clientThreads);
         server.addBean(client);
 
         server.start();
@@ -136,30 +143,26 @@
         prepare(new HttpServlet()
         {
             @Override
-            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
             {
-                Assert.assertTrue(req.getRequestURI().endsWith(path));
-                resp.setContentLength(data.length);
-                resp.getOutputStream().write(data);
+                Assert.assertTrue(request.getRequestURI().endsWith(path));
+                response.setContentLength(data.length);
+                response.getOutputStream().write(data);
             }
         });
 
         Request request = client.newRequest("localhost", httpConnector.getLocalPort())
-                .onResponseContentAsync(new Response.AsyncContentListener()
+                .onResponseContentAsync((response, content, callback) ->
                 {
-                    @Override
-                    public void onContent(Response response, ByteBuffer content, Callback callback)
+                    try
                     {
-                        try
-                        {
-                            if (delay > 0)
-                                TimeUnit.MILLISECONDS.sleep(delay);
-                            callback.succeeded();
-                        }
-                        catch (InterruptedException x)
-                        {
-                            callback.failed(x);
-                        }
+                        if (delay > 0)
+                            TimeUnit.MILLISECONDS.sleep(delay);
+                        callback.succeeded();
+                    }
+                    catch (InterruptedException x)
+                    {
+                        callback.failed(x);
                     }
                 })
                 .path(path);
@@ -171,4 +174,48 @@
         Assert.assertEquals(200, response.getStatus());
         Assert.assertArrayEquals(data, response.getContent());
     }
+
+    @Test
+    public void testURIRewrite() throws Exception
+    {
+        String originalPath = "/original/index.php";
+        String originalQuery = "foo=bar";
+        String remotePath = "/remote/index.php";
+        prepare(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                Assert.assertThat((String)request.getAttribute(FCGI.Headers.REQUEST_URI), Matchers.startsWith(originalPath));
+                Assert.assertEquals(originalQuery, request.getAttribute(FCGI.Headers.QUERY_STRING));
+                Assert.assertThat(request.getRequestURI(), Matchers.endsWith(remotePath));
+            }
+        });
+        context.stop();
+        String pathAttribute = "_path_attribute";
+        String queryAttribute = "_query_attribute";
+        ServletHolder fcgi = context.getServletHandler().getServlet("fcgi");
+        fcgi.setInitParameter(FastCGIProxyServlet.ORIGINAL_URI_ATTRIBUTE_INIT_PARAM, pathAttribute);
+        fcgi.setInitParameter(FastCGIProxyServlet.ORIGINAL_QUERY_ATTRIBUTE_INIT_PARAM, queryAttribute);
+        context.insertHandler(new HandlerWrapper()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                if (target.startsWith("/remote/"))
+                {
+                    request.setAttribute(pathAttribute, originalPath);
+                    request.setAttribute(queryAttribute, originalQuery);
+                }
+                super.handle(target, baseRequest, request, response);
+            }
+        });
+        context.start();
+
+        ContentResponse response = client.newRequest("localhost", httpConnector.getLocalPort())
+                .path(remotePath)
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
 }
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilterTest.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilterTest.java
index ea8025c..85c9fc0 100644
--- a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilterTest.java
+++ b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilterTest.java
@@ -20,6 +20,7 @@
 
 import java.io.IOException;
 import java.util.EnumSet;
+
 import javax.servlet.DispatcherType;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/WordPressHTTP2FastCGIProxyServer.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/WordPressHTTP2FastCGIProxyServer.java
new file mode 100644
index 0000000..31229b2
--- /dev/null
+++ b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/WordPressHTTP2FastCGIProxyServer.java
@@ -0,0 +1,99 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.fcgi.server.proxy;
+
+import java.util.EnumSet;
+
+import javax.servlet.DispatcherType;
+
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class WordPressHTTP2FastCGIProxyServer
+{
+    public static void main(String[] args) throws Exception
+    {
+        int tlsPort = 8443;
+
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setEndpointIdentificationAlgorithm("");
+        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
+        sslContextFactory.setTrustStorePassword("storepwd");
+        sslContextFactory.setCipherComparator(new HTTP2Cipher.CipherComparator());
+
+        Server server = new Server();
+
+        // HTTP(S) Configuration
+        HttpConfiguration config = new HttpConfiguration();
+        HttpConfiguration https_config = new HttpConfiguration(config);
+        https_config.addCustomizer(new SecureRequestCustomizer());
+        
+        // HTTP2 factory
+        HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(https_config);
+        ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+        alpn.setDefaultProtocol(h2.getProtocol());
+        
+        // SSL Factory
+        SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory,alpn.getProtocol());
+        
+        // HTTP2 Connector
+        ServerConnector http2Connector = 
+            new ServerConnector(server,ssl,alpn,h2,new HttpConnectionFactory(https_config));
+        http2Connector.setPort(tlsPort);
+        http2Connector.setIdleTimeout(15000);
+        server.addConnector(http2Connector);
+
+        String root = "/home/simon/programs/wordpress-3.7.1";
+
+        ServletContextHandler context = new ServletContextHandler(server, "/wp");
+        context.setResourceBase(root);
+        context.setWelcomeFiles(new String[]{"index.php"});
+
+        // Serve static resources
+        ServletHolder defaultServlet = new ServletHolder("default", DefaultServlet.class);
+        context.addServlet(defaultServlet, "/");
+
+        FilterHolder tryFilesFilter = context.addFilter(TryFilesFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
+//        tryFilesFilter.setInitParameter(TryFilesFilter.FILES_INIT_PARAM, "$path $path/index.php"); // Permalink /?p=123
+        tryFilesFilter.setInitParameter(TryFilesFilter.FILES_INIT_PARAM, "$path /index.php?p=$path"); // Permalink /%year%/%monthnum%/%postname%
+
+        // FastCGI
+        ServletHolder fcgiServlet = context.addServlet(FastCGIProxyServlet.class, "*.php");
+        fcgiServlet.setInitParameter(FastCGIProxyServlet.SCRIPT_ROOT_INIT_PARAM, root);
+        fcgiServlet.setInitParameter("proxyTo", "http://localhost:9000");
+        fcgiServlet.setInitParameter("prefix", "/");
+        fcgiServlet.setInitParameter(FastCGIProxyServlet.SCRIPT_PATTERN_INIT_PARAM, "(.+?\\.php)");
+
+        server.start();
+    }
+}
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/WordPressSPDYFastCGIProxyServer.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/WordPressSPDYFastCGIProxyServer.java
deleted file mode 100644
index 4e10978..0000000
--- a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/WordPressSPDYFastCGIProxyServer.java
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.fcgi.server.proxy;
-
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-import javax.servlet.DispatcherType;
-
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.servlet.DefaultServlet;
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnector;
-import org.eclipse.jetty.spdy.server.http.PushStrategy;
-import org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-
-public class WordPressSPDYFastCGIProxyServer
-{
-    public static void main(String[] args) throws Exception
-    {
-        int port = 8080;
-        int tlsPort = 8443;
-
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.setEndpointIdentificationAlgorithm("");
-        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
-        sslContextFactory.setKeyStorePassword("storepwd");
-        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
-        sslContextFactory.setTrustStorePassword("storepwd");
-
-        Server server = new Server();
-
-        Map<Short, PushStrategy> pushStrategies = new HashMap<>();
-        pushStrategies.put(SPDY.V3, new ReferrerPushStrategy());
-        HTTPSPDYServerConnector tlsConnector = new HTTPSPDYServerConnector(server, sslContextFactory, pushStrategies);
-        tlsConnector.setPort(tlsPort);
-        server.addConnector(tlsConnector);
-        HTTPSPDYServerConnector connector = new HTTPSPDYServerConnector(server, null, pushStrategies);
-        connector.setPort(port);
-        server.addConnector(connector);
-
-        String root = "/home/simon/programs/wordpress-3.7.1";
-
-        ServletContextHandler context = new ServletContextHandler(server, "/wp");
-        context.setResourceBase(root);
-        context.setWelcomeFiles(new String[]{"index.php"});
-
-        // Serve static resources
-        ServletHolder defaultServlet = new ServletHolder("default", DefaultServlet.class);
-        context.addServlet(defaultServlet, "/");
-
-        FilterHolder tryFilesFilter = context.addFilter(TryFilesFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
-//        tryFilesFilter.setInitParameter(TryFilesFilter.FILES_INIT_PARAM, "$path $path/index.php"); // Permalink /?p=123
-        tryFilesFilter.setInitParameter(TryFilesFilter.FILES_INIT_PARAM, "$path /index.php?p=$path"); // Permalink /%year%/%monthnum%/%postname%
-
-        // FastCGI
-        ServletHolder fcgiServlet = context.addServlet(FastCGIProxyServlet.class, "*.php");
-        fcgiServlet.setInitParameter(FastCGIProxyServlet.SCRIPT_ROOT_INIT_PARAM, root);
-        fcgiServlet.setInitParameter("proxyTo", "http://localhost:9000");
-        fcgiServlet.setInitParameter("prefix", "/");
-        fcgiServlet.setInitParameter(FastCGIProxyServlet.SCRIPT_PATTERN_INIT_PARAM, "(.+?\\.php)");
-
-        server.start();
-    }
-}
diff --git a/jetty-fcgi/pom.xml b/jetty-fcgi/pom.xml
index e0a5bc0..f437e0b 100644
--- a/jetty-fcgi/pom.xml
+++ b/jetty-fcgi/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty</groupId>
         <artifactId>jetty-project</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
+        <version>9.3.19-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-gcloud/jetty-gcloud-memcached-session-manager/pom.xml b/jetty-gcloud/jetty-gcloud-memcached-session-manager/pom.xml
new file mode 100644
index 0000000..ca61892
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-memcached-session-manager/pom.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.gcloud</groupId>
+    <artifactId>gcloud-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>jetty-gcloud-memcached-session-manager</artifactId>
+  <name>Jetty :: GCloud :: Memcached Session Manager</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty.gcloud</groupId>
+      <artifactId>jetty-gcloud-session-manager</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.googlecode.xmemcached</groupId>
+      <artifactId>xmemcached</artifactId>
+      <version>2.0.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+ <properties>
+   <bundle-symbolic-name>${project.groupId}.memcached.session</bundle-symbolic-name>
+ </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+          <groupId>org.apache.felix</groupId>
+          <artifactId>maven-bundle-plugin</artifactId>
+          <extensions>true</extensions>
+          <executions>
+              <execution>
+                  <goals>
+                      <goal>manifest</goal>
+                  </goals>
+                  <configuration>
+                      <instructions>
+                          <Export-Package>org.eclipse.jetty.gcloud.memcached.session.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}";</Export-Package>
+                      </instructions>
+                    </configuration>
+                 </execution>
+            </executions>
+      </plugin>
+   </plugins>
+  </build>
+
+</project>
diff --git a/jetty-gcloud/jetty-gcloud-memcached-session-manager/src/main/config/etc/gcloud-memcached-session-context.xml b/jetty-gcloud/jetty-gcloud-memcached-session-manager/src/main/config/etc/gcloud-memcached-session-context.xml
new file mode 100644
index 0000000..b35daa8
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-memcached-session-manager/src/main/config/etc/gcloud-memcached-session-context.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+  <!-- Get a reference to the GCloudSessionIdManager -->
+  <Ref id="Server">
+    <Call id="idMgr" name="getSessionIdManager"/>
+  </Ref>
+
+  <!-- Use the GCloudSessionIdManager to set up the GCloudMemcachedSessionManager -->
+  <Set name="sessionHandler">
+    <New class="org.eclipse.jetty.server.session.SessionHandler">
+      <Arg>
+        <New id="mgr" class="org.eclipse.jetty.gcloud.memcached.session.GCloudMemcachedSessionManager">
+          <Set name="sessionIdManager">
+            <Ref id="idMgr"/>
+          </Set>
+          <Set name="scavengeIntervalSec">600</Set>
+          <!-- uncomment and configure the secs before a memcache entry is evicted
+          <Set name="expirySec">86400</Set>
+           -->
+           <!-- uncomment and configure whether memcached does heartbeats or not
+           <Set name="heartbeats">false</Set>
+            -->
+          <Set name="host"><Env name="MEMCACHE_PORT_11211_TCP_ADDR" default="localhost"/></Set>
+          <Set name="port"><Env name="MEMCACHE_PORT_11211_TCP_PORT" default="11211"/></Set>
+        </New>
+      </Arg>
+    </New>
+  </Set>
+
+</Configure>
diff --git a/jetty-gcloud/jetty-gcloud-memcached-session-manager/src/main/config/etc/jetty-gcloud-memcached-sessions.xml b/jetty-gcloud/jetty-gcloud-memcached-session-manager/src/main/config/etc/jetty-gcloud-memcached-sessions.xml
new file mode 100644
index 0000000..38b81ba
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-memcached-session-manager/src/main/config/etc/jetty-gcloud-memcached-sessions.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <!-- ============================================================================== -->
+  <!-- Configure the Deployer to create a GCloudMemcachedSessionManager for every     -->
+  <!-- webapp that is deployed                                                        -->
+  <!-- ============================================================================== -->
+
+  <Ref id="DeploymentManager">
+    <!-- Add a customize step to the deployment lifecycle -->
+    <Call name="insertLifeCycleNode">
+      <Arg>deployed</Arg>
+      <Arg>starting</Arg>
+      <Arg>customise</Arg>
+    </Call>
+    <Call name="addLifeCycleBinding">
+      <Arg>
+        <New class="org.eclipse.jetty.deploy.bindings.GlobalWebappConfigBinding">
+         <Set name="jettyXml"><Property name="jetty.home" default="."/>/etc/gcloud-memcached-session-context.xml</Set>
+        </New>
+      </Arg>
+    </Call>
+  </Ref>
+
+</Configure>
diff --git a/jetty-gcloud/jetty-gcloud-memcached-session-manager/src/main/config/modules/gcloud-memcached-sessions.mod b/jetty-gcloud/jetty-gcloud-memcached-session-manager/src/main/config/modules/gcloud-memcached-sessions.mod
new file mode 100644
index 0000000..83697c4
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-memcached-session-manager/src/main/config/modules/gcloud-memcached-sessions.mod
@@ -0,0 +1,26 @@
+#
+# Jetty GCloudDatastore with Memcached Session Manager module
+#
+
+[depend]
+ext
+gcloud-session-idmgr
+
+
+[files]
+maven://com.googlecode.xmemcached/xmemcached/2.0.0|lib/xmemcached/xmemcached-2.0.0.jar
+maven://org.slf4j/slf4j-api/1.6.6|lib/ext/slf4j-api-1.6.6.jar
+
+[lib]
+lib/jetty-gcloud-memcached-session-manager-${jetty.version}.jar
+lib/xmemcached/*.jar
+
+
+[xml]
+etc/jetty-gcloud-memcached-sessions.xml
+
+[license]
+Xmemcached is an open source project hosted on Github and released under the Apache 2.0 license.
+https://github.com/killme2008/xmemcached
+http://www.apache.org/licenses/LICENSE-2.0.html
+
diff --git a/jetty-gcloud/jetty-gcloud-memcached-session-manager/src/main/java/org/eclipse/jetty/gcloud/memcached/session/GCloudMemcachedSessionManager.java b/jetty-gcloud/jetty-gcloud-memcached-session-manager/src/main/java/org/eclipse/jetty/gcloud/memcached/session/GCloudMemcachedSessionManager.java
new file mode 100644
index 0000000..5153c02
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-memcached-session-manager/src/main/java/org/eclipse/jetty/gcloud/memcached/session/GCloudMemcachedSessionManager.java
@@ -0,0 +1,367 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.util.Map;
+
+import org.eclipse.jetty.gcloud.session.GCloudSessionManager;
+import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+import com.google.cloud.datastore.Key;
+
+import net.rubyeye.xmemcached.MemcachedClient;
+import net.rubyeye.xmemcached.XMemcachedClient;
+import net.rubyeye.xmemcached.XMemcachedClientBuilder;
+import net.rubyeye.xmemcached.transcoders.SerializingTranscoder;
+
+/**
+ * GCloudMemcachedSessionManager
+ *
+ * Use memcached in front of GCloudDataStore
+ *
+ */
+public class GCloudMemcachedSessionManager extends GCloudSessionManager
+{
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    
+    protected String _host;
+    protected String _port;
+    protected MemcachedClient _client;
+    protected int _expirySec = 0;
+    private boolean _heartbeats = true;
+
+    
+
+    /**
+     * ContextClassloaderSerializingTranscoder
+     *
+     * A xmemcached transcoder that will use the thread context classloader to
+     * resolve classes during object deserialization: necessary for Servlet Spec
+     * classloading order of context classloader first.
+     *  
+     */
+    public class ContextClassloaderSerializingTranscoder extends SerializingTranscoder
+    {
+
+        @Override
+        protected Object deserialize(byte[] in)
+        {
+
+            if (in == null)
+                return null;
+
+            Object rv = null;
+            try (ByteArrayInputStream bis = new ByteArrayInputStream(in);ObjectInputStream is = new ClassLoadingObjectInputStream(bis);)
+            {
+
+                rv = is.readObject();
+            }
+            catch (IOException e)
+            {
+                LOG.warn("Caught IOException decoding " + in.length + " bytes of data", e);
+            }
+            catch (ClassNotFoundException e)
+            {
+                LOG.warn("Caught CNFE decoding " + in.length + " bytes of data", e);
+            }
+            
+            return rv;
+
+        }
+    }
+
+    
+    
+    /**
+     * MemcacheSession
+     *
+     * Needed to make a constructor public.
+     */
+    public class MemcacheSession extends GCloudSessionManager.Session
+    {
+
+        public MemcacheSession(String sessionId, long created, long accessed, long maxInterval)
+        {
+            super(sessionId, created, accessed, maxInterval);
+        }  
+    }
+    
+    /**
+     * Every time a Session is put into the cache one of these objects
+     * is created to copy the data out of the in-memory session, and 
+     * every time an object is read from the cache one of these objects
+     * a fresh Session object is created based on the data held by this
+     * object.
+     */
+    public class SerializableSessionData implements Serializable
+    {
+        /**
+         * 
+         */
+        private static final long serialVersionUID = -7779120106058533486L;
+        String clusterId;
+        String contextPath;
+        String vhost;
+        long accessed;
+        long lastAccessed;
+        long createTime;
+        long cookieSetTime;
+        String lastNode;
+        long expiry;
+        long maxInactive;
+        Map<String, Object> attributes;
+
+
+
+        public SerializableSessionData()
+        {}
+
+
+        public SerializableSessionData(Session s)
+        {
+            clusterId = s.getClusterId();
+            contextPath = s.getContextPath();
+            vhost = s.getVHost();
+            accessed = s.getAccessed();
+            lastAccessed = s.getLastAccessedTime();
+            createTime = s.getCreationTime();
+            cookieSetTime = s.getCookieSetTime();
+            lastNode = s.getLastNode();
+            expiry = s.getExpiry();
+            maxInactive = s.getMaxInactiveInterval();
+            attributes = s.getAttributeMap();
+       }
+       
+        
+        private void writeObject(java.io.ObjectOutputStream out) throws IOException
+        { 
+            out.writeUTF(clusterId); //session id
+            out.writeUTF(contextPath); //context path
+            out.writeUTF(vhost); //first vhost
+
+            out.writeLong(accessed);//accessTime
+            out.writeLong(lastAccessed); //lastAccessTime
+            out.writeLong(createTime); //time created
+            out.writeLong(cookieSetTime);//time cookie was set
+            out.writeUTF(lastNode); //name of last node managing
+
+            out.writeLong(expiry); 
+            out.writeLong(maxInactive);
+            out.writeObject(attributes);
+        }
+
+        private void readObject(java.io.ObjectInputStream ois) throws IOException, ClassNotFoundException
+        {
+            clusterId = ois.readUTF();
+            contextPath = ois.readUTF();
+            vhost = ois.readUTF();
+            accessed = ois.readLong();//accessTime
+            lastAccessed = ois.readLong(); //lastAccessTime
+            createTime = ois.readLong(); //time created
+            cookieSetTime = ois.readLong();//time cookie was set
+            lastNode = ois.readUTF(); //last managing node
+            expiry = ois.readLong(); 
+            maxInactive = ois.readLong();
+            Object o = ois.readObject();
+            attributes = ((Map<String,Object>)o);
+        }
+    }
+
+
+    
+    
+    
+    
+    /**
+     * @return the expiry setting for memcached
+     */
+    public int getExpirySec()
+    {
+        return _expirySec;
+    }
+
+    /**
+     * @param expirySec the time in seconds for an item to remain in memcached
+     */
+    public void setExpirySec(int expirySec)
+    {
+        _expirySec = expirySec;
+    }
+    
+    
+    /**
+     * @param heartbeats if true memcached heartbeats are enabled. Default is true.
+     */
+    public void setHeartbeats (boolean heartbeats)
+    {
+        _heartbeats  = heartbeats;
+    }
+
+    
+    @Override
+    public void doStart() throws Exception
+    {
+        if (StringUtil.isBlank(_host) || StringUtil.isBlank(_port))
+            throw new IllegalStateException("Memcached host and/or port not configured");
+
+        LOG.info("Memcached host {} port {}", _host, _port);
+        
+        XMemcachedClientBuilder builder = new XMemcachedClientBuilder(_host+":"+_port);
+        _client = builder.build();
+        _client.setEnableHeartBeat(_heartbeats);
+        
+
+        _client.setTranscoder(new ContextClassloaderSerializingTranscoder());
+        super.doStart();
+    }
+
+    @Override
+    public void doStop() throws Exception
+    {
+        super.doStop();
+        _client.shutdown();
+        _client = null;
+    }
+
+    @Override
+    protected Session load(Key key) throws Exception
+    {
+        //first try the memcache cache
+        if (LOG.isDebugEnabled()) LOG.debug("Loading key {} from memcached ", key.name());
+        Session session =  loadFromMemcached(key.name());
+        if (session != null)
+            return session;
+
+        //then try gcloudatastore
+        return super.load(key);
+    }
+
+    /**
+     * @param key the key for the memcache item
+     * @return the Session inflated from memcache
+     * @throws Exception
+     */
+    protected Session loadFromMemcached(String key) throws Exception
+    {
+        SerializableSessionData sd = _client.get(key);
+
+        if (sd == null)
+            return null;
+
+        Session session = new MemcacheSession (sd.clusterId, sd.createTime, sd.accessed, sd.maxInactive);
+        session.setLastNode(sd.lastNode);
+        session.setContextPath(sd.contextPath);
+        session.setVHost(sd.vhost);
+        session.setCookieSetTime(sd.cookieSetTime);
+        session.setLastAccessedTime(sd.lastAccessed);
+        session.setLastNode(sd.lastNode);
+        session.setExpiry(sd.expiry);
+        session.addAttributes(sd.attributes);
+        return session;
+    }
+
+
+    @Override
+    protected void save(Session session) throws Exception
+    {
+        //save to gcloud and then memcache
+        super.save(session);        
+        saveToMemcached(session);
+    }
+
+    
+    
+    @Override
+    protected void delete (GCloudSessionManager.Session session)
+    {  
+        Exception memcacheException = null;
+        try
+        {
+            deleteFromMemcached(session);
+        }
+        catch (Exception e)
+        {
+            memcacheException = e;
+        }
+        
+        super.delete(session);
+        if (memcacheException != null)
+            throw new RuntimeException(memcacheException);
+    }
+
+    
+    protected void deleteFromMemcached(Session session) throws Exception
+    {
+        Key gcloudKey = makeKey(session, _context);
+        _client.delete(gcloudKey.name());
+    }
+
+    /**
+     * Store the session into memcached
+     * @param session the Session to be serialized
+     * @throws Exception
+     */
+    protected void saveToMemcached(Session session) throws Exception
+    {
+        Key gcloudKey = makeKey(session, _context);
+        _client.set(gcloudKey.name(), getExpirySec(), new SerializableSessionData(session));
+    }
+
+    /**
+     * @return the host address of the memcached server
+     */
+    public String getHost()
+    {
+        return _host;
+    }
+
+    /**
+     * @param host the host address of the memcached server
+     */
+    public void setHost(String host)
+    {
+        _host = host;
+    }
+
+    /**
+     * @return the port of the memcached server
+     */
+    public String getPort()
+    {
+        return _port;
+    }
+    
+   
+
+    /**
+     * @param port the port of the memcached server
+     */
+    public void setPort(String port)
+    {
+        _port = port;
+    }
+}
diff --git a/jetty-gcloud/jetty-gcloud-session-manager/pom.xml b/jetty-gcloud/jetty-gcloud-session-manager/pom.xml
new file mode 100644
index 0000000..c83ba2c
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-session-manager/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.gcloud</groupId>
+    <artifactId>gcloud-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>jetty-gcloud-session-manager</artifactId>
+  <name>Jetty :: GCloud :: Session Manager</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.cloud</groupId>
+      <artifactId>gcloud-java-datastore</artifactId>
+      <version>${gcloud.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-webapp</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>websocket-servlet</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>websocket-server</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+ <properties>
+   <bundle-symbolic-name>${project.groupId}.session</bundle-symbolic-name>
+ </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+          <groupId>org.apache.felix</groupId>
+          <artifactId>maven-bundle-plugin</artifactId>
+          <extensions>true</extensions>
+          <executions>
+              <execution>
+                  <goals>
+                      <goal>manifest</goal>
+                  </goals>
+                  <configuration>
+                      <instructions>
+                          <Export-Package>org.eclipse.jetty.gcloud.session.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}";</Export-Package>
+                      </instructions>
+                    </configuration>
+                 </execution>
+            </executions>
+      </plugin>
+   </plugins>
+  </build>
+
+</project>
diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/gcloud-session-context.xml b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/gcloud-session-context.xml
new file mode 100644
index 0000000..111c2ca
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/gcloud-session-context.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+  <!-- Get a reference to the GCloudSessionIdManager -->
+  <Ref id="Server">
+    <Call id="idMgr" name="getSessionIdManager"/>
+  </Ref>
+
+  <!-- Use the GCloudSessionIdManager to set up the GCloudSessionManager -->
+  <Set name="sessionHandler">
+    <New class="org.eclipse.jetty.server.session.SessionHandler">
+      <Arg>
+        <New id="mgr" class="org.eclipse.jetty.gcloud.session.GCloudSessionManager">
+          <Set name="sessionIdManager">
+            <Ref id="idMgr"/>
+          </Set>
+          <Set name="scavengeIntervalSec">600</Set>
+        </New>
+      </Arg>
+    </New>
+  </Set>
+
+</Configure>
diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/jetty-gcloud-session-idmgr.xml b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/jetty-gcloud-session-idmgr.xml
new file mode 100644
index 0000000..3ca24fe
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/jetty-gcloud-session-idmgr.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+
+  <!-- ===================================================================== -->
+  <!-- Configure a GCloudSessionIdManager                                    -->
+  <!-- ===================================================================== -->
+  <Set name="sessionIdManager">
+    <New id="idMgr" class="org.eclipse.jetty.gcloud.session.GCloudSessionIdManager">
+      <Arg>
+        <Ref id="Server"/>
+      </Arg>
+      <Set name="workerName"><Property name="jetty.gcloudSession.workerName"><Default>node<Env name="GAE_MODULE_INSTANCE" default="0"/></Default></Property></Set>
+    </New>
+  </Set>
+
+</Configure>
diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml
new file mode 100644
index 0000000..dc0a474
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <!-- ===================================================================== -->
+  <!-- Configure the Deployer to create a GCloudSessionManager for every     -->
+  <!-- webapp that is deployed                                               -->
+  <!-- ===================================================================== -->
+
+  <Ref id="DeploymentManager">
+    <!-- Add a customize step to the deployment lifecycle -->
+    <Call name="insertLifeCycleNode">
+      <Arg>deployed</Arg>
+      <Arg>starting</Arg>
+      <Arg>customise</Arg>
+    </Call>
+    <Call name="addLifeCycleBinding">
+      <Arg>
+        <New class="org.eclipse.jetty.deploy.bindings.GlobalWebappConfigBinding">
+          <Set name="jettyXml"><Property name="jetty.home" default="."/>/etc/gcloud-session-context.xml</Set>
+        </New>
+      </Arg>
+    </Call>
+  </Ref>
+
+</Configure>
diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud-session-idmgr.mod b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud-session-idmgr.mod
new file mode 100644
index 0000000..06f8cd3
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud-session-idmgr.mod
@@ -0,0 +1,63 @@
+#
+# Jetty GCloudDatastore SessionIdManager module
+#
+
+[depend]
+annotations
+webapp
+
+[files]
+maven://com.google.cloud/gcloud-java-datastore/0.2.3|lib/gcloud/gcloud-java-datastore-0.2.3.jar
+maven://com.google.cloud/gcloud-java-core/0.2.3|lib/gcloud/gcloud-java-core-0.2.3.jar
+maven://com.google.auth/google-auth-library-credentials/0.3.1|lib/gcloud/google-auth-library-credentials-0.3.1.jar
+maven://com.google.auth/google-auth-library-oauth2-http/0.3.1|lib/gcloud/google-auth-library-oauth2-http-0.3.1.jar
+maven://com.google.http-client/google-http-client-jackson2/1.19.0|lib/gcloud/google-http-client-jackson2-1.19.0.jar
+maven://com.fasterxml.jackson.core/jackson-core/2.1.3|lib/gcloud/jackson-core-2.1.3.jar
+maven://com.google.http-client/google-http-client/1.21.0|lib/gcloud/google-http-client-1.21.0.jar
+maven://com.google.code.findbugs/jsr305/1.3.9|lib/gcloud/jsr305-1.3.9.jar
+maven://org.apache.httpcomponents/httpclient/4.0.1|lib/gcloud/httpclient-4.0.1.jar
+maven://org.apache.httpcomponents/httpcore/4.0.1|lib/gcloud/httpcore-4.0.1.jar
+maven://commons-logging/commons-logging/1.1.1|lib/gcloud/commons-logging-1.1.1.jar
+maven://commons-codec/commons-codec/1.3|lib/gcloud/commons-codec-1.3.jar
+maven://com.google.oauth-client/google-oauth-client/1.21.0|lib/gcloud/google-oauth-client-1.21.0.jar
+maven://com.google.guava/guava/19.0|lib/gcloud/guava-19.0.jar
+maven://com.google.api-client/google-api-client-appengine/1.21.0|lib/gcloud/google-api-client-appengine-1.21.0.jar
+maven://com.google.oauth-client/google-oauth-client-appengine/1.21.0|lib/gcloud/google-oauth-client-appengine-1.21.0.jar
+maven://com.google.oauth-client/google-oauth-client-servlet/1.21.0|lib/gcloud/google-oauth-client-servlet-1.21.0.jar
+maven://com.google.http-client/google-http-client-jdo/1.21.0|lib/gcloud/google-http-client-jdo-1.21.0.jar
+maven://com.google.api-client/google-api-client-servlet/1.21.0|lib/gcloud/google-api-client-servlet-1.21.0.jar
+maven://javax.jdo/jdo2-api/2.3-eb|lib/gcloud/jdo2-api-2.3-eb.jar
+maven://javax.transaction/transaction-api/1.1|lib/gcloud/transaction-api-1.1.jar
+maven://com.google.http-client/google-http-client-appengine/1.21.0|lib/gcloud/google-http-client-appengine-1.21.0.jar
+maven://com.google.http-client/google-http-client-jackson/1.21.0|lib/gcloud/google-http-client-jackson-1.21.0.jar
+maven://org.codehaus.jackson/jackson-core-asl/1.9.11|lib/gcloud/jackson-core-asl-1.9.11.jar
+maven://joda-time/joda-time/2.9.2|lib/gcloud/joda-time-2.9.2.jar
+maven://org.json/json/20151123|lib/gcloud/json-20151123.jar
+maven://com.google.cloud.datastore/datastore-v1beta3-protos/1.0.0-beta|lib/gcloud/datastore-v1beta3-protos-1.0.0-beta.jar
+maven://com.google.protobuf/protobuf-java/3.0.0-beta-1|lib/gcloud/protobuf-java-3.0.0-beta-1.jar
+maven://com.google.cloud.datastore/datastore-v1beta3-proto-client/1.0.0-beta.2|lib/gcloud/datastore-v1beta3-proto-client-1.0.0-beta.2.jar
+maven://com.google.http-client/google-http-client-protobuf/1.20.0|lib/gcloud/google-http-client-protobuf-1.20.0.jar
+maven://com.google.api-client/google-api-client/1.20.0|lib/gcloud/google-api-client-1.20.0.jar
+maven://com.google.guava/guava-jdk5/13.0|lib/gcloud/guava-jdk5-13.0.jar
+
+
+
+[lib]
+lib/jetty-gcloud-session-manager-${jetty.version}.jar
+lib/gcloud/*.jar
+
+[xml]
+etc/jetty-gcloud-session-idmgr.xml
+
+[license]
+GCloudDatastore is an open source project hosted on Github and released under the Apache 2.0 license.
+https://github.com/GoogleCloudPlatform/gcloud-java
+http://www.apache.org/licenses/LICENSE-2.0.html
+
+[ini-template]
+## Unique identifier to force the workername for this node in the cluster
+## If not set, will default to the string "node" plus the Env variable $GAE_MODULE_INSTANCE
+# jetty.gcloudSession.workerName=node1
+
+
+
diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud-sessions.mod b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud-sessions.mod
new file mode 100644
index 0000000..89e5bb7
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud-sessions.mod
@@ -0,0 +1,11 @@
+#
+# Jetty GCloudDatastore Session Manager module
+#
+
+[depend]
+gcloud-session-idmgr
+
+
+[xml]
+etc/jetty-gcloud-sessions.xml
+
diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java b/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java
new file mode 100644
index 0000000..52392ef
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java
@@ -0,0 +1,307 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.gcloud.session;
+
+import java.util.Random;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+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.server.session.AbstractSession;
+import org.eclipse.jetty.server.session.AbstractSessionIdManager;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+import com.google.cloud.datastore.Datastore;
+import com.google.cloud.datastore.DatastoreFactory;
+import com.google.cloud.datastore.DatastoreOptions;
+import com.google.cloud.datastore.Entity;
+import com.google.cloud.datastore.Key;
+import com.google.cloud.datastore.KeyFactory;
+
+
+
+/**
+ * GCloudSessionIdManager
+ *
+ * 
+ * 
+ */
+public class GCloudSessionIdManager extends AbstractSessionIdManager
+{
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    public static final int DEFAULT_IDLE_EXPIRY_MULTIPLE = 2;
+    public static final String KIND = "GCloudSessionId";
+    private Server _server;
+    private Datastore _datastore;
+    private KeyFactory _keyFactory;
+    
+    
+    
+ 
+    /**
+     * @param server
+     */
+    public GCloudSessionIdManager(Server server)
+    {
+        super();
+        _server = server;
+    }
+
+    /**
+     * @param server
+     * @param random
+     */
+    public GCloudSessionIdManager(Server server, Random random)
+    {
+       super(random);
+       _server = server;
+    }
+
+
+
+    /** 
+     * Start the id manager.
+     * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        _datastore = DatastoreOptions.defaultInstance().service();
+        _keyFactory = _datastore.newKeyFactory().kind(KIND);
+  
+        super.doStart();
+    }
+
+
+    
+    /** 
+     * Stop the id manager
+     * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+    }
+
+    
+   
+
+    
+    /** 
+     * Check to see if the given session id is being
+     * used by a session in any context.
+     * 
+     * This method will consult the cluster.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#idInUse(java.lang.String)
+     */
+    @Override
+    public boolean idInUse(String id)
+    {
+        if (id == null)
+            return false;
+        
+        String clusterId = getClusterId(id);
+        
+        //ask the cluster - this should also tickle the idle expiration timer on the sessionid entry
+        //keeping it valid
+        try
+        {
+            return exists(clusterId);
+        }
+        catch (Exception e)
+        {
+            LOG.warn("Problem checking inUse for id="+clusterId, e);
+            return false;
+        }
+        
+    }
+
+    /** 
+     * Remember a new in-use session id.
+     * 
+     * This will save the in-use session id to the cluster.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#addSession(javax.servlet.http.HttpSession)
+     */
+    @Override
+    public void addSession(HttpSession session)
+    {
+        if (session == null)
+            return;
+
+        //insert into the store
+        insert (((AbstractSession)session).getClusterId());
+    }
+
+ 
+    
+    
+    /** 
+     * Remove a session id from the list of in-use ids.
+     * 
+     * This will remvove the corresponding session id from the cluster.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#removeSession(javax.servlet.http.HttpSession)
+     */
+    @Override
+    public void removeSession(HttpSession session)
+    {
+        if (session == null)
+            return;
+
+        //delete from the cache
+        delete (((AbstractSession)session).getClusterId());
+    }
+
+    /** 
+     * Remove a session id. This compels all other contexts who have a session
+     * with the same id to also remove it.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
+     */
+    @Override
+    public void invalidateAll(String id)
+    {
+        //delete the session id from list of in-use sessions
+        delete (id);
+
+
+        //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 = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+            if (sessionHandler != null)
+            {
+                SessionManager manager = sessionHandler.getSessionManager();
+
+                if (manager != null && manager instanceof GCloudSessionManager)
+                {
+                    ((GCloudSessionManager)manager).invalidateSession(id);
+                }
+            }
+        }
+
+    }
+
+    /** 
+     * Change a session id. 
+     * 
+     * Typically this occurs when a previously existing session has passed through authentication.
+     * 
+     * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#renewSessionId(java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest)
+     */
+    @Override
+    public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request)
+    {
+        //generate a new id
+        String newClusterId = newSessionId(request.hashCode());
+
+        delete(oldClusterId);
+        insert(newClusterId);
+
+
+        //tell all contexts to update the id 
+        Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
+        for (int i=0; contexts!=null && i<contexts.length; i++)
+        {
+            SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+            if (sessionHandler != null) 
+            {
+                SessionManager manager = sessionHandler.getSessionManager();
+
+                if (manager != null && manager instanceof GCloudSessionManager)
+                {
+                    ((GCloudSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
+                }
+            }
+        }
+
+    }
+
+    
+    
+    /**
+     * Ask the datastore if a particular id exists.
+     * 
+     * @param id the session id to check for existence
+     * @return true if a session with that id exists, false otherwise
+     */
+    protected boolean exists (String id)
+    {
+        if (_datastore == null)
+            throw new IllegalStateException ("No DataStore");
+        Key key = _keyFactory.newKey(id);
+        return _datastore.get(key) != null;
+    }
+    
+
+    /**
+     * Put a session id into the cluster.
+     * 
+     * @param id the id to mark as in use
+     */
+    protected void insert (String id)
+    {        
+        if (_datastore == null)
+            throw new IllegalStateException ("No DataStore");
+
+        Entity entity = Entity.builder(makeKey(id))
+                        .set("id", id).build();
+        _datastore.put(entity);
+    }
+
+   
+   
+    
+    /**
+     * Remove a session id from the cluster.
+     * 
+     * @param id the id to remove
+     */
+    protected void delete (String id)
+    {
+        if (_datastore == null)
+            throw new IllegalStateException ("No DataStore");
+        
+        _datastore.delete(makeKey(id));
+    }
+    
+    
+
+    /**
+     * Generate a unique key from the session id.
+     * 
+     * @param id the id of the session
+     * @return a unique key for the session id
+     */
+    protected Key makeKey (String id)
+    {
+        return _keyFactory.newKey(id);
+    }
+}
diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java b/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java
new file mode 100644
index 0000000..008cd21
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java
@@ -0,0 +1,1307 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.gcloud.session;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandler.Context;
+import org.eclipse.jetty.server.session.AbstractSession;
+import org.eclipse.jetty.server.session.AbstractSessionManager;
+import org.eclipse.jetty.server.session.MemSession;
+import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
+import org.eclipse.jetty.util.thread.Scheduler;
+
+import com.google.cloud.datastore.Blob;
+import com.google.cloud.datastore.Datastore;
+import com.google.cloud.datastore.DatastoreException;
+import com.google.cloud.datastore.DatastoreOptions;
+import com.google.cloud.datastore.Entity;
+import com.google.cloud.datastore.GqlQuery;
+import com.google.cloud.datastore.Key;
+import com.google.cloud.datastore.KeyFactory;
+import com.google.cloud.datastore.Query;
+import com.google.cloud.datastore.Query.ResultType;
+import com.google.cloud.datastore.QueryResults;
+
+
+
+/**
+ * GCloudSessionManager
+ * 
+ * 
+ */
+public class GCloudSessionManager extends AbstractSessionManager
+{
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    
+    
+    public static final String KIND = "GCloudSession";
+    public static final int DEFAULT_MAX_QUERY_RESULTS = 100;
+    public static final long DEFAULT_SCAVENGE_SEC = 600; 
+    public static final int DEFAULT_BACKOFF_MS = 1000; //start at 1 sec
+    public static final int DEFAULT_MAX_RETRIES = 5;
+    
+    /**
+     * Sessions known to this node held in memory
+     */
+    private ConcurrentHashMap<String, GCloudSessionManager.Session> _sessions;
+
+    
+    /**
+     * The length of time a session can be in memory without being checked against
+     * the cluster. A value of 0 indicates that the session is never checked against
+     * the cluster - the current node is considered to be the master for the session.
+     *
+     */
+    private long _staleIntervalSec = 0;
+    
+    protected Scheduler.Task _task; //scavenge task
+    protected Scheduler _scheduler;
+    protected Scavenger _scavenger;
+    protected long _scavengeIntervalMs = 1000L * DEFAULT_SCAVENGE_SEC; //10mins
+    protected boolean _ownScheduler;
+    
+    private Datastore _datastore;
+    private KeyFactory _keyFactory;
+
+
+    private SessionEntityConverter _converter;
+
+
+    private int _maxResults = DEFAULT_MAX_QUERY_RESULTS;
+    private int _backoffMs = DEFAULT_BACKOFF_MS;
+    private int _maxRetries = DEFAULT_MAX_RETRIES;
+
+
+    private boolean _dsSet;
+
+
+    /**
+     * Scavenger
+     *
+     */
+    protected class Scavenger implements Runnable
+    {
+
+        @Override
+        public void run()
+        {
+           try
+           {
+               scavenge();
+           }
+           finally
+           {
+               if (_scheduler != null && _scheduler.isRunning())
+                   _task = _scheduler.schedule(this, _scavengeIntervalMs, TimeUnit.MILLISECONDS);
+           }
+        }
+    }
+
+    /**
+     * SessionEntityConverter
+     *
+     *
+     */
+    public class SessionEntityConverter
+    {
+        public  final String CLUSTERID = "clusterId";
+        public  final String CONTEXTPATH = "contextPath";
+        public  final String VHOST = "vhost";
+        public  final String ACCESSED = "accessed";
+        public  final String LASTACCESSED = "lastAccessed";
+        public  final String CREATETIME = "createTime";
+        public  final  String COOKIESETTIME = "cookieSetTime";
+        public  final String LASTNODE = "lastNode";
+        public  final String EXPIRY = "expiry";
+        public  final  String MAXINACTIVE = "maxInactive";
+        public  final  String ATTRIBUTES = "attributes";
+
+      
+        
+        public Entity entityFromSession (Session session, Key key) throws Exception
+        {
+            if (session == null)
+                return null;
+            
+            Entity entity = null;
+            
+            //serialize the attribute map
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(baos);
+            oos.writeObject(session.getAttributeMap());
+            oos.flush();
+            
+            //turn a session into an entity
+            entity = Entity.builder(key)
+                    .set(CLUSTERID, session.getId())
+                    .set(CONTEXTPATH, session.getContextPath())
+                    .set(VHOST, session.getVHost())
+                    .set(ACCESSED, session.getAccessed())
+                    .set(LASTACCESSED, session.getLastAccessedTime())
+                    .set(CREATETIME, session.getCreationTime())
+                    .set(COOKIESETTIME, session.getCookieSetTime())
+                    .set(LASTNODE,session.getLastNode())
+                    .set(EXPIRY, session.getExpiry())
+                    .set(MAXINACTIVE, session.getMaxInactiveInterval())
+                    .set(ATTRIBUTES, Blob.copyFrom(baos.toByteArray())).build();
+                     
+            return entity;
+        }
+        
+        public Session sessionFromEntity (Entity entity) throws Exception
+        {
+            if (entity == null)
+                return null;
+
+            final AtomicReference<Session> reference = new AtomicReference<Session>();
+            final AtomicReference<Exception> exception = new AtomicReference<Exception>();
+            Runnable load = new Runnable()
+            {
+                public void run ()
+                {
+                    try
+                    {
+                        //turn an entity into a Session
+                        String clusterId = entity.getString(CLUSTERID);
+                        String contextPath = entity.getString(CONTEXTPATH);
+                        String vhost = entity.getString(VHOST);
+                        long accessed = entity.getLong(ACCESSED);
+                        long lastAccessed = entity.getLong(LASTACCESSED);
+                        long createTime = entity.getLong(CREATETIME);
+                        long cookieSetTime = entity.getLong(COOKIESETTIME);
+                        String lastNode = entity.getString(LASTNODE);
+                        long expiry = entity.getLong(EXPIRY);
+                        long maxInactive = entity.getLong(MAXINACTIVE);
+                        Blob blob = (Blob) entity.getBlob(ATTRIBUTES);
+
+                        Session session = new Session (clusterId, createTime, accessed, maxInactive);
+                        session.setLastNode(lastNode);
+                        session.setContextPath(contextPath);
+                        session.setVHost(vhost);
+                        session.setCookieSetTime(cookieSetTime);
+                        session.setLastAccessedTime(lastAccessed);
+                        session.setLastNode(lastNode);
+                        session.setExpiry(expiry);
+                        try (ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(blob.asInputStream()))
+                        {
+                            Object o = ois.readObject();
+                            session.addAttributes((Map<String,Object>)o);
+                        }
+                        reference.set(session);
+                    }
+                    catch (Exception e)
+                    {
+                        exception.set(e);
+                    }
+                }
+            };
+            
+            if (_context==null)
+                load.run();
+            else
+                _context.getContextHandler().handle(null,load);
+   
+           
+            if (exception.get() != null)
+            {
+                exception.get().printStackTrace();
+                throw exception.get();
+            }
+            
+            return reference.get();
+        }
+    }
+    
+   
+    
+
+    
+    /**
+     * Session
+     *
+     * Representation of a session in local memory.
+     */
+    public class Session extends MemSession
+    {
+        
+        private ReentrantLock _lock = new ReentrantLock();
+        
+        /**
+         * The (canonical) context path for with which this session is associated
+         */
+        private String _contextPath;
+        
+        
+        
+        /**
+         * The time in msec since the epoch at which this session should expire
+         */
+        private long _expiryTime; 
+        
+        
+        /**
+         * Time in msec since the epoch at which this session was last read from cluster
+         */
+        private long _lastSyncTime;
+        
+        
+        /**
+         * The workername of last node known to be managing the session
+         */
+        private String _lastNode;
+        
+        
+        /**
+         * If dirty, session needs to be (re)sent to cluster
+         */
+        protected boolean _dirty=false;
+        
+        
+     
+
+        /**
+         * Any virtual hosts for the context with which this session is associated
+         */
+        private String _vhost;
+
+        
+        /**
+         * Count of how many threads are active in this session
+         */
+        private AtomicInteger _activeThreads = new AtomicInteger(0);
+        
+        
+        
+        
+        /**
+         * A new session.
+         * 
+         * @param request
+         */
+        protected Session (HttpServletRequest request)
+        {
+            super(GCloudSessionManager.this,request);
+            long maxInterval = getMaxInactiveInterval();
+            _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
+            _lastNode = getSessionIdManager().getWorkerName();
+           setVHost(GCloudSessionManager.getVirtualHost(_context));
+           setContextPath(GCloudSessionManager.getContextPath(_context));
+           _activeThreads.incrementAndGet(); //access will not be called on a freshly created session so increment here
+        }
+        
+        
+    
+        
+        /**
+         * A restored session.
+         * 
+         * @param sessionId
+         * @param created
+         * @param accessed
+         * @param maxInterval
+         */
+        protected Session (String sessionId, long created, long accessed, long maxInterval)
+        {
+            super(GCloudSessionManager.this, created, accessed, sessionId);
+            _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
+        }
+        
+        /** 
+         * Called on entry to the session.
+         * 
+         * @see org.eclipse.jetty.server.session.AbstractSession#access(long)
+         */
+        @Override
+        protected boolean access(long time)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Access session({}) for context {} on worker {}", getId(), getContextPath(), getSessionIdManager().getWorkerName());
+            try
+            {
+
+                long now = System.currentTimeMillis();
+                //lock so that no other thread can call access or complete until the first one has refreshed the session object if necessary
+                _lock.lock();
+                //a request thread is entering
+                if (_activeThreads.incrementAndGet() == 1)
+                {
+                    //if the first thread, check that the session in memory is not stale, if we're checking for stale sessions
+                    if (getStaleIntervalSec() > 0  && (now - getLastSyncTime()) >= (getStaleIntervalSec() * 1000L))
+                    {
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("Acess session({}) for context {} on worker {} stale session. Reloading.", getId(), getContextPath(), getSessionIdManager().getWorkerName());
+                        refresh();
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                LOG.warn(e);
+            }
+            finally
+            {            
+                _lock.unlock();
+            }
+
+            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()
+        {
+            super.complete();
+
+            //lock so that no other thread that might be calling access can proceed until this complete is done
+            _lock.lock();
+
+            try
+            {
+                //if this is the last request thread to be in the session
+                if (_activeThreads.decrementAndGet() == 0)
+                {
+                    try
+                    {
+                        //an invalid session will already have been removed from the
+                        //local session map and deleted from the cluster. If its valid save
+                        //it to the cluster.
+                        //TODO consider doing only periodic saves if only the last access
+                        //time to the session changes
+                        if (isValid())
+                        {
+                            //if session still valid && its dirty or stale or never been synced, write it to the cluster
+                            //otherwise, we just keep the updated last access time in memory
+                            if (_dirty || getLastSyncTime() == 0 || isStale(System.currentTimeMillis()))
+                            {
+                                willPassivate();
+                                save(this);
+                                didActivate();
+                            }
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        LOG.warn("Problem saving session({})",getId(), e);
+                    } 
+                    finally
+                    {
+                        _dirty = false;
+                    }
+                }
+            }
+            finally
+            {
+                _lock.unlock();
+            }
+        }
+        
+        /** Test if the session is stale
+         * @param atTime
+         * @return true if the session is stale at the time given
+         */
+        protected boolean isStale (long atTime)
+        {
+            return (getStaleIntervalSec() > 0) && (atTime - getLastSyncTime() >= (getStaleIntervalSec()*1000L));
+        }
+        
+        
+        /** Test if the session is dirty
+         * @return true if the dirty flag is set
+         */
+        protected boolean isDirty ()
+        {
+            return _dirty;
+        }
+
+        /** 
+         * Expire the session.
+         * 
+         * @see org.eclipse.jetty.server.session.AbstractSession#timeout()
+         */
+        @Override
+        protected void timeout()
+        {
+            if (LOG.isDebugEnabled()) LOG.debug("Timing out session {}", getId());
+            super.timeout();
+        }
+        
+      
+        
+        /**
+         * Reload the session from the cluster. If the node that
+         * last managed the session from the cluster is ourself,
+         * then the session does not need refreshing.
+         * NOTE: this method MUST be called with sufficient locks
+         * in place to prevent 2 or more concurrent threads from
+         * simultaneously updating the session.
+         */
+        private void refresh () 
+        throws Exception
+        {
+            //get fresh copy from the cluster
+            Session fresh = load(makeKey(getClusterId(), _context));
+
+            //if the session no longer exists, invalidate
+            if (fresh == null)
+            {
+                invalidate();
+                return;
+            }
+
+            //cluster copy assumed to be the same as we were the last
+            //node to manage it
+            if (fresh.getLastNode().equals(getLastNode()))
+                return;
+
+            setLastNode(getSessionIdManager().getWorkerName());
+            
+            //prepare for refresh
+            willPassivate();
+
+            //if fresh has no attributes, remove them
+            if (fresh.getAttributes() == 0)
+                this.clearAttributes();
+            else
+            {
+                //reconcile attributes
+                for (String key:fresh.getAttributeMap().keySet())
+                {
+                    Object freshvalue = fresh.getAttribute(key);
+
+                    //session does not already contain this attribute, so bind it
+                    if (getAttribute(key) == null)
+                    { 
+                        doPutOrRemove(key,freshvalue);
+                        bindValue(key,freshvalue);
+                    }
+                    else //session already contains this attribute, update its value
+                    {
+                        doPutOrRemove(key,freshvalue);
+                    }
+
+                }
+                // cleanup, remove values from session, that don't exist in data anymore:
+                for (String key : getNames())
+                {
+                    if (fresh.getAttribute(key) == null)
+                    {
+                        Object oldvalue = getAttribute(key);
+                        doPutOrRemove(key,null);
+                        unbindValue(key,oldvalue);
+                    }
+                }
+            }
+            //finish refresh
+            didActivate();
+        }
+
+
+        public void setExpiry (long expiry)
+        {
+            _expiryTime = expiry;
+        }
+        
+
+        public long getExpiry ()
+        {
+            return _expiryTime;
+        }
+        
+        public boolean isExpiredAt (long time)
+        {
+            if (_expiryTime <= 0)
+                return false; //never expires
+            
+            return  (_expiryTime <= time);
+        }
+        
+        public void swapId (String newId, String newNodeId)
+        {
+            //TODO probably synchronize rather than use the access/complete lock?
+            _lock.lock();
+            setClusterId(newId);
+            setNodeId(newNodeId);
+            _lock.unlock();
+        }
+        
+        @Override
+        public void setAttribute (String name, Object value)
+        {
+            Object old = changeAttribute(name, value);
+            if (value == null && old == null)
+                return; //if same as remove attribute but attribute was already removed, no change
+            
+           _dirty = true;
+        }
+        
+        
+        public String getContextPath()
+        {
+            return _contextPath;
+        }
+
+
+        public void setContextPath(String contextPath)
+        {
+            this._contextPath = contextPath;
+        }
+
+
+        public String getVHost()
+        {
+            return _vhost;
+        }
+
+
+        public void setVHost(String vhost)
+        {
+            this._vhost = vhost;
+        }
+        
+        public String getLastNode()
+        {
+            return _lastNode;
+        }
+
+
+        public void setLastNode(String lastNode)
+        {
+            _lastNode = lastNode;
+        }
+
+
+        public long getLastSyncTime()
+        {
+            return _lastSyncTime;
+        }
+
+
+        public void setLastSyncTime(long lastSyncTime)
+        {
+            _lastSyncTime = lastSyncTime;
+        }
+
+    }
+
+    
+    
+    
+    public void setDatastore (Datastore datastore)
+    {
+        _datastore = datastore;
+        _dsSet = true;
+    }
+
+
+    
+    /**
+     * 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");
+
+        if (!_dsSet)
+            _datastore = DatastoreOptions.defaultInstance().service();
+        _keyFactory = _datastore.newKeyFactory().kind(KIND);
+        _converter = new SessionEntityConverter();       
+        _sessions = new ConcurrentHashMap<String, Session>();
+
+        //try and use a common scheduler, fallback to own
+        _scheduler = getSessionHandler().getServer().getBean(Scheduler.class);
+        if (_scheduler == null)
+        {
+            _scheduler = new ScheduledExecutorScheduler();
+            _ownScheduler = true;
+            _scheduler.start();
+        }
+        else if (!_scheduler.isStarted())
+            throw new IllegalStateException("Shared scheduler not started");
+ 
+        setScavengeIntervalSec(getScavengeIntervalSec());
+        
+        super.doStart();
+    }
+
+
+    /**
+     * Stop the session manager.
+     *
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop()
+     */
+    @Override
+    public void doStop() throws Exception
+    {
+        super.doStop();
+
+        if (_task!=null)
+            _task.cancel();
+        _task=null;
+        if (_ownScheduler && _scheduler !=null)
+            _scheduler.stop();
+        _scheduler = null;
+
+        _sessions.clear();
+        _sessions = null;
+        
+        if (!_dsSet)
+            _datastore = null;
+    }
+
+
+
+    /**
+     * Look for sessions in local memory that have expired.
+     */
+    public void scavenge ()
+    {
+        try
+        {
+            //scavenge in the database every so often
+            scavengeGCloudDataStore();
+        }
+        catch (Exception e)
+        {
+            LOG.warn("Problem scavenging", e);
+        }
+    }
+
+ 
+    
+    protected void scavengeGCloudDataStore()
+    throws Exception
+    {
+       
+        //query the datastore for sessions that have expired
+        long now = System.currentTimeMillis();
+        
+        //give a bit of leeway so we don't immediately something that has only just expired a nanosecond ago
+        now = now - (_scavengeIntervalMs/2);
+        
+        if (LOG.isDebugEnabled())
+            LOG.debug("Scavenging for sessions expired before "+now);
+
+
+        GqlQuery.Builder builder = Query.gqlQueryBuilder(ResultType.ENTITY, "select * from "+KIND+" where expiry < @1 limit "+_maxResults);
+        builder.allowLiteral(true);
+        builder.addBinding(now);
+        Query<Entity> query = builder.build();
+        QueryResults<Entity> results = _datastore.run(query);
+        
+        while (results.hasNext())
+        {          
+            Entity sessionEntity = results.next();
+            scavengeSession(sessionEntity);        
+        }
+
+    }
+
+    /**
+     * Scavenge a session that has expired
+     * @param e the session info from datastore
+     * @throws Exception
+     */
+    protected void scavengeSession (Entity e)
+            throws Exception
+    {
+        long now = System.currentTimeMillis();
+        Session session = _converter.sessionFromEntity(e);
+        if (session == null)
+            return;
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("Scavenging session: {}",session.getId());
+        //if the session isn't in memory already, put it there so we can do a normal timeout call
+         Session memSession =  _sessions.putIfAbsent(session.getId(), session);
+         if (memSession == null)
+         {
+             memSession = session;
+         }
+
+        //final check
+        if (memSession.isExpiredAt(now))
+        {
+            if (LOG.isDebugEnabled()) LOG.debug("Session {} is definitely expired", memSession.getId());
+            memSession.timeout();   
+        }
+    }
+
+    public long getScavengeIntervalSec ()
+    {
+        return _scavengeIntervalMs/1000;
+    }
+
+    
+    
+    /**
+     * Set the interval between runs of the scavenger. It should not be run too
+     * often.
+     * 
+     * 
+     * @param sec the number of seconds between scavenge cycles
+     */
+    public void setScavengeIntervalSec (long sec)
+    {
+
+        long old_period=_scavengeIntervalMs;
+        long period=sec*1000L;
+
+        _scavengeIntervalMs=period;
+
+        if (_scavengeIntervalMs > 0)
+        {
+            //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");
+        }
+        else
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Scavenging disabled"); 
+        }
+
+ 
+        
+        synchronized (this)
+        {
+            if (_scheduler != null && (period!=old_period || _task==null))
+            {
+                //clean up any previously scheduled scavenger
+                if (_task!=null)
+                    _task.cancel();
+
+                //start a new one
+                if (_scavengeIntervalMs > 0)
+                {
+                    if (_scavenger == null)
+                        _scavenger = new Scavenger();
+
+                    _task = _scheduler.schedule(_scavenger,_scavengeIntervalMs,TimeUnit.MILLISECONDS);
+                }
+            }
+        }
+    }
+    
+    
+    public long getStaleIntervalSec()
+    {
+        return _staleIntervalSec;
+    }
+
+
+    public void setStaleIntervalSec(long staleIntervalSec)
+    {
+        _staleIntervalSec = staleIntervalSec;
+    }
+    
+    
+    public int getMaxResults()
+    {
+        return _maxResults;
+    }
+
+
+    public void setMaxResults(int maxResults)
+    {
+        if (_maxResults <= 0)
+            _maxResults = DEFAULT_MAX_QUERY_RESULTS;
+        else
+            _maxResults = maxResults;
+    }
+
+
+    /** 
+     * Add a new session for the context related to this session manager
+     * 
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
+     */
+    @Override
+    protected void addSession(AbstractSession session)
+    {
+        if (session==null)
+            return;
+        
+        if (LOG.isDebugEnabled()) LOG.debug("Adding session({}) to session manager for context {} on worker {}",session.getClusterId(), getContextPath(getContext()),getSessionIdManager().getWorkerName() + " with lastnode="+((Session)session).getLastNode());
+        _sessions.put(session.getClusterId(), (Session)session);
+        
+        try
+        {     
+                session.willPassivate();
+                save(((GCloudSessionManager.Session)session));
+                session.didActivate();
+            
+        }
+        catch (Exception e)
+        {
+            LOG.warn("Unable to store new session id="+session.getId() , e);
+        }
+    }
+
+    /** 
+     * Ask the cluster for the session.
+     * 
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
+     */
+    @Override
+    public AbstractSession getSession(String idInCluster)
+    {
+        Session session = null;
+
+        //try and find the session in this node's memory
+        Session memSession = (Session)_sessions.get(idInCluster);
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("getSession({}) {} in session map",idInCluster,(memSession==null?"not":""));
+
+        long now = System.currentTimeMillis();
+        try
+        {
+            //if the session is not in this node's memory, then load it from the datastore
+            if (memSession == null)
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("getSession({}): loading session data from cluster", idInCluster);
+
+                session = load(makeKey(idInCluster, _context));
+                if (session != null)
+                {
+                    //Check that it wasn't expired
+                    if (session.getExpiry() > 0 && session.getExpiry() <= now)
+                    {
+                        if (LOG.isDebugEnabled()) LOG.debug("getSession ({}): Session expired", idInCluster);
+                        //ensure that the session id for the expired session is deleted so that a new session with the 
+                        //same id cannot be created (because the idInUse() test would succeed)
+                        ((GCloudSessionIdManager)getSessionIdManager()).removeSession(session);
+                        return null;  
+                    }
+
+                    //Update the last worker node to me
+                    session.setLastNode(getSessionIdManager().getWorkerName());                            
+                    //TODO consider saving session here if lastNode was not this node
+
+                    //Check that another thread hasn't loaded the same session
+                    Session existingSession = _sessions.putIfAbsent(idInCluster, session);
+                    if (existingSession != null)
+                    {
+                        //use the one that the other thread inserted
+                        session = existingSession;
+                        LOG.debug("getSession({}): using session loaded by another request thread ", idInCluster);
+                    }
+                    else
+                    {
+                        //indicate that the session was reinflated
+                        session.didActivate();
+                        LOG.debug("getSession({}): loaded session from cluster", idInCluster);
+                    }
+                    return session;
+                }
+                else
+                {
+                    //The requested session does not exist anywhere in the cluster
+                    LOG.debug("getSession({}): No session in cluster matching",idInCluster);
+                    return null;
+                }
+            }
+            else
+            {
+               //The session exists in this node's memory
+               LOG.debug("getSession({}): returning session from local memory ", memSession.getClusterId());
+                return memSession;
+            }
+        }
+        catch (Exception e)
+        {
+            LOG.warn("Unable to load session="+idInCluster, e);
+            return null;
+        }
+    }
+    
+    
+
+    /** 
+     * The session manager is stopping.
+     * 
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#shutdownSessions()
+     */
+    @Override
+    protected void shutdownSessions() throws Exception
+    {
+        Set<String> keys = new HashSet<String>(_sessions.keySet());
+        for (String key:keys)
+        {
+            Session session = _sessions.remove(key); //take the session out of the session list
+            //If the session is dirty, then write it to the cluster.
+            //If the session is simply stale do NOT write it to the cluster, as some other node
+            //may have started managing that session - this means that the last accessed/expiry time
+            //will not be updated, meaning it may look like it can expire sooner than it should.
+            try
+            {
+                if (session.isDirty())
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Saving dirty session {} before exiting ", session.getId());
+                    save(session);
+                }
+            }
+            catch (Exception e)
+            {
+                LOG.warn(e);
+            }
+        }
+    }
+
+
+    @Override
+    protected AbstractSession newSession(HttpServletRequest request)
+    {
+        return new Session(request);
+    }
+
+    /** 
+     * Remove a session from local memory, and delete it from
+     * the cluster cache.
+     * 
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
+     */
+    @Override
+    protected boolean removeSession(String idInCluster)
+    {
+        Session session = (Session)_sessions.remove(idInCluster);
+        try
+        {
+            if (session != null)
+            {
+                delete(session);
+            }
+        }
+        catch (Exception e)
+        {
+            LOG.warn("Problem deleting session id="+idInCluster, e);
+        }
+        return session!=null;
+    }
+    
+    
+    
+    
+    @Override
+    public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
+    {
+        Session session = null;
+        try
+        {
+            //take the session with that id out of our managed list
+            session = (Session)_sessions.remove(oldClusterId);
+            if (session != null)
+            {
+                //TODO consider transactionality and ramifications if the session is live on another node
+                delete(session); //delete the old session from the cluster  
+                session.swapId(newClusterId, newNodeId); //update the session
+                _sessions.put(newClusterId, session); //put it into managed list under new key
+                save(session); //put the session under the new id into the cluster
+            }
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+        }
+
+        super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
+    }
+
+
+    /**
+     * Load a session from the clustered cache.
+     * 
+     * @param key the unique datastore key for the session
+     * @return the Session object restored from datastore
+     */
+    protected Session load (Key key)
+    throws Exception
+    {
+        if (_datastore == null)
+            throw new IllegalStateException("No DataStore");
+        
+        if (LOG.isDebugEnabled()) LOG.debug("Loading session {} from DataStore ", key);
+
+        Entity entity = _datastore.get(key);
+        if (entity == null)
+        {
+            if (LOG.isDebugEnabled()) LOG.debug("No session {} in DataStore ",key);
+            return null;
+        }
+        else
+        {
+            Session session = _converter.sessionFromEntity(entity);
+            session.setLastSyncTime(System.currentTimeMillis());
+            return session;
+        }
+    }
+    
+    
+    
+    /**
+     * Save or update the session to the cluster cache
+     * 
+     * @param session the session to save to datastore
+     * @throws Exception
+     */
+    protected void save (GCloudSessionManager.Session session)
+    throws Exception
+    {
+        if (_datastore == null)
+            throw new IllegalStateException("No DataStore");
+        
+        if (LOG.isDebugEnabled()) LOG.debug("Writing session {} to DataStore", session.getId());
+    
+        Entity entity = _converter.entityFromSession(session, makeKey(session, _context));
+        
+        //attempt the update with exponential back-off
+        int backoff = getBackoffMs();
+        int attempts;
+        for (attempts = 0; attempts < getMaxRetries(); attempts++)
+        {
+            try
+            {
+                _datastore.put(entity);
+                session.setLastSyncTime(System.currentTimeMillis());
+                return;
+            }
+            catch (DatastoreException e)
+            {
+                if (e.retryable())
+                {
+                    if (LOG.isDebugEnabled()) LOG.debug("Datastore put retry {} waiting {}ms", attempts, backoff);
+                        
+                    try
+                    {
+                        Thread.currentThread().sleep(backoff);
+                    }
+                    catch (InterruptedException x)
+                    {
+                    }
+                    backoff *= 2;
+                }
+                else
+                {
+                   throw e;
+                }
+            }
+        }
+        throw new IOException("Retries exhausted");
+    }
+    
+    
+    
+    public int getMaxRetries()
+    {
+        return _maxRetries;
+    }
+
+
+    public int getBackoffMs()
+    {
+        return _backoffMs;
+    }
+
+
+    /**
+     * @param backoffMs the backoffMs to set
+     */
+    public void setBackoffMs(int backoffMs)
+    {
+        _backoffMs = backoffMs;
+    }
+
+
+    /**
+     * @param maxRetries the maxRetries to set
+     */
+    public void setMaxRetries(int maxRetries)
+    {
+        _maxRetries = maxRetries;
+    }
+
+
+    /**
+     * Remove the session from the cluster cache.
+     * 
+     * @param session the session to delete from datastore
+     */
+    protected void delete (GCloudSessionManager.Session session)
+    {  
+        if (_datastore == null)
+            throw new IllegalStateException("No DataStore");
+        if (LOG.isDebugEnabled()) LOG.debug("Removing session {} from DataStore", session.getId());
+        _datastore.delete(makeKey(session, _context));
+    }
+
+    
+    /**
+     * Invalidate a session for this context with the given id
+     * 
+     * @param idInCluster the id of the session to invalidate
+     */
+    public void invalidateSession (String idInCluster)
+    {
+        Session session = (Session)_sessions.get(idInCluster);
+
+        if (session != null)
+        {
+            session.invalidate();
+        }
+    }
+
+    
+    /**
+     * Make a unique key for this session.
+     * As the same session id can be used across multiple contexts, to
+     * make it unique, the key must be composed of:
+     * <ol>
+     * <li>the id</li>
+     * <li>the context path</li>
+     * <li>the virtual hosts</li>
+     * </ol>
+     * 
+     * @param session the session for which the key should be created
+     * @param context the context to which the session belongs
+     * @return a unique datastore key for the session
+     */
+    protected Key makeKey (Session session, Context context)
+    {
+       return makeKey(session.getId(), context);
+    }
+    
+    /**
+     * Make a unique key for this session.
+     * As the same session id can be used across multiple contexts, to
+     * make it unique, the key must be composed of:
+     * <ol>
+     * <li>the id</li>
+     * <li>the context path</li>
+     * <li>the virtual hosts</li>
+     * </ol>
+     * 
+     * @param id the id of the session for which the key should be created
+     * @param context the context to which the session belongs
+     * @return a unique datastore key for the session
+     */
+    protected Key makeKey (String id, Context context)
+    {
+        return _keyFactory.newKey(canonicalizeKey(id,context));
+    }
+    
+    
+    /**
+     * Make a unique string from the session id and info from its Context
+     * @param id the id of the Session
+     * @param context the Context in which the Session exists
+     * @return a unique string representing the id of the session in the context
+     */
+    protected String canonicalizeKey(String id, Context context)
+    {
+        String key = getContextPath(context);
+        key = key + "_" + getVirtualHost(context);
+        key = key+"_"+id;
+        return key;
+    }
+    
+    /**
+     * Turn the context path into an acceptable string
+     * 
+     * @param context a context
+     * @return a stringified version of the context
+     */
+    private static String getContextPath (ContextHandler.Context context)
+    {
+        return canonicalize (context.getContextPath());
+    }
+
+    /**
+     * Get the first virtual host for the context.
+     *
+     * Used to help identify the exact session/contextPath.
+     *
+     * @param context a context
+     * @return a stringified form of the virtual hosts for the context, 0.0.0.0 if none are 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 name from a context path.
+     *
+     * @param path a context path
+     * @return a stringified form of the context path
+     */
+    private static String canonicalize (String path)
+    {
+        if (path==null)
+            return "";
+
+        return path.replace('/', '_').replace('.','_').replace('\\','_');
+    }
+
+}
diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTester.java b/jetty-gcloud/jetty-gcloud-session-manager/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTester.java
new file mode 100644
index 0000000..7e6e17e
--- /dev/null
+++ b/jetty-gcloud/jetty-gcloud-session-manager/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTester.java
@@ -0,0 +1,69 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.gcloud.session;
+
+
+
+
+import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+public class GCloudSessionTester
+{
+    public static void main( String[] args ) throws Exception
+    {
+        if (args.length < 4)
+            System.err.println("Usage: GCloudSessionTester projectid p12file password serviceaccount");
+        
+        System.setProperty("org.eclipse.jetty.server.session.LEVEL", "DEBUG");
+        
+        Server server = new Server(8080);
+        HashLoginService loginService = new HashLoginService();
+        loginService.setName( "Test Realm" );
+        loginService.setConfig( "../../jetty-distribution/target/distribution/demo-base/resources/realm.properties" );
+        server.addBean( loginService );
+
+    
+        GCloudSessionIdManager idmgr = new GCloudSessionIdManager(server);
+        idmgr.setWorkerName("w1");
+        server.setSessionIdManager(idmgr);
+
+ 
+        WebAppContext webapp = new WebAppContext();
+        webapp.setContextPath("/");
+        webapp.setWar("../../jetty-distribution/target/distribution/demo-base/webapps/test.war");
+        webapp.addAliasCheck(new AllowSymLinkAliasChecker());
+        GCloudSessionManager mgr = new GCloudSessionManager();
+        mgr.setSessionIdManager(idmgr);
+        webapp.setSessionHandler(new SessionHandler(mgr));
+
+        // A WebAppContext is a ContextHandler as well so it needs to be set to
+        // the server so it is aware of where to send the appropriate requests.
+        server.setHandler(webapp);
+
+        // Start things up! 
+        server.start();
+
+    
+        server.join();
+    }
+}
diff --git a/jetty-gcloud/pom.xml b/jetty-gcloud/pom.xml
new file mode 100644
index 0000000..cdafb5f
--- /dev/null
+++ b/jetty-gcloud/pom.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <artifactId>jetty-project</artifactId>
+    <groupId>org.eclipse.jetty</groupId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.eclipse.jetty.gcloud</groupId>
+  <artifactId>gcloud-parent</artifactId>
+  <packaging>pom</packaging>
+  <name>Jetty :: GCloud</name>
+
+  <properties>
+    <gcloud.version>0.2.3</gcloud.version>
+  </properties>
+
+  <modules>
+    <module>jetty-gcloud-session-manager</module>
+    <module>jetty-gcloud-memcached-session-manager</module>
+  </modules>
+
+</project>
diff --git a/jetty-http-spi/pom.xml b/jetty-http-spi/pom.xml
index 7d8dc67..76f7827 100644
--- a/jetty-http-spi/pom.xml
+++ b/jetty-http-spi/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-http-spi</artifactId>
@@ -33,35 +33,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>artifact-jar</id>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-        </executions>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
@@ -69,6 +40,19 @@
         </configuration>
       </plugin>
       <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+            <configuration>
+              <instructions>
+                <Bundle-Description>Jetty Http SPI</Bundle-Description>
+                <Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
+                <Provide-Capability>osgi.serviceloader; osgi.serviceloader=com.sun.net.httpserver.spi.HttpServerProvider</Provide-Capability>
+                <_nouses>true</_nouses>
+              </instructions>
+            </configuration>
+      </plugin>
+      <plugin>
         <groupId>org.jacoco</groupId>
         <artifactId>jacoco-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/DelegatingThreadPool.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/DelegatingThreadPool.java
index dafdc6b..4528309 100644
--- a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/DelegatingThreadPool.java
+++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/DelegatingThreadPool.java
@@ -20,7 +20,6 @@
 
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java
index 3ddcae4..1c3d5c3 100644
--- a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java
+++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java
@@ -46,7 +46,7 @@
 public class HttpSpiContextHandler extends ContextHandler

 {

     public static final Logger LOG = Log.getLogger(HttpSpiContextHandler.class);

-    

+

     private HttpContext _httpContext;

 

     private HttpHandler _httpHandler;

@@ -106,7 +106,7 @@
                 writer.println("</pre>");

             }

             

-            writer.println("<p><i><small><a href=\"http://eclipse.org/jetty\">Powered by jetty://</a></small></i></p>");

+            baseRequest.getHttpChannel().getHttpConfiguration().writePoweredBy(writer,"<p>","</p>");

 

             writer.close();

         }

diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchange.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchange.java
index 2238704..6be568a 100644
--- a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchange.java
+++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchange.java
@@ -32,9 +32,6 @@
 import com.sun.net.httpserver.HttpExchange;
 import com.sun.net.httpserver.HttpPrincipal;
 
-/* ------------------------------------------------------------ */
-/**
- */
 public class JettyHttpExchange extends HttpExchange implements JettyExchange
 {
     private JettyHttpExchangeDelegate _delegate;
@@ -46,9 +43,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.http.spi.JettyExchange#hashCode()
-     */
     @Override
     public int hashCode()
     {
@@ -56,9 +50,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#getRequestHeaders()
-     */
     @Override
     public Headers getRequestHeaders()
     {
@@ -66,9 +57,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#getResponseHeaders()
-     */
     @Override
     public Headers getResponseHeaders()
     {
@@ -76,9 +64,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#getRequestURI()
-     */
     @Override
     public URI getRequestURI()
     {
@@ -86,9 +71,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#getRequestMethod()
-     */
     @Override
     public String getRequestMethod()
     {
@@ -96,9 +78,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#getHttpContext()
-     */
     @Override
     public HttpContext getHttpContext()
     {
@@ -106,9 +85,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#close()
-     */
     @Override
     public void close()
     {
@@ -116,9 +92,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.http.spi.JettyExchange#equals(java.lang.Object)
-     */
     @Override
     public boolean equals(Object obj)
     {
@@ -126,9 +99,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#getRequestBody()
-     */
     @Override
     public InputStream getRequestBody()
     {
@@ -136,9 +106,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#getResponseBody()
-     */
     @Override
     public OutputStream getResponseBody()
     {
@@ -146,9 +113,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#sendResponseHeaders(int, long)
-     */
     @Override
     public void sendResponseHeaders(int rCode, long responseLength) throws IOException
     {
@@ -156,9 +120,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#getRemoteAddress()
-     */
     @Override
     public InetSocketAddress getRemoteAddress()
     {
@@ -166,9 +127,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#getResponseCode()
-     */
     @Override
     public int getResponseCode()
     {
@@ -176,9 +134,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#getLocalAddress()
-     */
     @Override
     public InetSocketAddress getLocalAddress()
     {
@@ -186,9 +141,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#getProtocol()
-     */
     @Override
     public String getProtocol()
     {
@@ -196,9 +148,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#getAttribute(java.lang.String)
-     */
     @Override
     public Object getAttribute(String name)
     {
@@ -206,9 +155,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#setAttribute(java.lang.String, java.lang.Object)
-     */
     @Override
     public void setAttribute(String name, Object value)
     {
@@ -216,9 +162,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see JettyHttpExchangeDelegate#setStreams(java.io.InputStream, java.io.OutputStream)
-     */
     @Override
     public void setStreams(InputStream i, OutputStream o)
     {
@@ -226,9 +169,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.http.spi.JettyExchange#getPrincipal()
-     */
     @Override
     public HttpPrincipal getPrincipal()
     {
@@ -236,18 +176,12 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.http.spi.JettyExchange#setPrincipal(com.sun.net.httpserver.HttpPrincipal)
-     */
     public void setPrincipal(HttpPrincipal principal)
     {
         _delegate.setPrincipal(principal);
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.http.spi.JettyExchange#toString()
-     */
     @Override
     public String toString()
     {
diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServer.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServer.java
index 31baab9..1aad634 100644
--- a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServer.java
+++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServer.java
@@ -28,6 +28,8 @@
 
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.NetworkConnector;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
@@ -48,24 +50,35 @@
 {
     private static final Logger LOG = Log.getLogger(JettyHttpServer.class);
 
+    private final HttpConfiguration _httpConfiguration;
 
-    private Server _server;
-    
+    private final Server _server;
+
     private boolean _serverShared;
 
     private InetSocketAddress _addr;
 
-    private ThreadPoolExecutor _executor;
 
     private Map<String, JettyHttpContext> _contexts = new HashMap<String, JettyHttpContext>();
-    
+
     private Map<String, Connector> _connectors = new HashMap<String, Connector>();
 
-    
+
     public JettyHttpServer(Server server, boolean shared)
     {
+        this(server,shared,new HttpConfiguration());
+    }
+
+    public JettyHttpServer(Server server, boolean shared, HttpConfiguration configuration)
+    {
         this._server = server;
         this._serverShared = shared;
+        this._httpConfiguration = configuration;
+    }
+
+    public HttpConfiguration getHttpConfiguration()
+    {
+        return _httpConfiguration;
     }
 
     @Override
@@ -83,10 +96,10 @@
                 }
             }
         }
-        
+
         if (_serverShared)
-                throw new IOException("jetty server is not bound to port " + addr.getPort());
-        
+            throw new IOException("jetty server is not bound to port " + addr.getPort());
+
         this._addr = addr;
 
         if (LOG.isDebugEnabled()) LOG.debug("binding server to port " + addr.getPort());
@@ -94,10 +107,18 @@
         connector.setPort(addr.getPort());
         connector.setHost(addr.getHostName());
         _server.addConnector(connector);
-        
+
         _connectors.put(addr.getHostName() + addr.getPort(), connector);
     }
 
+    protected ServerConnector newServerConnector(InetSocketAddress addr,int backlog)
+    {
+        ServerConnector connector = new ServerConnector(_server,new HttpConnectionFactory(_httpConfiguration));
+        connector.setPort(addr.getPort());
+        connector.setHost(addr.getHostName());
+        return connector;
+    }
+
     @Override
     public InetSocketAddress getAddress()
     {
@@ -108,7 +129,7 @@
     public void start()
     {
         if (_serverShared) return;
-        
+
         try
         {
             _server.start();
@@ -145,7 +166,7 @@
     {
         cleanUpContexts();
         cleanUpConnectors();
-        
+
         if (_serverShared) return;
 
         try
@@ -158,15 +179,15 @@
         }
     }
 
-        private void cleanUpContexts()
-        {
+    private void cleanUpContexts()
+    {
         for (Map.Entry<String, JettyHttpContext> stringJettyHttpContextEntry : _contexts.entrySet())
         {
             JettyHttpContext context = stringJettyHttpContextEntry.getValue();
             _server.removeBean(context.getJettyContextHandler());
         }
-                _contexts.clear();
-        }
+        _contexts.clear();
+    }
 
     private void cleanUpConnectors()
     {
@@ -176,15 +197,17 @@
             try
             {
                 connector.stop();
-            } catch (Exception ex) {
+            } 
+            catch (Exception ex) 
+            {
                 LOG.warn(ex);
             }
             _server.removeConnector(connector);
         }
-                _connectors.clear();
-        }
+        _connectors.clear();
+    }
 
-        @Override
+    @Override
     public HttpContext createContext(String path, HttpHandler httpHandler)
     {
         checkIfContextIsFree(path);
@@ -194,7 +217,7 @@
 
         ContextHandlerCollection chc = findContextHandlerCollection(_server.getHandlers());
         if (chc == null)
-                throw new RuntimeException("could not find ContextHandlerCollection, you must configure one");
+            throw new RuntimeException("could not find ContextHandlerCollection, you must configure one");
 
         chc.addHandler(jettyContextHandler);
         _contexts.put(path, context);
@@ -228,13 +251,13 @@
     private void checkIfContextIsFree(String path)
     {
         Handler serverHandler = _server.getHandler();
-                if (serverHandler instanceof ContextHandler)
-                {
-                        ContextHandler ctx = (ContextHandler) serverHandler;
-                        if (ctx.getContextPath().equals(path))
-                        throw new RuntimeException("another context already bound to path " + path);
-                }
-        
+        if (serverHandler instanceof ContextHandler)
+        {
+            ContextHandler ctx = (ContextHandler) serverHandler;
+            if (ctx.getContextPath().equals(path))
+                throw new RuntimeException("another context already bound to path " + path);
+        }
+
         Handler[] handlers = _server.getHandlers();
         if (handlers == null) return;
 
@@ -246,9 +269,9 @@
                     throw new RuntimeException("another context already bound to path " + path);
             }
         }
-        }
+    }
 
-        @Override
+    @Override
     public HttpContext createContext(String path)
     {
         return createContext(path, null);
diff --git a/jetty-http/pom.xml b/jetty-http/pom.xml
index e81fd43..42232a9 100644
--- a/jetty-http/pom.xml
+++ b/jetty-http/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <artifactId>jetty-project</artifactId>
     <groupId>org.eclipse.jetty</groupId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-http</artifactId>
@@ -27,44 +27,16 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",javax.net.*,*</Import-Package>
-              </instructions>
-            </configuration>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
         <executions>
           <execution>
-            <id>artifact-jar</id>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-          <execution>
             <id>test-jar</id>
             <goals>
               <goal>test-jar</goal>
             </goals>
           </execution>
         </executions>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
       </plugin>
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/BadMessageException.java b/jetty-http/src/main/java/org/eclipse/jetty/http/BadMessageException.java
new file mode 100644
index 0000000..daaea17
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/BadMessageException.java
@@ -0,0 +1,76 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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>Exception thrown to indicate a Bad HTTP Message has either been received
+ * or attempted to be generated.  Typically these are handled with either 400
+ * or 500 responses.</p>
+ */
+@SuppressWarnings("serial")
+public class BadMessageException extends RuntimeException
+{
+    final int _code;
+    final String _reason;
+
+    public BadMessageException()
+    {
+        this(400,null);
+    }
+    
+    public BadMessageException(int code)
+    {
+        this(code,null);
+    }
+    
+    public BadMessageException(String reason)
+    {
+        this(400,reason);
+    }
+    
+    public BadMessageException(String reason, Throwable cause)
+    {
+        this(400, reason, cause);
+    }
+    
+    public BadMessageException(int code, String reason)
+    {
+        super(code+": "+reason);
+        _code=code;
+        _reason=reason;
+    }
+    
+    public BadMessageException(int code, String reason, Throwable cause)
+    {
+        super(code+": "+reason, cause);
+        _code=code;
+        _reason=reason;
+    }
+    
+    public int getCode()
+    {
+        return _code;
+    }
+    
+    public String getReason()
+    {
+        return _reason;
+    }
+}
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/DateGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/DateGenerator.java
index 7df9c4f..878a94c 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/DateGenerator.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/DateGenerator.java
@@ -26,7 +26,6 @@
 
 /**
  * ThreadLocal Date formatters for HTTP style dates.
- *
  */
 public class DateGenerator
 {
@@ -56,6 +55,8 @@
     
     /**
      * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
+     * @param date the date in milliseconds
+     * @return the formatted date
      */
     public static String formatDate(long date)
     {
@@ -64,6 +65,8 @@
 
     /**
      * Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies
+     * @param buf the buffer to put the formatted date into
+     * @param date the date in milliseconds
      */
     public static void formatCookieDate(StringBuilder buf, long date)
     {
@@ -72,6 +75,8 @@
 
     /**
      * Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies
+     * @param date the date in milliseconds 
+     * @return the formatted date
      */
     public static String formatCookieDate(long date)
     {
@@ -85,6 +90,8 @@
 
     /**
      * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
+     * @param date the date in milliseconds
+     * @return the formatted date
      */
     public String doFormatDate(long date)
     {
@@ -125,6 +132,8 @@
 
     /**
      * Format "EEE, dd-MMM-yy HH:mm:ss 'GMT'" for cookies
+     * @param buf the buffer to format the date into
+     * @param date the date in milliseconds
      */
     public void doFormatCookieDate(StringBuilder buf, long date)
     {
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/DateParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/DateParser.java
index bd0d0cf..72fff6c 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/DateParser.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/DateParser.java
@@ -27,7 +27,7 @@
  * ThreadLocal data parsers for HTTP style dates
  *
  */
-class DateParser
+public class DateParser
 {
     private static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
     static
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/GzipHttpContent.java b/jetty-http/src/main/java/org/eclipse/jetty/http/GzipHttpContent.java
new file mode 100644
index 0000000..debea68
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/GzipHttpContent.java
@@ -0,0 +1,188 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import org.eclipse.jetty.http.MimeTypes.Type;
+
+import org.eclipse.jetty.util.resource.Resource;
+
+/* ------------------------------------------------------------ */
+public class GzipHttpContent implements HttpContent
+{
+    private final HttpContent _content; 
+    private final HttpContent _contentGz;
+    public final static String ETAG_GZIP="--gzip";
+    public final static String ETAG_GZIP_QUOTE="--gzip\"";
+    public final static PreEncodedHttpField CONTENT_ENCODING_GZIP=new PreEncodedHttpField(HttpHeader.CONTENT_ENCODING,"gzip");
+    
+    public static String removeGzipFromETag(String etag)
+    {
+        if (etag==null)
+            return null;
+        int i = etag.indexOf(ETAG_GZIP_QUOTE);
+        if (i<0)
+            return etag;
+        return etag.substring(0,i)+'"';
+    }
+    
+    public GzipHttpContent(HttpContent content, HttpContent contentGz)
+    {  
+        _content=content;
+        _contentGz=contentGz;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return _content.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        return _content.equals(obj);
+    }
+
+    @Override
+    public Resource getResource()
+    {
+        return _content.getResource();
+    }
+
+    @Override
+    public HttpField getETag()
+    {
+        return new HttpField(HttpHeader.ETAG,getETagValue());
+    }
+
+    @Override
+    public String getETagValue()
+    {
+        return _content.getResource().getWeakETag(ETAG_GZIP);
+    }
+
+    @Override
+    public HttpField getLastModified()
+    {
+        return _content.getLastModified();
+    }
+
+    @Override
+    public String getLastModifiedValue()
+    {
+        return _content.getLastModifiedValue();
+    }
+
+    @Override
+    public HttpField getContentType()
+    {
+        return _content.getContentType();
+    }
+
+    @Override
+    public String getContentTypeValue()
+    {
+        return _content.getContentTypeValue();
+    }
+
+    @Override
+    public HttpField getContentEncoding()
+    {
+        return CONTENT_ENCODING_GZIP;
+    }
+
+    @Override
+    public String getContentEncodingValue()
+    {
+        return CONTENT_ENCODING_GZIP.getValue();
+    }
+
+    @Override
+    public String getCharacterEncoding()
+    {
+        return _content.getCharacterEncoding();
+    }
+
+    @Override
+    public Type getMimeType()
+    {
+        return _content.getMimeType();
+    }
+
+    @Override
+    public void release()
+    {
+        _content.release();
+    }
+
+    @Override
+    public ByteBuffer getIndirectBuffer()
+    {
+        return _contentGz.getIndirectBuffer();
+    }
+
+    @Override
+    public ByteBuffer getDirectBuffer()
+    {
+        return _contentGz.getDirectBuffer();
+    }
+
+    @Override
+    public HttpField getContentLength()
+    {
+        return _contentGz.getContentLength();
+    }
+
+    @Override
+    public long getContentLengthValue()
+    {
+        return _contentGz.getContentLengthValue();
+    }
+
+    @Override
+    public InputStream getInputStream() throws IOException
+    {
+        return _contentGz.getInputStream();
+    }
+
+    @Override
+    public ReadableByteChannel getReadableByteChannel() throws IOException
+    {
+        return _contentGz.getReadableByteChannel();
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("GzipHttpContent@%x{r=%s|%s,lm=%s|%s,ct=%s}",hashCode(),
+                _content.getResource(),_contentGz.getResource(),
+                _content.getResource().lastModified(),_contentGz.getResource().lastModified(),
+                getContentType());
+    }
+
+    @Override
+    public HttpContent getGzipContent()
+    {
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java
new file mode 100644
index 0000000..889bd19
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java
@@ -0,0 +1,79 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.util.HostPort;
+
+
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public class HostPortHttpField extends HttpField
+{
+    final HostPort _hostPort;
+
+    public HostPortHttpField(String authority)
+    {
+        this(HttpHeader.HOST,HttpHeader.HOST.asString(),authority);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected HostPortHttpField(HttpHeader header, String name, String authority)
+    {
+        super(header,name,authority);
+        try
+        {
+            _hostPort=new HostPort(authority);
+        }
+        catch(Exception e)
+        {
+            throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad HostPort",e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the host.
+     * @return the host
+     */
+    public String getHost()
+    {
+        return _hostPort.getHost();
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the port.
+     * @return the port
+     */
+    public int getPort()
+    {
+        return _hostPort.getPort();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the port.
+     * @param defaultPort The default port to return if no port set
+     * @return the port
+     */
+    public int getPort(int defaultPort)
+    {
+        return _hostPort.getPort(defaultPort);
+    }
+}
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/Http1FieldPreEncoder.java b/jetty-http/src/main/java/org/eclipse/jetty/http/Http1FieldPreEncoder.java
new file mode 100644
index 0000000..6fbbb46
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/Http1FieldPreEncoder.java
@@ -0,0 +1,69 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http;
+
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+
+import java.util.Arrays;
+
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public class Http1FieldPreEncoder implements HttpFieldPreEncoder
+{
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.HttpFieldPreEncoder#getHttpVersion()
+     */
+    @Override
+    public HttpVersion getHttpVersion()
+    {
+        return HttpVersion.HTTP_1_0;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.HttpFieldPreEncoder#getEncodedField(org.eclipse.jetty.http.HttpHeader, java.lang.String, java.lang.String)
+     */
+    @Override
+    public byte[] getEncodedField(HttpHeader header, String headerString, String value)
+    {
+        if (header!=null)
+        {
+            int cbl=header.getBytesColonSpace().length;
+            byte[] bytes=Arrays.copyOf(header.getBytesColonSpace(), cbl+value.length()+2);
+            System.arraycopy(value.getBytes(ISO_8859_1),0,bytes,cbl,value.length());
+            bytes[bytes.length-2]=(byte)'\r';
+            bytes[bytes.length-1]=(byte)'\n';
+            return bytes;
+        }
+
+        byte[] n=headerString.getBytes(ISO_8859_1);
+        byte[] v=value.getBytes(ISO_8859_1);
+        byte[] bytes=Arrays.copyOf(n,n.length+2+v.length+2);
+        bytes[n.length]=(byte)':';
+        bytes[n.length]=(byte)' ';
+        bytes[bytes.length-2]=(byte)'\r';
+        bytes[bytes.length-1]=(byte)'\n';
+
+        return bytes;
+    }
+}
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java
new file mode 100644
index 0000000..1da0d42
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java
@@ -0,0 +1,32 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 compliance modes:
+ * <dl>
+ * <dt>RFC7230</dt><dd>(default) Compliance with RFC7230</dd>
+ * <dt>RFC2616</dt><dd>Wrapped/Continued headers and HTTP/0.9 supported</dd>
+ * <dt>LEGACY</dt><dd>(aka STRICT) Adherence to Servlet Specification requirement for 
+ * exact case of header names, bypassing the header caches, which are case insensitive, 
+ * otherwise equivalent to RFC2616</dd>
+ * </dl>
+ */
+public enum HttpCompliance { LEGACY, RFC2616, RFC7230 }
\ No newline at end of file
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java
index 997e895..5637807 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java
@@ -23,156 +23,58 @@
 import java.nio.ByteBuffer;
 import java.nio.channels.ReadableByteChannel;
 
-import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.http.MimeTypes.Type;
 import org.eclipse.jetty.util.resource.Resource;
 
 /* ------------------------------------------------------------ */
-/** HttpContent.
- * 
+/** HttpContent interface.
+ * <p>This information represents all the information about a 
+ * static resource that is needed to evaluate conditional headers
+ * and to serve the content if need be.     It can be implemented
+ * either transiently (values and fields generated on demand) or 
+ * persistently (values and fields pre-generated in anticipation of
+ * reuse in from a cache).
+ * </p> 
  *
  */
 public interface HttpContent
 {
-    String getContentType();
-    String getLastModified();
+    HttpField getContentType();
+    String getContentTypeValue();
+    String getCharacterEncoding();
+    Type getMimeType();
+
+    HttpField getContentEncoding();
+    String getContentEncodingValue();
+    
+    HttpField getContentLength();
+    long getContentLengthValue();
+    
+    HttpField getLastModified();
+    String getLastModifiedValue();
+    
+    HttpField getETag();
+    String getETagValue();
+    
     ByteBuffer getIndirectBuffer();
     ByteBuffer getDirectBuffer();
-    String getETag();
     Resource getResource();
-    long getContentLength();
     InputStream getInputStream() throws IOException;
     ReadableByteChannel getReadableByteChannel() throws IOException;
     void release();
 
-    /* ------------------------------------------------------------ */
-    /* ------------------------------------------------------------ */
-    /* ------------------------------------------------------------ */
-    public class ResourceAsHttpContent implements HttpContent
+    HttpContent getGzipContent();
+    
+    
+    public interface Factory
     {
-        final Resource _resource;
-        final String _mimeType;
-        final int _maxBuffer;
-        final String _etag;
-
-        /* ------------------------------------------------------------ */
-        public ResourceAsHttpContent(final Resource resource, final String mimeType)
-        {
-            this(resource,mimeType,-1,false);
-        }
-
-        /* ------------------------------------------------------------ */
-        public ResourceAsHttpContent(final Resource resource, final String mimeType, int maxBuffer)
-        {
-            this(resource,mimeType,maxBuffer,false);
-        }
-
-        /* ------------------------------------------------------------ */
-        public ResourceAsHttpContent(final Resource resource, final String mimeType, boolean etag)
-        {
-            this(resource,mimeType,-1,etag);
-        }
-
-        /* ------------------------------------------------------------ */
-        public ResourceAsHttpContent(final Resource resource, final String mimeType, int maxBuffer, boolean etag)
-        {
-            _resource=resource;
-            _mimeType=mimeType;
-            _maxBuffer=maxBuffer;
-            _etag=etag?resource.getWeakETag():null;
-        }
-
-        /* ------------------------------------------------------------ */
-        @Override
-        public String getContentType()
-        {
-            return _mimeType;
-        }
-
-        /* ------------------------------------------------------------ */
-        @Override
-        public String getLastModified()
-        {
-            return null;
-        }
-
-        /* ------------------------------------------------------------ */
-        @Override
-        public ByteBuffer getDirectBuffer()
-        {
-            if (_resource.length()<=0 || _maxBuffer<_resource.length())
-                return null;
-            try
-            {
-                return BufferUtil.toBuffer(_resource,true);
-            }
-            catch(IOException e)
-            {
-                throw new RuntimeException(e);
-            }
-        }
-        
-        /* ------------------------------------------------------------ */
-        @Override
-        public String getETag()
-        {
-            return _etag;
-        }
-
-        /* ------------------------------------------------------------ */
-        @Override
-        public ByteBuffer getIndirectBuffer()
-        {
-            if (_resource.length()<=0 || _maxBuffer<_resource.length())
-                return null;
-            try
-            {
-                return BufferUtil.toBuffer(_resource,false);
-            }
-            catch(IOException e)
-            {
-                throw new RuntimeException(e);
-            }
-        }
-
-        /* ------------------------------------------------------------ */
-        @Override
-        public long getContentLength()
-        {
-            return _resource.length();
-        }
-
-        /* ------------------------------------------------------------ */
-        @Override
-        public InputStream getInputStream() throws IOException
-        {
-            return _resource.getInputStream();
-        }
-        
-        /* ------------------------------------------------------------ */
-        @Override
-        public ReadableByteChannel getReadableByteChannel() throws IOException
-        {
-            return _resource.getReadableByteChannel();
-        }
-
-        /* ------------------------------------------------------------ */
-        @Override
-        public Resource getResource()
-        {
-            return _resource;
-        }
-
-        /* ------------------------------------------------------------ */
-        @Override
-        public void release()
-        {
-            _resource.close();
-        }
-        
-        @Override
-        public String toString()
-        {
-            return String.format("%s@%x{r=%s}",this.getClass().getSimpleName(),hashCode(),_resource);
-        }
+        /**
+         * @param path The path within the context to the resource
+         * @param maxBuffer The maximum buffer to allocated for this request.  For cached content, a larger buffer may have
+         * previously been allocated and returned by the {@link HttpContent#getDirectBuffer()} or {@link HttpContent#getIndirectBuffer()} calls.
+         * @return A {@link HttpContent}
+         * @throws IOException IO Exception reading content
+         */
+        HttpContent getContent(String path,int maxBuffer) throws IOException;
     }
 }
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java
index 54f2aad..3ce5ce3 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java
@@ -18,33 +18,38 @@
 
 package org.eclipse.jetty.http;
 
+import java.util.Objects;
 
-/* ------------------------------------------------------------ */
+import org.eclipse.jetty.util.StringUtil;
+
 /** A HTTP Field
  */
 public class HttpField
 {
+    private final static String __zeroquality="q=0";
     private final HttpHeader _header;
     private final String _name;
     private final String _value;
-        
+    // cached hashcode for case insensitive name
+    private int hash = 0;
+
     public HttpField(HttpHeader header, String name, String value)
     {
         _header = header;
         _name = name;
         _value = value;
-    }  
-    
+    }
+
     public HttpField(HttpHeader header, String value)
     {
         this(header,header.asString(),value);
     }
-    
+
     public HttpField(HttpHeader header, HttpHeaderValue value)
     {
         this(header,header.asString(),value.asString());
     }
-    
+
     public HttpField(String name, String value)
     {
         this(HttpHeader.CACHE.get(name),name,value);
@@ -64,7 +69,204 @@
     {
         return _value;
     }
-    
+
+    public int getIntValue()
+    {
+        return Integer.valueOf(_value);
+    }
+
+    public long getLongValue()
+    {
+        return Long.valueOf(_value);
+    }
+
+    public String[] getValues()
+    {
+        if (_value == null)
+            return null;
+        
+        QuotedCSV list = new QuotedCSV(false,_value);
+        return list.getValues().toArray(new String[list.size()]);
+    }
+
+    /**
+     * Look for a value in a possible multi valued field
+     * @param search Values to search for (case insensitive)
+     * @return True iff the value is contained in the field value entirely or
+     * as an element of a quoted comma separated list. List element parameters (eg qualities) are ignored,
+     * except if they are q=0, in which case the item itself is ignored.
+     */
+    public boolean contains(String search)
+    {
+        if (search==null)
+            return _value==null;
+        if (search.length()==0)
+            return false;
+        if (_value==null)
+            return false;
+        if (search.equals(_value))
+            return true;
+
+        search = StringUtil.asciiToLowerCase(search);
+
+        int state=0;
+        int match=0;
+        int param=0;
+
+        for (int i=0;i<_value.length();i++)
+        {
+            char c = _value.charAt(i);
+            switch(state)
+            {
+                case 0: // initial white space
+                    switch(c)
+                    {
+                        case '"': // open quote
+                            match=0;
+                            state=2;
+                            break;
+
+                        case ',': // ignore leading empty field
+                            break;
+
+                        case ';': // ignore leading empty field parameter
+                            param=-1;
+                            match=-1;
+                            state=5;
+                            break;
+
+                        case ' ': // more white space
+                        case '\t':
+                            break;
+
+                        default: // character
+                            match = Character.toLowerCase(c)==search.charAt(0)?1:-1;
+                            state=1;
+                            break;
+                    }
+                    break;
+
+                case 1: // In token
+                    switch(c)
+                    {
+                        case ',': // next field
+                            // Have we matched the token?
+                            if (match==search.length())
+                                return true;
+                            state=0;
+                            break;
+
+                        case ';':
+                            param=match>=0?0:-1;
+                            state=5; // parameter
+                            break;
+
+                        default:
+                            if (match>0)
+                            {
+                                if (match<search.length())
+                                    match=Character.toLowerCase(c)==search.charAt(match)?(match+1):-1;
+                                else if (c!=' ' && c!= '\t')
+                                    match=-1;
+                            }
+                            break;
+
+                    }
+                    break;
+
+                case 2: // In Quoted token
+                    switch(c)
+                    {
+                        case '\\': // quoted character
+                            state=3;
+                            break;
+
+                        case '"': // end quote
+                            state=4;
+                            break;
+
+                        default:
+                            if (match>=0)
+                            {
+                                if (match<search.length())
+                                    match=Character.toLowerCase(c)==search.charAt(match)?(match+1):-1;
+                                else
+                                    match=-1;
+                            }
+                    }
+                    break;
+
+                case 3: // In Quoted character in quoted token
+                    if (match>=0)
+                    {
+                        if (match<search.length())
+                            match=Character.toLowerCase(c)==search.charAt(match)?(match+1):-1;
+                        else
+                            match=-1;
+                    }
+                    state=2;
+                    break;
+
+                case 4: // WS after end quote
+                    switch(c)
+                    {
+                        case ' ': // white space
+                        case '\t': // white space
+                            break;
+
+                        case ';':
+                            state=5; // parameter
+                            break;
+
+                        case ',': // end token
+                            // Have we matched the token?
+                            if (match==search.length())
+                                return true;
+                            state=0;
+                            break;
+
+                        default:
+                            // This is an illegal token, just ignore
+                            match=-1;
+                    }
+                    break;
+
+                case 5:  // parameter
+                    switch(c)
+                    {
+                        case ',': // end token
+                            // Have we matched the token and not q=0?
+                            if (param!=__zeroquality.length() && match==search.length())
+                                return true;
+                            param=0;
+                            state=0;
+                            break;
+
+                        case ' ': // white space
+                        case '\t': // white space
+                            break;
+
+                        default:
+                            if (param>=0)
+                            {
+                                if (param<__zeroquality.length())
+                                    param=Character.toLowerCase(c)==__zeroquality.charAt(param)?(param+1):-1;
+                                else if (c!='0'&&c!='.')
+                                    param=-1;
+                            }
+
+                    }
+                    break;
+
+                default:
+                    throw new IllegalStateException();
+            }
+        }
+
+        return param!=__zeroquality.length() && match==search.length();
+    }
+
+
     @Override
     public String toString()
     {
@@ -72,7 +274,7 @@
         return getName() + ": " + (v==null?"":v);
     }
 
-    public boolean isSame(HttpField field)
+    public boolean isSameName(HttpField field)
     {
         if (field==null)
             return false;
@@ -84,6 +286,126 @@
             return true;
         return false;
     }
-    
-    
+
+    private int nameHashCode()
+    {
+        int h = this.hash;
+        int len = _name.length();
+        if (h == 0 && len > 0)
+        {
+            for (int i = 0; i < len; i++)
+            {
+                // simple case insensitive hash
+                char c = _name.charAt(i);
+                // assuming us-ascii (per last paragraph on http://tools.ietf.org/html/rfc7230#section-3.2.4)
+                if ((c >= 'a' && c <= 'z'))
+                    c -= 0x20;
+                h = 31 * h + c;
+            }
+            this.hash = h;
+        }
+        return h;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int vhc = Objects.hashCode(_value);
+        if (_header==null)
+            return vhc ^ nameHashCode();
+        return vhc ^ _header.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o==this)
+            return true;
+        if (!(o instanceof HttpField))
+            return false;
+        HttpField field=(HttpField)o;
+        if (_header!=field.getHeader())
+            return false;
+        if (!_name.equalsIgnoreCase(field.getName()))
+            return false;
+        if (_value==null && field.getValue()!=null)
+            return false;
+        return Objects.equals(_value,field.getValue());
+    }
+
+    public static class IntValueHttpField extends HttpField
+    {
+        private final int _int;
+
+        public IntValueHttpField(HttpHeader header, String name, String value, int intValue)
+        {
+            super(header,name,value);
+            _int=intValue;
+        }
+
+        public IntValueHttpField(HttpHeader header, String name, String value)
+        {
+            this(header,name,value,Integer.valueOf(value));
+        }
+
+        public IntValueHttpField(HttpHeader header, String name, int intValue)
+        {
+            this(header,name,Integer.toString(intValue),intValue);
+        }
+
+        public IntValueHttpField(HttpHeader header, int value)
+        {
+            this(header,header.asString(),value);
+        }
+
+        @Override
+        public int getIntValue()
+        {
+            return _int;
+        }
+
+        @Override
+        public long getLongValue()
+        {
+            return _int;
+        }
+    }
+
+    public static class LongValueHttpField extends HttpField
+    {
+        private final long _long;
+
+        public LongValueHttpField(HttpHeader header, String name, String value, long longValue)
+        {
+            super(header,name,value);
+            _long=longValue;
+        }
+
+        public LongValueHttpField(HttpHeader header, String name, String value)
+        {
+            this(header,name,value,Long.valueOf(value));
+        }
+
+        public LongValueHttpField(HttpHeader header, String name, long value)
+        {
+            this(header,name,Long.toString(value),value);
+        }
+
+        public LongValueHttpField(HttpHeader header,long value)
+        {
+            this(header,header.asString(),value);
+        }
+
+        @Override
+        public int getIntValue()
+        {
+            return (int)_long;
+        }
+
+        @Override
+        public long getLongValue()
+        {
+            return _long;
+        }
+    }
 }
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFieldPreEncoder.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFieldPreEncoder.java
new file mode 100644
index 0000000..35bd217
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFieldPreEncoder.java
@@ -0,0 +1,36 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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;
+
+
+/* ------------------------------------------------------------ */
+/** Interface to pre-encode HttpFields.  Used by {@link PreEncodedHttpField}
+ */
+public interface HttpFieldPreEncoder
+{
+    /* ------------------------------------------------------------ */
+    /** The major version this encoder is for.  Both HTTP/1.0 and HTTP/1.1
+     * use the same field encoding, so the {@link HttpVersion#HTTP_1_0} should
+     * be return for all HTTP/1.x encodings.
+     * @return The major version this encoder is for.
+     */
+    HttpVersion getHttpVersion();
+    byte[] getEncodedField(HttpHeader header, String headerString, String value);
+}
\ No newline at end of file
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
index 98d0041..a28d5cb 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
@@ -19,7 +19,7 @@
 package org.eclipse.jetty.http;
 
 import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -30,12 +30,11 @@
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.StringTokenizer;
-import java.util.regex.Pattern;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import org.eclipse.jetty.util.ArrayTernaryTrie;
-import org.eclipse.jetty.util.LazyList;
 import org.eclipse.jetty.util.QuotedStringTokenizer;
-import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.Trie;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -52,68 +51,101 @@
  */
 public class HttpFields implements Iterable<HttpField>
 {
+    @Deprecated
+    public static final String __separators = ", \t";
+
     private static final Logger LOG = Log.getLogger(HttpFields.class);
-    private final static Pattern __splitter = Pattern.compile("\\s*,\\s*");     
-    public final static String __separators = ", \t";
 
-    private final ArrayList<HttpField> _fields = new ArrayList<>(20);
-
+    private HttpField[] _fields;
+    private int _size;
+    
     /**
-     * Constructor.
+     * Initialize an empty HttpFields.
      */
     public HttpFields()
     {
+        _fields=new HttpField[20];
+    }
+    
+    /**
+     * Initialize an empty HttpFields.
+     * 
+     * @param capacity the capacity of the http fields
+     */
+    public HttpFields(int capacity)
+    {
+        _fields=new HttpField[capacity];
+    }
+    
+    /**
+     * Initialize HttpFields from copy.
+     * 
+     * @param fields the fields to copy data from
+     */
+    public HttpFields(HttpFields fields)
+    {
+        _fields=Arrays.copyOf(fields._fields,fields._fields.length+10);
+        _size=fields._size;
+    }
+
+    public int size()
+    {
+        return _size;
+    }
+    
+    @Override
+    public Iterator<HttpField> iterator()
+    {
+        return new Itr();
+    }
+    
+    public Stream<HttpField> stream()
+    {
+        return StreamSupport.stream(Arrays.spliterator(_fields,0,_size),false);
     }
 
     /**
      * Get Collection of header names.
+     * @return the unique set of field names.
      */
-    public Collection<String> getFieldNamesCollection()
+    public Set<String> getFieldNamesCollection()
     {
-        final Set<String> list = new HashSet<>(_fields.size());
-        for (HttpField f : _fields)
+        final Set<String> set = new HashSet<>(_size);
+        for (HttpField f : this)
         {
             if (f!=null)
-                list.add(f.getName());
+                set.add(f.getName());
         }
-        return list;
+        return set;
     }
 
     /**
      * Get enumeration of header _names. Returns an enumeration of strings representing the header
      * _names for this request.
+     * @return an enumeration of field names
      */
     public Enumeration<String> getFieldNames()
     {
         return Collections.enumeration(getFieldNamesCollection());
     }
 
-    public int size()
-    {
-        return _fields.size();
-    }
-
     /**
      * Get a Field by index.
+     * @param index the field index 
      * @return A Field value or null if the Field value has not been set
-     *
      */
-    public HttpField getField(int i)
+    public HttpField getField(int index)
     {
-        return _fields.get(i);
-    }
-
-    @Override
-    public Iterator<HttpField> iterator()
-    {
-        return _fields.iterator();
+        if (index>=_size)
+            throw new NoSuchElementException();
+        return _fields[index];
     }
 
     public HttpField getField(HttpHeader header)
     {
-        for (int i=0;i<_fields.size();i++)
+        for (int i=0;i<_size;i++)
         {
-            HttpField f=_fields.get(i);
+            HttpField f=_fields[i];
             if (f.getHeader()==header)
                 return f;
         }
@@ -122,21 +154,32 @@
 
     public HttpField getField(String name)
     {
-        for (int i=0;i<_fields.size();i++)
+        for (int i=0;i<_size;i++)
         {
-            HttpField f=_fields.get(i);
+            HttpField f=_fields[i];
             if (f.getName().equalsIgnoreCase(name))
                 return f;
         }
         return null;
     }
-    
+
+    public boolean contains(HttpField field)
+    {
+        for (int i=_size;i-->0;)
+        {
+            HttpField f=_fields[i];
+            if (f.isSameName(field) && (f.equals(field)||f.contains(field.getValue())))
+                return true;
+        }
+        return false;
+    }
+
     public boolean contains(HttpHeader header, String value)
     {
-        for (int i=0;i<_fields.size();i++)
+        for (int i=_size;i-->0;)
         {
-            HttpField f=_fields.get(i);
-            if (f.getHeader()==header && contains(f,value))
+            HttpField f=_fields[i];
+            if (f.getHeader()==header && f.contains(value))
                 return true;
         }
         return false;
@@ -144,39 +187,20 @@
     
     public boolean contains(String name, String value)
     {
-        for (int i=0;i<_fields.size();i++)
+        for (int i=_size;i-->0;)
         {
-            HttpField f=_fields.get(i);
-            if (f.getName().equalsIgnoreCase(name) && contains(f,value))
+            HttpField f=_fields[i];
+            if (f.getName().equalsIgnoreCase(name) && f.contains(value))
                 return true;
         }
         return false;
     }
-    
-    private boolean contains(HttpField field,String value)
-    {
-        String v = field.getValue();
-        if (v==null)
-            return false;
-
-        if (value.equalsIgnoreCase(v))
-            return true;
-
-        String[] split = __splitter.split(v);
-        for (int i = 0; split!=null && i < split.length; i++) 
-        {
-            if (value.equals(split[i]))
-                return true;
-        }
-
-        return false;
-    }
 
     public boolean contains(HttpHeader header)
     {
-        for (int i=0;i<_fields.size();i++)
+        for (int i=_size;i-->0;)
         {
-            HttpField f=_fields.get(i);
+            HttpField f=_fields[i];
             if (f.getHeader()==header)
                 return true;
         }
@@ -185,57 +209,264 @@
     
     public boolean containsKey(String name)
     {
-        for (int i=0;i<_fields.size();i++)
+        for (int i=_size;i-->0;)
         {
-            HttpField f=_fields.get(i);
+            HttpField f=_fields[i];
             if (f.getName().equalsIgnoreCase(name))
                 return true;
         }
         return false;
     }
-    
-    
+
+    @Deprecated
     public String getStringField(HttpHeader header)
     {
-        return getStringField(header.asString());
+        return get(header);
     }
-
+    
     public String get(HttpHeader header)
     {
-        return getStringField(header.asString());
+        for (int i=0;i<_size;i++)
+        {
+            HttpField f=_fields[i];
+            if (f.getHeader()==header)
+                return f.getValue();
+        }
+        return null;
     }
 
-    public String get(String header)
-    {
-        return getStringField(header);
-    }
-
-    /**
-     * @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
-     */
+    @Deprecated
     public String getStringField(String name)
     {
-        HttpField field = getField(name);
-        return field==null?null:field.getValue();
+        return get(name);
+    }
+    
+    public String get(String header)
+    {
+        for (int i=0;i<_size;i++)
+        {
+            HttpField f=_fields[i];
+            if (f.getName().equalsIgnoreCase(header))
+                return f.getValue();
+        }
+        return null;
     }
 
     /**
-     * Get multi headers
+     * Get multiple header of the same name
      *
      * @return List the values
+     * @param header the header
+     */
+    public List<String> getValuesList(HttpHeader header)
+    {
+        final List<String> list = new ArrayList<>();
+        for (HttpField f : this)
+            if (f.getHeader()==header)
+                list.add(f.getValue());
+        return list;
+    }
+    
+    /**
+     * Get multiple header of the same name
+     *    
+     * @return List the header values
      * @param name the case-insensitive field name
      */
     public List<String> getValuesList(String name)
     {
         final List<String> list = new ArrayList<>();
-        for (HttpField f : _fields)
+        for (HttpField f : this)
             if (f.getName().equalsIgnoreCase(name))
                 list.add(f.getValue());
         return list;
     }
 
+
+    /**
+     * Add comma separated values, but only if not already
+     * present.
+     * @param header The header to add the value(s) to
+     * @param values The value(s) to add
+     * @return True if headers were modified
+     */
+    public boolean addCSV(HttpHeader header,String... values)
+    {
+        QuotedCSV existing = null;
+        for (HttpField f : this)
+        {
+            if (f.getHeader()==header)
+            {
+                if (existing==null)
+                    existing = new QuotedCSV(false);
+                existing.addValue(f.getValue());
+            }
+        }
+        
+        String value = addCSV(existing,values);
+        if (value!=null)
+        {
+            add(header,value);
+            return true;
+        }
+        return false;
+    }
+    
+    /**
+     * Add comma separated values, but only if not already
+     * present.
+     * @param name The header to add the value(s) to
+     * @param values The value(s) to add
+     * @return True if headers were modified
+     */
+    public boolean addCSV(String name,String... values)
+    {
+        QuotedCSV existing = null;
+        for (HttpField f : this)
+        {
+            if (f.getName().equalsIgnoreCase(name))
+            {
+                if (existing==null)
+                    existing = new QuotedCSV(false);
+                existing.addValue(f.getValue());
+            }
+        }
+        String value = addCSV(existing,values);
+        if (value!=null)
+        {
+            add(name,value);
+            return true;
+        }
+        return false;
+    }
+
+    protected String addCSV(QuotedCSV existing,String... values)
+    {
+        // remove any existing values from the new values
+        boolean add = true;
+        if (existing!=null && !existing.isEmpty())
+        {
+            add = false;
+        
+            for (int i=values.length;i-->0;)
+            {
+                String unquoted = QuotedCSV.unquote(values[i]);
+                if (existing.getValues().contains(unquoted))
+                    values[i] = null;
+                else
+                    add = true;
+            }
+        }
+            
+        if (add)
+        {
+            StringBuilder value = new StringBuilder();
+            for (String v:values)
+            {
+                if (v==null)
+                    continue;
+                if (value.length()>0)
+                    value.append(", ");
+                value.append(v);
+            }
+            if (value.length()>0)
+                return value.toString();
+        }
+        
+        return null;
+    }
+    
+    /**
+     * Get multiple field values of the same name, split 
+     * as a {@link QuotedCSV}
+     *
+     * @return List the values with OWS stripped
+     * @param header The header
+     * @param keepQuotes True if the fields are kept quoted
+     */
+    public List<String> getCSV(HttpHeader header,boolean keepQuotes)
+    {
+        QuotedCSV values = null;
+        for (HttpField f : this)
+        {
+            if (f.getHeader()==header)
+            {
+                if (values==null)
+                    values = new QuotedCSV(keepQuotes);
+                values.addValue(f.getValue());
+            }
+        }
+        return values==null?Collections.emptyList():values.getValues();
+    }
+
+    /**
+     * Get multiple field values of the same name
+     * as a {@link QuotedCSV}
+     *
+     * @return List the values with OWS stripped
+     * @param name the case-insensitive field name
+     * @param keepQuotes True if the fields are kept quoted
+     */
+    public List<String> getCSV(String name,boolean keepQuotes)
+    {
+        QuotedCSV values = null;
+        for (HttpField f : this)
+        {
+            if (f.getName().equalsIgnoreCase(name))
+            {
+                if (values==null)
+                    values = new QuotedCSV(keepQuotes);
+                values.addValue(f.getValue());
+            }
+        }
+        return values==null?Collections.emptyList():values.getValues();
+    }
+
+    /**
+     * Get multiple field values of the same name, split and
+     * sorted as a {@link QuotedQualityCSV}
+     *
+     * @return List the values in quality order with the q param and OWS stripped
+     * @param header The header
+     */
+    public List<String> getQualityCSV(HttpHeader header)
+    {
+        QuotedQualityCSV values = null;
+        for (HttpField f : this)
+        {
+            if (f.getHeader()==header)
+            {
+                if (values==null)
+                    values = new QuotedQualityCSV();
+                values.addValue(f.getValue());
+            }
+        }
+
+        return values==null?Collections.emptyList():values.getValues();
+    }
+
+    /**
+     * Get multiple field values of the same name, split and
+     * sorted as a {@link QuotedQualityCSV}
+     *
+     * @return List the values in quality order with the q param and OWS stripped
+     * @param name the case-insensitive field name
+     */
+    public List<String> getQualityCSV(String name)
+    {
+        QuotedQualityCSV values = null;
+        for (HttpField f : this)
+        {
+            if (f.getName().equalsIgnoreCase(name))
+            {
+                if (values==null)
+                    values = new QuotedQualityCSV();
+                values.addValue(f.getValue());
+            }
+        }
+        return values==null?Collections.emptyList():values.getValues();
+    }
+
     /**
      * Get multi headers
      *
@@ -244,9 +475,9 @@
      */
     public Enumeration<String> getValues(final String name)
     {
-        for (int i=0;i<_fields.size();i++)
+        for (int i=0;i<_size;i++)
         {
-            final HttpField f = _fields.get(i);
+            final HttpField f = _fields[i];
             
             if (f.getName().equalsIgnoreCase(name) && f.getValue()!=null)
             {
@@ -261,9 +492,9 @@
                     {
                         if (field==null)
                         {
-                            while (i<_fields.size()) 
+                            while (i<_size) 
                             {
-                                field=_fields.get(i++);
+                                field=_fields[i++];
                                 if (field.getName().equalsIgnoreCase(name) && field.getValue()!=null)
                                     return true;
                             }
@@ -284,7 +515,6 @@
                         }
                         throw new NoSuchElementException();
                     }
-
                 };
             }
         }
@@ -302,6 +532,7 @@
      * @param separators String of separators.
      * @return Enumeration of the values, or null if no such header.
      */
+    @Deprecated
     public Enumeration<String> getValues(String name, final String separators)
     {
         final Enumeration<String> e = getValues(name);
@@ -342,22 +573,24 @@
     public void put(HttpField field)
     {
         boolean put=false;
-        for (int i=_fields.size();i-->0;)
+        for (int i=_size;i-->0;)
         {
-            HttpField f=_fields.get(i);
-            if (f.isSame(field))
+            HttpField f=_fields[i];
+            if (f.isSameName(field))
             {
                 if (put)
-                    _fields.remove(i);
+                {
+                    System.arraycopy(_fields,i+1,_fields,i,--_size-i);
+                }
                 else
                 {
-                    _fields.set(i,field);
+                    _fields[i]=field;
                     put=true;
                 }
             }
         }
         if (!put)
-            _fields.add(field);
+            add(field);
     }
     
     /**
@@ -413,19 +646,17 @@
      *
      * @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
+    public void add(String name, String value)
     {
         if (value == null)
             return;
 
         HttpField field = new HttpField(name, value);
-        _fields.add(field);
+        add(field);
     }
 
-    public void add(HttpHeader header, HttpHeaderValue value) throws IllegalArgumentException
+    public void add(HttpHeader header, HttpHeaderValue value)
     {
         add(header,value.toString());
     }
@@ -436,46 +667,55 @@
      *
      * @param header the header
      * @param value the value of the field.
-     * @exception IllegalArgumentException 
      */
-    public void add(HttpHeader header, String value) throws IllegalArgumentException
+    public void add(HttpHeader header, String value)
     {
         if (value == null) throw new IllegalArgumentException("null value");
 
         HttpField field = new HttpField(header, value);
-        _fields.add(field);
+        add(field);
     }
 
     /**
      * Remove a field.
      *
      * @param name the field to remove
+     * @return the header that was removed
      */
     public HttpField remove(HttpHeader name)
     {
-        for (int i=_fields.size();i-->0;)
+        HttpField removed=null;
+        for (int i=_size;i-->0;)
         {
-            HttpField f=_fields.get(i);
+            HttpField f=_fields[i];
             if (f.getHeader()==name)
-                return _fields.remove(i);
+            {
+                removed=f;
+                System.arraycopy(_fields,i+1,_fields,i,--_size-i);
+            }
         }
-        return null;
+        return removed;
     }
 
     /**
      * Remove a field.
      *
      * @param name the field to remove
+     * @return the header that was removed
      */
     public HttpField remove(String name)
     {
-        for (int i=_fields.size();i-->0;)
+        HttpField removed=null;
+        for (int i=_size;i-->0;)
         {
-            HttpField f=_fields.get(i);
+            HttpField f=_fields[i];
             if (f.getName().equalsIgnoreCase(name))
-                return _fields.remove(i);
+            {
+                removed=f;
+                System.arraycopy(_fields,i+1,_fields,i,--_size-i);
+            }
         }
-        return null;
+        return removed;
     }
 
     /**
@@ -483,12 +723,13 @@
      * case of the field name is ignored.
      *
      * @param name the case-insensitive field name
+     * @return the value of the field as a long
      * @exception NumberFormatException If bad long found
      */
     public long getLongField(String name) throws NumberFormatException
     {
         HttpField field = getField(name);
-        return field==null?-1L:StringUtil.toLong(field.getValue());
+        return field==null?-1L:field.getLongValue();
     }
 
     /**
@@ -496,6 +737,7 @@
      * of the field name is ignored.
      *
      * @param name the case-insensitive field name
+     * @return the value of the field as a number of milliseconds since unix epoch
      */
     public long getDateField(String name)
     {
@@ -576,13 +818,47 @@
     }
 
     @Override
-    public String
-    toString()
+    public int hashCode()
+    {
+        int hash=0;
+        for (HttpField field:_fields)
+            hash+=field.hashCode();
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (this == o)
+            return true;
+        if (!(o instanceof HttpFields))
+            return false;
+
+        HttpFields that = (HttpFields)o;
+
+        // Order is not important, so we cannot rely on List.equals().
+        if (size() != that.size())
+            return false;
+
+        loop: for (HttpField fi : this)
+        {
+            for (HttpField fa : that)
+            {
+                if (fi.equals(fa))
+                    continue loop;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString()
     {
         try
         {
             StringBuilder buffer = new StringBuilder();
-            for (HttpField field : _fields)
+            for (HttpField field : this)
             {
                 if (field != null)
                 {
@@ -604,21 +880,27 @@
         }
     }
 
-    /**
-     * Clear the header.
-     */
     public void clear()
     {
-        _fields.clear();
+        _size=0;
     }
-
+    
     public void add(HttpField field)
     {
-        _fields.add(field);
+        if (field!=null)
+        {
+            if (_size==_fields.length)
+                _fields=Arrays.copyOf(_fields,_size*2);
+            _fields[_size++]=field;
+        }
     }
 
-    
-    
+    public void addAll(HttpFields fields)
+    {
+        for (int i=0;i<fields._size;i++)
+            add(fields._fields[i]);
+    }
+
     /**
      * Add fields from another HttpFields instance. Single valued fields are replaced, while all
      * others are added.
@@ -640,6 +922,28 @@
     }
 
     /**
+     * Get field value without 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 parameters.
+     * @return The value.
+     */
+    public static String stripParameters(String value)
+    {
+        if (value == null) return null;
+
+        int i = value.indexOf(';');
+        if (i < 0) return value;
+        return value.substring(0, i).trim();
+    }
+
+    /**
      * 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:
      *
@@ -649,7 +953,7 @@
      *
      * </PRE>
      *
-     * @param value The Field value, possibly with parameteres.
+     * @param value The Field value, possibly with parameters.
      * @param parameters A map to populate with the parameters, or null
      * @return The value.
      */
@@ -678,8 +982,11 @@
         return value.substring(0, i).trim();
     }
 
+    @Deprecated
     private static final Float __one = new Float("1.0");
+    @Deprecated
     private static final Float __zero = new Float("0.0");
+    @Deprecated
     private static final Trie<Float> __qualities = new ArrayTernaryTrie<>();
     static
     {
@@ -701,6 +1008,7 @@
         __qualities.put("0.0", __zero);
     }
 
+    @Deprecated
     public static Float getQuality(String value)
     {
         if (value == null) return __zero;
@@ -742,55 +1050,47 @@
      * @param e Enumeration of values with quality parameters
      * @return values in quality order.
      */
+    @Deprecated
     public static List<String> qualityList(Enumeration<String> e)
     {
         if (e == null || !e.hasMoreElements())
             return Collections.emptyList();
 
-        Object list = null;
-        Object qual = null;
-
-        // Assume list will be well ordered and just add nonzero
-        while (e.hasMoreElements())
-        {
-            String v = e.nextElement();
-            Float q = getQuality(v);
-
-            if (q >= 0.001)
-            {
-                list = LazyList.add(list, v);
-                qual = LazyList.add(qual, q);
-            }
-        }
-
-        List<String> vl = LazyList.getList(list, false);
-        if (vl.size() < 2) 
-            return vl;
-
-        List<Float> ql = LazyList.getList(qual, false);
-
-        // sort list with swaps
-        Float last = __zero;
-        for (int i = vl.size(); i-- > 0;)
-        {
-            Float q = ql.get(i);
-            if (last.compareTo(q) > 0)
-            {
-                String 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;
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        while(e.hasMoreElements())
+            values.addValue(e.nextElement());
+        return values.getValues();
     }
 
 
+    private class Itr implements Iterator<HttpField> 
+    {
+        int _cursor;       // index of next element to return
+        int _last=-1;
+
+        public boolean hasNext() 
+        {
+            return _cursor != _size;
+        }
+
+        public HttpField next() 
+        {
+            int i = _cursor;
+            if (i >= _size)
+                throw new NoSuchElementException();
+            _cursor = i + 1;
+            return _fields[_last=i];
+        }
+
+        public void remove() 
+        {
+            if (_last<0)
+                throw new IllegalStateException();
+
+            System.arraycopy(_fields,_last+1,_fields,_last,--_size-_last);
+            _cursor=_last;
+            _last=-1;
+        }
+    }
 
 }
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java
index 98d96a0..e59b981 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java
@@ -21,7 +21,6 @@
 import java.io.IOException;
 import java.nio.BufferOverflowException;
 import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
@@ -32,28 +31,26 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-/* ------------------------------------------------------------ */
 /**
  * HttpGenerator. Builds HTTP Messages.
- *
+ * <p>
  * If the system property "org.eclipse.jetty.http.HttpGenerator.STRICT" is set to true,
  * then the generator will strictly pass on the exact strings received from methods and header
  * fields.  Otherwise a fast case insensitive string lookup is used that may alter the
  * case and white space of some methods/headers
- * </p>
  */
 public class HttpGenerator
 {
     private final static Logger LOG = Log.getLogger(HttpGenerator.class);
 
-    public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpGenerator.STRICT"); 
+    public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpGenerator.STRICT");
 
     private final static byte[] __colon_space = new byte[] {':',' '};
     private final static HttpHeaderValue[] CLOSE = {HttpHeaderValue.CLOSE};
-    public static final ResponseInfo CONTINUE_100_INFO = new ResponseInfo(HttpVersion.HTTP_1_1,null,-1,100,null,false);
-    public static final ResponseInfo PROGRESS_102_INFO = new ResponseInfo(HttpVersion.HTTP_1_1,null,-1,102,null,false);
-    public final static ResponseInfo RESPONSE_500_INFO =
-        new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(){{put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);}},0,HttpStatus.INTERNAL_SERVER_ERROR_500,null,false);
+    public static final MetaData.Response CONTINUE_100_INFO = new MetaData.Response(HttpVersion.HTTP_1_1,100,null,null,-1);
+    public static final MetaData.Response PROGRESS_102_INFO = new MetaData.Response(HttpVersion.HTTP_1_1,102,null,null,-1);
+    public final static MetaData.Response RESPONSE_500_INFO =
+        new MetaData.Response(HttpVersion.HTTP_1_1,HttpStatus.INTERNAL_SERVER_ERROR_500,null,new HttpFields(){{put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);}},0);
 
     // states
     public enum State { START, COMMITTED, COMPLETING, COMPLETING_1XX, END }
@@ -92,7 +89,7 @@
     {
         this(false,false);
     }
-    
+
     /* ------------------------------------------------------------------------------- */
     public HttpGenerator(boolean sendServerVersion,boolean sendXPoweredBy)
     {
@@ -165,7 +162,7 @@
     {
         return _noContent;
     }
-    
+
     /* ------------------------------------------------------------ */
     public void setPersistent(boolean persistent)
     {
@@ -202,7 +199,7 @@
     }
 
     /* ------------------------------------------------------------ */
-    public Result generateRequest(RequestInfo info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
+    public Result generateRequest(MetaData.Request info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
     {
         switch(_state)
         {
@@ -211,13 +208,16 @@
                 if (info==null)
                     return Result.NEED_INFO;
 
-                // Do we need a request header
                 if (header==null)
                     return Result.NEED_HEADER;
 
                 // If we have not been told our persistence, set the default
                 if (_persistent==null)
-                    _persistent=(info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
+                {
+                    _persistent=info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal();
+                    if (!_persistent && HttpMethod.CONNECT.is(info.getMethod()))
+                        _persistent=true;
+                }
 
                 // prepare the header
                 int pos=BufferUtil.flipToFill(header);
@@ -227,11 +227,11 @@
                     generateRequestLine(info,header);
 
                     if (info.getHttpVersion()==HttpVersion.HTTP_0_9)
-                        _noContent=true;
-                    else
-                        generateHeaders(info,header,content,last);
+                        throw new BadMessageException(500,"HTTP/0.9 not supported");
+                    
+                    generateHeaders(info,header,content,last);
 
-                    boolean expect100 = info.getHttpFields().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
+                    boolean expect100 = info.getFields().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
 
                     if (expect100)
                     {
@@ -254,8 +254,8 @@
                 }
                 catch(Exception e)
                 {
-                    String message= (e instanceof BufferOverflowException)?"Response header too large":e.getMessage();
-                    throw new IOException(message,e);
+                    String message= (e instanceof BufferOverflowException)?"Request header too large":e.getMessage();
+                    throw new BadMessageException(500,message,e);
                 }
                 finally
                 {
@@ -283,12 +283,9 @@
                 }
 
                 if (last)
-                {
                     _state=State.COMPLETING;
-                    return len>0?Result.FLUSH:Result.CONTINUE;
-                }
 
-                return Result.FLUSH;
+                return len>0?Result.FLUSH:Result.CONTINUE;
             }
 
             case COMPLETING:
@@ -331,7 +328,13 @@
     }
 
     /* ------------------------------------------------------------ */
-    public Result generateResponse(ResponseInfo info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
+    public Result generateResponse(MetaData.Response info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
+    {
+        return generateResponse(info,false,header,chunk,content,last);
+    }
+
+    /* ------------------------------------------------------------ */
+    public Result generateResponse(MetaData.Response info, boolean head, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
     {
         switch(_state)
         {
@@ -339,26 +342,34 @@
             {
                 if (info==null)
                     return Result.NEED_INFO;
-
-                // Handle 0.9
-                if (info.getHttpVersion() == HttpVersion.HTTP_0_9)
+                HttpVersion version=info.getHttpVersion();
+                if (version==null)
+                    throw new BadMessageException(500,"No version");
+                switch(version)
                 {
-                    _persistent = false;
-                    _endOfContent=EndOfContent.EOF_CONTENT;
-                    if (BufferUtil.hasContent(content))
-                        _contentPrepared+=content.remaining();
-                    _state = last?State.COMPLETING:State.COMMITTED;
-                    return Result.FLUSH;
+                    case HTTP_1_0:
+                        if (_persistent==null)
+                            _persistent=Boolean.FALSE;
+                        break;
+                        
+                    case HTTP_1_1:
+                        if (_persistent==null)
+                            _persistent=Boolean.TRUE;
+                        break;
+                        
+                    default:
+                        _persistent = false;
+                        _endOfContent=EndOfContent.EOF_CONTENT;
+                        if (BufferUtil.hasContent(content))
+                            _contentPrepared+=content.remaining();
+                        _state = last?State.COMPLETING:State.COMMITTED;
+                        return Result.FLUSH;
                 }
-
+                
                 // Do we need a response header
                 if (header==null)
                     return Result.NEED_HEADER;
 
-                // If we have not been told our persistence, set the default
-                if (_persistent==null)
-                    _persistent=(info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
-
                 // prepare the header
                 int pos=BufferUtil.flipToFill(header);
                 try
@@ -391,7 +402,7 @@
                     if (len>0)
                     {
                         _contentPrepared+=len;
-                        if (isChunking() && !info.isHead())
+                        if (isChunking() && !head)
                             prepareChunk(header,len);
                     }
                     _state = last?State.COMPLETING:State.COMMITTED;
@@ -399,7 +410,7 @@
                 catch(Exception e)
                 {
                     String message= (e instanceof BufferOverflowException)?"Response header too large":e.getMessage();
-                    throw new IOException(message,e);
+                    throw new BadMessageException(500,message,e);
                 }
                 finally
                 {
@@ -506,26 +517,18 @@
     }
 
     /* ------------------------------------------------------------ */
-    private void generateRequestLine(RequestInfo request,ByteBuffer header)
+    private void generateRequestLine(MetaData.Request request,ByteBuffer header)
     {
         header.put(StringUtil.getBytes(request.getMethod()));
         header.put((byte)' ');
-        header.put(StringUtil.getBytes(request.getUri()));
-        switch(request.getHttpVersion())
-        {
-            case HTTP_1_0:
-            case HTTP_1_1:
-                header.put((byte)' ');
-                header.put(request.getHttpVersion().toBytes());
-                break;
-            default:
-                throw new IllegalStateException();
-        }
+        header.put(StringUtil.getBytes(request.getURIString()));
+        header.put((byte)' ');
+        header.put(request.getHttpVersion().toBytes());
         header.put(HttpTokens.CRLF);
     }
 
     /* ------------------------------------------------------------ */
-    private void generateResponseLine(ResponseInfo response, ByteBuffer header)
+    private void generateResponseLine(MetaData.Response response, ByteBuffer header)
     {
         // Look for prepared response line
         int status=response.getStatus();
@@ -575,10 +578,10 @@
     }
 
     /* ------------------------------------------------------------ */
-    private void generateHeaders(Info _info,ByteBuffer header,ByteBuffer content,boolean last)
+    private void generateHeaders(MetaData _info,ByteBuffer header,ByteBuffer content,boolean last)
     {
-        final RequestInfo request=(_info instanceof RequestInfo)?(RequestInfo)_info:null;
-        final ResponseInfo response=(_info instanceof ResponseInfo)?(ResponseInfo)_info:null;
+        final MetaData.Request request=(_info instanceof MetaData.Request)?(MetaData.Request)_info:null;
+        final MetaData.Response response=(_info instanceof MetaData.Response)?(MetaData.Response)_info:null;
 
         // default field values
         int send=_send;
@@ -587,122 +590,132 @@
         boolean close=false;
         boolean content_type=false;
         StringBuilder connection = null;
+        long content_length = _info.getContentLength();
 
         // Generate fields
-        if (_info.getHttpFields() != null)
+        HttpFields fields = _info.getFields();
+        if (fields != null)
         {
-            for (HttpField field : _info.getHttpFields())
+            int n=fields.size();
+            for (int f=0;f<n;f++)
             {
+                HttpField field = fields.getField(f);
+                String v = field.getValue();
+                if (v==null || v.length()==0)
+                    continue; // rfc7230 does not allow no value
+
                 HttpHeader h = field.getHeader();
-
-                switch (h==null?HttpHeader.UNKNOWN:h)
+                if (h==null)
+                    putTo(field,header);
+                else
                 {
-                    case CONTENT_LENGTH:
-                        // handle specially below
-                        if (_info.getContentLength()>=0)
+                    switch (h)
+                    {
+                        case CONTENT_LENGTH:
                             _endOfContent=EndOfContent.CONTENT_LENGTH;
-                        break;
+                            if (content_length<0)
+                                content_length=Long.valueOf(field.getValue());
+                            // handle setting the field specially below
+                            break;
 
-                    case CONTENT_TYPE:
-                    {
-                        if (field.getValue().startsWith(MimeTypes.Type.MULTIPART_BYTERANGES.toString()))
-                            _endOfContent=EndOfContent.SELF_DEFINING_CONTENT;
-
-                        // write the field to the header
-                        content_type=true;
-                        putTo(field,header);
-                        break;
-                    }
-
-                    case TRANSFER_ENCODING:
-                    {
-                        if (_info.getHttpVersion() == HttpVersion.HTTP_1_1)
-                            transfer_encoding = field;
-                        // Do NOT add yet!
-                        break;
-                    }
-
-                    case CONNECTION:
-                    {
-                        if (request!=null)
+                        case CONTENT_TYPE:
+                        {
+                            // write the field to the header
+                            content_type=true;
                             putTo(field,header);
+                            break;
+                        }
 
-                        // Lookup and/or split connection value field
-                        HttpHeaderValue[] values = HttpHeaderValue.CLOSE.is(field.getValue())?CLOSE:new HttpHeaderValue[]{HttpHeaderValue.CACHE.get(field.getValue())};
-                        String[] split = null;
-
-                        if (values[0]==null)
+                        case TRANSFER_ENCODING:
                         {
+                            if (_info.getHttpVersion() == HttpVersion.HTTP_1_1)
+                                transfer_encoding = field;
+                            // Do NOT add yet!
+                            break;
+                        }
+
+                        case CONNECTION:
+                        {
+                            if (request!=null)
+                                putTo(field,header);
+
+                            // Lookup and/or split connection value field
+                            HttpHeaderValue[] values = HttpHeaderValue.CLOSE.is(field.getValue())?CLOSE:new HttpHeaderValue[]{HttpHeaderValue.CACHE.get(field.getValue())};
+                            String[] split = null;
+
+                            if (values[0]==null)
+                            {
                             split = StringUtil.csvSplit(field.getValue());
-                            if (split.length>0)
-                            {
-                                values=new HttpHeaderValue[split.length];
-                                for (int i=0;i<split.length;i++)
-                                    values[i]=HttpHeaderValue.CACHE.get(split[i]);
-                            }
-                        }
-
-                        // Handle connection values
-                        for (int i=0;i<values.length;i++)
-                        {
-                            HttpHeaderValue value=values[i];
-                            switch (value==null?HttpHeaderValue.UNKNOWN:value)
-                            {
-                                case UPGRADE:
+                                if (split.length>0)
                                 {
-                                    // special case for websocket connection ordering
-                                    header.put(HttpHeader.CONNECTION.getBytesColonSpace()).put(HttpHeader.UPGRADE.getBytes());
-                                    header.put(CRLF);
-                                    break;
+                                    values=new HttpHeaderValue[split.length];
+                                    for (int i=0;i<split.length;i++)
+                                        values[i]=HttpHeaderValue.CACHE.get(split[i]);
                                 }
+                            }
 
-                                case CLOSE:
+                            // Handle connection values
+                            for (int i=0;i<values.length;i++)
+                            {
+                                HttpHeaderValue value=values[i];
+                                switch (value==null?HttpHeaderValue.UNKNOWN:value)
                                 {
-                                    close=true;
-                                    if (response!=null)
+                                    case UPGRADE:
                                     {
+                                        // special case for websocket connection ordering
+                                        header.put(HttpHeader.CONNECTION.getBytesColonSpace()).put(HttpHeader.UPGRADE.getBytes());
+                                        header.put(CRLF);
+                                        break;
+                                    }
+
+                                    case CLOSE:
+                                    {
+                                        close=true;
                                         _persistent=false;
-                                        if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
-                                            _endOfContent=EndOfContent.EOF_CONTENT;
-                                    }
-                                    break;
-                                }
-
-                                case KEEP_ALIVE:
-                                {
-                                    if (_info.getHttpVersion() == HttpVersion.HTTP_1_0)
-                                    {
-                                        keep_alive = true;
                                         if (response!=null)
-                                            _persistent=true;
+                                        {
+                                            if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
+                                                _endOfContent=EndOfContent.EOF_CONTENT;
+                                        }
+                                        break;
                                     }
-                                    break;
-                                }
 
-                                default:
-                                {
-                                    if (connection==null)
-                                        connection=new StringBuilder();
-                                    else
-                                        connection.append(',');
-                                    connection.append(split==null?field.getValue():split[i]);
+                                    case KEEP_ALIVE:
+                                    {
+                                        if (_info.getHttpVersion() == HttpVersion.HTTP_1_0)
+                                        {
+                                            keep_alive = true;
+                                            if (response!=null)
+                                                _persistent=true;
+                                        }
+                                        break;
+                                    }
+
+                                    default:
+                                    {
+                                        if (connection==null)
+                                            connection=new StringBuilder();
+                                        else
+                                            connection.append(',');
+                                        connection.append(split==null?field.getValue():split[i]);
+                                    }
                                 }
                             }
+
+                            // Do NOT add yet!
+                            break;
                         }
 
-                        // Do NOT add yet!
-                        break;
-                    }
+                        case SERVER:
+                        {
+                            send=send&~SEND_SERVER;
+                            putTo(field,header);
+                            break;
+                        }
 
-                    case SERVER:
-                    {
-                        send=send&~SEND_SERVER;
-                        putTo(field,header);
-                        break;
+                        default:
+                            putTo(field,header);
                     }
-
-                    default:
-                        putTo(field,header);
                 }
             }
         }
@@ -710,13 +723,15 @@
 
         // Calculate how to end _content and connection, _content length and transfer encoding
         // settings.
+        // From http://tools.ietf.org/html/rfc7230#section-3.3.3
         // 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
+        // 3. If Transfer-Encoding==(.*,)?chunked && HTTP/1.1 && !HttpConnection==close then chunk
+        // 5. Content-Length without Transfer-Encoding
+        // 6. Request and none over the above, then Content-Length=0 if POST/PUT
+        // 7. close
+        
+        
         int status=response!=null?response.getStatus():-1;
         switch (_endOfContent)
         {
@@ -725,13 +740,12 @@
                 // written yet?
 
                 // Response known not to have a body
-                if (_contentPrepared == 0 && response!=null && (status < 200 || status == 204 || status == 304))
+                if (_contentPrepared == 0 && response!=null && _noContent)
                     _endOfContent=EndOfContent.NO_CONTENT;
                 else if (_info.getContentLength()>0)
                 {
                     // we have been given a content length
                     _endOfContent=EndOfContent.CONTENT_LENGTH;
-                    long content_length = _info.getContentLength();
                     if ((response!=null || content_length>0 || content_type ) && !_noContent)
                     {
                         // known length but not actually set.
@@ -744,20 +758,13 @@
                 {
                     // we have seen all the _content there is, so we can be content-length limited.
                     _endOfContent=EndOfContent.CONTENT_LENGTH;
-                    long content_length = _contentPrepared+BufferUtil.length(content);
+                    long actual_length = _contentPrepared+BufferUtil.length(content);
 
+                    if (content_length>=0 && content_length!=actual_length)
+                        throw new BadMessageException(500,"Content-Length header("+content_length+") != actual("+actual_length+")");
+                    
                     // Do we need to tell the headers about it
-                    if (content_length>0)
-                    {
-                        header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
-                        BufferUtil.putDecLong(header, content_length);
-                        header.put(HttpTokens.CRLF);
-                    }
-                    else if (!_noContent)
-                    {
-                        if (content_type || response!=null || (request!=null && __assumedContentMethods.contains(request.getMethod())))
-                            header.put(CONTENT_LENGTH_0);
-                    }
+                    putContentLength(header,actual_length,content_type,request,response);
                 }
                 else
                 {
@@ -773,22 +780,13 @@
                 break;
 
             case CONTENT_LENGTH:
-                long content_length = _info.getContentLength();
-                if (content_length>0)
-                {
-                    header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
-                    BufferUtil.putDecLong(header, content_length);
-                    header.put(HttpTokens.CRLF);
-                }
-                else if (!_noContent)
-                {
-                    if (content_type || response!=null || (request!=null && __assumedContentMethods.contains(request.getMethod())))
-                        header.put(CONTENT_LENGTH_0);
-                }
+            {
+                putContentLength(header,content_length,content_type,request,response);
                 break;
+            }
 
             case NO_CONTENT:
-                throw new IllegalStateException();
+                throw new BadMessageException(500);
 
             case EOF_CONTENT:
                 _persistent = request!=null;
@@ -811,7 +809,7 @@
                 if (c.endsWith(HttpHeaderValue.CHUNKED.toString()))
                     putTo(transfer_encoding,header);
                 else
-                    throw new IllegalArgumentException("BAD TE");
+                    throw new BadMessageException(500,"BAD TE");
             }
             else
                 header.put(TRANSFER_ENCODING_CHUNKED);
@@ -867,6 +865,22 @@
     }
 
     /* ------------------------------------------------------------------------------- */
+    private void putContentLength(ByteBuffer header, long contentLength, boolean contentType, MetaData.Request request, MetaData.Response response)
+    {
+        if (contentLength>0)
+        {
+            header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
+            BufferUtil.putDecLong(header, contentLength);
+            header.put(HttpTokens.CRLF);
+        }
+        else if (!_noContent)
+        {
+            if (contentType || response!=null || (request!=null && __assumedContentMethods.contains(request.getMethod())))
+                header.put(CONTENT_LENGTH_0);
+        }
+    }
+
+    /* ------------------------------------------------------------------------------- */
     public static byte[] getReasonBuffer(int code)
     {
         PreparedResponse status = code<__preprepared.length?__preprepared[code]:null;
@@ -879,8 +893,9 @@
     @Override
     public String toString()
     {
-        return String.format("%s{s=%s}",
+        return String.format("%s@%x{s=%s}",
                 getClass().getSimpleName(),
+                hashCode(),
                 _state);
     }
 
@@ -942,110 +957,13 @@
         }
     }
 
-    public static class Info
-    {
-        final HttpVersion _httpVersion;
-        final HttpFields _httpFields;
-        final long _contentLength;
-
-        private Info(HttpVersion httpVersion, HttpFields httpFields, long contentLength)
-        {
-            _httpVersion = httpVersion;
-            _httpFields = httpFields;
-            _contentLength = contentLength;
-        }
-
-        public HttpVersion getHttpVersion()
-        {
-            return _httpVersion;
-        }
-        public HttpFields getHttpFields()
-        {
-            return _httpFields;
-        }
-        public long getContentLength()
-        {
-            return _contentLength;
-        }
-    }
-
-    public static class RequestInfo extends Info
-    {
-        private final String _method;
-        private final String _uri;
-
-        public RequestInfo(HttpVersion httpVersion, HttpFields httpFields, long contentLength, String method, String uri)
-        {
-            super(httpVersion,httpFields,contentLength);
-            _method = method;
-            _uri = uri;
-        }
-
-        public String getMethod()
-        {
-            return _method;
-        }
-
-        public String getUri()
-        {
-            return _uri;
-        }
-
-        @Override
-        public String toString()
-        {
-            return String.format("RequestInfo{%s %s %s,%d}",_method,_uri,_httpVersion,_contentLength);
-        }
-    }
-
-    public static class ResponseInfo extends Info
-    {
-        private final int _status;
-        private final String _reason;
-        private final boolean _head;
-
-        public ResponseInfo(HttpVersion httpVersion, HttpFields httpFields, long contentLength, int status, String reason, boolean head)
-        {
-            super(httpVersion,httpFields,contentLength);
-            _status = status;
-            _reason = reason;
-            _head = head;
-        }
-
-        public boolean isInformational()
-        {
-            return _status>=100 && _status<200;
-        }
-
-        public int getStatus()
-        {
-            return _status;
-        }
-
-        public String getReason()
-        {
-            return _reason;
-        }
-
-        public boolean isHead()
-        {
-            return _head;
-        }
-
-        @Override
-        public String toString()
-        {
-            return String.format("ResponseInfo{%s %s %s,%d,%b}",_httpVersion,_status,_reason,_contentLength,_head);
-        }
-    } 
-
     private static void putSanitisedName(String s,ByteBuffer buffer)
     {
         int l=s.length();
         for (int i=0;i<l;i++)
         {
             char c=s.charAt(i);
-            
+
             if (c<0 || c>0xff || c=='\r' || c=='\n'|| c==':')
                 buffer.put((byte)'?');
             else
@@ -1059,7 +977,7 @@
         for (int i=0;i<l;i++)
         {
             char c=s.charAt(i);
-            
+
             if (c<0 || c>0xff || c=='\r' || c=='\n')
                 buffer.put((byte)' ');
             else
@@ -1069,9 +987,9 @@
 
     public static void putTo(HttpField field, ByteBuffer bufferInFillMode)
     {
-        if (field instanceof CachedHttpField)
+        if (field instanceof PreEncodedHttpField)
         {
-            ((CachedHttpField)field).putTo(bufferInFillMode);
+            ((PreEncodedHttpField)field).putTo(bufferInFillMode,HttpVersion.HTTP_1_0);
         }
         else
         {
@@ -1092,7 +1010,7 @@
         }
     }
 
-    public static void putTo(HttpFields fields, ByteBuffer bufferInFillMode) 
+    public static void putTo(HttpFields fields, ByteBuffer bufferInFillMode)
     {
         for (HttpField field : fields)
         {
@@ -1101,23 +1019,4 @@
         }
         BufferUtil.putCRLF(bufferInFillMode);
     }
-    
-    public static class CachedHttpField extends HttpField
-    {
-        private final byte[] _bytes;
-        public CachedHttpField(HttpHeader header,String value)
-        {
-            super(header,value);
-            int cbl=header.getBytesColonSpace().length;
-            _bytes=Arrays.copyOf(header.getBytesColonSpace(), cbl+value.length()+2);
-            System.arraycopy(value.getBytes(StandardCharsets.ISO_8859_1),0,_bytes,cbl,value.length());
-            _bytes[_bytes.length-2]=(byte)'\r';
-            _bytes[_bytes.length-1]=(byte)'\n';
-        }
-        
-        public void putTo(ByteBuffer bufferInFillMode)
-        {
-            bufferInFillMode.put(_bytes);
-        }
-    }
 }
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java
index a83fd58..1272102 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java
@@ -110,17 +110,30 @@
     IDENTITY("identity"),
     
     X_POWERED_BY("X-Powered-By"),
+    HTTP2_SETTINGS("HTTP2-Settings"),
 
+    STRICT_TRANSPORT_SECURITY("Strict-Transport-Security"),
+    
+    /* ------------------------------------------------------------ */
+    /** HTTP2 Fields.
+     */
+    C_METHOD(":method"),
+    C_SCHEME(":scheme"),
+    C_AUTHORITY(":authority"),
+    C_PATH(":path"),
+    C_STATUS(":status"),
+    
     UNKNOWN("::UNKNOWN::");
 
 
     /* ------------------------------------------------------------ */
-    public final static Trie<HttpHeader> CACHE= new ArrayTrie<>(512);
+    public final static Trie<HttpHeader> CACHE= new ArrayTrie<>(560);
     static
     {
         for (HttpHeader header : HttpHeader.values())
             if (header!=UNKNOWN)
-                CACHE.put(header.toString(),header);
+                if (!CACHE.put(header.toString(),header))
+                    throw new IllegalStateException();
     }
     
     private final String _string;
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
index e044bcb..d0a252b 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
@@ -18,67 +18,80 @@
 
 package org.eclipse.jetty.http;
 
-import static org.eclipse.jetty.http.HttpTokens.*;
-
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Locale;
 
 import org.eclipse.jetty.http.HttpTokens.EndOfContent;
 import org.eclipse.jetty.util.ArrayTernaryTrie;
 import org.eclipse.jetty.util.ArrayTrie;
 import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.HostPort;
-import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.Trie;
 import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.Utf8StringBuilder;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
+import static org.eclipse.jetty.http.HttpCompliance.LEGACY;
+import static org.eclipse.jetty.http.HttpCompliance.RFC2616;
+import static org.eclipse.jetty.http.HttpCompliance.RFC7230;
+import static org.eclipse.jetty.http.HttpTokens.CARRIAGE_RETURN;
+import static org.eclipse.jetty.http.HttpTokens.LINE_FEED;
+import static org.eclipse.jetty.http.HttpTokens.SPACE;
+import static org.eclipse.jetty.http.HttpTokens.TAB;
+
 
 /* ------------------------------------------------------------ */
-/** A Parser for HTTP 0.9, 1.0 and 1.1
+/** A Parser for 1.0 and 1.1 as defined by RFC7230
  * <p>
  * This parser parses HTTP client and server messages from buffers
  * passed in the {@link #parseNext(ByteBuffer)} method.  The parsed
- * elements of the HTTP message are passed as event calls to the 
+ * elements of the HTTP message are passed as event calls to the
  * {@link HttpHandler} instance the parser is constructed with.
  * If the passed handler is a {@link RequestHandler} then server side
- * parsing is performed and if it is a {@link ResponseHandler}, then 
+ * parsing is performed and if it is a {@link ResponseHandler}, then
  * client side parsing is done.
  * </p>
  * <p>
- * The contract of the {@link HttpHandler} API is that if a call returns 
- * true then the call to {@link #parseNext(ByteBuffer)} will return as 
+ * The contract of the {@link HttpHandler} API is that if a call returns
+ * true then the call to {@link #parseNext(ByteBuffer)} will return as
  * soon as possible also with a true response.  Typically this indicates
- * that the parsing has reached a stage where the caller should process 
+ * that the parsing has reached a stage where the caller should process
  * the events accumulated by the handler.    It is the preferred calling
- * style that handling such as calling a servlet to process a request, 
+ * style that handling such as calling a servlet to process a request,
  * should be done after a true return from {@link #parseNext(ByteBuffer)}
- * rather than from within the scope of a call like 
+ * rather than from within the scope of a call like
  * {@link RequestHandler#messageComplete()}
  * </p>
  * <p>
- * For performance, the parse is heavily dependent on the 
+ * For performance, the parse is heavily dependent on the
  * {@link Trie#getBest(ByteBuffer, int, int)} method to look ahead in a
  * single pass for both the structure ( : and CRLF ) and semantic (which
  * header and value) of a header.  Specifically the static {@link HttpHeader#CACHE}
- * is used to lookup common combinations of headers and values 
+ * is used to lookup common combinations of headers and values
  * (eg. "Connection: close"), or just header names (eg. "Connection:" ).
  * For headers who's value is not known statically (eg. Host, COOKIE) then a
  * per parser dynamic Trie of {@link HttpFields} from previous parsed messages
  * is used to help the parsing of subsequent messages.
  * </p>
  * <p>
- * If the system property "org.eclipse.jetty.http.HttpParser.STRICT" is set to true,
- * then the parser will strictly pass on the exact strings received for methods and header
- * fields.  Otherwise a fast case insensitive string lookup is used that may alter the
- * case of the method and/or headers
- * </p>
+ * The parser can work in varying compliance modes:
+ * <dl>
+ * <dt>RFC7230</dt><dd>(default) Compliance with RFC7230</dd>
+ * <dt>RFC2616</dt><dd>Wrapped headers and HTTP/0.9 supported</dd>
+ * <dt>LEGACY</dt><dd>(aka STRICT) Adherence to Servlet Specification requirement for
+ * exact case of header names, bypassing the header caches, which are case insensitive,
+ * otherwise equivalent to RFC2616</dd>
+ * </dl>
+ * @see <a href="http://tools.ietf.org/html/rfc7230">RFC 7230</a>
  */
 public class HttpParser
 {
     public static final Logger LOG = Log.getLogger(HttpParser.class);
-    public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpParser.STRICT"); 
+    @Deprecated
+    public final static String __STRICT="org.eclipse.jetty.http.HttpParser.STRICT";
     public final static int INITIAL_URI_LENGTH=256;
 
     /**
@@ -94,7 +107,7 @@
      * </ul>
      */
     public final static Trie<HttpField> CACHE = new ArrayTrie<>(2048);
-    
+
     // States
     public enum State
     {
@@ -118,17 +131,23 @@
         CHUNK_SIZE,
         CHUNK_PARAMS,
         CHUNK,
+        CHUNK_TRAILER,
         CHUNK_END,
         END,
-        CLOSED
+        CLOSE,  // The associated stream/endpoint should be closed
+        CLOSED  // The associated stream/endpoint is at EOF
     }
 
+    private final static EnumSet<State> __idleStates = EnumSet.of(State.START,State.END,State.CLOSE,State.CLOSED);
+    private final static EnumSet<State> __completeStates = EnumSet.of(State.END,State.CLOSE,State.CLOSED);
+
     private final boolean DEBUG=LOG.isDebugEnabled(); // Cache debug to help branch prediction
-    private final HttpHandler<ByteBuffer> _handler;
-    private final RequestHandler<ByteBuffer> _requestHandler;
-    private final ResponseHandler<ByteBuffer> _responseHandler;
+    private final HttpHandler _handler;
+    private final RequestHandler _requestHandler;
+    private final ResponseHandler _responseHandler;
+    private final ComplianceHandler _complianceHandler;
     private final int _maxHeaderBytes;
-    private final boolean _strict;
+    private final HttpCompliance _compliance;
     private HttpField _field;
     private HttpHeader _header;
     private String _headerString;
@@ -137,15 +156,15 @@
     private int _responseStatus;
     private int _headerBytes;
     private boolean _host;
+    private boolean _headerComplete;
 
     /* ------------------------------------------------------------------------------- */
     private volatile State _state=State.START;
     private volatile boolean _eof;
-    private volatile boolean _closed;
     private HttpMethod _method;
     private String _methodString;
     private HttpVersion _version;
-    private ByteBuffer _uri=ByteBuffer.allocate(INITIAL_URI_LENGTH); // Tune?
+    private Utf8StringBuilder _uri=new Utf8StringBuilder(INITIAL_URI_LENGTH); // Tune?
     private EndOfContent _endOfContent;
     private long _contentLength;
     private long _contentPosition;
@@ -181,20 +200,22 @@
         CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"deflate"));
         CACHE.put(new HttpField(HttpHeader.TRANSFER_ENCODING,"chunked"));
         CACHE.put(new HttpField(HttpHeader.EXPIRES,"Fri, 01 Jan 1990 00:00:00 GMT"));
-        
+
         // Add common Content types as fields
         for (String type : new String[]{"text/plain","text/html","text/xml","text/json","application/json","application/x-www-form-urlencoded"})
         {
-            HttpField field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type);
+            HttpField field=new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type);
             CACHE.put(field);
-            
-            for (String charset : new String[]{"UTF-8","ISO-8859-1"})
+
+            for (String charset : new String[]{"utf-8","iso-8859-1"})
             {
-                CACHE.put(new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset));
-                CACHE.put(new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type+"; charset="+charset));
+                CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset));
+                CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type+"; charset="+charset));
+                CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset.toUpperCase(Locale.ENGLISH)));
+                CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type+"; charset="+charset.toUpperCase(Locale.ENGLISH)));
             }
         }
-    
+
         // Add headers with null values so HttpParser can avoid looking up name again for unknown values
         for (HttpHeader h:HttpHeader.values())
             if (!CACHE.put(new HttpField(h,(String)null)))
@@ -207,51 +228,109 @@
         CACHE.put(new HttpField(HttpHeader.COOKIE,(String)null));
     }
 
-    /* ------------------------------------------------------------------------------- */
-    public HttpParser(RequestHandler<ByteBuffer> handler)
+    private static HttpCompliance compliance()
     {
-        this(handler,-1,__STRICT);
+        Boolean strict = Boolean.getBoolean(__STRICT);
+        return strict?HttpCompliance.LEGACY:HttpCompliance.RFC7230;
     }
 
     /* ------------------------------------------------------------------------------- */
-    public HttpParser(ResponseHandler<ByteBuffer> handler)
+    public HttpParser(RequestHandler handler)
     {
-        this(handler,-1,__STRICT);
+        this(handler,-1,compliance());
     }
 
     /* ------------------------------------------------------------------------------- */
-    public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes)
+    public HttpParser(ResponseHandler handler)
     {
-        this(handler,maxHeaderBytes,__STRICT);
+        this(handler,-1,compliance());
     }
 
     /* ------------------------------------------------------------------------------- */
-    public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes)
+    public HttpParser(RequestHandler handler,int maxHeaderBytes)
     {
-        this(handler,maxHeaderBytes,__STRICT);
+        this(handler,maxHeaderBytes,compliance());
     }
-    
+
     /* ------------------------------------------------------------------------------- */
-    public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict)
+    public HttpParser(ResponseHandler handler,int maxHeaderBytes)
+    {
+        this(handler,maxHeaderBytes,compliance());
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    @Deprecated
+    public HttpParser(RequestHandler handler,int maxHeaderBytes,boolean strict)
+    {
+        this(handler,maxHeaderBytes,strict?HttpCompliance.LEGACY:compliance());
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    @Deprecated
+    public HttpParser(ResponseHandler handler,int maxHeaderBytes,boolean strict)
+    {
+        this(handler,maxHeaderBytes,strict?HttpCompliance.LEGACY:compliance());
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public HttpParser(RequestHandler handler,HttpCompliance compliance)
+    {
+        this(handler,-1,compliance);
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public HttpParser(RequestHandler handler,int maxHeaderBytes,HttpCompliance compliance)
     {
         _handler=handler;
         _requestHandler=handler;
         _responseHandler=null;
         _maxHeaderBytes=maxHeaderBytes;
-        _strict=strict;
+        _compliance=compliance==null?compliance():compliance;
+        _complianceHandler=(ComplianceHandler)(handler instanceof ComplianceHandler?handler:null);
     }
 
     /* ------------------------------------------------------------------------------- */
-    public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict)
+    public HttpParser(ResponseHandler handler,int maxHeaderBytes,HttpCompliance compliance)
     {
         _handler=handler;
         _requestHandler=null;
         _responseHandler=handler;
         _maxHeaderBytes=maxHeaderBytes;
-        _strict=strict;
+        _compliance=compliance==null?compliance():compliance;
+        _complianceHandler=(ComplianceHandler)(handler instanceof ComplianceHandler?handler:null);
     }
 
     /* ------------------------------------------------------------------------------- */
+    public HttpHandler getHandler()
+    {
+        return _handler;
+    }
+    
+    /* ------------------------------------------------------------------------------- */
+    /** Check RFC compliance violation
+     * @param compliance The compliance level violated
+     * @param reason The reason for the violation
+     * @return True if the current compliance level is set so as to Not allow this violation
+     */
+    protected boolean complianceViolation(HttpCompliance compliance,String reason)
+    {
+        if (_complianceHandler==null)
+            return _compliance.ordinal()>=compliance.ordinal();
+        if (_compliance.ordinal()<compliance.ordinal())
+        {
+            _complianceHandler.onComplianceViolation(_compliance,compliance,reason);
+            return false;
+        }
+        return true;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    protected String legacyString(String orig, String cached)
+    {                   
+        return (_compliance!=LEGACY || orig.equals(cached) || complianceViolation(RFC2616,"case sensitive"))?cached:orig;
+    }
+    
+    /* ------------------------------------------------------------------------------- */
     public long getContentLength()
     {
         return _contentLength;
@@ -265,7 +344,7 @@
 
     /* ------------------------------------------------------------ */
     /** Set if a HEAD response is expected
-     * @param head
+     * @param head true if head response is expected
      */
     public void setHeadResponse(boolean head)
     {
@@ -309,6 +388,12 @@
     }
 
     /* ------------------------------------------------------------ */
+    public boolean isClose()
+    {
+        return isState(State.CLOSE);
+    }
+
+    /* ------------------------------------------------------------ */
     public boolean isClosed()
     {
         return isState(State.CLOSED);
@@ -317,13 +402,13 @@
     /* ------------------------------------------------------------ */
     public boolean isIdle()
     {
-        return isState(State.START)||isState(State.END)||isState(State.CLOSED);
+        return __idleStates.contains(_state);
     }
 
     /* ------------------------------------------------------------ */
     public boolean isComplete()
     {
-        return isState(State.END)||isState(State.CLOSED);
+        return __completeStates.contains(_state);
     }
 
     /* ------------------------------------------------------------------------------- */
@@ -333,80 +418,103 @@
     }
 
     /* ------------------------------------------------------------------------------- */
-    @SuppressWarnings("serial")
-    private static class BadMessageException extends RuntimeException
+    enum CharState { ILLEGAL, CR, LF, LEGAL }
+    private final static CharState[] __charState;
+    static
     {
-        private final int _code;
+        // token          = 1*tchar
+        // tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
+        //                / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
+        //                / DIGIT / ALPHA
+        //                ; any VCHAR, except delimiters
+        // quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE
+        // qdtext         = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
+        // obs-text       = %x80-FF
+        // comment        = "(" *( ctext / quoted-pair / comment ) ")"
+        // ctext          = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
+        // quoted-pair    = "\" ( HTAB / SP / VCHAR / obs-text )
 
-        private BadMessageException()
-        {
-            this(400,null);
-        }
-        
-        private BadMessageException(int code)
-        {
-            this(code,null);
-        }
-        
-        private BadMessageException(String message)
-        {
-            this(400,message);
-        }
-        
-        private BadMessageException(int code,String message)
-        {
-            super(message);
-            _code=code;
-        }
+        __charState=new CharState[256];
+        Arrays.fill(__charState,CharState.ILLEGAL);
+        __charState[LINE_FEED]=CharState.LF;
+        __charState[CARRIAGE_RETURN]=CharState.CR;
+        __charState[TAB]=CharState.LEGAL;
+        __charState[SPACE]=CharState.LEGAL;
+
+        __charState['!']=CharState.LEGAL;
+        __charState['#']=CharState.LEGAL;
+        __charState['$']=CharState.LEGAL;
+        __charState['%']=CharState.LEGAL;
+        __charState['&']=CharState.LEGAL;
+        __charState['\'']=CharState.LEGAL;
+        __charState['*']=CharState.LEGAL;
+        __charState['+']=CharState.LEGAL;
+        __charState['-']=CharState.LEGAL;
+        __charState['.']=CharState.LEGAL;
+        __charState['^']=CharState.LEGAL;
+        __charState['_']=CharState.LEGAL;
+        __charState['`']=CharState.LEGAL;
+        __charState['|']=CharState.LEGAL;
+        __charState['~']=CharState.LEGAL;
+
+        __charState['"']=CharState.LEGAL;
+
+        __charState['\\']=CharState.LEGAL;
+        __charState['(']=CharState.LEGAL;
+        __charState[')']=CharState.LEGAL;
+        Arrays.fill(__charState,0x21,0x27+1,CharState.LEGAL);
+        Arrays.fill(__charState,0x2A,0x5B+1,CharState.LEGAL);
+        Arrays.fill(__charState,0x5D,0x7E+1,CharState.LEGAL);
+        Arrays.fill(__charState,0x80,0xFF+1,CharState.LEGAL);
+
     }
-    
+
     /* ------------------------------------------------------------------------------- */
     private byte next(ByteBuffer buffer)
     {
         byte ch = buffer.get();
-        
-        if (_cr)
-        {
-            if (ch!=LINE_FEED)
-                throw new BadMessageException("Bad EOL");
-            _cr=false;
-            return ch;
-        }
 
-        if (ch>=0 && ch<SPACE)
+        CharState s = __charState[0xff & ch];
+        switch(s)
         {
-            if (ch==CARRIAGE_RETURN)
-            {
+            case ILLEGAL:
+                throw new IllegalCharacterException(_state,ch,buffer);
+
+            case LF:
+                _cr=false;
+                break;
+
+            case CR:
+                if (_cr)
+                    throw new BadMessageException("Bad EOL");
+
+                _cr=true;
                 if (buffer.hasRemaining())
                 {
                     if(_maxHeaderBytes>0 && _state.ordinal()<State.END.ordinal())
                         _headerBytes++;
-                    ch=buffer.get();
-                    if (ch!=LINE_FEED)
-                        throw new BadMessageException("Bad EOL");
+                    return next(buffer);
                 }
-                else
-                {
-                    _cr=true;
-                    // Can return 0 here to indicate the need for more characters, 
-                    // because a real 0 in the buffer would cause a BadMessage below 
-                    return 0;
-                }
-            }
-            // Only LF or TAB acceptable special characters
-            else if (!(ch==LINE_FEED || ch==TAB))
-                throw new IllegalCharacterException(_state,ch,buffer);
+
+                // Can return 0 here to indicate the need for more characters,
+                // because a real 0 in the buffer would cause a BadMessage below
+                return 0;
+
+            case LEGAL:
+                if (_cr)
+                    throw new BadMessageException("Bad EOL");
+
         }
-        
+
         return ch;
     }
-    
+
     /* ------------------------------------------------------------------------------- */
     /* Quick lookahead for the start state looking for a request method or a HTTP version,
      * otherwise skip white space until something else to parse.
      */
     private boolean quickStart(ByteBuffer buffer)
-    {    	
+    {
         if (_requestHandler!=null)
         {
             _method = HttpMethod.lookAheadGet(buffer);
@@ -414,7 +522,7 @@
             {
                 _methodString = _method.asString();
                 buffer.position(buffer.position()+_methodString.length()+1);
-                
+
                 setState(State.SPACE1);
                 return false;
             }
@@ -429,7 +537,7 @@
                 return false;
             }
         }
-        
+
         // Quick start look
         while (_state==State.START && buffer.hasRemaining())
         {
@@ -446,7 +554,7 @@
                 break;
             else if (ch<0)
                 throw new BadMessageException();
-            
+
             // count this white space as a header byte to avoid DOS
             if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
             {
@@ -464,7 +572,7 @@
         _string.append(s);
         _length=s.length();
     }
-    
+
     /* ------------------------------------------------------------------------------- */
     private String takeString()
     {
@@ -474,7 +582,25 @@
         _length=-1;
         return s;
     }
+    
+    /* ------------------------------------------------------------------------------- */
+    private boolean handleHeaderContentMessage()
+    {
+        boolean handle_header = _handler.headerComplete();
+        boolean handle_content = _handler.contentComplete();
+        boolean handle_message = _handler.messageComplete();
+        return handle_header || handle_content || handle_message;
+    }
+    
+    /* ------------------------------------------------------------------------------- */
+    private boolean handleContentMessage()
+    {
+        boolean handle_content = _handler.contentComplete();
+        boolean handle_message = _handler.messageComplete();
+        return handle_content || handle_message;
+    }
 
+    
     /* ------------------------------------------------------------------------------- */
     /* Parse a request or response line
      */
@@ -495,7 +621,7 @@
                 if (_state==State.URI)
                 {
                     LOG.warn("URI is too large >"+_maxHeaderBytes);
-                    throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
+                    throw new BadMessageException(HttpStatus.URI_TOO_LONG_414);
                 }
                 else
                 {
@@ -503,7 +629,7 @@
                         LOG.warn("request is too large >"+_maxHeaderBytes);
                     else
                         LOG.warn("response is too large >"+_maxHeaderBytes);
-                    throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
+                    throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431);
                 }
             }
 
@@ -515,8 +641,8 @@
                         _length=_string.length();
                         _methodString=takeString();
                         HttpMethod method=HttpMethod.CACHE.get(_methodString);
-                        if (method!=null && !_strict)
-                            _methodString=method.asString();
+                        if (method!=null)
+                            _methodString=legacyString(_methodString,method.asString());
                         setState(State.SPACE1);
                     }
                     else if (ch < SPACE)
@@ -556,7 +682,7 @@
                         }
                         else
                         {
-                            _uri.clear();
+                            _uri.reset();
                             setState(State.URI);
                             // quick scan for space or EoBuffer
                             if (buffer.hasArray())
@@ -570,24 +696,17 @@
 
                                 int len=i-p;
                                 _headerBytes+=len;
-                                
+
                                 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
                                 {
                                     LOG.warn("URI is too large >"+_maxHeaderBytes);
-                                    throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
+                                    throw new BadMessageException(HttpStatus.URI_TOO_LONG_414);
                                 }
-                                if (_uri.remaining()<=len)
-                                {
-                                    ByteBuffer uri = ByteBuffer.allocate(_uri.capacity()+2*len);
-                                    _uri.flip();
-                                    uri.put(_uri);
-                                    _uri=uri;
-                                }
-                                _uri.put(array,p-1,len+1);
+                                _uri.append(array,p-1,len+1);
                                 buffer.position(i-buffer.arrayOffset());
                             }
                             else
-                                _uri.put(ch);
+                                _uri.append(ch);
                         }
                     }
                     else if (ch < HttpTokens.SPACE)
@@ -607,8 +726,8 @@
                     }
                     else if (ch < HttpTokens.SPACE && ch>=0)
                     {
-                        handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
                         setState(State.HEADER);
+                        handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
                     }
                     else
                     {
@@ -624,24 +743,16 @@
                     else if (ch < HttpTokens.SPACE && ch>=0)
                     {
                         // HTTP/0.9
-                        _uri.flip();
-                        handle=_requestHandler.startRequest(_method,_methodString,_uri,null)||handle;
+                        if (complianceViolation(RFC7230,"HTTP/0.9"))
+                            throw new BadMessageException("HTTP/0.9 not supported");
+                        handle=_requestHandler.startRequest(_methodString,_uri.toString(), HttpVersion.HTTP_0_9);
                         setState(State.END);
                         BufferUtil.clear(buffer);
-                        handle=_handler.headerComplete()||handle;
-                        handle=_handler.messageComplete()||handle;
-                        return handle;
+                        handle = handleHeaderContentMessage() || handle;
                     }
                     else
                     {
-                        if (!_uri.hasRemaining())
-                        {
-                            ByteBuffer uri = ByteBuffer.allocate(_uri.capacity()*2);
-                            _uri.flip();
-                            uri.put(_uri);
-                            _uri=uri;
-                        }
-                        _uri.put(ch);
+                        _uri.append(ch);
                     }
                     break;
 
@@ -665,29 +776,8 @@
                                 version=HttpVersion.lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.arrayOffset()+buffer.limit());
                             else
                                 version=HttpVersion.CACHE.getBest(buffer,0,buffer.remaining());
-                            if (version==null)
-                            {
-                                if (_method==HttpMethod.PROXY)
-                                {
-                                    if (!(_requestHandler instanceof ProxyHandler))
-                                        throw new BadMessageException();
-                                    
-                                    _uri.flip();
-                                    String protocol=BufferUtil.toString(_uri);
-                                    // This is the proxy protocol, so we can assume entire first line is in buffer else 400
-                                    buffer.position(buffer.position()-1);
-                                    String sAddr = getProxyField(buffer);
-                                    String dAddr = getProxyField(buffer);
-                                    int sPort = BufferUtil.takeInt(buffer);
-                                    next(buffer);
-                                    int dPort = BufferUtil.takeInt(buffer);
-                                    next(buffer);
-                                    _state=State.START;
-                                    ((ProxyHandler)_requestHandler).proxied(protocol,sAddr,dAddr,sPort,dPort);
-                                    return false;
-                                }
-                            }
-                            else
+
+                            if (version!=null)
                             {
                                 int pos = buffer.position()+version.asString().length()-1;
                                 if (pos<buffer.limit())
@@ -714,19 +804,19 @@
                     {
                         if (_responseHandler!=null)
                         {
-                            handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
                             setState(State.HEADER);
+                            handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
                         }
                         else
                         {
                             // HTTP/0.9
-                            _uri.flip();
-                            handle=_requestHandler.startRequest(_method,_methodString,_uri, null)||handle;
+                            if (complianceViolation(RFC7230,"HTTP/0.9"))
+                                throw new BadMessageException("HTTP/0.9 not supported");
+
+                            handle=_requestHandler.startRequest(_methodString,_uri.toString(), HttpVersion.HTTP_0_9);
                             setState(State.END);
                             BufferUtil.clear(buffer);
-                            handle=_handler.headerComplete()||handle;
-                            handle=_handler.messageComplete()||handle;
-                            return handle;
+                            handle = handleHeaderContentMessage() || handle;
                         }
                     }
                     else if (ch<0)
@@ -743,17 +833,17 @@
                         }
                         if (_version==null)
                             throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
-                        
+
                         // Should we try to cache header fields?
-                        if (_connectionFields==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
+                        if (_connectionFields==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion() && _handler.getHeaderCacheSize()>0)
                         {
                             int header_cache = _handler.getHeaderCacheSize();
-                            _connectionFields=new ArrayTernaryTrie<>(header_cache);                            
+                            _connectionFields=new ArrayTernaryTrie<>(header_cache);
                         }
 
                         setState(State.HEADER);
-                        _uri.flip();
-                        handle=_requestHandler.startRequest(_method,_methodString,_uri, _version)||handle;
+
+                        handle=_requestHandler.startRequest(_methodString,_uri.toString(), _version)||handle;
                         continue;
                     }
                     else if (ch>=HttpTokens.SPACE)
@@ -767,7 +857,6 @@
                     if (ch == HttpTokens.LINE_FEED)
                     {
                         String reason=takeString();
-
                         setState(State.HEADER);
                         handle=_responseHandler.startResponse(_version, _responseStatus, reason)||handle;
                         continue;
@@ -777,7 +866,7 @@
                         _string.append((char)ch);
                         if (ch!=' '&&ch!='\t')
                             _length=_string.length();
-                    } 
+                    }
                     else
                         throw new BadMessageException();
                     break;
@@ -791,100 +880,107 @@
         return handle;
     }
 
-    private boolean handleKnownHeaders(ByteBuffer buffer)
+    private void parsedHeader()
     {
-        boolean add_to_connection_trie=false;
-        switch (_header)
+        // handler last header if any.  Delayed to here just in case there was a continuation line (above)
+        if (_headerString!=null || _valueString!=null)
         {
-            case CONTENT_LENGTH:
-                if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
+            // Handle known headers
+            if (_header!=null)
+            {
+                boolean add_to_connection_trie=false;
+                switch (_header)
                 {
-                    try
-                    {
-                        _contentLength=Long.parseLong(_valueString);
-                    }
-                    catch(NumberFormatException e)
-                    {
-                        LOG.ignore(e);
-                        throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
-                    }
-                    if (_contentLength <= 0)
-                        _endOfContent=EndOfContent.NO_CONTENT;
-                    else
-                        _endOfContent=EndOfContent.CONTENT_LENGTH;
-                }
-                break;
+                    case CONTENT_LENGTH:
+                        if (_endOfContent == EndOfContent.CONTENT_LENGTH)
+                        {
+                            throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "Duplicate Content-Length");
+                        }
+                        else if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
+                        {
+                            _contentLength=convertContentLength(_valueString);
+                            if (_contentLength <= 0)
+                                _endOfContent=EndOfContent.NO_CONTENT;
+                            else
+                                _endOfContent=EndOfContent.CONTENT_LENGTH;
+                        }
+                        break;
 
-            case TRANSFER_ENCODING:
-                if (_value==HttpHeaderValue.CHUNKED)
-                    _endOfContent=EndOfContent.CHUNKED_CONTENT;
-                else
-                {
-                    if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString()))
-                        _endOfContent=EndOfContent.CHUNKED_CONTENT;
-                    else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString()))
-                    {
-                        throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad chunking");
-                    }
-                }
-                break;
+                    case TRANSFER_ENCODING:
+                        if (_value==HttpHeaderValue.CHUNKED)
+                        {
+                            _endOfContent=EndOfContent.CHUNKED_CONTENT;
+                            _contentLength=-1;
+                        }
+                        else
+                        {
+                            if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString()))
+                                _endOfContent=EndOfContent.CHUNKED_CONTENT;
+                            else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString()))
+                                throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad chunking");
+                        }
+                        break;
 
-            case HOST:
-                add_to_connection_trie=_connectionFields!=null && _field==null;
-                _host=true;
+                    case HOST:
+                        _host=true;
+                        if (!(_field instanceof HostPortHttpField) && _valueString!=null && !_valueString.isEmpty())
+                        {
+                            _field=new HostPortHttpField(_header,legacyString(_headerString,_header.asString()),_valueString);
+                            add_to_connection_trie=_connectionFields!=null;
+                        }
+                      break;
 
-                if (_valueString==null || _valueString.length()==0)
-                {
-                    break;
+                    case CONNECTION:
+                        // Don't cache if not persistent
+                        if (_valueString!=null && _valueString.contains("close"))
+                            _connectionFields=null;
+
+                        break;
+
+                    case AUTHORIZATION:
+                    case ACCEPT:
+                    case ACCEPT_CHARSET:
+                    case ACCEPT_ENCODING:
+                    case ACCEPT_LANGUAGE:
+                    case COOKIE:
+                    case CACHE_CONTROL:
+                    case USER_AGENT:
+                        add_to_connection_trie=_connectionFields!=null && _field==null;
+                        break;
+
+                    default: break;
+
                 }
 
-                try
+                if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
                 {
-                    HostPort authority = new HostPort(_valueString);
-                    if (_requestHandler!=null)
-                        _requestHandler.parsedHostHeader(authority.getHost(),authority.getPort());
+                    if (_field==null)
+                        _field=new HttpField(_header,legacyString(_headerString,_header.asString()),_valueString);
+                    _connectionFields.put(_field);
                 }
-                catch (final Exception e)
-                {
-                    throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Host header")
-                    {{initCause(e);}};
-                }
-                
-              break;
-              
-            case CONNECTION:
-                // Don't cache if not persistent
-                if (_valueString!=null && _valueString.contains("close"))
-                {
-                    _closed=true;
-                    _connectionFields=null;
-                }
-                break;
-
-            case AUTHORIZATION:
-            case ACCEPT:
-            case ACCEPT_CHARSET:
-            case ACCEPT_ENCODING:
-            case ACCEPT_LANGUAGE:
-            case COOKIE:
-            case CACHE_CONTROL:
-            case USER_AGENT:
-                add_to_connection_trie=_connectionFields!=null && _field==null;
-                break;
-                
-            default: break;
+            }
+            _handler.parsedHeader(_field!=null?_field:new HttpField(_header,_headerString,_valueString));
         }
-    
-        if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
-        {
-            _field=new HttpField(_header,_valueString);
-            _connectionFields.put(_field);
-        }
-        
-        return false;
+
+        _headerString=_valueString=null;
+        _header=null;
+        _value=null;
+        _field=null;
     }
-    
-    
+
+    private long convertContentLength(String valueString)
+    {
+        try
+        {
+            return Long.parseLong(valueString);
+        }
+        catch(NumberFormatException e)
+        {
+            LOG.ignore(e);
+            throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Invalid Content-Length Value");
+        }
+    }
+
     /* ------------------------------------------------------------------------------- */
     /*
      * Parse the message headers and return true if the handler has signaled for a return
@@ -900,11 +996,11 @@
             byte ch=next(buffer);
             if (ch==0)
                 break;
-            
+
             if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
             {
                 LOG.warn("Header is too large >"+_maxHeaderBytes);
-                throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
+                throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431);
             }
 
             switch (_state)
@@ -916,6 +1012,9 @@
                         case HttpTokens.SPACE:
                         case HttpTokens.TAB:
                         {
+                            if (complianceViolation(RFC7230,"header folding"))
+                                throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Header Folding");
+
                             // header value without name - continuation?
                             if (_valueString==null)
                             {
@@ -933,176 +1032,165 @@
                             break;
                         }
 
-                        default:
+                        case HttpTokens.LINE_FEED:
                         {
-                            // handler last header if any.  Delayed to here just in case there was a continuation line (above)
-                            if (_headerString!=null || _valueString!=null)
+                            // process previous header
+                            parsedHeader();
+
+                            _contentPosition=0;
+
+                            // End of headers!
+
+                            // Was there a required host header?
+                            if (!_host && _version==HttpVersion.HTTP_1_1 && _requestHandler!=null)
                             {
-                                // Handle known headers
-                                if (_header!=null && handleKnownHeaders(buffer))
-                                {
-                                    _headerString=_valueString=null;
-                                    _header=null;
-                                    _value=null;
-                                    _field=null;
-                                    return true;
-                                }
-                                handle=_handler.parsedHeader(_field!=null?_field:new HttpField(_header,_headerString,_valueString))||handle;
+                                throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"No Host");
                             }
-                            _headerString=_valueString=null;
-                            _header=null;
-                            _value=null;
-                            _field=null;
 
-                            // now handle the ch
-                            if (ch == HttpTokens.LINE_FEED)
-                            {
-                                _contentPosition=0;
-
-                                // End of headers!
-
-                                // Was there a required host header?
-                                if (!_host && _version==HttpVersion.HTTP_1_1 && _requestHandler!=null)
-                                {
-                                    throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"No Host");
-                                }
-
-                                // is it a response that cannot have a body?
-                                if (_responseHandler !=null  && // response  
+                            // is it a response that cannot have a body?
+                            if (_responseHandler !=null  && // response
                                     (_responseStatus == 304  || // not-modified response
                                     _responseStatus == 204 || // no-content response
                                     _responseStatus < 200)) // 1xx response
-                                    _endOfContent=EndOfContent.NO_CONTENT; // ignore any other headers set
-                                
-                                // else if we don't know framing
-                                else if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
-                                {
-                                    if (_responseStatus == 0  // request
-                                            || _responseStatus == 304 // not-modified response
-                                            || _responseStatus == 204 // no-content response
-                                            || _responseStatus < 200) // 1xx response
-                                        _endOfContent=EndOfContent.NO_CONTENT;
-                                    else
-                                        _endOfContent=EndOfContent.EOF_CONTENT;
-                                }
+                                _endOfContent=EndOfContent.NO_CONTENT; // ignore any other headers set
 
-                                // How is the message ended?
-                                switch (_endOfContent)
-                                {
-                                    case EOF_CONTENT:
-                                        setState(State.EOF_CONTENT);
-                                        handle=_handler.headerComplete()||handle;
-                                        return handle;
-
-                                    case CHUNKED_CONTENT:
-                                        setState(State.CHUNKED_CONTENT);
-                                        handle=_handler.headerComplete()||handle;
-                                        return handle;
-
-                                    case NO_CONTENT:
-                                        handle=_handler.headerComplete()||handle;
-                                        setState(State.END);
-                                        handle=_handler.messageComplete()||handle;
-                                        return handle;
-
-                                    default:
-                                        setState(State.CONTENT);
-                                        handle=_handler.headerComplete()||handle;
-                                        return handle;
-                                }
-                            }
-                            else if (ch<=HttpTokens.SPACE)
-                                throw new BadMessageException();
-                            else
+                            // else if we don't know framing
+                            else if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
                             {
-                                if (buffer.hasRemaining())
-                                {
-                                    // Try a look ahead for the known header name and value.
-                                    HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
-                                    if (field==null)
-                                        field=CACHE.getBest(buffer,-1,buffer.remaining());
-                                        
-                                    if (field!=null)
-                                    {
-                                        final String n;
-                                        final String v;
+                                if (_responseStatus == 0  // request
+                                        || _responseStatus == 304 // not-modified response
+                                        || _responseStatus == 204 // no-content response
+                                        || _responseStatus < 200) // 1xx response
+                                    _endOfContent=EndOfContent.NO_CONTENT;
+                                else
+                                    _endOfContent=EndOfContent.EOF_CONTENT;
+                            }
 
-                                        if (_strict)
-                                        {
-                                            // Have to get the fields exactly from the buffer to match case
-                                            String fn=field.getName();
-                                            String fv=field.getValue();
-                                            n=BufferUtil.toString(buffer,buffer.position()-1,fn.length(),StandardCharsets.US_ASCII);
-                                            if (fv==null)
-                                                v=null;
-                                            else
-                                            {
-                                                v=BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StandardCharsets.ISO_8859_1);
-                                                field=new HttpField(field.getHeader(),n,v);
-                                            }
-                                        }
+                            // How is the message ended?
+                            switch (_endOfContent)
+                            {
+                                case EOF_CONTENT:
+                                    setState(State.EOF_CONTENT);
+                                    handle=_handler.headerComplete()||handle;
+                                    _headerComplete=true;
+                                    return handle;
+
+                                case CHUNKED_CONTENT:
+                                    setState(State.CHUNKED_CONTENT);
+                                    handle=_handler.headerComplete()||handle;
+                                    _headerComplete=true;
+                                    return handle;
+
+                                case NO_CONTENT:
+                                    setState(State.END);
+                                    return handleHeaderContentMessage();
+
+                                default:
+                                    setState(State.CONTENT);
+                                    handle=_handler.headerComplete()||handle;
+                                    _headerComplete=true;
+                                    return handle;
+                            }
+                        }
+
+                        default:
+                        {
+                            // now handle the ch
+                            if (ch<HttpTokens.SPACE)
+                                throw new BadMessageException();
+
+                            // process previous header
+                            parsedHeader();
+
+                            // handle new header
+                            if (buffer.hasRemaining())
+                            {
+                                // Try a look ahead for the known header name and value.
+                                HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
+                                if (field==null)
+                                    field=CACHE.getBest(buffer,-1,buffer.remaining());
+
+                                if (field!=null)
+                                {
+                                    final String n;
+                                    final String v;
+
+                                    if (_compliance==LEGACY)
+                                    {
+                                        // Have to get the fields exactly from the buffer to match case
+                                        String fn=field.getName();
+                                        n=legacyString(BufferUtil.toString(buffer,buffer.position()-1,fn.length(),StandardCharsets.US_ASCII),fn);
+                                        String fv=field.getValue();
+                                        if (fv==null)
+                                            v=null;
                                         else
                                         {
-                                            n=field.getName();
-                                            v=field.getValue(); 
+                                            v=legacyString(BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StandardCharsets.ISO_8859_1),fv);
+                                            field=new HttpField(field.getHeader(),n,v);
                                         }
-                                        
-                                        _header=field.getHeader();
-                                        _headerString=n;
-         
-                                        if (v==null)
+                                    }
+                                    else
+                                    {
+                                        n=field.getName();
+                                        v=field.getValue();
+                                    }
+
+                                    _header=field.getHeader();
+                                    _headerString=n;
+
+                                    if (v==null)
+                                    {
+                                        // Header only
+                                        setState(State.HEADER_VALUE);
+                                        _string.setLength(0);
+                                        _length=0;
+                                        buffer.position(buffer.position()+n.length()+1);
+                                        break;
+                                    }
+                                    else
+                                    {
+                                        // Header and value
+                                        int pos=buffer.position()+n.length()+v.length()+1;
+                                        byte b=buffer.get(pos);
+
+                                        if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED)
                                         {
-                                            // Header only
-                                            setState(State.HEADER_VALUE);
-                                            _string.setLength(0);
-                                            _length=0;
-                                            buffer.position(buffer.position()+n.length()+1);
+                                            _field=field;
+                                            _valueString=v;
+                                            setState(State.HEADER_IN_VALUE);
+
+                                            if (b==HttpTokens.CARRIAGE_RETURN)
+                                            {
+                                                _cr=true;
+                                                buffer.position(pos+1);
+                                            }
+                                            else
+                                                buffer.position(pos);
                                             break;
                                         }
                                         else
                                         {
-                                            // Header and value
-                                            int pos=buffer.position()+n.length()+v.length()+1;
-                                            byte b=buffer.get(pos);
-
-                                            if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED)
-                                            {                     
-                                                _field=field;
-                                                _valueString=v;
-                                                setState(State.HEADER_IN_VALUE);
-
-                                                if (b==HttpTokens.CARRIAGE_RETURN)
-                                                {
-                                                    _cr=true;
-                                                    buffer.position(pos+1);
-                                                }
-                                                else
-                                                    buffer.position(pos);
-                                                break;
-                                            }
-                                            else
-                                            {
-                                                setState(State.HEADER_IN_VALUE);
-                                                setString(v);
-                                                buffer.position(pos);
-                                                break;
-                                            }
+                                            setState(State.HEADER_IN_VALUE);
+                                            setString(v);
+                                            buffer.position(pos);
+                                            break;
                                         }
                                     }
                                 }
-
-                                // New header
-                                setState(State.HEADER_IN_NAME);
-                                _string.setLength(0);
-                                _string.append((char)ch);
-                                _length=1;
                             }
+
+                            // New header
+                            setState(State.HEADER_IN_NAME);
+                            _string.setLength(0);
+                            _string.append((char)ch);
+                            _length=1;
+
                         }
                     }
                     break;
 
                 case HEADER_IN_NAME:
-                    if (ch==HttpTokens.COLON || ch==HttpTokens.LINE_FEED)
+                    if (ch==HttpTokens.COLON)
                     {
                         if (_headerString==null)
                         {
@@ -1111,11 +1199,11 @@
                         }
                         _length=-1;
 
-                        setState(ch==HttpTokens.LINE_FEED?State.HEADER:State.HEADER_VALUE);
+                        setState(State.HEADER_VALUE);
                         break;
                     }
-                    
-                    if (ch>=HttpTokens.SPACE || ch==HttpTokens.TAB)
+
+                    if (ch>HttpTokens.SPACE)
                     {
                         if (_header!=null)
                         {
@@ -1129,6 +1217,22 @@
                             _length=_string.length();
                         break;
                     }
+                    
+                    if (ch==HttpTokens.LINE_FEED && !complianceViolation(RFC7230,"name only header"))
+                    {
+                        if (_headerString==null)
+                        {
+                            _headerString=takeString();
+                            _header=HttpHeader.CACHE.get(_headerString);
+                        }
+                        _value=null;
+                        _string.setLength(0);
+                        _valueString="";
+                        _length=-1;
+
+                        setState(State.HEADER);
+                        break;
+                    }
 
                     throw new IllegalCharacterException(_state,ch,buffer);
 
@@ -1140,21 +1244,20 @@
                         setState(State.HEADER_IN_VALUE);
                         break;
                     }
-                    
+
                     if (ch==HttpTokens.SPACE || ch==HttpTokens.TAB)
                         break;
-                    
+
                     if (ch==HttpTokens.LINE_FEED)
                     {
-                        if (_length > 0)
-                        {
-                            _value=null;
-                            _valueString=(_valueString==null)?takeString():(_valueString+" "+takeString());
-                        }
+                        _value=null;
+                        _string.setLength(0);
+                        _valueString="";
+                        _length=-1;
+
                         setState(State.HEADER);
-                        break; 
+                        break;
                     }
-                    
                     throw new IllegalCharacterException(_state,ch,buffer);
 
                 case HEADER_IN_VALUE:
@@ -1171,7 +1274,7 @@
                             _length=_string.length();
                         break;
                     }
-                    
+
                     if (ch==HttpTokens.LINE_FEED)
                     {
                         if (_length > 0)
@@ -1185,7 +1288,7 @@
                     }
 
                     throw new IllegalCharacterException(_state,ch,buffer);
-                    
+
                 default:
                     throw new IllegalStateException(_state.toString());
 
@@ -1198,6 +1301,7 @@
     /* ------------------------------------------------------------------------------- */
     /**
      * Parse until next Event.
+     * @param buffer the buffer to parse
      * @return True if an {@link RequestHandler} method was called and it returned true;
      */
     public boolean parseNext(ByteBuffer buffer)
@@ -1217,7 +1321,7 @@
                 if (quickStart(buffer))
                     return true;
             }
-            
+
             // Request/response line
             if (_state.ordinal()>= State.START.ordinal() && _state.ordinal()<State.HEADER.ordinal())
             {
@@ -1231,7 +1335,7 @@
                 if (parseHeaders(buffer))
                     return true;
             }
-            
+
             // parse content
             if (_state.ordinal()>= State.CONTENT.ordinal() && _state.ordinal()<State.END.ordinal())
             {
@@ -1239,7 +1343,7 @@
                 if (_responseStatus>0 && _headResponse)
                 {
                     setState(State.END);
-                    return _handler.messageComplete();
+                    return handleContentMessage();
                 }
                 else
                 {
@@ -1247,7 +1351,7 @@
                         return true;
                 }
             }
-            
+
             // handle end states
             if (_state==State.END)
             {
@@ -1255,8 +1359,9 @@
                 while (buffer.remaining()>0 && buffer.get(buffer.position())<=HttpTokens.SPACE)
                     buffer.get();
             }
-            else if (_state==State.CLOSED)
+            else if (_state==State.CLOSE)
             {
+                // Seeking EOF
                 if (BufferUtil.hasContent(buffer))
                 {
                     // Just ignore data when closed
@@ -1265,11 +1370,15 @@
                     if (_maxHeaderBytes>0 && _headerBytes>_maxHeaderBytes)
                     {
                         // Don't want to waste time reading data of a closed request
-                        throw new IllegalStateException("too much data after closed");
+                        throw new IllegalStateException("too much data seeking EOF");
                     }
                 }
             }
-            
+            else if (_state==State.CLOSED)
+            {
+                BufferUtil.clear(buffer);
+            }
+
             // Handle EOF
             if (_eof && !buffer.hasRemaining())
             {
@@ -1277,25 +1386,28 @@
                 {
                     case CLOSED:
                         break;
-                        
+
                     case START:
                         setState(State.CLOSED);
                         _handler.earlyEOF();
                         break;
-                        
+
                     case END:
+                    case CLOSE:
                         setState(State.CLOSED);
                         break;
-                        
-                    case EOF_CONTENT:
-                        setState(State.CLOSED);
-                        return _handler.messageComplete();
 
-                    case  CONTENT:
-                    case  CHUNKED_CONTENT:
-                    case  CHUNK_SIZE:
-                    case  CHUNK_PARAMS:
-                    case  CHUNK:
+                    case EOF_CONTENT:
+                    case CHUNK_END:
+                        setState(State.CLOSED);
+                        return handleContentMessage();
+
+                    case CONTENT:
+                    case CHUNKED_CONTENT:
+                    case CHUNK_SIZE:
+                    case CHUNK_PARAMS:
+                    case CHUNK:
+                    case CHUNK_TRAILER:
                         setState(State.CLOSED);
                         _handler.earlyEOF();
                         break;
@@ -1308,40 +1420,50 @@
                         break;
                 }
             }
-            
-            return false;
         }
         catch(BadMessageException e)
         {
             BufferUtil.clear(buffer);
 
-            LOG.warn("badMessage: "+e._code+(e.getMessage()!=null?" "+e.getMessage():"")+" for "+_handler);
-            if (DEBUG)
-                LOG.debug(e);
-            setState(State.CLOSED);
-            _handler.badMessage(e._code, e.getMessage());
-            return false;
+            Throwable cause = e.getCause();
+            boolean stack = LOG.isDebugEnabled() ||
+                    (!(cause instanceof NumberFormatException )  && (cause instanceof RuntimeException || cause instanceof Error));
+
+            if (stack)
+                LOG.warn("bad HTTP parsed: "+e._code+(e.getReason()!=null?" "+e.getReason():"")+" for "+_handler,e);
+            else
+                LOG.warn("bad HTTP parsed: "+e._code+(e.getReason()!=null?" "+e.getReason():"")+" for "+_handler);
+            setState(State.CLOSE);
+            _handler.badMessage(e.getCode(), e.getReason());
         }
-        catch(Exception e)
+        catch(NumberFormatException|IllegalStateException e)
         {
             BufferUtil.clear(buffer);
-
-            LOG.warn("badMessage: "+e.toString()+" for "+_handler);
+            LOG.warn("parse exception: {} in {} for {}",e.toString(),_state,_handler);
             if (DEBUG)
                 LOG.debug(e);
-            
-            if (_state.ordinal()<=State.END.ordinal())
-            {
-                setState(State.CLOSED);
-                _handler.badMessage(400,null);
-            }
-            else
-            {
-                _handler.earlyEOF();
-                setState(State.CLOSED);
-            }
+            badMessage();
 
-            return false;
+        }
+        catch(Exception|Error e)
+        {
+            BufferUtil.clear(buffer);
+            LOG.warn("parse exception: "+e.toString()+" for "+_handler,e);
+            badMessage();
+        }
+        return false;
+    }
+    
+    protected void badMessage()
+    {
+        if (_headerComplete)
+        {
+            _handler.earlyEOF();
+        }
+        else if (_state!=State.CLOSED)
+        {
+            setState(State.CLOSE);
+            _handler.badMessage(400,_requestHandler!=null?"Bad Request":"Bad Response");
         }
     }
 
@@ -1354,10 +1476,10 @@
             if (content == 0)
             {
                 setState(State.END);
-                return _handler.messageComplete();
+                return handleContentMessage();
             }
         }
-        
+
         // Handle _content
         byte ch;
         while (_state.ordinal() < State.END.ordinal() && remaining>0)
@@ -1378,7 +1500,7 @@
                     if (content == 0)
                     {
                         setState(State.END);
-                        return _handler.messageComplete();
+                        return handleContentMessage();
                     }
                     else
                     {
@@ -1401,7 +1523,7 @@
                         if(_contentPosition == _contentLength)
                         {
                             setState(State.END);
-                            return _handler.messageComplete();
+                            return handleContentMessage();
                         }
                     }
                     break;
@@ -1428,7 +1550,11 @@
                     if (ch == HttpTokens.LINE_FEED)
                     {
                         if (_chunkLength == 0)
+                        {
                             setState(State.CHUNK_END);
+                            if (_handler.contentComplete())
+                                return true;
+                        }
                         else
                             setState(State.CHUNK);
                     }
@@ -1445,7 +1571,11 @@
                     if (ch == HttpTokens.LINE_FEED)
                     {
                         if (_chunkLength == 0)
+                        {
                             setState(State.CHUNK_END);
+                            if (_handler.contentComplete())
+                                return true;
+                        }
                         else
                             setState(State.CHUNK);
                     }
@@ -1475,10 +1605,9 @@
                     }
                     break;
                 }
-                
+
                 case CHUNK_END:
                 {
-                    // TODO handle chunk trailer
                     ch=next(buffer);
                     if (ch==0)
                         break;
@@ -1487,20 +1616,32 @@
                         setState(State.END);
                         return _handler.messageComplete();
                     }
-                    throw new IllegalCharacterException(_state,ch,buffer);
+                    setState(State.CHUNK_TRAILER);
+                    break;
                 }
-                
+
+                case CHUNK_TRAILER:
+                {
+                    // TODO handle chunk trailer values
+                    ch=next(buffer);
+                    if (ch==0)
+                        break;
+                    if (ch == HttpTokens.LINE_FEED)
+                        setState(State.CHUNK_END);
+                    break;
+                }
+
                 case CLOSED:
                 {
                     BufferUtil.clear(buffer);
                     return false;
                 }
 
-                default: 
+                default:
                     break;
-                    
+
             }
-            
+
             remaining=buffer.remaining();
         }
         return false;
@@ -1508,42 +1649,41 @@
 
     /* ------------------------------------------------------------------------------- */
     public boolean isAtEOF()
- 
+
     {
         return _eof;
     }
-    
-    /* ------------------------------------------------------------------------------- */
-    public void atEOF()
 
-    {        
+    /* ------------------------------------------------------------------------------- */
+    /** Signal that the associated data source is at EOF
+     */
+    public void atEOF()
+    {
         if (DEBUG)
             LOG.debug("atEOF {}", this);
         _eof=true;
     }
 
     /* ------------------------------------------------------------------------------- */
+    /** Request that the associated data source be closed
+     */
     public void close()
     {
         if (DEBUG)
             LOG.debug("close {}", this);
-        setState(State.CLOSED);
+        setState(State.CLOSE);
     }
-    
+
     /* ------------------------------------------------------------------------------- */
     public void reset()
     {
         if (DEBUG)
             LOG.debug("reset {}", this);
+
         // reset state
-        if (_state==State.CLOSED)
+        if (_state==State.CLOSE || _state==State.CLOSED)
             return;
-        if (_closed)
-        {
-            setState(State.CLOSED);
-            return;
-        }
-        
+
         setState(State.START);
         _endOfContent=EndOfContent.UNKNOWN_CONTENT;
         _contentLength=-1;
@@ -1552,6 +1692,7 @@
         _contentChunk=null;
         _headerBytes=0;
         _host=false;
+        _headerComplete=false;
     }
 
     /* ------------------------------------------------------------------------------- */
@@ -1573,18 +1714,18 @@
     {
         _string.setLength(0);
         _length=0;
-        
+
         while (buffer.hasRemaining())
         {
             // process each character
             byte ch=next(buffer);
             if (ch<=' ')
                 return _string.toString();
-            _string.append((char)ch);    
+            _string.append((char)ch);
         }
         throw new BadMessageException();
     }
-    
+
     /* ------------------------------------------------------------------------------- */
     @Override
     public String toString()
@@ -1602,24 +1743,25 @@
     /* Event Handler interface
      * These methods return true if the caller should process the events
      * so far received (eg return from parseNext and call HttpChannel.handle).
-     * If multiple callbacks are called in sequence (eg 
+     * If multiple callbacks are called in sequence (eg
      * headerComplete then messageComplete) from the same point in the parsing
      * then it is sufficient for the caller to process the events only once.
      */
-    public interface HttpHandler<T>
+    public interface HttpHandler
     {
-        public boolean content(T item);
+        public boolean content(ByteBuffer item);
 
         public boolean headerComplete();
 
+        public boolean contentComplete(); 
+        
         public boolean messageComplete();
 
         /**
          * This is the method called by parser when a HTTP Header name and value is found
          * @param field The field parsed
-         * @return True if the parser should return to its caller
          */
-        public boolean parsedHeader(HttpField field);
+        public void parsedHeader(HttpField field);
 
         /* ------------------------------------------------------------ */
         /** Called to signal that an EOF was received unexpectedly
@@ -1633,7 +1775,7 @@
          * @param reason The textual reason for badness
          */
         public void badMessage(int status, String reason);
-        
+
         /* ------------------------------------------------------------ */
         /** @return the size in bytes of the per parser header cache
          */
@@ -1643,43 +1785,40 @@
     /* ------------------------------------------------------------------------------- */
     /* ------------------------------------------------------------------------------- */
     /* ------------------------------------------------------------------------------- */
-    public interface ProxyHandler 
-    {
-        void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort);
-    }
-
-    /* ------------------------------------------------------------------------------- */
-    /* ------------------------------------------------------------------------------- */
-    /* ------------------------------------------------------------------------------- */
-    public interface RequestHandler<T> extends HttpHandler<T>
+    public interface RequestHandler extends HttpHandler
     {
         /**
          * This is the method called by parser when the HTTP request line is parsed
-         * @param method The method as enum if of a known type
-         * @param methodString The method as a string
+         * @param method The method
          * @param uri The raw bytes of the URI.  These are copied into a ByteBuffer that will not be changed until this parser is reset and reused.
-         * @param version
+         * @param version the http version in use
          * @return true if handling parsing should return.
          */
-        public abstract boolean startRequest(HttpMethod method, String methodString, ByteBuffer uri, HttpVersion version);
+        public boolean startRequest(String method, String uri, HttpVersion version);
 
-        /**
-         * This is the method called by the parser after it has parsed the host header (and checked it's format). This is
-         * called after the {@link HttpHandler#parsedHeader(HttpField)} methods and before
-         * HttpHandler#headerComplete();
-         */
-        public abstract boolean parsedHostHeader(String host,int port);
     }
 
     /* ------------------------------------------------------------------------------- */
     /* ------------------------------------------------------------------------------- */
     /* ------------------------------------------------------------------------------- */
-    public interface ResponseHandler<T> extends HttpHandler<T>
+    public interface ResponseHandler extends HttpHandler
     {
         /**
          * This is the method called by parser when the HTTP request line is parsed
+         * @param version the http version in use
+         * @param status the response status
+         * @param reason the response reason phrase
+         * @return true if handling parsing should return
          */
-        public abstract boolean startResponse(HttpVersion version, int status, String reason);
+        public boolean startResponse(HttpVersion version, int status, String reason);
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
+    public interface ComplianceHandler extends HttpHandler
+    {
+        public void onComplianceViolation(HttpCompliance compliance,HttpCompliance required,String reason);
     }
 
     /* ------------------------------------------------------------------------------- */
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpScheme.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpScheme.java
index 9720d5a..acf8b70 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpScheme.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpScheme.java
@@ -61,7 +61,7 @@
     /* ------------------------------------------------------------ */
     public boolean is(String s)
     {
-        return _string.equalsIgnoreCase(s);
+        return s!=null && _string.equalsIgnoreCase(s);
     }
 
     public String asString()
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpStatus.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpStatus.java
index 7cea0a9..5ff0241 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpStatus.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpStatus.java
@@ -20,595 +20,9 @@
 
 /**
  * <p>
- * HttpStatusCode enum class, for status codes based on various HTTP RFCs. (see
- * table below)
+ * Http Status Codes
  * </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$
+ * @see <a href="http://www.iana.org/assignments/http-status-codes/">IANA HTTP Status Code Registry</a>
  */
 public class HttpStatus
 {
@@ -633,6 +47,7 @@
     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 PERMANENT_REDIRECT_308 = 308;
 
     public final static int BAD_REQUEST_400 = 400;
     public final static int UNAUTHORIZED_401 = 401;
@@ -647,14 +62,28 @@
     public final static int GONE_410 = 410;
     public final static int LENGTH_REQUIRED_411 = 411;
     public final static int PRECONDITION_FAILED_412 = 412;
+    @Deprecated
     public final static int REQUEST_ENTITY_TOO_LARGE_413 = 413;
+    public final static int PAYLOAD_TOO_LARGE_413 = 413;
+    @Deprecated
     public final static int REQUEST_URI_TOO_LONG_414 = 414;
+    public final static int URI_TOO_LONG_414 = 414;
     public final static int UNSUPPORTED_MEDIA_TYPE_415 = 415;
+    @Deprecated
     public final static int REQUESTED_RANGE_NOT_SATISFIABLE_416 = 416;
+    public final static int RANGE_NOT_SATISFIABLE_416 = 416;
     public final static int EXPECTATION_FAILED_417 = 417;
+    public final static int IM_A_TEAPOT_418 = 417;
+    public final static int ENHANCE_YOUR_CALM_420 = 420;
+    public final static int MISDIRECTED_REQUEST_421 = 421;
     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 UPGRADE_REQUIRED_426 = 426;
+    public final static int PRECONDITION_REQUIRED_428 = 428;
+    public final static int TOO_MANY_REQUESTS_429 = 429;
+    public final static int REQUEST_HEADER_FIELDS_TOO_LARGE_431 = 431;
+    public final static int UNAVAILABLE_FOR_LEGAL_REASONS_451 = 451;
 
     public final static int INTERNAL_SERVER_ERROR_500 = 500;
     public final static int NOT_IMPLEMENTED_501 = 501;
@@ -663,9 +92,11 @@
     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;
-
+    public final static int LOOP_DETECTED_508 = 508;
+    public final static int NOT_EXTENDED_510 = 510;
+    public final static int NETWORK_AUTHENTICATION_REQUIRED_511 = 511;
+    
+    public static final int MAX_CODE = 511;
 
     private static final Code[] codeMap = new Code[MAX_CODE+1];
 
@@ -680,135 +111,73 @@
 
     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"),
+        PERMANET_REDIRECT(PERMANENT_REDIRECT_308, "Permanent 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> */
+        PAYLOAD_TOO_LARGE(PAYLOAD_TOO_LARGE_413, "Payload Too Large"),
+        URI_TOO_LONG(URI_TOO_LONG_414, "URI Too Long"),
         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> */
+        RANGE_NOT_SATISFIABLE(RANGE_NOT_SATISFIABLE_416, "Range Not Satisfiable"),
         EXPECTATION_FAILED(EXPECTATION_FAILED_417, "Expectation Failed"),
-        /** <code>422 Unprocessable Entity</code> */
+        IM_A_TEAPOT(IM_A_TEAPOT_418, "Im a Teapot"),
+        ENHANCE_YOUR_CALM(ENHANCE_YOUR_CALM_420, "Enhance your Calm"),
+        MISDIRECTED_REQUEST(MISDIRECTED_REQUEST_421, "Misdirected Request"),
         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"),
+        UPGRADE_REQUIRED(UPGRADE_REQUIRED_426, "Upgrade Required"),
+        PRECONDITION_REQUIRED(PRECONDITION_REQUIRED_428, "Precondition Required"),
+        TOO_MANY_REQUESTS(TOO_MANY_REQUESTS_429, "Too Many Requests"),
+        REQUEST_HEADER_FIELDS_TOO_LARGE(REQUEST_HEADER_FIELDS_TOO_LARGE_431, "Request Header Fields Too Large"),
+        UNAVAILABLE_FOR_LEGAL_REASONS(UNAVAILABLE_FOR_LEGAL_REASONS_451, "Unavailable for Legal Reason"),
 
-        /*
-         * --------------------------------------------------------------------
-         * 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");
-
+        INSUFFICIENT_STORAGE(INSUFFICIENT_STORAGE_507, "Insufficient Storage"),
+        LOOP_DETECTED(LOOP_DETECTED_508, "Loop Detected"),
+        NOT_EXTENDED(NOT_EXTENDED_510, "Not Extended"),
+        NETWORK_AUTHENTICATION_REQUIRED(NETWORK_AUTHENTICATION_REQUIRED_511, "Network Authentication Required"),
+        
+        ;
+        
         private final int _code;
         private final String _message;
 
@@ -844,7 +213,7 @@
          * 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 -
+         * and <a href="http://tools.ietf.org/html/rfc7231">RFC 7231 -
          * HTTP/1.1</a>.
          *
          * @return true if within range of codes that belongs to
@@ -859,7 +228,7 @@
          * 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 -
+         * and <a href="http://tools.ietf.org/html/rfc7231">RFC 7231 -
          * HTTP/1.1</a>.
          *
          * @return true if within range of codes that belongs to
@@ -874,7 +243,7 @@
          * 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 -
+         * and <a href="http://tools.ietf.org/html/rfc7231">RFC 7231 -
          * HTTP/1.1</a>.
          *
          * @return true if within range of codes that belongs to
@@ -889,7 +258,7 @@
          * 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 -
+         * and <a href="http://tools.ietf.org/html/rfc7231">RFC 7231 -
          * HTTP/1.1</a>.
          *
          * @return true if within range of codes that belongs to
@@ -904,7 +273,7 @@
          * 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 -
+         * and <a href="http://tools.ietf.org/html/rfc7231">RFC 7231 -
          * HTTP/1.1</a>.
          *
          * @return true if within range of codes that belongs to
@@ -958,7 +327,7 @@
      * 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>.
+     * href="http://tools.ietf.org/html/rfc7231">RFC 7231 - HTTP/1.1</a>.
      *
      * @param code
      *            the code to test.
@@ -974,7 +343,7 @@
      * 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>.
+     * href="http://tools.ietf.org/html/rfc7231">RFC 7231 - HTTP/1.1</a>.
      *
      * @param code
      *            the code to test.
@@ -990,7 +359,7 @@
      * 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>.
+     * href="http://tools.ietf.org/html/rfc7231">RFC 7231 - HTTP/1.1</a>.
      *
      * @param code
      *            the code to test.
@@ -1006,7 +375,7 @@
      * 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>.
+     * href="http://tools.ietf.org/html/rfc7231">RFC 7231 - HTTP/1.1</a>.
      *
      * @param code
      *            the code to test.
@@ -1022,7 +391,7 @@
      * 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>.
+     * href="http://tools.ietf.org/html/rfc7231">RFC 7231 - HTTP/1.1</a>.
      *
      * @param code
      *            the code to test.
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java
deleted file mode 100644
index 4be8190..0000000
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java
+++ /dev/null
@@ -1,367 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-
-import org.eclipse.jetty.http.HttpGenerator.RequestInfo;
-import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.StringUtil;
-
-public class HttpTester
-{
-    private HttpTester()
-    {
-    }
-
-    public static Request newRequest()
-    {
-        return new Request();
-    }
-
-    public static Request parseRequest(String request)
-    {
-        Request r=new Request();
-        HttpParser parser =new HttpParser(r);
-        parser.parseNext(BufferUtil.toBuffer(request));
-        return r;
-    }
-
-    public static Request parseRequest(ByteBuffer request)
-    {
-        Request r=new Request();
-        HttpParser parser =new HttpParser(r);
-        parser.parseNext(request);
-        return r;
-    }
-
-    public static Response parseResponse(String response)
-    {
-        Response r=new Response();
-        HttpParser parser =new HttpParser(r);
-        parser.parseNext(BufferUtil.toBuffer(response));
-        return r;
-    }
-
-    public static Response parseResponse(ByteBuffer response)
-    {
-        Response r=new Response();
-        HttpParser parser =new HttpParser(r);
-        parser.parseNext(response);
-        return r;
-    }
-
-
-    public abstract static class Message extends HttpFields implements HttpParser.HttpHandler<ByteBuffer>
-    {
-        ByteArrayOutputStream _content;
-        HttpVersion _version=HttpVersion.HTTP_1_0;
-
-        public HttpVersion getVersion()
-        {
-            return _version;
-        }
-
-        public void setVersion(String version)
-        {
-            setVersion(HttpVersion.CACHE.get(version));
-        }
-
-        public void setVersion(HttpVersion version)
-        {
-            _version=version;
-        }
-
-        public void setContent(byte[] bytes)
-        {
-            try
-            {
-                _content=new ByteArrayOutputStream();
-                _content.write(bytes);
-            }
-            catch (IOException e)
-            {
-                throw new RuntimeException(e);
-            }
-        }
-
-        public void setContent(String content)
-        {
-            try
-            {
-                _content=new ByteArrayOutputStream();
-                _content.write(StringUtil.getBytes(content));
-            }
-            catch (IOException e)
-            {
-                throw new RuntimeException(e);
-            }
-        }
-
-        public void setContent(ByteBuffer content)
-        {
-            try
-            {
-                _content=new ByteArrayOutputStream();
-                _content.write(BufferUtil.toArray(content));
-            }
-            catch (IOException e)
-            {
-                throw new RuntimeException(e);
-            }
-        }
-        @Override
-        public boolean parsedHeader(HttpField field)
-        {
-            put(field.getName(),field.getValue());
-            return false;
-        }
-
-        @Override
-        public boolean messageComplete()
-        {
-            return true;
-        }
-
-        @Override
-        public boolean headerComplete()
-        {
-            _content=new ByteArrayOutputStream();
-            return false;
-        }
-
-        @Override
-        public void earlyEOF()
-        {
-        }
-
-        @Override
-        public boolean content(ByteBuffer ref)
-        {
-            try
-            {
-                _content.write(BufferUtil.toArray(ref));
-            }
-            catch (IOException e)
-            {
-                throw new RuntimeException(e);
-            }
-            return false;
-        }
-
-        @Override
-        public void badMessage(int status, String reason)
-        {
-            throw new RuntimeException(reason);
-        }
-
-        public ByteBuffer generate()
-        {
-            try
-            {
-                HttpGenerator generator = new HttpGenerator();
-                HttpGenerator.Info info = getInfo();
-                // System.err.println(info.getClass());
-                // System.err.println(info);
-
-                ByteArrayOutputStream out = new ByteArrayOutputStream();
-                ByteBuffer header=null;
-                ByteBuffer chunk=null;
-                ByteBuffer content=_content==null?null:ByteBuffer.wrap(_content.toByteArray());
-
-
-                loop: while(!generator.isEnd())
-                {
-                    HttpGenerator.Result result =  info instanceof RequestInfo
-                        ?generator.generateRequest((RequestInfo)info,header,chunk,content,true)
-                        :generator.generateResponse((ResponseInfo)info,header,chunk,content,true);
-                    switch(result)
-                    {
-                        case NEED_HEADER:
-                            header=BufferUtil.allocate(8192);
-                            continue;
-
-                        case NEED_CHUNK:
-                            chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
-                            continue;
-
-                        case NEED_INFO:
-                            throw new IllegalStateException();
-
-                        case FLUSH:
-                            if (BufferUtil.hasContent(header))
-                            {
-                                out.write(BufferUtil.toArray(header));
-                                BufferUtil.clear(header);
-                            }
-                            if (BufferUtil.hasContent(chunk))
-                            {
-                                out.write(BufferUtil.toArray(chunk));
-                                BufferUtil.clear(chunk);
-                            }
-                            if (BufferUtil.hasContent(content))
-                            {
-                                out.write(BufferUtil.toArray(content));
-                                BufferUtil.clear(content);
-                            }
-                            break;
-
-                        case SHUTDOWN_OUT:
-                            break loop;
-                    }
-                }
-
-                return ByteBuffer.wrap(out.toByteArray());
-            }
-            catch (IOException e)
-            {
-                throw new RuntimeException(e);
-            }
-
-        }
-        abstract public HttpGenerator.Info getInfo();
-
-        @Override
-        public int getHeaderCacheSize()
-        {
-            return 0;
-        }
-
-    }
-
-    public static class Request extends Message implements HttpParser.RequestHandler<ByteBuffer>
-    {
-        private String _method;
-        private String _uri;
-
-        @Override
-        public boolean startRequest(HttpMethod method, String methodString, ByteBuffer uri, HttpVersion version)
-        {
-            _method=methodString;
-            _uri=BufferUtil.toUTF8String(uri);
-            _version=version;
-            return false;
-        }
-
-        public String getMethod()
-        {
-            return _method;
-        }
-
-        public String getUri()
-        {
-            return _uri;
-        }
-
-        public void setMethod(String method)
-        {
-            _method=method;
-        }
-
-        public void setURI(String uri)
-        {
-            _uri=uri;
-        }
-
-        @Override
-        public HttpGenerator.RequestInfo getInfo()
-        {
-            return new HttpGenerator.RequestInfo(_version,this,_content==null?0:_content.size(),_method,_uri);
-        }
-
-        @Override
-        public String toString()
-        {
-            return String.format("%s %s %s\n%s\n",_method,_uri,_version,super.toString());
-        }
-
-        public void setHeader(String name, String value)
-        {
-            put(name,value);
-        }
-
-        @Override
-        public boolean parsedHostHeader(String host,int port)
-        {
-            return false;
-        }
-    }
-
-    public static class Response extends Message implements HttpParser.ResponseHandler<ByteBuffer>
-    {
-        private int _status;
-        private String _reason;
-
-        @Override
-        public boolean startResponse(HttpVersion version, int status, String reason)
-        {
-            _version=version;
-            _status=status;
-            _reason=reason;
-            return false;
-        }
-
-        public int getStatus()
-        {
-            return _status;
-        }
-
-        public String getReason()
-        {
-            return _reason;
-        }
-
-        public byte[] getContentBytes()
-        {
-            if (_content==null)
-                return null;
-            return _content.toByteArray();
-        }
-
-        public String getContent()
-        {
-            if (_content==null)
-                return null;
-            byte[] bytes=_content.toByteArray();
-
-            String content_type=get(HttpHeader.CONTENT_TYPE);
-            String encoding=MimeTypes.getCharsetFromContentType(content_type);
-            Charset charset=encoding==null?StandardCharsets.UTF_8:Charset.forName(encoding);
-
-            return new String(bytes,charset);
-        }
-
-        @Override
-        public HttpGenerator.ResponseInfo getInfo()
-        {
-            return new HttpGenerator.ResponseInfo(_version,this,_content==null?-1:_content.size(),_status,_reason,false);
-        }
-
-        @Override
-        public String toString()
-        {
-            return String.format("%s %s %s\n%s\n",_version,_status,_reason,super.toString());
-        }
-    }
-}
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTokens.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTokens.java
index 53b84d1..c955ffa 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTokens.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTokens.java
@@ -32,7 +32,7 @@
     static final byte[] CRLF = {CARRIAGE_RETURN,LINE_FEED};
     static final byte SEMI_COLON= (byte)';';
 
-    public enum EndOfContent { UNKNOWN_CONTENT,NO_CONTENT,EOF_CONTENT,CONTENT_LENGTH,CHUNKED_CONTENT,SELF_DEFINING_CONTENT }
+    public enum EndOfContent { UNKNOWN_CONTENT,NO_CONTENT,EOF_CONTENT,CONTENT_LENGTH,CHUNKED_CONTENT }
 
 }
 
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
index 375be00..4e1464e 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
@@ -20,15 +20,14 @@
 
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 
 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;
 
 
 /* ------------------------------------------------------------ */
@@ -45,359 +44,321 @@
  * <li>{@link #getQuery()} - query</li>
  * <li>{@link #getFragment()} - fragment</li>
  * </ul>
- *
+ * 
+ * <p>Any parameters will be returned from {@link #getPath()}, but are excluded from the
+ * return value of {@link #getDecodedPath()}.   If there are multiple parameters, the 
+ * {@link #getParam()} method returns only the last one.
  */
 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;
+    private enum State {
+    START,
+    HOST_OR_PATH,
+    SCHEME_OR_PATH,
+    HOST,
+    IPV6,
+    PORT,
+    PATH,
+    PARAM,
+    QUERY,
+    FRAGMENT,
+    ASTERISK};
 
-    final Charset _charset;
-    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;
-
-    public HttpURI()
-    {
-        _charset = URIUtil.__CHARSET;
-    }
-
-    public HttpURI(Charset charset)
-    {
-        _charset = charset;
-    }
+    private String _scheme;
+    private String _user;
+    private String _host;
+    private int _port;
+    private String _path;
+    private String _param;
+    private String _query;
+    private String _fragment;
+    
+    String _uri;
+    String _decodedPath;
 
     /* ------------------------------------------------------------ */
     /**
-     * @param parsePartialAuth If True, parse auth without prior scheme, else treat all URIs starting with / as paths
+     * Construct a normalized URI.
+     * Port is not set if it is the default port.
+     * @param scheme the URI scheme
+     * @param host the URI hose
+     * @param port the URI port
+     * @param path the URI path
+     * @param param the URI param
+     * @param query the URI query
+     * @param fragment the URI fragment
+     * @return the normalized URI
      */
-    public HttpURI(boolean parsePartialAuth)
+    public static HttpURI createHttpURI(String scheme, String host, int port, String path, String param, String query, String fragment)
     {
-        _partial=parsePartialAuth;
-        _charset = URIUtil.__CHARSET;
+        if (port==80 && HttpScheme.HTTP.is(scheme))
+            port=0;
+        if (port==443 && HttpScheme.HTTPS.is(scheme))
+            port=0;
+        return new HttpURI(scheme,host,port,path,param,query,fragment);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public HttpURI()
+    {
     }
 
-    public HttpURI(String raw)
+    /* ------------------------------------------------------------ */
+    public HttpURI(String scheme, String host, int port, String path, String param, String query, String fragment)
     {
-        _rawString=raw;
-        byte[] b = raw.getBytes(StandardCharsets.UTF_8);
-        parse(b,0,b.length);
-        _charset = URIUtil.__CHARSET;
+        _scheme = scheme;
+        _host = host;
+        _port = port;
+        _path = path;
+        _param = param;
+        _query = query;
+        _fragment = fragment;
     }
 
-    public HttpURI(byte[] raw,int offset, int length)
+    /* ------------------------------------------------------------ */
+    public HttpURI(HttpURI uri)
     {
-        parse2(raw,offset,length);
-        _charset = URIUtil.__CHARSET;
+        this(uri._scheme,uri._host,uri._port,uri._path,uri._param,uri._query,uri._fragment);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public HttpURI(String uri)
+    {
+        _port=-1;
+        parse(State.START,uri,0,uri.length());
     }
 
+    /* ------------------------------------------------------------ */
     public HttpURI(URI uri)
     {
-        parse(uri.toASCIIString());
-        _charset = URIUtil.__CHARSET;
-    }
-
-    public void parse(String raw)
-    {
-        byte[] b = StringUtil.getUtf8Bytes(raw);
-        parse2(b,0,b.length);
-        _rawString=raw;
-    }
-
-    public void parseConnect(String raw)
-    {
-        byte[] b = StringUtil.getBytes(raw);
-        parseConnect(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)
+        _uri=null;
+        
+        _scheme=uri.getScheme();
+        _host=uri.getHost();
+        if (_host==null && uri.getRawSchemeSpecificPart().startsWith("//"))
+            _host="";
+        _port=uri.getPort();
+        _user = uri.getUserInfo();
+        _path=uri.getRawPath();
+        
+        _decodedPath = uri.getPath();
+        if (_decodedPath != null)
         {
-            char c=(char)(0xff&_raw[i]);
-            int s=i++;
+            int p = _decodedPath.lastIndexOf(';');
+            if (p >= 0)
+                _param = _decodedPath.substring(p + 1);
+        }
+        _query=uri.getRawQuery();
+        _fragment=uri.getFragment();
+        
+        _decodedPath=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    public HttpURI(String scheme, String host, int port, String pathQuery)
+    {
+        _uri=null;
+        
+        _scheme=scheme;
+        _host=host;
+        _port=port;
+
+        parse(State.PATH,pathQuery,0,pathQuery.length());
+        
+    }
+
+    /* ------------------------------------------------------------ */
+    public void parse(String uri)
+    {
+        clear();
+        _uri=uri;
+        parse(State.START,uri,0,uri.length());
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Parse according to https://tools.ietf.org/html/rfc7230#section-5.3
+     * @param method The method to parse the URI against (used to allow CONNECT exceptions)
+     * @param uri The URI to parse
+     */
+    public void parseRequestTarget(String method,String uri)
+    {
+        clear();
+        _uri=uri;
+
+        if (HttpMethod.CONNECT.is(method))
+            _path=uri;
+        else
+            parse(uri.startsWith("/")?State.PATH:State.START,uri,0,uri.length());
+    }
+
+    /* ------------------------------------------------------------ */
+    @Deprecated
+    public void parseConnect(String uri)
+    {
+        clear();
+        _uri=uri;
+        _path=uri;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void parse(String uri, int offset, int length)
+    {
+        clear();
+        int end=offset+length;
+        _uri=uri.substring(offset,end);
+        parse(State.START,uri,offset,end);
+    }
+
+    /* ------------------------------------------------------------ */
+    private void parse(State state, final String uri, final int offset, final int end)
+    {
+        boolean encoded=false;
+        int mark=offset;
+        int path_mark=0;
+        
+        for (int i=offset; i<end; i++)
+        {
+            char c=uri.charAt(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 " + new String(_raw,offset,length,_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;
+                            mark = i;
+                            state = State.HOST_OR_PATH;
                             break;
                         case ';':
-                            _param=s;
-                            state=PARAM;
+                            mark=i+1;
+                            state=State.PARAM;
                             break;
                         case '?':
-                            _param=s;
-                            _query=s;
-                            state=QUERY;
+                            // assume empty path (if seen at start)
+                            _path = "";
+                            mark=i+1;
+                            state=State.QUERY;
                             break;
                         case '#':
-                            _param=s;
-                            _query=s;
-                            _fragment=s;
+                            mark=i+1;
+                            state=State.FRAGMENT;
                             break;
                         case '*':
-                            _path=s;
-                            state=ASTERISK;
+                            _path="*";
+                            state=State.ASTERISK;
                             break;
 
                         default:
-                            state=SCHEME_OR_PATH;
+                            mark=i;
+                            if (_scheme==null)
+                                state=State.SCHEME_OR_PATH;
+                            else
+                            {
+                                path_mark=i;
+                                state=State.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;
-                            }
+                            // must have been a scheme
+                            _scheme=uri.substring(mark,i);
+                            // Start again with scheme set
+                            state=State.START;
                             break;
-                        }
 
                         case '/':
-                        {
-                            state = PATH;
+                            // must have been in a path and still are
+                            state=State.PATH;
                             break;
-                        }
 
                         case ';':
-                        {
-                            _param = s;
-                            state = PARAM;
+                            // must have been in a path 
+                            mark=i+1;
+                            state=State.PARAM;
                             break;
-                        }
 
                         case '?':
-                        {
-                            _param = s;
-                            _query = s;
-                            state = QUERY;
+                            // must have been in a path 
+                            _path=uri.substring(mark,i);
+                            mark=i+1;
+                            state=State.QUERY;
                             break;
-                        }
 
-                        case '#':
-                        {
-                            _param = s;
-                            _query = s;
-                            _fragment = s;
+                        case '%':
+                            // must have be in an encoded path 
+                            encoded=true;
+                            state=State.PATH;
                             break;
-                        }
+                        
+                        case '#':
+                            // must have been in a path 
+                            _path=uri.substring(mark,i);
+                            state=State.FRAGMENT;
+                            break;
+                    }
+                    continue;
+                }
+                
+                case HOST_OR_PATH:
+                {
+                    switch(c)
+                    {
+                        case '/':
+                            _host="";
+                            mark=i+1;
+                            state=State.HOST;
+                            break;
+                            
+                        case '@':
+                        case ';':
+                        case '?':
+                        case '#':
+                            // was a path, look again
+                            i--;
+                            path_mark=mark;
+                            state=State.PATH;
+                            break;
+                        default:
+                            // it is a path
+                            path_mark=mark;
+                            state=State.PATH;
                     }
                     continue;
                 }
 
-                case AUTH:
+                case HOST:
                 {
                     switch (c)
                     {
-
                         case '/':
-                        {
-                            m = s;
-                            _path = m;
-                            _port = _path;
-                            state = PATH;
+                            _host = uri.substring(mark,i);
+                            path_mark=mark=i;
+                            state=State.PATH;
                             break;
-                        }
-                        case '@':
-                        {
-                            _host = i;
-                            break;
-                        }
                         case ':':
-                        {
-                            _port = s;
-                            state = PORT;
+                            if (i > mark)
+                                _host=uri.substring(mark,i);
+                            mark=i+1;
+                            state=State.PORT;
                             break;
-                        }
+                        case '@':
+                            if (_user!=null)
+                                throw new IllegalArgumentException("Bad authority");
+                            _user=uri.substring(mark,i);
+                            mark=i+1;
+                            break;
+                            
                         case '[':
-                        {
-                            state = IPV6;
+                            state=State.IPV6;
                             break;
-                        }
                     }
                     continue;
                 }
@@ -407,14 +368,21 @@
                     switch (c)
                     {
                         case '/':
-                        {
-                            throw new IllegalArgumentException("No closing ']' for " + new String(_raw,offset,length,_charset));
-                        }
+                            throw new IllegalArgumentException("No closing ']' for ipv6 in " + uri);
                         case ']':
-                        {
-                            state = AUTH;
+                            c = uri.charAt(++i);
+                            _host=uri.substring(mark,i);
+                            if (c == ':')
+                            {
+                                mark=i+1;
+                                state=State.PORT;
+                            }
+                            else
+                            {
+                                path_mark=mark=i;
+                                state=State.PATH;
+                            }
                             break;
-                        }
                     }
 
                     continue;
@@ -422,13 +390,20 @@
 
                 case PORT:
                 {
-                    if (c=='/')
+                    if (c=='@')
                     {
-                        m=s;
-                        _path=m;
-                        if (_port<=_authority)
-                            _port=_path;
-                        state=PATH;
+                        if (_user!=null)
+                            throw new IllegalArgumentException("Bad authority");
+                        // It wasn't a port, but a password!
+                        _user=_host+":"+uri.substring(mark,i);
+                        mark=i+1;
+                        state=State.HOST;
+                    }
+                    else if (c=='/')
+                    {
+                        _port=TypeUtil.parseInt(uri,mark,i-mark,10);
+                        path_mark=mark=i;
+                        state=State.PATH;
                     }
                     continue;
                 }
@@ -438,29 +413,22 @@
                     switch (c)
                     {
                         case ';':
-                        {
-                            _param = s;
-                            state = PARAM;
+                            mark=i+1;
+                            state=State.PARAM;
                             break;
-                        }
                         case '?':
-                        {
-                            _param = s;
-                            _query = s;
-                            state = QUERY;
+                            _path=uri.substring(path_mark,i);
+                            mark=i+1;
+                            state=State.QUERY;
                             break;
-                        }
                         case '#':
-                        {
-                            _param = s;
-                            _query = s;
-                            _fragment = s;
-                            break state;
-                        }
+                            _path=uri.substring(path_mark,i);
+                            mark=i+1;
+                            state=State.FRAGMENT;
+                            break;
                         case '%':
-                        {
-                            _encoded=true;
-                        }
+                            encoded=true;
+                            break;
                     }
                     continue;
                 }
@@ -470,17 +438,26 @@
                     switch (c)
                     {
                         case '?':
-                        {
-                            _query = s;
-                            state = QUERY;
+                            _path=uri.substring(path_mark,i);
+                            _param=uri.substring(mark,i);
+                            mark=i+1;
+                            state=State.QUERY;
                             break;
-                        }
                         case '#':
-                        {
-                            _query = s;
-                            _fragment = s;
-                            break state;
-                        }
+                            _path=uri.substring(path_mark,i);
+                            _param=uri.substring(mark,i);
+                            mark=i+1;
+                            state=State.FRAGMENT;
+                            break;
+                        case '/':
+                            encoded=true;
+                            // ignore internal params
+                            state=State.PATH;
+                            break;
+                        case ';':
+                            // multiple parameters
+                            mark=i+1;
+                            break;
                     }
                     continue;
                 }
@@ -489,8 +466,9 @@
                 {
                     if (c=='#')
                     {
-                        _fragment=s;
-                        break state;
+                        _query=uri.substring(mark,i);
+                        mark=i+1;
+                        state=State.FRAGMENT;
                     }
                     continue;
                 }
@@ -499,284 +477,307 @@
                 {
                     throw new IllegalArgumentException("only '*'");
                 }
+                
+                case FRAGMENT:
+                {
+                    _fragment=uri.substring(mark,end);
+                    i=end;
+                }
             }
         }
 
-        if (_port<_path)
-            _portValue=TypeUtil.parseInt(_raw, _port+1, _path-_port-1,10);
+        
+        switch(state)
+        {
+            case START:
+                break;
+            case SCHEME_OR_PATH:
+                _path=uri.substring(mark,end);
+                break;
+
+            case HOST_OR_PATH:
+                _path=uri.substring(mark,end);
+                break;
+                
+            case HOST:
+                if(end>mark)
+                    _host=uri.substring(mark,end);
+                break;
+                
+            case IPV6:
+                throw new IllegalArgumentException("No closing ']' for ipv6 in " + uri);
+
+            case PORT:
+                _port=TypeUtil.parseInt(uri,mark,end-mark,10);
+                break;
+                
+            case ASTERISK:
+                break;
+                
+            case FRAGMENT:
+                _fragment=uri.substring(mark,end);
+                break;
+                
+            case PARAM:
+                _path=uri.substring(path_mark,end);
+                _param=uri.substring(mark,end);
+                break;
+                
+            case PATH:
+                _path=uri.substring(path_mark,end);
+                break;
+                
+            case QUERY:
+                _query=uri.substring(mark,end);
+                break;
+        }
+        
+        if (!encoded)
+        {
+            if (_param==null)
+                _decodedPath=_path;
+            else
+                _decodedPath=_path.substring(0,_path.length()-_param.length()-1);
+        }
     }
 
+    /* ------------------------------------------------------------ */
     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 HttpScheme.HTTP.asString();
-        if (l==6 &&
-                _raw[_scheme]=='h' &&
-                _raw[_scheme+1]=='t' &&
-                _raw[_scheme+2]=='t' &&
-                _raw[_scheme+3]=='p' &&
-                _raw[_scheme+4]=='s' )
-            return HttpScheme.HTTPS.asString();
-
-        return new String(_raw,_scheme,_authority-_scheme-1,_charset);
+        return _scheme;
     }
 
-    public String getAuthority()
-    {
-        if (_authority==_path)
-            return null;
-        return new String(_raw,_authority,_path-_authority,_charset);
-    }
-
+    /* ------------------------------------------------------------ */
     public String getHost()
     {
-        if (_host==_port)
+        // Return null for empty host to retain compatibility with java.net.URI
+        if (_host!=null && _host.length()==0)
             return null;
-        return new String(_raw,_host,_port-_host,_charset);
+        return _host;
     }
 
+    /* ------------------------------------------------------------ */
     public int getPort()
     {
-        return _portValue;
+        return _port;
     }
 
+    /* ------------------------------------------------------------ */
+    /**
+     * The parsed Path.
+     * 
+     * @return the path as parsed on valid URI.  null for invalid URI.
+     */
     public String getPath()
     {
-        if (_path==_param)
-            return null;
-        return new String(_raw,_path,_param-_path,_charset);
+        return _path;
     }
 
+    /* ------------------------------------------------------------ */
     public String getDecodedPath()
     {
-        if (_path==_param)
-            return null;
-
-        Utf8StringBuilder utf8b=null;
-
-        for (int i=_path;i<_param;i++)
-        {
-            byte b = _raw[i];
-
-            if (b=='%')
-            {
-                if (utf8b==null)
-                {
-                    utf8b=new Utf8StringBuilder();
-                    utf8b.append(_raw,_path,i-_path);
-                }
-                
-                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 (utf8b!=null)
-            {
-                utf8b.append(b);
-            }
-        }
-
-        if (utf8b==null)
-            return StringUtil.toUTF8String(_raw, _path, _param-_path);
-        return utf8b.toString();
+        if (_decodedPath==null && _path!=null)
+            _decodedPath=URIUtil.decodePath(_path);
+        return _decodedPath;
     }
 
-    public String getDecodedPath(String encoding)
-    {
-        return getDecodedPath(Charset.forName(encoding));
-    }
-
-    public String getDecodedPath(Charset 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 new String(_raw,_path,_param-_path,encoding);
-
-        return new String(bytes,0,n,encoding);
-    }
-
-    public String getPathAndParam()
-    {
-        if (_path==_query)
-            return null;
-        return new String(_raw,_path,_query-_path,_charset);
-    }
-
-    public String getCompletePath()
-    {
-        if (_path==_end)
-            return null;
-        return new String(_raw,_path,_end-_path,_charset);
-    }
-
+    /* ------------------------------------------------------------ */
     public String getParam()
     {
-        if (_param==_query)
-            return null;
-        return new String(_raw,_param+1,_query-_param-1,_charset);
+        return _param;
     }
 
+    /* ------------------------------------------------------------ */
     public String getQuery()
     {
-        if (_query==_fragment)
-            return null;
-        return new String(_raw,_query+1,_fragment-_query-1,_charset);
+        return _query;
     }
 
-    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);
+        return _query!=null && _query.length()>0;
     }
 
+    /* ------------------------------------------------------------ */
     public String getFragment()
     {
-        if (_fragment==_end)
-            return null;
-        return new String(_raw,_fragment+1,_end-_fragment-1,_charset);
+        return _fragment;
     }
 
+    /* ------------------------------------------------------------ */
     public void decodeQueryTo(MultiMap<String> parameters)
     {
         if (_query==_fragment)
             return;
-        if (_charset.equals(StandardCharsets.UTF_8))
-            UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
-        else
-            UrlEncoded.decodeTo(new String(_raw,_query+1,_fragment-_query-1,_charset),parameters,_charset,-1);
+        UrlEncoded.decodeUtf8To(_query,parameters);
     }
 
+    /* ------------------------------------------------------------ */
     public void decodeQueryTo(MultiMap<String> 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,-1);
+        decodeQueryTo(parameters,Charset.forName(encoding));
     }
 
+    /* ------------------------------------------------------------ */
     public void decodeQueryTo(MultiMap<String> parameters, Charset encoding) throws UnsupportedEncodingException
     {
         if (_query==_fragment)
             return;
 
         if (encoding==null || StandardCharsets.UTF_8.equals(encoding))
-            UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
+            UrlEncoded.decodeUtf8To(_query,parameters);
         else
-            UrlEncoded.decodeTo(new String(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding,-1);
+            UrlEncoded.decodeTo(_query,parameters,encoding);
     }
 
+    /* ------------------------------------------------------------ */
     public void clear()
     {
-        _scheme=_authority=_host=_port=_path=_param=_query=_fragment=_end=0;
-        _raw=__empty;
-        _rawString="";
-        _encoded=false;
+        _uri=null;
+
+        _scheme=null;
+        _host=null;
+        _port=-1;
+        _path=null;
+        _param=null;
+        _query=null;
+        _fragment=null;
+
+        _decodedPath=null;
     }
 
+    /* ------------------------------------------------------------ */
+    public boolean isAbsolute()
+    {
+        return _scheme!=null && _scheme.length()>0;
+    }
+    
+    /* ------------------------------------------------------------ */
     @Override
     public String toString()
     {
-        if (_rawString==null)
-            _rawString=new String(_raw,_scheme,_end-_scheme,_charset);
-        return _rawString;
+        if (_uri==null)
+        {
+            StringBuilder out = new StringBuilder();
+            
+            if (_scheme!=null)
+                out.append(_scheme).append(':');
+            
+            if (_host != null)
+            {
+                out.append("//");
+                if (_user != null)
+                    out.append(_user).append('@');
+                out.append(_host);
+            }
+            
+            if (_port>0)
+                out.append(':').append(_port);
+            
+            if (_path!=null)
+                out.append(_path);
+            
+            if (_query!=null)
+                out.append('?').append(_query);
+            
+            if (_fragment!=null)
+                out.append('#').append(_fragment);
+            
+            if (out.length()>0)
+                _uri=out.toString();
+            else
+                _uri="";
+        }
+        return _uri;
     }
 
-    public void writeTo(Utf8StringBuilder buf)
+    /* ------------------------------------------------------------ */
+    public boolean equals(Object o)
     {
-        buf.append(_raw,_scheme,_end-_scheme);
+        if (o==this)
+            return true;
+        if (!(o instanceof HttpURI))
+            return false;
+        return toString().equals(o.toString());
     }
 
+    /* ------------------------------------------------------------ */
+    public void setScheme(String scheme)
+    {
+        _scheme=scheme;
+        _uri=null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param host the host
+     * @param port the port
+     */
+    public void setAuthority(String host, int port)
+    {
+        _host=host;
+        _port=port;
+        _uri=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param path the path
+     */
+    public void setPath(String path)
+    {
+        _uri=null;
+        _path=path;
+        _decodedPath=null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void setPathQuery(String path)
+    {
+        _uri=null;
+        _path=null;
+        _decodedPath=null;
+        _param=null;
+        _fragment=null;
+        if (path!=null)
+            parse(State.PATH,path,0,path.length());
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void setQuery(String query)
+    {
+        _query=query;
+        _uri=null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public URI toURI() throws URISyntaxException
+    {
+        return new URI(_scheme,null,_host,_port,_path,_query==null?null:UrlEncoded.decodeString(_query),_fragment);
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getPathQuery()
+    {
+        if (_query==null)
+            return _path;
+        return _path+"?"+_query;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public String getAuthority()
+    {
+        if (_port>0)
+            return _host+":"+_port;
+        return _host;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public String getUser()
+    {
+        return _user;
+    }
+
+
 }
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java b/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java
new file mode 100644
index 0000000..0224ba4
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java
@@ -0,0 +1,307 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.Collections;
+import java.util.Iterator;
+
+public class MetaData implements Iterable<HttpField>
+{
+    private HttpVersion _httpVersion;
+    private HttpFields _fields;
+    private long _contentLength;
+
+    public MetaData(HttpVersion version, HttpFields fields)
+    {
+        this(version, fields, Long.MIN_VALUE);
+    }
+
+    public MetaData(HttpVersion version, HttpFields fields, long contentLength)
+    {
+        _httpVersion = version;
+        _fields = fields;
+        _contentLength = contentLength;
+    }
+
+    protected void recycle()
+    {
+        _httpVersion = null;
+        if (_fields != null)
+            _fields.clear();
+        _contentLength = Long.MIN_VALUE;
+    }
+
+    public boolean isRequest()
+    {
+        return false;
+    }
+
+    public boolean isResponse()
+    {
+        return false;
+    }
+
+    /**
+     * @deprecated use {@link #getHttpVersion()} instead
+     */
+    @Deprecated
+    public HttpVersion getVersion()
+    {
+        return getHttpVersion();
+    }
+
+    /**
+     * @return the HTTP version of this MetaData object
+     */
+    public HttpVersion getHttpVersion()
+    {
+        return _httpVersion;
+    }
+
+    /**
+     * @param httpVersion the HTTP version to set
+     */
+    public void setHttpVersion(HttpVersion httpVersion)
+    {
+        _httpVersion = httpVersion;
+    }
+
+    /**
+     * @return the HTTP fields of this MetaData object
+     */
+    public HttpFields getFields()
+    {
+        return _fields;
+    }
+
+    /**
+     * @return the content length if available, otherwise {@link Long#MIN_VALUE}
+     */
+    public long getContentLength()
+    {
+        if (_contentLength == Long.MIN_VALUE)
+        {
+            if (_fields != null)
+            {
+                HttpField field = _fields.getField(HttpHeader.CONTENT_LENGTH);
+                _contentLength = field == null ? -1 : field.getLongValue();
+            }
+        }
+        return _contentLength;
+    }
+
+    /**
+     * @return an iterator over the HTTP fields
+     * @see #getFields()
+     */
+    public Iterator<HttpField> iterator()
+    {
+        HttpFields fields = getFields();
+        return fields == null ? Collections.<HttpField>emptyIterator() : fields.iterator();
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder out = new StringBuilder();
+        for (HttpField field : this)
+            out.append(field).append(System.lineSeparator());
+        return out.toString();
+    }
+
+    public static class Request extends MetaData
+    {
+        private String _method;
+        private HttpURI _uri;
+
+        public Request(HttpFields fields)
+        {
+            this(null, null, null, fields);
+        }
+
+        public Request(String method, HttpURI uri, HttpVersion version, HttpFields fields)
+        {
+            this(method, uri, version, fields, Long.MIN_VALUE);
+        }
+
+        public Request(String method, HttpURI uri, HttpVersion version, HttpFields fields, long contentLength)
+        {
+            super(version, fields, contentLength);
+            _method = method;
+            _uri = uri;
+        }
+
+        public Request(String method, HttpScheme scheme, HostPortHttpField hostPort, String uri, HttpVersion version, HttpFields fields)
+        {
+            this(method, new HttpURI(scheme == null ? null : scheme.asString(), hostPort.getHost(), hostPort.getPort(), uri), version, fields);
+        }
+
+        public Request(String method, HttpScheme scheme, HostPortHttpField hostPort, String uri, HttpVersion version, HttpFields fields, long contentLength)
+        {
+            this(method, new HttpURI(scheme == null ? null : scheme.asString(), hostPort.getHost(), hostPort.getPort(), uri), version, fields, contentLength);
+        }
+
+        public Request(String method, String scheme, HostPortHttpField hostPort, String uri, HttpVersion version, HttpFields fields, long contentLength)
+        {
+            this(method, new HttpURI(scheme, hostPort.getHost(), hostPort.getPort(), uri), version, fields, contentLength);
+        }
+
+        public Request(Request request)
+        {
+            this(request.getMethod(),new HttpURI(request.getURI()), request.getHttpVersion(), new HttpFields(request.getFields()), request.getContentLength());
+        }
+
+        // TODO MetaData should be immuttable!!! 
+        public void recycle()
+        {
+            super.recycle();
+            _method = null;
+            if (_uri != null)
+                _uri.clear();
+        }
+
+        @Override
+        public boolean isRequest()
+        {
+            return true;
+        }
+
+        /**
+         * @return the HTTP method
+         */
+        public String getMethod()
+        {
+            return _method;
+        }
+
+        /**
+         * @param method the HTTP method to set
+         */
+        public void setMethod(String method)
+        {
+            _method = method;
+        }
+
+        /**
+         * @return the HTTP URI
+         */
+        public HttpURI getURI()
+        {
+            return _uri;
+        }
+
+        /**
+         * @return the HTTP URI in string form
+         */
+        public String getURIString()
+        {
+            return _uri == null ? null : _uri.toString();
+        }
+
+        /**
+         * @param uri the HTTP URI to set
+         */
+        public void setURI(HttpURI uri)
+        {
+            _uri = uri;
+        }
+
+        @Override
+        public String toString()
+        {
+            HttpFields fields = getFields();
+            return String.format("%s{u=%s,%s,h=%d}",
+                    getMethod(), getURI(), getHttpVersion(), fields == null ? -1 : fields.size());
+        }
+    }
+
+    public static class Response extends MetaData
+    {
+        private int _status;
+        private String _reason;
+
+        public Response()
+        {
+            this(null, 0, null);
+        }
+
+        public Response(HttpVersion version, int status, HttpFields fields)
+        {
+            this(version, status, fields, Long.MIN_VALUE);
+        }
+
+        public Response(HttpVersion version, int status, HttpFields fields, long contentLength)
+        {
+            super(version, fields, contentLength);
+            _status = status;
+        }
+
+        public Response(HttpVersion version, int status, String reason, HttpFields fields, long contentLength)
+        {
+            super(version, fields, contentLength);
+            _reason = reason;
+            _status = status;
+        }
+
+        @Override
+        public boolean isResponse()
+        {
+            return true;
+        }
+
+        /**
+         * @return the HTTP status
+         */
+        public int getStatus()
+        {
+            return _status;
+        }
+
+        /**
+         * @return the HTTP reason
+         */
+        public String getReason()
+        {
+            return _reason;
+        }
+
+        /**
+         * @param status the HTTP status to set
+         */
+        public void setStatus(int status)
+        {
+            _status = status;
+        }
+
+        /**
+         * @param reason the HTTP reason to set
+         */
+        public void setReason(String reason)
+        {
+            _reason = reason;
+        }
+
+        @Override
+        public String toString()
+        {
+            HttpFields fields = getFields();
+            return String.format("%s{s=%d,h=%d}", getHttpVersion(), getStatus(), fields == null ? -1 : fields.size());
+        }
+    }
+}
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java b/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java
index 4866c05..5645d55 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java
@@ -18,16 +18,18 @@
 
 package org.eclipse.jetty.http;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
-import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
+import java.util.Properties;
 import java.util.Set;
 
 import org.eclipse.jetty.util.ArrayTrie;
@@ -39,11 +41,18 @@
 
 
 /* ------------------------------------------------------------ */
-/**
- * 
+/** MIME Type enum and utilities
+ *
  */
 public class MimeTypes
 {
+    /* ------------------------------------------------------------ */
+    private static final Logger LOG = Log.getLogger(MimeTypes.class);
+    private static final  Trie<ByteBuffer> TYPES= new ArrayTrie<ByteBuffer>(512);
+    private static final  Map<String,String> __dftMimeMap = new HashMap<String,String>();
+    private static final  Map<String,String> __inferredEncodings = new HashMap<String,String>();
+    private static final  Map<String,String> __assumedEncodings = new HashMap<String,String>();
+    
     public enum Type
     {
         FORM_ENCODED("application/x-www-form-urlencoded"),
@@ -56,20 +65,20 @@
         TEXT_JSON("text/json",StandardCharsets.UTF_8),
         APPLICATION_JSON("application/json",StandardCharsets.UTF_8),
 
-        TEXT_HTML_8859_1("text/html; charset=ISO-8859-1",TEXT_HTML),
-        TEXT_HTML_UTF_8("text/html; charset=UTF-8",TEXT_HTML),
-        
-        TEXT_PLAIN_8859_1("text/plain; charset=ISO-8859-1",TEXT_PLAIN),
-        TEXT_PLAIN_UTF_8("text/plain; charset=UTF-8",TEXT_PLAIN),
-        
-        TEXT_XML_8859_1("text/xml; charset=ISO-8859-1",TEXT_XML),
-        TEXT_XML_UTF_8("text/xml; charset=UTF-8",TEXT_XML),
-        
-        TEXT_JSON_8859_1("text/json; charset=ISO-8859-1",TEXT_JSON),
-        TEXT_JSON_UTF_8("text/json; charset=UTF-8",TEXT_JSON),
-        
-        APPLICATION_JSON_8859_1("text/json; charset=ISO-8859-1",APPLICATION_JSON),
-        APPLICATION_JSON_UTF_8("text/json; charset=UTF-8",APPLICATION_JSON);
+        TEXT_HTML_8859_1("text/html;charset=iso-8859-1",TEXT_HTML),
+        TEXT_HTML_UTF_8("text/html;charset=utf-8",TEXT_HTML),
+
+        TEXT_PLAIN_8859_1("text/plain;charset=iso-8859-1",TEXT_PLAIN),
+        TEXT_PLAIN_UTF_8("text/plain;charset=utf-8",TEXT_PLAIN),
+
+        TEXT_XML_8859_1("text/xml;charset=iso-8859-1",TEXT_XML),
+        TEXT_XML_UTF_8("text/xml;charset=utf-8",TEXT_XML),
+
+        TEXT_JSON_8859_1("text/json;charset=iso-8859-1",TEXT_JSON),
+        TEXT_JSON_UTF_8("text/json;charset=utf-8",TEXT_JSON),
+
+        APPLICATION_JSON_8859_1("application/json;charset=iso-8859-1",APPLICATION_JSON),
+        APPLICATION_JSON_UTF_8("application/json;charset=utf-8",APPLICATION_JSON);
 
 
         /* ------------------------------------------------------------ */
@@ -77,6 +86,7 @@
         private final Type _base;
         private final ByteBuffer _buffer;
         private final Charset _charset;
+        private final String _charsetString;
         private final boolean _assumedCharset;
         private final HttpField _field;
 
@@ -87,9 +97,10 @@
             _buffer=BufferUtil.toBuffer(s);
             _base=this;
             _charset=null;
+            _charsetString=null;
             _assumedCharset=false;
-            _field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,_string);
-        } 
+            _field=new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,_string);
+        }
 
         /* ------------------------------------------------------------ */
         Type(String s,Type base)
@@ -97,10 +108,11 @@
             _string=s;
             _buffer=BufferUtil.toBuffer(s);
             _base=base;
-            int i=s.indexOf("; charset=");
-            _charset=Charset.forName(s.substring(i+10));
+            int i=s.indexOf(";charset=");
+            _charset=Charset.forName(s.substring(i+9));
+            _charsetString=_charset.toString().toLowerCase(Locale.ENGLISH);
             _assumedCharset=false;
-            _field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,_string);
+            _field=new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,_string);
         }
 
         /* ------------------------------------------------------------ */
@@ -110,8 +122,9 @@
             _base=this;
             _buffer=BufferUtil.toBuffer(s);
             _charset=cs;
+            _charsetString=_charset==null?null:_charset.toString().toLowerCase(Locale.ENGLISH);
             _assumedCharset=true;
-            _field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,_string);
+            _field=new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,_string);
         }
 
         /* ------------------------------------------------------------ */
@@ -119,17 +132,23 @@
         {
             return _buffer.asReadOnlyBuffer();
         }
-        
+
         /* ------------------------------------------------------------ */
         public Charset getCharset()
         {
             return _charset;
         }
-        
+
+        /* ------------------------------------------------------------ */
+        public String getCharsetString()
+        {
+            return _charsetString;
+        }
+
         /* ------------------------------------------------------------ */
         public boolean is(String s)
         {
-            return _string.equalsIgnoreCase(s);    
+            return _string.equalsIgnoreCase(s);
         }
 
         /* ------------------------------------------------------------ */
@@ -137,7 +156,7 @@
         {
             return _string;
         }
-        
+
         /* ------------------------------------------------------------ */
         @Override
         public String toString()
@@ -165,12 +184,7 @@
     }
 
     /* ------------------------------------------------------------ */
-    private static final Logger LOG = Log.getLogger(MimeTypes.class);
-    public  final static Trie<MimeTypes.Type> CACHE= new ArrayTrie<>(512);
-    private final static Trie<ByteBuffer> TYPES= new ArrayTrie<ByteBuffer>(512);
-    private final static Map<String,String> __dftMimeMap = new HashMap<String,String>();
-    private final static Map<String,String> __encodings = new HashMap<String,String>();
-
+    public  static final Trie<MimeTypes.Type> CACHE= new ArrayTrie<>(512);
     static
     {
         for (MimeTypes.Type type : MimeTypes.Type.values())
@@ -181,39 +195,95 @@
             int charset=type.toString().indexOf(";charset=");
             if (charset>0)
             {
-                CACHE.put(type.toString().replace(";charset=","; charset="),type);
-                TYPES.put(type.toString().replace(";charset=","; charset="),type.asBuffer());
+                String alt=type.toString().replace(";charset=","; charset=");
+                CACHE.put(alt,type);
+                TYPES.put(alt,type.asBuffer());
             }
+            
+            if (type.isCharsetAssumed())
+                __assumedEncodings.put(type.asString(),type.getCharsetString());
         }
 
-        try
+        String resourceName = "org/eclipse/jetty/http/mime.properties";
+        try (InputStream stream = MimeTypes.class.getClassLoader().getResourceAsStream(resourceName))
         {
-            ResourceBundle mime = ResourceBundle.getBundle("org/eclipse/jetty/http/mime");
-            Enumeration<String> i = mime.getKeys();
-            while(i.hasMoreElements())
+            if (stream == null)
             {
-                String ext = i.nextElement();
-                String m = mime.getString(ext);
-                __dftMimeMap.put(StringUtil.asciiToLowerCase(ext),normalizeMimeType(m));
+                LOG.warn("Missing mime-type resource: {}", resourceName);
+            }
+            else
+            {
+                try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8))
+                {
+                    Properties props = new Properties();
+                    props.load(reader);
+                    props.stringPropertyNames().stream()
+                    .filter(x->x!=null)
+                    .forEach(x->
+                    __dftMimeMap.put(StringUtil.asciiToLowerCase(x), normalizeMimeType(props.getProperty(x))));
+
+                    if (__dftMimeMap.size()==0)
+                    {
+                        LOG.warn("Empty mime types at {}", resourceName);
+                    }
+                    else if (__dftMimeMap.size()<props.keySet().size())
+                    {
+                        LOG.warn("Duplicate or null mime-type extension in resource: {}", resourceName);
+                    }  
+                }
+                catch (IOException e)
+                {
+                    LOG.warn(e.toString());
+                    LOG.debug(e);
+                }
+
             }
         }
-        catch(MissingResourceException e)
+        catch (IOException e)
         {
             LOG.warn(e.toString());
             LOG.debug(e);
         }
-
-        try
+        
+        resourceName = "org/eclipse/jetty/http/encoding.properties";
+        try (InputStream stream = MimeTypes.class.getClassLoader().getResourceAsStream(resourceName))
         {
-            ResourceBundle encoding = ResourceBundle.getBundle("org/eclipse/jetty/http/encoding");
-            Enumeration<String> i = encoding.getKeys();
-            while(i.hasMoreElements())
+            if (stream == null)
+                LOG.warn("Missing encoding resource: {}", resourceName);
+            else
             {
-                String type = i.nextElement();
-                __encodings.put(type,encoding.getString(type));
+                try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8))
+                {
+                    Properties props = new Properties();
+                    props.load(reader);
+                    props.stringPropertyNames().stream()
+                    .filter(t->t!=null)
+                    .forEach(t->
+                    {
+                        String charset = props.getProperty(t);
+                        if (charset.startsWith("-"))
+                            __assumedEncodings.put(t, charset.substring(1));
+                        else
+                            __inferredEncodings.put(t, props.getProperty(t));
+                    });
+
+                    if (__inferredEncodings.size()==0)
+                    {
+                        LOG.warn("Empty encodings at {}", resourceName);
+                    }
+                    else if ((__inferredEncodings.size()+__assumedEncodings.size())<props.keySet().size())
+                    {
+                        LOG.warn("Null or duplicate encodings in resource: {}", resourceName);
+                    }
+                }
+                catch (IOException e)
+                {
+                    LOG.warn(e.toString());
+                    LOG.debug(e);
+                }
             }
         }
-        catch(MissingResourceException e)
+        catch (IOException e)
         {
             LOG.warn(e.toString());
             LOG.debug(e);
@@ -250,9 +320,46 @@
                 _mimeMap.put(StringUtil.asciiToLowerCase(ext.getKey()),normalizeMimeType(ext.getValue()));
         }
     }
-    
+
     /* ------------------------------------------------------------ */
     /** Get the MIME type by filename extension.
+     * Lookup only the static default mime map.
+     * @param filename A file name
+     * @return MIME type matching the longest dot extension of the
+     * file name.
+     */
+    public static String getDefaultMimeByExtension(String filename)
+    {
+        String 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 (type==null)
+                    type=__dftMimeMap.get(ext);
+            }
+        }
+
+        if (type==null)
+        {
+            if (type==null)
+                type=__dftMimeMap.get("*");
+        }
+
+        return type;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the MIME type by filename extension.
+     * Lookup the content and static default mime maps.
      * @param filename A file name
      * @return MIME type matching the longest dot extension of the
      * file name.
@@ -292,8 +399,8 @@
 
     /* ------------------------------------------------------------ */
     /** Set a mime mapping
-     * @param extension
-     * @param type
+     * @param extension the extension
+     * @param type the mime type
      */
     public void addMimeMapping(String extension,String type)
     {
@@ -305,7 +412,7 @@
     {
         return new HashSet<>(__dftMimeMap.values());
     }
-    
+
     /* ------------------------------------------------------------ */
     private static String normalizeMimeType(String type)
     {
@@ -336,6 +443,12 @@
                     quote=false;
                 continue;
             }
+            
+            if(';'==b && state<=8)
+            {
+                state = 1;
+                continue;
+            }
 
             switch(state)
             {
@@ -345,8 +458,6 @@
                         quote=true;
                         break;
                     }
-                    if (';'==b)
-                        state=1;
                     break;
 
                 case 1: if ('c'==b) state=2; else if (' '!=b) state=0; break;
@@ -386,9 +497,42 @@
         return null;
     }
 
-    public static String inferCharsetFromContentType(String value)
+    /**
+     * Access a mutable map of mime type to the charset inferred from that content type.
+     * An inferred encoding is used by when encoding/decoding a stream and is 
+     * explicitly set in any metadata (eg Content-Type).
+     * @return Map of mime type to charset
+     */
+    public static Map<String,String> getInferredEncodings()
     {
-        return __encodings.get(value);
+        return __inferredEncodings;
+    }
+    
+    /**
+     * Access a mutable map of mime type to the charset assumed for that content type.
+     * An assumed encoding is used by when encoding/decoding a stream, but is not 
+     * explicitly set in any metadata (eg Content-Type).
+     * @return Map of mime type to charset
+     */
+    public static Map<String,String> getAssumedEncodings()
+    {
+        return __inferredEncodings;
+    }
+
+    @Deprecated
+    public static String inferCharsetFromContentType(String contentType)
+    {
+        return getCharsetAssumedFromContentType(contentType);
+    }
+    
+    public static String getCharsetInferredFromContentType(String contentType)
+    {
+        return __inferredEncodings.get(contentType);
+    }
+
+    public static String getCharsetAssumedFromContentType(String contentType)
+    {
+        return __assumedEncodings.get(contentType);
     }
     
     public static String getContentTypeWithoutCharset(String value)
@@ -413,7 +557,7 @@
                 {
                     quote=true;
                 }
-                
+
                 switch(state)
                 {
                     case 11:
@@ -427,11 +571,11 @@
                         break;
                     default:
                         start=i;
-                        state=0;           
+                        state=0;
                 }
                 continue;
             }
-            
+
             if (quote)
             {
                 if (builder!=null && state!=10)
@@ -482,4 +626,5 @@
         return builder.toString();
 
     }
+
 }
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java b/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java
index 59a2c4a..951e9a3 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java
@@ -26,45 +26,55 @@
 import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
+import java.util.function.Predicate;
 
 import org.eclipse.jetty.util.ArrayTernaryTrie;
-import org.eclipse.jetty.util.IncludeExclude;
-import org.eclipse.jetty.util.Predicate;
 import org.eclipse.jetty.util.Trie;
 import org.eclipse.jetty.util.URIUtil;
 
-/* ------------------------------------------------------------ */
-/** URI path map to Object.
+/** 
+ * URI path map to Object.
+ * <p>
  * This mapping implements the path specification recommended
  * in the 2.2 Servlet API.
+ * </p>
  *
- * Path specifications can be of the following forms:<PRE>
+ * <p>
+ * Path specifications can be of the following forms:
+ * </p>
+ * <pre>
  * /foo/bar           - an exact path specification.
  * /foo/*             - a prefix path specification (must end '/*').
  * *.ext              - a suffix path specification.
  * /                  - the default path specification.
  * ""                 - the / path specification
- * </PRE>
- * Matching is performed in the following order <NL>
- * <LI>Exact match.
- * <LI>Longest prefix match.
- * <LI>Longest suffix match.
- * <LI>default.
- * </NL>
+ * </pre>
+ * 
+ * Matching is performed in the following order 
+ * <ol>
+ * <li>Exact match.</li>
+ * <li>Longest prefix match.</li>
+ * <li>Longest suffix match.</li>
+ * <li>default.</li>
+ * </ol>
+ * 
+ * <p>
  * 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>
+ * <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>
+ * <p>
  * This class is not synchronized.  If concurrent modifications are
  * possible then it should be synchronized at a higher level.
- *
- *
+ * 
+ * @param <O> the Map.Entry value type
+ * @deprecated replaced with {@link org.eclipse.jetty.http.pathmap.PathMappings} (this class will be removed in Jetty 10) 
  */
+@Deprecated
 public class PathMap<O> extends HashMap<String,O>
 {
     /* ------------------------------------------------------------ */
@@ -118,11 +128,13 @@
     }
 
     /* --------------------------------------------------------------- */
-    /** Construct from dictionary PathMap.
+    /** 
+     * Construct from dictionary PathMap.
+     * @param dictMap the map representing the dictionary to build this PathMap from
      */
-    public PathMap(Map<String, ? extends O> m)
+    public PathMap(Map<String, ? extends O> dictMap)
     {
-        putAll(m);
+        putAll(dictMap);
     }
 
     /* --------------------------------------------------------------- */
@@ -386,20 +398,23 @@
 
     /* --------------------------------------------------------------- */
     /**
+     * @param pathSpec the path spec
+     * @param path the path
      * @return true if match.
      */
     public static boolean match(String pathSpec, String path)
-        throws IllegalArgumentException
     {
         return match(pathSpec, path, false);
     }
 
     /* --------------------------------------------------------------- */
     /**
+     * @param pathSpec the path spec
+     * @param path the path
+     * @param noDefault true to not handle the default path "/" special, false to allow matcher rules to run  
      * @return true if match.
      */
     public static boolean match(String pathSpec, String path, boolean noDefault)
-        throws IllegalArgumentException
     {
         if (pathSpec.length()==0)
             return "/".equals(path);
@@ -435,6 +450,8 @@
 
     /* --------------------------------------------------------------- */
     /** Return the portion of a path that matches a path spec.
+     * @param pathSpec the path spec
+     * @param path the path
      * @return null if no match at all.
      */
     public static String pathMatch(String pathSpec, String path)
@@ -463,6 +480,8 @@
 
     /* --------------------------------------------------------------- */
     /** Return the portion of a path that is after a path spec.
+     * @param pathSpec the path spec
+     * @param path the path
      * @return The path info string
      */
     public static String pathInfo(String pathSpec, String path)
@@ -618,9 +637,5 @@
         { 
             return _map.containsMatch(s); 
         }
-        public boolean matches(String item)
-        {
-            return _map.containsMatch(item);
-        }
     }
 }
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/PreEncodedHttpField.java b/jetty-http/src/main/java/org/eclipse/jetty/http/PreEncodedHttpField.java
new file mode 100644
index 0000000..fc781da
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/PreEncodedHttpField.java
@@ -0,0 +1,121 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ServiceLoader;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** Pre encoded HttpField.
+ * <p>A HttpField that will be cached and used many times can be created as 
+ * a {@link PreEncodedHttpField}, which will use the {@link HttpFieldPreEncoder}
+ * instances discovered by the {@link ServiceLoader} to pre-encode the header 
+ * for each version of HTTP in use.  This will save garbage 
+ * and CPU each time the field is encoded into a response.
+ * </p>
+ */
+public class PreEncodedHttpField extends HttpField
+{
+    private final static Logger LOG = Log.getLogger(PreEncodedHttpField.class);
+    private final static HttpFieldPreEncoder[] __encoders;
+    
+    static
+    { 
+        List<HttpFieldPreEncoder> encoders = new ArrayList<>();
+        Iterator<HttpFieldPreEncoder> iter = ServiceLoader.load(HttpFieldPreEncoder.class,PreEncodedHttpField.class.getClassLoader()).iterator();
+        while (iter.hasNext())
+        {
+            try
+            {
+                HttpFieldPreEncoder encoder = iter.next();
+                if (index(encoder.getHttpVersion())>=0)
+                    encoders.add(encoder);
+            }
+            catch(Error|RuntimeException e)
+            {
+                LOG.debug(e);
+            }
+        }
+        LOG.debug("HttpField encoders loaded: {}",encoders);
+        int size=encoders.size();
+        
+        __encoders = new HttpFieldPreEncoder[size==0?1:size];
+        for (HttpFieldPreEncoder e:encoders)
+        {
+            int i = index(e.getHttpVersion());
+            if (__encoders[i]==null)
+                __encoders[i] = e;
+            else
+                LOG.warn("multiple PreEncoders for "+e.getHttpVersion());
+        }
+        
+        // Always support HTTP1
+        if (__encoders[0]==null)
+            __encoders[0] = new Http1FieldPreEncoder();              
+    }
+    
+    private static int index(HttpVersion version)
+    {
+        switch (version)
+        {
+            case HTTP_1_0:
+            case HTTP_1_1:
+                return 0;
+
+            case HTTP_2:
+                return 1;
+
+            default:
+                return -1;
+        }
+    }
+    
+    private final byte[][] _encodedField=new byte[__encoders.length][];
+
+    public PreEncodedHttpField(HttpHeader header,String name,String value)
+    {
+        super(header,name, value);
+        for (int i=0;i<__encoders.length;i++)
+            _encodedField[i]=__encoders[i].getEncodedField(header,header.asString(),value);
+    }
+    
+    public PreEncodedHttpField(HttpHeader header,String value)
+    {
+        this(header,header.asString(),value);
+    }
+    
+    public PreEncodedHttpField(String name,String value)
+    {
+        this(null,name,value);
+    }
+    
+    public void putTo(ByteBuffer bufferInFillMode, HttpVersion version)
+    {
+        bufferInFillMode.put(_encodedField[index(version)]);
+    }
+}
\ No newline at end of file
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java
new file mode 100644
index 0000000..c4961ad
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java
@@ -0,0 +1,317 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jetty.util.QuotedStringTokenizer;
+
+/* ------------------------------------------------------------ */
+/**
+ * Implements a quoted comma separated list of values
+ * in accordance with RFC7230.
+ * OWS is removed and quoted characters ignored for parsing.
+ * @see "https://tools.ietf.org/html/rfc7230#section-3.2.6"
+ * @see "https://tools.ietf.org/html/rfc7230#section-7"
+ */
+public class QuotedCSV implements Iterable<String>
+{    
+    private enum State { VALUE, PARAM_NAME, PARAM_VALUE};
+    
+    protected final List<String> _values = new ArrayList<>();
+    protected final boolean _keepQuotes;
+    
+    /* ------------------------------------------------------------ */
+    public QuotedCSV(String... values)
+    {
+        this(true,values);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public QuotedCSV(boolean keepQuotes,String... values)
+    {
+        _keepQuotes=keepQuotes;
+        for (String v:values)
+            addValue(v);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Add and parse a value string(s) 
+     * @param value A value that may contain one or more Quoted CSV items.
+     */
+    public void addValue(String value)
+    {
+        StringBuffer buffer = new StringBuffer();
+        
+        int l=value.length();
+        State state=State.VALUE;
+        boolean quoted=false;
+        boolean sloshed=false;
+        int nws_length=0;
+        int last_length=0;
+        int value_length=-1;
+        int param_name=-1;
+        int param_value=-1;
+        
+        for (int i=0;i<=l;i++)
+        {
+            char c=i==l?0:value.charAt(i);
+            
+            // Handle quoting https://tools.ietf.org/html/rfc7230#section-3.2.6
+            if (quoted && c!=0)
+            {
+                if (sloshed)
+                    sloshed=false;
+                else
+                {
+                    switch(c)
+                    {
+                        case '\\':
+                            sloshed=true;
+                            if (!_keepQuotes)
+                                continue;
+                            break;
+                        case '"':
+                            quoted=false;
+                            if (!_keepQuotes)
+                                continue;
+                            break;
+                    }
+                }
+
+                buffer.append(c);
+                nws_length=buffer.length();
+                continue;
+            }
+            
+            // Handle common cases
+            switch(c)
+            {
+                case ' ':
+                case '\t':
+                    if (buffer.length()>last_length) // not leading OWS
+                        buffer.append(c);
+                    continue;
+
+                case '"':
+                    quoted=true;
+                    if (_keepQuotes)
+                    {
+                        if (state==State.PARAM_VALUE && param_value<0)
+                            param_value=nws_length;
+                        buffer.append(c);
+                    }
+                    else if (state==State.PARAM_VALUE && param_value<0)
+                        param_value=nws_length;
+                    nws_length=buffer.length();
+                    continue;
+                    
+                case ';':                    
+                    buffer.setLength(nws_length); // trim following OWS
+                    if (state==State.VALUE)
+                    {
+                        parsedValue(buffer);
+                        value_length=buffer.length();
+                    }
+                    else
+                        parsedParam(buffer,value_length,param_name,param_value);
+                    nws_length=buffer.length();
+                    param_name=param_value=-1;
+                    buffer.append(c);
+                    last_length=++nws_length;
+                    state=State.PARAM_NAME;
+                    continue;
+                    
+                case ',':
+                case 0:
+                    if (nws_length>0)
+                    {
+                        buffer.setLength(nws_length); // trim following OWS
+                        switch(state)
+                        {
+                            case VALUE:
+                                parsedValue(buffer);
+                                value_length=buffer.length();
+                                break;
+                            case PARAM_NAME:
+                            case PARAM_VALUE:
+                                parsedParam(buffer,value_length,param_name,param_value);
+                                break;
+                        }
+                        _values.add(buffer.toString());
+                    }
+                    buffer.setLength(0);
+                    last_length=0;
+                    nws_length=0;
+                    value_length=param_name=param_value=-1;
+                    state=State.VALUE;
+                    continue;
+
+                case '=':
+                    switch (state)
+                    {
+                        case VALUE:
+                            // It wasn't really a value, it was a param name
+                            value_length=param_name=0;
+                            buffer.setLength(nws_length); // trim following OWS
+                            buffer.append(c);
+                            last_length=++nws_length;
+                            state=State.PARAM_VALUE;
+                            continue;
+
+                        case PARAM_NAME:
+                            buffer.setLength(nws_length); // trim following OWS
+                            buffer.append(c);
+                            last_length=++nws_length;
+                            state=State.PARAM_VALUE;
+                            continue;
+
+                        case PARAM_VALUE:
+                            if (param_value<0)
+                                param_value=nws_length;
+                            buffer.append(c);
+                            nws_length=buffer.length();
+                            continue;
+                    }
+                    continue;
+                    
+                default:
+                {
+                    switch (state)
+                    {
+                        case VALUE:
+                        {
+                            buffer.append(c);
+                            nws_length=buffer.length();
+                            continue;
+                        }
+
+                        case PARAM_NAME:
+                        {
+                            if (param_name<0)
+                                param_name=nws_length;
+                            buffer.append(c);
+                            nws_length=buffer.length();
+                            continue;
+                        }
+
+                        case PARAM_VALUE:
+                        {
+                            if (param_value<0)
+                                param_value=nws_length;
+                            buffer.append(c);
+                            nws_length=buffer.length();
+                            continue;
+                        }
+                    }
+                }
+            }  
+        }
+    }
+
+    /**
+     * Called when a value has been parsed
+     * @param buffer Containing the trimmed value, which may be mutated
+     */
+    protected void parsedValue(StringBuffer buffer)
+    {
+    }
+
+    /**
+     * Called when a parameter has been parsed
+     * @param buffer Containing the trimmed value and all parameters, which may be mutated
+     * @param valueLength The length of the value
+     * @param paramName The index of the start of the parameter just parsed
+     * @param paramValue The index of the start of the parameter value just parsed, or -1
+     */
+    protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue)
+    {
+    }
+
+    public int size()
+    {
+        return _values.size();
+    }
+
+    public boolean isEmpty()
+    {
+        return _values.isEmpty();
+    }
+
+    public List<String> getValues()
+    {
+        return _values;
+    }
+    
+    @Override
+    public Iterator<String> iterator()
+    {
+        return _values.iterator();
+    }
+    
+    public static String unquote(String s)
+    {
+        // handle trivial cases
+        int l=s.length();
+        if (s==null || l==0)
+            return s;
+        
+        // Look for any quotes
+        int i=0;
+        for (;i<l;i++)
+        {
+            char c=s.charAt(i);
+            if (c=='"')
+                break;
+        }
+        if (i==l)
+            return s;
+
+        boolean quoted=true;
+        boolean sloshed=false;
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(s,0,i);
+        i++;
+        for (;i<l;i++)
+        {
+            char c=s.charAt(i);
+            if (quoted)
+            {
+                if (sloshed)
+                {
+                    buffer.append(c);
+                    sloshed=false;
+                }
+                else if (c=='"')
+                    quoted=false;
+                else if (c=='\\')
+                    sloshed=true;
+                else
+                    buffer.append(c);
+            }
+            else if (c=='"')
+                quoted=true;
+            else
+                buffer.append(c);
+        }
+        return buffer.toString();
+    }
+}
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java
new file mode 100644
index 0000000..7aeb7f5
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java
@@ -0,0 +1,146 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/* ------------------------------------------------------------ */
+/**
+ * Implements a quoted comma separated list of quality values
+ * in accordance with RFC7230 and RFC7231.
+ * Values are returned sorted in quality order, with OWS and the 
+ * quality parameters removed.
+ * @see "https://tools.ietf.org/html/rfc7230#section-3.2.6"
+ * @see "https://tools.ietf.org/html/rfc7230#section-7"
+ * @see "https://tools.ietf.org/html/rfc7231#section-5.3.1"
+ */
+public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
+{    
+    private final static Double ZERO=new Double(0.0);
+    private final static Double ONE=new Double(1.0);
+    
+    private final List<Double> _quality = new ArrayList<>();
+    private boolean _sorted = false;
+    
+    /* ------------------------------------------------------------ */
+    public QuotedQualityCSV(String... values)
+    {
+        for (String v:values)
+            addValue(v);
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    public void addValue(String value)
+    {
+        super.addValue(value);
+        while(_quality.size()<_values.size())
+            _quality.add(ONE);
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void parsedValue(StringBuffer buffer)
+    {
+        super.parsedValue(buffer);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue)
+    {
+        if (paramName<0)
+        {
+            if (buffer.charAt(buffer.length()-1)==';')
+                buffer.setLength(buffer.length()-1);
+        }
+        if (paramValue>=0 && 
+            buffer.charAt(paramName)=='q' && paramValue>paramName && 
+            buffer.length()>=paramName && buffer.charAt(paramName+1)=='=')
+        {
+            Double q;
+            try
+            {
+                q=(_keepQuotes && buffer.charAt(paramValue)=='"')
+                    ?new Double(buffer.substring(paramValue+1,buffer.length()-1))
+                    :new Double(buffer.substring(paramValue));
+            }
+            catch(Exception e)
+            {
+                q=ZERO;
+            }
+            buffer.setLength(paramName-1);
+            
+            while(_quality.size()<_values.size())
+                _quality.add(ONE);
+            _quality.add(q);
+        }
+    }
+
+    public List<String> getValues()
+    {
+        if (!_sorted)
+            sort();
+        return _values;
+    }
+    
+    @Override
+    public Iterator<String> iterator()
+    {
+        if (!_sorted)
+            sort();
+        return _values.iterator();
+    }
+
+    protected void sort()
+    {
+        _sorted=true;
+
+        Double last = ZERO;
+
+        for (int i = _values.size(); i-- > 0;)
+        {
+            String v = _values.get(i);
+            Double q = _quality.get(i);
+
+            int compare=last.compareTo(q);
+            if (compare > 0)
+            {
+                _values.set(i, _values.get(i + 1));
+                _values.set(i + 1, v);
+                _quality.set(i, _quality.get(i + 1));
+                _quality.set(i + 1, q);
+                last = ZERO;
+                i = _values.size();
+                continue;
+            }
+
+            last=q;
+        }
+        
+        int last_element=_quality.size();
+        while(last_element>0 && _quality.get(--last_element).equals(ZERO))
+        {
+            _quality.remove(last_element);
+            _values.remove(last_element);
+        }
+    }
+}
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/ResourceHttpContent.java b/jetty-http/src/main/java/org/eclipse/jetty/http/ResourceHttpContent.java
new file mode 100644
index 0000000..9685a7b
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/ResourceHttpContent.java
@@ -0,0 +1,227 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+
+import org.eclipse.jetty.http.MimeTypes.Type;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.resource.Resource;
+
+
+/* ------------------------------------------------------------ */
+/** HttpContent created from a {@link Resource}.
+ * <p>The HttpContent is used to server static content that is not
+ * cached. So fields and values are only generated as need be an not 
+ * kept for reuse</p>
+ */
+public class ResourceHttpContent implements HttpContent
+{
+    final Resource _resource;
+    final String _contentType;
+    final int _maxBuffer;
+    HttpContent _gzip;
+    String _etag;
+
+    /* ------------------------------------------------------------ */
+    public ResourceHttpContent(final Resource resource, final String contentType)
+    {
+        this(resource,contentType,-1,null);
+    }
+
+    /* ------------------------------------------------------------ */
+    public ResourceHttpContent(final Resource resource, final String contentType, int maxBuffer)
+    {
+        this(resource,contentType,maxBuffer,null);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public ResourceHttpContent(final Resource resource, final String contentType, int maxBuffer, HttpContent gzip)
+    {
+        _resource=resource;
+        _contentType=contentType;
+        _maxBuffer=maxBuffer;
+        _gzip=gzip;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String getContentTypeValue()
+    {
+        return _contentType;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public HttpField getContentType()
+    {
+        return _contentType==null?null:new HttpField(HttpHeader.CONTENT_TYPE,_contentType);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public HttpField getContentEncoding()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String getContentEncodingValue()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String getCharacterEncoding()
+    {
+        return _contentType==null?null:MimeTypes.getCharsetFromContentType(_contentType);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public Type getMimeType()
+    {
+        return _contentType==null?null:MimeTypes.CACHE.get(MimeTypes.getContentTypeWithoutCharset(_contentType));
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public HttpField getLastModified()
+    {
+        long lm = _resource.lastModified();
+        return lm>=0?new HttpField(HttpHeader.LAST_MODIFIED,DateGenerator.formatDate(lm)):null;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String getLastModifiedValue()
+    {
+        long lm = _resource.lastModified();
+        return lm>=0?DateGenerator.formatDate(lm):null;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public ByteBuffer getDirectBuffer()
+    {
+        if (_resource.length()<=0 || _maxBuffer>0 && _maxBuffer<_resource.length())
+            return null;
+        try
+        {
+            return BufferUtil.toBuffer(_resource,true);
+        }
+        catch(IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public HttpField getETag()
+    {
+        return new HttpField(HttpHeader.ETAG,getETagValue());
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public String getETagValue()
+    {
+        return _resource.getWeakETag();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public ByteBuffer getIndirectBuffer()
+    {
+        if (_resource.length()<=0 || _maxBuffer>0 && _maxBuffer<_resource.length())
+            return null;
+        try
+        {
+            return BufferUtil.toBuffer(_resource,false);
+        }
+        catch(IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public HttpField getContentLength()
+    {
+        long l=_resource.length();
+        return l==-1?null:new HttpField.LongValueHttpField(HttpHeader.CONTENT_LENGTH,_resource.length());
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public long getContentLengthValue()
+    {
+        return _resource.length();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public InputStream getInputStream() throws IOException
+    {
+        return _resource.getInputStream();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public ReadableByteChannel getReadableByteChannel() throws IOException
+    {
+        return _resource.getReadableByteChannel();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public Resource getResource()
+    {
+        return _resource;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void release()
+    {
+        _resource.close();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%x{r=%s,gz=%b}",this.getClass().getSimpleName(),hashCode(),_resource,_gzip!=null);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public HttpContent getGzipContent()
+    {
+        return _gzip==null?null:new GzipHttpContent(this,_gzip);
+    }
+
+}
\ No newline at end of file
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathMappings.java b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathMappings.java
index 8000a77..b8196a4 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathMappings.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathMappings.java
@@ -23,6 +23,7 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.function.Predicate;
 
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
@@ -69,6 +70,11 @@
         mappings.clear();
     }
     
+    public void removeIf(Predicate<MappedResource<E>> predicate)
+    {
+        mappings.removeIf(predicate);
+    }
+    
     /**
      * Return a list of MappedResource matches for the specified path.
      * 
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecGroup.java b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecGroup.java
index 97bc717..5fe0763 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecGroup.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecGroup.java
@@ -51,7 +51,6 @@
     MIDDLE_GLOB,
     /**
      * For path specs that have a hardcoded prefix and a trailing wildcard glob.
-     * <p>
      * 
      * <pre>
      *   "/downloads/*"          - servlet spec
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecSet.java b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecSet.java
index 91795c2..4499c6e 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecSet.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecSet.java
@@ -24,8 +24,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
-
-import org.eclipse.jetty.util.Predicate;
+import java.util.function.Predicate;
 
 /**
  * A Set of PathSpec strings.
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java
index 16b21e2..f04afa9 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java
@@ -18,10 +18,26 @@
 
 package org.eclipse.jetty.http.pathmap;
 
+import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.URIUtil;
 
 public class ServletPathSpec extends PathSpec
 {
+    /**
+     * If a servlet or filter path mapping isn't a suffix mapping, ensure
+     * it starts with '/'
+     * 
+     * @param pathSpec the servlet or filter mapping pattern
+     * @return the pathSpec prefixed by '/' if appropriate
+     */
+    public static String normalize(String pathSpec)
+    {
+        if (StringUtil.isNotBlank(pathSpec) && !pathSpec.startsWith("/") && !pathSpec.startsWith("*")) 
+            return "/" + pathSpec;
+        return pathSpec;
+    }
+    
+    
     public ServletPathSpec(String servletPathSpec)
     {
         if (servletPathSpec == null)
diff --git a/jetty-http/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder b/jetty-http/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder
new file mode 100644
index 0000000..92640bd
--- /dev/null
+++ b/jetty-http/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder
@@ -0,0 +1 @@
+org.eclipse.jetty.http.Http1FieldPreEncoder
\ No newline at end of file
diff --git a/jetty-http/src/main/resources/org/eclipse/jetty/http/encoding.properties b/jetty-http/src/main/resources/org/eclipse/jetty/http/encoding.properties
index 311c802..e8b1d7b 100644
--- a/jetty-http/src/main/resources/org/eclipse/jetty/http/encoding.properties
+++ b/jetty-http/src/main/resources/org/eclipse/jetty/http/encoding.properties
@@ -1,4 +1,11 @@
-text/html	= ISO-8859-1
-text/plain	= ISO-8859-1
-text/xml	= UTF-8
-text/json   = UTF-8
+# Mapping of mime type to inferred or assumed charset
+# inferred charsets are used for encoding/decoding and explicitly set in associated metadata
+# assumed charsets are used for encoding/decoding, but are not set in associated metadata
+# In this file, assumed charsets are indicatd with a leading '-'
+
+text/html=utf-8
+text/plain=iso-8859-1
+text/xml=utf-8
+application/xhtml+xml=utf-8
+text/json=-utf-8
+application/vnd.api+json=-utf-8
\ No newline at end of file
diff --git a/jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties b/jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties
index 302cbd0..0112b88 100644
--- a/jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties
+++ b/jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties
@@ -10,6 +10,7 @@
 avi=video/x-msvideo
 bcpio=application/x-bcpio
 bin=application/octet-stream
+bmp=image/bmp
 cab=application/x-cabinet
 cdf=application/x-netcdf
 chm=application/vnd.ms-htmlhelp
@@ -19,7 +20,7 @@
 crt=application/x-x509-ca-cert
 csh=application/x-csh
 css=text/css
-csv=text/comma-separated-values
+csv=text/csv
 dcr=application/x-director
 dir=application/x-director
 dll=application/x-msdownload
@@ -51,6 +52,7 @@
 java=text/plain
 jnlp=application/x-java-jnlp-file
 jpe=image/jpeg
+jp2=image/jpeg2000
 jpeg=image/jpeg
 jpg=image/jpeg
 js=application/javascript
@@ -113,6 +115,7 @@
 qml=text/x-qml
 qt=video/quicktime
 ra=audio/x-pn-realaudio
+rar=application/x-rar-compressed
 ram=audio/x-pn-realaudio
 ras=image/x-cmu-raster
 rdf=application/rdf+xml
@@ -170,6 +173,7 @@
 wrl=model/vrml
 wtls-ca-certificate=application/vnd.wap.wtls-ca-certificate
 xbm=image/x-xbitmap
+xcf=image/xcf
 xht=application/xhtml+xml
 xhtml=application/xhtml+xml
 xls=application/vnd.ms-excel
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldTest.java
new file mode 100644
index 0000000..4cc6403
--- /dev/null
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldTest.java
@@ -0,0 +1,184 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.util.BufferUtil;
+import org.junit.Test;
+
+public class HttpFieldTest
+{
+
+    @Test
+    public void testContainsSimple() throws Exception
+    {
+        HttpField field = new HttpField("name","SomeValue");
+        assertTrue(field.contains("somevalue"));
+        assertTrue(field.contains("sOmEvAlUe"));
+        assertTrue(field.contains("SomeValue"));
+        assertFalse(field.contains("other"));
+        assertFalse(field.contains("some"));
+        assertFalse(field.contains("Some"));
+        assertFalse(field.contains("value"));
+        assertFalse(field.contains("v"));
+        assertFalse(field.contains(""));
+        assertFalse(field.contains(null));
+    }
+    
+    @Test
+    public void testCaseInsensitiveHashcode_KnownField() throws Exception
+    {
+        HttpField fieldFoo1 = new HttpField("Cookie","foo");
+        HttpField fieldFoo2 = new HttpField("cookie","foo");
+        
+        assertThat("Field hashcodes are case insensitive", fieldFoo1.hashCode(), is(fieldFoo2.hashCode()));
+    }
+    
+    @Test
+    public void testCaseInsensitiveHashcode_UnknownField() throws Exception
+    {
+        HttpField fieldFoo1 = new HttpField("X-Foo","bar");
+        HttpField fieldFoo2 = new HttpField("x-foo","bar");
+        
+        assertThat("Field hashcodes are case insensitive", fieldFoo1.hashCode(), is(fieldFoo2.hashCode()));
+    }
+
+    @Test
+    public void testContainsList() throws Exception
+    {
+        HttpField field = new HttpField("name",",aaa,Bbb,CCC, ddd , e e, \"\\\"f,f\\\"\", ");
+        assertTrue(field.contains("aaa"));
+        assertTrue(field.contains("bbb"));
+        assertTrue(field.contains("ccc"));
+        assertTrue(field.contains("Aaa"));
+        assertTrue(field.contains("Bbb"));
+        assertTrue(field.contains("Ccc"));
+        assertTrue(field.contains("AAA"));
+        assertTrue(field.contains("BBB"));
+        assertTrue(field.contains("CCC"));
+        assertTrue(field.contains("ddd"));
+        assertTrue(field.contains("e e"));
+        assertTrue(field.contains("\"f,f\""));
+        assertFalse(field.contains(""));
+        assertFalse(field.contains("aa"));
+        assertFalse(field.contains("bb"));
+        assertFalse(field.contains("cc"));
+        assertFalse(field.contains(null));
+    }
+    
+
+    @Test
+    public void testQualityContainsList() throws Exception
+    {
+        HttpField field;
+        
+        field = new HttpField("name","yes");
+        assertTrue(field.contains("yes"));
+        assertFalse(field.contains("no"));
+
+        field = new HttpField("name",",yes,");
+        assertTrue(field.contains("yes"));
+        assertFalse(field.contains("no"));
+        
+        field = new HttpField("name","other,yes,other");
+        assertTrue(field.contains("yes"));
+        assertFalse(field.contains("no"));
+        
+        field = new HttpField("name","other,  yes  ,other");
+        assertTrue(field.contains("yes"));
+        assertFalse(field.contains("no"));
+        
+        field = new HttpField("name","other,  y s  ,other");
+        assertTrue(field.contains("y s"));
+        assertFalse(field.contains("no"));
+
+        field = new HttpField("name","other,  \"yes\"  ,other");
+        assertTrue(field.contains("yes"));
+        assertFalse(field.contains("no"));
+        
+        field = new HttpField("name","other,  \"\\\"yes\\\"\"  ,other");
+        assertTrue(field.contains("\"yes\""));
+        assertFalse(field.contains("no"));
+        
+        field = new HttpField("name",";no,yes,;no");
+        assertTrue(field.contains("yes"));
+        assertFalse(field.contains("no"));
+
+        field = new HttpField("name","no;q=0,yes;q=1,no; q = 0");
+        assertTrue(field.contains("yes"));
+        assertFalse(field.contains("no"));
+        
+        field = new HttpField("name","no;q=0.0000,yes;q=0.0001,no; q = 0.00000");
+        assertTrue(field.contains("yes"));
+        assertFalse(field.contains("no"));
+        
+        field = new HttpField("name","no;q=0.0000,Yes;Q=0.0001,no; Q = 0.00000");
+        assertTrue(field.contains("yes"));
+        assertFalse(field.contains("no"));
+       
+    }
+    
+    @Test
+    public void testValues()
+    {
+        String[] values = new HttpField("name","value").getValues();
+        assertEquals(1,values.length);
+        assertEquals("value",values[0]);
+        
+
+        values = new HttpField("name","a,b,c").getValues();
+        assertEquals(3,values.length);
+        assertEquals("a",values[0]);
+        assertEquals("b",values[1]);
+        assertEquals("c",values[2]);
+
+        values = new HttpField("name","a,\"x,y,z\",c").getValues();
+        assertEquals(3,values.length);
+        assertEquals("a",values[0]);
+        assertEquals("x,y,z",values[1]);
+        assertEquals("c",values[2]);
+        
+        values = new HttpField("name","a,\"x,\\\"p,q\\\",z\",c").getValues();
+        assertEquals(3,values.length);
+        assertEquals("a",values[0]);
+        assertEquals("x,\"p,q\",z",values[1]);
+        assertEquals("c",values[2]);
+        
+    }
+    
+    @Test
+    public void testCachedField()
+    {
+        PreEncodedHttpField field = new PreEncodedHttpField(HttpHeader.ACCEPT,"something");
+        ByteBuffer buf = BufferUtil.allocate(256);
+        BufferUtil.clearToFill(buf);
+        field.putTo(buf,HttpVersion.HTTP_1_0);
+        BufferUtil.flipToFlush(buf,0);
+        String s=BufferUtil.toString(buf);
+        
+        assertEquals("Accept: something\r\n",s);
+    }
+}
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java
index 3db4309..0e0c5ab 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java
@@ -19,8 +19,11 @@
 package org.eclipse.jetty.http;
 
 import java.nio.ByteBuffer;
+import java.util.Collections;
 import java.util.Enumeration;
+import java.util.List;
 import java.util.Locale;
+import java.util.NoSuchElementException;
 
 import org.eclipse.jetty.util.BufferUtil;
 import org.hamcrest.Matchers;
@@ -34,9 +37,6 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
-/**
- *
- */
 public class HttpFieldsTest
 {
     @Test
@@ -47,9 +47,10 @@
         header.put("name0", "value:0");
         header.put("name1", "value1");
 
-        assertEquals("value:0",header.getStringField("name0"));
-        assertEquals("value1",header.getStringField("name1"));
-        assertNull(header.getStringField("name2"));
+        assertEquals(2,header.size());
+        assertEquals("value:0",header.get("name0"));
+        assertEquals("value1",header.get("name1"));
+        assertNull(header.get("name2"));
 
         int matches=0;
         Enumeration<String> e = header.getFieldNames();
@@ -98,10 +99,45 @@
         header.put("name0", "value0");
         header.put("name1", "value1");
 
-        assertEquals("value0",header.getStringField("name0"));
-        assertEquals("value0",header.getStringField("Name0"));
-        assertEquals("value1",header.getStringField("name1"));
-        assertEquals("value1",header.getStringField("Name1"));
+        assertEquals("value0",header.get("name0"));
+        assertEquals("value0",header.get("Name0"));
+        assertEquals("value1",header.get("name1"));
+        assertEquals("value1",header.get("Name1"));
+        assertEquals(null,header.get("Name2"));
+
+        assertEquals("value0",header.getField("name0").getValue());
+        assertEquals("value0",header.getField("Name0").getValue());
+        assertEquals("value1",header.getField("name1").getValue());
+        assertEquals("value1",header.getField("Name1").getValue());
+        assertEquals(null,header.getField("Name2"));
+
+        assertEquals("value0",header.getField(0).getValue());
+        assertEquals("value1",header.getField(1).getValue());
+        try
+        {
+            header.getField(2);
+            Assert.fail();
+        }
+        catch(NoSuchElementException e)
+        {}
+    }
+
+    @Test
+    public void testGetKnown() throws Exception
+    {
+        HttpFields header = new HttpFields();
+
+        header.put("Connection", "value0");
+        header.put(HttpHeader.ACCEPT, "value1");
+
+        assertEquals("value0",header.get(HttpHeader.CONNECTION));
+        assertEquals("value1",header.get(HttpHeader.ACCEPT));
+
+        assertEquals("value0",header.getField(HttpHeader.CONNECTION).getValue());
+        assertEquals("value1",header.getField(HttpHeader.ACCEPT).getValue());
+
+        assertEquals(null,header.getField(HttpHeader.AGE));
+        assertEquals(null,header.get(HttpHeader.AGE));
     }
 
     @Test
@@ -152,16 +188,16 @@
         header.put("name1", "xxxxxx");
         header.put("name2", "value2");
 
-        assertEquals("value0",header.getStringField("name0"));
-        assertEquals("xxxxxx",header.getStringField("name1"));
-        assertEquals("value2",header.getStringField("name2"));
+        assertEquals("value0",header.get("name0"));
+        assertEquals("xxxxxx",header.get("name1"));
+        assertEquals("value2",header.get("name2"));
 
         header.put("name1", "value1");
 
-        assertEquals("value0",header.getStringField("name0"));
-        assertEquals("value1",header.getStringField("name1"));
-        assertEquals("value2",header.getStringField("name2"));
-        assertNull(header.getStringField("name3"));
+        assertEquals("value0",header.get("name0"));
+        assertEquals("value1",header.get("name1"));
+        assertEquals("value2",header.get("name2"));
+        assertNull(header.get("name3"));
 
         int matches=0;
         Enumeration<String> e = header.getFieldNames();
@@ -187,22 +223,22 @@
     @Test
     public void testRemovePut() throws Exception
     {
-        HttpFields header = new HttpFields();
+        HttpFields header = new HttpFields(1);
 
         header.put("name0", "value0");
         header.put("name1", "value1");
         header.put("name2", "value2");
 
-        assertEquals("value0",header.getStringField("name0"));
-        assertEquals("value1",header.getStringField("name1"));
-        assertEquals("value2",header.getStringField("name2"));
+        assertEquals("value0",header.get("name0"));
+        assertEquals("value1",header.get("name1"));
+        assertEquals("value2",header.get("name2"));
 
         header.remove("name1");
 
-        assertEquals("value0",header.getStringField("name0"));
-        assertNull(header.getStringField("name1"));
-        assertEquals("value2",header.getStringField("name2"));
-        assertNull(header.getStringField("name3"));
+        assertEquals("value0",header.get("name0"));
+        assertNull(header.get("name1"));
+        assertEquals("value2",header.get("name2"));
+        assertNull(header.get("name3"));
 
         int matches=0;
         Enumeration<String> e = header.getFieldNames();
@@ -231,16 +267,16 @@
         fields.add("name1", "valueA");
         fields.add("name2", "value2");
 
-        assertEquals("value0",fields.getStringField("name0"));
-        assertEquals("valueA",fields.getStringField("name1"));
-        assertEquals("value2",fields.getStringField("name2"));
+        assertEquals("value0",fields.get("name0"));
+        assertEquals("valueA",fields.get("name1"));
+        assertEquals("value2",fields.get("name2"));
 
         fields.add("name1", "valueB");
 
-        assertEquals("value0",fields.getStringField("name0"));
-        assertEquals("valueA",fields.getStringField("name1"));
-        assertEquals("value2",fields.getStringField("name2"));
-        assertNull(fields.getStringField("name3"));
+        assertEquals("value0",fields.get("name0"));
+        assertEquals("valueA",fields.get("name1"));
+        assertEquals("value2",fields.get("name2"));
+        assertNull(fields.get("name3"));
 
         int matches=0;
         Enumeration<String> e = fields.getFieldNames();
@@ -264,7 +300,6 @@
         assertEquals(false, e.hasMoreElements());
     }
 
-
     @Test
     public void testGetValues() throws Exception
     {
@@ -303,10 +338,138 @@
         assertEquals(true, e.hasMoreElements());
         assertEquals(e.nextElement(), "value1D");
         assertEquals(false, e.hasMoreElements());
-
     }
 
     @Test
+    public void testGetCSV() throws Exception
+    {
+        HttpFields fields = new HttpFields();
+
+        fields.put("name0", "value0A,value0B");
+        fields.add("name0", "value0C,value0D");
+        fields.put("name1", "value1A, \"value\t, 1B\" ");
+        fields.add("name1", "\"value1C\",\tvalue1D");
+
+        Enumeration<String> e = fields.getValues("name0");
+        assertEquals(true, e.hasMoreElements());
+        assertEquals(e.nextElement(), "value0A,value0B");
+        assertEquals(true, e.hasMoreElements());
+        assertEquals(e.nextElement(), "value0C,value0D");
+        assertEquals(false, e.hasMoreElements());
+
+        e = Collections.enumeration(fields.getCSV("name0",false));
+        assertEquals(true, e.hasMoreElements());
+        assertEquals(e.nextElement(), "value0A");
+        assertEquals(true, e.hasMoreElements());
+        assertEquals(e.nextElement(), "value0B");
+        assertEquals(true, e.hasMoreElements());
+        assertEquals(e.nextElement(), "value0C");
+        assertEquals(true, e.hasMoreElements());
+        assertEquals(e.nextElement(), "value0D");
+        assertEquals(false, e.hasMoreElements());
+
+        e = Collections.enumeration(fields.getCSV("name1",false));
+        assertEquals(true, e.hasMoreElements());
+        assertEquals(e.nextElement(), "value1A");
+        assertEquals(true, e.hasMoreElements());
+        assertEquals(e.nextElement(), "value\t, 1B");
+        assertEquals(true, e.hasMoreElements());
+        assertEquals(e.nextElement(), "value1C");
+        assertEquals(true, e.hasMoreElements());
+        assertEquals(e.nextElement(), "value1D");
+        assertEquals(false, e.hasMoreElements());
+    }
+
+    @Test
+    public void testAddQuotedCSV() throws Exception
+    {
+        HttpFields fields = new HttpFields();
+
+        fields.put("some", "value");
+        fields.add("name", "\"zero\"");
+        fields.add("name", "one, \"1 + 1\"");
+        fields.put("other", "value");
+        fields.add("name", "three");
+        fields.add("name", "four, I V");
+
+        List<String> list = fields.getCSV("name",false);
+        assertEquals("zero",HttpFields.valueParameters(list.get(0),null));
+        assertEquals("one",HttpFields.valueParameters(list.get(1),null));
+        assertEquals("1 + 1",HttpFields.valueParameters(list.get(2),null));
+        assertEquals("three",HttpFields.valueParameters(list.get(3),null));
+        assertEquals("four",HttpFields.valueParameters(list.get(4),null));
+        assertEquals("I V",HttpFields.valueParameters(list.get(5),null));
+        
+        fields.addCSV("name","six");
+        list = fields.getCSV("name",false);
+        assertEquals("zero",HttpFields.valueParameters(list.get(0),null));
+        assertEquals("one",HttpFields.valueParameters(list.get(1),null));
+        assertEquals("1 + 1",HttpFields.valueParameters(list.get(2),null));
+        assertEquals("three",HttpFields.valueParameters(list.get(3),null));
+        assertEquals("four",HttpFields.valueParameters(list.get(4),null));
+        assertEquals("I V",HttpFields.valueParameters(list.get(5),null));
+        assertEquals("six",HttpFields.valueParameters(list.get(6),null));
+        
+        fields.addCSV("name","1 + 1","7","zero");
+        list = fields.getCSV("name",false);
+        assertEquals("zero",HttpFields.valueParameters(list.get(0),null));
+        assertEquals("one",HttpFields.valueParameters(list.get(1),null));
+        assertEquals("1 + 1",HttpFields.valueParameters(list.get(2),null));
+        assertEquals("three",HttpFields.valueParameters(list.get(3),null));
+        assertEquals("four",HttpFields.valueParameters(list.get(4),null));
+        assertEquals("I V",HttpFields.valueParameters(list.get(5),null));
+        assertEquals("six",HttpFields.valueParameters(list.get(6),null));
+        assertEquals("7",HttpFields.valueParameters(list.get(7),null));   
+    }
+    
+    @Test
+    public void testGetQualityCSV() throws Exception
+    {
+        HttpFields fields = new HttpFields();
+
+        fields.put("some", "value");
+        fields.add("name", "zero;q=0.9,four;q=0.1");
+        fields.put("other", "value");
+        fields.add("name", "nothing;q=0");
+        fields.add("name", "one;q=0.4");
+        fields.add("name", "three;x=y;q=0.2;a=b,two;q=0.3");
+        fields.add("name", "first;");
+
+        
+        List<String> list = fields.getQualityCSV("name");
+        assertEquals("first",HttpFields.valueParameters(list.get(0),null));
+        assertEquals("zero",HttpFields.valueParameters(list.get(1),null));
+        assertEquals("one",HttpFields.valueParameters(list.get(2),null));
+        assertEquals("two",HttpFields.valueParameters(list.get(3),null));
+        assertEquals("three",HttpFields.valueParameters(list.get(4),null));
+        assertEquals("four",HttpFields.valueParameters(list.get(5),null));
+    }
+    
+    @Test
+    public void testGetQualityCSVHeader() throws Exception
+    {
+        HttpFields fields = new HttpFields();
+
+        fields.put("some", "value");
+        fields.add("Accept", "zero;q=0.9,four;q=0.1");
+        fields.put("other", "value");
+        fields.add("Accept", "nothing;q=0");
+        fields.add("Accept", "one;q=0.4");
+        fields.add("Accept", "three;x=y;q=0.2;a=b,two;q=0.3");
+        fields.add("Accept", "first;");
+
+        
+        List<String> list = fields.getQualityCSV(HttpHeader.ACCEPT);
+        assertEquals("first",HttpFields.valueParameters(list.get(0),null));
+        assertEquals("zero",HttpFields.valueParameters(list.get(1),null));
+        assertEquals("one",HttpFields.valueParameters(list.get(2),null));
+        assertEquals("two",HttpFields.valueParameters(list.get(3),null));
+        assertEquals("three",HttpFields.valueParameters(list.get(4),null));
+        assertEquals("four",HttpFields.valueParameters(list.get(5),null));
+    }
+
+
+    @Test
     public void testDateFields() throws Exception
     {
         HttpFields fields = new HttpFields();
@@ -345,7 +508,7 @@
         assertEquals(951825600000L,d5);
 
         fields.putDateField("D2",d1);
-        assertEquals("Fri, 31 Dec 1999 23:59:59 GMT",fields.getStringField("D2"));
+        assertEquals("Fri, 31 Dec 1999 23:59:59 GMT",fields.get("D2"));
     }
 
     @Test
@@ -354,16 +517,16 @@
         HttpFields fields = new HttpFields();
 
         fields.putDateField("Dzero",0);
-        assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Dzero"));
+        assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.get("Dzero"));
 
         fields.putDateField("Dminus",-1);
-        assertEquals("Wed, 31 Dec 1969 23:59:59 GMT",fields.getStringField("Dminus"));
+        assertEquals("Wed, 31 Dec 1969 23:59:59 GMT",fields.get("Dminus"));
 
         fields.putDateField("Dminus",-1000);
-        assertEquals("Wed, 31 Dec 1969 23:59:59 GMT",fields.getStringField("Dminus"));
+        assertEquals("Wed, 31 Dec 1969 23:59:59 GMT",fields.get("Dminus"));
 
         fields.putDateField("Dancient",Long.MIN_VALUE);
-        assertEquals("Sun, 02 Dec 55 16:47:04 GMT",fields.getStringField("Dancient"));
+        assertEquals("Sun, 02 Dec 55 16:47:04 GMT",fields.get("Dancient"));
     }
 
     @Test
@@ -373,15 +536,33 @@
 
         header.put("I1", "42");
         header.put("I2", " 43 99");
-        header.put("I3", "-44;");
+        header.put("I3", "-44");
         header.put("I4", " - 45abc");
         header.put("N1", " - ");
         header.put("N2", "xx");
 
         long i1=header.getLongField("I1");
-        long i2=header.getLongField("I2");
+        try
+        {
+            header.getLongField("I2");
+            assertTrue(false);
+        }
+        catch(NumberFormatException e)
+        {
+            assertTrue(true);
+        }
+
         long i3=header.getLongField("I3");
-        long i4=header.getLongField("I4");
+
+        try
+        {
+            header.getLongField("I4");
+            assertTrue(false);
+        }
+        catch(NumberFormatException e)
+        {
+            assertTrue(true);
+        }
 
         try{
             header.getLongField("N1");
@@ -402,15 +583,12 @@
         }
 
         assertEquals(42,i1);
-        assertEquals(43,i2);
         assertEquals(-44,i3);
-        assertEquals(-45,i4);
 
         header.putLongField("I5", 46);
         header.putLongField("I6",-47);
-        assertEquals("46",header.getStringField("I5"));
-        assertEquals("-47",header.getStringField("I6"));
-
+        assertEquals("46",header.get("I5"));
+        assertEquals("-47",header.get("I6"));
     }
 
     @Test
@@ -418,20 +596,36 @@
     {
         HttpFields header = new HttpFields();
 
-        header.add("0", "");
-        header.add("1", ",");
-        header.add("2", ",,");
-        header.add("3", "abc");
-        header.add("4", "def");
-        header.add("5", "abc,def,hig");
-        header.add("6", "abc");
-        header.add("6", "def");
-        header.add("6", "hig");
+        header.add("n0", "");
+        header.add("n1", ",");
+        header.add("n2", ",,");
+        header.add("N3", "abc");
+        header.add("N4", "def");
+        header.add("n5", "abc,def,hig");
+        header.add("N6", "abc");
+        header.add("n6", "def");
+        header.add("N6", "hig");
+        header.add("n7", "abc ,  def;q=0.9  ,  hig");
+        header.add("n8", "abc ,  def;q=0  ,  hig");
+        header.add(HttpHeader.ACCEPT, "abc ,  def;q=0  ,  hig");
 
-        for (int i=0;i<7;i++)
+        for (int i=0;i<8;i++)
         {
-            assertFalse(""+i,header.contains(""+i,"xyz"));
-            assertEquals(""+i,i>=4,header.contains(""+i,"def"));
+            assertTrue(header.containsKey("n"+i));
+            assertTrue(header.containsKey("N"+i));
+            assertFalse(""+i,header.contains("n"+i,"xyz"));
+            assertEquals(""+i,i>=4,header.contains("n"+i,"def"));
         }
+
+
+        assertTrue(header.contains(new HttpField("N5","def")));
+        assertTrue(header.contains(new HttpField("accept","abc")));
+        assertTrue(header.contains(HttpHeader.ACCEPT,"abc"));
+        assertFalse(header.contains(new HttpField("N5","xyz")));
+        assertFalse(header.contains(new HttpField("N8","def")));
+        assertFalse(header.contains(HttpHeader.ACCEPT,"def"));
+        assertFalse(header.contains(HttpHeader.AGE,"abc"));
+
+        assertFalse(header.containsKey("n11"));
     }
 }
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java
index ca5ad8a..7dd1886 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java
@@ -29,16 +29,16 @@
 {
     public final static String[] connect={null,"keep-alive","close"};
 
-    class Info extends HttpGenerator.RequestInfo
+    class Info extends MetaData.Request
     {
         Info(String method,String uri)
         {
-            super(HttpVersion.HTTP_1_1,new HttpFields(),-1,method,uri);
+            super(method,new HttpURI(uri),HttpVersion.HTTP_1_1,new HttpFields(),-1);
         }
 
         public Info(String method,String uri, int contentLength)
         {
-            super(HttpVersion.HTTP_1_1,new HttpFields(),contentLength,method,uri);
+            super(method,new HttpURI(uri),HttpVersion.HTTP_1_1,new HttpFields(),contentLength);
         }
     }
 
@@ -54,8 +54,8 @@
         Assert.assertEquals(HttpGenerator.State.START, gen.getState());
 
         Info info = new Info("GET","/index.html");
-        info.getHttpFields().add("Host","something");
-        info.getHttpFields().add("User-Agent","test");
+        info.getFields().add("Host","something");
+        info.getFields().add("User-Agent","test");
         Assert.assertTrue(!gen.isChunking());
 
         result=gen.generateRequest(info,null,null,null, true);
@@ -91,8 +91,8 @@
         Assert.assertEquals(HttpGenerator.State.START, gen.getState());
 
         Info info = new Info("POST","/index.html");
-        info.getHttpFields().add("Host","something");
-        info.getHttpFields().add("User-Agent","test");
+        info.getFields().add("Host","something");
+        info.getFields().add("User-Agent","test");
         Assert.assertTrue(!gen.isChunking());
 
         result=gen.generateRequest(info,null,null,null, true);
@@ -130,8 +130,8 @@
         Assert.assertEquals(HttpGenerator.State.START, gen.getState());
 
         Info info = new Info("POST","/index.html");
-        info.getHttpFields().add("Host","something");
-        info.getHttpFields().add("User-Agent","test");
+        info.getFields().add("Host","something");
+        info.getFields().add("User-Agent","test");
 
         result=gen.generateRequest(info,null,null,content0, true);
         Assert.assertEquals(HttpGenerator.Result.NEED_HEADER, result);
@@ -176,8 +176,8 @@
         Assert.assertEquals(HttpGenerator.State.START, gen.getState());
 
         Info info = new Info("POST","/index.html");
-        info.getHttpFields().add("Host","something");
-        info.getHttpFields().add("User-Agent","test");
+        info.getFields().add("Host","something");
+        info.getFields().add("User-Agent","test");
 
         result=gen.generateRequest(info,null,null,content0, false);
         Assert.assertEquals(HttpGenerator.Result.NEED_HEADER, result);
@@ -248,8 +248,8 @@
         Assert.assertEquals(HttpGenerator.State.START, gen.getState());
 
         Info info = new Info("POST","/index.html",58);
-        info.getHttpFields().add("Host","something");
-        info.getHttpFields().add("User-Agent","test");
+        info.getFields().add("Host","something");
+        info.getFields().add("User-Agent","test");
 
         result=gen.generateRequest(info,null,null,content0, false);
         Assert.assertEquals(HttpGenerator.Result.NEED_HEADER, result);
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java
index 6156e20..288d8c7 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java
@@ -18,6 +18,13 @@
 
 package org.eclipse.jetty.http;
 
+import static org.hamcrest.Matchers.either;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -31,13 +38,6 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
-import static org.hamcrest.Matchers.either;
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
 @RunWith(Parameterized.class)
 public class HttpGeneratorServerHTTPTest
 {
@@ -59,14 +59,6 @@
 
         String response = run.result.build(run.httpVersion, gen, "OK\r\nTest", run.connection.val, null, run.chunks);
 
-        if (run.httpVersion == 9)
-        {
-            assertFalse(t, gen.isPersistent());
-            if (run.result._body != null)
-                assertEquals(t, run.result._body, response);
-            return;
-        }
-
         HttpParser parser = new HttpParser(handler);
         parser.setHeadResponse(run.result._head);
 
@@ -80,8 +72,7 @@
         else
             assertTrue(t, gen.isPersistent() || EnumSet.of(ConnectionType.CLOSE, ConnectionType.TE_CLOSE).contains(run.connection));
 
-        if (run.httpVersion > 9)
-            assertEquals("OK??Test", _reason);
+        assertEquals("OK??Test", _reason);
 
         if (_content == null)
             assertTrue(t, run.result._body == null);
@@ -145,7 +136,7 @@
             }
             ByteBuffer header = null;
             ByteBuffer chunk = null;
-            HttpGenerator.ResponseInfo info = null;
+            MetaData.Response info = null;
 
             loop:
             while (true)
@@ -157,12 +148,12 @@
                 // Generate
                 boolean last = !BufferUtil.hasContent(content);
 
-                HttpGenerator.Result result = gen.generateResponse(info, header, chunk, content, last);
+                HttpGenerator.Result result = gen.generateResponse(info, _head, header, chunk, content, last);
 
                 switch (result)
                 {
                     case NEED_INFO:
-                        info = new HttpGenerator.ResponseInfo(HttpVersion.fromVersion(version), _fields, _contentLength, _code, reason, _head);
+                        info = new MetaData.Response(HttpVersion.fromVersion(version), _code, reason, _fields, _contentLength);
                         continue;
 
                     case NEED_HEADER:
@@ -216,7 +207,7 @@
         }
     }
 
-    private class Handler implements HttpParser.ResponseHandler<ByteBuffer>
+    private class Handler implements HttpParser.ResponseHandler
     {
         @Override
         public boolean content(ByteBuffer ref)
@@ -241,15 +232,20 @@
         }
 
         @Override
+        public boolean contentComplete()
+        {
+            return true;
+        }
+
+        @Override
         public boolean messageComplete()
         {
             return true;
         }
 
         @Override
-        public boolean parsedHeader(HttpField field)
+        public void parsedHeader(HttpField field)
         {
-            return false;
         }
 
         @Override
@@ -347,7 +343,7 @@
         for (Result result : results)
         {
             // Loop over HTTP versions
-            for (int v = 9; v <= 11; v++)
+            for (int v = 10; v <= 11; v++)
             {
                 // Loop over chunks
                 for (int chunks = 1; chunks <= 6; chunks++)
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java
index 2126555..230dc97 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java
@@ -18,21 +18,55 @@
 
 package org.eclipse.jetty.http;
 
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
-import org.eclipse.jetty.util.BufferUtil;
-import org.junit.Assert;
-import org.junit.Test;
-
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.startsWith;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.util.BufferUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
 public class HttpGeneratorServerTest
 { 
+    @Test
+    public void test_0_9() throws Exception
+    {
+        ByteBuffer header = BufferUtil.allocate(8096);
+        ByteBuffer content = BufferUtil.toBuffer("0123456789");
+
+        HttpGenerator gen = new HttpGenerator();
+
+        HttpGenerator.Result result = gen.generateResponse(null, null, null, content, true);
+        assertEquals(HttpGenerator.Result.NEED_INFO, result);
+        assertEquals(HttpGenerator.State.START, gen.getState());
+
+        MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_0_9, 200, null, new HttpFields(), 10);
+        info.getFields().add("Content-Type", "test/data");
+        info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
+
+        result = gen.generateResponse(info, null, null, content, true);
+        assertEquals(HttpGenerator.Result.FLUSH, result);
+        assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
+        String response = BufferUtil.toString(header);
+        BufferUtil.clear(header);
+        response += BufferUtil.toString(content);
+        BufferUtil.clear(content);        
+
+        result = gen.generateResponse(null, null, null, content, false);
+        assertEquals(HttpGenerator.Result.SHUTDOWN_OUT, result);
+        assertEquals(HttpGenerator.State.END, gen.getState());
+        
+        assertEquals(10, gen.getContentPrepared());
+        
+        assertThat(response, not(containsString("200 OK")));
+        assertThat(response, not(containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT")));
+        assertThat(response, not(containsString("Content-Length: 10")));
+        assertThat(response, containsString("0123456789"));
+    }
     
     @Test
     public void testSimple() throws Exception
@@ -46,9 +80,9 @@
         assertEquals(HttpGenerator.Result.NEED_INFO, result);
         assertEquals(HttpGenerator.State.START, gen.getState());
 
-        ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 10, 200, null, false);
-        info.getHttpFields().add("Content-Type", "test/data");
-        info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
+        MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), 10);
+        info.getFields().add("Content-Type", "test/data");
+        info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
 
         result = gen.generateResponse(info, null, null, content, true);
         assertEquals(HttpGenerator.Result.NEED_HEADER, result);
@@ -81,9 +115,9 @@
 
         HttpGenerator gen = new HttpGenerator();
         
-        ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 10, 204, "Foo", false);
-        info.getHttpFields().add("Content-Type", "test/data");
-        info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
+        MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 204, "Foo", new HttpFields(), 10);
+        info.getFields().add("Content-Type", "test/data");
+        info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
 
         HttpGenerator.Result result = gen.generateResponse(info, header, null, content, true);
      
@@ -118,9 +152,9 @@
         assertEquals(HttpGenerator.Result.NEED_INFO, result);
         assertEquals(HttpGenerator.State.START, gen.getState());
 
-        ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 10, 200, null, false);
-        info.getHttpFields().add("Content-Type", "test/data;\r\nextra=value");
-        info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
+        MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), 10);
+        info.getFields().add("Content-Type", "test/data;\r\nextra=value");
+        info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
 
         result = gen.generateResponse(info, null, null, content, true);
         assertEquals(HttpGenerator.Result.NEED_HEADER, result);
@@ -150,11 +184,11 @@
     public void testSendServerXPoweredBy() throws Exception
     {
         ByteBuffer header = BufferUtil.allocate(8096);
-        ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
+        MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), -1);
         HttpFields fields = new HttpFields();
         fields.add(HttpHeader.SERVER, "SomeServer");
         fields.add(HttpHeader.X_POWERED_BY, "SomePower");
-        ResponseInfo infoF = new ResponseInfo(HttpVersion.HTTP_1_1, fields, -1, 200, null, false);
+        MetaData.Response infoF = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, fields, -1);
         String head;
 
         HttpGenerator gen = new HttpGenerator(true, true);
@@ -205,8 +239,8 @@
         assertEquals(HttpGenerator.Result.NEED_INFO, result);
         assertEquals(HttpGenerator.State.START, gen.getState());
 
-        ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
-        info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
+        MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), -1);
+        info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
 
         result = gen.generateResponse(info, null, null, null, true);
         assertEquals(HttpGenerator.Result.NEED_HEADER, result);
@@ -238,10 +272,10 @@
         assertEquals(HttpGenerator.Result.NEED_INFO, result);
         assertEquals(HttpGenerator.State.START, gen.getState());
 
-        ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 101, null, false);
-        info.getHttpFields().add("Upgrade", "WebSocket");
-        info.getHttpFields().add("Connection", "Upgrade");
-        info.getHttpFields().add("Sec-WebSocket-Accept", "123456789==");
+        MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 101, null, new HttpFields(), -1);
+        info.getFields().add("Upgrade", "WebSocket");
+        info.getFields().add("Connection", "Upgrade");
+        info.getFields().add("Sec-WebSocket-Accept", "123456789==");
 
         result = gen.generateResponse(info, header, null, null, true);
         assertEquals(HttpGenerator.Result.FLUSH, result);
@@ -273,8 +307,8 @@
         assertEquals(HttpGenerator.Result.NEED_INFO, result);
         assertEquals(HttpGenerator.State.START, gen.getState());
 
-        ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
-        info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
+        MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), -1);
+        info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
         result = gen.generateResponse(info, null, null, content0, false);
         assertEquals(HttpGenerator.Result.NEED_HEADER, result);
         assertEquals(HttpGenerator.State.START, gen.getState());
@@ -333,8 +367,8 @@
         assertEquals(HttpGenerator.Result.NEED_INFO, result);
         assertEquals(HttpGenerator.State.START, gen.getState());
 
-        ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false);
-        info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
+        MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), 59);
+        info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
         result = gen.generateResponse(info, null, null, content0, false);
         assertEquals(HttpGenerator.Result.NEED_HEADER, result);
         assertEquals(HttpGenerator.State.START, gen.getState());
@@ -396,8 +430,8 @@
         assertEquals(HttpGenerator.Result.NEED_INFO, result);
         assertEquals(HttpGenerator.State.START, gen.getState());
 
-        ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false);
-        info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
+        MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), BufferUtil.length(content0)+BufferUtil.length(content1));
+        info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
         result = gen.generateResponse(info, null, null, content0, false);
         assertEquals(HttpGenerator.Result.NEED_HEADER, result);
         assertEquals(HttpGenerator.State.START, gen.getState());
@@ -441,7 +475,7 @@
         fields.put(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
         String customValue = "test";
         fields.add(HttpHeader.CONNECTION, customValue);
-        ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_0, fields, -1, 200, "OK", false);
+        MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_0, 200, "OK", fields, -1);
         ByteBuffer header = BufferUtil.allocate(4096);
         HttpGenerator.Result result = generator.generateResponse(info, header, null, null, true);
         Assert.assertSame(HttpGenerator.Result.FLUSH, result);
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
index 3bbef8f..f4324aa 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
@@ -25,26 +25,26 @@
 
 import org.eclipse.jetty.http.HttpParser.State;
 import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
+import static org.hamcrest.Matchers.containsString;
 import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
 
 public class HttpParserTest
 {
     /**
      * Parse until {@link State#END} state.
      * If the parser is already in the END state, then it is {@link HttpParser#reset()} and re-parsed.
+     *
      * @param parser The parser to test
+     * @param buffer the buffer to parse
      * @throws IllegalStateException If the buffers have already been partially parsed.
      */
-    private static void parseAll(HttpParser parser, ByteBuffer buffer)
+    public static void parseAll(HttpParser parser, ByteBuffer buffer)
     {
         if (parser.isState(State.END))
             parser.reset();
@@ -52,13 +52,13 @@
             throw new IllegalStateException("!START");
 
         // continue parsing
-        int remaining=buffer.remaining();
-        while (!parser.isState(State.END) && remaining>0)
+        int remaining = buffer.remaining();
+        while (!parser.isState(State.END) && remaining > 0)
         {
-            int was_remaining=remaining;
+            int was_remaining = remaining;
             parser.parseNext(buffer);
-            remaining=buffer.remaining();
-            if (remaining==was_remaining)
+            remaining = buffer.remaining();
+            if (remaining == was_remaining)
                 break;
         }
     }
@@ -66,1512 +66,1845 @@
     @Test
     public void HttpMethodTest()
     {
-        assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer("Wibble ")));
-        assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer("GET")));
-        assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer("MO")));
-        
-        assertEquals(HttpMethod.GET,HttpMethod.lookAheadGet(BufferUtil.toBuffer("GET ")));
-        assertEquals(HttpMethod.MOVE,HttpMethod.lookAheadGet(BufferUtil.toBuffer("MOVE ")));
-        
+        Assert.assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer("Wibble ")));
+        Assert.assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer("GET")));
+        Assert.assertNull(HttpMethod.lookAheadGet(BufferUtil.toBuffer("MO")));
+
+        Assert.assertEquals(HttpMethod.GET, HttpMethod.lookAheadGet(BufferUtil.toBuffer("GET ")));
+        Assert.assertEquals(HttpMethod.MOVE, HttpMethod.lookAheadGet(BufferUtil.toBuffer("MOVE ")));
+
         ByteBuffer b = BufferUtil.allocateDirect(128);
-        BufferUtil.append(b,BufferUtil.toBuffer("GET"));
-        assertNull(HttpMethod.lookAheadGet(b));
-        
-        BufferUtil.append(b,BufferUtil.toBuffer(" "));
-        assertEquals(HttpMethod.GET,HttpMethod.lookAheadGet(b));
+        BufferUtil.append(b, BufferUtil.toBuffer("GET"));
+        Assert.assertNull(HttpMethod.lookAheadGet(b));
+
+        BufferUtil.append(b, BufferUtil.toBuffer(" "));
+        Assert.assertEquals(HttpMethod.GET, HttpMethod.lookAheadGet(b));
     }
-    
+
     @Test
     public void testLineParse_Mock_IP() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer("POST /mock/127.0.0.1 HTTP/1.1\015\012" + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer("POST /mock/127.0.0.1 HTTP/1.1\r\n" + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        assertEquals("POST", _methodOrVersion);
-        assertEquals("/mock/127.0.0.1", _uriOrStatus);
-        assertEquals("HTTP/1.1", _versionOrReason);
-        assertEquals(-1, _headers);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        Assert.assertEquals("POST", _methodOrVersion);
+        Assert.assertEquals("/mock/127.0.0.1", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.1", _versionOrReason);
+        Assert.assertEquals(-1, _headers);
     }
-    
+
     @Test
     public void testLineParse0() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer("POST /foo HTTP/1.0\015\012" + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer("POST /foo HTTP/1.0\r\n" + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        assertEquals("POST", _methodOrVersion);
-        assertEquals("/foo", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals(-1, _headers);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        Assert.assertEquals("POST", _methodOrVersion);
+        Assert.assertEquals("/foo", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(-1, _headers);
+    }
+
+    @Test
+    public void testLineParse1_RFC2616() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer("GET /999\r\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler, HttpCompliance.RFC2616);
+        parseAll(parser, buffer);
+
+        Assert.assertNull(_bad);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/999", _uriOrStatus);
+        Assert.assertEquals("HTTP/0.9", _versionOrReason);
+        Assert.assertEquals(-1, _headers);
+        assertThat(_complianceViolation,containsString("0.9"));
     }
 
     @Test
     public void testLineParse1() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer("GET /999\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer("GET /999\r\n");
 
-        _versionOrReason= null;
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/999", _uriOrStatus);
-        assertEquals(null, _versionOrReason);
-        assertEquals(-1, _headers);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        Assert.assertEquals("HTTP/0.9 not supported", _bad);
+        Assert.assertNull(_complianceViolation);
+    }
+
+    @Test
+    public void testLineParse2_RFC2616() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer("POST /222  \r\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler, HttpCompliance.RFC2616);
+        parseAll(parser, buffer);
+
+        Assert.assertNull(_bad);
+        Assert.assertEquals("POST", _methodOrVersion);
+        Assert.assertEquals("/222", _uriOrStatus);
+        Assert.assertEquals("HTTP/0.9", _versionOrReason);
+        Assert.assertEquals(-1, _headers);
+        assertThat(_complianceViolation,containsString("0.9"));
     }
 
     @Test
     public void testLineParse2() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer("POST /222  \015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer("POST /222  \r\n");
 
-        _versionOrReason= null;
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        assertEquals("POST", _methodOrVersion);
-        assertEquals("/222", _uriOrStatus);
-        assertEquals(null, _versionOrReason);
-        assertEquals(-1, _headers);
+        _versionOrReason = null;
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        Assert.assertEquals("HTTP/0.9 not supported", _bad);
+        Assert.assertNull(_complianceViolation);
     }
 
     @Test
     public void testLineParse3() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer("POST /fo\u0690 HTTP/1.0\015\012" + "\015\012",StandardCharsets.UTF_8);
+        ByteBuffer buffer = BufferUtil.toBuffer("POST /fo\u0690 HTTP/1.0\r\n" + "\r\n", StandardCharsets.UTF_8);
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        assertEquals("POST", _methodOrVersion);
-        assertEquals("/fo\u0690", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals(-1, _headers);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        Assert.assertEquals("POST", _methodOrVersion);
+        Assert.assertEquals("/fo\u0690", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(-1, _headers);
     }
 
     @Test
     public void testLineParse4() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer("POST /foo?param=\u0690 HTTP/1.0\015\012" + "\015\012",StandardCharsets.UTF_8);
+        ByteBuffer buffer = BufferUtil.toBuffer("POST /foo?param=\u0690 HTTP/1.0\r\n" + "\r\n", StandardCharsets.UTF_8);
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        assertEquals("POST", _methodOrVersion);
-        assertEquals("/foo?param=\u0690", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals(-1, _headers);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        Assert.assertEquals("POST", _methodOrVersion);
+        Assert.assertEquals("/foo?param=\u0690", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(-1, _headers);
     }
 
     @Test
     public void testLongURLParse() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer("POST /123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/ HTTP/1.0\015\012" + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer("POST /123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/ HTTP/1.0\r\n" + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        assertEquals("POST", _methodOrVersion);
-        assertEquals("/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals(-1, _headers);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        Assert.assertEquals("POST", _methodOrVersion);
+        Assert.assertEquals("/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(-1, _headers);
     }
-    
+
     @Test
     public void testConnect() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer("CONNECT 192.168.1.2:80 HTTP/1.1\015\012" + "\015\012");
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        assertEquals("CONNECT", _methodOrVersion);
-        assertEquals("192.168.1.2:80", _uriOrStatus);
-        assertEquals("HTTP/1.1", _versionOrReason);
-        assertEquals(-1, _headers);
+        ByteBuffer buffer = BufferUtil.toBuffer("CONNECT 192.168.1.2:80 HTTP/1.1\r\n" + "\r\n");
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        Assert.assertEquals("CONNECT", _methodOrVersion);
+        Assert.assertEquals("192.168.1.2:80", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.1", _versionOrReason);
+        Assert.assertEquals(-1, _headers);
     }
 
     @Test
     public void testSimple() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.0\015\012" +
-                "Host: localhost\015\012" +
-                "Connection: close\015\012" +
-                "\015\012");
-        
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n" +
+                        "Host: localhost\r\n" +
+                        "Connection: close\r\n" +
+                        "\r\n");
 
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals("Host", _hdr[0]);
-        assertEquals("localhost", _val[0]);
-        assertEquals("Connection", _hdr[1]);
-        assertEquals("close", _val[1]);
-        assertEquals(1, _headers);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals("Host", _hdr[0]);
+        Assert.assertEquals("localhost", _val[0]);
+        Assert.assertEquals("Connection", _hdr[1]);
+        Assert.assertEquals("close", _val[1]);
+        Assert.assertEquals(1, _headers);
     }
 
     @Test
+    public void testFoldedField2616() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n" +
+                        "Host: localhost\r\n" +
+                        "Name: value\r\n" +
+                        " extra\r\n" +
+                        "\r\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler, HttpCompliance.RFC2616);
+        parseAll(parser, buffer);
+
+        Assert.assertThat(_bad, Matchers.nullValue());
+        Assert.assertEquals("Host", _hdr[0]);
+        Assert.assertEquals("localhost", _val[0]);
+        Assert.assertEquals("Name", _hdr[1]);
+        Assert.assertEquals("value extra", _val[1]);
+        Assert.assertEquals(1, _headers);
+        assertThat(_complianceViolation,containsString("folding"));
+    }
+
+    @Test
+    public void testFoldedField7230() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n" +
+                        "Host: localhost\r\n" +
+                        "Name: value\r\n" +
+                        " extra\r\n" +
+                        "\r\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler, 4096, HttpCompliance.RFC7230);
+        parseAll(parser, buffer);
+
+        Assert.assertThat(_bad, Matchers.notNullValue());
+        Assert.assertThat(_bad, Matchers.containsString("Header Folding"));
+        Assert.assertNull(_complianceViolation);
+    }
+    
+    @Test
+    public void testWhiteSpaceInName() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n" +
+                        "Host: localhost\r\n" +
+                        "N ame: value\r\n" +
+                        "\r\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler, 4096, HttpCompliance.RFC7230);
+        parseAll(parser, buffer);
+
+        Assert.assertThat(_bad, Matchers.notNullValue());
+        Assert.assertThat(_bad, Matchers.containsString("Illegal character"));
+    }
+    
+    @Test
+    public void testWhiteSpaceAfterName() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n" +
+                        "Host: localhost\r\n" +
+                        "Name : value\r\n" +
+                        "\r\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler, 4096, HttpCompliance.RFC7230);
+        parseAll(parser, buffer);
+
+        Assert.assertThat(_bad, Matchers.notNullValue());
+        Assert.assertThat(_bad, Matchers.containsString("Illegal character"));
+    }
+    
+    @Test
+    public void testNoValue() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n" +
+                        "Host: localhost\r\n" +
+                        "Name0: \r\n" +
+                        "Name1:\r\n" +
+                        "\r\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals("Host", _hdr[0]);
+        Assert.assertEquals("localhost", _val[0]);
+        Assert.assertEquals("Name0", _hdr[1]);
+        Assert.assertEquals("", _val[1]);
+        Assert.assertEquals("Name1", _hdr[2]);
+        Assert.assertEquals("", _val[2]);
+        Assert.assertEquals(2, _headers);
+    }
+
+    @Test
+    public void testNoColon2616() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n" +
+                        "Host: localhost\r\n" +
+                        "Name\r\n" +
+                        "Other: value\r\n" +
+                        "\r\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler,HttpCompliance.RFC2616);
+        parseAll(parser, buffer);
+
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals("Host", _hdr[0]);
+        Assert.assertEquals("localhost", _val[0]);
+        Assert.assertEquals("Name", _hdr[1]);
+        Assert.assertEquals("", _val[1]);
+        Assert.assertEquals("Other", _hdr[2]);
+        Assert.assertEquals("value", _val[2]);
+        Assert.assertEquals(2, _headers);
+        assertThat(_complianceViolation,containsString("name only"));
+    }
+    
+    @Test
+    public void testNoColon7230() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n" +
+                        "Host: localhost\r\n" +
+                        "Name\r\n" +
+                        "\r\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler,HttpCompliance.RFC7230);
+        parseAll(parser, buffer);
+        Assert.assertThat(_bad, Matchers.containsString("Illegal character"));
+        Assert.assertNull(_complianceViolation);
+    }
+    
+
+    @Test
     public void testHeaderParseDirect() throws Exception
     {
-        ByteBuffer b0= BufferUtil.toBuffer(
-                "GET / HTTP/1.0\015\012" +
-                        "Host: localhost\015\012" +
-                        "Header1: value1\015\012" +
-                        "Header 2  :   value 2a  \015\012" +
-                        "    value 2b  \015\012" +
-                        "Header3: \015\012" +
-                        "Header4 \015\012" +
-                        "  value4\015\012" +
-                        "Server5 : notServer\015\012" +
-                        "Host Header: notHost\015\012" +
-                        "Connection: close\015\012" +
-                        "Accept-Encoding: gzip, deflated\015\012" +
-                        "Accept: unknown\015\012" +
-                "\015\012");
+        ByteBuffer b0 = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n" +
+                        "Host: localhost\r\n" +
+                        "Header1: value1\r\n" +
+                        "Header2:   value 2a  \r\n" +
+                        "Header3: 3\r\n" +
+                        "Header4:value4\r\n" +
+                        "Server5: notServer\r\n" +
+                        "HostHeader: notHost\r\n" +
+                        "Connection: close\r\n" +
+                        "Accept-Encoding: gzip, deflated\r\n" +
+                        "Accept: unknown\r\n" +
+                        "\r\n");
         ByteBuffer buffer = BufferUtil.allocateDirect(b0.capacity());
-        int pos=BufferUtil.flipToFill(buffer);
-        BufferUtil.put(b0,buffer);
-        BufferUtil.flipToFlush(buffer,pos);
-        
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
+        int pos = BufferUtil.flipToFill(buffer);
+        BufferUtil.put(b0, buffer);
+        BufferUtil.flipToFlush(buffer, pos);
 
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals("Host", _hdr[0]);
-        assertEquals("localhost", _val[0]);
-        assertEquals("Header1", _hdr[1]);
-        assertEquals("value1", _val[1]);
-        assertEquals("Header 2", _hdr[2]);
-        assertEquals("value 2a value 2b", _val[2]);
-        assertEquals("Header3", _hdr[3]);
-        assertEquals(null, _val[3]);
-        assertEquals("Header4", _hdr[4]);
-        assertEquals("value4", _val[4]);
-        assertEquals("Server5", _hdr[5]);
-        assertEquals("notServer", _val[5]);
-        assertEquals("Host Header", _hdr[6]);
-        assertEquals("notHost", _val[6]);
-        assertEquals("Connection", _hdr[7]);
-        assertEquals("close", _val[7]);
-        assertEquals("Accept-Encoding", _hdr[8]);
-        assertEquals("gzip, deflated", _val[8]);
-        assertEquals("Accept", _hdr[9]);
-        assertEquals("unknown", _val[9]);
-        assertEquals(9, _headers);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals("Host", _hdr[0]);
+        Assert.assertEquals("localhost", _val[0]);
+        Assert.assertEquals("Header1", _hdr[1]);
+        Assert.assertEquals("value1", _val[1]);
+        Assert.assertEquals("Header2", _hdr[2]);
+        Assert.assertEquals("value 2a", _val[2]);
+        Assert.assertEquals("Header3", _hdr[3]);
+        Assert.assertEquals("3", _val[3]);
+        Assert.assertEquals("Header4", _hdr[4]);
+        Assert.assertEquals("value4", _val[4]);
+        Assert.assertEquals("Server5", _hdr[5]);
+        Assert.assertEquals("notServer", _val[5]);
+        Assert.assertEquals("HostHeader", _hdr[6]);
+        Assert.assertEquals("notHost", _val[6]);
+        Assert.assertEquals("Connection", _hdr[7]);
+        Assert.assertEquals("close", _val[7]);
+        Assert.assertEquals("Accept-Encoding", _hdr[8]);
+        Assert.assertEquals("gzip, deflated", _val[8]);
+        Assert.assertEquals("Accept", _hdr[9]);
+        Assert.assertEquals("unknown", _val[9]);
+        Assert.assertEquals(9, _headers);
     }
-    
+
     @Test
     public void testHeaderParseCRLF() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.0\015\012" +
-                        "Host: localhost\015\012" +
-                        "Header1: value1\015\012" +
-                        "Header 2  :   value 2a  \015\012" +
-                        "    value 2b  \015\012" +
-                        "Header3: \015\012" +
-                        "Header4 \015\012" +
-                        "  value4\015\012" +
-                        "Server5 : notServer\015\012" +
-                        "Host Header: notHost\015\012" +
-                        "Connection: close\015\012" +
-                        "Accept-Encoding: gzip, deflated\015\012" +
-                        "Accept: unknown\015\012" +
-                "\015\012");
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n" +
+                        "Host: localhost\r\n" +
+                        "Header1: value1\r\n" +
+                        "Header2:   value 2a  \r\n" +
+                        "Header3: 3\r\n" +
+                        "Header4:value4\r\n" +
+                        "Server5: notServer\r\n" +
+                        "HostHeader: notHost\r\n" +
+                        "Connection: close\r\n" +
+                        "Accept-Encoding: gzip, deflated\r\n" +
+                        "Accept: unknown\r\n" +
+                        "\r\n");
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
 
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals("Host", _hdr[0]);
-        assertEquals("localhost", _val[0]);
-        assertEquals("Header1", _hdr[1]);
-        assertEquals("value1", _val[1]);
-        assertEquals("Header 2", _hdr[2]);
-        assertEquals("value 2a value 2b", _val[2]);
-        assertEquals("Header3", _hdr[3]);
-        assertEquals(null, _val[3]);
-        assertEquals("Header4", _hdr[4]);
-        assertEquals("value4", _val[4]);
-        assertEquals("Server5", _hdr[5]);
-        assertEquals("notServer", _val[5]);
-        assertEquals("Host Header", _hdr[6]);
-        assertEquals("notHost", _val[6]);
-        assertEquals("Connection", _hdr[7]);
-        assertEquals("close", _val[7]);
-        assertEquals("Accept-Encoding", _hdr[8]);
-        assertEquals("gzip, deflated", _val[8]);
-        assertEquals("Accept", _hdr[9]);
-        assertEquals("unknown", _val[9]);
-        assertEquals(9, _headers);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals("Host", _hdr[0]);
+        Assert.assertEquals("localhost", _val[0]);
+        Assert.assertEquals("Header1", _hdr[1]);
+        Assert.assertEquals("value1", _val[1]);
+        Assert.assertEquals("Header2", _hdr[2]);
+        Assert.assertEquals("value 2a", _val[2]);
+        Assert.assertEquals("Header3", _hdr[3]);
+        Assert.assertEquals("3", _val[3]);
+        Assert.assertEquals("Header4", _hdr[4]);
+        Assert.assertEquals("value4", _val[4]);
+        Assert.assertEquals("Server5", _hdr[5]);
+        Assert.assertEquals("notServer", _val[5]);
+        Assert.assertEquals("HostHeader", _hdr[6]);
+        Assert.assertEquals("notHost", _val[6]);
+        Assert.assertEquals("Connection", _hdr[7]);
+        Assert.assertEquals("close", _val[7]);
+        Assert.assertEquals("Accept-Encoding", _hdr[8]);
+        Assert.assertEquals("gzip, deflated", _val[8]);
+        Assert.assertEquals("Accept", _hdr[9]);
+        Assert.assertEquals("unknown", _val[9]);
+        Assert.assertEquals(9, _headers);
     }
 
     @Test
     public void testHeaderParseLF() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
+        ByteBuffer buffer = BufferUtil.toBuffer(
                 "GET / HTTP/1.0\n" +
                         "Host: localhost\n" +
                         "Header1: value1\n" +
-                        "Header 2  :   value 2a  \n" +
-                        "    value 2b  \n" +
-                        "Header3: \n" +
-                        "Header4 \n" +
-                        "  value4\n" +
-                        "Server5 : notServer\n" +
-                        "Host Header: notHost\n" +
+                        "Header2:   value 2a value 2b  \n" +
+                        "Header3: 3\n" +
+                        "Header4:value4\n" +
+                        "Server5: notServer\n" +
+                        "HostHeader: notHost\n" +
                         "Connection: close\n" +
                         "Accept-Encoding: gzip, deflated\n" +
                         "Accept: unknown\n" +
-                "\n");
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
+                        "\n");
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
 
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals("Host", _hdr[0]);
-        assertEquals("localhost", _val[0]);
-        assertEquals("Header1", _hdr[1]);
-        assertEquals("value1", _val[1]);
-        assertEquals("Header 2", _hdr[2]);
-        assertEquals("value 2a value 2b", _val[2]);
-        assertEquals("Header3", _hdr[3]);
-        assertEquals(null, _val[3]);
-        assertEquals("Header4", _hdr[4]);
-        assertEquals("value4", _val[4]);
-        assertEquals("Server5", _hdr[5]);
-        assertEquals("notServer", _val[5]);
-        assertEquals("Host Header", _hdr[6]);
-        assertEquals("notHost", _val[6]);
-        assertEquals("Connection", _hdr[7]);
-        assertEquals("close", _val[7]);
-        assertEquals("Accept-Encoding", _hdr[8]);
-        assertEquals("gzip, deflated", _val[8]);
-        assertEquals("Accept", _hdr[9]);
-        assertEquals("unknown", _val[9]);
-        assertEquals(9, _headers);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals("Host", _hdr[0]);
+        Assert.assertEquals("localhost", _val[0]);
+        Assert.assertEquals("Header1", _hdr[1]);
+        Assert.assertEquals("value1", _val[1]);
+        Assert.assertEquals("Header2", _hdr[2]);
+        Assert.assertEquals("value 2a value 2b", _val[2]);
+        Assert.assertEquals("Header3", _hdr[3]);
+        Assert.assertEquals("3", _val[3]);
+        Assert.assertEquals("Header4", _hdr[4]);
+        Assert.assertEquals("value4", _val[4]);
+        Assert.assertEquals("Server5", _hdr[5]);
+        Assert.assertEquals("notServer", _val[5]);
+        Assert.assertEquals("HostHeader", _hdr[6]);
+        Assert.assertEquals("notHost", _val[6]);
+        Assert.assertEquals("Connection", _hdr[7]);
+        Assert.assertEquals("close", _val[7]);
+        Assert.assertEquals("Accept-Encoding", _hdr[8]);
+        Assert.assertEquals("gzip, deflated", _val[8]);
+        Assert.assertEquals("Accept", _hdr[9]);
+        Assert.assertEquals("unknown", _val[9]);
+        Assert.assertEquals(9, _headers);
     }
 
     @Test
     public void testQuoted() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
+        ByteBuffer buffer = BufferUtil.toBuffer(
                 "GET / HTTP/1.0\n" +
                         "Name0: \"value0\"\t\n" +
                         "Name1: \"value\t1\"\n" +
                         "Name2: \"value\t2A\",\"value,2B\"\t\n" +
-                "\n");
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
+                        "\n");
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
 
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals("Name0", _hdr[0]);
-        assertEquals("\"value0\"", _val[0]);
-        assertEquals("Name1", _hdr[1]);
-        assertEquals("\"value\t1\"", _val[1]);
-        assertEquals("Name2", _hdr[2]);
-        assertEquals("\"value\t2A\",\"value,2B\"", _val[2]);
-        assertEquals(2, _headers);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals("Name0", _hdr[0]);
+        Assert.assertEquals("\"value0\"", _val[0]);
+        Assert.assertEquals("Name1", _hdr[1]);
+        Assert.assertEquals("\"value\t1\"", _val[1]);
+        Assert.assertEquals("Name2", _hdr[2]);
+        Assert.assertEquals("\"value\t2A\",\"value,2B\"", _val[2]);
+        Assert.assertEquals(2, _headers);
     }
 
     @Test
     public void testEncodedHeader() throws Exception
     {
-        ByteBuffer buffer=BufferUtil.allocate(4096);
-        BufferUtil.flipToFill(buffer); 
-        BufferUtil.put(BufferUtil.toBuffer("GET "),buffer);
+        ByteBuffer buffer = BufferUtil.allocate(4096);
+        BufferUtil.flipToFill(buffer);
+        BufferUtil.put(BufferUtil.toBuffer("GET "), buffer);
         buffer.put("/foo/\u0690/".getBytes(StandardCharsets.UTF_8));
-        BufferUtil.put(BufferUtil.toBuffer(" HTTP/1.0\r\n"),buffer);
-        BufferUtil.put(BufferUtil.toBuffer("Header1: "),buffer);
+        BufferUtil.put(BufferUtil.toBuffer(" HTTP/1.0\r\n"), buffer);
+        BufferUtil.put(BufferUtil.toBuffer("Header1: "), buffer);
         buffer.put("\u00e6 \u00e6".getBytes(StandardCharsets.ISO_8859_1));
-        BufferUtil.put(BufferUtil.toBuffer("  \r\n\r\n"),buffer);
-        BufferUtil.flipToFlush(buffer,0);
-                    
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
+        BufferUtil.put(BufferUtil.toBuffer("  \r\n\r\n"), buffer);
+        BufferUtil.flipToFlush(buffer, 0);
 
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/foo/\u0690/", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals("Header1", _hdr[0]);
-        assertEquals("\u00e6 \u00e6", _val[0]);
-        assertEquals(0, _headers);
-        assertEquals(null,_bad);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/foo/\u0690/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals("Header1", _hdr[0]);
+        Assert.assertEquals("\u00e6 \u00e6", _val[0]);
+        Assert.assertEquals(0, _headers);
+        Assert.assertEquals(null, _bad);
     }
 
     @Test
     public void testBadMethodEncoding() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-            "G\u00e6T / HTTP/1.0\r\nHeader0: value0\r\n\n\n");
-        
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        assertThat(_bad,Matchers.notNullValue());
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "G\u00e6T / HTTP/1.0\r\nHeader0: value0\r\n\n\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        Assert.assertThat(_bad, Matchers.notNullValue());
     }
 
     @Test
     public void testBadVersionEncoding() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-            "GET / H\u00e6P/1.0\r\nHeader0: value0\r\n\n\n");
-        
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        assertThat(_bad,Matchers.notNullValue());
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / H\u00e6P/1.0\r\nHeader0: value0\r\n\n\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        Assert.assertThat(_bad, Matchers.notNullValue());
     }
 
     @Test
     public void testBadHeaderEncoding() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-            "GET / HTTP/1.0\r\nH\u00e6der0: value0\r\n\n\n");
-        
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        assertThat(_bad,Matchers.notNullValue());
-    } 
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\nH\u00e6der0: value0\r\n\n\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        Assert.assertThat(_bad, Matchers.notNullValue());
+    }
 
     @Test
     public void testHeaderTab() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-            "GET / HTTP/1.1\r\n" +
-            "Host: localhost\r\n" +
-            "Header: value\talternate\r\n" +
-            "\n\n");
-        
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/", _uriOrStatus);
-        assertEquals("HTTP/1.1", _versionOrReason);
-        assertEquals("Host", _hdr[0]);
-        assertEquals("localhost", _val[0]);
-        assertEquals("Header", _hdr[1]);
-        assertEquals("value\talternate", _val[1]);
-    } 
-    
-    @Test
-    public void testNonStrict() throws Exception
-    {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "get / http/1.0\015\012" +
-                "HOST: localhost\015\012" +
-                "cOnNeCtIoN: ClOsE\015\012"+
-                "\015\012");
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler,-1,false);
-        parseAll(parser,buffer);
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.1\r\n" +
+                        "Host: localhost\r\n" +
+                        "Header: value\talternate\r\n" +
+                        "\n\n");
 
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals("Host", _hdr[0]);
-        assertEquals("localhost", _val[0]);
-        assertEquals("Connection", _hdr[1]);
-        assertEquals("close", _val[1]);
-        assertEquals(1, _headers);
-    }
-    
-    @Test
-    public void testStrict() throws Exception
-    {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "gEt / http/1.0\015\012" +
-                "HOST: localhost\015\012" +
-                "cOnNeCtIoN: ClOsE\015\012"+
-                "\015\012");
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler,-1,true);
-        parseAll(parser,buffer);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
 
-        assertEquals("gEt", _methodOrVersion);
-        assertEquals("/", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals("HOST", _hdr[0]);
-        assertEquals("localhost", _val[0]);
-        assertEquals("cOnNeCtIoN", _hdr[1]);
-        assertEquals("ClOsE", _val[1]);
-        assertEquals(1, _headers);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.1", _versionOrReason);
+        Assert.assertEquals("Host", _hdr[0]);
+        Assert.assertEquals("localhost", _val[0]);
+        Assert.assertEquals("Header", _hdr[1]);
+        Assert.assertEquals("value\talternate", _val[1]);
     }
-    
+
+    @Test
+    public void testCaseInsensitive() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "get / http/1.0\r\n" +
+                        "HOST: localhost\r\n" +
+                        "cOnNeCtIoN: ClOsE\r\n" +
+                        "\r\n");
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler, -1, HttpCompliance.RFC7230);
+        parseAll(parser, buffer);
+        Assert.assertNull(_bad);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals("Host", _hdr[0]);
+        Assert.assertEquals("localhost", _val[0]);
+        Assert.assertEquals("Connection", _hdr[1]);
+        Assert.assertEquals("close", _val[1]);
+        Assert.assertEquals(1, _headers);
+        Assert.assertNull(_complianceViolation);
+    }
+
+    @Test
+    public void testCaseSensitiveLegacy() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "gEt / http/1.0\r\n" +
+                        "HOST: localhost\r\n" +
+                        "cOnNeCtIoN: ClOsE\r\n" +
+                        "\r\n");
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler, -1, HttpCompliance.LEGACY);
+        parseAll(parser, buffer);
+        Assert.assertNull(_bad);
+        Assert.assertEquals("gEt", _methodOrVersion);
+        Assert.assertEquals("/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals("HOST", _hdr[0]);
+        Assert.assertEquals("localhost", _val[0]);
+        Assert.assertEquals("cOnNeCtIoN", _hdr[1]);
+        Assert.assertEquals("ClOsE", _val[1]);
+        Assert.assertEquals(1, _headers);
+        assertThat(_complianceViolation,containsString("case sensitive"));
+    }
+
     @Test
     public void testSplitHeaderParse() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "XXXXSPLIT / HTTP/1.0\015\012" +
-                    "Host: localhost\015\012" +
-                    "Header1: value1\015\012" +
-                    "Header2  :   value 2a  \015\012" +
-                    "                    value 2b  \015\012" +
-                    "Header3: \015\012" +
-                    "Header4 \015\012" +
-                    "  value4\015\012" +
-                    "Server5: notServer\015\012" +
-                    "\015\012ZZZZ");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "XXXXSPLIT / HTTP/1.0\r\n" +
+                        "Host: localhost\r\n" +
+                        "Header1: value1\r\n" +
+                        "Header2:   value 2a  \r\n" +
+                        "Header3: 3\r\n" +
+                        "Header4:value4\r\n" +
+                        "Server5: notServer\r\n" +
+                        "\r\nZZZZ");
         buffer.position(2);
-        buffer.limit(buffer.capacity()-2);
-        buffer=buffer.slice();
+        buffer.limit(buffer.capacity() - 2);
+        buffer = buffer.slice();
 
-        for (int i=0;i<buffer.capacity()-4;i++)
+        for (int i = 0; i < buffer.capacity() - 4; i++)
         {
-            HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-            HttpParser parser= new HttpParser(handler);
+            HttpParser.RequestHandler handler = new Handler();
+            HttpParser parser = new HttpParser(handler);
 
-            // System.err.println(BufferUtil.toDetailString(buffer));
             buffer.position(2);
-            buffer.limit(2+i);
+            buffer.limit(2 + i);
 
             if (!parser.parseNext(buffer))
             {
                 // consumed all
-                assertEquals(0,buffer.remaining());
+                Assert.assertEquals(0, buffer.remaining());
 
                 // parse the rest
-                buffer.limit(buffer.capacity()-2);
+                buffer.limit(buffer.capacity() - 2);
                 parser.parseNext(buffer);
             }
 
-            assertEquals("SPLIT", _methodOrVersion);
-            assertEquals("/", _uriOrStatus);
-            assertEquals("HTTP/1.0", _versionOrReason);
-            assertEquals("Host", _hdr[0]);
-            assertEquals("localhost", _val[0]);
-            assertEquals("Header1", _hdr[1]);
-            assertEquals("value1", _val[1]);
-            assertEquals("Header2", _hdr[2]);
-            assertEquals("value 2a value 2b", _val[2]);
-            assertEquals("Header3", _hdr[3]);
-            assertEquals(null, _val[3]);
-            assertEquals("Header4", _hdr[4]);
-            assertEquals("value4", _val[4]);
-            assertEquals("Server5", _hdr[5]);
-            assertEquals("notServer", _val[5]);
-            assertEquals(5, _headers);
+            Assert.assertEquals("SPLIT", _methodOrVersion);
+            Assert.assertEquals("/", _uriOrStatus);
+            Assert.assertEquals("HTTP/1.0", _versionOrReason);
+            Assert.assertEquals("Host", _hdr[0]);
+            Assert.assertEquals("localhost", _val[0]);
+            Assert.assertEquals("Header1", _hdr[1]);
+            Assert.assertEquals("value1", _val[1]);
+            Assert.assertEquals("Header2", _hdr[2]);
+            Assert.assertEquals("value 2a", _val[2]);
+            Assert.assertEquals("Header3", _hdr[3]);
+            Assert.assertEquals("3", _val[3]);
+            Assert.assertEquals("Header4", _hdr[4]);
+            Assert.assertEquals("value4", _val[4]);
+            Assert.assertEquals("Server5", _hdr[5]);
+            Assert.assertEquals("notServer", _val[5]);
+            Assert.assertEquals(5, _headers);
         }
     }
 
     @Test
     public void testChunkParse() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET /chunk HTTP/1.0\015\012"
-                        + "Header1: value1\015\012"
-                        + "Transfer-Encoding: chunked\015\012"
-                        + "\015\012"
-                        + "a;\015\012"
-                        + "0123456789\015\012"
-                        + "1a\015\012"
-                        + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\015\012"
-                        + "0\015\012"
-                        + "\015\012");
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET /chunk HTTP/1.0\r\n"
+                        + "Header1: value1\r\n"
+                        + "Transfer-Encoding: chunked\r\n"
+                        + "\r\n"
+                        + "a;\r\n"
+                        + "0123456789\r\n"
+                        + "1a\r\n"
+                        + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
+                        + "0\r\n"
+                        + "\r\n");
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
 
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/chunk", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals(1, _headers);
-        assertEquals("Header1", _hdr[0]);
-        assertEquals("value1", _val[0]);
-        assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/chunk", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(1, _headers);
+        Assert.assertEquals("Header1", _hdr[0]);
+        Assert.assertEquals("value1", _val[0]);
+        Assert.assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
 
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
+    }
+
+    @Test
+    public void testChunkParseTrailer() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET /chunk HTTP/1.0\r\n"
+                        + "Header1: value1\r\n"
+                        + "Transfer-Encoding: chunked\r\n"
+                        + "\r\n"
+                        + "a;\r\n"
+                        + "0123456789\r\n"
+                        + "1a\r\n"
+                        + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
+                        + "0\r\n"
+                        + "Trailer: value\r\n"
+                        + "\r\n");
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/chunk", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(1, _headers);
+        Assert.assertEquals("Header1", _hdr[0]);
+        Assert.assertEquals("value1", _val[0]);
+        Assert.assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
+
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
+    }
+
+    @Test
+    public void testChunkParseBadTrailer() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET /chunk HTTP/1.0\r\n"
+                        + "Header1: value1\r\n"
+                        + "Transfer-Encoding: chunked\r\n"
+                        + "\r\n"
+                        + "a;\r\n"
+                        + "0123456789\r\n"
+                        + "1a\r\n"
+                        + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
+                        + "0\r\n"
+                        + "Trailer: value");
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/chunk", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(1, _headers);
+        Assert.assertEquals("Header1", _hdr[0]);
+        Assert.assertEquals("value1", _val[0]);
+        Assert.assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
+
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_early);
+    }
+
+
+    @Test
+    public void testChunkParseNoTrailer() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET /chunk HTTP/1.0\r\n"
+                        + "Header1: value1\r\n"
+                        + "Transfer-Encoding: chunked\r\n"
+                        + "\r\n"
+                        + "a;\r\n"
+                        + "0123456789\r\n"
+                        + "1a\r\n"
+                        + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
+                        + "0\r\n");
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/chunk", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(1, _headers);
+        Assert.assertEquals("Header1", _hdr[0]);
+        Assert.assertEquals("value1", _val[0]);
+        Assert.assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
+
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
     }
 
     @Test
     public void testStartEOF() throws Exception
     {
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.atEOF();
         parser.parseNext(BufferUtil.EMPTY_BUFFER);
 
-        assertTrue(_early);
-        assertEquals(null,_bad);
+        Assert.assertTrue(_early);
+        Assert.assertEquals(null, _bad);
     }
 
     @Test
     public void testEarlyEOF() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET /uri HTTP/1.0\015\012"
-                        + "Content-Length: 20\015\012"
-                        + "\015\012"
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET /uri HTTP/1.0\r\n"
+                        + "Content-Length: 20\r\n"
+                        + "\r\n"
                         + "0123456789");
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.atEOF();
-        parseAll(parser,buffer);
+        parseAll(parser, buffer);
 
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/uri", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals("0123456789", _content);
-        
-        assertTrue(_early);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/uri", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals("0123456789", _content);
+
+        Assert.assertTrue(_early);
     }
 
     @Test
     public void testChunkEarlyEOF() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET /chunk HTTP/1.0\015\012"
-                        + "Header1: value1\015\012"
-                        + "Transfer-Encoding: chunked\015\012"
-                        + "\015\012"
-                        + "a;\015\012"
-                        + "0123456789\015\012");
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET /chunk HTTP/1.0\r\n"
+                        + "Header1: value1\r\n"
+                        + "Transfer-Encoding: chunked\r\n"
+                        + "\r\n"
+                        + "a;\r\n"
+                        + "0123456789\r\n");
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.atEOF();
-        parseAll(parser,buffer);
+        parseAll(parser, buffer);
 
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/chunk", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals(1, _headers);
-        assertEquals("Header1", _hdr[0]);
-        assertEquals("value1", _val[0]);
-        assertEquals("0123456789", _content);
-        
-        assertTrue(_early);
-        
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/chunk", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(1, _headers);
+        Assert.assertEquals("Header1", _hdr[0]);
+        Assert.assertEquals("value1", _val[0]);
+        Assert.assertEquals("0123456789", _content);
+
+        Assert.assertTrue(_early);
     }
 
     @Test
     public void testMultiParse() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                          "GET /mp HTTP/1.0\015\012"
-                        + "Connection: Keep-Alive\015\012"
-                        + "Header1: value1\015\012"
-                        + "Transfer-Encoding: chunked\015\012"
-                        + "\015\012"
-                        + "a;\015\012"
-                        + "0123456789\015\012"
-                        + "1a\015\012"
-                        + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\015\012"
-                        + "0\015\012"
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET /mp HTTP/1.0\r\n"
+                        + "Connection: Keep-Alive\r\n"
+                        + "Header1: value1\r\n"
+                        + "Transfer-Encoding: chunked\r\n"
+                        + "\r\n"
+                        + "a;\r\n"
+                        + "0123456789\r\n"
+                        + "1a\r\n"
+                        + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
+                        + "0\r\n"
 
-                        + "\015\012"
+                        + "\r\n"
 
-                        + "POST /foo HTTP/1.0\015\012"
-                        + "Connection: Keep-Alive\015\012"
-                        + "Header2: value2\015\012"
-                        + "Content-Length: 0\015\012"
-                        + "\015\012"
+                        + "POST /foo HTTP/1.0\r\n"
+                        + "Connection: Keep-Alive\r\n"
+                        + "Header2: value2\r\n"
+                        + "Content-Length: 0\r\n"
+                        + "\r\n"
 
-                        + "PUT /doodle HTTP/1.0\015\012"
-                        + "Connection: close\015\012"
-                        + "Header3: value3\015\012"
-                        + "Content-Length: 10\015\012"
-                        + "\015\012"
-                        + "0123456789\015\012");
+                        + "PUT /doodle HTTP/1.0\r\n"
+                        + "Connection: close\r\n"
+                        + "Header3: value3\r\n"
+                        + "Content-Length: 10\r\n"
+                        + "\r\n"
+                        + "0123456789\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/mp", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals(2, _headers);
-        assertEquals("Header1", _hdr[1]);
-        assertEquals("value1", _val[1]);
-        assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/mp", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(2, _headers);
+        Assert.assertEquals("Header1", _hdr[1]);
+        Assert.assertEquals("value1", _val[1]);
+        Assert.assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
 
         parser.reset();
         init();
         parser.parseNext(buffer);
-        assertEquals("POST", _methodOrVersion);
-        assertEquals("/foo", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals(2, _headers);
-        assertEquals("Header2", _hdr[1]);
-        assertEquals("value2", _val[1]);
-        assertEquals(null, _content);
+        Assert.assertEquals("POST", _methodOrVersion);
+        Assert.assertEquals("/foo", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(2, _headers);
+        Assert.assertEquals("Header2", _hdr[1]);
+        Assert.assertEquals("value2", _val[1]);
+        Assert.assertEquals(null, _content);
 
         parser.reset();
         init();
         parser.parseNext(buffer);
         parser.atEOF();
-        assertEquals("PUT", _methodOrVersion);
-        assertEquals("/doodle", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals(2, _headers);
-        assertEquals("Header3", _hdr[1]);
-        assertEquals("value3", _val[1]);
-        assertEquals("0123456789", _content);
+        Assert.assertEquals("PUT", _methodOrVersion);
+        Assert.assertEquals("/doodle", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(2, _headers);
+        Assert.assertEquals("Header3", _hdr[1]);
+        Assert.assertEquals("value3", _val[1]);
+        Assert.assertEquals("0123456789", _content);
     }
 
     @Test
     public void testMultiParseEarlyEOF() throws Exception
     {
-        ByteBuffer buffer0= BufferUtil.toBuffer(
-                          "GET /mp HTTP/1.0\015\012"
-                        + "Connection: Keep-Alive\015\012");
+        ByteBuffer buffer0 = BufferUtil.toBuffer(
+                "GET /mp HTTP/1.0\r\n"
+                        + "Connection: Keep-Alive\r\n");
 
-        ByteBuffer buffer1= BufferUtil.toBuffer("Header1: value1\015\012"
-                        + "Transfer-Encoding: chunked\015\012"
-                        + "\015\012"
-                        + "a;\015\012"
-                        + "0123456789\015\012"
-                        + "1a\015\012"
-                        + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\015\012"
-                        + "0\015\012"
+        ByteBuffer buffer1 = BufferUtil.toBuffer("Header1: value1\r\n"
+                + "Transfer-Encoding: chunked\r\n"
+                + "\r\n"
+                + "a;\r\n"
+                + "0123456789\r\n"
+                + "1a\r\n"
+                + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
+                + "0\r\n"
 
-                        + "\015\012"
+                + "\r\n"
 
-                        + "POST /foo HTTP/1.0\015\012"
-                        + "Connection: Keep-Alive\015\012"
-                        + "Header2: value2\015\012"
-                        + "Content-Length: 0\015\012"
-                        + "\015\012"
+                + "POST /foo HTTP/1.0\r\n"
+                + "Connection: Keep-Alive\r\n"
+                + "Header2: value2\r\n"
+                + "Content-Length: 0\r\n"
+                + "\r\n"
 
-                        + "PUT /doodle HTTP/1.0\015\012"
-                        + "Connection: close\015\012"
-                        + "Header3: value3\015\012"
-                        + "Content-Length: 10\015\012"
-                        + "\015\012"
-                        + "0123456789\015\012");
+                + "PUT /doodle HTTP/1.0\r\n"
+                + "Connection: close\r\n"
+                + "Header3: value3\r\n"
+                + "Content-Length: 10\r\n"
+                + "\r\n"
+                + "0123456789\r\n");
 
-
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer0);
         parser.atEOF();
         parser.parseNext(buffer1);
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/mp", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals(2, _headers);
-        assertEquals("Header1", _hdr[1]);
-        assertEquals("value1", _val[1]);
-        assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/mp", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(2, _headers);
+        Assert.assertEquals("Header1", _hdr[1]);
+        Assert.assertEquals("value1", _val[1]);
+        Assert.assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
 
         parser.reset();
         init();
         parser.parseNext(buffer1);
-        assertEquals("POST", _methodOrVersion);
-        assertEquals("/foo", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals(2, _headers);
-        assertEquals("Header2", _hdr[1]);
-        assertEquals("value2", _val[1]);
-        assertEquals(null, _content);
+        Assert.assertEquals("POST", _methodOrVersion);
+        Assert.assertEquals("/foo", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(2, _headers);
+        Assert.assertEquals("Header2", _hdr[1]);
+        Assert.assertEquals("value2", _val[1]);
+        Assert.assertEquals(null, _content);
 
         parser.reset();
         init();
         parser.parseNext(buffer1);
-        assertEquals("PUT", _methodOrVersion);
-        assertEquals("/doodle", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals(2, _headers);
-        assertEquals("Header3", _hdr[1]);
-        assertEquals("value3", _val[1]);
-        assertEquals("0123456789", _content);
+        Assert.assertEquals("PUT", _methodOrVersion);
+        Assert.assertEquals("/doodle", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.0", _versionOrReason);
+        Assert.assertEquals(2, _headers);
+        Assert.assertEquals("Header3", _hdr[1]);
+        Assert.assertEquals("value3", _val[1]);
+        Assert.assertEquals("0123456789", _content);
     }
-    
+
     @Test
     public void testResponseParse0() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "HTTP/1.1 200 Correct\015\012"
-                        + "Content-Length: 10\015\012"
-                        + "Content-Type: text/plain\015\012"
-                        + "\015\012"
-                        + "0123456789\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "HTTP/1.1 200 Correct\r\n"
+                        + "Content-Length: 10\r\n"
+                        + "Content-Type: text/plain\r\n"
+                        + "\r\n"
+                        + "0123456789\r\n");
 
-        HttpParser.ResponseHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.ResponseHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("HTTP/1.1", _methodOrVersion);
-        assertEquals("200", _uriOrStatus);
-        assertEquals("Correct", _versionOrReason);
-        assertEquals(10,_content.length());
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
+        Assert.assertEquals("HTTP/1.1", _methodOrVersion);
+        Assert.assertEquals("200", _uriOrStatus);
+        Assert.assertEquals("Correct", _versionOrReason);
+        Assert.assertEquals(10, _content.length());
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
     }
 
     @Test
     public void testResponseParse1() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "HTTP/1.1 304 Not-Modified\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "HTTP/1.1 304 Not-Modified\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.ResponseHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.ResponseHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("HTTP/1.1", _methodOrVersion);
-        assertEquals("304", _uriOrStatus);
-        assertEquals("Not-Modified", _versionOrReason);
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
+        Assert.assertEquals("HTTP/1.1", _methodOrVersion);
+        Assert.assertEquals("304", _uriOrStatus);
+        Assert.assertEquals("Not-Modified", _versionOrReason);
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
     }
 
     @Test
     public void testResponseParse2() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                          "HTTP/1.1 204 No-Content\015\012"
-                        + "Header: value\015\012"
-                        + "\015\012"
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "HTTP/1.1 204 No-Content\r\n"
+                        + "Header: value\r\n"
+                        + "\r\n"
 
-                        + "HTTP/1.1 200 Correct\015\012"
-                        + "Content-Length: 10\015\012"
-                        + "Content-Type: text/plain\015\012"
-                        + "\015\012"
-                        + "0123456789\015\012");
+                        + "HTTP/1.1 200 Correct\r\n"
+                        + "Content-Length: 10\r\n"
+                        + "Content-Type: text/plain\r\n"
+                        + "\r\n"
+                        + "0123456789\r\n");
 
-        HttpParser.ResponseHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.ResponseHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("HTTP/1.1", _methodOrVersion);
-        assertEquals("204", _uriOrStatus);
-        assertEquals("No-Content", _versionOrReason);
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
+        Assert.assertEquals("HTTP/1.1", _methodOrVersion);
+        Assert.assertEquals("204", _uriOrStatus);
+        Assert.assertEquals("No-Content", _versionOrReason);
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
 
         parser.reset();
         init();
 
         parser.parseNext(buffer);
         parser.atEOF();
-        assertEquals("HTTP/1.1", _methodOrVersion);
-        assertEquals("200", _uriOrStatus);
-        assertEquals("Correct", _versionOrReason);
-        assertEquals(_content.length(), 10);
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
+        Assert.assertEquals("HTTP/1.1", _methodOrVersion);
+        Assert.assertEquals("200", _uriOrStatus);
+        Assert.assertEquals("Correct", _versionOrReason);
+        Assert.assertEquals(_content.length(), 10);
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
     }
 
     @Test
     public void testResponseParse3() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "HTTP/1.1 200\015\012"
-                        + "Content-Length: 10\015\012"
-                        + "Content-Type: text/plain\015\012"
-                        + "\015\012"
-                        + "0123456789\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "HTTP/1.1 200\r\n"
+                        + "Content-Length: 10\r\n"
+                        + "Content-Type: text/plain\r\n"
+                        + "\r\n"
+                        + "0123456789\r\n");
 
-        HttpParser.ResponseHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.ResponseHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("HTTP/1.1", _methodOrVersion);
-        assertEquals("200", _uriOrStatus);
-        assertEquals(null, _versionOrReason);
-        assertEquals(_content.length(), 10);
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
+        Assert.assertEquals("HTTP/1.1", _methodOrVersion);
+        Assert.assertEquals("200", _uriOrStatus);
+        Assert.assertEquals(null, _versionOrReason);
+        Assert.assertEquals(_content.length(), 10);
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
     }
 
     @Test
     public void testResponseParse4() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "HTTP/1.1 200 \015\012"
-                        + "Content-Length: 10\015\012"
-                        + "Content-Type: text/plain\015\012"
-                        + "\015\012"
-                        + "0123456789\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "HTTP/1.1 200 \r\n"
+                        + "Content-Length: 10\r\n"
+                        + "Content-Type: text/plain\r\n"
+                        + "\r\n"
+                        + "0123456789\r\n");
 
-        HttpParser.ResponseHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.ResponseHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("HTTP/1.1", _methodOrVersion);
-        assertEquals("200", _uriOrStatus);
-        assertEquals(null, _versionOrReason);
-        assertEquals(_content.length(), 10);
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
+        Assert.assertEquals("HTTP/1.1", _methodOrVersion);
+        Assert.assertEquals("200", _uriOrStatus);
+        Assert.assertEquals(null, _versionOrReason);
+        Assert.assertEquals(_content.length(), 10);
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
     }
 
     @Test
     public void testResponseEOFContent() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "HTTP/1.1 200 \015\012"
-                        + "Content-Type: text/plain\015\012"
-                        + "\015\012"
-                        + "0123456789\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "HTTP/1.1 200 \r\n"
+                        + "Content-Type: text/plain\r\n"
+                        + "\r\n"
+                        + "0123456789\r\n");
 
-        HttpParser.ResponseHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.ResponseHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.atEOF();
         parser.parseNext(buffer);
-        
-        assertEquals("HTTP/1.1", _methodOrVersion);
-        assertEquals("200", _uriOrStatus);
-        assertEquals(null, _versionOrReason);
-        assertEquals(12,_content.length());
-        assertEquals("0123456789\015\012",_content);
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
-    }    
-    
+
+        Assert.assertEquals("HTTP/1.1", _methodOrVersion);
+        Assert.assertEquals("200", _uriOrStatus);
+        Assert.assertEquals(null, _versionOrReason);
+        Assert.assertEquals(12, _content.length());
+        Assert.assertEquals("0123456789\r\n", _content);
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
+    }
+
     @Test
     public void testResponse304WithContentLength() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "HTTP/1.1 304 found\015\012"
-                        + "Content-Length: 10\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "HTTP/1.1 304 found\r\n"
+                        + "Content-Length: 10\r\n"
+                        + "\r\n");
 
-        HttpParser.ResponseHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.ResponseHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("HTTP/1.1", _methodOrVersion);
-        assertEquals("304", _uriOrStatus);
-        assertEquals("found", _versionOrReason);
-        assertEquals(null,_content);
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
+        Assert.assertEquals("HTTP/1.1", _methodOrVersion);
+        Assert.assertEquals("304", _uriOrStatus);
+        Assert.assertEquals("found", _versionOrReason);
+        Assert.assertEquals(null, _content);
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
     }
 
     @Test
     public void testResponse101WithTransferEncoding() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "HTTP/1.1 101 switching protocols\015\012"
-                        + "Transfer-Encoding: chunked\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "HTTP/1.1 101 switching protocols\r\n"
+                        + "Transfer-Encoding: chunked\r\n"
+                        + "\r\n");
 
-        HttpParser.ResponseHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.ResponseHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("HTTP/1.1", _methodOrVersion);
-        assertEquals("101", _uriOrStatus);
-        assertEquals("switching protocols", _versionOrReason);
-        assertEquals(null,_content);
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
+        Assert.assertEquals("HTTP/1.1", _methodOrVersion);
+        Assert.assertEquals("101", _uriOrStatus);
+        Assert.assertEquals("switching protocols", _versionOrReason);
+        Assert.assertEquals(null, _content);
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
     }
-    
+
     @Test
     public void testSeekEOF() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "HTTP/1.1 200 OK\015\012"
-                        + "Content-Length: 0\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012"
-                        + "\015\012" // extra CRLF ignored
-                        + "HTTP/1.1 400 OK\015\012");  // extra data causes close ??
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "HTTP/1.1 200 OK\r\n"
+                        + "Content-Length: 0\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n"
+                        + "\r\n" // extra CRLF ignored
+                        + "HTTP/1.1 400 OK\r\n");  // extra data causes close ??
 
-
-        HttpParser.ResponseHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.ResponseHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
 
         parser.parseNext(buffer);
-        assertEquals("HTTP/1.1", _methodOrVersion);
-        assertEquals("200", _uriOrStatus);
-        assertEquals("OK", _versionOrReason);
-        assertEquals(null,_content);
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
+        Assert.assertEquals("HTTP/1.1", _methodOrVersion);
+        Assert.assertEquals("200", _uriOrStatus);
+        Assert.assertEquals("OK", _versionOrReason);
+        Assert.assertEquals(null, _content);
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
 
+        parser.close();
         parser.reset();
         parser.parseNext(buffer);
-        assertFalse(buffer.hasRemaining());
-        assertTrue(parser.isClosed());
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
     }
 
     @Test
     public void testNoURI() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET\015\012"
-                        + "Content-Length: 0\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET\r\n"
+                        + "Content-Length: 0\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
 
         parser.parseNext(buffer);
-        assertEquals(null,_methodOrVersion);
-        assertEquals("No URI",_bad);
-        assertFalse(buffer.hasRemaining());
-        assertEquals(HttpParser.State.CLOSED,parser.getState());
+        Assert.assertEquals(null, _methodOrVersion);
+        Assert.assertEquals("No URI", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
     }
 
     @Test
     public void testNoURI2() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET \015\012"
-                        + "Content-Length: 0\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET \r\n"
+                        + "Content-Length: 0\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
 
         parser.parseNext(buffer);
-        assertEquals(null,_methodOrVersion);
-        assertEquals("No URI",_bad);
-        assertFalse(buffer.hasRemaining());
-        assertEquals(HttpParser.State.CLOSED,parser.getState());
+        Assert.assertEquals(null, _methodOrVersion);
+        Assert.assertEquals("No URI", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
     }
 
     @Test
     public void testUnknownReponseVersion() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "HPPT/7.7 200 OK\015\012"
-                        + "Content-Length: 0\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "HPPT/7.7 200 OK\r\n"
+                        + "Content-Length: 0\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.ResponseHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.ResponseHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
 
         parser.parseNext(buffer);
-        assertEquals(null,_methodOrVersion);
-        assertEquals("Unknown Version",_bad);
-        assertFalse(buffer.hasRemaining());
-        assertEquals(HttpParser.State.CLOSED,parser.getState());
+        Assert.assertEquals(null, _methodOrVersion);
+        Assert.assertEquals("Unknown Version", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
+
     }
 
     @Test
     public void testNoStatus() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "HTTP/1.1\015\012"
-                        + "Content-Length: 0\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "HTTP/1.1\r\n"
+                        + "Content-Length: 0\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.ResponseHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.ResponseHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
 
         parser.parseNext(buffer);
-        assertEquals(null,_methodOrVersion);
-        assertEquals("No Status",_bad);
-        assertFalse(buffer.hasRemaining());
-        assertEquals(HttpParser.State.CLOSED,parser.getState());
+        Assert.assertEquals(null, _methodOrVersion);
+        Assert.assertEquals("No Status", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
     }
 
     @Test
     public void testNoStatus2() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "HTTP/1.1 \015\012"
-                        + "Content-Length: 0\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "HTTP/1.1 \r\n"
+                        + "Content-Length: 0\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        
+        HttpParser.ResponseHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+
         parser.parseNext(buffer);
-        assertEquals(null,_methodOrVersion);
-        assertEquals("No Status",_bad);
-        assertFalse(buffer.hasRemaining());
-        assertEquals(HttpParser.State.CLOSED,parser.getState());
+        Assert.assertEquals(null, _methodOrVersion);
+        Assert.assertEquals("No Status", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
     }
 
     @Test
     public void testBadRequestVersion() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HPPT/7.7\015\012"
-                        + "Content-Length: 0\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HPPT/7.7\r\n"
+                        + "Content-Length: 0\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
 
         parser.parseNext(buffer);
-        assertEquals(null,_methodOrVersion);
-        assertEquals("Unknown Version",_bad);
-        assertFalse(buffer.hasRemaining());
-        assertEquals(HttpParser.State.CLOSED,parser.getState()); 
-        
-        buffer= BufferUtil.toBuffer(
-            "GET / HTTP/1.01\015\012"
-                + "Content-Length: 0\015\012"
-                + "Connection: close\015\012"
-                + "\015\012");
+        Assert.assertEquals(null, _methodOrVersion);
+        Assert.assertEquals("Unknown Version", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
+
+        buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.01\r\n"
+                        + "Content-Length: 0\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
         handler = new Handler();
-        parser= new HttpParser(handler);
+        parser = new HttpParser(handler);
 
         parser.parseNext(buffer);
-        assertEquals(null,_methodOrVersion);
-        assertEquals("Unknown Version",_bad);
-        assertFalse(buffer.hasRemaining());
-        assertEquals(HttpParser.State.CLOSED,parser.getState());
+        Assert.assertEquals(null, _methodOrVersion);
+        Assert.assertEquals("Unknown Version", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
     }
-    
+
     @Test
     public void testBadCR() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
+        ByteBuffer buffer = BufferUtil.toBuffer(
                 "GET / HTTP/1.0\r\n"
                         + "Content-Length: 0\r"
                         + "Connection: close\r"
                         + "\r");
 
-        HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
 
         parser.parseNext(buffer);
-        assertEquals("Bad EOL",_bad);
-        assertFalse(buffer.hasRemaining());
-        assertEquals(HttpParser.State.CLOSED,parser.getState());
+        Assert.assertEquals("Bad EOL", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
 
-
-        buffer= BufferUtil.toBuffer(
-            "GET / HTTP/1.0\r"
-                + "Content-Length: 0\r"
-                + "Connection: close\r"
-                + "\r");
+        buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r"
+                        + "Content-Length: 0\r"
+                        + "Connection: close\r"
+                        + "\r");
 
         handler = new Handler();
-        parser= new HttpParser(handler);
+        parser = new HttpParser(handler);
 
         parser.parseNext(buffer);
-        assertEquals("Bad EOL",_bad);
-        assertFalse(buffer.hasRemaining());
-        assertEquals(HttpParser.State.CLOSED,parser.getState());
+        Assert.assertEquals("Bad EOL", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
     }
 
     @Test
     public void testBadContentLength0() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.0\015\012"
-                        + "Content-Length: abc\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n"
+                        + "Content-Length: abc\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
 
         parser.parseNext(buffer);
-        assertEquals("GET",_methodOrVersion);
-        assertEquals("Bad Content-Length",_bad);
-        assertFalse(buffer.hasRemaining());
-        assertEquals(HttpParser.State.CLOSED,parser.getState());
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("Invalid Content-Length Value", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
     }
 
     @Test
     public void testBadContentLength1() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.0\015\012"
-                        + "Content-Length: 9999999999999999999999999999999999999999999999\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n"
+                        + "Content-Length: 9999999999999999999999999999999999999999999999\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
 
         parser.parseNext(buffer);
-        assertEquals("GET",_methodOrVersion);
-        assertEquals("Bad Content-Length",_bad);
-        assertFalse(buffer.hasRemaining());
-        assertEquals(HttpParser.State.CLOSED,parser.getState());
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("Invalid Content-Length Value", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
     }
 
     @Test
     public void testBadContentLength2() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.0\015\012"
-                        + "Content-Length: 1.5\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.0\r\n"
+                        + "Content-Length: 1.5\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
 
         parser.parseNext(buffer);
-        assertEquals("GET",_methodOrVersion);
-        assertEquals("Bad Content-Length",_bad);
-        assertFalse(buffer.hasRemaining());
-        assertEquals(HttpParser.State.CLOSED,parser.getState());
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("Invalid Content-Length Value", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
     }
-    
+
+    @Test
+    public void testDuplicateContentLengthWithLargerThenCorrectValue()
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "POST / HTTP/1.1\r\n"
+                        + "Content-Length: 2\r\n"
+                        + "Content-Length: 1\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n"
+                        + "X");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+
+        parser.parseNext(buffer);
+        Assert.assertEquals("POST", _methodOrVersion);
+        Assert.assertEquals("Duplicate Content-Length", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
+    }
+
+    @Test
+    public void testDuplicateContentLengthWithCorrectThenLargerValue()
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "POST / HTTP/1.1\r\n"
+                        + "Content-Length: 1\r\n"
+                        + "Content-Length: 2\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n"
+                        + "X");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+
+        parser.parseNext(buffer);
+        Assert.assertEquals("POST", _methodOrVersion);
+        Assert.assertEquals("Duplicate Content-Length", _bad);
+        Assert.assertFalse(buffer.hasRemaining());
+        Assert.assertEquals(HttpParser.State.CLOSE, parser.getState());
+        parser.atEOF();
+        parser.parseNext(BufferUtil.EMPTY_BUFFER);
+        Assert.assertEquals(HttpParser.State.CLOSED, parser.getState());
+    }
+
+    @Test
+    public void testTransferEncodingChunkedThenContentLength()
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "POST /chunk HTTP/1.1\r\n"
+                        + "Host: localhost\r\n"
+                        + "Transfer-Encoding: chunked\r\n"
+                        + "Content-Length: 1\r\n"
+                        + "\r\n"
+                        + "1\r\n"
+                        + "X\r\n"
+                        + "0\r\n"
+                        + "\r\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+
+        Assert.assertEquals("POST", _methodOrVersion);
+        Assert.assertEquals("/chunk", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.1", _versionOrReason);
+        Assert.assertEquals("X", _content);
+
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
+    }
+
+    @Test
+    public void testContentLengthThenTransferEncodingChunked()
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "POST /chunk HTTP/1.1\r\n"
+                        + "Host: localhost\r\n"
+                        + "Content-Length: 1\r\n"
+                        + "Transfer-Encoding: chunked\r\n"
+                        + "\r\n"
+                        + "1\r\n"
+                        + "X\r\n"
+                        + "0\r\n"
+                        + "\r\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+
+        Assert.assertEquals("POST", _methodOrVersion);
+        Assert.assertEquals("/chunk", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.1", _versionOrReason);
+        Assert.assertEquals("X", _content);
+
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
+    }
+
     @Test
     public void testHost() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.1\015\012"
-                        + "Host: host\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.1\r\n"
+                        + "Host: host\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("host",_host);
-        assertEquals(0,_port);
+        Assert.assertEquals("host", _host);
+        Assert.assertEquals(0, _port);
     }
-    
+
     @Test
     public void testUriHost11() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET http://host/ HTTP/1.1\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET http://host/ HTTP/1.1\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("No Host",_bad);
-        assertEquals("http://host/",_uriOrStatus);
-        assertEquals(0,_port);
+        Assert.assertEquals("No Host", _bad);
+        Assert.assertEquals("http://host/", _uriOrStatus);
+        Assert.assertEquals(0, _port);
     }
-    
+
     @Test
     public void testUriHost10() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET http://host/ HTTP/1.0\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET http://host/ HTTP/1.0\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
         Assert.assertNull(_bad);
-        assertEquals("http://host/",_uriOrStatus);
-        assertEquals(0,_port);
+        Assert.assertEquals("http://host/", _uriOrStatus);
+        Assert.assertEquals(0, _port);
     }
-    
+
     @Test
     public void testNoHost() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.1\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.1\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("No Host",_bad);
+        Assert.assertEquals("No Host", _bad);
     }
-    
+
     @Test
     public void testIPHost() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.1\015\012"
-                        + "Host: 192.168.0.1\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.1\r\n"
+                        + "Host: 192.168.0.1\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("192.168.0.1",_host);
-        assertEquals(0,_port);
+        Assert.assertEquals("192.168.0.1", _host);
+        Assert.assertEquals(0, _port);
     }
-    
+
     @Test
     public void testIPv6Host() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.1\015\012"
-                        + "Host: [::1]\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.1\r\n"
+                        + "Host: [::1]\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("[::1]",_host);
-        assertEquals(0,_port);
+        Assert.assertEquals("[::1]", _host);
+        Assert.assertEquals(0, _port);
     }
-    
+
     @Test
     public void testBadIPv6Host() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.1\015\012"
-                        + "Host: [::1\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        try(StacklessLogging s = new StacklessLogging(HttpParser.class))
+        {
+            ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.1\r\n"
+                    + "Host: [::1\r\n"
+                    + "Connection: close\r\n"
+                    + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parser.parseNext(buffer);
-        assertEquals("Bad Host header",_bad);
+            HttpParser.RequestHandler handler = new Handler();
+            HttpParser parser = new HttpParser(handler);
+            parser.parseNext(buffer);
+            Assert.assertThat(_bad, Matchers.containsString("Bad"));
+        }
     }
-    
+
     @Test
     public void testHostPort() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.1\015\012"
-                        + "Host: myhost:8888\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.1\r\n"
+                        + "Host: myhost:8888\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("myhost",_host);
-        assertEquals(8888,_port);
+        Assert.assertEquals("myhost", _host);
+        Assert.assertEquals(8888, _port);
     }
-    
+
     @Test
     public void testHostBadPort() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.1\015\012"
-                        + "Host: myhost:xxx\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.1\r\n"
+                        + "Host: myhost:testBadPort\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("Bad Host header",_bad);
+        Assert.assertThat(_bad, Matchers.containsString("Bad Host"));
     }
 
     @Test
     public void testIPHostPort() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.1\015\012"
-                        + "Host: 192.168.0.1:8888\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.1\r\n"
+                        + "Host: 192.168.0.1:8888\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("192.168.0.1",_host);
-        assertEquals(8888,_port);
+        Assert.assertEquals("192.168.0.1", _host);
+        Assert.assertEquals(8888, _port);
     }
 
     @Test
     public void testIPv6HostPort() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.1\015\012"
-                        + "Host: [::1]:8888\015\012"
-                        + "Connection: close\015\012"
-                        + "\015\012");
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.1\r\n"
+                        + "Host: [::1]:8888\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
-        assertEquals("[::1]",_host);
-        assertEquals(8888,_port);
+        Assert.assertEquals("[::1]", _host);
+        Assert.assertEquals(8888, _port);
     }
 
     @Test
+    public void testEmptyHostPort() throws Exception
+    {
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.1\r\n"
+                        + "Host:\r\n"
+                        + "Connection: close\r\n"
+                        + "\r\n");
+
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parser.parseNext(buffer);
+        Assert.assertEquals(null, _host);
+        Assert.assertEquals(null, _bad);
+    }
+    @Test
     public void testCachedField() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-            "GET / HTTP/1.1\r\n"+
-            "Host: www.smh.com.au\r\n"+
-            "\r\n");
-        
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-        assertEquals("www.smh.com.au",parser.getFieldCache().get("Host: www.smh.com.au").getValue());
-        HttpField field=_fields.get(0);
-        
-        //System.err.println(parser.getFieldCache());
-        
-        buffer.position(0);
-        parseAll(parser,buffer);
-        assertTrue(field==_fields.get(0));
-    }
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "GET / HTTP/1.1\r\n" +
+                        "Host: www.smh.com.au\r\n" +
+                        "\r\n");
 
-    @Test
-    public void testProxyProtocol() throws Exception
-    {
-        ByteBuffer buffer=BufferUtil
-            .toBuffer("PROXY TCP4 107.47.45.254 10.0.1.116 27689 80\015\012"
-                +"GET / HTTP/1.1\015\012"
-                +"Host: localhost \015\012"
-                +"Connection: close\015\012"+"\015\012"+"\015\012");
-
-        Handler handler=new Handler();
-        HttpParser parser=new HttpParser((HttpParser.RequestHandler<ByteBuffer>)handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parseAll(parser, buffer);
+        Assert.assertEquals("www.smh.com.au", parser.getFieldCache().get("Host: www.smh.com.au").getValue());
+        HttpField field = _fields.get(0);
 
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/", _uriOrStatus);
-        assertEquals("HTTP/1.1", _versionOrReason);
-        assertEquals("PROXY TCP4 107.47.45.254 10.0.1.116 27689 80", _proxy);
-        assertEquals("Host", _hdr[0]);
-        assertEquals("localhost", _val[0]);
-        assertEquals("Connection", _hdr[1]);
-        assertEquals("close", _val[1]);
-        assertEquals(1, _headers);
-    }
-
-    @Test
-    public void testSplitProxyHeaderParseTest() throws Exception
-    {
-        Handler handler=new Handler();
-        HttpParser parser=new HttpParser((HttpParser.RequestHandler<ByteBuffer>)handler);
-
-        ByteBuffer buffer=BufferUtil.toBuffer("PROXY TCP4 207.47.45.254 10.0.1.116 27689 80\015\012");
-        parser.parseNext(buffer);
-
-        buffer=BufferUtil.toBuffer(
-            "GET / HTTP/1.1\015\012"
-                +"Host: localhost \015\012"
-                +"Connection: close\015\012"
-                +"\015\012"
-                +"\015\012");
-
-        parser.parseNext(buffer);
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/", _uriOrStatus);
-        assertEquals("HTTP/1.1", _versionOrReason);
-        assertEquals("PROXY TCP4 207.47.45.254 10.0.1.116 27689 80", _proxy);
-        assertEquals("Host", _hdr[0]);
-        assertEquals("localhost", _val[0]);
-        assertEquals("Connection", _hdr[1]);
-        assertEquals("close", _val[1]);
-        assertEquals(1, _headers);
-    }
-
-    @Test
-    public void testFolded() throws Exception
-    {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.0\015\012" +
-                "Host: localhost\015\012" +
-                "Connection: close\015\012" +
-                "Content-Type: application/soap+xml; charset=utf-8; \015\012"+
-                "\taction=\"xxx\" \015\012" +
-                "\015\012");
-        
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals("Host", _hdr[0]);
-        assertEquals("localhost", _val[0]);
-        assertEquals("Connection", _hdr[1]);
-        assertEquals("close", _val[1]);
-        assertEquals("Content-Type", _hdr[2]);
-        assertEquals("application/soap+xml; charset=utf-8; action=\"xxx\"", _val[2]);
-        assertEquals(2, _headers);
+        buffer.position(0);
+        parseAll(parser, buffer);
+        Assert.assertTrue(field == _fields.get(0));
     }
 
     @Test
     public void testParseRequest() throws Exception
     {
         ByteBuffer buffer = BufferUtil.toBuffer(
-                "GET / HTTP/1.1\r\n" + 
-                "Host: localhost\r\n" + 
-                "Header1: value1\r\n" + 
-                "Connection: close\r\n" + 
-                "Accept-Encoding: gzip, deflated\r\n" + 
-                "Accept: unknown\r\n" + 
-                "\r\n");
+                "GET / HTTP/1.1\r\n" +
+                        "Host: localhost\r\n" +
+                        "Header1: value1\r\n" +
+                        "Connection: close\r\n" +
+                        "Accept-Encoding: gzip, deflated\r\n" +
+                        "Accept: unknown\r\n" +
+                        "\r\n");
 
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
         parser.parseNext(buffer);
 
-        assertEquals("GET",_methodOrVersion);
-        assertEquals("/",_uriOrStatus);
-        assertEquals("HTTP/1.1",_versionOrReason);
-        assertEquals("Host",_hdr[0]);
-        assertEquals("localhost",_val[0]);
-        assertEquals("Connection",_hdr[2]);
-        assertEquals("close",_val[2]);
-        assertEquals("Accept-Encoding",_hdr[3]);
-        assertEquals("gzip, deflated",_val[3]);
-        assertEquals("Accept",_hdr[4]);
-        assertEquals("unknown",_val[4]);
+        Assert.assertEquals("GET", _methodOrVersion);
+        Assert.assertEquals("/", _uriOrStatus);
+        Assert.assertEquals("HTTP/1.1", _versionOrReason);
+        Assert.assertEquals("Host", _hdr[0]);
+        Assert.assertEquals("localhost", _val[0]);
+        Assert.assertEquals("Connection", _hdr[2]);
+        Assert.assertEquals("close", _val[2]);
+        Assert.assertEquals("Accept-Encoding", _hdr[3]);
+        Assert.assertEquals("gzip, deflated", _val[3]);
+        Assert.assertEquals("Accept", _hdr[4]);
+        Assert.assertEquals("unknown", _val[4]);
     }
 
     @Test
     public void testHTTP2Preface() throws Exception
     {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "PRI * HTTP/2.0\015\012" +
-                "\015\012" +
-                "SM\015\012"+
-                "\015\012");
-        
-        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
+        ByteBuffer buffer = BufferUtil.toBuffer(
+                "PRI * HTTP/2.0\r\n" +
+                        "\r\n" +
+                        "SM\r\n" +
+                        "\r\n");
 
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
-        assertEquals("PRI", _methodOrVersion);
-        assertEquals("*", _uriOrStatus);
-        assertEquals("HTTP/2.0", _versionOrReason);
-        assertEquals(-1, _headers);
-        assertEquals(null, _bad);
+        HttpParser.RequestHandler handler = new Handler();
+        HttpParser parser = new HttpParser(handler);
+        parseAll(parser, buffer);
+
+        Assert.assertTrue(_headerCompleted);
+        Assert.assertTrue(_messageCompleted);
+        Assert.assertEquals("PRI", _methodOrVersion);
+        Assert.assertEquals("*", _uriOrStatus);
+        Assert.assertEquals("HTTP/2.0", _versionOrReason);
+        Assert.assertEquals(-1, _headers);
+        Assert.assertEquals(null, _bad);
     }
 
     @Before
     public void init()
     {
-        _host=null;
-        _port=0;
-        _bad=null;
-        _content=null;
-        _methodOrVersion=null;
-        _uriOrStatus=null;
-        _versionOrReason=null;
-        _fields.clear();
-        _hdr=null;
-        _val=null;
-        _headers=0;
-        _early=false;
-        _headerCompleted=false;
-        _messageCompleted=false;
-        _proxy=null;
+        _bad = null;
+        _content = null;
+        _methodOrVersion = null;
+        _uriOrStatus = null;
+        _versionOrReason = null;
+        _hdr = null;
+        _val = null;
+        _headers = 0;
+        _headerCompleted = false;
+        _messageCompleted = false;
+        _complianceViolation = null;
     }
 
     private String _host;
@@ -1581,70 +1914,85 @@
     private String _methodOrVersion;
     private String _uriOrStatus;
     private String _versionOrReason;
-    private List<HttpField> _fields=new ArrayList<>();
+    private List<HttpField> _fields = new ArrayList<>();
     private String[] _hdr;
     private String[] _val;
     private int _headers;
     private boolean _early;
     private boolean _headerCompleted;
     private boolean _messageCompleted;
-    private String _proxy;
+    private String _complianceViolation;
 
-    private class Handler implements HttpParser.RequestHandler<ByteBuffer>, HttpParser.ResponseHandler<ByteBuffer>, HttpParser.ProxyHandler
+    private class Handler implements HttpParser.RequestHandler, HttpParser.ResponseHandler, HttpParser.ComplianceHandler
     {
+        private HttpFields fields;
+
         @Override
         public boolean content(ByteBuffer ref)
         {
-            if (_content==null)
-                _content="";
-            String c = BufferUtil.toString(ref,StandardCharsets.UTF_8);
-            _content= _content + c;
+            if (_content == null)
+                _content = "";
+            String c = BufferUtil.toString(ref, StandardCharsets.UTF_8);
+            _content = _content + c;
             ref.position(ref.limit());
             return false;
         }
 
         @Override
-        public boolean startRequest(HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version)
+        public boolean startRequest(String method, String uri, HttpVersion version)
         {
             _fields.clear();
-            _headers= -1;
-            _hdr= new String[10];
-            _val= new String[10];
-            _methodOrVersion= method;
-            _uriOrStatus= BufferUtil.toUTF8String(uri);
-            _versionOrReason= version==null?null:version.asString();
+            _headers = -1;
+            _hdr = new String[10];
+            _val = new String[10];
+            _methodOrVersion = method;
+            _uriOrStatus = uri;
+            _versionOrReason = version == null ? null : version.asString();
+
+            fields = new HttpFields();
             _messageCompleted = false;
             _headerCompleted = false;
-            _early=false;
+            _early = false;
             return false;
         }
 
         @Override
-        public boolean parsedHeader(HttpField field)
+        public void parsedHeader(HttpField field)
         {
             _fields.add(field);
-            _hdr[++_headers]= field.getName();
-            _val[_headers]= field.getValue();
-            return false;
-        }
+            _hdr[++_headers] = field.getName();
+            _val[_headers] = field.getValue();
 
-        @Override
-        public boolean parsedHostHeader(String host,int port)
-        {
-            _host=host;
-            _port=port;
-            return false;
+            if (field instanceof HostPortHttpField)
+            {
+                HostPortHttpField hpfield = (HostPortHttpField)field;
+                _host = hpfield.getHost();
+                _port = hpfield.getPort();
+            }
         }
 
         @Override
         public boolean headerComplete()
         {
-            _content= null;
+            _content = null;
+            String s0 = fields.toString();
+            String s1 = fields.toString();
+            if (!s0.equals(s1))
+            {
+                throw new IllegalStateException();
+            }
+
             _headerCompleted = true;
             return false;
         }
 
         @Override
+        public boolean contentComplete()
+        {
+            return false;
+        }
+
+        @Override
         public boolean messageComplete()
         {
             _messageCompleted = true;
@@ -1654,7 +2002,7 @@
         @Override
         public void badMessage(int status, String reason)
         {
-            _bad=reason==null?(""+status):reason;
+            _bad = reason == null ? ("" + status) : reason;
         }
 
         @Override
@@ -1664,8 +2012,11 @@
             _methodOrVersion = version.asString();
             _uriOrStatus = Integer.toString(status);
             _versionOrReason = reason;
-            _hdr= new String[9];
-            _val= new String[9];
+
+            fields = new HttpFields();
+            _hdr = new String[9];
+            _val = new String[9];
+
             _messageCompleted = false;
             _headerCompleted = false;
             return false;
@@ -1674,7 +2025,7 @@
         @Override
         public void earlyEOF()
         {
-            _early=true;
+            _early = true;
         }
 
         @Override
@@ -1684,9 +2035,9 @@
         }
 
         @Override
-        public void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort)
+        public void onComplianceViolation(HttpCompliance compliance, HttpCompliance required, String reason)
         {
-            _proxy="PROXY "+protocol+" "+sAddr+" "+dAddr+" "+sPort+" "+dPort;
+            _complianceViolation=reason;
         }
     }
 }
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpTester.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpTester.java
new file mode 100644
index 0000000..bc6b06c
--- /dev/null
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpTester.java
@@ -0,0 +1,549 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/**
+ * A HTTP Testing helper class.
+ * 
+ * Example usage:
+ * <pre>
+ *        try(Socket socket = new Socket("www.google.com",80))
+ *        {
+ *          HttpTester.Request request = HttpTester.newRequest();
+ *          request.setMethod("POST");
+ *          request.setURI("/search");
+ *          request.setVersion(HttpVersion.HTTP_1_0);
+ *          request.put(HttpHeader.HOST,"www.google.com");
+ *          request.put("Content-Type","application/x-www-form-urlencoded");
+ *          request.setContent("q=jetty%20server");
+ *          ByteBuffer output = request.generate();
+ *          
+ *          socket.getOutputStream().write(output.array(),output.arrayOffset()+output.position(),output.remaining());
+ *          HttpTester.Input input = HttpTester.from(socket.getInputStream());
+ *          HttpTester.Response response = HttpTester.parseResponse(input);
+ *          System.err.printf("%s %s %s%n",response.getVersion(),response.getStatus(),response.getReason());
+ *          for (HttpField field:response)
+ *              System.err.printf("%s: %s%n",field.getName(),field.getValue());
+ *          System.err.printf("%n%s%n",response.getContent());
+ *       }
+ * </pre>
+ */
+public class HttpTester
+{
+    private final static Logger LOG = Log.getLogger(HttpTester.class);
+    
+    private HttpTester()
+    {
+    }
+
+    public static Request newRequest()
+    {
+        Request r=new Request();
+        r.setMethod(HttpMethod.GET.asString());
+        r.setURI("/");
+        r.setVersion(HttpVersion.HTTP_1_1);
+        return r;
+    }
+
+    public static Request parseRequest(String request)
+    {
+        Request r=new Request();
+        HttpParser parser =new HttpParser(r);
+        parser.parseNext(BufferUtil.toBuffer(request));
+        return r;
+    }
+
+    public static Request parseRequest(ByteBuffer request)
+    {
+        Request r=new Request();
+        HttpParser parser =new HttpParser(r);
+        parser.parseNext(request);
+        return r;
+    }
+    
+    public static Response parseResponse(String response)
+    {
+        Response r=new Response();
+        HttpParser parser =new HttpParser(r);
+        parser.parseNext(BufferUtil.toBuffer(response));
+        return r;
+    }
+
+    public static Response parseResponse(ByteBuffer response)
+    {
+        Response r=new Response();
+        HttpParser parser =new HttpParser(r);
+        parser.parseNext(response);
+        return r;
+    }
+    
+    public static Response parseResponse(InputStream responseStream) throws IOException
+    {
+        ByteArrayOutputStream contentStream = new ByteArrayOutputStream();
+        IO.copy(responseStream, contentStream);
+        
+        Response r=new Response();
+        HttpParser parser =new HttpParser(r);
+        parser.parseNext(ByteBuffer.wrap(contentStream.toByteArray()));
+        return r;
+    }
+    
+    public abstract static class Input
+    {
+        boolean _eof=false;
+        HttpParser _parser;
+        ByteBuffer _buffer = BufferUtil.allocate(8192);
+        
+        public ByteBuffer getBuffer()
+        {
+            return _buffer;
+        }
+        
+        public void setHttpParser(HttpParser parser)
+        {
+            _parser=parser;
+        }
+        
+        public HttpParser getHttpParser()
+        {
+            return _parser;
+        }
+        
+        public HttpParser takeHttpParser()
+        {
+            HttpParser p=_parser;
+            _parser=null;
+            return p;
+        }
+        
+        public boolean isEOF()
+        {
+            return BufferUtil.isEmpty(_buffer) && _eof;
+        }
+        
+        public abstract int fillBuffer() throws IOException; 
+        
+    }
+
+    public static Input from(final InputStream in)
+    {
+        return new Input()
+        {
+            @Override
+            public int fillBuffer() throws IOException
+            {
+                BufferUtil.compact(_buffer);
+                int len=in.read(_buffer.array(),_buffer.arrayOffset()+_buffer.limit(),BufferUtil.space(_buffer));
+                if (len<0)
+                    _eof=true;
+                else
+                    _buffer.limit(_buffer.limit()+len);
+                return len;
+            }
+        };
+    }
+    
+    public static Input from(final ReadableByteChannel in)
+    {
+        return new Input()
+        {
+            @Override
+            public int fillBuffer() throws IOException
+            {
+                BufferUtil.compact(_buffer);
+                int pos=BufferUtil.flipToFill(_buffer);
+                int len=in.read(_buffer);
+                if (len<0)
+                    _eof=true;
+                BufferUtil.flipToFlush(_buffer,pos);
+                return len;
+            }
+        };
+    }
+    
+    public static Response parseResponse(Input in) throws IOException
+    {
+        Response r;
+        HttpParser parser=in.takeHttpParser();
+        if (parser==null)
+        {
+            r=new Response();
+            parser = new HttpParser(r);
+        }
+        else
+            r=(Response)parser.getHandler();
+        
+        parseResponse(in, parser, r);
+    
+        if(r.isComplete())
+            return r;
+    
+        in.setHttpParser(parser);
+        return null;
+    }
+    
+    public static void parseResponse(Input in, Response response) throws IOException
+    {
+        HttpParser parser = in.takeHttpParser();
+        if (parser == null)
+        {
+            parser = new HttpParser(response);
+        }
+        parseResponse(in, parser, response);
+    
+        if (!response.isComplete())
+            in.setHttpParser(parser);
+    }
+    
+    private static void parseResponse(Input in, HttpParser parser, Response r) throws IOException
+    {
+        ByteBuffer buffer = in.getBuffer();
+        
+        int len=0;
+        while(len>=0)
+        {
+            if (BufferUtil.hasContent(buffer))
+                if (parser.parseNext(buffer))
+                    break;
+            if (in.fillBuffer()<=0)
+                break;
+        }
+    }
+
+    public abstract static class Message extends HttpFields implements HttpParser.HttpHandler
+    {
+        boolean _earlyEOF;
+        boolean _complete=false;
+        ByteArrayOutputStream _content;
+        HttpVersion _version=HttpVersion.HTTP_1_0;
+
+        public boolean isComplete()
+        {
+            return _complete;
+        }
+        
+        public HttpVersion getVersion()
+        {
+            return _version;
+        }
+
+        public void setVersion(String version)
+        {
+            setVersion(HttpVersion.CACHE.get(version));
+        }
+
+        public void setVersion(HttpVersion version)
+        {
+            _version=version;
+        }
+
+        public void setContent(byte[] bytes)
+        {
+            try
+            {
+                _content=new ByteArrayOutputStream();
+                _content.write(bytes);
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public void setContent(String content)
+        {
+            try
+            {
+                _content=new ByteArrayOutputStream();
+                _content.write(StringUtil.getBytes(content));
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public void setContent(ByteBuffer content)
+        {
+            try
+            {
+                _content=new ByteArrayOutputStream();
+                _content.write(BufferUtil.toArray(content));
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public byte[] getContentBytes()
+        {
+            if (_content==null)
+                return null;
+            return _content.toByteArray();
+        }
+
+        public String getContent()
+        {
+            if (_content==null)
+                return null;
+            byte[] bytes=_content.toByteArray();
+
+            String content_type=get(HttpHeader.CONTENT_TYPE);
+            String encoding=MimeTypes.getCharsetFromContentType(content_type);
+            Charset charset=encoding==null?StandardCharsets.UTF_8:Charset.forName(encoding);
+
+            return new String(bytes,charset);
+        }
+        
+        @Override
+        public void parsedHeader(HttpField field)
+        {
+            add(field.getName(),field.getValue());
+        }
+
+        @Override
+        public boolean contentComplete()
+        {
+            return false;
+        }
+        
+        @Override
+        public boolean messageComplete()
+        {
+            _complete=true;
+            return true;
+        }
+
+        @Override
+        public boolean headerComplete()
+        {
+            _content=new ByteArrayOutputStream();
+            return false;
+        }
+
+        @Override
+        public void earlyEOF()
+        {
+            _earlyEOF = true;
+        }
+    
+        public boolean isEarlyEOF()
+        {
+            return _earlyEOF;
+        }
+    
+        @Override
+        public boolean content(ByteBuffer ref)
+        {
+            try
+            {
+                _content.write(BufferUtil.toArray(ref));
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+            return false;
+        }
+
+        @Override
+        public void badMessage(int status, String reason)
+        {
+            throw new RuntimeException(reason);
+        }
+
+        public ByteBuffer generate()
+        {
+            try
+            {
+                HttpGenerator generator = new HttpGenerator();
+                MetaData info = getInfo();
+                // System.err.println(info.getClass());
+                // System.err.println(info);
+
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                ByteBuffer header=null;
+                ByteBuffer chunk=null;
+                ByteBuffer content=_content==null?null:ByteBuffer.wrap(_content.toByteArray());
+
+
+                loop: while(!generator.isEnd())
+                {
+                    HttpGenerator.Result result =  info instanceof MetaData.Request
+                        ?generator.generateRequest((MetaData.Request)info,header,chunk,content,true)
+                        :generator.generateResponse((MetaData.Response)info,false,header,chunk,content,true);
+                    switch(result)
+                    {
+                        case NEED_HEADER:
+                            header=BufferUtil.allocate(8192);
+                            continue;
+
+                        case NEED_CHUNK:
+                            chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
+                            continue;
+
+                        case NEED_INFO:
+                            throw new IllegalStateException();
+
+                        case FLUSH:
+                            if (BufferUtil.hasContent(header))
+                            {
+                                out.write(BufferUtil.toArray(header));
+                                BufferUtil.clear(header);
+                            }
+                            if (BufferUtil.hasContent(chunk))
+                            {
+                                out.write(BufferUtil.toArray(chunk));
+                                BufferUtil.clear(chunk);
+                            }
+                            if (BufferUtil.hasContent(content))
+                            {
+                                out.write(BufferUtil.toArray(content));
+                                BufferUtil.clear(content);
+                            }
+                            break;
+
+                        case SHUTDOWN_OUT:
+                            break loop;
+                    }
+                }
+
+                return ByteBuffer.wrap(out.toByteArray());
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+
+        }
+        abstract public MetaData getInfo();
+
+        @Override
+        public int getHeaderCacheSize()
+        {
+            return 0;
+        }
+
+    }
+
+    public static class Request extends Message implements HttpParser.RequestHandler
+    {
+        private String _method;
+        private String _uri;
+
+        @Override
+        public boolean startRequest(String method, String uri, HttpVersion version)
+        {
+            _method=method;
+            _uri=uri.toString();
+            _version=version;
+            return false;
+        }
+
+        public String getMethod()
+        {
+            return _method;
+        }
+
+        public String getUri()
+        {
+            return _uri;
+        }
+
+        public void setMethod(String method)
+        {
+            _method=method;
+        }
+
+        public void setURI(String uri)
+        {
+            _uri=uri;
+        }
+
+        @Override
+        public MetaData.Request getInfo()
+        {
+            return new MetaData.Request(_method,new HttpURI(_uri),_version,this,_content==null?0:_content.size());
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("%s %s %s\n%s\n",_method,_uri,_version,super.toString());
+        }
+
+        public void setHeader(String name, String value)
+        {
+            put(name,value);
+        }
+    }
+
+    public static class Response extends Message implements HttpParser.ResponseHandler
+    {
+        private int _status;
+        private String _reason;
+
+        @Override
+        public boolean startResponse(HttpVersion version, int status, String reason)
+        {
+            _version=version;
+            _status=status;
+            _reason=reason;
+            return false;
+        }
+
+        public int getStatus()
+        {
+            return _status;
+        }
+
+        public String getReason()
+        {
+            return _reason;
+        }
+
+        @Override
+        public MetaData.Response getInfo()
+        {
+            return new MetaData.Response(_version,_status,_reason,this,_content==null?-1:_content.size());
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("%s %s %s\n%s\n",_version,_status,_reason,super.toString());
+        }
+    }
+}
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpTesterTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpTesterTest.java
new file mode 100644
index 0000000..4ecfaf2
--- /dev/null
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpTesterTest.java
@@ -0,0 +1,323 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.junit.Test;
+
+public class HttpTesterTest
+{
+
+    public void testExampleUsage() throws Exception
+    {
+        try(Socket socket = new Socket("www.google.com",80))
+        {
+            HttpTester.Request request = HttpTester.newRequest();
+            request.setMethod("POST");
+            request.setURI("/search");
+            request.setVersion(HttpVersion.HTTP_1_0);
+            request.put(HttpHeader.HOST,"www.google.com");
+            request.put("Content-Type","application/x-www-form-urlencoded");
+            request.setContent("q=jetty%20server");
+            ByteBuffer output = request.generate();
+
+            socket.getOutputStream().write(output.array(),output.arrayOffset()+output.position(),output.remaining());
+            HttpTester.Input input = HttpTester.from(socket.getInputStream());
+            HttpTester.Response response = HttpTester.parseResponse(input);
+            System.err.printf("%s %s %s%n",response.getVersion(),response.getStatus(),response.getReason());
+            for (HttpField field:response)
+                System.err.printf("%s: %s%n",field.getName(),field.getValue());
+            System.err.printf("%n%s%n",response.getContent());
+        }
+    }
+    
+    @Test
+    public void testGetRequestBuffer10()
+    {
+        HttpTester.Request request =HttpTester.parseRequest(
+            "GET /uri HTTP/1.0\r\n"+
+            "Host: localhost\r\n"+
+            "Connection: keep-alive\r\n"+
+            "\r\n"+
+            "GET /some/other/request /HTTP/1.0\r\n"+
+            "Host: localhost\r\n"+
+            "\r\n"
+        );
+        assertThat(request.getMethod(),is("GET"));
+        assertThat(request.getUri(),is("/uri"));
+        assertThat(request.getVersion(),is(HttpVersion.HTTP_1_0));
+        assertThat(request.get(HttpHeader.HOST),is("localhost"));
+        assertThat(request.getContent(),is(""));
+    }
+    
+    @Test
+    public void testGetRequestBuffer11()
+    {
+        HttpTester.Request request =HttpTester.parseRequest(
+            "GET /uri HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "\r\n"+
+            "GET /some/other/request /HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "\r\n"
+        );
+        assertThat(request.getMethod(),is("GET"));
+        assertThat(request.getUri(),is("/uri"));
+        assertThat(request.getVersion(),is(HttpVersion.HTTP_1_1));
+        assertThat(request.get(HttpHeader.HOST),is("localhost"));
+        assertThat(request.getContent(),is(""));
+    }
+
+    
+    
+    @Test
+    public void testPostRequestBuffer10()
+    {
+        HttpTester.Request request =HttpTester.parseRequest(
+            "POST /uri HTTP/1.0\r\n"+
+            "Host: localhost\r\n"+
+            "Connection: keep-alive\r\n"+
+            "Content-Length: 16\r\n"+
+            "\r\n"+
+            "0123456789ABCDEF"+
+            "\r\n"+
+            "GET /some/other/request /HTTP/1.0\r\n"+
+            "Host: localhost\r\n"+
+            "\r\n"
+        );
+        assertThat(request.getMethod(),is("POST"));
+        assertThat(request.getUri(),is("/uri"));
+        assertThat(request.getVersion(),is(HttpVersion.HTTP_1_0));
+        assertThat(request.get(HttpHeader.HOST),is("localhost"));
+        assertThat(request.getContent(),is("0123456789ABCDEF"));
+        
+    }
+    
+    @Test
+    public void testPostRequestBuffer11()
+    {
+        HttpTester.Request request =HttpTester.parseRequest(
+            "POST /uri HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "Transfer-Encoding: chunked\r\n"+
+            "\r\n"+
+            "A\r\n"+
+            "0123456789\r\n"+
+            "6\r\n"+
+            "ABCDEF\r\n"+
+            "0\r\n"+
+            "\r\n"+
+            "GET /some/other/request /HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "\r\n"
+        );
+        assertThat(request.getMethod(),is("POST"));
+        assertThat(request.getUri(),is("/uri"));
+        assertThat(request.getVersion(),is(HttpVersion.HTTP_1_1));
+        assertThat(request.get(HttpHeader.HOST),is("localhost"));
+        assertThat(request.getContent(),is("0123456789ABCDEF"));
+        
+    }
+    
+
+    @Test
+    public void testResponseEOFBuffer()
+    {
+        HttpTester.Response response =HttpTester.parseResponse(
+            "HTTP/1.1 200 OK\r\n"+
+            "Header: value\r\n"+
+            "Connection: close\r\n"+
+            "\r\n"+
+            "0123456789ABCDEF"
+        );
+
+        assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
+        assertThat(response.getStatus(),is(200));
+        assertThat(response.getReason(),is("OK"));
+        assertThat(response.get("Header"),is("value"));
+        assertThat(response.getContent(),is("0123456789ABCDEF")); 
+    }
+    
+    @Test
+    public void testResponseLengthBuffer()
+    {
+        HttpTester.Response response =HttpTester.parseResponse(
+            "HTTP/1.1 200 OK\r\n"+
+            "Header: value\r\n"+
+            "Content-Length: 16\r\n"+
+            "\r\n"+
+            "0123456789ABCDEF"+
+            "HTTP/1.1 200 OK\r\n"+
+            "\r\n"
+        );
+
+        assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
+        assertThat(response.getStatus(),is(200));
+        assertThat(response.getReason(),is("OK"));
+        assertThat(response.get("Header"),is("value"));
+        assertThat(response.getContent(),is("0123456789ABCDEF")); 
+    }
+    
+    @Test
+    public void testResponseChunkedBuffer()
+    {
+        HttpTester.Response response =HttpTester.parseResponse(
+            "HTTP/1.1 200 OK\r\n"+
+            "Header: value\r\n"+
+            "Transfer-Encoding: chunked\r\n"+
+            "\r\n"+
+            "A\r\n"+
+            "0123456789\r\n"+
+            "6\r\n"+
+            "ABCDEF\r\n"+
+            "0\r\n"+
+            "\r\n"+
+            "HTTP/1.1 200 OK\r\n"+
+            "\r\n"
+        );
+
+        assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
+        assertThat(response.getStatus(),is(200));
+        assertThat(response.getReason(),is("OK"));
+        assertThat(response.get("Header"),is("value"));
+        assertThat(response.getContent(),is("0123456789ABCDEF")); 
+    }
+
+    @Test
+    public void testResponsesInput() throws Exception
+    {
+        ByteArrayInputStream stream = new ByteArrayInputStream((
+            "HTTP/1.1 200 OK\r\n"+
+            "Header: value\r\n"+
+            "Transfer-Encoding: chunked\r\n"+
+            "\r\n"+
+            "A\r\n"+
+            "0123456789\r\n"+
+            "6\r\n"+
+            "ABCDEF\r\n"+
+            "0\r\n"+
+            "\r\n"+
+            "HTTP/1.1 400 OK\r\n"+
+            "Next: response\r\n"+
+            "Content-Length: 16\r\n"+
+            "\r\n"+
+            "0123456789ABCDEF").getBytes(StandardCharsets.ISO_8859_1)
+        );
+
+        HttpTester.Input in = HttpTester.from(stream);
+        
+        HttpTester.Response response = HttpTester.parseResponse(in);
+        
+        assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
+        assertThat(response.getStatus(),is(200));
+        assertThat(response.getReason(),is("OK"));
+        assertThat(response.get("Header"),is("value"));
+        assertThat(response.getContent(),is("0123456789ABCDEF")); 
+        
+        response = HttpTester.parseResponse(in);
+        assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
+        assertThat(response.getStatus(),is(400));
+        assertThat(response.getReason(),is("OK"));
+        assertThat(response.get("Next"),is("response"));
+        assertThat(response.getContent(),is("0123456789ABCDEF"));    
+    }
+    
+    @Test
+    public void testResponsesSplitInput() throws Exception
+    {
+        PipedOutputStream src = new PipedOutputStream();
+        PipedInputStream stream = new PipedInputStream(src)
+        {
+            @Override
+            public synchronized int read(byte[] b, int off, int len) throws IOException
+            {
+                if (available()==0)
+                    return 0;
+                return super.read(b,off,len);
+            }
+        };
+        
+        HttpTester.Input in = HttpTester.from(stream);
+        
+        src.write((
+            "HTTP/1.1 200 OK\r\n"+
+            "Header: value\r\n"+
+            "Transfer-Encoding: chunked\r\n"+
+            "\r\n"+
+            "A\r\n"+
+            "0123456789\r\n"+
+            "6\r\n"+
+            "ABC"
+            ).getBytes(StandardCharsets.ISO_8859_1)
+        );
+
+        HttpTester.Response response = HttpTester.parseResponse(in);
+        assertThat(response,nullValue());
+        src.write((
+            "DEF\r\n"+
+            "0\r\n"+
+            "\r\n"+
+            "HTTP/1.1 400 OK\r\n"+
+            "Next: response\r\n"+
+            "Content-Length: 16\r\n"+
+            "\r\n"+
+            "0123456789"
+            ).getBytes(StandardCharsets.ISO_8859_1)
+        );
+        
+        
+        response = HttpTester.parseResponse(in);
+        assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
+        assertThat(response.getStatus(),is(200));
+        assertThat(response.getReason(),is("OK"));
+        assertThat(response.get("Header"),is("value"));
+        assertThat(response.getContent(),is("0123456789ABCDEF")); 
+        
+        response = HttpTester.parseResponse(in);
+        assertThat(response,nullValue());
+        
+        src.write((
+            "ABCDEF"
+            ).getBytes(StandardCharsets.ISO_8859_1)
+        );
+        
+        response = HttpTester.parseResponse(in);
+        assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
+        assertThat(response.getStatus(),is(400));
+        assertThat(response.getReason(),is("OK"));
+        assertThat(response.get("Next"),is("response"));
+        assertThat(response.getContent(),is("0123456789ABCDEF"));    
+    }
+    
+    
+    
+    
+
+}
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURIParseTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURIParseTest.java
new file mode 100644
index 0000000..3f58f68
--- /dev/null
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURIParseTest.java
@@ -0,0 +1,285 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeNotNull;
+
+
+@RunWith(Parameterized.class)
+public class HttpURIParseTest
+{
+    @Parameters(name="{0}")
+    public static List<String[]> data()
+    {
+        String[][] tests = {
+
+        // Nothing but path 
+        {"path",null,null,"-1","path",null,null,null},
+        {"path/path",null,null,"-1","path/path",null,null,null},
+        {"%65ncoded/path",null,null,"-1","%65ncoded/path",null,null,null},
+                
+        // Basic path reference     
+        {"/path/to/context",null,null,"-1","/path/to/context",null,null,null},
+        
+        // Basic with encoded query 
+        {"http://example.com/path/to/context;param?query=%22value%22#fragment","http","example.com","-1","/path/to/context;param","param","query=%22value%22","fragment"},
+        {"http://[::1]/path/to/context;param?query=%22value%22#fragment","http","[::1]","-1","/path/to/context;param","param","query=%22value%22","fragment"},
+        
+        // Basic with parameters and query
+        {"http://example.com:8080/path/to/context;param?query=%22value%22#fragment","http","example.com","8080","/path/to/context;param","param","query=%22value%22","fragment"},
+        {"http://[::1]:8080/path/to/context;param?query=%22value%22#fragment","http","[::1]","8080","/path/to/context;param","param","query=%22value%22","fragment"},
+        
+        // Path References
+        {"/path/info",null,null,null,"/path/info",null,null,null},
+        {"/path/info#fragment",null,null,null,"/path/info",null,null,"fragment"},
+        {"/path/info?query",null,null,null,"/path/info",null,"query",null},
+        {"/path/info?query#fragment",null,null,null,"/path/info",null,"query","fragment"},
+        {"/path/info;param",null,null,null,"/path/info;param","param",null,null},
+        {"/path/info;param#fragment",null,null,null,"/path/info;param","param",null,"fragment"},
+        {"/path/info;param?query",null,null,null,"/path/info;param","param","query",null},
+        {"/path/info;param?query#fragment",null,null,null,"/path/info;param","param","query","fragment"},
+        // FIXME: {"/path/info;a=b/foo;c=d",null,null,null,"/data/info;a=b/foo;c=d","foo=bar1",null,null},
+
+        // Protocol Less (aka scheme-less) URIs
+        {"//host/path/info",null,"host",null,"/path/info",null,null,null},
+        {"//user@host/path/info",null,"host",null,"/path/info",null,null,null},
+        {"//user@host:8080/path/info",null,"host","8080","/path/info",null,null,null},
+        {"//host:8080/path/info",null,"host","8080","/path/info",null,null,null},
+        
+        // Host Less 
+        {"http:/path/info","http",null,null,"/path/info",null,null,null},
+        {"http:/path/info#fragment","http",null,null,"/path/info",null,null,"fragment"},
+        {"http:/path/info?query","http",null,null,"/path/info",null,"query",null},
+        {"http:/path/info?query#fragment","http",null,null,"/path/info",null,"query","fragment"},
+        {"http:/path/info;param","http",null,null,"/path/info;param","param",null,null},
+        {"http:/path/info;param#fragment","http",null,null,"/path/info;param","param",null,"fragment"},
+        {"http:/path/info;param?query","http",null,null,"/path/info;param","param","query",null},
+        {"http:/path/info;param?query#fragment","http",null,null,"/path/info;param","param","query","fragment"},
+        
+        // Everything and the kitchen sink
+        {"http://user@host:8080/path/info;param?query#fragment","http","host","8080","/path/info;param","param","query","fragment"},
+        {"xxxxx://user@host:8080/path/info;param?query#fragment","xxxxx","host","8080","/path/info;param","param","query","fragment"},
+        
+        // No host, parameter with no content
+        {"http:///;?#","http",null,null,"/;","","",""},
+        
+        // Path with query that has no value
+        {"/path/info?a=?query",null,null,null,"/path/info",null,"a=?query",null},
+        
+        // Path with query alt syntax
+        {"/path/info?a=;query",null,null,null,"/path/info",null,"a=;query",null},
+
+        // URI with host character
+        {"/@path/info",null,null,null,"/@path/info",null,null,null},
+        {"/user@path/info",null,null,null,"/user@path/info",null,null,null},
+        {"//user@host/info",null,"host",null,"/info",null,null,null},
+        {"//@host/info",null,"host",null,"/info",null,null,null},
+        {"@host/info",null,null,null,"@host/info",null,null,null},
+        
+        // Scheme-less, with host and port (overlapping with path)
+        {"//host:8080//",null,"host","8080","//",null,null,null},
+        
+        // File reference
+        {"file:///path/info","file",null,null,"/path/info",null,null,null},
+        {"file:/path/info","file",null,null,"/path/info",null,null,null},
+        
+        // Bad URI (no scheme, no host, no path) 
+        {"//",null,null,null,null,null,null,null},
+        
+        // Simple localhost references
+        {"http://localhost/","http","localhost",null,"/",null,null,null},
+        {"http://localhost:8080/", "http", "localhost","8080","/", null, null,null},
+        {"http://localhost/?x=y", "http", "localhost",null,"/", null,"x=y",null},
+        
+        // Simple path with parameter 
+        {"/;param",null, null,null,"/;param", "param",null,null},
+        {";param",null, null,null,";param", "param",null,null},
+        
+        // Simple path with query
+        {"/?x=y",null, null,null,"/", null,"x=y",null},
+        {"/?abc=test",null, null,null,"/", null,"abc=test",null},
+        
+        // Simple path with fragment
+        {"/#fragment",null, null,null,"/", null,null,"fragment"},
+        
+        // Simple IPv4 host with port (default path)
+        {"http://192.0.0.1:8080/","http","192.0.0.1","8080","/",null,null,null},
+        
+        // Simple IPv6 host with port (default path)
+        
+        {"http://[2001:db8::1]:8080/","http","[2001:db8::1]","8080","/",null,null,null},
+        // IPv6 authenticated host with port (default path)
+        
+        {"http://user@[2001:db8::1]:8080/","http","[2001:db8::1]","8080","/",null,null,null},
+        
+        // Simple IPv6 host no port (default path)
+        {"http://[2001:db8::1]/","http","[2001:db8::1]",null,"/",null,null,null},
+        
+        // Scheme-less IPv6, host with port (default path)
+        {"//[2001:db8::1]:8080/",null,"[2001:db8::1]","8080","/",null,null,null},
+        
+        // Interpreted as relative path of "*" (no host/port/scheme/query/fragment)
+        {"*",null,null,null,"*",null, null,null},
+
+        // Path detection Tests (seen from JSP/JSTL and <c:url> use)
+        {"http://host:8080/path/info?q1=v1&q2=v2","http","host","8080","/path/info",null,"q1=v1&q2=v2",null},
+        {"/path/info?q1=v1&q2=v2",null,null,null,"/path/info",null,"q1=v1&q2=v2",null},
+        {"/info?q1=v1&q2=v2",null,null,null,"/info",null,"q1=v1&q2=v2",null},
+        {"info?q1=v1&q2=v2",null,null,null,"info",null,"q1=v1&q2=v2",null},
+        {"info;q1=v1?q2=v2",null,null,null,"info;q1=v1","q1=v1","q2=v2",null},
+        
+        // Path-less, query only (seen from JSP/JSTL and <c:url> use)
+        {"?q1=v1&q2=v2",null,null,null,"",null,"q1=v1&q2=v2",null}
+        };
+        
+        return Arrays.asList(tests);
+    }
+    
+    @Parameter(0)
+    public String input;
+
+    @Parameter(1)
+    public String scheme;
+    
+    @Parameter(2)
+    public String host;
+    
+    @Parameter(3)
+    public String port;
+
+    @Parameter(4)
+    public String path;
+    
+    @Parameter(5)
+    public String param;
+    
+    @Parameter(6)
+    public String query;
+    
+    @Parameter(7)
+    public String fragment;
+    
+    @Test
+    public void testParseString() throws Exception
+    {
+        HttpURI httpUri = new HttpURI(input);
+        
+        try
+        {
+            new URI(input);
+            // URI is valid (per java.net.URI parsing)
+            
+            // Test case sanity check
+            assertThat("[" + input + "] expected path (test case) cannot be null",path,notNullValue());
+
+            // Assert expectations
+            assertThat("[" + input + "] .scheme",httpUri.getScheme(),is(scheme));
+            assertThat("[" + input + "] .host",httpUri.getHost(),is(host));
+            assertThat("[" + input + "] .port",httpUri.getPort(),is(port == null ? -1 : Integer.parseInt(port)));
+            assertThat("[" + input + "] .path",httpUri.getPath(),is(path));
+            assertThat("[" + input + "] .param",httpUri.getParam(),is(param));
+            assertThat("[" + input + "] .query",httpUri.getQuery(),is(query));
+            assertThat("[" + input + "] .fragment",httpUri.getFragment(),is(fragment));
+            assertThat("[" + input + "] .toString",httpUri.toString(),is(input));
+        }
+        catch (URISyntaxException e)
+        {
+            // Assert HttpURI values for invalid URI (such as "//")
+            assertThat("[" + input + "] .scheme",httpUri.getScheme(),is(nullValue()));
+            assertThat("[" + input + "] .host",httpUri.getHost(),is(nullValue()));
+            assertThat("[" + input + "] .port",httpUri.getPort(),is(-1));
+            assertThat("[" + input + "] .path",httpUri.getPath(),is(nullValue()));
+            assertThat("[" + input + "] .param",httpUri.getParam(),is(nullValue()));
+            assertThat("[" + input + "] .query",httpUri.getQuery(),is(nullValue()));
+            assertThat("[" + input + "] .fragment",httpUri.getFragment(),is(nullValue()));
+        }
+    }
+    
+    @Test
+    public void testParseURI() throws Exception
+    {
+        URI javaUri = null;
+        try
+        {
+            javaUri = new URI(input);
+            assumeNotNull(javaUri);
+        }
+        catch (URISyntaxException e)
+        {
+            // Ignore, as URI is invalid anyway
+            assumeNoException(e);
+        }
+        
+        HttpURI httpUri = new HttpURI(javaUri);
+
+        assertThat("[" + input + "] .scheme",httpUri.getScheme(),is(scheme));
+        assertThat("[" + input + "] .host",httpUri.getHost(),is(host));
+        assertThat("[" + input + "] .port",httpUri.getPort(),is(port == null ? -1 : Integer.parseInt(port)));
+        assertThat("[" + input + "] .path",httpUri.getPath(),is(path));
+        assertThat("[" + input + "] .param",httpUri.getParam(),is(param));
+        assertThat("[" + input + "] .query",httpUri.getQuery(),is(query));
+        assertThat("[" + input + "] .fragment",httpUri.getFragment(),is(fragment));
+        
+        assertThat("[" + input + "] .toString",httpUri.toString(),is(input));
+    }
+    
+    @Test
+    public void testCompareToJavaNetURI() throws Exception
+    {
+        URI javaUri = null;
+        try
+        {
+            javaUri = new URI(input);
+            assumeNotNull(javaUri);
+        }
+        catch (URISyntaxException e)
+        {
+            // Ignore, as URI is invalid anyway
+            assumeNoException(e);
+        }
+        
+        HttpURI httpUri = new HttpURI(javaUri);
+        
+        assertThat("[" + input + "] .scheme",httpUri.getScheme(),is(javaUri.getScheme()));
+        assertThat("[" + input + "] .host",httpUri.getHost(),is(javaUri.getHost()));
+        assertThat("[" + input + "] .port",httpUri.getPort(),is(javaUri.getPort()));
+        assertThat("[" + input + "] .path",httpUri.getPath(),is(javaUri.getRawPath()));
+        // Not Relevant for java.net.URI -- assertThat("["+input+"] .param", httpUri.getParam(), is(param));
+        assertThat("[" + input + "] .query",httpUri.getQuery(),is(javaUri.getRawQuery()));
+        assertThat("[" + input + "] .fragment",httpUri.getFragment(),is(javaUri.getFragment()));
+        assertThat("[" + input + "] .toString",httpUri.toString(),is(javaUri.toASCIIString()));
+    }
+}
\ No newline at end of file
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java
index b61c9b3..0853abf 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java
@@ -19,61 +19,207 @@
 
 package org.eclipse.jetty.http;
 
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
-import java.net.URI;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 
+import org.eclipse.jetty.util.MultiMap;
 import org.junit.Test;
 
-
-/* ------------------------------------------------------------ */
 public class HttpURITest
 {
-    String[][] tests=
-    {
-        {"/path/to/context",null,null,"-1","/path/to/context",null,null,null},
-        {"http://example.com/path/to/context;param?query=%22value%22#fragment","http","example.com","-1","/path/to/context","param","query=%22value%22","fragment"},
-        {"http://[::1]/path/to/context;param?query=%22value%22#fragment","http","[::1]","-1","/path/to/context","param","query=%22value%22","fragment"},
-        {"http://example.com:8080/path/to/context;param?query=%22value%22#fragment","http","example.com","8080","/path/to/context","param","query=%22value%22","fragment"},
-        {"http://[::1]:8080/path/to/context;param?query=%22value%22#fragment","http","[::1]","8080","/path/to/context","param","query=%22value%22","fragment"},
-    };
-    
-    public static int
-    INPUT=0,SCHEME=1,HOST=2,PORT=3,PATH=4,PARAM=5,QUERY=6,FRAGMENT=7;
-
-    /* ------------------------------------------------------------ */
     @Test
-    public void testFromString() throws Exception
+    public void testInvalidAddress() throws Exception
     {
-        for (String[] test:tests)
-        {
-            HttpURI uri = new HttpURI(test[INPUT]);
+        assertInvalidURI("http://[ffff::1:8080/", "Invalid URL; no closing ']' -- should throw exception");
+        assertInvalidURI("**", "only '*', not '**'");
+        assertInvalidURI("*/", "only '*', not '*/'");
+    }
 
-            assertEquals(test[SCHEME], uri.getScheme());
-            assertEquals(test[HOST], uri.getHost());
-            assertEquals(Integer.parseInt(test[PORT]), uri.getPort());
-            assertEquals(test[PATH], uri.getPath());
-            assertEquals(test[PARAM], uri.getParam());
-            assertEquals(test[QUERY], uri.getQuery());
-            assertEquals(test[FRAGMENT], uri.getFragment());
+    private void assertInvalidURI(String invalidURI, String message)
+    {
+        HttpURI uri = new HttpURI();
+        try
+        {
+            uri.parse(invalidURI);
+            fail(message);
+        }
+        catch (IllegalArgumentException e)
+        {
+            assertTrue(true);
         }
     }
 
-    /* ------------------------------------------------------------ */
     @Test
-    public void testFromURI() throws Exception
+    public void testParse()
     {
-        for (String[] test:tests)
-        {
-            HttpURI uri = new HttpURI(new URI(test[INPUT]));
+        HttpURI uri = new HttpURI();
 
-            assertEquals(test[SCHEME], uri.getScheme());
-            assertEquals(test[HOST], uri.getHost());
-            assertEquals(Integer.parseInt(test[PORT]), uri.getPort());
-            assertEquals(test[PATH], uri.getPath());
-            assertEquals(test[PARAM], uri.getParam());
-            assertEquals(test[QUERY], uri.getQuery());
-            assertEquals(test[FRAGMENT], uri.getFragment());
+        uri.parse("*");
+        assertThat(uri.getHost(),nullValue());
+        assertThat(uri.getPath(),is("*"));
+        
+        uri.parse("/foo/bar");
+        assertThat(uri.getHost(),nullValue());
+        assertThat(uri.getPath(),is("/foo/bar"));
+        
+        uri.parse("//foo/bar");
+        assertThat(uri.getHost(),is("foo"));
+        assertThat(uri.getPath(),is("/bar"));
+        
+        uri.parse("http://foo/bar");
+        assertThat(uri.getHost(),is("foo"));
+        assertThat(uri.getPath(),is("/bar"));
+    }
+
+    @Test
+    public void testParseRequestTarget()
+    {
+        HttpURI uri = new HttpURI();
+
+        uri.parseRequestTarget("GET","*");
+        assertThat(uri.getHost(),nullValue());
+        assertThat(uri.getPath(),is("*"));
+        
+        uri.parseRequestTarget("GET","/foo/bar");
+        assertThat(uri.getHost(),nullValue());
+        assertThat(uri.getPath(),is("/foo/bar"));
+        
+        uri.parseRequestTarget("GET","//foo/bar");
+        assertThat(uri.getHost(),nullValue());
+        assertThat(uri.getPath(),is("//foo/bar"));
+        
+        uri.parseRequestTarget("GET","http://foo/bar");
+        assertThat(uri.getHost(),is("foo"));
+        assertThat(uri.getPath(),is("/bar"));
+    }
+
+    @Test
+    public void testExtB() throws Exception
+    {
+        for (String value: new String[]{"a","abcdABCD","\u00C0","\u697C","\uD869\uDED5","\uD840\uDC08"} )
+        {
+            HttpURI uri = new HttpURI("/path?value="+URLEncoder.encode(value,"UTF-8"));
+
+            MultiMap<String> parameters = new MultiMap<>();
+            uri.decodeQueryTo(parameters,StandardCharsets.UTF_8);
+            assertEquals(value,parameters.getString("value"));
         }
     }
+
+    @Test
+    public void testAt() throws Exception
+    {
+        HttpURI uri = new HttpURI("/@foo/bar");
+        assertEquals("/@foo/bar",uri.getPath());
+    }
+
+    @Test
+    public void testParams() throws Exception
+    {
+        HttpURI uri = new HttpURI("/foo/bar");
+        assertEquals("/foo/bar",uri.getPath());
+        assertEquals("/foo/bar",uri.getDecodedPath());
+        assertEquals(null,uri.getParam());
+        
+        uri = new HttpURI("/foo/bar;jsessionid=12345");
+        assertEquals("/foo/bar;jsessionid=12345",uri.getPath());
+        assertEquals("/foo/bar",uri.getDecodedPath());
+        assertEquals("jsessionid=12345",uri.getParam());
+        
+        uri = new HttpURI("/foo;abc=123/bar;jsessionid=12345");
+        assertEquals("/foo;abc=123/bar;jsessionid=12345",uri.getPath());
+        assertEquals("/foo/bar",uri.getDecodedPath());
+        assertEquals("jsessionid=12345",uri.getParam());
+        
+        uri = new HttpURI("/foo;abc=123/bar;jsessionid=12345?name=value");
+        assertEquals("/foo;abc=123/bar;jsessionid=12345",uri.getPath());
+        assertEquals("/foo/bar",uri.getDecodedPath());
+        assertEquals("jsessionid=12345",uri.getParam());
+        
+        uri = new HttpURI("/foo;abc=123/bar;jsessionid=12345#target");
+        assertEquals("/foo;abc=123/bar;jsessionid=12345",uri.getPath());
+        assertEquals("/foo/bar",uri.getDecodedPath());
+        assertEquals("jsessionid=12345",uri.getParam());
+    }
+    
+    @Test
+    public void testMutableURI()
+    {
+        HttpURI uri = new HttpURI("/foo/bar");
+        assertEquals("/foo/bar",uri.toString());
+        assertEquals("/foo/bar",uri.getPath());
+        assertEquals("/foo/bar",uri.getDecodedPath());
+
+        uri.setScheme("http");
+        assertEquals("http:/foo/bar",uri.toString());
+        assertEquals("/foo/bar",uri.getPath());
+        assertEquals("/foo/bar",uri.getDecodedPath());
+
+        uri.setAuthority("host",0);
+        assertEquals("http://host/foo/bar",uri.toString());
+        assertEquals("/foo/bar",uri.getPath());
+        assertEquals("/foo/bar",uri.getDecodedPath());
+
+        uri.setAuthority("host",8888);
+        assertEquals("http://host:8888/foo/bar",uri.toString());
+        assertEquals("/foo/bar",uri.getPath());
+        assertEquals("/foo/bar",uri.getDecodedPath());
+        
+        uri.setPathQuery("/f%30%30;p0/bar;p1;p2");
+        assertEquals("http://host:8888/f%30%30;p0/bar;p1;p2",uri.toString());
+        assertEquals("/f%30%30;p0/bar;p1;p2",uri.getPath());
+        assertEquals("/f00/bar",uri.getDecodedPath());
+        assertEquals("p2",uri.getParam());
+        assertEquals(null,uri.getQuery());
+        
+        uri.setPathQuery("/f%30%30;p0/bar;p1;p2?name=value");
+        assertEquals("http://host:8888/f%30%30;p0/bar;p1;p2?name=value",uri.toString());
+        assertEquals("/f%30%30;p0/bar;p1;p2",uri.getPath());
+        assertEquals("/f00/bar",uri.getDecodedPath());
+        assertEquals("p2",uri.getParam());
+        assertEquals("name=value",uri.getQuery());
+        
+        uri.setQuery("other=123456");
+        assertEquals("http://host:8888/f%30%30;p0/bar;p1;p2?other=123456",uri.toString());
+        assertEquals("/f%30%30;p0/bar;p1;p2",uri.getPath());
+        assertEquals("/f00/bar",uri.getDecodedPath());
+        assertEquals("p2",uri.getParam());
+        assertEquals("other=123456",uri.getQuery());
+    }
+
+    @Test
+    public void testSchemeAndOrAuthority() throws Exception
+    {
+        HttpURI uri = new HttpURI("/path/info");
+        assertEquals("/path/info",uri.toString());
+        
+        uri.setAuthority("host",0);
+        assertEquals("//host/path/info",uri.toString());
+        
+        uri.setAuthority("host",8888);
+        assertEquals("//host:8888/path/info",uri.toString());
+        
+        uri.setScheme("http");
+        assertEquals("http://host:8888/path/info",uri.toString());
+        
+        uri.setAuthority(null,0);
+        assertEquals("http:/path/info",uri.toString());
+        
+    }
+    
+    @Test
+    public void testBasicAuthCredentials() throws Exception
+    {
+        HttpURI uri = new HttpURI("http://user:password@example.com:8888/blah");
+        assertEquals("http://user:password@example.com:8888/blah", uri.toString());
+        assertEquals(uri.getAuthority(), "example.com:8888");
+        assertEquals(uri.getUser(), "user:password");
+    }
 }
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/MimeTypesTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/MimeTypesTest.java
index a997116..2a72195 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/MimeTypesTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/MimeTypesTest.java
@@ -18,9 +18,11 @@
 
 package org.eclipse.jetty.http;
 
+import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
 
 import org.junit.Test;
 
@@ -75,25 +77,34 @@
         assertNotNull(prefix,contentType);
         assertEquals(prefix,expectedMimeType,contentType);
     }
+    
+    private void assertCharsetFromContentType(String contentType, String expectedCharset)
+    {
+        assertThat("getCharsetFromContentType(\"" + contentType + "\")",
+                MimeTypes.getCharsetFromContentType(contentType), is(expectedCharset));
+    }
 
     @Test
     public void testCharsetFromContentType()
     {
-        assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar;charset=abc;some=else"));
-        assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar;charset=abc"));
-        assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar ; charset = abc"));
-        assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar ; charset = abc ; some=else"));
-        assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar;other=param;charset=abc;some=else"));
-        assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar;other=param;charset=abc"));
-        assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar other = param ; charset = abc"));
-        assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar other = param ; charset = abc ; some=else"));
-        assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar other = param ; charset = abc"));
-        assertEquals("abc",MimeTypes.getCharsetFromContentType("foo/bar other = param ; charset = \"abc\" ; some=else"));
-        assertEquals(null,MimeTypes.getCharsetFromContentType("foo/bar"));
-        assertEquals("UTF-8",MimeTypes.getCharsetFromContentType("foo/bar;charset=uTf8"));
-        assertEquals("UTF-8",MimeTypes.getCharsetFromContentType("foo/bar;other=\"charset=abc\";charset=uTf8"));
-        assertEquals("UTF-8",MimeTypes.getCharsetFromContentType("text/html;charset=utf-8"));
-
+        assertCharsetFromContentType("foo/bar;charset=abc;some=else", "abc");
+        assertCharsetFromContentType("foo/bar;charset=abc", "abc");
+        assertCharsetFromContentType("foo/bar ; charset = abc", "abc");
+        assertCharsetFromContentType("foo/bar ; charset = abc ; some=else", "abc");
+        assertCharsetFromContentType("foo/bar;other=param;charset=abc;some=else", "abc");
+        assertCharsetFromContentType("foo/bar;other=param;charset=abc", "abc");
+        assertCharsetFromContentType("foo/bar other = param ; charset = abc", "abc");
+        assertCharsetFromContentType("foo/bar other = param ; charset = abc ; some=else", "abc");
+        assertCharsetFromContentType("foo/bar other = param ; charset = abc", "abc");
+        assertCharsetFromContentType("foo/bar other = param ; charset = \"abc\" ; some=else", "abc");
+        assertCharsetFromContentType("foo/bar", null);
+        assertCharsetFromContentType("foo/bar;charset=uTf8", "utf-8");
+        assertCharsetFromContentType("foo/bar;other=\"charset=abc\";charset=uTf8", "utf-8");
+        assertCharsetFromContentType("application/pdf; charset=UTF-8", "utf-8");
+        assertCharsetFromContentType("application/pdf;; charset=UTF-8", "utf-8");
+        assertCharsetFromContentType("application/pdf;;; charset=UTF-8", "utf-8");
+        assertCharsetFromContentType("application/pdf;;;; charset=UTF-8", "utf-8");
+        assertCharsetFromContentType("text/html;charset=utf-8", "utf-8");
     }
 
     @Test
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java
index 1286097..657410f 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -146,6 +145,7 @@
 
     /**
      * See JIRA issue: JETTY-88.
+     * @throws Exception failed test
      */
     @Test
     public void testPathMappingsOnlyMatchOnDirectoryNames() throws Exception
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedCSVTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedCSVTest.java
new file mode 100644
index 0000000..7e87faa
--- /dev/null
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedCSVTest.java
@@ -0,0 +1,155 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class QuotedCSVTest
+{
+    @Test
+    public void testOWS()
+    {
+        QuotedCSV values = new QuotedCSV();
+        values.addValue("  value 0.5  ;  pqy = vwz  ;  q =0.5  ,  value 1.0 ,  other ; param ");
+        Assert.assertThat(values,Matchers.contains(
+                "value 0.5;pqy=vwz;q=0.5",
+                "value 1.0",
+                "other;param"));
+    }
+    
+    @Test
+    public void testEmpty()
+    {
+        QuotedCSV values = new QuotedCSV();
+        values.addValue(",aaaa,  , bbbb ,,cccc,");
+        Assert.assertThat(values,Matchers.contains(
+                "aaaa",
+                "bbbb",
+                "cccc"));
+    }
+        
+    @Test
+    public void testQuoted()
+    {
+        QuotedCSV values = new QuotedCSV();
+        values.addValue("A;p=\"v\",B,\"C, D\"");
+        Assert.assertThat(values,Matchers.contains(
+                "A;p=\"v\"",
+                "B",
+                "\"C, D\""));
+    }
+    
+    @Test
+    public void testOpenQuote()
+    {
+        QuotedCSV values = new QuotedCSV();
+        values.addValue("value;p=\"v");
+        Assert.assertThat(values,Matchers.contains(
+                "value;p=\"v"));
+    }
+    
+    @Test
+    public void testQuotedNoQuotes()
+    {
+        QuotedCSV values = new QuotedCSV(false);
+        values.addValue("A;p=\"v\",B,\"C, D\"");
+        Assert.assertThat(values,Matchers.contains(
+                "A;p=v",
+                "B",
+                "C, D"));
+    }
+    
+    @Test
+    public void testOpenQuoteNoQuotes()
+    {
+        QuotedCSV values = new QuotedCSV(false);
+        values.addValue("value;p=\"v");
+        assertThat(values,Matchers.contains(
+                "value;p=v"));
+    }
+
+    @Test
+    public void testParamsOnly()
+    {
+        QuotedCSV values = new QuotedCSV(false);
+        values.addValue("for=192.0.2.43, for=\"[2001:db8:cafe::17]\", for=unknown");
+        assertThat(values,Matchers.contains(
+                "for=192.0.2.43",
+                "for=[2001:db8:cafe::17]",
+                "for=unknown"));
+    }
+
+    @Test
+    public void testMutation()
+    {
+        QuotedCSV values = new QuotedCSV(false)
+        {
+
+            @Override
+            protected void parsedValue(StringBuffer buffer)
+            {
+                if (buffer.toString().contains("DELETE"))
+                {
+                    String s = buffer.toString().replace("DELETE","");
+                    buffer.setLength(0);
+                    buffer.append(s);
+                }
+                if (buffer.toString().contains("APPEND"))
+                {
+                    String s = buffer.toString().replace("APPEND","Append")+"!";
+                    buffer.setLength(0);
+                    buffer.append(s);
+                }
+            }
+
+            @Override
+            protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue)
+            {
+                String name = paramValue>0?buffer.substring(paramName,paramValue-1):buffer.substring(paramName);
+                if ("IGNORE".equals(name))
+                    buffer.setLength(paramName-1);
+            }
+            
+        };
+            
+        values.addValue("normal;param=val, testAPPENDandDELETEvalue ; n=v; IGNORE = this; x=y ");
+        assertThat(values,Matchers.contains(
+                "normal;param=val",
+                "testAppendandvalue!;n=v;x=y"));
+    }
+    
+    
+    @Test
+    public void testUnQuote()
+    {
+        assertThat(QuotedCSV.unquote(""),is(""));
+        assertThat(QuotedCSV.unquote("\"\""),is(""));
+        assertThat(QuotedCSV.unquote("foo"),is("foo"));
+        assertThat(QuotedCSV.unquote("\"foo\""),is("foo"));
+        assertThat(QuotedCSV.unquote("f\"o\"o"),is("foo"));
+        assertThat(QuotedCSV.unquote("\"\\\"foo\""),is("\"foo"));
+        assertThat(QuotedCSV.unquote("\\foo"),is("\\foo"));
+    }
+    
+}
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedQualityCSVTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedQualityCSVTest.java
new file mode 100644
index 0000000..cbd6cc0
--- /dev/null
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedQualityCSVTest.java
@@ -0,0 +1,167 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class QuotedQualityCSVTest
+{
+
+    @Test
+    public void test7231_5_3_2_example1()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue(" audio/*; q=0.2, audio/basic");
+        Assert.assertThat(values,Matchers.contains("audio/basic","audio/*"));
+    }
+
+    @Test
+    public void test7231_5_3_2_example2()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue("text/plain; q=0.5, text/html,");
+        values.addValue("text/x-dvi; q=0.8, text/x-c");
+        Assert.assertThat(values,Matchers.contains("text/html","text/x-c","text/x-dvi","text/plain"));
+    }
+    
+    @Test
+    public void test7231_5_3_2_example3()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue("text/*, text/plain, text/plain;format=flowed, */*");
+        
+        // Note this sort is only on quality and not the most specific type as per 5.3.2
+        Assert.assertThat(values,Matchers.contains("text/*","text/plain","text/plain;format=flowed","*/*"));
+    }
+    
+    @Test
+    public void test7231_5_3_2_example4()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue("text/*;q=0.3, text/html;q=0.7, text/html;level=1,");
+        values.addValue("text/html;level=2;q=0.4, */*;q=0.5");
+        Assert.assertThat(values,Matchers.contains(
+                "text/html;level=1",
+                "text/html",
+                "*/*",
+                "text/html;level=2",
+                "text/*"
+                ));
+    }
+    
+    @Test
+    public void test7231_5_3_4_example1()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue("compress, gzip");
+        values.addValue("");
+        values.addValue("*");
+        values.addValue("compress;q=0.5, gzip;q=1.0");
+        values.addValue("gzip;q=1.0, identity; q=0.5, *;q=0");
+        
+        Assert.assertThat(values,Matchers.contains(
+                "compress",
+                "gzip",
+                "*",
+                "gzip",
+                "gzip",
+                "compress",
+                "identity"
+                ));
+    }
+
+    @Test
+    public void testOWS()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue("  value 0.5  ;  p = v  ;  q =0.5  ,  value 1.0 ");
+        Assert.assertThat(values,Matchers.contains(
+                "value 1.0",
+                "value 0.5;p=v"));
+    }
+    
+    @Test
+    public void testEmpty()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue(",aaaa,  , bbbb ,,cccc,");
+        Assert.assertThat(values,Matchers.contains(
+                "aaaa",
+                "bbbb",
+                "cccc"));
+    }
+        
+    @Test
+    public void testQuoted()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue("  value 0.5  ;  p = \"v  ;  q = \\\"0.5\\\"  ,  value 1.0 \"  ");
+        Assert.assertThat(values,Matchers.contains(
+                "value 0.5;p=\"v  ;  q = \\\"0.5\\\"  ,  value 1.0 \""));
+    }
+    
+    @Test
+    public void testOpenQuote()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue("value;p=\"v");
+        Assert.assertThat(values,Matchers.contains(
+                "value;p=\"v"));
+    }
+    
+    @Test
+    public void testQuotedQuality()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue("  value 0.5  ;  p = v  ;  q = \"0.5\"  ,  value 1.0 ");
+        Assert.assertThat(values,Matchers.contains(
+                "value 1.0",
+                "value 0.5;p=v"));
+    }
+    
+    @Test
+    public void testBadQuality()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue("value0.5;p=v;q=0.5,value1.0,valueBad;q=X");
+        Assert.assertThat(values,Matchers.contains(
+                "value1.0",
+                "value0.5;p=v"));
+    }
+    
+
+    @Test
+    public void testSameQuality()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue("one;q=0.5,two;q=0.5,three;q=0.5");
+        Assert.assertThat(values.getValues(),Matchers.contains("one","two","three"));
+    }
+
+    @Test
+    public void testNoQuality()
+    {
+        QuotedQualityCSV values = new QuotedQualityCSV();
+        values.addValue("one,two;,three;x=y");
+        Assert.assertThat(values.getValues(),Matchers.contains("one","two","three;x=y"));
+    }
+    
+}
diff --git a/jetty-http/src/test/resources/jetty-logging.properties b/jetty-http/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..adf68c7
--- /dev/null
+++ b/jetty-http/src/test/resources/jetty-logging.properties
@@ -0,0 +1,3 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.LEVEL=DEBUG
+#org.eclipse.jetty.server.LEVEL=DEBUG
diff --git a/jetty-http2/http2-alpn-tests/pom.xml b/jetty-http2/http2-alpn-tests/pom.xml
new file mode 100644
index 0000000..60078c8
--- /dev/null
+++ b/jetty-http2/http2-alpn-tests/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.eclipse.jetty.http2</groupId>
+        <artifactId>http2-parent</artifactId>
+        <version>9.3.19-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>http2-alpn-tests</artifactId>
+    <name>Jetty :: HTTP2 :: ALPN Tests</name>
+
+    <properties>
+        <bundle-symbolic-name>${project.groupId}.alpn.tests</bundle-symbolic-name>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>org.mortbay.jetty.alpn</groupId>
+                                    <artifactId>alpn-boot</artifactId>
+                                    <version>${alpn.version}</version>
+                                    <type>jar</type>
+                                    <overWrite>false</overWrite>
+                                    <outputDirectory>${project.build.directory}/alpn</outputDirectory>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <argLine>-Xbootclasspath/p:${project.build.directory}/alpn/alpn-boot-${alpn.version}.jar</argLine>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.eclipse.jetty.alpn</groupId>
+            <artifactId>alpn-api</artifactId>
+            <version>${alpn.api.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-alpn-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.http2</groupId>
+            <artifactId>http2-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.toolchain</groupId>
+            <artifactId>jetty-test-helper</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    
+</project>
diff --git a/jetty-http2/http2-alpn-tests/src/test/java/org/eclipse/jetty/http2/alpn/tests/ALPNNegotiationTest.java b/jetty-http2/http2-alpn-tests/src/test/java/org/eclipse/jetty/http2/alpn/tests/ALPNNegotiationTest.java
new file mode 100644
index 0000000..21e2f48
--- /dev/null
+++ b/jetty-http2/http2-alpn-tests/src/test/java/org/eclipse/jetty/http2/alpn/tests/ALPNNegotiationTest.java
@@ -0,0 +1,324 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.alpn.tests;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSocket;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ALPNNegotiationTest extends AbstractALPNTest
+{
+    @Test
+    public void testGentleCloseDuringHandshake() throws Exception
+    {
+        InetSocketAddress address = prepare();
+        SslContextFactory sslContextFactory = newSslContextFactory();
+        sslContextFactory.start();
+        SSLEngine sslEngine = sslContextFactory.newSSLEngine(address);
+        sslEngine.setUseClientMode(true);
+        ALPN.put(sslEngine, new ALPN.ClientProvider()
+        {
+            @Override
+            public void unsupported()
+            {
+            }
+
+            @Override
+            public List<String> protocols()
+            {
+                return Arrays.asList("h2");
+            }
+
+            @Override
+            public void selected(String protocol)
+            {
+            }
+        });
+        sslEngine.beginHandshake();
+
+        ByteBuffer encrypted = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
+        sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
+        encrypted.flip();
+
+        try (SocketChannel channel = SocketChannel.open(address))
+        {
+            // Send ClientHello, immediately followed by TLS Close Alert and then by FIN
+            channel.write(encrypted);
+            sslEngine.closeOutbound();
+            encrypted.clear();
+            sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
+            encrypted.flip();
+            channel.write(encrypted);
+            channel.shutdownOutput();
+
+            // Read ServerHello from server
+            encrypted.clear();
+            int read = channel.read(encrypted);
+            encrypted.flip();
+            Assert.assertTrue(read > 0);
+            // Cannot decrypt, as the SSLEngine has been already closed
+
+            // It may happen that the read() above read both the ServerHello and the TLS Close Alert.
+            // Now if we can read more, we should read the TLS Close Alert and then the TCP FIN.
+            encrypted.clear();
+            read = channel.read(encrypted);
+            if (read > 0)
+            {
+                encrypted.flip();
+                Assert.assertEquals(21, encrypted.get());
+                encrypted.clear();
+                Assert.assertEquals(-1, channel.read(encrypted));
+            }
+        }
+    }
+
+    @Test
+    public void testAbruptCloseDuringHandshake() throws Exception
+    {
+        InetSocketAddress address = prepare();
+        SslContextFactory sslContextFactory = newSslContextFactory();
+        sslContextFactory.start();
+        SSLEngine sslEngine = sslContextFactory.newSSLEngine(address);
+        sslEngine.setUseClientMode(true);
+        ALPN.put(sslEngine, new ALPN.ClientProvider()
+        {
+            @Override
+            public void unsupported()
+            {
+            }
+
+            @Override
+            public List<String> protocols()
+            {
+                return Arrays.asList("h2");
+            }
+
+            @Override
+            public void selected(String s)
+            {
+            }
+        });
+        sslEngine.beginHandshake();
+
+        ByteBuffer encrypted = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
+        sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
+        encrypted.flip();
+
+        try (SocketChannel channel = SocketChannel.open(address))
+        {
+            // Send ClientHello, immediately followed by FIN (no TLS Close Alert)
+            channel.write(encrypted);
+            channel.shutdownOutput();
+
+            // Read ServerHello from server
+            encrypted.clear();
+            int read = channel.read(encrypted);
+            encrypted.flip();
+            Assert.assertTrue(read > 0);
+            ByteBuffer decrypted = ByteBuffer.allocate(sslEngine.getSession().getApplicationBufferSize());
+            sslEngine.unwrap(encrypted, decrypted);
+
+            // It may happen that the read() above read both the ServerHello and the TLS Close Alert.
+            if (!encrypted.hasRemaining())
+            {
+                // Now if we can read more, we should read the TLS Close Alert and then the TCP FIN.
+                encrypted.clear();
+                read = channel.read(encrypted);
+                Assert.assertTrue(read > 0);
+                encrypted.flip();
+            }
+            Assert.assertEquals(21, encrypted.get());
+            encrypted.clear();
+            Assert.assertEquals(-1, channel.read(encrypted));
+        }
+    }
+
+    @Test
+    public void testClientAdvertisingHTTPServerSpeaksHTTP() throws Exception
+    {
+        InetSocketAddress address = prepare();
+
+        SslContextFactory sslContextFactory = newSslContextFactory();
+        sslContextFactory.start();
+        SSLContext sslContext = sslContextFactory.getSslContext();
+
+        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()))
+        {
+            client.setUseClientMode(true);
+            client.setSoTimeout(5000);
+
+            ALPN.put(client, new ALPN.ClientProvider()
+            {
+                @Override
+                public void unsupported()
+                {
+                }
+
+                @Override
+                public List<String> protocols()
+                {
+                    return Arrays.asList("http/1.1");
+                }
+
+                @Override
+                public void selected(String protocol)
+                {
+                    Assert.assertEquals("http/1.1", protocol);
+                }
+            });
+
+            client.startHandshake();
+
+            // Verify that the server really speaks http/1.1
+
+            OutputStream output = client.getOutputStream();
+            output.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost:" + address.getPort() + "\r\n" +
+                    "\r\n" +
+                    "").getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            InputStream input = client.getInputStream();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
+            String line = reader.readLine();
+            Assert.assertTrue(line.contains(" 404 "));
+        }
+    }
+
+    @Test
+    public void testClientAdvertisingMultipleProtocolsServerSpeaksHTTPWhenNegotiated() throws Exception
+    {
+        InetSocketAddress address = prepare();
+
+        SslContextFactory sslContextFactory = newSslContextFactory();
+        sslContextFactory.start();
+        SSLContext sslContext = sslContextFactory.getSslContext();
+        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()))
+        {
+            client.setUseClientMode(true);
+            client.setSoTimeout(5000);
+
+            ALPN.put(client, new ALPN.ClientProvider()
+            {
+                @Override
+                public void unsupported()
+                {
+                }
+
+                @Override
+                public List<String> protocols()
+                {
+                    return Arrays.asList("unknown/1.0", "http/1.1");
+                }
+
+                @Override
+                public void selected(String protocol)
+                {
+                    Assert.assertEquals("http/1.1", protocol);
+                }
+            });
+
+            client.startHandshake();
+
+            // Verify that the server really speaks http/1.1
+
+            OutputStream output = client.getOutputStream();
+            output.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost:" + address.getPort() + "\r\n" +
+                    "\r\n" +
+                    "").getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            InputStream input = client.getInputStream();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
+            String line = reader.readLine();
+            Assert.assertTrue(line.contains(" 404 "));
+        }
+    }
+
+    @Test
+    public void testClientNotSupportingALPNServerSpeaksDefaultProtocol() throws Exception
+    {
+        InetSocketAddress address = prepare();
+
+        SslContextFactory sslContextFactory = newSslContextFactory();
+        sslContextFactory.start();
+        SSLContext sslContext = sslContextFactory.getSslContext();
+        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()))
+        {
+            client.setUseClientMode(true);
+            client.setSoTimeout(5000);
+
+            ALPN.put(client, new ALPN.ClientProvider()
+            {
+                @Override
+                public void unsupported()
+                {
+                }
+
+                @Override
+                public List<String> protocols()
+                {
+                    return null;
+                }
+
+                @Override
+                public void selected(String s)
+                {
+                }
+            });
+
+            client.startHandshake();
+
+            // Verify that the server really speaks http/1.1
+
+            OutputStream output = client.getOutputStream();
+            output.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost:" + address.getPort() + "\r\n" +
+                    "\r\n" +
+                    "").getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            InputStream input = client.getInputStream();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
+            String line = reader.readLine();
+            Assert.assertTrue(line.contains(" 404 "));
+        }
+    }
+}
diff --git a/jetty-http2/http2-alpn-tests/src/test/java/org/eclipse/jetty/http2/alpn/tests/AbstractALPNTest.java b/jetty-http2/http2-alpn-tests/src/test/java/org/eclipse/jetty/http2/alpn/tests/AbstractALPNTest.java
new file mode 100644
index 0000000..7bd184e
--- /dev/null
+++ b/jetty-http2/http2-alpn-tests/src/test/java/org/eclipse/jetty/http2/alpn/tests/AbstractALPNTest.java
@@ -0,0 +1,93 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.alpn.tests;
+
+import java.net.InetSocketAddress;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.JDK;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Rule;
+
+public class AbstractALPNTest
+{
+    @Rule
+    public final TestTracker tracker = new TestTracker();
+    protected Server server;
+    protected ServerConnector connector;
+
+    @Before
+    public void before()
+    {
+        // The mandatory cipher needed to run HTTP/2
+        // over TLS is only available in JDK 8.
+        Assume.assumeTrue(JDK.IS_8);
+    }
+
+    protected InetSocketAddress prepare() throws Exception
+    {
+        server = new Server();
+        HttpConfiguration httpConfiguration = new HttpConfiguration();
+        HttpConnectionFactory h1 = new HttpConnectionFactory(httpConfiguration);
+        HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpConfiguration);
+        ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+        alpn.setDefaultProtocol(h1.getProtocol());
+        
+        connector = new ServerConnector(server, newSslContextFactory(), alpn, h1, h2);
+        connector.setPort(0);
+        connector.setIdleTimeout(30000);
+        server.addConnector(connector);
+        server.start();
+
+        ALPN.debug = true;
+
+        return new InetSocketAddress("localhost", connector.getLocalPort());
+    }
+
+    protected SslContextFactory newSslContextFactory()
+    {
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
+        sslContextFactory.setTrustStorePassword("storepwd");
+        sslContextFactory.setIncludeProtocols("TLSv1.2");
+        // The mandatory HTTP/2 cipher.
+        sslContextFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
+        return sslContextFactory;
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        if (server != null)
+            server.stop();
+    }
+}
diff --git a/jetty-http2/http2-alpn-tests/src/test/resources/jetty-logging.properties b/jetty-http2/http2-alpn-tests/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..29a0dfa
--- /dev/null
+++ b/jetty-http2/http2-alpn-tests/src/test/resources/jetty-logging.properties
@@ -0,0 +1,3 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+org.eclipse.jetty.alpn.LEVEL=DEBUG
+org.eclipse.jetty.http2.LEVEL=DEBUG
diff --git a/jetty-spdy/spdy-alpn-tests/src/test/resources/keystore.jks b/jetty-http2/http2-alpn-tests/src/test/resources/keystore.jks
similarity index 100%
rename from jetty-spdy/spdy-alpn-tests/src/test/resources/keystore.jks
rename to jetty-http2/http2-alpn-tests/src/test/resources/keystore.jks
Binary files differ
diff --git a/jetty-spdy/spdy-alpn-tests/src/test/resources/truststore.jks b/jetty-http2/http2-alpn-tests/src/test/resources/truststore.jks
similarity index 100%
rename from jetty-spdy/spdy-alpn-tests/src/test/resources/truststore.jks
rename to jetty-http2/http2-alpn-tests/src/test/resources/truststore.jks
Binary files differ
diff --git a/jetty-http2/http2-client/pom.xml b/jetty-http2/http2-client/pom.xml
new file mode 100644
index 0000000..15a44ef
--- /dev/null
+++ b/jetty-http2/http2-client/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.http2</groupId>
+    <artifactId>http2-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>http2-client</artifactId>
+  <name>Jetty :: HTTP2 :: Client</name>
+
+ <properties>
+    <bundle-symbolic-name>${project.groupId}.client</bundle-symbolic-name>
+  </properties>
+
+  <build>
+  </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.eclipse.jetty.http2</groupId>
+            <artifactId>http2-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-alpn-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.eclipse.jetty.toolchain</groupId>
+            <artifactId>jetty-test-helper</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-servlet</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-servlets</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-proxy</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.http2</groupId>
+            <artifactId>http2-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
new file mode 100644
index 0000000..0fa71b7
--- /dev/null
+++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
@@ -0,0 +1,425 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.alpn.client.ALPNClientConnectionFactory;
+import org.eclipse.jetty.http2.BufferingFlowControlStrategy;
+import org.eclipse.jetty.http2.FlowControlStrategy;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ManagedSelector;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.io.SelectChannelEndPoint;
+import org.eclipse.jetty.io.SelectorManager;
+import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
+import org.eclipse.jetty.util.thread.Scheduler;
+import org.eclipse.jetty.util.thread.strategy.ProduceConsume;
+
+/**
+ * <p>{@link HTTP2Client} provides an asynchronous, non-blocking implementation
+ * to send HTTP/2 frames to a server.</p>
+ * <p>Typical usage:</p>
+ * <pre>
+ * // Create and start HTTP2Client.
+ * HTTP2Client client = new HTTP2Client();
+ * SslContextFactory sslContextFactory = new SslContextFactory();
+ * client.addBean(sslContextFactory);
+ * client.start();
+ *
+ * // Connect to host.
+ * String host = "webtide.com";
+ * int port = 443;
+ *
+ * FuturePromise&lt;Session&gt; sessionPromise = new FuturePromise&lt;&gt;();
+ * client.connect(sslContextFactory, new InetSocketAddress(host, port), new ServerSessionListener.Adapter(), sessionPromise);
+ *
+ * // Obtain the client Session object.
+ * Session session = sessionPromise.get(5, TimeUnit.SECONDS);
+ *
+ * // Prepare the HTTP request headers.
+ * HttpFields requestFields = new HttpFields();
+ * requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
+ * // Prepare the HTTP request object.
+ * MetaData.Request request = new MetaData.Request("PUT", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
+ * // Create the HTTP/2 HEADERS frame representing the HTTP request.
+ * HeadersFrame headersFrame = new HeadersFrame(request, null, false);
+ *
+ * // Prepare the listener to receive the HTTP response frames.
+ * Stream.Listener responseListener = new new Stream.Listener.Adapter()
+ * {
+ *      &#64;Override
+ *      public void onHeaders(Stream stream, HeadersFrame frame)
+ *      {
+ *          System.err.println(frame);
+ *      }
+ *
+ *      &#64;Override
+ *      public void onData(Stream stream, DataFrame frame, Callback callback)
+ *      {
+ *          System.err.println(frame);
+ *          callback.succeeded();
+ *      }
+ * };
+ *
+ * // Send the HEADERS frame to create a stream.
+ * FuturePromise&lt;Stream&gt; streamPromise = new FuturePromise&lt;&gt;();
+ * session.newStream(headersFrame, streamPromise, responseListener);
+ * Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
+ *
+ * // Use the Stream object to send request content, if any, using a DATA frame.
+ * ByteBuffer content = ...;
+ * DataFrame requestContent = new DataFrame(stream.getId(), content, true);
+ * stream.data(requestContent, Callback.Adapter.INSTANCE);
+ *
+ * // When done, stop the client.
+ * client.stop();
+ * </pre>
+ */
+@ManagedObject
+public class HTTP2Client extends ContainerLifeCycle
+{
+    private Executor executor;
+    private Scheduler scheduler;
+    private ByteBufferPool bufferPool;
+    private ClientConnectionFactory connectionFactory;
+    private SelectorManager selector;
+    private int selectors = 1;
+    private long idleTimeout = 30000;
+    private long connectTimeout = 10000;
+    private int inputBufferSize = 8192;
+    private List<String> protocols = Arrays.asList("h2", "h2-17", "h2-16", "h2-15", "h2-14");
+    private int initialSessionRecvWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
+    private int initialStreamRecvWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
+    private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F);
+    private ExecutionStrategy.Factory executionStrategyFactory = new ProduceConsume.Factory();
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        if (executor == null)
+            setExecutor(new QueuedThreadPool());
+
+        if (scheduler == null)
+            setScheduler(new ScheduledExecutorScheduler());
+
+        if (bufferPool == null)
+            setByteBufferPool(new MappedByteBufferPool());
+
+        if (connectionFactory == null)
+        {
+            HTTP2ClientConnectionFactory h2 = new HTTP2ClientConnectionFactory();
+            setClientConnectionFactory((endPoint, context) ->
+            {
+                ClientConnectionFactory factory = h2;
+                SslContextFactory sslContextFactory = (SslContextFactory)context.get(SslClientConnectionFactory.SSL_CONTEXT_FACTORY_CONTEXT_KEY);
+                if (sslContextFactory != null)
+                {
+                    ALPNClientConnectionFactory alpn = new ALPNClientConnectionFactory(getExecutor(), h2, getProtocols());
+                    factory = new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), alpn);
+                }
+                return factory.newConnection(endPoint, context);
+            });
+        }
+
+        if (selector == null)
+        {
+            selector = newSelectorManager();
+            addBean(selector);
+        }
+        selector.setConnectTimeout(getConnectTimeout());
+
+        super.doStart();
+    }
+
+    protected SelectorManager newSelectorManager()
+    {
+        return new ClientSelectorManager(getExecutor(), getScheduler(), getSelectors());
+    }
+
+    public Executor getExecutor()
+    {
+        return executor;
+    }
+
+    public void setExecutor(Executor executor)
+    {
+        this.updateBean(this.executor, executor);
+        this.executor = executor;
+    }
+
+    public Scheduler getScheduler()
+    {
+        return scheduler;
+    }
+
+    public void setScheduler(Scheduler scheduler)
+    {
+        this.updateBean(this.scheduler, scheduler);
+        this.scheduler = scheduler;
+    }
+
+    public ByteBufferPool getByteBufferPool()
+    {
+        return bufferPool;
+    }
+
+    public void setByteBufferPool(ByteBufferPool bufferPool)
+    {
+        this.updateBean(this.bufferPool, bufferPool);
+        this.bufferPool = bufferPool;
+    }
+
+    public ClientConnectionFactory getClientConnectionFactory()
+    {
+        return connectionFactory;
+    }
+
+    public void setClientConnectionFactory(ClientConnectionFactory connectionFactory)
+    {
+        this.updateBean(this.connectionFactory, connectionFactory);
+        this.connectionFactory = connectionFactory;
+    }
+
+    public FlowControlStrategy.Factory getFlowControlStrategyFactory()
+    {
+        return flowControlStrategyFactory;
+    }
+
+    public void setFlowControlStrategyFactory(FlowControlStrategy.Factory flowControlStrategyFactory)
+    {
+        this.flowControlStrategyFactory = flowControlStrategyFactory;
+    }
+
+    public ExecutionStrategy.Factory getExecutionStrategyFactory()
+    {
+        return executionStrategyFactory;
+    }
+
+    public void setExecutionStrategyFactory(ExecutionStrategy.Factory executionStrategyFactory)
+    {
+        this.executionStrategyFactory = executionStrategyFactory;
+    }
+
+    @ManagedAttribute("The number of selectors")
+    public int getSelectors()
+    {
+        return selectors;
+    }
+
+    public void setSelectors(int selectors)
+    {
+        this.selectors = selectors;
+    }
+
+    @ManagedAttribute("The idle timeout in milliseconds")
+    public long getIdleTimeout()
+    {
+        return idleTimeout;
+    }
+
+    public void setIdleTimeout(long idleTimeout)
+    {
+        this.idleTimeout = idleTimeout;
+    }
+
+    @ManagedAttribute("The connect timeout in milliseconds")
+    public long getConnectTimeout()
+    {
+        return connectTimeout;
+    }
+
+    public void setConnectTimeout(long connectTimeout)
+    {
+        this.connectTimeout = connectTimeout;
+        SelectorManager selector = this.selector;
+        if (selector != null)
+            selector.setConnectTimeout(connectTimeout);
+    }
+
+    @ManagedAttribute("The size of the buffer used to read from the network")
+    public int getInputBufferSize()
+    {
+        return inputBufferSize;
+    }
+
+    public void setInputBufferSize(int inputBufferSize)
+    {
+        this.inputBufferSize = inputBufferSize;
+    }
+
+    @ManagedAttribute("The ALPN protocol list")
+    public List<String> getProtocols()
+    {
+        return protocols;
+    }
+
+    public void setProtocols(List<String> protocols)
+    {
+        this.protocols = protocols;
+    }
+
+    @ManagedAttribute("The initial size of session's flow control receive window")
+    public int getInitialSessionRecvWindow()
+    {
+        return initialSessionRecvWindow;
+    }
+
+    public void setInitialSessionRecvWindow(int initialSessionRecvWindow)
+    {
+        this.initialSessionRecvWindow = initialSessionRecvWindow;
+    }
+
+    @ManagedAttribute("The initial size of stream's flow control receive window")
+    public int getInitialStreamRecvWindow()
+    {
+        return initialStreamRecvWindow;
+    }
+
+    public void setInitialStreamRecvWindow(int initialStreamRecvWindow)
+    {
+        this.initialStreamRecvWindow = initialStreamRecvWindow;
+    }
+
+    public void connect(InetSocketAddress address, Session.Listener listener, Promise<Session> promise)
+    {
+        connect(null, address, listener, promise);
+    }
+
+    public void connect(SslContextFactory sslContextFactory, InetSocketAddress address, Session.Listener listener, Promise<Session> promise)
+    {
+        connect(sslContextFactory, address, listener, promise, null);
+    }
+
+    public void connect(SslContextFactory sslContextFactory, InetSocketAddress address, Session.Listener listener, Promise<Session> promise, Map<String, Object> context)
+    {
+        try
+        {
+            SocketChannel channel = SocketChannel.open();
+            configure(channel);
+            channel.configureBlocking(false);
+            context = contextFrom(sslContextFactory, address, listener, promise, context);
+            if (channel.connect(address))
+                selector.accept(channel, context);
+            else
+                selector.connect(channel, context);
+        }
+        catch (Throwable x)
+        {
+            promise.failed(x);
+        }
+    }
+
+    public void accept(SslContextFactory sslContextFactory, SocketChannel channel, Session.Listener listener, Promise<Session> promise)
+    {
+        try
+        {
+            if (!channel.isConnected())
+                throw new IllegalStateException("SocketChannel must be connected");
+            channel.configureBlocking(false);
+            Map<String, Object> context = contextFrom(sslContextFactory, (InetSocketAddress)channel.getRemoteAddress(), listener, promise, null);
+            selector.accept(channel, context);
+        }
+        catch (Throwable x)
+        {
+            promise.failed(x);
+        }
+    }
+
+    private Map<String, Object> contextFrom(SslContextFactory sslContextFactory, InetSocketAddress address, Session.Listener listener, Promise<Session> promise, Map<String, Object> context)
+    {
+        if (context == null)
+            context = new HashMap<>();
+        context.put(HTTP2ClientConnectionFactory.CLIENT_CONTEXT_KEY, this);
+        context.put(HTTP2ClientConnectionFactory.SESSION_LISTENER_CONTEXT_KEY, listener);
+        context.put(HTTP2ClientConnectionFactory.SESSION_PROMISE_CONTEXT_KEY, promise);
+        if (sslContextFactory != null)
+            context.put(SslClientConnectionFactory.SSL_CONTEXT_FACTORY_CONTEXT_KEY, sslContextFactory);
+        context.put(SslClientConnectionFactory.SSL_PEER_HOST_CONTEXT_KEY, address.getHostString());
+        context.put(SslClientConnectionFactory.SSL_PEER_PORT_CONTEXT_KEY, address.getPort());
+        context.putIfAbsent(ClientConnectionFactory.CONNECTOR_CONTEXT_KEY, this);
+        return context;
+    }
+
+    protected void configure(SocketChannel channel) throws IOException
+    {
+        channel.socket().setTcpNoDelay(true);
+    }
+
+    private class ClientSelectorManager extends SelectorManager
+    {
+        private ClientSelectorManager(Executor executor, Scheduler scheduler, int selectors)
+        {
+            super(executor, scheduler, selectors);
+        }
+
+        @Override
+        protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey selectionKey) throws IOException
+        {
+            return new SelectChannelEndPoint(channel, selector, selectionKey, getScheduler(), getIdleTimeout());
+        }
+
+        @Override
+        public Connection newConnection(SocketChannel channel, EndPoint endpoint, Object attachment) throws IOException
+        {
+            @SuppressWarnings("unchecked")
+            Map<String, Object> context = (Map<String, Object>)attachment;
+            context.put(HTTP2ClientConnectionFactory.BYTE_BUFFER_POOL_CONTEXT_KEY, getByteBufferPool());
+            context.put(HTTP2ClientConnectionFactory.EXECUTOR_CONTEXT_KEY, getExecutor());
+            context.put(HTTP2ClientConnectionFactory.SCHEDULER_CONTEXT_KEY, getScheduler());
+            return getClientConnectionFactory().newConnection(endpoint, context);
+        }
+
+        @Override
+        protected void connectionFailed(SocketChannel channel, Throwable failure, Object attachment)
+        {
+            @SuppressWarnings("unchecked")
+            Map<String, Object> context = (Map<String, Object>)attachment;
+            if (LOG.isDebugEnabled())
+            {
+                Object host = context.get(SslClientConnectionFactory.SSL_PEER_HOST_CONTEXT_KEY);
+                Object port = context.get(SslClientConnectionFactory.SSL_PEER_PORT_CONTEXT_KEY);
+                LOG.debug("Could not connect to {}:{}", host, port);
+            }
+            @SuppressWarnings("unchecked")
+            Promise<Session> promise = (Promise<Session>)context.get(HTTP2ClientConnectionFactory.SESSION_PROMISE_CONTEXT_KEY);
+            promise.failed(failure);
+        }
+    }
+}
diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java
new file mode 100644
index 0000000..093a97c
--- /dev/null
+++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java
@@ -0,0 +1,197 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.http2.FlowControlStrategy;
+import org.eclipse.jetty.http2.HTTP2Connection;
+import org.eclipse.jetty.http2.ISession;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.frames.PrefaceFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.http2.generator.Generator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
+import org.eclipse.jetty.util.thread.Scheduler;
+
+public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
+{
+    public static final String CLIENT_CONTEXT_KEY = "http2.client";
+    public static final String BYTE_BUFFER_POOL_CONTEXT_KEY = "http2.client.byteBufferPool";
+    public static final String EXECUTOR_CONTEXT_KEY = "http2.client.executor";
+    public static final String SCHEDULER_CONTEXT_KEY = "http2.client.scheduler";
+    public static final String SESSION_LISTENER_CONTEXT_KEY = "http2.client.sessionListener";
+    public static final String SESSION_PROMISE_CONTEXT_KEY = "http2.client.sessionPromise";
+
+    private final Connection.Listener connectionListener = new ConnectionListener();
+    private int initialSessionRecvWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
+
+    @Override
+    public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
+    {
+        HTTP2Client client = (HTTP2Client)context.get(CLIENT_CONTEXT_KEY);
+        ByteBufferPool byteBufferPool = (ByteBufferPool)context.get(BYTE_BUFFER_POOL_CONTEXT_KEY);
+        Executor executor = (Executor)context.get(EXECUTOR_CONTEXT_KEY);
+        Scheduler scheduler = (Scheduler)context.get(SCHEDULER_CONTEXT_KEY);
+        Session.Listener listener = (Session.Listener)context.get(SESSION_LISTENER_CONTEXT_KEY);
+        @SuppressWarnings("unchecked")
+        Promise<Session> promise = (Promise<Session>)context.get(SESSION_PROMISE_CONTEXT_KEY);
+
+        Generator generator = new Generator(byteBufferPool);
+        FlowControlStrategy flowControl = newFlowControlStrategy();
+        if (flowControl == null)
+            flowControl = client.getFlowControlStrategyFactory().newFlowControlStrategy();
+        HTTP2ClientSession session = new HTTP2ClientSession(scheduler, endPoint, generator, listener, flowControl);
+        Parser parser = new Parser(byteBufferPool, session, 4096, 8192);
+        HTTP2ClientConnection connection = new HTTP2ClientConnection(client, byteBufferPool, executor, endPoint,
+                parser, session, client.getInputBufferSize(), client.getExecutionStrategyFactory(), promise, listener);
+        connection.addListener(connectionListener);
+        return customize(connection, context);
+    }
+
+    /**
+     * @deprecated use {@link HTTP2Client#setFlowControlStrategyFactory(FlowControlStrategy.Factory)} instead
+     */
+    @Deprecated
+    protected FlowControlStrategy newFlowControlStrategy()
+    {
+        return null;
+    }
+
+    /**
+     * @deprecated use {@link HTTP2Client#getInitialSessionRecvWindow()} instead
+     */
+    @Deprecated
+    public int getInitialSessionRecvWindow()
+    {
+        return initialSessionRecvWindow;
+    }
+
+    /**
+     * @deprecated use {@link HTTP2Client#setInitialSessionRecvWindow(int)} instead
+     */
+    @Deprecated
+    public void setInitialSessionRecvWindow(int initialSessionRecvWindow)
+    {
+        this.initialSessionRecvWindow = initialSessionRecvWindow;
+    }
+
+    private class HTTP2ClientConnection extends HTTP2Connection implements Callback
+    {
+        private final HTTP2Client client;
+        private final Promise<Session> promise;
+        private final Session.Listener listener;
+
+        private HTTP2ClientConnection(HTTP2Client client, ByteBufferPool byteBufferPool, Executor executor, EndPoint endpoint, Parser parser, ISession session, int bufferSize, ExecutionStrategy.Factory executionFactory, Promise<Session> promise, Session.Listener listener)
+        {
+            super(byteBufferPool, executor, endpoint, parser, session, bufferSize, executionFactory);
+            this.client = client;
+            this.promise = promise;
+            this.listener = listener;
+        }
+
+        @Override
+        public int getMessagesIn()
+        {
+            HTTP2ClientSession session = (HTTP2ClientSession)getSession();
+            return (int)session.getStreamsOpened();
+        }
+
+        @Override
+        public int getMessagesOut()
+        {
+            HTTP2ClientSession session = (HTTP2ClientSession)getSession();
+            return (int)session.getStreamsClosed();
+        }
+
+        @Override
+        public void onOpen()
+        {
+            Map<Integer, Integer> settings = listener.onPreface(getSession());
+            if (settings == null)
+                settings = Collections.emptyMap();
+
+            PrefaceFrame prefaceFrame = new PrefaceFrame();
+            SettingsFrame settingsFrame = new SettingsFrame(settings, false);
+
+            ISession session = getSession();
+
+            int sessionRecv = client.getInitialSessionRecvWindow();
+            if (sessionRecv == FlowControlStrategy.DEFAULT_WINDOW_SIZE)
+                sessionRecv = initialSessionRecvWindow;
+
+            int windowDelta = sessionRecv - FlowControlStrategy.DEFAULT_WINDOW_SIZE;
+            if (windowDelta > 0)
+            {
+                session.updateRecvWindow(windowDelta);
+                session.frames(null, this, prefaceFrame, settingsFrame, new WindowUpdateFrame(0, windowDelta));
+            }
+            else
+            {
+                session.frames(null, this, prefaceFrame, settingsFrame);
+            }
+            // Only start reading from server after we have sent the client preface,
+            // otherwise we risk to read the server preface (a SETTINGS frame) and
+            // reply to that before we have the chance to send the client preface.
+            super.onOpen();
+        }
+
+        @Override
+        public void succeeded()
+        {
+            promise.succeeded(getSession());
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            close();
+            promise.failed(x);
+        }
+    }
+
+    private class ConnectionListener implements Connection.Listener
+    {
+        @Override
+        public void onOpened(Connection connection)
+        {
+            HTTP2ClientConnection http2Connection = (HTTP2ClientConnection)connection;
+            http2Connection.client.addManaged((LifeCycle)http2Connection.getSession());
+        }
+
+        @Override
+        public void onClosed(Connection connection)
+        {
+            HTTP2ClientConnection http2Connection = (HTTP2ClientConnection)connection;
+            http2Connection.client.removeBean(http2Connection.getSession());
+        }
+    }
+}
diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientSession.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientSession.java
new file mode 100644
index 0000000..764ff8d
--- /dev/null
+++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientSession.java
@@ -0,0 +1,146 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.eclipse.jetty.http2.FlowControlStrategy;
+import org.eclipse.jetty.http2.HTTP2Session;
+import org.eclipse.jetty.http2.IStream;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.generator.Generator;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Scheduler;
+
+public class HTTP2ClientSession extends HTTP2Session
+{
+    private static final Logger LOG = Log.getLogger(HTTP2ClientSession.class);
+
+    private final AtomicLong streamsOpened = new AtomicLong();
+    private final AtomicLong streamsClosed = new AtomicLong();
+
+    public HTTP2ClientSession(Scheduler scheduler, EndPoint endPoint, Generator generator, Session.Listener listener, FlowControlStrategy flowControl)
+    {
+        super(scheduler, endPoint, generator, listener, flowControl, 1);
+    }
+
+    @Override
+    protected void onStreamOpened(IStream stream)
+    {
+        super.onStreamOpened(stream);
+        streamsOpened.incrementAndGet();
+    }
+
+    @Override
+    protected void onStreamClosed(IStream stream)
+    {
+        super.onStreamClosed(stream);
+        streamsClosed.incrementAndGet();
+    }
+
+    public long getStreamsOpened()
+    {
+        return streamsOpened.get();
+    }
+
+    public long getStreamsClosed()
+    {
+        return streamsClosed.get();
+    }
+
+    @Override
+    public void onHeaders(HeadersFrame frame)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Received {}", frame);
+
+        int streamId = frame.getStreamId();
+        IStream stream = getStream(streamId);
+        if (stream == null)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Ignoring {}, stream #{} not found", frame, streamId);
+        }
+        else
+        {
+            stream.process(frame, Callback.NOOP);
+            notifyHeaders(stream, frame);
+        }
+    }
+
+    private void notifyHeaders(IStream stream, HeadersFrame frame)
+    {
+        Stream.Listener listener = stream.getListener();
+        if (listener == null)
+            return;
+        try
+        {
+            listener.onHeaders(stream, frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    @Override
+    public void onPushPromise(PushPromiseFrame frame)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Received {}", frame);
+
+        int streamId = frame.getStreamId();
+        int pushStreamId = frame.getPromisedStreamId();
+        IStream stream = getStream(streamId);
+        if (stream == null)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Ignoring {}, stream #{} not found", frame, streamId);
+        }
+        else
+        {
+            IStream pushStream = createRemoteStream(pushStreamId);
+            pushStream.process(frame, Callback.NOOP);
+            Stream.Listener listener = notifyPush(stream, pushStream, frame);
+            pushStream.setListener(listener);
+        }
+    }
+
+    private Stream.Listener notifyPush(IStream stream, IStream pushStream, PushPromiseFrame frame)
+    {
+        Stream.Listener listener = stream.getListener();
+        if (listener == null)
+            return null;
+        try
+        {
+            return listener.onPush(pushStream, frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+            return null;
+        }
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/AbstractTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/AbstractTest.java
new file mode 100644
index 0000000..4d97ad7
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/AbstractTest.java
@@ -0,0 +1,129 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.http.HttpServlet;
+
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Rule;
+
+public class AbstractTest
+{
+    @Rule
+    public TestTracker tracker = new TestTracker();
+    protected ServerConnector connector;
+    protected String servletPath = "/test";
+    protected HTTP2Client client;
+    protected Server server;
+
+    protected void start(HttpServlet servlet) throws Exception
+    {
+        prepareServer(new HTTP2ServerConnectionFactory(new HttpConfiguration()));
+        ServletContextHandler context = new ServletContextHandler(server, "/", true, false);
+        context.addServlet(new ServletHolder(servlet), servletPath + "/*");
+        customizeContext(context);
+        server.start();
+
+        prepareClient();
+        client.start();
+    }
+
+    protected void customizeContext(ServletContextHandler context)
+    {
+    }
+
+    protected void start(ServerSessionListener listener) throws Exception
+    {
+        prepareServer(new RawHTTP2ServerConnectionFactory(new HttpConfiguration(), listener));
+        server.start();
+
+        prepareClient();
+        client.start();
+    }
+
+    protected void prepareServer(ConnectionFactory... connectionFactories)
+    {
+        QueuedThreadPool serverExecutor = new QueuedThreadPool();
+        serverExecutor.setName("server");
+        server = new Server(serverExecutor);
+        connector = new ServerConnector(server, 1, 1, connectionFactories);
+        server.addConnector(connector);
+    }
+
+    protected void prepareClient()
+    {
+        client = new HTTP2Client();
+        QueuedThreadPool clientExecutor = new QueuedThreadPool();
+        clientExecutor.setName("client");
+        client.setExecutor(clientExecutor);
+    }
+
+    protected Session newClient(Session.Listener listener) throws Exception
+    {
+        String host = "localhost";
+        int port = connector.getLocalPort();
+        InetSocketAddress address = new InetSocketAddress(host, port);
+        FuturePromise<Session> promise = new FuturePromise<>();
+        client.connect(address, listener, promise);
+        return promise.get(5, TimeUnit.SECONDS);
+    }
+
+    protected MetaData.Request newRequest(String method, HttpFields fields)
+    {
+        return newRequest(method, "", fields);
+    }
+
+    protected MetaData.Request newRequest(String method, String pathInfo, HttpFields fields)
+    {
+        String host = "localhost";
+        int port = connector.getLocalPort();
+        String authority = host + ":" + port;
+        return new MetaData.Request(method, HttpScheme.HTTP, new HostPortHttpField(authority), servletPath + pathInfo, HttpVersion.HTTP_2, fields);
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        if (client != null)
+            client.stop();
+        if (server != null)
+            server.stop();
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/AsyncIOTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/AsyncIOTest.java
new file mode 100644
index 0000000..913aa29
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/AsyncIOTest.java
@@ -0,0 +1,248 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class AsyncIOTest extends AbstractTest
+{
+    @Test
+    public void testLastContentAvailableBeforeService() throws Exception
+    {
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(final HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                // Wait for the data to fully arrive.
+                sleep(1000);
+
+                final AsyncContext asyncContext = request.startAsync();
+                asyncContext.setTimeout(0);
+                request.getInputStream().setReadListener(new EmptyReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        ServletInputStream input = request.getInputStream();
+                        while (input.isReady())
+                        {
+                            int read = input.read();
+                            if (read < 0)
+                                break;
+                        }
+                        if (input.isFinished())
+                            asyncContext.complete();
+                    }
+                });
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+
+        HttpFields fields = new HttpFields();
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame = new HeadersFrame(metaData, null, false);
+        final CountDownLatch latch = new CountDownLatch(1);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        session.newStream(frame, promise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+        stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(16), true), Callback.NOOP);
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testLastContentAvailableAfterServiceReturns() throws Exception
+    {
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(final HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                final AsyncContext asyncContext = request.startAsync();
+                asyncContext.setTimeout(0);
+                request.getInputStream().setReadListener(new EmptyReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        ServletInputStream input = request.getInputStream();
+                        while (input.isReady())
+                        {
+                            int read = input.read();
+                            if (read < 0)
+                                break;
+                        }
+                        if (input.isFinished())
+                            asyncContext.complete();
+                    }
+                });
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+
+        HttpFields fields = new HttpFields();
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame = new HeadersFrame(metaData, null, false);
+        final CountDownLatch latch = new CountDownLatch(1);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        session.newStream(frame, promise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+
+        // Wait until service() returns.
+        Thread.sleep(1000);
+        stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(16), true), Callback.NOOP);
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testSomeContentAvailableAfterServiceReturns() throws Exception
+    {
+        final AtomicInteger count = new AtomicInteger();
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(final HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                final AsyncContext asyncContext = request.startAsync();
+                asyncContext.setTimeout(0);
+                request.getInputStream().setReadListener(new EmptyReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        count.incrementAndGet();
+                        ServletInputStream input = request.getInputStream();
+                        while (input.isReady())
+                        {
+                            int read = input.read();
+                            if (read < 0)
+                                break;
+                        }
+                        if (input.isFinished())
+                            asyncContext.complete();
+                    }
+                });
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+
+        HttpFields fields = new HttpFields();
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame = new HeadersFrame(metaData, null, false);
+        final CountDownLatch latch = new CountDownLatch(1);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        session.newStream(frame, promise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+
+        // Wait until service() returns.
+        Thread.sleep(1000);
+        stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1), false), Callback.NOOP);
+
+        // Wait until onDataAvailable() returns.
+        Thread.sleep(1000);
+        stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1), true), Callback.NOOP);
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        // Make sure onDataAvailable() has been called twice
+        Assert.assertEquals(2, count.get());
+    }
+
+    private static void sleep(long ms) throws InterruptedIOException
+    {
+        try
+        {
+            Thread.sleep(ms);
+        }
+        catch (InterruptedException x)
+        {
+            throw new InterruptedIOException();
+        }
+    }
+
+    private static class EmptyReadListener implements ReadListener
+    {
+        @Override
+        public void onDataAvailable() throws IOException
+        {
+        }
+
+        @Override
+        public void onAllDataRead() throws IOException
+        {
+        }
+
+        @Override
+        public void onError(Throwable t)
+        {
+        }
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/AsyncServletTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/AsyncServletTest.java
new file mode 100644
index 0000000..d48e7cc
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/AsyncServletTest.java
@@ -0,0 +1,349 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Promise;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class AsyncServletTest extends AbstractTest
+{
+    @Test
+    public void testStartAsyncThenDispatch() throws Exception
+    {
+        byte[] content = new byte[1024];
+        new Random().nextBytes(content);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                AsyncContext asyncContext = (AsyncContext)request.getAttribute(AsyncContext.class.getName());
+                if (asyncContext == null)
+                {
+                    AsyncContext context = request.startAsync();
+                    context.setTimeout(0);
+                    request.setAttribute(AsyncContext.class.getName(), context);
+                    context.start(() ->
+                    {
+                        sleep(1000);
+                        context.dispatch();
+                    });
+                }
+                else
+                {
+                    response.getOutputStream().write(content);
+                }
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+
+        HttpFields fields = new HttpFields();
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame = new HeadersFrame(metaData, null, true);
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        CountDownLatch latch = new CountDownLatch(1);
+        session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                try
+                {
+                    buffer.write(BufferUtil.toArray(frame.getData()));
+                    callback.succeeded();
+                    if (frame.isEndStream())
+                        latch.countDown();
+                }
+                catch (IOException x)
+                {
+                    callback.failed(x);
+                }
+            }
+        });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        Assert.assertArrayEquals(content, buffer.toByteArray());
+    }
+
+    @Test
+    public void testStartAsyncThenClientSessionIdleTimeout() throws Exception
+    {
+        CountDownLatch serverLatch = new CountDownLatch(1);
+        start(new AsyncOnErrorServlet(serverLatch));
+        long idleTimeout = 1000;
+        client.setIdleTimeout(idleTimeout);
+
+        Session session = newClient(new Session.Listener.Adapter());
+        HttpFields fields = new HttpFields();
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame = new HeadersFrame(metaData, null, true);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        session.newStream(frame, promise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response response = (MetaData.Response)frame.getMetaData();
+                if (response.getStatus() == HttpStatus.INTERNAL_SERVER_ERROR_500 && frame.isEndStream())
+                    clientLatch.countDown();
+            }
+        });
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+        stream.setIdleTimeout(10 * idleTimeout);
+
+        // When the client closes, the server receives the
+        // corresponding frame and acts by notifying the failure,
+        // which sends back to the client the error response.
+        Assert.assertTrue(serverLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+        Assert.assertTrue(clientLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testStartAsyncThenClientStreamIdleTimeout() throws Exception
+    {
+        CountDownLatch serverLatch = new CountDownLatch(1);
+        start(new AsyncOnErrorServlet(serverLatch));
+        long idleTimeout = 1000;
+        client.setIdleTimeout(10 * idleTimeout);
+
+        Session session = newClient(new Session.Listener.Adapter());
+        HttpFields fields = new HttpFields();
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame = new HeadersFrame(metaData, null, true);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        session.newStream(frame, promise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public boolean onIdleTimeout(Stream stream, Throwable x)
+            {
+                clientLatch.countDown();
+                return true;
+            }
+        });
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+        stream.setIdleTimeout(idleTimeout);
+
+        // When the client resets, the server receives the
+        // corresponding frame and acts by notifying the failure,
+        // but the response is not sent back to the client.
+        Assert.assertTrue(serverLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+        Assert.assertTrue(clientLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testStartAsyncThenServerSessionIdleTimeout() throws Exception
+    {
+        testStartAsyncThenServerIdleTimeout(1000, 10 * 1000);
+    }
+
+    @Test
+    public void testStartAsyncThenServerStreamIdleTimeout() throws Exception
+    {
+        testStartAsyncThenServerIdleTimeout(10 * 1000, 1000);
+    }
+
+    private void testStartAsyncThenServerIdleTimeout(long sessionTimeout, long streamTimeout) throws Exception
+    {
+        prepareServer(new HTTP2ServerConnectionFactory(new HttpConfiguration())
+        {
+            @Override
+            protected ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint)
+            {
+                return new HTTPServerSessionListener(connector, endPoint)
+                {
+                    @Override
+                    public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+                    {
+                        stream.setIdleTimeout(streamTimeout);
+                        return super.onNewStream(stream, frame);
+                    }
+                };
+            }
+        });
+        connector.setIdleTimeout(sessionTimeout);
+        ServletContextHandler context = new ServletContextHandler(server, "/");
+        long timeout = Math.min(sessionTimeout, streamTimeout);
+        CountDownLatch errorLatch = new CountDownLatch(1);
+        context.addServlet(new ServletHolder(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                AsyncContext asyncContext = (AsyncContext)request.getAttribute(AsyncContext.class.getName());
+                if (asyncContext == null)
+                {
+                    AsyncContext context = request.startAsync();
+                    context.setTimeout(2 * timeout);
+                    request.setAttribute(AsyncContext.class.getName(), context);
+                    context.addListener(new AsyncListener()
+                    {
+                        @Override
+                        public void onComplete(AsyncEvent event) throws IOException
+                        {
+                        }
+
+                        @Override
+                        public void onTimeout(AsyncEvent event) throws IOException
+                        {
+                            event.getAsyncContext().complete();
+                        }
+
+                        @Override
+                        public void onError(AsyncEvent event) throws IOException
+                        {
+                            errorLatch.countDown();
+                        }
+
+                        @Override
+                        public void onStartAsync(AsyncEvent event) throws IOException
+                        {
+                        }
+                    });
+                }
+                else
+                {
+                    throw new ServletException();
+                }
+            }
+        }), servletPath + "/*");
+        server.start();
+
+        prepareClient();
+        client.start();
+
+        Session session = newClient(new Session.Listener.Adapter());
+        HttpFields fields = new HttpFields();
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame = new HeadersFrame(metaData, null, true);
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response response = (MetaData.Response)frame.getMetaData();
+                if (response.getStatus() == HttpStatus.OK_200 && frame.isEndStream())
+                    clientLatch.countDown();
+            }
+        });
+
+        // When the server idle times out, but the request has been dispatched
+        // then the server must ignore the idle timeout as per Servlet semantic.
+        Assert.assertFalse(errorLatch.await(2 * timeout, TimeUnit.MILLISECONDS));
+        Assert.assertTrue(clientLatch.await(2 * timeout, TimeUnit.MILLISECONDS));
+    }
+
+    private void sleep(long ms)
+    {
+        try
+        {
+            Thread.sleep(ms);
+        }
+        catch (InterruptedException x)
+        {
+            x.printStackTrace();
+        }
+    }
+
+    private static class AsyncOnErrorServlet extends HttpServlet implements AsyncListener
+    {
+        private final CountDownLatch latch;
+
+        public AsyncOnErrorServlet(CountDownLatch latch)
+        {
+            this.latch = latch;
+        }
+
+        @Override
+        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {
+            AsyncContext asyncContext = (AsyncContext)request.getAttribute(AsyncContext.class.getName());
+            if (asyncContext == null)
+            {
+                AsyncContext context = request.startAsync();
+                context.setTimeout(0);
+                request.setAttribute(AsyncContext.class.getName(), context);
+                context.addListener(this);
+            }
+            else
+            {
+                throw new ServletException();
+            }
+        }
+
+        @Override
+        public void onComplete(AsyncEvent event) throws IOException
+        {
+        }
+
+        @Override
+        public void onTimeout(AsyncEvent event) throws IOException
+        {
+        }
+
+        @Override
+        public void onError(AsyncEvent event) throws IOException
+        {
+            HttpServletResponse response = (HttpServletResponse)event.getSuppliedResponse();
+            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
+            event.getAsyncContext().complete();
+            latch.countDown();
+        }
+
+        @Override
+        public void onStartAsync(AsyncEvent event) throws IOException
+        {
+        }
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/BufferingFlowControlStrategyTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/BufferingFlowControlStrategyTest.java
new file mode 100644
index 0000000..948e36d
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/BufferingFlowControlStrategyTest.java
@@ -0,0 +1,31 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import org.eclipse.jetty.http2.BufferingFlowControlStrategy;
+import org.eclipse.jetty.http2.FlowControlStrategy;
+
+public class BufferingFlowControlStrategyTest extends FlowControlStrategyTest
+{
+    @Override
+    protected FlowControlStrategy newFlowControlStrategy()
+    {
+        return new BufferingFlowControlStrategy(0.5F);
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/Client.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/Client.java
new file mode 100644
index 0000000..a2c64ff
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/Client.java
@@ -0,0 +1,94 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Jetty;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class Client
+{
+    public static void main(String[] args) throws Exception
+    {
+        HTTP2Client client = new HTTP2Client();
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        client.addBean(sslContextFactory);
+        client.start();
+
+        String host = "webtide.com";
+        int port = 443;
+
+        FuturePromise<Session> sessionPromise = new FuturePromise<>();
+        client.connect(sslContextFactory, new InetSocketAddress(host, port), new ServerSessionListener.Adapter(), sessionPromise);
+        Session session = sessionPromise.get(5, TimeUnit.SECONDS);
+
+        HttpFields requestFields = new HttpFields();
+        requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
+        MetaData.Request metaData = new MetaData.Request("GET", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
+        HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
+        final Phaser phaser = new Phaser(2);
+        session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                System.err.println(frame);
+                if (frame.isEndStream())
+                    phaser.arrive();
+            }
+
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                System.err.println(frame);
+                callback.succeeded();
+                if (frame.isEndStream())
+                    phaser.arrive();
+            }
+
+            @Override
+            public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+            {
+                System.err.println(frame);
+                phaser.register();
+                return this;
+            }
+        });
+
+        phaser.awaitAdvanceInterruptibly(phaser.arrive(), 5, TimeUnit.SECONDS);
+
+        client.stop();
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ConnectTimeoutTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ConnectTimeoutTest.java
new file mode 100644
index 0000000..666ebdb
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ConnectTimeoutTest.java
@@ -0,0 +1,90 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.util.Promise;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class ConnectTimeoutTest extends AbstractTest
+{
+    @Test
+    public void testConnectTimeout() throws Exception
+    {
+        final String host = "10.255.255.1";
+        final int port = 80;
+        int connectTimeout = 1000;
+        assumeConnectTimeout(host, port, connectTimeout);
+
+        start(new ServerSessionListener.Adapter());
+        client.setConnectTimeout(connectTimeout);
+
+        InetSocketAddress address = new InetSocketAddress(host, port);
+        final CountDownLatch latch = new CountDownLatch(1);
+        client.connect(address, new Session.Listener.Adapter(), new Promise.Adapter<Session>()
+        {
+            @Override
+            public void failed(Throwable x)
+            {
+                Assert.assertTrue(x instanceof SocketTimeoutException);
+                latch.countDown();
+            }
+        });
+
+        Assert.assertTrue(latch.await(2 * connectTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    private void assumeConnectTimeout(String host, int port, int connectTimeout) throws IOException
+    {
+        boolean socketTimeout = false;
+        
+        try (Socket socket = new Socket())
+        {
+            // Try to connect to a private address in the 10.x.y.z range.
+            // These addresses are usually not routed, so an attempt to
+            // connect to them will hang the connection attempt, which is
+            // what we want to simulate in this test.
+            socket.connect(new InetSocketAddress(host, port), connectTimeout);
+        }
+        catch (SocketTimeoutException x)
+        {
+            // We expect a timeout during connect, allow test to continue.
+            socketTimeout = true;
+        }
+        catch (Throwable x)
+        {
+            // Dump stacktrace when there is an unexpected test failure
+            // Useful when debugging
+            x.printStackTrace(System.err);
+        }
+        
+        // Abort the test if we can connect.
+        Assume.assumeTrue("Should have seen connect timeout",socketTimeout);
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/EmptyHttpServlet.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/EmptyHttpServlet.java
new file mode 100644
index 0000000..f5a1608
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/EmptyHttpServlet.java
@@ -0,0 +1,34 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class EmptyHttpServlet extends HttpServlet
+{
+    @Override
+    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/FlowControlStalledTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/FlowControlStalledTest.java
new file mode 100644
index 0000000..0b05a0a
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/FlowControlStalledTest.java
@@ -0,0 +1,305 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.BufferingFlowControlStrategy;
+import org.eclipse.jetty.http2.FlowControlStrategy;
+import org.eclipse.jetty.http2.ISession;
+import org.eclipse.jetty.http2.IStream;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class FlowControlStalledTest
+{
+    @Rule
+    public TestTracker tracker = new TestTracker();
+    protected ServerConnector connector;
+    protected HTTP2Client client;
+    protected Server server;
+
+    protected void start(FlowControlStrategy.Factory flowControlFactory, ServerSessionListener listener) throws Exception
+    {
+        QueuedThreadPool serverExecutor = new QueuedThreadPool();
+        serverExecutor.setName("server");
+        server = new Server(serverExecutor);
+        RawHTTP2ServerConnectionFactory connectionFactory = new RawHTTP2ServerConnectionFactory(new HttpConfiguration(), listener);
+        connectionFactory.setFlowControlStrategyFactory(flowControlFactory);
+        connector = new ServerConnector(server, connectionFactory);
+        server.addConnector(connector);
+        server.start();
+
+        client = new HTTP2Client();
+        QueuedThreadPool clientExecutor = new QueuedThreadPool();
+        clientExecutor.setName("client");
+        client.setExecutor(clientExecutor);
+        client.setFlowControlStrategyFactory(flowControlFactory);
+        client.start();
+    }
+
+    protected Session newClient(Session.Listener listener) throws Exception
+    {
+        String host = "localhost";
+        int port = connector.getLocalPort();
+        InetSocketAddress address = new InetSocketAddress(host, port);
+        FuturePromise<Session> promise = new FuturePromise<>();
+        client.connect(address, listener, promise);
+        return promise.get(5, TimeUnit.SECONDS);
+    }
+
+    protected MetaData.Request newRequest(String method, String target, HttpFields fields)
+    {
+        String host = "localhost";
+        int port = connector.getLocalPort();
+        String authority = host + ":" + port;
+        return new MetaData.Request(method, HttpScheme.HTTP, new HostPortHttpField(authority), target, HttpVersion.HTTP_2, fields);
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        // Allow WINDOW_UPDATE frames to be sent/received to avoid exception stack traces.
+        Thread.sleep(1000);
+        client.stop();
+        server.stop();
+    }
+
+    @Test
+    public void testStreamStalledIsInvokedOnlyOnce() throws Exception
+    {
+        AtomicReference<CountDownLatch> stallLatch = new AtomicReference<>(new CountDownLatch(1));
+        CountDownLatch unstallLatch = new CountDownLatch(1);
+        start(() -> new BufferingFlowControlStrategy(0.5f)
+        {
+            @Override
+            public void onStreamStalled(IStream stream)
+            {
+                super.onStreamStalled(stream);
+                stallLatch.get().countDown();
+            }
+
+            @Override
+            protected void onStreamUnstalled(IStream stream)
+            {
+                super.onStreamUnstalled(stream);
+                unstallLatch.countDown();
+            }
+        }, new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Request request = (MetaData.Request)frame.getMetaData();
+                MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
+
+                if (request.getURIString().endsWith("/stall"))
+                {
+                    stream.headers(new HeadersFrame(stream.getId(), response, null, false), new Callback()
+                    {
+                        @Override
+                        public void succeeded()
+                        {
+                            // Send a large chunk of data so the stream gets stalled.
+                            ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE + 1);
+                            stream.data(new DataFrame(stream.getId(), data, true), NOOP);
+                        }
+                    });
+                }
+                else
+                {
+                    stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
+                }
+
+                return null;
+            }
+        });
+
+        // Use a large session window so that only the stream gets stalled.
+        client.setInitialSessionRecvWindow(5 * FlowControlStrategy.DEFAULT_WINDOW_SIZE);
+        Session client = newClient(new Session.Listener.Adapter());
+
+        CountDownLatch latch = new CountDownLatch(1);
+        Queue<Callback> callbacks = new ArrayDeque<>();
+        MetaData.Request request = newRequest("GET", "/stall", new HttpFields());
+        client.newStream(new HeadersFrame(request, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callbacks.offer(callback);
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+
+        Assert.assertTrue(stallLatch.get().await(5, TimeUnit.SECONDS));
+
+        // First stream is now stalled, check that writing a second stream
+        // does not result in the first be notified again of being stalled.
+        stallLatch.set(new CountDownLatch(1));
+
+        request = newRequest("GET", "/", new HttpFields());
+        client.newStream(new HeadersFrame(request, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter());
+
+        Assert.assertFalse(stallLatch.get().await(1, TimeUnit.SECONDS));
+
+        // Consume all data.
+        while (!latch.await(10, TimeUnit.MILLISECONDS))
+        {
+            Callback callback = callbacks.poll();
+            if (callback != null)
+                callback.succeeded();
+        }
+
+        // Make sure the unstall callback is invoked.
+        Assert.assertTrue(unstallLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testSessionStalledIsInvokedOnlyOnce() throws Exception
+    {
+        AtomicReference<CountDownLatch> stallLatch = new AtomicReference<>(new CountDownLatch(1));
+        CountDownLatch unstallLatch = new CountDownLatch(1);
+        start(() -> new BufferingFlowControlStrategy(0.5f)
+        {
+            @Override
+            public void onSessionStalled(ISession session)
+            {
+                super.onSessionStalled(session);
+                stallLatch.get().countDown();
+            }
+
+            @Override
+            protected void onSessionUnstalled(ISession session)
+            {
+                super.onSessionUnstalled(session);
+                unstallLatch.countDown();
+            }
+        }, new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Request request = (MetaData.Request)frame.getMetaData();
+                MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
+
+                if (request.getURIString().endsWith("/stall"))
+                {
+                    stream.headers(new HeadersFrame(stream.getId(), response, null, false), new Callback()
+                    {
+                        @Override
+                        public void succeeded()
+                        {
+                            // Send a large chunk of data so the session gets stalled.
+                            ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE + 1);
+                            stream.data(new DataFrame(stream.getId(), data, true), NOOP);
+                        }
+                    });
+                }
+                else
+                {
+                    stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
+                }
+
+                return null;
+            }
+        });
+
+        // Use a large stream window so that only the session gets stalled.
+        client.setInitialStreamRecvWindow(5 * FlowControlStrategy.DEFAULT_WINDOW_SIZE);
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public Map<Integer, Integer> onPreface(Session session)
+            {
+                Map<Integer, Integer> settings = new HashMap<>();
+                settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, client.getInitialStreamRecvWindow());
+                return settings;
+            }
+        });
+
+        CountDownLatch latch = new CountDownLatch(1);
+        Queue<Callback> callbacks = new ArrayDeque<>();
+        MetaData.Request request = newRequest("GET", "/stall", new HttpFields());
+        session.newStream(new HeadersFrame(request, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callbacks.offer(callback);
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+
+        Assert.assertTrue(stallLatch.get().await(5, TimeUnit.SECONDS));
+
+        // The session is now stalled, check that writing a second stream
+        // does not result in the session be notified again of being stalled.
+        stallLatch.set(new CountDownLatch(1));
+
+        request = newRequest("GET", "/", new HttpFields());
+        session.newStream(new HeadersFrame(request, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter());
+
+        Assert.assertFalse(stallLatch.get().await(1, TimeUnit.SECONDS));
+
+        // Consume all data.
+        while (!latch.await(10, TimeUnit.MILLISECONDS))
+        {
+            Callback callback = callbacks.poll();
+            if (callback != null)
+                callback.succeeded();
+        }
+
+        // Make sure the unstall callback is invoked.
+        Assert.assertTrue(unstallLatch.await(5, TimeUnit.SECONDS));
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/FlowControlStrategyTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/FlowControlStrategyTest.java
new file mode 100644
index 0000000..7abb44c
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/FlowControlStrategyTest.java
@@ -0,0 +1,963 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Exchanger;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.FlowControlStrategy;
+import org.eclipse.jetty.http2.HTTP2Session;
+import org.eclipse.jetty.http2.HTTP2Stream;
+import org.eclipse.jetty.http2.ISession;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FutureCallback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+public abstract class FlowControlStrategyTest
+{
+    @Rule
+    public TestTracker tracker = new TestTracker();
+    protected ServerConnector connector;
+    protected HTTP2Client client;
+    protected Server server;
+
+    protected abstract FlowControlStrategy newFlowControlStrategy();
+
+    protected void start(ServerSessionListener listener) throws Exception
+    {
+        QueuedThreadPool serverExecutor = new QueuedThreadPool();
+        serverExecutor.setName("server");
+        server = new Server(serverExecutor);
+        RawHTTP2ServerConnectionFactory connectionFactory = new RawHTTP2ServerConnectionFactory(new HttpConfiguration(), listener);
+        connectionFactory.setFlowControlStrategyFactory(FlowControlStrategyTest.this::newFlowControlStrategy);
+        connector = new ServerConnector(server, connectionFactory);
+        server.addConnector(connector);
+        server.start();
+
+        client = new HTTP2Client();
+        QueuedThreadPool clientExecutor = new QueuedThreadPool();
+        clientExecutor.setName("client");
+        client.setExecutor(clientExecutor);
+        client.setFlowControlStrategyFactory(FlowControlStrategyTest.this::newFlowControlStrategy);
+        client.start();
+    }
+
+    protected Session newClient(Session.Listener listener) throws Exception
+    {
+        String host = "localhost";
+        int port = connector.getLocalPort();
+        InetSocketAddress address = new InetSocketAddress(host, port);
+        FuturePromise<Session> promise = new FuturePromise<>();
+        client.connect(address, listener, promise);
+        return promise.get(5, TimeUnit.SECONDS);
+    }
+
+    protected MetaData.Request newRequest(String method, HttpFields fields)
+    {
+        String host = "localhost";
+        int port = connector.getLocalPort();
+        String authority = host + ":" + port;
+        return new MetaData.Request(method, HttpScheme.HTTP, new HostPortHttpField(authority), "/", HttpVersion.HTTP_2, fields);
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        // Allow WINDOW_UPDATE frames to be sent/received to avoid exception stack traces.
+        Thread.sleep(1000);
+        client.stop();
+        server.stop();
+    }
+
+    @Test
+    public void testWindowSizeUpdates() throws Exception
+    {
+        final CountDownLatch prefaceLatch = new CountDownLatch(1);
+        final CountDownLatch stream1Latch = new CountDownLatch(1);
+        final CountDownLatch stream2Latch = new CountDownLatch(1);
+        final CountDownLatch settingsLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Map<Integer, Integer> onPreface(Session session)
+            {
+                HTTP2Session serverSession = (HTTP2Session)session;
+                Assert.assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, serverSession.getSendWindow());
+                Assert.assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, serverSession.getRecvWindow());
+                prefaceLatch.countDown();
+                return null;
+            }
+
+            @Override
+            public void onSettings(Session session, SettingsFrame frame)
+            {
+                for (Stream stream : session.getStreams())
+                {
+                    HTTP2Stream serverStream = (HTTP2Stream)stream;
+                    Assert.assertEquals(0, serverStream.getSendWindow());
+                    Assert.assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, serverStream.getRecvWindow());
+                }
+                settingsLatch.countDown();
+            }
+
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                HTTP2Stream serverStream = (HTTP2Stream)stream;
+                MetaData.Request request = (MetaData.Request)frame.getMetaData();
+                if ("GET".equalsIgnoreCase(request.getMethod()))
+                {
+                    Assert.assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, serverStream.getSendWindow());
+                    Assert.assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, serverStream.getRecvWindow());
+                    stream1Latch.countDown();
+                }
+                else
+                {
+                    Assert.assertEquals(0, serverStream.getSendWindow());
+                    Assert.assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, serverStream.getRecvWindow());
+                    stream2Latch.countDown();
+                }
+                return null;
+            }
+        });
+
+        HTTP2Session clientSession = (HTTP2Session)newClient(new Session.Listener.Adapter());
+
+        Assert.assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, clientSession.getSendWindow());
+        Assert.assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, clientSession.getRecvWindow());
+        Assert.assertTrue(prefaceLatch.await(5, TimeUnit.SECONDS));
+
+        MetaData.Request request1 = newRequest("GET", new HttpFields());
+        FuturePromise<Stream> promise1 = new FuturePromise<>();
+        clientSession.newStream(new HeadersFrame(request1, null, true), promise1, new Stream.Listener.Adapter());
+        HTTP2Stream clientStream1 = (HTTP2Stream)promise1.get(5, TimeUnit.SECONDS);
+
+        Assert.assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, clientStream1.getSendWindow());
+        Assert.assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, clientStream1.getRecvWindow());
+        Assert.assertTrue(stream1Latch.await(5, TimeUnit.SECONDS));
+
+        // Send a SETTINGS frame that changes the window size.
+        // This tells the server that its stream send window must be updated,
+        // so on the client it's the receive window that must be updated.
+        Map<Integer, Integer> settings = new HashMap<>();
+        settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, 0);
+        SettingsFrame frame = new SettingsFrame(settings, false);
+        FutureCallback callback = new FutureCallback();
+        clientSession.settings(frame, callback);
+        callback.get(5, TimeUnit.SECONDS);
+
+        Assert.assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, clientStream1.getSendWindow());
+        Assert.assertEquals(0, clientStream1.getRecvWindow());
+        settingsLatch.await(5, TimeUnit.SECONDS);
+
+        // Now create a new stream, it must pick up the new value.
+        MetaData.Request request2 = newRequest("POST", new HttpFields());
+        FuturePromise<Stream> promise2 = new FuturePromise<>();
+        clientSession.newStream(new HeadersFrame(request2, null, true), promise2, new Stream.Listener.Adapter());
+        HTTP2Stream clientStream2 = (HTTP2Stream)promise2.get(5, TimeUnit.SECONDS);
+
+        Assert.assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, clientStream2.getSendWindow());
+        Assert.assertEquals(0, clientStream2.getRecvWindow());
+        Assert.assertTrue(stream2Latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testFlowControlWithConcurrentSettings() throws Exception
+    {
+        // Initial window is 64 KiB. We allow the client to send 1024 B
+        // then we change the window to 512 B. At this point, the client
+        // must stop sending data (although the initial window allows it).
+
+        final int size = 512;
+        // We get 3 data frames: the first of 1024 and 2 of 512 each
+        // after the flow control window has been reduced.
+        final CountDownLatch dataLatch = new CountDownLatch(3);
+        final AtomicReference<Callback> callbackRef = new AtomicReference<>();
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
+            {
+                HttpFields fields = new HttpFields();
+                MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, 200, fields);
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), response, null, true);
+                stream.headers(responseFrame, Callback.NOOP);
+
+                return new Stream.Listener.Adapter()
+                {
+                    private final AtomicInteger dataFrames = new AtomicInteger();
+
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        dataLatch.countDown();
+                        int dataFrameCount = dataFrames.incrementAndGet();
+                        if (dataFrameCount == 1)
+                        {
+                            callbackRef.set(callback);
+                            Map<Integer, Integer> settings = new HashMap<>();
+                            settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, size);
+                            stream.getSession().settings(new SettingsFrame(settings, false), Callback.NOOP);
+                            // Do not succeed the callback here.
+                        }
+                        else if (dataFrameCount > 1)
+                        {
+                            // Consume the data.
+                            callback.succeeded();
+                        }
+                    }
+                };
+            }
+        });
+
+        // Two SETTINGS frames, the initial one and the one we send from the server.
+        final CountDownLatch settingsLatch = new CountDownLatch(2);
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public void onSettings(Session session, SettingsFrame frame)
+            {
+                settingsLatch.countDown();
+            }
+        });
+
+        MetaData.Request request = newRequest("POST", new HttpFields());
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        session.newStream(new HeadersFrame(request, null, false), promise, new Stream.Listener.Adapter());
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+
+        // Send first chunk that exceeds the window.
+        stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(size * 2), false), Callback.NOOP);
+        settingsLatch.await(5, TimeUnit.SECONDS);
+
+        // Send the second chunk of data, must not arrive since we're flow control stalled on the client.
+        stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(size * 2), true), Callback.NOOP);
+        Assert.assertFalse(dataLatch.await(1, TimeUnit.SECONDS));
+
+        // Consume the data arrived to server, this will resume flow control on the client.
+        callbackRef.get().succeeded();
+
+        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testServerFlowControlOneBigWrite() throws Exception
+    {
+        final int windowSize = 1536;
+        final int length = 5 * windowSize;
+        final CountDownLatch settingsLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public void onSettings(Session session, SettingsFrame frame)
+            {
+                settingsLatch.countDown();
+            }
+
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
+            {
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, false);
+                stream.headers(responseFrame, Callback.NOOP);
+
+                DataFrame dataFrame = new DataFrame(stream.getId(), ByteBuffer.allocate(length), true);
+                stream.data(dataFrame, Callback.NOOP);
+                return null;
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+
+        Map<Integer, Integer> settings = new HashMap<>();
+        settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, windowSize);
+        session.settings(new SettingsFrame(settings, false), Callback.NOOP);
+
+        Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
+
+        final CountDownLatch dataLatch = new CountDownLatch(1);
+        final Exchanger<Callback> exchanger = new Exchanger<>();
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
+        session.newStream(requestFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            private AtomicInteger dataFrames = new AtomicInteger();
+
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                try
+                {
+                    int dataFrames = this.dataFrames.incrementAndGet();
+                    if (dataFrames == 1 || dataFrames == 2)
+                    {
+                        // Do not consume the data frame.
+                        // We should then be flow-control stalled.
+                        exchanger.exchange(callback);
+                    }
+                    else if (dataFrames == 3 || dataFrames == 4 || dataFrames == 5)
+                    {
+                        // Consume totally.
+                        callback.succeeded();
+                        if (frame.isEndStream())
+                            dataLatch.countDown();
+                    }
+                    else
+                    {
+                        Assert.fail();
+                    }
+                }
+                catch (InterruptedException x)
+                {
+                    callback.failed(x);
+                }
+            }
+        });
+
+        Callback callback = exchanger.exchange(null, 5, TimeUnit.SECONDS);
+        checkThatWeAreFlowControlStalled(exchanger);
+
+        // Consume the first chunk.
+        callback.succeeded();
+
+        callback = exchanger.exchange(null, 5, TimeUnit.SECONDS);
+        checkThatWeAreFlowControlStalled(exchanger);
+
+        // Consume the second chunk.
+        callback.succeeded();
+
+        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testClientFlowControlOneBigWrite() throws Exception
+    {
+        final int windowSize = 1536;
+        final Exchanger<Callback> exchanger = new Exchanger<>();
+        final CountDownLatch settingsLatch = new CountDownLatch(1);
+        final CountDownLatch dataLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Map<Integer, Integer> onPreface(Session session)
+            {
+                Map<Integer, Integer> settings = new HashMap<>();
+                settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, windowSize);
+                return settings;
+            }
+
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
+            {
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, false);
+                stream.headers(responseFrame, Callback.NOOP);
+                return new Stream.Listener.Adapter()
+                {
+                    private AtomicInteger dataFrames = new AtomicInteger();
+
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        try
+                        {
+                            int dataFrames = this.dataFrames.incrementAndGet();
+                            if (dataFrames == 1 || dataFrames == 2)
+                            {
+                                // Do not consume the data frame.
+                                // We should then be flow-control stalled.
+                                exchanger.exchange(callback);
+                            }
+                            else if (dataFrames == 3 || dataFrames == 4 || dataFrames == 5)
+                            {
+                                // Consume totally.
+                                callback.succeeded();
+                                if (frame.isEndStream())
+                                    dataLatch.countDown();
+                            }
+                            else
+                            {
+                                Assert.fail();
+                            }
+                        }
+                        catch (InterruptedException x)
+                        {
+                            callback.failed(x);
+                        }
+                    }
+                };
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public void onSettings(Session session, SettingsFrame frame)
+            {
+                settingsLatch.countDown();
+            }
+        });
+
+        Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
+
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
+        FuturePromise<Stream> streamPromise = new FuturePromise<>();
+        session.newStream(requestFrame, streamPromise, null);
+        Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
+
+        final int length = 5 * windowSize;
+        DataFrame dataFrame = new DataFrame(stream.getId(), ByteBuffer.allocate(length), true);
+        stream.data(dataFrame, Callback.NOOP);
+
+        Callback callback = exchanger.exchange(null, 5, TimeUnit.SECONDS);
+        checkThatWeAreFlowControlStalled(exchanger);
+
+        // Consume the first chunk.
+        callback.succeeded();
+
+        callback = exchanger.exchange(null, 5, TimeUnit.SECONDS);
+        checkThatWeAreFlowControlStalled(exchanger);
+
+        // Consume the second chunk.
+        callback.succeeded();
+
+        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    private void checkThatWeAreFlowControlStalled(Exchanger<Callback> exchanger) throws Exception
+    {
+        try
+        {
+            exchanger.exchange(null, 1, TimeUnit.SECONDS);
+            Assert.fail();
+        }
+        catch (TimeoutException x)
+        {
+            // Expected.
+        }
+    }
+
+    @Test
+    public void testSessionStalledStallsNewStreams() throws Exception
+    {
+        final int windowSize = 1024;
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
+            {
+                MetaData.Request request = (MetaData.Request)requestFrame.getMetaData();
+                if ("POST".equalsIgnoreCase(request.getMethod()))
+                {
+                    // Send data to consume most of the session window.
+                    ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE - windowSize);
+                    DataFrame dataFrame = new DataFrame(stream.getId(), data, true);
+                    stream.data(dataFrame, Callback.NOOP);
+                    return null;
+                }
+                else
+                {
+                    // For every stream, send down half the window size of data.
+                    MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                    HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, false);
+                    stream.headers(responseFrame, Callback.NOOP);
+                    DataFrame dataFrame = new DataFrame(stream.getId(), ByteBuffer.allocate(windowSize / 2), true);
+                    stream.data(dataFrame, Callback.NOOP);
+                    return null;
+                }
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+
+        // First request is just to consume most of the session window.
+        final List<Callback> callbacks1 = new ArrayList<>();
+        final CountDownLatch prepareLatch = new CountDownLatch(1);
+        MetaData.Request request1 = newRequest("POST", new HttpFields());
+        session.newStream(new HeadersFrame(request1, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                // Do not consume the data to reduce the session window.
+                callbacks1.add(callback);
+                if (frame.isEndStream())
+                    prepareLatch.countDown();
+            }
+        });
+        Assert.assertTrue(prepareLatch.await(5, TimeUnit.SECONDS));
+
+        // Second request will consume half of the remaining the session window.
+        MetaData.Request request2 = newRequest("GET", new HttpFields());
+        session.newStream(new HeadersFrame(request2, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                // Do not consume it to stall flow control.
+            }
+        });
+
+        // Third request will consume the whole session window, which is now stalled.
+        // A fourth request will not be able to receive data.
+        MetaData.Request request3 = newRequest("GET", new HttpFields());
+        session.newStream(new HeadersFrame(request3, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                // Do not consume it to stall flow control.
+            }
+        });
+
+        // Fourth request is now stalled.
+        final CountDownLatch latch = new CountDownLatch(1);
+        MetaData.Request request4 = newRequest("GET", new HttpFields());
+        session.newStream(new HeadersFrame(request4, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+
+        // Verify that the data does not arrive because the server session is stalled.
+        Assert.assertFalse(latch.await(1, TimeUnit.SECONDS));
+
+        // Consume the data of the first response.
+        // This will open up the session window, allowing the fourth stream to send data.
+        for (Callback callback : callbacks1)
+            callback.succeeded();
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testServerSendsBigContent() throws Exception
+    {
+        final byte[] data = new byte[1024 * 1024];
+        new Random().nextBytes(data);
+
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
+            {
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, false);
+                stream.headers(responseFrame, Callback.NOOP);
+                DataFrame dataFrame = new DataFrame(stream.getId(), ByteBuffer.wrap(data), true);
+                stream.data(dataFrame, Callback.NOOP);
+                return null;
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
+        final byte[] bytes = new byte[data.length];
+        final CountDownLatch latch = new CountDownLatch(1);
+        session.newStream(requestFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            private int received;
+
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                int remaining = frame.remaining();
+                frame.getData().get(bytes, received, remaining);
+                this.received += remaining;
+                callback.succeeded();
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+
+        Assert.assertTrue(latch.await(15, TimeUnit.SECONDS));
+        Assert.assertArrayEquals(data, bytes);
+    }
+
+    @Test
+    public void testServerTwoDataFramesWithStalledStream() throws Exception
+    {
+        // Frames in queue = DATA1, DATA2.
+        // Server writes part of DATA1, then stalls.
+        // A window update unstalls the session, verify that the data is correctly sent.
+
+        Random random = new Random();
+        final byte[] chunk1 = new byte[1024];
+        random.nextBytes(chunk1);
+        final byte[] chunk2 = new byte[2048];
+        random.nextBytes(chunk2);
+
+        // Two SETTINGS frames: the initial after the preface,
+        // and the explicit where we set the stream window size to zero.
+        final AtomicReference<CountDownLatch> settingsLatch = new AtomicReference<>(new CountDownLatch(2));
+        final CountDownLatch dataLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public void onSettings(Session session, SettingsFrame frame)
+            {
+                settingsLatch.get().countDown();
+            }
+
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                stream.data(new DataFrame(stream.getId(), ByteBuffer.wrap(chunk1), false), Callback.NOOP);
+                stream.data(new DataFrame(stream.getId(), ByteBuffer.wrap(chunk2), true), Callback.NOOP);
+                dataLatch.countDown();
+                return null;
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        Map<Integer, Integer> settings = new HashMap<>();
+        settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, 0);
+        session.settings(new SettingsFrame(settings, false), Callback.NOOP);
+        Assert.assertTrue(settingsLatch.get().await(5, TimeUnit.SECONDS));
+
+        byte[] content = new byte[chunk1.length + chunk2.length];
+        final ByteBuffer buffer = ByteBuffer.wrap(content);
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
+        final CountDownLatch responseLatch = new CountDownLatch(1);
+        session.newStream(requestFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                buffer.put(frame.getData());
+                callback.succeeded();
+                if (frame.isEndStream())
+                    responseLatch.countDown();
+            }
+        });
+        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+
+        // Now we have the 2 DATA frames queued in the server.
+
+        // Unstall the stream window.
+        settingsLatch.set(new CountDownLatch(1));
+        settings.clear();
+        settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, chunk1.length / 2);
+        session.settings(new SettingsFrame(settings, false), Callback.NOOP);
+        Assert.assertTrue(settingsLatch.get().await(5, TimeUnit.SECONDS));
+
+        Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+
+        // Check that the data is sent correctly.
+        byte[] expected = new byte[content.length];
+        System.arraycopy(chunk1, 0, expected, 0, chunk1.length);
+        System.arraycopy(chunk2, 0, expected, chunk1.length, chunk2.length);
+        Assert.assertArrayEquals(expected, content);
+    }
+
+    @Test
+    public void testClientSendingInitialSmallWindow() throws Exception
+    {
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                MetaData metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, false);
+                stream.headers(responseFrame, Callback.NOOP);
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        // Since we echo back the data
+                        // asynchronously we must copy it.
+                        ByteBuffer data = frame.getData();
+                        ByteBuffer copy = ByteBuffer.allocateDirect(data.remaining());
+                        copy.put(data).flip();
+                        stream.data(new DataFrame(stream.getId(), copy, frame.isEndStream()), callback);
+                    }
+                };
+            }
+        });
+
+        final int initialWindow = 16;
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public Map<Integer, Integer> onPreface(Session session)
+            {
+                Map<Integer, Integer> settings = new HashMap<>();
+                settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, initialWindow);
+                return settings;
+            }
+        });
+
+        byte[] requestData = new byte[initialWindow * 4];
+        new Random().nextBytes(requestData);
+
+        byte[] responseData = new byte[requestData.length];
+        final ByteBuffer responseContent = ByteBuffer.wrap(responseData);
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
+        FuturePromise<Stream> streamPromise = new FuturePromise<>();
+        final CountDownLatch latch = new CountDownLatch(1);
+        session.newStream(requestFrame, streamPromise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                responseContent.put(frame.getData());
+                callback.succeeded();
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+        Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
+
+        ByteBuffer requestContent = ByteBuffer.wrap(requestData);
+        DataFrame dataFrame = new DataFrame(stream.getId(), requestContent, true);
+        stream.data(dataFrame, Callback.NOOP);
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+
+        responseContent.flip();
+        Assert.assertArrayEquals(requestData, responseData);
+    }
+
+    @Test
+    public void testClientExceedingSessionWindow() throws Exception
+    {
+        // On server, we don't consume the data.
+        start(new ServerSessionListener.Adapter());
+
+        final CountDownLatch closeLatch = new CountDownLatch(1);
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public void onClose(Session session, GoAwayFrame frame)
+            {
+                if (frame.getError() == ErrorCode.FLOW_CONTROL_ERROR.code)
+                    closeLatch.countDown();
+            }
+        });
+
+        // Consume the whole session and stream window.
+        MetaData.Request metaData = newRequest("POST", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
+        FuturePromise<Stream> streamPromise = new FuturePromise<>();
+        session.newStream(requestFrame, streamPromise, new Stream.Listener.Adapter());
+        Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
+        ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
+        final CountDownLatch dataLatch = new CountDownLatch(1);
+        stream.data(new DataFrame(stream.getId(), data, false), new Callback.NonBlocking()
+        {
+            @Override
+            public void succeeded()
+            {
+                dataLatch.countDown();
+            }
+        });
+        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+
+        // The following "sneaky" write may clash with the write
+        // of the reply SETTINGS frame sent by the client in
+        // response to the server SETTINGS frame.
+        // It is not enough to use a latch on the server to
+        // wait for the reply frame, since the client may have
+        // sent the bytes, but not yet be ready to write again.
+        Thread.sleep(1000);
+
+        // Now the client is supposed to not send more frames.
+        // If it does, the connection must be closed.
+        HTTP2Session http2Session = (HTTP2Session)session;
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(connector.getByteBufferPool());
+        ByteBuffer extraData = ByteBuffer.allocate(1024);
+        http2Session.getGenerator().data(lease, new DataFrame(stream.getId(), extraData, true), extraData.remaining());
+        List<ByteBuffer> buffers = lease.getByteBuffers();
+        http2Session.getEndPoint().write(Callback.NOOP, buffers.toArray(new ByteBuffer[buffers.size()]));
+
+        // Expect the connection to be closed.
+        Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testClientExceedingStreamWindow() throws Exception
+    {
+        // On server, we don't consume the data.
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Map<Integer, Integer> onPreface(Session session)
+            {
+                // Enlarge the session window.
+                ((ISession)session).updateRecvWindow(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
+                return super.onPreface(session);
+            }
+        });
+
+        final CountDownLatch closeLatch = new CountDownLatch(1);
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public void onClose(Session session, GoAwayFrame frame)
+            {
+                if (frame.getError() == ErrorCode.FLOW_CONTROL_ERROR.code)
+                    closeLatch.countDown();
+            }
+        });
+
+        // Consume the whole stream window.
+        MetaData.Request metaData = newRequest("POST", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
+        FuturePromise<Stream> streamPromise = new FuturePromise<>();
+        session.newStream(requestFrame, streamPromise, new Stream.Listener.Adapter());
+        Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
+        ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
+        final CountDownLatch dataLatch = new CountDownLatch(1);
+        stream.data(new DataFrame(stream.getId(), data, false), new Callback.NonBlocking()
+        {
+            @Override
+            public void succeeded()
+            {
+                dataLatch.countDown();
+            }
+        });
+        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+
+        // Wait for a while before doing the "sneaky" write
+        // below, see comments in the previous test case.
+        Thread.sleep(1000);
+
+        // Now the client is supposed to not send more frames.
+        // If it does, the connection must be closed.
+        HTTP2Session http2Session = (HTTP2Session)session;
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(connector.getByteBufferPool());
+        ByteBuffer extraData = ByteBuffer.allocate(1024);
+        http2Session.getGenerator().data(lease, new DataFrame(stream.getId(), extraData, true), extraData.remaining());
+        List<ByteBuffer> buffers = lease.getByteBuffers();
+        http2Session.getEndPoint().write(Callback.NOOP, buffers.toArray(new ByteBuffer[buffers.size()]));
+
+        // Expect the connection to be closed.
+        Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testFlowControlWhenServerResetsStream() throws Exception
+    {
+        // On server, don't consume the data and immediately reset.
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Request request = (MetaData.Request)frame.getMetaData();
+
+                if (HttpMethod.GET.is(request.getMethod()))
+                    return new Stream.Listener.Adapter();
+
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        // Fail the callback to enlarge the session window.
+                        // More data frames will be discarded because the
+                        // stream is reset, and automatically consumed to
+                        // keep the session window large for other streams.
+                        callback.failed(new Throwable());
+                        stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+                    }
+                };
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        MetaData.Request metaData = newRequest("POST", new HttpFields());
+        HeadersFrame frame = new HeadersFrame(metaData, null, false);
+        FuturePromise<Stream> streamPromise = new FuturePromise<>();
+        final CountDownLatch resetLatch = new CountDownLatch(1);
+        session.newStream(frame, streamPromise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onReset(Stream stream, ResetFrame frame)
+            {
+                resetLatch.countDown();
+            }
+        });
+        Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
+
+        // Perform a big upload that will stall the flow control windows.
+        ByteBuffer data = ByteBuffer.allocate(5 * FlowControlStrategy.DEFAULT_WINDOW_SIZE);
+        final CountDownLatch dataLatch = new CountDownLatch(1);
+        stream.data(new DataFrame(stream.getId(), data, true), new Callback.NonBlocking()
+        {
+            @Override
+            public void failed(Throwable x)
+            {
+                dataLatch.countDown();
+            }
+        });
+
+        Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/HTTP2Test.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/HTTP2Test.java
new file mode 100644
index 0000000..15e3d36
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/HTTP2Test.java
@@ -0,0 +1,462 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Jetty;
+import org.eclipse.jetty.util.Promise;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HTTP2Test extends AbstractTest
+{
+    @Test
+    public void testRequestNoContentResponseNoContent() throws Exception
+    {
+        start(new EmptyHttpServlet());
+
+        Session session = newClient(new Session.Listener.Adapter());
+
+        HttpFields fields = new HttpFields();
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame = new HeadersFrame(metaData, null, true);
+        final CountDownLatch latch = new CountDownLatch(1);
+        session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                Assert.assertTrue(stream.getId() > 0);
+
+                Assert.assertTrue(frame.isEndStream());
+                Assert.assertEquals(stream.getId(), frame.getStreamId());
+                Assert.assertTrue(frame.getMetaData().isResponse());
+                MetaData.Response response = (MetaData.Response)frame.getMetaData();
+                Assert.assertEquals(200, response.getStatus());
+
+                latch.countDown();
+            }
+        });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testRequestNoContentResponseContent() throws Exception
+    {
+        final byte[] content = "Hello World!".getBytes(StandardCharsets.UTF_8);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+            {
+                resp.getOutputStream().write(content);
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+
+        HttpFields fields = new HttpFields();
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame = new HeadersFrame(metaData, null, true);
+        final CountDownLatch latch = new CountDownLatch(2);
+        session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                Assert.assertTrue(stream.getId() > 0);
+
+                Assert.assertFalse(frame.isEndStream());
+                Assert.assertEquals(stream.getId(), frame.getStreamId());
+                Assert.assertTrue(frame.getMetaData().isResponse());
+                MetaData.Response response = (MetaData.Response)frame.getMetaData();
+                Assert.assertEquals(200, response.getStatus());
+
+                latch.countDown();
+            }
+
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                Assert.assertTrue(frame.isEndStream());
+                Assert.assertEquals(ByteBuffer.wrap(content), frame.getData());
+
+                callback.succeeded();
+                latch.countDown();
+            }
+        });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testMultipleRequests() throws Exception
+    {
+        final String downloadBytes = "X-Download";
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                int download = request.getIntHeader(downloadBytes);
+                byte[] content = new byte[download];
+                new Random().nextBytes(content);
+                response.getOutputStream().write(content);
+            }
+        });
+
+        int requests = 20;
+        Session session = newClient(new Session.Listener.Adapter());
+
+        Random random = new Random();
+        HttpFields fields = new HttpFields();
+        fields.putLongField(downloadBytes, random.nextInt(128 * 1024));
+        fields.put("User-Agent", "HTTP2Client/" + Jetty.VERSION);
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame = new HeadersFrame(metaData, null, true);
+        final CountDownLatch latch = new CountDownLatch(requests);
+        for (int i = 0; i < requests; ++i)
+        {
+            session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+            {
+                @Override
+                public void onData(Stream stream, DataFrame frame, Callback callback)
+                {
+                    callback.succeeded();
+                    if (frame.isEndStream())
+                        latch.countDown();
+                }
+            });
+        }
+
+        Assert.assertTrue(latch.await(requests, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testCustomResponseCode() throws Exception
+    {
+        final int status = 475;
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                response.setStatus(status);
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        HttpFields fields = new HttpFields();
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame = new HeadersFrame(metaData, null, true);
+        final CountDownLatch latch = new CountDownLatch(1);
+        session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response response = (MetaData.Response)frame.getMetaData();
+                Assert.assertEquals(status, response.getStatus());
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testHostHeader() throws Exception
+    {
+        final String host = "fooBar";
+        final int port = 1313;
+        final String authority = host + ":" + port;
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                Assert.assertEquals(host, request.getServerName());
+                Assert.assertEquals(port, request.getServerPort());
+                Assert.assertEquals(authority, request.getHeader("Host"));
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        HostPortHttpField hostHeader = new HostPortHttpField(authority);
+        MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, hostHeader, servletPath, HttpVersion.HTTP_2, new HttpFields());
+        HeadersFrame frame = new HeadersFrame(metaData, null, true);
+        final CountDownLatch latch = new CountDownLatch(1);
+        session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response response = (MetaData.Response)frame.getMetaData();
+                Assert.assertEquals(200, response.getStatus());
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testServerSendsGoAwayOnStop() throws Exception
+    {
+        start(new ServerSessionListener.Adapter());
+
+        CountDownLatch closeLatch = new CountDownLatch(1);
+        newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public void onClose(Session session, GoAwayFrame frame)
+            {
+                closeLatch.countDown();
+            }
+        });
+
+        Thread.sleep(1000);
+
+        server.stop();
+
+        Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testClientSendsGoAwayOnStop() throws Exception
+    {
+        CountDownLatch closeLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public void onClose(Session session, GoAwayFrame frame)
+            {
+                closeLatch.countDown();
+            }
+        });
+
+        newClient(new Session.Listener.Adapter());
+
+        Thread.sleep(1000);
+
+        client.stop();
+
+        Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testMaxConcurrentStreams() throws Exception
+    {
+        int maxStreams = 2;
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Map<Integer, Integer> onPreface(Session session)
+            {
+                Map<Integer, Integer> settings = new HashMap<>(1);
+                settings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, maxStreams);
+                return settings;
+            }
+
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields(), 0);
+                stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
+                return null;
+            }
+        });
+
+        CountDownLatch settingsLatch = new CountDownLatch(1);
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public void onSettings(Session session, SettingsFrame frame)
+            {
+                settingsLatch.countDown();
+            }
+        });
+        Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
+
+        MetaData.Request request1 = newRequest("GET", new HttpFields());
+        FuturePromise<Stream> promise1 = new FuturePromise<>();
+        CountDownLatch exchangeLatch1 = new CountDownLatch(2);
+        session.newStream(new HeadersFrame(request1, null, false), promise1, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    exchangeLatch1.countDown();
+            }
+        });
+        Stream stream1 = promise1.get(5, TimeUnit.SECONDS);
+
+        MetaData.Request request2 = newRequest("GET", new HttpFields());
+        FuturePromise<Stream> promise2 = new FuturePromise<>();
+        CountDownLatch exchangeLatch2 = new CountDownLatch(2);
+        session.newStream(new HeadersFrame(request2, null, false), promise2, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    exchangeLatch2.countDown();
+            }
+        });
+        Stream stream2 = promise2.get(5, TimeUnit.SECONDS);
+
+        // The third stream must not be created.
+        MetaData.Request request3 = newRequest("GET", new HttpFields());
+        CountDownLatch maxStreamsLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(request3, null, false), new Promise.Adapter<Stream>()
+        {
+            @Override
+            public void failed(Throwable x)
+            {
+                if (x instanceof IllegalStateException)
+                    maxStreamsLatch.countDown();
+            }
+        }, new Stream.Listener.Adapter());
+
+        Assert.assertTrue(maxStreamsLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertEquals(2, session.getStreams().size());
+
+        // End the second stream.
+        stream2.data(new DataFrame(stream2.getId(), BufferUtil.EMPTY_BUFFER, true), new Callback()
+        {
+            @Override
+            public void succeeded()
+            {
+                exchangeLatch2.countDown();
+            }
+        });
+        Assert.assertTrue(exchangeLatch2.await(5, TimeUnit.SECONDS));
+        Assert.assertEquals(1, session.getStreams().size());
+
+        // Create a fourth stream.
+        MetaData.Request request4 = newRequest("GET", new HttpFields());
+        CountDownLatch exchangeLatch4 = new CountDownLatch(2);
+        session.newStream(new HeadersFrame(request4, null, true), new Promise.Adapter<Stream>()
+        {
+            @Override
+            public void succeeded(Stream result)
+            {
+                exchangeLatch4.countDown();
+            }
+        }, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    exchangeLatch4.countDown();
+            }
+        });
+        Assert.assertTrue(exchangeLatch4.await(5, TimeUnit.SECONDS));
+        Assert.assertEquals(1, session.getStreams().size());
+
+        // End the first stream.
+        stream1.data(new DataFrame(stream1.getId(), BufferUtil.EMPTY_BUFFER, true), new Callback()
+        {
+            @Override
+            public void succeeded()
+            {
+                exchangeLatch1.countDown();
+            }
+        });
+        Assert.assertTrue(exchangeLatch2.await(5, TimeUnit.SECONDS));
+        Assert.assertEquals(0, session.getStreams().size());
+    }
+
+    @Test
+    public void testCleanGoAwayDoesNotTriggerFailureNotification() throws Exception
+    {
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
+                HeadersFrame response = new HeadersFrame(stream.getId(), metaData, null, true);
+                stream.headers(response, Callback.NOOP);
+                // Close cleanly.
+                stream.getSession().close(ErrorCode.NO_ERROR.code, null, Callback.NOOP);
+                return null;
+            }
+        });
+
+        CountDownLatch closeLatch = new CountDownLatch(1);
+        CountDownLatch failureLatch = new CountDownLatch(1);
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public void onClose(Session session, GoAwayFrame frame)
+            {
+                closeLatch.countDown();
+            }
+
+            @Override
+            public void onFailure(Session session, Throwable failure)
+            {
+                failureLatch.countDown();
+            }
+        });
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame request = new HeadersFrame(metaData, null, true);
+        session.newStream(request, new Promise.Adapter<>(), new Stream.Listener.Adapter());
+
+        // Make sure onClose() is called.
+        Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertFalse(failureLatch.await(1, TimeUnit.SECONDS));
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/IdleTimeoutTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/IdleTimeoutTest.java
new file mode 100644
index 0000000..c339161
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/IdleTimeoutTest.java
@@ -0,0 +1,589 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.FlowControlStrategy;
+import org.eclipse.jetty.http2.HTTP2Session;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.log.Log;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IdleTimeoutTest extends AbstractTest
+{
+    private final int idleTimeout = 1000;
+
+    @Test
+    public void testServerEnforcingIdleTimeout() throws Exception
+    {
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
+                stream.headers(responseFrame, Callback.NOOP);
+                return null;
+            }
+        });
+        connector.setIdleTimeout(idleTimeout);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public void onClose(Session session, GoAwayFrame frame)
+            {
+                latch.countDown();
+            }
+        });
+
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
+        session.newStream(requestFrame, new Promise.Adapter<Stream>()
+        {
+            @Override
+            public void succeeded(Stream stream)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+            }
+        }, new Stream.Listener.Adapter());
+
+        Assert.assertTrue(latch.await(5 * idleTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testServerEnforcingIdleTimeoutWithUnrespondedStream() throws Exception
+    {
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+                return null;
+            }
+        });
+        connector.setIdleTimeout(idleTimeout);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public void onClose(Session session, GoAwayFrame frame)
+            {
+                latch.countDown();
+            }
+        });
+
+        // The request is not replied, and the server should idle timeout.
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
+        session.newStream(requestFrame, new Promise.Adapter<Stream>()
+        {
+            @Override
+            public void succeeded(Stream stream)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+            }
+        }, new Stream.Listener.Adapter());
+
+        Assert.assertTrue(latch.await(5 * idleTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testServerNotEnforcingIdleTimeoutWithinCallback() throws Exception
+    {
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+                // Stay in the callback for more than idleTimeout,
+                // but not for an integer number of idle timeouts,
+                // to avoid a race where the idle timeout fires
+                // again before we can send the headers to the client.
+                sleep(idleTimeout + idleTimeout / 2);
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
+                stream.headers(responseFrame, Callback.NOOP);
+                return null;
+            }
+        });
+        connector.setIdleTimeout(idleTimeout);
+
+        final CountDownLatch closeLatch = new CountDownLatch(1);
+        Session session = newClient(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public void onClose(Session session, GoAwayFrame frame)
+            {
+                closeLatch.countDown();
+            }
+        });
+
+        final CountDownLatch replyLatch = new CountDownLatch(1);
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
+        session.newStream(requestFrame, new Promise.Adapter<Stream>()
+        {
+            @Override
+            public void succeeded(Stream stream)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+            }
+        }, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                replyLatch.countDown();
+            }
+        });
+
+        Assert.assertTrue(replyLatch.await(5 * idleTimeout, TimeUnit.MILLISECONDS));
+
+        // Just make sure onClose() has never been called, but don't wait too much
+        Assert.assertFalse(closeLatch.await(idleTimeout / 2, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testClientEnforcingIdleTimeout() throws Exception
+    {
+        final CountDownLatch closeLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
+                stream.headers(responseFrame, Callback.NOOP);
+                return null;
+            }
+
+            @Override
+            public void onClose(Session session, GoAwayFrame frame)
+            {
+                closeLatch.countDown();
+            }
+        });
+        client.setIdleTimeout(idleTimeout);
+
+        Session session = newClient(new Session.Listener.Adapter());
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
+        session.newStream(requestFrame, new Promise.Adapter<Stream>()
+        {
+            @Override
+            public void succeeded(Stream stream)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+            }
+        }, new Stream.Listener.Adapter());
+
+        Assert.assertTrue(closeLatch.await(5 * idleTimeout, TimeUnit.MILLISECONDS));
+        Assert.assertTrue(session.isClosed());
+    }
+
+    @Test
+    public void testClientEnforcingIdleTimeoutWithUnrespondedStream() throws Exception
+    {
+        final CountDownLatch closeLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+                return null;
+            }
+
+            @Override
+            public void onClose(Session session, GoAwayFrame frame)
+            {
+                closeLatch.countDown();
+            }
+        });
+        client.setIdleTimeout(idleTimeout);
+
+        Session session = newClient(new Session.Listener.Adapter());
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
+        session.newStream(requestFrame, new Promise.Adapter<Stream>()
+        {
+            @Override
+            public void succeeded(Stream stream)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+            }
+        }, new Stream.Listener.Adapter());
+
+        Assert.assertTrue(closeLatch.await(5 * idleTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testClientNotEnforcingIdleTimeoutWithinCallback() throws Exception
+    {
+        final CountDownLatch closeLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
+                stream.headers(responseFrame, Callback.NOOP);
+                return null;
+            }
+
+            @Override
+            public void onClose(Session session, GoAwayFrame frame)
+            {
+                closeLatch.countDown();
+            }
+        });
+        client.setIdleTimeout(idleTimeout);
+
+        Session session = newClient(new Session.Listener.Adapter());
+
+        final CountDownLatch replyLatch = new CountDownLatch(1);
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
+        session.newStream(requestFrame, new Promise.Adapter<Stream>()
+        {
+            @Override
+            public void succeeded(Stream stream)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+            }
+        }, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                // Stay in the callback for more than idleTimeout,
+                // but not for an integer number of idle timeouts,
+                // to avoid that the idle timeout fires again.
+                sleep(idleTimeout + idleTimeout / 2);
+                replyLatch.countDown();
+            }
+        });
+
+        Assert.assertFalse(closeLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+        Assert.assertTrue(replyLatch.await(5 * idleTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testClientEnforcingStreamIdleTimeout() throws Exception
+    {
+        final int idleTimeout = 1000;
+        start(new HttpServlet()
+        {
+            @Override
+            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+            {
+                sleep(2 * idleTimeout);
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+
+        final CountDownLatch dataLatch = new CountDownLatch(1);
+        final CountDownLatch timeoutLatch = new CountDownLatch(1);
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
+        session.newStream(requestFrame, new Promise.Adapter<Stream>()
+        {
+            @Override
+            public void succeeded(Stream stream)
+            {
+                stream.setIdleTimeout(idleTimeout);
+            }
+        }, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                dataLatch.countDown();
+            }
+
+            @Override
+            public boolean onIdleTimeout(Stream stream, Throwable x)
+            {
+                Assert.assertThat(x, Matchers.instanceOf(TimeoutException.class));
+                timeoutLatch.countDown();
+                return true;
+            }
+        });
+
+        Assert.assertTrue(timeoutLatch.await(5, TimeUnit.SECONDS));
+        // We must not receive any DATA frame.
+        Assert.assertFalse(dataLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+        // Stream must be gone.
+        Assert.assertTrue(session.getStreams().isEmpty());
+        // Session must not be closed, nor disconnected.
+        Assert.assertFalse(session.isClosed());
+        Assert.assertFalse(((HTTP2Session)session).isDisconnected());
+    }
+
+    @Test
+    public void testServerEnforcingStreamIdleTimeout() throws Exception
+    {
+        final CountDownLatch timeoutLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                stream.setIdleTimeout(idleTimeout);
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public boolean onIdleTimeout(Stream stream, Throwable x)
+                    {
+                        timeoutLatch.countDown();
+                        return true;
+                    }
+                };
+            }
+        });
+
+        final CountDownLatch resetLatch = new CountDownLatch(1);
+        Session session = newClient(new Session.Listener.Adapter());
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        // Stream does not end here, but we won't send any DATA frame.
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
+        session.newStream(requestFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onReset(Stream stream, ResetFrame frame)
+            {
+                resetLatch.countDown();
+            }
+        });
+
+        Assert.assertTrue(timeoutLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
+        // Stream must be gone.
+        Assert.assertTrue(session.getStreams().isEmpty());
+        // Session must not be closed, nor disconnected.
+        Assert.assertFalse(session.isClosed());
+        Assert.assertFalse(((HTTP2Session)session).isDisconnected());
+    }
+
+    @Test
+    public void testStreamIdleTimeoutIsNotEnforcedWhenReceiving() throws Exception
+    {
+        final CountDownLatch timeoutLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                stream.setIdleTimeout(idleTimeout);
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public boolean onIdleTimeout(Stream stream, Throwable x)
+                    {
+                        timeoutLatch.countDown();
+                        return true;
+                    }
+                };
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        session.newStream(requestFrame, promise, new Stream.Listener.Adapter());
+        final Stream stream = promise.get(5, TimeUnit.SECONDS);
+
+        sleep(idleTimeout / 2);
+        final CountDownLatch dataLatch = new CountDownLatch(1);
+        stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1), false), new Callback()
+        {
+            private int sends;
+
+            @Override
+            public void succeeded()
+            {
+                sleep(idleTimeout / 2);
+                final boolean last = ++sends == 2;
+                stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1), last), !last ? this : new Callback.NonBlocking()
+                {
+                    @Override
+                    public void succeeded()
+                    {
+                        // Idle timeout should not fire while receiving.
+                        Assert.assertEquals(1, timeoutLatch.getCount());
+                        dataLatch.countDown();
+                    }
+                });
+            }
+        });
+
+        Assert.assertTrue(dataLatch.await(5 * idleTimeout, TimeUnit.MILLISECONDS));
+        // The server did not send a response, so it will eventually timeout.
+        Assert.assertTrue(timeoutLatch.await(5 * idleTimeout, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testStreamIdleTimeoutIsNotEnforcedWhenSending() throws Exception
+    {
+        final CountDownLatch resetLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
+                return null;
+            }
+
+            @Override
+            public void onReset(Session session, ResetFrame frame)
+            {
+                resetLatch.countDown();
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
+        FuturePromise<Stream> promise = new FuturePromise<Stream>()
+        {
+            @Override
+            public void succeeded(Stream stream)
+            {
+                stream.setIdleTimeout(idleTimeout);
+                super.succeeded(stream);
+            }
+        };
+        session.newStream(requestFrame, promise, new Stream.Listener.Adapter());
+        final Stream stream = promise.get(5, TimeUnit.SECONDS);
+
+        sleep(idleTimeout / 2);
+        stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1), false), Callback.NOOP);
+        sleep(idleTimeout / 2);
+        stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1), false), Callback.NOOP);
+        sleep(idleTimeout / 2);
+        stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1), true), Callback.NOOP);
+
+        Assert.assertFalse(resetLatch.await(1, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testBufferedReadsResetStreamIdleTimeout() throws Exception
+    {
+        int bufferSize = 8192;
+        long delay = 1000;
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                ServletInputStream input = request.getInputStream();
+                byte[] buffer = new byte[bufferSize];
+                while (true)
+                {
+                    int read = input.read(buffer);
+                    Log.getLogger(IdleTimeoutTest.class).info("Read {} bytes", read);
+                    if (read < 0)
+                        break;
+                    sleep(delay);
+                }
+            }
+        });
+        connector.setIdleTimeout(2 * delay);
+
+        Session session = newClient(new Session.Listener.Adapter());
+        MetaData.Request metaData = newRequest("POST", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        CountDownLatch latch = new CountDownLatch(1);
+        session.newStream(requestFrame, promise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+
+        // Send data larger than the flow control window.
+        // The client will send bytes up to the flow control window immediately
+        // and they will be buffered by the server; the Servlet will consume them slowly.
+        // Servlet reads should reset the idle timeout.
+        int contentLength = FlowControlStrategy.DEFAULT_WINDOW_SIZE + 1;
+        ByteBuffer data = ByteBuffer.allocate(contentLength);
+        stream.data(new DataFrame(stream.getId(), data, true), Callback.NOOP);
+
+        Assert.assertTrue(latch.await(2 * (contentLength / bufferSize + 1) * delay, TimeUnit.MILLISECONDS));
+    }
+
+    private void sleep(long value)
+    {
+        try
+        {
+            TimeUnit.MILLISECONDS.sleep(value);
+        }
+        catch (InterruptedException x)
+        {
+            Assert.fail();
+        }
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PingTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PingTest.java
new file mode 100644
index 0000000..a6be2d5
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PingTest.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.PingFrame;
+import org.eclipse.jetty.util.Callback;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PingTest extends AbstractTest
+{
+    @Test
+    public void testPing() throws Exception
+    {
+        start(new ServerSessionListener.Adapter());
+
+        final byte[] payload = new byte[8];
+        new Random().nextBytes(payload);
+        final CountDownLatch latch = new CountDownLatch(1);
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public void onPing(Session session, PingFrame frame)
+            {
+                Assert.assertTrue(frame.isReply());
+                Assert.assertArrayEquals(payload, frame.getPayload());
+                latch.countDown();
+            }
+        });
+
+        PingFrame frame = new PingFrame(payload, false);
+        session.ping(frame, Callback.NOOP);
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PrefaceTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PrefaceTest.java
new file mode 100644
index 0000000..6bcf06e
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PrefaceTest.java
@@ -0,0 +1,328 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayDeque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PingFrame;
+import org.eclipse.jetty.http2.frames.PrefaceFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.generator.Generator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PrefaceTest extends AbstractTest
+{
+    @Test
+    public void testServerPrefaceReplySentAfterClientPreface() throws Exception
+    {
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public void onAccept(Session session)
+            {
+                // Send the server preface from here.
+                session.settings(new SettingsFrame(new HashMap<>(), false), Callback.NOOP);
+            }
+
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
+                stream.headers(responseFrame, Callback.NOOP);
+                return null;
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public Map<Integer, Integer> onPreface(Session session)
+            {
+                try
+                {
+                    // Wait for the server preface (a SETTINGS frame) to
+                    // arrive on the client, and for its reply to be sent.
+                    Thread.sleep(1000);
+                    return null;
+                }
+                catch (InterruptedException x)
+                {
+                    x.printStackTrace();
+                    return null;
+                }
+            }
+        });
+
+        CountDownLatch latch = new CountDownLatch(1);
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
+        session.newStream(requestFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testClientPrefaceReplySentAfterServerPreface() throws Exception
+    {
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Map<Integer, Integer> onPreface(Session session)
+            {
+                Map<Integer, Integer> settings = new HashMap<>();
+                settings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, 128);
+                return settings;
+            }
+
+            @Override
+            public void onPing(Session session, PingFrame frame)
+            {
+                session.close(ErrorCode.NO_ERROR.code, null, Callback.NOOP);
+            }
+        });
+
+        ByteBufferPool byteBufferPool = client.getByteBufferPool();
+        try (SocketChannel socket = SocketChannel.open())
+        {
+            socket.connect(new InetSocketAddress("localhost", connector.getLocalPort()));
+
+            Generator generator = new Generator(byteBufferPool);
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.control(lease, new PrefaceFrame());
+            Map<Integer, Integer> clientSettings = new HashMap<>();
+            clientSettings.put(SettingsFrame.ENABLE_PUSH, 0);
+            generator.control(lease, new SettingsFrame(clientSettings, false));
+            // The PING frame just to make sure the client stops reading.
+            generator.control(lease, new PingFrame(true));
+
+            List<ByteBuffer> buffers = lease.getByteBuffers();
+            socket.write(buffers.toArray(new ByteBuffer[buffers.size()]));
+
+            Queue<SettingsFrame> settings = new ArrayDeque<>();
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onSettings(SettingsFrame frame)
+                {
+                    settings.offer(frame);
+                }
+            }, 4096, 8192);
+
+            ByteBuffer buffer = byteBufferPool.acquire(1024, true);
+            while (true)
+            {
+                BufferUtil.clearToFill(buffer);
+                int read = socket.read(buffer);
+                BufferUtil.flipToFlush(buffer, 0);
+                if (read < 0)
+                    break;
+                parser.parse(buffer);
+            }
+
+            Assert.assertEquals(2, settings.size());
+            SettingsFrame frame1 = settings.poll();
+            Assert.assertFalse(frame1.isReply());
+            SettingsFrame frame2 = settings.poll();
+            Assert.assertTrue(frame2.isReply());
+        }
+    }
+
+    @Test
+    public void testOnPrefaceNotifiedForStandardUpgrade() throws Exception
+    {
+        Integer maxConcurrentStreams = 128;
+        AtomicReference<CountDownLatch> serverPrefaceLatch = new AtomicReference<>(new CountDownLatch(1));
+        AtomicReference<CountDownLatch> serverSettingsLatch = new AtomicReference<>(new CountDownLatch(1));
+        HttpConfiguration config = new HttpConfiguration();
+        prepareServer(new HttpConnectionFactory(config), new HTTP2CServerConnectionFactory(config)
+        {
+            @Override
+            protected ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint)
+            {
+                return new ServerSessionListener.Adapter()
+                {
+                    @Override
+                    public Map<Integer, Integer> onPreface(Session session)
+                    {
+                        Map<Integer, Integer> serverSettings = new HashMap<>();
+                        serverSettings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, maxConcurrentStreams);
+                        serverPrefaceLatch.get().countDown();
+                        return serverSettings;
+                    }
+
+                    @Override
+                    public void onSettings(Session session, SettingsFrame frame)
+                    {
+                        serverSettingsLatch.get().countDown();
+                    }
+
+                    @Override
+                    public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+                    {
+                        MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
+                        stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
+                        return null;
+                    }
+                };
+            }
+        });
+        server.start();
+
+        ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+        try (SocketChannel socket = SocketChannel.open())
+        {
+            socket.connect(new InetSocketAddress("localhost", connector.getLocalPort()));
+
+            String upgradeRequest = "" +
+                    "GET /one HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "Connection: Upgrade, HTTP2-Settings\r\n" +
+                    "Upgrade: h2c\r\n" +
+                    "HTTP2-Settings: \r\n" +
+                    "\r\n";
+            ByteBuffer upgradeBuffer = ByteBuffer.wrap(upgradeRequest.getBytes(StandardCharsets.ISO_8859_1));
+            socket.write(upgradeBuffer);
+
+            // Make sure onPreface() is called on server.
+            Assert.assertTrue(serverPrefaceLatch.get().await(5, TimeUnit.SECONDS));
+            Assert.assertTrue(serverSettingsLatch.get().await(5, TimeUnit.SECONDS));
+
+            // The 101 response is the reply to the client preface SETTINGS frame.
+            ByteBuffer buffer = byteBufferPool.acquire(1024, true);
+            http1: while (true)
+            {
+                BufferUtil.clearToFill(buffer);
+                int read = socket.read(buffer);
+                BufferUtil.flipToFlush(buffer, 0);
+                if (read < 0)
+                    Assert.fail();
+
+                int crlfs = 0;
+                while (buffer.hasRemaining())
+                {
+                    byte b = buffer.get();
+                    if (b == '\r' || b == '\n')
+                        ++crlfs;
+                    else
+                        crlfs = 0;
+                    if (crlfs == 4)
+                        break http1;
+                }
+            }
+
+            // Reset the latches on server.
+            serverPrefaceLatch.set(new CountDownLatch(1));
+            serverSettingsLatch.set(new CountDownLatch(1));
+
+            // After the 101, the client must send the connection preface.
+            Generator generator = new Generator(byteBufferPool);
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.control(lease, new PrefaceFrame());
+            Map<Integer, Integer> clientSettings = new HashMap<>();
+            clientSettings.put(SettingsFrame.ENABLE_PUSH, 1);
+            generator.control(lease, new SettingsFrame(clientSettings, false));
+            List<ByteBuffer> buffers = lease.getByteBuffers();
+            socket.write(buffers.toArray(new ByteBuffer[buffers.size()]));
+
+            // However, we should not call onPreface() again.
+            Assert.assertFalse(serverPrefaceLatch.get().await(1, TimeUnit.SECONDS));
+            // Although we should notify of the SETTINGS frame.
+            Assert.assertTrue(serverSettingsLatch.get().await(5, TimeUnit.SECONDS));
+
+            CountDownLatch clientSettingsLatch = new CountDownLatch(1);
+            AtomicBoolean responded = new AtomicBoolean();
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onSettings(SettingsFrame frame)
+                {
+                    if (frame.isReply())
+                        return;
+                    Assert.assertEquals(maxConcurrentStreams, frame.getSettings().get(SettingsFrame.MAX_CONCURRENT_STREAMS));
+                    clientSettingsLatch.countDown();
+                }
+
+                @Override
+                public void onHeaders(HeadersFrame frame)
+                {
+                    if (frame.isEndStream())
+                        responded.set(true);
+                }
+            }, 4096, 8192);
+
+            // HTTP/2 parsing.
+            while (true)
+            {
+                parser.parse(buffer);
+                if (responded.get())
+                    break;
+
+                BufferUtil.clearToFill(buffer);
+                int read = socket.read(buffer);
+                BufferUtil.flipToFlush(buffer, 0);
+                if (read < 0)
+                    Assert.fail();
+            }
+
+            Assert.assertTrue(clientSettingsLatch.await(5, TimeUnit.SECONDS));
+        }
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PriorityTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PriorityTest.java
new file mode 100644
index 0000000..1e5692e
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PriorityTest.java
@@ -0,0 +1,184 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PriorityFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Promise;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PriorityTest extends AbstractTest
+{
+    @Test
+    public void testPriorityBeforeHeaders() throws Exception
+    {
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
+                stream.headers(responseFrame, Callback.NOOP);
+                return null;
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        int streamId = session.priority(new PriorityFrame(0, 13, false), Callback.NOOP);
+        Assert.assertTrue(streamId > 0);
+
+        CountDownLatch latch = new CountDownLatch(2);
+        MetaData metaData = newRequest("GET", new HttpFields());
+        HeadersFrame headersFrame = new HeadersFrame(streamId, metaData, null, true);
+        session.newStream(headersFrame, new Promise.Adapter<Stream>()
+        {
+            @Override
+            public void succeeded(Stream result)
+            {
+                Assert.assertEquals(streamId, result.getId());
+                latch.countDown();
+            }
+        }, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testPriorityAfterHeaders() throws Exception
+    {
+        CountDownLatch beforeRequests = new CountDownLatch(1);
+        CountDownLatch afterRequests = new CountDownLatch(2);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                try
+                {
+                    beforeRequests.await(5, TimeUnit.SECONDS);
+                    MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                    HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
+                    stream.headers(responseFrame, Callback.NOOP);
+                    afterRequests.countDown();
+                    return null;
+                }
+                catch (InterruptedException x)
+                {
+                    x.printStackTrace();
+                    return null;
+                }
+            }
+        });
+
+        CountDownLatch responses = new CountDownLatch(2);
+        Stream.Listener.Adapter listener = new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    responses.countDown();
+            }
+        };
+
+        Session session = newClient(new Session.Listener.Adapter());
+        MetaData metaData1 = newRequest("GET", "/one", new HttpFields());
+        HeadersFrame headersFrame1 = new HeadersFrame(metaData1, null, true);
+        FuturePromise<Stream> promise1 = new FuturePromise<>();
+        session.newStream(headersFrame1, promise1, listener);
+        Stream stream1 = promise1.get(5, TimeUnit.SECONDS);
+
+        MetaData metaData2 = newRequest("GET", "/two", new HttpFields());
+        HeadersFrame headersFrame2 = new HeadersFrame(metaData2, null, true);
+        FuturePromise<Stream> promise2 = new FuturePromise<>();
+        session.newStream(headersFrame2, promise2, listener);
+        Stream stream2 = promise2.get(5, TimeUnit.SECONDS);
+
+        int streamId = session.priority(new PriorityFrame(stream1.getId(), stream2.getId(), 13, false), Callback.NOOP);
+        Assert.assertEquals(stream1.getId(), streamId);
+
+        // Give time to the PRIORITY frame to arrive to server.
+        Thread.sleep(1000);
+        beforeRequests.countDown();
+
+        Assert.assertTrue(afterRequests.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(responses.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testHeadersWithPriority() throws Exception
+    {
+        PriorityFrame priorityFrame = new PriorityFrame(13, 200, true);
+        CountDownLatch latch = new CountDownLatch(2);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                PriorityFrame priority = frame.getPriority();
+                Assert.assertNotNull(priority);
+                Assert.assertEquals(priorityFrame.getParentStreamId(), priority.getParentStreamId());
+                Assert.assertEquals(priorityFrame.getWeight(), priority.getWeight());
+                Assert.assertEquals(priorityFrame.isExclusive(), priority.isExclusive());
+                latch.countDown();
+
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
+                stream.headers(responseFrame, Callback.NOOP);
+                return null;
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        MetaData metaData = newRequest("GET", "/one", new HttpFields());
+        HeadersFrame headersFrame = new HeadersFrame(metaData, priorityFrame, true);
+        session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java
new file mode 100644
index 0000000..11f8ad8
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java
@@ -0,0 +1,121 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.nio.charset.StandardCharsets;
+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.HttpFields;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.ProxyConnectionFactory;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Promise;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ProxyProtocolTest
+{
+    private Server server;
+    private ServerConnector connector;
+    private HTTP2Client client;
+
+    public void startServer(Handler handler) throws Exception
+    {
+        server = new Server();
+        HttpConfiguration configuration = new HttpConfiguration();
+        connector = new ServerConnector(server, new ProxyConnectionFactory(), new HTTP2CServerConnectionFactory(configuration));
+        server.addConnector(connector);
+        server.setHandler(handler);
+
+        client = new HTTP2Client();
+        server.addBean(client, true);
+
+        server.start();
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        if (server != null)
+            server.stop();
+    }
+
+    @Test
+    public void test_PROXY_GET() throws Exception
+    {
+        startServer(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+            }
+        });
+
+        String request1 = "PROXY TCP4 1.2.3.4 5.6.7.8 1111 2222\r\n";
+        SocketChannel channel = SocketChannel.open();
+        channel.connect(new InetSocketAddress("localhost", connector.getLocalPort()));
+        channel.write(ByteBuffer.wrap(request1.getBytes(StandardCharsets.UTF_8)));
+
+        FuturePromise<Session> promise = new FuturePromise<>();
+        client.accept(null, channel, new Session.Listener.Adapter(), promise);
+        Session session = promise.get(5, TimeUnit.SECONDS);
+
+        HttpFields fields = new HttpFields();
+        String uri = "http://localhost:" + connector.getLocalPort() + "/";
+        MetaData.Request metaData = new MetaData.Request("GET", new HttpURI(uri), HttpVersion.HTTP_2, fields);
+        HeadersFrame frame = new HeadersFrame(metaData, null, true);
+        CountDownLatch latch = new CountDownLatch(1);
+        session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response response = (MetaData.Response)frame.getMetaData();
+                Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+                if (frame.isEndStream())
+                    latch.countDown();
+            }
+        });
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyTest.java
new file mode 100644
index 0000000..504df3b
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyTest.java
@@ -0,0 +1,196 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.proxy.AsyncProxyServlet;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class ProxyTest
+{
+    @Rule
+    public final TestTracker tracker = new TestTracker();
+    private HTTP2Client client;
+    private Server proxy;
+    private ServerConnector proxyConnector;
+    private Server server;
+    private ServerConnector serverConnector;
+
+    private void startServer(HttpServlet servlet) throws Exception
+    {
+        QueuedThreadPool serverPool = new QueuedThreadPool();
+        serverPool.setName("server");
+        server = new Server(serverPool);
+        serverConnector = new ServerConnector(server);
+        server.addConnector(serverConnector);
+
+        ServletContextHandler appCtx = new ServletContextHandler(server, "/", true, false);
+        ServletHolder appServletHolder = new ServletHolder(servlet);
+        appCtx.addServlet(appServletHolder, "/*");
+
+        server.start();
+    }
+
+    private void startProxy(HttpServlet proxyServlet, Map<String, String> initParams) throws Exception
+    {
+        QueuedThreadPool proxyPool = new QueuedThreadPool();
+        proxyPool.setName("proxy");
+        proxy = new Server(proxyPool);
+
+        HttpConfiguration configuration = new HttpConfiguration();
+        configuration.setSendDateHeader(false);
+        configuration.setSendServerVersion(false);
+        String value = initParams.get("outputBufferSize");
+        if (value != null)
+            configuration.setOutputBufferSize(Integer.valueOf(value));
+        proxyConnector = new ServerConnector(proxy, new HTTP2ServerConnectionFactory(configuration));
+        proxy.addConnector(proxyConnector);
+
+        ServletContextHandler proxyContext = new ServletContextHandler(proxy, "/", true, false);
+        ServletHolder proxyServletHolder = new ServletHolder(proxyServlet);
+        proxyServletHolder.setInitParameters(initParams);
+        proxyContext.addServlet(proxyServletHolder, "/*");
+
+        proxy.start();
+    }
+
+    private void startClient() throws Exception
+    {
+        QueuedThreadPool clientExecutor = new QueuedThreadPool();
+        clientExecutor.setName("client");
+        client = new HTTP2Client();
+        client.setExecutor(clientExecutor);
+        client.start();
+    }
+
+    private Session newClient(Session.Listener listener) throws Exception
+    {
+        String host = "localhost";
+        int port = proxyConnector.getLocalPort();
+        InetSocketAddress address = new InetSocketAddress(host, port);
+        FuturePromise<Session> promise = new FuturePromise<>();
+        client.connect(address, listener, promise);
+        return promise.get(5, TimeUnit.SECONDS);
+    }
+
+    private MetaData.Request newRequest(String method, String path, HttpFields fields)
+    {
+        String host = "localhost";
+        int port = proxyConnector.getLocalPort();
+        String authority = host + ":" + port;
+        return new MetaData.Request(method, HttpScheme.HTTP, new HostPortHttpField(authority), path, HttpVersion.HTTP_2, fields);
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        client.stop();
+        proxy.stop();
+        server.stop();
+    }
+
+    @Test
+    public void testServerBigDownloadSlowClient() throws Exception
+    {
+        final CountDownLatch serverLatch = new CountDownLatch(1);
+        final byte[] content = new byte[1024 * 1024];
+        startServer(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                response.getOutputStream().write(content);
+                serverLatch.countDown();
+            }
+        });
+        Map<String, String> params = new HashMap<>();
+        params.put("proxyTo", "http://localhost:" + serverConnector.getLocalPort());
+        startProxy(new AsyncProxyServlet.Transparent()
+        {
+            @Override
+            protected void sendProxyRequest(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Request proxyRequest)
+            {
+                proxyRequest.version(HttpVersion.HTTP_1_1);
+                super.sendProxyRequest(clientRequest, proxyResponse, proxyRequest);
+            }
+        }, params);
+        startClient();
+
+        final CountDownLatch clientLatch = new CountDownLatch(1);
+        Session session = newClient(new Session.Listener.Adapter());
+        MetaData.Request metaData = newRequest("GET", "/", new HttpFields());
+        HeadersFrame frame = new HeadersFrame(metaData, null, true);
+        session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                try
+                {
+                    TimeUnit.MILLISECONDS.sleep(1);
+                    callback.succeeded();
+                    if (frame.isEndStream())
+                        clientLatch.countDown();
+                }
+                catch (InterruptedException x)
+                {
+                    callback.failed(x);
+                }
+            }
+        });
+
+        Assert.assertTrue(serverLatch.await(15, TimeUnit.SECONDS));
+        Assert.assertTrue(clientLatch.await(15, TimeUnit.SECONDS));
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PushCacheFilterTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PushCacheFilterTest.java
new file mode 100644
index 0000000..7b07932
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PushCacheFilterTest.java
@@ -0,0 +1,888 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.EnumSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlets.PushCacheFilter;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PushCacheFilterTest extends AbstractTest
+{
+    @Override
+    protected void customizeContext(ServletContextHandler context)
+    {
+        context.addFilter(PushCacheFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
+    }
+
+    @Test
+    public void testPush() throws Exception
+    {
+        final String primaryResource = "/primary.html";
+        final String secondaryResource = "/secondary.png";
+        final byte[] secondaryData = "SECONDARY".getBytes("UTF-8");
+        start(new HttpServlet()
+        {
+            @Override
+            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+            {
+                String requestURI = req.getRequestURI();
+                ServletOutputStream output = resp.getOutputStream();
+                if (requestURI.endsWith(primaryResource))
+                    output.print("<html><head></head><body>PRIMARY</body></html>");
+                else if (requestURI.endsWith(secondaryResource))
+                    output.write(secondaryData);
+            }
+        });
+
+        final Session session = newClient(new Session.Listener.Adapter());
+
+        // Request for the primary and secondary resource to build the cache.
+        final String referrerURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
+        HttpFields primaryFields = new HttpFields();
+        MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch warmupLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                {
+                    // Request for the secondary resource.
+                    HttpFields secondaryFields = new HttpFields();
+                    secondaryFields.put(HttpHeader.REFERER, referrerURI);
+                    MetaData.Request secondaryRequest = newRequest("GET", secondaryResource, secondaryFields);
+                    session.newStream(new HeadersFrame(secondaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+                    {
+                        @Override
+                        public void onData(Stream stream, DataFrame frame, Callback callback)
+                        {
+                            callback.succeeded();
+                            warmupLatch.countDown();
+                        }
+                    });
+                }
+            }
+        });
+        Assert.assertTrue(warmupLatch.await(5, TimeUnit.SECONDS));
+
+        // Request again the primary resource, we should get the secondary resource pushed.
+        primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch primaryResponseLatch = new CountDownLatch(1);
+        final CountDownLatch pushLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+            {
+                return new Adapter()
+                {
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        callback.succeeded();
+                        if (frame.isEndStream())
+                            pushLatch.countDown();
+                    }
+                };
+            }
+
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                    primaryResponseLatch.countDown();
+            }
+        });
+        Assert.assertTrue(pushLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testPushReferrerNoPath() throws Exception
+    {
+        final String primaryResource = "/primary.html";
+        final String secondaryResource = "/secondary.png";
+        final byte[] secondaryData = "SECONDARY".getBytes("UTF-8");
+        start(new HttpServlet()
+        {
+            @Override
+            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+            {
+                String requestURI = req.getRequestURI();
+                ServletOutputStream output = resp.getOutputStream();
+                if (requestURI.endsWith(primaryResource))
+                    output.print("<html><head></head><body>PRIMARY</body></html>");
+                else if (requestURI.endsWith(secondaryResource))
+                    output.write(secondaryData);
+            }
+        });
+
+        final Session session = newClient(new Session.Listener.Adapter());
+
+        // Request for the primary and secondary resource to build the cache.
+        // The referrerURI does not point to the primary resource, so there will be no
+        // resource association with the primary resource and therefore won't be pushed.
+        final String referrerURI = "http://localhost:" + connector.getLocalPort();
+        HttpFields primaryFields = new HttpFields();
+        MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch warmupLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                {
+                    // Request for the secondary resource.
+                    HttpFields secondaryFields = new HttpFields();
+                    secondaryFields.put(HttpHeader.REFERER, referrerURI);
+                    MetaData.Request secondaryRequest = newRequest("GET", secondaryResource, secondaryFields);
+                    session.newStream(new HeadersFrame(secondaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+                    {
+                        @Override
+                        public void onData(Stream stream, DataFrame frame, Callback callback)
+                        {
+                            callback.succeeded();
+                            warmupLatch.countDown();
+                        }
+                    });
+                }
+            }
+        });
+        Assert.assertTrue(warmupLatch.await(5, TimeUnit.SECONDS));
+
+        // Request again the primary resource, we should not get the secondary resource pushed.
+        primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch primaryResponseLatch = new CountDownLatch(1);
+        final CountDownLatch pushLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+            {
+                return new Adapter()
+                {
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        callback.succeeded();
+                        if (frame.isEndStream())
+                            pushLatch.countDown();
+                    }
+                };
+            }
+
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                    primaryResponseLatch.countDown();
+            }
+        });
+        Assert.assertFalse(pushLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testPushIsReset() throws Exception
+    {
+        final String primaryResource = "/primary.html";
+        final String secondaryResource = "/secondary.png";
+        final byte[] secondaryData = "SECONDARY".getBytes("UTF-8");
+        start(new HttpServlet()
+        {
+            @Override
+            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+            {
+                String requestURI = req.getRequestURI();
+                ServletOutputStream output = resp.getOutputStream();
+                if (requestURI.endsWith(primaryResource))
+                    output.print("<html><head></head><body>PRIMARY</body></html>");
+                else if (requestURI.endsWith(secondaryResource))
+                    output.write(secondaryData);
+            }
+        });
+
+        final Session session = newClient(new Session.Listener.Adapter());
+
+        // Request for the primary and secondary resource to build the cache.
+        final String primaryURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
+        HttpFields primaryFields = new HttpFields();
+        MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch warmupLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                {
+                    // Request for the secondary resource.
+                    HttpFields secondaryFields = new HttpFields();
+                    secondaryFields.put(HttpHeader.REFERER, primaryURI);
+                    MetaData.Request secondaryRequest = newRequest("GET", secondaryResource, secondaryFields);
+                    session.newStream(new HeadersFrame(secondaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+                    {
+                        @Override
+                        public void onData(Stream stream, DataFrame frame, Callback callback)
+                        {
+                            callback.succeeded();
+                            warmupLatch.countDown();
+                        }
+                    });
+                }
+            }
+        });
+        Assert.assertTrue(warmupLatch.await(5, TimeUnit.SECONDS));
+
+        // Request again the primary resource, we should get the secondary resource pushed.
+        primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch primaryResponseLatch = new CountDownLatch(1);
+        final CountDownLatch pushLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+            {
+                // Reset the stream as soon as we see the push.
+                ResetFrame resetFrame = new ResetFrame(stream.getId(), ErrorCode.REFUSED_STREAM_ERROR.code);
+                stream.reset(resetFrame, Callback.NOOP);
+                return new Adapter()
+                {
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        callback.succeeded();
+                        pushLatch.countDown();
+                    }
+                };
+            }
+
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                    primaryResponseLatch.countDown();
+            }
+        });
+        // We should not receive pushed data that we reset.
+        Assert.assertFalse(pushLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
+
+        // Make sure the session is sane by requesting the secondary resource.
+        HttpFields secondaryFields = new HttpFields();
+        secondaryFields.put(HttpHeader.REFERER, primaryURI);
+        MetaData.Request secondaryRequest = newRequest("GET", secondaryResource, secondaryFields);
+        final CountDownLatch secondaryResponseLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(secondaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                    secondaryResponseLatch.countDown();
+            }
+        });
+        Assert.assertTrue(secondaryResponseLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testPushWithoutPrimaryResponseContent() throws Exception
+    {
+        final String primaryResource = "/primary.html";
+        final String secondaryResource = "/secondary.png";
+        start(new HttpServlet()
+        {
+            @Override
+            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                String requestURI = request.getRequestURI();
+                final ServletOutputStream output = response.getOutputStream();
+                if (requestURI.endsWith(secondaryResource))
+                    output.write("SECONDARY".getBytes(StandardCharsets.UTF_8));
+            }
+        });
+
+        final Session session = newClient(new Session.Listener.Adapter());
+
+        // Request for the primary and secondary resource to build the cache.
+        final String primaryURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
+        HttpFields primaryFields = new HttpFields();
+        MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch warmupLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                {
+                    // Request for the secondary resource.
+                    HttpFields secondaryFields = new HttpFields();
+                    secondaryFields.put(HttpHeader.REFERER, primaryURI);
+                    MetaData.Request secondaryRequest = newRequest("GET", secondaryResource, secondaryFields);
+                    session.newStream(new HeadersFrame(secondaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+                    {
+                        @Override
+                        public void onData(Stream stream, DataFrame frame, Callback callback)
+                        {
+                            callback.succeeded();
+                            warmupLatch.countDown();
+                        }
+                    });
+                }
+            }
+        });
+        Assert.assertTrue(warmupLatch.await(5, TimeUnit.SECONDS));
+
+        Thread.sleep(1000);
+
+        // Request again the primary resource, we should get the secondary resource pushed.
+        primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch primaryResponseLatch = new CountDownLatch(1);
+        final CountDownLatch pushLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    primaryResponseLatch.countDown();
+            }
+
+            @Override
+            public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+            {
+                return new Adapter()
+                {
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        callback.succeeded();
+                        if (frame.isEndStream())
+                            pushLatch.countDown();
+                    }
+                };
+            }
+        });
+        Assert.assertTrue(pushLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testRecursivePush() throws Exception
+    {
+        final String primaryResource = "/primary.html";
+        final String secondaryResource1 = "/secondary1.css";
+        final String secondaryResource2 = "/secondary2.js";
+        final String tertiaryResource = "/tertiary.png";
+        start(new HttpServlet()
+        {
+            @Override
+            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                String requestURI = request.getRequestURI();
+                final ServletOutputStream output = response.getOutputStream();
+                if (requestURI.endsWith(primaryResource))
+                    output.print("<html><head></head><body>PRIMARY</body></html>");
+                else if (requestURI.endsWith(secondaryResource1))
+                    output.print("body { background-image: url(\"" + tertiaryResource + "\"); }");
+                else if (requestURI.endsWith(secondaryResource2))
+                    output.print("(function() { window.alert('HTTP/2'); })()");
+                if (requestURI.endsWith(tertiaryResource))
+                    output.write("TERTIARY".getBytes(StandardCharsets.UTF_8));
+            }
+        });
+
+        final Session session = newClient(new Session.Listener.Adapter());
+
+        // Request for the primary, secondary and tertiary resource to build the cache.
+        final String primaryURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
+        HttpFields primaryFields = new HttpFields();
+        MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch warmupLatch = new CountDownLatch(2);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                {
+                    // Request for the secondary resources.
+                    String secondaryURI1 = "http://localhost:" + connector.getLocalPort() + servletPath + secondaryResource1;
+                    HttpFields secondaryFields1 = new HttpFields();
+                    secondaryFields1.put(HttpHeader.REFERER, primaryURI);
+                    MetaData.Request secondaryRequest1 = newRequest("GET", secondaryResource1, secondaryFields1);
+                    session.newStream(new HeadersFrame(secondaryRequest1, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+                    {
+                        @Override
+                        public void onData(Stream stream, DataFrame frame, Callback callback)
+                        {
+                            callback.succeeded();
+                            if (frame.isEndStream())
+                            {
+                                // Request for the tertiary resource.
+                                HttpFields tertiaryFields = new HttpFields();
+                                tertiaryFields.put(HttpHeader.REFERER, secondaryURI1);
+                                MetaData.Request tertiaryRequest = newRequest("GET", tertiaryResource, tertiaryFields);
+                                session.newStream(new HeadersFrame(tertiaryRequest, null, true), new Promise.Adapter<>(), new Adapter()
+                                {
+                                    @Override
+                                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                                    {
+                                        callback.succeeded();
+                                        if (frame.isEndStream())
+                                            warmupLatch.countDown();
+                                    }
+                                });
+                            }
+                        }
+                    });
+
+                    HttpFields secondaryFields2 = new HttpFields();
+                    secondaryFields2.put(HttpHeader.REFERER, primaryURI);
+                    MetaData.Request secondaryRequest2 = newRequest("GET", secondaryResource2, secondaryFields2);
+                    session.newStream(new HeadersFrame(secondaryRequest2, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+                    {
+                        @Override
+                        public void onData(Stream stream, DataFrame frame, Callback callback)
+                        {
+                            callback.succeeded();
+                            if (frame.isEndStream())
+                                warmupLatch.countDown();
+                        }
+                    });
+                }
+            }
+        });
+        Assert.assertTrue(warmupLatch.await(5, TimeUnit.SECONDS));
+
+        Thread.sleep(1000);
+
+        // Request again the primary resource, we should get the secondary and tertiary resources pushed.
+        primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch primaryResponseLatch = new CountDownLatch(1);
+        final CountDownLatch primaryPushesLatch = new CountDownLatch(3);
+        final CountDownLatch recursiveLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                    primaryResponseLatch.countDown();
+            }
+
+            @Override
+            public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+            {
+                // The stream id of the PUSH_PROMISE must
+                // always be a client stream and therefore odd.
+                Assert.assertEquals(1, frame.getStreamId() & 1);
+                return new Adapter()
+                {
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        callback.succeeded();
+                        if (frame.isEndStream())
+                            primaryPushesLatch.countDown();
+                    }
+
+                    @Override
+                    public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+                    {
+                        return new Adapter()
+                        {
+                            @Override
+                            public void onData(Stream stream, DataFrame frame, Callback callback)
+                            {
+                                callback.succeeded();
+                                if (frame.isEndStream())
+                                    recursiveLatch.countDown();
+                            }
+                        };
+                    }
+                };
+            }
+        });
+
+        Assert.assertTrue(primaryPushesLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertFalse(recursiveLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
+
+        // Make sure that explicitly requesting a secondary resource, we get the tertiary pushed.
+        CountDownLatch secondaryResponseLatch = new CountDownLatch(1);
+        CountDownLatch secondaryPushLatch = new CountDownLatch(1);
+        MetaData.Request secondaryRequest = newRequest("GET", secondaryResource1, new HttpFields());
+        session.newStream(new HeadersFrame(secondaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                    secondaryResponseLatch.countDown();
+            }
+
+            @Override
+            public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+            {
+                return new Adapter()
+                {
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        callback.succeeded();
+                        if (frame.isEndStream())
+                            secondaryPushLatch.countDown();
+                    }
+                };
+            }
+        });
+
+        Assert.assertTrue(secondaryPushLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(secondaryResponseLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testSelfPush() throws Exception
+    {
+        // The test case is that of a login page, for example.
+        // When the user sends the credentials to the login page,
+        // the login may fail and redirect to the same login page,
+        // perhaps with different query parameters.
+        // In this case a request for the login page will push
+        // the login page itself, which will generate the pushed
+        // request for the login page, which will push the login
+        // page itself, etc. which is not the desired behavior.
+
+        final String primaryResource = "/login.html";
+        start(new HttpServlet()
+        {
+            @Override
+            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                ServletOutputStream output = response.getOutputStream();
+                String credentials = request.getParameter("credentials");
+                if (credentials == null)
+                {
+                    output.print("<html><head></head><body>LOGIN</body></html>");
+                }
+                else if ("secret".equals(credentials))
+                {
+                    output.print("<html><head></head><body>OK</body></html>");
+                }
+                else
+                {
+                    response.setStatus(HttpStatus.TEMPORARY_REDIRECT_307);
+                    response.setHeader(HttpHeader.LOCATION.asString(), primaryResource);
+                }
+            }
+        });
+        final String primaryURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
+
+        final Session session = newClient(new Session.Listener.Adapter());
+
+        // Login with the wrong credentials, causing a redirect to self.
+        HttpFields primaryFields = new HttpFields();
+        MetaData.Request primaryRequest = newRequest("GET", primaryResource + "?credentials=wrong", primaryFields);
+        final CountDownLatch warmupLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                {
+                    MetaData.Response response = (MetaData.Response)frame.getMetaData();
+                    if (response.getStatus() == HttpStatus.TEMPORARY_REDIRECT_307)
+                    {
+                        // Follow the redirect.
+                        String location = response.getFields().get(HttpHeader.LOCATION);
+                        HttpFields redirectFields = new HttpFields();
+                        redirectFields.put(HttpHeader.REFERER, primaryURI);
+                        MetaData.Request redirectRequest = newRequest("GET", location, redirectFields);
+                        session.newStream(new HeadersFrame(redirectRequest, null, true), new Promise.Adapter<>(), new Adapter()
+                        {
+                            @Override
+                            public void onData(Stream stream, DataFrame frame, Callback callback)
+                            {
+                                callback.succeeded();
+                                if (frame.isEndStream())
+                                    warmupLatch.countDown();
+                            }
+                        });
+                    }
+                }
+            }
+        });
+        Assert.assertTrue(warmupLatch.await(5, TimeUnit.SECONDS));
+
+        Thread.sleep(1000);
+
+        // Login with the right credentials, there must be no push.
+        primaryRequest = newRequest("GET", primaryResource + "?credentials=secret", primaryFields);
+        final CountDownLatch primaryResponseLatch = new CountDownLatch(1);
+        final CountDownLatch pushLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                    primaryResponseLatch.countDown();
+            }
+
+            @Override
+            public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+            {
+                pushLatch.countDown();
+                return null;
+            }
+        });
+        Assert.assertFalse(pushLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testPushWithQueryParameters() throws Exception
+    {
+        String name = "foo";
+        String value = "bar";
+        final String primaryResource = "/primary.html?"+name + "=" +value;
+        final String secondaryResource = "/secondary.html?" + name + "=" + value;
+        start(new HttpServlet()
+        {
+            @Override
+            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                String requestURI = request.getRequestURI();
+                if (requestURI.endsWith(primaryResource))
+                {
+                    response.setStatus(HttpStatus.OK_200);
+                }
+                else if (requestURI.endsWith(secondaryResource))
+                {
+                    String param = request.getParameter(name);
+                    if (param == null)
+                        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
+                    else
+                        response.setStatus(HttpStatus.OK_200);
+                }
+            }
+        });
+
+        final Session session = newClient(new Session.Listener.Adapter());
+
+        // Request for the primary and secondary resource to build the cache.
+        final String primaryURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
+        HttpFields primaryFields = new HttpFields();
+        MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch warmupLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                {
+                    // Request for the secondary resource.
+                    HttpFields secondaryFields = new HttpFields();
+                    secondaryFields.put(HttpHeader.REFERER, primaryURI);
+                    MetaData.Request secondaryRequest = newRequest("GET", secondaryResource, secondaryFields);
+                    session.newStream(new HeadersFrame(secondaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+                    {
+                        @Override
+                        public void onHeaders(Stream stream, HeadersFrame frame)
+                        {
+                            if (frame.isEndStream())
+                                warmupLatch.countDown();
+                        }
+                    });
+                }
+            }
+        });
+        Assert.assertTrue(warmupLatch.await(5, TimeUnit.SECONDS));
+
+        Thread.sleep(1000);
+
+        // Request again the primary resource, we should get the secondary resource pushed.
+        primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch primaryResponseLatch = new CountDownLatch(1);
+        final CountDownLatch pushLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+            {
+                MetaData metaData = frame.getMetaData();
+                Assert.assertTrue(metaData instanceof MetaData.Request);
+                MetaData.Request pushedRequest = (MetaData.Request)metaData;
+                Assert.assertEquals(servletPath + secondaryResource, pushedRequest.getURI().getPathQuery());
+                return new Adapter()
+                {
+                    @Override
+                    public void onHeaders(Stream stream, HeadersFrame frame)
+                    {
+                        if (frame.isEndStream())
+                        {
+                            MetaData.Response response = (MetaData.Response)frame.getMetaData();
+                            if (response.getStatus() == HttpStatus.OK_200)
+                                pushLatch.countDown();
+                        }
+                    }
+                };
+            }
+
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    primaryResponseLatch.countDown();
+            }
+        });
+        Assert.assertTrue(pushLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testPOSTRequestIsNotPushed() throws Exception
+    {
+        final String primaryResource = "/primary.html";
+        final String secondaryResource = "/secondary.png";
+        final byte[] secondaryData = "SECONDARY".getBytes("UTF-8");
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+            {
+                String requestURI = req.getRequestURI();
+                ServletOutputStream output = resp.getOutputStream();
+                if (requestURI.endsWith(primaryResource))
+                    output.print("<html><head></head><body>PRIMARY</body></html>");
+                else if (requestURI.endsWith(secondaryResource))
+                    output.write(secondaryData);
+            }
+        });
+
+        final Session session = newClient(new Session.Listener.Adapter());
+
+        // Request for the primary and secondary resource to build the cache.
+        final String referrerURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
+        HttpFields primaryFields = new HttpFields();
+        MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
+        final CountDownLatch warmupLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                {
+                    // Request for the secondary resource.
+                    HttpFields secondaryFields = new HttpFields();
+                    secondaryFields.put(HttpHeader.REFERER, referrerURI);
+                    MetaData.Request secondaryRequest = newRequest("GET", secondaryResource, secondaryFields);
+                    session.newStream(new HeadersFrame(secondaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+                    {
+                        @Override
+                        public void onData(Stream stream, DataFrame frame, Callback callback)
+                        {
+                            callback.succeeded();
+                            warmupLatch.countDown();
+                        }
+                    });
+                }
+            }
+        });
+        Assert.assertTrue(warmupLatch.await(5, TimeUnit.SECONDS));
+
+        // Request again the primary resource with POST, we should not get the secondary resource pushed.
+        primaryRequest = newRequest("POST", primaryResource, primaryFields);
+        final CountDownLatch primaryResponseLatch = new CountDownLatch(1);
+        final CountDownLatch pushLatch = new CountDownLatch(1);
+        session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+            {
+                return new Adapter()
+                {
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        callback.succeeded();
+                        if (frame.isEndStream())
+                            pushLatch.countDown();
+                    }
+                };
+            }
+
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                    primaryResponseLatch.countDown();
+            }
+        });
+        Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertFalse(pushLatch.await(1, TimeUnit.SECONDS));
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SessionFailureTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SessionFailureTest.java
new file mode 100644
index 0000000..8909b4b
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SessionFailureTest.java
@@ -0,0 +1,123 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http2.HTTP2Session;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SessionFailureTest extends AbstractTest
+{
+    @Test
+    public void testWrongPreface() throws Exception
+    {
+        final CountDownLatch latch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public void onFailure(Session session, Throwable failure)
+            {
+                latch.countDown();
+            }
+        });
+
+        try (Socket socket = new Socket("localhost", connector.getLocalPort()))
+        {
+            // Preface starts with byte 0x50, send something different.
+            OutputStream output = socket.getOutputStream();
+            output.write(0x0);
+            output.flush();
+
+            Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+
+            // The server will reply with a GOAWAY frame, and then shutdown.
+            // Read until EOF.
+            socket.setSoTimeout(1000);
+            InputStream input = socket.getInputStream();
+            while (true)
+            {
+                if (input.read() < 0)
+                    break;
+            }
+        }
+    }
+
+    @Test
+    public void testWriteFailure() throws Exception
+    {
+        final CountDownLatch writeLatch = new CountDownLatch(1);
+        final CountDownLatch serverFailureLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                // Forcibly close the connection.
+                ((HTTP2Session)stream.getSession()).getEndPoint().close();
+                // Now try to write something: it should fail.
+                stream.headers(frame, new Callback()
+                {
+                    @Override
+                    public void failed(Throwable x)
+                    {
+                        writeLatch.countDown();
+                    }
+                });
+                return null;
+            }
+
+            @Override
+            public void onFailure(Session session, Throwable failure)
+            {
+                serverFailureLatch.countDown();
+            }
+        });
+
+        final CountDownLatch clientFailureLatch = new CountDownLatch(1);
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public void onFailure(Session session, Throwable failure)
+            {
+                clientFailureLatch.countDown();
+            }
+        });
+        HeadersFrame frame = new HeadersFrame(newRequest("GET", new HttpFields()), null, true);
+        Promise<Stream> promise = new Promise.Adapter<>();
+        session.newStream(frame, promise, null);
+
+        Assert.assertTrue(writeLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(serverFailureLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(clientFailureLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertFalse(((HTTP2Session)session).getEndPoint().isOpen());
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SimpleFlowControlStrategyTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SimpleFlowControlStrategyTest.java
new file mode 100644
index 0000000..cc91e63
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SimpleFlowControlStrategyTest.java
@@ -0,0 +1,31 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import org.eclipse.jetty.http2.FlowControlStrategy;
+import org.eclipse.jetty.http2.SimpleFlowControlStrategy;
+
+public class SimpleFlowControlStrategyTest extends FlowControlStrategyTest
+{
+    @Override
+    protected FlowControlStrategy newFlowControlStrategy()
+    {
+        return new SimpleFlowControlStrategy();
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SmallThreadPoolLoadTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SmallThreadPoolLoadTest.java
new file mode 100644
index 0000000..09bf97c
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SmallThreadPoolLoadTest.java
@@ -0,0 +1,218 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Locale;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.IntStream;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.server.AbstractHTTP2ServerConnectionFactory;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+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.thread.QueuedThreadPool;
+import org.eclipse.jetty.util.thread.Scheduler;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SmallThreadPoolLoadTest extends AbstractTest
+{
+    private final Logger logger = Log.getLogger(SmallThreadPoolLoadTest.class);
+    private final AtomicLong requestIds = new AtomicLong();
+
+    @Override
+    protected void customizeContext(ServletContextHandler context)
+    {
+        QueuedThreadPool serverThreads = (QueuedThreadPool)context.getServer().getThreadPool();
+        serverThreads.setDetailedDump(true);
+        serverThreads.setMaxThreads(5);
+        serverThreads.setLowThreadsThreshold(1);
+
+        AbstractHTTP2ServerConnectionFactory h2 = connector.getBean(AbstractHTTP2ServerConnectionFactory.class);
+        h2.setInitialSessionRecvWindow(Integer.MAX_VALUE);
+    }
+
+    @Test
+    public void testConcurrentWithSmallServerThreadPool() throws Exception
+    {
+        start(new LoadServlet());
+
+        // Only one connection to the server.
+        Session session = newClient(new Session.Listener.Adapter());
+
+        int runs = 10;
+        int iterations = 512;
+        boolean result = IntStream.range(0, 16).parallel()
+                .mapToObj(i -> IntStream.range(0, runs)
+                        .mapToObj(j -> run(session, iterations))
+                        .reduce(true, (acc, res) -> acc && res))
+                .reduce(true, (acc, res) -> acc && res);
+
+        Assert.assertTrue(result);
+    }
+
+    private boolean run(Session session, int iterations)
+    {
+        try
+        {
+            CountDownLatch latch = new CountDownLatch(iterations);
+            int factor = (logger.isDebugEnabled() ? 25 : 1) * 100;
+
+            // Dumps the state of the client if the test takes too long.
+            final Thread testThread = Thread.currentThread();
+            Scheduler.Task task = client.getScheduler().schedule(() ->
+            {
+                logger.warn("Interrupting test, it is taking too long{}{}{}{}",
+                        System.lineSeparator(), server.dump(),
+                        System.lineSeparator(), client.dump());
+                testThread.interrupt();
+            }, iterations * factor, TimeUnit.MILLISECONDS);
+
+            long successes = 0;
+            long begin = System.nanoTime();
+            for (int i = 0; i < iterations; ++i)
+            {
+                boolean success = test(session, latch);
+                if (success)
+                    ++successes;
+            }
+
+            Assert.assertTrue(latch.await(iterations, TimeUnit.SECONDS));
+            long end = System.nanoTime();
+            Assert.assertThat(successes, Matchers.greaterThan(0L));
+            task.cancel();
+            long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin);
+            logger.info("{} requests in {} ms, {}/{} success/failure, {} req/s",
+                    iterations, elapsed,
+                    successes, iterations - successes,
+                    elapsed > 0 ? iterations * 1000 / elapsed : -1);
+            return true;
+        }
+        catch (Exception x)
+        {
+            x.printStackTrace();
+            return false;
+        }
+    }
+
+    private boolean test(Session session, CountDownLatch latch) throws Exception
+    {
+        ThreadLocalRandom random = ThreadLocalRandom.current();
+        // Choose a random method
+        boolean download = random.nextBoolean();
+        HttpMethod method = download ? HttpMethod.GET : HttpMethod.POST;
+
+        int maxContentLength = 128 * 1024;
+        int contentLength = random.nextInt(maxContentLength) + 1;
+
+        long requestId = requestIds.incrementAndGet();
+        MetaData.Request request = newRequest(method.asString(), "/" + requestId, new HttpFields());
+        if (download)
+            request.getFields().put("X-Download", String.valueOf(contentLength));
+        HeadersFrame requestFrame = new HeadersFrame(request, null, download);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        CountDownLatch requestLatch = new CountDownLatch(1);
+        AtomicBoolean reset = new AtomicBoolean();
+        session.newStream(requestFrame, promise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    requestLatch.countDown();
+            }
+
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                if (frame.isEndStream())
+                    requestLatch.countDown();
+            }
+
+            @Override
+            public void onReset(Stream stream, ResetFrame frame)
+            {
+                reset.set(true);
+                requestLatch.countDown();
+            }
+        });
+        if (!download)
+        {
+            Stream stream = promise.get(5, TimeUnit.SECONDS);
+            stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(contentLength), true), Callback.NOOP);
+        }
+
+        boolean success = requestLatch.await(5, TimeUnit.SECONDS);
+        if (success)
+            latch.countDown();
+        else
+            logger.warn("Request {} took too long{}{}{}{}", requestId,
+                    System.lineSeparator(), server.dump(),
+                    System.lineSeparator(), client.dump());
+        return !reset.get();
+    }
+
+    private static class LoadServlet extends HttpServlet
+    {
+        @Override
+        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {
+            String method = request.getMethod().toUpperCase(Locale.ENGLISH);
+            switch (method)
+            {
+                case "GET":
+                {
+                    int contentLength = request.getIntHeader("X-Download");
+                    if (contentLength > 0)
+                        response.getOutputStream().write(new byte[contentLength]);
+                    break;
+                }
+                case "POST":
+                {
+                    IO.copy(request.getInputStream(), response.getOutputStream());
+                    break;
+                }
+            }
+        }
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/StreamCloseTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/StreamCloseTest.java
new file mode 100644
index 0000000..d9817f0
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/StreamCloseTest.java
@@ -0,0 +1,359 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.HTTP2Session;
+import org.eclipse.jetty.http2.HTTP2Stream;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Promise;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class StreamCloseTest extends AbstractTest
+{
+    @Test
+    public void testRequestClosedRemotelyClosesStream() throws Exception
+    {
+        final CountDownLatch latch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                Assert.assertTrue(((HTTP2Stream)stream).isRemotelyClosed());
+                latch.countDown();
+                return null;
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        HeadersFrame frame = new HeadersFrame(newRequest("GET", new HttpFields()), null, true);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        session.newStream(frame, promise, null);
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+        Assert.assertTrue(((HTTP2Stream)stream).isLocallyClosed());
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testRequestClosedResponseClosedClosesStream() throws Exception
+    {
+        final CountDownLatch latch = new CountDownLatch(2);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(final Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame response = new HeadersFrame(stream.getId(), metaData, null, true);
+                stream.headers(response, new Callback()
+                {
+                    @Override
+                    public void succeeded()
+                    {
+                        Assert.assertTrue(stream.isClosed());
+                        Assert.assertEquals(0, stream.getSession().getStreams().size());
+                        latch.countDown();
+                    }
+                });
+                return null;
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        HeadersFrame frame = new HeadersFrame(newRequest("GET", new HttpFields()), null, true);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        session.newStream(frame, promise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                // The stream promise may not be notified yet here.
+                latch.countDown();
+            }
+        });
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(stream.isClosed());
+    }
+
+    @Test
+    public void testRequestDataClosedResponseDataClosedClosesStream() throws Exception
+    {
+        final CountDownLatch serverDataLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame response = new HeadersFrame(stream.getId(), metaData, null, false);
+                stream.headers(response, Callback.NOOP);
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onData(final Stream stream, DataFrame frame, final Callback callback)
+                    {
+                        Assert.assertTrue(((HTTP2Stream)stream).isRemotelyClosed());
+
+                        // We must copy the data that we send asynchronously.
+                        ByteBuffer data = frame.getData();
+                        ByteBuffer copy = ByteBuffer.allocate(data.remaining());
+                        copy.put(data).flip();
+                        stream.data(new DataFrame(stream.getId(), copy, frame.isEndStream()), new Callback()
+                        {
+                            @Override
+                            public void succeeded()
+                            {
+                                Assert.assertTrue(stream.isClosed());
+                                Assert.assertEquals(0, stream.getSession().getStreams().size());
+                                callback.succeeded();
+                                serverDataLatch.countDown();
+                            }
+                        });
+                    }
+                };
+            }
+        });
+
+        final CountDownLatch completeLatch = new CountDownLatch(1);
+        Session session = newClient(new Session.Listener.Adapter());
+        HeadersFrame frame = new HeadersFrame(newRequest("GET", new HttpFields()), null, false);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        session.newStream(frame, promise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                // The sent data callback may not be notified yet here.
+                callback.succeeded();
+                completeLatch.countDown();
+            }
+        });
+        final Stream stream = promise.get(5, TimeUnit.SECONDS);
+        Assert.assertFalse(stream.isClosed());
+        Assert.assertFalse(((HTTP2Stream)stream).isLocallyClosed());
+
+        final CountDownLatch clientDataLatch = new CountDownLatch(1);
+        stream.data(new DataFrame(stream.getId(), ByteBuffer.wrap(new byte[512]), true), new Callback()
+        {
+            @Override
+            public void succeeded()
+            {
+                // Here the stream may be just locally closed or fully closed.
+                clientDataLatch.countDown();
+            }
+        });
+
+        Assert.assertTrue(clientDataLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(serverDataLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(stream.isClosed());
+        Assert.assertEquals(0, stream.getSession().getStreams().size());
+    }
+
+    @Test
+    public void testPushedStreamIsClosed() throws Exception
+    {
+        final CountDownLatch serverLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                PushPromiseFrame pushFrame = new PushPromiseFrame(stream.getId(), 0, newRequest("GET", new HttpFields()));
+                stream.push(pushFrame, new Promise.Adapter<Stream>()
+                {
+                    @Override
+                    public void succeeded(final Stream pushedStream)
+                    {
+                        // When created, pushed stream must be implicitly remotely closed.
+                        Assert.assertTrue(((HTTP2Stream)pushedStream).isRemotelyClosed());
+                        // Send some data with endStream = true.
+                        pushedStream.data(new DataFrame(pushedStream.getId(), ByteBuffer.allocate(16), true), new Callback()
+                        {
+                            @Override
+                            public void succeeded()
+                            {
+                                Assert.assertTrue(pushedStream.isClosed());
+                                serverLatch.countDown();
+                            }
+                        });
+                    }
+                }, new Stream.Listener.Adapter());
+                HeadersFrame response = new HeadersFrame(stream.getId(), new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields()), null, true);
+                stream.headers(response, Callback.NOOP);
+                return null;
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        HeadersFrame frame = new HeadersFrame(newRequest("GET", new HttpFields()), null, true);
+        final CountDownLatch clientLatch = new CountDownLatch(1);
+        session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public Stream.Listener onPush(Stream pushedStream, PushPromiseFrame frame)
+            {
+                Assert.assertTrue(((HTTP2Stream)pushedStream).isLocallyClosed());
+                return new Adapter()
+                {
+                    @Override
+                    public void onData(Stream pushedStream, DataFrame frame, Callback callback)
+                    {
+                        Assert.assertTrue(pushedStream.isClosed());
+                        callback.succeeded();
+                        clientLatch.countDown();
+                    }
+                };
+            }
+        });
+
+        Assert.assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testPushedStreamResetIsClosed() throws Exception
+    {
+        final CountDownLatch serverLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(final Stream stream, HeadersFrame frame)
+            {
+                PushPromiseFrame pushFrame = new PushPromiseFrame(stream.getId(), 0, newRequest("GET", new HttpFields()));
+                stream.push(pushFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onReset(Stream pushedStream, ResetFrame frame)
+                    {
+                        Assert.assertTrue(pushedStream.isReset());
+                        Assert.assertTrue(pushedStream.isClosed());
+                        HeadersFrame response = new HeadersFrame(stream.getId(), new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields()), null, true);
+                        stream.headers(response, Callback.NOOP);
+                        serverLatch.countDown();
+                    }
+                });
+                return null;
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+        HeadersFrame frame = new HeadersFrame(newRequest("GET", new HttpFields()), null, true);
+        final CountDownLatch clientLatch = new CountDownLatch(2);
+        session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public Stream.Listener onPush(final Stream pushedStream, PushPromiseFrame frame)
+            {
+                pushedStream.reset(new ResetFrame(pushedStream.getId(), ErrorCode.REFUSED_STREAM_ERROR.code), new Callback()
+                {
+                    @Override
+                    public void succeeded()
+                    {
+                        Assert.assertTrue(pushedStream.isReset());
+                        Assert.assertTrue(pushedStream.isClosed());
+                        clientLatch.countDown();
+                    }
+                });
+                return null;
+            }
+
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                clientLatch.countDown();
+            }
+        });
+
+        Assert.assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testFailedSessionClosesIdleStream() throws Exception
+    {
+        AtomicReference<Session> sessionRef = new AtomicReference<>();
+        final CountDownLatch latch = new CountDownLatch(1);
+        final List<Stream> streams = new ArrayList<>();
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                streams.add(stream);
+                MetaData.Request request = (MetaData.Request)frame.getMetaData();
+                if ("GET".equals(request.getMethod()))
+                {
+                    ((HTTP2Session)stream.getSession()).getEndPoint().close();
+                    // Try to write something to force an error.
+                    stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1024), true), Callback.NOOP);
+                }
+                return null;
+            }
+
+            @Override
+            public void onFailure(Session session, Throwable failure)
+            {
+                sessionRef.set(session);
+                latch.countDown();
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+
+        // First stream will be idle on server.
+        HeadersFrame request1 = new HeadersFrame(newRequest("HEAD", new HttpFields()), null, true);
+        session.newStream(request1, new Promise.Adapter<>(), new Stream.Listener.Adapter());
+
+        // Second stream will fail on server.
+        HeadersFrame request2 = new HeadersFrame(newRequest("GET", new HttpFields()), null, true);
+        session.newStream(request2, new Promise.Adapter<>(), new Stream.Listener.Adapter());
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        Session serverSession = sessionRef.get();
+
+        // Wait for the server to finish the close activities.
+        Thread.sleep(1000);
+
+        Assert.assertEquals(0, serverSession.getStreams().size());
+        for (Stream stream : streams)
+            Assert.assertTrue(stream.isClosed());
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/StreamCountTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/StreamCountTest.java
new file mode 100644
index 0000000..7702bff
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/StreamCountTest.java
@@ -0,0 +1,196 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.HTTP2Session;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class StreamCountTest extends AbstractTest
+{
+    @Test
+    public void testServerAllowsOneStreamEnforcedByClient() throws Exception
+    {
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Map<Integer, Integer> onPreface(Session session)
+            {
+                Map<Integer, Integer> settings = new HashMap<>();
+                settings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, 1);
+                return settings;
+            }
+
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        if (frame.isEndStream())
+                        {
+                            HttpFields fields = new HttpFields();
+                            MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, fields);
+                            stream.headers(new HeadersFrame(stream.getId(), metaData, null, true), callback);
+                        }
+                        else
+                        {
+                            callback.succeeded();
+                        }
+                    }
+                };
+            }
+        });
+
+        final CountDownLatch settingsLatch = new CountDownLatch(1);
+        Session session = newClient(new Session.Listener.Adapter()
+        {
+            @Override
+            public void onSettings(Session session, SettingsFrame frame)
+            {
+                settingsLatch.countDown();
+            }
+        });
+
+        Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
+
+        HttpFields fields = new HttpFields();
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame1 = new HeadersFrame(metaData, null, false);
+        FuturePromise<Stream> streamPromise1 = new FuturePromise<>();
+        final CountDownLatch responseLatch = new CountDownLatch(1);
+        session.newStream(frame1, streamPromise1, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    responseLatch.countDown();
+            }
+        });
+        Stream stream1 = streamPromise1.get(5, TimeUnit.SECONDS);
+
+        HeadersFrame frame2 = new HeadersFrame(metaData, null, false);
+        FuturePromise<Stream> streamPromise2 = new FuturePromise<>();
+        session.newStream(frame2, streamPromise2, new Stream.Listener.Adapter());
+
+        try
+        {
+            streamPromise2.get(5, TimeUnit.SECONDS);
+            Assert.fail();
+        }
+        catch (ExecutionException x)
+        {
+            // Expected
+        }
+
+        stream1.data(new DataFrame(stream1.getId(), BufferUtil.EMPTY_BUFFER, true), Callback.NOOP);
+        Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testServerAllowsOneStreamEnforcedByServer() throws Exception
+    {
+        final CountDownLatch resetLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                HTTP2Session session = (HTTP2Session)stream.getSession();
+                session.setMaxRemoteStreams(1);
+
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        if (frame.isEndStream())
+                        {
+                            HttpFields fields = new HttpFields();
+                            MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, fields);
+                            stream.headers(new HeadersFrame(stream.getId(), metaData, null, true), callback);
+                        }
+                        else
+                        {
+                            callback.succeeded();
+                        }
+                    }
+                };
+            }
+        });
+
+        Session session = newClient(new Session.Listener.Adapter());
+
+        HttpFields fields = new HttpFields();
+        MetaData.Request metaData = newRequest("GET", fields);
+        HeadersFrame frame1 = new HeadersFrame(metaData, null, false);
+        FuturePromise<Stream> streamPromise1 = new FuturePromise<>();
+        final CountDownLatch responseLatch = new CountDownLatch(1);
+        session.newStream(frame1, streamPromise1, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                if (frame.isEndStream())
+                    responseLatch.countDown();
+            }
+        });
+
+        Stream stream1 = streamPromise1.get(5, TimeUnit.SECONDS);
+
+        HeadersFrame frame2 = new HeadersFrame(metaData, null, false);
+        FuturePromise<Stream> streamPromise2 = new FuturePromise<>();
+        session.newStream(frame2, streamPromise2, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onReset(Stream stream, ResetFrame frame)
+            {
+                resetLatch.countDown();
+            }
+        });
+
+        streamPromise2.get(5, TimeUnit.SECONDS);
+        Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
+
+        stream1.data(new DataFrame(stream1.getId(), BufferUtil.EMPTY_BUFFER, true), Callback.NOOP);
+        Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/StreamResetTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/StreamResetTest.java
new file mode 100644
index 0000000..649f874
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/StreamResetTest.java
@@ -0,0 +1,584 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.FlowControlStrategy;
+import org.eclipse.jetty.http2.ISession;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.server.HttpOutput;
+import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FutureCallback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class StreamResetTest extends AbstractTest
+{
+    @Test
+    public void testStreamSendingResetIsRemoved() throws Exception
+    {
+        start(new ServerSessionListener.Adapter());
+
+        Session client = newClient(new Session.Listener.Adapter());
+        MetaData.Request request = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(request, null, false);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        client.newStream(requestFrame, promise, new Stream.Listener.Adapter());
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+        ResetFrame resetFrame = new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code);
+        FutureCallback resetCallback = new FutureCallback();
+        stream.reset(resetFrame, resetCallback);
+        resetCallback.get(5, TimeUnit.SECONDS);
+        // After reset the stream should be gone.
+        Assert.assertEquals(0, client.getStreams().size());
+    }
+
+    @Test
+    public void testStreamReceivingResetIsRemoved() throws Exception
+    {
+        final AtomicReference<Stream> streamRef = new AtomicReference<>();
+        final CountDownLatch resetLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onReset(Stream stream, ResetFrame frame)
+                    {
+                        Assert.assertNotNull(stream);
+                        Assert.assertTrue(stream.isReset());
+                        streamRef.set(stream);
+                        resetLatch.countDown();
+                    }
+                };
+            }
+        });
+
+        Session client = newClient(new Session.Listener.Adapter());
+        MetaData.Request request = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame = new HeadersFrame(request, null, false);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        client.newStream(requestFrame, promise, new Stream.Listener.Adapter());
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+        ResetFrame resetFrame = new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code);
+        stream.reset(resetFrame, Callback.NOOP);
+
+        Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
+
+        // Wait a while to let the server remove the
+        // stream after returning from onReset().
+        Thread.sleep(1000);
+
+        Stream serverStream = streamRef.get();
+        Assert.assertEquals(0, serverStream.getSession().getStreams().size());
+    }
+
+    @Test
+    public void testStreamResetDoesNotCloseConnection() throws Exception
+    {
+        final CountDownLatch serverResetLatch = new CountDownLatch(1);
+        final CountDownLatch serverDataLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame)
+            {
+                MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), response, null, false);
+                stream.headers(responseFrame, Callback.NOOP);
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onData(Stream stream, DataFrame frame, Callback callback)
+                    {
+                        callback.succeeded();
+                        stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(16), true), Callback.NOOP);
+                        serverDataLatch.countDown();
+                    }
+
+                    @Override
+                    public void onReset(Stream stream, ResetFrame frame)
+                    {
+                        // Simulate that there is pending data to send.
+                        stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(16), true), new Callback()
+                        {
+                            @Override
+                            public void failed(Throwable x)
+                            {
+                                serverResetLatch.countDown();
+                            }
+                        });
+                    }
+                };
+            }
+        });
+
+        Session client = newClient(new Session.Listener.Adapter());
+        MetaData.Request request1 = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame1 = new HeadersFrame(request1, null, false);
+        FuturePromise<Stream> promise1 = new FuturePromise<>();
+        final CountDownLatch stream1HeadersLatch = new CountDownLatch(1);
+        final CountDownLatch stream1DataLatch = new CountDownLatch(1);
+        client.newStream(requestFrame1, promise1, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                stream1HeadersLatch.countDown();
+            }
+
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                stream1DataLatch.countDown();
+            }
+        });
+        Stream stream1 = promise1.get(5, TimeUnit.SECONDS);
+        Assert.assertTrue(stream1HeadersLatch.await(5, TimeUnit.SECONDS));
+
+        MetaData.Request request2 = newRequest("GET", new HttpFields());
+        HeadersFrame requestFrame2 = new HeadersFrame(request2, null, false);
+        FuturePromise<Stream> promise2 = new FuturePromise<>();
+        final CountDownLatch stream2DataLatch = new CountDownLatch(1);
+        client.newStream(requestFrame2, promise2, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+                stream2DataLatch.countDown();
+            }
+        });
+        Stream stream2 = promise2.get(5, TimeUnit.SECONDS);
+
+        ResetFrame resetFrame = new ResetFrame(stream1.getId(), ErrorCode.CANCEL_STREAM_ERROR.code);
+        stream1.reset(resetFrame, Callback.NOOP);
+
+        Assert.assertTrue(serverResetLatch.await(5, TimeUnit.SECONDS));
+        // Stream MUST NOT receive data sent by server after reset.
+        Assert.assertFalse(stream1DataLatch.await(1, TimeUnit.SECONDS));
+
+        // The other stream should still be working.
+        stream2.data(new DataFrame(stream2.getId(), ByteBuffer.allocate(16), true), Callback.NOOP);
+        Assert.assertTrue(serverDataLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(stream2DataLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testBlockingWriteAfterStreamReceivingReset() throws Exception
+    {
+        final CountDownLatch resetLatch = new CountDownLatch(1);
+        final CountDownLatch dataLatch = new CountDownLatch(1);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                Charset charset = StandardCharsets.UTF_8;
+                byte[] data = "AFTER RESET".getBytes(charset);
+
+                response.setStatus(200);
+                response.setContentType("text/plain;charset=" + charset.name());
+                response.setContentLength(data.length * 10);
+                response.flushBuffer();
+
+                try
+                {
+                    // Wait for the reset to happen.
+                    Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
+                }
+                catch (InterruptedException x)
+                {
+                    throw new InterruptedIOException();
+                }
+
+                try
+                {
+                    // Write some content after the stream has
+                    // been reset, it should throw an exception.
+                    for (int i = 0; i < 10; i++)
+                    {
+                        Thread.sleep(500);
+                        response.getOutputStream().write(data);
+                        response.flushBuffer();
+                    }
+                }
+                catch (InterruptedException x)
+                {
+
+                }
+                catch (IOException x)
+                {
+                    dataLatch.countDown();
+                }
+            }
+        });
+
+        Session client = newClient(new Session.Listener.Adapter());
+        MetaData.Request request = newRequest("GET", new HttpFields());
+        HeadersFrame frame = new HeadersFrame(request, null, true);
+        client.newStream(frame, new FuturePromise<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+                resetLatch.countDown();
+            }
+        });
+
+        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testAsyncWriteAfterStreamReceivingReset() throws Exception
+    {
+        final CountDownLatch resetLatch = new CountDownLatch(1);
+        final CountDownLatch dataLatch = new CountDownLatch(1);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void doGet(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
+            {
+                Charset charset = StandardCharsets.UTF_8;
+                final ByteBuffer data = ByteBuffer.wrap("AFTER RESET".getBytes(charset));
+
+                response.setStatus(200);
+                response.setContentType("text/plain;charset=" + charset.name());
+                response.setContentLength(data.remaining());
+                response.flushBuffer();
+
+                try
+                {
+                    // Wait for the reset to happen.
+                    Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
+                    // Wait for the reset to arrive to the server and be processed.
+                    Thread.sleep(1000);
+                }
+                catch (InterruptedException x)
+                {
+                    throw new InterruptedIOException();
+                }
+
+                // Write some content asynchronously after the stream has been reset.
+                final AsyncContext context = request.startAsync();
+                new Thread()
+                {
+                    @Override
+                    public void run()
+                    {
+                        try
+                        {
+                            // Wait for the request thread to exit
+                            // doGet() so this is really asynchronous.
+                            Thread.sleep(1000);
+
+                            HttpOutput output = (HttpOutput)response.getOutputStream();
+                            output.sendContent(data, new Callback()
+                            {
+                                @Override
+                                public void failed(Throwable x)
+                                {
+                                    context.complete();
+                                    dataLatch.countDown();
+                                }
+                            });
+                        }
+                        catch (Throwable x)
+                        {
+                            x.printStackTrace();
+                        }
+                    }
+                }.start();
+            }
+        });
+
+        Session client = newClient(new Session.Listener.Adapter());
+        MetaData.Request request = newRequest("GET", new HttpFields());
+        HeadersFrame frame = new HeadersFrame(request, null, true);
+        client.newStream(frame, new FuturePromise<>(), new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+                stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+                resetLatch.countDown();
+            }
+        });
+
+        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testClientResetConsumesQueuedData() throws Exception
+    {
+        start(new EmptyHttpServlet());
+
+        Session client = newClient(new Session.Listener.Adapter());
+        MetaData.Request request = newRequest("GET", new HttpFields());
+        HeadersFrame frame = new HeadersFrame(request, null, false);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        client.newStream(frame, promise, new Stream.Listener.Adapter());
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+        ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
+        CountDownLatch dataLatch = new CountDownLatch(1);
+        stream.data(new DataFrame(stream.getId(), data, false), new Callback()
+        {
+            @Override
+            public void succeeded()
+            {
+                dataLatch.countDown();
+            }
+        });
+        // The server does not read the data, so the flow control window should be zero.
+        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertEquals(0, ((ISession)client).updateSendWindow(0));
+
+        // Now reset the stream.
+        stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+
+        // Wait for the server to receive the reset and process
+        // it, and for the client to process the window updates.
+        Thread.sleep(1000);
+
+        Assert.assertThat(((ISession)client).updateSendWindow(0), Matchers.greaterThan(0));
+    }
+
+    @Test
+    public void testServerExceptionConsumesQueuedData() throws Exception
+    {
+        try (StacklessLogging suppressor = new StacklessLogging(ServletHandler.class))
+        {
+            start(new HttpServlet()
+            {
+                @Override
+                protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+                {
+                    try
+                    {
+                        // Wait to let the data sent by the client to be queued.
+                        Thread.sleep(1000);
+                        throw new IllegalStateException("explictly_thrown_by_test");
+                    }
+                    catch (InterruptedException e)
+                    {
+                        throw new InterruptedIOException();
+                    }
+                }
+            });
+
+            Session client = newClient(new Session.Listener.Adapter());
+            MetaData.Request request = newRequest("GET", new HttpFields());
+            HeadersFrame frame = new HeadersFrame(request, null, false);
+            FuturePromise<Stream> promise = new FuturePromise<>();
+            client.newStream(frame, promise, new Stream.Listener.Adapter());
+            Stream stream = promise.get(5, TimeUnit.SECONDS);
+            ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
+            CountDownLatch dataLatch = new CountDownLatch(1);
+            stream.data(new DataFrame(stream.getId(), data, false), new Callback()
+            {
+                @Override
+                public void succeeded()
+                {
+                    dataLatch.countDown();
+                }
+            });
+            // The server does not read the data, so the flow control window should be zero.
+            Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
+            Assert.assertEquals(0, ((ISession)client).updateSendWindow(0));
+
+            // Wait for the server process the exception, and
+            // for the client to process the window updates.
+            Thread.sleep(2000);
+
+            Assert.assertThat(((ISession)client).updateSendWindow(0), Matchers.greaterThan(0));
+        }
+    }
+
+    @Test
+    public void testResetAfterAsyncRequestBlockingWriteStalledByFlowControl() throws Exception
+    {
+        int windowSize = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
+        CountDownLatch writeLatch = new CountDownLatch(1);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                AsyncContext asyncContext = request.startAsync();
+                asyncContext.start(() ->
+                {
+                    try
+                    {
+                        // Make sure we are in async wait before writing.
+                        Thread.sleep(1000);
+                        response.getOutputStream().write(new byte[10 * windowSize]);
+                        asyncContext.complete();
+                    }
+                    catch (IOException x)
+                    {
+                        writeLatch.countDown();
+                    }
+                    catch (Throwable x)
+                    {
+                        x.printStackTrace();
+                    }
+                });
+            }
+        });
+
+        Deque<Object> dataQueue = new ArrayDeque<>();
+        AtomicLong received = new AtomicLong();
+        CountDownLatch latch = new CountDownLatch(1);
+        Session client = newClient(new Session.Listener.Adapter());
+        MetaData.Request request = newRequest("GET", new HttpFields());
+        HeadersFrame frame = new HeadersFrame(request, null, true);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        client.newStream(frame, promise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                dataQueue.offer(frame);
+                dataQueue.offer(callback);
+                // Do not consume the data yet.
+                if (received.addAndGet(frame.getData().remaining()) == windowSize)
+                    latch.countDown();
+            }
+        });
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+
+        // Reset and consume.
+        stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+        dataQueue.stream()
+                .filter(item -> item instanceof Callback)
+                .map(item -> (Callback)item)
+                .forEach(Callback::succeeded);
+
+        Assert.assertTrue(writeLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testResetAfterAsyncRequestAsyncWriteStalledByFlowControl() throws Exception
+    {
+        int windowSize = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
+        CountDownLatch writeLatch = new CountDownLatch(1);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                AsyncContext asyncContext = request.startAsync();
+                ServletOutputStream output = response.getOutputStream();
+                output.setWriteListener(new WriteListener()
+                {
+                    private boolean written;
+
+                    @Override
+                    public void onWritePossible() throws IOException
+                    {
+                        while (output.isReady())
+                        {
+                            if (written)
+                            {
+                                asyncContext.complete();
+                                break;
+                            }
+                            else
+                            {
+                                output.write(new byte[10 * windowSize]);
+                                written = true;
+                            }
+                        }
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        writeLatch.countDown();
+                    }
+                });
+            }
+        });
+
+        Deque<Callback> dataQueue = new ArrayDeque<>();
+        AtomicLong received = new AtomicLong();
+        CountDownLatch latch = new CountDownLatch(1);
+        Session client = newClient(new Session.Listener.Adapter());
+        MetaData.Request request = newRequest("GET", new HttpFields());
+        HeadersFrame frame = new HeadersFrame(request, null, true);
+        FuturePromise<Stream> promise = new FuturePromise<>();
+        client.newStream(frame, promise, new Stream.Listener.Adapter()
+        {
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                dataQueue.offer(callback);
+                // Do not consume the data yet.
+                if (received.addAndGet(frame.getData().remaining()) == windowSize)
+                    latch.countDown();
+            }
+        });
+        Stream stream = promise.get(5, TimeUnit.SECONDS);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+
+        // Reset and consume.
+        stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+        dataQueue.forEach(Callback::succeeded);
+
+        Assert.assertTrue(writeLatch.await(5, TimeUnit.SECONDS));
+    }
+}
diff --git a/jetty-http2/http2-client/src/test/resources/jetty-logging.properties b/jetty-http2/http2-client/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..5304801
--- /dev/null
+++ b/jetty-http2/http2-client/src/test/resources/jetty-logging.properties
@@ -0,0 +1,5 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.LEVEL=DEBUG
+#org.eclipse.jetty.http2.LEVEL=DEBUG
+org.eclipse.jetty.http2.hpack.LEVEL=INFO
+#org.eclipse.jetty.servlets.LEVEL=DEBUG
diff --git a/jetty-http2/http2-common/pom.xml b/jetty-http2/http2-common/pom.xml
new file mode 100644
index 0000000..17e958b
--- /dev/null
+++ b/jetty-http2/http2-common/pom.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.http2</groupId>
+    <artifactId>http2-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>http2-common</artifactId>
+  <name>Jetty :: HTTP2 :: Common</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty.http2</groupId>
+      <artifactId>http2-hpack</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-jmx</artifactId>
+      <version>${project.version}</version>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.common</bundle-symbolic-name>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <executions>
+          <execution>
+            <goals>
+              <goal>manifest</goal>
+            </goals>
+            <configuration>
+              <instructions>
+                <Export-Package>org.eclipse.jetty.http2.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}";</Export-Package>
+              </instructions>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/AbstractFlowControlStrategy.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/AbstractFlowControlStrategy.java
new file mode 100644
index 0000000..03a5abc
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/AbstractFlowControlStrategy.java
@@ -0,0 +1,244 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.annotation.ManagedOperation;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+@ManagedObject
+public abstract class AbstractFlowControlStrategy implements FlowControlStrategy, Dumpable
+{
+    protected static final Logger LOG = Log.getLogger(FlowControlStrategy.class);
+
+    private final AtomicLong sessionStall = new AtomicLong();
+    private final AtomicLong sessionStallTime = new AtomicLong();
+    private final Map<IStream, Long> streamsStalls = new ConcurrentHashMap<>();
+    private final AtomicLong streamsStallTime = new AtomicLong();
+    private int initialStreamSendWindow;
+    private int initialStreamRecvWindow;
+
+    public AbstractFlowControlStrategy(int initialStreamSendWindow)
+    {
+        this.initialStreamSendWindow = initialStreamSendWindow;
+        this.initialStreamRecvWindow = DEFAULT_WINDOW_SIZE;
+    }
+
+    @ManagedAttribute(value = "The initial size of stream's flow control send window", readonly = true)
+    public int getInitialStreamSendWindow()
+    {
+        return initialStreamSendWindow;
+    }
+
+    @ManagedAttribute(value = "The initial size of stream's flow control receive window", readonly = true)
+    public int getInitialStreamRecvWindow()
+    {
+        return initialStreamRecvWindow;
+    }
+
+    @Override
+    public void onStreamCreated(IStream stream)
+    {
+        stream.updateSendWindow(initialStreamSendWindow);
+        stream.updateRecvWindow(initialStreamRecvWindow);
+    }
+
+    @Override
+    public void onStreamDestroyed(IStream stream)
+    {
+    }
+
+    @Override
+    public void updateInitialStreamWindow(ISession session, int initialStreamWindow, boolean local)
+    {
+        int previousInitialStreamWindow;
+        if (local)
+        {
+            previousInitialStreamWindow = getInitialStreamRecvWindow();
+            this.initialStreamRecvWindow = initialStreamWindow;
+        }
+        else
+        {
+            previousInitialStreamWindow = getInitialStreamSendWindow();
+            this.initialStreamSendWindow = initialStreamWindow;
+        }
+        int delta = initialStreamWindow - previousInitialStreamWindow;
+
+        // SPEC: updates of the initial window size only affect stream windows, not session's.
+        for (Stream stream : session.getStreams())
+        {
+            if (local)
+            {
+                ((IStream)stream).updateRecvWindow(delta);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Updated initial stream recv window {} -> {} for {}", previousInitialStreamWindow, initialStreamWindow, stream);
+            }
+            else
+            {
+                session.onWindowUpdate((IStream)stream, new WindowUpdateFrame(stream.getId(), delta));
+            }
+        }
+    }
+
+    @Override
+    public void onWindowUpdate(ISession session, IStream stream, WindowUpdateFrame frame)
+    {
+        int delta = frame.getWindowDelta();
+        if (frame.getStreamId() > 0)
+        {
+            // The stream may have been removed concurrently.
+            if (stream != null)
+            {
+                int oldSize = stream.updateSendWindow(delta);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Updated stream send window {} -> {} for {}", oldSize, oldSize + delta, stream);
+                if (oldSize <= 0)
+                    onStreamUnstalled(stream);
+            }
+        }
+        else
+        {
+            int oldSize = session.updateSendWindow(delta);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Updated session send window {} -> {} for {}", oldSize, oldSize + delta, session);
+            if (oldSize <= 0)
+                onSessionUnstalled(session);
+        }
+    }
+
+    @Override
+    public void onDataReceived(ISession session, IStream stream, int length)
+    {
+        int oldSize = session.updateRecvWindow(-length);
+        if (LOG.isDebugEnabled())
+            LOG.debug("Data received, {} bytes, updated session recv window {} -> {} for {}", length, oldSize, oldSize - length, session);
+
+        if (stream != null)
+        {
+            oldSize = stream.updateRecvWindow(-length);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Data received, {} bytes, updated stream recv window {} -> {} for {}", length, oldSize, oldSize - length, stream);
+        }
+    }
+
+    @Override
+    public void windowUpdate(ISession session, IStream stream, WindowUpdateFrame frame)
+    {
+    }
+
+    @Override
+    public void onDataSending(IStream stream, int length)
+    {
+        if (length == 0)
+            return;
+
+        ISession session = stream.getSession();
+        int oldSessionWindow = session.updateSendWindow(-length);
+        int newSessionWindow = oldSessionWindow - length;
+        if (LOG.isDebugEnabled())
+            LOG.debug("Sending, session send window {} -> {} for {}", oldSessionWindow, newSessionWindow, session);
+        if (newSessionWindow <= 0)
+            onSessionStalled(session);
+
+        int oldStreamWindow = stream.updateSendWindow(-length);
+        int newStreamWindow = oldStreamWindow - length;
+        if (LOG.isDebugEnabled())
+            LOG.debug("Sending, stream send window {} -> {} for {}", oldStreamWindow, newStreamWindow, stream);
+        if (newStreamWindow <= 0)
+            onStreamStalled(stream);
+    }
+
+    @Override
+    public void onDataSent(IStream stream, int length)
+    {
+    }
+
+    protected void onSessionStalled(ISession session)
+    {
+        sessionStall.set(System.nanoTime());
+        if (LOG.isDebugEnabled())
+            LOG.debug("Session stalled {}", session);
+    }
+
+    protected void onStreamStalled(IStream stream)
+    {
+        streamsStalls.put(stream, System.nanoTime());
+        if (LOG.isDebugEnabled())
+            LOG.debug("Stream stalled {}", stream);
+    }
+
+    protected void onSessionUnstalled(ISession session)
+    {
+        sessionStallTime.addAndGet(System.nanoTime() - sessionStall.getAndSet(0));
+        if (LOG.isDebugEnabled())
+            LOG.debug("Session unstalled {}", session);
+    }
+
+    protected void onStreamUnstalled(IStream stream)
+    {
+        Long time = streamsStalls.remove(stream);
+        if (time != null)
+            streamsStallTime.addAndGet(System.nanoTime() - time);
+        if (LOG.isDebugEnabled())
+            LOG.debug("Stream unstalled {}", stream);
+    }
+
+    @ManagedAttribute(value = "The time, in milliseconds, that the session flow control has stalled", readonly = true)
+    public long getSessionStallTime()
+    {
+        return TimeUnit.NANOSECONDS.toMillis(sessionStallTime.get());
+    }
+
+    @ManagedAttribute(value = "The time, in milliseconds, that the streams flow control has stalled", readonly = true)
+    public long getStreamsStallTime()
+    {
+        return TimeUnit.NANOSECONDS.toMillis(streamsStallTime.get());
+    }
+
+    @ManagedOperation(value = "Resets the statistics", impact = "ACTION")
+    public void reset()
+    {
+        sessionStallTime.set(0);
+        streamsStallTime.set(0);
+    }
+
+    @Override
+    public String dump()
+    {
+        return ContainerLifeCycle.dump(this);
+    }
+
+    @Override
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        out.append(toString()).append(System.lineSeparator());
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/BufferingFlowControlStrategy.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/BufferingFlowControlStrategy.java
new file mode 100644
index 0000000..85db949
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/BufferingFlowControlStrategy.java
@@ -0,0 +1,220 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.util.Atomics;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+
+/**
+ * <p>A flow control strategy that accumulates updates and emits window control
+ * frames when the accumulated value reaches a threshold.</p>
+ * <p>The sender flow control window is represented in the receiver as two
+ * buckets: a bigger bucket, initially full, that is drained when data is
+ * received, and a smaller bucket, initially empty, that is filled when data is
+ * consumed. Only the smaller bucket can refill the bigger bucket.</p>
+ * <p>The smaller bucket is defined as a fraction of the bigger bucket.</p>
+ * <p>For a more visual representation, see the
+ * <a href="http://en.wikipedia.org/wiki/Shishi-odoshi">rocking bamboo fountain</a>.</p>
+ * <p>The algorithm works in this way.</p>
+ * <p>The initial bigger bucket (BB) capacity is 100, and let's imagine the smaller
+ * bucket (SB) being 40% of the bigger bucket: 40.</p>
+ * <p>The receiver receives a data frame of 60, so now BB=40; the data frame is
+ * passed to the application that consumes 25, so now SB=25. Since SB is not full,
+ * no window control frames are emitted.</p>
+ * <p>The application consumes other 20, so now SB=45. Since SB is full, its 45
+ * are transferred to BB, which is now BB=85, and a window control frame is sent
+ * with delta=45.</p>
+ * <p>The application consumes the remaining 15, so now SB=15, and no window
+ * control frame is emitted.</p>
+ */
+@ManagedObject
+public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy
+{
+    private final AtomicInteger maxSessionRecvWindow = new AtomicInteger(DEFAULT_WINDOW_SIZE);
+    private final AtomicInteger sessionLevel = new AtomicInteger();
+    private final Map<IStream, AtomicInteger> streamLevels = new ConcurrentHashMap<>();
+    private float bufferRatio;
+
+    public BufferingFlowControlStrategy(float bufferRatio)
+    {
+        this(DEFAULT_WINDOW_SIZE, bufferRatio);
+    }
+
+    public BufferingFlowControlStrategy(int initialStreamSendWindow, float bufferRatio)
+    {
+        super(initialStreamSendWindow);
+        this.bufferRatio = bufferRatio;
+    }
+
+    @ManagedAttribute("The ratio between the receive buffer and the consume buffer")
+    public float getBufferRatio()
+    {
+        return bufferRatio;
+    }
+
+    public void setBufferRatio(float bufferRatio)
+    {
+        this.bufferRatio = bufferRatio;
+    }
+
+    @Override
+    public void onStreamCreated(IStream stream)
+    {
+        super.onStreamCreated(stream);
+        streamLevels.put(stream, new AtomicInteger());
+    }
+
+    @Override
+    public void onStreamDestroyed(IStream stream)
+    {
+        streamLevels.remove(stream);
+        super.onStreamDestroyed(stream);
+    }
+
+    @Override
+    public void onDataConsumed(ISession session, IStream stream, int length)
+    {
+        if (length <= 0)
+            return;
+
+        float ratio = bufferRatio;
+
+        WindowUpdateFrame windowFrame = null;
+        int level = sessionLevel.addAndGet(length);
+        int maxLevel = (int)(maxSessionRecvWindow.get() * ratio);
+        if (level > maxLevel)
+        {
+            if (sessionLevel.compareAndSet(level, 0))
+            {
+                session.updateRecvWindow(level);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Data consumed, {} bytes, updated session recv window by {}/{} for {}", length, level, maxLevel, session);
+                windowFrame = new WindowUpdateFrame(0, level);
+            }
+            else
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Data consumed, {} bytes, concurrent session recv window level {}/{} for {}", length, sessionLevel, maxLevel, session);
+            }
+        }
+        else
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Data consumed, {} bytes, session recv window level {}/{} for {}", length, level, maxLevel, session);
+        }
+
+        Frame[] windowFrames = Frame.EMPTY_ARRAY;
+        if (stream != null)
+        {
+            if (stream.isClosed())
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Data consumed, {} bytes, ignoring update stream recv window for closed {}", length, stream);
+            }
+            else
+            {
+                AtomicInteger streamLevel = streamLevels.get(stream);
+                if (streamLevel != null)
+                {
+                    level = streamLevel.addAndGet(length);
+                    maxLevel = (int)(getInitialStreamRecvWindow() * ratio);
+                    if (level > maxLevel)
+                    {
+                        level = streamLevel.getAndSet(0);
+                        stream.updateRecvWindow(level);
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("Data consumed, {} bytes, updated stream recv window by {}/{} for {}", length, level, maxLevel, stream);
+                        WindowUpdateFrame frame = new WindowUpdateFrame(stream.getId(), level);
+                        if (windowFrame == null)
+                            windowFrame = frame;
+                        else
+                            windowFrames = new Frame[]{frame};
+                    }
+                    else
+                    {
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("Data consumed, {} bytes, stream recv window level {}/{} for {}", length, level, maxLevel, stream);
+                    }
+                }
+            }
+        }
+
+        if (windowFrame != null)
+            session.frames(stream, Callback.NOOP, windowFrame, windowFrames);
+    }
+
+    @Override
+    public void windowUpdate(ISession session, IStream stream, WindowUpdateFrame frame)
+    {
+        super.windowUpdate(session, stream, frame);
+
+        // Window updates cannot be negative.
+        // The SettingsFrame.INITIAL_WINDOW_SIZE setting
+        // only influences the *stream* window size.
+        // Therefore the session window can only be enlarged,
+        // and here we keep track of its max value.
+
+        // Updating the max session recv window is done here
+        // so that if a peer decides to send an unilateral
+        // window update to enlarge the session window,
+        // without the corresponding data consumption, here
+        // we can track it.
+        // Note that it is not perfect, since there is a time
+        // window between the session recv window being updated
+        // before the window update frame is sent, and the
+        // invocation of this method: in between data may arrive
+        // and reduce the session recv window size.
+        // But eventually the max value will be seen.
+
+        // Note that we cannot avoid the time window described
+        // above by updating the session recv window from here
+        // because there is a race between the sender and the
+        // receiver: the sender may receive a window update and
+        // send more data, while this method has not yet been
+        // invoked; when the data is received the session recv
+        // window may become negative and the connection will
+        // be closed (per specification).
+
+        if (frame.getStreamId() == 0)
+        {
+            int sessionWindow = session.updateRecvWindow(0);
+            Atomics.updateMax(maxSessionRecvWindow, sessionWindow);
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%x[ratio=%.2f,sessionLevel=%s,sessionStallTime=%dms,streamsStallTime=%dms]",
+                getClass().getSimpleName(),
+                hashCode(),
+                bufferRatio,
+                sessionLevel,
+                getSessionStallTime(),
+                getStreamsStallTime());
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/CloseState.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/CloseState.java
new file mode 100644
index 0000000..a0a0a43
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/CloseState.java
@@ -0,0 +1,24 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+public enum CloseState
+{
+    NOT_CLOSED, LOCALLY_CLOSED, REMOTELY_CLOSED, CLOSED
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ErrorCode.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ErrorCode.java
new file mode 100644
index 0000000..fceb6f8
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ErrorCode.java
@@ -0,0 +1,103 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Standard HTTP/2 error codes.
+ */
+public enum ErrorCode
+{
+    /**
+     * Indicates no errors.
+     */
+    NO_ERROR(0),
+    /**
+     * Indicates a generic HTTP/2 protocol violation.
+     */
+    PROTOCOL_ERROR(1),
+    /**
+     * Indicates an internal error.
+     */
+    INTERNAL_ERROR(2),
+    /**
+     * Indicates a HTTP/2 flow control violation.
+     */
+    FLOW_CONTROL_ERROR(3),
+    /**
+     * Indicates that a SETTINGS frame did not receive a reply in a timely manner.
+     */
+    SETTINGS_TIMEOUT_ERROR(4),
+    /**
+     * Indicates that a stream frame has been received after the stream was closed.
+     */
+    STREAM_CLOSED_ERROR(5),
+    /**
+     * Indicates that a frame has an invalid length.
+     */
+    FRAME_SIZE_ERROR(6),
+    /**
+     * Indicates that a stream was rejected before application processing.
+     */
+    REFUSED_STREAM_ERROR(7),
+    /**
+     * Indicates that a stream is no longer needed.
+     */
+    CANCEL_STREAM_ERROR(8),
+    /**
+     * Indicates inability to maintain the HPACK compression context.
+     */
+    COMPRESSION_ERROR(9),
+    /**
+     * Indicates that the connection established by a HTTP CONNECT was abnormally closed.
+     */
+    HTTP_CONNECT_ERROR(10),
+    /**
+     * Indicates that the other peer might be generating excessive load.
+     */
+    ENHANCE_YOUR_CALM_ERROR(11),
+    /**
+     * Indicates that the transport properties do not meet minimum security requirements.
+     */
+    INADEQUATE_SECURITY_ERROR(12),
+    /**
+     * Indicates that HTTP/1.1 must be used rather than HTTP/2.
+     */
+    HTTP_1_1_REQUIRED_ERROR(13);
+
+    public final int code;
+
+    private ErrorCode(int code)
+    {
+        this.code = code;
+        Codes.codes.put(code, this);
+    }
+
+    public static ErrorCode from(int error)
+    {
+        return Codes.codes.get(error);
+    }
+
+    private static class Codes
+    {
+        private static final Map<Integer, ErrorCode> codes = new HashMap<>();
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/Flags.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/Flags.java
new file mode 100644
index 0000000..5f1aa46
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/Flags.java
@@ -0,0 +1,29 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+public interface Flags
+{
+    public static final int NONE = 0x00;
+    public static final int END_STREAM = 0x01;
+    public static final int ACK = 0x01;
+    public static final int END_HEADERS = 0x04;
+    public static final int PADDING = 0x08;
+    public static final int PRIORITY = 0x20;
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/FlowControlStrategy.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/FlowControlStrategy.java
new file mode 100644
index 0000000..6ebf373
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/FlowControlStrategy.java
@@ -0,0 +1,49 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+
+public interface FlowControlStrategy
+{
+    public static int DEFAULT_WINDOW_SIZE = 65535;
+
+    public void onStreamCreated(IStream stream);
+
+    public void onStreamDestroyed(IStream stream);
+
+    public void updateInitialStreamWindow(ISession session, int initialStreamWindow, boolean local);
+
+    public void onWindowUpdate(ISession session, IStream stream, WindowUpdateFrame frame);
+
+    public void onDataReceived(ISession session, IStream stream, int length);
+
+    public void onDataConsumed(ISession session, IStream stream, int length);
+
+    public void windowUpdate(ISession session, IStream stream, WindowUpdateFrame frame);
+
+    public void onDataSending(IStream stream, int length);
+
+    public void onDataSent(IStream stream, int length);
+
+    public interface Factory
+    {
+        public FlowControlStrategy newFlowControlStrategy();
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Cipher.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Cipher.java
new file mode 100644
index 0000000..6dbdfe2
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Cipher.java
@@ -0,0 +1,356 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+import java.util.Comparator;
+
+import org.eclipse.jetty.util.ArrayTrie;
+import org.eclipse.jetty.util.Trie;
+
+public class HTTP2Cipher
+{
+    public static final Comparator<String> COMPARATOR = new CipherComparator();
+
+    private final static Trie<Boolean> __blackProtocols = new ArrayTrie<>(6*5);
+    private final static Trie<Boolean> __blackCiphers = new ArrayTrie<>(275*40);
+
+    static
+    {
+        for (String p : new String[]
+        {
+                "TLSv1.2","TLSv1.1", "TLSv1", "SSL", "SSLv2", "SSLv3"
+        })
+        {
+            __blackProtocols.put(p,Boolean.TRUE);
+        }
+
+        for (String c : new String[]
+        {
+            "TLS_NULL_WITH_NULL_NULL",
+            "TLS_RSA_WITH_NULL_MD5",
+            "TLS_RSA_WITH_NULL_SHA",
+            "TLS_RSA_EXPORT_WITH_RC4_40_MD5",
+            "TLS_RSA_WITH_RC4_128_MD5",
+            "TLS_RSA_WITH_RC4_128_SHA",
+            "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
+            "TLS_RSA_WITH_IDEA_CBC_SHA",
+            "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA",
+            "TLS_RSA_WITH_DES_CBC_SHA",
+            "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
+            "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
+            "TLS_DH_DSS_WITH_DES_CBC_SHA",
+            "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA",
+            "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
+            "TLS_DH_RSA_WITH_DES_CBC_SHA",
+            "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA",
+            "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+            "TLS_DHE_DSS_WITH_DES_CBC_SHA",
+            "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+            "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+            "TLS_DHE_RSA_WITH_DES_CBC_SHA",
+            "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+            "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5",
+            "TLS_DH_anon_WITH_RC4_128_MD5",
+            "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
+            "TLS_DH_anon_WITH_DES_CBC_SHA",
+            "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA",
+            "TLS_KRB5_WITH_DES_CBC_SHA",
+            "TLS_KRB5_WITH_3DES_EDE_CBC_SHA",
+            "TLS_KRB5_WITH_RC4_128_SHA",
+            "TLS_KRB5_WITH_IDEA_CBC_SHA",
+            "TLS_KRB5_WITH_DES_CBC_MD5",
+            "TLS_KRB5_WITH_3DES_EDE_CBC_MD5",
+            "TLS_KRB5_WITH_RC4_128_MD5",
+            "TLS_KRB5_WITH_IDEA_CBC_MD5",
+            "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",
+            "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",
+            "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
+            "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",
+            "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",
+            "TLS_KRB5_EXPORT_WITH_RC4_40_MD5",
+            "TLS_PSK_WITH_NULL_SHA",
+            "TLS_DHE_PSK_WITH_NULL_SHA",
+            "TLS_RSA_PSK_WITH_NULL_SHA",
+            "TLS_RSA_WITH_AES_128_CBC_SHA",
+            "TLS_DH_DSS_WITH_AES_128_CBC_SHA",
+            "TLS_DH_RSA_WITH_AES_128_CBC_SHA",
+            "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
+            "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
+            "TLS_DH_anon_WITH_AES_128_CBC_SHA",
+            "TLS_RSA_WITH_AES_256_CBC_SHA",
+            "TLS_DH_DSS_WITH_AES_256_CBC_SHA",
+            "TLS_DH_RSA_WITH_AES_256_CBC_SHA",
+            "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
+            "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
+            "TLS_DH_anon_WITH_AES_256_CBC_SHA",
+            "TLS_RSA_WITH_NULL_SHA256",
+            "TLS_RSA_WITH_AES_128_CBC_SHA256",
+            "TLS_RSA_WITH_AES_256_CBC_SHA256",
+            "TLS_DH_DSS_WITH_AES_128_CBC_SHA256",
+            "TLS_DH_RSA_WITH_AES_128_CBC_SHA256",
+            "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
+            "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",
+            "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA",
+            "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA",
+            "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",
+            "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",
+            "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA",
+            "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
+            "TLS_DH_DSS_WITH_AES_256_CBC_SHA256",
+            "TLS_DH_RSA_WITH_AES_256_CBC_SHA256",
+            "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
+            "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
+            "TLS_DH_anon_WITH_AES_128_CBC_SHA256",
+            "TLS_DH_anon_WITH_AES_256_CBC_SHA256",
+            "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
+            "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA",
+            "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA",
+            "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",
+            "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
+            "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",
+            "TLS_PSK_WITH_RC4_128_SHA",
+            "TLS_PSK_WITH_3DES_EDE_CBC_SHA",
+            "TLS_PSK_WITH_AES_128_CBC_SHA",
+            "TLS_PSK_WITH_AES_256_CBC_SHA",
+            "TLS_DHE_PSK_WITH_RC4_128_SHA",
+            "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA",
+            "TLS_DHE_PSK_WITH_AES_128_CBC_SHA",
+            "TLS_DHE_PSK_WITH_AES_256_CBC_SHA",
+            "TLS_RSA_PSK_WITH_RC4_128_SHA",
+            "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA",
+            "TLS_RSA_PSK_WITH_AES_128_CBC_SHA",
+            "TLS_RSA_PSK_WITH_AES_256_CBC_SHA",
+            "TLS_RSA_WITH_SEED_CBC_SHA",
+            "TLS_DH_DSS_WITH_SEED_CBC_SHA",
+            "TLS_DH_RSA_WITH_SEED_CBC_SHA",
+            "TLS_DHE_DSS_WITH_SEED_CBC_SHA",
+            "TLS_DHE_RSA_WITH_SEED_CBC_SHA",
+            "TLS_DH_anon_WITH_SEED_CBC_SHA",
+            "TLS_RSA_WITH_AES_128_GCM_SHA256",
+            "TLS_RSA_WITH_AES_256_GCM_SHA384",
+            "TLS_DH_RSA_WITH_AES_128_GCM_SHA256",
+            "TLS_DH_RSA_WITH_AES_256_GCM_SHA384",
+            "TLS_DH_DSS_WITH_AES_128_GCM_SHA256",
+            "TLS_DH_DSS_WITH_AES_256_GCM_SHA384",
+            "TLS_DH_anon_WITH_AES_128_GCM_SHA256",
+            "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
+            "TLS_PSK_WITH_AES_128_GCM_SHA256",
+            "TLS_PSK_WITH_AES_256_GCM_SHA384",
+            "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256",
+            "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384",
+            "TLS_PSK_WITH_AES_128_CBC_SHA256",
+            "TLS_PSK_WITH_AES_256_CBC_SHA384",
+            "TLS_PSK_WITH_NULL_SHA256",
+            "TLS_PSK_WITH_NULL_SHA384",
+            "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",
+            "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",
+            "TLS_DHE_PSK_WITH_NULL_SHA256",
+            "TLS_DHE_PSK_WITH_NULL_SHA384",
+            "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256",
+            "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384",
+            "TLS_RSA_PSK_WITH_NULL_SHA256",
+            "TLS_RSA_PSK_WITH_NULL_SHA384",
+            "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",
+            "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256",
+            "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256",
+            "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
+            "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
+            "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256",
+            "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
+            "TLS_ECDH_ECDSA_WITH_NULL_SHA",
+            "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
+            "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
+            "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
+            "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
+            "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
+            "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
+            "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
+            "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+            "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+            "TLS_ECDH_RSA_WITH_NULL_SHA",
+            "TLS_ECDH_RSA_WITH_RC4_128_SHA",
+            "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
+            "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
+            "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
+            "TLS_ECDHE_RSA_WITH_NULL_SHA",
+            "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
+            "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
+            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
+            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+            "TLS_ECDH_anon_WITH_NULL_SHA",
+            "TLS_ECDH_anon_WITH_RC4_128_SHA",
+            "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
+            "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
+            "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
+            "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA",
+            "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA",
+            "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA",
+            "TLS_SRP_SHA_WITH_AES_128_CBC_SHA",
+            "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA",
+            "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA",
+            "TLS_SRP_SHA_WITH_AES_256_CBC_SHA",
+            "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA",
+            "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA",
+            "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
+            "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
+            "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
+            "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
+            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
+            "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
+            "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
+            "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
+            "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
+            "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
+            "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",
+            "TLS_ECDHE_PSK_WITH_RC4_128_SHA",
+            "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA",
+            "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
+            "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
+            "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",
+            "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384",
+            "TLS_ECDHE_PSK_WITH_NULL_SHA",
+            "TLS_ECDHE_PSK_WITH_NULL_SHA256",
+            "TLS_ECDHE_PSK_WITH_NULL_SHA384",
+            "TLS_RSA_WITH_ARIA_128_CBC_SHA256",
+            "TLS_RSA_WITH_ARIA_256_CBC_SHA384",
+            "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256",
+            "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384",
+            "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256",
+            "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384",
+            "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256",
+            "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384",
+            "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256",
+            "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384",
+            "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256",
+            "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384",
+            "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256",
+            "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384",
+            "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256",
+            "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384",
+            "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256",
+            "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384",
+            "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256",
+            "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384",
+            "TLS_RSA_WITH_ARIA_128_GCM_SHA256",
+            "TLS_RSA_WITH_ARIA_256_GCM_SHA384",
+            "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256",
+            "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384",
+            "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256",
+            "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384",
+            "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256",
+            "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384",
+            "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256",
+            "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384",
+            "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256",
+            "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384",
+            "TLS_PSK_WITH_ARIA_128_CBC_SHA256",
+            "TLS_PSK_WITH_ARIA_256_CBC_SHA384",
+            "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256",
+            "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384",
+            "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256",
+            "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384",
+            "TLS_PSK_WITH_ARIA_128_GCM_SHA256",
+            "TLS_PSK_WITH_ARIA_256_GCM_SHA384",
+            "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256",
+            "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384",
+            "TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256",
+            "TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384",
+            "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
+            "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
+            "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384",
+            "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384",
+            "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256",
+            "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384",
+            "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256",
+            "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384",
+            "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256",
+            "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384",
+            "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256",
+            "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384",
+            "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256",
+            "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384",
+            "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256",
+            "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384",
+            "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256",
+            "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384",
+            "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256",
+            "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384",
+            "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384",
+            "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",
+            "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384",
+            "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",
+            "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",
+            "TLS_RSA_WITH_AES_128_CCM",
+            "TLS_RSA_WITH_AES_256_CCM",
+            "TLS_RSA_WITH_AES_128_CCM_8",
+            "TLS_RSA_WITH_AES_256_CCM_8",
+            "TLS_PSK_WITH_AES_128_CCM",
+            "TLS_PSK_WITH_AES_256_CCM",
+            "TLS_PSK_WITH_AES_128_CCM_8",
+            "TLS_PSK_WITH_AES_256_CCM_8"
+        })
+        {
+            __blackCiphers.put(c,Boolean.TRUE);
+        }
+    }
+
+    public static boolean isBlackListProtocol(String tlsProtocol)
+    {
+        Boolean b = __blackProtocols.get(tlsProtocol);
+        return b != null && b;
+    }
+
+    public static boolean isBlackListCipher(String tlsCipher)
+    {
+        Boolean b = __blackCiphers.get(tlsCipher);
+        return b != null && b;
+    }
+
+    /**
+     * Comparator that orders non blacklisted ciphers before blacklisted ones.
+     */
+    public static class CipherComparator implements Comparator<String>
+    {
+        @Override
+        public int compare(String c1, String c2)
+        {
+            boolean b1=isBlackListCipher(c1);
+            boolean b2=isBlackListCipher(c2);
+            if (b1==b2)
+                return 0;
+            if (b1)
+                return 1;
+            return -1;
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java
new file mode 100644
index 0000000..7cabc37
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java
@@ -0,0 +1,260 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.Queue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
+
+public class HTTP2Connection extends AbstractConnection
+{
+    protected static final Logger LOG = Log.getLogger(HTTP2Connection.class);
+
+    private final Queue<Runnable> tasks = new ArrayDeque<>();
+    private final HTTP2Producer producer = new HTTP2Producer();
+    private final AtomicLong bytesIn = new AtomicLong();
+    private final ByteBufferPool byteBufferPool;
+    private final Parser parser;
+    private final ISession session;
+    private final int bufferSize;
+    private final ExecutionStrategy executionStrategy;
+
+    public HTTP2Connection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize, ExecutionStrategy.Factory executionFactory)
+    {
+        super(endPoint, executor);
+        this.byteBufferPool = byteBufferPool;
+        this.parser = parser;
+        this.session = session;
+        this.bufferSize = bufferSize;
+        this.executionStrategy = executionFactory.newExecutionStrategy(producer, executor);
+    }
+
+    @Override
+    public long getBytesIn()
+    {
+        return bytesIn.get();
+    }
+
+    @Override
+    public long getBytesOut()
+    {
+        return session.getBytesWritten();
+    }
+
+    public ISession getSession()
+    {
+        return session;
+    }
+
+    protected Parser getParser()
+    {
+        return parser;
+    }
+
+    protected void setInputBuffer(ByteBuffer buffer)
+    {
+        producer.buffer = buffer;
+    }
+
+    @Override
+    public void onOpen()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("HTTP2 Open {} ", this);
+        super.onOpen();
+        executionStrategy.execute();
+    }
+
+    @Override
+    public void onClose()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("HTTP2 Close {} ", this);
+        super.onClose();
+    }
+
+    @Override
+    public void onFillable()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("HTTP2 onFillable {} ", this);
+        executionStrategy.execute();
+    }
+
+    private int fill(EndPoint endPoint, ByteBuffer buffer)
+    {
+        try
+        {
+            if (endPoint.isInputShutdown())
+                return -1;
+            return endPoint.fill(buffer);
+        }
+        catch (IOException x)
+        {
+            LOG.debug("Could not read from " + endPoint, x);
+            return -1;
+        }
+    }
+
+    @Override
+    public boolean onIdleExpired()
+    {
+        boolean idle = isFillInterested();
+        if (idle)
+        {
+            boolean close = session.onIdleTimeout();
+            if (close)
+                session.close(ErrorCode.NO_ERROR.code, "idle_timeout", Callback.NOOP);
+        }
+        return false;
+    }
+
+    protected void offerTask(Runnable task, boolean dispatch)
+    {
+        offerTask(task);
+        if (dispatch)
+            executionStrategy.dispatch();
+        else
+            executionStrategy.execute();
+    }
+
+    @Override
+    public void close()
+    {
+        // We don't call super from here, otherwise we close the
+        // endPoint and we're not able to read or write anymore.
+        session.close(ErrorCode.NO_ERROR.code, "close", Callback.NOOP);
+    }
+
+    private void offerTask(Runnable task)
+    {
+        synchronized (this)
+        {
+            tasks.offer(task);
+        }
+    }
+
+    private Runnable pollTask()
+    {
+        synchronized (this)
+        {
+            return tasks.poll();
+        }
+    }
+
+    protected class HTTP2Producer implements ExecutionStrategy.Producer
+    {
+        private final Callback fillCallback = new FillCallback();
+        private ByteBuffer buffer;
+
+        @Override
+        public Runnable produce()
+        {
+            Runnable task = pollTask();
+            if (LOG.isDebugEnabled())
+                LOG.debug("Dequeued task {}", task);
+            if (task != null)
+                return task;
+
+            if (isFillInterested())
+                return null;
+
+            if (buffer == null)
+                buffer = byteBufferPool.acquire(bufferSize, false); // TODO: make directness customizable
+            boolean looping = BufferUtil.hasContent(buffer);
+            while (true)
+            {
+                if (looping)
+                {
+                    while (buffer.hasRemaining())
+                        parser.parse(buffer);
+
+                    task = pollTask();
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Dequeued new task {}", task);
+                    if (task != null)
+                    {
+                        release();
+                        return task;
+                    }
+                }
+
+                int filled = fill(getEndPoint(), buffer);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Filled {} bytes", filled);
+
+                if (filled == 0)
+                {
+                    release();
+                    getEndPoint().fillInterested(fillCallback);
+                    return null;
+                }
+                else if (filled < 0)
+                {
+                    release();
+                    session.onShutdown();
+                    return null;
+                }
+                else
+                {
+                    bytesIn.addAndGet(filled);
+                }
+
+                looping = true;
+            }
+        }
+
+        private void release()
+        {
+            if (buffer != null && !buffer.hasRemaining())
+            {
+                byteBufferPool.release(buffer);
+                buffer = null;
+            }
+        }
+    }
+
+    private class FillCallback implements Callback.NonBlocking
+    {
+        @Override
+        public void succeeded()
+        {
+            onFillable();
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            onFillInterestedFailed(x);
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Flusher.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Flusher.java
new file mode 100644
index 0000000..783dcaa
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Flusher.java
@@ -0,0 +1,389 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IteratingCallback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class HTTP2Flusher extends IteratingCallback
+{
+    private static final Logger LOG = Log.getLogger(HTTP2Flusher.class);
+
+    private final Queue<WindowEntry> windows = new ArrayDeque<>();
+    private final List<Entry> frames = new ArrayList<>();
+    private final Map<IStream, Integer> streams = new HashMap<>();
+    private final List<Entry> resets = new ArrayList<>();
+    private final List<Entry> actives = new ArrayList<>();
+    private final HTTP2Session session;
+    private final ByteBufferPool.Lease lease;
+    private Throwable terminated;
+
+    public HTTP2Flusher(HTTP2Session session)
+    {
+        this.session = session;
+        this.lease = new ByteBufferPool.Lease(session.getGenerator().getByteBufferPool());
+    }
+
+    public void window(IStream stream, WindowUpdateFrame frame)
+    {
+        Throwable closed;
+        synchronized (this)
+        {
+            closed = terminated;
+            if (closed == null)
+                windows.offer(new WindowEntry(stream, frame));
+        }
+        // Flush stalled data.
+        if (closed == null)
+            iterate();
+    }
+
+    public boolean prepend(Entry entry)
+    {
+        Throwable closed;
+        synchronized (this)
+        {
+            closed = terminated;
+            if (closed == null)
+            {
+                frames.add(0, entry);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Prepended {}, frames={}", entry, frames.size());
+            }
+        }
+        if (closed == null)
+            return true;
+        closed(entry, closed);
+        return false;
+    }
+
+    public boolean append(Entry entry)
+    {
+        Throwable closed;
+        synchronized (this)
+        {
+            closed = terminated;
+            if (closed == null)
+            {
+                frames.add(entry);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Appended {}, frames={}", entry, frames.size());
+            }
+        }
+        if (closed == null)
+            return true;
+        closed(entry, closed);
+        return false;
+    }
+
+    private Entry remove(int index)
+    {
+        synchronized (this)
+        {
+            return frames.remove(index);
+        }
+    }
+
+    public int getQueueSize()
+    {
+        synchronized (this)
+        {
+            return frames.size();
+        }
+    }
+
+    @Override
+    protected Action process() throws Throwable
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Flushing {}", session);
+
+        synchronized (this)
+        {
+            if (terminated != null)
+                throw terminated;
+
+            // First thing, update the window sizes, so we can
+            // reason about the frames to remove from the queue.
+            while (!windows.isEmpty())
+            {
+                WindowEntry entry = windows.poll();
+                entry.perform();
+            }
+
+            // Now the window sizes cannot change.
+            // Window updates that happen concurrently will
+            // be queued and processed on the next iteration.
+            int sessionWindow = session.getSendWindow();
+
+            int index = 0;
+            int size = frames.size();
+            while (index < size)
+            {
+                Entry entry = frames.get(index);
+                IStream stream = entry.stream;
+
+                // If the stream has been reset, don't send the frame.
+                if (stream != null && stream.isReset() && !entry.isProtocol())
+                {
+                    remove(index);
+                    --size;
+                    resets.add(entry);
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Gathered for reset {}", entry);
+                    continue;
+                }
+
+                // Check if the frame fits in the flow control windows.
+                int remaining = entry.dataRemaining();
+                if (remaining > 0)
+                {
+                    if (sessionWindow <= 0)
+                    {
+                        ++index;
+                        // There may be *non* flow controlled frames to send.
+                        continue;
+                    }
+
+                    if (stream != null)
+                    {
+                        // The stream may have a smaller window than the session.
+                        Integer streamWindow = streams.get(stream);
+                        if (streamWindow == null)
+                        {
+                            streamWindow = stream.updateSendWindow(0);
+                            streams.put(stream, streamWindow);
+                        }
+
+                        // Is it a frame belonging to an already stalled stream ?
+                        if (streamWindow <= 0)
+                        {
+                            ++index;
+                            // There may be *non* flow controlled frames to send.
+                            continue;
+                        }
+                    }
+
+                    // The frame fits both flow control windows, reduce them.
+                    sessionWindow -= remaining;
+                    if (stream != null)
+                        streams.put(stream, streams.get(stream) - remaining);
+                }
+
+                // The frame will be written, remove it from the queue.
+                remove(index);
+                --size;
+                actives.add(entry);
+
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Gathered for write {}", entry);
+            }
+            streams.clear();
+        }
+
+        // Perform resets outside the sync block.
+        for (int i = 0; i < resets.size(); ++i)
+        {
+            Entry entry = resets.get(i);
+            entry.reset();
+        }
+        resets.clear();
+
+        if (actives.isEmpty())
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Flushed {}", session);
+            return Action.IDLE;
+        }
+
+        for (int i = 0; i < actives.size(); ++i)
+        {
+            Entry entry = actives.get(i);
+            Throwable failure = entry.generate(lease);
+            if (failure != null)
+            {
+                // Failure to generate the entry is catastrophic.
+                failed(failure);
+                return Action.SUCCEEDED;
+            }
+        }
+
+        List<ByteBuffer> byteBuffers = lease.getByteBuffers();
+        if (LOG.isDebugEnabled())
+            LOG.debug("Writing {} buffers ({} bytes) for {} frames {}", byteBuffers.size(), lease.getTotalLength(), actives.size(), actives);
+        session.getEndPoint().write(this, byteBuffers.toArray(new ByteBuffer[byteBuffers.size()]));
+        return Action.SCHEDULED;
+    }
+
+    @Override
+    public void succeeded()
+    {
+        lease.recycle();
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("Written {} frames for {}", actives.size(), actives);
+
+        actives.forEach(Entry::succeeded);
+        actives.clear();
+
+        super.succeeded();
+    }
+
+    @Override
+    protected void onCompleteSuccess()
+    {
+        throw new IllegalStateException();
+    }
+
+    @Override
+    protected void onCompleteFailure(Throwable x)
+    {
+        lease.recycle();
+
+        Throwable closed;
+        synchronized (this)
+        {
+            closed = terminated;
+            terminated = x;
+            if (LOG.isDebugEnabled())
+                LOG.debug("{}, active/queued={}/{}", closed != null ? "Closing" : "Failing", actives.size(), frames.size());
+            actives.addAll(frames);
+            frames.clear();
+        }
+
+        actives.forEach(entry -> entry.failed(x));
+        actives.clear();
+
+        // If the failure came from within the
+        // flusher, we need to close the connection.
+        if (closed == null)
+            session.abort(x);
+    }
+
+    void terminate(Throwable cause)
+    {
+        Throwable closed;
+        synchronized (this)
+        {
+            closed = terminated;
+            terminated = cause;
+            if (LOG.isDebugEnabled())
+                LOG.debug("{}", closed != null ? "Terminated" : "Terminating");
+        }
+        if (closed == null)
+            iterate();
+    }
+
+    private void closed(Entry entry, Throwable failure)
+    {
+        entry.failed(failure);
+    }
+
+    public static abstract class Entry extends Callback.Nested
+    {
+        protected final Frame frame;
+        protected final IStream stream;
+
+        protected Entry(Frame frame, IStream stream, Callback callback)
+        {
+            super(callback);
+            this.frame = frame;
+            this.stream = stream;
+        }
+
+        public int dataRemaining()
+        {
+            return 0;
+        }
+
+        public Throwable generate(ByteBufferPool.Lease lease)
+        {
+            return null;
+        }
+
+        public void reset()
+        {
+            failed(new EofException("reset"));
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            if (stream != null)
+            {
+                stream.close();
+                stream.getSession().removeStream(stream);
+            }
+            super.failed(x);
+        }
+
+        public boolean isProtocol()
+        {
+            switch (frame.getType())
+            {
+                case PRIORITY:
+                case RST_STREAM:
+                case GO_AWAY:
+                case WINDOW_UPDATE:
+                case DISCONNECT:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        @Override
+        public String toString()
+        {
+            return frame.toString();
+        }
+    }
+
+    private class WindowEntry
+    {
+        private final IStream stream;
+        private final WindowUpdateFrame frame;
+
+        public WindowEntry(IStream stream, WindowUpdateFrame frame)
+        {
+            this.stream = stream;
+            this.frame = frame;
+        }
+
+        public void perform()
+        {
+            FlowControlStrategy flowControl = session.getFlowControlStrategy();
+            flowControl.onWindowUpdate(session, stream, frame);
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java
new file mode 100644
index 0000000..1d1772c
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java
@@ -0,0 +1,1325 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+import java.io.IOException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.DisconnectFrame;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PingFrame;
+import org.eclipse.jetty.http2.frames.PriorityFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.http2.generator.Generator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.Atomics;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.CountingCallback;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Scheduler;
+
+@ManagedObject
+public abstract class HTTP2Session extends ContainerLifeCycle implements ISession, Parser.Listener
+{
+    private static final Logger LOG = Log.getLogger(HTTP2Session.class);
+
+    private final ConcurrentMap<Integer, IStream> streams = new ConcurrentHashMap<>();
+    private final AtomicInteger streamIds = new AtomicInteger();
+    private final AtomicInteger lastStreamId = new AtomicInteger();
+    private final AtomicInteger localStreamCount = new AtomicInteger();
+    private final AtomicInteger remoteStreamCount = new AtomicInteger();
+    private final AtomicInteger sendWindow = new AtomicInteger();
+    private final AtomicInteger recvWindow = new AtomicInteger();
+    private final AtomicReference<CloseState> closed = new AtomicReference<>(CloseState.NOT_CLOSED);
+    private final AtomicLong bytesWritten = new AtomicLong();
+    private final Scheduler scheduler;
+    private final EndPoint endPoint;
+    private final Generator generator;
+    private final Session.Listener listener;
+    private final FlowControlStrategy flowControl;
+    private final HTTP2Flusher flusher;
+    private int maxLocalStreams;
+    private int maxRemoteStreams;
+    private long streamIdleTimeout;
+    private int initialSessionRecvWindow;
+    private boolean pushEnabled;
+    private long idleTime;
+
+    public HTTP2Session(Scheduler scheduler, EndPoint endPoint, Generator generator, Session.Listener listener, FlowControlStrategy flowControl, int initialStreamId)
+    {
+        this.scheduler = scheduler;
+        this.endPoint = endPoint;
+        this.generator = generator;
+        this.listener = listener;
+        this.flowControl = flowControl;
+        this.flusher = new HTTP2Flusher(this);
+        this.maxLocalStreams = -1;
+        this.maxRemoteStreams = -1;
+        this.streamIds.set(initialStreamId);
+        this.streamIdleTimeout = endPoint.getIdleTimeout();
+        this.sendWindow.set(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
+        this.recvWindow.set(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
+        this.pushEnabled = true; // SPEC: by default, push is enabled.
+        this.idleTime = System.nanoTime();
+    }
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        addBean(flowControl);
+        super.doStart();
+    }
+
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+        close(ErrorCode.NO_ERROR.code, "stop", new Callback.NonBlocking()
+        {
+            @Override
+            public void succeeded()
+            {
+                disconnect();
+            }
+
+            @Override
+            public void failed(Throwable x)
+            {
+                disconnect();
+            }
+        });
+    }
+
+    @ManagedAttribute(value = "The flow control strategy", readonly = true)
+    public FlowControlStrategy getFlowControlStrategy()
+    {
+        return flowControl;
+    }
+
+    public int getMaxLocalStreams()
+    {
+        return maxLocalStreams;
+    }
+
+    public void setMaxLocalStreams(int maxLocalStreams)
+    {
+        this.maxLocalStreams = maxLocalStreams;
+    }
+
+    public int getMaxRemoteStreams()
+    {
+        return maxRemoteStreams;
+    }
+
+    public void setMaxRemoteStreams(int maxRemoteStreams)
+    {
+        this.maxRemoteStreams = maxRemoteStreams;
+    }
+
+    @ManagedAttribute("The stream's idle timeout")
+    public long getStreamIdleTimeout()
+    {
+        return streamIdleTimeout;
+    }
+
+    public void setStreamIdleTimeout(long streamIdleTimeout)
+    {
+        this.streamIdleTimeout = streamIdleTimeout;
+    }
+
+    @ManagedAttribute("The initial size of session's flow control receive window")
+    public int getInitialSessionRecvWindow()
+    {
+        return initialSessionRecvWindow;
+    }
+
+    public void setInitialSessionRecvWindow(int initialSessionRecvWindow)
+    {
+        this.initialSessionRecvWindow = initialSessionRecvWindow;
+    }
+
+    public EndPoint getEndPoint()
+    {
+        return endPoint;
+    }
+
+    public Generator getGenerator()
+    {
+        return generator;
+    }
+
+    @Override
+    public long getBytesWritten()
+    {
+        return bytesWritten.get();
+    }
+
+    @Override
+    public void onData(final DataFrame frame)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Received {}", frame);
+
+        int streamId = frame.getStreamId();
+        final IStream stream = getStream(streamId);
+
+        // SPEC: the session window must be updated even if the stream is null.
+        // The flow control length includes the padding bytes.
+        final int flowControlLength = frame.remaining() + frame.padding();
+        flowControl.onDataReceived(this, stream, flowControlLength);
+
+        if (stream != null)
+        {
+            if (getRecvWindow() < 0)
+            {
+                close(ErrorCode.FLOW_CONTROL_ERROR.code, "session_window_exceeded", Callback.NOOP);
+            }
+            else
+            {
+                stream.process(frame, new Callback()
+                {
+                    @Override
+                    public void succeeded()
+                    {
+                        complete();
+                    }
+
+                    @Override
+                    public void failed(Throwable x)
+                    {
+                        // Consume also in case of failures, to free the
+                        // session flow control window for other streams.
+                        complete();
+                    }
+
+                    private void complete()
+                    {
+                        notIdle();
+                        stream.notIdle();
+                        flowControl.onDataConsumed(HTTP2Session.this, stream, flowControlLength);
+                    }
+                });
+            }
+        }
+        else
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Ignoring {}, stream #{} not found", frame, streamId);
+            // We must enlarge the session flow control window,
+            // otherwise other requests will be stalled.
+            flowControl.onDataConsumed(this, null, flowControlLength);
+        }
+    }
+
+    @Override
+    public abstract void onHeaders(HeadersFrame frame);
+
+    @Override
+    public void onPriority(PriorityFrame frame)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Received {}", frame);
+    }
+
+    @Override
+    public void onReset(ResetFrame frame)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Received {}", frame);
+
+        IStream stream = getStream(frame.getStreamId());
+        if (stream != null)
+            stream.process(frame, Callback.NOOP);
+        else
+            notifyReset(this, frame);
+    }
+
+    @Override
+    public void onSettings(SettingsFrame frame)
+    {
+        // SPEC: SETTINGS frame MUST be replied.
+        onSettings(frame, true);
+    }
+
+    public void onSettings(SettingsFrame frame, boolean reply)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Received {}", frame);
+
+        if (frame.isReply())
+            return;
+
+        // Iterate over all settings
+        for (Map.Entry<Integer, Integer> entry : frame.getSettings().entrySet())
+        {
+            int key = entry.getKey();
+            int value = entry.getValue();
+            switch (key)
+            {
+                case SettingsFrame.HEADER_TABLE_SIZE:
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Update HPACK header table size to {}", value);
+                    generator.setHeaderTableSize(value);
+                    break;
+                }
+                case SettingsFrame.ENABLE_PUSH:
+                {
+                    // SPEC: check the value is sane.
+                    if (value != 0 && value != 1)
+                    {
+                        onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_enable_push");
+                        return;
+                    }
+                    pushEnabled = value == 1;
+                    break;
+                }
+                case SettingsFrame.MAX_CONCURRENT_STREAMS:
+                {
+                    maxLocalStreams = value;
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Update max local concurrent streams to {}", maxLocalStreams);
+                    break;
+                }
+                case SettingsFrame.INITIAL_WINDOW_SIZE:
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Update initial window size to {}", value);
+                    flowControl.updateInitialStreamWindow(this, value, false);
+                    break;
+                }
+                case SettingsFrame.MAX_FRAME_SIZE:
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Update max frame size to {}", value);
+                    // SPEC: check the max frame size is sane.
+                    if (value < Frame.DEFAULT_MAX_LENGTH || value > Frame.MAX_MAX_LENGTH)
+                    {
+                        onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_max_frame_size");
+                        return;
+                    }
+                    generator.setMaxFrameSize(value);
+                    break;
+                }
+                case SettingsFrame.MAX_HEADER_LIST_SIZE:
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Update max header list size to {}", value);
+                    generator.setMaxHeaderListSize(value);
+                    break;
+                }
+                default:
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Unknown setting {}:{}", key, value);
+                    break;
+                }
+            }
+        }
+        notifySettings(this, frame);
+
+        if (reply)
+        {
+            SettingsFrame replyFrame = new SettingsFrame(Collections.emptyMap(), true);
+            settings(replyFrame, Callback.NOOP);
+        }
+    }
+
+    @Override
+    public void onPing(PingFrame frame)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Received {}", frame);
+
+        if (frame.isReply())
+        {
+            notifyPing(this, frame);
+        }
+        else
+        {
+            PingFrame reply = new PingFrame(frame.getPayload(), true);
+            control(null, Callback.NOOP, reply);
+        }
+    }
+
+    /**
+     * This method is called when receiving a GO_AWAY from the other peer.
+     * We check the close state to act appropriately:
+     *
+     * * NOT_CLOSED: we move to REMOTELY_CLOSED and queue a disconnect, so
+     *   that the content of the queue is written, and then the connection
+     *   closed. We notify the application after being terminated.
+     *   See <code>HTTP2Session.ControlEntry#succeeded()</code>
+     *
+     * * In all other cases, we do nothing since other methods are already
+     *   performing their actions.
+     *
+     * @param frame the GO_AWAY frame that has been received.
+     * @see #close(int, String, Callback)
+     * @see #onShutdown()
+     * @see #onIdleTimeout()
+     */
+    @Override
+    public void onGoAway(final GoAwayFrame frame)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Received {}", frame);
+
+        while (true)
+        {
+            CloseState current = closed.get();
+            switch (current)
+            {
+                case NOT_CLOSED:
+                {
+                    if (closed.compareAndSet(current, CloseState.REMOTELY_CLOSED))
+                    {
+                        // We received a GO_AWAY, so try to write
+                        // what's in the queue and then disconnect.
+                        notifyClose(this, frame);
+                        control(null, Callback.NOOP, new DisconnectFrame());
+                        return;
+                    }
+                    break;
+                }
+                default:
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Ignored {}, already closed", frame);
+                    return;
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onWindowUpdate(WindowUpdateFrame frame)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Received {}", frame);
+
+        int streamId = frame.getStreamId();
+        if (streamId > 0)
+        {
+            IStream stream = getStream(streamId);
+            if (stream != null)
+            {
+                stream.process(frame, Callback.NOOP);
+                onWindowUpdate(stream, frame);
+            }
+        }
+        else
+        {
+            onWindowUpdate(null, frame);
+        }
+    }
+
+    @Override
+    public void onConnectionFailure(int error, String reason)
+    {
+        notifyFailure(this, new IOException(String.format("%d/%s", error, reason)));
+        close(error, reason, Callback.NOOP);
+    }
+
+    @Override
+    public void newStream(HeadersFrame frame, Promise<Stream> promise, Stream.Listener listener)
+    {
+        // Synchronization is necessary to atomically create
+        // the stream id and enqueue the frame to be sent.
+        boolean queued;
+        synchronized (this)
+        {
+            int streamId = frame.getStreamId();
+            if (streamId <= 0)
+            {
+                streamId = streamIds.getAndAdd(2);
+                PriorityFrame priority = frame.getPriority();
+                priority = priority == null ? null : new PriorityFrame(streamId, priority.getParentStreamId(),
+                        priority.getWeight(), priority.isExclusive());
+                frame = new HeadersFrame(streamId, frame.getMetaData(), priority, frame.isEndStream());
+            }
+            final IStream stream = createLocalStream(streamId, promise);
+            if (stream == null)
+                return;
+            stream.setListener(listener);
+
+            ControlEntry entry = new ControlEntry(frame, stream, new PromiseCallback<>(promise, stream));
+            queued = flusher.append(entry);
+        }
+        // Iterate outside the synchronized block.
+        if (queued)
+            flusher.iterate();
+    }
+
+    @Override
+    public int priority(PriorityFrame frame, Callback callback)
+    {
+        int streamId = frame.getStreamId();
+        IStream stream = streams.get(streamId);
+        if (stream == null)
+        {
+            streamId = streamIds.getAndAdd(2);
+            frame = new PriorityFrame(streamId, frame.getParentStreamId(),
+                    frame.getWeight(), frame.isExclusive());
+        }
+        control(stream, callback, frame);
+        return streamId;
+    }
+
+    @Override
+    public void push(IStream stream, Promise<Stream> promise, PushPromiseFrame frame, Stream.Listener listener)
+    {
+        // Synchronization is necessary to atomically create
+        // the stream id and enqueue the frame to be sent.
+        boolean queued;
+        synchronized (this)
+        {
+            int streamId = streamIds.getAndAdd(2);
+            frame = new PushPromiseFrame(frame.getStreamId(), streamId, frame.getMetaData());
+
+            final IStream pushStream = createLocalStream(streamId, promise);
+            if (pushStream == null)
+                return;
+            pushStream.setListener(listener);
+
+            ControlEntry entry = new ControlEntry(frame, pushStream, new PromiseCallback<>(promise, pushStream));
+            queued = flusher.append(entry);
+        }
+        // Iterate outside the synchronized block.
+        if (queued)
+            flusher.iterate();
+    }
+
+
+    @Override
+    public void settings(SettingsFrame frame, Callback callback)
+    {
+        control(null, callback, frame);
+    }
+
+    @Override
+    public void ping(PingFrame frame, Callback callback)
+    {
+        if (frame.isReply())
+            callback.failed(new IllegalArgumentException());
+        else
+            control(null, callback, frame);
+    }
+
+    protected void reset(ResetFrame frame, Callback callback)
+    {
+        control(getStream(frame.getStreamId()), callback, frame);
+    }
+
+    /**
+     * Invoked internally and by applications to send a GO_AWAY frame to the
+     * other peer. We check the close state to act appropriately:
+     *
+     * * NOT_CLOSED: we move to LOCALLY_CLOSED and queue a GO_AWAY. When the
+     *   GO_AWAY has been written, it will only cause the output to be shut
+     *   down (not the connection closed), so that the application can still
+     *   read frames arriving from the other peer.
+     *   Ideally the other peer will notice the GO_AWAY and close the connection.
+     *   When that happen, we close the connection from {@link #onShutdown()}.
+     *   Otherwise, the idle timeout mechanism will close the connection, see
+     *   {@link #onIdleTimeout()}.
+     *
+     * * In all other cases, we do nothing since other methods are already
+     *   performing their actions.
+     *
+     * @param error the error code
+     * @param reason the reason
+     * @param callback the callback to invoke when the operation is complete
+     * @see #onGoAway(GoAwayFrame)
+     * @see #onShutdown()
+     * @see #onIdleTimeout()
+     */
+    @Override
+    public boolean close(int error, String reason, Callback callback)
+    {
+        while (true)
+        {
+            CloseState current = closed.get();
+            switch (current)
+            {
+                case NOT_CLOSED:
+                {
+                    if (closed.compareAndSet(current, CloseState.LOCALLY_CLOSED))
+                    {
+                        byte[] payload = null;
+                        if (reason != null)
+                        {
+                            // Trim the reason to avoid attack vectors.
+                            reason = reason.substring(0, Math.min(reason.length(), 32));
+                            payload = reason.getBytes(StandardCharsets.UTF_8);
+                        }
+                        GoAwayFrame frame = new GoAwayFrame(lastStreamId.get(), error, payload);
+                        control(null, callback, frame);
+                        return true;
+                    }
+                    break;
+                }
+                default:
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Ignoring close {}/{}, already closed", error, reason);
+                    callback.succeeded();
+                    return false;
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean isClosed()
+    {
+        return closed.get() != CloseState.NOT_CLOSED;
+    }
+
+    private void control(IStream stream, Callback callback, Frame frame)
+    {
+        frames(stream, callback, frame, Frame.EMPTY_ARRAY);
+    }
+
+    @Override
+    public void frames(IStream stream, Callback callback, Frame frame, Frame... frames)
+    {
+        // We want to generate as late as possible to allow re-prioritization;
+        // generation will happen while processing the entries.
+
+        // The callback needs to be notified only when the last frame completes.
+
+        int length = frames.length;
+        if (length == 0)
+        {
+            frame(new ControlEntry(frame, stream, callback), true);
+        }
+        else
+        {
+            callback = new CountingCallback(callback, 1 + length);
+            frame(new ControlEntry(frame, stream, callback), false);
+            for (int i = 1; i <= length; ++i)
+                frame(new ControlEntry(frames[i - 1], stream, callback), i == length);
+        }
+    }
+
+    @Override
+    public void data(IStream stream, Callback callback, DataFrame frame)
+    {
+        // We want to generate as late as possible to allow re-prioritization.
+        frame(new DataEntry(frame, stream, callback), true);
+    }
+
+    private void frame(HTTP2Flusher.Entry entry, boolean flush)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} {}", flush ? "Sending" : "Queueing", entry.frame);
+        // Ping frames are prepended to process them as soon as possible.
+        boolean queued = entry.frame.getType() == FrameType.PING ? flusher.prepend(entry) : flusher.append(entry);
+        if (queued && flush)
+        {
+            if (entry.stream != null)
+                entry.stream.notIdle();
+            flusher.iterate();
+        }
+    }
+
+    protected IStream createLocalStream(int streamId, Promise<Stream> promise)
+    {
+        while (true)
+        {
+            int localCount = localStreamCount.get();
+            int maxCount = getMaxLocalStreams();
+            if (maxCount >= 0 && localCount >= maxCount)
+            {
+                promise.failed(new IllegalStateException("Max local stream count " + maxCount + " exceeded"));
+                return null;
+            }
+            if (localStreamCount.compareAndSet(localCount, localCount + 1))
+                break;
+        }
+
+        IStream stream = newStream(streamId, true);
+        if (streams.putIfAbsent(streamId, stream) == null)
+        {
+            stream.setIdleTimeout(getStreamIdleTimeout());
+            flowControl.onStreamCreated(stream);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Created local {}", stream);
+            return stream;
+        }
+        else
+        {
+            promise.failed(new IllegalStateException("Duplicate stream " + streamId));
+            return null;
+        }
+    }
+
+    protected IStream createRemoteStream(int streamId)
+    {
+        // SPEC: exceeding max concurrent streams is treated as stream error.
+        while (true)
+        {
+            int remoteCount = remoteStreamCount.get();
+            int maxCount = getMaxRemoteStreams();
+            if (maxCount >= 0 && remoteCount >= maxCount)
+            {
+                reset(new ResetFrame(streamId, ErrorCode.REFUSED_STREAM_ERROR.code), Callback.NOOP);
+                return null;
+            }
+            if (remoteStreamCount.compareAndSet(remoteCount, remoteCount + 1))
+                break;
+        }
+
+        IStream stream = newStream(streamId, false);
+
+        // SPEC: duplicate stream is treated as connection error.
+        if (streams.putIfAbsent(streamId, stream) == null)
+        {
+            updateLastStreamId(streamId);
+            stream.setIdleTimeout(getStreamIdleTimeout());
+            flowControl.onStreamCreated(stream);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Created remote {}", stream);
+            return stream;
+        }
+        else
+        {
+            close(ErrorCode.PROTOCOL_ERROR.code, "duplicate_stream", Callback.NOOP);
+            return null;
+        }
+    }
+
+    protected IStream newStream(int streamId, boolean local)
+    {
+        return new HTTP2Stream(scheduler, this, streamId, local);
+    }
+
+    @Override
+    public void removeStream(IStream stream)
+    {
+        IStream removed = streams.remove(stream.getId());
+        if (removed != null)
+        {
+            assert removed == stream;
+
+            boolean local = stream.isLocal();
+            if (local)
+                localStreamCount.decrementAndGet();
+            else
+                remoteStreamCount.decrementAndGet();
+
+            onStreamClosed(stream);
+
+            flowControl.onStreamDestroyed(stream);
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("Removed {} {}", local ? "local" : "remote", stream);
+        }
+    }
+
+    @Override
+    public Collection<Stream> getStreams()
+    {
+        List<Stream> result = new ArrayList<>();
+        result.addAll(streams.values());
+        return result;
+    }
+
+    @ManagedAttribute("The number of active streams")
+    public int getStreamCount()
+    {
+        return streams.size();
+    }
+
+    @Override
+    public IStream getStream(int streamId)
+    {
+        return streams.get(streamId);
+    }
+
+    @ManagedAttribute(value = "The flow control send window", readonly = true)
+    public int getSendWindow()
+    {
+        return sendWindow.get();
+    }
+
+    @ManagedAttribute(value = "The flow control receive window", readonly = true)
+    public int getRecvWindow()
+    {
+        return recvWindow.get();
+    }
+
+    @Override
+    public int updateSendWindow(int delta)
+    {
+        return sendWindow.getAndAdd(delta);
+    }
+
+    @Override
+    public int updateRecvWindow(int delta)
+    {
+        return recvWindow.getAndAdd(delta);
+    }
+
+    @Override
+    public void onWindowUpdate(IStream stream, WindowUpdateFrame frame)
+    {
+        // WindowUpdateFrames arrive concurrently with writes.
+        // Increasing (or reducing) the window size concurrently
+        // with writes requires coordination with the flusher, that
+        // decides how many frames to write depending on the available
+        // window sizes. If the window sizes vary concurrently, the
+        // flusher may take non-optimal or wrong decisions.
+        // Here, we "queue" window updates to the flusher, so it will
+        // be the only component responsible for window updates, for
+        // both increments and reductions.
+        flusher.window(stream, frame);
+    }
+
+    @Override
+    @ManagedAttribute(value = "Whether HTTP/2 push is enabled", readonly = true)
+    public boolean isPushEnabled()
+    {
+        return pushEnabled;
+    }
+
+    /**
+     * A typical close by a remote peer involves a GO_AWAY frame followed by TCP FIN.
+     * This method is invoked when the TCP FIN is received, or when an exception is
+     * thrown while reading, and we check the close state to act appropriately:
+     *
+     * * NOT_CLOSED: means that the remote peer did not send a GO_AWAY (abrupt close)
+     *   or there was an exception while reading, and therefore we terminate.
+     *
+     * * LOCALLY_CLOSED: we have sent the GO_AWAY to the remote peer, which received
+     *   it and closed the connection; we queue a disconnect to close the connection
+     *   on the local side.
+     *   The GO_AWAY just shutdown the output, so we need this step to make sure the
+     *   connection is closed. See {@link #close(int, String, Callback)}.
+     *
+     * * REMOTELY_CLOSED: we received the GO_AWAY, and the TCP FIN afterwards, so we
+     *   do nothing since the handling of the GO_AWAY will take care of closing the
+     *   connection. See {@link #onGoAway(GoAwayFrame)}.
+     *
+     * @see #onGoAway(GoAwayFrame)
+     * @see #close(int, String, Callback)
+     * @see #onIdleTimeout()
+     */
+    @Override
+    public void onShutdown()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Shutting down {}", this);
+
+        switch (closed.get())
+        {
+            case NOT_CLOSED:
+            {
+                // The other peer did not send a GO_AWAY, no need to be gentle.
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Abrupt close for {}", this);
+                abort(new ClosedChannelException());
+                break;
+            }
+            case LOCALLY_CLOSED:
+            {
+                // We have closed locally, and only shutdown
+                // the output; now queue a disconnect.
+                control(null, Callback.NOOP, new DisconnectFrame());
+                break;
+            }
+            case REMOTELY_CLOSED:
+            {
+                // Nothing to do, the GO_AWAY frame we
+                // received will close the connection.
+                break;
+            }
+            default:
+            {
+                break;
+            }
+        }
+    }
+
+    /**
+     * This method is invoked when the idle timeout triggers. We check the close state
+     * to act appropriately:
+     *
+     * * NOT_CLOSED: it's a real idle timeout, we just initiate a close, see
+     *   {@link #close(int, String, Callback)}.
+     *
+     * * LOCALLY_CLOSED: we have sent a GO_AWAY and only shutdown the output, but the
+     *   other peer did not close the connection so we never received the TCP FIN, and
+     *   therefore we terminate.
+     *
+     * * REMOTELY_CLOSED: the other peer sent us a GO_AWAY, we should have queued a
+     *   disconnect, but for some reason it was not processed (for example, queue was
+     *   stuck because of TCP congestion), therefore we terminate.
+     *   See {@link #onGoAway(GoAwayFrame)}.
+     *
+     * @return true if the session should be closed, false otherwise
+     * @see #onGoAway(GoAwayFrame)
+     * @see #close(int, String, Callback)
+     * @see #onShutdown()
+     */
+    @Override
+    public boolean onIdleTimeout()
+    {
+        switch (closed.get())
+        {
+            case NOT_CLOSED:
+            {
+                long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - idleTime);
+                if (elapsed < endPoint.getIdleTimeout())
+                    return false;
+                return notifyIdleTimeout(this);
+            }
+            case LOCALLY_CLOSED:
+            case REMOTELY_CLOSED:
+            {
+                abort(new TimeoutException("Idle timeout " + endPoint.getIdleTimeout() + " ms"));
+                return false;
+            }
+            default:
+            {
+                return false;
+            }
+        }
+    }
+
+    private void notIdle()
+    {
+        idleTime = System.nanoTime();
+    }
+
+    @Override
+    public void onFrame(Frame frame)
+    {
+        onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "upgrade");
+    }
+
+    protected void onStreamOpened(IStream stream)
+    {
+    }
+
+    protected void onStreamClosed(IStream stream)
+    {
+    }
+
+    public void disconnect()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Disconnecting {}", this);
+        endPoint.close();
+    }
+
+    private void terminate(Throwable cause)
+    {
+        while (true)
+        {
+            CloseState current = closed.get();
+            switch (current)
+            {
+                case NOT_CLOSED:
+                case LOCALLY_CLOSED:
+                case REMOTELY_CLOSED:
+                {
+                    if (closed.compareAndSet(current, CloseState.CLOSED))
+                    {
+                        flusher.terminate(cause);
+                        for (IStream stream : streams.values())
+                            stream.close();
+                        streams.clear();
+                        disconnect();
+                        return;
+                    }
+                    break;
+                }
+                default:
+                {
+                    return;
+                }
+            }
+        }
+    }
+
+    protected void abort(Throwable failure)
+    {
+        notifyFailure(this, failure);
+        terminate(failure);
+    }
+
+    public boolean isDisconnected()
+    {
+        return !endPoint.isOpen();
+    }
+
+    private void updateLastStreamId(int streamId)
+    {
+        Atomics.updateMax(lastStreamId, streamId);
+    }
+
+    protected Stream.Listener notifyNewStream(Stream stream, HeadersFrame frame)
+    {
+        try
+        {
+            return listener.onNewStream(stream, frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+            return null;
+        }
+    }
+
+    protected void notifySettings(Session session, SettingsFrame frame)
+    {
+        try
+        {
+            listener.onSettings(session, frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected void notifyPing(Session session, PingFrame frame)
+    {
+        try
+        {
+            listener.onPing(session, frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected void notifyReset(Session session, ResetFrame frame)
+    {
+        try
+        {
+            listener.onReset(session, frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected void notifyClose(Session session, GoAwayFrame frame)
+    {
+        try
+        {
+            listener.onClose(session, frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected boolean notifyIdleTimeout(Session session)
+    {
+        try
+        {
+            return listener.onIdleTimeout(session);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+            return true;
+        }
+    }
+
+    protected void notifyFailure(Session session, Throwable failure)
+    {
+        try
+        {
+            listener.onFailure(session, failure);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%x{l:%s <-> r:%s,queueSize=%d,sendWindow=%s,recvWindow=%s,streams=%d,%s}",
+                getClass().getSimpleName(),
+                hashCode(),
+                getEndPoint().getLocalAddress(),
+                getEndPoint().getRemoteAddress(),
+                flusher.getQueueSize(),
+                sendWindow,
+                recvWindow,
+                streams.size(),
+                closed);
+    }
+
+    private class ControlEntry extends HTTP2Flusher.Entry
+    {
+        private int bytes;
+
+        private ControlEntry(Frame frame, IStream stream, Callback callback)
+        {
+            super(frame, stream, callback);
+        }
+
+        public Throwable generate(ByteBufferPool.Lease lease)
+        {
+            try
+            {
+                bytes = generator.control(lease, frame);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Generated {}", frame);
+                prepare();
+                return null;
+            }
+            catch (Throwable x)
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Failure generating frame " + frame, x);
+                return x;
+            }
+        }
+
+        /**
+         * <p>Performs actions just before writing the frame to the network.</p>
+         * <p>Some frame, when sent over the network, causes the receiver
+         * to react and send back frames that may be processed by the original
+         * sender *before* {@link #succeeded()} is called.
+         * <p>If the action to perform updates some state, this update may
+         * not be seen by the received frames and cause errors.</p>
+         * <p>For example, suppose the action updates the stream window to a
+         * larger value; the sender sends the frame; the receiver is now entitled
+         * to send back larger data; when the data is received by the original
+         * sender, the action may have not been performed yet, causing the larger
+         * data to be rejected, when it should have been accepted.</p>
+         */
+        private void prepare()
+        {
+            switch (frame.getType())
+            {
+                case SETTINGS:
+                {
+                    SettingsFrame settingsFrame = (SettingsFrame)frame;
+                    Integer initialWindow = settingsFrame.getSettings().get(SettingsFrame.INITIAL_WINDOW_SIZE);
+                    if (initialWindow != null)
+                        flowControl.updateInitialStreamWindow(HTTP2Session.this, initialWindow, true);
+                    break;
+                }
+                default:
+                {
+                    break;
+                }
+            }
+        }
+
+        @Override
+        public void succeeded()
+        {
+            bytesWritten.addAndGet(bytes);
+            switch (frame.getType())
+            {
+                case HEADERS:
+                {
+                    onStreamOpened(stream);
+                    HeadersFrame headersFrame = (HeadersFrame)frame;
+                    if (stream.updateClose(headersFrame.isEndStream(), true))
+                        removeStream(stream);
+                    break;
+                }
+                case RST_STREAM:
+                {
+                    if (stream != null)
+                    {
+                        stream.close();
+                        removeStream(stream);
+                    }
+                    break;
+                }
+                case PUSH_PROMISE:
+                {
+                    // Pushed streams are implicitly remotely closed.
+                    // They are closed when sending an end-stream DATA frame.
+                    stream.updateClose(true, false);
+                    break;
+                }
+                case GO_AWAY:
+                {
+                    // We just sent a GO_AWAY, only shutdown the
+                    // output without closing yet, to allow reads.
+                    getEndPoint().shutdownOutput();
+                    break;
+                }
+                case WINDOW_UPDATE:
+                {
+                    flowControl.windowUpdate(HTTP2Session.this, stream, (WindowUpdateFrame)frame);
+                    break;
+                }
+                case DISCONNECT:
+                {
+                    terminate(new ClosedChannelException());
+                    break;
+                }
+                default:
+                {
+                    break;
+                }
+            }
+            super.succeeded();
+        }
+    }
+
+    private class DataEntry extends HTTP2Flusher.Entry
+    {
+        private int length;
+        private int bytes;
+
+        private DataEntry(DataFrame frame, IStream stream, Callback callback)
+        {
+            super(frame, stream, callback);
+        }
+
+        @Override
+        public int dataRemaining()
+        {
+            // We don't do any padding, so the flow control length is
+            // always the data remaining. This simplifies the handling
+            // of data frames that cannot be completely written due to
+            // the flow control window exhausting, since in that case
+            // we would have to count the padding only once.
+            return ((DataFrame)frame).remaining();
+        }
+
+        public Throwable generate(ByteBufferPool.Lease lease)
+        {
+            try
+            {
+                int flowControlLength = dataRemaining();
+
+                int sessionSendWindow = getSendWindow();
+                if (sessionSendWindow < 0)
+                    throw new IllegalStateException();
+
+                int streamSendWindow = stream.updateSendWindow(0);
+                if (streamSendWindow < 0)
+                    throw new IllegalStateException();
+
+                int window = Math.min(streamSendWindow, sessionSendWindow);
+
+                int length = this.length = Math.min(flowControlLength, window);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Generated {}, length/window={}/{}", frame, length, window);
+
+                bytes = generator.data(lease, (DataFrame)frame, length);
+                flowControl.onDataSending(stream, length);
+                return null;
+            }
+            catch (Throwable x)
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Failure generating frame " + frame, x);
+                return x;
+            }
+        }
+
+        @Override
+        public void succeeded()
+        {
+            bytesWritten.addAndGet(bytes);
+            flowControl.onDataSent(stream, length);
+            // Do we have more to send ?
+            DataFrame dataFrame = (DataFrame)frame;
+            if (dataFrame.remaining() > 0)
+            {
+                // We have written part of the frame, but there is more to write.
+                // We need to keep the correct ordering of frames, to avoid that other
+                // frames for the same stream are written before this one is finished.
+                flusher.prepend(this);
+            }
+            else
+            {
+                // Only now we can update the close state
+                // and eventually remove the stream.
+                if (stream.updateClose(dataFrame.isEndStream(), true))
+                    removeStream(stream);
+                super.succeeded();
+            }
+        }
+    }
+
+    private static class PromiseCallback<C> implements Callback
+    {
+        private final Promise<C> promise;
+        private final C value;
+
+        private PromiseCallback(Promise<C> promise, C value)
+        {
+            this.promise = promise;
+            this.value = value;
+        }
+
+        @Override
+        public void succeeded()
+        {
+            promise.succeeded(value);
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            promise.failed(x);
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Stream.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Stream.java
new file mode 100644
index 0000000..befd9c0
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Stream.java
@@ -0,0 +1,419 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.io.IdleTimeout;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Scheduler;
+
+public class HTTP2Stream extends IdleTimeout implements IStream
+{
+    private static final Logger LOG = Log.getLogger(HTTP2Stream.class);
+
+    private final AtomicReference<ConcurrentMap<String, Object>> attributes = new AtomicReference<>();
+    private final AtomicReference<CloseState> closeState = new AtomicReference<>(CloseState.NOT_CLOSED);
+    private final AtomicInteger sendWindow = new AtomicInteger();
+    private final AtomicInteger recvWindow = new AtomicInteger();
+    private final ISession session;
+    private final int streamId;
+    private final boolean local;
+    private volatile Listener listener;
+    private volatile boolean localReset;
+    private volatile boolean remoteReset;
+
+    public HTTP2Stream(Scheduler scheduler, ISession session, int streamId, boolean local)
+    {
+        super(scheduler);
+        this.session = session;
+        this.streamId = streamId;
+        this.local = local;
+    }
+
+    @Override
+    public int getId()
+    {
+        return streamId;
+    }
+
+    @Override
+    public boolean isLocal()
+    {
+        return local;
+    }
+
+    @Override
+    public ISession getSession()
+    {
+        return session;
+    }
+
+    @Override
+    public void headers(HeadersFrame frame, Callback callback)
+    {
+        session.frames(this, callback, frame, Frame.EMPTY_ARRAY);
+    }
+
+    @Override
+    public void push(PushPromiseFrame frame, Promise<Stream> promise, Listener listener)
+    {
+        session.push(this, promise, frame, listener);
+    }
+
+    @Override
+    public void data(DataFrame frame, Callback callback)
+    {
+        session.data(this, callback, frame);
+    }
+
+    @Override
+    public void reset(ResetFrame frame, Callback callback)
+    {
+        if (isReset())
+            return;
+        localReset = true;
+        session.frames(this, callback, frame, Frame.EMPTY_ARRAY);
+    }
+
+    @Override
+    public Object getAttribute(String key)
+    {
+        return attributes().get(key);
+    }
+
+    @Override
+    public void setAttribute(String key, Object value)
+    {
+        attributes().put(key, value);
+    }
+
+    @Override
+    public Object removeAttribute(String key)
+    {
+        return attributes().remove(key);
+    }
+
+    @Override
+    public boolean isReset()
+    {
+        return localReset || remoteReset;
+    }
+
+    @Override
+    public boolean isClosed()
+    {
+        return closeState.get() == CloseState.CLOSED;
+    }
+
+    public boolean isRemotelyClosed()
+    {
+        return closeState.get() == CloseState.REMOTELY_CLOSED;
+    }
+
+    public boolean isLocallyClosed()
+    {
+        return closeState.get() == CloseState.LOCALLY_CLOSED;
+    }
+
+    @Override
+    public boolean isOpen()
+    {
+        return !isClosed();
+    }
+
+    @Override
+    protected void onIdleExpired(TimeoutException timeout)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Idle timeout {}ms expired on {}", getIdleTimeout(), this);
+
+        // Notify the application.
+        if (notifyIdleTimeout(this, timeout))
+        {
+            // Tell the other peer that we timed out.
+            reset(new ResetFrame(getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+        }
+    }
+
+    private ConcurrentMap<String, Object> attributes()
+    {
+        ConcurrentMap<String, Object> map = attributes.get();
+        if (map == null)
+        {
+            map = new ConcurrentHashMap<>();
+            if (!attributes.compareAndSet(null, map))
+            {
+                map = attributes.get();
+            }
+        }
+        return map;
+    }
+
+    @Override
+    public Listener getListener()
+    {
+        return listener;
+    }
+
+    @Override
+    public void setListener(Listener listener)
+    {
+        this.listener = listener;
+    }
+
+    @Override
+    public void process(Frame frame, Callback callback)
+    {
+        notIdle();
+        switch (frame.getType())
+        {
+            case HEADERS:
+            {
+                onHeaders((HeadersFrame)frame, callback);
+                break;
+            }
+            case DATA:
+            {
+                onData((DataFrame)frame, callback);
+                break;
+            }
+            case RST_STREAM:
+            {
+                onReset((ResetFrame)frame, callback);
+                break;
+            }
+            case PUSH_PROMISE:
+            {
+                onPush((PushPromiseFrame)frame, callback);
+                break;
+            }
+            case WINDOW_UPDATE:
+            {
+                onWindowUpdate((WindowUpdateFrame)frame, callback);
+                break;
+            }
+            default:
+            {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    private void onHeaders(HeadersFrame frame, Callback callback)
+    {
+        if (updateClose(frame.isEndStream(), false))
+            session.removeStream(this);
+        callback.succeeded();
+    }
+
+    private void onData(DataFrame frame, Callback callback)
+    {
+        if (getRecvWindow() < 0)
+        {
+            // It's a bad client, it does not deserve to be
+            // treated gently by just resetting the stream.
+            session.close(ErrorCode.FLOW_CONTROL_ERROR.code, "stream_window_exceeded", Callback.NOOP);
+            callback.failed(new IOException("stream_window_exceeded"));
+            return;
+        }
+
+        // SPEC: remotely closed streams must be replied with a reset.
+        if (isRemotelyClosed())
+        {
+            reset(new ResetFrame(streamId, ErrorCode.STREAM_CLOSED_ERROR.code), Callback.NOOP);
+            callback.failed(new EOFException("stream_closed"));
+            return;
+        }
+
+        if (isReset())
+        {
+            // Just drop the frame.
+            callback.failed(new IOException("stream_reset"));
+            return;
+        }
+
+        if (updateClose(frame.isEndStream(), false))
+            session.removeStream(this);
+        notifyData(this, frame, callback);
+    }
+
+    private void onReset(ResetFrame frame, Callback callback)
+    {
+        remoteReset = true;
+        close();
+        session.removeStream(this);
+        callback.succeeded();
+        notifyReset(this, frame);
+    }
+
+    private void onPush(PushPromiseFrame frame, Callback callback)
+    {
+        // Pushed streams are implicitly locally closed.
+        // They are closed when receiving an end-stream DATA frame.
+        updateClose(true, true);
+        callback.succeeded();
+    }
+
+    private void onWindowUpdate(WindowUpdateFrame frame, Callback callback)
+    {
+        callback.succeeded();
+    }
+
+    @Override
+    public boolean updateClose(boolean update, boolean local)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Update close for {} close={} local={}", this, update, local);
+
+        if (!update)
+            return false;
+
+        while (true)
+        {
+            CloseState current = closeState.get();
+            switch (current)
+            {
+                case NOT_CLOSED:
+                {
+                    CloseState newValue = local ? CloseState.LOCALLY_CLOSED : CloseState.REMOTELY_CLOSED;
+                    if (closeState.compareAndSet(current, newValue))
+                        return false;
+                    break;
+                }
+                case LOCALLY_CLOSED:
+                {
+                    if (local)
+                        return false;
+                    close();
+                    return true;
+                }
+                case REMOTELY_CLOSED:
+                {
+                    if (!local)
+                        return false;
+                    close();
+                    return true;
+                }
+                default:
+                {
+                    return false;
+                }
+            }
+        }
+    }
+
+    public int getSendWindow()
+    {
+        return sendWindow.get();
+    }
+
+    public int getRecvWindow()
+    {
+        return recvWindow.get();
+    }
+
+    @Override
+    public int updateSendWindow(int delta)
+    {
+        return sendWindow.getAndAdd(delta);
+    }
+
+    @Override
+    public int updateRecvWindow(int delta)
+    {
+        return recvWindow.getAndAdd(delta);
+    }
+
+    @Override
+    public void close()
+    {
+        closeState.set(CloseState.CLOSED);
+        onClose();
+    }
+
+    private void notifyData(Stream stream, DataFrame frame, Callback callback)
+    {
+        final Listener listener = this.listener;
+        if (listener == null)
+            return;
+        try
+        {
+            listener.onData(stream, frame, callback);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    private void notifyReset(Stream stream, ResetFrame frame)
+    {
+        final Listener listener = this.listener;
+        if (listener == null)
+            return;
+        try
+        {
+            listener.onReset(stream, frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    private boolean notifyIdleTimeout(Stream stream, Throwable failure)
+    {
+        Listener listener = this.listener;
+        if (listener == null)
+            return true;
+        try
+        {
+            return listener.onIdleTimeout(stream, failure);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+            return true;
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%x#%d{sendWindow=%s,recvWindow=%s,reset=%b,%s}", getClass().getSimpleName(),
+                hashCode(), getId(), sendWindow, recvWindow, isReset(), closeState);
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java
new file mode 100644
index 0000000..eccba96
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java
@@ -0,0 +1,135 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+
+/**
+ * <p>The SPI interface for implementing a HTTP/2 session.</p>
+ * <p>This class extends {@link Session} by adding the methods required to
+ * implement the HTTP/2 session functionalities.</p>
+ */
+public interface ISession extends Session
+{
+    @Override
+    public IStream getStream(int streamId);
+
+    /**
+     * <p>Removes the given {@code stream}.</p>
+     *
+     * @param stream the stream to remove
+     */
+    public void removeStream(IStream stream);
+
+    /**
+     * <p>Enqueues the given frames to be written to the connection.</p>
+     *
+     * @param stream   the stream the frames belong to
+     * @param callback the callback that gets notified when the frames have been sent
+     * @param frame    the first frame to enqueue
+     * @param frames   additional frames to enqueue
+     */
+    public void frames(IStream stream, Callback callback, Frame frame, Frame... frames);
+
+    /**
+     * <p>Enqueues the given PUSH_PROMISE frame to be written to the connection.</p>
+     * <p>Differently from {@link #frames(IStream, Callback, Frame, Frame...)}, this method
+     * generates atomically the stream id for the pushed stream.</p>
+     *
+     * @param stream   the stream associated to the pushed stream
+     * @param promise  the promise that gets notified of the pushed stream creation
+     * @param frame    the PUSH_PROMISE frame to enqueue
+     * @param listener the listener that gets notified of pushed stream events
+     */
+    public void push(IStream stream, Promise<Stream> promise, PushPromiseFrame frame, Stream.Listener listener);
+
+    /**
+     * <p>Enqueues the given DATA frame to be written to the connection.</p>
+     *
+     * @param stream   the stream the data frame belongs to
+     * @param callback the callback that gets notified when the frame has been sent
+     * @param frame    the DATA frame to send
+     */
+    public void data(IStream stream, Callback callback, DataFrame frame);
+
+    /**
+     * <p>Updates the session send window by the given {@code delta}.</p>
+     *
+     * @param delta the delta value (positive or negative) to add to the session send window
+     * @return the previous value of the session send window
+     */
+    public int updateSendWindow(int delta);
+
+    /**
+     * <p>Updates the session receive window by the given {@code delta}.</p>
+     *
+     * @param delta the delta value (positive or negative) to add to the session receive window
+     * @return the previous value of the session receive window
+     */
+    public int updateRecvWindow(int delta);
+
+    /**
+     * <p>Callback method invoked when a WINDOW_UPDATE frame has been received.</p>
+     *
+     * @param stream the stream the window update belongs to, or null if the window update belongs to the session
+     * @param frame  the WINDOW_UPDATE frame received
+     */
+    public void onWindowUpdate(IStream stream, WindowUpdateFrame frame);
+
+    /**
+     * @return whether the push functionality is enabled
+     */
+    public boolean isPushEnabled();
+
+    /**
+     * <p>Callback invoked when the connection reads -1.</p>
+     *
+     * @see #onIdleTimeout()
+     * @see #close(int, String, Callback)
+     */
+    public void onShutdown();
+
+    /**
+     * <p>Callback invoked when the idle timeout expires.</p>
+     *
+     * @see #onShutdown()
+     * @see #close(int, String, Callback)
+     */
+    public boolean onIdleTimeout();
+
+    /**
+     * <p>Callback method invoked during an HTTP/1.1 to HTTP/2 upgrade requests
+     * to process the given synthetic frame.</p>
+     *
+     * @param frame the synthetic frame to process
+     */
+    public void onFrame(Frame frame);
+
+    /**
+     * @return the number of bytes written by this session
+     */
+    public long getBytesWritten();
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/IStream.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/IStream.java
new file mode 100644
index 0000000..6137dfe
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/IStream.java
@@ -0,0 +1,108 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+import java.io.Closeable;
+
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.util.Callback;
+
+/**
+ * <p>The SPI interface for implementing a HTTP/2 stream.</p>
+ * <p>This class extends {@link Stream} by adding the methods required to
+ * implement the HTTP/2 stream functionalities.</p>
+ */
+public interface IStream extends Stream, Closeable
+{
+    /**
+     * <p>The constant used as attribute key to store/retrieve the HTTP
+     * channel associated with this stream</p>
+     *
+     * @see #setAttribute(String, Object)
+     */
+    public static final String CHANNEL_ATTRIBUTE = IStream.class.getName() + ".channel";
+
+    /**
+     * @return whether this stream is local or remote
+     */
+    public boolean isLocal();
+
+    @Override
+    public ISession getSession();
+
+    /**
+     * @return the {@link org.eclipse.jetty.http2.api.Stream.Listener} associated with this stream
+     * @see #setListener(Listener)
+     */
+    public Listener getListener();
+
+    /**
+     * @param listener the {@link org.eclipse.jetty.http2.api.Stream.Listener} associated with this stream
+     * @see #getListener()
+     */
+    public void setListener(Listener listener);
+
+    /**
+     * <p>Processes the given {@code frame}, belonging to this stream.</p>
+     *
+     * @param frame the frame to process
+     * @param callback the callback to complete when frame has been processed
+     */
+    public void process(Frame frame, Callback callback);
+
+    /**
+     * <p>Updates the close state of this stream.</p>
+     *
+     * @param update whether to update the close state
+     * @param local  whether the update comes from a local operation
+     *               (such as sending a frame that ends the stream)
+     *               or a remote operation (such as receiving a frame
+     * @return whether the stream has been fully closed by this invocation
+     */
+    public boolean updateClose(boolean update, boolean local);
+
+    /**
+     * <p>Forcibly closes this stream.</p>
+     */
+    @Override
+    public void close();
+
+    /**
+     * <p>Updates the stream send window by the given {@code delta}.</p>
+     *
+     * @param delta the delta value (positive or negative) to add to the stream send window
+     * @return the previous value of the stream send window
+     */
+    public int updateSendWindow(int delta);
+
+    /**
+     * <p>Updates the stream receive window by the given {@code delta}.</p>
+     *
+     * @param delta the delta value (positive or negative) to add to the stream receive window
+     * @return the previous value of the stream receive window
+     */
+    public int updateRecvWindow(int delta);
+
+    /**
+     * <p>Marks this stream as not idle so that the
+     * {@link #getIdleTimeout() idle timeout} is postponed.</p>
+     */
+    public void notIdle();
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/SimpleFlowControlStrategy.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/SimpleFlowControlStrategy.java
new file mode 100644
index 0000000..92c0def
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/SimpleFlowControlStrategy.java
@@ -0,0 +1,72 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2;
+
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.util.Callback;
+
+public class SimpleFlowControlStrategy extends AbstractFlowControlStrategy
+{
+    public SimpleFlowControlStrategy()
+    {
+        this(DEFAULT_WINDOW_SIZE);
+    }
+
+    public SimpleFlowControlStrategy(int initialStreamSendWindow)
+    {
+        super(initialStreamSendWindow);
+    }
+
+    @Override
+    public void onDataConsumed(ISession session, IStream stream, int length)
+    {
+        if (length <= 0)
+            return;
+
+        // This is the simple algorithm for flow control.
+        // This method is called when a whole flow controlled frame has been consumed.
+        // We send a WindowUpdate every time, even if the frame was very small.
+
+        WindowUpdateFrame sessionFrame = new WindowUpdateFrame(0, length);
+        session.updateRecvWindow(length);
+        if (LOG.isDebugEnabled())
+            LOG.debug("Data consumed, increased session recv window by {} for {}", length, session);
+
+        Frame[] streamFrame = Frame.EMPTY_ARRAY;
+        if (stream != null)
+        {
+            if (stream.isClosed())
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Data consumed, ignoring update stream recv window by {} for closed {}", length, stream);
+            }
+            else
+            {
+                streamFrame = new Frame[1];
+                streamFrame[0] = new WindowUpdateFrame(stream.getId(), length);
+                stream.updateRecvWindow(length);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Data consumed, increased stream recv window by {} for {}", length, stream);
+            }
+        }
+
+        session.frames(stream, Callback.NOOP, sessionFrame, streamFrame);
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java
new file mode 100644
index 0000000..e2c4dbb
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java
@@ -0,0 +1,267 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.api;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PingFrame;
+import org.eclipse.jetty.http2.frames.PriorityFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+
+/**
+ * <p>A {@link Session} represents the client-side endpoint of a HTTP/2 connection to a single origin server.</p>
+ * <p>Once a {@link Session} has been obtained, it can be used to open HTTP/2 streams:</p>
+ * <pre>
+ * Session session = ...;
+ * HeadersFrame frame = ...;
+ * Promise&lt;Stream&gt; promise = ...
+ * session.newStream(frame, promise, new Stream.Listener.Adapter()
+ * {
+ *     public void onHeaders(Stream stream, HeadersFrame frame)
+ *     {
+ *         // Reply received
+ *     }
+ * });
+ * </pre>
+ * <p>A {@link Session} is the active part of the endpoint, and by calling its API applications can generate
+ * events on the connection; conversely {@link Session.Listener} is the passive part of the endpoint, and
+ * has callbacks that are invoked when events happen on the connection.</p>
+ *
+ * @see Session.Listener
+ */
+public interface Session
+{
+    /**
+     * <p>Sends the given HEADERS {@code frame} to create a new {@link Stream}.</p>
+     *
+     * @param frame    the HEADERS frame containing the HTTP headers
+     * @param promise  the promise that gets notified of the stream creation
+     * @param listener the listener that gets notified of stream events
+     */
+    public void newStream(HeadersFrame frame, Promise<Stream> promise, Stream.Listener listener);
+
+    /**
+     * <p>Sends the given PRIORITY {@code frame}.</p>
+     * <p>If the {@code frame} references a {@code streamId} that does not exist
+     * (for example {@code 0}), then a new {@code streamId} will be allocated, to
+     * support <em>unused anchor streams</em> that act as parent for other streams.</p>
+     *
+     * @param frame    the PRIORITY frame to send
+     * @param callback the callback that gets notified when the frame has been sent
+     * @return         the new stream id generated by the PRIORITY frame, or the stream id
+     *                 that it is already referencing
+     */
+    public int priority(PriorityFrame frame, Callback callback);
+
+    /**
+     * <p>Sends the given SETTINGS {@code frame} to configure the session.</p>
+     *
+     * @param frame    the SETTINGS frame to send
+     * @param callback the callback that gets notified when the frame has been sent
+     */
+    public void settings(SettingsFrame frame, Callback callback);
+
+    /**
+     * <p>Sends the given PING {@code frame}.</p>
+     * <p>PING frames may be used to test the connection integrity and to measure
+     * round-trip time.</p>
+     *
+     * @param frame    the PING frame to send
+     * @param callback the callback that gets notified when the frame has been sent
+     */
+    public void ping(PingFrame frame, Callback callback);
+
+    /**
+     * <p>Closes the session by sending a GOAWAY frame with the given error code
+     * and payload.</p>
+     * <p>The GOAWAY frame is sent only once; subsequent or concurrent attempts to
+     * close the session will have no effect.</p>
+     *
+     * @param error    the error code
+     * @param payload  an optional payload (may be null)
+     * @param callback the callback that gets notified when the frame has been sent
+     * @return true if the frame is being sent, false if the session was already closed
+     */
+    public boolean close(int error, String payload, Callback callback);
+
+    /**
+     * @return whether the session is not open
+     */
+    public boolean isClosed();
+
+    /**
+     * @return a snapshot of all the streams currently belonging to this session
+     */
+    public Collection<Stream> getStreams();
+
+    /**
+     * <p>Retrieves the stream with the given {@code streamId}.</p>
+     *
+     * @param streamId the stream id of the stream looked for
+     * @return the stream with the given id, or null if no such stream exist
+     */
+    public Stream getStream(int streamId);
+
+    /**
+     * <p>A {@link Listener} is the passive counterpart of a {@link Session} and
+     * receives events happening on a HTTP/2 connection.</p>
+     *
+     * @see Session
+     */
+    public interface Listener
+    {
+        /**
+         * <p>Callback method invoked:</p>
+         * <ul>
+         *     <li>for clients, just before the preface is sent, to gather the
+         *     SETTINGS configuration options the client wants to send to the server;</li>
+         *     <li>for servers, just after having received the preface, to gather
+         *     the SETTINGS configuration options the server wants to send to the
+         *     client.</li>
+         * </ul>
+         *
+         * @param session the session
+         * @return a (possibly empty or null) map containing SETTINGS configuration
+         * options to send.
+         */
+        public Map<Integer, Integer> onPreface(Session session);
+
+        /**
+         * <p>Callback method invoked when a new stream is being created upon
+         * receiving a HEADERS frame representing a HTTP request.</p>
+         * <p>Applications should implement this method to process HTTP requests,
+         * typically providing a HTTP response via
+         * {@link Stream#headers(HeadersFrame, Callback)}.</p>
+         * <p>Applications can detect whether request DATA frames will be arriving
+         * by testing {@link HeadersFrame#isEndStream()}. If the application is
+         * interested in processing the DATA frames, it must return a
+         * {@link Stream.Listener} implementation that overrides
+         * {@link Stream.Listener#onData(Stream, DataFrame, Callback)}.</p>
+         *
+         * @param stream the newly created stream
+         * @param frame  the HEADERS frame received
+         * @return a {@link Stream.Listener} that will be notified of stream events
+         */
+        public Stream.Listener onNewStream(Stream stream, HeadersFrame frame);
+
+        /**
+         * <p>Callback method invoked when a SETTINGS frame has been received.</p>
+         *
+         * @param session the session
+         * @param frame   the SETTINGS frame received
+         */
+        public void onSettings(Session session, SettingsFrame frame);
+
+        /**
+         * <p>Callback method invoked when a PING frame has been received.</p>
+         *
+         * @param session the session
+         * @param frame   the PING frame received
+         */
+        public void onPing(Session session, PingFrame frame);
+
+        /**
+         * <p>Callback method invoked when a RST_STREAM frame has been received for an unknown stream.</p>
+         *
+         * @param session the session
+         * @param frame   the RST_STREAM frame received
+         * @see Stream.Listener#onReset(Stream, ResetFrame)
+         */
+        public void onReset(Session session, ResetFrame frame);
+
+        /**
+         * <p>Callback method invoked when a GOAWAY frame has been received.</p>
+         *
+         * @param session the session
+         * @param frame   the GOAWAY frame received
+         */
+        public void onClose(Session session, GoAwayFrame frame);
+
+        /**
+         * <p>Callback method invoked when the idle timeout expired.</p>
+         * @param session the session
+         * @return whether the session should be closed
+         */
+        public boolean onIdleTimeout(Session session);
+
+        /**
+         * <p>Callback method invoked when a failure has been detected for this session.</p>
+         *
+         * @param session the session
+         * @param failure the failure
+         */
+        public void onFailure(Session session, Throwable failure);
+
+        /**
+         * <p>Empty implementation of {@link Stream.Listener}.</p>
+         */
+        public static class Adapter implements Session.Listener
+        {
+            @Override
+            public Map<Integer, Integer> onPreface(Session session)
+            {
+                return null;
+            }
+
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                return null;
+            }
+
+            @Override
+            public void onSettings(Session session, SettingsFrame frame)
+            {
+            }
+
+            @Override
+            public void onPing(Session session, PingFrame frame)
+            {
+            }
+
+            @Override
+            public void onReset(Session session, ResetFrame frame)
+            {
+            }
+
+            @Override
+            public void onClose(Session session, GoAwayFrame frame)
+            {
+            }
+
+            @Override
+            public boolean onIdleTimeout(Session session)
+            {
+                return true;
+            }
+
+            @Override
+            public void onFailure(Session session, Throwable failure)
+            {
+            }
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java
new file mode 100644
index 0000000..85da193
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java
@@ -0,0 +1,242 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.api;
+
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+
+/**
+ * <p>A {@link Stream} represents a bidirectional exchange of data on top of a {@link Session}.</p>
+ * <p>Differently from socket streams, where the input and output streams are permanently associated
+ * with the socket (and hence with the connection that the socket represents), there can be multiple
+ * HTTP/2 streams present concurrent for a HTTP/2 session.</p>
+ * <p>A {@link Stream} maps to a HTTP request/response cycle, and after the request/response cycle is
+ * completed, the stream is closed and removed from the session.</p>
+ * <p>Like {@link Session}, {@link Stream} is the active part and by calling its API applications
+ * can generate events on the stream; conversely, {@link Stream.Listener} is the passive part, and
+ * its callbacks are invoked when events happen on the stream.</p>
+ *
+ * @see Stream.Listener
+ */
+public interface Stream
+{
+    /**
+     * @return the stream unique id
+     */
+    public int getId();
+
+    /**
+     * @return the session this stream is associated to
+     */
+    public Session getSession();
+
+    /**
+     * <p>Sends the given HEADERS {@code frame} representing a HTTP response.</p>
+     *
+     * @param frame    the HEADERS frame to send
+     * @param callback the callback that gets notified when the frame has been sent
+     */
+    public void headers(HeadersFrame frame, Callback callback);
+
+    /**
+     * <p>Sends the given PUSH_PROMISE {@code frame}.</p>
+     *
+     * @param frame   the PUSH_PROMISE frame to send
+     * @param promise the promise that gets notified of the pushed stream creation
+     * @param listener the listener that gets notified of stream events
+     */
+    public void push(PushPromiseFrame frame, Promise<Stream> promise, Listener listener);
+
+    /**
+     * <p>Sends the given DATA {@code frame}.</p>
+     *
+     * @param frame    the DATA frame to send
+     * @param callback the callback that gets notified when the frame has been sent
+     */
+    public void data(DataFrame frame, Callback callback);
+
+    /**
+     * <p>Sends the given RST_STREAM {@code frame}.</p>
+     *
+     * @param frame    the RST_FRAME to send
+     * @param callback the callback that gets notified when the frame has been sent
+     */
+    public void reset(ResetFrame frame, Callback callback);
+
+    /**
+     * @param key the attribute key
+     * @return an arbitrary object associated with the given key to this stream
+     * or null if no object can be found for the given key.
+     * @see #setAttribute(String, Object)
+     */
+    public Object getAttribute(String key);
+
+    /**
+     * @param key   the attribute key
+     * @param value an arbitrary object to associate with the given key to this stream
+     * @see #getAttribute(String)
+     * @see #removeAttribute(String)
+     */
+    public void setAttribute(String key, Object value);
+
+    /**
+     * @param key the attribute key
+     * @return the arbitrary object associated with the given key to this stream
+     * @see #setAttribute(String, Object)
+     */
+    public Object removeAttribute(String key);
+
+    /**
+     * @return whether this stream has been reset
+     */
+    public boolean isReset();
+
+    /**
+     * @return whether this stream is closed, both locally and remotely.
+     */
+    public boolean isClosed();
+
+    /**
+     * @return the stream idle timeout
+     * @see #setIdleTimeout(long)
+     */
+    public long getIdleTimeout();
+
+    /**
+     * @param idleTimeout the stream idle timeout
+     * @see #getIdleTimeout()
+     * @see Stream.Listener#onTimeout(Stream, Throwable)
+     */
+    public void setIdleTimeout(long idleTimeout);
+
+    /**
+     * <p>A {@link Stream.Listener} is the passive counterpart of a {@link Stream} and receives
+     * events happening on a HTTP/2 stream.</p>
+     *
+     * @see Stream
+     */
+    public interface Listener
+    {
+        /**
+         * <p>Callback method invoked when a HEADERS frame representing the HTTP response has been received.</p>
+         *
+         * @param stream the stream
+         * @param frame  the HEADERS frame received
+         */
+        public void onHeaders(Stream stream, HeadersFrame frame);
+
+        /**
+         * <p>Callback method invoked when a PUSH_PROMISE frame has been received.</p>
+         *
+         * @param stream the stream
+         * @param frame  the PUSH_PROMISE frame received
+         * @return a {@link Stream.Listener} that will be notified of pushed stream events
+         */
+        public Listener onPush(Stream stream, PushPromiseFrame frame);
+
+        /**
+         * <p>Callback method invoked when a DATA frame has been received.</p>
+         *
+         * @param stream   the stream
+         * @param frame    the DATA frame received
+         * @param callback the callback to complete when the bytes of the DATA frame have been consumed
+         */
+        public void onData(Stream stream, DataFrame frame, Callback callback);
+
+        /**
+         * <p>Callback method invoked when a RST_STREAM frame has been received for this stream.</p>
+         *
+         * @param stream the stream
+         * @param frame  the RST_FRAME received
+         * @see Session.Listener#onReset(Session, ResetFrame)
+         */
+        public void onReset(Stream stream, ResetFrame frame);
+
+        /**
+         * <p>Callback method invoked when the stream exceeds its idle timeout.</p>
+         *
+         * @param stream the stream
+         * @param x      the timeout failure
+         * @see #getIdleTimeout()
+         * @deprecated use {@link #onIdleTimeout(Stream, Throwable)} instead
+         */
+        @Deprecated
+        public default void onTimeout(Stream stream, Throwable x)
+        {
+        }
+
+        /**
+         * <p>Callback method invoked when the stream exceeds its idle timeout.</p>
+         *
+         * @param stream the stream
+         * @param x      the timeout failure
+         * @see #getIdleTimeout()
+         * @return true to reset the stream, false to ignore the idle timeout
+         */
+        public default boolean onIdleTimeout(Stream stream, Throwable x)
+        {
+            onTimeout(stream, x);
+            return true;
+        }
+
+        /**
+         * <p>Empty implementation of {@link Listener}</p>
+         */
+        public static class Adapter implements Listener
+        {
+            @Override
+            public void onHeaders(Stream stream, HeadersFrame frame)
+            {
+            }
+
+            @Override
+            public Listener onPush(Stream stream, PushPromiseFrame frame)
+            {
+                return null;
+            }
+
+            @Override
+            public void onData(Stream stream, DataFrame frame, Callback callback)
+            {
+                callback.succeeded();
+            }
+
+            @Override
+            public void onReset(Stream stream, ResetFrame frame)
+            {
+            }
+
+            @Override
+            public void onTimeout(Stream stream, Throwable x)
+            {
+            }
+
+            @Override
+            public boolean onIdleTimeout(Stream stream, Throwable x)
+            {
+                onTimeout(stream, x);
+                return true;
+            }
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/server/ServerSessionListener.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/server/ServerSessionListener.java
new file mode 100644
index 0000000..ffb07b6
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/server/ServerSessionListener.java
@@ -0,0 +1,44 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.api.server;
+
+import org.eclipse.jetty.http2.api.Session;
+
+/**
+ * <p>Server-side extension of {@link org.eclipse.jetty.http2.api.Session.Listener} that exposes additional events.</p>
+ */
+public interface ServerSessionListener extends Session.Listener
+{
+    /**
+     * <p>Callback method invoked when a connection has been accepted by the server.</p>
+     * @param session the session
+     */
+    public void onAccept(Session session);
+
+    /**
+     * <p>Empty implementation of {@link ServerSessionListener}</p>
+     */
+    public static class Adapter extends Session.Listener.Adapter implements ServerSessionListener
+    {
+        @Override
+        public void onAccept(Session session)
+        {
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/DataFrame.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/DataFrame.java
new file mode 100644
index 0000000..1a80fc0
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/DataFrame.java
@@ -0,0 +1,80 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.ByteBuffer;
+
+public class DataFrame extends Frame
+{
+    private final int streamId;
+    private final ByteBuffer data;
+    private final boolean endStream;
+    private final int padding;
+
+    public DataFrame(int streamId, ByteBuffer data, boolean endStream)
+    {
+        this(streamId, data, endStream, 0);
+    }
+
+    public DataFrame(int streamId, ByteBuffer data, boolean endStream, int padding)
+    {
+        super(FrameType.DATA);
+        this.streamId = streamId;
+        this.data = data;
+        this.endStream = endStream;
+        this.padding = padding;
+    }
+
+    public int getStreamId()
+    {
+        return streamId;
+    }
+
+    public ByteBuffer getData()
+    {
+        return data;
+    }
+
+    public boolean isEndStream()
+    {
+        return endStream;
+    }
+
+    /**
+     * @return the number of data bytes remaining.
+     */
+    public int remaining()
+    {
+        return data.remaining();
+    }
+
+    /**
+     * @return the number of bytes used for padding that count towards flow control.
+     */
+    public int padding()
+    {
+        return padding;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s#%d{length:%d,end=%b}", super.toString(), streamId, data.remaining(), endStream);
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/DisconnectFrame.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/DisconnectFrame.java
new file mode 100644
index 0000000..bd07eeb
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/DisconnectFrame.java
@@ -0,0 +1,27 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+public class DisconnectFrame extends Frame
+{
+    public DisconnectFrame()
+    {
+        super(FrameType.DISCONNECT);
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/Frame.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/Frame.java
new file mode 100644
index 0000000..2e7c671
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/Frame.java
@@ -0,0 +1,45 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+public abstract class Frame
+{
+    public static final int HEADER_LENGTH = 9;
+    public static final int DEFAULT_MAX_LENGTH = 0x40_00;
+    public static final int MAX_MAX_LENGTH = 0xFF_FF_FF;
+    public static final Frame[] EMPTY_ARRAY = new Frame[0];
+
+    private final FrameType type;
+
+    protected Frame(FrameType type)
+    {
+        this.type = type;
+    }
+
+    public FrameType getType()
+    {
+        return type;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%x", getClass().getSimpleName(), hashCode());
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/FrameType.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/FrameType.java
new file mode 100644
index 0000000..beb99c3
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/FrameType.java
@@ -0,0 +1,62 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum FrameType
+{
+    DATA(0),
+    HEADERS(1),
+    PRIORITY(2),
+    RST_STREAM(3),
+    SETTINGS(4),
+    PUSH_PROMISE(5),
+    PING(6),
+    GO_AWAY(7),
+    WINDOW_UPDATE(8),
+    CONTINUATION(9),
+    // Synthetic frames only needed by the implementation.
+    PREFACE(10),
+    DISCONNECT(11);
+
+    public static FrameType from(int type)
+    {
+        return Types.types.get(type);
+    }
+
+    private final int type;
+
+    private FrameType(int type)
+    {
+        this.type = type;
+        Types.types.put(type, this);
+    }
+
+    public int getType()
+    {
+        return type;
+    }
+
+    private static class Types
+    {
+        private static final Map<Integer, FrameType> types = new HashMap<>();
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/GoAwayFrame.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/GoAwayFrame.java
new file mode 100644
index 0000000..d57a407
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/GoAwayFrame.java
@@ -0,0 +1,78 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.jetty.http2.ErrorCode;
+
+public class GoAwayFrame extends Frame
+{
+    private final int lastStreamId;
+    private final int error;
+    private final byte[] payload;
+
+    public GoAwayFrame(int lastStreamId, int error, byte[] payload)
+    {
+        super(FrameType.GO_AWAY);
+        this.lastStreamId = lastStreamId;
+        this.error = error;
+        this.payload = payload;
+    }
+
+    public int getLastStreamId()
+    {
+        return lastStreamId;
+    }
+
+    public int getError()
+    {
+        return error;
+    }
+
+    public byte[] getPayload()
+    {
+        return payload;
+    }
+
+    public String tryConvertPayload()
+    {
+        if (payload == null || payload.length == 0)
+            return "";
+        try
+        {
+            return new String(payload, StandardCharsets.UTF_8);
+        }
+        catch (Throwable x)
+        {
+            return "";
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        ErrorCode errorCode = ErrorCode.from(error);
+        return String.format("%s,%d/%s/%s",
+                super.toString(),
+                lastStreamId,
+                errorCode != null ? errorCode.toString() : String.valueOf(error),
+                tryConvertPayload());
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/HeadersFrame.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/HeadersFrame.java
new file mode 100644
index 0000000..df59eea
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/HeadersFrame.java
@@ -0,0 +1,88 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import org.eclipse.jetty.http.MetaData;
+
+public class HeadersFrame extends Frame
+{
+    private final int streamId;
+    private final MetaData metaData;
+    private final PriorityFrame priority;
+    private final boolean endStream;
+
+    /**
+     * <p>Creates a new {@code HEADERS} frame with an unspecified stream {@code id}.</p>
+     * <p>The stream {@code id} will be generated by the implementation while sending
+     * this frame to the other peer.</p>
+     *
+     * @param metaData  the metadata containing HTTP request information
+     * @param priority  the PRIORITY frame associated with this HEADERS frame
+     * @param endStream whether this frame ends the stream
+     */
+    public HeadersFrame(MetaData metaData, PriorityFrame priority, boolean endStream)
+    {
+        this(0, metaData, priority, endStream);
+    }
+
+    /**
+     * <p>Creates a new {@code HEADERS} frame with the specified stream {@code id}.</p>
+     * <p>{@code HEADERS} frames with a specific stream {@code id} are typically used
+     * in responses to request {@code HEADERS} frames.</p>
+     *
+     * @param streamId  the stream id
+     * @param metaData  the metadata containing HTTP request/response information
+     * @param priority  the PRIORITY frame associated with this HEADERS frame
+     * @param endStream whether this frame ends the stream
+     */
+    public HeadersFrame(int streamId, MetaData metaData, PriorityFrame priority, boolean endStream)
+    {
+        super(FrameType.HEADERS);
+        this.streamId = streamId;
+        this.metaData = metaData;
+        this.priority = priority;
+        this.endStream = endStream;
+    }
+
+    public int getStreamId()
+    {
+        return streamId;
+    }
+
+    public MetaData getMetaData()
+    {
+        return metaData;
+    }
+
+    public PriorityFrame getPriority()
+    {
+        return priority;
+    }
+
+    public boolean isEndStream()
+    {
+        return endStream;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s#%d{end=%b}", super.toString(), streamId, endStream);
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PingFrame.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PingFrame.java
new file mode 100644
index 0000000..c7dfcfd
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PingFrame.java
@@ -0,0 +1,103 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.util.Objects;
+
+public class PingFrame extends Frame
+{
+    public static final int PING_LENGTH = 8;
+    private static final byte[] EMPTY_PAYLOAD = new byte[8];
+
+    private final byte[] payload;
+    private final boolean reply;
+
+    /**
+     * Creates a PING frame with an empty payload.
+     *
+     * @param reply whether this PING frame is a reply
+     */
+    public PingFrame(boolean reply)
+    {
+        this(EMPTY_PAYLOAD, reply);
+    }
+
+    /**
+     * Creates a PING frame with the given {@code long} {@code value} as payload.
+     *
+     * @param value the value to use as a payload for this PING frame
+     * @param reply whether this PING frame is a reply
+     */
+    public PingFrame(long value, boolean reply)
+    {
+        this(toBytes(value), reply);
+    }
+
+    /**
+     * Creates a PING frame with the given {@code payload}.
+     *
+     * @param payload the payload for this PING frame
+     * @param reply whether this PING frame is a reply
+     */
+    public PingFrame(byte[] payload, boolean reply)
+    {
+        super(FrameType.PING);
+        this.payload = Objects.requireNonNull(payload);
+        if (payload.length != PING_LENGTH)
+            throw new IllegalArgumentException("PING payload must be 8 bytes");
+        this.reply = reply;
+    }
+
+    public byte[] getPayload()
+    {
+        return payload;
+    }
+
+    public long getPayloadAsLong()
+    {
+        return toLong(payload);
+    }
+
+    public boolean isReply()
+    {
+        return reply;
+    }
+
+    private static byte[] toBytes(long value)
+    {
+        byte[] result = new byte[8];
+        for (int i = result.length - 1; i >= 0; --i)
+        {
+            result[i] = (byte)(value & 0xFF);
+            value >>= 8;
+        }
+        return result;
+    }
+
+    private static long toLong(byte[] payload)
+    {
+        long result = 0;
+        for (int i = 0; i < 8; ++i)
+        {
+            result <<= 8;
+            result |= (payload[i] & 0xFF);
+        }
+        return result;
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PrefaceFrame.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PrefaceFrame.java
new file mode 100644
index 0000000..d5bc6c8
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PrefaceFrame.java
@@ -0,0 +1,48 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.charset.StandardCharsets;
+
+public class PrefaceFrame extends Frame
+{
+    /**
+     * The bytes of the HTTP/2 preface that form a legal HTTP/1.1
+     * request, used in the direct upgrade.
+     */
+    public static final byte[] PREFACE_PREAMBLE_BYTES = (
+            "PRI * HTTP/2.0\r\n" +
+            "\r\n"
+    ).getBytes(StandardCharsets.US_ASCII);
+
+    /**
+     * The HTTP/2 preface bytes.
+     */
+    public static final byte[] PREFACE_BYTES = (
+            "PRI * HTTP/2.0\r\n" +
+            "\r\n" +
+            "SM\r\n" +
+            "\r\n"
+    ).getBytes(StandardCharsets.US_ASCII);
+
+    public PrefaceFrame()
+    {
+        super(FrameType.PREFACE);
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PriorityFrame.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PriorityFrame.java
new file mode 100644
index 0000000..d790ded
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PriorityFrame.java
@@ -0,0 +1,78 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+public class PriorityFrame extends Frame
+{
+    public static final int PRIORITY_LENGTH = 5;
+
+    private final int streamId;
+    private final int parentStreamId;
+    private final int weight;
+    private final boolean exclusive;
+
+    public PriorityFrame(int parentStreamId, int weight, boolean exclusive)
+    {
+        this(0, parentStreamId, weight, exclusive);
+    }
+
+    public PriorityFrame(int streamId, int parentStreamId, int weight, boolean exclusive)
+    {
+        super(FrameType.PRIORITY);
+        this.streamId = streamId;
+        this.parentStreamId = parentStreamId;
+        this.weight = weight;
+        this.exclusive = exclusive;
+    }
+
+    public int getStreamId()
+    {
+        return streamId;
+    }
+
+    /**
+     * @deprecated use {@link #getParentStreamId()} instead.
+     */
+    @Deprecated
+    public int getDependentStreamId()
+    {
+        return getParentStreamId();
+    }
+
+    public int getParentStreamId()
+    {
+        return parentStreamId;
+    }
+
+    public int getWeight()
+    {
+        return weight;
+    }
+
+    public boolean isExclusive()
+    {
+        return exclusive;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s#%d/#%d{weight=%d,exclusive=%b}", super.toString(), streamId, parentStreamId, weight, exclusive);
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PushPromiseFrame.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PushPromiseFrame.java
new file mode 100644
index 0000000..b6d32ee
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PushPromiseFrame.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import org.eclipse.jetty.http.MetaData;
+
+public class PushPromiseFrame extends Frame
+{
+    private final int streamId;
+    private final int promisedStreamId;
+    private final MetaData metaData;
+
+    public PushPromiseFrame(int streamId, int promisedStreamId, MetaData metaData)
+    {
+        super(FrameType.PUSH_PROMISE);
+        this.streamId = streamId;
+        this.promisedStreamId = promisedStreamId;
+        this.metaData = metaData;
+    }
+
+    public int getStreamId()
+    {
+        return streamId;
+    }
+
+    public int getPromisedStreamId()
+    {
+        return promisedStreamId;
+    }
+
+    public MetaData getMetaData()
+    {
+        return metaData;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s#%d/#%d", super.toString(), streamId, promisedStreamId);
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/ResetFrame.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/ResetFrame.java
new file mode 100644
index 0000000..79af74c
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/ResetFrame.java
@@ -0,0 +1,56 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.util.Locale;
+
+import org.eclipse.jetty.http2.ErrorCode;
+
+public class ResetFrame extends Frame
+{
+    public static final int RESET_LENGTH = 4;
+
+    private final int streamId;
+    private final int error;
+
+    public ResetFrame(int streamId, int error)
+    {
+        super(FrameType.RST_STREAM);
+        this.streamId = streamId;
+        this.error = error;
+    }
+
+    public int getStreamId()
+    {
+        return streamId;
+    }
+
+    public int getError()
+    {
+        return error;
+    }
+
+    @Override
+    public String toString()
+    {
+        ErrorCode errorCode = ErrorCode.from(error);
+        String reason = errorCode == null ? "error=" + error : errorCode.name().toLowerCase(Locale.ENGLISH);
+        return String.format("%s#%d{%s}", super.toString(), streamId, reason);
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/SettingsFrame.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/SettingsFrame.java
new file mode 100644
index 0000000..1c9ad65
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/SettingsFrame.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.util.Map;
+
+public class SettingsFrame extends Frame
+{
+    public static final int HEADER_TABLE_SIZE = 1;
+    public static final int ENABLE_PUSH = 2;
+    public static final int MAX_CONCURRENT_STREAMS = 3;
+    public static final int INITIAL_WINDOW_SIZE = 4;
+    public static final int MAX_FRAME_SIZE = 5;
+    public static final int MAX_HEADER_LIST_SIZE = 6;
+    
+    private final Map<Integer, Integer> settings;
+    private final boolean reply;
+
+    public SettingsFrame(Map<Integer, Integer> settings, boolean reply)
+    {
+        super(FrameType.SETTINGS);
+        this.settings = settings;
+        this.reply = reply;
+    }
+
+    public Map<Integer, Integer> getSettings()
+    {
+        return settings;
+    }
+
+    public boolean isReply()
+    {
+        return reply;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s,reply=%b:%s", super.toString(), reply, settings);
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/WindowUpdateFrame.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/WindowUpdateFrame.java
new file mode 100644
index 0000000..5527b7a
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/WindowUpdateFrame.java
@@ -0,0 +1,50 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+public class WindowUpdateFrame extends Frame
+{
+    public static final int WINDOW_UPDATE_LENGTH = 4;
+
+    private final int streamId;
+    private final int windowDelta;
+
+    public WindowUpdateFrame(int streamId, int windowDelta)
+    {
+        super(FrameType.WINDOW_UPDATE);
+        this.streamId = streamId;
+        this.windowDelta = windowDelta;
+    }
+
+    public int getStreamId()
+    {
+        return streamId;
+    }
+
+    public int getWindowDelta()
+    {
+        return windowDelta;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s#%d,delta=%d", super.toString(), streamId, windowDelta);
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/DataGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/DataGenerator.java
new file mode 100644
index 0000000..0329d7c
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/DataGenerator.java
@@ -0,0 +1,95 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+
+public class DataGenerator
+{
+    private final HeaderGenerator headerGenerator;
+
+    public DataGenerator(HeaderGenerator headerGenerator)
+    {
+        this.headerGenerator = headerGenerator;
+    }
+
+    public int generate(ByteBufferPool.Lease lease, DataFrame frame, int maxLength)
+    {
+        return generateData(lease, frame.getStreamId(), frame.getData(), frame.isEndStream(), maxLength);
+    }
+
+    public int generateData(ByteBufferPool.Lease lease, int streamId, ByteBuffer data, boolean last, int maxLength)
+    {
+        if (streamId < 0)
+            throw new IllegalArgumentException("Invalid stream id: " + streamId);
+
+        int dataLength = data.remaining();
+        int maxFrameSize = headerGenerator.getMaxFrameSize();
+        if (dataLength <= maxLength && dataLength <= maxFrameSize)
+        {
+            // Single frame.
+            return generateFrame(lease, streamId, data, last);
+        }
+
+        // Other cases, we need to slice the original buffer into multiple frames.
+
+        int length = Math.min(maxLength, dataLength);
+        int frames = length / maxFrameSize;
+        if (frames * maxFrameSize != length)
+            ++frames;
+
+        int totalLength = 0;
+        int begin = data.position();
+        int end = data.limit();
+        for (int i = 1; i <= frames; ++i)
+        {
+            int limit = begin + Math.min(maxFrameSize * i, length);
+            data.limit(limit);
+            ByteBuffer slice = data.slice();
+            data.position(limit);
+            totalLength += generateFrame(lease, streamId, slice, i == frames && last && limit == end);
+        }
+        data.limit(end);
+
+        return totalLength;
+    }
+
+    private int generateFrame(ByteBufferPool.Lease lease, int streamId, ByteBuffer data, boolean last)
+    {
+        int length = data.remaining();
+
+        int flags = Flags.NONE;
+        if (last)
+            flags |= Flags.END_STREAM;
+
+        ByteBuffer header = headerGenerator.generate(lease, FrameType.DATA, Frame.HEADER_LENGTH + length, length, flags, streamId);
+        BufferUtil.flipToFlush(header, 0);
+        lease.append(header, true);
+        lease.append(data, false);
+
+        return Frame.HEADER_LENGTH + length;
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/DisconnectGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/DisconnectGenerator.java
new file mode 100644
index 0000000..67f927a
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/DisconnectGenerator.java
@@ -0,0 +1,36 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.io.ByteBufferPool;
+
+public class DisconnectGenerator extends FrameGenerator
+{
+    public DisconnectGenerator()
+    {
+        super(null);
+    }
+
+    @Override
+    public int generate(ByteBufferPool.Lease lease, Frame frame)
+    {
+        return 0;
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/FrameGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/FrameGenerator.java
new file mode 100644
index 0000000..2eae0cb
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/FrameGenerator.java
@@ -0,0 +1,47 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.io.ByteBufferPool;
+
+public abstract class FrameGenerator
+{
+    private final HeaderGenerator headerGenerator;
+
+    protected FrameGenerator(HeaderGenerator headerGenerator)
+    {
+        this.headerGenerator = headerGenerator;
+    }
+
+    public abstract int generate(ByteBufferPool.Lease lease, Frame frame);
+
+    protected ByteBuffer generateHeader(ByteBufferPool.Lease lease, FrameType frameType, int length, int flags, int streamId)
+    {
+        return headerGenerator.generate(lease, frameType, Frame.HEADER_LENGTH + length, length, flags, streamId);
+    }
+
+    public int getMaxFrameSize()
+    {
+        return headerGenerator.getMaxFrameSize();
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/Generator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/Generator.java
new file mode 100644
index 0000000..3b1d56f
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/Generator.java
@@ -0,0 +1,92 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.hpack.HpackEncoder;
+import org.eclipse.jetty.io.ByteBufferPool;
+
+public class Generator
+{
+    private final ByteBufferPool byteBufferPool;
+    private final HeaderGenerator headerGenerator;
+    private final HpackEncoder hpackEncoder;
+    private final FrameGenerator[] generators;
+    private final DataGenerator dataGenerator;
+
+    public Generator(ByteBufferPool byteBufferPool)
+    {
+        this(byteBufferPool, 4096, 0);
+    }
+
+    public Generator(ByteBufferPool byteBufferPool, int maxDynamicTableSize, int maxHeaderBlockFragment)
+    {
+        this.byteBufferPool = byteBufferPool;
+
+        headerGenerator = new HeaderGenerator();
+        hpackEncoder = new HpackEncoder(maxDynamicTableSize);
+
+        this.generators = new FrameGenerator[FrameType.values().length];
+        this.generators[FrameType.HEADERS.getType()] = new HeadersGenerator(headerGenerator, hpackEncoder, maxHeaderBlockFragment);
+        this.generators[FrameType.PRIORITY.getType()] = new PriorityGenerator(headerGenerator);
+        this.generators[FrameType.RST_STREAM.getType()] = new ResetGenerator(headerGenerator);
+        this.generators[FrameType.SETTINGS.getType()] = new SettingsGenerator(headerGenerator);
+        this.generators[FrameType.PUSH_PROMISE.getType()] = new PushPromiseGenerator(headerGenerator, hpackEncoder);
+        this.generators[FrameType.PING.getType()] = new PingGenerator(headerGenerator);
+        this.generators[FrameType.GO_AWAY.getType()] = new GoAwayGenerator(headerGenerator);
+        this.generators[FrameType.WINDOW_UPDATE.getType()] = new WindowUpdateGenerator(headerGenerator);
+        this.generators[FrameType.CONTINUATION.getType()] = null; // Never generated explicitly.
+        this.generators[FrameType.PREFACE.getType()] = new PrefaceGenerator();
+        this.generators[FrameType.DISCONNECT.getType()] = new DisconnectGenerator();
+
+        this.dataGenerator = new DataGenerator(headerGenerator);
+    }
+
+    public ByteBufferPool getByteBufferPool()
+    {
+        return byteBufferPool;
+    }
+
+    public void setHeaderTableSize(int headerTableSize)
+    {
+        hpackEncoder.setRemoteMaxDynamicTableSize(headerTableSize);
+    }
+
+    public void setMaxFrameSize(int maxFrameSize)
+    {
+        headerGenerator.setMaxFrameSize(maxFrameSize);
+    }
+
+    public int control(ByteBufferPool.Lease lease, Frame frame)
+    {
+        return generators[frame.getType().getType()].generate(lease, frame);
+    }
+
+    public int data(ByteBufferPool.Lease lease, DataFrame frame, int maxLength)
+    {
+        return dataGenerator.generate(lease, frame, maxLength);
+    }
+
+    public void setMaxHeaderListSize(int value)
+    {
+        hpackEncoder.setMaxHeaderListSize(value);
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/GoAwayGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/GoAwayGenerator.java
new file mode 100644
index 0000000..be8875b
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/GoAwayGenerator.java
@@ -0,0 +1,72 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+
+public class GoAwayGenerator extends FrameGenerator
+{
+    public GoAwayGenerator(HeaderGenerator headerGenerator)
+    {
+        super(headerGenerator);
+    }
+
+    @Override
+    public int generate(ByteBufferPool.Lease lease, Frame frame)
+    {
+        GoAwayFrame goAwayFrame = (GoAwayFrame)frame;
+        return generateGoAway(lease, goAwayFrame.getLastStreamId(), goAwayFrame.getError(), goAwayFrame.getPayload());
+    }
+
+    public int generateGoAway(ByteBufferPool.Lease lease, int lastStreamId, int error, byte[] payload)
+    {
+        if (lastStreamId < 0)
+            throw new IllegalArgumentException("Invalid last stream id: " + lastStreamId);
+
+        // The last streamId + the error code.
+        int fixedLength = 4 + 4;
+
+        // Make sure we don't exceed the default frame max length.
+        int maxPayloadLength = Frame.DEFAULT_MAX_LENGTH - fixedLength;
+        if (payload != null && payload.length > maxPayloadLength)
+            payload = Arrays.copyOfRange(payload, 0, maxPayloadLength);
+
+        int length = fixedLength + (payload != null ? payload.length : 0);
+        ByteBuffer header = generateHeader(lease, FrameType.GO_AWAY, length, Flags.NONE, 0);
+
+        header.putInt(lastStreamId);
+        header.putInt(error);
+
+        if (payload != null)
+            header.put(payload);
+
+        BufferUtil.flipToFlush(header, 0);
+        lease.append(header, true);
+
+        return Frame.HEADER_LENGTH + length;
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeaderGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeaderGenerator.java
new file mode 100644
index 0000000..d0a14ea
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeaderGenerator.java
@@ -0,0 +1,52 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.io.ByteBufferPool;
+
+public class HeaderGenerator
+{
+    private int maxFrameSize = Frame.DEFAULT_MAX_LENGTH;
+
+    public ByteBuffer generate(ByteBufferPool.Lease lease, FrameType frameType, int capacity, int length, int flags, int streamId)
+    {
+        ByteBuffer header = lease.acquire(capacity, true);
+        header.put((byte)((length & 0x00_FF_00_00) >>> 16));
+        header.put((byte)((length & 0x00_00_FF_00) >>> 8));
+        header.put((byte)((length & 0x00_00_00_FF)));
+        header.put((byte)frameType.getType());
+        header.put((byte)flags);
+        header.putInt(streamId);
+        return header;
+    }
+
+    public int getMaxFrameSize()
+    {
+        return maxFrameSize;
+    }
+
+    public void setMaxFrameSize(int maxFrameSize)
+    {
+        this.maxFrameSize = maxFrameSize;
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeadersGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeadersGenerator.java
new file mode 100644
index 0000000..6fbf882
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeadersGenerator.java
@@ -0,0 +1,146 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PriorityFrame;
+import org.eclipse.jetty.http2.hpack.HpackEncoder;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+
+public class HeadersGenerator extends FrameGenerator
+{
+    private final HpackEncoder encoder;
+    private final int maxHeaderBlockFragment;
+    private final PriorityGenerator priorityGenerator;
+
+    public HeadersGenerator(HeaderGenerator headerGenerator, HpackEncoder encoder)
+    {
+        this(headerGenerator, encoder, 0);
+    }
+
+    public HeadersGenerator(HeaderGenerator headerGenerator, HpackEncoder encoder, int maxHeaderBlockFragment)
+    {
+        super(headerGenerator);
+        this.encoder = encoder;
+        this.maxHeaderBlockFragment = maxHeaderBlockFragment;
+        this.priorityGenerator = new PriorityGenerator(headerGenerator);
+    }
+
+    @Override
+    public int generate(ByteBufferPool.Lease lease, Frame frame)
+    {
+        HeadersFrame headersFrame = (HeadersFrame)frame;
+        return generateHeaders(lease, headersFrame.getStreamId(), headersFrame.getMetaData(), headersFrame.getPriority(), headersFrame.isEndStream());
+    }
+
+    public int generateHeaders(ByteBufferPool.Lease lease, int streamId, MetaData metaData, PriorityFrame priority, boolean endStream)
+    {
+        if (streamId < 0)
+            throw new IllegalArgumentException("Invalid stream id: " + streamId);
+
+        int flags = Flags.NONE;
+
+        if (priority != null)
+            flags = Flags.PRIORITY;
+
+        int maxFrameSize = getMaxFrameSize();
+        ByteBuffer hpacked = lease.acquire(maxFrameSize, false);
+        BufferUtil.clearToFill(hpacked);
+        encoder.encode(hpacked, metaData);
+        int hpackedLength = hpacked.position();
+        BufferUtil.flipToFlush(hpacked, 0);
+
+        // Split into CONTINUATION frames if necessary.
+        if (maxHeaderBlockFragment > 0 && hpackedLength > maxHeaderBlockFragment)
+        {
+            if (endStream)
+                flags |= Flags.END_STREAM;
+
+            int length = maxHeaderBlockFragment;
+            if (priority != null)
+                length += PriorityFrame.PRIORITY_LENGTH;
+
+            ByteBuffer header = generateHeader(lease, FrameType.HEADERS, length, flags, streamId);
+            generatePriority(header, priority);
+            BufferUtil.flipToFlush(header, 0);
+            lease.append(header, true);
+            hpacked.limit(maxHeaderBlockFragment);
+            lease.append(hpacked.slice(), false);
+
+            int totalLength = Frame.HEADER_LENGTH + length;
+
+            int position = maxHeaderBlockFragment;
+            int limit = position + maxHeaderBlockFragment;
+            while (limit < hpackedLength)
+            {
+                hpacked.position(position).limit(limit);
+                header = generateHeader(lease, FrameType.CONTINUATION, maxHeaderBlockFragment, Flags.NONE, streamId);
+                BufferUtil.flipToFlush(header, 0);
+                lease.append(header, true);
+                lease.append(hpacked.slice(), false);
+                position += maxHeaderBlockFragment;
+                limit += maxHeaderBlockFragment;
+                totalLength += Frame.HEADER_LENGTH + maxHeaderBlockFragment;
+            }
+
+            hpacked.position(position).limit(hpackedLength);
+            header = generateHeader(lease, FrameType.CONTINUATION, hpacked.remaining(), Flags.END_HEADERS, streamId);
+            BufferUtil.flipToFlush(header, 0);
+            lease.append(header, true);
+            lease.append(hpacked, true);
+            totalLength += Frame.HEADER_LENGTH + hpacked.remaining();
+
+            return totalLength;
+        }
+        else
+        {
+            flags |= Flags.END_HEADERS;
+            if (endStream)
+                flags |= Flags.END_STREAM;
+
+            int length = hpackedLength;
+            if (priority != null)
+                length += PriorityFrame.PRIORITY_LENGTH;
+
+            ByteBuffer header = generateHeader(lease, FrameType.HEADERS, length, flags, streamId);
+            generatePriority(header, priority);
+            BufferUtil.flipToFlush(header, 0);
+            lease.append(header, true);
+            lease.append(hpacked, true);
+
+            return Frame.HEADER_LENGTH + length;
+        }
+    }
+
+    private void generatePriority(ByteBuffer header, PriorityFrame priority)
+    {
+        if (priority != null)
+        {
+            priorityGenerator.generatePriorityBody(header, priority.getStreamId(),
+                    priority.getParentStreamId(), priority.getWeight(), priority.isExclusive());
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PingGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PingGenerator.java
new file mode 100644
index 0000000..6810388
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PingGenerator.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.frames.PingFrame;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+
+public class PingGenerator extends FrameGenerator
+{
+    public PingGenerator(HeaderGenerator headerGenerator)
+    {
+        super(headerGenerator);
+    }
+
+    @Override
+    public int generate(ByteBufferPool.Lease lease, Frame frame)
+    {
+        PingFrame pingFrame = (PingFrame)frame;
+        return generatePing(lease, pingFrame.getPayload(), pingFrame.isReply());
+    }
+
+    public int generatePing(ByteBufferPool.Lease lease, byte[] payload, boolean reply)
+    {
+        if (payload.length != PingFrame.PING_LENGTH)
+            throw new IllegalArgumentException("Invalid payload length: " + payload.length);
+
+        ByteBuffer header = generateHeader(lease, FrameType.PING, PingFrame.PING_LENGTH, reply ? Flags.ACK : Flags.NONE, 0);
+
+        header.put(payload);
+
+        BufferUtil.flipToFlush(header, 0);
+        lease.append(header, true);
+
+        return Frame.HEADER_LENGTH + PingFrame.PING_LENGTH;
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PrefaceGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PrefaceGenerator.java
new file mode 100644
index 0000000..a472d00
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PrefaceGenerator.java
@@ -0,0 +1,40 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.PrefaceFrame;
+import org.eclipse.jetty.io.ByteBufferPool;
+
+public class PrefaceGenerator extends FrameGenerator
+{
+    public PrefaceGenerator()
+    {
+        super(null);
+    }
+
+    @Override
+    public int generate(ByteBufferPool.Lease lease, Frame frame)
+    {
+        lease.append(ByteBuffer.wrap(PrefaceFrame.PREFACE_BYTES), false);
+        return PrefaceFrame.PREFACE_BYTES.length;
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PriorityGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PriorityGenerator.java
new file mode 100644
index 0000000..ad41538
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PriorityGenerator.java
@@ -0,0 +1,69 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.frames.PriorityFrame;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+
+public class PriorityGenerator extends FrameGenerator
+{
+    public PriorityGenerator(HeaderGenerator headerGenerator)
+    {
+        super(headerGenerator);
+    }
+
+    @Override
+    public int generate(ByteBufferPool.Lease lease, Frame frame)
+    {
+        PriorityFrame priorityFrame = (PriorityFrame)frame;
+        return generatePriority(lease, priorityFrame.getStreamId(), priorityFrame.getParentStreamId(), priorityFrame.getWeight(), priorityFrame.isExclusive());
+    }
+
+    public int generatePriority(ByteBufferPool.Lease lease, int streamId, int parentStreamId, int weight, boolean exclusive)
+    {
+        ByteBuffer header = generateHeader(lease, FrameType.PRIORITY, PriorityFrame.PRIORITY_LENGTH, Flags.NONE, streamId);
+        generatePriorityBody(header, streamId, parentStreamId, weight, exclusive);
+        BufferUtil.flipToFlush(header, 0);
+        lease.append(header, true);
+        return Frame.HEADER_LENGTH + PriorityFrame.PRIORITY_LENGTH;
+    }
+
+    public void generatePriorityBody(ByteBuffer header, int streamId, int parentStreamId, int weight, boolean exclusive)
+    {
+        if (streamId < 0)
+            throw new IllegalArgumentException("Invalid stream id: " + streamId);
+        if (parentStreamId < 0)
+            throw new IllegalArgumentException("Invalid parent stream id: " + parentStreamId);
+        if (parentStreamId == streamId)
+            throw new IllegalArgumentException("Stream " + streamId + " cannot depend on stream " + parentStreamId);
+        if (weight < 1 || weight > 256)
+            throw new IllegalArgumentException("Invalid weight: " + weight);
+
+        if (exclusive)
+            parentStreamId |= 0x80_00_00_00;
+        header.putInt(parentStreamId);
+        header.put((byte)(weight - 1));
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PushPromiseGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PushPromiseGenerator.java
new file mode 100644
index 0000000..9de01ff
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PushPromiseGenerator.java
@@ -0,0 +1,79 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.hpack.HpackEncoder;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+
+public class PushPromiseGenerator extends FrameGenerator
+{
+    private final HpackEncoder encoder;
+
+    public PushPromiseGenerator(HeaderGenerator headerGenerator, HpackEncoder encoder)
+    {
+        super(headerGenerator);
+        this.encoder = encoder;
+    }
+
+    @Override
+    public int generate(ByteBufferPool.Lease lease, Frame frame)
+    {
+        PushPromiseFrame pushPromiseFrame = (PushPromiseFrame)frame;
+        return generatePushPromise(lease, pushPromiseFrame.getStreamId(), pushPromiseFrame.getPromisedStreamId(), pushPromiseFrame.getMetaData());
+    }
+
+    public int generatePushPromise(ByteBufferPool.Lease lease, int streamId, int promisedStreamId, MetaData metaData)
+    {
+        if (streamId < 0)
+            throw new IllegalArgumentException("Invalid stream id: " + streamId);
+        if (promisedStreamId < 0)
+            throw new IllegalArgumentException("Invalid promised stream id: " + promisedStreamId);
+
+        int maxFrameSize = getMaxFrameSize();
+        // The promised streamId space.
+        int extraSpace = 4;
+        maxFrameSize -= extraSpace;
+
+        ByteBuffer hpacked = lease.acquire(maxFrameSize, false);
+        BufferUtil.clearToFill(hpacked);
+        encoder.encode(hpacked, metaData);
+        int hpackedLength = hpacked.position();
+        BufferUtil.flipToFlush(hpacked, 0);
+
+        int length = hpackedLength + extraSpace;
+        int flags = Flags.END_HEADERS;
+
+        ByteBuffer header = generateHeader(lease, FrameType.PUSH_PROMISE, length, flags, streamId);
+        header.putInt(promisedStreamId);
+        BufferUtil.flipToFlush(header, 0);
+
+        lease.append(header, true);
+        lease.append(hpacked, true);
+
+        return Frame.HEADER_LENGTH + length;
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/ResetGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/ResetGenerator.java
new file mode 100644
index 0000000..d4c519f
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/ResetGenerator.java
@@ -0,0 +1,56 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+
+public class ResetGenerator extends FrameGenerator
+{
+    public ResetGenerator(HeaderGenerator headerGenerator)
+    {
+        super(headerGenerator);
+    }
+
+    @Override
+    public int generate(ByteBufferPool.Lease lease, Frame frame)
+    {
+        ResetFrame resetFrame = (ResetFrame)frame;
+        return generateReset(lease, resetFrame.getStreamId(), resetFrame.getError());
+    }
+
+    public int generateReset(ByteBufferPool.Lease lease, int streamId, int error)
+    {
+        if (streamId < 0)
+            throw new IllegalArgumentException("Invalid stream id: " + streamId);
+
+        ByteBuffer header = generateHeader(lease, FrameType.RST_STREAM, ResetFrame.RESET_LENGTH, Flags.NONE, streamId);
+        header.putInt(error);
+        BufferUtil.flipToFlush(header, 0);
+        lease.append(header, true);
+
+        return Frame.HEADER_LENGTH + ResetFrame.RESET_LENGTH;
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/SettingsGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/SettingsGenerator.java
new file mode 100644
index 0000000..0a26757
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/SettingsGenerator.java
@@ -0,0 +1,66 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+
+public class SettingsGenerator extends FrameGenerator
+{
+    public SettingsGenerator(HeaderGenerator headerGenerator)
+    {
+        super(headerGenerator);
+    }
+
+    @Override
+    public int generate(ByteBufferPool.Lease lease, Frame frame)
+    {
+        SettingsFrame settingsFrame = (SettingsFrame)frame;
+        return generateSettings(lease, settingsFrame.getSettings(), settingsFrame.isReply());
+    }
+
+    public int generateSettings(ByteBufferPool.Lease lease, Map<Integer, Integer> settings, boolean reply)
+    {
+        // Two bytes for the identifier, four bytes for the value.
+        int entryLength = 2 + 4;
+        int length = entryLength * settings.size();
+        if (length > getMaxFrameSize())
+            throw new IllegalArgumentException("Invalid settings, too big");
+
+        ByteBuffer header = generateHeader(lease, FrameType.SETTINGS, length, reply ? Flags.ACK : Flags.NONE, 0);
+
+        for (Map.Entry<Integer, Integer> entry : settings.entrySet())
+        {
+            header.putShort(entry.getKey().shortValue());
+            header.putInt(entry.getValue());
+        }
+
+        BufferUtil.flipToFlush(header, 0);
+        lease.append(header, true);
+
+        return Frame.HEADER_LENGTH + length;
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/WindowUpdateGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/WindowUpdateGenerator.java
new file mode 100644
index 0000000..3451e05
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/WindowUpdateGenerator.java
@@ -0,0 +1,56 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.generator;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+
+public class WindowUpdateGenerator extends FrameGenerator
+{
+    public WindowUpdateGenerator(HeaderGenerator headerGenerator)
+    {
+        super(headerGenerator);
+    }
+
+    @Override
+    public int generate(ByteBufferPool.Lease lease, Frame frame)
+    {
+        WindowUpdateFrame windowUpdateFrame = (WindowUpdateFrame)frame;
+        return generateWindowUpdate(lease, windowUpdateFrame.getStreamId(), windowUpdateFrame.getWindowDelta());
+    }
+
+    public int generateWindowUpdate(ByteBufferPool.Lease lease, int streamId, int windowUpdate)
+    {
+        if (windowUpdate < 0)
+            throw new IllegalArgumentException("Invalid window update: " + windowUpdate);
+
+        ByteBuffer header = generateHeader(lease, FrameType.WINDOW_UPDATE, WindowUpdateFrame.WINDOW_UPDATE_LENGTH, Flags.NONE, streamId);
+        header.putInt(windowUpdate);
+        BufferUtil.flipToFlush(header, 0);
+        lease.append(header, true);
+
+        return Frame.HEADER_LENGTH + WindowUpdateFrame.WINDOW_UPDATE_LENGTH;
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/BodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/BodyParser.java
new file mode 100644
index 0000000..347e49c
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/BodyParser.java
@@ -0,0 +1,225 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PingFrame;
+import org.eclipse.jetty.http2.frames.PriorityFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * <p>The base parser for the frame body of HTTP/2 frames.</p>
+ * <p>Subclasses implement {@link #parse(ByteBuffer)} to parse
+ * the frame specific body.</p>
+ *
+ * @see Parser
+ */
+public abstract class BodyParser
+{
+    protected static final Logger LOG = Log.getLogger(BodyParser.class);
+
+    private final HeaderParser headerParser;
+    private final Parser.Listener listener;
+
+    protected BodyParser(HeaderParser headerParser, Parser.Listener listener)
+    {
+        this.headerParser = headerParser;
+        this.listener = listener;
+    }
+
+    /**
+     * <p>Parses the body bytes in the given {@code buffer}; only the body
+     * bytes are consumed, therefore when this method returns, the buffer
+     * may contain unconsumed bytes.</p>
+     *
+     * @param buffer the buffer to parse
+     * @return true if the whole body bytes were parsed, false if not enough
+     * body bytes were present in the buffer
+     */
+    public abstract boolean parse(ByteBuffer buffer);
+
+    protected void emptyBody(ByteBuffer buffer)
+    {
+        connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_frame");
+    }
+
+    protected boolean hasFlag(int bit)
+    {
+        return headerParser.hasFlag(bit);
+    }
+
+    protected boolean isPadding()
+    {
+        return headerParser.hasFlag(Flags.PADDING);
+    }
+
+    protected boolean isEndStream()
+    {
+        return headerParser.hasFlag(Flags.END_STREAM);
+    }
+
+    protected int getStreamId()
+    {
+        return headerParser.getStreamId();
+    }
+
+    protected int getBodyLength()
+    {
+        return headerParser.getLength();
+    }
+
+    protected void notifyData(DataFrame frame)
+    {
+        try
+        {
+            listener.onData(frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected void notifyHeaders(HeadersFrame frame)
+    {
+        try
+        {
+            listener.onHeaders(frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected void notifyPriority(PriorityFrame frame)
+    {
+        try
+        {
+            listener.onPriority(frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected void notifyReset(ResetFrame frame)
+    {
+        try
+        {
+            listener.onReset(frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected void notifySettings(SettingsFrame frame)
+    {
+        try
+        {
+            listener.onSettings(frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected void notifyPushPromise(PushPromiseFrame frame)
+    {
+        try
+        {
+            listener.onPushPromise(frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected void notifyPing(PingFrame frame)
+    {
+        try
+        {
+            listener.onPing(frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected void notifyGoAway(GoAwayFrame frame)
+    {
+        try
+        {
+            listener.onGoAway(frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected void notifyWindowUpdate(WindowUpdateFrame frame)
+    {
+        try
+        {
+            listener.onWindowUpdate(frame);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    protected boolean connectionFailure(ByteBuffer buffer, int error, String reason)
+    {
+        BufferUtil.clear(buffer);
+        notifyConnectionFailure(error, reason);
+        return false;
+    }
+
+    private void notifyConnectionFailure(int error, String reason)
+    {
+        try
+        {
+            listener.onConnectionFailure(error, reason);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ContinuationBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ContinuationBodyParser.java
new file mode 100644
index 0000000..7a5fead
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ContinuationBodyParser.java
@@ -0,0 +1,115 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+
+public class ContinuationBodyParser extends BodyParser
+{
+    private final HeaderBlockParser headerBlockParser;
+    private final HeaderBlockFragments headerBlockFragments;
+    private State state = State.PREPARE;
+    private int length;
+
+    public ContinuationBodyParser(HeaderParser headerParser, Parser.Listener listener, HeaderBlockParser headerBlockParser, HeaderBlockFragments headerBlockFragments)
+    {
+        super(headerParser, listener);
+        this.headerBlockParser = headerBlockParser;
+        this.headerBlockFragments = headerBlockFragments;
+    }
+
+    @Override
+    protected void emptyBody(ByteBuffer buffer)
+    {
+        if (hasFlag(Flags.END_HEADERS))
+            onHeaders();
+    }
+
+    @Override
+    public boolean parse(ByteBuffer buffer)
+    {
+        while (buffer.hasRemaining())
+        {
+            switch (state)
+            {
+                case PREPARE:
+                {
+                    // SPEC: wrong streamId is treated as connection error.
+                    if (getStreamId() == 0)
+                        return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_continuation_frame");
+
+                    if (getStreamId() != headerBlockFragments.getStreamId())
+                        return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_continuation_stream");
+
+                    length = getBodyLength();
+                    state = State.FRAGMENT;
+                    break;
+                }
+                case FRAGMENT:
+                {
+                    int remaining = buffer.remaining();
+                    if (remaining < length)
+                    {
+                        headerBlockFragments.storeFragment(buffer, remaining, false);
+                        length -= remaining;
+                        break;
+                    }
+                    else
+                    {
+                        boolean last = hasFlag(Flags.END_HEADERS);
+                        headerBlockFragments.storeFragment(buffer, length, last);
+                        reset();
+                        if (last)
+                            onHeaders();
+                        return true;
+                    }
+                }
+                default:
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        return false;
+    }
+
+    private void onHeaders()
+    {
+        ByteBuffer headerBlock = headerBlockFragments.complete();
+        MetaData metaData = headerBlockParser.parse(headerBlock, headerBlock.remaining());
+        HeadersFrame frame = new HeadersFrame(getStreamId(), metaData, headerBlockFragments.getPriorityFrame(), headerBlockFragments.isEndStream());
+        notifyHeaders(frame);
+    }
+
+    private void reset()
+    {
+        state = State.PREPARE;
+        length = 0;
+    }
+
+    private enum State
+    {
+        PREPARE, FRAGMENT
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/DataBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/DataBodyParser.java
new file mode 100644
index 0000000..27c8b5b
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/DataBodyParser.java
@@ -0,0 +1,145 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.util.BufferUtil;
+
+public class DataBodyParser extends BodyParser
+{
+    private State state = State.PREPARE;
+    private int padding;
+    private int paddingLength;
+    private int length;
+
+    public DataBodyParser(HeaderParser headerParser, Parser.Listener listener)
+    {
+        super(headerParser, listener);
+    }
+
+    private void reset()
+    {
+        state = State.PREPARE;
+        padding = 0;
+        paddingLength = 0;
+        length = 0;
+    }
+
+    @Override
+    protected void emptyBody(ByteBuffer buffer)
+    {
+        if (isPadding())
+            connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_data_frame");
+        else
+            onData(BufferUtil.EMPTY_BUFFER, false, 0);
+    }
+
+    @Override
+    public boolean parse(ByteBuffer buffer)
+    {
+        boolean loop = false;
+        while (buffer.hasRemaining() || loop)
+        {
+            switch (state)
+            {
+                case PREPARE:
+                {
+                    // SPEC: wrong streamId is treated as connection error.
+                    if (getStreamId() == 0)
+                        return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_data_frame");
+
+                    length = getBodyLength();
+                    state = isPadding() ? State.PADDING_LENGTH : State.DATA;
+                    break;
+                }
+                case PADDING_LENGTH:
+                {
+                    padding = 1; // We have seen this byte.
+                    paddingLength = buffer.get() & 0xFF;
+                    --length;
+                    length -= paddingLength;
+                    state = State.DATA;
+                    loop = length == 0;
+                    if (length < 0)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_data_frame_padding");
+                    break;
+                }
+                case DATA:
+                {
+                    int size = Math.min(buffer.remaining(), length);
+                    int position = buffer.position();
+                    int limit = buffer.limit();
+                    buffer.limit(position + size);
+                    ByteBuffer slice = buffer.slice();
+                    buffer.limit(limit);
+                    buffer.position(position + size);
+
+                    length -= size;
+                    if (length == 0)
+                    {
+                        state = State.PADDING;
+                        loop = paddingLength == 0;
+                        // Padding bytes include the bytes that define the
+                        // padding length plus the actual padding bytes.
+                        onData(slice, false, padding + paddingLength);
+                    }
+                    else
+                    {
+                        // We got partial data, simulate a smaller frame, and stay in DATA state.
+                        // No padding for these synthetic frames (even if we have read
+                        // the padding length already), it will be accounted at the end.
+                        onData(slice, true, 0);
+                    }
+                    break;
+                }
+                case PADDING:
+                {
+                    int size = Math.min(buffer.remaining(), paddingLength);
+                    buffer.position(buffer.position() + size);
+                    paddingLength -= size;
+                    if (paddingLength == 0)
+                    {
+                        reset();
+                        return true;
+                    }
+                    break;
+                }
+                default:
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        return false;
+    }
+
+    private void onData(ByteBuffer buffer, boolean fragment, int padding)
+    {
+        DataFrame frame = new DataFrame(getStreamId(), buffer, !fragment && isEndStream(), padding);
+        notifyData(frame);
+    }
+
+    private enum State
+    {
+        PREPARE, PADDING_LENGTH, DATA, PADDING
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/GoAwayBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/GoAwayBodyParser.java
new file mode 100644
index 0000000..e443fbd
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/GoAwayBodyParser.java
@@ -0,0 +1,177 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+
+public class GoAwayBodyParser extends BodyParser
+{
+    private State state = State.PREPARE;
+    private int cursor;
+    private int length;
+    private int lastStreamId;
+    private int error;
+    private byte[] payload;
+
+    public GoAwayBodyParser(HeaderParser headerParser, Parser.Listener listener)
+    {
+        super(headerParser, listener);
+    }
+
+    private void reset()
+    {
+        state = State.PREPARE;
+        cursor = 0;
+        length = 0;
+        lastStreamId = 0;
+        error = 0;
+        payload = null;
+    }
+
+    @Override
+    public boolean parse(ByteBuffer buffer)
+    {
+        while (buffer.hasRemaining())
+        {
+            switch (state)
+            {
+                case PREPARE:
+                {
+                    state = State.LAST_STREAM_ID;
+                    length = getBodyLength();
+                    break;
+                }
+                case LAST_STREAM_ID:
+                {
+                    if (buffer.remaining() >= 4)
+                    {
+                        lastStreamId = buffer.getInt();
+                        lastStreamId &= 0x7F_FF_FF_FF;
+                        state = State.ERROR;
+                        length -= 4;
+                        if (length <= 0)
+                            return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
+                    }
+                    else
+                    {
+                        state = State.LAST_STREAM_ID_BYTES;
+                        cursor = 4;
+                    }
+                    break;
+                }
+                case LAST_STREAM_ID_BYTES:
+                {
+                    int currByte = buffer.get() & 0xFF;
+                    --cursor;
+                    lastStreamId += currByte << (8 * cursor);
+                    --length;
+                    if (cursor > 0 && length <= 0)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
+                    if (cursor == 0)
+                    {
+                        lastStreamId &= 0x7F_FF_FF_FF;
+                        state = State.ERROR;
+                        if (length == 0)
+                            return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
+                    }
+                    break;
+                }
+                case ERROR:
+                {
+                    if (buffer.remaining() >= 4)
+                    {
+                        error = buffer.getInt();
+                        state = State.PAYLOAD;
+                        length -= 4;
+                        if (length < 0)
+                            return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
+                        if (length == 0)
+                            return onGoAway(lastStreamId, error, null);
+                    }
+                    else
+                    {
+                        state = State.ERROR_BYTES;
+                        cursor = 4;
+                    }
+                    break;
+                }
+                case ERROR_BYTES:
+                {
+                    int currByte = buffer.get() & 0xFF;
+                    --cursor;
+                    error += currByte << (8 * cursor);
+                    --length;
+                    if (cursor > 0 && length <= 0)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
+                    if (cursor == 0)
+                    {
+                        state = State.PAYLOAD;
+                        if (length == 0)
+                            return onGoAway(lastStreamId, error, null);
+                    }
+                    break;
+                }
+                case PAYLOAD:
+                {
+                    payload = new byte[length];
+                    if (buffer.remaining() >= length)
+                    {
+                        buffer.get(payload);
+                        return onGoAway(lastStreamId, error, payload);
+                    }
+                    else
+                    {
+                        state = State.PAYLOAD_BYTES;
+                        cursor = length;
+                    }
+                    break;
+                }
+                case PAYLOAD_BYTES:
+                {
+                    payload[payload.length - cursor] = buffer.get();
+                    --cursor;
+                    if (cursor == 0)
+                        return onGoAway(lastStreamId, error, payload);
+                    break;
+                }
+                default:
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean onGoAway(int lastStreamId, int error, byte[] payload)
+    {
+        GoAwayFrame frame = new GoAwayFrame(lastStreamId, error, payload);
+        reset();
+        notifyGoAway(frame);
+        return true;
+    }
+
+    private enum State
+    {
+        PREPARE, LAST_STREAM_ID, LAST_STREAM_ID_BYTES, ERROR, ERROR_BYTES, PAYLOAD, PAYLOAD_BYTES
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockFragments.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockFragments.java
new file mode 100644
index 0000000..ebf003d
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockFragments.java
@@ -0,0 +1,95 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.frames.PriorityFrame;
+
+public class HeaderBlockFragments
+{
+    private PriorityFrame priorityFrame;
+    private boolean endStream;
+    private int streamId;
+    private ByteBuffer storage;
+
+    public void storeFragment(ByteBuffer fragment, int length, boolean last)
+    {
+        if (storage == null)
+        {
+            int space = last ? length : length * 2;
+            storage = ByteBuffer.allocate(space);
+        }
+
+        // Grow the storage if necessary.
+        if (storage.remaining() < length)
+        {
+            int space = last ? length : length * 2;
+            int capacity = storage.position() + space;
+            ByteBuffer newStorage = ByteBuffer.allocate(capacity);
+            storage.flip();
+            newStorage.put(storage);
+            storage = newStorage;
+        }
+
+        // Copy the fragment into the storage.
+        int limit = fragment.limit();
+        fragment.limit(fragment.position() + length);
+        storage.put(fragment);
+        fragment.limit(limit);
+    }
+
+    public PriorityFrame getPriorityFrame()
+    {
+        return priorityFrame;
+    }
+
+    public void setPriorityFrame(PriorityFrame priorityFrame)
+    {
+        this.priorityFrame = priorityFrame;
+    }
+
+    public boolean isEndStream()
+    {
+        return endStream;
+    }
+
+    public void setEndStream(boolean endStream)
+    {
+        this.endStream = endStream;
+    }
+
+    public ByteBuffer complete()
+    {
+        ByteBuffer result = storage;
+        storage = null;
+        result.flip();
+        return result;
+    }
+
+    public int getStreamId()
+    {
+        return streamId;
+    }
+
+    public void setStreamId(int streamId)
+    {
+        this.streamId = streamId;
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockParser.java
new file mode 100644
index 0000000..f53add8
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockParser.java
@@ -0,0 +1,88 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.hpack.HpackDecoder;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+
+public class HeaderBlockParser
+{
+    private final ByteBufferPool byteBufferPool;
+    private final HpackDecoder hpackDecoder;
+    private ByteBuffer blockBuffer;
+
+    public HeaderBlockParser(ByteBufferPool byteBufferPool, HpackDecoder hpackDecoder)
+    {
+        this.byteBufferPool = byteBufferPool;
+        this.hpackDecoder = hpackDecoder;
+    }
+
+    public MetaData parse(ByteBuffer buffer, int blockLength)
+    {
+        // We must wait for the all the bytes of the header block to arrive.
+        // If they are not all available, accumulate them.
+        // When all are available, decode them.
+
+        int accumulated = blockBuffer == null ? 0 : blockBuffer.position();
+        int remaining = blockLength - accumulated;
+
+        if (buffer.remaining() < remaining)
+        {
+            if (blockBuffer == null)
+            {
+                blockBuffer = byteBufferPool.acquire(blockLength, false);
+                BufferUtil.clearToFill(blockBuffer);
+            }
+            blockBuffer.put(buffer);
+            return null;
+        }
+        else
+        {
+            int limit = buffer.limit();
+            buffer.limit(buffer.position() + remaining);
+            ByteBuffer toDecode;
+            if (blockBuffer != null)
+            {
+                blockBuffer.put(buffer);
+                BufferUtil.flipToFlush(blockBuffer, 0);
+                toDecode = blockBuffer;
+            }
+            else
+            {
+                toDecode = buffer;
+            }
+
+            MetaData result = hpackDecoder.decode(toDecode);
+
+            buffer.limit(limit);
+
+            if (blockBuffer != null)
+            {
+                byteBufferPool.release(blockBuffer);
+                blockBuffer = null;
+            }
+
+            return result;
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderParser.java
new file mode 100644
index 0000000..150fcf2
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderParser.java
@@ -0,0 +1,151 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.frames.Frame;
+
+/**
+ * <p>The parser for the frame header of HTTP/2 frames.</p>
+ *
+ * @see Parser
+ */
+public class HeaderParser
+{
+    private State state = State.LENGTH;
+    private int cursor;
+
+    private int length;
+    private int type;
+    private int flags;
+    private int streamId;
+
+    protected void reset()
+    {
+        state = State.LENGTH;
+        cursor = 0;
+
+        length = 0;
+        type = 0;
+        flags = 0;
+        streamId = 0;
+    }
+
+    /**
+     * <p>Parses the header bytes in the given {@code buffer}; only the header
+     * bytes are consumed, therefore when this method returns, the buffer may
+     * contain unconsumed bytes.</p>
+     *
+     * @param buffer the buffer to parse
+     * @return true if the whole header bytes were parsed, false if not enough
+     * header bytes were present in the buffer
+     */
+    public boolean parse(ByteBuffer buffer)
+    {
+        while (buffer.hasRemaining())
+        {
+            switch (state)
+            {
+                case LENGTH:
+                {
+                    int octet = buffer.get() & 0xFF;
+                    length = (length << 8) + octet;
+                    if (++cursor == 3)
+                    {
+                        length &= Frame.MAX_MAX_LENGTH;
+                        state = State.TYPE;
+                    }
+                    break;
+                }
+                case TYPE:
+                {
+                    type = buffer.get() & 0xFF;
+                    state = State.FLAGS;
+                    break;
+                }
+                case FLAGS:
+                {
+                    flags = buffer.get() & 0xFF;
+                    state = State.STREAM_ID;
+                    break;
+                }
+                case STREAM_ID:
+                {
+                    if (buffer.remaining() >= 4)
+                    {
+                        streamId = buffer.getInt();
+                        // Most significant bit MUST be ignored as per specification.
+                        streamId &= 0x7F_FF_FF_FF;
+                        return true;
+                    }
+                    else
+                    {
+                        state = State.STREAM_ID_BYTES;
+                        cursor = 4;
+                    }
+                    break;
+                }
+                case STREAM_ID_BYTES:
+                {
+                    int currByte = buffer.get() & 0xFF;
+                    --cursor;
+                    streamId += currByte << (8 * cursor);
+                    if (cursor == 0)
+                    {
+                        // Most significant bit MUST be ignored as per specification.
+                        streamId &= 0x7F_FF_FF_FF;
+                        return true;
+                    }
+                    break;
+                }
+                default:
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        return false;
+    }
+
+    public int getLength()
+    {
+        return length;
+    }
+
+    public int getFrameType()
+    {
+        return type;
+    }
+
+    public boolean hasFlag(int bit)
+    {
+        return (flags & bit) == bit;
+    }
+
+    public int getStreamId()
+    {
+        return streamId;
+    }
+
+    private enum State
+    {
+        LENGTH, TYPE, FLAGS, STREAM_ID, STREAM_ID_BYTES
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeadersBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeadersBodyParser.java
new file mode 100644
index 0000000..f639567
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeadersBodyParser.java
@@ -0,0 +1,240 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PriorityFrame;
+import org.eclipse.jetty.util.BufferUtil;
+
+public class HeadersBodyParser extends BodyParser
+{
+    private final HeaderBlockParser headerBlockParser;
+    private final HeaderBlockFragments headerBlockFragments;
+    private State state = State.PREPARE;
+    private int cursor;
+    private int length;
+    private int paddingLength;
+    private boolean exclusive;
+    private int parentStreamId;
+    private int weight;
+
+    public HeadersBodyParser(HeaderParser headerParser, Parser.Listener listener, HeaderBlockParser headerBlockParser, HeaderBlockFragments headerBlockFragments)
+    {
+        super(headerParser, listener);
+        this.headerBlockParser = headerBlockParser;
+        this.headerBlockFragments = headerBlockFragments;
+    }
+
+    private void reset()
+    {
+        state = State.PREPARE;
+        cursor = 0;
+        length = 0;
+        paddingLength = 0;
+        exclusive = false;
+        parentStreamId = 0;
+        weight = 0;
+    }
+
+    @Override
+    protected void emptyBody(ByteBuffer buffer)
+    {
+        if (hasFlag(Flags.END_HEADERS))
+        {
+            MetaData metaData = headerBlockParser.parse(BufferUtil.EMPTY_BUFFER, 0);
+            onHeaders(0, 0, false, metaData);
+        }
+        else
+        {
+            headerBlockFragments.setStreamId(getStreamId());
+            headerBlockFragments.setEndStream(isEndStream());
+            if (hasFlag(Flags.PRIORITY))
+                connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_headers_priority_frame");
+        }
+    }
+
+    @Override
+    public boolean parse(ByteBuffer buffer)
+    {
+        boolean loop = false;
+        while (buffer.hasRemaining() || loop)
+        {
+            switch (state)
+            {
+                case PREPARE:
+                {
+                    // SPEC: wrong streamId is treated as connection error.
+                    if (getStreamId() == 0)
+                        return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_headers_frame");
+
+                    length = getBodyLength();
+
+                    if (isPadding())
+                    {
+                        state = State.PADDING_LENGTH;
+                    }
+                    else if (hasFlag(Flags.PRIORITY))
+                    {
+                        state = State.EXCLUSIVE;
+                    }
+                    else
+                    {
+                        state = State.HEADERS;
+                    }
+                    break;
+                }
+                case PADDING_LENGTH:
+                {
+                    paddingLength = buffer.get() & 0xFF;
+                    --length;
+                    length -= paddingLength;
+                    state = hasFlag(Flags.PRIORITY) ? State.EXCLUSIVE : State.HEADERS;
+                    loop = length == 0;
+                    if (length < 0)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_headers_frame_padding");
+                    break;
+                }
+                case EXCLUSIVE:
+                {
+                    // We must only peek the first byte and not advance the buffer
+                    // because the 31 least significant bits represent the stream id.
+                    int currByte = buffer.get(buffer.position());
+                    exclusive = (currByte & 0x80) == 0x80;
+                    state = State.PARENT_STREAM_ID;
+                    break;
+                }
+                case PARENT_STREAM_ID:
+                {
+                    if (buffer.remaining() >= 4)
+                    {
+                        parentStreamId = buffer.getInt();
+                        parentStreamId &= 0x7F_FF_FF_FF;
+                        length -= 4;
+                        state = State.WEIGHT;
+                        if (length < 1)
+                            return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_headers_frame");
+                    }
+                    else
+                    {
+                        state = State.PARENT_STREAM_ID_BYTES;
+                        cursor = 4;
+                    }
+                    break;
+                }
+                case PARENT_STREAM_ID_BYTES:
+                {
+                    int currByte = buffer.get() & 0xFF;
+                    --cursor;
+                    parentStreamId += currByte << (8 * cursor);
+                    --length;
+                    if (cursor > 0 && length <= 0)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_headers_frame");
+                    if (cursor == 0)
+                    {
+                        parentStreamId &= 0x7F_FF_FF_FF;
+                        state = State.WEIGHT;
+                        if (length < 1)
+                            return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_headers_frame");
+                    }
+                    break;
+                }
+                case WEIGHT:
+                {
+                    weight = (buffer.get() & 0xFF) + 1;
+                    --length;
+                    state = State.HEADERS;
+                    loop = length == 0;
+                    break;
+                }
+                case HEADERS:
+                {
+                    if (hasFlag(Flags.END_HEADERS))
+                    {
+                        MetaData metaData = headerBlockParser.parse(buffer, length);
+                        if (metaData != null)
+                        {
+                            if (LOG.isDebugEnabled())
+                                LOG.debug("Parsed {} frame hpack from {}", FrameType.HEADERS, buffer);
+                            state = State.PADDING;
+                            loop = paddingLength == 0;
+                            onHeaders(parentStreamId, weight, exclusive, metaData);
+                        }
+                    }
+                    else
+                    {
+                        int remaining = buffer.remaining();
+                        if (remaining < length)
+                        {
+                            headerBlockFragments.storeFragment(buffer, remaining, false);
+                            length -= remaining;
+                        }
+                        else
+                        {
+                            headerBlockFragments.setStreamId(getStreamId());
+                            headerBlockFragments.setEndStream(isEndStream());
+                            if (hasFlag(Flags.PRIORITY))
+                                headerBlockFragments.setPriorityFrame(new PriorityFrame(getStreamId(), parentStreamId, weight, exclusive));
+                            headerBlockFragments.storeFragment(buffer, length, false);
+                            state = State.PADDING;
+                            loop = paddingLength == 0;
+                        }
+                    }
+                    break;
+                }
+                case PADDING:
+                {
+                    int size = Math.min(buffer.remaining(), paddingLength);
+                    buffer.position(buffer.position() + size);
+                    paddingLength -= size;
+                    if (paddingLength == 0)
+                    {
+                        reset();
+                        return true;
+                    }
+                    break;
+                }
+                default:
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        return false;
+    }
+
+    private void onHeaders(int parentStreamId, int weight, boolean exclusive, MetaData metaData)
+    {
+        PriorityFrame priorityFrame = null;
+        if (hasFlag(Flags.PRIORITY))
+            priorityFrame = new PriorityFrame(getStreamId(), parentStreamId, weight, exclusive);
+        HeadersFrame frame = new HeadersFrame(getStreamId(), metaData, priorityFrame, isEndStream());
+        notifyHeaders(frame);
+    }
+
+    private enum State
+    {
+        PREPARE, PADDING_LENGTH, EXCLUSIVE, PARENT_STREAM_ID, PARENT_STREAM_ID_BYTES, WEIGHT, HEADERS, PADDING
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/Parser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/Parser.java
new file mode 100644
index 0000000..cf1d074
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/Parser.java
@@ -0,0 +1,294 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PingFrame;
+import org.eclipse.jetty.http2.frames.PriorityFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.http2.hpack.HpackDecoder;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * <p>The HTTP/2 protocol parser.</p>
+ * <p>This parser makes use of the {@link HeaderParser} and of
+ * {@link BodyParser}s to parse HTTP/2 frames.</p>
+ */
+public class Parser
+{
+    private static final Logger LOG = Log.getLogger(Parser.class);
+
+    private final Listener listener;
+    private final HeaderParser headerParser;
+    private final BodyParser[] bodyParsers;
+    private boolean continuation;
+    private State state = State.HEADER;
+
+    public Parser(ByteBufferPool byteBufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize)
+    {
+        this.listener = listener;
+        this.headerParser = new HeaderParser();
+        this.bodyParsers = new BodyParser[FrameType.values().length];
+
+        HeaderBlockParser headerBlockParser = new HeaderBlockParser(byteBufferPool, new HpackDecoder(maxDynamicTableSize, maxHeaderSize));
+        HeaderBlockFragments headerBlockFragments = new HeaderBlockFragments();
+
+        bodyParsers[FrameType.DATA.getType()] = new DataBodyParser(headerParser, listener);
+        bodyParsers[FrameType.HEADERS.getType()] = new HeadersBodyParser(headerParser, listener, headerBlockParser, headerBlockFragments);
+        bodyParsers[FrameType.PRIORITY.getType()] = new PriorityBodyParser(headerParser, listener);
+        bodyParsers[FrameType.RST_STREAM.getType()] = new ResetBodyParser(headerParser, listener);
+        bodyParsers[FrameType.SETTINGS.getType()] = new SettingsBodyParser(headerParser, listener);
+        bodyParsers[FrameType.PUSH_PROMISE.getType()] = new PushPromiseBodyParser(headerParser, listener, headerBlockParser);
+        bodyParsers[FrameType.PING.getType()] = new PingBodyParser(headerParser, listener);
+        bodyParsers[FrameType.GO_AWAY.getType()] = new GoAwayBodyParser(headerParser, listener);
+        bodyParsers[FrameType.WINDOW_UPDATE.getType()] = new WindowUpdateBodyParser(headerParser, listener);
+        bodyParsers[FrameType.CONTINUATION.getType()] = new ContinuationBodyParser(headerParser, listener, headerBlockParser, headerBlockFragments);
+    }
+
+    private void reset()
+    {
+        headerParser.reset();
+        state = State.HEADER;
+    }
+
+    /**
+     * <p>Parses the given {@code buffer} bytes and emit events to a {@link Listener}.</p>
+     * <p>When this method returns, the buffer may not be fully consumed, so invocations
+     * to this method should be wrapped in a loop:</p>
+     * <pre>
+     * while (buffer.hasRemaining())
+     *     parser.parse(buffer);
+     * </pre>
+     *
+     * @param buffer the buffer to parse
+     */
+    public void parse(ByteBuffer buffer)
+    {
+        try
+        {
+            while (true)
+            {
+                switch (state)
+                {
+                    case HEADER:
+                    {
+                        if (!parseHeader(buffer))
+                            return;
+                        break;
+                    }
+                    case BODY:
+                    {
+                        if (!parseBody(buffer))
+                            return;
+                        break;
+                    }
+                    default:
+                    {
+                        throw new IllegalStateException();
+                    }
+                }
+            }
+        }
+        catch (Throwable x)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug(x);
+            BufferUtil.clear(buffer);
+            notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "parser_error");
+        }
+    }
+
+    protected boolean parseHeader(ByteBuffer buffer)
+    {
+        if (!headerParser.parse(buffer))
+            return false;
+
+        FrameType frameType = FrameType.from(getFrameType());
+        if (LOG.isDebugEnabled())
+            LOG.debug("Parsed {} frame header from {}", frameType, buffer);
+
+        if (continuation)
+        {
+            if (frameType != FrameType.CONTINUATION)
+            {
+                // SPEC: CONTINUATION frames must be consecutive.
+                BufferUtil.clear(buffer);
+                notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "continuation_frame_expected");
+                return false;
+            }
+            if (headerParser.hasFlag(Flags.END_HEADERS))
+            {
+                continuation = false;
+            }
+        }
+        else
+        {
+            if (frameType == FrameType.HEADERS &&
+                    !headerParser.hasFlag(Flags.END_HEADERS))
+            {
+                continuation = true;
+            }
+        }
+        state = State.BODY;
+        return true;
+    }
+
+    protected boolean parseBody(ByteBuffer buffer)
+    {
+        int type = getFrameType();
+        if (type < 0 || type >= bodyParsers.length)
+        {
+            BufferUtil.clear(buffer);
+            notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "unknown_frame_type_" + type);
+            return false;
+        }
+
+        BodyParser bodyParser = bodyParsers[type];
+        if (headerParser.getLength() == 0)
+        {
+            bodyParser.emptyBody(buffer);
+        }
+        else
+        {
+            if (!bodyParser.parse(buffer))
+                return false;
+        }
+        if (LOG.isDebugEnabled())
+            LOG.debug("Parsed {} frame body from {}", FrameType.from(type), buffer);
+        reset();
+        return true;
+    }
+
+    protected int getFrameType()
+    {
+        return headerParser.getFrameType();
+    }
+
+    protected boolean hasFlag(int bit)
+    {
+        return headerParser.hasFlag(bit);
+    }
+
+    protected void notifyConnectionFailure(int error, String reason)
+    {
+        try
+        {
+            listener.onConnectionFailure(error, reason);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    public interface Listener
+    {
+        public void onData(DataFrame frame);
+
+        public void onHeaders(HeadersFrame frame);
+
+        public void onPriority(PriorityFrame frame);
+
+        public void onReset(ResetFrame frame);
+
+        public void onSettings(SettingsFrame frame);
+
+        public void onPushPromise(PushPromiseFrame frame);
+
+        public void onPing(PingFrame frame);
+
+        public void onGoAway(GoAwayFrame frame);
+
+        public void onWindowUpdate(WindowUpdateFrame frame);
+
+        public void onConnectionFailure(int error, String reason);
+
+        public static class Adapter implements Listener
+        {
+            @Override
+            public void onData(DataFrame frame)
+            {
+            }
+
+            @Override
+            public void onHeaders(HeadersFrame frame)
+            {
+            }
+
+            @Override
+            public void onPriority(PriorityFrame frame)
+            {
+            }
+
+            @Override
+            public void onReset(ResetFrame frame)
+            {
+            }
+
+            @Override
+            public void onSettings(SettingsFrame frame)
+            {
+            }
+
+            @Override
+            public void onPushPromise(PushPromiseFrame frame)
+            {
+            }
+
+            @Override
+            public void onPing(PingFrame frame)
+            {
+            }
+
+            @Override
+            public void onGoAway(GoAwayFrame frame)
+            {
+            }
+
+            @Override
+            public void onWindowUpdate(WindowUpdateFrame frame)
+            {
+            }
+
+            @Override
+            public void onConnectionFailure(int error, String reason)
+            {
+                LOG.warn("Connection failure: {}/{}", error, reason);
+            }
+        }
+    }
+
+    private enum State
+    {
+        HEADER, BODY
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PingBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PingBodyParser.java
new file mode 100644
index 0000000..8bcb188
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PingBodyParser.java
@@ -0,0 +1,107 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.PingFrame;
+
+public class PingBodyParser extends BodyParser
+{
+    private State state = State.PREPARE;
+    private int cursor;
+    private byte[] payload;
+
+    public PingBodyParser(HeaderParser headerParser, Parser.Listener listener)
+    {
+        super(headerParser, listener);
+    }
+
+    private void reset()
+    {
+        state = State.PREPARE;
+        cursor = 0;
+        payload = null;
+    }
+
+    @Override
+    public boolean parse(ByteBuffer buffer)
+    {
+        while (buffer.hasRemaining())
+        {
+            switch (state)
+            {
+                case PREPARE:
+                {
+                    // SPEC: wrong streamId is treated as connection error.
+                    if (getStreamId() != 0)
+                        return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_ping_frame");
+                    // SPEC: wrong body length is treated as connection error.
+                    if (getBodyLength() != 8)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_ping_frame");
+                    state = State.PAYLOAD;
+                    break;
+                }
+                case PAYLOAD:
+                {
+                    payload = new byte[8];
+                    if (buffer.remaining() >= 8)
+                    {
+                        buffer.get(payload);
+                        return onPing(payload);
+                    }
+                    else
+                    {
+                        state = State.PAYLOAD_BYTES;
+                        cursor = 8;
+                    }
+                    break;
+                }
+                case PAYLOAD_BYTES:
+                {
+                    payload[8 - cursor] = buffer.get();
+                    --cursor;
+                    if (cursor == 0)
+                        return onPing(payload);
+                    break;
+                }
+                default:
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean onPing(byte[] payload)
+    {
+        PingFrame frame = new PingFrame(payload, hasFlag(Flags.ACK));
+        reset();
+        notifyPing(frame);
+        return true;
+    }
+
+    private enum State
+    {
+        PREPARE, PAYLOAD, PAYLOAD_BYTES
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PrefaceParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PrefaceParser.java
new file mode 100644
index 0000000..b367edf
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PrefaceParser.java
@@ -0,0 +1,88 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.frames.PrefaceFrame;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class PrefaceParser
+{
+    private static final Logger LOG = Log.getLogger(PrefaceParser.class);
+
+    private final Parser.Listener listener;
+    private int cursor;
+
+    public PrefaceParser(Parser.Listener listener)
+    {
+        this.listener = listener;
+    }
+
+    /**
+     * <p>Advances this parser after the {@link PrefaceFrame#PREFACE_PREAMBLE_BYTES}.</p>
+     * <p>This allows the HTTP/1.1 parser to parse the preamble of the preface,
+     * which is a legal HTTP/1.1 request, and this parser will parse the remaining
+     * bytes, that are not parseable by a HTTP/1.1 parser.</p>
+     */
+    protected void directUpgrade()
+    {
+        if (cursor != 0)
+            throw new IllegalStateException();
+        cursor = PrefaceFrame.PREFACE_PREAMBLE_BYTES.length;
+    }
+
+    public boolean parse(ByteBuffer buffer)
+    {
+        while (buffer.hasRemaining())
+        {
+            int currByte = buffer.get();
+            if (currByte != PrefaceFrame.PREFACE_BYTES[cursor])
+            {
+                BufferUtil.clear(buffer);
+                notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "invalid_preface");
+                return false;
+            }
+            ++cursor;
+            if (cursor == PrefaceFrame.PREFACE_BYTES.length)
+            {
+                cursor = 0;
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Parsed preface bytes from {}", buffer);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    protected void notifyConnectionFailure(int error, String reason)
+    {
+        try
+        {
+            listener.onConnectionFailure(error, reason);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PriorityBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PriorityBodyParser.java
new file mode 100644
index 0000000..a1a627a
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PriorityBodyParser.java
@@ -0,0 +1,130 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.frames.PriorityFrame;
+
+public class PriorityBodyParser extends BodyParser
+{
+    private State state = State.PREPARE;
+    private int cursor;
+    private boolean exclusive;
+    private int parentStreamId;
+
+    public PriorityBodyParser(HeaderParser headerParser, Parser.Listener listener)
+    {
+        super(headerParser, listener);
+    }
+
+    private void reset()
+    {
+        state = State.PREPARE;
+        cursor = 0;
+        exclusive = false;
+        parentStreamId = 0;
+    }
+
+    @Override
+    public boolean parse(ByteBuffer buffer)
+    {
+        while (buffer.hasRemaining())
+        {
+            switch (state)
+            {
+                case PREPARE:
+                {
+                    // SPEC: wrong streamId is treated as connection error.
+                    if (getStreamId() == 0)
+                        return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_priority_frame");
+                    int length = getBodyLength();
+                    if (length != 5)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_priority_frame");
+                    state = State.EXCLUSIVE;
+                    break;
+                }
+                case EXCLUSIVE:
+                {
+                    // We must only peek the first byte and not advance the buffer
+                    // because the 31 least significant bits represent the stream id.
+                    int currByte = buffer.get(buffer.position());
+                    exclusive = (currByte & 0x80) == 0x80;
+                    state = State.PARENT_STREAM_ID;
+                    break;
+                }
+                case PARENT_STREAM_ID:
+                {
+                    if (buffer.remaining() >= 4)
+                    {
+                        parentStreamId = buffer.getInt();
+                        parentStreamId &= 0x7F_FF_FF_FF;
+                        state = State.WEIGHT;
+                    }
+                    else
+                    {
+                        state = State.PARENT_STREAM_ID_BYTES;
+                        cursor = 4;
+                    }
+                    break;
+                }
+                case PARENT_STREAM_ID_BYTES:
+                {
+                    int currByte = buffer.get() & 0xFF;
+                    --cursor;
+                    parentStreamId += currByte << (8 * cursor);
+                    if (cursor == 0)
+                    {
+                        parentStreamId &= 0x7F_FF_FF_FF;
+                        state = State.WEIGHT;
+                    }
+                    break;
+                }
+                case WEIGHT:
+                {
+                    // SPEC: stream cannot depend on itself.
+                    if (getStreamId() == parentStreamId)
+                        return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_priority_frame");
+
+                    int weight = (buffer.get() & 0xFF) + 1;
+                    return onPriority(parentStreamId, weight, exclusive);
+                }
+                default:
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean onPriority(int parentStreamId, int weight, boolean exclusive)
+    {
+        PriorityFrame frame = new PriorityFrame(getStreamId(), parentStreamId, weight, exclusive);
+        reset();
+        notifyPriority(frame);
+        return true;
+    }
+
+    private enum State
+    {
+        PREPARE, EXCLUSIVE, PARENT_STREAM_ID, PARENT_STREAM_ID_BYTES, WEIGHT
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PushPromiseBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PushPromiseBodyParser.java
new file mode 100644
index 0000000..c5375b4
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PushPromiseBodyParser.java
@@ -0,0 +1,167 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+
+public class PushPromiseBodyParser extends BodyParser
+{
+    private final HeaderBlockParser headerBlockParser;
+    private State state = State.PREPARE;
+    private int cursor;
+    private int length;
+    private int paddingLength;
+    private int streamId;
+
+    public PushPromiseBodyParser(HeaderParser headerParser, Parser.Listener listener, HeaderBlockParser headerBlockParser)
+    {
+        super(headerParser, listener);
+        this.headerBlockParser = headerBlockParser;
+    }
+
+    private void reset()
+    {
+        state = State.PREPARE;
+        cursor = 0;
+        length = 0;
+        paddingLength = 0;
+        streamId = 0;
+    }
+
+    @Override
+    public boolean parse(ByteBuffer buffer)
+    {
+        boolean loop = false;
+        while (buffer.hasRemaining() || loop)
+        {
+            switch (state)
+            {
+                case PREPARE:
+                {
+                    // SPEC: wrong streamId is treated as connection error.
+                    if (getStreamId() == 0)
+                        return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_push_promise_frame");
+
+                    // For now we don't support PUSH_PROMISE frames that don't have END_HEADERS.
+                    if (!hasFlag(Flags.END_HEADERS))
+                        return connectionFailure(buffer, ErrorCode.INTERNAL_ERROR.code, "unsupported_push_promise_frame");
+
+                    length = getBodyLength();
+
+                    if (isPadding())
+                    {
+                        state = State.PADDING_LENGTH;
+                    }
+                    else
+                    {
+                        state = State.STREAM_ID;
+                    }
+                    break;
+                }
+                case PADDING_LENGTH:
+                {
+                    paddingLength = buffer.get() & 0xFF;
+                    --length;
+                    length -= paddingLength;
+                    state = State.STREAM_ID;
+                    if (length < 4)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_push_promise_frame");
+                    break;
+                }
+                case STREAM_ID:
+                {
+                    if (buffer.remaining() >= 4)
+                    {
+                        streamId = buffer.getInt();
+                        streamId &= 0x7F_FF_FF_FF;
+                        length -= 4;
+                        state = State.HEADERS;
+                        loop = length == 0;
+                    }
+                    else
+                    {
+                        state = State.STREAM_ID_BYTES;
+                        cursor = 4;
+                    }
+                    break;
+                }
+                case STREAM_ID_BYTES:
+                {
+                    int currByte = buffer.get() & 0xFF;
+                    --cursor;
+                    streamId += currByte << (8 * cursor);
+                    --length;
+                    if (cursor > 0 && length <= 0)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_push_promise_frame");
+                    if (cursor == 0)
+                    {
+                        streamId &= 0x7F_FF_FF_FF;
+                        state = State.HEADERS;
+                        loop = length == 0;
+                    }
+                    break;
+                }
+                case HEADERS:
+                {
+                    MetaData metaData = headerBlockParser.parse(buffer, length);
+                    if (metaData != null)
+                    {
+                        state = State.PADDING;
+                        loop = paddingLength == 0;
+                        onPushPromise(streamId, metaData);
+                    }
+                    break;
+                }
+                case PADDING:
+                {
+                    int size = Math.min(buffer.remaining(), paddingLength);
+                    buffer.position(buffer.position() + size);
+                    paddingLength -= size;
+                    if (paddingLength == 0)
+                    {
+                        reset();
+                        return true;
+                    }
+                    break;
+                }
+                default:
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        return false;
+    }
+
+    private void onPushPromise(int streamId, MetaData metaData)
+    {
+        PushPromiseFrame frame = new PushPromiseFrame(getStreamId(), streamId, metaData);
+        notifyPushPromise(frame);
+    }
+
+    private enum State
+    {
+        PREPARE, PADDING_LENGTH, STREAM_ID, STREAM_ID_BYTES, HEADERS, PADDING
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ResetBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ResetBodyParser.java
new file mode 100644
index 0000000..0fab4dc
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ResetBodyParser.java
@@ -0,0 +1,105 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+
+public class ResetBodyParser extends BodyParser
+{
+    private State state = State.PREPARE;
+    private int cursor;
+    private int error;
+
+    public ResetBodyParser(HeaderParser headerParser, Parser.Listener listener)
+    {
+        super(headerParser, listener);
+    }
+
+    private void reset()
+    {
+        state = State.PREPARE;
+        cursor = 0;
+        error = 0;
+    }
+
+    @Override
+    public boolean parse(ByteBuffer buffer)
+    {
+        while (buffer.hasRemaining())
+        {
+            switch (state)
+            {
+                case PREPARE:
+                {
+                    // SPEC: wrong streamId is treated as connection error.
+                    if (getStreamId() == 0)
+                        return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_rst_stream_frame");
+                    int length = getBodyLength();
+                    if (length != 4)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_rst_stream_frame");
+                    state = State.ERROR;
+                    break;
+                }
+                case ERROR:
+                {
+                    if (buffer.remaining() >= 4)
+                    {
+                        return onReset(buffer.getInt());
+                    }
+                    else
+                    {
+                        state = State.ERROR_BYTES;
+                        cursor = 4;
+                    }
+                    break;
+                }
+                case ERROR_BYTES:
+                {
+                    int currByte = buffer.get() & 0xFF;
+                    --cursor;
+                    error += currByte << (8 * cursor);
+                    if (cursor == 0)
+                        return onReset(error);
+                    break;
+                }
+                default:
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean onReset(int error)
+    {
+        ResetFrame frame = new ResetFrame(getStreamId(), error);
+        reset();
+        notifyReset(frame);
+        return true;
+    }
+
+    private enum State
+    {
+        PREPARE, ERROR, ERROR_BYTES
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ServerParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ServerParser.java
new file mode 100644
index 0000000..d6307d7
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ServerParser.java
@@ -0,0 +1,167 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class ServerParser extends Parser
+{
+    private static final Logger LOG = Log.getLogger(ServerParser.class);
+
+    private final Listener listener;
+    private final PrefaceParser prefaceParser;
+    private State state = State.PREFACE;
+    private boolean notifyPreface = true;
+
+    public ServerParser(ByteBufferPool byteBufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize)
+    {
+        super(byteBufferPool, listener, maxDynamicTableSize, maxHeaderSize);
+        this.listener = listener;
+        this.prefaceParser = new PrefaceParser(listener);
+    }
+
+    /**
+     * <p>A direct upgrade is an unofficial upgrade from HTTP/1.1 to HTTP/2.0.</p>
+     * <p>A direct upgrade is initiated when {@code org.eclipse.jetty.server.HttpConnection}
+     * sees a request with these bytes:</p>
+     * <pre>
+     * PRI * HTTP/2.0\r\n
+     * \r\n
+     * </pre>
+     * <p>This request is part of the HTTP/2.0 preface, indicating that a
+     * HTTP/2.0 client is attempting a h2c direct connection.</p>
+     * <p>This is not a standard HTTP/1.1 Upgrade path.</p>
+     */
+    public void directUpgrade()
+    {
+        if (state != State.PREFACE)
+            throw new IllegalStateException();
+        prefaceParser.directUpgrade();
+    }
+
+    /**
+     * <p>The standard HTTP/1.1 upgrade path.</p>
+     */
+    public void standardUpgrade()
+    {
+        if (state != State.PREFACE)
+            throw new IllegalStateException();
+        notifyPreface = false;
+    }
+
+    @Override
+    public void parse(ByteBuffer buffer)
+    {
+        try
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Parsing {}", buffer);
+
+            while (true)
+            {
+                switch (state)
+                {
+                    case PREFACE:
+                    {
+                        if (!prefaceParser.parse(buffer))
+                            return;
+                        if (notifyPreface)
+                            onPreface();
+                        state = State.SETTINGS;
+                        break;
+                    }
+                    case SETTINGS:
+                    {
+                        if (!parseHeader(buffer))
+                            return;
+                        if (getFrameType() != FrameType.SETTINGS.getType() || hasFlag(Flags.ACK))
+                        {
+                            BufferUtil.clear(buffer);
+                            notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "invalid_preface");
+                            return;
+                        }
+                        if (!parseBody(buffer))
+                            return;
+                        state = State.FRAMES;
+                        break;
+                    }
+                    case FRAMES:
+                    {
+                        // Stay forever in the FRAMES state.
+                        super.parse(buffer);
+                        return;
+                    }
+                    default:
+                    {
+                        throw new IllegalStateException();
+                    }
+                }
+            }
+        }
+        catch (Throwable x)
+        {
+            LOG.debug(x);
+            BufferUtil.clear(buffer);
+            notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "parser_error");
+        }
+    }
+
+    protected void onPreface()
+    {
+        notifyPreface();
+    }
+
+    private void notifyPreface()
+    {
+        try
+        {
+            listener.onPreface();
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    public interface Listener extends Parser.Listener
+    {
+        public void onPreface();
+
+        public static class Adapter extends Parser.Listener.Adapter implements Listener
+        {
+            @Override
+            public void onPreface()
+            {
+            }
+        }
+    }
+
+    private enum State
+    {
+        PREFACE, SETTINGS, FRAMES
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java
new file mode 100644
index 0000000..26ad059
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java
@@ -0,0 +1,212 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class SettingsBodyParser extends BodyParser
+{
+    private static final Logger LOG = Log.getLogger(SettingsBodyParser.class);
+    private State state = State.PREPARE;
+    private int cursor;
+    private int length;
+    private int settingId;
+    private int settingValue;
+    private Map<Integer, Integer> settings;
+
+    public SettingsBodyParser(HeaderParser headerParser, Parser.Listener listener)
+    {
+        super(headerParser, listener);
+    }
+
+    protected void reset()
+    {
+        state = State.PREPARE;
+        cursor = 0;
+        length = 0;
+        settingId = 0;
+        settingValue = 0;
+        settings = null;
+    }
+
+    @Override
+    protected void emptyBody(ByteBuffer buffer)
+    {
+        onSettings(new HashMap<>());
+    }
+
+    @Override
+    public boolean parse(ByteBuffer buffer)
+    {
+        while (buffer.hasRemaining())
+        {
+            switch (state)
+            {
+                case PREPARE:
+                {
+                    // SPEC: wrong streamId is treated as connection error.
+                    if (getStreamId() != 0)
+                        return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_frame");
+                    length = getBodyLength();
+                    settings = new HashMap<>();
+                    state = State.SETTING_ID;
+                    break;
+                }
+                case SETTING_ID:
+                {
+                    if (buffer.remaining() >= 2)
+                    {
+                        settingId = buffer.getShort() & 0xFF_FF;
+                        state = State.SETTING_VALUE;
+                        length -= 2;
+                        if (length <= 0)
+                            return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_settings_frame");
+                    }
+                    else
+                    {
+                        cursor = 2;
+                        settingId = 0;
+                        state = State.SETTING_ID_BYTES;
+                    }
+                    break;
+                }
+                case SETTING_ID_BYTES:
+                {
+                    int currByte = buffer.get() & 0xFF;
+                    --cursor;
+                    settingId += currByte << (8 * cursor);
+                    --length;
+                    if (length <= 0)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_settings_frame");
+                    if (cursor == 0)
+                    {
+                        state = State.SETTING_VALUE;
+                    }
+                    break;
+                }
+                case SETTING_VALUE:
+                {
+                    if (buffer.remaining() >= 4)
+                    {
+                        settingValue = buffer.getInt();
+                        if (LOG.isDebugEnabled())
+                            LOG.debug(String.format("setting %d=%d",settingId, settingValue));
+                        settings.put(settingId, settingValue);
+                        state = State.SETTING_ID;
+                        length -= 4;
+                        if (length == 0)
+                            return onSettings(settings);
+                    }
+                    else
+                    {
+                        cursor = 4;
+                        settingValue = 0;
+                        state = State.SETTING_VALUE_BYTES;
+                    }
+                    break;
+                }
+                case SETTING_VALUE_BYTES:
+                {
+                    int currByte = buffer.get() & 0xFF;
+                    --cursor;
+                    settingValue += currByte << (8 * cursor);
+                    --length;
+                    if (cursor > 0 && length <= 0)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_settings_frame");
+                    if (cursor == 0)
+                    {
+                        if (LOG.isDebugEnabled())
+                            LOG.debug(String.format("setting %d=%d",settingId, settingValue));
+                        settings.put(settingId, settingValue);
+                        state = State.SETTING_ID;
+                        if (length == 0)
+                            return onSettings(settings);
+                    }
+                    break;
+                }
+                default:
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        return false;
+    }
+
+    protected boolean onSettings(Map<Integer, Integer> settings)
+    {
+        SettingsFrame frame = new SettingsFrame(settings, hasFlag(Flags.ACK));
+        reset();
+        notifySettings(frame);
+        return true;
+    }
+
+    public static SettingsFrame parseBody(final ByteBuffer buffer)
+    {
+        final int bodyLength = buffer.remaining();
+        final AtomicReference<SettingsFrame> frameRef = new AtomicReference<>();
+        SettingsBodyParser parser = new SettingsBodyParser(null, null)
+        {
+            @Override
+            protected int getStreamId()
+            {
+                return 0;
+            }
+
+            @Override
+            protected int getBodyLength()
+            {
+                return bodyLength;
+            }
+
+            @Override
+            protected boolean onSettings(Map<Integer, Integer> settings)
+            {
+                frameRef.set(new SettingsFrame(settings, false));
+                return true;
+            }
+
+            @Override
+            protected boolean connectionFailure(ByteBuffer buffer, int error, String reason)
+            {
+                frameRef.set(null);
+                return false;
+            }
+        };
+        if (bodyLength == 0)
+            parser.emptyBody(buffer);
+        else
+            parser.parse(buffer);
+        return frameRef.get();
+    }
+
+    private enum State
+    {
+        PREPARE, SETTING_ID, SETTING_ID_BYTES, SETTING_VALUE, SETTING_VALUE_BYTES
+    }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/WindowUpdateBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/WindowUpdateBodyParser.java
new file mode 100644
index 0000000..9c9f68b
--- /dev/null
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/WindowUpdateBodyParser.java
@@ -0,0 +1,106 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.parser;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+
+public class WindowUpdateBodyParser extends BodyParser
+{
+    private State state = State.PREPARE;
+    private int cursor;
+    private int windowDelta;
+
+    public WindowUpdateBodyParser(HeaderParser headerParser, Parser.Listener listener)
+    {
+        super(headerParser, listener);
+    }
+
+    private void reset()
+    {
+        state = State.PREPARE;
+        cursor = 0;
+        windowDelta = 0;
+    }
+
+    @Override
+    public boolean parse(ByteBuffer buffer)
+    {
+        while (buffer.hasRemaining())
+        {
+            switch (state)
+            {
+                case PREPARE:
+                {
+                    int length = getBodyLength();
+                    if (length != 4)
+                        return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_window_update_frame");
+                    state = State.WINDOW_DELTA;
+                    break;
+                }
+                case WINDOW_DELTA:
+                {
+                    if (buffer.remaining() >= 4)
+                    {
+                        windowDelta = buffer.getInt() & 0x7F_FF_FF_FF;
+                        return onWindowUpdate(windowDelta);
+                    }
+                    else
+                    {
+                        state = State.WINDOW_DELTA_BYTES;
+                        cursor = 4;
+                    }
+                    break;
+                }
+                case WINDOW_DELTA_BYTES:
+                {
+                    byte currByte = buffer.get();
+                    --cursor;
+                    windowDelta += (currByte & 0xFF) << 8 * cursor;
+                    if (cursor == 0)
+                    {
+                        windowDelta &= 0x7F_FF_FF_FF;
+                        return onWindowUpdate(windowDelta);
+                    }
+                    break;
+                }
+                default:
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean onWindowUpdate(int windowDelta)
+    {
+        WindowUpdateFrame frame = new WindowUpdateFrame(getStreamId(), windowDelta);
+        reset();
+        notifyWindowUpdate(frame);
+        return true;
+    }
+
+    private enum State
+    {
+        PREPARE, WINDOW_DELTA, WINDOW_DELTA_BYTES
+    }
+}
diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/api/UsageTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/api/UsageTest.java
new file mode 100644
index 0000000..3a86678
--- /dev/null
+++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/api/UsageTest.java
@@ -0,0 +1,60 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.api;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class UsageTest
+{
+    @Ignore
+    @Test
+    public void test() throws Exception
+    {
+//        HTTP2Client client = new HTTP2Client();
+//        client.connect("localhost", 8080, new Promise.Adapter<Session>()
+//        {
+//            @Override
+//            public void succeeded(Session session)
+//            {
+//                session.newStream(new HeadersFrame(info, null, true), new Stream.Listener.Adapter()
+//                {
+//                    @Override
+//                    public void onData(Stream stream, DataFrame frame)
+//                    {
+//                        System.out.println("received frame = " + frame);
+//                    }
+//                }, new Adapter<Stream>()
+//                {
+//                    @Override
+//                    public void succeeded(Stream stream)
+//                    {
+//                        DataFrame frame = new DataFrame(stream.getId(), ByteBuffer.wrap("HELLO".getBytes(StandardCharsets.UTF_8)), true);
+//                        stream.data(frame, new Callback.Adapter());
+//                    }
+//                });
+//            }
+//        });
+
+        // KINDA CALLBACK HELL ABOVE.
+        // BELOW USING COMPLETABLES:
+
+//        client.connect("localhost", 8080).then(session -> session.newStream(...)).then(stream -> stream.data(...));
+    }
+}
diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java
new file mode 100644
index 0000000..4409795
--- /dev/null
+++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java
@@ -0,0 +1,158 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.generator.HeaderGenerator;
+import org.eclipse.jetty.http2.generator.HeadersGenerator;
+import org.eclipse.jetty.http2.hpack.HpackEncoder;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ContinuationParseTest
+{
+    @Test
+    public void testParseOneByteAtATime() throws Exception
+    {
+        ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+        HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder());
+
+        final List<HeadersFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(HeadersFrame frame)
+            {
+                frames.add(frame);
+            }
+
+            @Override
+            public void onConnectionFailure(int error, String reason)
+            {
+                frames.add(new HeadersFrame(null, null, false));
+            }
+        }, 4096, 8192);
+
+        // Iterate a few times to be sure the parser is properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            int streamId = 13;
+            HttpFields fields = new HttpFields();
+            fields.put("Accept", "text/html");
+            fields.put("User-Agent", "Jetty");
+            MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields);
+
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generateHeaders(lease, streamId, metaData, null, true);
+
+            List<ByteBuffer> byteBuffers = lease.getByteBuffers();
+            Assert.assertEquals(2, byteBuffers.size());
+
+            ByteBuffer headersBody = byteBuffers.remove(1);
+            int start = headersBody.position();
+            int length = headersBody.remaining();
+            int oneThird = length / 3;
+            int lastThird = length - 2 * oneThird;
+
+            // Adjust the length of the HEADERS frame.
+            ByteBuffer headersHeader = byteBuffers.get(0);
+            headersHeader.put(0, (byte)((oneThird >>> 16) & 0xFF));
+            headersHeader.put(1, (byte)((oneThird >>> 8) & 0xFF));
+            headersHeader.put(2, (byte)(oneThird & 0xFF));
+
+            // Remove the END_HEADERS flag from the HEADERS header.
+            headersHeader.put(4, (byte)(headersHeader.get(4) & ~Flags.END_HEADERS));
+
+            // New HEADERS body.
+            headersBody.position(start);
+            headersBody.limit(start + oneThird);
+            byteBuffers.add(headersBody.slice());
+
+            // Split the rest of the HEADERS body into CONTINUATION frames.
+            // First CONTINUATION header.
+            byte[] continuationHeader1 = new byte[9];
+            continuationHeader1[0] = (byte)((oneThird >>> 16) & 0xFF);
+            continuationHeader1[1] = (byte)((oneThird >>> 8) & 0xFF);
+            continuationHeader1[2] = (byte)(oneThird & 0xFF);
+            continuationHeader1[3] = (byte)FrameType.CONTINUATION.getType();
+            continuationHeader1[4] = Flags.NONE;
+            continuationHeader1[5] = 0x00;
+            continuationHeader1[6] = 0x00;
+            continuationHeader1[7] = 0x00;
+            continuationHeader1[8] = (byte)streamId;
+            byteBuffers.add(ByteBuffer.wrap(continuationHeader1));
+            // First CONTINUATION body.
+            headersBody.position(start + oneThird);
+            headersBody.limit(start + 2 * oneThird);
+            byteBuffers.add(headersBody.slice());
+            // Second CONTINUATION header.
+            byte[] continuationHeader2 = new byte[9];
+            continuationHeader2[0] = (byte)((lastThird >>> 16) & 0xFF);
+            continuationHeader2[1] = (byte)((lastThird >>> 8) & 0xFF);
+            continuationHeader2[2] = (byte)(lastThird & 0xFF);
+            continuationHeader2[3] = (byte)FrameType.CONTINUATION.getType();
+            continuationHeader2[4] = Flags.END_HEADERS;
+            continuationHeader2[5] = 0x00;
+            continuationHeader2[6] = 0x00;
+            continuationHeader2[7] = 0x00;
+            continuationHeader2[8] = (byte)streamId;
+            byteBuffers.add(ByteBuffer.wrap(continuationHeader2));
+            headersBody.position(start + 2 * oneThird);
+            headersBody.limit(start + length);
+            byteBuffers.add(headersBody.slice());
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
+                }
+            }
+
+            Assert.assertEquals(1, frames.size());
+            HeadersFrame frame = frames.get(0);
+            Assert.assertEquals(streamId, frame.getStreamId());
+            Assert.assertTrue(frame.isEndStream());
+            MetaData.Request request = (MetaData.Request)frame.getMetaData();
+            Assert.assertEquals(metaData.getMethod(), request.getMethod());
+            Assert.assertEquals(metaData.getURI(), request.getURI());
+            for (int j = 0; j < fields.size(); ++j)
+            {
+                HttpField field = fields.getField(j);
+                Assert.assertTrue(request.getFields().contains(field));
+            }
+            PriorityFrame priority = frame.getPriority();
+            Assert.assertNull(priority);
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/DataGenerateParseTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/DataGenerateParseTest.java
new file mode 100644
index 0000000..7017038
--- /dev/null
+++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/DataGenerateParseTest.java
@@ -0,0 +1,152 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.jetty.http2.generator.DataGenerator;
+import org.eclipse.jetty.http2.generator.HeaderGenerator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class DataGenerateParseTest
+{
+    private final byte[] smallContent = new byte[128];
+    private final byte[] largeContent = new byte[128 * 1024];
+    private final ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+
+    public DataGenerateParseTest()
+    {
+        Random random = new Random();
+        random.nextBytes(smallContent);
+        random.nextBytes(largeContent);
+    }
+
+    @Test
+    public void testGenerateParseNoContentNoPadding()
+    {
+        testGenerateParseContent(BufferUtil.EMPTY_BUFFER);
+    }
+
+    @Test
+    public void testGenerateParseSmallContentNoPadding()
+    {
+        testGenerateParseContent(ByteBuffer.wrap(smallContent));
+    }
+
+    private void testGenerateParseContent(ByteBuffer content)
+    {
+        List<DataFrame> frames = testGenerateParse(content);
+        Assert.assertEquals(1, frames.size());
+        DataFrame frame = frames.get(0);
+        Assert.assertTrue(frame.getStreamId() != 0);
+        Assert.assertTrue(frame.isEndStream());
+        Assert.assertEquals(content, frame.getData());
+    }
+
+    @Test
+    public  void testGenerateParseLargeContent()
+    {
+        ByteBuffer content = ByteBuffer.wrap(largeContent);
+        List<DataFrame> frames = testGenerateParse(content);
+        Assert.assertEquals(8, frames.size());
+        ByteBuffer aggregate = ByteBuffer.allocate(content.remaining());
+        for (int i = 1; i <= frames.size(); ++i)
+        {
+            DataFrame frame = frames.get(i - 1);
+            Assert.assertTrue(frame.getStreamId() != 0);
+            Assert.assertEquals(i == frames.size(), frame.isEndStream());
+            aggregate.put(frame.getData());
+        }
+        aggregate.flip();
+        Assert.assertEquals(content, aggregate);
+    }
+
+    private List<DataFrame> testGenerateParse(ByteBuffer data)
+    {
+        DataGenerator generator = new DataGenerator(new HeaderGenerator());
+
+        final List<DataFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onData(DataFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generateData(lease, 13, data.slice(), true, data.remaining());
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                parser.parse(buffer);
+            }
+        }
+
+        return frames;
+    }
+
+    @Test
+    public void testGenerateParseOneByteAtATime()
+    {
+        DataGenerator generator = new DataGenerator(new HeaderGenerator());
+
+        final List<DataFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onData(DataFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            ByteBuffer data = ByteBuffer.wrap(largeContent);
+            generator.generateData(lease, 13, data.slice(), true, data.remaining());
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
+                }
+            }
+
+            Assert.assertEquals(largeContent.length, frames.size());
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/GoAwayGenerateParseTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/GoAwayGenerateParseTest.java
new file mode 100644
index 0000000..c5655dd
--- /dev/null
+++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/GoAwayGenerateParseTest.java
@@ -0,0 +1,121 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.jetty.http2.generator.GoAwayGenerator;
+import org.eclipse.jetty.http2.generator.HeaderGenerator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class GoAwayGenerateParseTest
+{
+    private final ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+
+    @Test
+    public void testGenerateParse() throws Exception
+    {
+        GoAwayGenerator generator = new GoAwayGenerator(new HeaderGenerator());
+
+        final List<GoAwayFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onGoAway(GoAwayFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        int lastStreamId = 13;
+        int error = 17;
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generateGoAway(lease, lastStreamId, error, null);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(buffer);
+                }
+            }
+        }
+
+        Assert.assertEquals(1, frames.size());
+        GoAwayFrame frame = frames.get(0);
+        Assert.assertEquals(lastStreamId, frame.getLastStreamId());
+        Assert.assertEquals(error, frame.getError());
+        Assert.assertNull(frame.getPayload());
+    }
+
+    @Test
+    public void testGenerateParseOneByteAtATime() throws Exception
+    {
+        GoAwayGenerator generator = new GoAwayGenerator(new HeaderGenerator());
+
+        final List<GoAwayFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onGoAway(GoAwayFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        int lastStreamId = 13;
+        int error = 17;
+        byte[] payload = new byte[16];
+        new Random().nextBytes(payload);
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generateGoAway(lease, lastStreamId, error, payload);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
+                }
+            }
+
+            Assert.assertEquals(1, frames.size());
+            GoAwayFrame frame = frames.get(0);
+            Assert.assertEquals(lastStreamId, frame.getLastStreamId());
+            Assert.assertEquals(error, frame.getError());
+            Assert.assertArrayEquals(payload, frame.getPayload());
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/HeadersGenerateParseTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/HeadersGenerateParseTest.java
new file mode 100644
index 0000000..0f5c68c
--- /dev/null
+++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/HeadersGenerateParseTest.java
@@ -0,0 +1,160 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.generator.HeaderGenerator;
+import org.eclipse.jetty.http2.generator.HeadersGenerator;
+import org.eclipse.jetty.http2.hpack.HpackEncoder;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HeadersGenerateParseTest
+{
+    private final ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+
+    @Test
+    public void testGenerateParse() throws Exception
+    {
+        HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder());
+
+        int streamId = 13;
+        HttpFields fields = new HttpFields();
+        fields.put("Accept", "text/html");
+        fields.put("User-Agent", "Jetty");
+        MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields);
+
+        final List<HeadersFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(HeadersFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            PriorityFrame priorityFrame = new PriorityFrame(streamId, 3 * streamId, 200, true);
+            generator.generateHeaders(lease, streamId, metaData, priorityFrame, true);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(buffer);
+                }
+            }
+
+            Assert.assertEquals(1, frames.size());
+            HeadersFrame frame = frames.get(0);
+            Assert.assertEquals(streamId, frame.getStreamId());
+            Assert.assertTrue(frame.isEndStream());
+            MetaData.Request request = (MetaData.Request)frame.getMetaData();
+            Assert.assertEquals(metaData.getMethod(), request.getMethod());
+            Assert.assertEquals(metaData.getURI(), request.getURI());
+            for (int j = 0; j < fields.size(); ++j)
+            {
+                HttpField field = fields.getField(j);
+                Assert.assertTrue(request.getFields().contains(field));
+            }
+            PriorityFrame priority = frame.getPriority();
+            Assert.assertNotNull(priority);
+            Assert.assertEquals(priorityFrame.getStreamId(), priority.getStreamId());
+            Assert.assertEquals(priorityFrame.getParentStreamId(), priority.getParentStreamId());
+            Assert.assertEquals(priorityFrame.getWeight(), priority.getWeight());
+            Assert.assertEquals(priorityFrame.isExclusive(), priority.isExclusive());
+        }
+    }
+
+    @Test
+    public void testGenerateParseOneByteAtATime() throws Exception
+    {
+        HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder());
+
+        final List<HeadersFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(HeadersFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            int streamId = 13;
+            HttpFields fields = new HttpFields();
+            fields.put("Accept", "text/html");
+            fields.put("User-Agent", "Jetty");
+            MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields);
+
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            PriorityFrame priorityFrame = new PriorityFrame(streamId, 3 * streamId, 200, true);
+            generator.generateHeaders(lease, streamId, metaData, priorityFrame, true);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                buffer = buffer.slice();
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
+                }
+            }
+
+            Assert.assertEquals(1, frames.size());
+            HeadersFrame frame = frames.get(0);
+            Assert.assertEquals(streamId, frame.getStreamId());
+            Assert.assertTrue(frame.isEndStream());
+            MetaData.Request request = (MetaData.Request)frame.getMetaData();
+            Assert.assertEquals(metaData.getMethod(), request.getMethod());
+            Assert.assertEquals(metaData.getURI(), request.getURI());
+            for (int j = 0; j < fields.size(); ++j)
+            {
+                HttpField field = fields.getField(j);
+                Assert.assertTrue(request.getFields().contains(field));
+            }
+            PriorityFrame priority = frame.getPriority();
+            Assert.assertNotNull(priority);
+            Assert.assertEquals(priorityFrame.getStreamId(), priority.getStreamId());
+            Assert.assertEquals(priorityFrame.getParentStreamId(), priority.getParentStreamId());
+            Assert.assertEquals(priorityFrame.getWeight(), priority.getWeight());
+            Assert.assertEquals(priorityFrame.isExclusive(), priority.isExclusive());
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/PingGenerateParseTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/PingGenerateParseTest.java
new file mode 100644
index 0000000..189a278
--- /dev/null
+++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/PingGenerateParseTest.java
@@ -0,0 +1,150 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.jetty.http2.generator.HeaderGenerator;
+import org.eclipse.jetty.http2.generator.PingGenerator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PingGenerateParseTest
+{
+    private final ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+
+    @Test
+    public void testGenerateParse() throws Exception
+    {
+        PingGenerator generator = new PingGenerator(new HeaderGenerator());
+
+        final List<PingFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onPing(PingFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        byte[] payload = new byte[8];
+        new Random().nextBytes(payload);
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generatePing(lease, payload, true);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(buffer);
+                }
+            }
+        }
+
+        Assert.assertEquals(1, frames.size());
+        PingFrame frame = frames.get(0);
+        Assert.assertArrayEquals(payload, frame.getPayload());
+        Assert.assertTrue(frame.isReply());
+    }
+
+    @Test
+    public void testGenerateParseOneByteAtATime() throws Exception
+    {
+        PingGenerator generator = new PingGenerator(new HeaderGenerator());
+
+        final List<PingFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onPing(PingFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        byte[] payload = new byte[8];
+        new Random().nextBytes(payload);
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generatePing(lease, payload, true);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
+                }
+            }
+
+            Assert.assertEquals(1, frames.size());
+            PingFrame frame = frames.get(0);
+            Assert.assertArrayEquals(payload, frame.getPayload());
+            Assert.assertTrue(frame.isReply());
+        }
+    }
+
+    @Test
+    public void testPayloadAsLong() throws Exception
+    {
+        PingGenerator generator = new PingGenerator(new HeaderGenerator());
+
+        final List<PingFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onPing(PingFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        PingFrame ping = new PingFrame(System.nanoTime(), true);
+        generator.generate(lease, ping);
+
+        for (ByteBuffer buffer : lease.getByteBuffers())
+        {
+            while (buffer.hasRemaining())
+            {
+                parser.parse(buffer);
+            }
+        }
+
+        Assert.assertEquals(1, frames.size());
+        PingFrame pong = frames.get(0);
+        Assert.assertEquals(ping.getPayloadAsLong(), pong.getPayloadAsLong());
+        Assert.assertTrue(pong.isReply());
+    }
+}
diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/PriorityGenerateParseTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/PriorityGenerateParseTest.java
new file mode 100644
index 0000000..e4d222a
--- /dev/null
+++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/PriorityGenerateParseTest.java
@@ -0,0 +1,124 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.http2.generator.HeaderGenerator;
+import org.eclipse.jetty.http2.generator.PriorityGenerator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PriorityGenerateParseTest
+{
+    private final ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+
+    @Test
+    public void testGenerateParse() throws Exception
+    {
+        PriorityGenerator generator = new PriorityGenerator(new HeaderGenerator());
+
+        final List<PriorityFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onPriority(PriorityFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        int streamId = 13;
+        int parentStreamId = 17;
+        int weight = 256;
+        boolean exclusive = true;
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generatePriority(lease, streamId, parentStreamId, weight, exclusive);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(buffer);
+                }
+            }
+        }
+
+        Assert.assertEquals(1, frames.size());
+        PriorityFrame frame = frames.get(0);
+        Assert.assertEquals(streamId, frame.getStreamId());
+        Assert.assertEquals(parentStreamId, frame.getParentStreamId());
+        Assert.assertEquals(weight, frame.getWeight());
+        Assert.assertEquals(exclusive, frame.isExclusive());
+    }
+
+    @Test
+    public void testGenerateParseOneByteAtATime() throws Exception
+    {
+        PriorityGenerator generator = new PriorityGenerator(new HeaderGenerator());
+
+        final List<PriorityFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onPriority(PriorityFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        int streamId = 13;
+        int parentStreamId = 17;
+        int weight = 3;
+        boolean exclusive = true;
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generatePriority(lease, streamId, parentStreamId, weight, exclusive);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
+                }
+            }
+
+            Assert.assertEquals(1, frames.size());
+            PriorityFrame frame = frames.get(0);
+            Assert.assertEquals(streamId, frame.getStreamId());
+            Assert.assertEquals(parentStreamId, frame.getParentStreamId());
+            Assert.assertEquals(weight, frame.getWeight());
+            Assert.assertEquals(exclusive, frame.isExclusive());
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/PushPromiseGenerateParseTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/PushPromiseGenerateParseTest.java
new file mode 100644
index 0000000..7235885
--- /dev/null
+++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/PushPromiseGenerateParseTest.java
@@ -0,0 +1,147 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.generator.HeaderGenerator;
+import org.eclipse.jetty.http2.generator.PushPromiseGenerator;
+import org.eclipse.jetty.http2.hpack.HpackEncoder;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PushPromiseGenerateParseTest
+{
+    private final ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+
+    @Test
+    public void testGenerateParse() throws Exception
+    {
+        PushPromiseGenerator generator = new PushPromiseGenerator(new HeaderGenerator(), new HpackEncoder());
+
+        final List<PushPromiseFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onPushPromise(PushPromiseFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        int streamId = 13;
+        int promisedStreamId = 17;
+        HttpFields fields = new HttpFields();
+        fields.put("Accept", "text/html");
+        fields.put("User-Agent", "Jetty");
+        MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields);
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generatePushPromise(lease, streamId, promisedStreamId, metaData);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(buffer);
+                }
+            }
+
+            Assert.assertEquals(1, frames.size());
+            PushPromiseFrame frame = frames.get(0);
+            Assert.assertEquals(streamId, frame.getStreamId());
+            Assert.assertEquals(promisedStreamId, frame.getPromisedStreamId());
+            MetaData.Request request = (MetaData.Request)frame.getMetaData();
+            Assert.assertEquals(metaData.getMethod(), request.getMethod());
+            Assert.assertEquals(metaData.getURI(), request.getURI());
+            for (int j = 0; j < fields.size(); ++j)
+            {
+                HttpField field = fields.getField(j);
+                Assert.assertTrue(request.getFields().contains(field));
+            }
+        }
+    }
+
+    @Test
+    public void testGenerateParseOneByteAtATime() throws Exception
+    {
+        PushPromiseGenerator generator = new PushPromiseGenerator(new HeaderGenerator(), new HpackEncoder());
+
+        final List<PushPromiseFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onPushPromise(PushPromiseFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        int streamId = 13;
+        int promisedStreamId = 17;
+        HttpFields fields = new HttpFields();
+        fields.put("Accept", "text/html");
+        fields.put("User-Agent", "Jetty");
+        MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields);
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generatePushPromise(lease, streamId, promisedStreamId, metaData);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
+                }
+            }
+
+            Assert.assertEquals(1, frames.size());
+            PushPromiseFrame frame = frames.get(0);
+            Assert.assertEquals(streamId, frame.getStreamId());
+            Assert.assertEquals(promisedStreamId, frame.getPromisedStreamId());
+            MetaData.Request request = (MetaData.Request)frame.getMetaData();
+            Assert.assertEquals(metaData.getMethod(), request.getMethod());
+            Assert.assertEquals(metaData.getURI(), request.getURI());
+            for (int j = 0; j < fields.size(); ++j)
+            {
+                HttpField field = fields.getField(j);
+                Assert.assertTrue(request.getFields().contains(field));
+            }
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/ResetGenerateParseTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/ResetGenerateParseTest.java
new file mode 100644
index 0000000..2ac72a9
--- /dev/null
+++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/ResetGenerateParseTest.java
@@ -0,0 +1,116 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.http2.generator.HeaderGenerator;
+import org.eclipse.jetty.http2.generator.ResetGenerator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ResetGenerateParseTest
+{
+    private final ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+
+    @Test
+    public void testGenerateParse() throws Exception
+    {
+        ResetGenerator generator = new ResetGenerator(new HeaderGenerator());
+
+        final List<ResetFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onReset(ResetFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        int streamId = 13;
+        int error = 17;
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generateReset(lease, streamId, error);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(buffer);
+                }
+            }
+        }
+
+        Assert.assertEquals(1, frames.size());
+        ResetFrame frame = frames.get(0);
+        Assert.assertEquals(streamId, frame.getStreamId());
+        Assert.assertEquals(error, frame.getError());
+    }
+
+    @Test
+    public void testGenerateParseOneByteAtATime() throws Exception
+    {
+        ResetGenerator generator = new ResetGenerator(new HeaderGenerator());
+
+        final List<ResetFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onReset(ResetFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        int streamId = 13;
+        int error = 17;
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generateReset(lease, streamId, error);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
+                }
+            }
+
+            Assert.assertEquals(1, frames.size());
+            ResetFrame frame = frames.get(0);
+            Assert.assertEquals(streamId, frame.getStreamId());
+            Assert.assertEquals(error, frame.getError());
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/SettingsGenerateParseTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/SettingsGenerateParseTest.java
new file mode 100644
index 0000000..1057657
--- /dev/null
+++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/SettingsGenerateParseTest.java
@@ -0,0 +1,181 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.generator.HeaderGenerator;
+import org.eclipse.jetty.http2.generator.SettingsGenerator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SettingsGenerateParseTest
+{
+    private final ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+
+    @Test
+    public void testGenerateParseNoSettings() throws Exception
+    {
+        List<SettingsFrame> frames = testGenerateParse(Collections.<Integer, Integer>emptyMap());
+        Assert.assertEquals(1, frames.size());
+        SettingsFrame frame = frames.get(0);
+        Assert.assertEquals(0, frame.getSettings().size());
+        Assert.assertTrue(frame.isReply());
+    }
+
+    @Test
+    public void testGenerateParseSettings() throws Exception
+    {
+        Map<Integer, Integer> settings1 = new HashMap<>();
+        int key1 = 13;
+        Integer value1 = 17;
+        settings1.put(key1, value1);
+        int key2 = 19;
+        Integer value2 = 23;
+        settings1.put(key2, value2);
+        List<SettingsFrame> frames = testGenerateParse(settings1);
+        Assert.assertEquals(1, frames.size());
+        SettingsFrame frame = frames.get(0);
+        Map<Integer, Integer> settings2 = frame.getSettings();
+        Assert.assertEquals(2, settings2.size());
+        Assert.assertEquals(value1, settings2.get(key1));
+        Assert.assertEquals(value2, settings2.get(key2));
+    }
+
+    private List<SettingsFrame> testGenerateParse(Map<Integer, Integer> settings)
+    {
+        SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator());
+
+        final List<SettingsFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onSettings(SettingsFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generateSettings(lease, settings, true);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(buffer);
+                }
+            }
+        }
+
+        return frames;
+    }
+
+    @Test
+    public void testGenerateParseInvalidSettings() throws Exception
+    {
+        SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator());
+
+        final AtomicInteger errorRef = new AtomicInteger();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onConnectionFailure(int error, String reason)
+            {
+                errorRef.set(error);
+            }
+        }, 4096, 8192);
+
+        Map<Integer, Integer> settings1 = new HashMap<>();
+        settings1.put(13, 17);
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        generator.generateSettings(lease, settings1, true);
+        // Modify the length of the frame to make it invalid
+        ByteBuffer bytes = lease.getByteBuffers().get(0);
+        bytes.putShort(1, (short)(bytes.getShort(1) - 1));
+
+        for (ByteBuffer buffer : lease.getByteBuffers())
+        {
+            while (buffer.hasRemaining())
+            {
+                parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
+            }
+        }
+
+        Assert.assertEquals(ErrorCode.FRAME_SIZE_ERROR.code, errorRef.get());
+    }
+
+    @Test
+    public void testGenerateParseOneByteAtATime() throws Exception
+    {
+        SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator());
+
+        final List<SettingsFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onSettings(SettingsFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        Map<Integer, Integer> settings1 = new HashMap<>();
+        int key = 13;
+        Integer value = 17;
+        settings1.put(key, value);
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generateSettings(lease, settings1, true);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
+                }
+            }
+
+            Assert.assertEquals(1, frames.size());
+            SettingsFrame frame = frames.get(0);
+            Map<Integer, Integer> settings2 = frame.getSettings();
+            Assert.assertEquals(1, settings2.size());
+            Assert.assertEquals(value, settings2.get(key));
+            Assert.assertTrue(frame.isReply());
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/WindowUpdateGenerateParseTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/WindowUpdateGenerateParseTest.java
new file mode 100644
index 0000000..d6a7b0c
--- /dev/null
+++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/WindowUpdateGenerateParseTest.java
@@ -0,0 +1,116 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.frames;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.http2.generator.HeaderGenerator;
+import org.eclipse.jetty.http2.generator.WindowUpdateGenerator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class WindowUpdateGenerateParseTest
+{
+    private final ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+
+    @Test
+    public void testGenerateParse() throws Exception
+    {
+        WindowUpdateGenerator generator = new WindowUpdateGenerator(new HeaderGenerator());
+
+        final List<WindowUpdateFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onWindowUpdate(WindowUpdateFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        int streamId = 13;
+        int windowUpdate = 17;
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generateWindowUpdate(lease, streamId, windowUpdate);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(buffer);
+                }
+            }
+        }
+
+        Assert.assertEquals(1, frames.size());
+        WindowUpdateFrame frame = frames.get(0);
+        Assert.assertEquals(streamId, frame.getStreamId());
+        Assert.assertEquals(windowUpdate, frame.getWindowDelta());
+    }
+
+    @Test
+    public void testGenerateParseOneByteAtATime() throws Exception
+    {
+        WindowUpdateGenerator generator = new WindowUpdateGenerator(new HeaderGenerator());
+
+        final List<WindowUpdateFrame> frames = new ArrayList<>();
+        Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+        {
+            @Override
+            public void onWindowUpdate(WindowUpdateFrame frame)
+            {
+                frames.add(frame);
+            }
+        }, 4096, 8192);
+
+        int streamId = 13;
+        int windowUpdate = 17;
+
+        // Iterate a few times to be sure generator and parser are properly reset.
+        for (int i = 0; i < 2; ++i)
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.generateWindowUpdate(lease, streamId, windowUpdate);
+
+            frames.clear();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                while (buffer.hasRemaining())
+                {
+                    parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
+                }
+            }
+
+            Assert.assertEquals(1, frames.size());
+            WindowUpdateFrame frame = frames.get(0);
+            Assert.assertEquals(streamId, frame.getStreamId());
+            Assert.assertEquals(windowUpdate, frame.getWindowDelta());
+        }
+    }
+}
diff --git a/jetty-http2/http2-common/src/test/resources/jetty-logging.properties b/jetty-http2/http2-common/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..b4e4380
--- /dev/null
+++ b/jetty-http2/http2-common/src/test/resources/jetty-logging.properties
@@ -0,0 +1,2 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+org.eclipse.jetty.http2.LEVEL=INFO
diff --git a/jetty-http2/http2-hpack/pom.xml b/jetty-http2/http2-hpack/pom.xml
new file mode 100644
index 0000000..c71765b
--- /dev/null
+++ b/jetty-http2/http2-hpack/pom.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.http2</groupId>
+    <artifactId>http2-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>http2-hpack</artifactId>
+  <name>Jetty :: HTTP2 :: HPACK</name>
+
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.hpack</bundle-symbolic-name>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-util</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-http</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-io</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-util-ajax</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+  </build>
+
+</project>
diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/AuthorityHttpField.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/AuthorityHttpField.java
new file mode 100644
index 0000000..40ff2dc
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/AuthorityHttpField.java
@@ -0,0 +1,43 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http2.hpack;
+
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpHeader;
+
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public class AuthorityHttpField extends HostPortHttpField
+{
+    public final static String AUTHORITY = HpackContext.STATIC_TABLE[1][0];
+    
+    public AuthorityHttpField(String authority)
+    {
+        super(HttpHeader.C_AUTHORITY,AUTHORITY,authority);
+    }
+    
+    @Override
+    public String toString()
+    {
+        return String.format("%s(preparsed h=%s p=%d)",super.toString(),getHost(),getPort());
+    }
+}
diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java
new file mode 100644
index 0000000..09b3f6d
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java
@@ -0,0 +1,486 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.hpack;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.util.ArrayTernaryTrie;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.Trie;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * HPACK - Header Compression for HTTP/2
+ * <p>This class maintains the compression context for a single HTTP/2
+ * connection. Specifically it holds the static and dynamic Header Field Tables
+ * and the associated sizes and limits.
+ * </p>
+ * <p>It is compliant with draft 11 of the specification</p>
+ */
+public class HpackContext
+{
+    public static final Logger LOG = Log.getLogger(HpackContext.class);
+    private static final String EMPTY = "";
+    public static final String[][] STATIC_TABLE =
+    {
+        {null,null},
+        /* 1  */ {":authority",EMPTY},
+        /* 2  */ {":method","GET"},
+        /* 3  */ {":method","POST"},
+        /* 4  */ {":path","/"},
+        /* 5  */ {":path","/index.html"},
+        /* 6  */ {":scheme","http"},
+        /* 7  */ {":scheme","https"},
+        /* 8  */ {":status","200"},
+        /* 9  */ {":status","204"},
+        /* 10 */ {":status","206"},
+        /* 11 */ {":status","304"},
+        /* 12 */ {":status","400"},
+        /* 13 */ {":status","404"},
+        /* 14 */ {":status","500"},
+        /* 15 */ {"accept-charset",EMPTY},
+        /* 16 */ {"accept-encoding","gzip, deflate"},
+        /* 17 */ {"accept-language",EMPTY},
+        /* 18 */ {"accept-ranges",EMPTY},
+        /* 19 */ {"accept",EMPTY},
+        /* 20 */ {"access-control-allow-origin",EMPTY},
+        /* 21 */ {"age",EMPTY},
+        /* 22 */ {"allow",EMPTY},
+        /* 23 */ {"authorization",EMPTY},
+        /* 24 */ {"cache-control",EMPTY},
+        /* 25 */ {"content-disposition",EMPTY},
+        /* 26 */ {"content-encoding",EMPTY},
+        /* 27 */ {"content-language",EMPTY},
+        /* 28 */ {"content-length",EMPTY},
+        /* 29 */ {"content-location",EMPTY},
+        /* 30 */ {"content-range",EMPTY},
+        /* 31 */ {"content-type",EMPTY},
+        /* 32 */ {"cookie",EMPTY},
+        /* 33 */ {"date",EMPTY},
+        /* 34 */ {"etag",EMPTY},
+        /* 35 */ {"expect",EMPTY},
+        /* 36 */ {"expires",EMPTY},
+        /* 37 */ {"from",EMPTY},
+        /* 38 */ {"host",EMPTY},
+        /* 39 */ {"if-match",EMPTY},
+        /* 40 */ {"if-modified-since",EMPTY},
+        /* 41 */ {"if-none-match",EMPTY},
+        /* 42 */ {"if-range",EMPTY},
+        /* 43 */ {"if-unmodified-since",EMPTY},
+        /* 44 */ {"last-modified",EMPTY},
+        /* 45 */ {"link",EMPTY},
+        /* 46 */ {"location",EMPTY},
+        /* 47 */ {"max-forwards",EMPTY},
+        /* 48 */ {"proxy-authenticate",EMPTY},
+        /* 49 */ {"proxy-authorization",EMPTY},
+        /* 50 */ {"range",EMPTY},
+        /* 51 */ {"referer",EMPTY},
+        /* 52 */ {"refresh",EMPTY},
+        /* 53 */ {"retry-after",EMPTY},
+        /* 54 */ {"server",EMPTY},
+        /* 55 */ {"set-cookie",EMPTY},
+        /* 56 */ {"strict-transport-security",EMPTY},
+        /* 57 */ {"transfer-encoding",EMPTY},
+        /* 58 */ {"user-agent",EMPTY},
+        /* 59 */ {"vary",EMPTY},
+        /* 60 */ {"via",EMPTY},
+        /* 61 */ {"www-authenticate",EMPTY},
+    };
+
+    private static final Map<HttpField,Entry> __staticFieldMap = new HashMap<>();
+    private static final Trie<StaticEntry> __staticNameMap = new ArrayTernaryTrie<>(true,512);
+    private static final StaticEntry[] __staticTableByHeader = new StaticEntry[HttpHeader.UNKNOWN.ordinal()];
+    private static final StaticEntry[] __staticTable=new StaticEntry[STATIC_TABLE.length];
+    public static final int STATIC_SIZE = STATIC_TABLE.length-1;
+    static
+    {
+        Set<String> added = new HashSet<>();
+        for (int i=1;i<STATIC_TABLE.length;i++)
+        {
+            StaticEntry entry=null;
+
+            String name  = STATIC_TABLE[i][0];
+            String value = STATIC_TABLE[i][1];
+            HttpHeader header = HttpHeader.CACHE.get(name);
+            if (header!=null && value!=null)
+            {
+                switch (header)
+                {
+                    case C_METHOD:
+                    {
+
+                        HttpMethod method = HttpMethod.CACHE.get(value);
+                        if (method!=null)
+                            entry=new StaticEntry(i,new StaticTableHttpField(header,name,value,method));
+                        break;
+                    }
+
+                    case C_SCHEME:
+                    {
+
+                        HttpScheme scheme = HttpScheme.CACHE.get(value);
+                        if (scheme!=null)
+                            entry=new StaticEntry(i,new StaticTableHttpField(header,name,value,scheme));
+                        break;
+                    }
+
+                    case C_STATUS:
+                    {
+                        entry=new StaticEntry(i,new StaticTableHttpField(header,name,value,Integer.valueOf(value)));
+                        break;
+                    }
+
+                    default:
+                        break;
+                }
+            }
+
+            if (entry==null)
+                entry=new StaticEntry(i,header==null?new HttpField(STATIC_TABLE[i][0],value):new HttpField(header,name,value));
+
+
+            __staticTable[i]=entry;
+
+            if (entry._field.getValue()!=null)
+                __staticFieldMap.put(entry._field,entry);
+
+            if (!added.contains(entry._field.getName()))
+            {
+                added.add(entry._field.getName());
+                __staticNameMap.put(entry._field.getName(),entry);
+                if (__staticNameMap.get(entry._field.getName())==null)
+                    throw new IllegalStateException("name trie too small");
+            }
+        }
+
+        for (HttpHeader h : HttpHeader.values())
+        {
+            StaticEntry entry = __staticNameMap.get(h.asString());
+            if (entry!=null)
+                __staticTableByHeader[h.ordinal()]=entry;
+        }
+    }
+
+    private int _maxDynamicTableSizeInBytes;
+    private int _dynamicTableSizeInBytes;
+    private final DynamicTable _dynamicTable;
+    private final Map<HttpField,Entry> _fieldMap = new HashMap<>();
+    private final Map<String,Entry> _nameMap = new HashMap<>();
+
+    HpackContext(int maxDynamicTableSize)
+    {
+        _maxDynamicTableSizeInBytes=maxDynamicTableSize;
+        int guesstimateEntries = 10+maxDynamicTableSize/(32+10+10);
+        _dynamicTable=new DynamicTable(guesstimateEntries);
+        if (LOG.isDebugEnabled())
+            LOG.debug(String.format("HdrTbl[%x] created max=%d",hashCode(),maxDynamicTableSize));
+    }
+
+    public void resize(int newMaxDynamicTableSize)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug(String.format("HdrTbl[%x] resized max=%d->%d",hashCode(),_maxDynamicTableSizeInBytes,newMaxDynamicTableSize));
+        _maxDynamicTableSizeInBytes=newMaxDynamicTableSize;
+        _dynamicTable.evict();
+    }
+
+    public Entry get(HttpField field)
+    {
+        Entry entry = _fieldMap.get(field);
+        if (entry==null)
+            entry=__staticFieldMap.get(field);
+        return entry;
+    }
+
+    public Entry get(String name)
+    {
+        Entry entry = __staticNameMap.get(name);
+        if (entry!=null)
+            return entry;
+        return _nameMap.get(StringUtil.asciiToLowerCase(name));
+    }
+
+    public Entry get(int index)
+    {
+        if (index<=STATIC_SIZE)
+            return __staticTable[index];
+
+        return _dynamicTable.get(index);
+    }
+
+    public Entry get(HttpHeader header)
+    {
+        Entry e = __staticTableByHeader[header.ordinal()];
+        if (e==null)
+            return get(header.asString());
+        return e;
+    }
+
+    public static Entry getStatic(HttpHeader header)
+    {
+        return __staticTableByHeader[header.ordinal()];
+    }
+
+    public Entry add(HttpField field)
+    {
+        Entry entry=new Entry(field);
+        int size = entry.getSize();
+        if (size>_maxDynamicTableSizeInBytes)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug(String.format("HdrTbl[%x] !added size %d>%d",hashCode(),size,_maxDynamicTableSizeInBytes));
+            return null;
+        }
+        _dynamicTableSizeInBytes+=size;
+        _dynamicTable.add(entry);
+        _fieldMap.put(field,entry);
+        _nameMap.put(StringUtil.asciiToLowerCase(field.getName()),entry);
+
+        if (LOG.isDebugEnabled())
+            LOG.debug(String.format("HdrTbl[%x] added %s",hashCode(),entry));
+        _dynamicTable.evict();
+        return entry;
+    }
+
+    /**
+     * @return Current dynamic table size in entries
+     */
+    public int size()
+    {
+        return _dynamicTable.size();
+    }
+
+    /**
+     * @return Current Dynamic table size in Octets
+     */
+    public int getDynamicTableSize()
+    {
+        return _dynamicTableSizeInBytes;
+    }
+
+    /**
+     * @return Max Dynamic table size in Octets
+     */
+    public int getMaxDynamicTableSize()
+    {
+        return _maxDynamicTableSizeInBytes;
+    }
+
+    public int index(Entry entry)
+    {
+        if (entry._slot<0)
+            return 0;
+        if (entry.isStatic())
+            return entry._slot;
+
+        return _dynamicTable.index(entry);
+    }
+
+    public static int staticIndex(HttpHeader header)
+    {
+        if (header==null)
+            return 0;
+        Entry entry=__staticNameMap.get(header.asString());
+        if (entry==null)
+            return 0;
+        return entry._slot;
+    }
+
+
+    @Override
+    public String toString()
+    {
+        return String.format("HpackContext@%x{entries=%d,size=%d,max=%d}",hashCode(),_dynamicTable.size(),_dynamicTableSizeInBytes,_maxDynamicTableSizeInBytes);
+    }
+
+    private class DynamicTable 
+    {
+        Entry[] _entries;
+        int _size;
+        int _offset;
+        int _growby;
+        
+        private DynamicTable(int initCapacity)
+        {
+            _entries=new Entry[initCapacity];
+            _growby=initCapacity;
+        }
+
+        public void add(Entry entry)
+        {
+            if (_size==_entries.length)
+            {
+                Entry[] entries = new Entry[_entries.length+_growby];
+                for (int i=0;i<_size;i++)
+                {
+                    int slot = (_offset+i)%_entries.length;
+                    entries[i]=_entries[slot];
+                    entries[i]._slot=i;
+                }
+                _entries=entries;
+                _offset=0;
+            }
+            int slot=(_size++ + _offset)%_entries.length;
+            _entries[slot]=entry;
+            entry._slot=slot;
+        }
+
+        public int index(Entry entry)
+        {
+            return STATIC_SIZE + _size-(entry._slot-_offset+_entries.length)%_entries.length;
+        }
+        
+        public Entry get(int index)
+        {
+            int d = index-STATIC_SIZE-1;
+            if (d<0 || d>=_size)
+                return null;
+            int slot = (_offset+_size-d-1)%_entries.length;
+            return _entries[slot];
+        }
+
+        public int size()
+        {
+            return _size;
+        }
+
+        private void evict()
+        {
+            while (_dynamicTableSizeInBytes>_maxDynamicTableSizeInBytes)
+            {
+                Entry entry = _entries[_offset];
+                _entries[_offset]=null;
+                _offset = (_offset+1)%_entries.length;
+                _size--;
+                if (LOG.isDebugEnabled())
+                    LOG.debug(String.format("HdrTbl[%x] evict %s",HpackContext.this.hashCode(),entry));
+                _dynamicTableSizeInBytes-=entry.getSize();
+                entry._slot=-1;
+                _fieldMap.remove(entry.getHttpField());
+                String lc=StringUtil.asciiToLowerCase(entry.getHttpField().getName());
+                if (entry==_nameMap.get(lc))
+                    _nameMap.remove(lc);
+
+            }
+            if (LOG.isDebugEnabled())
+                LOG.debug(String.format("HdrTbl[%x] entries=%d, size=%d, max=%d",HpackContext.this.hashCode(),_dynamicTable.size(),_dynamicTableSizeInBytes,_maxDynamicTableSizeInBytes));
+        }
+
+    }
+
+    public static class Entry
+    {
+        final HttpField _field;
+        int _slot; // The index within it's array
+
+        Entry()
+        {
+            _slot=-1;
+            _field=null;
+        }
+
+        Entry(HttpField field)
+        {
+            _field=field;
+        }
+
+        public int getSize()
+        {
+            String value = _field.getValue();
+            return 32 + _field.getName().length() + (value == null ? 0 : value.length());
+        }
+
+        public HttpField getHttpField()
+        {
+            return _field;
+        }
+
+        public boolean isStatic()
+        {
+            return false;
+        }
+
+        public byte[] getStaticHuffmanValue()
+        {
+            return null;
+        }
+
+        public String toString()
+        {
+            return String.format("{%s,%d,%s,%x}",isStatic()?"S":"D",_slot,_field,hashCode());
+        }
+    }
+
+    public static class StaticEntry extends Entry
+    {
+        private final byte[] _huffmanValue;
+        private final byte _encodedField;
+
+        StaticEntry(int index,HttpField field)
+        {
+            super(field);
+            _slot=index;
+            String value = field.getValue();
+            if (value!=null && value.length()>0)
+            {
+                int huffmanLen = Huffman.octetsNeeded(value);
+                int lenLen = NBitInteger.octectsNeeded(7,huffmanLen);
+                _huffmanValue = new byte[1+lenLen+huffmanLen];
+                ByteBuffer buffer = ByteBuffer.wrap(_huffmanValue);
+
+                // Indicate Huffman
+                buffer.put((byte)0x80);
+                // Add huffman length
+                NBitInteger.encode(buffer,7,huffmanLen);
+                // Encode value
+                Huffman.encode(buffer,value);
+            }
+            else
+                _huffmanValue=null;
+
+            _encodedField=(byte)(0x80|index);
+        }
+
+        @Override
+        public boolean isStatic()
+        {
+            return true;
+        }
+
+        @Override
+        public byte[] getStaticHuffmanValue()
+        {
+            return _huffmanValue;
+        }
+
+        public byte getEncodedField()
+        {
+            return _encodedField;
+        }
+    }
+}
diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java
new file mode 100644
index 0000000..4ebbbe4
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java
@@ -0,0 +1,277 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http2.hpack;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.hpack.HpackContext.Entry;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Hpack Decoder
+ * <p>This is not thread safe and may only be called by 1 thread at a time.</p>
+ */
+public class HpackDecoder
+{
+    public static final Logger LOG = Log.getLogger(HpackDecoder.class);
+    public final static HttpField.LongValueHttpField CONTENT_LENGTH_0 =
+            new HttpField.LongValueHttpField(HttpHeader.CONTENT_LENGTH,0L);
+
+    private final HpackContext _context;
+    private final MetaDataBuilder _builder;
+    private int _localMaxDynamicTableSize;
+
+    /**
+     * @param localMaxDynamicTableSize  The maximum allowed size of the local dynamic header field table.
+     * @param maxHeaderSize The maximum allowed size of a headers block, expressed as total of all name and value characters, plus 32 per field
+     */
+    public HpackDecoder(int localMaxDynamicTableSize, int maxHeaderSize)
+    {
+        _context=new HpackContext(localMaxDynamicTableSize);
+        _localMaxDynamicTableSize=localMaxDynamicTableSize;
+        _builder = new MetaDataBuilder(maxHeaderSize);
+    }
+
+    public HpackContext getHpackContext()
+    {
+        return _context;
+    }
+
+    public void setLocalMaxDynamicTableSize(int localMaxdynamciTableSize)
+    {
+        _localMaxDynamicTableSize=localMaxdynamciTableSize;
+    }
+
+    public MetaData decode(ByteBuffer buffer)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug(String.format("CtxTbl[%x] decoding %d octets",_context.hashCode(),buffer.remaining()));
+
+        // If the buffer is big, don't even think about decoding it
+        if (buffer.remaining()>_builder.getMaxSize())
+            throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,"Header frame size "+buffer.remaining()+">"+_builder.getMaxSize());
+
+        while(buffer.hasRemaining())
+        {
+            if (LOG.isDebugEnabled() && buffer.hasArray())
+            {
+                int l=Math.min(buffer.remaining(),32);
+                LOG.debug("decode {}{}",
+                        TypeUtil.toHexString(buffer.array(),buffer.arrayOffset()+buffer.position(),l),
+                        l<buffer.remaining()?"...":"");
+            }
+
+            byte b = buffer.get();
+            if (b<0)
+            {
+                // 7.1 indexed if the high bit is set
+                int index = NBitInteger.decode(buffer,7);
+                Entry entry=_context.get(index);
+                if (entry==null)
+                {
+                    throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "Unknown index "+index);
+                }
+                else if (entry.isStatic())
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("decode IdxStatic {}",entry);
+                    // emit field
+                    _builder.emit(entry.getHttpField());
+
+                    // TODO copy and add to reference set if there is room
+                    // _context.add(entry.getHttpField());
+                }
+                else
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("decode Idx {}",entry);
+                    // emit
+                    _builder.emit(entry.getHttpField());
+                }
+            }
+            else
+            {
+                // look at the first nibble in detail
+                byte f= (byte)((b&0xF0)>>4);
+                String name;
+                HttpHeader header;
+                String value;
+
+                boolean indexed;
+                int name_index;
+
+                switch (f)
+                {
+                    case 2: // 7.3
+                    case 3: // 7.3
+                        // change table size
+                        int size = NBitInteger.decode(buffer,5);
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("decode resize="+size);
+                        if (size>_localMaxDynamicTableSize)
+                            throw new IllegalArgumentException();
+                        _context.resize(size);
+                        continue;
+
+                    case 0: // 7.2.2
+                    case 1: // 7.2.3
+                        indexed=false;
+                        name_index=NBitInteger.decode(buffer,4);
+                        break;
+
+                    case 4: // 7.2.1
+                    case 5: // 7.2.1
+                    case 6: // 7.2.1
+                    case 7: // 7.2.1
+                        indexed=true;
+                        name_index=NBitInteger.decode(buffer,6);
+                        break;
+
+                    default:
+                        throw new IllegalStateException();
+                }
+
+                boolean huffmanName=false;
+
+                // decode the name
+                if (name_index>0)
+                {
+                    Entry name_entry=_context.get(name_index);
+                    name=name_entry.getHttpField().getName();
+                    header=name_entry.getHttpField().getHeader();
+                }
+                else
+                {
+                    huffmanName = (buffer.get()&0x80)==0x80;
+                    int length = NBitInteger.decode(buffer,7);
+                    _builder.checkSize(length,huffmanName);
+                    if (huffmanName)
+                        name=Huffman.decode(buffer,length);
+                    else
+                        name=toASCIIString(buffer,length);
+                    for (int i=0;i<name.length();i++)
+                    {
+                        char c=name.charAt(i);
+                        if (c>='A'&&c<='Z')
+                        {
+                            throw new BadMessageException(400,"Uppercase header name");
+                        }
+                    }
+                    header=HttpHeader.CACHE.get(name);
+                }
+
+                // decode the value
+                boolean huffmanValue = (buffer.get()&0x80)==0x80;
+                int length = NBitInteger.decode(buffer,7);
+                _builder.checkSize(length,huffmanValue);
+                if (huffmanValue)
+                    value=Huffman.decode(buffer,length);
+                else
+                    value=toASCIIString(buffer,length);
+
+                // Make the new field
+                HttpField field;
+                if (header==null)
+                {
+                    // just make a normal field and bypass header name lookup
+                    field = new HttpField(null,name,value);
+                }
+                else
+                {
+                    // might be worthwhile to create a value HttpField if it is indexed
+                    // and/or of a type that may be looked up multiple times.
+                    switch(header)
+                    {
+                        case C_STATUS:
+                            if (indexed)
+                                field = new HttpField.IntValueHttpField(header,name,value);
+                            else
+                                field = new HttpField(header,name,value);
+                            break;
+
+                        case C_AUTHORITY:
+                            field = new AuthorityHttpField(value);
+                            break;
+
+                        case CONTENT_LENGTH:
+                            if ("0".equals(value))
+                                field = CONTENT_LENGTH_0;
+                            else
+                                field = new HttpField.LongValueHttpField(header,name,value);
+                            break;
+
+                        default:
+                            field = new HttpField(header,name,value);
+                            break;
+                    }
+                }
+
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("decoded '{}' by {}/{}/{}",
+                            field,
+                            name_index > 0 ? "IdxName" : (huffmanName ? "HuffName" : "LitName"),
+                            huffmanValue ? "HuffVal" : "LitVal",
+                            indexed ? "Idx" : "");
+                }
+
+                // emit the field
+                _builder.emit(field);
+
+                // if indexed
+                if (indexed)
+                {
+                    // add to dynamic table
+                    if (_context.add(field)==null)
+                        throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,"Indexed field value too large");
+                }
+
+            }
+        }
+
+        return _builder.build();
+    }
+
+    public static String toASCIIString(ByteBuffer buffer,int length)
+    {
+        StringBuilder builder = new StringBuilder(length);
+        int position=buffer.position();
+        int start=buffer.arrayOffset()+ position;
+        int end=start+length;
+        buffer.position(position+length);
+        byte[] array=buffer.array();
+        for (int i=start;i<end;i++)
+            builder.append((char)(0x7f&array[i]));
+        return builder.toString();
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("HpackDecoder@%x{%s}",hashCode(),_context);
+    }
+}
diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java
new file mode 100644
index 0000000..b20e489
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java
@@ -0,0 +1,387 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http2.hpack;
+
+import java.nio.ByteBuffer;
+import java.util.EnumSet;
+
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http.PreEncodedHttpField;
+import org.eclipse.jetty.http2.hpack.HpackContext.Entry;
+import org.eclipse.jetty.http2.hpack.HpackContext.StaticEntry;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class HpackEncoder
+{
+    public static final Logger LOG = Log.getLogger(HpackEncoder.class);
+
+    private final static HttpField[] __status= new HttpField[599];
+
+
+    final static EnumSet<HttpHeader> __DO_NOT_HUFFMAN =
+            EnumSet.of(
+                    HttpHeader.AUTHORIZATION,
+                    HttpHeader.CONTENT_MD5,
+                    HttpHeader.PROXY_AUTHENTICATE,
+                    HttpHeader.PROXY_AUTHORIZATION);
+
+    final static EnumSet<HttpHeader> __DO_NOT_INDEX =
+            EnumSet.of(
+                    // HttpHeader.C_PATH,  // TODO more data needed
+                    // HttpHeader.DATE,    // TODO more data needed
+                    HttpHeader.AUTHORIZATION,
+                    HttpHeader.CONTENT_MD5,
+                    HttpHeader.CONTENT_RANGE,
+                    HttpHeader.ETAG,
+                    HttpHeader.IF_MODIFIED_SINCE,
+                    HttpHeader.IF_UNMODIFIED_SINCE,
+                    HttpHeader.IF_NONE_MATCH,
+                    HttpHeader.IF_RANGE,
+                    HttpHeader.IF_MATCH,
+                    HttpHeader.LOCATION,
+                    HttpHeader.RANGE,
+                    HttpHeader.RETRY_AFTER,
+                    // HttpHeader.EXPIRES,
+                    HttpHeader.LAST_MODIFIED,
+                    HttpHeader.SET_COOKIE,
+                    HttpHeader.SET_COOKIE2);
+
+
+    final static EnumSet<HttpHeader> __NEVER_INDEX =
+            EnumSet.of(
+                    HttpHeader.AUTHORIZATION,
+                    HttpHeader.SET_COOKIE,
+                    HttpHeader.SET_COOKIE2);
+
+    static
+    {
+        for (HttpStatus.Code code : HttpStatus.Code.values())
+            __status[code.getCode()]=new PreEncodedHttpField(HttpHeader.C_STATUS,Integer.toString(code.getCode()));
+    }
+
+    private final HpackContext _context;
+    private final boolean _debug;
+    private int _remoteMaxDynamicTableSize;
+    private int _localMaxDynamicTableSize;
+    private int _maxHeaderListSize;
+    private int _headerListSize;
+
+    public HpackEncoder()
+    {
+        this(4096,4096,-1);
+    }
+
+    public HpackEncoder(int localMaxDynamicTableSize)
+    {
+        this(localMaxDynamicTableSize,4096,-1);
+    }
+    
+    public HpackEncoder(int localMaxDynamicTableSize,int remoteMaxDynamicTableSize)
+    {
+        this(localMaxDynamicTableSize,remoteMaxDynamicTableSize,-1);
+    }
+    
+    public HpackEncoder(int localMaxDynamicTableSize,int remoteMaxDynamicTableSize, int maxHeaderListSize)
+    {
+        _context=new HpackContext(remoteMaxDynamicTableSize);
+        _remoteMaxDynamicTableSize=remoteMaxDynamicTableSize;
+        _localMaxDynamicTableSize=localMaxDynamicTableSize;
+        _maxHeaderListSize=maxHeaderListSize;
+        _debug=LOG.isDebugEnabled();
+    }
+
+    public int getMaxHeaderListSize()
+    {
+        return _maxHeaderListSize;
+    }
+
+    public void setMaxHeaderListSize(int maxHeaderListSize)
+    {
+        _maxHeaderListSize = maxHeaderListSize;
+    }
+
+    public HpackContext getHpackContext()
+    {
+        return _context;
+    }
+
+    public void setRemoteMaxDynamicTableSize(int remoteMaxDynamicTableSize)
+    {
+        _remoteMaxDynamicTableSize=remoteMaxDynamicTableSize;
+    }
+
+    public void setLocalMaxDynamicTableSize(int localMaxDynamicTableSize)
+    {
+        _localMaxDynamicTableSize=localMaxDynamicTableSize;
+    }
+
+    public void encode(ByteBuffer buffer, MetaData metadata)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug(String.format("CtxTbl[%x] encoding",_context.hashCode()));
+
+        _headerListSize=0;
+        int pos = buffer.position();
+
+        // Check the dynamic table sizes!
+        int maxDynamicTableSize=Math.min(_remoteMaxDynamicTableSize,_localMaxDynamicTableSize);
+        if (maxDynamicTableSize!=_context.getMaxDynamicTableSize())
+            encodeMaxDynamicTableSize(buffer,maxDynamicTableSize);
+
+        // Add Request/response meta fields
+        if (metadata.isRequest())
+        {
+            MetaData.Request request = (MetaData.Request)metadata;
+
+            // TODO optimise these to avoid HttpField creation
+            String scheme=request.getURI().getScheme();
+            encode(buffer,new HttpField(HttpHeader.C_SCHEME,scheme==null?HttpScheme.HTTP.asString():scheme));
+            encode(buffer,new HttpField(HttpHeader.C_METHOD,request.getMethod()));
+            encode(buffer,new HttpField(HttpHeader.C_AUTHORITY,request.getURI().getAuthority()));
+            encode(buffer,new HttpField(HttpHeader.C_PATH,request.getURI().getPathQuery()));
+        }
+        else if (metadata.isResponse())
+        {
+            MetaData.Response response = (MetaData.Response)metadata;
+            int code=response.getStatus();
+            HttpField status = code<__status.length?__status[code]:null;
+            if (status==null)
+                status=new HttpField.IntValueHttpField(HttpHeader.C_STATUS,code);
+            encode(buffer,status);
+        }
+
+        // Add all the other fields
+        for (HttpField field : metadata)
+            encode(buffer,field);
+
+        // Check size
+        if (_maxHeaderListSize>0 && _headerListSize>_maxHeaderListSize)
+        {
+            LOG.warn("Header list size too large {} > {} for {}",_headerListSize,_maxHeaderListSize);
+            if (LOG.isDebugEnabled())
+                LOG.debug("metadata={}",metadata);
+        }
+        
+        if (LOG.isDebugEnabled())
+            LOG.debug(String.format("CtxTbl[%x] encoded %d octets",_context.hashCode(), buffer.position() - pos));
+    }
+
+    public void encodeMaxDynamicTableSize(ByteBuffer buffer, int maxDynamicTableSize)
+    {
+        if (maxDynamicTableSize>_remoteMaxDynamicTableSize)
+            throw new IllegalArgumentException();
+        buffer.put((byte)0x20);
+        NBitInteger.encode(buffer,5,maxDynamicTableSize);
+        _context.resize(maxDynamicTableSize);
+    }
+
+    public void encode(ByteBuffer buffer, HttpField field)
+    {
+        if (field.getValue()==null)
+            field = new HttpField(field.getHeader(),field.getName(),"");
+        
+        int field_size = field.getName().length() + field.getValue().length();
+        _headerListSize+=field_size+32;
+        
+        final int p=_debug?buffer.position():-1;
+
+        String encoding=null;
+
+        // Is there an entry for the field?
+        Entry entry = _context.get(field);
+        if (entry!=null)
+        {
+            // Known field entry, so encode it as indexed
+            if (entry.isStatic())
+            {
+                buffer.put(((StaticEntry)entry).getEncodedField());
+                if (_debug)
+                    encoding="IdxFieldS1";
+            }
+            else
+            {
+                int index=_context.index(entry);
+                buffer.put((byte)0x80);
+                NBitInteger.encode(buffer,7,index);
+                if (_debug)
+                    encoding="IdxField"+(entry.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(7,index));
+            }
+        }
+        else
+        {
+            // Unknown field entry, so we will have to send literally.
+            final boolean indexed;
+
+            // But do we know it's name?
+            HttpHeader header = field.getHeader();
+
+            // Select encoding strategy
+            if (header==null)
+            {
+                // Select encoding strategy for unknown header names
+                Entry name = _context.get(field.getName());
+
+                if (field instanceof PreEncodedHttpField)
+                {
+                    int i=buffer.position();
+                    ((PreEncodedHttpField)field).putTo(buffer,HttpVersion.HTTP_2);
+                    byte b=buffer.get(i);
+                    indexed=b<0||b>=0x40;
+                    if (_debug)
+                        encoding=indexed?"PreEncodedIdx":"PreEncoded";
+                }
+                // has the custom header name been seen before?
+                else if (name==null)
+                {
+                    // unknown name and value, so let's index this just in case it is
+                    // the first time we have seen a custom name or a custom field.
+                    // unless the name is changing, this is worthwhile
+                    indexed=true;
+                    encodeName(buffer,(byte)0x40,6,field.getName(),null);
+                    encodeValue(buffer,true,field.getValue());
+                    if (_debug)
+                        encoding="LitHuffNHuffVIdx";
+                }
+                else
+                {
+                    // known custom name, but unknown value.
+                    // This is probably a custom field with changing value, so don't index.
+                    indexed=false;
+                    encodeName(buffer,(byte)0x00,4,field.getName(),null);
+                    encodeValue(buffer,true,field.getValue());
+                    if (_debug)
+                        encoding="LitHuffNHuffV!Idx";
+                }
+            }
+            else
+            {
+                // Select encoding strategy for known header names
+                Entry name = _context.get(header);
+
+                if (field instanceof PreEncodedHttpField)
+                {
+                    // Preencoded field
+                    int i=buffer.position();
+                    ((PreEncodedHttpField)field).putTo(buffer,HttpVersion.HTTP_2);
+                    byte b=buffer.get(i);
+                    indexed=b<0||b>=0x40;
+                    if (_debug)
+                        encoding=indexed?"PreEncodedIdx":"PreEncoded";
+                }
+                else if (__DO_NOT_INDEX.contains(header))
+                {
+                    // Non indexed field
+                    indexed=false;
+                    boolean never_index=__NEVER_INDEX.contains(header);
+                    boolean huffman=!__DO_NOT_HUFFMAN.contains(header);
+                    encodeName(buffer,never_index?(byte)0x10:(byte)0x00,4,header.asString(),name);
+                    encodeValue(buffer,huffman,field.getValue());
+
+                    if (_debug)
+                        encoding="Lit"+
+                                ((name==null)?"HuffN":("IdxN"+(name.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(4,_context.index(name)))))+
+                                (huffman?"HuffV":"LitV")+
+                                (indexed?"Idx":(never_index?"!!Idx":"!Idx"));
+                }
+                else if (field_size>=_context.getMaxDynamicTableSize() || header==HttpHeader.CONTENT_LENGTH && field.getValue().length()>2)
+                {
+                    // Non indexed if field too large or a content length for 3 digits or more
+                    indexed=false;
+                    encodeName(buffer,(byte)0x00,4,header.asString(),name);
+                    encodeValue(buffer,true,field.getValue());
+                    if (_debug)
+                        encoding="LitIdxNS"+(1+NBitInteger.octectsNeeded(4,_context.index(name)))+"HuffV!Idx";
+                }
+                else
+                {
+                    // indexed
+                    indexed=true;
+                    boolean huffman=!__DO_NOT_HUFFMAN.contains(header);
+                    encodeName(buffer,(byte)0x40,6,header.asString(),name);
+                    encodeValue(buffer,huffman,field.getValue());
+                    if (_debug)
+                        encoding=((name==null)?"LitHuffN":("LitIdxN"+(name.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(6,_context.index(name)))))+
+                                (huffman?"HuffVIdx":"LitVIdx");
+                }
+            }
+
+            // If we want the field referenced, then we add it to our
+            // table and reference set.
+            if (indexed)
+                if (_context.add(field)==null)
+                    throw new IllegalStateException();
+        }
+
+        if (_debug)
+        {
+            int e=buffer.position();
+            if (LOG.isDebugEnabled())
+                LOG.debug("encode {}:'{}' to '{}'",encoding,field,TypeUtil.toHexString(buffer.array(),buffer.arrayOffset()+p,e-p));
+        }
+    }
+
+    private void encodeName(ByteBuffer buffer, byte mask, int bits, String name, Entry entry)
+    {
+        buffer.put(mask);
+        if (entry==null)
+        {
+            // leave name index bits as 0
+            // Encode the name always with lowercase huffman
+            buffer.put((byte)0x80);
+            NBitInteger.encode(buffer,7,Huffman.octetsNeededLC(name));
+            Huffman.encodeLC(buffer,name);
+        }
+        else
+        {
+            NBitInteger.encode(buffer,bits,_context.index(entry));
+        }
+    }
+
+    static void encodeValue(ByteBuffer buffer, boolean huffman, String value)
+    {
+        if (huffman)
+        {
+            // huffman literal value
+            buffer.put((byte)0x80);
+            NBitInteger.encode(buffer,7,Huffman.octetsNeeded(value));
+            Huffman.encode(buffer,value);
+        }
+        else
+        {
+            // add literal assuming iso_8859_1
+            buffer.put((byte)0x00);
+            NBitInteger.encode(buffer,7,value.length());
+            for (int i=0;i<value.length();i++)
+            {
+                char c=value.charAt(i);
+                if (c<' '|| c>127)
+                    throw new IllegalArgumentException();
+                buffer.put((byte)c);
+            }
+        }
+    }
+}
diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackFieldPreEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackFieldPreEncoder.java
new file mode 100644
index 0000000..15e61cd
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackFieldPreEncoder.java
@@ -0,0 +1,97 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http2.hpack;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http.HttpFieldPreEncoder;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.util.BufferUtil;
+
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public class HpackFieldPreEncoder implements HttpFieldPreEncoder
+{
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.HttpFieldPreEncoder#getHttpVersion()
+     */
+    @Override
+    public HttpVersion getHttpVersion()
+    {
+        return HttpVersion.HTTP_2;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.HttpFieldPreEncoder#getEncodedField(org.eclipse.jetty.http.HttpHeader, java.lang.String, java.lang.String)
+     */
+    @Override
+    public byte[] getEncodedField(HttpHeader header, String name, String value)
+    {
+        boolean not_indexed=HpackEncoder.__DO_NOT_INDEX.contains(header);
+        
+        ByteBuffer buffer = BufferUtil.allocate(name.length()+value.length()+10);
+        BufferUtil.clearToFill(buffer);
+        boolean huffman;
+        int bits;
+        
+        if (not_indexed)
+        {
+            // Non indexed field
+            boolean never_index=HpackEncoder.__NEVER_INDEX.contains(header);
+            huffman=!HpackEncoder.__DO_NOT_HUFFMAN.contains(header);
+            buffer.put(never_index?(byte)0x10:(byte)0x00);
+            bits=4;
+        }
+        else if (header==HttpHeader.CONTENT_LENGTH && value.length()>1)
+        {
+            // Non indexed content length for 2 digits or more
+            buffer.put((byte)0x00);
+            huffman=true;
+            bits=4;
+        }
+        else
+        {
+            // indexed
+            buffer.put((byte)0x40);
+            huffman=!HpackEncoder.__DO_NOT_HUFFMAN.contains(header);
+            bits=6;
+        }
+        
+        int name_idx=HpackContext.staticIndex(header);
+        if (name_idx>0)
+            NBitInteger.encode(buffer,bits,name_idx);
+        else
+        {
+            buffer.put((byte)0x80);
+            NBitInteger.encode(buffer,7,Huffman.octetsNeededLC(name));
+            Huffman.encodeLC(buffer,name);
+        }
+
+        HpackEncoder.encodeValue(buffer,huffman,value);
+        
+        BufferUtil.flipToFlush(buffer,0);
+        return BufferUtil.toArray(buffer);
+    }
+}
diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java
new file mode 100644
index 0000000..b298e4f
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java
@@ -0,0 +1,480 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.hpack;
+
+import java.nio.ByteBuffer;
+
+public class Huffman
+{
+
+    // Appendix C: Huffman Codes
+    // http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-C
+    static final int[][] CODES =
+    { 
+        /*    (  0)  |11111111|11000                      */       {0x1ff8,13},
+        /*    (  1)  |11111111|11111111|1011000           */     {0x7fffd8,23},
+        /*    (  2)  |11111111|11111111|11111110|0010     */    {0xfffffe2,28},
+        /*    (  3)  |11111111|11111111|11111110|0011     */    {0xfffffe3,28},
+        /*    (  4)  |11111111|11111111|11111110|0100     */    {0xfffffe4,28},
+        /*    (  5)  |11111111|11111111|11111110|0101     */    {0xfffffe5,28},
+        /*    (  6)  |11111111|11111111|11111110|0110     */    {0xfffffe6,28},
+        /*    (  7)  |11111111|11111111|11111110|0111     */    {0xfffffe7,28},
+        /*    (  8)  |11111111|11111111|11111110|1000     */    {0xfffffe8,28},
+        /*    (  9)  |11111111|11111111|11101010          */     {0xffffea,24},
+        /*    ( 10)  |11111111|11111111|11111111|111100   */   {0x3ffffffc,30},
+        /*    ( 11)  |11111111|11111111|11111110|1001     */    {0xfffffe9,28},
+        /*    ( 12)  |11111111|11111111|11111110|1010     */    {0xfffffea,28},
+        /*    ( 13)  |11111111|11111111|11111111|111101   */   {0x3ffffffd,30},
+        /*    ( 14)  |11111111|11111111|11111110|1011     */    {0xfffffeb,28},
+        /*    ( 15)  |11111111|11111111|11111110|1100     */    {0xfffffec,28},
+        /*    ( 16)  |11111111|11111111|11111110|1101     */    {0xfffffed,28},
+        /*    ( 17)  |11111111|11111111|11111110|1110     */    {0xfffffee,28},
+        /*    ( 18)  |11111111|11111111|11111110|1111     */    {0xfffffef,28},
+        /*    ( 19)  |11111111|11111111|11111111|0000     */    {0xffffff0,28},
+        /*    ( 20)  |11111111|11111111|11111111|0001     */    {0xffffff1,28},
+        /*    ( 21)  |11111111|11111111|11111111|0010     */    {0xffffff2,28},
+        /*    ( 22)  |11111111|11111111|11111111|111110   */   {0x3ffffffe,30},
+        /*    ( 23)  |11111111|11111111|11111111|0011     */    {0xffffff3,28},
+        /*    ( 24)  |11111111|11111111|11111111|0100     */    {0xffffff4,28},
+        /*    ( 25)  |11111111|11111111|11111111|0101     */    {0xffffff5,28},
+        /*    ( 26)  |11111111|11111111|11111111|0110     */    {0xffffff6,28},
+        /*    ( 27)  |11111111|11111111|11111111|0111     */    {0xffffff7,28},
+        /*    ( 28)  |11111111|11111111|11111111|1000     */    {0xffffff8,28},
+        /*    ( 29)  |11111111|11111111|11111111|1001     */    {0xffffff9,28},
+        /*    ( 30)  |11111111|11111111|11111111|1010     */    {0xffffffa,28},
+        /*    ( 31)  |11111111|11111111|11111111|1011     */    {0xffffffb,28},
+        /*' ' ( 32)  |010100                              */         {0x14, 6},
+        /*'!' ( 33)  |11111110|00                         */        {0x3f8,10},
+        /*'"' ( 34)  |11111110|01                         */        {0x3f9,10},
+        /*'#' ( 35)  |11111111|1010                       */        {0xffa,12},
+        /*'$' ( 36)  |11111111|11001                      */       {0x1ff9,13},
+        /*'%' ( 37)  |010101                              */         {0x15, 6},
+        /*'&' ( 38)  |11111000                            */         {0xf8, 8},
+        /*''' ( 39)  |11111111|010                        */        {0x7fa,11},
+        /*'(' ( 40)  |11111110|10                         */        {0x3fa,10},
+        /*')' ( 41)  |11111110|11                         */        {0x3fb,10},
+        /*'*' ( 42)  |11111001                            */         {0xf9, 8},
+        /*'+' ( 43)  |11111111|011                        */        {0x7fb,11},
+        /*',' ( 44)  |11111010                            */         {0xfa, 8},
+        /*'-' ( 45)  |010110                              */         {0x16, 6},
+        /*'.' ( 46)  |010111                              */         {0x17, 6},
+        /*'/' ( 47)  |011000                              */         {0x18, 6},
+        /*'0' ( 48)  |00000                               */          {0x0, 5},
+        /*'1' ( 49)  |00001                               */          {0x1, 5},
+        /*'2' ( 50)  |00010                               */          {0x2, 5},
+        /*'3' ( 51)  |011001                              */         {0x19, 6},
+        /*'4' ( 52)  |011010                              */         {0x1a, 6},
+        /*'5' ( 53)  |011011                              */         {0x1b, 6},
+        /*'6' ( 54)  |011100                              */         {0x1c, 6},
+        /*'7' ( 55)  |011101                              */         {0x1d, 6},
+        /*'8' ( 56)  |011110                              */         {0x1e, 6},
+        /*'9' ( 57)  |011111                              */         {0x1f, 6},
+        /*':' ( 58)  |1011100                             */         {0x5c, 7},
+        /*';' ( 59)  |11111011                            */         {0xfb, 8},
+        /*'<' ( 60)  |11111111|1111100                    */       {0x7ffc,15},
+        /*'=' ( 61)  |100000                              */         {0x20, 6},
+        /*'>' ( 62)  |11111111|1011                       */        {0xffb,12},
+        /*'?' ( 63)  |11111111|00                         */        {0x3fc,10},
+        /*'@' ( 64)  |11111111|11010                      */       {0x1ffa,13},
+        /*'A' ( 65)  |100001                              */         {0x21, 6},
+        /*'B' ( 66)  |1011101                             */         {0x5d, 7},
+        /*'C' ( 67)  |1011110                             */         {0x5e, 7},
+        /*'D' ( 68)  |1011111                             */         {0x5f, 7},
+        /*'E' ( 69)  |1100000                             */         {0x60, 7},
+        /*'F' ( 70)  |1100001                             */         {0x61, 7},
+        /*'G' ( 71)  |1100010                             */         {0x62, 7},
+        /*'H' ( 72)  |1100011                             */         {0x63, 7},
+        /*'I' ( 73)  |1100100                             */         {0x64, 7},
+        /*'J' ( 74)  |1100101                             */         {0x65, 7},
+        /*'K' ( 75)  |1100110                             */         {0x66, 7},
+        /*'L' ( 76)  |1100111                             */         {0x67, 7},
+        /*'M' ( 77)  |1101000                             */         {0x68, 7},
+        /*'N' ( 78)  |1101001                             */         {0x69, 7},
+        /*'O' ( 79)  |1101010                             */         {0x6a, 7},
+        /*'P' ( 80)  |1101011                             */         {0x6b, 7},
+        /*'Q' ( 81)  |1101100                             */         {0x6c, 7},
+        /*'R' ( 82)  |1101101                             */         {0x6d, 7},
+        /*'S' ( 83)  |1101110                             */         {0x6e, 7},
+        /*'T' ( 84)  |1101111                             */         {0x6f, 7},
+        /*'U' ( 85)  |1110000                             */         {0x70, 7},
+        /*'V' ( 86)  |1110001                             */         {0x71, 7},
+        /*'W' ( 87)  |1110010                             */         {0x72, 7},
+        /*'X' ( 88)  |11111100                            */         {0xfc, 8},
+        /*'Y' ( 89)  |1110011                             */         {0x73, 7},
+        /*'Z' ( 90)  |11111101                            */         {0xfd, 8},
+        /*'[' ( 91)  |11111111|11011                      */       {0x1ffb,13},
+        /*'\' ( 92)  |11111111|11111110|000               */      {0x7fff0,19},
+        /*']' ( 93)  |11111111|11100                      */       {0x1ffc,13},
+        /*'^' ( 94)  |11111111|111100                     */       {0x3ffc,14},
+        /*'_' ( 95)  |100010                              */         {0x22, 6},
+        /*'`' ( 96)  |11111111|1111101                    */       {0x7ffd,15},
+        /*'a' ( 97)  |00011                               */          {0x3, 5},
+        /*'b' ( 98)  |100011                              */         {0x23, 6},
+        /*'c' ( 99)  |00100                               */          {0x4, 5},
+        /*'d' (100)  |100100                              */         {0x24, 6},
+        /*'e' (101)  |00101                               */          {0x5, 5},
+        /*'f' (102)  |100101                              */         {0x25, 6},
+        /*'g' (103)  |100110                              */         {0x26, 6},
+        /*'h' (104)  |100111                              */         {0x27, 6},
+        /*'i' (105)  |00110                               */          {0x6, 5},
+        /*'j' (106)  |1110100                             */         {0x74, 7},
+        /*'k' (107)  |1110101                             */         {0x75, 7},
+        /*'l' (108)  |101000                              */         {0x28, 6},
+        /*'m' (109)  |101001                              */         {0x29, 6},
+        /*'n' (110)  |101010                              */         {0x2a, 6},
+        /*'o' (111)  |00111                               */          {0x7, 5},
+        /*'p' (112)  |101011                              */         {0x2b, 6},
+        /*'q' (113)  |1110110                             */         {0x76, 7},
+        /*'r' (114)  |101100                              */         {0x2c, 6},
+        /*'s' (115)  |01000                               */          {0x8, 5},
+        /*'t' (116)  |01001                               */          {0x9, 5},
+        /*'u' (117)  |101101                              */         {0x2d, 6},
+        /*'v' (118)  |1110111                             */         {0x77, 7},
+        /*'w' (119)  |1111000                             */         {0x78, 7},
+        /*'x' (120)  |1111001                             */         {0x79, 7},
+        /*'y' (121)  |1111010                             */         {0x7a, 7},
+        /*'z' (122)  |1111011                             */         {0x7b, 7},
+        /*'{' (123)  |11111111|1111110                    */       {0x7ffe,15},
+        /*'|' (124)  |11111111|100                        */        {0x7fc,11},
+        /*'}' (125)  |11111111|111101                     */       {0x3ffd,14},
+        /*'~' (126)  |11111111|11101                      */       {0x1ffd,13},
+        /*    (127)  |11111111|11111111|11111111|1100     */    {0xffffffc,28},
+        /*    (128)  |11111111|11111110|0110              */      {0xfffe6,20},
+        /*    (129)  |11111111|11111111|010010            */     {0x3fffd2,22},
+        /*    (130)  |11111111|11111110|0111              */      {0xfffe7,20},
+        /*    (131)  |11111111|11111110|1000              */      {0xfffe8,20},
+        /*    (132)  |11111111|11111111|010011            */     {0x3fffd3,22},
+        /*    (133)  |11111111|11111111|010100            */     {0x3fffd4,22},
+        /*    (134)  |11111111|11111111|010101            */     {0x3fffd5,22},
+        /*    (135)  |11111111|11111111|1011001           */     {0x7fffd9,23},
+        /*    (136)  |11111111|11111111|010110            */     {0x3fffd6,22},
+        /*    (137)  |11111111|11111111|1011010           */     {0x7fffda,23},
+        /*    (138)  |11111111|11111111|1011011           */     {0x7fffdb,23},
+        /*    (139)  |11111111|11111111|1011100           */     {0x7fffdc,23},
+        /*    (140)  |11111111|11111111|1011101           */     {0x7fffdd,23},
+        /*    (141)  |11111111|11111111|1011110           */     {0x7fffde,23},
+        /*    (142)  |11111111|11111111|11101011          */     {0xffffeb,24},
+        /*    (143)  |11111111|11111111|1011111           */     {0x7fffdf,23},
+        /*    (144)  |11111111|11111111|11101100          */     {0xffffec,24},
+        /*    (145)  |11111111|11111111|11101101          */     {0xffffed,24},
+        /*    (146)  |11111111|11111111|010111            */     {0x3fffd7,22},
+        /*    (147)  |11111111|11111111|1100000           */     {0x7fffe0,23},
+        /*    (148)  |11111111|11111111|11101110          */     {0xffffee,24},
+        /*    (149)  |11111111|11111111|1100001           */     {0x7fffe1,23},
+        /*    (150)  |11111111|11111111|1100010           */     {0x7fffe2,23},
+        /*    (151)  |11111111|11111111|1100011           */     {0x7fffe3,23},
+        /*    (152)  |11111111|11111111|1100100           */     {0x7fffe4,23},
+        /*    (153)  |11111111|11111110|11100             */     {0x1fffdc,21},
+        /*    (154)  |11111111|11111111|011000            */     {0x3fffd8,22},
+        /*    (155)  |11111111|11111111|1100101           */     {0x7fffe5,23},
+        /*    (156)  |11111111|11111111|011001            */     {0x3fffd9,22},
+        /*    (157)  |11111111|11111111|1100110           */     {0x7fffe6,23},
+        /*    (158)  |11111111|11111111|1100111           */     {0x7fffe7,23},
+        /*    (159)  |11111111|11111111|11101111          */     {0xffffef,24},
+        /*    (160)  |11111111|11111111|011010            */     {0x3fffda,22},
+        /*    (161)  |11111111|11111110|11101             */     {0x1fffdd,21},
+        /*    (162)  |11111111|11111110|1001              */      {0xfffe9,20},
+        /*    (163)  |11111111|11111111|011011            */     {0x3fffdb,22},
+        /*    (164)  |11111111|11111111|011100            */     {0x3fffdc,22},
+        /*    (165)  |11111111|11111111|1101000           */     {0x7fffe8,23},
+        /*    (166)  |11111111|11111111|1101001           */     {0x7fffe9,23},
+        /*    (167)  |11111111|11111110|11110             */     {0x1fffde,21},
+        /*    (168)  |11111111|11111111|1101010           */     {0x7fffea,23},
+        /*    (169)  |11111111|11111111|011101            */     {0x3fffdd,22},
+        /*    (170)  |11111111|11111111|011110            */     {0x3fffde,22},
+        /*    (171)  |11111111|11111111|11110000          */     {0xfffff0,24},
+        /*    (172)  |11111111|11111110|11111             */     {0x1fffdf,21},
+        /*    (173)  |11111111|11111111|011111            */     {0x3fffdf,22},
+        /*    (174)  |11111111|11111111|1101011           */     {0x7fffeb,23},
+        /*    (175)  |11111111|11111111|1101100           */     {0x7fffec,23},
+        /*    (176)  |11111111|11111111|00000             */     {0x1fffe0,21},
+        /*    (177)  |11111111|11111111|00001             */     {0x1fffe1,21},
+        /*    (178)  |11111111|11111111|100000            */     {0x3fffe0,22},
+        /*    (179)  |11111111|11111111|00010             */     {0x1fffe2,21},
+        /*    (180)  |11111111|11111111|1101101           */     {0x7fffed,23},
+        /*    (181)  |11111111|11111111|100001            */     {0x3fffe1,22},
+        /*    (182)  |11111111|11111111|1101110           */     {0x7fffee,23},
+        /*    (183)  |11111111|11111111|1101111           */     {0x7fffef,23},
+        /*    (184)  |11111111|11111110|1010              */      {0xfffea,20},
+        /*    (185)  |11111111|11111111|100010            */     {0x3fffe2,22},
+        /*    (186)  |11111111|11111111|100011            */     {0x3fffe3,22},
+        /*    (187)  |11111111|11111111|100100            */     {0x3fffe4,22},
+        /*    (188)  |11111111|11111111|1110000           */     {0x7ffff0,23},
+        /*    (189)  |11111111|11111111|100101            */     {0x3fffe5,22},
+        /*    (190)  |11111111|11111111|100110            */     {0x3fffe6,22},
+        /*    (191)  |11111111|11111111|1110001           */     {0x7ffff1,23},
+        /*    (192)  |11111111|11111111|11111000|00       */    {0x3ffffe0,26},
+        /*    (193)  |11111111|11111111|11111000|01       */    {0x3ffffe1,26},
+        /*    (194)  |11111111|11111110|1011              */      {0xfffeb,20},
+        /*    (195)  |11111111|11111110|001               */      {0x7fff1,19},
+        /*    (196)  |11111111|11111111|100111            */     {0x3fffe7,22},
+        /*    (197)  |11111111|11111111|1110010           */     {0x7ffff2,23},
+        /*    (198)  |11111111|11111111|101000            */     {0x3fffe8,22},
+        /*    (199)  |11111111|11111111|11110110|0        */    {0x1ffffec,25},
+        /*    (200)  |11111111|11111111|11111000|10       */    {0x3ffffe2,26},
+        /*    (201)  |11111111|11111111|11111000|11       */    {0x3ffffe3,26},
+        /*    (202)  |11111111|11111111|11111001|00       */    {0x3ffffe4,26},
+        /*    (203)  |11111111|11111111|11111011|110      */    {0x7ffffde,27},
+        /*    (204)  |11111111|11111111|11111011|111      */    {0x7ffffdf,27},
+        /*    (205)  |11111111|11111111|11111001|01       */    {0x3ffffe5,26},
+        /*    (206)  |11111111|11111111|11110001          */     {0xfffff1,24},
+        /*    (207)  |11111111|11111111|11110110|1        */    {0x1ffffed,25},
+        /*    (208)  |11111111|11111110|010               */      {0x7fff2,19},
+        /*    (209)  |11111111|11111111|00011             */     {0x1fffe3,21},
+        /*    (210)  |11111111|11111111|11111001|10       */    {0x3ffffe6,26},
+        /*    (211)  |11111111|11111111|11111100|000      */    {0x7ffffe0,27},
+        /*    (212)  |11111111|11111111|11111100|001      */    {0x7ffffe1,27},
+        /*    (213)  |11111111|11111111|11111001|11       */    {0x3ffffe7,26},
+        /*    (214)  |11111111|11111111|11111100|010      */    {0x7ffffe2,27},
+        /*    (215)  |11111111|11111111|11110010          */     {0xfffff2,24},
+        /*    (216)  |11111111|11111111|00100             */     {0x1fffe4,21},
+        /*    (217)  |11111111|11111111|00101             */     {0x1fffe5,21},
+        /*    (218)  |11111111|11111111|11111010|00       */    {0x3ffffe8,26},
+        /*    (219)  |11111111|11111111|11111010|01       */    {0x3ffffe9,26},
+        /*    (220)  |11111111|11111111|11111111|1101     */    {0xffffffd,28},
+        /*    (221)  |11111111|11111111|11111100|011      */    {0x7ffffe3,27},
+        /*    (222)  |11111111|11111111|11111100|100      */    {0x7ffffe4,27},
+        /*    (223)  |11111111|11111111|11111100|101      */    {0x7ffffe5,27},
+        /*    (224)  |11111111|11111110|1100              */      {0xfffec,20},
+        /*    (225)  |11111111|11111111|11110011          */     {0xfffff3,24},
+        /*    (226)  |11111111|11111110|1101              */      {0xfffed,20},
+        /*    (227)  |11111111|11111111|00110             */     {0x1fffe6,21},
+        /*    (228)  |11111111|11111111|101001            */     {0x3fffe9,22},
+        /*    (229)  |11111111|11111111|00111             */     {0x1fffe7,21},
+        /*    (230)  |11111111|11111111|01000             */     {0x1fffe8,21},
+        /*    (231)  |11111111|11111111|1110011           */     {0x7ffff3,23},
+        /*    (232)  |11111111|11111111|101010            */     {0x3fffea,22},
+        /*    (233)  |11111111|11111111|101011            */     {0x3fffeb,22},
+        /*    (234)  |11111111|11111111|11110111|0        */    {0x1ffffee,25},
+        /*    (235)  |11111111|11111111|11110111|1        */    {0x1ffffef,25},
+        /*    (236)  |11111111|11111111|11110100          */     {0xfffff4,24},
+        /*    (237)  |11111111|11111111|11110101          */     {0xfffff5,24},
+        /*    (238)  |11111111|11111111|11111010|10       */    {0x3ffffea,26},
+        /*    (239)  |11111111|11111111|1110100           */     {0x7ffff4,23},
+        /*    (240)  |11111111|11111111|11111010|11       */    {0x3ffffeb,26},
+        /*    (241)  |11111111|11111111|11111100|110      */    {0x7ffffe6,27},
+        /*    (242)  |11111111|11111111|11111011|00       */    {0x3ffffec,26},
+        /*    (243)  |11111111|11111111|11111011|01       */    {0x3ffffed,26},
+        /*    (244)  |11111111|11111111|11111100|111      */    {0x7ffffe7,27},
+        /*    (245)  |11111111|11111111|11111101|000      */    {0x7ffffe8,27},
+        /*    (246)  |11111111|11111111|11111101|001      */    {0x7ffffe9,27},
+        /*    (247)  |11111111|11111111|11111101|010      */    {0x7ffffea,27},
+        /*    (248)  |11111111|11111111|11111101|011      */    {0x7ffffeb,27},
+        /*    (249)  |11111111|11111111|11111111|1110     */    {0xffffffe,28},
+        /*    (250)  |11111111|11111111|11111101|100      */    {0x7ffffec,27},
+        /*    (251)  |11111111|11111111|11111101|101      */    {0x7ffffed,27},
+        /*    (252)  |11111111|11111111|11111101|110      */    {0x7ffffee,27},
+        /*    (253)  |11111111|11111111|11111101|111      */    {0x7ffffef,27},
+        /*    (254)  |11111111|11111111|11111110|000      */    {0x7fffff0,27},
+        /*    (255)  |11111111|11111111|11111011|10       */    {0x3ffffee,26},
+        /*EOS (256)  |11111111|11111111|11111111|111111   */   {0x3fffffff,30},
+    };
+
+    static final int[][] LCCODES = new int[CODES.length][];
+    
+    // Huffman decode tree stored in a flattened char array for good 
+    // locality of reference.
+    static final char[] tree;
+    static final char[] rowsym;
+    static final byte[] rowbits;
+
+    // Build the Huffman lookup tree and LC TABLE
+    static 
+    {
+        System.arraycopy(CODES,0,LCCODES,0,CODES.length);
+        for (int i='A';i<='Z';i++)
+            LCCODES[i]=LCCODES['a'+i-'A'];
+        
+        int r=0;
+        for (int i=0;i<CODES.length;i++)
+            r+=(CODES[i][1]+7)/8;
+        tree=new char[r*256];
+        rowsym=new char[r];
+        rowbits=new byte[r];
+
+        r=0;
+        for (int sym = 0; sym < CODES.length; sym++) 
+        {
+            int code = CODES[sym][0];
+            int len = CODES[sym][1];
+
+            int current = 0;
+
+            while (len > 8) 
+            {
+                len -= 8;
+                int i = ((code >>> len) & 0xFF);
+
+                int t=current*256+i;
+                current = tree[t];
+                if (current == 0)
+                {
+                    tree[t] = (char)++r;
+                    current=r;
+                }
+            }
+
+            int terminal = ++r;
+            rowsym[r]=(char)sym;
+            int b = len & 0x07;
+            int terminalBits = b == 0?8:b;
+
+            rowbits[r]=(byte)terminalBits;
+            int shift = 8 - len;
+            int start = current*256 + ((code << shift) & 0xFF);
+            int end = start + (1<<shift);
+            for (int i = start; i < end; i++)
+                tree[i]=(char)terminal;
+        }
+    }
+
+    public static String decode(ByteBuffer buffer)
+    {  
+        return decode(buffer,buffer.remaining());
+    }
+
+    public static String decode(ByteBuffer buffer,int length)
+    {        
+        StringBuilder out = new StringBuilder(length*2);
+        int node = 0;
+        int current = 0;
+        int bits = 0;
+        
+        byte[] array = buffer.array();
+        int position=buffer.position();
+        int start=buffer.arrayOffset()+position;
+        int end=start+length;
+        buffer.position(position+length);
+        
+        for (int i=start; i<end; i++)
+        {
+            int b = array[i]&0xFF;
+            current = (current << 8) | b;
+            bits += 8;
+            while (bits >= 8) 
+            {
+                int c = (current >>> (bits - 8)) & 0xFF;
+                node = tree[node*256+c];
+                if (rowbits[node]!=0) 
+                {
+                    // terminal node
+                    out.append(rowsym[node]);
+                    bits -= rowbits[node];
+                    node = 0;
+                } 
+                else 
+                {
+                    // non-terminal node
+                    bits -= 8;
+                }
+            }
+        }
+
+        while (bits > 0) 
+        {
+            int c = (current << (8 - bits)) & 0xFF;
+            node = tree[node*256+c];
+            if (rowbits[node]==0 || rowbits[node] > bits) 
+                break;
+            
+            if (rowbits[node]==0)
+                throw new IllegalStateException();
+            
+            out.append(rowsym[node]);
+            bits -= rowbits[node];
+            node = 0;
+        }
+
+        return out.toString();
+    }
+
+    public static int octetsNeeded(String s)
+    {   
+        return octetsNeeded(CODES,s);
+    }
+    
+    public static void encode(ByteBuffer buffer,String s)
+    {
+        encode(CODES,buffer,s);
+    }
+    
+    public static int octetsNeededLC(String s)
+    {
+        return octetsNeeded(LCCODES,s);
+    }
+
+    public static void encodeLC(ByteBuffer buffer, String s)
+    {
+        encode(LCCODES,buffer,s);
+    }
+    
+    private static int octetsNeeded(final int[][] table,String s)
+    {   
+        int needed=0;
+        int len = s.length();
+        for (int i=0;i<len;i++)
+        {
+            char c=s.charAt(i);
+            if (c>=128 || c<' ')
+                throw new IllegalArgumentException();
+            needed += table[c][1];
+        }
+
+        return (needed+7) / 8;
+    }
+
+    private static void encode(final int[][] table,ByteBuffer buffer,String s)
+    {
+        long current = 0;
+        int n = 0;
+
+        byte[] array = buffer.array();
+        int p=buffer.arrayOffset()+buffer.position();
+
+        int len = s.length();
+        for (int i=0;i<len;i++)
+        {
+            char c=s.charAt(i);
+            if (c>=128 || c<' ')
+                throw new IllegalArgumentException();
+            int code = table[c][0];
+            int bits = table[c][1];
+
+            current <<= bits;
+            current |= code;
+            n += bits;
+
+            while (n >= 8) 
+            {
+                n -= 8;
+                array[p++]=(byte)(current >> n);
+            }
+        }
+
+        if (n > 0) 
+        {
+          current <<= (8 - n);
+          current |= (0xFF >>> n); 
+          array[p++]=(byte)current;
+        }
+        
+        buffer.position(p-buffer.arrayOffset());
+    }
+
+}
diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java
new file mode 100644
index 0000000..dd55f4c
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java
@@ -0,0 +1,196 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http2.hpack;
+
+
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+
+public class MetaDataBuilder
+{
+    private final int _maxSize;
+    private int _size;
+    private int _status;
+    private String _method;
+    private HttpScheme _scheme;
+    private HostPortHttpField _authority;
+    private String _path;
+    private long _contentLength=Long.MIN_VALUE;
+    private HttpFields _fields = new HttpFields(10);
+
+    /**
+     * @param maxHeadersSize The maximum size of the headers, expressed as total name and value characters.
+     */
+    MetaDataBuilder(int maxHeadersSize)
+    {
+        _maxSize=maxHeadersSize;
+    }
+
+    /** Get the maxSize.
+     * @return the maxSize
+     */
+    public int getMaxSize()
+    {
+        return _maxSize;
+    }
+
+    /** Get the size.
+     * @return the current size in bytes
+     */
+    public int getSize()
+    {
+        return _size;
+    }
+
+    public void emit(HttpField field)
+    {
+        HttpHeader header = field.getHeader();
+        String name = field.getName();
+        String value = field.getValue();
+        int field_size = name.length() + (value == null ? 0 : value.length());
+        _size+=field_size+32;
+        if (_size>_maxSize)
+            throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,"Header size "+_size+">"+_maxSize);
+
+        if (field instanceof StaticTableHttpField)
+        {
+            StaticTableHttpField staticField = (StaticTableHttpField)field;
+            switch(header)
+            {
+                case C_STATUS:
+                    _status=(Integer)staticField.getStaticValue();
+                    break;
+
+                case C_METHOD:
+                    _method=value;
+                    break;
+
+                case C_SCHEME:
+                    _scheme = (HttpScheme)staticField.getStaticValue();
+                    break;
+
+                default:
+                    throw new IllegalArgumentException(name);
+            }
+        }
+        else if (header!=null)
+        {
+            switch(header)
+            {
+                case C_STATUS:
+                    _status=field.getIntValue();
+                    break;
+
+                case C_METHOD:
+                    _method=value;
+                    break;
+
+                case C_SCHEME:
+                    if (value != null)
+                        _scheme = HttpScheme.CACHE.get(value);
+                    break;
+
+                case C_AUTHORITY:
+                    if (field instanceof HostPortHttpField)
+                        _authority = (HostPortHttpField)field;
+                    else if (value != null)
+                        _authority = new AuthorityHttpField(value);
+                    break;
+
+                case HOST:
+                    // :authority fields must come first.  If we have one, ignore the host header as far as authority goes.
+                    if (_authority==null)
+                    {
+                        if (field instanceof HostPortHttpField)
+                            _authority = (HostPortHttpField)field;
+                        else if (value != null)
+                            _authority = new AuthorityHttpField(value);
+                    }
+                    _fields.add(field);
+                    break;
+
+                case C_PATH:
+                    _path = value;
+                    break;
+
+                case CONTENT_LENGTH:
+                    _contentLength = field.getLongValue();
+                    _fields.add(field);
+                    break;
+
+                default:
+                    if (name.charAt(0)!=':')
+                        _fields.add(field);
+                    break;
+            }
+        }
+        else
+        {
+            if (name.charAt(0)!=':')
+                _fields.add(field);
+        }
+    }
+
+    public MetaData build()
+    {
+        try
+        {
+            HttpFields fields = _fields;
+            _fields = new HttpFields(Math.max(10,fields.size()+5));
+
+            if (_method!=null)
+                return new MetaData.Request(_method,_scheme,_authority,_path,HttpVersion.HTTP_2,fields,_contentLength);
+            if (_status!=0)
+                return new MetaData.Response(HttpVersion.HTTP_2,_status,fields,_contentLength);
+            return new MetaData(HttpVersion.HTTP_2,fields,_contentLength);
+        }
+        finally
+        {
+            _status=0;
+            _method=null;
+            _scheme=null;
+            _authority=null;
+            _path=null;
+            _size=0;
+            _contentLength=Long.MIN_VALUE;
+        }
+    }
+
+    /**
+     * Check that the max size will not be exceeded.
+     * @param length the length
+     * @param huffman the huffman name
+     */
+    public void checkSize(int length, boolean huffman)
+    {
+        // Apply a huffman fudge factor
+        if (huffman)
+            length=(length*4)/3;
+        if ((_size+length)>_maxSize)
+            throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,"Header size "+(_size+length)+">"+_maxSize);
+    }
+}
diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/NBitInteger.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/NBitInteger.java
new file mode 100644
index 0000000..185ec4b
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/NBitInteger.java
@@ -0,0 +1,151 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.hpack;
+
+import java.nio.ByteBuffer;
+
+public class NBitInteger
+{
+    public static int octectsNeeded(int n,int i)
+    {
+        if (n==8)
+        {
+            int nbits = 0xFF;
+            i=i-nbits;
+            if (i<0)
+                return 1;
+            if (i==0)
+                return 2;
+            int lz=Integer.numberOfLeadingZeros(i);
+            int log=32-lz;
+            return 1+(log+6)/7;
+        }
+        
+        int nbits = 0xFF >>> (8 - n);
+        i=i-nbits;
+        if (i<0)
+            return 0;
+        if (i==0)
+            return 1;
+        int lz=Integer.numberOfLeadingZeros(i);
+        int log=32-lz;
+        return (log+6)/7;
+    }
+    
+    public static void encode(ByteBuffer buf, int n, int i)
+    {
+        if (n==8)
+        {
+            if (i < 0xFF)
+            {
+                buf.put((byte)i);
+            }
+            else
+            {
+                buf.put((byte)0xFF);
+
+                int length = i - 0xFF;
+                while (true)
+                {
+                    if ((length & ~0x7F) == 0)
+                    {
+                        buf.put((byte)length);
+                        return;
+                    }
+                    else
+                    {
+                        buf.put((byte)((length & 0x7F) | 0x80));
+                        length >>>= 7;
+                    }
+                }
+            }
+        }
+        else
+        {
+            int p=buf.position()-1;
+            int bits = 0xFF >>> (8 - n);
+
+            if (i < bits)
+            {
+                buf.put(p,(byte)((buf.get(p)&~bits)|i));
+            }
+            else
+            {
+                buf.put(p,(byte)(buf.get(p)|bits));
+
+                int length = i - bits;
+                while (true)
+                {
+                    if ((length & ~0x7F) == 0)
+                    {
+                        buf.put((byte)length);
+                        return;
+                    }
+                    else
+                    {
+                        buf.put((byte)((length & 0x7F) | 0x80));
+                        length >>>= 7;
+                    }
+                }
+            }
+        }
+    }
+
+    public static int decode(ByteBuffer buffer, int n)
+    {
+        if (n==8)
+        {
+            int nbits = 0xFF;
+
+            int i=buffer.get()&0xff;
+            
+            if (i == nbits)
+            {       
+                int m=1;
+                int b;
+                do
+                {
+                    b = 0xff&buffer.get();
+                    i = i + (b&127) * m;
+                    m = m*128;
+                }
+                while ((b&128) == 128);
+            }
+            return i;
+        }
+        
+        int nbits = 0xFF >>> (8 - n);
+
+        int i=buffer.get(buffer.position()-1)&nbits;
+        
+        if (i == nbits)
+        {       
+            int m=1;
+            int b;
+            do
+            {
+                b = 0xff&buffer.get();
+                i = i + (b&127) * m;
+                m = m*128;
+            }
+            while ((b&128) == 128);
+        }
+        return i;
+    }
+}
diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/StaticTableHttpField.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/StaticTableHttpField.java
new file mode 100644
index 0000000..b7a1ca0
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/StaticTableHttpField.java
@@ -0,0 +1,61 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http2.hpack;
+
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
+
+/* ------------------------------------------------------------ */
+public class StaticTableHttpField extends HttpField
+{
+    private final Object _value;
+
+    public StaticTableHttpField(HttpHeader header, String name, String valueString, Object value)
+    {
+        super(header,name,valueString);
+        if (value==null)
+            throw new IllegalArgumentException();
+        _value=value;
+    }
+    
+    public StaticTableHttpField(HttpHeader header,String valueString, Object value)
+    {
+        this (header,header.asString(),valueString, value);
+    }
+    
+    public StaticTableHttpField(String name, String valueString, Object value)
+    {
+        super(name,valueString);
+        if (value==null)
+            throw new IllegalArgumentException();
+        _value=value;
+    }
+
+    public Object getStaticValue()
+    {
+        return _value;
+    }
+    
+    @Override
+    public String toString()
+    {
+        return super.toString()+"(evaluated)";
+    }
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder b/jetty-http2/http2-hpack/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder
new file mode 100644
index 0000000..4dca767
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder
@@ -0,0 +1 @@
+org.eclipse.jetty.http2.hpack.HpackFieldPreEncoder
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackContextTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackContextTest.java
new file mode 100644
index 0000000..674978f
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackContextTest.java
@@ -0,0 +1,451 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http2.hpack;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http2.hpack.HpackContext.Entry;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public class HpackContextTest
+{
+
+    @Test
+    public void testStaticName()
+    {
+        HpackContext ctx = new HpackContext(4096);
+        Entry entry=ctx.get(":method");
+        assertEquals(":method",entry.getHttpField().getName());
+        Assert.assertTrue(entry.isStatic());
+        Assert.assertThat(entry.toString(),Matchers.startsWith("{S,2,:method: "));
+    }
+    
+    @Test
+    public void testEmptyAdd()
+    {
+        HpackContext ctx = new HpackContext(0);
+        HttpField field = new HttpField("foo","bar");
+        Assert.assertNull(ctx.add(field));
+    }
+    
+    @Test
+    public void testTooBigAdd()
+    {
+        HpackContext ctx = new HpackContext(37);
+        HttpField field = new HttpField("foo","bar");
+        Assert.assertNull(ctx.add(field));
+    }
+    
+    @Test
+    public void testJustRight()
+    {
+        HpackContext ctx = new HpackContext(38);
+        HttpField field = new HttpField("foo","bar");
+        Entry entry=ctx.add(field);
+        Assert.assertNotNull(entry);
+        Assert.assertThat(entry.toString(),Matchers.startsWith("{D,0,foo: bar,"));
+    }
+    
+    @Test
+    public void testEvictOne()
+    {
+        HpackContext ctx = new HpackContext(38);
+        HttpField field0 = new HttpField("foo","bar");
+        
+        assertEquals(field0,ctx.add(field0).getHttpField());
+        assertEquals(field0,ctx.get("foo").getHttpField());
+        
+        HttpField field1 = new HttpField("xxx","yyy");
+        assertEquals(field1,ctx.add(field1).getHttpField());
+
+        assertNull(ctx.get(field0));
+        assertNull(ctx.get("foo"));
+        assertEquals(field1,ctx.get(field1).getHttpField());
+        assertEquals(field1,ctx.get("xxx").getHttpField());
+        
+    }
+
+    @Test
+    public void testEvictNames()
+    {
+        HpackContext ctx = new HpackContext(38*2);
+        HttpField[] field = 
+        {
+           new HttpField("name","v0"),
+           new HttpField("name","v1"),
+           new HttpField("name","v2"),
+           new HttpField("name","v3"),
+           new HttpField("name","v4"),
+           new HttpField("name","v5"),
+        };
+        
+        Entry[] entry = new Entry[field.length];
+        
+        // Add 2 name entries to fill table
+        for (int i=0;i<=1;i++)
+            entry[i]=ctx.add(field[i]);
+        
+        // check there is a name reference and it is the most recent added
+        assertEquals(entry[1],ctx.get("name"));
+
+        // Add 1 other entry to table and evict 1
+        ctx.add(new HttpField("xxx","yyy"));
+        
+        // check the name reference has been not been evicted
+        assertEquals(entry[1],ctx.get("name"));
+        
+        // Add 1 other entry to table and evict 1
+        ctx.add(new HttpField("foo","bar"));
+        
+        // name is evicted
+        assertNull(ctx.get("name"));
+    }
+    @Test
+    public void testGetAddStatic()
+    {
+        HpackContext ctx = new HpackContext(4096);
+
+        // Look for the field.  Should find static version.
+        HttpField methodGet = new HttpField(":method","GET");
+        assertEquals(methodGet,ctx.get(methodGet).getHttpField());
+        assertTrue(ctx.get(methodGet).isStatic());
+        
+        // Add static version to dynamic table
+        Entry e0=ctx.add(ctx.get(methodGet).getHttpField());
+        
+        // Look again and should see dynamic version
+        assertEquals(methodGet,ctx.get(methodGet).getHttpField());
+        assertFalse(methodGet==ctx.get(methodGet).getHttpField());
+        assertFalse(ctx.get(methodGet).isStatic());
+        
+        // Duplicates allows
+        Entry e1=ctx.add(ctx.get(methodGet).getHttpField());
+        
+        // Look again and should see dynamic version
+        assertEquals(methodGet,ctx.get(methodGet).getHttpField());
+        assertFalse(methodGet==ctx.get(methodGet).getHttpField());
+        assertFalse(ctx.get(methodGet).isStatic());
+        assertFalse(e0==e1);
+    }
+    
+    @Test
+    public void testGetAddStaticName()
+    {
+        HpackContext ctx = new HpackContext(4096);
+        HttpField methodOther = new HttpField(":method","OTHER");
+
+        // Look for the field by name.  Should find static version.
+        assertEquals(":method",ctx.get(":method").getHttpField().getName());
+        assertTrue(ctx.get(":method").isStatic());
+        
+        // Add dynamic entry with method
+        ctx.add(methodOther);
+        
+        // Look for the field by name.  Should find static version.
+        assertEquals(":method",ctx.get(":method").getHttpField().getName());
+        assertTrue(ctx.get(":method").isStatic()); 
+    }
+
+    @Test
+    public void testIndexes()
+    {
+        // Only enough space for 5 entries
+        HpackContext ctx = new HpackContext(38*5);
+        
+        HttpField methodPost = new HttpField(":method","POST");
+        HttpField[] field = 
+        {
+           new HttpField("fo0","b0r"),
+           new HttpField("fo1","b1r"),
+           new HttpField("fo2","b2r"),
+           new HttpField("fo3","b3r"),
+           new HttpField("fo4","b4r"),
+           new HttpField("fo5","b5r"),
+           new HttpField("fo6","b6r"),
+           new HttpField("fo7","b7r"),
+           new HttpField("fo8","b8r"),
+           new HttpField("fo9","b9r"),
+           new HttpField("foA","bAr"),
+        };
+        
+        Entry[] entry = new Entry[100];
+        
+        // Lookup the index of a static field
+        assertEquals(0,ctx.size());
+        assertEquals(":authority",ctx.get(1).getHttpField().getName());
+        assertEquals(3,ctx.index(ctx.get(methodPost)));
+        assertEquals(methodPost,ctx.get(3).getHttpField());
+        assertEquals("www-authenticate",ctx.get(61).getHttpField().getName());
+        assertEquals(null,ctx.get(62));
+        
+        // Add a single entry  
+        entry[0]=ctx.add(field[0]);
+        
+        // Check new entry is 62 
+        assertEquals(1,ctx.size());
+        assertEquals(62,ctx.index(entry[0]));
+        assertEquals(entry[0],ctx.get(62));
+        
+        // and statics still OK
+        assertEquals(":authority",ctx.get(1).getHttpField().getName());
+        assertEquals(3,ctx.index(ctx.get(methodPost)));
+        assertEquals(methodPost,ctx.get(3).getHttpField());
+        assertEquals("www-authenticate",ctx.get(61).getHttpField().getName());
+        assertEquals(null,ctx.get(62+ctx.size()));
+        
+
+        // Add 4 more entries
+        for (int i=1;i<=4;i++)  
+            entry[i]=ctx.add(field[i]);
+
+        // Check newest entry is at 62 oldest at 66
+        assertEquals(5,ctx.size());
+        int index=66;
+        for (int i=0;i<=4;i++)  
+        {
+            assertEquals(index,ctx.index(entry[i]));
+            assertEquals(entry[i],ctx.get(index));
+            index--;
+        }
+
+        // and statics still OK
+        assertEquals(":authority",ctx.get(1).getHttpField().getName());
+        assertEquals(3,ctx.index(ctx.get(methodPost)));
+        assertEquals(methodPost,ctx.get(3).getHttpField());
+        assertEquals("www-authenticate",ctx.get(61).getHttpField().getName());
+        assertEquals(null,ctx.get(62+ctx.size()));
+        
+        // add 1 more entry and this should cause an eviction!
+        entry[5]=ctx.add(field[5]);
+
+        // Check newest entry is at 1 oldest at 5
+        index=66;
+        for (int i=1;i<=5;i++)  
+        {
+            assertEquals(index,ctx.index(entry[i]));
+            assertEquals(entry[i],ctx.get(index));
+            index--;
+        }
+        // check entry 0 evicted
+        assertNull(ctx.get(field[0]));
+        assertEquals(0,ctx.index(entry[0]));
+
+        // and statics still OK
+        assertEquals(":authority",ctx.get(1).getHttpField().getName());
+        assertEquals(3,ctx.index(ctx.get(methodPost)));
+        assertEquals(methodPost,ctx.get(3).getHttpField());
+        assertEquals("www-authenticate",ctx.get(61).getHttpField().getName());
+        assertEquals(null,ctx.get(62+ctx.size()));
+        
+        // Add 4 more entries
+        for (int i=6;i<=9;i++)  
+            entry[i]=ctx.add(field[i]);
+        
+        // Check newest entry is at 1 oldest at 5
+        index=66;
+        for (int i=5;i<=9;i++)  
+        {
+            assertEquals(index,ctx.index(entry[i]));
+            assertEquals(entry[i],ctx.get(index));
+            index--;
+        }
+        // check entry 0-4 evicted
+        for (int i=0;i<=4;i++) 
+        {
+            assertNull(ctx.get(field[i]));
+            assertEquals(0,ctx.index(entry[i]));
+        }
+        
+
+        // Add new entries enough so that array queue will wrap
+        for (int i=10;i<=52;i++)
+            entry[i]=ctx.add(new HttpField("n"+i,"v"+i));
+
+        index=66;
+        for (int i=48;i<=52;i++)  
+        {
+            assertEquals(index,ctx.index(entry[i]));
+            assertEquals(entry[i],ctx.get(index));
+            index--;
+        }
+    }
+    
+
+    @Test
+    public void testResize()
+    {
+        // Only enough space for 5 entries
+        HpackContext ctx = new HpackContext(38*5);
+        
+        HttpField[] field = 
+        {
+           new HttpField("fo0","b0r"),
+           new HttpField("fo1","b1r"),
+           new HttpField("fo2","b2r"),
+           new HttpField("fo3","b3r"),
+           new HttpField("fo4","b4r"),
+           new HttpField("fo5","b5r"),
+           new HttpField("fo6","b6r"),
+           new HttpField("fo7","b7r"),
+           new HttpField("fo8","b8r"),
+           new HttpField("fo9","b9r"),
+           new HttpField("foA","bAr"),
+        };
+        Entry[] entry = new Entry[field.length];
+        
+        // Add 5 entries
+        for (int i=0;i<=4;i++)  
+            entry[i]=ctx.add(field[i]);
+        
+        assertEquals(5,ctx.size());
+        
+        // check indexes
+        int index=66;
+        for (int i=0;i<=4;i++)  
+        {
+            assertEquals(index,ctx.index(entry[i]));
+            assertEquals(entry[i],ctx.get(index));
+            index--;
+        }
+
+        // resize so that only 2 entries may be held
+        ctx.resize(38*2);
+        assertEquals(2,ctx.size());
+        
+        // check indexes
+        index=63;
+        for (int i=3;i<=4;i++)  
+        {
+            assertEquals(index,ctx.index(entry[i]));
+            assertEquals(entry[i],ctx.get(index));
+            index--;
+        }
+        
+        // resize so that 6.5 entries may be held
+        ctx.resize(38*6+19);
+        assertEquals(2,ctx.size());
+
+        // check indexes
+        index=63;
+        for (int i=3;i<=4;i++)  
+        {
+            assertEquals(index,ctx.index(entry[i]));
+            assertEquals(entry[i],ctx.get(index));
+            index--;
+        }
+        
+
+        // Add 5 entries
+        for (int i=5;i<=9;i++)  
+            entry[i]=ctx.add(field[i]);
+        
+        assertEquals(6,ctx.size());
+
+        // check indexes
+        index=67;
+        for (int i=4;i<=9;i++)  
+        {
+            assertEquals(index,ctx.index(entry[i]));
+            assertEquals(entry[i],ctx.get(index));
+            index--;
+        }
+        
+
+        // resize so that only 100 entries may be held
+        ctx.resize(38*100);
+        assertEquals(6,ctx.size());
+        // check indexes
+        index=67;
+        for (int i=4;i<=9;i++)  
+        {
+            assertEquals(index,ctx.index(entry[i]));
+            assertEquals(entry[i],ctx.get(index));
+            index--;
+        }
+        
+        // add 50 fields
+        for (int i=0;i<50;i++)
+            ctx.add(new HttpField("n"+i,"v"+i));
+
+        // check indexes
+        index=67+50;
+        for (int i=4;i<=9;i++)  
+        {
+            assertEquals(index,ctx.index(entry[i]));
+            assertEquals(entry[i],ctx.get(index));
+            index--;
+        }
+        
+        
+    }
+    
+    @Test
+    public void testStaticHuffmanValues()
+    {
+        HpackContext ctx = new HpackContext(4096);
+        for (int i=2;i<=14;i++)
+        {
+            Entry entry=ctx.get(i);
+            assertTrue(entry.isStatic());
+            
+            ByteBuffer buffer = ByteBuffer.wrap(entry.getStaticHuffmanValue());
+            int huff = 0xff&buffer.get();
+            assertTrue((0x80&huff)==0x80);
+            
+            int len = NBitInteger.decode(buffer,7);
+            
+            assertEquals(len,buffer.remaining());
+            String value = Huffman.decode(buffer);
+            
+            assertEquals(entry.getHttpField().getValue(),value);
+            
+        }
+    }
+    
+
+    
+    @Test
+    public void testNameInsensitivity()
+    {
+        HpackContext ctx = new HpackContext(4096);
+        assertEquals("content-length",ctx.get("content-length").getHttpField().getName());
+        assertEquals("content-length",ctx.get("Content-Length").getHttpField().getName());
+        assertTrue(ctx.get("Content-Length").isStatic());
+        assertTrue(ctx.get("Content-Type").isStatic());
+        
+        ctx.add(new HttpField("Wibble","Wobble"));
+        assertEquals("Wibble",ctx.get("wibble").getHttpField().getName());
+        assertEquals("Wibble",ctx.get("Wibble").getHttpField().getName());
+        
+    }
+}
diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java
new file mode 100644
index 0000000..907a9ab
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java
@@ -0,0 +1,232 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http2.hpack;
+
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class HpackDecoderTest
+{
+    @Test
+    public void testDecodeD_3()
+    {
+        HpackDecoder decoder = new HpackDecoder(4096,8192);
+
+        // First request
+        String encoded="828684410f7777772e6578616d706c652e636f6d";
+        ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
+
+        MetaData.Request request = (MetaData.Request)decoder.decode(buffer);
+
+        assertEquals("GET", request.getMethod());
+        assertEquals(HttpScheme.HTTP.asString(),request.getURI().getScheme());
+        assertEquals("/",request.getURI().getPath());
+        assertEquals("www.example.com",request.getURI().getHost());
+        assertFalse(request.iterator().hasNext());
+
+        // Second request
+        encoded="828684be58086e6f2d6361636865";
+        buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
+
+        request = (MetaData.Request)decoder.decode(buffer);
+
+        assertEquals("GET", request.getMethod());
+        assertEquals(HttpScheme.HTTP.asString(),request.getURI().getScheme());
+        assertEquals("/",request.getURI().getPath());
+        assertEquals("www.example.com",request.getURI().getHost());
+        Iterator<HttpField> iterator=request.iterator();
+        assertTrue(iterator.hasNext());
+        assertEquals(new HttpField("cache-control","no-cache"),iterator.next());
+        assertFalse(iterator.hasNext());
+
+        // Third request
+        encoded="828785bf400a637573746f6d2d6b65790c637573746f6d2d76616c7565";
+        buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
+
+        request = (MetaData.Request)decoder.decode(buffer);
+
+        assertEquals("GET",request.getMethod());
+        assertEquals(HttpScheme.HTTPS.asString(),request.getURI().getScheme());
+        assertEquals("/index.html",request.getURI().getPath());
+        assertEquals("www.example.com",request.getURI().getHost());
+        iterator=request.iterator();
+        assertTrue(iterator.hasNext());
+        assertEquals(new HttpField("custom-key","custom-value"),iterator.next());
+        assertFalse(iterator.hasNext());
+    }
+
+    @Test
+    public void testDecodeD_4()
+    {
+        HpackDecoder decoder = new HpackDecoder(4096,8192);
+
+        // First request
+        String encoded="828684418cf1e3c2e5f23a6ba0ab90f4ff";
+        ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
+
+        MetaData.Request request = (MetaData.Request)decoder.decode(buffer);
+
+        assertEquals("GET", request.getMethod());
+        assertEquals(HttpScheme.HTTP.asString(),request.getURI().getScheme());
+        assertEquals("/",request.getURI().getPath());
+        assertEquals("www.example.com",request.getURI().getHost());
+        assertFalse(request.iterator().hasNext());
+
+        // Second request
+        encoded="828684be5886a8eb10649cbf";
+        buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
+
+        request = (MetaData.Request)decoder.decode(buffer);
+
+        assertEquals("GET", request.getMethod());
+        assertEquals(HttpScheme.HTTP.asString(),request.getURI().getScheme());
+        assertEquals("/",request.getURI().getPath());
+        assertEquals("www.example.com",request.getURI().getHost());
+        Iterator<HttpField> iterator=request.iterator();
+        assertTrue(iterator.hasNext());
+        assertEquals(new HttpField("cache-control","no-cache"),iterator.next());
+        assertFalse(iterator.hasNext());
+    }
+
+    @Test
+    public void testDecodeWithArrayOffset()
+    {
+        String value = "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==";
+
+        HpackDecoder decoder = new HpackDecoder(4096,8192);
+        String encoded = "8682418cF1E3C2E5F23a6bA0Ab90F4Ff841f0822426173696320515778685a475270626a70766347567549484e6c633246745a513d3d";
+        byte[] bytes = TypeUtil.fromHexString(encoded);
+        byte[] array = new byte[bytes.length + 1];
+        System.arraycopy(bytes, 0, array, 1, bytes.length);
+        ByteBuffer buffer = ByteBuffer.wrap(array, 1, bytes.length).slice();
+
+        MetaData.Request request = (MetaData.Request)decoder.decode(buffer);
+
+        assertEquals("GET", request.getMethod());
+        assertEquals(HttpScheme.HTTP.asString(),request.getURI().getScheme());
+        assertEquals("/",request.getURI().getPath());
+        assertEquals("www.example.com",request.getURI().getHost());
+        assertEquals(1,request.getFields().size());
+        HttpField field = request.iterator().next();
+        assertEquals(HttpHeader.AUTHORIZATION, field.getHeader());
+        assertEquals(value, field.getValue());
+    }
+
+    @Test
+    public void testDecodeHuffmanWithArrayOffset()
+    {
+        HpackDecoder decoder = new HpackDecoder(4096,8192);
+
+        String encoded="8286418cf1e3c2e5f23a6ba0ab90f4ff84";
+        byte[] bytes = TypeUtil.fromHexString(encoded);
+        byte[] array = new byte[bytes.length + 1];
+        System.arraycopy(bytes, 0, array, 1, bytes.length);
+        ByteBuffer buffer = ByteBuffer.wrap(array, 1, bytes.length).slice();
+
+        MetaData.Request request = (MetaData.Request)decoder.decode(buffer);
+
+        assertEquals("GET", request.getMethod());
+        assertEquals(HttpScheme.HTTP.asString(),request.getURI().getScheme());
+        assertEquals("/",request.getURI().getPath());
+        assertEquals("www.example.com",request.getURI().getHost());
+        assertFalse(request.iterator().hasNext());
+    }
+    
+    @Test
+    public void testNghttpx()
+    {        
+        // Response encoded by nghttpx
+        String encoded="886196C361Be940b6a65B6850400B8A00571972e080a62D1Bf5f87497cA589D34d1f9a0f0d0234327690Aa69D29aFcA954D3A5358980Ae112e0f7c880aE152A9A74a6bF3";
+        ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
+
+        HpackDecoder decoder = new HpackDecoder(4096,8192);
+        MetaData.Response response = (MetaData.Response)decoder.decode(buffer);
+
+        assertThat(response.getStatus(),is(200));
+        assertThat(response.getFields().size(),is(6));
+        assertTrue(response.getFields().contains(new HttpField(HttpHeader.DATE,"Fri, 15 Jul 2016 02:36:20 GMT")));
+        assertTrue(response.getFields().contains(new HttpField(HttpHeader.CONTENT_TYPE,"text/html")));
+        assertTrue(response.getFields().contains(new HttpField(HttpHeader.CONTENT_ENCODING,"")));
+        assertTrue(response.getFields().contains(new HttpField(HttpHeader.CONTENT_LENGTH,"42")));
+        assertTrue(response.getFields().contains(new HttpField(HttpHeader.SERVER,"nghttpx nghttp2/1.12.0")));
+        assertTrue(response.getFields().contains(new HttpField(HttpHeader.VIA,"1.1 nghttpx")));
+    }
+
+    @Test
+    public void testTooBigToIndex()
+    {
+        String encoded = "44FfEc02Df3990A190A0D4Ee5b3d2940Ec98Aa4a62D127D29e273a0aA20dEcAa190a503b262d8a2671D4A2672a927aA874988a2471D05510750c951139EdA2452a3a548cAa1aA90bE4B228342864A9E0D450A5474a92992a1aA513395448E3A0Aa17B96cFe3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f14E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F353F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F54f";
+        ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
+
+        HpackDecoder decoder = new HpackDecoder(128,8192);
+        try
+        {
+            decoder.decode(buffer);
+            Assert.fail();
+        }
+        catch (BadMessageException e)
+        {
+            assertThat(e.getCode(),equalTo(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431));
+            assertThat(e.getReason(),Matchers.startsWith("Indexed field value too large"));
+        }
+    }
+
+    @Test
+    public void testUnknownIndex()
+    {
+        String encoded = "BE";
+        ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
+
+        HpackDecoder decoder = new HpackDecoder(128,8192);
+        try
+        {
+            decoder.decode(buffer);
+            Assert.fail();
+        }
+        catch (BadMessageException e)
+        {
+            assertThat(e.getCode(),equalTo(HttpStatus.BAD_REQUEST_400));
+            assertThat(e.getReason(),Matchers.startsWith("Unknown index"));
+        }
+    
+    }
+}
diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackEncoderTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackEncoderTest.java
new file mode 100644
index 0000000..7a18485
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackEncoderTest.java
@@ -0,0 +1,255 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http2.hpack;
+
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.TypeUtil;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public class HpackEncoderTest
+{
+    @Test
+    public void testUnknownFieldsContextManagement()
+    {
+        HpackEncoder encoder = new HpackEncoder(38*5);
+        HttpFields fields = new HttpFields();
+        
+
+        HttpField[] field = 
+        {
+           new HttpField("fo0","b0r"),
+           new HttpField("fo1","b1r"),
+           new HttpField("fo2","b2r"),
+           new HttpField("fo3","b3r"),
+           new HttpField("fo4","b4r"),
+           new HttpField("fo5","b5r"),
+           new HttpField("fo6","b6r"),
+           new HttpField("fo7","b7r"),
+           new HttpField("fo8","b8r"),
+           new HttpField("fo9","b9r"),
+           new HttpField("foA","bAr"),
+        };
+        
+        // Add 4 entries
+        for (int i=0;i<=3;i++)  
+            fields.add(field[i]);
+        
+        // encode them
+        ByteBuffer buffer = BufferUtil.allocate(4096);
+        int pos = BufferUtil.flipToFill(buffer);
+        encoder.encode(buffer,new MetaData(HttpVersion.HTTP_2,fields));
+        BufferUtil.flipToFlush(buffer,pos);
+        
+        // something was encoded!
+        assertThat(buffer.remaining(),Matchers.greaterThan(0));
+        
+        // All are in the dynamic table
+        Assert.assertEquals(4,encoder.getHpackContext().size());
+                
+        // encode exact same fields again!
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,new MetaData(HttpVersion.HTTP_2,fields));
+        BufferUtil.flipToFlush(buffer,0);
+
+        // All are in the dynamic table
+        Assert.assertEquals(4,encoder.getHpackContext().size());
+        
+        // Add 4 more fields
+        for (int i=4;i<=7;i++)  
+            fields.add(field[i]);
+        
+        // encode
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,new MetaData(HttpVersion.HTTP_2,fields));
+        BufferUtil.flipToFlush(buffer,0);
+
+        // something was encoded!
+        assertThat(buffer.remaining(),Matchers.greaterThan(0));
+
+        // max dynamic table size reached
+        Assert.assertEquals(5,encoder.getHpackContext().size());
+        
+        
+        // remove some fields
+        for (int i=0;i<=7;i+=2)  
+            fields.remove(field[i].getName());
+
+        // encode
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,new MetaData(HttpVersion.HTTP_2,fields));
+        BufferUtil.flipToFlush(buffer,0);
+        
+        // something was encoded!
+        assertThat(buffer.remaining(),Matchers.greaterThan(0));
+
+        // max dynamic table size reached
+        Assert.assertEquals(5,encoder.getHpackContext().size());
+
+
+        // remove another fields
+        fields.remove(field[1].getName());
+
+        // encode
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,new MetaData(HttpVersion.HTTP_2,fields));
+        BufferUtil.flipToFlush(buffer,0);
+        
+        // something was encoded!
+        assertThat(buffer.remaining(),Matchers.greaterThan(0));
+
+        // max dynamic table size reached
+        Assert.assertEquals(5,encoder.getHpackContext().size());
+
+        
+        // re add the field
+
+        fields.add(field[1]);
+
+        // encode
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,new MetaData(HttpVersion.HTTP_2,fields));
+        BufferUtil.flipToFlush(buffer,0);
+        
+        // something was encoded!
+        assertThat(buffer.remaining(),Matchers.greaterThan(0));
+
+        // max dynamic table size reached
+        Assert.assertEquals(5,encoder.getHpackContext().size());
+
+    }
+
+
+    @Test
+    public void testNeverIndexSetCookie()
+    {
+        HpackEncoder encoder = new HpackEncoder(38*5);
+        ByteBuffer buffer = BufferUtil.allocate(4096);
+        
+        HttpFields fields = new HttpFields();
+        fields.put("set-cookie","some cookie value");
+
+        // encode
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,new MetaData(HttpVersion.HTTP_2,fields));
+        BufferUtil.flipToFlush(buffer,0);
+        
+        // something was encoded!
+        assertThat(buffer.remaining(),Matchers.greaterThan(0));
+        
+        // empty dynamic table
+        Assert.assertEquals(0,encoder.getHpackContext().size());
+        
+
+        // encode again
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,new MetaData(HttpVersion.HTTP_2,fields));
+        BufferUtil.flipToFlush(buffer,0);
+        
+        // something was encoded!
+        assertThat(buffer.remaining(),Matchers.greaterThan(0));
+        
+        // empty dynamic table
+        Assert.assertEquals(0,encoder.getHpackContext().size());
+        
+    }
+    
+
+    @Test
+    public void testFieldLargerThanTable()
+    {
+        HttpFields fields = new HttpFields();
+
+        HpackEncoder encoder = new HpackEncoder(128);
+        ByteBuffer buffer0 = BufferUtil.allocate(4096);
+        int pos = BufferUtil.flipToFill(buffer0);
+        encoder.encode(buffer0,new MetaData(HttpVersion.HTTP_2,fields));
+        BufferUtil.flipToFlush(buffer0,pos);
+        
+        encoder = new HpackEncoder(128);
+        fields.add(new HttpField("user-agent","jetty/test")); 
+        ByteBuffer buffer1 = BufferUtil.allocate(4096);
+        pos = BufferUtil.flipToFill(buffer1);
+        encoder.encode(buffer1,new MetaData(HttpVersion.HTTP_2,fields));
+        BufferUtil.flipToFlush(buffer1,pos);
+        
+        encoder = new HpackEncoder(128);
+        fields.add(new HttpField(":path",
+            "This is a very large field, whose size is larger than the dynamic table so it should not be indexed as it will not fit in the table ever!"+
+            "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "+
+            "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY "+
+            "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ "));
+        ByteBuffer buffer2 = BufferUtil.allocate(4096);
+        pos = BufferUtil.flipToFill(buffer2);
+        encoder.encode(buffer2,new MetaData(HttpVersion.HTTP_2,fields));
+        BufferUtil.flipToFlush(buffer2,pos);
+        
+        encoder = new HpackEncoder(128);
+        fields.add(new HttpField("host","somehost"));
+        ByteBuffer buffer = BufferUtil.allocate(4096);
+        pos = BufferUtil.flipToFill(buffer);
+        encoder.encode(buffer,new MetaData(HttpVersion.HTTP_2,fields));
+        BufferUtil.flipToFlush(buffer,pos);
+
+        //System.err.println(BufferUtil.toHexString(buffer0));
+        //System.err.println(BufferUtil.toHexString(buffer1));
+        //System.err.println(BufferUtil.toHexString(buffer2));
+        //System.err.println(BufferUtil.toHexString(buffer));
+        
+        // something was encoded!
+        assertThat(buffer.remaining(),Matchers.greaterThan(0));
+        
+        // check first field is static index name and dynamic index body
+        assertThat((buffer.get(buffer0.remaining())&0xFF)>>6,equalTo(1));
+        
+        // check first field is static index name and literal body
+        assertThat((buffer.get(buffer1.remaining())&0xFF)>>4,equalTo(0));
+        
+        // check first field is static index name and dynamic index body
+        assertThat((buffer.get(buffer2.remaining())&0xFF)>>6,equalTo(1));        
+        
+        // Only first and third fields are put in the table
+        HpackContext context = encoder.getHpackContext();
+        assertThat(context.size(),equalTo(2));
+        assertThat(context.get(HpackContext.STATIC_SIZE+1).getHttpField().getName(),equalTo("host"));
+        assertThat(context.get(HpackContext.STATIC_SIZE+2).getHttpField().getName(),equalTo("user-agent"));
+        assertThat(context.getDynamicTableSize(),equalTo(
+        context.get(HpackContext.STATIC_SIZE+1).getSize()+context.get(HpackContext.STATIC_SIZE+2).getSize()));
+        
+    }
+
+    
+
+}
diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackPerfTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackPerfTest.java
new file mode 100644
index 0000000..a9572a6
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackPerfTest.java
@@ -0,0 +1,135 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.http2.hpack;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FilenameFilter;
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.ajax.JSON;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class HpackPerfTest
+{
+    int _maxDynamicTableSize=4*1024;
+    int _unencodedSize;
+    int _encodedSize;
+    
+    @Before
+    public void before()
+    {
+        _unencodedSize=0;
+        _encodedSize=0;
+    }
+
+    @After
+    public void after()
+    {        
+        System.err.printf("dynamictable=%d unencoded=%d encoded=%d p=%3.1f%%%n",_maxDynamicTableSize,_unencodedSize,_encodedSize,100.0*_encodedSize/_unencodedSize);
+
+    }
+    
+    @Test
+    public void simpleTest() throws Exception
+    {
+        runStories(_maxDynamicTableSize);
+    }
+    
+    private void runStories(int maxDynamicTableSize) throws Exception
+    {
+        // Find files
+        File data = MavenTestingUtils.getTestResourceDir("data");
+        String[] files = data.list(new FilenameFilter()
+        {
+            @Override
+            public boolean accept(File dir, String name)
+            {
+                return name.startsWith("story_");
+            }
+        });
+        
+        // Parse JSON
+        Map<String,Object>[] stories = new Map[files.length];
+        int i=0;
+        for (String story : files)
+            stories[i++]=(Map<String,Object>)JSON.parse(new FileReader(new File(data,story)));
+        
+        ByteBuffer buffer = BufferUtil.allocate(256*1024);
+        
+        // Encode all the requests
+        encodeStories(buffer,stories,"request");
+
+        // clear table
+        BufferUtil.clearToFill(buffer);
+        BufferUtil.flipToFlush(buffer,0);
+        
+        // Encode all the responses
+        encodeStories(buffer,stories,"response");
+        
+    }
+    
+    private void encodeStories(ByteBuffer buffer,Map<String,Object>[] stories, String type) throws Exception
+    {
+        for (Map<String,Object> story : stories)
+        {
+            if (type.equals(story.get("context")))
+            {
+                HpackEncoder encoder = new HpackEncoder(_maxDynamicTableSize,_maxDynamicTableSize);
+                
+                // System.err.println(story);
+                Object[] cases = (Object[])story.get("cases");
+                for (Object c : cases)
+                {
+                    // System.err.println("  "+c);
+                    Object[] headers = (Object[])((Map<String,Object>)c).get("headers");
+                    // System.err.println("    "+headers);
+                    HttpFields fields = new HttpFields();
+                    for (Object header:headers)
+                    {
+                        Map<String,String> h = (Map<String,String>)header;
+                        Map.Entry<String, String> e = h.entrySet().iterator().next();
+                        fields.add(e.getKey(),e.getValue());
+                        _unencodedSize+=e.getKey().length()+e.getValue().length();
+                        
+                    }
+
+                    BufferUtil.clearToFill(buffer);
+                    encoder.encode(buffer,new MetaData(HttpVersion.HTTP_2,fields));
+                    BufferUtil.flipToFlush(buffer,0);
+                    _encodedSize+=buffer.remaining();
+                    
+                }
+            }
+        }
+
+    }
+    
+    
+}
diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java
new file mode 100644
index 0000000..c7466b4
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java
@@ -0,0 +1,209 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.hpack;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.DateGenerator;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http.MetaData.Response;
+import org.eclipse.jetty.http.PreEncodedHttpField;
+import org.eclipse.jetty.util.BufferUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class HpackTest
+{
+    final static HttpField ServerJetty = new PreEncodedHttpField(HttpHeader.SERVER,"jetty");
+    final static HttpField XPowerJetty = new PreEncodedHttpField(HttpHeader.X_POWERED_BY,"jetty");
+    final static HttpField Date = new PreEncodedHttpField(HttpHeader.DATE,DateGenerator.formatDate(System.currentTimeMillis()));
+    
+    @Test
+    public void encodeDecodeResponseTest()
+    {
+        HpackEncoder encoder = new HpackEncoder();
+        HpackDecoder decoder = new HpackDecoder(4096,8192);
+        ByteBuffer buffer = BufferUtil.allocate(16*1024);
+        
+        HttpFields fields0 = new HttpFields();
+        fields0.add(HttpHeader.CONTENT_TYPE,"text/html");
+        fields0.add(HttpHeader.CONTENT_LENGTH,"1024");
+        fields0.add(new HttpField(HttpHeader.CONTENT_ENCODING,(String)null));
+        fields0.add(ServerJetty);
+        fields0.add(XPowerJetty);
+        fields0.add(Date);
+        fields0.add(HttpHeader.SET_COOKIE,"abcdefghijklmnopqrstuvwxyz");
+        fields0.add("custom-key","custom-value");
+        Response original0 = new MetaData.Response(HttpVersion.HTTP_2,200,fields0);
+        
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,original0);
+        BufferUtil.flipToFlush(buffer,0);
+        Response decoded0 = (Response)decoder.decode(buffer);
+        original0.getFields().put(new HttpField(HttpHeader.CONTENT_ENCODING,""));
+        assertMetadataSame(original0,decoded0);
+        
+        // Same again?
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,original0);
+        BufferUtil.flipToFlush(buffer,0);
+        Response decoded0b = (Response)decoder.decode(buffer);
+
+        assertMetadataSame(original0,decoded0b);        
+
+        HttpFields fields1 = new HttpFields();
+        fields1.add(HttpHeader.CONTENT_TYPE,"text/plain");
+        fields1.add(HttpHeader.CONTENT_LENGTH,"1234");
+        fields1.add(HttpHeader.CONTENT_ENCODING," ");
+        fields1.add(ServerJetty);
+        fields1.add(XPowerJetty);
+        fields1.add(Date);
+        fields1.add("Custom-Key","Other-Value");
+        Response original1 = new MetaData.Response(HttpVersion.HTTP_2,200,fields1);
+
+        // Same again?
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,original1);
+        BufferUtil.flipToFlush(buffer,0);
+        Response decoded1 = (Response)decoder.decode(buffer);
+
+        assertMetadataSame(original1,decoded1);
+        Assert.assertEquals("custom-key",decoded1.getFields().getField("Custom-Key").getName());
+    }
+    
+    @Test
+    public void encodeDecodeTooLargeTest()
+    {
+        HpackEncoder encoder = new HpackEncoder();
+        HpackDecoder decoder = new HpackDecoder(4096,164);
+        ByteBuffer buffer = BufferUtil.allocate(16*1024);
+        
+        HttpFields fields0 = new HttpFields();
+        fields0.add("1234567890","1234567890123456789012345678901234567890");
+        fields0.add("Cookie","abcdeffhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR");
+        MetaData original0= new MetaData(HttpVersion.HTTP_2,fields0);
+        
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,original0);
+        BufferUtil.flipToFlush(buffer,0);
+        MetaData decoded0 = (MetaData)decoder.decode(buffer);
+
+        assertMetadataSame(original0,decoded0);
+               
+        HttpFields fields1 = new HttpFields();
+        fields1.add("1234567890","1234567890123456789012345678901234567890");
+        fields1.add("Cookie","abcdeffhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR");
+        fields1.add("x","y");
+        MetaData original1 = new MetaData(HttpVersion.HTTP_2,fields1);
+
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,original1);
+        BufferUtil.flipToFlush(buffer,0);
+        try
+        {
+            decoder.decode(buffer);
+            Assert.fail();
+        }
+        catch(BadMessageException e)
+        {
+            assertEquals(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,e.getCode());
+        }
+    }
+
+    @Test
+    public void evictReferencedFieldTest()
+    {
+        HpackEncoder encoder = new HpackEncoder(200,200);
+        HpackDecoder decoder = new HpackDecoder(200,1024);
+        ByteBuffer buffer = BufferUtil.allocate(16*1024);
+        
+        HttpFields fields0 = new HttpFields();
+        fields0.add("123456789012345678901234567890123456788901234567890","value");
+        fields0.add("foo","abcdeffhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR");
+        MetaData original0= new MetaData(HttpVersion.HTTP_2,fields0);
+
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,original0);
+        BufferUtil.flipToFlush(buffer,0);
+        MetaData decoded0 = (MetaData)decoder.decode(buffer);
+
+        assertEquals(2,encoder.getHpackContext().size());
+        assertEquals(2,decoder.getHpackContext().size());
+        assertEquals("123456789012345678901234567890123456788901234567890",encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length+1).getHttpField().getName());
+        assertEquals("foo",encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length+0).getHttpField().getName());
+        
+        assertMetadataSame(original0,decoded0);
+               
+        HttpFields fields1 = new HttpFields();
+        fields1.add("123456789012345678901234567890123456788901234567890","other_value");
+        fields1.add("x","y");
+        MetaData original1 = new MetaData(HttpVersion.HTTP_2,fields1);
+
+        BufferUtil.clearToFill(buffer);
+        encoder.encode(buffer,original1);
+        BufferUtil.flipToFlush(buffer,0);
+        MetaData decoded1 = (MetaData)decoder.decode(buffer);
+        assertMetadataSame(original1,decoded1);
+        
+        assertEquals(2,encoder.getHpackContext().size());
+        assertEquals(2,decoder.getHpackContext().size());
+        assertEquals("x",encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length+0).getHttpField().getName());
+        assertEquals("foo",encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length+1).getHttpField().getName());        
+    }
+    
+    private void assertMetadataSame(MetaData.Response expected, MetaData.Response actual)
+    {
+        assertThat("Response.status", actual.getStatus(), is(expected.getStatus()));
+        assertThat("Response.reason", actual.getReason(), is(expected.getReason()));
+        assertMetadataSame((MetaData)expected,(MetaData)actual);
+    }
+
+    private void assertMetadataSame(MetaData expected, MetaData actual)
+    {
+        assertThat("Metadata.contentLength",actual.getContentLength(),is(expected.getContentLength()));
+        assertThat("Metadata.version" + ".version", actual.getHttpVersion(),is(expected.getHttpVersion()));
+        assertHttpFieldsSame("Metadata.fields",expected.getFields(),actual.getFields());
+    }
+
+    private void assertHttpFieldsSame(String msg, HttpFields expected, HttpFields actual)
+    {
+        assertThat(msg + ".size", actual.size(), is(expected.size()));
+        
+        for (HttpField actualField : actual)
+        {
+            if ("DATE".equalsIgnoreCase(actualField.getName()))
+            {
+                // skip comparison on Date, as these values can often differ by 1 second
+                // during testing.
+                continue;
+            }
+            assertThat(msg + ".contains(" + actualField + ")",expected.contains(actualField),is(true));
+        }
+    }
+}
diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HuffmanTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HuffmanTest.java
new file mode 100644
index 0000000..3ad9c02
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HuffmanTest.java
@@ -0,0 +1,108 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.hpack;
+
+import java.nio.ByteBuffer;
+import java.util.Locale;
+
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.TypeUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HuffmanTest
+{
+    String[][] tests =
+        {
+            {"D.4.1","f1e3c2e5f23a6ba0ab90f4ff","www.example.com"},
+            {"D.4.2","a8eb10649cbf","no-cache"},
+            {"D.6.1k","6402","302"},
+            {"D.6.1v","aec3771a4b","private"},
+            {"D.6.1d","d07abe941054d444a8200595040b8166e082a62d1bff","Mon, 21 Oct 2013 20:13:21 GMT"},
+            {"D.6.1l","9d29ad171863c78f0b97c8e9ae82ae43d3","https://www.example.com"},
+            {"D.6.2te","640cff","303"},
+        };
+
+    @Test
+    public void testDecode() throws Exception
+    {
+        for (String[] test:tests)
+        {
+            byte[] encoded=TypeUtil.fromHexString(test[1]);
+            String decoded=Huffman.decode(ByteBuffer.wrap(encoded));
+            Assert.assertEquals(test[0],test[2],decoded);
+        }
+    }
+
+    @Test
+    public void testDecodeTrailingFF() throws Exception
+    {
+        for (String[] test:tests)
+        {
+            byte[] encoded=TypeUtil.fromHexString(test[1]+"FF");
+            String decoded=Huffman.decode(ByteBuffer.wrap(encoded));
+            Assert.assertEquals(test[0],test[2],decoded);
+        }
+    }
+
+    @Test
+    public void testEncode() throws Exception
+    {
+        for (String[] test:tests)
+        {
+            ByteBuffer buf = BufferUtil.allocate(1024);
+            int pos=BufferUtil.flipToFill(buf);
+            Huffman.encode(buf,test[2]);
+            BufferUtil.flipToFlush(buf,pos);
+            String encoded=TypeUtil.toHexString(BufferUtil.toArray(buf)).toLowerCase(Locale.ENGLISH);
+            Assert.assertEquals(test[0],test[1],encoded);
+            Assert.assertEquals(test[1].length()/2,Huffman.octetsNeeded(test[2]));
+        }
+    }
+
+    @Test
+    public void testEncode8859Only() throws Exception
+    {
+        char bad[] = {(char)128,(char)0,(char)-1,' '-1};
+        for (int i=0;i<bad.length;i++)
+        {
+            String s="bad '"+bad[i]+"'";
+
+            try
+            {
+                Huffman.octetsNeeded(s);
+                Assert.fail("i="+i);
+            }
+            catch(IllegalArgumentException e)
+            {
+            }
+
+            try
+            {
+                Huffman.encode(BufferUtil.allocate(32),s);
+                Assert.fail("i="+i);
+            }
+            catch(IllegalArgumentException e)
+            {
+            }
+        }
+    }
+
+
+}
diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/NBitIntegerTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/NBitIntegerTest.java
new file mode 100644
index 0000000..03a7e44
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/NBitIntegerTest.java
@@ -0,0 +1,214 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.hpack;
+
+import static org.junit.Assert.assertEquals;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.TypeUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NBitIntegerTest
+{
+
+    @Test 
+    public void testOctetsNeeded()
+    {
+        assertEquals(0,NBitInteger.octectsNeeded(5,10));
+        assertEquals(2,NBitInteger.octectsNeeded(5,1337));
+        assertEquals(1,NBitInteger.octectsNeeded(8,42));
+        assertEquals(3,NBitInteger.octectsNeeded(8,1337));
+
+        assertEquals(0,NBitInteger.octectsNeeded(6,62));
+        assertEquals(1,NBitInteger.octectsNeeded(6,63));
+        assertEquals(1,NBitInteger.octectsNeeded(6,64));
+        assertEquals(2,NBitInteger.octectsNeeded(6,63+0x00+0x80*0x01));
+        assertEquals(3,NBitInteger.octectsNeeded(6,63+0x00+0x80*0x80));
+        assertEquals(4,NBitInteger.octectsNeeded(6,63+0x00+0x80*0x80*0x80));
+    }
+
+    @Test
+    public void testEncode()
+    {
+        testEncode(6,0,"00");
+        testEncode(6,1,"01");
+        testEncode(6,62,"3e");
+        testEncode(6,63,"3f00");
+        testEncode(6,63+1,"3f01");
+        testEncode(6,63+0x7e,"3f7e");
+        testEncode(6,63+0x7f,"3f7f");
+        testEncode(6,63+0x00+0x80*0x01,"3f8001");
+        testEncode(6,63+0x01+0x80*0x01,"3f8101");
+        testEncode(6,63+0x7f+0x80*0x01,"3fFf01");
+        testEncode(6,63+0x00+0x80*0x02,"3f8002");
+        testEncode(6,63+0x01+0x80*0x02,"3f8102");
+        testEncode(6,63+0x7f+0x80*0x7f,"3fFf7f");
+        testEncode(6,63+0x00+0x80*0x80,     "3f808001");
+        testEncode(6,63+0x7f+0x80*0x80*0x7f,"3fFf807f");
+        testEncode(6,63+0x00+0x80*0x80*0x80,"3f80808001");
+
+        testEncode(8,0,"00");
+        testEncode(8,1,"01");
+        testEncode(8,128,"80");
+        testEncode(8,254,"Fe");
+        testEncode(8,255,"Ff00");
+        testEncode(8,255+1,"Ff01");
+        testEncode(8,255+0x7e,"Ff7e");
+        testEncode(8,255+0x7f,"Ff7f");
+        testEncode(8,255+0x80,"Ff8001");
+        testEncode(8,255+0x00+0x80*0x80,"Ff808001");
+    }
+
+    public void testEncode(int n,int i,String expected)
+    {
+        ByteBuffer buf = BufferUtil.allocate(16);
+        int p=BufferUtil.flipToFill(buf);
+        if (n<8)
+            buf.put((byte)0x00);
+        NBitInteger.encode(buf,n,i);
+        BufferUtil.flipToFlush(buf,p);
+        String r=TypeUtil.toHexString(BufferUtil.toArray(buf));
+        assertEquals(expected,r);
+        
+        assertEquals(expected.length()/2,(n<8?1:0)+NBitInteger.octectsNeeded(n,i));
+    }
+    
+    @Test
+    public void testDecode()
+    {
+        testDecode(6,0,"00");
+        testDecode(6,1,"01");
+        testDecode(6,62,"3e");
+        testDecode(6,63,"3f00");
+        testDecode(6,63+1,"3f01");
+        testDecode(6,63+0x7e,"3f7e");
+        testDecode(6,63+0x7f,"3f7f");
+        testDecode(6,63+0x80,"3f8001");
+        testDecode(6,63+0x81,"3f8101");
+        testDecode(6,63+0x7f+0x80*0x01,"3fFf01");
+        testDecode(6,63+0x00+0x80*0x02,"3f8002");
+        testDecode(6,63+0x01+0x80*0x02,"3f8102");
+        testDecode(6,63+0x7f+0x80*0x7f,"3fFf7f");
+        testDecode(6,63+0x00+0x80*0x80,     "3f808001");
+        testDecode(6,63+0x7f+0x80*0x80*0x7f,"3fFf807f");
+        testDecode(6,63+0x00+0x80*0x80*0x80,"3f80808001");
+        
+        testDecode(8,0,"00");
+        testDecode(8,1,"01");
+        testDecode(8,128,"80");
+        testDecode(8,254,"Fe");
+        testDecode(8,255,"Ff00");
+        testDecode(8,255+1,"Ff01");
+        testDecode(8,255+0x7e,"Ff7e");
+        testDecode(8,255+0x7f,"Ff7f");
+        testDecode(8,255+0x80,"Ff8001");
+        testDecode(8,255+0x00+0x80*0x80,"Ff808001");
+    }
+    
+    
+    public void testDecode(int n,int expected,String encoded)
+    {
+        ByteBuffer buf = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
+        buf.position(n==8?0:1);
+        Assert.assertEquals(expected,NBitInteger.decode(buf,n));
+    }
+    
+    @Test
+    public void testEncodeExampleD_1_1()
+    {
+        ByteBuffer buf = BufferUtil.allocate(16);
+        int p=BufferUtil.flipToFill(buf);
+        buf.put((byte)0x77);
+        buf.put((byte)0xFF);
+        NBitInteger.encode(buf,5,10);
+        BufferUtil.flipToFlush(buf,p);
+        
+        String r=TypeUtil.toHexString(BufferUtil.toArray(buf));
+        
+        assertEquals("77Ea",r);
+        
+    }
+    
+    @Test
+    public void testDecodeExampleD_1_1()
+    {
+        ByteBuffer buf = ByteBuffer.wrap(TypeUtil.fromHexString("77EaFF"));
+        buf.position(2);
+        
+        Assert.assertEquals(10,NBitInteger.decode(buf,5));
+    }
+    
+
+    @Test
+    public void testEncodeExampleD_1_2()
+    {
+        ByteBuffer buf = BufferUtil.allocate(16);
+        int p=BufferUtil.flipToFill(buf);
+        buf.put((byte)0x88);
+        buf.put((byte)0x00);
+        NBitInteger.encode(buf,5,1337);
+        BufferUtil.flipToFlush(buf,p);
+        
+        String r=TypeUtil.toHexString(BufferUtil.toArray(buf));
+        
+        Assert.assertEquals("881f9a0a",r);
+        
+    }
+    
+    @Test
+    public void testDecodeExampleD_1_2()
+    {
+        ByteBuffer buf = ByteBuffer.wrap(TypeUtil.fromHexString("881f9a0aff"));
+        buf.position(2);
+        
+        Assert.assertEquals(1337,NBitInteger.decode(buf,5));
+    }
+    
+    
+    @Test
+    public void testEncodeExampleD_1_3()
+    {
+        ByteBuffer buf = BufferUtil.allocate(16);
+        int p=BufferUtil.flipToFill(buf);
+        buf.put((byte)0x88);
+        buf.put((byte)0xFF);
+        NBitInteger.encode(buf,8,42);
+        BufferUtil.flipToFlush(buf,p);
+        
+        String r=TypeUtil.toHexString(BufferUtil.toArray(buf));
+        
+        Assert.assertEquals("88Ff2a",r);
+        
+    }
+
+    
+    @Test
+    public void testDecodeExampleD_1_3()
+    {
+        ByteBuffer buf = ByteBuffer.wrap(TypeUtil.fromHexString("882aFf"));
+        buf.position(1);
+        
+        Assert.assertEquals(42,NBitInteger.decode(buf,8));
+    }
+    
+
+}
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_00.json b/jetty-http2/http2-hpack/src/test/resources/data/story_00.json
new file mode 100644
index 0000000..44d521f
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_00.json
@@ -0,0 +1,53 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yahoo.co.jp"
+        },
+        {
+          ":path": "/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.yahoo.co.jp"
+        },
+        {
+          ":path": "/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/top/sp2/cmn/logo-ns-130528.png"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_01.json b/jetty-http2/http2-hpack/src/test/resources/data/story_01.json
new file mode 100644
index 0000000..54aab3b
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_01.json
@@ -0,0 +1,52 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":scheme": "https"
+        },
+        {
+          ":authority": "example.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          ":method": "GET"
+        },
+        {
+          "user-agent": "hpack-test"
+        },
+        {
+          "cookie": "xxxxxxx1"
+        },
+        {
+          "x-hello": "world"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":scheme": "https"
+        },
+        {
+          ":authority": "example.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          ":method": "GET"
+        },
+        {
+          "user-agent": "hpack-test"
+        },
+        {
+          "cookie": "xxxxxxx2"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_02.json b/jetty-http2/http2-hpack/src/test/resources/data/story_02.json
new file mode 100644
index 0000000..4348579
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_02.json
@@ -0,0 +1,339 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "amazon.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "g-ecx.images-amazon.com"
+        },
+        {
+          ":path": "/images/G/01/gno/beacon/BeaconSprite-US-01._V401903535_.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.amazon.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "g-ecx.images-amazon.com"
+        },
+        {
+          ":path": "/images/G/01/x-locale/common/transparent-pixel._V386942464_.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.amazon.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "g-ecx.images-amazon.com"
+        },
+        {
+          ":path": "/images/G/01/img12/other/disaster-relief/300-column/sandy-relief_300x75._V400689491_.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.amazon.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.amazon.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "g-ecx.images-amazon.com"
+        },
+        {
+          ":path": "/images/G/01/x-locale/common/transparent-pixel._V192234675_.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.amazon.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "g-ecx.images-amazon.com"
+        },
+        {
+          ":path": "/images/G/01/img12/shoes/sales_events/11_nov/1030_AccessoriesPROMO_GWright._V400626950_.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.amazon.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "g-ecx.images-amazon.com"
+        },
+        {
+          ":path": "/images/G/01/Automotive/rotos/Duracell600_120._V192204764_.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.amazon.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "g-ecx.images-amazon.com"
+        },
+        {
+          ":path": "/images/G/01/ui/loadIndicators/loadIndicator-large._V192195480_.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.amazon.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ecx.images-amazon.com"
+        },
+        {
+          ":path": "/images/I/41HZ-ND-SUL._SL135_.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.amazon.com/"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_03.json b/jetty-http2/http2-hpack/src/test/resources/data/story_03.json
new file mode 100644
index 0000000..3d7e5be
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_03.json
@@ -0,0 +1,342 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "baidu.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "baidu.com"
+        },
+        {
+          ":path": "/favicon.ico"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.baidu.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.baidu.com"
+        },
+        {
+          ":path": "/img/baidu_sylogo1.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.baidu.com/"
+        },
+        {
+          "cookie": "BAIDUID=B6136AC10EBE0A8FCD216EB64C4C1A5C:FG=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.baidu.com"
+        },
+        {
+          ":path": "/cache/global/img/gs.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.baidu.com/"
+        },
+        {
+          "cookie": "BAIDUID=B6136AC10EBE0A8FCD216EB64C4C1A5C:FG=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s1.bdstatic.com"
+        },
+        {
+          ":path": "/r/www/cache/global/js/tangram-1.3.4c1.0.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.baidu.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s1.bdstatic.com"
+        },
+        {
+          ":path": "/r/www/cache/global/js/home-1.8.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.baidu.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s1.bdstatic.com"
+        },
+        {
+          ":path": "/r/www/cache/user/js/u-1.3.4.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.baidu.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s1.bdstatic.com"
+        },
+        {
+          ":path": "/r/www/img/i-1.0.0.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.baidu.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.baidu.com"
+        },
+        {
+          ":path": "/favicon.ico"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "BAIDUID=B6136AC10EBE0A8FCD216EB64C4C1A5C:FG=1"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_04.json b/jetty-http2/http2-hpack/src/test/resources/data/story_04.json
new file mode 100644
index 0000000..3d7e5be
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_04.json
@@ -0,0 +1,342 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "baidu.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "baidu.com"
+        },
+        {
+          ":path": "/favicon.ico"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.baidu.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.baidu.com"
+        },
+        {
+          ":path": "/img/baidu_sylogo1.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.baidu.com/"
+        },
+        {
+          "cookie": "BAIDUID=B6136AC10EBE0A8FCD216EB64C4C1A5C:FG=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.baidu.com"
+        },
+        {
+          ":path": "/cache/global/img/gs.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.baidu.com/"
+        },
+        {
+          "cookie": "BAIDUID=B6136AC10EBE0A8FCD216EB64C4C1A5C:FG=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s1.bdstatic.com"
+        },
+        {
+          ":path": "/r/www/cache/global/js/tangram-1.3.4c1.0.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.baidu.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s1.bdstatic.com"
+        },
+        {
+          ":path": "/r/www/cache/global/js/home-1.8.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.baidu.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s1.bdstatic.com"
+        },
+        {
+          ":path": "/r/www/cache/user/js/u-1.3.4.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.baidu.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s1.bdstatic.com"
+        },
+        {
+          ":path": "/r/www/img/i-1.0.0.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.baidu.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.baidu.com"
+        },
+        {
+          ":path": "/favicon.ico"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "BAIDUID=B6136AC10EBE0A8FCD216EB64C4C1A5C:FG=1"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_05.json b/jetty-http2/http2-hpack/src/test/resources/data/story_05.json
new file mode 100644
index 0000000..dfcd8e1
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_05.json
@@ -0,0 +1,366 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "geo.craigslist.org"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.craigslist.org"
+        },
+        {
+          ":path": "/about/sites/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.craigslist.org"
+        },
+        {
+          ":path": "/styles/countries.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.craigslist.org/about/sites/"
+        },
+        {
+          "cookie": "cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.craigslist.org"
+        },
+        {
+          ":path": "/js/formats.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.craigslist.org/about/sites/"
+        },
+        {
+          "cookie": "cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.craigslist.org"
+        },
+        {
+          ":path": "/js/jquery-1.4.2.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.craigslist.org/about/sites/"
+        },
+        {
+          "cookie": "cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.craigslist.org"
+        },
+        {
+          ":path": "/favicon.ico"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "shoals.craigslist.org"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.craigslist.org/about/sites/"
+        },
+        {
+          "cookie": "cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.craigslist.org"
+        },
+        {
+          ":path": "/styles/craigslist.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://shoals.craigslist.org/"
+        },
+        {
+          "cookie": "cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A; cl_def_lang=en; cl_def_hp=shoals"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.craigslist.org"
+        },
+        {
+          ":path": "/js/formats.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://shoals.craigslist.org/"
+        },
+        {
+          "cookie": "cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A; cl_def_lang=en; cl_def_hp=shoals"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.craigslist.org"
+        },
+        {
+          ":path": "/js/homepage.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://shoals.craigslist.org/"
+        },
+        {
+          "cookie": "cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A; cl_def_lang=en; cl_def_hp=shoals"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_06.json b/jetty-http2/http2-hpack/src/test/resources/data/story_06.json
new file mode 100644
index 0000000..74dac51
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_06.json
@@ -0,0 +1,342 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ebay.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.ebay.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ebay-stories.com"
+        },
+        {
+          ":path": "/wp-content/uploads/2012/11/Iso-65.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.ebay.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "rover.ebay.com"
+        },
+        {
+          ":path": "/roversync/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.ebay.com/"
+        },
+        {
+          "cookie": "ebay=%5Esbf%3D%23%5E; dp1=bpbf/%238000000000005276504d^u1p/QEBfX0BAX19AQA**5276504d^; cssg=c67883f113a0a56964e646c6ffaa1abe; s=CgAD4ACBQlm5NYzY3ODgzZjExM2EwYTU2OTY0ZTY0NmM2ZmZhYTFhYmUBSgAYUJZuTTUwOTUxY2NkLjAuMS4zLjE1MS4zLjAuMeN+7JE*; nonsession=CgAFMABhSdlBNNTA5NTFjY2QuMC4xLjEuMTQ5LjMuMC4xAMoAIFn7Hk1jNjc4ODNmMTEzYTBhNTY5NjRlNjQ2YzZmZmFhMWFjMQDLAAFQlSPVMX8u5Z8*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "p.ebaystatic.com"
+        },
+        {
+          ":path": "/aw/pics/s.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.ebay.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "p.ebaystatic.com"
+        },
+        {
+          ":path": "/aw/pics/mops/2012_doodles/Holiday/DS3/ImgWeek_1_Penguin_Small_150x30.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.ebay.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "p.ebaystatic.com"
+        },
+        {
+          ":path": "/aw/pics/globalHeader/facebook/g12.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.ebay.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "p.ebaystatic.com"
+        },
+        {
+          ":path": "/aw/pics/globalHeader/twitter/g12.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.ebay.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "p.ebaystatic.com"
+        },
+        {
+          ":path": "/aw/pics/globalHeader/icon_mobile_gray_11x16.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.ebay.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "srx.main.ebayrtm.com"
+        },
+        {
+          ":path": "/rtm"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.ebay.com/"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_07.json b/jetty-http2/http2-hpack/src/test/resources/data/story_07.json
new file mode 100644
index 0000000..cabe2c3
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_07.json
@@ -0,0 +1,345 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "static.ak.fbcdn.net"
+        },
+        {
+          ":path": "/rsrc.php/v2/yb/r/GsNJNwuI-UM.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.facebook.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "static.ak.fbcdn.net"
+        },
+        {
+          ":path": "/rsrc.php/v2/yY/r/u8iA3kXb8Y1.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.facebook.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "static.ak.fbcdn.net"
+        },
+        {
+          ":path": "/rsrc.php/v2/yI/r/qANVTsC52fp.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.facebook.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "static.ak.fbcdn.net"
+        },
+        {
+          ":path": "/rsrc.php/v2/yt/r/FZaMKqARgC6.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.facebook.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "static.ak.fbcdn.net"
+        },
+        {
+          ":path": "/rsrc.php/v2/yZ/r/jlKDoX15kHG.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.facebook.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "static.ak.fbcdn.net"
+        },
+        {
+          ":path": "/rsrc.php/v2/yO/r/_MRarphcCIq.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.facebook.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "static.ak.fbcdn.net"
+        },
+        {
+          ":path": "/rsrc.php/v2/yP/r/CRkiDDWTd1u.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.facebook.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "static.ak.fbcdn.net"
+        },
+        {
+          ":path": "/rsrc.php/v2/yX/x/Qq6L1haQrYr.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://static.ak.fbcdn.net/rsrc.php/v2/yI/r/qANVTsC52fp.css"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "static.ak.fbcdn.net"
+        },
+        {
+          ":path": "/rsrc.php/v2/yN/r/EarbWo_mDU-.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.facebook.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "static.ak.fbcdn.net"
+        },
+        {
+          ":path": "/rsrc.php/v2/y7/x/9jt7oVdF7z3.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://static.ak.fbcdn.net/rsrc.php/v2/yO/r/_MRarphcCIq.css"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_08.json b/jetty-http2/http2-hpack/src/test/resources/data/story_08.json
new file mode 100644
index 0000000..f0fefde
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_08.json
@@ -0,0 +1,363 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "flickr.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.flickr.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "BX=c99r6jp89a7no&b=3&s=q4; localization=en-us%3Bus%3Bus"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "us.adserver.yahoo.com"
+        },
+        {
+          ":path": "/a"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.flickr.com/"
+        },
+        {
+          "cookie": "B=4m2rqu589a507&b=3&s=1v; k_visit=1; MSC=t=1351947310X; CH=AgBQlRQgADwDIAAbDSAAGrIgADpuIAAoriAALMQgAAs0IAA7CCAAJ0MgABo3; ucs=bnas=0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.flickr.com"
+        },
+        {
+          ":path": "/images/share-this-icons-sprite.png.v6"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "BX=c99r6jp89a7no&b=3&s=q4; localization=en-us%3Bus%3Bus"
+        },
+        {
+          "referer": "http://www.flickr.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.flickr.com"
+        },
+        {
+          ":path": "/images/flickr-sprite.png.v4"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.flickr.com/"
+        },
+        {
+          "cookie": "BX=c99r6jp89a7no&b=3&s=q4; localization=en-us%3Bus%3Bus"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.flickr.com"
+        },
+        {
+          ":path": "/flanal_event.gne"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "BX=c99r6jp89a7no&b=3&s=q4; localization=en-us%3Bus%3Bus; ywadp10001561398679=1956875541"
+        },
+        {
+          "referer": "http://www.flickr.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "y.analytics.yahoo.com"
+        },
+        {
+          ":path": "/fpc.pl"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.flickr.com/"
+        },
+        {
+          "cookie": "B=4m2rqu589a507&b=3&s=1v; k_visit=1; MSC=t=1351947310X; CH=AgBQlRQgADwDIAAbDSAAGrIgADpuIAAoriAALMQgAAs0IAA7CCAAJ0MgABo3; ucs=bnas=0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "d.yimg.com"
+        },
+        {
+          ":path": "/ce/soup/soup_generated_fragment.gne"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.flickr.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "geo.yahoo.com"
+        },
+        {
+          ":path": "/b"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.flickr.com/"
+        },
+        {
+          "cookie": "B=4m2rqu589a507&b=3&s=1v; k_visit=1; MSC=t=1351947310X; CH=AgBQlRQgADwDIAAbDSAAGrIgADpuIAAoriAALMQgAAs0IAA7CCAAJ0MgABo3; ucs=bnas=0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.flickr.com"
+        },
+        {
+          ":path": "/photos/nasacommons/4940913342/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "BX=c99r6jp89a7no&b=3&s=q4; localization=en-us%3Bus%3Bus; ywadp10001561398679=1956875541; fl_v=souhp; fpc10001561398679=Qvv1ikW_|aUqazlyMaa|fses10001561398679=|aUqazlyMaa|Qvv1ikW_|fvis10001561398679=Zj1odHRwJTNBJTJGJTJGd3d3LmZsaWNrci5jb20lMkYmdD0xMzUxOTUwMDc1JmI9JTJGaW5kZXhfc291cC5nbmU=|8M1871YYH0|8M1871YYH0|8M1871YYH0|8|8M1871YYH0|8M1871YYH0"
+        },
+        {
+          "referer": "http://www.flickr.com/"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_09.json b/jetty-http2/http2-hpack/src/test/resources/data/story_09.json
new file mode 100644
index 0000000..b922bc7
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_09.json
@@ -0,0 +1,345 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "linkedin.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.linkedin.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s.c.lnkd.licdn.com"
+        },
+        {
+          ":path": "/scds/concat/common/js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.linkedin.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s.c.lnkd.licdn.com"
+        },
+        {
+          ":path": "/scds/concat/common/css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.linkedin.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s.c.lnkd.licdn.com"
+        },
+        {
+          ":path": "/scds/concat/common/js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.linkedin.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s.c.lnkd.licdn.com"
+        },
+        {
+          ":path": "/scds/concat/common/css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.linkedin.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s.c.lnkd.licdn.com"
+        },
+        {
+          ":path": "/scds/concat/common/css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.linkedin.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s.c.lnkd.licdn.com"
+        },
+        {
+          ":path": "/scds/concat/common/js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.linkedin.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.linkedin.com"
+        },
+        {
+          ":path": "/analytics/noauthtracker"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-requested-with": "XMLHttpRequest"
+        },
+        {
+          "referer": "http://www.linkedin.com/"
+        },
+        {
+          "cookie": "bcookie=\"v=2&bae845a5-83ed-4590-becf-f0f3d586432b\"; leo_auth_token=\"GST:UDbWFFpLLdcS6gHJ7NJa3XYRsc7W_gDwutbWnlWLfo7G_2Y4jfLH-H:1351948419:4b5c0f1309310a9b659b97d8960e64fdd635526b\"; JSESSIONID=\"ajax:0608630266152992729\"; visit=\"v=1&G\"; X-LI-IDC=C1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "s.c.lnkd.licdn.com"
+        },
+        {
+          ":path": "/scds/common/u/img/favicon_v3.ico"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.linkedin.com/"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_10.json b/jetty-http2/http2-hpack/src/test/resources/data/story_10.json
new file mode 100644
index 0000000..686aa68
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_10.json
@@ -0,0 +1,339 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "msn.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.msn.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ads1.msads.net"
+        },
+        {
+          ":path": "/library/primedns.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.msn.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "col.stj.s-msn.com"
+        },
+        {
+          ":path": "/primedns.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.msn.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "blu.stc.s-msn.com"
+        },
+        {
+          ":path": "/as/wea3/i/en-us/law/39.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.msn.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "col.stj.s-msn.com"
+        },
+        {
+          ":path": "/primedns.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.msn.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "col.stc.s-msn.com"
+        },
+        {
+          ":path": "/br/sc/i/ff/adchoices_gif2.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.msn.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "col.stb00.s-msn.com"
+        },
+        {
+          ":path": "/i/80/53CAC6A10B6248682CF221B24A92.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.msn.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "col.stb01.s-msn.com"
+        },
+        {
+          ":path": "/i/E0/A6C312635EF0A355668C820EB5343.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.msn.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "col.stb00.s-msn.com"
+        },
+        {
+          ":path": "/i/BB/B1F619A1AD4D4AA6B0648BDBBCDEED.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.msn.com/"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_11.json b/jetty-http2/http2-hpack/src/test/resources/data/story_11.json
new file mode 100644
index 0000000..cc86a09
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_11.json
@@ -0,0 +1,369 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "nytimes.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "t.pointroll.com"
+        },
+        {
+          ":path": "/PointRoll/Track/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.bbc.co.uk/news/business-20178000"
+        },
+        {
+          "cookie": "PRbu=EzZdduhgq; PRgo=BBBAAFMnA; PRti4CD975E46CAEA=B"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "t.pointroll.com"
+        },
+        {
+          ":path": "/PointRoll/Track/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.bbc.co.uk/news/business-20178000"
+        },
+        {
+          "cookie": "PRbu=EzZdduhgq; PRgo=BBBAAFMnA; PRti4CD975E46CAEA=B"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "graphics8.nytimes.com"
+        },
+        {
+          ":path": "/packages/css/multimedia/bundles/projects/2012/HPLiveDebateFlex.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.nytimes.com/"
+        },
+        {
+          "cookie": "RMID=007f010022166047bee9002b; adxcs=-"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "graphics8.nytimes.com"
+        },
+        {
+          ":path": "/js/app/common/slideshow/embeddedSlideshowBuilder.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.nytimes.com/"
+        },
+        {
+          "cookie": "RMID=007f010022166047bee9002b; adxcs=-"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "graphics8.nytimes.com"
+        },
+        {
+          ":path": "/css/0.1/screen/slideshow/modules/slidingGallery.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.nytimes.com/"
+        },
+        {
+          "cookie": "RMID=007f010022166047bee9002b; adxcs=-"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "graphics8.nytimes.com"
+        },
+        {
+          ":path": "/adx/images/ADS/31/46/ad.314668/NYT_MBM_IPHON_LEFT_Oct11.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.nytimes.com/"
+        },
+        {
+          "cookie": "RMID=007f010022166047bee9002b; adxcs=-"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "graphics8.nytimes.com"
+        },
+        {
+          ":path": "/packages/js/multimedia/bundles/projects/2012/HPLiveDebateFlex.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.nytimes.com/"
+        },
+        {
+          "cookie": "RMID=007f010022166047bee9002b; adxcs=-"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "graphics8.nytimes.com"
+        },
+        {
+          ":path": "/packages/js/multimedia/data/FilmStripPromo/2012_election_filmstrip.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.nytimes.com/"
+        },
+        {
+          "cookie": "RMID=007f010022166047bee9002b; adxcs=-"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "graphics8.nytimes.com"
+        },
+        {
+          ":path": "/packages/js/elections/2012/debates/videostrip/filmstrip.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.nytimes.com/"
+        },
+        {
+          "cookie": "RMID=007f010022166047bee9002b; adxcs=-"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_12.json b/jetty-http2/http2-hpack/src/test/resources/data/story_12.json
new file mode 100644
index 0000000..3b27fd7
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_12.json
@@ -0,0 +1,369 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "pinterest.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "media-cache-lt0.pinterest.com"
+        },
+        {
+          ":path": "/upload/164311086374323731_DhZSfIfc_b.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://pinterest.com/"
+        },
+        {
+          "cookie": "_pinterest_sess=\"eJyLMnSMyghISi53cnEMyqgo9ElPya0M1jdw9/S0tY8vycxNtfUN8TX0Dck28A9JrvQPtLVVK04tLs5MsfXM9az0C3HKicpKN/JzSa/yrQrKiswKNY3MijSJzMrI8M1KN/bNDTT1rQo08Uy3tQUAm3EkCA==\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "media-cache-lt0.pinterest.com"
+        },
+        {
+          ":path": "/upload/161637074097583855_SNjDRMKe_b.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://pinterest.com/"
+        },
+        {
+          "cookie": "_pinterest_sess=\"eJyLMnSMyghISi53cnEMyqgo9ElPya0M1jdw9/S0tY8vycxNtfUN8TX0Dck28A9JrvQPtLVVK04tLs5MsfXM9az0C3HKicpKN/JzSa/yrQrKiswKNY3MijSJzMrI8M1KN/bNDTT1rQo08Uy3tQUAm3EkCA==\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "media-cache-lt0.pinterest.com"
+        },
+        {
+          ":path": "/upload/273593746083022624_FCoEkXsC_b.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://pinterest.com/"
+        },
+        {
+          "cookie": "_pinterest_sess=\"eJyLMnSMyghISi53cnEMyqgo9ElPya0M1jdw9/S0tY8vycxNtfUN8TX0Dck28A9JrvQPtLVVK04tLs5MsfXM9az0C3HKicpKN/JzSa/yrQrKiswKNY3MijSJzMrI8M1KN/bNDTT1rQo08Uy3tQUAm3EkCA==\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "media-cache-lt0.pinterest.com"
+        },
+        {
+          ":path": "/upload/52917364342893663_qtPmJgkx_b.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://pinterest.com/"
+        },
+        {
+          "cookie": "_pinterest_sess=\"eJyLMnSMyghISi53cnEMyqgo9ElPya0M1jdw9/S0tY8vycxNtfUN8TX0Dck28A9JrvQPtLVVK04tLs5MsfXM9az0C3HKicpKN/JzSa/yrQrKiswKNY3MijSJzMrI8M1KN/bNDTT1rQo08Uy3tQUAm3EkCA==\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "media-cache-lt0.pinterest.com"
+        },
+        {
+          ":path": "/upload/116952921544035902_KyTWinzm_b.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://pinterest.com/"
+        },
+        {
+          "cookie": "_pinterest_sess=\"eJyLMnSMyghISi53cnEMyqgo9ElPya0M1jdw9/S0tY8vycxNtfUN8TX0Dck28A9JrvQPtLVVK04tLs5MsfXM9az0C3HKicpKN/JzSa/yrQrKiswKNY3MijSJzMrI8M1KN/bNDTT1rQo08Uy3tQUAm3EkCA==\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "media-cache-lt0.pinterest.com"
+        },
+        {
+          ":path": "/upload/283445370267774252_AttBMVfT_b.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://pinterest.com/"
+        },
+        {
+          "cookie": "_pinterest_sess=\"eJyLMnSMyghISi53cnEMyqgo9ElPya0M1jdw9/S0tY8vycxNtfUN8TX0Dck28A9JrvQPtLVVK04tLs5MsfXM9az0C3HKicpKN/JzSa/yrQrKiswKNY3MijSJzMrI8M1KN/bNDTT1rQo08Uy3tQUAm3EkCA==\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "media-cache-lt0.pinterest.com"
+        },
+        {
+          ":path": "/upload/237142736599025827_ufDEHdRe_b.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://pinterest.com/"
+        },
+        {
+          "cookie": "_pinterest_sess=\"eJyLMnSMyghISi53cnEMyqgo9ElPya0M1jdw9/S0tY8vycxNtfUN8TX0Dck28A9JrvQPtLVVK04tLs5MsfXM9az0C3HKicpKN/JzSa/yrQrKiswKNY3MijSJzMrI8M1KN/bNDTT1rQo08Uy3tQUAm3EkCA==\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "media-cache-lt0.pinterest.com"
+        },
+        {
+          ":path": "/upload/224194887669533381_UBmi659g_b.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://pinterest.com/"
+        },
+        {
+          "cookie": "_pinterest_sess=\"eJyLMnSMyghISi53cnEMyqgo9ElPya0M1jdw9/S0tY8vycxNtfUN8TX0Dck28A9JrvQPtLVVK04tLs5MsfXM9az0C3HKicpKN/JzSa/yrQrKiswKNY3MijSJzMrI8M1KN/bNDTT1rQo08Uy3tQUAm3EkCA==\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "media-cache-lt0.pinterest.com"
+        },
+        {
+          ":path": "/upload/274156696036479907_A1ezgnsj_b.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://pinterest.com/"
+        },
+        {
+          "cookie": "_pinterest_sess=\"eJyLMnSMyghISi53cnEMyqgo9ElPya0M1jdw9/S0tY8vycxNtfUN8TX0Dck28A9JrvQPtLVVK04tLs5MsfXM9az0C3HKicpKN/JzSa/yrQrKiswKNY3MijSJzMrI8M1KN/bNDTT1rQo08Uy3tQUAm3EkCA==\""
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_13.json b/jetty-http2/http2-hpack/src/test/resources/data/story_13.json
new file mode 100644
index 0000000..a692cb5
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_13.json
@@ -0,0 +1,342 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "qq.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "mat1.gtimg.com"
+        },
+        {
+          ":path": "/www/images/qq2012/followme.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.qq.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "mat1.gtimg.com"
+        },
+        {
+          ":path": "/www/images/qq2012/sosologo.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.qq.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "mat1.gtimg.com"
+        },
+        {
+          ":path": "/www/images/qq2012/festival/da18search.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.qq.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "mat1.gtimg.com"
+        },
+        {
+          ":path": "/www/images/qq2012/festival/da18bodybg05.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.qq.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "mat1.gtimg.com"
+        },
+        {
+          ":path": "/www/images/qq2012/loginall_1.2.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.qq.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "mat1.gtimg.com"
+        },
+        {
+          ":path": "/www/images/qq2012/aikanLoading1.1.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.qq.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "mat1.gtimg.com"
+        },
+        {
+          ":path": "/joke/Koala/Qfast1.0.1.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.qq.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "mat1.gtimg.com"
+        },
+        {
+          ":path": "/www/images/qq2012/mobileNews.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.qq.com/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "img1.gtimg.com"
+        },
+        {
+          ":path": "/v/pics/hv1/241/117/1186/77149726.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.qq.com/"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_14.json b/jetty-http2/http2-hpack/src/test/resources/data/story_14.json
new file mode 100644
index 0000000..c07d85f
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_14.json
@@ -0,0 +1,339 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "sina.com.cn"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.sina.com.cn"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "news.sina.com.cn"
+        },
+        {
+          ":path": "/js/87/20121024/201218ConfTop.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.sina.com.cn/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "int.dpool.sina.com.cn"
+        },
+        {
+          ":path": "/iplookup/iplookup.php"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.sina.com.cn/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i3.sinaimg.cn"
+        },
+        {
+          ":path": "/video/2012/1103/U7805P167DT20121103211853.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.sina.com.cn/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i3.sinaimg.cn"
+        },
+        {
+          ":path": "/home/2012/1102/U6041P30DT20121102122146.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.sina.com.cn/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i3.sinaimg.cn"
+        },
+        {
+          ":path": "/home/deco/2009/0330/logo_home.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.sina.com.cn/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "d1.sina.com.cn"
+        },
+        {
+          ":path": "/shh/lechan/20121016sina/logo1.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.sina.com.cn/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i0.sinaimg.cn"
+        },
+        {
+          ":path": "/home/2012/1103/U8551P30DT20121103063734.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.sina.com.cn/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i1.sinaimg.cn"
+        },
+        {
+          ":path": "/home/2012/1101/U6648P30DT20121101141432.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.sina.com.cn/"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_15.json b/jetty-http2/http2-hpack/src/test/resources/data/story_15.json
new file mode 100644
index 0000000..1bd29a3
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_15.json
@@ -0,0 +1,336 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "taobao.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.taobao.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.taobao.com"
+        },
+        {
+          ":path": "/index_global.php"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "a.tbcdn.cn"
+        },
+        {
+          ":path": "/p/fp/2011a/assets/space.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.taobao.com/index_global.php"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "a.tbcdn.cn"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.taobao.com/index_global.php"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "a.tbcdn.cn"
+        },
+        {
+          ":path": "/p/fp/2011hk/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.taobao.com/index_global.php"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "a.tbcdn.cn"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.taobao.com/index_global.php"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "a.tbcdn.cn"
+        },
+        {
+          ":path": "/p/fp/2010c/js/fp-direct-promo-min.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.taobao.com/index_global.php"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "img01.taobaocdn.com"
+        },
+        {
+          ":path": "/tps/i1/T1fqY2XilfXXahsVgc-1000-40.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.taobao.com/index_global.php"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "img01.taobaocdn.com"
+        },
+        {
+          ":path": "/tps/i1/T1rZiwXgtfXXXXXXXX-110-135.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.taobao.com/index_global.php"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_16.json b/jetty-http2/http2-hpack/src/test/resources/data/story_16.json
new file mode 100644
index 0000000..e638e7d
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_16.json
@@ -0,0 +1,375 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "en.wikipedia.org"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "centralnotice_bucket=1; clicktracking-session=eJko6IiUcEm69ehQfaakQlJfiLy9lShNP; mediaWiki.user.bucket%3Aext.articleFeedback-tracking=10%3Atrack; mediaWiki.user.id=EM83jsjaqPzIMLwBTiKF3aLiiTKeweez; mediaWiki.user.bucket%3Aext.articleFeedback-options=8%3Ashow"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "en.wikipedia.org"
+        },
+        {
+          ":path": "/wiki/Main_Page"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "centralnotice_bucket=1; clicktracking-session=eJko6IiUcEm69ehQfaakQlJfiLy9lShNP; mediaWiki.user.bucket%3Aext.articleFeedback-tracking=10%3Atrack; mediaWiki.user.id=EM83jsjaqPzIMLwBTiKF3aLiiTKeweez; mediaWiki.user.bucket%3Aext.articleFeedback-options=8%3Ashow"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "bits.wikimedia.org"
+        },
+        {
+          ":path": "/en.wikipedia.org/load.php"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://en.wikipedia.org/wiki/Main_Page"
+        },
+        {
+          "if-modified-since": "Wed, 31 Oct 2012 17:52:04 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "bits.wikimedia.org"
+        },
+        {
+          ":path": "/en.wikipedia.org/load.php"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://en.wikipedia.org/wiki/Main_Page"
+        },
+        {
+          "if-modified-since": "Thu, 01 Nov 2012 09:33:27 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "bits.wikimedia.org"
+        },
+        {
+          ":path": "/en.wikipedia.org/load.php"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://en.wikipedia.org/wiki/Main_Page"
+        },
+        {
+          "if-modified-since": "Sat, 03 Nov 2012 12:53:27 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "bits.wikimedia.org"
+        },
+        {
+          ":path": "/en.wikipedia.org/load.php"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://en.wikipedia.org/wiki/Main_Page"
+        },
+        {
+          "if-modified-since": "Wed, 31 Oct 2012 17:52:04 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "bits.wikimedia.org"
+        },
+        {
+          ":path": "/en.wikipedia.org/load.php"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://en.wikipedia.org/wiki/Main_Page"
+        },
+        {
+          "if-modified-since": "Thu, 01 Nov 2012 09:33:27 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "upload.wikimedia.org"
+        },
+        {
+          ":path": "/wikipedia/en/c/ca/Kanthirava_cropped.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://en.wikipedia.org/wiki/Main_Page"
+        },
+        {
+          "if-modified-since": "Fri, 02 Nov 2012 23:46:59 GMT"
+        },
+        {
+          "if-none-match": "288bdb2fd5e5a4f7272f58fcb083a7e1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "upload.wikimedia.org"
+        },
+        {
+          ":path": "/wikipedia/commons/thumb/d/d2/Dancing_girl_ajanta_%28cropped%29.jpg/72px-Dancing_girl_ajanta_%28cropped%29.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://en.wikipedia.org/wiki/Main_Page"
+        },
+        {
+          "if-modified-since": "Tue, 30 Oct 2012 17:37:15 GMT"
+        },
+        {
+          "if-none-match": "6e8d56df9be35494b4d9f0ea72ed1a3e"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "bits.wikimedia.org"
+        },
+        {
+          ":path": "/en.wikipedia.org/load.php"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://en.wikipedia.org/wiki/Main_Page"
+        },
+        {
+          "if-modified-since": "Sat, 03 Nov 2012 12:53:27 GMT"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_17.json b/jetty-http2/http2-hpack/src/test/resources/data/story_17.json
new file mode 100644
index 0000000..50fa4ca
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_17.json
@@ -0,0 +1,348 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yahoo.co.jp"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.yahoo.co.jp"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/top/sp2/clr/1/clr-121025.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/top/sp/logo.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/bookstore/common/special/2012/0829_05/banner/84x84_1.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/oi"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/weather/general/transparent_s/clouds.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/weather/general/transparent_s/sun.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/bookstore/common/special/2012/0829_05/banner/84x84_2.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/premium/contents/bnr/2012/50x50/0928_store_supernatural.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_18.json b/jetty-http2/http2-hpack/src/test/resources/data/story_18.json
new file mode 100644
index 0000000..43377fb
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_18.json
@@ -0,0 +1,351 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yahoo.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "d.yimg.com"
+        },
+        {
+          ":path": "/hd/ch7news/7_world/1103_0700_nat_elephant_sml_1898chj-1898chl.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://au.yahoo.com/?p=us"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "au.yahoo.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "B=4m2rqu589a507&b=3&s=1v"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "d.yimg.com"
+        },
+        {
+          ":path": "/mi/ywa.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://au.yahoo.com/?p=us"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yui.yahooapis.com"
+        },
+        {
+          ":path": "/combo"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://au.yahoo.com/?p=us"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "secure-au.imrworldwide.com"
+        },
+        {
+          ":path": "/cgi-bin/m"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://au.yahoo.com/?p=us"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "chart.finance.yahoo.com"
+        },
+        {
+          ":path": "/instrument/1.0/%5Eaxjo/chart;range=5d/image;size=179x98"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://au.yahoo.com/?p=us"
+        },
+        {
+          "cookie": "B=4m2rqu589a507&b=3&s=1v; session_start_time=1351947275160; k_visit=1; push_time_start=1351947295160"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "chart.finance.yahoo.com"
+        },
+        {
+          ":path": "/instrument/1.0/%5Eaord/chart;range=5d/image;size=179x98"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://au.yahoo.com/?p=us"
+        },
+        {
+          "cookie": "B=4m2rqu589a507&b=3&s=1v; session_start_time=1351947275160; k_visit=1; push_time_start=1351947295160"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "chart.finance.yahoo.com"
+        },
+        {
+          ":path": "/instrument/1.0/audusd=x/chart;range=5d/image;size=179x98"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://au.yahoo.com/?p=us"
+        },
+        {
+          "cookie": "B=4m2rqu589a507&b=3&s=1v; session_start_time=1351947275160; k_visit=1; push_time_start=1351947295160"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "cm.au.yahoo.overture.com"
+        },
+        {
+          ":path": "/js_flat_1_0/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://au.yahoo.com/?p=us"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_19.json b/jetty-http2/http2-hpack/src/test/resources/data/story_19.json
new file mode 100644
index 0000000..fe8dd3a
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_19.json
@@ -0,0 +1,345 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yandex.ru"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.yandex.ru"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yabs.yandex.ru"
+        },
+        {
+          ":path": "/count/Vnw_3zF2dkO40002Zhl8KGa5KPK2cmPfMeYpO2zG0vAeOuAefZIAgoA2KAe2fPOOP96yq4ba1fDKGQC1hlDVeQN8GfVD17e7"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yandex.ru/"
+        },
+        {
+          "cookie": "t=p; yandexuid=6410453771351949451"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yabs.yandex.ru"
+        },
+        {
+          ":path": "/count/Vnw_3mft8wq40000Zhl8KGa5KP6yq4ba1fDKhlDVeQN8GfVD17a3=qcOn49K2cmPfMcbQagXZWgYAgoA2KAMM66IcD7W3"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yandex.ru/"
+        },
+        {
+          "cookie": "t=p; yandexuid=6410453771351949451"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yandex.st"
+        },
+        {
+          ":path": "/lego/_/pDu9OWAQKB0s2J9IojKpiS_Eho.ico"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yandex.st"
+        },
+        {
+          ":path": "/www/1.359/www/i/yandex3.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yandex.ru/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yandex.st"
+        },
+        {
+          ":path": "/morda-logo/i/logo.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yandex.ru/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "mc.yandex.ru"
+        },
+        {
+          ":path": "/watch/722545"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yandex.ru/"
+        },
+        {
+          "cookie": "t=p; yandexuid=6410453771351949451"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yandex.st"
+        },
+        {
+          ":path": "/www/1.359/www/pages-desktop/www-css/_www-css.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yandex.ru/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yandex.st"
+        },
+        {
+          ":path": "/www/_/_r7pp-b-hKoDbgyGYy0IB3wlkno.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yandex.ru/"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_20.json b/jetty-http2/http2-hpack/src/test/resources/data/story_20.json
new file mode 100644
index 0000000..f86f11a
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_20.json
@@ -0,0 +1,5674 @@
+{
+  "context": "request",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yahoo.co.jp"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.yahoo.co.jp"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/top/sp2/clr/1/clr-121025.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/top/sp/logo.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/bookstore/common/special/2012/0829_05/banner/84x84_1.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/oi"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/weather/general/transparent_s/clouds.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/weather/general/transparent_s/sun.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/bookstore/common/special/2012/0829_05/banner/84x84_2.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/premium/contents/bnr/2012/50x50/0928_store_supernatural.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/bookstore/common/special/2012/0829_05/banner/84x84_3.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/bookstore/common/special/2012/0829_05/banner/84x84_5.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/500052/1080894/20121029/meulz5rknmobtjfqmyz8-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/oi"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/70506/1082209/20121024/ffmwiwdybofwysftxna1-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/yahoo/javascript/yfa_visual5_tbp.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.yahoo.co.jp"
+        },
+        {
+          ":path": "/javascript/fp_base_bd_ga_5.0.42.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/yahoo/javascript/csc/20060824/lib2obf_b6.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/top/sp2/pr/tb_bg-120110.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/top/sp2/uhd/homepage_bg-120123.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/sicons/bookstore16.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/sicons/movie16.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/sicons/game16.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/top/sp2/cmn/pic_all-121004.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/sicons/fortune16.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/top/sp2/emg/disaster_ttl2.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/video-topics/rec/1211/03_e01.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/top/sp2/clr/1/clr-121025.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://k.yimg.jp/images/top/sp2/clr/1/clr-121025.css"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/top/sp2/cmp/comp_all-121012.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "k.yimg.jp"
+        },
+        {
+          ":path": "/images/top/sp2/spotlight/2011/1031o.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "news.c.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/20121103-00000193-sph-000-thumb.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/2237/1080330/20121103/bg6so7sbgcqenc9py6xk-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "www.yahoo.co.jp"
+        },
+        {
+          ":path": "/favicon.ico"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/images/security/pf/yjsecure.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/js/yjaxc.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "dailynews.yahoo.co.jp"
+        },
+        {
+          ":path": "/fc/sports/nippon_series/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://www.yahoo.co.jp/"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b; YJTOPICSFBREAD=d=juTus3MJdA6fAPKQn3MJyoWvkTaY6I2RngPiVKE3BMv8AFX.C4TMg0utwM_uXg_sKn7y2yDVFKE-&v=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "dailynews.yahoo.co.jp"
+        },
+        {
+          ":path": "/fc/js/fb_pc.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b; YJTOPICSFBREAD=d=juTus3MJdA6fAPKQn3MJyoWvkTaY6I2RngPiVKE3BMv8AFX.C4TMg0utwM_uXg_sKn7y2yDVFKE-&v=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/71629/1073618/20121029/ypxcyyekc_ruhypdisqu-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/fc.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/icon/photo.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/mh/news.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/30/1077242/20121029/ixbislu9ygczxzdkfnpt-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/facebook/news_Facebook_76x76.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/rapid/1.5.0/ult.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/new2.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/wiki/nestopics_icon_40.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/yahoo/javascript/yfa_visual5.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/193/1072227/20121029/uyzwkpexjszyi2zgct4p-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/2959/1085127/20121102/dalvv9p9fw9tribawawe-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/2959/1085124/20121102/bz9rzgnremydaxbp4ihb-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/wiki/editor/topics_pr_linkimg_l2.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "lpt.c.yimg.jp"
+        },
+        {
+          ":path": "/im_sigg537mI30DS9hWeZeGpWl75Q---x200-y190-q90/amd/20121103-00000542-sanspo-000-view.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "amd.c.yimg.jp"
+        },
+        {
+          ":path": "/im_siggHulEjLwgzPyrVDkZ9oNPng---x200-y133-q90/amd/20121031-00005828-yj_corptrend-000-51670401-view.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "amd.c.yimg.jp"
+        },
+        {
+          ":path": "/im_siggrMDL3ZpnqnwM4Z1FYvhX2Q---x200-y133-q90/amd/20121101-00005830-yj_corptrend-000-51751400-view.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/css/import_ver2.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/commerce/js/libs/jquery/core/1.4.2/jquery.min.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/yui/jp/uhd/olympic/1.0.2/img/uhdChnk.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/v1/yn_gnavi_sprite_20120926.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/topics/css/import_ver2.css?date=20121029"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/oi"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/oi"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/social/btnMx.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/topics/css/import_ver2.css?date=20121029"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/wiki/ytopics_sprite_icons.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/topics/css/import_ver2.css?date=20121029"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/wiki/ytopics_sprite_backgrounds.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/topics/css/import_ver2.css?date=20121029"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/wiki/relTabLeft.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/topics/css/import_ver2.css?date=20121029"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/wiki/relTabRight.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/topics/css/import_ver2.css?date=20121029"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/wiki/bullet_list.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/topics/css/import_ver2.css?date=20121029"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/yui/jp/uft/1.0.0/img/utfChnk.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/wiki/accountTitleBg.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/topics/css/import_ver2.css?date=20121029"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/social/sprite_icoSns16.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/topics/css/import_ver2.css?date=20121029"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/topics/wiki/trendTitleBg.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/topics/css/import_ver2.css?date=20121029"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/yahoo/javascript/csc/20060824/lib2obf_b4.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ah.yimg.jp"
+        },
+        {
+          ":path": "/bdv/164354/1084075/20121101/4feasfvz47csxcoydlvl-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "platform.twitter.com"
+        },
+        {
+          ":path": "/widgets.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        },
+        {
+          "cookie": "pid=v3:1351947306477664316206054; k=10.35.101.123.1351947536129989; guest_id=v1%3A135194753658491573; __utma=43838368.2140315505.1351947542.1351947542.1351947542.1; __utmb=43838368.2.10.1351947542; __utmz=43838368.1351947542.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "platform.twitter.com"
+        },
+        {
+          ":path": "/widgets/tweet_button.1351848862.html"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        },
+        {
+          "cookie": "pid=v3:1351947306477664316206054; k=10.35.101.123.1351947536129989; guest_id=v1%3A135194753658491573; __utma=43838368.2140315505.1351947542.1351947542.1351947542.1; __utmb=43838368.2.10.1351947542; __utmz=43838368.1351947542.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "connect.facebook.net"
+        },
+        {
+          ":path": "/ja_JP/all.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "bkskapi.dailynews.yahoo.co.jp"
+        },
+        {
+          ":path": "/detail"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b; YJTOPICSFBREAD=d=juTus3MJdA6fAPKQn3MJyoWvkTaY6I2RngPiVKE3BMv8AFX.C4TMg0utwM_uXg_sKn7y2yDVFKE-&v=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "platform.twitter.com"
+        },
+        {
+          ":path": "/widgets/follow_button.1351848862.html"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        },
+        {
+          "cookie": "pid=v3:1351947306477664316206054; k=10.35.101.123.1351947536129989; guest_id=v1%3A135194753658491573; __utma=43838368.2140315505.1351947542.1351947542.1351947542.1; __utmb=43838368.2.10.1351947542; __utmz=43838368.1351947542.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "p.twitter.com"
+        },
+        {
+          ":path": "/t.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://platform.twitter.com/widgets/tweet_button.1351848862.html"
+        },
+        {
+          "cookie": "pid=v3:1351947306477664316206054; k=10.35.101.123.1351947536129989; guest_id=v1%3A135194753658491573; __utma=43838368.2140315505.1351947542.1351947542.1351947542.1; __utmb=43838368.2.10.1351947542; __utmz=43838368.1351947542.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "cdn.api.twitter.com"
+        },
+        {
+          ":path": "/1/urls/count.json"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://platform.twitter.com/widgets/tweet_button.1351848862.html"
+        },
+        {
+          "cookie": "pid=v3:1351947306477664316206054; k=10.35.101.123.1351947536129989; guest_id=v1%3A135194753658491573; __utma=43838368.2140315505.1351947542.1351947542.1351947542.1; __utmb=43838368.2.10.1351947542; __utmz=43838368.1351947542.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "r.twimg.com"
+        },
+        {
+          ":path": "/jot"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://platform.twitter.com/widgets/tweet_button.1351848862.html"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "cdn.api.twitter.com"
+        },
+        {
+          ":path": "/1/users/show.json"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://platform.twitter.com/widgets/follow_button.1351848862.html"
+        },
+        {
+          "cookie": "pid=v3:1351947306477664316206054; k=10.35.101.123.1351947536129989; guest_id=v1%3A135194753658491573; __utma=43838368.2140315505.1351947542.1351947542.1351947542.1; __utmb=43838368.2.10.1351947542; __utmz=43838368.1351947542.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "p.twitter.com"
+        },
+        {
+          ":path": "/f.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://platform.twitter.com/widgets/follow_button.1351848862.html"
+        },
+        {
+          "cookie": "pid=v3:1351947306477664316206054; k=10.35.101.123.1351947536129989; guest_id=v1%3A135194753658491573; __utma=43838368.2140315505.1351947542.1351947542.1351947542.1; __utmb=43838368.2.10.1351947542; __utmz=43838368.1351947542.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "r.twimg.com"
+        },
+        {
+          ":path": "/jot"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://platform.twitter.com/widgets/follow_button.1351848862.html"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "POST"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ocsp.verisign.com"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "115"
+        },
+        {
+          "content-type": "application/ocsp-request"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "dailynews.yahoo.co.jp"
+        },
+        {
+          ":path": "/favicon.ico"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b; YJTOPICSFBREAD=d=juTus3MJdA6fAPKQn3MJyoWvkTaY6I2RngPiVKE3BMv8AFX.C4TMg0utwM_uXg_sKn7y2yDVFKE-&v=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/css/yj2.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/clear.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/cobranding/sanspo.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/lib/news/json/jsr_class_1_1.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "headlines.yahoo.co.jp"
+        },
+        {
+          ":path": "/hl"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://dailynews.yahoo.co.jp/fc/sports/nippon_series/?1351933494"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/lib/news/socialModule/realtimeSearch_1_0-min.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/lib/news/jquery/jquery.template.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/lib/news/socialModule/facebook_1_3_1-min.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "amd.c.yimg.jp"
+        },
+        {
+          ":path": "/im_siggvNnG417_XZJF5TsJPh7FFQ---x200-y190-q90/amd/20121103-00000542-sanspo-000-4-view.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/70506/1082210/20121024/0ffcv4drh8ir07jvroju-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/71629/1082189/20121029/2n1tdzicd8j7eotfexbi-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/lib/news/jquery/jquery.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/lib/news/widgets/widgets_1_1.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/2959/1085127/20121102/ilaj2_d_zo_9bjginqty-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/2959/1085124/20121102/vval_chos32k_7okick9-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/30/1072134/20121029/qv2c_lhjbra0qr5ps55w-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/v1/css/master-news.css"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/css,*/*;q=0.1"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/oi"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/v1/news_socialbutton.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/news/v1/css/master-news.css?v11"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/media/ymui/img/lineWide_4x1.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/news/v1/css/master-news.css?v11"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/v1/yn_sprite_icons.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/news/v1/css/master-news.css?v11"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/media/ymui/img/carrrot_2.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/news/v1/css/master-news.css?v11"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/media/ymui/img/photoNew_45x15.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/news/v1/css/master-news.css?v11"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/v1/sprite_bgRTSearchBox.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/news/v1/css/master-news.css?v11"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/v1/sprite_icoTwitter.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/news/v1/css/master-news.css?v11"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/v1/yn_sprite_background.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/news/v1/css/master-news.css?v11"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/v1/ranking.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/news/v1/css/master-news.css?v11"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/v1/yn_gnavi_sprite.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/news/v1/css/master-news.css?v11"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/oi"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/yui/jp/ult/arrow.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/listing/tool/yjaxc/yjaxc-iframe.html"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "realtime.search.yahooapis.jp"
+        },
+        {
+          ":path": "/v1/post"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/lib/news/widgets/tweet_button.html"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/oi"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/listing/tool/yjaxc/yjaxc-iframe.html?src0=http%3A%2F%2Fyjaxc.yahoo.co.jp%2Foi%3Fs%3Danemos_news01295%26i%3D2078709534%26w%3D%26apg%3D1%26t%3Dj%26u%3Dhttp%253A%252F%252Fheadlines.yahoo.co.jp%252Fhl%253Fa%253D20121103-00000542-sanspo-base%26ref%3Dhttp%253A%252F%252Fdailynews.yahoo.co.jp%252Ffc%252Fsports%252Fnippon_series%252F%253F1351933494%26enc%3DEUC-JP%26spaceId%3D%26jisx0402%3D%26type%3D%26crawlUrl%3D&src1=http%3A%2F%2Fyjaxc.yahoo.co.jp%2Fjs%2Fyjaxc-internal-banner.js%3Fimgurl%3Dhttp%253A%252F%252Fah.yimg.jp%252Fimages%252Fim%252Finnerad%252F%26imgpath%3Dbnr1_ss_1_300-250.jpg%26clickurl%3Dhttp%253A%252F%252Frd.yahoo.co.jp%252Fbzc%252Fsds%252F97648%252Fevt%253D97648%252F*http%253A%252F%252Flisting.yahoo.co.jp%252Fy_promo%252Flisting01%252F%253Fo%253DJP1350"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "headlines.yahoo.co.jp"
+        },
+        {
+          ":path": "/sc/show"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ah.yimg.jp"
+        },
+        {
+          ":path": "/bdv/164354/1080825/20121101/hii0znrxvsbx0dt01k_g-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/lib/news/widgets/images/tweet.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/lib/news/widgets/tweet_button.html?_=1351949189638&count=none&id=twitter_tweet_button_2&lang=ja&original_referer=http%3A%2F%2Fheadlines.yahoo.co.jp%2Fhl%3Fa%3D20121103-00000542-sanspo-base&redirect=&text=%E5%B7%A8%E4%BA%BA%E3%81%8C%EF%BC%93%E5%B9%B4%E3%81%B6%E3%82%8A%E6%97%A5%E6%9C%AC%E4%B8%80%EF%BC%81%E5%BE%A9%E5%B8%B0%E3%81%AE%E9%98%BF%E9%83%A8%E3%81%8C%E6%B1%BA%E5%8B%9D%E6%89%93%EF%BC%88%E3%82%B5%E3%83%B3%E3%82%B1%E3%82%A4%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%84%EF%BC%89%20-%20Y!%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9&url=http%3A%2F%2Fheadlines.yahoo.co.jp%2Fhl%3Fa%3D20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/lib/news/widgets/images/tweet_ja.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/lib/news/widgets/tweet_button.html?_=1351949189638&count=none&id=twitter_tweet_button_2&lang=ja&original_referer=http%3A%2F%2Fheadlines.yahoo.co.jp%2Fhl%3Fa%3D20121103-00000542-sanspo-base&redirect=&text=%E5%B7%A8%E4%BA%BA%E3%81%8C%EF%BC%93%E5%B9%B4%E3%81%B6%E3%82%8A%E6%97%A5%E6%9C%AC%E4%B8%80%EF%BC%81%E5%BE%A9%E5%B8%B0%E3%81%AE%E9%98%BF%E9%83%A8%E3%81%8C%E6%B1%BA%E5%8B%9D%E6%89%93%EF%BC%88%E3%82%B5%E3%83%B3%E3%82%B1%E3%82%A4%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%84%EF%BC%89%20-%20Y!%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9&url=http%3A%2F%2Fheadlines.yahoo.co.jp%2Fhl%3Fa%3D20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/lib/news/widgets/tweet_button.html"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "puffer.c.yimg.jp"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "puffer.c.yimg.jp"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "puffer.c.yimg.jp"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "puffer.c.yimg.jp"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/images/im/imgim/pc2/im1001149230pcmr1.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/listing/tool/yjaxc/yjaxc-iframe.html?src0=http%3A%2F%2Fyjaxc.yahoo.co.jp%2Foi%3Fs%3Danemos_news01295%26i%3D2078709534%26w%3D%26apg%3D1%26t%3Dj%26u%3Dhttp%253A%252F%252Fheadlines.yahoo.co.jp%252Fhl%253Fa%253D20121103-00000542-sanspo-base%26ref%3Dhttp%253A%252F%252Fdailynews.yahoo.co.jp%252Ffc%252Fsports%252Fnippon_series%252F%253F1351933494%26enc%3DEUC-JP%26spaceId%3D%26jisx0402%3D%26type%3D%26crawlUrl%3D&src1=http%3A%2F%2Fyjaxc.yahoo.co.jp%2Fjs%2Fyjaxc-internal-banner.js%3Fimgurl%3Dhttp%253A%252F%252Fah.yimg.jp%252Fimages%252Fim%252Finnerad%252F%26imgpath%3Dbnr1_ss_1_300-250.jpg%26clickurl%3Dhttp%253A%252F%252Frd.yahoo.co.jp%252Fbzc%252Fsds%252F97648%252Fevt%253D97648%252F*http%253A%252F%252Flisting.yahoo.co.jp%252Fy_promo%252Flisting01%252F%253Fo%253DJP1350"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "urls.api.twitter.com"
+        },
+        {
+          ":path": "/1/urls/count.json"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/lib/news/widgets/tweet_button.html?_=1351949189638&count=none&id=twitter_tweet_button_2&lang=ja&original_referer=http%3A%2F%2Fheadlines.yahoo.co.jp%2Fhl%3Fa%3D20121103-00000542-sanspo-base&redirect=&text=%E5%B7%A8%E4%BA%BA%E3%81%8C%EF%BC%93%E5%B9%B4%E3%81%B6%E3%82%8A%E6%97%A5%E6%9C%AC%E4%B8%80%EF%BC%81%E5%BE%A9%E5%B8%B0%E3%81%AE%E9%98%BF%E9%83%A8%E3%81%8C%E6%B1%BA%E5%8B%9D%E6%89%93%EF%BC%88%E3%82%B5%E3%83%B3%E3%82%B1%E3%82%A4%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%84%EF%BC%89%20-%20Y!%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9&url=http%3A%2F%2Fheadlines.yahoo.co.jp%2Fhl%3Fa%3D20121103-00000542-sanspo-base"
+        },
+        {
+          "cookie": "pid=v3:1351947306477664316206054; k=10.35.101.123.1351947536129989; guest_id=v1%3A135194753658491573; __utma=43838368.2140315505.1351947542.1351947542.1351947542.1; __utmb=43838368.2.10.1351947542; __utmz=43838368.1351947542.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "puffer.c.yimg.jp"
+        },
+        {
+          ":path": "/"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "headlines.yahoo.co.jp"
+        },
+        {
+          ":path": "/favicon.ico"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b; YJNEWSCOMMENT=d=06yLIwT4EjfCUHM_ZATPTTC.be6FwFeXux__KeWeqlDK.dHwKDQYqgJFHj9.HJlNGmTwWgk.h4h.sU8V_TDfRcrHwDjLWrrsKoxSuxiWaUP8PvEUAbJUe9xIk79LoWKr6tlDjWRkyBVLbqWqtJB_axSkadUO&v=1; YJNEWSFB=d=g52f45b4EjeJqzak676NkVYuFf6EMD66FMZIfmpIG32ywhp.ZRx6EAf7vGDLjqk7eQGKmGgvFcgo&v=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "headlines.yahoo.co.jp"
+        },
+        {
+          ":path": "/hl"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?a=20121103-00000542-sanspo-base"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b; YJNEWSCOMMENT=d=06yLIwT4EjfCUHM_ZATPTTC.be6FwFeXux__KeWeqlDK.dHwKDQYqgJFHj9.HJlNGmTwWgk.h4h.sU8V_TDfRcrHwDjLWrrsKoxSuxiWaUP8PvEUAbJUe9xIk79LoWKr6tlDjWRkyBVLbqWqtJB_axSkadUO&v=1; YJNEWSFB=d=g52f45b4EjeJqzak676NkVYuFf6EMD66FMZIfmpIG32ywhp.ZRx6EAf7vGDLjqk7eQGKmGgvFcgo&v=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/images/tech/bcn1.js"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=moto&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/3124/1073472/20121029/mkgcwzthl_hbpnxy_tno-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=moto&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/71629/1074517/20121029/kqo8rgbu_aykmxxf3cqf-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=moto&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/media/ymui/img/carrrot_5.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://i.yimg.jp/images/news/v1/css/master-news.css?v11"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/2959/1085127/20121102/crs1gvpzl74_ilnbd8yl-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=moto&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/bylines/v201209/main/bnr/bnr_300x90.png"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=moto&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/oi"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=moto&t=l"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/twitter/tw_spo_300_60.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=moto&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/193/1074340/20121029/rhnusvihwpg9khhap9vn-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=moto&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/1862/1083691/20121030/fk8wxszqyglpfwkac5kl-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=moto&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "headlines.yahoo.co.jp"
+        },
+        {
+          ":path": "/hl"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=moto&t=l"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b; YJNEWSCOMMENT=d=06yLIwT4EjfCUHM_ZATPTTC.be6FwFeXux__KeWeqlDK.dHwKDQYqgJFHj9.HJlNGmTwWgk.h4h.sU8V_TDfRcrHwDjLWrrsKoxSuxiWaUP8PvEUAbJUe9xIk79LoWKr6tlDjWRkyBVLbqWqtJB_axSkadUO&v=1; YJNEWSFB=d=g52f45b4EjeJqzak676NkVYuFf6EMD66FMZIfmpIG32ywhp.ZRx6EAf7vGDLjqk7eQGKmGgvFcgo&v=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/71629/1082190/20121029/_xhxh5ukdv3s6oigo4bq-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=socc&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/71629/1075880/20121029/vstketkrv3fci_zfj0fb-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=socc&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/30/1072203/20121029/rzqkxhmakk4bokrlm87_-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=socc&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/oi"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=socc&t=l"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "i.yimg.jp"
+        },
+        {
+          ":path": "/images/news/module/md20120709_gsearch.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=socc&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "content.yieldmanager.edgesuite.net"
+        },
+        {
+          ":path": "/atoms/71/f8/fb/ee/71f8fbeed96e2ac38d4638d6c6e2ece4.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=socc&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "headlines.yahoo.co.jp"
+        },
+        {
+          ":path": "/hl"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=socc&t=l"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b; YJNEWSCOMMENT=d=06yLIwT4EjfCUHM_ZATPTTC.be6FwFeXux__KeWeqlDK.dHwKDQYqgJFHj9.HJlNGmTwWgk.h4h.sU8V_TDfRcrHwDjLWrrsKoxSuxiWaUP8PvEUAbJUe9xIk79LoWKr6tlDjWRkyBVLbqWqtJB_axSkadUO&v=1; YJNEWSFB=d=g52f45b4EjeJqzak676NkVYuFf6EMD66FMZIfmpIG32ywhp.ZRx6EAf7vGDLjqk7eQGKmGgvFcgo&v=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/71629/1073618/20121029/tldo4m5hcuajwtl9ebr7-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=base&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/3514/1070875/20121102/di3ufilunjw9ul9nrygs-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=base&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/oi"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=base&t=l"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "amd.c.yimg.jp"
+        },
+        {
+          ":path": "/im_siggSTQFSGS6B_ulqQXD.kRB.g---x150-y83-q90/amd/20121103-00000687-yom-000-1-thumb.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=base&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/30/1072095/20121029/_wzyz3fszhqvouc6tuav-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=base&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "content.yieldmanager.edgesuite.net"
+        },
+        {
+          ":path": "/atoms/23/03/f1/77/2303f17798f0116b59cd53fbb5f89873.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=base&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "headlines.yahoo.co.jp"
+        },
+        {
+          ":path": "/hl"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=base&t=l"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b; YJNEWSCOMMENT=d=06yLIwT4EjfCUHM_ZATPTTC.be6FwFeXux__KeWeqlDK.dHwKDQYqgJFHj9.HJlNGmTwWgk.h4h.sU8V_TDfRcrHwDjLWrrsKoxSuxiWaUP8PvEUAbJUe9xIk79LoWKr6tlDjWRkyBVLbqWqtJB_axSkadUO&v=1; YJNEWSFB=d=g52f45b4EjeJqzak676NkVYuFf6EMD66FMZIfmpIG32ywhp.ZRx6EAf7vGDLjqk7eQGKmGgvFcgo&v=1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/71629/1074517/20121029/qym5s_fuonkwfh4vw608-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=spo&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/71629/1073614/20121029/wnskbirfjfjyqqjwjnx3-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=spo&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "yjaxc.yahoo.co.jp"
+        },
+        {
+          ":path": "/oi"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "*/*"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=spo&t=l"
+        },
+        {
+          "cookie": "B=76j09a189a6h4&b=3&s=0b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/2959/1085124/20121102/71ewr2thlfq_2yiqyhjw-a.gif"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=spo&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/30/1072106/20121029/hczia9w6zzi3jskznvxo-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=spo&t=l"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":method": "GET"
+        },
+        {
+          ":scheme": "http"
+        },
+        {
+          ":authority": "ai.yimg.jp"
+        },
+        {
+          ":path": "/bdv/26008/1077726/20121029/zuabaglgdswip3wx_faw-a.jpg"
+        },
+        {
+          "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0"
+        },
+        {
+          "accept": "image/png,image/*;q=0.8,*/*;q=0.5"
+        },
+        {
+          "accept-language": "en-US,en;q=0.5"
+        },
+        {
+          "accept-encoding": "gzip, deflate"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "referer": "http://headlines.yahoo.co.jp/hl?c=spo&t=l"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_21.json b/jetty-http2/http2-hpack/src/test/resources/data/story_21.json
new file mode 100644
index 0000000..86b67a6
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_21.json
@@ -0,0 +1,15422 @@
+{
+  "context": "response",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":status": "301"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:26 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "location": "http://www.amazon.com/"
+        },
+        {
+          "content-length": "230"
+        },
+        {
+          "keep-alive": "timeout=2, max=20"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "6577"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 23 Oct 2012 17:58:47 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 19 Oct 2012 23:59:58 GMT"
+        },
+        {
+          "age": "932740"
+        },
+        {
+          "x-amz-cf-id": "whiC_hNmBgrO48K-Fv1AqlFY-Cig61exld9QXg99v4RwPo9kzfqE9Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 26 Sep 2012 22:18:37 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 25 Sep 2012 20:26:21 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "3249950"
+        },
+        {
+          "x-amz-cf-id": "LClrkUlr2-9oeIoNwP-CxxGuMmJQguT1mVNFchiptNXT2gkurFa1cw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "3774"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 30 Oct 2012 23:02:44 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 23:02:44 GMT"
+        },
+        {
+          "age": "309703"
+        },
+        {
+          "x-amz-cf-id": "0SnGIpF0HloiJDtBSskp0LPclCOdy-cBHBsP3LTUdBy-0l2hmYRW2w=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:26 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "set-cookie": "skin=noskin; path=/; domain=.amazon.com; expires=Sat, 03-Nov-2012 13:04:26 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-amz-id-1": "0HE6SZ5H5Z4Y71SA54J9"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "x-amz-id-2": "MqdgMKxu1H6fgZ4cXY/fMQyxCVupVtmdiA3mTqc2n4+baHA/4MFE3DtVsBH6B4hp"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 11 Sep 2012 13:31:51 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 12 Sep 2008 10:13:34 GMT"
+        },
+        {
+          "age": "4577556"
+        },
+        {
+          "x-amz-cf-id": "AvgiZt6QvGiJjVptinxMWssqdE82ATuSxl0D-EfQqkg6iVMBCgJkdQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "7813"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 07:00:01 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 03:41:23 GMT"
+        },
+        {
+          "age": "108266"
+        },
+        {
+          "x-amz-cf-id": "C0P4ip7yqOsc3niS_9Bd5ONzppJ1_dduEL7eXeMlCIsohE0Nad0iCw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2503"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 29 Nov 2011 16:46:38 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 02 Jun 2010 17:55:54 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "29362669"
+        },
+        {
+          "x-amz-cf-id": "T5hInOb6hUOq4NSYTVt8TjsCSGRUHafPxwGkS5jiNGzpLmixDUmWug=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "7441"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 28 Sep 2012 14:59:28 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 01:01:41 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "3103499"
+        },
+        {
+          "x-amz-cf-id": "8puqnUMeyh0E9DCrrjWoD5tD9GjqgGyr6aMyg--qLs2ztEb3QIj9HQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4127"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 27 Sep 2012 21:46:40 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 12 Sep 2012 23:45:20 GMT"
+        },
+        {
+          "age": "3165467"
+        },
+        {
+          "x-amz-cf-id": "LModLJjBuUU3HjY0zayadcTVs_lDPQK-oIK3WWYJl9ykREzfNIm9Mw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3135"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 16 Sep 2012 13:49:30 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 13 Sep 2012 04:46:34 GMT"
+        },
+        {
+          "age": "4144497"
+        },
+        {
+          "x-amz-cf-id": "6OFsf9nPu-D4_yWQy3sINzNayyg6fm625JfZVcPmqYdOicciN0i5rg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3039"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 04 Mar 2012 12:44:05 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Fri, 02 Mar 2012 18:46:04 GMT"
+        },
+        {
+          "age": "21082822"
+        },
+        {
+          "x-amz-cf-id": "r7y3D04cQyZW1pY59rhfKoWDuC9yHfp5enkf0y9a6BKaF_6PcDVg7g=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4222"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 16 Jul 2012 17:01:48 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Jun 2009 12:02:10 GMT"
+        },
+        {
+          "age": "9489759"
+        },
+        {
+          "x-amz-cf-id": "VmOehiu6mDUBpTHylminnvdcSz7Pz3bwA_dNil6iko3qIIKu4pvwQg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1711"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 28 Nov 2011 02:10:41 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 15 Nov 2007 04:25:55 GMT"
+        },
+        {
+          "age": "29501626"
+        },
+        {
+          "x-amz-cf-id": "wDhU0erCw0YoyRgAjloixwiytE5IdFT-rCT5ITwxPx5uFgGAv50Y9Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3721"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 18 Nov 2011 22:23:31 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 04 Jan 2011 18:48:20 GMT"
+        },
+        {
+          "age": "30292856"
+        },
+        {
+          "x-amz-cf-id": "YXNG-nYMiEVi0gaRGKrCIfNODPvIKOE5L-dzPeXzyYh1LuQTLjvdSA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "24369"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 07:00:00 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 03:43:42 GMT"
+        },
+        {
+          "age": "21867"
+        },
+        {
+          "x-amz-cf-id": "2asasoycqewuj5Fwn6w6mjCvOMdZQZLZhNhdl44Yyo1-AI9hQ7bFfg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1216"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 28 Jul 2012 05:05:57 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 28 Feb 2011 18:36:12 GMT"
+        },
+        {
+          "age": "8495910"
+        },
+        {
+          "x-amz-cf-id": "6h3O56dcjyQ3aM_m5a8_ir-VgVzDCmcwyIUXFSYcLFIQISyj5Q46vA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "47767"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 14:52:48 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 08 Oct 2012 22:30:55 GMT"
+        },
+        {
+          "age": "1289499"
+        },
+        {
+          "x-amz-cf-id": "sO6PqD2yEqaPpl5EvNYnGspKuhuAXsV3jf-Ya1imW1287RLowvVQTQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4613"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 27 Nov 2011 23:44:52 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 21:42:35 GMT"
+        },
+        {
+          "age": "29510375"
+        },
+        {
+          "x-amz-cf-id": "qv844DZxuLlk-LGj1-AJNpjz_LOzLR2cGsrHaLRm9jAHtj0ynP66dQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3934"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 20 Oct 2012 00:01:41 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 28 Sep 2012 13:30:37 GMT"
+        },
+        {
+          "age": "1256566"
+        },
+        {
+          "x-amz-cf-id": "JW5QyuWGDa5ik9AIEsBmnV6YrytP9At48rtnwWjKkn77rXOj8cx9WA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1344"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 00:59:30 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 18:51:08 GMT"
+        },
+        {
+          "age": "43497"
+        },
+        {
+          "x-amz-cf-id": "IKlxWmc8byHH_FJFiboqtD71dz0H-GkBay3SaG26MwMCXfLft_HgYw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 15 Aug 2012 22:51:53 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=624307871"
+        },
+        {
+          "expires": "Mon, 16 Aug 2032 07:55:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:27 GMT"
+        },
+        {
+          "content-length": "5354"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 13:25:50 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630462932"
+        },
+        {
+          "expires": "Tue, 26 Oct 2032 13:39:59 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:27 GMT"
+        },
+        {
+          "content-length": "3658"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 08:00:53 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=629882804"
+        },
+        {
+          "expires": "Tue, 19 Oct 2032 20:31:11 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:27 GMT"
+        },
+        {
+          "content-length": "7632"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "4804"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 21:53:07 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 21:10:04 GMT"
+        },
+        {
+          "age": "227481"
+        },
+        {
+          "x-amz-cf-id": "bdyVYgf7boW90khU9D9kSQ4rt8H41h_yufp79zDL_rVA8a9brz2IwA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "148"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 15 Jul 2012 13:57:33 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 01 Aug 2011 23:32:56 GMT"
+        },
+        {
+          "age": "9587215"
+        },
+        {
+          "x-amz-cf-id": "Gv6tJh4vJVFIOBxlsPYY_n-mupZYOqsq0_IXHTz6WS89qWAryOKy8g=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "356"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 28 Sep 2012 01:43:37 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 21 Jul 2011 20:40:59 GMT"
+        },
+        {
+          "age": "3151251"
+        },
+        {
+          "x-amz-cf-id": "A4eNx4xBAu8rDK_SSjVdCoYo59rIc5aBFph1neHeq97ohGyzANNfoA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "6986"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 30 Sep 2012 10:59:49 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 01:06:32 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "2945079"
+        },
+        {
+          "x-amz-cf-id": "BCz8ITjlEUNn-tAfee5IQYTwG9afocm__16MmahSICmeqky8tmv-qA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:28 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "set-cookie": "skin=noskin; path=/; domain=.amazon.com; expires=Sat, 03-Nov-2012 13:04:26 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-amz-id-1": "0HE6SZ5H5Z4Y71SA54J9"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "x-amz-id-2": "MqdgMKxu1H6fgZ4cXY/fMQyxCVupVtmdiA3mTqc2n4+baHA/4MFE3DtVsBH6B4hp"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "x-amzn-requestid": "ffd52343-25b6-11e2-ac17-5765013d7795"
+        },
+        {
+          "cache-control": "max-age=29030400, public"
+        },
+        {
+          "content-length": "1140"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "296"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:28 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "5728"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 10 Oct 2012 01:33:28 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Sat, 22 Oct 2011 00:40:50 GMT"
+        },
+        {
+          "age": "8903"
+        },
+        {
+          "x-amz-cf-id": "odznSvAIyfv7NmqZX6A2oTHE_IX5rh889vBaPKfwpg3AMo9k3ScXcA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-length": "6951"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:28 GMT"
+        },
+        {
+          "server": "Server"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "17849"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-amz-id-2": "ZgdRydvxV2Z5YPfl9vhZZTYWMOX7vl8vIt9RuMTlm258HbYgx1yMhHsXiVTR5T1w"
+        },
+        {
+          "x-amz-request-id": "135182B51618D297"
+        },
+        {
+          "date": "Wed, 18 Jul 2012 22:57:25 GMT"
+        },
+        {
+          "last-modified": "Wed, 18 Jul 2012 22:43:26 GMT"
+        },
+        {
+          "etag": "\"08b0f3794e1b5e9a927698e159741c50\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "AmazonS3"
+        },
+        {
+          "age": "50666"
+        },
+        {
+          "x-amz-cf-id": "4wcVhu3OEiWfFvYvE3GH5UYO4KY8UpnlLcJRRRqZh0Q1zELrmrAmsA=="
+        },
+        {
+          "via": "1.0 c33edbf5459a4a44749c2cb5ecdb3fca.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 02:31:02 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630422753"
+        },
+        {
+          "expires": "Tue, 26 Oct 2032 02:30:20 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:27 GMT"
+        },
+        {
+          "content-length": "54217"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1827"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 27 Sep 2012 22:12:58 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 12 Mar 2012 23:57:13 GMT"
+        },
+        {
+          "age": "3163891"
+        },
+        {
+          "x-amz-cf-id": "wU_0sJ4VJxKBN-1nsDAgg_KUmfVbpaaGyo7YelU9wE9JmGGGKQ92EQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1134"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 09 Oct 2012 18:09:17 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 12 Mar 2012 23:57:13 GMT"
+        },
+        {
+          "age": "2141712"
+        },
+        {
+          "x-amz-cf-id": "SoQLSlLwLn_jdEOyiXGwginYyKphpwUS6-dbQ3wpPqBJIRg6mOrKvQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "636"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 20 Oct 2012 16:53:05 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 02 Jun 2010 22:18:48 GMT"
+        },
+        {
+          "age": "1195884"
+        },
+        {
+          "x-amz-cf-id": "twHfjXTW5hFlDA9KoqKVBWXyksHgSNg4Vhy3mstETvyPHr3CpZq6_Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "246"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 26 Jul 2012 10:28:32 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 02 Jun 2010 22:19:30 GMT"
+        },
+        {
+          "age": "8649357"
+        },
+        {
+          "x-amz-cf-id": "GuAxInIiaQe4FRi5wBUXvlgCqbiXlqBaFsFj_nccpovWEb1sePoPdQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:29 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "set-cookie": "skin=noskin; path=/; domain=.amazon.com; expires=Sat, 03-Nov-2012 13:04:26 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-amz-id-1": "05FGR6EKSNDPZCE5TRJQ"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "x-amz-id-2": "Tjab3PJkvWGMyS25sjt7CpJ2clcWTx72Uj5PrdRHrCIku2pHj7RnTwl293ZTfYMX"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "x-amzn-requestid": "ffd52343-25b6-11e2-ac17-5765013d7795"
+        },
+        {
+          "cache-control": "max-age=29030400, public"
+        },
+        {
+          "content-length": "1140"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 04 Sep 2012 11:58:23 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=625532809"
+        },
+        {
+          "expires": "Mon, 30 Aug 2032 12:11:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:29 GMT"
+        },
+        {
+          "content-length": "656"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Mon, 01 Oct 2012 10:33:01 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=628540179"
+        },
+        {
+          "expires": "Mon, 04 Oct 2032 07:34:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:29 GMT"
+        },
+        {
+          "content-length": "2251"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 21 Sep 2012 09:55:10 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=627262883"
+        },
+        {
+          "expires": "Sun, 19 Sep 2032 12:45:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:29 GMT"
+        },
+        {
+          "content-length": "1098"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 11:48:59 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630506046"
+        },
+        {
+          "expires": "Wed, 27 Oct 2032 01:38:35 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:29 GMT"
+        },
+        {
+          "content-length": "1342"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "content-length": "1101"
+        },
+        {
+          "last-modified": "Mon, 09 Jul 2012 16:40:17 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "public, max-age=621651328"
+        },
+        {
+          "expires": "Fri, 16 Jul 2032 13:59:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2424"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 18 Oct 2012 18:56:17 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 07 Oct 2011 03:33:58 GMT"
+        },
+        {
+          "age": "1361293"
+        },
+        {
+          "x-amz-cf-id": "MRpSS6BPNijyVanqgR6mydKxGphIrKl9jTmcGgiX2DmcWgVdFwTQ2w=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3802"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 08:34:27 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 31 Jan 2012 23:51:01 GMT"
+        },
+        {
+          "age": "1312203"
+        },
+        {
+          "x-amz-cf-id": "ohsa-VbEM_mSc8EnpAEXEtU6Nk599gGxk2FtaVe9QKvloqbwSCbxNA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-length": "2"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:29 GMT"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "server": "Server"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0RMJJXGT1KVEMXX3VSJP"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "NqGNEb/SfkxLIfbOv73h5JBBcLVNGjGLK9GCNJwg5TW2rI8DxuXtaszrL5UZhTYZ"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "set-cookie": "skin=noskin; path=/; domain=.amazon.com; expires=Sat, 03-Nov-2012 13:04:26 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-amz-id-1": "12MZRY3TA9X8CDYHBV5T"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "x-amz-id-2": "mlslIMHpZawNFsGF0x1LVEqrRVG3ckOj+P3RRTLmw7Th6oLI75FjRKiMc9ln/2lK"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "x-amzn-requestid": "ffd52343-25b6-11e2-ac17-5765013d7795"
+        },
+        {
+          "cache-control": "max-age=29030400, public"
+        },
+        {
+          "content-length": "1140"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "28988"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 20 Oct 2012 08:13:25 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 23 Jul 2012 20:35:57 GMT"
+        },
+        {
+          "age": "1227065"
+        },
+        {
+          "x-amz-cf-id": "mNIt-n16LCJdi8mcEtro9XVeAtGNT2eusOQLsXk2aBoA_LGn9iuGbQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "30231"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 06:32:33 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 00:00:59 GMT"
+        },
+        {
+          "age": "196317"
+        },
+        {
+          "x-amz-cf-id": "FIDb9FCQOTap3p4J66TlrId8wIZ_I0Uw8DgL3KGyPEN5FIgqZ4BPpA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "25698"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 28 Sep 2012 10:39:32 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 19 Sep 2012 17:12:28 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "3119098"
+        },
+        {
+          "x-amz-cf-id": "hKKlixQii0vlb9a_DiDDphY3LEUoIv24q9VfC3UOtpnJV37uLWUpmw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "26311"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 16:56:42 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 15 Oct 2012 20:13:30 GMT"
+        },
+        {
+          "age": "245268"
+        },
+        {
+          "x-amz-cf-id": "lpU11IQ1j_7om8_FVILszZ1CEV-8zAg172J2ph8lsbqt3hrfJnS7eQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "34586"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 25 Oct 2012 21:34:09 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 16:33:14 GMT"
+        },
+        {
+          "age": "747021"
+        },
+        {
+          "x-amz-cf-id": "DNbatysenX2LUU6xkuO3lGcxhDVX2DG5CzqVUpLHPw-L-eSRtn7_vw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "x-amzn-requestid": "014c3370-25b7-11e2-b46a-73e2a83196ed"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "53"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0RMJJXGT1KVEMXX3VSJP"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "NqGNEb/SfkxLIfbOv73h5JBBcLVNGjGLK9GCNJwg5TW2rI8DxuXtaszrL5UZhTYZ"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "last-modified": "Tue, 21 Sep 2010 17:37:41 GMT"
+        },
+        {
+          "etag": "\"4486-7c5a6340\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "2590"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "35541"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 30 Oct 2012 07:00:01 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 24 Aug 2012 15:19:22 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "367469"
+        },
+        {
+          "x-amz-cf-id": "kpSB8Aj4s2QcAS66S2b7mB-wlCfwmdJWltC0f0sPcsiYvcEbitBpoQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "31343"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 26 Oct 2012 21:17:40 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 18:24:45 GMT"
+        },
+        {
+          "age": "661610"
+        },
+        {
+          "x-amz-cf-id": "i3CTyh6smISPVQ7LqJU5PQ5QUwHdPuZiSswgKhn1iRrMR73x5YQglg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "30919"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 16:31:27 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 23 Aug 2012 22:11:01 GMT"
+        },
+        {
+          "age": "1283583"
+        },
+        {
+          "x-amz-cf-id": "m8mt86i5hOEx1AMpRbo6qBzFxqJqTLek8zyWFYjxj2NFT6p2yRbnZw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 15:20:43 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630213450"
+        },
+        {
+          "expires": "Sat, 23 Oct 2032 16:22:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "content-length": "3978"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Sat, 02 Jun 2012 17:24:51 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=617973507"
+        },
+        {
+          "expires": "Fri, 04 Jun 2032 00:22:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "content-length": "2352"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 28 Sep 2012 07:05:33 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=628204224"
+        },
+        {
+          "expires": "Thu, 30 Sep 2032 10:14:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "content-length": "2192"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 00:31:19 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630455624"
+        },
+        {
+          "expires": "Tue, 26 Oct 2032 11:38:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "content-length": "1783"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "content-length": "3742"
+        },
+        {
+          "last-modified": "Wed, 13 Jun 2012 19:39:59 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "public, max-age=618388596"
+        },
+        {
+          "expires": "Tue, 08 Jun 2032 19:41:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "359"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 23 Jan 2012 18:47:58 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 12 Aug 2011 13:43:43 GMT"
+        },
+        {
+          "age": "20390"
+        },
+        {
+          "x-amz-cf-id": "K1hCWmx7ReBxsX55XuAkjHXE0mRsJjI50uNKE5GUBq4SdF4TzxN--A=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\", CP=\"PSAo PSDo OUR SAM OTR DSP COR\""
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/iu3?d=amazon.com&a1=&a2=0101e39f75aa93465ee8202686e3f52bc2745bcaf2fc55ecfd1eae647167a83ecbb5&old_oo=0&n=1351947870865&dcc=t"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "set-cookie": "ad-id=A7PYmy2fB0qZnFwhmisdExM|t; Domain=.amazon-adsystem.com; Expires=Thu, 01-Jan-2037 00:00:01 GMT; Path=/"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "20"
+        },
+        {
+          "content-type": "text/plain"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\", CP=\"PSAo PSDo OUR SAM OTR DSP COR\""
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/iu3?d=amazon.com&a1=&a2=0101e39f75aa93465ee8202686e3f52bc2745bcaf2fc55ecfd1eae647167a83ecbb5&old_oo=0&n=1351947870865&dcc=t"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "set-cookie": "ad-privacy=0; Domain=.amazon-adsystem.com; Expires=Thu, 01-Jan-2037 00:00:01 GMT; Path=/"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "217"
+        },
+        {
+          "content-type": "text/html;charset=ISO-8859-1"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\", CP=\"PSAo PSDo OUR SAM OTR DSP COR\""
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/iu3?d=amazon.com&a1=&a2=0101e39f75aa93465ee8202686e3f52bc2745bcaf2fc55ecfd1eae647167a83ecbb5&old_oo=0&n=1351947870865&dcc=t"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "set-cookie": "ad-privacy=0; Domain=.amazon-adsystem.com; Expires=Thu, 01-Jan-2037 00:00:01 GMT; Path=/"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "158"
+        },
+        {
+          "content-type": "text/html;charset=ISO-8859-1"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "content-type": "text/html;charset=ISO-8859-1"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "160"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-location": "http://spe.atdmt.com/images/pixel.gif"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR CUR ADM DEV TAIo PSAo PSDo OUR BUS UNI PUR COM NAV INT DEM STA PRE OTC\""
+        },
+        {
+          "set-cookie": "MUID=38CD881268FB628E3D318C1F6BFB6289; expires=Monday, 03-Nov-2014 00:00:00 GMT; path=/; domain=.atdmt.com"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "313"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-location": "http://spe.atdmt.com/images/pixel.gif"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR CUR ADM DEV TAIo PSAo PSDo OUR BUS UNI PUR COM NAV INT DEM STA PRE OTC\""
+        },
+        {
+          "set-cookie": "MUID=39C1843BD7CB679E06238036D4CB670B; expires=Monday, 03-Nov-2014 00:00:00 GMT; path=/; domain=.atdmt.com"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-length": "6951"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "server": "Server"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "304"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "17849"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-amz-id-2": "ZgdRydvxV2Z5YPfl9vhZZTYWMOX7vl8vIt9RuMTlm258HbYgx1yMhHsXiVTR5T1w"
+        },
+        {
+          "x-amz-request-id": "135182B51618D297"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "last-modified": "Wed, 18 Jul 2012 22:43:26 GMT"
+        },
+        {
+          "etag": "\"08b0f3794e1b5e9a927698e159741c50\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "AmazonS3"
+        },
+        {
+          "age": "50669"
+        },
+        {
+          "x-amz-cf-id": "UB_lB5ijt678Q2wJ3BJ-U_cVvtSnWAVfUTXcCKhh-QAhIQ9BCb3djQ=="
+        },
+        {
+          "via": "1.0 c33edbf5459a4a44749c2cb5ecdb3fca.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "x-amzn-requestid": "01a883c0-25b7-11e2-ac1e-e97d81479c85"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "53"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "set-cookie": "skin=noskin; path=/; domain=.amazon.com; expires=Sat, 03-Nov-2012 13:04:26 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-amz-id-1": "05PW0GT01QWP44EFKF0E"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "x-amz-id-2": "GCho/mJOD51Oufijqw6gqph1/1sIiACGCKJXKh2zpMaDj6u5gA1ggWuscsW3aojI"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "x-amzn-requestid": "ffd52343-25b6-11e2-ac17-5765013d7795"
+        },
+        {
+          "cache-control": "max-age=29030400, public"
+        },
+        {
+          "content-length": "1140"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "187"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-amz-id-2": "a+sM7JnK1z8XJALH7//6/PrXIyqStu8OXpFxVoaX6gaWUfZFD47Fr2QQUa/fp7AI"
+        },
+        {
+          "x-amz-request-id": "1388995ECC503151"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 17:26:53 GMT"
+        },
+        {
+          "x-amz-meta-jets3t-original-file-date-iso8601": "2011-11-07T21:33:44.000Z"
+        },
+        {
+          "x-amz-meta-md5-hash": "a20db430a00109d80184570ec2b5d38e"
+        },
+        {
+          "cache-control": "max-age=864000"
+        },
+        {
+          "last-modified": "Tue, 08 Nov 2011 18:37:11 GMT"
+        },
+        {
+          "etag": "\"a20db430a00109d80184570ec2b5d38e\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "AmazonS3"
+        },
+        {
+          "age": "157059"
+        },
+        {
+          "x-amz-cf-id": "Ls6I3zhLRw-y0K8aIUpki-XaifKyUBIlqjKRtAaQI11Yw135jA8Ahg=="
+        },
+        {
+          "via": "1.0 06b38b2ddcbb45c8e33db161a2316149.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "232"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-amz-id-2": "j62Ubwkhgqe/6HUlxy6AgFFF3a9toD+TP5OG4thryUyvAuIRkEPZznTcEkslc2vu"
+        },
+        {
+          "x-amz-request-id": "D24296DC343E3D4D"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 17:26:53 GMT"
+        },
+        {
+          "x-amz-meta-jets3t-original-file-date-iso8601": "2012-08-30T18:01:46.000Z"
+        },
+        {
+          "x-amz-meta-md5-hash": "ac923fb65b36b7e6df1b85ed9a2eeb25"
+        },
+        {
+          "cache-control": "max-age=864000"
+        },
+        {
+          "last-modified": "Thu, 30 Aug 2012 18:02:23 GMT"
+        },
+        {
+          "etag": "\"ac923fb65b36b7e6df1b85ed9a2eeb25\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "AmazonS3"
+        },
+        {
+          "age": "157059"
+        },
+        {
+          "x-amz-cf-id": "u4HxdeiPj2Z69lQ7aky8PwR3bIL1DI2LZviCrEidCeKNRXIzSU04Hw=="
+        },
+        {
+          "via": "1.0 06b38b2ddcbb45c8e33db161a2316149.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "246"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-amz-id-2": "A8pOeFTyzWm76hXU6FfLkQf4c9wZCOf1QF9R4TGkjXEInQJp6farkkg0WB0HfwcK"
+        },
+        {
+          "x-amz-request-id": "4B032A9637D718D5"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 17:26:53 GMT"
+        },
+        {
+          "x-amz-meta-jets3t-original-file-date-iso8601": "2011-11-08T18:38:37.000Z"
+        },
+        {
+          "x-amz-meta-md5-hash": "abb0183baa0ea995b47dcc0e9972095a"
+        },
+        {
+          "cache-control": "max-age=864000"
+        },
+        {
+          "last-modified": "Tue, 08 Nov 2011 18:41:02 GMT"
+        },
+        {
+          "etag": "\"abb0183baa0ea995b47dcc0e9972095a\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "AmazonS3"
+        },
+        {
+          "age": "157059"
+        },
+        {
+          "x-amz-cf-id": "o02bJWU_jSE66IwLSFs3qnpdALQRgcE53RerA0FNaohnIpayFuIBWg=="
+        },
+        {
+          "via": "1.0 06b38b2ddcbb45c8e33db161a2316149.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "227"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-amz-id-2": "9LJz7DXOJVq1v+6jQBPW+PKANy+8UBrktRUpS5ldd7n64fM5B0dvTEVk3oKOAaQj"
+        },
+        {
+          "x-amz-request-id": "7E12EE38DC5DE3F0"
+        },
+        {
+          "date": "Fri, 26 Oct 2012 20:55:51 GMT"
+        },
+        {
+          "x-amz-meta-jets3t-original-file-date-iso8601": "2012-04-11T18:59:01.000Z"
+        },
+        {
+          "x-amz-meta-md5-hash": "e45b8e1074fb65e447d6d8a91eff8a94"
+        },
+        {
+          "cache-control": "max-age=864000"
+        },
+        {
+          "last-modified": "Wed, 11 Apr 2012 18:59:43 GMT"
+        },
+        {
+          "etag": "\"e45b8e1074fb65e447d6d8a91eff8a94\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "AmazonS3"
+        },
+        {
+          "age": "662921"
+        },
+        {
+          "x-amz-cf-id": "B6N7v4ZLzrFyVRVxNchTyVO2unOzJHIK39q8SJis7c6pWrIOw4rC1Q=="
+        },
+        {
+          "via": "1.0 06b38b2ddcbb45c8e33db161a2316149.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "236"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-amz-id-2": "3TVcuu6MQ87em+GdI+9z27lbDxXU5i9H9Zz9GIbm33BZo9vPgIC/AkilBK5tF75Q"
+        },
+        {
+          "x-amz-request-id": "F105689791C8CFC6"
+        },
+        {
+          "date": "Fri, 26 Oct 2012 20:55:51 GMT"
+        },
+        {
+          "x-amz-meta-jets3t-original-file-date-iso8601": "2012-05-18T18:46:04.000Z"
+        },
+        {
+          "x-amz-meta-md5-hash": "b1178d4fb4111d1058a187cf31c95ab9"
+        },
+        {
+          "cache-control": "max-age=864000"
+        },
+        {
+          "last-modified": "Fri, 18 May 2012 18:47:11 GMT"
+        },
+        {
+          "etag": "\"b1178d4fb4111d1058a187cf31c95ab9\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "AmazonS3"
+        },
+        {
+          "age": "662921"
+        },
+        {
+          "x-amz-cf-id": "sKPhYUyawRrJWnfWsyGL2s3ckBnoapJQYfxvp1W3KVKwMiUhkEK51w=="
+        },
+        {
+          "via": "1.0 06b38b2ddcbb45c8e33db161a2316149.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "p3p": "CP=\"CUR ADM OUR NOR STA NID\""
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/ecm3?ex=openx.com&id=40a611fe-06f9-cb74-26a8-7b5edc1205e7"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "set-cookie": "i=cbdc52da-b3d4-4f79-bc70-3121d006fdfa; version=1; path=/; domain=.openx.net; max-age=63072000;"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "server": "TRP Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "CP=\"NOI CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT\""
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/ecm3?id=&ex=rubiconproject.com&status=no-user-id"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "set-cookie": "SERVERID=; Expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/"
+        },
+        {
+          "keep-alive": "timeout=5, max=200"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "text/plain; charset=UTF-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "10979"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 21 Oct 2012 02:44:45 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 06 Mar 2012 22:20:25 GMT"
+        },
+        {
+          "age": "1160386"
+        },
+        {
+          "x-amz-cf-id": "F1Bnl4JS9Kyz-O5cpuXeg0_j5GumDg2Qt1wp0zonzvxPGICjmUSeMg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1526"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 21 Oct 2012 09:00:12 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 15 Jul 2011 19:42:36 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "1137859"
+        },
+        {
+          "x-amz-cf-id": "JGLRgB6lNjaxDdggNb9JkO58_vLkHmUReZ84GjyLh10FHCjDZ1d15Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\", CP=\"PSAo PSDo OUR SAM OTR DSP COR\""
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/iu3?d=amazon.com&a1=&a2=0101e39f75aa93465ee8202686e3f52bc2745bcaf2fc55ecfd1eae647167a83ecbb5&old_oo=0&n=1351947870865&dcc=t"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "set-cookie": "ad-privacy=0; Domain=.amazon-adsystem.com; Expires=Thu, 01-Jan-2037 00:00:01 GMT; Path=/"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "57"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "57"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 20 Apr 2012 10:05:40 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=614707622"
+        },
+        {
+          "expires": "Tue, 27 Apr 2032 05:11:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:30 GMT"
+        },
+        {
+          "content-length": "1095"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "11117"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 29 Oct 2012 16:56:50 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 24 Sep 2012 20:44:19 GMT"
+        },
+        {
+          "age": "418061"
+        },
+        {
+          "x-amz-cf-id": "5o_BiUaTAD5lYQsK4IV5TzqQ5cWYNQbnt0xLwze5jS-445EERctTFg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "31461"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 17 Oct 2012 18:29:24 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 22:41:15 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "1449307"
+        },
+        {
+          "x-amz-cf-id": "tw9-4WwNBPYcd_2n5w02SSvFLzYSQr7PgoWnUBoekvj_CAvLUtzicg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 01 Sep 2009 21:16:26 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=618550558"
+        },
+        {
+          "expires": "Thu, 10 Jun 2032 16:40:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "content-length": "3215"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 08:30:59 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=613322830"
+        },
+        {
+          "expires": "Sun, 11 Apr 2032 04:31:41 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "content-length": "3542"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2045"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 16 Jul 2012 05:26:37 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 13 Jul 2011 21:50:54 GMT"
+        },
+        {
+          "age": "9531474"
+        },
+        {
+          "x-amz-cf-id": "vjhm9zt0l4ak9rTfcaqoDHHqCVyjy5BT-0EXxKy0Qu1D7GuQLeuwKQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/ecm3?ex=doubleclick.net&google_gid=CAESEDp6zTGnEVOFXTsUTIntlOo&google_cver=1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "server": "Cookie Matcher"
+        },
+        {
+          "content-length": "310"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 29 May 2009 20:56:27 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=613357164"
+        },
+        {
+          "expires": "Sun, 11 Apr 2032 14:03:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "content-length": "739"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\", CP=\"PSAo PSDo OUR SAM OTR DSP COR\""
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/iu3?d=amazon.com&a1=&a2=0101e39f75aa93465ee8202686e3f52bc2745bcaf2fc55ecfd1eae647167a83ecbb5&old_oo=0&n=1351947870865&dcc=t"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "set-cookie": "ad-privacy=0; Domain=.amazon-adsystem.com; Expires=Thu, 01-Jan-2037 00:00:01 GMT; Path=/"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "57"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "23861"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 09 Oct 2012 23:49:58 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 23:23:03 GMT"
+        },
+        {
+          "age": "2121273"
+        },
+        {
+          "x-amz-cf-id": "6igCzs5zDRpfl3uREE8U8b7UsUeKiOXlnR5OwfDF4SRDwMzu4n2Hxw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 06:26:14 GMT"
+        },
+        {
+          "last-modified": "Wed, 05 Sep 2012 21:17:19 GMT"
+        },
+        {
+          "etag": "\"19309a1-2b15-e65bd5c0\""
+        },
+        {
+          "cache-control": "max-age=172800"
+        },
+        {
+          "server": "Apache/2.2.3 (Red Hat)"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR LAW CUR ADMo DEVo TAIo PSAo PSDo IVAo IVDo HISo OTPo OUR SAMo BUS UNI COM NAV INT DEM CNT STA PRE LOC\", CP=\"NOI DSP COR LAW CUR ADMo DEVo TAIo PSAo PSDo IVAo IVDo HISo OTPo OUR SAMo BUS UNI COM NAV INT DEM CNT STA PRE LOC\""
+        },
+        {
+          "content-length": "3891"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "p3p": "policyref=\"http://tag.admeld.com/w3c/p3p.xml\", CP=\"PSAo PSDo OUR SAM OTR BUS DSP ALL COR\""
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/ecm3?id=bfd291d0-29a4-4e30-a3a2-90bfcce51d8f&ex=admeld.com"
+        },
+        {
+          "content-length": "275"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "set-cookie": "meld_sess=bfd291d0-29a4-4e30-a3a2-90bfcce51d8f;expires=Sun, 05 May 2013 03:59:31 GMT;path=/;domain=tag.admeld.com;"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:32 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\", CP=\"PSAo PSDo OUR SAM OTR DSP COR\""
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/iu3?d=amazon.com&a1=&a2=0101e39f75aa93465ee8202686e3f52bc2745bcaf2fc55ecfd1eae647167a83ecbb5&old_oo=0&n=1351947870865&dcc=t"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "set-cookie": "ad-privacy=0; Domain=.amazon-adsystem.com; Expires=Thu, 01-Jan-2037 00:00:01 GMT; Path=/"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "57"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "content-length": "9834"
+        },
+        {
+          "last-modified": "Tue, 14 Aug 2012 08:19:30 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "public, max-age=623720235"
+        },
+        {
+          "expires": "Mon, 09 Aug 2032 12:41:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 25 Feb 2009 15:49:48 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=580546798"
+        },
+        {
+          "expires": "Thu, 10 Jun 2032 16:40:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:31 GMT"
+        },
+        {
+          "content-length": "349"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-lookup": "MISS from cdn-images.amazon.com:10080"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "26111"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 20:57:01 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:57:01 GMT"
+        },
+        {
+          "age": "144451"
+        },
+        {
+          "x-amz-cf-id": "14X7FbQwII7W32KG_B6UnQzAAN04K4czIvpQXldrJky1-W_FKI0wsA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "expires": "Mon, 29 Oct 2012 20:03:22 GMT"
+        },
+        {
+          "last-modified": "Tue, 10 Jul 2012 09:55:27 GMT"
+        },
+        {
+          "etag": "\"1930a25-22c7-badbe1c0\""
+        },
+        {
+          "cache-control": "max-age=90400000, public"
+        },
+        {
+          "server": "Apache/2.2.3 (Red Hat)"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR LAW CUR ADMo DEVo TAIo PSAo PSDo IVAo IVDo HISo OTPo OUR SAMo BUS UNI COM NAV INT DEM CNT STA PRE LOC\", CP=\"NOI DSP COR LAW CUR ADMo DEVo TAIo PSAo PSDo IVAo IVDo HISo OTPo OUR SAMo BUS UNI COM NAV INT DEM CNT STA PRE LOC\""
+        },
+        {
+          "content-length": "2708"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:32 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 18 Apr 2012 10:47:18 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=623739489"
+        },
+        {
+          "expires": "Mon, 09 Aug 2032 18:02:41 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:32 GMT"
+        },
+        {
+          "content-length": "2622"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-lookup": "MISS from cdn-images.amazon.com:10080"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:33 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 20:03:15 GMT"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 20:03:15 GMT"
+        },
+        {
+          "etag": "EDAADA0BBD2E54186FB78DECDB80E1E00940A39A"
+        },
+        {
+          "cache-control": "max-age=283721,public,no-transform,must-revalidate"
+        },
+        {
+          "x-ocsp-reponder-id": "t8edcaocsp2"
+        },
+        {
+          "content-length": "471"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/ocsp-response"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:33 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 16:35:33 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 16:35:33 GMT"
+        },
+        {
+          "etag": "179CA2EEF2B7FE96BC99C52D47B1A6E9E36FF3A7"
+        },
+        {
+          "cache-control": "max-age=357659,public,no-transform,must-revalidate"
+        },
+        {
+          "x-ocsp-reponder-id": "t8edcaocsp2"
+        },
+        {
+          "content-length": "471"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/ocsp-response"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:32 GMT"
+        },
+        {
+          "server": "Apache/2.2.4 (Unix) DAV/2 mod_ssl/2.2.4 OpenSSL/0.9.7a mod_fastcgi/2.4.2"
+        },
+        {
+          "set-cookie": "KADUSERCOOKIE=A682BC39-E933-4AED-86D3-69AAE2AAD12F; domain=pubmatic.com; expires=Sun, 03-Nov-2013 13:04:32 GMT; path=/"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR LAW CUR ADMo DEVo TAIo PSAo PSDo IVAo IVDo HISo OTPo OUR SAMo BUS UNI COM NAV INT DEM CNT STA PRE LOC\""
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "text/html"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:33 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\", CP=\"PSAo PSDo OUR SAM OTR DSP COR\""
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/iu3?d=amazon.com&a1=&a2=0101e39f75aa93465ee8202686e3f52bc2745bcaf2fc55ecfd1eae647167a83ecbb5&old_oo=0&n=1351947870865&dcc=t"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "set-cookie": "ad-privacy=0; Domain=.amazon-adsystem.com; Expires=Thu, 01-Jan-2037 00:00:01 GMT; Path=/"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "57"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:33 GMT"
+        },
+        {
+          "server": "Apache/2.2.4 (Unix) DAV/2 mod_ssl/2.2.4 OpenSSL/0.9.8e-fips-rhel5 mod_fastcgi/2.4.2"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR LAW CUR ADMo DEVo TAIo PSAo PSDo IVAo IVDo HISo OTPo OUR SAMo BUS UNI COM NAV INT DEM CNT STA PRE LOC\""
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "text/html"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 22 Oct 2010 19:31:50 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=583595968"
+        },
+        {
+          "expires": "Mon, 09 Aug 2032 18:02:41 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:34 GMT"
+        },
+        {
+          "content-length": "355"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-lookup": "MISS from cdn-images.amazon.com:10080"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 16 Oct 2012 21:06:15 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630485187"
+        },
+        {
+          "expires": "Tue, 26 Oct 2032 19:51:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:34 GMT"
+        },
+        {
+          "content-length": "3398"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-lookup": "MISS from cdn-images.amazon.com:10080"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "24345"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 10 Oct 2012 00:37:19 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 23:23:04 GMT"
+        },
+        {
+          "age": "2118435"
+        },
+        {
+          "x-amz-cf-id": "tLO5krWbCYmRm9LIjXDN76fC2DJhTSiML3Wx7P5GT_QflWQzjjyLSw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Mon, 09 Apr 2012 04:08:58 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=613372173"
+        },
+        {
+          "expires": "Sun, 11 Apr 2032 18:14:07 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:34 GMT"
+        },
+        {
+          "content-length": "3972"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-lookup": "MISS from cdn-images.amazon.com:10080"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "21733"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 21 Oct 2012 01:33:35 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 08 Oct 2012 20:03:10 GMT"
+        },
+        {
+          "age": "1164660"
+        },
+        {
+          "x-amz-cf-id": "boy0DjYIiv9QiDUAB5IT03oJw0Oe-TzQq2zaLbbC8-MCS_l6Jrzf5g=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 05:55:45 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630608237"
+        },
+        {
+          "expires": "Thu, 28 Oct 2032 06:01:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:35 GMT"
+        },
+        {
+          "content-length": "17650"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-lookup": "MISS from cdn-images.amazon.com:10080"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "23996"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 24 Oct 2012 00:39:53 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 08 Oct 2012 16:18:49 GMT"
+        },
+        {
+          "age": "908682"
+        },
+        {
+          "x-amz-cf-id": "qC_RVL0YLxYoBvaZ0uqbs2VI6jEgUk34Rk19qpGdS2VsFUS3KkWYMg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "21582"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 18:47:58 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Mon, 08 Oct 2012 20:03:11 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "1275397"
+        },
+        {
+          "x-amz-cf-id": "vHqKStRXJPYsiKzS0tTwY_1XKiks6HvwJGT9UArSlfAs6OnCYHQ7lA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "22139"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 18 Oct 2012 09:05:16 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 08 Oct 2012 20:03:12 GMT"
+        },
+        {
+          "age": "1396759"
+        },
+        {
+          "x-amz-cf-id": "3iqSry0i3VhqyuBUo-iRW3UgESSNBQ3lv4YeFtxPi_-Acv46jt6IAw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "849"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 27 Sep 2012 07:28:18 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Sat, 16 Jul 2011 00:30:22 GMT"
+        },
+        {
+          "age": "3216977"
+        },
+        {
+          "x-amz-cf-id": "jh36SMSXaXj_TeRR9z4Vs8-cbbEoHRGJkz-zWwqnTDkn3mU7WYIILw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 17 Jan 2012 01:18:38 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=613357224"
+        },
+        {
+          "expires": "Sun, 11 Apr 2032 14:04:59 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:35 GMT"
+        },
+        {
+          "content-length": "2305"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-lookup": "MISS from cdn-images.amazon.com:10080"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "2645"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 16 Oct 2012 14:50:28 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 22 Oct 2010 23:53:27 GMT"
+        },
+        {
+          "age": "1548848"
+        },
+        {
+          "x-amz-cf-id": "RYwtx5a09etraZHlO8Ut-TcazsQhaIMlHsC_JTzCEXAYeXfmbh0AWA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 22 May 2012 10:05:12 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=623739489"
+        },
+        {
+          "expires": "Mon, 09 Aug 2032 18:02:44 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:35 GMT"
+        },
+        {
+          "content-length": "656"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "590"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 19 Jul 2012 07:06:19 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 06 Oct 2010 20:47:18 GMT"
+        },
+        {
+          "age": "9266297"
+        },
+        {
+          "x-amz-cf-id": "jMk_WLA7tRGazvX3ieenZ8uPLkOB1NVjnlmuRGPRPfUGpdfopJWMug=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "618"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 17:16:59 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 14 Sep 2012 15:15:31 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "1280857"
+        },
+        {
+          "x-amz-cf-id": "gsm-rW_jbFRe2Ne92Oddd13REiBnDzo9k53P59LW-6WZhjFKi2gpcg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "6409"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 06 Oct 2012 05:21:58 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 25 Apr 2012 05:07:39 GMT"
+        },
+        {
+          "age": "2446958"
+        },
+        {
+          "x-amz-cf-id": "RenD1oaMDB7WDA0nJBiT78PBjnjUmYU-NT3-eSlLX03q5vM16mQthg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "13915"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 30 Oct 2012 05:57:41 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 19:21:14 GMT"
+        },
+        {
+          "age": "371215"
+        },
+        {
+          "x-amz-cf-id": "y-qky_uSUebpzoYKGYMa1lzGeUux4fgnmRhwkgvrXQn78lG5AhfFmA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 22 May 2012 06:41:37 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=621353717"
+        },
+        {
+          "expires": "Tue, 13 Jul 2032 03:19:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:36 GMT"
+        },
+        {
+          "content-length": "1580"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4456"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 05:26:53 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 05:26:32 GMT"
+        },
+        {
+          "age": "286663"
+        },
+        {
+          "x-amz-cf-id": "QAWFAFoQqqdK6bsebuU01EfGVCwSV_HjtTaLA-hlTAAOA6d4Wsz4wg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2951"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 18:38:41 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 05:26:34 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "66355"
+        },
+        {
+          "x-amz-cf-id": "Aubb5MuBO28D2I8jIDVYWmI2-8g4Tt0cpl6beMcpVSNTX5gZMv4wgQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4666"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 17:58:34 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 17:58:34 GMT"
+        },
+        {
+          "age": "155162"
+        },
+        {
+          "x-amz-cf-id": "OBft3yppuSTyI5o52ZSa5lfbjZWp3ShzF8-dM45Pf0qkJIYh24nQtQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5192"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 16:39:49 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 25 Jul 2012 11:12:38 GMT"
+        },
+        {
+          "age": "69923"
+        },
+        {
+          "x-amz-cf-id": "Y6S4ynuqdWWDNdGNvXNtU6fnNBBOoJLWVPdhgnyEnTTMDvI_4XuMzQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5529"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 03:41:12 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 03:15:26 GMT"
+        },
+        {
+          "age": "293004"
+        },
+        {
+          "x-amz-cf-id": "ShMwoXym8XNH4S1fFX15TBcjBioYagCJaBprDbqaOtJjOiNkWdeg7g=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3629"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 23:15:49 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Sat, 23 Jun 2012 21:37:13 GMT"
+        },
+        {
+          "age": "1259327"
+        },
+        {
+          "x-amz-cf-id": "HlyMS446sa886uO7DjJyUavWa-mHH0XLcyzUrb49K-G4mkqMbSOsIA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3509"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 25 May 2012 23:03:05 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 24 May 2012 18:35:18 GMT"
+        },
+        {
+          "age": "13960891"
+        },
+        {
+          "x-amz-cf-id": "-IZ-Kzdl4oTM776x_-SdI-Sf-rdBE0xeKYvmj2otONB4guu3l0lNsA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4879"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 04:39:14 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 03:14:55 GMT"
+        },
+        {
+          "age": "289522"
+        },
+        {
+          "x-amz-cf-id": "UnkzEKXd4DwGvLUL-8-afWzVHMcB4T-tYr9-HLWFRsfbiIvYylwIxA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2809"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 17:24:44 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Tue, 11 Sep 2012 11:41:25 GMT"
+        },
+        {
+          "age": "70792"
+        },
+        {
+          "x-amz-cf-id": "ApQSczR7vg5QCdxHZR6hBm1pbl-hGvpxOz6MPp9eCEoBx99I8-atXg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1979"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 18:13:18 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 18:13:18 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "240678"
+        },
+        {
+          "x-amz-cf-id": "WvvSWSGbGBRHrBf0RFjJ3qJ_o4y9yJuT8SeGDq1dpYvwTANrwyr-Ow=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6942"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 17:58:34 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 17:58:34 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "155162"
+        },
+        {
+          "x-amz-cf-id": "XA_j3mVwjsJMTgHec3EMMTJ547z0XmIPH8hlMt5tuM1OEe2Kuo_A8g=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1854"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 03:41:12 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 03:15:25 GMT"
+        },
+        {
+          "age": "293004"
+        },
+        {
+          "x-amz-cf-id": "rPNUJ_0Y4uE0WKLOFZddVt9Pt-15ixc8M9WYFxZcxUq204PEfNtVuw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3661"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 16:20:50 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 11 Sep 2012 21:41:22 GMT"
+        },
+        {
+          "age": "161026"
+        },
+        {
+          "x-amz-cf-id": "10WYvTpJNEH0MAP3wne0pXXNE8ZnAI4eVJA3EDPrJ6TF3rv0YJJK_A=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2729"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 23:43:43 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 01 Jun 2011 05:24:35 GMT"
+        },
+        {
+          "age": "70759"
+        },
+        {
+          "x-amz-cf-id": "kb2nMqvt29Qj3LdcwS6ww1KobMLzUlJWjzEzfJ49hrIYV9AchOannQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3916"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 17:24:44 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 07 Sep 2012 09:03:37 GMT"
+        },
+        {
+          "age": "70792"
+        },
+        {
+          "x-amz-cf-id": "JJZDbMR4RLNK_HVvTbUnNaDilC24pIBmtY7BRd5JkQybHgLPJLr2Bw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2103"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 18:14:15 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 18:14:15 GMT"
+        },
+        {
+          "age": "240621"
+        },
+        {
+          "x-amz-cf-id": "AG9h0_9ASRuhaLjhB4fsbvE6ktpeabI5v9S-fSJ_K1SOdr8skxsQ8A=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2120"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 18:29:52 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 18:15:06 GMT"
+        },
+        {
+          "age": "239684"
+        },
+        {
+          "x-amz-cf-id": "3V9zS2t71NKOHjc-zbQieHw8D15BF88HM5DVQY9j5z7qaxE8JDHbyA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2065"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 17:25:28 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 17:48:33 GMT"
+        },
+        {
+          "age": "70748"
+        },
+        {
+          "x-amz-cf-id": "Nkglfv_cx2pdr2EZJF0D475iF5L5LK6Fxcq0dUDPSxCVHftCYtgHng=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2131"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 18:14:15 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 18:14:15 GMT"
+        },
+        {
+          "age": "240621"
+        },
+        {
+          "x-amz-cf-id": "Xp7P1ZCF0IjKGtBRruIMsC780cNrxhNJ5960ejB13uXf3su3MHM7PQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2962"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 07:00:00 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 05:49:53 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "194676"
+        },
+        {
+          "x-amz-cf-id": "peTzFJr516_fOBQY5OC91FWEamWDNAqIh8_l1VUiaqrMRgGupou75w=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4626"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 07:00:02 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 05:49:52 GMT"
+        },
+        {
+          "age": "194674"
+        },
+        {
+          "x-amz-cf-id": "VMe4jZb7miZ0G-rv3uBPNmdTpYUIYgkj8UaXTHlFZ8sd2SONziLchg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3581"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 17:29:39 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:29:39 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "243297"
+        },
+        {
+          "x-amz-cf-id": "DQkavQgzFQLKNbG-YUMaAIW1JOadibOwvA_xdhashOIKLfpQuAn2gA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5534"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 05:49:57 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 05:49:53 GMT"
+        },
+        {
+          "age": "285279"
+        },
+        {
+          "x-amz-cf-id": "Quota3IS8N_cDOH-5AgNf6IoirJJCjYpEsTfXJKx-QYO8uoPPsgF5Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3336"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 07:03:12 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 07:03:12 GMT"
+        },
+        {
+          "age": "280884"
+        },
+        {
+          "x-amz-cf-id": "nKFIlcQrEACy_wNDwvMk7o4wDqwiqg22gTbbx1GgRtKIfeQCY4rAUg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3660"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 07:05:38 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 07:04:35 GMT"
+        },
+        {
+          "age": "280738"
+        },
+        {
+          "x-amz-cf-id": "5flYXqijU45smYJrbpa6DyFQK79BKOC75IH_AD_gBLabCf2_f6JJ4w=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3631"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 07:03:29 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 07:03:29 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "280867"
+        },
+        {
+          "x-amz-cf-id": "9zt7Ykby0o5nJUdXmLURwVG97OcVN7UDmlxqqBAr6-kpce1NUZ3vHQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4831"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 07:02:44 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 07:02:44 GMT"
+        },
+        {
+          "age": "280912"
+        },
+        {
+          "x-amz-cf-id": "arnnoA4osVhZ9WWxe1rw1kH36LVZpueZhg5Ij7p06zynPPuP_PbhAg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "71"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 21 Oct 2012 07:10:55 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 01:53:07 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "1144421"
+        },
+        {
+          "x-amz-cf-id": "otV5h9P0a9-ozjyL5asNyiDOMvE4cnJFvEUCY1qCIkCO1BlYJsF-fQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "4528"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 26 Oct 2012 23:30:10 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Sat, 13 Oct 2012 00:03:47 GMT"
+        },
+        {
+          "age": "653666"
+        },
+        {
+          "x-amz-cf-id": "zat2zuNOe5PZPMKX9J5IIEc2EvGHW2wIWsGnPPG6NWdX7cWtkKBL3Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "4305"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 19:34:27 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 18:07:44 GMT"
+        },
+        {
+          "age": "63009"
+        },
+        {
+          "x-amz-cf-id": "P_0GsZlh-uhXRNgmMVIxDRrsh8CWCBHEX0sNhI9WcxKsR6VpK8qf1A=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "8307"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 07:00:07 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 19:02:26 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "194669"
+        },
+        {
+          "x-amz-cf-id": "_1G9P5_LnBwk_k5A76Q4cs4aOE-c9Y2U6KPOYakVoWIblaFat2Quuw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "3817"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 21:23:14 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 18 Oct 2012 22:25:34 GMT"
+        },
+        {
+          "age": "1006882"
+        },
+        {
+          "x-amz-cf-id": "b-rYDPAafNePCeY3rEXSUu_SHu_vhZoVf-W-90RHYbQi7NMrF7IuVw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "14262"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 26 Oct 2012 19:54:19 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 26 Oct 2012 19:43:52 GMT"
+        },
+        {
+          "age": "666617"
+        },
+        {
+          "x-amz-cf-id": "DYBg6QCNjA9V6sSGrhw4w4QT--gzcIbkjjJZKjphipyO-cYwRK1p8w=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:36 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0D7SZ3NDXXQ10K0Y1P2W"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "Yhw+pyNdxXVfOz+enqEPz5i4xubYpPznUk/Z7jiBcq/rnwK5Kwv0df5Ff+b2ROcL"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "last-modified": "Tue, 21 Sep 2010 17:37:41 GMT"
+        },
+        {
+          "etag": "\"4486-7c5a6340\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "2590"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "-1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "5639"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 23 Oct 2012 16:30:50 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 16:30:51 GMT"
+        },
+        {
+          "age": "938026"
+        },
+        {
+          "x-amz-cf-id": "O6Rt-cRJLPLokVndpOyGdTuWoLI6bPqiRt_TrdmIiYayIHUPbzY__A=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "14998"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 30 Oct 2012 20:28:09 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 20:28:09 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "318987"
+        },
+        {
+          "x-amz-cf-id": "LmtQ4CHgGq-tu05FlE0VnrOXiq0ALsXRzmG89ZFrPGFrzAJOWjQADw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "5391"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 30 Oct 2012 23:27:41 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 19 Oct 2012 17:44:45 GMT"
+        },
+        {
+          "age": "308215"
+        },
+        {
+          "x-amz-cf-id": "B9874IgNcW6Dl_iw2J-uxdH_JRYl-xRAXmd-Fx2sDQjm3zOmOAGS6A=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "14682"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 00:57:21 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 21:44:15 GMT"
+        },
+        {
+          "age": "216435"
+        },
+        {
+          "x-amz-cf-id": "JNivNWPfMlDwvI1crpnpaAtSZ9ynv0-1kJNOR3Kmfy-pFt3R00dHPQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "14468"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 07:00:12 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 03:52:02 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "194664"
+        },
+        {
+          "x-amz-cf-id": "u_bSf4kdjci5AymBMUSnaNCb7yBkMN4vlmaw6b2yHQaKkeC6bOoqXQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "17329"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 07:00:57 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 20 Apr 2012 19:39:30 GMT"
+        },
+        {
+          "age": "194619"
+        },
+        {
+          "x-amz-cf-id": "8NlR_O7HCbbYd6sWYXsaGIxoNNX3kno3ZaIkqqgmHYk3r7P0msD2hA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "5249"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 11 Oct 2012 22:47:04 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 13 Sep 2012 19:26:45 GMT"
+        },
+        {
+          "age": "1952252"
+        },
+        {
+          "x-amz-cf-id": "P3861d5dz7qGT13WJVUssV0-8x_VFQt4TtyhgjHZkDhDFHeoBpixcA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 25 Jun 2010 18:22:49 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=623879811"
+        },
+        {
+          "expires": "Wed, 11 Aug 2032 09:01:27 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:36 GMT"
+        },
+        {
+          "content-length": "2903"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 28 Sep 2012 00:00:56 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630492875"
+        },
+        {
+          "expires": "Tue, 26 Oct 2032 21:59:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:37 GMT"
+        },
+        {
+          "content-length": "1467"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 03:41:46 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630506567"
+        },
+        {
+          "expires": "Wed, 27 Oct 2032 01:47:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:37 GMT"
+        },
+        {
+          "content-length": "4150"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 22 May 2012 06:41:37 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=621353716"
+        },
+        {
+          "expires": "Tue, 13 Jul 2032 03:19:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:37 GMT"
+        },
+        {
+          "content-length": "1580"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 17 Jun 2009 18:26:49 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=613395574"
+        },
+        {
+          "expires": "Mon, 12 Apr 2032 00:44:11 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:37 GMT"
+        },
+        {
+          "content-length": "856"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 02:32:26 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630557957"
+        },
+        {
+          "expires": "Wed, 27 Oct 2032 16:03:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:36 GMT"
+        },
+        {
+          "content-length": "74964"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "3564"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 21 Oct 2012 19:37:21 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 12 Sep 2012 12:02:28 GMT"
+        },
+        {
+          "age": "1099637"
+        },
+        {
+          "x-amz-cf-id": "UnPWM9TK0CYPiYCFO7JpmgG2mEPdetqHfd_sfHtz7tBC7MdT1QthvQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "4267"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 30 Oct 2012 04:55:12 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 19:55:09 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "374966"
+        },
+        {
+          "x-amz-cf-id": "cjoJQzghJEJQidTuBdcGV7vefvG_4fs26RZ_XZHGp3J47Hsqk_mYqA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 05:23:58 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630506503"
+        },
+        {
+          "expires": "Wed, 27 Oct 2032 01:46:20 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:37 GMT"
+        },
+        {
+          "content-length": "115718"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1104"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 19:46:04 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 02 Jun 2010 22:12:17 GMT"
+        },
+        {
+          "age": "1271916"
+        },
+        {
+          "x-amz-cf-id": "CWh3NmGhidrPUirYTJeaMy1q9wfohhNrJcJHsnvLdnXf-hfmvNt_Eg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "6577"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 23 Oct 2012 17:59:58 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 17:58:26 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "2532"
+        },
+        {
+          "x-amz-cf-id": "h2X5ptCOi7LbCmEDN_-FkzuUX3O9fZGO3nRZaYiuaVmjsZJVea0KlA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "469"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 21 May 2012 18:57:43 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 17 May 2012 16:40:47 GMT"
+        },
+        {
+          "age": "76922"
+        },
+        {
+          "x-amz-cf-id": "cTNh37gW3aRYzh0_ymNAgRrpHgiKNyjCHWwWepii3kfSm2mifkCOuQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1628"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 17 May 2012 00:31:56 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 15 May 2012 05:09:34 GMT"
+        },
+        {
+          "age": "35944"
+        },
+        {
+          "x-amz-cf-id": "Lg2DdGTzo_5b8Nxk_htSqW7FB2nLWjG6cKbaOt8zmZY66pVw8K4fCA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "356"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 16 May 2012 19:42:46 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 15 May 2012 05:09:34 GMT"
+        },
+        {
+          "age": "3065596"
+        },
+        {
+          "x-amz-cf-id": "1OH_QkiX-oKt7sV0toMi-aqqotKtLvRU2cjdTLewhf5rcVlqqk1ODg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Miss from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:40 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0D7SZ3NDXXQ10K0Y1P2W"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "Yhw+pyNdxXVfOz+enqEPz5i4xubYpPznUk/Z7jiBcq/rnwK5Kwv0df5Ff+b2ROcL"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "last-modified": "Tue, 21 Sep 2010 17:37:41 GMT"
+        },
+        {
+          "etag": "\"4486-7c5a6340\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "2590"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "x-amzn-requestid": "070e59c2-25b7-11e2-9647-1185f0fe0569"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:40 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0D7SZ3NDXXQ10K0Y1P2W"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "Yhw+pyNdxXVfOz+enqEPz5i4xubYpPznUk/Z7jiBcq/rnwK5Kwv0df5Ff+b2ROcL"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "last-modified": "Tue, 21 Sep 2010 17:37:41 GMT"
+        },
+        {
+          "etag": "\"4486-7c5a6340\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "2590"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "x-amzn-requestid": "0727fc26-25b7-11e2-bb20-cf84a5272b25"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4885"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 07:00:03 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 07 Aug 2012 04:35:28 GMT"
+        },
+        {
+          "age": "21877"
+        },
+        {
+          "x-amz-cf-id": "IporfETtFCxA5v41bAp-pDjDCP4cWlKwkoaOGvvvrxS1Mw1yvUBlGQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1543"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 11 Sep 2012 13:00:00 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Jul 2010 18:12:37 GMT"
+        },
+        {
+          "age": "4579480"
+        },
+        {
+          "x-amz-cf-id": "-6t8SJvNBh6dZt3rUiRrjIVqnnVJiFbFC-QSFxcqJYuBXrzKAWWdoQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "592"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 20:03:59 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 01:37:57 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "1011641"
+        },
+        {
+          "x-amz-cf-id": "8vWzDFif0eREdPSdflEYZlgYAymCWdiDYl8Kv7KC_Fl0ALuKJG_4ag=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1646"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 16 Feb 2012 06:11:58 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 13 Oct 2010 20:47:21 GMT"
+        },
+        {
+          "age": "22575162"
+        },
+        {
+          "x-amz-cf-id": "3RdIhQfLO9XOQFa49YXot07-NIDImEld2xoGH4X9880KTfwAGihvfQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 22:37:51 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 22:37:51 GMT"
+        },
+        {
+          "content-type": "application/ocsp-response"
+        },
+        {
+          "content-transfer-encoding": "binary"
+        },
+        {
+          "content-length": "1596"
+        },
+        {
+          "cache-control": "max-age=379990, public, no-transform, must-revalidate"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:41 GMT"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 20:27:41 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 20:27:41 GMT"
+        },
+        {
+          "content-type": "application/ocsp-response"
+        },
+        {
+          "content-transfer-encoding": "binary"
+        },
+        {
+          "content-length": "1596"
+        },
+        {
+          "cache-control": "max-age=372180, public, no-transform, must-revalidate"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:41 GMT"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:42 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0ZHTZBAADKEZ1K4EGBW6"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "yJRRI8wh/xPuc4C3nBZz5KNXS1OE9OJu63P/XksiIWL9W09cknSsgC8WWr29GiDY"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "last-modified": "Tue, 21 Sep 2010 17:37:41 GMT"
+        },
+        {
+          "etag": "\"4486-7c5a6340\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "2590"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "x-amzn-requestid": "0727fc26-25b7-11e2-bb20-cf84a5272b25"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:42 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "1ZH0W43SZ47AMV593QDH"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "wGMRjKh1Pt/o44qHjshIvp7ZIPt2LK8Cze7/o3+/lfgxPUcgTEKCAZBzlDIoLoet"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "452"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:42 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "480"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:42 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 18:39:58 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 22:20:44 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 22:20:44 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "43420"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "53038"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "304"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1543"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:42 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 12 Aug 2011 13:43:43 GMT"
+        },
+        {
+          "age": "20402"
+        },
+        {
+          "x-amz-cf-id": "6gJoa6bOvFtigUntTCjVHju-EqLbWnHKw6eJPDdiGK6ocghu7-S_cg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 24 May 2012 09:43:11 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=621326683"
+        },
+        {
+          "expires": "Mon, 12 Jul 2032 19:49:25 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:42 GMT"
+        },
+        {
+          "content-length": "4823"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:47 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0DHCSP7QGSTG72H1QPRB"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "iwlAGigQepIxbg6chfZtqXoGGw6vBTgxU/ol+ayO5+ttKg7WcjWBSrPG+7aY5Eey"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:48 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "06NWT8YAYSXDSXHFEBX3"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "+dgTelR5Pvj5ApOpupZ5c4EXyGBnWsp/CtTohBqWXrKuiSIBT+ZSU/92HDHIoBxS"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2393"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 30 Sep 2012 01:55:28 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 15 Dec 2009 04:00:19 GMT"
+        },
+        {
+          "age": "2977760"
+        },
+        {
+          "x-amz-cf-id": "iMEciUEJFtmANuUhvSWuNQKwQ56xFPVzicWdjaUIS7KD_SS41vr3pQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "599"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 28 Sep 2012 14:12:09 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 01:32:16 GMT"
+        },
+        {
+          "age": "3106359"
+        },
+        {
+          "x-amz-cf-id": "tN10_L-OYWE-jbnsbpustU_nkePz1Ht2dF12FZfdzo8SDh6xAzABhw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3457"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 30 Oct 2012 16:44:30 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 06 Aug 2012 02:14:57 GMT"
+        },
+        {
+          "age": "332418"
+        },
+        {
+          "x-amz-cf-id": "nAZvrqCa80oBwwxAEUWbmmOUITdcLIDdCpFL12zOCAKgSf4n0wfGcQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3174"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 08 Mar 2012 01:24:50 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 15 Oct 2010 14:25:35 GMT"
+        },
+        {
+          "age": "20777998"
+        },
+        {
+          "x-amz-cf-id": "o4ZBTBwVKGrCOxAmevzGBdf3GXvw24E_Ibo53uEwUJbXc2EdAiOMyQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "601"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 30 Sep 2012 13:45:07 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 01:33:44 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "2935181"
+        },
+        {
+          "x-amz-cf-id": "_ELm77X7wvQxQ8ccnoUS-_woGWJlpaS6DF6N2WkCaQSwNycPUCFD4A=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3446"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 05 Jan 2012 14:17:10 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Thu, 10 Feb 2011 01:27:46 GMT"
+        },
+        {
+          "age": "26174858"
+        },
+        {
+          "x-amz-cf-id": "-Kkrw4riucQdic2OO_FiGGTwkrKyhvjx0Mcpi0Tz7UmWTD6LAmwHeg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3793"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 26 Oct 2012 19:06:32 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 07 Aug 2012 03:08:01 GMT"
+        },
+        {
+          "age": "669496"
+        },
+        {
+          "x-amz-cf-id": "X6dyQ-kDIuminUqGxtG-vyD7eZ70sWXg-ltBtWE0KkdI8OEM-Mpleg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "609"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 25 Sep 2012 16:01:58 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 15:54:14 GMT"
+        },
+        {
+          "age": "3358970"
+        },
+        {
+          "x-amz-cf-id": "b_pAd8eX4r8HYc3jV3dhKukKkfwIrYz-zWqHjipfMmhJSE_781h1oQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3653"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 01 Feb 2012 02:46:14 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:13:29 GMT"
+        },
+        {
+          "age": "23883514"
+        },
+        {
+          "x-amz-cf-id": "bjtvmLGP6K92dIdVA6duj28yhxkrL38nJMkNCptOUGcR-vblR3Dgog=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3268"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 23 Oct 2012 16:12:38 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:13:47 GMT"
+        },
+        {
+          "age": "939130"
+        },
+        {
+          "x-amz-cf-id": "NjXCi6TD-dhpmdMViBrtzqn_DEt71fFljKydDm_2OCDAPJEMAg5xnA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2974"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 30 Oct 2012 09:47:16 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 03 Apr 2012 10:20:17 GMT"
+        },
+        {
+          "age": "357452"
+        },
+        {
+          "x-amz-cf-id": "Z_49skoUilBV6g2nhKUJZO1CTvzkPUHD_SDUxrSh1etd8GS3FknVlQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3246"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 26 Oct 2012 04:05:23 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Tue, 02 Feb 2010 23:26:50 GMT"
+        },
+        {
+          "age": "723565"
+        },
+        {
+          "x-amz-cf-id": "0SbSlmo1oQR1EZaPujBcrkn2rp6-JwnKeWPjRJuZmV1Z6bYwy17-7Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3677"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 23:48:37 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:14:18 GMT"
+        },
+        {
+          "age": "998171"
+        },
+        {
+          "x-amz-cf-id": "kIXZdAY5Fnpheu46XC3kSBlJD9BzYrmLWTcGFXMEBT4VLXyNpe1SPw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2600"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 14 Dec 2011 20:22:55 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 28 Sep 2011 08:02:37 GMT"
+        },
+        {
+          "age": "28053713"
+        },
+        {
+          "x-amz-cf-id": "jdbN9e43yR0jKrrOZUnGZIUGi2GCJHSK3n2VKaDm3XT7Xy-Rj8OqNQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1489"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 18 Jul 2012 14:38:05 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 04 Jun 2010 01:32:52 GMT"
+        },
+        {
+          "age": "9325603"
+        },
+        {
+          "x-amz-cf-id": "X3VwQpWnMPHQC7MJRw6T-DaBIBalsbD0mGV4Ng9yHU5gBejjAez0dA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:48 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "1C1W41NW5TYBHBJNXB7G"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "AatuyevJiRUtb6/2d9LbJJ3EZwL4Qi5oAN43fcnZTs+cBfpfR5xhWX4o6c4AFjko"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:48 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "101M70TJ0XKDRA31P9ZW"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "8Q84WjUy4qxnCmekXyK0cOh2MgfmCnF2jlIdsknvZNKQIyUhsXloJp0QYIhPIFFw"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:49 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0R7WZ6VQ04KDQ1RKBSDK"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "iCw5Tdn11Ug6Qn1eOt4uOA+JEPcRxl56zUcXJAWRQ7+nE2ZJ4m5XU7DWbrWSX6Jj"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4760"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 09:19:33 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 27 Jul 2012 14:32:32 GMT"
+        },
+        {
+          "age": "1309517"
+        },
+        {
+          "x-amz-cf-id": "wn_k8I4g86z9xU39JO_mi8Znx5qLGm-YEKtqp9bzQUcLva6y9aPWWg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3348"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 14 Sep 2012 14:36:02 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 20 Jul 2012 14:38:01 GMT"
+        },
+        {
+          "age": "4314528"
+        },
+        {
+          "x-amz-cf-id": "FEA_NXcSb4YgiTvDGcoQ8YzVOim-T7ZoGwBNe_-tBm3HybITjhj1bw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "587"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 20 Oct 2012 15:34:28 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 15:54:16 GMT"
+        },
+        {
+          "age": "1200622"
+        },
+        {
+          "x-amz-cf-id": "vmJhsk3IfjzGGQAAi64eB8PuL0vI87SwHahTEYuBFlmrsmi_dxZ-IA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5691"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 09 Oct 2012 04:35:25 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Thu, 13 Sep 2012 17:48:15 GMT"
+        },
+        {
+          "age": "2190565"
+        },
+        {
+          "x-amz-cf-id": "UiucrnKNxdras37pId8z-tCHGNPWFMZJXLOIxPAsl_08EFRty9FxZA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4764"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 16 Oct 2012 16:34:25 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 04 Oct 2012 02:17:34 GMT"
+        },
+        {
+          "age": "1542625"
+        },
+        {
+          "x-amz-cf-id": "quvhesbVUpq0D4qHZPiMZU_LBC4xAEbnNL5tNfJoazAENn82wpjOFA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "588"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 28 Sep 2012 12:35:39 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 01:41:22 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "3112151"
+        },
+        {
+          "x-amz-cf-id": "RNt3gJPkHWBNRTsYRS0r-qEJUzHeKw0wd8LRUaKmPjRoB_txmvKk0g=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3687"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 25 Jul 2012 15:43:46 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 13 Jul 2012 14:50:09 GMT"
+        },
+        {
+          "age": "8716864"
+        },
+        {
+          "x-amz-cf-id": "P_VlWy_DI7Q9lOv31mLocJxGBGCO3x_Flg_jDhAwOvSOlHxhfkj4hA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5547"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 21 Mar 2012 20:01:12 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 21 Mar 2012 14:44:09 GMT"
+        },
+        {
+          "age": "19587818"
+        },
+        {
+          "x-amz-cf-id": "gVRXsClGSK87EC1y6EX-0j9CO-BL95NF-urXdrKWurV1eDIRau04Cw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4020"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 09:18:15 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Sat, 13 Oct 2012 21:42:50 GMT"
+        },
+        {
+          "age": "1309595"
+        },
+        {
+          "x-amz-cf-id": "mvpI8qWvGHh__TojWYI7VYmcaawpJ27bnnPJ08ozq7UlLXJiYycA4g=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4769"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 08 Oct 2012 21:18:35 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 31 Jul 2012 08:49:37 GMT"
+        },
+        {
+          "age": "2216775"
+        },
+        {
+          "x-amz-cf-id": "d8GIZ1IcSWZMBXJ8HsZ_vts4ysREuGFsNrOBA_iB5au7tUOZqueqQQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1759"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 27 Sep 2012 01:27:26 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 12 Jan 2009 18:19:36 GMT"
+        },
+        {
+          "age": "3238644"
+        },
+        {
+          "x-amz-cf-id": "bTf74h2ZtQt_I09t6RmrbYg-97o_HtttusNHsh-MGb8gUA51AY7Twg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "17741"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 12 Oct 2012 14:19:15 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Wed, 19 May 2010 09:51:41 GMT"
+        },
+        {
+          "age": "1896338"
+        },
+        {
+          "x-amz-cf-id": "UYx91fWWjcOHKIO2wY26UNYf5EQYYW4g5IWOLayy-_r3LBCjQQsV6w=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Mon, 26 Mar 2012 20:19:08 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630656332"
+        },
+        {
+          "expires": "Thu, 28 Oct 2032 19:23:45 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:53 GMT"
+        },
+        {
+          "content-length": "2126"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Tue, 17 Jul 2012 05:47:45 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=627946664"
+        },
+        {
+          "expires": "Mon, 27 Sep 2032 10:42:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:53 GMT"
+        },
+        {
+          "content-length": "1518"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 04 Nov 2009 06:54:44 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=613395773"
+        },
+        {
+          "expires": "Mon, 12 Apr 2032 00:47:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:53 GMT"
+        },
+        {
+          "content-length": "2288"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 21 Sep 2012 07:31:02 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=626993061"
+        },
+        {
+          "expires": "Thu, 16 Sep 2032 09:49:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:53 GMT"
+        },
+        {
+          "content-length": "2752"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 04:13:23 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630408896"
+        },
+        {
+          "expires": "Mon, 25 Oct 2032 22:39:49 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:53 GMT"
+        },
+        {
+          "content-length": "18249"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "796"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 12 Oct 2012 10:45:19 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Wed, 19 May 2010 09:51:41 GMT"
+        },
+        {
+          "age": "1909174"
+        },
+        {
+          "x-amz-cf-id": "9MHc1JZ7kR7Xm1k_06GjXXBjMfsbYsbpxH549UlaToDw3PtOlOpJng=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:52 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "05Y7M552RGFYHV8MY341"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "oGWKRn1W+jjcnVaAmsQPuDLm0eqlACpITrO3bAh2oWT2VrepfzlHFl5s2S6e8ah5"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-sap-pg": "photo_display_on_website"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "expires": "-1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2358"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 21 Nov 2011 14:41:51 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:13:29 GMT"
+        },
+        {
+          "age": "30061382"
+        },
+        {
+          "x-amz-cf-id": "rRtoTrePiEQnYeC12h_GTXYCVfIghwtr3bSzq5YQmQ2ZGyWgspVhvQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2343"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 28 Nov 2011 00:30:41 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 06 Apr 2010 23:10:58 GMT"
+        },
+        {
+          "age": "29507652"
+        },
+        {
+          "x-amz-cf-id": "47jA2MZ4Sw4DCikN2VyaaWmwTHmqX3rU2MwTeNh7e1Jz_UoUPpYraQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3059"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 05 Jan 2012 13:26:27 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 10 Feb 2011 01:27:46 GMT"
+        },
+        {
+          "age": "26177906"
+        },
+        {
+          "x-amz-cf-id": "WYavyIQcFAiZj--Zhj4aZuRfOIHvmeL3UfzvuuRJG_cspyZyEBTZvw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1090"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 06:05:00 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 06 Apr 2010 23:10:58 GMT"
+        },
+        {
+          "age": "25193"
+        },
+        {
+          "x-amz-cf-id": "8VDa9TCRS7VKfaAxdyPoMxc3nU5HHd7-ochC_jBjm5Htnl-jkMkuTA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "730"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 25 Jul 2012 12:42:51 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 31 Jan 2011 08:00:09 GMT"
+        },
+        {
+          "age": "8727722"
+        },
+        {
+          "x-amz-cf-id": "QrXHFzwiaMq4tNw6xz1x_Fy8OExfQwsb9eYgNg9x5of9ynYhiimfqg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1271"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 05 Jan 2012 19:58:22 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:13:47 GMT"
+        },
+        {
+          "age": "26154391"
+        },
+        {
+          "x-amz-cf-id": "zfueMiQqfM6t_I7CSioRWzJ6Ja4aCnQfweQZmxivqYZ8HJfnkGedAQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1333"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 08 Feb 2012 22:12:29 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 10 Feb 2011 01:27:46 GMT"
+        },
+        {
+          "age": "23208744"
+        },
+        {
+          "x-amz-cf-id": "ELEGmBCM3aCsE_CitSFuw5Z_9oO1nINZ1Td8UGwaW5JQqOgNgrbAgw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1361"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 07 Mar 2012 21:41:49 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Fri, 07 Oct 2011 06:20:36 GMT"
+        },
+        {
+          "age": "20791384"
+        },
+        {
+          "x-amz-cf-id": "pOqim5pkNLfn0oKxi7ICFEmFFenUh0PbL_R9Lb9h6TpuGt0tRp4z8Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3006"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 13 Oct 2011 00:59:11 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 02 Feb 2010 23:33:20 GMT"
+        },
+        {
+          "age": "33480342"
+        },
+        {
+          "x-amz-cf-id": "G9CB0PE2to9TXhCktQwgI5sW6rlvjeiIaljq43R1hfimZv_emDTQ5Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3051"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 22 Nov 2011 21:04:40 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:13:47 GMT"
+        },
+        {
+          "age": "29952013"
+        },
+        {
+          "x-amz-cf-id": "8B2pTWiByqir35Y8C9JwI9kCzXLE0qdWzMliO7vI6X4z0IPFb7OJSw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3784"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 24 Jan 2012 22:05:08 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 21 May 2010 10:34:44 GMT"
+        },
+        {
+          "age": "24505185"
+        },
+        {
+          "x-amz-cf-id": "iiwiqgcVCWG_cRsMapSsC7zbtuul0T9eNy4NjAklVsbJhZbhbNP0Hw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2439"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 20 Nov 2011 13:48:40 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 28 Sep 2011 08:02:37 GMT"
+        },
+        {
+          "age": "30150973"
+        },
+        {
+          "x-amz-cf-id": "N3_BBMhFY33Y_cabN1aZofeaRTYY2qxgxIlyDqqnyzTHoBb-HQQ4kA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3087"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 10:10:03 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Mon, 25 Jul 2011 21:08:27 GMT"
+        },
+        {
+          "age": "1047291"
+        },
+        {
+          "x-amz-cf-id": "pUS_TqP5ZtROVGDGuR-eDXw0ijzMiGzziwONZiQwoWOmwMrlTnRUiQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3377"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 12:55:04 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 05 Jun 2009 14:46:00 GMT"
+        },
+        {
+          "age": "1037390"
+        },
+        {
+          "x-amz-cf-id": "U8QzlM0myAnVtennCypCEE35AVBn9P7vU8rfVC-qdIV19u_F-61loA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 28 Sep 2012 12:16:12 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:15:32 GMT"
+        },
+        {
+          "age": "3113322"
+        },
+        {
+          "x-amz-cf-id": "yRMGD1lIFrzR7iBctL75xllVcgCY4oq5vxpU8vyBYtYIhDG4wgfhhw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2639"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 11 Jan 2011 12:36:42 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 21 Dec 2010 20:23:19 GMT"
+        },
+        {
+          "age": "57198492"
+        },
+        {
+          "x-amz-cf-id": "KUP-5JnHht-4Vm9AI5uL23vWqItT_PCAoTXYhS67UcTYyHi9H4qomw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "53"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 29 Sep 2012 16:43:06 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 23:12:42 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "3010908"
+        },
+        {
+          "x-amz-cf-id": "9nFbAiM9DpxC3cnA__WbfWVECGjZkkgmC6B1r2D6H_pZUeOJpmckKw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1266"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 13 Dec 2011 01:03:43 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 12 Dec 2011 22:30:22 GMT"
+        },
+        {
+          "age": "28209671"
+        },
+        {
+          "x-amz-cf-id": "4XS4NQ5jtAPsXr8uz5LSIhit3YaYLOacfgxTqaJdmgGUSVeVX3L52w=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"31-ris0UMaL_SL500_SS100_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "549"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 17 Sep 2012 06:10:41 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 04:41:18 GMT"
+        },
+        {
+          "age": "4085653"
+        },
+        {
+          "x-amz-cf-id": "8cM2EbSq2xMoZmAuqaRNnHUXo5fFEkwP993iO66WXR8cQhYdXav_-A=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2309"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 00:31:48 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 18 Sep 2012 23:01:43 GMT"
+        },
+        {
+          "age": "1081986"
+        },
+        {
+          "x-amz-cf-id": "I_rWsREl-5AOAKtcn3LicFnMAMonpKIxSr1BGia7JpyGrtD-VJlFAw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "4689"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 10 Oct 2012 00:37:12 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 15 Jul 2011 19:43:59 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "2118462"
+        },
+        {
+          "x-amz-cf-id": "zJr0j4xcaVnqbt7keScwtlVcaVTMpKQyOnG62dfi3bC2Yn7ZUU8hmQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "304"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 24 Aug 2012 23:48:39 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 18 Jul 2008 22:54:46 GMT"
+        },
+        {
+          "age": "6095775"
+        },
+        {
+          "x-amz-cf-id": "PKmkuepDkCjjY62A77yQuYuUte5c2Ty-_6DlKmAvhcwzRsHyn5nIrQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "3183"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 27 Sep 2012 03:20:41 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:20:32 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "3231853"
+        },
+        {
+          "x-amz-cf-id": "OwAw73zfegmW_QHY-kswWa1c-b8oV4xZ-5eevFXbZxfqoKPE6umE4Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "859"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 15:34:26 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Tue, 22 May 2012 07:35:03 GMT"
+        },
+        {
+          "age": "163828"
+        },
+        {
+          "x-amz-cf-id": "lfeQCmQ-LTseaf2mLmw-Ec-MgECRsFNyDab6oODONovNp3KBwaWuNQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "904"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 15:34:26 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 21 Apr 2011 09:50:08 GMT"
+        },
+        {
+          "age": "163828"
+        },
+        {
+          "x-amz-cf-id": "fx4kuYG47HcKaHNWoFHoUpVuQ9sWagCnJ3Kox-WAsGeI9bLRuIFkZA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"31-ris0UMaL_SL500_SS100_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "988"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 15:34:26 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 11 Apr 2012 09:49:52 GMT"
+        },
+        {
+          "age": "163828"
+        },
+        {
+          "x-amz-cf-id": "pdz_b69t7pq2FxRxED3J9qfLWu_VlLnSgt3UkrYI2IsWgh0cxAcbRg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "7477"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 13:03:42 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 01:06:13 GMT"
+        },
+        {
+          "age": "1296072"
+        },
+        {
+          "x-amz-cf-id": "IpXkwhJGWUHRMnyRAQVIxqffI1_ZEyW-nzUYrSWrfr8R_Y1uuGRCCQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "702"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 14 Sep 2012 15:53:26 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 12 Oct 2011 01:31:30 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "4309888"
+        },
+        {
+          "x-amz-cf-id": "HsG6bJczHwzkieHglmFzE2QCmzLxTaLPXXnAy-N55tzbuvrtZRCzCw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "7983"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 12:29:27 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 15 Jul 2011 19:44:12 GMT"
+        },
+        {
+          "age": "1298127"
+        },
+        {
+          "x-amz-cf-id": "sCXON_3fv6WubL7uLSJaIqpVlCgjIetJyv1h_hMdF4cY17dhW5AtuQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "21587"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 07:42:49 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 27 Sep 2012 18:42:30 GMT"
+        },
+        {
+          "age": "1315325"
+        },
+        {
+          "x-amz-cf-id": "T9aSuzcFAaMOSl0BfGK1RZtk2bHJRFveb6whI8ExXPmes7P1gEIr_g=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "14808"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 14 Sep 2012 19:42:20 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Sat, 02 Jun 2012 16:25:51 GMT"
+        },
+        {
+          "age": "4296154"
+        },
+        {
+          "x-amz-cf-id": "0V2zXn_NrH1y0_eQiJhavI_iThDjEBl3W8rbz6WK2bL14yhUFFMzMg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "929"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 15:34:26 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Sun, 20 Jun 2010 09:46:10 GMT"
+        },
+        {
+          "age": "163828"
+        },
+        {
+          "x-amz-cf-id": "kSSVSJhWVu77d7bZr26ow7tGxAtxQIJKxzBSnMTb-BvsXBOXCVvmrQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"41+6XVdi5mL_SX36_SY36_CR,0,0,36,36_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\", CP=\"PSAo PSDo OUR SAM OTR DSP COR\""
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/iu3?d=amazon.com&a1=&a2=0101e39f75aa93465ee8202686e3f52bc2745bcaf2fc55ecfd1eae647167a83ecbb5&old_oo=0&n=1351947870865&dcc=t"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "set-cookie": "ad-privacy=0; Domain=.amazon-adsystem.com; Expires=Thu, 01-Jan-2037 00:00:01 GMT; Path=/"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "170"
+        },
+        {
+          "content-type": "text/html;charset=ISO-8859-1"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "304"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "246"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-amz-id-2": "A8pOeFTyzWm76hXU6FfLkQf4c9wZCOf1QF9R4TGkjXEInQJp6farkkg0WB0HfwcK"
+        },
+        {
+          "x-amz-request-id": "4B032A9637D718D5"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        },
+        {
+          "x-amz-meta-jets3t-original-file-date-iso8601": "2011-11-08T18:38:37.000Z"
+        },
+        {
+          "x-amz-meta-md5-hash": "abb0183baa0ea995b47dcc0e9972095a"
+        },
+        {
+          "cache-control": "max-age=864000"
+        },
+        {
+          "last-modified": "Thu, 30 Aug 2012 18:02:23 GMT"
+        },
+        {
+          "etag": "\"ac923fb65b36b7e6df1b85ed9a2eeb25\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "AmazonS3"
+        },
+        {
+          "age": "157082"
+        },
+        {
+          "x-amz-cf-id": "QZJcXJG7Ji8wu-0D789ZbWAnaHnVN-XBUkupFYbHTmHhHcHrJq7P9Q=="
+        },
+        {
+          "via": "1.0 06b38b2ddcbb45c8e33db161a2316149.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "451"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 15:21:37 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=628223582"
+        },
+        {
+          "expires": "Thu, 30 Sep 2032 15:37:56 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        },
+        {
+          "content-length": "856"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 04:12:21 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630383568"
+        },
+        {
+          "expires": "Mon, 25 Oct 2032 15:37:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        },
+        {
+          "content-length": "6979"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 17 Oct 2012 16:08:26 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=629262241"
+        },
+        {
+          "expires": "Tue, 12 Oct 2032 16:08:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        },
+        {
+          "content-length": "1860"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Mon, 18 May 2009 10:07:44 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=580533835"
+        },
+        {
+          "expires": "Mon, 27 Sep 2032 10:42:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        },
+        {
+          "content-length": "810"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-lookup": "MISS from cdn-images.amazon.com:10080"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "516"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "p3p": "policyref=\"http://tag.admeld.com/w3c/p3p.xml\", CP=\"PSAo PSDo OUR SAM OTR BUS DSP ALL COR\""
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/ecm3?id=bfd291d0-29a4-4e30-a3a2-90bfcce51d8f&ex=admeld.com"
+        },
+        {
+          "content-length": "275"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "set-cookie": "meld_sess=bfd291d0-29a4-4e30-a3a2-90bfcce51d8f;expires=Sun, 05 May 2013 03:59:31 GMT;path=/;domain=tag.admeld.com;"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Thu, 27 Sep 2012 18:22:05 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 14:49:26 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 14:49:26 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "25380"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "80128"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\", CP=\"PSAo PSDo OUR SAM OTR DSP COR\""
+        },
+        {
+          "location": "http://s.amazon-adsystem.com/iu3?d=amazon.com&a1=&a2=0101e39f75aa93465ee8202686e3f52bc2745bcaf2fc55ecfd1eae647167a83ecbb5&old_oo=0&n=1351947870865&dcc=t"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "set-cookie": "ad-privacy=0; Domain=.amazon-adsystem.com; Expires=Thu, 01-Jan-2037 00:00:01 GMT; Path=/"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "57"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 28 Sep 2012 08:55:13 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=628633148"
+        },
+        {
+          "expires": "Tue, 05 Oct 2032 09:24:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        },
+        {
+          "content-length": "16588"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 11 Apr 2012 19:05:24 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=617190640"
+        },
+        {
+          "expires": "Tue, 25 May 2032 22:55:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        },
+        {
+          "content-length": "10742"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 25 May 2012 04:46:13 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=621621842"
+        },
+        {
+          "expires": "Fri, 16 Jul 2032 05:48:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:55 GMT"
+        },
+        {
+          "content-length": "3960"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 26 Oct 2012 09:03:20 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=630485970"
+        },
+        {
+          "expires": "Tue, 26 Oct 2032 20:04:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:54 GMT"
+        },
+        {
+          "content-length": "14019"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:56 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "16ZMB88V50KWWQXFQZNR"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "0PU9VNtv9/YHthxuwDwGQDz7kHY8GLhrhbQs/A0BbIf1y/GiCONyJttmltEgnDaZ"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:56 GMT"
+        },
+        {
+          "x-amzn-requestid": "10a7eef8-25b7-11e2-a2e0-8bdc8a85b675"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "53"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:56 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "16KH0V157G0TXXQVTR1Z"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "6Hy72ZQh4+twTqX90DijzRdHDpTv81nJLyxFZMBbjNHkb8qZfDO7y4+75uITFMjm"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "2212"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 27 Sep 2012 04:09:18 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 03 Aug 2011 20:37:32 GMT"
+        },
+        {
+          "age": "3228938"
+        },
+        {
+          "x-amz-cf-id": "e_uegUCHnyG0CG1xWLrNCiBH1sC8uTUlb-e31fTCz2013GXiBQpv3g=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"11+dlfeUrBL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1658"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 16 May 2012 17:46:43 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:13:29 GMT"
+        },
+        {
+          "age": "14757493"
+        },
+        {
+          "x-amz-cf-id": "rjUV7bECb3foXt-4i01sG21mlvMgo9nOu7EtcMeIYzyJSWWqNNlDOw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "16364"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 14 Dec 2011 03:17:38 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Fri, 04 Nov 2011 23:48:42 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "28115238"
+        },
+        {
+          "x-amz-cf-id": "I6W0oRiMtuRDCDZetJr8DtOxr0nMh8QpK29mcgE2eMGEw69ogMaPdg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 13 Jul 2011 17:18:50 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=616029616"
+        },
+        {
+          "expires": "Wed, 12 May 2032 12:25:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:56 GMT"
+        },
+        {
+          "content-length": "594"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:56 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0X3NVRPASEGRWVCHH1H3"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "+dgTelR5Pvj5ApOpupZ5c4EXyGBnWsp/CtTohBqWXrKuiSIBT+ZSU/92HDHIoBxS"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2902"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 27 Oct 2012 23:34:12 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 20 Jul 2012 15:59:40 GMT"
+        },
+        {
+          "age": "567044"
+        },
+        {
+          "x-amz-cf-id": "bYLWczW4h_oqRLXSyZdMo3kjSsqUZNWoGXrVLgvaIVqM8SXrlaPicA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3155"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 26 Jul 2012 09:16:33 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 19 Jul 2012 17:10:12 GMT"
+        },
+        {
+          "age": "8653703"
+        },
+        {
+          "x-amz-cf-id": "pxFBejzs4oRgmqxYc2s6mbS_QBknibmyPLQGd8Ejv-8cWl04HQGPtA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"41+6XVdi5mL_SX36_SY36_CR,0,0,36,36_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3536"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 27 May 2012 08:44:57 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 19 May 2011 14:46:28 GMT"
+        },
+        {
+          "age": "13839599"
+        },
+        {
+          "x-amz-cf-id": "vToxkdVmSZQS0V3iRZM-gCcaadczMLYZw_REFYGTBUn-HP5HfdAeDA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3143"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 06 Aug 2012 15:53:39 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 06 Aug 2012 03:38:02 GMT"
+        },
+        {
+          "age": "7679477"
+        },
+        {
+          "x-amz-cf-id": "752wgDaFeaq4IQjicHeqyUiLYIjEjsca-nvc2LB2o4jOXMT99M6E2g=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3209"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 28 Feb 2012 22:53:21 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 21 Oct 2010 19:52:40 GMT"
+        },
+        {
+          "age": "21478295"
+        },
+        {
+          "x-amz-cf-id": "UDgO86Lg7vsbRr_HLz0bT_G-fu7CRAkkBmD_NFdclHEzx1CJpRhtKw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"31-ris0UMaL_SL500_SS100_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3210"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 27 Oct 2012 23:45:01 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Fri, 20 Jul 2012 15:59:43 GMT"
+        },
+        {
+          "age": "566395"
+        },
+        {
+          "x-amz-cf-id": "rEUppa210byJyTItTaRQ2r-jhwUwD4c2CKORz2AGJaLhjCZ_LQ2w9w=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:56 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0ESJ91CM6R89TGWH3QJG"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "Mg+wYQrytsBDnHHKyZlGNrkR5GzVMXvkIuJkR86aYslzZP5CJX/EEO5A8c1v2Jk4"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "21282"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 21:09:43 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 21:09:31 GMT"
+        },
+        {
+          "age": "143714"
+        },
+        {
+          "x-amz-cf-id": "jFqxNpOKI6HWPqTs3raLIRgnJcu3GReFRL3MjibUt9APw7R9CfzNqQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"11+dlfeUrBL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "439"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 20 Oct 2012 19:18:25 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:16:17 GMT"
+        },
+        {
+          "age": "1187193"
+        },
+        {
+          "x-amz-cf-id": "5en8vtO00oTNRx5jsyJYxwuPMEVufg38JPIk7uytbZiFN77vUtBibg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"11+dlfeUrBL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:57 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "1AB4PH2A91QH3YJJ2WCZ"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "V5wX099kS85XeVk46pZgvH7cjgKx1WawnTJkqYth5ykinf4em6ZqgNlZk9K/lPby"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "67"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 05:50:27 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:16:14 GMT"
+        },
+        {
+          "age": "1062871"
+        },
+        {
+          "x-amz-cf-id": "eIpkgqRGkyaTW4PSLscvrasxuDsM3f4NIrPYv6VI1sZt_IgixOKN_A=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"11+dlfeUrBL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "861"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 07:26:29 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Sat, 24 Nov 2007 03:04:01 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "1316309"
+        },
+        {
+          "x-amz-cf-id": "5MBwLvJE3E5vg0khYEnuWX6RGzCOtyfFmYYy2nuztIayL9O0paE0oA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 23 Oct 2012 09:12:37 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 04 Jun 2010 01:44:17 GMT"
+        },
+        {
+          "age": "964341"
+        },
+        {
+          "x-amz-cf-id": "W5ENbtcrY4pVK3r7ND94JJHXcEjjBccP7Q77zOSiZQUgWtPBn75lHw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1598"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 20 Oct 2012 15:51:07 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:16:29 GMT"
+        },
+        {
+          "age": "1199631"
+        },
+        {
+          "x-amz-cf-id": "nFYTABAConMh8T_fXHANG9_r3FjyUfnSBWjxeb2GqJKtgi_mNRIXMw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1657"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 25 Sep 2012 17:28:50 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:16:30 GMT"
+        },
+        {
+          "age": "3353768"
+        },
+        {
+          "x-amz-cf-id": "ssIfXXDbBp8rP_142eUVOboNUYX4Iood5RH_Qe9W7wP1xbkZp9MChw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3013"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 28 Nov 2011 22:24:05 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:13:29 GMT"
+        },
+        {
+          "age": "29428853"
+        },
+        {
+          "x-amz-cf-id": "vO0R09hZC6TnyhMNTV9jEegb2DgNmH-Z1KaQiyeSbsYm8eoBtHUnDw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "455"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:58 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "2522"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:58 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:58 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "dl_s": "a104"
+        },
+        {
+          "x-host": "a104 D=1922"
+        },
+        {
+          "p3p": "CP=\"ALL DSP COR PSAa PSDa OUR IND COM NAV INT LOC OTC\", policyref=\"http://ch.questionmarket.com/w3c/audit2007/p3p_DynamicLogic.xml\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "558"
+        },
+        {
+          "keep-alive": "timeout=120, max=934"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:58 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "dl_s": "a104"
+        },
+        {
+          "x-host": "a103 D=213"
+        },
+        {
+          "p3p": "CP=\"ALL DSP COR PSAa PSDa OUR IND COM NAV INT LOC OTC\", policyref=\"http://ch.questionmarket.com/w3c/audit2007/p3p_DynamicLogic.xml\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "78"
+        },
+        {
+          "keep-alive": "timeout=120, max=941"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "expires": "Mon, 26 Jul 1997 05:00:00 GMT"
+        },
+        {
+          "cache-control": "post-check=0, pre-check=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "PL=_974702-1-83324420; expires=Sun, 03-Nov-2013 13:04:58 GMT; path=/; domain=.questionmarket.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Wed, 17 Oct 2012 17:29:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 05:34:15 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 05:34:15 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "27737"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "27043"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:58 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "dl_s": "a212"
+        },
+        {
+          "x-host": "a212 D=715"
+        },
+        {
+          "p3p": "CP=\"ALL DSP COR PSAa PSDa OUR IND COM NAV INT LOC OTC\", policyref=\"http://ch.questionmarket.com/w3c/audit2007/p3p_DynamicLogic.xml\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "keep-alive": "timeout=120, max=973"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "Mon, 26 Jul 1997 05:00:00 GMT"
+        },
+        {
+          "cache-control": "post-check=0, pre-check=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "ES=974702-1d5qN-0; expires=Wed, 25-Dec-2013 05:04:58 GMT; path=/; domain=.questionmarket.com;"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:58 GMT"
+        },
+        {
+          "x-amzn-requestid": "123b3889-25b7-11e2-8af6-a1b44f97a3ae"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "53"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:58 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "1ZP9F4EGXPG1W1PYCNJK"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "AsL0eWMF3+3wScCyR0bGR31dSLss9n05o3uERrVw6pReE9DgHJ1jKFUl9eThTjRS"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:58 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "168BW4BHQH0ZXM7FXMPB"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "cEL12N93+m4WoEqFtPIfCFUi+syrhK4ihYi/vQREHqBRscgh343Rj/z+yvBSU/1C"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:04:58 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "1PF1RSF1KPZFT8AG8QH3"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "BMEF9l9idgW7J6L5kAVCmgL1L1VX3ONDIY4c/R7+/waDULftLKfFOhjdf5bX9Jgw"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "76"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 25 Sep 2012 13:31:43 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 15:53:50 GMT"
+        },
+        {
+          "age": "3367996"
+        },
+        {
+          "x-amz-cf-id": "ecrxOwZyLIYoOI7n1HZdjAnEynOb8rnSjf9oMBvu9l-mcg-DvsQ5tA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"11+dlfeUrBL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2953"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 14 Oct 2012 20:13:59 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Fri, 18 Mar 2011 23:50:26 GMT"
+        },
+        {
+          "age": "1702260"
+        },
+        {
+          "x-amz-cf-id": "RRnk1cdyHejHw9mprrW3eHhVJDtMgC2-2Z_Ozk1TtfyHQbMGDRM1gQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2908"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 00:29:01 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:13:47 GMT"
+        },
+        {
+          "age": "45358"
+        },
+        {
+          "x-amz-cf-id": "dVVqpxaUvghScp5L3V8knNH7yD0rPX4f2QIUqHQG7hWgDw29J2DGyQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"41+6XVdi5mL_SX36_SY36_CR,0,0,36,36_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2684"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 29 Oct 2012 09:50:03 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 02 Feb 2010 23:26:50 GMT"
+        },
+        {
+          "age": "443696"
+        },
+        {
+          "x-amz-cf-id": "PlD1_xjVuo-YCz7_Za4wyP6LFVnojIw0t9xjXHByHBqF2ZeqI4b_qg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"31-ris0UMaL_SL500_SS100_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "879"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 08 Jul 2012 02:35:34 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:12:48 GMT"
+        },
+        {
+          "age": "10232965"
+        },
+        {
+          "x-amz-cf-id": "NFgWbhPAIPS6Aa_OA1MZi4RJV38Eh2JztiuONINXzMa0bG62le6jyA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"11+dlfeUrBL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "829"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 29 Nov 2011 15:46:34 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:12:49 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "29366305"
+        },
+        {
+          "x-amz-cf-id": "09OGcA01CtEV2DWxFMbKMdNmD6Wr6zUzXg6LlkVtCG84Y07dTLSY0Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "874"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 20 Oct 2012 17:41:46 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:12:48 GMT"
+        },
+        {
+          "age": "1192993"
+        },
+        {
+          "x-amz-cf-id": "rwvtxrU_8yM4vEsn3LxI68PMeCTNAaI2pID_hvPOaXhJAUXpLcUqOQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "839"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 28 Jul 2012 16:53:55 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:12:48 GMT"
+        },
+        {
+          "age": "8453464"
+        },
+        {
+          "x-amz-cf-id": "jK_V3T8gBhnVX6aGpizNe84I03zSajHOTGC01MnF2N-ZKUFTuuznfg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "873"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 02 Oct 2012 10:07:50 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:12:48 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "2775429"
+        },
+        {
+          "x-amz-cf-id": "zP8uDh7oW4qGktFpCJQlKyzDvuaUlw8SfQPZeV2OLAF_FolwTs7PYA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "839"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 04 Oct 2012 03:38:14 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:12:48 GMT"
+        },
+        {
+          "age": "2626005"
+        },
+        {
+          "x-amz-cf-id": "zCUT7P4ddAsA_5EOdXS-ecWSi3Caf1GD9wdw37lzeKfQj2j9AmHUiQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "829"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 18 Feb 2012 00:35:57 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:12:46 GMT"
+        },
+        {
+          "age": "22422542"
+        },
+        {
+          "x-amz-cf-id": "QKTCegDFqVr3XC8HIuieCb-HlLFpsf1vJJyDKhTn9DFbJiLWVXQjLQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"11+dlfeUrBL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "874"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sun, 29 Jul 2012 00:33:38 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:12:47 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "8425881"
+        },
+        {
+          "x-amz-cf-id": "FQmsZuwjmRdgwUM5HxTx27tY2H27QOrbg1DJdNz_RrAOa991o5Lvyw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:05:01 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "1KF6CJF0ZA3T90MCGE8X"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "EhBekXVsIWcz6GtFV9/VWJHMzQoVZUur+CK0EG1dAElJjqTWpGnJuKNs84/dLZ0q"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "394"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 23 Oct 2012 16:46:08 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 01:46:54 GMT"
+        },
+        {
+          "age": "937134"
+        },
+        {
+          "x-amz-cf-id": "L9oihLkhCsGUlU-3jqFLwWX3TyuBQLcp_cHchbBiCmtUA736X8Kphg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"11+dlfeUrBL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "627"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 11 Oct 2012 14:33:21 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:15:44 GMT"
+        },
+        {
+          "age": "1981901"
+        },
+        {
+          "x-amz-cf-id": "7ml4lF66qQTzIXFECW3ocZ5nG9vHHfuYDmXpdeBjLVSaQQolCys92A=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "621"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 10 Oct 2012 11:21:25 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:15:46 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "2079817"
+        },
+        {
+          "x-amz-cf-id": "CoLcmSXF2suFL6QBnOjjLxEUhf72VKlrplyC4RYJlfNREf6-Xi0pXw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2358"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 29 Nov 2011 01:52:38 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:13:29 GMT"
+        },
+        {
+          "age": "29416344"
+        },
+        {
+          "x-amz-cf-id": "dRi8YsVC5rJM48lwap3Mh06QHVr9w6Oq0Pfg31QRRKiDl1QSIT3zdg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1594"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 20 Oct 2012 10:14:19 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:15:59 GMT"
+        },
+        {
+          "age": "1219843"
+        },
+        {
+          "x-amz-cf-id": "1biuu9U97pslYVYAmMFhrwSwOBYVwXiLgwm1MRKI7_h7l2zmYZZEaQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 26 Sep 2012 22:18:37 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 25 Sep 2012 20:26:21 GMT"
+        },
+        {
+          "age": "3249985"
+        },
+        {
+          "x-amz-cf-id": "fX317kpOFNd5ZTVD5dUMrmSEeYRzqm4WpyDuKNG28yJ4O9eAvvazUw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"41+6XVdi5mL_SX36_SY36_CR,0,0,36,36_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1061"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 19 Oct 2012 11:04:19 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:18:05 GMT"
+        },
+        {
+          "age": "1303243"
+        },
+        {
+          "x-amz-cf-id": "mAtXqkAkC4DjophBzYEW5S6QprX5KyDZpPzyK9EozCLiukdFrBze5A=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "850"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 19 Nov 2011 02:02:32 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:12:52 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "30279750"
+        },
+        {
+          "x-amz-cf-id": "V9zouqOby7zTdw8N1UFD-7MjNT6Is1zC6qGyOi0cvSwTqMJZ1Qkygw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2561"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 01:47:15 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:13:29 GMT"
+        },
+        {
+          "age": "1077467"
+        },
+        {
+          "x-amz-cf-id": "S275mzRyfiEZwFTcPfyW0eoYH91UX_599Ev_ECgM6b_xOLfjspcbHg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"31-ris0UMaL_SL500_SS100_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "564"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 29 Nov 2011 11:54:46 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 04 Jun 2010 01:27:32 GMT"
+        },
+        {
+          "age": "29380216"
+        },
+        {
+          "x-amz-cf-id": "R7S8on0vdk1HXxrim-2c2Zh8aNdO6mf4C0WS3tKHegaM2jWsiM5epQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1698"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 29 Nov 2011 12:21:20 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 01:03:45 GMT"
+        },
+        {
+          "age": "29378622"
+        },
+        {
+          "x-amz-cf-id": "YvMqD5UqqFKzy1f2mFcHqX7aM_4HxOmRkYus-RhWfCdy_pqhPAEz_g=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"11+dlfeUrBL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "558"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 20 Oct 2012 11:34:34 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Fri, 04 Jun 2010 01:27:17 GMT"
+        },
+        {
+          "age": "1215028"
+        },
+        {
+          "x-amz-cf-id": "cVa44QM2u8IAuUF9QEfpfnoTg8a0J18iQn7wd2DQr-jo-fY8BpiHkQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "2268"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 28 Jul 2012 02:30:22 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 01:00:18 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "8505280"
+        },
+        {
+          "x-amz-cf-id": "22Ju-KfgdJeRvZSlX5DsdLObbhPEZeBSd4Gc72ZIaWMiVqmbMkB3Bg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "161"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 25 Sep 2012 20:28:46 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:15:25 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "3342976"
+        },
+        {
+          "x-amz-cf-id": "qmBPDk2JKVaJRpv7A4mhguHOgSAQ224UfofPNOhYc7AXsZ28LFXM-Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1567"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 29 Sep 2012 12:18:43 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:14:03 GMT"
+        },
+        {
+          "age": "3026779"
+        },
+        {
+          "x-amz-cf-id": "9apayreRLqLNv5SP9KNS5EuSvY5sPVDHoDgblKHgUdkUCoUDFfPCbw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "2075"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 08 Feb 2012 03:25:50 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:19:09 GMT"
+        },
+        {
+          "age": "23276352"
+        },
+        {
+          "x-amz-cf-id": "x7eg4fYYkTWpaWFE4nN7BtaqRyiZfNva-voci7p3Xzbfipq19svpKQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"11+dlfeUrBL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1703"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 29 Nov 2011 09:32:04 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:14:39 GMT"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "age": "29388778"
+        },
+        {
+          "x-amz-cf-id": "Sud7TM_0UEovovJxWwSbgeoYTXcAYAv14GhdR63hJZOjeBUYsvtKqQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"01-XuHrwcHL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "2601"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 25 Jul 2012 12:53:34 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:17:36 GMT"
+        },
+        {
+          "age": "8727088"
+        },
+        {
+          "x-amz-cf-id": "mAk0OO6evBoCPjFxnJqirH_8vom2gwHEBysCZcqQfQejl1rp3OGD1Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1581"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 11:57:50 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:13:01 GMT"
+        },
+        {
+          "age": "1040832"
+        },
+        {
+          "x-amz-cf-id": "Dib_HmcmgtWNGzNtGv3F6ZAXixIagykEiamEBmt2obULi0G_yrbohw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "etag": "\"01+KP3cIDVL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "44"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 20 Oct 2012 02:45:35 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 16:13:49 GMT"
+        },
+        {
+          "age": "1246767"
+        },
+        {
+          "x-amz-cf-id": "wjm3efkQaATVWVoZIxXYwJ9h7_UJVQWBeywk_XevnjWO-aG5dXU1uw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Thu, 12 Mar 2009 22:20:15 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=613385389"
+        },
+        {
+          "expires": "Sun, 11 Apr 2032 21:54:51 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:05:02 GMT"
+        },
+        {
+          "content-length": "1167"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "etag": "\"11Z3ZviGhqL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:05:02 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0N0ET7DXR0Z0S958XDT2"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "rVbwKM7uj9c8KPLYmRAVt4kYRdMGzqE9h6Tyc+UcXD6y0h6n5t1Qps2dG4l0erjn"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1419"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 21 Nov 2011 14:19:01 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:13:29 GMT"
+        },
+        {
+          "age": "30062762"
+        },
+        {
+          "x-amz-cf-id": "V720Rh4VXwLpavgc0gzogCAvcfu8eju-6aTUgkZ0KVqt2Dmee6LRbA=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3603"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 06:00:11 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Tue, 06 Apr 2010 23:10:58 GMT"
+        },
+        {
+          "age": "25492"
+        },
+        {
+          "x-amz-cf-id": "fQb0nipMtXJuYbIZySKBDRsrklJuv7qAkK8XsAxNdlaxuu3_Nb882A=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"41+6XVdi5mL_SX36_SY36_CR,0,0,36,36_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "610"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 10 Oct 2012 05:09:08 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 01:30:08 GMT"
+        },
+        {
+          "age": "2102155"
+        },
+        {
+          "x-amz-cf-id": "CaXyn6Of0tMpboI2JSReSog7OZegmXSk2HeG_0TZ-GXKneON61wt4Q=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"11+dlfeUrBL#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6063"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 28 Sep 2010 14:19:43 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Sun, 23 May 2010 07:31:32 GMT"
+        },
+        {
+          "age": "66264320"
+        },
+        {
+          "x-amz-cf-id": "Xi5psl_5u_FA8F_cxp1Bgg5JQqekzjzXubyxkq6OioH5vJa_xpl7bg=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"31-ris0UMaL_SL500_SS100_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3808"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Mon, 23 Jan 2012 20:55:29 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 28 Sep 2011 08:02:37 GMT"
+        },
+        {
+          "age": "24595774"
+        },
+        {
+          "x-amz-cf-id": "YCExo8QCW6i8b43rK1WKdbqGi4BBr67tDQvHKeByUOjFZWkYcGmSVw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4743"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Thu, 16 Feb 2012 18:43:39 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 21 Nov 2011 14:13:47 GMT"
+        },
+        {
+          "age": "22530084"
+        },
+        {
+          "x-amz-cf-id": "f-ZNWJJlfQWW0yKzQd7uCRUJaMBxf_uM7iH1fbKBNdQQggwy-fMhMQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5433"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 10 Apr 2012 03:22:11 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 15 Feb 2012 16:43:09 GMT"
+        },
+        {
+          "age": "17919772"
+        },
+        {
+          "x-amz-cf-id": "d_if579P0cd1V3nzUXiXXtDTylQLMz1X-zUPk2ry79G_nUYX-N0rAQ=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4449"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Fri, 23 Mar 2012 00:45:43 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "etag": "\"41YZLkI+UsL_SL135_#1\""
+        },
+        {
+          "last-modified": "Fri, 09 Sep 2011 07:00:28 GMT"
+        },
+        {
+          "age": "19484360"
+        },
+        {
+          "x-amz-cf-id": "XHbZSMpsPMFYPGHelyqQvYo133-6UF8nzLJrfmuubfb8md_c3wKN8w=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4065"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Tue, 25 Sep 2012 13:07:27 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Thu, 04 Feb 2010 16:43:55 GMT"
+        },
+        {
+          "age": "3369456"
+        },
+        {
+          "x-amz-cf-id": "cqvYtZEgzgfwS7oAvqOkuzRizhSe9arLcRGaP5nnF2izwecHSwS-Cw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"31-ris0UMaL_SL500_SS100_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4778"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Wed, 14 Sep 2011 17:50:19 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "cache-control": "max-age=630720000,public"
+        },
+        {
+          "expires": "Wed, 18 May 2033 03:33:20 GMT"
+        },
+        {
+          "last-modified": "Mon, 24 Aug 2009 16:48:22 GMT"
+        },
+        {
+          "age": "35925284"
+        },
+        {
+          "x-amz-cf-id": "wXY9DDbUrHJoSYufMPmtErRRutYkp32cOy1b07_NcZFdUScDaLORpw=="
+        },
+        {
+          "via": "1.0 e0361d2450a4995d92d661bf6b825ede.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        },
+        {
+          "etag": "\"41+6XVdi5mL_SX36_SY36_CR,0,0,36,36_#1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "last-modified": "Wed, 22 Sep 2010 23:59:48 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=613358641"
+        },
+        {
+          "expires": "Sun, 11 Apr 2032 14:29:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:05:02 GMT"
+        },
+        {
+          "content-length": "22760"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:05:03 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0M6TJE3FZYTXRNRY512Y"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "uk/tDjh11RBjIvdv0Gj9JUCG/M9iirulGzM8OhRvjW/qch4hPreEf7n4VnzczAr6"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:05:03 GMT"
+        },
+        {
+          "server": "Server"
+        },
+        {
+          "x-amz-id-1": "0DNVGFVH4CF96QBN4TM9"
+        },
+        {
+          "p3p": "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \""
+        },
+        {
+          "x-amz-id-2": "vE+1ySfLV/mErY5cd7s2NdxUOOOUvbYCVUyYCdjxcxbN3eyQRj3PwWhhDk9+xh+Q"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "session-id=178-5926262-3769435; path=/; domain=.amazon.com; expires=Tue, 01-Jan-2036 08:00:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_22.json b/jetty-http2/http2-hpack/src/test/resources/data/story_22.json
new file mode 100644
index 0000000..a2d7031
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_22.json
@@ -0,0 +1,14947 @@
+{
+  "context": "response",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:30 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "cache-control": "max-age=86400"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 12:56:30 GMT"
+        },
+        {
+          "last-modified": "Tue, 12 Jan 2010 13:48:00 GMT"
+        },
+        {
+          "etag": "\"51-4b4c7d90\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "81"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "text/html"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:30 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "cache-control": "max-age=86400"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 12:56:30 GMT"
+        },
+        {
+          "last-modified": "Mon, 24 Jan 2011 11:52:00 GMT"
+        },
+        {
+          "etag": "\"13e-4d3d67e0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "318"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "text/plain"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:32 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        },
+        {
+          "content-length": "4034"
+        },
+        {
+          "content-type": "text/html;charset=gbk"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 12:56:32 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "set-cookie": "BAIDUID=B6136AC10EBE0A8FCD216EB64C4C1A5C:FG=1; expires=Sat, 03-Nov-42 12:56:32 GMT; path=/; domain=.baidu.com"
+        },
+        {
+          "p3p": "CP=\" OTI DSP COR IVA OUR IND COM \""
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:32 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 20 Jan 2011 07:15:35 GMT"
+        },
+        {
+          "etag": "\"65e-49a41e65933c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1630"
+        },
+        {
+          "cache-control": "max-age=315360000"
+        },
+        {
+          "expires": "Tue, 01 Nov 2022 12:56:32 GMT"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:32 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 19 Apr 2012 09:51:20 GMT"
+        },
+        {
+          "etag": "\"5b-4be051d263600\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "91"
+        },
+        {
+          "cache-control": "max-age=315360000"
+        },
+        {
+          "expires": "Tue, 01 Nov 2022 12:56:32 GMT"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:33 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "p3p": "CP=\" OTI DSP COR IVA OUR IND COM \""
+        },
+        {
+          "set-cookie": "BAIDUID=94A36D239683687B3C92793A22BA0C93:FG=1; expires=Sun, 03-Nov-13 12:56:33 GMT; max-age=31536000; path=/; domain=.baidu.com; version=1"
+        },
+        {
+          "last-modified": "Thu, 11 Aug 2011 07:44:31 GMT"
+        },
+        {
+          "etag": "\"57d9-4aa35f79b95c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=315360000"
+        },
+        {
+          "expires": "Tue, 01 Nov 2022 12:56:33 GMT"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8000"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "application/javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:33 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "p3p": "CP=\" OTI DSP COR IVA OUR IND COM \""
+        },
+        {
+          "set-cookie": "BAIDUID=129ADB92B43CFC527CA7FB93BCB8AB88:FG=1; expires=Sun, 03-Nov-13 12:56:33 GMT; max-age=31536000; path=/; domain=.baidu.com; version=1"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 06:54:50 GMT"
+        },
+        {
+          "etag": "\"5555-4cd7d9cac8280\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=315360000"
+        },
+        {
+          "expires": "Tue, 01 Nov 2022 12:56:33 GMT"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "7499"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "application/javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:33 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "p3p": "CP=\" OTI DSP COR IVA OUR IND COM \""
+        },
+        {
+          "set-cookie": "BAIDUID=CC2AAA2AE3AF303A945CAF8E9945BC2D:FG=1; expires=Sun, 03-Nov-13 12:56:33 GMT; max-age=31536000; path=/; domain=.baidu.com; version=1"
+        },
+        {
+          "last-modified": "Thu, 20 Sep 2012 04:32:55 GMT"
+        },
+        {
+          "etag": "\"26fa-4ca1a9df6cbc0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=315360000"
+        },
+        {
+          "expires": "Tue, 01 Nov 2022 12:56:33 GMT"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3586"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "application/javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:33 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "p3p": "CP=\" OTI DSP COR IVA OUR IND COM \""
+        },
+        {
+          "set-cookie": "BAIDUID=6C1D358E43AAD143080C18E498033F71:FG=1; expires=Sun, 03-Nov-13 12:56:33 GMT; max-age=31536000; path=/; domain=.baidu.com; version=1"
+        },
+        {
+          "last-modified": "Thu, 30 Jun 2011 10:56:51 GMT"
+        },
+        {
+          "etag": "\"25f-4a6ebc21c42c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "607"
+        },
+        {
+          "cache-control": "max-age=315360000"
+        },
+        {
+          "expires": "Tue, 01 Nov 2022 12:56:33 GMT"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/png"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:34 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-length": "147"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 12:56:32 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "set-cookie": "BAIDUID=B6136AC10EBE0A8FCD216EB64C4C1A5C:FG=1; expires=Sat, 03-Nov-42 12:56:32 GMT; path=/; domain=.baidu.com"
+        },
+        {
+          "p3p": "CP=\" OTI DSP COR IVA OUR IND COM \""
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "last-modified": "Mon, 24 Jan 2011 11:52:05 GMT"
+        },
+        {
+          "etag": "\"13e-49a963a8e0340\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "BAIDUID=7B3F07DA7EB7581C6AAAAC2399C0F469:FG=1; max-age=31536000; expires=Sun, 03-Nov-13 12:56:39 GMT; domain=.hao123.com; path=/; version=1"
+        },
+        {
+          "p3p": "CP=\" OTI DSP COR IVA OUR IND COM \""
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 12:56:39 GMT"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:39 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "etag": "\"2880337125\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 14 Feb 2012 03:23:36 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:40 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "content-length": "795"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:40 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "etag": "\"1970946457\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 21 Aug 2012 12:19:47 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:40 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "content-length": "49"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:40 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "etag": "\"3508231446\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Wed, 12 Sep 2012 06:59:30 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:40 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "content-length": "4465"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:40 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "JSP2/1.0.2"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:41 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "5928"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "etag": "\"3116325809147476198\""
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 02:09:15 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 02:07:33 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "JSP2/1.0.2"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:41 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "10550"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "etag": "\"13813589230791420108\""
+        },
+        {
+          "expires": "Fri, 01 Nov 2013 10:53:47 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 06:05:04 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "etag": "\"991580451\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 05:07:04 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:40 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "11081"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:40 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "etag": "\"2826587238\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 18 Sep 2012 06:59:28 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:41 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "content-length": "1803"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:41 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "etag": "\"2820264983\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 18 Sep 2012 02:45:17 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:41 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "content-length": "3703"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:41 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "etag": "\"779014302\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Mon, 06 Aug 2012 10:17:50 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:41 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "content-length": "2053"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:41 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "etag": "\"439052966\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 05:07:03 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:40 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "38609"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:40 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "etag": "\"1503293558\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 05:07:03 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:41 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "13174"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:41 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "etag": "\"790245820\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 05:07:03 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:40 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "33536"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:40 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "etag": "\"1136345403\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Wed, 17 Oct 2012 17:08:02 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:43 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "content-length": "3731"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:43 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "etag": "\"1881822353\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 16 Oct 2012 15:28:36 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:43 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1007"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:43 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "etag": "\"55301462\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Fri, 19 Oct 2012 15:59:17 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:43 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2179"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:43 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "BAIDUID=7B3F07DA7EB7581C6AAAAC2399C0F469:FG=1; max-age=31536000; expires=Sun, 03-Nov-13 12:56:39 GMT; domain=.hao123.com; path=/; version=1"
+        },
+        {
+          "p3p": "CP=\" OTI DSP COR IVA OUR IND COM \""
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:43 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:43 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "etag": "\"2082195828\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 18 Jan 2011 06:39:02 GMT"
+        },
+        {
+          "content-length": "43"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "etag": "\"2082195828\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 18 Jan 2011 06:39:02 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:43 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:43 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "etag": "\"1129043035\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Fri, 19 Oct 2012 15:58:50 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:43 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1962"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:43 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "JSP2/1.0.2"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:43 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "1076"
+        },
+        {
+          "etag": "\"2330871933\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Fri, 13 Jul 2012 03:17:42 GMT"
+        },
+        {
+          "expires": "Sat, 12 Jan 2013 11:31:05 GMT"
+        },
+        {
+          "cache-control": "max-age=15552000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "etag": "\"2084456863\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 18 Jan 2011 03:02:19 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:43 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:43 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:44 GMT"
+        },
+        {
+          "server": "ECOM Apache 1.0.13.0"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "text/html"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "etag": "\"1905870111\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Wed, 18 Nov 2009 09:44:09 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 12:56:44 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:44 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.15"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:18 GMT"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "No-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "set-cookie": "JSESSIONID=24206446514B3C020AC50919B9330503; Path=/"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "x-powered-by": "PHP/5.2.3"
+        },
+        {
+          "cache-control": "max-age=259200"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 12:56:46 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "set-cookie": "loc=1%7C%B1%B1%BE%A9%7C%B1%B1%BE%A9; expires=Tue, 06-Nov-2012 12:56:46 GMT; path=/; domain=.hao123.com"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "684"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:46 GMT"
+        },
+        {
+          "server": "lighttpd"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "media": "media"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Fri, 26 Oct 2012 12:24:13 GMT"
+        },
+        {
+          "last-modified": "Sat, 25 Apr 2009 07:04:00 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:46 GMT"
+        },
+        {
+          "server": "apache"
+        },
+        {
+          "content-length": "8992"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:47 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 10:00:20 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:26:47 GMT"
+        },
+        {
+          "cache-control": "max-age=1800"
+        },
+        {
+          "set-cookie": "id58=05eNElCVFI9c8Hfk16UMAg==; expires=Tue, 01-Nov-22 12:56:47 GMT; domain=58.com; path=/"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"CUR ADM OUR NOR STA NID\""
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "etag": "\"1840151033\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 14 Feb 2012 03:23:36 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:47 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4959"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:47 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "etag": "\"1236171233\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 14 Feb 2012 03:23:36 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:47 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4571"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:47 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "BAIDUID=7B3F07DA7EB7581C6AAAAC2399C0F469:FG=1; max-age=31536000; expires=Sun, 03-Nov-13 12:56:39 GMT; domain=.hao123.com; path=/; version=1"
+        },
+        {
+          "p3p": "CP=\" OTI DSP COR IVA OUR IND COM \""
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 12:56:47 GMT"
+        },
+        {
+          "cache-control": "max-age=31104000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:47 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "etag": "\"567172269\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Thu, 01 Mar 2012 02:31:58 GMT"
+        },
+        {
+          "content-length": "1150"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:54:27 GMT"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "last-modified": "Thu, 16 Aug 2012 08:37:54 GMT"
+        },
+        {
+          "cache-control": "max-age=300"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "age": "1"
+        },
+        {
+          "x-via": "1.1 bjgm232:8102 (Cdn Cache Server V2.0), 1.1 stsz70:8105 (Cdn Cache Server V2.0), 1.1 gdyf13:9080 (Cdn Cache Server V2.0)"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 04:02:33 GMT"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "22332"
+        },
+        {
+          "last-modified": "Mon, 25 Apr 2011 10:41:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "1"
+        },
+        {
+          "x-via": "1.1 gm240:8104 (Cdn Cache Server V2.0), 1.1 stcz163:8106 (Cdn Cache Server V2.0), 1.1 gdyf13:8184 (Cdn Cache Server V2.0)"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Fri, 26 Oct 2012 12:49:31 GMT"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "9309"
+        },
+        {
+          "last-modified": "Mon, 25 Apr 2011 08:07:52 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "1"
+        },
+        {
+          "x-via": "1.1 gm243:8106 (Cdn Cache Server V2.0), 1.1 stsz75:8104 (Cdn Cache Server V2.0), 1.1 gdyf16:9080 (Cdn Cache Server V2.0)"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.2.0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:49 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 16 Aug 2012 08:37:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Fri, 26 Oct 2012 12:49:31 GMT"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "23163"
+        },
+        {
+          "last-modified": "Mon, 25 Apr 2011 10:46:11 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "1"
+        },
+        {
+          "x-via": "1.1 gm240:8101 (Cdn Cache Server V2.0), 1.1 stsz74:8080 (Cdn Cache Server V2.0), 1.1 gdyf17:8184 (Cdn Cache Server V2.0)"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Fri, 26 Oct 2012 12:49:21 GMT"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "281"
+        },
+        {
+          "last-modified": "Mon, 25 Apr 2011 08:07:52 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "1"
+        },
+        {
+          "x-via": "1.1 gm239:8102 (Cdn Cache Server V2.0), 1.1 stsz70:88 (Cdn Cache Server V2.0), 1.1 gdyf20:8184 (Cdn Cache Server V2.0)"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 04:29:06 GMT"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "183"
+        },
+        {
+          "last-modified": "Wed, 11 Jan 2012 07:50:38 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "1"
+        },
+        {
+          "x-via": "1.1 tjtg100:80 (Cdn Cache Server V2.0), 1.1 gm240:8106 (Cdn Cache Server V2.0), 1.1 stsz75:8107 (Cdn Cache Server V2.0), 1.1 gdyf18:8360 (Cdn Cache Server V2.0)"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:50 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Sun, 24 Apr 2011 02:10:42 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:26:50 GMT"
+        },
+        {
+          "cache-control": "max-age=1800"
+        },
+        {
+          "set-cookie": "id58=05eNElCVFI9c8Hfk16UMAg==; expires=Tue, 01-Nov-22 12:56:47 GMT; domain=58.com; path=/"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"CUR ADM OUR NOR STA NID\""
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.2.3"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:50 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "last-modified": "Tue, 16 Oct 2012 03:32:08 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Thu, 18 Oct 2012 03:17:25 GMT"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "11545"
+        },
+        {
+          "last-modified": "Mon, 25 Apr 2011 09:08:36 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "1"
+        },
+        {
+          "x-via": "1.1 gm240:8107 (Cdn Cache Server V2.0), 1.1 stcz158:8104 (Cdn Cache Server V2.0), 1.1 gdyf16:8184 (Cdn Cache Server V2.0)"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:51 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "set-cookie": "TIEBAUID=cb23caae14130a0d384a57f1; expires=Thu, 31-Dec-2020 15:59:59 GMT; path=/; domain=tieba.baidu.com"
+        },
+        {
+          "location": "http://tieba.baidu.com/index.html"
+        },
+        {
+          "tracecode": "34115693691177833738110320"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.15"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:24 GMT"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "No-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "set-cookie": "JSESSIONID=24206446514B3C020AC50919B9330503; Path=/"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "W/\"4286-1337327987000\""
+        },
+        {
+          "last-modified": "Fri, 18 May 2012 07:59:47 GMT"
+        },
+        {
+          "content-length": "4286"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "JSP2/1.0.2"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:52 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "5352"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "etag": "\"3854054371452794933\""
+        },
+        {
+          "expires": "Fri, 01 Nov 2013 07:42:36 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 07:18:44 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:52 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "20557"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 01:59:00 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:52 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "etag": "\"1496242645\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Sun, 15 Aug 2010 16:00:00 GMT"
+        },
+        {
+          "expires": "Mon, 12 Sep 2022 12:56:52 GMT"
+        },
+        {
+          "cache-control": "max-age=311040000"
+        },
+        {
+          "content-length": "1574"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:52 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:52 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "13644"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 03:33:51 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:52 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:52 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "8122"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 06:49:50 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:52 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:51 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 11:28:20 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:52 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "19956"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 06:36:26 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:52 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 07:23:32 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4240"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "etag": "\"464442779\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Sun, 15 Aug 2010 16:00:00 GMT"
+        },
+        {
+          "expires": "Mon, 12 Sep 2022 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=311040000"
+        },
+        {
+          "content-length": "231"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "etag": "\"531551641\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Sun, 15 Aug 2010 16:00:00 GMT"
+        },
+        {
+          "expires": "Mon, 12 Sep 2022 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=311040000"
+        },
+        {
+          "content-length": "339"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "server": "ECOM Apache 1.0.13.0"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "text/html"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "7748"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 03:02:59 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:52 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "set-cookie": "TIEBAUID=cb23caae14130a0d384a57f1; expires=Thu, 31-Dec-2020 15:59:59 GMT; path=/; domain=tieba.baidu.com"
+        },
+        {
+          "location": "http://tieba.baidu.com/index.html"
+        },
+        {
+          "tracecode": "34115693691177833738110320"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-length": "29476"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 03:31:40 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:52 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 05:21:05 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "7301"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "7993"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 07:14:19 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "last-modified": "Thu, 14 Oct 2010 10:10:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Fri, 29 Oct 2010 06:30:56 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Wed, 14 Sep 2011 06:13:02 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:52 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "35637"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 04:22:40 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:52 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "set-cookie": "TIEBAUID=cb23caae14130a0d384a57f1; expires=Thu, 31-Dec-2020 15:59:59 GMT; path=/; domain=tieba.baidu.com"
+        },
+        {
+          "location": "http://tieba.baidu.com/index.html"
+        },
+        {
+          "tracecode": "34115693691177833738110320"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-length": "4997"
+        },
+        {
+          "last-modified": "Thu, 21 Jun 2012 03:57:37 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 29 Jun 2012 10:13:16 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2267"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "1480"
+        },
+        {
+          "last-modified": "Mon, 21 May 2012 07:58:06 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "8922"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 03:21:07 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "etag": "\"4172175030\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 10:48:27 GMT"
+        },
+        {
+          "content-length": "9393"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "server": "apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "9294"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:16:26 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:52 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "40299"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 04:07:00 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:52 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:36:44 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8579"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "18605"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:36:43 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "set-cookie": "TIEBAUID=cb23caae14130a0d384a57f1; expires=Thu, 31-Dec-2020 15:59:59 GMT; path=/; domain=tieba.baidu.com"
+        },
+        {
+          "location": "http://tieba.baidu.com/index.html"
+        },
+        {
+          "tracecode": "34115693691177833738110320"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-length": "20099"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:36:43 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "18865"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:36:44 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "20123"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:36:44 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "location": "http://msg.baidu.com/msg/msg_dataGetmsgCount?from=msg"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "206"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "17869"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:36:43 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "18072"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:36:44 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "21328"
+        },
+        {
+          "last-modified": "Wed, 19 Jan 2011 09:16:11 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "1481"
+        },
+        {
+          "last-modified": "Tue, 24 Jul 2012 03:59:23 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "220"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "etag": "\"3965408141\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 16 Oct 2012 06:54:58 GMT"
+        },
+        {
+          "expires": "Mon, 12 Sep 2022 12:56:52 GMT"
+        },
+        {
+          "cache-control": "max-age=311040000"
+        },
+        {
+          "content-length": "39993"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:52 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "11279"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:36:43 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:36:44 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "22384"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "set-cookie": "TIEBAUID=cb23caae14130a0d384a57f1; expires=Thu, 31-Dec-2020 15:59:59 GMT; path=/; domain=tieba.baidu.com"
+        },
+        {
+          "location": "http://tieba.baidu.com/index.html"
+        },
+        {
+          "tracecode": "34115693691177833738110320"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-length": "20962"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:36:43 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "61444"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 07:13:16 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:54 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "17732"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 09:16:23 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Fri, 26 Oct 2012 12:58:13 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:53 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:53 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 11 Oct 2011 07:38:09 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "999"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "3949"
+        },
+        {
+          "last-modified": "Mon, 27 Aug 2012 06:36:50 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "1158"
+        },
+        {
+          "last-modified": "Mon, 03 Sep 2012 03:47:57 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "1008"
+        },
+        {
+          "last-modified": "Mon, 27 Aug 2012 08:26:17 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Wed, 14 Sep 2011 06:13:06 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3059"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "text/html; charset=GBK"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "set-cookie": "TIEBA_USERTYPE=7a2a671a262b15b7e6f4819b; expires=Thu, 31-Dec-2020 15:59:59 GMT; path=/; domain=tieba.baidu.com"
+        },
+        {
+          "location": "http://tieba.baidu.com/index.html"
+        },
+        {
+          "tracecode": "34173872330388372234110320"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-length": "20962"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:36:43 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:54 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "text/html; charset=GBK"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "1158"
+        },
+        {
+          "last-modified": "Mon, 03 Sep 2012 03:47:57 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "tracecode": "34176809970478157322110320"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "607"
+        },
+        {
+          "last-modified": "Tue, 24 Jul 2012 03:59:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2921"
+        },
+        {
+          "last-modified": "Tue, 17 May 2011 06:39:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2832"
+        },
+        {
+          "last-modified": "Tue, 17 May 2011 06:39:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "3059"
+        },
+        {
+          "last-modified": "Tue, 17 May 2011 06:39:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2801"
+        },
+        {
+          "last-modified": "Tue, 17 May 2011 06:39:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2923"
+        },
+        {
+          "last-modified": "Wed, 14 Sep 2011 06:13:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "20220"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 07:56:05 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:58 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2830"
+        },
+        {
+          "last-modified": "Tue, 17 May 2011 06:39:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:58 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:58 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2877"
+        },
+        {
+          "last-modified": "Tue, 17 May 2011 06:39:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:58 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:58 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2967"
+        },
+        {
+          "last-modified": "Wed, 14 Sep 2011 06:13:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:58 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:58 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "177"
+        },
+        {
+          "last-modified": "Wed, 14 Sep 2011 06:13:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:58 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:58 GMT"
+        },
+        {
+          "content-type": "text/html; charset=GBK"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "1158"
+        },
+        {
+          "last-modified": "Mon, 03 Sep 2012 03:47:57 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:57 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "tracecode": "34180192590438703882110320"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:58 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "7067"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:58 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:58 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Wed, 14 Sep 2011 06:12:51 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:58 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "288"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:58 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "8236"
+        },
+        {
+          "last-modified": "Wed, 14 Sep 2011 06:13:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:58 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2842"
+        },
+        {
+          "last-modified": "Tue, 17 May 2011 06:39:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:59 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "etag": "\"4291424757\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Thu, 21 Jun 2012 08:04:46 GMT"
+        },
+        {
+          "expires": "Mon, 12 Sep 2022 12:56:59 GMT"
+        },
+        {
+          "cache-control": "max-age=311040000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3558"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "etag": "\"4232694198\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Thu, 21 Jun 2012 08:04:45 GMT"
+        },
+        {
+          "expires": "Mon, 12 Sep 2022 12:56:59 GMT"
+        },
+        {
+          "cache-control": "max-age=311040000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10901"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=44849399"
+        },
+        {
+          "expires": "Sun, 06 Apr 2014 15:06:58 GMT"
+        },
+        {
+          "last-modified": "Thu, 31 Dec 2009 08:37:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=40280084"
+        },
+        {
+          "expires": "Wed, 12 Feb 2014 17:51:43 GMT"
+        },
+        {
+          "last-modified": "Fri, 16 Apr 2010 03:07:31 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=12713623"
+        },
+        {
+          "expires": "Sat, 30 Mar 2013 16:30:42 GMT"
+        },
+        {
+          "last-modified": "Sat, 14 Jan 2012 05:49:33 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=33539862"
+        },
+        {
+          "expires": "Tue, 26 Nov 2013 17:34:41 GMT"
+        },
+        {
+          "last-modified": "Sun, 19 Sep 2010 03:41:35 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4405195"
+        },
+        {
+          "expires": "Mon, 24 Dec 2012 12:36:54 GMT"
+        },
+        {
+          "last-modified": "Tue, 24 Jul 2012 13:37:09 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "media": "media"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Fri, 26 Oct 2012 12:24:13 GMT"
+        },
+        {
+          "last-modified": "Sat, 25 Apr 2009 07:04:00 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:58 GMT"
+        },
+        {
+          "server": "apache"
+        },
+        {
+          "content-length": "27159"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "etag": "\"2269828500\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 24 Aug 2010 14:26:41 GMT"
+        },
+        {
+          "expires": "Mon, 12 Sep 2022 12:56:59 GMT"
+        },
+        {
+          "cache-control": "max-age=311040000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "571"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=25111771"
+        },
+        {
+          "expires": "Wed, 21 Aug 2013 04:26:30 GMT"
+        },
+        {
+          "last-modified": "Sat, 02 Apr 2011 05:57:56 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=9297121"
+        },
+        {
+          "expires": "Tue, 19 Feb 2013 03:29:00 GMT"
+        },
+        {
+          "last-modified": "Mon, 02 Apr 2012 07:52:56 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=41820075"
+        },
+        {
+          "expires": "Sun, 02 Mar 2014 13:38:14 GMT"
+        },
+        {
+          "last-modified": "Thu, 11 Mar 2010 11:34:29 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=10209204"
+        },
+        {
+          "expires": "Fri, 01 Mar 2013 16:50:23 GMT"
+        },
+        {
+          "last-modified": "Mon, 12 Mar 2012 05:10:10 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "etag": "eab7a0d6b87c3b4ed2c270c0380dbf29"
+        },
+        {
+          "cache-control": "max-age=0, must-revalidate"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "set-cookie": "HMACCOUNT=0F8500D919421ADB; Path=/; Domain=hm.baidu.com; Expires=Sun, 18 Jan 2038 00:00:00 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "5779"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=8515483"
+        },
+        {
+          "expires": "Sun, 10 Feb 2013 02:21:42 GMT"
+        },
+        {
+          "last-modified": "Fri, 20 Apr 2012 10:07:32 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, max-age=0, no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:00 GMT"
+        },
+        {
+          "server": "apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:00 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "1158"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:54:28 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:00 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "tracecode": "34180192590438703882110320"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:00 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "1158"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:54:28 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:00 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "tracecode": "34180192590438703882110320"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:00 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "3949"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 12:11:36 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:00 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "BAIDUID=53B0A4069A29BECD16EF519984CCA005:FG=1; max-age=946080000; expires=Mon, 27-Oct-42 12:57:02 GMT; domain=.baidu.com; path=/; version=1"
+        },
+        {
+          "p3p": "CP=\" OTI DSP COR IVA OUR IND COM \""
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "etag": "\"1698673572\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:49:58 GMT"
+        },
+        {
+          "expires": "Fri, 01 Feb 2013 12:57:02 GMT"
+        },
+        {
+          "cache-control": "max-age=7776000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "502"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:02 GMT"
+        },
+        {
+          "server": "apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "BAIDUID=53B0A4069A29BECDD09DC829CEEA31EE:FG=1; max-age=946080000; expires=Mon, 27-Oct-42 12:57:02 GMT; domain=.baidu.com; path=/; version=1"
+        },
+        {
+          "p3p": "CP=\" OTI DSP COR IVA OUR IND COM \""
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "etag": "\"2769205800\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:49:58 GMT"
+        },
+        {
+          "expires": "Fri, 01 Feb 2013 12:57:02 GMT"
+        },
+        {
+          "cache-control": "max-age=7776000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3712"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:02 GMT"
+        },
+        {
+          "server": "apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "etag": "\"1610142698\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Thu, 21 Jun 2012 08:04:47 GMT"
+        },
+        {
+          "expires": "Mon, 12 Sep 2022 12:56:59 GMT"
+        },
+        {
+          "cache-control": "max-age=311040000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "158920"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:56:59 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:04 GMT"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "2550"
+        },
+        {
+          "last-modified": "Tue, 30 Mar 2010 09:25:50 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:04 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "tracecode": "34180192590438703882110320"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:07 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Wed, 14 Sep 2011 06:11:42 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:56:58 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "etag": "\"1905870111\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Wed, 18 Nov 2009 09:44:09 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 12:57:07 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:07 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, max-age=0, no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:07 GMT"
+        },
+        {
+          "server": "apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 09:21:56 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "787"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "522"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "content-length": "2550"
+        },
+        {
+          "last-modified": "Tue, 24 Jul 2012 03:59:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=14920916"
+        },
+        {
+          "expires": "Thu, 25 Apr 2013 05:39:04 GMT"
+        },
+        {
+          "last-modified": "Thu, 24 Nov 2011 03:33:15 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=20272763"
+        },
+        {
+          "expires": "Wed, 26 Jun 2013 04:16:31 GMT"
+        },
+        {
+          "last-modified": "Sat, 23 Jul 2011 06:18:22 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3386843"
+        },
+        {
+          "expires": "Wed, 12 Dec 2012 17:44:31 GMT"
+        },
+        {
+          "last-modified": "Fri, 17 Aug 2012 03:22:22 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=319801"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 05:47:09 GMT"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 03:17:06 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "etag": "\"2813066485\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Thu, 12 Aug 2010 08:49:52 GMT"
+        },
+        {
+          "expires": "Mon, 12 Sep 2022 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=311040000"
+        },
+        {
+          "content-length": "1490"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4715"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5848"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "last-modified": "Thu, 29 Mar 2012 08:20:20 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1173183"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 02:50:11 GMT"
+        },
+        {
+          "last-modified": "Sun, 07 Oct 2012 09:11:02 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=131604"
+        },
+        {
+          "expires": "Mon, 05 Nov 2012 01:30:32 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 11:50:19 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=305486"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 01:48:34 GMT"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 11:14:16 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=10106809"
+        },
+        {
+          "expires": "Thu, 28 Feb 2013 12:23:57 GMT"
+        },
+        {
+          "last-modified": "Wed, 14 Mar 2012 14:03:29 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=11660411"
+        },
+        {
+          "expires": "Mon, 18 Mar 2013 11:57:19 GMT"
+        },
+        {
+          "last-modified": "Tue, 07 Feb 2012 14:56:45 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1356978"
+        },
+        {
+          "expires": "Mon, 19 Nov 2012 05:53:26 GMT"
+        },
+        {
+          "last-modified": "Wed, 03 Oct 2012 03:04:31 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=10204165"
+        },
+        {
+          "expires": "Fri, 01 Mar 2013 15:26:33 GMT"
+        },
+        {
+          "last-modified": "Mon, 12 Mar 2012 07:58:17 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=9726906"
+        },
+        {
+          "expires": "Sun, 24 Feb 2013 02:52:14 GMT"
+        },
+        {
+          "last-modified": "Fri, 23 Mar 2012 09:06:55 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2265452"
+        },
+        {
+          "expires": "Thu, 29 Nov 2012 18:14:40 GMT"
+        },
+        {
+          "last-modified": "Wed, 12 Sep 2012 02:22:04 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "8761"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5828"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "6430"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "6892"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "4639"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 10:10:35 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "853"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=6834284"
+        },
+        {
+          "expires": "Mon, 21 Jan 2013 15:21:52 GMT"
+        },
+        {
+          "last-modified": "Tue, 29 May 2012 08:07:39 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=8117075"
+        },
+        {
+          "expires": "Tue, 05 Feb 2013 11:41:43 GMT"
+        },
+        {
+          "last-modified": "Sun, 29 Apr 2012 15:27:57 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=6108255"
+        },
+        {
+          "expires": "Sun, 13 Jan 2013 05:41:23 GMT"
+        },
+        {
+          "last-modified": "Fri, 15 Jun 2012 03:28:38 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2157496"
+        },
+        {
+          "expires": "Wed, 28 Nov 2012 12:15:24 GMT"
+        },
+        {
+          "last-modified": "Fri, 14 Sep 2012 14:20:35 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=7406109"
+        },
+        {
+          "expires": "Mon, 28 Jan 2013 06:12:17 GMT"
+        },
+        {
+          "last-modified": "Wed, 16 May 2012 02:26:49 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=5979956"
+        },
+        {
+          "expires": "Fri, 11 Jan 2013 18:03:04 GMT"
+        },
+        {
+          "last-modified": "Mon, 18 Jun 2012 02:45:15 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=12913110"
+        },
+        {
+          "expires": "Mon, 01 Apr 2013 23:55:38 GMT"
+        },
+        {
+          "last-modified": "Mon, 09 Jan 2012 15:00:07 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 13:37:52 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "4639"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:08 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3673"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "834"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 10:10:35 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5828"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "586"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "581"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=5746593"
+        },
+        {
+          "expires": "Wed, 09 Jan 2013 01:13:42 GMT"
+        },
+        {
+          "last-modified": "Sat, 23 Jun 2012 12:24:03 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4420208"
+        },
+        {
+          "expires": "Mon, 24 Dec 2012 16:47:17 GMT"
+        },
+        {
+          "last-modified": "Tue, 24 Jul 2012 05:16:52 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "text/html; charset=GBK"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "2550"
+        },
+        {
+          "last-modified": "Tue, 30 Mar 2010 09:25:50 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:04 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "tracecode": "34277428450405149450110320"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "set-cookie": "wise_device=0; expires=Sun, 03-Nov-2013 12:57:07 GMT; path=/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=750933"
+        },
+        {
+          "expires": "Mon, 12 Nov 2012 05:32:42 GMT"
+        },
+        {
+          "last-modified": "Wed, 17 Oct 2012 03:46:02 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=31704989"
+        },
+        {
+          "expires": "Tue, 05 Nov 2013 11:53:37 GMT"
+        },
+        {
+          "last-modified": "Sun, 31 Oct 2010 15:04:09 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2436757"
+        },
+        {
+          "expires": "Sat, 01 Dec 2012 17:49:46 GMT"
+        },
+        {
+          "last-modified": "Sat, 08 Sep 2012 03:11:54 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=108530"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 19:05:59 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 00:39:29 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=519223"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 13:10:52 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 12:29:43 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3860382"
+        },
+        {
+          "expires": "Tue, 18 Dec 2012 05:16:51 GMT"
+        },
+        {
+          "last-modified": "Mon, 06 Aug 2012 04:17:45 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 09:21:56 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "2820"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "618"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "572"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=9498052"
+        },
+        {
+          "expires": "Thu, 21 Feb 2013 11:18:01 GMT"
+        },
+        {
+          "last-modified": "Wed, 28 Mar 2012 16:15:24 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4541952"
+        },
+        {
+          "expires": "Wed, 26 Dec 2012 02:36:21 GMT"
+        },
+        {
+          "last-modified": "Sat, 21 Jul 2012 09:38:45 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4854455"
+        },
+        {
+          "expires": "Sat, 29 Dec 2012 17:24:44 GMT"
+        },
+        {
+          "last-modified": "Sat, 14 Jul 2012 04:01:58 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=217395"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 01:20:24 GMT"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 12:10:39 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=606214"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 13:20:43 GMT"
+        },
+        {
+          "last-modified": "Sat, 20 Oct 2012 12:10:00 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=10024120"
+        },
+        {
+          "expires": "Wed, 27 Feb 2013 13:25:49 GMT"
+        },
+        {
+          "last-modified": "Fri, 16 Mar 2012 11:59:49 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4573190"
+        },
+        {
+          "expires": "Wed, 26 Dec 2012 11:16:59 GMT"
+        },
+        {
+          "last-modified": "Fri, 20 Jul 2012 16:17:28 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=8773628"
+        },
+        {
+          "expires": "Wed, 13 Feb 2013 02:04:17 GMT"
+        },
+        {
+          "last-modified": "Sat, 14 Apr 2012 10:42:52 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "10566"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "840"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "10010"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "5929"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1970"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2878"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 13:37:52 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2367"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 13:37:52 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "138"
+        },
+        {
+          "last-modified": "Tue, 24 Jul 2012 03:59:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "248"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "340"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "522"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1284"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "142"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2867849"
+        },
+        {
+          "expires": "Thu, 06 Dec 2012 17:34:38 GMT"
+        },
+        {
+          "last-modified": "Wed, 29 Aug 2012 03:42:11 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=742724"
+        },
+        {
+          "expires": "Mon, 12 Nov 2012 03:15:53 GMT"
+        },
+        {
+          "last-modified": "Wed, 17 Oct 2012 08:19:41 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1777991"
+        },
+        {
+          "expires": "Sat, 24 Nov 2012 02:50:20 GMT"
+        },
+        {
+          "last-modified": "Sun, 23 Sep 2012 09:10:47 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=962398"
+        },
+        {
+          "expires": "Wed, 14 Nov 2012 16:17:07 GMT"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 06:17:12 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=14125794"
+        },
+        {
+          "expires": "Tue, 16 Apr 2013 00:47:03 GMT"
+        },
+        {
+          "last-modified": "Mon, 12 Dec 2011 13:17:20 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "954"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=431506"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 12:48:55 GMT"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 13:13:37 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=11487816"
+        },
+        {
+          "expires": "Sat, 16 Mar 2013 12:00:45 GMT"
+        },
+        {
+          "last-modified": "Sat, 11 Feb 2012 14:49:57 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2738958"
+        },
+        {
+          "expires": "Wed, 05 Dec 2012 05:46:27 GMT"
+        },
+        {
+          "last-modified": "Sat, 01 Sep 2012 03:18:32 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=5566505"
+        },
+        {
+          "expires": "Sun, 06 Jan 2013 23:12:14 GMT"
+        },
+        {
+          "last-modified": "Wed, 27 Jun 2012 16:26:59 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "3063"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 10:10:36 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "539"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "17226"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "833"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "820"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:10 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Wed, 14 Sep 2011 06:12:46 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4911"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1394"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:10 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "5772"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10954"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "994"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:10 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=54046405"
+        },
+        {
+          "expires": "Tue, 22 Jul 2014 01:50:34 GMT"
+        },
+        {
+          "last-modified": "Mon, 01 Jun 2009 11:10:18 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=707871"
+        },
+        {
+          "expires": "Sun, 11 Nov 2012 17:35:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 18 Oct 2012 03:41:27 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=362012"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 17:30:42 GMT"
+        },
+        {
+          "last-modified": "Fri, 26 Oct 2012 03:50:06 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=18793623"
+        },
+        {
+          "expires": "Sun, 09 Jun 2013 01:24:13 GMT"
+        },
+        {
+          "last-modified": "Fri, 26 Aug 2011 12:03:04 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=430516"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 12:32:26 GMT"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 13:46:37 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=32880607"
+        },
+        {
+          "expires": "Tue, 19 Nov 2013 02:27:17 GMT"
+        },
+        {
+          "last-modified": "Mon, 04 Oct 2010 09:56:55 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3367264"
+        },
+        {
+          "expires": "Wed, 12 Dec 2012 12:18:14 GMT"
+        },
+        {
+          "last-modified": "Fri, 17 Aug 2012 14:15:02 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3543896"
+        },
+        {
+          "expires": "Fri, 14 Dec 2012 13:22:06 GMT"
+        },
+        {
+          "last-modified": "Mon, 13 Aug 2012 12:07:18 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "2725"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:10 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:54:28 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "4823"
+        },
+        {
+          "last-modified": "Wed, 14 Sep 2011 06:12:46 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:10 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "598"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "4344"
+        },
+        {
+          "last-modified": "Fri, 09 Sep 2011 07:29:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:10 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=920631"
+        },
+        {
+          "expires": "Wed, 14 Nov 2012 04:41:01 GMT"
+        },
+        {
+          "last-modified": "Sat, 13 Oct 2012 05:29:27 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=10801080"
+        },
+        {
+          "expires": "Fri, 08 Mar 2013 13:15:10 GMT"
+        },
+        {
+          "last-modified": "Mon, 27 Feb 2012 12:21:10 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=19349568"
+        },
+        {
+          "expires": "Sat, 15 Jun 2013 11:49:58 GMT"
+        },
+        {
+          "last-modified": "Sat, 13 Aug 2011 15:11:34 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2597506"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 14:28:56 GMT"
+        },
+        {
+          "last-modified": "Tue, 04 Sep 2012 09:53:37 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=27218873"
+        },
+        {
+          "expires": "Sat, 14 Sep 2013 13:45:03 GMT"
+        },
+        {
+          "last-modified": "Sat, 12 Feb 2011 11:21:23 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3377775"
+        },
+        {
+          "expires": "Wed, 12 Dec 2012 15:13:25 GMT"
+        },
+        {
+          "last-modified": "Fri, 17 Aug 2012 08:24:39 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4247008"
+        },
+        {
+          "expires": "Sat, 22 Dec 2012 16:40:38 GMT"
+        },
+        {
+          "last-modified": "Sat, 28 Jul 2012 05:30:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1815543"
+        },
+        {
+          "expires": "Sat, 24 Nov 2012 13:16:13 GMT"
+        },
+        {
+          "last-modified": "Sat, 22 Sep 2012 12:19:04 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=270710"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 16:09:00 GMT"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 06:33:30 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1790915"
+        },
+        {
+          "expires": "Sat, 24 Nov 2012 06:25:45 GMT"
+        },
+        {
+          "last-modified": "Sun, 23 Sep 2012 02:00:00 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=14218"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 16:54:08 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:03:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3723118"
+        },
+        {
+          "expires": "Sun, 16 Dec 2012 15:09:08 GMT"
+        },
+        {
+          "last-modified": "Thu, 09 Aug 2012 08:33:13 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4635533"
+        },
+        {
+          "expires": "Thu, 27 Dec 2012 04:36:03 GMT"
+        },
+        {
+          "last-modified": "Thu, 19 Jul 2012 05:39:24 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=12138710"
+        },
+        {
+          "expires": "Sun, 24 Mar 2013 00:49:00 GMT"
+        },
+        {
+          "last-modified": "Fri, 27 Jan 2012 13:13:30 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=19371229"
+        },
+        {
+          "expires": "Sat, 15 Jun 2013 17:50:59 GMT"
+        },
+        {
+          "last-modified": "Sat, 13 Aug 2011 03:09:31 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3202347"
+        },
+        {
+          "expires": "Mon, 10 Dec 2012 14:29:37 GMT"
+        },
+        {
+          "last-modified": "Tue, 21 Aug 2012 09:52:15 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3678437"
+        },
+        {
+          "expires": "Sun, 16 Dec 2012 02:44:27 GMT"
+        },
+        {
+          "last-modified": "Fri, 10 Aug 2012 09:22:36 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1085523"
+        },
+        {
+          "expires": "Fri, 16 Nov 2012 02:29:13 GMT"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 09:53:04 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=51122420"
+        },
+        {
+          "expires": "Wed, 18 Jun 2014 05:37:30 GMT"
+        },
+        {
+          "last-modified": "Sat, 08 Aug 2009 03:36:30 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 13:37:52 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4381984"
+        },
+        {
+          "expires": "Mon, 24 Dec 2012 06:10:14 GMT"
+        },
+        {
+          "last-modified": "Wed, 25 Jul 2012 02:31:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=7305155"
+        },
+        {
+          "expires": "Sun, 27 Jan 2013 02:09:45 GMT"
+        },
+        {
+          "last-modified": "Fri, 18 May 2012 10:32:00 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=13340027"
+        },
+        {
+          "expires": "Sat, 06 Apr 2013 22:30:57 GMT"
+        },
+        {
+          "last-modified": "Fri, 30 Dec 2011 17:49:36 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3517984"
+        },
+        {
+          "expires": "Fri, 14 Dec 2012 06:10:14 GMT"
+        },
+        {
+          "last-modified": "Tue, 14 Aug 2012 02:31:02 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4872542"
+        },
+        {
+          "expires": "Sat, 29 Dec 2012 22:26:12 GMT"
+        },
+        {
+          "last-modified": "Fri, 13 Jul 2012 17:59:05 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "48731"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1855967"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 00:29:57 GMT"
+        },
+        {
+          "last-modified": "Fri, 21 Sep 2012 13:51:36 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1357261"
+        },
+        {
+          "expires": "Mon, 19 Nov 2012 05:58:11 GMT"
+        },
+        {
+          "last-modified": "Wed, 03 Oct 2012 02:55:08 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=12271010"
+        },
+        {
+          "expires": "Mon, 25 Mar 2013 13:34:00 GMT"
+        },
+        {
+          "last-modified": "Tue, 24 Jan 2012 11:43:29 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1069555"
+        },
+        {
+          "expires": "Thu, 15 Nov 2012 22:03:05 GMT"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 18:45:20 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3114138"
+        },
+        {
+          "expires": "Sun, 09 Dec 2012 13:59:29 GMT"
+        },
+        {
+          "last-modified": "Thu, 23 Aug 2012 10:52:35 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=15254111"
+        },
+        {
+          "expires": "Mon, 29 Apr 2013 02:12:22 GMT"
+        },
+        {
+          "last-modified": "Wed, 16 Nov 2011 10:26:48 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:10 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "6834"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:10 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1956253"
+        },
+        {
+          "expires": "Mon, 26 Nov 2012 04:21:24 GMT"
+        },
+        {
+          "last-modified": "Wed, 19 Sep 2012 06:08:44 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3164696"
+        },
+        {
+          "expires": "Mon, 10 Dec 2012 04:02:07 GMT"
+        },
+        {
+          "last-modified": "Wed, 22 Aug 2012 06:47:19 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=15466892"
+        },
+        {
+          "expires": "Wed, 01 May 2013 13:18:43 GMT"
+        },
+        {
+          "last-modified": "Fri, 11 Nov 2011 12:14:06 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=56032"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:31:03 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 05:49:26 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=7623169"
+        },
+        {
+          "expires": "Wed, 30 Jan 2013 18:30:00 GMT"
+        },
+        {
+          "last-modified": "Fri, 11 May 2012 01:51:32 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=12924665"
+        },
+        {
+          "expires": "Tue, 02 Apr 2013 03:08:16 GMT"
+        },
+        {
+          "last-modified": "Mon, 09 Jan 2012 08:35:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2176101"
+        },
+        {
+          "expires": "Wed, 28 Nov 2012 17:25:32 GMT"
+        },
+        {
+          "last-modified": "Fri, 14 Sep 2012 04:00:28 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=14007174"
+        },
+        {
+          "expires": "Sun, 14 Apr 2013 15:50:05 GMT"
+        },
+        {
+          "last-modified": "Thu, 15 Dec 2011 07:11:23 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1438855"
+        },
+        {
+          "expires": "Tue, 20 Nov 2012 04:38:06 GMT"
+        },
+        {
+          "last-modified": "Mon, 01 Oct 2012 05:35:21 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=6323658"
+        },
+        {
+          "expires": "Tue, 15 Jan 2013 17:31:29 GMT"
+        },
+        {
+          "last-modified": "Sun, 10 Jun 2012 03:48:34 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=41611"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:30:42 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 13:50:08 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=25072164"
+        },
+        {
+          "expires": "Tue, 20 Aug 2013 17:26:35 GMT"
+        },
+        {
+          "last-modified": "Sun, 03 Apr 2011 03:58:23 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=13004317"
+        },
+        {
+          "expires": "Wed, 03 Apr 2013 01:15:48 GMT"
+        },
+        {
+          "last-modified": "Sat, 07 Jan 2012 12:19:57 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=778015"
+        },
+        {
+          "expires": "Mon, 12 Nov 2012 13:04:06 GMT"
+        },
+        {
+          "last-modified": "Tue, 16 Oct 2012 12:43:21 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1778334"
+        },
+        {
+          "expires": "Sat, 24 Nov 2012 02:56:05 GMT"
+        },
+        {
+          "last-modified": "Sun, 23 Sep 2012 08:59:23 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=5531525"
+        },
+        {
+          "expires": "Sun, 06 Jan 2013 13:29:16 GMT"
+        },
+        {
+          "last-modified": "Thu, 28 Jun 2012 11:53:00 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2468675"
+        },
+        {
+          "expires": "Sun, 02 Dec 2012 02:41:46 GMT"
+        },
+        {
+          "last-modified": "Fri, 07 Sep 2012 09:28:00 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1223853"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 16:54:44 GMT"
+        },
+        {
+          "last-modified": "Sat, 06 Oct 2012 05:02:04 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3496295"
+        },
+        {
+          "expires": "Fri, 14 Dec 2012 00:08:46 GMT"
+        },
+        {
+          "last-modified": "Tue, 14 Aug 2012 14:34:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:09 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:09 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=20150086"
+        },
+        {
+          "expires": "Mon, 24 Jun 2013 18:11:57 GMT"
+        },
+        {
+          "last-modified": "Tue, 26 Jul 2011 02:27:39 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:11 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=5057999"
+        },
+        {
+          "expires": "Tue, 01 Jan 2013 01:57:10 GMT"
+        },
+        {
+          "last-modified": "Mon, 09 Jul 2012 10:57:13 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2613405"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 18:53:57 GMT"
+        },
+        {
+          "last-modified": "Tue, 04 Sep 2012 01:03:41 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4365433"
+        },
+        {
+          "expires": "Mon, 24 Dec 2012 01:34:25 GMT"
+        },
+        {
+          "last-modified": "Wed, 25 Jul 2012 11:42:45 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=19834480"
+        },
+        {
+          "expires": "Fri, 21 Jun 2013 02:31:52 GMT"
+        },
+        {
+          "last-modified": "Tue, 02 Aug 2011 09:47:52 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=11268"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 16:05:00 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 06:41:35 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=5616267"
+        },
+        {
+          "expires": "Mon, 07 Jan 2013 13:01:39 GMT"
+        },
+        {
+          "last-modified": "Tue, 26 Jun 2012 12:48:17 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=7263532"
+        },
+        {
+          "expires": "Sat, 26 Jan 2013 14:36:04 GMT"
+        },
+        {
+          "last-modified": "Sat, 19 May 2012 09:39:27 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "JSP2/1.0.2"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:08 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "94544"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "etag": "\"1031174008914679718\""
+        },
+        {
+          "expires": "Tue, 20 Aug 2013 13:41:19 GMT"
+        },
+        {
+          "last-modified": "Sun, 08 Jul 2012 14:17:09 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "723"
+        },
+        {
+          "last-modified": "Tue, 24 Jul 2012 03:59:23 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "tracecode": "34277428450405149450110320"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "set-cookie": "wise_device=0; expires=Sun, 03-Nov-2013 12:57:07 GMT; path=/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "3728"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "987"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "3108"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:25:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 09:07:40 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "598"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "717"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 13:37:52 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "973"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "386"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Wed, 25 Jul 2012 07:00:44 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-length": "584"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "last-modified": "Wed, 30 May 2012 03:14:35 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "429"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "2725"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "last-modified": "Thu, 14 Oct 2010 10:10:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:10 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1633"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "733"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1647"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 10:49:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1217631"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 15:11:03 GMT"
+        },
+        {
+          "last-modified": "Sat, 06 Oct 2012 08:29:30 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4935379"
+        },
+        {
+          "expires": "Sun, 30 Dec 2012 15:53:31 GMT"
+        },
+        {
+          "last-modified": "Thu, 12 Jul 2012 07:04:33 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=94090"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 15:05:22 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 08:40:52 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=268205"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 15:27:17 GMT"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 07:57:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3501790"
+        },
+        {
+          "expires": "Fri, 14 Dec 2012 01:40:22 GMT"
+        },
+        {
+          "last-modified": "Tue, 14 Aug 2012 11:30:52 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1089367"
+        },
+        {
+          "expires": "Fri, 16 Nov 2012 03:33:19 GMT"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 07:44:57 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=5175917"
+        },
+        {
+          "expires": "Wed, 02 Jan 2013 10:42:29 GMT"
+        },
+        {
+          "last-modified": "Fri, 06 Jul 2012 17:26:38 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2826947"
+        },
+        {
+          "expires": "Thu, 06 Dec 2012 06:12:59 GMT"
+        },
+        {
+          "last-modified": "Thu, 30 Aug 2012 02:25:38 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 13:37:52 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "560"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2850"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:31:14 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1196"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "1804"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:09:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1320"
+        },
+        {
+          "last-modified": "Wed, 30 May 2012 03:19:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Wed, 30 May 2012 03:19:18 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1990"
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=14263076"
+        },
+        {
+          "expires": "Wed, 17 Apr 2013 14:55:08 GMT"
+        },
+        {
+          "last-modified": "Fri, 09 Dec 2011 09:01:20 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2086351"
+        },
+        {
+          "expires": "Tue, 27 Nov 2012 16:29:43 GMT"
+        },
+        {
+          "last-modified": "Sun, 16 Sep 2012 05:52:10 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=6392807"
+        },
+        {
+          "expires": "Wed, 16 Jan 2013 12:43:59 GMT"
+        },
+        {
+          "last-modified": "Fri, 08 Jun 2012 13:23:38 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4373528"
+        },
+        {
+          "expires": "Mon, 24 Dec 2012 03:49:20 GMT"
+        },
+        {
+          "last-modified": "Wed, 25 Jul 2012 07:12:56 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=960053"
+        },
+        {
+          "expires": "Wed, 14 Nov 2012 15:38:05 GMT"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 07:35:25 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1446556"
+        },
+        {
+          "expires": "Tue, 20 Nov 2012 06:46:28 GMT"
+        },
+        {
+          "last-modified": "Mon, 01 Oct 2012 01:18:39 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=13730089"
+        },
+        {
+          "expires": "Thu, 11 Apr 2013 10:52:01 GMT"
+        },
+        {
+          "last-modified": "Wed, 21 Dec 2011 17:07:33 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4447089"
+        },
+        {
+          "expires": "Tue, 25 Dec 2012 00:15:21 GMT"
+        },
+        {
+          "last-modified": "Mon, 23 Jul 2012 14:20:53 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1048930"
+        },
+        {
+          "expires": "Thu, 15 Nov 2012 16:19:22 GMT"
+        },
+        {
+          "last-modified": "Wed, 10 Oct 2012 06:12:51 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "etag": "\"4013610198\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Wed, 20 Jun 2012 16:39:57 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1828"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=23765568"
+        },
+        {
+          "expires": "Mon, 05 Aug 2013 14:30:00 GMT"
+        },
+        {
+          "last-modified": "Tue, 03 May 2011 09:51:35 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "9466"
+        },
+        {
+          "last-modified": "Wed, 25 Jul 2012 06:58:47 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2736291"
+        },
+        {
+          "expires": "Wed, 05 Dec 2012 05:02:03 GMT"
+        },
+        {
+          "last-modified": "Sat, 01 Sep 2012 04:47:30 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=7202517"
+        },
+        {
+          "expires": "Fri, 25 Jan 2013 21:39:09 GMT"
+        },
+        {
+          "last-modified": "Sun, 20 May 2012 19:33:18 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=962816"
+        },
+        {
+          "expires": "Wed, 14 Nov 2012 16:24:09 GMT"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 06:03:21 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=28898351"
+        },
+        {
+          "expires": "Fri, 04 Oct 2013 00:16:24 GMT"
+        },
+        {
+          "last-modified": "Tue, 04 Jan 2011 14:18:50 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=26910239"
+        },
+        {
+          "expires": "Wed, 11 Sep 2013 00:01:12 GMT"
+        },
+        {
+          "last-modified": "Sat, 19 Feb 2011 14:49:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=314449"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 04:18:02 GMT"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 06:15:34 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=17303680"
+        },
+        {
+          "expires": "Wed, 22 May 2013 19:31:53 GMT"
+        },
+        {
+          "last-modified": "Thu, 29 Sep 2011 23:47:53 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=8742316"
+        },
+        {
+          "expires": "Tue, 12 Feb 2013 17:22:29 GMT"
+        },
+        {
+          "last-modified": "Sun, 15 Apr 2012 04:06:41 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=951041"
+        },
+        {
+          "expires": "Wed, 14 Nov 2012 13:07:54 GMT"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 12:35:50 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=12357995"
+        },
+        {
+          "expires": "Tue, 26 Mar 2013 13:43:48 GMT"
+        },
+        {
+          "last-modified": "Sun, 22 Jan 2012 11:24:02 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-length": "30289"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:14:30 GMT"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "tracecode": "34277428450405149450110320"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "set-cookie": "wise_device=0; expires=Sun, 03-Nov-2013 12:57:07 GMT; path=/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=6924353"
+        },
+        {
+          "expires": "Tue, 22 Jan 2013 16:23:06 GMT"
+        },
+        {
+          "last-modified": "Sun, 27 May 2012 06:05:27 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=35998348"
+        },
+        {
+          "expires": "Wed, 25 Dec 2013 04:29:41 GMT"
+        },
+        {
+          "last-modified": "Sat, 24 Jul 2010 05:52:16 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=403161"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 04:56:34 GMT"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 04:58:30 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=11795987"
+        },
+        {
+          "expires": "Wed, 20 Mar 2013 01:37:00 GMT"
+        },
+        {
+          "last-modified": "Sat, 04 Feb 2012 11:37:39 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4466678"
+        },
+        {
+          "expires": "Tue, 25 Dec 2012 05:41:51 GMT"
+        },
+        {
+          "last-modified": "Mon, 23 Jul 2012 03:27:57 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4380824"
+        },
+        {
+          "expires": "Mon, 24 Dec 2012 05:50:57 GMT"
+        },
+        {
+          "last-modified": "Wed, 25 Jul 2012 03:09:45 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4370018"
+        },
+        {
+          "expires": "Mon, 24 Dec 2012 02:50:51 GMT"
+        },
+        {
+          "last-modified": "Wed, 25 Jul 2012 09:09:57 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=22046695"
+        },
+        {
+          "expires": "Tue, 16 Jul 2013 17:02:08 GMT"
+        },
+        {
+          "last-modified": "Sun, 12 Jun 2011 04:47:22 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "etag": "\"1795935374\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Thu, 20 Sep 2012 06:30:31 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "490"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1813409"
+        },
+        {
+          "expires": "Sat, 24 Nov 2012 12:40:42 GMT"
+        },
+        {
+          "last-modified": "Sat, 22 Sep 2012 13:30:14 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2381150"
+        },
+        {
+          "expires": "Sat, 01 Dec 2012 02:23:03 GMT"
+        },
+        {
+          "last-modified": "Sun, 09 Sep 2012 10:05:33 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=12085408"
+        },
+        {
+          "expires": "Sat, 23 Mar 2013 10:00:41 GMT"
+        },
+        {
+          "last-modified": "Sat, 28 Jan 2012 18:50:17 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=7660325"
+        },
+        {
+          "expires": "Thu, 31 Jan 2013 04:49:18 GMT"
+        },
+        {
+          "last-modified": "Thu, 10 May 2012 05:13:02 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=20831903"
+        },
+        {
+          "expires": "Tue, 02 Jul 2013 15:35:36 GMT"
+        },
+        {
+          "last-modified": "Sun, 10 Jul 2011 07:40:26 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:12 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "42293"
+        },
+        {
+          "last-modified": "Wed, 30 May 2012 03:19:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:57:12 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=13227345"
+        },
+        {
+          "expires": "Fri, 05 Apr 2013 15:12:58 GMT"
+        },
+        {
+          "last-modified": "Mon, 02 Jan 2012 08:25:43 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=5658505"
+        },
+        {
+          "expires": "Tue, 08 Jan 2013 00:45:38 GMT"
+        },
+        {
+          "last-modified": "Mon, 25 Jun 2012 13:20:23 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1397477"
+        },
+        {
+          "expires": "Mon, 19 Nov 2012 17:08:30 GMT"
+        },
+        {
+          "last-modified": "Tue, 02 Oct 2012 04:34:38 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3108210"
+        },
+        {
+          "expires": "Sun, 09 Dec 2012 12:20:43 GMT"
+        },
+        {
+          "last-modified": "Thu, 23 Aug 2012 14:10:13 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1080824"
+        },
+        {
+          "expires": "Fri, 16 Nov 2012 01:10:57 GMT"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 12:29:45 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=10544861"
+        },
+        {
+          "expires": "Tue, 05 Mar 2013 14:04:54 GMT"
+        },
+        {
+          "last-modified": "Sun, 04 Mar 2012 10:41:50 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=9336177"
+        },
+        {
+          "expires": "Tue, 19 Feb 2013 14:20:10 GMT"
+        },
+        {
+          "last-modified": "Sun, 01 Apr 2012 10:11:18 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=947069"
+        },
+        {
+          "expires": "Wed, 14 Nov 2012 12:01:43 GMT"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 14:48:15 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1473803"
+        },
+        {
+          "expires": "Tue, 20 Nov 2012 14:20:37 GMT"
+        },
+        {
+          "last-modified": "Sun, 30 Sep 2012 10:10:28 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1395562"
+        },
+        {
+          "expires": "Mon, 19 Nov 2012 16:36:36 GMT"
+        },
+        {
+          "last-modified": "Tue, 02 Oct 2012 05:38:29 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4082149"
+        },
+        {
+          "expires": "Thu, 20 Dec 2012 18:53:02 GMT"
+        },
+        {
+          "last-modified": "Wed, 01 Aug 2012 01:05:35 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4020129"
+        },
+        {
+          "expires": "Thu, 20 Dec 2012 01:39:23 GMT"
+        },
+        {
+          "last-modified": "Thu, 02 Aug 2012 11:32:55 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=30533221"
+        },
+        {
+          "expires": "Tue, 22 Oct 2013 22:24:15 GMT"
+        },
+        {
+          "last-modified": "Sat, 27 Nov 2010 18:03:12 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=9250886"
+        },
+        {
+          "expires": "Mon, 18 Feb 2013 14:38:40 GMT"
+        },
+        {
+          "last-modified": "Tue, 03 Apr 2012 09:34:21 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=7796153"
+        },
+        {
+          "expires": "Fri, 01 Feb 2013 18:33:07 GMT"
+        },
+        {
+          "last-modified": "Mon, 07 May 2012 01:45:28 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=12403487"
+        },
+        {
+          "expires": "Wed, 27 Mar 2013 02:22:01 GMT"
+        },
+        {
+          "last-modified": "Sat, 21 Jan 2012 10:07:40 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=342416"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 12:04:10 GMT"
+        },
+        {
+          "last-modified": "Fri, 26 Oct 2012 14:43:22 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=13044961"
+        },
+        {
+          "expires": "Wed, 03 Apr 2013 12:33:15 GMT"
+        },
+        {
+          "last-modified": "Fri, 06 Jan 2012 13:45:12 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=90589"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 14:07:03 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 10:37:35 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=13128536"
+        },
+        {
+          "expires": "Thu, 04 Apr 2013 11:46:10 GMT"
+        },
+        {
+          "last-modified": "Wed, 04 Jan 2012 15:19:22 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3557238"
+        },
+        {
+          "expires": "Fri, 14 Dec 2012 17:04:32 GMT"
+        },
+        {
+          "last-modified": "Mon, 13 Aug 2012 04:42:37 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=2348339"
+        },
+        {
+          "expires": "Fri, 30 Nov 2012 17:16:13 GMT"
+        },
+        {
+          "last-modified": "Mon, 10 Sep 2012 04:19:15 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=303065"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 01:08:19 GMT"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 12:35:04 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3713169"
+        },
+        {
+          "expires": "Sun, 16 Dec 2012 12:23:23 GMT"
+        },
+        {
+          "last-modified": "Thu, 09 Aug 2012 14:04:55 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3556632"
+        },
+        {
+          "expires": "Fri, 14 Dec 2012 16:54:26 GMT"
+        },
+        {
+          "last-modified": "Mon, 13 Aug 2012 05:02:50 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4793201"
+        },
+        {
+          "expires": "Sat, 29 Dec 2012 00:23:55 GMT"
+        },
+        {
+          "last-modified": "Sun, 15 Jul 2012 14:03:52 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=488079"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 04:31:53 GMT"
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 05:47:55 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=12998284"
+        },
+        {
+          "expires": "Tue, 02 Apr 2013 23:35:18 GMT"
+        },
+        {
+          "last-modified": "Sat, 07 Jan 2012 15:41:05 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=38194284"
+        },
+        {
+          "expires": "Sun, 19 Jan 2014 14:28:38 GMT"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 09:54:26 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:13 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "17574"
+        },
+        {
+          "last-modified": "Fri, 26 Oct 2012 03:00:00 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:57:13 GMT"
+        },
+        {
+          "cache-control": "max-age=3600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1010448"
+        },
+        {
+          "expires": "Thu, 15 Nov 2012 05:38:02 GMT"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 03:35:37 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=573690"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:18:44 GMT"
+        },
+        {
+          "last-modified": "Sun, 21 Oct 2012 06:14:13 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=9690556"
+        },
+        {
+          "expires": "Sat, 23 Feb 2013 16:46:30 GMT"
+        },
+        {
+          "last-modified": "Sat, 24 Mar 2012 05:18:41 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=20727056"
+        },
+        {
+          "expires": "Mon, 01 Jul 2013 10:28:10 GMT"
+        },
+        {
+          "last-modified": "Tue, 12 Jul 2011 17:55:22 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=5867076"
+        },
+        {
+          "expires": "Thu, 10 Jan 2013 10:41:50 GMT"
+        },
+        {
+          "last-modified": "Wed, 20 Jun 2012 17:28:01 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1938815"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:30:49 GMT"
+        },
+        {
+          "last-modified": "Wed, 19 Sep 2012 15:50:04 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:14 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=25417039"
+        },
+        {
+          "expires": "Sat, 24 Aug 2013 17:14:33 GMT"
+        },
+        {
+          "last-modified": "Sat, 26 Mar 2011 04:22:35 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1258204"
+        },
+        {
+          "expires": "Sun, 18 Nov 2012 02:27:19 GMT"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 09:57:06 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1855684"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 00:25:19 GMT"
+        },
+        {
+          "last-modified": "Fri, 21 Sep 2012 14:01:06 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=6489659"
+        },
+        {
+          "expires": "Thu, 17 Jan 2013 15:38:14 GMT"
+        },
+        {
+          "last-modified": "Wed, 06 Jun 2012 07:35:17 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4972705"
+        },
+        {
+          "expires": "Mon, 31 Dec 2012 02:15:40 GMT"
+        },
+        {
+          "last-modified": "Wed, 11 Jul 2012 10:20:25 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4075979"
+        },
+        {
+          "expires": "Thu, 20 Dec 2012 17:10:14 GMT"
+        },
+        {
+          "last-modified": "Wed, 01 Aug 2012 04:31:16 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        },
+        {
+          "content-length": "16"
+        },
+        {
+          "content-type": "text/html;charset=gbk"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "set-cookie": "BDRCVFR[RQbEFtOPS6t]=mbxnW11j9Dfmh7GuZR8mvqV; path=/; domain=rp.baidu.com"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        },
+        {
+          "content-length": "16"
+        },
+        {
+          "content-type": "text/html;charset=gbk"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "set-cookie": "BDRCVFR[74hAi0as9Oc]=mbxnW11j9Dfmh7GuZR8mvqV; path=/; domain=rp.baidu.com"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        },
+        {
+          "content-length": "16"
+        },
+        {
+          "content-type": "text/html;charset=gbk"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "set-cookie": "BDRCVFR[INlq_Cf3RCm]=mbxnW11j9Dfmh7GuZR8mvqV; path=/; domain=rp.baidu.com"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "BWS/1.0"
+        },
+        {
+          "content-length": "16"
+        },
+        {
+          "content-type": "text/html;charset=gbk"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "set-cookie": "BDRCVFR[wqXIM55hsyY]=mbxnW11j9Dfmh7GuZR8mvqV; path=/; domain=rp.baidu.com"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "17574"
+        },
+        {
+          "last-modified": "Sat Nov  3 20:57:15 2012"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 26 Jul 1997 05:00:00 GMT"
+        },
+        {
+          "cache-control": "post-check=0, pre-check=0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=433162"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 13:16:37 GMT"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 12:18:30 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=701481"
+        },
+        {
+          "expires": "Sun, 11 Nov 2012 15:48:36 GMT"
+        },
+        {
+          "last-modified": "Thu, 18 Oct 2012 07:14:33 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=473366"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 00:26:41 GMT"
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 13:58:23 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=5523078"
+        },
+        {
+          "expires": "Sun, 06 Jan 2013 11:08:33 GMT"
+        },
+        {
+          "last-modified": "Thu, 28 Jun 2012 16:34:39 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=16759229"
+        },
+        {
+          "expires": "Thu, 16 May 2013 12:17:44 GMT"
+        },
+        {
+          "last-modified": "Wed, 12 Oct 2011 14:16:17 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=4189499"
+        },
+        {
+          "expires": "Sat, 22 Dec 2012 00:42:14 GMT"
+        },
+        {
+          "last-modified": "Sun, 29 Jul 2012 13:27:17 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "JSP2/1.0.2"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "etag": "\"2607371477\""
+        },
+        {
+          "last-modified": "Tue, 18 Sep 2012 05:49:17 GMT"
+        },
+        {
+          "expires": "Sun, 17 Mar 2013 06:26:16 GMT"
+        },
+        {
+          "cache-control": "max-age=15552000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "JSP2/1.0.2"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "etag": "\"2063226227\""
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 10:39:26 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:09:38 GMT"
+        },
+        {
+          "cache-control": "max-age=1800"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=1345881"
+        },
+        {
+          "expires": "Mon, 19 Nov 2012 02:48:36 GMT"
+        },
+        {
+          "last-modified": "Wed, 03 Oct 2012 09:14:33 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=96063"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 15:38:18 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 07:35:08 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=3617766"
+        },
+        {
+          "expires": "Sat, 15 Dec 2012 09:53:21 GMT"
+        },
+        {
+          "last-modified": "Sat, 11 Aug 2012 19:05:03 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=11496580"
+        },
+        {
+          "expires": "Sat, 16 Mar 2013 14:26:55 GMT"
+        },
+        {
+          "last-modified": "Sat, 11 Feb 2012 09:57:55 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "server": "apache 1.1.26.0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=307195"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 02:17:10 GMT"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 10:17:24 GMT"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "JSP2/1.0.2"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:15 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "etag": "\"618192838\""
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 12:49:53 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "JSP2/1.0.2"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:57:16 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "9799"
+        },
+        {
+          "etag": "\"2773371803\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 03:58:12 GMT"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 09:54:56 GMT"
+        },
+        {
+          "cache-control": "max-age=604800"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_23.json b/jetty-http2/http2-hpack/src/test/resources/data/story_23.json
new file mode 100644
index 0000000..935e29d
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_23.json
@@ -0,0 +1,13406 @@
+{
+  "context": "response",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":status": "301"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:10 GMT"
+        },
+        {
+          "server": "Apache/2.2.22 (Unix)"
+        },
+        {
+          "set-cookie": "BBC-UID=557049b5114e60f649a3590541375a237274de5c1020f1118262d353e424f5650Mozilla%2f5%2e0%20%28Macintosh%3b%20Intel%20Mac%20OS%20X%2010%2e8%3b%20rv%3a16%2e0%29%20Gecko%2f20100101%20Firefox%2f16%2e0; expires=Sun, 03-Nov-13 13:37:10 GMT; path=/; domain=.bbc.co.uk;"
+        },
+        {
+          "location": "http://www.bbc.co.uk/"
+        },
+        {
+          "content-length": "229"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "cache-control": "private, max-age=60"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:37:38 GMT"
+        },
+        {
+          "etag": "\"dfc69d0362a1518353bddd8fa0dcc7cf-49cffe7f817cb754d3241794c0a3e991\""
+        },
+        {
+          "x-pal-host": "pal047.cwwtf.bbc.co.uk:80"
+        },
+        {
+          "content-length": "25186"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:11 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-action": "PASS (non-cacheable)"
+        },
+        {
+          "x-cache-age": "33"
+        },
+        {
+          "vary": "X-CDN"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:12 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Mon, 15 Oct 2012 10:42:57 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=900"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:52:11 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2414"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "keep-alive": "timeout=4, max=175"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "cache-control": "max-age=86400"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 13:37:12 GMT"
+        },
+        {
+          "etag": "\"dfc69d0362a1518353bddd8fa0dcc7cf-49cffe7f817cb754d3241794c0a3e991\""
+        },
+        {
+          "x-pal-host": "pal047.cwwtf.bbc.co.uk:80"
+        },
+        {
+          "content-length": "958"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:12 GMT"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "x-cache-action": "PASS (non-cacheable)"
+        },
+        {
+          "x-cache-age": "33"
+        },
+        {
+          "vary": "X-CDN"
+        },
+        {
+          "last-modified": "Wed, 28 Jun 2006 14:32:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "keep-alive": "timeout=4, max=190"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 05 May 2011 09:15:59 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4786"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "max-age=30845970"
+        },
+        {
+          "expires": "Sat, 26 Oct 2013 13:56:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "etag": "\"d1a-4af7eb4dd8880\""
+        },
+        {
+          "expires": "Tue, 13 Nov 2012 11:52:05 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 17 Oct 2011 13:37:22 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1227"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "etag": "\"677-4af7eb4bf0400\""
+        },
+        {
+          "expires": "Tue, 13 Nov 2012 11:49:51 GMT"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 17 Oct 2011 13:37:20 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "644"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 13:31:55 GMT"
+        },
+        {
+          "etag": "\"3c9-4cd32b163a8c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 08:47:52 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "418"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 13:31:55 GMT"
+        },
+        {
+          "etag": "\"217-4cd32b163a8c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 08:48:03 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "211"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 10:08:28 GMT"
+        },
+        {
+          "etag": "\"7316-4cbc5c0a6df00\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 24 Oct 2013 08:20:52 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5855"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 13:33:03 GMT"
+        },
+        {
+          "etag": "\"714c-4cd32b57141c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 08:48:07 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5891"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 16 Nov 2011 15:42:16 GMT"
+        },
+        {
+          "etag": "\"5ec2-4b1dbf2c82600\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Tue, 17 Sep 2013 11:45:35 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "7383"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 10:08:26 GMT"
+        },
+        {
+          "etag": "\"41d9-4cbc5c0885a80\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 24 Oct 2013 08:21:11 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:13 GMT"
+        },
+        {
+          "content-length": "5723"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 13:32:01 GMT"
+        },
+        {
+          "etag": "\"9029-4cd32b1bf3640\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 08:47:52 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "11967"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 26 Apr 2010 14:22:59 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "916"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "max-age=27418917"
+        },
+        {
+          "expires": "Mon, 16 Sep 2013 21:59:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 06 Jul 2011 17:14:05 GMT"
+        },
+        {
+          "etag": "\"20a0c-4a769ba3ff140\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Tue, 17 Sep 2013 11:50:03 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "37892"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:14 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 26 Apr 2010 14:18:35 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "31308"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "max-age=27418893"
+        },
+        {
+          "expires": "Mon, 16 Sep 2013 21:58:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:14 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://www.googleadservices.com/pagead/p3p.xml\", CP=\"NOI DEV PSA PSD IVA IVD OTP OUR OTR IND OTC\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "etag": "16031183393755591049"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:45:54 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:45:54 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-disposition": "attachment"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "content-length": "11145"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "3084"
+        },
+        {
+          "cache-control": "public, max-age=3600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 13:31:56 GMT"
+        },
+        {
+          "etag": "\"722a-4cd32b172eb00\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 08:48:06 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5813"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "302"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:18 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "302"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:19 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Tue, 08 May 2012 20:06:18 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 14:30:10 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 14:30:10 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "83229"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:37:19 GMT"
+        },
+        {
+          "cache-control": "max-age=86400, private"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 13:37:19 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "x-proc-data": "pd3-bgas02-0"
+        },
+        {
+          "content-type": "application/javascript;charset=ISO-8859-1"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:18 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 03 Oct 2012 14:20:11 GMT"
+        },
+        {
+          "etag": "\"1fbb-4cb2856215cc0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=2592000"
+        },
+        {
+          "expires": "Fri, 30 Nov 2012 15:42:45 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3254"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 10:08:24 GMT"
+        },
+        {
+          "etag": "\"3fc-4cbc5c069d600\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 24 Oct 2013 08:21:03 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1020"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 10:08:24 GMT"
+        },
+        {
+          "etag": "\"bc-4cbc5c069d600\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 24 Oct 2013 08:20:59 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "188"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Thu, 27 Sep 2012 10:15:51 GMT"
+        },
+        {
+          "etag": "\"293-4caac394743c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 03 Oct 2013 09:51:46 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:18 GMT"
+        },
+        {
+          "content-length": "659"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "etag": "\"11e-4caac394743c0\""
+        },
+        {
+          "expires": "Thu, 03 Oct 2013 09:51:45 GMT"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 27 Sep 2012 10:15:51 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "286"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "etag": "\"25d-4caac394743c0\""
+        },
+        {
+          "expires": "Thu, 03 Oct 2013 09:51:56 GMT"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 27 Sep 2012 10:15:51 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "605"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:19:32 GMT"
+        },
+        {
+          "etag": "\"121f4-4cd5e1b17b100\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31470349"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 17:19:32 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10750"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "2378"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:19 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Fri, 24 Jun 2011 10:14:39 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 22:58:53 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 22:58:53 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "12034"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "52707"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "308"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "308"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 01 Jan 2000 00:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.nedstat.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND NAV COM\""
+        },
+        {
+          "set-cookie": "s1=50951E1074D40255; expires=Thu, 02-Nov-2017 13:37:20 GMT; path=/; domain=.bbc.co.uk"
+        },
+        {
+          "location": "http://sa.bbc.co.uk/bbc/bbc/s?name=home.page&ns_m2=yes&ns_setsiteck=50951E1074D40255&geo_edition=int&pal_route=default&ml_name=barlesque&app_type=web&language=en-GB&ml_version=0.14.2&pal_webapp=wwhomepage&bbc_mc=not_set&screen_resolution=1366x768&blq_s=3.5&blq_r=3.5&blq_v=default-worldwide&ns__t=1351949839865&ns_c=UTF-8&ns_ti=BBC%20-%20Homepage&ns_jspageurl=http%3A//www.bbc.co.uk/&ns_referrer="
+        },
+        {
+          "content-length": "656"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:19:32 GMT"
+        },
+        {
+          "etag": "\"608f-4cd5e1b17b100\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31470342"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 17:19:32 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "6150"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 10:08:24 GMT"
+        },
+        {
+          "etag": "\"223-4cbc5c069d600\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 24 Oct 2013 08:20:57 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "547"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "308"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 10:08:24 GMT"
+        },
+        {
+          "etag": "\"89f-4cbc5c069d600\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 24 Oct 2013 08:20:51 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2207"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 01 Jan 2000 00:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.nedstat.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND NAV COM\""
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "308"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 13:32:08 GMT"
+        },
+        {
+          "etag": "\"11d21-4cd32b22a0600\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 08:48:10 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "24284"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "3291"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "last-modified": "Wed, 19 Sep 2012 18:25:49 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 20:14:11 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 20:14:11 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "1581"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "62589"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:19:26 GMT"
+        },
+        {
+          "etag": "\"1b7f-4cd5e1abc2380\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31470341"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 17:19:26 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "7039"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:19:26 GMT"
+        },
+        {
+          "etag": "\"20a3-4cd5e1abc2380\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31470356"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 17:19:26 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8355"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "308"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:19:26 GMT"
+        },
+        {
+          "etag": "\"3038-4cd5e1abc2380\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31470335"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 17:19:26 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "content-length": "12344"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "last-modified": "Fri, 07 Sep 2012 16:46:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 10:27:06 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:27:06 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "23913"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "11414"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:19:26 GMT"
+        },
+        {
+          "etag": "\"1ff0-4cd5e1abc2380\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31470356"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 17:19:26 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8176"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "308"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=63072000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 06:54:04 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 06:50:43 GMT"
+        },
+        {
+          "content-length": "3810"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=63072000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Fri, 31 Oct 2014 21:56:00 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 21:51:59 GMT"
+        },
+        {
+          "content-length": "5049"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=63072000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 09:47:13 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 09:31:24 GMT"
+        },
+        {
+          "content-length": "5071"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Thu, 19 Jul 2012 22:49:47 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 22:03:55 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 22:03:55 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "31936"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "56006"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:19:26 GMT"
+        },
+        {
+          "etag": "\"323-4cd5e1abc2380\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31470350"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 17:19:26 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "803"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "347"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 06 Jul 2011 17:14:01 GMT"
+        },
+        {
+          "etag": "\"1a56e-4a769ba02e840\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Tue, 17 Sep 2013 11:50:28 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "30642"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 14:27:29 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 20:29:16 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 20:29:16 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "18065"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "61685"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLuB86QsXkGiDUw6LAw6IpFSRlNUwBT4lFpER0UXYGEV+3P4; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:37:21 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas08-1"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=63072000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 12:03:34 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 11:02:09 GMT"
+        },
+        {
+          "content-length": "13746"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:19:26 GMT"
+        },
+        {
+          "etag": "\"136-4cd5e1abc2380\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31470294"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 17:19:26 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "content-length": "310"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=63072000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 08:07:24 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 08:04:03 GMT"
+        },
+        {
+          "content-length": "10701"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "cache-control": "max-age=63072000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 11:46:30 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:37:23 GMT"
+        },
+        {
+          "content-length": "4375"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=63072000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 03:14:26 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 03:10:05 GMT"
+        },
+        {
+          "content-length": "3965"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=63072000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 12:49:53 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 12:39:23 GMT"
+        },
+        {
+          "content-length": "4844"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 13:23:10 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31490899"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 13:28:30 GMT"
+        },
+        {
+          "content-length": "18949"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 17:01:19 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 02:00:11 GMT"
+        },
+        {
+          "content-length": "25085"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:30:25 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=31493423"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 14:10:58 GMT"
+        },
+        {
+          "content-length": "27938"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=63072000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 05:49:17 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 02:43:46 GMT"
+        },
+        {
+          "content-length": "5569"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:22 GMT"
+        },
+        {
+          "server": "Omniture DC/2.0.0"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "set-cookie": "s_vi=[CS]v1|284A8F0905160D8B-600001A1C0108CD4[CE]; Expires=Thu,  2 Nov 2017 13:37:22 GMT; Domain=sa.bbc.com; Path=/"
+        },
+        {
+          "location": "http://sa.bbc.com/b/ss/bbcwglobalprod/1/H.22.1/s0268806509673?AQB=1&pccr=true&vidn=284A8F0905160D8B-600001A1C0108CD4&&ndh=1&t=3%2F10%2F2012%209%3A37%3A21%206%20240&vmt=4F9A739C&vmf=bbc.112.2o7.net&ce=UTF-8&cdp=3&pageName=bbc%20%7C%20homepage&g=http%3A%2F%2Fwww.bbc.co.uk%2F&cc=USD&ch=homepage&events=event2&h1=bbc%7Chomepage&v2=D%3DpageName&c5=INDEX&v5=D%3Dc5&c6=homepage&v6=D%3Dc6&c11=saturday%7C1%3A30pm&c15=%2F&v15=D%3Dc15&c17=bbc%20%7C%20homepage&v17=D%3Dc17&c31=HTML&v31=D%3Dc31&c32=Not%20available&v32=D%3Dc32&v49=Direct%20Load&c53=www.bbc.co.uk&v53=D%3Dc53&c54=http%3A%2F%2Fwww.bbc.co.uk&v54=D%3Dc54&c55=bbc.com&v55=D%3Dc55&c56=D%3DpageName&v56=D%3Dc56&c57=yes&v57=D%3Dc57&c62=wpp%7Cldb%7Cm08%7Cm2l%7Cm6i%7Cm1f%7Cmpu%7Cm29%7Cm4t%7Cm80&v62=D%3Dc62&s=1366x768&c=24&j=1.7&v=Y&k=Y&bw=994&bh=649&p=Java%20Applet%20Plug-in%3BQuickTime%20Plug-in%207.7.1%3B&AQE=1"
+        },
+        {
+          "x-c": "ms-4.4.9"
+        },
+        {
+          "expires": "Fri, 02 Nov 2012 13:37:22 GMT"
+        },
+        {
+          "last-modified": "Sun, 04 Nov 2012 13:37:22 GMT"
+        },
+        {
+          "cache-control": "no-cache, no-store, max-age=0, no-transform, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA OUR IND COM NAV STA\""
+        },
+        {
+          "xserver": "www614"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "keep-alive": "timeout=15"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "text/plain"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=86400"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:02:56 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 09:46:49 GMT"
+        },
+        {
+          "content-length": "2041"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "etag": "\"c82a-4cd8003bbf440\""
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=86400"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:34:34 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:33:07 GMT"
+        },
+        {
+          "content-length": "5173"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "etag": "\"3ca55-4cd817fe482c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:22 GMT"
+        },
+        {
+          "server": "Omniture DC/2.0.0"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "set-cookie": "s_vi=[CS]v1|284A8F0905160D8B-600001A1C0108CD4[CE]; Expires=Thu,  2 Nov 2017 13:37:22 GMT; Domain=sa.bbc.com; Path=/"
+        },
+        {
+          "location": "http://sa.bbc.com/b/ss/bbcwglobalprod/1/H.22.1/s0268806509673?AQB=1&pccr=true&vidn=284A8F0905160D8B-600001A1C0108CD4&&ndh=1&t=3%2F10%2F2012%209%3A37%3A21%206%20240&vmt=4F9A739C&vmf=bbc.112.2o7.net&ce=UTF-8&cdp=3&pageName=bbc%20%7C%20homepage&g=http%3A%2F%2Fwww.bbc.co.uk%2F&cc=USD&ch=homepage&events=event2&h1=bbc%7Chomepage&v2=D%3DpageName&c5=INDEX&v5=D%3Dc5&c6=homepage&v6=D%3Dc6&c11=saturday%7C1%3A30pm&c15=%2F&v15=D%3Dc15&c17=bbc%20%7C%20homepage&v17=D%3Dc17&c31=HTML&v31=D%3Dc31&c32=Not%20available&v32=D%3Dc32&v49=Direct%20Load&c53=www.bbc.co.uk&v53=D%3Dc53&c54=http%3A%2F%2Fwww.bbc.co.uk&v54=D%3Dc54&c55=bbc.com&v55=D%3Dc55&c56=D%3DpageName&v56=D%3Dc56&c57=yes&v57=D%3Dc57&c62=wpp%7Cldb%7Cm08%7Cm2l%7Cm6i%7Cm1f%7Cmpu%7Cm29%7Cm4t%7Cm80&v62=D%3Dc62&s=1366x768&c=24&j=1.7&v=Y&k=Y&bw=994&bh=649&p=Java%20Applet%20Plug-in%3BQuickTime%20Plug-in%207.7.1%3B&AQE=1"
+        },
+        {
+          "x-c": "ms-4.4.9"
+        },
+        {
+          "expires": "Fri, 02 Nov 2012 13:37:22 GMT"
+        },
+        {
+          "last-modified": "Sun, 04 Nov 2012 13:37:22 GMT"
+        },
+        {
+          "cache-control": "no-cache, no-store, max-age=0, no-transform, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA OUR IND COM NAV STA\""
+        },
+        {
+          "xserver": "www620"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "keep-alive": "timeout=15"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "etag": "\"50951E12-5F83-53505C0B\""
+        },
+        {
+          "vary": "*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 10:24:39 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31480349"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 10:32:40 GMT"
+        },
+        {
+          "content-length": "34963"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 19:51:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=86400"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 03:52:27 GMT"
+        },
+        {
+          "content-length": "16335"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "etag": "\"4627f-4ccbf4cca7880\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "content-length": "1140"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-transform, max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Omniture DC/2.0.0"
+        },
+        {
+          "last-modified": "Mon, 09 Jul 2012 16:00:20 GMT"
+        },
+        {
+          "etag": "\"115240-38-b5f12d00\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "56"
+        },
+        {
+          "cache-control": "max-age=7776000"
+        },
+        {
+          "expires": "Sun, 07 Oct 2012 17:35:44 GMT"
+        },
+        {
+          "xserver": "www465"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 13:25:41 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=31535948"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 01:59:43 GMT"
+        },
+        {
+          "content-length": "41157"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 15:49:09 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31535796"
+        },
+        {
+          "expires": "Fri, 01 Nov 2013 04:08:52 GMT"
+        },
+        {
+          "content-length": "43495"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=63072000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 11:29:44 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 11:15:24 GMT"
+        },
+        {
+          "content-length": "83456"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:57:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=86400"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 11:13:50 GMT"
+        },
+        {
+          "content-length": "18324"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "etag": "\"8c10d-4cd6f66d2d640\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:22 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 15:44:45 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=86400"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 18:39:25 GMT"
+        },
+        {
+          "content-length": "24118"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "etag": "\"bae19-4cd48aa479540\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.22 (Unix) mod_ssl/2.2.22 OpenSSL/0.9.7d"
+        },
+        {
+          "cache-control": "max-age=86400"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:01:50 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Fri, 26 Oct 2012 21:25:25 GMT"
+        },
+        {
+          "content-length": "21999"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "etag": "\"8cfa9-4ccfcf53bbb40\""
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=86400"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 01:27:36 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 19:32:42 GMT"
+        },
+        {
+          "content-length": "29003"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "etag": "\"b3983-4cbe1c0594a80\""
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=86400"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:14:22 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:56:52 GMT"
+        },
+        {
+          "content-length": "28742"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "etag": "\"9b7c0-4cd6f64243100\""
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=31457054"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 12:04:47 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 11:57:38 GMT"
+        },
+        {
+          "content-length": "24526"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "etag": "\"3ca55-4cd817fe482c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "cache-control": "max-age=86400"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 17:24:54 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 14:40:40 GMT"
+        },
+        {
+          "content-length": "45064"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "etag": "\"fcf54-4cd841e9faa00\""
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 03:17:34 GMT"
+        },
+        {
+          "server": "collection8"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID\""
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "public, max-age=604800"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:23 GMT"
+        },
+        {
+          "content-length": "5450"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:24 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-length": "173"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "set-cookie": "v=848381a268c12381e2d991b8bfad50951e1458d396-6634083950951e14834_3366; expires=Sat, 03-Nov-2012 14:07:24 GMT; path=/; domain=.effectivemeasure.net"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "server": "collection10"
+        },
+        {
+          "cache-directive": "no-cache"
+        },
+        {
+          "pragma-directive": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 12 Jul 2011 16:02:59 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "928"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "max-age=27418899"
+        },
+        {
+          "expires": "Mon, 16 Sep 2013 21:59:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:27 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:26:47 GMT"
+        },
+        {
+          "cache-control": "max-age=62707765"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:26:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "content-length": "3417"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:27:16 GMT"
+        },
+        {
+          "content-length": "2419"
+        },
+        {
+          "cache-control": "max-age=62707751"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:26:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:27:16 GMT"
+        },
+        {
+          "content-length": "1281"
+        },
+        {
+          "cache-control": "max-age=62707783"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:27:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:27:28 GMT"
+        },
+        {
+          "cache-control": "max-age=62707813"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:27:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "content-length": "56"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 18 Nov 2009 12:17:02 GMT"
+        },
+        {
+          "content-length": "1275"
+        },
+        {
+          "cache-control": "max-age=345600"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "vary": "X-CDN"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "keep-alive": "timeout=5, max=778"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 09:35:09 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-length": "941"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-action": "MISS"
+        },
+        {
+          "x-cache-age": "0"
+        },
+        {
+          "cache-control": "private, max-age=0, must-revalidate"
+        },
+        {
+          "x-lb-nocache": "true"
+        },
+        {
+          "vary": "X-CDN"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 22 Nov 2011 09:50:32 GMT"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1409"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "max-age=33813709"
+        },
+        {
+          "expires": "Fri, 29 Nov 2013 22:19:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 22 Nov 2011 09:39:54 GMT"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3436"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "max-age=33814374"
+        },
+        {
+          "expires": "Fri, 29 Nov 2013 22:30:23 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 22 Nov 2011 09:31:26 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "348"
+        },
+        {
+          "cache-control": "max-age=33076455"
+        },
+        {
+          "expires": "Thu, 21 Nov 2013 09:31:44 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 12 Jul 2012 11:26:22 GMT"
+        },
+        {
+          "content-length": "3667"
+        },
+        {
+          "cache-control": "max-age=53347413"
+        },
+        {
+          "expires": "Mon, 14 Jul 2014 00:21:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 12 Jul 2012 11:26:27 GMT"
+        },
+        {
+          "content-length": "1999"
+        },
+        {
+          "cache-control": "max-age=53214541"
+        },
+        {
+          "expires": "Sat, 12 Jul 2014 11:26:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "301"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        },
+        {
+          "location": "http://emp.bbci.co.uk/emp/releases/bump/revisions/905298/embed.js?emp=worldwide&enableClear=1"
+        },
+        {
+          "content-length": "305"
+        },
+        {
+          "cache-control": "max-age=211"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:27:21 GMT"
+        },
+        {
+          "cache-control": "max-age=62707796"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:27:25 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "content-length": "303"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:27:37 GMT"
+        },
+        {
+          "content-length": "4740"
+        },
+        {
+          "cache-control": "max-age=62707822"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:27:51 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:37:27 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "91216"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:27 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-action": "MISS"
+        },
+        {
+          "x-cache-age": "0"
+        },
+        {
+          "cache-control": "private, max-age=0, must-revalidate"
+        },
+        {
+          "x-lb-nocache": "true"
+        },
+        {
+          "vary": "X-CDN"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 17:14:09 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "446"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-action": "MISS"
+        },
+        {
+          "x-cache-age": "0"
+        },
+        {
+          "cache-control": "private, max-age=0, must-revalidate"
+        },
+        {
+          "x-lb-nocache": "true"
+        },
+        {
+          "vary": "X-CDN"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 28 Jul 2010 11:07:36 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8432"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "max-age=33813771"
+        },
+        {
+          "expires": "Fri, 29 Nov 2013 22:20:20 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Mon, 09 Jul 2012 15:09:23 GMT"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2234"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "max-age=53344068"
+        },
+        {
+          "expires": "Sun, 13 Jul 2014 23:25:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 07:38:38 GMT"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1657"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "max-age=62100138"
+        },
+        {
+          "expires": "Thu, 23 Oct 2014 07:39:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 02:43:33 GMT"
+        },
+        {
+          "content-length": "5496"
+        },
+        {
+          "cache-control": "max-age=63033059"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 02:48:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 11:02:09 GMT"
+        },
+        {
+          "content-length": "4994"
+        },
+        {
+          "cache-control": "max-age=63062731"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 11:03:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "BGUID=65e0995521ce0199c628d193f15847bbfbb0331236b8ab2579e9db3ae85993f0; expires=Wed, 02-Nov-16 13:37:29 GMT; path=/; domain=bbc.co.uk;"
+        },
+        {
+          "last-modified": "Wed, 01 Mar 2006 15:13:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "cache-control": "max-age=0, no-cache=Set-Cookie"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "keep-alive": "timeout=10, max=182"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "location": "http://emp.bbci.co.uk/emp/releases/bump/revisions/905298/embed.js?emp=worldwide&enableClear=1"
+        },
+        {
+          "content-length": "2628"
+        },
+        {
+          "cache-control": "max-age=166"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:40:15 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 11 Jul 2012 23:13:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 00:21:03 GMT"
+        },
+        {
+          "cache-control": "max-age=63024342"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 00:23:11 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "content-length": "3624"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 00:21:28 GMT"
+        },
+        {
+          "content-length": "4643"
+        },
+        {
+          "cache-control": "max-age=63024320"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 00:22:49 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 09:22:54 GMT"
+        },
+        {
+          "content-length": "9275"
+        },
+        {
+          "cache-control": "max-age=62711088"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 09:22:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 09:22:55 GMT"
+        },
+        {
+          "cache-control": "max-age=62711138"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 09:23:07 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "content-length": "11982"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 11:29:54 GMT"
+        },
+        {
+          "content-length": "5363"
+        },
+        {
+          "cache-control": "max-age=63064402"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 11:30:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 10:08:24 GMT"
+        },
+        {
+          "etag": "\"3ff-4cbc5c069d600\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 24 Oct 2013 08:22:26 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1023"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 00:34:55 GMT"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5633"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=63025209"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 00:37:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 07 Mar 2012 10:43:47 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "7269"
+        },
+        {
+          "cache-control": "max-age=43235623"
+        },
+        {
+          "expires": "Tue, 18 Mar 2014 23:31:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:20:54 GMT"
+        },
+        {
+          "cache-control": "max-age=62707524"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:22:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:29 GMT"
+        },
+        {
+          "content-length": "34395"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 09:22:57 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "24217"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "max-age=62711138"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 09:23:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 15:48:27 GMT"
+        },
+        {
+          "content-length": "20316"
+        },
+        {
+          "cache-control": "max-age=63051888"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 08:02:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 12 Jul 2011 15:59:09 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "31310"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "max-age=27418847"
+        },
+        {
+          "expires": "Mon, 16 Sep 2013 21:58:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 12 Jul 2011 15:59:10 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "31708"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "max-age=27418799"
+        },
+        {
+          "expires": "Mon, 16 Sep 2013 21:57:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:32 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 12 Jul 2012 10:02:19 GMT"
+        },
+        {
+          "cache-control": "max-age=53347291"
+        },
+        {
+          "expires": "Mon, 14 Jul 2014 00:19:05 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:34 GMT"
+        },
+        {
+          "content-length": "358"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 12 Jul 2011 15:59:09 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5794"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "max-age=27418850"
+        },
+        {
+          "expires": "Mon, 16 Sep 2013 21:58:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:34 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 12 Jul 2012 10:02:20 GMT"
+        },
+        {
+          "cache-control": "max-age=53394356"
+        },
+        {
+          "expires": "Mon, 14 Jul 2014 13:23:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:35 GMT"
+        },
+        {
+          "content-length": "3759"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Thu, 26 Apr 2012 15:23:19 GMT"
+        },
+        {
+          "etag": "\"453b-4be96914da7c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Tue, 17 Sep 2013 11:52:07 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3403"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:35 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 12 Jul 2012 10:02:24 GMT"
+        },
+        {
+          "cache-control": "max-age=53487765"
+        },
+        {
+          "expires": "Tue, 15 Jul 2014 15:20:20 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:35 GMT"
+        },
+        {
+          "content-length": "6382"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 09:22:55 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1304"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "max-age=62711132"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 09:23:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:37 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:37 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "etag": "\"392d4eaba4ddbcf7bb408352be465c19\""
+        },
+        {
+          "cache-control": "max-age=1728000, private"
+        },
+        {
+          "x-lb-nocache": "true"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-length": "286"
+        },
+        {
+          "keep-alive": "timeout=45"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 09:22:54 GMT"
+        },
+        {
+          "cache-control": "max-age=62711099"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 09:22:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:37 GMT"
+        },
+        {
+          "content-length": "5788"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "2410"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:38 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:38 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 01 Jan 2000 00:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.nedstat.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND NAV COM\""
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Thu, 19 Apr 2012 16:34:19 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 23:06:55 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 23:06:55 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "9836"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "52243"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:37 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "etag": "\"392d4eaba4ddbcf7bb408352be465c19\""
+        },
+        {
+          "cache-control": "private, max-age=30"
+        },
+        {
+          "x-lb-nocache": "true"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-length": "47"
+        },
+        {
+          "keep-alive": "timeout=45"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "327"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:38 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Mon, 09 Jul 2012 15:09:22 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "970"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "max-age=52968695"
+        },
+        {
+          "expires": "Wed, 09 Jul 2014 15:09:13 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:38 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Mon, 09 Jul 2012 15:09:21 GMT"
+        },
+        {
+          "content-length": "126"
+        },
+        {
+          "cache-control": "max-age=52968716"
+        },
+        {
+          "expires": "Wed, 09 Jul 2014 15:09:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:38 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:27:16 GMT"
+        },
+        {
+          "cache-control": "max-age=62707784"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:27:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:38 GMT"
+        },
+        {
+          "content-length": "3595"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:26:49 GMT"
+        },
+        {
+          "content-length": "1859"
+        },
+        {
+          "cache-control": "max-age=62707757"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:26:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:38 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "301"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        },
+        {
+          "location": "http://emp.bbci.co.uk/emp/releases/worldwide/revisions/749603_749269_749444_6/embed.js?mediaset=journalism-pc"
+        },
+        {
+          "content-length": "317"
+        },
+        {
+          "cache-control": "max-age=160"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:40:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:38 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 11 Jul 2012 23:13:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 09:22:54 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1014"
+        },
+        {
+          "cache-control": "max-age=62711082"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 09:22:20 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:38 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "location": "http://emp.bbci.co.uk/emp/releases/worldwide/revisions/749603_749269_749444_6/embed.js?mediaset=journalism-pc"
+        },
+        {
+          "content-length": "6090"
+        },
+        {
+          "cache-control": "max-age=167"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:40:26 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:39 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 18 Sep 2012 12:56:25 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 10:08:24 GMT"
+        },
+        {
+          "etag": "\"adb-4cbc5c069d600\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Thu, 24 Oct 2013 08:21:42 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2779"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:38 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-disposition": "attachment"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:40 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1545"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "2506"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:40 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Tue, 15 Feb 2011 16:39:27 GMT"
+        },
+        {
+          "etag": "\"6414-49c54cec44dc0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Tue, 17 Sep 2013 11:49:13 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "7619"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:39 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 20:42:56 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 15:05:54 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 15:05:54 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "37317"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "81106"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "327"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:40 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Mon, 09 Jul 2012 15:09:22 GMT"
+        },
+        {
+          "cache-control": "max-age=52968720"
+        },
+        {
+          "expires": "Wed, 09 Jul 2014 15:09:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:38 GMT"
+        },
+        {
+          "content-length": "36830"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:40 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-length": "359"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "530"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:40 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:26:46 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4729"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "max-age=62707754"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:26:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:27:33 GMT"
+        },
+        {
+          "content-length": "130"
+        },
+        {
+          "cache-control": "max-age=62707807"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:27:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 13:37:38 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "81990"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:38 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-action": "MISS"
+        },
+        {
+          "x-cache-age": "0"
+        },
+        {
+          "cache-control": "private, max-age=0, must-revalidate"
+        },
+        {
+          "x-lb-nocache": "true"
+        },
+        {
+          "vary": "X-CDN"
+        },
+        {
+          "last-modified": "Wed, 26 Sep 2012 09:35:41 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "nginx/1.2.0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:43 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "location": "http://ad-emea.doubleclick.net/adj/N2581.122656.2214702362621/B6422491.8;sz=120x30;click0=http://ad.doubleclick.net/click%3Bh%3Dv8/3d22/3/0/%2a/q%3B264679025%3B0-0%3B1%3B49066565%3B47-120/30%3B47423100/47438653/1%3B%3B%7Eokv%3D%3Bslot%3Dpartner_button1%3Bsz%3D120x30%3Bsectn%3Dnews%3Bctype%3Dcontent%3Bnews%3Damerica%3Breferrer%3D%3Bdomain%3Dwww.bbc.co.uk%3Breferrer_domain%3Dwww.bbc.co.uk%3Brsi%3D%3Bheadline%3Dromneypromisesus%2527realchang%3B%7Esscs%3D%3f;ord=835861?"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.2.0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:43 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "483"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:44 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Tue, 15 May 2012 22:04:59 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 16:00:52 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 16:00:52 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "4146"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "77812"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Fri, 11 Mar 2011 22:15:34 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 19:51:36 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 19:51:36 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "3834"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "63968"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "326"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:44 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "212"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:44 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLuB86QsXkGiDUw6LAw6IpFSRlNUwBT4lFpGQscUZ2EV0HP8; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:37:44 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas06-9"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:44 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:44 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-length": "173"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "set-cookie": "v=1de6548e4a0d3e45a7c991b8bfad50951e1458d396-6634083950951e28834_3366; expires=Sat, 03-Nov-2012 14:07:44 GMT; path=/; domain=.effectivemeasure.net"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "server": "collection10"
+        },
+        {
+          "cache-directive": "no-cache"
+        },
+        {
+          "pragma-directive": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:50 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "P3P - policyref=\"http://www.adfusion.com/w3c/adfusion.xml\", CP=\"NON DSP COR CURa TIA\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "location": "http://www.adfusion.com/AdServer/default.aspx?e=i&lid=10641&ct="
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-length": "188"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "age": "245581"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:45 GMT"
+        },
+        {
+          "last-modified": "Mon, 14 Nov 2011 18:32:16 GMT"
+        },
+        {
+          "content-length": "4442"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:50 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "P3P - policyref=\"http://www.adfusion.com/w3c/adfusion.xml\", CP=\"NON DSP COR CURa TIA\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "location": "http://www.adfusion.com/AdServer/default.aspx?e=i&lid=10641&ct="
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-length": "4449"
+        },
+        {
+          "set-cookie": "cid=6f010485-f507-4a4e-a485-feb7619db013; domain=.adfusion.com; expires=Wed, 01-Jan-2020 06:00:00 GMT; path=/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "age": "73519"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:45 GMT"
+        },
+        {
+          "last-modified": "Fri, 20 Apr 2012 15:50:57 GMT"
+        },
+        {
+          "content-length": "1709"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "age": "172785"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:45 GMT"
+        },
+        {
+          "last-modified": "Wed, 22 Aug 2012 17:58:48 GMT"
+        },
+        {
+          "content-length": "4036"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "age": "120980"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:45 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Dec 2011 21:51:59 GMT"
+        },
+        {
+          "content-length": "4919"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Mon, 09 Jul 2012 15:09:22 GMT"
+        },
+        {
+          "cache-control": "max-age=52968714"
+        },
+        {
+          "expires": "Wed, 09 Jul 2014 15:09:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:44 GMT"
+        },
+        {
+          "content-length": "1128"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:44 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 12 Jul 2011 15:59:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "207"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "max-age=27418929"
+        },
+        {
+          "expires": "Mon, 16 Sep 2013 21:59:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 26 Oct 2012 19:57:24 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "26021"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=62403629"
+        },
+        {
+          "expires": "Sun, 26 Oct 2014 19:58:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "max-age=300, public, s-maxage=120"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:28 GMT"
+        },
+        {
+          "keep-alive": "timeout=5, max=852"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:42:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"8550-4c7d8d79b86c0\""
+        },
+        {
+          "last-modified": "Wed, 22 Aug 2012 11:14:11 GMT"
+        },
+        {
+          "content-length": "12181"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 22 Aug 2012 11:14:13 GMT"
+        },
+        {
+          "etag": "\"31a9-4c7d8d7ba0b40\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=300, public, s-maxage=120"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:38:33 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2364"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:46 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "application/json;charset=UTF-8"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "access-control-allow-methods": "POST, GET, PUT, OPTIONS"
+        },
+        {
+          "access-control-allow-headers": "Content-Type, X-Requested-With, *"
+        },
+        {
+          "content-length": "117"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:47 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "last-modified": "Wed, 22 Aug 2012 11:14:08 GMT"
+        },
+        {
+          "etag": "\"212e-4c7d8d76dc000\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "expires": "Tue, 17 Sep 2013 11:50:39 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8494"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:47 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:57 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 01 Jan 2000 00:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.nedstat.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND NAV COM\""
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Mon, 09 Jul 2012 15:09:23 GMT"
+        },
+        {
+          "cache-control": "max-age=52968675"
+        },
+        {
+          "expires": "Wed, 09 Jul 2014 15:09:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:57 GMT"
+        },
+        {
+          "content-length": "126"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "575"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:58 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "location": "http://mp.apmebf.com/ad/fm/13001-83639-22765-1?mpt=853079&mpvc=http://ad.doubleclick.net/click%3Bh%3Dv8/3d22/3/0/%2a/v%3B264640753%3B0-0%3B0%3B73659506%3B3454-728/90%3B47524155/47539673/1%3B%3B%7Eokv%3D%3Bslot%3Dleaderboard%3Bsz%3D728x90%2C970x66%2C970x90%2C970x250%3Bsectn%3Dnews%3Bctype%3Dcontent%3Bnews%3Dworld%3Breferrer%3Dnewsworlduscanada20104929%3Bdomain%3Dwww.bbc.co.uk%3Breferrer_domain%3Dwww.bbc.co.uk%3Brsi%3D%3Bheadline%3Dbombkillspakistanipolitician%3B%7Esscs%3D%3f&host=altfarm.mediaplex.com"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:58 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:58 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 01 Mar 2006 15:13:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "cache-control": "max-age=0, no-cache=Set-Cookie"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:37:58 GMT"
+        },
+        {
+          "keep-alive": "timeout=10, max=176"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "341"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:58 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-disposition": "attachment"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1387"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 06:50:47 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4705"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=63047620"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 06:51:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR PSAo PSDo OUR IND UNI COM NAV\""
+        },
+        {
+          "set-cookie": "S=g14vo-413-1351949879145-ya; domain=.apmebf.com; path=/; expires=Mon, 03-Nov-2014 13:37:59 GMT"
+        },
+        {
+          "location": "http://altfarm.mediaplex.com/ad/fm/13001-83639-22765-1?mpt=853079&mpvc=http://ad.doubleclick.net/click%3Bh%3Dv8/3d22/3/0/%2a/v%3B264640753%3B0-0%3B0%3B73659506%3B3454-728/90%3B47524155/47539673/1%3B%3B%7Eokv%3D%3Bslot%3Dleaderboard%3Bsz%3D728x90%2C970x66%2C970x90%2C970x250%3Bsectn%3Dnews%3Bctype%3Dcontent%3Bnews%3Dworld%3Breferrer%3Dnewsworlduscanada20104929%3Bdomain%3Dwww.bbc.co.uk%3Breferrer_domain%3Dwww.bbc.co.uk%3Brsi%3D%3Bheadline%3Dbombkillspakistanipolitician%3B%7Esscs%3D%3f&no_cj_c=1&upsid=545485072431"
+        },
+        {
+          "content-length": "711"
+        },
+        {
+          "keep-alive": "timeout=5"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR PSAo PSDo OUR IND UNI COM NAV\""
+        },
+        {
+          "set-cookie": "mojo3=13001:22765; expires=Sun, 2-Nov-2014 16:28:10 GMT; path=/; domain=.mediaplex.com;"
+        },
+        {
+          "location": "http://img.mediaplex.com/content/0/13001/728x90_090512_MVT_Standard.html?mpck=altfarm.mediaplex.com%2Fad%2Fck%2F13001-83639-22765-1%3Fmpt%3D853079&mpt=853079&mpvc=http://ad.doubleclick.net/click%3Bh%3Dv8/3d22/3/0/%2a/v%3B264640753%3B0-0%3B0%3B73659506%3B3454-728/90%3B47524155/47539673/1%3B%3B%7Eokv%3D%3Bslot%3Dleaderboard%3Bsz%3D728x90%2C970x66%2C970x90%2C970x250%3Bsectn%3Dnews%3Bctype%3Dcontent%3Bnews%3Dworld%3Breferrer%3Dnewsworlduscanada20104929%3Bdomain%3Dwww.bbc.co.uk%3Breferrer_domain%3Dwww.bbc.co.uk%3Brsi%3D%3Bheadline%3Dbombkillspakistanipolitician%3B%7Esscs%3D%3f"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:58 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 13:34:50 GMT"
+        },
+        {
+          "etag": "\"a5f202-357-4cba066fe7280\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1285"
+        },
+        {
+          "keep-alive": "timeout=5"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "2518"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "339"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 11:26:09 GMT"
+        },
+        {
+          "cache-control": "max-age=63064269"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 11:29:07 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:58 GMT"
+        },
+        {
+          "content-length": "18429"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "332"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-length": "332"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "339"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:37:55 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "84226"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:55 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-action": "MISS"
+        },
+        {
+          "x-cache-age": "0"
+        },
+        {
+          "cache-control": "private, max-age=0, must-revalidate"
+        },
+        {
+          "x-lb-nocache": "true"
+        },
+        {
+          "vary": "X-CDN"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "expires": "Sat Nov 03 13:37:59 UTC 2012"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "p3p": "CP=\"NOI CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT\""
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-length": "1273"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "212"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:05 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "P3P - policyref=\"http://www.adfusion.com/w3c/adfusion.xml\", CP=\"NON DSP COR CURa TIA\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "location": "http://www.adfusion.com/AdServer/default.aspx?e=i&lid=10641&ct="
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-length": "188"
+        },
+        {
+          "set-cookie": "cid=6f010485-f507-4a4e-a485-feb7619db013; domain=.adfusion.com; expires=Wed, 01-Jan-2020 06:00:00 GMT; path=/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLuB86QsXkGiDUw6LAw6IpFSRlNUwBT4lNpFQ0QUg4OfFcQQ1VXgXlFGVpIpliI6eB4eKxBjZB19azY=; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:38:00 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas01-1"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "expires": "Sat Nov 03 13:37:59 UTC 2012"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "p3p": "CP=\"NOI CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT\""
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "7375"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:05 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "P3P - policyref=\"http://www.adfusion.com/w3c/adfusion.xml\", CP=\"NON DSP COR CURa TIA\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "location": "http://www.adfusion.com/AdServer/default.aspx?e=i&lid=10641&ct="
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-length": "4449"
+        },
+        {
+          "set-cookie": "cid=6f010485-f507-4a4e-a485-feb7619db013; domain=.adfusion.com; expires=Wed, 01-Jan-2020 06:00:00 GMT; path=/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "56812"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:45:03 GMT"
+        },
+        {
+          "server": "Apache/2.2.3 (CentOS)"
+        },
+        {
+          "last-modified": "Sat, 18 Aug 2012 06:04:35 GMT"
+        },
+        {
+          "etag": "\"5d0aba4-ddec-4c7840d06c2c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=3600"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:45:03 GMT"
+        },
+        {
+          "x-permitted-cross-domain-policies": "all"
+        },
+        {
+          "age": "3177"
+        },
+        {
+          "x-amz-cf-id": "Nqvl7hdh1ZID-spjM3MSj_rmvJrOblt2qYh9QKaP4AUq-J79-UBaKg=="
+        },
+        {
+          "via": "1.0 f9710778d388f0645feb35b6ec48d316.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:00 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-length": "173"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "set-cookie": "v=51bfcbb530581eaf84e991b8bfad50951e1458d396-6634083950951e38834_3366; expires=Sat, 03-Nov-2012 14:08:00 GMT; path=/; domain=.effectivemeasure.net"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "server": "collection10"
+        },
+        {
+          "cache-directive": "no-cache"
+        },
+        {
+          "pragma-directive": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:37:59 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "application/json;charset=UTF-8"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "access-control-allow-methods": "POST, GET, PUT, OPTIONS"
+        },
+        {
+          "access-control-allow-headers": "Content-Type, X-Requested-With, *"
+        },
+        {
+          "content-length": "111"
+        },
+        {
+          "cache-control": "max-age=31"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:00 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:27:19 GMT"
+        },
+        {
+          "cache-control": "max-age=62707764"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:27:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:06 GMT"
+        },
+        {
+          "content-length": "297"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:06 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 01 Mar 2006 15:13:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "cache-control": "max-age=0, no-cache=Set-Cookie"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:38:06 GMT"
+        },
+        {
+          "keep-alive": "timeout=10, max=162"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 12 Jul 2012 10:02:19 GMT"
+        },
+        {
+          "cache-control": "max-age=53487737"
+        },
+        {
+          "expires": "Tue, 15 Jul 2014 15:20:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "content-length": "7969"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 16:22:37 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4852"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=62995766"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 16:27:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 23:13:05 GMT"
+        },
+        {
+          "content-length": "6548"
+        },
+        {
+          "cache-control": "max-age=63020343"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 23:17:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 08:04:19 GMT"
+        },
+        {
+          "cache-control": "max-age=62880339"
+        },
+        {
+          "expires": "Sat, 01 Nov 2014 08:23:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "content-length": "4144"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 14:00:52 GMT"
+        },
+        {
+          "content-length": "3761"
+        },
+        {
+          "cache-control": "max-age=62901025"
+        },
+        {
+          "expires": "Sat, 01 Nov 2014 14:08:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 18:22:05 GMT"
+        },
+        {
+          "content-length": "6600"
+        },
+        {
+          "cache-control": "max-age=62916820"
+        },
+        {
+          "expires": "Sat, 01 Nov 2014 18:31:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "454"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 14:39:26 GMT"
+        },
+        {
+          "content-length": "6851"
+        },
+        {
+          "cache-control": "max-age=62989321"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 14:40:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 11:27:56 GMT"
+        },
+        {
+          "content-length": "5411"
+        },
+        {
+          "cache-control": "max-age=62320782"
+        },
+        {
+          "expires": "Sat, 25 Oct 2014 20:57:49 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "location": "http://img.mediaplex.com/content/0/18916/LT_XML_RateTable_ScrollingHeadline_PurpleArrows_RecordLows_728x90_050112.js?mpck=altfarm.mediaplex.com%2Fad%2Fck%2F18916-133472-32866-8%3Fmpt%3D862767&mpt=862767&mpvc=http://ad.doubleclick.net/click%3Bh%3Dv8/3d22/3/0/%2a/o%3B264441578%3B0-0%3B0%3B19196826%3B3454-728/90%3B49903581/49895429/1%3B%3B%7Eokv%3D%3Bslot%3Dleaderboard%3Bsz%3D728x90%2C970x66%2C970x90%2C970x250%3Bsectn%3Dnews%3Bctype%3Dindex%3Bnews%3Dworld%3Breferrer%3Dnewsworldasia20190337%3Bdomain%3Dwww.bbc.co.uk%3Breferrer_domain%3Dwww.bbc.co.uk%3Brsi%3DJ08781_10132%3Brsi%3DJ08781_10628%3B%7Esscs%3D%3f"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR PSAo PSDo OUR IND UNI COM NAV\""
+        },
+        {
+          "set-cookie": "mojo3=18916:32866/13001:22765; expires=Sun, 2-Nov-2014 16:53:09 GMT; path=/; domain=.mediaplex.com;"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 01 Jan 2000 00:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.nedstat.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND NAV COM\""
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 01 May 2012 19:15:40 GMT"
+        },
+        {
+          "etag": "\"2119c1-1526-4befe65754f00\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "6980"
+        },
+        {
+          "keep-alive": "timeout=5"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:38:05 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "90666"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:05 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-action": "MISS"
+        },
+        {
+          "x-cache-age": "0"
+        },
+        {
+          "cache-control": "private, max-age=0, must-revalidate"
+        },
+        {
+          "x-lb-nocache": "true"
+        },
+        {
+          "vary": "X-CDN"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 19 Oct 2011 07:28:39 GMT"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "18359"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=52349338"
+        },
+        {
+          "expires": "Wed, 02 Jul 2014 11:07:05 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 18:19:24 GMT"
+        },
+        {
+          "cache-control": "max-age=63002534"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 18:20:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "content-length": "5791"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 09:30:47 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5035"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=63057300"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 09:33:07 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 06:55:42 GMT"
+        },
+        {
+          "cache-control": "max-age=62961571"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 06:57:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "content-length": "6266"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 15:53:37 GMT"
+        },
+        {
+          "content-length": "4757"
+        },
+        {
+          "cache-control": "max-age=62820986"
+        },
+        {
+          "expires": "Fri, 31 Oct 2014 15:54:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 09:01:42 GMT"
+        },
+        {
+          "cache-control": "max-age=62882710"
+        },
+        {
+          "expires": "Sat, 01 Nov 2014 09:03:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "content-length": "5396"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:09:30 GMT"
+        },
+        {
+          "content-length": "4759"
+        },
+        {
+          "cache-control": "max-age=62965934"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 08:10:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 13:24:17 GMT"
+        },
+        {
+          "content-length": "3202"
+        },
+        {
+          "cache-control": "max-age=62989607"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 14:44:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 13:01:25 GMT"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4022"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=62983627"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 13:05:15 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:08 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 08:54:55 GMT"
+        },
+        {
+          "cache-control": "max-age=62970180"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 09:21:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:08 GMT"
+        },
+        {
+          "content-length": "4853"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 13:44:04 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3289"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "max-age=62901702"
+        },
+        {
+          "expires": "Sat, 01 Nov 2014 14:19:50 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:08 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 16:55:01 GMT"
+        },
+        {
+          "content-length": "7105"
+        },
+        {
+          "cache-control": "max-age=62911093"
+        },
+        {
+          "expires": "Sat, 01 Nov 2014 16:56:20 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 14 Sep 2012 07:57:36 GMT"
+        },
+        {
+          "cache-control": "max-age=58732357"
+        },
+        {
+          "expires": "Sun, 14 Sep 2014 08:10:45 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:08 GMT"
+        },
+        {
+          "content-length": "3960"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Tue, 21 Aug 2012 07:36:37 GMT"
+        },
+        {
+          "content-length": "3948"
+        },
+        {
+          "cache-control": "max-age=56656721"
+        },
+        {
+          "expires": "Thu, 21 Aug 2014 07:36:49 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:08 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 09 Mar 2006 21:32:31 GMT"
+        },
+        {
+          "etag": "\"9f40d-3a-40e969d2259c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:08 GMT"
+        },
+        {
+          "content-length": "63"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Mon, 05 Mar 2012 12:15:23 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "59958"
+        },
+        {
+          "cache-control": "max-age=42071869"
+        },
+        {
+          "expires": "Wed, 05 Mar 2014 12:15:56 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "pragma": ""
+        },
+        {
+          "content-length": "479"
+        },
+        {
+          "cache-control": "max-age=30"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:08 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "2493"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:09 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "321"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:09 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:09 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-length": "363"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "212"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:09 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:14 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "P3P - policyref=\"http://www.adfusion.com/w3c/adfusion.xml\", CP=\"NON DSP COR CURa TIA\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "location": "http://www.adfusion.com/AdServer/default.aspx?e=i&lid=10641&ct="
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-length": "188"
+        },
+        {
+          "set-cookie": "cid=6f010485-f507-4a4e-a485-feb7619db013; domain=.adfusion.com; expires=Wed, 01-Jan-2020 06:00:00 GMT; path=/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:14 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "P3P - policyref=\"http://www.adfusion.com/w3c/adfusion.xml\", CP=\"NON DSP COR CURa TIA\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "location": "http://www.adfusion.com/AdServer/default.aspx?e=i&lid=10641&ct="
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-length": "4449"
+        },
+        {
+          "set-cookie": "cid=6f010485-f507-4a4e-a485-feb7619db013; domain=.adfusion.com; expires=Wed, 01-Jan-2020 06:00:00 GMT; path=/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-disposition": "attachment"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:09 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1396"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLuBg59Xwl/EEO1FURBA3cAlgmns+ZjtUnj+OABraDN8bCZzDmjhJGhbKAxEWBcNLyPWU8lPaSzTe300; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:38:09 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas04-1"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:09 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:09 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-length": "173"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "set-cookie": "v=d12d53fe4bd39099b1d991b8bfad50951e1458d396-6634083950951e41834_3366; expires=Sat, 03-Nov-2012 14:08:09 GMT; path=/; domain=.effectivemeasure.net"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "server": "collection10"
+        },
+        {
+          "cache-directive": "no-cache"
+        },
+        {
+          "pragma-directive": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Mon, 09 Jul 2012 15:09:21 GMT"
+        },
+        {
+          "cache-control": "max-age=52968683"
+        },
+        {
+          "expires": "Wed, 09 Jul 2014 15:09:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:09 GMT"
+        },
+        {
+          "content-length": "11937"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:09 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:26:43 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1594"
+        },
+        {
+          "cache-control": "max-age=62707699"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:26:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Mon, 16 Jul 2012 20:54:14 GMT"
+        },
+        {
+          "etag": "\"6d6c23-816c-4c4f8a1e64980\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "33132"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:08 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:22 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 01 Jan 2000 00:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.nedstat.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND NAV COM\""
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "317"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "1222"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "4631"
+        },
+        {
+          "set-cookie": "PRpc=|HrYvHDG1:1|#;domain=ads.pointroll.com; path=/; expires=Mon, 03-Nov-2014 13:38:23 GMT;"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 12:33:21 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "17839"
+        },
+        {
+          "cache-control": "max-age=62987851"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 14:15:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 02:51:54 GMT"
+        },
+        {
+          "cache-control": "max-age=62946923"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 02:53:45 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:22 GMT"
+        },
+        {
+          "content-length": "4827"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 15:48:41 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5891"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=62993480"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 15:49:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 10:44:04 GMT"
+        },
+        {
+          "content-length": "3664"
+        },
+        {
+          "cache-control": "max-age=62975317"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 10:46:59 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 13:08:31 GMT"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5589"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=62813487"
+        },
+        {
+          "expires": "Fri, 31 Oct 2014 13:49:49 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 11:47:26 GMT"
+        },
+        {
+          "content-length": "6787"
+        },
+        {
+          "cache-control": "max-age=62806355"
+        },
+        {
+          "expires": "Fri, 31 Oct 2014 11:50:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 01 Mar 2006 15:13:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "cache-control": "max-age=0, no-cache=Set-Cookie"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "keep-alive": "timeout=10, max=150"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:22 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "cache-control": "private, max-age=30"
+        },
+        {
+          "x-lb-nocache": "true"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-length": "47"
+        },
+        {
+          "keep-alive": "timeout=45"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "1232"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "4657"
+        },
+        {
+          "set-cookie": "PRpc=|HrYwHDG0:1|HrYvHDG1:1|#;domain=ads.pointroll.com; path=/; expires=Mon, 03-Nov-2014 13:38:23 GMT;"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 12:54:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "6289"
+        },
+        {
+          "cache-control": "max-age=62291861"
+        },
+        {
+          "expires": "Sat, 25 Oct 2014 12:56:04 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 05:59:50 GMT"
+        },
+        {
+          "cache-control": "max-age=62871966"
+        },
+        {
+          "expires": "Sat, 01 Nov 2014 06:04:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "content-length": "4071"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 09:39:39 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "6862"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=62885045"
+        },
+        {
+          "expires": "Sat, 01 Nov 2014 09:42:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Mon, 24 Sep 2012 12:45:46 GMT"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4014"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=59612845"
+        },
+        {
+          "expires": "Wed, 24 Sep 2014 12:45:48 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "360"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 05:12:35 GMT"
+        },
+        {
+          "content-length": "6772"
+        },
+        {
+          "cache-control": "max-age=62955857"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 05:22:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-length": "548"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "545"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "545"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Tue, 01 Feb 2011 16:45:17 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 19:49:21 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 19:49:21 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "4447"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "64143"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Tue, 25 Sep 2012 20:37:11 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 20:14:54 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 20:14:54 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "2993"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "62610"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:38:21 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "103860"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-action": "MISS"
+        },
+        {
+          "x-cache-age": "0"
+        },
+        {
+          "cache-control": "private, max-age=0, must-revalidate"
+        },
+        {
+          "x-lb-nocache": "true"
+        },
+        {
+          "vary": "X-CDN"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 18:53:42 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 21:59:32 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 21:59:32 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "5619"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "56332"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 18:54:38 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 21:59:33 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 21:59:33 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "5379"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "56331"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Fri, 05 Nov 2010 18:11:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 08:14:05 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:14:05 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "19459"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "content-type": "text/plain"
+        },
+        {
+          "content-length": "3528"
+        },
+        {
+          "cache-control": "private, max-age=506694"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-disposition": "attachment"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "882"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 07:32:16 GMT"
+        },
+        {
+          "cache-control": "max-age=62963730"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 07:33:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "content-length": "6112"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 02:53:00 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "6904"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=62947059"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 02:56:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 23:54:38 GMT"
+        },
+        {
+          "content-length": "6467"
+        },
+        {
+          "cache-control": "max-age=62677369"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 00:01:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 01:56:44 GMT"
+        },
+        {
+          "content-length": "5790"
+        },
+        {
+          "cache-control": "max-age=62943937"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 02:04:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Thu, 15 Dec 2011 11:42:59 GMT"
+        },
+        {
+          "content-length": "13166"
+        },
+        {
+          "cache-control": "max-age=60312573"
+        },
+        {
+          "expires": "Thu, 02 Oct 2014 15:07:56 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 19:47:01 GMT"
+        },
+        {
+          "content-length": "6715"
+        },
+        {
+          "cache-control": "max-age=63007794"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 19:48:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "Sun, 05-Jun-2005 22:00:00 GMT"
+        },
+        {
+          "set-cookie": "u2=0c1d5772-7a75-4913-9e34-91b1ec36e6763Qv0b0; expires=Fri, 01-Feb-2013 09:38:24 GMT; domain=.serving-sys.com; path=/"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"NOI DEVa OUR BUS UNI\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "2370"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 13:38:24 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "17960"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "x-cache-action": "MISS"
+        },
+        {
+          "x-cache-age": "0"
+        },
+        {
+          "cache-control": "max-age=63072000"
+        },
+        {
+          "x-lb-nocache": "true"
+        },
+        {
+          "vary": "X-CDN"
+        },
+        {
+          "last-modified": "Mon, 08 Nov 2010 14:53:56 GMT"
+        },
+        {
+          "keep-alive": "timeout=5, max=713"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 24 May 2012 20:52:06 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 20:45:30 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 20:45:30 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "18377"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "60774"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLuBg59Xwl/EEO1FURBA3cAlgmns+ZhxgFZ2Xd28p4N8+qai9qH+vXEtJkM6N3AzKCRseBomFyz6/H0m; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:38:24 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas09-1"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Mon, 08 Nov 2010 14:53:48 GMT"
+        },
+        {
+          "content-length": "17610"
+        },
+        {
+          "cache-control": "max-age=63072000"
+        },
+        {
+          "expires": "Mon, 03 Nov 2014 13:38:23 GMT"
+        },
+        {
+          "vary": "X-CDN"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "keep-alive": "timeout=5, max=793"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "server": "Omniture DC/2.0.0"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "set-cookie": "s_vi=[CS]v1|284A8F0905160D8B-600001A1C0108CD4[CE]; Expires=Thu,  2 Nov 2017 13:38:24 GMT; Domain=sa.bbc.com; Path=/"
+        },
+        {
+          "x-c": "ms-4.4.9"
+        },
+        {
+          "expires": "Fri, 02 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "last-modified": "Sun, 04 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "cache-control": "no-cache, no-store, max-age=0, no-transform, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "etag": "\"50951E50-1A84-669E56BC\""
+        },
+        {
+          "vary": "*"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA OUR IND COM NAV STA\""
+        },
+        {
+          "xserver": "www665"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "keep-alive": "timeout=15"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 00:08:06 GMT"
+        },
+        {
+          "cache-control": "max-age=62937399"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 00:15:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "content-length": "7180"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 15:54:41 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4902"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=62848278"
+        },
+        {
+          "expires": "Fri, 31 Oct 2014 23:29:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 23:33:51 GMT"
+        },
+        {
+          "content-length": "3930"
+        },
+        {
+          "cache-control": "max-age=62935245"
+        },
+        {
+          "expires": "Sat, 01 Nov 2014 23:39:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-length": "173"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "set-cookie": "v=72911efde47ed7df994991b8bfad50951e1458d396-6634083950951e50834_3366; expires=Sat, 03-Nov-2012 14:08:24 GMT; path=/; domain=.effectivemeasure.net"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "server": "collection10"
+        },
+        {
+          "cache-directive": "no-cache"
+        },
+        {
+          "pragma-directive": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 23:02:53 GMT"
+        },
+        {
+          "content-length": "3589"
+        },
+        {
+          "cache-control": "max-age=62846973"
+        },
+        {
+          "expires": "Fri, 31 Oct 2014 23:07:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "content-type": "text/plain"
+        },
+        {
+          "content-length": "15586"
+        },
+        {
+          "cache-control": "private, max-age=506675"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "20151"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 17:13:44 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"0544416d4b2cd1:b56\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Mon, 05 Mar 2012 12:20:37 GMT"
+        },
+        {
+          "cache-control": "max-age=42072135"
+        },
+        {
+          "expires": "Wed, 05 Mar 2014 12:20:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:22 GMT"
+        },
+        {
+          "content-length": "59958"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 08:26:48 GMT"
+        },
+        {
+          "cache-control": "max-age=62707710"
+        },
+        {
+          "expires": "Thu, 30 Oct 2014 08:26:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "content-length": "189"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 26 Oct 2012 15:18:41 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5471"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=62387032"
+        },
+        {
+          "expires": "Sun, 26 Oct 2014 15:22:16 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 15 Nov 2011 09:49:33 GMT"
+        },
+        {
+          "content-length": "3645"
+        },
+        {
+          "cache-control": "max-age=59275525"
+        },
+        {
+          "expires": "Sat, 20 Sep 2014 15:03:49 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 01:30:29 GMT"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5937"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "max-age=62942201"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 01:35:04 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 17:42:14 GMT"
+        },
+        {
+          "cache-control": "max-age=63000297"
+        },
+        {
+          "expires": "Sun, 02 Nov 2014 17:43:20 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "content-length": "6807"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "x-pad": "avoid browser bug"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 23:03:40 GMT"
+        },
+        {
+          "content-length": "4382"
+        },
+        {
+          "cache-control": "max-age=62846946"
+        },
+        {
+          "expires": "Fri, 31 Oct 2014 23:07:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 23:49:59 GMT"
+        },
+        {
+          "content-length": "5299"
+        },
+        {
+          "cache-control": "max-age=62768266"
+        },
+        {
+          "expires": "Fri, 31 Oct 2014 01:16:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:24 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:26 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "content-length": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-type": "text/plain; charset=utf-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:26 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "content-length": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-type": "text/plain; charset=utf-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "29965"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 17:12:34 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"02d8becd3b2cd1:b56\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:31 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "content-length": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-type": "text/plain; charset=utf-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:31 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 01 Jan 2000 00:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.nedstat.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND NAV COM\""
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:31 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 01 Mar 2006 15:13:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "cache-control": "max-age=0, no-cache=Set-Cookie"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:38:31 GMT"
+        },
+        {
+          "keep-alive": "timeout=10, max=91"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "312"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:32 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "1216"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:32 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:32 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "4631"
+        },
+        {
+          "set-cookie": "PRpc=|HrYwHDG0:1|HrYvHDG1:2|#;domain=ads.pointroll.com; path=/; expires=Mon, 03-Nov-2014 13:38:32 GMT;"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "322"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 20 Jul 2012 11:50:26 GMT"
+        },
+        {
+          "cache-control": "max-age=53907180"
+        },
+        {
+          "expires": "Sun, 20 Jul 2014 11:51:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:32 GMT"
+        },
+        {
+          "content-length": "2149"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-disposition": "attachment"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1660"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "609"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-msadid": "291301914"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:32 GMT"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "2882"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "320"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "50"
+        },
+        {
+          "allow": "GET"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 10:23:48 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "nginx/0.8.54"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:26 GMT"
+        },
+        {
+          "content-type": "application/xml"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "set-cookie": "pixel_f52666608=1; Domain=.dimestore.com; Expires=Sun, 03-Nov-2013 13:38:26 GMT"
+        },
+        {
+          "location": "http://content.dimestore.com/pixel.gif"
+        },
+        {
+          "content-length": "0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:38:30 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "91710"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cache-action": "MISS"
+        },
+        {
+          "x-cache-age": "0"
+        },
+        {
+          "cache-control": "private, max-age=0, must-revalidate"
+        },
+        {
+          "x-lb-nocache": "true"
+        },
+        {
+          "vary": "X-CDN"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "538"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "356"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "539"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-length": "545"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:34 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "content-length": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-type": "text/plain; charset=utf-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "321"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:34 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "18600"
+        },
+        {
+          "allow": "GET"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:03:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:34 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "Sun, 05-Jun-2005 22:00:00 GMT"
+        },
+        {
+          "set-cookie": "u2=0c1d5772-7a75-4913-9e34-91b1ec36e6763Qv0bg; expires=Fri, 01-Feb-2013 09:38:34 GMT; domain=.serving-sys.com; path=/"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"NOI DEVa OUR BUS UNI\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:33 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "245"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:34 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/0.6.35"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "49"
+        },
+        {
+          "last-modified": "Fri, 05 Aug 2011 18:45:32 GMT"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:34 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:35 GMT"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 19 Sep 2012 23:30:39 GMT"
+        },
+        {
+          "etag": "\"bed15b-d00-4ca1664f965c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "3328"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:35 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:37 GMT"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "set-cookie": "BMX_3PC=1; path=/; domain=.voicefive.com;"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI COR NID CUR DEV TAI PSA IVA OUR STA UNI NAV INT\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "vary": "User-Agent,Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8240"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:38:37 GMT"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:37 GMT"
+        },
+        {
+          "content-length": "392"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "set-cookie": "CMDD=AAIWeQE*;domain=casalemedia.com;path=/;expires=Sun, 04 Nov 2012 13:38:37 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLuBg59Xwl/EEO1FURBA3cAlgmns+bAxJsyTL2hw/WD8n92ZW/I4JwcH7E4ANXFCKgXlxsjUzY2F7dfMxN21mUJt+5Zxhw==; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:38:37 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas02-1"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:37 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:37 GMT"
+        },
+        {
+          "server": "Omniture DC/2.0.0"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "set-cookie": "s_vi=[CS]v1|284A8F0905160D8B-600001A1C0108CD4[CE]; Expires=Thu,  2 Nov 2017 13:38:37 GMT; Domain=sa.bbc.com; Path=/"
+        },
+        {
+          "x-c": "ms-4.4.9"
+        },
+        {
+          "expires": "Fri, 02 Nov 2012 13:38:37 GMT"
+        },
+        {
+          "last-modified": "Sun, 04 Nov 2012 13:38:37 GMT"
+        },
+        {
+          "cache-control": "no-cache, no-store, max-age=0, no-transform, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "etag": "\"50951E5D-2288-2D7FF956\""
+        },
+        {
+          "vary": "*"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA OUR IND COM NAV STA\""
+        },
+        {
+          "xserver": "www381"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "keep-alive": "timeout=15"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:37 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-length": "173"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "set-cookie": "v=b90bb042855f902565c991b8bfad50951e1458d396-6634083950951e5d834_3366; expires=Sat, 03-Nov-2012 14:08:37 GMT; path=/; domain=.effectivemeasure.net"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "server": "collection10"
+        },
+        {
+          "cache-directive": "no-cache"
+        },
+        {
+          "pragma-directive": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:37 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache/2.2.19 (Unix)"
+        },
+        {
+          "content-type": "application/json;charset=UTF-8"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "access-control-allow-methods": "POST, GET, PUT, OPTIONS"
+        },
+        {
+          "access-control-allow-headers": "Content-Type, X-Requested-With, *"
+        },
+        {
+          "content-length": "112"
+        },
+        {
+          "cache-control": "max-age=17"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:37 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 24 Jun 2005 22:51:33 GMT"
+        },
+        {
+          "etag": "\"fec19c-1b-3fa51a4b8c740\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "38"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:37 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 15:55:36 GMT"
+        },
+        {
+          "etag": "\"4016f-8a-4cca7e25a0e00\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "135"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:38 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "273"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:38 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/2179194/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:38:38 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:41 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "content-length": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-type": "text/plain; charset=utf-8"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_24.json b/jetty-http2/http2-hpack/src/test/resources/data/story_24.json
new file mode 100644
index 0000000..c294895
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_24.json
@@ -0,0 +1,1187 @@
+{
+  "context": "response",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "location": "http://www.craigslist.org/about/sites/"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:34:16 GMT"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=14400"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 10:03:45 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 10:03:45 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "10344"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 10:03:45 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Tue, 16 Oct 2012 22:03:00 GMT"
+        },
+        {
+          "date": "Tue, 16 Oct 2012 22:03:00 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "1099"
+        },
+        {
+          "content-type": "text/css; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 15 Nov 2012 22:03:00 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=15, public"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:33:59 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:33:59 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "6344"
+        },
+        {
+          "content-type": "text/javascript; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:34:14 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 06:13:28 GMT"
+        },
+        {
+          "date": "Tue, 09 Oct 2012 06:13:28 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "28017"
+        },
+        {
+          "content-type": "text/javascript; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 06:13:28 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "last-modified": "Mon, 23 Jun 2008 23:06:11 GMT"
+        },
+        {
+          "cache-control": "public, max-age=315360000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Tue, 09 Oct 2012 05:33:00 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "1150"
+        },
+        {
+          "content-type": "text/plain"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Fri, 07 Oct 2022 05:33:00 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=3600, public"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 12:36:54 GMT"
+        },
+        {
+          "set-cookie": "cl_def_hp=shoals; domain=.craigslist.org; path=/; expires=Sun, 03-Nov-13 12:36:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:36:54 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "6245"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:36:54 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:53:37 GMT"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 20:53:37 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "6047"
+        },
+        {
+          "content-type": "text/css; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 01 Dec 2012 20:53:37 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:53:17 GMT"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 20:53:17 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "6344"
+        },
+        {
+          "content-type": "text/javascript; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 01 Dec 2012 20:53:17 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 06:01:51 GMT"
+        },
+        {
+          "date": "Tue, 09 Oct 2012 06:01:51 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "473"
+        },
+        {
+          "content-type": "text/javascript; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 06:01:51 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 06:01:55 GMT"
+        },
+        {
+          "date": "Tue, 09 Oct 2012 06:01:55 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "28017"
+        },
+        {
+          "content-type": "text/javascript; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 06:01:55 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "last-modified": "Mon, 23 Jun 2008 23:06:11 GMT"
+        },
+        {
+          "cache-control": "public, max-age=315360000"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Tue, 09 Oct 2012 05:33:00 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "1150"
+        },
+        {
+          "content-type": "text/plain"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Fri, 07 Oct 2022 05:33:00 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=600"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:34:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:34:21 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "9660"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:49:21 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 06:21:38 GMT"
+        },
+        {
+          "date": "Tue, 09 Oct 2012 06:21:38 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "225"
+        },
+        {
+          "content-type": "text/javascript; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 06:21:38 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 07:20:18 GMT"
+        },
+        {
+          "date": "Tue, 09 Oct 2012 07:20:18 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "2857"
+        },
+        {
+          "content-type": "text/javascript; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 07:20:18 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 21:50:50 GMT"
+        },
+        {
+          "date": "Tue, 30 Oct 2012 21:50:50 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "1398"
+        },
+        {
+          "content-type": "text/javascript; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 29 Nov 2012 21:50:50 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 21:26:48 GMT"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 21:26:48 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "40353"
+        },
+        {
+          "content-type": "text/javascript; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 01 Dec 2012 21:26:48 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 21:46:27 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 21:46:27 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "2458"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sun, 02 Dec 2012 21:46:27 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Mon, 15 Oct 2012 22:07:17 GMT"
+        },
+        {
+          "date": "Mon, 15 Oct 2012 22:07:17 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "727"
+        },
+        {
+          "content-type": "text/javascript; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Wed, 14 Nov 2012 22:07:17 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=2592000"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 21:46:28 GMT"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=2592000"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 21:46:27 GMT"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=2592000"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 21:46:28 GMT"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=2592000"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 21:46:27 GMT"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=600"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:34:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:34:17 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "10780"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:49:18 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:17:50 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:17:50 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "2245"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 13:17:50 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=2592000"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 22:00:11 GMT"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=2592000"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 27 Oct 2012 12:09:05 GMT"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=2592000"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 27 Oct 2012 07:36:12 GMT"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=2592000"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Mon, 22 Oct 2012 22:00:12 GMT"
+        },
+        {
+          "server": "Apache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=14400"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 12:53:23 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:53:23 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "5733"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 12:53:23 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=14400"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 10:58:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 10:58:55 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "1480"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 10:58:55 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "public, max-age=14400"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 09:53:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 09:53:38 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "10400"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Mon, 03 Dec 2012 09:53:38 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "max-age=2592000, public"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 06:01:47 GMT"
+        },
+        {
+          "date": "Tue, 09 Oct 2012 06:01:47 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "4861"
+        },
+        {
+          "content-type": "text/css; charset=iso-8859-1"
+        },
+        {
+          "x-frame-options": "Allow-From https://forums.craigslist.org"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 06:01:47 GMT"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_25.json b/jetty-http2/http2-hpack/src/test/resources/data/story_25.json
new file mode 100644
index 0000000..a974038
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_25.json
@@ -0,0 +1,8637 @@
+{
+  "context": "response",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":status": "301"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "etag": ""
+        },
+        {
+          "last-modified": "Sat, 3 Nov 2012 13:18:25 GMT"
+        },
+        {
+          "location": "http://www.ebay.com"
+        },
+        {
+          "rlogid": "p4fug%60fvehq%60%3C%3Dsm%2Bpu56*a37%3Fb0%60-13ac6788085"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:56 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "nonsession=CgAFMABhSdlBNNTA5NTFjY2QuMC4xLjEuMTQ5LjMuMC4xAMoAIFn7Hk1jNjc4ODNmMTEzYTBhNTY5NjRlNjQ2YzZmZmFhMWFjMQDLAAFQlSPVMX8u5Z8*; Domain=.ebay.com; Expires=Sun, 03-Nov-2013 13:31:57 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-length": "14114"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:56 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:58 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 00:07:22 GMT"
+        },
+        {
+          "etag": "\"558014-cc3-4cd77eb75a280\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "3267"
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4n%60rujfudlwc%3D9vt*ts67.g15ea31-13ac67885c6-0x1a1"
+        },
+        {
+          "set-cookie": "npii=bcguid/c67885c613a0a0a9f6568b16ff5917ee5276504e^tguid/c67883f113a0a56964e646c6ffaa1ac15276504e^; Domain=.ebay.com; Expires=Sun, 03-Nov-2013 13:31:58 GMT; Path=/"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa ADMa DEVa PSDo PSAa OUR SAMo IND UNI COM NAV INT STA DEM PRE\""
+        },
+        {
+          "cache-control": "private, no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:57 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:58 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 15:31:45 GMT"
+        },
+        {
+          "last-modified": "Sat, 16 Aug 2003 20:42:27 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80fb69e73664c31:6fe\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "49"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:58 GMT"
+        },
+        {
+          "last-modified": "Thu, 18 Oct 2012 23:53:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80183dd68badcd1:720\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "2539"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:58 GMT"
+        },
+        {
+          "expires": "Mon, 10 Dec 2012 22:14:50 GMT"
+        },
+        {
+          "last-modified": "Tue, 10 Jul 2012 00:18:56 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "219"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:58 GMT"
+        },
+        {
+          "last-modified": "Tue, 10 Jul 2012 00:17:53 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"808e7072315ecd1:5f1\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "201"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:58 GMT"
+        },
+        {
+          "last-modified": "Wed, 18 Jul 2012 20:33:33 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "1623"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9vl*th%7Fbad%7F72%3D-13ac6788850-0x16f"
+        },
+        {
+          "cache-control": "private, max-age=86400"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:58 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9ve*t%28747%60e%7E6-13ac678862e-0xfb"
+        },
+        {
+          "cache-control": "private, max-age=86400"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:57 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9ve*t%28747%60e%7Eb-13ac6788670-0x141"
+        },
+        {
+          "cache-control": "private, max-age=86400"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:57 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9u%7E*t%28750g07p-13a4b38c06e-0x161"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 23:50:57 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "4212"
+        },
+        {
+          "cache-control": "max-age=29468235"
+        },
+        {
+          "expires": "Thu, 10 Oct 2013 15:09:13 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9u%7E*t%28750g07%3B-13a65e7cdbc-0x16b"
+        },
+        {
+          "last-modified": "Mon, 15 Oct 2012 19:11:16 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "5662"
+        },
+        {
+          "cache-control": "max-age=29915920"
+        },
+        {
+          "expires": "Tue, 15 Oct 2013 19:30:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9u%7E*t%28750g07m-13abdd493d5-0x16d"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:39:43 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "9184"
+        },
+        {
+          "cache-control": "max-age=31391044"
+        },
+        {
+          "expires": "Fri, 01 Nov 2013 21:16:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 31 Jan 2012 02:58:34 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-length": "3282"
+        },
+        {
+          "cache-control": "max-age=23333195"
+        },
+        {
+          "expires": "Wed, 31 Jul 2013 14:58:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 06 Sep 2012 16:51:31 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "5440"
+        },
+        {
+          "cache-control": "max-age=26536771"
+        },
+        {
+          "expires": "Fri, 06 Sep 2013 16:51:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 19:15:19 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5148"
+        },
+        {
+          "cache-control": "max-age=31458789"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 16:05:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 27 Sep 2010 09:07:24 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5679"
+        },
+        {
+          "cache-control": "public, max-age=31536000"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:31:59 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 19:19:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7150"
+        },
+        {
+          "cache-control": "max-age=31468533"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 18:47:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 05:51:41 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-length": "5679"
+        },
+        {
+          "cache-control": "max-age=30644382"
+        },
+        {
+          "expires": "Thu, 24 Oct 2013 05:51:41 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 21:24:39 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5636"
+        },
+        {
+          "cache-control": "max-age=31450187"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 13:41:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 23:03:23 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7177"
+        },
+        {
+          "cache-control": "max-age=31488510"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 00:20:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 18:41:38 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "8702"
+        },
+        {
+          "cache-control": "max-age=31321403"
+        },
+        {
+          "expires": "Fri, 01 Nov 2013 01:55:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 14:29:39 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "13046"
+        },
+        {
+          "cache-control": "max-age=29637746"
+        },
+        {
+          "expires": "Sat, 12 Oct 2013 14:14:25 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9u%7E*tm63.%3C72om6%3E-13abcb35937-0x14e"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 21:07:34 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "60906"
+        },
+        {
+          "cache-control": "max-age=31372049"
+        },
+        {
+          "expires": "Fri, 01 Nov 2013 15:59:27 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 18 Sep 2012 01:10:29 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "42205"
+        },
+        {
+          "cache-control": "max-age=27517110"
+        },
+        {
+          "expires": "Wed, 18 Sep 2013 01:10:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 27 Sep 2010 09:07:24 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "53359"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "private, max-age=31536000"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:31:59 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:01 GMT"
+        },
+        {
+          "last-modified": "Tue, 16 Oct 2012 18:06:59 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "2583"
+        },
+        {
+          "etag": "\"80e3ea8c9abcd1:639\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9vl*th%7Fbad%7F710-13ac67892f3-0x131"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:01 GMT"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "set-cookie": "PS=T.0; Domain=main.ebayrtm.com; Expires=Sun, 03-Nov-2013 13:32:01 GMT; Path=/rtm"
+        },
+        {
+          "location": "http://srx.main.ebayrtm.com/rtm?RtmCmd&a=json&l=@@__@@__@@&g=c67883f113a0a56964e646c6ffaa1ac1&c=1H4sIAAAAAAAAAB2OwWrCQBCG74LvMOClLXR3Zsckm8gevLR4SEuJhx5ySdMVg6krujXap%2B8kMDDD%2F%2F18zKJqIryFKyADpgVTkWRQVlswSGY%2BOzGjK8Nf1%2FeNThTCQ9m03TGGy34Fm2P0PUgA7xV8AqGyKzhf64JShY%2Fw6ttD0OJBGYKX7ux34aZHKGIyTlbbfTu29S9KR0LDS%2Fec5yO27BLKs%2BkkJw6cvjFuH%2BOpLrQehkH5r%2Bau2vAzIWkxKjK5Sq3KeMxs5vzmY90flk%2Fz2T9Cveg66wAAAA%3D%3D&p=11527:11528:11529&di=11527:11528:11529&v=4&enc=UTF-8&bm=286807&ord=1351949521580&cg=c67885c613a0a0a9f6568b16ff5917ee&cb=vjo.dsf.assembly.VjClientAssembler._callback0&_vrdm=1351949521581&r=yes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:01 GMT"
+        },
+        {
+          "expires": "Mon, 10 Dec 2012 02:40:28 GMT"
+        },
+        {
+          "last-modified": "Thu, 09 Jul 2009 18:33:39 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "1406"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:01 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:56 GMT"
+        },
+        {
+          "last-modified": "Mon, 13 Jul 2009 22:43:33 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"8018ba59b4ca1:5d2\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "386"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 09 Jul 2009 18:33:41 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "3155"
+        },
+        {
+          "etag": "\"80e3ea8c9abcd1:639\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9vl*th%7Fbad%7F715-13ac6789351-0x12a"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "4320"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:01 GMT"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "set-cookie": "HT=1351949521580%0211529%04287876%06261345%0311528%04286823%06260443%0311527%04286801%06203908; Domain=main.ebayrtm.com; Path=/rtm"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:01 GMT"
+        },
+        {
+          "expires": "Mon, 26 Nov 2012 01:07:49 GMT"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 18:48:16 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "92574"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:01 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:55 GMT"
+        },
+        {
+          "last-modified": "Thu, 29 Mar 2012 22:36:00 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"048ac50fcdcd1:603\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "35128"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9u%7E*t%28750g07m-133f0e5fedc-0x142"
+        },
+        {
+          "last-modified": "Tue, 29 Nov 2011 19:19:17 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-length": "545"
+        },
+        {
+          "cache-control": "max-age=30563305"
+        },
+        {
+          "expires": "Wed, 23 Oct 2013 07:20:26 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:02 GMT"
+        },
+        {
+          "expires": "Tue, 18 Dec 2012 00:25:08 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 00:26:02 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "22428"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:02 GMT"
+        },
+        {
+          "expires": "Tue, 18 Dec 2012 00:25:05 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 00:28:28 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "33895"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:02 GMT"
+        },
+        {
+          "expires": "Mon, 17 Dec 2012 16:09:55 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 18:41:38 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "38761"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:02 GMT"
+        },
+        {
+          "expires": "Mon, 17 Dec 2012 15:00:24 GMT"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 14:28:46 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "60078"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9u%7E*t%28750d%7F23-13abd599c11-0x167"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 20:14:10 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "72619"
+        },
+        {
+          "cache-control": "max-age=31382938"
+        },
+        {
+          "expires": "Fri, 01 Nov 2013 19:00:59 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9u%7E*tm63.%3C72om6%3E-13ac20abb51-0x14c"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 16:52:54 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "69800"
+        },
+        {
+          "cache-control": "max-age=31461635"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 16:52:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "nonsession=CgAFMABhSdlBNNTA5NTFjY2QuMC4xLjEuMTQ5LjMuMC4xAMoAIFn7Hk1jNjc4ODNmMTEzYTBhNTY5NjRlNjQ2YzZmZmFhMWFjMQDLAAFQlSPVMX8u5Z8*; Domain=.ebay.com; Expires=Sun, 03-Nov-2013 13:31:57 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "content-length": "1150"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:06 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 15:31:45 GMT"
+        },
+        {
+          "last-modified": "Sat, 16 Aug 2003 20:42:27 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "49"
+        },
+        {
+          "etag": "\"80fb69e73664c31:6fe\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:55 GMT"
+        },
+        {
+          "last-modified": "Thu, 18 Oct 2012 23:53:57 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80183dd68badcd1:720\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "2539"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "last-modified": "Thu, 30 Jul 2009 23:41:29 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "53"
+        },
+        {
+          "etag": "\"80e3ea8c9abcd1:639\""
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 15:14:28 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "expires": "Mon, 26 Nov 2012 05:56:41 GMT"
+        },
+        {
+          "last-modified": "Thu, 30 Jul 2009 23:41:33 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "53"
+        },
+        {
+          "etag": "\"80b4fd446f11ca1:876\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890; Domain=.ebay.com; Expires=Thu, 02-Nov-2017 13:32:07 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-length": "24567"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "expires": "Sun, 16 Dec 2012 17:51:49 GMT"
+        },
+        {
+          "last-modified": "Sun, 26 Feb 2012 07:40:00 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1968"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 16:37:50 GMT"
+        },
+        {
+          "last-modified": "Tue, 07 Jul 2009 20:18:42 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "613"
+        },
+        {
+          "etag": "\"80fb69e73664c31:6fe\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:57 GMT"
+        },
+        {
+          "last-modified": "Thu, 23 Aug 2007 20:40:22 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80183dd68badcd1:720\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "229"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "expires": "Mon, 26 Nov 2012 05:56:41 GMT"
+        },
+        {
+          "last-modified": "Thu, 04 Oct 2007 21:44:36 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "1887"
+        },
+        {
+          "etag": "\"0bad4c1cf6c81:682\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 15:31:45 GMT"
+        },
+        {
+          "last-modified": "Sat, 16 Aug 2003 20:42:27 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80fb69e73664c31:6fe\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "49"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "expires": "Mon, 19 Nov 2012 22:20:32 GMT"
+        },
+        {
+          "last-modified": "Sat, 23 Apr 2011 09:37:40 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3936"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "expires": "Thu, 13 Dec 2012 18:43:54 GMT"
+        },
+        {
+          "last-modified": "Thu, 09 Sep 2010 17:31:30 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4059"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "expires": "Thu, 13 Dec 2012 18:24:38 GMT"
+        },
+        {
+          "last-modified": "Thu, 09 Sep 2010 17:15:08 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4397"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 19 Jan 2012 01:26:02 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-length": "10248"
+        },
+        {
+          "cache-control": "max-age=22290839"
+        },
+        {
+          "expires": "Fri, 19 Jul 2013 13:26:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 10 Nov 2011 13:27:41 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-length": "1794"
+        },
+        {
+          "cache-control": "max-age=16286156"
+        },
+        {
+          "expires": "Sat, 11 May 2013 01:28:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sat, 19 May 2012 13:20:12 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2031"
+        },
+        {
+          "cache-control": "max-age=4358180"
+        },
+        {
+          "expires": "Mon, 24 Dec 2012 00:08:27 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 14:04:13 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2394"
+        },
+        {
+          "cache-control": "max-age=31533954"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 12:58:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 04 Oct 2012 13:43:50 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3680"
+        },
+        {
+          "cache-control": "max-age=31532824"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 12:39:11 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sat, 16 Jun 2012 04:23:57 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3263"
+        },
+        {
+          "cache-control": "max-age=10191966"
+        },
+        {
+          "expires": "Fri, 01 Mar 2013 12:38:13 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Tue, 04 Sep 2012 13:46:03 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2469"
+        },
+        {
+          "cache-control": "max-age=26067492"
+        },
+        {
+          "expires": "Sun, 01 Sep 2013 06:30:19 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Sat, 17 Dec 2011 17:14:44 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "2521"
+        },
+        {
+          "cache-control": "max-age=19496613"
+        },
+        {
+          "expires": "Mon, 17 Jun 2013 05:15:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 15 Dec 2011 00:35:12 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-length": "9133"
+        },
+        {
+          "cache-control": "max-age=19263809"
+        },
+        {
+          "expires": "Fri, 14 Jun 2013 12:35:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 14:11:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3761"
+        },
+        {
+          "cache-control": "max-age=31534424"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:05:51 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sun, 23 Sep 2012 00:16:12 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2956"
+        },
+        {
+          "cache-control": "max-age=27524859"
+        },
+        {
+          "expires": "Wed, 18 Sep 2013 03:19:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 14:13:08 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2236"
+        },
+        {
+          "cache-control": "max-age=31105641"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 13:59:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 07 May 2012 03:34:14 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1500"
+        },
+        {
+          "cache-control": "max-age=4657042"
+        },
+        {
+          "expires": "Thu, 27 Dec 2012 11:09:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Wed, 17 Oct 2012 21:46:06 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2528"
+        },
+        {
+          "cache-control": "max-age=28697569"
+        },
+        {
+          "expires": "Tue, 01 Oct 2013 17:04:56 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Tue, 02 Oct 2012 21:27:24 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1930"
+        },
+        {
+          "cache-control": "max-age=28812498"
+        },
+        {
+          "expires": "Thu, 03 Oct 2013 01:00:25 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sat, 20 Oct 2012 02:57:28 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1722"
+        },
+        {
+          "cache-control": "max-age=30889406"
+        },
+        {
+          "expires": "Sun, 27 Oct 2013 01:55:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Tue, 17 Jul 2012 14:30:13 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2961"
+        },
+        {
+          "cache-control": "max-age=16431611"
+        },
+        {
+          "expires": "Sun, 12 May 2013 17:52:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 14:14:04 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2160"
+        },
+        {
+          "cache-control": "max-age=30945129"
+        },
+        {
+          "expires": "Sun, 27 Oct 2013 17:24:16 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 04 Oct 2012 14:11:43 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2841"
+        },
+        {
+          "cache-control": "max-age=30221178"
+        },
+        {
+          "expires": "Sat, 19 Oct 2013 08:18:25 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 07 May 2012 13:59:59 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1541"
+        },
+        {
+          "cache-control": "max-age=7259332"
+        },
+        {
+          "expires": "Sat, 26 Jan 2013 14:00:59 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Wed, 06 Jun 2012 14:05:14 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2123"
+        },
+        {
+          "cache-control": "max-age=12334548"
+        },
+        {
+          "expires": "Tue, 26 Mar 2013 07:47:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 04 Oct 2012 14:21:18 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "947"
+        },
+        {
+          "cache-control": "max-age=27136636"
+        },
+        {
+          "expires": "Fri, 13 Sep 2013 15:29:23 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 04 Oct 2012 14:45:15 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1846"
+        },
+        {
+          "cache-control": "max-age=29813010"
+        },
+        {
+          "expires": "Mon, 14 Oct 2013 14:55:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 14:17:41 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2166"
+        },
+        {
+          "cache-control": "max-age=30506466"
+        },
+        {
+          "expires": "Tue, 22 Oct 2013 15:33:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:08 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 04 Oct 2012 13:48:57 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3964"
+        },
+        {
+          "cache-control": "max-age=31161579"
+        },
+        {
+          "expires": "Wed, 30 Oct 2013 05:31:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:08 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 04 Oct 2012 13:43:32 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4653"
+        },
+        {
+          "cache-control": "max-age=30556712"
+        },
+        {
+          "expires": "Wed, 23 Oct 2013 05:30:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:08 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 11 Sep 2012 18:42:51 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "31476"
+        },
+        {
+          "cache-control": "max-age=26975444"
+        },
+        {
+          "expires": "Wed, 11 Sep 2013 18:42:51 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Sat, 17 Dec 2011 17:14:45 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "11181"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "max-age=19496528"
+        },
+        {
+          "expires": "Mon, 17 Jun 2013 05:14:15 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 04 Oct 2012 14:19:33 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5580"
+        },
+        {
+          "cache-control": "max-age=31535661"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:26:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9vl*th%7Fbad%7F714-13ac678af80-0x141"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "903"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:08 GMT"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "set-cookie": "HT=1351949521580%0211529%04287876%06261345%0311528%04286823%06260443%0311527%04286801%06203908%011351949527269%02981%04-1%060%03980%04-1%060%03979%04-1%060%03688%04-1%060%03255%04-1%060; Domain=main.ebayrtm.com; Path=/rtm"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:09 GMT"
+        },
+        {
+          "expires": "Mon, 17 Dec 2012 16:40:39 GMT"
+        },
+        {
+          "last-modified": "Wed, 10 Jun 2009 16:58:56 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80fb69e73664c31:6fe\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "1161"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:09 GMT"
+        },
+        {
+          "expires": "Wed, 28 Nov 2012 05:06:23 GMT"
+        },
+        {
+          "last-modified": "Sat, 16 May 2009 01:16:00 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "1180"
+        },
+        {
+          "etag": "\"04864dfc3d5c91:5b1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "66"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Fri, 19 Jun 2009 17:50:32 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"064b8706f1c91:682\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:09 GMT"
+        },
+        {
+          "expires": "Sat, 27 Oct 2012 08:29:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:09 GMT"
+        },
+        {
+          "expires": "Thu, 13 Dec 2012 19:35:46 GMT"
+        },
+        {
+          "last-modified": "Thu, 17 Mar 2011 21:09:01 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "13713"
+        },
+        {
+          "etag": "\"0bad4c1cf6c81:682\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 14 Feb 2012 08:00:47 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "53098"
+        },
+        {
+          "cache-control": "max-age=24560912"
+        },
+        {
+          "expires": "Wed, 14 Aug 2013 20:00:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 23:46:15 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "27209"
+        },
+        {
+          "cache-control": "max-age=31054448"
+        },
+        {
+          "expires": "Mon, 28 Oct 2013 23:46:15 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "397"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:10 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:10 GMT"
+        },
+        {
+          "expires": "Sat, 15 Dec 2012 09:56:28 GMT"
+        },
+        {
+          "last-modified": "Sat, 16 Aug 2003 20:42:27 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "49"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:10 GMT"
+        },
+        {
+          "last-modified": "Mon, 08 Mar 2010 19:32:35 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "146"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 20:38:55 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1918"
+        },
+        {
+          "cache-control": "max-age=30051310"
+        },
+        {
+          "expires": "Thu, 17 Oct 2013 09:07:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 18:00:33 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 18:12:07 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 18:12:07 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "26810"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "69603"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "content-length": "1150"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:11 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "expires": "Mon, 26 Nov 2012 00:29:24 GMT"
+        },
+        {
+          "last-modified": "Fri, 18 Mar 2005 23:03:54 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "260"
+        },
+        {
+          "etag": "\"04864dfc3d5c91:5b1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "expires": "Tue, 11 Dec 2012 20:45:48 GMT"
+        },
+        {
+          "last-modified": "Fri, 27 Aug 2010 00:22:53 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80183dd68badcd1:720\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "366"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "last-modified": "Fri, 18 Mar 2005 23:04:39 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "172"
+        },
+        {
+          "etag": "\"80e3ea8c9abcd1:639\""
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 16:57:28 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890; Domain=.ebay.com; Expires=Thu, 02-Nov-2017 13:32:12 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-length": "90925"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:11 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "expires": "Thu, 13 Dec 2012 19:00:40 GMT"
+        },
+        {
+          "last-modified": "Thu, 09 Sep 2010 17:31:30 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "11504"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "expires": "Thu, 13 Dec 2012 19:00:40 GMT"
+        },
+        {
+          "last-modified": "Thu, 09 Sep 2010 17:31:30 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "11504"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 18 Oct 2012 22:46:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3962"
+        },
+        {
+          "cache-control": "max-age=29068398"
+        },
+        {
+          "expires": "Sun, 06 Oct 2013 00:05:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:53:50 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4670"
+        },
+        {
+          "cache-control": "max-age=31276058"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 13:19:50 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 01:52:55 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5679"
+        },
+        {
+          "cache-control": "max-age=28834042"
+        },
+        {
+          "expires": "Thu, 03 Oct 2013 06:59:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Wed, 25 Jul 2012 20:50:41 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3929"
+        },
+        {
+          "cache-control": "max-age=17978904"
+        },
+        {
+          "expires": "Thu, 30 May 2013 15:40:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 06 Aug 2012 06:13:28 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2473"
+        },
+        {
+          "cache-control": "max-age=22036778"
+        },
+        {
+          "expires": "Tue, 16 Jul 2013 14:51:50 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 27 Sep 2010 09:07:24 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "6172"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "private, max-age=31536000"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:32:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 16 Mar 2012 00:40:23 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "52950"
+        },
+        {
+          "cache-control": "max-age=27212892"
+        },
+        {
+          "expires": "Sat, 14 Sep 2013 12:40:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sun, 27 May 2012 12:26:18 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6163"
+        },
+        {
+          "cache-control": "max-age=17941535"
+        },
+        {
+          "expires": "Thu, 30 May 2013 05:17:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 13 Aug 2012 06:52:35 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4526"
+        },
+        {
+          "cache-control": "max-age=24498920"
+        },
+        {
+          "expires": "Wed, 14 Aug 2013 02:47:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 27 Sep 2010 09:07:24 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "48924"
+        },
+        {
+          "cache-control": "public, max-age=31536000"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:32:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:14 GMT"
+        },
+        {
+          "expires": "Mon, 17 Dec 2012 16:40:39 GMT"
+        },
+        {
+          "last-modified": "Wed, 19 Oct 2011 00:48:19 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80fb69e73664c31:6fe\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "275"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "1145"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Tue, 15 Feb 2011 17:36:11 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80b7dad536cdcb1:854\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:14 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:55 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:14 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:59:25 GMT"
+        },
+        {
+          "last-modified": "Wed, 02 Feb 2011 19:43:17 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80183dd68badcd1:720\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "3546"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:14 GMT"
+        },
+        {
+          "expires": "Thu, 27 Sep 2012 23:15:40 GMT"
+        },
+        {
+          "last-modified": "Fri, 21 Jan 2011 20:52:52 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "6342"
+        },
+        {
+          "etag": "\"0aa782badb9cb1:682\""
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:14 GMT"
+        },
+        {
+          "last-modified": "Thu, 09 Sep 2010 03:40:20 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "10896"
+        },
+        {
+          "etag": "\"80e3ea8c9abcd1:639\""
+        },
+        {
+          "expires": "Sun, 16 Dec 2012 05:44:38 GMT"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:14 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:57 GMT"
+        },
+        {
+          "last-modified": "Fri, 22 Jun 2012 22:09:21 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "59719"
+        },
+        {
+          "etag": "\"04864dfc3d5c91:5b1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:14 GMT"
+        },
+        {
+          "last-modified": "Fri, 22 Jun 2012 22:09:21 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "59719"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:57 GMT"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4n%60rujfudlwc%3D9vt*ts67.62d5%3C%3E7-13ac678c943-0x1a4"
+        },
+        {
+          "cache-control": "private, no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/json"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:14 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890; Domain=.ebay.com; Expires=Thu, 02-Nov-2017 13:32:16 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "80046"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:15 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        },
+        {
+          "transaction": "uk.r+607b~`s,RcmdId FindingProductv4,RlogId p4pmiw%60jtb9%3Fuk.r%2B607b%7E%60s-13ac678cb27-0xb7"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Mon, 17 Dec 2012 13:46:54 GMT"
+        },
+        {
+          "last-modified": "Thu, 09 Sep 2010 17:31:30 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "739"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Thu, 22 Nov 2012 14:19:41 GMT"
+        },
+        {
+          "last-modified": "Sat, 23 Apr 2011 09:37:40 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1649"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 06:10:27 GMT"
+        },
+        {
+          "last-modified": "Fri, 18 Mar 2005 23:01:04 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "134"
+        },
+        {
+          "etag": "\"078525ce2cc51:586\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Fri, 14 Dec 2012 05:08:13 GMT"
+        },
+        {
+          "last-modified": "Fri, 21 Oct 2005 18:47:40 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80fb69e73664c31:6fe\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "231"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Sun, 09 Dec 2012 06:52:18 GMT"
+        },
+        {
+          "last-modified": "Tue, 06 Sep 2005 19:37:46 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80183dd68badcd1:720\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "643"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Fri, 26 Oct 2012 04:43:31 GMT"
+        },
+        {
+          "last-modified": "Thu, 06 Oct 2005 21:29:14 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "369"
+        },
+        {
+          "etag": "\"0aa782badb9cb1:682\""
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "199"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Fri, 21 Oct 2005 18:47:42 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80b7dad536cdcb1:854\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:55 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "last-modified": "Wed, 04 Mar 2009 03:12:30 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "2388"
+        },
+        {
+          "etag": "\"80e3ea8c9abcd1:639\""
+        },
+        {
+          "expires": "Sun, 16 Dec 2012 05:44:38 GMT"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Mon, 19 Nov 2012 22:20:46 GMT"
+        },
+        {
+          "last-modified": "Thu, 09 Sep 2010 17:31:30 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1713"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Sun, 11 Nov 2012 06:40:21 GMT"
+        },
+        {
+          "last-modified": "Thu, 09 Sep 2010 17:30:38 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1389"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Mon, 26 Nov 2012 04:31:42 GMT"
+        },
+        {
+          "last-modified": "Fri, 18 Mar 2005 23:04:34 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80fb69e73664c31:6fe\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "141"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:56 GMT"
+        },
+        {
+          "last-modified": "Fri, 18 Mar 2005 23:05:11 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "136"
+        },
+        {
+          "etag": "\"80ad8befe2cc51:8e4\""
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Thu, 29 Nov 2012 16:39:21 GMT"
+        },
+        {
+          "last-modified": "Tue, 02 Aug 2011 07:55:09 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6651"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:16 GMT"
+        },
+        {
+          "expires": "Thu, 06 Dec 2012 09:42:11 GMT"
+        },
+        {
+          "last-modified": "Mon, 28 Mar 2011 14:55:59 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "13825"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "last-modified": "Sat, 16 Aug 2003 20:42:25 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "49"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:57 GMT"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890;Domain=.ebay.com;Expires=Thu, 02-Nov-2017 13:32:21 GMT;Path=/ "
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-length": "80046"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        },
+        {
+          "transaction": "uk.r+607b~`s,RcmdId FindingProductv4,RlogId p4pmiw%60jtb9%3Fuk.r%2B607b%7E%60s-13ac678cb27-0xb7"
+        },
+        {
+          "rlogid": "t6ulcpjqcj9%3Fuk%601d72f%2B12%60b-13ac678e09e-0xc0"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "expires": "Sun, 18 Nov 2012 22:28:04 GMT"
+        },
+        {
+          "last-modified": "Thu, 04 Oct 2007 21:44:39 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "3179"
+        },
+        {
+          "etag": "\"078525ce2cc51:586\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "last-modified": "Fri, 27 Aug 2010 00:22:48 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "391"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 15:13:22 GMT"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "last-modified": "Fri, 18 Mar 2005 23:05:11 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "136"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "last-modified": "Thu, 06 Sep 2007 00:11:47 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "542"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:55 GMT"
+        },
+        {
+          "last-modified": "Thu, 06 Jul 2006 00:07:00 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"0aa151a90a0c61:5e2\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "43"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "last-modified": "Tue, 24 May 2011 22:33:41 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "1351"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 15:13:22 GMT"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "last-modified": "Thu, 06 Sep 2007 00:21:31 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80f7a0df1bf0c71:5b1\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "6386"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 23:31:47 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-length": "3276"
+        },
+        {
+          "cache-control": "max-age=30794366"
+        },
+        {
+          "expires": "Fri, 25 Oct 2013 23:31:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 23:31:47 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-length": "668"
+        },
+        {
+          "cache-control": "max-age=30794366"
+        },
+        {
+          "expires": "Fri, 25 Oct 2013 23:31:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Sat, 14 Jul 2012 00:50:47 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-length": "44"
+        },
+        {
+          "cache-control": "max-age=21813506"
+        },
+        {
+          "expires": "Sun, 14 Jul 2013 00:50:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:56 GMT"
+        },
+        {
+          "last-modified": "Tue, 02 Feb 2010 19:44:14 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "13817"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 27 Sep 2010 09:07:24 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-language": "cs-CZ"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "6172"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "private, max-age=31536000"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:32:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 03:28:30 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-length": "6661"
+        },
+        {
+          "cache-control": "max-age=31413369"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 03:28:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 06 Jul 2012 01:08:44 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-length": "351"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "max-age=21123378"
+        },
+        {
+          "expires": "Sat, 06 Jul 2013 01:08:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 03:07:31 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4553"
+        },
+        {
+          "cache-control": "max-age=29866596"
+        },
+        {
+          "expires": "Tue, 15 Oct 2013 05:48:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sat, 22 Sep 2012 19:51:38 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4694"
+        },
+        {
+          "cache-control": "max-age=24377502"
+        },
+        {
+          "expires": "Mon, 12 Aug 2013 17:04:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 21:48:15 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5307"
+        },
+        {
+          "cache-control": "max-age=31498703"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 03:10:44 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 23:32:05 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-length": "3014"
+        },
+        {
+          "cache-control": "max-age=30794343"
+        },
+        {
+          "expires": "Fri, 25 Oct 2013 23:31:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "expires": "Tue, 18 Dec 2012 13:32:22 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:53:41 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "15981"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "expires": "Sun, 18 Nov 2012 22:28:04 GMT"
+        },
+        {
+          "last-modified": "Fri, 25 Jun 2010 01:28:28 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "578"
+        },
+        {
+          "etag": "\"0c688b6514cb1:586\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 27 Sep 2010 09:07:24 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-language": "pt-BR"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9688"
+        },
+        {
+          "cache-control": "public, max-age=31536000"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:32:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "last-modified": "Tue, 20 Sep 2011 23:59:47 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "1780"
+        },
+        {
+          "etag": "\"80e3ea8c9abcd1:639\""
+        },
+        {
+          "expires": "Mon, 26 Nov 2012 00:03:41 GMT"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9u%7E*t%28750g07p-139944fe49b-0x176"
+        },
+        {
+          "last-modified": "Thu, 23 Aug 2012 17:24:32 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-length": "8722"
+        },
+        {
+          "cache-control": "max-age=26399474"
+        },
+        {
+          "expires": "Thu, 05 Sep 2013 02:43:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "last-modified": "Wed, 12 Jan 2011 20:27:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "342"
+        },
+        {
+          "expires": "Mon, 26 Nov 2012 16:35:19 GMT"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:55 GMT"
+        },
+        {
+          "last-modified": "Wed, 19 Oct 2011 01:17:47 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "6641"
+        },
+        {
+          "etag": "\"80af29e9fc8dcc1:8e4\""
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Wed, 11 Jul 2012 18:03:37 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2752"
+        },
+        {
+          "cache-control": "max-age=14608007"
+        },
+        {
+          "expires": "Sun, 21 Apr 2013 15:19:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "last-modified": "Tue, 07 Jul 2009 20:18:42 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "613"
+        },
+        {
+          "expires": "Mon, 26 Nov 2012 16:35:19 GMT"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "etag": "\"03d21f40ffc91:5b1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 12 Jul 2012 22:44:28 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "551"
+        },
+        {
+          "cache-control": "max-age=21719526"
+        },
+        {
+          "expires": "Fri, 12 Jul 2013 22:44:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "expires": "Mon, 17 Dec 2012 20:39:34 GMT"
+        },
+        {
+          "last-modified": "Tue, 20 Sep 2011 02:08:20 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80183dd68badcd1:720\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "17673"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "t6ulcpjqcj9%3Fuk%601d72f%2B4%603g-13ac678e4f2-0x104"
+        },
+        {
+          "set-cookie": "nonsession=CgADLAAFQlSPuMQDKACBZ+x5mYzY3OGU0YzgxM2EwYTVlNmM4ZDZjODQ2ZmZmOGYzYjlPrxuR;Domain=.raptor.ebaydesc.com;Expires=Sun, 03-Nov-2013 13:32:22 GMT;Path=/ "
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 00:03:20 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "54313"
+        },
+        {
+          "cache-control": "max-age=31401067"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 00:03:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 00:03:52 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "20701"
+        },
+        {
+          "cache-control": "max-age=31401093"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 00:03:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9ve*t%28747%60e%7E%3A-13ac678e523-0x15c"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "3577"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "set-cookie": "HT=1351949521580%0211529%04287876%06261345%0311528%04286823%06260443%0311527%04286801%06203908%011351949527269%02981%04-1%060%03980%04-1%060%03979%04-1%060%03688%04-1%060%03255%04-1%060%011351949541760%0211575%04-1%060%031527%04-1%060%03829%04-1%060%03912%04-1%060%03827%04-1%060%03876%04-1%060%03825%04-1%060%03433%04-1%060%031651%04-1%060%031650%04-1%060; Domain=main.ebayrtm.com; Path=/rtm"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 21:23:48 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "5138"
+        },
+        {
+          "cache-control": "max-age=31132229"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 21:22:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:23 GMT"
+        },
+        {
+          "last-modified": "Thu, 04 Oct 2007 21:44:39 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "3179"
+        },
+        {
+          "expires": "Fri, 14 Dec 2012 20:03:15 GMT"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "etag": "\"03d21f40ffc91:5b1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890; Domain=.ebay.com; Expires=Thu, 02-Nov-2017 13:32:23 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "7004"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:23 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        },
+        {
+          "transaction": "uk.r+607b~`s,RcmdId FindingProductv4,RlogId p4pmiw%60jtb9%3Fuk.r%2B607b%7E%60s-13ac678cb27-0xb7"
+        },
+        {
+          "rlogid": "t6ulcpjqcj9%3Fuk%601d72f%2B12%60b-13ac678e09e-0xc0"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:23 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:47:00 GMT"
+        },
+        {
+          "last-modified": "Fri, 27 Aug 2010 00:22:54 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "366"
+        },
+        {
+          "etag": "\"80af29e9fc8dcc1:8e4\""
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:23 GMT"
+        },
+        {
+          "expires": "Fri, 26 Oct 2012 04:43:31 GMT"
+        },
+        {
+          "last-modified": "Thu, 06 Oct 2005 21:29:14 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80fb69e73664c31:6fe\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "141"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:23 GMT"
+        },
+        {
+          "last-modified": "Mon, 25 Jul 2005 20:31:43 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "64"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:59 GMT"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "etag": "\"03d21f40ffc91:5b1\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:23 GMT"
+        },
+        {
+          "expires": "Sun, 18 Nov 2012 22:28:04 GMT"
+        },
+        {
+          "last-modified": "Tue, 25 Sep 2012 05:16:18 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "7623"
+        },
+        {
+          "etag": "\"0c688b6514cb1:586\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Fri, 19 Oct 2012 22:52:32 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "949"
+        },
+        {
+          "cache-control": "max-age=30273610"
+        },
+        {
+          "expires": "Sat, 19 Oct 2013 22:52:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:22 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Tue, 19 Jun 2012 05:08:42 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1069"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "max-age=19670318"
+        },
+        {
+          "expires": "Wed, 19 Jun 2013 05:31:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 03:14:23 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "6931"
+        },
+        {
+          "cache-control": "max-age=30721319"
+        },
+        {
+          "expires": "Fri, 25 Oct 2013 03:14:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlog": "uh%60jk%3D9vj*ts67.21336g2-13ac678eef8"
+        },
+        {
+          "x-ebay-request-id": "13ac678e-ef80-a5ac-0760-c260ff104b3e!ajax.all.get!10.90.192.118!ebay.com[]"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:24 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:25 GMT"
+        },
+        {
+          "expires": "Sun, 18 Nov 2012 22:28:04 GMT"
+        },
+        {
+          "last-modified": "Thu, 04 Oct 2012 18:56:36 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "9521"
+        },
+        {
+          "etag": "\"0c688b6514cb1:586\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4n%60rujfudlwc%3D9un%7F4g65%60%283ab%3D-13ac678ef91-0x19c"
+        },
+        {
+          "cache-control": "private, no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/json"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:25 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890; Domain=.ebay.com; Expires=Thu, 02-Nov-2017 13:32:30 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-length": "90235"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:30 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        },
+        {
+          "transaction": "uk.r+607b~`s,RcmdId FindingProductv4,RlogId p4pmiw%60jtb9%3Fuk.r%2B607b%7E%60s-13ac678cb27-0xb7"
+        },
+        {
+          "rlogid": "t6ulcpjqcj9%3Fuk%601d72f%2B12%60b-13ac678e09e-0xc0"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:31 GMT"
+        },
+        {
+          "expires": "Thu, 27 Sep 2012 23:15:40 GMT"
+        },
+        {
+          "last-modified": "Fri, 21 Jan 2011 20:52:52 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "366"
+        },
+        {
+          "etag": "\"0aa782badb9cb1:682\""
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890; Domain=.ebay.com; Expires=Thu, 02-Nov-2017 13:32:32 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "80050"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:31 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        },
+        {
+          "transaction": "uk.r+607b~go,RcmdId FindingProductv4,RlogId p4pmiw%60jtb9%3Fuk.r%2B607b%7Ego-13ac6790aa0-0xb6"
+        },
+        {
+          "rlogid": "t6ulcpjqcj9%3Fuk%601d72f%2B12%60b-13ac678e09e-0xc0"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:32 GMT"
+        },
+        {
+          "expires": "Fri, 26 Oct 2012 04:43:31 GMT"
+        },
+        {
+          "last-modified": "Thu, 06 Oct 2005 21:29:14 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "366"
+        },
+        {
+          "etag": "\"0aa782badb9cb1:682\""
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:32 GMT"
+        },
+        {
+          "expires": "Fri, 14 Dec 2012 13:33:03 GMT"
+        },
+        {
+          "last-modified": "Wed, 02 May 2012 23:09:29 GMT"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1544"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4n%60rujfudlwc%3D9un%7F4g65%60%28c1eg-13ac67910d1-0x179"
+        },
+        {
+          "cache-control": "private, no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:32 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Tue, 29 May 2012 21:33:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4908"
+        },
+        {
+          "cache-control": "max-age=8305824"
+        },
+        {
+          "expires": "Thu, 07 Feb 2013 16:42:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:33 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890; Domain=.ebay.com; Expires=Thu, 02-Nov-2017 13:32:36 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-length": "91130"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:36 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        },
+        {
+          "transaction": "uk.r+607b~go,RcmdId FindingProductv4,RlogId p4pmiw%60jtb9%3Fuk.r%2B607b%7Ego-13ac6790aa0-0xb6"
+        },
+        {
+          "rlogid": "t6ulcpjqcj9%3Fuk%601d72f%2B12%60b-13ac678e09e-0xc0"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:37 GMT"
+        },
+        {
+          "expires": "Thu, 27 Sep 2012 23:15:40 GMT"
+        },
+        {
+          "last-modified": "Fri, 21 Jan 2011 20:52:52 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "366"
+        },
+        {
+          "etag": "\"0aa782badb9cb1:682\""
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890; Domain=.ebay.com; Expires=Thu, 02-Nov-2017 13:32:37 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "80060"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:37 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        },
+        {
+          "transaction": "v .r+616d2tu,RcmdId FindingProductv4,RlogId p4pmiw%60jtb9%3Fv%7F.r%2B616d2tu-13ac6791ff9-0xbc"
+        },
+        {
+          "rlogid": "t6ulcpjqcj9%3Fuk%601d72f%2B12%60b-13ac678e09e-0xc0"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:38 GMT"
+        },
+        {
+          "expires": "Fri, 26 Oct 2012 04:43:31 GMT"
+        },
+        {
+          "last-modified": "Thu, 06 Oct 2005 21:29:14 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "366"
+        },
+        {
+          "etag": "\"0aa782badb9cb1:682\""
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 27 Sep 2010 09:07:24 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1290"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "private, max-age=31536000"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:32:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:39 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cteonnt-length": "4588"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4n%60rujfudlwc%3D9un%7F4g66%60%283d30-13ac67928dc-0x197"
+        },
+        {
+          "cache-control": "private, no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:39 GMT"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "set-cookie": "npii=btguid/c67883f113a0a56964e646c6ffaa1ac152765078^cguid/c67885c613a0a0a9f6568b16ff5917ee52765078^; Domain=.ebay.com; Expires=Sun, 03-Nov-2013 13:32:40 GMT; Path=/"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa ADMa DEVa PSDo PSAa OUR SAMo IND UNI COM NAV INT STA DEM PRE\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890; Domain=.ebay.com; Expires=Thu, 02-Nov-2017 13:32:39 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-length": "91165"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:39 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        },
+        {
+          "transaction": "v .r+616d2tu,RcmdId FindingProductv4,RlogId p4pmiw%60jtb9%3Fv%7F.r%2B616d2tu-13ac6791ff9-0xbc"
+        },
+        {
+          "rlogid": "t6ulcpjqcj9%3Fuk%601d72f%2B12%60b-13ac678e09e-0xc0"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4n%60rujfudlwc%3D9un%7F4g65%60%28555f-13ac6792d15-0x18d"
+        },
+        {
+          "cache-control": "private, no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:41 GMT"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "set-cookie": "npii=bcguid/c67885c613a0a0a9f6568b16ff5917ee52765079^tguid/c67883f113a0a56964e646c6ffaa1ac152765079^; Domain=.ebay.com; Expires=Sun, 03-Nov-2013 13:32:41 GMT; Path=/"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa ADMa DEVa PSDo PSAa OUR SAMo IND UNI COM NAV INT STA DEM PRE\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "301"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890; Domain=.ebay.com; Expires=Thu, 02-Nov-2017 13:32:42 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "location": "http://www.ebay.com/fashion/health-beauty"
+        },
+        {
+          "rlogid": "p4pmiw%60jtb9%3Fv%7F.wcc%60dh72%3C-13ac6793402"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:42 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890; Domain=.ebay.com; Expires=Thu, 02-Nov-2017 13:32:43 GMT; Path=/"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-length": "12792"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        },
+        {
+          "transaction": "v .r+616d2tu,RcmdId FindingProductv4,RlogId p4pmiw%60jtb9%3Fv%7F.r%2B616d2tu-13ac6791ff9-0xbc"
+        },
+        {
+          "rlogid": "p4u%60tsjfgkpfiuf%3F%3Ctq%28qq.d%605g%6053-13ac679336d"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:47:45 GMT"
+        },
+        {
+          "last-modified": "Thu, 05 Jul 2012 18:43:18 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "2469"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 24 May 2012 16:22:27 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5850"
+        },
+        {
+          "cache-control": "max-age=31509956"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 06:18:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "access-control-allow-origin": "*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 27 Sep 2010 09:07:24 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5679"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "public, max-age=31536000"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:32:43 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cteonnt-length": "4588"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 15:54:54 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6322"
+        },
+        {
+          "cache-control": "max-age=31522197"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 09:42:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "access-control-allow-origin": "*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9u%7E*t%28750g07n-13aac9b9c70-0x176"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 22:29:19 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "250"
+        },
+        {
+          "cache-control": "max-age=31102053"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 13:00:16 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 26 Dec 2011 17:33:25 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5900"
+        },
+        {
+          "cache-control": "max-age=31504816"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 04:52:59 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "access-control-allow-origin": "*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 15:32:58 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3953"
+        },
+        {
+          "cache-control": "max-age=31511815"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 06:49:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "access-control-allow-origin": "*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 17:37:08 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4418"
+        },
+        {
+          "cache-control": "max-age=29623692"
+        },
+        {
+          "expires": "Sat, 12 Oct 2013 10:20:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 27 Sep 2010 09:07:24 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10098"
+        },
+        {
+          "cache-control": "private, max-age=31536000"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:32:43 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "last-modified": "Tue, 07 Aug 2012 21:01:25 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"804039cedf74cd1:603\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "113266"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Tue, 07 Aug 2012 05:29:55 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7341"
+        },
+        {
+          "cache-control": "max-age=19386157"
+        },
+        {
+          "expires": "Sat, 15 Jun 2013 22:35:20 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "access-control-allow-origin": "*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 21:57:15 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7462"
+        },
+        {
+          "cache-control": "max-age=31508534"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 05:54:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "access-control-allow-origin": "*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 02:39:26 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "10090"
+        },
+        {
+          "cache-control": "max-age=31501318"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 03:54:41 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "access-control-allow-origin": "*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 16 Oct 2012 19:32:45 GMT"
+        },
+        {
+          "content-type": "text/css;charset=UTF-8"
+        },
+        {
+          "content-length": "6377"
+        },
+        {
+          "cache-control": "max-age=30002402"
+        },
+        {
+          "expires": "Wed, 16 Oct 2013 19:32:45 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Tue, 29 May 2012 19:40:07 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4488"
+        },
+        {
+          "cache-control": "max-age=7260395"
+        },
+        {
+          "expires": "Sat, 26 Jan 2013 14:19:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Fri, 20 Jul 2012 23:29:38 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4889"
+        },
+        {
+          "cache-control": "max-age=19908420"
+        },
+        {
+          "expires": "Fri, 21 Jun 2013 23:39:43 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Tue, 09 Oct 2012 14:02:17 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3638"
+        },
+        {
+          "cache-control": "max-age=28507880"
+        },
+        {
+          "expires": "Sun, 29 Sep 2013 12:24:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Tue, 02 Oct 2012 18:02:08 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5356"
+        },
+        {
+          "cache-control": "max-age=29198995"
+        },
+        {
+          "expires": "Mon, 07 Oct 2013 12:22:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "access-control-allow-origin": "*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 12 Jul 2012 01:56:06 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4607"
+        },
+        {
+          "cache-control": "max-age=20432905"
+        },
+        {
+          "expires": "Fri, 28 Jun 2013 01:21:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 23 Jul 2012 16:18:08 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5128"
+        },
+        {
+          "cache-control": "max-age=23738740"
+        },
+        {
+          "expires": "Mon, 05 Aug 2013 07:38:23 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 16:44:35 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7892"
+        },
+        {
+          "cache-control": "max-age=31534799"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:12:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "access-control-allow-origin": "*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Tue, 16 Oct 2012 03:33:23 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4234"
+        },
+        {
+          "cache-control": "max-age=29726488"
+        },
+        {
+          "expires": "Sun, 13 Oct 2013 14:54:11 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 20 Aug 2012 20:26:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3021"
+        },
+        {
+          "cache-control": "max-age=26204352"
+        },
+        {
+          "expires": "Mon, 02 Sep 2013 20:31:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Fri, 28 Sep 2012 15:22:25 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4919"
+        },
+        {
+          "cache-control": "max-age=30719689"
+        },
+        {
+          "expires": "Fri, 25 Oct 2013 02:47:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sun, 22 Jul 2012 18:13:25 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4788"
+        },
+        {
+          "cache-control": "max-age=20831615"
+        },
+        {
+          "expires": "Tue, 02 Jul 2013 16:06:19 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Fri, 06 Jul 2012 20:15:28 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3987"
+        },
+        {
+          "cache-control": "max-age=17873930"
+        },
+        {
+          "expires": "Wed, 29 May 2013 10:31:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 16 Aug 2012 14:53:01 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4623"
+        },
+        {
+          "cache-control": "max-age=20477655"
+        },
+        {
+          "expires": "Fri, 28 Jun 2013 13:46:59 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Fri, 27 Jul 2012 17:58:05 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4821"
+        },
+        {
+          "cache-control": "max-age=20499738"
+        },
+        {
+          "expires": "Fri, 28 Jun 2013 19:55:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Fri, 13 Jul 2012 05:37:24 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5080"
+        },
+        {
+          "cache-control": "max-age=19887245"
+        },
+        {
+          "expires": "Fri, 21 Jun 2013 17:46:49 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 19:02:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "18863"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "max-age=40850"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:53:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Thu, 27 Sep 2012 13:24:40 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "10030"
+        },
+        {
+          "cache-control": "max-age=31521522"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 09:31:25 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "access-control-allow-origin": "*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 16 Jul 2012 07:04:19 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6320"
+        },
+        {
+          "cache-control": "max-age=23373247"
+        },
+        {
+          "expires": "Thu, 01 Aug 2013 02:06:51 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "access-control-allow-origin": "*"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9u%7E*t%28750g3%7E1-13a3ef29997-0x16d"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 05:17:21 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "60127"
+        },
+        {
+          "cache-control": "max-age=29262216"
+        },
+        {
+          "expires": "Tue, 08 Oct 2013 05:56:19 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Tue, 10 Jul 2012 21:28:46 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5983"
+        },
+        {
+          "cache-control": "max-age=23738926"
+        },
+        {
+          "expires": "Mon, 05 Aug 2013 07:41:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 14:51:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "11895"
+        },
+        {
+          "cache-control": "max-age=31510005"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 06:19:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 08:46:05 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7674"
+        },
+        {
+          "cache-control": "max-age=31245727"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 04:54:50 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Wed, 08 Aug 2012 18:20:46 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "12744"
+        },
+        {
+          "cache-control": "max-age=26388803"
+        },
+        {
+          "expires": "Wed, 04 Sep 2013 23:46:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Mon, 27 Aug 2012 19:38:43 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5852"
+        },
+        {
+          "cache-control": "max-age=27384452"
+        },
+        {
+          "expires": "Mon, 16 Sep 2013 12:20:15 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Tue, 22 Nov 2011 18:32:07 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6016"
+        },
+        {
+          "cache-control": "max-age=31505442"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 05:03:25 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sun, 23 Sep 2012 20:27:09 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "8741"
+        },
+        {
+          "cache-control": "max-age=28128091"
+        },
+        {
+          "expires": "Wed, 25 Sep 2013 02:54:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 18:53:28 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5727"
+        },
+        {
+          "cache-control": "max-age=31502723"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 04:18:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Tue, 16 Oct 2012 19:32:46 GMT"
+        },
+        {
+          "content-type": "application/x-javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "54418"
+        },
+        {
+          "cache-control": "max-age=30002439"
+        },
+        {
+          "expires": "Wed, 16 Oct 2013 19:33:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:44 GMT"
+        },
+        {
+          "last-modified": "Tue, 22 May 2012 19:02:53 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "2288"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:44 GMT"
+        },
+        {
+          "expires": "Sun, 25 Nov 2012 23:46:56 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Aug 2011 00:39:15 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "cache-control": "max-age=3888000"
+        },
+        {
+          "content-length": "5743"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sun, 15 Jul 2012 05:26:27 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6174"
+        },
+        {
+          "cache-control": "max-age=20606181"
+        },
+        {
+          "expires": "Sun, 30 Jun 2013 01:29:05 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 20:13:40 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "8182"
+        },
+        {
+          "cache-control": "max-age=30802674"
+        },
+        {
+          "expires": "Sat, 26 Oct 2013 01:50:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9u%7E*t%28750g065-13911ec26be-0x16d"
+        },
+        {
+          "last-modified": "Wed, 08 Aug 2012 21:42:19 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "58636"
+        },
+        {
+          "cache-control": "max-age=24211902"
+        },
+        {
+          "expires": "Sat, 10 Aug 2013 19:04:25 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:44 GMT"
+        },
+        {
+          "last-modified": "Wed, 03 Oct 2012 22:05:50 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"804039cedf74cd1:603\""
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "content-length": "63909"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 15:58:50 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "13008"
+        },
+        {
+          "cache-control": "max-age=31535919"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:31:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4n%60rujfudlwc%3D9vt*ts67.4e6f0e0-13ac6793f33-0x19b"
+        },
+        {
+          "cache-control": "private, no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:45 GMT"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "set-cookie": "npii=btguid/c67883f113a0a56964e646c6ffaa1ac15276507d^cguid/c67885c613a0a0a9f6568b16ff5917ee5276507d^; Domain=.ebay.com; Expires=Sun, 03-Nov-2013 13:32:45 GMT; Path=/"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa ADMa DEVa PSDo PSAa OUR SAMo IND UNI COM NAV INT STA DEM PRE\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "rlogid": "p4pphdlwc%3D9u%7E*t%28750g3%7Fo-13a40772552-0x169"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 05:19:14 GMT"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "115864"
+        },
+        {
+          "cache-control": "max-age=29287759"
+        },
+        {
+          "expires": "Tue, 08 Oct 2013 13:02:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "2703"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Wed, 28 Mar 2012 22:30:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:44 GMT"
+        },
+        {
+          "expires": "Tue, 30 Oct 2012 19:27:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lucky9=1959890; Domain=.ebay.com; Expires=Thu, 02-Nov-2017 13:32:58 GMT; Path=/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/javascript;charset=UTF-8"
+        },
+        {
+          "content-length": "80064"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:32:58 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 23:04:12 GMT"
+        },
+        {
+          "transaction": "uk.r+607b~k|,RcmdId FindingProductv4,RlogId p4pmiw%60jtb9%3Fuk.r%2B607b%7Ek%7C-13ac6796fd6-0xc1"
+        },
+        {
+          "rlogid": "p4u%60tsjfgkpfiuf%3F%3Ctq%28qq.d%605g%6053-13ac679336d"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_26.json b/jetty-http2/http2-hpack/src/test/resources/data/story_26.json
new file mode 100644
index 0000000..143ced2
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_26.json
@@ -0,0 +1,4439 @@
+{
+  "context": "response",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "522"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:20 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "IVe/SwucJuBsLtVHWJw2PMdOTOxuEWUir5igQNThkTg="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=17216869"
+        },
+        {
+          "expires": "Tue, 21 May 2013 19:18:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/css; charset=utf-8"
+        },
+        {
+          "last-modified": "Tue, 24 Apr 2012 22:13:35 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "95tUymdadFLd8Dpml8VnOoUG7KhisOwk74Kd/aIGfU0="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "123"
+        },
+        {
+          "cache-control": "public, max-age=16394910"
+        },
+        {
+          "expires": "Sun, 12 May 2013 06:59:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/css; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:37:35 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "Qc0GcUiwi3io8aSRIdXaahYr6KKhphvV6NlN8vo/bD4="
+        },
+        {
+          "content-length": "14684"
+        },
+        {
+          "cache-control": "public, max-age=31067635"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:44:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:23 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "7exUqkoZxtfLseR1zLxJlXnpYK6MOognZuCKx7drdRo="
+        },
+        {
+          "content-length": "14438"
+        },
+        {
+          "cache-control": "public, max-age=24926560"
+        },
+        {
+          "expires": "Mon, 19 Aug 2013 00:53:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 17:08:56 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "eRvyJLXIvW3Vu9d+m439v+LGKqXiLSKmz7w9/xMAUpc="
+        },
+        {
+          "content-length": "17475"
+        },
+        {
+          "cache-control": "public, max-age=31124427"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 18:31:11 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/css; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 22:55:27 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "14hoEcywNNMBIVUS5B7AD7RDGiDvJ4BGeOVgJbBDzf0="
+        },
+        {
+          "content-length": "44191"
+        },
+        {
+          "cache-control": "public, max-age=31067635"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:44:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 18:34:48 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "41/HuGcFNMmys4cvGKlBeylojdVDP4+VBIf1giu3eNQ="
+        },
+        {
+          "content-length": "754"
+        },
+        {
+          "cache-control": "public, max-age=29985162"
+        },
+        {
+          "expires": "Wed, 16 Oct 2013 14:03:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 16:05:53 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "oKQwv0JLYost+zqlv8x+C7MEL7zRBbeMomoc54M5RZY="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2349"
+        },
+        {
+          "cache-control": "public, max-age=31067361"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:40:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "2626"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:08:50 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "zh8tRHKFtERIZ+K/eGiM1utm1H66OnOj1qwPAN7Ck9A="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=31068570"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 03:00:19 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Fri, 28 Sep 2012 15:01:14 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "pricqIchHztHxKAQSidQiwGmRf62vAL6I7Oi0r/Ki08="
+        },
+        {
+          "content-length": "8036"
+        },
+        {
+          "cache-control": "public, max-age=31066940"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:33:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 21:42:19 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "Kur2FUUQjAQ0yPQJC9fvK56/+LWZHvyQF6Ce2Fuaf2k="
+        },
+        {
+          "content-length": "36302"
+        },
+        {
+          "cache-control": "public, max-age=31068470"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:58:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:02:51 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "wk2MWysJhw3Et7CRGMA1sE9HWuyzy8oCvtT2V7iPXeg="
+        },
+        {
+          "content-length": "8230"
+        },
+        {
+          "cache-control": "public, max-age=25639699"
+        },
+        {
+          "expires": "Tue, 27 Aug 2013 06:59:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 21:38:44 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "GMzzyj0B89LYf1zKH9hqxZekz5mYTmsuxwLugWyc2Gg="
+        },
+        {
+          "content-length": "4878"
+        },
+        {
+          "cache-control": "public, max-age=31068529"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:59:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 04:37:48 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:37:48 GMT"
+        },
+        {
+          "content-type": "application/ocsp-response"
+        },
+        {
+          "content-transfer-encoding": "binary"
+        },
+        {
+          "content-length": "1814"
+        },
+        {
+          "cache-control": "max-age=575215, public, no-transform, must-revalidate"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:53 GMT"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-fb-metrics": "{\"w\":53,\"r\":26,\"q\":0,\"a\":25}"
+        },
+        {
+          "x-fb-server": "10.74.89.23"
+        },
+        {
+          "x-fb-debug": "7iLjsQVXsunUKXe3NlV2ytaBGzQ0VHCkMX/J6rEuB6Y="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:54 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "43"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/css; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 15:06:53 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "ur+THlFHeLotsmDlQWYPw2GRELyvg28JmE0JYVt56uo="
+        },
+        {
+          "content-length": "1155"
+        },
+        {
+          "cache-control": "public, max-age=31067714"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:46:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:54 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/css; charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 31 Aug 2012 22:13:28 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "6/fKHkRtBpT4oqBg33tFHk2pO6SDZlUG11Uq4/AlUIE="
+        },
+        {
+          "content-length": "516"
+        },
+        {
+          "cache-control": "public, max-age=26316304"
+        },
+        {
+          "expires": "Wed, 04 Sep 2013 02:55:58 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:54 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "232"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Sat, 21 Apr 2012 07:03:57 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "XtTsONeGHGs/1vRRv8cvNY1ciB3XqlrvnTq2GZXvnqM="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=30419619"
+        },
+        {
+          "expires": "Mon, 21 Oct 2013 14:44:35 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Fri, 07 Sep 2012 15:18:40 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "E2JBYTapyXFjxTTVqkrekTVKDp1lDQQT/7YxcxfNU2U="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "400"
+        },
+        {
+          "cache-control": "public, max-age=31066933"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:33:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 21:41:45 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "iPbLdjapQzRoatbOUDrN+exDj8EPHJAcsZ48pVtprtA="
+        },
+        {
+          "content-length": "35766"
+        },
+        {
+          "cache-control": "public, max-age=31068980"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 03:07:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:54 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 14:34:50 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "0ci0R5R2ivIVCRrwtG507Eej+LTK8dUL8dIiZp70+dU="
+        },
+        {
+          "content-length": "10902"
+        },
+        {
+          "cache-control": "public, max-age=31066965"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:33:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:55 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/css; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:38:28 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "P2Bq0ebVXjtf8GyQp1HHux8NxlftUMXZuY8XF+yaOVo="
+        },
+        {
+          "content-length": "4676"
+        },
+        {
+          "cache-control": "public, max-age=31088728"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 08:36:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/css; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:37:10 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "yHzV/c0w3ZyInkRbLCDrA0t5adSMyAG4prBOk+i+t6Y="
+        },
+        {
+          "content-length": "20791"
+        },
+        {
+          "cache-control": "public, max-age=31067654"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:45:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "11113"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 14:34:47 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "u363OvKFmnm717JBUXA5ePB8Ts0ppRI7+eEJwOOep6w="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=31066988"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:34:04 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:02:57 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "Yty+Te4OzfswtmjzbJmJZaybyM0hxXiRU2NtHEbDuPE="
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "cache-control": "public, max-age=25753573"
+        },
+        {
+          "expires": "Wed, 28 Aug 2013 14:37:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:24 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "yKFyrxwqBiumMPfWvv4morUUsmz9djZtSdmCoQMnchs="
+        },
+        {
+          "content-length": "571"
+        },
+        {
+          "cache-control": "public, max-age=25753811"
+        },
+        {
+          "expires": "Wed, 28 Aug 2013 14:41:07 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:22 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "HbryxnP7HNa7kdTChA6BppSjLQw0gz9ZzESCqEH3/9k="
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "cache-control": "public, max-age=25753770"
+        },
+        {
+          "expires": "Wed, 28 Aug 2013 14:40:26 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Tue, 28 Aug 2012 01:20:00 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "edgqdu1lFmUW32Et2hHoiAsp9kFIch8QDiciO71cQ4w="
+        },
+        {
+          "content-length": "12817"
+        },
+        {
+          "cache-control": "public, max-age=26458041"
+        },
+        {
+          "expires": "Thu, 05 Sep 2013 18:18:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:09:30 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "rXXtxI3fPgNHe6wKIDRBR0xjttUNeG+BDQM8QfKQa+A="
+        },
+        {
+          "content-length": "11688"
+        },
+        {
+          "cache-control": "public, max-age=31068990"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 03:07:27 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:57 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "35165"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:06:47 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "fUJ2Nc9qJdBC1AnYFA5Vs1f7ozv+i/PTKO+Vep0A0HQ="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=31068521"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:59:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:57 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:07:00 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "s9ZmJKnYGlEjIGo8xQzxlhVM4nilGHgcv1fhK1Z7F1w="
+        },
+        {
+          "content-length": "1028"
+        },
+        {
+          "cache-control": "public, max-age=31191849"
+        },
+        {
+          "expires": "Wed, 30 Oct 2013 13:15:07 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:07:08 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "hMQvA7xhuAspt5fOxedr0fWzQZNLkyizVtlmspzgVG8="
+        },
+        {
+          "content-length": "47844"
+        },
+        {
+          "cache-control": "public, max-age=31068990"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 03:07:27 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:57 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 19:17:24 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "1EkJKYLfWucaDVhZCEVAAo57HpAH7rvF4r9IwDXM2B8="
+        },
+        {
+          "content-length": "42391"
+        },
+        {
+          "cache-control": "public, max-age=31143832"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 23:54:49 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:57 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "13181"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:10:32 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "dO6OKUf38jDdtAnTrM28wZTBz9Y5hU/0EJdd1CkWGDs="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=31068989"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 03:07:27 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 19:03:57 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "IlZ4dyc3v7fqrfiamqJbFeFTWRkUvEUs2L8KLXNa+5o="
+        },
+        {
+          "content-length": "23736"
+        },
+        {
+          "cache-control": "public, max-age=31478360"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 20:50:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:57 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:35:28 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "REVz0k4Gud7bedzv9KSTG2i1KOporb0T14mWht95MIE="
+        },
+        {
+          "content-length": "12969"
+        },
+        {
+          "cache-control": "public, max-age=31394885"
+        },
+        {
+          "expires": "Fri, 01 Nov 2013 21:39:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:06:44 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "VKvuYL/9rqvjXh7mr8LjSJmcgQLZ/a+Ztqj2aUsPacc="
+        },
+        {
+          "content-length": "8457"
+        },
+        {
+          "cache-control": "public, max-age=31088728"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 08:36:26 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-fb-metrics": "{\"w\":26,\"r\":18,\"q\":0,\"a\":30}"
+        },
+        {
+          "x-fb-server": "10.164.86.49"
+        },
+        {
+          "x-fb-debug": "egJridVr0Ohgw3QbFe66p8hTV/ZDa+ldtrrj55f1Dwg="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "43"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-fb-metrics": "{\"w\":15,\"r\":74,\"q\":0,\"a\":21}"
+        },
+        {
+          "x-fb-server": "10.164.212.85"
+        },
+        {
+          "x-fb-debug": "7JVbUHGoJcLzIbHgkJPv0DSBycKYYPPorUc0i6OdRw8="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "43"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-fb-metrics": "{\"w\":33,\"r\":14,\"q\":0,\"a\":27}"
+        },
+        {
+          "x-fb-server": "10.164.203.85"
+        },
+        {
+          "x-fb-debug": "SHFiC3r5rPZSoyQ6AT4o7Lrz58o2i0cRMRoLoKKAVLc="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "10334"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-fb-metrics": "{\"w\":47,\"r\":27,\"q\":0,\"a\":24}"
+        },
+        {
+          "x-fb-server": "10.164.121.55"
+        },
+        {
+          "x-fb-debug": "B8TQ25HLrpUM+2nuhej+798G7ib2rXsLxKDRmmd6364="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "79019"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:05 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "q1YlNQFhUrIi0HF88gF/s47itTMC0ALVS2i6Xo/eSFQ="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=17216856"
+        },
+        {
+          "expires": "Tue, 21 May 2013 19:18:35 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:20 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "OOKrpYeJ1K2euVWUg0h3X4OLDU+bPXAhHe2ZbKmaIIo="
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "cache-control": "public, max-age=17216855"
+        },
+        {
+          "expires": "Tue, 21 May 2013 19:18:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cnection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 21:42:28 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "BWYgCEbzeWyERD5oPP51t1mG+xnS0km1r6TZrds9BdY="
+        },
+        {
+          "content-length": "55530"
+        },
+        {
+          "cache-control": "public, max-age=31068989"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 03:07:27 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-fb-metrics": "{\"w\":47,\"r\":27,\"q\":0,\"a\":24}"
+        },
+        {
+          "x-fb-server": "10.164.121.55"
+        },
+        {
+          "x-fb-debug": "6DDnMStngO3Ec4qHVJLnouT/OjWIKWOh3p7X19lwA2E="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "67"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "x-fb-debug": "DY+dO6Fs6HOjJLzXfO2vzcoACugopwtj+ZfSFe3+0Io="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "67"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Mon, 15 Oct 2012 01:19:28 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "U3t5ojZAXSlz/rftvVXMdi+dQaAlCxv95u4nFdmaOpU="
+        },
+        {
+          "content-length": "6332"
+        },
+        {
+          "cache-control": "public, max-age=29865483"
+        },
+        {
+          "expires": "Tue, 15 Oct 2013 04:49:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:19 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "WkN9kzT0IbJnmPVAe/JpiO1KA3sDm5JiLNu+peaU22E="
+        },
+        {
+          "content-length": "1025"
+        },
+        {
+          "cache-control": "public, max-age=17216854"
+        },
+        {
+          "expires": "Tue, 21 May 2013 19:18:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:00 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cnection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "7905"
+        },
+        {
+          "content-type": "text/css; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:38:51 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "flUDwRXAcKGlCZV+B6xp1kix2zMM2jCaLr8GXWfOS9o="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=31191685"
+        },
+        {
+          "expires": "Wed, 30 Oct 2013 13:12:25 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:00 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "960"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Thu, 07 Jun 2012 20:17:10 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "s2bSrOgSOrnc1I2Y+hCmZNjO4JKRAsJvvEShk7xvQh0="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=25753518"
+        },
+        {
+          "expires": "Wed, 28 Aug 2013 14:36:20 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Fri, 13 Jul 2012 13:05:49 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "dSqFMj3BQam7KrjoHsDUySNYx2e/ZA4jk+iLwQD5q+M="
+        },
+        {
+          "content-length": "1421"
+        },
+        {
+          "cache-control": "public, max-age=25753545"
+        },
+        {
+          "expires": "Wed, 28 Aug 2013 14:36:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cnection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "3977"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 21:41:38 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "if1LyEGCEK6E/tHFIYqTdcReGf2YlH/8CNcvt0MSb5c="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=31162173"
+        },
+        {
+          "expires": "Wed, 30 Oct 2013 05:00:35 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:03 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "0MQqsPt7SaQdEz9msJEk0wieC0zyyvfgvjy4gscfRm4="
+        },
+        {
+          "content-length": "316"
+        },
+        {
+          "cache-control": "public, max-age=25628126"
+        },
+        {
+          "expires": "Tue, 27 Aug 2013 03:46:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cnection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6779"
+        },
+        {
+          "last-modified": "Wed, 09 May 2012 22:55:50 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7899"
+        },
+        {
+          "last-modified": "Mon, 14 May 2012 09:15:54 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "8961"
+        },
+        {
+          "last-modified": "Fri, 10 Aug 2012 23:04:25 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7270"
+        },
+        {
+          "last-modified": "Thu, 17 May 2012 17:02:26 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "10284"
+        },
+        {
+          "last-modified": "Fri, 14 Sep 2012 13:43:01 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6932"
+        },
+        {
+          "last-modified": "Tue, 15 May 2012 16:53:10 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "19404"
+        },
+        {
+          "last-modified": "Fri, 13 Jul 2012 21:38:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-fb-metrics": "{\"w\":23,\"r\":24,\"q\":0,\"a\":31}"
+        },
+        {
+          "x-fb-server": "10.164.204.89"
+        },
+        {
+          "x-fb-debug": "mhzgPOTS+rD7XyjD1gp3zWldoiZpmeeyK0sWCXxCmL8="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:04 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "43"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-fb-metrics": "{\"w\":13,\"r\":137,\"q\":0,\"a\":23}"
+        },
+        {
+          "x-fb-server": "10.164.167.53"
+        },
+        {
+          "x-fb-debug": "uWC6Yw5Jjt8tp6GMW/0c7q4sQiyN+cfsFumyrajMSLE="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:04 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "43"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-fb-metrics": "{\"w\":20,\"r\":43,\"q\":0,\"a\":30}"
+        },
+        {
+          "x-fb-server": "10.165.52.67"
+        },
+        {
+          "x-fb-debug": "K6O9zzGnsjkFUcjVnvogKEp8WyYKDD5/1SRA3JOqTz8="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:04 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "10334"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-fb-metrics": "{\"w\":40,\"r\":33,\"q\":0,\"a\":22}"
+        },
+        {
+          "x-fb-server": "10.164.10.79"
+        },
+        {
+          "x-fb-debug": "gU+KRCRWrUp+aETSVFA2+QqzJ57Mry5y8i9NZISRzV4="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:04 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "79019"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        },
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-fb-metrics": "{\"w\":40,\"r\":33,\"q\":0,\"a\":22}"
+        },
+        {
+          "x-fb-server": "10.164.10.79"
+        },
+        {
+          "x-fb-debug": "2KYdrNd+vAjbaW2+l9lZ0c9qQnQQuLC0uV+aDWEfnEs="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:04 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "67"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "x-fb-debug": "ltZY31wZe0x9jjXZ+/GQMCIZ6L+UzLcVFaj4Ye8cEag="
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:04 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "67"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "32220"
+        },
+        {
+          "last-modified": "Mon, 15 Oct 2012 15:37:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "105703"
+        },
+        {
+          "last-modified": "Mon, 15 Oct 2012 15:38:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "36768"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 01:55:42 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "119574"
+        },
+        {
+          "last-modified": "Fri, 26 Oct 2012 09:41:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "659"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:22 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "yE8mx2kOMcI8Q4MtoKCXYAXv7xSMQBGufoB0y/qkYEs="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=16309260"
+        },
+        {
+          "expires": "Sat, 11 May 2013 07:12:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6966"
+        },
+        {
+          "last-modified": "Thu, 17 May 2012 16:26:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4501"
+        },
+        {
+          "last-modified": "Fri, 06 Jul 2012 11:36:45 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "37432"
+        },
+        {
+          "last-modified": "Mon, 04 Jun 2012 08:35:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7198"
+        },
+        {
+          "last-modified": "Tue, 15 May 2012 16:45:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "13454"
+        },
+        {
+          "last-modified": "Thu, 31 May 2012 01:48:38 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "access-control-allow-origin": "http://www.facebook.com"
+        },
+        {
+          "access-control-allow-credentials": "true"
+        },
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:08 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "19537"
+        },
+        {
+          "last-modified": "Tue, 19 Jun 2012 08:51:27 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5842"
+        },
+        {
+          "last-modified": "Mon, 14 May 2012 22:03:47 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6973"
+        },
+        {
+          "last-modified": "Tue, 22 May 2012 23:26:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7263"
+        },
+        {
+          "last-modified": "Mon, 11 Jun 2012 21:11:11 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6222"
+        },
+        {
+          "last-modified": "Tue, 15 May 2012 02:10:10 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "8005"
+        },
+        {
+          "last-modified": "Fri, 13 Jul 2012 02:50:27 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7949"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:25:30 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5850"
+        },
+        {
+          "last-modified": "Wed, 23 May 2012 13:04:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4860"
+        },
+        {
+          "last-modified": "Fri, 18 May 2012 06:59:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "36615"
+        },
+        {
+          "last-modified": "Fri, 08 Jun 2012 09:18:35 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "26180"
+        },
+        {
+          "last-modified": "Fri, 01 Jun 2012 12:58:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "8551"
+        },
+        {
+          "last-modified": "Wed, 16 May 2012 00:19:20 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7021"
+        },
+        {
+          "last-modified": "Fri, 18 May 2012 08:58:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "8126"
+        },
+        {
+          "last-modified": "Thu, 17 May 2012 10:31:00 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "21780"
+        },
+        {
+          "last-modified": "Fri, 20 Jul 2012 20:43:14 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "16109"
+        },
+        {
+          "last-modified": "Fri, 08 Jun 2012 21:45:00 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5248"
+        },
+        {
+          "last-modified": "Tue, 15 May 2012 17:19:22 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7600"
+        },
+        {
+          "last-modified": "Tue, 15 May 2012 00:02:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "8148"
+        },
+        {
+          "last-modified": "Tue, 04 Sep 2012 05:25:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "23688"
+        },
+        {
+          "last-modified": "Tue, 05 Jun 2012 16:58:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "32579"
+        },
+        {
+          "last-modified": "Fri, 15 Jun 2012 23:21:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "36154"
+        },
+        {
+          "last-modified": "Tue, 15 May 2012 16:53:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "8261"
+        },
+        {
+          "last-modified": "Thu, 17 May 2012 16:15:19 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "34603"
+        },
+        {
+          "last-modified": "Tue, 05 Jun 2012 19:33:30 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "19320"
+        },
+        {
+          "last-modified": "Fri, 20 Jul 2012 22:33:10 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/css; charset=utf-8"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 21:59:56 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "cEr4CpqyPlutZEM5egM7EW1V/FoNLas8puqhILOyn6g="
+        },
+        {
+          "content-length": "1589"
+        },
+        {
+          "cache-control": "public, max-age=31191410"
+        },
+        {
+          "expires": "Wed, 30 Oct 2013 13:08:05 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:15 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cnection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:34 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "SjbWzWIhc5uDeUgKzkah4oVawEfOfsqgn79tJvLiODA="
+        },
+        {
+          "content-length": "124"
+        },
+        {
+          "cache-control": "public, max-age=25632795"
+        },
+        {
+          "expires": "Tue, 27 Aug 2013 05:04:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:15 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cnection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:02:59 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "8BYYADIiLWZPRqrmghOTJnnu5b75InjLJYums29XQC4="
+        },
+        {
+          "content-length": "178"
+        },
+        {
+          "cache-control": "public, max-age=25638838"
+        },
+        {
+          "expires": "Tue, 27 Aug 2013 06:45:13 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:15 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 26 Oct 2012 21:42:07 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "rg/3x10ePyW5+Yv14okaeMgQpdIDitUpRdeQlHd62wU="
+        },
+        {
+          "content-length": "3403"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "public, max-age=31099667"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 11:39:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:15 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:11:34 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "+G0d7Y1/nAK76h5l1ygZcgFyqkHdYYjzu9bN8TThTW0="
+        },
+        {
+          "content-length": "3794"
+        },
+        {
+          "cache-control": "public, max-age=31090886"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 09:12:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cnection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Thu, 27 Sep 2012 22:19:28 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "Zx9+0hICgorbmj40+TVQqx/6DWk0JFijw5sOouOK4x8="
+        },
+        {
+          "content-length": "4316"
+        },
+        {
+          "cache-control": "public, max-age=31191442"
+        },
+        {
+          "expires": "Wed, 30 Oct 2013 13:08:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:02:59 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "y9gp03qLmGwdrjrggsFyxKUnduRuH6ZkhHy3J217wnA="
+        },
+        {
+          "content-length": "82"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "public, max-age=25628121"
+        },
+        {
+          "expires": "Tue, 27 Aug 2013 03:46:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:15 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "y31IzOtXz6QEqDb4Yh8nL9E7Jz3QdrtFTVTfJpFI67s="
+        },
+        {
+          "content-length": "281"
+        },
+        {
+          "cache-control": "public, max-age=25628127"
+        },
+        {
+          "expires": "Tue, 27 Aug 2013 03:46:43 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cnection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:07:48 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "cdKo7nf6SbFgEUsu8p8ZpYkyd14IkSsYwu8pEpjIPW8="
+        },
+        {
+          "content-length": "18581"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "public, max-age=31068960"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 03:07:15 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:15 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "text/css; charset=utf-8"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:28:22 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "Mcoj0fymAm3BWRvLoE9uVgrJkXk8Wldn9hUKly6PE60="
+        },
+        {
+          "content-length": "686"
+        },
+        {
+          "cache-control": "public, max-age=31067324"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 02:40:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cnection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "70824"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 14:35:10 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "U6CnJQe7lFM5/wvYBRcEyvixo284qs3dxFI4vIJ3Sfo="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=31117935"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 16:43:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:15 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "6953"
+        },
+        {
+          "content-type": "application/x-shockwave-flash"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 18:56:50 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "8ROXKJ/K5IoNZG3RcJoN8LoohKyoDW2iTe6nY9hRINI="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=260332"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 13:10:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "6953"
+        },
+        {
+          "content-type": "application/x-shockwave-flash"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 18:56:50 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "8ROXKJ/K5IoNZG3RcJoN8LoohKyoDW2iTe6nY9hRINI="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=260331"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 13:10:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:21 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 17:45:29 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-fb-debug": "casrvtlqM38DGgUK+sC64wYFWqXchCM2wnMjgM8VC98="
+        },
+        {
+          "content-length": "15685"
+        },
+        {
+          "cache-control": "public, max-age=31299110"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 19:03:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cnection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "1591"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "last-modified": "Thu, 20 Sep 2012 01:12:35 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "E9XqLqcAPtaMWK+vlxTTyhNPMewUq9nSCKax+m9KMwk="
+        },
+        {
+          "x-cnection": "close"
+        },
+        {
+          "cache-control": "public, max-age=28776235"
+        },
+        {
+          "expires": "Wed, 02 Oct 2013 14:15:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:51:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_27.json b/jetty-http2/http2-hpack/src/test/resources/data/story_27.json
new file mode 100644
index 0000000..0a0077d
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_27.json
@@ -0,0 +1,9890 @@
+{
+  "context": "response",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:12 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:12 GMT; path=/; domain=.flickr.com"
+        },
+        {
+          "location": "http://www.flickr.com/"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "x-served-by": "www199.flickr.mud.yahoo.com"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "20"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:13 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 00:05:01 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www199.flickr.mud.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "no-store, no-cache, must-revalidate, max-age=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "via": "HTTP/1.1 r16.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ]), HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:15 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "private, no-store, max-age=0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 09:50:22 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Fri, 06 Jul 2012 19:15:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www25.flickr.mud.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "max-age=315360000"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "age": "273053"
+        },
+        {
+          "via": "HTTP/1.1 r42.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cHs f ]), HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cHs f ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "35102"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 04:23:38 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Tue, 06 Mar 2012 23:48:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "21830"
+        },
+        {
+          "x-served-by": "www145.flickr.mud.yahoo.com"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "cache-control": "max-age=315360000"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "age": "206257"
+        },
+        {
+          "via": "HTTP/1.1 r46.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cHs f ]), HTTP/1.1 r9.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cHs f ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:15 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Fri, 06 Jul 2012 19:15:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www187.flickr.mud.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "via": "HTTP/1.1 r44.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ]), HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "35102"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:15 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:16 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 355 dc10_ne1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:17 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "content-length": "327"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "fpc site tracked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:28:23 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://p3p.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE GOV\""
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:00:19 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding,User-Agent"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=3600"
+        },
+        {
+          "content-type": "text/plain; charset=utf-8"
+        },
+        {
+          "age": "773"
+        },
+        {
+          "content-length": "111260"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "ATS/3.2.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:16 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:19 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Fri, 06 Jul 2012 19:15:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www18.flickr.mud.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "via": "HTTP/1.1 r48.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ]), HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "35102"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "fldetectedlang=en-us; expires=Wed, 02-Jan-2013 13:41:19 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:20 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5002"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:30:08 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:33 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "45842"
+        },
+        {
+          "x-cache": "HIT from photocache510.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache510.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache510.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:20 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2849"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:51:39 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:30 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "88063"
+        },
+        {
+          "x-cache": "HIT from photocache540.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache540.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache540.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:20 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3533"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Thu, 27 Oct 2022 22:31:31 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:27 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "609449"
+        },
+        {
+          "x-cache": "HIT from photocache530.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache530.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache530.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:20 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "8165"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sun, 03 Jul 2022 07:34:11 UTC"
+        },
+        {
+          "last-modified": "Tue, 10 Nov 2009 20:15:34 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "1332"
+        },
+        {
+          "x-cache": "HIT from photocache324.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache324.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache324.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:20 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "16040"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:51:39 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:32 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "104768"
+        },
+        {
+          "x-cache": "HIT from photocache518.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache518.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache518.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:20 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "16818"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:51:39 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:19 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "104768"
+        },
+        {
+          "x-cache": "HIT from photocache530.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache530.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache530.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:20 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "258838"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 09:14:20 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:34 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "3930"
+        },
+        {
+          "x-cache": "HIT from photocache534.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache534.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache534.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:20 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "15513"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 09:47:34 UTC"
+        },
+        {
+          "last-modified": "Mon, 06 Jun 2011 11:22:59 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "9645"
+        },
+        {
+          "x-cache": "HIT from photocache502.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache502.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache502.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:20 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 380 dc11_ne1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:21 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "fpc site tracked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:21 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7878"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Wed, 20 Jul 2022 12:23:18 UTC"
+        },
+        {
+          "last-modified": "Mon, 23 Jun 2008 19:15:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "41746"
+        },
+        {
+          "x-cache": "HIT from photocache114.flickr.mud.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache114.flickr.mud.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache114.flickr.mud.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:21 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:21 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3339"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sat, 01 Oct 2022 14:49:54 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Oct 2012 03:50:32 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "79702"
+        },
+        {
+          "x-cache": "HIT from photocache902.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache902.flickr.bf1.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache902.flickr.bf1.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:21 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Fri, 06 Jul 2012 19:15:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www145.flickr.mud.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/javascript; charset=utf-8"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "via": "HTTP/1.1 r02.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ]), HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "35102"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "fldetectedlang=en-us; expires=Wed, 02-Jan-2013 13:41:19 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:22 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3403"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 11:01:02 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 17:19:47 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "146624"
+        },
+        {
+          "x-cache": "HIT from photocache538.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache538.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache538.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:22 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3018"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 11:35:06 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 17:19:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "498650"
+        },
+        {
+          "x-cache": "HIT from photocache509.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache509.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache509.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:22 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3493"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 11:01:02 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 17:19:49 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "184716"
+        },
+        {
+          "x-cache": "HIT from photocache507.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache507.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache507.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:22 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "19066"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 11:35:06 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 17:19:39 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "498650"
+        },
+        {
+          "x-cache": "HIT from photocache534.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache534.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache534.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:22 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "14587"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 11:35:06 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:44:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "498650"
+        },
+        {
+          "x-cache": "HIT from photocache512.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache512.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache512.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:22 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "16541"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 09:51:39 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:55 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "64145"
+        },
+        {
+          "x-cache": "HIT from photocache538.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache538.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache538.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:22 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "17669"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 11:35:06 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:48 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "498070"
+        },
+        {
+          "x-cache": "HIT from photocache536.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache536.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache536.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:22 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "17009"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 11:35:06 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:40 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "498070"
+        },
+        {
+          "x-cache": "HIT from photocache529.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache529.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache529.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:22 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "110090"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 09:50:49 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "104763"
+        },
+        {
+          "x-cache": "HIT from photocache508.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache508.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache508.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:22 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "121145"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 09:14:22 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:19 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "104763"
+        },
+        {
+          "x-cache": "HIT from photocache506.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache506.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache506.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:23 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Fri, 06 Jul 2012 19:15:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www105.flickr.mud.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "via": "HTTP/1.1 r08.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ]), HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "35102"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:23 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:24 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 328 dc26_ne1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:25 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "fpc site tracked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:24 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5372"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sun, 24 Jul 2022 05:03:05 UTC"
+        },
+        {
+          "last-modified": "Sat, 10 Jul 2010 18:58:01 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "113797"
+        },
+        {
+          "x-cache": "HIT from photocache417.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache417.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache417.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:24 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:25 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "101072"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 09:13:50 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:33 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "399124"
+        },
+        {
+          "x-cache": "HIT from photocache502.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache502.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache502.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:25 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Fri, 06 Jul 2012 19:15:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www135.flickr.mud.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "age": "3"
+        },
+        {
+          "via": "HTTP/1.1 r34.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ]), HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "35102"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:25 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:26 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 358 dc28_ne1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:27 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "fpc site tracked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:26 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:26 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:27 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:27 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "131822"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 09:13:19 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:40 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "399125"
+        },
+        {
+          "x-cache": "HIT from photocache529.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache529.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache529.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:27 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Fri, 06 Jul 2012 19:15:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www198.flickr.mud.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "age": "3"
+        },
+        {
+          "via": "HTTP/1.1 r34.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ]), HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "35102"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:27 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:28 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 366 dc36_ne1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:29 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "fpc site tracked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:29 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:29 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "137767"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 09:13:04 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:48 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "959688"
+        },
+        {
+          "x-cache": "HIT from photocache512.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache512.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache512.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Fri, 06 Jul 2012 19:15:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www24.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "age": "1"
+        },
+        {
+          "via": "HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "35102"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:30 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "9755"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 13:27:41 UTC"
+        },
+        {
+          "last-modified": "Mon, 04 Oct 2010 23:11:18 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "783768"
+        },
+        {
+          "x-cache": "HIT from photocache526.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache526.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache526.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "8018"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 13:27:41 UTC"
+        },
+        {
+          "last-modified": "Mon, 04 Oct 2010 23:11:16 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "783767"
+        },
+        {
+          "x-cache": "HIT from photocache521.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache521.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache521.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5509"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 13:27:41 UTC"
+        },
+        {
+          "last-modified": "Mon, 04 Oct 2010 23:11:19 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "849721"
+        },
+        {
+          "x-cache": "HIT from photocache517.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache517.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache517.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "24244"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 13:27:41 UTC"
+        },
+        {
+          "last-modified": "Mon, 04 Oct 2010 23:11:19 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "832233"
+        },
+        {
+          "x-cache": "HIT from photocache503.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache503.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache503.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "15699"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:49:46 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 17:19:54 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "184724"
+        },
+        {
+          "x-cache": "HIT from photocache535.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache535.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache535.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "26051"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 13:27:41 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 19:06:02 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "783767"
+        },
+        {
+          "x-cache": "HIT from photocache501.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache501.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache501.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "13263"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:38:08 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 17:19:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "216108"
+        },
+        {
+          "x-cache": "HIT from photocache525.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache525.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache525.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "19324"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sun, 03 Jul 2022 22:23:27 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 17:19:49 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "301732"
+        },
+        {
+          "x-cache": "HIT from photocache539.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache539.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache539.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "21573"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 13:27:42 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 17:19:47 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "14644"
+        },
+        {
+          "x-cache": "HIT from photocache522.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache522.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache522.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "20730"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 13:27:42 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:44:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "301890"
+        },
+        {
+          "x-cache": "HIT from photocache528.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache528.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache528.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "32916"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:38:08 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 17:19:39 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "251785"
+        },
+        {
+          "x-cache": "HIT from photocache526.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache526.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache526.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "26574"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 13:27:42 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:55 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "832232"
+        },
+        {
+          "x-cache": "HIT from photocache506.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache506.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache506.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "27759"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 22:06:23 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:48 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "301883"
+        },
+        {
+          "x-cache": "HIT from photocache528.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache528.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache528.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "11409"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:38:10 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "832232"
+        },
+        {
+          "x-cache": "HIT from photocache524.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache524.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache524.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "26453"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sun, 24 Jul 2022 04:56:22 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:33 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "3058"
+        },
+        {
+          "x-cache": "HIT from photocache526.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache526.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache526.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "23128"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 22:06:23 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:33 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "301877"
+        },
+        {
+          "x-cache": "HIT from photocache534.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache534.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache534.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "28275"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 22:06:23 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:40 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "301881"
+        },
+        {
+          "x-cache": "HIT from photocache505.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache505.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache505.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:30 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "26267"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 22:06:23 UTC"
+        },
+        {
+          "last-modified": "Wed, 15 Sep 2010 19:43:19 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "301876"
+        },
+        {
+          "x-cache": "HIT from photocache514.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache514.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache514.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:31 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 369 dc23_ne1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:32 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "fpc site tracked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:31 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:31 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:31 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Wed, 31 Oct 2012 09:31:12 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Tue, 06 Mar 2012 23:48:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www96.flickr.mud.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "max-age=1209600"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/vnd.microsoft.icon"
+        },
+        {
+          "age": "274220"
+        },
+        {
+          "via": "HTTP/1.1 r15.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cHs f ]), HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cHs f ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "6640"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:30 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:33 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Tue, 06 Mar 2012 23:48:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www1.flickr.mud.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "via": "HTTP/1.1 r12.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ]), HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "6640"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:33 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "19144"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:38:09 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:27 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "301872"
+        },
+        {
+          "x-cache": "HIT from photocache508.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache508.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache508.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "24110"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:38:09 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "840419"
+        },
+        {
+          "x-cache": "HIT from photocache505.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache505.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache505.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "32406"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 18:43:07 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "166451"
+        },
+        {
+          "x-cache": "HIT from photocache529.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache529.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache529.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "20059"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:38:09 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "382867"
+        },
+        {
+          "x-cache": "HIT from photocache505.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache505.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache505.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "23332"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:41:55 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "68360"
+        },
+        {
+          "x-cache": "HIT from photocache525.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache525.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache525.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "18543"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 11:24:48 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "458533"
+        },
+        {
+          "x-cache": "HIT from photocache538.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache538.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache538.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "15399"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sun, 30 Oct 2022 01:48:16 UTC"
+        },
+        {
+          "last-modified": "Wed, 31 Mar 2010 17:47:11 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "424858"
+        },
+        {
+          "x-cache": "HIT from photocache324.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache324.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache324.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "29793"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Tue, 16 Aug 2022 22:34:17 UTC"
+        },
+        {
+          "last-modified": "Mon, 17 May 2010 22:00:34 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "840419"
+        },
+        {
+          "x-cache": "HIT from photocache401.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache401.flickr.ac4.yahoo.com:81"
+        },
+        {
+          "via": "1.1 photocache401.flickr.ac4.yahoo.com:81 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "13571"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 11:16:36 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "783188"
+        },
+        {
+          "x-cache": "HIT from photocache508.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache508.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache508.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "18149"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sun, 12 Jun 2022 23:12:34 UTC"
+        },
+        {
+          "last-modified": "Mon, 17 May 2010 21:59:16 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "953364"
+        },
+        {
+          "x-cache": "HIT from photocache414.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache414.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache414.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "15403"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:38:09 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "388250"
+        },
+        {
+          "x-cache": "HIT from photocache529.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache529.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache529.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "14796"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 22:06:24 UTC"
+        },
+        {
+          "last-modified": "Mon, 17 May 2010 21:59:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "320180"
+        },
+        {
+          "x-cache": "HIT from photocache506.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache506.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache506.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "18710"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:38:09 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:24 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "840419"
+        },
+        {
+          "x-cache": "HIT from photocache531.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache531.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache531.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "32555"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sun, 19 Jun 2022 15:01:08 UTC"
+        },
+        {
+          "last-modified": "Mon, 17 May 2010 22:00:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "58741"
+        },
+        {
+          "x-cache": "HIT from photocache412.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache412.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache412.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "13726"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:38:10 UTC"
+        },
+        {
+          "last-modified": "Mon, 17 May 2010 21:59:24 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "840419"
+        },
+        {
+          "x-cache": "HIT from photocache531.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache531.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache531.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "27660"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 22:06:36 UTC"
+        },
+        {
+          "last-modified": "Mon, 17 May 2010 21:59:47 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "320180"
+        },
+        {
+          "x-cache": "HIT from photocache526.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache526.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache526.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "19545"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:38:09 UTC"
+        },
+        {
+          "last-modified": "Mon, 17 May 2010 21:59:30 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "840419"
+        },
+        {
+          "x-cache": "HIT from photocache517.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache517.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache517.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "30518"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 16:28:03 UTC"
+        },
+        {
+          "last-modified": "Wed, 31 Mar 2010 17:47:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "49685"
+        },
+        {
+          "x-cache": "HIT from photocache507.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache507.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache507.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:34 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 310 dc33_ne1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:35 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "fpc site tracked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:35 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:38 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 342 dc32_ne1"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:39 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "site tracked"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:39 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Tue, 06 Mar 2012 23:48:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www31.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "age": "3"
+        },
+        {
+          "via": "HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "6640"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:39 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:40 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "private, no-store, max-age=0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:41 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 352 dc19_ne1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:42 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "fpc site tracked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "imp=a$le#1351950101610_669834368_ap2101_int|; Domain=.teracent.net; Expires=Thu, 02-May-2013 13:41:41 GMT; Path=/tase"
+        },
+        {
+          "p3p": "CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "expires": "Sat, 6 May 1995 12:00:00 GMT"
+        },
+        {
+          "cache-control": "post-check=0, pre-check=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:41 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:41 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Tue, 06 Mar 2012 23:48:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www56.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "via": "HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "6640"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:44 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2830"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 07:50:16 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:41:21 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "4009"
+        },
+        {
+          "x-cache": "HIT from photocache501.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache501.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache501.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4884"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 07:44:25 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:41:51 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "159147"
+        },
+        {
+          "x-cache": "HIT from photocache520.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache520.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache520.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3513"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 07:49:35 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:41:18 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "4010"
+        },
+        {
+          "x-cache": "HIT from photocache507.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache507.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache507.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6899"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 07:49:08 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:41:22 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "2328198"
+        },
+        {
+          "x-cache": "HIT from photocache518.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache518.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache518.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4695"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Thu, 03 Nov 2022 23:49:24 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:41:39 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "x-cache": "MISS from photocache514.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "MISS from photocache514.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache514.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2596"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 07:55:46 UTC"
+        },
+        {
+          "last-modified": "Mon, 04 Oct 2010 23:11:49 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431512"
+        },
+        {
+          "x-cache": "HIT from photocache518.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache518.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache518.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3516"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 17:53:36 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 17:19:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "302049"
+        },
+        {
+          "x-cache": "HIT from photocache501.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache501.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache501.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4922"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 17:19:47 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "36944"
+        },
+        {
+          "x-cache": "HIT from photocache522.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache522.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache522.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3158"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 07:46:15 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:41:39 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "2328157"
+        },
+        {
+          "x-cache": "HIT from photocache512.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache512.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache512.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4541"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:12 UTC"
+        },
+        {
+          "last-modified": "Thu, 16 Sep 2010 17:19:49 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "x-cache": "HIT from photocache507.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache507.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache507.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        },
+        {
+          "age": "302053"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4145"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 07:51:21 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:40:59 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "2328231"
+        },
+        {
+          "x-cache": "HIT from photocache508.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache508.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache508.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3419"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:06:37 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "650868"
+        },
+        {
+          "x-cache": "HIT from photocache506.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache506.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache506.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5035"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 20:41:31 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:41:02 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "3999"
+        },
+        {
+          "x-cache": "HIT from photocache534.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache534.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache534.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:44 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "private, no-store, max-age=0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:45 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 305 dc9_ne1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:46 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "content-length": "45"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "fpc site tracked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:45 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5313"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Thu, 01 Sep 2022 04:55:06 UTC"
+        },
+        {
+          "last-modified": "Fri, 31 Aug 2012 18:28:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "959613"
+        },
+        {
+          "x-cache": "HIT from photocache906.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache906.flickr.bf1.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache906.flickr.bf1.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:45 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4675"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Wed, 07 Sep 2022 05:56:52 UTC"
+        },
+        {
+          "last-modified": "Thu, 06 Sep 2012 18:42:55 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "338945"
+        },
+        {
+          "x-cache": "HIT from photocache907.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache907.flickr.bf1.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache907.flickr.bf1.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:45 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7282"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sun, 28 Aug 2022 09:31:01 UTC"
+        },
+        {
+          "last-modified": "Mon, 27 Aug 2012 22:28:20 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "73654"
+        },
+        {
+          "x-cache": "HIT from photocache924.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache924.flickr.bf1.yahoo.com:85"
+        },
+        {
+          "via": "1.1 photocache924.flickr.bf1.yahoo.com:85 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:45 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4242"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Thu, 01 Sep 2022 04:55:06 UTC"
+        },
+        {
+          "last-modified": "Fri, 31 Aug 2012 18:28:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "959613"
+        },
+        {
+          "x-cache": "HIT from photocache926.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache926.flickr.bf1.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache926.flickr.bf1.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:45 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4561"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sun, 17 Jul 2022 03:19:16 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:41:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "4005"
+        },
+        {
+          "x-cache": "HIT from photocache203.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache203.flickr.bf1.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache203.flickr.bf1.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:45 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6350"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sun, 17 Jul 2022 04:55:35 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 16:18:41 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431728"
+        },
+        {
+          "x-cache": "HIT from photocache204.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache204.flickr.bf1.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache204.flickr.bf1.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:45 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3341"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sun, 17 Jul 2022 18:09:37 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:41:14 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "2328215"
+        },
+        {
+          "x-cache": "HIT from photocache205.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache205.flickr.bf1.yahoo.com:85"
+        },
+        {
+          "via": "1.1 photocache205.flickr.bf1.yahoo.com:85 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:45 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7005"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 18 Jul 2022 01:41:12 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:41:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "4064"
+        },
+        {
+          "x-cache": "HIT from photocache202.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache202.flickr.bf1.yahoo.com:85"
+        },
+        {
+          "via": "1.1 photocache202.flickr.bf1.yahoo.com:85 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:45 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4068"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 18 Jul 2022 03:45:04 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:41:01 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "438025"
+        },
+        {
+          "x-cache": "HIT from photocache202.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache202.flickr.bf1.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache202.flickr.bf1.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:45 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7960"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 18 Jul 2022 03:44:32 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:41:20 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "2328207"
+        },
+        {
+          "x-cache": "HIT from photocache205.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache205.flickr.bf1.yahoo.com:85"
+        },
+        {
+          "via": "1.1 photocache205.flickr.bf1.yahoo.com:85 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:45 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:46 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4395"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 18 Jul 2022 03:45:05 UTC"
+        },
+        {
+          "last-modified": "Mon, 01 Nov 2010 05:40:57 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "2328241"
+        },
+        {
+          "x-cache": "HIT from photocache202.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache202.flickr.bf1.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache202.flickr.bf1.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:48 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Tue, 06 Mar 2012 23:48:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www30.flickr.bf1.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "age": "3"
+        },
+        {
+          "via": "HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "6640"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:48 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3706"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:24 UTC"
+        },
+        {
+          "last-modified": "Mon, 17 May 2010 21:59:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "608527"
+        },
+        {
+          "x-cache": "HIT from photocache522.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache522.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache522.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4610"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 16:47:58 UTC"
+        },
+        {
+          "last-modified": "Wed, 04 Aug 2010 23:21:27 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "598783"
+        },
+        {
+          "x-cache": "HIT from photocache515.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache515.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache515.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2551"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:12 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:45 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "801137"
+        },
+        {
+          "x-cache": "HIT from photocache529.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache529.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache529.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2822"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 11:43:25 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "x-cache": "HIT from photocache504.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache504.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache504.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        },
+        {
+          "age": "151390"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4463"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:56:37 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:39 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "252496"
+        },
+        {
+          "x-cache": "HIT from photocache520.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache520.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache520.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1441"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:25 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:11 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "496025"
+        },
+        {
+          "x-cache": "HIT from photocache501.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache501.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache501.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3780"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sat, 22 Oct 2022 00:35:44 UTC"
+        },
+        {
+          "last-modified": "Wed, 31 Mar 2010 17:47:11 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "988214"
+        },
+        {
+          "x-cache": "HIT from photocache312.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache312.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache312.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5215"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 24 Oct 2022 02:15:29 UTC"
+        },
+        {
+          "last-modified": "Wed, 31 Mar 2010 17:47:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431732"
+        },
+        {
+          "x-cache": "HIT from photocache324.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache324.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache324.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2610"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:24 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:49:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "872080"
+        },
+        {
+          "x-cache": "HIT from photocache520.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache520.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache520.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4241"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 08:00:12 UTC"
+        },
+        {
+          "last-modified": "Wed, 31 Mar 2010 17:47:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "2356"
+        },
+        {
+          "x-cache": "HIT from photocache513.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache513.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache513.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4461"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:10:07 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "301869"
+        },
+        {
+          "x-cache": "HIT from photocache505.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache505.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache505.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4292"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 13:51:59 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "258540"
+        },
+        {
+          "x-cache": "HIT from photocache521.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache521.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache521.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3353"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:49:11 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "x-cache": "HIT from photocache518.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache518.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache518.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        },
+        {
+          "age": "496025"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4008"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 20:42:08 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:10 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "435217"
+        },
+        {
+          "x-cache": "HIT from photocache522.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache522.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache522.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4081"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:43:52 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:39 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "435217"
+        },
+        {
+          "x-cache": "HIT from photocache521.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache521.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache521.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3996"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 15:30:11 UTC"
+        },
+        {
+          "last-modified": "Wed, 31 Mar 2010 17:46:59 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "872288"
+        },
+        {
+          "x-cache": "HIT from photocache522.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache522.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache522.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2178"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 20:42:08 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:44 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431529"
+        },
+        {
+          "x-cache": "HIT from photocache523.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache523.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache523.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3292"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:39:41 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:21 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431732"
+        },
+        {
+          "x-cache": "HIT from photocache533.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache533.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache533.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3415"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:34 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "89675"
+        },
+        {
+          "x-cache": "HIT from photocache522.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache522.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache522.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3963"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 15:19:56 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:59:09 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "x-cache": "HIT from photocache533.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache533.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache533.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        },
+        {
+          "age": "89675"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3036"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Wed, 04 Aug 2010 23:21:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "245531"
+        },
+        {
+          "x-cache": "HIT from photocache511.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache511.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache511.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1843"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:40 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431517"
+        },
+        {
+          "x-cache": "HIT from photocache537.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache537.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache537.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6971"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "301877"
+        },
+        {
+          "x-cache": "HIT from photocache537.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache537.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache537.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3968"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:24 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:49:02 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431731"
+        },
+        {
+          "x-cache": "HIT from photocache527.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache527.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache527.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:49 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "private, no-store, max-age=0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:50 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 326 dc12_ne1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:51 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "fpc site tracked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:50 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:50 GMT"
+        },
+        {
+          "server": "YTS/1.20.13"
+        },
+        {
+          "x-rightmedia-hostname": "raptor0740.rm.bf1.yahoo.com"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID CURa ADMa DEVa PSAa PSDa OUR BUS COM INT OTC PUR STA\""
+        },
+        {
+          "location": "http://ad.yieldmanager.com/pixel?id=365081&t=2"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate, max-age=0"
+        },
+        {
+          "vary": "*"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:41:50 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:50 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4726"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 17:54:01 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:36 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "211089"
+        },
+        {
+          "x-cache": "HIT from photocache519.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache519.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache519.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4433"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 04 Jul 2022 00:08:57 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:49:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431736"
+        },
+        {
+          "x-cache": "HIT from photocache539.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache539.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache539.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2677"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:24 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:49:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431735"
+        },
+        {
+          "x-cache": "HIT from photocache524.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache524.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache524.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3081"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:24 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:54 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "164867"
+        },
+        {
+          "x-cache": "HIT from photocache511.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache511.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache511.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3457"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:39 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "435221"
+        },
+        {
+          "x-cache": "HIT from photocache526.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache526.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache526.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4433"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:12 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:42 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "424621"
+        },
+        {
+          "x-cache": "HIT from photocache528.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache528.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache528.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "6486"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:24 UTC"
+        },
+        {
+          "last-modified": "Wed, 31 Mar 2010 17:46:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431736"
+        },
+        {
+          "x-cache": "HIT from photocache540.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache540.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache540.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4899"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:12 UTC"
+        },
+        {
+          "last-modified": "Wed, 04 Aug 2010 23:21:41 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "x-cache": "HIT from photocache507.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache507.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache507.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        },
+        {
+          "age": "598778"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3981"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sat, 15 Oct 2022 23:23:06 UTC"
+        },
+        {
+          "last-modified": "Wed, 31 Mar 2010 17:46:57 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431687"
+        },
+        {
+          "x-cache": "HIT from photocache311.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache311.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache311.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7338"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sun, 12 Jun 2022 22:06:49 UTC"
+        },
+        {
+          "last-modified": "Mon, 17 May 2010 22:00:34 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "608533"
+        },
+        {
+          "x-cache": "HIT from photocache415.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache415.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache415.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5640"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:48:36 UTC"
+        },
+        {
+          "last-modified": "Wed, 31 Mar 2010 17:47:01 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "988205"
+        },
+        {
+          "x-cache": "HIT from photocache505.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache505.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache505.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4609"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:24 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:24 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "871819"
+        },
+        {
+          "x-cache": "HIT from photocache530.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache530.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache530.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3776"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:39:41 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:30 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431533"
+        },
+        {
+          "x-cache": "HIT from photocache531.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache531.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache531.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4551"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 11:35:21 UTC"
+        },
+        {
+          "last-modified": "Tue, 17 Aug 2010 18:40:02 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "415587"
+        },
+        {
+          "x-cache": "HIT from photocache522.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache522.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache522.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4452"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 20:41:31 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:44 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "36952"
+        },
+        {
+          "x-cache": "HIT from photocache505.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache505.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache505.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2023"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 20:45:55 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:45 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "x-cache": "HIT from photocache533.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache533.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache533.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        },
+        {
+          "age": "682487"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:51 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Tue, 06 Mar 2012 23:48:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www144.flickr.mud.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "age": "3"
+        },
+        {
+          "via": "HTTP/1.1 r29.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ]), HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "6640"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:51 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4675"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:44:19 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:59:16 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "89679"
+        },
+        {
+          "x-cache": "HIT from photocache502.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache502.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache502.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3139"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:09:56 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:59:00 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "89670"
+        },
+        {
+          "x-cache": "HIT from photocache531.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache531.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache531.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3167"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:12 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:26 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431533"
+        },
+        {
+          "x-cache": "HIT from photocache515.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache515.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache515.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4227"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:25 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "89679"
+        },
+        {
+          "x-cache": "HIT from photocache505.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache505.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache505.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2768"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 11:43:02 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:09 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "x-cache": "HIT from photocache506.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache506.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache506.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        },
+        {
+          "age": "206489"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4375"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:24 UTC"
+        },
+        {
+          "last-modified": "Mon, 17 May 2010 21:59:30 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "988235"
+        },
+        {
+          "x-cache": "HIT from photocache525.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache525.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache525.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3506"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 18:52:47 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:36 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431534"
+        },
+        {
+          "x-cache": "HIT from photocache507.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache507.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache507.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3522"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:24 UTC"
+        },
+        {
+          "last-modified": "Mon, 17 May 2010 21:59:24 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "988233"
+        },
+        {
+          "x-cache": "HIT from photocache515.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache515.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache515.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:53 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "private, no-store, max-age=0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:54 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 307 dc1_ne1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:55 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "content-length": "45"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "fpc site tracked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:54 GMT"
+        },
+        {
+          "server": "YTS/1.20.13"
+        },
+        {
+          "x-rightmedia-hostname": "raptor0291.rm.bf1.yahoo.com"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID CURa ADMa DEVa PSAa PSDa OUR BUS COM INT OTC PUR STA\""
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate, max-age=0"
+        },
+        {
+          "vary": "*"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:41:54 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:54 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:54 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:54 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:54 GMT"
+        },
+        {
+          "server": "YTS/1.20.13"
+        },
+        {
+          "x-rightmedia-hostname": "raptor0921.rm.bf1.yahoo.com"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID CURa ADMa DEVa PSAa PSDa OUR BUS COM INT OTC PUR STA\""
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate, max-age=0"
+        },
+        {
+          "vary": "*"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:41:54 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:54 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "location": "http://ad.yieldmanager.com/imp?Z=1x1&s=768714&T=3&_salt=2374354217&B=12&m=2&u=http%3A%2F%2Fwww.flickr.com%2Fphotos%2Fnasacommons%2Ftags%2Fnationalaeronauticsandspaceadministration%2Fpage3%2F&r=0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:55 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "last-modified": "Tue, 06 Mar 2012 23:48:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "x-served-by": "www13.flickr.mud.yahoo.com"
+        },
+        {
+          "x-flickr-static": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "via": "HTTP/1.1 r18.ycpi.mud.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ]), HTTP/1.1 r02.ycpi.mia.yahoo.net (YahooTrafficServer/1.20.20 [cMsSf ])"
+        },
+        {
+          "server": "YTS/1.20.20"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-length": "6640"
+        },
+        {
+          "expires": "Mon, 28 Jul 2014 23:30:00 GMT"
+        },
+        {
+          "set-cookie": "localization=en-us%3Bus%3Bus; expires=Sat, 01-Nov-2014 13:41:55 GMT; path=/; domain=.flickr.com"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5525"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:39:41 UTC"
+        },
+        {
+          "last-modified": "Wed, 04 Aug 2010 23:21:42 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "435224"
+        },
+        {
+          "x-cache": "HIT from photocache503.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache503.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache503.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4651"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:21:30 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:59:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "166264"
+        },
+        {
+          "x-cache": "HIT from photocache524.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache524.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache524.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4344"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:43:52 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "301880"
+        },
+        {
+          "x-cache": "HIT from photocache506.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache506.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache506.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4372"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:35 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431536"
+        },
+        {
+          "x-cache": "HIT from photocache525.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache525.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache525.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4446"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:44 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431689"
+        },
+        {
+          "x-cache": "HIT from photocache534.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache534.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache534.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4431"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Wed, 31 Mar 2010 17:46:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "x-cache": "HIT from photocache511.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache511.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache511.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        },
+        {
+          "age": "431690"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "13813"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Sat, 03 Sep 2022 23:41:41 UTC"
+        },
+        {
+          "last-modified": "Mon, 17 May 2010 22:00:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "608533"
+        },
+        {
+          "x-cache": "HIT from photocache408.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache408.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache408.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2010"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:43:52 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431537"
+        },
+        {
+          "x-cache": "HIT from photocache535.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache535.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache535.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4596"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:24 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:27 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "301900"
+        },
+        {
+          "x-cache": "HIT from photocache540.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache540.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache540.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4383"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:25 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:51 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "x-cache": "HIT from photocache540.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache540.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache540.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        },
+        {
+          "age": "89682"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3501"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 08:02:11 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:59:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "89673"
+        },
+        {
+          "x-cache": "HIT from photocache506.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache506.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache506.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3829"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:11 UTC"
+        },
+        {
+          "last-modified": "Wed, 04 Aug 2010 23:21:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "871646"
+        },
+        {
+          "x-cache": "HIT from photocache523.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache523.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache523.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3781"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 20:46:07 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:49:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "435223"
+        },
+        {
+          "x-cache": "HIT from photocache502.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache502.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache502.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4186"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 20:41:31 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:51 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "363909"
+        },
+        {
+          "x-cache": "HIT from photocache524.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache524.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache524.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3814"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:43:52 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:59:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "x-cache": "HIT from photocache537.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache537.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache537.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        },
+        {
+          "age": "89682"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2478"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:25 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:11 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431536"
+        },
+        {
+          "x-cache": "HIT from photocache511.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache511.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache511.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "7387"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Wed, 31 Mar 2010 17:47:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431739"
+        },
+        {
+          "x-cache": "HIT from photocache523.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache523.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache523.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5870"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:04:10 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "337938"
+        },
+        {
+          "x-cache": "HIT from photocache525.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache525.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache525.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "5202"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:40:12 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:57:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431738"
+        },
+        {
+          "x-cache": "HIT from photocache505.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache505.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache505.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2820"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "431536"
+        },
+        {
+          "x-cache": "HIT from photocache538.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache538.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache538.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3044"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:43:52 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:07:30 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "x-cache": "HIT from photocache516.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache516.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache516.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        },
+        {
+          "age": "2363"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "4834"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 12:34:28 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:59:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "89682"
+        },
+        {
+          "x-cache": "HIT from photocache522.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache522.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache522.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3365"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:56:07 UTC"
+        },
+        {
+          "last-modified": "Tue, 03 Aug 2010 22:58:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "252421"
+        },
+        {
+          "x-cache": "HIT from photocache528.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache528.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache528.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "3978"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "max-age=315360000,public"
+        },
+        {
+          "expires": "Mon, 30 May 2022 10:39:41 UTC"
+        },
+        {
+          "last-modified": "Mon, 30 Aug 2010 06:49:20 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "age": "872078"
+        },
+        {
+          "x-cache": "HIT from photocache532.flickr.ac4.yahoo.com"
+        },
+        {
+          "x-cache-lookup": "HIT from photocache532.flickr.ac4.yahoo.com:83"
+        },
+        {
+          "via": "1.1 photocache532.flickr.ac4.yahoo.com:83 (squid/2.7.STABLE9)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:56 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "private, no-store, max-age=0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:57 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "set-cookie": "itsessionid10001561398679=aUqazlyMaa|fses10001561398679=; path=/; domain=.analytics.yahoo.com"
+        },
+        {
+          "ts": "0 372 dc33_ne1"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:58 GMT"
+        },
+        {
+          "cache-control": "no-cache, private, must-revalidate"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "tracking-status": "fpc site tracked"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:57 GMT"
+        },
+        {
+          "server": "YTS/1.20.13"
+        },
+        {
+          "x-rightmedia-hostname": "raptor0663.rm.bf1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID CURa ADMa DEVa PSAa PSDa OUR BUS COM INT OTC PUR STA\""
+        },
+        {
+          "location": "http://ad.yieldmanager.com/pixel?id=372009&t=2"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate, max-age=0"
+        },
+        {
+          "vary": "*"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:41:57 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:57 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:57 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:57 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:57 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:41:58 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://info.yahoo.com/w3c/p3p.xml\", CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV\""
+        },
+        {
+          "cache-control": "no-cache, no-store, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_28.json b/jetty-http2/http2-hpack/src/test/resources/data/story_28.json
new file mode 100644
index 0000000..26c5a59
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_28.json
@@ -0,0 +1,5293 @@
+{
+  "context": "response",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":status": "301"
+        },
+        {
+          "location": "http://www.linkedin.com/"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:39 GMT"
+        },
+        {
+          "server": "lighttpd"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "X-LI-IDC=C1"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-li-uuid": "E2zvmRmjhYEFJpx7GePGrg=="
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:39 GMT"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 22:43:31 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4224"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=31224822"
+        },
+        {
+          "expires": "Wed, 30 Oct 2013 22:47:23 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "1757"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=23972673"
+        },
+        {
+          "expires": "Thu, 08 Aug 2013 00:18:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 17:17:34 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "1104"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=31119071"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 17:24:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 22:43:30 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4725"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=31225090"
+        },
+        {
+          "expires": "Wed, 30 Oct 2013 22:51:51 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 22:43:29 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "18531"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=31225288"
+        },
+        {
+          "expires": "Wed, 30 Oct 2013 22:55:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 22:43:30 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-li-uuid": "YP+9O9z6wRIwf5dQLCsAAA=="
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "81464"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=31224801"
+        },
+        {
+          "expires": "Wed, 30 Oct 2013 22:47:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "L1e=495eba97; path=/"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-li-uuid": "gLtcwO0VwxJQ3EsqHysAAA=="
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:43 GMT"
+        },
+        {
+          "age": "1"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 19:08:05 GMT"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-li-uuid": "YP+9O9z6wRIwf5dQLCsAAA=="
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "361"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=31188041"
+        },
+        {
+          "expires": "Wed, 30 Oct 2013 12:34:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 19:11:14 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-li-uuid": "CCdnnfDTwhJwlNCV9ioAAA=="
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=31463444"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 17:04:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:44 GMT"
+        },
+        {
+          "content-length": "3489"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Mon, 13 Aug 2012 19:03:38 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1062"
+        },
+        {
+          "cache-control": "max-age=24472323"
+        },
+        {
+          "expires": "Tue, 13 Aug 2013 19:05:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:44 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "14888"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 15:51:19 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 06:13:29 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 18:13:29 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "age": "25215"
+        },
+        {
+          "cache-control": "max-age=43200, public"
+        },
+        {
+          "server": "GFE/2.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "age": "168216"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "age": "168216"
+        },
+        {
+          "server": "GFE/2.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Sat, 03-Nov-2012 13:13:45 GMT"
+        },
+        {
+          "etag": "M0-0eb75f26"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, no-transform, must-revalidate, max-age=604800"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 13:13:45 GMT"
+        },
+        {
+          "content-length": "2298"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:45 GMT"
+        },
+        {
+          "server": "QS"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "set-cookie": "mc=50951889-343da-16f7e-ae952; expires=Mon, 05-May-2014 13:13:45 GMT; path=/; domain=.quantserve.com"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID CURa ADMa DEVa PSAo PSDo OUR SAMa IND COM NAV\""
+        },
+        {
+          "cache-control": "private, no-cache, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 04 Aug 1978 12:00:00 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:45 GMT"
+        },
+        {
+          "server": "QS"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Sat, 17 Nov 2012 13:13:45 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:45 GMT"
+        },
+        {
+          "content-length": "1140"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-transform, max-age=1209600"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:45 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lang=\"v=2&lang=en-us\"; Version=1; Domain=linkedin.com; Path=/"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-li-uuid": "jguBxDxCP8A3strRU6z0Sw=="
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:49 GMT"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "x-fs-uuid": "8e0b81c43c423fc037b2dad153acf44b"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "lighttpd"
+        },
+        {
+          "set-cookie": "lang=\"v=2&lang=en-us\"; Version=1; Domain=linkedin.com; Path=/"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-li-uuid": "jguBxDxCP8A3strRU6z0Sw=="
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:49 GMT"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "x-fs-uuid": "8e0b81c43c423fc037b2dad153acf44b"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"1672258277\""
+        },
+        {
+          "last-modified": "Wed, 22 Jun 2011 20:28:00 GMT"
+        },
+        {
+          "content-length": "1150"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Thu, 16 Aug 2012 01:24:42 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1044"
+        },
+        {
+          "cache-control": "max-age=24721219"
+        },
+        {
+          "expires": "Fri, 16 Aug 2013 16:14:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:50 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Wed, 17 Oct 2012 23:09:52 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9168"
+        },
+        {
+          "cache-control": "max-age=30168335"
+        },
+        {
+          "expires": "Fri, 18 Oct 2013 17:19:25 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:50 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:51 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, no-cache, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 04 Aug 1978 12:00:00 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:51 GMT"
+        },
+        {
+          "server": "QS"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "age": "168222"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:51 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 18:10:36 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-li-uuid": "eAzMxNUMwxJwGtyV9ioAAA=="
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=31525993"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 10:27:04 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:51 GMT"
+        },
+        {
+          "content-length": "223"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Sat, 01 Sep 2012 00:58:09 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-li-uuid": "CCdnnfDTwhJwlNCV9ioAAA=="
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=26048872"
+        },
+        {
+          "expires": "Sun, 01 Sep 2013 01:01:43 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:51 GMT"
+        },
+        {
+          "content-length": "11160"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lang=\"v=2&lang=en-us\"; Version=1; Domain=linkedin.com; Path=/"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-li-uuid": "AZRbIR9V68fBQlYZzQoS1w=="
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:54 GMT"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "x-fs-uuid": "01945b211f55ebc7c1425619cd0a12d7"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"1672258277\""
+        },
+        {
+          "last-modified": "Wed, 22 Jun 2011 20:28:00 GMT"
+        },
+        {
+          "content-length": "4714"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, no-cache, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 04 Aug 1978 12:00:00 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:54 GMT"
+        },
+        {
+          "server": "QS"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:54 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "age": "168226"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:54 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "lang=\"v=2&lang=en-us\"; Version=1; Domain=linkedin.com; Path=/"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-li-uuid": "wL1PItpHScLBRl0PVkiLLQ=="
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:59 GMT"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "x-fs-uuid": "c0bd4f22da4749c2c1465d0f56488b2d"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"1672258277\""
+        },
+        {
+          "last-modified": "Wed, 22 Jun 2011 20:28:00 GMT"
+        },
+        {
+          "content-length": "4714"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:59 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, no-cache, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 04 Aug 1978 12:00:00 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:59 GMT"
+        },
+        {
+          "server": "QS"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "age": "168230"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:13:59 GMT"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "sl=\"delete me\"; Version=1; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-li-uuid": "vZHDyRjuiQCkhZBzyxGqrg=="
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:01 GMT"
+        },
+        {
+          "age": "3"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "x-fs-uuid": "bd91c3c918ee8900a4859073cb11aaae"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"1672258277\""
+        },
+        {
+          "last-modified": "Fri, 27 Nov 2009 06:27:21 GMT"
+        },
+        {
+          "content-length": "6176"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Tue, 02 Oct 2012 07:53:23 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4012"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=28752189"
+        },
+        {
+          "expires": "Wed, 02 Oct 2013 07:57:11 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 17:17:34 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "628"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=3381907"
+        },
+        {
+          "expires": "Wed, 12 Dec 2012 16:39:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-length": "1044"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=31490642"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 00:38:04 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 18:36:32 GMT"
+        },
+        {
+          "x-li-uuid": "KMg5e7HswhIQwgDTIysAAA=="
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 00:20:08 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "17928"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=31230754"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 00:26:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 10:10:40 GMT"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-li-uuid": "YP+9O9z6wRIwf5dQLCsAAA=="
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "11863"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=30574946"
+        },
+        {
+          "expires": "Wed, 23 Oct 2013 10:16:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 17:10:42 GMT"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "77011"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=30513689"
+        },
+        {
+          "expires": "Tue, 22 Oct 2013 17:15:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "352"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:05 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "sl=\"delete me\"; Version=1; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-li-uuid": "vZHDyRjuiQCkhZBzyxGqrg=="
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:04 GMT"
+        },
+        {
+          "age": "1"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "x-fs-uuid": "bd91c3c918ee8900a4859073cb11aaae"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"1672258277\""
+        },
+        {
+          "last-modified": "Fri, 27 Nov 2009 06:27:21 GMT"
+        },
+        {
+          "content-length": "6176"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 09:02:37 GMT"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 09:02:37 GMT"
+        },
+        {
+          "content-type": "application/ocsp-response"
+        },
+        {
+          "content-transfer-encoding": "binary"
+        },
+        {
+          "content-length": "1186"
+        },
+        {
+          "cache-control": "max-age=503312, public, no-transform, must-revalidate"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:05 GMT"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:06 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, no-cache, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 04 Aug 1978 12:00:00 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:06 GMT"
+        },
+        {
+          "server": "QS"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "age": "168237"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "age": "168237"
+        },
+        {
+          "server": "GFE/2.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:06 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "X-LI-IDC=C1"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 22 Aug 2012 09:55:42 GMT"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:08 GMT"
+        },
+        {
+          "x-fs-uuid": "4062fd4fc89b6346ee2b61950a9840a5"
+        },
+        {
+          "x-li-uuid": "QGL9T8ibY0buK2GVCphApQ=="
+        },
+        {
+          "age": "1"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "sl=\"delete me\"; Version=1; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-li-uuid": "rDlCGMNjprrsLUOMpHhJIw=="
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:09 GMT"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "x-fs-uuid": "bd91c3c918ee8900a4859073cb11aaae"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"1672258277\""
+        },
+        {
+          "last-modified": "Fri, 27 Nov 2009 06:27:21 GMT"
+        },
+        {
+          "content-length": "0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "352"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:09 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "X-LI-IDC=C1"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 22 Aug 2012 09:55:42 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:09 GMT"
+        },
+        {
+          "x-fs-uuid": "4062fd4fc89b6346ee2b61950a9840a5"
+        },
+        {
+          "x-li-uuid": "CDPTy/MVwxKQFKu9mysAAA=="
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Tue, 02 Oct 2012 17:09:44 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-li-uuid": "YP+9O9z6wRIwf5dQLCsAAA=="
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "64"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=28813847"
+        },
+        {
+          "expires": "Thu, 03 Oct 2013 01:04:56 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 19:11:14 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-li-uuid": "iA7sd9nTwhIQefs/LCsAAA=="
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=31463319"
+        },
+        {
+          "expires": "Sat, 02 Nov 2013 17:02:48 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:09 GMT"
+        },
+        {
+          "content-length": "603"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1156"
+        },
+        {
+          "last-modified": "Fri, 18 Apr 2008 18:03:54 GMT"
+        },
+        {
+          "etag": "1208541834000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=5196477"
+        },
+        {
+          "expires": "Wed, 02 Jan 2013 16:42:07 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "3476"
+        },
+        {
+          "last-modified": "Thu, 01 Sep 2011 13:46:08 GMT"
+        },
+        {
+          "etag": "1314884768000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=29243668"
+        },
+        {
+          "expires": "Tue, 08 Oct 2013 00:28:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1465"
+        },
+        {
+          "last-modified": "Wed, 15 Dec 2010 19:56:09 GMT"
+        },
+        {
+          "etag": "1292442969000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=5723024"
+        },
+        {
+          "expires": "Tue, 08 Jan 2013 18:57:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1705"
+        },
+        {
+          "last-modified": "Wed, 13 May 2009 06:47:06 GMT"
+        },
+        {
+          "etag": "1242197226000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=28717727"
+        },
+        {
+          "expires": "Tue, 01 Oct 2013 22:22:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1748"
+        },
+        {
+          "last-modified": "Mon, 13 Feb 2012 04:16:54 GMT"
+        },
+        {
+          "etag": "1329106614000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=29613032"
+        },
+        {
+          "expires": "Sat, 12 Oct 2013 07:04:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2115"
+        },
+        {
+          "last-modified": "Mon, 06 Jun 2011 11:28:28 GMT"
+        },
+        {
+          "etag": "1307359708000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=29255048"
+        },
+        {
+          "expires": "Tue, 08 Oct 2013 03:38:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2065"
+        },
+        {
+          "last-modified": "Fri, 15 Aug 2008 06:35:34 GMT"
+        },
+        {
+          "etag": "1218782134000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "cache-control": "max-age=23989474"
+        },
+        {
+          "expires": "Thu, 08 Aug 2013 04:58:44 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-cdn": "AKAM"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1777"
+        },
+        {
+          "last-modified": "Sun, 06 Apr 2008 16:44:40 GMT"
+        },
+        {
+          "etag": "1207500280000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=24015458"
+        },
+        {
+          "expires": "Thu, 08 Aug 2013 12:11:48 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2536"
+        },
+        {
+          "last-modified": "Sun, 17 Apr 2011 11:26:00 GMT"
+        },
+        {
+          "etag": "1303039560000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=30996570"
+        },
+        {
+          "expires": "Mon, 28 Oct 2013 07:23:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "1445"
+        },
+        {
+          "last-modified": "Sun, 14 Dec 2008 19:47:28 GMT"
+        },
+        {
+          "etag": "1229284048000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=31023826"
+        },
+        {
+          "expires": "Mon, 28 Oct 2013 14:57:56 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "11785"
+        },
+        {
+          "last-modified": "Tue, 21 Feb 2012 04:44:27 GMT"
+        },
+        {
+          "etag": "1329799467000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=29267475"
+        },
+        {
+          "expires": "Tue, 08 Oct 2013 07:05:25 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:11 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, no-cache, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 04 Aug 1978 12:00:00 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:11 GMT"
+        },
+        {
+          "server": "QS"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "age": "168242"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "age": "168242"
+        },
+        {
+          "server": "GFE/2.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:11 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "1013"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:44:36 GMT"
+        },
+        {
+          "etag": "1351921476485"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=31508935"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 05:43:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:11 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-li-uuid": "uBgHimv9whIwouPuKysAAA=="
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "X-LI-IDC=C1"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 09 Aug 2012 02:40:28 GMT"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:12 GMT"
+        },
+        {
+          "x-fs-uuid": "b73d9dcff4c8d40067468ef689e05a93"
+        },
+        {
+          "x-li-uuid": "tz2dz/TI1ABnRo72ieBakw=="
+        },
+        {
+          "age": "1"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "6507"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "sl=\"delete me\"; Version=1; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-li-uuid": "+8Oyp3i1cl6p243WV3LM6g=="
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:13 GMT"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "x-fs-uuid": "bd91c3c918ee8900a4859073cb11aaae"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"1672258277\""
+        },
+        {
+          "last-modified": "Fri, 27 Nov 2009 06:27:21 GMT"
+        },
+        {
+          "content-length": "0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "537"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:13 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "2469"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:13 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "last-modified": "Tue, 08 May 2012 20:09:07 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "321"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "81845"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Tue, 21 Feb 2012 01:03:49 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 20:03:29 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 20:03:29 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "24156"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "61845"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 18:10:35 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=31525781"
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 10:23:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:13 GMT"
+        },
+        {
+          "content-length": "94"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "etag": "\"5f7cc9080cad02333445367dae64546d:1351006466\""
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 15:34:26 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=3600"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:14 GMT"
+        },
+        {
+          "content-length": "1028"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Mon, 18 Jun 2012 13:12:57 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"eb63c14544dcd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/7.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "2955"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:14 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:14 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 16:25:49 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 16:25:49 GMT"
+        },
+        {
+          "etag": "46977404F0473696BBDC518B1845C60E809A3249"
+        },
+        {
+          "cache-control": "max-age=356494,public,no-transform,must-revalidate"
+        },
+        {
+          "x-ocsp-reponder-id": "t8edcaocsp6"
+        },
+        {
+          "content-length": "471"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/ocsp-response"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "etag": "\"65786c291a4603aa5150a1884452838d:1271351254\""
+        },
+        {
+          "last-modified": "Thu, 15 Apr 2010 17:07:29 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=2144448000"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:14 GMT"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "etag": "\"4cdd47b7bd15f75838435f1207ac1414:1351006993\""
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 15:43:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=315360000"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:14 GMT"
+        },
+        {
+          "content-length": "12232"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "CP=\"COM NAV INT STA NID OUR IND NOI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "set-cookie": "cckz=1mcwy3r; Domain=media6degrees.com; Expires=Thu, 02-May-2013 13:14:15 GMT; Path=/"
+        },
+        {
+          "location": "http://action.media6degrees.com/orbserv/nsjs?ncv=33&ns=299&pcv=39&nc=1&pixId=13086&cckz=true"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:14 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:15 GMT"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "X-LI-IDC=C1"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 17 Jul 2012 23:10:45 GMT"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:15 GMT"
+        },
+        {
+          "x-fs-uuid": "1034b0b6c77d1f91819a2d929764b582"
+        },
+        {
+          "x-li-uuid": "EDSwtsd9H5GBmi2Sl2S1gg=="
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "6507"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "sl=\"delete me\"; Version=1; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-li-uuid": "iPSByYTV24172TMFAcPcSg=="
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:15 GMT"
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "x-fs-uuid": "bd91c3c918ee8900a4859073cb11aaae"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"1672258277\""
+        },
+        {
+          "last-modified": "Fri, 27 Nov 2009 06:27:21 GMT"
+        },
+        {
+          "content-length": "0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "539"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "CP=\"COM NAV INT STA NID OUR IND NOI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "set-cookie": "cckz=1mcwy3s; Domain=media6degrees.com; Expires=Thu, 02-May-2013 13:14:16 GMT; Path=/"
+        },
+        {
+          "location": "http://action.media6degrees.com/orbserv/nsjs?ncv=33&ns=299&pcv=39&nc=1&pixId=13086&cckz=true"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "CP=\"COM NAV INT STA NID OUR IND NOI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "set-cookie": "JSESSIONID=CE33DFE7D94779C13B04C4D9B43D7792; Path=/orbserv; HttpOnly"
+        },
+        {
+          "content-type": "text/html;charset=ISO-8859-1"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "content-length": "5"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:15 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "841"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "u=8|0BAgYJ9UoGCfVKAAAAAAAAQEAAQIhdawAARg9fRc3AAAAAAT9PvgAAAAAAu9ZfgAAAAAO_VtUCBMBAAuBLQA; Version=1; Domain=.agkn.com; Max-Age=63072000; Expires=Mon, 03-Nov-2014 13:14:16 GMT; Path=/"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT\""
+        },
+        {
+          "expires": "Sat, 01 Jan 2000 00:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "image/gif;charset=ISO-8859-1"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:15 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "X-LI-IDC=C1"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 17 Jul 2012 23:10:45 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        },
+        {
+          "x-fs-uuid": "1034b0b6c77d1f91819a2d929764b582"
+        },
+        {
+          "x-li-uuid": "EDSwtsd9H5GBmi2Sl2S1gg=="
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "6507"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "2036"
+        },
+        {
+          "last-modified": "Thu, 29 Nov 2007 19:09:10 GMT"
+        },
+        {
+          "etag": "1196363350000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=31005797"
+        },
+        {
+          "expires": "Mon, 28 Oct 2013 09:57:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-li-uuid": "uBgHimv9whIwouPuKysAAA=="
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "access-control-allow-origin": "http://www.linkedin.com"
+        },
+        {
+          "last-modified": "Sat, 15 Sep 2012 06:22:35 GMT"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=27345738"
+        },
+        {
+          "expires": "Mon, 16 Sep 2013 01:16:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        },
+        {
+          "content-length": "146"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "last-modified": "Tue, 03 Jul 2012 21:15:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 03:30:07 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 03:30:07 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "19787"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "35049"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "304"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Mon, 18 Jun 2012 13:12:57 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"eb63c14544dcd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/7.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "2955"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "etag": "\"84332e7556647543d5f87647f37a8a6d:1346087966\""
+        },
+        {
+          "last-modified": "Mon, 27 Aug 2012 17:19:26 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=600"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        },
+        {
+          "content-length": "741"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, no-cache, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 04 Aug 1978 12:00:00 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        },
+        {
+          "server": "QS"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "age": "168247"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "age": "168247"
+        },
+        {
+          "server": "GFE/2.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "etag": "\"f13746374fa151b24abd8bf99a396878:1347294343\""
+        },
+        {
+          "last-modified": "Mon, 10 Sep 2012 16:25:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=2144448000"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        },
+        {
+          "content-length": "1028"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "etag": "\"4d5ead3cfaa1fd96263197170ccaed07:1347294382\""
+        },
+        {
+          "last-modified": "Mon, 10 Sep 2012 16:26:22 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1507"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "max-age=2144448000"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:16 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "X-LI-IDC=C1"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sun, 05 Aug 2012 15:35:43 GMT"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:18 GMT"
+        },
+        {
+          "x-fs-uuid": "e4dcbadff47540485aebb5d079a66252"
+        },
+        {
+          "x-li-uuid": "5Ny63/R1QEha67XQeaZiUg=="
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "6507"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "sl=\"delete me\"; Version=1; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-li-uuid": "qwi+h66wCYWzSNcKexV4yw=="
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:18 GMT"
+        },
+        {
+          "age": "1"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "x-fs-uuid": "bd91c3c918ee8900a4859073cb11aaae"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"1672258277\""
+        },
+        {
+          "last-modified": "Fri, 27 Nov 2009 06:27:21 GMT"
+        },
+        {
+          "content-length": "0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "538"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:19 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "CP=\"COM NAV INT STA NID OUR IND NOI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "set-cookie": "JSESSIONID=AA69DF8838B33636B86F3AD5917D28DD; Path=/orbserv; HttpOnly"
+        },
+        {
+          "content-type": "text/html;charset=ISO-8859-1"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "content-length": "5"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:19 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "859"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:19 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "X-LI-IDC=C1"
+        },
+        {
+          "p3p": "CP=\"CAO DSP COR CUR ADMi DEVi TAIi PSAi PSDi IVAi IVDi CONi OUR DELi SAMi UNRi PUBi OTRi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT POL PRE\""
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sun, 05 Aug 2012 15:35:43 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:19 GMT"
+        },
+        {
+          "x-fs-uuid": "e4dcbadff47540485aebb5d079a66252"
+        },
+        {
+          "x-li-uuid": "aGvE/vUVwxKwMlIRJCsAAA=="
+        },
+        {
+          "age": "0"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-length": "6507"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "u=8|0BAgYJ9UoGCfVKwAAAAABAQEAAQQhdawAARg9fRc3AAAAAAT9PvgAAAAAAu5WuAAAAAAO_VtUCBMBAAuBLQA; Version=1; Domain=.agkn.com; Max-Age=63072000; Expires=Mon, 03-Nov-2014 13:14:19 GMT; Path=/"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT\""
+        },
+        {
+          "expires": "Sat, 01 Jan 2000 00:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "image/gif;charset=ISO-8859-1"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:18 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-length": "3183"
+        },
+        {
+          "last-modified": "Tue, 04 Mar 2008 16:53:17 GMT"
+        },
+        {
+          "etag": "1204649597000"
+        },
+        {
+          "server": "Jetty(6.1.26)"
+        },
+        {
+          "x-cdn": "AKAM"
+        },
+        {
+          "cache-control": "max-age=31199347"
+        },
+        {
+          "expires": "Wed, 30 Oct 2013 15:43:26 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:19 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-li-uuid": "uBgHimv9whIwouPuKysAAA=="
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "304"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Mon, 18 Jun 2012 13:12:57 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"eb63c14544dcd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/7.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "2955"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:19 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:19 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, no-cache, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 04 Aug 1978 12:00:00 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:19 GMT"
+        },
+        {
+          "server": "QS"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "age": "168250"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Thu, 01 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Wed, 19 Apr 2000 11:43:00 GMT"
+        },
+        {
+          "last-modified": "Wed, 21 Jan 2004 19:51:30 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, proxy-revalidate"
+        },
+        {
+          "age": "168250"
+        },
+        {
+          "server": "GFE/2.0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:14:19 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_29.json b/jetty-http2/http2-hpack/src/test/resources/data/story_29.json
new file mode 100644
index 0000000..01bdbbf
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_29.json
@@ -0,0 +1,13780 @@
+{
+  "context": "response",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":status": "301"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "location": "http://www.msn.com/"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:27 GMT"
+        },
+        {
+          "content-length": "142"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "p3p": "CP=\"NON UNI COM NAV STA LOC CURa DEVa PSAa PSDa OUR IND\""
+        },
+        {
+          "set-cookie": "SRCHUSR=AUTOREDIR=0&GEOVAR=&DOB=20121103; expires=Mon, 03-Nov-2014 13:29:29 GMT; domain=.msn.com; path=/"
+        },
+        {
+          "errorcodecount": "[0:0]"
+        },
+        {
+          "s": "CO3SCH010020101"
+        },
+        {
+          "edge-control": "no-store"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:28 GMT"
+        },
+        {
+          "content-length": "41648"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=43200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"808cfaf3c1ac81:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "age": "7374"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2007 15:02:13 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 23:26:35 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=86400,public"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"078def13c1fcd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "age": "33322"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Fri, 20 Apr 2012 21:31:28 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:14:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"083df89b6bac81:0\""
+        },
+        {
+          "server": "BLUMPPSTCA08"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "1162"
+        },
+        {
+          "age": "9388284"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Tue, 20 May 2008 20:17:34 GMT"
+        },
+        {
+          "expires": "Wed, 17 Jul 2013 21:38:05 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=86400,public"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"078def13c1fcd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "age": "33322"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Fri, 20 Apr 2012 21:31:28 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:14:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"096b38d19cc1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "417"
+        },
+        {
+          "age": "11654605"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Tue, 03 May 2011 20:32:28 GMT"
+        },
+        {
+          "expires": "Fri, 21 Jun 2013 16:06:04 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"99789f9faea8cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "2712"
+        },
+        {
+          "age": "378940"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 19:20:21 GMT"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 04:13:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"d6db97976eb9cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "3262"
+        },
+        {
+          "age": "37907"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 02:54:50 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 02:57:42 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"6c2a9d5170b1cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "4648"
+        },
+        {
+          "age": "23683"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 22:47:02 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 06:54:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"0922651f38cb1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "4286"
+        },
+        {
+          "age": "9388299"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Tue, 10 Aug 2010 00:03:00 GMT"
+        },
+        {
+          "expires": "Wed, 17 Jul 2013 21:37:50 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"f9f8904b5ab9cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "4352"
+        },
+        {
+          "age": "46151"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 00:29:32 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 00:40:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"8437263c89b8cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "6131"
+        },
+        {
+          "age": "23313"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 23:33:02 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 07:00:56 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"54a642c148b9cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "5522"
+        },
+        {
+          "age": "23304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 22:23:59 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 07:01:05 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"f5d7f6f68b8cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "4048"
+        },
+        {
+          "age": "23326"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 19:38:15 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 07:00:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"789825b3b68cc1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "7075"
+        },
+        {
+          "age": "304691"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Aug 2011 18:27:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 00:51:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"6a4618d83cb9cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "11544"
+        },
+        {
+          "age": "59087"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 20:58:43 GMT"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 21:04:42 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"188d971c36b9cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "3753"
+        },
+        {
+          "age": "23326"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 20:10:32 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 07:00:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80a9243afa8cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "2135"
+        },
+        {
+          "age": "64967"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 19:24:57 GMT"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 19:26:42 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"da259a60afb9cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "6786"
+        },
+        {
+          "age": "9961"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 10:38:35 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 10:43:28 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"3a2bfd7658b9cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "6859"
+        },
+        {
+          "age": "47464"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 00:16:26 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 00:18:25 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"feefae84ab9cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "7066"
+        },
+        {
+          "age": "23304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 22:39:23 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 07:01:05 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"4b1b97dcc0b9cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "14928"
+        },
+        {
+          "age": "2676"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 12:43:44 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 12:44:53 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80f314cf17b7cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "23864"
+        },
+        {
+          "age": "290866"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 03:28:35 GMT"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 04:41:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"9c71d229a3b9cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "7497"
+        },
+        {
+          "age": "14681"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 09:11:09 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 09:24:48 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=300"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=43200"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"91588811bb8bcd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "2420"
+        },
+        {
+          "age": "41017"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Wed, 05 Sep 2012 23:06:23 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 14:05:53 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"0e49dbec5aecb1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "4082"
+        },
+        {
+          "age": "8615761"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Fri, 07 Jan 2011 23:51:04 GMT"
+        },
+        {
+          "expires": "Fri, 26 Jul 2013 20:13:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"ac1668bfc52ca1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "657"
+        },
+        {
+          "age": "159348"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Thu, 22 Oct 2009 09:46:35 GMT"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 17:13:42 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"803ab9aa463acd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "24630"
+        },
+        {
+          "age": "9388297"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Fri, 25 May 2012 07:19:05 GMT"
+        },
+        {
+          "expires": "Wed, 17 Jul 2013 21:37:53 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"02bfb6a29b7cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "65670"
+        },
+        {
+          "age": "285810"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 05:34:38 GMT"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 06:06:00 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80d88a9463acd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "74"
+        },
+        {
+          "age": "8077925"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Fri, 25 May 2012 07:19:03 GMT"
+        },
+        {
+          "expires": "Fri, 02 Aug 2013 01:37:25 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"8097d798463acd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "48"
+        },
+        {
+          "age": "9389765"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Fri, 25 May 2012 07:18:35 GMT"
+        },
+        {
+          "expires": "Wed, 17 Jul 2013 21:13:25 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"5bc3dfd117b7cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "6234"
+        },
+        {
+          "age": "290867"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 03:28:39 GMT"
+        },
+        {
+          "expires": "Thu, 31 Oct 2013 04:41:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"801e6b9c463acd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "1117"
+        },
+        {
+          "age": "8614139"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Fri, 25 May 2012 07:18:41 GMT"
+        },
+        {
+          "expires": "Fri, 26 Jul 2013 20:40:31 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"077efa8463acd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "age": "11574965"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Fri, 25 May 2012 07:19:02 GMT"
+        },
+        {
+          "expires": "Sat, 22 Jun 2013 14:13:25 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"8097d798463acd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "1142"
+        },
+        {
+          "age": "9388300"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Fri, 25 May 2012 07:18:35 GMT"
+        },
+        {
+          "expires": "Wed, 17 Jul 2013 21:37:50 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"8086f4a5463acd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "172"
+        },
+        {
+          "age": "9388300"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Fri, 25 May 2012 07:18:57 GMT"
+        },
+        {
+          "expires": "Wed, 17 Jul 2013 21:37:50 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"8016f7a74e67cc1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "421"
+        },
+        {
+          "age": "9389765"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Tue, 30 Aug 2011 19:54:41 GMT"
+        },
+        {
+          "expires": "Wed, 17 Jul 2013 21:13:25 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"9a7af237eb5cd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "5436"
+        },
+        {
+          "age": "462287"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 02:35:10 GMT"
+        },
+        {
+          "expires": "Tue, 29 Oct 2013 05:04:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "content-length": "1640"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "3989"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10263730-T100595690-C40000000000114208"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "p3p": "CP=\"NON UNI COM NAV STA LOC CURa DEVa PSAa PSDa OUR IND\""
+        },
+        {
+          "set-cookie": "SRCHD=MS=2546729&D=2546729&AF=NOFORM; expires=Mon, 03-Nov-2014 13:29:30 GMT; domain=.msn.com; path=/"
+        },
+        {
+          "errorcodecount": "[0:0]"
+        },
+        {
+          "s": "CO3SCH010133009"
+        },
+        {
+          "edge-control": "no-store"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "content-length": "2197"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "location": "http://c.atdmt.com/c.gif?udc=true&di=340&pi=7317&ps=95101&lng=en-us&tp=http%3A%2F%2Fwww.msn.com%2Fdefaultwpe3w.aspx&rid=e32241cc231e4226b91543c154dd3b7e&rnd=1351949370023&rf=&scr=1366x768&RedC=c.msn.com&MXFR=3D632B5B5356602B36252F56575660EB"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "MUID=3D632B5B5356602B36252F56575660EB&TUID=1; domain=.msn.com; expires=Mon, 03-Nov-2014 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "content-length": "0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-location": "http://spe.atdmt.com/images/pixel.gif"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:29 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/3642305/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "GFE/2.0"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001001"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=1800"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"0287ded7fb9cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/7.0"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "1811"
+        },
+        {
+          "age": "1765"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 04:58:56 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:30:04 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=1800"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80722a7c098cc1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/7.0"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "9346"
+        },
+        {
+          "age": "1170"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Tue, 01 Nov 2011 18:04:09 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:40:00 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=1800"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"0af38a5c098cc1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/7.0"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "20808"
+        },
+        {
+          "age": "1411"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Tue, 01 Nov 2011 18:04:06 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:35:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=1800"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"8091e4ec7fb9cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/7.0"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "1142"
+        },
+        {
+          "age": "1226"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 04:58:55 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:39:04 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "location": "http://c.msn.com/c.gif?udc=true&di=340&pi=7317&ps=95101&lng=en-us&tp=http%3A%2F%2Fwww.msn.com%2Fdefaultwpe3w.aspx&rid=e32241cc231e4226b91543c154dd3b7e&rnd=1351949370023&rf=&scr=1366x768&MUID=39C1843BD7CB679E06238036D4CB670B&cb=1cdb9c7414258b0"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "SRM_M=39C1843BD7CB679E06238036D4CB670B; domain=c.atdmt.com; expires=Mon, 03-Nov-2014 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "content-length": "0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 10:27:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"04baaef128fcc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "ANONCHK=0; domain=c.msn.com; expires=Tue, 06-Nov-2012 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=1800"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"299a82bdeb9cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/7.0"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-length": "20046"
+        },
+        {
+          "age": "1074"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 15:28:42 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:36 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Fri, 17 Aug 2012 14:27:55 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 19:34:58 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 19:34:58 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "64473"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 07:00:23 GMT"
+        },
+        {
+          "server": "Jetty(6.1.22)"
+        },
+        {
+          "p3p": "policyref=\"/w3c/policy.xml\", CP=\"NOI DSP COR CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT\""
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "x-powered-by": "Mirror Image Internet"
+        },
+        {
+          "via": "1.1 bfi061004 (MII-APC/2.2)"
+        },
+        {
+          "x-mii-cache-hit": "1"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "keep-alive": "timeout=2"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"0e2349e463acd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "93"
+        },
+        {
+          "age": "8077926"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:31 GMT"
+        },
+        {
+          "last-modified": "Fri, 25 May 2012 07:18:44 GMT"
+        },
+        {
+          "expires": "Fri, 02 Aug 2013 01:37:25 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "set-cookie": "pudm_AAAA=MLuxc4uHAF5HEldAttN+mTMH5l3UFcGfjYAvMSjMMwDWP3TDUWl1; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:29:31 GMT; Path=/"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "x-proc-data": "pd3-bgas02-0"
+        },
+        {
+          "content-type": "application/javascript;charset=ISO-8859-1"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "p3p": "CP=\"NON UNI COM NAV STA LOC CURa DEVa PSAa PSDa OUR IND\""
+        },
+        {
+          "set-cookie": "SRCHD=SM=1&MS=2546729&D=2546729&AF=NOFORM; expires=Mon, 03-Nov-2014 13:29:31 GMT; domain=.msn.com; path=/"
+        },
+        {
+          "errorcodecount": "[0:0]"
+        },
+        {
+          "s": "CO3SCH010120128"
+        },
+        {
+          "edge-control": "no-store"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:30 GMT"
+        },
+        {
+          "content-length": "1672"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"0a420aa463acd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "387"
+        },
+        {
+          "age": "11654712"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:31 GMT"
+        },
+        {
+          "last-modified": "Fri, 25 May 2012 07:19:04 GMT"
+        },
+        {
+          "expires": "Fri, 21 Jun 2013 16:04:19 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"05ba19a463acd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "4842"
+        },
+        {
+          "age": "11654572"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "last-modified": "Fri, 25 May 2012 07:18:38 GMT"
+        },
+        {
+          "expires": "Fri, 21 Jun 2013 16:06:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80b325a7463acd1:0\""
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "1105"
+        },
+        {
+          "age": "8614141"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "last-modified": "Fri, 25 May 2012 07:18:59 GMT"
+        },
+        {
+          "expires": "Fri, 26 Jul 2013 20:40:31 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "content-length": "562"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "884"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10669318-T100595843-C108000000000115722"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "3114"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-radid": "P10603404-T100595756-C48000000000113484"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:31 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1323"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:31 GMT"
+        },
+        {
+          "content-length": "1336"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "3147"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10720545-T100595939-C52000000000120598"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "406"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-radid": "P3782944-T100582739-C521263"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:31 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "325"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"0bd514f14ac31:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "content-length": "85"
+        },
+        {
+          "age": "12894277"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "last-modified": "Tue, 15 Jul 2003 16:49:38 GMT"
+        },
+        {
+          "expires": "Fri, 07 Jun 2013 07:44:55 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, max-age=31535999"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "2710"
+        },
+        {
+          "age": "3659370"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "expires": "Sun, 22 Sep 2013 05:00:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, max-age=31508189"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "9220"
+        },
+        {
+          "age": "3659358"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "expires": "Sat, 21 Sep 2013 21:16:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, max-age=31338077"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "3607"
+        },
+        {
+          "age": "3400121"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "expires": "Sun, 22 Sep 2013 22:02:08 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 09:35:33 GMT"
+        },
+        {
+          "etag": "\"1411999884f419ea8219bf9b531a9c66\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "application/javascript; charset=utf-8"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3260"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "CP=\"CAO DSP LAW CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa OUR BUS IND UNI COM NAV INT\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/json; charset=utf-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "etag": "\"377d257f2d2e294916143c069141c1c5:1328738114\""
+        },
+        {
+          "last-modified": "Wed, 08 Feb 2012 21:55:14 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "CP=\"CAO DSP LAW CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa OUR BUS IND UNI COM NAV INT\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:10:52 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "status": "200 OK"
+        },
+        {
+          "content-type": "application/javascript;charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "889"
+        },
+        {
+          "server": "tfe"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate, post-check=0, pre-check=0"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "x-transaction": "49df427f743e57d8"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "expires": "Tue, 31 Mar 1981 05:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0"
+        },
+        {
+          "set-cookie": "guest_id=v1%3A135194937257731566; Expires=Mon, 3-Nov-2014 13:29:32 GMT; Path=/; Domain=.twitter.com"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "65"
+        },
+        {
+          "server": "tfe"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 09:48:13 GMT"
+        },
+        {
+          "etag": "\"b036a791811effc968bfcb43fa6d6910\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "7242"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:32 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "p3p": "CP=\"CAO DSP LAW CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa OUR BUS IND UNI COM NAV INT\""
+        },
+        {
+          "cache-control": "public, max-age=1800"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:34 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 16:28:26 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 16:28:26 GMT"
+        },
+        {
+          "etag": "412224A3234E88A2760468333271010BB1C6D1AA"
+        },
+        {
+          "cache-control": "max-age=355731,public,no-transform,must-revalidate"
+        },
+        {
+          "x-ocsp-reponder-id": "t8edcaocsp4"
+        },
+        {
+          "content-length": "471"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/ocsp-response"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/ocsp-response"
+        },
+        {
+          "content-transfer-encoding": "Binary"
+        },
+        {
+          "content-length": "1938"
+        },
+        {
+          "connection": "Close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:34 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 16:28:26 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 16:28:26 GMT"
+        },
+        {
+          "etag": "412224A3234E88A2760468333271010BB1C6D1AA"
+        },
+        {
+          "cache-control": "max-age=355731,public,no-transform,must-revalidate"
+        },
+        {
+          "x-ocsp-reponder-id": "t8edcaocsp4"
+        },
+        {
+          "content-length": "471"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "application/ocsp-response"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-aspnetmvc-version": "4.0"
+        },
+        {
+          "x-ua-compatible": "IE=Edge;chrome=1"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "content-length": "28018"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 06:48:54 GMT"
+        },
+        {
+          "server": "Jetty(6.1.22)"
+        },
+        {
+          "p3p": "policyref=\"/w3c/policy.xml\", CP=\"NOI DSP COR CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT\""
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "x-powered-by": "Mirror Image Internet"
+        },
+        {
+          "via": "1.1 bfi061001 (MII-APC/2.2)"
+        },
+        {
+          "x-mii-cache-hit": "1"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "keep-alive": "timeout=2"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"80ebcf5152b0cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "6717"
+        },
+        {
+          "age": "1026619"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 12:39:47 GMT"
+        },
+        {
+          "expires": "Tue, 22 Oct 2013 16:19:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"0195ab552b0cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "8659"
+        },
+        {
+          "age": "1026617"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 12:42:34 GMT"
+        },
+        {
+          "expires": "Tue, 22 Oct 2013 16:19:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 10:27:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"04baaef128fcc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "ANONCHK=0; domain=c.msn.com; expires=Tue, 06-Nov-2012 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"707a74ac52b0cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "9389"
+        },
+        {
+          "age": "1026617"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 12:42:19 GMT"
+        },
+        {
+          "expires": "Tue, 22 Oct 2013 16:19:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"f0edab8b52b0cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "3749"
+        },
+        {
+          "age": "1026645"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 12:41:24 GMT"
+        },
+        {
+          "expires": "Tue, 22 Oct 2013 16:18:50 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-aspnetmvc-version": "4.0"
+        },
+        {
+          "x-ua-compatible": "IE=Edge;chrome=1"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "content-length": "4286"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 23:16:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"951751d2bdb7cd1:0\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001001"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431991"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "773"
+        },
+        {
+          "age": "416777"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 17:43:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431989"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "4747"
+        },
+        {
+          "age": "69970"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 18:03:14 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001004"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=423916"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "45125"
+        },
+        {
+          "age": "4791"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 09:55:00 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public,max-age=31536000"
+        },
+        {
+          "content-length": "42060"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"1ac2aef461a9cc1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "p3p": "CP=\"ALL IND DSP COR ADM CONo CUR CUSo IVAo IVDo PSA PSD TAI TELo OUR SAMo CNT COM INT NAV ONL PHY PRE PUR UNI\""
+        },
+        {
+          "vtag": "279606632500000000"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "age": "3987586"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:36 GMT"
+        },
+        {
+          "last-modified": "Tue, 22 Nov 2011 21:59:06 GMT"
+        },
+        {
+          "expires": "Wed, 18 Sep 2013 09:49:50 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:36 GMT"
+        },
+        {
+          "content-length": "954"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "2008"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10723443-T100550693-C29000000000082620"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "anj=Kfu=8fG7]PCxrx)0s]#%2L_'x%SEV/hnJip4FQV_eKj?9kb10I3SSI79ox)!lG@t]; path=/; expires=Fri, 01-Feb-2013 13:29:36 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:36 GMT"
+        },
+        {
+          "content-length": "615"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:35 GMT"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "381"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "allow": "GET"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 04:51:56 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:36 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "anj=Kfu=8fG7]PCxrx)0s]#%2L_'x%SEV/hnJip4FQV_eKj?9kb10I3SSI79ox)!lG@t]; path=/; expires=Fri, 01-Feb-2013 13:29:36 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:36 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-msadid": "300089389.300024620"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:36 GMT"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "2290"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "40789"
+        },
+        {
+          "allow": "GET"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 21:57:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "20802"
+        },
+        {
+          "allow": "GET"
+        },
+        {
+          "expires": "Mon, 05 Nov 2012 06:30:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431968"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "4319"
+        },
+        {
+          "age": "1707"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 13:00:38 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431760"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "5321"
+        },
+        {
+          "age": "14923"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 09:16:54 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431996"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "4605"
+        },
+        {
+          "age": "46358"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 00:36:55 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431925"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "4097"
+        },
+        {
+          "age": "64344"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:35:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=432000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "4692"
+        },
+        {
+          "age": "32851"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 04:22:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431754"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "2555"
+        },
+        {
+          "age": "14924"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 09:16:47 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431758"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "4026"
+        },
+        {
+          "age": "54890"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 22:10:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431997"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "4589"
+        },
+        {
+          "age": "403007"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 21:32:47 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431990"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "3476"
+        },
+        {
+          "age": "82129"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:40:38 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431839"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "5927"
+        },
+        {
+          "age": "53455"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 22:36:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431993"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "4900"
+        },
+        {
+          "age": "83837"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:12:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431997"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "4420"
+        },
+        {
+          "age": "88061"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 13:01:53 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/octet-stream"
+        },
+        {
+          "content-length": "2209"
+        },
+        {
+          "allow": "GET"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 19:27:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431982"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "5496"
+        },
+        {
+          "age": "63170"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:56:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431991"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "3450"
+        },
+        {
+          "age": "89554"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 12:36:54 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431990"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "2271"
+        },
+        {
+          "age": "92961"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 11:40:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431991"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "4009"
+        },
+        {
+          "age": "86314"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 13:30:54 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431989"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "3779"
+        },
+        {
+          "age": "86708"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 13:24:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431976"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "3115"
+        },
+        {
+          "age": "69186"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 18:16:07 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431990"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "4867"
+        },
+        {
+          "age": "80624"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 15:05:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431993"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "5187"
+        },
+        {
+          "age": "140253"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 22:31:57 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431966"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "5244"
+        },
+        {
+          "age": "140817"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 22:22:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431931"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "3807"
+        },
+        {
+          "age": "303973"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Mon, 05 Nov 2012 01:02:15 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431977"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "4135"
+        },
+        {
+          "age": "143835"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 21:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431943"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "5096"
+        },
+        {
+          "age": "64149"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:39:31 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431988"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "3801"
+        },
+        {
+          "age": "125275"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 02:41:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431990"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "4499"
+        },
+        {
+          "age": "144182"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 21:26:25 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/json; charset=utf-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:37 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-aspnetmvc-version": "4.0"
+        },
+        {
+          "x-ua-compatible": "IE=Edge;chrome=1"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:42 GMT"
+        },
+        {
+          "content-length": "23449"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"0feb9575b2cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "5942"
+        },
+        {
+          "age": "717045"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:42 GMT"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 16:33:48 GMT"
+        },
+        {
+          "expires": "Sat, 26 Oct 2013 06:18:57 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"0f9f3446b2cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "8528"
+        },
+        {
+          "age": "717044"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:42 GMT"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 16:40:26 GMT"
+        },
+        {
+          "expires": "Sat, 26 Oct 2013 06:18:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"968a7a9552b0cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "8149"
+        },
+        {
+          "age": "1026651"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:42 GMT"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 12:41:40 GMT"
+        },
+        {
+          "expires": "Tue, 22 Oct 2013 16:18:51 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 10:27:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"04baaef128fcc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "ANONCHK=0; domain=c.msn.com; expires=Tue, 06-Nov-2012 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:42 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-aspnetmvc-version": "4.0"
+        },
+        {
+          "x-ua-compatible": "IE=Edge;chrome=1"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:42 GMT"
+        },
+        {
+          "content-length": "4286"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 23:16:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"951751d2bdb7cd1:0\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001004"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:42 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:42 GMT"
+        },
+        {
+          "content-length": "954"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "2008"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10723443-T100550693-C29000000000082620"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001001"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:42 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, max-age=24"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "machine": "SN1********532"
+        },
+        {
+          "rendertime": "11/2/2012 8:14:13 AM"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "34971"
+        },
+        {
+          "age": "39"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:42 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 15:14:13 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:44:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "anj=Kfu=8fG3x=Cxrx)0s]#%2L_'x%SEV/hnKu94FQV_eKj?9kb10I3SSI7:0wHz@)G?)i4ZhK; path=/; expires=Fri, 01-Feb-2013 13:29:43 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "content-length": "514"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, max-age=7775467"
+        },
+        {
+          "content-type": "text/css; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "machine": "SN1********532"
+        },
+        {
+          "rendertime": "11/2/2012 8:14:13 AM"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "27220"
+        },
+        {
+          "age": "226499"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 22:25:52 GMT"
+        },
+        {
+          "expires": "Tue, 29 Jan 2013 22:25:51 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public"
+        },
+        {
+          "content-length": "1465"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "age": "77450"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "last-modified": "Thu, 06 Sep 2012 03:42:16 GMT"
+        },
+        {
+          "expires": "Fri, 16 Nov 2012 15:58:54 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-msadid": "300089440.299934848"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "514"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "3936"
+        },
+        {
+          "age": "1664"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 11:56:42 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 14:35:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "4646"
+        },
+        {
+          "age": "1653"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 02:42:24 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 17:14:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "14780"
+        },
+        {
+          "allow": "GET"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 04:45:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "41165"
+        },
+        {
+          "age": "1664"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 02:52:34 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 14:31:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/json; charset=utf-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "location": "http://m.adnxs.com/msftcookiehandler?t=1&c=MUID%3d39C1843BD7CB679E06238036D4CB670B"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "content-length": "13"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "sess=1; path=/; expires=Sun, 04-Nov-2012 13:29:43 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "content-length": "43"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "7206"
+        },
+        {
+          "age": "1871"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 19:21:20 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 14:46:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "2833"
+        },
+        {
+          "age": "4319"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 01:22:37 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:57:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "4141"
+        },
+        {
+          "age": "1871"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 09:14:16 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 14:47:24 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, max-age=600"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:39:43 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "machine": "SN1********529"
+        },
+        {
+          "rendertime": "11/3/2012 6:29:43 AM"
+        },
+        {
+          "x-rendertime": "0.017 secs"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "content-length": "6790"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "7264"
+        },
+        {
+          "age": "4321"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 02:45:33 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:47:42 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, max-age=7776000"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "machine": "SN1********532"
+        },
+        {
+          "rendertime": "11/2/2012 8:14:13 AM"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "3083"
+        },
+        {
+          "age": "227101"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:44 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 22:24:43 GMT"
+        },
+        {
+          "expires": "Tue, 29 Jan 2013 22:24:43 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, max-age=7775989"
+        },
+        {
+          "content-type": "text/css; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "machine": "SN1********532"
+        },
+        {
+          "rendertime": "11/2/2012 8:14:13 AM"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "631"
+        },
+        {
+          "age": "227144"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:44 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 22:23:50 GMT"
+        },
+        {
+          "expires": "Tue, 29 Jan 2013 22:23:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "5366"
+        },
+        {
+          "age": "2169"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:44 GMT"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 23:46:05 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 14:23:35 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, max-age=7775981"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "5125"
+        },
+        {
+          "age": "227144"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:44 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 22:23:41 GMT"
+        },
+        {
+          "expires": "Tue, 29 Jan 2013 22:23:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "38544"
+        },
+        {
+          "age": "1664"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:44 GMT"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 02:46:18 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 14:32:00 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, max-age=7775962"
+        },
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "68447"
+        },
+        {
+          "age": "227086"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:44 GMT"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 22:24:21 GMT"
+        },
+        {
+          "expires": "Tue, 29 Jan 2013 22:24:20 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "machine": "SN1********525"
+        },
+        {
+          "rendertime": "11/3/2012 6:29:44 AM"
+        },
+        {
+          "x-rendertime": "0.003 secs"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:43 GMT"
+        },
+        {
+          "content-length": "1238"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "machine": "SN1********509"
+        },
+        {
+          "rendertime": "11/3/2012 6:29:44 AM"
+        },
+        {
+          "set-cookie": "zip=c:cz; domain=msn.com; expires=Sat, 10-Nov-2012 14:29:44 GMT; path=/"
+        },
+        {
+          "x-rendertime": "0.067 secs"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:44 GMT"
+        },
+        {
+          "content-length": "138"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-aspnetmvc-version": "4.0"
+        },
+        {
+          "x-ua-compatible": "IE=Edge;chrome=1"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:46 GMT"
+        },
+        {
+          "content-length": "25630"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 23:16:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"951751d2bdb7cd1:0\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 10:27:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"04baaef128fcc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "ANONCHK=0; domain=c.msn.com; expires=Tue, 06-Nov-2012 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:47 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001001"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:47 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=422586"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "818"
+        },
+        {
+          "age": "321488"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:47 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 17:34:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:47 GMT"
+        },
+        {
+          "content-length": "954"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "1996"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10723443-T100550693-C29000000000082620"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001005"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:47 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "anj=Kfu=8fG68%Cxrx)0s]#%2L_'x%SEV/hnJPh4FQV_eKj?9AMF4:V)4hY/82QjU'-Rw1Ra^uI$+VZ; path=/; expires=Fri, 01-Feb-2013 13:29:47 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:47 GMT"
+        },
+        {
+          "content-length": "1463"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "ntcoent-length": "42"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "6:29:42 AM"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "trackingid": "3bd68bdc-1e91-410e-bd92-a85913c08914"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:47 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "54"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:47 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/json; charset=utf-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:47 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "must-revalidate"
+        },
+        {
+          "expires": "Mon, 05 Nov 2012 13:29:48 GMT"
+        },
+        {
+          "set-cookie": "fc=rqbE3Poup4Ofv8GxDEGHJ0T2mPet6qErhzJbX2aX0FVY8uK-xitYHevMNKV5qRPTQHHOFrDBExLyIrZ-jLRD_r3wc-cQ7FRKnITKYzO3zYV52dhK4dSErN9-EcLOAtq0; Domain=.turn.com; Expires=Thu, 02-May-2013 13:29:48 GMT; Path=/"
+        },
+        {
+          "content-type": "text/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:47 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "private, no-cache, no-store, must-revalidate"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:48 GMT"
+        },
+        {
+          "content-length": "4142"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "cache-control": "private, max-age=0, no-cache, no-store, must-revalidate, proxy-revalidate"
+        },
+        {
+          "expires": "Sat, 1 Jan 2000 01:01:00 GMT"
+        },
+        {
+          "last-modified": "Sat, 1 Jan 2000 01:01:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "AdifyServer"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "45"
+        },
+        {
+          "set-cookie": "s=1,2*50951c4c*3Q4liHxMwG*rZye1ewDYpnkdvSJkze6tOMY_w==*; path=/; expires=Mon, 03-Nov-2014 13:29:48 GMT; domain=afy11.net;"
+        },
+        {
+          "p3p": "policyref=\"http://ad.afy11.net/privacy.xml\", CP=\" NOI DSP NID ADMa DEVa PSAa PSDa OUR OTRa IND COM NAV STA OTC\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "sess=1; path=/; expires=Sun, 04-Nov-2012 13:29:49 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "location": "http://r.turn.com/r/bd?ddc=1&pid=54&cver=1&uid=3755642153863499992"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:49 GMT"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "content-type": "text/html; charset=ISO-8859-1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "GlassFish v3"
+        },
+        {
+          "p3p": "policyref=\"/bh/w3c/p3p.xml\", CP=\"NOI DSP COR NID CURa DEVa PSAa OUR BUS COM NAV INT\""
+        },
+        {
+          "set-cookie": "pb_rtb_ev=2-535461.3194305635051091579.0.0.1351949514647; Domain=.contextweb.com; Expires=Sun, 03-Nov-2013 13:31:54 GMT; Path=/"
+        },
+        {
+          "cw-server": "lga-app602"
+        },
+        {
+          "cache-control": "private, max-age=0, no-cache, no-store"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "content-type": "image/gif;charset=ISO-8859-1"
+        },
+        {
+          "content-language": "en-US"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:31:54 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "cache-control": "post-check=0, pre-check=0"
+        },
+        {
+          "content-location": "partner.html"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:49 GMT"
+        },
+        {
+          "expires": "Mon, 26 Jul 1997 05:00:00 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:29:49 GMT"
+        },
+        {
+          "location": "http://cdn.spotxchange.com/media/thumbs/pixel/pixel.gif"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR IND UNI COM NAV ADMa\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "Apache/2.2.21 (Unix) mod_ssl/2.2.21 OpenSSL/0.9.8e-fips-rhel5"
+        },
+        {
+          "set-cookie": "partner-0=eNptzM0KgkAUQOF1vUvgzzCF0MJwkpGuF3WyGXcpBKOZLYLJ%2B%2FRJtGx7OHyc7fxVdOBsU4lSxifZiCQyaiJ8vAh7EaLNnNGZ1471rMOaGp3dmvTomUpuO5ocWmkxBA4q5nKsWZfeZ6PLZxswi8GwdAigh3dOwFB9Tf%2Bfeb0UP2fgcvlRFQTJOQA6u1wJh0r4OQ3L4%2B3XH6c7OqM%3D; expires=Sun, 03-Mar-2013 13:29:49 GMT; path=/; domain=.spotxchange.com"
+        },
+        {
+          "tcn": "choice"
+        },
+        {
+          "vary": "negotiate"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "location": "http://r.turn.com/r/cms/id/0/ddc/1/pid/18/uid/?google_gid=CAESEITR3tLElIgxNs25jzV8Md0&google_cver=1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:49 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "server": "Cookie Matcher"
+        },
+        {
+          "content-length": "300"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:49 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "cache-control": "public, max-age=30, proxy-revalidate"
+        },
+        {
+          "expires": "Mon, 26 Jul 1997 05:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "p3p": "CP=\"CUR ADM OUR NOR STA NID\""
+        },
+        {
+          "set-cookie": "i=cbdc52da-b3d4-4f79-bc70-3121d006fdfa; expires=Mon, 03-Nov-2014 13:29:49 GMT; path=/; domain=.openx.net"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:49 GMT"
+        },
+        {
+          "server": "Apache/2.2.3 (CentOS)"
+        },
+        {
+          "x-powered-by": "PHP/5.3.3"
+        },
+        {
+          "p3p": "CP=\"NOI CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT\""
+        },
+        {
+          "set-cookie": "put_1185=3194305635051091579; expires=Wed, 02-Jan-2013 13:29:49 GMT; path=/; domain=.rubiconproject.com"
+        },
+        {
+          "content-length": "49"
+        },
+        {
+          "keep-alive": "timeout=30, max=9907"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:49 GMT"
+        },
+        {
+          "server": "Apache/2.2.22 (Ubuntu)"
+        },
+        {
+          "x-powered-by": "PHP/5.3.10-1ubuntu3.4"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "private, max-age=0, no-cache, max-age=86400, must-revalidate"
+        },
+        {
+          "p3p": "CP=\"CUR ADM OUR NOR STA NID\""
+        },
+        {
+          "set-cookie": "ljtrtb=eJyrVjJUslIyNrQ0MTYwNTM2NTA1NLA0NDW3VKoFAE9vBcg%3D; expires=Sun, 03-Nov-2013 13:29:49 GMT; path=/; domain=.lijit.com"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 13:29:49 GMT"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:48 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3194305635051091579; Domain=.turn.com; Expires=Thu, 02-May-2013 13:29:49 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:49 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3194305635051091579; Domain=.turn.com; Expires=Thu, 02-May-2013 13:29:49 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:49 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3194305635051091579; Domain=.turn.com; Expires=Thu, 02-May-2013 13:29:49 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:49 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "adaptv/1.0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "set-cookie": "rtbData0=\"key=turn:value=3194305635051091579:expiresAt=Sat+Nov+10+05%3A29%3A49+PST+2012:32-Compatible=true\";Path=/;Domain=.adap.tv;Expires=Mon, 03-Nov-2014 13:29:49 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "W/\"21947-1351808321000\""
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 22:18:41 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "21947"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:48 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:49 GMT"
+        },
+        {
+          "server": "Apache/2.2.4 (Unix) DAV/2 mod_ssl/2.2.4 OpenSSL/0.9.7a mod_fastcgi/2.4.2"
+        },
+        {
+          "set-cookie": "PUBRETARGET=82_1446557389; domain=pubmatic.com; expires=Tue, 03-Nov-2015 13:29:49 GMT; path=/"
+        },
+        {
+          "content-length": "1"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR LAW CUR ADMo DEVo TAIo PSAo PSDo IVAo IVDo HISo OTPo OUR SAMo BUS UNI COM NAV INT DEM CNT STA PRE LOC\""
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 26 May 2011 15:59:36 UTC"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "max-age=182357"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:50 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:29:50 GMT"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:50 GMT"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "set-cookie": "CMDD=;domain=casalemedia.com;path=/;expires=Sun, 04 Nov 2012 13:29:50 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "p3p": "policyref=\"http://tag.admeld.com/w3c/p3p.xml\", CP=\"PSAo PSDo OUR SAM OTR BUS DSP ALL COR\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "expires": "Mon, 26 Jul 1997 05:00:00 GMT"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:50 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "none"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:50 GMT"
+        },
+        {
+          "expires": "Mon, 26 Jul 1997 05:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://files.adbrite.com/w3c/p3p.xml\",CP=\"NOI PSA PSD OUR IND UNI NAV DEM STA OTC\""
+        },
+        {
+          "server": "XPEHb/1.2"
+        },
+        {
+          "set-cookie": "rb2=CiQKBjc0MjY5Nxjxxt_6vwEiEzMxOTQzMDU2MzUwNTEwOTE1NzkQAQ; path=/; domain=.adbrite.com; expires=Fri, 01-Feb-2013 13:29:50 GMT"
+        },
+        {
+          "content-length": "59"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "none"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:50 GMT"
+        },
+        {
+          "expires": "Mon, 26 Jul 1997 05:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://files.adbrite.com/w3c/p3p.xml\",CP=\"NOI PSA PSD OUR IND UNI NAV DEM STA OTC\""
+        },
+        {
+          "server": "XPEHb/1.2"
+        },
+        {
+          "set-cookie": "rb2=CiUKBzExMTM4NzQY78bf-r8BIhMzMTk0MzA1NjM1MDUxMDkxNTc5EAE; path=/; domain=.adbrite.com; expires=Fri, 01-Feb-2013 13:29:50 GMT"
+        },
+        {
+          "content-length": "59"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-aspnetmvc-version": "4.0"
+        },
+        {
+          "x-ua-compatible": "IE=Edge;chrome=1"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:51 GMT"
+        },
+        {
+          "content-length": "24328"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 23:16:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"951751d2bdb7cd1:0\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"809c1885b2cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "7723"
+        },
+        {
+          "age": "717364"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 16:35:09 GMT"
+        },
+        {
+          "expires": "Sat, 26 Oct 2013 06:13:48 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"08343346b2cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "11495"
+        },
+        {
+          "age": "717363"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 16:39:58 GMT"
+        },
+        {
+          "expires": "Sat, 26 Oct 2013 06:13:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001003"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:51 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"4bbce916b2cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "10962"
+        },
+        {
+          "age": "717363"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 16:38:33 GMT"
+        },
+        {
+          "expires": "Sat, 26 Oct 2013 06:13:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 10:27:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"04baaef128fcc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "ANONCHK=0; domain=c.msn.com; expires=Tue, 06-Nov-2012 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"cf3edfd75b2cd1:0\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "2004"
+        },
+        {
+          "age": "717364"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 16:37:22 GMT"
+        },
+        {
+          "expires": "Sat, 26 Oct 2013 06:13:48 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "content-length": "955"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "2008"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10723443-T100550693-C29000000000082620"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=309678"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "26369"
+        },
+        {
+          "age": "62302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 10:12:48 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001004"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:51 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=430622"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "45394"
+        },
+        {
+          "age": "62302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:48:32 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "anj=Kfu=8fG5+^Cxrx)0s]#%2L_'x%SEV/hnK]14FQV_eKj?9AMF4:V)4hY/82QjU'-Rw1k^WD2#$i1)erK!!*m?S=+svq; path=/; expires=Fri, 01-Feb-2013 13:29:52 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "content-length": "1391"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "must-revalidate"
+        },
+        {
+          "expires": "Mon, 05 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "set-cookie": "fc=PwF5GJAr9THO6EaVkyk6nl6s2gAVQMeB09yelt5Ns41Y8uK-xitYHevMNKV5qRPT6cGxTnB2KJjyGGqZV9P7973wc-cQ7FRKnITKYzO3zYV52dhK4dSErN9-EcLOAtq0; Domain=.turn.com; Expires=Thu, 02-May-2013 13:29:52 GMT; Path=/"
+        },
+        {
+          "content-type": "text/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "p3p": "CP=\"NOI CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT\""
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "set-cookie": "p=1-dPmPP55J4f0S;Path=/;Domain=.rfihub.com"
+        },
+        {
+          "location": "http://ib.adnxs.com/pxj?bidder=18&seg=378601&action=setuids('672725195243299649','');&redir="
+        },
+        {
+          "content-length": "0"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "anj=Kfu=8fG7DHCxrx)0s]#%2L_'x%SEV/hnK)x4FQV_eKj?9AMF4:V)4hY/82QjU'-Rw1k^WD2#$i1)erM67KPze!'cEZmBiRY; path=/; expires=Fri, 01-Feb-2013 13:29:52 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/json; charset=utf-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "private, no-cache, no-store, must-revalidate"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:52 GMT"
+        },
+        {
+          "content-length": "4142"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3194305635051091579; Domain=.turn.com; Expires=Thu, 02-May-2013 13:29:53 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:53 GMT"
+        },
+        {
+          "location": "http://dpm.demdex.net/ibs:dpid=375&dpuuid=3194305635051091579"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3194305635051091579; Domain=.turn.com; Expires=Thu, 02-May-2013 13:29:53 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:53 GMT"
+        },
+        {
+          "location": "http://tags.bluekai.com/site/4499?id=3194305635051091579"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3582991558377661871; Domain=.p-td.com; Expires=Thu, 02-May-2013 13:29:53 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:53 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat,  03 Nov 2012 13:29:53 GMT"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "server": "AAWebServer"
+        },
+        {
+          "p3p": "policyref=\"http://www.adadvisor.net/w3c/p3p.xml\",CP=\"NOI NID\""
+        },
+        {
+          "content-length": "21"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "set-cookie": "ab=0001%3ATeN7H043oXvJ0Gd0I9Rzik4yxpbxTv3S; Domain=.adadvisor.net; Expires=Sat,  03 Nov 2013 13:29:53 GMT; Path=/"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI NID CURa ADMa DEVa PSAa PSDa OUR SAMa BUS PUR COM NAV INT\""
+        },
+        {
+          "dcs": "la-dcs-3-1.internal.demdex.com 1.9.12"
+        },
+        {
+          "set-cookie": "demdex=24048888904140259620062165691276314735;Path=/;Domain=.demdex.net;Expires=Thu, 03-Nov-2022 23:37:33 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 2009 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache,no-store,must-revalidate,max-age=0,proxy-revalidate,no-transform,private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "location": "http://dpm.demdex.net/demconf.jpg?et:ibs%7cdata:dpid=375&dpuuid=3194305635051091579"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "server": "Jetty(7.2.2.v20101205)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI NID CURa ADMa DEVa PSAa PSDa OUR SAMa BUS PUR COM NAV INT\""
+        },
+        {
+          "dcs": "la-dcs-6-3.internal.demdex.com 1.9.12"
+        },
+        {
+          "set-cookie": "dpm=24048888904140259620062165691276314735;Path=/;Domain=.dpm.demdex.net;Expires=Thu, 03-Nov-2022 23:37:33 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 2009 00:00:00 GMT"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "no-cache,no-store,must-revalidate,max-age=0,proxy-revalidate,no-transform,private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "sts": "OK"
+        },
+        {
+          "content-length": "308"
+        },
+        {
+          "server": "Jetty(7.2.2.v20101205)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3194305635051091579; Domain=.turn.com; Expires=Thu, 02-May-2013 13:29:53 GMT; Path=/"
+        },
+        {
+          "location": "http://segment-pixel.invitemedia.com/set_partner_uid?partnerID=402&sscs_active=1&partnerUID=3194305635051091579"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:53 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3194305635051091579; Domain=.turn.com; Expires=Thu, 02-May-2013 13:29:53 GMT; Path=/"
+        },
+        {
+          "location": "http://dpm.demdex.net/ibs:dpid=470&dpuuid=3194305635051091579"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:53 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI NID CURa ADMa DEVa PSAa PSDa OUR SAMa BUS PUR COM NAV INT\""
+        },
+        {
+          "dcs": "la-dcs-6-4.internal.demdex.com 1.9.12"
+        },
+        {
+          "set-cookie": "dpm=24048888904140259620062165691276314735;Path=/;Domain=.dpm.demdex.net;Expires=Thu, 03-Nov-2022 23:37:33 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 2009 00:00:00 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "no-cache,no-store,must-revalidate,max-age=0,proxy-revalidate,no-transform,private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "sts": "OK"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "server": "Jetty(7.2.2.v20101205)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:53 GMT"
+        },
+        {
+          "set-cookie": "io_frequency=;Path=/;Domain=invitemedia.com;Expires=Thu, 01-Jan-1970 00:00:01 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"OTI DSP COR ADMo TAIo PSAo PSDo CONo OUR SAMo OTRo STP UNI PUR COM NAV INT DEM STA PRE LOC\""
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "location": "http://cm.g.doubleclick.net/pixel?google_nid=invitemedia&google_cm&google_hm=ZGdnc8YbR_itxPPM3K8lNw=="
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "server": "Jetty(7.3.1.v20110307)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "location": "http://g-pixel.invitemedia.com/gmatcher?google_gid=CAESEIZ7yBsa025UuJ5EUDIscrc&google_cver=1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "server": "Cookie Matcher"
+        },
+        {
+          "content-length": "293"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "set-cookie": "io_frequency=;Path=/;Domain=invitemedia.com;Expires=Thu, 01-Jan-1970 00:00:01 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"OTI DSP COR ADMo TAIo PSAo PSDo CONo OUR SAMo OTRo STP UNI PUR COM NAV INT DEM STA PRE LOC\""
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "server": "Jetty(7.3.1.v20110307)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3512552298351731296; Domain=.audienceiq.com; Expires=Thu, 02-May-2013 13:29:54 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:53 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3819163497929337273; Domain=.audienceiq.com; Expires=Thu, 02-May-2013 13:29:54 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "server": "Apache/2.2.3 (CentOS)"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR CUR ADMo DEVo PSAo PSDo OUR SAMo BUS UNI NAV\", policyref=\"http://tags.bluekai.com/w3c/p3p.xml\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store"
+        },
+        {
+          "set-cookie": "bkdc=wdc; expires=Mon, 03-Dec-2012 13:29:54 GMT; path=/; domain=.bluekai.com"
+        },
+        {
+          "bk-server": "f325"
+        },
+        {
+          "content-length": "62"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=430617"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "49975"
+        },
+        {
+          "age": "62304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:48:27 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=402248"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "54206"
+        },
+        {
+          "age": "62304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 11:55:38 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=404329"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "34129"
+        },
+        {
+          "age": "62304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 12:30:19 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=430621"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "30171"
+        },
+        {
+          "age": "62303"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:48:32 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=424287"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "32018"
+        },
+        {
+          "age": "62303"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 18:02:58 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=427425"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "24185"
+        },
+        {
+          "age": "62303"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 18:55:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=272174"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "33943"
+        },
+        {
+          "age": "62303"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Mon, 05 Nov 2012 23:47:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=325871"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "52353"
+        },
+        {
+          "age": "62304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 14:42:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=430158"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "29736"
+        },
+        {
+          "age": "62304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:40:48 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=430924"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "16459"
+        },
+        {
+          "age": "62304"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:53:34 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=392023"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "16888"
+        },
+        {
+          "age": "62303"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 09:05:14 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=430621"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "39134"
+        },
+        {
+          "age": "62303"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:48:32 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=430616"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "18663"
+        },
+        {
+          "age": "62303"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:48:27 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=430616"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "22545"
+        },
+        {
+          "age": "62303"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:48:27 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=309680"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "48746"
+        },
+        {
+          "age": "62303"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 10:12:51 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=307453"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "32656"
+        },
+        {
+          "age": "62303"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 09:35:44 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431902"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "4473"
+        },
+        {
+          "age": "16748"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 08:49:08 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=429492"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "5239"
+        },
+        {
+          "age": "22750"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 06:28:56 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=426970"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "4784"
+        },
+        {
+          "age": "22751"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 05:46:53 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431944"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "3843"
+        },
+        {
+          "age": "50495"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 23:27:23 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431143"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "4058"
+        },
+        {
+          "age": "50541"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 23:13:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431761"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "3291"
+        },
+        {
+          "age": "84703"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 13:54:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=430621"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "34459"
+        },
+        {
+          "age": "62302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:48:33 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 10:27:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"04baaef128fcc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "ANONCHK=0; domain=c.msn.com; expires=Tue, 06-Nov-2012 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:55 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:55 GMT"
+        },
+        {
+          "content-length": "954"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "1996"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10723443-T100550693-C29000000000082620"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001006"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:55 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "anj=Kfu=8fG4S]cvjr/?0P(*AuB-u**g1:XIFC`Ei'/AQwFYO^vhHR3SSI7:0ssX1ka!s@?zYs*/7]T1O`l^oQUpqMxHLk'kk[7/>IRq; path=/; expires=Fri, 01-Feb-2013 13:29:56 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:56 GMT"
+        },
+        {
+          "content-length": "1447"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "must-revalidate"
+        },
+        {
+          "expires": "Mon, 05 Nov 2012 13:29:56 GMT"
+        },
+        {
+          "set-cookie": "fc=2flUeaaDSas8xOI0IwXvS5DprXaL8T8Iioo9PyFO3fRY8uK-xitYHevMNKV5qRPT3ar07KU0y6i_uQzRwZVJBr3wc-cQ7FRKnITKYzO3zYV52dhK4dSErN9-EcLOAtq0; Domain=.turn.com; Expires=Thu, 02-May-2013 13:29:56 GMT; Path=/"
+        },
+        {
+          "content-type": "text/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:55 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001005"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:55 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001002"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:55 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=337125"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "47409"
+        },
+        {
+          "age": "62303"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 17:50:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:55 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html;charset=UTF-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "private, no-cache, no-store, must-revalidate"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:56 GMT"
+        },
+        {
+          "content-length": "4142"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=430158"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "40464"
+        },
+        {
+          "age": "62303"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:54 GMT"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 19:40:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "server": "Microsoft-IIS/7.0"
+        },
+        {
+          "p3p": "CP=\"NON DSP COR CURa PSA PSD OUR BUS NAV STA\""
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:55 GMT"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "vary": "Accept-Encoding"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 10:27:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"04baaef128fcc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "ANONCHK=0; domain=c.msn.com; expires=Tue, 06-Nov-2012 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:56 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:57 GMT"
+        },
+        {
+          "content-length": "1162"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "2685"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10661134-T100558395-C70000000000110100"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001005"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:57 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001004"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:56 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001005"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:57 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-length": "2346"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:57 GMT"
+        },
+        {
+          "location": "http://s0.2mdn.net/viewad/3642305/1-1x1.gif"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "DCLK-AdSvr"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "last-modified": "Tue, 08 May 2012 20:09:07 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 14:30:09 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "321"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "82788"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 24 May 2012 20:12:56 GMT"
+        },
+        {
+          "date": "Fri, 02 Nov 2012 20:51:00 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 20:51:00 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "16399"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "age": "59938"
+        },
+        {
+          "cache-control": "public, max-age=86400"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:57 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 10:27:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"04baaef128fcc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "ANONCHK=0; domain=c.msn.com; expires=Tue, 06-Nov-2012 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:58 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:59 GMT"
+        },
+        {
+          "content-length": "953"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "1996"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10723443-T100550693-C29000000000082620"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001006"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:58 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "anj=Kfu=8fG2<rcvjr/?0P(*AuB-u**g1:XIB_LEi'/AQwFYO^vhHR3SSI7:0ssX1dkoAOc['^!Xu%1Q*<TAN0EGDS>RnOx8gy:7tmWz0W/8y; path=/; expires=Fri, 01-Feb-2013 13:29:59 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:59 GMT"
+        },
+        {
+          "content-length": "539"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001005"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:58 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001003"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:59 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-msadid": "300089440.299934848"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:58 GMT"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "508"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3194305635051091579; Domain=.turn.com; Expires=Thu, 02-May-2013 13:29:59 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:58 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:59 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 10:27:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"04baaef128fcc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "ANONCHK=0; domain=c.msn.com; expires=Tue, 06-Nov-2012 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:00 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:59 GMT"
+        },
+        {
+          "content-length": "954"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "2008"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10723443-T100550693-C29000000000082620"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001005"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:59 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001004"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:59 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001001"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:00 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "anj=Kfu=8fG6kGcvjr/?0P(*AuB-u**g1:XIDv6Ei'/AQwFYO^vhHR3SSI7:0ssX1dl0I/A@=2rt>+)p4?5?fIu<PZksk:x)IOr/*M`m)sza^*g0Y08QVgH; path=/; expires=Fri, 01-Feb-2013 13:30:00 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:00 GMT"
+        },
+        {
+          "content-length": "536"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-msadid": "300089389.300024911"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:29:59 GMT"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "2292"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:00 GMT"
+        },
+        {
+          "set-cookie": "io_frequency=;Path=/;Domain=invitemedia.com;Expires=Thu, 01-Jan-1970 00:00:01 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"OTI DSP COR ADMo TAIo PSAo PSDo CONo OUR SAMo OTRo STP UNI PUR COM NAV INT DEM STA PRE LOC\""
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "server": "Jetty(7.3.1.v20110307)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:00 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 10:27:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"04baaef128fcc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "ANONCHK=0; domain=c.msn.com; expires=Tue, 06-Nov-2012 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:01 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:02 GMT"
+        },
+        {
+          "content-length": "954"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "2008"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10723443-T100550693-C29000000000082620"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001006"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:01 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001001"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:01 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001004"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:01 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "anj=Kfu=8fG10Qcvjr/?0P(*AuB-u**g1:XICjmEi'/AQwFYO^vhHR3SSI7:0ssX1dl0I/A@=2rt>+)p4?5?fIu<PZksk:8=1cxYEkOC%bAiN80044UhHvz!%M]c5$rYZ; path=/; expires=Fri, 01-Feb-2013 13:30:02 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:02 GMT"
+        },
+        {
+          "content-length": "555"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:02 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "mt2/2.6.2.2465 Sep 24 2012 22:21:34 ewr-pixel-x1 pid 0x7b39 31545"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID CURa ADMa DEVa PSAa PSDa OUR BUS COM INT OTC PUR STA\""
+        },
+        {
+          "set-cookie": "uuid=50951c5a-a617-32b8-be76-b1fea84d696f; domain=.mathtag.com; path=/; expires=Sun, 03-Nov-2013 13:30:02 GMT"
+        },
+        {
+          "location": "http://sync.mathtag.com/sync/img?mt_exid=13&mt_exuid=3755642153863499992&mm_bnc"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-msadid": "300088980.299949017.299886085"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:01 GMT"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "2684"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "33683"
+        },
+        {
+          "allow": "GET"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 04:46:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:02 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:02 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "server": "mt2/2.6.2.2465 Sep 24 2012 22:21:34 ewr-pixel-x6 pid 0x3dc6 15814"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID CURa ADMa DEVa PSAa PSDa OUR BUS COM INT OTC PUR STA\""
+        },
+        {
+          "set-cookie": "mt_mop=13:1351949402|10002:1351949402; domain=.mathtag.com; path=/; expires=Mon, 03-Dec-2012 13:30:02 GMT"
+        },
+        {
+          "location": "http://tags.bluekai.com/site/2948?id=50951c5a-a617-32b8-be76-b1fea84d696f"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:02 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:02 GMT"
+        },
+        {
+          "server": "Apache/2.2.3 (CentOS)"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR CUR ADMo DEVo PSAo PSDo OUR SAMo BUS UNI NAV\", policyref=\"http://tags.bluekai.com/w3c/p3p.xml\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 13:30:02 GMT"
+        },
+        {
+          "cache-control": "max-age=86400, private"
+        },
+        {
+          "set-cookie": "bkdc=wdc; expires=Mon, 03-Dec-2012 13:30:02 GMT; path=/; domain=.bluekai.com"
+        },
+        {
+          "bk-server": "ed3c"
+        },
+        {
+          "content-length": "62"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 10:27:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"04baaef128fcc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "ANONCHK=0; domain=c.msn.com; expires=Tue, 06-Nov-2012 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:02 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001005"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:02 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001003"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:03 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001001"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:02 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:03 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "1996"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-radid": "P10723443-T100550693-C29000000000082620"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:03 GMT"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "954"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "anj=Kfu=8fG5`$cvjr/?0P(*AuB-u**g1:XIF)WEi'/AQwFYO^vhHR3SSI7:0ssX1dl0I/A@=2rt>+)p4?5?fIu<PZksk:8=1cxY3czU+5<.*N0EGE.[oe!wqakQLnSHY42'_N; path=/; expires=Fri, 01-Feb-2013 13:30:03 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:03 GMT"
+        },
+        {
+          "content-length": "534"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-msadid": "300089440.299934848"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:03 GMT"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "509"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI NID CURa ADMa DEVa PSAa PSDa OUR SAMa BUS PUR COM NAV INT\""
+        },
+        {
+          "dcs": "la-dcs-4-2.internal.demdex.com 1.9.12"
+        },
+        {
+          "set-cookie": "dpm=24048888904140259620062165691276314735;Path=/;Domain=.dpm.demdex.net;Expires=Thu, 03-Nov-2022 23:37:33 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 2009 00:00:00 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "no-cache,no-store,must-revalidate,max-age=0,proxy-revalidate,no-transform,private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "sts": "OK"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "server": "Jetty(7.2.2.v20101205)"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "server": "Microsoft-IIS/8.0"
+        },
+        {
+          "x-aspnetmvc-version": "4.0"
+        },
+        {
+          "x-ua-compatible": "IE=Edge;chrome=1"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-frame-options": "SAMEORIGIN"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:04 GMT"
+        },
+        {
+          "content-length": "26996"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 23:16:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"951751d2bdb7cd1:0\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "private, no-cache, proxy-revalidate, no-store"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "last-modified": "Thu, 20 Oct 2011 10:27:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"04baaef128fcc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "set-cookie": "ANONCHK=0; domain=c.msn.com; expires=Tue, 06-Nov-2012 13:29:30 GMT; path=/;"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:05 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001005"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:04 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, must-revalidate"
+        },
+        {
+          "content-type": "text/html; Charset=utf-8"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 19:29:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"01c35bc2fa3cd1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:05 GMT"
+        },
+        {
+          "content-length": "954"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "2008"
+        },
+        {
+          "expires": "Fri, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "x-radid": "P10723443-T100550693-C29000000000082620"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=426988"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "27953"
+        },
+        {
+          "age": "303593"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:05 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 23:46:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=416068"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "42814"
+        },
+        {
+          "age": "303593"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:05 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 20:44:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache, no-store, must-revalidate"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "last-modified": "Mon, 18 Jul 2011 19:03:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "etag": "\"a29327667d45cc1:0\""
+        },
+        {
+          "server": "Microsoft-IIS/7.5"
+        },
+        {
+          "s": "VIEMSNVM001002"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:05 GMT"
+        },
+        {
+          "content-length": "42"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store, no-cache, private"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Sat, 15 Nov 2008 16:00:00 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.adnxs.com/w3c/policy/p3p.xml\", CP=\"NOI DSP COR ADM PSAo PSDo OURo SAMo UNRo OTRo BUS COM NAV DEM STA PRE\""
+        },
+        {
+          "x-xss-protection": "0"
+        },
+        {
+          "set-cookie": "anj=Kfu=8fG3H<cvjr/?0P(*AuB-u**g1:XIC8]Ei'/AQwFYO^vhHR3SSI7:0ssX1dl0I/A@=2rt>+)p4?5?fIu<PZksk:^@W+Nwgwmo%ACvi+5<.*N0Iu+9Y'W#w3TL$v$Kfc5PKO+; path=/; expires=Fri, 01-Feb-2013 13:30:05 GMT; domain=.adnxs.com; HttpOnly"
+        },
+        {
+          "content-type": "text/html; charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:05 GMT"
+        },
+        {
+          "content-length": "607"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:06 GMT"
+        },
+        {
+          "server": "Jetty(6.1.22)"
+        },
+        {
+          "set-cookie": "wfivefivec=8293faeb-8917-4006-95bc-2d52d4cd2f6d;Path=/;Domain=.w55c.net;Expires=Mon, 03-Nov-14 13:30:06 GMT"
+        },
+        {
+          "p3p": "policyref=\"https://cts.w55c.net/ct/p3p_policy_ref.xml\", CP=\"UNI PUR COM INT STA OTC STP OUR CUR TAIo COR DSP NOI\""
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "x-powered-by": "Mirror Image Internet"
+        },
+        {
+          "content-length": "42"
+        },
+        {
+          "via": "1.1 ttn061005 (MII-APC/2.2)"
+        },
+        {
+          "keep-alive": "timeout=2"
+        },
+        {
+          "connection": "Keep-Alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-store"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "0"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-msadid": "300089440.299934848"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:05 GMT"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-length": "511"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/json; charset=utf-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:05 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:05 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Mon, 01 Jan 1990 00:00:00 GMT"
+        },
+        {
+          "cache-control": "private, no-cache, no-cache=Set-Cookie, no-store, proxy-revalidate"
+        },
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=418020"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "41311"
+        },
+        {
+          "age": "303593"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:06 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 21:17:13 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=426681"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "47495"
+        },
+        {
+          "age": "303593"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:06 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 23:41:34 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431985"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA08"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA08"
+        },
+        {
+          "content-length": "26998"
+        },
+        {
+          "age": "385360"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:06 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 02:27:11 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431851"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA06"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA06"
+        },
+        {
+          "content-length": "27541"
+        },
+        {
+          "age": "327876"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:06 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 18:23:01 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431570"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "40200"
+        },
+        {
+          "age": "303592"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:06 GMT"
+        },
+        {
+          "expires": "Mon, 05 Nov 2012 01:03:04 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=426988"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "31967"
+        },
+        {
+          "age": "303593"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:06 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 23:46:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431525"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "45843"
+        },
+        {
+          "age": "303592"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:06 GMT"
+        },
+        {
+          "expires": "Mon, 05 Nov 2012 01:02:19 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=431595"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA07"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA07"
+        },
+        {
+          "content-length": "41590"
+        },
+        {
+          "age": "303593"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:06 GMT"
+        },
+        {
+          "expires": "Mon, 05 Nov 2012 01:03:28 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "public, must-revalidate, proxy-revalidate, max-age=426365"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "server": "CO1MPPSTCA05"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "p3p": "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\""
+        },
+        {
+          "s": "CO1MPPSTCA05"
+        },
+        {
+          "content-length": "46706"
+        },
+        {
+          "age": "303593"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:30:06 GMT"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 23:36:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_30.json b/jetty-http2/http2-hpack/src/test/resources/data/story_30.json
new file mode 100644
index 0000000..6ecb39d
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_30.json
@@ -0,0 +1,28257 @@
+{
+  "context": "response",
+  "cases": [
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:05 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "location": "http://www.nytimes.com/"
+        },
+        {
+          "content-length": "207"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html; charset=iso-8859-1"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:05 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "content-length": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-type": "text/plain; charset=utf-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:05 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "content-length": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-type": "text/plain; charset=utf-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 02 Oct 2012 23:33:57 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1054"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=242"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:43:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "915"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=75412"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:35:58 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "563"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=75368"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:35:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 13:17:50 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "5433"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=71334"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:28:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 02 Oct 2012 23:33:51 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "11190"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=255"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:43:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:01:02 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "414"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=124"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 00:30:38 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1460"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=141"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:27 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "593"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=75423"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:36:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 00:30:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "14014"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=181"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:42:07 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 13:32:22 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2864"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=71377"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:28:43 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 14 May 2010 15:09:30 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1204"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=41140"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 01:04:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 14 Sep 2011 21:26:27 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1330"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=249513"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 10:57:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Thu, 28 Jun 2012 19:00:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1261"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=249513"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 10:57:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Mar 2012 17:13:25 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "33140"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=121"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:07 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 14 May 2010 14:49:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "75"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=348760"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:31:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "769"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=63729"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:21:15 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 24 Oct 2012 22:07:11 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2335"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=501801"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 09:02:27 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 05 Jun 2012 14:20:01 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5065"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=139006"
+        },
+        {
+          "expires": "Mon, 05 Nov 2012 04:15:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 18:30:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9619"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=449914"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 18:37:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 22:09:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9453"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=463322"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 22:21:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:05 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "RMID=007f010022166047bee9002b; Expires=Sun, 03 Nov 2013 13:39:05 GMT; Path=/; Domain=.nytimes.com;"
+        },
+        {
+          "set-cookie": "adxcs=-; path=/; domain=.nytimes.com"
+        },
+        {
+          "vary": "Host"
+        },
+        {
+          "cteonnt-length": "174626"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 20:52:54 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "10651"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=548063"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 21:53:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 00:08:29 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9713"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=481042"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 03:16:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 13 Sep 2012 21:58:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "13397"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=50108"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 03:34:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 14:25:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10596"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=43276"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 01:40:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 13 Sep 2012 21:58:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "857"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=50108"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 03:34:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 22:20:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "23526"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=60"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:40:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 19:26:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "9473"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=551938"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 22:58:04 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:54:29 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5048"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=63748"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:21:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 14 May 2010 14:49:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3428"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=348719"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:31:05 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 22:24:49 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9374"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=550656"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 22:36:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 01:15:10 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9656"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=561534"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 01:38:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 00:27:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "10211"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=564327"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 02:24:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 02:03:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9722"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568295"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:30:41 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 23:51:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9372"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=555580"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 23:58:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 17:14:51 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "22786"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=559540"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 01:04:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 01:28:33 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9942"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=564328"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 02:24:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 02:54:41 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9364"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568578"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:35:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 06:09:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "9404"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=578228"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 06:16:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 22:38:39 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10217"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=478523"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 02:34:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 01:19:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9052"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568307"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:30:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 23:00:45 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9379"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=553121"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 23:17:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 19:02:49 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9989"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=538058"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 19:06:44 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 22:36:19 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "10950"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=531474"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 17:17:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 16:22:38 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "11381"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=529651"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 16:46:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 18:00:09 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10322"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=534703"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 18:10:49 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 17:11:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "22643"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=537513"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 18:57:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:59:51 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8617"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=459081"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 21:10:27 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 19:29:35 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10164"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=539911"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 19:37:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 05:22:48 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "9476"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=488920"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 05:27:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 22:29:10 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9077"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=551868"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 22:56:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 17:43:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10600"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=540450"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 19:46:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 20:32:14 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9896"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=547139"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 21:38:05 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 14 May 2010 15:09:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "572"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=41141"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 01:04:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Tue, 28 Apr 2009 18:18:22 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "404"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=41148"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 01:04:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 14 Sep 2012 11:12:29 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2845"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=63810"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:22:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 13 Sep 2012 16:49:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3246"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=64066"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:26:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 22:45:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "18527"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=51883"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:03:49 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "p3p": "CP=\"IDC DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/0.7.59"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:21 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "402"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=61804"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 06:49:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1108"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=33319"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 22:54:25 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Fri, 14 May 2010 14:50:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "3113"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=250845"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 11:19:51 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "132"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=82000"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 12:25:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "130"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=44124"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 01:54:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 12:22:59 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2027"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=141"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:27 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5636"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=39613"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:39:19 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 02:57:18 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "9333"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=592839"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 10:19:45 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 04:04:47 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "12380"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=574165"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 05:08:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 03:51:38 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "14526"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=574163"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 05:08:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 09:37:18 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "27338"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=592839"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 10:19:45 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 03:21:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "17678"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=574163"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 05:08:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "15086"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "cache-control": "private, max-age=47723"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 02:54:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 15 Oct 2012 20:38:16 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "14119"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=38721"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:24:27 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 22:23:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "15038"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=77030"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 11:02:56 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "15051"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=39616"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:39:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 03:52:36 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "9914"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=574165"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 05:08:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 04:26:29 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "13388"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=574135"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 05:08:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:54:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=45924"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 02:24:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:06 GMT"
+        },
+        {
+          "content-length": "73046"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=39601"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:39:16 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:15 GMT"
+        },
+        {
+          "content-length": "1713"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 21:26:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1246"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=45118"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 02:11:13 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:15 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9877"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=39601"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:39:16 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:15 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "328"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=63772"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:22:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:16 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/1.0.0"
+        },
+        {
+          "set-cookie": "nyt-m=0F8E3E619FFB422270FEEB659AA60437&e=i.1354338000&t=i.10&v=i.0&l=l.25.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1&n=i.2&g=i.0&er=i.1351949956&vr=l.4.0.0.0.0&pr=l.4.1.0.0.0&vp=i.0&gf=l.10.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1; expires=Thu, 02-Nov-2017 13:39:16 GMT; path=/; domain=.nytimes.com"
+        },
+        {
+          "content-length": "114"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "424"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=81965"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 12:25:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "504"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=81965"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 12:25:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Fri, 25 Mar 2011 19:37:16 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "68"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=1088067"
+        },
+        {
+          "expires": "Fri, 16 Nov 2012 03:53:44 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 25 Mar 2011 19:37:16 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "3422"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=279280"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 19:13:56 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "nncoection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "368"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=82007"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 12:26:04 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=39602"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:39:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:16 GMT"
+        },
+        {
+          "content-length": "8728"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:48:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "7020"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=39602"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:39:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 14 May 2010 14:49:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "193"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=349609"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:46:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 14 May 2010 15:30:45 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "193"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=349609"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:46:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 27 Feb 2010 03:33:33 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "192"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=349609"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:46:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 14 May 2010 14:49:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "192"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=349609"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:46:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:50:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "35113"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=592829"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 10:19:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:48:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "29467"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=592829"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 10:19:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:51:50 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "16823"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=592829"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 10:19:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "550"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=82007"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 12:26:04 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:52:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "16912"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=592829"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 10:19:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:49:19 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "25024"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=592829"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 10:19:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:49:48 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "24454"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=592829"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 10:19:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:50:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "30450"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=592829"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 10:19:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:51:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "19300"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=592829"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 10:19:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 12:34:42 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10032"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=536866"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 18:47:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 03:00:09 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "9307"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=566721"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:04:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 04:58:40 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9354"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573743"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 05:01:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 08 Jun 2012 19:24:51 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "49"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=349611"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:46:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "4654"
+        },
+        {
+          "set-cookie": "PRpc=|HwqgHD3W:1|HrYwHDG0:1|HrYvHDG1:2|#;domain=ads.pointroll.com; path=/; expires=Mon, 03-Nov-2014 13:39:17 GMT;"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "186"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=72083"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:40:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=45062"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 02:10:19 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "188"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=80765"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 12:05:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "74"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=45140"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 02:11:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "cache-control": "max-age=1200"
+        },
+        {
+          "etag": "\"4c3fb0afe8567a750ed2a0ea49e6944fba16bf00\""
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-served-by": "apiservices-a004.krxd.net"
+        },
+        {
+          "x-request-backend": "controltag"
+        },
+        {
+          "x-config-age": "400"
+        },
+        {
+          "x-cache-hits": "103"
+        },
+        {
+          "x-age": "377"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "content-length": "26341"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "64"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=49385"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 03:22:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 05 Oct 2012 17:38:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2153"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=35030"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 23:23:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "159"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "5623"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 21:27:49 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=82032"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 12:26:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:16 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Tue, 01 Mar 2011 22:54:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "2078"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=279283"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 19:14:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "nncoection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 03 Mar 2011 19:32:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "593"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=279282"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 19:14:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "nncoection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 03 Mar 2011 19:32:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1062"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=279282"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 19:14:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "nncoection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 03 Mar 2011 19:32:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "830"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=279285"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 19:14:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "nncoection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 14 Nov 2011 19:12:50 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1230"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=310332"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 03:51:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=49385"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 03:22:23 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "nncoection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 03 Mar 2011 19:32:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "319"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=279282"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 19:14:00 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3582991558377661871; Domain=.p-td.com; Expires=Thu, 02-May-2013 13:39:18 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "content-length": "65"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "cache-control": "no-cache"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1968"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=39604"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:39:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLs37lVrcF5/ZLq6rynLbPIrYKIQNSvBay5eXmklcaqTTiLoOeuQVXo/ErioUh8fUwMfecUglqHQguFB4PZbqjfC/R139oOkeO9km0JPhPmAlXHZdbK2WUEIbFJNiFPRmQDogdoIloef2Ff8AdiQTdLWj97qwBkClC8B/JlbaEoMNL360/5sp2oAT08Y; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:39:18 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas02-12"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:19 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "content-length": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-type": "text/plain; charset=utf-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=39604"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:39:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "content-length": "11292"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "x-amz-id-2": "DlMsa95OAPS0ALn9DdiKGfdHovCM2TvJb0VQCd4x9FF44D35U9YbOWNnUKKYQL89"
+        },
+        {
+          "x-amz-request-id": "8987CB21B2CF98CC"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:20 GMT"
+        },
+        {
+          "cache-control": "max-age=10"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:35:29 GMT"
+        },
+        {
+          "etag": "\"8254326a395317db7b197564fa83e476\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": ""
+        },
+        {
+          "content-length": "92155"
+        },
+        {
+          "server": "AmazonS3"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=39603"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:39:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:19 GMT"
+        },
+        {
+          "content-length": "645"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "6174"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=39603"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 00:39:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:19 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "303"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:19 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "location": "/dcsa5pgfq10000c9zuysqk0lm_6i8y/dcs.gif?dcsredirect=126&dcstlh=0&dcstlv=0&dcsdat=1351949959619&dcssip=www.nytimes.com&dcsuri=/&WT.co_f=130.129.68.73-2680109824.30259656&WT.vt_sid=130.129.68.73-2680109824.30259656.1351949959620&WT.vt_f_tlv=0&WT.tz=-4&WT.bh=9&WT.ul=en-US&WT.cd=24&WT.sr=1366x768&WT.jo=Yes&WT.ti=The%20New%20York%20Times%20-%20Breaking%20News,%20World%20News%20%26%20Multimedia&WT.js=Yes&WT.jv=1.7&WT.ct=unknown&WT.bs=994x649&WT.fi=No&WT.tv=1.0.7&WT.dl=0&WT.es=www.nytimes.com/&WT.z_fbc=&WT.cg_n=Homepage&WT.z_rcgn=Homepage&WT.z_gpt=Homepage&WT.z_nyts=&WT.z_nytd=&WT.z_rmid=007f010022166047bee9002b&WT.rv=0&WT.mc_ev=&WT.vt_f_tlh=0&WT.vt_f_d=1&WT.vt_f_s=1&WT.vt_f_a=1&WT.vt_f=1"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "set-cookie": "ACOOKIE=C8ctADEzMC4xMjkuNjguNzMtMjY4MDEwOTgyNC4zMDI1OTY1NgAAAAAAAAABAAAAmLwAAIcelVCHHpVQAQAAAHhHAACHHpVQhx6VUAAAAAA-; path=/; expires=Thu, 10-Dec-2015 10:27:34 GMT"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM NAV INT STA\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:19 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "set-cookie": "ACOOKIE=C8ctADEzMC4xMjkuNjguNzMtMjY4MDEwOTgyNC4zMDI1OTY1NgAAAAAAAAABAAAAmLwAAIcelVCHHpVQAQAAAHhHAACHHpVQhx6VUAAAAAA-; path=/; expires=Mon, 03-Nov-2014 13:39:19 GMT"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM NAV INT STA\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "67"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Mon, 27 Aug 2012 21:22:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "430"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=45030"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 02:09:49 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:19 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 21:27:48 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1103"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=82037"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 12:26:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:19 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1766"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=45133"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 02:11:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:19 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "etag": "\"15850e53f035443e5db6f28f75a9c1ac:1351623427\""
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 18:57:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=1200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "content-length": "17391"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "public, max-age=600"
+        },
+        {
+          "content-type": "text/javascript;charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:18 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:31:16 GMT"
+        },
+        {
+          "server": "ECS (lhr/4BD8)"
+        },
+        {
+          "status": "200 OK"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "9879"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:22 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 13:39:22 GMT"
+        },
+        {
+          "last-modified": "Tue, 31 Jul 2012 23:02:57 GMT"
+        },
+        {
+          "server": "ECS (lhr/4BF1)"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "35"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "etag": "\"f523ab9a498518867f12ca24f39ed087:1310577714\""
+        },
+        {
+          "last-modified": "Wed, 13 Jul 2011 17:10:50 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "cache-control": "max-age=1200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:21 GMT"
+        },
+        {
+          "content-length": "14202"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:24 GMT"
+        },
+        {
+          "etag": "\"894881535586350a54ec0f6e94779ee35c2169bd\""
+        },
+        {
+          "x-age": "0"
+        },
+        {
+          "x-cache": "MISS"
+        },
+        {
+          "x-cache-hits": "0"
+        },
+        {
+          "x-kuid": "IAJ5Qqdp"
+        },
+        {
+          "x-request-backend": "user_data"
+        },
+        {
+          "x-served-by": "apiservices-a002.krxd.net"
+        },
+        {
+          "content-length": "100"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "cache-control": "private, no-cache, no-store"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:24 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.krxd.net/kruxcontent/p3p.xml\", CP=\"NON DSP COR NID OUR DEL SAM OTR UNR COM NAV INT DEM CNT STA PRE LOC OTC\""
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "_kuid_=IAJ5QtWI; path=/; expires=Thu, 02-May-13 13:39:24 GMT; domain=.krxd.net"
+        },
+        {
+          "x-request-time": "D=250 t=1351949964465812"
+        },
+        {
+          "x-served-by": "beacon-a002.krxd.net"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:25 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "content-length": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-type": "text/plain; charset=utf-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "public, max-age=604800"
+        },
+        {
+          "content-type": "text/css;charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:21 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 10:41:53 GMT"
+        },
+        {
+          "server": "ECS (lhr/4B9B)"
+        },
+        {
+          "status": "200 OK"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "54615"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "131"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=63879"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:24:07 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:28 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "130"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=41854"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 01:17:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:28 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "set-cookie": "ACOOKIE=C8ctADEzMC4xMjkuNjguNzMtMjY4MDEwOTgyNC4zMDI1OTY1NgAAAAAAAAABAAAAmLwAAJEelVCHHpVQAQAAAHhHAACRHpVQhx6VUAAAAAA-; path=/; expires=Mon, 03-Nov-2014 13:39:29 GMT"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM NAV INT STA\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "67"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "303"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "content-type": "text/plain"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "vary": "Host"
+        },
+        {
+          "location": "http://www.nytimes.com/glogin?URI=http://www.nytimes.com/2012/11/03/us/pennsylvania-omitted-poison-data-in-water-report.html&OQ=hpQ26_rQ3D0&OP=36bee93bQ2FQ27NgQ3FQ27zwQ3FQ27nnnQ27vQ3FoJQ27!N)ujNNQ3FQ2FQ27Q2FQ60Q22Q2FQ27Q22Q22Q27Q60Q20Q27TuQ27gSzzuwJEQ24zCQ240NoCQ3FQ3FS!0gNCuNz0!Q24Q3FQ240Cz0nQ24Q3FSj0jSgNjQ3FQ3AvQ3FoJ"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "302"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "set-cookie": "NYT-S=0MdTeGr8L1EDLDXrmvxADeHzL3eTxUhvw7deFz9JchiAIUFL2BEX5FWcV.Ynx4rkFI; expires=Mon, 03-Dec-2012 13:39:29 GMT; path=/; domain=.nytimes.com"
+        },
+        {
+          "location": "http://www.nytimes.com/2012/11/03/us/pennsylvania-omitted-poison-data-in-water-report.html?hp&_r=0"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "495"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=72064"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:40:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "539"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=30984"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 22:15:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "777"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=70943"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "172"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=55043"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:56:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 21:27:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "7026"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=67908"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:31:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "set-cookie": "NYT-S=0MdTeGr8L1EDLDXrmvxADeHzL3eTxUhvw7deFz9JchiAIUFL2BEX5FWcV.Ynx4rkFI; expires=Mon, 03-Dec-2012 13:39:29 GMT; path=/; domain=.nytimes.com"
+        },
+        {
+          "location": "http://www.nytimes.com/2012/11/03/us/pennsylvania-omitted-poison-data-in-water-report.html?hp&_r=0"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "NYT-S=0MTlTX5YhqzXfDXrmvxADeHBiKThPzJplXdeFz9JchiAJK89nlVaR7bsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "vary": "Host"
+        },
+        {
+          "cteonnt-length": "61027"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 21:27:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3093"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=71366"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:28:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5636"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=71943"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:38:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Mon, 15 Oct 2012 20:38:16 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "2481"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=70868"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:20:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 15 Oct 2012 20:38:16 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1241"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=67395"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:22:44 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "p3p": "CP=\"IDC DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/0.7.59"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR PSAo PSDo OUR BUS OTC\""
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "x-aspnet-version": "2.0.50727"
+        },
+        {
+          "content-length": "1"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-type": "text/plain; charset=utf-8"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:11:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "568"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=72065"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:40:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1307"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=67397"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:22:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:48:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "47727"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=66869"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:13:58 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 17 Sep 2012 20:20:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2093"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=70944"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "693"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=67422"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:23:11 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2045"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=71899"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:37:48 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1052"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=71981"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:39:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:11:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2466"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=72115"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:41:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "885"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=70944"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 10 Sep 2012 21:02:50 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3686"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=67060"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:17:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "636"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=72224"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:43:13 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "850"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=70987"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:22:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1057"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=70895"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:04 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:47:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1577"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=70868"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:20:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "546"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=71763"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:35:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MTlTX5YhqzXfDXrmvxADeHBiKThPzJplXdeFz9JchiAJK89nlVaR7bsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1911"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=71744"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:35:13 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "648"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=66989"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:15:58 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "NYT-S=0MWfXncvqFxdbDXrmvxADeHBiKThPzJplXdeFz9JchiAJYOVLIKocjgsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cteonnt-length": "45"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "59"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "956"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=66847"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:13:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "303"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "location": "http://d1aivi5dp2wry5.cloudfront.net/pixel.gif"
+        },
+        {
+          "p3p": "CP=\"NOI PSAo OUR IND COM NAV STA\""
+        },
+        {
+          "server": "Microsoft-IIS/7.0"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 25 Mar 2011 19:37:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1110"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=251474"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 11:30:43 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:16 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "525"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=70973"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:22:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "522"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=66881"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:14:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1766"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=63849"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:23:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "864"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=72341"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:45:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5039"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=66914"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:14:43 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 22:21:50 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "561"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=70935"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:44 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 26 Apr 2012 15:13:40 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8381"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=351783"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 15:22:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Thu, 04 Nov 2010 16:47:55 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1896"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=51812"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:03:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 17 Sep 2012 18:13:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5551"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=71858"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:37:07 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 01:19:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "15650"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=571282"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:20:51 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 22:23:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "22905"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=51812"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:03:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1713"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=70944"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Fri, 11 Sep 2009 17:35:00 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "69"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=72168"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:42:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 30 May 2012 16:54:32 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "13402"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=18685"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 18:50:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8728"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=67350"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:21:59 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "656"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=67439"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:23:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5248"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=70974"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:22:23 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1103"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=70946"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-amz-id-2": "YvVdaXFdQYBoLiHhS/Pvn3QQnQ4PguJp3trhCHZSnIIo5NT7S98Tdyovty62vHSG"
+        },
+        {
+          "x-amz-request-id": "CD0C61042E9125AE"
+        },
+        {
+          "date": "Thu, 05 Jul 2012 20:17:51 GMT"
+        },
+        {
+          "cache-control": "max-age=600000"
+        },
+        {
+          "last-modified": "Wed, 28 Sep 2011 20:00:18 GMT"
+        },
+        {
+          "etag": "\"df3e567d6f16d040326c7a0ea29a4f41\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "AmazonS3"
+        },
+        {
+          "age": "64616"
+        },
+        {
+          "x-amz-cf-id": "oorNbpV0j8hpPM9RfynFxe0GrjwY7QWan8Mzs8SdZ-6uuC5_pgLgZA=="
+        },
+        {
+          "via": "1.0 2cfcdac9b945cce88c21cc3f30d884cd.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "126"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=67490"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:24:19 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/1.0.0"
+        },
+        {
+          "set-cookie": "nyt-m=8F26281382200A491A2301632AB3A6B8&e=i.1354338000&t=i.10&v=i.1&l=l.25.2802408197.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1&n=i.2&g=i.0&er=i.1351949956&vr=l.4.1.0.0.0&pr=l.4.2.0.0.0&vp=i.0&gf=l.10.2802408197.-1.-1.-1.-1.-1.-1.-1.-1.-1; expires=Thu, 02-Nov-2017 13:39:29 GMT; path=/; domain=.nytimes.com"
+        },
+        {
+          "content-length": "113"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 16 Oct 2012 21:00:10 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5399"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=66914"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:14:43 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1401"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=71732"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:35:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 02 Mar 2011 21:46:12 GMT"
+        },
+        {
+          "etag": "\"195-49d86d768f100\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-host": "b103 D=2883"
+        },
+        {
+          "keep-alive": "timeout=120, max=990"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "age": "93     "
+        },
+        {
+          "content-length": "405"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3831"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=71853"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:37:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 21:26:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1246"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=70966"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:22:15 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "37497"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=72227"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:43:16 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9877"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=70946"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "424"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=71023"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:23:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "268"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=67682"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:27:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "873"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=70936"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:45 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:11:33 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "368"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=71023"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:23:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3481"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=71854"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:37:03 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "550"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=70868"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:20:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=51842"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:03:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "content-length": "9458"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2328"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=51833"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:03:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 21:27:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2619"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=51842"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:03:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "129"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=51812"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:03:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 21:27:49 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "35"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=42508"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 01:27:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "68"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=69239"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:53:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=71028"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:23:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1147"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=66888"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:14:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:29 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 01 Mar 2011 22:54:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2078"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=251463"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 11:30:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 03 Mar 2011 19:32:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "593"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=251463"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 11:30:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 03 Mar 2011 19:32:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1062"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=251463"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 11:30:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 03 Mar 2011 19:32:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "830"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=251463"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 11:30:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "46"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=65132"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:45:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 03 Mar 2011 19:32:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "319"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=251463"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 11:30:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 25 Mar 2011 19:37:14 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "68"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=1148040"
+        },
+        {
+          "expires": "Fri, 16 Nov 2012 20:33:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MSC2NhKvY9wzDXrmvxADeHz0Rn194VZRXdeFz9JchiAJYOVLIKocjgsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "ALT_ID=007f010046a161bb587e0031; Expires=Sun, 03 Nov 2013 13:39:30 GMT; Path=/; Domain=.nytimes.com;"
+        },
+        {
+          "ntcoent-length": "89"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "99"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MSC2NhKvY9wzDXrmvxADeHz0Rn194VZRXdeFz9JchiAJYOVLIKocjgsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "156"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "ALT_ID=007f010046a161bb587e0031; Expires=Sun, 03 Nov 2013 13:39:30 GMT; Path=/; Domain=.nytimes.com;"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "522"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MSC2NhKvY9wzDXrmvxADeHz0Rn194VZRXdeFz9JchiAJYOVLIKocjgsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "5623"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 22:51:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5799"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=351376"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 15:15:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 10:59:57 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9147"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=336431"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 11:06:41 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 17:13:57 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9389"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=445021"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 17:16:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 11 Oct 2012 22:48:20 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5801"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=349648"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:46:58 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 04:10:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "9726"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=402546"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 05:28:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 02:47:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8968"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568247"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:30:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "2160"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "384"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=66991"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:16:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3710"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=71770"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:35:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1435"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=72025"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:39:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "193"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=66872"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:14:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "11292"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=67441"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:23:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1968"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=70895"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:05 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3582991558377661871; Domain=.p-td.com; Expires=Thu, 02-May-2013 13:39:30 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "645"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=67389"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:22:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "6174"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=63855"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:23:45 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MSC2NhKvY9wzDXrmvxADeHz0Rn194VZRXdeFz9JchiAJYOVLIKocjgsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1008"
+        },
+        {
+          "last-modified": "Thu, 05 Jul 2012 20:14:20 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=86400, private"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "nncoection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "ALT_ID=007f010046a161bb587e0031; Expires=Sun, 03 Nov 2013 13:39:30 GMT; Path=/; Domain=.nytimes.com;"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "max-age=86400, private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "451"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "303"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "location": "/dcsypfq3j00000gsclwfljaeo_6i3w/dcs.gif?dcsredirect=112&dcstlh=0&dcstlv=0&dcsdat=1351949970618&dcssip=www.nytimes.com&dcsuri=/2012/11/03/us/pennsylvania-omitted-poison-data-in-water-report.html&dcsqry=%3Fhp%26_r=0&dcsref=http://www.nytimes.com/&WT.co_f=130.129.68.73-2680109824.30259656&WT.vt_sid=130.129.68.73-2680109824.30259656.1351949959620&WT.tz=-4&WT.bh=9&WT.ul=en-US&WT.cd=24&WT.sr=1366x768&WT.jo=Yes&WT.ti=Pennsylvania%20Omitted%20Poison%20Data%20in%20Water%20Report%20-%20NYTimes.com&WT.js=Yes&WT.jv=1.7&WT.ct=unknown&WT.bs=994x649&WT.fi=No&WT.tv=1.0.7&WT.dl=0&WT.es=www.nytimes.com/2012/11/03/us/pennsylvania-omitted-poison-data-in-water-report.html&WT.z_cad=1&WT.z_fbc=0&WT.cg_n=U.S.&WT.z_rcgn=U.S.&WT.z_gpt=Article&WT.z_gpst=News&WT.cre=The%20New%20York%20Times&WT.z_nyts=0MSC2NhKvY9wzDXrmvxADeHz0Rn194VZRXdeFz9JchiAJYOVLIKocjgsV.Ynx4rkFI&WT.z_nytd=&WT.z_rmid=007f010022166047bee9002b&WT.z_ref=nytimes.com&WT.rv=0&WT.z_hdl=Pennsylvania%20Omitted%20Poison%20Data%20in%20Water%20Report&WT.z_aid=nyta-100000001882561&WT.z_pud=20121102&WT.z_put=Archive&WT.z.gsg=Archive&WT.z_gat=1987-Present&WT.z_pua=free&WT.z_clmst=JON%20HURDLE&WT.z_puv=Normal&WT.z_pudr=1%20Day&WT.z_pyr=2012&WT.mc_ev=&WT.vt_f_tlv=&WT.vt_f_tlh=1351949969&WT.vt_f_d=&WT.vt_f_s=&WT.vt_f_a=&WT.vt_f="
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "set-cookie": "ACOOKIE=C8ctADEzMC4xMjkuNjguNzMtMjY4MDEwOTgyNC4zMDI1OTY1NgAAAAAAAAACAAAAmLwAAJEelVCHHpVQ9r0AAJIelVCSHpVQAQAAAHhHAACSHpVQhx6VUAAAAAA-; path=/; expires=Thu, 10-Dec-2015 10:27:34 GMT"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM NAV INT STA\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLs3719rcF5/JLq6ryuLbPLJXeyC9jZZv+I7SU4qR++R9DDoNb/ysNCSXuwJ979WKhzt9zeOAxNfu6nTWtMntzlCge0zMN6tn5CjIsSTK5oUfaLnJaYMq4VYwS5vxNRs6EHC1nNdLw29SQ2GQ1OzYMBqy0e3FBBr8pVvZLpT4SSw1y6vIpitypkpC3SUacb3M5M=; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:39:30 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas09-2"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MSC2NhKvY9wzDXrmvxADeHz0Rn194VZRXdeFz9JchiAJYOVLIKocjgsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "684"
+        },
+        {
+          "last-modified": "Thu, 05 Jul 2012 19:11:21 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=86400, private"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "set-cookie": "ACOOKIE=C8ctADEzMC4xMjkuNjguNzMtMjY4MDEwOTgyNC4zMDI1OTY1NgAAAAAAAAACAAAAmLwAAJEelVCHHpVQ9r0AAJIelVCSHpVQAQAAAHhHAACSHpVQhx6VUAAAAAA-; path=/; expires=Mon, 03-Nov-2014 13:39:30 GMT"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM NAV INT STA\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "67"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 27 Aug 2012 21:22:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "430"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=67493"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:24:23 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 14 Mar 2012 16:16:16 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2791"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=547136"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 21:38:26 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 21:27:48 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1103"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=43733"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 01:48:23 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1330"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=72392"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:46:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1261"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=72392"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:46:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1687"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=67920"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:31:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "74"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=67981"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:32:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 17 Jul 2012 16:21:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1029"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=72522"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:48:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 17 Jul 2012 16:21:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "108"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=70940"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:50 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "73"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=67925"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:31:35 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "ALT_ID=007f010046a161bb587e0031; Expires=Sun, 03 Nov 2013 13:39:30 GMT; Path=/; Domain=.nytimes.com;"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "max-age=604800, private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1211"
+        },
+        {
+          "last-modified": "Wed, 14 Mar 2012 16:16:16 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MSC2NhKvY9wzDXrmvxADeHz0Rn194VZRXdeFz9JchiAJYOVLIKocjgsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "684"
+        },
+        {
+          "last-modified": "Thu, 05 Jul 2012 19:11:21 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=86400, private"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 20:42:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10779"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=66917"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:14:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 12:45:53 GMT"
+        },
+        {
+          "server": "safe"
+        },
+        {
+          "cache-control": "public, max-age=3600"
+        },
+        {
+          "content-length": "141"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-type": "application/ocsp-response"
+        },
+        {
+          "content-length": "1330"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:30 GMT"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "access-control-allow-origin": "*"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:22 GMT"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "x-fb-debug": "viBL2OtZRmc+1Eqe26sXit4heGPBgfLwrdCHHhHx73Y="
+        },
+        {
+          "content-length": "1916"
+        },
+        {
+          "cache-control": "public, max-age=25750987"
+        },
+        {
+          "expires": "Wed, 28 Aug 2013 14:42:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:31 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "cache-control": "private, no-cache, no-store"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:31 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.krxd.net/kruxcontent/p3p.xml\", CP=\"NON DSP COR NID OUR DEL SAM OTR UNR COM NAV INT DEM CNT STA PRE LOC OTC\""
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "_kuid_=IAJ5QtWI; path=/; expires=Thu, 02-May-13 13:39:31 GMT; domain=.krxd.net"
+        },
+        {
+          "x-request-time": "D=263 t=1351949971765301"
+        },
+        {
+          "x-served-by": "beacon-a006.krxd.net"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=1800"
+        },
+        {
+          "content-type": "text/javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:31 GMT"
+        },
+        {
+          "etag": "\"ad69b1e55307637e8f511c3651fe4a796330656f\""
+        },
+        {
+          "x-age": "0"
+        },
+        {
+          "x-cache": "MISS"
+        },
+        {
+          "x-cache-hits": "0"
+        },
+        {
+          "x-kuid": "IAJ5QtWI"
+        },
+        {
+          "x-request-backend": "user_data"
+        },
+        {
+          "x-served-by": "apiservices-a002.krxd.net"
+        },
+        {
+          "content-length": "117"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:31 GMT"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "set-cookie": "NYT-S=0MSC2NhKvY9wzDXrmvxADeHz0Rn194VZRXdeFz9JchiAJYOVLIKocjgsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "684"
+        },
+        {
+          "last-modified": "Thu, 05 Jul 2012 19:11:21 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=86400, private"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-length": "1406"
+        },
+        {
+          "last-modified": "Fri, 03 Aug 2012 23:51:15 GMT"
+        },
+        {
+          "etag": "\"57e-501c63f3\""
+        },
+        {
+          "accept-ranges": "bytes"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 11:36:59 GMT"
+        },
+        {
+          "etag": "\"30-4cd95ab8fecc0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-host": "b205 D=2031"
+        },
+        {
+          "keep-alive": "timeout=120, max=972"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:31 GMT"
+        },
+        {
+          "age": "161    "
+        },
+        {
+          "content-length": "48"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:30:44 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5873"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=419"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:46:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:31 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 15 Jul 2010 17:33:38 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "583"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=251500"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 11:31:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:32 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2007 19:58:55 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "79"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=251499"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 11:31:11 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:32 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "etag": "\"d04742eb975012ec5e5aecce3effd7ce:1350417145\""
+        },
+        {
+          "last-modified": "Tue, 25 Sep 2012 18:41:40 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:31 GMT"
+        },
+        {
+          "content-length": "3167"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/0.7.67"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:32 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "235"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=50212"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 03:36:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:12:26 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "402"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=70900"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:20 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1108"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=45394"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 02:16:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "content-type": "image/x-icon"
+        },
+        {
+          "set-cookie": "NYT-S=0MuwkWjSilDpbDXrmvxADeHGJBFC9b9qDRdeFz9JchiAKuaKkdl/6loIV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "684"
+        },
+        {
+          "last-modified": "Thu, 05 Jul 2012 19:11:21 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=86400, private"
+        },
+        {
+          "vary": "Host"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-length": "1406"
+        },
+        {
+          "last-modified": "Fri, 03 Aug 2012 23:51:15 GMT"
+        },
+        {
+          "etag": "\"57e-501c63f3\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "ntcoent-length": "142315"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:20:34 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2017"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64909"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:41:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 05 Jul 2012 21:20:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1687"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=43"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:40:23 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 13:51:54 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9372"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=519781"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 14:02:41 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 18:59:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "21158"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=451415"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 19:03:15 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 02:58:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9420"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=480982"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 03:16:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 24 May 2012 19:30:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1217"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=31"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:40:11 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "p3p": "CP=\"IDC DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/0.7.59"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 17 May 2012 20:43:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "586"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=57"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:40:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 05 Jul 2012 21:20:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "17724"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=44"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:40:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "586"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=70933"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:24 GMT"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "set-cookie": "ALT_ID=007f010046a161bb587e0031; Expires=Sun, 03 Nov 2013 13:39:30 GMT; Path=/; Domain=.nytimes.com;"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "max-age=604800, private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1211"
+        },
+        {
+          "last-modified": "Wed, 14 Mar 2012 16:16:16 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MlRvbBBJvPrbDXrmvxADeHGBFC0o.wGyedeFz9JchiAKuaKkdl/6loIV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "593"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=72068"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:40:48 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "380"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=76028"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:46:48 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1435"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=67567"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:25:47 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 10:03:09 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "6703"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568282"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:31:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 15:35:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10892"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 00:21:48 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "9029"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 16:05:06 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10524"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568298"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:31:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2045"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=71054"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:23:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "416"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=71053"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:23:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 19:05:57 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "31084"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "402"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=71054"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:23:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Fri, 20 May 2011 16:30:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "9829"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:14:44 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "526"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=50213"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 14 May 2010 14:49:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "67"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=217288"
+        },
+        {
+          "expires": "Tue, 06 Nov 2012 02:01:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 21 Apr 2010 19:31:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "507"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=307895"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 03:11:15 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 21 Apr 2010 19:31:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1578"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=358516"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 17:14:56 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 03 Mar 2010 22:58:31 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1927"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=601439"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 12:43:39 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 19:39:26 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "10726"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568482"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:34:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 23 Oct 2012 05:20:00 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9316"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 28 Dec 2006 14:20:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "183"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=349196"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:39:36 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 04:24:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "13748"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 10:40:02 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8817"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Fri, 14 Mar 2008 23:03:40 GMT"
+        },
+        {
+          "etag": "\"19f-4486dae50ab00\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-host": "b203 D=1778"
+        },
+        {
+          "keep-alive": "timeout=120, max=997"
+        },
+        {
+          "content-type": "application/javascript"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "age": "43     "
+        },
+        {
+          "content-length": "415"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/1.0.0"
+        },
+        {
+          "set-cookie": "nyt-m=65755B60FD3DAC5F62160A7CED54655D&e=i.1354338000&t=i.10&v=i.1&l=l.25.2802408197.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1&n=i.2&g=i.0&er=i.1351949956&vr=l.4.1.0.0.0&pr=l.4.3.0.0.0&vp=i.0&gf=l.10.2802408197.-1.-1.-1.-1.-1.-1.-1.-1.-1; expires=Thu, 02-Nov-2017 13:39:40 GMT; path=/; domain=.nytimes.com"
+        },
+        {
+          "content-length": "114"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 19:28:25 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8578"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 29 Oct 2012 18:53:28 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "40163"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 00:59:01 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "31146"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=582958"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 07:35:38 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Mon, 22 Oct 2012 17:24:54 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "16403"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 27 Oct 2012 18:43:40 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "11670"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=308055"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 03:13:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 28 Apr 2010 14:36:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "987"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=568614"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 28 Apr 2010 14:36:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2533"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=568614"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 19 Sep 2012 16:22:54 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "14479"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568614"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 09 Apr 2012 21:40:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "14215"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568614"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Thu, 11 Dec 2008 21:32:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "2981"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=568614"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 17:09:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "13040"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=43242"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 01:40:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 28 Apr 2009 18:18:22 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "441"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=43242"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 01:40:22 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 21:41:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "14372"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=54908"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:54:48 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 01 Apr 2009 23:28:14 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1305"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=30976"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 22:15:56 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 20 Mar 2012 16:46:14 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "16641"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=76250"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:50:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "public, max-age=600"
+        },
+        {
+          "content-type": "text/javascript;charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:40 GMT"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:32:42 GMT"
+        },
+        {
+          "server": "ECS (lhr/4BB6)"
+        },
+        {
+          "status": "200 OK"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "9160"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "64"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=70518"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:14:59 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:15:29 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2092"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private, max-age=86400"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 23 Sep 2011 19:12:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1453"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=47"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:40:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "119"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=65032"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:43:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1261"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=65032"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:43:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "188"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=70571"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:15:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:14:45 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "74"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=70936"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:21:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 28 Aug 2012 21:17:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "880"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64971"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4164"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=76141"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:48:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "230"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64971"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2404"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64971"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "5136"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64867"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:40:48 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "29137"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64954"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:15 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5752"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64971"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "723"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=76141"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:48:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "ALT_ID=007f010046a161bb587e0031; Expires=Sun, 03 Nov 2013 13:39:30 GMT; Path=/; Domain=.nytimes.com;"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "586"
+        },
+        {
+          "last-modified": "Thu, 06 Dec 2007 12:15:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "vary": "Host"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MlRvbBBJvPrbDXrmvxADeHGBFC0o.wGyedeFz9JchiAKuaKkdl/6loIV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "etag": "\"14f8931-69a-4409d16c699c0\""
+        },
+        {
+          "cteonnt-length": "1690"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 14 May 2010 14:50:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "170"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=348768"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:32:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 14 May 2010 14:50:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "106"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=348768"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:32:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Fri, 14 May 2010 14:50:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "3113"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=348768"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:32:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2303"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=64971"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "16036"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64972"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 31 Aug 2010 02:21:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "783"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=64970"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 17 Jan 2011 01:28:46 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1930"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=86400"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 24 Jun 2009 19:31:51 GMT"
+        },
+        {
+          "etag": "\"1506ccd-181-46d1d28b0d7c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cteonnt-length": "385"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "233"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1875"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1313"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64974"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:35 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "956"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=65049"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:43:50 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 24 Apr 2012 15:30:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "7711"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568612"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 23 Jun 2011 19:37:35 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8232"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568612"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Tue, 28 Feb 2012 20:00:47 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "8082"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568612"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 23 Jun 2011 20:05:40 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8047"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568612"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 16 Mar 2012 15:04:36 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8295"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568612"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLs37yNvMF5jo9YXjSnP7LCTHV9qwwoXglYNnSgoXY6BgbxUe/dD4KFNMYHqMNEyxe2rbNMTaq4ZLc54Mwg8CSJxvNnh/ajkRBOJfEPCwjrLmcL1OEvcVlVd6ZL/LHB0ixoNHfgWDborMHCUfZtXPvcBFlRNv7qTCM+wn7NY4LUipF+V+epFmBpnIArrjDPO; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:39:41 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas12-2"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 29 Mar 2011 21:16:30 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8374"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568612"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "303"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "location": "/dcsaon9rw0000008ifmgqtaeo_2f9c/dcs.gif?dcsredirect=112&dcstlh=0&dcstlv=0&dcsdat=1351949981735&dcssip=www.nytimes.com&dcsuri=/pages/science/index.html&dcsref=http://www.nytimes.com/2012/11/03/us/pennsylvania-omitted-poison-data-in-water-report.html%3Fhp%26_r=0&WT.co_f=130.129.68.73-2680109824.30259656&WT.vt_sid=130.129.68.73-2680109824.30259656.1351949959620&WT.tz=-4&WT.bh=9&WT.ul=en-US&WT.cd=24&WT.sr=1366x768&WT.jo=Yes&WT.ti=Science%20News%20-%20The%20New%20York%20Times&WT.js=Yes&WT.jv=1.7&WT.ct=unknown&WT.bs=994x649&WT.fi=No&WT.tv=1.0.7&WT.dl=0&WT.es=www.nytimes.com/pages/science/index.html&WT.cg_n=Science&WT.z_rcgn=Science&WT.z_gpt=Section%20Front&WT.z_nyts=0MlRvbBBJvPrbDXrmvxADeHGBFC0o.wGyedeFz9JchiAKuaKkdl/6loIV.Ynx4rkFI&WT.z_nytd=&WT.z_rmid=007f010022166047bee9002b&WT.z_ref=nytimes.com&WT.rv=0&WT.mc_ev=&WT.vt_f_tlv=&WT.vt_f_tlh=1351949981&WT.vt_f_d=&WT.vt_f_s=&WT.vt_f_a=&WT.vt_f="
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "set-cookie": "ACOOKIE=C8ctADEzMC4xMjkuNjguNzMtMjY4MDEwOTgyNC4zMDI1OTY1NgAAAAAAAAADAAAAmLwAAJEelVCHHpVQ9r0AAJIelVCSHpVQ+r0AAJ0elVCdHpVQAQAAAHhHAACdHpVQhx6VUAAAAAA-; path=/; expires=Thu, 10-Dec-2015 10:27:34 GMT"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM NAV INT STA\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 08 Sep 2011 15:52:38 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8274"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568612"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 29 Mar 2011 21:21:57 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8001"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568612"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Mon, 04 Apr 2011 17:44:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "8310"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568612"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 13 Mar 2012 15:48:45 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8162"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 29 Mar 2011 19:24:18 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8410"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 29 Mar 2011 19:26:27 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8253"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 30 Mar 2011 20:20:51 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8372"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 29 Mar 2011 19:25:21 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8030"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 24 Jun 2009 19:31:51 GMT"
+        },
+        {
+          "etag": "\"1506ccd-181-46d1d28b0d7c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cteonnt-length": "385"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "74"
+        },
+        {
+          "ntcoent-length": "62"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Fri, 09 Mar 2012 18:41:14 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "8093"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 29 Mar 2011 21:31:02 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8036"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 14 Mar 2012 19:42:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8124"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 29 Mar 2011 21:33:45 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8207"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 29 Mar 2011 21:36:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8363"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 29 Mar 2011 21:41:44 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8151"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568613"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:36:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "set-cookie": "ACOOKIE=C8ctADEzMC4xMjkuNjguNzMtMjY4MDEwOTgyNC4zMDI1OTY1NgAAAAAAAAADAAAAmLwAAJEelVCHHpVQ9r0AAJIelVCSHpVQ+r0AAJ0elVCdHpVQAQAAAHhHAACdHpVQhx6VUAAAAAA-; path=/; expires=Mon, 03-Nov-2014 13:39:41 GMT"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM NAV INT STA\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "67"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 17 Mar 2010 16:56:32 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "20"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private, max-age=64931"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:41:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "last-modified": "Tue, 31 Jul 2012 23:02:57 GMT"
+        },
+        {
+          "server": "ECS (lhr/4BF1)"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "35"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "public, max-age=604800"
+        },
+        {
+          "content-type": "text/css;charset=utf-8"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:41 GMT"
+        },
+        {
+          "last-modified": "Sun, 28 Oct 2012 04:22:00 GMT"
+        },
+        {
+          "server": "ECS (lhr/4BB5)"
+        },
+        {
+          "status": "200 OK"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "136727"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "cache-control": "private, no-cache, no-store"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:43 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.krxd.net/kruxcontent/p3p.xml\", CP=\"NON DSP COR NID OUR DEL SAM OTR UNR COM NAV INT DEM CNT STA PRE LOC OTC\""
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "_kuid_=IAJ5QtWI; path=/; expires=Thu, 02-May-13 13:39:43 GMT; domain=.krxd.net"
+        },
+        {
+          "x-request-time": "D=245 t=1351949983602996"
+        },
+        {
+          "x-served-by": "beacon-a006.krxd.net"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/0.7.67"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:43 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 21 Aug 2012 20:06:02 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1780"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=72048"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:40:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "511"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=72077"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:41:02 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 16:29:48 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "6000"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=571763"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:29:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 16:26:29 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "6454"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=571763"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:29:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:45 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "156"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "NYT-S=0Ma.K9EWB.dlLDXrmvxADeHD./oTk5S0O8deFz9JchiAImJkOx2rgxzsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "vary": "Host"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "ntcoent-length": "64765"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 17:02:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8653"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=571763"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:29:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:54:29 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "21379"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=71749"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:35:34 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 14:47:21 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "15456"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=571763"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:29:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 14:29:59 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "49747"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=571763"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:29:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:45 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 20:15:59 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3168"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=72146"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:42:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 14 Jun 2012 20:14:49 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10547"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=49875"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 03:31:01 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "p3p": "CP=\"IDC DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/0.7.59"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "303"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:45 GMT"
+        },
+        {
+          "location": "http://d1aivi5dp2wry5.cloudfront.net/pixel.gif"
+        },
+        {
+          "p3p": "CP=\"NOI PSAo OUR IND COM NAV STA\""
+        },
+        {
+          "server": "Microsoft-IIS/7.0"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/1.0.0"
+        },
+        {
+          "set-cookie": "nyt-m=39895E64FA29A782E08DBEA5DC0005A0&e=i.1354338000&t=i.10&v=i.2&l=l.25.2802408197.2634689326.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1&n=i.2&g=i.0&er=i.1351949956&vr=l.4.2.0.0.0&pr=l.4.4.0.0.0&vp=i.0&gf=l.10.2802408197.2634689326.-1.-1.-1.-1.-1.-1.-1.-1; expires=Thu, 02-Nov-2017 13:39:46 GMT; path=/; domain=.nytimes.com"
+        },
+        {
+          "content-length": "113"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "NYT-S=0MKyyIqtYtFvnDXrmvxADeHJm9OXN6YxpedeFz9JchiAIrvq48TSAR4YV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "ntcoent-length": "50"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "67"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "NYT-S=0MKyyIqtYtFvnDXrmvxADeHJm9OXN6YxpedeFz9JchiAIrvq48TSAR4YV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cteonnt-length": "45"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "59"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "304"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-amz-id-2": "YvVdaXFdQYBoLiHhS/Pvn3QQnQ4PguJp3trhCHZSnIIo5NT7S98Tdyovty62vHSG"
+        },
+        {
+          "x-amz-request-id": "CD0C61042E9125AE"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "cache-control": "max-age=600000"
+        },
+        {
+          "last-modified": "Wed, 28 Sep 2011 20:00:18 GMT"
+        },
+        {
+          "etag": "\"df3e567d6f16d040326c7a0ea29a4f41\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "server": "AmazonS3"
+        },
+        {
+          "age": "64633"
+        },
+        {
+          "x-amz-cf-id": "kbjTp1EH_MixUnZ7pqHXKi1pQZ7tCItHqSP2iVMrIwMt36MEvhC5bQ=="
+        },
+        {
+          "via": "1.0 2cfcdac9b945cce88c21cc3f30d884cd.cloudfront.net (CloudFront)"
+        },
+        {
+          "x-cache": "Hit from cloudfront"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "993"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "NYT-S=0MKyyIqtYtFvnDXrmvxADeHJm9OXN6YxpedeFz9JchiAIrvq48TSAR4YV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "vary": "Host"
+        },
+        {
+          "cteonnt-length": "1220"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "767"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "156"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "522"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "59"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "NYT-S=0MrbR7PgLIdPLDXrmvxADeHJm9OXN6YxpedeFz9JchiAIa2oeaXI7u7sV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cteonnt-length": "45"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5623"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1071"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3582991558377661871; Domain=.p-td.com; Expires=Thu, 02-May-2013 13:39:46 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:45 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLs3MVVvMC5/TPbCQtB9rewZ4ac3sWtlL/To9pTTnVflQ7/pHvfl9TutghgWq8BWX5hkGMxwV0G2TYPnu7UrqddmXeJqDHsu7UtpznAhQDBIXp76SiJpTi8Xt/SyOHvyVjspIxogIORYGOkXT50L77u7Afv+N5dTX/mGL4zVQJ5xgVd7PhAxSnfU7wMqMA10uXsSVCgB; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:39:46 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas14-2"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "67"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=75225"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:33:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "set-cookie": "ACOOKIE=C8ctADEzMC4xMjkuNjguNzMtMjY4MDEwOTgyNC4zMDI1OTY1NgAAAAAAAAADAAAAmLwAAJEelVCHHpVQ9r0AAKIelVCSHpVQ+r0AAJ0elVCdHpVQAQAAAHhHAACiHpVQhx6VUAAAAAA-; path=/; expires=Mon, 03-Nov-2014 13:39:46 GMT"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM NAV INT STA\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "67"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:46 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "421"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "NYT-S=0MCYDgCh5sLPnDXrmvxADeHJm9OXN6YxpedeFz9JchiAI32QSUxrnkuIV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cteonnt-length": "611"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:47 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "216"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:47 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "677"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1270"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=65537"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:52:04 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:47 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 01 Oct 2012 22:20:20 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "850"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=82550"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 12:35:37 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:47 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2007 19:58:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "78"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=343858"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 13:10:45 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:47 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:47 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1625"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 25 Jun 2012 18:51:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "193"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=348897"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:34:44 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:47 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "cache-control": "private, no-cache, no-store"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:47 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.krxd.net/kruxcontent/p3p.xml\", CP=\"NON DSP COR NID OUR DEL SAM OTR UNR COM NAV INT DEM CNT STA PRE LOC OTC\""
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "_kuid_=IAJ5QtWI; path=/; expires=Thu, 02-May-13 13:39:47 GMT; domain=.krxd.net"
+        },
+        {
+          "x-request-time": "D=261 t=1351949987674149"
+        },
+        {
+          "x-served-by": "beacon-a002.krxd.net"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/0.7.67"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:47 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 25 Jun 2012 18:51:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "195"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=348898"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:34:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:48 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:48 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1625"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 17 Nov 2011 21:55:54 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1439"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=348897"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:34:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 27 Sep 2011 18:42:47 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "120"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=348920"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:35:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 17 Nov 2010 22:08:39 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "79"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=348893"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:34:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 17 Nov 2010 22:08:39 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "444"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=348893"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:34:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "314"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=69564"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:59:13 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:49 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "x-amz-id-2": "ceFRS1NFRp8F1PuWGjAzR4CMD8nHMNrIGG1K803RtQWtRwA6ZZIyFDi3hvyD0rEU"
+        },
+        {
+          "x-amz-request-id": "1F4B6B1CFD329F7B"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:50 GMT"
+        },
+        {
+          "last-modified": "Mon, 20 Aug 2012 08:07:13 GMT"
+        },
+        {
+          "etag": "\"327b3e51a98ec9a7794030292d94bfdd\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "content-length": "2329"
+        },
+        {
+          "server": "AmazonS3"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 11 Nov 2011 21:24:38 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "992"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=348902"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:34:52 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:50 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:52 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1625"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MPfF5c8nIM93DXrmvxADeHz.PZL72gaixdeFz9JchiAI32QSUxrnkuIV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 20:18:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4913"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=75470"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:37:42 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:52 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 25 Oct 2012 15:40:30 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9921"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=62694"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:04:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:52 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:52 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "p3p": "CP=\"IDC DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/0.7.59"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:52 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "59"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MVMayrSvblfnDXrmvxADeHz.PZL72gaixdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "45"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:38:56 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "216"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MrRmN0I2VxMLDXrmvxADeHx2AlW/TY.ZkdeFz9JchiAI32QSUxrnkuIV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:52 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/1.0.0"
+        },
+        {
+          "set-cookie": "nyt-m=484D5CE2EC7396EB483BD34967CEFAEA&e=i.1354338000&t=i.10&v=i.2&l=l.25.2802408197.2634689326.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1&n=i.2&g=i.0&er=i.1351949956&vr=l.4.2.0.0.0&pr=l.4.5.0.0.0&vp=i.0&gf=l.10.2802408197.2634689326.-1.-1.-1.-1.-1.-1.-1.-1; expires=Thu, 02-Nov-2017 13:39:52 GMT; path=/; domain=.nytimes.com"
+        },
+        {
+          "content-length": "113"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1064"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 20:48:26 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "cache-control": "public, max-age=0"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "175"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "216"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0M3MAhi1As7rzDXrmvxADeHIS37KQvQCBqdeFz9JchiAI32QSUxrnkuIV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "522"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0M3MAhi1As7rzDXrmvxADeHIS37KQvQCBqdeFz9JchiAI32QSUxrnkuIV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "NYT-S=0MsUPd65dnaE7DXrmvxADeHIS37KQvQCBqdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cteonnt-length": "45"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "59"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "156"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "last-modified": "Mon, 27 Aug 2012 09:47:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "cache-control": "public, max-age=0"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "846"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "5623"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0M3MAhi1As7rzDXrmvxADeHIS37KQvQCBqdeFz9JchiAI32QSUxrnkuIV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3582991558377661871; Domain=.p-td.com; Expires=Thu, 02-May-2013 13:39:53 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1015"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLs38VNrMF5/o7ZYjynLbLK61Fp4LCE4qYA8/Pkh6qaTfl607NSARhRSu/Fzrhux2ZemT7dtNzdkLuaRJQuxoxMjsTLW4MWkvPvMQEm63fgskbXcOyhmIGhAp8smwaMCPqeCAzEoxoL8g46HoNIA8DP6t0XbPL6ADv/liREWAhRB2zl4cdzIiQpdChU6FSzIgUundpLxZgo=; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:39:53 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas07-3"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "5623"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MsUPd65dnaE7DXrmvxADeHIS37KQvQCBqdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "216"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1625"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MsUPd65dnaE7DXrmvxADeHIS37KQvQCBqdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:53 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "674"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "cache-control": "private, no-cache, no-store"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:54 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.krxd.net/kruxcontent/p3p.xml\", CP=\"NON DSP COR NID OUR DEL SAM OTR UNR COM NAV INT DEM CNT STA PRE LOC OTC\""
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "_kuid_=IAJ5QtWI; path=/; expires=Thu, 02-May-13 13:39:54 GMT; domain=.krxd.net"
+        },
+        {
+          "x-request-time": "D=261 t=1351949994087668"
+        },
+        {
+          "x-served-by": "beacon-a006.krxd.net"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/0.7.67"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:54 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:55 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1625"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MsUPd65dnaE7DXrmvxADeHIS37KQvQCBqdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 19 Sep 2012 14:55:07 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9556"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=18600"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 18:49:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1625"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MzziEIMyxqcHDXrmvxADeHFvzcPY0HhncdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "p3p": "CP=\"IDC DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/0.7.59"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/1.0.0"
+        },
+        {
+          "set-cookie": "nyt-m=BB78456D636A55DD29717BF2DF3A713D&e=i.1354338000&t=i.10&v=i.2&l=l.25.2802408197.2634689326.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1&n=i.2&g=i.0&er=i.1351949956&vr=l.4.2.0.0.0&pr=l.4.6.0.0.0&vp=i.0&gf=l.10.2802408197.2634689326.-1.-1.-1.-1.-1.-1.-1.-1; expires=Thu, 02-Nov-2017 13:39:57 GMT; path=/; domain=.nytimes.com"
+        },
+        {
+          "content-length": "113"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1625"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MzziEIMyxqcHDXrmvxADeHFvzcPY0HhncdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "674"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MzziEIMyxqcHDXrmvxADeHFvzcPY0HhncdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1086"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1625"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MzziEIMyxqcHDXrmvxADeHFvzcPY0HhncdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "522"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "156"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MzziEIMyxqcHDXrmvxADeHFvzcPY0HhncdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1625"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MzziEIMyxqcHDXrmvxADeHFvzcPY0HhncdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "5623"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MzziEIMyxqcHDXrmvxADeHFvzcPY0HhncdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "982"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "last-modified": "Thu, 12 Apr 2012 20:48:26 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "cache-control": "public, max-age=0"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "175"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLs38VVrcF5/ZLq6rynLbPIrDNNqLmAVAjAcmlywO48hlDb+EIf12DpSuoFj6R+qKjtBkmg2hwh475ZyhKkLcXnOHD8snyDB8tqBczw0L+1ELClDEhhnAWZEtcBDLaM8gpIzDffofr8PXgb4mdcdsGQEwxlXd7J5w1a+LaHURhUo7KSjHyswVRH8QuXMXjpbXabRMAqNp3YYzXPS12ub; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:39:57 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas11-2"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "421"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MQAEyqJ3kflHDXrmvxADeHFvzcPY0HhncdeFz9JchiAIul1FwAbE3OsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "611"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3582991558377661871; Domain=.p-td.com; Expires=Thu, 02-May-2013 13:39:57 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:58 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "674"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MzziEIMyxqcHDXrmvxADeHFvzcPY0HhncdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:58 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "216"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "last-modified": "Mon, 27 Aug 2012 09:47:24 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:39:57 GMT"
+        },
+        {
+          "cache-control": "public, max-age=0"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "server": "sffe"
+        },
+        {
+          "content-length": "846"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:58 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1625"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MzziEIMyxqcHDXrmvxADeHFvzcPY0HhncdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "cache-control": "private, no-cache, no-store"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:58 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.krxd.net/kruxcontent/p3p.xml\", CP=\"NON DSP COR NID OUR DEL SAM OTR UNR COM NAV INT DEM CNT STA PRE LOC OTC\""
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "_kuid_=IAJ5QtWI; path=/; expires=Thu, 02-May-13 13:39:58 GMT; domain=.krxd.net"
+        },
+        {
+          "x-request-time": "D=258 t=1351949998446073"
+        },
+        {
+          "x-served-by": "beacon-a006.krxd.net"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/0.7.67"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:39:58 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:00 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1625"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MzziEIMyxqcHDXrmvxADeHFvzcPY0HhncdeFz9JchiALEJMkToPY7aYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "1625"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0M0SovqlRxK8PDXrmvxADeHKjc4hKLrMXGdeFz9JchiAI7clrZeQfNdsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "vary": "Host"
+        },
+        {
+          "ntcoent-length": "59049"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "p3p": "CP=\"IDC DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/0.7.59"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "59"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "NYT-S=0MMbUZRWJFzCHDXrmvxADeHKjc4hKLrMXGdeFz9JchiALVSSvxGfo9IYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cteonnt-length": "45"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0M0SovqlRxK8PDXrmvxADeHKjc4hKLrMXGdeFz9JchiAI7clrZeQfNdsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/1.0.0"
+        },
+        {
+          "set-cookie": "nyt-m=A04BB1C6D05156CDD246EFA62A7CC3A3&e=i.1354338000&t=i.10&v=i.2&l=l.25.2802408197.2634689326.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1&n=i.2&g=i.0&er=i.1351949956&vr=l.4.2.0.0.0&pr=l.4.7.0.0.0&vp=i.0&gf=l.10.2802408197.2634689326.-1.-1.-1.-1.-1.-1.-1.-1; expires=Thu, 02-Nov-2017 13:40:03 GMT; path=/; domain=.nytimes.com"
+        },
+        {
+          "content-length": "113"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1065"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0M0SovqlRxK8PDXrmvxADeHKjc4hKLrMXGdeFz9JchiAI7clrZeQfNdsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "vary": "Host"
+        },
+        {
+          "cteonnt-length": "1240"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "777"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "522"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0M0SovqlRxK8PDXrmvxADeHKjc4hKLrMXGdeFz9JchiAI7clrZeQfNdsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "156"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1013"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5623"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLs37lNrMF5/o7ZYjynLbLK6rQEBceRsik1IRXIEfZYJjiQapJzwsXeZjT0U5HMKHV3mXKvvaQ4FLg4p3hY87Lr+QiD9QLBuC5Vhn49p+QQ/emsZO26pJ7Jdh6OeXLf4hds9p5K2fB19Ja95VikymGQrDgRB3gOb5zehMs2tUQJAktvEjgSgsPeIBsEg8ddP/NbMOovVC1aBKj+1; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:40:03 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd3-bgas14-2"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3582991558377661871; Domain=.p-td.com; Expires=Thu, 02-May-2013 13:40:03 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:03 GMT"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5623"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0M0SovqlRxK8PDXrmvxADeHKjc4hKLrMXGdeFz9JchiAI7clrZeQfNdsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:04 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "674"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:04 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0M0SovqlRxK8PDXrmvxADeHKjc4hKLrMXGdeFz9JchiAI7clrZeQfNdsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "216"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:04 GMT"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1628"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0M0SovqlRxK8PDXrmvxADeHKjc4hKLrMXGdeFz9JchiAI7clrZeQfNdsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "cache-control": "private, no-cache, no-store"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:04 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.krxd.net/kruxcontent/p3p.xml\", CP=\"NON DSP COR NID OUR DEL SAM OTR UNR COM NAV INT DEM CNT STA PRE LOC OTC\""
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "_kuid_=IAJ5QtWI; path=/; expires=Thu, 02-May-13 13:40:04 GMT; domain=.krxd.net"
+        },
+        {
+          "x-request-time": "D=274 t=1351950004445628"
+        },
+        {
+          "x-served-by": "beacon-a006.krxd.net"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/0.7.67"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:04 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 23:48:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3699"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=79"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:23 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:04 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:05 GMT"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1628"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0M0SovqlRxK8PDXrmvxADeHKjc4hKLrMXGdeFz9JchiAI7clrZeQfNdsV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:14 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1397"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=55146"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:59:15 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "ntcoent-length": "135910"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1628"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MZmF2WzRIAizDXrmvxADeHI4U72bYMorSdeFz9JchiALVSSvxGfo9IYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "vary": "Host"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 17 Sep 2012 20:00:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1566"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=202"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:43:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 02:21:37 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "9333"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=564467"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 02:27:56 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 17:00:35 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8948"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=530746"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 17:05:55 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1276"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=64897"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:41:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Mon, 17 Sep 2012 20:00:10 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "19737"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=202"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:43:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "248"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64919"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "921"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=20124"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 19:15:33 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 04:10:50 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10427"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=571269"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:21:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "59"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "NYT-S=0Ma.K9EWB.dlLDXrmvxADeHI4U72bYMorSdeFz9JchiALJvjis/thrUYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cteonnt-length": "45"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 02:54:41 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "33089"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573548"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 00:13:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "8950"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=559591"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 01:06:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:18:13 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "431"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/css"
+        },
+        {
+          "cache-control": "private, max-age=64941"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:30 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Mar 2012 17:13:24 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "52128"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=97"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:41:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 06:54:58 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10027"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=580777"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 06:59:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "p3p": "CP=\"IDC DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/0.7.59"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 03:35:34 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9303"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573129"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:52:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 00:50:49 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10045"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=559835"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 01:10:44 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 23:02:10 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "10110"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=552339"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 23:05:48 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 02:40:24 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9118"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=568308"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 03:31:57 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 02 Oct 2007 16:06:30 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "3308"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=573548"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 22:25:29 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "14634"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573549"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 04:22:34 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "14341"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573549"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:50:19 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "16624"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=361320"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 18:02:09 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/1.0.0"
+        },
+        {
+          "set-cookie": "nyt-m=918B6703052398FA5C152AAC782FA51F&e=i.1354338000&t=i.10&v=i.2&l=l.25.2802408197.2634689326.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1&n=i.2&g=i.0&er=i.1351949956&vr=l.4.2.0.0.0&pr=l.4.8.0.0.0&vp=i.0&gf=l.10.2802408197.2634689326.-1.-1.-1.-1.-1.-1.-1.-1; expires=Thu, 02-Nov-2017 13:40:09 GMT; path=/; domain=.nytimes.com"
+        },
+        {
+          "content-length": "114"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 30 Oct 2012 22:51:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "15006"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573549"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 20:07:29 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "51622"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=369068"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 20:11:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 02:55:22 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "19143"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=394445"
+        },
+        {
+          "expires": "Thu, 08 Nov 2012 03:14:14 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 23:23:53 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "9474"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=553714"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 23:28:43 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 04:48:38 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8030"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573439"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:57:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 20 Jun 2012 14:59:39 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "10115"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573549"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 20 Aug 2009 21:16:01 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2105"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=351563"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 15:19:32 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 04:00:27 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "35977"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573549"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Tue, 27 Apr 2010 17:46:57 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "1787"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=349039"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:37:28 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 14 Oct 2011 20:04:24 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "8189"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573550"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:19 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 17 Jun 2011 13:57:26 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2824"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573550"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:19 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 15 Nov 2011 19:33:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "11967"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573550"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:19 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 10 Jun 2011 13:57:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2404"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573152"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:52:41 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 02 Jun 2011 21:47:45 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "17489"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=352377"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 15:33:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Tue, 31 Jul 2012 15:09:43 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "7708"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573550"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:19 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 23 Mar 2007 20:45:48 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "4035"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573550"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:19 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 03 Jun 2010 13:02:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1660"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=352377"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 15:33:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 21 Jun 2012 18:52:41 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "11884"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=75841"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:44:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 31 Oct 2012 21:41:52 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "17667"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=50037"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 03:34:06 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 05 Jul 2012 20:15:36 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2092"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private, max-age=86400"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 13:40:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:10 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 24 Jun 2009 19:31:51 GMT"
+        },
+        {
+          "etag": "\"1506ccd-181-46d1d28b0d7c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cteonnt-length": "385"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "233"
+        },
+        {
+          "ntcoent-length": "62"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 13 Jan 2011 15:17:04 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1870"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=86400"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 13:40:10 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:10 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:10 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Wed, 24 Jun 2009 19:31:51 GMT"
+        },
+        {
+          "etag": "\"1506ccd-181-46d1d28b0d7c0\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cteonnt-length": "385"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "74"
+        },
+        {
+          "ntcoent-length": "62"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:10 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1866"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=604800"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:10 GMT"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 13:40:10 GMT"
+        },
+        {
+          "last-modified": "Tue, 31 Jul 2012 23:02:57 GMT"
+        },
+        {
+          "server": "ECS (lhr/4BF1)"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "35"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Thu, 15 Dec 2011 19:05:45 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=52304"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 04:11:53 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:09 GMT"
+        },
+        {
+          "content-length": "42039"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:14:45 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "66"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=71036"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 09:24:08 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "247"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64954"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:46 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:05 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2289"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64924"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:16 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 04 Aug 2012 21:19:03 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "2443"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=64924"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 07:42:16 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "vary": "Cookie"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:38:24 +0000"
+        },
+        {
+          "cache-control": "max-age=132, must-revalidate"
+        },
+        {
+          "keep-alive": "timeout=60, max=940"
+        },
+        {
+          "connection": "Keep-Alive"
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 18 Mar 2009 18:50:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "188"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=349116"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:38:48 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 18 Mar 2009 18:50:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "106"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=349122"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:38:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 18 Mar 2009 18:50:08 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "131"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=349122"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:38:54 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 09 Sep 2009 16:10:20 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "351"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=349157"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:39:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Wed, 09 Sep 2009 16:10:20 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "224"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "cache-control": "private, max-age=349157"
+        },
+        {
+          "expires": "Wed, 07 Nov 2012 14:39:29 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "872"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLs38FVrcF5/ZLq6rynLbPIrzBMgf6gCZEFYbnEyqJwHBZC0garfrvMOIs+B939ykqVLVck4XSTGQIMvPdVthDcyttnh/a+rfKsxItpUfG2D5pA4KGuH2gGpHenDN3B0M6eEi6BWcidf0I+ZlqmL9bIpDHIKu64kT0YghPKjDoejnlzus5jQeqqP4igBfBMSRX3hgEHkgrccVaMsrutxL45k8Cggfg==; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:40:12 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd4-bgas03-2"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 03:08:29 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "18011"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573549"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "303"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "location": "/dcsj5tb4n100000sl76culaeo_4f3w/dcs.gif?dcsredirect=112&dcstlh=0&dcstlv=0&dcsdat=1351950012922&dcssip=www.nytimes.com&dcsuri=/pages/nyregion/index.html&dcsref=http://www.nytimes.com/2012/11/04/education/edlife/a-new-kind-of-tutoring-aims-to-make-students-smarter.html%3Fpagewanted=4%26ref=science&WT.co_f=130.129.68.73-2680109824.30259656&WT.vt_sid=130.129.68.73-2680109824.30259656.1351949959620&WT.tz=-4&WT.bh=9&WT.ul=en-US&WT.cd=24&WT.sr=1366x768&WT.jo=Yes&WT.ti=New%20York%20Region%20News%20-%20The%20New%20York%20Times&WT.js=Yes&WT.jv=1.7&WT.ct=unknown&WT.bs=994x649&WT.fi=No&WT.tv=1.0.7&WT.dl=0&WT.es=www.nytimes.com/pages/nyregion/index.html&WT.cg_n=N.Y./Region&WT.z_rcgn=N.Y./Region&WT.z_gpt=Section%20Front&WT.z_nyts=0Ma.K9EWB.dlLDXrmvxADeHI4U72bYMorSdeFz9JchiALJvjis/thrUYV.Ynx4rkFI&WT.z_nytd=&WT.z_rmid=007f010022166047bee9002b&WT.z_ref=nytimes.com&WT.rv=0&WT.mc_ev=&WT.vt_f_tlv=&WT.vt_f_tlh=1351950010&WT.vt_f_d=&WT.vt_f_s=&WT.vt_f_a=&WT.vt_f="
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "set-cookie": "ACOOKIE=C8ctADEzMC4xMjkuNjguNzMtMjY4MDEwOTgyNC4zMDI1OTY1NgAAAAAAAAAEAAAAmLwAAJEelVCHHpVQ9r0AALkelVCSHpVQ+r0AAJ0elVCdHpVQ970AALwelVC8HpVQAQAAAHhHAAC8HpVQhx6VUAAAAAA-; path=/; expires=Thu, 10-Dec-2015 10:27:34 GMT"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM NAV INT STA\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 03:06:34 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "25015"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573549"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 03:06:11 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "25206"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=573549"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 04:59:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "304"
+        },
+        {
+          "x-amz-id-2": "d90CCR9lSoaaNwupUMIdb/ey0mevoBrnI3ZRtI8HHFRBEUtsLjtNMBWb2X6DprZz"
+        },
+        {
+          "x-amz-request-id": "D6FC61A73C17CF70"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:14 GMT"
+        },
+        {
+          "cache-control": "max-age=10"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 13:35:29 GMT"
+        },
+        {
+          "etag": "\"8254326a395317db7b197564fa83e476\""
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-type": ""
+        },
+        {
+          "content-length": "92155"
+        },
+        {
+          "server": "AmazonS3"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:13 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "set-cookie": "ACOOKIE=C8ctADEzMC4xMjkuNjguNzMtMjY4MDEwOTgyNC4zMDI1OTY1NgAAAAAAAAAEAAAAmLwAAJEelVCHHpVQ9r0AALkelVCSHpVQ+r0AAJ0elVCdHpVQ970AAL0elVC8HpVQAQAAAHhHAAC9HpVQhx6VUAAAAAA-; path=/; expires=Mon, 03-Nov-2014 13:40:13 GMT"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM NAV INT STA\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "67"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 07:39:41 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=300"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:45:12 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:12 GMT"
+        },
+        {
+          "content-length": "4073"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/png"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:13 GMT"
+        },
+        {
+          "etag": "\"fb5af56cb67780c01fb3516e7c3f74e3\""
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:40:13 GMT"
+        },
+        {
+          "last-modified": "Wed, 04 Apr 2012 20:38:25 GMT"
+        },
+        {
+          "server": "ECS (lhr/4BA4)"
+        },
+        {
+          "x-amz-id-2": "cBUUBPPZMto6skduFezdQ+9bNRCuwVjOvgFgohhb4PfZdlrYG33n3GM2BJ25YTtq"
+        },
+        {
+          "x-amz-request-id": "6758CF2C16F8B0C0"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "1662"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:13 GMT"
+        },
+        {
+          "etag": "\"0940a52ad7b3435f775918b80b088f27\""
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:40:13 GMT"
+        },
+        {
+          "last-modified": "Thu, 30 Sep 2010 12:29:59 GMT"
+        },
+        {
+          "server": "ECS (lhr/4BCC)"
+        },
+        {
+          "x-amz-id-2": "jDnDMjao+nUL4vfffUU3TFPTyN1KxJt9Dy6uCIra3CbwdS9wgqVrVK02Lhf++2Kk"
+        },
+        {
+          "x-amz-request-id": "296BB691837A53B5"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "980"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:13 GMT"
+        },
+        {
+          "etag": "\"7e3ff7f7848a81a63b6e2e0bfb394799\""
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:40:13 GMT"
+        },
+        {
+          "last-modified": "Sun, 12 Jun 2011 15:43:21 GMT"
+        },
+        {
+          "server": "ECS (lhr/4BA1)"
+        },
+        {
+          "x-amz-id-2": "QHLqCpOps7vUVMXN1QzHCNBpV3eM2RaV2CNFSW7QQ5BQSiaY2U04JVbdV76t6uPA"
+        },
+        {
+          "x-amz-request-id": "4E56272125A99862"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "2607"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:13 GMT"
+        },
+        {
+          "etag": "\"3ff974032fd77223fd059c6d234ebb43\""
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:40:13 GMT"
+        },
+        {
+          "last-modified": "Tue, 14 Jun 2011 16:57:49 GMT"
+        },
+        {
+          "server": "ECS (lhr/4BDF)"
+        },
+        {
+          "x-amz-id-2": "q0tabYbFY5+80zmAw4/5X92+LUMhsasmJRWZU8wZxF7S+TYVyMgxcBM47nT2KV2t"
+        },
+        {
+          "x-amz-request-id": "5C17C42AD2E36B99"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "3219"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:13 GMT"
+        },
+        {
+          "etag": "\"ac105110a13a3d24f2b5d502fb0db4e8\""
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:40:13 GMT"
+        },
+        {
+          "last-modified": "Tue, 16 Jun 2009 22:22:00 GMT"
+        },
+        {
+          "server": "ECS (lhr/4BAD)"
+        },
+        {
+          "x-amz-id-2": "AlVWqZvxNaFzFrxq+Lo/jRl878iwJzLMeSmnDalUQWo33DvFg3BUtTZnEF+yRJ5W"
+        },
+        {
+          "x-amz-request-id": "F6C83DE008C7DEFA"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "1673"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "cache-control": "max-age=31536000"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:13 GMT"
+        },
+        {
+          "etag": "\"6c157f6f56910c50a03faa81d54546bd\""
+        },
+        {
+          "expires": "Sun, 03 Nov 2013 13:40:13 GMT"
+        },
+        {
+          "last-modified": "Tue, 06 Sep 2011 20:24:40 GMT"
+        },
+        {
+          "server": "ECS (lhr/4BAC)"
+        },
+        {
+          "x-amz-id-2": "KFCqXFOHRJbWl2rZ7FfvRitLrhcAZnqUIfhUqkDIDZOQB0cgxkSjAlC9Qh0pOvZ3"
+        },
+        {
+          "x-amz-request-id": "8BF259D47606A786"
+        },
+        {
+          "x-cache": "HIT"
+        },
+        {
+          "content-length": "2437"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "cache-control": "private, no-cache, no-store"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:13 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.krxd.net/kruxcontent/p3p.xml\", CP=\"NON DSP COR NID OUR DEL SAM OTR UNR COM NAV INT DEM CNT STA PRE LOC OTC\""
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "_kuid_=IAJ5QtWI; path=/; expires=Thu, 02-May-13 13:40:13 GMT; domain=.krxd.net"
+        },
+        {
+          "x-request-time": "D=271 t=1351950013983899"
+        },
+        {
+          "x-served-by": "beacon-a002.krxd.net"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/0.7.67"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:14 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 19:52:17 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "15613"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=564661"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 02:31:18 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Tue, 03 Apr 2012 15:22:24 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "1955"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=299"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:45:16 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 02:54:40 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "14642"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=579064"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 06:31:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 02 Nov 2012 19:54:59 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "15613"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=547394"
+        },
+        {
+          "expires": "Fri, 09 Nov 2012 21:43:31 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MA4.kVz1KQmzDXrmvxADeHK.rUS.yFrOJdeFz9JchiALJvjis/thrUYV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "216"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/1.0.0"
+        },
+        {
+          "set-cookie": "nyt-m=8FF27B7C7E22BE437F4E72563691B5C2&e=i.1354338000&t=i.10&v=i.3&l=l.25.2802408197.2634689326.1254883451.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1.-1&n=i.2&g=i.0&er=i.1351949956&vr=l.4.3.0.0.0&pr=l.4.9.0.0.0&vp=i.0&gf=l.10.2802408197.2634689326.1254883451.-1.-1.-1.-1.-1.-1.-1; expires=Thu, 02-Nov-2017 13:40:17 GMT; path=/; domain=.nytimes.com"
+        },
+        {
+          "content-length": "113"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "cache-control": "max-age=0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:01 GMT"
+        },
+        {
+          "p3p": "CP=\"IDC DSP COR DEVa TAIa OUR BUS UNI\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "server": "nginx/0.7.59"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "303"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:16 GMT"
+        },
+        {
+          "location": "http://d1aivi5dp2wry5.cloudfront.net/pixel.gif"
+        },
+        {
+          "p3p": "CP=\"NOI PSAo OUR IND COM NAV STA\""
+        },
+        {
+          "server": "Microsoft-IIS/7.0"
+        },
+        {
+          "x-aspnet-version": "4.0.30319"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MP9kmlu11B5nDXrmvxADeHK.rUS.yFrOJdeFz9JchiAKfXJAEEJyUmIV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "59"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "45"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "NYT-S=0MP9kmlu11B5nDXrmvxADeHK.rUS.yFrOJdeFz9JchiAKfXJAEEJyUmIV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "ntcoent-length": "50"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "67"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:54:29 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "7020"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=76680"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 10:58:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:49:48 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "41440"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=579064"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 06:31:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:50:15 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "53346"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=579064"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 06:31:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:48:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "55860"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=579064"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 06:31:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:49:19 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "46030"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=579064"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 06:31:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:51:50 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "25431"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=579064"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 06:31:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:52:12 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "27683"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=579064"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 06:31:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:50:56 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "71084"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=579064"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 06:31:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/html; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1966"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 05:51:23 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-length": "34384"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/jpeg"
+        },
+        {
+          "cache-control": "private, max-age=579064"
+        },
+        {
+          "expires": "Sat, 10 Nov 2012 06:31:21 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:18 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "1024"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:18 GMT"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "521"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:18 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0M6GemS05ruUDDXrmvxADeHAjAqSvifpeXdeFz9JchiAKfXJAEEJyUmIV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:18 GMT"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-length": "156"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:18 GMT"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MMaQ/2qmmFHvDXrmvxADeHAjAqSvifpeXdeFz9JchiAIcUoUNnYFX3YV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cteonnt-length": "45"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "content-length": "59"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:18 GMT"
+        },
+        {
+          "server": "nginx/1.0.10"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5623"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "p3p": "policyref=\"http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml\", CP=\"CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\""
+        },
+        {
+          "content-type": "text/javascript; charset=UTF-8"
+        },
+        {
+          "x-content-type-options": "nosniff"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:18 GMT"
+        },
+        {
+          "server": "cafe"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-length": "979"
+        },
+        {
+          "x-xss-protection": "1; mode=block"
+        },
+        {
+          "x-frame-options": "ALLOWALL"
+        },
+        {
+          "age": "3217"
+        },
+        {
+          "content-disposition": "attachment"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache-Coyote/1.1"
+        },
+        {
+          "p3p": "policyref=\"/w3c/p3p.xml\", CP=\"NOI CURa DEVa TAIa PSAa PSDa IVAa IVDa OUR IND UNI NAV\""
+        },
+        {
+          "cache-control": "max-age=0, no-cache, no-store, private, must-revalidate, s-maxage=0"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "set-cookie": "uid=3582991558377661871; Domain=.p-td.com; Expires=Thu, 02-May-2013 13:40:18 GMT; Path=/"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:18 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "keep-alive": "timeout=20"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "p3p": "policyref=\"http://www.imrworldwide.com/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA ADM OUR IND UNI NAV COM\""
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "set-cookie": "rts_AAAA=MLs3MV9rcF5/e8raSsB8J1a8xoyhfqgGdE1oaHcypJ43DeCuFAmr4KFNP2NyUKmpJDKN5oHAaw4FLg6p/xU87LpGBP1V8Vwc/u8nqIWD9h6mituy2I3ef/nmfXXys59Ro9/kQ77nLnGIV6iKi6h89ib3bNEkAgz8RVtHEqb8hpvCshkDOJUx9+7dGislj1zxURVYlAk37LbXfBKtqErQegJsJj8=; Domain=.revsci.net; Expires=Sun, 03-Nov-2013 13:40:18 GMT; Path=/"
+        },
+        {
+          "x-proc-data": "pd4-bgas09-2"
+        },
+        {
+          "p3p": "policyref=\"http://js.revsci.net/w3c/rsip3p.xml\", CP=\"NON PSA PSD IVA IVD OTP SAM IND UNI PUR COM NAV INT DEM CNT STA PRE OTC HEA\""
+        },
+        {
+          "server": "RSI"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "Thu, 01 Jan 1970 00:00:00 GMT"
+        },
+        {
+          "content-type": "application/javascript;charset=UTF-8"
+        },
+        {
+          "transfer-encoding": "chunked"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "connection": "close"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:18 GMT"
+        },
+        {
+          "server": "Microsoft-IIS/6.0"
+        },
+        {
+          "x-powered-by": "ASP.NET"
+        },
+        {
+          "set-cookie": "ACOOKIE=C8ctADEzMC4xMjkuNjguNzMtMjY4MDEwOTgyNC4zMDI1OTY1NgAAAAAAAAAEAAAAmLwAAJEelVCHHpVQ9r0AALkelVCSHpVQ+r0AAJ0elVCdHpVQ970AAMIelVC8HpVQAQAAAHhHAADCHpVQhx6VUAAAAAA-; path=/; expires=Mon, 03-Nov-2014 13:40:18 GMT"
+        },
+        {
+          "p3p": "CP=\"NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM NAV INT STA\""
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "expires": "-1"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "67"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:18 GMT"
+        },
+        {
+          "server": "Sun-ONE-Web-Server/6.1"
+        },
+        {
+          "ntcoent-length": "1068"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/json"
+        },
+        {
+          "cache-control": "private"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "5623"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "x-powered-by": "PHP/5.2.9"
+        },
+        {
+          "content-type": "text/html"
+        },
+        {
+          "set-cookie": "NYT-S=0MMaQ/2qmmFHvDXrmvxADeHAjAqSvifpeXdeFz9JchiAIcUoUNnYFX3YV.Ynx4rkFI; path=/; domain=.nytimes.com"
+        },
+        {
+          "expires": "Thu, 01 Dec 1994 16:00:00 GMT"
+        },
+        {
+          "cache-control": "no-cache"
+        },
+        {
+          "pragma": "no-cache"
+        },
+        {
+          "transfer-encoding": "chunked"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "last-modified": "Fri, 12 Oct 2012 00:14:49 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "content-length": "13551"
+        },
+        {
+          "cneonction": "close"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "cache-control": "private, max-age=68002"
+        },
+        {
+          "expires": "Sun, 04 Nov 2012 08:33:40 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:18 GMT"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "last-modified": "Sat, 03 Nov 2012 07:39:41 GMT"
+        },
+        {
+          "accept-ranges": "bytes"
+        },
+        {
+          "vary": "Accept-Encoding"
+        },
+        {
+          "content-encoding": "gzip"
+        },
+        {
+          "nncoection": "close"
+        },
+        {
+          "content-type": "application/x-javascript"
+        },
+        {
+          "cache-control": "private, max-age=300"
+        },
+        {
+          "expires": "Sat, 03 Nov 2012 13:45:17 GMT"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:17 GMT"
+        },
+        {
+          "content-length": "4073"
+        },
+        {
+          "connection": "keep-alive"
+        },
+        {
+          "cneonction": "close"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "204"
+        },
+        {
+          "cache-control": "private, no-cache, no-store"
+        },
+        {
+          "content-length": "0"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:19 GMT"
+        },
+        {
+          "p3p": "policyref=\"http://cdn.krxd.net/kruxcontent/p3p.xml\", CP=\"NON DSP COR NID OUR DEL SAM OTR UNR COM NAV INT DEM CNT STA PRE LOC OTC\""
+        },
+        {
+          "server": "Apache"
+        },
+        {
+          "set-cookie": "_kuid_=IAJ5QtWI; path=/; expires=Thu, 02-May-13 13:40:19 GMT; domain=.krxd.net"
+        },
+        {
+          "x-request-time": "D=259 t=1351950019348818"
+        },
+        {
+          "x-served-by": "beacon-a002.krxd.net"
+        },
+        {
+          "connection": "keep-alive"
+        }
+      ]
+    },
+    {
+      "headers": [
+        {
+          ":status": "200"
+        },
+        {
+          "server": "nginx/0.7.67"
+        },
+        {
+          "date": "Sat, 03 Nov 2012 13:40:19 GMT"
+        },
+        {
+          "content-type": "image/gif"
+        },
+        {
+          "content-length": "43"
+        },
+        {
+          "connection": "close"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/data/story_31.json b/jetty-http2/http2-hpack/src/test/resources/data/story_31.json
new file mode 100644
index 0000000..62f43e2
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/data/story_31.json
@@ -0,0 +1,4555 @@
+{
+  "cases": [
+    {
+      "seqno": 0, 
+      "headers": [
+        {
+          "content-length": "522"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "expires": "Tue, 21 May 2013 19:18:33 GMT"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:20 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "public, max-age=17216869"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:44 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/gif"
+        }, 
+        {
+          "x-fb-debug": "IVe/SwucJuBsLtVHWJw2PMdOTOxuEWUir5igQNThkTg="
+        }
+      ]
+    }, 
+    {
+      "seqno": 1, 
+      "headers": [
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Sun, 12 May 2013 06:59:14 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "content-length": "123"
+        }, 
+        {
+          "last-modified": "Tue, 24 Apr 2012 22:13:35 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=16394910"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:44 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "text/css; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "95tUymdadFLd8Dpml8VnOoUG7KhisOwk74Kd/aIGfU0="
+        }
+      ]
+    }, 
+    {
+      "seqno": 2, 
+      "headers": [
+        {
+          "content-length": "14684"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:44:39 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:37:35 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31067635"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:44 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "text/css; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "Qc0GcUiwi3io8aSRIdXaahYr6KKhphvV6NlN8vo/bD4="
+        }
+      ]
+    }, 
+    {
+      "seqno": 3, 
+      "headers": [
+        {
+          "content-length": "14438"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "expires": "Mon, 19 Aug 2013 00:53:24 GMT"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:23 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "public, max-age=24926560"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:44 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "7exUqkoZxtfLseR1zLxJlXnpYK6MOognZuCKx7drdRo="
+        }
+      ]
+    }, 
+    {
+      "seqno": 4, 
+      "headers": [
+        {
+          "content-length": "17475"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 18:31:11 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Mon, 29 Oct 2012 17:08:56 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31124427"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:44 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "eRvyJLXIvW3Vu9d+m439v+LGKqXiLSKmz7w9/xMAUpc="
+        }
+      ]
+    }, 
+    {
+      "seqno": 5, 
+      "headers": [
+        {
+          "content-length": "44191"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:44:39 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 22:55:27 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31067635"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:44 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "text/css; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "14hoEcywNNMBIVUS5B7AD7RDGiDvJ4BGeOVgJbBDzf0="
+        }
+      ]
+    }, 
+    {
+      "seqno": 6, 
+      "headers": [
+        {
+          "content-length": "754"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 16 Oct 2013 14:03:31 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Fri, 12 Oct 2012 18:34:48 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=29985162"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "41/HuGcFNMmys4cvGKlBeylojdVDP4+VBIf1giu3eNQ="
+        }
+      ]
+    }, 
+    {
+      "seqno": 7, 
+      "headers": [
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:40:10 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "content-length": "2349"
+        }, 
+        {
+          "last-modified": "Thu, 25 Oct 2012 16:05:53 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31067361"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "oKQwv0JLYost+zqlv8x+C7MEL7zRBbeMomoc54M5RZY="
+        }
+      ]
+    }, 
+    {
+      "seqno": 8, 
+      "headers": [
+        {
+          "content-length": "2626"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 03:00:19 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:08:50 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31068570"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "zh8tRHKFtERIZ+K/eGiM1utm1H66OnOj1qwPAN7Ck9A="
+        }
+      ]
+    }, 
+    {
+      "seqno": 9, 
+      "headers": [
+        {
+          "content-length": "8036"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:33:09 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Fri, 28 Sep 2012 15:01:14 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31066940"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "pricqIchHztHxKAQSidQiwGmRf62vAL6I7Oi0r/Ki08="
+        }
+      ]
+    }, 
+    {
+      "seqno": 10, 
+      "headers": [
+        {
+          "content-length": "36302"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:58:39 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sat, 27 Oct 2012 21:42:19 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31068470"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "Kur2FUUQjAQ0yPQJC9fvK56/+LWZHvyQF6Ce2Fuaf2k="
+        }
+      ]
+    }, 
+    {
+      "seqno": 11, 
+      "headers": [
+        {
+          "content-length": "8230"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 27 Aug 2013 06:59:08 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:02:51 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=25639699"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "wk2MWysJhw3Et7CRGMA1sE9HWuyzy8oCvtT2V7iPXeg="
+        }
+      ]
+    }, 
+    {
+      "seqno": 12, 
+      "headers": [
+        {
+          "content-length": "4878"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:59:38 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sat, 27 Oct 2012 21:38:44 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31068529"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:49 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "GMzzyj0B89LYf1zKH9hqxZekz5mYTmsuxwLugWyc2Gg="
+        }
+      ]
+    }, 
+    {
+      "seqno": 13, 
+      "headers": [
+        {
+          "content-length": "1814"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "content-transfer-encoding": "binary"
+        }, 
+        {
+          "expires": "Sat, 10 Nov 2012 04:37:48 GMT"
+        }, 
+        {
+          "last-modified": "Sat, 03 Nov 2012 04:37:48 GMT"
+        }, 
+        {
+          "connection": "Keep-Alive"
+        }, 
+        {
+          "cache-control": "max-age=575215, public, no-transform, must-revalidate"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:53 GMT"
+        }, 
+        {
+          "nncoection": "close"
+        }, 
+        {
+          "content-type": "application/ocsp-response"
+        }
+      ]
+    }, 
+    {
+      "seqno": 14, 
+      "headers": [
+        {
+          "content-length": "43"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "x-fb-metrics": "{\"w\":53,\"r\":26,\"q\":0,\"a\":25}"
+        }, 
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        }, 
+        {
+          "x-fb-server": "10.74.89.23"
+        }, 
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "pragma": "no-cache"
+        }, 
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:54 GMT"
+        }, 
+        {
+          "content-type": "image/gif"
+        }, 
+        {
+          "x-fb-debug": "7iLjsQVXsunUKXe3NlV2ytaBGzQ0VHCkMX/J6rEuB6Y="
+        }
+      ]
+    }, 
+    {
+      "seqno": 15, 
+      "headers": [
+        {
+          "content-length": "1155"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:46:08 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 15:06:53 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31067714"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:54 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "text/css; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "ur+THlFHeLotsmDlQWYPw2GRELyvg28JmE0JYVt56uo="
+        }
+      ]
+    }, 
+    {
+      "seqno": 16, 
+      "headers": [
+        {
+          "content-length": "516"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 04 Sep 2013 02:55:58 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Fri, 31 Aug 2012 22:13:28 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=26316304"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:54 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "text/css; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "6/fKHkRtBpT4oqBg33tFHk2pO6SDZlUG11Uq4/AlUIE="
+        }
+      ]
+    }, 
+    {
+      "seqno": 17, 
+      "headers": [
+        {
+          "content-length": "232"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Mon, 21 Oct 2013 14:44:35 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Sat, 21 Apr 2012 07:03:57 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=30419619"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "XtTsONeGHGs/1vRRv8cvNY1ciB3XqlrvnTq2GZXvnqM="
+        }
+      ]
+    }, 
+    {
+      "seqno": 18, 
+      "headers": [
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:33:09 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "content-length": "400"
+        }, 
+        {
+          "last-modified": "Fri, 07 Sep 2012 15:18:40 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31066933"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "E2JBYTapyXFjxTTVqkrekTVKDp1lDQQT/7YxcxfNU2U="
+        }
+      ]
+    }, 
+    {
+      "seqno": 19, 
+      "headers": [
+        {
+          "content-length": "35766"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 03:07:14 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sat, 27 Oct 2012 21:41:45 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31068980"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:54 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "iPbLdjapQzRoatbOUDrN+exDj8EPHJAcsZ48pVtprtA="
+        }
+      ]
+    }, 
+    {
+      "seqno": 20, 
+      "headers": [
+        {
+          "content-length": "10902"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:33:40 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 14:34:50 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31066965"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:55 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "0ci0R5R2ivIVCRrwtG507Eej+LTK8dUL8dIiZp70+dU="
+        }
+      ]
+    }, 
+    {
+      "seqno": 21, 
+      "headers": [
+        {
+          "content-length": "4676"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 08:36:24 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:38:28 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31088728"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "text/css; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "P2Bq0ebVXjtf8GyQp1HHux8NxlftUMXZuY8XF+yaOVo="
+        }
+      ]
+    }, 
+    {
+      "seqno": 22, 
+      "headers": [
+        {
+          "content-length": "20791"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:45:10 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:37:10 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31067654"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "text/css; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "yHzV/c0w3ZyInkRbLCDrA0t5adSMyAG4prBOk+i+t6Y="
+        }
+      ]
+    }, 
+    {
+      "seqno": 23, 
+      "headers": [
+        {
+          "content-length": "11113"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:34:04 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 14:34:47 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31066988"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "u363OvKFmnm717JBUXA5ePB8Ts0ppRI7+eEJwOOep6w="
+        }
+      ]
+    }, 
+    {
+      "seqno": 24, 
+      "headers": [
+        {
+          "content-length": "43"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 28 Aug 2013 14:37:09 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:02:57 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=25753573"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/gif"
+        }, 
+        {
+          "x-fb-debug": "Yty+Te4OzfswtmjzbJmJZaybyM0hxXiRU2NtHEbDuPE="
+        }
+      ]
+    }, 
+    {
+      "seqno": 25, 
+      "headers": [
+        {
+          "content-length": "571"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 28 Aug 2013 14:41:07 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:24 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=25753811"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "yKFyrxwqBiumMPfWvv4morUUsmz9djZtSdmCoQMnchs="
+        }
+      ]
+    }, 
+    {
+      "seqno": 26, 
+      "headers": [
+        {
+          "content-length": "43"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 28 Aug 2013 14:40:26 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:22 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=25753770"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/gif"
+        }, 
+        {
+          "x-fb-debug": "HbryxnP7HNa7kdTChA6BppSjLQw0gz9ZzESCqEH3/9k="
+        }
+      ]
+    }, 
+    {
+      "seqno": 27, 
+      "headers": [
+        {
+          "content-length": "12817"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Thu, 05 Sep 2013 18:18:17 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Tue, 28 Aug 2012 01:20:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=26458041"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:56 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "edgqdu1lFmUW32Et2hHoiAsp9kFIch8QDiciO71cQ4w="
+        }
+      ]
+    }, 
+    {
+      "seqno": 28, 
+      "headers": [
+        {
+          "content-length": "11688"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 03:07:27 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:09:30 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31068990"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:57 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "rXXtxI3fPgNHe6wKIDRBR0xjttUNeG+BDQM8QfKQa+A="
+        }
+      ]
+    }, 
+    {
+      "seqno": 29, 
+      "headers": [
+        {
+          "content-length": "35165"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:59:38 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:06:47 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31068521"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:57 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "fUJ2Nc9qJdBC1AnYFA5Vs1f7ozv+i/PTKO+Vep0A0HQ="
+        }
+      ]
+    }, 
+    {
+      "seqno": 30, 
+      "headers": [
+        {
+          "content-length": "1028"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 30 Oct 2013 13:15:07 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:07:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31191849"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:58 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "s9ZmJKnYGlEjIGo8xQzxlhVM4nilGHgcv1fhK1Z7F1w="
+        }
+      ]
+    }, 
+    {
+      "seqno": 31, 
+      "headers": [
+        {
+          "content-length": "47844"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 03:07:27 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:07:08 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31068990"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:57 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "hMQvA7xhuAspt5fOxedr0fWzQZNLkyizVtlmspzgVG8="
+        }
+      ]
+    }, 
+    {
+      "seqno": 32, 
+      "headers": [
+        {
+          "content-length": "42391"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 23:54:49 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Mon, 29 Oct 2012 19:17:24 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31143832"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:57 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "1EkJKYLfWucaDVhZCEVAAo57HpAH7rvF4r9IwDXM2B8="
+        }
+      ]
+    }, 
+    {
+      "seqno": 33, 
+      "headers": [
+        {
+          "content-length": "13181"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 03:07:27 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:10:32 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31068989"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:58 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "dO6OKUf38jDdtAnTrM28wZTBz9Y5hU/0EJdd1CkWGDs="
+        }
+      ]
+    }, 
+    {
+      "seqno": 34, 
+      "headers": [
+        {
+          "content-length": "23736"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Sat, 02 Nov 2013 20:50:17 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Fri, 02 Nov 2012 19:03:57 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31478360"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:57 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "IlZ4dyc3v7fqrfiamqJbFeFTWRkUvEUs2L8KLXNa+5o="
+        }
+      ]
+    }, 
+    {
+      "seqno": 35, 
+      "headers": [
+        {
+          "content-length": "12969"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Fri, 01 Nov 2013 21:39:03 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Thu, 01 Nov 2012 20:35:28 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31394885"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:58 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "REVz0k4Gud7bedzv9KSTG2i1KOporb0T14mWht95MIE="
+        }
+      ]
+    }, 
+    {
+      "seqno": 36, 
+      "headers": [
+        {
+          "content-length": "8457"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 08:36:26 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:06:44 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31088728"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:58 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "VKvuYL/9rqvjXh7mr8LjSJmcgQLZ/a+Ztqj2aUsPacc="
+        }
+      ]
+    }, 
+    {
+      "seqno": 37, 
+      "headers": [
+        {
+          "content-length": "43"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "x-fb-metrics": "{\"w\":26,\"r\":18,\"q\":0,\"a\":30}"
+        }, 
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        }, 
+        {
+          "x-fb-server": "10.164.86.49"
+        }, 
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "pragma": "no-cache"
+        }, 
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        }, 
+        {
+          "content-type": "image/gif"
+        }, 
+        {
+          "x-fb-debug": "egJridVr0Ohgw3QbFe66p8hTV/ZDa+ldtrrj55f1Dwg="
+        }
+      ]
+    }, 
+    {
+      "seqno": 38, 
+      "headers": [
+        {
+          "content-length": "43"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "x-fb-metrics": "{\"w\":15,\"r\":74,\"q\":0,\"a\":21}"
+        }, 
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        }, 
+        {
+          "x-fb-server": "10.164.212.85"
+        }, 
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "pragma": "no-cache"
+        }, 
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        }, 
+        {
+          "content-type": "image/gif"
+        }, 
+        {
+          "x-fb-debug": "7JVbUHGoJcLzIbHgkJPv0DSBycKYYPPorUc0i6OdRw8="
+        }
+      ]
+    }, 
+    {
+      "seqno": 39, 
+      "headers": [
+        {
+          "content-length": "10334"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "x-fb-metrics": "{\"w\":33,\"r\":14,\"q\":0,\"a\":27}"
+        }, 
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        }, 
+        {
+          "x-fb-server": "10.164.203.85"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "pragma": "no-cache"
+        }, 
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }, 
+        {
+          "x-fb-debug": "SHFiC3r5rPZSoyQ6AT4o7Lrz58o2i0cRMRoLoKKAVLc="
+        }
+      ]
+    }, 
+    {
+      "seqno": 40, 
+      "headers": [
+        {
+          "content-length": "79019"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "x-fb-metrics": "{\"w\":47,\"r\":27,\"q\":0,\"a\":24}"
+        }, 
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        }, 
+        {
+          "x-fb-server": "10.164.121.55"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "pragma": "no-cache"
+        }, 
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }, 
+        {
+          "x-fb-debug": "B8TQ25HLrpUM+2nuhej+798G7ib2rXsLxKDRmmd6364="
+        }
+      ]
+    }, 
+    {
+      "seqno": 41, 
+      "headers": [
+        {
+          "content-length": "43"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 21 May 2013 19:18:35 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:05 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=17216856"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/gif"
+        }, 
+        {
+          "x-fb-debug": "q1YlNQFhUrIi0HF88gF/s47itTMC0ALVS2i6Xo/eSFQ="
+        }
+      ]
+    }, 
+    {
+      "seqno": 42, 
+      "headers": [
+        {
+          "content-length": "43"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 21 May 2013 19:18:34 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:20 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=17216855"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/gif"
+        }, 
+        {
+          "x-fb-debug": "OOKrpYeJ1K2euVWUg0h3X4OLDU+bPXAhHe2ZbKmaIIo="
+        }
+      ]
+    }, 
+    {
+      "seqno": 43, 
+      "headers": [
+        {
+          "content-length": "55530"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 03:07:27 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sat, 27 Oct 2012 21:42:28 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31068989"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:58 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "BWYgCEbzeWyERD5oPP51t1mG+xnS0km1r6TZrds9BdY="
+        }
+      ]
+    }, 
+    {
+      "seqno": 44, 
+      "headers": [
+        {
+          "content-length": "67"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "x-fb-metrics": "{\"w\":47,\"r\":27,\"q\":0,\"a\":24}"
+        }, 
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        }, 
+        {
+          "x-fb-server": "10.164.121.55"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "pragma": "no-cache"
+        }, 
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "6DDnMStngO3Ec4qHVJLnouT/OjWIKWOh3p7X19lwA2E="
+        }
+      ]
+    }, 
+    {
+      "seqno": 45, 
+      "headers": [
+        {
+          "content-length": "67"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "DY+dO6Fs6HOjJLzXfO2vzcoACugopwtj+ZfSFe3+0Io="
+        }
+      ]
+    }, 
+    {
+      "seqno": 46, 
+      "headers": [
+        {
+          "content-length": "6332"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 15 Oct 2013 04:49:02 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Mon, 15 Oct 2012 01:19:28 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=29865483"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:50:59 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "U3t5ojZAXSlz/rftvVXMdi+dQaAlCxv95u4nFdmaOpU="
+        }
+      ]
+    }, 
+    {
+      "seqno": 47, 
+      "headers": [
+        {
+          "content-length": "1025"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 21 May 2013 19:18:34 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:19 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=17216854"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:00 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/gif"
+        }, 
+        {
+          "x-fb-debug": "WkN9kzT0IbJnmPVAe/JpiO1KA3sDm5JiLNu+peaU22E="
+        }
+      ]
+    }, 
+    {
+      "seqno": 48, 
+      "headers": [
+        {
+          "content-length": "7905"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 30 Oct 2013 13:12:25 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:38:51 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31191685"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:00 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "text/css; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "flUDwRXAcKGlCZV+B6xp1kix2zMM2jCaLr8GXWfOS9o="
+        }
+      ]
+    }, 
+    {
+      "seqno": 49, 
+      "headers": [
+        {
+          "content-length": "960"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 28 Aug 2013 14:36:20 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Thu, 07 Jun 2012 20:17:10 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=25753518"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:02 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "s2bSrOgSOrnc1I2Y+hCmZNjO4JKRAsJvvEShk7xvQh0="
+        }
+      ]
+    }, 
+    {
+      "seqno": 50, 
+      "headers": [
+        {
+          "content-length": "1421"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 28 Aug 2013 14:36:47 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Fri, 13 Jul 2012 13:05:49 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=25753545"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:02 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "dSqFMj3BQam7KrjoHsDUySNYx2e/ZA4jk+iLwQD5q+M="
+        }
+      ]
+    }, 
+    {
+      "seqno": 51, 
+      "headers": [
+        {
+          "content-length": "3977"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 30 Oct 2013 05:00:35 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Sat, 27 Oct 2012 21:41:38 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31162173"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:02 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "if1LyEGCEK6E/tHFIYqTdcReGf2YlH/8CNcvt0MSb5c="
+        }
+      ]
+    }, 
+    {
+      "seqno": 52, 
+      "headers": [
+        {
+          "content-length": "316"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 27 Aug 2013 03:46:29 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:03 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=25628126"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "0MQqsPt7SaQdEz9msJEk0wieC0zyyvfgvjy4gscfRm4="
+        }
+      ]
+    }, 
+    {
+      "seqno": 53, 
+      "headers": [
+        {
+          "content-length": "6779"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Wed, 09 May 2012 22:55:50 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 54, 
+      "headers": [
+        {
+          "content-length": "7899"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Mon, 14 May 2012 09:15:54 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 55, 
+      "headers": [
+        {
+          "content-length": "8961"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "last-modified": "Fri, 10 Aug 2012 23:04:25 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 56, 
+      "headers": [
+        {
+          "content-length": "7270"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "last-modified": "Thu, 17 May 2012 17:02:26 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 57, 
+      "headers": [
+        {
+          "content-length": "10284"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Fri, 14 Sep 2012 13:43:01 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 58, 
+      "headers": [
+        {
+          "content-length": "6932"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Tue, 15 May 2012 16:53:10 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 59, 
+      "headers": [
+        {
+          "content-length": "19404"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "last-modified": "Fri, 13 Jul 2012 21:38:17 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 60, 
+      "headers": [
+        {
+          "content-length": "43"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "x-fb-metrics": "{\"w\":23,\"r\":24,\"q\":0,\"a\":31}"
+        }, 
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        }, 
+        {
+          "x-fb-server": "10.164.204.89"
+        }, 
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "pragma": "no-cache"
+        }, 
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:04 GMT"
+        }, 
+        {
+          "content-type": "image/gif"
+        }, 
+        {
+          "x-fb-debug": "mhzgPOTS+rD7XyjD1gp3zWldoiZpmeeyK0sWCXxCmL8="
+        }
+      ]
+    }, 
+    {
+      "seqno": 61, 
+      "headers": [
+        {
+          "content-length": "43"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "x-fb-metrics": "{\"w\":13,\"r\":137,\"q\":0,\"a\":23}"
+        }, 
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        }, 
+        {
+          "x-fb-server": "10.164.167.53"
+        }, 
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "pragma": "no-cache"
+        }, 
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:04 GMT"
+        }, 
+        {
+          "content-type": "image/gif"
+        }, 
+        {
+          "x-fb-debug": "uWC6Yw5Jjt8tp6GMW/0c7q4sQiyN+cfsFumyrajMSLE="
+        }
+      ]
+    }, 
+    {
+      "seqno": 62, 
+      "headers": [
+        {
+          "content-length": "10334"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "x-fb-metrics": "{\"w\":20,\"r\":43,\"q\":0,\"a\":30}"
+        }, 
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        }, 
+        {
+          "x-fb-server": "10.165.52.67"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "pragma": "no-cache"
+        }, 
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:04 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }, 
+        {
+          "x-fb-debug": "K6O9zzGnsjkFUcjVnvogKEp8WyYKDD5/1SRA3JOqTz8="
+        }
+      ]
+    }, 
+    {
+      "seqno": 63, 
+      "headers": [
+        {
+          "content-length": "79019"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "x-fb-metrics": "{\"w\":40,\"r\":33,\"q\":0,\"a\":22}"
+        }, 
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        }, 
+        {
+          "x-fb-server": "10.164.10.79"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "pragma": "no-cache"
+        }, 
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:04 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }, 
+        {
+          "x-fb-debug": "gU+KRCRWrUp+aETSVFA2+QqzJ57Mry5y8i9NZISRzV4="
+        }
+      ]
+    }, 
+    {
+      "seqno": 64, 
+      "headers": [
+        {
+          "content-length": "67"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "x-fb-metrics": "{\"w\":40,\"r\":33,\"q\":0,\"a\":22}"
+        }, 
+        {
+          "expires": "Thu, 1 Apr 2004 01:01:01 GMT"
+        }, 
+        {
+          "x-fb-server": "10.164.10.79"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "last-modified": "Thu, 1 Apr 2004 01:01:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "pragma": "no-cache"
+        }, 
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:04 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "2KYdrNd+vAjbaW2+l9lZ0c9qQnQQuLC0uV+aDWEfnEs="
+        }
+      ]
+    }, 
+    {
+      "seqno": 65, 
+      "headers": [
+        {
+          "content-length": "67"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:04 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "ltZY31wZe0x9jjXZ+/GQMCIZ6L+UzLcVFaj4Ye8cEag="
+        }
+      ]
+    }, 
+    {
+      "seqno": 66, 
+      "headers": [
+        {
+          "content-length": "32220"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Mon, 15 Oct 2012 15:37:08 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 67, 
+      "headers": [
+        {
+          "content-length": "105703"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Mon, 15 Oct 2012 15:38:03 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 68, 
+      "headers": [
+        {
+          "content-length": "36768"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Thu, 01 Nov 2012 01:55:42 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "content-type": "image/gif"
+        }
+      ]
+    }, 
+    {
+      "seqno": 69, 
+      "headers": [
+        {
+          "content-length": "119574"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Fri, 26 Oct 2012 09:41:12 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 70, 
+      "headers": [
+        {
+          "content-length": "659"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Sat, 11 May 2013 07:12:06 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:22 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=16309260"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:06 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/x-icon"
+        }, 
+        {
+          "x-fb-debug": "yE8mx2kOMcI8Q4MtoKCXYAXv7xSMQBGufoB0y/qkYEs="
+        }
+      ]
+    }, 
+    {
+      "seqno": 71, 
+      "headers": [
+        {
+          "content-length": "6966"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "last-modified": "Thu, 17 May 2012 16:26:06 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 72, 
+      "headers": [
+        {
+          "content-length": "4501"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Fri, 06 Jul 2012 11:36:45 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 73, 
+      "headers": [
+        {
+          "content-length": "37432"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Mon, 04 Jun 2012 08:35:52 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 74, 
+      "headers": [
+        {
+          "content-length": "7198"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Tue, 15 May 2012 16:45:13 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 75, 
+      "headers": [
+        {
+          "content-length": "13454"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Thu, 31 May 2012 01:48:38 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 76, 
+      "headers": [
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "transfer-encoding": "chunked"
+        }, 
+        {
+          "access-control-allow-credentials": "true"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "pragma": "no-cache"
+        }, 
+        {
+          "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:08 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "http://www.facebook.com"
+        }, 
+        {
+          "content-type": "application/json"
+        }
+      ]
+    }, 
+    {
+      "seqno": 77, 
+      "headers": [
+        {
+          "content-length": "19537"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "last-modified": "Tue, 19 Jun 2012 08:51:27 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 78, 
+      "headers": [
+        {
+          "content-length": "5842"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Mon, 14 May 2012 22:03:47 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 79, 
+      "headers": [
+        {
+          "content-length": "6973"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Tue, 22 May 2012 23:26:43 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 80, 
+      "headers": [
+        {
+          "content-length": "7263"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Mon, 11 Jun 2012 21:11:11 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 81, 
+      "headers": [
+        {
+          "content-length": "6222"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "last-modified": "Tue, 15 May 2012 02:10:10 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 82, 
+      "headers": [
+        {
+          "content-length": "8005"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Fri, 13 Jul 2012 02:50:27 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 83, 
+      "headers": [
+        {
+          "content-length": "7949"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Wed, 31 Oct 2012 17:25:30 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 84, 
+      "headers": [
+        {
+          "content-length": "5850"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Wed, 23 May 2012 13:04:05 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 85, 
+      "headers": [
+        {
+          "content-length": "4860"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Fri, 18 May 2012 06:59:13 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 86, 
+      "headers": [
+        {
+          "content-length": "36615"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Fri, 08 Jun 2012 09:18:35 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 87, 
+      "headers": [
+        {
+          "content-length": "26180"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Fri, 01 Jun 2012 12:58:28 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 88, 
+      "headers": [
+        {
+          "content-length": "8551"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Wed, 16 May 2012 00:19:20 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:10 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 89, 
+      "headers": [
+        {
+          "content-length": "7021"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Fri, 18 May 2012 08:58:03 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:10 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 90, 
+      "headers": [
+        {
+          "content-length": "8126"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Thu, 17 May 2012 10:31:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:10 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 91, 
+      "headers": [
+        {
+          "content-length": "21780"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Fri, 20 Jul 2012 20:43:14 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:10 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 92, 
+      "headers": [
+        {
+          "content-length": "16109"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Fri, 08 Jun 2012 21:45:00 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 93, 
+      "headers": [
+        {
+          "content-length": "5248"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Tue, 15 May 2012 17:19:22 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 94, 
+      "headers": [
+        {
+          "content-length": "7600"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Tue, 15 May 2012 00:02:13 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 95, 
+      "headers": [
+        {
+          "content-length": "8148"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "last-modified": "Tue, 04 Sep 2012 05:25:17 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 96, 
+      "headers": [
+        {
+          "content-length": "23688"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:10 GMT"
+        }, 
+        {
+          "last-modified": "Tue, 05 Jun 2012 16:58:56 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:10 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 97, 
+      "headers": [
+        {
+          "content-length": "32579"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Fri, 15 Jun 2012 23:21:31 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 98, 
+      "headers": [
+        {
+          "content-length": "36154"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Tue, 15 May 2012 16:53:04 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 99, 
+      "headers": [
+        {
+          "content-length": "8261"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Thu, 17 May 2012 16:15:19 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/jpeg"
+        }
+      ]
+    }, 
+    {
+      "seqno": 100, 
+      "headers": [
+        {
+          "content-length": "34603"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "expires": "Sat, 17 Nov 2012 12:51:03 GMT"
+        }, 
+        {
+          "last-modified": "Tue, 05 Jun 2012 19:33:30 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 101, 
+      "headers": [
+        {
+          "content-length": "19320"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "accept-ranges": "bytes"
+        }, 
+        {
+          "last-modified": "Fri, 20 Jul 2012 22:33:10 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "max-age=1209600"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:09 GMT"
+        }, 
+        {
+          "content-type": "image/png"
+        }
+      ]
+    }, 
+    {
+      "seqno": 102, 
+      "headers": [
+        {
+          "content-length": "1589"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 30 Oct 2013 13:08:05 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Sat, 27 Oct 2012 21:59:56 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31191410"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:15 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "text/css; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "cEr4CpqyPlutZEM5egM7EW1V/FoNLas8puqhILOyn6g="
+        }
+      ]
+    }, 
+    {
+      "seqno": 103, 
+      "headers": [
+        {
+          "content-length": "124"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 27 Aug 2013 05:04:30 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:34 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=25632795"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:15 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "SjbWzWIhc5uDeUgKzkah4oVawEfOfsqgn79tJvLiODA="
+        }
+      ]
+    }, 
+    {
+      "seqno": 104, 
+      "headers": [
+        {
+          "content-length": "178"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "expires": "Tue, 27 Aug 2013 06:45:13 GMT"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:02:59 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          "cache-control": "public, max-age=25638838"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:15 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "8BYYADIiLWZPRqrmghOTJnnu5b75InjLJYums29XQC4="
+        }
+      ]
+    }, 
+    {
+      "seqno": 105, 
+      "headers": [
+        {
+          "content-length": "3403"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 11:39:02 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Fri, 26 Oct 2012 21:42:07 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31099667"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:15 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "rg/3x10ePyW5+Yv14okaeMgQpdIDitUpRdeQlHd62wU="
+        }
+      ]
+    }, 
+    {
+      "seqno": 106, 
+      "headers": [
+        {
+          "content-length": "3794"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 09:12:42 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:11:34 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31090886"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:16 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "+G0d7Y1/nAK76h5l1ygZcgFyqkHdYYjzu9bN8TThTW0="
+        }
+      ]
+    }, 
+    {
+      "seqno": 107, 
+      "headers": [
+        {
+          "content-length": "4316"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 30 Oct 2013 13:08:38 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Thu, 27 Sep 2012 22:19:28 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31191442"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:16 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "Zx9+0hICgorbmj40+TVQqx/6DWk0JFijw5sOouOK4x8="
+        }
+      ]
+    }, 
+    {
+      "seqno": 108, 
+      "headers": [
+        {
+          "content-length": "82"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 27 Aug 2013 03:46:37 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:02:59 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=25628121"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:16 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "y9gp03qLmGwdrjrggsFyxKUnduRuH6ZkhHy3J217wnA="
+        }
+      ]
+    }, 
+    {
+      "seqno": 109, 
+      "headers": [
+        {
+          "content-length": "281"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 27 Aug 2013 03:46:43 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Thu, 12 Apr 2012 03:03:15 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=25628127"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:16 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "y31IzOtXz6QEqDb4Yh8nL9E7Jz3QdrtFTVTfJpFI67s="
+        }
+      ]
+    }, 
+    {
+      "seqno": 110, 
+      "headers": [
+        {
+          "content-length": "18581"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 03:07:15 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:07:48 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31068960"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:15 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "cdKo7nf6SbFgEUsu8p8ZpYkyd14IkSsYwu8pEpjIPW8="
+        }
+      ]
+    }, 
+    {
+      "seqno": 111, 
+      "headers": [
+        {
+          "content-length": "686"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 02:40:01 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 21:28:22 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31067324"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:17 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "text/css; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "Mcoj0fymAm3BWRvLoE9uVgrJkXk8Wldn9hUKly6PE60="
+        }
+      ]
+    }, 
+    {
+      "seqno": 112, 
+      "headers": [
+        {
+          "content-length": "70824"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 29 Oct 2013 16:43:30 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Sun, 28 Oct 2012 14:35:10 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31117935"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:15 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "image/png"
+        }, 
+        {
+          "x-fb-debug": "U6CnJQe7lFM5/wvYBRcEyvixo284qs3dxFI4vIJ3Sfo="
+        }
+      ]
+    }, 
+    {
+      "seqno": 113, 
+      "headers": [
+        {
+          "content-length": "6953"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 06 Nov 2012 13:10:12 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Mon, 29 Oct 2012 18:56:50 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=260332"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:20 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-shockwave-flash"
+        }, 
+        {
+          "x-fb-debug": "8ROXKJ/K5IoNZG3RcJoN8LoohKyoDW2iTe6nY9hRINI="
+        }
+      ]
+    }, 
+    {
+      "seqno": 114, 
+      "headers": [
+        {
+          "content-length": "6953"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Tue, 06 Nov 2012 13:10:12 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Mon, 29 Oct 2012 18:56:50 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=260331"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:21 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-shockwave-flash"
+        }, 
+        {
+          "x-fb-debug": "8ROXKJ/K5IoNZG3RcJoN8LoohKyoDW2iTe6nY9hRINI="
+        }
+      ]
+    }, 
+    {
+      "seqno": 115, 
+      "headers": [
+        {
+          "content-length": "15685"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Thu, 31 Oct 2013 19:03:10 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Tue, 30 Oct 2012 17:45:29 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=31299110"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:20 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "casrvtlqM38DGgUK+sC64wYFWqXchCM2wnMjgM8VC98="
+        }
+      ]
+    }, 
+    {
+      "seqno": 116, 
+      "headers": [
+        {
+          "content-length": "1591"
+        }, 
+        {
+          "x-content-type-options": "nosniff"
+        }, 
+        {
+          "content-encoding": "gzip"
+        }, 
+        {
+          "expires": "Wed, 02 Oct 2013 14:15:24 GMT"
+        }, 
+        {
+          "vary": "Accept-Encoding"
+        }, 
+        {
+          "x-cnection": "close"
+        }, 
+        {
+          "last-modified": "Thu, 20 Sep 2012 01:12:35 GMT"
+        }, 
+        {
+          "connection": "keep-alive"
+        }, 
+        {
+          ":status": "200"
+        }, 
+        {
+          "cache-control": "public, max-age=28776235"
+        }, 
+        {
+          "date": "Sat, 03 Nov 2012 12:51:29 GMT"
+        }, 
+        {
+          "access-control-allow-origin": "*"
+        }, 
+        {
+          "content-type": "application/x-javascript; charset=utf-8"
+        }, 
+        {
+          "x-fb-debug": "E9XqLqcAPtaMWK+vlxTTyhNPMewUq9nSCKax+m9KMwk="
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jetty-http2/http2-hpack/src/test/resources/jetty-logging.properties b/jetty-http2/http2-hpack/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..e40e8e4
--- /dev/null
+++ b/jetty-http2/http2-hpack/src/test/resources/jetty-logging.properties
@@ -0,0 +1,3 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+org.eclipse.jetty.http2.LEVEL=INFO
+org.eclipse.jetty.http2.hpack.LEVEL=INFO
diff --git a/jetty-http2/http2-http-client-transport/pom.xml b/jetty-http2/http2-http-client-transport/pom.xml
new file mode 100644
index 0000000..c3ddb74
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/pom.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.eclipse.jetty.http2</groupId>
+        <artifactId>http2-parent</artifactId>
+        <version>9.3.19-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>http2-http-client-transport</artifactId>
+    <name>Jetty :: HTTP2 :: HTTP Client Transport</name>
+
+    <properties>
+        <bundle-symbolic-name>${project.groupId}.client.http</bundle-symbolic-name>
+    </properties>
+
+    <profiles>
+        <profile>
+            <id>jdk8</id>
+            <activation>
+                <jdk>[1.8,1.9)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-dependency-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>copy</id>
+                                <phase>generate-resources</phase>
+                                <goals>
+                                    <goal>copy</goal>
+                                </goals>
+                                <configuration>
+                                    <artifactItems>
+                                        <artifactItem>
+                                            <groupId>org.mortbay.jetty.alpn</groupId>
+                                            <artifactId>alpn-boot</artifactId>
+                                            <version>${alpn.version}</version>
+                                            <type>jar</type>
+                                            <overWrite>false</overWrite>
+                                            <outputDirectory>${project.build.directory}/alpn</outputDirectory>
+                                        </artifactItem>
+                                    </artifactItems>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <argLine>-Xbootclasspath/p:${project.build.directory}/alpn/alpn-boot-${alpn.version}.jar</argLine>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>jdk9</id>
+            <activation>
+                <jdk>[1.9,)</jdk>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>org.eclipse.jetty</groupId>
+                    <artifactId>jetty-alpn-java-client</artifactId>
+                    <version>${project.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.eclipse.jetty</groupId>
+                    <artifactId>jetty-alpn-java-server</artifactId>
+                    <version>${project.version}</version>
+                    <scope>test</scope>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.http2</groupId>
+            <artifactId>http2-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.eclipse.jetty.toolchain</groupId>
+            <artifactId>jetty-test-helper</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.http2</groupId>
+            <artifactId>http2-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpChannelOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpChannelOverHTTP2.java
new file mode 100644
index 0000000..18fa3c5
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpChannelOverHTTP2.java
@@ -0,0 +1,123 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client.http;
+
+import org.eclipse.jetty.client.HttpChannel;
+import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.HttpReceiver;
+import org.eclipse.jetty.client.HttpSender;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.util.Callback;
+
+public class HttpChannelOverHTTP2 extends HttpChannel
+{
+    private final HttpConnectionOverHTTP2 connection;
+    private final Session session;
+    private final boolean push;
+    private final HttpSenderOverHTTP2 sender;
+    private final HttpReceiverOverHTTP2 receiver;
+    private Stream stream;
+
+    public HttpChannelOverHTTP2(HttpDestination destination, HttpConnectionOverHTTP2 connection, Session session, boolean push)
+    {
+        super(destination);
+        this.connection = connection;
+        this.session = session;
+        this.push = push;
+        this.sender = new HttpSenderOverHTTP2(this);
+        this.receiver = new HttpReceiverOverHTTP2(this);
+    }
+
+    protected HttpConnectionOverHTTP2 getHttpConnection()
+    {
+        return connection;
+    }
+
+    public Session getSession()
+    {
+        return session;
+    }
+
+    public Stream.Listener getStreamListener()
+    {
+        return receiver;
+    }
+
+    @Override
+    protected HttpSender getHttpSender()
+    {
+        return sender;
+    }
+
+    @Override
+    protected HttpReceiver getHttpReceiver()
+    {
+        return receiver;
+    }
+
+    public Stream getStream()
+    {
+        return stream;
+    }
+
+    public void setStream(Stream stream)
+    {
+        this.stream = stream;
+    }
+
+    @Override
+    public void send()
+    {
+        HttpExchange exchange = getHttpExchange();
+        if (exchange != null)
+            sender.send(exchange);
+    }
+
+    @Override
+    public void release()
+    {
+        connection.release(this);
+    }
+
+    @Override
+    public boolean abort(HttpExchange exchange, Throwable requestFailure, Throwable responseFailure)
+    {
+        boolean aborted = super.abort(exchange, requestFailure, responseFailure);
+        if (aborted)
+        {
+            Stream stream = getStream();
+            if (stream != null)
+                stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+        }
+        return aborted;
+    }
+
+    @Override
+    public void exchangeTerminated(HttpExchange exchange, Result result)
+    {
+        super.exchangeTerminated(exchange, result);
+        if (!push)
+            release();
+    }
+}
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
new file mode 100644
index 0000000..81f9648
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
@@ -0,0 +1,233 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client.http;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jetty.alpn.client.ALPNClientConnectionFactory;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpClientTransport;
+import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.ProxyConfiguration;
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http2.HTTP2Session;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.client.HTTP2ClientConnectionFactory;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+@ManagedObject("The HTTP/2 client transport")
+public class HttpClientTransportOverHTTP2 extends ContainerLifeCycle implements HttpClientTransport
+{
+    private final HTTP2Client client;
+    private ClientConnectionFactory connectionFactory;
+    private HttpClient httpClient;
+    private boolean useALPN = true;
+
+    public HttpClientTransportOverHTTP2(HTTP2Client client)
+    {
+        this.client = client;
+    }
+
+    @ManagedAttribute(value = "The number of selectors", readonly = true)
+    public int getSelectors()
+    {
+        return client.getSelectors();
+    }
+
+    public boolean isUseALPN()
+    {
+        return useALPN;
+    }
+
+    public void setUseALPN(boolean useALPN)
+    {
+        this.useALPN = useALPN;
+    }
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        if (!client.isStarted())
+        {
+            client.setExecutor(httpClient.getExecutor());
+            client.setScheduler(httpClient.getScheduler());
+            client.setByteBufferPool(httpClient.getByteBufferPool());
+            client.setConnectTimeout(httpClient.getConnectTimeout());
+            client.setIdleTimeout(httpClient.getIdleTimeout());
+            client.setInputBufferSize(httpClient.getResponseBufferSize());
+        }
+        addBean(client);
+        super.doStart();
+
+        this.connectionFactory = new HTTP2ClientConnectionFactory();
+        client.setClientConnectionFactory((endPoint, context) ->
+        {
+            HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
+            return destination.getClientConnectionFactory().newConnection(endPoint, context);
+        });
+    }
+
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+        removeBean(client);
+    }
+
+    protected HttpClient getHttpClient()
+    {
+        return httpClient;
+    }
+
+    @Override
+    public void setHttpClient(HttpClient client)
+    {
+        httpClient = client;
+    }
+
+    @Override
+    public HttpDestination newHttpDestination(Origin origin)
+    {
+        return new HttpDestinationOverHTTP2(httpClient, origin);
+    }
+
+    @Override
+    public void connect(InetSocketAddress address, Map<String, Object> context)
+    {
+        client.setConnectTimeout(httpClient.getConnectTimeout());
+
+        SessionListenerPromise listenerPromise = new SessionListenerPromise(context);
+
+        HttpDestinationOverHTTP2 destination = (HttpDestinationOverHTTP2)context.get(HTTP_DESTINATION_CONTEXT_KEY);
+        SslContextFactory sslContextFactory = null;
+        if (HttpScheme.HTTPS.is(destination.getScheme()))
+            sslContextFactory = httpClient.getSslContextFactory();
+
+        client.connect(sslContextFactory, address, listenerPromise, listenerPromise, context);
+    }
+
+    @Override
+    public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
+    {
+        endPoint.setIdleTimeout(httpClient.getIdleTimeout());
+
+        ClientConnectionFactory factory = connectionFactory;
+        HttpDestinationOverHTTP2 destination = (HttpDestinationOverHTTP2)context.get(HTTP_DESTINATION_CONTEXT_KEY);
+        ProxyConfiguration.Proxy proxy = destination.getProxy();
+        boolean ssl = proxy == null ? HttpScheme.HTTPS.is(destination.getScheme()) : proxy.isSecure();
+        if (ssl && isUseALPN())
+            factory = new ALPNClientConnectionFactory(client.getExecutor(), factory, client.getProtocols());
+        return factory.newConnection(endPoint, context);
+    }
+
+    protected HttpConnectionOverHTTP2 newHttpConnection(HttpDestination destination, Session session)
+    {
+        return new HttpConnectionOverHTTP2(destination, session);
+    }
+
+    protected void onClose(HttpConnectionOverHTTP2 connection, GoAwayFrame frame)
+    {
+        connection.close();
+    }
+
+    private class SessionListenerPromise extends Session.Listener.Adapter implements Promise<Session>
+    {
+        private final Map<String, Object> context;
+        private HttpConnectionOverHTTP2 connection;
+
+        private SessionListenerPromise(Map<String, Object> context)
+        {
+            this.context = context;
+        }
+
+        @Override
+        public void succeeded(Session session)
+        {
+            connection = newHttpConnection(destination(), session);
+            promise().succeeded(connection);
+        }
+
+        @Override
+        public void failed(Throwable failure)
+        {
+            promise().failed(failure);
+        }
+
+        private HttpDestinationOverHTTP2 destination()
+        {
+            return (HttpDestinationOverHTTP2)context.get(HTTP_DESTINATION_CONTEXT_KEY);
+        }
+
+        @SuppressWarnings("unchecked")
+        private Promise<Connection> promise()
+        {
+            return (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
+        }
+
+        @Override
+        public Map<Integer, Integer> onPreface(Session session)
+        {
+            Map<Integer, Integer> settings = new HashMap<>();
+            settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, client.getInitialStreamRecvWindow());
+            return settings;
+        }
+
+        @Override
+        public void onSettings(Session session, SettingsFrame frame)
+        {
+            Map<Integer, Integer> settings = frame.getSettings();
+            if (settings.containsKey(SettingsFrame.MAX_CONCURRENT_STREAMS))
+                destination().setMaxRequestsPerConnection(settings.get(SettingsFrame.MAX_CONCURRENT_STREAMS));
+        }
+
+        @Override
+        public void onClose(Session session, GoAwayFrame frame)
+        {
+            HttpClientTransportOverHTTP2.this.onClose(connection, frame);
+        }
+
+        @Override
+        public boolean onIdleTimeout(Session session)
+        {
+            return connection.onIdleTimeout(((HTTP2Session)session).getEndPoint().getIdleTimeout());
+        }
+
+        @Override
+        public void onFailure(Session session, Throwable failure)
+        {
+            HttpConnectionOverHTTP2 c = connection;
+            if (c != null)
+                c.close(failure);
+        }
+    }
+}
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpConnectionOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpConnectionOverHTTP2.java
new file mode 100644
index 0000000..da0093a
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpConnectionOverHTTP2.java
@@ -0,0 +1,130 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client.http;
+
+import java.nio.channels.AsynchronousCloseException;
+import java.util.Set;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.jetty.client.HttpChannel;
+import org.eclipse.jetty.client.HttpConnection;
+import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.SendFailure;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.ConcurrentHashSet;
+
+public class HttpConnectionOverHTTP2 extends HttpConnection
+{
+    private final Set<HttpChannel> channels = new ConcurrentHashSet<>();
+    private final AtomicBoolean closed = new AtomicBoolean();
+    private final Session session;
+
+    public HttpConnectionOverHTTP2(HttpDestination destination, Session session)
+    {
+        super(destination);
+        this.session = session;
+    }
+
+    public Session getSession()
+    {
+        return session;
+    }
+
+    @Override
+    protected SendFailure send(HttpExchange exchange)
+    {
+        exchange.getRequest().version(HttpVersion.HTTP_2);
+        normalizeRequest(exchange.getRequest());
+
+        // One connection maps to N channels, so for each exchange we create a new channel.
+        HttpChannel channel = newHttpChannel(false);
+        channels.add(channel);
+
+        return send(channel, exchange);
+    }
+
+    protected HttpChannelOverHTTP2 newHttpChannel(boolean push)
+    {
+        return new HttpChannelOverHTTP2(getHttpDestination(), this, getSession(), push);
+    }
+
+    protected void release(HttpChannel channel)
+    {
+        channels.remove(channel);
+        getHttpDestination().release(this);
+    }
+
+    @Override
+    public boolean onIdleTimeout(long idleTimeout)
+    {
+        boolean close = super.onIdleTimeout(idleTimeout);
+        if (close)
+            close(new TimeoutException("idle_timeout"));
+        return false;
+    }
+
+    @Override
+    public void close()
+    {
+        close(new AsynchronousCloseException());
+    }
+
+    protected void close(Throwable failure)
+    {
+        if (closed.compareAndSet(false, true))
+        {
+            getHttpDestination().close(this);
+
+            abort(failure);
+
+            session.close(ErrorCode.NO_ERROR.code, failure.getMessage(), Callback.NOOP);
+        }
+    }
+
+    @Override
+    public boolean isClosed()
+    {
+        return closed.get();
+    }
+
+    private void abort(Throwable failure)
+    {
+        for (HttpChannel channel : channels)
+        {
+            HttpExchange exchange = channel.getHttpExchange();
+            if (exchange != null)
+                exchange.getRequest().abort(failure);
+        }
+        channels.clear();
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%h[%s]",
+                getClass().getSimpleName(),
+                this,
+                session);
+    }
+}
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java
new file mode 100644
index 0000000..0345e37
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java
@@ -0,0 +1,39 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client.http;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.MultiplexHttpDestination;
+import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.SendFailure;
+
+public class HttpDestinationOverHTTP2 extends MultiplexHttpDestination<HttpConnectionOverHTTP2>
+{
+    public HttpDestinationOverHTTP2(HttpClient client, Origin origin)
+    {
+        super(client, origin);
+    }
+
+    @Override
+    protected SendFailure send(HttpConnectionOverHTTP2 connection, HttpExchange exchange)
+    {
+        return connection.send(exchange);
+    }
+}
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpReceiverOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpReceiverOverHTTP2.java
new file mode 100644
index 0000000..1bae533
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpReceiverOverHTTP2.java
@@ -0,0 +1,250 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client.http;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Queue;
+import java.util.function.BiFunction;
+
+import org.eclipse.jetty.client.HttpChannel;
+import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.HttpReceiver;
+import org.eclipse.jetty.client.HttpRequest;
+import org.eclipse.jetty.client.HttpResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IteratingCallback;
+
+public class HttpReceiverOverHTTP2 extends HttpReceiver implements Stream.Listener
+{
+    private final ContentNotifier contentNotifier = new ContentNotifier();
+
+    public HttpReceiverOverHTTP2(HttpChannel channel)
+    {
+        super(channel);
+    }
+
+    @Override
+    protected HttpChannelOverHTTP2 getHttpChannel()
+    {
+        return (HttpChannelOverHTTP2)super.getHttpChannel();
+    }
+
+    @Override
+    public void onHeaders(Stream stream, HeadersFrame frame)
+    {
+        HttpExchange exchange = getHttpExchange();
+        if (exchange == null)
+            return;
+
+        HttpResponse response = exchange.getResponse();
+        MetaData.Response metaData = (MetaData.Response)frame.getMetaData();
+        response.version(metaData.getHttpVersion()).status(metaData.getStatus()).reason(metaData.getReason());
+
+        if (responseBegin(exchange))
+        {
+            HttpFields headers = metaData.getFields();
+            for (HttpField header : headers)
+            {
+                if (!responseHeader(exchange, header))
+                    return;
+            }
+
+            if (responseHeaders(exchange))
+            {
+                int status = metaData.getStatus();
+                boolean informational = HttpStatus.isInformational(status) && status != HttpStatus.SWITCHING_PROTOCOLS_101;
+                if (frame.isEndStream() || informational)
+                    responseSuccess(exchange);
+            }
+        }
+    }
+
+    @Override
+    public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+    {
+        HttpExchange exchange = getHttpExchange();
+        if (exchange == null)
+            return null;
+
+        HttpRequest request = exchange.getRequest();
+        MetaData.Request metaData = (MetaData.Request)frame.getMetaData();
+        HttpRequest pushRequest = (HttpRequest)getHttpDestination().getHttpClient().newRequest(metaData.getURIString());
+
+        BiFunction<Request, Request, Response.CompleteListener> pushListener = request.getPushListener();
+        if (pushListener != null)
+        {
+            Response.CompleteListener listener = pushListener.apply(request, pushRequest);
+            if (listener != null)
+            {
+                HttpChannelOverHTTP2 pushChannel = getHttpChannel().getHttpConnection().newHttpChannel(true);
+                List<Response.ResponseListener> listeners = Collections.singletonList(listener);
+                HttpExchange pushExchange = new HttpExchange(getHttpDestination(), pushRequest, listeners);
+                pushChannel.associate(pushExchange);
+                pushChannel.setStream(stream);
+                // TODO: idle timeout ?
+                pushExchange.requestComplete(null);
+                pushExchange.terminateRequest();
+                return pushChannel.getStreamListener();
+            }
+        }
+
+        stream.reset(new ResetFrame(stream.getId(), ErrorCode.REFUSED_STREAM_ERROR.code), Callback.NOOP);
+        return null;
+    }
+
+    @Override
+    public void onData(Stream stream, DataFrame frame, Callback callback)
+    {
+        HttpExchange exchange = getHttpExchange();
+        if (exchange == null)
+        {
+            callback.failed(new IOException("terminated"));
+            return;
+        }
+
+        // We must copy the data since we do not know when the
+        // application will consume the bytes and the parsing
+        // will continue as soon as this method returns, eventually
+        // leading to reusing the underlying buffer for more reads.
+        ByteBufferPool byteBufferPool = getHttpDestination().getHttpClient().getByteBufferPool();
+        ByteBuffer original = frame.getData();
+        int length = original.remaining();
+        final ByteBuffer copy = byteBufferPool.acquire(length, original.isDirect());
+        BufferUtil.clearToFill(copy);
+        copy.put(original);
+        BufferUtil.flipToFlush(copy, 0);
+
+        contentNotifier.offer(new DataInfo(exchange, copy, callback, frame.isEndStream()));
+        contentNotifier.iterate();
+    }
+
+    @Override
+    public void onReset(Stream stream, ResetFrame frame)
+    {
+        HttpExchange exchange = getHttpExchange();
+        if (exchange == null)
+            return;
+
+        ErrorCode error = ErrorCode.from(frame.getError());
+        String reason = error == null ? "reset" : error.name().toLowerCase(Locale.ENGLISH);
+        exchange.getRequest().abort(new IOException(reason));
+    }
+
+    @Override
+    public boolean onIdleTimeout(Stream stream, Throwable x)
+    {
+        responseFailure(x);
+        return true;
+    }
+
+    private class ContentNotifier extends IteratingCallback
+    {
+        private final Queue<DataInfo> queue = new ArrayDeque<>();
+        private DataInfo dataInfo;
+
+        private boolean offer(DataInfo dataInfo)
+        {
+            synchronized (this)
+            {
+                return queue.offer(dataInfo);
+            }
+        }
+
+        @Override
+        protected Action process() throws Exception
+        {
+            DataInfo dataInfo;
+            synchronized (this)
+            {
+                dataInfo = queue.poll();
+            }
+
+            if (dataInfo == null)
+            {
+                DataInfo prevDataInfo = this.dataInfo;
+                if (prevDataInfo != null && prevDataInfo.last)
+                    return Action.SUCCEEDED;
+                return Action.IDLE;
+            }
+
+            this.dataInfo = dataInfo;
+            responseContent(dataInfo.exchange, dataInfo.buffer, this);
+            return Action.SCHEDULED;
+        }
+
+        @Override
+        public void succeeded()
+        {
+            ByteBufferPool byteBufferPool = getHttpDestination().getHttpClient().getByteBufferPool();
+            byteBufferPool.release(dataInfo.buffer);
+            dataInfo.callback.succeeded();
+            super.succeeded();
+        }
+
+        @Override
+        protected void onCompleteSuccess()
+        {
+            responseSuccess(dataInfo.exchange);
+        }
+
+        @Override
+        protected void onCompleteFailure(Throwable failure)
+        {
+            ByteBufferPool byteBufferPool = getHttpDestination().getHttpClient().getByteBufferPool();
+            byteBufferPool.release(dataInfo.buffer);
+            dataInfo.callback.failed(failure);
+            responseFailure(failure);
+        }
+    }
+
+    private static class DataInfo
+    {
+        private final HttpExchange exchange;
+        private final ByteBuffer buffer;
+        private final Callback callback;
+        private final boolean last;
+
+        private DataInfo(HttpExchange exchange, ByteBuffer buffer, Callback callback, boolean last)
+        {
+            this.exchange = exchange;
+            this.buffer = buffer;
+            this.callback = callback;
+            this.last = last;
+        }
+    }
+}
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpSenderOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpSenderOverHTTP2.java
new file mode 100644
index 0000000..6092fe3
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpSenderOverHTTP2.java
@@ -0,0 +1,122 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client.http;
+
+import java.net.URI;
+
+import org.eclipse.jetty.client.HttpContent;
+import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.HttpSender;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+
+public class HttpSenderOverHTTP2 extends HttpSender
+{
+    public HttpSenderOverHTTP2(HttpChannelOverHTTP2 channel)
+    {
+        super(channel);
+    }
+
+    @Override
+    protected HttpChannelOverHTTP2 getHttpChannel()
+    {
+        return (HttpChannelOverHTTP2)super.getHttpChannel();
+    }
+
+    @Override
+    protected void sendHeaders(HttpExchange exchange, final HttpContent content, final Callback callback)
+    {
+        Request request = exchange.getRequest();
+        String path = relativize(request.getPath());
+        HttpURI uri = new HttpURI(request.getScheme(), request.getHost(), request.getPort(), path, null, request.getQuery(), null);
+        MetaData.Request metaData = new MetaData.Request(request.getMethod(), uri, HttpVersion.HTTP_2, request.getHeaders());
+        HeadersFrame headersFrame = new HeadersFrame(metaData, null, !content.hasContent());
+        HttpChannelOverHTTP2 channel = getHttpChannel();
+        Promise<Stream> promise = new Promise<Stream>()
+        {
+            @Override
+            public void succeeded(Stream stream)
+            {
+                getHttpChannel().setStream(stream);
+                stream.setIdleTimeout(request.getIdleTimeout());
+
+                if (content.hasContent() && !expects100Continue(request))
+                {
+                    boolean advanced = content.advance();
+                    boolean lastContent = content.isLast();
+                    if (advanced || lastContent)
+                    {
+                        DataFrame dataFrame = new DataFrame(stream.getId(), content.getByteBuffer(), lastContent);
+                        stream.data(dataFrame, callback);
+                        return;
+                    }
+                }
+                callback.succeeded();
+            }
+
+            @Override
+            public void failed(Throwable failure)
+            {
+                callback.failed(failure);
+            }
+        };
+        // TODO optimize the send of HEADERS and DATA frames.
+        channel.getSession().newStream(headersFrame, promise, channel.getStreamListener());
+    }
+
+    private String relativize(String path)
+    {
+        try
+        {
+            String result = path;
+            URI uri = URI.create(result);
+            if (uri.isAbsolute())
+                result = uri.getPath();
+            return result.isEmpty() ? "/" : result;
+        }
+        catch (Throwable x)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Could not relativize " + path);
+            return path;
+        }
+    }
+
+    @Override
+    protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback)
+    {
+        if (content.isConsumed())
+        {
+            callback.succeeded();
+        }
+        else
+        {
+            Stream stream = getHttpChannel().getStream();
+            DataFrame frame = new DataFrame(stream.getId(), content.getByteBuffer(), content.isLast());
+            stream.data(frame, callback);
+        }
+    }
+}
diff --git a/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/AbstractTest.java b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/AbstractTest.java
new file mode 100644
index 0000000..586fd49
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/AbstractTest.java
@@ -0,0 +1,86 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client.http;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Rule;
+
+public class AbstractTest
+{
+    @Rule
+    public TestTracker tracker = new TestTracker();
+    protected Server server;
+    protected ServerConnector connector;
+    protected HttpClient client;
+
+    protected void start(ServerSessionListener listener) throws Exception
+    {
+        prepareServer(new RawHTTP2ServerConnectionFactory(new HttpConfiguration(), listener));
+        server.start();
+        prepareClient();
+        client.start();
+    }
+
+    protected void start(Handler handler) throws Exception
+    {
+        prepareServer(new HTTP2ServerConnectionFactory(new HttpConfiguration()));
+        server.setHandler(handler);
+        server.start();
+        prepareClient();
+        client.start();
+    }
+
+    protected void prepareServer(ConnectionFactory connectionFactory)
+    {
+        QueuedThreadPool serverExecutor = new QueuedThreadPool();
+        serverExecutor.setName("server");
+        server = new Server(serverExecutor);
+        connector = new ServerConnector(server, 1, 1, connectionFactory);
+        server.addConnector(connector);
+    }
+
+    protected void prepareClient() throws Exception
+    {
+        client = new HttpClient(new HttpClientTransportOverHTTP2(new HTTP2Client()), null);
+        QueuedThreadPool clientExecutor = new QueuedThreadPool();
+        clientExecutor.setName("client");
+        client.setExecutor(clientExecutor);
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        if (client != null)
+            client.stop();
+        if (server != null)
+            server.stop();
+    }
+}
diff --git a/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/DirectHTTP2OverTLSTest.java b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/DirectHTTP2OverTLSTest.java
new file mode 100644
index 0000000..ea5f564
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/DirectHTTP2OverTLSTest.java
@@ -0,0 +1,132 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client.http;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class DirectHTTP2OverTLSTest
+{
+    @Rule
+    public TestTracker tracker = new TestTracker();
+    private Server server;
+    private ServerConnector connector;
+    private HttpClient client;
+
+    private void start(Handler handler) throws Exception
+    {
+        startServer(handler);
+        startClient();
+    }
+
+    private void startServer(Handler handler) throws Exception
+    {
+        QueuedThreadPool serverThreads = new QueuedThreadPool();
+        serverThreads.setName("server");
+        server = new Server(serverThreads);
+        HttpConfiguration httpsConfig = new HttpConfiguration();
+        httpsConfig.addCustomizer(new SecureRequestCustomizer());
+        ConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
+        ConnectionFactory ssl = new SslConnectionFactory(newSslContextFactory(), h2.getProtocol());
+        connector = new ServerConnector(server, 1, 1, ssl, h2);
+        server.addConnector(connector);
+        server.setHandler(handler);
+        server.start();
+    }
+
+    private void startClient() throws Exception
+    {
+        QueuedThreadPool clientThreads = new QueuedThreadPool();
+        clientThreads.setName("client");
+        HttpClientTransportOverHTTP2 transport = new HttpClientTransportOverHTTP2(new HTTP2Client());
+        transport.setUseALPN(false);
+        client = new HttpClient(transport, newSslContextFactory());
+        client.setExecutor(clientThreads);
+        client.start();
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        if (client != null)
+            client.stop();
+        if (server != null)
+            server.stop();
+    }
+
+    private SslContextFactory newSslContextFactory()
+    {
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setUseCipherSuitesOrder(true);
+        sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
+        return sslContextFactory;
+    }
+
+    @Test
+    public void testDirectHTTP2OverTLS() throws Exception
+    {
+        // The client knows a priori that the server speaks h2 on a particular port.
+
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+            }
+        });
+
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(HttpScheme.HTTPS.asString())
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+}
diff --git a/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2Test.java b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2Test.java
new file mode 100644
index 0000000..bf9f2e7
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2Test.java
@@ -0,0 +1,552 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.client.HttpProxy;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.HTTP2Session;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.generator.Generator;
+import org.eclipse.jetty.http2.parser.ServerParser;
+import org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class HttpClientTransportOverHTTP2Test extends AbstractTest
+{
+    @Test
+    public void testPropertiesAreForwarded() throws Exception
+    {
+        HTTP2Client http2Client = new HTTP2Client();
+        HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP2(http2Client), null);
+        Executor executor = new QueuedThreadPool();
+        httpClient.setExecutor(executor);
+        httpClient.setConnectTimeout(13);
+        httpClient.setIdleTimeout(17);
+
+        httpClient.start();
+
+        Assert.assertTrue(http2Client.isStarted());
+        Assert.assertSame(httpClient.getExecutor(), http2Client.getExecutor());
+        Assert.assertSame(httpClient.getScheduler(), http2Client.getScheduler());
+        Assert.assertSame(httpClient.getByteBufferPool(), http2Client.getByteBufferPool());
+        Assert.assertEquals(httpClient.getConnectTimeout(), http2Client.getConnectTimeout());
+        Assert.assertEquals(httpClient.getIdleTimeout(), http2Client.getIdleTimeout());
+
+        httpClient.stop();
+
+        Assert.assertTrue(http2Client.isStopped());
+    }
+
+    @Test
+    public void testRequestAbortSendsResetFrame() throws Exception
+    {
+        CountDownLatch resetLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onReset(Stream stream, ResetFrame frame)
+                    {
+                        resetLatch.countDown();
+                    }
+                };
+            }
+        });
+
+        try
+        {
+            client.newRequest("localhost", connector.getLocalPort())
+                    .onRequestCommit(request -> request.abort(new Exception("explicitly_aborted_by_test")))
+                    .send();
+            Assert.fail();
+        }
+        catch (ExecutionException x)
+        {
+            Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testResponseAbortSendsResetFrame() throws Exception
+    {
+        CountDownLatch resetLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
+                stream.headers(new HeadersFrame(stream.getId(), metaData, null, false), new Callback()
+                {
+                    @Override
+                    public void succeeded()
+                    {
+                        ByteBuffer data = ByteBuffer.allocate(1024);
+                        stream.data(new DataFrame(stream.getId(), data, false), NOOP);
+                    }
+                });
+
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onReset(Stream stream, ResetFrame frame)
+                    {
+                        resetLatch.countDown();
+                    }
+                };
+            }
+        });
+
+        try
+        {
+            client.newRequest("localhost", connector.getLocalPort())
+                    .onResponseContent((response, buffer) -> response.abort(new Exception("explicitly_aborted_by_test")))
+                    .send();
+            Assert.fail();
+        }
+        catch (ExecutionException x)
+        {
+            Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testRequestHasHTTP2Version() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                HttpVersion version = HttpVersion.fromString(request.getProtocol());
+                response.setStatus(version == HttpVersion.HTTP_2 ? HttpStatus.OK_200 : HttpStatus.INTERNAL_SERVER_ERROR_500);
+            }
+        });
+
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .onRequestBegin(request ->
+                {
+                    if (request.getVersion() != HttpVersion.HTTP_2)
+                        request.abort(new Exception("Not a HTTP/2 request"));
+                })
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    @Test
+    public void testLastStreamId() throws Exception
+    {
+        prepareServer(new RawHTTP2ServerConnectionFactory(new HttpConfiguration(), new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Map<Integer, Integer> onPreface(Session session)
+            {
+                Map<Integer, Integer> settings = new HashMap<>();
+                settings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, 1);
+                return settings;
+            }
+
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                MetaData.Request request = (MetaData.Request)frame.getMetaData();
+                if (HttpMethod.HEAD.is(request.getMethod()))
+                {
+                    stream.getSession().close(ErrorCode.REFUSED_STREAM_ERROR.code, null, Callback.NOOP);
+                }
+                else
+                {
+                    MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
+                    stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
+                }
+                return null;
+            }
+        }));
+        server.start();
+
+        CountDownLatch latch = new CountDownLatch(2);
+        AtomicInteger lastStream = new AtomicInteger();
+        AtomicReference<Stream> streamRef = new AtomicReference<>();
+        CountDownLatch streamLatch = new CountDownLatch(1);
+        client = new HttpClient(new HttpClientTransportOverHTTP2(new HTTP2Client())
+        {
+            @Override
+            protected HttpConnectionOverHTTP2 newHttpConnection(HttpDestination destination, Session session)
+            {
+                return new HttpConnectionOverHTTP2(destination, session)
+                {
+                    @Override
+                    protected HttpChannelOverHTTP2 newHttpChannel(boolean push)
+                    {
+                        return new HttpChannelOverHTTP2(getHttpDestination(), this, getSession(), push)
+                        {
+                            @Override
+                            public void setStream(Stream stream)
+                            {
+                                super.setStream(stream);
+                                streamRef.set(stream);
+                                streamLatch.countDown();
+                            }
+                        };
+                    }
+                };
+            }
+
+            @Override
+            protected void onClose(HttpConnectionOverHTTP2 connection, GoAwayFrame frame)
+            {
+                super.onClose(connection, frame);
+                lastStream.set(frame.getLastStreamId());
+                latch.countDown();
+            }
+        }, null);
+        QueuedThreadPool clientExecutor = new QueuedThreadPool();
+        clientExecutor.setName("client");
+        client.setExecutor(clientExecutor);
+        client.start();
+
+        // Prime the connection to allow client and server prefaces to be exchanged.
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .path("/zero")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        org.eclipse.jetty.client.api.Request request = client.newRequest("localhost", connector.getLocalPort())
+                .method(HttpMethod.HEAD)
+                .path("/one");
+        request.send(result ->
+        {
+            if (result.isFailed())
+                latch.countDown();
+        });
+
+        Assert.assertTrue(streamLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+
+        Stream stream = streamRef.get();
+        Assert.assertNotNull(stream);
+        Assert.assertEquals(lastStream.get(), stream.getId());
+    }
+
+    @Test
+    public void testAbsoluteFormTarget() throws Exception
+    {
+        String path = "/path";
+        String query = "a=b";
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                Assert.assertEquals(path, request.getRequestURI());
+                Assert.assertEquals(query, request.getQueryString());
+            }
+        });
+
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .path("http://localhost:" + connector.getLocalPort() + path + "?" + query)
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    @Test
+    public void testRequestViaForwardHttpProxy() throws Exception
+    {
+        String path = "/path";
+        String query = "a=b";
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                Assert.assertEquals(path, request.getRequestURI());
+                Assert.assertEquals(query, request.getQueryString());
+            }
+        });
+
+        int proxyPort = connector.getLocalPort();
+        client.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort));
+
+        int serverPort = proxyPort + 1; // Any port will do, just not the same as the proxy.
+        ContentResponse response = client.newRequest("localhost", serverPort)
+                .path(path + "?" + query)
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    @Test
+    public void testConnectionIdleTimeoutSendsResetFrame() throws Exception
+    {
+        long idleTimeout = 1000;
+
+        CountDownLatch resetLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onReset(Stream stream, ResetFrame frame)
+                    {
+                        resetLatch.countDown();
+                    }
+                };
+            }
+        });
+        client.stop();
+        client.setIdleTimeout(idleTimeout);
+        client.start();
+
+        try
+        {
+            client.newRequest("localhost", connector.getLocalPort())
+                    // Make sure the connection idle times out, not the stream.
+                    .idleTimeout(2 * idleTimeout, TimeUnit.MILLISECONDS)
+                    .send();
+            Assert.fail();
+        }
+        catch (ExecutionException e)
+        {
+            // Expected.
+        }
+
+        Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testRequestIdleTimeoutSendsResetFrame() throws Exception
+    {
+        CountDownLatch resetLatch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                return new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onReset(Stream stream, ResetFrame frame)
+                    {
+                        resetLatch.countDown();
+                    }
+                };
+            }
+        });
+
+        try
+        {
+            long idleTimeout = 1000;
+            client.newRequest("localhost", connector.getLocalPort())
+                    .idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
+                    .send();
+            Assert.fail();
+        }
+        catch (ExecutionException e)
+        {
+            // Expected.
+        }
+
+        Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testClientStopsServerDoesNotCloseClientCloses() throws Exception
+    {
+        try (ServerSocket server = new ServerSocket(0))
+        {
+            List<Session> sessions = new ArrayList<>();
+            HTTP2Client h2Client = new HTTP2Client();
+            HttpClient client = new HttpClient(new HttpClientTransportOverHTTP2(h2Client)
+            {
+                @Override
+                protected HttpConnectionOverHTTP2 newHttpConnection(HttpDestination destination, Session session)
+                {
+                    sessions.add(session);
+                    return super.newHttpConnection(destination, session);
+                }
+            }, null);
+            QueuedThreadPool clientExecutor = new QueuedThreadPool();
+            clientExecutor.setName("client");
+            client.setExecutor(clientExecutor);
+            client.start();
+
+            CountDownLatch resultLatch = new CountDownLatch(1);
+            client.newRequest("localhost", server.getLocalPort())
+                    .send(result ->
+                    {
+                        if (result.getResponse().getStatus() == HttpStatus.OK_200)
+                            resultLatch.countDown();
+                    });
+
+            ByteBufferPool byteBufferPool = new MappedByteBufferPool();
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            Generator generator = new Generator(byteBufferPool);
+
+            try (Socket socket = server.accept())
+            {
+                socket.setSoTimeout(1000);
+                OutputStream output = socket.getOutputStream();
+                InputStream input = socket.getInputStream();
+
+                ServerParser parser = new ServerParser(byteBufferPool, new ServerParser.Listener.Adapter()
+                {
+                    @Override
+                    public void onHeaders(HeadersFrame request)
+                    {
+                        // Server's preface.
+                        generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+                        // Reply to client's SETTINGS.
+                        generator.control(lease, new SettingsFrame(new HashMap<>(), true));
+                        // Response.
+                        MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
+                        HeadersFrame response = new HeadersFrame(request.getStreamId(), metaData, null, true);
+                        generator.control(lease, response);
+
+                        try
+                        {
+                            // Write the frames.
+                            for (ByteBuffer buffer : lease.getByteBuffers())
+                                output.write(BufferUtil.toArray(buffer));
+                        }
+                        catch (Throwable x)
+                        {
+                            x.printStackTrace();
+                        }
+                    }
+                }, 4096, 8192);
+
+                byte[] bytes = new byte[1024];
+                while (true)
+                {
+                    try
+                    {
+                        int read = input.read(bytes);
+                        if (read < 0)
+                            Assert.fail();
+                        parser.parse(ByteBuffer.wrap(bytes, 0, read));
+                    }
+                    catch (SocketTimeoutException x)
+                    {
+                        break;
+                    }
+                }
+
+                Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+
+                // The client will send a GO_AWAY, but the server will not close.
+                client.stop();
+
+                // Give some time to process the stop/close operations.
+                Thread.sleep(1000);
+
+                Assert.assertTrue(h2Client.getBeans(Session.class).isEmpty());
+
+                for (Session session : sessions)
+                {
+                    Assert.assertTrue(session.isClosed());
+                    Assert.assertTrue(((HTTP2Session)session).isDisconnected());
+                }
+            }
+        }
+    }
+
+    @Ignore
+    @Test
+    public void testExternalServer() throws Exception
+    {
+        HTTP2Client http2Client = new HTTP2Client();
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP2(http2Client), sslContextFactory);
+        Executor executor = new QueuedThreadPool();
+        httpClient.setExecutor(executor);
+
+        httpClient.start();
+
+//        ContentResponse response = httpClient.GET("https://http2.akamai.com/");
+        ContentResponse response = httpClient.GET("https://webtide.com/");
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        httpClient.stop();
+    }
+}
diff --git a/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/MaxConcurrentStreamsTest.java b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/MaxConcurrentStreamsTest.java
new file mode 100644
index 0000000..7619e2f
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/MaxConcurrentStreamsTest.java
@@ -0,0 +1,184 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client.http;
+
+import java.io.IOException;
+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.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MaxConcurrentStreamsTest extends AbstractTest
+{
+    private void start(int maxConcurrentStreams, Handler handler) throws Exception
+    {
+        HTTP2ServerConnectionFactory http2 = new HTTP2ServerConnectionFactory(new HttpConfiguration());
+        http2.setMaxConcurrentStreams(maxConcurrentStreams);
+        prepareServer(http2);
+        server.setHandler(handler);
+        server.start();
+        prepareClient();
+        client.start();
+    }
+
+    @Test
+    public void testOneConcurrentStream() throws Exception
+    {
+        long sleep = 1000;
+        start(1, new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                // Sleep a bit to allow the second request to be queued.
+                sleep(sleep);
+            }
+        });
+
+        primeConnection();
+
+        CountDownLatch latch = new CountDownLatch(2);
+
+        // First request is sent immediately.
+        client.newRequest("localhost", connector.getLocalPort())
+                .path("/first")
+                .send(result ->
+                {
+                    if (result.isSucceeded())
+                        latch.countDown();
+                });
+
+        // Second request is queued.
+        client.newRequest("localhost", connector.getLocalPort())
+                .path("/second")
+                .send(result ->
+                {
+                    if (result.isSucceeded())
+                        latch.countDown();
+                });
+
+        // When the first request returns, the second must be sent.
+        Assert.assertTrue(latch.await(5 * sleep, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testTwoConcurrentStreamsThirdWaits() throws Exception
+    {
+        int maxStreams = 2;
+        long sleep = 1000;
+        start(maxStreams, new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                sleep(sleep);
+            }
+        });
+
+        primeConnection();
+
+        // Send requests up to the max allowed.
+        for (int i = 0; i < maxStreams; ++i)
+        {
+            client.newRequest("localhost", connector.getLocalPort())
+                    .path("/" + i)
+                    .send(null);
+        }
+
+        // Send the request in excess.
+        CountDownLatch latch = new CountDownLatch(1);
+        String path = "/excess";
+        client.newRequest("localhost", connector.getLocalPort())
+                .path(path)
+                .send(result ->
+                {
+                    if (result.getResponse().getStatus() == HttpStatus.OK_200)
+                        latch.countDown();
+                });
+
+        // The last exchange should remain in the queue.
+        HttpDestinationOverHTTP2 destination = (HttpDestinationOverHTTP2)client.getDestination("http", "localhost", connector.getLocalPort());
+        Assert.assertEquals(1, destination.getHttpExchanges().size());
+        Assert.assertEquals(path, destination.getHttpExchanges().peek().getRequest().getPath());
+
+        Assert.assertTrue(latch.await(5 * sleep, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testAbortedWhileQueued() throws Exception
+    {
+        long sleep = 1000;
+        start(1, new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                sleep(sleep);
+            }
+        });
+
+        primeConnection();
+
+        // Send a request that is aborted while queued.
+        client.newRequest("localhost", connector.getLocalPort())
+                .path("/aborted")
+                .onRequestQueued(request -> request.abort(new Exception()))
+                .send(null);
+
+        // Must be able to send another request.
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort()).path("/check").send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    private void primeConnection() throws Exception
+    {
+        // Prime the connection so that the maxConcurrentStream setting arrives to the client.
+        client.newRequest("localhost", connector.getLocalPort()).path("/prime").send();
+        // Wait for the server to clean up and remove the stream that primes the connection.
+        sleep(1000);
+    }
+
+    private void sleep(long time)
+    {
+        try
+        {
+            Thread.sleep(time);
+        }
+        catch (InterruptedException x)
+        {
+            throw new RuntimeException(x);
+        }
+    }
+}
diff --git a/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/PushedResourcesTest.java b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/PushedResourcesTest.java
new file mode 100644
index 0000000..b467d6d
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/PushedResourcesTest.java
@@ -0,0 +1,213 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.client.http;
+
+import java.io.IOException;
+import java.util.Random;
+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.client.HttpRequest;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.util.BufferingResponseListener;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PushedResourcesTest extends AbstractTest
+{
+    @Test
+    public void testPushedResourceCancelled() throws Exception
+    {
+        String pushPath = "/secondary";
+        CountDownLatch latch = new CountDownLatch(1);
+        start(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                HttpURI pushURI = new HttpURI("http://localhost:" + connector.getLocalPort() + pushPath);
+                MetaData.Request pushRequest = new MetaData.Request(HttpMethod.GET.asString(), pushURI, HttpVersion.HTTP_2, new HttpFields());
+                stream.push(new PushPromiseFrame(stream.getId(), 0, pushRequest), new Promise.Adapter<Stream>()
+                {
+                    @Override
+                    public void succeeded(Stream pushStream)
+                    {
+                        // Just send the normal response and wait for the reset.
+                        MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
+                        stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
+                    }
+                }, new Stream.Listener.Adapter()
+                {
+                    @Override
+                    public void onReset(Stream stream, ResetFrame frame)
+                    {
+                        latch.countDown();
+                    }
+                });
+                return null;
+            }
+        });
+
+        HttpRequest request = (HttpRequest)client.newRequest("localhost", connector.getLocalPort());
+        ContentResponse response = request
+                .pushListener((mainRequest, pushedRequest) -> null)
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testPushedResources() throws Exception
+    {
+        Random random = new Random();
+        byte[] bytes = new byte[512];
+        random.nextBytes(bytes);
+        byte[] pushBytes1 = new byte[1024];
+        random.nextBytes(pushBytes1);
+        byte[] pushBytes2 = new byte[2048];
+        random.nextBytes(pushBytes2);
+
+        String path1 = "/secondary1";
+        String path2 = "/secondary2";
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                if (target.equals(path1))
+                {
+                    response.getOutputStream().write(pushBytes1);
+                }
+                else if (target.equals(path2))
+                {
+                    response.getOutputStream().write(pushBytes2);
+                }
+                else
+                {
+                    baseRequest.getPushBuilder()
+                            .path(path1)
+                            .push();
+                    baseRequest.getPushBuilder()
+                            .path(path2)
+                            .push();
+                    response.getOutputStream().write(bytes);
+                }
+            }
+        });
+
+        CountDownLatch latch1 = new CountDownLatch(1);
+        CountDownLatch latch2 = new CountDownLatch(1);
+        HttpRequest request = (HttpRequest)client.newRequest("localhost", connector.getLocalPort());
+        ContentResponse response = request
+                .pushListener((mainRequest, pushedRequest) -> new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        Assert.assertTrue(result.isSucceeded());
+                        if (pushedRequest.getPath().equals(path1))
+                        {
+                            Assert.assertArrayEquals(pushBytes1, getContent());
+                            latch1.countDown();
+                        }
+                        else if (pushedRequest.getPath().equals(path2))
+                        {
+                            Assert.assertArrayEquals(pushBytes2, getContent());
+                            latch2.countDown();
+                        }
+                    }
+                })
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+        Assert.assertArrayEquals(bytes, response.getContent());
+        Assert.assertTrue(latch1.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(latch2.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testPushedResourceRedirect() throws Exception
+    {
+        Random random = new Random();
+        byte[] pushBytes = new byte[512];
+        random.nextBytes(pushBytes);
+
+        String oldPath = "/old";
+        String newPath = "/new";
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                if (target.equals(oldPath))
+                    response.sendRedirect(newPath);
+                else if (target.equals(newPath))
+                    response.getOutputStream().write(pushBytes);
+                else
+                    baseRequest.getPushBuilder().path(oldPath).push();
+            }
+        });
+
+        CountDownLatch latch = new CountDownLatch(1);
+        HttpRequest request = (HttpRequest)client.newRequest("localhost", connector.getLocalPort());
+        ContentResponse response = request
+                .pushListener((mainRequest, pushedRequest) -> new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        Assert.assertTrue(result.isSucceeded());
+                        Assert.assertEquals(oldPath, pushedRequest.getPath());
+                        Assert.assertEquals(newPath, result.getRequest().getPath());
+                        Assert.assertArrayEquals(pushBytes, getContent());
+                        latch.countDown();
+                    }
+                })
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+}
diff --git a/jetty-http2/http2-http-client-transport/src/test/resources/jetty-logging.properties b/jetty-http2/http2-http-client-transport/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..287d283
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/test/resources/jetty-logging.properties
@@ -0,0 +1,5 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.client.LEVEL=DEBUG
+org.eclipse.jetty.http2.hpack.LEVEL=INFO
+#org.eclipse.jetty.http2.LEVEL=DEBUG
+#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
diff --git a/jetty-spdy/spdy-http-client-transport/src/test/resources/keystore.jks b/jetty-http2/http2-http-client-transport/src/test/resources/keystore.jks
similarity index 100%
rename from jetty-spdy/spdy-http-client-transport/src/test/resources/keystore.jks
rename to jetty-http2/http2-http-client-transport/src/test/resources/keystore.jks
Binary files differ
diff --git a/jetty-http2/http2-server/pom.xml b/jetty-http2/http2-server/pom.xml
new file mode 100644
index 0000000..82bf5a5
--- /dev/null
+++ b/jetty-http2/http2-server/pom.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.http2</groupId>
+    <artifactId>http2-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>http2-server</artifactId>
+  <name>Jetty :: HTTP2 :: Server</name>
+
+ <properties>
+    <bundle-symbolic-name>${project.groupId}.server</bundle-symbolic-name>
+  </properties>
+
+  <build>
+    <plugins>
+    </plugins>
+  </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.eclipse.jetty.http2</groupId>
+            <artifactId>http2-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.alpn</groupId>
+            <artifactId>alpn-api</artifactId>
+            <version>${alpn.api.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-servlet</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-servlets</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-alpn-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.toolchain</groupId>
+            <artifactId>jetty-test-helper</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/jetty-http2/http2-server/src/main/config/etc/jetty-http2.xml b/jetty-http2/http2-server/src/main/config/etc/jetty-http2.xml
new file mode 100644
index 0000000..064a204
--- /dev/null
+++ b/jetty-http2/http2-server/src/main/config/etc/jetty-http2.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<!-- ============================================================= -->
+<!-- Configure a HTTP2 on the ssl connector.                       -->
+<!-- ============================================================= -->
+<Configure id="sslConnector" class="org.eclipse.jetty.server.ServerConnector">
+  <Call name="addConnectionFactory">
+    <Arg>
+      <New class="org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory">
+        <Arg name="config"><Ref refid="sslHttpConfig"/></Arg>
+        <Set name="maxConcurrentStreams"><Property name="jetty.http2.maxConcurrentStreams" deprecated="http2.maxConcurrentStreams" default="1024"/></Set>
+        <Set name="initialStreamRecvWindow"><Property name="jetty.http2.initialStreamRecvWindow" deprecated="jetty.http2.initialStreamSendWindow" default="65535"/></Set>
+      </New>
+    </Arg>
+  </Call>
+
+  <Ref refid="sslContextFactory">
+    <Set name="CipherComparator">
+      <Get class="org.eclipse.jetty.http2.HTTP2Cipher" name="COMPARATOR"/>
+    </Set>
+    <Set name="useCipherSuitesOrder">true</Set>
+  </Ref>
+
+</Configure>
diff --git a/jetty-http2/http2-server/src/main/config/etc/jetty-http2c.xml b/jetty-http2/http2-server/src/main/config/etc/jetty-http2c.xml
new file mode 100644
index 0000000..8e53064
--- /dev/null
+++ b/jetty-http2/http2-server/src/main/config/etc/jetty-http2c.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<!-- ============================================================= -->
+<!-- Configure a HTTP2 on the ssl connector.                       -->
+<!-- ============================================================= -->
+<Configure id="httpConnector" class="org.eclipse.jetty.server.ServerConnector">
+  <Call name="addConnectionFactory">
+    <Arg>
+      <New class="org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory">
+        <Arg name="config"><Ref refid="httpConfig"/></Arg>
+        <Set name="maxConcurrentStreams"><Property name="jetty.http2c.maxConcurrentStreams" deprecated="http2.maxConcurrentStreams" default="1024"/></Set>
+        <Set name="initialStreamRecvWindow"><Property name="jetty.http2c.initialStreamRecvWindow" deprecated="jetty.http2c.initialStreamSendWindow" default="65535"/></Set>
+      </New>
+    </Arg>
+  </Call>
+</Configure>
diff --git a/jetty-http2/http2-server/src/main/config/modules/http2.mod b/jetty-http2/http2-server/src/main/config/modules/http2.mod
new file mode 100644
index 0000000..6b2b0cd
--- /dev/null
+++ b/jetty-http2/http2-server/src/main/config/modules/http2.mod
@@ -0,0 +1,20 @@
+#
+# HTTP2 Support Module
+#
+
+[depend]
+ssl
+alpn
+
+[lib]
+lib/http2/*.jar
+
+[xml]
+etc/jetty-http2.xml
+
+[ini-template]
+## Max number of concurrent streams per connection
+# jetty.http2.maxConcurrentStreams=1024
+
+## Initial stream receive window (client to server)
+# jetty.http2.initialStreamRecvWindow=65535
diff --git a/jetty-http2/http2-server/src/main/config/modules/http2c.mod b/jetty-http2/http2-server/src/main/config/modules/http2c.mod
new file mode 100644
index 0000000..b6a3eab
--- /dev/null
+++ b/jetty-http2/http2-server/src/main/config/modules/http2c.mod
@@ -0,0 +1,22 @@
+#
+# HTTP2 Clear Text Support Module
+# This module adds support for HTTP/2 clear text to the
+# HTTP/1 clear text connector (defined in jetty-http.xml).
+# The resulting connector will accept both HTTP/1 and HTTP/2 connections.
+#
+
+[depend]
+http
+
+[lib]
+lib/http2/*.jar
+
+[xml]
+etc/jetty-http2c.xml
+
+[ini-template]
+## Max number of concurrent streams per connection
+# jetty.http2c.maxConcurrentStreams=1024
+
+## Initial stream receive window (client to server)
+# jetty.http2c.initialStreamRecvWindow=65535
diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java
new file mode 100644
index 0000000..0783256
--- /dev/null
+++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java
@@ -0,0 +1,236 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import java.util.Objects;
+
+import org.eclipse.jetty.http2.BufferingFlowControlStrategy;
+import org.eclipse.jetty.http2.FlowControlStrategy;
+import org.eclipse.jetty.http2.HTTP2Connection;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.generator.Generator;
+import org.eclipse.jetty.http2.parser.ServerParser;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.AbstractConnectionFactory;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.annotation.Name;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
+import org.eclipse.jetty.util.thread.strategy.ProduceExecuteConsume;
+
+@ManagedObject
+public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConnectionFactory
+{
+    private final Connection.Listener connectionListener = new ConnectionListener();
+    private final HttpConfiguration httpConfiguration;
+    private int maxDynamicTableSize = 4096;
+    private int initialStreamRecvWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
+    private int initialSessionRecvWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
+    private int maxConcurrentStreams = 128;
+    private int maxHeaderBlockFragment = 0;
+    private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F);
+    private ExecutionStrategy.Factory executionStrategyFactory = new ProduceExecuteConsume.Factory();
+    private long streamIdleTimeout;
+
+    public AbstractHTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration)
+    {
+        this(httpConfiguration,"h2","h2-17","h2-16","h2-15","h2-14");
+    }
+
+    protected AbstractHTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration, String... protocols)
+    {
+        super(protocols);
+        this.httpConfiguration = Objects.requireNonNull(httpConfiguration);
+        addBean(httpConfiguration);
+    }
+
+    @ManagedAttribute("The HPACK dynamic table maximum size")
+    public int getMaxDynamicTableSize()
+    {
+        return maxDynamicTableSize;
+    }
+
+    public void setMaxDynamicTableSize(int maxDynamicTableSize)
+    {
+        this.maxDynamicTableSize = maxDynamicTableSize;
+    }
+
+    @ManagedAttribute("The initial size of session's flow control receive window")
+    public int getInitialSessionRecvWindow()
+    {
+        return initialSessionRecvWindow;
+    }
+
+    public void setInitialSessionRecvWindow(int initialSessionRecvWindow)
+    {
+        this.initialSessionRecvWindow = initialSessionRecvWindow;
+    }
+
+    @ManagedAttribute("The initial size of stream's flow control receive window")
+    public int getInitialStreamRecvWindow()
+    {
+        return initialStreamRecvWindow;
+    }
+
+    public void setInitialStreamRecvWindow(int initialStreamRecvWindow)
+    {
+        this.initialStreamRecvWindow = initialStreamRecvWindow;
+    }
+
+    /**
+     * @deprecated use {@link #getInitialStreamRecvWindow()} instead,
+     * since "send" is meant on the client, but this is the server configuration
+     */
+    @Deprecated
+    public int getInitialStreamSendWindow()
+    {
+        return getInitialStreamRecvWindow();
+    }
+
+    /**
+     * @deprecated use {@link #setInitialStreamRecvWindow(int)} instead,
+     * since "send" is meant on the client, but this is the server configuration
+     */
+    @Deprecated
+    public void setInitialStreamSendWindow(int initialStreamSendWindow)
+    {
+        setInitialStreamRecvWindow(initialStreamSendWindow);
+    }
+
+    @ManagedAttribute("The max number of concurrent streams per session")
+    public int getMaxConcurrentStreams()
+    {
+        return maxConcurrentStreams;
+    }
+
+    public void setMaxConcurrentStreams(int maxConcurrentStreams)
+    {
+        this.maxConcurrentStreams = maxConcurrentStreams;
+    }
+
+    public int getMaxHeaderBlockFragment()
+    {
+        return maxHeaderBlockFragment;
+    }
+
+    public void setMaxHeaderBlockFragment(int maxHeaderBlockFragment)
+    {
+        this.maxHeaderBlockFragment = maxHeaderBlockFragment;
+    }
+
+    public FlowControlStrategy.Factory getFlowControlStrategyFactory()
+    {
+        return flowControlStrategyFactory;
+    }
+
+    public void setFlowControlStrategyFactory(FlowControlStrategy.Factory flowControlStrategyFactory)
+    {
+        this.flowControlStrategyFactory = flowControlStrategyFactory;
+    }
+
+    public ExecutionStrategy.Factory getExecutionStrategyFactory()
+    {
+        return executionStrategyFactory;
+    }
+
+    public void setExecutionStrategyFactory(ExecutionStrategy.Factory executionStrategyFactory)
+    {
+        this.executionStrategyFactory = executionStrategyFactory;
+    }
+
+    @ManagedAttribute("The stream idle timeout in milliseconds")
+    public long getStreamIdleTimeout()
+    {
+        return streamIdleTimeout;
+    }
+
+    public void setStreamIdleTimeout(long streamIdleTimeout)
+    {
+        this.streamIdleTimeout = streamIdleTimeout;
+    }
+
+    public HttpConfiguration getHttpConfiguration()
+    {
+        return httpConfiguration;
+    }
+
+    @Override
+    public Connection newConnection(Connector connector, EndPoint endPoint)
+    {
+        ServerSessionListener listener = newSessionListener(connector, endPoint);
+
+        Generator generator = new Generator(connector.getByteBufferPool(), getMaxDynamicTableSize(), getMaxHeaderBlockFragment());
+        FlowControlStrategy flowControl = newFlowControlStrategy();
+        if (flowControl == null)
+            flowControl = getFlowControlStrategyFactory().newFlowControlStrategy();
+        HTTP2ServerSession session = new HTTP2ServerSession(connector.getScheduler(), endPoint, generator, listener, flowControl);
+        session.setMaxLocalStreams(getMaxConcurrentStreams());
+        session.setMaxRemoteStreams(getMaxConcurrentStreams());
+        // For a single stream in a connection, there will be a race between
+        // the stream idle timeout and the connection idle timeout. However,
+        // the typical case is that the connection will be busier and the
+        // stream idle timeout will expire earlier than the connection's.
+        long streamIdleTimeout = getStreamIdleTimeout();
+        if (streamIdleTimeout <= 0)
+            streamIdleTimeout = endPoint.getIdleTimeout();
+        session.setStreamIdleTimeout(streamIdleTimeout);
+        session.setInitialSessionRecvWindow(getInitialSessionRecvWindow());
+
+        ServerParser parser = newServerParser(connector, session);
+        HTTP2Connection connection = new HTTP2ServerConnection(connector.getByteBufferPool(), connector.getExecutor(),
+                        endPoint, httpConfiguration, parser, session, getInputBufferSize(), getExecutionStrategyFactory(), listener);
+        connection.addListener(connectionListener);
+        return configure(connection, connector, endPoint);
+    }
+
+    /**
+     * @deprecated use {@link #setFlowControlStrategyFactory(FlowControlStrategy.Factory)} instead
+     */
+    @Deprecated
+    protected FlowControlStrategy newFlowControlStrategy()
+    {
+        return null;
+    }
+
+    protected abstract ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint);
+
+    protected ServerParser newServerParser(Connector connector, ServerParser.Listener listener)
+    {
+        return new ServerParser(connector.getByteBufferPool(), listener, getMaxDynamicTableSize(), getHttpConfiguration().getRequestHeaderSize());
+    }
+
+    private class ConnectionListener implements Connection.Listener
+    {
+        @Override
+        public void onOpened(Connection connection)
+        {
+            addManaged((LifeCycle)((HTTP2Connection)connection).getSession());
+        }
+
+        @Override
+        public void onClosed(Connection connection)
+        {
+            removeBean(((HTTP2Connection)connection).getSession());
+        }
+    }
+}
diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2CServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2CServerConnectionFactory.java
new file mode 100644
index 0000000..a02376d
--- /dev/null
+++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2CServerConnectionFactory.java
@@ -0,0 +1,78 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.MetaData.Request;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.util.annotation.Name;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** HTTP2 Clear Text Connection factory.
+ * <p>This extension of HTTP2ServerConnection Factory sets the
+ * protocol name to "h2c" as used by the clear text upgrade mechanism
+ * for HTTP2 and marks all TLS ciphers as unacceptable.
+ * </p>
+ * <p>If used in combination with a {@link HttpConnectionFactory} as the
+ * default protocol, this factory can support the non-standard direct
+ * update mechanism, where a HTTP1 request of the form "PRI * HTTP/2.0"
+ * is used to trigger a switch to a HTTP2 connection.    This approach
+ * allows a single port to accept either HTTP/1 or HTTP/2 direct
+ * connections.
+ */
+public class HTTP2CServerConnectionFactory extends HTTP2ServerConnectionFactory implements ConnectionFactory.Upgrading
+{
+    private static final Logger LOG = Log.getLogger(HTTP2CServerConnectionFactory.class);
+
+    public HTTP2CServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration)
+    {
+        super(httpConfiguration,"h2c","h2c-17","h2c-16","h2c-15","h2c-14");
+    }
+
+    @Override
+    public boolean isAcceptable(String protocol, String tlsProtocol, String tlsCipher)
+    {
+        // Never use TLS with h2c
+        return false;
+    }
+
+    @Override
+    public Connection upgradeConnection(Connector connector, EndPoint endPoint, Request request, HttpFields response101) throws BadMessageException
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} upgraded {}{}", this, request.toString(), request.getFields());
+
+        if (request.getContentLength() > 0)
+            return null;
+
+        HTTP2ServerConnection connection = (HTTP2ServerConnection)newConnection(connector, endPoint);
+        if (connection.upgrade(request))
+            return connection;
+        return null;
+    }
+}
diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java
new file mode 100644
index 0000000..e8f1299
--- /dev/null
+++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java
@@ -0,0 +1,322 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http.MetaData.Request;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.HTTP2Connection;
+import org.eclipse.jetty.http2.ISession;
+import org.eclipse.jetty.http2.IStream;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PrefaceFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.parser.ServerParser;
+import org.eclipse.jetty.http2.parser.SettingsBodyParser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.util.B64Code;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
+
+public class HTTP2ServerConnection extends HTTP2Connection implements Connection.UpgradeTo
+{
+    private final Queue<HttpChannelOverHTTP2> channels = new ArrayDeque<>();
+    private final List<Frame> upgradeFrames = new ArrayList<>();
+    private final AtomicLong totalRequests = new AtomicLong();
+    private final AtomicLong totalResponses = new AtomicLong();
+    private final ServerSessionListener listener;
+    private final HttpConfiguration httpConfig;
+
+    public HTTP2ServerConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, HttpConfiguration httpConfig, ServerParser parser, ISession session, int inputBufferSize, ServerSessionListener listener)
+    {
+        this(byteBufferPool, executor, endPoint, httpConfig, parser, session, inputBufferSize, ExecutionStrategy.Factory.getDefault(), listener);
+    }
+
+    public HTTP2ServerConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, HttpConfiguration httpConfig, ServerParser parser, ISession session, int inputBufferSize, ExecutionStrategy.Factory executionFactory, ServerSessionListener listener)
+    {
+        super(byteBufferPool, executor, endPoint, parser, session, inputBufferSize, executionFactory);
+        this.listener = listener;
+        this.httpConfig = httpConfig;
+    }
+
+    @Override
+    public int getMessagesIn()
+    {
+        return totalRequests.intValue();
+    }
+
+    @Override
+    public int getMessagesOut()
+    {
+        return totalResponses.intValue();
+    }
+
+    @Override
+    protected ServerParser getParser()
+    {
+        return (ServerParser)super.getParser();
+    }
+
+    @Override
+    public void onUpgradeTo(ByteBuffer buffer)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("HTTP2 onUpgradeTo {} {}", this, BufferUtil.toDetailString(buffer));
+        setInputBuffer(buffer);
+    }
+
+    @Override
+    public void onOpen()
+    {
+        notifyAccept(getSession());
+        for (Frame frame : upgradeFrames)
+            getSession().onFrame(frame);
+        super.onOpen();
+    }
+
+    private void notifyAccept(ISession session)
+    {
+        try
+        {
+            listener.onAccept(session);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+        }
+    }
+
+    public void onNewStream(Connector connector, IStream stream, HeadersFrame frame)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Processing {} on {}", frame, stream);
+        HttpChannelOverHTTP2 channel = provideHttpChannel(connector, stream);
+        Runnable task = channel.onRequest(frame);
+        if (task != null)
+            offerTask(task, false);
+    }
+
+    public void onData(IStream stream, DataFrame frame, Callback callback)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Processing {} on {}", frame, stream);
+        HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
+        if (channel != null)
+        {
+            Runnable task = channel.onRequestContent(frame, callback);
+            if (task != null)
+                offerTask(task, false);
+        }
+    }
+
+    public boolean onStreamTimeout(IStream stream, Throwable failure)
+    {
+        HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
+        boolean result = channel != null && channel.onStreamTimeout(failure);
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} idle timeout on {}: {}", result ? "Processed" : "Ignored", stream, failure);
+        return result;
+    }
+
+    public void onStreamFailure(IStream stream, Throwable failure)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Processing failure on {}: {}", stream, failure);
+        HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
+        if (channel != null)
+            channel.onFailure(failure);
+    }
+
+    public boolean onSessionTimeout(Throwable failure)
+    {
+        ISession session = getSession();
+        boolean result = true;
+        for (Stream stream : session.getStreams())
+        {
+            HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
+            if (channel != null)
+                result &= !channel.isRequestHandled();
+        }
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} idle timeout on {}: {}", result ? "Processed" : "Ignored", session, failure);
+        return result;
+    }
+
+    public void onSessionFailure(Throwable failure)
+    {
+        ISession session = getSession();
+        if (LOG.isDebugEnabled())
+            LOG.debug("Processing failure on {}: {}", session, failure);
+        for (Stream stream : session.getStreams())
+            onStreamFailure((IStream)stream, failure);
+    }
+
+    public void push(Connector connector, IStream stream, MetaData.Request request)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Processing push {} on {}", request, stream);
+        HttpChannelOverHTTP2 channel = provideHttpChannel(connector, stream);
+        Runnable task = channel.onPushRequest(request);
+        if (task != null)
+            offerTask(task, true);
+    }
+
+    private HttpChannelOverHTTP2 provideHttpChannel(Connector connector, IStream stream)
+    {
+        HttpChannelOverHTTP2 channel = pollChannel();
+        if (channel != null)
+        {
+            channel.getHttpTransport().setStream(stream);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Recycling channel {} for {}", channel, this);
+        }
+        else
+        {
+            HttpTransportOverHTTP2 transport = new HttpTransportOverHTTP2(connector, this);
+            transport.setStream(stream);
+            channel = newServerHttpChannelOverHTTP2(connector, httpConfig, transport);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Creating channel {} for {}", channel, this);
+        }
+        stream.setAttribute(IStream.CHANNEL_ATTRIBUTE, channel);
+        return channel;
+    }
+
+    protected ServerHttpChannelOverHTTP2 newServerHttpChannelOverHTTP2(Connector connector, HttpConfiguration httpConfig, HttpTransportOverHTTP2 transport)
+    {
+        return new ServerHttpChannelOverHTTP2(connector, httpConfig, getEndPoint(), transport);
+    }
+
+    private void offerChannel(HttpChannelOverHTTP2 channel)
+    {
+        synchronized (this)
+        {
+            channels.offer(channel);
+        }
+    }
+
+    private HttpChannelOverHTTP2 pollChannel()
+    {
+        synchronized (this)
+        {
+            return channels.poll();
+        }
+    }
+
+    public boolean upgrade(Request request)
+    {
+        if (HttpMethod.PRI.is(request.getMethod()))
+        {
+            getParser().directUpgrade();
+        }
+        else
+        {
+            HttpField settingsField = request.getFields().getField(HttpHeader.HTTP2_SETTINGS);
+            if (settingsField == null)
+                throw new BadMessageException("Missing " + HttpHeader.HTTP2_SETTINGS + " header");
+            String value = settingsField.getValue();
+            final byte[] settings = B64Code.decodeRFC4648URL(value == null ? "" : value);
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} settings {}",this,TypeUtil.toHexString(settings));
+
+            SettingsFrame settingsFrame = SettingsBodyParser.parseBody(BufferUtil.toBuffer(settings));
+            if (settingsFrame == null)
+            {
+                LOG.warn("Invalid {} header value: {}", HttpHeader.HTTP2_SETTINGS, value);
+                throw new BadMessageException();
+            }
+
+            getParser().standardUpgrade();
+
+            upgradeFrames.add(new PrefaceFrame());
+            upgradeFrames.add(settingsFrame);
+            // Remember the request to send a response from onOpen().
+            upgradeFrames.add(new HeadersFrame(1, new Request(request), null, true));
+        }
+        return true;
+    }
+
+    protected class ServerHttpChannelOverHTTP2 extends HttpChannelOverHTTP2 implements ExecutionStrategy.Rejectable
+    {
+        public ServerHttpChannelOverHTTP2(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransportOverHTTP2 transport)
+        {
+            super(connector, configuration, endPoint, transport);
+        }
+
+        @Override
+        public Runnable onRequest(HeadersFrame frame)
+        {
+            totalRequests.incrementAndGet();
+            return super.onRequest(frame);
+        }
+
+        @Override
+        public void onCompleted()
+        {
+            totalResponses.incrementAndGet();
+            super.onCompleted();
+            if (!getStream().isReset())
+                recycle();
+        }
+
+        @Override
+        public void recycle()
+        {
+            getStream().removeAttribute(IStream.CHANNEL_ATTRIBUTE);
+            super.recycle();
+            offerChannel(this);
+        }
+
+        @Override
+        public void reject()
+        {
+            IStream stream = getStream();
+            if (LOG.isDebugEnabled())
+                LOG.debug("HTTP2 Request #{}/{} rejected", stream.getId(), Integer.toHexString(stream.getSession().hashCode()));
+            stream.reset(new ResetFrame(stream.getId(), ErrorCode.ENHANCE_YOUR_CALM_ERROR.code), Callback.NOOP);
+            // Consume the existing queued data frames to
+            // avoid stalling the session flow control.
+            consumeInput();
+        }
+    }
+}
diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java
new file mode 100644
index 0000000..f16b9a7
--- /dev/null
+++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java
@@ -0,0 +1,184 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.IStream;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.NegotiatingServerConnection.CipherDiscriminator;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.annotation.Name;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionFactory implements CipherDiscriminator
+{
+    private static final Logger LOG = Log.getLogger(HTTP2ServerConnectionFactory.class);
+
+    public HTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration)
+    {
+        super(httpConfiguration);
+    }
+
+    protected HTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration,String... protocols)
+    {
+        super(httpConfiguration,protocols);
+    }
+
+    @Override
+    protected ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint)
+    {
+        return new HTTPServerSessionListener(connector, endPoint);
+    }
+
+    @Override
+    public boolean isAcceptable(String protocol, String tlsProtocol, String tlsCipher)
+    {
+        // TODO remove this draft 14 protection
+        // Implement 9.2.2
+        boolean acceptable = "h2-14".equals(protocol) || !(HTTP2Cipher.isBlackListProtocol(tlsProtocol) && HTTP2Cipher.isBlackListCipher(tlsCipher));
+        if (LOG.isDebugEnabled())
+            LOG.debug("proto={} tls={} cipher={} 9.2.2-acceptable={}",protocol,tlsProtocol,tlsCipher,acceptable);
+        return acceptable;
+    }
+
+    protected class HTTPServerSessionListener extends ServerSessionListener.Adapter implements Stream.Listener
+    {
+        private final Connector connector;
+        private final EndPoint endPoint;
+
+        public HTTPServerSessionListener(Connector connector, EndPoint endPoint)
+        {
+            this.connector = connector;
+            this.endPoint = endPoint;
+        }
+
+        protected HTTP2ServerConnection getConnection()
+        {
+            return (HTTP2ServerConnection)endPoint.getConnection();
+        }
+
+        @Override
+        public Map<Integer, Integer> onPreface(Session session)
+        {
+            Map<Integer, Integer> settings = new HashMap<>();
+            settings.put(SettingsFrame.HEADER_TABLE_SIZE, getMaxDynamicTableSize());
+            settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, getInitialStreamRecvWindow());
+            int maxConcurrentStreams = getMaxConcurrentStreams();
+            if (maxConcurrentStreams >= 0)
+                settings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, maxConcurrentStreams);
+            settings.put(SettingsFrame.MAX_HEADER_LIST_SIZE, getHttpConfiguration().getRequestHeaderSize());
+            return settings;
+        }
+
+        @Override
+        public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+        {
+            getConnection().onNewStream(connector, (IStream)stream, frame);
+            return this;
+        }
+
+        @Override
+        public boolean onIdleTimeout(Session session)
+        {
+            boolean close = super.onIdleTimeout(session);
+            if (!close)
+                return false;
+
+            long idleTimeout = getConnection().getEndPoint().getIdleTimeout();
+            return getConnection().onSessionTimeout(new TimeoutException("Session idle timeout " + idleTimeout + " ms"));
+        }
+
+        @Override
+        public void onClose(Session session, GoAwayFrame frame)
+        {
+            ErrorCode error = ErrorCode.from(frame.getError());
+            if (error == null)
+                error = ErrorCode.STREAM_CLOSED_ERROR;
+            String reason = frame.tryConvertPayload();
+            if (reason != null && !reason.isEmpty())
+                reason = " (" + reason + ")";
+            getConnection().onSessionFailure(new EofException("HTTP/2 " + error + reason));
+        }
+
+        @Override
+        public void onFailure(Session session, Throwable failure)
+        {
+            getConnection().onSessionFailure(failure);
+        }
+
+        @Override
+        public void onHeaders(Stream stream, HeadersFrame frame)
+        {
+            // Servers do not receive responses.
+            close(stream, "response_headers");
+        }
+
+        @Override
+        public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
+        {
+            // Servers do not receive pushes.
+            close(stream, "push_promise");
+            return null;
+        }
+
+        @Override
+        public void onData(Stream stream, DataFrame frame, Callback callback)
+        {
+            getConnection().onData((IStream)stream, frame, callback);
+        }
+
+        @Override
+        public void onReset(Stream stream, ResetFrame frame)
+        {
+            ErrorCode error = ErrorCode.from(frame.getError());
+            if (error == null)
+                error = ErrorCode.CANCEL_STREAM_ERROR;
+            getConnection().onStreamFailure((IStream)stream, new EofException("HTTP/2 " + error));
+        }
+
+        @Override
+        public boolean onIdleTimeout(Stream stream, Throwable x)
+        {
+            return getConnection().onStreamTimeout((IStream)stream, x);
+        }
+
+        private void close(Stream stream, String reason)
+        {
+            stream.getSession().close(ErrorCode.PROTOCOL_ERROR.code, reason, Callback.NOOP);
+        }
+    }
+}
diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerSession.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerSession.java
new file mode 100644
index 0000000..ecd3566
--- /dev/null
+++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerSession.java
@@ -0,0 +1,143 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.FlowControlStrategy;
+import org.eclipse.jetty.http2.HTTP2Session;
+import org.eclipse.jetty.http2.IStream;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.http2.generator.Generator;
+import org.eclipse.jetty.http2.parser.ServerParser;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Scheduler;
+
+public class HTTP2ServerSession extends HTTP2Session implements ServerParser.Listener
+{
+    private static final Logger LOG = Log.getLogger(HTTP2ServerSession.class);
+
+    private final ServerSessionListener listener;
+
+    public HTTP2ServerSession(Scheduler scheduler, EndPoint endPoint, Generator generator, ServerSessionListener listener, FlowControlStrategy flowControl)
+    {
+        super(scheduler, endPoint, generator, listener, flowControl, 2);
+        this.listener = listener;
+    }
+
+    @Override
+    public void onPreface()
+    {
+        // SPEC: send a SETTINGS frame upon receiving the preface.
+        Map<Integer, Integer> settings = notifyPreface(this);
+        if (settings == null)
+            settings = Collections.emptyMap();
+        SettingsFrame settingsFrame = new SettingsFrame(settings, false);
+
+        WindowUpdateFrame windowFrame = null;
+        int sessionWindow = getInitialSessionRecvWindow() - FlowControlStrategy.DEFAULT_WINDOW_SIZE;
+        if (sessionWindow > 0)
+        {
+            updateRecvWindow(sessionWindow);
+            windowFrame = new WindowUpdateFrame(0, sessionWindow);
+        }
+
+        if (windowFrame == null)
+            frames(null, Callback.NOOP, settingsFrame, Frame.EMPTY_ARRAY);
+        else
+            frames(null, Callback.NOOP, settingsFrame, windowFrame);
+    }
+
+    @Override
+    public void onHeaders(HeadersFrame frame)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Received {}", frame);
+
+        MetaData metaData = frame.getMetaData();
+        if (metaData.isRequest())
+        {
+            IStream stream = createRemoteStream(frame.getStreamId());
+            if (stream != null)
+            {
+                onStreamOpened(stream);
+                stream.process(frame, Callback.NOOP);
+                Stream.Listener listener = notifyNewStream(stream, frame);
+                stream.setListener(listener);
+            }
+        }
+        else
+        {
+            onConnectionFailure(ErrorCode.INTERNAL_ERROR.code, "invalid_request");
+        }
+    }
+
+    @Override
+    public void onPushPromise(PushPromiseFrame frame)
+    {
+        onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "push_promise");
+    }
+
+    private Map<Integer, Integer> notifyPreface(Session session)
+    {
+        try
+        {
+            return listener.onPreface(session);
+        }
+        catch (Throwable x)
+        {
+            LOG.info("Failure while notifying listener " + listener, x);
+            return null;
+        }
+    }
+
+    @Override
+    public void onFrame(Frame frame)
+    {
+        switch (frame.getType())
+        {
+            case PREFACE:
+                onPreface();
+                break;
+            case SETTINGS:
+                // SPEC: the required reply to this SETTINGS frame is the 101 response.
+                onSettings((SettingsFrame)frame, false);
+                break;
+            case HEADERS:
+                onHeaders((HeadersFrame)frame);
+                break;
+            default:
+                super.onFrame(frame);
+                break;
+        }
+    }
+}
diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java
new file mode 100644
index 0000000..c825089
--- /dev/null
+++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java
@@ -0,0 +1,357 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpHeaderValue;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http.PreEncodedHttpField;
+import org.eclipse.jetty.http2.IStream;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpInput;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class HttpChannelOverHTTP2 extends HttpChannel
+{
+    private static final Logger LOG = Log.getLogger(HttpChannelOverHTTP2.class);
+    private static final HttpField SERVER_VERSION = new PreEncodedHttpField(HttpHeader.SERVER, HttpConfiguration.SERVER_VERSION);
+    private static final HttpField POWERED_BY = new PreEncodedHttpField(HttpHeader.X_POWERED_BY, HttpConfiguration.SERVER_VERSION);
+
+    private boolean _expect100Continue;
+    private boolean _delayedUntilContent;
+    private boolean _handled;
+
+    public HttpChannelOverHTTP2(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransportOverHTTP2 transport)
+    {
+        super(connector, configuration, endPoint, transport);
+    }
+
+    protected IStream getStream()
+    {
+        return getHttpTransport().getStream();
+    }
+
+    @Override
+    public boolean isExpecting100Continue()
+    {
+        return _expect100Continue;
+    }
+
+    @Override
+    public void setIdleTimeout(long timeoutMs)
+    {
+        getStream().setIdleTimeout(timeoutMs);
+    }
+
+    @Override
+    public long getIdleTimeout()
+    {
+        return getStream().getIdleTimeout();
+    }
+
+    public Runnable onRequest(HeadersFrame frame)
+    {
+        try
+        {
+            MetaData.Request request = (MetaData.Request)frame.getMetaData();
+            HttpFields fields = request.getFields();
+
+            // HTTP/2 sends the Host header as the :authority
+            // pseudo-header, so we need to synthesize a Host header.
+            if (!fields.contains(HttpHeader.HOST))
+            {
+                String authority = request.getURI().getAuthority();
+                if (authority != null)
+                {
+                    // Lower-case to be consistent with other HTTP/2 headers.
+                    fields.put("host", authority);
+                }
+            }
+
+            _expect100Continue = fields.contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
+
+            HttpFields response = getResponse().getHttpFields();
+            if (getHttpConfiguration().getSendServerVersion())
+                response.add(SERVER_VERSION);
+            if (getHttpConfiguration().getSendXPoweredBy())
+                response.add(POWERED_BY);
+
+            onRequest(request);
+
+            boolean endStream = frame.isEndStream();
+            if (endStream)
+            {
+                onContentComplete();
+                onRequestComplete();
+            }
+
+            _delayedUntilContent = getHttpConfiguration().isDelayDispatchUntilContent() &&
+                    !endStream && !_expect100Continue;
+            _handled = !_delayedUntilContent;
+
+            if (LOG.isDebugEnabled())
+            {
+                Stream stream = getStream();
+                LOG.debug("HTTP2 Request #{}/{}, delayed={}:{}{} {} {}{}{}",
+                        stream.getId(), Integer.toHexString(stream.getSession().hashCode()),
+                        _delayedUntilContent, System.lineSeparator(),
+                        request.getMethod(), request.getURI(), request.getHttpVersion(),
+                        System.lineSeparator(), fields);
+            }
+
+            return _delayedUntilContent ? null : this;
+        }
+        catch (BadMessageException x)
+        {
+            onBadMessage(x.getCode(), x.getReason());
+            return null;
+        }
+        catch (Throwable x)
+        {
+            onBadMessage(HttpStatus.INTERNAL_SERVER_ERROR_500, null);
+            return null;
+        }
+    }
+
+    public Runnable onPushRequest(MetaData.Request request)
+    {
+        try
+        {
+            onRequest(request);
+            getRequest().setAttribute("org.eclipse.jetty.pushed", Boolean.TRUE);
+            onContentComplete();
+            onRequestComplete();
+
+            if (LOG.isDebugEnabled())
+            {
+                Stream stream = getStream();
+                LOG.debug("HTTP2 PUSH Request #{}/{}:{}{} {} {}{}{}",
+                        stream.getId(), Integer.toHexString(stream.getSession().hashCode()), System.lineSeparator(),
+                        request.getMethod(), request.getURI(), request.getHttpVersion(),
+                        System.lineSeparator(), request.getFields());
+            }
+
+            return this;
+        }
+        catch (BadMessageException x)
+        {
+            onBadMessage(x.getCode(), x.getReason());
+            return null;
+        }
+        catch (Throwable x)
+        {
+            onBadMessage(HttpStatus.INTERNAL_SERVER_ERROR_500, null);
+            return null;
+        }
+    }
+
+    @Override
+    public HttpTransportOverHTTP2 getHttpTransport()
+    {
+        return (HttpTransportOverHTTP2)super.getHttpTransport();
+    }
+
+    @Override
+    public void recycle()
+    {
+        _expect100Continue = false;
+        _delayedUntilContent = false;
+        _handled = false;
+        super.recycle();
+        getHttpTransport().recycle();
+    }
+
+    @Override
+    protected void commit(MetaData.Response info)
+    {
+        super.commit(info);
+        if (LOG.isDebugEnabled())
+        {
+            Stream stream = getStream();
+            LOG.debug("HTTP2 Commit Response #{}/{}:{}{} {} {}{}{}",
+                    stream.getId(), Integer.toHexString(stream.getSession().hashCode()), System.lineSeparator(), info.getHttpVersion(), info.getStatus(), info.getReason(),
+                    System.lineSeparator(), info.getFields());
+        }
+    }
+
+    public Runnable onRequestContent(DataFrame frame, final Callback callback)
+    {
+        Stream stream = getStream();
+        if (stream.isReset())
+        {
+            // Consume previously queued content to
+            // enlarge the session flow control window.
+            consumeInput();
+            // Consume immediately this content.
+            callback.succeeded();
+            return null;
+        }
+
+        // We must copy the data since we do not know when the
+        // application will consume the bytes (we queue them by
+        // calling onContent()), and the parsing will continue
+        // as soon as this method returns, eventually leading
+        // to reusing the underlying buffer for more reads.
+        final ByteBufferPool byteBufferPool = getByteBufferPool();
+        ByteBuffer original = frame.getData();
+        int length = original.remaining();
+        final ByteBuffer copy = byteBufferPool.acquire(length, original.isDirect());
+        BufferUtil.clearToFill(copy);
+        copy.put(original);
+        BufferUtil.flipToFlush(copy, 0);
+
+        boolean handle = onContent(new HttpInput.Content(copy)
+        {
+            @Override
+            public boolean isNonBlocking()
+            {
+                return callback.isNonBlocking();
+            }
+
+            @Override
+            public void succeeded()
+            {
+                byteBufferPool.release(copy);
+                callback.succeeded();
+            }
+
+            @Override
+            public void failed(Throwable x)
+            {
+                byteBufferPool.release(copy);
+                callback.failed(x);
+            }
+        });
+
+        boolean endStream = frame.isEndStream();
+        if (endStream)
+        {
+            boolean handle_content = onContentComplete();
+            boolean handle_request = onRequestComplete();
+            handle |= handle_content | handle_request;
+        }
+
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("HTTP2 Request #{}/{}: {} bytes of {} content, handle: {}",
+                    stream.getId(),
+                    Integer.toHexString(stream.getSession().hashCode()),
+                    length,
+                    endStream ? "last" : "some",
+                    handle);
+        }
+
+        boolean wasDelayed = _delayedUntilContent;
+        _delayedUntilContent = false;
+        if (wasDelayed)
+            _handled = true;
+        return handle || wasDelayed ? this : null;
+    }
+
+    public boolean isRequestHandled()
+    {
+        return _handled;
+    }
+
+    public boolean onStreamTimeout(Throwable failure)
+    {
+        if (!_handled)
+            return true;
+
+        HttpInput input = getRequest().getHttpInput();
+        boolean readFailed = input.failed(failure);
+        if (readFailed)
+            handle();
+
+        boolean writeFailed = getHttpTransport().onStreamTimeout(failure);
+
+        return readFailed || writeFailed;
+    }
+
+    public void onFailure(Throwable failure)
+    {
+        getHttpTransport().onStreamFailure(failure);
+        if (onEarlyEOF())
+            handle();
+        else
+            getState().asyncError(failure);
+    }
+
+    protected void consumeInput()
+    {
+        getRequest().getHttpInput().consumeAll();
+    }
+
+    /**
+     * 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.
+     *
+     * @throws IOException if the InputStream cannot be created
+     */
+    @Override
+    public void continue100(int available) throws IOException
+    {
+        // If the client is expecting 100 CONTINUE, then send it now.
+        // TODO: consider using an AtomicBoolean ?
+        if (isExpecting100Continue())
+        {
+            _expect100Continue = false;
+
+            // is content missing?
+            if (available == 0)
+            {
+                if (getResponse().isCommitted())
+                    throw new IOException("Committed before 100 Continues");
+
+                boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false);
+                if (!committed)
+                    throw new IOException("Concurrent commit while trying to send 100-Continue");
+            }
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        IStream stream = getStream();
+        long streamId = -1;
+        if (stream != null)
+            streamId = stream.getId();
+        return String.format("%s#%d", super.toString(), getStream() == null ? -1 : streamId);
+    }
+}
diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java
new file mode 100644
index 0000000..3c7e77d
--- /dev/null
+++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java
@@ -0,0 +1,335 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.IStream;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpTransport;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class HttpTransportOverHTTP2 implements HttpTransport
+{
+    private static final Logger LOG = Log.getLogger(HttpTransportOverHTTP2.class);
+
+    private final AtomicBoolean commit = new AtomicBoolean();
+    private final TransportCallback transportCallback = new TransportCallback();
+    private final Connector connector;
+    private final HTTP2ServerConnection connection;
+    private IStream stream;
+
+    public HttpTransportOverHTTP2(Connector connector, HTTP2ServerConnection connection)
+    {
+        this.connector = connector;
+        this.connection = connection;
+    }
+
+    @Override
+    public boolean isOptimizedForDirectBuffers()
+    {
+        // Because sent buffers are passed directly to the endpoint without
+        // copying we can defer to the endpoint
+        return connection.getEndPoint().isOptimizedForDirectBuffers();
+    }
+
+    public IStream getStream()
+    {
+        return stream;
+    }
+
+    public void setStream(IStream stream)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} setStream {}", this, stream.getId());
+        this.stream = stream;
+    }
+
+    public void recycle()
+    {
+        this.stream = null;
+        commit.set(false);
+    }
+
+    @Override
+    public void send(MetaData.Response info, boolean isHeadRequest, ByteBuffer content, boolean lastContent, Callback callback)
+    {
+        boolean hasContent = BufferUtil.hasContent(content) && !isHeadRequest;
+
+        if (info != null)
+        {
+            int status = info.getStatus();
+            boolean informational = HttpStatus.isInformational(status) && status != HttpStatus.SWITCHING_PROTOCOLS_101;
+            boolean committed = false;
+            if (!informational)
+                committed = commit.compareAndSet(false, true);
+
+            if (committed || informational)
+            {
+                if (hasContent)
+                {
+                    Callback commitCallback = new Callback.Nested(callback)
+                    {
+                        @Override
+                        public void succeeded()
+                        {
+                            if (transportCallback.start(callback, false))
+                                send(content, lastContent, transportCallback);
+                        }
+                    };
+                    if (transportCallback.start(commitCallback, true))
+                        commit(info, false, transportCallback);
+                }
+                else
+                {
+                    if (transportCallback.start(callback, false))
+                        commit(info, lastContent, transportCallback);
+                }
+            }
+            else
+            {
+                callback.failed(new IllegalStateException("committed"));
+            }
+        }
+        else
+        {
+            if (hasContent || lastContent)
+            {
+                if (transportCallback.start(callback, false))
+                    send(content, lastContent, transportCallback);
+            }
+            else
+            {
+                callback.succeeded();
+            }
+        }
+    }
+
+    @Override
+    public boolean isPushSupported()
+    {
+        return stream.getSession().isPushEnabled();
+    }
+
+    @Override
+    public void push(final MetaData.Request request)
+    {
+        if (!stream.getSession().isPushEnabled())
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("HTTP/2 Push disabled for {}", request);
+            return;
+        }
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("HTTP/2 Push {}",request);
+
+        stream.push(new PushPromiseFrame(stream.getId(), 0, request), new Promise<Stream>()
+        {
+            @Override
+            public void succeeded(Stream pushStream)
+            {
+                connection.push(connector, (IStream)pushStream, request);
+            }
+
+            @Override
+            public void failed(Throwable x)
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Could not push " + request, x);
+            }
+        }, new Stream.Listener.Adapter()); // TODO: handle reset from the client ?
+    }
+
+    private void commit(MetaData.Response info, boolean endStream, Callback callback)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("HTTP2 Response #{}:{}{} {}{}{}",
+                    stream.getId(), System.lineSeparator(), HttpVersion.HTTP_2, info.getStatus(),
+                    System.lineSeparator(), info.getFields());
+        }
+
+        HeadersFrame frame = new HeadersFrame(stream.getId(), info, null, endStream);
+        stream.headers(frame, callback);
+    }
+
+    private void send(ByteBuffer content, boolean lastContent, Callback callback)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("HTTP2 Response #{}: {} content bytes{}",
+                    stream.getId(), content.remaining(), lastContent ? " (last chunk)" : "");
+        }
+        DataFrame frame = new DataFrame(stream.getId(), content, lastContent);
+        stream.data(frame, callback);
+    }
+
+    public void onStreamFailure(Throwable failure)
+    {
+        transportCallback.failed(failure);
+    }
+
+    public boolean onStreamTimeout(Throwable failure)
+    {
+        return transportCallback.onIdleTimeout(failure);
+    }
+
+    @Override
+    public void onCompleted()
+    {
+        // If the stream is not closed, it is still reading the request content.
+        // Send a reset to the other end so that it stops sending data.
+        if (!stream.isClosed())
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("HTTP2 Response #{}: unconsumed request content, resetting stream", stream.getId());
+            stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+        }
+
+        // Consume the existing queued data frames to
+        // avoid stalling the session flow control.
+        HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
+        if (channel != null)
+            channel.consumeInput();
+    }
+
+    @Override
+    public void abort(Throwable failure)
+    {
+        IStream stream = this.stream;
+        if (LOG.isDebugEnabled())
+            LOG.debug("HTTP2 Response #{} aborted", stream == null ? -1 : stream.getId());
+        if (stream != null)
+            stream.reset(new ResetFrame(stream.getId(), ErrorCode.INTERNAL_ERROR.code), Callback.NOOP);
+    }
+
+    private class TransportCallback implements Callback
+    {
+        private State state = State.IDLE;
+        private Callback callback;
+        private boolean commit;
+
+        public boolean start(Callback callback, boolean commit)
+        {
+            State state;
+            synchronized (this)
+            {
+                state = this.state;
+                if (state == State.IDLE)
+                {
+                    this.state = State.WRITING;
+                    this.callback = callback;
+                    this.commit = commit;
+                    return true;
+                }
+            }
+            callback.failed(new IllegalStateException("Invalid transport state: " + state));
+            return false;
+        }
+
+        @Override
+        public void succeeded()
+        {
+            boolean commit;
+            Callback callback = null;
+            synchronized (this)
+            {
+                commit = this.commit;
+                if (state == State.WRITING)
+                {
+                    callback = this.callback;
+                    this.callback = null;
+                    this.state = State.IDLE;
+                }
+            }
+            if (LOG.isDebugEnabled())
+                LOG.debug("HTTP2 Response #{} {}", stream.getId(), commit ? "committed" : "flushed content");
+            if (callback != null)
+                callback.succeeded();
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            boolean commit;
+            Callback callback = null;
+            synchronized (this)
+            {
+                commit = this.commit;
+                if (state == State.WRITING)
+                {
+                    callback = this.callback;
+                    this.callback = null;
+                    this.state = State.FAILED;
+                }
+            }
+            if (LOG.isDebugEnabled())
+                LOG.debug("HTTP2 Response #" + stream.getId() + " failed to " + (commit ? "commit" : "flush"), x);
+            if (callback != null)
+                callback.failed(x);
+        }
+
+        @Override
+        public boolean isNonBlocking()
+        {
+            return callback.isNonBlocking();
+        }
+
+        private boolean onIdleTimeout(Throwable failure)
+        {
+            boolean result;
+            Callback callback = null;
+            synchronized (this)
+            {
+                result = state == State.WRITING;
+                if (result)
+                {
+                    callback = this.callback;
+                    this.callback = null;
+                    this.state = State.TIMEOUT;
+                }
+            }
+            if (LOG.isDebugEnabled())
+                LOG.debug("HTTP2 Response #" + stream.getId() + " idle timeout", failure);
+            if (result)
+                callback.failed(failure);
+            return result;
+        }
+    }
+
+    private enum State
+    {
+        IDLE, WRITING, FAILED, TIMEOUT
+    }
+}
diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/RawHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/RawHTTP2ServerConnectionFactory.java
new file mode 100644
index 0000000..ebab6eb
--- /dev/null
+++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/RawHTTP2ServerConnectionFactory.java
@@ -0,0 +1,41 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+
+public class RawHTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionFactory
+{
+    private final ServerSessionListener listener;
+
+    public RawHTTP2ServerConnectionFactory(HttpConfiguration httpConfiguration,ServerSessionListener listener)
+    {
+        super(httpConfiguration);
+        this.listener = listener;
+    }
+
+    @Override
+    protected ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint)
+    {
+        return listener;
+    }
+}
diff --git a/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/AbstractServerTest.java b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/AbstractServerTest.java
new file mode 100644
index 0000000..c040e36
--- /dev/null
+++ b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/AbstractServerTest.java
@@ -0,0 +1,128 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.nio.ByteBuffer;
+
+import javax.servlet.http.HttpServlet;
+
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.generator.Generator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Rule;
+
+public class AbstractServerTest
+{
+    @Rule
+    public TestTracker tracker = new TestTracker();
+    protected ServerConnector connector;
+    protected ByteBufferPool byteBufferPool;
+    protected Generator generator;
+    protected Server server;
+    protected String path;
+
+    protected void startServer(HttpServlet servlet) throws Exception
+    {
+        prepareServer(new HTTP2ServerConnectionFactory(new HttpConfiguration()));
+        ServletContextHandler context = new ServletContextHandler(server, "/");
+        context.addServlet(new ServletHolder(servlet), path);
+        server.start();
+    }
+
+    protected void startServer(ServerSessionListener listener) throws Exception
+    {
+        prepareServer(new RawHTTP2ServerConnectionFactory(new HttpConfiguration(),listener));
+        server.start();
+    }
+
+    private void prepareServer(ConnectionFactory connectionFactory)
+    {
+        QueuedThreadPool serverExecutor = new QueuedThreadPool();
+        serverExecutor.setName("server");
+        server = new Server(serverExecutor);
+        connector = new ServerConnector(server, connectionFactory);
+        server.addConnector(connector);
+        path = "/test";
+        byteBufferPool = new MappedByteBufferPool();
+        generator = new Generator(byteBufferPool);
+    }
+
+    protected MetaData.Request newRequest(String method, HttpFields fields)
+    {
+        String host = "localhost";
+        int port = connector.getLocalPort();
+        String authority = host + ":" + port;
+        return new MetaData.Request(method, HttpScheme.HTTP, new HostPortHttpField(authority), path, HttpVersion.HTTP_2, fields);
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        if (server!=null)
+            server.stop();
+    }
+
+    protected boolean parseResponse(Socket client, Parser parser) throws IOException
+    {
+        return parseResponse(client, parser, 1000);
+    }
+
+    protected boolean parseResponse(Socket client, Parser parser, long timeout) throws IOException
+    {
+        byte[] buffer = new byte[2048];
+        InputStream input = client.getInputStream();
+        client.setSoTimeout((int)timeout);
+        while (true)
+        {
+            try
+            {
+                int read = input.read(buffer);
+                if (read < 0)
+                    return true;
+                parser.parse(ByteBuffer.wrap(buffer, 0, read));
+                if (client.isClosed())
+                    return true;
+            }
+            catch (SocketTimeoutException x)
+            {
+                return false;
+            }
+        }
+    }
+}
diff --git a/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/CloseTest.java b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/CloseTest.java
new file mode 100644
index 0000000..307cece
--- /dev/null
+++ b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/CloseTest.java
@@ -0,0 +1,254 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.HTTP2Session;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PrefaceFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CloseTest extends AbstractServerTest
+{
+    @Test
+    public void testClientAbruptlyClosesConnection() throws Exception
+    {
+        final CountDownLatch closeLatch = new CountDownLatch(1);
+        final AtomicReference<Session> sessionRef = new AtomicReference<>();
+        startServer(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                try
+                {
+                    sessionRef.set(stream.getSession());
+                    MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                    // Reply with HEADERS.
+                    stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
+                    closeLatch.await(5, TimeUnit.SECONDS);
+                    return null;
+                }
+                catch (InterruptedException x)
+                {
+                    return null;
+                }
+            }
+        });
+
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        generator.control(lease, new PrefaceFrame());
+        generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        generator.control(lease, new HeadersFrame(1, metaData, null, true));
+
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                output.write(BufferUtil.toArray(buffer));
+            }
+
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onHeaders(HeadersFrame frame)
+                {
+                    try
+                    {
+                        // Close the connection just after
+                        // receiving the response headers.
+                        client.close();
+                        closeLatch.countDown();
+                    }
+                    catch (IOException x)
+                    {
+                        throw new RuntimeIOException(x);
+                    }
+                }
+            }, 4096, 8192);
+
+            parseResponse(client, parser);
+
+            // We need to give some time to the server to receive and process the TCP FIN.
+            Thread.sleep(1000);
+
+            Session session = sessionRef.get();
+            Assert.assertTrue(session.isClosed());
+            Assert.assertTrue(((HTTP2Session)session).isDisconnected());
+        }
+    }
+
+    @Test
+    public void testClientSendsGoAwayButDoesNotCloseConnectionServerCloses() throws Exception
+    {
+        final AtomicReference<Session> sessionRef = new AtomicReference<>();
+        startServer(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                sessionRef.set(stream.getSession());
+                MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
+                return null;
+            }
+        });
+
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        generator.control(lease, new PrefaceFrame());
+        generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        generator.control(lease, new HeadersFrame(1, metaData, null, true));
+        generator.control(lease, new GoAwayFrame(1, ErrorCode.NO_ERROR.code, "OK".getBytes("UTF-8")));
+
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                output.write(BufferUtil.toArray(buffer));
+            }
+
+            // Don't close the connection; the server should close.
+
+            final CountDownLatch responseLatch = new CountDownLatch(1);
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onHeaders(HeadersFrame frame)
+                {
+                    // Even if we sent the GO_AWAY immediately after the
+                    // HEADERS, the server is able to send us the response.
+                    responseLatch.countDown();
+                }
+            }, 4096, 8192);
+
+            parseResponse(client, parser);
+
+            Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+
+            // Wait for the server to close.
+            Thread.sleep(1000);
+
+            // Client received the TCP FIN from server.
+            Assert.assertEquals(-1, client.getInputStream().read());
+
+            // Server is closed.
+            Session session = sessionRef.get();
+            Assert.assertTrue(session.isClosed());
+            Assert.assertTrue(((HTTP2Session)session).isDisconnected());
+        }
+    }
+
+    @Test
+    public void testServerSendsGoAwayClientDoesNotCloseServerIdleTimeout() throws Exception
+    {
+        final long idleTimeout = 1000;
+        final AtomicReference<Session> sessionRef = new AtomicReference<>();
+        startServer(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                stream.setIdleTimeout(10 * idleTimeout);
+                sessionRef.set(stream.getSession());
+                MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
+                stream.getSession().close(ErrorCode.NO_ERROR.code, "OK", Callback.NOOP);
+                return null;
+            }
+        });
+        connector.setIdleTimeout(idleTimeout);
+
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        generator.control(lease, new PrefaceFrame());
+        generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        generator.control(lease, new HeadersFrame(1, metaData, null, true));
+
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                output.write(BufferUtil.toArray(buffer));
+            }
+
+            final CountDownLatch responseLatch = new CountDownLatch(1);
+            final CountDownLatch closeLatch = new CountDownLatch(1);
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onHeaders(HeadersFrame frame)
+                {
+                    responseLatch.countDown();
+                }
+
+                @Override
+                public void onGoAway(GoAwayFrame frame)
+                {
+                    closeLatch.countDown();
+                }
+            }, 4096, 8192);
+
+            parseResponse(client, parser);
+
+            Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+            Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
+
+            // Don't close the connection.
+
+            // Wait for the server to idle timeout.
+            Thread.sleep(2 * idleTimeout);
+
+            // Client received the TCP FIN from server.
+            Assert.assertEquals(-1, client.getInputStream().read());
+
+            // Server is closed.
+            Session session = sessionRef.get();
+            Assert.assertTrue(session.isClosed());
+            Assert.assertTrue(((HTTP2Session)session).isDisconnected());
+        }
+    }
+}
diff --git a/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServer.java b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServer.java
new file mode 100644
index 0000000..5e47d7d
--- /dev/null
+++ b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServer.java
@@ -0,0 +1,83 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.Date;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SocketCustomizationListener;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
+public class HTTP2CServer extends Server
+{
+    public HTTP2CServer(int port)
+    {
+        HttpConfiguration config = new HttpConfiguration();
+        // HTTP + HTTP/2 connector
+        
+        HttpConnectionFactory http1 = new HttpConnectionFactory(config);
+        HTTP2CServerConnectionFactory http2c = new HTTP2CServerConnectionFactory(config);
+        ServerConnector connector = new ServerConnector(this,http1,http2c);
+        connector.setPort(port);
+        addConnector(connector);
+
+        ((QueuedThreadPool)getThreadPool()).setName("server");
+
+        setHandler(new SimpleHandler());
+        
+    }
+
+    public static void main(String... args ) throws Exception
+    {
+        HTTP2CServer server = new HTTP2CServer(8080);
+        server.start();
+    }
+
+    private static class SimpleHandler extends AbstractHandler
+    {
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            String code=request.getParameter("code");
+            if (code!=null)
+                response.setStatus(Integer.parseInt(code));
+
+            response.setHeader("Custom","Value");
+            response.setContentType("text/plain");
+            String content = "Hello from Jetty using "+request.getProtocol() +"\n";
+            content+="uri="+request.getRequestURI()+"\n";
+            content+="date="+new Date()+"\n";
+            response.setContentLength(content.length());
+            response.getOutputStream().print(content);
+        }
+    }
+}
diff --git a/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServerTest.java b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServerTest.java
new file mode 100644
index 0000000..a7314d9
--- /dev/null
+++ b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServerTest.java
@@ -0,0 +1,345 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PrefaceFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.generator.Generator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConnection;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.Utf8StringBuilder;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class HTTP2CServerTest extends AbstractServerTest
+{
+    @Before
+    public void before() throws Exception
+    {
+        server = new HTTP2CServer(0);
+        server.start();
+        connector = (ServerConnector)server.getConnectors()[0];
+    }
+
+    @After
+    public void after() throws Exception
+    {
+        server.stop();
+    }
+
+    @Test
+    public void testHTTP_1_0_Simple() throws Exception
+    {
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            client.getOutputStream().write("GET / HTTP/1.0\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1));
+            client.getOutputStream().flush();
+
+            String response = IO.toString(client.getInputStream());
+
+            assertThat(response, containsString("HTTP/1.1 200 OK"));
+            assertThat(response, containsString("Hello from Jetty using HTTP/1.0"));
+        }
+    }
+
+    @Test
+    public void testHTTP_1_1_Simple() throws Exception
+    {
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            client.getOutputStream().write("GET /one HTTP/1.1\r\nHost: localhost\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1));
+            client.getOutputStream().write("GET /two HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1));
+            client.getOutputStream().flush();
+
+            String response = IO.toString(client.getInputStream());
+
+            assertThat(response, containsString("HTTP/1.1 200 OK"));
+            assertThat(response, containsString("Hello from Jetty using HTTP/1.1"));
+            assertThat(response, containsString("uri=/one"));
+            assertThat(response, containsString("uri=/two"));
+        }
+    }
+
+    @Test
+    public void testHTTP_1_1_Upgrade() throws Exception
+    {
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            output.write(("" +
+                    "GET /one HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "Connection: something, else, upgrade, HTTP2-Settings\r\n" +
+                    "Upgrade: h2c\r\n" +
+                    "HTTP2-Settings: \r\n" +
+                    "\r\n").getBytes(StandardCharsets.ISO_8859_1));
+            output.flush();
+
+            InputStream input = client.getInputStream();
+            Utf8StringBuilder upgrade = new Utf8StringBuilder();
+            int crlfs = 0;
+            while (true)
+            {
+                int read = input.read();
+                if (read == '\r' || read == '\n')
+                    ++crlfs;
+                else
+                    crlfs = 0;
+                upgrade.append((byte)read);
+                if (crlfs == 4)
+                    break;
+            }
+
+            assertTrue(upgrade.toString().startsWith("HTTP/1.1 101 "));
+
+            byteBufferPool = new MappedByteBufferPool();
+            generator = new Generator(byteBufferPool);
+
+            final AtomicReference<HeadersFrame> headersRef = new AtomicReference<>();
+            final AtomicReference<DataFrame> dataRef = new AtomicReference<>();
+            final AtomicReference<CountDownLatch> latchRef = new AtomicReference<>(new CountDownLatch(2));
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onHeaders(HeadersFrame frame)
+                {
+                    headersRef.set(frame);
+                    latchRef.get().countDown();
+                }
+
+                @Override
+                public void onData(DataFrame frame)
+                {
+                    dataRef.set(frame);
+                    latchRef.get().countDown();
+                }
+            }, 4096, 8192);
+
+            parseResponse(client, parser);
+
+            Assert.assertTrue(latchRef.get().await(5, TimeUnit.SECONDS));
+
+            HeadersFrame response = headersRef.get();
+            Assert.assertNotNull(response);
+            MetaData.Response responseMetaData = (MetaData.Response)response.getMetaData();
+            Assert.assertEquals(200, responseMetaData.getStatus());
+
+            DataFrame responseData = dataRef.get();
+            Assert.assertNotNull(responseData);
+
+            String content = BufferUtil.toString(responseData.getData());
+
+            // The upgrade request is seen as HTTP/1.1.
+            assertThat(content, containsString("Hello from Jetty using HTTP/1.1"));
+            assertThat(content, containsString("uri=/one"));
+
+            // Send a HTTP/2 request.
+            headersRef.set(null);
+            dataRef.set(null);
+            latchRef.set(new CountDownLatch(2));
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.control(lease, new PrefaceFrame());
+            generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+            MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, new HostPortHttpField("localhost:" + connector.getLocalPort()), "/two", HttpVersion.HTTP_2, new HttpFields());
+            generator.control(lease, new HeadersFrame(3, metaData, null, true));
+            for (ByteBuffer buffer : lease.getByteBuffers())
+                output.write(BufferUtil.toArray(buffer));
+            output.flush();
+
+            parseResponse(client, parser);
+
+            Assert.assertTrue(latchRef.get().await(5, TimeUnit.SECONDS));
+
+            response = headersRef.get();
+            Assert.assertNotNull(response);
+            responseMetaData = (MetaData.Response)response.getMetaData();
+            Assert.assertEquals(200, responseMetaData.getStatus());
+
+            responseData = dataRef.get();
+            Assert.assertNotNull(responseData);
+
+            content = BufferUtil.toString(responseData.getData());
+
+            assertThat(content, containsString("Hello from Jetty using HTTP/2.0"));
+            assertThat(content, containsString("uri=/two"));
+        }
+    }
+
+    @Test
+    public void testHTTP_2_0_Direct() throws Exception
+    {
+        final CountDownLatch latch = new CountDownLatch(3);
+
+        byteBufferPool = new MappedByteBufferPool();
+        generator = new Generator(byteBufferPool);
+
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        generator.control(lease, new PrefaceFrame());
+        generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+        MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, new HostPortHttpField("localhost:" + connector.getLocalPort()), "/test", HttpVersion.HTTP_2, new HttpFields());
+        generator.control(lease, new HeadersFrame(1, metaData, null, true));
+
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                output.write(BufferUtil.toArray(buffer));
+            }
+
+            final AtomicReference<HeadersFrame> headersRef = new AtomicReference<>();
+            final AtomicReference<DataFrame> dataRef = new AtomicReference<>();
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onSettings(SettingsFrame frame)
+                {
+                    latch.countDown();
+                }
+
+                @Override
+                public void onHeaders(HeadersFrame frame)
+                {
+                    headersRef.set(frame);
+                    latch.countDown();
+                }
+
+                @Override
+                public void onData(DataFrame frame)
+                {
+                    dataRef.set(frame);
+                    latch.countDown();
+                }
+            }, 4096, 8192);
+
+            parseResponse(client, parser);
+
+            Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+
+            HeadersFrame response = headersRef.get();
+            Assert.assertNotNull(response);
+            MetaData.Response responseMetaData = (MetaData.Response)response.getMetaData();
+            Assert.assertEquals(200, responseMetaData.getStatus());
+
+            DataFrame responseData = dataRef.get();
+            Assert.assertNotNull(responseData);
+
+            String s = BufferUtil.toString(responseData.getData());
+
+            assertThat(s, containsString("Hello from Jetty using HTTP/2.0"));
+            assertThat(s, containsString("uri=/test"));
+        }
+    }
+
+    @Test
+    public void testHTTP_2_0_DirectWithoutH2C() throws Exception
+    {
+        AtomicLong fills = new AtomicLong();
+        // Remove "h2c", leaving only "http/1.1".
+        connector.clearConnectionFactories();
+        HttpConnectionFactory connectionFactory = new HttpConnectionFactory()
+        {
+            @Override
+            public Connection newConnection(Connector connector, EndPoint endPoint)
+            {
+                HttpConnection connection = new HttpConnection(getHttpConfiguration(), connector, endPoint,getHttpCompliance(),isRecordHttpComplianceViolations())
+                {
+                    @Override
+                    public void onFillable()
+                    {
+                        fills.incrementAndGet();
+                        super.onFillable();
+                    }
+                };
+                return configure(connection, connector, endPoint);
+            }
+        };
+        connector.addConnectionFactory(connectionFactory);
+        connector.setDefaultProtocol(connectionFactory.getProtocol());
+
+        // Now send a HTTP/2 direct request, which
+        // will have the PRI * HTTP/2.0 preface.
+
+        byteBufferPool = new MappedByteBufferPool();
+        generator = new Generator(byteBufferPool);
+
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        generator.control(lease, new PrefaceFrame());
+
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+                output.write(BufferUtil.toArray(buffer));
+
+            // We sent a HTTP/2 preface, but the server has no "h2c" connection
+            // factory so it does not know how to handle this request.
+
+            InputStream input = client.getInputStream();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
+            String responseLine = reader.readLine();
+            Assert.assertThat(responseLine, Matchers.containsString(" 426 "));
+            while (true)
+            {
+                if (reader.read() < 0)
+                    break;
+            }
+        }
+
+        // Make sure we did not spin.
+        Thread.sleep(1000);
+        Assert.assertThat(fills.get(), Matchers.lessThan(5L));
+    }
+}
diff --git a/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2ServerTest.java b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2ServerTest.java
new file mode 100644
index 0000000..36fd24d
--- /dev/null
+++ b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2ServerTest.java
@@ -0,0 +1,582 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http2.server;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.Flags;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.FrameType;
+import org.eclipse.jetty.http2.frames.GoAwayFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PingFrame;
+import org.eclipse.jetty.http2.frames.PrefaceFrame;
+import org.eclipse.jetty.http2.frames.PriorityFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.generator.Generator;
+import org.eclipse.jetty.http2.parser.Parser;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.ManagedSelector;
+import org.eclipse.jetty.io.SelectChannelEndPoint;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HTTP2ServerTest extends AbstractServerTest
+{
+    @Test
+    public void testNoPrefaceBytes() throws Exception
+    {
+        startServer(new HttpServlet(){});
+
+        // No preface bytes.
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        generator.control(lease, new HeadersFrame(1, metaData, null, true));
+
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                output.write(BufferUtil.toArray(buffer));
+            }
+
+            final CountDownLatch latch = new CountDownLatch(1);
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onGoAway(GoAwayFrame frame)
+                {
+                    latch.countDown();
+                }
+            }, 4096, 8192);
+
+            parseResponse(client, parser);
+
+            Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testRequestResponseNoContent() throws Exception
+    {
+        final CountDownLatch latch = new CountDownLatch(3);
+        startServer(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+            {
+                latch.countDown();
+            }
+        });
+
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        generator.control(lease, new PrefaceFrame());
+        generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        generator.control(lease, new HeadersFrame(1, metaData, null, true));
+
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                output.write(BufferUtil.toArray(buffer));
+            }
+
+            final AtomicReference<HeadersFrame> frameRef = new AtomicReference<>();
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onSettings(SettingsFrame frame)
+                {
+                    latch.countDown();
+                }
+
+                @Override
+                public void onHeaders(HeadersFrame frame)
+                {
+                    frameRef.set(frame);
+                    latch.countDown();
+                }
+            }, 4096, 8192);
+
+            parseResponse(client, parser);
+
+            Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+
+            HeadersFrame response = frameRef.get();
+            Assert.assertNotNull(response);
+            MetaData.Response responseMetaData = (MetaData.Response)response.getMetaData();
+            Assert.assertEquals(200, responseMetaData.getStatus());
+        }
+    }
+
+    @Test
+    public void testRequestResponseContent() throws Exception
+    {
+        final byte[] content = "Hello, world!".getBytes(StandardCharsets.UTF_8);
+        final CountDownLatch latch = new CountDownLatch(4);
+        startServer(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+            {
+                latch.countDown();
+                resp.getOutputStream().write(content);
+            }
+        });
+
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        generator.control(lease, new PrefaceFrame());
+        generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        generator.control(lease, new HeadersFrame(1, metaData, null, true));
+
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                output.write(BufferUtil.toArray(buffer));
+            }
+
+            final AtomicReference<HeadersFrame> headersRef = new AtomicReference<>();
+            final AtomicReference<DataFrame> dataRef = new AtomicReference<>();
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onSettings(SettingsFrame frame)
+                {
+                    latch.countDown();
+                }
+
+                @Override
+                public void onHeaders(HeadersFrame frame)
+                {
+                    headersRef.set(frame);
+                    latch.countDown();
+                }
+
+                @Override
+                public void onData(DataFrame frame)
+                {
+                    dataRef.set(frame);
+                    latch.countDown();
+                }
+            }, 4096, 8192);
+
+            parseResponse(client, parser);
+
+            Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+
+            HeadersFrame response = headersRef.get();
+            Assert.assertNotNull(response);
+            MetaData.Response responseMetaData = (MetaData.Response)response.getMetaData();
+            Assert.assertEquals(200, responseMetaData.getStatus());
+
+            DataFrame responseData = dataRef.get();
+            Assert.assertNotNull(responseData);
+            Assert.assertArrayEquals(content, BufferUtil.toArray(responseData.getData()));
+        }
+    }
+
+    @Test
+    public void testBadPingWrongPayload() throws Exception
+    {
+        startServer(new HttpServlet(){});
+
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        generator.control(lease, new PrefaceFrame());
+        generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+        generator.control(lease, new PingFrame(new byte[8], false));
+        // Modify the length of the frame to a wrong one.
+        lease.getByteBuffers().get(2).putShort(0, (short)7);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                output.write(BufferUtil.toArray(buffer));
+            }
+
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onGoAway(GoAwayFrame frame)
+                {
+                    Assert.assertEquals(ErrorCode.FRAME_SIZE_ERROR.code, frame.getError());
+                    latch.countDown();
+                }
+            }, 4096, 8192);
+
+            parseResponse(client, parser);
+
+            Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testBadPingWrongStreamId() throws Exception
+    {
+        startServer(new HttpServlet(){});
+
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        generator.control(lease, new PrefaceFrame());
+        generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+        generator.control(lease, new PingFrame(new byte[8], false));
+        // Modify the streamId of the frame to non zero.
+        lease.getByteBuffers().get(2).putInt(4, 1);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+            {
+                output.write(BufferUtil.toArray(buffer));
+            }
+
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onGoAway(GoAwayFrame frame)
+                {
+                    Assert.assertEquals(ErrorCode.PROTOCOL_ERROR.code, frame.getError());
+                    latch.countDown();
+                }
+            }, 4096, 8192);
+
+            parseResponse(client, parser);
+
+            Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testCommitFailure() throws Exception
+    {
+        final long delay = 1000;
+        final AtomicBoolean broken = new AtomicBoolean();
+        startServer(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                try
+                {
+                    // Wait for the SETTINGS frames to be exchanged.
+                    Thread.sleep(delay);
+                    broken.set(true);
+                }
+                catch (InterruptedException x)
+                {
+                    throw new InterruptedIOException();
+                }
+            }
+        });
+        server.stop();
+
+        ServerConnector connector2 = new ServerConnector(server, new HTTP2ServerConnectionFactory(new HttpConfiguration()))
+        {
+            @Override
+            protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
+            {
+                return new SelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout())
+                {
+                    @Override
+                    public void write(Callback callback, ByteBuffer... buffers) throws IllegalStateException
+                    {
+                        if (broken.get())
+                            callback.failed(new IOException("explicitly_thrown_by_test"));
+                        else
+                            super.write(callback, buffers);
+                    }
+                };
+            }
+        };
+        server.addConnector(connector2);
+        server.start();
+
+        ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+        generator.control(lease, new PrefaceFrame());
+        generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+        MetaData.Request metaData = newRequest("GET", new HttpFields());
+        generator.control(lease, new HeadersFrame(1, metaData, null, true));
+        try (Socket client = new Socket("localhost", connector2.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+                output.write(BufferUtil.toArray(buffer));
+
+            // The server will close the connection abruptly since it
+            // cannot write and therefore cannot even send the GO_AWAY.
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter(), 4096, 8192);
+            boolean closed = parseResponse(client, parser, 2 * delay);
+            Assert.assertTrue(closed);
+        }
+    }
+
+    @Test
+    public void testNonISOHeader() throws Exception
+    {
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            startServer(new HttpServlet()
+            {
+                @Override
+                protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+                {
+                    // Invalid header name, the connection must be closed.
+                    response.setHeader("Euro_(\u20AC)", "42");
+                }
+            });
+
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.control(lease, new PrefaceFrame());
+            generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+            MetaData.Request metaData = newRequest("GET", new HttpFields());
+            generator.control(lease, new HeadersFrame(1, metaData, null, true));
+
+            try (Socket client = new Socket("localhost", connector.getLocalPort()))
+            {
+                OutputStream output = client.getOutputStream();
+                for (ByteBuffer buffer : lease.getByteBuffers())
+                    output.write(BufferUtil.toArray(buffer));
+                output.flush();
+
+                Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter(), 4096, 8192);
+                boolean closed = parseResponse(client, parser);
+
+                Assert.assertTrue(closed);
+            }
+        }
+    }
+
+    @Test
+    public void testRequestWithContinuationFrames() throws Exception
+    {
+        testRequestWithContinuationFrames(null, () ->
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.control(lease, new PrefaceFrame());
+            generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+            MetaData.Request metaData = newRequest("GET", new HttpFields());
+            generator.control(lease, new HeadersFrame(1, metaData, null, true));
+            return lease;
+        });
+    }
+
+    @Test
+    public void testRequestWithPriorityWithContinuationFrames() throws Exception
+    {
+        PriorityFrame priority = new PriorityFrame(1, 13, 200, true);
+        testRequestWithContinuationFrames(priority, () ->
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.control(lease, new PrefaceFrame());
+            generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+            MetaData.Request metaData = newRequest("GET", new HttpFields());
+            generator.control(lease, new HeadersFrame(1, metaData, priority, true));
+            return lease;
+        });
+    }
+
+    @Test
+    public void testRequestWithContinuationFramesWithEmptyHeadersFrame() throws Exception
+    {
+        testRequestWithContinuationFrames(null, () ->
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.control(lease, new PrefaceFrame());
+            generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+            MetaData.Request metaData = newRequest("GET", new HttpFields());
+            generator.control(lease, new HeadersFrame(1, metaData, null, true));
+            // Take the HeadersFrame header and set the length to zero.
+            List<ByteBuffer> buffers = lease.getByteBuffers();
+            ByteBuffer headersFrameHeader = buffers.get(2);
+            headersFrameHeader.put(0, (byte)0);
+            headersFrameHeader.putShort(1, (short)0);
+            // Insert a CONTINUATION frame header for the body of the HEADERS frame.
+            lease.insert(3, buffers.get(4).slice(), false);
+            return lease;
+        });
+    }
+
+    @Test
+    public void testRequestWithPriorityWithContinuationFramesWithEmptyHeadersFrame() throws Exception
+    {
+        PriorityFrame priority = new PriorityFrame(1, 13, 200, true);
+        testRequestWithContinuationFrames(null, () ->
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.control(lease, new PrefaceFrame());
+            generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+            MetaData.Request metaData = newRequest("GET", new HttpFields());
+            generator.control(lease, new HeadersFrame(1, metaData, priority, true));
+            // Take the HeadersFrame header and set the length to just the priority frame.
+            List<ByteBuffer> buffers = lease.getByteBuffers();
+            ByteBuffer headersFrameHeader = buffers.get(2);
+            headersFrameHeader.put(0, (byte)0);
+            headersFrameHeader.putShort(1, (short)PriorityFrame.PRIORITY_LENGTH);
+            // Insert a CONTINUATION frame header for the body of the HEADERS frame.
+            lease.insert(3, buffers.get(4).slice(), false);
+            return lease;
+        });
+    }
+
+    @Test
+    public void testRequestWithContinuationFramesWithEmptyContinuationFrame() throws Exception
+    {
+        testRequestWithContinuationFrames(null, () ->
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.control(lease, new PrefaceFrame());
+            generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+            MetaData.Request metaData = newRequest("GET", new HttpFields());
+            generator.control(lease, new HeadersFrame(1, metaData, null, true));
+            // Take the ContinuationFrame header, duplicate it, and set the length to zero.
+            List<ByteBuffer> buffers = lease.getByteBuffers();
+            ByteBuffer continuationFrameHeader = buffers.get(4);
+            ByteBuffer duplicate = ByteBuffer.allocate(continuationFrameHeader.remaining());
+            duplicate.put(continuationFrameHeader).flip();
+            continuationFrameHeader.flip();
+            continuationFrameHeader.put(0, (byte)0);
+            continuationFrameHeader.putShort(1, (short)0);
+            // Insert a CONTINUATION frame header for the body of the previous CONTINUATION frame.
+            lease.insert(5, duplicate, false);
+            return lease;
+        });
+    }
+
+    @Test
+    public void testRequestWithContinuationFramesWithEmptyLastContinuationFrame() throws Exception
+    {
+        testRequestWithContinuationFrames(null, () ->
+        {
+            ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
+            generator.control(lease, new PrefaceFrame());
+            generator.control(lease, new SettingsFrame(new HashMap<>(), false));
+            MetaData.Request metaData = newRequest("GET", new HttpFields());
+            generator.control(lease, new HeadersFrame(1, metaData, null, true));
+            // Take the last CONTINUATION frame and reset the flag.
+            List<ByteBuffer> buffers = lease.getByteBuffers();
+            ByteBuffer continuationFrameHeader = buffers.get(buffers.size() - 2);
+            continuationFrameHeader.put(4, (byte)0);
+            // Add a last, empty, CONTINUATION frame.
+            ByteBuffer last = ByteBuffer.wrap(new byte[]{
+                    0, 0, 0, // Length
+                    (byte)FrameType.CONTINUATION.getType(),
+                    (byte)Flags.END_HEADERS,
+                    0, 0, 0, 1 // Stream ID
+            });
+            lease.append(last, false);
+            return lease;
+        });
+    }
+
+    private void testRequestWithContinuationFrames(PriorityFrame priorityFrame, Callable<ByteBufferPool.Lease> frames) throws Exception
+    {
+        final CountDownLatch serverLatch = new CountDownLatch(1);
+        startServer(new ServerSessionListener.Adapter()
+        {
+            @Override
+            public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+            {
+                if (priorityFrame != null)
+                {
+                    PriorityFrame priority = frame.getPriority();
+                    Assert.assertNotNull(priority);
+                    Assert.assertEquals(priorityFrame.getStreamId(), priority.getStreamId());
+                    Assert.assertEquals(priorityFrame.getParentStreamId(), priority.getParentStreamId());
+                    Assert.assertEquals(priorityFrame.getWeight(), priority.getWeight());
+                    Assert.assertEquals(priorityFrame.isExclusive(), priority.isExclusive());
+                }
+
+                serverLatch.countDown();
+
+                MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
+                HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, true);
+                stream.headers(responseFrame, Callback.NOOP);
+                return null;
+            }
+        });
+        generator = new Generator(byteBufferPool, 4096, 4);
+
+        ByteBufferPool.Lease lease = frames.call();
+
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            for (ByteBuffer buffer : lease.getByteBuffers())
+                output.write(BufferUtil.toArray(buffer));
+            output.flush();
+
+            Assert.assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
+
+            final CountDownLatch clientLatch = new CountDownLatch(1);
+            Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
+            {
+                @Override
+                public void onHeaders(HeadersFrame frame)
+                {
+                    if (frame.isEndStream())
+                        clientLatch.countDown();
+                }
+            }, 4096, 8192);
+            boolean closed = parseResponse(client, parser);
+
+            Assert.assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+            Assert.assertFalse(closed);
+        }
+    }
+}
diff --git a/jetty-http2/http2-server/src/test/resources/jetty-logging.properties b/jetty-http2/http2-server/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..9611e7c
--- /dev/null
+++ b/jetty-http2/http2-server/src/test/resources/jetty-logging.properties
@@ -0,0 +1,4 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.LEVEL=DEBUG
+#org.eclipse.jetty.http2.LEVEL=DEBUG
+org.eclipse.jetty.http2.hpack.LEVEL=INFO
diff --git a/jetty-http2/pom.xml b/jetty-http2/pom.xml
new file mode 100644
index 0000000..7dcab97
--- /dev/null
+++ b/jetty-http2/pom.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <artifactId>jetty-project</artifactId>
+    <groupId>org.eclipse.jetty</groupId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.eclipse.jetty.http2</groupId>
+  <artifactId>http2-parent</artifactId>
+  <packaging>pom</packaging>
+  <name>Jetty :: HTTP2</name>
+
+  <modules>
+    <module>http2-client</module>
+    <module>http2-common</module>
+    <module>http2-hpack</module>
+    <module>http2-http-client-transport</module>
+    <module>http2-server</module>
+  </modules>
+
+  <profiles>
+    <profile>
+      <id>jdk8</id>
+      <activation>
+        <jdk>[1.8,1.9)</jdk>
+      </activation>
+      <modules>
+        <module>http2-alpn-tests</module>
+      </modules>
+    </profile>
+  </profiles>
+
+</project>
diff --git a/jetty-infinispan/pom.xml b/jetty-infinispan/pom.xml
new file mode 100644
index 0000000..e0bfb8f
--- /dev/null
+++ b/jetty-infinispan/pom.xml
@@ -0,0 +1,58 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-project</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>jetty-infinispan</artifactId>
+  <name>Jetty :: Infinispan Session Managers</name>
+  <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.infinispan</bundle-symbolic-name>
+    <infinispan.version>7.1.1.Final</infinispan.version>
+  </properties>
+  <build>
+    <defaultGoal>install</defaultGoal>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+            <configuration>
+              <descriptorRefs>
+                <descriptorRef>config</descriptorRef>
+              </descriptorRefs>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+          </archive>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+         <groupId>org.infinispan</groupId>
+         <artifactId>infinispan-core</artifactId>
+         <version>${infinispan.version}</version>
+    </dependency>
+    <dependency>
+       <groupId>org.eclipse.jetty</groupId>
+       <artifactId>jetty-server</artifactId>
+       <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/jetty-infinispan/src/main/config/etc/jetty-infinispan.xml b/jetty-infinispan/src/main/config/etc/jetty-infinispan.xml
new file mode 100644
index 0000000..49d7ab6
--- /dev/null
+++ b/jetty-infinispan/src/main/config/etc/jetty-infinispan.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <!-- ===================================================================== -->
+  <!-- Get a reference to the default local cache.                           -->
+  <!-- ===================================================================== -->
+  <New id="local" class="org.infinispan.manager.DefaultCacheManager">
+     <Get id="cache" name="cache"></Get>
+  </New>
+
+  <!-- ===================================================================== -->
+  <!-- Get a reference to a hotrod client for a remote cache.               -->
+  <!-- Change the name of the cache to match your setup.                    -->
+  <!-- ===================================================================== -->
+  <!--
+  <New id="hotrodMgr" class="org.infinispan.client.hotrod.RemoteCacheManager">
+    <Call id="cache" name="getCache">
+      <Arg>sessions</Arg>
+    </Call>
+  </New>
+  -->
+
+
+  <!-- ===================================================================== -->
+  <!-- Configure a SessionIdManager with the cache selected above.          -->
+  <!-- ===================================================================== -->
+  <Set name="sessionIdManager">
+    <New id="idMgr" class="org.eclipse.jetty.session.infinispan.InfinispanSessionIdManager">
+      <Arg>
+        <Ref refid="Server"/>
+      </Arg>
+      <Set name="workerName"><Property name="jetty.infinispanSession.workerName" default="node1"/></Set>
+      <Set name="cache"><Ref refid="cache"/></Set>
+    </New>
+  </Set>
+
+</Configure>
diff --git a/jetty-infinispan/src/main/config/modules/infinispan.mod b/jetty-infinispan/src/main/config/modules/infinispan.mod
new file mode 100644
index 0000000..afa39fc
--- /dev/null
+++ b/jetty-infinispan/src/main/config/modules/infinispan.mod
@@ -0,0 +1,33 @@
+#
+# Jetty Infinispan module
+#
+
+[depend]
+annotations
+webapp
+
+[files]
+maven://org.infinispan/infinispan-core/7.1.1.Final|lib/infinispan/infinispan-core-7.1.1.Final.jar
+maven://org.infinispan/infinispan-commons/7.1.1.Final|lib/infinispan/infinispan-commons-7.1.1.Final.jar
+maven://org.jgroups/jgroups/3.6.1.Final|lib/infinispan/jgroups-3.6.1.Final.jar
+maven://org.jboss.marshalling/jboss-marshalling-osgi/1.4.4.Final|lib/infinispan/jboss-marshalling-osgi-1.4.4.Final.jar
+maven://org.jboss.logging/jboss-logging/3.1.2.GA|lib/infinispan/jboss-logging-3.1.2.GA.jar
+
+[lib]
+lib/jetty-infinispan-${jetty.version}.jar
+lib/infinispan/*.jar
+
+[xml]
+etc/jetty-infinispan.xml
+
+[license]
+Infinispan is an open source project hosted on Github and released under the Apache 2.0 license.
+http://infinispan.org/
+http://www.apache.org/licenses/LICENSE-2.0.html
+
+[ini-template]
+## Infinispan Session config
+
+## Unique identifier for this node in the cluster
+# jetty.infinispanSession.workerName=node1
+
diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java
new file mode 100644
index 0000000..6888f69
--- /dev/null
+++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java
@@ -0,0 +1,374 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.session.infinispan;
+
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+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.server.session.AbstractSession;
+import org.eclipse.jetty.server.session.AbstractSessionIdManager;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.infinispan.commons.api.BasicCache;
+
+
+
+
+/**
+ * InfinispanSessionIdManager
+ *
+ * Maintain a set of in-use session ids. This session id manager does NOT locally store 
+ * a list of in-use sesssion ids, but rather stores them in the cluster cache. Thus,
+ * all operations to this session manager involve interaction with a possibly remote
+ * cache.
+ * 
+ * For each session id that is in-use, an entry of the following form is put into 
+ * the cluster cache:
+ * <pre>
+ *   ("__o.e.j.s.infinispanIdMgr__"+[id], [id])
+ * </pre>
+ * where [id] is the id of the session.
+ * 
+ * If the first session to be added is not immortal (ie it has a timeout on it) then
+ * the corresponding session id is entered into infinispan with an idle expiry timeout
+ * equivalent to double the session's timeout (the multiplier is configurable).
+ * 
+ * 
+ * Having one entry per in-use session id means that there is no contention on
+ * cache entries (as would be the case if a single entry was kept containing a 
+ * list of in-use session ids).
+ * 
+ * 
+ */
+public class InfinispanSessionIdManager extends AbstractSessionIdManager
+{
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    public final static String ID_KEY = "__o.e.j.s.infinispanIdMgr__";
+    public static final int DEFAULT_IDLE_EXPIRY_MULTIPLE = 2;
+    protected BasicCache<String,Object> _cache;
+    private Server _server;
+    private int _idleExpiryMultiple = DEFAULT_IDLE_EXPIRY_MULTIPLE;
+
+    
+    
+    
+    
+    public InfinispanSessionIdManager(Server server)
+    {
+        super();
+        _server = server;
+    }
+
+    public InfinispanSessionIdManager(Server server, Random random)
+    {
+       super(random);
+       _server = server;
+    }
+
+    
+    
+    /** 
+     * Start the id manager.
+     * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        super.doStart();
+    }
+
+    
+    
+    /** 
+     * Stop the id manager
+     * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+    }
+
+    
+   
+
+    
+    /** 
+     * Check to see if the given session id is being
+     * used by a session in any context.
+     * 
+     * This method will consult the cluster.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#idInUse(java.lang.String)
+     */
+    @Override
+    public boolean idInUse(String id)
+    {
+        if (id == null)
+            return false;
+        
+        String clusterId = getClusterId(id);
+        
+        //ask the cluster - this should also tickle the idle expiration timer on the sessionid entry
+        //keeping it valid
+        try
+        {
+            return exists(clusterId);
+        }
+        catch (Exception e)
+        {
+            LOG.warn("Problem checking inUse for id="+clusterId, e);
+            return false;
+        }
+        
+    }
+
+    /** 
+     * Remember a new in-use session id.
+     * 
+     * This will save the in-use session id to the cluster.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#addSession(javax.servlet.http.HttpSession)
+     */
+    @Override
+    public void addSession(HttpSession session)
+    {
+        if (session == null)
+            return;
+
+        //insert into the cache and set an idle expiry on the entry that
+        //is based off the max idle time configured for the session. If the
+        //session is immortal, then there is no idle expiry on the corresponding
+        //session id
+        if (session.getMaxInactiveInterval() == 0)
+            insert (((AbstractSession)session).getClusterId());
+        else
+            insert (((AbstractSession)session).getClusterId(), session.getMaxInactiveInterval() * getIdleExpiryMultiple());
+    }
+    
+    
+    public void setIdleExpiryMultiple (int multiplier)
+    {
+        if (multiplier <= 1)
+        {
+            LOG.warn("Idle expiry multiple of {} for session ids set to less than minimum. Using value of {} instead.", multiplier, DEFAULT_IDLE_EXPIRY_MULTIPLE);
+        }
+        _idleExpiryMultiple = multiplier;
+    }
+
+    public int getIdleExpiryMultiple ()
+    {
+        return _idleExpiryMultiple;
+    }
+    
+    
+    /** 
+     * Remove a session id from the list of in-use ids.
+     * 
+     * This will remvove the corresponding session id from the cluster.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#removeSession(javax.servlet.http.HttpSession)
+     */
+    @Override
+    public void removeSession(HttpSession session)
+    {
+        if (session == null)
+            return;
+
+        //delete from the cache
+        delete (((AbstractSession)session).getClusterId());
+    }
+
+    /** 
+     * Remove a session id. This compels all other contexts who have a session
+     * with the same id to also remove it.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
+     */
+    @Override
+    public void invalidateAll(String id)
+    {
+        //delete the session id from list of in-use sessions
+        delete (id);
+
+
+        //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 = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+            if (sessionHandler != null)
+            {
+                SessionManager manager = sessionHandler.getSessionManager();
+
+                if (manager != null && manager instanceof InfinispanSessionManager)
+                {
+                    ((InfinispanSessionManager)manager).invalidateSession(id);
+                }
+            }
+        }
+
+    }
+
+    /** 
+     * Change a session id. 
+     * 
+     * Typically this occurs when a previously existing session has passed through authentication.
+     * 
+     * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#renewSessionId(java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest)
+     */
+    @Override
+    public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request)
+    {
+        //generate a new id
+        String newClusterId = newSessionId(request.hashCode());
+
+        delete(oldClusterId);
+        insert(newClusterId);
+
+
+        //tell all contexts to update the id 
+        Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
+        for (int i=0; contexts!=null && i<contexts.length; i++)
+        {
+            SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+            if (sessionHandler != null) 
+            {
+                SessionManager manager = sessionHandler.getSessionManager();
+
+                if (manager != null && manager instanceof InfinispanSessionManager)
+                {
+                    ((InfinispanSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Get the cache.
+     * @return the cache
+     */
+    public BasicCache<String,Object> getCache() 
+    {
+        return _cache;
+    }
+
+    /**
+     * Set the cache.
+     * @param cache the cache
+     */
+    public void setCache(BasicCache<String,Object> cache) 
+    {
+        this._cache = cache;
+    }
+    
+    
+    
+    /**
+     * Do any operation to the session id in the cache to
+     * ensure its idle expiry time moves forward
+     * @param id the session id
+     */
+    public void touch (String id)
+    {
+        exists(id);
+    }
+    
+    
+    
+    /**
+     * Ask the cluster if a particular id exists.
+     * 
+     * @param id the session id
+     * @return true if exists
+     */
+    protected boolean exists (String id)
+    {
+        if (_cache == null)
+            throw new IllegalStateException ("No cache");
+        
+        return _cache.containsKey(makeKey(id));
+    }
+    
+
+    /**
+     * Put a session id into the cluster.
+     * 
+     * @param id the session id
+     */
+    protected void insert (String id)
+    {        
+        if (_cache == null)
+            throw new IllegalStateException ("No cache");
+        
+        _cache.putIfAbsent(makeKey(id), id);
+    }
+    
+    
+    /**
+     * Put a session id into the cluster with an idle expiry.
+     * 
+     * @param id the session id
+     * @param idleTimeOutSec idle timeout in seconds
+     */
+    protected void insert (String id, long idleTimeOutSec)
+    {
+        if (_cache == null)
+            throw new IllegalStateException ("No cache");
+        
+        _cache.putIfAbsent(makeKey(id),id,-1L, TimeUnit.SECONDS, idleTimeOutSec, TimeUnit.SECONDS);
+    }
+   
+    
+    /**
+     * Remove a session id from the cluster.
+     * 
+     * @param id the session id
+     */
+    protected void delete (String id)
+    {
+        if (_cache == null)
+            throw new IllegalStateException ("No cache");
+        
+        _cache.remove(makeKey(id));
+    }
+    
+    
+
+    /**
+     * Generate a unique cache key from the session id.
+     * 
+     * @param id the session id
+     * @return unique cache id
+     */
+    protected String makeKey (String id)
+    {
+        return ID_KEY+id;
+    }
+}
diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java
new file mode 100644
index 0000000..cf021d8
--- /dev/null
+++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java
@@ -0,0 +1,1170 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.session.infinispan;
+
+import java.io.IOException;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandler.Context;
+import org.eclipse.jetty.server.session.AbstractSession;
+import org.eclipse.jetty.server.session.AbstractSessionManager;
+import org.eclipse.jetty.server.session.MemSession;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
+import org.eclipse.jetty.util.thread.Scheduler;
+import org.infinispan.Cache;
+import org.infinispan.commons.api.BasicCache;
+import org.omg.CORBA._IDLTypeStub;
+
+/**
+ * InfinispanSessionManager
+ * 
+ * The data for a session relevant to a particular context is stored in an Infinispan (clustered) cache:
+ * <pre>
+ * Key:   is the id of the session + the context path + the vhost for the context 
+ * Value: is the data of the session
+ * </pre>
+ * 
+ * The key is necessarily complex because the same session id can be in-use by more than one
+ * context. In this case, the contents of the session will strictly be different for each
+ * context, although the id will be the same.
+ * 
+ * Sessions are also kept in local memory when they are used by this session manager. This allows
+ * multiple different request threads in the same context to call Request.getSession() and
+ * obtain the same object.
+ * 
+ * This session manager support scavenging, which is only done over the set of sessions in its
+ * local memory. This can result in some sessions being "stranded" in the cluster cache if no
+ * session manager is currently managing it (eg the node managing the session crashed and it
+ * was never requested on another node).
+ * 
+ */
+public class InfinispanSessionManager extends AbstractSessionManager
+{
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    
+    /**
+     * Clustered cache of sessions
+     */
+    private BasicCache<String, Object> _cache;
+    
+    
+    /**
+     * Sessions known to this node held in memory
+     */
+    private ConcurrentHashMap<String, InfinispanSessionManager.Session> _sessions;
+
+    
+    /**
+     * The length of time a session can be in memory without being checked against
+     * the cluster. A value of 0 indicates that the session is never checked against
+     * the cluster - the current node is considered to be the master for the session.
+     *
+     */
+    private long _staleIntervalSec = 0;
+    
+    protected Scheduler.Task _task; //scavenge task
+    protected Scheduler _scheduler;
+    protected Scavenger _scavenger;
+    protected long _scavengeIntervalMs = 1000L * 60 * 10; //10mins
+    protected boolean _ownScheduler;
+    
+    
+
+    /**
+     * Scavenger
+     *
+     */
+    protected class Scavenger implements Runnable
+    {
+
+        @Override
+        public void run()
+        {
+           try
+           {
+               scavenge();
+           }
+           finally
+           {
+               if (_scheduler != null && _scheduler.isRunning())
+                   _task = _scheduler.schedule(this, _scavengeIntervalMs, TimeUnit.MILLISECONDS);
+           }
+        }
+    }
+    
+    
+    /*
+     * Every time a Session is put into the cache one of these objects
+     * is created to copy the data out of the in-memory session, and 
+     * every time an object is read from the cache one of these objects
+     * a fresh Session object is created based on the data held by this
+     * object.
+     */
+    public class SerializableSessionData implements Serializable
+    {
+        /**
+         * 
+         */
+        private static final long serialVersionUID = -7779120106058533486L;
+        String clusterId;
+        String contextPath;
+        String vhost;
+        long accessed;
+        long lastAccessed;
+        long createTime;
+        long cookieSetTime;
+        String lastNode;
+        long expiry;
+        long maxInactive;
+        Map<String, Object> attributes;
+
+        public SerializableSessionData()
+        {
+
+        }
+
+       
+       public SerializableSessionData(Session s)
+       {
+           clusterId = s.getClusterId();
+           contextPath = s.getContextPath();
+           vhost = s.getVHost();
+           accessed = s.getAccessed();
+           lastAccessed = s.getLastAccessedTime();
+           createTime = s.getCreationTime();
+           cookieSetTime = s.getCookieSetTime();
+           lastNode = s.getLastNode();
+           expiry = s.getExpiry();
+           maxInactive = s.getMaxInactiveInterval();
+           attributes = s.getAttributeMap(); // TODO pointer, not a copy
+       }
+        
+        private void writeObject(java.io.ObjectOutputStream out) throws IOException
+        {  
+            out.writeUTF(clusterId); //session id
+            out.writeUTF(contextPath); //context path
+            out.writeUTF(vhost); //first vhost
+
+            out.writeLong(accessed);//accessTime
+            out.writeLong(lastAccessed); //lastAccessTime
+            out.writeLong(createTime); //time created
+            out.writeLong(cookieSetTime);//time cookie was set
+            out.writeUTF(lastNode); //name of last node managing
+      
+            out.writeLong(expiry); 
+            out.writeLong(maxInactive);
+            out.writeObject(attributes);
+        }
+        
+        private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
+        {
+            clusterId = in.readUTF();
+            contextPath = in.readUTF();
+            vhost = in.readUTF();
+            
+            accessed = in.readLong();//accessTime
+            lastAccessed = in.readLong(); //lastAccessTime
+            createTime = in.readLong(); //time created
+            cookieSetTime = in.readLong();//time cookie was set
+            lastNode = in.readUTF(); //last managing node
+            expiry = in.readLong(); 
+            maxInactive = in.readLong();
+            attributes = (HashMap<String,Object>)in.readObject();
+        }
+        
+    }
+    
+ 
+    
+    
+    /**
+     * Session
+     *
+     * Representation of a session in local memory.
+     */
+    public class Session extends MemSession
+    {
+        
+        private ReentrantLock _lock = new ReentrantLock();
+        
+        /**
+         * The (canonical) context path for with which this session is associated
+         */
+        private String _contextPath;
+        
+        
+        
+        /**
+         * The time in msec since the epoch at which this session should expire
+         */
+        private long _expiryTime; 
+        
+        
+        /**
+         * Time in msec since the epoch at which this session was last read from cluster
+         */
+        private long _lastSyncTime;
+        
+        
+        /**
+         * The workername of last node known to be managing the session
+         */
+        private String _lastNode;
+        
+        
+        /**
+         * If dirty, session needs to be (re)sent to cluster
+         */
+        protected boolean _dirty=false;
+        
+        
+     
+
+        /**
+         * Any virtual hosts for the context with which this session is associated
+         */
+        private String _vhost;
+
+        
+        /**
+         * Count of how many threads are active in this session
+         */
+        private AtomicInteger _activeThreads = new AtomicInteger(0);
+        
+        
+        
+        
+        /**
+         * A new session.
+         * 
+         * @param request the request
+         */
+        protected Session (HttpServletRequest request)
+        {
+            super(InfinispanSessionManager.this,request);
+            long maxInterval = getMaxInactiveInterval();
+            _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
+            _lastNode = getSessionIdManager().getWorkerName();
+           setVHost(InfinispanSessionManager.getVirtualHost(_context));
+           setContextPath(InfinispanSessionManager.getContextPath(_context));
+           _activeThreads.incrementAndGet(); //access will not be called on a freshly created session so increment here
+        }
+        
+        
+        protected Session (SerializableSessionData sd)
+        {
+            super(InfinispanSessionManager.this, sd.createTime, sd.accessed, sd.clusterId);
+            _expiryTime = (sd.maxInactive <= 0 ? 0 : (System.currentTimeMillis() + sd.maxInactive*1000L));
+            setLastNode(sd.lastNode);
+            setContextPath(sd.contextPath);
+            setVHost(sd.vhost);
+            addAttributes(sd.attributes);
+        }
+        
+        
+        /**
+         * A restored session.
+         * 
+         * @param sessionId the session id
+         * @param created time created
+         * @param accessed time last accessed
+         * @param maxInterval max expiry interval
+         */
+        protected Session (String sessionId, long created, long accessed, long maxInterval)
+        {
+            super(InfinispanSessionManager.this, created, accessed, sessionId);
+            _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
+        }
+        
+        /** 
+         * Called on entry to the session.
+         * 
+         * @see org.eclipse.jetty.server.session.AbstractSession#access(long)
+         */
+        @Override
+        protected boolean access(long time)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Access session({}) for context {} on worker {}", getId(), getContextPath(), getSessionIdManager().getWorkerName());
+            try
+            {
+
+                long now = System.currentTimeMillis();
+                //lock so that no other thread can call access or complete until the first one has refreshed the session object if necessary
+                _lock.lock();
+                //a request thread is entering
+                if (_activeThreads.incrementAndGet() == 1)
+                {
+                    //if the first thread, check that the session in memory is not stale, if we're checking for stale sessions
+                    if (getStaleIntervalSec() > 0  && (now - getLastSyncTime()) >= (getStaleIntervalSec() * 1000L))
+                    {
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("Acess session({}) for context {} on worker {} stale session. Reloading.", getId(), getContextPath(), getSessionIdManager().getWorkerName());
+                        refresh();
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                LOG.warn(e);
+            }
+            finally
+            {            
+                _lock.unlock();
+            }
+
+            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()
+        {
+            super.complete();
+
+            //lock so that no other thread that might be calling access can proceed until this complete is done
+            _lock.lock();
+
+            try
+            {
+                //if this is the last request thread to be in the session
+                if (_activeThreads.decrementAndGet() == 0)
+                {
+                    try
+                    {
+                        //an invalid session will already have been removed from the
+                        //local session map and deleted from the cluster. If its valid save
+                        //it to the cluster.
+                        //TODO consider doing only periodic saves if only the last access
+                        //time to the session changes
+                        if (isValid())
+                        {
+                            //if session still valid && its dirty or stale or never been synced, write it to the cluster
+                            //otherwise, we just keep the updated last access time in memory
+                            if (_dirty || getLastSyncTime() == 0 || isStale(System.currentTimeMillis()))
+                            {
+                                willPassivate();
+                                save(this);
+                                didActivate();
+                            }
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        LOG.warn("Problem saving session({})",getId(), e);
+                    } 
+                    finally
+                    {
+                        _dirty = false;
+                    }
+                }
+            }
+            finally
+            {
+                _lock.unlock();
+            }
+        }
+        
+        /** Test if the session is stale
+         * @param atTime time when stale
+         * @return true if stale
+         */
+        protected boolean isStale (long atTime)
+        {
+            return (getStaleIntervalSec() > 0) && (atTime - getLastSyncTime() >= (getStaleIntervalSec()*1000L));
+        }
+        
+        
+        /** Test if the session is dirty
+         * @return true if dirty
+         */
+        protected boolean isDirty ()
+        {
+            return _dirty;
+        }
+
+        /** 
+         * Expire the session.
+         * 
+         * @see org.eclipse.jetty.server.session.AbstractSession#timeout()
+         */
+        @Override
+        protected void timeout()
+        {
+            super.timeout();
+        }
+        
+      
+        
+        /**
+         * Reload the session from the cluster. If the node that
+         * last managed the session from the cluster is ourself,
+         * then the session does not need refreshing.
+         * NOTE: this method MUST be called with sufficient locks
+         * in place to prevent 2 or more concurrent threads from
+         * simultaneously updating the session.
+         */
+        private void refresh ()
+        {
+            //get fresh copy from the cluster
+            Session fresh = load(makeKey(getClusterId(), _context));
+
+            //if the session no longer exists, invalidate
+            if (fresh == null)
+            {
+                invalidate();
+                return;
+            }
+
+            //cluster copy assumed to be the same as we were the last
+            //node to manage it
+            if (fresh.getLastNode().equals(getLastNode()))
+                return;
+
+            setLastNode(getSessionIdManager().getWorkerName());
+            
+            //prepare for refresh
+            willPassivate();
+
+            //if fresh has no attributes, remove them
+            if (fresh.getAttributes() == 0)
+                this.clearAttributes();
+            else
+            {
+                //reconcile attributes
+                for (String key:fresh.getAttributeMap().keySet())
+                {
+                    Object freshvalue = fresh.getAttribute(key);
+
+                    //session does not already contain this attribute, so bind it
+                    if (getAttribute(key) == null)
+                    { 
+                        doPutOrRemove(key,freshvalue);
+                        bindValue(key,freshvalue);
+                    }
+                    else //session already contains this attribute, update its value
+                    {
+                        doPutOrRemove(key,freshvalue);
+                    }
+
+                }
+                // cleanup, remove values from session, that don't exist in data anymore:
+                for (String key : getNames())
+                {
+                    if (fresh.getAttribute(key) == null)
+                    {
+                        Object oldvalue = getAttribute(key);
+                        doPutOrRemove(key,null);
+                        unbindValue(key,oldvalue);
+                    }
+                }
+            }
+            //finish refresh
+            didActivate();
+        }
+
+
+        public void setExpiry (long expiry)
+        {
+            _expiryTime = expiry;
+        }
+        
+
+        public long getExpiry ()
+        {
+            return _expiryTime;
+        }
+        
+        public void swapId (String newId, String newNodeId)
+        {
+            //TODO probably synchronize rather than use the access/complete lock?
+            _lock.lock();
+            setClusterId(newId);
+            setNodeId(newNodeId);
+            _lock.unlock();
+        }
+        
+        @Override
+        public void setAttribute (String name, Object value)
+        {
+            Object old = changeAttribute(name, value);
+            if (value == null && old == null)
+                return; //if same as remove attribute but attribute was already removed, no change
+            
+           _dirty = true;
+        }
+        
+        
+        public String getContextPath()
+        {
+            return _contextPath;
+        }
+
+
+        public void setContextPath(String contextPath)
+        {
+            this._contextPath = contextPath;
+        }
+
+
+        public String getVHost()
+        {
+            return _vhost;
+        }
+
+
+        public void setVHost(String vhost)
+        {
+            this._vhost = vhost;
+        }
+        
+        public String getLastNode()
+        {
+            return _lastNode;
+        }
+
+
+        public void setLastNode(String lastNode)
+        {
+            _lastNode = lastNode;
+        }
+
+
+        public long getLastSyncTime()
+        {
+            return _lastSyncTime;
+        }
+
+
+        public void setLastSyncTime(long lastSyncTime)
+        {
+            _lastSyncTime = lastSyncTime;
+        }
+
+    }
+
+
+
+    
+    /**
+     * 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");
+        
+        if (_cache == null)
+            throw new IllegalStateException("No session cache defined");
+        
+        _sessions = new ConcurrentHashMap<String, Session>();
+
+        //try and use a common scheduler, fallback to own
+        _scheduler = getSessionHandler().getServer().getBean(Scheduler.class);
+        if (_scheduler == null)
+        {
+            _scheduler = new ScheduledExecutorScheduler();
+            _ownScheduler = true;
+            _scheduler.start();
+        }
+        else if (!_scheduler.isStarted())
+            throw new IllegalStateException("Shared scheduler not started");
+ 
+        setScavengeInterval(getScavengeInterval());
+        
+        super.doStart();
+    }
+
+
+    /**
+     * Stop the session manager.
+     *
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop()
+     */
+    @Override
+    public void doStop() throws Exception
+    {
+        super.doStop();
+
+        if (_task!=null)
+            _task.cancel();
+        _task=null;
+        if (_ownScheduler && _scheduler !=null)
+            _scheduler.stop();
+        _scheduler = null;
+        
+        _sessions.clear();
+        _sessions = null;
+    }
+    
+    
+    
+    /**
+     * Look for sessions in local memory that have expired.
+     */
+    /**
+     * 
+     */
+    public void scavenge ()
+    {
+        Set<String> candidateIds = new HashSet<String>();
+        long now = System.currentTimeMillis();
+        
+        LOG.info("SessionManager for context {} scavenging at {} ", getContextPath(getContext()), now);
+        for (Map.Entry<String, Session> entry:_sessions.entrySet())
+        {
+            long expiry = entry.getValue().getExpiry();
+            if (expiry > 0 && expiry < now)
+                candidateIds.add(entry.getKey());
+        }
+
+        for (String candidateId:candidateIds)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Session {} expired ", candidateId);
+            
+            Session candidateSession = _sessions.get(candidateId);
+            if (candidateSession != null)
+            {
+                //double check the state of the session in the cache, as the
+                //session may have migrated to another node. This leaves a window
+                //where the cached session may have been changed by another node
+                Session cachedSession = load(makeKey(candidateId, _context));
+                if (cachedSession == null)
+                {
+                   if (LOG.isDebugEnabled()) LOG.debug("Locally expired session({}) does not exist in cluster ",candidateId);
+                    //the session no longer exists, do a full invalidation
+                    candidateSession.timeout();
+                }
+                else if (getSessionIdManager().getWorkerName().equals(cachedSession.getLastNode()))
+                {
+                    if (LOG.isDebugEnabled()) LOG.debug("Expiring session({}) local to session manager",candidateId);
+                    //if I am the master of the session then it can be timed out
+                    candidateSession.timeout();
+                }
+                else
+                {
+                    //some other node is the master of the session, simply remove it from my memory
+                    if (LOG.isDebugEnabled()) LOG.debug("Session({}) not local to this session manager, removing from local memory", candidateId);
+                    candidateSession.willPassivate();
+                    _sessions.remove(candidateSession.getClusterId());
+                }
+
+            }
+        }
+    }
+    
+    
+
+    public long getScavengeInterval ()
+    {
+        return _scavengeIntervalMs/1000;
+    }
+
+    
+    
+    /**
+     * Set the interval between runs of the scavenger. It should not be run too
+     * often.
+     * 
+     * 
+     * @param sec scavenge interval in seconds
+     */
+    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");
+        
+        synchronized (this)
+        {
+            if (_scheduler != null && (period!=old_period || _task==null))
+            {
+                if (_task!=null)
+                    _task.cancel();
+                if (_scavenger == null)
+                    _scavenger = new Scavenger();
+                
+                _task = _scheduler.schedule(_scavenger,_scavengeIntervalMs,TimeUnit.MILLISECONDS);
+            }
+        }
+    }
+    
+    
+    
+
+    /**
+     * Get the clustered cache instance.
+     * 
+     * @return the cache
+     */
+    public BasicCache<String, Object> getCache() 
+    {
+        return _cache;
+    }
+
+    
+    
+    /**
+     * Set the clustered cache instance.
+     * 
+     * @param cache the cache
+     */
+    public void setCache (BasicCache<String, Object> cache) 
+    {
+        this._cache = cache;
+    }
+
+
+    
+    
+    
+    public long getStaleIntervalSec()
+    {
+        return _staleIntervalSec;
+    }
+
+
+    public void setStaleIntervalSec(long staleIntervalSec)
+    {
+        _staleIntervalSec = staleIntervalSec;
+    }
+
+
+    /** 
+     * Add a new session for the context related to this session manager
+     * 
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
+     */
+    @Override
+    protected void addSession(AbstractSession session)
+    {
+        if (session==null)
+            return;
+        
+        if (LOG.isDebugEnabled()) LOG.debug("Adding session({}) to session manager for context {} on worker {}",session.getClusterId(), getContextPath(getContext()),getSessionIdManager().getWorkerName() + " with lastnode="+((Session)session).getLastNode());
+        _sessions.put(session.getClusterId(), (Session)session);
+        
+        try
+        {     
+                session.willPassivate();
+                save(((InfinispanSessionManager.Session)session));
+                session.didActivate();
+            
+        }
+        catch (Exception e)
+        {
+            LOG.warn("Unable to store new session id="+session.getId() , e);
+        }
+    }
+
+    /** 
+     * Ask the cluster for the session.
+     * 
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
+     */
+    @Override
+    public AbstractSession getSession(String idInCluster)
+    {
+        Session session = null;
+
+        //try and find the session in this node's memory
+        Session memSession = (Session)_sessions.get(idInCluster);
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("getSession({}) {} in session map",idInCluster,(memSession==null?"not":""));
+
+        long now = System.currentTimeMillis();
+        try
+        {
+            //if the session is not in this node's memory, then load it from the cluster cache
+            if (memSession == null)
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("getSession({}): loading session data from cluster", idInCluster);
+
+                session = load(makeKey(idInCluster, _context));
+                if (session != null)
+                {
+                    //We retrieved a session with the same key from the database
+
+                    //Check that it wasn't expired
+                    if (session.getExpiry() > 0 && session.getExpiry() <= now)
+                    {
+                        if (LOG.isDebugEnabled()) LOG.debug("getSession ({}): Session expired", idInCluster);
+                        //ensure that the session id for the expired session is deleted so that a new session with the 
+                        //same id cannot be created (because the idInUse() test would succeed)
+                        ((InfinispanSessionIdManager)getSessionIdManager()).removeSession(session);
+                        return null;  
+                    }
+
+                    //Update the last worker node to me
+                    session.setLastNode(getSessionIdManager().getWorkerName());                            
+                    //TODO consider saving session here if lastNode was not this node
+
+                    //Check that another thread hasn't loaded the same session
+                    Session existingSession = _sessions.putIfAbsent(idInCluster, session);
+                    if (existingSession != null)
+                    {
+                        //use the one that the other thread inserted
+                        session = existingSession;
+                        LOG.debug("getSession({}): using session loaded by another request thread ", idInCluster);
+                    }
+                    else
+                    {
+                        //indicate that the session was reinflated
+                        session.didActivate();
+                        LOG.debug("getSession({}): loaded session from cluster", idInCluster);
+                    }
+                    return session;
+                }
+                else
+                {
+                    //The requested session does not exist anywhere in the cluster
+                    LOG.debug("getSession({}): No session in cluster matching",idInCluster);
+                    return null;
+                }
+            }
+            else
+            {
+               //The session exists in this node's memory
+               LOG.debug("getSession({}): returning session from local memory ", memSession.getClusterId());
+                return memSession;
+            }
+        }
+        catch (Exception e)
+        {
+            LOG.warn("Unable to load session="+idInCluster, e);
+            return null;
+        }
+    }
+    
+    
+
+    /** 
+     * The session manager is stopping.
+     * 
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#shutdownSessions()
+     */
+    @Override
+    protected void shutdownSessions() throws Exception
+    {
+        Set<String> keys = new HashSet<String>(_sessions.keySet());
+        for (String key:keys)
+        {
+            Session session = _sessions.remove(key); //take the session out of the session list
+            //If the session is dirty, then write it to the cluster.
+            //If the session is simply stale do NOT write it to the cluster, as some other node
+            //may have started managing that session - this means that the last accessed/expiry time
+            //will not be updated, meaning it may look like it can expire sooner than it should.
+            try
+            {
+                if (session.isDirty())
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Saving dirty session {} before exiting ", session.getId());
+                    save(session);
+                }
+            }
+            catch (Exception e)
+            {
+                LOG.warn(e);
+            }
+        }
+    }
+
+
+    @Override
+    protected AbstractSession newSession(HttpServletRequest request)
+    {
+        return new Session(request);
+    }
+
+    /** 
+     * Remove a session from local memory, and delete it from
+     * the cluster cache.
+     * 
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
+     */
+    @Override
+    protected boolean removeSession(String idInCluster)
+    {
+        Session session = (Session)_sessions.remove(idInCluster);
+        try
+        {
+            if (session != null)
+                delete(session);
+        }
+        catch (Exception e)
+        {
+            LOG.warn("Problem deleting session id="+idInCluster, e);
+        }
+        return session!=null;
+    }
+    
+    
+    
+    
+    @Override
+    public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
+    {
+        Session session = null;
+        try
+        {
+            //take the session with that id out of our managed list
+            session = (Session)_sessions.remove(oldClusterId);
+            if (session != null)
+            {
+                //TODO consider transactionality and ramifications if the session is live on another node
+                delete(session); //delete the old session from the cluster  
+                session.swapId(newClusterId, newNodeId); //update the session
+                _sessions.put(newClusterId, session); //put it into managed list under new key
+                save(session); //put the session under the new id into the cluster
+            }
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+        }
+
+        super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
+    }
+
+
+    /**
+     * Load a session from the clustered cache.
+     * 
+     * @param key the session key
+     * @return the session
+     */
+    protected Session load (String key)
+    {
+        if (_cache == null)
+            throw new IllegalStateException("No cache");
+        
+        if (LOG.isDebugEnabled()) LOG.debug("Loading session {} from cluster", key);
+
+        SerializableSessionData storableSession = (SerializableSessionData)_cache.get(key);
+        if (storableSession == null)
+        {
+            if (LOG.isDebugEnabled()) LOG.debug("No session {} in cluster ",key);
+            return null;
+        }
+        else
+        {
+            Session session = new Session (storableSession);
+            session.setLastSyncTime(System.currentTimeMillis());
+            return session;
+        }
+    }
+    
+    
+    
+    /**
+     * Save or update the session to the cluster cache
+     * 
+     * @param session the session
+     * @throws Exception if unable to save
+     */
+    protected void save (InfinispanSessionManager.Session session)
+    throws Exception
+    {
+        if (_cache == null)
+            throw new IllegalStateException("No cache");
+        
+        if (LOG.isDebugEnabled()) LOG.debug("Writing session {} to cluster", session.getId());
+    
+        SerializableSessionData storableSession = new SerializableSessionData(session);
+
+        //Put an idle timeout on the cache entry if the session is not immortal - 
+        //if no requests arrive at any node before this timeout occurs, or no node 
+        //scavenges the session before this timeout occurs, the session will be removed.
+        //NOTE: that no session listeners can be called for this.
+        InfinispanSessionIdManager sessionIdManager = (InfinispanSessionIdManager)getSessionIdManager();
+        if (storableSession.maxInactive > 0)
+            _cache.put(makeKey(session, _context), storableSession, -1, TimeUnit.SECONDS, storableSession.maxInactive*sessionIdManager.getIdleExpiryMultiple(), TimeUnit.SECONDS);
+        else
+            _cache.put(makeKey(session, _context), storableSession);
+        
+        //tickle the session id manager to keep the sessionid entry for this session up-to-date
+        sessionIdManager.touch(session.getClusterId());
+        
+        session.setLastSyncTime(System.currentTimeMillis());
+    }
+    
+    
+    
+    /**
+     * Remove the session from the cluster cache.
+     * 
+     * @param session the session
+     */
+    protected void delete (InfinispanSessionManager.Session session)
+    {  
+        if (_cache == null)
+            throw new IllegalStateException("No cache");
+        if (LOG.isDebugEnabled()) LOG.debug("Removing session {} from cluster", session.getId());
+        _cache.remove(makeKey(session, _context));
+    }
+
+    
+    /**
+     * Invalidate a session for this context with the given id
+     * 
+     * @param idInCluster session id in cluster
+     */
+    public void invalidateSession (String idInCluster)
+    {
+        Session session = (Session)_sessions.get(idInCluster);
+
+        if (session != null)
+        {
+            session.invalidate();
+        }
+    }
+
+    
+    /**
+     * Make a unique key for this session.
+     * As the same session id can be used across multiple contexts, to
+     * make it unique, the key must be composed of:
+     * <ol>
+     * <li>the id</li>
+     * <li>the context path</li>
+     * <li>the virtual hosts</li>
+     * </ol>
+     * 
+     *TODO consider the difference between getClusterId and getId
+     * @param session
+     * @return
+     */
+    private String makeKey (Session session, Context context)
+    {
+       return makeKey(session.getId(), context);
+    }
+    
+    /**
+     * Make a unique key for this session.
+     * As the same session id can be used across multiple contexts, to
+     * make it unique, the key must be composed of:
+     * <ol>
+     * <li>the id</li>
+     * <li>the context path</li>
+     * <li>the virtual hosts</li>
+     * </ol>
+     * 
+     *TODO consider the difference between getClusterId and getId
+     * @param session
+     * @return
+     */
+    private String makeKey (String id, Context context)
+    {
+        String key = getContextPath(context);
+        key = key + "_" + getVirtualHost(context);
+        key = key+"_"+id;
+        return key;
+    }
+    
+    /**
+     * Turn the context path into an acceptable string
+     * 
+     * @param context
+     * @return
+     */
+    private static String getContextPath (ContextHandler.Context context)
+    {
+        return canonicalize (context.getContextPath());
+    }
+
+    /**
+     * 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 name from a context path.
+     *
+     * @param path
+     * @return
+     */
+    private static String canonicalize (String path)
+    {
+        if (path==null)
+            return "";
+
+        return path.replace('/', '_').replace('.','_').replace('\\','_');
+    }
+
+}
diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/WebAppMarshaller.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/WebAppMarshaller.java
new file mode 100644
index 0000000..e7fe8b2
--- /dev/null
+++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/WebAppMarshaller.java
@@ -0,0 +1,84 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.session.infinispan;
+
+import org.infinispan.commons.marshall.jboss.AbstractJBossMarshaller;
+import org.jboss.marshalling.ContextClassResolver;
+
+
+/**
+ * WebAppMarshaller
+ *
+ * An implementation of the AbstractJBossMarshaller code that is just
+ * enough to provide a ContextClassResolver that will use the Thread Context Classloader
+ * in order to deserialize session attribute classes.  
+ * 
+ * This is necessary because the standard infinispan marshaller (GenericJBossMarshaller) uses the
+ * classloader of the loader that loaded itself. When using the infinispan module in Jetty, all of
+ * the infinispan classes will be on the container classpath. That means that the GenericJBossMarshaller
+ * returns the container classloader which is unable to load any webapp classes. This class ensures
+ * that it is always the webapp's classloader that will be used.
+ * 
+ * In order to use this class, you should put a hotrod-client.properties file into the
+ * ${jetty.base}/resources directory that contains this line:
+ * 
+ * infinispan.client.hotrod.marshaller=org.eclipse.jetty.session.infinispan.WebAppMarshaller
+ * 
+ * You will also need to add the following lines to a context xml file for your webapp to
+ * permit the webapp's classloader to see the org.eclipse.jetty.session.infinispan classes for
+ * the deserialization to work correctly:
+ * 
+ *  &lt;Call name="prependServerClass"&gt;
+ *   &lt;Arg&gt;-org.eclipse.jetty.session.infinispan.&lt;/Arg&gt;
+ * &lt;/Call&gt;
+ *
+ */
+public class WebAppMarshaller extends AbstractJBossMarshaller 
+{
+
+    /**
+     * WebAppContextClassResolver
+     *
+     * Provides the Thread Context Classloader to use for deserializing.
+     * 
+     */
+    public static class WebAppContextClassResolver extends ContextClassResolver
+    {
+        public WebAppContextClassResolver ()
+        {
+            super();
+        }
+
+        @Override
+        protected ClassLoader getClassLoader()
+        {
+            return Thread.currentThread().getContextClassLoader();
+        }
+    }
+
+
+
+    public WebAppMarshaller ()
+    {
+        super();		
+        baseCfg.setClassResolver(new WebAppContextClassResolver());                         
+    }
+
+
+}
diff --git a/jetty-io/pom.xml b/jetty-io/pom.xml
index 617aa68..5516f8a 100644
--- a/jetty-io/pom.xml
+++ b/jetty-io/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <artifactId>jetty-project</artifactId>
     <groupId>org.eclipse.jetty</groupId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-io</artifactId>
@@ -22,44 +22,10 @@
       <artifactId>jetty-test-helper</artifactId>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-core</artifactId>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>artifact-jar</id>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-        </executions>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java
index be983a2..4534718 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java
@@ -23,12 +23,10 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicReference;
 
 import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.thread.NonBlockingThread;
 
 /**
  * <p>A convenience base implementation of {@link Connection}.</p>
@@ -41,31 +39,20 @@
 {
     private static final Logger LOG = Log.getLogger(AbstractConnection.class);
 
-    public static final boolean EXECUTE_ONFILLABLE=true;
-
     private final List<Listener> listeners = new CopyOnWriteArrayList<>();
-    private final AtomicReference<State> _state = new AtomicReference<>(IDLE);
     private final long _created=System.currentTimeMillis();
     private final EndPoint _endPoint;
     private final Executor _executor;
     private final Callback _readCallback;
-    private final boolean _executeOnfillable;
     private int _inputBufferSize=2048;
 
     protected AbstractConnection(EndPoint endp, Executor executor)
     {
-        this(endp,executor,EXECUTE_ONFILLABLE);
-    }
-
-    protected AbstractConnection(EndPoint endp, Executor executor, final boolean executeOnfillable)
-    {
         if (executor == null)
             throw new IllegalArgumentException("Executor must not be null!");
         _endPoint = endp;
         _executor = executor;
         _readCallback = new ReadCallback();
-        _executeOnfillable=executeOnfillable;
-        _state.set(IDLE);
     }
 
     @Override
@@ -74,6 +61,12 @@
         listeners.add(listener);
     }
 
+    @Override
+    public void removeListener(Listener listener)
+    {
+        listeners.remove(listener);
+    }
+
     public int getInputBufferSize()
     {
         return _inputBufferSize;
@@ -89,9 +82,26 @@
         return _executor;
     }
 
+    @Deprecated
+    public boolean isDispatchIO()
+    {
+        return false;
+    }
+
     protected void failedCallback(final Callback callback, final Throwable x)
     {
-        if (NonBlockingThread.isNonBlockingThread())
+        if (callback.isNonBlocking())
+        {
+            try
+            {
+                callback.failed(x);
+            }
+            catch (Exception e)
+            {
+                LOG.warn(e);
+            }
+        }
+        else
         {
             try
             {
@@ -100,7 +110,14 @@
                     @Override
                     public void run()
                     {
-                        callback.failed(x);
+                        try
+                        {
+                            callback.failed(x);
+                        }
+                        catch (Exception e)
+                        {
+                            LOG.warn(e);
+                        }
                     }
                 });
             }
@@ -110,10 +127,6 @@
                 callback.failed(x);
             }
         }
-        else
-        {
-            callback.failed(x);
-        }
     }
 
     /**
@@ -126,30 +139,22 @@
     {
         if (LOG.isDebugEnabled())
             LOG.debug("fillInterested {}",this);
-
-        while(true)
-        {
-            State state=_state.get();
-            if (next(state,state.fillInterested()))
-                break;
-        }
+        getEndPoint().fillInterested(_readCallback);
     }
 
-    public void fillInterested(Callback callback)
+    public void tryFillInterested()
     {
-        if (LOG.isDebugEnabled())
-            LOG.debug("fillInterested {}",this);
+        tryFillInterested(_readCallback);
+    }
 
-        while(true)
-        {
-            State state=_state.get();
-            // TODO yuck
-            if (state instanceof FillingInterestedCallback && ((FillingInterestedCallback)state)._callback==callback)
-                break;
-            State next=new FillingInterestedCallback(callback,state);
-            if (next(state,next))
-                break;
-        }
+    public void tryFillInterested(Callback callback)
+    {
+        getEndPoint().tryFillInterested(callback);
+    }
+
+    public boolean isFillInterested()
+    {
+        return getEndPoint().isFillInterested();
     }
 
     /**
@@ -176,12 +181,12 @@
                 if (_endPoint.isOutputShutdown())
                     _endPoint.close();
                 else
+                {
                     _endPoint.shutdownOutput();
+                    fillInterested();
+                }
             }
         }
-
-        if (_endPoint.isOpen())
-            fillInterested();
     }
 
     /**
@@ -226,6 +231,12 @@
     }
 
     @Override
+    public boolean onIdleExpired()
+    {
+        return true;
+    }
+
+    @Override
     public int getMessagesIn()
     {
         return -1;
@@ -258,334 +269,24 @@
     @Override
     public String toString()
     {
-        return String.format("%s@%x[%s,%s]",
+        return String.format("%s@%x[%s]",
                 getClass().getSimpleName(),
                 hashCode(),
-                _state.get(),
                 _endPoint);
     }
 
-    public boolean next(State state, State next)
-    {
-        if (next==null)
-            return true;
-        if(_state.compareAndSet(state,next))
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("{}-->{} {}",state,next,this);
-            if (next!=state)
-                next.onEnter(AbstractConnection.this);
-            return true;
-        }
-        return false;
-    }
-
-    private static final class IdleState extends State
-    {
-        private IdleState()
-        {
-            super("IDLE");
-        }
-
-        @Override
-        State fillInterested()
-        {
-            return FILL_INTERESTED;
-        }
-    }
-
-
-    private static final class FillInterestedState extends State
-    {
-        private FillInterestedState()
-        {
-            super("FILL_INTERESTED");
-        }
-
-        @Override
-        public void onEnter(AbstractConnection connection)
-        {
-            connection.getEndPoint().fillInterested(connection._readCallback);
-        }
-
-        @Override
-        State fillInterested()
-        {
-            return this;
-        }
-
-        @Override
-        public State onFillable()
-        {
-            return FILLING;
-        }
-
-        @Override
-        State onFailed()
-        {
-            return IDLE;
-        }
-    }
-
-
-    private static final class RefillingState extends State
-    {
-        private RefillingState()
-        {
-            super("REFILLING");
-        }
-
-        @Override
-        State fillInterested()
-        {
-            return FILLING_FILL_INTERESTED;
-        }
-
-        @Override
-        public State onFilled()
-        {
-            return IDLE;
-        }
-    }
-
-
-    private static final class FillingFillInterestedState extends State
-    {
-        private FillingFillInterestedState(String name)
-        {
-            super(name);
-        }
-
-        @Override
-        State fillInterested()
-        {
-            return this;
-        }
-
-        State onFilled()
-        {
-            return FILL_INTERESTED;
-        }
-    }
-
-
-    private static final class FillingState extends State
-    {
-        private FillingState()
-        {
-            super("FILLING");
-        }
-
-        @Override
-        public void onEnter(AbstractConnection connection)
-        {
-            if (connection._executeOnfillable)
-                connection.getExecutor().execute(connection._runOnFillable);
-            else
-                connection._runOnFillable.run();
-        }
-
-        @Override
-        State fillInterested()
-        {
-            return FILLING_FILL_INTERESTED;
-        }
-
-        @Override
-        public State onFilled()
-        {
-            return IDLE;
-        }
-    }
-
-
-    public static class State
-    {
-        private final String _name;
-        State(String name)
-        {
-            _name=name;
-        }
-
-        @Override
-        public String toString()
-        {
-            return _name;
-        }
-
-        void onEnter(AbstractConnection connection)
-        {
-        }
-
-        State fillInterested()
-        {
-            throw new IllegalStateException(this.toString());
-        }
-
-        State onFillable()
-        {
-            throw new IllegalStateException(this.toString());
-        }
-
-        State onFilled()
-        {
-            throw new IllegalStateException(this.toString());
-        }
-
-        State onFailed()
-        {
-            throw new IllegalStateException(this.toString());
-        }
-    }
-
-
-    public static final State IDLE=new IdleState();
-
-    public static final State FILL_INTERESTED=new FillInterestedState();
-
-    public static final State FILLING=new FillingState();
-
-    public static final State REFILLING=new RefillingState();
-
-    public static final State FILLING_FILL_INTERESTED=new FillingFillInterestedState("FILLING_FILL_INTERESTED");
-
-    public class NestedState extends State
-    {
-        private final State _nested;
-
-        NestedState(State nested)
-        {
-            super("NESTED("+nested+")");
-            _nested=nested;
-        }
-        NestedState(String name,State nested)
-        {
-            super(name+"("+nested+")");
-            _nested=nested;
-        }
-
-        @Override
-        State fillInterested()
-        {
-            return new NestedState(_nested.fillInterested());
-        }
-
-        @Override
-        State onFillable()
-        {
-            return new NestedState(_nested.onFillable());
-        }
-
-        @Override
-        State onFilled()
-        {
-            return new NestedState(_nested.onFilled());
-        }
-    }
-
-
-    public class FillingInterestedCallback extends NestedState
-    {
-        private final Callback _callback;
-
-        FillingInterestedCallback(Callback callback,State nested)
-        {
-            super("FILLING_INTERESTED_CALLBACK",nested==FILLING?REFILLING:nested);
-            _callback=callback;
-        }
-
-        @Override
-        void onEnter(final AbstractConnection connection)
-        {
-            Callback callback=new Callback()
-            {
-                @Override
-                public void succeeded()
-                {
-                    while(true)
-                    {
-                        State state = connection._state.get();
-                        if (!(state instanceof NestedState))
-                            break;
-                        State nested=((NestedState)state)._nested;
-                        if (connection.next(state,nested))
-                            break;
-                    }
-                    _callback.succeeded();
-                }
-
-                @Override
-                public void failed(Throwable x)
-                {
-                    while(true)
-                    {
-                        State state = connection._state.get();
-                        if (!(state instanceof NestedState))
-                            break;
-                        State nested=((NestedState)state)._nested;
-                        if (connection.next(state,nested))
-                            break;
-                    }
-                    _callback.failed(x);
-                }
-            };
-
-            connection.getEndPoint().fillInterested(callback);
-        }
-    }
-
-    private final Runnable _runOnFillable = new Runnable()
-    {
-        @Override
-        public void run()
-        {
-            try
-            {
-                onFillable();
-            }
-            finally
-            {
-                while(true)
-                {
-                    State state=_state.get();
-                    if (next(state,state.onFilled()))
-                        break;
-                }
-            }
-        }
-    };
-
-
     private class ReadCallback implements Callback
     {
         @Override
         public void succeeded()
         {
-            while(true)
-            {
-                State state=_state.get();
-                if (next(state,state.onFillable()))
-                    break;
-            }
+            onFillable();
         }
 
         @Override
         public void failed(final Throwable x)
         {
-            _executor.execute(new Runnable()
-            {
-                @Override
-                public void run()
-                {
-                    while(true)
-                    {
-                        State state=_state.get();
-                        if (next(state,state.onFailed()))
-                            break;
-                    }
-                    onFillInterestedFailed(x);
-                }
-            });
+            onFillInterestedFailed(x);
         }
 
         @Override
@@ -593,5 +294,5 @@
         {
             return String.format("AC.ReadCB@%x{%s}", AbstractConnection.this.hashCode(),AbstractConnection.this);
         }
-    };
+    }
 }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
index af4d1e3..4b3a5d0 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
@@ -40,16 +40,16 @@
     private final FillInterest _fillInterest = new FillInterest()
     {
         @Override
-        protected boolean needsFill() throws IOException
+        protected void needsFillInterest() throws IOException
         {
-            return AbstractEndPoint.this.needsFill();
+            AbstractEndPoint.this.needsFillInterest();
         }
     };
 
     private final WriteFlusher _writeFlusher = new WriteFlusher(this)
     {
         @Override
-        protected void onIncompleteFlushed()
+        protected void onIncompleteFlush()
         {
             AbstractEndPoint.this.onIncompleteFlush();
         }
@@ -93,6 +93,12 @@
     }
 
     @Override
+    public boolean isOptimizedForDirectBuffers()
+    {
+        return false;
+    }
+
+    @Override
     public void onOpen()
     {
         if (LOG.isDebugEnabled())
@@ -101,29 +107,41 @@
     }
 
     @Override
-    public void onClose()
+    public void close()
     {
-        super.onClose();
-        if (LOG.isDebugEnabled())
-            LOG.debug("onClose {}",this);
+        onClose();
         _writeFlusher.onClose();
         _fillInterest.onClose();
     }
 
-    @Override
-    public void close()
+    protected void close(Throwable failure)
     {
         onClose();
+        _writeFlusher.onFail(failure);
+        _fillInterest.onFail(failure);
     }
 
     @Override
-    public void fillInterested(Callback callback) throws IllegalStateException
+    public void fillInterested(Callback callback)
     {
         notIdle();
         _fillInterest.register(callback);
     }
 
     @Override
+    public boolean tryFillInterested(Callback callback)
+    {
+        notIdle();
+        return _fillInterest.tryRegister(callback);
+    }
+
+    @Override
+    public boolean isFillInterested()
+    {
+        return _fillInterest.isInterested();
+    }
+
+    @Override
     public void write(Callback callback, ByteBuffer... buffers) throws IllegalStateException
     {
         _writeFlusher.write(callback, buffers);
@@ -131,9 +149,9 @@
 
     protected abstract void onIncompleteFlush();
 
-    protected abstract boolean needsFill() throws IOException;
+    protected abstract void needsFillInterest() throws IOException;
 
-    protected FillInterest getFillInterest()
+    public FillInterest getFillInterest()
     {
         return _fillInterest;
     }
@@ -146,6 +164,10 @@
     @Override
     protected void onIdleExpired(TimeoutException timeout)
     {
+        Connection connection = _connection;
+        if (connection != null && !connection.onIdleExpired())
+            return;
+
         boolean output_shutdown=isOutputShutdown();
         boolean input_shutdown=isInputShutdown();
         boolean fillFailed = _fillInterest.onFail(timeout);
@@ -188,18 +210,28 @@
     @Override
     public String toString()
     {
-        return String.format("%s@%x{%s<->%d,%s,%s,%s,%s,%s,%d/%d,%s}",
-                getClass().getSimpleName(),
+        Class<?> c=getClass();
+        String name=c.getSimpleName();
+        while (name.length()==0 && c.getSuperclass()!=null)
+        {
+            c=c.getSuperclass();
+            name=c.getSimpleName();
+        }
+
+        Connection connection = getConnection();
+        return String.format("%s@%x{%s<->%d,%s,%s,%s,%s,%s,%d/%d,%s@%x}",
+                name,
                 hashCode(),
                 getRemoteAddress(),
                 getLocalAddress().getPort(),
                 isOpen()?"Open":"CLOSED",
                 isInputShutdown()?"ISHUT":"in",
                 isOutputShutdown()?"OSHUT":"out",
-                _fillInterest.isInterested()?"R":"-",
-                _writeFlusher.isInProgress()?"W":"-",
+                _fillInterest.toStateString(),
+                _writeFlusher.toStateString(),
                 getIdleFor(),
                 getIdleTimeout(),
-                getConnection()==null?null:getConnection().getClass().getSimpleName());
+                connection == null ? null : connection.getClass().getSimpleName(),
+                connection == null ? 0 : connection.hashCode());
     }
 }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java
index 7b6e241..f56d0d9 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java
@@ -19,25 +19,33 @@
 package org.eclipse.jetty.io;
 
 import java.nio.ByteBuffer;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-import org.eclipse.jetty.util.BufferUtil;
 
 public class ArrayByteBufferPool implements ByteBufferPool
 {
     private final int _min;
-    private final Bucket[] _direct;
-    private final Bucket[] _indirect;
+    private final int _maxQueue;
+    private final ByteBufferPool.Bucket[] _direct;
+    private final ByteBufferPool.Bucket[] _indirect;
     private final int _inc;
 
     public ArrayByteBufferPool()
     {
-        this(0,1024,64*1024);
+        this(-1,-1,-1,-1);
     }
 
     public ArrayByteBufferPool(int minSize, int increment, int maxSize)
     {
+        this(minSize,increment,maxSize,-1);
+    }
+    
+    public ArrayByteBufferPool(int minSize, int increment, int maxSize, int maxQueue)
+    {
+        if (minSize<=0)
+            minSize=0;
+        if (increment<=0)
+            increment=1024;
+        if (maxSize<=0)
+            maxSize=64*1024;
         if (minSize>=increment)
             throw new IllegalArgumentException("minSize >= increment");
         if ((maxSize%increment)!=0 || increment>=maxSize)
@@ -45,31 +53,28 @@
         _min=minSize;
         _inc=increment;
 
-        _direct=new Bucket[maxSize/increment];
-        _indirect=new Bucket[maxSize/increment];
+        _direct=new ByteBufferPool.Bucket[maxSize/increment];
+        _indirect=new ByteBufferPool.Bucket[maxSize/increment];
+        _maxQueue=maxQueue;
 
         int size=0;
         for (int i=0;i<_direct.length;i++)
         {
             size+=_inc;
-            _direct[i]=new Bucket(size);
-            _indirect[i]=new Bucket(size);
+            _direct[i]=new ByteBufferPool.Bucket(this,size,_maxQueue);
+            _indirect[i]=new ByteBufferPool.Bucket(this,size,_maxQueue);
         }
     }
 
     @Override
     public ByteBuffer acquire(int size, boolean direct)
     {
-        Bucket bucket = bucketFor(size,direct);
-        ByteBuffer buffer = bucket==null?null:bucket._queue.poll();
-
-        if (buffer == null)
-        {
-            int capacity = bucket==null?size:bucket._size;
-            buffer = direct ? BufferUtil.allocateDirect(capacity) : BufferUtil.allocate(capacity);
-        }
-
-        return buffer;
+        ByteBufferPool.Bucket bucket = bucketFor(size,direct);
+        if (bucket==null)
+            return newByteBuffer(size,direct);
+            
+        return bucket.acquire(direct);
+            
     }
 
     @Override
@@ -77,12 +82,9 @@
     {
         if (buffer!=null)
         {    
-            Bucket bucket = bucketFor(buffer.capacity(),buffer.isDirect());
+            ByteBufferPool.Bucket bucket = bucketFor(buffer.capacity(),buffer.isDirect());
             if (bucket!=null)
-            {
-                BufferUtil.clear(buffer);
-                bucket._queue.offer(buffer);
-            }
+                bucket.release(buffer);
         }
     }
 
@@ -90,43 +92,25 @@
     {
         for (int i=0;i<_direct.length;i++)
         {
-            _direct[i]._queue.clear();
-            _indirect[i]._queue.clear();
+            _direct[i].clear();
+            _indirect[i].clear();
         }
     }
 
-    private Bucket bucketFor(int size,boolean direct)
+    private ByteBufferPool.Bucket bucketFor(int size,boolean direct)
     {
         if (size<=_min)
             return null;
         int b=(size-1)/_inc;
         if (b>=_direct.length)
             return null;
-        Bucket bucket = direct?_direct[b]:_indirect[b];
+        ByteBufferPool.Bucket bucket = direct?_direct[b]:_indirect[b];
                 
         return bucket;
     }
 
-    public static class Bucket
-    {
-        public final int _size;
-        public final Queue<ByteBuffer> _queue= new ConcurrentLinkedQueue<>();
-
-        Bucket(int size)
-        {
-            _size=size;
-        }
-        
-        @Override
-        public String toString()
-        {
-            return String.format("Bucket@%x{%d,%d}",hashCode(),_size,_queue.size());
-        }
-    }
-    
-
     // Package local for testing
-    Bucket[] bucketsFor(boolean direct)
+    ByteBufferPool.Bucket[] bucketsFor(boolean direct)
     {
         return direct ? _direct : _indirect;
     }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java
index 50b4b10..54021e4 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java
@@ -18,16 +18,22 @@
 
 package org.eclipse.jetty.io;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.channels.ClosedChannelException;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayDeque;
+import java.util.Queue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
 
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Locker;
 import org.eclipse.jetty.util.thread.Scheduler;
 
 
@@ -39,14 +45,25 @@
 {
     static final Logger LOG = Log.getLogger(ByteArrayEndPoint.class);
     public final static InetSocketAddress NOIP=new InetSocketAddress(0);
+    private static final ByteBuffer EOF = BufferUtil.allocate(0);
 
-    protected volatile ByteBuffer _in;
-    protected volatile ByteBuffer _out;
-    protected volatile boolean _ishut;
-    protected volatile boolean _oshut;
-    protected volatile boolean _closed;
-    protected volatile boolean _growOutput;
+    private final Runnable _runFillable = new Runnable()
+    {
+        @Override
+        public void run()
+        {
+            getFillInterest().fillable();
+        }
+    };
 
+    private final Locker _locker = new Locker();
+    private final Condition _hasOutput = _locker.newCondition();
+    private final Queue<ByteBuffer> _inQ = new ArrayDeque<>();
+    private ByteBuffer _out;
+    private boolean _ishut;
+    private boolean _oshut;
+    private boolean _closed;
+    private boolean _growOutput;
 
     /* ------------------------------------------------------------ */
     /**
@@ -59,7 +76,8 @@
 
     /* ------------------------------------------------------------ */
     /**
-     *
+     * @param input the input bytes
+     * @param outputSize the output size
      */
     public ByteArrayEndPoint(byte[] input, int outputSize)
     {
@@ -68,7 +86,8 @@
 
     /* ------------------------------------------------------------ */
     /**
-     *
+     * @param input the input string (converted to bytes using default encoding charset)
+     * @param outputSize the output size
      */
     public ByteArrayEndPoint(String input, int outputSize)
     {
@@ -97,15 +116,12 @@
     public ByteArrayEndPoint(Scheduler timer, long idleTimeoutMs, ByteBuffer input, ByteBuffer output)
     {
         super(timer,NOIP,NOIP);
-        _in=input==null?BufferUtil.EMPTY_BUFFER:input;
+        if (BufferUtil.hasContent(input))
+            addInput(input);
         _out=output==null?BufferUtil.allocate(1024):output;
         setIdleTimeout(idleTimeoutMs);
     }
 
-
-
-
-
     /* ------------------------------------------------------------ */
     @Override
     protected void onIncompleteFlush()
@@ -114,52 +130,95 @@
     }
 
     /* ------------------------------------------------------------ */
+    protected void execute(Runnable task)
+    {
+        new Thread(task,"BAEPoint-"+Integer.toHexString(hashCode())).start();
+    }
+
+    /* ------------------------------------------------------------ */
     @Override
-    protected boolean needsFill() throws IOException
+    protected void needsFillInterest() throws IOException
     {
-        if (_closed)
-            throw new ClosedChannelException();
-        return _in == null || BufferUtil.hasContent(_in);
-    }
+        try(Locker.Lock lock = _locker.lock())
+        {
+            if (_closed)
+                throw new ClosedChannelException();
 
-    /* ------------------------------------------------------------ */
-    /**
-     * @return Returns the in.
-     */
-    public ByteBuffer getIn()
-    {
-        return _in;
+            ByteBuffer in = _inQ.peek();
+            if (BufferUtil.hasContent(in) || in==EOF)
+                execute(_runFillable);
+        }
     }
 
     /* ------------------------------------------------------------ */
     /**
      */
-    public void setInputEOF()
+    public void addInputEOF()
     {
-        _in = null;
+        addInput((ByteBuffer)null);
     }
 
     /* ------------------------------------------------------------ */
     /**
      * @param in The in to set.
      */
-    public void setInput(ByteBuffer in)
+    public void addInput(ByteBuffer in)
     {
-        _in = in;
-        if (in == null || BufferUtil.hasContent(in))
-            getFillInterest().fillable();
+        boolean fillable=false;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            if (_inQ.peek()==EOF)
+                throw new RuntimeIOException(new EOFException());
+            boolean was_empty=_inQ.isEmpty();
+            if (in==null)
+            {
+                _inQ.add(EOF);
+                fillable=true;
+            }
+            if (BufferUtil.hasContent(in))
+            {
+                _inQ.add(in);
+                fillable=was_empty;
+            }
+        }
+        if (fillable)
+            _runFillable.run();
     }
 
     /* ------------------------------------------------------------ */
-    public void setInput(String s)
+    public void addInputAndExecute(ByteBuffer in)
     {
-        setInput(BufferUtil.toBuffer(s,StandardCharsets.UTF_8));
+        boolean fillable=false;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            if (_inQ.peek()==EOF)
+                throw new RuntimeIOException(new EOFException());
+            boolean was_empty=_inQ.isEmpty();
+            if (in==null)
+            {
+                _inQ.add(EOF);
+                fillable=true;
+            }
+            if (BufferUtil.hasContent(in))
+            {
+                _inQ.add(in);
+                fillable=was_empty;
+            }
+        }
+        if (fillable)
+            execute(_runFillable);
     }
 
     /* ------------------------------------------------------------ */
-    public void setInput(String s,Charset charset)
+    public void addInput(String s)
     {
-        setInput(BufferUtil.toBuffer(s,charset));
+        addInput(BufferUtil.toBuffer(s,StandardCharsets.UTF_8));
+    }
+
+    /* ------------------------------------------------------------ */
+    public void addInput(String s,Charset charset)
+    {
+        addInput(BufferUtil.toBuffer(s,charset));
     }
 
     /* ------------------------------------------------------------ */
@@ -168,7 +227,10 @@
      */
     public ByteBuffer getOutput()
     {
-        return _out;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            return _out;
+        }
     }
 
     /* ------------------------------------------------------------ */
@@ -182,6 +244,7 @@
 
     /* ------------------------------------------------------------ */
     /**
+     * @param charset the charset to encode the output as
      * @return Returns the out.
      */
     public String getOutputString(Charset charset)
@@ -195,8 +258,37 @@
      */
     public ByteBuffer takeOutput()
     {
-        ByteBuffer b=_out;
-        _out=BufferUtil.allocate(b.capacity());
+        ByteBuffer b;
+
+        try(Locker.Lock lock = _locker.lock())
+        {
+            b=_out;
+            _out=BufferUtil.allocate(b.capacity());
+        }
+        getWriteFlusher().completeWrite();
+        return b;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Wait for some output
+     * @param time Time to wait
+     * @param unit Units for time to wait
+     * @return The buffer of output
+     * @throws InterruptedException
+     */
+    public ByteBuffer waitForOutput(long time,TimeUnit unit) throws InterruptedException
+    {
+        ByteBuffer b;
+
+        try(Locker.Lock lock = _locker.lock())
+        {
+            while (BufferUtil.isEmpty(_out) && !_closed && !_oshut)
+            {
+                _hasOutput.await(time,unit);
+            }
+            b=_out;
+            _out=BufferUtil.allocate(b.capacity());
+        }
         getWriteFlusher().completeWrite();
         return b;
     }
@@ -212,6 +304,7 @@
 
     /* ------------------------------------------------------------ */
     /**
+     * @param charset the charset to encode the output as
      * @return Returns the out.
      */
     public String takeOutputString(Charset charset)
@@ -226,7 +319,10 @@
      */
     public void setOutput(ByteBuffer out)
     {
-        _out = out;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            _out = out;
+        }
         getWriteFlusher().completeWrite();
     }
 
@@ -237,7 +333,10 @@
     @Override
     public boolean isOpen()
     {
-        return !_closed;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            return !_closed;
+        }
     }
 
     /* ------------------------------------------------------------ */
@@ -246,7 +345,10 @@
     @Override
     public boolean isInputShutdown()
     {
-        return _ishut||_closed;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            return _ishut||_closed;
+        }
     }
 
     /* ------------------------------------------------------------ */
@@ -255,15 +357,24 @@
     @Override
     public boolean isOutputShutdown()
     {
-        return _oshut||_closed;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            return _oshut||_closed;
+        }
     }
 
     /* ------------------------------------------------------------ */
-    private void shutdownInput()
+    public void shutdownInput()
     {
-        _ishut=true;
-        if (_oshut)
-            close();
+        boolean close=false;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            _ishut=true;
+            if (_oshut && !_closed)
+                close=_closed=true;
+        }
+        if (close)
+            super.close();
     }
 
     /* ------------------------------------------------------------ */
@@ -273,9 +384,16 @@
     @Override
     public void shutdownOutput()
     {
-        _oshut=true;
-        if (_ishut)
-            close();
+        boolean close=false;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            _oshut=true;
+            _hasOutput.signalAll();
+            if (_ishut && !_closed)
+                close=_closed=true;
+        }
+        if (close)
+            super.close();
     }
 
     /* ------------------------------------------------------------ */
@@ -285,8 +403,16 @@
     @Override
     public void close()
     {
-        super.close();
-        _closed=true;
+        boolean close=false;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            if (!_closed)
+                close=_closed=_ishut=_oshut=true;
+
+            _hasOutput.signalAll();
+        }
+        if (close)
+            super.close();
     }
 
     /* ------------------------------------------------------------ */
@@ -305,13 +431,44 @@
     @Override
     public int fill(ByteBuffer buffer) throws IOException
     {
-        if (_closed)
-            throw new EofException("CLOSED");
-        if (_in==null)
-            shutdownInput();
-        if (_ishut)
-            return -1;
-        int filled=BufferUtil.append(buffer,_in);
+        int filled=0;
+        boolean close=false;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            while(true)
+            {
+                if (_closed)
+                    throw new EofException("CLOSED");
+
+                if (_ishut)
+                    return -1;
+
+                if (_inQ.isEmpty())
+                    break;
+
+                ByteBuffer in= _inQ.peek();
+                if (in==EOF)
+                {
+                    _ishut=true;
+                    if (_oshut)
+                        close=_closed=true;
+                    filled=-1;
+                    break;
+                }
+
+                if (BufferUtil.hasContent(in))
+                {
+                    filled=BufferUtil.append(buffer,in);
+                    if (BufferUtil.isEmpty(in))
+                        _inQ.poll();
+                    break;
+                }
+                _inQ.poll();
+            }
+        }
+
+        if (close)
+            super.close();
         if (filled>0)
             notIdle();
         return filled;
@@ -324,41 +481,47 @@
     @Override
     public boolean flush(ByteBuffer... buffers) throws IOException
     {
-        if (_closed)
-            throw new IOException("CLOSED");
-        if (_oshut)
-            throw new IOException("OSHUT");
-
         boolean flushed=true;
-        boolean idle=true;
-
-        for (ByteBuffer b : buffers)
+        try(Locker.Lock lock = _locker.lock())
         {
-            if (BufferUtil.hasContent(b))
+            if (_closed)
+                throw new IOException("CLOSED");
+            if (_oshut)
+                throw new IOException("OSHUT");
+
+            boolean idle=true;
+
+            for (ByteBuffer b : buffers)
             {
-                if (_growOutput && b.remaining()>BufferUtil.space(_out))
-                {
-                    BufferUtil.compact(_out);
-                    if (b.remaining()>BufferUtil.space(_out))
-                    {
-                        ByteBuffer n = BufferUtil.allocate(_out.capacity()+b.remaining()*2);
-                        BufferUtil.append(n,_out);
-                        _out=n;
-                    }
-                }
-
-                if (BufferUtil.append(_out,b)>0)
-                    idle=false;
-
                 if (BufferUtil.hasContent(b))
                 {
-                    flushed=false;
-                    break;
+                    if (_growOutput && b.remaining()>BufferUtil.space(_out))
+                    {
+                        BufferUtil.compact(_out);
+                        if (b.remaining()>BufferUtil.space(_out))
+                        {
+                            ByteBuffer n = BufferUtil.allocate(_out.capacity()+b.remaining()*2);
+                            BufferUtil.append(n,_out);
+                            _out=n;
+                        }
+                    }
+
+                    if (BufferUtil.append(_out,b)>0)
+                        idle=false;
+
+                    if (BufferUtil.hasContent(b))
+                    {
+                        flushed=false;
+                        break;
+                    }
                 }
             }
+            if (!idle)
+            {
+                notIdle();
+                _hasOutput.signalAll();
+            }
         }
-        if (!idle)
-            notIdle();
         return flushed;
     }
 
@@ -368,13 +531,16 @@
      */
     public void reset()
     {
-        getFillInterest().onClose();
-        getWriteFlusher().onClose();
-        _ishut=false;
-        _oshut=false;
-        _closed=false;
-        _in=null;
-        BufferUtil.clear(_out);
+        try(Locker.Lock lock = _locker.lock())
+        {
+            getFillInterest().onClose();
+            getWriteFlusher().onClose();
+            _ishut=false;
+            _oshut=false;
+            _closed=false;
+            _inQ.clear();
+            BufferUtil.clear(_out);
+        }
     }
 
     /* ------------------------------------------------------------ */
@@ -405,5 +571,20 @@
         _growOutput=growOutput;
     }
 
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        int q;
+        ByteBuffer b;
+        String o;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            q=_inQ.size();
+            b=_inQ.peek();
+            o=BufferUtil.toDetailString(_out);
+        }
+        return String.format("%s[q=%d,q[0]=%s,o=%s]",super.toString(),q,b,o);
+    }
 
 }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java
index e588514..8f12ee3 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java
@@ -19,6 +19,13 @@
 package org.eclipse.jetty.io;
 
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jetty.util.BufferUtil;
 
 /**
  * <p>A {@link ByteBuffer} pool.</p>
@@ -48,4 +55,156 @@
      * @see #acquire(int, boolean)
      */
     public void release(ByteBuffer buffer);
+
+    default ByteBuffer newByteBuffer(int capacity, boolean direct)
+    {
+        return direct ? BufferUtil.allocateDirect(capacity) : BufferUtil.allocate(capacity);
+    }
+
+    public static class Lease
+    {
+        private final ByteBufferPool byteBufferPool;
+        private final List<ByteBuffer> buffers;
+        private final List<Boolean> recycles;
+
+        public Lease(ByteBufferPool byteBufferPool)
+        {
+            this.byteBufferPool = byteBufferPool;
+            this.buffers = new ArrayList<>();
+            this.recycles = new ArrayList<>();
+        }
+
+        public ByteBuffer acquire(int capacity, boolean direct)
+        {
+            ByteBuffer buffer = byteBufferPool.acquire(capacity, direct);
+            BufferUtil.clearToFill(buffer);
+            return buffer;
+        }
+
+        public void append(ByteBuffer buffer, boolean recycle)
+        {
+            buffers.add(buffer);
+            recycles.add(recycle);
+        }
+
+        public void insert(int index, ByteBuffer buffer, boolean recycle)
+        {
+            buffers.add(index, buffer);
+            recycles.add(index, recycle);
+        }
+
+        public List<ByteBuffer> getByteBuffers()
+        {
+            return buffers;
+        }
+
+        public long getTotalLength()
+        {
+            long length = 0;
+            for (int i = 0; i < buffers.size(); ++i)
+                length += buffers.get(i).remaining();
+            return length;
+        }
+
+        public int getSize()
+        {
+            return buffers.size();
+        }
+
+        public void recycle()
+        {
+            for (int i = 0; i < buffers.size(); ++i)
+            {
+                ByteBuffer buffer = buffers.get(i);
+                if (recycles.get(i))
+                    byteBufferPool.release(buffer);
+            }
+            buffers.clear();
+            recycles.clear();
+        }
+    }
+
+    class Bucket
+    {
+        private final Deque<ByteBuffer> _queue = new ConcurrentLinkedDeque<>();
+        private final ByteBufferPool _pool;
+        private final int _capacity;
+        private final AtomicInteger _space;
+
+        public Bucket(ByteBufferPool pool, int bufferSize, int maxSize)
+        {
+            _pool = pool;
+            _capacity = bufferSize;
+            _space = maxSize > 0 ? new AtomicInteger(maxSize) : null;
+        }
+
+        public ByteBuffer acquire(boolean direct)
+        {
+            ByteBuffer buffer = queuePoll();
+            if (buffer == null)
+                return _pool.newByteBuffer(_capacity, direct);
+            if (_space != null)
+                _space.incrementAndGet();
+            return buffer;
+        }
+
+        public void release(ByteBuffer buffer)
+        {
+            BufferUtil.clear(buffer);
+            if (_space == null)
+                queueOffer(buffer);
+            else if (_space.decrementAndGet() >= 0)
+                queueOffer(buffer);
+            else
+                _space.incrementAndGet();
+        }
+
+        public void clear()
+        {
+            if (_space == null)
+            {
+                queueClear();
+            }
+            else
+            {
+                int s = _space.getAndSet(0);
+                while (s-- > 0)
+                {
+                    if (queuePoll() == null)
+                        _space.incrementAndGet();
+                }
+            }
+        }
+
+        private void queueOffer(ByteBuffer buffer)
+        {
+            _queue.offerFirst(buffer);
+        }
+
+        private ByteBuffer queuePoll()
+        {
+            return _queue.poll();
+        }
+
+        private void queueClear()
+        {
+            _queue.clear();
+        }
+
+        boolean isEmpty()
+        {
+            return _queue.isEmpty();
+        }
+
+        int size()
+        {
+            return _queue.size();
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("Bucket@%x{%d/%d}", hashCode(), size(), _capacity);
+        }
+    }
 }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java
index 8e277cc..5f686a9 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java
@@ -23,8 +23,9 @@
 import java.net.Socket;
 import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
-import java.nio.channels.GatheringByteChannel;
 import java.nio.channels.SocketChannel;
+import java.util.Arrays;
+import java.util.stream.Collectors;
 
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.log.Log;
@@ -39,7 +40,7 @@
 {
     private static final Logger LOG = Log.getLogger(ChannelEndPoint.class);
 
-    private final ByteChannel _channel;
+    private final SocketChannel _channel;
     private final Socket _socket;
     private volatile boolean _ishut;
     private volatile boolean _oshut;
@@ -49,11 +50,17 @@
         super(scheduler,
             (InetSocketAddress)channel.socket().getLocalSocketAddress(),
             (InetSocketAddress)channel.socket().getRemoteSocketAddress());
-        _channel = channel;
+        _channel=channel;
         _socket=channel.socket();
     }
 
     @Override
+    public boolean isOptimizedForDirectBuffers()
+    {
+        return true;
+    }
+
+    @Override
     public boolean isOpen()
     {
         return _channel.isOpen();
@@ -163,13 +170,13 @@
     @Override
     public boolean flush(ByteBuffer... buffers) throws IOException
     {
-        int flushed=0;
+        long flushed=0;
         try
         {
             if (buffers.length==1)
                 flushed=_channel.write(buffers[0]);
-            else if (buffers.length>1 && _channel instanceof GatheringByteChannel)
-                flushed= (int)((GatheringByteChannel)_channel).write(buffers,0,buffers.length);
+            else if (buffers.length>1)
+                flushed=_channel.write(buffers,0,buffers.length);
             else
             {
                 for (ByteBuffer b : buffers)
@@ -225,7 +232,7 @@
     }
 
     @Override
-    protected boolean needsFill() throws IOException
+    protected void needsFillInterest() throws IOException
     {
         throw new UnsupportedOperationException();
     }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java
index 69612b3..f76cb8a 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java
@@ -19,17 +19,17 @@
 package org.eclipse.jetty.io;
 
 import java.io.IOException;
-import java.nio.ByteBuffer;
 import java.util.Map;
 
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
 
 /**
  * Factory for client-side {@link Connection} instances.
  */
 public interface ClientConnectionFactory
 {
+    public static final String CONNECTOR_CONTEXT_KEY = "client.connector";
+
     /**
      *
      * @param endPoint the {@link org.eclipse.jetty.io.EndPoint} to link the newly created connection to
@@ -39,52 +39,10 @@
      */
     public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException;
 
-    public static class Helper
+    public default Connection customize(Connection connection, Map<String, Object> context)
     {
-        private static Logger LOG = Log.getLogger(Helper.class);
-
-        private Helper()
-        {
-        }
-
-        /**
-         * Replaces the given {@code oldConnection} with the given {@code newConnection} on the
-         * {@link EndPoint} associated with {@code oldConnection}, performing connection lifecycle management.
-         * <p />
-         * The {@code oldConnection} will be closed by invoking {@link org.eclipse.jetty.io.Connection#onClose()}
-         * and the {@code newConnection} will be opened by invoking {@link org.eclipse.jetty.io.Connection#onOpen(ByteBuffer)}.
-         * @param oldConnection the old connection to replace
-         * @param newConnection the new connection replacement
-         */
-        public static void replaceConnection(Connection oldConnection, Connection newConnection)
-        {
-            close(oldConnection);
-            oldConnection.getEndPoint().setConnection(newConnection);
-            open(newConnection);
-        }
-
-        private static void open(Connection connection)
-        {
-            try
-            {
-                connection.onOpen();
-            }
-            catch (Throwable x)
-            {
-                LOG.debug(x);
-            }
-        }
-
-        private static void close(Connection connection)
-        {
-            try
-            {
-                connection.onClose();
-            }
-            catch (Throwable x)
-            {
-                LOG.debug(x);
-            }
-        }
+        ContainerLifeCycle connector = (ContainerLifeCycle)context.get(CONNECTOR_CONTEXT_KEY);
+        connector.getBeans(Connection.Listener.class).forEach(connection::addListener);
+        return connection;
     }
 }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java
index 05f1ad9..e7c4a06 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java
@@ -21,22 +21,40 @@
 import java.io.Closeable;
 import java.nio.ByteBuffer;
 
+import org.eclipse.jetty.util.component.Container;
+
 /**
  * <p>A {@link Connection} is associated to an {@link EndPoint} so that I/O events
  * happening on the {@link EndPoint} can be processed by the {@link Connection}.</p>
- * <p>A typical implementation of {@link Connection} overrides {@link #onOpen(ByteBuffer)} to
- * {@link EndPoint#fillInterested(Callback) set read interest} on the {@link EndPoint},
+ * <p>A typical implementation of {@link Connection} overrides {@link #onOpen()} to
+ * {@link EndPoint#fillInterested(org.eclipse.jetty.util.Callback) set read interest} on the {@link EndPoint},
  * and when the {@link EndPoint} signals read readyness, this {@link Connection} can
  * read bytes from the network and interpret them.</p>
  */
 public interface Connection extends Closeable
 {
+    /**
+     * <p>Adds a listener of connection events.</p>
+     *
+     * @param listener the listener to add
+     */
     public void addListener(Listener listener);
 
+    /**
+     * <p>Removes a listener of connection events.</p>
+     *
+     * @param listener the listener to remove
+     */
+    public void removeListener(Listener listener);
+
+    /**
+     * <p>Callback method invoked when this connection is opened.</p>
+     * <p>Creators of the connection implementation are responsible for calling this method.</p>
+     */
     public void onOpen();
 
     /**
-     * <p>Callback method invoked when this {@link Connection} is closed.</p>
+     * <p>Callback method invoked when this connection is closed.</p>
      * <p>Creators of the connection implementation are responsible for calling this method.</p>
      */
     public void onClose();
@@ -45,7 +63,7 @@
      * @return the {@link EndPoint} associated with this {@link Connection}
      */
     public EndPoint getEndPoint();
-    
+
     /**
      * <p>Performs a logical close of this connection.</p>
      * <p>For simple connections, this may just mean to delegate the close to the associated
@@ -55,37 +73,59 @@
     @Override
     public void close();
 
+    /**
+     * <p>Callback method invoked upon an idle timeout event.</p>
+     * <p>Implementations of this method may return true to indicate that the idle timeout
+     * handling should proceed normally, typically failing the EndPoint and causing it to
+     * be closed.</p>
+     * <p>When false is returned, the handling of the idle timeout event is halted
+     * immediately and the EndPoint left in the state it was before the idle timeout event.</p>
+     *
+     * @return true to let the EndPoint handle the idle timeout,
+     *         false to tell the EndPoint to halt the handling of the idle timeout.
+     */
+    public boolean onIdleExpired();
+
     public int getMessagesIn();
     public int getMessagesOut();
     public long getBytesIn();
     public long getBytesOut();
     public long getCreatedTimeStamp();
-    
+
     public interface UpgradeFrom extends Connection
     {
-        /* ------------------------------------------------------------ */
-        /** Take the input buffer from the connection on upgrade.
+        /**
+         * <p>Takes the input buffer from the connection on upgrade.</p>
          * <p>This method is used to take any unconsumed input from
-         * a connection during an upgrade.
+         * a connection during an upgrade.</p>
+         *
          * @return A buffer of unconsumed input. The caller must return the buffer
          * to the bufferpool when consumed and this connection must not.
          */
         ByteBuffer onUpgradeFrom();
     }
-    
+
     public interface UpgradeTo extends Connection
     {
         /**
-         * <p>Callback method invoked when this {@link Connection} is upgraded.</p>
+         * <p>Callback method invoked when this connection is upgraded.</p>
          * <p>This must be called before {@link #onOpen()}.</p>
-         * @param prefilledBuffer An optional buffer that can contain prefilled data. Typically this
+         * @param prefilled An optional buffer that can contain prefilled data. Typically this
          * results from an upgrade of one protocol to the other where the old connection has buffered
          * data destined for the new connection.  The new connection must take ownership of the buffer
          * and is responsible for returning it to the buffer pool
          */
         void onUpgradeTo(ByteBuffer prefilled);
     }
-    
+
+    /**
+     * <p>A Listener for connection events.</p>
+     * <p>Listeners can be added to a {@link Connection} to get open and close events.
+     * The AbstractConnectionFactory implements a pattern where objects implement
+     * this interface that have been added via {@link Container#addBean(Object)} to
+     * the Connector or ConnectionFactory are added as listeners to all new connections
+     * </p>
+     */
     public interface Listener
     {
         public void onOpened(Connection connection);
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ConnectionStatistics.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ConnectionStatistics.java
new file mode 100644
index 0000000..0db5704
--- /dev/null
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ConnectionStatistics.java
@@ -0,0 +1,233 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.io;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.LongAdder;
+
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.annotation.ManagedOperation;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+import org.eclipse.jetty.util.statistic.CounterStatistic;
+import org.eclipse.jetty.util.statistic.SampleStatistic;
+
+/**
+ * <p>A {@link Connection.Listener} that tracks connection statistics.</p>
+ * <p>Adding an instance of this class as a bean to a server Connector
+ * (for the server) or to HttpClient (for the client) will trigger the
+ * tracking of the connection statistics for all connections managed
+ * by the server Connector or by HttpClient.</p>
+ */
+@ManagedObject("Tracks statistics on connections")
+public class ConnectionStatistics extends AbstractLifeCycle implements Connection.Listener, Dumpable
+{
+    private final CounterStatistic _connections = new CounterStatistic();
+    private final SampleStatistic _connectionsDuration = new SampleStatistic();
+    private final LongAdder _rcvdBytes = new LongAdder();
+    private final AtomicLong _bytesInStamp = new AtomicLong();
+    private final LongAdder _sentBytes = new LongAdder();
+    private final AtomicLong _bytesOutStamp = new AtomicLong();
+    private final LongAdder _messagesIn = new LongAdder();
+    private final AtomicLong _messagesInStamp = new AtomicLong();
+    private final LongAdder _messagesOut = new LongAdder();
+    private final AtomicLong _messagesOutStamp = new AtomicLong();
+
+    @ManagedOperation(value = "Resets the statistics", impact = "ACTION")
+    public void reset()
+    {
+        _connections.reset();
+        _connectionsDuration.reset();
+        _rcvdBytes.reset();
+        _bytesInStamp.set(System.nanoTime());
+        _sentBytes.reset();
+        _bytesOutStamp.set(System.nanoTime());
+        _messagesIn.reset();
+        _messagesInStamp.set(System.nanoTime());
+        _messagesOut.reset();
+        _messagesOutStamp.set(System.nanoTime());
+    }
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        reset();
+    }
+
+    @Override
+    public void onOpened(Connection connection)
+    {
+        if (!isStarted())
+            return;
+
+        _connections.increment();
+    }
+
+    @Override
+    public void onClosed(Connection connection)
+    {
+        if (!isStarted())
+            return;
+
+        _connections.decrement();
+
+        long elapsed = System.currentTimeMillis() - connection.getCreatedTimeStamp();
+        _connectionsDuration.set(elapsed);
+
+        long bytesIn = connection.getBytesIn();
+        if (bytesIn > 0)
+            _rcvdBytes.add(bytesIn);
+        long bytesOut = connection.getBytesOut();
+        if (bytesOut > 0)
+            _sentBytes.add(bytesOut);
+
+        long messagesIn = connection.getMessagesIn();
+        if (messagesIn > 0)
+            _messagesIn.add(messagesIn);
+        long messagesOut = connection.getMessagesOut();
+        if (messagesOut > 0)
+            _messagesOut.add(messagesOut);
+    }
+
+    @ManagedAttribute("Total number of bytes received by tracked connections")
+    public long getReceivedBytes()
+    {
+        return _rcvdBytes.sum();
+    }
+
+    @ManagedAttribute("Total number of bytes received per second since the last invocation of this method")
+    public long getReceivedBytesRate()
+    {
+        long now = System.nanoTime();
+        long then = _bytesInStamp.getAndSet(now);
+        long elapsed = TimeUnit.NANOSECONDS.toMillis(now - then);
+        return elapsed == 0 ? 0 : getReceivedBytes() * 1000 / elapsed;
+    }
+
+    @ManagedAttribute("Total number of bytes sent by tracked connections")
+    public long getSentBytes()
+    {
+        return _sentBytes.sum();
+    }
+
+    @ManagedAttribute("Total number of bytes sent per second since the last invocation of this method")
+    public long getSentBytesRate()
+    {
+        long now = System.nanoTime();
+        long then = _bytesOutStamp.getAndSet(now);
+        long elapsed = TimeUnit.NANOSECONDS.toMillis(now - then);
+        return elapsed == 0 ? 0 : getSentBytes() * 1000 / elapsed;
+    }
+
+    @ManagedAttribute("The max duration of a connection in ms")
+    public long getConnectionDurationMax()
+    {
+        return _connectionsDuration.getMax();
+    }
+
+    @ManagedAttribute("The mean duration of a connection in ms")
+    public double getConnectionDurationMean()
+    {
+        return _connectionsDuration.getMean();
+    }
+
+    @ManagedAttribute("The standard deviation of the duration of a connection")
+    public double getConnectionDurationStdDev()
+    {
+        return _connectionsDuration.getStdDev();
+    }
+
+    @ManagedAttribute("The total number of connections opened")
+    public long getConnectionsTotal()
+    {
+        return _connections.getTotal();
+    }
+
+    @ManagedAttribute("The current number of open connections")
+    public long getConnections()
+    {
+        return _connections.getCurrent();
+    }
+
+    @ManagedAttribute("The max number of open connections")
+    public long getConnectionsMax()
+    {
+        return _connections.getMax();
+    }
+
+    @ManagedAttribute("The total number of messages received")
+    public long getReceivedMessages()
+    {
+        return _messagesIn.sum();
+    }
+
+    @ManagedAttribute("Total number of messages received per second since the last invocation of this method")
+    public long getReceivedMessagesRate()
+    {
+        long now = System.nanoTime();
+        long then = _messagesInStamp.getAndSet(now);
+        long elapsed = TimeUnit.NANOSECONDS.toMillis(now - then);
+        return elapsed == 0 ? 0 : getReceivedMessages() * 1000 / elapsed;
+    }
+
+    @ManagedAttribute("The total number of messages sent")
+    public long getSentMessages()
+    {
+        return _messagesOut.sum();
+    }
+
+    @ManagedAttribute("Total number of messages sent per second since the last invocation of this method")
+    public long getSentMessagesRate()
+    {
+        long now = System.nanoTime();
+        long then = _messagesOutStamp.getAndSet(now);
+        long elapsed = TimeUnit.NANOSECONDS.toMillis(now - then);
+        return elapsed == 0 ? 0 : getSentMessages() * 1000 / elapsed;
+    }
+
+    @Override
+    public String dump()
+    {
+        return ContainerLifeCycle.dump(this);
+    }
+
+    @Override
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        ContainerLifeCycle.dumpObject(out, this);
+        List<String> children = new ArrayList<>();
+        children.add(String.format("connections=%s", _connections));
+        children.add(String.format("durations=%s", _connectionsDuration));
+        children.add(String.format("bytes in/out=%s/%s", getReceivedBytes(), getSentBytes()));
+        children.add(String.format("messages in/out=%s/%s", getReceivedMessages(), getSentMessages()));
+        ContainerLifeCycle.dump(out, indent, children);
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%x", getClass().getSimpleName(), hashCode());
+    }
+}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java
index fe1d081..4e7c05d 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java
@@ -26,33 +26,35 @@
 import java.nio.channels.WritePendingException;
 
 import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FutureCallback;
+import org.eclipse.jetty.util.IteratingCallback;
 
 /**
  *
  * A transport EndPoint
- * 
+ *
  * <h3>Asynchronous Methods</h3>
- * <p>The asynchronous scheduling methods of {@link EndPoint} 
+ * <p>The asynchronous scheduling methods of {@link EndPoint}
  * has been influenced by NIO.2 Futures and Completion
  * handlers, but does not use those actual interfaces because they have
  * some inefficiencies.</p>
  * <p>This class will frequently be used in conjunction with some of the utility
  * implementations of {@link Callback}, such as {@link FutureCallback} and
- * {@link ExecutorCallback}. Examples are:</p>
+ * {@link IteratingCallback}. Examples are:</p>
  *
  * <h3>Blocking Read</h3>
  * <p>A FutureCallback can be used to block until an endpoint is ready to be filled
- * from:
+ * from:</p>
  * <blockquote><pre>
  * FutureCallback&lt;String&gt; future = new FutureCallback&lt;&gt;();
  * endpoint.fillInterested("ContextObj",future);
  * ...
  * String context = future.get(); // This blocks
  * int filled=endpoint.fill(mybuffer);
- * </pre></blockquote></p>
+ * </pre></blockquote>
  *
  * <h3>Dispatched Read</h3>
- * <p>By using a different callback, the read can be done asynchronously in its own dispatched thread:
+ * <p>By using a different callback, the read can be done asynchronously in its own dispatched thread:</p>
  * <blockquote><pre>
  * endpoint.fillInterested("ContextObj",new ExecutorCallback&lt;String&gt;(executor)
  * {
@@ -63,22 +65,22 @@
  *   }
  *   public void onFailed(String context,Throwable cause) {...}
  * });
- * </pre></blockquote></p>
+ * </pre></blockquote>
  * <p>The executor callback can also be customized to not dispatch in some circumstances when
  * it knows it can use the callback thread and does not need to dispatch.</p>
  *
  * <h3>Blocking Write</h3>
  * <p>The write contract is that the callback complete is not called until all data has been
- * written or there is a failure.  For blocking this looks like:
+ * written or there is a failure.  For blocking this looks like:</p>
  * <blockquote><pre>
  * FutureCallback&lt;String&gt; future = new FutureCallback&lt;&gt;();
  * endpoint.write("ContextObj",future,headerBuffer,contentBuffer);
  * String context = future.get(); // This blocks
- * </pre></blockquote></p>
+ * </pre></blockquote>
  *
  * <h3>Dispatched Write</h3>
  * <p>Note also that multiple buffers may be passed in write so that gather writes
- * can be done:
+ * can be done:</p>
  * <blockquote><pre>
  * endpoint.write("ContextObj",new ExecutorCallback&lt;String&gt;(executor)
  * {
@@ -89,7 +91,7 @@
  *   }
  *   public void onFailed(String context,Throwable cause) {...}
  * },headerBuffer,contentBuffer);
- * </pre></blockquote></p>
+ * </pre></blockquote>
  */
 public interface EndPoint extends Closeable
 {
@@ -158,7 +160,7 @@
      * operation, the position is unchanged and the limit is increased to reflect the new data filled.
      * @return an <code>int</code> value indicating the number of bytes
      * filled or -1 if EOF is read or the input is shutdown.
-     * @throws EofException If the endpoint is closed.
+     * @throws IOException if the endpoint is closed.
      */
     int fill(ByteBuffer buffer) throws IOException;
 
@@ -167,10 +169,10 @@
      * Flush data from the passed header/buffer to this endpoint.  As many bytes as can be consumed
      * are taken from the header/buffer position up until the buffer limit.  The header/buffers position
      * is updated to indicate how many bytes have been consumed.
-     * @return True IFF all the buffers have been consumed and the endpoint has flushed the data to its 
+     * @param buffer the buffers to flush
+     * @return True IFF all the buffers have been consumed and the endpoint has flushed the data to its
      * destination (ie is not buffering any data).
-     *
-     * @throws EofException If the endpoint is closed or output is shutdown.
+     * @throws IOException If the endpoint is closed or output is shutdown.
      */
     boolean flush(ByteBuffer... buffer) throws IOException;
 
@@ -184,13 +186,13 @@
     /** 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.
-     * @return the max idle time in ms or if ms <= 0 implies an infinite timeout
+     * @return the max idle time in ms or if ms &lt;= 0 implies an infinite timeout
      */
     long getIdleTimeout();
 
     /* ------------------------------------------------------------ */
     /** Set the idle timeout.
-     * @param idleTimeout the idle timeout in MS. Timeout <= 0 implies an infinite timeout
+     * @param idleTimeout the idle timeout in MS. Timeout &lt;= 0 implies an infinite timeout
      */
     void setIdleTimeout(long idleTimeout);
 
@@ -204,6 +206,20 @@
     void fillInterested(Callback callback) throws ReadPendingException;
 
     /**
+     * <p>Requests callback methods to be invoked when a call to {@link #fill(ByteBuffer)} would return data or EOF.</p>
+     *
+     * @param callback the callback to call when an error occurs or we are readable.
+     * @return true if set
+     */
+    boolean tryFillInterested(Callback callback);
+
+    /**
+     * @return whether {@link #fillInterested(Callback)} has been called, but {@link #fill(ByteBuffer)} has not yet
+     * been called
+     */
+    boolean isFillInterested();
+
+    /**
      * <p>Writes the given buffers via {@link #flush(ByteBuffer...)} and invokes callback methods when either
      * all the data has been flushed or an error occurs.</p>
      *
@@ -238,6 +254,11 @@
      */
     void onClose();
 
+    /** Is the endpoint optimized for DirectBuffer usage
+     * @return True if direct buffers can be used optimally.
+     */
+    boolean isOptimizedForDirectBuffers();
+
 
     /** Upgrade connections.
      * Close the old connection, update the endpoint and open the new connection.
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java b/jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java
index a0a740e..72c12ac 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java
@@ -21,115 +21,151 @@
 import java.io.IOException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.ReadPendingException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-
-/* ------------------------------------------------------------ */
-/** 
+/**
  * A Utility class to help implement {@link EndPoint#fillInterested(Callback)}
  * by keeping state and calling the context and callback objects.
- * 
  */
 public abstract class FillInterest
 {
     private final static Logger LOG = Log.getLogger(FillInterest.class);
     private final AtomicReference<Callback> _interested = new AtomicReference<>(null);
+    private Throwable _lastSet;
 
-    /* ------------------------------------------------------------ */
     protected FillInterest()
     {
     }
 
-    /* ------------------------------------------------------------ */
-    /** Call to register interest in a callback when a read is possible.
-     * The callback will be called either immediately if {@link #needsFill()} 
+    /**
+     * Call to register interest in a callback when a read is possible.
+     * The callback will be called either immediately if {@link #needsFillInterest()}
      * returns true or eventually once {@link #fillable()} is called.
-     * @param callback
-     * @throws ReadPendingException
+     *
+     * @param callback the callback to register
+     * @throws ReadPendingException if unable to read due to pending read op
      */
-    public <C> void register(Callback callback) throws ReadPendingException
+    public void register(Callback callback) throws ReadPendingException
     {
-        if (callback==null)
-            throw new IllegalArgumentException();
-        
-        if (!_interested.compareAndSet(null,callback))
+        if (!tryRegister(callback))
         {
-            LOG.warn("Read pending for "+_interested.get()+" prevented "+callback);
+            LOG.warn("Read pending for {} prevented {}", _interested, callback);
+            if (LOG.isDebugEnabled())
+                LOG.warn("callback set at ",_lastSet);
             throw new ReadPendingException();
+        }   
+    }
+    
+    /**
+     * Call to register interest in a callback when a read is possible.
+     * The callback will be called either immediately if {@link #needsFillInterest()}
+     * returns true or eventually once {@link #fillable()} is called.
+     *
+     * @param callback the callback to register
+     * @return true if the register succeeded
+     */
+    public boolean tryRegister(Callback callback)
+    {
+        if (callback == null)
+            throw new IllegalArgumentException();
+
+        if (!_interested.compareAndSet(null, callback))
+            return false;
+
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("{} register {}",this,callback);
+            _lastSet=new Throwable(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()) + ":" + Thread.currentThread().getName());
         }
+        
         try
         {
-            if (needsFill())
-                fillable();
+            needsFillInterest();
         }
-        catch(IOException e)
+        catch (Throwable e)
         {
             onFail(e);
         }
+        
+        return true;
     }
 
-    /* ------------------------------------------------------------ */
-    /** Call to signal that a read is now possible.
+    /**
+     * Call to signal that a read is now possible.
      */
     public void fillable()
     {
-        Callback callback=_interested.get();
-        if (callback!=null && _interested.compareAndSet(callback,null))
+        Callback callback = _interested.get();
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} fillable {}",this,callback);
+        if (callback != null && _interested.compareAndSet(callback, null))
             callback.succeeded();
+        else if (LOG.isDebugEnabled())
+            LOG.debug("{} lost race {}",this,callback);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @return True if a read callback has been registered
      */
     public boolean isInterested()
     {
-        return _interested.get()!=null;
+        return _interested.get() != null;
     }
     
-    /* ------------------------------------------------------------ */
-    /** Call to signal a failure to a registered interest
+    public boolean isCallbackNonBlocking()
+    {
+        Callback callback = _interested.get();
+        return callback!=null && callback.isNonBlocking();
+    }
+
+    /**
+     * Call to signal a failure to a registered interest
+     *
+     * @param cause the cause of the failure
      * @return true if the cause was passed to a {@link Callback} instance
      */
     public boolean onFail(Throwable cause)
     {
-        Callback callback=_interested.get();
-        if (callback!=null && _interested.compareAndSet(callback,null))
+        Callback callback = _interested.get();
+        if (callback != null && _interested.compareAndSet(callback, null))
         {
             callback.failed(cause);
             return true;
         }
         return false;
     }
-    
-    /* ------------------------------------------------------------ */
+
     public void onClose()
     {
-        Callback callback=_interested.get();
-        if (callback!=null && _interested.compareAndSet(callback,null))
+        Callback callback = _interested.get();
+        if (callback != null && _interested.compareAndSet(callback, null))
             callback.failed(new ClosedChannelException());
     }
-    
-    /* ------------------------------------------------------------ */
+
     @Override
     public String toString()
     {
-        return String.format("FillInterest@%x{%b,%s}",hashCode(),_interested.get(),_interested.get());
+        return String.format("FillInterest@%x{%b,%s}", hashCode(), _interested.get()!=null, _interested.get());
     }
+
     
-    /* ------------------------------------------------------------ */
-    /** Register the read interest 
+    public String toStateString()
+    {
+        return _interested.get()==null?"-":"FI";
+    }
+
+    /**
+     * Register the read interest
      * Abstract method to be implemented by the Specific ReadInterest to
-     * enquire if a read is immediately possible and if not to schedule a future
-     * call to {@link #fillable()} or {@link #onFail(Throwable)}
-     * @return true if a read is possible
-     * @throws IOException
+     * schedule a future call to {@link #fillable()} or {@link #onFail(Throwable)}
+     *
+     * @throws IOException if unable to fulfill interest in fill
      */
-    abstract protected boolean needsFill() throws IOException;
-    
-    
+    abstract protected void needsFillInterest() throws IOException;
 }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/IdleTimeout.java b/jetty-io/src/main/java/org/eclipse/jetty/io/IdleTimeout.java
index 5f2c44d..8fccc67 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/IdleTimeout.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/IdleTimeout.java
@@ -28,7 +28,7 @@
 
 /**
  * An Abstract implementation of an Idle Timeout.
- * <p/>
+ * <p>
  * This implementation is optimised that timeout operations are not cancelled on
  * every operation. Rather timeout are allowed to expire and a check is then made
  * to see when the last operation took place.  If the idle timeout has not expired,
@@ -61,6 +61,11 @@
         _scheduler = scheduler;
     }
 
+    public Scheduler getScheduler()
+    {
+        return _scheduler;
+    }
+    
     public long getIdleTimestamp()
     {
         return _idleTimestamp;
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java
new file mode 100644
index 0000000..3a80593
--- /dev/null
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java
@@ -0,0 +1,779 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.Closeable;
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+import java.nio.channels.CancelledKeyException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
+import org.eclipse.jetty.util.thread.Locker;
+import org.eclipse.jetty.util.thread.Scheduler;
+
+/**
+ * <p>{@link ManagedSelector} wraps a {@link Selector} simplifying non-blocking operations on channels.</p>
+ * <p>{@link ManagedSelector} runs the select loop, which waits on {@link Selector#select()} until events
+ * happen for registered channels. When events happen, it notifies the {@link EndPoint} associated
+ * with the channel.</p>
+ */
+public class ManagedSelector extends AbstractLifeCycle implements Runnable, Dumpable
+{
+    private static final Logger LOG = Log.getLogger(ManagedSelector.class);
+
+    private final Locker _locker = new Locker();
+    private boolean _selecting = false;
+    private final Queue<Runnable> _actions = new ArrayDeque<>();
+    private final SelectorManager _selectorManager;
+    private final int _id;
+    private final ExecutionStrategy _strategy;
+    private Selector _selector;
+
+    public ManagedSelector(SelectorManager selectorManager, int id)
+    {
+        this(selectorManager, id, ExecutionStrategy.Factory.getDefault());
+    }
+
+    public ManagedSelector(SelectorManager selectorManager, int id, ExecutionStrategy.Factory executionFactory)
+    {
+        _selectorManager = selectorManager;
+        _id = id;
+        _strategy = executionFactory.newExecutionStrategy(new SelectorProducer(), selectorManager.getExecutor());
+        setStopTimeout(5000);
+    }
+
+    public ExecutionStrategy getExecutionStrategy()
+    {
+        return _strategy;
+    }
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        super.doStart();
+        _selector = newSelector();
+        _selectorManager.execute(this);
+    }
+
+    protected Selector newSelector() throws IOException
+    {
+        return Selector.open();
+    }
+
+    public int size()
+    {
+        Selector s = _selector;
+        if (s == null)
+            return 0;
+        return s.keys().size();
+    }
+
+    @Override
+    protected void doStop() throws Exception
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Stopping {}", this);
+        CloseEndPoints close_endps = new CloseEndPoints();
+        submit(close_endps);
+        close_endps.await(getStopTimeout());
+        super.doStop();
+        CloseSelector close_selector = new CloseSelector();
+        submit(close_selector);
+        close_selector.await(getStopTimeout());
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("Stopped {}", this);
+    }
+
+    public void submit(Runnable change)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Queued change {} on {}", change, this);
+
+        Selector selector = null;
+        try (Locker.Lock lock = _locker.lock())
+        {
+            _actions.offer(change);
+            if (_selecting)
+            {
+                selector = _selector;
+                // To avoid the extra select wakeup.
+                _selecting = false;
+            }
+        }
+        if (selector != null)
+            selector.wakeup();
+    }
+
+    @Override
+    public void run()
+    {
+        _strategy.execute();
+    }
+
+    /**
+     * A {@link SelectableEndPoint} is an {@link EndPoint} that wish to be
+     * notified of non-blocking events by the {@link ManagedSelector}.
+     */
+    public interface SelectableEndPoint extends EndPoint
+    {
+        /**
+         * Callback method invoked when a read or write events has been
+         * detected by the {@link ManagedSelector} for this endpoint.
+         *
+         * @return a job that may block or null
+         */
+        Runnable onSelected();
+
+        /**
+         * Callback method invoked when all the keys selected by the
+         * {@link ManagedSelector} for this endpoint have been processed.
+         */
+        void updateKey();
+    }
+
+    private class SelectorProducer implements ExecutionStrategy.Producer
+    {
+        private Set<SelectionKey> _keys = Collections.emptySet();
+        private Iterator<SelectionKey> _cursor = Collections.emptyIterator();
+
+        @Override
+        public Runnable produce()
+        {
+            while (true)
+            {
+                Runnable task = processSelected();
+                if (task != null)
+                    return task;
+
+                Runnable action = runActions();
+                if (action != null)
+                    return action;
+
+                update();
+
+                if (!select())
+                    return null;
+            }
+        }
+
+        private Runnable runActions()
+        {
+            while (true)
+            {
+                Runnable action;
+                try (Locker.Lock lock = _locker.lock())
+                {
+                    action = _actions.poll();
+                    if (action == null)
+                    {
+                        // No more actions, so we need to select
+                        _selecting = true;
+                        return null;
+                    }
+                }
+
+                if (action instanceof Product)
+                    return action;
+
+                // Running the change may queue another action.
+                runChange(action);
+            }
+        }
+
+        private void runChange(Runnable change)
+        {
+            try
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Running change {}", change);
+                change.run();
+            }
+            catch (Throwable x)
+            {
+                LOG.debug("Could not run change " + change, x);
+            }
+        }
+
+        private boolean select()
+        {
+            try
+            {
+                Selector selector = _selector;
+                if (selector != null && selector.isOpen())
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Selector loop waiting on select");
+                    int selected = selector.select();
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Selector loop woken up from select, {}/{} selected", selected, selector.keys().size());
+
+                    try (Locker.Lock lock = _locker.lock())
+                    {
+                        // finished selecting
+                        _selecting = false;
+                    }
+
+                    _keys = selector.selectedKeys();
+                    _cursor = _keys.iterator();
+
+                    return true;
+                }
+            }
+            catch (Throwable x)
+            {
+                closeNoExceptions(_selector);
+                if (isRunning())
+                    LOG.warn(x);
+                else
+                    LOG.debug(x);
+            }
+            return false;
+        }
+
+        private Runnable processSelected()
+        {
+            while (_cursor.hasNext())
+            {
+                SelectionKey key = _cursor.next();
+                if (key.isValid())
+                {
+                    Object attachment = key.attachment();
+                    try
+                    {
+                        if (attachment instanceof SelectableEndPoint)
+                        {
+                            // Try to produce a task
+                            Runnable task = ((SelectableEndPoint)attachment).onSelected();
+                            if (task != null)
+                                return task;
+                        }
+                        else if (key.isConnectable())
+                        {
+                            Runnable task = processConnect(key, (Connect)attachment);
+                            if (task != null)
+                                return task;
+                        }
+                        else if (key.isAcceptable())
+                        {
+                            processAccept(key);
+                        }
+                        else
+                        {
+                            throw new IllegalStateException("key=" + key + ", att=" + attachment + ", iOps=" + key.interestOps() + ", rOps=" + key.readyOps());
+                        }
+                    }
+                    catch (CancelledKeyException x)
+                    {
+                        LOG.debug("Ignoring cancelled key for channel {}", key.channel());
+                        if (attachment instanceof EndPoint)
+                            closeNoExceptions((EndPoint)attachment);
+                    }
+                    catch (Throwable x)
+                    {
+                        LOG.warn("Could not process key for channel " + key.channel(), x);
+                        if (attachment instanceof EndPoint)
+                            closeNoExceptions((EndPoint)attachment);
+                    }
+                }
+                else
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Selector loop ignoring invalid key for channel {}", key.channel());
+                    Object attachment = key.attachment();
+                    if (attachment instanceof EndPoint)
+                        closeNoExceptions((EndPoint)attachment);
+                }
+            }
+            return null;
+        }
+
+        private void update()
+        {
+            for (SelectionKey key : _keys)
+                updateKey(key);
+            _keys.clear();
+        }
+
+        private void updateKey(SelectionKey key)
+        {
+            Object attachment = key.attachment();
+            if (attachment instanceof SelectableEndPoint)
+                ((SelectableEndPoint)attachment).updateKey();
+        }
+    }
+
+    private interface Product extends Runnable
+    {
+    }
+
+    private Runnable processConnect(SelectionKey key, final Connect connect)
+    {
+        SocketChannel channel = (SocketChannel)key.channel();
+        try
+        {
+            key.attach(connect.attachment);
+            boolean connected = _selectorManager.finishConnect(channel);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Connected {} {}", connected, channel);
+            if (connected)
+            {
+                if (connect.timeout.cancel())
+                {
+                    key.interestOps(0);
+                    return new CreateEndPoint(channel, key)
+                    {
+                        @Override
+                        protected void failed(Throwable failure)
+                        {
+                            super.failed(failure);
+                            connect.failed(failure);
+                        }
+                    };
+                }
+                else
+                {
+                    throw new SocketTimeoutException("Concurrent Connect Timeout");
+                }
+            }
+            else
+            {
+                throw new ConnectException();
+            }
+        }
+        catch (Throwable x)
+        {
+            connect.failed(x);
+            return null;
+        }
+    }
+
+    private void processAccept(SelectionKey key)
+    {
+        ServerSocketChannel server = (ServerSocketChannel)key.channel();
+        SocketChannel channel = null;
+        try
+        {
+            while ((channel = server.accept()) != null)
+            {
+                _selectorManager.accepted(channel);
+            }
+        }
+        catch (Throwable x)
+        {
+            closeNoExceptions(channel);
+            LOG.warn("Accept failed for channel " + channel, x);
+        }
+    }
+
+    private void closeNoExceptions(Closeable closeable)
+    {
+        try
+        {
+            if (closeable != null)
+                closeable.close();
+        }
+        catch (Throwable x)
+        {
+            LOG.ignore(x);
+        }
+    }
+
+    private EndPoint createEndPoint(SocketChannel channel, SelectionKey selectionKey) throws IOException
+    {
+        EndPoint endPoint = _selectorManager.newEndPoint(channel, this, selectionKey);
+        _selectorManager.endPointOpened(endPoint);
+        Connection connection = _selectorManager.newConnection(channel, endPoint, selectionKey.attachment());
+        endPoint.setConnection(connection);
+        selectionKey.attach(endPoint);
+        _selectorManager.connectionOpened(connection);
+        if (LOG.isDebugEnabled())
+            LOG.debug("Created {}", endPoint);
+        return endPoint;
+    }
+
+    public void destroyEndPoint(final EndPoint endPoint)
+    {
+        final Connection connection = endPoint.getConnection();
+        submit(new Product()
+        {
+            @Override
+            public void run()
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Destroyed {}", endPoint);
+                if (connection != null)
+                    _selectorManager.connectionClosed(connection);
+                _selectorManager.endPointClosed(endPoint);
+            }
+        });
+    }
+
+    @Override
+    public String dump()
+    {
+        return ContainerLifeCycle.dump(this);
+    }
+
+    @Override
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        out.append(String.valueOf(this)).append(" id=").append(String.valueOf(_id)).append(System.lineSeparator());
+
+        Selector selector = _selector;
+        if (selector != null && selector.isOpen())
+        {
+            final ArrayList<Object> dump = new ArrayList<>(selector.keys().size() * 2);
+
+            DumpKeys dumpKeys = new DumpKeys(dump);
+            submit(dumpKeys);
+            dumpKeys.await(5, TimeUnit.SECONDS);
+
+            ContainerLifeCycle.dump(out, indent, dump);
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        Selector selector = _selector;
+        return String.format("%s id=%s keys=%d selected=%d",
+                super.toString(),
+                _id,
+                selector != null && selector.isOpen() ? selector.keys().size() : -1,
+                selector != null && selector.isOpen() ? selector.selectedKeys().size() : -1);
+    }
+
+    private class DumpKeys implements Runnable
+    {
+        private final CountDownLatch latch = new CountDownLatch(1);
+        private final List<Object> _dumps;
+
+        private DumpKeys(List<Object> dumps)
+        {
+            this._dumps = dumps;
+        }
+
+        @Override
+        public void run()
+        {
+            Selector selector = _selector;
+            if (selector != null && selector.isOpen())
+            {
+                Set<SelectionKey> keys = selector.keys();
+                _dumps.add(selector + " keys=" + keys.size());
+                for (SelectionKey key : keys)
+                {
+                    try
+                    {
+                        _dumps.add(String.format("SelectionKey@%x{i=%d}->%s", key.hashCode(), key.interestOps(), key.attachment()));
+                    }
+                    catch (Throwable x)
+                    {
+                        LOG.ignore(x);
+                    }
+                }
+            }
+            latch.countDown();
+        }
+
+        public boolean await(long timeout, TimeUnit unit)
+        {
+            try
+            {
+                return latch.await(timeout, unit);
+            }
+            catch (InterruptedException x)
+            {
+                return false;
+            }
+        }
+    }
+
+    class Acceptor implements Runnable
+    {
+        private final ServerSocketChannel _channel;
+
+        public Acceptor(ServerSocketChannel channel)
+        {
+            this._channel = channel;
+        }
+
+        @Override
+        public void run()
+        {
+            try
+            {
+                SelectionKey key = _channel.register(_selector, SelectionKey.OP_ACCEPT, null);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} acceptor={}", this, key);
+            }
+            catch (Throwable x)
+            {
+                closeNoExceptions(_channel);
+                LOG.warn(x);
+            }
+        }
+    }
+
+    class Accept implements Runnable, Closeable
+    {
+        private final SocketChannel channel;
+        private final Object attachment;
+
+        Accept(SocketChannel channel, Object attachment)
+        {
+            this.channel = channel;
+            this.attachment = attachment;
+        }
+
+        @Override
+        public void close()
+        {
+            LOG.debug("closed accept of {}", channel);
+            closeNoExceptions(channel);
+        }
+
+        @Override
+        public void run()
+        {
+            try
+            {
+                final SelectionKey key = channel.register(_selector, 0, attachment);
+                submit(new CreateEndPoint(channel, key));
+            }
+            catch (Throwable x)
+            {
+                closeNoExceptions(channel);
+                LOG.debug(x);
+            }
+        }
+    }
+
+    private class CreateEndPoint implements Product, Closeable
+    {
+        private final SocketChannel channel;
+        private final SelectionKey key;
+
+        public CreateEndPoint(SocketChannel channel, SelectionKey key)
+        {
+            this.channel = channel;
+            this.key = key;
+        }
+
+        @Override
+        public void run()
+        {
+            try
+            {
+                createEndPoint(channel, key);
+            }
+            catch (Throwable x)
+            {
+                LOG.debug(x);
+                failed(x);
+            }
+        }
+
+        @Override
+        public void close()
+        {
+            LOG.debug("closed creation of {}", channel);
+            closeNoExceptions(channel);
+        }
+
+        protected void failed(Throwable failure)
+        {
+            closeNoExceptions(channel);
+            LOG.debug(failure);
+        }
+    }
+
+    class Connect implements Runnable
+    {
+        private final AtomicBoolean failed = new AtomicBoolean();
+        private final SocketChannel channel;
+        private final Object attachment;
+        private final Scheduler.Task timeout;
+
+        Connect(SocketChannel channel, Object attachment)
+        {
+            this.channel = channel;
+            this.attachment = attachment;
+            this.timeout = ManagedSelector.this._selectorManager.getScheduler().schedule(new ConnectTimeout(this), ManagedSelector.this._selectorManager.getConnectTimeout(), TimeUnit.MILLISECONDS);
+        }
+
+        @Override
+        public void run()
+        {
+            try
+            {
+                channel.register(_selector, SelectionKey.OP_CONNECT, this);
+            }
+            catch (Throwable x)
+            {
+                failed(x);
+            }
+        }
+
+        private void failed(Throwable failure)
+        {
+            if (failed.compareAndSet(false, true))
+            {
+                timeout.cancel();
+                closeNoExceptions(channel);
+                ManagedSelector.this._selectorManager.connectionFailed(channel, failure, attachment);
+            }
+        }
+    }
+
+    private class ConnectTimeout implements Runnable
+    {
+        private final Connect connect;
+
+        private ConnectTimeout(Connect connect)
+        {
+            this.connect = connect;
+        }
+
+        @Override
+        public void run()
+        {
+            SocketChannel channel = connect.channel;
+            if (channel.isConnectionPending())
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Channel {} timed out while connecting, closing it", channel);
+                connect.failed(new SocketTimeoutException("Connect Timeout"));
+            }
+        }
+    }
+
+    private class CloseEndPoints implements Runnable
+    {
+        private final CountDownLatch _latch = new CountDownLatch(1);
+        private CountDownLatch _allClosed;
+
+        @Override
+        public void run()
+        {
+            List<EndPoint> end_points = new ArrayList<>();
+            for (SelectionKey key : _selector.keys())
+            {
+                if (key.isValid())
+                {
+                    Object attachment = key.attachment();
+                    if (attachment instanceof EndPoint)
+                        end_points.add((EndPoint)attachment);
+                }
+            }
+
+            int size = end_points.size();
+            if (LOG.isDebugEnabled())
+                LOG.debug("Closing {} endPoints on {}", size, ManagedSelector.this);
+
+            _allClosed = new CountDownLatch(size);
+            _latch.countDown();
+
+            for (EndPoint endp : end_points)
+                submit(new EndPointCloser(endp, _allClosed));
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("Closed {} endPoints on {}", size, ManagedSelector.this);
+        }
+
+        public boolean await(long timeout)
+        {
+            try
+            {
+                return _latch.await(timeout, TimeUnit.MILLISECONDS) &&
+                        _allClosed.await(timeout, TimeUnit.MILLISECONDS);
+            }
+            catch (InterruptedException x)
+            {
+                return false;
+            }
+        }
+    }
+
+    private class EndPointCloser implements Product
+    {
+        private final EndPoint _endPoint;
+        private final CountDownLatch _latch;
+
+        private EndPointCloser(EndPoint endPoint, CountDownLatch latch)
+        {
+            _endPoint = endPoint;
+            _latch = latch;
+        }
+
+        @Override
+        public void run()
+        {
+            closeNoExceptions(_endPoint.getConnection());
+            _latch.countDown();
+        }
+    }
+
+    private class CloseSelector implements Runnable
+    {
+        private CountDownLatch _latch = new CountDownLatch(1);
+
+        @Override
+        public void run()
+        {
+            Selector selector = _selector;
+            _selector = null;
+            closeNoExceptions(selector);
+            _latch.countDown();
+        }
+
+        public boolean await(long timeout)
+        {
+            try
+            {
+                return _latch.await(timeout, TimeUnit.MILLISECONDS);
+            }
+            catch (InterruptedException x)
+            {
+                return false;
+            }
+        }
+    }
+}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/MappedByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/MappedByteBufferPool.java
index e46e1f1..0bc2b38 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/MappedByteBufferPool.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/MappedByteBufferPool.java
@@ -19,55 +19,53 @@
 package org.eclipse.jetty.io;
 
 import java.nio.ByteBuffer;
-import java.util.Queue;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
 
 import org.eclipse.jetty.util.BufferUtil;
 
 public class MappedByteBufferPool implements ByteBufferPool
 {
-    private final ConcurrentMap<Integer, Queue<ByteBuffer>> directBuffers = new ConcurrentHashMap<>();
-    private final ConcurrentMap<Integer, Queue<ByteBuffer>> heapBuffers = new ConcurrentHashMap<>();
-    private final int factor;
+    private final ConcurrentMap<Integer, Bucket> directBuffers = new ConcurrentHashMap<>();
+    private final ConcurrentMap<Integer, Bucket> heapBuffers = new ConcurrentHashMap<>();
+    private final int _factor;
+    private final int _maxQueue;
+    private final Function<Integer, Bucket> _newBucket;
 
     public MappedByteBufferPool()
     {
-        this(1024);
+        this(-1);
     }
 
     public MappedByteBufferPool(int factor)
     {
-        this.factor = factor;
+        this(factor,-1,null);
+    }
+    
+    public MappedByteBufferPool(int factor,int maxQueue)
+    {
+        this(factor,maxQueue,null);
+    }
+    
+    public MappedByteBufferPool(int factor,int maxQueue,Function<Integer, Bucket> newBucket)
+    {
+        _factor = factor<=0?1024:factor;
+        _maxQueue = maxQueue;
+        _newBucket = newBucket!=null?newBucket:i->new Bucket(this,i*_factor,_maxQueue);
     }
 
     @Override
     public ByteBuffer acquire(int size, boolean direct)
     {
-        int bucket = bucketFor(size);
-        ConcurrentMap<Integer, Queue<ByteBuffer>> buffers = buffersFor(direct);
+        int b = bucketFor(size);
+        ConcurrentMap<Integer, Bucket> buffers = bucketsFor(direct);
 
-        ByteBuffer result = null;
-        Queue<ByteBuffer> byteBuffers = buffers.get(bucket);
-        if (byteBuffers != null)
-            result = byteBuffers.poll();
-
-        if (result == null)
-        {
-            int capacity = bucket * factor;
-            result = newByteBuffer(capacity, direct);
-        }
-
-        BufferUtil.clear(result);
-        return result;
-    }
-
-    protected ByteBuffer newByteBuffer(int capacity, boolean direct)
-    {
-        return direct ? BufferUtil.allocateDirect(capacity)
-                      : BufferUtil.allocate(capacity);
+        Bucket bucket = buffers.get(b);
+        if (bucket==null)
+            return newByteBuffer(b*_factor, direct);
+        return bucket.acquire(direct);
     }
 
     @Override
@@ -77,41 +75,33 @@
             return; // nothing to do
         
         // validate that this buffer is from this pool
-        assert((buffer.capacity() % factor) == 0);
+        assert((buffer.capacity() % _factor) == 0);
         
-        int bucket = bucketFor(buffer.capacity());
-        ConcurrentMap<Integer, Queue<ByteBuffer>> buffers = buffersFor(buffer.isDirect());
+        int b = bucketFor(buffer.capacity());
+        ConcurrentMap<Integer, Bucket> buckets = bucketsFor(buffer.isDirect());
 
-        // Avoid to create a new queue every time, just to be discarded immediately
-        Queue<ByteBuffer> byteBuffers = buffers.get(bucket);
-        if (byteBuffers == null)
-        {
-            byteBuffers = new ConcurrentLinkedQueue<>();
-            Queue<ByteBuffer> existing = buffers.putIfAbsent(bucket, byteBuffers);
-            if (existing != null)
-                byteBuffers = existing;
-        }
-
-        BufferUtil.clear(buffer);
-        byteBuffers.offer(buffer);
+        Bucket bucket = buckets.computeIfAbsent(b,_newBucket);
+        bucket.release(buffer);
     }
 
     public void clear()
     {
+        directBuffers.values().forEach(Bucket::clear);
         directBuffers.clear();
+        heapBuffers.values().forEach(Bucket::clear);
         heapBuffers.clear();
     }
 
     private int bucketFor(int size)
     {
-        int bucket = size / factor;
-        if (size % factor > 0)
+        int bucket = size / _factor;
+        if (size % _factor > 0)
             ++bucket;
         return bucket;
     }
 
     // Package local for testing
-    ConcurrentMap<Integer, Queue<ByteBuffer>> buffersFor(boolean direct)
+    ConcurrentMap<Integer, Bucket> bucketsFor(boolean direct)
     {
         return direct ? directBuffers : heapBuffers;
     }
@@ -121,7 +111,7 @@
         private final AtomicInteger tag = new AtomicInteger();
 
         @Override
-        protected ByteBuffer newByteBuffer(int capacity, boolean direct)
+        public ByteBuffer newByteBuffer(int capacity, boolean direct)
         {
             ByteBuffer buffer = super.newByteBuffer(capacity + 4, direct);
             buffer.limit(buffer.capacity());
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java
index 1499e9f..a4202df 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.io;
 
 import java.io.IOException;
-import java.nio.ByteBuffer;
 import java.util.Map;
 import java.util.concurrent.Executor;
 
@@ -109,9 +108,7 @@
         EndPoint endPoint = getEndPoint();
         try
         {
-            Connection oldConnection = endPoint.getConnection();
-            Connection newConnection = connectionFactory.newConnection(endPoint, context);
-            ClientConnectionFactory.Helper.replaceConnection(oldConnection, newConnection);
+            endPoint.upgrade(connectionFactory.newConnection(endPoint, context));
         }
         catch (Throwable x)
         {
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficListener.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficListener.java
index 692d325..16a8646 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficListener.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficListener.java
@@ -67,7 +67,7 @@
     /**
      * <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 />
+     * 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.
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java
index 2c3ebf8..dafff33 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java
@@ -35,7 +35,7 @@
 
     private final List<NetworkTrafficListener> listeners;
 
-    public NetworkTrafficSelectChannelEndPoint(SocketChannel channel, SelectorManager.ManagedSelector selectSet, SelectionKey key, Scheduler scheduler, long idleTimeout, List<NetworkTrafficListener> listeners) throws IOException
+    public NetworkTrafficSelectChannelEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key, Scheduler scheduler, long idleTimeout, List<NetworkTrafficListener> listeners) throws IOException
     {
         super(channel, selectSet, key, scheduler, idleTimeout);
         this.listeners = listeners;
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/SelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/SelectChannelEndPoint.java
index 65c346a..2503159 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/SelectChannelEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/SelectChannelEndPoint.java
@@ -18,148 +18,256 @@
 
 package org.eclipse.jetty.io;
 
+import java.io.Closeable;
 import java.nio.channels.CancelledKeyException;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.SocketChannel;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 
-import org.eclipse.jetty.io.SelectorManager.ManagedSelector;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Locker;
 import org.eclipse.jetty.util.thread.Scheduler;
 
 /**
  * An ChannelEndpoint that can be scheduled by {@link SelectorManager}.
  */
-public class SelectChannelEndPoint extends ChannelEndPoint implements SelectorManager.SelectableEndPoint
+public class SelectChannelEndPoint extends ChannelEndPoint implements ManagedSelector.SelectableEndPoint
 {
     public static final Logger LOG = Log.getLogger(SelectChannelEndPoint.class);
 
-    private final Runnable _updateTask = new Runnable()
-    {
-        @Override
-        public void run()
-        {
-            try
-            {
-                if (getChannel().isOpen())
-                {
-                    int oldInterestOps = _key.interestOps();
-                    int newInterestOps = _interestOps.get();
-                    if (newInterestOps != oldInterestOps)
-                        setKeyInterests(oldInterestOps, newInterestOps);
-                }
-            }
-            catch (CancelledKeyException x)
-            {
-                LOG.debug("Ignoring key update for concurrently closed channel {}", this);
-                close();
-            }
-            catch (Exception x)
-            {
-                LOG.warn("Ignoring key update for " + this, x);
-                close();
-            }
-        }
-    };
+    private final Locker _locker = new Locker();
+    private boolean _updatePending;
 
     /**
      * true if {@link ManagedSelector#destroyEndPoint(EndPoint)} has not been called
      */
     private final AtomicBoolean _open = new AtomicBoolean();
-    private final SelectorManager.ManagedSelector _selector;
+    private final ManagedSelector _selector;
     private final SelectionKey _key;
     /**
-     * The desired value for {@link SelectionKey#interestOps()}
+     * The current value for {@link SelectionKey#interestOps()}.
      */
-    private final AtomicInteger _interestOps = new AtomicInteger();
+    private int _currentInterestOps;
+    /**
+     * The desired value for {@link SelectionKey#interestOps()}.
+     */
+    private int _desiredInterestOps;
+
+    private final Runnable _runUpdateKey = new Runnable()
+    {
+        @Override
+        public void run()
+        {
+            updateKey();
+        }
+
+        @Override
+        public String toString()
+        {
+            return SelectChannelEndPoint.this.toString()+":runUpdateKey";
+        }
+    };
+
+    private abstract class RunnableCloseable implements Runnable, Closeable
+    {
+        @Override
+        public void close()
+        {
+            try
+            {
+                SelectChannelEndPoint.this.close();
+            }
+            catch (Throwable x)
+            {
+                LOG.warn(x);
+            }
+        }
+    }
+
+    private final Runnable _runFillable = new RunnableCloseable()
+    {
+        @Override
+        public void run()
+        {
+            getFillInterest().fillable();
+        }
+
+        @Override
+        public String toString()
+        {
+            return SelectChannelEndPoint.this.toString()+":runFillable";
+        }
+    };
+    private final Runnable _runCompleteWrite = new RunnableCloseable()
+    {
+        @Override
+        public void run()
+        {
+            getWriteFlusher().completeWrite();
+        }
+
+        @Override
+        public String toString()
+        {
+            return SelectChannelEndPoint.this.toString()+":runCompleteWrite";
+        }
+    };
+    private final Runnable _runCompleteWriteFillable = new RunnableCloseable()
+    {
+        @Override
+        public void run()
+        {
+            getWriteFlusher().completeWrite();
+            getFillInterest().fillable();
+        }
+
+        @Override
+        public String toString()
+        {
+            return SelectChannelEndPoint.this.toString()+":runFillableCompleteWrite";
+        }
+    };
 
     public SelectChannelEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey key, Scheduler scheduler, long idleTimeout)
     {
-        super(scheduler,channel);
+        super(scheduler, channel);
         _selector = selector;
         _key = key;
         setIdleTimeout(idleTimeout);
     }
 
     @Override
-    protected boolean needsFill()
+    protected void needsFillInterest()
     {
-        updateLocalInterests(SelectionKey.OP_READ, true);
-        return false;
+        changeInterests(SelectionKey.OP_READ);
     }
 
     @Override
     protected void onIncompleteFlush()
     {
-        updateLocalInterests(SelectionKey.OP_WRITE, true);
+        changeInterests(SelectionKey.OP_WRITE);
     }
 
     @Override
-    public void onSelected()
+    public Runnable onSelected()
     {
-        assert _selector.isSelectorThread();
-        int oldInterestOps = _key.interestOps();
+        /**
+         * This method may run concurrently with {@link #changeInterests(int)}.
+         */
+
         int readyOps = _key.readyOps();
-        int newInterestOps = oldInterestOps & ~readyOps;
-        setKeyInterests(oldInterestOps, newInterestOps);
-        updateLocalInterests(readyOps, false);
-        if (_key.isReadable())
-            getFillInterest().fillable();
-        if (_key.isWritable())
-            getWriteFlusher().completeWrite();
+        int oldInterestOps;
+        int newInterestOps;
+        try (Locker.Lock lock = _locker.lock())
+        {
+            _updatePending = true;
+            // Remove the readyOps, that here can only be OP_READ or OP_WRITE (or both).
+            oldInterestOps = _desiredInterestOps;
+            newInterestOps = oldInterestOps & ~readyOps;
+            _desiredInterestOps = newInterestOps;
+        }
+
+        boolean readable = (readyOps & SelectionKey.OP_READ) != 0;
+        boolean writable = (readyOps & SelectionKey.OP_WRITE) != 0;
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("onSelected {}->{} r={} w={} for {}", oldInterestOps, newInterestOps, readable, writable, this);
+
+        // Run non-blocking code immediately.
+        // This producer knows that this non-blocking code is special
+        // and that it must be run in this thread and not fed to the
+        // ExecutionStrategy, which could not have any thread to run these
+        // tasks (or it may starve forever just after having run them).
+        if (readable && getFillInterest().isCallbackNonBlocking())
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Direct readable run {}",this);
+            _runFillable.run();
+            readable = false;
+        }
+        if (writable && getWriteFlusher().isCallbackNonBlocking())
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Direct writable run {}",this);
+            _runCompleteWrite.run();
+            writable = false;
+        }
+
+        // return task to complete the job
+        Runnable task= readable ? (writable ? _runCompleteWriteFillable : _runFillable)
+                : (writable ? _runCompleteWrite : null);
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("task {}",task);
+        return task;
     }
 
-
-    private void updateLocalInterests(int operation, boolean add)
+    @Override
+    public void updateKey()
     {
-        while (true)
+        /**
+         * This method may run concurrently with {@link #changeInterests(int)}.
+         */
+
+        try
         {
-            int oldInterestOps = _interestOps.get();
+            int oldInterestOps;
             int newInterestOps;
-            if (add)
-                newInterestOps = oldInterestOps | operation;
-            else
-                newInterestOps = oldInterestOps & ~operation;
-
-            if (isInputShutdown())
-                newInterestOps &= ~SelectionKey.OP_READ;
-            if (isOutputShutdown())
-                newInterestOps &= ~SelectionKey.OP_WRITE;
-
-            if (newInterestOps != oldInterestOps)
+            try (Locker.Lock lock = _locker.lock())
             {
-                if (_interestOps.compareAndSet(oldInterestOps, newInterestOps))
+                _updatePending = false;
+                oldInterestOps = _currentInterestOps;
+                newInterestOps = _desiredInterestOps;
+                if (oldInterestOps != newInterestOps)
                 {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Local interests updating {} -> {} for {}", oldInterestOps, newInterestOps, this);
-                    _selector.updateKey(_updateTask);
-                }
-                else
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Local interests update conflict: now {}, was {}, attempted {} for {}", _interestOps.get(), oldInterestOps, newInterestOps, this);
-                    continue;
+                    _currentInterestOps = newInterestOps;
+                    _key.interestOps(newInterestOps);
                 }
             }
-            else
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Ignoring local interests update {} -> {} for {}", oldInterestOps, newInterestOps, this);
-            }
-            break;
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("Key interests updated {} -> {} on {}", oldInterestOps, newInterestOps, this);
+        }
+        catch (CancelledKeyException x)
+        {
+            LOG.debug("Ignoring key update for concurrently closed channel {}", this);
+            close();
+        }
+        catch (Throwable x)
+        {
+            LOG.warn("Ignoring key update for " + this, x);
+            close();
         }
     }
 
-
-    private void setKeyInterests(int oldInterestOps, int newInterestOps)
+    private void changeInterests(int operation)
     {
-        _key.interestOps(newInterestOps);
+        /**
+         * This method may run concurrently with
+         * {@link #updateKey()} and {@link #onSelected()}.
+         */
+
+        int oldInterestOps;
+        int newInterestOps;
+        boolean pending;
+        try (Locker.Lock lock = _locker.lock())
+        {
+            pending = _updatePending;
+            oldInterestOps = _desiredInterestOps;
+            newInterestOps = oldInterestOps | operation;
+            if (newInterestOps != oldInterestOps)
+                _desiredInterestOps = newInterestOps;
+        }
+
         if (LOG.isDebugEnabled())
-            LOG.debug("Key interests updated {} -> {} on {}", oldInterestOps, newInterestOps, this);
+            LOG.debug("changeInterests p={} {}->{} for {}", pending, oldInterestOps, newInterestOps, this);
+
+        if (!pending)
+            _selector.submit(_runUpdateKey);
     }
 
+
     @Override
     public void close()
     {
@@ -189,23 +297,22 @@
     @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.
         try
         {
-            boolean valid = _key!=null && _key.isValid();
+            boolean valid = _key != null && _key.isValid();
             int keyInterests = valid ? _key.interestOps() : -1;
             int keyReadiness = valid ? _key.readyOps() : -1;
-            return String.format("%s{io=%d,kio=%d,kro=%d}",
+            return String.format("%s{io=%d/%d,kio=%d,kro=%d}",
                     super.toString(),
-                    _interestOps.get(),
+                    _currentInterestOps,
+                    _desiredInterestOps,
                     keyInterests,
                     keyReadiness);
         }
-        catch (CancelledKeyException x)
+        catch (Throwable x)
         {
-            return String.format("%s{io=%s,kio=-2,kro=-2}", super.toString(), _interestOps.get());
+            return String.format("%s{io=%s,kio=-2,kro=-2}", super.toString(), _desiredInterestOps);
         }
     }
 }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java b/jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java
index 51615e6..06c4e5d 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java
@@ -18,36 +18,20 @@
 
 package org.eclipse.jetty.io;
 
-import java.io.Closeable;
 import java.io.IOException;
-import java.net.ConnectException;
+import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.SocketAddress;
-import java.net.SocketTimeoutException;
-import java.nio.channels.CancelledKeyException;
 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.Queue;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
 
-import org.eclipse.jetty.util.ConcurrentArrayQueue;
-import org.eclipse.jetty.util.TypeUtil;
-import org.eclipse.jetty.util.annotation.ManagedAttribute;
-import org.eclipse.jetty.util.component.AbstractLifeCycle;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.component.Dumpable;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.thread.NonBlockingThread;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
 import org.eclipse.jetty.util.thread.Scheduler;
 
 /**
@@ -56,19 +40,17 @@
  * <p>{@link SelectorManager} subclasses implement methods to return protocol-specific
  * {@link EndPoint}s and {@link Connection}s.</p>
  */
-public abstract class SelectorManager extends AbstractLifeCycle implements Dumpable
+public abstract class SelectorManager extends ContainerLifeCycle implements Dumpable
 {
-    public static final String SUBMIT_KEY_UPDATES = "org.eclipse.jetty.io.SelectorManager.submitKeyUpdates";
     public static final int DEFAULT_CONNECT_TIMEOUT = 15000;
     protected static final Logger LOG = Log.getLogger(SelectorManager.class);
-    private final static boolean __submitKeyUpdates = Boolean.valueOf(System.getProperty(SUBMIT_KEY_UPDATES, "true"));
 
     private final Executor executor;
     private final Scheduler scheduler;
     private final ManagedSelector[] _selectors;
     private long _connectTimeout = DEFAULT_CONNECT_TIMEOUT;
+    private ExecutionStrategy.Factory _executionFactory = ExecutionStrategy.Factory.getDefault();
     private long _selectorIndex;
-    private int _priorityDelta;
 
     protected SelectorManager(Executor executor, Scheduler scheduler)
     {
@@ -77,7 +59,7 @@
 
     protected SelectorManager(Executor executor, Scheduler scheduler, int selectors)
     {
-        if (selectors<=0)
+        if (selectors <= 0)
             throw new IllegalArgumentException("No selectors");
         this.executor = executor;
         this.scheduler = scheduler;
@@ -114,40 +96,41 @@
         _connectTimeout = milliseconds;
     }
 
-
-    @ManagedAttribute("The priority delta to apply to selector threads")
-    public int getSelectorPriorityDelta()
+    /**
+     * @return the {@link ExecutionStrategy.Factory} used by {@link ManagedSelector}
+     */
+    public ExecutionStrategy.Factory getExecutionStrategyFactory()
     {
-        return _priorityDelta;
+        return _executionFactory;
     }
 
     /**
-     * Sets the selector thread priority delta to the given amount.
-     * <p>This allows the selector threads to run at a different priority.
-     * Typically this would be used to lower the priority to give preference
-     * to handling previously accepted connections rather than accepting
-     * new connections.</p>
-     *
-     * @param selectorPriorityDelta the amount to change the thread priority
-     *                              delta to (may be negative)
-     * @see Thread#getPriority()
+     * @param _executionFactory the {@link ExecutionStrategy.Factory} used by {@link ManagedSelector}
      */
+    public void setExecutionStrategyFactory(ExecutionStrategy.Factory _executionFactory)
+    {
+        if (isRunning())
+            throw new IllegalStateException("Cannot change " + ExecutionStrategy.Factory.class.getSimpleName() + " after start()");
+        this._executionFactory = _executionFactory;
+    }
+
+    /**
+     * @return the selector priority delta
+     * @deprecated not implemented
+     */
+    @Deprecated
+    public int getSelectorPriorityDelta()
+    {
+        return 0;
+    }
+
+    /**
+     * @param selectorPriorityDelta the selector priority delta
+     * @deprecated not implemented
+     */
+    @Deprecated
     public void setSelectorPriorityDelta(int selectorPriorityDelta)
     {
-        int oldDelta = _priorityDelta;
-        _priorityDelta = selectorPriorityDelta;
-        if (oldDelta != selectorPriorityDelta && isStarted())
-        {
-            for (ManagedSelector selector : _selectors)
-            {
-                Thread thread = selector._thread;
-                if (thread != null)
-                {
-                    int deltaDiff = selectorPriorityDelta - oldDelta;
-                    thread.setPriority(Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY, thread.getPriority() - deltaDiff)));
-                }
-            }
-        }
     }
 
     /**
@@ -168,14 +151,45 @@
         return _selectors.length;
     }
 
-    private ManagedSelector chooseSelector()
+    private ManagedSelector chooseSelector(SocketChannel channel)
     {
+        // Ideally we would like to have all connections from the same client end
+        // up on the same selector (to try to avoid smearing the data from a single
+        // client over all cores), but because of proxies, the remote address may not
+        // really be the client - so we have to hedge our bets to ensure that all
+        // channels don't end up on the one selector for a proxy.
+        ManagedSelector candidate1 = null;
+        if (channel != null)
+        {
+            try
+            {
+                SocketAddress remote = channel.getRemoteAddress();
+                if (remote instanceof InetSocketAddress)
+                {
+                    byte[] addr = ((InetSocketAddress)remote).getAddress().getAddress();
+                    if (addr != null)
+                    {
+                        int s = addr[addr.length - 1] & 0xFF;
+                        candidate1 = _selectors[s % getSelectorCount()];
+                    }
+                }
+            }
+            catch (IOException x)
+            {
+                LOG.ignore(x);
+            }
+        }
+
         // 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 selectors.
         long s = _selectorIndex++;
         int index = (int)(s % getSelectorCount());
-        return _selectors[index];
+        ManagedSelector candidate2 = _selectors[index];
+
+        if (candidate1 == null || candidate1.size() >= candidate2.size() * 2)
+            return candidate2;
+        return candidate1;
     }
 
     /**
@@ -190,11 +204,12 @@
      */
     public void connect(SocketChannel channel, Object attachment)
     {
-        ManagedSelector set = chooseSelector();
+        ManagedSelector set = chooseSelector(channel);
         set.submit(set.new Connect(channel, attachment));
     }
 
     /**
+     * @param channel the channel to accept
      * @see #accept(SocketChannel, Object)
      */
     public void accept(SocketChannel channel)
@@ -209,12 +224,12 @@
      * just after a non-blocking connect via {@link SocketChannel#connect(SocketAddress)} that completed
      * successfully.</p>
      *
-     * @param channel the channel to register
+     * @param channel    the channel to register
      * @param attachment the attachment object
      */
     public void accept(SocketChannel channel, Object attachment)
     {
-        final ManagedSelector selector = chooseSelector();
+        final ManagedSelector selector = chooseSelector(channel);
         selector.submit(selector.new Accept(channel, attachment));
     }
 
@@ -228,7 +243,7 @@
      */
     public void acceptor(ServerSocketChannel server)
     {
-        final ManagedSelector selector = chooseSelector();
+        final ManagedSelector selector = chooseSelector(null);
         selector.submit(selector.new Acceptor(server));
     }
 
@@ -239,7 +254,7 @@
      * be overridden by subclasses if a server channel is provided.
      *
      * @param channel the
-     * @throws IOException
+     * @throws IOException if unable to accept channel
      */
     protected void accepted(SocketChannel channel) throws IOException
     {
@@ -249,14 +264,13 @@
     @Override
     protected void doStart() throws Exception
     {
-        super.doStart();
         for (int i = 0; i < _selectors.length; i++)
         {
             ManagedSelector selector = newSelector(i);
             _selectors[i] = selector;
-            selector.start();
-            execute(new NonBlockingThread(selector));
+            addBean(selector);
         }
+        super.doStart();
     }
 
     /**
@@ -267,15 +281,15 @@
      */
     protected ManagedSelector newSelector(int id)
     {
-        return new ManagedSelector(id);
+        return new ManagedSelector(this, id, getExecutionStrategyFactory());
     }
 
     @Override
     protected void doStop() throws Exception
     {
-        for (ManagedSelector selector : _selectors)
-            selector.stop();
         super.doStop();
+        for (ManagedSelector selector : _selectors)
+            removeBean(selector);
     }
 
     /**
@@ -345,8 +359,8 @@
      * <p>Callback method invoked when a non-blocking connect cannot be completed.</p>
      * <p>By default it just logs with level warning.</p>
      *
-     * @param channel the channel that attempted the connect
-     * @param ex the exception that caused the connect to fail
+     * @param channel    the channel that attempted the connect
+     * @param ex         the exception that caused the connect to fail
      * @param attachment the attachment object associated at registration
      */
     protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
@@ -359,14 +373,14 @@
      * <p>This method is invoked as a result of the registration of a channel via {@link #connect(SocketChannel, Object)}
      * or {@link #accept(SocketChannel)}.</p>
      *
-     * @param channel   the channel associated to the endpoint
-     * @param selector the selector the channel is registered to
-     * @param selectionKey      the selection key
+     * @param channel      the channel associated to the endpoint
+     * @param selector     the selector the channel is registered to
+     * @param selectionKey the selection key
      * @return a new endpoint
      * @throws IOException if the endPoint cannot be created
      * @see #newConnection(SocketChannel, EndPoint, Object)
      */
-    protected abstract EndPoint newEndPoint(SocketChannel channel, SelectorManager.ManagedSelector selector, SelectionKey selectionKey) throws IOException;
+    protected abstract EndPoint newEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey selectionKey) throws IOException;
 
     /**
      * <p>Factory method to create {@link Connection}.</p>
@@ -375,691 +389,8 @@
      * @param endpoint   the endpoint
      * @param attachment the attachment
      * @return a new connection
-     * @throws IOException
+     * @throws IOException if unable to create new connection
      * @see #newEndPoint(SocketChannel, ManagedSelector, SelectionKey)
      */
     public abstract Connection newConnection(SocketChannel channel, EndPoint endpoint, Object attachment) throws IOException;
-
-    @Override
-    public String dump()
-    {
-        return ContainerLifeCycle.dump(this);
-    }
-
-    @Override
-    public void dump(Appendable out, String indent) throws IOException
-    {
-        ContainerLifeCycle.dumpObject(out, this);
-        ContainerLifeCycle.dump(out, indent, TypeUtil.asList(_selectors));
-    }
-
-    private enum State
-    {
-        CHANGES, MORE_CHANGES, SELECT, WAKEUP, PROCESS
-    }
-
-    /**
-     * <p>{@link ManagedSelector} wraps a {@link Selector} simplifying non-blocking operations on channels.</p>
-     * <p>{@link ManagedSelector} runs the select loop, which waits on {@link Selector#select()} until events
-     * happen for registered channels. When events happen, it notifies the {@link EndPoint} associated
-     * with the channel.</p>
-     */
-    public class ManagedSelector extends AbstractLifeCycle implements Runnable, Dumpable
-    {
-        private final AtomicReference<State> _state= new AtomicReference<>(State.PROCESS);
-        private final Queue<Runnable> _changes = new ConcurrentArrayQueue<>();
-        private final int _id;
-        private Selector _selector;
-        private volatile Thread _thread;
-
-        public ManagedSelector(int id)
-        {
-            _id = id;
-            setStopTimeout(5000);
-        }
-
-        @Override
-        protected void doStart() throws Exception
-        {
-            super.doStart();
-            _selector = Selector.open();
-            _state.set(State.PROCESS);
-        }
-
-        @Override
-        protected void doStop() throws Exception
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Stopping {}", this);
-            Stop stop = new Stop();
-            submit(stop);
-            stop.await(getStopTimeout());
-            if (LOG.isDebugEnabled())
-                LOG.debug("Stopped {}", this);
-        }
-
-        /**
-         * Submit a task to update a selector key.  If the System property {@link SelectorManager#SUBMIT_KEY_UPDATES}
-         * is set true (default is false), the task is passed to {@link #submit(Runnable)}.   Otherwise it is run immediately and the selector
-         * woken up if need be.
-         * @param update the update to a key
-         */
-        public void updateKey(Runnable update)
-        {
-            if (__submitKeyUpdates)
-            {
-                submit(update);
-            }
-            else
-            {
-                // Run only 1 change at once
-                synchronized (this)
-                {
-                    runChange(update);
-                }
-                if (_state.compareAndSet(State.SELECT, State.WAKEUP))
-                   wakeup();
-            }
-        }
-
-        /**
-         * <p>Submits a change to be executed in the selector thread.</p>
-         * <p>Changes may be submitted from any thread, and the selector thread woken up
-         * (if necessary) to execute the change.</p>
-         *
-         * @param change the change to submit
-         */
-        public void submit(Runnable change)
-        {
-            // This method may be called from the selector thread, and therefore
-            // we could directly run the change without queueing, but this may
-            // lead to stack overflows on a busy server, so we always offer the
-            // change to the queue and process the state.
-
-            _changes.offer(change);
-            if (LOG.isDebugEnabled())
-                LOG.debug("Queued change {}", change);
-
-            out: while (true)
-            {
-                switch (_state.get())
-                {
-                    case SELECT:
-                        // Avoid multiple wakeup() calls if we the CAS fails
-                        if (!_state.compareAndSet(State.SELECT, State.WAKEUP))
-                            continue;
-                        wakeup();
-                        break out;
-                    case CHANGES:
-                        // Tell the selector thread that we have more changes.
-                        // If we fail to CAS, we possibly need to wakeup(), so loop.
-                        if (_state.compareAndSet(State.CHANGES, State.MORE_CHANGES))
-                            break out;
-                        continue;
-                    case WAKEUP:
-                        // Do nothing, we have already a wakeup scheduled
-                        break out;
-                    case MORE_CHANGES:
-                        // Do nothing, we already notified the selector thread of more changes
-                        break out;
-                    case PROCESS:
-                        // Do nothing, the changes will be run after the processing
-                        break out;
-                    default:
-                        throw new IllegalStateException();
-                }
-            }
-        }
-
-        private void runChanges()
-        {
-            Runnable change;
-            while ((change = _changes.poll()) != null)
-                runChange(change);
-        }
-
-        protected void runChange(Runnable change)
-        {
-            try
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Running change {}", change);
-                change.run();
-            }
-            catch (Throwable x)
-            {
-                LOG.debug("Could not run change " + change, x);
-            }
-        }
-
-        @Override
-        public void run()
-        {
-            _thread = Thread.currentThread();
-            String name = _thread.getName();
-            int priority = _thread.getPriority();
-            try
-            {
-                if (_priorityDelta != 0)
-                    _thread.setPriority(Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY, priority + _priorityDelta)));
-
-                _thread.setName(String.format("%s-selector-%s@%h/%d", name, SelectorManager.this.getClass().getSimpleName(), SelectorManager.this.hashCode(), _id));
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Starting {} on {}", _thread, this);
-                while (isRunning())
-                    select();
-                while(isStopping())
-                    runChanges();
-            }
-            finally
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Stopped {} on {}", _thread, this);
-                _thread.setName(name);
-                if (_priorityDelta != 0)
-                    _thread.setPriority(priority);
-            }
-        }
-
-        /**
-         * <p>Process changes and waits on {@link Selector#select()}.</p>
-         *
-         * @see #submit(Runnable)
-         */
-        public void select()
-        {
-            boolean debug = LOG.isDebugEnabled();
-            try
-            {
-                _state.set(State.CHANGES);
-
-                // Run the changes, and only exit if we ran all changes
-                out: while(true)
-                {
-                    switch (_state.get())
-                    {
-                        case CHANGES:
-                            runChanges();
-                            if (_state.compareAndSet(State.CHANGES, State.SELECT))
-                                break out;
-                            continue;
-                        case MORE_CHANGES:
-                            runChanges();
-                            _state.set(State.CHANGES);
-                            continue;
-                        default:
-                            throw new IllegalStateException();
-                    }
-                }
-                // Must check first for SELECT and *then* for WAKEUP
-                // because we read the state twice in the assert, and
-                // it could change from SELECT to WAKEUP in between.
-                assert _state.get() == State.SELECT || _state.get() == State.WAKEUP;
-
-                if (debug)
-                    LOG.debug("Selector loop waiting on select");
-                int selected = _selector.select();
-                if (debug)
-                    LOG.debug("Selector loop woken up from select, {}/{} selected", selected, _selector.keys().size());
-
-                _state.set(State.PROCESS);
-
-                Set<SelectionKey> selectedKeys = _selector.selectedKeys();
-                for (SelectionKey key : selectedKeys)
-                {
-                    if (key.isValid())
-                    {
-                        processKey(key);
-                    }
-                    else
-                    {
-                        if (debug)
-                            LOG.debug("Selector loop ignoring invalid key for channel {}", key.channel());
-                        Object attachment = key.attachment();
-                        if (attachment instanceof EndPoint)
-                            ((EndPoint)attachment).close();
-                    }
-                }
-                selectedKeys.clear();
-            }
-            catch (Throwable x)
-            {
-                if (isRunning())
-                    LOG.warn(x);
-                else
-                    LOG.ignore(x);
-            }
-        }
-
-        private void processKey(SelectionKey key)
-        {
-            Object attachment = key.attachment();
-            try
-            {
-                if (attachment instanceof SelectableEndPoint)
-                {
-                    ((SelectableEndPoint)attachment).onSelected();
-                }
-                else if (key.isConnectable())
-                {
-                    processConnect(key, (Connect)attachment);
-                }
-                else if (key.isAcceptable())
-                {
-                    processAccept(key);
-                }
-                else
-                {
-                    throw new IllegalStateException();
-                }
-            }
-            catch (CancelledKeyException x)
-            {
-                LOG.debug("Ignoring cancelled key for channel {}", key.channel());
-                if (attachment instanceof EndPoint)
-                    closeNoExceptions((EndPoint)attachment);
-            }
-            catch (Throwable x)
-            {
-                LOG.warn("Could not process key for channel " + key.channel(), x);
-                if (attachment instanceof EndPoint)
-                    closeNoExceptions((EndPoint)attachment);
-            }
-        }
-
-        private void processConnect(SelectionKey key, Connect connect)
-        {
-            SocketChannel channel = (SocketChannel)key.channel();
-            try
-            {
-                key.attach(connect.attachment);
-                boolean connected = finishConnect(channel);
-                if (connected)
-                {
-                    if (connect.timeout.cancel())
-                    {
-                        key.interestOps(0);
-                        EndPoint endpoint = createEndPoint(channel, key);
-                        key.attach(endpoint);
-                    }
-                    else
-                    {
-                        throw new SocketTimeoutException("Concurrent Connect Timeout");
-                    }
-                }
-                else
-                {
-                    throw new ConnectException();
-                }
-            }
-            catch (Throwable x)
-            {
-                connect.failed(x);
-            }
-        }
-
-        private void processAccept(SelectionKey key)
-        {
-            ServerSocketChannel server = (ServerSocketChannel)key.channel();
-            SocketChannel channel = null;
-            try
-            {
-                while ((channel = server.accept()) != null)
-                {
-                    accepted(channel);
-                }
-            }
-            catch (Throwable x)
-            {
-                closeNoExceptions(channel);
-                LOG.warn("Accept failed for channel " + channel, x);
-            }
-        }
-
-        private void closeNoExceptions(Closeable closeable)
-        {
-            try
-            {
-                if (closeable != null)
-                    closeable.close();
-            }
-            catch (Throwable x)
-            {
-                LOG.ignore(x);
-            }
-        }
-
-        public void wakeup()
-        {
-            _selector.wakeup();
-        }
-
-        public boolean isSelectorThread()
-        {
-            return Thread.currentThread() == _thread;
-        }
-
-        private EndPoint createEndPoint(SocketChannel channel, SelectionKey selectionKey) throws IOException
-        {
-            EndPoint endPoint = newEndPoint(channel, this, selectionKey);
-            endPointOpened(endPoint);
-            Connection connection = newConnection(channel, endPoint, selectionKey.attachment());
-            endPoint.setConnection(connection);
-            connectionOpened(connection);
-            if (LOG.isDebugEnabled())
-                LOG.debug("Created {}", endPoint);
-            return endPoint;
-        }
-
-        public void destroyEndPoint(EndPoint endPoint)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Destroyed {}", endPoint);
-            Connection connection = endPoint.getConnection();
-            if (connection != null)
-                connectionClosed(connection);
-            endPointClosed(endPoint);
-        }
-
-        @Override
-        public String dump()
-        {
-            return ContainerLifeCycle.dump(this);
-        }
-
-        @Override
-        public void dump(Appendable out, String indent) throws IOException
-        {
-            out.append(String.valueOf(this)).append(" id=").append(String.valueOf(_id)).append("\n");
-
-            Thread selecting = _thread;
-
-            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 && selector.isOpen())
-            {
-                final ArrayList<Object> dump = new ArrayList<>(selector.keys().size() * 2);
-                dump.add(where);
-
-                DumpKeys dumpKeys = new DumpKeys(dump);
-                submit(dumpKeys);
-                dumpKeys.await(5, TimeUnit.SECONDS);
-
-                ContainerLifeCycle.dump(out, indent, dump);
-            }
-        }
-
-        public void dumpKeysState(List<Object> dumps)
-        {
-            Selector selector = _selector;
-            Set<SelectionKey> keys = selector.keys();
-            dumps.add(selector + " keys=" + keys.size());
-            for (SelectionKey key : keys)
-            {
-                if (key.isValid())
-                    dumps.add(key.attachment() + " iOps=" + key.interestOps() + " rOps=" + key.readyOps());
-                else
-                    dumps.add(key.attachment() + " iOps=-1 rOps=-1");
-            }
-        }
-
-        @Override
-        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 class DumpKeys implements Runnable
-        {
-            private final CountDownLatch latch = new CountDownLatch(1);
-            private final List<Object> _dumps;
-
-            private DumpKeys(List<Object> dumps)
-            {
-                this._dumps = dumps;
-            }
-
-            @Override
-            public void run()
-            {
-                dumpKeysState(_dumps);
-                latch.countDown();
-            }
-
-            public boolean await(long timeout, TimeUnit unit)
-            {
-                try
-                {
-                    return latch.await(timeout, unit);
-                }
-                catch (InterruptedException x)
-                {
-                    return false;
-                }
-            }
-        }
-
-        private class Acceptor implements Runnable
-        {
-            private final ServerSocketChannel _channel;
-
-            public Acceptor(ServerSocketChannel channel)
-            {
-                this._channel = channel;
-            }
-
-            @Override
-            public void run()
-            {
-                try
-                {
-                    SelectionKey key = _channel.register(_selector, SelectionKey.OP_ACCEPT, null);
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("{} acceptor={}", this, key);
-                }
-                catch (Throwable x)
-                {
-                    closeNoExceptions(_channel);
-                    LOG.warn(x);
-                }
-            }
-        }
-
-        private class Accept implements Runnable
-        {
-            private final SocketChannel channel;
-            private final Object attachment;
-
-            private Accept(SocketChannel channel, Object attachment)
-            {
-                this.channel = channel;
-                this.attachment = attachment;
-            }
-
-            @Override
-            public void run()
-            {
-                try
-                {
-                    SelectionKey key = channel.register(_selector, 0, attachment);
-                    EndPoint endpoint = createEndPoint(channel, key);
-                    key.attach(endpoint);
-                }
-                catch (Throwable x)
-                {
-                    closeNoExceptions(channel);
-                    LOG.debug(x);
-                }
-            }
-        }
-
-        private class Connect implements Runnable
-        {
-            private final AtomicBoolean failed = new AtomicBoolean();
-            private final SocketChannel channel;
-            private final Object attachment;
-            private final Scheduler.Task timeout;
-
-            private Connect(SocketChannel channel, Object attachment)
-            {
-                this.channel = channel;
-                this.attachment = attachment;
-                this.timeout = scheduler.schedule(new ConnectTimeout(this), getConnectTimeout(), TimeUnit.MILLISECONDS);
-            }
-
-            @Override
-            public void run()
-            {
-                try
-                {
-                    channel.register(_selector, SelectionKey.OP_CONNECT, this);
-                }
-                catch (Throwable x)
-                {
-                    failed(x);
-                }
-            }
-
-            private void failed(Throwable failure)
-            {
-                if (failed.compareAndSet(false, true))
-                {
-                    timeout.cancel();
-                    closeNoExceptions(channel);
-                    connectionFailed(channel, failure, attachment);
-                }
-            }
-        }
-
-        private class ConnectTimeout implements Runnable
-        {
-            private final Connect connect;
-
-            private ConnectTimeout(Connect connect)
-            {
-                this.connect = connect;
-            }
-
-            @Override
-            public void run()
-            {
-                SocketChannel channel = connect.channel;
-                if (channel.isConnectionPending())
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Channel {} timed out while connecting, closing it", channel);
-                    connect.failed(new SocketTimeoutException("Connect Timeout"));
-                }
-            }
-        }
-
-        private class Stop implements Runnable
-        {
-            private final CountDownLatch latch = new CountDownLatch(1);
-
-            @Override
-            public void run()
-            {
-                try
-                {
-                    for (SelectionKey key : _selector.keys())
-                    {
-                        Object attachment = key.attachment();
-                        if (attachment instanceof EndPoint)
-                        {
-                            EndPointCloser closer = new EndPointCloser((EndPoint)attachment);
-                            execute(closer);
-                            // We are closing the SelectorManager, so we want to block the
-                            // selector thread here until we have closed all EndPoints.
-                            // This is different than calling close() directly, because close()
-                            // can wait forever, while here we are limited by the stop timeout.
-                            closer.await(getStopTimeout());
-                        }
-                    }
-
-                    closeNoExceptions(_selector);
-                }
-                finally
-                {
-                    latch.countDown();
-                }
-            }
-
-            public boolean await(long timeout)
-            {
-                try
-                {
-                    return latch.await(timeout, TimeUnit.MILLISECONDS);
-                }
-                catch (InterruptedException x)
-                {
-                    return false;
-                }
-            }
-        }
-
-        private class EndPointCloser implements Runnable
-        {
-            private final CountDownLatch latch = new CountDownLatch(1);
-            private final EndPoint endPoint;
-
-            private EndPointCloser(EndPoint endPoint)
-            {
-                this.endPoint = endPoint;
-            }
-
-            @Override
-            public void run()
-            {
-                try
-                {
-                    closeNoExceptions(endPoint.getConnection());
-                }
-                finally
-                {
-                    latch.countDown();
-                }
-            }
-
-            private boolean await(long timeout)
-            {
-                try
-                {
-                    return latch.await(timeout, TimeUnit.MILLISECONDS);
-                }
-                catch (InterruptedException x)
-                {
-                    return false;
-                }
-            }
-        }
-    }
-
-    /**
-     * A {@link SelectableEndPoint} is an {@link EndPoint} that wish to be notified of
-     * non-blocking events by the {@link ManagedSelector}.
-     */
-    public interface SelectableEndPoint extends EndPoint
-    {
-        /**
-         * <p>Callback method invoked when a read or write events has been detected by the {@link ManagedSelector}
-         * for this endpoint.</p>
-         */
-        void onSelected();
-    }
 }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedPrintWriter.java b/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedPrintWriter.java
deleted file mode 100644
index ae6a918..0000000
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedPrintWriter.java
+++ /dev/null
@@ -1,683 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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);
-        }
-
-        if (LOG.isDebugEnabled())
-            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();
-        }
-    }
-}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java
index 473e100..d337899 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java
@@ -37,10 +37,9 @@
 /**
  * A Utility class to help implement {@link EndPoint#write(Callback, ByteBuffer...)} by calling
  * {@link EndPoint#flush(ByteBuffer...)} until all content is written.
- * The abstract method {@link #onIncompleteFlushed()} is called when not all content has been written after a call to
- * flush and should organise for the {@link #completeWrite()} method to be called when a subsequent call to flush
+ * The abstract method {@link #onIncompleteFlush()} is called when not all content has been written after a call to
+ * flush and should organize for the {@link #completeWrite()} method to be called when a subsequent call to flush
  * should  be able to make more progress.
- * <p>
  */
 abstract public class WriteFlusher
 {
@@ -269,25 +268,37 @@
             if (_callback!=null)
                 _callback.succeeded();
         }
+
+        boolean isCallbackNonBlocking()
+        {
+            return _callback!=null && _callback.isNonBlocking();
+        }
+    }
+
+    public boolean isCallbackNonBlocking()
+    {
+        State s = _state.get();
+        return (s instanceof PendingState) && ((PendingState)s).isCallbackNonBlocking();
     }
 
     /**
      * Abstract call to be implemented by specific WriteFlushers. It should schedule a call to {@link #completeWrite()}
      * or {@link #onFail(Throwable)} when appropriate.
      */
-    abstract protected void onIncompleteFlushed();
+    abstract protected void onIncompleteFlush();
 
     /**
      * Tries to switch state to WRITING. If successful it writes the given buffers to the EndPoint. If state transition
      * fails it'll fail the callback.
      *
      * If not all buffers can be written in one go it creates a new <code>PendingState</code> object to preserve the state
-     * and then calls {@link #onIncompleteFlushed()}. The remaining buffers will be written in {@link #completeWrite()}.
+     * and then calls {@link #onIncompleteFlush()}. The remaining buffers will be written in {@link #completeWrite()}.
      *
      * If all buffers have been written it calls callback.complete().
      *
      * @param callback the callback to call on either failed or complete
      * @param buffers the buffers to flush to the endpoint
+     * @throws WritePendingException if unable to write due to prior pending write
      */
     public void write(Callback callback, ByteBuffer... buffers) throws WritePendingException
     {
@@ -308,7 +319,7 @@
                     LOG.debug("flushed incomplete");
                 PendingState pending=new PendingState(buffers, callback);
                 if (updateState(__WRITING,pending))
-                    onIncompleteFlushed();
+                    onIncompleteFlush();
                 else
                     fail(pending);
                 return;
@@ -336,7 +347,7 @@
 
 
     /**
-     * Complete a write that has not completed and that called {@link #onIncompleteFlushed()} to request a call to this
+     * Complete a write that has not completed and that called {@link #onIncompleteFlush()} to request a call to this
      * method when a call to {@link EndPoint#flush(ByteBuffer...)} is likely to be able to progress.
      *
      * It tries to switch from PENDING to COMPLETING. If state transition fails, then it does nothing as the callback
@@ -344,7 +355,7 @@
      * {@link #onFail(Throwable)} or {@link #onClose()}
      */
     public void completeWrite()
-    {         
+    {
         if (DEBUG)
             LOG.debug("completeWrite: {}", this);
 
@@ -371,7 +382,7 @@
                 if (buffers!=pending.getBuffers())
                     pending=new PendingState(buffers, pending._callback);
                 if (updateState(__COMPLETING,pending))
-                    onIncompleteFlushed();
+                    onIncompleteFlush();
                 else
                     fail(pending);
                 return;
@@ -393,11 +404,12 @@
         }
     }
 
-    /* ------------------------------------------------------------ */
-    /** Flush the buffers iteratively until no progress is made
+    /**
+     * Flushes the buffers iteratively until no progress is made.
+     *
      * @param buffers The buffers to flush
      * @return The unflushed buffers, or null if all flushed
-     * @throws IOException
+     * @throws IOException if unable to flush
      */
     protected ByteBuffer[] flush(ByteBuffer[] buffers) throws IOException
     {
@@ -407,12 +419,15 @@
             int before=buffers.length==0?0:buffers[0].remaining();
             boolean flushed=_endPoint.flush(buffers);
             int r=buffers.length==0?0:buffers[0].remaining();
-            
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("Flushed={} {}/{}+{} {}",flushed,before-r,before,buffers.length-1,this);
+
             if (flushed)
                 return null;
-            
+
             progress=before!=r;
-            
+
             int not_empty=0;
             while(r==0)
             {
@@ -428,14 +443,17 @@
 
             if (not_empty>0)
                 buffers=Arrays.copyOfRange(buffers,not_empty,buffers.length);
-        }        
-        
+        }
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("!fully flushed {}",this);
+
         // If buffers is null, then flush has returned false but has consumed all the data!
         // This is probably SSL being unable to flush the encrypted buffer, so return EMPTY_BUFFERS
         // and that will keep this WriteFlusher pending.
         return buffers==null?EMPTY_BUFFERS:buffers;
     }
-    
+
     /* ------------------------------------------------------------ */
     /** Notify the flusher of a failure
      * @param cause The cause of the failure
@@ -477,8 +495,6 @@
 
     public void onClose()
     {
-        if (_state.get()==__IDLE)
-            return;
         onFail(new ClosedChannelException());
     }
 
@@ -505,4 +521,23 @@
     {
         return String.format("WriteFlusher@%x{%s}", hashCode(), _state.get());
     }
+
+    public String toStateString()
+    {
+        switch(_state.get().getType())
+        {
+            case WRITING:
+                return "W";
+            case PENDING:
+                return "P";
+            case COMPLETING:
+                return "C";
+            case IDLE:
+                return "-";
+            case FAILED:
+                return "F";
+            default:
+                return "?";
+        }
+    }
 }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/ALPNProcessor.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/ALPNProcessor.java
new file mode 100644
index 0000000..f8fddc8
--- /dev/null
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/ALPNProcessor.java
@@ -0,0 +1,52 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.io.ssl;
+
+import java.util.List;
+
+import javax.net.ssl.SSLEngine;
+
+public interface ALPNProcessor
+{
+    public interface Server
+    {
+        public static final ALPNProcessor.Server NOOP = new ALPNProcessor.Server()
+        {
+        };
+
+        public default void configure(SSLEngine sslEngine)
+        {
+        }
+    }
+
+    public interface Client
+    {
+        public static final Client NOOP = new Client()
+        {
+        };
+
+        public default void configure(SSLEngine sslEngine, List<String> protocols)
+        {
+        }
+
+        public default void process(SSLEngine sslEngine)
+        {
+        }
+    }
+}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java
index 3467b4a..6066274 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java
@@ -26,11 +26,14 @@
 
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 
 public class SslClientConnectionFactory implements ClientConnectionFactory
 {
+    public static final String SSL_CONTEXT_FACTORY_CONTEXT_KEY = "ssl.context.factory";
     public static final String SSL_PEER_HOST_CONTEXT_KEY = "ssl.peer.host";
     public static final String SSL_PEER_PORT_CONTEXT_KEY = "ssl.peer.port";
     public static final String SSL_ENGINE_CONTEXT_KEY = "ssl.engine";
@@ -58,11 +61,13 @@
         context.put(SSL_ENGINE_CONTEXT_KEY, engine);
 
         SslConnection sslConnection = newSslConnection(byteBufferPool, executor, endPoint, engine);
-        sslConnection.setRenegotiationAllowed(sslContextFactory.isRenegotiationAllowed());
         endPoint.setConnection(sslConnection);
+
         EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
         appEndPoint.setConnection(connectionFactory.newConnection(appEndPoint, context));
 
+        customize(sslConnection, context);
+
         return sslConnection;
     }
 
@@ -70,4 +75,18 @@
     {
         return new SslConnection(byteBufferPool, executor, endPoint, engine);
     }
+
+    @Override
+    public Connection customize(Connection connection, Map<String, Object> context)
+    {
+        if (connection instanceof SslConnection)
+        {
+            SslConnection sslConnection = (SslConnection)connection;
+            sslConnection.setRenegotiationAllowed(sslContextFactory.isRenegotiationAllowed());
+            sslConnection.setRenegotiationLimit(sslContextFactory.getRenegotiationLimit());
+            ContainerLifeCycle connector = (ContainerLifeCycle)context.get(ClientConnectionFactory.CONNECTOR_CONTEXT_KEY);
+            connector.getBeans(SslHandshakeListener.class).forEach(sslConnection::addHandshakeListener);
+        }
+        return ClientConnectionFactory.super.customize(connection, context);
+    }
 }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
index a2790a6..b192a9d 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
@@ -21,7 +21,8 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.ClosedChannelException;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 import javax.net.ssl.SSLEngine;
@@ -29,6 +30,7 @@
 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
 import javax.net.ssl.SSLEngineResult.Status;
 import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
 
 import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.AbstractEndPoint;
@@ -36,8 +38,6 @@
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.io.EofException;
-import org.eclipse.jetty.io.FillInterest;
-import org.eclipse.jetty.io.RuntimeIOException;
 import org.eclipse.jetty.io.SelectChannelEndPoint;
 import org.eclipse.jetty.io.WriteFlusher;
 import org.eclipse.jetty.util.BufferUtil;
@@ -79,17 +79,19 @@
 public class SslConnection extends AbstractConnection
 {
     private static final Logger LOG = Log.getLogger(SslConnection.class);
-    private static final boolean DEBUG = LOG.isDebugEnabled(); // Easy for the compiler to remove the code if DEBUG==false
-    private static final ByteBuffer __FILL_CALLED_FLUSH= BufferUtil.allocate(0);
-    private static final ByteBuffer __FLUSH_CALLED_FILL= BufferUtil.allocate(0);
+
+    private final List<SslHandshakeListener> handshakeListeners = new ArrayList<>();
     private final ByteBufferPool _bufferPool;
     private final SSLEngine _sslEngine;
     private final DecryptedEndPoint _decryptedEndPoint;
     private ByteBuffer _decryptedInput;
     private ByteBuffer _encryptedInput;
     private ByteBuffer _encryptedOutput;
-    private final boolean _encryptedDirectBuffers = false;
+    private final boolean _encryptedDirectBuffers = true;
     private final boolean _decryptedDirectBuffers = false;
+    private boolean _renegotiationAllowed;
+    private int _renegotiationLimit = -1;
+    private boolean _closedOutbound;
     private final Runnable _runCompletWrite = new Runnable()
     {
         @Override
@@ -98,18 +100,55 @@
             _decryptedEndPoint.getWriteFlusher().completeWrite();
         }
     };
-    private boolean _renegotiationAllowed;
+    private final Runnable _runFillable = new Runnable()
+    {
+        @Override
+        public void run()
+        {
+            _decryptedEndPoint.getFillInterest().fillable();
+        }
+    };
+    private final Callback _nonBlockingReadCallback = new Callback.NonBlocking()
+    {
+        @Override
+        public void succeeded()
+        {
+            onFillable();
+        }
+
+        @Override
+        public void failed(final Throwable x)
+        {
+            onFillInterestedFailed(x);
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("SSLC.NBReadCB@%x{%s}", SslConnection.this.hashCode(),SslConnection.this);
+        }
+    };
 
     public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine)
     {
-        // This connection does not execute calls to onfillable, so they will be called by the selector thread.
-        // onfillable does not block and will only wakeup another thread to do the actual reading and handling.
-        super(endPoint, executor, !EXECUTE_ONFILLABLE);
+        // This connection does not execute calls to onFillable(), so they will be called by the selector thread.
+        // onFillable() does not block and will only wakeup another thread to do the actual reading and handling.
+        super(endPoint, executor);
         this._bufferPool = byteBufferPool;
         this._sslEngine = sslEngine;
         this._decryptedEndPoint = newDecryptedEndPoint();
     }
 
+    public void addHandshakeListener(SslHandshakeListener listener)
+    {
+        handshakeListeners.add(listener);
+    }
+
+    public boolean removeHandshakeListener(SslHandshakeListener listener)
+    {
+        return handshakeListeners.remove(listener);
+    }
+
     protected DecryptedEndPoint newDecryptedEndPoint()
     {
         return new DecryptedEndPoint();
@@ -132,24 +171,33 @@
 
     public void setRenegotiationAllowed(boolean renegotiationAllowed)
     {
-        this._renegotiationAllowed = renegotiationAllowed;
+        _renegotiationAllowed = renegotiationAllowed;
+    }
+
+    /**
+     * @return The number of renegotions allowed for this connection.  When the limit
+     * is 0 renegotiation will be denied. If the limit is less than 0 then no limit is applied. 
+     */
+    public int getRenegotiationLimit()
+    {
+        return _renegotiationLimit;
+    }
+
+    /**
+     * @param renegotiationLimit The number of renegotions allowed for this connection.  
+     * When the limit is 0 renegotiation will be denied. If the limit is less than 0 then no limit is applied.
+     * Default -1.
+     */
+    public void setRenegotiationLimit(int renegotiationLimit)
+    {
+        _renegotiationLimit = renegotiationLimit;
     }
 
     @Override
     public void onOpen()
     {
-        try
-        {
-            // Begin the handshake
-            _sslEngine.beginHandshake();
-            super.onOpen();
-            getDecryptedEndPoint().getConnection().onOpen();
-        }
-        catch (SSLException x)
-        {
-            getEndPoint().close();
-            throw new RuntimeIOException(x);
-        }
+        super.onOpen();
+        getDecryptedEndPoint().getConnection().onOpen();
     }
 
     @Override
@@ -166,6 +214,12 @@
     }
 
     @Override
+    public boolean onIdleExpired()
+    {
+        return getDecryptedEndPoint().getConnection().onIdleExpired();
+    }
+
+    @Override
     public void onFillable()
     {
         // onFillable means that there are encrypted bytes ready to be filled.
@@ -174,7 +228,7 @@
         // to do the fill and/or flush again and these calls will do the actually
         // filling.
 
-        if (DEBUG)
+        if (LOG.isDebugEnabled())
             LOG.debug("onFillable enter {}", _decryptedEndPoint);
 
         // We have received a close handshake, close the end point to send FIN.
@@ -186,16 +240,19 @@
         _decryptedEndPoint.getFillInterest().fillable();
 
         // If we are handshaking, then wake up any waiting write as well as it may have been blocked on the read
+        boolean runComplete = false;
         synchronized(_decryptedEndPoint)
         {
             if (_decryptedEndPoint._flushRequiresFillToProgress)
             {
                 _decryptedEndPoint._flushRequiresFillToProgress = false;
-                getExecutor().execute(_runCompletWrite);
+                runComplete = true;
             }
         }
+        if (runComplete)
+            _runCompletWrite.run();
 
-        if (DEBUG)
+        if (LOG.isDebugEnabled())
             LOG.debug("onFillable exit {}", _decryptedEndPoint);
     }
 
@@ -258,7 +315,7 @@
                 boolean fillable = false;
                 synchronized (DecryptedEndPoint.this)
                 {
-                    if (DEBUG)
+                    if (LOG.isDebugEnabled())
                         LOG.debug("write.complete {}", SslConnection.this.getEndPoint());
 
                     releaseEncryptedOutputBuffer();
@@ -273,7 +330,7 @@
                 }
                 if (fillable)
                     getFillInterest().fillable();
-                getExecutor().execute(_runCompletWrite);
+                _runCompletWrite.run();
             }
 
             @Override
@@ -282,64 +339,66 @@
                 // This means that a write of data has failed.  Writes are done
                 // only if there is an active writeflusher or a read needed to write
                 // data.  In either case the appropriate callback is passed on.
-                boolean fail_filler = false;
+                boolean fail_filler;
                 synchronized (DecryptedEndPoint.this)
                 {
-                    if (DEBUG)
-                        LOG.debug("{} write.failed", SslConnection.this, x);
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("write failed {}", SslConnection.this, x);
+
                     BufferUtil.clear(_encryptedOutput);
                     releaseEncryptedOutputBuffer();
 
                     _cannotAcceptMoreAppDataToFlush = false;
-
+                    fail_filler = _fillRequiresFlushToProgress;
                     if (_fillRequiresFlushToProgress)
-                    {
                         _fillRequiresFlushToProgress = false;
-                        fail_filler = true;
-                    }
                 }
 
-                final boolean filler_failed=fail_filler;
-
                 failedCallback(new Callback()
                 {
                     @Override
-                    public void succeeded()
-                    {
-                    }
-
-                    @Override
                     public void failed(Throwable x)
                     {
-                        if (filler_failed)
+                        if (fail_filler)
                             getFillInterest().onFail(x);
                         getWriteFlusher().onFail(x);
                     }
+                }, x);
+            }
 
-                },x);
+            @Override
+            public boolean isNonBlocking()
+            {
+                return getWriteFlusher().isCallbackNonBlocking();
             }
         };
 
         public DecryptedEndPoint()
         {
-            super(null,getEndPoint().getLocalAddress(), getEndPoint().getRemoteAddress());
-            setIdleTimeout(getEndPoint().getIdleTimeout());
+            // Disable idle timeout checking: no scheduler and -1 timeout for this instance.
+            super(null, getEndPoint().getLocalAddress(), getEndPoint().getRemoteAddress());
+            super.setIdleTimeout(-1);
         }
 
         @Override
-        protected FillInterest getFillInterest()
+        public long getIdleTimeout()
         {
-            return super.getFillInterest();
+            return getEndPoint().getIdleTimeout();
         }
 
         @Override
         public void setIdleTimeout(long idleTimeout)
         {
-            super.setIdleTimeout(idleTimeout);
             getEndPoint().setIdleTimeout(idleTimeout);
         }
 
         @Override
+        public boolean isOpen()
+        {
+            return getEndPoint().isOpen();
+        }
+
+        @Override
         protected WriteFlusher getWriteFlusher()
         {
             return super.getWriteFlusher();
@@ -353,23 +412,25 @@
             // OR if we are handshaking we need to read some encrypted data OR
             // if neither then we should just try the flush again.
             boolean try_again = false;
+            boolean write = false;
+            boolean need_fill_interest = false;
             synchronized (DecryptedEndPoint.this)
             {
-                if (DEBUG)
-                    LOG.debug("onIncompleteFlush {}", getEndPoint());
+                if (LOG.isDebugEnabled())
+                    LOG.debug("onIncompleteFlush {}", SslConnection.this);
                 // If we have pending output data,
                 if (BufferUtil.hasContent(_encryptedOutput))
                 {
                     // write it
                     _cannotAcceptMoreAppDataToFlush = true;
-                    getEndPoint().write(_writeCallback, _encryptedOutput);
+                    write = true;
                 }
                 // If we are handshaking and need to read,
                 else if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP)
                 {
                     // check if we are actually read blocked in order to write
-                    _flushRequiresFillToProgress = true;
-                    SslConnection.this.fillInterested();
+                    _flushRequiresFillToProgress = true; 
+                    need_fill_interest = !SslConnection.this.isFillInterested();
                 }
                 else
                 {
@@ -382,8 +443,11 @@
                 }
             }
 
-
-            if (try_again)
+            if (write)
+                getEndPoint().write(_writeCallback, _encryptedOutput);                
+            else if (need_fill_interest)
+                ensureFillInterested();
+            else if (try_again)
             {
                 // If the output is closed,
                 if (isOutputShutdown())
@@ -395,29 +459,30 @@
                 else
                 {
                     // try to flush what is pending
-                    // because this is a special case (see above) we could probably
-                    // avoid the dispatch, but best to be sure
+                    // execute to avoid recursion
                     getExecutor().execute(_runCompletWrite);
                 }
             }
         }
 
         @Override
-        protected boolean needsFill() throws IOException
+        protected void needsFillInterest() throws IOException
         {
             // This means that the decrypted data consumer has called the fillInterested
             // method on the DecryptedEndPoint, so we have to work out if there is
             // decrypted data to be filled or what callbacks to setup to be told when there
             // might be more encrypted data available to attempt another call to fill
-
+            boolean fillable;
+            boolean write = false;
             synchronized (DecryptedEndPoint.this)
             {
                 // Do we already have some app data, then app can fill now so return true
-                if (BufferUtil.hasContent(_decryptedInput))
-                    return true;
+                fillable = (BufferUtil.hasContent(_decryptedInput))
+                        // or if we have encryptedInput and have not underflowed yet, the it is worth trying a fill
+                        || BufferUtil.hasContent(_encryptedInput) && !_underFlown;
 
                 // If we have no encrypted data to decrypt OR we have some, but it is not enough
-                if (BufferUtil.isEmpty(_encryptedInput) || _underFlown)
+                if (!fillable)
                 {
                     // We are not ready to read data
 
@@ -431,31 +496,24 @@
                         {
                             // write it
                             _cannotAcceptMoreAppDataToFlush = true;
-                            getEndPoint().write(_writeCallback, _encryptedOutput);
+                            write = true;
                         }
                         else
                         {
                             // we have already written the net data
                             // pretend we are readable so the wrap is done by next readable callback
                             _fillRequiresFlushToProgress = false;
-                            return true;
+                            fillable=true;
                         }
                     }
-                    else
-                    {
-                        // Normal readable callback
-                        // Get called back on onfillable when then is more data to fill
-                        SslConnection.this.fillInterested();
-                    }
-
-                    return false;
-                }
-                else
-                {
-                    // We are ready to read data
-                    return true;
                 }
             }
+            if (write)
+                getEndPoint().write(_writeCallback, _encryptedOutput);
+            else if (fillable)
+                getExecutor().execute(_runFillable);
+            else 
+                ensureFillInterested();
         }
 
         @Override
@@ -476,100 +534,183 @@
         }
 
         @Override
-        public synchronized int fill(ByteBuffer buffer) throws IOException
+        public int fill(ByteBuffer buffer) throws IOException
         {
-            if (DEBUG)
-                LOG.debug("{} fill enter", SslConnection.this);
             try
             {
-                // Do we already have some decrypted data?
-                if (BufferUtil.hasContent(_decryptedInput))
-                    return BufferUtil.append(buffer,_decryptedInput);
-
-                // We will need a network buffer
-                if (_encryptedInput == null)
-                    _encryptedInput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
-                else
-                    BufferUtil.compact(_encryptedInput);
-
-                // We also need an app buffer, but can use the passed buffer if it is big enough
-                ByteBuffer app_in;
-                if (BufferUtil.space(buffer) > _sslEngine.getSession().getApplicationBufferSize())
-                    app_in = buffer;
-                else if (_decryptedInput == null)
-                    app_in = _decryptedInput = _bufferPool.acquire(_sslEngine.getSession().getApplicationBufferSize(), _decryptedDirectBuffers);
-                else
-                    app_in = _decryptedInput;
-
-                // loop filling and unwrapping until we have something
-                while (true)
+                synchronized (this)
                 {
-                    // Let's try reading some encrypted data... even if we have some already.
-                    int net_filled = getEndPoint().fill(_encryptedInput);
-                    if (DEBUG)
-                        LOG.debug("{} filled {} encrypted bytes", SslConnection.this, net_filled);
-
-                    decryption: while (true)
+                    Throwable failure = null;
+                    try
                     {
-                        // Let's unwrap even if we have no net data because in that
-                        // case we want to fall through to the handshake handling
-                        int pos = BufferUtil.flipToFill(app_in);
-                        SSLEngineResult unwrapResult;
-                        try
-                        {
-                            unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
-                        }
-                        finally
-                        {
-                            BufferUtil.flipToFlush(app_in, pos);
-                        }
-                        if (DEBUG)
-                            LOG.debug("{} unwrap {}", SslConnection.this, unwrapResult);
+                        // Do we already have some decrypted data?
+                        if (BufferUtil.hasContent(_decryptedInput))
+                            return BufferUtil.append(buffer,_decryptedInput);
 
-                        HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
-                        HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus();
-                        Status unwrapResultStatus = unwrapResult.getStatus();
+                        // We will need a network buffer
+                        if (_encryptedInput == null)
+                            _encryptedInput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
+                        else
+                            BufferUtil.compact(_encryptedInput);
 
-                        // Extra check on unwrapResultStatus == OK with zero length buffer is due
-                        // to SSL client on android (see bug #454773)
-                        _underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW || unwrapResultStatus == Status.OK && unwrapResult.bytesConsumed()==0 && unwrapResult.bytesProduced()==0;
+                        // We also need an app buffer, but can use the passed buffer if it is big enough
+                        ByteBuffer app_in;
+                        if (BufferUtil.space(buffer) > _sslEngine.getSession().getApplicationBufferSize())
+                            app_in = buffer;
+                        else if (_decryptedInput == null)
+                            app_in = _decryptedInput = _bufferPool.acquire(_sslEngine.getSession().getApplicationBufferSize(), _decryptedDirectBuffers);
+                        else
+                            app_in = _decryptedInput;
 
-                        if (_underFlown)
+                        // loop filling and unwrapping until we have something
+                        while (true)
                         {
-                            if (net_filled < 0)
-                                closeInbound();
-                            if (net_filled <= 0)
-                                return net_filled;
-                        }
+                            // Let's try reading some encrypted data... even if we have some already.
+                            int net_filled = getEndPoint().fill(_encryptedInput);
 
-                        switch (unwrapResultStatus)
-                        {
-                            case CLOSED:
+                            if (net_filled > 0 && !_handshaken && _sslEngine.isOutboundDone())
+                                throw new SSLHandshakeException("Closed during handshake");
+
+                            decryption: while (true)
                             {
-                                switch (handshakeStatus)
+                                // Let's unwrap even if we have no net data because in that
+                                // case we want to fall through to the handshake handling
+                                int pos = BufferUtil.flipToFill(app_in);
+                                SSLEngineResult unwrapResult;
+                                try
                                 {
-                                    case NOT_HANDSHAKING:
+                                    unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
+                                }
+                                finally
+                                {
+                                    BufferUtil.flipToFlush(app_in, pos);
+                                }
+                                if (LOG.isDebugEnabled())
+                                {
+                                    LOG.debug("net={} unwrap {} {}", net_filled, unwrapResult.toString().replace('\n',' '), SslConnection.this);
+                                    LOG.debug("filled {} {}",BufferUtil.toHexSummary(buffer), SslConnection.this);
+                                }
+
+                                HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
+                                HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus();
+                                Status unwrapResultStatus = unwrapResult.getStatus();
+
+                                // Extra check on unwrapResultStatus == OK with zero bytes consumed
+                                // or produced is due to an SSL client on Android (see bug #454773).
+                                _underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW ||
+                                        unwrapResultStatus == Status.OK && unwrapResult.bytesConsumed() == 0 && unwrapResult.bytesProduced() == 0;
+
+                                if (_underFlown)
+                                {
+                                    if (net_filled < 0)
+                                        closeInbound();
+                                    if (net_filled <= 0)
+                                        return net_filled;
+                                }
+
+                                switch (unwrapResultStatus)
+                                {
+                                    case CLOSED:
                                     {
-                                        // We were not handshaking, so just tell the app we are closed
-                                        return -1;
+                                        switch (handshakeStatus)
+                                        {
+                                            case NOT_HANDSHAKING:
+                                            {
+                                                // We were not handshaking, so just tell the app we are closed
+                                                return -1;
+                                            }
+                                            case NEED_TASK:
+                                            {
+                                                _sslEngine.getDelegatedTask().run();
+                                                continue;
+                                            }
+                                            case NEED_WRAP:
+                                            {
+                                                // We need to send some handshake data (probably the close handshake).
+                                                // We return -1 so that the application can drive the close by flushing
+                                                // or shutting down the output.
+                                                return -1;
+                                            }
+                                            case NEED_UNWRAP:
+                                            {
+                                                // We expected to read more, but we got closed.
+                                                // Return -1 to indicate to the application to drive the close.
+                                                return -1;
+                                            }
+                                            default:
+                                            {
+                                                throw new IllegalStateException();
+                                            }
+                                        }
                                     }
-                                    case NEED_TASK:
+                                    case BUFFER_UNDERFLOW:
+                                    case OK:
                                     {
-                                        _sslEngine.getDelegatedTask().run();
-                                        continue;
-                                    }
-                                    case NEED_WRAP:
-                                    {
-                                        // We need to send some handshake data (probably the close handshake).
-                                        // We return -1 so that the application can drive the close by flushing
-                                        // or shutting down the output.
-                                        return -1;
-                                    }
-                                    case NEED_UNWRAP:
-                                    {
-                                        // We expected to read more, but we got closed.
-                                        // Return -1 to indicate to the application to drive the close.
-                                        return -1;
+                                        if (unwrapHandshakeStatus == HandshakeStatus.FINISHED)
+                                            handshakeFinished();
+
+                                        // Check whether re-negotiation is allowed
+                                        if (!allowRenegotiate(handshakeStatus))
+                                            return -1;
+                                        
+                                        // If bytes were produced, don't bother with the handshake status;
+                                        // pass the decrypted data to the application, which will perform
+                                        // another call to fill() or flush().
+                                        if (unwrapResult.bytesProduced() > 0)
+                                        {
+                                            if (app_in == buffer)
+                                                return unwrapResult.bytesProduced();
+                                            return BufferUtil.append(buffer,_decryptedInput);
+                                        }
+
+                                        switch (handshakeStatus)
+                                        {
+                                            case NOT_HANDSHAKING:
+                                            {
+                                                if (_underFlown)
+                                                    break decryption;
+                                                continue;
+                                            }
+                                            case NEED_TASK:
+                                            {
+                                                _sslEngine.getDelegatedTask().run();
+                                                continue;
+                                            }
+                                            case NEED_WRAP:
+                                            {
+                                                // If we are called from flush()
+                                                // return to let it do the wrapping.
+                                                if (_flushRequiresFillToProgress)
+                                                    return 0;
+
+                                                _fillRequiresFlushToProgress = true;
+                                                flush(BufferUtil.EMPTY_BUFFER);
+                                                if (BufferUtil.isEmpty(_encryptedOutput))
+                                                {
+                                                    // The flush wrote all the encrypted bytes so continue to fill.
+                                                    _fillRequiresFlushToProgress = false;
+                                                    if (_underFlown)
+                                                        break decryption;
+                                                    continue;
+                                                }
+                                                else
+                                                {
+                                                    // The flush did not complete, return from fill()
+                                                    // and let the write completion mechanism to kick in.
+                                                    return 0;
+                                                }
+                                            }
+                                            case NEED_UNWRAP:
+                                            {
+                                                if (_underFlown)
+                                                    break decryption;
+                                                continue;
+                                            }
+                                            default:
+                                            {
+                                                throw new IllegalStateException();
+                                            }
+                                        }
                                     }
                                     default:
                                     {
@@ -577,118 +718,101 @@
                                     }
                                 }
                             }
-                            case BUFFER_UNDERFLOW:
-                            case OK:
-                            {
-                                if (unwrapHandshakeStatus == HandshakeStatus.FINISHED && !_handshaken)
-                                {
-                                    _handshaken = true;
-                                    if (DEBUG)
-                                        LOG.debug("{} {} handshake completed", SslConnection.this,
-                                                _sslEngine.getUseClientMode() ? "client-side" : "resumed session server-side");
-                                }
+                        }
+                    }
+                    catch (SSLHandshakeException x)
+                    {
+                        notifyHandshakeFailed(_sslEngine, x);
+                        failure = x;
+                        throw x;
+                    }
+                    catch (SSLException x)
+                    {
+                        if (!_handshaken)
+                        {
+                            x = (SSLException)new SSLHandshakeException(x.getMessage()).initCause(x);
+                            notifyHandshakeFailed(_sslEngine, x);
+                        }
+                        failure = x;
+                        throw x;
+                    }
+                    catch (Throwable x)
+                    {
+                        failure = x;
+                        throw x;
+                    }
+                    finally
+                    {
+                        // If we are handshaking, then wake up any waiting write as well as it may have been blocked on the read
+                        if (_flushRequiresFillToProgress)
+                        {
+                            _flushRequiresFillToProgress = false;
+                            getExecutor().execute(failure == null ? _runCompletWrite : new FailWrite(failure));
+                        }
 
-                                // Check whether renegotiation is allowed
-                                if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
-                                {
-                                    if (DEBUG)
-                                        LOG.debug("{} renegotiation denied", SslConnection.this);
-                                    closeInbound();
-                                    return -1;
-                                }
-
-                                // If bytes were produced, don't bother with the handshake status;
-                                // pass the decrypted data to the application, which will perform
-                                // another call to fill() or flush().
-                                if (unwrapResult.bytesProduced() > 0)
-                                {
-                                    if (app_in == buffer)
-                                        return unwrapResult.bytesProduced();
-                                    return BufferUtil.append(buffer,_decryptedInput);
-                                }
-
-                                switch (handshakeStatus)
-                                {
-                                    case NOT_HANDSHAKING:
-                                    {
-                                        if (_underFlown)
-                                            break decryption;
-                                        continue;
-                                    }
-                                    case NEED_TASK:
-                                    {
-                                        _sslEngine.getDelegatedTask().run();
-                                        continue;
-                                    }
-                                    case NEED_WRAP:
-                                    {
-                                        // If we are called from flush()
-                                        // return to let it do the wrapping.
-                                        if (buffer == __FLUSH_CALLED_FILL)
-                                            return 0;
-
-                                        _fillRequiresFlushToProgress = true;
-                                        flush(__FILL_CALLED_FLUSH);
-                                        if (BufferUtil.isEmpty(_encryptedOutput))
-                                        {
-                                            // The flush wrote all the encrypted bytes so continue to fill
-                                            _fillRequiresFlushToProgress = false;
-                                            continue;
-                                        }
-                                        else
-                                        {
-                                            // The flush did not complete, return from fill()
-                                            // and let the write completion mechanism to kick in.
-                                            return 0;
-                                        }
-                                    }
-                                    case NEED_UNWRAP:
-                                    {
-                                        if (_underFlown)
-                                            break decryption;
-                                        continue;
-                                    }
-                                    default:
-                                    {
-                                        throw new IllegalStateException();
-                                    }
-                                }
-                            }
-                            default:
-                            {
-                                throw new IllegalStateException();
-                            }
+                        if (_encryptedInput != null && !_encryptedInput.hasRemaining())
+                        {
+                            _bufferPool.release(_encryptedInput);
+                            _encryptedInput = null;
+                        }
+                        if (_decryptedInput != null && !_decryptedInput.hasRemaining())
+                        {
+                            _bufferPool.release(_decryptedInput);
+                            _decryptedInput = null;
                         }
                     }
                 }
             }
-            catch (Exception e)
+            catch (Throwable x)
             {
-                close();
-                throw e;
+                close(x);
+                throw x;
             }
-            finally
-            {
-                // If we are handshaking, then wake up any waiting write as well as it may have been blocked on the read
-                if (_flushRequiresFillToProgress)
-                {
-                    _flushRequiresFillToProgress = false;
-                    getExecutor().execute(_runCompletWrite);
-                }
+        }
 
-                if (_encryptedInput != null && !_encryptedInput.hasRemaining())
-                {
-                    _bufferPool.release(_encryptedInput);
-                    _encryptedInput = null;
-                }
-                if (_decryptedInput != null && !_decryptedInput.hasRemaining())
-                {
-                    _bufferPool.release(_decryptedInput);
-                    _decryptedInput = null;
-                }
-                if (DEBUG)
-                    LOG.debug("{} fill exit", SslConnection.this);
+        private void handshakeFinished()
+        {
+            if (_handshaken)
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Renegotiated {}", SslConnection.this);
+                if (_renegotiationLimit>0)
+                    _renegotiationLimit--;
             }
+            else
+            {
+                _handshaken = true;
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} handshake succeeded {}/{} {}",
+                        _sslEngine.getUseClientMode() ? "client" : "resumed server",
+                            _sslEngine.getSession().getProtocol(),_sslEngine.getSession().getCipherSuite(),
+                            SslConnection.this);
+                notifyHandshakeSucceeded(_sslEngine);
+            }
+        }
+
+        private boolean allowRenegotiate(HandshakeStatus handshakeStatus)
+        {   
+            if (!_handshaken || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING)
+                return true;
+
+            if (!isRenegotiationAllowed())
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Renegotiation denied {}", SslConnection.this);
+                closeInbound();
+                return false;
+            }
+            
+            if (_renegotiationLimit==0)
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Renegotiation limit exceeded {}", SslConnection.this);
+                closeInbound();
+                return false;
+            }
+            
+            return true;
         }
 
         private void closeInbound()
@@ -704,7 +828,7 @@
         }
 
         @Override
-        public synchronized boolean flush(ByteBuffer... appOuts) throws IOException
+        public boolean flush(ByteBuffer... appOuts) throws IOException
         {
             // The contract for flush does not require that all appOuts bytes are written
             // or even that any appOut bytes are written!  If the connection is write block
@@ -713,149 +837,162 @@
             // it is the applications responsibility to call flush again - either in a busy loop
             // or better yet by using EndPoint#write to do the flushing.
 
-            if (DEBUG)
-                LOG.debug("{} flush enter {}", SslConnection.this, Arrays.toString(appOuts));
-            int consumed=0;
+            if (LOG.isDebugEnabled())
+            {
+                for (ByteBuffer b : appOuts)
+                    LOG.debug("flush {} {}", BufferUtil.toHexSummary(b), SslConnection.this);
+            }
+
             try
             {
-                if (_cannotAcceptMoreAppDataToFlush)
+                synchronized (this)
                 {
-                    if (_sslEngine.isOutboundDone())
-                        throw new EofException(new ClosedChannelException());
-                    return false;
-                }
-
-                // We will need a network buffer
-                if (_encryptedOutput == null)
-                    _encryptedOutput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
-
-                while (true)
-                {
-                    // We call sslEngine.wrap to try to take bytes from appOut buffers and encrypt them into the _netOut buffer
-                    BufferUtil.compact(_encryptedOutput);
-                    int pos = BufferUtil.flipToFill(_encryptedOutput);
-                    SSLEngineResult wrapResult;
                     try
                     {
-                        wrapResult=_sslEngine.wrap(appOuts, _encryptedOutput);
+                        if (_cannotAcceptMoreAppDataToFlush)
+                        {
+                            if (_sslEngine.isOutboundDone())
+                                throw new EofException(new ClosedChannelException());
+                            return false;
+                        }
+
+                        // We will need a network buffer
+                        if (_encryptedOutput == null)
+                            _encryptedOutput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
+
+                        while (true)
+                        {
+                            // We call sslEngine.wrap to try to take bytes from appOut buffers and encrypt them into the _netOut buffer
+                            BufferUtil.compact(_encryptedOutput);
+                            int pos = BufferUtil.flipToFill(_encryptedOutput);
+                            SSLEngineResult wrapResult;
+                            try
+                            {
+                                wrapResult = _sslEngine.wrap(appOuts, _encryptedOutput);
+                            }
+                            finally
+                            {
+                                BufferUtil.flipToFlush(_encryptedOutput, pos);
+                            }
+                            if (LOG.isDebugEnabled())
+                                LOG.debug("wrap {} {}", wrapResult.toString().replace('\n',' '), SslConnection.this);
+
+                            Status wrapResultStatus = wrapResult.getStatus();
+
+                            boolean allConsumed=true;
+                            for (ByteBuffer b : appOuts)
+                                if (BufferUtil.hasContent(b))
+                                    allConsumed=false;
+
+                            // and deal with the results returned from the sslEngineWrap
+                            switch (wrapResultStatus)
+                            {
+                                case CLOSED:
+                                {
+                                    // The SSL engine has close, but there may be close handshake that needs to be written
+                                    if (BufferUtil.hasContent(_encryptedOutput))
+                                    {
+                                        _cannotAcceptMoreAppDataToFlush = true;
+                                        getEndPoint().flush(_encryptedOutput);
+                                        getEndPoint().shutdownOutput();
+                                        // If we failed to flush the close handshake then we will just pretend that
+                                        // the write has progressed normally and let a subsequent call to flush
+                                        // (or WriteFlusher#onIncompleteFlushed) to finish writing the close handshake.
+                                        // The caller will find out about the close on a subsequent flush or fill.
+                                        if (BufferUtil.hasContent(_encryptedOutput))
+                                            return false;
+                                    }
+                                    // otherwise we have written, and the caller will close the underlying connection
+                                    else
+                                    {
+                                        getEndPoint().shutdownOutput();
+                                    }
+                                    return allConsumed;
+                                }
+                                case BUFFER_UNDERFLOW:
+                                {
+                                    throw new IllegalStateException();
+                                }
+                                default:
+                                {
+                                    if (LOG.isDebugEnabled())
+                                        LOG.debug("wrap {} {} {}", wrapResultStatus, BufferUtil.toHexSummary(_encryptedOutput), SslConnection.this);
+
+                                    if (wrapResult.getHandshakeStatus() == HandshakeStatus.FINISHED)
+                                        handshakeFinished();
+
+                                    HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
+
+                                    // Check whether re-negotiation is allowed
+                                    if (!allowRenegotiate(handshakeStatus))
+                                    {
+                                        getEndPoint().shutdownOutput();
+                                        return allConsumed;
+                                    }
+                                    
+                                    // if we have net bytes, let's try to flush them
+                                    if (BufferUtil.hasContent(_encryptedOutput))
+                                        if (!getEndPoint().flush(_encryptedOutput))
+                                            getEndPoint().flush(_encryptedOutput); // one retry
+
+                                    // But we also might have more to do for the handshaking state.
+                                    switch (handshakeStatus)
+                                    {
+                                        case NOT_HANDSHAKING:
+                                            // If we have not consumed all and had just finished handshaking, then we may
+                                            // have just flushed the last handshake in the encrypted buffers, so we should
+                                            // try again.
+                                            if (!allConsumed && wrapResult.getHandshakeStatus()==HandshakeStatus.FINISHED && BufferUtil.isEmpty(_encryptedOutput))
+                                                continue;
+
+                                            // Return true if we consumed all the bytes and encrypted are all flushed
+                                            return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
+
+                                        case NEED_TASK:
+                                            // run the task and continue
+                                            _sslEngine.getDelegatedTask().run();
+                                            continue;
+
+                                        case NEED_WRAP:
+                                            // Hey we just wrapped! Oh well who knows what the sslEngine is thinking, so continue and we will wrap again
+                                            continue;
+
+                                        case NEED_UNWRAP:
+                                            // Ah we need to fill some data so we can write.
+                                            // So if we were not called from fill and the app is not reading anyway
+                                            if (!_fillRequiresFlushToProgress && !getFillInterest().isInterested())
+                                            {
+                                                // Tell the onFillable method that there might be a write to complete
+                                                _flushRequiresFillToProgress = true;
+                                                fill(BufferUtil.EMPTY_BUFFER);
+                                                // Check if after the fill() we need to wrap again
+                                                if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_WRAP)
+                                                    continue;
+                                            }
+                                            return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
+
+                                        case FINISHED:
+                                            throw new IllegalStateException();
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    catch (SSLHandshakeException x)
+                    {
+                        notifyHandshakeFailed(_sslEngine, x);
+                        throw x;
                     }
                     finally
                     {
-                        BufferUtil.flipToFlush(_encryptedOutput, pos);
-                    }
-
-                    if (DEBUG)
-                        LOG.debug("{} wrap {}", SslConnection.this, wrapResult);
-                    if (wrapResult.bytesConsumed()>0)
-                        consumed+=wrapResult.bytesConsumed();
-                    Status wrapResultStatus = wrapResult.getStatus();
-
-                    boolean allConsumed=true;
-                    for (ByteBuffer b : appOuts)
-                        if (BufferUtil.hasContent(b))
-                            allConsumed=false;
-
-                    // and deal with the results returned from the sslEngineWrap
-                    switch (wrapResultStatus)
-                    {
-                        case CLOSED:
-                            // The SSL engine has close, but there may be close handshake that needs to be written
-                            if (BufferUtil.hasContent(_encryptedOutput))
-                            {
-                                _cannotAcceptMoreAppDataToFlush = true;
-                                getEndPoint().flush(_encryptedOutput);
-                                getEndPoint().shutdownOutput();
-                                // If we failed to flush the close handshake then we will just pretend that
-                                // the write has progressed normally and let a subsequent call to flush
-                                // (or WriteFlusher#onIncompleteFlushed) to finish writing the close handshake.
-                                // The caller will find out about the close on a subsequent flush or fill.
-                                if (BufferUtil.hasContent(_encryptedOutput))
-                                    return false;
-                            }
-                            // otherwise we have written, and the caller will close the underlying connection
-                            else
-                            {
-                                getEndPoint().shutdownOutput();
-                            }
-                            return allConsumed;
-
-                        case BUFFER_UNDERFLOW:
-                            throw new IllegalStateException();
-
-                        default:
-                            if (DEBUG)
-                                LOG.debug("{} {} {}", this, wrapResultStatus, BufferUtil.toDetailString(_encryptedOutput));
-
-                            if (wrapResult.getHandshakeStatus() == HandshakeStatus.FINISHED && !_handshaken)
-                            {
-                                _handshaken = true;
-                                if (DEBUG)
-                                    LOG.debug("{} {} handshake completed", SslConnection.this, "server-side");
-                            }
-
-                            HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
-
-                            // Check whether renegotiation is allowed
-                            if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
-                            {
-                                if (DEBUG)
-                                    LOG.debug("{} renegotiation denied", SslConnection.this);
-                                getEndPoint().shutdownOutput();
-                                return allConsumed;
-                            }
-
-                            // if we have net bytes, let's try to flush them
-                            if (BufferUtil.hasContent(_encryptedOutput))
-                                if (!getEndPoint().flush(_encryptedOutput))
-                                    getEndPoint().flush(_encryptedOutput); // one retry
-
-                            // But we also might have more to do for the handshaking state.
-                            switch (handshakeStatus)
-                            {
-                                case NOT_HANDSHAKING:
-                                    // If we have not consumed all and had just finished handshaking, then we may
-                                    // have just flushed the last handshake in the encrypted buffers, so we should
-                                    // try again.
-                                    if (!allConsumed && wrapResult.getHandshakeStatus()==HandshakeStatus.FINISHED && BufferUtil.isEmpty(_encryptedOutput))
-                                        continue;
-
-                                    // Return true if we consumed all the bytes and encrypted are all flushed
-                                    return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
-
-                                case NEED_TASK:
-                                    // run the task and continue
-                                    _sslEngine.getDelegatedTask().run();
-                                    continue;
-
-                                case NEED_WRAP:
-                                    // Hey we just wrapped! Oh well who knows what the sslEngine is thinking, so continue and we will wrap again
-                                    continue;
-
-                                case NEED_UNWRAP:
-                                    // Ah we need to fill some data so we can write.
-                                    // So if we were not called from fill and the app is not reading anyway
-                                    if (appOuts[0]!=__FILL_CALLED_FLUSH && !getFillInterest().isInterested())
-                                    {
-                                        // Tell the onFillable method that there might be a write to complete
-                                        _flushRequiresFillToProgress = true;
-                                        fill(__FLUSH_CALLED_FILL);
-                                        // Check if after the fill() we need to wrap again
-                                        if (handshakeStatus == HandshakeStatus.NEED_WRAP)
-                                            continue;
-                                    }
-                                    return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
-
-                                case FINISHED:
-                                    throw new IllegalStateException();
-                            }
+                        releaseEncryptedOutputBuffer();
                     }
                 }
             }
-            finally
+            catch (Throwable x)
             {
-                if (DEBUG)
-                    LOG.debug("{} flush exit, consumed {}", SslConnection.this, consumed);
-                releaseEncryptedOutputBuffer();
+                close(x);
+                throw x;
             }
         }
 
@@ -873,31 +1010,55 @@
         @Override
         public void shutdownOutput()
         {
-            boolean ishut = isInputShutdown();
-            boolean oshut = isOutputShutdown();
-            if (DEBUG)
-                LOG.debug("{} shutdownOutput: oshut={}, ishut={}", SslConnection.this, oshut, ishut);
-            if (ishut)
+            try
             {
-                // Aggressively close, since inbound close alert has already been processed
-                // and the TLS specification allows to close the connection directly, which
-                // is what most other implementations expect: a FIN rather than a TLS close
-                // reply. If a TLS close reply is sent, most implementations send a RST.
+                boolean flush = false;
+                boolean close = false;
+                synchronized (_decryptedEndPoint)
+                {
+                    boolean ishut = isInputShutdown();
+                    boolean oshut = isOutputShutdown();
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("shutdownOutput: oshut={}, ishut={} {}", oshut, ishut, SslConnection.this);
+
+                    if (oshut)
+                        return;
+
+                    if (!_closedOutbound)
+                    {
+                        _closedOutbound=true; // Only attempt this once
+                        _sslEngine.closeOutbound();
+                        flush = true;
+                    }
+
+                    // TODO review close logic here
+                    if (ishut)
+                        close = true;
+                }
+
+                if (flush)
+                    flush(BufferUtil.EMPTY_BUFFER); // Send the TLS close message.
+                if (close)
+                    getEndPoint().close();
+                else
+                    ensureFillInterested();
+            }
+            catch (Throwable x)
+            {
+                LOG.ignore(x);
                 getEndPoint().close();
             }
-            else if (!oshut)
+        }
+        
+        private void ensureFillInterested()
+        {
+            if (getFillInterest().isCallbackNonBlocking())
             {
-                try
-                {
-                    _sslEngine.closeOutbound();
-                    flush(BufferUtil.EMPTY_BUFFER); // Send close handshake
-                    SslConnection.this.fillInterested(); // seek reply FIN or RST or close handshake
-                }
-                catch (Exception e)
-                {
-                    LOG.ignore(e);
-                    getEndPoint().close();
-                }
+                SslConnection.this.tryFillInterested(_nonBlockingReadCallback);
+            }
+            else
+            {
+                SslConnection.this.tryFillInterested();
             }
         }
 
@@ -910,16 +1071,18 @@
         @Override
         public void close()
         {
-            super.close();
-            // First send the TLS Close Alert, then the FIN
+            // First send the TLS Close Alert, then the FIN.
             shutdownOutput();
             getEndPoint().close();
+            super.close();
         }
 
-        @Override
-        public boolean isOpen()
+        protected void close(Throwable failure)
         {
-            return getEndPoint().isOpen();
+            // First send the TLS Close Alert, then the FIN.
+            shutdownOutput();
+            getEndPoint().close();
+            super.close(failure);
         }
 
         @Override
@@ -934,10 +1097,62 @@
             return _sslEngine.isInboundDone();
         }
 
+        private void notifyHandshakeSucceeded(SSLEngine sslEngine)
+        {
+            SslHandshakeListener.Event event = null;
+            for (SslHandshakeListener listener : handshakeListeners)
+            {
+                if (event == null)
+                    event = new SslHandshakeListener.Event(sslEngine);
+                try
+                {
+                    listener.handshakeSucceeded(event);
+                }
+                catch (Throwable x)
+                {
+                    LOG.info("Exception while notifying listener " + listener, x);
+                }
+            }
+        }
+
+        private void notifyHandshakeFailed(SSLEngine sslEngine, Throwable failure)
+        {
+            SslHandshakeListener.Event event = null;
+            for (SslHandshakeListener listener : handshakeListeners)
+            {
+                if (event == null)
+                    event = new SslHandshakeListener.Event(sslEngine);
+                try
+                {
+                    listener.handshakeFailed(event, failure);
+                }
+                catch (Throwable x)
+                {
+                    LOG.info("Exception while notifying listener " + listener, x);
+                }
+            }
+        }
+
         @Override
         public String toString()
         {
             return super.toString()+"->"+getEndPoint().toString();
         }
+
+        private class FailWrite implements Runnable
+        {
+            private final Throwable failure;
+
+            private FailWrite(Throwable failure)
+            {
+                this.failure = failure;
+            }
+
+            @Override
+            public void run()
+            {
+                getWriteFlusher().onFail(failure);
+            }
+        }
     }
 }
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslHandshakeListener.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslHandshakeListener.java
new file mode 100644
index 0000000..90e0192
--- /dev/null
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslHandshakeListener.java
@@ -0,0 +1,71 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.io.ssl;
+
+import java.util.EventListener;
+import java.util.EventObject;
+
+import javax.net.ssl.SSLEngine;
+
+/**
+ * <p>Implementations of this interface are notified of TLS handshake events.</p>
+ * <p>Similar to {@link javax.net.ssl.HandshakeCompletedListener}, but for {@link SSLEngine}.</p>
+ * <p>Typical usage if to add instances of this class as beans to a server connector, or
+ * to a client connector.</p>
+ */
+public interface SslHandshakeListener extends EventListener
+{
+    /**
+     * <p>Callback method invoked when the TLS handshake succeeds.</p>
+     *
+     * @param event the event object carrying information about the TLS handshake event
+     */
+    default void handshakeSucceeded(Event event)
+    {
+    }
+
+    /**
+     * <p>Callback method invoked when the TLS handshake fails.</p>
+     *
+     * @param event the event object carrying information about the TLS handshake event
+     * @param failure the failure that caused the TLS handshake to fail
+     */
+    default void handshakeFailed(Event event, Throwable failure)
+    {
+    }
+
+    /**
+     * <p>The event object carrying information about TLS handshake events.</p>
+     */
+    public static class Event extends EventObject
+    {
+        public Event(Object source)
+        {
+            super(source);
+        }
+
+        /**
+         * @return the SSLEngine associated to the TLS handshake event
+         */
+        public SSLEngine getSSLEngine()
+        {
+            return (SSLEngine)getSource();
+        }
+    }
+}
diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayByteBufferPoolTest.java
index 3690f94..70039cf 100644
--- a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayByteBufferPoolTest.java
+++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayByteBufferPoolTest.java
@@ -24,8 +24,12 @@
 import static org.junit.Assert.assertTrue;
 
 import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.concurrent.ConcurrentMap;
 
+import org.eclipse.jetty.io.ByteBufferPool.Bucket;
 import org.hamcrest.Matchers;
+import org.junit.Assert;
 import org.junit.Test;
 
 public class ArrayByteBufferPoolTest
@@ -34,7 +38,7 @@
     public void testMinimumRelease() throws Exception
     {
         ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(10,100,1000);
-        ArrayByteBufferPool.Bucket[] buckets = bufferPool.bucketsFor(true);
+        ByteBufferPool.Bucket[] buckets = bufferPool.bucketsFor(true);
 
         for (int size=1;size<=9;size++)
         {
@@ -42,13 +46,13 @@
 
             assertTrue(buffer.isDirect());
             assertEquals(size,buffer.capacity());
-            for (ArrayByteBufferPool.Bucket bucket : buckets)
-                assertTrue(bucket._queue.isEmpty());
+            for (ByteBufferPool.Bucket bucket : buckets)
+                assertTrue(bucket.isEmpty());
 
             bufferPool.release(buffer);
 
-            for (ArrayByteBufferPool.Bucket bucket : buckets)
-                assertTrue(bucket._queue.isEmpty());
+            for (ByteBufferPool.Bucket bucket : buckets)
+                assertTrue(bucket.isEmpty());
         }
     }
 
@@ -56,7 +60,7 @@
     public void testMaxRelease() throws Exception
     {
         ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(10,100,1000);
-        ArrayByteBufferPool.Bucket[] buckets = bufferPool.bucketsFor(true);
+        ByteBufferPool.Bucket[] buckets = bufferPool.bucketsFor(true);
 
         for (int size=999;size<=1001;size++)
         {
@@ -65,15 +69,15 @@
 
             assertTrue(buffer.isDirect());
             assertThat(buffer.capacity(),greaterThanOrEqualTo(size));
-            for (ArrayByteBufferPool.Bucket bucket : buckets)
-                assertTrue(bucket._queue.isEmpty());
+            for (ByteBufferPool.Bucket bucket : buckets)
+                assertTrue(bucket.isEmpty());
 
             bufferPool.release(buffer);
 
             int pooled=0;
-            for (ArrayByteBufferPool.Bucket bucket : buckets)
+            for (ByteBufferPool.Bucket bucket : buckets)
             {
-                pooled+=bucket._queue.size();
+                pooled+=bucket.size();
             }
             assertEquals(size<=1000,1==pooled);
         }
@@ -83,7 +87,7 @@
     public void testAcquireRelease() throws Exception
     {
         ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(10,100,1000);
-        ArrayByteBufferPool.Bucket[] buckets = bufferPool.bucketsFor(true);
+        ByteBufferPool.Bucket[] buckets = bufferPool.bucketsFor(true);
 
         for (int size=390;size<=510;size++)
         {
@@ -92,19 +96,19 @@
 
             assertTrue(buffer.isDirect());
             assertThat(buffer.capacity(), greaterThanOrEqualTo(size));
-            for (ArrayByteBufferPool.Bucket bucket : buckets)
-                assertTrue(bucket._queue.isEmpty());
+            for (ByteBufferPool.Bucket bucket : buckets)
+                assertTrue(bucket.isEmpty());
 
             bufferPool.release(buffer);
 
             int pooled=0;
-            for (ArrayByteBufferPool.Bucket bucket : buckets)
+            for (ByteBufferPool.Bucket bucket : buckets)
             {
-                if (!bucket._queue.isEmpty())
+                if (!bucket.isEmpty())
                 {
-                    pooled+=bucket._queue.size();
-                    assertThat(bucket._size,greaterThanOrEqualTo(size));
-                    assertThat(bucket._size,Matchers.lessThan(size+100));
+                    pooled+=bucket.size();
+                    // TODO assertThat(bucket._bufferSize,greaterThanOrEqualTo(size));
+                    // TODO assertThat(bucket._bufferSize,Matchers.lessThan(size+100));
                 }
             }
             assertEquals(1,pooled);
@@ -115,7 +119,7 @@
     public void testAcquireReleaseAcquire() throws Exception
     {
         ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(10,100,1000);
-        ArrayByteBufferPool.Bucket[] buckets = bufferPool.bucketsFor(true);
+        ByteBufferPool.Bucket[] buckets = bufferPool.bucketsFor(true);
 
         for (int size=390;size<=510;size++)
         {
@@ -128,13 +132,13 @@
             bufferPool.release(buffer3);
 
             int pooled=0;
-            for (ArrayByteBufferPool.Bucket bucket : buckets)
+            for (ByteBufferPool.Bucket bucket : buckets)
             {
-                if (!bucket._queue.isEmpty())
+                if (!bucket.isEmpty())
                 {
-                    pooled+=bucket._queue.size();
-                    assertThat(bucket._size,greaterThanOrEqualTo(size));
-                    assertThat(bucket._size,Matchers.lessThan(size+100));
+                    pooled+=bucket.size();
+                    // TODO assertThat(bucket._bufferSize,greaterThanOrEqualTo(size));
+                    // TODO assertThat(bucket._bufferSize,Matchers.lessThan(size+100));
                 }
             }
             assertEquals(1,pooled);
@@ -143,5 +147,29 @@
             assertTrue(buffer1!=buffer3);
         }
     }
+    
+
+    @Test
+    public void testMaxQueue() throws Exception
+    {
+        ArrayByteBufferPool bufferPool = new ArrayByteBufferPool(-1,-1,-1,2);
+
+        ByteBuffer buffer1 = bufferPool.acquire(512, false);
+        ByteBuffer buffer2 = bufferPool.acquire(512, false);
+        ByteBuffer buffer3 = bufferPool.acquire(512, false);
+
+        Bucket[] buckets = bufferPool.bucketsFor(false);
+        Arrays.asList(buckets).forEach(b->assertEquals(0,b.size()));
+        
+        bufferPool.release(buffer1);
+        Bucket bucket=Arrays.asList(buckets).stream().filter(b->b.size()>0).findFirst().get();
+        assertEquals(1, bucket.size());
+
+        bufferPool.release(buffer2);
+        assertEquals(2, bucket.size());
+        
+        bufferPool.release(buffer3);
+        assertEquals(2, bucket.size());
+    }
 
 }
diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/ByteArrayEndPointTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ByteArrayEndPointTest.java
index 35172ba..c1e039c 100644
--- a/jetty-io/src/test/java/org/eclipse/jetty/io/ByteArrayEndPointTest.java
+++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ByteArrayEndPointTest.java
@@ -18,17 +18,10 @@
 
 package org.eclipse.jetty.io;
 
-import static org.hamcrest.Matchers.*;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
 import org.eclipse.jetty.toolchain.test.AdvancedRunner;
@@ -42,6 +35,16 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 @RunWith(AdvancedRunner.class)
 public class ByteArrayEndPointTest
 {
@@ -64,7 +67,7 @@
     public void testFill() throws Exception
     {
         ByteArrayEndPoint endp = new ByteArrayEndPoint();
-        endp.setInput("test input");
+        endp.addInput("test input");
 
         ByteBuffer buffer = BufferUtil.allocate(1024);
 
@@ -73,13 +76,13 @@
 
         assertEquals(0,endp.fill(buffer));
 
-        endp.setInput(" more");
+        endp.addInput(" more");
         assertEquals(5,endp.fill(buffer));
         assertEquals("test input more",BufferUtil.toString(buffer));
 
         assertEquals(0,endp.fill(buffer));
 
-        endp.setInput((ByteBuffer)null);
+        endp.addInput((ByteBuffer)null);
 
         assertEquals(-1,endp.fill(buffer));
 
@@ -96,7 +99,7 @@
         }
 
         endp.reset();
-        endp.setInput("and more");
+        endp.addInput("and more");
         buffer = BufferUtil.allocate(4);
 
         assertEquals(4,endp.fill(buffer));
@@ -105,7 +108,6 @@
         BufferUtil.clear(buffer);
         assertEquals(4,endp.fill(buffer));
         assertEquals("more",BufferUtil.toString(buffer));
-
     }
 
     @Test
@@ -155,12 +157,13 @@
     public void testReadable() throws Exception
     {
         ByteArrayEndPoint endp = new ByteArrayEndPoint(_scheduler, 5000);
-        endp.setInput("test input");
+        endp.addInput("test input");
 
         ByteBuffer buffer = BufferUtil.allocate(1024);
         FutureCallback fcb = new FutureCallback();
 
         endp.fillInterested(fcb);
+        fcb.get(100,TimeUnit.MILLISECONDS);
         assertTrue(fcb.isDone());
         assertEquals(null, fcb.get());
         assertEquals(10, endp.fill(buffer));
@@ -168,10 +171,12 @@
 
         fcb = new FutureCallback();
         endp.fillInterested(fcb);
+        Thread.sleep(100);
         assertFalse(fcb.isDone());
         assertEquals(0, endp.fill(buffer));
 
-        endp.setInput(" more");
+        endp.addInput(" more");
+        fcb.get(1000,TimeUnit.MILLISECONDS);
         assertTrue(fcb.isDone());
         assertEquals(null, fcb.get());
         assertEquals(5, endp.fill(buffer));
@@ -179,16 +184,18 @@
 
         fcb = new FutureCallback();
         endp.fillInterested(fcb);
+        Thread.sleep(100);
         assertFalse(fcb.isDone());
         assertEquals(0, endp.fill(buffer));
 
-        endp.setInput((ByteBuffer)null);
+        endp.addInput((ByteBuffer)null);
         assertTrue(fcb.isDone());
         assertEquals(null, fcb.get());
         assertEquals(-1, endp.fill(buffer));
 
         fcb = new FutureCallback();
         endp.fillInterested(fcb);
+        fcb.get(1000,TimeUnit.MILLISECONDS);
         assertTrue(fcb.isDone());
         assertEquals(null, fcb.get());
         assertEquals(-1, endp.fill(buffer));
@@ -197,10 +204,9 @@
 
         fcb = new FutureCallback();
         endp.fillInterested(fcb);
-        assertTrue(fcb.isDone());
         try
         {
-            fcb.get();
+            fcb.get(1000,TimeUnit.MILLISECONDS);
             fail();
         }
         catch (ExecutionException e)
@@ -262,31 +268,34 @@
     @Test
     public void testIdle() throws Exception
     {
-        long idleTimeout = 500;
+        long idleTimeout = 1500;
+        long halfIdleTimeout = idleTimeout / 2;
+        long oneAndHalfIdleTimeout = idleTimeout + halfIdleTimeout;
+
         ByteArrayEndPoint endp = new ByteArrayEndPoint(_scheduler, idleTimeout);
-        endp.setInput("test");
         endp.setGrowOutput(false);
+        endp.addInput("test");
         endp.setOutput(BufferUtil.allocate(5));
 
-        // no idle check
         assertTrue(endp.isOpen());
-        Thread.sleep(idleTimeout * 2);
+        Thread.sleep(oneAndHalfIdleTimeout);
+        // Still open because it has not been oshut or closed explicitly
+        // and there are no callbacks, so idle timeout is ignored.
         assertTrue(endp.isOpen());
 
-        // normal read
+        // Normal read is immediate, since there is data to read.
         ByteBuffer buffer = BufferUtil.allocate(1024);
         FutureCallback fcb = new FutureCallback();
-
         endp.fillInterested(fcb);
+        fcb.get(idleTimeout, TimeUnit.MILLISECONDS);
         assertTrue(fcb.isDone());
-        assertEquals(null, fcb.get());
         assertEquals(4, endp.fill(buffer));
         assertEquals("test", BufferUtil.toString(buffer));
 
-        // read timeout
+        // Wait for a read timeout.
         fcb = new FutureCallback();
         endp.fillInterested(fcb);
-        long start = System.currentTimeMillis();
+        long start = System.nanoTime();
         try
         {
             fcb.get();
@@ -296,7 +305,7 @@
         {
             assertThat(t.getCause(), instanceOf(TimeoutException.class));
         }
-        assertThat(System.currentTimeMillis() - start, greaterThan(idleTimeout / 2));
+        assertThat(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start), greaterThan(halfIdleTimeout));
         assertThat("Endpoint open", endp.isOpen(), is(true));
 
         // We need to delay the write timeout test below from the read timeout test above.
@@ -304,12 +313,12 @@
         // because of the read timeout above runs concurrently with the write below, and
         // if it runs just after the write below, the test fails because the write callback
         // below fails immediately rather than after the idle timeout.
-        Thread.sleep(idleTimeout / 2);
+        Thread.sleep(halfIdleTimeout);
 
-        // write timeout
+        // Write more than the output capacity, then wait for idle timeout.
         fcb = new FutureCallback();
         endp.write(fcb, BufferUtil.toBuffer("This is too long"));
-        start = System.currentTimeMillis();
+        start = System.nanoTime();
         try
         {
             fcb.get();
@@ -319,20 +328,20 @@
         {
             assertThat(t.getCause(), instanceOf(TimeoutException.class));
         }
-        assertThat(System.currentTimeMillis() - start, greaterThan(idleTimeout / 2));
+        assertThat(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start), greaterThan(halfIdleTimeout));
+        // Still open because it has not been oshut or closed explicitly.
         assertThat("Endpoint open", endp.isOpen(), is(true));
 
+        // Make sure the endPoint is closed when the callback fails.
         endp.fillInterested(new Closer(endp));
-        
-        // Still no idle close (wait half the time)
-        Thread.sleep(idleTimeout / 2);
+        Thread.sleep(halfIdleTimeout);
+        // Still open because it has not been oshut or closed explicitly.
         assertThat("Endpoint open", endp.isOpen(), is(true));
 
-        // shutdown out
+        // Shutdown output.
         endp.shutdownOutput();
 
-        // idle close (wait double the time)
-        Thread.sleep(idleTimeout * 2);
-        assertThat("Endpoint open", endp.isOpen(), is(false));
+        Thread.sleep(idleTimeout);
+        assertThat("Endpoint closed", endp.isOpen(), is(false));
     }
 }
diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java
index 19fa64b..4665f71 100644
--- a/jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java
+++ b/jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java
@@ -18,11 +18,6 @@
 
 package org.eclipse.jetty.io;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -43,7 +38,6 @@
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
-import org.eclipse.jetty.io.ClientConnectionFactory.Helper;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.toolchain.test.OS;
 import org.eclipse.jetty.util.BufferUtil;
@@ -51,6 +45,11 @@
 import org.junit.Assert;
 import org.junit.Test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 public class IOTest
 {
     @Test
@@ -437,9 +436,9 @@
         connector.bind(null);
         InetSocketAddress addr=(InetSocketAddress)connector.getLocalAddress();
         Future<AsynchronousSocketChannel> acceptor = connector.accept();
-        
+
         AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
-        
+
         client.connect(new InetSocketAddress("127.0.0.1",addr.getPort())).get(5, TimeUnit.SECONDS);
 
         AsynchronousSocketChannel server = acceptor.get(5, TimeUnit.SECONDS);
@@ -457,14 +456,14 @@
 
         Assert.assertEquals(ByteBuffer.wrap(data), read);
     }
-    
+
     @Test
     public void testGatherWrite() throws Exception
     {
         File dir = MavenTestingUtils.getTargetTestingDir();
         if (!dir.exists())
             dir.mkdir();
-        
+
         File file = File.createTempFile("test",".txt",dir);
         file.deleteOnExit();
         FileChannel out = FileChannel.open(file.toPath(),
@@ -472,7 +471,7 @@
                 StandardOpenOption.READ,
                 StandardOpenOption.WRITE,
                 StandardOpenOption.DELETE_ON_CLOSE);
-        
+
         ByteBuffer[] buffers = new ByteBuffer[4096];
         long expected=0;
         for (int i=0;i<buffers.length;i++)
@@ -480,9 +479,9 @@
             buffers[i]=BufferUtil.toBuffer(i);
             expected+=buffers[i].remaining();
         }
-        
+
         long wrote = IO.write(out,buffers,0,buffers.length);
-        
+
         assertEquals(expected,wrote);
 
         for (int i=0;i<buffers.length;i++)
diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/MappedByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/MappedByteBufferPoolTest.java
index a0e18e5..c0c5b46 100644
--- a/jetty-io/src/test/java/org/eclipse/jetty/io/MappedByteBufferPoolTest.java
+++ b/jetty-io/src/test/java/org/eclipse/jetty/io/MappedByteBufferPoolTest.java
@@ -27,12 +27,11 @@
 import static org.junit.Assert.fail;
 
 import java.nio.ByteBuffer;
-import java.util.Queue;
 import java.util.concurrent.ConcurrentMap;
 
+import org.eclipse.jetty.io.ByteBufferPool.Bucket;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.StringUtil;
-import org.hamcrest.Matchers;
 import org.junit.Test;
 
 public class MappedByteBufferPoolTest
@@ -41,56 +40,62 @@
     public void testAcquireRelease() throws Exception
     {
         MappedByteBufferPool bufferPool = new MappedByteBufferPool();
-        ConcurrentMap<Integer,Queue<ByteBuffer>> buffers = bufferPool.buffersFor(true);
+        ConcurrentMap<Integer,Bucket> buckets = bufferPool.bucketsFor(true);
 
         int size = 512;
         ByteBuffer buffer = bufferPool.acquire(size, true);
 
         assertTrue(buffer.isDirect());
         assertThat(buffer.capacity(), greaterThanOrEqualTo(size));
-        assertTrue(buffers.isEmpty());
+        assertTrue(buckets.isEmpty());
 
         bufferPool.release(buffer);
 
-        assertEquals(1, buffers.size());
+        assertEquals(1, buckets.size());
+        assertEquals(1, buckets.values().iterator().next().size());
     }
 
     @Test
     public void testAcquireReleaseAcquire() throws Exception
     {
         MappedByteBufferPool bufferPool = new MappedByteBufferPool();
-        ConcurrentMap<Integer,Queue<ByteBuffer>> buffers = bufferPool.buffersFor(false);
+        ConcurrentMap<Integer,Bucket> buckets = bufferPool.bucketsFor(false);
 
         ByteBuffer buffer1 = bufferPool.acquire(512, false);
         bufferPool.release(buffer1);
         ByteBuffer buffer2 = bufferPool.acquire(512, false);
 
         assertSame(buffer1, buffer2);
+        assertEquals(1, buckets.size());
+        assertEquals(0, buckets.values().iterator().next().size());
 
         bufferPool.release(buffer2);
 
-        assertEquals(1, buffers.size());
+        assertEquals(1, buckets.size());
+        assertEquals(1, buckets.values().iterator().next().size());
     }
 
     @Test
     public void testAcquireReleaseClear() throws Exception
     {
         MappedByteBufferPool bufferPool = new MappedByteBufferPool();
-        ConcurrentMap<Integer,Queue<ByteBuffer>> buffers = bufferPool.buffersFor(true);
+        ConcurrentMap<Integer,Bucket> buckets = bufferPool.bucketsFor(true);
 
         ByteBuffer buffer = bufferPool.acquire(512, true);
         bufferPool.release(buffer);
 
-        assertEquals(1, buffers.size());
+        assertEquals(1, buckets.size());
+        assertEquals(1, buckets.values().iterator().next().size());
 
         bufferPool.clear();
 
-        assertTrue(buffers.isEmpty());
+        assertTrue(buckets.isEmpty());
     }
     
     /**
      * In a scenario where MappedByteBufferPool is being used improperly, such as releasing a buffer that wasn't created/acquired by the MappedByteBufferPool,
      * an assertion is tested for.
+     * @throws Exception test failure
      */
     @Test
     public void testReleaseAssertion() throws Exception
@@ -128,4 +133,30 @@
         buffer = pool.acquire(1024,false);
         assertThat(BufferUtil.toDetailString(buffer),containsString("@T00000002"));
     }
+    
+
+
+    @Test
+    public void testMaxQueue() throws Exception
+    {
+        MappedByteBufferPool bufferPool = new MappedByteBufferPool(-1,2);
+        ConcurrentMap<Integer,Bucket> buckets = bufferPool.bucketsFor(false);
+
+        ByteBuffer buffer1 = bufferPool.acquire(512, false);
+        ByteBuffer buffer2 = bufferPool.acquire(512, false);
+        ByteBuffer buffer3 = bufferPool.acquire(512, false);
+        assertEquals(0, buckets.size());
+
+        bufferPool.release(buffer1);
+        assertEquals(1, buckets.size());
+        Bucket bucket=buckets.values().iterator().next();
+        assertEquals(1, bucket.size());
+
+        bufferPool.release(buffer2);
+        assertEquals(2, bucket.size());
+        
+        bufferPool.release(buffer3);
+        assertEquals(2, bucket.size());
+
+    }
 }
diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointSslTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointSslTest.java
index c7e5e93..acc9b94 100644
--- a/jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointSslTest.java
+++ b/jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointSslTest.java
@@ -77,6 +77,7 @@
         engine.setUseClientMode(false);
         SslConnection sslConnection = new SslConnection(__byteBufferPool, _threadPool, endpoint, engine);
         sslConnection.setRenegotiationAllowed(__sslCtxFactory.isRenegotiationAllowed());
+        sslConnection.setRenegotiationLimit(__sslCtxFactory.getRenegotiationLimit());
         Connection appConnection = super.newConnection(channel,sslConnection.getDecryptedEndPoint());
         sslConnection.getDecryptedEndPoint().setConnection(appConnection);
         return sslConnection;
@@ -225,6 +226,7 @@
         filled=client.read(sslIn);
         Assert.assertEquals(-1,filled);
 
+        Thread.sleep(100); // TODO This should not be needed
         Assert.assertFalse(server.isOpen());
     }
 
diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointTest.java
index 2a6ebb5..2412d12 100644
--- a/jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointTest.java
+++ b/jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointTest.java
@@ -36,20 +36,26 @@
 import java.nio.channels.ServerSocketChannel;
 import java.nio.channels.SocketChannel;
 import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.FutureCallback;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.eclipse.jetty.util.thread.Scheduler;
 import org.eclipse.jetty.util.thread.TimerScheduler;
+import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 public class SelectChannelEndPointTest
@@ -116,13 +122,22 @@
 
     public class TestConnection extends AbstractConnection
     {
+        volatile FutureCallback _blockingRead;
         ByteBuffer _in = BufferUtil.allocate(32 * 1024);
         ByteBuffer _out = BufferUtil.allocate(32 * 1024);
         long _last = -1;
+        final CountDownLatch _latch;
 
         public TestConnection(EndPoint endp)
         {
             super(endp, _threadPool);
+            _latch=null;
+        }
+        
+        public TestConnection(EndPoint endp,CountDownLatch latch)
+        {
+            super(endp, _threadPool);
+            _latch=latch;
         }
 
         @Override
@@ -133,8 +148,41 @@
         }
 
         @Override
-        public synchronized void onFillable()
+        public void onFillInterestedFailed(Throwable cause)
         {
+            Callback blocking = _blockingRead;
+            if (blocking!=null)
+            {
+                _blockingRead=null;
+                blocking.failed(cause);
+                return;
+            }
+            super.onFillInterestedFailed(cause);
+        }
+        
+        @Override
+        public void onFillable()
+        {
+            if (_latch!=null)
+            {
+                try
+                {
+                    _latch.await();
+                }
+                catch (InterruptedException e)
+                {
+                    e.printStackTrace();
+                }
+            }
+            
+            Callback blocking = _blockingRead;
+            if (blocking!=null)
+            {
+                _blockingRead=null;
+                blocking.succeeded();
+                return;
+            }
+            
             EndPoint _endp = getEndPoint();
             try
             {
@@ -145,6 +193,7 @@
                     progress = false;
 
                     // Fill the input buffer with everything available
+                    BufferUtil.compact(_in);
                     if (BufferUtil.isFull(_in))
                         throw new IllegalStateException("FULL " + BufferUtil.toDetailString(_in));
                     int filled = _endp.fill(_in);
@@ -154,9 +203,9 @@
                     // If the tests wants to block, then block
                     while (_blockAt > 0 && _endp.isOpen() && _in.remaining() < _blockAt)
                     {
-                        FutureCallback blockingRead = new FutureCallback();
-                        fillInterested(blockingRead);
-                        blockingRead.get();
+                        FutureCallback future = _blockingRead = new FutureCallback();
+                        fillInterested();
+                        future.get();
                         filled = _endp.fill(_in);
                         progress |= filled > 0;
                     }
@@ -183,6 +232,9 @@
                     if (_endp.isInputShutdown())
                         _endp.shutdownOutput();
                 }
+
+                if (_endp.isOpen())
+                    fillInterested();
             }
             catch (ExecutionException e)
             {
@@ -209,8 +261,6 @@
             }
             finally
             {
-                if (_endp.isOpen())
-                    fillInterested();
             }
         }
     }
@@ -643,4 +693,132 @@
         }
         assertFalse(server.isOpen());
     }
+    
+
+    // TODO make this test reliable
+    @Test
+    @Ignore
+    public void testRejectedExecution() throws Exception
+    {
+        _manager.stop();
+        _threadPool.stop();
+        
+        final CountDownLatch latch = new CountDownLatch(1);
+        
+        BlockingQueue<Runnable> q = new ArrayBlockingQueue<>(4);
+        _threadPool = new QueuedThreadPool(4,4,60000,q);
+        _manager = new SelectorManager(_threadPool, _scheduler, 1)
+        {
+            @Override
+            public Connection newConnection(SocketChannel channel, EndPoint endpoint, Object attachment)
+            {
+                return new TestConnection(endpoint,latch);
+            }
+
+            @Override
+            protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey selectionKey) throws IOException
+            {
+                SelectChannelEndPoint endp = new SelectChannelEndPoint(channel, selectSet, selectionKey, getScheduler(), 60000);
+                _lastEndPoint = endp;
+                _lastEndPointLatch.countDown();
+                return endp;
+            }
+        };
+        
+        _threadPool.start();
+        _manager.start();
+        
+        AtomicInteger timeout = new AtomicInteger();
+        AtomicInteger rejections = new AtomicInteger();
+        AtomicInteger echoed = new AtomicInteger();
+        
+        CountDownLatch closed = new CountDownLatch(20);
+        for (int i=0;i<20;i++)
+        {
+            new Thread()
+            {
+                public void run()
+                {
+                    try(Socket client = newClient();)
+                    {
+                        client.setSoTimeout(5000);
+
+                        SocketChannel server = _connector.accept();
+                        server.configureBlocking(false);
+
+                        _manager.accept(server);
+
+                        // Write client to server
+                        client.getOutputStream().write("HelloWorld".getBytes(StandardCharsets.UTF_8));
+                        client.getOutputStream().flush();
+                        client.shutdownOutput();
+
+                        // Verify echo server to client
+                        for (char c : "HelloWorld".toCharArray())
+                        {
+                            int b = client.getInputStream().read();
+                            assertTrue(b > 0);
+                            assertEquals(c, (char)b);
+                        }
+                        assertEquals(-1,client.getInputStream().read());
+                        echoed.incrementAndGet();
+                    }
+                    catch(SocketTimeoutException x)
+                    {
+                        x.printStackTrace();
+                        timeout.incrementAndGet();
+                    }
+                    catch(Throwable x)
+                    {
+                        rejections.incrementAndGet();
+                    }
+                    finally
+                    {
+                        closed.countDown();
+                    }
+                }
+            }.start();
+        }
+
+        // unblock the handling
+        latch.countDown();
+        
+        // wait for all clients to complete or fail
+        closed.await();
+        
+        // assert some clients must have been rejected
+        Assert.assertThat(rejections.get(),Matchers.greaterThan(0));
+        // but not all of them
+        Assert.assertThat(rejections.get(),Matchers.lessThan(20));
+        // none should have timed out
+        Assert.assertThat(timeout.get(),Matchers.equalTo(0));
+        // and the rest should have worked
+        Assert.assertThat(echoed.get(),Matchers.equalTo(20-rejections.get())); 
+        
+        // and the selector is still working for new requests
+        try(Socket client = newClient();)
+        {
+            client.setSoTimeout(5000);
+
+            SocketChannel server = _connector.accept();
+            server.configureBlocking(false);
+
+            _manager.accept(server);
+
+            // Write client to server
+            client.getOutputStream().write("HelloWorld".getBytes(StandardCharsets.UTF_8));
+            client.getOutputStream().flush();
+            client.shutdownOutput();
+
+            // Verify echo server to client
+            for (char c : "HelloWorld".toCharArray())
+            {
+                int b = client.getInputStream().read();
+                assertTrue(b > 0);
+                assertEquals(c, (char)b);
+            }
+            assertEquals(-1,client.getInputStream().read());
+        }
+        
+    }
 }
diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/SelectorManagerTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/SelectorManagerTest.java
index 59aa5f2..198e00a 100644
--- a/jetty-io/src/test/java/org/eclipse/jetty/io/SelectorManagerTest.java
+++ b/jetty-io/src/test/java/org/eclipse/jetty/io/SelectorManagerTest.java
@@ -120,7 +120,7 @@
             long timeout = connectTimeout * 2;
             timeoutConnection.set(timeout);
             final CountDownLatch latch1 = new CountDownLatch(1);
-            selectorManager.connect(client1, new Callback.Adapter()
+            selectorManager.connect(client1, new Callback()
             {
                 @Override
                 public void failed(Throwable x)
@@ -141,7 +141,7 @@
                 client2.connect(address);
                 timeoutConnection.set(0);
                 final CountDownLatch latch2 = new CountDownLatch(1);
-                selectorManager.connect(client2, new Callback.Adapter()
+                selectorManager.connect(client2, new Callback()
                 {
                     @Override
                     public void succeeded()
diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java
index fdee04e..5bcb14d 100644
--- a/jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java
+++ b/jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java
@@ -33,9 +33,9 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLSocket;
 
-import org.eclipse.jetty.io.SelectorManager.ManagedSelector;
 import org.eclipse.jetty.io.ssl.SslConnection;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.util.BufferUtil;
@@ -81,6 +81,7 @@
             engine.setUseClientMode(false);
             SslConnection sslConnection = new SslConnection(__byteBufferPool, getExecutor(), endpoint, engine);
             sslConnection.setRenegotiationAllowed(__sslCtxFactory.isRenegotiationAllowed());
+            sslConnection.setRenegotiationLimit(__sslCtxFactory.getRenegotiationLimit());
             Connection appConnection = new TestConnection(sslConnection.getDecryptedEndPoint());
             sslConnection.getDecryptedEndPoint().setConnection(appConnection);
             return sslConnection;
@@ -150,6 +151,8 @@
         _threadPool.start();
         _scheduler.start();
         _manager.start();
+        __sslCtxFactory.setRenegotiationAllowed(true);
+        __sslCtxFactory.setRenegotiationLimit(-1);
 
     }
 
@@ -170,7 +173,7 @@
 
         public TestConnection(EndPoint endp)
         {
-            super(endp, _threadPool,true);
+            super(endp, _threadPool);
         }
 
         @Override
@@ -249,7 +252,7 @@
             }
         }
     }
-    protected Socket newClient() throws IOException
+    protected SSLSocket newClient() throws IOException
     {
         SSLSocket socket = __sslCtxFactory.newSslSocket();
         socket.connect(_connector.socket().getLocalSocketAddress());
@@ -277,11 +280,116 @@
         len=5;
         while(len>0)
             len-=client.getInputStream().read(buffer);
-        Assert.assertEquals(1, _dispatches.get());
 
         client.close();
     }
 
+    @Test
+    public void testRenegotiate() throws Exception
+    {
+        SSLSocket client = newClient();
+        client.setSoTimeout(60000);
+
+        SocketChannel server = _connector.accept();
+        server.configureBlocking(false);
+        _manager.accept(server);
+
+        client.getOutputStream().write("Hello".getBytes(StandardCharsets.UTF_8));
+        byte[] buffer = new byte[1024];
+        int len=client.getInputStream().read(buffer);
+        Assert.assertEquals(5, len);
+        Assert.assertEquals("Hello",new String(buffer,0,len,StandardCharsets.UTF_8));
+
+        client.startHandshake();
+        
+        client.getOutputStream().write("World".getBytes(StandardCharsets.UTF_8));
+        len=client.getInputStream().read(buffer);
+        Assert.assertEquals(5, len);
+        Assert.assertEquals("World",new String(buffer,0,len,StandardCharsets.UTF_8));
+
+        client.close();
+    }
+
+    @Test
+    public void testRenegotiateNotAllowed() throws Exception
+    {
+        __sslCtxFactory.setRenegotiationAllowed(false);
+        
+        SSLSocket client = newClient();
+        client.setSoTimeout(60000);
+
+        SocketChannel server = _connector.accept();
+        server.configureBlocking(false);
+        _manager.accept(server);
+
+        client.getOutputStream().write("Hello".getBytes(StandardCharsets.UTF_8));
+        byte[] buffer = new byte[1024];
+        int len=client.getInputStream().read(buffer);
+        Assert.assertEquals(5, len);
+        Assert.assertEquals("Hello",new String(buffer,0,len,StandardCharsets.UTF_8));
+
+        client.startHandshake();
+        
+        client.getOutputStream().write("World".getBytes(StandardCharsets.UTF_8));
+        try
+        {
+            client.getInputStream().read(buffer);
+            Assert.fail();
+        }
+        catch(SSLException e)
+        {
+            // expected
+        }
+    }
+
+    @Test
+    public void testRenegotiateLimit() throws Exception
+    {
+        __sslCtxFactory.setRenegotiationAllowed(true);
+        __sslCtxFactory.setRenegotiationLimit(2);
+        
+        SSLSocket client = newClient();
+        client.setSoTimeout(60000);
+
+        SocketChannel server = _connector.accept();
+        server.configureBlocking(false);
+        _manager.accept(server);
+
+        client.getOutputStream().write("Good".getBytes(StandardCharsets.UTF_8));
+        byte[] buffer = new byte[1024];
+        int len=client.getInputStream().read(buffer);
+        Assert.assertEquals(4, len);
+        Assert.assertEquals("Good",new String(buffer,0,len,StandardCharsets.UTF_8));
+        
+        client.startHandshake();
+
+        client.getOutputStream().write("Bye".getBytes(StandardCharsets.UTF_8));
+        len=client.getInputStream().read(buffer);
+        Assert.assertEquals(3, len);
+        Assert.assertEquals("Bye",new String(buffer,0,len,StandardCharsets.UTF_8));
+        
+        client.startHandshake();
+
+        client.getOutputStream().write("Cruel".getBytes(StandardCharsets.UTF_8));
+        len=client.getInputStream().read(buffer);
+        Assert.assertEquals(5, len);
+        Assert.assertEquals("Cruel",new String(buffer,0,len,StandardCharsets.UTF_8));
+
+        client.startHandshake();
+        
+        client.getOutputStream().write("World".getBytes(StandardCharsets.UTF_8));
+        try
+        {
+            client.getInputStream().read(buffer);
+            Assert.fail();
+        }
+        catch(SSLException e)
+        {
+            // expected
+        }
+    }
+    
+    
 
     @Test
     public void testWriteOnConnect() throws Exception
@@ -309,7 +417,7 @@
     public void testBlockedWrite() throws Exception
     {
         Socket client = newClient();
-        client.setSoTimeout(60000);
+        client.setSoTimeout(5000);
 
         SocketChannel server = _connector.accept();
         server.configureBlocking(false);
diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java
index 7944323..ae037ff 100644
--- a/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java
+++ b/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java
@@ -18,125 +18,93 @@
 
 package org.eclipse.jetty.io;
 
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.when;
-
 import java.io.IOException;
+import java.io.InterruptedIOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.WritePendingException;
-import java.nio.charset.StandardCharsets;
-import java.security.SecureRandom;
 import java.util.Arrays;
-import java.util.concurrent.Callable;
+import java.util.Random;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Exchanger;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 
-import org.eclipse.jetty.util.BlockingCallback;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.FutureCallback;
 import org.hamcrest.Matchers;
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.mockito.stubbing.Answer;
 
-@RunWith(MockitoJUnitRunner.class)
 public class WriteFlusherTest
 {
-    private final AtomicBoolean _flushIncomplete = new AtomicBoolean(false);
-    private final ExecutorService executor = Executors.newFixedThreadPool(16);
-    @Mock
-    private EndPoint _endPointMock;
-    private WriteFlusher _flusher;
-    private ByteArrayEndPoint _endp;
-
-    @Before
-    public void before()
+    @Test
+    public void testCompleteNoBlocking() throws Exception
     {
-        _endp = new ByteArrayEndPoint(new byte[]{}, 10);
-        _flushIncomplete.set(false);
-        _flusher = new WriteFlusher(_endp)
-        {
-            @Override
-            protected void onIncompleteFlushed()
-            {
-                _flushIncomplete.set(true);
-            }
-        };
+        testCompleteWrite(false);
     }
 
     @Test
     public void testIgnorePreviousFailures() throws Exception
     {
-        _endp.setGrowOutput(true);
+        testCompleteWrite(true);
+    }
+
+    private void testCompleteWrite(boolean failBefore) throws Exception
+    {
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint(new byte[0], 16);
+        endPoint.setGrowOutput(true);
+
+        AtomicBoolean incompleteFlush = new AtomicBoolean();
+        WriteFlusher flusher = new WriteFlusher(endPoint)
+        {
+            @Override
+            protected void onIncompleteFlush()
+            {
+                incompleteFlush.set(true);
+            }
+        };
+
+        if (failBefore)
+            flusher.onFail(new IOException("Ignored because no operation in progress"));
 
         FutureCallback callback = new FutureCallback();
-        _flusher.onFail(new IOException("Ignored because no operation in progress"));
-        _flusher.write(callback, BufferUtil.toBuffer("How "), BufferUtil.toBuffer("now "), BufferUtil.toBuffer("brown "), BufferUtil.toBuffer("cow!"));
-        assertCallbackIsDone(callback);
-        assertFlushIsComplete();
-        assertThat("context and callback.get() are equal",callback.get() , equalTo(null));
-        assertThat("string in endpoint matches expected string", "How now brown cow!",
-                equalTo(_endp.takeOutputString()));
-        assertTrue(_flusher.isIdle());
-    }
+        flusher.write(callback, BufferUtil.toBuffer("How "), BufferUtil.toBuffer("now "), BufferUtil.toBuffer("brown "), BufferUtil.toBuffer("cow!"));
 
-    @Test
-    public void testCompleteNoBlocking() throws Exception
-    {
-        _endp.setGrowOutput(true);
-
-        FutureCallback callback = new FutureCallback();
-        _flusher.write(callback, BufferUtil.toBuffer("How "), BufferUtil.toBuffer("now "), BufferUtil.toBuffer("brown "), BufferUtil.toBuffer("cow!"));
-        assertCallbackIsDone(callback);
-        assertFlushIsComplete();
-        assertThat("context and callback.get() are equal", callback.get(), equalTo(null));
-        assertThat("string in endpoint matches expected string", "How now brown cow!",
-                equalTo(_endp.takeOutputString()));
-        assertTrue(_flusher.isIdle());
-    }
-
-    private void assertFlushIsComplete()
-    {
-        assertThat("flush is complete", _flushIncomplete.get(), is(false));
-    }
-
-    private void assertCallbackIsDone(FutureCallback callback)
-    {
-        assertThat("callback is done", callback.isDone(), is(true));
+        Assert.assertTrue(callback.isDone());
+        Assert.assertFalse(incompleteFlush.get());
+        Assert.assertEquals("How now brown cow!", endPoint.takeOutputString());
+        Assert.assertTrue(flusher.isIdle());
     }
 
     @Test
     public void testClosedNoBlocking() throws Exception
     {
-        _endp.close();
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint(new byte[0], 16);
+        endPoint.close();
+
+        AtomicBoolean incompleteFlush = new AtomicBoolean();
+        WriteFlusher flusher = new WriteFlusher(endPoint)
+        {
+            @Override
+            protected void onIncompleteFlush()
+            {
+                incompleteFlush.set(true);
+            }
+        };
 
         FutureCallback callback = new FutureCallback();
-        _flusher.write(callback, BufferUtil.toBuffer("How "), BufferUtil.toBuffer("now "), BufferUtil.toBuffer("brown "), BufferUtil.toBuffer("cow!"));
-        assertCallbackIsDone(callback);
-        assertFlushIsComplete();
+        flusher.write(callback, BufferUtil.toBuffer("foo"));
+
+        Assert.assertTrue(callback.isDone());
+        Assert.assertFalse(incompleteFlush.get());
+
         try
         {
-            assertEquals(callback.get(),null);
+            callback.get();
             Assert.fail();
         }
         catch (ExecutionException e)
@@ -145,67 +113,88 @@
             Assert.assertTrue(cause instanceof IOException);
             Assert.assertThat(cause.getMessage(), Matchers.containsString("CLOSED"));
         }
-        assertEquals("", _endp.takeOutputString());
-        assertTrue(_flusher.isIdle());
+        Assert.assertEquals("", endPoint.takeOutputString());
+        Assert.assertTrue(flusher.isIdle());
     }
 
-
     @Test
     public void testCompleteBlocking() throws Exception
     {
-        FutureCallback callback = new FutureCallback();
-        _flusher.write(callback, BufferUtil.toBuffer("How "), BufferUtil.toBuffer("now "), BufferUtil.toBuffer("brown "), BufferUtil.toBuffer("cow!"));
-        assertFalse(callback.isDone());
-        assertFalse(callback.isCancelled());
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint(new byte[0], 10);
 
-        assertTrue(_flushIncomplete.get());
+        AtomicBoolean incompleteFlush = new AtomicBoolean();
+        WriteFlusher flusher = new WriteFlusher(endPoint)
+        {
+            @Override
+            protected void onIncompleteFlush()
+            {
+                incompleteFlush.set(true);
+            }
+        };
+
+        FutureCallback callback = new FutureCallback();
+        flusher.write(callback, BufferUtil.toBuffer("How now brown cow!"));
+
+        Assert.assertFalse(callback.isDone());
+        Assert.assertFalse(callback.isCancelled());
+
+        Assert.assertTrue(incompleteFlush.get());
+
         try
         {
-            assertEquals(callback.get(10, TimeUnit.MILLISECONDS),null);
+            callback.get(100, TimeUnit.MILLISECONDS);
             Assert.fail();
         }
-        catch (TimeoutException to)
+        catch (TimeoutException x)
         {
-            _flushIncomplete.set(false);
+            incompleteFlush.set(false);
         }
 
-        assertEquals("How now br", _endp.takeOutputString());
-        _flusher.completeWrite();
-        assertCallbackIsDone(callback);
-        assertEquals(callback.get(),null);
-        assertEquals("own cow!", _endp.takeOutputString());
-        assertFlushIsComplete();
-        assertTrue(_flusher.isIdle());
+        Assert.assertEquals("How now br", endPoint.takeOutputString());
+
+        flusher.completeWrite();
+
+        Assert.assertTrue(callback.isDone());
+        Assert.assertEquals("own cow!", endPoint.takeOutputString());
+        Assert.assertFalse(incompleteFlush.get());
+        Assert.assertTrue(flusher.isIdle());
     }
 
     @Test
     public void testCloseWhileBlocking() throws Exception
     {
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint(new byte[0], 10);
+
+        AtomicBoolean incompleteFlush = new AtomicBoolean();
+        WriteFlusher flusher = new WriteFlusher(endPoint)
+        {
+            @Override
+            protected void onIncompleteFlush()
+            {
+                incompleteFlush.set(true);
+            }
+        };
+
         FutureCallback callback = new FutureCallback();
-        _flusher.write(callback, BufferUtil.toBuffer("How "), BufferUtil.toBuffer("now "), BufferUtil.toBuffer("brown "), BufferUtil.toBuffer("cow!"));
+        flusher.write(callback, BufferUtil.toBuffer("How now brown cow!"));
 
-        assertFalse(callback.isDone());
-        assertFalse(callback.isCancelled());
+        Assert.assertFalse(callback.isDone());
+        Assert.assertFalse(callback.isCancelled());
 
-        assertTrue(_flushIncomplete.get());
+        Assert.assertTrue(incompleteFlush.get());
+        incompleteFlush.set(false);
+
+        Assert.assertEquals("How now br", endPoint.takeOutputString());
+
+        endPoint.close();
+        flusher.completeWrite();
+
+        Assert.assertTrue(callback.isDone());
+        Assert.assertFalse(incompleteFlush.get());
+
         try
         {
-            assertEquals(callback.get(10, TimeUnit.MILLISECONDS),null);
-            Assert.fail();
-        }
-        catch (TimeoutException to)
-        {
-            _flushIncomplete.set(false);
-        }
-
-        assertEquals("How now br", _endp.takeOutputString());
-        _endp.close();
-        _flusher.completeWrite();
-        assertCallbackIsDone(callback);
-        assertFlushIsComplete();
-        try
-        {
-            assertEquals(callback.get(),null);
+            callback.get();
             Assert.fail();
         }
         catch (ExecutionException e)
@@ -214,199 +203,112 @@
             Assert.assertTrue(cause instanceof IOException);
             Assert.assertThat(cause.getMessage(), Matchers.containsString("CLOSED"));
         }
-        assertEquals("", _endp.takeOutputString());
-        assertTrue(_flusher.isIdle());
+        Assert.assertEquals("", endPoint.takeOutputString());
+        Assert.assertTrue(flusher.isIdle());
     }
 
     @Test
     public void testFailWhileBlocking() throws Exception
     {
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint(new byte[0], 10);
+
+        AtomicBoolean incompleteFlush = new AtomicBoolean();
+        WriteFlusher flusher = new WriteFlusher(endPoint)
+        {
+            @Override
+            protected void onIncompleteFlush()
+            {
+                incompleteFlush.set(true);
+            }
+        };
+
         FutureCallback callback = new FutureCallback();
-        _flusher.write(callback, BufferUtil.toBuffer("How "), BufferUtil.toBuffer("now "), BufferUtil.toBuffer("brown "), BufferUtil.toBuffer("cow!"));
+        flusher.write(callback, BufferUtil.toBuffer("How now brown cow!"));
 
-        assertFalse(callback.isDone());
-        assertFalse(callback.isCancelled());
+        Assert.assertFalse(callback.isDone());
+        Assert.assertFalse(callback.isCancelled());
 
-        assertTrue(_flushIncomplete.get());
+        Assert.assertTrue(incompleteFlush.get());
+        incompleteFlush.set(false);
+
+        Assert.assertEquals("How now br", endPoint.takeOutputString());
+
+        String reason = "Failure";
+        flusher.onFail(new IOException(reason));
+        flusher.completeWrite();
+
+        Assert.assertTrue(callback.isDone());
+        Assert.assertFalse(incompleteFlush.get());
+
         try
         {
-            assertEquals(callback.get(10, TimeUnit.MILLISECONDS),null);
-            Assert.fail();
-        }
-        catch (TimeoutException to)
-        {
-            _flushIncomplete.set(false);
-        }
-
-        assertEquals("How now br", _endp.takeOutputString());
-        _flusher.onFail(new IOException("Failure"));
-        _flusher.completeWrite();
-        assertCallbackIsDone(callback);
-        assertFlushIsComplete();
-        try
-        {
-            assertEquals(callback.get(),null);
+            callback.get();
             Assert.fail();
         }
         catch (ExecutionException e)
         {
             Throwable cause = e.getCause();
             Assert.assertTrue(cause instanceof IOException);
-            Assert.assertThat(cause.getMessage(), Matchers.containsString("Failure"));
+            Assert.assertEquals(reason, cause.getMessage());
         }
-        assertEquals("", _endp.takeOutputString());
-
-        assertTrue(_flusher.isIdle());
-    }
-
-    private static class ConcurrentFlusher extends WriteFlusher implements Runnable
-    {
-        final ByteArrayEndPoint _endp;
-        final SecureRandom _random;
-        final ScheduledThreadPoolExecutor _scheduler;
-        final StringBuilder _content = new StringBuilder();
-
-        ConcurrentFlusher(ByteArrayEndPoint endp, SecureRandom random, ScheduledThreadPoolExecutor scheduler)
-        {
-            super(endp);
-            _endp = endp;
-            _random = random;
-            _scheduler = scheduler;
-        }
-
-        @Override
-        protected void onIncompleteFlushed()
-        {
-            _scheduler.schedule(this, 1 + _random.nextInt(9), TimeUnit.MILLISECONDS);
-        }
-
-        @Override
-        public synchronized void run()
-        {
-            _content.append(_endp.takeOutputString());
-            completeWrite();
-        }
-
-        @Override
-        public synchronized String toString()
-        {
-            _content.append(_endp.takeOutputString());
-            return _content.toString();
-        }
+        Assert.assertEquals("", endPoint.takeOutputString());
+        Assert.assertTrue(flusher.isIdle());
     }
 
     @Test
     public void testConcurrent() throws Exception
     {
-        final SecureRandom random = new SecureRandom();
-        final ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(100);
-
-
-        ConcurrentFlusher[] flushers = new ConcurrentFlusher[50000];
-        FutureCallback[] futures = new FutureCallback[flushers.length];
-        for (int i = 0; i < flushers.length; i++)
-        {
-            int size = 5 + random.nextInt(15);
-            ByteArrayEndPoint endp = new ByteArrayEndPoint(new byte[]{}, size);
-
-            final ConcurrentFlusher flusher = new ConcurrentFlusher(endp, random, scheduler);
-            flushers[i] = flusher;
-            final FutureCallback callback = new FutureCallback();
-            futures[i] = callback;
-            scheduler.schedule(new Runnable()
-            {
-                @Override
-                public void run()
-                {
-                    flusher.onFail(new Throwable("THE CAUSE"));
-                }
-            }
-                    , random.nextInt(75) + 1, TimeUnit.MILLISECONDS);
-            flusher.write(callback, BufferUtil.toBuffer("How Now Brown Cow."), BufferUtil.toBuffer(" The quick brown fox jumped over the lazy dog!"));
-        }
-
-        int completed = 0;
-        int failed = 0;
-
-        for (int i = 0; i < flushers.length; i++)
-        {
-            try
-            {
-                futures[i].get();
-                assertEquals("How Now Brown Cow. The quick brown fox jumped over the lazy dog!", flushers[i].toString());
-                completed++;
-            }
-            catch (Exception e)
-            {
-                assertThat(e.getMessage(), Matchers.containsString("THE CAUSE"));
-                failed++;
-            }
-        }
-
-        assertThat(completed, Matchers.greaterThan(0));
-        assertThat(failed, Matchers.greaterThan(0));
-
-        scheduler.shutdown();
-    }
-
-    @Test
-    public void testConcurrentAccessToWriteAndOnFail() throws Exception
-    {
-        // TODO review this test - It was changed for the boolean flush return, but not really well inspected
-
-        final CountDownLatch failedCalledLatch = new CountDownLatch(1);
-        final CountDownLatch writeCalledLatch = new CountDownLatch(1);
-        final CountDownLatch writeCompleteLatch = new CountDownLatch(1);
-
-        final WriteFlusher writeFlusher = new WriteFlusher(_endPointMock)
-        {
-            @Override
-            public void write(Callback callback, ByteBuffer... buffers)
-            {
-                super.write(callback, buffers);
-                writeCompleteLatch.countDown();
-            }
-
-            @Override
-            protected void onIncompleteFlushed()
-            {
-            }
-        };
-
-        endPointFlushExpectation(writeCalledLatch, failedCalledLatch);
-
-        ExposingStateCallback callback = new ExposingStateCallback();
-        executor.submit(new Writer(writeFlusher, callback));
-        assertThat("Write has been called.", writeCalledLatch.await(5, TimeUnit.SECONDS), is(true));
-        executor.submit(new FailedCaller(writeFlusher, failedCalledLatch)).get();
-
-
-        // callback failed is NOT called because in WRITING state failed() doesn't know about the callback. However
-        // either the write succeeds or we get an IOException which will call callback.failed()
-        assertThat("write complete", writeCompleteLatch.await(5, TimeUnit.SECONDS), is(true));
-
-
-        // in this testcase we more or less emulate that the write has successfully finished and we return from
-        // EndPoint.flush() back to WriteFlusher.write(). Then someone calls failed. So the callback should have been
-        // completed.
+        Random random = new Random();
+        ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(100);
         try
         {
-            callback.get(5,TimeUnit.SECONDS);
-            assertThat("callback completed", callback.isCompleted(), is(true));
-            assertThat("callback failed", callback.isFailed(), is(false));
+            String reason = "THE_CAUSE";
+            ConcurrentWriteFlusher[] flushers = new ConcurrentWriteFlusher[50000];
+            FutureCallback[] futures = new FutureCallback[flushers.length];
+            for (int i = 0; i < flushers.length; ++i)
+            {
+                int size = 5 + random.nextInt(15);
+                ByteArrayEndPoint endPoint = new ByteArrayEndPoint(new byte[0], size);
+                ConcurrentWriteFlusher flusher = new ConcurrentWriteFlusher(endPoint, scheduler, random);
+                flushers[i] = flusher;
+                FutureCallback callback = new FutureCallback();
+                futures[i] = callback;
+                scheduler.schedule(() -> flusher.onFail(new Throwable(reason)), random.nextInt(75) + 1, TimeUnit.MILLISECONDS);
+                flusher.write(callback, BufferUtil.toBuffer("How Now Brown Cow."), BufferUtil.toBuffer(" The quick brown fox jumped over the lazy dog!"));
+            }
+
+            int completed = 0;
+            int failed = 0;
+            for (int i = 0; i < flushers.length; ++i)
+            {
+                try
+                {
+                    futures[i].get(15, TimeUnit.SECONDS);
+                    Assert.assertEquals("How Now Brown Cow. The quick brown fox jumped over the lazy dog!", flushers[i].getContent());
+                    completed++;
+                }
+                catch (ExecutionException x)
+                {
+                    Assert.assertEquals(reason, x.getCause().getMessage());
+                    failed++;
+                }
+            }
+            Assert.assertThat(completed, Matchers.greaterThan(0));
+            Assert.assertThat(failed, Matchers.greaterThan(0));
+            Assert.assertEquals(flushers.length, completed + failed);
         }
-        catch(ExecutionException e)
+        finally
         {
-            // ignored because failure is expected
-            assertThat("callback failed", callback.isFailed(), is(true));
+            scheduler.shutdown();
         }
-        assertThat("callback completed", callback.isDone(), is(true));
     }
 
     @Test
     public void testPendingWriteDoesNotStoreConsumedBuffers() throws Exception
     {
-        int toWrite = _endp.getOutput().capacity();
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint(new byte[0], 10);
+
+        int toWrite = endPoint.getOutput().capacity();
         byte[] chunk1 = new byte[toWrite / 2];
         Arrays.fill(chunk1, (byte)1);
         ByteBuffer buffer1 = ByteBuffer.wrap(chunk1);
@@ -414,9 +316,20 @@
         Arrays.fill(chunk1, (byte)2);
         ByteBuffer buffer2 = ByteBuffer.wrap(chunk2);
 
-        _flusher.write(new Callback.Adapter(), buffer1, buffer2);
-        assertTrue(_flushIncomplete.get());
-        assertFalse(buffer1.hasRemaining());
+        AtomicBoolean incompleteFlush = new AtomicBoolean();
+        WriteFlusher flusher = new WriteFlusher(endPoint)
+        {
+            @Override
+            protected void onIncompleteFlush()
+            {
+                incompleteFlush.set(true);
+            }
+        };
+
+
+        flusher.write(Callback.NOOP, buffer1, buffer2);
+        Assert.assertTrue(incompleteFlush.get());
+        Assert.assertFalse(buffer1.hasRemaining());
 
         // Reuse buffer1
         buffer1.clear();
@@ -424,312 +337,143 @@
         int remaining1 = buffer1.remaining();
 
         // Complete the write
-        _endp.takeOutput();
-        _flusher.completeWrite();
+        endPoint.takeOutput();
+        flusher.completeWrite();
 
         // Make sure buffer1 is unchanged
-        assertEquals(remaining1, buffer1.remaining());
-    }
-
-    private class ExposingStateCallback extends FutureCallback
-    {
-        private boolean failed = false;
-        private boolean completed = false;
-
-        @Override
-        public void succeeded()
-        {
-            completed = true;
-            super.succeeded();
-        }
-
-        @Override
-        public void failed(Throwable cause)
-        {
-            failed = true;
-            super.failed(cause);
-        }
-
-        public boolean isFailed()
-        {
-            return failed;
-        }
-
-        public boolean isCompleted()
-        {
-            return completed;
-        }
+        Assert.assertEquals(remaining1, buffer1.remaining());
     }
 
     @Test(expected = WritePendingException.class)
-    public void testConcurrentAccessToWrite() throws Throwable
+    public void testConcurrentWrites() throws Exception
     {
-        final CountDownLatch flushCalledLatch = new CountDownLatch(1);
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint(new byte[0], 16);
 
-        final WriteFlusher writeFlusher = new WriteFlusher(_endPointMock)
+        CountDownLatch flushLatch = new CountDownLatch(1);
+        WriteFlusher flusher = new WriteFlusher(endPoint)
         {
             @Override
-            protected void onIncompleteFlushed()
+            protected ByteBuffer[] flush(ByteBuffer[] buffers) throws IOException
+            {
+                try
+                {
+                    flushLatch.countDown();
+                    Thread.sleep(2000);
+                    return super.flush(buffers);
+                }
+                catch (InterruptedException x)
+                {
+                    throw new InterruptedIOException();
+                }
+            }
+
+            @Override
+            protected void onIncompleteFlush()
             {
             }
         };
 
-        // in this test we just want to make sure that we called write twice at the same time
-        when(_endPointMock.flush(any(ByteBuffer[].class))).thenAnswer(new Answer<Object>()
+        // Two concurrent writes.
+        new Thread(() -> flusher.write(Callback.NOOP, BufferUtil.toBuffer("foo"))).start();
+        Assert.assertTrue(flushLatch.await(1, TimeUnit.SECONDS));
+        // The second write throws WritePendingException.
+        flusher.write(Callback.NOOP, BufferUtil.toBuffer("bar"));
+    }
+
+    @Test
+    public void testConcurrentWriteAndOnFail() throws Exception
+    {
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint(new byte[0], 16);
+
+        WriteFlusher flusher = new WriteFlusher(endPoint)
         {
             @Override
-            public Object answer(InvocationOnMock invocation) throws Throwable
+            protected ByteBuffer[] flush(ByteBuffer[] buffers) throws IOException
             {
-                flushCalledLatch.countDown();
-                // make sure we stay here, so write is called twice at the same time
-                Thread.sleep(5000);
-                return Boolean.TRUE;
+                ByteBuffer[] result = super.flush(buffers);
+                boolean notified = onFail(new Throwable());
+                Assert.assertFalse(notified);
+                return result;
             }
-        });
 
-        executor.submit(new Writer(writeFlusher, new FutureCallback()));
-        // make sure that we call .get() on the write that executed second by waiting on this latch
-        assertThat("Flush has been called once", flushCalledLatch.await(5, TimeUnit.SECONDS), is(true));
+            @Override
+            protected void onIncompleteFlush()
+            {
+            }
+        };
+
+        FutureCallback callback = new FutureCallback();
+        flusher.write(callback, BufferUtil.toBuffer("foo"));
+
+        // Callback must be successfully completed.
+        callback.get(1, TimeUnit.SECONDS);
+        // Flusher must be idle - not failed - since the write succeeded.
+        Assert.assertTrue(flusher.isIdle());
+    }
+
+    @Test
+    public void testConcurrentIncompleteFlushAndOnFail() throws Exception
+    {
+        int capacity = 8;
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint(new byte[0], capacity);
+        String reason = "the_reason";
+
+        WriteFlusher flusher = new WriteFlusher(endPoint)
+        {
+            @Override
+            protected void onIncompleteFlush()
+            {
+                onFail(new Throwable(reason));
+            }
+        };
+
+        FutureCallback callback = new FutureCallback();
+        byte[] content = new byte[capacity * 2];
+        flusher.write(callback, BufferUtil.toBuffer(content));
+
         try
         {
-            executor.submit(new Writer(writeFlusher, new FutureCallback())).get();
+            // Callback must be failed.
+            callback.get(1, TimeUnit.SECONDS);
         }
-        catch (ExecutionException e)
+        catch (ExecutionException x)
         {
-            throw e.getCause();
+            Assert.assertEquals(reason, x.getCause().getMessage());
         }
     }
 
-    private void endPointFlushExpectation(final CountDownLatch writeCalledLatch,
-                                          final CountDownLatch failedCalledLatch) throws IOException
+    private static class ConcurrentWriteFlusher extends WriteFlusher implements Runnable
     {
-        when(_endPointMock.flush(any(ByteBuffer[].class))).thenAnswer(new Answer<Object>()
+        private final ByteArrayEndPoint endPoint;
+        private final ScheduledExecutorService scheduler;
+        private final Random random;
+        private String content = "";
+
+        private ConcurrentWriteFlusher(ByteArrayEndPoint endPoint, ScheduledThreadPoolExecutor scheduler, Random random)
         {
-            @Override
-            public Object answer(InvocationOnMock invocation) throws Throwable
-            {
-                Object[] arguments = invocation.getArguments();
-                ByteBuffer byteBuffer = (ByteBuffer)arguments[0];
-                BufferUtil.flipToFill(byteBuffer); // pretend everything has been written
-                writeCalledLatch.countDown();
-                failedCalledLatch.await(5, TimeUnit.SECONDS);
-                return Boolean.TRUE;
-            }
-        });
-    }
-
-    @Test
-    public void testConcurrentAccessToIncompleteWriteAndOnFail() throws Exception
-    {
-        final CountDownLatch failedCalledLatch = new CountDownLatch(1);
-        final CountDownLatch onIncompleteFlushedCalledLatch = new CountDownLatch(1);
-        final CountDownLatch writeCalledLatch = new CountDownLatch(1);
-        final CountDownLatch completeWrite = new CountDownLatch(1);
-
-        final WriteFlusher writeFlusher = new WriteFlusher(new EndPointConcurrentAccessToIncompleteWriteAndOnFailMock(writeCalledLatch, failedCalledLatch))
-        {
-            @Override
-            protected void onIncompleteFlushed()
-            {
-                onIncompleteFlushedCalledLatch.countDown();
-                try
-                {
-                    failedCalledLatch.await(5, TimeUnit.SECONDS);
-                }
-                catch (InterruptedException e)
-                {
-                    e.printStackTrace();
-                }
-                completeWrite();
-                completeWrite.countDown();
-            }
-        };
-
-        ExposingStateCallback callback = new ExposingStateCallback();
-        executor.submit(new Writer(writeFlusher, callback));
-        assertThat("Write has been called.", writeCalledLatch.await(5, TimeUnit.SECONDS), is(true));
-        // make sure we're in pending state when calling onFail
-        assertThat("onIncompleteFlushed has been called.", onIncompleteFlushedCalledLatch.await(5,
-                TimeUnit.SECONDS), is(true));
-        executor.submit(new FailedCaller(writeFlusher, failedCalledLatch));
-        assertThat("Failed has been called.", failedCalledLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("completeWrite done", completeWrite.await(5, TimeUnit.SECONDS), is(true));
-        // when we fail in PENDING state, we should have called callback.failed()
-        assertThat("callback failed has been called", callback.isFailed(), is(true));
-        assertThat("callback complete has not been called", callback.isCompleted(), is(false));
-    }
-
-    private static class EndPointConcurrentAccessToIncompleteWriteAndOnFailMock extends ByteArrayEndPoint
-    {
-        private final CountDownLatch writeCalledLatch;
-        private final CountDownLatch failedCalledLatch;
-        private final AtomicBoolean stalled=new AtomicBoolean(false);
-
-        public EndPointConcurrentAccessToIncompleteWriteAndOnFailMock(CountDownLatch writeCalledLatch, CountDownLatch failedCalledLatch)
-        {
-            this.writeCalledLatch = writeCalledLatch;
-            this.failedCalledLatch = failedCalledLatch;
+            super(endPoint);
+            this.endPoint = endPoint;
+            this.scheduler = scheduler;
+            this.random = random;
         }
 
         @Override
-        public boolean flush(ByteBuffer... buffers) throws IOException
+        protected void onIncompleteFlush()
         {
-            writeCalledLatch.countDown();
-            ByteBuffer byteBuffer = buffers[0];
-            int oldPos = byteBuffer.position();
-            if (byteBuffer.remaining() == 2)
-            {
-                // make sure we stall at least once
-                if (!stalled.get())
-                {
-                    stalled.set(true);
-                    return false;
-                }
-                
-                // make sure failed is called before we go on
-                try
-                {
-                    failedCalledLatch.await(5, TimeUnit.SECONDS);
-                }
-                catch (InterruptedException e)
-                {
-                    e.printStackTrace();
-                }
-                BufferUtil.flipToFill(byteBuffer);
-            }
-            else if (byteBuffer.remaining() == 3)
-            {
-                byteBuffer.position(1); // pretend writing one byte
-            }
-            else
-            {
-                byteBuffer.position(byteBuffer.limit());
-            }
-
-            for (ByteBuffer b: buffers)
-                if (BufferUtil.hasContent(b))
-                    return false;
-            return true;
-        }
-    }
-
-    @Test
-    public void testIterationOnNonBlockedStall() throws Exception
-    {
-        final Exchanger<Integer> exchange = new Exchanger<>();
-        final AtomicInteger window = new AtomicInteger(10);
-        EndPointIterationOnNonBlockedStallMock endp=new EndPointIterationOnNonBlockedStallMock(window);
-        final WriteFlusher writeFlusher = new WriteFlusher(endp)
-        {
-            @Override
-            protected void onIncompleteFlushed()
-            {
-                executor.submit(new Runnable() 
-                { 
-                    public void run() 
-                    {
-                        try
-                        {
-                            while(window.get()==0)
-                                window.addAndGet(exchange.exchange(0));
-                            completeWrite(); 
-                        }
-                        catch(Throwable th)
-                        {
-                            th.printStackTrace();
-                        }
-                    }
-                });
-
-            }
-        };
-
-        BlockingCallback callback = new BlockingCallback();
-        writeFlusher.write(callback,BufferUtil.toBuffer("How "),BufferUtil.toBuffer("now "),BufferUtil.toBuffer("brown "),BufferUtil.toBuffer("cow."));
-        exchange.exchange(0);
-        
-        Assert.assertThat(endp.takeOutputString(StandardCharsets.US_ASCII),Matchers.equalTo("How now br"));
-        
-        exchange.exchange(1);
-        exchange.exchange(0);
-        
-        Assert.assertThat(endp.takeOutputString(StandardCharsets.US_ASCII),Matchers.equalTo("o"));
-        
-        exchange.exchange(8);
-        callback.block();
-        
-        Assert.assertThat(endp.takeOutputString(StandardCharsets.US_ASCII),Matchers.equalTo("wn cow."));
-        
-    }
-
-    private static class EndPointIterationOnNonBlockedStallMock extends ByteArrayEndPoint
-    {
-        final AtomicInteger _window;
-        
-        public EndPointIterationOnNonBlockedStallMock(AtomicInteger window)
-        {
-            _window=window;
+            scheduler.schedule(this, 1 + random.nextInt(9), TimeUnit.MILLISECONDS);
         }
 
         @Override
-        public boolean flush(ByteBuffer... buffers) throws IOException
+        public void run()
         {
-            ByteBuffer byteBuffer = buffers[0];
-            
-            if (_window.get()>0 && byteBuffer.hasRemaining())
-            {
-                // consume 1 byte
-                byte one = byteBuffer.get(byteBuffer.position());
-                if (super.flush(ByteBuffer.wrap(new byte[]{one})))
-                {
-                    _window.decrementAndGet();
-                    byteBuffer.position(byteBuffer.position()+1);
-                }
-            }
-            for (ByteBuffer b: buffers)
-                if (BufferUtil.hasContent(b))
-                    return false;
-            return true;
-        }
-    }
-    
-
-    private static class FailedCaller implements Callable<FutureCallback>
-    {
-        private final WriteFlusher writeFlusher;
-        private CountDownLatch failedCalledLatch;
-
-        public FailedCaller(WriteFlusher writeFlusher, CountDownLatch failedCalledLatch)
-        {
-            this.writeFlusher = writeFlusher;
-            this.failedCalledLatch = failedCalledLatch;
+            content += endPoint.takeOutputString();
+            completeWrite();
         }
 
-        @Override
-        public FutureCallback call()
+        private String getContent()
         {
-            writeFlusher.onFail(new IllegalStateException());
-            failedCalledLatch.countDown();
-            return null;
-        }
-    }
-
-    private class Writer implements Callable<FutureCallback>
-    {
-        private final WriteFlusher writeFlusher;
-        private FutureCallback callback;
-
-        public Writer(WriteFlusher writeFlusher, FutureCallback callback)
-        {
-            this.writeFlusher = writeFlusher;
-            this.callback = callback;
-        }
-
-        @Override
-        public FutureCallback call()
-        {
-            writeFlusher.write(callback, BufferUtil.toBuffer("foo"));
-            return callback;
+            content += endPoint.takeOutputString();
+            return content;
         }
     }
 }
diff --git a/jetty-io/src/test/resources/jetty-logging.properties b/jetty-io/src/test/resources/jetty-logging.properties
index d4922ad..257743e 100644
--- a/jetty-io/src/test/resources/jetty-logging.properties
+++ b/jetty-io/src/test/resources/jetty-logging.properties
@@ -1,2 +1,5 @@
 org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
 org.eclipse.jetty.LEVEL=INFO
+#org.eclipse.jetty.io.AbstractConnection.LEVEL=DEBUG
+#org.eclipse.jetty.io.ManagedSelector.LEVEL=DEBUG
+#org.eclipse.jetty.io.ssl.SslConnection.LEVEL=DEBUG
diff --git a/jetty-jaas/pom.xml b/jetty-jaas/pom.xml
index 79d4538..fbd8644 100644
--- a/jetty-jaas/pom.xml
+++ b/jetty-jaas/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-jaas</artifactId>
@@ -13,52 +13,6 @@
   </properties>
   <build>
     <plugins>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-               <_versionpolicy> </_versionpolicy>
-               <Import-Package>javax.sql.*,javax.security.*,javax.naming.*,
-               javax.servlet.*;version="[2.6.0,3.2)",
-               *</Import-Package>
-              </instructions>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
       <!-- always include the sources to be able to prepare the eclipse-jetty-SDK feature
       with a snapshot. -->
       <plugin>
diff --git a/jetty-jaas/src/main/config/etc/jetty-jaas.xml b/jetty-jaas/src/main/config/etc/jetty-jaas.xml
index 9381d10..4e42594 100644
--- a/jetty-jaas/src/main/config/etc/jetty-jaas.xml
+++ b/jetty-jaas/src/main/config/etc/jetty-jaas.xml
@@ -1,9 +1,8 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
-
     <!-- ======================================================== -->
     <!-- java.security.auth.login.config System property          -->
     <!-- This is usually a runtime parameter to the jvm, but      -->
@@ -11,7 +10,7 @@
     <!-- ======================================================== -->
     <Call class="java.lang.System" name="setProperty">
       <Arg>java.security.auth.login.config</Arg>
-      <Arg><Property name="jetty.base" default="." />/<Property name="jaas.login.conf" default="etc/login.conf"/></Arg>
+      <Arg><Property name="jetty.base" default="." />/<Property name="jetty.jaas.login.conf" deprecated="jaas.login.conf" default="etc/login.conf"/></Arg>
     </Call>
 
 </Configure>
diff --git a/jetty-jaas/src/main/config/modules/jaas.mod b/jetty-jaas/src/main/config/modules/jaas.mod
index 4932140..fee3f59 100644
--- a/jetty-jaas/src/main/config/modules/jaas.mod
+++ b/jetty-jaas/src/main/config/modules/jaas.mod
@@ -12,5 +12,6 @@
 etc/jetty-jaas.xml
 
 [ini-template]
-## JAAS Configuration
-jaas.login.conf=etc/login.conf
+## The file location (relative to $jetty.base) for the
+## JAAS "java.security.auth.login.config" system property
+# jetty.jaas.login.conf=etc/login.conf
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASGroup.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASGroup.java
index a881b34..f771c59 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASGroup.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASGroup.java
@@ -24,15 +24,12 @@
 import java.util.HashSet;
 import java.util.Iterator;
 
-
 public class JAASGroup implements Group 
 {
     public static final String ROLES = "__roles__";
     
     private String _name = null;
     private HashSet<Principal> _members = null;
-    
-    
    
     public JAASGroup(String n)
     {
@@ -41,42 +38,21 @@
     }
    
     /* ------------------------------------------------------------ */
-    /**
-     *
-     * @param principal <description>
-     * @return <description>
-     */
     public synchronized boolean addMember(Principal principal)
     {
         return _members.add(principal);
     }
 
-    /**
-     *
-     * @param principal <description>
-     * @return <description>
-     */
     public synchronized boolean removeMember(Principal principal)
     {
         return _members.remove(principal);
     }
 
-    /**
-     *
-     * @param principal <description>
-     * @return <description>
-     */
     public boolean isMember(Principal principal)
     {
         return _members.contains(principal);
     }
 
-
-    
-    /**
-     *
-     * @return <description>
-     */
     public Enumeration<? extends Principal> members()
     {
 
@@ -105,23 +81,11 @@
         return new MembersEnumeration (_members.iterator());
     }
 
-
-    /**
-     *
-     * @return <description>
-     */
     public int hashCode()
     {
         return getName().hashCode();
     }
 
-
-    
-    /**
-     *
-     * @param object <description>
-          * @return <description>
-     */
     public boolean equals(Object object)
     {
         if (! (object instanceof JAASGroup))
@@ -130,23 +94,14 @@
         return ((JAASGroup)object).getName().equals(getName());
     }
 
-    /**
-     *
-     * @return <description>
-     */
     public String toString()
     {
         return getName();
     }
 
-    /**
-     *
-     * @return <description>
-     */
     public String getName()
     {
         
         return _name;
     }
-
 }
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASLoginService.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASLoginService.java
index 1bf9ea5..4eb6684 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASLoginService.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASLoginService.java
@@ -34,14 +34,13 @@
 import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
+import javax.servlet.ServletRequest;
 
 import org.eclipse.jetty.jaas.callback.ObjectCallback;
 import org.eclipse.jetty.jaas.callback.RequestParameterCallback;
 import org.eclipse.jetty.security.DefaultIdentityService;
 import org.eclipse.jetty.security.IdentityService;
 import org.eclipse.jetty.security.LoginService;
-import org.eclipse.jetty.server.HttpChannel;
-import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.UserIdentity;
 import org.eclipse.jetty.util.Loader;
 import org.eclipse.jetty.util.component.AbstractLifeCycle;
@@ -49,9 +48,9 @@
 import org.eclipse.jetty.util.log.Logger;
 
 /* ---------------------------------------------------- */
-/** JAASLoginService
+/** 
+ * JAASLoginService
  *
- * @org.apache.xbean.XBean element="jaasUserRealm" description="Creates a UserRealm suitable for use with JAAS"
  */
 public class JAASLoginService extends AbstractLifeCycle implements LoginService
 {
@@ -181,7 +180,8 @@
     }
 
     /* ------------------------------------------------------------ */
-    public UserIdentity login(final String username,final Object credentials)
+    @Override
+    public UserIdentity login(final String username,final Object credentials, final ServletRequest request)
     {
         try
         {
@@ -210,17 +210,9 @@
                             }
                             else if (callback instanceof RequestParameterCallback)
                             {
-                                HttpChannel channel = HttpChannel.getCurrentHttpChannel();
-
-                                if (channel == null)
-                                    return;
-                                Request request = channel.getRequest();
-
-                                if (request != null)
-                                {
-                                    RequestParameterCallback rpc = (RequestParameterCallback)callback;
+                                RequestParameterCallback rpc = (RequestParameterCallback)callback;
+                                if (request!=null)
                                     rpc.setParameterValues(Arrays.asList(request.getParameterValues(rpc.getParameterName())));
-                                }
                             }
                             else
                                 throw new UnsupportedCallbackException(callback);
@@ -230,7 +222,7 @@
             }
             else
             {
-                Class clazz = Loader.loadClass(getClass(), _callbackHandlerClass);
+                Class<?> clazz = Loader.loadClass(getClass(), _callbackHandlerClass);
                 callbackHandler = (CallbackHandler)clazz.newInstance();
             }
             //set up the login context
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASPrincipal.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASPrincipal.java
index 90cd4ac..b9bada8 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASPrincipal.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASPrincipal.java
@@ -21,41 +21,22 @@
 import java.io.Serializable;
 import java.security.Principal;
 
-
-
-/* ---------------------------------------------------- */
-/** JAASPrincipal
- * <p>Impl class of Principal interface.
- *
- * <p><h4>Notes</h4>
+/** 
+ * JAASPrincipal
  * <p>
- *
- * <p><h4>Usage</h4>
- * <pre>
- */
-/*
- * </pre>
- *
- * @see
- * @version 1.0 Tue Apr 15 2003
- * 
+ * Impl class of Principal interface.
  */
 public class JAASPrincipal implements Principal, Serializable
 {
-    /**
-     * 
-     */
     private static final long serialVersionUID = -5538962177019315479L;
     
     private String _name = null;
     
-    
     public JAASPrincipal(String userName)
     {
         this._name = userName;
     }
 
-
     public boolean equals (Object p)
     {
         if (! (p instanceof JAASPrincipal))
@@ -64,26 +45,20 @@
         return getName().equals(((JAASPrincipal)p).getName());
     }
 
-
     public int hashCode ()
     {
         return getName().hashCode();
     }
 
-
     public String getName ()
     {
         return this._name;
     }
 
-
     public String toString ()
     {
         return getName();
     }
-    
-
-    
 }
 
     
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASRole.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASRole.java
index f8369e9..0358fee 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASRole.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASRole.java
@@ -18,13 +18,8 @@
 
 package org.eclipse.jetty.jaas;
 
-
 public class JAASRole extends JAASPrincipal
 {
-    
-    /**
-     * 
-     */
     private static final long serialVersionUID = 3465114254970134526L;
 
     public JAASRole(String name)
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASUserPrincipal.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASUserPrincipal.java
index f65f3e6..437dbf3 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASUserPrincipal.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASUserPrincipal.java
@@ -23,15 +23,11 @@
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginContext;
 
-
-
-/* ---------------------------------------------------- */
-/** JAASUserPrincipal
- * <p>Implements the JAAS version of the
+/** 
+ * JAASUserPrincipal
+ * <p>
+ * Implements the JAAS version of the
  *  org.eclipse.jetty.http.UserPrincipal interface.
- *
- * @version $Id: JAASUserPrincipal.java 4780 2009-03-17 15:36:08Z jesse $
- *
  */
 public class JAASUserPrincipal implements Principal
 {
@@ -40,7 +36,6 @@
     private final LoginContext _loginContext;
 
     /* ------------------------------------------------ */
-
     public JAASUserPrincipal(String name, Subject subject, LoginContext loginContext)
     {
         this._name = name;
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/RoleCheckPolicy.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/RoleCheckPolicy.java
index e3591ae..e52762e 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/RoleCheckPolicy.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/RoleCheckPolicy.java
@@ -21,7 +21,6 @@
 import java.security.Principal;
 import java.security.acl.Group;
 
-
 public interface RoleCheckPolicy 
 {
     /* ------------------------------------------------ */
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/StrictRoleCheckPolicy.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/StrictRoleCheckPolicy.java
index bd01185..361413a 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/StrictRoleCheckPolicy.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/StrictRoleCheckPolicy.java
@@ -31,7 +31,6 @@
  * 
  *
  * 
- * @org.apache.xbean.XBean description ="Check only topmost role in stack of roles for user"
  */
 public class StrictRoleCheckPolicy implements RoleCheckPolicy
 {
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/callback/DefaultCallbackHandler.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/callback/DefaultCallbackHandler.java
index 96ea96c..2fe7a0a 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/callback/DefaultCallbackHandler.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/callback/DefaultCallbackHandler.java
@@ -29,28 +29,11 @@
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.util.security.Password;
 
-
-
-/* ---------------------------------------------------- */
-/** DefaultUsernameCredentialCallbackHandler
- * <p>
- *
- * <p><h4>Notes</h4>
- * <p>
- *
- * <p><h4>Usage</h4>
- * <pre>
- */
-/*
- * </pre>
- *
- * @see
- * @version 1.0 Tue Apr 15 2003
- *
+/** 
+ * DefaultUsernameCredentialCallbackHandler
  */
 public class DefaultCallbackHandler extends AbstractCallbackHandler
 {
-
     private Request _request;
 
     public void setRequest (Request request)
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/callback/ObjectCallback.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/callback/ObjectCallback.java
index fb50632..b3b70f4 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/callback/ObjectCallback.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/callback/ObjectCallback.java
@@ -20,31 +20,16 @@
 
 import javax.security.auth.callback.Callback;
 
-
-/* ---------------------------------------------------- */
-/** ObjectCallback
- *
- * <p>Can be used as a LoginModule Callback to
+/** 
+ * ObjectCallback
+ * <p>
+ * Can be used as a LoginModule Callback to
  * obtain a user's credential as an Object, rather than
  * a char[], to which some credentials may not be able
  * to be converted
- *
- * <p><h4>Notes</h4>
- * <p>
- *
- * <p><h4>Usage</h4>
- * <pre>
- */
-/*
- * </pre>
- *
- * @see
- * @version 1.0 Tue Apr 15 2003
- * 
  */
 public class ObjectCallback implements Callback
 {
-
     protected Object _object;
     
     public void setObject(Object o)
@@ -57,11 +42,8 @@
         return _object;
     }
 
-
     public void clearObject ()
     {
         _object = null;
     }
-    
-    
 }
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/callback/RequestParameterCallback.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/callback/RequestParameterCallback.java
index e2deea9..1770ab8 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/callback/RequestParameterCallback.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/callback/RequestParameterCallback.java
@@ -22,18 +22,12 @@
 
 import javax.security.auth.callback.Callback;
 
-
 /**
- *
  * RequestParameterCallback
- *
+ * <p>
  * Allows a JAAS callback handler to access any parameter from the j_security_check FORM.
  * This means that a LoginModule can access form fields other than the j_username and j_password
  * fields, and use it, for example, to authenticate a user.
- *
- *
- * @version $Revision: 4780 $ $Date: 2009-03-17 16:36:08 +0100 (Tue, 17 Mar 2009) $
- *
  */
 public class RequestParameterCallback implements Callback
 {
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractDatabaseLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractDatabaseLoginModule.java
index 0a72a7d..4980e6e 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractDatabaseLoginModule.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractDatabaseLoginModule.java
@@ -21,7 +21,6 @@
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -54,21 +53,36 @@
     private String dbUserRoleTableUserField;
     private String dbUserRoleTableRoleField;
 
-
-
-
     /**
      * @return a java.sql.Connection from the database
-     * @throws Exception
+     * @throws Exception if unable to get the connection
      */
     public abstract Connection getConnection () throws Exception;
+    
+    
+    public class JDBCUserInfo extends UserInfo
+    {
+        public JDBCUserInfo (String userName, Credential credential)
+        {
+            super(userName, credential);
+        }
+        
+        
+        
+        @Override
+        public List<String> doFetchRoles ()
+        throws Exception
+        {
+           return getRoles(getUserName());
+        }
+    }
 
 
 
     /* ------------------------------------------------ */
     /** Load info from database
      * @param userName user info to load
-     * @exception SQLException
+     * @exception Exception if unable to get the user info
      */
     public UserInfo getUserInfo (String userName)
         throws Exception
@@ -95,8 +109,22 @@
                 return null;
             }
 
+          
+
+            return new JDBCUserInfo (userName, Credential.getCredential(dbCredential));
+        }
+    }
+    
+    
+    public List<String>  getRoles (String userName)
+    throws Exception
+    {
+        List<String> roles = new ArrayList<String>();
+        
+        try (Connection connection = getConnection())
+        {
             //query for role names
-            List<String> roles = new ArrayList<String>();
+
             try (PreparedStatement statement = connection.prepareStatement (rolesQuery))
             {
                 statement.setString (1, userName);
@@ -109,10 +137,13 @@
                     }
                 }
             }
-
-            return new UserInfo (userName, Credential.getCredential(dbCredential), roles);
+          
         }
+
+        return roles;
     }
+    
+    
 
 
     public void initialize(Subject subject,
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java
index ad95933..b3191c3 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java
@@ -44,7 +44,6 @@
  *
  * Abstract base class for all LoginModules. Subclasses should
  * just need to implement getUserInfo method.
- *
  */
 public abstract class AbstractLoginModule implements LoginModule
 {
@@ -55,6 +54,12 @@
     private JAASUserInfo currentUser;
     private Subject subject;
 
+    /**
+     * JAASUserInfo
+     *
+     * This class unites the UserInfo data with jaas concepts
+     * such as Subject and Principals
+     */
     public class JAASUserInfo
     {
         private UserInfo user;
@@ -63,7 +68,8 @@
 
         public JAASUserInfo (UserInfo u)
         {
-            setUserInfo(u);
+            this.user = u;
+            this.principal = new JAASPrincipal(u.getUserName());
         }
 
         public String getUserName ()
@@ -76,19 +82,7 @@
             return this.principal;
         }
 
-        public void setUserInfo (UserInfo u)
-        {
-            this.user = u;
-            this.principal = new JAASPrincipal(u.getUserName());
-            this.roles = new ArrayList<JAASRole>();
-            if (u.getRoleNames() != null)
-            {
-                Iterator<String> itor = u.getRoleNames().iterator();
-                while (itor.hasNext())
-                    this.roles.add(new JAASRole((String)itor.next()));
-            }
-        }
-
+ 
         public void setJAASInfo (Subject subject)
         {
             subject.getPrincipals().add(this.principal);
@@ -107,10 +101,20 @@
         {
             return this.user.checkCredential(suppliedCredential);
         }
+        
+        public void fetchRoles() throws Exception
+        {
+            this.user.fetchRoles();
+            this.roles = new ArrayList<JAASRole>();
+            if (this.user.getRoleNames() != null)
+            {
+                Iterator<String> itor = this.user.getRoleNames().iterator();
+                while (itor.hasNext())
+                    this.roles.add(new JAASRole((String)itor.next()));
+            }
+        }
     }
 
-
-
     public Subject getSubject ()
     {
         return this.subject;
@@ -162,7 +166,7 @@
     }
     /**
      * @see javax.security.auth.spi.LoginModule#abort()
-     * @throws LoginException
+     * @throws LoginException if unable to abort
      */
     public boolean abort() throws LoginException
     {
@@ -173,11 +177,10 @@
     /**
      * @see javax.security.auth.spi.LoginModule#commit()
      * @return true if committed, false if not (likely not authenticated)
-     * @throws LoginException
+     * @throws LoginException if unable to commit
      */
     public boolean commit() throws LoginException
     {
-
         if (!isAuthenticated())
         {
             currentUser = null;
@@ -215,7 +218,7 @@
     /**
      * @see javax.security.auth.spi.LoginModule#login()
      * @return true if is authenticated, false otherwise
-     * @throws LoginException
+     * @throws LoginException if unable to login
      */
     public boolean login() throws LoginException
     {
@@ -255,7 +258,10 @@
             setAuthenticated(currentUser.checkCredential(webCredential));
           
             if (isAuthenticated())
+            {
+                currentUser.fetchRoles();
                 return true;
+            }
             else
                 throw new FailedLoginException();
         }
@@ -278,7 +284,7 @@
     /**
      * @see javax.security.auth.spi.LoginModule#logout()
      * @return true always
-     * @throws LoginException
+     * @throws LoginException if unable to logout
      */
     public boolean logout() throws LoginException
     {
@@ -288,10 +294,10 @@
 
     /**
      * @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, java.util.Map, java.util.Map)
-     * @param subject
-     * @param callbackHandler
-     * @param sharedState
-     * @param options
+     * @param subject the subject
+     * @param callbackHandler the callback handler
+     * @param sharedState the shared state map
+     * @param options the option map
      */
     public void initialize(Subject subject, CallbackHandler callbackHandler,
             Map<String,?> sharedState, Map<String,?> options)
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/DataSourceLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/DataSourceLoginModule.java
index 1f1d6ae..97afdbb 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/DataSourceLoginModule.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/DataSourceLoginModule.java
@@ -42,12 +42,15 @@
     private DataSource dataSource;
 
     /* ------------------------------------------------ */
-    /** Init LoginModule.
+    /** 
+     * Init LoginModule.
+     * <p>
      * Called once by JAAS after new instance created.
-     * @param subject
-     * @param callbackHandler
-     * @param sharedState
-     * @param options
+     * 
+     * @param subject the subject
+     * @param callbackHandler the callback handler
+     * @param sharedState the shared state map
+     * @param options the option map
      */
     public void initialize(Subject subject,
                            CallbackHandler callbackHandler,
@@ -70,21 +73,15 @@
         }
     }
 
-
     /**
      * Get a connection from the DataSource
      * @see AbstractDatabaseLoginModule#getConnection()
      * @return the connection for the datasource
-     * @throws Exception
+     * @throws Exception if unable to get the connection
      */
     public Connection getConnection ()
     throws Exception
     {
         return dataSource.getConnection();
     }
-
-
-
-
-
 }
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/JDBCLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/JDBCLoginModule.java
index 4498103..b255df0 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/JDBCLoginModule.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/JDBCLoginModule.java
@@ -29,22 +29,14 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-
-
-/* ---------------------------------------------------- */
-/** JDBCLoginModule
- * <p>JAAS LoginModule to retrieve user information from
- *  a database and authenticate the user.
- *
- * <p><h4>Notes</h4>
+/** 
+ * JDBCLoginModule
+ * <p>
+ * JAAS LoginModule to retrieve user information from
+ * a database and authenticate the user.
+ * <h1>Notes</h1>
  * <p>This version uses plain old JDBC connections NOT
  * Datasources.
- *
- * <p><h4>Usage</h4>
- * <pre>
- * </pre>
- *
- * @version 1.0 Tue Apr 15 2003
  */
 public class JDBCLoginModule extends AbstractDatabaseLoginModule
 {
@@ -55,12 +47,11 @@
     private String dbUserName;
     private String dbPassword;
 
-
     /**
      * Get a connection from the DriverManager
      * @see AbstractDatabaseLoginModule#getConnection()
      * @return the connection for this datasource
-     * @throws Exception
+     * @throws Exception if unable to get the connection
      */
     public Connection getConnection ()
     throws Exception
@@ -80,12 +71,15 @@
 
 
     /* ------------------------------------------------ */
-    /** Init LoginModule.
+    /** 
+     * Init LoginModule.
+     * <p>
      * Called once by JAAS after new instance created.
-     * @param subject
-     * @param callbackHandler
-     * @param sharedState
-     * @param options
+     * 
+     * @param subject the subject
+     * @param callbackHandler the callback handler
+     * @param sharedState the shared state map
+     * @param options the options map
      */
     public void initialize(Subject subject,
                            CallbackHandler callbackHandler,
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/LdapLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/LdapLoginModule.java
index c6fc988..15ecea9 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/LdapLoginModule.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/LdapLoginModule.java
@@ -43,21 +43,20 @@
 import javax.security.auth.login.LoginException;
 
 import org.eclipse.jetty.jaas.callback.ObjectCallback;
+import org.eclipse.jetty.util.B64Code;
+import org.eclipse.jetty.util.TypeUtil;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.security.Credential;
 
 /**
  * A LdapLoginModule for use with JAAS setups
- * <p/>
+ * <p>
  * The jvm should be started with the following parameter:
- * <br><br>
- * <code>
+ * <pre>
  * -Djava.security.auth.login.config=etc/ldap-loginModule.conf
- * </code>
- * <br><br>
+ * </pre>
  * and an example of the ldap-loginModule.conf would be:
- * <br><br>
  * <pre>
  * ldaploginmodule {
  *    org.eclipse.jetty.server.server.plus.jaas.spi.LdapLoginModule required
@@ -80,11 +79,7 @@
  *    roleMemberAttribute="uniqueMember"
  *    roleObjectClass="groupOfUniqueNames";
  *    };
- *  </pre>
- *
- *
- *
- *
+ * </pre>
  */
 public class LdapLoginModule extends AbstractLoginModule
 {
@@ -137,7 +132,7 @@
 
     /**
      * name of the attribute that a users password is stored under
-     * <p/>
+     * <p>
      * NOTE: not always accessible, see force binding login
      */
     private String _userPasswordAttribute = "userPassword";
@@ -183,21 +178,46 @@
 
     private DirContext _rootContext;
 
+    
+    public class LDAPUserInfo extends UserInfo
+    {
+    	Attributes attributes;
+    	
+        /**
+         * @param userName
+         * @param credential
+         */
+        public LDAPUserInfo(String userName, Credential credential, Attributes attributes)
+        {
+            super(userName, credential);
+            this.attributes = attributes;
+        }
+
+        @Override
+        public List<String> doFetchRoles() throws Exception
+        {
+            return getUserRoles(_rootContext, getUserName(), attributes);
+        }
+        
+    }
+    
+    
     /**
      * get the available information about the user
-     * <p/>
+     * <p>
      * for this LoginModule, the credential can be null which will result in a
      * binding ldap authentication scenario
-     * <p/>
+     * <p>
      * roles are also an optional concept if required
      *
-     * @param username
+     * @param username the user name
      * @return the userinfo for the username
-     * @throws Exception
+     * @throws Exception if unable to get the user info
      */
     public UserInfo getUserInfo(String username) throws Exception
     {
-        String pwdCredential = getUserCredentials(username);
+    	Attributes attributes = getUserAttributes(username);
+        String pwdCredential = getUserCredentials(attributes);
 
         if (pwdCredential == null)
         {
@@ -206,9 +226,7 @@
 
         pwdCredential = convertCredentialLdapToJetty(pwdCredential);
         Credential credential = Credential.getCredential(pwdCredential);
-        List<String> roles = getUserRoles(_rootContext, username);
-
-        return new UserInfo(username, credential, roles);
+        return new LDAPUserInfo(username, credential, attributes);
     }
 
     protected String doRFC2254Encoding(String inputString)
@@ -243,61 +261,47 @@
     }
 
     /**
-     * attempts to get the users credentials from the users context
-     * <p/>
+     * attempts to get the users LDAP attributes from the users context
+     * <p>
      * NOTE: this is not an user authenticated operation
      *
      * @param username
      * @return
      * @throws LoginException
      */
-    private String getUserCredentials(String username) throws LoginException
+    private Attributes getUserAttributes(String username) throws LoginException
     {
-        String ldapCredential = null;
+    	Attributes attributes = null;
 
-        SearchControls ctls = new SearchControls();
-        ctls.setCountLimit(1);
-        ctls.setDerefLinkFlag(true);
-        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
-
-        String filter = "(&(objectClass={0})({1}={2}))";
-
-        LOG.debug("Searching for users with filter: \'" + filter + "\'" + " from base dn: " + _userBaseDn);
-
-        try
-        {
-            Object[] filterArguments = {_userObjectClass, _userIdAttribute, username};
-            NamingEnumeration<SearchResult> results = _rootContext.search(_userBaseDn, filter, filterArguments, ctls);
-
-            LOG.debug("Found user?: " + results.hasMoreElements());
-
-            if (!results.hasMoreElements())
-            {
-                throw new LoginException("User not found.");
-            }
-
-            SearchResult result = findUser(username);
-
-            Attributes attributes = result.getAttributes();
-
-            Attribute attribute = attributes.get(_userPasswordAttribute);
-            if (attribute != null)
-            {
-                try
-                {
-                    byte[] value = (byte[]) attribute.get();
-
-                    ldapCredential = new String(value);
-                }
-                catch (NamingException e)
-                {
-                    LOG.debug("no password available under attribute: " + _userPasswordAttribute);
-                }
-            }
-        }
-        catch (NamingException e)
-        {
+    	SearchResult result;
+		try {
+			result = findUser(username);
+	        attributes = result.getAttributes();
+		}
+		catch (NamingException e) {
             throw new LoginException("Root context binding failure.");
+		}
+    	
+    	return attributes;
+	}
+    
+    private String getUserCredentials(Attributes attributes) throws LoginException
+    {
+    	String ldapCredential = null;
+
+        Attribute attribute = attributes.get(_userPasswordAttribute);
+        if (attribute != null)
+        {
+            try
+            {
+                byte[] value = (byte[]) attribute.get();
+
+                ldapCredential = new String(value);
+            }
+            catch (NamingException e)
+            {
+                LOG.debug("no password available under attribute: " + _userPasswordAttribute);
+            }
         }
 
         LOG.debug("user cred is: " + ldapCredential);
@@ -307,7 +311,7 @@
 
     /**
      * attempts to get the users roles from the root context
-     * <p/>
+     * <p>
      * NOTE: this is not an user authenticated operation
      *
      * @param dirContext
@@ -315,9 +319,22 @@
      * @return
      * @throws LoginException
      */
-    private List<String> getUserRoles(DirContext dirContext, String username) throws LoginException, NamingException
+    private List<String> getUserRoles(DirContext dirContext, String username, Attributes attributes) throws LoginException, NamingException
     {
-        String userDn = _userRdnAttribute + "=" + username + "," + _userBaseDn;
+        String rdnValue = username;
+        Attribute attribute = attributes.get(_userRdnAttribute);
+		if (attribute != null)
+		{
+		    try
+		    {
+		        rdnValue = (String) attribute.get();	// switch to the value stored in the _userRdnAttribute if we can
+		    }
+		    catch (NamingException e)
+		    {
+		    }
+		}
+
+        String userDn = _userRdnAttribute + "=" + rdnValue + "," + _userBaseDn;
 
         return getUserRolesByDn(dirContext, userDn);
     }
@@ -373,13 +390,13 @@
 
     /**
      * since ldap uses a context bind for valid authentication checking, we override login()
-     * <p/>
+     * <p>
      * if credentials are not available from the users context or if we are forcing the binding check
      * then we try a binding authentication check, otherwise if we have the users encoded password then
      * we can try authentication via that mechanic
      *
      * @return true if authenticated, false otherwise
-     * @throws LoginException
+     * @throws LoginException if unable to login
      */
     public boolean login() throws LoginException
     {
@@ -402,28 +419,36 @@
                 return isAuthenticated();
             }
 
+            boolean authed = false;
+
             if (_forceBindingLogin)
             {
-                return bindingLogin(webUserName, webCredential);
+                authed = bindingLogin(webUserName, webCredential);
             }
-
-            // This sets read and the credential
-            UserInfo userInfo = getUserInfo(webUserName);
-
-            if (userInfo == null)
+            else
             {
-                setAuthenticated(false);
-                return false;
+                // This sets read and the credential
+                UserInfo userInfo = getUserInfo(webUserName);
+
+                if (userInfo == null)
+                {
+                    setAuthenticated(false);
+                    return false;
+                }
+
+                setCurrentUser(new JAASUserInfo(userInfo));
+
+                if (webCredential instanceof String)
+                    authed = credentialLogin(Credential.getCredential((String) webCredential));
+                else
+                    authed = credentialLogin(webCredential);
             }
 
-            setCurrentUser(new JAASUserInfo(userInfo));
+            //only fetch roles if authenticated
+            if (authed)
+                getCurrentUser().fetchRoles();
 
-            if (webCredential instanceof String)
-            {
-                return credentialLogin(Credential.getCredential((String) webCredential));
-            }
-
-            return credentialLogin(webCredential);
+            return authed;
         }
         catch (UnsupportedCallbackException e)
         {
@@ -450,9 +475,9 @@
     /**
      * password supplied authentication check
      *
-     * @param webCredential
+     * @param webCredential the web credential
      * @return true if authenticated
-     * @throws LoginException
+     * @throws LoginException if unable to login
      */
     protected boolean credentialLogin(Object webCredential) throws LoginException
     {
@@ -466,10 +491,11 @@
      * has an ACI (access control instruction) that allow the access to any user or at least
      * for the user that logs in.
      *
-     * @param username
-     * @param password
+     * @param username the user name
+     * @param password the password
      * @return true always
-     * @throws LoginException
+     * @throws LoginException if unable to bind the login
+     * @throws NamingException if failure to bind login
      */
     public boolean bindingLogin(String username, Object password) throws LoginException, NamingException
     {
@@ -512,16 +538,18 @@
 
         String filter = "(&(objectClass={0})({1}={2}))";
 
-        LOG.info("Searching for users with filter: \'" + filter + "\'" + " from base dn: " + _userBaseDn);
+        if (LOG.isDebugEnabled())
+            LOG.debug("Searching for user " + username + " with filter: \'" + filter + "\'" + " from base dn: " + _userBaseDn);
 
         Object[] filterArguments = new Object[]{
-            _userObjectClass,
-            _userIdAttribute,
-            username
+                                                _userObjectClass,
+                                                _userIdAttribute,
+                                                username
         };
         NamingEnumeration<SearchResult> results = _rootContext.search(_userBaseDn, filter, filterArguments, ctls);
 
-        LOG.info("Found user?: " + results.hasMoreElements());
+        if (LOG.isDebugEnabled())
+            LOG.debug("Found user?: " + results.hasMoreElements());
 
         if (!results.hasMoreElements())
         {
@@ -534,12 +562,13 @@
 
     /**
      * Init LoginModule.
+     * <p>
      * Called once by JAAS after new instance is created.
      *
-     * @param subject
-     * @param callbackHandler
-     * @param sharedState
-     * @param options
+     * @param subject the subect
+     * @param callbackHandler the callback handler
+     * @param sharedState the shared state map
+     * @param options the option map
      */
     public void initialize(Subject subject,
                            CallbackHandler callbackHandler,
@@ -662,38 +691,36 @@
         return env;
     }
 
-    public static String convertCredentialJettyToLdap(String encryptedPassword)
-    {
-        if ("MD5:".startsWith(encryptedPassword.toUpperCase(Locale.ENGLISH)))
-        {
-            return "{MD5}" + encryptedPassword.substring("MD5:".length(), encryptedPassword.length());
-        }
-
-        if ("CRYPT:".startsWith(encryptedPassword.toUpperCase(Locale.ENGLISH)))
-        {
-            return "{CRYPT}" + encryptedPassword.substring("CRYPT:".length(), encryptedPassword.length());
-        }
-
-        return encryptedPassword;
-    }
-
     public static String convertCredentialLdapToJetty(String encryptedPassword)
     {
         if (encryptedPassword == null)
         {
-            return encryptedPassword;
+            return null;
         }
 
-        if ("{MD5}".startsWith(encryptedPassword.toUpperCase(Locale.ENGLISH)))
+        if (encryptedPassword.toUpperCase(Locale.ENGLISH).startsWith("{MD5}"))
         {
-            return "MD5:" + encryptedPassword.substring("{MD5}".length(), encryptedPassword.length());
+            String src = encryptedPassword.substring("{MD5}".length(), encryptedPassword.length());
+            return "MD5:" + base64ToHex(src);
         }
 
-        if ("{CRYPT}".startsWith(encryptedPassword.toUpperCase(Locale.ENGLISH)))
+        if (encryptedPassword.toUpperCase(Locale.ENGLISH).startsWith("{CRYPT}"))
         {
             return "CRYPT:" + encryptedPassword.substring("{CRYPT}".length(), encryptedPassword.length());
         }
 
         return encryptedPassword;
     }
+
+    private static String base64ToHex(String src)
+    {
+        byte[] bytes = B64Code.decode(src);
+        return TypeUtil.toString(bytes, 16);
+    }
+
+    private static String hexToBase64(String src)
+    {
+        byte[] bytes = TypeUtil.fromHexString(src);
+        return new String(B64Code.encode(bytes));
+    }
 }
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java
index c389a7e..969727c 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java
@@ -36,8 +36,6 @@
 
 /**
  * PropertyFileLoginModule
- *
- *
  */
 public class PropertyFileLoginModule extends AbstractLoginModule
 {
@@ -55,10 +53,11 @@
      *
      * @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, java.util.Map,
      *      java.util.Map)
-     * @param subject
-     * @param callbackHandler
-     * @param sharedState
-     * @param options
+     *      
+     * @param subject the subject
+     * @param callbackHandler the callback handler
+     * @param sharedState the shared state map
+     * @param options the options map
      */
     public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options)
     {
@@ -102,10 +101,10 @@
     }
 
     /**
-     * Don't implement this as we want to pre-fetch all of the users.
+     * 
      *
-     * @param userName
-     * @throws Exception
+     * @param userName the user name
+     * @throws Exception if unable to get the user information
      */
     public UserInfo getUserInfo(String userName) throws Exception
     {
@@ -118,6 +117,8 @@
         if (userIdentity==null)
             return null;
 
+        //TODO in future versions change the impl of PropertyUserStore so its not
+        //storing Subjects etc, just UserInfo
         Set<Principal> principals = userIdentity.getSubject().getPrincipals();
 
         List<String> roles = new ArrayList<String>();
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/UserInfo.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/UserInfo.java
index 7a9df97..d0b9287 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/UserInfo.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/UserInfo.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.jaas.spi;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import org.eclipse.jetty.util.security.Credential;
@@ -29,24 +30,70 @@
  * This is the information read from the external source
  * about a user.
  * 
- * Can be cached by a UserInfoCache implementation
+ * Can be cached.
  */
 public class UserInfo
 {
     
     private String _userName;
     private Credential _credential;
-    private List<String> _roleNames;
+    protected List<String> _roleNames = new ArrayList<>();
+    protected boolean _rolesLoaded = false;
     
     
+    /**
+     * @param userName
+     * @param credential
+     * @param roleNames
+     */
     public UserInfo (String userName, Credential credential, List<String> roleNames)
     {
         _userName = userName;
         _credential = credential;
-        _roleNames = new ArrayList<String>();
         if (roleNames != null)
         {
-            _roleNames.addAll(roleNames);
+            synchronized (_roleNames)
+            {
+                _roleNames.addAll(roleNames);
+                _rolesLoaded = true;
+            }
+        }
+    }
+    
+    
+    /**
+     * @param userName
+     * @param credential
+     */
+    public UserInfo (String userName, Credential credential)
+    {
+        this (userName, credential, null);
+    }
+    
+    
+    
+    /**
+     * Should be overridden by subclasses to obtain
+     * role info
+     * 
+     * @return
+     * @throws Exception
+     */
+    public List<String> doFetchRoles ()
+    throws Exception
+    {
+        return Collections.emptyList();
+    }
+    
+    public void fetchRoles () throws Exception
+    {
+        synchronized (_roleNames)
+        {
+            if (!_rolesLoaded)
+            {
+                _roleNames.addAll(doFetchRoles());
+                _rolesLoaded = true;
+            }
         }
     }
     
@@ -56,8 +103,8 @@
     }
     
     public List<String> getRoleNames ()
-    {
-        return new ArrayList<String>(_roleNames);
+    {   
+        return Collections.unmodifiableList(_roleNames);
     }
     
     public boolean checkCredential (Object suppliedCredential)
diff --git a/jetty-jaspi/pom.xml b/jetty-jaspi/pom.xml
index e428fd4..ba13197 100644
--- a/jetty-jaspi/pom.xml
+++ b/jetty-jaspi/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-jaspi</artifactId>
@@ -10,57 +10,10 @@
   <description>Jetty security infrastructure</description>
   <url>http://www.eclipse.org/jetty</url>
   <properties>
-    <bundle-symbolic-name>${project.groupId}.jaspi</bundle-symbolic-name>
+    <bundle-symbolic-name>${project.groupId}.security.jaspi</bundle-symbolic-name>
   </properties>
   <build>
     <plugins>
-     <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",*</Import-Package>
-                <Export-Package>org.eclipse.jetty.security.jaspi.*;version="${parsedVersion.osgiVersion}"</Export-Package>
-              </instructions>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>               
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
@@ -70,6 +23,24 @@
       </plugin>
     </plugins>
   </build>
+  <profiles>
+    <profile>
+      <id>jdk9</id>
+      <activation>
+        <jdk>[1.9,)</jdk>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+              <argLine>@{argLine} --add-modules java.se.ee</argLine>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
   <dependencies>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
@@ -85,7 +56,6 @@
       <groupId>org.eclipse.jetty.orbit</groupId>
       <artifactId>javax.security.auth.message</artifactId>
     </dependency>
-    
     <dependency>
       <groupId>org.apache.geronimo.components</groupId>
       <artifactId>geronimo-jaspi</artifactId>
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
index feee999..6796b72 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
@@ -121,7 +121,7 @@
     @Override
     public UserIdentity login(String username, Object password, ServletRequest request)
     { 
-        UserIdentity user = _loginService.login(username, password);
+        UserIdentity user = _loginService.login(username, password, request);
         if (user != null)
         {
             renewSession((HttpServletRequest)request, null);
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java
index 3ec76b0..2250e37 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java
@@ -135,6 +135,8 @@
      * If {@link #setServiceSubject(Subject)} has not been used to 
      * set a subject, then the {@link Server#getBeans(Class)} method is
      * used to look for a Subject.
+     * @param server the server to pull the Subject from
+     * @return the subject
      */
     protected Subject findServiceSubject(Server server)
     {
@@ -151,6 +153,9 @@
      * If {@link #setServerName(String)} has not been called, then
      * use the name of the a principal in the service subject.
      * If not found, return "server".
+     * @param server not used
+     * @param subject the subject to use
+     * @return the server name from the subject (or default value if not found in subject or principals)
      */
     protected String findServerName(Server server, Subject subject)
     {
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiMessageInfo.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiMessageInfo.java
index c5d8c8a..fa59103 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiMessageInfo.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiMessageInfo.java
@@ -27,11 +27,8 @@
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 
-
 /**
  * Almost an implementation of jaspi MessageInfo.
- *
- * @version $Rev: 4660 $ $Date: 2009-02-25 17:29:53 +0100 (Wed, 25 Feb 2009) $
  */
 public class JaspiMessageInfo implements MessageInfo
 {
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/ServletCallbackHandler.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/ServletCallbackHandler.java
index 6356d54..4f7959e 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/ServletCallbackHandler.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/ServletCallbackHandler.java
@@ -39,10 +39,7 @@
 import org.eclipse.jetty.server.UserIdentity;
 
 /**
- * 
  * Idiot class required by jaspi stupidity
- * 
- * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $
  */
 public class ServletCallbackHandler implements CallbackHandler
 {
@@ -74,7 +71,7 @@
                 PasswordValidationCallback passwordValidationCallback = (PasswordValidationCallback) callback;
                 Subject subject = passwordValidationCallback.getSubject();
 
-                UserIdentity user = _loginService.login(passwordValidationCallback.getUsername(),passwordValidationCallback.getPassword());
+                UserIdentity user = _loginService.login(passwordValidationCallback.getUsername(),passwordValidationCallback.getPassword(), null);
                 
                 if (user!=null)
                 {
@@ -91,7 +88,7 @@
                         credentialValidationCallback.getUsername(),
                         credentialValidationCallback.getCredential());
 
-                UserIdentity user = _loginService.login(credentialValidationCallback.getUsername(),credentialValidationCallback.getCredential());
+                UserIdentity user = _loginService.login(credentialValidationCallback.getUsername(),credentialValidationCallback.getCredential(), null);
 
                 if (user!=null)
                 {
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/SimpleAuthConfig.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/SimpleAuthConfig.java
index 5559e88..b3480b2 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/SimpleAuthConfig.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/SimpleAuthConfig.java
@@ -26,9 +26,6 @@
 import javax.security.auth.message.config.ServerAuthConfig;
 import javax.security.auth.message.config.ServerAuthContext;
 
-/**
- * @version $Rev: 4660 $ $Date: 2009-02-25 17:29:53 +0100 (Wed, 25 Feb 2009) $
- */
 public class SimpleAuthConfig implements ServerAuthConfig
 {
     public static final String HTTP_SERVLET = "HttpServlet";
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java
index c7784bc..9bbeb5b 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java
@@ -45,9 +45,6 @@
 import org.eclipse.jetty.util.security.Credential;
 import org.eclipse.jetty.util.security.Password;
 
-/**
- * @version $Rev: 4792 $ $Date: 2009-03-18 22:55:52 +0100 (Wed, 18 Mar 2009) $
- */
 public class BaseAuthModule implements ServerAuthModule, ServerAuthContext
 {
     private static final Class[] SUPPORTED_MESSAGE_TYPES = new Class[] { HttpServletRequest.class, HttpServletResponse.class };
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BasicAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BasicAuthModule.java
index d59f117..7454f78 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BasicAuthModule.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BasicAuthModule.java
@@ -36,10 +36,7 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.security.Constraint;
 
-/**
- * @deprecated use *ServerAuthentication
- * @version $Rev: 4660 $ $Date: 2009-02-25 17:29:53 +0100 (Wed, 25 Feb 2009) $
- */
+@Deprecated
 public class BasicAuthModule extends BaseAuthModule
 {
     private static final Logger LOG = Log.getLogger(BasicAuthModule.class);
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/ClientCertAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/ClientCertAuthModule.java
index 0c30951..143996c 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/ClientCertAuthModule.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/ClientCertAuthModule.java
@@ -34,10 +34,7 @@
 import org.eclipse.jetty.util.security.Constraint;
 import org.eclipse.jetty.util.security.Password;
 
-/**
- * @deprecated use *ServerAuthentication
- * @version $Rev: 4530 $ $Date: 2009-02-13 00:47:44 +0100 (Fri, 13 Feb 2009) $
- */
+@Deprecated
 public class ClientCertAuthModule extends BaseAuthModule
 {
 
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/DigestAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/DigestAuthModule.java
index afe3bcf..7c9138a 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/DigestAuthModule.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/DigestAuthModule.java
@@ -42,15 +42,11 @@
 import org.eclipse.jetty.util.security.Constraint;
 import org.eclipse.jetty.util.security.Credential;
 
-/**
- * @deprecated use *ServerAuthentication
- * @version $Rev: 4627 $ $Date: 2009-02-20 00:07:19 +0100 (Fri, 20 Feb 2009) $
- */
+@Deprecated
 public class DigestAuthModule extends BaseAuthModule
 {
     private static final Logger LOG = Log.getLogger(DigestAuthModule.class);
 
-
     protected long maxNonceAge = 0;
 
     protected long nonceSecret = this.hashCode() ^ System.currentTimeMillis();
@@ -213,11 +209,10 @@
     }
 
     /**
-     * @param nonce
+     * @param nonce the nonce
      * @param timestamp should be timestamp of request.
      * @return -1 for a bad nonce, 0 for a stale none, 1 for a good nonce
      */
-    /* ------------------------------------------------------------ */
     public int checkNonce(String nonce, long timestamp)
     {
         try
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
index 0fdf4b4..10cc25d 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
@@ -33,7 +33,6 @@
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
-import org.eclipse.jetty.security.CrossContextPsuedoSession;
 import org.eclipse.jetty.security.authentication.DeferredAuthentication;
 import org.eclipse.jetty.security.authentication.LoginCallbackImpl;
 import org.eclipse.jetty.security.authentication.SessionAuthentication;
@@ -45,10 +44,7 @@
 import org.eclipse.jetty.util.security.Constraint;
 import org.eclipse.jetty.util.security.Password;
 
-/**
- * @deprecated use *ServerAuthentication
- * @version $Rev: 4792 $ $Date: 2009-03-18 22:55:52 +0100 (Wed, 18 Mar 2009) $
- */
+@Deprecated
 public class FormAuthModule extends BaseAuthModule
 {
     private static final Logger LOG = Log.getLogger(FormAuthModule.class);
@@ -79,7 +75,6 @@
 
     private String _formLoginPath;
 
-    private CrossContextPsuedoSession<UserInfo> ssoSource;
 
     public FormAuthModule()
     {
@@ -92,17 +87,6 @@
         setErrorPage(errorPage);
     }
 
-    /**
-     * @deprecated
-     */
-    public FormAuthModule(CallbackHandler callbackHandler, CrossContextPsuedoSession<UserInfo> ssoSource, 
-                          String loginPage, String errorPage)
-    {
-        super(callbackHandler);
-        this.ssoSource = ssoSource;
-        setLoginPage(loginPage);
-        setErrorPage(errorPage);
-    }
 
     @Override
     public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, 
@@ -112,7 +96,6 @@
         super.initialize(requestPolicy, responsePolicy, handler, options);
         setLoginPage((String) options.get(LOGIN_PAGE_KEY));
         setErrorPage((String) options.get(ERROR_PAGE_KEY));
-        ssoSource = (CrossContextPsuedoSession<UserInfo>) options.get(SSO_SOURCE_KEY);
     }
 
     private void setLoginPage(String path)
@@ -232,17 +215,7 @@
 
                 return AuthStatus.SUCCESS;  
             }
-            else if (ssoSource != null)
-            {
-                UserInfo userInfo = ssoSource.fetch(request);
-                if (userInfo != null)
-                {
-                    boolean success = tryLogin(messageInfo, clientSubject, response, session, userInfo.getUserName(), new Password(new String(userInfo.getPassword())));
-                    if (success) { return AuthStatus.SUCCESS; }
-                }
-            }
             
-           
 
             // if we can't send challenge
             if (DeferredAuthentication.isDeferred(response))
@@ -311,12 +284,6 @@
                 }
             }
 
-            // Sign-on to SSO mechanism
-            if (ssoSource != null)
-            {
-                UserInfo userInfo = new UserInfo(username, pwdChars);
-                ssoSource.store(userInfo, response);
-            }
             return true;
         }
         return false;
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/UserInfo.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/UserInfo.java
index 9721083..4880f5d 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/UserInfo.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/UserInfo.java
@@ -20,9 +20,6 @@
 
 import java.util.Arrays;
 
-/**
- * @version $Rev: 4466 $ $Date: 2009-02-10 23:42:54 +0100 (Tue, 10 Feb 2009) $
- */
 public class UserInfo
 {
     private final String userName;
diff --git a/jetty-jmx/pom.xml b/jetty-jmx/pom.xml
index 3cf3e96..7b7c422 100644
--- a/jetty-jmx/pom.xml
+++ b/jetty-jmx/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-jmx</artifactId>
@@ -15,52 +15,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.management.*,*</Import-Package>
-              </instructions>
-            </configuration>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-jmx/src/main/config/etc/jetty-jmx-remote.xml b/jetty-jmx/src/main/config/etc/jetty-jmx-remote.xml
index 3527d8b..bd6cbc6 100644
--- a/jetty-jmx/src/main/config/etc/jetty-jmx-remote.xml
+++ b/jetty-jmx/src/main/config/etc/jetty-jmx-remote.xml
@@ -1,28 +1,39 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
+  <!-- =========================================================== -->
+  <!-- Set the java.rmi.server.hostname property in case you've    -->
+  <!-- got a misconfigured /etc/hosts entry or the like.           -->
+  <!-- =========================================================== -->
+  <!--
+  <Call class="java.lang.System" name="setProperty">
+    <Arg>java.rmi.server.hostname</Arg>
+    <Arg>127.0.0.1</Arg>
+  </Call>
+  -->
+
   <!-- Add a remote JMX connector. The parameters of the constructor
-       below specify the JMX service URL, and the object name string for the
-       connector server bean. The parameters of the JMXServiceURL constructor
-       specify the protocol that clients will use to connect to the remote JMX
-       connector (RMI), the hostname of the server (local hostname), port number
-       (automatically assigned), and the URL path. Note that URL path contains
-       the RMI registry hostname and port number, that may need to be modified
-       in order to comply with the firewall requirements.
+  below specify the JMX service URL, and the object name string for the
+  connector server bean. The parameters of the JMXServiceURL constructor
+  specify the protocol that clients will use to connect to the remote JMX
+  connector (RMI), the hostname of the server (local hostname), port number
+  (automatically assigned), and the URL path. Note that URL path contains
+  the RMI registry hostname and port number, that may need to be modified
+  in order to comply with the firewall requirements.
   -->
   <Call name="addBean">
     <Arg>
       <New id="ConnectorServer" class="org.eclipse.jetty.jmx.ConnectorServer">
-	<Arg>
-	  <New class="javax.management.remote.JMXServiceURL">
-	    <Arg type="java.lang.String">rmi</Arg>
-	    <Arg type="java.lang.String" />
-	    <Arg type="java.lang.Integer"><Property name="jetty.jmxrmiport" default="1099"/></Arg>
-	    <Arg type="java.lang.String">/jndi/rmi://<Property name="jetty.jmxrmihost" default="localhost"/>:<Property name="jetty.jmxrmiport" default="1099"/>/jmxrmi</Arg>
-	  </New>
-	</Arg>
-	<Arg>org.eclipse.jetty.jmx:name=rmiconnectorserver</Arg>
+        <Arg>
+          <New class="javax.management.remote.JMXServiceURL">
+            <Arg type="java.lang.String">rmi</Arg>
+            <Arg type="java.lang.String"><Property name="jetty.jmxremote.rmihost" deprecated="jetty.jmxrmihost" default="localhost"/></Arg>
+            <Arg type="java.lang.Integer"><Property name="jetty.jmxremote.rmiport" deprecated="jetty.jmxrmiport" default="1099"/></Arg>
+            <Arg type="java.lang.String">/jndi/rmi://<Property name="jetty.jmxremote.rmihost" deprecated="jetty.jmxrmihost" default="localhost"/>:<Property name="jetty.jmxremote.rmiport" deprecated="jetty.jmxrmiport" default="1099"/>/jmxrmi</Arg>
+          </New>
+        </Arg>
+        <Arg>org.eclipse.jetty.jmx:name=rmiconnectorserver</Arg>
       </New>
     </Arg>
   </Call>
diff --git a/jetty-jmx/src/main/config/etc/jetty-jmx.xml b/jetty-jmx/src/main/config/etc/jetty-jmx.xml
index aca96f7..e07ea74 100644
--- a/jetty-jmx/src/main/config/etc/jetty-jmx.xml
+++ b/jetty-jmx/src/main/config/etc/jetty-jmx.xml
@@ -1,20 +1,9 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
   <!-- =========================================================== -->
-  <!-- Set the java.rmi.server.hostname property in case you've    -->
-  <!-- got a misconfigured /etc/hosts entry or the like.           -->
-  <!-- =========================================================== -->
-  <!--
-  <Call class="java.lang.System" name="setProperty">
-    <Arg>java.rmi.server.hostname</Arg>
-    <Arg>127.0.0.1</Arg>
-  </Call>
-  -->
-
-  <!-- =========================================================== -->
   <!-- Get the platform mbean server                               -->
   <!-- =========================================================== -->
   <Call id="MBeanServer" class="java.lang.management.ManagementFactory"
diff --git a/jetty-jmx/src/main/config/modules/jmx-remote.mod b/jetty-jmx/src/main/config/modules/jmx-remote.mod
index b6be74a..f8a5111 100644
--- a/jetty-jmx/src/main/config/modules/jmx-remote.mod
+++ b/jetty-jmx/src/main/config/modules/jmx-remote.mod
@@ -9,10 +9,8 @@
 etc/jetty-jmx-remote.xml
 
 [ini-template]
-## JMX Configuration
-## Enable for an open port accessible by remote machines
-# jetty.jmxrmihost=localhost
-# jetty.jmxrmiport=1099
-## Strictly speaking you shouldn't need --exec to use this in most environments.
-## If this isn't working, make sure you enable --exec as well
-# -Dcom.sun.management.jmxremote
+## The host/address to bind RMI to
+# jetty.jmxremote.rmihost=localhost
+
+## The port RMI listens to
+# jetty.jmxremote.rmiport=1099
diff --git a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java
index 7bda529..9dea1d2 100644
--- a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java
+++ b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java
@@ -58,7 +58,7 @@
      * The actual address of the new connector server, as returned
      * by its getAddress method, will not necessarily be exactly the same.
      * @param name object name string to be assigned to connector server bean
-     * @throws Exception
+     * @throws Exception if unable to setup connector server
      */
     public ConnectorServer(JMXServiceURL serviceURL, String name)
         throws Exception
@@ -78,7 +78,7 @@
      * be Strings. The appropriate type of each associated value depends on
      * the attribute. The contents of environment are not changed by this call.
      * @param name object name string to be assigned to connector server bean
-     * @throws Exception
+     * @throws Exception if unable to create connector server
      */
     public ConnectorServer(JMXServiceURL svcUrl, Map<String,?> environment, String name)
          throws Exception
diff --git a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java
index 1205803..3516a4d 100644
--- a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java
+++ b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java
@@ -21,7 +21,6 @@
 import java.io.IOException;
 import java.util.Locale;
 import java.util.Map;
-import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -29,9 +28,9 @@
 import javax.management.InstanceNotFoundException;
 import javax.management.MBeanRegistrationException;
 import javax.management.MBeanServer;
-import javax.management.ObjectInstance;
 import javax.management.ObjectName;
 
+import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.component.Container;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.component.Destroyable;
@@ -42,18 +41,19 @@
 /**
  * Container class for the MBean instances
  */
+@ManagedObject("The component that registers beans as MBeans")
 public class MBeanContainer implements Container.InheritedListener, Dumpable, Destroyable
 {
     private final static Logger LOG = Log.getLogger(MBeanContainer.class.getName());
-    private final static ConcurrentMap<String, AtomicInteger> __unique = new ConcurrentHashMap<String, AtomicInteger>();
+    private final static ConcurrentMap<String, AtomicInteger> __unique = new ConcurrentHashMap<>();
 
     public static void resetUnique()
     {
         __unique.clear();
     }
-    
+
     private final MBeanServer _mbeanServer;
-    private final WeakHashMap<Object, ObjectName> _beans = new WeakHashMap<Object, ObjectName>();
+    private final Map<Object, ObjectName> _beans = new ConcurrentHashMap<>();
     private String _domain = null;
 
     /**
@@ -62,25 +62,23 @@
      * @param object instance for which object name is looked up
      * @return object name associated with specified instance, or null if not found
      */
-    public synchronized ObjectName findMBean(Object object)
+    public ObjectName findMBean(Object object)
     {
-        ObjectName bean = _beans.get(object);
-        return bean == null ? null : bean;
+        return _beans.get(object);
     }
 
     /**
      * Lookup an instance by object name
      *
-     * @param oname object name of instance
+     * @param objectName object name of instance
      * @return instance associated with specified object name, or null if not found
      */
-    public synchronized Object findBean(ObjectName oname)
+    public Object findBean(ObjectName objectName)
     {
         for (Map.Entry<Object, ObjectName> entry : _beans.entrySet())
         {
-            ObjectName bean = entry.getValue();
-            if (bean.equals(oname))
-                return entry.getKey();
+            if (entry.getKey().equals(objectName))
+                return entry.getValue();
         }
         return null;
     }
@@ -130,92 +128,90 @@
     public void beanAdded(Container parent, Object obj)
     {
         if (LOG.isDebugEnabled())
-            LOG.debug("beanAdded {}->{}",parent,obj);
-        
-        // Is their an object name for the parent
-        ObjectName pname=null;
-        if (parent!=null)
+            LOG.debug("beanAdded {}->{}", parent, obj);
+
+        // Is there an object name for the parent ?
+        ObjectName parentObjectName = null;
+        if (parent != null)
         {
-            pname=_beans.get(parent);
-            if (pname==null)
+            parentObjectName = findMBean(parent);
+            if (parentObjectName == null)
             {
-                // create the parent bean
-                beanAdded(null,parent);
-                pname=_beans.get(parent);
+                // Create the parent bean.
+                beanAdded(null, parent);
+                parentObjectName = findMBean(parent);
             }
         }
-        
-        // Does an mbean already exist?
+
+        // Does the mbean already exist ?
         if (obj == null || _beans.containsKey(obj))
             return;
-        
+
         try
         {
-            // Create an MBean for the object
+            // Create an MBean for the object.
             Object mbean = ObjectMBean.mbeanFor(obj);
             if (mbean == null)
                 return;
 
-            
-            ObjectName oname = null;
+            ObjectName objectName = null;
             if (mbean instanceof ObjectMBean)
             {
                 ((ObjectMBean)mbean).setMBeanContainer(this);
-                oname = ((ObjectMBean)mbean).getObjectName();
+                objectName = ((ObjectMBean)mbean).getObjectName();
             }
 
-            //no override mbean object name, so make a generic one
-            if (oname == null)
-            {      
-                //if no explicit domain, create one
+            // No override of the mbean's ObjectName, so make a generic one.
+            if (objectName == null)
+            {
+                // If no explicit domain, create one.
                 String domain = _domain;
                 if (domain == null)
                     domain = obj.getClass().getPackage().getName();
 
-
                 String type = obj.getClass().getName().toLowerCase(Locale.ENGLISH);
                 int dot = type.lastIndexOf('.');
                 if (dot >= 0)
                     type = type.substring(dot + 1);
 
+                StringBuilder buf = new StringBuilder();
 
-                StringBuffer buf = new StringBuffer();
+                String context = (mbean instanceof ObjectMBean) ? makeName(((ObjectMBean)mbean).getObjectContextBasis()) : null;
+                if (context == null && parentObjectName != null)
+                    context = parentObjectName.getKeyProperty("context");
 
-                String context = (mbean instanceof ObjectMBean)?makeName(((ObjectMBean)mbean).getObjectContextBasis()):null;
-                if (context==null && pname!=null)
-                    context=pname.getKeyProperty("context");
-                                
-                if (context != null && context.length()>1)
+                if (context != null && context.length() > 1)
                     buf.append("context=").append(context).append(",");
-                
+
                 buf.append("type=").append(type);
 
-                String name = (mbean instanceof ObjectMBean)?makeName(((ObjectMBean)mbean).getObjectNameBasis()):context;
-                if (name != null && name.length()>1)
+                String name = (mbean instanceof ObjectMBean) ? makeName(((ObjectMBean)mbean).getObjectNameBasis()) : context;
+                if (name != null && name.length() > 1)
                     buf.append(",").append("name=").append(name);
 
                 String basis = buf.toString();
-                
+
                 AtomicInteger count = __unique.get(basis);
-                if (count==null)
+                if (count == null)
                 {
-                    count=__unique.putIfAbsent(basis,new AtomicInteger());
-                    if (count==null)
-                        count=__unique.get(basis);
+                    count = new AtomicInteger();
+                    AtomicInteger existing = __unique.putIfAbsent(basis, count);
+                    if (existing != null)
+                        count = existing;
                 }
-                
-                oname = ObjectName.getInstance(domain + ":" + basis + ",id=" + count.getAndIncrement());
+
+                objectName = ObjectName.getInstance(domain + ":" + basis + ",id=" + count.getAndIncrement());
             }
 
-            ObjectInstance oinstance = _mbeanServer.registerMBean(mbean, oname);
+            _mbeanServer.registerMBean(mbean, objectName);
             if (LOG.isDebugEnabled())
-                LOG.debug("Registered {}", oinstance.getObjectName());
-            _beans.put(obj, oinstance.getObjectName());
+                LOG.debug("Registered {}", objectName);
 
+            _beans.put(obj, objectName);
         }
-        catch (Exception e)
+        catch (Throwable x)
         {
-            LOG.warn("bean: " + obj, e);
+            LOG.warn("bean: " + obj, x);
         }
     }
 
@@ -223,26 +219,12 @@
     public void beanRemoved(Container parent, Object obj)
     {
         if (LOG.isDebugEnabled())
-            LOG.debug("beanRemoved {}",obj);
-        ObjectName bean = _beans.remove(obj);
+            LOG.debug("beanRemoved {}", obj);
 
-        if (bean != null)
-        {
-            try
-            {
-                _mbeanServer.unregisterMBean(bean);
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Unregistered {}", bean);
-            }
-            catch (javax.management.InstanceNotFoundException e)
-            {
-                LOG.ignore(e);
-            }
-            catch (Exception e)
-            {
-                LOG.warn(e);
-            }
-        }
+        ObjectName objectName = _beans.remove(obj);
+
+        if (objectName != null)
+            unregister(objectName);
     }
 
     /**
@@ -251,9 +233,15 @@
      */
     public String makeName(String basis)
     {
-        if (basis==null)
-            return basis;
-        return basis.replace(':', '_').replace('*', '_').replace('?', '_').replace('=', '_').replace(',', '_').replace(' ', '_');
+        if (basis == null)
+            return null;
+        return basis
+                .replace(':', '_')
+                .replace('*', '_')
+                .replace('?', '_')
+                .replace('=', '_')
+                .replace(',', '_')
+                .replace(' ', '_');
     }
 
     @Override
@@ -272,17 +260,26 @@
     @Override
     public void destroy()
     {
-        for (ObjectName oname : _beans.values())
-            if (oname!=null)
-            {
-                try
-                {
-                    _mbeanServer.unregisterMBean(oname);
-                }
-                catch (MBeanRegistrationException | InstanceNotFoundException e)
-                {
-                    LOG.warn(e);
-                }
-            }
+        _beans.values().stream()
+                .filter(objectName -> objectName != null)
+                .forEach(this::unregister);
+    }
+
+    private void unregister(ObjectName objectName)
+    {
+        try
+        {
+            getMBeanServer().unregisterMBean(objectName);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Unregistered {}", objectName);
+        }
+        catch (MBeanRegistrationException | InstanceNotFoundException x)
+        {
+            LOG.ignore(x);
+        }
+        catch (Throwable x)
+        {
+            LOG.warn(x);
+        }
     }
 }
diff --git a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java
index ce2b59b..dde1ea4 100644
--- a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java
+++ b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java
@@ -59,18 +59,18 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-/* ------------------------------------------------------------ */
-/** ObjectMBean.
+/** 
+ * ObjectMBean.
+ * <p>
  * A dynamic MBean that can wrap an arbitary Object instance.
  * the attributes and methods exposed by this bean are controlled by
  * the merge of property bundles discovered by names related to all
  * superclasses and all superinterfaces.
- *
+ * <p>
  * Attributes and methods exported may be "Object" and must exist on the
  * wrapped object, or "MBean" and must exist on a subclass of OBjectMBean
  * or "MObject" which exists on the wrapped object, but whose values are
  * converted to MBean object names.
- *
  */
 public class ObjectMBean implements DynamicMBean
 {
@@ -592,7 +592,7 @@
      * getter and setter methods. Descriptions are obtained with a call to findDescription with the
      * attribute name.
      *
-     * @param method
+     * @param method the method to define
      * @param attributeAnnotation "description" or "access:description" or "type:access:description"  where type is
      * one of: <ul>
      * <li>"Object" The field/method is on the managed object.
@@ -601,6 +601,7 @@
      * <li>"MMBean" The field/method is on the mbean proxy object and value should be converted to MBean reference
      * </ul>
      * the access is either "RW" or "RO".
+     * @return the mbean attribute info for the method
      */
     public MBeanAttributeInfo defineAttribute(Method method, ManagedAttribute attributeAnnotation)
     {
@@ -662,7 +663,7 @@
                 {
 
                     // look for a declared setter
-                    if (methods[m].getName().equals(declaredSetter) && methods[m].getParameterTypes().length == 1)
+                    if (methods[m].getName().equals(declaredSetter) && methods[m].getParameterCount() == 1)
                     {
                         if (setter != null)
                         {
@@ -681,7 +682,7 @@
                 }
 
                 // look for a setter
-                if ( methods[m].getName().equals("set" + uName) && methods[m].getParameterTypes().length == 1)
+                if ( methods[m].getName().equals("set" + uName) && methods[m].getParameterCount() == 1)
                 {
                     if (setter != null)
                     {
diff --git a/jetty-jmx/src/test/java/org/eclipse/jetty/jmx/MBeanContainerLifeCycleTest.java b/jetty-jmx/src/test/java/org/eclipse/jetty/jmx/MBeanContainerLifeCycleTest.java
new file mode 100644
index 0000000..3c2689d
--- /dev/null
+++ b/jetty-jmx/src/test/java/org/eclipse/jetty/jmx/MBeanContainerLifeCycleTest.java
@@ -0,0 +1,116 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.jmx;
+
+import java.lang.management.ManagementFactory;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MBeanContainerLifeCycleTest
+{
+    private ContainerLifeCycle container;
+    private MBeanServer mbeanServer;
+
+    @Before
+    public void prepare() throws Exception
+    {
+        container = new ContainerLifeCycle();
+        mbeanServer = ManagementFactory.getPlatformMBeanServer();
+        MBeanContainer mbeanContainer = new MBeanContainer(mbeanServer);
+        container.addBean(mbeanContainer);
+        container.start();
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        container.stop();
+    }
+
+    @Test
+    public void testAddBeanRegistersMBeanRemoveBeanUnregistersMBean() throws Exception
+    {
+        // Adding a bean to the container should register the MBean.
+        QueuedThreadPool bean = new QueuedThreadPool();
+        container.addBean(bean);
+
+        String pkg = bean.getClass().getPackage().getName();
+        Set<ObjectName> objectNames = mbeanServer.queryNames(ObjectName.getInstance(pkg + ":*"), null);
+        Assert.assertEquals(1, objectNames.size());
+
+        // Removing the bean should unregister the MBean.
+        container.removeBean(bean);
+        objectNames = mbeanServer.queryNames(ObjectName.getInstance(pkg + ":*"), null);
+        Assert.assertEquals(0, objectNames.size());
+    }
+
+    @Test
+    public void testStoppingContainerDoesNotUnregistersMBeans() throws Exception
+    {
+        QueuedThreadPool bean = new QueuedThreadPool();
+        container.addBean(bean, true);
+
+        String pkg = bean.getClass().getPackage().getName();
+        Set<ObjectName> objectNames = mbeanServer.queryNames(ObjectName.getInstance(pkg + ":*"), null);
+        Assert.assertEquals(1, objectNames.size());
+
+        container.stop();
+
+        objectNames = mbeanServer.queryNames(ObjectName.getInstance(pkg + ":*"), null);
+        Assert.assertEquals(1, objectNames.size());
+
+        // Remove the MBeans to start clean on the next test.
+        objectNames.forEach(objectName ->
+        {
+            try
+            {
+                mbeanServer.unregisterMBean(objectName);
+            }
+            catch (Throwable ignored)
+            {
+            }
+        });
+    }
+
+    @Test
+    public void testDestroyingContainerUnregistersMBeans() throws Exception
+    {
+        QueuedThreadPool bean = new QueuedThreadPool();
+        container.addBean(bean, true);
+
+        String pkg = bean.getClass().getPackage().getName();
+        Set<ObjectName> objectNames = mbeanServer.queryNames(ObjectName.getInstance(pkg + ":*"), null);
+        Assert.assertEquals(1, objectNames.size());
+
+        container.stop();
+        container.destroy();
+
+        objectNames = mbeanServer.queryNames(ObjectName.getInstance(pkg + ":*"), null);
+        Assert.assertEquals(0, objectNames.size());
+    }
+}
diff --git a/jetty-jndi/pom.xml b/jetty-jndi/pom.xml
index a071d9c..58c63e2 100644
--- a/jetty-jndi/pom.xml
+++ b/jetty-jndi/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-jndi</artifactId>
@@ -15,52 +15,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.naming.*,*</Import-Package>
-              </instructions>
-            </configuration>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/BindingEnumeration.java b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/BindingEnumeration.java
index a428f86..93ec46d 100644
--- a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/BindingEnumeration.java
+++ b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/BindingEnumeration.java
@@ -25,14 +25,8 @@
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 
-/** BindingEnumeration
- * <p>Implementation of NamingEnumeration
- *
- * <p><h4>Notes</h4>
- * <p>Used to return results of Context.listBindings();
- *
- * <p><h4>Usage</h4>
- *
+/** 
+ * BindingEnumeration
  */
 public class BindingEnumeration implements NamingEnumeration<Binding>
 {
diff --git a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java
index 9caa1e7..4cb5701 100644
--- a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java
+++ b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java
@@ -37,30 +37,25 @@
 
 
 /**
- * ContextFactory.java
- *
+ * ContextFactory
+ * <p>
  * This is an object factory that produces a jndi naming
  * context based on a classloader.
- *
- *  It is used for the java:comp context.
- *
- *  This object factory is bound at java:comp. When a
- *  lookup arrives for java:comp,  this object factory
- *  is invoked and will return a context specific to
- *  the caller's environment (so producing the java:comp/env
- *  specific to a webapp).
- *
- *  The context selected is based on classloaders. First
- *  we try looking at the thread context classloader if it is set, and walk its
- *  hierarchy, creating a context if none is found. If the thread context classloader
- *  is not set, then we use the classloader associated with the current Context.
- *  
- *  If there is no current context, or no classloader, we return null.
- *
- * Created: Fri Jun 27 09:26:40 2003
- *
- *
- *
+ * <p>
+ * It is used for the <code>java:comp</code> context.
+ * <p>
+ * This object factory is bound at <code>java:comp</code>. When a
+ * lookup arrives for java:comp,  this object factory
+ * is invoked and will return a context specific to
+ * the caller's environment (so producing the <code>java:comp/env</code>
+ * specific to a webapp).
+ * <p>
+ * The context selected is based on classloaders. First
+ * we try looking at the thread context classloader if it is set, and walk its
+ * hierarchy, creating a context if none is found. If the thread context classloader
+ * is not set, then we use the classloader associated with the current Context.
+ * <p> 
+ * If there is no current context, or no classloader, we return null.
  */
 public class ContextFactory implements ObjectFactory
 {
@@ -87,15 +82,15 @@
 
     /**
      * Find or create a context which pertains to a classloader.
-     *
+     * <p>
      * If the thread context classloader is set, we try to find an already-created naming context
      * for it. If one does not exist, we walk its classloader hierarchy until one is found, or we 
      * run out of parent classloaders. In the latter case, we will create a new naming context associated
      * with the original thread context classloader.
-     * 
+     * <p>
      * If the thread context classloader is not set, we obtain the classloader from the current 
      * jetty Context, and look for an already-created naming context. 
-     * 
+     * <p>
      * If there is no current jetty Context, or it has no associated classloader, we 
      * return null.
      * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable)
@@ -177,12 +172,13 @@
 
     /**
      * Create a new NamingContext.
-     * @param obj
-     * @param loader
-     * @param env
-     * @param name
-     * @param parentCtx
-     * @throws Exception
+     * @param obj the object to create
+     * @param loader the classloader for the naming context
+     * @param env the jndi env for the entry
+     * @param name the name of the entry
+     * @param parentCtx the parent context of the entry
+     * @return the newly created naming context
+     * @throws Exception if unable to create a new naming context
      */
     public NamingContext newNamingContext(Object obj, ClassLoader loader, Hashtable env, Name name, Context parentCtx)
     throws Exception
@@ -201,7 +197,8 @@
   
     /**
      * Find the naming Context for the given classloader
-     * @param loader
+     * @param loader the classloader for the context
+     * @return the context for the classloader
      */
     public Context getContextForClassLoader(ClassLoader loader)
     {
diff --git a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/DataSourceCloser.java b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/DataSourceCloser.java
index 053c63c..8408a73 100644
--- a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/DataSourceCloser.java
+++ b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/DataSourceCloser.java
@@ -24,6 +24,7 @@
 
 import javax.sql.DataSource;
 
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.component.Destroyable;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
diff --git a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/NameEnumeration.java b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/NameEnumeration.java
index 8e99a53..0f4ede8 100644
--- a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/NameEnumeration.java
+++ b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/NameEnumeration.java
@@ -25,14 +25,8 @@
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 
-/** NameEnumeration
- * <p>Implementation of NamingEnumeration interface.
- *
- * <p><h4>Notes</h4>
- * <p>Used for returning results of Context.list();
- *
- * <p><h4>Usage</h4>
- *
+/** 
+ * NameEnumeration
  */
 public class NameEnumeration implements NamingEnumeration<NameClassPair>
 {
diff --git a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/NamingContext.java b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/NamingContext.java
index de1da80..2404c29 100644
--- a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/NamingContext.java
+++ b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/NamingContext.java
@@ -18,7 +18,6 @@
 
 package org.eclipse.jetty.jndi;
 
-
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -48,13 +47,13 @@
 import org.eclipse.jetty.util.component.Dumpable;
 import org.eclipse.jetty.util.log.Logger;
 
-
-/*------------------------------------------------*/
-/** NamingContext
- * <p>Implementation of Context interface.
- *
- * <p><h4>Notes</h4>
- * <p>All Names are expected to be Compound, not Composite.
+/** 
+ * NamingContext
+ * <p>
+ * Implementation of Context interface.
+ * <p>
+ * <b>Notes:</b>
+ * All Names are expected to be Compound, not Composite.
  */
 @SuppressWarnings("unchecked")
 public class NamingContext implements Context, Cloneable, Dumpable
@@ -178,11 +177,6 @@
     }
 
     /*------------------------------------------------*/
-    /**
-     * Setter for _parser
-     *
-     *
-     */
     public void setNameParser (NameParser parser)
     {
         _parser = parser;
@@ -1261,6 +1255,7 @@
      *
      * @param name a <code>Name</code> value
      * @param obj an <code>Object</code> value
+     * @throws NameAlreadyBoundException if name already bound
      */
     public void addBinding (Name name, Object obj) throws NameAlreadyBoundException
     {
@@ -1337,7 +1332,7 @@
     /*------------------------------------------------*/
     /**
      * Remove leading or trailing empty components from
-     * name. Eg "/comp/env/" -> "comp/env"
+     * name. Eg "/comp/env/" -&gt; "comp/env"
      *
      * @param name the name to normalize
      * @return normalized name
diff --git a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/NamingUtil.java b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/NamingUtil.java
index 6dee1b9..3cc0ac3 100644
--- a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/NamingUtil.java
+++ b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/NamingUtil.java
@@ -33,13 +33,7 @@
 
 
 /**
- * Util.java
- *
- *
- * Created: Tue Jul  1 18:26:17 2003
- *
- *
- * @version 1.0
+ * Naming Utility Methods
  */
 public class NamingUtil
 {
@@ -53,6 +47,7 @@
      * @param ctx the context into which to bind
      * @param nameStr the name relative to context to bind
      * @param obj the object to be bound
+     * @return the bound context
      * @exception NamingException if an error occurs
      */
     public static Context bind (Context ctx, String nameStr, Object obj)
@@ -112,7 +107,7 @@
      * @param ctx the context containing the name for which to list the bindings
      * @param name the name in the context to list
      * @return map: key is fully qualified name, value is the bound object
-     * @throws NamingException
+     * @throws NamingException if unable to flatten bindings
      */
     public static Map flattenBindings (Context ctx, String name)
     throws NamingException
diff --git a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/factories/MailSessionReference.java b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/factories/MailSessionReference.java
index 13e6ee9..beced59 100644
--- a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/factories/MailSessionReference.java
+++ b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/factories/MailSessionReference.java
@@ -92,17 +92,8 @@
         {
             this.password = password;
         }
-
-
     };
 
-
-
-
-
-    /**
-     *
-     */
     public MailSessionReference()
     {
        super ("javax.mail.Session", MailSessionReference.class.getName(), null);
@@ -117,7 +108,7 @@
      * @param arg2 not used
      * @param arg3 not used
      * @return the object found
-     * @throws Exception
+     * @throws Exception if unable to get object instance
      */
     public Object getObjectInstance(Object ref, Name arg1, Context arg2, Hashtable arg3) throws Exception
     {
diff --git a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/java/javaRootURLContext.java b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/java/javaRootURLContext.java
index fe49b06..4950c21 100644
--- a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/java/javaRootURLContext.java
+++ b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/java/javaRootURLContext.java
@@ -37,14 +37,12 @@
 
 
 
-/** javaRootURLContext
- * <p>This is the root of the java: url namespace
- *
- * <p><h4>Notes</h4>
- * <p>Thanks to Rickard Oberg for the idea of binding an ObjectFactory at "comp".
- *
- * <p><h4>Usage</h4>
- * <pre>
+/** 
+ * javaRootURLContext
+ * <p>
+ * This is the root of the <code>java:</code> url namespace
+ * <p>
+ * (Thanks to Rickard Oberg for the idea of binding an ObjectFactory at "comp")
  */
 public class javaRootURLContext implements Context
 {
diff --git a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/java/javaURLContextFactory.java b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/java/javaURLContextFactory.java
index e69d332..ecb8218 100644
--- a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/java/javaURLContextFactory.java
+++ b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/java/javaURLContextFactory.java
@@ -29,14 +29,10 @@
 import org.eclipse.jetty.util.log.Logger;
 
 
-/** javaURLContextFactory
- * <p>This is the URL context factory for the java: URL.
- *
- * <p><h4>Notes</h4>
+/** 
+ * javaURLContextFactory
  * <p>
- *
- * <p><h4>Usage</h4>
- * <pre>
+ * This is the URL context factory for the <code>java:</code> URL.
  */
 public class javaURLContextFactory implements ObjectFactory
 {
diff --git a/jetty-jsp/pom.xml b/jetty-jsp/pom.xml
deleted file mode 100644
index 24f262c..0000000
--- a/jetty-jsp/pom.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <parent>
-    <groupId>org.eclipse.jetty</groupId>
-    <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
-  </parent>
-  <modelVersion>4.0.0</modelVersion>
-  <artifactId>jetty-jsp</artifactId>
-  <name>Jetty :: Glassfish JSP Implementation</name>
-  <url>http://www.eclipse.org/jetty</url>
-  <packaging>jar</packaging>
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-
-  <dependencies>
-
-    <dependency>
-        <groupId>org.eclipse.jetty</groupId>
-        <artifactId>jetty-util</artifactId>
-        <version>${project.version}</version>
-        <scope>provided</scope>
-    </dependency>
-    <dependency>
-        <groupId>org.eclipse.jetty</groupId>
-        <artifactId>jetty-server</artifactId>
-        <version>${project.version}</version>
-        <scope>provided</scope>
-    </dependency>
-
-    <!-- Schemas -->
-    <dependency>
-      <groupId>org.eclipse.jetty.toolchain</groupId>
-      <artifactId>jetty-schemas</artifactId>
-    </dependency>
-
-    <!-- servlet api -->
-    <dependency>
-       <groupId>javax.servlet</groupId>
-       <artifactId>javax.servlet-api</artifactId>
-    </dependency>
-
-    <!-- JSP Api -->
-    <dependency>
-      <groupId>javax.servlet.jsp</groupId>
-      <artifactId>javax.servlet.jsp-api</artifactId>
-    </dependency>
-    <!-- JSP Impl -->
-    <dependency>
-      <groupId>org.glassfish.web</groupId>
-      <artifactId>javax.servlet.jsp</artifactId>
-    </dependency>
-
-    <!-- JSTL Api -->
-    <dependency>
-       <groupId>org.eclipse.jetty.orbit</groupId>
-       <artifactId>javax.servlet.jsp.jstl</artifactId>
-    </dependency>
-    <!-- JSTL Impl -->
-    <dependency>
-       <groupId>org.glassfish.web</groupId>
-       <artifactId>javax.servlet.jsp.jstl</artifactId>
-    </dependency>
-
-    <!-- EL Api -->
-    <!-- Not needed as glassfish impl jars contain also the api classes
-    <dependency>
-      <groupId>javax.el</groupId>
-      <artifactId>javax.el-api</artifactId>
-    </dependency>
-    -->
-
-    <!-- EL Impl -->
-    <dependency>
-        <groupId>org.glassfish</groupId>
-        <artifactId>javax.el</artifactId>
-    </dependency>
-
-
-    <!-- Eclipse Java Compiler (for JSP Compilation) -->
-    <dependency>
-      <groupId>org.eclipse.jetty.orbit</groupId>
-      <artifactId>org.eclipse.jdt.core</artifactId>
-    </dependency>
-  </dependencies>
-</project>
diff --git a/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jsp.mod b/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jsp.mod
deleted file mode 100644
index 130d2b3..0000000
--- a/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jsp.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Glassfish JSP Module
-#
-[name]
-jsp-impl
-
-[lib]
-lib/jsp/*.jar
diff --git a/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jstl.mod b/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jstl.mod
deleted file mode 100644
index 4b8e6f3..0000000
--- a/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jstl.mod
+++ /dev/null
@@ -1,6 +0,0 @@
-#
-# Glassfish JSTL
-[name]
-jstl-impl
-
-# This file is empty as glassfish jstl is provided by glassfish jsp
diff --git a/jetty-jsp/src/main/java/org/eclipse/jetty/jsp/JettyJspServlet.java b/jetty-jsp/src/main/java/org/eclipse/jetty/jsp/JettyJspServlet.java
deleted file mode 100644
index fb6c30d..0000000
--- a/jetty-jsp/src/main/java/org/eclipse/jetty/jsp/JettyJspServlet.java
+++ /dev/null
@@ -1,107 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.jsp;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.jasper.servlet.JspServlet;
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.util.URIUtil;
-import org.eclipse.jetty.util.resource.Resource;
-
-
-/**
- * JettyJspServlet
- *
- * Wrapper for the jsp servlet that handles receiving requests mapped from 
- * jsp-property-groups. Mappings could be wildcard urls like "/*", which would
- * include welcome files, but we need those to be handled by the DefaultServlet.
- */
-public class JettyJspServlet extends JspServlet
-{
-
-    /**
-     * 
-     */
-    private static final long serialVersionUID = -5387857473125086791L;
-
-    @Override
-    public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
-    {
-        
-        
-        HttpServletRequest request = null;
-        if (req instanceof HttpServletRequest)
-            request = (HttpServletRequest)req;
-        else
-            throw new ServletException("Request not HttpServletRequest");
-
-        String servletPath=null;
-        String pathInfo=null;
-        if (request.getAttribute("javax.servlet.include.request_uri")!=null)
-        {
-            servletPath=(String)request.getAttribute("javax.servlet.include.servlet_path");
-            pathInfo=(String)request.getAttribute("javax.servlet.include.path_info");
-            if (servletPath==null)
-            {
-                servletPath=request.getServletPath();
-                pathInfo=request.getPathInfo();
-            }
-        }
-        else
-        {
-            servletPath = request.getServletPath();
-            pathInfo = request.getPathInfo();
-        }
-        
-        String pathInContext = URIUtil.addPaths(servletPath,pathInfo);
-        String jspFile = getInitParameter("jspFile");
-        
-        //if the request is for a jsp file then fall through to the jsp servlet
-        if (jspFile == null)
-        {
-            if (pathInContext.endsWith("/"))
-            {
-                //dispatch via forward to the default servlet
-                getServletContext().getNamedDispatcher("default").forward(req, resp);
-                return;
-            }
-            else
-            {      
-                //check if it resolves to a directory
-                Resource resource = ((ContextHandler.Context)getServletContext()).getContextHandler().getResource(pathInContext);           
-                if (resource!=null && resource.isDirectory())
-                {
-                    //dispatch via forward to the default servlet
-                    getServletContext().getNamedDispatcher("default").forward(req, resp);
-                    return;
-                }
-            }
-        }
-
-        //fall through to the normal jsp servlet handling
-        super.service(req, resp);
-    }
-
-    
-}
diff --git a/jetty-jsp/src/main/resources/readme.txt b/jetty-jsp/src/main/resources/readme.txt
deleted file mode 100644
index 39d448b..0000000
--- a/jetty-jsp/src/main/resources/readme.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This jar file is purely to work around a problem with the Maven Dependency plugin.
-Several modules in jetty use the Dependency plugin to copy or unpack the dependencies of  other modules.
-However, the Dependency plugin is not capable of unpacking or copying a dependency of type 'pom', which
-this module is, as it consists purely of external dependencies needed to run jsp.
diff --git a/jetty-jspc-maven-plugin/pom.xml b/jetty-jspc-maven-plugin/pom.xml
index c300ba2..f8c317f 100644
--- a/jetty-jspc-maven-plugin/pom.xml
+++ b/jetty-jspc-maven-plugin/pom.xml
@@ -2,31 +2,34 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-jspc-maven-plugin</artifactId>
   <packaging>maven-plugin</packaging>
   <name>Jetty :: Jetty JSPC Maven Plugin</name>
   <properties>
+    <bundle-symbolic-name>${project.groupId}.jspc.plugin</bundle-symbolic-name>
   </properties>
   <build>
     <plugins>
       <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
           <skip>true</skip>
         </configuration>
       </plugin>
       <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-plugin-plugin</artifactId>
-        <version>2.9</version>
+        <version>3.4</version>
         <executions>
           <execution>
             <id>exec-plugin-doc</id>
             <phase>generate-sources</phase>
             <goals>
-              <goal>xdoc</goal>
+              <goal>descriptor</goal>
               <goal>helpmojo</goal>
             </goals>
           </execution>
diff --git a/jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java b/jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java
index 27377fc..7baf09b 100644
--- a/jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java
+++ b/jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java
@@ -25,55 +25,48 @@
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.net.URI;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-import java.util.regex.Pattern;
 
 import org.apache.jasper.JspC;
+import org.apache.jasper.servlet.JspCServletContext;
+import org.apache.jasper.servlet.TldScanner;
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.project.MavenProject;
+import org.apache.tomcat.JarScanner;
+import org.apache.tomcat.util.scan.StandardJarScanner;
 import org.codehaus.plexus.util.FileUtils;
 import org.codehaus.plexus.util.StringUtils;
 import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.PatternMatcher;
 import org.eclipse.jetty.util.resource.Resource;
 
 /**
- * <p>
  * This goal will compile jsps for a webapp so that they can be included in a
  * war.
- * </p>
  * <p>
- * At runtime, the plugin will use the jsp2.0 jspc compiler if you are running
- * on a 1.4 or lower jvm. If you are using a 1.5 jvm, then the jsp2.1 compiler
- * will be selected. (this is the same behaviour as the <a
- * href="http://jetty.mortbay.org/maven-plugin">jetty plugin</a> for executing
- * webapps).
+ * At runtime, the plugin will use the jspc compiler to precompile jsps and tags.
  * </p>
  * <p>
  * Note that the same java compiler will be used as for on-the-fly compiled
  * jsps, which will be the Eclipse java compiler.
- * </p>
- * 
  * <p>
  * See <a
- * href="http://docs.codehaus.org/display/JETTY/Maven+Jetty+Jspc+Plugin">Usage
+ * href="https://www.eclipse.org/jetty/documentation/current/jetty-jspc-maven-plugin.html">Usage
  * Guide</a> for instructions on using this plugin.
  * </p>
- * 
- * @author janb
- * 
  * @goal jspc
  * @phase process-classes
- * @requiresDependencyResolution compile
+ * @requiresDependencyResolution compile+runtime
  * @description Runs jspc compiler to produce .java and .class files
  */
 public class JspcMojo extends AbstractMojo
@@ -87,13 +80,42 @@
      *
      * Add some extra setters to standard JspC class to help configure it
      * for running in maven.
+     * 
+     * TODO move all setters on the plugin onto this jspc class instead.
      */
     public static class JettyJspC extends JspC
     {
+   
+        private boolean scanAll;
+        
         public void setClassLoader (ClassLoader loader)
         {
             this.loader = loader;
         }
+        
+       public void setScanAllDirectories (boolean scanAll)
+       {
+           this.scanAll = scanAll;
+       }
+       
+       public boolean getScanAllDirectories ()
+       {
+           return this.scanAll;
+       }
+       
+
+        @Override
+        protected TldScanner newTldScanner(JspCServletContext context, boolean namespaceAware, boolean validate, boolean blockExternal)
+        {            
+            if (context != null && context.getAttribute(JarScanner.class.getName()) == null) 
+            {
+                StandardJarScanner jarScanner = new StandardJarScanner();             
+                jarScanner.setScanAllDirectories(getScanAllDirectories());
+                context.setAttribute(JarScanner.class.getName(), jarScanner);
+            }
+                
+            return super.newTldScanner(context, namespaceAware, validate, blockExternal);
+        }      
     }
     
     
@@ -110,7 +132,7 @@
      * The artifacts for the project.
      * 
      * @since jetty-7.6.3
-     * @parameter expression="${project.artifacts}"
+     * @parameter default-value="${project.artifacts}"
      * @readonly
      */
     private Set projectArtifacts;
@@ -119,7 +141,7 @@
     /**
      * The maven project.
      * 
-     * @parameter expression="${project}"
+     * @parameter default-value="${project}"
      * @required
      * @readonly
      */
@@ -130,7 +152,7 @@
     /**
      * The artifacts for the plugin itself.
      * 
-     * @parameter expression="${plugin.artifacts}"
+     * @parameter default-value="${plugin.artifacts}"
      * @readonly
      */
     private List pluginArtifacts;
@@ -215,7 +237,7 @@
     /**
      * The location of the compiled classes for the webapp
      * 
-     * @parameter expression="${project.build.outputDirectory}"
+     * @parameter default-value="${project.build.outputDirectory}"
      */
     private File classesDirectory;
 
@@ -229,6 +251,19 @@
     
     
     /**
+     * Source version - if not set defaults to jsp default (currently 1.7)
+     * @parameter 
+     */
+    private String sourceVersion;
+    
+    
+    /**
+     * Target version - if not set defaults to jsp default (currently 1.7)
+     * @parameter 
+     */
+    private String targetVersion;
+    
+    /**
      * 
      * The JspC instance being used to compile the jsps.
      * 
@@ -237,7 +272,14 @@
     private JettyJspC jspc;
 
 
-
+    /**
+     * Whether dirs on the classpath should be scanned as well as jars.
+     * True by default. This allows for scanning for tlds of dependent projects that
+     * are in the reactor as unassembled jars.
+     * 
+     * @parameter default-value=true
+     */
+    private boolean scanAllDirectories;
     
 
     public void execute() throws MojoExecutionException, MojoFailureException
@@ -251,7 +293,11 @@
             getLog().info("webXml="+webXml);
             getLog().info("insertionMarker="+ (insertionMarker == null || insertionMarker.equals("") ? END_OF_WEBAPP : insertionMarker));
             getLog().info("keepSources=" + keepSources);
-            getLog().info("mergeFragment=" + mergeFragment);            
+            getLog().info("mergeFragment=" + mergeFragment);  
+            if (sourceVersion != null)
+                getLog().info("sourceVersion="+sourceVersion);
+            if (targetVersion != null)
+                getLog().info("targetVersion="+targetVersion);
         }
         try
         {
@@ -274,21 +320,17 @@
         List<URL> webAppUrls = setUpWebAppClassPath();
         
         //set up the classpath of the container (ie jetty and jsp jars)
-        String sysClassPath = setUpSysClassPath();
-        
-        //get the list of system classpath jars that contain tlds
-        List<URL> tldJarUrls = getSystemJarsWithTlds();
-        
-        for (URL u:tldJarUrls)
-        {
-            if (getLog().isDebugEnabled())
-                getLog().debug(" sys jar with tlds: "+u);
-            webAppUrls.add(u);
-        }
+        Set<URL> pluginJars = getPluginJars();
+        Set<URL> providedJars = getProvidedScopeJars(pluginJars);
+ 
 
+        //Make a classloader so provided jars will be on the classpath
+        List<URL> sysUrls = new ArrayList<URL>();      
+        sysUrls.addAll(providedJars);     
+        URLClassLoader sysClassLoader = new URLClassLoader((URL[])sysUrls.toArray(new URL[0]), currentClassLoader);
       
-        //use the classpaths as the classloader
-        URLClassLoader webAppClassLoader = new URLClassLoader((URL[]) webAppUrls.toArray(new URL[0]), currentClassLoader);
+        //make a classloader with the webapp classpath
+        URLClassLoader webAppClassLoader = new URLClassLoader((URL[]) webAppUrls.toArray(new URL[0]), sysClassLoader);
         StringBuffer webAppClassPath = new StringBuffer();
 
         for (int i = 0; i < webAppUrls.size(); i++)
@@ -311,26 +353,40 @@
         if (jspc == null)
             jspc = new JettyJspC();
         
+
         jspc.setWebXmlFragment(webXmlFragment);
         jspc.setUriroot(webAppSourceDirectory);     
         jspc.setOutputDir(generatedClasses);
-        jspc.setClassPath(sysClassPath+System.getProperty("path.separator")+webAppClassPath.toString());
         jspc.setClassLoader(fakeWebAppClassLoader);
+        jspc.setScanAllDirectories(scanAllDirectories);
         jspc.setCompile(true);
+        if (sourceVersion != null)
+            jspc.setCompilerSourceVM(sourceVersion);
+        if (targetVersion != null)
+            jspc.setCompilerTargetVM(targetVersion);
 
         // JspC#setExtensions() does not exist, so 
         // always set concrete list of files that will be processed.
         String jspFiles = getJspFiles(webAppSourceDirectory);
-        getLog().info("Compiling "+jspFiles);
-        getLog().info("Includes="+includes);
-        getLog().info("Excludes="+excludes);
-        jspc.setJspFiles(jspFiles);
+       
+        try
+        {
+            if (jspFiles == null | jspFiles.equals(""))
+            {
+                getLog().info("No files selected to precompile");
+            }
+            else
+            {
+                getLog().info("Compiling "+jspFiles+" from includes="+includes+" excludes="+excludes);
+                jspc.setJspFiles(jspFiles);
+                jspc.execute();
+            }
+        }
+        finally
+        {
 
-        getLog().info("Files selected to precompile: " + jspFiles);
-
-        jspc.execute();
-
-        Thread.currentThread().setContextClassLoader(currentClassLoader);
+            Thread.currentThread().setContextClassLoader(currentClassLoader);
+        }
     }
 
     private String getJspFiles(String webAppSourceDirectory)
@@ -345,7 +401,7 @@
      * Until Jasper supports the option to generate the srcs in a different dir
      * than the classes, this is the best we can do.
      * 
-     * @throws Exception
+     * @throws Exception if unable to clean srcs
      */
     public void cleanupSrcs() throws Exception
     {
@@ -391,7 +447,7 @@
      * If you dont specify the insertionMarker, then the fragment will be
      * inserted at the end of the file just before the &lt;/webapp&gt;
      * 
-     * @throws Exception
+     * @throws Exception if unable to merge the web xml
      */
     public void mergeWebXml() throws Exception
     {
@@ -406,58 +462,63 @@
                 return;
             }
 
-            File fragmentWebXml = new File(webXmlFragment);
-            if (!fragmentWebXml.exists())
+            File fragmentWebXml = new File(webXmlFragment);         
+            File mergedWebXml = new File(fragmentWebXml.getParentFile(), "web.xml");
+
+            try (BufferedReader webXmlReader = new BufferedReader(new FileReader(webXml));
+                 PrintWriter mergedWebXmlWriter = new PrintWriter(new FileWriter(mergedWebXml))) 
             {
-                getLog().info("No fragment web.xml file generated");
-            }
-            File mergedWebXml = new File(fragmentWebXml.getParentFile(),
-            "web.xml");
-            try (BufferedReader webXmlReader = new BufferedReader(new FileReader(
-                    webXml));
-                 PrintWriter mergedWebXmlWriter = new PrintWriter(new FileWriter(
-                    mergedWebXml))) {
 
-                // read up to the insertion marker or the </webapp> if there is no
-                // marker
-                boolean atInsertPoint = false;
-                boolean atEOF = false;
-                String marker = (insertionMarker == null
-                        || insertionMarker.equals("") ? END_OF_WEBAPP : insertionMarker);
-                while (!atInsertPoint && !atEOF)
+                if (!fragmentWebXml.exists())
                 {
-                    String line = webXmlReader.readLine();
-                    if (line == null)
-                        atEOF = true;
-                    else if (line.indexOf(marker) >= 0)
-                    {
-                        atInsertPoint = true;
-                    }
-                    else
-                    {
-                        mergedWebXmlWriter.println(line);
-                    }
-                }
-                
-                if (atEOF && !atInsertPoint)
-                    throw new IllegalStateException("web.xml does not contain insertionMarker "+insertionMarker);
-                
-                //put in a context init-param to flag that the contents have been precompiled
-                mergedWebXmlWriter.println("<context-param><param-name>"+PRECOMPILED_FLAG+"</param-name><param-value>true</param-value></context-param>");
-                
-
-                // put in the generated fragment
-                try (BufferedReader fragmentWebXmlReader = new BufferedReader(
-                        new FileReader(fragmentWebXml))) {
-                    IO.copy(fragmentWebXmlReader, mergedWebXmlWriter);
-
-                    // if we inserted just before the </web-app>, put it back in
-                    if (marker.equals(END_OF_WEBAPP))
-                        mergedWebXmlWriter.println(END_OF_WEBAPP);
-
-                    // copy in the rest of the original web.xml file
+                    getLog().info("No fragment web.xml file generated");
+                    //just copy existing web.xml to expected position
                     IO.copy(webXmlReader, mergedWebXmlWriter);
                 }
+                else
+                {
+                    // read up to the insertion marker or the </webapp> if there is no
+                    // marker
+                    boolean atInsertPoint = false;
+                    boolean atEOF = false;
+                    String marker = (insertionMarker == null
+                            || insertionMarker.equals("") ? END_OF_WEBAPP : insertionMarker);
+                    while (!atInsertPoint && !atEOF)
+                    {
+                        String line = webXmlReader.readLine();
+                        if (line == null)
+                            atEOF = true;
+                        else if (line.indexOf(marker) >= 0)
+                        {
+                            atInsertPoint = true;
+                        }
+                        else
+                        {
+                            mergedWebXmlWriter.println(line);
+                        }
+                    }
+
+                    if (atEOF && !atInsertPoint)
+                        throw new IllegalStateException("web.xml does not contain insertionMarker "+insertionMarker);
+
+                    //put in a context init-param to flag that the contents have been precompiled
+                    mergedWebXmlWriter.println("<context-param><param-name>"+PRECOMPILED_FLAG+"</param-name><param-value>true</param-value></context-param>");
+
+
+                    // put in the generated fragment
+                    try (BufferedReader fragmentWebXmlReader = 
+                            new BufferedReader(new FileReader(fragmentWebXml))) 
+                    {
+                        IO.copy(fragmentWebXmlReader, mergedWebXmlWriter);
+
+                        // if we inserted just before the </web-app>, put it back in
+                        if (marker.equals(END_OF_WEBAPP))
+                            mergedWebXmlWriter.println(END_OF_WEBAPP);
+
+                        // copy in the rest of the original web.xml file
+                        IO.copy(webXmlReader, mergedWebXmlWriter);
+                    }
+                }
             }
         }
     }
@@ -510,87 +571,63 @@
     }
     
     
-    private String setUpSysClassPath () throws Exception
+    
+    /**
+     * @return
+     * @throws MalformedURLException
+     */
+    private Set<URL> getPluginJars () throws MalformedURLException
     {
-        StringBuffer buff = new StringBuffer();
-        
-        //Put each of the plugin's artifacts onto the system classpath for jspc
+        HashSet<URL> pluginJars = new HashSet<>();
         for (Iterator<Artifact> iter = pluginArtifacts.iterator(); iter.hasNext(); )
         {
             Artifact pluginArtifact = iter.next();
             if ("jar".equalsIgnoreCase(pluginArtifact.getType()))
             {
                 if (getLog().isDebugEnabled()) { getLog().debug("Adding plugin artifact "+pluginArtifact);}
-                buff.append(pluginArtifact.getFile().getAbsolutePath());
-                if (iter.hasNext())
-                    buff.append(File.pathSeparator);
+                pluginJars.add(pluginArtifact.getFile().toURI().toURL());
             }
         }
         
+        return pluginJars;
+    }
+    
+    
+    
+    /**
+     * @param pluginJars
+     * @return
+     * @throws MalformedURLException
+     */
+    private Set<URL>  getProvidedScopeJars (Set<URL> pluginJars) throws MalformedURLException
+    {
+        if (!useProvidedScope)
+            return Collections.emptySet();
         
-        if (useProvidedScope)
-        {
-            for ( Iterator<Artifact> iter = projectArtifacts.iterator(); iter.hasNext(); )
-            {                   
-                Artifact artifact = iter.next();
-                if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope()))
+        HashSet<URL> providedJars = new HashSet<>();
+        
+        for ( Iterator<Artifact> iter = projectArtifacts.iterator(); iter.hasNext(); )
+        {                   
+            Artifact artifact = iter.next();
+            if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope()))
+            {
+                //test to see if the provided artifact was amongst the plugin artifacts
+                URL jar = artifact.getFile().toURI().toURL();
+                if (!pluginJars.contains(jar))
                 {
-                    //test to see if the provided artifact was amongst the plugin artifacts
-                    String path = artifact.getFile().getAbsolutePath();
-                    if (! buff.toString().contains(path))
-                    {
-                        if (buff.length() != 0)
-                            buff.append(File.pathSeparator);
-                        buff.append(path);
-                        if (getLog().isDebugEnabled()) { getLog().debug("Adding provided artifact: "+artifact);}
-                    }  
-                    else
-                    {
-                        if (getLog().isDebugEnabled()) { getLog().debug("Skipping provided artifact: "+artifact);}
-                    }
+                    providedJars.add(jar);
+                    if (getLog().isDebugEnabled()) { getLog().debug("Adding provided artifact: "+artifact);}
+                }  
+                else
+                {
+                    if (getLog().isDebugEnabled()) { getLog().debug("Skipping provided artifact: "+artifact);}
                 }
             }
         }
-
-        return buff.toString();
+        return providedJars;
     }
 
     
-    /**
-     * Glassfish jsp requires that we set up the list of system jars that have
-     * tlds in them.
-     * 
-     * This method is a little fragile, as it relies on knowing that the jstl jars
-     * are the only ones in the system path that contain tlds.
-     * @return
-     * @throws Exception
-     */
-    private List<URL> getSystemJarsWithTlds() throws Exception
-    {
-        getLog().debug("tld pattern=" + tldJarNamePatterns);   
-        final List<URL> list = new ArrayList<URL>();
-        List<URI> artifactUris = new ArrayList<URI>();
-        Pattern pattern = Pattern.compile(tldJarNamePatterns);
-        for (Iterator<Artifact> iter = pluginArtifacts.iterator(); iter.hasNext(); )
-        {
-            Artifact pluginArtifact = iter.next();
-            Resource res = Resource.newResource(pluginArtifact.getFile());
-            getLog().debug("scan jar: "+res.getURI());
-            artifactUris.add(res.getURI());
-        }
-        
-        PatternMatcher matcher = new PatternMatcher()
-        {
-            public void matched(URI uri) throws Exception
-            {
-                //uri of system artifact matches pattern defining list of jars known to contain tlds
-                list.add(uri.toURL());
-            }
-        };
-        matcher.match(pattern, artifactUris.toArray(new URI[artifactUris.size()]), false);
-        
-        return list;
-    }
     
     private File getWebXmlFile ()
     throws IOException
diff --git a/jetty-maven-plugin/pom.xml b/jetty-maven-plugin/pom.xml
index 503c88d..1d699d3 100644
--- a/jetty-maven-plugin/pom.xml
+++ b/jetty-maven-plugin/pom.xml
@@ -2,15 +2,17 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-maven-plugin</artifactId>
   <packaging>maven-plugin</packaging>
   <name>Jetty :: Jetty Maven Plugin</name>
+  <description>Jetty maven plugins</description>
   <properties>
     <mavenVersion>3.0.3</mavenVersion>
-    <pluginToolsVersion>3.1</pluginToolsVersion>
+    <pluginToolsVersion>3.4</pluginToolsVersion>
+    <bundle-symbolic-name>${project.groupId}.maven.plugin</bundle-symbolic-name>
   </properties>
   <build>
     <plugins>
@@ -29,7 +31,6 @@
             <id>exec-plugin-doc</id>
             <phase>generate-sources</phase>
             <goals>
-              <goal>xdoc</goal>
               <goal>helpmojo</goal>
             </goals>
           </execution>
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java
index 791477b..09b1c39 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java
@@ -18,7 +18,6 @@
 
 package org.eclipse.jetty.maven.plugin;
 
-
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.InputStream;
@@ -40,35 +39,23 @@
 import org.apache.maven.project.MavenProject;
 import org.codehaus.plexus.util.FileUtils;
 import org.eclipse.jetty.security.LoginService;
-import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.RequestLog;
+import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ShutdownMonitor;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
 import org.eclipse.jetty.server.handler.HandlerCollection;
-import org.eclipse.jetty.util.Scanner;
+import org.eclipse.jetty.util.PathWatcher;
 import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.xml.XmlConfiguration;
 
-
-
 /**
- * AbstractJettyMojo
- *
  * Common base class for most jetty mojos.
- * 
- * 
  */
 public abstract class AbstractJettyMojo extends AbstractMojo
 {
     /**
-     * 
-     */
-    public String PORT_SYSPROPERTY = "jetty.port";
-    
-    
-    /**
      * Whether or not to include dependencies on the plugin's classpath with &lt;scope&gt;provided&lt;/scope&gt;
      * Use WITH CAUTION as you may wind up with duplicate jars/classes.
      * 
@@ -77,7 +64,6 @@
      */
     protected boolean useProvidedScope;
     
-    
     /**
      * List of goals that are NOT to be used
      * 
@@ -86,9 +72,6 @@
      */
     protected String[] excludedGoals;
     
-
-  
-    
     /**
      * List of other contexts to set up. Consider using instead
      * the &lt;jettyXml&gt; element to specify external jetty xml config file. 
@@ -99,7 +82,6 @@
      */
     protected ContextHandler[] contextHandlers;
     
-    
     /**
      * List of security realms to set up. Consider using instead
      * the &lt;jettyXml&gt; element to specify external jetty xml config file. 
@@ -109,7 +91,6 @@
      * @parameter
      */
     protected LoginService[] loginServices;
-    
 
     /**
      * A RequestLog implementation to use for the webapp at runtime.
@@ -121,7 +102,6 @@
      */
     protected RequestLog requestLog;
     
-    
     /**
      * An instance of org.eclipse.jetty.webapp.WebAppContext that represents the webapp.
      * Use any of its setters to configure the webapp. This is the preferred and most
@@ -132,25 +112,23 @@
      */
     protected JettyWebAppContext webApp;
 
-
     /**
      * The interval in seconds to scan the webapp for changes 
      * and restart the context if necessary. Ignored if reload
      * is enabled. Disabled by default.
      * 
-     * @parameter expression="${jetty.scanIntervalSeconds}" default-value="0"
+     * @parameter property="jetty.scanIntervalSeconds" default-value="0"
      * @required
      */
     protected int scanIntervalSeconds;
     
-    
     /**
      * reload can be set to either 'automatic' or 'manual'
      *
      * if 'manual' then the context can be reloaded by a linefeed in the console
      * if 'automatic' then traditional reloading on changed files is enabled.
      * 
-     * @parameter expression="${jetty.reload}" default-value="automatic"
+     * @parameter property="jetty.reload" default-value="automatic"
      */
     protected String reload;
 
@@ -162,7 +140,7 @@
      * that have been set on the command line, by the JVM, or directly 
      * in the POM via systemProperties. Optional.
      * 
-     * @parameter expression="${jetty.systemPropertiesFile}"
+     * @parameter property="jetty.systemPropertiesFile"
      */
     protected File systemPropertiesFile;
 
@@ -208,17 +186,15 @@
     /**
      * Use the dump() facility of jetty to print out the server configuration to logging
      * 
-     * @parameter expression"${dumponStart}" default-value="false"
+     * @parameter property="dumponStart" default-value="false"
      */
     protected boolean dumpOnStart;
     
-   
-    
     
     /**  
      * Skip this mojo execution.
      * 
-     * @parameter expression="${jetty.skip}" default-value="false"
+     * @parameter property="jetty.skip" default-value="false"
      */
     protected boolean skip;
 
@@ -236,7 +212,7 @@
     /**
      * The maven project.
      *
-     * @parameter expression="${project}"
+     * @parameter default-value="${project}"
      * @readonly
      */
     protected MavenProject project;
@@ -245,14 +221,14 @@
     /**
      * The artifacts for the project.
      * 
-     * @parameter expression="${project.artifacts}"
+     * @parameter default-value="${project.artifacts}"
      * @readonly
      */
     protected Set projectArtifacts;
     
     
     /** 
-     * @parameter expression="${mojoExecution}" 
+     * @parameter default-value="${mojoExecution}" 
      * @readonly
      */
     protected org.apache.maven.plugin.MojoExecution execution;
@@ -261,7 +237,7 @@
     /**
      * The artifacts for the plugin itself.
      * 
-     * @parameter expression="${plugin.artifacts}"
+     * @parameter default-value="${plugin.artifacts}"
      * @readonly
      */
     protected List pluginArtifacts;
@@ -278,33 +254,27 @@
     
     /**
      * A wrapper for the Server object
+     * @parameter
      */
-    protected JettyServer server = new JettyServer();
+    protected Server server;
     
     
     /**
      * A scanner to check for changes to the webapp
      */
-    protected Scanner scanner;
+    protected PathWatcher scanner;
     
     
-    /**
-     *  List of files and directories to scan
-     */
-    protected ArrayList<File> scanList;
-    
-    
-    /**
-     * List of Listeners for the scanner
-     */
-    protected ArrayList<Scanner.BulkListener> scannerListeners;
-    
     
     /**
      * A scanner to check ENTER hits on the console
      */
     protected Thread consoleScanner;
     
+    protected ServerSupport serverSupport;
+    
+    
+    
     
     /**
      * <p>
@@ -361,9 +331,6 @@
     
     
     
-    /**
-     * @throws MojoExecutionException
-     */
     public void configurePluginClasspath() throws MojoExecutionException
     {  
         //if we are configured to include the provided dependencies on the plugin's classpath
@@ -403,13 +370,6 @@
         }
     }
     
-    
-    
-    
-    /**
-     * @param artifact
-     * @return
-     */
     public boolean isPluginArtifact(Artifact artifact)
     {
         if (pluginArtifacts == null || pluginArtifacts.isEmpty())
@@ -427,12 +387,6 @@
         return isPluginArtifact;
     }
 
-    
-    
-    
-    /**
-     * @throws Exception
-     */
     public void finishConfigurationBeforeStart() throws Exception
     {
         HandlerCollection contexts = (HandlerCollection)server.getChildHandlerByClass(ContextHandlerCollection.class);
@@ -445,31 +399,24 @@
         }
     }
 
-   
-   
-
-    /**
-     * @throws Exception
-     */
     public void applyJettyXml() throws Exception
-    {
-        if (getJettyXmlFiles() == null)
-            return;
-
-        this.server.applyXmlConfigurations(getJettyXmlFiles());
+    {        
+        Server tmp = ServerSupport.applyXmlConfigurations(server, getJettyXmlFiles());
+        if (server == null)
+            server = tmp;
+        
+        if (server == null)
+            server = new Server();
     }
 
-
-
-    
-    /**
-     * @throws MojoExecutionException
-     */
     public void startJetty () throws MojoExecutionException
     {
         try
         {
             getLog().debug("Starting Jetty Server ...");
+            
+            //make sure Jetty does not use URLConnection caches with the plugin
+            Resource.setDefaultUseCaches(false);
          
             configureMonitor();
             
@@ -478,54 +425,32 @@
             //apply any config from a jetty.xml file first which is able to
             //be overwritten by config in the pom.xml
             applyJettyXml ();      
-
+            
             // if a <httpConnector> was specified in the pom, use it
             if (httpConnector != null)
             {
                 // check that its port was set
                 if (httpConnector.getPort() <= 0)
                 {
-                    //use any jetty.port settings provided
-                    String tmp = System.getProperty(PORT_SYSPROPERTY, MavenServerConnector.DEFAULT_PORT_STR); 
+                    //use any jetty.http.port settings provided
+                    String tmp = System.getProperty(MavenServerConnector.PORT_SYSPROPERTY, System.getProperty("jetty.port", MavenServerConnector.DEFAULT_PORT_STR));
                     httpConnector.setPort(Integer.parseInt(tmp.trim()));
                 }  
-                if (httpConnector.getServer() == null)
-                    httpConnector.setServer(this.server);
-                this.server.addConnector(httpConnector);
+                httpConnector.setServer(server);
             }
 
-            // if the user hasn't configured the connectors in a jetty.xml file so use a default one
-            Connector[] connectors = this.server.getConnectors();
-            if (connectors == null|| connectors.length == 0)
-            {
-                //if <httpConnector> not configured in the pom, create one
-                if (httpConnector == null)
-                {
-                    httpConnector = new MavenServerConnector();               
-                    //use any jetty.port settings provided
-                    String tmp = System.getProperty(PORT_SYSPROPERTY, MavenServerConnector.DEFAULT_PORT_STR);
-                    httpConnector.setPort(Integer.parseInt(tmp.trim()));
-                }
-                if (httpConnector.getServer() == null)
-                    httpConnector.setServer(this.server);
-                this.server.setConnectors(new Connector[] {httpConnector});
-            }
+            ServerSupport.configureConnectors(server, httpConnector);
 
-            //set up a RequestLog if one is provided
-            if (this.requestLog != null)
-                this.server.setRequestLog(this.requestLog);
-
-            //set up the webapp and any context provided
-            this.server.configureHandlers();
+            //set up a RequestLog if one is provided and the handle structure
+            ServerSupport.configureHandlers(server, this.requestLog);
+            
+            //Set up list of default Configurations to apply to a webapp
+            ServerSupport.configureDefaultConfigurationClasses(server);
             configureWebApplication();
-            this.server.addWebApplication(webApp);
+            ServerSupport.addWebApplication(server, webApp);
 
             // set up security realms
-            for (int i = 0; (this.loginServices != null) && i < this.loginServices.length; i++)
-            {
-                getLog().debug(this.loginServices[i].getClass().getName() + ": "+ this.loginServices[i].toString());
-                this.server.addBean(this.loginServices[i]);
-            }
+            ServerSupport.configureLoginServices(server, loginServices);
 
             //do any other configuration required by the
             //particular Jetty version
@@ -535,17 +460,20 @@
             this.server.start();
 
             getLog().info("Started Jetty Server");
-           
-            
+
             if ( dumpOnStart )
             {
                 getLog().info(this.server.dump());
             }
-            
+
             // start the scanner thread (if necessary) on the main webapp
-            configureScanner ();
-            startScanner();
-            
+            if (isScanningEnabled())
+            {
+                scanner = new PathWatcher();
+                configureScanner ();
+                startScanner();
+            }
+
             // start the new line scanner thread if necessary
             startConsoleScanner();
 
@@ -581,11 +509,15 @@
     }
 
     
+    
+    
+    
+    
     /**
      * Subclasses should invoke this to setup basic info
      * on the webapp
      * 
-     * @throws MojoExecutionException
+     * @throws Exception if unable to configure web application 
      */
     public void configureWebApplication () throws Exception
     {
@@ -635,39 +567,40 @@
      * Run a scanner thread on the given list of files and directories, calling
      * stop/start on the given list of LifeCycle objects if any of the watched
      * files change.
-     *
+     * @throws Exception if unable to start scanner 
      */
-    private void startScanner() throws Exception
+    public void startScanner() throws Exception
     {
-        // check if scanning is enabled
-        if (scanIntervalSeconds <= 0) return;
-
-        // check if reload is manual. It disables file scanning
-        if ( "manual".equalsIgnoreCase( reload ) )
-        {
-            // issue a warning if both scanIntervalSeconds and reload
-            // are enabled
-            getLog().warn("scanIntervalSeconds is set to " + scanIntervalSeconds + " but will be IGNORED due to manual reloading");
+        if (!isScanningEnabled())
             return;
-        }
 
-        scanner = new Scanner();
-        scanner.setReportExistingFilesOnStartup(false);
-        scanner.setScanInterval(scanIntervalSeconds);
-        scanner.setScanDirs(scanList);
-        scanner.setRecursive(true);
-        Iterator itor = (this.scannerListeners==null?null:this.scannerListeners.iterator());
-        while (itor!=null && itor.hasNext())
-            scanner.addListener((Scanner.Listener)itor.next());
-        getLog().info("Starting scanner at interval of " + scanIntervalSeconds + " seconds.");
+        scanner.setNotifyExistingOnStart(false);
+       
+       
         scanner.start();
     }
     
     
+    public boolean isScanningEnabled ()
+    {
+        if (scanIntervalSeconds <=0 || "manual".equalsIgnoreCase( reload ))
+            return false;
+        return true;
+    }
+    
+    public void stopScanner() throws Exception
+    {
+        if (!isScanningEnabled())
+            return;
+        
+        if (scanner != null)
+            scanner.stop();
+    }
     
     
     /**
      * Run a thread that monitors the console input to detect ENTER hits.
+     * @throws Exception if unable to start the console
      */
     protected void startConsoleScanner() throws Exception
     {
@@ -679,12 +612,6 @@
         }       
     }
 
-    
-    
-    
-    /**
-     * 
-     */
     protected void printSystemProperties ()
     {
         // print out which system properties were set up
@@ -702,13 +629,10 @@
         }
     }
 
-    
-    
-    
     /**
      * Try and find a jetty-web.xml file, using some
      * historical naming conventions if necessary.
-     * @param webInfDir
+     * @param webInfDir the web inf directory
      * @return the jetty web xml file
      */
     public File findJettyWebXmlFile (File webInfDir)
@@ -730,13 +654,6 @@
         return null;
     }
 
-
-   
-    
-    /**
-     * @param file
-     * @throws Exception
-     */
     public void setSystemPropertiesFile(File file) throws Exception
     {
         this.systemPropertiesFile = file;
@@ -762,12 +679,6 @@
         } 
     }
     
-    
-    
-    
-    /**
-     * @param systemProperties
-     */
     public void setSystemProperties(SystemProperties systemProperties)
     {
         if (this.systemProperties == null)
@@ -781,15 +692,6 @@
         }
     }
     
-
-    
-
-    
-    
-    
-    /**
-     * @return
-     */
     public List<File> getJettyXmlFiles()
     {
         if ( this.jettyXml == null )
@@ -816,12 +718,6 @@
         return jettyXmlFiles;
     }
 
-    
-    
-    /**
-     * @param goal
-     * @return
-     */
     public boolean isExcluded (String goal)
     {
         if (excludedGoals == null || goal == null)
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ConsoleScanner.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ConsoleScanner.java
index 7e8a84e..0f32c54 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ConsoleScanner.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ConsoleScanner.java
@@ -20,9 +20,6 @@
 
 import java.io.IOException;
 
-
-
-
 /**
  * ConsoleScanner
  *
@@ -30,16 +27,8 @@
  */
 public class ConsoleScanner extends Thread 
 {
-    
     private final AbstractJettyMojo mojo;
     
-    
-    
-    
-    
-    /**
-     * @param mojo
-     */
     public ConsoleScanner(AbstractJettyMojo mojo) 
     {
         this.mojo = mojo;
@@ -47,12 +36,6 @@
         setDaemon(true);
     }
     
-    
-    
-    
-    /** 
-     * @see java.lang.Thread#run()
-     */
     public void run() 
     {  
         try 
@@ -69,12 +52,6 @@
         }
     }
     
-    
-    
-    
-    /**
-     * 
-     */
     private void getSomeSleep() 
     {
         try 
@@ -87,12 +64,6 @@
         }
     }
     
-    
-    
-    
-    /**
-     * @throws IOException
-     */
     private void checkSystemInput() throws IOException 
     {     
         while (System.in.available() > 0) {
@@ -107,9 +78,6 @@
         }
     }
     
-    
-    
-    
     /**
      * Skip buffered bytes of system console.
      */
@@ -136,12 +104,6 @@
         }      
     }
     
-    
-    
-    
-    /**
-     * 
-     */
     private void restartWebApp()
     {
         try
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyDeployWar.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyDeployWar.java
index 28b1420..96ea192 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyDeployWar.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyDeployWar.java
@@ -18,8 +18,6 @@
 

 package org.eclipse.jetty.maven.plugin;

 

-import java.io.File;

-

 import org.apache.maven.plugin.MojoExecutionException;

 import org.apache.maven.plugin.MojoFailureException;

 

@@ -74,8 +72,7 @@
     {

         super.finishConfigurationBeforeStart();

         //only stop the server at shutdown if we are blocking

-        server.setStopAtShutdown(!nonblocking);

-       

+        server.setStopAtShutdown(!nonblocking); 

     }

 

 }

diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyEffectiveWebXml.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyEffectiveWebXml.java
index e0f9b8d..30785b4 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyEffectiveWebXml.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyEffectiveWebXml.java
@@ -42,20 +42,21 @@
     /**
      * The target directory
      * 
-     * @parameter expression="${project.build.directory}"
+     * @parameter default-value="${project.build.directory}"
      * @required
      * @readonly
      */
     protected File target;
     
     /**
-     * The target directory
+     * The name of the file to generate into
      * 
      * @parameter 
      */
     protected File effectiveWebXml;
     
     
+    
     protected boolean deleteOnExit = true;
     
 
@@ -73,16 +74,7 @@
     {
         //Only do enough setup to be able to produce a quickstart-web.xml file 
         
-        //if the user didn't nominate a file to generate into, pick the name and
-        //make sure that it is deleted on exit
-        if (effectiveWebXml == null)
-        {
-            deleteOnExit = true;
-            effectiveWebXml = new File(target, "effective-web.xml");
-            effectiveWebXml.deleteOnExit();
-        }
-        
-        Resource descriptor = Resource.newResource(effectiveWebXml);
+
         
         QueuedThreadPool tpool = null;
         
@@ -93,27 +85,41 @@
             //apply any config from a jetty.xml file first to our "fake" server instance
             //TODO probably not necessary
             applyJettyXml ();  
-
         
-            server.configureHandlers();
+            ServerSupport.configureHandlers(server, null);
+            ServerSupport.configureDefaultConfigurationClasses(server);
                    
             //ensure config of the webapp based on settings in plugin
             configureWebApplication();
             
-            
             //set the webapp up to do very little other than generate the quickstart-web.xml
             webApp.setCopyWebDir(false);
             webApp.setCopyWebInf(false);
             webApp.setGenerateQuickStart(true);
-    
-            if (!effectiveWebXml.getParentFile().exists())
-                effectiveWebXml.getParentFile().mkdirs();
-            if (!effectiveWebXml.exists())
-                effectiveWebXml.createNewFile();
+
+
+            //if the user didn't nominate a file to generate into, pick the name and
+            //make sure that it is deleted on exit
+            if (webApp.getQuickStartWebDescriptor() == null)
+            {
+                if (effectiveWebXml == null)
+                {
+                    deleteOnExit = true;
+                    effectiveWebXml = new File(target, "effective-web.xml");
+                    effectiveWebXml.deleteOnExit();
+                }
+
+                Resource descriptor = Resource.newResource(effectiveWebXml);
+
+                if (!effectiveWebXml.getParentFile().exists())
+                    effectiveWebXml.getParentFile().mkdirs();
+                if (!effectiveWebXml.exists())
+                    effectiveWebXml.createNewFile();
+
+                webApp.setQuickStartWebDescriptor(descriptor);
+            }
             
-            webApp.setQuickStartWebDescriptor(descriptor);
-            
-            server.addWebApplication(webApp);
+            ServerSupport.addWebApplication(server, webApp);
                        
             //if our server has a thread pool associated we can do any annotation scanning multithreaded,
             //otherwise scanning will be single threaded
@@ -143,7 +149,7 @@
             try
             {
                 //just show the result in the log
-                getLog().info(IO.toString(descriptor.getInputStream()));
+                getLog().info(IO.toString(webApp.getQuickStartWebDescriptor().getInputStream()));
             }
             catch (IOException e)
             {
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java
index fd14e99..c4d0300 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java
@@ -45,7 +45,7 @@
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
 import org.eclipse.jetty.annotations.AnnotationConfiguration;
-import org.eclipse.jetty.quickstart.QuickStartDescriptorGenerator;
+import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.util.resource.ResourceCollection;
@@ -53,25 +53,20 @@
 
 
 /**
+ * This goal is used to deploy your unassembled webapp into a forked JVM.
  * <p>
- *  This goal is used to deploy your unassembled webapp into a forked JVM.
- *  </p>
- *  <p>
- *  You need to define a jetty.xml file to configure connectors etc. You can use the normal setters of o.e.j.webapp.WebAppContext on the <b>webApp</b>
- *  configuration element for this plugin. You may also need context xml file for any particularly complex webapp setup.
- *  about your webapp.
- *  </p>
- *  <p>
- *  Unlike the other jetty goals, this does NOT support the <b>scanIntervalSeconds</b> parameter: the webapp will be deployed only once.
- *  </p>
- *  <p>
- *  The <b>stopKey</b>, <b>stopPort</b> configuration elements can be used to control the stopping of the forked process. By default, this plugin will launch
- *  the forked jetty instance and wait for it to complete (in which case it acts much like the <b>jetty:run</b> goal, and you will need to Cntrl-C to stop).
- *  By setting the configuration element <b>waitForChild</b> to <b>false</b>, the plugin will terminate after having forked the jetty process. In this case
- *  you can use the <b>jetty:stop</b> goal to terminate the process.
- *  <p>
- *  See <a href="http://www.eclipse.org/jetty/documentation/">http://www.eclipse.org/jetty/documentation</a> for more information on this and other jetty plugins.
- *  </p>
+ * You need to define a jetty.xml file to configure connectors etc. You can use the normal setters of o.e.j.webapp.WebAppContext on the <b>webApp</b>
+ * configuration element for this plugin. You may also need context xml file for any particularly complex webapp setup.
+ * about your webapp.
+ * <p>
+ * Unlike the other jetty goals, this does NOT support the <b>scanIntervalSeconds</b> parameter: the webapp will be deployed only once.
+ * <p>
+ * The <b>stopKey</b>, <b>stopPort</b> configuration elements can be used to control the stopping of the forked process. By default, this plugin will launch
+ * the forked jetty instance and wait for it to complete (in which case it acts much like the <b>jetty:run</b> goal, and you will need to Cntrl-C to stop).
+ * By setting the configuration element <b>waitForChild</b> to <b>false</b>, the plugin will terminate after having forked the jetty process. In this case
+ * you can use the <b>jetty:stop</b> goal to terminate the process.
+ * <p>
+ * See <a href="http://www.eclipse.org/jetty/documentation/">http://www.eclipse.org/jetty/documentation</a> for more information on this and other jetty plugins.
  * 
  * @goal run-forked
  * @requiresDependencyResolution test
@@ -81,21 +76,10 @@
  */
 public class JettyRunForkedMojo extends JettyRunMojo
 {    
-    public static final String DEFAULT_WEBAPP_SRC = "src"+File.separator+"main"+File.separator+"webapp";
-    public static final String FAKE_WEBAPP = "webapp-tmp";
-    
-    
-    public String PORT_SYSPROPERTY = "jetty.port";
-
-    
- 
-    
-    
-    
     /**
      * The target directory
      * 
-     * @parameter expression="${project.build.directory}"
+     * @parameter default-value="${project.build.directory}"
      * @required
      * @readonly
      */
@@ -104,34 +88,34 @@
     /**
      * The file into which to generate the quickstart web xml for the forked process to use
      * 
-     * @parameter expression="${project.build.directory}/fork-web.xml"
+     * @parameter default-value="${project.build.directory}/fork-web.xml"
      */
     protected File forkWebXml;
     
     
     /**
      * Arbitrary jvm args to pass to the forked process
-     * @parameter expression="${jetty.jvmArgs}"
+     * @parameter property="jetty.jvmArgs"
      */
     private String jvmArgs;
     
     
     /**
-     * @parameter expression="${plugin.artifacts}"
+     * @parameter default-value="${plugin.artifacts}"
      * @readonly
      */
     private List pluginArtifacts;
     
     
     /**
-     * @parameter expression="${plugin}"
+     * @parameter default-value="${plugin}"
      * @readonly
      */
     private PluginDescriptor plugin;
     
     
     /**
-     * @parameter expression="true" default-value="true"
+     * @parameter default-value="true"
      */
     private boolean waitForChild;
 
@@ -249,42 +233,49 @@
     {
         //Only do enough setup to be able to produce a quickstart-web.xml file to
         //pass onto the forked process to run     
-        
-        if (forkWebXml == null)
-            forkWebXml = new File (target, "fork-web.xml");
-        
+
         try
         {
             printSystemProperties();
 
             //do NOT apply the jettyXml configuration - as the jvmArgs may be needed for it to work 
+            if (server == null)
+                server = new Server();
 
             //ensure handler structure enabled
-            server.configureHandlers();
+            ServerSupport.configureHandlers(server, null);
+            
+            ServerSupport.configureDefaultConfigurationClasses(server);
                    
             //ensure config of the webapp based on settings in plugin
             configureWebApplication();
             
             //copy the base resource as configured by the plugin
             originalBaseResource = webApp.getBaseResource();
-            
+
             //get the original persistance setting
             originalPersistTemp = webApp.isPersistTempDirectory();
-            
+
             //set the webapp up to do very little other than generate the quickstart-web.xml
             webApp.setCopyWebDir(false);
             webApp.setCopyWebInf(false);
             webApp.setGenerateQuickStart(true);
-         
-            if (!forkWebXml.getParentFile().exists())
-                forkWebXml.getParentFile().mkdirs();
-            if (!forkWebXml.exists())
-                forkWebXml.createNewFile();
-            
-            webApp.setQuickStartWebDescriptor(Resource.newResource(forkWebXml));
+
+            if (webApp.getQuickStartWebDescriptor() == null)
+            {
+                if (forkWebXml == null)
+                    forkWebXml = new File (target, "fork-web.xml");
+
+                if (!forkWebXml.getParentFile().exists())
+                    forkWebXml.getParentFile().mkdirs();
+                if (!forkWebXml.exists())
+                    forkWebXml.createNewFile();
+
+                webApp.setQuickStartWebDescriptor(Resource.newResource(forkWebXml));
+            }
             
             //add webapp to our fake server instance
-            server.addWebApplication(webApp);
+            ServerSupport.addWebApplication(server, webApp);
                        
             //if our server has a thread pool associated we can do annotation scanning multithreaded,
             //otherwise scanning will be single threaded
@@ -431,13 +422,6 @@
         }
     }
 
-
-
-
-    /**
-     * @return
-     * @throws MojoExecutionException
-     */
     public List<String> getProvidedJars() throws MojoExecutionException
     {  
         //if we are configured to include the provided dependencies on the plugin's classpath
@@ -464,13 +448,6 @@
             return null;
     }
     
-   
-    
-    
-    /**
-     * @return
-     * @throws MojoExecutionException
-     */
     public File prepareConfiguration() throws MojoExecutionException
     {
         try
@@ -610,15 +587,6 @@
         return warArtifacts;
     }
     
-    
-    
-    
-    
-    
-    /**
-     * @param artifact
-     * @return
-     */
     public boolean isPluginArtifact(Artifact artifact)
     {
         if (pluginArtifacts == null || pluginArtifacts.isEmpty())
@@ -636,13 +604,6 @@
         return isPluginArtifact;
     }
     
-    
-    
-    
-    /**
-     * @return
-     * @throws Exception
-     */
     private Set<Artifact> getExtraJars()
     throws Exception
     {
@@ -668,15 +629,6 @@
         return extraJars;
     }
 
-    
-
-   
-
-    
-    /**
-     * @return
-     * @throws Exception
-     */
     public String getContainerClassPath() throws Exception
     {
         StringBuilder classPath = new StringBuilder();
@@ -742,13 +694,6 @@
         return "java";
     }
     
-
-    
-    
-    /**
-     * @param path
-     * @return
-     */
     public static String fileSeparators(String path)
     {
         StringBuilder ret = new StringBuilder();
@@ -766,13 +711,6 @@
         return ret.toString();
     }
 
-
-    
-    
-    /**
-     * @param path
-     * @return
-     */
     public static String pathSeparators(String path)
     {
         StringBuilder ret = new StringBuilder();
@@ -790,24 +728,11 @@
         return ret.toString();
     }
 
-
-    
-    
-    /**
-     * @return
-     */
     private String createToken ()
     {
         return Long.toString(random.nextLong()^System.currentTimeMillis(), 36).toUpperCase(Locale.ENGLISH);
     }
     
-
-    
-    
-    /**
-     * @param mode
-     * @param inputStream
-     */
     private void startPump(String mode, InputStream inputStream)
     {
         ConsoleStreamer pump = new ConsoleStreamer(mode,inputStream);
@@ -816,13 +741,6 @@
         thread.start();
     }
 
-
-    
-    
-    /**
-     * @param strings
-     * @return
-     */
     private String toCSV (List<String> strings)
     {
         if (strings == null)
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java
index c059a5b..0fe35ff 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java
@@ -31,33 +31,27 @@
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
-import org.codehaus.plexus.util.FileUtils;
-import org.eclipse.jetty.util.Scanner;
+import org.eclipse.jetty.util.PathWatcher;
+import org.eclipse.jetty.util.PathWatcher.PathWatchEvent;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.webapp.WebAppContext;
 
-
 /**
- *  <p>
  *  This goal is used in-situ on a Maven project without first requiring that the project 
  *  is assembled into a war, saving time during the development cycle.
+ *  <p>
  *  The plugin forks a parallel lifecycle to ensure that the "compile" phase has been completed before invoking Jetty. This means
  *  that you do not need to explicity execute a "mvn compile" first. It also means that a "mvn clean jetty:run" will ensure that
  *  a full fresh compile is done before invoking Jetty.
- *  </p>
  *  <p>
  *  Once invoked, the plugin can be configured to run continuously, scanning for changes in the project and automatically performing a 
  *  hot redeploy when necessary. This allows the developer to concentrate on coding changes to the project using their IDE of choice and have those changes
  *  immediately and transparently reflected in the running web container, eliminating development time that is wasted on rebuilding, reassembling and redeploying.
- *  </p>
  *  <p>
  *  You may also specify the location of a jetty.xml file whose contents will be applied before any plugin configuration.
  *  This can be used, for example, to deploy a static webapp that is not part of your maven build. 
- *  </p>
  *  <p>
  *  There is a <a href="http://www.eclipse.org/jetty/documentation/current/maven-and-jetty.html">reference guide</a> to the configuration parameters for this plugin.
- *  </p>
- * 
  * 
  * @goal run
  * @requiresDependencyResolution test
@@ -85,7 +79,7 @@
      * The default location of the web.xml file. Will be used
      * if &lt;webApp&gt;&lt;descriptor&gt; is not set.
      * 
-     * @parameter expression="${maven.war.webxml}"
+     * @parameter default-value="${maven.war.webxml}"
      * @readonly
      */
     protected String webXml;
@@ -94,26 +88,41 @@
     /**
      * The directory containing generated classes.
      *
-     * @parameter expression="${project.build.outputDirectory}"
+     * @parameter default-value="${project.build.outputDirectory}"
      * @required
      * 
      */
     protected File classesDirectory;
     
+    /**
+     * An optional pattern for includes/excludes of classes in the classesDirectory
+     * @parameter
+     */
+    protected ScanPattern scanClassesPattern;
+    
+    
+    
     
     /**
      * The directory containing generated test classes.
      * 
-     * @parameter expression="${project.build.testOutputDirectory}"
+     * @parameter default-value="${project.build.testOutputDirectory}"
      * @required
      */
     protected File testClassesDirectory;
     
+    /**
+     * An optional pattern for includes/excludes of classes in the testClassesDirectory
+     * @parameter
+     */
+    protected ScanPattern scanTestClassesPattern;
+    
+   
     
     /**
      * Root directory for all html/jsp etc files
      *
-     * @parameter expression="${maven.war.src}"
+     * @parameter default-value="${maven.war.src}"
      * 
      */
     protected File webAppSourceDirectory;
@@ -136,12 +145,6 @@
 
     
     /**
-     * Extra scan targets as a list
-     */
-    protected List<File> extraScanTargets;
-    
-    
-    /**
      * maven-war-plugin reference
      */
     protected WarPluginInfo warPluginInfo;
@@ -232,59 +235,6 @@
         {
             throw new MojoExecutionException("Location of classesDirectory does not exist");
         }
-        
-        extraScanTargets = new ArrayList<File>();
-        if (scanTargets != null)
-        {            
-            for (int i=0; i< scanTargets.length; i++)
-            {
-                getLog().info("Added extra scan target:"+ scanTargets[i]);
-                extraScanTargets.add(scanTargets[i]);
-            }            
-        }
-        
-        if (scanTargetPatterns!=null)
-        {
-            for (int i=0;i<scanTargetPatterns.length; i++)
-            {
-                Iterator itor = scanTargetPatterns[i].getIncludes().iterator();
-                StringBuffer strbuff = new StringBuffer();
-                while (itor.hasNext())
-                {
-                    strbuff.append((String)itor.next());
-                    if (itor.hasNext())
-                        strbuff.append(",");
-                }
-                String includes = strbuff.toString();
-                
-                itor = scanTargetPatterns[i].getExcludes().iterator();
-                strbuff= new StringBuffer();
-                while (itor.hasNext())
-                {
-                    strbuff.append((String)itor.next());
-                    if (itor.hasNext())
-                        strbuff.append(",");
-                }
-                String excludes = strbuff.toString();
-
-                try
-                {
-                    List<File> files = FileUtils.getFiles(scanTargetPatterns[i].getDirectory(), includes, excludes);
-                    itor = files.iterator();
-                    while (itor.hasNext())
-                        getLog().info("Adding extra scan target from pattern: "+itor.next());
-                    List<File> currentTargets = extraScanTargets;
-                    if(currentTargets!=null && !currentTargets.isEmpty())
-                        currentTargets.addAll(files);
-                    else
-                        extraScanTargets = files;
-                }
-                catch (IOException e)
-                {
-                    throw new MojoExecutionException(e.getMessage());
-                }
-            }
-        }
     }
 
    
@@ -329,9 +279,7 @@
        //get copy of a list of war artifacts
        Set<Artifact> matchedWarArtifacts = new HashSet<Artifact>();
 
-       //make sure each of the war artifacts is added to the scanner
-       for (Artifact a:getWarArtifacts())
-           extraScanTargets.add(a.getFile());
+
 
        //process any overlays and the war type artifacts
        List<Overlay> overlays = new ArrayList<Overlay>();
@@ -420,77 +368,36 @@
     public void configureScanner ()
     throws MojoExecutionException
     {
-        // start the scanner thread (if necessary) on the main webapp
-        scanList = new ArrayList<File>();
-        if (webApp.getDescriptor() != null)
+        try
         {
-            try (Resource r = Resource.newResource(webApp.getDescriptor());)
-            {
-                scanList.add(r.getFile());
-            }
-            catch (IOException e)
-            {
-                throw new MojoExecutionException("Problem configuring scanner for web.xml", e);
-            }
+            gatherScannables();
+        }
+        catch (Exception e)
+        {
+            throw new MojoExecutionException("Error forming scan list", e);
         }
 
-        if (webApp.getJettyEnvXml() != null)
+        scanner.addListener(new PathWatcher.EventListListener()
         {
-            try (Resource r = Resource.newResource(webApp.getJettyEnvXml());)
-            {
-                scanList.add(r.getFile());
-            }
-            catch (IOException e)
-            {
-                throw new MojoExecutionException("Problem configuring scanner for jetty-env.xml", e);
-            }
-        }
 
-        if (webApp.getDefaultsDescriptor() != null)
-        {
-            try (Resource r = Resource.newResource(webApp.getDefaultsDescriptor());)
-            {
-                if (!WebAppContext.WEB_DEFAULTS_XML.equals(webApp.getDefaultsDescriptor()))
-                    scanList.add(r.getFile());
-            }
-            catch (IOException e)
-            {
-                throw new MojoExecutionException("Problem configuring scanner for webdefaults.xml", e);
-            }
-        }
-        
-        if (webApp.getOverrideDescriptor() != null)
-        {
-            try (Resource r = Resource.newResource(webApp.getOverrideDescriptor());)
-            {
-                scanList.add(r.getFile());
-            }
-            catch (IOException e)
-            {
-                throw new MojoExecutionException("Problem configuring scanner for webdefaults.xml", e);
-            }
-        }
-        
-        
-        File jettyWebXmlFile = findJettyWebXmlFile(new File(webAppSourceDirectory,"WEB-INF"));
-        if (jettyWebXmlFile != null)
-            scanList.add(jettyWebXmlFile);
-        scanList.addAll(extraScanTargets);
-        scanList.add(project.getFile());
-        if (webApp.getTestClasses() != null)
-            scanList.add(webApp.getTestClasses());
-        if (webApp.getClasses() != null)
-        scanList.add(webApp.getClasses());
-        scanList.addAll(webApp.getWebInfLib());
-     
-        scannerListeners = new ArrayList<Scanner.BulkListener>();
-        scannerListeners.add(new Scanner.BulkListener()
-        {
-            public void filesChanged (List changes)
+            @Override
+            public void onPathWatchEvents(List<PathWatchEvent> events)
             {
                 try
                 {
-                    boolean reconfigure = changes.contains(project.getFile().getCanonicalPath());
+                    boolean reconfigure = false;
+                    if (events != null)
+                    {
+                        for (PathWatchEvent e:events)
+                        {
+                            if (e.getPath().equals(project.getFile().toPath()))
+                            {
+                                reconfigure = true;
+                                break;
+                            }
+                        }
+                    }
+
                     restartWebApp(reconfigure);
                 }
                 catch (Exception e)
@@ -502,7 +409,115 @@
     }
 
     
-    
+    public void gatherScannables() throws Exception
+    {
+        if (webApp.getDescriptor() != null)
+        {
+            Resource r = Resource.newResource(webApp.getDescriptor());
+            scanner.watch(r.getFile().toPath());
+        }
+        
+        if (webApp.getJettyEnvXml() != null)
+            scanner.watch(new File(webApp.getJettyEnvXml()).toPath());
+
+        if (webApp.getDefaultsDescriptor() != null)
+        {
+            if (!WebAppContext.WEB_DEFAULTS_XML.equals(webApp.getDefaultsDescriptor()))
+                scanner.watch(new File(webApp.getDefaultsDescriptor()).toPath());
+        }
+
+        if (webApp.getOverrideDescriptor() != null)
+        {
+            scanner.watch(new File(webApp.getOverrideDescriptor()).toPath());
+        }
+        
+        File jettyWebXmlFile = findJettyWebXmlFile(new File(webAppSourceDirectory,"WEB-INF"));
+        if (jettyWebXmlFile != null)
+        {
+            scanner.watch(jettyWebXmlFile.toPath());
+        }
+        
+        //make sure each of the war artifacts is added to the scanner
+        for (Artifact a:getWarArtifacts())
+        {
+            scanner.watch(a.getFile().toPath());
+        }
+        
+        //handle the explicit extra scan targets
+        if (scanTargets != null)
+        {
+            for (File f:scanTargets)
+            {
+                if (f.isDirectory())
+                {
+                    PathWatcher.Config config = new PathWatcher.Config(f.toPath());
+                    config.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
+                    scanner.watch(config);
+                }
+                else
+                    scanner.watch(f.toPath());
+            }
+        }
+        
+        //handle the extra scan patterns
+        if (scanTargetPatterns != null)
+        {
+            for (ScanTargetPattern p:scanTargetPatterns)
+            {
+                PathWatcher.Config config = new PathWatcher.Config(p.getDirectory().toPath());
+                config.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
+                for (String pattern:p.getExcludes())
+                    config.addExcludeGlobRelative(pattern);
+                for (String pattern:p.getIncludes())
+                    config.addIncludeGlobRelative(pattern);
+                scanner.watch(config);
+            }
+        }
+      
+
+        scanner.watch(project.getFile().toPath());
+
+        if (webApp.getTestClasses() != null && webApp.getTestClasses().exists())
+        {
+            PathWatcher.Config config = new PathWatcher.Config(webApp.getTestClasses().toPath());
+            config.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);           
+            if (scanTestClassesPattern != null)
+            {
+                for (String p:scanTestClassesPattern.getExcludes())
+                    config.addExcludeGlobRelative(p);
+                for (String p:scanTestClassesPattern.getIncludes())
+                    config.addIncludeGlobRelative(p);
+            }
+            scanner.watch(config);
+        }
+        
+        if (webApp.getClasses() != null && webApp.getClasses().exists())
+        {
+            PathWatcher.Config config = new PathWatcher.Config(webApp.getClasses().toPath());
+            config.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
+            if (scanClassesPattern != null)
+            {
+                for (String p:scanClassesPattern.getExcludes())
+                    config.addExcludeGlobRelative(p);
+
+                for (String p:scanClassesPattern.getIncludes())
+                    config.addIncludeGlobRelative(p);
+
+            }
+            scanner.watch(config);
+        }
+
+        if (webApp.getWebInfLib() != null)
+        {
+            for (File f:webApp.getWebInfLib())
+            {
+                PathWatcher.Config config = new PathWatcher.Config(f.toPath());
+                config.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
+                scanner.watch(config);
+            }
+        }
+    }
+
     
     /** 
      * @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#restartWebApp(boolean)
@@ -511,7 +526,9 @@
     {
         getLog().info("restarting "+webApp);
         getLog().debug("Stopping webapp ...");
+        stopScanner();
         webApp.stop();
+
         getLog().debug("Reconfiguring webapp ...");
  
         checkPomConfiguration();
@@ -522,23 +539,14 @@
         if (reconfigureScanner)
         {
             getLog().info("Reconfiguring scanner after change to pom.xml ...");
-            scanList.clear();
-            if (webApp.getDescriptor() != null)
-                scanList.add(new File(webApp.getDescriptor()));
-            if (webApp.getJettyEnvXml() != null)
-                scanList.add(new File(webApp.getJettyEnvXml()));
-            scanList.addAll(extraScanTargets);
-            scanList.add(project.getFile());
-            if (webApp.getTestClasses() != null)
-                scanList.add(webApp.getTestClasses());
-            if (webApp.getClasses() != null)
-            scanList.add(webApp.getClasses());
-            scanList.addAll(webApp.getWebInfLib());
-            scanner.setScanDirs(scanList);
+            scanner.reset();
+            warArtifacts = null;
+            configureScanner();
         }
 
         getLog().debug("Restarting webapp ...");
         webApp.start();
+        startScanner();
         getLog().info("Restart completed at "+new Date().toString());
     }
     
@@ -588,8 +596,8 @@
         warArtifacts = new ArrayList<Artifact>();
         for ( Iterator<Artifact> iter = projectArtifacts.iterator(); iter.hasNext(); )
         {
-            Artifact artifact = (Artifact) iter.next();            
-            if (artifact.getType().equals("war"))
+            Artifact artifact = (Artifact) iter.next(); 
+            if (artifact.getType().equals("war") || artifact.getType().equals("zip"))
             {
                 try
                 {                  
@@ -605,13 +613,6 @@
         return warArtifacts;
     }
 
-    
-    
-    /**
-     * @param o
-     * @param warArtifacts
-     * @return
-     */
     protected Artifact getArtifactForOverlay (OverlayConfig o, List<Artifact> warArtifacts)
     {
         if (o == null || warArtifacts == null || warArtifacts.isEmpty())
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarExplodedMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarExplodedMojo.java
index 59d8976..fc75cad 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarExplodedMojo.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarExplodedMojo.java
@@ -19,12 +19,12 @@
 package org.eclipse.jetty.maven.plugin;
 
 import java.io.File;
-import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
-import org.eclipse.jetty.util.Scanner;
+import org.eclipse.jetty.util.PathWatcher;
+import org.eclipse.jetty.util.PathWatcher.PathWatchEvent;
 
 /**
  * 
@@ -52,7 +52,7 @@
     /**
      * The location of the war file.
      * 
-     * @parameter expression="${project.build.directory}/${project.build.finalName}"
+     * @parameter default-value="${project.build.directory}/${project.build.finalName}"
      * @required
      */
     private File war;
@@ -96,27 +96,51 @@
      */
     public void configureScanner() throws MojoExecutionException
     {
-        scanList = new ArrayList<File>();
-        scanList.add(project.getFile());
+        scanner.watch(project.getFile().toPath());
         File webInfDir = new File(war,"WEB-INF");
-        scanList.add(new File(webInfDir, "web.xml"));
+        File webXml = new File(webInfDir, "web.xml");
+        if (webXml.exists())
+            scanner.watch(webXml.toPath());
         File jettyWebXmlFile = findJettyWebXmlFile(webInfDir);
         if (jettyWebXmlFile != null)
-            scanList.add(jettyWebXmlFile);
+            scanner.watch(jettyWebXmlFile.toPath());
         File jettyEnvXmlFile = new File(webInfDir, "jetty-env.xml");
         if (jettyEnvXmlFile.exists())
-            scanList.add(jettyEnvXmlFile);
-        scanList.add(new File(webInfDir, "classes"));
-        scanList.add(new File(webInfDir, "lib"));
+            scanner.watch(jettyEnvXmlFile.toPath());
 
-        scannerListeners = new ArrayList<Scanner.BulkListener>();
-        scannerListeners.add(new Scanner.BulkListener()
+        File classes = new File(webInfDir, "classes");
+        if (classes.exists())
         {
-            public void filesChanged(List changes)
+            PathWatcher.Config classesConfig = new PathWatcher.Config(classes.toPath());
+            classesConfig.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
+            scanner.watch(classesConfig);
+        }
+
+        File lib = new File(webInfDir, "lib");
+        if (lib.exists())
+        {
+            PathWatcher.Config libConfig = new PathWatcher.Config(lib.toPath());
+            libConfig.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
+            scanner.watch(libConfig);   
+        }
+
+        scanner.addListener(new PathWatcher.EventListListener()
+        {
+
+            @Override
+            public void onPathWatchEvents(List<PathWatchEvent> events)
             {
                 try
                 {
-                    boolean reconfigure = changes.contains(project.getFile().getCanonicalPath());
+                    boolean reconfigure = false;
+                    for (PathWatchEvent e:events)
+                    {
+                        if (e.getPath().equals(project.getFile().toPath()))
+                        {
+                            reconfigure = true;
+                            break;
+                        }
+                    }
                     restartWebApp(reconfigure);
                 }
                 catch (Exception e)
@@ -137,6 +161,7 @@
     {
         getLog().info("Restarting webapp");
         getLog().debug("Stopping webapp ...");
+        stopScanner();
         webApp.stop();
         getLog().debug("Reconfiguring webapp ...");
 
@@ -147,23 +172,13 @@
         if (reconfigureScanner)
         {
             getLog().info("Reconfiguring scanner after change to pom.xml ...");
-            scanList.clear();
-            scanList.add(project.getFile());
-            File webInfDir = new File(war,"WEB-INF");
-            scanList.add(new File(webInfDir, "web.xml"));
-            File jettyWebXmlFile = findJettyWebXmlFile(webInfDir);
-            if (jettyWebXmlFile != null)
-                scanList.add(jettyWebXmlFile);
-            File jettyEnvXmlFile = new File(webInfDir, "jetty-env.xml");
-            if (jettyEnvXmlFile.exists())
-                scanList.add(jettyEnvXmlFile);
-            scanList.add(new File(webInfDir, "classes"));
-            scanList.add(new File(webInfDir, "lib"));
-            scanner.setScanDirs(scanList);
+            scanner.reset();
+            configureScanner();
         }
 
         getLog().debug("Restarting webapp ...");
         webApp.start();
+        startScanner();
         getLog().info("Restart completed.");
     }
 
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarMojo.java
index 70adabc..4624929 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarMojo.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarMojo.java
@@ -19,12 +19,12 @@
 package org.eclipse.jetty.maven.plugin;
 
 import java.io.File;
-import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
-import org.eclipse.jetty.util.Scanner;
+import org.eclipse.jetty.util.PathWatcher;
+import org.eclipse.jetty.util.PathWatcher.PathWatchEvent;
 
 /**
  * <p>
@@ -50,7 +50,7 @@
 
     /**
      * The location of the war file.
-     * @parameter expression="${project.build.directory}/${project.build.finalName}.war"
+     * @parameter default-value="${project.build.directory}/${project.build.finalName}.war"
      * @required
      */
     private File war;
@@ -100,18 +100,26 @@
      */
     public void configureScanner() throws MojoExecutionException
     {
-        scanList = new ArrayList();
-        scanList.add(project.getFile());
-        scanList.add(war);
-        
-        scannerListeners = new ArrayList();
-        scannerListeners.add(new Scanner.BulkListener()
+        scanner.watch(project.getFile().toPath());
+        scanner.watch(war.toPath());
+
+        scanner.addListener(new PathWatcher.EventListListener()
         {
-            public void filesChanged(List changes)
+
+            @Override
+            public void onPathWatchEvents(List<PathWatchEvent> events)
             {
                 try
                 {
-                    boolean reconfigure = changes.contains(project.getFile().getCanonicalPath());
+                    boolean reconfigure = false;
+                    for (PathWatchEvent e:events)
+                    {
+                        if (e.getPath().equals(project.getFile().toPath()))
+                        {
+                            reconfigure = true;
+                            break;
+                        }
+                    }
                     restartWebApp(reconfigure);
                 }
                 catch (Exception e)
@@ -132,6 +140,7 @@
     {
         getLog().info("Restarting webapp ...");
         getLog().debug("Stopping webapp ...");
+        stopScanner();
         webApp.stop();
         getLog().debug("Reconfiguring webapp ...");
 
@@ -142,14 +151,13 @@
         if (reconfigureScanner)
         {
             getLog().info("Reconfiguring scanner after change to pom.xml ...");
-            scanList.clear();
-            scanList.add(project.getFile());
-            scanList.add(war);
-            scanner.setScanDirs(scanList);
+            scanner.reset();
+            configureScanner();
         }
 
         getLog().debug("Restarting webapp ...");
         webApp.start();
+        startScanner();
         getLog().info("Restart completed.");
     }
 
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyServer.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyServer.java
deleted file mode 100644
index ec7a255..0000000
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyServer.java
+++ /dev/null
@@ -1,156 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.maven.plugin;
-
-
-import java.io.File;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.RequestLog;
-import org.eclipse.jetty.server.handler.ContextHandlerCollection;
-import org.eclipse.jetty.server.handler.DefaultHandler;
-import org.eclipse.jetty.server.handler.HandlerCollection;
-import org.eclipse.jetty.server.handler.RequestLogHandler;
-import org.eclipse.jetty.util.resource.Resource;
-import org.eclipse.jetty.webapp.WebAppContext;
-import org.eclipse.jetty.xml.XmlConfiguration;
-
-
-/**
- * JettyServer
- * 
- * Maven jetty plugin version of a wrapper for the Server class.
- * 
- */
-public class JettyServer extends org.eclipse.jetty.server.Server
-{ 
-    private RequestLog requestLog;
-    private ContextHandlerCollection contexts;
-    
-    
-    
-    /**
-     * 
-     */
-    public JettyServer()
-    {
-        super();
-        //make sure Jetty does not use URLConnection caches with the plugin
-        Resource.setDefaultUseCaches(false);
-    }
-
-   
-    public void setRequestLog (RequestLog requestLog)
-    {
-        this.requestLog = requestLog;
-    }
-
-    /**
-     * @see org.eclipse.jetty.server.Server#doStart()
-     */
-    public void doStart() throws Exception
-    {
-        super.doStart();
-    }
-
- 
-    /**
-     * @see org.eclipse.jetty.server.handler.HandlerCollection#addHandler(org.eclipse.jetty.server.Handler)
-     */
-    public void addWebApplication(WebAppContext webapp) throws Exception
-    {  
-        contexts.addHandler (webapp);
-    }
-
-    
-    /**
-     * Set up the handler structure to receive a webapp.
-     * Also put in a DefaultHandler so we get a nice page
-     * than a 404 if we hit the root and the webapp's
-     * context isn't at root.
-     * @throws Exception
-     */
-    public void configureHandlers () throws Exception 
-    {
-        DefaultHandler defaultHandler = new DefaultHandler();
-        RequestLogHandler requestLogHandler = new RequestLogHandler();
-        if (this.requestLog != null)
-            requestLogHandler.setRequestLog(this.requestLog);
-        
-        contexts = (ContextHandlerCollection)super.getChildHandlerByClass(ContextHandlerCollection.class);
-        if (contexts==null)
-        {   
-            contexts = new ContextHandlerCollection();
-            HandlerCollection handlers = (HandlerCollection)super.getChildHandlerByClass(HandlerCollection.class);
-            if (handlers==null)
-            {
-                handlers = new HandlerCollection();               
-                super.setHandler(handlers);                            
-                handlers.setHandlers(new Handler[]{contexts, defaultHandler, requestLogHandler});
-            }
-            else
-            {
-                handlers.addHandler(contexts);
-            }
-        }  
-    }
-
-    /**
-     * Apply xml files to server startup, passing in ourselves as the 
-     * "Server" instance.
-     * 
-     * @param files
-     * @throws Exception
-     */
-    public  void applyXmlConfigurations (List<File> files) 
-    throws Exception
-    {
-        if (files == null || files.isEmpty())
-            return;
-
-       Map<String,Object> lastMap = Collections.singletonMap("Server", (Object)this);
-
-        for ( File xmlFile : files )
-        {
-            if (PluginLog.getLog() != null)
-                PluginLog.getLog().info( "Configuring Jetty from xml configuration file = " + xmlFile.getCanonicalPath() );   
-
-
-            XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(xmlFile));
-
-            //chain ids from one config file to another
-            if (lastMap != null)
-                xmlConfiguration.getIdMap().putAll(lastMap); 
-
-            //Set the system properties each time in case the config file set a new one
-            Enumeration<?> ensysprop = System.getProperties().propertyNames();
-            while (ensysprop.hasMoreElements())
-            {
-                String name = (String)ensysprop.nextElement();
-                xmlConfiguration.getProperties().put(name,System.getProperty(name));
-            }
-            xmlConfiguration.configure(); 
-            lastMap = xmlConfiguration.getIdMap();
-        }
-    }
-}
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java
index af35489..638f8bf 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java
@@ -21,6 +21,7 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.lang.reflect.Method;
 import java.net.MalformedURLException;
 import java.util.ArrayList;
 import java.util.EventListener;
@@ -71,23 +72,25 @@
     private static final String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN = ".*/javax.servlet-[^/]*\\.jar$|.*/servlet-api-[^/]*\\.jar$|.*javax.servlet.jsp.jstl-[^/]*\\.jar|.*taglibs-standard-impl-.*\\.jar";
     private static final String WEB_INF_CLASSES_PREFIX = "/WEB-INF/classes";
     private static final String WEB_INF_LIB_PREFIX = "/WEB-INF/lib";
+    
+    
+    public  static final String[] DEFAULT_CONFIGURATION_CLASSES = {
+                                                           "org.eclipse.jetty.maven.plugin.MavenWebInfConfiguration",
+                                                           "org.eclipse.jetty.webapp.WebXmlConfiguration",
+                                                           "org.eclipse.jetty.webapp.MetaInfConfiguration",
+                                                           "org.eclipse.jetty.webapp.FragmentConfiguration",
+                                                           "org.eclipse.jetty.plus.webapp.EnvConfiguration",
+                                                           "org.eclipse.jetty.plus.webapp.PlusConfiguration",
+                                                           "org.eclipse.jetty.annotations.AnnotationConfiguration",
+                                                           "org.eclipse.jetty.webapp.JettyWebXmlConfiguration"
+                                                           };
 
-    private final Configuration[] _defaultConfigurations = {
-                                                             new MavenWebInfConfiguration(),
-                                                             new WebXmlConfiguration(),
-                                                             new MetaInfConfiguration(),
-                                                             new FragmentConfiguration(),
-                                                             new EnvConfiguration(),
-                                                             new PlusConfiguration(),
-                                                             new AnnotationConfiguration(),
-                                                             new JettyWebXmlConfiguration()
-                                                            };
 
-    private final Configuration[] _quickStartConfigurations = {
-                                                                new MavenQuickStartConfiguration(),
-                                                                new EnvConfiguration(),
-                                                                new PlusConfiguration(),
-                                                                new JettyWebXmlConfiguration()
+    private final String[] QUICKSTART_CONFIGURATION_CLASSES = {
+                                                                "org.eclipse.jetty.maven.plugin.MavenQuickStartConfiguration",
+                                                                "org.eclipse.jetty.plus.webapp.EnvConfiguration",
+                                                                "org.eclipse.jetty.plus.webapp.PlusConfiguration",
+                                                                "org.eclipse.jetty.webapp.JettyWebXmlConfiguration"
                                                                };
 
     private File _classes = null;
@@ -99,6 +102,9 @@
     private String _jettyEnvXml;
     private List<Overlay> _overlays;
     private Resource _quickStartWebXml;
+    private String _originAttribute;
+    private boolean _generateOrigin;
+   
     
  
     
@@ -130,7 +136,7 @@
    
 
   
-
+    /* ------------------------------------------------------------ */
     public JettyWebAppContext ()
     throws Exception
     {
@@ -138,81 +144,124 @@
         // Turn off copyWebInf option as it is not applicable for plugin.
         super.setCopyWebInf(false);
     }
+    
+    /* ------------------------------------------------------------ */
     public void setContainerIncludeJarPattern(String pattern)
     {
         _containerIncludeJarPattern = pattern;
     }
     
+    /* ------------------------------------------------------------ */
     public String getContainerIncludeJarPattern()
     {
         return _containerIncludeJarPattern;
     }
     
-    
+    /* ------------------------------------------------------------ */
     public String getWebInfIncludeJarPattern()
     {
         return _webInfIncludeJarPattern;
     }
+    
+    /* ------------------------------------------------------------ */
     public void setWebInfIncludeJarPattern(String pattern)
     {
         _webInfIncludeJarPattern = pattern;
     }
    
-   
+    /* ------------------------------------------------------------ */
     public List<File> getClassPathFiles()
     {
         return this._classpathFiles;
     }
     
-    
+    /* ------------------------------------------------------------ */
     public void setJettyEnvXml (String jettyEnvXml)
     {
         this._jettyEnvXml = jettyEnvXml;
     }
     
+    /* ------------------------------------------------------------ */
     public String getJettyEnvXml()
     {
         return this._jettyEnvXml;
     }
 
-   
+    /* ------------------------------------------------------------ */
     public void setClasses(File dir)
     {
         _classes = dir;
     }
     
+    /* ------------------------------------------------------------ */
     public File getClasses()
     {
         return _classes;
     }
     
+    /* ------------------------------------------------------------ */
     public void setWebInfLib (List<File> jars)
     {
         _webInfJars.addAll(jars);
     }
     
-    
+    /* ------------------------------------------------------------ */
     public void setTestClasses (File dir)
     {
         _testClasses = dir;
     }
     
-    
+    /* ------------------------------------------------------------ */
     public File getTestClasses ()
     {
         return _testClasses;
     }
     
+    
+    /* ------------------------------------------------------------ */
     /**
      * Ordered list of wars to overlay on top of the current project. The list
      * may contain an overlay that represents the current project.
-     * @param overlays
+     * @param overlays the list of overlays
      */
     public void setOverlays (List<Overlay> overlays)
     {
         _overlays = overlays;
     }
     
+    /**
+     * @return the originAttribute
+     */
+    public String getOriginAttribute()
+    {
+        return _originAttribute;
+    }
+
+    /**
+     * @param originAttribute the originAttribute to set
+     */
+    public void setOriginAttribute(String originAttribute)
+    {
+        _originAttribute = originAttribute;
+    }
+
+    /**
+     * @return the generateOrigin
+     */
+    public boolean isGenerateOrigin()
+    {
+        return _generateOrigin;
+    }
+
+    /**
+     * @param generateOrigin the generateOrigin to set
+     */
+    public void setGenerateOrigin(boolean generateOrigin)
+    {
+        _generateOrigin = generateOrigin;
+    }
+
+    /* ------------------------------------------------------------ */
     public List<Overlay> getOverlays()
     {
         return _overlays;
@@ -231,7 +280,13 @@
     }
     
     /* ------------------------------------------------------------ */
-    public void setQuickStartWebDescriptor (Resource quickStartWebXml)
+    public void setQuickStartWebDescriptor (String quickStartWebXml) throws Exception
+    {
+        setQuickStartWebDescriptor(Resource.newResource(quickStartWebXml));
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected void setQuickStartWebDescriptor (Resource quickStartWebXml)
     {
         _quickStartWebXml = quickStartWebXml;
     }
@@ -260,17 +315,20 @@
         
         setBaseResource(new ResourceCollection(resources.toArray(new String[resources.size()])));
     }
-
+    
+    /* ------------------------------------------------------------ */
     public List<File> getWebInfLib()
     {
         return _webInfJars;
     }
     
+    /* ------------------------------------------------------------ */
     public void setGenerateQuickStart (boolean quickStart)
     {
         _isGenerateQuickStart = quickStart;
     }
     
+    /* ------------------------------------------------------------ */
     public boolean isGenerateQuickStart()
     {
         return _isGenerateQuickStart;
@@ -278,7 +336,7 @@
     
    
     
-    
+    /* ------------------------------------------------------------ */
     @Override
     protected void startWebapp() throws Exception
     {
@@ -287,42 +345,47 @@
             if (getQuickStartWebDescriptor() == null)
                 throw new IllegalStateException ("No location to generate quickstart descriptor");
 
-            QuickStartDescriptorGenerator generator = new QuickStartDescriptorGenerator(this, _preconfigProcessor.getXML());
+            QuickStartDescriptorGenerator generator = new QuickStartDescriptorGenerator(this, _preconfigProcessor.getXML(), _originAttribute, _generateOrigin);
             try (FileOutputStream fos = new FileOutputStream(getQuickStartWebDescriptor().getFile()))
             {
                 generator.generateQuickStartWebXml(fos);
             }
         }
         else
+        {
+            if (LOG.isDebugEnabled()) { LOG.debug("Calling full start on webapp");}
             super.startWebapp();
+        }
     }
     
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void stopWebapp() throws Exception
+    {
+        if (isGenerateQuickStart())
+            return;
 
+        if (LOG.isDebugEnabled()) { LOG.debug("Calling stop of fully started webapp");}
+        super.stopWebapp();
+    }
+    
+    /* ------------------------------------------------------------ */
     @Override
     public void doStart () throws Exception
     {
         //choose if this will be a quickstart or normal start
         if (!isGenerateQuickStart() && getQuickStartWebDescriptor() != null)
-            setConfigurations(_quickStartConfigurations);
-        else
         {
-            setConfigurations(_defaultConfigurations);
+            setConfigurationClasses(QUICKSTART_CONFIGURATION_CLASSES);
+        }
+        else
+        { 
             if (isGenerateQuickStart())
             {
                 _preconfigProcessor = new PreconfigureDescriptorProcessor();
                 getMetaData().addDescriptorProcessor(_preconfigProcessor);
             }
         }
-        
-
-        //inject configurations with config from maven plugin    
-        for (Configuration c:getConfigurations())
-        {
-            if (c instanceof EnvConfiguration && getJettyEnvXml() != null)
-                ((EnvConfiguration)c).setJettyEnvXml(Resource.toURL(new File(getJettyEnvXml())));
-            else if (c instanceof MavenQuickStartConfiguration && getQuickStartWebDescriptor() != null)
-                ((MavenQuickStartConfiguration)c).setQuickStartWebXml(getQuickStartWebDescriptor());         
-        }
 
         //Set up the pattern that tells us where the jars are that need scanning
 
@@ -362,10 +425,31 @@
                 _webInfJarMap.put(fileName, file);
         }
         
+        //check for CDI
+        initCDI();
+        
         // CHECK setShutdown(false);
         super.doStart();
     }
-     
+    
+    
+    @Override
+    protected void loadConfigurations() throws Exception
+    {
+        super.loadConfigurations();
+        
+        //inject configurations with config from maven plugin    
+        for (Configuration c:getConfigurations())
+        {
+            if (c instanceof EnvConfiguration && getJettyEnvXml() != null)
+                ((EnvConfiguration)c).setJettyEnvXml(Resource.toURL(new File(getJettyEnvXml())));
+            else if (c instanceof MavenQuickStartConfiguration && getQuickStartWebDescriptor() != null)
+                ((MavenQuickStartConfiguration)c).setQuickStartWebXml(getQuickStartWebDescriptor());         
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
     public void doStop () throws Exception
     { 
         if (_classpathFiles != null)
@@ -386,8 +470,9 @@
         // CHECK setShutdown(true);
         //just wait a little while to ensure no requests are still being processed
         Thread.currentThread().sleep(500L);
+
         super.doStop();
-        
+
         //remove all listeners, servlets and filters. This is because we will re-apply
         //any context xml file, which means they would potentially be added multiple times.
         setEventListeners(new EventListener[0]);
@@ -396,7 +481,9 @@
         getServletHandler().setServlets(new ServletHolder[0]);
         getServletHandler().setServletMappings(new ServletMapping[0]);
     }
-
+    
+    
+    /* ------------------------------------------------------------ */
     @Override
     public Resource getResource(String uriInContext) throws MalformedURLException
     {
@@ -470,7 +557,9 @@
         }
         return resource;
     }
-
+    
+    
+    /* ------------------------------------------------------------ */
     @Override
     public Set<String> getResourcePaths(String path)
     {
@@ -507,7 +596,7 @@
         return paths;
     }
     
-    
+    /* ------------------------------------------------------------ */
     public String addPattern (String s, String pattern)
     {
         if (s == null)
@@ -524,4 +613,29 @@
         
         return s;
     }
+    
+    
+    /* ------------------------------------------------------------ */
+    public void initCDI()
+    {
+        Class cdiInitializer = null;
+        try
+        {
+            cdiInitializer = Thread.currentThread().getContextClassLoader().loadClass("org.eclipse.jetty.cdi.servlet.JettyWeldInitializer");
+            Method initWebAppMethod = cdiInitializer.getMethod("initWebApp", new Class[]{WebAppContext.class});
+            initWebAppMethod.invoke(null, new Object[]{this});
+        }
+        catch (ClassNotFoundException e)
+        {
+            LOG.debug("o.e.j.cdi.servlet.JettyWeldInitializer not found, no cdi integration available");
+        }
+        catch (NoSuchMethodException e)
+        {
+            LOG.warn("o.e.j.cdi.servlet.JettyWeldInitializer.initWebApp() not found, no cdi integration available");
+        }
+        catch (Exception e)
+        {
+           LOG.warn("Problem initializing cdi", e);
+        }
+    }
 }
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenServerConnector.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenServerConnector.java
index 8df8bd0..b5cc636 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenServerConnector.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenServerConnector.java
@@ -47,6 +47,8 @@
  */
 public class MavenServerConnector extends AbstractLifeCycle implements Connector
 {
+    public static String PORT_SYSPROPERTY = "jetty.http.port";
+    
     public static final int DEFAULT_PORT = 8080;
     public static final String DEFAULT_PORT_STR = String.valueOf(DEFAULT_PORT);
     public static final int DEFAULT_MAX_IDLE_TIME = 30000;
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenWebInfConfiguration.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenWebInfConfiguration.java
index 6fd2ec1..2ea7443 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenWebInfConfiguration.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenWebInfConfiguration.java
@@ -40,8 +40,7 @@
  * MavenWebInfConfiguration
  * 
  * WebInfConfiguration to take account of overlaid wars expressed as project dependencies and
- * potentiall configured via the maven-war-plugin.
- *
+ * potential configured via the maven-war-plugin.
  */
 public class MavenWebInfConfiguration extends WebInfConfiguration
 {
@@ -51,9 +50,6 @@
     protected static int COUNTER = 0; 
     protected Resource _originalResourceBase;
     protected List<Resource>  _unpackedOverlayResources;
-  
-    
-    
     
     /** 
      * @see org.eclipse.jetty.webapp.WebInfConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
@@ -192,7 +188,7 @@
      * Get the jars to examine from the files from which we have
      * synthesized the classpath. Note that the classpath is not
      * set at this point, so we cannot get them from the classpath.
-     * @param context
+     * @param context the web app context
      * @return the list of jars found
      */
     @Override
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/OverlayConfig.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/OverlayConfig.java
index 0000b18..e394fa0 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/OverlayConfig.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/OverlayConfig.java
@@ -29,8 +29,6 @@
 
 /**
  * OverlayConfig
- *
- *
  */
 public class OverlayConfig
 {
@@ -271,7 +269,7 @@
      * @param gid Artifact groupId
      * @param aid Artifact artifactId
      * @param cls Artifact classifier
-     * @return
+     * @return true if matched
      */
     public boolean matchesArtifact (String gid, String aid, String cls)
     {
@@ -286,9 +284,9 @@
     /**
      * Check if this overlay configuration matches an Artifact's info
      * 
-     * @param gid
-     * @param aid
-     * @return
+     * @param gid the group id
+     * @param aid the artifact id
+     * @return true if matched
      */
     public boolean matchesArtifact (String gid, String aid)
     {
@@ -299,9 +297,6 @@
         return false;
     }
     
-    
-    
-    
     public String toString()
     {
         StringBuffer strbuff = new StringBuffer();
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ScanPattern.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ScanPattern.java
new file mode 100644
index 0000000..3119d5f
--- /dev/null
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ScanPattern.java
@@ -0,0 +1,53 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.maven.plugin;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * ScanPattern
+ *
+ * A pattern of includes and excludes.
+ */
+public class ScanPattern
+{
+    private List<String> _includes = Collections.emptyList();
+    private List<String> _excludes = Collections.emptyList();
+    
+    public void setIncludes (List<String> includes)
+    {
+        _includes= includes;
+    }
+    
+    public void setExcludes(List<String> excludes)
+    {
+        _excludes = excludes;
+    }
+    
+    public List<String> getIncludes()
+    {
+        return _includes;
+    }
+    
+    public List<String> getExcludes()
+    {
+        return _excludes;
+    }
+}
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ScanTargetPattern.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ScanTargetPattern.java
index 8ee7cba..8fcde84 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ScanTargetPattern.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ScanTargetPattern.java
@@ -46,8 +46,7 @@
 public class ScanTargetPattern
 {
     private File _directory;
-    private List _includes = Collections.EMPTY_LIST;
-    private List _excludes = Collections.EMPTY_LIST;
+    private ScanPattern _pattern;
 
     /**
      * @return the _directory
@@ -65,24 +64,28 @@
         this._directory = directory;
     }
     
-    public void setIncludes (List includes)
+    public void setIncludes (List<String> includes)
     {
-        _includes= includes;
+        if (_pattern == null)
+            _pattern = new ScanPattern();
+        _pattern.setIncludes(includes);
     }
     
-    public void setExcludes(List excludes)
+    public void setExcludes(List<String> excludes)
     {
-        _excludes = excludes;
+        if (_pattern == null)
+            _pattern = new ScanPattern();
+        _pattern.setExcludes(excludes);
     }
     
-    public List getIncludes()
+    public List<String> getIncludes()
     {
-        return _includes;
+        return (_pattern == null? Collections.emptyList() : _pattern.getIncludes());
     }
     
-    public List getExcludes()
+    public List<String> getExcludes()
     {
-        return _excludes;
+        return (_pattern == null? Collections.emptyList() : _pattern.getExcludes());
     }
 
 }
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/SelectiveJarResource.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/SelectiveJarResource.java
index 2656b2b..f7e8423 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/SelectiveJarResource.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/SelectiveJarResource.java
@@ -52,30 +52,20 @@
     public static final List<String> DEFAULT_INCLUDES = Arrays.asList(new String[]{"**"});// No includes supplied, so set it to 'matches all'
     public static final List<String> DEFAULT_EXCLUDES = Collections.emptyList(); //No includes, set to no exclusions
 
-
     List<String> _includes = null;
     List<String> _excludes = null;
     boolean _caseSensitive = false;
     
-
-    /**
-     * @param url
-     */
     public SelectiveJarResource(URL url)
     {
         super(url);
     }
   
-    /**
-     * @param url
-     * @param useCaches
-     */
     public SelectiveJarResource(URL url, boolean useCaches)
     {
         super(url, useCaches);
     }
     
-    
     public void setCaseSensitive (boolean caseSensitive)
     {
         _caseSensitive = caseSensitive;
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ServerSupport.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ServerSupport.java
new file mode 100644
index 0000000..eeda811
--- /dev/null
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ServerSupport.java
@@ -0,0 +1,218 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.maven.plugin;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jetty.security.LoginService;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.RequestLog;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.server.handler.DefaultHandler;
+import org.eclipse.jetty.server.handler.HandlerCollection;
+import org.eclipse.jetty.server.handler.RequestLogHandler;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.Configuration;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.xml.XmlConfiguration;
+
+/**
+ * ServerSupport
+ *
+ * Helps configure the Server instance.
+ * 
+ */
+public class ServerSupport
+{
+    
+    public static void configureDefaultConfigurationClasses (Server server)
+    {
+        server.setAttribute(Configuration.ATTR, JettyWebAppContext.DEFAULT_CONFIGURATION_CLASSES);
+    }
+    
+    
+    /**
+     * Set up the handler structure to receive a webapp.
+     * Also put in a DefaultHandler so we get a nice page
+     * than a 404 if we hit the root and the webapp's
+     * context isn't at root.
+     * @param server the server
+     * @param requestLog the request log
+     * @throws Exception if unable to configure the handlers
+     */
+    public static void configureHandlers (Server server, RequestLog requestLog) throws Exception 
+    {
+        if (server == null)
+            throw new IllegalArgumentException ("Server is null");
+
+        DefaultHandler defaultHandler = new DefaultHandler();
+        RequestLogHandler requestLogHandler = new RequestLogHandler();
+        if (requestLog != null)
+            requestLogHandler.setRequestLog(requestLog);
+
+        ContextHandlerCollection contexts = findContextHandlerCollection(server);
+        if (contexts == null)
+        {   
+            contexts = new ContextHandlerCollection();
+            HandlerCollection handlers = (HandlerCollection)server.getChildHandlerByClass(HandlerCollection.class);
+            if (handlers == null)
+            {
+                handlers = new HandlerCollection();               
+                server.setHandler(handlers);                            
+                handlers.setHandlers(new Handler[]{contexts, defaultHandler, requestLogHandler});
+            }
+            else
+            {
+                handlers.addHandler(contexts);
+            }
+        }  
+    }
+    
+
+    /**
+     * Configure at least one connector for the server
+     * 
+     * @param server the server
+     * @param connector the connector
+     */
+    public static void configureConnectors (Server server, Connector connector)
+    {
+        if (server == null)
+            throw new IllegalArgumentException("Server is null");
+        
+        //if a connector is provided, use it
+        if (connector != null)
+        {
+            server.addConnector(connector);
+            return;
+        }
+        
+        
+
+        // if the user hasn't configured the connectors in a jetty.xml file so use a default one
+        Connector[] connectors = server.getConnectors();
+        if (connectors == null || connectors.length == 0)
+        {
+            //Make a new default connector
+            MavenServerConnector tmp = new MavenServerConnector();               
+            //use any jetty.http.port settings provided
+            String port = System.getProperty(MavenServerConnector.PORT_SYSPROPERTY, System.getProperty("jetty.port", MavenServerConnector.DEFAULT_PORT_STR));
+            tmp.setPort(Integer.parseInt(port.trim()));
+            tmp.setServer(server);
+            server.setConnectors(new Connector[] {tmp});
+        }
+    }
+    
+    
+    /**
+     * Set up any security LoginServices provided.
+     * 
+     * @param server the server
+     * @param loginServices the login services
+     */
+    public static void configureLoginServices (Server server, LoginService[] loginServices)
+    {
+        if (server == null)
+            throw new IllegalArgumentException ("Server is null");
+
+        if (loginServices != null)
+        {
+            for (LoginService loginService:loginServices)
+            {
+                PluginLog.getLog().debug(loginService.getClass().getName() + ": "+ loginService.toString());
+                server.addBean(loginService);
+            }
+        }
+    }
+    
+    public static void addWebApplication(Server server, WebAppContext webapp) throws Exception
+    {  
+        if (server == null)
+            throw new IllegalArgumentException ("Server is null");
+       ContextHandlerCollection contexts = findContextHandlerCollection(server);
+       if (contexts == null)
+           throw new IllegalStateException("ContextHandlerCollection is null");
+       contexts.addHandler (webapp);
+    }
+    
+
+    public static ContextHandlerCollection findContextHandlerCollection (Server server)
+    {
+        if (server == null)
+            return null;
+
+        return (ContextHandlerCollection)server.getChildHandlerByClass(ContextHandlerCollection.class);
+    }
+
+
+    /**
+     * Apply xml files to server startup, passing in ourselves as the 
+     * "Server" instance.
+     * 
+     * @param server the server to apply the xml to
+     * @param files the list of xml files
+     * @return the Server implementation, after the xml is applied
+     * @throws Exception if unable to apply the xml configuration
+     */
+    public static Server applyXmlConfigurations (Server server, List<File> files) 
+    throws Exception
+    {
+        if (files == null || files.isEmpty())
+            return server;
+
+        Map<String,Object> lastMap = new HashMap<String,Object>();
+        
+        if (server != null)
+            lastMap.put("Server", server);
+     
+
+        for ( File xmlFile : files )
+        {
+            if (PluginLog.getLog() != null)
+                PluginLog.getLog().info( "Configuring Jetty from xml configuration file = " + xmlFile.getCanonicalPath() );   
+
+
+            XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(xmlFile));
+
+            //chain ids from one config file to another
+            if (lastMap != null)
+                xmlConfiguration.getIdMap().putAll(lastMap); 
+
+            //Set the system properties each time in case the config file set a new one
+            Enumeration<?> ensysprop = System.getProperties().propertyNames();
+            while (ensysprop.hasMoreElements())
+            {
+                String name = (String)ensysprop.nextElement();
+                xmlConfiguration.getProperties().put(name,System.getProperty(name));
+            }
+            xmlConfiguration.configure(); 
+            lastMap = xmlConfiguration.getIdMap();
+        }
+        
+        return (Server)lastMap.get("Server");
+    }
+
+}
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java
index f122a53..040a582 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java
@@ -31,8 +31,8 @@
 import java.util.Set;
 import java.util.TreeMap;
 
-import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ShutdownMonitor;
 import org.eclipse.jetty.server.handler.HandlerCollection;
 import org.eclipse.jetty.util.StringUtil;
@@ -45,20 +45,16 @@
 
 
 /**
- * Starter
- * 
- * Class which is exec'ed to create a new jetty process. Used by the JettyRunForked mojo.
- *
+ * Starter Class which is exec'ed to create a new jetty process. Used by the JettyRunForked mojo.
  */
 public class Starter
 { 
-    public static final String PORT_SYSPROPERTY = "jetty.port";
     private static final Logger LOG = Log.getLogger(Starter.class);
 
     private List<File> jettyXmls; // list of jetty.xml config files to apply - Mandatory
     private File contextXml; //name of context xml file to configure the webapp - Mandatory
 
-    private JettyServer server = new JettyServer();
+    private Server server;
     private JettyWebAppContext webApp;
 
     
@@ -116,41 +112,23 @@
     
     
     
-    /**
-     * @throws Exception
-     */
     public void configureJetty () throws Exception
     {
         LOG.debug("Starting Jetty Server ...");
-
+        Resource.setDefaultUseCaches(false);
+        
         //apply any configs from jetty.xml files first 
         applyJettyXml ();
 
-        // if the user hasn't configured a connector in the jetty.xml
-        //then use a default
-        Connector[] connectors = this.server.getConnectors();
-        if (connectors == null|| connectors.length == 0)
-        {
-            //if a SystemProperty -Djetty.port=<portnum> has been supplied, use that as the default port
-            MavenServerConnector httpConnector = new MavenServerConnector();
-            httpConnector.setServer(this.server);
-            String tmp = System.getProperty(PORT_SYSPROPERTY, MavenServerConnector.DEFAULT_PORT_STR);
-            httpConnector.setPort(Integer.parseInt(tmp.trim()));
-            connectors = new Connector[] {httpConnector};
-            this.server.setConnectors(connectors);
-        }
-
-        //check that everything got configured, and if not, make the handlers
-        HandlerCollection handlers = (HandlerCollection) server.getChildHandlerByClass(HandlerCollection.class);
-        if (handlers == null)
-        {
-            handlers = new HandlerCollection();
-            server.setHandler(handlers);
-        }
+        //ensure there's a connector
+        ServerSupport.configureConnectors(server, null);
 
         //check if contexts already configured, create if not
-        this.server.configureHandlers();
-
+        ServerSupport.configureHandlers(server, null);
+        
+        //Set up list of default Configurations to apply to a webapp
+        ServerSupport.configureDefaultConfigurationClasses(server);
+        
         webApp = new JettyWebAppContext();
         
         //configure webapp from properties file describing unassembled webapp
@@ -177,7 +155,7 @@
             xmlConfiguration.configure(webApp);
         }
 
-        this.server.addWebApplication(webApp);
+        ServerSupport.addWebApplication(server, webApp);
 
         if(stopPort>0 && stopKey!=null)
         {
@@ -188,10 +166,6 @@
         }
     }
     
-    
-    /**
-     * @throws Exception
-     */
     public void configureWebApp ()
     throws Exception
     {
@@ -340,10 +314,6 @@
         
     }
 
-    /**
-     * @param args
-     * @throws Exception
-     */
     public void getConfiguration (String[] args)
     throws Exception
     {
@@ -394,9 +364,6 @@
     }
 
 
-    /**
-     * @throws Exception
-     */
     public void run() throws Exception
     {
         LOG.info("Started Jetty Server");
@@ -404,18 +371,12 @@
     }
 
     
-    /**
-     * @throws Exception
-     */
     public void join () throws Exception
     {
         server.join();
     }
     
     
-    /**
-     * @param e
-     */
     public void communicateStartupResult (Exception e)
     {
         if (token != null)
@@ -430,22 +391,21 @@
     
     /**
      * Apply any jetty xml files given
-     * @throws Exception
+     * @throws Exception if unable to apply the xml
      */
     public void applyJettyXml() throws Exception
     {
-        if (jettyXmls == null)
-            return;
-        this.server.applyXmlConfigurations(jettyXmls);
+        Server tmp = ServerSupport.applyXmlConfigurations(server, jettyXmls);
+        if (server == null)
+            server = tmp;
+        
+        if (server == null)
+            server = new Server();
     }
 
 
 
 
-    /**
-     * @param handler
-     * @param handlers
-     */
     protected void prependHandler (Handler handler, HandlerCollection handlers)
     {
         if (handler == null || handlers == null)
@@ -460,11 +420,6 @@
     
     
     
-    /**
-     * @param c
-     * @param wars
-     * @return
-     */
     protected Artifact getArtifactForOverlayConfig (OverlayConfig c, List<Artifact> wars)
     {
         if (wars == null || wars.isEmpty() || c == null)
@@ -499,11 +454,6 @@
         return list;
     }
     
-    
-    
-    /**
-     * @param args
-     */
     public static final void main(String[] args)
     {
         if (args == null)
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/WarPluginInfo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/WarPluginInfo.java
index 1296254..2ac8910 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/WarPluginInfo.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/WarPluginInfo.java
@@ -30,11 +30,9 @@
 import org.codehaus.plexus.util.xml.Xpp3Dom;
 import org.eclipse.jetty.util.StringUtil;
 
-
-
 /**
  * WarPluginInfo
- *
+ * <p>
  * Information about the maven-war-plugin contained in the pom
  */
 public class WarPluginInfo
@@ -46,10 +44,6 @@
     private List<OverlayConfig> _overlayConfigs;
     
     
-    
-    /**
-     * @param project
-     */
     public WarPluginInfo (MavenProject project)
     {
         _project = project;
@@ -60,7 +54,7 @@
     
     /**
      * Find the maven-war-plugin, if one is configured
-     * @return
+     * @return the plugin
      */
     public Plugin getPlugin()
     {
@@ -87,7 +81,7 @@
 
     /**
      * Get value of dependentWarIncludes for maven-war-plugin
-     * @return
+     * @return the list of dependent war includes
      */
     public List<String> getDependentMavenWarIncludes()
     {
@@ -116,7 +110,7 @@
     
     /**
      * Get value of dependentWarExcludes for maven-war-plugin
-     * @return
+     * @return the list of dependent war excludes
      */
     public List<String> getDependentMavenWarExcludes()
     {
@@ -146,7 +140,7 @@
     /**
      * Get config for any overlays that have been declared for the maven-war-plugin.
      * 
-     * @return
+     * @return the list of overlay configs
      */
     public List<OverlayConfig> getMavenWarOverlayConfigs ()
     {
diff --git a/jetty-monitor/pom.xml b/jetty-monitor/pom.xml
index a50a73b..91c28d4 100644
--- a/jetty-monitor/pom.xml
+++ b/jetty-monitor/pom.xml
@@ -1,25 +1,9 @@
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
--->
+<?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-monitor</artifactId>
@@ -32,52 +16,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.management.*,*</Import-Package>
-              </instructions>
-            </configuration>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
diff --git a/jetty-monitor/src/main/config/etc/jetty-monitor.xml b/jetty-monitor/src/main/config/etc/jetty-monitor.xml
index 61270b5..a8f6633 100644
--- a/jetty-monitor/src/main/config/etc/jetty-monitor.xml
+++ b/jetty-monitor/src/main/config/etc/jetty-monitor.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
   <!-- Create Thread Monitor, and add to the Server as a lifecycle -->
@@ -13,15 +13,15 @@
         <!-- To enable logging CPU utilization for threads above specified threshold, -->
         <!-- uncomment the following lines, changing log interval (in milliseconds)  -->
         <!-- and log threshold (in percent) as desired.                              -->
-        <!-- 
+        <!--
         <Set name="logInterval">10000</Set>
         <Set name="logThreshold">1</Set>
         -->
-        
+
         <!-- To enable detail dump of the server whenever a thread is detected as spinning, -->
         <!-- uncomment the following lines. -->
         <!--
-        <Set name="dumpable"><Ref id="Server"/></Set>
+        <Set name="dumpable"><Ref refid="Server"/></Set>
         -->
       </New>
     </Arg>
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/JMXMonitor.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/JMXMonitor.java
index 4033d40..91053cc 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/JMXMonitor.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/JMXMonitor.java
@@ -29,10 +29,9 @@
 import org.eclipse.jetty.monitor.jmx.ServiceConnection;
 import org.eclipse.jetty.xml.XmlConfiguration;
 
-/* ------------------------------------------------------------ */
 /**
  * JMXMonitor
- *
+ * <p>
  * Performs monitoring of the values of the attributes of MBeans
  * and executes specified actions as well as sends notifications
  * of the specified events that have occurred.
@@ -120,7 +119,7 @@
      * Retrieves a connection to JMX service
      *
      * @return server connection
-     * @throws IOException
+     * @throws IOException if unable to obtain server connection
      */
     public static MBeanServerConnection getServiceConnection()
         throws IOException
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitor.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitor.java
index 7130f4f..1d5ebea 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitor.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/ThreadMonitor.java
@@ -24,8 +24,11 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.eclipse.jetty.monitor.thread.ThreadMonitorException;
 import org.eclipse.jetty.monitor.thread.ThreadMonitorInfo;
@@ -60,7 +63,7 @@
     /**
      * Instantiates a new thread monitor.
      *
-     * @throws Exception
+     * @throws Exception if unable to instantiate thread monitor
      */
     public ThreadMonitor() throws Exception
     {
@@ -72,7 +75,7 @@
      * Instantiates a new thread monitor.
      *
      * @param intervalMs scan interval
-     * @throws Exception
+     * @throws Exception if unable to instantiate thread monitor
      */
     public ThreadMonitor(int intervalMs) throws Exception
     {
@@ -85,7 +88,7 @@
      *
      * @param intervalMs scan interval
      * @param threshold busy threshold
-     * @throws Exception
+     * @throws Exception if unable to instantiate thread monitor
      */
     public ThreadMonitor(int intervalMs, int threshold) throws Exception
     {
@@ -99,7 +102,7 @@
      * @param intervalMs scan interval
      * @param threshold busy threshold
      * @param depth stack compare depth
-     * @throws Exception
+     * @throws Exception if unable to instantiate thread monitor
      */
     public ThreadMonitor(int intervalMs, int threshold, int depth) throws Exception
     {
@@ -114,7 +117,7 @@
      * @param threshold busy threshold
      * @param depth stack compare depth
      * @param trail length of stack trail
-     * @throws Exception
+     * @throws Exception if unable to instantiate thread monitor
      */
     public ThreadMonitor(int intervalMs, int threshold, int depth, int trail) throws Exception
     {
@@ -321,6 +324,8 @@
                 _runner.join();
             }
             catch (InterruptedException ex) {}
+            
+            _monitorInfo.clear();
         }
     }
 
@@ -424,6 +429,8 @@
             // retrieving a single thread stack trace.
             Map<Thread,StackTraceElement[]> all = Thread.getAllStackTraces();
             
+            Set<Long> newOrUpdatedIds = new HashSet<Long>();
+            
             for (Map.Entry<Thread,StackTraceElement[]> entry : all.entrySet())
             {
                 Thread thread = entry.getKey();
@@ -490,7 +497,18 @@
                         }
                     }
                 }
+                newOrUpdatedIds.add(Long.valueOf(threadId));
             }
+            
+            //clean out threads that no longer exist
+            Iterator<Long> iter = _monitorInfo.keySet().iterator();
+            while (iter.hasNext())
+            {
+                Long id = iter.next();
+                if (!newOrUpdatedIds.contains(id))
+                    iter.remove();
+            }
+            newOrUpdatedIds.clear();
         }
         catch (Exception ex)
         {
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorAction.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorAction.java
index 0300bfa..a382d4b 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorAction.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorAction.java
@@ -50,10 +50,6 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 
-
-/* ------------------------------------------------------------ */
-/**
- */
 public class JavaMonitorAction extends MonitorAction
 {
     private static final Logger LOG = Log.getLogger(JavaMonitorAction.class);
@@ -68,12 +64,6 @@
     private String _session;
     
     /* ------------------------------------------------------------ */
-    /**
-     * @param notifier
-     * @param pollInterval
-     * @throws Exception 
-     * @throws MalformedObjectNameException 
-     */
     public JavaMonitorAction(EventNotifier notifier, String url, String uuid, String appid, long pollInterval)
         throws Exception
     {
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTools.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTools.java
index 0e21705..18c8301 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTools.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTools.java
@@ -30,12 +30,11 @@
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.annotation.ManagedOperation;
 
-/* ------------------------------------------------------------ */
 /**
  * Derived from the JMX bean classes created by Kees Jan Koster for the java-monitor
  * J2EE probe http://code.google.com/p/java-monitor-probes/source/browse/.
  * 
- * @author kjkoster <kjkoster@gmail.com>
+ * @author <a href="mailto:kjkoster@gmail.com">Kees Jan Koster</a>
  */
 @ManagedObject("Java Monitoring Tools")
 public class JavaMonitorTools
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTrigger.java
index bc98ac3..bc7b430 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTrigger.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/integration/JavaMonitorTrigger.java
@@ -23,8 +23,8 @@
 import org.eclipse.jetty.monitor.triggers.AttrEventTrigger;
 
 
-/* ------------------------------------------------------------ */
 /**
+ * @param <TYPE> the trigger type
  */
 public class JavaMonitorTrigger <TYPE extends Comparable<TYPE>>
     extends AttrEventTrigger<TYPE>
@@ -35,13 +35,6 @@
     private int _count;
     
     /* ------------------------------------------------------------ */
-    /**
-     * @param nameObject
-     * @param attributeName
-     * @param id
-     * @param dynamic
-     * @throws IllegalArgumentException
-     */
     public JavaMonitorTrigger(ObjectName nameObject, String attributeName, String id, String name, boolean dynamic)
         throws IllegalArgumentException
     {   
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ConsoleNotifier.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ConsoleNotifier.java
index d534f17..949474e 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ConsoleNotifier.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ConsoleNotifier.java
@@ -35,7 +35,7 @@
      * Constructs a new notifier with specified format string
      * 
      * @param format the {@link java.util.Formatter format string}
-     * @throws IllegalArgumentException
+     * @throws IllegalArgumentException if format is invalid
      */
     public ConsoleNotifier(String format)
         throws IllegalArgumentException
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventNotifier.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventNotifier.java
index 79e4f58..98865a0 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventNotifier.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventNotifier.java
@@ -31,7 +31,8 @@
     /* ------------------------------------------------------------ */
     /**
      * This method is called when a notification event is received by the containing object
-     *  
+     * 
+     * @param trigger the event trigger 
      * @param state an {@link org.eclipse.jetty.monitor.jmx.EventState event state} 
      * @param timestamp time stamp of the event
      */
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventState.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventState.java
index 75fc935..1d67417 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventState.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventState.java
@@ -30,6 +30,7 @@
  * 
  * Holds the state of one or more {@link org.eclipse.jetty.monitor.jmx.EventTrigger event trigger}
  * instances to be used when sending notifications as well as executing the actions
+ * @param <TYPE> the event trigger type
  */
 public class EventState<TYPE>
 {
@@ -39,6 +40,7 @@
      * State
      * 
      * Holds the state of a single {@link org.eclipse.jetty.monitor.jmx.EventTrigger event trigger}
+     * @param <TYPE> the event trigger type
      */
     public static class TriggerState<TYPE>
     {
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventTrigger.java
index 0e12e16..ac33d84 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventTrigger.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/EventTrigger.java
@@ -58,7 +58,9 @@
      * Abstract method to verify if the event trigger conditions
      * are in the appropriate state for an event to be triggered
      * 
+     * @param timestamp the timestamp to match 
      * @return true to trigger an event
+     * @throws Exception if unable to match
      */
     public abstract boolean match(long timestamp) throws Exception;
 
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/LoggingNotifier.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/LoggingNotifier.java
index 889e8e1..05fc428 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/LoggingNotifier.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/LoggingNotifier.java
@@ -40,7 +40,7 @@
      * Constructs a new notifier with specified format string
      * 
      * @param format the {@link java.util.Formatter format string}
-     * @throws IllegalArgumentException
+     * @throws IllegalArgumentException if format is invalid
      */
     public LoggingNotifier(String format)
         throws IllegalArgumentException
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorAction.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorAction.java
index 2469d9a..e0aa330 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorAction.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/MonitorAction.java
@@ -45,7 +45,7 @@
      * Creates a new monitor action 
      * 
      * @param trigger event trigger to be associated with this action
-     * @throws InvalidParameterException
+     * @throws InvalidParameterException if trigger is invalid
      */
     public MonitorAction(EventTrigger trigger)
         throws InvalidParameterException
@@ -60,7 +60,7 @@
      * 
      * @param trigger event trigger to be associated with this action
      * @param notifier event notifier to be associated with this action
-     * @throws InvalidParameterException
+     * @throws InvalidParameterException if trigger is invalid
      */
     public MonitorAction(EventTrigger trigger, EventNotifier notifier)
         throws InvalidParameterException
@@ -75,7 +75,7 @@
      * @param trigger event trigger to be associated with this action
      * @param notifier event notifier to be associated with this action
      * @param pollInterval interval for polling of the JMX server
-     * @throws InvalidParameterException
+     * @throws InvalidParameterException if trigger is invalid
      */
     public MonitorAction(EventTrigger trigger, EventNotifier notifier, long pollInterval)
         throws InvalidParameterException
@@ -91,7 +91,7 @@
      * @param notifier event notifier to be associated with this action
      * @param pollInterval interval for polling of the JMX server
      * @param pollDelay delay before starting to poll the JMX server
-     * @throws InvalidParameterException
+     * @throws InvalidParameterException if trigger is invalid
      */
     public MonitorAction(EventTrigger trigger, EventNotifier notifier, long pollInterval, long pollDelay)
         throws InvalidParameterException
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ServiceConnection.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ServiceConnection.java
index 758aa54..c13887e 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ServiceConnection.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/jmx/ServiceConnection.java
@@ -54,7 +54,7 @@
     /**
      * Construct a loopback connection to an internal server
      * 
-     * @throws IOException
+     * @throws IOException if unable to construct service connection
      */
     public ServiceConnection()
         throws IOException
@@ -67,7 +67,7 @@
      * Construct a connection to specified server
      * 
      * @param url URL of JMX server
-     * @throws IOException
+     * @throws IOException if unable to construct service connection
      */
     public ServiceConnection(String url)
         throws IOException
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AggregateEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AggregateEventTrigger.java
index c7c1728..339c654 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AggregateEventTrigger.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AggregateEventTrigger.java
@@ -72,27 +72,18 @@
     }
     
     /* ------------------------------------------------------------ */
-    /**
-     * @param trigger
-     */
     public void add(EventTrigger trigger)
     {
         _triggers.add(trigger);
     }
     
     /* ------------------------------------------------------------ */
-    /**
-     * @param triggers
-     */
     public void addAll(List<EventTrigger> triggers)
     {
         _triggers.addAll(triggers);
     }
         
     /* ------------------------------------------------------------ */
-    /**
-     * @param triggers
-     */
     public void addAll(EventTrigger... triggers)
     {
         _triggers.addAll(Arrays.asList(triggers));
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AttrEventTrigger.java
index 613c388..9cdec0c 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AttrEventTrigger.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/AttrEventTrigger.java
@@ -32,11 +32,9 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-
-/* ------------------------------------------------------------ */
 /**
  * AttrEventTrigger
- * 
+ * <p>
  * Event trigger that polls a value of an MXBean attribute
  * and matches every invocation of this trigger. It can be
  * used to send notifications of the value of an attribute
@@ -44,6 +42,7 @@
  * a base class for the event triggers that match the 
  * value of an attribute of the MXBean being polled against
  * some specified criteria.
+ * @param <TYPE> the event trigger type
  */
 public class AttrEventTrigger<TYPE extends Comparable<TYPE>> 
     extends EventTrigger
@@ -64,8 +63,8 @@
      * @param objectName object name of an MBean to be polled
      * @param attributeName name of an MBean attribute to be polled
      * 
-     * @throws MalformedObjectNameException
-     * @throws IllegalArgumentException
+     * @throws MalformedObjectNameException if unable to find object due to malformed name reference
+     * @throws IllegalArgumentException if parameters are invalid
      */
     public AttrEventTrigger(String objectName, String attributeName)
         throws MalformedObjectNameException, IllegalArgumentException
@@ -91,7 +90,7 @@
      * @param nameObject object name of an MBean to be polled
      * @param attributeName name of an MBean attribute to be polled
      * 
-     * @throws IllegalArgumentException
+     * @throws IllegalArgumentException if parameters are invalid
      */
     public AttrEventTrigger(ObjectName nameObject, String attributeName)
         throws IllegalArgumentException
@@ -113,7 +112,7 @@
     /**
      * Verify if the event trigger conditions are in the 
      * appropriate state for an event to be triggered.
-     * This event trigger uses the match(Comparable<TYPE>)
+     * This event trigger uses the match(Comparable&lt;TYPE&gt;)
      * method to compare the value of the MXBean attribute
      * to the conditions specified by the subclasses.
      * 
@@ -162,6 +161,8 @@
      * appropriate state for an event to be triggered.
      * Allows subclasses to override the default behavior
      * that matches every invocation of this trigger
+     * @param value the value to match
+     * @return always true
      */
     public boolean match(Comparable<TYPE> value)
     {
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/EqualToAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/EqualToAttrEventTrigger.java
index 76b2750..ec32691 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/EqualToAttrEventTrigger.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/EqualToAttrEventTrigger.java
@@ -21,12 +21,12 @@
 import javax.management.MalformedObjectNameException;
 
 
-/* ------------------------------------------------------------ */
 /**
  * EqualToAttrEventTrigger
- * 
+ * <p> 
  * Event trigger that polls a value of an MXBean attribute and
  * checks if it is equal to specified value. 
+ * @param <TYPE> the event trigger type
  */
 public class EqualToAttrEventTrigger<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
 {
@@ -42,8 +42,8 @@
      * @param attributeName name of an MBean attribute to be polled
      * @param value target value of the attribute
      * 
-     * @throws MalformedObjectNameException
-     * @throws IllegalArgumentException
+     * @throws MalformedObjectNameException if unable to find object due to name issue
+     * @throws IllegalArgumentException on invalid parameters
      */
     public EqualToAttrEventTrigger(String objectName, String attributeName, TYPE value)
         throws MalformedObjectNameException, IllegalArgumentException
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanAttrEventTrigger.java
index 9205865..92a785f 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanAttrEventTrigger.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanAttrEventTrigger.java
@@ -20,13 +20,12 @@
 
 import javax.management.MalformedObjectNameException;
 
-
-/* ------------------------------------------------------------ */
 /**
  * GreaterThanAttrEventTrigger
- * 
+ * <p> 
  * Event trigger that polls a value of an MXBean attribute and
  * checks if it is greater than specified min value. 
+ * @param <TYPE> event trigger type
  */
 public class GreaterThanAttrEventTrigger<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
 {
@@ -42,8 +41,8 @@
      * @param attributeName name of an MBean attribute to be polled
      * @param min minimum value of the attribute
      * 
-     * @throws MalformedObjectNameException
-     * @throws IllegalArgumentException
+     * @throws MalformedObjectNameException on bad object name
+     * @throws IllegalArgumentException on bad parameters
      */
     public GreaterThanAttrEventTrigger(String objectName, String attributeName, TYPE min)
         throws MalformedObjectNameException, IllegalArgumentException
@@ -72,7 +71,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Returns the string representation of this event trigger
-     * in the format "min<name". 
+     * in the format "min&lt;name". 
      * 
      * @return string representation of the event trigger
      * 
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanOrEqualToAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanOrEqualToAttrEventTrigger.java
index 7d4b53d..7b4acd4 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanOrEqualToAttrEventTrigger.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/GreaterThanOrEqualToAttrEventTrigger.java
@@ -20,13 +20,12 @@
 
 import javax.management.MalformedObjectNameException;
 
-
-/* ------------------------------------------------------------ */
 /**
  * GreaterThanOrEqualToAttrEventTrigger
- * 
+ * <p>
  * Event trigger that polls a value of an MXBean attribute and
  * checks if it is greater than or equal to specified min value. 
+ * @param <TYPE> event trigger type
  */
 public class GreaterThanOrEqualToAttrEventTrigger<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
 {
@@ -42,8 +41,8 @@
      * @param attributeName name of an MBean attribute to be polled
      * @param min minimum value of the attribute
      * 
-     * @throws MalformedObjectNameException
-     * @throws IllegalArgumentException
+     * @throws MalformedObjectNameException on bad object name
+     * @throws IllegalArgumentException on bad parameters
      */
     public GreaterThanOrEqualToAttrEventTrigger(String objectName, String attributeName, TYPE min)
         throws MalformedObjectNameException, IllegalArgumentException
@@ -72,7 +71,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Returns the string representation of this event trigger
-     * in the format "min<=name". 
+     * in the format "min&lt;=name". 
      * 
      * @return string representation of the event trigger
      * 
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanAttrEventTrigger.java
index 99e49cf..e630992 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanAttrEventTrigger.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanAttrEventTrigger.java
@@ -20,13 +20,12 @@
 
 import javax.management.MalformedObjectNameException;
 
-
-/* ------------------------------------------------------------ */
 /**
  * LessThanAttrEventTrigger
- * 
+ * <p>
  * Event trigger that polls a value of an MXBean attribute and
  * checks if it is greater than specified max value. 
+ * @param <TYPE> event trigger type
  */
 public class LessThanAttrEventTrigger<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
 {
@@ -42,8 +41,8 @@
      * @param attributeName name of an MBean attribute to be polled
      * @param max maximum value of the attribute
      * 
-     * @throws MalformedObjectNameException
-     * @throws IllegalArgumentException
+     * @throws MalformedObjectNameException on bad object name
+     * @throws IllegalArgumentException on bad parameters
      */
     public LessThanAttrEventTrigger(String objectName, String attributeName, TYPE max)
         throws MalformedObjectNameException, IllegalArgumentException
@@ -72,7 +71,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Returns the string representation of this event trigger
-     * in the format "name<max". 
+     * in the format "name&lt;max". 
      * 
      * @return string representation of the event trigger
      * 
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanOrEqualToAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanOrEqualToAttrEventTrigger.java
index 71ad9de..adeda97 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanOrEqualToAttrEventTrigger.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/LessThanOrEqualToAttrEventTrigger.java
@@ -21,12 +21,12 @@
 import javax.management.MalformedObjectNameException;
 
 
-/* ------------------------------------------------------------ */
 /**
  * LessThanOrEqualToAttrEventTrigger
- * 
+ * <p> 
  * Event trigger that polls a value of an MXBean attribute and
  * checks if it is less than or equal to specified max value. 
+ * @param <TYPE> event trigger type 
  */
 public class LessThanOrEqualToAttrEventTrigger<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
 {
@@ -42,8 +42,8 @@
      * @param attributeName name of an MBean attribute to be polled
      * @param max maximum value of the attribute
      * 
-     * @throws MalformedObjectNameException
-     * @throws IllegalArgumentException
+     * @throws MalformedObjectNameException on bad object name
+     * @throws IllegalArgumentException on bad parameters
      */
     public LessThanOrEqualToAttrEventTrigger(String objectName, String attributeName, TYPE max)
         throws MalformedObjectNameException, IllegalArgumentException
@@ -72,7 +72,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Returns the string representation of this event trigger
-     * in the format "name<=max". 
+     * in the format "name&lt;=max". 
      * 
      * @return string representation of the event trigger
      * 
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeAttrEventTrigger.java
index 59a1991..9e1144b 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeAttrEventTrigger.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeAttrEventTrigger.java
@@ -20,14 +20,13 @@
 
 import javax.management.MalformedObjectNameException;
 
-
-/* ------------------------------------------------------------ */
 /**
  * RangeAttrEventTrigger
- * 
+ * <p> 
  * Event trigger that polls a value of an MXBean attribute and
  * checks if it is in a range from specified min value to
  * specified max value. 
+ * @param <TYPE> event trigger type
  */
 public class RangeAttrEventTrigger<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
 {
@@ -45,8 +44,8 @@
      * @param min minimum value of the attribute
      * @param max maximum value of the attribute
      * 
-     * @throws MalformedObjectNameException
-     * @throws IllegalArgumentException
+     * @throws MalformedObjectNameException on bad object name
+     * @throws IllegalArgumentException on bad parameters
      */
     public RangeAttrEventTrigger(String objectName, String attributeName,TYPE min, TYPE max)
         throws MalformedObjectNameException, IllegalArgumentException
@@ -79,7 +78,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Returns the string representation of this event trigger
-     * in the format "min<name<max". 
+     * in the format "min&lt;name&lt;max". 
      * 
      * @return string representation of the event trigger
      * 
diff --git a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeInclAttrEventTrigger.java b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeInclAttrEventTrigger.java
index 7876628..24eb3c4 100644
--- a/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeInclAttrEventTrigger.java
+++ b/jetty-monitor/src/main/java/org/eclipse/jetty/monitor/triggers/RangeInclAttrEventTrigger.java
@@ -20,14 +20,13 @@
 
 import javax.management.MalformedObjectNameException;
 
-
-/* ------------------------------------------------------------ */
 /**
  * RangeInclAttrEventTrigger
- * 
+ * <p>
  * Event trigger that polls a value of an MXBean attribute and
  * checks if it is in a range from specified min value to
  * specified max value including the range bounds. 
+ * @param <TYPE> event trigger type
  */
 public class RangeInclAttrEventTrigger<TYPE extends Comparable<TYPE>> extends AttrEventTrigger<TYPE>
 {
@@ -45,8 +44,8 @@
      * @param min minimum value of the attribute
      * @param max maximum value of the attribute
      * 
-     * @throws MalformedObjectNameException
-     * @throws IllegalArgumentException
+     * @throws MalformedObjectNameException on bad object name
+     * @throws IllegalArgumentException on bad parameters
      */
     public RangeInclAttrEventTrigger(String objectName, String attributeName,TYPE min, TYPE max)
         throws MalformedObjectNameException, IllegalArgumentException
@@ -79,7 +78,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Returns the string representation of this event trigger
-     * in the format "min<=name<=max". 
+     * in the format "min&lt;=name&lt;=max". 
      * 
      * @return string representation of the event trigger
      * 
diff --git a/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/ThreadMonitorTest.java b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/ThreadMonitorTest.java
index b31eee0..714a7db 100644
--- a/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/ThreadMonitorTest.java
+++ b/jetty-monitor/src/test/java/org/eclipse/jetty/monitor/ThreadMonitorTest.java
@@ -25,8 +25,8 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.eclipse.jetty.util.component.Dumpable;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.junit.Ignore;
 import org.junit.Test;
 
 
@@ -37,64 +37,66 @@
 {
     public final static int DURATION=4000;
     
+    @Ignore
     @Test
     public void monitorTest() throws Exception
     {
-        ((StdErrLog)Log.getLogger(ThreadMonitor.class.getName())).setHideStacks(true);
-        ((StdErrLog)Log.getLogger(ThreadMonitor.class.getName())).setSource(false);
-        
-        final AtomicInteger countLogs=new AtomicInteger(0);
-        final AtomicInteger countSpin=new AtomicInteger(0);
-        
-        ThreadMonitor monitor = new ThreadMonitor(1000,50,1,1)
+        try(StacklessLogging stackless=new StacklessLogging(ThreadMonitor.class))
         {
-            @Override
-            protected void logThreadInfo(boolean logAll)
+
+            final AtomicInteger countLogs=new AtomicInteger(0);
+            final AtomicInteger countSpin=new AtomicInteger(0);
+
+            ThreadMonitor monitor = new ThreadMonitor(1000,50,1,1)
             {
-                if (logAll)
-                    countLogs.incrementAndGet();
-                else
-                    countSpin.incrementAndGet();
-                super.logThreadInfo(logAll);
-            }
-        };
-        monitor.setDumpable(new Dumpable()
-        {
-            public void dump(Appendable out, String indent) throws IOException
+                @Override
+                protected void logThreadInfo(boolean logAll)
+                {
+                    if (logAll)
+                        countLogs.incrementAndGet();
+                    else
+                        countSpin.incrementAndGet();
+                    super.logThreadInfo(logAll);
+                }
+            };
+            monitor.setDumpable(new Dumpable()
             {
-                out.append(dump());
-            }
-            
-            public String dump()
+                public void dump(Appendable out, String indent) throws IOException
+                {
+                    out.append(dump());
+                }
+
+                public String dump()
+                {
+                    return "Dump Spinning";
+                }
+            });
+
+            monitor.logCpuUsage(2000,0);
+            monitor.start();
+
+            Random rnd = new Random();
+            for (long cnt=0; cnt<100; cnt++)
             {
-                return "Dump Spinning";
+                long value = rnd.nextLong() % 50 + 50;
+                Sleeper sleeper = new Sleeper(value);
+                Thread runner = new Thread(sleeper);
+                runner.setDaemon(true);
+                runner.start();
             }
-        });
-        
-        monitor.logCpuUsage(2000,0);
-        monitor.start();
-        
-        Random rnd = new Random();
-        for (long cnt=0; cnt<100; cnt++)
-        {
-            long value = rnd.nextLong() % 50 + 50;
-            Sleeper sleeper = new Sleeper(value);
-            Thread runner = new Thread(sleeper);
-            runner.setDaemon(true);
+
+            Spinner spinner = new Spinner();
+            Thread runner = new Thread(spinner);
             runner.start();
+
+            Thread.sleep(DURATION);
+
+            spinner.setDone();
+            monitor.stop();
+
+            assertTrue(countLogs.get() >= 1);
+            assertTrue(countSpin.get() >= 2);
         }
-        
-        Spinner spinner = new Spinner();
-        Thread runner = new Thread(spinner);
-        runner.start();
-        
-        Thread.sleep(DURATION);
-                
-        spinner.setDone();
-        monitor.stop();
-        
-        assertTrue(countLogs.get() >= 1);
-        assertTrue(countSpin.get() >= 2);
     }
 
 
diff --git a/jetty-nosql/pom.xml b/jetty-nosql/pom.xml
index df17be7..ff0b75a 100644
--- a/jetty-nosql/pom.xml
+++ b/jetty-nosql/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-nosql</artifactId>
@@ -14,57 +14,6 @@
   <build>
     <defaultGoal>install</defaultGoal>
     <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <configuration>
-          <instructions>
-            <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",org.eclipse.jetty.server.session.jmx;version="9.1";resolution:=optional,,org.eclipse.jetty.*;version="9.1",*</Import-Package>
-          </instructions>
-        </configuration>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>artifact-jar</id>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-        </executions>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
     </plugins>
   </build>
   <dependencies>
diff --git a/jetty-nosql/src/main/config/etc/jetty-nosql.xml b/jetty-nosql/src/main/config/etc/jetty-nosql.xml
new file mode 100644
index 0000000..ee1ebd1
--- /dev/null
+++ b/jetty-nosql/src/main/config/etc/jetty-nosql.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <!-- ===================================================================== -->
+  <!-- Configure a MongoSessionIdManager                                     -->
+  <!-- ===================================================================== -->
+  <Set name="sessionIdManager">
+    <New id="sessionIdMgr" class="org.eclipse.jetty.nosql.mongodb.MongoSessionIdManager">
+      <Arg>
+        <Ref refid="Server"/>
+      </Arg>
+      <Set name="workerName"><Property name="jetty.nosqlSession.workerName" default="node1"/></Set>
+      <Set name="scavengePeriod"><Property name="jetty.nosqlSession.scavenge" default="1800"/></Set>
+    </New>
+  </Set>
+
+</Configure>
diff --git a/jetty-nosql/src/main/config/modules/nosql.mod b/jetty-nosql/src/main/config/modules/nosql.mod
index a4189c9..a5b5a9e 100644
--- a/jetty-nosql/src/main/config/modules/nosql.mod
+++ b/jetty-nosql/src/main/config/modules/nosql.mod
@@ -1,9 +1,31 @@
 #
-# Jetty Nosql module
+# Jetty NoSql module
 #
 
 [depend]
 webapp
 
+[files]
+maven://org.mongodb/mongo-java-driver/2.6.1|lib/nosql/mongo-java-driver-2.6.1.jar
+
 [lib]
-lib/jetty-nosql-${jetty.version}.jar
\ No newline at end of file
+lib/jetty-nosql-${jetty.version}.jar
+lib/nosql/*.jar
+
+[xml]
+etc/jetty-nosql.xml
+
+[license]
+The java driver for the MongoDB document-based database system is hosted on GitHub and released under the Apache 2.0 license.
+http://www.mongodb.org/
+http://www.apache.org/licenses/LICENSE-2.0.html
+
+[ini-template]
+## MongoDB SessionIdManager config
+
+## Unique identifier for this node in the cluster
+# jetty.nosqlSession.workerName=node1
+
+## Interval in seconds between scavenging expired sessions
+# jetty.nosqlSession.scavenge=1800
+
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSession.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSession.java
index 30abdb8..b80b2b4 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSession.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSession.java
@@ -18,6 +18,7 @@
 
 package org.eclipse.jetty.nosql;
 
+
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -32,7 +33,9 @@
 /* ------------------------------------------------------------ */
 public class NoSqlSession extends MemSession
 {
-    private final static Logger __log = Log.getLogger("org.eclipse.jetty.server.session");
+    private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    
+    private enum IdleState {NOT_IDLE, IDLE, IDLING, DEIDLING};
 
     private final NoSqlSessionManager _manager;
     private Set<String> _dirty;
@@ -40,6 +43,10 @@
     private Object _version;
     private long _lastSync;
 
+    private IdleState _idle = IdleState.NOT_IDLE;
+    
+    private boolean _deIdleFailed;
+
     /* ------------------------------------------------------------ */
     public NoSqlSession(NoSqlSessionManager manager, HttpServletRequest request)
     {
@@ -73,7 +80,7 @@
     }
     
     
-
+    /* ------------------------------------------------------------ */
     @Override
     public void setAttribute(String name, Object value)
     {
@@ -93,7 +100,7 @@
     }
     
     
-
+    /* ------------------------------------------------------------ */
     @Override
     protected void timeout() throws IllegalStateException
     {
@@ -106,14 +113,28 @@
     @Override
     protected void checkValid() throws IllegalStateException
     {
-        super.checkValid();
+        //whenever a method is called on the session, check that it was not idled and
+        //reinflate it if necessary
+        if (!isDeIdleFailed() && _manager.getIdlePeriod() > 0 && isIdle())
+            deIdle();
+        try
+        {
+            super.checkValid();
+        }
+        catch (IllegalStateException e)
+        {
+            throw new IllegalStateException (e.getMessage()+" idle="+_idle+" deidleFailed="+_deIdleFailed+" version="+_version, e);
+        }
     }
+    
+    
 
     /* ------------------------------------------------------------ */
     @Override
     protected boolean access(long time)
     {
-        __log.debug("NoSqlSession:access:active {} time {}", _active, time);
+        if (LOG.isDebugEnabled())
+            LOG.debug("NoSqlSession:access:active {} time {}", _active, time);
         if (_active.incrementAndGet()==1)
         {
             long period=_manager.getStalePeriod()*1000L;
@@ -122,7 +143,8 @@
             else if (period>0)
             {
                 long stale=time-_lastSync;
-                __log.debug("NoSqlSession:access:stale "+stale);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("NoSqlSession:access:stale "+stale);
                 if (stale>period)
                     refresh();
             }
@@ -170,7 +192,112 @@
             _lastSync=getAccessed();
         }
     }
+    
+    
+    /* ------------------------------------------------------------ */
+    public void idle ()
+    {
+        synchronized (this)
+        {
+            if (!isIdle() && !isIdling()) //don't re-idle an idle session as the attribute map will be empty
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Idling {}", super.getId());
+                setIdling();
+                save(false);
+                willPassivate();
+                clearAttributes();
+                setIdle(true);
+            }
+        }
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    public synchronized void deIdle()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Checking before de-idling {}, isidle:{}, isDeidleFailed:", super.getId(), isIdle(), isDeIdleFailed());
+        
+        if (isIdle() && !isDeIdleFailed())
+        {
 
+            setDeIdling();
+            if (LOG.isDebugEnabled())
+                LOG.debug("De-idling " + super.getId());
+
+            // Update access time to prevent race with idling period
+            super.access(System.currentTimeMillis());
+
+            //access may have expired and invalidated the session, so only deidle if it is still valid
+            if (isValid())
+            {
+                try
+                {    
+                    setIdle(false);
+                    _version=_manager.refresh(this, new Long(0)); //ensure version should not match to force refresh
+                    if (_version == null)
+                        setDeIdleFailed(true);
+                }
+                catch (Exception e)
+                {
+                    setDeIdleFailed(true);
+                    LOG.warn("Problem de-idling session " + super.getId(), e);
+                    invalidate();
+                }
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public synchronized boolean isIdle ()
+    {
+        return _idle == IdleState.IDLE;
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    public synchronized boolean isIdling ()
+    {
+        return _idle == IdleState.IDLING;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public synchronized boolean isDeIdling()
+    {
+        return _idle == IdleState.DEIDLING;
+    }
+    
+    
+    public synchronized void setIdling ()
+    {
+        _idle = IdleState.IDLING;
+    }
+    
+    public synchronized void setDeIdling ()
+    {
+        _idle = IdleState.DEIDLING;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public synchronized void setIdle (boolean idle)
+    {
+        if (idle)
+            _idle = IdleState.IDLE;
+        else
+            _idle = IdleState.NOT_IDLE;
+    }
+    
+
+    public boolean isDeIdleFailed()
+    {
+        return _deIdleFailed;
+    }
+
+    public void setDeIdleFailed(boolean _deIdleFailed)
+    {
+        this._deIdleFailed = _deIdleFailed;
+    }
 
     /* ------------------------------------------------------------ */
     protected void refresh()
@@ -209,13 +336,17 @@
     {
         return _version;
     }
-
+    
+    
+    /* ------------------------------------------------------------ */
     @Override
     public void setClusterId(String clusterId)
     {
         super.setClusterId(clusterId);
     }
 
+    
+    /* ------------------------------------------------------------ */
     @Override
     public void setNodeId(String nodeId)
     {
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java
index ff60f45..80b0c58 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java
@@ -35,7 +35,6 @@
  * NoSqlSessionManager
  *
  * Base class for SessionManager implementations using nosql frameworks
- * 
  */
 public abstract class NoSqlSessionManager extends AbstractSessionManager implements SessionManager
 {
@@ -46,6 +45,7 @@
     private int _stalePeriod=0;
     private int _savePeriod=0;
     private int _idlePeriod=-1;
+    private boolean _deidleBeforeExpiry = true;
     private boolean _invalidateOnStop;
     private boolean _preserveOnStop = true;
     private boolean _saveAllAttributes;
@@ -83,6 +83,8 @@
         
         if (session==null)
         {
+            __log.debug("Session {} is not in memory", idInCluster);
+            
             //session not in this node's memory, load it
             session=loadSession(idInCluster);
             
@@ -100,7 +102,7 @@
                     __log.debug("session loaded ", idInCluster);
                 
                 //check if the session we just loaded has actually expired, maybe while we weren't running
-                if (getMaxInactiveInterval() > 0 && session.getAccessed() > 0 && ((getMaxInactiveInterval()*1000L)+session.getAccessed()) < System.currentTimeMillis())
+                if (session.getMaxInactiveInterval() > 0 && session.getAccessed() > 0 && ((session.getMaxInactiveInterval()*1000L)+session.getAccessed()) < System.currentTimeMillis())
                 {
                     __log.debug("session expired ", idInCluster);
                     expire(idInCluster);
@@ -110,6 +112,8 @@
             else
                 __log.debug("session does not exist {}", idInCluster);
         }
+        else
+            session.deIdle();
 
         return session;
     }
@@ -207,8 +211,15 @@
                 //we need to expire the session with its listeners, so load it
                 session = loadSession(idInCluster);
             }
+            else
+            {
+                //deidle if the session was idled
+                if (isDeidleBeforeExpiry())
+                    session.deIdle();
+            }
 
-            if (session != null)
+            //check that session is still valid after potential de-idle
+            if (session != null && session.isValid())
                 session.timeout();
         }
         catch (Exception e)
@@ -241,7 +252,7 @@
      * The State Period is the maximum time in seconds that an in memory session is allows to be stale:
      * <ul>  
      * <li>If this period is exceeded, the DB will be checked to see if a more recent version is available.</li>
-     * <li>If the state period is set to a value < 0, then no staleness check will be made.</li>
+     * <li>If the state period is set to a value &lt; 0, then no staleness check will be made.</li>
      * <li>If the state period is set to 0, then a staleness check is made whenever the active request count goes from 0 to 1.</li>
      * </ul>
      * @return the stalePeriod in seconds
@@ -256,7 +267,7 @@
      * The State Period is the maximum time in seconds that an in memory session is allows to be stale:
      * <ul>  
      * <li>If this period is exceeded, the DB will be checked to see if a more recent version is available.</li>
-     * <li>If the state period is set to a value < 0, then no staleness check will be made.</li>
+     * <li>If the state period is set to a value &lt; 0, then no staleness check will be made.</li>
      * <li>If the state period is set to 0, then a staleness check is made whenever the active request count goes from 0 to 1.</li>
      * </ul>
      * @param stalePeriod the stalePeriod in seconds
@@ -274,9 +285,9 @@
      * <li>a save period of -1 means the session is never saved to the DB other than on a shutdown</li>
      * <li>a save period of 0 means the session is written to the DB whenever the active request count goes from 1 to 0.</li>
      * <li>a save period of 1 means the session is written to the DB whenever the active request count goes from 1 to 0 and the session is dirty.</li>
-     * <li>a save period of > 1 means the session is written after that period in seconds of being dirty.</li>
+     * <li>a save period of &gt; 1 means the session is written after that period in seconds of being dirty.</li>
      * </ul>
-     * @return the savePeriod -2,-1,0,1 or the period in seconds >=2 
+     * @return the savePeriod -2,-1,0,1 or the period in seconds &gt;=2 
      */
     public int getSavePeriod()
     {
@@ -291,9 +302,9 @@
      * <li>a save period of -1 means the session is never saved to the DB other than on a shutdown</li>
      * <li>a save period of 0 means the session is written to the DB whenever the active request count goes from 1 to 0.</li>
      * <li>a save period of 1 means the session is written to the DB whenever the active request count goes from 1 to 0 and the session is dirty.</li>
-     * <li>a save period of > 1 means the session is written after that period in seconds of being dirty.</li>
+     * <li>a save period of &gt; 1 means the session is written after that period in seconds of being dirty.</li>
      * </ul>
-     * @param savePeriod the savePeriod -2,-1,0,1 or the period in seconds >=2 
+     * @param savePeriod the savePeriod -2,-1,0,1 or the period in seconds &gt;=2 
      */
     public void setSavePeriod(int savePeriod)
     {
@@ -304,8 +315,7 @@
     /**
      * The Idle Period is the time in seconds before an in memory session is passivated.
      * When this period is exceeded, the session will be passivated and removed from memory.  If the session was dirty, it will be written to the DB.
-     * If the idle period is set to a value < 0, then the session is never idled.
-     * If the save period is set to 0, then the session is idled whenever the active request count goes from 1 to 0.
+     * If the idle period is set to a value &lt; 0, then the session is never idled.
      * @return the idlePeriod
      */
     public int getIdlePeriod()
@@ -317,8 +327,7 @@
     /**
      * The Idle Period is the time in seconds before an in memory session is passivated.
      * When this period is exceeded, the session will be passivated and removed from memory.  If the session was dirty, it will be written to the DB.
-     * If the idle period is set to a value < 0, then the session is never idled.
-     * If the save period is set to 0, then the session is idled whenever the active request count goes from 1 to 0.
+     * If the idle period is set to a value &lt; 0, then the session is never idled.
      * @param idlePeriod the idlePeriod in seconds
      */
     public void setIdlePeriod(int idlePeriod)
@@ -359,7 +368,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Preserve sessions when the session manager is stopped otherwise remove them from the DB.
-     * @param removeOnStop the removeOnStop to set
+     * @param preserveOnStop the preserveOnStop to set
      */
     public void setPreserveOnStop(boolean preserveOnStop)
     {
@@ -387,6 +396,18 @@
     }
     
     /* ------------------------------------------------------------ */
+    public boolean isDeidleBeforeExpiry()
+    {
+        return _deidleBeforeExpiry;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void setDeidleBeforeExpiry(boolean deidleBeforeExpiry)
+    {
+        _deidleBeforeExpiry = deidleBeforeExpiry;
+    }
+    
+    /* ------------------------------------------------------------ */
     @Override
     public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
     {
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java
index 252dc47..9f95eb9 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java
@@ -21,6 +21,7 @@
 
 import java.net.UnknownHostException;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -34,6 +35,7 @@
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.session.AbstractSessionIdManager;
 import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.util.ConcurrentHashSet;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
@@ -49,13 +51,13 @@
 
 /**
  * Based partially on the JDBCSessionIdManager.
- *
+ * <p>
  * Theory is that we really only need the session id manager for the local 
  * instance so we have something to scavenge on, namely the list of known ids
- * 
+ * <p>
  * This class has a timer that runs a periodic scavenger thread to query
  *  for all id's known to this node whose precalculated expiry time has passed.
- *  
+ * <p>
  * These found sessions are then run through the invalidateAll(id) method that 
  * is a bit hinky but is supposed to notify all handlers this id is now DOA and 
  * ought to be cleaned up.  this ought to result in a save operation on the session
@@ -74,10 +76,10 @@
     
     final DBCollection _sessions;
     protected Server _server;
-    private Scheduler _scheduler;
-    private boolean _ownScheduler;
-    private Scheduler.Task _scavengerTask;
-    private Scheduler.Task _purgerTask;
+    protected Scheduler _scheduler;
+    protected boolean _ownScheduler;
+    protected Scheduler.Task _scavengerTask;
+    protected Scheduler.Task _purgerTask;
  
 
     
@@ -109,10 +111,15 @@
     
     /**
      * the collection of session ids known to this manager
-     * 
-     * TODO consider if this ought to be concurrent or not
      */
-    protected final Set<String> _sessionsIds = new HashSet<String>();
+    protected final Set<String> _sessionsIds = new ConcurrentHashSet<>();
+
+    /**
+     * The maximum number of items to return from a purge query.
+     */
+    private int _purgeLimit = 0;
+
+    private int _scavengeBlockSize;
     
     
     /**
@@ -127,6 +134,7 @@
             try
             {
                 scavenge();
+                idle();
             }
             finally
             {
@@ -193,6 +201,17 @@
                               .get());
                             
 
+        // index our accessed and valid fields so that purges are faster, note that the "valid" field is first
+        // so that we can take advantage of index prefixes
+        // http://docs.mongodb.org/manual/core/index-compound/#compound-index-prefix
+        
+        DBObject validKey = BasicDBObjectBuilder.start().add(MongoSessionManager.__VALID, 1).add(MongoSessionManager.__ACCESSED, 1).get();
+        _sessions.createIndex(validKey,
+                BasicDBObjectBuilder.start()
+                .add("name", MongoSessionManager.__VALID+"_1_"+MongoSessionManager.__ACCESSED+"_1")
+                .add("ns", _sessions.getFullName())
+                .add("sparse", false)
+                .add("background", true).get());
     }
  
     /* ------------------------------------------------------------ */
@@ -205,29 +224,59 @@
     {
         long now = System.currentTimeMillis();
         __log.debug("SessionIdManager:scavenge:at {}", now);        
-        synchronized (_sessionsIds)
-        {         
-            /*
-             * run a query returning results that:
-             *  - are in the known list of sessionIds
-             *  - the expiry time has passed
-             *  
-             *  we limit the query to return just the __ID so we are not sucking back full sessions
-             */
-            BasicDBObject query = new BasicDBObject();     
-            query.put(MongoSessionManager.__ID,new BasicDBObject("$in", _sessionsIds ));
-            query.put(MongoSessionManager.__EXPIRY, new BasicDBObject("$gt", 0));
-            query.put(MongoSessionManager.__EXPIRY, new BasicDBObject("$lt", now));
-        
+        /*
+         * run a query returning results that:
+         *  - are in the known list of sessionIds
+         *  - the expiry time has passed
+         *  
+         *  we limit the query to return just the __ID so we are not sucking back full sessions
+         *  
+         *  break scavenge query into blocks for faster mongo queries
+         */
+        Set<String> block = new HashSet<String>();
             
-            DBCursor checkSessions = _sessions.find(query, new BasicDBObject(MongoSessionManager.__ID, 1));
-                        
-            for ( DBObject session : checkSessions )
-            {             
-                __log.debug("SessionIdManager:scavenge: expiring session {}", (String)session.get(MongoSessionManager.__ID));
-                expireAll((String)session.get(MongoSessionManager.__ID));
+        Iterator<String> itor = _sessionsIds.iterator();
+        while (itor.hasNext())
+        {
+            block.add(itor.next());
+            if ((_scavengeBlockSize > 0) && (block.size() == _scavengeBlockSize))
+            {
+                //got a block
+                scavengeBlock (now, block);
+                //reset for next run
+                block.clear();
             }
-        }      
+        }
+        
+        //non evenly divisble block size, or doing it all at once
+        if (!block.isEmpty())
+            scavengeBlock(now, block);
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Check a block of session ids for expiry and thus scavenge.
+     * 
+     * @param atTime purge at time
+     * @param ids set of session ids
+     */
+    protected void scavengeBlock (long atTime, Set<String> ids)
+    {
+        if (ids == null)
+            return;
+        
+        BasicDBObject query = new BasicDBObject();     
+        query.put(MongoSessionManager.__ID,new BasicDBObject("$in", ids ));
+        query.put(MongoSessionManager.__EXPIRY, new BasicDBObject("$gt", 0).append("$lt", atTime));   
+            
+        DBCursor checkSessions = _sessions.find(query, new BasicDBObject(MongoSessionManager.__ID, 1));
+                        
+        for ( DBObject session : checkSessions )
+        {             
+            __log.debug("SessionIdManager:scavenge: expiring session {}", (String)session.get(MongoSessionManager.__ID));
+            expireAll((String)session.get(MongoSessionManager.__ID));
+        }            
     }
     
     /* ------------------------------------------------------------ */
@@ -273,11 +322,16 @@
         __log.debug("PURGING");
         BasicDBObject invalidQuery = new BasicDBObject();
 
-        invalidQuery.put(MongoSessionManager.__ACCESSED, new BasicDBObject("$lt",System.currentTimeMillis() - _purgeInvalidAge));
         invalidQuery.put(MongoSessionManager.__VALID, false);
+        invalidQuery.put(MongoSessionManager.__ACCESSED, new BasicDBObject("$lt",System.currentTimeMillis() - _purgeInvalidAge));
         
         DBCursor oldSessions = _sessions.find(invalidQuery, new BasicDBObject(MongoSessionManager.__ID, 1));
 
+        if (_purgeLimit > 0)
+        {
+            oldSessions.limit(_purgeLimit);
+        }
+
         for (DBObject session : oldSessions)
         {
             String id = (String)session.get("id");
@@ -291,11 +345,16 @@
         {
             BasicDBObject validQuery = new BasicDBObject();
 
-            validQuery.put(MongoSessionManager.__ACCESSED,new BasicDBObject("$lt",System.currentTimeMillis() - _purgeValidAge));
             validQuery.put(MongoSessionManager.__VALID, true);
+            validQuery.put(MongoSessionManager.__ACCESSED,new BasicDBObject("$lt",System.currentTimeMillis() - _purgeValidAge));
 
             oldSessions = _sessions.find(validQuery,new BasicDBObject(MongoSessionManager.__ID,1));
 
+            if (_purgeLimit > 0)
+            {
+                oldSessions.limit(_purgeLimit);
+            }
+
             for (DBObject session : oldSessions)
             {
                 String id = (String)session.get(MongoSessionManager.__ID);
@@ -357,7 +416,7 @@
     /** 
      * The period in seconds between scavenge checks.
      * 
-     * @param scavengePeriod
+     * @param scavengePeriod the scavenge period in seconds
      */
     public void setScavengePeriod(long scavengePeriod)
     {
@@ -368,6 +427,42 @@
     }
 
     /* ------------------------------------------------------------ */
+    /** When scavenging, the max number of session ids in the query.
+     * 
+     * @param size the scavenge block size
+     */
+    public void setScavengeBlockSize (int size)
+    {
+        _scavengeBlockSize = size;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getScavengeBlockSize ()
+    {
+        return _scavengeBlockSize;
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * The maximum number of items to return from a purge query. If &lt;= 0 there is no limit. Defaults to 0
+     * 
+     * @param purgeLimit the purge limit 
+     */
+    public void setPurgeLimit(int purgeLimit)
+    {
+        _purgeLimit = purgeLimit;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getPurgeLimit()
+    {
+        return _purgeLimit;
+    }
+
+
+
+    /* ------------------------------------------------------------ */
     public void setPurgeDelay(long purgeDelay)
     {
         if ( isRunning() )
@@ -388,6 +483,7 @@
     /**
      * sets how old a session is to be persisted past the point it is
      * no longer valid
+     * @param purgeValidAge the purge valid age
      */
     public void setPurgeInvalidAge(long purgeValidAge)
     {
@@ -406,6 +502,7 @@
      * considered no longer viable and should be removed
      * 
      * NOTE: set this value to 0 to disable purging of valid sessions
+     * @param purgeValidAge the purge valid age
      */
     public void setPurgeValidAge(long purgeValidAge)
     {
@@ -531,10 +628,7 @@
         
         __log.debug("MongoSessionIdManager:addSession {}", session.getId());
         
-        synchronized (_sessionsIds)
-        {
-            _sessionsIds.add(session.getId());
-        }
+        _sessionsIds.add(session.getId());
         
     }
 
@@ -547,10 +641,7 @@
             return;
         }
         
-        synchronized (_sessionsIds)
-        {
-            _sessionsIds.remove(session.getId());
-        }
+        _sessionsIds.remove(session.getId());
     }
 
     /* ------------------------------------------------------------ */
@@ -562,56 +653,71 @@
     @Override
     public void invalidateAll(String sessionId)
     {
-        synchronized (_sessionsIds)
+        _sessionsIds.remove(sessionId);
+            
+        //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++)
         {
-            _sessionsIds.remove(sessionId);
-                
-            //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 = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+            if (sessionHandler != null) 
             {
-                SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
-                if (sessionHandler != null) 
-                {
-                    SessionManager manager = sessionHandler.getSessionManager();
+                SessionManager manager = sessionHandler.getSessionManager();
 
-                    if (manager != null && manager instanceof MongoSessionManager)
-                    {
-                        ((MongoSessionManager)manager).invalidateSession(sessionId);
-                    }
+                if (manager != null && manager instanceof MongoSessionManager)
+                {
+                    ((MongoSessionManager)manager).invalidateSession(sessionId);
                 }
             }
-        }      
-    } 
+        }
+    }      
+ 
 
     /* ------------------------------------------------------------ */
     /**
      * Expire this session for all contexts that are sharing the session 
      * id.
-     * @param sessionId
+     * @param sessionId the session id
      */
     public void expireAll (String sessionId)
     {
-        synchronized (_sessionsIds)
+        _sessionsIds.remove(sessionId);
+            
+            
+        //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++)
         {
-            _sessionsIds.remove(sessionId);
-            
-            
-            //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 = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+            if (sessionHandler != null) 
             {
-                SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
-                if (sessionHandler != null) 
-                {
-                    SessionManager manager = sessionHandler.getSessionManager();
+                SessionManager manager = sessionHandler.getSessionManager();
 
-                    if (manager != null && manager instanceof MongoSessionManager)
-                    {
-                        ((MongoSessionManager)manager).expire(sessionId);
-                    }
+                if (manager != null && manager instanceof MongoSessionManager)
+                {
+                    ((MongoSessionManager)manager).expire(sessionId);
+                }
+            }
+        }      
+    }
+    
+    
+    public void idle ()
+    {
+        //tell all contexts to passivate out idle sessions
+        Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
+        for (int i=0; contexts!=null && i<contexts.length; i++)
+        {
+            SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+            if (sessionHandler != null) 
+            {
+                SessionManager manager = sessionHandler.getSessionManager();
+
+                if (manager != null && manager instanceof MongoSessionManager)
+                {
+                    ((MongoSessionManager)manager).idle();
                 }
             }
         }      
@@ -624,24 +730,21 @@
         //generate a new id
         String newClusterId = newSessionId(request.hashCode());
 
-        synchronized (_sessionsIds)
+        _sessionsIds.remove(oldClusterId);//remove the old one from the list
+        _sessionsIds.add(newClusterId); //add in the new session id to the list
+
+        //tell all contexts to update the id 
+        Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
+        for (int i=0; contexts!=null && i<contexts.length; i++)
         {
-            _sessionsIds.remove(oldClusterId);//remove the old one from the list
-            _sessionsIds.add(newClusterId); //add in the new session id to the list
-
-            //tell all contexts to update the id 
-            Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
-            for (int i=0; contexts!=null && i<contexts.length; i++)
+            SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+            if (sessionHandler != null) 
             {
-                SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
-                if (sessionHandler != null) 
-                {
-                    SessionManager manager = sessionHandler.getSessionManager();
+                SessionManager manager = sessionHandler.getSessionManager();
 
-                    if (manager != null && manager instanceof MongoSessionManager)
-                    {
-                        ((MongoSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
-                    }
+                if (manager != null && manager instanceof MongoSessionManager)
+                {
+                    ((MongoSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
                 }
             }
         }
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java
index e700789..9828f63 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java
@@ -43,12 +43,11 @@
 import com.mongodb.DBObject;
 import com.mongodb.MongoException;
 import com.mongodb.WriteConcern;
-import com.mongodb.WriteResult;
 
 
 /**
  * MongoSessionManager
- *
+ * <p>
  * Clustered session manager using MongoDB as the shared DB instance.
  * The document model is an outer object that contains the elements:
  * <ul>
@@ -70,30 +69,28 @@
  * </p>
  * <p>
  * For example:
- * <code>
+ * <pre>
  * { "_id"       : ObjectId("52845534a40b66410f228f23"), 
  *    "accessed" :  NumberLong("1384818548903"), 
  *    "maxIdle"  : 1,
+ *    "created"  : NumberLong("1384818548903"),
+ *    "expiry"   : NumberLong("1384818549903"),
+ *    "id"       : "w01ijx2vnalgv1sqrpjwuirprp7", 
+ *    "valid"    : true 
  *    "context"  : { "::/contextA" : { "A"            : "A", 
  *                                     "__metadata__" : { "version" : NumberLong(2) } 
  *                                   },
  *                   "::/contextB" : { "B"            : "B", 
  *                                     "__metadata__" : { "version" : NumberLong(1) } 
  *                                   } 
- *                 }, 
- *    "created"  : NumberLong("1384818548903"),
- *    "expiry"   : NumberLong("1384818549903"),
- *    "id"       : "w01ijx2vnalgv1sqrpjwuirprp7", 
- *    "valid"    : true 
+ *                 } 
  * }
- * </code>
- * </p>
+ * </pre>
  * <p>
  * In MongoDB, the nesting level is indicated by "." separators for the key name. Thus to
  * interact with a session attribute, the key is composed of:
- * "context".unique_context_name.attribute_name
- *  Eg  "context"."::/contextA"."A"
- *  </p>
+ * <code>"context".unique_context_name.attribute_name</code>
+ *  Eg  <code>"context"."::/contextA"."A"</code>
  */
 @ManagedObject("Mongo Session Manager")
 public class MongoSessionManager extends NoSqlSessionManager
@@ -223,7 +220,8 @@
     {
         try
         {
-            __log.debug("MongoSessionManager:save session {}", session.getClusterId());
+            if (__log.isDebugEnabled())
+                __log.debug("MongoSessionManager:save session {}", session.getClusterId());
             session.willPassivate();
 
             // Form query for upsert
@@ -239,9 +237,8 @@
             // handle valid or invalid
             if (session.isValid())
             {
-                long expiry = (session.getMaxInactiveInterval() > 0?(session.getAccessed()+(1000L*getMaxInactiveInterval())):0);
-                __log.debug("MongoSessionManager: calculated expiry {} for session {}", expiry, session.getId());
-                
+                long expiry = (session.getMaxInactiveInterval() > 0?(session.getAccessed()+(1000L*session.getMaxInactiveInterval())):0);
+ 
                 // handle new or existing
                 if (version == null)
                 {
@@ -252,11 +249,12 @@
                     sets.put(__VALID,true);
                    
                     sets.put(getContextAttributeKey(__VERSION),version);
-                    sets.put(__MAX_IDLE, getMaxInactiveInterval());
-                    sets.put(__EXPIRY, expiry);
+                    sets.put(__MAX_IDLE, session.getMaxInactiveInterval());  //seconds
+                    sets.put(__EXPIRY, expiry); //milliseconds
                 }
                 else
                 {
+                   
                     version = new Long(((Number)version).longValue() + 1);
                     update.put("$inc",_version_1); 
                     //if max idle time and/or expiry is smaller for this context, then choose that for the whole session doc
@@ -266,15 +264,22 @@
                     DBObject o = _dbSessions.findOne(new BasicDBObject("id",session.getClusterId()), fields);
                     if (o != null)
                     {
-                        Integer currentMaxIdle = (Integer)o.get(__MAX_IDLE);
-                        Long currentExpiry = (Long)o.get(__EXPIRY);
-                        if (currentMaxIdle != null && getMaxInactiveInterval() > 0 && getMaxInactiveInterval() < currentMaxIdle)
-                            sets.put(__MAX_IDLE, getMaxInactiveInterval());
-                        if (currentExpiry != null && expiry > 0 && expiry != currentExpiry)
+                       Integer tmpInt = (Integer)o.get(__MAX_IDLE);
+                        int currentMaxIdle = (tmpInt == null? 0:tmpInt.intValue());
+                        Long tmpLong = (Long)o.get(__EXPIRY);
+                        long currentExpiry = (tmpLong == null? 0 : tmpLong.longValue()); 
+                        
+                
+                       
+                        if (currentMaxIdle != session.getMaxInactiveInterval())
+                            sets.put(__MAX_IDLE, session.getMaxInactiveInterval());
+
+                        if (currentExpiry != expiry)
                             sets.put(__EXPIRY, expiry);
+                       
                     }
                 }
-                
+            
                 sets.put(__ACCESSED,session.getAccessed());
                 Set<String> names = session.takeDirty();
                 if (isSaveAllAttributes() || upsert)
@@ -325,7 +330,8 @@
     @Override
     protected Object refresh(NoSqlSession session, Object version)
     {
-        __log.debug("MongoSessionManager:refresh session {}", session.getId());
+        if (__log.isDebugEnabled())
+            __log.debug("MongoSessionManager:refresh session {}", session.getId());
 
         // check if our in memory version is the same as what is on the disk
         if (version != null)
@@ -338,7 +344,8 @@
                 
                 if (saved != null && saved.equals(version))
                 {
-                    __log.debug("MongoSessionManager:refresh not needed session {}", session.getId());
+                    if (__log.isDebugEnabled())
+                        __log.debug("MongoSessionManager:refresh not needed session {}", session.getId());
                     return version;
                 }
                 version = saved;
@@ -350,8 +357,9 @@
 
         // If it doesn't exist, invalidate
         if (o == null)
-        {
-            __log.debug("MongoSessionManager:refresh:marking session {} invalid, no object", session.getClusterId());
+        {            
+            if (__log.isDebugEnabled())
+                __log.debug("MongoSessionManager:refresh:marking session {} invalid, no object", session.getClusterId());
             session.invalidate();
             return null;
         }
@@ -360,7 +368,8 @@
         Boolean valid = (Boolean)o.get(__VALID);
         if (valid == null || !valid)
         {
-            __log.debug("MongoSessionManager:refresh:marking session {} invalid, valid flag {}", session.getClusterId(), valid);
+            if (__log.isDebugEnabled())
+                __log.debug("MongoSessionManager:refresh:marking session {} invalid, valid flag {}", session.getClusterId(), valid);
             session.invalidate();
             return null;
         }
@@ -370,6 +379,9 @@
         session.willPassivate();
         try
         {     
+            //replace the maxInactiveInterval with the saved value
+            session.setMaxInactiveInterval((Integer)o.get(__MAX_IDLE));
+            
             DBObject attrs = (DBObject)getNestedValue(o,getContextKey());    
             //if disk version now has no attributes, get rid of them
             if (attrs == null || attrs.keySet().size() == 0)
@@ -389,7 +401,7 @@
                     Object value = decodeValue(attrs.get(name));
 
                     //session does not already contain this attribute, so bind it
-                    if (session.getAttribute(attr) == null)
+                    if (session.doGet(attr) == null)
                     { 
                         session.doPutOrRemove(attr,value);
                         session.bindValue(attr,value);
@@ -438,19 +450,26 @@
 
         return null;
     }
+    
+    
+   
+                             
+    
 
     /*------------------------------------------------------------ */
     @Override
     protected synchronized NoSqlSession loadSession(String clusterId)
     {
         DBObject o = _dbSessions.findOne(new BasicDBObject(__ID,clusterId));
-        
-        __log.debug("MongoSessionManager:id={} loaded={}", clusterId, o);
+
+        if (__log.isDebugEnabled())
+            __log.debug("MongoSessionManager:id={} loaded={}", clusterId, o);
         if (o == null)
             return null;
-        
+
         Boolean valid = (Boolean)o.get(__VALID);
-        __log.debug("MongoSessionManager:id={} valid={}", clusterId, valid);
+        if (__log.isDebugEnabled())
+            __log.debug("MongoSessionManager:id={} valid={}", clusterId, valid);
         if (valid == null || !valid)
             return null;
         
@@ -459,18 +478,21 @@
             Object version = o.get(getContextAttributeKey(__VERSION));
             Long created = (Long)o.get(__CREATED);
             Long accessed = (Long)o.get(__ACCESSED);
+            Integer maxIdle = (Integer)o.get(__MAX_IDLE);
           
             NoSqlSession session = null;
 
             // get the session for the context
             DBObject attrs = (DBObject)getNestedValue(o,getContextKey());
-
-            __log.debug("MongoSessionManager:attrs {}", attrs);
+            if (__log.isDebugEnabled())
+                __log.debug("MongoSessionManager:attrs {}", attrs);
             if (attrs != null)
             {
-                __log.debug("MongoSessionManager: session {} present for context {}", clusterId, getContextKey());
+                if (__log.isDebugEnabled())
+                    __log.debug("MongoSessionManager: session {} present for context {}", clusterId, getContextKey());
                 //only load a session if it exists for this context
                 session = new NoSqlSession(this,created,accessed,clusterId,version);
+                session.setMaxInactiveInterval(maxIdle); //setup the saved maxInactiveInterval for the session
                 
                 for (String name : attrs.keySet())
                 {
@@ -486,7 +508,7 @@
                 }
                 session.didActivate();
             }
-            else
+            else if (__log.isDebugEnabled())
                 __log.debug("MongoSessionManager: session  {} not present for context {}",clusterId, getContextKey());        
 
             return session;
@@ -508,7 +530,8 @@
     @Override
     protected boolean remove(NoSqlSession session)
     {
-        __log.debug("MongoSessionManager:remove:session {} for context {}",session.getClusterId(), getContextKey());
+        if (__log.isDebugEnabled())
+            __log.debug("MongoSessionManager:remove:session {} for context {}",session.getClusterId(), getContextKey());
 
         /*
          * Check if the session exists and if it does remove the context
@@ -542,7 +565,8 @@
     @Override
     protected void expire (String idInCluster)
     {
-        __log.debug("MongoSessionManager:expire session {} ", idInCluster);
+        if (__log.isDebugEnabled())
+            __log.debug("MongoSessionManager:expire session {} ", idInCluster);
 
         //Expire the session for this context
         super.expire(idInCluster);
@@ -565,6 +589,32 @@
     }
     
     
+    
+    /**
+     *  Passivate out any sessions that are not expired, but have been idle
+     *  longer than the idle timeout
+     */
+    protected void idle ()
+    {
+        //no idle timout set, so don't idle out any sessions
+        if (getIdlePeriod() <= 0)
+            return;
+        
+        long idleMs = getIdlePeriod()*1000L;
+        long now = System.currentTimeMillis();
+
+
+        for (NoSqlSession session:_sessions.values())
+        {
+            if (session.getAccessed()+ idleMs < now)
+            {
+                //idle the session by passivating the session to mongo, then clearing the session's attribute map in memory
+                session.idle();
+            }
+        }
+    }
+    
+    
     /*------------------------------------------------------------ */
     /** 
      * Change the session id. Note that this will change the session id for all contexts for which the session id is in use.
@@ -706,6 +756,7 @@
      * 
      * the count() operation itself is optimized to perform on the server side
      * and avoid loading to client side.
+     * @return the session store count
      */
     @ManagedAttribute("total number of known sessions in the store")
     public long getSessionStoreCount()
diff --git a/jetty-osgi/jetty-osgi-alpn/pom.xml b/jetty-osgi/jetty-osgi-alpn/pom.xml
index ccdc2b4..2220949 100644
--- a/jetty-osgi/jetty-osgi-alpn/pom.xml
+++ b/jetty-osgi/jetty-osgi-alpn/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-osgi-alpn</artifactId>
@@ -25,26 +25,32 @@
             </goals>
             <configuration>
               <versionString>${alpn.api.version}</versionString>
+              <propertyPrefix>alpn</propertyPrefix>
             </configuration>
           </execution>
         </executions>
       </plugin>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
         <configuration>
-          <archive>
-            <manifestEntries>
-              <Bundle-ManifestVersion>2</Bundle-ManifestVersion>
+            <instructions>
               <Bundle-SymbolicName>${bundle-symbolic-name};singleton:=true</Bundle-SymbolicName>
               <Bundle-Name>Jetty OSGi ALPN Fragment</Bundle-Name>
-              <Bundle-Version>${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}</Bundle-Version>
-              <Export-Package>org.eclipse.jetty.alpn;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package>
+              <Import-Package>!javax.*;!org.eclipse.jetty.*</Import-Package>
+              <Export-Package>org.eclipse.jetty.alpn;version="${alpn.majorVersion}.${alpn.minorVersion}.${alpn.incrementalVersion}"</Export-Package>
               <Fragment-Host>system.bundle;extension:=framework</Fragment-Host>
-            </manifestEntries>
-          </archive>
+            </instructions>
         </configuration>
       </plugin>
     </plugins>
   </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty.alpn</groupId>
+      <artifactId>alpn-api</artifactId>
+      <version>${alpn.api.version}</version>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
 </project>
diff --git a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
index 51e7db6..d03f77c 100644
--- a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-osgi-boot-jsp</artifactId>
@@ -42,28 +42,7 @@
       <artifactId>apache-jsp</artifactId>
       <version>${project.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.orbit</groupId>
-      <artifactId>javax.servlet.jsp.jstl</artifactId>
-    </dependency>
-   <dependency>
-       <groupId>org.glassfish.web</groupId>
-       <artifactId>javax.servlet.jsp.jstl</artifactId>
-    </dependency>
-   <dependency>
-       <groupId>org.mortbay.jasper</groupId>
-       <artifactId>apache-el</artifactId>
-       <version>8.0.9.M3</version>
-    </dependency>
   </dependencies>
-<!--
-   <dependency>
-       <groupId>javax.el</groupId>
-       <artifactId>javax.el-api</artifactId>
-       <version>3.0.0</version>
-    </dependency>
-  </dependencies>
--->
 
   <build>
     <plugins>
@@ -72,12 +51,6 @@
         <artifactId>maven-jar-plugin</artifactId>
         <executions>
           <execution>
-            <id>artifact-jar</id>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-          <execution>
             <id>test-jar</id>
             <goals>
               <goal>test-jar</goal>
@@ -94,15 +67,6 @@
           <groupId>org.apache.felix</groupId>
           <artifactId>maven-bundle-plugin</artifactId>
           <extensions>true</extensions>
-          <executions>
-              <execution>
-                  <id>bundle-manifest</id>
-                  <phase>process-classes</phase>
-                  <goals>
-                      <goal>manifest</goal>
-                  </goals>
-              </execution>
-          </executions>
           <configuration>
               <instructions>
                 <Bundle-Name>Jetty-OSGi-Jasper Integration</Bundle-Name>
@@ -110,6 +74,7 @@
                 <Fragment-Host>org.eclipse.jetty.osgi.boot</Fragment-Host>
                 <Export-Package>!org.eclipse.jetty.osgi.boot.*</Export-Package>
                 <Import-Package>org.eclipse.jdt.*;resolution:=optional,
+                                org.eclipse.jdt.core.compiler.*;resolution:=optional,
                 com.sun.el;resolution:=optional,
  com.sun.el.lang;resolution:=optional,
  com.sun.el.parser;resolution:=optional,
@@ -125,20 +90,20 @@
  javax.servlet.jsp.jstl.fmt;version="1.2";resolution:=optional,
  javax.servlet.jsp.jstl.sql;version="1.2";resolution:=optional,
  javax.servlet.jsp.jstl.tlv;version="1.2";resolution:=optional,
- org.apache.el;version="[8.0.9,9)";resolution:=optional,
- org.apache.el.lang;version="[8.0.9,9)";resolution:=optional,
- org.apache.el.stream;version="[8.0.9,9)";resolution:=optional,
- org.apache.el.util;version="[8.0.9,9)";resolution:=optional,
- org.apache.el.parser;version="[8.0.9,9)";resolution:=optional,
- org.apache.jasper;version="[8.0.9,9)";resolution:=optional,
- org.apache.jasper.compiler;version="[8.0.9,9)";resolution:=optional,
- org.apache.jasper.compiler.tagplugin;version="[8.0.9,9)";resolution:=optional,
- org.apache.jasper.runtime;version="[8.0.9,9)";resolution:=optional,
- org.apache.jasper.security;version="[8.0.9,9)";resolution:=optional,
- org.apache.jasper.servlet;version="[8.0.9,9)";resolution:=optional,
- org.apache.jasper.tagplugins.jstl;version="[8.0.9,9)";resolution:=optional,
- org.apache.jasper.util;version="[8.0.9,9)";resolution:=optional,
- org.apache.jasper.xmlparser;version="[8.0.9,9)";resolution:=optional,
+ org.apache.el;version="[8.0.23,9)";resolution:=optional,
+ org.apache.el.lang;version="[8.0.23,9)";resolution:=optional,
+ org.apache.el.stream;version="[8.0.23,9)";resolution:=optional,
+ org.apache.el.util;version="[8.0.23,9)";resolution:=optional,
+ org.apache.el.parser;version="[8.0.23,9)";resolution:=optional,
+ org.apache.jasper;version="[8.0.23,9)";resolution:=optional,
+ org.apache.jasper.compiler;version="[8.0.23,9)";resolution:=optional,
+ org.apache.jasper.compiler.tagplugin;version="[8.0.23,9)";resolution:=optional,
+ org.apache.jasper.runtime;version="[8.0.23,9)";resolution:=optional,
+ org.apache.jasper.security;version="[8.0.23,9)";resolution:=optional,
+ org.apache.jasper.servlet;version="[8.0.23,9)";resolution:=optional,
+ org.apache.jasper.tagplugins.jstl;version="[8.0.23,9)";resolution:=optional,
+ org.apache.jasper.util;version="[8.0.23,9)";resolution:=optional,
+ org.apache.jasper.xmlparser;version="[8.0.23,9)";resolution:=optional,
  org.apache.taglibs.standard;version="1.2";resolution:=optional,
  org.apache.taglibs.standard.extra.spath;version="1.2";resolution:=optional,
  org.apache.taglibs.standard.functions;version="1.2";resolution:=optional,
@@ -162,8 +127,8 @@
  org.apache.taglibs.standard.tag.rt.xml;version="1.2";resolution:=optional,
  org.apache.taglibs.standard.tei;version="1.2";resolution:=optional,
  org.apache.taglibs.standard.tlv;version="1.2";resolution:=optional,
- org.apache.tomcat;version="[8.0.9,9)";resolution:=optional,
- org.eclipse.jetty.jsp;version="[9.2,10)";resolution:=optional,
+ org.apache.tomcat;version="[8.0.23,9)";resolution:=optional,
+ org.eclipse.jetty.jsp;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:=optional,
  org.osgi.*,
  org.xml.*;resolution:=optional,
  org.xml.sax.*;resolution:=optional,
@@ -172,8 +137,7 @@
  org.w3c.dom.ls;resolution:=optional,
  javax.xml.parser;resolution:=optional
  </Import-Package>
-               <_nouses>true</_nouses>
-               <DynamicImport-Package>org.eclipse.jetty.jsp.*;version="9.2.6",org.apache.jasper.*;version="8.0.9",org.apache.el.*;version="8.0.9"</DynamicImport-Package>
+               <DynamicImport-Package>org.eclipse.jetty.jsp.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",org.apache.jasper.*;version="8.0.23",org.apache.el.*;version="8.0.23"</DynamicImport-Package>
               </instructions>
           </configuration>
       </plugin>
diff --git a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/JSTLBundleDiscoverer.java b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/JSTLBundleDiscoverer.java
index bca25d2..dff0f06 100644
--- a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/JSTLBundleDiscoverer.java
+++ b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/JSTLBundleDiscoverer.java
@@ -19,19 +19,13 @@
 package org.eclipse.jetty.osgi.boot.jasper;
 
 import java.io.File;
-import java.io.InputStream;
-import java.lang.reflect.Field;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Set;
 
-import javax.servlet.Servlet;
-import javax.servlet.jsp.JspContext;
 import javax.servlet.jsp.JspFactory;
 
-import org.apache.jasper.Constants;
-import org.apache.jasper.compiler.Localizer;
 import org.eclipse.jetty.deploy.DeploymentManager;
 import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
 import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
@@ -40,9 +34,6 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.FrameworkUtil;
-import org.xml.sax.EntityResolver;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
 
 /**
  * 
@@ -68,17 +59,7 @@
      */
     private static String DEFAULT_JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.el.core.WhenTag";
 
-    // used to be "org.apache.jasper.runtime.JspFactoryImpl" but now
-    // the standard tag library implementation are stored in a separate bundle.
-
-    // DISABLED please use the tld bundle argument for the OSGiAppProvider
-    // /**
-    // * Default name of a class that belongs to the bundle where the Java
-    // server Faces tld files are defined.
-    // * This is the sun's reference implementation.
-    // */
-    // private static String DEFAUT_JSF_IMPL_CLASS =
-    // "com.sun.faces.config.ConfigureListener";
+ 
 
     /**
      * Default jsp factory implementation. Idally jasper is osgified and we can
@@ -91,8 +72,6 @@
 
     public JSTLBundleDiscoverer()
     {
-        //fixupDtdResolution();
-
         try
         {
             // sanity check:
@@ -168,8 +147,6 @@
             Bundle tldBundle = FrameworkUtil.getBundle(jstlClass);
             File tldBundleLocation = locatorHelper.getBundleInstallLocation(tldBundle);
             
-            System.err.println("jstl bundle: "+tldBundle);
-            System.err.println("jstl bundle location: "+tldBundleLocation);
             if (tldBundleLocation != null && tldBundleLocation.isDirectory())
             {
                 // try to find the jar files inside this folder
@@ -177,7 +154,6 @@
                 {
                     if (f.getName().endsWith(".jar") && f.isFile())
                     {
-                        System.err.println("Tld jar in dir: "+f.toURI());
                         urls.add(f.toURI().toURL());
                     }
                     else if (f.isDirectory() && f.getName().equals("lib"))
@@ -186,7 +162,6 @@
                         {
                             if (f2.getName().endsWith(".jar") && f2.isFile())
                             {
-                                System.err.println("Tld jar in lib dir: "+f2.toURI());
                                 urls.add(f2.toURI().toURL());
                             }
                         }
@@ -196,7 +171,6 @@
             }
             else if (tldBundleLocation != null)
             {
-                System.err.println("Tld bundle uri: "+tldBundleLocation.toURI());
                 urls.add(tldBundleLocation.toURI().toURL());
               
                 String pattern = (String)deployer.getContextAttribute("org.eclipse.jetty.server.webapp.containerIncludeBundlePattern");
@@ -206,90 +180,10 @@
                     pattern += "|"+tldBundle.getSymbolicName();
                     deployer.setContextAttribute("org.eclipse.jetty.server.webapp.containerIncludeBundlePattern", pattern);
                 }
-                System.err.println("PATTERN: "+pattern);
             }
         }
         
         return urls.toArray(new URL[urls.size()]);
     }
 
-    /**
-     * Jasper resolves the dtd when it parses a taglib descriptor. It uses this
-     * code to do that:
-     * ParserUtils.getClass().getResourceAsStream(resourcePath); where
-     * resourcePath is for example:
-     * /javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd Unfortunately, the
-     * dtd file is not in the exact same classloader as ParserUtils class and
-     * the dtds are packaged in 2 separate bundles. OSGi does not look in the
-     * dependencies' classloader when a resource is searched.
-     * <p>
-     * The workaround consists of setting the entity resolver. That is a patch
-     * added to the version of glassfish-jasper-jetty. IT is also present in the
-     * latest version of glassfish jasper. Could not use introspection to set
-     * new value on a static friendly field :(
-     * </p>
-     */
-   void fixupDtdResolution()
-    {
-        try
-        {
-           // ParserUtils.setEntityResolver(new MyFixedupEntityResolver());
-         
-
-        }
-        catch (Exception e)
-        {
-            e.printStackTrace();
-        }
-
-    }
-
-    /**
-     * Instead of using the ParserUtil's classloader, we use a class that is
-     * indeed next to the resource for sure.
-     */
-    //static class MyFixedupEntityResolver implements EntityResolver
-    //{
-        /**
-         * Same values than in ParserUtils...
-         */
-      /*  static final String[] CACHED_DTD_PUBLIC_IDS = { Constants.TAGLIB_DTD_PUBLIC_ID_11, Constants.TAGLIB_DTD_PUBLIC_ID_12,
-                                                       Constants.WEBAPP_DTD_PUBLIC_ID_22, Constants.WEBAPP_DTD_PUBLIC_ID_23, };
-
-        static final String[] CACHED_DTD_RESOURCE_PATHS = { Constants.TAGLIB_DTD_RESOURCE_PATH_11, Constants.TAGLIB_DTD_RESOURCE_PATH_12,
-                                                           Constants.WEBAPP_DTD_RESOURCE_PATH_22, Constants.WEBAPP_DTD_RESOURCE_PATH_23, };
-
-        static final String[] CACHED_SCHEMA_RESOURCE_PATHS = { Constants.TAGLIB_SCHEMA_RESOURCE_PATH_20, Constants.TAGLIB_SCHEMA_RESOURCE_PATH_21,
-                                                              Constants.WEBAPP_SCHEMA_RESOURCE_PATH_24, Constants.WEBAPP_SCHEMA_RESOURCE_PATH_25, };*/
-
-      /*  public InputSource resolveEntity(String publicId, String systemId) throws SAXException
-        {
-            for (int i = 0; i < CACHED_DTD_PUBLIC_IDS.length; i++)
-            {
-                String cachedDtdPublicId = CACHED_DTD_PUBLIC_IDS[i];
-                if (cachedDtdPublicId.equals(publicId))
-                {
-                    String resourcePath = CACHED_DTD_RESOURCE_PATHS[i];
-                    InputStream input = null;
-                    input = Servlet.class.getResourceAsStream(resourcePath);
-                    if (input == null)
-                    {
-                        input = JspContext.class.getResourceAsStream(resourcePath);
-                        if (input == null)
-                        {*/
-                            // if that failed try again with the original code:
-                            // although it is likely not changed.
-                   /*         input = this.getClass().getResourceAsStream(resourcePath);
-                      }
-                    }
-                    if (input == null) { throw new SAXException(Localizer.getMessage("jsp.error.internal.filenotfound", resourcePath)); }
-                    InputSource isrc = new InputSource(input);
-                    return isrc;
-                }
-            }
-
-            return null;
-        }
-    }*/
-
 }
diff --git a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java
index 7269446..30f4a8b 100644
--- a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java
+++ b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java
@@ -47,9 +47,6 @@
      */
     public void start(BundleContext context) throws Exception
     {
-        //jsr199 compilation does not work in osgi
-        System.setProperty("org.apache.jasper.compiler.disablejsr199", Boolean.TRUE.toString());
-        
         //set up some classes that will look for bundles with tlds that must be converted
         //to urls and treated as if they are on the Jetty container's classpath so that 
         //jasper can deal with them
diff --git a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
index 0eecb0b..b73dc94 100644
--- a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
@@ -19,8 +19,8 @@
       <artifactId>jetty-util</artifactId>
     </dependency>
     <dependency>
-    	<groupId>org.eclipse.osgi</groupId>
-    	<artifactId>org.eclipse.osgi</artifactId>
+      <groupId>org.eclipse.osgi</groupId>
+      <artifactId>org.eclipse.osgi</artifactId>
     </dependency>
   </dependencies>
 
@@ -31,12 +31,6 @@
         <artifactId>maven-jar-plugin</artifactId>
         <executions>
           <execution>
-            <id>artifact-jar</id>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-          <execution>
             <id>test-jar</id>
             <goals>
               <goal>test-jar</goal>
@@ -53,15 +47,6 @@
           <groupId>org.apache.felix</groupId>
           <artifactId>maven-bundle-plugin</artifactId>
           <extensions>true</extensions>
-          <executions>
-              <execution>
-                  <id>bundle-manifest</id>
-                  <phase>process-classes</phase>
-                  <goals>
-                      <goal>manifest</goal> 
-                  </goals>
-              </execution>
-          </executions>
           <configuration>
               <instructions>
                   <Bundle-Name>RFC66 War URL</Bundle-Name>
diff --git a/jetty-osgi/jetty-osgi-boot-warurl/src/main/java/org/eclipse/jetty/osgi/boot/warurl/internal/WarBundleManifestGenerator.java b/jetty-osgi/jetty-osgi-boot-warurl/src/main/java/org/eclipse/jetty/osgi/boot/warurl/internal/WarBundleManifestGenerator.java
index 116b0ce..980cc34 100644
--- a/jetty-osgi/jetty-osgi-boot-warurl/src/main/java/org/eclipse/jetty/osgi/boot/warurl/internal/WarBundleManifestGenerator.java
+++ b/jetty-osgi/jetty-osgi-boot-warurl/src/main/java/org/eclipse/jetty/osgi/boot/warurl/internal/WarBundleManifestGenerator.java
@@ -259,7 +259,7 @@
         {
             poundIndex = url.length();
         }
-        UrlEncoded.decodeUtf8To(url.getBytes(), questionMarkIndex+1,
+        UrlEncoded.decodeUtf8To(url, questionMarkIndex+1,
                     poundIndex - questionMarkIndex - 1, res);
         return res;
     }
diff --git a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-deployer.xml b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-deployer.xml
index b03a648..19b3a5d 100644
--- a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-deployer.xml
+++ b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-deployer.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
diff --git a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-http.xml b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-http.xml
new file mode 100644
index 0000000..319ae6c
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-http.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<!-- ============================================================= -->
+<!-- Configure the Jetty Server instance with an ID "Server"       -->
+<!-- by adding a HTTP connector.                                   -->
+<!-- This configuration must be used in conjunction with jetty.xml -->
+<!-- ============================================================= -->
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <!-- =========================================================== -->
+  <!-- Add a HTTP Connector.                                       -->
+  <!-- Configure an o.e.j.server.ServerConnector with a single     -->
+  <!-- HttpConnectionFactory instance using the common httpConfig  -->
+  <!-- instance defined in jetty.xml                               -->
+  <!--                                                             -->
+  <!-- Consult the javadoc of o.e.j.server.ServerConnector and     -->
+  <!-- o.e.j.server.HttpConnectionFactory for all configuration    -->
+  <!-- that may be set here.                                       -->
+  <!-- =========================================================== -->
+  <Call name="addConnector">
+    <Arg>
+      <New class="org.eclipse.jetty.server.ServerConnector">
+        <Arg name="server"><Ref refid="Server" /></Arg>
+        <Arg name="factories">
+          <Array type="org.eclipse.jetty.server.ConnectionFactory">
+            <Item>
+              <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+                <Arg name="config"><Ref refid="httpConfig" /></Arg>
+              </New>
+            </Item>
+          </Array>
+        </Arg>
+        <Set name="host"><Property name="jetty.http.host" /></Set>
+        <Set name="port"><Property name="jetty.http.port" default="80" /></Set>
+        <Set name="idleTimeout"><Property name="jetty.http.idleTimeout" default="30000"/></Set>
+      </New>
+    </Arg>
+  </Call>
+
+</Configure>
diff --git a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-selector.xml b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-selector.xml
deleted file mode 100644
index 67cd87c..0000000
--- a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-selector.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
-
-
-    <!-- =========================================================== -->
-    <!-- Add connector                                               -->
-    <!-- =========================================================== -->
-
-    <Call name="addConnector">
-      <Arg>
-          <New class="org.eclipse.jetty.server.ServerConnector">
-            <Arg><Ref refid="Server" /></Arg>
-             <Arg name="factories">
-               <Array type="org.eclipse.jetty.server.ConnectionFactory">
-                <Item>
-                  <New class="org.eclipse.jetty.server.HttpConnectionFactory">
-                   <Arg name="config"><Ref refid="httpConfig" /></Arg>
-                  </New>
-                </Item>
-              </Array>
-             </Arg>
-            <Set name="host"><Property name="jetty.host" /></Set>
-            <Set name="port"><Property name="jetty.port" default="8080"/></Set>
-            <Set name="idleTimeout">300000</Set>
-          </New>
-      </Arg>
-    </Call>
-
-</Configure>
diff --git a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty.xml b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty.xml
index 5c882b0..a3850c9 100644
--- a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty.xml
+++ b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 
 <!-- =============================================================== -->
@@ -43,7 +43,7 @@
 
     <New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
       <Set name="secureScheme">https</Set>
-      <Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
+      <Set name="securePort"><Property name="jetty.httpConfig.securePort" default="8443" /></Set>
       <Set name="outputBufferSize">32768</Set>
       <Set name="requestHeaderSize">8192</Set>
       <Set name="responseHeaderSize">8192</Set>
@@ -63,21 +63,25 @@
 
 
     <!-- =========================================================== -->
-    <!-- jetty-jndi by default                                       -->
-    <!-- =========================================================== -->
-    <Call class="org.eclipse.jetty.webapp.Configuration$ClassList" name="setServerDefault">
-      <Arg><Ref refid="Server" /></Arg>
-      <Call name="addAfter">
-        <Arg name="afterClass">org.eclipse.jetty.webapp.FragmentConfiguration</Arg>
-        <Arg>
-          <Array type="String">
-            <Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
-            <Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
-            <Item>org.eclipse.jetty.annotations.AnnotationConfiguration</Item>
-          </Array>
-        </Arg>
-      </Call>
+    <!-- Set up the list of default configuration classes            -->
+    <!-- =========================================================== -->    
+    <Call name="setAttribute">
+       <Arg>org.eclipse.jetty.webapp.configuration</Arg>
+       <Arg>
+        <New class="org.eclipse.jetty.webapp.Configuration$ClassList">
+          <Arg>
+            <Array type="String">
+              <Item>org.eclipse.jetty.osgi.boot.OSGiWebInfConfiguration</Item>
+              <Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
+              <Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
+              <Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
+              <Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>  
+            </Array>
+          </Arg>
+        </New>
+       </Arg>
     </Call>
+   
 
     <Call class="java.lang.System" name="setProperty">
       <Arg>java.naming.factory.initial</Arg>
diff --git a/jetty-osgi/jetty-osgi-boot/pom.xml b/jetty-osgi/jetty-osgi-boot/pom.xml
index fdc45b7..a4215b8 100644
--- a/jetty-osgi/jetty-osgi-boot/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-osgi-boot</artifactId>
@@ -67,12 +67,6 @@
                 <artifactId>maven-jar-plugin</artifactId>
                 <executions>
                     <execution>
-                        <id>artifact-jar</id>
-                        <goals>
-                            <goal>jar</goal>
-                        </goals>
-                    </execution>
-                    <execution>
                         <id>test-jar</id>
                         <goals>
                             <goal>test-jar</goal>
@@ -89,20 +83,11 @@
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
                 <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <id>bundle-manifest</id>
-                        <phase>process-classes</phase>
-                        <goals>
-                            <goal>manifest</goal>
-                        </goals>
-                    </execution>
-                </executions>
                 <configuration>
                     <instructions>
                         <Bundle-SymbolicName>org.eclipse.jetty.osgi.boot;singleton:=true</Bundle-SymbolicName>
                         <Bundle-Activator>org.eclipse.jetty.osgi.boot.JettyBootstrapActivator</Bundle-Activator>
-                        <DynamicImport-Package>org.eclipse.jetty.*;version="[9.1,10.0)"</DynamicImport-Package>
+                        <DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
                         <Import-Package>javax.mail;version="1.4.0";resolution:=optional,
  javax.mail.event;version="1.4.0";resolution:=optional,
  javax.mail.internet;version="1.4.0";resolution:=optional,
@@ -112,8 +97,6 @@
  javax.servlet.http;version="[3.1,3.2)",
  javax.transaction;version="1.1.0";resolution:=optional,
  javax.transaction.xa;version="1.1.0";resolution:=optional,
- org.eclipse.jetty.annotations;version="9.1";resolution:=optional,
- org.eclipse.jetty.plus.webapp;version="9.1";resolution:=optional,
  org.objectweb.asm;version=4;resolution:=optional,
  org.osgi.framework,
  org.osgi.service.cm;version="1.2.0",
@@ -126,6 +109,7 @@
  org.slf4j.helpers;resolution:=optional,
  org.xml.sax,
  org.xml.sax.helpers,
+ org.eclipse.jetty.annotations;resolution:=optional,
  *
                         </Import-Package>
                         <_nouses>true</_nouses>
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
index 62f2d0b..6b14927 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
@@ -25,7 +25,6 @@
 import org.eclipse.jetty.annotations.ClassNameResolver;
 import org.eclipse.jetty.osgi.boot.OSGiWebInfConfiguration;
 import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
-import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.resource.Resource;
@@ -150,7 +149,7 @@
      * @param parser The parser
      * @param webbundle The current webbundle
      * @param fragmentBundle The OSGi fragment bundle to scan
-     * @throws Exception
+     * @throws Exception if unable to parse fragment bundle
      */
     protected void parseFragmentBundle(WebAppContext context, AnnotationParser parser,
             Bundle webbundle, Bundle fragmentBundle) throws Exception
@@ -163,8 +162,7 @@
      * @param context The webapp context
      * @param parser The parser
      * @param webbundle The current webbundle
-     * @param fragmentBundle The OSGi required bundle to scan
-     * @throws Exception
+     * @throws Exception if unable to parse the web bundle
      */
     protected void parseWebBundle(WebAppContext context, AnnotationParser parser, Bundle webbundle)
     throws Exception
@@ -177,8 +175,8 @@
      * @param context The webapp context
      * @param parser The parser
      * @param webbundle The current webbundle
-     * @param fragmentBundle The OSGi required bundle to scan
-     * @throws Exception
+     * @param requiredBundle The OSGi required bundle to scan
+     * @throws Exception if unable to parse the required bundle
      */
     protected void parseRequiredBundle(WebAppContext context, AnnotationParser parser,
             Bundle webbundle, Bundle requiredBundle) throws Exception
@@ -209,8 +207,8 @@
     
     /**
      * Returns the same classname resolver than for the webInfjar scanner
-     * @param context
-     * @return
+     * @param context the web app context
+     * @return the class name resolver
      */
     protected ClassNameResolver createClassNameResolver(final WebAppContext context)
     {
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java
index a0aac73..1330173 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java
@@ -30,7 +30,6 @@
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.eclipse.jetty.annotations.ClassNameResolver;
-import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
 import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory;
 import org.eclipse.jetty.util.ConcurrentHashSet;
 import org.eclipse.jetty.util.resource.Resource;
@@ -52,9 +51,10 @@
     
     /**
      * Keep track of a jetty URI Resource and its associated OSGi bundle.
-     * @param uri
-     * @param bundle
-     * @throws Exception 
+     * 
+     * @param bundle the bundle to index
+     * @return the resource for the bundle
+     * @throws Exception if unable to create the resource reference
      */
     protected Resource indexBundle(Bundle bundle) throws Exception
     {
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractContextProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractContextProvider.java
index 1a7525e..72ba6de 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractContextProvider.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractContextProvider.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.osgi.boot;
 
 import java.io.File;
-import java.net.URL;
 import java.util.Dictionary;
 import java.util.HashMap;
 
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java
index a72453b..bb587af 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java
@@ -20,12 +20,9 @@
 
 import java.io.File;
 import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.HashMap;
-import java.util.List;
 
 import org.eclipse.jetty.deploy.App;
 import org.eclipse.jetty.deploy.AppProvider;
@@ -34,13 +31,13 @@
 import org.eclipse.jetty.osgi.boot.internal.webapp.OSGiWebappClassLoader;
 import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory;
 import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.util.ArrayUtil;
-import org.eclipse.jetty.util.component.AbstractLifeCycle;
 import org.eclipse.jetty.util.Loader;
+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.JarResource;
 import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.Configuration;
 import org.eclipse.jetty.webapp.WebAppContext;
 import org.eclipse.jetty.xml.XmlConfiguration;
 import org.osgi.framework.Bundle;
@@ -48,54 +45,22 @@
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.packageadmin.PackageAdmin;
 
-
-
-
 /**
  * AbstractWebAppProvider
- *
+ * <p>
  * Base class for Jetty DeploymentManager Providers that are capable of deploying a webapp,
  * either from a bundle or an OSGi service.
- * 
  */
 public abstract class AbstractWebAppProvider extends AbstractLifeCycle implements AppProvider
 {
     private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
     
-    public static String __defaultConfigurations[] = {
-                                                            "org.eclipse.jetty.osgi.boot.OSGiWebInfConfiguration",
-                                                            "org.eclipse.jetty.webapp.WebXmlConfiguration",
-                                                            "org.eclipse.jetty.webapp.MetaInfConfiguration",
-                                                            "org.eclipse.jetty.webapp.FragmentConfiguration",
-                                                            "org.eclipse.jetty.webapp.JettyWebXmlConfiguration"                          
-                                                     };
-    
-    public static void setDefaultConfigurations (String[] defaultConfigs)
-    {
-        __defaultConfigurations = defaultConfigs;
-    }
-    
-    public static String[] getDefaultConfigurations ()
-    {
-        List<String> configs = ArrayUtil.asMutableList(__defaultConfigurations);
-        if (annotationsAvailable())
-        {
-            //add before JettyWebXmlConfiguration
-            int i = configs.indexOf("org.eclipse.jetty.webapp.JettyWebXmlConfiguration");
-            configs.add(i, "org.eclipse.jetty.osgi.annotations.AnnotationConfiguration");
-        }
-        
-        if (jndiAvailable())
-        {
-            //add in EnvConfiguration and PlusConfiguration just after FragmentConfiguration
-            int i = configs.indexOf("org.eclipse.jetty.webapp.FragmentConfiguration");
-            configs.add(++i, "org.eclipse.jetty.plus.webapp.EnvConfiguration");
-            configs.add(++i, "org.eclipse.jetty.plus.webapp.PlusConfiguration");
-        }
-
-        return configs.toArray(new String[configs.size()]);
-    }
-
+    /* ------------------------------------------------------------ */
+    /**
+     * Check if we should be enabling annotation processing
+     * 
+     * @return true if the jetty-annotations.jar is present, false otherwise
+     */
     private static boolean annotationsAvailable()
     {
         boolean result = false;
@@ -114,7 +79,12 @@
         return result;
     }
     
-    
+    /* ------------------------------------------------------------ */
+    /**
+     * Check if jndi is support is present.
+     * 
+     * @return true if the jetty-jndi.jar is present, false otherwise
+     */
     private static boolean jndiAvailable()
     {
         try
@@ -146,11 +116,13 @@
     
     private ServerInstanceWrapper _serverWrapper;
     
+    
+    
     /* ------------------------------------------------------------ */
     /**
      * OSGiApp
      *
-     *
+     * Represents a deployable webapp.
      */
     public class OSGiApp extends AbstractOSGiApp
     {
@@ -316,10 +288,8 @@
             // Set up what has been configured on the provider
             _webApp.setParentLoaderPriority(isParentLoaderPriority());
             _webApp.setExtractWAR(isExtract());
-            if (getConfigurationClasses() != null)
-                _webApp.setConfigurationClasses(getConfigurationClasses());
-            else
-                _webApp.setConfigurationClasses(getDefaultConfigurations());
+            _webApp.setConfigurationClasses(getConfigurationClasses());
+
 
             if (getDefaultsDescriptor() != null)
                 _webApp.setDefaultsDescriptor(getDefaultsDescriptor());
@@ -605,13 +575,27 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * 
-     */
     public String[] getConfigurationClasses()
     {
-        return _configurationClasses;
+        if (_configurationClasses != null)
+            return _configurationClasses;
+
+        Configuration.ClassList defaults = Configuration.ClassList.serverDefault(_serverWrapper.getServer());
+
+        //add before JettyWebXmlConfiguration
+        if (annotationsAvailable())
+            defaults.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration", 
+                               "org.eclipse.jetty.osgi.annotations.AnnotationConfiguration");
+
+        //add in EnvConfiguration and PlusConfiguration just after FragmentConfiguration
+        if (jndiAvailable())
+            defaults.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration",
+                              "org.eclipse.jetty.plus.webapp.EnvConfiguration",
+                              "org.eclipse.jetty.plus.webapp.PlusConfiguration");
+       String[] asArray = new String[defaults.size()];
+       return defaults.toArray(asArray);
     }
+    
 
     /* ------------------------------------------------------------ */
     public void setServerInstanceWrapper(ServerInstanceWrapper wrapper)
@@ -625,9 +609,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @return
-     */
     public DeploymentManager getDeploymentManager()
     {
         return _deploymentManager;
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java
index d960611..82e40ee 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java
@@ -33,28 +33,20 @@
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.ServiceRegistration;
 
-
-
 /**
  * BundleContextProvider
- *
+ * <p>
  * Handles deploying OSGi bundles that define a context xml file for configuring them.
- * 
- *
  */
 public class BundleContextProvider extends AbstractContextProvider implements BundleProvider
 {    
     private static final Logger LOG = Log.getLogger(AbstractContextProvider.class);
-    
 
     private Map<String, App> _appMap = new HashMap<String, App>();
     
     private Map<Bundle, List<App>> _bundleMap = new HashMap<Bundle, List<App>>();
     
     private ServiceRegistration _serviceRegForBundles;
-    
-
-    
   
     /* ------------------------------------------------------------ */
     public BundleContextProvider(ServerInstanceWrapper wrapper)
@@ -96,11 +88,6 @@
 
 
     /* ------------------------------------------------------------ */
-    /**
-     * @param bundle
-     * @param contextFiles
-     * @return
-     */
     public boolean bundleAdded (Bundle bundle) throws Exception
     {
         if (bundle == null)
@@ -149,8 +136,8 @@
     /* ------------------------------------------------------------ */
     /** 
      * Bundle has been removed. If it was a context we deployed, undeploy it.
-     * @param bundle
      * 
+     * @param bundle the bundle
      * @return true if this was a context we had deployed, false otherwise
      */
     public boolean bundleRemoved (Bundle bundle) throws Exception
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java
index da4a5d2..a5b2b6f 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java
@@ -25,19 +25,17 @@
 
 import org.eclipse.jetty.deploy.App;
 import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
+import org.eclipse.jetty.osgi.boot.utils.Util;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.ServiceRegistration;
 
-
-
 /**
  * BundleWebAppProvider
- *
+ * <p>
  * A Jetty Provider that knows how to deploy a WebApp contained inside a Bundle.
- * 
  */
 public class BundleWebAppProvider extends AbstractWebAppProvider implements BundleProvider
 {     
@@ -52,17 +50,11 @@
     
 
     /* ------------------------------------------------------------ */
-    /**
-     * @param wrapper
-     */
     public BundleWebAppProvider (ServerInstanceWrapper wrapper)
     {
         super(wrapper);
     }
     
-    
-    
-    
     /* ------------------------------------------------------------ */
     /** 
      * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
@@ -107,7 +99,7 @@
     /* ------------------------------------------------------------ */
     /**
      * A bundle has been added that could be a webapp 
-     * @param bundle
+     * @param bundle the bundle
      */
     public boolean bundleAdded (Bundle bundle) throws Exception
     {
@@ -122,9 +114,10 @@
             Dictionary headers = bundle.getHeaders();
 
             //does the bundle have a OSGiWebappConstants.JETTY_WAR_FOLDER_PATH 
-            if (headers.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH) != null)
+            String resourcePath = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH, OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH, headers);
+            if (resourcePath != null)
             {
-                String base = (String)headers.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
+                String base = resourcePath;
                 contextPath = getContextPath(bundle);
                 String originId = getOriginId(bundle, base);
  
@@ -188,8 +181,8 @@
     /* ------------------------------------------------------------ */
     /** 
      * Bundle has been removed. If it was a webapp we deployed, undeploy it.
-     * @param bundle
      * 
+     * @param bundle the bundle
      * @return true if this was a webapp we had deployed, false otherwise
      */
     public boolean bundleRemoved (Bundle bundle) throws Exception
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
index 000a361..4840782 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
@@ -36,13 +36,12 @@
 
 /**
  * JettyBootstrapActivator
- * 
+ * <p>
  * Bootstrap jetty and publish a default Server instance as an OSGi service.
- * 
+ * <p>
  * Listen for other Server instances to be published as services and support them as deployment targets.
- * 
+ * <p>
  * Listen for Bundles to be activated, and deploy those that represent webapps/ContextHandlers to one of the known Server instances.
- * 
  */
 public class JettyBootstrapActivator implements BundleActivator
 {
@@ -74,7 +73,7 @@
      * webapps. Setup the BundleListener that supports the extender pattern for
      * the jetty ContextHandler.
      * 
-     * @param context
+     * @param context the bundle context
      */
     public void start(final BundleContext context) throws Exception
     {
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java
index 16b80d6..64969f0 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java
@@ -30,6 +30,7 @@
      * configuration.
      */
     public static final String JETTY_HOME = "jetty.home";
+    public static final String JETTY_BASE = "jetty.base";
 
     /**
      * System property to point to a bundle that embeds a jetty configuration
@@ -42,21 +43,20 @@
      * Usual system property used as the hostname for a typical jetty
      * configuration.
      */
-    public static final String JETTY_HOST = "jetty.host";
+    public static final String JETTY_HOST = "jetty.http.host";
 
     /**
      * Usual system property used as the port for http for a typical jetty
      * configuration.
      */
-    public static final String JETTY_PORT = "jetty.port";
+    public static final String JETTY_PORT = "jetty.http.port";
 
     /**
      * Usual system property used as the port for https for a typical jetty
      * configuration.
      */
-    public static final String JETTY_PORT_SSL = "jetty.port.ssl";
-    
-    
+    public static final String JETTY_PORT_SSL = "jetty.ssl.port";
+
     //for managed jetty instances, name of the configuration parameters
     /**
      * PID of the jetty servers's ManagedFactory
@@ -85,5 +85,4 @@
      * List of URLs to the folders where the legacy J2EE shared libraries are stored aka lib/ext, lib/jsp etc.
      */
     public static final String MANAGED_JETTY_SHARED_LIB_FOLDER_URLS = "managedJettySharedLibFolderUrls";
-    
 }
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java
index 19fa68c..8587178 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java
@@ -19,16 +19,21 @@
 package org.eclipse.jetty.osgi.boot;
 
 import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.TreeMap;
 import java.util.regex.Pattern;
 
 import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory;
+import org.eclipse.jetty.osgi.boot.utils.Util;
 import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -202,7 +207,7 @@
     @Override
     public void configure(WebAppContext context) throws Exception
     {
-        TreeMap<String, Resource> patchResourcesPath = new TreeMap<String, Resource>();
+        TreeMap<String, Resource> prependedResourcesPath = new TreeMap<String, Resource>();
         TreeMap<String, Resource> appendedResourcesPath = new TreeMap<String, Resource>();
              
         Bundle bundle = (Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
@@ -226,46 +231,33 @@
                 // looked up.
                 for (Bundle frag : fragments)
                 {
-                    String fragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH);
-                    String patchFragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH);
-                    if (fragFolder != null)
-                    {
-                        URL fragUrl = frag.getEntry(fragFolder);
-                        if (fragUrl == null) { throw new IllegalArgumentException("Unable to locate " + fragFolder
-                                                                                  + " inside "
-                                                                                  + " the fragment '"
-                                                                                  + frag.getSymbolicName()
-                                                                                  + "'"); }
-                        fragUrl = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(fragUrl);
-                        String key = fragFolder.startsWith("/") ? fragFolder.substring(1) : fragFolder;
-                        appendedResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(fragUrl));
-                    }
-                    if (patchFragFolder != null)
-                    {
-                        URL patchFragUrl = frag.getEntry(patchFragFolder);
-                        if (patchFragUrl == null)
-                        { 
-                            throw new IllegalArgumentException("Unable to locate " + patchFragUrl
-                                                               + " inside fragment '"+frag.getSymbolicName()+ "'"); 
-                        }
-                        patchFragUrl = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(patchFragUrl);
-                        String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder;
-                        patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl));
-                    }
+                    String path = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH,OSGiWebappConstants.JETTY_WAR_FRAGMENT_RESOURCE_PATH,frag.getHeaders());
+                    convertFragmentPathToResource(path, frag, appendedResourcesPath);
+                    path = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH, OSGiWebappConstants.JETTY_WAR_PREPEND_FRAGMENT_RESOURCE_PATH, frag.getHeaders());
+                    convertFragmentPathToResource(path, frag, prependedResourcesPath);
                 }
                 if (!appendedResourcesPath.isEmpty())
-                    context.setAttribute(WebInfConfiguration.RESOURCE_DIRS, new HashSet<Resource>(appendedResourcesPath.values()));
+                {
+                    LinkedHashSet<Resource> resources = new LinkedHashSet<Resource>();
+                    //Add in any existing setting of extra resource dirs
+                    Set<Resource> resourceDirs = (Set<Resource>)context.getAttribute(WebInfConfiguration.RESOURCE_DIRS);
+                    if (resourceDirs != null && !resourceDirs.isEmpty())
+                        resources.addAll(resourceDirs);
+                    //Then append the values from JETTY_WAR_FRAGMENT_FOLDER_PATH
+                    resources.addAll(appendedResourcesPath.values());
+                    
+                    context.setAttribute(WebInfConfiguration.RESOURCE_DIRS, resources);
+                }
             }
         }
         
         super.configure(context);
 
-        // place the patch resources at the beginning of the contexts's resource base
-        if (!patchResourcesPath.isEmpty())
+        // place the prepended resources at the beginning of the contexts's resource base
+        if (!prependedResourcesPath.isEmpty())
         {
-            Resource[] resources = new Resource[1+patchResourcesPath.size()];
-            ResourceCollection mergedResources = new ResourceCollection (patchResourcesPath.values().toArray(new Resource[patchResourcesPath.size()]));
-            System.arraycopy(patchResourcesPath.values().toArray(new Resource[patchResourcesPath.size()]), 0, resources, 0, patchResourcesPath.size());
+            Resource[] resources = new Resource[1+prependedResourcesPath.size()];
+            System.arraycopy(prependedResourcesPath.values().toArray(new Resource[prependedResourcesPath.size()]), 0, resources, 0, prependedResourcesPath.size());
             resources[resources.length-1] = context.getBaseResource();
             context.setBaseResource(new ResourceCollection(resources));
         }
@@ -311,4 +303,41 @@
         
         return resources;
     }
+    
+
+    /**
+     * Convert a path inside a fragment into a Resource
+     * @param resourcePath
+     * @param fragment
+     * @param resourceMap
+     * @throws Exception
+     */
+    private void convertFragmentPathToResource (String resourcePath, Bundle fragment, Map<String, Resource> resourceMap )
+    throws Exception
+    {
+        if (resourcePath == null)
+            return;
+
+        URL url = fragment.getEntry(resourcePath);
+        if (url == null) 
+        { 
+            throw new IllegalArgumentException("Unable to locate " + resourcePath
+                                               + " inside "
+                                               + " the fragment '"
+                                               + fragment.getSymbolicName()
+                                               + "'"); 
+        }
+        url = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(url);
+        URI uri;
+        try
+        {
+           uri = url.toURI();
+        }
+        catch (URISyntaxException e)
+        {
+            uri = new URI(url.toString().replaceAll(" ", "%20"));
+        }
+        String key = resourcePath.startsWith("/") ? resourcePath.substring(1) : resourcePath;
+        resourceMap.put(key + ";" + fragment.getSymbolicName(), Resource.newResource(uri));
+    }
 }
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java
index 025e99a..d00cb5b 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java
@@ -67,16 +67,23 @@
     public static final String JETTY_CONTEXT_FILE_PATH = "Jetty-ContextFilePath";
 
     /** path within the bundle to the folder that contains the basic resources. */
+    @Deprecated
     public static final String JETTY_WAR_FOLDER_PATH = "Jetty-WarFolderPath";
+    public static final String JETTY_WAR_RESOURCE_PATH = "Jetty-WarResourcePath";
 
     /** path within a fragment hosted by a web-bundle to a folder that contains basic resources.
      * the path is appended to the lookup path where jetty locates static resources */
+    @Deprecated
     public static final String JETTY_WAR_FRAGMENT_FOLDER_PATH = "Jetty-WarFragmentFolderPath";
+    public static final String JETTY_WAR_FRAGMENT_RESOURCE_PATH = "Jetty-WarFragmentResourcePath";
+    
 
     /** path within a fragment hosted by a web-bundle to a folder that contains basic resources.
      * The path is prefixed to the lookup path where jetty locates static resources:
      * this will override static resources with the same name in the web-bundle. */
+    @Deprecated
     public static final String JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH = "Jetty-WarPatchFragmentFolderPath";
+    public static final String JETTY_WAR_PREPEND_FRAGMENT_RESOURCE_PATH = "Jetty-WarPrependFragmentResourcePath";
 
   
     /** installation path of webapp bundle
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceWebAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceWebAppProvider.java
index 81133bd..1c2f5e9 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceWebAppProvider.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceWebAppProvider.java
@@ -36,19 +36,15 @@
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 
-
-
 /**
  * ServiceWebAppProvider
- *
+ * <p>
  * Jetty Provider that knows how to deploy a WebApp that has been registered as an OSGi service.
- * 
  */
 public class ServiceWebAppProvider extends AbstractWebAppProvider implements ServiceProvider
 {   
     private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
     
-    
     /**
      * Map of ServiceRef to App. Used when it is an osgi service that is a WebAppContext.
      */
@@ -56,7 +52,6 @@
     
     private ServiceRegistration _serviceRegForServices;
     
-    
     /**
      * ServiceApp
      *
@@ -91,9 +86,6 @@
     
     
     /* ------------------------------------------------------------ */
-    /**
-     * @param wrapper
-     */
     public ServiceWebAppProvider (ServerInstanceWrapper wrapper)
     {
         super(wrapper);
@@ -128,6 +120,8 @@
      
         String base = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
         if (base == null)
+            base = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH);
+        if (base == null)
             base = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_WAR);
         
        if (base == null)
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
index 88ace4a..731e222 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
@@ -20,7 +20,6 @@
 
 import java.io.File;
 import java.net.MalformedURLException;
-import java.net.URI;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Dictionary;
@@ -32,7 +31,6 @@
 import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
 import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
 import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory;
-import org.eclipse.jetty.osgi.boot.utils.OSGiClassLoader;
 import org.eclipse.jetty.osgi.boot.utils.Util;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.util.log.Log;
@@ -44,12 +42,11 @@
 
 /**
  * DefaultJettyAtJettyHomeHelper
- * 
- * 
+ * <p>
  * Creates a default instance of Jetty, based on the values of the
  * System properties "jetty.home" or "jetty.home.bundle", one of which
  * must be specified in order to create the default instance.
- * 
+ * <p> 
  * Called by the {@link JettyBootstrapActivator} during the starting of the
  * bundle. 
  */
@@ -65,7 +62,7 @@
     /**
      * Set of config files to apply to a jetty Server instance if none are supplied by SYS_PROP_JETTY_ETC_FILES
      */
-    public static final String DEFAULT_JETTY_ETC_FILES = "etc/jetty.xml,etc/jetty-selector.xml,etc/jetty-deployer.xml";
+    public static final String DEFAULT_JETTY_ETC_FILES = "etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml";
     
     /**
      * Default location within bundle of a jetty home dir.
@@ -90,10 +87,13 @@
      * files.
      * </p>
      * <p>
-     * In both cases the system properties jetty.host, jetty.port and
-     * jetty.port.ssl are passed to the configuration files that might use them
+     * In both cases the system properties jetty.http.host, jetty.http.port and
+     * jetty.ssl.port are passed to the configuration files that might use them
      * as part of their properties.
      * </p>
+     * @param bundleContext the bundle context
+     * @return the configured server
+     * @throws Exception if unable to create / configure / or start the server
      */
     public static Server startJettyAtJettyHome(BundleContext bundleContext) throws Exception
     {
@@ -157,7 +157,12 @@
         List<URL> configURLs = jettyHomeDir != null ? getJettyConfigurationURLs(jettyHomeDir) : getJettyConfigurationURLs(jettyHomeBundle, properties);
 
         LOG.info("Configuring the default jetty server with {}",configURLs);
-        LOG.info("JETTY.HOME="+properties.get(OSGiServerConstants.JETTY_HOME));
+        String home=properties.get(OSGiServerConstants.JETTY_HOME);
+        String base=properties.get(OSGiServerConstants.JETTY_BASE);
+        if (base==null)
+            base=home;
+        LOG.info("JETTY.HOME="+home);
+        LOG.info("JETTY.BASE="+base);
         ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
         try
         {
@@ -166,13 +171,13 @@
             // these properties usually are the ones passed to this type of
             // configuration.
             properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME);
-            Util.setProperty(properties, OSGiServerConstants.JETTY_HOST, System.getProperty(OSGiServerConstants.JETTY_HOST));
-            Util.setProperty(properties, OSGiServerConstants.JETTY_PORT, System.getProperty(OSGiServerConstants.JETTY_PORT));
-            Util.setProperty(properties, OSGiServerConstants.JETTY_PORT_SSL, System.getProperty(OSGiServerConstants.JETTY_PORT_SSL));
-
+            Util.setProperty(properties, OSGiServerConstants.JETTY_HOST, System.getProperty(OSGiServerConstants.JETTY_HOST, System.getProperty("jetty.host")));
+            Util.setProperty(properties, OSGiServerConstants.JETTY_PORT, System.getProperty(OSGiServerConstants.JETTY_PORT, System.getProperty("jetty.port")));
+            Util.setProperty(properties, OSGiServerConstants.JETTY_PORT_SSL, System.getProperty(OSGiServerConstants.JETTY_PORT_SSL, System.getProperty("ssl.port")));
+            Util.setProperty(properties, OSGiServerConstants.JETTY_HOME, home);
+            Util.setProperty(properties, OSGiServerConstants.JETTY_BASE, base);
             Server server = ServerInstanceWrapper.configure(null, configURLs, properties);
-            //ensure jetty.home is set
-            server.setAttribute(OSGiServerConstants.JETTY_HOME, properties.get(OSGiServerConstants.JETTY_HOME));
+            
             
             //Register the default Server instance as an OSGi service.
             //The JettyServerServiceTracker will notice it and set it up to deploy bundles as wars etc
@@ -180,6 +185,11 @@
             LOG.info("Default jetty server configured");
             return server;
         }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+            throw e;
+        }
         finally
         {
             Thread.currentThread().setContextClassLoader(contextCl);
@@ -280,9 +290,9 @@
     /**
      * Get a resource representing a directory inside a bundle. If the dir is null,
      * return a resource representing the installation location of the bundle.
-     * @param bundle
-     * @param dir
-     * @return
+     * @param bundle the bundle
+     * @param dir the directory
+     * @return the resource found
      */
     public static Resource findDir (Bundle bundle, String dir)
     {
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java
index 153b4da..aaa2b8f 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.osgi.boot.internal.serverfactory;
 
 import java.io.File;
-import java.io.InputStream;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -52,12 +51,10 @@
 import org.eclipse.jetty.osgi.boot.utils.Util;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
-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;
 import org.eclipse.jetty.xml.XmlConfiguration;
-import org.xml.sax.SAXParseException;
 
 /**
  * ServerInstanceWrapper
@@ -147,47 +144,42 @@
 
         for (URL jettyConfiguration : jettyConfigurations)
         {
-            InputStream is = null;
-            try
-            {
-                // Execute a Jetty configuration file
-                Resource r = Resource.newResource(jettyConfiguration);
-                if (!r.exists())
-                {
-                    LOG.warn("File does not exist "+r);
-                    throw new IllegalStateException("No such jetty server config file: "+r);
-                }
-                is = r.getInputStream();
-                XmlConfiguration config = new XmlConfiguration(is);
-                config.getIdMap().putAll(id_map);
-                config.getProperties().putAll(properties);
-                
-                // #334062 compute the URL of the folder that contains the
-                // conf file and set it as a property so we can compute relative paths
-                // from it.
-                String urlPath = jettyConfiguration.toString();
-                int lastSlash = urlPath.lastIndexOf('/');
-                if (lastSlash > 4)
-                {
-                    urlPath = urlPath.substring(0, lastSlash);
-                    config.getProperties().put(PROPERTY_THIS_JETTY_XML_FOLDER_URL, urlPath);
-                }
-     
-                Object o = config.configure();
-                if (server == null)
-                    server = (Server)o;
-                
-                id_map = config.getIdMap();
-            }
-            catch (SAXParseException saxparse)
-            {
-                LOG.warn("Unable to configure the jetty/etc file " + jettyConfiguration, saxparse);
-                throw saxparse;
-            }
-            finally
-            {
-                IO.close(is);
-            }
+        	try(Resource r = Resource.newResource(jettyConfiguration))
+        	{
+        		// Execute a Jetty configuration file
+        		if (!r.exists())
+        		{
+        			LOG.warn("File does not exist "+r);
+        			throw new IllegalStateException("No such jetty server config file: "+r);
+        		}
+
+        		XmlConfiguration config = new XmlConfiguration(r.getURL());
+
+        		config.getIdMap().putAll(id_map);
+        		config.getProperties().putAll(properties);
+
+        		// #334062 compute the URL of the folder that contains the
+        		// conf file and set it as a property so we can compute relative paths
+        		// from it.
+        		String urlPath = jettyConfiguration.toString();
+        		int lastSlash = urlPath.lastIndexOf('/');
+        		if (lastSlash > 4)
+        		{
+        			urlPath = urlPath.substring(0, lastSlash);
+        			config.getProperties().put(PROPERTY_THIS_JETTY_XML_FOLDER_URL, urlPath);
+        		}
+
+        		Object o = config.configure();
+        		if (server == null)
+        			server = (Server)o;
+
+        		id_map = config.getIdMap();
+        	}
+        	catch (Exception e)
+        	{
+        		LOG.warn("Configuration error in " + jettyConfiguration);
+        		throw e;
+        	}
         }
 
         return server;
@@ -214,7 +206,7 @@
      * The classloader that should be the parent classloader for each webapp
      * deployed on this server.
      * 
-     * @return
+     * @return the classloader
      */
     public ClassLoader getParentClassLoaderForWebapps()
     {
@@ -250,7 +242,6 @@
         return _ctxtCollection;
     }
     
-    
     /* ------------------------------------------------------------ */
     public void start(Server server, Dictionary props) throws Exception
     {
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/BundleWatcher.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/BundleWatcher.java
index 30d4121..a2bab07 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/BundleWatcher.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/BundleWatcher.java
@@ -42,12 +42,9 @@
 /**
  * BundleWatcher
  * 
- * 
  * Tracks the installation and removal of Bundles in the OSGi environment. Any bundles
  * that are added are passed to the set of Jetty DeploymentManager providers to see if
  * the bundle should be deployed as a webapp or ContextHandler into Jetty.
- * 
- * @author hmalphettes
  */
 public class BundleWatcher implements BundleTrackerCustomizer
 {
@@ -66,9 +63,6 @@
  
     
     /* ------------------------------------------------------------ */
-    /**
-     * @throws Exception
-     */
     public BundleWatcher() throws Exception
     {
         _bundle = FrameworkUtil.getBundle(this.getClass());
@@ -133,10 +127,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @param managedServerName
-     * @return
-     */
     public Map<ServiceReference, BundleProvider> getDeployers(String managedServerName)
     {
         if (managedServerName == null)
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java
index 1148b7c..cc96f81 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java
@@ -30,14 +30,11 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.eclipse.jetty.server.Server;
-
 /**
  * LibExtClassLoaderHelper
- * 
- * 
+ * <p>
  * Helper to create a URL class-loader with the jars inside
- * ${jetty.home}/lib/ext and ${jetty.home}/resources. In an ideal world, every
+ * <code>${jetty.home}/lib/ext</code> and <code>${jetty.home}/resources</code>. In an ideal world, every
  * library is an OSGi bundle that does loads nicely. To support standard jars or
  * bundles that cannot be loaded in the current OSGi environment, we support
  * inserting the jars in the usual jetty/lib/ext folders in the proper classpath
@@ -45,7 +42,6 @@
  * <p>
  * The drawback is that those jars will not be available in the OSGi
  * classloader.
- * </p>
  * <p>
  * Alternatives to placing jars in lib/ext:
  * <ol>
@@ -56,7 +52,6 @@
  * <li>Use equinox Buddy-Policy: register a buddy of the jetty bootstrapper
  * bundle. (Note: it will work only on equinox)</li>
  * </ol>
- * </p>
  */
 public class LibExtClassLoaderHelper
 {
@@ -78,11 +73,12 @@
     
     /* ------------------------------------------------------------ */
     /**
-     * @param server
+     * @param jettyHome the jetty home 
+     * @param parentClassLoader the parent classloader
      * @return a url classloader with the jars of resources, lib/ext and the
      *         jars passed in the other argument. The parent classloader usually
      *         is the JettyBootStrapper (an osgi classloader.
-     * @throws MalformedURLException
+     * @throws MalformedURLException if the jetty home reference is invalid
      */
     public static ClassLoader createLibEtcClassLoader(File jettyHome, ClassLoader parentClassLoader) throws MalformedURLException
     {
@@ -134,12 +130,14 @@
     
     /* ------------------------------------------------------------ */
     /**
-     * @param server
+     * @param jarsContainerOrJars the jars via file references
+     * @param otherJarsOrFolder more jars via url references
+     * @param parentClassLoader the parent classloader
      * @return a url classloader with the jars of resources, lib/ext and the
      *         jars passed in the other argument. The parent classloader usually
      *         is the JettyBootStrapper (an osgi classloader). If there was no
      *         extra jars to insert, then just return the parentClassLoader.
-     * @throws MalformedURLException
+     * @throws MalformedURLException if there is a bad jar file reference
      */
     public static ClassLoader createLibExtClassLoader(List<File> jarsContainerOrJars, List<URL> otherJarsOrFolder, ClassLoader parentClassLoader) 
     throws MalformedURLException
@@ -183,13 +181,14 @@
      * depending too much directly on a particular logging framework.
      * <p>
      * We can afford to do some implementation specific code for a logging
-     * framework only in a fragment. <br/>
+     * framework only in a fragment.
+     * <p>
      * Trying to configure log4j and logback in here.
-     * </p>
      * <p>
      * We recommend that slf4j jars are all placed in the osgi framework. And a
      * single implementation if possible packaged as an osgi bundle is there.
-     * </p>
+     * @param jettyHome the jetty home reference
+     * @param childrenFiles the map of child files
      */
     protected static void processFilesInResourcesFolder(File jettyHome, Map<String, File> childrenFiles)
     {
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java
index 3f83f1e..7776c1c 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java
@@ -85,7 +85,7 @@
      * @param parent The parent classloader.
      * @param context The WebAppContext
      * @param contributor The bundle that defines this web-application.
-     * @throws IOException
+     * @throws IOException if unable to cerate the OSGiWebappClassLoader
      */
     public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor)
     throws IOException
@@ -272,6 +272,7 @@
      * WebappContext So we place a fake one there to start with. We replace it
      * with the actual webapp context with this method. We also apply the
      * extraclasspath there at the same time.
+     * @param webappContext the web app context
      */
     public void setWebappContext(WebAppContext webappContext)
     {
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/ServiceWatcher.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/ServiceWatcher.java
index 431f254..9f5c3fb 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/ServiceWatcher.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/ServiceWatcher.java
@@ -54,16 +54,11 @@
     
     public static final String FILTER = "(objectclass=" + ServiceProvider.class.getName() + ")";
 
-    
     //track all instances of deployers of webapps as bundles       
     ServiceTracker _serviceTracker;
     
-    
      
     /* ------------------------------------------------------------ */
-    /**
-     * @param registry
-     */
     public ServiceWatcher() throws Exception
     {
         //track all instances of deployers of webapps
@@ -75,10 +70,6 @@
 
    
     /* ------------------------------------------------------------ */
-    /**
-     * @param managedServerName
-     * @return
-     */
     public Map<ServiceReference, ServiceProvider> getDeployers(String managedServerName)
     {
         if (managedServerName == null)
@@ -154,9 +145,10 @@
     
     /* ------------------------------------------------------------ */
     /** Deploy ContextHandler that is a Service.
-     * 
-     * @param reference
-     * @return
+     * @param context the bundle context 
+     * @param contextHandler  the context handler
+     * @param reference the service reference
+     * @return the object added
      */
     public Object addService (BundleContext context, ContextHandler contextHandler, ServiceReference reference)
     {
@@ -200,8 +192,9 @@
     /* ------------------------------------------------------------ */
     /**
      * Undeploy a ContextHandler that is a Service.
-     * 
-     * @param reference
+     * @param context the bundle context 
+     * @param contextHandler the context handler
+     * @param reference the service reference
      */
     public void removeService (BundleContext context, ContextHandler contextHandler, ServiceReference reference)
     {
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelper.java
index 4c28990..380afc1 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelper.java
@@ -22,10 +22,8 @@
 import org.osgi.framework.Bundle;
 
 /**
- * 
  * BundleClassLoaderHelper
- * 
- * 
+ * <p> 
  * Is there a clean OSGi way to go from the Bundle object to the classloader of
  * the Bundle ? You can certainly take a class inside the bundle and get the
  * bundle's classloader that way. Getting the classloader directly from the
@@ -34,11 +32,9 @@
  * We could use fragments that are specific to each OSGi implementation. Using
  * introspection here to keep packaging simple and avoid the multiplication of
  * the jars.
- * </p>
  * <p>
  * The default implementation relies on introspection and supports equinox-3.5
  * and felix-2.0.0
- * </p>
  */
 public interface BundleClassLoaderHelper
 {
@@ -50,6 +46,7 @@
     public static BundleClassLoaderHelper DEFAULT = new DefaultBundleClassLoaderHelper();
 
     /**
+     * @param bundle the bundle
      * @return The classloader of a given bundle. Assuming the bundle is
      *         started.
      */
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java
index 253e623..82146b1 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java
@@ -28,12 +28,9 @@
 
 /**
  * BundleFileLocatorHelper
- * 
- * 
+ * <p> 
  * From a bundle to its location on the filesystem. Assumes the bundle is not a
  * jar.
- * 
- * @author hmalphettes
  */
 public interface BundleFileLocatorHelper
 {
@@ -50,21 +47,20 @@
      * other situations.
      * <p>
      * Currently only works with bundles that are not jar.
-     * </p>
      * 
      * @param bundle The bundle
      * @return Its installation location as a file.
-     * @throws Exception
+     * @throws Exception if unable to get the install location
      */
     public File getBundleInstallLocation(Bundle bundle) throws Exception;
 
     /**
      * Locate a file inside a bundle.
      * 
-     * @param bundle
-     * @param path
-     * @return file object
-     * @throws Exception
+     * @param bundle the bundle
+     * @param path the path
+     * @return file the file object
+     * @throws Exception if unable to get the file
      */
     public File getFileInBundle(Bundle bundle, String path) throws Exception;
 
@@ -76,11 +72,11 @@
      * files inside jars alone. In fact we only support the second situation for
      * development purpose where the bundle was imported in pde and the classes
      * kept in a jar.
-     * </p>
      * 
-     * @param bundle
+     * @param bundle the bundle
      * @return The jar(s) file that is either the bundle itself, either the jars
      *         embedded inside it.
+     * @throws Exception if unable to locate the jars
      */
     public File[] locateJarsInsideBundle(Bundle bundle) throws Exception;
 
@@ -88,37 +84,37 @@
      * Helper method equivalent to Bundle#getEntry(String entryPath) except that
      * it searches for entries in the fragments by using the findEntries method.
      * 
-     * @param bundle
-     * @param entryPath
+     * @param bundle the bundle
+     * @param entryPath the entry path
      * @return null or all the entries found for that path.
      */
     public Enumeration<URL> findEntries(Bundle bundle, String entryPath);
     
     /**
-     * Only useful for equinox: on felix we get the file:// or jar:// url
+     * Only useful for equinox: on felix we get the <code>file://</code> or <code>jar://</code> url
      * already. Other OSGi implementations have not been tested
      * <p>
-     * Get a URL to the bundle entry that uses a common protocol (i.e. file:
-     * jar: or http: etc.).
-     * </p>
+     * Get a URL to the bundle entry that uses a common protocol (i.e. <code>file:</code>
+     * <code>jar:</code> or <code>http:</code> etc.).
      * 
+     * @param url the url 
      * @return a URL to the bundle entry that uses a common protocol
+     * @throws Exception if unable to get the local url
      */
     public URL getLocalURL(URL url) throws Exception;
     
     /**
-     * Only useful for equinox: on felix we get the file:// url already. Other
+     * Only useful for equinox: on felix we get the <code>file://</code> url already. Other
      * OSGi implementations have not been tested
      * <p>
-     * Get a URL to the content of the bundle entry that uses the file:
+     * Get a URL to the content of the bundle entry that uses the <code>file:</code>
      * protocol. The content of the bundle entry may be downloaded or extracted
      * to the local file system in order to create a file: URL.
      * 
+     * @param url the url 
      * @return a URL to the content of the bundle entry that uses the file:
      *         protocol
-     *         </p>
-     * @throws IOException 
-     * @throws Exception 
+     * @throws Exception if unable to get the file url
      */
     public URL getFileURL(URL url) throws Exception;
 
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/EventSender.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/EventSender.java
index 945cc45..d545fdd 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/EventSender.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/EventSender.java
@@ -28,10 +28,7 @@
 import org.osgi.service.event.EventAdmin;
 
 /**
- * EventSender
- *
  * Utility class for emiting OSGi EventAdmin events
- * 
  */
 public class EventSender
 {    
@@ -42,18 +39,10 @@
     public static final String UNDEPLOYED_EVENT = "org/osgi/service/web/UNDEPLOYED"; 
     public static final String FAILED_EVENT = "org/osgi/service/web/FAILED"; 
     
-    
     private static final EventSender __instance = new EventSender();
     private Bundle _myBundle;
     private EventAdmin _eventAdmin;
     
-    
-    
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * 
-     */
     private EventSender ()
     {
         _myBundle = FrameworkUtil.getBundle(EventSender.class);
@@ -62,26 +51,11 @@
             _eventAdmin = (EventAdmin)_myBundle.getBundleContext().getService(ref);
     }
     
-    
-    
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @return
-     */
     public static EventSender getInstance()
     {
         return __instance;
     }
 
-    
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * @param topic
-     * @param wab
-     * @param contextPath
-     */
     public  void send (String topic, Bundle wab, String contextPath)
     {
         if (topic==null || wab==null || contextPath==null)
@@ -90,15 +64,6 @@
         send(topic, wab, contextPath, null);
     }
     
-    
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * @param topic
-     * @param wab
-     * @param contextPath
-     * @param ex
-     */
     public  void send (String topic, Bundle wab, String contextPath, Exception ex)
     {        
         if (_eventAdmin == null)
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/FakeURLClassLoader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/FakeURLClassLoader.java
index d2c20b7..f6b3bfd 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/FakeURLClassLoader.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/FakeURLClassLoader.java
@@ -22,29 +22,21 @@
 import java.net.URLClassLoader;
 
 /**
- * 
  * FakeURLClassLoader
- * 
+ * <p>
  * A URLClassloader that overrides the getURLs() method to return the list
  * of urls passed in to the constructor, but otherwise acts as if it has no
  * urls, which would cause it to delegate to the parent classloader (in this
  * case an OSGi classloader).
- * 
+ * <p>
  * The main use of this class is with jars containing tlds. Jasper expects a
  * URL classloader to inspect for jars with tlds.
- * 
  */
 public class FakeURLClassLoader extends URLClassLoader
 {
-
     private URL[] _jars;
     
-    
     /* ------------------------------------------------------------ */
-    /**
-     * @param osgiClassLoader
-     * @param jars
-     */
     public FakeURLClassLoader(ClassLoader osgiClassLoader, URL[] jars)
     {
         super(new URL[] {},osgiClassLoader);
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/Util.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/Util.java
index da7c7c1..fae2f58 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/Util.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/Util.java
@@ -27,23 +27,43 @@
 import java.util.StringTokenizer;
 
 /**
- * Util
- *
- * Various useful functions used widely.
+ * Various useful functions utility methods for OSGi wide use.
  */
 public class Util
 {
     public static final String DEFAULT_DELIMS = ",;";
 
+    
+    /**
+     * Get the value of a manifest header.
+     * 
+     * @param name the name of the header
+     * @param altName an alternative name for the header (useful for deprecated names)
+     * @param manifest the dictionary
+     * @return the value from the manifest
+     */
+    public static String getManifestHeaderValue (String name, String altName, Dictionary manifest)
+    {
+        if (manifest == null)
+            return null;
+        if (name == null && altName == null)
+            return null;
+        if (name != null)
+            return (String)manifest.get(name);
+        return (String)manifest.get(altName);
+    }
+    
+  
+    
     /* ------------------------------------------------------------ */
     /**
      * Treating the string as a separated list of filenames,
      * convert and return the list of urls.
      * 
-     * @param val the separated list
-     * @param delims the separators (default is ,;)
-     * @return
-     * @throws MalformedURLException 
+     * @param val the separated list of filenames
+     * @param delims the separators (default is <code>,;</code>)
+     * @return the list of URLs found in the input list
+     * @throws Exception if unable to convert entry to a URL
      */
     public static List<URL> fileNamesAsURLs(String val, String delims) 
     throws Exception
@@ -74,13 +94,13 @@
     
     /* ------------------------------------------------------------ */
     /**
-     * recursively substitute the ${sysprop} by their actual system property.
-     * ${sysprop,defaultvalue} will use 'defaultvalue' as the value if no
+     * recursively substitute the <code>${sysprop}</code> by their actual system property.
+     * <code>${sysprop,defaultvalue}</code> will use <code>'defaultvalue'</code> as the value if no
      * sysprop is defined. Not the most efficient code but we are shooting for
      * simplicity and speed of development here.
      * 
-     * @param value
-     * @return
+     * @param value the input string
+     * @return the string with replaced properties
      */
     public static String resolvePropertyValue(String value)
     {
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
index f757b5c..dcaca01 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
@@ -29,15 +29,14 @@
 
 /**
  * DefaultBundleClassLoaderHelper
- * 
- * 
+ * <p>
  * Default implementation of the BundleClassLoaderHelper. Uses introspection to
  * support equinox-3.5 and felix-2.0.0
  */
 public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
 {
     private static final Logger LOG = Log.getLogger(BundleClassLoaderHelper.class);
-    private static enum OSGiContainerType {EquinoxOld, EquinoxLuna, FelixOld, Felix403};
+    private static enum OSGiContainerType {EquinoxOld, EquinoxLuna, FelixOld, Felix403, Concierge};
     private static OSGiContainerType osgiContainer;
     private static Class Equinox_BundleHost_Class;
     private static Class Equinox_EquinoxBundle_Class;
@@ -56,6 +55,12 @@
     private static Field Felix_ModuleImpl_m_ClassLoader_Field;
     private static Method Felix_BundleWiring_getClassLoader_Method;
     
+    // Concierge
+    private static Class Concierge_BundleImpl_Class;
+    private static Class Concierge_BundleWiring_Class;
+    private static Method Concierge_BundleImpl_Adapt_Method;
+    private static Method Concierge_BundleWiring_getClassLoader_Method;
+    
     
     private static void checkContainerType (Bundle bundle)
     {
@@ -102,10 +107,22 @@
         }
         catch (ClassNotFoundException e)
         {
-            LOG.warn("Unknown OSGi container type");
-            return;
+            LOG.ignore(e);
         }
         
+        try
+        {
+            Concierge_BundleImpl_Class = bundle.getClass().getClassLoader().loadClass("org.eclipse.concierge.BundleImpl");
+            osgiContainer = OSGiContainerType.Concierge;
+            return;
+        }
+        catch (ClassNotFoundException e)
+        {
+            LOG.ignore(e);
+        }
+
+        LOG.warn("Unknown OSGi container type");
+        return;
     }
 
   
@@ -115,7 +132,7 @@
     /**
      * Assuming the bundle is started.
      * 
-     * @param bundle
+     * @param bundle the bundle
      * @return classloader object
      */
     public ClassLoader getBundleClassLoader(Bundle bundle)
@@ -168,6 +185,12 @@
             {
                 return internalGetFelixBundleClassLoader(bundle); 
             }
+
+            case Concierge:
+            {
+                return internalGetConciergeBundleClassLoader(bundle); 
+            }
+
             default:
             {
                 LOG.warn("No classloader found for bundle "+bundle.getSymbolicName());
@@ -363,4 +386,55 @@
         LOG.warn("No classloader for felix platform for bundle "+bundle.getSymbolicName());
         return null;
     }
+    
+    /**
+     * @param bundle
+     * @return
+     */
+    private static ClassLoader internalGetConciergeBundleClassLoader(Bundle bundle)
+    {
+        if (osgiContainer == OSGiContainerType.Concierge)
+        {
+            try
+            {
+                /** 
+                 * In Concierge:
+                 * 
+                 * Option A:
+                 * <pre>
+                 * Concierge concierge = new Concierge(...);
+                 * BundleWiring bundleWiring = concierge.getWiring(); // method is public
+                 * </pre>
+                 * Problem: getWiring not yet implementd
+                 * 
+                 * Option B:
+                 * <pre>
+                 * Concierge concierge = new Concierge(...);
+                 * BundleWiring bundleWiring = concierge.adapt(org.osgi.framework.wiring.BundleWiring);
+                 * </pre>
+                 * Same approach as done in Felix.
+                 * 
+                 */
+                if (Concierge_BundleWiring_Class == null) {
+                    Concierge_BundleWiring_Class = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring");
+                    Concierge_BundleImpl_Adapt_Method = Concierge_BundleImpl_Class.getMethod("adapt", new Class[] {Class.class});
+                    Concierge_BundleImpl_Adapt_Method.setAccessible(true);
+                    Concierge_BundleWiring_getClassLoader_Method = Concierge_BundleWiring_Class.getMethod("getClassLoader");
+                    Concierge_BundleWiring_getClassLoader_Method.setAccessible(true);
+                }
+
+                Object wiring = Concierge_BundleImpl_Adapt_Method.invoke(bundle, new Object[] {Concierge_BundleWiring_Class});
+                ClassLoader cl = (ClassLoader)Concierge_BundleWiring_getClassLoader_Method.invoke(wiring);
+                return cl;
+            }
+            catch (Exception e)
+            {
+                LOG.warn(e);
+                return null;
+            }
+        }
+
+        LOG.warn("No classloader for Concierge platform for bundle "+bundle.getSymbolicName());
+        return null;
+    }
 }
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
index 92fa6f4..a2ad48b 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
@@ -32,18 +32,15 @@
 
 import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
 import org.eclipse.jetty.util.URIUtil;
-import org.eclipse.jetty.util.resource.FileResource;
+import org.eclipse.jetty.util.resource.PathResource;
 import org.eclipse.jetty.util.resource.Resource;
 import org.osgi.framework.Bundle;
 
 /**
  * DefaultFileLocatorHelper
- * 
- * 
+ * <p> 
  * From a bundle to its location on the filesystem. Assumes the bundle is not a
  * jar.
- * 
- * @author hmalphettes
  */
 public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
 {
@@ -89,7 +86,7 @@
      * 
      * @param bundle The bundle
      * @return Its installation location as a file.
-     * @throws Exception
+     * @throws Exception if unable to get the bundle install location
      */
     public File getBundleInstallLocation(Bundle bundle) throws Exception
     {
@@ -100,10 +97,10 @@
 
         if (url.getProtocol().equals("file"))
         {
-            // some osgi frameworks do use the file protocole directly in some
+            // some osgi frameworks do use the file protocol directly in some
             // situations. Do use the FileResource to transform the URL into a
             // File: URL#toURI is broken
-            return new FileResource(url).getFile().getParentFile().getParentFile();
+            return new PathResource(url).getFile().getParentFile().getParentFile();
         }
         else if (url.getProtocol().equals("bundleentry"))
         {
@@ -212,10 +209,10 @@
     /**
      * Locate a file inside a bundle.
      * 
-     * @param bundle
-     * @param path
+     * @param bundle the bundle
+     * @param path the path
      * @return file object
-     * @throws Exception
+     * @throws Exception if unable to get the file in the bundle
      */
     public File getFileInBundle(Bundle bundle, String path) throws Exception
     {
@@ -239,8 +236,8 @@
      * it searches for entries in the fragments by using the Bundle#findEntries
      * method.
      * 
-     * @param bundle
-     * @param entryPath
+     * @param bundle the bundle
+     * @param entryPath the entry path
      * @return null or all the entries found for that path.
      */
     public Enumeration<URL> findEntries(Bundle bundle, String entryPath)
@@ -266,7 +263,7 @@
      * kept in a jar.
      * </p>
      * 
-     * @param bundle
+     * @param bundle the bundle
      * @return The jar(s) file that is either the bundle itself, either the jars
      *         embedded inside it.
      */
@@ -349,7 +346,7 @@
      * @return a URL to the content of the bundle entry that uses the file:
      *         protocol
      *         </p>
-     * @throws IOException 
+     * @throws Exception if unable to get the file url 
      */
     public URL getFileURL(URL url) throws Exception
  
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java
index 28665af..289b5da 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java
@@ -36,11 +36,10 @@
 
 /**
  * PackageAdminServiceTracker
- * 
- * 
+ * <p>
  * When the PackageAdmin service is activated we can look for the fragments
  * attached to this bundle and do a fake "activate" on them.
- * 
+ * <p> 
  * See particularly the jetty-osgi-boot-jsp fragment bundle that uses this
  * facility.
  */
@@ -124,8 +123,8 @@
      * bundle. when we drop the support for the older versions of OSGi, we will
      * stop using the PackageAdmin service.
      * 
-     * @param bundle
-     * @return
+     * @param bundle the bundle
+     * @return the bundle fragment list
      */
     public Bundle[] getFragments(Bundle bundle)
     {
@@ -143,9 +142,8 @@
      * collect the required-bundles and fragment when the directive
      * visibility:=reexport is added to a required-bundle.
      * 
-     * @param bundle
-     * @param webFragOrAnnotationOrResources
-     * @return
+     * @param bundle the bundle
+     * @return the bundle fragment and required list
      */
     public Bundle[] getFragmentsAndRequiredBundles(Bundle bundle)
     {
@@ -165,9 +163,13 @@
      * transitively when the directive 'visibility:=reexport' is added to a
      * required-bundle.
      * 
-     * @param bundle
-     * @param webFragOrAnnotationOrResources
-     * @return
+     * @param bundle the bundle
+     * @param admin the admin package
+     * @param deps The map of fragment and required bundles associated to the value of the
+     *         jetty-web attribute.
+     * @param onlyReexport true to collect resources and web-fragments
+     *            transitively if and only if the directive visibility is
+     *            reexport.
      */
     protected void collectFragmentsAndRequiredBundles(Bundle bundle, PackageAdmin admin, Map<String, Bundle> deps, boolean onlyReexport)
     {
@@ -193,12 +195,13 @@
      * A simplistic but good enough parser for the Require-Bundle header. Parses
      * the version range attribute and the visibility directive.
      * 
+     * @param bundle the bundle
+     * @param admin the admin package 
+     * @param deps The map of required bundles associated to the value of the
+     *         jetty-web attribute.
      * @param onlyReexport true to collect resources and web-fragments
      *            transitively if and only if the directive visibility is
      *            reexport.
-     * @param bundle
-     * @return The map of required bundles associated to the value of the
-     *         jetty-web attribute.
      */
     protected void collectRequiredBundles(Bundle bundle, PackageAdmin admin, Map<String, Bundle> deps, boolean onlyReexport)
     {
diff --git a/jetty-osgi/jetty-osgi-httpservice/contexts/httpservice.xml b/jetty-osgi/jetty-osgi-httpservice/contexts/httpservice.xml
index 28b06ea..98c0031 100644
--- a/jetty-osgi/jetty-osgi-httpservice/contexts/httpservice.xml
+++ b/jetty-osgi/jetty-osgi-httpservice/contexts/httpservice.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 <Configure class="org.eclipse.jetty.servlet.ServletContextHandler">
   <!-- this configures the servlet session manager -->
   <Set name="SessionHandler">
diff --git a/jetty-osgi/jetty-osgi-httpservice/pom.xml b/jetty-osgi/jetty-osgi-httpservice/pom.xml
index 631303c..3251f95 100644
--- a/jetty-osgi/jetty-osgi-httpservice/pom.xml
+++ b/jetty-osgi/jetty-osgi-httpservice/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-httpservice</artifactId>
@@ -61,12 +61,6 @@
         <artifactId>maven-jar-plugin</artifactId>
         <executions>
           <execution>
-            <id>artifact-jar</id>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-          <execution>
             <id>test-jar</id>
             <goals>
               <goal>test-jar</goal>
@@ -83,15 +77,6 @@
           <groupId>org.apache.felix</groupId>
           <artifactId>maven-bundle-plugin</artifactId>
           <extensions>true</extensions>
-          <executions>
-              <execution>
-                  <id>bundle-manifest</id>
-                  <phase>process-classes</phase>
-                  <goals>
-                      <goal>manifest</goal>
-                  </goals>
-              </execution>
-          </executions>
           <configuration>
               <instructions>
                 <Bundle-SymbolicName>org.eclipse.jetty.osgi.httpservice</Bundle-SymbolicName>
diff --git a/jetty-osgi/jetty-osgi-npn/pom.xml b/jetty-osgi/jetty-osgi-npn/pom.xml
deleted file mode 100644
index 092278f..0000000
--- a/jetty-osgi/jetty-osgi-npn/pom.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <parent>
-    <groupId>org.eclipse.jetty.osgi</groupId>
-    <artifactId>jetty-osgi-project</artifactId>
-    <version>9.2.11-SNAPSHOT</version>
-  </parent>
-  <modelVersion>4.0.0</modelVersion>
-  <artifactId>jetty-osgi-npn</artifactId>
-  <name>Jetty :: OSGi NPN Fragment</name>
-  <packaging>jar</packaging>
-  <properties>
-    <bundle-symbolic-name>org.eclipse.jetty.osgi.npn.fragment</bundle-symbolic-name>
-  </properties>
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>build-helper-maven-plugin</artifactId>
-        <version>1.7</version>
-        <executions>
-          <execution>
-            <id>parse-version</id>
-            <goals>
-              <goal>parse-version</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestEntries>
-              <Bundle-ManifestVersion>2</Bundle-ManifestVersion>
-              <Bundle-SymbolicName>${bundle-symbolic-name};singleton:=true</Bundle-SymbolicName>
-              <Bundle-Name>Jetty OSGi NPN Fragment</Bundle-Name>
-              <Bundle-Version>${parsedVersion.osgiVersion}</Bundle-Version>
-              <Export-Package>org.eclipse.jetty.npn</Export-Package>
-              <Fragment-Host>system.bundle;extension:=framework</Fragment-Host>
-            </manifestEntries>
-          </archive>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-</project>
diff --git a/jetty-osgi/pom.xml b/jetty-osgi/pom.xml
index 10f69c5..2b326f2 100644
--- a/jetty-osgi/pom.xml
+++ b/jetty-osgi/pom.xml
@@ -1,15 +1,19 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
+
+  <modelVersion>4.0.0</modelVersion>
   <groupId>org.eclipse.jetty.osgi</groupId>
   <artifactId>jetty-osgi-project</artifactId>
   <name>Jetty :: OSGi</name>
   <url>http://www.eclipse.org/jetty</url>
   <packaging>pom</packaging>
+
   <properties>
     <osgi-version>3.6.0.v20100517</osgi-version>
     <osgi-services-version>3.2.100.v20100503</osgi-services-version>
@@ -18,6 +22,7 @@
     <logback-version>0.9.29</logback-version>
     <slf4j-version>1.6.1</slf4j-version>
   </properties>
+
   <modules>
     <module>jetty-osgi-boot</module>
     <module>jetty-osgi-boot-jsp</module>
@@ -25,22 +30,22 @@
     <module>jetty-osgi-httpservice</module>
     <module>test-jetty-osgi-webapp</module>
     <module>test-jetty-osgi-context</module>
-    <module>test-jetty-osgi</module>
+    <module>test-jetty-osgi-fragment</module>
     <module>jetty-osgi-alpn</module>
   </modules>
+
   <profiles>
     <profile>
-      <id>npn</id>
+      <id>jdk8</id>
       <activation>
-        <jdk>1.7</jdk>
+        <jdk>[1.8,1.9)</jdk>
       </activation>
       <modules>
-<!--
-        <module>jetty-osgi-npn</module>
--->
+        <module>test-jetty-osgi</module>
       </modules>
     </profile>
   </profiles>
+
   <build>
     <resources>
       <resource>
@@ -91,6 +96,7 @@
       </plugin>
     </plugins>
   </build>
+
   <dependencyManagement>
     <dependencies>
       <dependency>
@@ -192,4 +198,5 @@
       </dependency>
     </dependencies>
   </dependencyManagement>
+
 </project>
diff --git a/jetty-osgi/test-jetty-osgi-context/pom.xml b/jetty-osgi/test-jetty-osgi-context/pom.xml
index 9578cf7..518b55d 100644
--- a/jetty-osgi/test-jetty-osgi-context/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-context/pom.xml
@@ -2,11 +2,11 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>test-jetty-osgi-context</artifactId>
-  <name>Jetty :: OSGi :: Context</name>
+  <name>Jetty :: OSGi :: Test Context</name>
   <description>Test Jetty OSGi bundle with a ContextHandler</description>
   <url>http://www.eclipse.org/jetty</url>
   <properties>
@@ -19,18 +19,18 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
-    	<groupId>org.eclipse.osgi</groupId>
-    	<artifactId>org.eclipse.osgi</artifactId>
-        <scope>provided</scope>
+      <groupId>org.eclipse.osgi</groupId>
+      <artifactId>org.eclipse.osgi</artifactId>
+      <scope>provided</scope>
     </dependency>
     <dependency>
-        <groupId>org.eclipse.osgi</groupId>
-        <artifactId>org.eclipse.osgi.services</artifactId>
-        <scope>provided</scope>
+      <groupId>org.eclipse.osgi</groupId>
+      <artifactId>org.eclipse.osgi.services</artifactId>
+      <scope>provided</scope>
     </dependency>
     <dependency>
-        <groupId>org.eclipse.jetty.toolchain</groupId>
-        <artifactId>jetty-schemas</artifactId>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-schemas</artifactId>
     </dependency>
   </dependencies>
 
@@ -58,12 +58,6 @@
                 <artifactId>maven-jar-plugin</artifactId> 
                 <executions>
                     <execution>
-                        <id>artifact-jar</id> 
-                        <goals>
-                            <goal>jar</goal> 
-                        </goals>
-                    </execution>
-                    <execution>
                         <id>test-jar</id> 
                         <goals>
                             <goal>test-jar</goal>
@@ -80,15 +74,6 @@
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
                 <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <id>bundle-manifest</id>
-                        <phase>process-classes</phase>
-                        <goals>
-                            <goal>manifest</goal> 
-                        </goals>
-                    </execution>
-                </executions>
                 <configuration>
                     <instructions>
                         <Bundle-SymbolicName>org.eclipse.jetty.osgi.testcontext;singleton:=true</Bundle-SymbolicName>
diff --git a/jetty-osgi/test-jetty-osgi-context/src/main/context/acme.xml b/jetty-osgi/test-jetty-osgi-context/src/main/context/acme.xml
index 1ff97a4..f6c5cf5 100644
--- a/jetty-osgi/test-jetty-osgi-context/src/main/context/acme.xml
+++ b/jetty-osgi/test-jetty-osgi-context/src/main/context/acme.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure class="org.eclipse.jetty.server.handler.ContextHandler">
 
@@ -8,7 +8,7 @@
     <Arg><Property name="bundle.root"/></Arg>
   </Call>
 
-  <Ref id="res">
+  <Ref refid="res">
     <Call id="base" name="addPath">
       <Arg>/static/</Arg>
     </Call>
@@ -18,7 +18,7 @@
 
   <!-- Set up the base resource for static files relative to inside bundle -->
   <Set name="baseResource">
-     <Ref id="base"/>
+     <Ref refid="base"/>
   </Set>
 
   <Set name="handler">
diff --git a/jetty-osgi/test-jetty-osgi-context/src/main/java/com/acme/osgi/Activator.java b/jetty-osgi/test-jetty-osgi-context/src/main/java/com/acme/osgi/Activator.java
index 4411db4..554adf2 100644
--- a/jetty-osgi/test-jetty-osgi-context/src/main/java/com/acme/osgi/Activator.java
+++ b/jetty-osgi/test-jetty-osgi-context/src/main/java/com/acme/osgi/Activator.java
@@ -24,15 +24,9 @@
 import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
 
-import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.ContextHandler;
-import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.util.tracker.BundleTracker;
 
 /**
  * Bootstrap a ContextHandler
diff --git a/jetty-osgi/test-jetty-osgi-fragment/pom.xml b/jetty-osgi/test-jetty-osgi-fragment/pom.xml
new file mode 100644
index 0000000..6eaab43
--- /dev/null
+++ b/jetty-osgi/test-jetty-osgi-fragment/pom.xml
@@ -0,0 +1,83 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.osgi</groupId>
+    <artifactId>jetty-osgi-project</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>test-jetty-osgi-fragment</artifactId>
+  <name>Jetty :: OSGi :: WebApp Fragment</name>
+  <description>Test Jetty OSGi Webapp Fragment bundle</description>
+  <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.webapp.fragment</bundle-symbolic-name>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-webapp</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.osgi</groupId>
+      <artifactId>org.eclipse.osgi</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.osgi</groupId>
+      <artifactId>org.eclipse.osgi.services</artifactId>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+
+      <build> 
+        <resources>
+          <resource>
+            <directory>src/main/resources</directory>
+          </resource>
+        </resources>
+
+        <plugins>
+           <plugin>
+             <groupId>org.apache.maven.plugins</groupId>
+             <artifactId>maven-deploy-plugin</artifactId>
+             <configuration>
+               <!-- DO NOT DEPLOY (or Release) -->
+               <skip>true</skip>
+             </configuration>
+            </plugin> 
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId> 
+                <artifactId>maven-jar-plugin</artifactId> 
+                <executions>
+                    <execution>
+                        <id>test-jar</id> 
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <archive>
+                        <manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile> 
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${bundle-symbolic-name}</Bundle-SymbolicName>
+                        <Bundle-Name>Jetty OSGi Test WebApp Fragment</Bundle-Name>
+                        <Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
+<Fragment-Host>org.eclipse.jetty.tests.test-spec-webapp</Fragment-Host>
+<Jetty-WarFragmentFolderPath>/</Jetty-WarFragmentFolderPath>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+  
+</project>
diff --git a/jetty-osgi/test-jetty-osgi-fragment/src/main/resources/frag.html b/jetty-osgi/test-jetty-osgi-fragment/src/main/resources/frag.html
new file mode 100644
index 0000000..9dd6187
--- /dev/null
+++ b/jetty-osgi/test-jetty-osgi-fragment/src/main/resources/frag.html
@@ -0,0 +1,5 @@
+<html>
+  <body>
+    <h1>FRAGMENT</h1>
+  </body>
+</html>
diff --git a/jetty-osgi/test-jetty-osgi-webapp/pom.xml b/jetty-osgi/test-jetty-osgi-webapp/pom.xml
index f7a0999..03c3a7f 100644
--- a/jetty-osgi/test-jetty-osgi-webapp/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-webapp/pom.xml
@@ -2,12 +2,12 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>test-jetty-osgi-webapp</artifactId>
-  <name>Jetty :: OSGi :: WebApp</name>
+  <name>Jetty :: OSGi :: Test WebApp</name>
   <description>Test Jetty OSGi Webapp bundle</description>
   <url>http://www.eclipse.org/jetty</url>
   <properties>
@@ -19,18 +19,18 @@
       <artifactId>jetty-webapp</artifactId>
     </dependency>
     <dependency>
-    	<groupId>org.eclipse.osgi</groupId>
-    	<artifactId>org.eclipse.osgi</artifactId>
-        <scope>provided</scope>
+      <groupId>org.eclipse.osgi</groupId>
+      <artifactId>org.eclipse.osgi</artifactId>
+      <scope>provided</scope>
     </dependency>
     <dependency>
-        <groupId>org.eclipse.osgi</groupId>
-        <artifactId>org.eclipse.osgi.services</artifactId>
-        <scope>provided</scope>
+      <groupId>org.eclipse.osgi</groupId>
+      <artifactId>org.eclipse.osgi.services</artifactId>
+      <scope>provided</scope>
     </dependency>
   </dependencies>
 
-      <build> 
+      <build>
         <resources>
           <resource>
             <directory>src/main/resources</directory>
@@ -45,19 +45,13 @@
                <!-- DO NOT DEPLOY (or Release) -->
                <skip>true</skip>
              </configuration>
-            </plugin> 
+            </plugin>
             <plugin>
-                <groupId>org.apache.maven.plugins</groupId> 
-                <artifactId>maven-jar-plugin</artifactId> 
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
                 <executions>
                     <execution>
-                        <id>artifact-jar</id> 
-                        <goals>
-                            <goal>jar</goal> 
-                        </goals>
-                    </execution>
-                    <execution>
-                        <id>test-jar</id> 
+                        <id>test-jar</id>
                         <goals>
                             <goal>test-jar</goal>
                         </goals>
@@ -65,7 +59,7 @@
                 </executions>
                 <configuration>
                     <archive>
-                        <manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile> 
+                        <manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
                     </archive>
                 </configuration>
             </plugin>
@@ -73,15 +67,6 @@
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
                 <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <id>bundle-manifest</id>
-                        <phase>process-classes</phase>
-                        <goals>
-                            <goal>manifest</goal> 
-                        </goals>
-                    </execution>
-                </executions>
                 <configuration>
                     <instructions>
                         <Bundle-SymbolicName>org.eclipse.jetty.osgi.testapp;singleton:=true</Bundle-SymbolicName>
@@ -90,8 +75,7 @@
                         <Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
                         <!-- disable the uses directive: jetty will accomodate pretty much any versions
                         of the packages it uses; no need to reflect some tight dependency determined at
-                        compilation time. --> 
-                        <_nouses>true</_nouses>
+                        compilation time. -->
                         <Import-Package>
  org.osgi.framework,
  org.osgi.service.cm;version="1.2.0",
@@ -106,7 +90,8 @@
  org.xml.sax.helpers,
  *
                         </Import-Package>
-                        <DynamicImport-Package>org.eclipse.jetty.*;version="[9.1,10.0)"</DynamicImport-Package>
+                        <Export-Package>com.acme.osgi</Export-Package>
+                        <DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/jetty-osgi/test-jetty-osgi-webapp/src/main/java/com/acme/osgi/Activator.java b/jetty-osgi/test-jetty-osgi-webapp/src/main/java/com/acme/osgi/Activator.java
index c670bea..6a61e54 100644
--- a/jetty-osgi/test-jetty-osgi-webapp/src/main/java/com/acme/osgi/Activator.java
+++ b/jetty-osgi/test-jetty-osgi-webapp/src/main/java/com/acme/osgi/Activator.java
@@ -18,19 +18,21 @@
 
 package com.acme.osgi;
 
+import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.Dictionary;
 import java.util.Hashtable;
 
-import org.eclipse.jetty.server.Server;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
 import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.webapp.WebAppContext;
-import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.util.tracker.BundleTracker;
 
 /**
  * Bootstrap a webapp
@@ -40,6 +42,28 @@
 public class Activator implements BundleActivator
 {
 
+
+    public static class TestServlet extends HttpServlet
+    {
+
+        /** 
+         * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+         */
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            //report the mimetype of a file
+            String mimetype = req.getServletContext().getMimeType("file.gz");
+            resp.setContentType("text/html");
+            PrintWriter writer = resp.getWriter();
+            writer.write("<html><body><p>MIMETYPE="+mimetype+"</p></body</html>");
+            writer.flush();
+        }
+        
+    }
+
+    
+    
     /**
      * 
      * @param context
@@ -47,25 +71,10 @@
     public void start(BundleContext context) throws Exception
     {
         String serverName = "defaultJettyServer";
-        
-        /* Uncomment to create a different server instance to deploy to. Also change
-         * TestJettyOSGiBootWebAppAsService to use the port 9999
-         
-        Server server = new Server();
-        //do any setup on Server in here
-        serverName = "fooServer";
-        Dictionary serverProps = new Hashtable();
-        //define the unique name of the server instance
-        serverProps.put("managedServerName", serverName);
-        serverProps.put("jetty.port", "9999");
-        //let Jetty apply some configuration files to the Server instance
-        serverProps.put("jetty.etc.config.urls", "file:/opt/jetty/etc/jetty.xml,file:/opt/jetty/etc/jetty-selector.xml,file:/opt/jetty/etc/jetty-deployer.xml");
-        //register as an OSGi Service for Jetty to find 
-        context.registerService(Server.class.getName(), server, serverProps);
-        */
-        
+     
         //Create a webapp context as a Service and target it at the Server created above
         WebAppContext webapp = new WebAppContext();
+        webapp.addServlet(new ServletHolder(new TestServlet()), "/mime");
         Dictionary props = new Hashtable();
         props.put("war",".");
         props.put("contextPath","/acme");
diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml
index 31a0ab1..4751f4a 100644
--- a/jetty-osgi/test-jetty-osgi/pom.xml
+++ b/jetty-osgi/test-jetty-osgi/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
@@ -11,16 +11,15 @@
   <description>Jetty OSGi Integration test</description>
   <url>http://www.eclipse.org/jetty</url>
   <properties>
-    <bundle-symbolic-name>${project.groupId}.boot.test.spdy</bundle-symbolic-name>
+    <bundle-symbolic-name>${project.groupId}.boot.test.osgi</bundle-symbolic-name>
     <jetty-orbit-url>http://download.eclipse.org/jetty/orbit/</jetty-orbit-url>
     <assembly-directory>target/distribution</assembly-directory>
-    <exam.version>3.5.0</exam.version>
-    <url.version>1.5.2</url.version>
+    <exam.version>4.10.0</exam.version>
+    <url.version>2.5.2</url.version>
     <injection.bundle.version>1.0</injection.bundle.version>
   </properties>
   <dependencies>
     <!-- Pax Exam Dependencies -->
-
     <dependency>
       <groupId>org.ops4j.pax.exam</groupId>
       <artifactId>pax-exam</artifactId>
@@ -33,16 +32,13 @@
       <version>${exam.version}</version>
       <scope>test</scope>
     </dependency>
-    
-
-    <!-- use the forked container so we can pass it system properties eg for npn/alpn -->
+    <!-- Use the forked container so we can pass it system properties eg for alpn -->
     <dependency>
         <groupId>org.ops4j.pax.exam</groupId>
         <artifactId>pax-exam-container-forked</artifactId>
         <version>${exam.version}</version>
         <scope>test</scope>
     </dependency>
- 
     <dependency>
       <groupId>org.ops4j.pax.exam</groupId>
       <artifactId>pax-exam-junit4</artifactId>
@@ -68,34 +64,19 @@
       <scope>test</scope>
     </dependency>
 
-    <!-- OSGi R4 frameworks -->
-<!--
     <dependency>
-      <groupId>org.apache.felix</groupId>
-      <artifactId>org.apache.felix.framework</artifactId>
-      <version>4.4.0</version>
+      <groupId>org.eclipse.platform</groupId>
+      <artifactId>org.eclipse.osgi</artifactId>
+      <version>3.11.2</version>
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.osgi</groupId>
-      <artifactId>org.osgi.enterprise</artifactId>
-      <version>5.0.0</version>
-      <scope>test</scope>
-    </dependency>
--->
-    <dependency>
-      <groupId>org.eclipse</groupId>
-      <artifactId>osgi</artifactId>
-      <version>3.10.0-v20140606-1445</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-        <groupId>org.eclipse.osgi</groupId>
+        <groupId>org.eclipse.platform</groupId>
         <artifactId>org.eclipse.osgi.services</artifactId>
+        <version>3.5.100</version>
         <scope>test</scope>
     </dependency>
 
-
     <!-- Jetty OSGi Deps -->
     <dependency>
       <groupId>org.eclipse.jetty.osgi</groupId>
@@ -129,7 +110,6 @@
         </exclusion>
       </exclusions>
     </dependency>
-
     <dependency>
       <groupId>org.eclipse.jetty.osgi</groupId>
       <artifactId>jetty-httpservice</artifactId>
@@ -142,14 +122,12 @@
       <artifactId>jetty-osgi-servlet-api</artifactId>
       <version>3.1.0.M3</version>
     </dependency>
-
     <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-jta_1.1_spec</artifactId>
       <version>1.1.1</version>
       <scope>test</scope>
     </dependency>
-
     <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-atinject_1.0_spec</artifactId>
@@ -162,14 +140,44 @@
       <version>1.0.1</version>
      <scope>test</scope>
     </dependency>
-
     <dependency>
-     <groupId>org.mortbay.jasper</groupId>
-      <artifactId>apache-el</artifactId>
-      <version>8.0.33</version>
-     <scope>test</scope>
+       <groupId>org.glassfish.web</groupId>
+       <artifactId>javax.servlet.jsp.jstl</artifactId>
+       <version>1.2.2</version>
+      <exclusions>
+        <exclusion>
+          <groupId>javax.servlet.jsp.jstl</groupId>
+          <artifactId>jstl-api</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>javax.servlet</groupId>
+          <artifactId>servlet-api</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>javax.servlet.jsp</groupId>
+          <artifactId>jsp-api</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>javax.el</groupId>
+          <artifactId>el-api</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
-
+    <dependency>
+        <groupId>org.eclipse.jetty.orbit</groupId>
+        <artifactId>javax.servlet.jsp.jstl</artifactId>
+        <version>1.2.0.v201105211821</version>
+        <exclusions>
+          <exclusion>
+            <groupId>org.eclipse.jetty.orbit</groupId>
+            <artifactId>javax.servlet</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.eclipse.jetty.orbit</groupId>
+            <artifactId>javax.servlet.jsp</artifactId>
+          </exclusion>
+        </exclusions>
+    </dependency>
 
     <!-- Jetty Deps -->
     <dependency>
@@ -223,6 +231,7 @@
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-util</artifactId>
+      <version>${project.version}</version> 
       <scope>runtime</scope>
     </dependency>
     <dependency>
@@ -279,28 +288,9 @@
       <scope>runtime</scope>
     </dependency>
     <dependency>
-      <groupId>org.eclipse.jetty.spdy</groupId>
-      <artifactId>spdy-core</artifactId>
+      <groupId>org.eclipse.jetty.http2</groupId>
+      <artifactId>http2-server</artifactId>
       <version>${project.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.spdy</groupId>
-      <artifactId>spdy-server</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.spdy</groupId>
-      <artifactId>spdy-http-server</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.spdy</groupId>
-      <artifactId>spdy-client</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.mortbay.jetty.alpn</groupId>
@@ -325,8 +315,6 @@
       <artifactId>jetty-schemas</artifactId>
       <scope>runtime</scope>
     </dependency>
-
-
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-plus</artifactId>
@@ -342,7 +330,6 @@
       <classifier>webbundle</classifier>
       <scope>test</scope>
     </dependency>
-
     <dependency>
       <groupId>org.eclipse.jetty.tests</groupId>
       <artifactId>test-spec-webapp</artifactId>
@@ -350,21 +337,23 @@
       <type>war</type>
       <scope>test</scope>
     </dependency>
-
     <dependency>
       <groupId>org.eclipse.jetty.tests</groupId>
       <artifactId>test-container-initializer</artifactId>
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
-
-
+    <dependency>
+      <groupId>org.eclipse.jetty.osgi</groupId>
+      <artifactId>test-jetty-osgi-fragment</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>org.eclipse.jetty.tests</groupId>
       <artifactId>test-mock-resources</artifactId>
       <version>${project.version}</version>
     </dependency>
-
     <dependency>
       <groupId>org.eclipse.jetty.osgi</groupId>
       <artifactId>test-jetty-osgi-context</artifactId>
@@ -382,14 +371,22 @@
       <artifactId>jetty-test-helper</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>${slf4j-version}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
   <build>
     <plugins>
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
+        <!-- downgrading to version 2.18 here as there's a known JVM crash issue with the 2.19.x series -->
+        <version>2.18.1</version>
         <configuration>
           <!-- No point defining -Xbootclasspath as the actual OSGi VM is run as a forked process by pax-exam -->
-          <!-- But we do pass the sys property of the alpn-boot jar so that it can be configued inside tests -->
+          <!-- But we do pass the sys property of the alpn-boot jar so that it can be configured inside tests -->
           <argLine>-Dmortbay-alpn-boot=${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn.version}/alpn-boot-${alpn.version}.jar</argLine>
         </configuration>
       </plugin>
diff --git a/jetty-osgi/test-jetty-osgi/src/main/resources/jetty-logging.properties b/jetty-osgi/test-jetty-osgi/src/main/resources/jetty-logging.properties
index 5250a08..147cf00 100644
--- a/jetty-osgi/test-jetty-osgi/src/main/resources/jetty-logging.properties
+++ b/jetty-osgi/test-jetty-osgi/src/main/resources/jetty-logging.properties
@@ -1,2 +1 @@
 org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
-org.eclipse.jetty.spdy.LEVEL=WARN
diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-deployer.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-deployer.xml
index b03a648..19b3a5d 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-deployer.xml
+++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-deployer.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-http.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-http.xml
new file mode 100644
index 0000000..319ae6c
--- /dev/null
+++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-http.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<!-- ============================================================= -->
+<!-- Configure the Jetty Server instance with an ID "Server"       -->
+<!-- by adding a HTTP connector.                                   -->
+<!-- This configuration must be used in conjunction with jetty.xml -->
+<!-- ============================================================= -->
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <!-- =========================================================== -->
+  <!-- Add a HTTP Connector.                                       -->
+  <!-- Configure an o.e.j.server.ServerConnector with a single     -->
+  <!-- HttpConnectionFactory instance using the common httpConfig  -->
+  <!-- instance defined in jetty.xml                               -->
+  <!--                                                             -->
+  <!-- Consult the javadoc of o.e.j.server.ServerConnector and     -->
+  <!-- o.e.j.server.HttpConnectionFactory for all configuration    -->
+  <!-- that may be set here.                                       -->
+  <!-- =========================================================== -->
+  <Call name="addConnector">
+    <Arg>
+      <New class="org.eclipse.jetty.server.ServerConnector">
+        <Arg name="server"><Ref refid="Server" /></Arg>
+        <Arg name="factories">
+          <Array type="org.eclipse.jetty.server.ConnectionFactory">
+            <Item>
+              <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+                <Arg name="config"><Ref refid="httpConfig" /></Arg>
+              </New>
+            </Item>
+          </Array>
+        </Arg>
+        <Set name="host"><Property name="jetty.http.host" /></Set>
+        <Set name="port"><Property name="jetty.http.port" default="80" /></Set>
+        <Set name="idleTimeout"><Property name="jetty.http.idleTimeout" default="30000"/></Set>
+      </New>
+    </Arg>
+  </Call>
+
+</Configure>
diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-http2.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-http2.xml
new file mode 100644
index 0000000..3362c1b
--- /dev/null
+++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-http2.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<!-- ============================================================= -->
+<!-- Configure a HTTP2 on the ssl connector.                       -->
+<!-- ============================================================= -->
+<Configure id="sslConnector" class="org.eclipse.jetty.server.ServerConnector">
+  <Call name="addConnectionFactory">
+    <Arg>
+      <New class="org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory">
+        <Arg name="config"><Ref refid="sslHttpConfig"/></Arg>
+        <Set name="maxConcurrentStreams"><Property name="jetty.http2.maxConcurrentStreams" default="1024"/></Set>
+      </New>
+    </Arg>
+  </Call>
+</Configure>
+
diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-https.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-https.xml
index a6bef16..c9d497e 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-https.xml
+++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-https.xml
@@ -1,47 +1,28 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- ============================================================= -->
 <!-- Configure a HTTPS connector.                                  -->
 <!-- This configuration must be used in conjunction with jetty.xml -->
 <!-- and jetty-ssl.xml.                                            -->
 <!-- ============================================================= -->
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
+<Configure id="sslConnector" class="org.eclipse.jetty.server.ServerConnector">
 
-  <!-- =========================================================== -->
-  <!-- Add a HTTPS Connector.                                      -->
-  <!-- Configure an o.e.j.server.ServerConnector with connection   -->
-  <!-- factories for TLS (aka SSL) and HTTP to provide HTTPS.      -->
-  <!-- All accepted TLS connections are wired to a HTTP connection.-->
-  <!--                                                             -->
-  <!-- Consult the javadoc of o.e.j.server.ServerConnector,        -->
-  <!-- o.e.j.server.SslConnectionFactory and                       -->
-  <!-- o.e.j.server.HttpConnectionFactory for all configuration    -->
-  <!-- that may be set here.                                       -->
-  <!-- =========================================================== -->
-  <Call id="httpsConnector" name="addConnector">
+  <Call name="addIfAbsentConnectionFactory">
     <Arg>
-      <New class="org.eclipse.jetty.server.ServerConnector">
-        <Arg name="server"><Ref refid="Server" /></Arg>
-          <Arg name="factories">
-            <Array type="org.eclipse.jetty.server.ConnectionFactory">
-              <Item>
-                <New class="org.eclipse.jetty.server.SslConnectionFactory">
-                  <Arg name="next">http/1.1</Arg>
-                  <Arg name="sslContextFactory"><Ref refid="sslContextFactory"/></Arg>
-                </New>
-              </Item>
-              <Item>
-                <New class="org.eclipse.jetty.server.HttpConnectionFactory">
-                  <Arg name="config"><Ref refid="sslHttpConfig"/></Arg>
-                </New>
-              </Item>
-            </Array>
-          </Arg>
-          <Set name="host"><Property name="jetty.host" /></Set>
-          <Set name="port"><Property name="jetty.https.port" default="8443" /></Set>
-          <Set name="idleTimeout">30000</Set>
-        </New>
+      <New class="org.eclipse.jetty.server.SslConnectionFactory">
+        <Arg name="next">http/1.1</Arg>
+        <Arg name="sslContextFactory"><Ref refid="sslContextFactory"/></Arg>
+      </New>
     </Arg>
   </Call>
+
+  <Call name="addConnectionFactory">
+    <Arg>
+      <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+        <Arg name="config"><Ref refid="sslHttpConfig" /></Arg>
+      </New>
+    </Arg>
+  </Call>
+  
 </Configure>
diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-selector.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-selector.xml
deleted file mode 100644
index 67cd87c..0000000
--- a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-selector.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
-
-
-    <!-- =========================================================== -->
-    <!-- Add connector                                               -->
-    <!-- =========================================================== -->
-
-    <Call name="addConnector">
-      <Arg>
-          <New class="org.eclipse.jetty.server.ServerConnector">
-            <Arg><Ref refid="Server" /></Arg>
-             <Arg name="factories">
-               <Array type="org.eclipse.jetty.server.ConnectionFactory">
-                <Item>
-                  <New class="org.eclipse.jetty.server.HttpConnectionFactory">
-                   <Arg name="config"><Ref refid="httpConfig" /></Arg>
-                  </New>
-                </Item>
-              </Array>
-             </Arg>
-            <Set name="host"><Property name="jetty.host" /></Set>
-            <Set name="port"><Property name="jetty.port" default="8080"/></Set>
-            <Set name="idleTimeout">300000</Set>
-          </New>
-      </Arg>
-    </Call>
-
-</Configure>
diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-spdy.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-spdy.xml
deleted file mode 100644
index 2bc1fe3..0000000
--- a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-spdy.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
-
-    <!-- =========================================================== -->
-    <!-- Add HTTP Customizer for Secure request                      -->
-    <!-- =========================================================== -->
-    <New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
-        <Set name="secureScheme">https</Set>
-        <Set name="securePort">
-            <SystemProperty name="jetty.spdy.port" default="8443"/>
-        </Set>
-        <Set name="outputBufferSize">32768</Set>
-        <Set name="requestHeaderSize">8192</Set>
-        <Set name="responseHeaderSize">8192</Set>
-        <Call name="addCustomizer">
-            <Arg>
-                <New class="org.eclipse.jetty.server.SecureRequestCustomizer"/>
-            </Arg>
-        </Call>
-    </New>
-
-
-    <!-- =========================================================== -->
-    <!-- Setup a SSL Context factory                                 -->
-    <!-- =========================================================== -->
-    <New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
-        <Set name="KeyStorePath"><Property name="jetty.home" default="."/>/etc/keystore
-        </Set>
-        <Set name="KeyStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
-        <Set name="KeyManagerPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
-        <Set name="TrustStorePath"><Property name="jetty.home" default="."/>/etc/keystore
-        </Set>
-        <Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
-    </New>
-
-    <!-- =========================================================== -->
-    <!-- Set connectors                                              -->
-    <!-- =========================================================== -->
-    <Call id="sslConnector" name="addConnector">
-        <Arg>
-            <New class="org.eclipse.jetty.server.ServerConnector">
-                <Arg name="server">
-                    <Ref refid="Server"/>
-                </Arg>
-                <Arg name="factories">
-                    <Array type="org.eclipse.jetty.server.ConnectionFactory">
-                        <Item>
-                            <New class="org.eclipse.jetty.server.SslConnectionFactory">
-                                <Arg name="next">alpn</Arg>
-                                <Arg name="sslContextFactory">
-                                    <Ref refid="sslContextFactory"/>
-                                </Arg>
-                            </New>
-                        </Item>
-
-                        <Item>
-                            <New class="org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory">
-                                <Arg name="protocols">
-                                    <Array type="String">
-                                        <Item>spdy/3</Item>
-                                        <Item>spdy/2</Item>
-                                        <Item>http/1.1</Item>
-                                    </Array>
-                                </Arg>
-                                <Set name="defaultProtocol">http/1.1</Set>
-                            </New>
-                        </Item>
-
-                        <Item>
-                            <New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
-                                <Arg name="version" type="int">3</Arg>
-                                <Arg name="config">
-                                    <Ref refid="httpConfig"/>
-                                </Arg>
-                                <!-- Set the initial window size for this SPDY connector. -->
-                                <!-- See: http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-2.6.8-WINDOW_UPDATE -->
-                                <Set name="initialWindowSize">65536</Set>
-                            </New>
-                        </Item>
-
-                        <Item>
-                            <New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
-                                <Arg name="version" type="int">2</Arg>
-                                <Arg name="config">
-                                    <Ref refid="httpConfig"/>
-                                </Arg>
-                                <!-- Set the initial window size for this SPDY connector. -->
-                                <!-- See: http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-2.6.8-WINDOW_UPDATE -->
-                                <Set name="initialWindowSize">65536</Set>
-                            </New>
-                        </Item>
-
-                        <Item>
-                            <New class="org.eclipse.jetty.server.HttpConnectionFactory">
-                                <Arg name="config">
-                                    <Ref refid="httpConfig"/>
-                                </Arg>
-                            </New>
-                        </Item>
-                    </Array>
-                </Arg>
-                <Set name="host">
-                    <Property name="jetty.host"/>
-                </Set>
-                <Set name="port">
-                    <SystemProperty name="jetty.spdy.port" default="8443"/>
-                </Set>
-                <Set name="idleTimeout">30000</Set>
-            </New>
-        </Arg>
-    </Call>
-
-</Configure>
diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-ssl.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-ssl.xml
index b4c3551..dce437d 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-ssl.xml
+++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-ssl.xml
@@ -1,20 +1,45 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- ============================================================= -->
-<!-- Configure a TLS (SSL) Context Factory                         -->
-<!-- This configuration must be used in conjunction with jetty.xml -->
-<!-- and either jetty-https.xml or jetty-spdy.xml (but not both)   -->
+<!-- Base SSL configuration                                        -->
+<!-- This configuration needs to be used together with 1 or more   -->
+<!-- of jetty-https.xml and/or jetty-http2.xml                     -->
 <!-- ============================================================= -->
-<Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
-  <Set name="KeyStorePath"><Property name="jetty.home" default="." />/<Property name="jetty.keystore" default="etc/keystore"/></Set>
-  <Set name="KeyStorePassword"><Property name="jetty.keystore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
-  <Set name="KeyManagerPassword"><Property name="jetty.keymanager.password" default="OBF:1u2u1wml1z7s1z7a1wnl1u2g"/></Set>
-  <Set name="TrustStorePath"><Property name="jetty.home" default="." />/<Property name="jetty.truststore" default="etc/keystore"/></Set>
-  <Set name="TrustStorePassword"><Property name="jetty.truststore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
-  <Set name="EndpointIdentificationAlgorithm"></Set>
-  <Set name="ExcludeCipherSuites">
-    <Array type="String">
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <!-- =========================================================== -->
+  <!-- Add a SSL Connector with no protocol factories              -->
+  <!-- =========================================================== -->
+  <Call  name="addConnector">
+    <Arg>
+      <New id="sslConnector" class="org.eclipse.jetty.server.ServerConnector">
+        <Arg name="server"><Ref refid="Server" /></Arg>
+          <Arg name="factories">
+            <Array type="org.eclipse.jetty.server.ConnectionFactory">
+            </Array>
+          </Arg>
+          <Set name="host"><Property name="jetty.ssl.host" /></Set>
+          <Set name="port"><Property name="jetty.ssl.port" default="443" /></Set>
+          <Set name="idleTimeout"><Property name="jetty.ssl.idleTimeout" default="30000"/></Set>
+          <Set name="soLingerTime"><Property name="jetty.ssl.soLingerTime" default="-1"/></Set>
+        </New>
+    </Arg>
+  </Call>
+
+  <!-- ============================================================= -->
+  <!-- Create a TLS (SSL) Context Factory  for later reuse           -->
+  <!-- ============================================================= -->
+  <New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
+    <Set name="KeyStorePath"><Property name="jetty.base" default="." />/<Property name="jetty.sslContext.keyStorePath" default="etc/keystore"/></Set>
+    <Set name="KeyStorePassword"><Property name="jetty.sslContext.keyStorePassword" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
+    <Set name="TrustStorePath"><Property name="jetty.base" default="." />/<Property name="jetty.sslContext.trustStorePath" default="etc/keystore"/></Set>
+    <Set name="TrustStorePassword"><Property name="jetty.sslContext.trustStorePassword" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
+    <Set name="EndpointIdentificationAlgorithm"></Set>
+    <Set name="NeedClientAuth"><Property name="jetty.sslContext.needClientAuth" default="false"/></Set>
+    <Set name="WantClientAuth"><Property name="jetty.sslContext.wantClientAuth" default="false"/></Set>
+    <Set name="ExcludeCipherSuites">
+     <Array type="String">
       <Item>SSL_RSA_WITH_DES_CBC_SHA</Item>
       <Item>SSL_DHE_RSA_WITH_DES_CBC_SHA</Item>
       <Item>SSL_DHE_DSS_WITH_DES_CBC_SHA</Item>
@@ -22,8 +47,9 @@
       <Item>SSL_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
       <Item>SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
       <Item>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</Item>
-    </Array>
-  </Set>
+     </Array>
+    </Set>
+  </New>
 
   <!-- =========================================================== -->
   <!-- Create a TLS specific HttpConfiguration based on the        -->
diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-testrealm.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-testrealm.xml
index d26b427..9c6a142 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-testrealm.xml
+++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-testrealm.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
     <!-- =========================================================== -->
     <!-- Configure Authentication Login Service                      -->
@@ -12,7 +12,7 @@
       <Arg>
         <New class="org.eclipse.jetty.security.HashLoginService">
           <Set name="name">Test Realm</Set>
-          <Set name="config"><Property name="jetty.home" default="src/test/config"/>realm.properties</Set>
+          <Set name="config"><Property name="jetty.home" default="src/test/config"/>/realm.properties</Set>
           <Set name="refreshInterval">0</Set>
         </New>
       </Arg>
diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty.xml
index 170530b..a3850c9 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty.xml
+++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 
 <!-- =============================================================== -->
@@ -15,11 +15,11 @@
     <!-- Server Thread Pool                                          -->
     <!-- =========================================================== -->
     <Get name="ThreadPool">
-      <!-- Default queued blocking threadpool -->
       <Set name="minThreads">10</Set>
       <Set name="maxThreads">200</Set>
     </Get>
 
+
     <!-- =========================================================== -->
     <!-- Set handler Collection Structure                            -->
     <!-- =========================================================== -->
@@ -43,7 +43,7 @@
 
     <New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
       <Set name="secureScheme">https</Set>
-      <Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
+      <Set name="securePort"><Property name="jetty.httpConfig.securePort" default="8443" /></Set>
       <Set name="outputBufferSize">32768</Set>
       <Set name="requestHeaderSize">8192</Set>
       <Set name="responseHeaderSize">8192</Set>
@@ -63,21 +63,25 @@
 
 
     <!-- =========================================================== -->
-    <!-- jetty-jndi by default                                       -->
-    <!-- =========================================================== -->
-    <Call class="org.eclipse.jetty.webapp.Configuration$ClassList" name="setServerDefault">
-      <Arg><Ref refid="Server" /></Arg>
-      <Call name="addAfter">
-        <Arg name="afterClass">org.eclipse.jetty.webapp.FragmentConfiguration</Arg>
-        <Arg>
-          <Array type="String">
-            <Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
-            <Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
-            <Item>org.eclipse.jetty.annotations.AnnotationConfiguration</Item>
-          </Array>
-        </Arg>
-      </Call>
+    <!-- Set up the list of default configuration classes            -->
+    <!-- =========================================================== -->    
+    <Call name="setAttribute">
+       <Arg>org.eclipse.jetty.webapp.configuration</Arg>
+       <Arg>
+        <New class="org.eclipse.jetty.webapp.Configuration$ClassList">
+          <Arg>
+            <Array type="String">
+              <Item>org.eclipse.jetty.osgi.boot.OSGiWebInfConfiguration</Item>
+              <Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
+              <Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
+              <Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
+              <Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>  
+            </Array>
+          </Arg>
+        </New>
+       </Arg>
     </Call>
+   
 
     <Call class="java.lang.System" name="setProperty">
       <Arg>java.naming.factory.initial</Arg>
diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/keystore b/jetty-osgi/test-jetty-osgi/src/test/config/etc/keystore
index d6592f9..428ba54 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/config/etc/keystore
+++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/keystore
Binary files differ
diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/webdefault.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/webdefault.xml
index 1d61c93..d54d76f 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/config/etc/webdefault.xml
+++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/webdefault.xml
@@ -1,113 +1,142 @@
 <?xml version="1.0" encoding="UTF-8"?>
-
-<!-- ===================================================================== -->
-<!-- This file contains the default descriptor for web applications.       -->
-<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
-<!-- The intent of this descriptor is to include jetty specific or common  -->
-<!-- configuration for all webapps.   If a context has a webdefault.xml    -->
-<!-- descriptor, it is applied before the contexts own web.xml file        -->
-<!--                                                                       -->
-<!-- A context may be assigned a default descriptor by:                    -->
-<!--  + Calling WebApplicationContext.setDefaultsDescriptor                -->
-<!--  + Passed an arg to addWebApplications                                -->
-<!--                                                                       -->
-<!-- This file is used both as the resource within the jetty.jar (which is -->
-<!-- used as the default if no explicit defaults descriptor is set) and it -->
-<!-- is copied to the etc directory of the Jetty distro and explicitly     -->
-<!-- by the jetty.xml file.                                                -->
-<!--                                                                       -->
-<!-- ===================================================================== -->
-<web-app
-   xmlns="http://java.sun.com/xml/ns/javaee"
+<web-app 
+   xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
-   metadata-complete="true"
-   version="2.5">
+   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+   metadata-complete="false"
+   version="3.1"> 
+
+  <!-- ===================================================================== -->
+  <!-- This file contains the default descriptor for web applications.       -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+  <!-- The intent of this descriptor is to include jetty specific or common  -->
+  <!-- configuration for all webapps.   If a context has a webdefault.xml    -->
+  <!-- descriptor, it is applied before the context's own web.xml file       -->
+  <!--                                                                       -->
+  <!-- A context may be assigned a default descriptor by calling             -->
+  <!-- WebAppContext.setDefaultsDescriptor(String).                          -->
+  <!--                                                                       -->
+  <!-- This file is present in the jetty-webapp.jar, and is used as the      -->
+  <!-- defaults descriptor if no other is explicitly set on a context.       -->
+  <!--                                                                       -->
+  <!-- A copy of this file is also placed into the $JETTY_HOME/etc dir of    -->
+  <!-- the  distribution, and is referenced by some of the other xml files,  -->
+  <!-- eg the jetty-deploy.xml file.                                         -->
+  <!-- ===================================================================== -->
 
   <description>
-    Default web.xml file.
+    Default web.xml file.  
     This file is applied to a Web application before it's own WEB_INF/web.xml file
   </description>
 
+  <!-- ==================================================================== -->
+  <!-- Removes static references to beans from javax.el.BeanELResolver to   -->
+  <!-- ensure webapp classloader can be released on undeploy                -->
+  <!-- ==================================================================== -->
+  <listener>
+   <listener-class>org.eclipse.jetty.servlet.listener.ELContextCleaner</listener-class>
+  </listener>
+  
+  <!-- ==================================================================== -->
+  <!-- Removes static cache of Methods from java.beans.Introspector to      -->
+  <!-- ensure webapp classloader can be released on undeploy                -->
+  <!-- ==================================================================== -->  
+  <listener>
+   <listener-class>org.eclipse.jetty.servlet.listener.IntrospectorCleaner</listener-class>
+  </listener>
+  
 
   <!-- ==================================================================== -->
   <!-- Context params to control Session Cookies                            -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
-  <!-- UNCOMMENT TO ACTIVATE
-  <context-param>
-    <param-name>org.eclipse.jetty.servlet.SessionDomain</param-name>
-    <param-value>127.0.0.1</param-value>
-  </context-param>
-
-  <context-param>
-    <param-name>org.eclipse.jetty.servlet.SessionPath</param-name>
-    <param-value>/</param-value>
-  </context-param>
-
-  <context-param>
-    <param-name>org.eclipse.jetty.servlet.MaxAge</param-name>
-    <param-value>-1</param-value>
-  </context-param>
+  <!--
+    UNCOMMENT TO ACTIVATE 
+    <context-param> 
+      <param-name>org.eclipse.jetty.servlet.SessionDomain</param-name> 
+      <param-value>127.0.0.1</param-value> 
+    </context-param> 
+    <context-param>
+      <param-name>org.eclipse.jetty.servlet.SessionPath</param-name>
+      <param-value>/</param-value>
+    </context-param>
+    <context-param>
+      <param-name>org.eclipse.jetty.servlet.MaxAge</param-name>
+      <param-value>-1</param-value>
+    </context-param>
   -->
 
-
   <!-- ==================================================================== -->
   <!-- The default servlet.                                                 -->
   <!-- This servlet, normally mapped to /, provides the handling for static -->
   <!-- content, OPTIONS and TRACE methods for the context.                  -->
   <!-- The following initParameters are supported:                          -->
-  <!--                                                                      -->
-  <!--   acceptRanges     If true, range requests and responses are         -->
-  <!--                    supported                                         -->
-  <!--                                                                      -->
-  <!--   dirAllowed       If true, directory listings are returned if no    -->
-  <!--                    welcome file is found. Else 403 Forbidden.        -->
-  <!--                                                                      -->
-  <!--   welcomeServlets  If true, attempt to dispatch to welcome files     -->
-  <!--                    that are servlets, if no matching static          -->
-  <!--                    resources can be found.                           -->
-  <!--                                                                      -->
-  <!--   redirectWelcome  If true, redirect welcome file requests           -->
-  <!--                    else use request dispatcher forwards              -->
-  <!--                                                                      -->
-  <!--   gzip             If set to true, then static content will be served-->
-  <!--                    as gzip content encoded if a matching resource is -->
-  <!--                    found ending with ".gz"                           -->
-  <!--                                                                      -->
-  <!--   resoureBase      Can be set to replace the context resource base   -->
-  <!--                                                                      -->
-  <!--   relativeResourceBase                                               -->
-  <!--                    Set with a pathname relative to the base of the   -->
-  <!--                    servlet context root. Useful for only serving     -->
-  <!--                    static content from only specific subdirectories. -->
-  <!--                                                                      -->
-  <!--   useFileMappedBuffer                                                -->
-  <!--                    If set to true (the default), a  memory mapped    -->
-  <!--                    file buffer will be used to serve static content  -->
-  <!--                    when using an NIO connector. Setting this value   -->
-  <!--                    to false means that a direct buffer will be used  -->
-  <!--                    instead. If you are having trouble with Windows   -->
-  <!--                    file locking, set this to false.                  -->
-  <!--                                                                      -->
-  <!--  cacheControl      If set, all static content will have this value   -->
-  <!--                    set as the cache-control header.                  -->
-  <!--                                                                      -->
-  <!--  maxCacheSize      Maximum size of the static resource cache         -->
-  <!--                                                                      -->
-  <!--  maxCachedFileSize Maximum size of any single file in the cache      -->
-  <!--                                                                      -->
-  <!--  maxCachedFiles    Maximum number of files in the cache              -->
-  <!--                                                                      -->
-  <!--  cacheType         "nio", "bio" or "both" to determine the type(s)   -->
-  <!--                    of resource cache. A bio cached buffer may be used-->
-  <!--                    by nio but is not as efficient as a nio buffer.   -->
-  <!--                    An nio cached buffer may not be used by bio.      -->
-  <!--                                                                      -->
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+  <!--  
+ *  acceptRanges      If true, range requests and responses are
+ *                    supported
+ *
+ *  dirAllowed        If true, directory listings are returned if no
+ *                    welcome file is found. Else 403 Forbidden.
+ *
+ *  welcomeServlets   If true, attempt to dispatch to welcome files
+ *                    that are servlets, but only after no matching static
+ *                    resources could be found. If false, then a welcome
+ *                    file must exist on disk. If "exact", then exact
+ *                    servlet matches are supported without an existing file.
+ *                    Default is true.
+ *
+ *                    This must be false if you want directory listings,
+ *                    but have index.jsp in your welcome file list.
+ *
+ *  redirectWelcome   If true, welcome files are redirected rather than
+ *                    forwarded to.
+ *
+ *  gzip              If set to true, then static content will be served as
+ *                    gzip content encoded if a matching resource is
+ *                    found ending with ".gz"
+ *
+ *  resourceBase      Set to replace the context resource base
+ *
+ *  resourceCache     If set, this is a context attribute name, which the servlet
+ *                    will use to look for a shared ResourceCache instance.
+ *
+ *  relativeResourceBase
+ *                    Set with a pathname relative to the base of the
+ *                    servlet context root. Useful for only serving static content out
+ *                    of only specific subdirectories.
+ *
+ *  pathInfoOnly      If true, only the path info will be applied to the resourceBase
+ *
+ *  stylesheet        Set with the location of an optional stylesheet that will be used
+ *                    to decorate the directory listing html.
+ *
+ *  aliases           If True, aliases of resources are allowed (eg. symbolic
+ *                    links and caps variations). May bypass security constraints.
+ *                    
+ *  etags             If True, weak etags will be generated and handled.
+ *
+ *  maxCacheSize      The maximum total size of the cache or 0 for no cache.
+ *  maxCachedFileSize The maximum size of a file to cache
+ *  maxCachedFiles    The maximum number of files to cache
+ *
+ *  useFileMappedBuffer
+ *                    If set to true, it will use mapped file buffers to serve static content
+ *                    when using an NIO connector. Setting this value to false means that
+ *                    a direct buffer will be used instead of a mapped file buffer.
+ *                    This file sets the value to true.
+ *
+ *  cacheControl      If set, all static content will have this value set as the cache-control
+ *                    header.
+ *
+ -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <servlet>
     <servlet-name>default</servlet-name>
     <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
     <init-param>
+      <param-name>aliases</param-name>
+      <param-value>false</param-value>
+    </init-param>
+    <init-param>
       <param-name>acceptRanges</param-name>
       <param-value>true</param-value>
     </init-param>
@@ -129,19 +158,19 @@
     </init-param>
     <init-param>
       <param-name>maxCachedFileSize</param-name>
-      <param-value>10000000</param-value>
+      <param-value>200000000</param-value>
     </init-param>
     <init-param>
       <param-name>maxCachedFiles</param-name>
-      <param-value>1000</param-value>
-    </init-param>
-    <init-param>
-      <param-name>cacheType</param-name>
-      <param-value>both</param-value>
+      <param-value>2048</param-value>
     </init-param>
     <init-param>
       <param-name>gzip</param-name>
-      <param-value>true</param-value>
+      <param-value>false</param-value>
+    </init-param>
+    <init-param>
+      <param-name>etags</param-name>
+      <param-value>false</param-value>
     </init-param>
     <init-param>
       <param-name>useFileMappedBuffer</param-name>
@@ -149,6 +178,12 @@
     </init-param>
     <!--
     <init-param>
+      <param-name>resourceCache</param-name>
+      <param-value>resourceCache</param-value>
+    </init-param>
+    -->
+    <!--
+    <init-param>
       <param-name>cacheControl</param-name>
       <param-value>max-age=3600,public</param-value>
     </init-param>
@@ -156,18 +191,21 @@
     <load-on-startup>0</load-on-startup>
   </servlet>
 
-  <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>default</servlet-name>
+    <url-pattern>/</url-pattern>
+  </servlet-mapping>
 
 
   <!-- ==================================================================== -->
   <!-- JSP Servlet                                                          -->
-  <!-- This is the jasper JSP servlet from the jakarta project              -->
+  <!-- This is the jasper JSP servlet.                                      -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <!-- The JSP page compiler and execution servlet, which is the mechanism  -->
-  <!-- used by Glassfish to support JSP pages.  Traditionally, this servlet -->
-  <!-- is mapped to URL patterh "*.jsp".  This servlet supports the         -->
-  <!-- following initialization parameters (default values are in square    -->
-  <!-- brackets):                                                           -->
+  <!-- used by the jsp container to support JSP pages.  Traditionally,      -->
+  <!-- this servlet is mapped to URL pattern "*.jsp".  This servlet         -->
+  <!-- supports the following initialization parameters (default values     -->
+  <!-- are in square brackets):                                             -->
   <!--                                                                      -->
   <!--   checkInterval       If development is false and reloading is true, -->
   <!--                       background compiles are enabled. checkInterval -->
@@ -175,7 +213,7 @@
   <!--                       if a JSP page needs to be recompiled. [300]    -->
   <!--                                                                      -->
   <!--   compiler            Which compiler Ant should use to compile JSP   -->
-  <!--                       pages.  See the Ant documenation for more      -->
+  <!--                       pages.  See the Ant documentation for more     -->
   <!--                       information. [javac]                           -->
   <!--                                                                      -->
   <!--   classdebuginfo      Should the class file be compiled with         -->
@@ -236,30 +274,31 @@
   <!--   xpoweredBy          Determines whether X-Powered-By response       -->
   <!--                       header is added by generated servlet  [false]  -->
   <!--                                                                      -->
-  <!-- If you wish to use Jikes to compile JSP pages:                       -->
-  <!--   Set the init parameter "compiler" to "jikes".  Define              -->
-  <!--   the property "-Dbuild.compiler.emacs=true" when starting Jetty     -->
-  <!--   to cause Jikes to emit error messages in a format compatible with  -->
-  <!--   Jasper.                                                            -->
-  <!--   If you get an error reporting that jikes can't use UTF-8 encoding, -->
-  <!--   try setting the init parameter "javaEncoding" to "ISO-8859-1".     -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <servlet id="jsp">
     <servlet-name>jsp</servlet-name>
-    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+    <servlet-class>org.eclipse.jetty.jsp.JettyJspServlet</servlet-class>
     <init-param>
-        <param-name>logVerbosityLevel</param-name>
-        <param-value>DEBUG</param-value>
+      <param-name>logVerbosityLevel</param-name>
+      <param-value>DEBUG</param-value>
     </init-param>
     <init-param>
-        <param-name>fork</param-name>
-        <param-value>false</param-value>
+      <param-name>fork</param-name>
+      <param-value>false</param-value>
     </init-param>
     <init-param>
-        <param-name>xpoweredBy</param-name>
-        <param-value>false</param-value>
+      <param-name>xpoweredBy</param-name>
+      <param-value>false</param-value>
     </init-param>
-    <!--
+    <init-param>
+      <param-name>compilerTargetVM</param-name>
+      <param-value>1.7</param-value>
+    </init-param>
+    <init-param>
+      <param-name>compilerSourceVM</param-name>
+      <param-value>1.7</param-value>
+    </init-param>
+    <!--  
     <init-param>
         <param-name>classpath</param-name>
         <param-value>?</param-value>
@@ -280,50 +319,10 @@
     <url-pattern>*.XSP</url-pattern>
   </servlet-mapping>
 
+
   <!-- ==================================================================== -->
-  <!-- Dynamic Servlet Invoker.                                             -->
-  <!-- This servlet invokes anonymous servlets that have not been defined   -->
-  <!-- in the web.xml or by other means. The first element of the pathInfo  -->
-  <!-- of a request passed to the envoker is treated as a servlet name for  -->
-  <!-- an existing servlet, or as a class name of a new servlet.            -->
-  <!-- This servlet is normally mapped to /servlet/*                        -->
-  <!-- This servlet support the following initParams:                       -->
-  <!--                                                                      -->
-  <!--  nonContextServlets       If false, the invoker can only load        -->
-  <!--                           servlets from the contexts classloader.    -->
-  <!--                           This is false by default and setting this  -->
-  <!--                           to true may have security implications.    -->
-  <!--                                                                      -->
-  <!--  verbose                  If true, log dynamic loads                 -->
-  <!--                                                                      -->
-  <!--  *                        All other parameters are copied to the     -->
-  <!--                           each dynamic servlet as init parameters    -->
+  <!-- Default session configuration                                        -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
-  <!-- Uncomment for dynamic invocation
-  <servlet>
-    <servlet-name>invoker</servlet-name>
-    <servlet-class>org.eclipse.jetty.servlet.Invoker</servlet-class>
-    <init-param>
-      <param-name>verbose</param-name>
-      <param-value>false</param-value>
-    </init-param>
-    <init-param>
-      <param-name>nonContextServlets</param-name>
-      <param-value>false</param-value>
-    </init-param>
-    <init-param>
-      <param-name>dynamicParam</param-name>
-      <param-value>anyValue</param-value>
-    </init-param>
-    <load-on-startup>0</load-on-startup>
-  </servlet>
-
-  <servlet-mapping> <servlet-name>invoker</servlet-name> <url-pattern>/servlet/*</url-pattern> </servlet-mapping>
-  -->
-
-
-
-  <!-- ==================================================================== -->
   <session-config>
     <session-timeout>30</session-timeout>
   </session-config>
@@ -331,7 +330,7 @@
   <!-- ==================================================================== -->
   <!-- Default MIME mappings                                                -->
   <!-- The default MIME mappings are provided by the mime.properties        -->
-  <!-- resource in the org.eclipse.jetty.server.jar file.  Additional or modified  -->
+  <!-- resource in the jetty-http.jar file.  Additional or modified         -->
   <!-- mappings may be specified here                                       -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <!-- UNCOMMENT TO ACTIVATE
@@ -342,6 +341,8 @@
   -->
 
   <!-- ==================================================================== -->
+  <!-- Default welcome files                                                -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <welcome-file-list>
     <welcome-file>index.html</welcome-file>
     <welcome-file>index.htm</welcome-file>
@@ -349,48 +350,170 @@
   </welcome-file-list>
 
   <!-- ==================================================================== -->
+  <!-- Default locale encodings                                             -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <locale-encoding-mapping-list>
-    <locale-encoding-mapping><locale>ar</locale><encoding>ISO-8859-6</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>be</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>bg</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ca</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>cs</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>da</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>de</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>el</locale><encoding>ISO-8859-7</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>en</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>es</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>et</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>fi</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>fr</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>hr</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>hu</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>is</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>it</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>iw</locale><encoding>ISO-8859-8</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ja</locale><encoding>Shift_JIS</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ko</locale><encoding>EUC-KR</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>lt</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>lv</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>mk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>nl</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>no</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>pl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>pt</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ro</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ru</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sh</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sk</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sq</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sr</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sv</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>tr</locale><encoding>ISO-8859-9</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>uk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>zh</locale><encoding>GB2312</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>zh_TW</locale><encoding>Big5</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ar</locale>
+      <encoding>ISO-8859-6</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>be</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>bg</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ca</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>cs</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>da</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>de</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>el</locale>
+      <encoding>ISO-8859-7</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>en</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>es</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>et</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>fi</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>fr</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>hr</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>hu</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>is</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>it</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>iw</locale>
+      <encoding>ISO-8859-8</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ja</locale>
+      <encoding>Shift_JIS</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ko</locale>
+      <encoding>EUC-KR</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>lt</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>lv</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>mk</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>nl</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>no</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>pl</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>pt</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ro</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ru</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sh</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sk</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sl</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sq</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sr</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sv</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>tr</locale>
+      <encoding>ISO-8859-9</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>uk</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>zh</locale>
+      <encoding>GB2312</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>zh_TW</locale>
+      <encoding>Big5</encoding>
+    </locale-encoding-mapping>
   </locale-encoding-mapping-list>
 
+  <!-- ==================================================================== -->
+  <!-- Disable TRACE method with security constraint                        -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <security-constraint>
     <web-resource-collection>
       <web-resource-name>Disable TRACE</web-resource-name>
@@ -399,6 +522,13 @@
     </web-resource-collection>
     <auth-constraint/>
   </security-constraint>
+  <security-constraint>
+    <web-resource-collection>
+      <web-resource-name>Enable everything but TRACE</web-resource-name>
+      <url-pattern>/</url-pattern>
+      <http-method-omission>TRACE</http-method-omission>
+    </web-resource-collection>
+  </security-constraint>
 
 </web-app>
 
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java
index 4760a5d..5f71dda 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java
@@ -18,18 +18,10 @@
 
 package org.eclipse.jetty.osgi.test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
-import static org.ops4j.pax.exam.CoreOptions.options;
-import static org.ops4j.pax.exam.CoreOptions.systemProperty;
-
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-
 import javax.inject.Inject;
 
 import org.eclipse.jetty.client.HttpClient;
@@ -40,14 +32,21 @@
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
 import org.ops4j.pax.exam.CoreOptions;
 import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.Configuration;
 import org.ops4j.pax.exam.junit.PaxExam;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
 /**
  * TestJettyOSGiBootContextAsService
  * 
@@ -70,37 +69,36 @@
     {
         ArrayList<Option> options = new ArrayList<Option>();
         options.add(CoreOptions.junitBundles());
-        options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
+        options.addAll(configureJettyHomeAndPort("jetty-http.xml"));
         options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*"));
         options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
 
         // a bundle that registers a webapp as a service for the jetty osgi core
         // to pick up and deploy
         options.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("test-jetty-osgi-context").versionAsInProject().start());
-
-        options.addAll(Arrays.asList(options(systemProperty("pax.exam.logging").value("none"))));
-        options.addAll(Arrays.asList(options(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL))));
-        options.addAll(Arrays.asList(options(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL))));
+        options.add(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL));
+        options.add(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL));
         
         return options.toArray(new Option[options.size()]);
     }
 
     public static List<Option> configureJettyHomeAndPort(String jettySelectorFileName)
     {
-        File etcFolder = new File("src/test/config/etc");
-        String etc = "file://" + etcFolder.getAbsolutePath();
+        File etc = new File("src/test/config/etc");
+        StringBuffer xmlConfigs = new StringBuffer();
+        xmlConfigs.append(new File(etc, "jetty.xml").toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc, jettySelectorFileName).toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc,"jetty-deployer.xml").toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc, "jetty-testrealm.xml").toURI());
+        
         List<Option> options = new ArrayList<Option>();
-        options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS).value(etc     + "/jetty.xml;"
-                + etc
-                + "/"
-                + jettySelectorFileName
-                + ";"
-                + etc
-                + "/jetty-deployer.xml;"
-                + etc
-                + "/jetty-testrealm.xml"));
-        options.add(systemProperty("jetty.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT)));
-        options.add(systemProperty("jetty.home").value(etcFolder.getParentFile().getAbsolutePath()));
+        options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS).value(xmlConfigs.toString()));
+        options.add(systemProperty("jetty.http.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_HTTP_PORT)));
+        options.add(systemProperty("jetty.ssl.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_SSL_PORT)));
+        options.add(systemProperty("jetty.home").value(etc.getParentFile().getAbsolutePath()));
         return options;
     }
 
@@ -122,7 +120,7 @@
         try
         {
             client.start();
-            ContentResponse response = client.GET("http://127.0.0.1:" + TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT + "/acme/index.html");
+            ContentResponse response = client.GET("http://127.0.0.1:" + TestJettyOSGiBootCore.DEFAULT_HTTP_PORT + "/acme/index.html");
             assertEquals(HttpStatus.OK_200, response.getStatus());
 
             String content = new String(response.getContent());
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootCore.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootCore.java
index e6f7f1d..295dac1 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootCore.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootCore.java
@@ -18,14 +18,9 @@
 
 package org.eclipse.jetty.osgi.test;
  
-import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
-import static org.ops4j.pax.exam.CoreOptions.options;
-import static org.ops4j.pax.exam.CoreOptions.systemProperty;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-
 import javax.inject.Inject;
 
 import org.junit.Ignore;
@@ -39,6 +34,10 @@
 import org.ops4j.pax.exam.options.MavenUrlReference.VersionResolver;
 import org.osgi.framework.BundleContext;
 
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
 
 /**
  * Default OSGi setup integration test
@@ -47,8 +46,17 @@
 public class TestJettyOSGiBootCore
 {
     private static final String LOG_LEVEL = "WARN";
-    public static int DEFAULT_JETTY_HTTP_PORT = 9876;
+    
+    public static final int DEFAULT_HTTP_PORT=TestOSGiUtil.findFreePort("jetty.http.port");
+    public static final int DEFAULT_SSL_PORT=TestOSGiUtil.findFreePort("jetty.ssl.port");
 
+    static
+    {
+        System.err.println("DEFAULT_HTTP_PORT="+DEFAULT_HTTP_PORT);
+        System.err.println("DEFAULT_SSL_PORT="+DEFAULT_SSL_PORT);
+    }
+    
+    
     @Inject
     private BundleContext bundleContext;
 
@@ -71,7 +79,8 @@
     { 
         List<Option> res = new ArrayList<Option>();
         // get the jetty home config from the osgi boot bundle.
-        res.add(CoreOptions.systemProperty("jetty.port").value(String.valueOf(DEFAULT_JETTY_HTTP_PORT)));
+        res.add(CoreOptions.systemProperty("jetty.http.port").value(String.valueOf(DEFAULT_HTTP_PORT)));
+        res.add(CoreOptions.systemProperty("jetty.ssl.port").value(String.valueOf(DEFAULT_SSL_PORT)));
         res.add(CoreOptions.systemProperty("jetty.home.bundle").value("org.eclipse.jetty.osgi.boot"));
         res.addAll(coreJettyDependencies());
         return res;
@@ -81,7 +90,7 @@
     public static List<Option> coreJettyDependencies()
     {
         List<Option> res = new ArrayList<Option>();
-
+        
         res.add(mavenBundle().groupId( "org.ow2.asm" ).artifactId( "asm" ).versionAsInProject().start());
         res.add(mavenBundle().groupId( "org.ow2.asm" ).artifactId( "asm-commons" ).versionAsInProject().start());
         res.add(mavenBundle().groupId( "org.ow2.asm" ).artifactId( "asm-tree" ).versionAsInProject().start());
@@ -93,10 +102,10 @@
         res.add(mavenBundle().groupId( "org.apache.geronimo.specs" ).artifactId( "geronimo-jta_1.1_spec" ).version("1.1.1").noStart());
         res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.mail.glassfish" ).version( "1.4.1.v201005082020" ).noStart());
 
+        res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-util" ).versionAsInProject().noStart());
         res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-deploy" ).versionAsInProject().noStart());
         res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-server" ).versionAsInProject().noStart());  
         res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-servlet" ).versionAsInProject().noStart());  
-        res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-util" ).versionAsInProject().noStart());
         res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-http" ).versionAsInProject().noStart());
         res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-xml" ).versionAsInProject().noStart());
         res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-webapp" ).versionAsInProject().noStart());
@@ -141,7 +150,7 @@
         res.add(mavenBundle().groupId("org.mortbay.jasper").artifactId("apache-jsp").versionAsInProject());
         res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("apache-jsp").versionAsInProject());
         res.add(mavenBundle().groupId("org.glassfish.web").artifactId("javax.servlet.jsp.jstl").versionAsInProject());
-        res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.eclipse.jdt.core").versionAsInProject());
+        res.add(mavenBundle().groupId("org.eclipse.jdt.core.compiler").artifactId("ecj").versionAsInProject());
         res.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("jetty-osgi-boot-jsp").versionAsInProject().noStart());
         return res;
     }
@@ -170,6 +179,7 @@
     @Test
     public void testHttpService() throws Exception
     {
-        TestOSGiUtil.testHttpServiceGreetings(bundleContext, "http", DEFAULT_JETTY_HTTP_PORT);
+        TestOSGiUtil.testHttpServiceGreetings(bundleContext, "http", DEFAULT_HTTP_PORT);
     }
+
 }
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootHTTP2.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootHTTP2.java
new file mode 100644
index 0000000..ccb50b4
--- /dev/null
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootHTTP2.java
@@ -0,0 +1,114 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.osgi.test;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import javax.inject.Inject;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.osgi.framework.BundleContext;
+
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+/**
+ * HTTP2 setup.
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class TestJettyOSGiBootHTTP2
+{
+    private static final String LOG_LEVEL = "WARN";
+
+
+    @Inject
+    private BundleContext bundleContext;
+
+    @Configuration
+    public Option[] config()
+    {
+        ArrayList<Option> options = new ArrayList<Option>();
+        options.addAll(TestJettyOSGiBootWithJsp.configureJettyHomeAndPort(true,"jetty-http2.xml"));
+        options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
+        options.addAll(http2JettyDependencies());
+        options.add(CoreOptions.junitBundles());
+        options.addAll(TestJettyOSGiBootCore.httpServiceJetty());
+        options.add(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL));
+        options.add(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL));
+        return options.toArray(new Option[options.size()]);
+    }
+
+    public static List<Option> http2JettyDependencies()
+    {
+        List<Option> res = new ArrayList<Option>();
+        res.add(CoreOptions.systemProperty("jetty.http.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_HTTP_PORT)));
+        res.add(CoreOptions.systemProperty("jetty.ssl.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_SSL_PORT)));
+
+        String alpnBoot = System.getProperty("mortbay-alpn-boot");
+        if (alpnBoot == null) { throw new IllegalStateException("Define path to alpn boot jar as system property -Dmortbay-alpn-boot"); }
+        File checkALPNBoot = new File(alpnBoot);
+        if (!checkALPNBoot.exists()) { throw new IllegalStateException("Unable to find the alpn boot jar here: " + alpnBoot); }
+
+        res.add(CoreOptions.vmOptions("-Xbootclasspath/p:" + checkALPNBoot.getAbsolutePath()));
+
+        res.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("jetty-osgi-alpn").versionAsInProject().noStart());
+        res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-alpn-server").versionAsInProject().start());
+
+        res.add(mavenBundle().groupId("org.eclipse.jetty.http2").artifactId("http2-common").versionAsInProject().noStart());
+        res.add(mavenBundle().groupId("org.eclipse.jetty.http2").artifactId("http2-hpack").versionAsInProject().noStart());
+        res.add(mavenBundle().groupId("org.eclipse.jetty.http2").artifactId("http2-server").versionAsInProject().noStart());
+        return res;
+    }
+ 
+    @Test
+    public void checkALPNBootOnBootstrapClasspath() throws Exception
+    {
+        Class<?> alpn = Thread.currentThread().getContextClassLoader().loadClass("org.eclipse.jetty.alpn.ALPN");
+        Assert.assertNotNull(alpn);
+        Assert.assertNull(alpn.getClassLoader());
+    }
+    
+    @Ignore
+    @Test
+    public void assertAllBundlesActiveOrResolved() throws Exception
+    {
+        TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
+    }
+
+    
+    @Test
+    public void testHTTP2OnHttpService() throws Exception
+    {
+        TestOSGiUtil.testHttpServiceGreetings(bundleContext, "https", TestJettyOSGiBootCore.DEFAULT_SSL_PORT);
+    }
+
+}
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootSpdy.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootSpdy.java
deleted file mode 100644
index 107581f..0000000
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootSpdy.java
+++ /dev/null
@@ -1,120 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.osgi.test;
-
-import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
-import static org.ops4j.pax.exam.CoreOptions.systemProperty;
-import static org.ops4j.pax.exam.CoreOptions.options;
-
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import javax.inject.Inject;
-
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.ops4j.pax.exam.CoreOptions;
-import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.Configuration;
-import org.ops4j.pax.exam.junit.PaxExam;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-
-/**
- * SPDY setup.
- */
-@RunWith(PaxExam.class)
-public class TestJettyOSGiBootSpdy
-{
-    private static final String LOG_LEVEL = "WARN";
-    
-    private static final String JETTY_SPDY_PORT = "jetty.spdy.port";
-
-    private static final int DEFAULT_JETTY_SPDY_PORT = 9877;
-
-    @Inject
-    private BundleContext bundleContext;
-
-    @Configuration
-    public Option[] config()
-    {
-        ArrayList<Option> options = new ArrayList<Option>();
-        options.addAll(TestJettyOSGiBootWithJsp.configureJettyHomeAndPort("jetty-spdy.xml"));
-        options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
-        options.addAll(spdyJettyDependencies());
-        options.add(CoreOptions.junitBundles());
-        options.addAll(TestJettyOSGiBootCore.httpServiceJetty());
-        options.addAll(Arrays.asList(options(systemProperty("pax.exam.logging").value("none"))));
-        options.addAll(Arrays.asList(options(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL))));
-        options.addAll(Arrays.asList(options(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL))));
-        return options.toArray(new Option[options.size()]);
-    }
-
-    public static List<Option> spdyJettyDependencies()
-    {
-        List<Option> res = new ArrayList<Option>();
-        res.add(CoreOptions.systemProperty(JETTY_SPDY_PORT).value(String.valueOf(DEFAULT_JETTY_SPDY_PORT)));
-
-        String alpnBoot = System.getProperty("mortbay-alpn-boot");
-        if (alpnBoot == null) { throw new IllegalStateException("Define path to alpn boot jar as system property -Dmortbay-alpn-boot"); }
-        File checkALPNBoot = new File(alpnBoot);
-        if (!checkALPNBoot.exists()) { throw new IllegalStateException("Unable to find the alpn boot jar here: " + alpnBoot); }
-
-
-        res.add(CoreOptions.vmOptions("-Xbootclasspath/p:" + alpnBoot));
-
-        res.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("jetty-osgi-alpn").versionAsInProject().noStart());
-        res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-alpn-server").versionAsInProject().start());
-
-        res.add(mavenBundle().groupId("org.eclipse.jetty.spdy").artifactId("spdy-client").versionAsInProject().noStart());
-        res.add(mavenBundle().groupId("org.eclipse.jetty.spdy").artifactId("spdy-core").versionAsInProject().noStart());
-        res.add(mavenBundle().groupId("org.eclipse.jetty.spdy").artifactId("spdy-server").versionAsInProject().noStart());
-        res.add(mavenBundle().groupId("org.eclipse.jetty.spdy").artifactId("spdy-http-common").versionAsInProject().noStart());
-        res.add(mavenBundle().groupId("org.eclipse.jetty.spdy").artifactId("spdy-http-server").versionAsInProject().noStart());
-        return res;
-    }
-
-    @Test
-    public void checkALPNBootOnBootstrapClasspath() throws Exception
-    {
-        Class<?> alpn = Thread.currentThread().getContextClassLoader().loadClass("org.eclipse.jetty.alpn.ALPN");
-        Assert.assertNotNull(alpn);
-        Assert.assertNull(alpn.getClassLoader());
-    }
-
-    @Ignore
-    @Test
-    public void assertAllBundlesActiveOrResolved()
-    {
-        TestOSGiUtil.debugBundles(bundleContext);
-        TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
-    }
-
-    @Test
-    public void testSpdyOnHttpService() throws Exception
-    {
-        TestOSGiUtil.testHttpServiceGreetings(bundleContext, "https", DEFAULT_JETTY_SPDY_PORT);
-    }
-
-}
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWebAppAsService.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWebAppAsService.java
index bb49333..a1e8c43 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWebAppAsService.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWebAppAsService.java
@@ -22,12 +22,10 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
-import static org.ops4j.pax.exam.CoreOptions.options;
 import static org.ops4j.pax.exam.CoreOptions.systemProperty;
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 import javax.inject.Inject;
@@ -41,20 +39,20 @@
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
 import org.ops4j.pax.exam.CoreOptions;
 import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.Configuration;
 import org.ops4j.pax.exam.junit.PaxExam;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 
 /**
  * TestJettyOSGiBootWebAppAsService
- * 
+ *
  * Tests deployment of a WebAppContext as an osgi Service.
- * 
+ *
  * Tests the ServiceWebAppProvider.
- * 
+ *
  * Pax-Exam to make sure the jetty-osgi-boot can be started along with the
  * httpservice web-bundle. Then make sure we can deploy an OSGi service on the
  * top of this.
@@ -72,16 +70,15 @@
     {
         ArrayList<Option> options = new ArrayList<Option>();
         options.add(CoreOptions.junitBundles());
-        options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
+        options.addAll(configureJettyHomeAndPort("jetty-http.xml"));
         options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*"));
         options.add(CoreOptions.systemPackages("com.sun.org.apache.xalan.internal.res","com.sun.org.apache.xml.internal.utils",
                                                "com.sun.org.apache.xml.internal.utils", "com.sun.org.apache.xpath.internal",
                                                "com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects"));
      
         options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
-        options.addAll(Arrays.asList(options(systemProperty("pax.exam.logging").value("none"))));
-        options.addAll(Arrays.asList(options(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL))));
-        options.addAll(Arrays.asList(options(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL))));
+        options.add(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL));
+        options.add(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL));
 
         options.addAll(jspDependencies());
         return options.toArray(new Option[options.size()]);
@@ -90,20 +87,21 @@
 
     public static List<Option> configureJettyHomeAndPort(String jettySelectorFileName)
     {
-        File etcFolder = new File("src/test/config/etc");
-        String etc = "file://" + etcFolder.getAbsolutePath();
+        File etc = new File("src/test/config/etc");
+        StringBuffer xmlConfigs = new StringBuffer();
+        xmlConfigs.append(new File(etc, "jetty.xml").toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc, jettySelectorFileName).toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc, "jetty-deployer.xml").toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc, "jetty-testrealm.xml").toURI());
+        
         List<Option> options = new ArrayList<Option>();
-        options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS).value(etc     + "/jetty.xml;"
-                + etc
-                + "/"
-                + jettySelectorFileName
-                + ";"
-                + etc
-                + "/jetty-deployer.xml;"
-                + etc
-                + "/jetty-testrealm.xml"));
-        options.add(systemProperty("jetty.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT)));
-        options.add(systemProperty("jetty.home").value(etcFolder.getParentFile().getAbsolutePath()));
+        options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS).value(xmlConfigs.toString()));
+        options.add(systemProperty("jetty.http.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_HTTP_PORT)));
+        options.add(systemProperty("jetty.ssl.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_SSL_PORT)));
+        options.add(systemProperty("jetty.home").value(etc.getParentFile().getAbsolutePath()));
         return options;
     }
 
@@ -137,11 +135,16 @@
         {
             client.start();
 
-            ContentResponse response = client.GET("http://127.0.0.1:" + TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT + "/acme/index.html");
+            ContentResponse response = client.GET("http://127.0.0.1:" + TestJettyOSGiBootCore.DEFAULT_HTTP_PORT + "/acme/index.html");
             assertEquals(HttpStatus.OK_200, response.getStatus());
 
             String content = new String(response.getContent());
             assertTrue(content.indexOf("<h1>Test OSGi WebApp</h1>") != -1);
+            
+            response = client.GET("http://127.0.0.1:" + TestJettyOSGiBootCore.DEFAULT_HTTP_PORT + "/acme/mime");
+            assertEquals(HttpStatus.OK_200, response.getStatus());
+            content = new String(response.getContent());
+            assertTrue(content.indexOf("MIMETYPE=application/gzip") != -1);
         }
         finally
         {
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithAnnotations.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithAnnotations.java
index b1e4f66..f8df69d 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithAnnotations.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithAnnotations.java
@@ -21,12 +21,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
-import static org.ops4j.pax.exam.CoreOptions.options;
 import static org.ops4j.pax.exam.CoreOptions.systemProperty;
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 import javax.inject.Inject;
@@ -62,47 +60,46 @@
     @Configuration
     public static Option[] configure()
     {
-
         ArrayList<Option> options = new ArrayList<Option>();
         options.add(CoreOptions.junitBundles());
-        options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
+        options.addAll(configureJettyHomeAndPort("jetty-http.xml"));
         options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.sql.*","javax.xml.*", "javax.activation.*"));
         options.add(CoreOptions.systemPackages("com.sun.org.apache.xalan.internal.res","com.sun.org.apache.xml.internal.utils",
                                                "com.sun.org.apache.xml.internal.utils", "com.sun.org.apache.xpath.internal",
                                                "com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects"));
      
         options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
-        options.addAll(Arrays.asList(options(systemProperty("pax.exam.logging").value("none"))));
-        options.addAll(Arrays.asList(options(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL))));
-        options.addAll(Arrays.asList(options(systemProperty("org.eclipse.jetty.annotations.LEVEL").value(LOG_LEVEL))));
-        //options.addAll(TestJettyOSGiBootCore.consoleDependencies());
+        options.add(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL));
+        options.add(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL));
+        // options.addAll(TestJettyOSGiBootCore.consoleDependencies());
         options.addAll(jspDependencies());
         options.addAll(annotationDependencies());
+        options.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("test-jetty-osgi-fragment").versionAsInProject().noStart());
         return options.toArray(new Option[options.size()]);
     }
 
     public static List<Option> configureJettyHomeAndPort(String jettySelectorFileName)
     {
-        File etcFolder = new File("src/test/config/etc");
-        String etc = "file://" + etcFolder.getAbsolutePath();
+        File etc = new File("src/test/config/etc");
+      
         List<Option> options = new ArrayList<Option>();
-        String xmlConfigs = etc     + "/jetty.xml;"
-                + etc
-                + "/"
-                + jettySelectorFileName
-                + ";"
-                + etc
-                + "/jetty-ssl.xml;"
-                + etc
-                + "/jetty-https.xml;"
-                + etc
-                + "/jetty-deployer.xml;"
-                + etc
-                + "/jetty-testrealm.xml";
-
-        options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS).value(xmlConfigs));
-        options.add(systemProperty("jetty.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT)));
-        options.add(systemProperty("jetty.home").value(etcFolder.getParentFile().getAbsolutePath()));
+        StringBuffer xmlConfigs = new StringBuffer();
+        xmlConfigs.append(new File(etc, "jetty.xml").toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc,jettySelectorFileName).toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc, "jetty-ssl.xml").toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc, "jetty-https.xml").toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc, "jetty-deployer.xml").toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc, "jetty-testrealm.xml").toURI());
+        
+        options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS).value(xmlConfigs.toString()));
+        options.add(systemProperty("jetty.http.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_HTTP_PORT)));
+        options.add(systemProperty("jetty.ssl.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_SSL_PORT)));
+        options.add(systemProperty("jetty.home").value(etc.getParentFile().getAbsolutePath()));
         return options;
     }
 
@@ -120,13 +117,14 @@
         res.add(mavenBundle().groupId("org.eclipse.jetty.tests").artifactId("test-spec-webapp").classifier("webbundle").versionAsInProject());
         return res;
     }
-    
+
 
     @Ignore
     @Test
     public void assertAllBundlesActiveOrResolved()
     {
         TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
+        TestOSGiUtil.debugBundles(bundleContext);
     }
 
     // at the moment can't run httpservice with jsp at the same time.
@@ -135,7 +133,7 @@
     @Test
     public void testHttpService() throws Exception
     {
-        TestOSGiUtil.testHttpServiceGreetings(bundleContext, "http", TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT);
+        TestOSGiUtil.testHttpServiceGreetings(bundleContext, "http", TestJettyOSGiBootCore.DEFAULT_HTTP_PORT);
     }
 
  
@@ -143,20 +141,25 @@
     @Test
     public void testIndex() throws Exception
     {        
+        // TestOSGiUtil.debugBundles(bundleContext);
         HttpClient client = new HttpClient();
         try
         {
             client.start();
-            ContentResponse response = client.GET("http://127.0.0.1:" + TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT + "/index.html");
+            ContentResponse response = client.GET("http://127.0.0.1:" + TestJettyOSGiBootCore.DEFAULT_HTTP_PORT + "/index.html");
             assertEquals(HttpStatus.OK_200, response.getStatus());
 
             String content = new String(response.getContent());
             assertTrue(content.contains("<h1>Servlet 3.1 Test WebApp</h1>"));
             
-            Request req = client.POST("http://127.0.0.1:" + TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT + "/test");
+            Request req = client.POST("http://127.0.0.1:" + TestJettyOSGiBootCore.DEFAULT_HTTP_PORT + "/test");
             response = req.send();
             content = new String(response.getContent());
             assertTrue(content.contains("<p><b>Result: <span class=\"pass\">PASS</span></p>"));
+            
+            response = client.GET("http://127.0.0.1:" + TestJettyOSGiBootCore.DEFAULT_HTTP_PORT + "/frag.html");
+            content = new String(response.getContent());
+            assertTrue(content.contains("<h1>FRAGMENT</h1>"));
         }
         finally
         {
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithJsp.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithJsp.java
index f482371..7177a89 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithJsp.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithJsp.java
@@ -21,12 +21,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
-import static org.ops4j.pax.exam.CoreOptions.options;
 import static org.ops4j.pax.exam.CoreOptions.systemProperty;
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 import javax.inject.Inject;
@@ -35,6 +33,7 @@
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
+import org.eclipse.jetty.toolchain.test.OS;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,7 +51,7 @@
 @RunWith(PaxExam.class)
 public class TestJettyOSGiBootWithJsp
 {
-    private static final String LOG_LEVEL = "INFO";
+    private static final String LOG_LEVEL = "WARN";
 
     @Inject
     BundleContext bundleContext = null;
@@ -60,46 +59,49 @@
     @Configuration
     public static Option[] configure()
     {
-
         ArrayList<Option> options = new ArrayList<Option>();
         options.add(CoreOptions.junitBundles());
-        options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
+        options.addAll(configureJettyHomeAndPort(false,"jetty-http.xml"));
         options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*", "javax.activation.*"));
         options.add(CoreOptions.systemPackages("com.sun.org.apache.xalan.internal.res","com.sun.org.apache.xml.internal.utils",
                                                "com.sun.org.apache.xml.internal.utils", "com.sun.org.apache.xpath.internal",
                                                "com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects"));
      
         options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
-        options.addAll(Arrays.asList(options(systemProperty("pax.exam.logging").value("none"))));
-        options.addAll(Arrays.asList(options(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL))));
-        options.addAll(Arrays.asList(options(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL))));
-        options.addAll(Arrays.asList(options(systemProperty("org.eclipse.jetty.annotations.LEVEL").value("DEBUG"))));
+        options.add(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL));
+        options.add(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL));
         options.addAll(jspDependencies());
+        options.add(CoreOptions.cleanCaches(true));
         return options.toArray(new Option[options.size()]);
     }
 
-    public static List<Option> configureJettyHomeAndPort(String jettySelectorFileName)
+    public static List<Option> configureJettyHomeAndPort(boolean ssl,String jettySelectorFileName)
     {
-        File etcFolder = new File("src/test/config/etc");
-        String etc = "file://" + etcFolder.getAbsolutePath();
+        File etc = new File(OS.separators("src/test/config/etc"));
+        
         List<Option> options = new ArrayList<Option>();
-        String xmlConfigs = etc     + "/jetty.xml;"
-                + etc
-                + "/"
-                + jettySelectorFileName
-                + ";"
-                + etc
-                + "/jetty-ssl.xml;"
-                + etc
-                + "/jetty-https.xml;"
-                + etc
-                + "/jetty-deployer.xml;"
-                + etc
-                + "/jetty-testrealm.xml";
+        StringBuffer xmlConfigs = new StringBuffer();    
+        xmlConfigs.append(new File(etc, "jetty.xml").toURI());
+        xmlConfigs.append(";");
+        if (ssl)
+        {
+            xmlConfigs.append(new File(etc, "jetty-ssl.xml").toURI());
+            xmlConfigs.append(";");
+            xmlConfigs.append(new File(etc, "jetty-https.xml").toURI());
+            xmlConfigs.append(";");
 
-        options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS).value(xmlConfigs));
-        options.add(systemProperty("jetty.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT)));
-        options.add(systemProperty("jetty.home").value(etcFolder.getParentFile().getAbsolutePath()));
+        }
+        xmlConfigs.append(new File(etc, jettySelectorFileName).toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc, "jetty-deployer.xml").toURI());
+        xmlConfigs.append(";");
+        xmlConfigs.append(new File(etc, "jetty-testrealm.xml").toURI());
+
+        options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS).value(xmlConfigs.toString()));
+        options.add(systemProperty("jetty.http.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_HTTP_PORT)));
+        options.add(systemProperty("jetty.ssl.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_SSL_PORT)));
+        options.add(systemProperty("jetty.home").value(etc.getParentFile().getAbsolutePath()));
+        options.add(systemProperty("jetty.base").value(etc.getParentFile().getAbsolutePath()));
         return options;
     }
 
@@ -114,8 +116,8 @@
     }
 
 
-    @Test
     @Ignore
+    @Test
     public void assertAllBundlesActiveOrResolved()
     {
         TestOSGiUtil.debugBundles(bundleContext);
@@ -128,19 +130,20 @@
     @Test
     public void testHttpService() throws Exception
     {
-        TestOSGiUtil.testHttpServiceGreetings(bundleContext, "http", TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT);
+        TestOSGiUtil.testHttpServiceGreetings(bundleContext, "http", TestJettyOSGiBootCore.DEFAULT_HTTP_PORT);
     }
 
  
     @Test
     public void testJspDump() throws Exception
     {
-   
+        // TestOSGiUtil.debugBundles(bundleContext);
         HttpClient client = new HttpClient();
         try
         {
             client.start();
-            ContentResponse response = client.GET("http://127.0.0.1:" + TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT + "/jsp/jstl.jsp");
+            ContentResponse response = client.GET("http://127.0.0.1:" + TestJettyOSGiBootCore.DEFAULT_HTTP_PORT + "/jsp/jstl.jsp");
+            
             assertEquals(HttpStatus.OK_200, response.getStatus());
             String content = new String(response.getContent());
             assertTrue(content.contains("JSTL Example"));           
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java
index d022cbe..bd64e9e 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java
@@ -19,8 +19,8 @@
 package org.eclipse.jetty.osgi.test;
 
 import java.io.IOException;
+import java.net.ServerSocket;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 import javax.servlet.ServletException;
@@ -33,7 +33,6 @@
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.junit.Assert;
-import org.ops4j.pax.exam.Option;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
@@ -186,4 +185,41 @@
             client.stop();
         }
     }
+
+    public static int findFreePort(String systemProperty)
+    {
+        String freeport = System.getProperty(systemProperty);
+        if (freeport!=null)
+            return Integer.valueOf(freeport);
+        
+        try (ServerSocket socket = new ServerSocket(0))
+        {
+            socket.setReuseAddress(true);
+            int port = socket.getLocalPort();
+            System.setProperty(systemProperty,Integer.toString(port));
+            return port;
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    
+    public static void main(String... args)
+    {
+        int freeport = TestOSGiUtil.findFreePort("test");
+        System.err.println("Found Free port="+freeport);
+
+        
+        try (ServerSocket socket = new ServerSocket(TestOSGiUtil.findFreePort("test")))
+        {
+            System.err.println("reused port="+socket.getLocalPort());
+        }
+        catch (IOException e)
+        {
+            e.printStackTrace();
+        }
+        
+    }
 }
diff --git a/jetty-osgi/test-jetty-osgi/src/test/resources/log4j.properties b/jetty-osgi/test-jetty-osgi/src/test/resources/log4j.properties
index f9eff1f..58ffa5a 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/resources/log4j.properties
+++ b/jetty-osgi/test-jetty-osgi/src/test/resources/log4j.properties
@@ -1,6 +1,6 @@
 # LOG4J levels: OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL
 #
-log4j.rootLogger=ALL,CONSOLE
+log4j.rootLogger=DEBUG,CONSOLE
 
 log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
 #log4j.appender.CONSOLE.threshold=INFO
@@ -9,5 +9,8 @@
 log4j.appender.CONSOLE.layout.ConversionPattern=%d [%5p][%c] %m%n
 
 # Level tuning
-log4j.logger.org.eclipse.jetty=INFO
+# log4j.logger.org.eclipse.jetty=DEBUG
+# log4j.logger.org.eclipse.jetty.security=DEBUG
+log4j.logger.shaded.org.eclipse.aether=WARN
+log4j.logger.shaded.org.apache.http=WARN
 log4j.logger.org.ops4j=WARN
diff --git a/jetty-overlay-deployer/pom.xml b/jetty-overlay-deployer/pom.xml
index a531f46..9f0a73b 100644
--- a/jetty-overlay-deployer/pom.xml
+++ b/jetty-overlay-deployer/pom.xml
@@ -10,6 +10,7 @@
   <description>Overlayed deployer</description>
   <url>http://www.eclipse.org/jetty</url>
   <properties>
+    <bundle-symbolic-name>${project.groupId}.overlays</bundle-symbolic-name>
   </properties>
   <build>
     <plugins>
diff --git a/jetty-overlay-deployer/src/main/config/etc/jetty-overlay.xml b/jetty-overlay-deployer/src/main/config/etc/jetty-overlay.xml
index 62317a3..60f23d6 100644
--- a/jetty-overlay-deployer/src/main/config/etc/jetty-overlay.xml
+++ b/jetty-overlay-deployer/src/main/config/etc/jetty-overlay.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Add a ContextProvider to the deployment manager                 -->
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/WEB-INF/overlay.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/WEB-INF/overlay.xml
index 11c70ac..8d29a8a 100644
--- a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/WEB-INF/overlay.xml
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/WEB-INF/overlay.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
 
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/WEB-INF/overlay.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/WEB-INF/overlay.xml
index f6cfcf6..7a15e73 100644
--- a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/WEB-INF/overlay.xml
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/WEB-INF/overlay.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
 
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/WEB-INF/overlay.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/WEB-INF/overlay.xml
index 6a71455..90685db 100644
--- a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/WEB-INF/overlay.xml
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/WEB-INF/overlay.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
 
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/root=root/WEB-INF/overlay.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/root=root/WEB-INF/overlay.xml
index 287e1e0..ca23e18 100644
--- a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/root=root/WEB-INF/overlay.xml
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/root=root/WEB-INF/overlay.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure class="org.eclipse.jetty.server.handler.ContextHandler">
   <!--
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/WEB-INF/jetty-web.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/WEB-INF/jetty-web.xml
index 16cba2a..4723bf7 100644
--- a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/WEB-INF/jetty-web.xml
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/WEB-INF/jetty-web.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
   <Call class="org.eclipse.jetty.util.log.Log" name="info"><Arg>Executing jetty-web.xml for <Property name="overlay.instance"/></Arg></Call>
-</Configure>
\ No newline at end of file
+</Configure>
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/WEB-INF/template.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/WEB-INF/template.xml
index 3104fca..fddd63b 100644
--- a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/WEB-INF/template.xml
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/WEB-INF/template.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure class="org.eclipse.jetty.overlays.TemplateContext">
   <Set name="parentLoaderPriority" type="boolean">false</Set>
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/WEB-INF/web-default.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/WEB-INF/web-default.xml
index 22f82f4..d54d76f 100644
--- a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/WEB-INF/web-default.xml
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/WEB-INF/web-default.xml
@@ -1,43 +1,68 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<web-app 
+   xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
+   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+   metadata-complete="false"
+   version="3.1"> 
 
   <!-- ===================================================================== -->
   <!-- This file contains the default descriptor for web applications.       -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
   <!-- The intent of this descriptor is to include jetty specific or common  -->
   <!-- configuration for all webapps.   If a context has a webdefault.xml    -->
-  <!-- descriptor, it is applied before the contexts own web.xml file        -->
+  <!-- descriptor, it is applied before the context's own web.xml file       -->
   <!--                                                                       -->
-  <!-- A context may be assigned a default descriptor by:                    -->
-  <!--  + Calling WebApplicationContext.setDefaultsDescriptor                -->
-  <!--  + Passed an arg to addWebApplications                                -->
+  <!-- A context may be assigned a default descriptor by calling             -->
+  <!-- WebAppContext.setDefaultsDescriptor(String).                          -->
   <!--                                                                       -->
-  <!-- This file is used both as the resource within the jetty.jar (which is -->
-  <!-- used as the default if no explicit defaults descriptor is set) and it -->
-  <!-- is copied to the etc directory of the Jetty distro and explicitly     -->
-  <!-- by the jetty.xml file.                                                -->
+  <!-- This file is present in the jetty-webapp.jar, and is used as the      -->
+  <!-- defaults descriptor if no other is explicitly set on a context.       -->
   <!--                                                                       -->
+  <!-- A copy of this file is also placed into the $JETTY_HOME/etc dir of    -->
+  <!-- the  distribution, and is referenced by some of the other xml files,  -->
+  <!-- eg the jetty-deploy.xml file.                                         -->
   <!-- ===================================================================== -->
-<web-app
-  xmlns="http://java.sun.com/xml/ns/javaee"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
-  metadata-complete="true"
-  version="2.5"
->
 
   <description>
     Default web.xml file.  
     This file is applied to a Web application before it's own WEB_INF/web.xml file
   </description>
 
+  <!-- ==================================================================== -->
+  <!-- Removes static references to beans from javax.el.BeanELResolver to   -->
+  <!-- ensure webapp classloader can be released on undeploy                -->
+  <!-- ==================================================================== -->
+  <listener>
+   <listener-class>org.eclipse.jetty.servlet.listener.ELContextCleaner</listener-class>
+  </listener>
+  
+  <!-- ==================================================================== -->
+  <!-- Removes static cache of Methods from java.beans.Introspector to      -->
+  <!-- ensure webapp classloader can be released on undeploy                -->
+  <!-- ==================================================================== -->  
+  <listener>
+   <listener-class>org.eclipse.jetty.servlet.listener.IntrospectorCleaner</listener-class>
+  </listener>
+  
 
   <!-- ==================================================================== -->
   <!-- Context params to control Session Cookies                            -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <!--
-    UNCOMMENT TO ACTIVATE <context-param> <param-name>org.eclipse.jetty.servlet.SessionDomain</param-name> <param-value>127.0.0.1</param-value> </context-param> <context-param>
-    <param-name>org.eclipse.jetty.servlet.SessionPath</param-name> <param-value>/</param-value> </context-param> <context-param> <param-name>org.eclipse.jetty.servlet.MaxAge</param-name>
-    <param-value>-1</param-value> </context-param>
+    UNCOMMENT TO ACTIVATE 
+    <context-param> 
+      <param-name>org.eclipse.jetty.servlet.SessionDomain</param-name> 
+      <param-value>127.0.0.1</param-value> 
+    </context-param> 
+    <context-param>
+      <param-name>org.eclipse.jetty.servlet.SessionPath</param-name>
+      <param-value>/</param-value>
+    </context-param>
+    <context-param>
+      <param-name>org.eclipse.jetty.servlet.MaxAge</param-name>
+      <param-value>-1</param-value>
+    </context-param>
   -->
 
   <!-- ==================================================================== -->
@@ -71,33 +96,39 @@
  *
  *  resourceBase      Set to replace the context resource base
  *
- *  resourceCache     If set, this is a context attribute name, which the servlet 
- *                    will use to look for a shared ResourceCache instance. 
- *                        
+ *  resourceCache     If set, this is a context attribute name, which the servlet
+ *                    will use to look for a shared ResourceCache instance.
+ *
  *  relativeResourceBase
  *                    Set with a pathname relative to the base of the
  *                    servlet context root. Useful for only serving static content out
  *                    of only specific subdirectories.
  *
+ *  pathInfoOnly      If true, only the path info will be applied to the resourceBase
+ *
+ *  stylesheet        Set with the location of an optional stylesheet that will be used
+ *                    to decorate the directory listing html.
+ *
  *  aliases           If True, aliases of resources are allowed (eg. symbolic
  *                    links and caps variations). May bypass security constraints.
+ *                    
+ *  etags             If True, weak etags will be generated and handled.
  *
  *  maxCacheSize      The maximum total size of the cache or 0 for no cache.
  *  maxCachedFileSize The maximum size of a file to cache
  *  maxCachedFiles    The maximum number of files to cache
  *
  *  useFileMappedBuffer
- *                    If set to true, it will use mapped file buffer to serve static content
- *                    when using NIO connector. Setting this value to false means that
+ *                    If set to true, it will use mapped file buffers to serve static content
+ *                    when using an NIO connector. Setting this value to false means that
  *                    a direct buffer will be used instead of a mapped file buffer.
- *                    By default, this is set to true.
+ *                    This file sets the value to true.
  *
  *  cacheControl      If set, all static content will have this value set as the cache-control
  *                    header.
+ *
  -->
- 
- 
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <servlet>
     <servlet-name>default</servlet-name>
     <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
@@ -127,7 +158,7 @@
     </init-param>
     <init-param>
       <param-name>maxCachedFileSize</param-name>
-      <param-value>10000000</param-value>
+      <param-value>200000000</param-value>
     </init-param>
     <init-param>
       <param-name>maxCachedFiles</param-name>
@@ -135,16 +166,22 @@
     </init-param>
     <init-param>
       <param-name>gzip</param-name>
-      <param-value>true</param-value>
+      <param-value>false</param-value>
+    </init-param>
+    <init-param>
+      <param-name>etags</param-name>
+      <param-value>false</param-value>
     </init-param>
     <init-param>
       <param-name>useFileMappedBuffer</param-name>
       <param-value>true</param-value>
     </init-param>
+    <!--
     <init-param>
       <param-name>resourceCache</param-name>
-      <param-value>org.eclipse.jetty.server.ResourceCache</param-value>
+      <param-value>resourceCache</param-value>
     </init-param>
+    -->
     <!--
     <init-param>
       <param-name>cacheControl</param-name>
@@ -162,13 +199,13 @@
 
   <!-- ==================================================================== -->
   <!-- JSP Servlet                                                          -->
-  <!-- This is the jasper JSP servlet from the jakarta project              -->
+  <!-- This is the jasper JSP servlet.                                      -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <!-- The JSP page compiler and execution servlet, which is the mechanism  -->
-  <!-- used by Glassfish to support JSP pages.  Traditionally, this servlet -->
-  <!-- is mapped to URL patterh "*.jsp".  This servlet supports the         -->
-  <!-- following initialization parameters (default values are in square    -->
-  <!-- brackets):                                                           -->
+  <!-- used by the jsp container to support JSP pages.  Traditionally,      -->
+  <!-- this servlet is mapped to URL pattern "*.jsp".  This servlet         -->
+  <!-- supports the following initialization parameters (default values     -->
+  <!-- are in square brackets):                                             -->
   <!--                                                                      -->
   <!--   checkInterval       If development is false and reloading is true, -->
   <!--                       background compiles are enabled. checkInterval -->
@@ -176,7 +213,7 @@
   <!--                       if a JSP page needs to be recompiled. [300]    -->
   <!--                                                                      -->
   <!--   compiler            Which compiler Ant should use to compile JSP   -->
-  <!--                       pages.  See the Ant documenation for more      -->
+  <!--                       pages.  See the Ant documentation for more     -->
   <!--                       information. [javac]                           -->
   <!--                                                                      -->
   <!--   classdebuginfo      Should the class file be compiled with         -->
@@ -237,19 +274,10 @@
   <!--   xpoweredBy          Determines whether X-Powered-By response       -->
   <!--                       header is added by generated servlet  [false]  -->
   <!--                                                                      -->
-  <!-- If you wish to use Jikes to compile JSP pages:                       -->
-  <!--   Set the init parameter "compiler" to "jikes".  Define              -->
-  <!--   the property "-Dbuild.compiler.emacs=true" when starting Jetty     -->
-  <!--   to cause Jikes to emit error messages in a format compatible with  -->
-  <!--   Jasper.                                                            -->
-  <!--   If you get an error reporting that jikes can't use UTF-8 encoding, -->
-  <!--   try setting the init parameter "javaEncoding" to "ISO-8859-1".     -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
-  <servlet
-    id="jsp"
-  >
+  <servlet id="jsp">
     <servlet-name>jsp</servlet-name>
-    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+    <servlet-class>org.eclipse.jetty.jsp.JettyJspServlet</servlet-class>
     <init-param>
       <param-name>logVerbosityLevel</param-name>
       <param-value>DEBUG</param-value>
@@ -262,6 +290,14 @@
       <param-name>xpoweredBy</param-name>
       <param-value>false</param-value>
     </init-param>
+    <init-param>
+      <param-name>compilerTargetVM</param-name>
+      <param-value>1.7</param-value>
+    </init-param>
+    <init-param>
+      <param-name>compilerSourceVM</param-name>
+      <param-value>1.7</param-value>
+    </init-param>
     <!--  
     <init-param>
         <param-name>classpath</param-name>
@@ -285,6 +321,8 @@
 
 
   <!-- ==================================================================== -->
+  <!-- Default session configuration                                        -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <session-config>
     <session-timeout>30</session-timeout>
   </session-config>
@@ -292,7 +330,7 @@
   <!-- ==================================================================== -->
   <!-- Default MIME mappings                                                -->
   <!-- The default MIME mappings are provided by the mime.properties        -->
-  <!-- resource in the org.eclipse.jetty.server.jar file.  Additional or modified  -->
+  <!-- resource in the jetty-http.jar file.  Additional or modified         -->
   <!-- mappings may be specified here                                       -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <!-- UNCOMMENT TO ACTIVATE
@@ -303,13 +341,17 @@
   -->
 
   <!-- ==================================================================== -->
+  <!-- Default welcome files                                                -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <welcome-file-list>
-    <welcome-file>index.jsp</welcome-file>
     <welcome-file>index.html</welcome-file>
     <welcome-file>index.htm</welcome-file>
+    <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>
 
   <!-- ==================================================================== -->
+  <!-- Default locale encodings                                             -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <locale-encoding-mapping-list>
     <locale-encoding-mapping>
       <locale>ar</locale>
@@ -469,6 +511,9 @@
     </locale-encoding-mapping>
   </locale-encoding-mapping-list>
 
+  <!-- ==================================================================== -->
+  <!-- Disable TRACE method with security constraint                        -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <security-constraint>
     <web-resource-collection>
       <web-resource-name>Disable TRACE</web-resource-name>
@@ -477,6 +522,13 @@
     </web-resource-collection>
     <auth-constraint/>
   </security-constraint>
+  <security-constraint>
+    <web-resource-collection>
+      <web-resource-name>Enable everything but TRACE</web-resource-name>
+      <url-pattern>/</url-pattern>
+      <http-method-omission>TRACE</http-method-omission>
+    </web-resource-collection>
+  </security-constraint>
 
 </web-app>
 
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/WEB-INF/overlay.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/WEB-INF/overlay.xml
index 813ca58..f3037a1 100644
--- a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/WEB-INF/overlay.xml
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/WEB-INF/overlay.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.eclipse.org/configure.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.eclipse.org/configure_9_3.dtd">
 
 
 <Configure class="org.eclipse.jetty.server.handler.ContextHandler">
diff --git a/jetty-plus/pom.xml b/jetty-plus/pom.xml
index 6570135..41de199 100644
--- a/jetty-plus/pom.xml
+++ b/jetty-plus/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-plus</artifactId>
@@ -14,59 +14,6 @@
   </properties>
   <build>
     <plugins>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-               <_nouses>true</_nouses>
-               <!-- Export-Package>
-                 org.eclipse.jetty.plus.annotation;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}",
-                 org.eclipse.jetty.plus.webapp;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}",
-                 org.eclipse.jetty.plus.jndi;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}",
-                 org.eclipse.jetty.plus.security;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"
-               </Export-Package -->
-               <Import-Package>javax.sql.*,javax.security.*,javax.naming.*,
-               javax.servlet.*;version="[2.6.0,3.2)",javax.transaction.*;version="[1.1,1.3)",
-               *
-               </Import-Package>
-              </instructions>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
       <!-- always include the sources to be able to prepare the eclipse-jetty-SDK feature
       with a snapshot. -->
       <plugin>
diff --git a/jetty-plus/src/main/config/etc/jetty-plus.xml b/jetty-plus/src/main/config/etc/jetty-plus.xml
index bfbcce5..ed30824 100644
--- a/jetty-plus/src/main/config/etc/jetty-plus.xml
+++ b/jetty-plus/src/main/config/etc/jetty-plus.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Configure extended support for webapps                          -->
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java
index f38e734..e32e45e 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java
@@ -91,7 +91,7 @@
     /**
      * A class has been found that has an annotation of interest
      * to this initializer.
-     * @param className
+     * @param className the class name to add
      */
     public void addAnnotatedTypeName (String className)
     {
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/Injection.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/Injection.java
index d67ac28..f33b046 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/Injection.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/Injection.java
@@ -32,11 +32,10 @@
 
 /**
  * Injection
- *
+ * <p>
  * Represents the injection of a resource into a target (method or field).
  * The injection is performed by doing an ENC lookup using the jndi
  * name provided, and setting the object obtained on the target.
- *
  */
 public class Injection
 {
@@ -168,7 +167,7 @@
 
     /**
      * Inject a value for a Resource from JNDI into an object
-     * @param injectable
+     * @param injectable the object to inject 
      */
     public void inject (Object injectable)
     {
@@ -187,7 +186,7 @@
     /**
      * The Resource must already exist in the ENC of this webapp.
      * @return the injected valud
-     * @throws NamingException
+     * @throws NamingException if unable to lookup value
      */
     public Object lookupInjectedValue ()
     throws NamingException
@@ -200,8 +199,8 @@
 
     /**
      * Inject value from jndi into a field of an instance
-     * @param field
-     * @param injectable
+     * @param field the field to inject into
+     * @param injectable the value to inject
      */
     protected void injectField (Field field, Object injectable)
     {
@@ -221,8 +220,8 @@
 
     /**
      * Inject value from jndi into a setter method of an instance
-     * @param method
-     * @param injectable
+     * @param method the method to inject into
+     * @param injectable the value to inject
      */
     protected void injectMethod (Method method, Object injectable)
     {
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollection.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollection.java
index 77ceef2..750d48c 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollection.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollection.java
@@ -28,11 +28,8 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-
 /**
  * LifeCycleCallbackCollection
- *
- *
  */
 public class LifeCycleCallbackCollection
 {
@@ -46,7 +43,7 @@
     /**
      * Add a Callback to the list of callbacks.
      * 
-     * @param callback
+     * @param callback the callback
      */
     public void add (LifeCycleCallback callback)
     {
@@ -95,10 +92,10 @@
     }
     
     /**
-     * Call the method, if one exists, that is annotated with PostConstruct
-     * or with &lt;post-construct&gt; in web.xml
+     * Call the method, if one exists, that is annotated with <code>&#064;PostConstruct</code>
+     * or with <code>&lt;post-construct&gt;</code> in web.xml
      * @param o the object on which to attempt the callback
-     * @throws Exception
+     * @throws Exception if unable to call {@link PostConstructCallback}
      */
     public void callPostConstructCallback (Object o)
     throws Exception
@@ -120,9 +117,10 @@
 
     
     /**
-     * Call the method, if one exists, that is annotated with PreDestroy
-     * or with &lt;pre-destroy&gt; in web.xml
+     * Call the method, if one exists, that is annotated with <code>&#064;PreDestroy</code>
+     * or with <code>&lt;pre-destroy&gt;</code> in web.xml
      * @param o the object on which to attempt the callback
+     * @throws Exception if unable to call {@link PreDestroyCallback}
      */
     public void callPreDestroyCallback (Object o)
     throws Exception
@@ -141,7 +139,7 @@
     
     /**
      * Generate a read-only view of the post-construct callbacks
-     * @return
+     * @return the map of {@link PostConstructCallback}s
      */
     public Map<String, List<LifeCycleCallback>> getPostConstructCallbackMap()
     {
@@ -150,7 +148,7 @@
     
     /**
      * Generate a read-only view of the pre-destroy callbacks
-     * @return
+     * @return the map of {@link PreDestroyCallback}s
      */
     public Map<String, List<LifeCycleCallback>> getPreDestroyCallbackMap()
     {
@@ -159,7 +157,7 @@
     
     /**
      * Amalgamate all post-construct callbacks and return a read only list
-     * @return
+     * @return the collection of {@link PostConstructCallback}s
      */
     public Collection<LifeCycleCallback> getPostConstructCallbacks()
     {
@@ -173,7 +171,7 @@
     
     /**
      * Amalgamate all pre-destroy callbacks and return a read only list
-     * @return
+     * @return the collection of {@link PreDestroyCallback}s
      */
     public Collection<LifeCycleCallback> getPreDestroyCallbacks()
     {
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/RunAs.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/RunAs.java
index 9471248..bd15db8 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/RunAs.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/RunAs.java
@@ -22,8 +22,8 @@
 
 /**
  * RunAs
- * <p/>
- * Represents a &lt;run-as&gt; element in web.xml, or a runAs annotation.
+ * <p>
+ * Represents a <code>&lt;run-as&gt;</code> element in web.xml, or a <code>&#064;RunAs</code> annotation.
  */
 public class RunAs
 {
@@ -54,10 +54,6 @@
         return _roleName;
     }
 
-
-    /**
-     * @param holder
-     */
     public void setRunAs (ServletHolder holder)
     {
         if (holder == null)
@@ -70,6 +66,5 @@
             if (holder.getRegistration().getRunAsRole() == null)
                 holder.getRegistration().setRunAsRole(_roleName);
         }
-            
     }
 }
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/EnvEntry.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/EnvEntry.java
index 29fda4a..6c8a078 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/EnvEntry.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/EnvEntry.java
@@ -24,8 +24,6 @@
 
 /**
  * EnvEntry
- *
- *
  */
 public class EnvEntry extends NamingEntry
 {
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntry.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntry.java
index 3c68995..63250f5 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntry.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntry.java
@@ -28,15 +28,13 @@
 import org.eclipse.jetty.jndi.NamingUtil;
 import org.eclipse.jetty.util.log.Logger;
 
-
-
 /**
  * NamingEntry
- *
+ * <p>
  * Base class for all jndi related entities. Instances of
  * subclasses of this class are declared in jetty.xml or in a 
  * webapp's WEB-INF/jetty-env.xml file.
- *
+ * <p>
  * NOTE: that all NamingEntries will be bound in a single namespace.
  *  The "global" level is just in the top level context. The "local"
  *  level is a context specific to a webapp.
@@ -82,7 +80,7 @@
      * be linked to the webapp's env-entry, resource-ref etc entries.
      * 
      * @param jndiName the name of the object which will eventually be in java:comp/env
-     * @throws NamingException
+     * @throws NamingException if unable to create naming entry
      */
     protected NamingEntry (String jndiName)
     throws NamingException
@@ -94,14 +92,15 @@
  
     
     /**
-     * Add a java:comp/env binding for the object represented by this NamingEntry,
+     * Add a <code>java:comp/env</code> binding for the object represented by this NamingEntry,
      * but bind it as the name supplied
-     * @throws NamingException
+     * @param localName the local name to bind
+     * @throws NamingException if unable to bind
      */
     public void bindToENC(String localName)
     throws NamingException
     {
-        //TODO - check on the whole overriding/non-overriding thing
+        // TODO - check on the whole overriding/non-overriding thing
         InitialContext ic = new InitialContext();
         Context env = (Context)ic.lookup("java:comp/env");
         __log.debug("Binding java:comp/env/"+localName+" to "+_objectNameString);
@@ -169,22 +168,24 @@
     
     /**
      * Save the NamingEntry for later use.
-     * 
+     * <p>
      * Saving is done by binding the NamingEntry
      * itself, and the value it represents into
      * JNDI. In this way, we can link to the
      * value it represents later, but also
      * still retrieve the NamingEntry itself too.
-     * 
+     * <p>
      * The object is bound at the jndiName passed in.
      * This NamingEntry is bound at __/jndiName.
-     * 
+     * <p>
      * eg
-     * 
+     * <pre>
      * jdbc/foo    : DataSource
      * __/jdbc/foo : NamingEntry
+     * </pre>
      * 
-     * @throws NamingException
+     * @param object the object to save 
+     * @throws NamingException if unable to save
      */
     protected void save (Object object)
     throws NamingException
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntryUtil.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntryUtil.java
index 06844db..e4490e5 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntryUtil.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntryUtil.java
@@ -48,7 +48,8 @@
      * @param scope the scope of the lookup
      * @param asName the name to bind as
      * @param mappedName the name from the environment to link to asName
-     * @throws NamingException
+     * @return true if bind success, false if not bound
+     * @throws NamingException if unable to bind
      */
     public static boolean bindToENC (Object scope, String asName, String mappedName)
     throws NamingException
@@ -74,10 +75,10 @@
     /**
      * Find a NamingEntry in the given scope.
      *
-     * @param scope
-     * @param jndiName
+     * @param scope the object scope
+     * @param jndiName the jndi name
      * @return the naming entry for the given scope
-     * @throws NamingException
+     * @throws NamingException if unable to lookup naming entry
      */
     public static NamingEntry lookupNamingEntry (Object scope, String jndiName)
     throws NamingException
@@ -112,10 +113,10 @@
      * Get all NameEntries of a certain type in the given naming
      * environment scope (server-wide names or context-specific names)
      *
-     * @param scope
+     * @param scope the object scope
      * @param clazz the type of the entry
      * @return all NameEntries of a certain type in the given naming environment scope (server-wide names or context-specific names)
-     * @throws NamingException
+     * @throws NamingException if unable to lookup the naming entries
      */
     public static List<Object> lookupNamingEntries (Object scope, Class<?> clazz)
     throws NamingException
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/Resource.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/Resource.java
index c96d21e..b055bd8 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/Resource.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/Resource.java
@@ -18,19 +18,13 @@
 
 package org.eclipse.jetty.plus.jndi;
 
-
 import javax.naming.NamingException;
 
-
-
 /**
  * Resource
- *
- *
  */
 public class Resource extends NamingEntry
 {
-    
     public  Resource (Object scope, String jndiName, Object objToBind)
     throws NamingException
     {
@@ -38,15 +32,10 @@
         save(objToBind);
     }
     
-    /**
-     * @param jndiName
-     * @param objToBind
-     */
     public Resource (String jndiName, Object objToBind)
     throws NamingException
     {
         super(jndiName);
         save(objToBind);
     }
-
 }
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java
index f669759..291e8b7 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java
@@ -32,6 +32,7 @@
 import javax.naming.InitialContext;
 import javax.naming.NameNotFoundException;
 import javax.naming.NamingException;
+import javax.servlet.ServletRequest;
 import javax.sql.DataSource;
 
 import org.eclipse.jetty.plus.jndi.NamingEntryUtil;
@@ -45,9 +46,8 @@
 
 
 /**
- *
  * DataSourceUserRealm
- *
+ * <p>
  * Obtain user/password/role information from a database
  * via jndi DataSource.
  */
@@ -73,6 +73,27 @@
     private String _userSql;
     private String _roleSql;
     private boolean _createTables = false;
+    
+    
+    /**
+     * DBUser
+     */
+    public class DBUser extends KnownUser
+    {
+        private int _key;
+        
+        public DBUser(String name, Credential credential, int key)
+        {
+            super(name, credential);
+            _key = key;
+        }
+        
+        public int getKey ()
+        {
+            return _key;
+        }
+        
+    }
 
     /* ------------------------------------------------------------ */
     public DataSourceLoginService()
@@ -288,15 +309,15 @@
     /* ------------------------------------------------------------ */
     /** Load user's info from database.
      *
-     * @param userName
+     * @param userName the user name
      */
-    @Override
+    @Deprecated
     protected UserIdentity loadUser (String userName)
     {
         try
         {
             try (Connection connection = getConnection();
-                 PreparedStatement statement1 = connection.prepareStatement(_userSql))
+                    PreparedStatement statement1 = connection.prepareStatement(_userSql))
             {
                 statement1.setObject(1, userName);
                 try (ResultSet rs1 = statement1.executeQuery())
@@ -305,19 +326,20 @@
                     {
                         int key = rs1.getInt(_userTableKey);
                         String credentials = rs1.getString(_userTablePasswordField);
-                        List<String> roles = new ArrayList<String>();
-                        try (PreparedStatement statement2 = connection.prepareStatement(_roleSql))
-                        {
-                            statement2.setInt(1, key);
-                            try (ResultSet rs2 = statement2.executeQuery())
+                       
+                            List<String> roles = new ArrayList<String>();
+                            try (PreparedStatement statement2 = connection.prepareStatement(_roleSql))
                             {
-                                while (rs2.next())
+                                statement2.setInt(1, key);
+                                try (ResultSet rs2 = statement2.executeQuery())
                                 {
-                                    roles.add(rs2.getString(_roleTableRoleField));
+                                    while (rs2.next())
+                                    {
+                                        roles.add(rs2.getString(_roleTableRoleField));
+                                    }
                                 }
                             }
-                        }
-                        return putUser(userName, Credential.getCredential(credentials), roles.toArray(new String[roles.size()]));
+                            return putUser(userName,  Credential.getCredential(credentials), roles.toArray(new String[roles.size()]));
                     }
                 }
             }
@@ -334,10 +356,81 @@
     }
     
     
+    /** 
+     * @see org.eclipse.jetty.security.MappedLoginService#loadUserInfo(java.lang.String)
+     */
+    public KnownUser loadUserInfo (String username)
+    {
+        try
+        {
+            try (Connection connection = getConnection();
+                    PreparedStatement statement1 = connection.prepareStatement(_userSql))
+            {
+                statement1.setObject(1, username);
+                try (ResultSet rs1 = statement1.executeQuery())
+                {
+                    if (rs1.next())
+                    {
+                        int key = rs1.getInt(_userTableKey);
+                        String credentials = rs1.getString(_userTablePasswordField);
+                        
+                        return new DBUser(username, Credential.getCredential(credentials), key);
+                    }
+                }
+            }
+        }
+        catch (NamingException e)
+        {
+            LOG.warn("No datasource for "+_jndiName, e);
+        }
+        catch (SQLException e)
+        {
+            LOG.warn("Problem loading user info for "+username, e);
+        }
+        return null;
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.security.MappedLoginService#loadRoleInfo(org.eclipse.jetty.security.MappedLoginService.KnownUser)
+     */
+    public String[] loadRoleInfo (KnownUser user)
+    {
+        DBUser dbuser = (DBUser)user;
+
+        try
+        {
+            try (Connection connection = getConnection();
+                    PreparedStatement statement2 = connection.prepareStatement(_roleSql))
+            {
+
+                List<String> roles = new ArrayList<String>();
+
+                statement2.setInt(1, dbuser.getKey());
+                try (ResultSet rs2 = statement2.executeQuery())
+                {
+                    while (rs2.next())
+                    {
+                        roles.add(rs2.getString(_roleTableRoleField));
+                    }
+                    
+                    return roles.toArray(new String[roles.size()]);
+                }
+            }
+        }
+        catch (NamingException e)
+        {
+            LOG.warn("No datasource for "+_jndiName, e);
+        }
+        catch (SQLException e)
+        {
+            LOG.warn("Problem loading user info for "+user.getName(), e);
+        }
+        return null;
+    }
     
     /* ------------------------------------------------------------ */
     @Override
-    public UserIdentity login(String username, Object credentials)
+    public UserIdentity login(String username, Object credentials, ServletRequest request)
     {
         long now = System.currentTimeMillis();
         if (now - _lastPurge > _cacheMs || _cacheMs == 0)
@@ -346,7 +439,7 @@
             _lastPurge = now;
         }
  
-        return super.login(username,credentials);
+        return super.login(username,credentials, request);
     }
 
     /* ------------------------------------------------------------ */
@@ -355,7 +448,8 @@
      * necessary sql query strings based on the configured table
      * and column names.
      *
-     * @throws NamingException
+     * @throws NamingException if unable to init jndi
+     * @throws SQLException if unable to init database
      */
     public void initDb() throws NamingException, SQLException
     {
@@ -366,9 +460,9 @@
         InitialContext ic = new InitialContext();
         assert ic!=null;
 
-        //TODO Should we try webapp scope too?
+        // TODO Should we try webapp scope too?
 
-        //try finding the datasource in the Server scope
+        // try finding the datasource in the Server scope
         if (_server != null)
         {
             try
@@ -401,8 +495,6 @@
         prepareTables();
     }
 
-
-
     private void prepareTables()
     throws NamingException, SQLException
     {
@@ -503,12 +595,10 @@
         }
     }
 
-
     private Connection getConnection ()
     throws NamingException, SQLException
     {
         initDb();
         return _datasource.getConnection();
     }
-
 }
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/EnvConfiguration.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/EnvConfiguration.java
index f2e1fbb..5d9fd42 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/EnvConfiguration.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/EnvConfiguration.java
@@ -40,14 +40,13 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.webapp.AbstractConfiguration;
+import org.eclipse.jetty.webapp.Configuration;
 import org.eclipse.jetty.webapp.WebAppContext;
 import org.eclipse.jetty.xml.XmlConfiguration;
 
 
 /**
  * EnvConfiguration
- *
- *
  */
 public class EnvConfiguration extends AbstractConfiguration
 {
@@ -61,10 +60,6 @@
         this.jettyEnvXmlUrl = url;
     }
 
-    /**
-     * @see Configuration#configure(WebAppContext)
-     * @throws Exception
-     */
     @Override
     public void preConfigure (WebAppContext context) throws Exception
     {
@@ -72,9 +67,6 @@
         createEnvContext(context);
     }
 
-    /**
-     * @throws Exception
-     */
     @Override
     public void configure (WebAppContext context) throws Exception
     {
@@ -138,8 +130,7 @@
 
     /**
      * Remove jndi setup from start
-     * @see Configuration#deconfigure(WebAppContext)
-     * @throws Exception
+     * @throws Exception if unable to deconfigure
      */
     @Override
     public void deconfigure (WebAppContext context) throws Exception
@@ -179,8 +170,7 @@
 
     /**
      * Remove all jndi setup
-     * @see Configuration#deconfigure(WebAppContext)
-     * @throws Exception
+     * @throws Exception if unable to destroy
      */
     @Override
     public void destroy (WebAppContext context) throws Exception
@@ -207,7 +197,8 @@
      * web.xml file can potentially override them.
      *
      * We first bind EnvEntries declared in Server scope, then WebAppContext scope.
-     * @throws NamingException
+     * @param context the context to use for the object scope
+     * @throws NamingException if unable to bind env entries
      */
     public void bindEnvEntries (WebAppContext context)
     throws NamingException
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusConfiguration.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusConfiguration.java
index 01cfd5c..45cf899 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusConfiguration.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusConfiguration.java
@@ -48,13 +48,13 @@
     public void preConfigure (WebAppContext context)
     throws Exception
     {
-        context.addDecorator(new PlusDecorator(context));
+        context.getObjectFactory().addDecorator(new PlusDecorator(context));
     }
 
     @Override
     public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception
     {
-        context.addDecorator(new PlusDecorator(context));
+        context.getObjectFactory().addDecorator(new PlusDecorator(context));
     }
 
     @Override
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDecorator.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDecorator.java
index feac071..7e6c168 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDecorator.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDecorator.java
@@ -21,7 +21,7 @@
 import org.eclipse.jetty.plus.annotation.InjectionCollection;
 import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
 import org.eclipse.jetty.plus.annotation.RunAsCollection;
-import org.eclipse.jetty.servlet.ServletContextHandler.Decorator;
+import org.eclipse.jetty.util.Decorator;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.webapp.WebAppContext;
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessor.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessor.java
index 69b51f9..decf7a4 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessor.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessor.java
@@ -48,10 +48,7 @@
 
 /**
  * PlusDescriptorProcessor
- *
- *
  */
-
 public class PlusDescriptorProcessor extends IterativeDescriptorProcessor
 {
     private static final Logger LOG = Log.getLogger(PlusDescriptorProcessor.class);
@@ -109,14 +106,13 @@
     {
     }
 
-
-
-
     /**
      * JavaEE 5.4.1.3
-     *
-     * @param node
-     * @throws Exception
+     * 
+     * @param context the context 
+     * @param descriptor the descriptor 
+     * @param node the xml node
+     * @throws Exception if unable to process jndi bindings
      */
     public void visitEnvEntry (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     throws Exception
@@ -188,18 +184,20 @@
 
     /**
      * Common Annotations Spec section 2.3:
+     * <p>
      *  resource-ref is for:
-     *    - javax.sql.DataSource
-     *    - javax.jms.ConnectionFactory
-     *    - javax.jms.QueueConnectionFactory
-     *    - javax.jms.TopicConnectionFactory
-     *    - javax.mail.Session
-     *    - java.net.URL
-     *    - javax.resource.cci.ConnectionFactory
-     *    - org.omg.CORBA_2_3.ORB
-     *    - any other connection factory defined by a resource adapter
+     * <ul>
+     * <li>javax.sql.DataSource</li>
+     * <li>javax.jms.ConnectionFactory</li>
+     * <li>javax.jms.QueueConnectionFactory</li>
+     * <li>javax.jms.TopicConnectionFactory</li>
+     * <li>javax.mail.Session</li>
+     * <li>java.net.URL</li>
+     * <li>javax.resource.cci.ConnectionFactory</li>
+     * <li>org.omg.CORBA_2_3.ORB</li>
+     * <li>any other connection factory defined by a resource adapter</li>
+     * </ul>
      *
-     * TODO
      * If web.xml contains a resource-ref with injection targets, all resource-ref entries
      * of the same name are ignored in web fragments. If web.xml does not contain any
      * injection-targets, then they are merged from all the fragments.
@@ -211,8 +209,10 @@
      * a real resource in the environment. At the moment, we insist that the
      * jetty.xml file name of the resource has to be exactly the same as the
      * name in web.xml deployment descriptor, but it shouldn't have to be
-     *
+     * 
+     * <p>
      * Maintenance update 3.0a to spec:
+     * <p>
      *   Update Section 8.2.3.h.ii with the following -  If a resource reference
      *   element is specified in two fragments, while absent from the main web.xml,
      *   and all the attributes and child elements of the resource reference element
@@ -220,13 +220,15 @@
      *   It is considered an error if a resource reference element has the same name
      *   specified in two fragments, while absent from the main web.xml and the attributes
      *   and child elements are not identical in the two fragments. For example, if two
-     *   web fragments declare a <resource-ref> with the same <resource-ref-name> element
+     *   web fragments declare a <code>&lt;resource-ref&gt;</code> with the same <code>&lt;resource-ref-name&gt;</code> element
      *   but the type in one is specified as javax.sql.DataSource while the type in the
      *   other is that of a java mail resource, then an error must be reported and the
      *   application MUST fail to deploy.
-     *
-     * @param node
-     * @throws Exception
+     *   
+     * @param context the context  
+     * @param descriptor the descriptor
+     * @param node the xml node
+     * @throws Exception if unable to bind nodes, or load classes
      */
     public void visitResourceRef (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     throws Exception
@@ -338,13 +340,18 @@
 
     /**
      * Common Annotations Spec section 2.3:
-     *   resource-env-ref is for:
-     *     - javax.transaction.UserTransaction
-     *     - javax.resource.cci.InteractionSpec
-     *     - anything else that is not a connection factory
-     *
-     * @param node
-     * @throws Exception
+     * <p>
+     * resource-env-ref is for:
+     * <ul>
+     * <li>javax.transaction.UserTransaction</li>
+     * <li>javax.resource.cci.InteractionSpec</li>
+     * <li>anything else that is not a connection factory</li>
+     * </ul>
+     * 
+     * @param context the context 
+     * @param descriptor the descriptor 
+     * @param node the xml node
+     * @throws Exception if unable to load classes, or bind jndi entries
      */
     public void visitResourceEnvRef (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     throws Exception
@@ -438,11 +445,17 @@
 
     /**
      * Common Annotations Spec section 2.3:
-     *   message-destination-ref is for:
-     *     - javax.jms.Queue
-     *     - javax.jms.Topic
-     * @param node
-     * @throws Exception
+     * <p>
+     * message-destination-ref is for:
+     * <ul>
+     * <li>javax.jms.Queue</li>
+     * <li>javax.jms.Topic</li>
+     * </ul>
+     *     
+     * @param context the context 
+     * @param descriptor the descriptor 
+     * @param node the xml node
+     * @throws Exception if unable to load classes or bind jndi entries
      */
     public void visitMessageDestinationRef (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     throws Exception
@@ -536,7 +549,10 @@
      * are ignored. Otherwise, post-constructs from fragments are merged.
      * post-construct is the name of a class and method to call after all
      * resources have been setup but before the class is put into use
-     * @param node
+     * 
+     * @param context the context 
+     * @param descriptor the descriptor 
+     * @param node the xml node
      */
     public void visitPostConstruct(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
@@ -624,7 +640,10 @@
      *
      * pre-destroy is the name of a class and method to call just as
      * the instance is being destroyed
-     * @param node
+     * 
+     * @param context the context 
+     * @param descriptor the descriptor 
+     * @param node the xml node
      */
     public void visitPreDestroy(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
@@ -705,12 +724,13 @@
 
 
     /**
-     * Iterate over the &lt;injection-target&gt; entries for a node
-     *
-     * @param descriptor
-     * @param node
-     * @param jndiName
-     * @param valueClass
+     * Iterate over the <code>&lt;injection-target&gt;</code> entries for a node
+     * 
+     * @param context the context 
+     * @param descriptor the descriptor 
+     * @param node the xml node
+     * @param jndiName the jndi name
+     * @param valueClass the value class
      */
     public void addInjections (WebAppContext context, Descriptor descriptor, XmlParser.Node node, String jndiName, Class<?> valueClass)
     {
@@ -763,9 +783,9 @@
 
 
     /**
-     * @param name
-     * @param value
-     * @throws Exception
+     * @param name the jndi name
+     * @param value the value
+     * @throws Exception if unable to bind entry
      */
     public void bindEnvEntry(String name, Object value) throws Exception
     {
@@ -799,12 +819,14 @@
 
     /**
      * Bind a resource reference.
-     *
+     * <p>
      * If a resource reference with the same name is in a jetty-env.xml
      * file, it will already have been bound.
-     *
-     * @param name
-     * @throws Exception
+     * 
+     * @param context the context 
+     * @param name the jndi name
+     * @param typeClass the type class
+     * @throws Exception if unable to bind resource
      */
     public void bindResourceRef(WebAppContext context, String name, Class<?> typeClass)
     throws Exception
@@ -812,10 +834,6 @@
         bindEntry(context, name, typeClass);
     }
 
-    /**
-     * @param name
-     * @throws Exception
-     */
     public void bindResourceEnvRef(WebAppContext context, String name, Class<?> typeClass)
     throws Exception
     {
@@ -834,16 +852,17 @@
      * Bind a resource with the given name from web.xml of the given type
      * with a jndi resource from either the server or the webapp's naming
      * environment.
-     *
+     * <p>
      * As the servlet spec does not cover the mapping of names in web.xml with
      * names from the execution environment, jetty uses the concept of a Link, which is
      * a subclass of the NamingEntry class. A Link defines a mapping of a name
      * from web.xml with a name from the execution environment (ie either the server or the
      * webapp's naming environment).
-     *
+     * 
+     * @param context the context
      * @param name name of the resource from web.xml
-     * @param typeClass
-     * @throws Exception
+     * @param typeClass the type class
+     * @throws Exception the exception
      */
     protected void bindEntry (WebAppContext context, String name, Class<?> typeClass)
     throws Exception
diff --git a/jetty-plus/src/test/java/org/eclipse/jetty/plus/jndi/TestNamingEntries.java b/jetty-plus/src/test/java/org/eclipse/jetty/plus/jndi/TestNamingEntries.java
index 2af30e2..6e5bfdd 100644
--- a/jetty-plus/src/test/java/org/eclipse/jetty/plus/jndi/TestNamingEntries.java
+++ b/jetty-plus/src/test/java/org/eclipse/jetty/plus/jndi/TestNamingEntries.java
@@ -45,9 +45,6 @@
 import org.junit.Before;
 import org.junit.Test;
 
-/**
- *
- */
 public class TestNamingEntries
 {
     public class ScopeA
@@ -146,7 +143,7 @@
      * after each test we should scrape out any lingering bindings to prevent cross test pollution
      * as observed when running java 7
      *
-     * @throws Exception
+     * @throws Exception on test failure
      */
     @After
     public void after() throws Exception
diff --git a/jetty-plus/src/test/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessorTest.java b/jetty-plus/src/test/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessorTest.java
index 1f05b4e..6ac396c 100644
--- a/jetty-plus/src/test/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessorTest.java
+++ b/jetty-plus/src/test/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessorTest.java
@@ -19,12 +19,12 @@
 package org.eclipse.jetty.plus.webapp;
 
 
-import java.lang.reflect.InvocationTargetException;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.lang.reflect.InvocationTargetException;
 import java.net.URL;
 
 import javax.naming.Context;
@@ -42,8 +42,6 @@
 
 /**
  * PlusDescriptorProcessorTest
- *
- *
  */
 public class PlusDescriptorProcessorTest
 {
@@ -53,9 +51,7 @@
     protected FragmentDescriptor fragDescriptor3;
     protected FragmentDescriptor fragDescriptor4;
     protected WebAppContext context;
-    /**
-     * @throws java.lang.Exception
-     */
+    
     @Before
     public void setUp() throws Exception
     {
diff --git a/jetty-proxy/pom.xml b/jetty-proxy/pom.xml
index e2d362a..141695f 100644
--- a/jetty-proxy/pom.xml
+++ b/jetty-proxy/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-proxy</artifactId>
@@ -15,52 +15,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",*</Import-Package>
-              </instructions>
-            </configuration>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
@@ -98,6 +52,13 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-http</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.eclipse.jetty.toolchain</groupId>
       <artifactId>jetty-test-helper</artifactId>
       <scope>test</scope>
diff --git a/jetty-proxy/src/main/config/etc/jetty-proxy.xml b/jetty-proxy/src/main/config/etc/jetty-proxy.xml
index 0e96724..20ef318 100644
--- a/jetty-proxy/src/main/config/etc/jetty-proxy.xml
+++ b/jetty-proxy/src/main/config/etc/jetty-proxy.xml
@@ -1,35 +1,35 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
-    <Set name="handler">
-        <New class="org.eclipse.jetty.proxy.ConnectHandler">
-            <Set name="handler">
-                <New class="org.eclipse.jetty.servlet.ServletHandler">
-                    <Call id="proxyHolder" name="addServletWithMapping">
-                        <Arg><Property name="jetty.proxy.servletClass" default="org.eclipse.jetty.proxy.ProxyServlet"/></Arg>
-                        <Arg><Property name="jetty.proxy.servletMapping" default="/*"/></Arg>
-                        <Call name="setInitParameter">
-                            <Arg>maxThreads</Arg>
-                            <Arg><Property name="jetty.proxy.maxThreads" default="128" /></Arg>
-                        </Call>
-                        <Call name="setInitParameter">
-                            <Arg>maxConnections</Arg>
-                            <Arg><Property name="jetty.proxy.maxConnections" default="256" /></Arg>
-                        </Call>
-                        <Call name="setInitParameter">
-                            <Arg>idleTimeout</Arg>
-                            <Arg><Property name="jetty.proxy.idleTimeout" default="30000" /></Arg>
-                        </Call>
-                        <Call name="setInitParameter">
-                            <Arg>timeout</Arg>
-                            <Arg><Property name="jetty.proxy.timeout" default="60000" /></Arg>
-                        </Call>
-                    </Call>
-                </New>
-            </Set>
+  <Set name="handler">
+    <New class="org.eclipse.jetty.proxy.ConnectHandler">
+      <Set name="handler">
+        <New class="org.eclipse.jetty.servlet.ServletHandler">
+          <Call id="proxyHolder" name="addServletWithMapping">
+            <Arg><Property name="jetty.proxy.servletClass" default="org.eclipse.jetty.proxy.ProxyServlet"/></Arg>
+            <Arg><Property name="jetty.proxy.servletMapping" default="/*"/></Arg>
+            <Call name="setInitParameter">
+              <Arg>maxThreads</Arg>
+              <Arg><Property name="jetty.proxy.maxThreads" default="128" /></Arg>
+            </Call>
+            <Call name="setInitParameter">
+              <Arg>maxConnections</Arg>
+              <Arg><Property name="jetty.proxy.maxConnections" default="256" /></Arg>
+            </Call>
+            <Call name="setInitParameter">
+              <Arg>idleTimeout</Arg>
+              <Arg><Property name="jetty.proxy.idleTimeout" default="30000" /></Arg>
+            </Call>
+            <Call name="setInitParameter">
+              <Arg>timeout</Arg>
+              <Arg><Property name="jetty.proxy.timeout" default="60000" /></Arg>
+            </Call>
+          </Call>
         </New>
-    </Set>
+      </Set>
+    </New>
+  </Set>
 
 </Configure>
diff --git a/jetty-proxy/src/main/config/modules/proxy.mod b/jetty-proxy/src/main/config/modules/proxy.mod
index a879ae1..6b91f68 100644
--- a/jetty-proxy/src/main/config/modules/proxy.mod
+++ b/jetty-proxy/src/main/config/modules/proxy.mod
@@ -14,9 +14,9 @@
 
 [ini-template]
 ## Proxy Configuration
-#jetty.proxy.servletClass=org.eclipse.jetty.proxy.ProxyServlet
-#jetty.proxy.servletMapping=/*
-#jetty.proxy.maxThreads=128
-#jetty.proxy.maxConnections=256
-#jetty.proxy.idleTimeout=30000
-#jetty.proxy.timeout=60000
+# jetty.proxy.servletClass=org.eclipse.jetty.proxy.ProxyServlet
+# jetty.proxy.servletMapping=/*
+# jetty.proxy.maxThreads=128
+# jetty.proxy.maxConnections=256
+# jetty.proxy.idleTimeout=30000
+# jetty.proxy.timeout=60000
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java
index 763d963..09c5026 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java
@@ -18,7 +18,6 @@
 
 package org.eclipse.jetty.proxy;
 
-import java.io.IOException;
 import java.net.InetAddress;
 import java.net.URI;
 import java.net.UnknownHostException;
@@ -40,7 +39,9 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.client.ContinueProtocolHandler;
 import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.ProtocolHandlers;
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.client.api.Response;
 import org.eclipse.jetty.http.HttpField;
@@ -80,6 +81,7 @@
  */
 public abstract class AbstractProxyServlet extends HttpServlet
 {
+    protected static final String CLIENT_REQUEST_ATTRIBUTE = "org.eclipse.jetty.proxy.clientRequest";
     protected static final Set<String> HOP_HEADERS;
     static
     {
@@ -215,6 +217,7 @@
      * <p>Creates a {@link HttpClient} instance, configured with init parameters of this servlet.</p>
      * <p>The init parameters used to configure the {@link HttpClient} instance are:</p>
      * <table>
+     * <caption>Init Parameters</caption>
      * <thead>
      * <tr>
      * <th>init-param</th>
@@ -323,8 +326,10 @@
             // Content must not be decoded, otherwise the client gets confused.
             client.getContentDecoderFactories().clear();
 
-            // No protocol handlers, pass everything to the client.
-            client.getProtocolHandlers().clear();
+            // Pass traffic to the client, only intercept what's necessary.
+            ProtocolHandlers protocolHandlers = client.getProtocolHandlers();
+            protocolHandlers.clear();
+            protocolHandlers.put(new ProxyContinueProtocolHandler());
 
             return client;
         }
@@ -412,11 +417,11 @@
      * like {@link HttpServletResponse#sendError(int)}.</p>
      *
      * @param clientRequest the client request
-     * @param clientResponse the client response
+     * @param proxyResponse the client response
      */
-    protected void onProxyRewriteFailed(HttpServletRequest clientRequest, HttpServletResponse clientResponse)
+    protected void onProxyRewriteFailed(HttpServletRequest clientRequest, HttpServletResponse proxyResponse)
     {
-        clientResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        sendProxyResponseError(clientRequest, proxyResponse, HttpStatus.FORBIDDEN_403);
     }
 
     protected boolean hasContent(HttpServletRequest clientRequest)
@@ -426,6 +431,11 @@
                 clientRequest.getHeader(HttpHeader.TRANSFER_ENCODING.asString()) != null;
     }
 
+    protected boolean expects100Continue(HttpServletRequest request)
+    {
+        return HttpHeaderValue.CONTINUE.asString().equals(request.getHeader(HttpHeader.EXPECT.asString()));
+    }
+
     protected void copyRequestHeaders(HttpServletRequest clientRequest, Request proxyRequest)
     {
         // First clear possibly existing headers, as we are going to copy those from the client request.
@@ -548,8 +558,7 @@
             int status = failure instanceof TimeoutException ?
                     HttpStatus.REQUEST_TIMEOUT_408 :
                     HttpStatus.INTERNAL_SERVER_ERROR_500;
-            proxyResponse.setStatus(status);
-            clientRequest.getAsyncContext().complete();
+            sendProxyResponseError(clientRequest, proxyResponse, status);
         }
     }
 
@@ -623,10 +632,13 @@
             {
                 // Use Jetty specific behavior to close connection.
                 proxyResponse.sendError(-1);
-                AsyncContext asyncContext = clientRequest.getAsyncContext();
-                asyncContext.complete();
+                if (clientRequest.isAsyncStarted())
+                {
+                    AsyncContext asyncContext = clientRequest.getAsyncContext();
+                    asyncContext.complete();
+                }
             }
-            catch (IOException x)
+            catch (Throwable x)
             {
                 if (_log.isDebugEnabled())
                     _log.debug(getRequestId(clientRequest) + " could not close the connection", failure);
@@ -635,13 +647,13 @@
         else
         {
             proxyResponse.resetBuffer();
-            if (failure instanceof TimeoutException)
-                proxyResponse.setStatus(HttpServletResponse.SC_GATEWAY_TIMEOUT);
-            else
-                proxyResponse.setStatus(HttpServletResponse.SC_BAD_GATEWAY);
-            proxyResponse.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
-            AsyncContext asyncContext = clientRequest.getAsyncContext();
-            asyncContext.complete();
+            int status = failure instanceof TimeoutException ?
+                    HttpStatus.GATEWAY_TIMEOUT_504 :
+                    HttpStatus.BAD_GATEWAY_502;
+            int serverStatus = serverResponse == null ? status : serverResponse.getStatus();
+            if (expects100Continue(clientRequest) && serverStatus >= HttpStatus.OK_200)
+                status = serverStatus;
+            sendProxyResponseError(clientRequest, proxyResponse, status);
         }
     }
 
@@ -650,6 +662,20 @@
         return System.identityHashCode(clientRequest);
     }
 
+    protected void sendProxyResponseError(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, int status)
+    {
+        proxyResponse.setStatus(status);
+        proxyResponse.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
+        if (clientRequest.isAsyncStarted())
+            clientRequest.getAsyncContext().complete();
+    }
+
+    protected void onContinue(HttpServletRequest clientRequest, Request proxyRequest)
+    {
+        if (_log.isDebugEnabled())
+            _log.debug("{} handling 100 Continue", getRequestId(clientRequest));
+    }
+
     /**
      * <p>Utility class that implement transparent proxy functionalities.</p>
      * <p>Configuration parameters:</p>
@@ -728,4 +754,14 @@
             return rewrittenURI.toString();
         }
     }
+
+    class ProxyContinueProtocolHandler extends ContinueProtocolHandler
+    {
+        @Override
+        protected void onContinue(Request request)
+        {
+            HttpServletRequest clientRequest = (HttpServletRequest)request.getAttributes().get(CLIENT_REQUEST_ATTRIBUTE);
+            AbstractProxyServlet.this.onContinue(clientRequest, request);
+        }
+    }
 }
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java
index d1c882e..20c5cd3 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java
@@ -41,7 +41,6 @@
 
 import org.eclipse.jetty.client.ContentDecoder;
 import org.eclipse.jetty.client.GZIPContentDecoder;
-import org.eclipse.jetty.client.api.ContentProvider;
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.client.api.Response;
 import org.eclipse.jetty.client.api.Result;
@@ -66,9 +65,10 @@
  */
 public class AsyncMiddleManServlet extends AbstractProxyServlet
 {
-    private static final String PROXY_REQUEST_COMMITTED = AsyncMiddleManServlet.class.getName() + ".proxyRequestCommitted";
-    private static final String CLIENT_TRANSFORMER = AsyncMiddleManServlet.class.getName() + ".clientTransformer";
-    private static final String SERVER_TRANSFORMER = AsyncMiddleManServlet.class.getName() + ".serverTransformer";
+    private static final String PROXY_REQUEST_CONTENT_COMMITTED_ATTRIBUTE = AsyncMiddleManServlet.class.getName() + ".proxyRequestContentCommitted";
+    private static final String CLIENT_TRANSFORMER_ATTRIBUTE = AsyncMiddleManServlet.class.getName() + ".clientTransformer";
+    private static final String SERVER_TRANSFORMER_ATTRIBUTE = AsyncMiddleManServlet.class.getName() + ".serverTransformer";
+    private static final String CONTINUE_ACTION_ATTRIBUTE = AsyncMiddleManServlet.class.getName() + ".continueAction";
 
     @Override
     protected void service(HttpServletRequest clientRequest, HttpServletResponse proxyResponse) throws ServletException, IOException
@@ -91,8 +91,6 @@
                 .method(clientRequest.getMethod())
                 .version(HttpVersion.fromString(clientRequest.getProtocol()));
 
-        boolean hasContent = hasContent(clientRequest);
-
         copyRequestHeaders(clientRequest, proxyRequest);
 
         addProxyHeaders(clientRequest, proxyRequest);
@@ -105,16 +103,43 @@
         // If there is content, the send of the proxy request
         // is delayed and performed when the content arrives,
         // to allow optimization of the Content-Length header.
-        if (hasContent)
-            proxyRequest.content(newProxyContentProvider(clientRequest, proxyResponse, proxyRequest));
+        if (hasContent(clientRequest))
+        {
+            DeferredContentProvider provider = newProxyContentProvider(clientRequest, proxyResponse, proxyRequest);
+            proxyRequest.content(provider);
+
+            if (expects100Continue(clientRequest))
+            {
+                proxyRequest.attribute(CLIENT_REQUEST_ATTRIBUTE, clientRequest);
+                proxyRequest.attribute(CONTINUE_ACTION_ATTRIBUTE, (Runnable)() ->
+                {
+                    try
+                    {
+                        ServletInputStream input = clientRequest.getInputStream();
+                        input.setReadListener(newProxyReadListener(clientRequest, proxyResponse, proxyRequest, provider));
+                    }
+                    catch (Throwable failure)
+                    {
+                        onClientRequestFailure(clientRequest, proxyRequest, proxyResponse, failure);
+                    }
+                });
+                sendProxyRequest(clientRequest, proxyResponse, proxyRequest);
+            }
+            else
+            {
+                ServletInputStream input = clientRequest.getInputStream();
+                input.setReadListener(newProxyReadListener(clientRequest, proxyResponse, proxyRequest, provider));
+            }
+        }
         else
+        {
             sendProxyRequest(clientRequest, proxyResponse, proxyRequest);
+        }
     }
 
-    protected ContentProvider newProxyContentProvider(final HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Request proxyRequest) throws IOException
+    protected DeferredContentProvider newProxyContentProvider(final HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Request proxyRequest) throws IOException
     {
-        ServletInputStream input = clientRequest.getInputStream();
-        DeferredContentProvider provider = new DeferredContentProvider()
+        return new DeferredContentProvider()
         {
             @Override
             public boolean offer(ByteBuffer buffer, Callback callback)
@@ -124,8 +149,6 @@
                 return super.offer(buffer, callback);
             }
         };
-        input.setReadListener(newProxyReadListener(clientRequest, proxyResponse, proxyRequest, provider));
-        return provider;
     }
 
     protected ReadListener newProxyReadListener(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Request proxyRequest, DeferredContentProvider provider)
@@ -138,6 +161,7 @@
         return new ProxyWriter(clientRequest, proxyResponse);
     }
 
+    @Override
     protected Response.CompleteListener newProxyResponseListener(HttpServletRequest clientRequest, HttpServletResponse proxyResponse)
     {
         return new ProxyResponseListener(clientRequest, proxyResponse);
@@ -153,6 +177,14 @@
         return ContentTransformer.IDENTITY;
     }
 
+    @Override
+    protected void onContinue(HttpServletRequest clientRequest, Request proxyRequest)
+    {
+        super.onContinue(clientRequest, proxyRequest);
+        Runnable action = (Runnable)proxyRequest.getAttributes().get(CONTINUE_ACTION_ATTRIBUTE);
+        action.run();
+    }
+
     private void transform(ContentTransformer transformer, ByteBuffer input, boolean finished, List<ByteBuffer> output) throws IOException
     {
         try
@@ -196,10 +228,10 @@
 
     private void cleanup(HttpServletRequest clientRequest)
     {
-        ContentTransformer clientTransformer = (ContentTransformer)clientRequest.getAttribute(CLIENT_TRANSFORMER);
+        ContentTransformer clientTransformer = (ContentTransformer)clientRequest.getAttribute(CLIENT_TRANSFORMER_ATTRIBUTE);
         if (clientTransformer instanceof Destroyable)
             ((Destroyable)clientTransformer).destroy();
-        ContentTransformer serverTransformer = (ContentTransformer)clientRequest.getAttribute(SERVER_TRANSFORMER);
+        ContentTransformer serverTransformer = (ContentTransformer)clientRequest.getAttribute(SERVER_TRANSFORMER_ATTRIBUTE);
         if (serverTransformer instanceof Destroyable)
             ((Destroyable)serverTransformer).destroy();
     }
@@ -207,7 +239,7 @@
     /**
      * <p>Convenience extension of {@link AsyncMiddleManServlet} that offers transparent proxy functionalities.</p>
      *
-     * @see TransparentDelegate
+     * @see org.eclipse.jetty.proxy.AbstractProxyServlet.TransparentDelegate
      */
     public static class Transparent extends ProxyServlet
     {
@@ -236,6 +268,7 @@
         private final Request proxyRequest;
         private final DeferredContentProvider provider;
         private final int contentLength;
+        private final boolean expects100Continue;
         private int length;
 
         protected ProxyReader(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Request proxyRequest, DeferredContentProvider provider)
@@ -245,6 +278,7 @@
             this.proxyRequest = proxyRequest;
             this.provider = provider;
             this.contentLength = clientRequest.getContentLength();
+            this.expects100Continue = expects100Continue(clientRequest);
         }
 
         @Override
@@ -258,7 +292,7 @@
         {
             if (!provider.isClosed())
             {
-                process(BufferUtil.EMPTY_BUFFER, new Adapter()
+                process(BufferUtil.EMPTY_BUFFER, new Callback()
                 {
                     @Override
                     public void failed(Throwable x)
@@ -286,14 +320,18 @@
             while (input.isReady() && !input.isFinished())
             {
                 int read = readClientRequestContent(input, buffer);
+
                 if (_log.isDebugEnabled())
                     _log.debug("{} asynchronous read {} bytes on {}", getRequestId(clientRequest), read, input);
 
+                if (read < 0)
+                    return Action.SUCCEEDED;
+
                 if (contentLength > 0 && read > 0)
                     length += read;
 
                 ByteBuffer content = read > 0 ? ByteBuffer.wrap(buffer, 0, read) : BufferUtil.EMPTY_BUFFER;
-                boolean finished = read < 0 || length == contentLength;
+                boolean finished = length == contentLength;
                 process(content, this, finished);
 
                 if (read > 0)
@@ -316,15 +354,13 @@
 
         private void process(ByteBuffer content, Callback callback, boolean finished) throws IOException
         {
-            ContentTransformer transformer = (ContentTransformer)clientRequest.getAttribute(CLIENT_TRANSFORMER);
+            ContentTransformer transformer = (ContentTransformer)clientRequest.getAttribute(CLIENT_TRANSFORMER_ATTRIBUTE);
             if (transformer == null)
             {
                 transformer = newClientRequestContentTransformer(clientRequest, proxyRequest);
-                clientRequest.setAttribute(CLIENT_TRANSFORMER, transformer);
+                clientRequest.setAttribute(CLIENT_TRANSFORMER_ATTRIBUTE, transformer);
             }
 
-            boolean committed = clientRequest.getAttribute(PROXY_REQUEST_COMMITTED) != null;
-
             int contentBytes = content.remaining();
 
             // Skip transformation for empty non-last buffers.
@@ -356,11 +392,15 @@
             if (_log.isDebugEnabled())
                 _log.debug("{} upstream content transformation {} -> {} bytes", getRequestId(clientRequest), contentBytes, newContentBytes);
 
-            if (!committed && (size > 0 || finished))
+            boolean contentCommitted = clientRequest.getAttribute(PROXY_REQUEST_CONTENT_COMMITTED_ATTRIBUTE) != null;
+            if (!contentCommitted && (size > 0 || finished))
             {
-                proxyRequest.header(HttpHeader.CONTENT_LENGTH, null);
-                clientRequest.setAttribute(PROXY_REQUEST_COMMITTED, true);
-                sendProxyRequest(clientRequest, proxyResponse, proxyRequest);
+                clientRequest.setAttribute(PROXY_REQUEST_CONTENT_COMMITTED_ATTRIBUTE, true);
+                if (!expects100Continue)
+                {
+                    proxyRequest.header(HttpHeader.CONTENT_LENGTH, null);
+                    sendProxyRequest(clientRequest, proxyResponse, proxyRequest);
+                }
             }
 
             if (size == 0)
@@ -396,6 +436,7 @@
         @Override
         public void onBegin(Response serverResponse)
         {
+            response = serverResponse;
             proxyResponse.setStatus(serverResponse.getStatus());
         }
 
@@ -425,11 +466,11 @@
                     clientRequest.setAttribute(WRITE_LISTENER_ATTRIBUTE, proxyWriter);
                 }
 
-                ContentTransformer transformer = (ContentTransformer)clientRequest.getAttribute(SERVER_TRANSFORMER);
+                ContentTransformer transformer = (ContentTransformer)clientRequest.getAttribute(SERVER_TRANSFORMER_ATTRIBUTE);
                 if (transformer == null)
                 {
                     transformer = newServerResponseContentTransformer(clientRequest, proxyResponse, serverResponse);
-                    clientRequest.setAttribute(SERVER_TRANSFORMER, transformer);
+                    clientRequest.setAttribute(SERVER_TRANSFORMER_ATTRIBUTE, transformer);
                 }
 
                 length += contentBytes;
@@ -497,7 +538,7 @@
                     if (contentLength < 0)
                     {
                         ProxyWriter proxyWriter = (ProxyWriter)clientRequest.getAttribute(WRITE_LISTENER_ATTRIBUTE);
-                        ContentTransformer transformer = (ContentTransformer)clientRequest.getAttribute(SERVER_TRANSFORMER);
+                        ContentTransformer transformer = (ContentTransformer)clientRequest.getAttribute(SERVER_TRANSFORMER_ATTRIBUTE);
 
                         transform(transformer, BufferUtil.EMPTY_BUFFER, true, buffers);
 
@@ -539,7 +580,6 @@
         @Override
         public void onComplete(Result result)
         {
-            response = result.getResponse();
             if (result.isSucceeded())
                 complete.succeeded();
             else
@@ -681,19 +721,19 @@
          * <p>Typical implementations:</p>
          * <pre>
          * // Identity transformation (no transformation, the input is copied to the output)
-         * public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output)
+         * public void transform(ByteBuffer input, boolean finished, List&lt;ByteBuffer&gt; output)
          * {
          *     output.add(input);
          * }
          *
          * // Discard transformation (all input is discarded)
-         * public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output)
+         * public void transform(ByteBuffer input, boolean finished, List&lt;ByteBuffer&gt; output)
          * {
          *     // Empty
          * }
          *
          * // Buffering identity transformation (all input is buffered aside until it is finished)
-         * public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output)
+         * public void transform(ByteBuffer input, boolean finished, List&lt;ByteBuffer&gt; output)
          * {
          *     ByteBuffer copy = ByteBuffer.allocate(input.remaining());
          *     copy.put(input).flip();
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java
index b1bfdd5..9abd360 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.proxy;
 
 import java.io.IOException;
-import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.channels.WritePendingException;
 
@@ -52,17 +51,17 @@
     private static final String WRITE_LISTENER_ATTRIBUTE = AsyncProxyServlet.class.getName() + ".writeListener";
 
     @Override
-    protected ContentProvider proxyRequestContent(Request proxyRequest, HttpServletRequest request) throws IOException
+    protected ContentProvider proxyRequestContent(HttpServletRequest request, HttpServletResponse response, Request proxyRequest) throws IOException
     {
         ServletInputStream input = request.getInputStream();
         DeferredContentProvider provider = new DeferredContentProvider();
-        input.setReadListener(newReadListener(proxyRequest, request, provider));
+        input.setReadListener(newReadListener(request, response, proxyRequest, provider));
         return provider;
     }
 
-    protected ReadListener newReadListener(Request proxyRequest, HttpServletRequest request, DeferredContentProvider provider)
+    protected ReadListener newReadListener(HttpServletRequest request, HttpServletResponse response, Request proxyRequest, DeferredContentProvider provider)
     {
-        return new StreamReader(proxyRequest, request, provider);
+        return new StreamReader(request, response, proxyRequest, provider);
     }
 
     @Override
@@ -107,7 +106,7 @@
     /**
      * <p>Convenience extension of {@link AsyncProxyServlet} that offers transparent proxy functionalities.</p>
      *
-     * @see TransparentDelegate
+     * @see org.eclipse.jetty.proxy.AbstractProxyServlet.TransparentDelegate
      */
     public static class Transparent extends AsyncProxyServlet
     {
@@ -121,23 +120,25 @@
         }
 
         @Override
-        protected URI rewriteURI(HttpServletRequest request)
+        protected String rewriteTarget(HttpServletRequest clientRequest)
         {
-            return URI.create(delegate.rewriteTarget(request));
+            return delegate.rewriteTarget(clientRequest);
         }
     }
 
     protected class StreamReader extends IteratingCallback implements ReadListener
     {
         private final byte[] buffer = new byte[getHttpClient().getRequestBufferSize()];
-        private final Request proxyRequest;
         private final HttpServletRequest request;
+        private final HttpServletResponse response;
+        private final Request proxyRequest;
         private final DeferredContentProvider provider;
 
-        protected StreamReader(Request proxyRequest, HttpServletRequest request, DeferredContentProvider provider)
+        protected StreamReader(HttpServletRequest request, HttpServletResponse response, Request proxyRequest, DeferredContentProvider provider)
         {
-            this.proxyRequest = proxyRequest;
             this.request = request;
+            this.response = response;
+            this.proxyRequest = proxyRequest;
             this.provider = provider;
         }
 
@@ -158,7 +159,7 @@
         @Override
         public void onError(Throwable t)
         {
-            onClientRequestFailure(proxyRequest, request, t);
+            onClientRequestFailure(request, proxyRequest, response, t);
         }
 
         @Override
@@ -178,7 +179,7 @@
                 {
                     if (_log.isDebugEnabled())
                         _log.debug("{} proxying content to upstream: {} bytes", requestId, read);
-                    onRequestContent(proxyRequest, request, provider, buffer, 0, read, this);
+                    onRequestContent(request, proxyRequest, provider, buffer, 0, read, this);
                     return Action.SCHEDULED;
                 }
             }
@@ -197,7 +198,7 @@
             }
         }
 
-        protected void onRequestContent(Request proxyRequest, HttpServletRequest request, DeferredContentProvider provider, byte[] buffer, int offset, int length, Callback callback)
+        protected void onRequestContent(HttpServletRequest request, Request proxyRequest, DeferredContentProvider provider, byte[] buffer, int offset, int length, Callback callback)
         {
             provider.offer(ByteBuffer.wrap(buffer, offset, length), callback);
         }
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/BalancerServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/BalancerServlet.java
index 57bcdfd..45e26fc 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/BalancerServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/BalancerServlet.java
@@ -31,6 +31,7 @@
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 
+import org.eclipse.jetty.client.api.Response;
 import org.eclipse.jetty.util.URIUtil;
 
 public class BalancerServlet extends ProxyServlet
@@ -129,7 +130,7 @@
     }
 
     @Override
-    protected URI rewriteURI(HttpServletRequest request)
+    protected String rewriteTarget(HttpServletRequest request)
     {
         BalancerMember balancerMember = selectBalancerMember(request);
         if (_log.isDebugEnabled())
@@ -138,7 +139,7 @@
         String query = request.getQueryString();
         if (query != null)
             path += "?" + query;
-        return URI.create(balancerMember.getProxyTo() + "/" + path).normalize();
+        return URI.create(balancerMember.getProxyTo() + "/" + path).normalize().toString();
     }
 
     private BalancerMember selectBalancerMember(HttpServletRequest request)
@@ -214,7 +215,7 @@
     }
 
     @Override
-    protected String filterResponseHeader(HttpServletRequest request, String headerName, String headerValue)
+    protected String filterServerResponseHeader(HttpServletRequest request, Response serverResponse, String headerName, String headerValue)
     {
         if (_proxyPassReverse && REVERSE_PROXY_HEADERS.contains(headerName))
         {
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
index acb79e8..2e5dd35 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
@@ -41,6 +41,7 @@
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ManagedSelector;
 import org.eclipse.jetty.io.MappedByteBufferPool;
 import org.eclipse.jetty.io.SelectChannelEndPoint;
 import org.eclipse.jetty.io.SelectorManager;
@@ -208,7 +209,7 @@
      * @param response      the http response
      * @param serverAddress the remote server address in the form {@code host:port}
      */
-    protected void handleConnect(Request baseRequest, final HttpServletRequest request, final HttpServletResponse response, String serverAddress)
+    protected void handleConnect(Request baseRequest, HttpServletRequest request, HttpServletResponse response, String serverAddress)
     {
         baseRequest.setHandled(true);
         try
@@ -234,7 +235,7 @@
                 return;
             }
 
-            final HttpTransport transport = baseRequest.getHttpChannel().getHttpTransport();
+            HttpTransport transport = baseRequest.getHttpChannel().getHttpTransport();
             // TODO Handle CONNECT over HTTP2!
             if (!(transport instanceof HttpConnection))
             {
@@ -244,7 +245,7 @@
                 return;
             }
 
-            final AsyncContext asyncContext = request.startAsync();
+            AsyncContext asyncContext = request.startAsync();
             asyncContext.setTimeout(0);
 
             if (LOG.isDebugEnabled())
@@ -357,6 +358,7 @@
         try
         {
             response.setStatus(statusCode);
+            response.setContentLength(0);
             if (statusCode != HttpServletResponse.SC_OK)
                 response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
             response.getOutputStream().close();
@@ -450,6 +452,7 @@
      * @param endPoint the endPoint to write to
      * @param buffer   the buffer to write
      * @param callback the completion callback to invoke
+     * @param context  the context information related to the connection
      */
     protected void write(EndPoint endPoint, ByteBuffer buffer, Callback callback, ConcurrentMap<String, Object> context)
     {
@@ -543,14 +546,8 @@
         protected void connectionFailed(SocketChannel channel, final Throwable ex, final Object attachment)
         {
             close(channel);
-            getExecutor().execute(new Runnable()
-            {
-                public void run()
-                {
-                    ConnectContext connectContext = (ConnectContext)attachment;
-                    onConnectFailure(connectContext.request, connectContext.response, connectContext.asyncContext, ex);
-                }
-            });
+            ConnectContext connectContext = (ConnectContext)attachment;
+            onConnectFailure(connectContext.request, connectContext.response, connectContext.asyncContext, ex);
         }
     }
 
@@ -610,14 +607,8 @@
         public void onOpen()
         {
             super.onOpen();
-            getExecutor().execute(new Runnable()
-            {
-                public void run()
-                {
-                    onConnectSuccess(connectContext, UpstreamConnection.this);
-                    fillInterested();
-                }
-            });
+            onConnectSuccess(connectContext, UpstreamConnection.this);
+            fillInterested();
         }
 
         @Override
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyConnection.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyConnection.java
index e9d2328..4285ae5 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyConnection.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyConnection.java
@@ -121,6 +121,7 @@
             {
                 if (LOG.isDebugEnabled())
                     LOG.debug(ProxyConnection.this + " could not fill", x);
+                bufferPool.release(buffer);
                 disconnect();
                 return Action.SUCCEEDED;
             }
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
index 8435dd3..ccf7596 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
@@ -18,10 +18,12 @@
 
 package org.eclipse.jetty.proxy;
 
+import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URI;
 import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 
 import javax.servlet.AsyncContext;
@@ -30,13 +32,16 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.client.AsyncContentProvider;
 import org.eclipse.jetty.client.api.ContentProvider;
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.client.api.Response;
 import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.util.DeferredContentProvider;
 import org.eclipse.jetty.client.util.InputStreamContentProvider;
 import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IteratingCallback;
 
 /**
  * <p>Servlet 3.0 asynchronous proxy servlet.</p>
@@ -48,12 +53,14 @@
  */
 public class ProxyServlet extends AbstractProxyServlet
 {
+    private static final String CONTINUE_ACTION_ATTRIBUTE = ProxyServlet.class.getName() + ".continueAction";
+
     @Override
     protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
     {
         final int requestId = getRequestId(request);
 
-        URI rewrittenURI = rewriteURI(request);
+        String rewrittenTarget = rewriteTarget(request);
 
         if (_log.isDebugEnabled())
         {
@@ -61,20 +68,20 @@
             if (request.getQueryString() != null)
                 uri.append("?").append(request.getQueryString());
             if (_log.isDebugEnabled())
-                _log.debug("{} rewriting: {} -> {}", requestId, uri, rewrittenURI);
+                _log.debug("{} rewriting: {} -> {}", requestId, uri, rewrittenTarget);
         }
 
-        if (rewrittenURI == null)
+        if (rewrittenTarget == null)
         {
-            onRewriteFailed(request, response);
+            onProxyRewriteFailed(request, response);
             return;
         }
 
-        final Request proxyRequest = getHttpClient().newRequest(rewrittenURI)
+        final Request proxyRequest = getHttpClient().newRequest(rewrittenTarget)
                 .method(request.getMethod())
                 .version(HttpVersion.fromString(request.getProtocol()));
 
-        copyHeaders(request, proxyRequest);
+        copyRequestHeaders(request, proxyRequest);
 
         addProxyHeaders(request, proxyRequest);
 
@@ -84,64 +91,45 @@
         proxyRequest.timeout(getTimeout(), TimeUnit.MILLISECONDS);
 
         if (hasContent(request))
-            proxyRequest.content(proxyRequestContent(proxyRequest, request));
-
-        customizeProxyRequest(proxyRequest, request);
+        {
+            if (expects100Continue(request))
+            {
+                DeferredContentProvider deferred = new DeferredContentProvider();
+                proxyRequest.content(deferred);
+                proxyRequest.attribute(CLIENT_REQUEST_ATTRIBUTE, request);
+                proxyRequest.attribute(CONTINUE_ACTION_ATTRIBUTE, (Runnable)() ->
+                {
+                    try
+                    {
+                        ContentProvider provider = proxyRequestContent(request, response, proxyRequest);
+                        new DelegatingContentProvider(request, proxyRequest, response, provider, deferred).iterate();
+                    }
+                    catch (Throwable failure)
+                    {
+                        onClientRequestFailure(request, proxyRequest, response, failure);
+                    }
+                });
+            }
+            else
+            {
+                proxyRequest.content(proxyRequestContent(request, response, proxyRequest));
+            }
+        }
 
         sendProxyRequest(request, response, proxyRequest);
     }
 
-    /**
-     * @deprecated use {@link #copyRequestHeaders(HttpServletRequest, Request)} instead
-     */
-    @Deprecated
-    protected void copyHeaders(HttpServletRequest clientRequest, Request proxyRequest)
+    protected ContentProvider proxyRequestContent(HttpServletRequest request, HttpServletResponse response, Request proxyRequest) throws IOException
     {
-        copyRequestHeaders(clientRequest, proxyRequest);
+        return new ProxyInputStreamContentProvider(request, response, proxyRequest, request.getInputStream());
     }
 
-    protected ContentProvider proxyRequestContent(final Request proxyRequest, final HttpServletRequest request) throws IOException
-    {
-        return new ProxyInputStreamContentProvider(proxyRequest, request, request.getInputStream());
-    }
-
+    @Override
     protected Response.Listener newProxyResponseListener(HttpServletRequest request, HttpServletResponse response)
     {
         return new ProxyResponseListener(request, response);
     }
 
-    protected void onClientRequestFailure(Request proxyRequest, HttpServletRequest request, Throwable failure)
-    {
-        if (_log.isDebugEnabled())
-            _log.debug(getRequestId(request) + " client request failure", failure);
-        proxyRequest.abort(failure);
-    }
-
-    /**
-     * @deprecated use {@link #onProxyRewriteFailed(HttpServletRequest, HttpServletResponse)}
-     */
-    @Deprecated
-    protected void onRewriteFailed(HttpServletRequest request, HttpServletResponse response) throws IOException
-    {
-        onProxyRewriteFailed(request, response);
-    }
-
-    /**
-     * @deprecated use {@link #onServerResponseHeaders(HttpServletRequest, HttpServletResponse, Response)}
-     */
-    @Deprecated
-    protected void onResponseHeaders(HttpServletRequest request, HttpServletResponse response, Response proxyResponse)
-    {
-        onServerResponseHeaders(request, response, proxyResponse);
-    }
-
-    // TODO: remove in Jetty 9.3, only here for backward compatibility.
-    @Override
-    protected String filterServerResponseHeader(HttpServletRequest clientRequest, Response serverResponse, String headerName, String headerValue)
-    {
-        return filterResponseHeader(clientRequest, headerName, headerValue);
-    }
-
     protected void onResponseContent(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, byte[] buffer, int offset, int length, Callback callback)
     {
         try
@@ -157,63 +145,19 @@
         }
     }
 
-    /**
-     * @deprecated Use {@link #onProxyResponseSuccess(HttpServletRequest, HttpServletResponse, Response)}
-     */
-    @Deprecated
-    protected void onResponseSuccess(HttpServletRequest request, HttpServletResponse response, Response proxyResponse)
+    @Override
+    protected void onContinue(HttpServletRequest clientRequest, Request proxyRequest)
     {
-        onProxyResponseSuccess(request, response, proxyResponse);
-    }
-
-    /**
-     * @deprecated Use {@link #onProxyResponseFailure(HttpServletRequest, HttpServletResponse, Response, Throwable)}
-     */
-    @Deprecated
-    protected void onResponseFailure(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, Throwable failure)
-    {
-        onProxyResponseFailure(request, response, proxyResponse, failure);
-    }
-
-    /**
-     * @deprecated use {@link #rewriteTarget(HttpServletRequest)}
-     */
-    @Deprecated
-    protected URI rewriteURI(HttpServletRequest request)
-    {
-        String newTarget = rewriteTarget(request);
-        return newTarget == null ? null : URI.create(newTarget);
-    }
-
-    /**
-     * @deprecated use {@link #sendProxyRequest(HttpServletRequest, HttpServletResponse, Request)}
-     */
-    @Deprecated
-    protected void customizeProxyRequest(Request proxyRequest, HttpServletRequest request)
-    {
-    }
-
-    /**
-     * Extension point for remote server response header filtering.
-     * The default implementation returns the header value as is.
-     * If null is returned, this header won't be forwarded back to the client.
-     *
-     * @param headerName the header name
-     * @param headerValue the header value
-     * @param request the request to proxy
-     * @return filteredHeaderValue the new header value
-     * @deprecated use {@link #filterServerResponseHeader(HttpServletRequest, Response, String, String)} instead
-     */
-    @Deprecated
-    protected String filterResponseHeader(HttpServletRequest request, String headerName, String headerValue)
-    {
-        return headerValue;
+        super.onContinue(clientRequest, proxyRequest);
+        Runnable action = (Runnable)proxyRequest.getAttributes().get(CONTINUE_ACTION_ATTRIBUTE);
+        Executor executor = getHttpClient().getExecutor();
+        executor.execute(action);
     }
 
     /**
      * <p>Convenience extension of {@link ProxyServlet} that offers transparent proxy functionalities.</p>
      *
-     * @see TransparentDelegate
+     * @see org.eclipse.jetty.proxy.AbstractProxyServlet.TransparentDelegate
      */
     public static class Transparent extends ProxyServlet
     {
@@ -227,9 +171,9 @@
         }
 
         @Override
-        protected URI rewriteURI(HttpServletRequest request)
+        protected String rewriteTarget(HttpServletRequest request)
         {
-            return URI.create(delegate.rewriteTarget(request));
+            return delegate.rewriteTarget(request);
         }
     }
 
@@ -253,7 +197,7 @@
         @Override
         public void onHeaders(Response proxyResponse)
         {
-            onResponseHeaders(request, response, proxyResponse);
+            onServerResponseHeaders(request, response, proxyResponse);
         }
 
         @Override
@@ -274,18 +218,12 @@
                 offset = 0;
             }
 
-            onResponseContent(request, response, proxyResponse, buffer, offset, length, new Callback()
+            onResponseContent(request, response, proxyResponse, buffer, offset, length, new Callback.Nested(callback)
             {
                 @Override
-                public void succeeded()
-                {
-                    callback.succeeded();
-                }
-
-                @Override
                 public void failed(Throwable x)
                 {
-                    callback.failed(x);
+                    super.failed(x);
                     proxyResponse.abort(x);
                 }
             });
@@ -295,9 +233,9 @@
         public void onComplete(Result result)
         {
             if (result.isSucceeded())
-                onResponseSuccess(request, response, result.getResponse());
+                onProxyResponseSuccess(request, response, result.getResponse());
             else
-                onResponseFailure(request, response, result.getResponse(), result.getFailure());
+                onProxyResponseFailure(request, response, result.getResponse(), result.getFailure());
             if (_log.isDebugEnabled())
                 _log.debug("{} proxying complete", getRequestId(request));
         }
@@ -305,14 +243,16 @@
 
     protected class ProxyInputStreamContentProvider extends InputStreamContentProvider
     {
+        private final HttpServletResponse response;
         private final Request proxyRequest;
         private final HttpServletRequest request;
 
-        protected ProxyInputStreamContentProvider(Request proxyRequest, HttpServletRequest request, InputStream input)
+        protected ProxyInputStreamContentProvider(HttpServletRequest request, HttpServletResponse response, Request proxyRequest, InputStream input)
         {
             super(input);
-            this.proxyRequest = proxyRequest;
             this.request = request;
+            this.response = response;
+            this.proxyRequest = proxyRequest;
         }
 
         @Override
@@ -326,10 +266,10 @@
         {
             if (_log.isDebugEnabled())
                 _log.debug("{} proxying content to upstream: {} bytes", getRequestId(request), length);
-            return onRequestContent(proxyRequest, request, buffer, offset, length);
+            return onRequestContent(request, proxyRequest, buffer, offset, length);
         }
 
-        protected ByteBuffer onRequestContent(Request proxyRequest, final HttpServletRequest request, byte[] buffer, int offset, int length)
+        protected ByteBuffer onRequestContent(HttpServletRequest request, Request proxyRequest, byte[] buffer, int offset, int length)
         {
             return super.onRead(buffer, offset, length);
         }
@@ -337,7 +277,84 @@
         @Override
         protected void onReadFailure(Throwable failure)
         {
-            onClientRequestFailure(proxyRequest, request, failure);
+            onClientRequestFailure(request, proxyRequest, response, failure);
+        }
+    }
+
+    private class DelegatingContentProvider extends IteratingCallback implements AsyncContentProvider.Listener
+    {
+        private final HttpServletRequest clientRequest;
+        private final Request proxyRequest;
+        private final HttpServletResponse proxyResponse;
+        private final Iterator<ByteBuffer> iterator;
+        private final DeferredContentProvider deferred;
+
+        private DelegatingContentProvider(HttpServletRequest clientRequest, Request proxyRequest, HttpServletResponse proxyResponse, ContentProvider provider, DeferredContentProvider deferred)
+        {
+            this.clientRequest = clientRequest;
+            this.proxyRequest = proxyRequest;
+            this.proxyResponse = proxyResponse;
+            this.iterator = provider.iterator();
+            this.deferred = deferred;
+            if (provider instanceof AsyncContentProvider)
+                ((AsyncContentProvider)provider).setListener(this);
+        }
+
+        @Override
+        protected Action process() throws Exception
+        {
+            if (!iterator.hasNext())
+                return Action.SUCCEEDED;
+
+            ByteBuffer buffer = iterator.next();
+            if (buffer == null)
+                return Action.IDLE;
+
+            deferred.offer(buffer, this);
+            return Action.SCHEDULED;
+        }
+
+        @Override
+        public void succeeded()
+        {
+            if (iterator instanceof Callback)
+                ((Callback)iterator).succeeded();
+            super.succeeded();
+        }
+
+        @Override
+        protected void onCompleteSuccess()
+        {
+            try
+            {
+                if (iterator instanceof Closeable)
+                    ((Closeable)iterator).close();
+                deferred.close();
+            }
+            catch (Throwable x)
+            {
+                _log.ignore(x);
+            }
+        }
+
+        @Override
+        protected void onCompleteFailure(Throwable failure)
+        {
+            if (iterator instanceof Callback)
+                ((Callback)iterator).failed(failure);
+            onClientRequestFailure(clientRequest, proxyRequest, proxyResponse, failure);
+        }
+
+        @Override
+        public boolean isNonBlocking()
+        {
+            return true;
+        }
+
+        @Override
+        public void onContent()
+        {
+            iterate();
         }
     }
 }
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AbstractConnectHandlerTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AbstractConnectHandlerTest.java
index 7cfe965..29f2430 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AbstractConnectHandlerTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AbstractConnectHandlerTest.java
@@ -18,24 +18,25 @@
 
 package org.eclipse.jetty.proxy;
 
-import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.Socket;
 
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.NetworkConnector;
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpParser;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
+import org.eclipse.jetty.toolchain.test.TestTracker;
 import org.junit.After;
+import org.junit.Rule;
 
 public abstract class AbstractConnectHandlerTest
 {
+    @Rule
+    public final TestTracker tracker = new TestTracker();
     protected Server server;
     protected ServerConnector serverConnector;
     protected Server proxy;
-    protected Connector proxyConnector;
+    protected ServerConnector proxyConnector;
     protected ConnectHandler connectHandler;
 
     protected void prepareProxy() throws Exception
@@ -65,15 +66,16 @@
         proxy.stop();
     }
 
-    protected SimpleHttpResponse readResponse(BufferedReader reader) throws IOException
+    protected HttpTester.Response readResponse(InputStream inputStream) throws IOException
     {
-        return new SimpleHttpParser().readResponse(reader);
+        HttpTester.Input input = HttpTester.from(inputStream);
+        return HttpTester.parseResponse(input);
     }
 
     protected Socket newSocket() throws IOException
     {
-        Socket socket = new Socket("localhost", ((NetworkConnector)proxyConnector).getLocalPort());
-        socket.setSoTimeout(5000);
+        Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
+        socket.setSoTimeout(20000);
         return socket;
     }
 }
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java
index dffe302..3f97039 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java
@@ -70,6 +70,7 @@
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.OS;
 import org.eclipse.jetty.toolchain.test.TestTracker;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.IO;
@@ -77,7 +78,7 @@
 import org.eclipse.jetty.util.ajax.JSON;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.junit.After;
 import org.junit.Assert;
@@ -94,6 +95,7 @@
     private ServerConnector proxyConnector;
     private Server server;
     private ServerConnector serverConnector;
+    private StacklessLogging stackless;
 
     private void startServer(HttpServlet servlet) throws Exception
     {
@@ -136,8 +138,8 @@
         proxyContext.addServlet(proxyServletHolder, "/*");
 
         proxy.start();
-
-        ((StdErrLog)proxyServlet._log).setHideStacks(true);
+        
+        stackless=new StacklessLogging(proxyServlet._log);
     }
 
     private void startClient() throws Exception
@@ -156,6 +158,7 @@
         client.stop();
         proxy.stop();
         server.stop();
+        stackless.close();
     }
 
     @Test
@@ -631,6 +634,17 @@
     @Test
     public void testLargeChunkedBufferedDownstreamTransformation() throws Exception
     {
+        testLargeChunkedBufferedDownstreamTransformation(false);
+    }
+
+    @Test
+    public void testLargeChunkedGzippedBufferedDownstreamTransformation() throws Exception
+    {
+        testLargeChunkedBufferedDownstreamTransformation(true);
+    }
+
+    private void testLargeChunkedBufferedDownstreamTransformation(final boolean gzipped) throws Exception
+    {
         // Tests the race between a incomplete write performed from ProxyResponseListener.onSuccess()
         // and ProxyResponseListener.onComplete() being called before the write has completed.
 
@@ -639,10 +653,18 @@
             @Override
             protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
             {
-                ServletOutputStream output = response.getOutputStream();
+                OutputStream output = response.getOutputStream();
+                if (gzipped)
+                {
+                    output = new GZIPOutputStream(output);
+                    response.setHeader(HttpHeader.CONTENT_ENCODING.asString(), "gzip");
+                }
+
+                Random random = new Random();
                 byte[] chunk = new byte[1024 * 1024];
                 for (int i = 0; i < 16; ++i)
                 {
+                    random.nextBytes(chunk);
                     output.write(chunk);
                     output.flush();
                 }
@@ -653,7 +675,10 @@
             @Override
             protected ContentTransformer newServerResponseContentTransformer(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Response serverResponse)
             {
-                return new BufferingContentTransformer();
+                ContentTransformer transformer = new BufferingContentTransformer();
+                if (gzipped)
+                    transformer = new GZIPContentTransformer(transformer);
+                return transformer;
             }
         });
         startClient();
@@ -1041,9 +1066,14 @@
         {
             Assert.assertFalse(paths.iterator().hasNext());
         }
-        try (DirectoryStream<Path> paths = Files.newDirectoryStream(targetTestsDir, outputPrefix + "*.*"))
+
+        // File deletion is delayed on windows, testing for deletion is not going to work
+        if(!OS.IS_WINDOWS)
         {
-            Assert.assertFalse(paths.iterator().hasNext());
+            try (DirectoryStream<Path> paths = Files.newDirectoryStream(targetTestsDir, outputPrefix + "*.*"))
+            {
+                Assert.assertFalse(paths.iterator().hasNext());
+            }
         }
     }
 
@@ -1097,7 +1127,6 @@
         // Send only part of the content; the proxy will idle timeout.
         final byte[] data = new byte[]{'c', 'a', 'f', 'e'};
         ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort())
-                .header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
                 .content(new BytesContentProvider(data)
                 {
                     @Override
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncProxyServletLoadTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncProxyServletLoadTest.java
index 670e0c1..e1f4619 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncProxyServletLoadTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncProxyServletLoadTest.java
@@ -24,6 +24,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerSSLTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerSSLTest.java
index f58d4a0..084d47a 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerSSLTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerSSLTest.java
@@ -18,11 +18,9 @@
 
 package org.eclipse.jetty.proxy;
 
-import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
@@ -35,12 +33,13 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.junit.Assert;
 import org.junit.Before;
@@ -77,23 +76,18 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-
-            // Be sure the buffered input does not have anything buffered
-            Assert.assertFalse(input.ready());
+            HttpTester.Response response = readResponse(socket.getInputStream());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             // Upgrade the socket to SSL
             try (SSLSocket sslSocket = wrapSocket(socket))
             {
                 output = sslSocket.getOutputStream();
-                input = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));
 
                 request =
                         "GET /echo HTTP/1.1\r\n" +
@@ -102,9 +96,9 @@
                 output.write(request.getBytes(StandardCharsets.UTF_8));
                 output.flush();
 
-                response = readResponse(input);
-                Assert.assertEquals("200", response.getCode());
-                Assert.assertEquals("GET /echo", response.getBody());
+                response = readResponse(sslSocket.getInputStream());
+                Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+                Assert.assertEquals("GET /echo", response.getContent());
             }
         }
     }
@@ -120,23 +114,18 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-
-            // Be sure the buffered input does not have anything buffered
-            Assert.assertFalse(input.ready());
+            HttpTester.Response response = readResponse(socket.getInputStream());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             // Upgrade the socket to SSL
             try (SSLSocket sslSocket = wrapSocket(socket))
             {
                 output = sslSocket.getOutputStream();
-                input = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));
 
                 for (int i = 0; i < 10; ++i)
                 {
@@ -149,9 +138,9 @@
                     output.write(request.getBytes(StandardCharsets.UTF_8));
                     output.flush();
 
-                    response = readResponse(input);
-                    Assert.assertEquals("200", response.getCode());
-                    Assert.assertEquals("POST /echo?param=" + i + "\r\nHELLO", response.getBody());
+                    response = readResponse(sslSocket.getInputStream());
+                    Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+                    Assert.assertEquals("POST /echo?param=" + i + "\r\nHELLO", response.getContent());
                 }
             }
         }
@@ -187,10 +176,14 @@
                 while ((read = input.read()) >= 0)
                     baos.write(read);
                 baos.close();
+                byte[] bytes = baos.toByteArray();
 
                 ServletOutputStream output = httpResponse.getOutputStream();
-                output.println(builder.toString());
-                output.write(baos.toByteArray());
+                if (bytes.length == 0)
+                    output.print(builder.toString());
+                else
+                    output.println(builder.toString());
+                output.write(bytes);
             }
             else
             {
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java
index 8a4c0bc..ca04050 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java
@@ -18,11 +18,9 @@
 
 package org.eclipse.jetty.proxy;
 
-import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.net.InetAddress;
 import java.net.Socket;
@@ -38,12 +36,13 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
 import org.eclipse.jetty.util.B64Code;
 import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.Promise;
@@ -75,14 +74,13 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(socket.getInputStream());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
         }
     }
 
@@ -97,14 +95,13 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(socket.getInputStream());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
         }
     }
 
@@ -119,14 +116,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             request = "" +
                     "GET /echo" + " HTTP/1.1\r\n" +
@@ -136,8 +133,8 @@
             output.flush();
 
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("GET /echo", response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("GET /echo", response.getContent());
         }
     }
 
@@ -156,14 +153,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 403 from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("403", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.FORBIDDEN_403, response.getStatus());
 
             // Socket should be closed
             Assert.assertEquals(-1, input.read());
@@ -177,14 +174,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             request = "" +
                     "GET /echo" + " HTTP/1.1\r\n" +
@@ -194,8 +191,8 @@
             output.flush();
 
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("GET /echo", response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("GET /echo", response.getContent());
         }
     }
 
@@ -214,14 +211,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 403 from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("403", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.FORBIDDEN_403, response.getStatus());
 
             // Socket should be closed
             Assert.assertEquals(-1, input.read());
@@ -235,14 +232,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             request = "" +
                     "GET /echo" + " HTTP/1.1\r\n" +
@@ -252,8 +249,8 @@
             output.flush();
 
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("GET /echo", response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("GET /echo", response.getContent());
         }
     }
 
@@ -291,15 +288,15 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 407 from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("407", response.getCode());
-            Assert.assertTrue(response.getHeaders().containsKey("Proxy-Authenticate".toLowerCase(Locale.ENGLISH)));
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407, response.getStatus());
+            Assert.assertTrue(response.containsKey("Proxy-Authenticate".toLowerCase(Locale.ENGLISH)));
 
             // Socket should be closed
             Assert.assertEquals(-1, input.read());
@@ -315,14 +312,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             request = "" +
                     "GET /echo" + " HTTP/1.1\r\n" +
@@ -332,8 +329,8 @@
             output.flush();
 
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("GET /echo", response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("GET /echo", response.getContent());
         }
     }
 
@@ -363,23 +360,18 @@
                 "CONNECT " + hostPort + " HTTP/1.1\r\n" +
                 "Host: " + hostPort + "\r\n" +
                 "\r\n";
-        Socket socket = newSocket();
-        socket.setSoTimeout(30000);
-        try
+        try (Socket socket = newSocket())
         {
+            socket.setSoTimeout(30000);
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 500 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("Response Code", "500", response.getCode());
-        }
-        finally
-        {
-            socket.close();
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals("Response Code", HttpStatus.INTERNAL_SERVER_ERROR_500, response.getStatus());
         }
     }
 
@@ -394,14 +386,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             request = "" +
                     "GET /echo" + " HTTP/1.1\r\n" +
@@ -411,8 +403,8 @@
             output.flush();
 
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("GET /echo", response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("GET /echo", response.getContent());
         }
     }
 
@@ -430,19 +422,19 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             // The pipelined request must have gone up to the server as is
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("GET /echo", response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("GET /echo", response.getContent());
         }
     }
 
@@ -457,14 +449,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             for (int i = 0; i < 10; ++i)
             {
@@ -476,8 +468,8 @@
                 output.flush();
 
                 response = readResponse(input);
-                Assert.assertEquals("200", response.getCode());
-                Assert.assertEquals("GET /echo", response.getBody());
+                Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+                Assert.assertEquals("GET /echo", response.getContent());
             }
         }
     }
@@ -493,14 +485,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             request = "" +
                     "GET /echo HTTP/1.1\r\n" +
@@ -510,8 +502,8 @@
             output.flush();
 
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("GET /echo", response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("GET /echo", response.getContent());
 
             // Idle server is shut down
             disposeServer();
@@ -532,14 +524,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             request = "" +
                     "GET /close HTTP/1.1\r\n" +
@@ -564,14 +556,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             request = "" +
                     "POST /echo HTTP/1.1\r\n" +
@@ -583,8 +575,8 @@
             output.flush();
 
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("POST /echo\r\nHELLO", response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("POST /echo\r\nHELLO", response.getContent());
 
             request = "" +
                     "GET /echo" + " HTTP/1.1\r\n" +
@@ -594,14 +586,21 @@
             output.flush();
 
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("GET /echo", response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("GET /echo", response.getContent());
         }
     }
 
     @Test
     public void testCONNECTAndPOSTWithBigBody() throws Exception
     {
+        // Use a longer idle timeout since this test
+        // may take a long time on slower machines.
+        long idleTimeout = 5 * 60 * 1000;
+        serverConnector.setIdleTimeout(idleTimeout);
+        proxyConnector.setIdleTimeout(idleTimeout);
+        connectHandler.setIdleTimeout(idleTimeout);
+
         String hostPort = "localhost:" + serverConnector.getLocalPort();
 
         String request = "" +
@@ -610,15 +609,16 @@
                 "\r\n";
         try (Socket socket = newSocket())
         {
+            socket.setSoTimeout((int)idleTimeout);
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             StringBuilder body = new StringBuilder();
             String chunk = "0123456789ABCDEF";
@@ -635,8 +635,8 @@
             output.flush();
 
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("POST /echo\r\n" + body, response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("POST /echo\r\n" + body, response.getContent());
         }
     }
 
@@ -696,14 +696,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             String body = "0123456789ABCDEF";
             request = "" +
@@ -716,8 +716,8 @@
             output.flush();
 
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("POST /echo\r\n" + body, response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("POST /echo\r\n" + body, response.getContent());
         }
     }
 
@@ -735,20 +735,20 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
             socket.shutdownOutput();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             // The pipelined request must have gone up to the server as is
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("GET /echo", response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("GET /echo", response.getContent());
         }
     }
 
@@ -763,14 +763,14 @@
         try (Socket socket = newSocket())
         {
             OutputStream output = socket.getOutputStream();
-            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            InputStream input = socket.getInputStream();
 
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
             // Expect 200 OK from the CONNECT request
-            SimpleHttpResponse response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
+            HttpTester.Response response = readResponse(input);
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
 
             request = "" +
                     "GET /echo" + " HTTP/1.1\r\n" +
@@ -782,8 +782,8 @@
 
             // The pipelined request must have gone up to the server as is
             response = readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals("GET /echo", response.getBody());
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertEquals("GET /echo", response.getContent());
         }
     }
 
@@ -809,10 +809,14 @@
                     while ((read = input.read()) >= 0)
                         baos.write(read);
                     baos.close();
+                    byte[] bytes = baos.toByteArray();
 
                     ServletOutputStream output = httpResponse.getOutputStream();
-                    output.println(builder.toString());
-                    output.write(baos.toByteArray());
+                    if (bytes.length == 0)
+                        output.print(builder.toString());
+                    else
+                        output.println(builder.toString());
+                    output.write(bytes);
                     break;
                 }
                 case "/close":
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/EchoHttpServlet.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/EchoHttpServlet.java
index c49c54c..d7acbb9 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/EchoHttpServlet.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/EchoHttpServlet.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.proxy;
 
 import java.io.IOException;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/EmptyHttpServlet.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/EmptyHttpServlet.java
index 176a3d2..90ed367 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/EmptyHttpServlet.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/EmptyHttpServlet.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.proxy;
 
 import java.io.IOException;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ForwardProxyServerTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ForwardProxyServerTest.java
new file mode 100644
index 0000000..d50ba60
--- /dev/null
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ForwardProxyServerTest.java
@@ -0,0 +1,221 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.proxy;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpProxy;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.AbstractConnectionFactory;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Utf8StringBuilder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ForwardProxyServerTest
+{
+    @Parameterized.Parameters
+    public static Object[] parameters()
+    {
+        return new Object[]{null, newSslContextFactory()};
+    }
+
+    @Rule
+    public final TestTracker tracker = new TestTracker();
+    private final SslContextFactory serverSslContextFactory;
+    private Server server;
+    private ServerConnector serverConnector;
+    private Server proxy;
+    private ServerConnector proxyConnector;
+
+    public ForwardProxyServerTest(SslContextFactory serverSslContextFactory)
+    {
+        this.serverSslContextFactory = serverSslContextFactory;
+    }
+
+    protected void startServer(ConnectionFactory connectionFactory) throws Exception
+    {
+        QueuedThreadPool serverThreads = new QueuedThreadPool();
+        serverThreads.setName("server");
+        server = new Server(serverThreads);
+        serverConnector = new ServerConnector(server, serverSslContextFactory, connectionFactory);
+        server.addConnector(serverConnector);
+        server.start();
+    }
+
+    protected void startProxy() throws Exception
+    {
+        QueuedThreadPool proxyThreads = new QueuedThreadPool();
+        proxyThreads.setName("proxy");
+        proxy = new Server(proxyThreads);
+        proxyConnector = new ServerConnector(proxy);
+        proxy.addConnector(proxyConnector);
+        // Under Windows, it takes a while to detect that a connection
+        // attempt fails, so use an explicit timeout
+        ConnectHandler connectHandler = new ConnectHandler();
+        connectHandler.setConnectTimeout(1000);
+        proxy.setHandler(connectHandler);
+
+        ServletContextHandler proxyHandler = new ServletContextHandler(connectHandler, "/");
+        proxyHandler.addServlet(ProxyServlet.class, "/*");
+
+        proxy.start();
+    }
+
+    protected HttpProxy newHttpProxy()
+    {
+        return new HttpProxy("localhost", proxyConnector.getLocalPort());
+    }
+
+    private static SslContextFactory newSslContextFactory()
+    {
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        String keyStorePath = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath();
+        sslContextFactory.setKeyStorePath(keyStorePath);
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setKeyManagerPassword("keypwd");
+        return sslContextFactory;
+    }
+
+    @After
+    public void stop() throws Exception
+    {
+        stopProxy();
+        stopServer();
+    }
+
+    protected void stopServer() throws Exception
+    {
+        if (server != null)
+        {
+            server.stop();
+            server.join();
+        }
+    }
+
+    protected void stopProxy() throws Exception
+    {
+        if (proxy != null)
+        {
+            proxy.stop();
+            proxy.join();
+        }
+    }
+
+    @Test
+    public void testRequestTarget() throws Exception
+    {
+        startServer(new AbstractConnectionFactory("http/1.1")
+        {
+            @Override
+            public Connection newConnection(Connector connector, EndPoint endPoint)
+            {
+                return new AbstractConnection(endPoint, connector.getExecutor())
+                {
+                    @Override
+                    public void onOpen()
+                    {
+                        super.onOpen();
+                        fillInterested();
+                    }
+
+                    @Override
+                    public void onFillable()
+                    {
+                        try
+                        {
+                            // When using TLS, multiple reads are required.
+                            ByteBuffer buffer = BufferUtil.allocate(1024);
+                            int filled = 0;
+                            while (filled == 0)
+                                filled = getEndPoint().fill(buffer);
+                            Utf8StringBuilder builder = new Utf8StringBuilder();
+                            builder.append(buffer);
+                            String request = builder.toString();
+
+                            // ProxyServlet will receive an absolute URI from
+                            // the client, and convert it to a relative URI.
+                            // The ConnectHandler won't modify what the client
+                            // sent, which must be a relative URI.
+                            Assert.assertThat(request.length(), Matchers.greaterThan(0));
+                            if (serverSslContextFactory == null)
+                                Assert.assertFalse(request.contains("http://"));
+                            else
+                                Assert.assertFalse(request.contains("https://"));
+
+                            String response = "" +
+                                    "HTTP/1.1 200 OK\r\n" +
+                                    "Content-Length: 0\r\n" +
+                                    "\r\n";
+                            getEndPoint().write(Callback.NOOP, ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8)));
+                        }
+                        catch (Throwable x)
+                        {
+                            x.printStackTrace();
+                            close();
+                        }
+                    }
+                };
+            }
+        });
+        startProxy();
+
+        HttpClient httpClient = new HttpClient(newSslContextFactory());
+        httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
+        httpClient.start();
+
+        try
+        {
+            ContentResponse response = httpClient.newRequest("localhost", serverConnector.getLocalPort())
+                    .scheme(serverSslContextFactory == null ? "http" : "https")
+                    .method(HttpMethod.GET)
+                    .path("/test")
+                    .send();
+
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+        }
+        finally
+        {
+            httpClient.stop();
+        }
+    }
+}
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ForwardProxyTLSServerTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ForwardProxyTLSServerTest.java
new file mode 100644
index 0000000..39a75cd
--- /dev/null
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ForwardProxyTLSServerTest.java
@@ -0,0 +1,650 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.proxy;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpProxy;
+import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Destination;
+import org.eclipse.jetty.client.util.BasicAuthentication;
+import org.eclipse.jetty.client.util.FutureResponseListener;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConnection;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ForwardProxyTLSServerTest
+{
+    @Parameterized.Parameters
+    public static Object[] parameters()
+    {
+        return new Object[]{null, newSslContextFactory()};
+    }
+
+    @Rule
+    public final TestTracker tracker = new TestTracker();
+    private final SslContextFactory proxySslContextFactory;
+    private Server server;
+    private ServerConnector serverConnector;
+    private Server proxy;
+    private ServerConnector proxyConnector;
+
+    public ForwardProxyTLSServerTest(SslContextFactory proxySslContextFactory)
+    {
+        this.proxySslContextFactory = proxySslContextFactory;
+    }
+
+    protected void startTLSServer(Handler handler) throws Exception
+    {
+        QueuedThreadPool serverThreads = new QueuedThreadPool();
+        serverThreads.setName("server");
+        server = new Server(serverThreads);
+        serverConnector = new ServerConnector(server, newSslContextFactory());
+        server.addConnector(serverConnector);
+        server.setHandler(handler);
+        server.start();
+    }
+
+    protected void startProxy() throws Exception
+    {
+        startProxy(new ConnectHandler());
+    }
+
+    protected void startProxy(ConnectHandler connectHandler) throws Exception
+    {
+        QueuedThreadPool proxyThreads = new QueuedThreadPool();
+        proxyThreads.setName("proxy");
+        proxy = new Server(proxyThreads);
+        proxyConnector = new ServerConnector(proxy, proxySslContextFactory);
+        proxy.addConnector(proxyConnector);
+        // Under Windows, it takes a while to detect that a connection
+        // attempt fails, so use an explicit timeout
+        connectHandler.setConnectTimeout(1000);
+        proxy.setHandler(connectHandler);
+        proxy.start();
+    }
+
+    protected HttpProxy newHttpProxy()
+    {
+        return new HttpProxy(new Origin.Address("localhost", proxyConnector.getLocalPort()), proxySslContextFactory != null);
+    }
+
+    private static SslContextFactory newSslContextFactory()
+    {
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        String keyStorePath = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath();
+        sslContextFactory.setKeyStorePath(keyStorePath);
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setKeyManagerPassword("keypwd");
+        return sslContextFactory;
+    }
+
+    @After
+    public void stop() throws Exception
+    {
+        stopProxy();
+        stopServer();
+    }
+
+    protected void stopServer() throws Exception
+    {
+        if (server != null)
+        {
+            server.stop();
+            server.join();
+        }
+    }
+
+    protected void stopProxy() throws Exception
+    {
+        if (proxy != null)
+        {
+            proxy.stop();
+            proxy.join();
+        }
+    }
+
+    @Test
+    public void testOneExchange() throws Exception
+    {
+        startTLSServer(new ServerHandler());
+        startProxy();
+
+        HttpClient httpClient = new HttpClient(newSslContextFactory());
+        httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
+        httpClient.start();
+
+        try
+        {
+            // Use a numeric host to test the URI of the CONNECT request.
+            // URIs such as host:80 may interpret "host" as the scheme,
+            // but when the host is numeric it is not a valid URI.
+            String host = "127.0.0.1";
+            String body = "BODY";
+            ContentResponse response = httpClient.newRequest(host, serverConnector.getLocalPort())
+                    .scheme(HttpScheme.HTTPS.asString())
+                    .method(HttpMethod.GET)
+                    .path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
+                    .timeout(5, TimeUnit.SECONDS)
+                    .send();
+
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            String content = response.getContentAsString();
+            Assert.assertEquals(body, content);
+        }
+        finally
+        {
+            httpClient.stop();
+        }
+    }
+
+    @Test
+    public void testTwoExchanges() throws Exception
+    {
+        startTLSServer(new ServerHandler());
+        startProxy();
+
+        HttpClient httpClient = new HttpClient(newSslContextFactory());
+        httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
+        httpClient.start();
+
+        try
+        {
+            String body = "BODY";
+            ContentResponse response1 = httpClient.newRequest("localhost", serverConnector.getLocalPort())
+                    .scheme(HttpScheme.HTTPS.asString())
+                    .method(HttpMethod.GET)
+                    .path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
+                    .timeout(5, TimeUnit.SECONDS)
+                    .send();
+
+            Assert.assertEquals(HttpStatus.OK_200, response1.getStatus());
+            String content = response1.getContentAsString();
+            Assert.assertEquals(body, content);
+
+            content = "body=" + body;
+            ContentResponse response2 = httpClient.newRequest("localhost", serverConnector.getLocalPort())
+                    .scheme(HttpScheme.HTTPS.asString())
+                    .method(HttpMethod.POST)
+                    .path("/echo")
+                    .header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.FORM_ENCODED.asString())
+                    .header(HttpHeader.CONTENT_LENGTH, String.valueOf(content.length()))
+                    .content(new StringContentProvider(content))
+                    .timeout(5, TimeUnit.SECONDS)
+                    .send();
+
+            Assert.assertEquals(HttpStatus.OK_200, response2.getStatus());
+            content = response2.getContentAsString();
+            Assert.assertEquals(body, content);
+        }
+        finally
+        {
+            httpClient.stop();
+        }
+    }
+
+    @Test
+    public void testTwoConcurrentExchanges() throws Exception
+    {
+        startTLSServer(new ServerHandler());
+        startProxy();
+
+        final HttpClient httpClient = new HttpClient(newSslContextFactory());
+        httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
+        httpClient.start();
+
+        try
+        {
+            final AtomicReference<Connection> connection = new AtomicReference<>();
+            final CountDownLatch connectionLatch = new CountDownLatch(1);
+            String content1 = "BODY";
+            ContentResponse response1 = httpClient.newRequest("localhost", serverConnector.getLocalPort())
+                    .scheme(HttpScheme.HTTPS.asString())
+                    .method(HttpMethod.GET)
+                    .path("/echo?body=" + URLEncoder.encode(content1, "UTF-8"))
+                    .onRequestCommit(request ->
+                    {
+                        Destination destination = httpClient.getDestination(HttpScheme.HTTPS.asString(), "localhost", serverConnector.getLocalPort());
+                        destination.newConnection(new Promise.Adapter<Connection>()
+                        {
+                            @Override
+                            public void succeeded(Connection result)
+                            {
+                                connection.set(result);
+                                connectionLatch.countDown();
+                            }
+                        });
+                    })
+                    .timeout(5, TimeUnit.SECONDS)
+                    .send();
+
+            Assert.assertEquals(HttpStatus.OK_200, response1.getStatus());
+            String content = response1.getContentAsString();
+            Assert.assertEquals(content1, content);
+
+            Assert.assertTrue(connectionLatch.await(5, TimeUnit.SECONDS));
+
+            String body2 = "body=" + content1;
+            org.eclipse.jetty.client.api.Request request2 = httpClient.newRequest("localhost", serverConnector.getLocalPort())
+                    .scheme(HttpScheme.HTTPS.asString())
+                    .method(HttpMethod.POST)
+                    .path("/echo")
+                    .header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.FORM_ENCODED.asString())
+                    .header(HttpHeader.CONTENT_LENGTH, String.valueOf(body2.length()))
+                    .content(new StringContentProvider(body2));
+
+            // Make sure the second connection can send the exchange via the tunnel
+            FutureResponseListener listener2 = new FutureResponseListener(request2);
+            connection.get().send(request2, listener2);
+            ContentResponse response2 = listener2.get(5, TimeUnit.SECONDS);
+
+            Assert.assertEquals(HttpStatus.OK_200, response2.getStatus());
+            String content2 = response2.getContentAsString();
+            Assert.assertEquals(content1, content2);
+        }
+        finally
+        {
+            httpClient.stop();
+        }
+    }
+
+    @Test
+    public void testShortIdleTimeoutOverriddenByRequest() throws Exception
+    {
+        // Short idle timeout for HttpClient.
+        long idleTimeout = 500;
+
+        startTLSServer(new ServerHandler());
+        startProxy(new ConnectHandler()
+        {
+            @Override
+            protected void handleConnect(Request baseRequest, HttpServletRequest request, HttpServletResponse response, String serverAddress)
+            {
+                try
+                {
+                    // Make sure the proxy remains idle enough.
+                    Thread.sleep(2 * idleTimeout);
+                    super.handleConnect(baseRequest, request, response, serverAddress);
+                }
+                catch (InterruptedException x)
+                {
+                    onConnectFailure(request, response, null, x);
+                }
+            }
+        });
+
+        HttpClient httpClient = new HttpClient(newSslContextFactory());
+        httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
+        // Short idle timeout for HttpClient.
+        httpClient.setIdleTimeout(idleTimeout);
+        httpClient.start();
+
+        try
+        {
+            String host = "localhost";
+            String body = "BODY";
+            ContentResponse response = httpClient.newRequest(host, serverConnector.getLocalPort())
+                    .scheme(HttpScheme.HTTPS.asString())
+                    .method(HttpMethod.GET)
+                    .path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
+                    // Long idle timeout for the request.
+                    .idleTimeout(10 * idleTimeout, TimeUnit.MILLISECONDS)
+                    .timeout(5, TimeUnit.SECONDS)
+                    .send();
+
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            String content = response.getContentAsString();
+            Assert.assertEquals(body, content);
+        }
+        finally
+        {
+            httpClient.stop();
+        }
+    }
+
+    @Test
+    public void testProxyDown() throws Exception
+    {
+        startTLSServer(new ServerHandler());
+        startProxy();
+        int proxyPort = proxyConnector.getLocalPort();
+        stopProxy();
+
+        HttpClient httpClient = new HttpClient(newSslContextFactory());
+        httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(new Origin.Address("localhost", proxyPort), proxySslContextFactory != null));
+        httpClient.start();
+
+        try
+        {
+            String body = "BODY";
+            httpClient.newRequest("localhost", serverConnector.getLocalPort())
+                    .scheme(HttpScheme.HTTPS.asString())
+                    .method(HttpMethod.GET)
+                    .path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
+                    .timeout(5, TimeUnit.SECONDS)
+                    .send();
+            Assert.fail();
+        }
+        catch (ExecutionException x)
+        {
+            Assert.assertThat(x.getCause(), Matchers.instanceOf(ConnectException.class));
+        }
+        finally
+        {
+            httpClient.stop();
+        }
+    }
+
+    @Test
+    public void testServerDown() throws Exception
+    {
+        startTLSServer(new ServerHandler());
+        int serverPort = serverConnector.getLocalPort();
+        stopServer();
+        startProxy();
+
+        HttpClient httpClient = new HttpClient(newSslContextFactory());
+        httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
+        httpClient.start();
+
+        try
+        {
+            String body = "BODY";
+            httpClient.newRequest("localhost", serverPort)
+                    .scheme(HttpScheme.HTTPS.asString())
+                    .method(HttpMethod.GET)
+                    .path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
+                    .timeout(5, TimeUnit.SECONDS)
+                    .send();
+            Assert.fail();
+        }
+        catch (ExecutionException x)
+        {
+            // Expected
+        }
+        finally
+        {
+            httpClient.stop();
+        }
+    }
+
+    @Test
+    public void testProxyClosesConnection() throws Exception
+    {
+        startTLSServer(new ServerHandler());
+        startProxy(new ConnectHandler()
+        {
+            @Override
+            protected void handleConnect(Request baseRequest, HttpServletRequest request, HttpServletResponse response, String serverAddress)
+            {
+                ((HttpConnection)baseRequest.getHttpChannel().getHttpTransport()).close();
+            }
+        });
+
+        HttpClient httpClient = new HttpClient(newSslContextFactory());
+        httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
+        httpClient.start();
+
+        try
+        {
+            httpClient.newRequest("localhost", serverConnector.getLocalPort())
+                    .scheme(HttpScheme.HTTPS.asString())
+                    .timeout(5, TimeUnit.SECONDS)
+                    .send();
+            Assert.fail();
+        }
+        catch (ExecutionException x)
+        {
+            // Expected
+        }
+        finally
+        {
+            httpClient.stop();
+        }
+    }
+
+    @Test
+    public void testProxyAuthentication() throws Exception
+    {
+        final String realm = "test-realm";
+        testProxyAuthentication(realm, new ConnectHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                String proxyAuth = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString());
+                if (proxyAuth == null)
+                {
+                    baseRequest.setHandled(true);
+                    response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407);
+                    response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + realm + "\"");
+                    return;
+                }
+                super.handle(target, baseRequest, request, response);
+            }
+        });
+    }
+
+    @Test
+    public void testProxyAuthenticationWithResponseContent() throws Exception
+    {
+        final String realm = "test-realm";
+        testProxyAuthentication(realm, new ConnectHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                String proxyAuth = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString());
+                if (proxyAuth == null)
+                {
+                    baseRequest.setHandled(true);
+                    response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407);
+                    response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + realm + "\"");
+                    response.getOutputStream().write(new byte[4096]);
+                    return;
+                }
+                super.handle(target, baseRequest, request, response);
+            }
+        });
+    }
+
+    @Test
+    public void testProxyAuthenticationWithIncludedAddressWithResponseContent() throws Exception
+    {
+        final String realm = "test-realm";
+        testProxyAuthentication(realm, new ConnectHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                String proxyAuth = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString());
+                if (proxyAuth == null)
+                {
+                    baseRequest.setHandled(true);
+                    response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407);
+                    response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + realm + "\"");
+                    response.getOutputStream().write(new byte[1024]);
+                    return;
+                }
+                super.handle(target, baseRequest, request, response);
+            }
+        }, true);
+    }
+
+    @Test
+    public void testProxyAuthenticationClosesConnection() throws Exception
+    {
+        final String realm = "test-realm";
+        testProxyAuthentication(realm, new ConnectHandler()
+        {
+            @Override
+            protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address)
+            {
+                final String header = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.toString());
+                if (header == null || !header.startsWith("Basic "))
+                {
+                    response.setHeader(HttpHeader.PROXY_AUTHENTICATE.toString(), "Basic realm=\"" + realm + "\"");
+                    // Returning false adds Connection: close to the 407 response.
+                    return false;
+                }
+                else
+                {
+                    return true;
+                }
+            }
+        });
+    }
+
+    private void testProxyAuthentication(String realm, ConnectHandler connectHandler) throws Exception
+    {
+        testProxyAuthentication(realm, connectHandler, false);
+    }
+
+    private void testProxyAuthentication(String realm, ConnectHandler connectHandler, boolean includeAddress) throws Exception
+    {
+        startTLSServer(new ServerHandler());
+        startProxy(connectHandler);
+
+        HttpClient httpClient = new HttpClient(newSslContextFactory());
+        HttpProxy httpProxy = newHttpProxy();
+        if (includeAddress)
+            httpProxy.getIncludedAddresses().add("localhost:" + serverConnector.getLocalPort());
+        httpClient.getProxyConfiguration().getProxies().add(httpProxy);
+        URI uri = URI.create((proxySslContextFactory == null ? "http" : "https") + "://localhost:" + proxyConnector.getLocalPort());
+        httpClient.getAuthenticationStore().addAuthentication(new BasicAuthentication(uri, realm, "proxyUser", "proxyPassword"));
+        httpClient.start();
+
+        try
+        {
+            String host = "localhost";
+            String body = "BODY";
+            ContentResponse response = httpClient.newRequest(host, serverConnector.getLocalPort())
+                    .scheme(HttpScheme.HTTPS.asString())
+                    .method(HttpMethod.GET)
+                    .path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
+                    .timeout(5, TimeUnit.SECONDS)
+                    .send();
+
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            String content = response.getContentAsString();
+            Assert.assertEquals(body, content);
+        }
+        finally
+        {
+            httpClient.stop();
+        }
+    }
+
+    @Test
+    @Ignore("External Proxy Server no longer stable enough for testing")
+    public void testExternalProxy() throws Exception
+    {
+        // Free proxy server obtained from http://hidemyass.com/proxy-list/
+        String proxyHost = "81.208.25.53";
+        int proxyPort = 3128;
+        try
+        {
+            new Socket(proxyHost, proxyPort).close();
+        }
+        catch (Throwable x)
+        {
+            Assume.assumeNoException(x);
+        }
+
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.start();
+
+        HttpClient httpClient = new HttpClient(newSslContextFactory());
+        httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
+        httpClient.start();
+
+        try
+        {
+            ContentResponse response = httpClient.newRequest("https://www.google.com")
+                    // Use a longer timeout, sometimes the proxy takes a while to answer
+                    .timeout(20, TimeUnit.SECONDS)
+                    .send();
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+        }
+        finally
+        {
+            httpClient.stop();
+        }
+    }
+
+    private static class ServerHandler extends AbstractHandler
+    {
+        public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
+        {
+            request.setHandled(true);
+
+            String uri = httpRequest.getRequestURI();
+            if ("/echo".equals(uri))
+            {
+                String body = httpRequest.getParameter("body");
+                ServletOutputStream output = httpResponse.getOutputStream();
+                output.print(body);
+            }
+            else
+            {
+                throw new ServletException();
+            }
+        }
+    }
+}
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletFailureTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletFailureTest.java
index 157c646..bf913c8 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletFailureTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletFailureTest.java
@@ -18,10 +18,8 @@
 
 package org.eclipse.jetty.proxy;
 
-import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.net.Socket;
 import java.nio.ByteBuffer;
@@ -30,6 +28,7 @@
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -43,6 +42,7 @@
 import org.eclipse.jetty.client.api.Response;
 import org.eclipse.jetty.client.util.BytesContentProvider;
 import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
@@ -50,11 +50,8 @@
 import org.eclipse.jetty.servlet.ServletHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpParser;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
 import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.junit.After;
 import org.junit.Assert;
@@ -68,7 +65,7 @@
 {
     private static final String PROXIED_HEADER = "X-Proxied";
 
-    @Parameterized.Parameters
+    @Parameterized.Parameters(name = "{0}")
     public static Iterable<Object[]> data()
     {
         return Arrays.asList(new Object[][]{
@@ -206,14 +203,13 @@
             // Do not send the promised content, wait to idle timeout.
 
             socket.setSoTimeout(2 * idleTimeout);
-            SimpleHttpParser parser = new SimpleHttpParser();
-            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
-            SimpleHttpResponse response = parser.readResponse(reader);
-            Assert.assertTrue(Integer.parseInt(response.getCode()) >= 500);
-            String connectionHeader = response.getHeaders().get("connection");
+    
+            HttpTester.Response response = HttpTester.parseResponse(socket.getInputStream());
+            Assert.assertTrue(response.getStatus() >= 500);
+            String connectionHeader = response.get("connection");
             Assert.assertNotNull(connectionHeader);
             Assert.assertTrue(connectionHeader.contains("close"));
-            Assert.assertEquals(-1, reader.read());
+            Assert.assertEquals(-1, socket.getInputStream().read());
         }
     }
 
@@ -242,14 +238,13 @@
             // Do not send all the promised content, wait to idle timeout.
 
             socket.setSoTimeout(2 * idleTimeout);
-            SimpleHttpParser parser = new SimpleHttpParser();
-            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
-            SimpleHttpResponse response = parser.readResponse(reader);
-            Assert.assertTrue(Integer.parseInt(response.getCode()) >= 500);
-            String connectionHeader = response.getHeaders().get("connection");
+            
+            HttpTester.Response response = HttpTester.parseResponse(socket.getInputStream());
+            Assert.assertTrue(response.getStatus() >= 500);
+            String connectionHeader = response.get("connection");
             Assert.assertNotNull(connectionHeader);
             Assert.assertTrue(connectionHeader.contains("close"));
-            Assert.assertEquals(-1, reader.read());
+            Assert.assertEquals(-1, socket.getInputStream().read());
         }
     }
 
@@ -262,17 +257,20 @@
             proxyServlet = new AsyncProxyServlet()
             {
                 @Override
-                protected ContentProvider proxyRequestContent(Request proxyRequest, HttpServletRequest request) throws IOException
+                protected ContentProvider proxyRequestContent(HttpServletRequest request, HttpServletResponse response, Request proxyRequest) throws IOException
                 {
-                    return new DeferredContentProvider()
+                    DeferredContentProvider provider = new DeferredContentProvider()
                     {
                         @Override
                         public boolean offer(ByteBuffer buffer, Callback callback)
                         {
-                            // Ignore all content to trigger the test condition.
-                            return true;
+                            // Send less content to trigger the test condition.
+                            buffer.limit(buffer.limit() - 1);
+                            return super.offer(buffer.slice(), callback);
                         }
                     };
+                    request.getInputStream().setReadListener(newReadListener(request, response, proxyRequest, provider));
+                    return provider;
                 }
             };
         }
@@ -281,7 +279,7 @@
             proxyServlet = new ProxyServlet()
             {
                 @Override
-                protected ContentProvider proxyRequestContent(Request proxyRequest, HttpServletRequest request) throws IOException
+                protected ContentProvider proxyRequestContent(HttpServletRequest request, HttpServletResponse response, Request proxyRequest) throws IOException
                 {
                     return new BytesContentProvider(content)
                     {
@@ -300,12 +298,15 @@
         prepareServer(new EchoHttpServlet());
         long idleTimeout = 1000;
         serverConnector.setIdleTimeout(idleTimeout);
+        
+        try(StacklessLogging stackless = new StacklessLogging(ServletHandler.class))
+        {
+            ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort())
+                    .content(new BytesContentProvider(content))
+                    .send();
 
-        ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort())
-                .content(new BytesContentProvider(content))
-                .send();
-
-        Assert.assertEquals(500, response.getStatus());
+            Assert.assertEquals(500, response.getStatus());
+        }
     }
 
     @Test(expected = TimeoutException.class)
@@ -387,8 +388,7 @@
     @Test
     public void testServerException() throws Exception
     {
-        ((StdErrLog)Log.getLogger(ServletHandler.class)).setHideStacks(true);
-        try
+        try (StacklessLogging stackless = new StacklessLogging(ServletHandler.class))
         {
             prepareProxy();
             prepareServer(new HttpServlet()
@@ -406,14 +406,5 @@
 
             Assert.assertEquals(500, response.getStatus());
         }
-        finally
-        {
-            ((StdErrLog)Log.getLogger(ServletHandler.class)).setHideStacks(false);
-        }
-    }
-
-    private interface Function<T, R>
-    {
-        public R apply(T arg);
     }
 }
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java
index 6663e63..ead9ce9 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java
@@ -53,6 +53,7 @@
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
@@ -71,9 +72,12 @@
 import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
 import org.eclipse.jetty.client.util.BufferingResponseListener;
 import org.eclipse.jetty.client.util.BytesContentProvider;
+import org.eclipse.jetty.client.util.DeferredContentProvider;
 import org.eclipse.jetty.client.util.InputStreamResponseListener;
 import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpHeaderValue;
 import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.Server;
@@ -141,7 +145,7 @@
 
     private void startProxy() throws Exception
     {
-        startProxy(new HashMap<String, String>());
+        startProxy(new HashMap<>());
     }
 
     private void startProxy(Map<String, String> initParams) throws Exception
@@ -186,9 +190,12 @@
     @After
     public void dispose() throws Exception
     {
-        client.stop();
-        proxy.stop();
-        server.stop();
+        if (client != null)
+            client.stop();
+        if (proxy != null)
+            proxy.stop();
+        if (server != null)
+            server.stop();
     }
 
     @Test
@@ -672,7 +679,7 @@
         testTransparentProxyWithQuery("", "/proxy", "");
     }
 
-    private void testTransparentProxyWithQuery(final String proxyToContext, String prefix, final String target) throws Exception
+    private void testTransparentProxyWithQuery(String proxyToContext, String prefix, String target) throws Exception
     {
         final String query = "a=1&b=2";
         startServer(new HttpServlet()
@@ -1230,5 +1237,227 @@
         Assert.assertEquals(200, response.getStatus());
     }
 
-    // TODO: test proxy authentication
+    @Test
+    public void testExpect100ContinueRespond100Continue() throws Exception
+    {
+        CountDownLatch serverLatch1 = new CountDownLatch(1);
+        CountDownLatch serverLatch2 = new CountDownLatch(1);
+        startServer(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                serverLatch1.countDown();
+
+                try
+                {
+                    serverLatch2.await(5, TimeUnit.SECONDS);
+                }
+                catch (Throwable x)
+                {
+                    throw new InterruptedIOException();
+                }
+
+                // Send the 100 Continue.
+                ServletInputStream input = request.getInputStream();
+
+                // Echo the content.
+                IO.copy(input, response.getOutputStream());
+            }
+        });
+        startProxy();
+        startClient();
+
+        byte[] content = new byte[1024];
+        CountDownLatch contentLatch = new CountDownLatch(1);
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.newRequest("localhost", serverConnector.getLocalPort())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(new BytesContentProvider(content))
+                .onRequestContent((request, buffer) -> contentLatch.countDown())
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        if (result.isSucceeded())
+                        {
+                            if (result.getResponse().getStatus() == HttpStatus.OK_200)
+                            {
+                                if (Arrays.equals(content, getContent()))
+                                    clientLatch.countDown();
+                            }
+                        }
+                    }
+                });
+
+        // Wait until we arrive on the server.
+        Assert.assertTrue(serverLatch1.await(5, TimeUnit.SECONDS));
+        // The client should not send the content yet.
+        Assert.assertFalse(contentLatch.await(1, TimeUnit.SECONDS));
+
+        // Make the server send the 100 Continue.
+        serverLatch2.countDown();
+
+        // The client has sent the content.
+        Assert.assertTrue(contentLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testExpect100ContinueRespond100ContinueDelayedRequestContent() throws Exception
+    {
+        startServer(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                // Send the 100 Continue.
+                ServletInputStream input = request.getInputStream();
+                // Echo the content.
+                IO.copy(input, response.getOutputStream());
+            }
+        });
+        startProxy();
+        startClient();
+
+        byte[] content = new byte[1024];
+        new Random().nextBytes(content);
+        int chunk1 = content.length / 2;
+        DeferredContentProvider contentProvider = new DeferredContentProvider();
+        contentProvider.offer(ByteBuffer.wrap(content, 0, chunk1));
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.newRequest("localhost", serverConnector.getLocalPort())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(contentProvider)
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        if (result.isSucceeded())
+                        {
+                            if (result.getResponse().getStatus() == HttpStatus.OK_200)
+                            {
+                                if (Arrays.equals(content, getContent()))
+                                    clientLatch.countDown();
+                            }
+                        }
+                    }
+                });
+
+        // Wait a while and then offer more content.
+        Thread.sleep(1000);
+        contentProvider.offer(ByteBuffer.wrap(content, chunk1, content.length - chunk1));
+        contentProvider.close();
+
+        Assert.assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testExpect100ContinueRespond100ContinueSomeRequestContentThenFailure() throws Exception
+    {
+        CountDownLatch serverLatch = new CountDownLatch(1);
+        startServer(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                // Send the 100 Continue.
+                ServletInputStream input = request.getInputStream();
+                try
+                {
+                    // Echo the content.
+                    IO.copy(input, response.getOutputStream());
+                }
+                catch (IOException x)
+                {
+                    serverLatch.countDown();
+                }
+            }
+        });
+        startProxy();
+        startClient();
+
+        long idleTimeout = 1000;
+        client.setIdleTimeout(idleTimeout);
+
+        byte[] content = new byte[1024];
+        new Random().nextBytes(content);
+        int chunk1 = content.length / 2;
+        DeferredContentProvider contentProvider = new DeferredContentProvider();
+        contentProvider.offer(ByteBuffer.wrap(content, 0, chunk1));
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.newRequest("localhost", serverConnector.getLocalPort())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(contentProvider)
+                .send(result ->
+                {
+                    if (result.isFailed())
+                        clientLatch.countDown();
+                });
+
+        // Wait more than the idle timeout to break the connection.
+        Thread.sleep(2 * idleTimeout);
+
+        Assert.assertTrue(serverLatch.await(555, TimeUnit.SECONDS));
+        Assert.assertTrue(clientLatch.await(555, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testExpect100ContinueRespond417ExpectationFailed() throws Exception
+    {
+        CountDownLatch serverLatch1 = new CountDownLatch(1);
+        CountDownLatch serverLatch2 = new CountDownLatch(1);
+        startServer(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                serverLatch1.countDown();
+
+                try
+                {
+                    serverLatch2.await(5, TimeUnit.SECONDS);
+                }
+                catch (Throwable x)
+                {
+                    throw new InterruptedIOException();
+                }
+
+                // Send the 417 Expectation Failed.
+                response.setStatus(HttpStatus.EXPECTATION_FAILED_417);
+            }
+        });
+        startProxy();
+        startClient();
+
+        byte[] content = new byte[1024];
+        CountDownLatch contentLatch = new CountDownLatch(1);
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.newRequest("localhost", serverConnector.getLocalPort())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(new BytesContentProvider(content))
+                .onRequestContent((request, buffer) -> contentLatch.countDown())
+                .send(result ->
+                {
+                    if (result.isFailed())
+                    {
+                        if (result.getResponse().getStatus() == HttpStatus.EXPECTATION_FAILED_417)
+                            clientLatch.countDown();
+                    }
+                });
+
+        // Wait until we arrive on the server.
+        Assert.assertTrue(serverLatch1.await(5, TimeUnit.SECONDS));
+        // The client should not send the content yet.
+        Assert.assertFalse(contentLatch.await(1, TimeUnit.SECONDS));
+
+        // Make the server send the 417 Expectation Failed.
+        serverLatch2.countDown();
+
+        // The client should not send the content.
+        Assert.assertFalse(contentLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+    }
 }
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java
deleted file mode 100644
index cf3c975..0000000
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java
+++ /dev/null
@@ -1,512 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.proxy;
-
-import java.io.IOException;
-import java.net.ConnectException;
-import java.net.Socket;
-import java.net.URI;
-import java.net.URLEncoder;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpProxy;
-import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Destination;
-import org.eclipse.jetty.client.util.BasicAuthentication;
-import org.eclipse.jetty.client.util.FutureResponseListener;
-import org.eclipse.jetty.client.util.StringContentProvider;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.HttpScheme;
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.MimeTypes;
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.HttpConnection;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
-import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.hamcrest.Matchers;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Assume;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class ProxyTunnellingTest
-{
-    @Rule
-    public TestTracker tracker = new TestTracker();
-
-    private SslContextFactory sslContextFactory;
-    private Server server;
-    private ServerConnector serverConnector;
-    private Server proxy;
-    private ServerConnector proxyConnector;
-
-    protected int proxyPort()
-    {
-        return proxyConnector.getLocalPort();
-    }
-
-    protected void startSSLServer(Handler handler) throws Exception
-    {
-        sslContextFactory = new SslContextFactory();
-        String keyStorePath = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath();
-        sslContextFactory.setKeyStorePath(keyStorePath);
-        sslContextFactory.setKeyStorePassword("storepwd");
-        sslContextFactory.setKeyManagerPassword("keypwd");
-        server = new Server();
-        serverConnector = new ServerConnector(server, sslContextFactory);
-        server.addConnector(serverConnector);
-        server.setHandler(handler);
-        server.start();
-    }
-
-    protected void startProxy() throws Exception
-    {
-        startProxy(new ConnectHandler());
-    }
-
-    protected void startProxy(ConnectHandler connectHandler) throws Exception
-    {
-        proxy = new Server();
-        proxyConnector = new ServerConnector(proxy);
-        proxy.addConnector(proxyConnector);
-        // Under Windows, it takes a while to detect that a connection
-        // attempt fails, so use an explicit timeout
-        connectHandler.setConnectTimeout(1000);
-        proxy.setHandler(connectHandler);
-        proxy.start();
-    }
-
-    @After
-    public void stop() throws Exception
-    {
-        stopProxy();
-        stopServer();
-    }
-
-    protected void stopServer() throws Exception
-    {
-        if (server != null)
-        {
-            server.stop();
-            server.join();
-        }
-    }
-
-    protected void stopProxy() throws Exception
-    {
-        if (proxy != null)
-        {
-            proxy.stop();
-            proxy.join();
-        }
-    }
-
-    @Test
-    public void testOneExchangeViaSSL() throws Exception
-    {
-        startSSLServer(new ServerHandler());
-        startProxy();
-
-        HttpClient httpClient = new HttpClient(sslContextFactory);
-        httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
-        httpClient.start();
-
-        try
-        {
-            // Use a numeric host to test the URI of the CONNECT request.
-            String host = "127.0.0.1";
-            String body = "BODY";
-            ContentResponse response = httpClient.newRequest(host, serverConnector.getLocalPort())
-                    .scheme(HttpScheme.HTTPS.asString())
-                    .method(HttpMethod.GET)
-                    .path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
-                    .send();
-
-            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
-            String content = response.getContentAsString();
-            Assert.assertEquals(body, content);
-        }
-        finally
-        {
-            httpClient.stop();
-        }
-    }
-
-    @Test
-    public void testTwoExchangesViaSSL() throws Exception
-    {
-        startSSLServer(new ServerHandler());
-        startProxy();
-
-        HttpClient httpClient = new HttpClient(sslContextFactory);
-        httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
-        httpClient.start();
-
-        try
-        {
-            String body = "BODY";
-            ContentResponse response1 = httpClient.newRequest("localhost", serverConnector.getLocalPort())
-                    .scheme(HttpScheme.HTTPS.asString())
-                    .method(HttpMethod.GET)
-                    .path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
-                    .send();
-
-            Assert.assertEquals(HttpStatus.OK_200, response1.getStatus());
-            String content = response1.getContentAsString();
-            Assert.assertEquals(body, content);
-
-            content = "body=" + body;
-            ContentResponse response2 = httpClient.newRequest("localhost", serverConnector.getLocalPort())
-                    .scheme(HttpScheme.HTTPS.asString())
-                    .method(HttpMethod.POST)
-                    .path("/echo")
-                    .header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.FORM_ENCODED.asString())
-                    .header(HttpHeader.CONTENT_LENGTH, String.valueOf(content.length()))
-                    .content(new StringContentProvider(content))
-                    .send();
-
-            Assert.assertEquals(HttpStatus.OK_200, response2.getStatus());
-            content = response2.getContentAsString();
-            Assert.assertEquals(body, content);
-        }
-        finally
-        {
-            httpClient.stop();
-        }
-    }
-
-    @Test
-    public void testTwoConcurrentExchangesViaSSL() throws Exception
-    {
-        startSSLServer(new ServerHandler());
-        startProxy();
-
-        final HttpClient httpClient = new HttpClient(sslContextFactory);
-        httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
-        httpClient.start();
-
-        try
-        {
-            final AtomicReference<Connection> connection = new AtomicReference<>();
-            final CountDownLatch connectionLatch = new CountDownLatch(1);
-            String body1 = "BODY";
-            ContentResponse response1 = httpClient.newRequest("localhost", serverConnector.getLocalPort())
-                    .scheme(HttpScheme.HTTPS.asString())
-                    .method(HttpMethod.GET)
-                    .path("/echo?body=" + URLEncoder.encode(body1, "UTF-8"))
-                    .onRequestCommit(new org.eclipse.jetty.client.api.Request.CommitListener()
-                    {
-                        @Override
-                        public void onCommit(org.eclipse.jetty.client.api.Request request)
-                        {
-                            Destination destination = httpClient.getDestination(HttpScheme.HTTPS.asString(), "localhost", serverConnector.getLocalPort());
-                            destination.newConnection(new Promise.Adapter<Connection>()
-                            {
-                                @Override
-                                public void succeeded(Connection result)
-                                {
-                                    connection.set(result);
-                                    connectionLatch.countDown();
-                                }
-                            });
-                        }
-                    })
-                    .send();
-
-            Assert.assertEquals(HttpStatus.OK_200, response1.getStatus());
-            String content = response1.getContentAsString();
-            Assert.assertEquals(body1, content);
-
-            Assert.assertTrue(connectionLatch.await(5, TimeUnit.SECONDS));
-
-            String body2 = "body=" + body1;
-            org.eclipse.jetty.client.api.Request request2 = httpClient.newRequest("localhost", serverConnector.getLocalPort())
-                    .scheme(HttpScheme.HTTPS.asString())
-                    .method(HttpMethod.POST)
-                    .path("/echo")
-                    .header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.FORM_ENCODED.asString())
-                    .header(HttpHeader.CONTENT_LENGTH, String.valueOf(body2.length()))
-                    .content(new StringContentProvider(body2));
-
-            // Make sure the second connection can send the exchange via the tunnel
-            FutureResponseListener listener2 = new FutureResponseListener(request2);
-            connection.get().send(request2, listener2);
-            ContentResponse response2 = listener2.get(5, TimeUnit.SECONDS);
-
-            Assert.assertEquals(HttpStatus.OK_200, response2.getStatus());
-            String content2 = response1.getContentAsString();
-            Assert.assertEquals(body1, content2);
-        }
-        finally
-        {
-            httpClient.stop();
-        }
-    }
-
-    @Test
-    public void testProxyDown() throws Exception
-    {
-        startSSLServer(new ServerHandler());
-        startProxy();
-        int proxyPort = proxyPort();
-        stopProxy();
-
-        HttpClient httpClient = new HttpClient(sslContextFactory);
-        httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort));
-        httpClient.start();
-
-        try
-        {
-            String body = "BODY";
-            httpClient.newRequest("localhost", serverConnector.getLocalPort())
-                    .scheme(HttpScheme.HTTPS.asString())
-                    .method(HttpMethod.GET)
-                    .path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
-                    .send();
-            Assert.fail();
-        }
-        catch (ExecutionException x)
-        {
-            Assert.assertThat(x.getCause(), Matchers.instanceOf(ConnectException.class));
-        }
-        finally
-        {
-            httpClient.stop();
-        }
-    }
-
-    @Test
-    public void testServerDown() throws Exception
-    {
-        startSSLServer(new ServerHandler());
-        int serverPort = serverConnector.getLocalPort();
-        stopServer();
-        startProxy();
-
-        HttpClient httpClient = new HttpClient(sslContextFactory);
-        httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
-        httpClient.start();
-
-        try
-        {
-            String body = "BODY";
-            httpClient.newRequest("localhost", serverPort)
-                    .scheme(HttpScheme.HTTPS.asString())
-                    .method(HttpMethod.GET)
-                    .path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
-                    .send();
-            Assert.fail();
-        }
-        catch (ExecutionException x)
-        {
-            // Expected
-        }
-        finally
-        {
-            httpClient.stop();
-        }
-    }
-
-    @Test
-    public void testProxyClosesConnection() throws Exception
-    {
-        startSSLServer(new ServerHandler());
-        startProxy(new ConnectHandler()
-        {
-            @Override
-            protected void handleConnect(Request baseRequest, HttpServletRequest request, HttpServletResponse response, String serverAddress)
-            {
-                ((HttpConnection)baseRequest.getHttpChannel().getHttpTransport()).close();
-            }
-        });
-
-        HttpClient httpClient = new HttpClient(sslContextFactory);
-        httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
-        httpClient.start();
-
-        try
-        {
-            httpClient.newRequest("localhost", serverConnector.getLocalPort())
-                    .scheme(HttpScheme.HTTPS.asString())
-                    .send();
-            Assert.fail();
-        }
-        catch (ExecutionException x)
-        {
-            // Expected
-        }
-        finally
-        {
-            httpClient.stop();
-        }
-    }
-
-    @Test
-    public void testProxyAuthentication() throws Exception
-    {
-        final String realm = "test-realm";
-        testProxyAuthentication(realm, new ConnectHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-            {
-                String proxyAuth = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString());
-                if (proxyAuth == null)
-                {
-                    baseRequest.setHandled(true);
-                    response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407);
-                    response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + realm + "\"");
-                    return;
-                }
-                super.handle(target, baseRequest, request, response);
-            }
-        });
-    }
-
-    @Test
-    public void testProxyAuthenticationClosesConnection() throws Exception
-    {
-        final String realm = "test-realm";
-        testProxyAuthentication(realm, new ConnectHandler()
-        {
-            @Override
-            protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address)
-            {
-                final String header = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.toString());
-                if (header == null || !header.startsWith("Basic "))
-                {
-                    response.setHeader(HttpHeader.PROXY_AUTHENTICATE.toString(), "Basic realm=\"" + realm + "\"");
-                    // Returning false adds Connection: close to the 407 response.
-                    return false;
-                }
-                else
-                {
-                    return true;
-                }
-            }
-        });
-    }
-
-    private void testProxyAuthentication(String realm, ConnectHandler connectHandler) throws Exception
-    {
-        startSSLServer(new ServerHandler());
-        startProxy(connectHandler);
-
-        HttpClient httpClient = new HttpClient(sslContextFactory);
-        httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
-        httpClient.getAuthenticationStore().addAuthentication(
-                new BasicAuthentication(URI.create("http://localhost:" + proxyPort()), realm, "proxyUser", "proxyPassword"));
-        httpClient.start();
-
-        try
-        {
-            String host = "localhost";
-            String body = "BODY";
-            ContentResponse response = httpClient.newRequest(host, serverConnector.getLocalPort())
-                    .scheme(HttpScheme.HTTPS.asString())
-                    .method(HttpMethod.GET)
-                    .path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
-                    .send();
-
-            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
-            String content = response.getContentAsString();
-            Assert.assertEquals(body, content);
-        }
-        finally
-        {
-            httpClient.stop();
-        }
-    }
-
-    @Test
-    @Ignore("External Proxy Server no longer stable enough for testing")
-    public void testExternalProxy() throws Exception
-    {
-        // Free proxy server obtained from http://hidemyass.com/proxy-list/
-        String proxyHost = "81.208.25.53";
-        int proxyPort = 3128;
-        try
-        {
-            new Socket(proxyHost, proxyPort).close();
-        }
-        catch (Throwable x)
-        {
-            Assume.assumeNoException(x);
-        }
-
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.start();
-
-        HttpClient httpClient = new HttpClient(sslContextFactory);
-        httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
-        httpClient.start();
-
-        try
-        {
-            ContentResponse response = httpClient.newRequest("https://www.google.com")
-                    // Use a longer timeout, sometimes the proxy takes a while to answer
-                    .timeout(20, TimeUnit.SECONDS)
-                    .send();
-            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
-        }
-        finally
-        {
-            httpClient.stop();
-        }
-    }
-
-    private static class ServerHandler extends AbstractHandler
-    {
-        public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
-        {
-            request.setHandled(true);
-
-            String uri = httpRequest.getRequestURI();
-            if ("/echo".equals(uri))
-            {
-                String body = httpRequest.getParameter("body");
-                ServletOutputStream output = httpResponse.getOutputStream();
-                output.print(body);
-            }
-            else
-            {
-                throw new ServletException();
-            }
-        }
-    }
-}
diff --git a/jetty-quickstart/README.txt b/jetty-quickstart/README.txt
deleted file mode 100644
index 43ad7f3..0000000
--- a/jetty-quickstart/README.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-mvn exec:java -Dexec.classpathScope=test -Dexec.mainClass="org.eclipse.jetty.quickstart.StartBenchmarkWar"
-
-OR
-
-mvn exec:java -Dexec.classpathScope=test -Dexec.mainClass="org.eclipse.jetty.quickstart.PreconfigureBenchmarkWar"
-mvn exec:java -Dexec.classpathScope=test -Dexec.mainClass="org.eclipse.jetty.quickstart.QuickStartBenchmarkWar"
diff --git a/jetty-quickstart/pom.xml b/jetty-quickstart/pom.xml
index 4bad197..079a20d 100644
--- a/jetty-quickstart/pom.xml
+++ b/jetty-quickstart/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.eclipse.jetty</groupId>
@@ -10,6 +10,9 @@
   <name>Jetty :: Quick Start</name>
   <description>Jetty Quick Start</description>
   <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.quickstart</bundle-symbolic-name>
+  </properties>
   <dependencies>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
@@ -79,26 +82,14 @@
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
+      <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
   <build>
     <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
     </plugins>
   </build>
 </project>
diff --git a/jetty-quickstart/src/main/config/etc/example-quickstart.xml b/jetty-quickstart/src/main/config/etc/example-quickstart.xml
index 3f02e60..ddeedf9 100644
--- a/jetty-quickstart/src/main/config/etc/example-quickstart.xml
+++ b/jetty-quickstart/src/main/config/etc/example-quickstart.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"  encoding="ISO-8859-1"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- An example context XML for a quickstart webapp
 A quick started webapp has all the jar scanning and fragment resolution done in a 
diff --git a/jetty-quickstart/src/main/config/modules/quickstart.mod b/jetty-quickstart/src/main/config/modules/quickstart.mod
index 89db9fd..4e59dd0 100644
--- a/jetty-quickstart/src/main/config/modules/quickstart.mod
+++ b/jetty-quickstart/src/main/config/modules/quickstart.mod
@@ -7,6 +7,5 @@
 plus
 annotations
 
-
 [lib]
 lib/jetty-quickstart-${jetty.version}.jar
diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/AttributeNormalizer.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/AttributeNormalizer.java
new file mode 100644
index 0000000..faa8d99
--- /dev/null
+++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/AttributeNormalizer.java
@@ -0,0 +1,472 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.quickstart;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+
+/**
+ * Normalize Attribute to String.
+ * <p>
+ * Replaces and expands:
+ * <ul>
+ * <li>${WAR}</li>
+ * <li>${WAR.path}</li>
+ * <li>${WAR.uri}</li>
+ * <li>${jetty.base}</li>
+ * <li>${jetty.base.uri}</li>
+ * <li>${jetty.home}</li>
+ * <li>${jetty.home.uri}</li>
+ * <li>${user.home}</li>
+ * <li>${user.home.uri}</li>
+ * <li>${user.dir}</li>
+ * <li>${user.dir.uri}</li>
+ * </ul>
+ */
+public class AttributeNormalizer
+{
+    private static final Logger LOG = Log.getLogger(AttributeNormalizer.class);
+    private static final Pattern __propertyPattern = Pattern.compile("(?<=[^$]|^)\\$\\{([^}]*)\\}");
+    
+    private static class Attribute
+    {
+        final String key;
+        final String value;
+        final int weight;
+        
+        public Attribute(String key, String value, int weight)
+        {
+            this.key = key;
+            this.value = value;
+            this.weight = weight;
+        }
+    }
+
+    private static URI toCanonicalURI(URI uri)
+    {
+        uri = uri.normalize();
+        String ascii = uri.toASCIIString();
+        if (ascii.endsWith("/"))
+        {
+            try
+            {
+                uri = new URI(ascii.substring(0,ascii.length()-1));
+            }
+            catch(URISyntaxException e)
+            {
+                throw new IllegalArgumentException(e);
+            }
+        }
+        return uri;
+    }
+    
+    private static Path toCanonicalPath(String path)
+    {
+        if (path == null)
+            return null;
+        if (path.length()>1 && path.endsWith("/"))
+            path = path.substring(0,path.length()-1);
+        return toCanonicalPath(FileSystems.getDefault().getPath(path));
+    }
+    
+    private static Path toCanonicalPath(Path path)
+    {
+        if (path == null)
+        {
+            return null;
+        }
+        if (Files.exists(path))
+        {
+            try
+            {
+                return path.toRealPath();
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException(e);
+            }
+        }
+        return path.toAbsolutePath();
+    }
+    
+    private static class PathAttribute extends Attribute
+    {
+        public final Path path;
+        
+        public PathAttribute(String key, Path path, int weight)
+        {
+            super(key,path.toString(),weight);
+            this.path = path;
+        }
+        
+        @Override
+        public String toString()
+        {
+            return String.format("PathAttribute[%s=>%s]",key,path);
+        }
+    }
+    
+    private static class URIAttribute extends Attribute
+    {
+        public final URI uri;
+        
+        public URIAttribute(String key, URI uri, int weight)
+        {
+            super(key,uri.toASCIIString(),weight);
+            this.uri = uri;
+        }
+        
+        @Override
+        public String toString()
+        {
+            return String.format("URIAttribute[%s=>%s]",key,uri);
+        }
+    }
+    
+    private static Comparator<Attribute> attrComparator = new Comparator<Attribute>()
+    {
+        @Override
+        public int compare(Attribute o1, Attribute o2)
+        {
+            if( (o1.value == null) && (o2.value != null) )
+            {
+                return -1;
+            }
+            
+            if( (o1.value != null) && (o2.value == null) )
+            {
+                return 1;
+            }
+            
+            if( (o1.value == null) && (o2.value == null) )
+            {
+                return 0;
+            }
+            
+            // Different lengths?
+            int diff = o2.value.length() - o1.value.length();
+            if(diff != 0)
+            {
+                return diff;
+            }
+            
+            // Different names?
+            diff = o2.value.compareTo(o1.value);
+            if(diff != 0)
+            {
+                return diff;
+            }
+            
+            // The paths are the same, base now on weight
+            return o2.weight - o1.weight;
+        }
+    };
+
+    private static void add(List<PathAttribute>paths,List<URIAttribute> uris,String key,int weight)
+    {
+        String value = System.getProperty(key);
+        if (value!=null)
+        {
+            Path path = toCanonicalPath(value);
+            paths.add(new PathAttribute(key,path,weight));
+            uris.add(new URIAttribute(key+".uri",toCanonicalURI(path.toUri()),weight));
+        }
+    }
+
+    private URI warURI;
+    private Map<String,Attribute> attributes = new HashMap<>();
+    private List<PathAttribute> paths = new ArrayList<>();
+    private List<URIAttribute> uris = new ArrayList<>();
+    
+    public AttributeNormalizer(Resource baseResource)
+    {
+        if (baseResource==null)
+            throw new IllegalArgumentException("No base resource!");
+            
+        warURI = toCanonicalURI(baseResource.getURI());
+        if (!warURI.isAbsolute())
+            throw new IllegalArgumentException("WAR URI is not absolute: " + warURI);
+        
+        add(paths,uris,"jetty.base",9);
+        add(paths,uris,"jetty.home",8);
+        add(paths,uris,"user.home",7);
+        add(paths,uris,"user.dir",6);
+
+        if (warURI.getScheme().equalsIgnoreCase("file"))
+            paths.add(new PathAttribute("WAR.path",toCanonicalPath(new File(warURI).toString()),10));
+        uris.add(new URIAttribute("WAR.uri", warURI,9)); // preferred encoding
+        uris.add(new URIAttribute("WAR", warURI,8)); // legacy encoding
+        
+        Collections.sort(paths,attrComparator);
+        Collections.sort(uris,attrComparator);
+        
+        Stream.concat(paths.stream(),uris.stream()).forEach(a->attributes.put(a.key,a));        
+        
+        if (LOG.isDebugEnabled())
+        {
+            for (Attribute attr : attributes.values())
+            {
+                LOG.debug(attr.toString());
+            }
+        }
+    }
+
+    /**
+     * Normalize a URI, URL, or File reference by replacing known attributes with ${key} attributes.
+     *
+     * @param o the object to normalize into a string
+     * @return the string representation of the object, with expansion keys.
+     */
+    public String normalize(Object o)
+    {
+        try
+        {
+            // Find a URI
+            URI uri = null;
+            Path path = null;
+            if (o instanceof URI)
+                uri = toCanonicalURI(((URI)o));
+            else if (o instanceof Resource)
+                uri = toCanonicalURI(((Resource)o).getURI());
+            else if (o instanceof URL)
+                uri = toCanonicalURI(((URL)o).toURI());
+            else if (o instanceof File)
+                path = ((File)o).getAbsoluteFile().getCanonicalFile().toPath();
+            else if (o instanceof Path)
+                path = (Path)o;
+            else
+            {
+                String s = o.toString();
+                try
+                {
+                    uri = new URI(s);
+                    if (uri.getScheme() == null)
+                    {
+                        // Unknown scheme? not relevant to normalize
+                        return s;
+                    }
+                }
+                catch(URISyntaxException e)
+                {
+                    // This path occurs for many reasons, but most common is when this
+                    // is executed on MS Windows, on a string like "D:\jetty"
+                    // and the new URI() fails for
+                    // java.net.URISyntaxException: Illegal character in opaque part at index 2: D:\jetty
+                    return s;
+                }
+            }
+
+            if (uri!=null)
+            {
+                if ("jar".equalsIgnoreCase(uri.getScheme()))
+                {
+                    String raw = uri.getRawSchemeSpecificPart();
+                    int bang = raw.indexOf("!/");
+                    String normal = normalize(raw.substring(0,bang));
+                    String suffix = raw.substring(bang);
+                    return "jar:" + normal + suffix;
+                }
+                else
+                {
+                    if(uri.isAbsolute())
+                    {
+                        return normalizeUri(uri);
+                    }
+                }
+            }
+            else if (path!=null)
+                return normalizePath(path);
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+        }
+        return String.valueOf(o);
+    }
+    
+    protected String normalizeUri(URI uri)
+    {
+        for (URIAttribute a : uris)
+        {
+            try
+            {
+                if (uri.compareTo(a.uri)==0)
+                    return String.format("${%s}",a.key);
+
+                if (!a.uri.getScheme().equalsIgnoreCase(uri.getScheme()))
+                    continue;
+                if (a.uri.getHost()==null && uri.getHost()!=null)
+                    continue;
+                if (a.uri.getHost()!=null && !a.uri.getHost().equals(uri.getHost()))
+                    continue;
+
+                if (a.uri.getPath().equals(uri.getPath()))
+                    return a.value;
+
+                if (!uri.getPath().startsWith(a.uri.getPath()))
+                    continue;
+
+                String s = uri.getPath().substring(a.uri.getPath().length());
+
+                if (s.charAt(0)!='/')
+                    continue;
+
+                return String.format("${%s}%s",a.key,new URI(s).toASCIIString());
+            }
+            catch(URISyntaxException e)
+            {
+                LOG.ignore(e);
+            }
+        }
+        return uri.toASCIIString();
+    }
+
+    protected String normalizePath(Path path)
+    {
+        for (PathAttribute a : paths)
+        {
+            try
+            {
+                if (path.equals(a.path) || Files.isSameFile(path,a.path))
+                    return String.format("${%s}",a.key);
+            }
+            catch (IOException ignore)
+            {
+                LOG.ignore(ignore);
+            }
+
+            if (path.startsWith(a.path))
+                return String.format("${%s}%c%s",a.key,File.separatorChar,a.path.relativize(path).toString());
+        }
+
+        return path.toString();
+    }
+
+    public String expand(String str)
+    {
+        return expand(str,new Stack<String>());
+    }
+
+    public String expand(String str, Stack<String> seenStack)
+    {
+        if (str == null)
+        {
+            return str;
+        }
+
+        if (str.indexOf("${") < 0)
+        {
+            // Contains no potential expressions.
+            return str;
+        }
+
+        Matcher mat = __propertyPattern.matcher(str);
+        StringBuilder expanded = new StringBuilder();
+        int offset = 0;
+        String property;
+        String value;
+
+        while (mat.find(offset))
+        {
+            property = mat.group(1);
+
+            // Loop detection
+            if (seenStack.contains(property))
+            {
+                StringBuilder err = new StringBuilder();
+                err.append("Property expansion loop detected: ");
+                int idx = seenStack.lastIndexOf(property);
+                for (int i = idx; i < seenStack.size(); i++)
+                {
+                    err.append(seenStack.get(i));
+                    err.append(" -> ");
+                }
+                err.append(property);
+                throw new RuntimeException(err.toString());
+            }
+
+            seenStack.push(property);
+
+            // find property name
+            expanded.append(str.subSequence(offset,mat.start()));
+            // get property value
+            value = getString(property);
+            if (value == null)
+            {
+                if(LOG.isDebugEnabled())
+                    LOG.debug("Unable to expand: {}",property);
+                expanded.append(mat.group());
+            }
+            else
+            {
+                // recursively expand
+                value = expand(value,seenStack);
+                expanded.append(value);
+            }
+            // update offset
+            offset = mat.end();
+        }
+
+        // leftover
+        expanded.append(str.substring(offset));
+
+        // special case for "$$"
+        if (expanded.indexOf("$$") >= 0)
+        {
+            return expanded.toString().replaceAll("\\$\\$","\\$");
+        }
+
+        return expanded.toString();
+    }
+
+    private String getString(String property)
+    {
+        if(property==null)
+        {
+            return null;
+        }
+
+        Attribute a = attributes.get(property);
+        if (a!=null)
+            return a.value;
+
+        // Use system properties next
+        return System.getProperty(property);
+    }
+}
diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java
index 062158a..543e8ad 100644
--- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java
+++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java
@@ -31,8 +31,7 @@
 {
     private static final Logger LOG = Log.getLogger(PreconfigureQuickStartWar.class);
     static final boolean ORIGIN=LOG.isDebugEnabled();
-
-
+    
     public static void main(String... args) throws Exception
     {
         Resource war = null;
@@ -47,6 +46,7 @@
 
             case 1:
                 dir = Resource.newResource(args[0]);
+                break;
 
             case 2:
                 war = Resource.newResource(args[0]);
@@ -74,7 +74,7 @@
                 break;
         }
 
-
+        
         preconfigure(war,dir,xml);
     }
 
@@ -82,9 +82,9 @@
      * @param war The war (or directory) to preconfigure
      * @param dir The directory to expand the war into (or null if war is a directory)
      * @param xml A context XML to apply (or null if none)
-     * @throws Exception
+     * @throws Exception if unable to pre configure
      */
-    public static void preconfigure(Resource war, Resource dir, Resource xml) throws Exception
+    public static void preconfigure(Resource war, Resource dir, Resource xml) throws Exception 
     {
         // Do we need to unpack a war?
         if (war != null)
@@ -96,7 +96,7 @@
                 dir.getFile().mkdirs();
             JarResource.newJarResource(war).copyTo(dir.getFile());
         }
-
+        
         final Server server = new Server();
 
         QuickStartWebApp webapp = new QuickStartWebApp();
@@ -114,9 +114,9 @@
         server.start();
         server.stop();
     }
-
-
-
+    
+    
+    
 
     private static void error(String message)
     {
diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java
index 504f702..5abd8f0 100644
--- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java
+++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java
@@ -31,10 +31,9 @@
 
 /**
  * QuickStartConfiguration
- * 
+ * <p> 
  * Re-inflate a deployable webapp from a saved effective-web.xml
  * which combines all pre-parsed web xml descriptors and annotations.
- * 
  */
 public class QuickStartConfiguration extends WebInfConfiguration
 {
@@ -57,7 +56,7 @@
         Resource webApp = context.newResource(war);
 
         // Accept aliases for WAR files
-        if (webApp.getAlias() != null)
+        if (webApp.isAlias())
         {
             LOG.debug(webApp + " anti-aliased to " + webApp.getAlias());
             webApp = context.newResource(webApp.getAlias());
@@ -83,9 +82,9 @@
     /**
      * Get the quickstart-web.xml file as a Resource.
      * 
-     * @param context
-     * @return
-     * @throws Exception
+     * @param context the web app context
+     * @return the Resource for the quickstart-web.xml
+     * @throws Exception if unable to find the quickstart xml
      */
     public Resource getQuickStartWebXml (WebAppContext context) throws Exception
     {
@@ -138,7 +137,7 @@
         context.getMetaData().addDescriptorProcessor(new QuickStartDescriptorProcessor());
         
         //add a decorator that will find introspectable annotations
-        context.addDecorator(new AnnotationDecorator(context)); //this must be the last Decorator because they are run in reverse order!
+        context.getObjectFactory().addDecorator(new AnnotationDecorator(context)); //this must be the last Decorator because they are run in reverse order!
         
         //add a context bean that will run ServletContainerInitializers as the context starts
         ServletContainerInitializersStarter starter = (ServletContainerInitializersStarter)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZER_STARTER);
diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorGenerator.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorGenerator.java
index bc02018..b9380e2 100644
--- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorGenerator.java
+++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorGenerator.java
@@ -22,6 +22,9 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EventListener;
@@ -48,13 +51,15 @@
 import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.FilterMapping;
+import org.eclipse.jetty.servlet.ServletContextHandler.JspConfig;
 import org.eclipse.jetty.servlet.ServletHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.servlet.ServletMapping;
-import org.eclipse.jetty.servlet.ServletContextHandler.JspConfig;
 import org.eclipse.jetty.util.QuotedStringTokenizer;
+import org.eclipse.jetty.util.StringUtil;
 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.Constraint;
 import org.eclipse.jetty.webapp.MetaData;
 import org.eclipse.jetty.webapp.MetaData.OriginInfo;
@@ -62,11 +67,9 @@
 import org.eclipse.jetty.webapp.WebAppContext;
 import org.eclipse.jetty.xml.XmlAppendable;
 
-
-
 /**
  * QuickStartDescriptorGenerator
- *
+ * <p>
  * Generate an effective web.xml from a WebAppContext, including all components 
  * from web.xml, web-fragment.xmls annotations etc.
  */
@@ -74,10 +77,15 @@
 {
     private static final Logger LOG = Log.getLogger(QuickStartDescriptorGenerator.class);
     
+    public static final String ORIGIN = "org.eclipse.jetty.originAttribute";
     public static final String DEFAULT_QUICKSTART_DESCRIPTOR_NAME = "quickstart-web.xml";
+    public static final String DEFAULT_ORIGIN_ATTRIBUTE_NAME = "origin";
     
     protected WebAppContext _webApp;
     protected String _extraXML;
+    protected String _originAttribute;
+    protected boolean _generateOrigin;
+    protected int _count;
   
     
     
@@ -85,18 +93,21 @@
      * @param w the source WebAppContext
      * @param extraXML any extra xml snippet to append
      */
-    public QuickStartDescriptorGenerator (WebAppContext w,  String extraXML)
+    public QuickStartDescriptorGenerator (WebAppContext w,  String extraXML, String originAttribute, boolean generateOrigin)
     {
         _webApp = w;
         _extraXML = extraXML;
+        _originAttribute = (StringUtil.isBlank(originAttribute)?DEFAULT_ORIGIN_ATTRIBUTE_NAME:originAttribute);
+        _generateOrigin = generateOrigin || LOG.isDebugEnabled();
+        _count = 0;
     }
     
     
     /**
      * Perform the generation of the xml file
-     * @throws IOException 
-     * @throws FileNotFoundException 
-     * @throws Exception
+     * @param stream the stream to generate the quickstart-web.xml to
+     * @throws IOException if unable to generate the quickstart-web.xml
+     * @throws FileNotFoundException if unable to find the file 
      */
     public void generateQuickStartWebXml (OutputStream stream) throws FileNotFoundException, IOException 
     {   
@@ -130,18 +141,28 @@
         // Set some special context parameters
 
         // The location of the war file on disk
-        String resourceBase = _webApp.getBaseResource().getFile().getCanonicalFile().getAbsoluteFile().toURI().toString();
+        AttributeNormalizer normalizer = new AttributeNormalizer(_webApp.getBaseResource());
 
         // The library order
         addContextParamFromAttribute(out,ServletContext.ORDERED_LIBS);
         //the servlet container initializers
         addContextParamFromAttribute(out,AnnotationConfiguration.CONTAINER_INITIALIZERS);
         //the tlds discovered
-        addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_TLDS,resourceBase);
+        addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_TLDS,normalizer);
         //the META-INF/resources discovered
-        addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_RESOURCES,resourceBase);
+        addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_RESOURCES,normalizer);
 
 
+        //add the name of the origin attribute, if it is being used
+        if (_generateOrigin)
+        {
+            out.openTag("context-param")
+            .tag("param-name", ORIGIN)
+            .tag("param-value", _originAttribute)
+            .closeTag();    
+        }
+        
+        
         // init params
         for (String p : _webApp.getInitParams().keySet())
             out.openTag("context-param",origin(md,"context-param." + p))
@@ -517,19 +538,6 @@
      */
     private void addContextParamFromAttribute(XmlAppendable out, String attribute) throws IOException
     {
-        addContextParamFromAttribute(out,attribute,null);
-    }
-    
-    /**
-     * Turn context attribute into context-param to store.
-     * 
-     * @param out
-     * @param attribute
-     * @param resourceBase
-     * @throws IOException
-     */
-    private void addContextParamFromAttribute(XmlAppendable out, String attribute, String resourceBase) throws IOException
-    {
         Object o = _webApp.getAttribute(attribute);
         if (o == null)
             return;
@@ -544,10 +552,7 @@
                     v.append(",\n    ");
                 else
                     v.append("\n    ");
-                if (resourceBase==null)
-                    QuotedStringTokenizer.quote(v,i.toString());
-                else
-                    QuotedStringTokenizer.quote(v,i.toString().replace(resourceBase,"${WAR}/"));
+                QuotedStringTokenizer.quote(v,i.toString());
             }
         }
         out.openTag("context-param")
@@ -555,6 +560,40 @@
         .tagCDATA("param-value",v.toString())
         .closeTag();        
     }
+    
+    /**
+     * Turn context attribute into context-param to store.
+     * 
+     * @param out
+     * @param attribute
+     * @param resourceBase
+     * @throws IOException
+     */
+    private void addContextParamFromAttribute(XmlAppendable out, String attribute, AttributeNormalizer normalizer) throws IOException
+    {
+        Object o = _webApp.getAttribute(attribute);
+        if (o == null)
+            return;
+        
+        Collection<?> c =  (o instanceof Collection)? (Collection<?>)o:Collections.singletonList(o);
+        StringBuilder v=new StringBuilder();
+        for (Object i:c)
+        {
+            if (i!=null)
+            {
+                if (v.length()>0)
+                    v.append(",\n    ");
+                else
+                    v.append("\n    ");
+                QuotedStringTokenizer.quote(v,normalizer.normalize(i));
+            }
+        }
+        out.openTag("context-param")
+        .tag("param-name",attribute)
+        .tagCDATA("param-value",v.toString())
+        .closeTag();  
+        
+    }
 
     /**
      * Generate xml for a Holder (Filter/Servlet)
@@ -668,13 +707,13 @@
     /**
      * Find the origin (web.xml, fragment, annotation etc) of a web artifact from MetaData.
      * 
-     * @param md
-     * @param name
-     * @return
+     * @param md the metadata
+     * @param name the name
+     * @return the origin map
      */
     public Map<String, String> origin(MetaData md, String name)
     {
-        if (!LOG.isDebugEnabled())
+        if (!_generateOrigin)
             return Collections.emptyMap();
         if (name == null)
             return Collections.emptyMap();
@@ -682,7 +721,7 @@
         if (LOG.isDebugEnabled()) LOG.debug("origin of "+name+" is "+origin);
         if (origin == null)
             return Collections.emptyMap();
-        return Collections.singletonMap("origin",origin.toString());
+        return Collections.singletonMap(_originAttribute,origin.toString()+":"+(_count++));
     }
      
 }
diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorProcessor.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorProcessor.java
index b4f6446..6b6a6bd 100644
--- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorProcessor.java
+++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorProcessor.java
@@ -28,9 +28,12 @@
 import org.eclipse.jetty.annotations.AnnotationConfiguration;
 import org.eclipse.jetty.annotations.ServletContainerInitializersStarter;
 import org.eclipse.jetty.plus.annotation.ContainerInitializer;
+import org.eclipse.jetty.servlet.ServletMapping;
 import org.eclipse.jetty.util.QuotedStringTokenizer;
+import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.util.resource.ResourceCollection;
+import org.eclipse.jetty.webapp.DefaultsDescriptor;
 import org.eclipse.jetty.webapp.Descriptor;
 import org.eclipse.jetty.webapp.IterativeDescriptorProcessor;
 import org.eclipse.jetty.webapp.MetaInfConfiguration;
@@ -44,6 +47,9 @@
  */
 public class QuickStartDescriptorProcessor extends IterativeDescriptorProcessor
 {
+    
+    private String _originAttributeName = null;
+    
     /**
      * 
      */
@@ -52,6 +58,7 @@
         try
         {
             registerVisitor("context-param", this.getClass().getMethod("visitContextParam", __signature));
+            registerVisitor("servlet-mapping", this.getClass().getMethod("visitServletMapping", __signature));
         }    
         catch (Exception e)
         {
@@ -65,6 +72,7 @@
     @Override
     public void start(WebAppContext context, Descriptor descriptor)
     {
+        _originAttributeName = context.getInitParameter(QuickStartDescriptorGenerator.ORIGIN);
     }
 
     /**
@@ -73,13 +81,49 @@
     @Override
     public void end(WebAppContext context, Descriptor descriptor)
     { 
+        _originAttributeName = null;
     }
     
+    
+    /**
+     * Process a servlet-mapping element
+     * 
+     * @param context the webapp
+     * @param descriptor the xml file to process
+     * @param node the servlet-mapping element in the xml file to process
+     */
+    public void visitServletMapping(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
+    {
+        String servletName = node.getString("servlet-name", false, true);
+        ServletMapping mapping = null;
+        ServletMapping[] mappings = context.getServletHandler().getServletMappings();
+
+        if (mappings != null)
+        {
+            for (ServletMapping m:mappings)
+            {
+                if (servletName.equals(m.getServletName()))
+                {
+                    mapping = m;
+                    break;
+                }
+            }
+        }
+        
+        if (mapping != null && _originAttributeName != null)
+        {
+            String origin = node.getAttribute(_originAttributeName);
+            if (!StringUtil.isBlank(origin) && origin.startsWith(DefaultsDescriptor.class.getSimpleName()))
+                mapping.setDefault(true);
+        }
+    }
 
     /**
-     * @param context
-     * @param descriptor
-     * @param node
+     * Process a context-param element
+     * @param context  the webapp 
+     * @param descriptor the xml file to process
+     * @param node the context-param node in the xml file
+     * @throws Exception
      */
     public void visitContextParam (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
             throws Exception
@@ -91,11 +135,16 @@
         // extract values
         switch(name)
         {
+            case QuickStartDescriptorGenerator.ORIGIN:
+            {
+                //value already contains what we need
+                break;
+            }
             case ServletContext.ORDERED_LIBS:
             case AnnotationConfiguration.CONTAINER_INITIALIZERS:
             case MetaInfConfiguration.METAINF_TLDS:
             case MetaInfConfiguration.METAINF_RESOURCES:
-
+            {
                 context.removeAttribute(name);
                 
                 QuotedStringTokenizer tok = new QuotedStringTokenizer(value,",");
@@ -103,14 +152,20 @@
                     values.add(tok.nextToken().trim());
                 
                 break;
-                
+            }
             default:
                 values.add(value);
         }
 
+        AttributeNormalizer normalizer = new AttributeNormalizer(context.getBaseResource());
         // handle values
         switch(name)
         {
+            case QuickStartDescriptorGenerator.ORIGIN:
+            {
+                context.setAttribute(QuickStartDescriptorGenerator.ORIGIN, value);
+                break;
+            }
             case ServletContext.ORDERED_LIBS:
             {
                 List<Object> libs = new ArrayList<>();
@@ -134,30 +189,29 @@
             case MetaInfConfiguration.METAINF_TLDS:
             {
                 List<Object> tlds = new ArrayList<>();
-                String war=context.getBaseResource().getURI().toString();
                 Object o=context.getAttribute(MetaInfConfiguration.METAINF_TLDS);
                 if (o instanceof Collection<?>)
                     tlds.addAll((Collection<?>)o);
                 for (String i : values)
                 {
-                    Resource r = Resource.newResource(i.replace("${WAR}/",war));
+                    Resource r = Resource.newResource(normalizer.expand(i));
                     if (r.exists())
-                        tlds.add(r.getURL());
+                        tlds.add(r.getURI().toURL());
                     else
                         throw new IllegalArgumentException("TLD not found: "+r);                    
                 }
-                
-                if (tlds.size()>0)
-                    context.setAttribute(MetaInfConfiguration.METAINF_TLDS,tlds);
+
+                //empty list signals that tlds were prescanned but none found.
+                //a missing METAINF_TLDS attribute means that prescanning was not done.
+                context.setAttribute(MetaInfConfiguration.METAINF_TLDS,tlds);
                 break;
             }
             
             case MetaInfConfiguration.METAINF_RESOURCES:
             {
-                String war=context.getBaseResource().getURI().toString();
                 for (String i : values)
                 {
-                    Resource r = Resource.newResource(i.replace("${WAR}/",war));
+                    Resource r = Resource.newResource(normalizer.expand(i));
                     if (r.exists())
                         visitMetaInfResource(context,r); 
                     else
diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java
index 78aa2baa248..89af1b9 100644
--- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java
+++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java
@@ -29,45 +29,42 @@
 
 /**
  * QuickStartWar
- *
  */
 public class QuickStartWebApp extends WebAppContext
 {
     private static final Logger LOG = Log.getLogger(QuickStartWebApp.class);
-
-
-
-    public static final String[] __configurationClasses = new String[]
+    
+    public static final String[] __configurationClasses = new String[] 
             {
                 org.eclipse.jetty.quickstart.QuickStartConfiguration.class.getCanonicalName(),
                 org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(),
                 org.eclipse.jetty.plus.webapp.PlusConfiguration.class.getCanonicalName(),
                 org.eclipse.jetty.webapp.JettyWebXmlConfiguration.class.getCanonicalName()
             };
-
-
+    
     private boolean _preconfigure=false;
     private boolean _autoPreconfigure=false;
     private boolean _startWebapp=false;
     private PreconfigureDescriptorProcessor _preconfigProcessor;
-
-
+    private String _originAttribute;
+    private boolean _generateOrigin;
+    
+    
     public static final String[] __preconfigurationClasses = new String[]
-    {
-        org.eclipse.jetty.webapp.WebInfConfiguration.class.getCanonicalName(),
+    { 
+        org.eclipse.jetty.webapp.WebInfConfiguration.class.getCanonicalName(), 
         org.eclipse.jetty.webapp.WebXmlConfiguration.class.getCanonicalName(),
-        org.eclipse.jetty.webapp.MetaInfConfiguration.class.getCanonicalName(),
+        org.eclipse.jetty.webapp.MetaInfConfiguration.class.getCanonicalName(), 
         org.eclipse.jetty.webapp.FragmentConfiguration.class.getCanonicalName(),
-        org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(),
+        org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(), 
         org.eclipse.jetty.plus.webapp.PlusConfiguration.class.getCanonicalName(),
         org.eclipse.jetty.annotations.AnnotationConfiguration.class.getCanonicalName(),
     };
-
+    
     public QuickStartWebApp()
     {
         super();
         setConfigurationClasses(__preconfigurationClasses);
-        setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",".*\\.jar");
     }
 
     public boolean isPreconfigure()
@@ -75,9 +72,9 @@
         return _preconfigure;
     }
 
-    /* ------------------------------------------------------------ */
-    /** Preconfigure webapp
-     * @param preconfigure  If true, then starting the webapp will generate
+    /** 
+     * Preconfigure webapp
+     * @param preconfigure  If true, then starting the webapp will generate 
      * the WEB-INF/quickstart-web.xml rather than start the webapp.
      */
     public void setPreconfigure(boolean preconfigure)
@@ -89,22 +86,60 @@
     {
         return _autoPreconfigure;
     }
-
+    
     public void setAutoPreconfigure(boolean autoPrecompile)
     {
         _autoPreconfigure = autoPrecompile;
     }
+    
+    public void setOriginAttribute (String name)
+    {
+        _originAttribute = name;
+    }
+    
+    /**
+     * @return the originAttribute
+     */
+    public String getOriginAttribute()
+    {
+        return _originAttribute;
+    }
+
+    /**
+     * @return the generateOrigin
+     */
+    public boolean getGenerateOrigin()
+    {
+        return _generateOrigin;
+    }
+
+    /**
+     * @param generateOrigin the generateOrigin to set
+     */
+    public void setGenerateOrigin(boolean generateOrigin)
+    {
+        _generateOrigin = generateOrigin;
+    }
 
     @Override
     protected void startWebapp() throws Exception
     {
         if (isPreconfigure())
             generateQuickstartWebXml(_preconfigProcessor.getXML());
-
+        
         if (_startWebapp)
             super.startWebapp();
     }
-
+    
+    @Override
+    protected void stopWebapp() throws Exception
+    {
+        if (!_startWebapp)
+            return;
+        
+        super.stopWebapp();
+    }
+    
     @Override
     protected void doStart() throws Exception
     {
@@ -125,7 +160,7 @@
             dir=Resource.newResource(w.substring(0,w.length()-4));
 
             if (!dir.exists())
-            {
+            {                       
                 LOG.info("Quickstart Extract " + war + " to " + dir);
                 dir.getFile().mkdirs();
                 JarResource.newJarResource(war).copyTo(dir.getFile());
@@ -134,12 +169,12 @@
             setWar(null);
             setBaseResource(dir);
         }
-        else
+        else 
             throw new IllegalArgumentException();
 
 
         Resource qswebxml=dir.addPath("/WEB-INF/quickstart-web.xml");
-
+        
         if (isPreconfigure())
         {
             _preconfigProcessor = new PreconfigureDescriptorProcessor();
@@ -152,32 +187,29 @@
             _startWebapp=true;
         }
         else if (_autoPreconfigure)
-        {
+        {   
             LOG.info("Quickstart preconfigure: {}(war={},dir={})",this,war,dir);
 
-            _preconfigProcessor = new PreconfigureDescriptorProcessor();
+            _preconfigProcessor = new PreconfigureDescriptorProcessor();    
             getMetaData().addDescriptorProcessor(_preconfigProcessor);
             setPreconfigure(true);
             _startWebapp=true;
         }
         else
             _startWebapp=true;
-
+            
         super.doStart();
     }
 
-
     public void generateQuickstartWebXml(String extraXML) throws Exception
     {
         Resource descriptor = getWebInf().addPath(QuickStartDescriptorGenerator.DEFAULT_QUICKSTART_DESCRIPTOR_NAME);
         if (!descriptor.exists())
             descriptor.getFile().createNewFile();
-        QuickStartDescriptorGenerator generator = new QuickStartDescriptorGenerator(this, extraXML);
+        QuickStartDescriptorGenerator generator = new QuickStartDescriptorGenerator(this, extraXML, _originAttribute, _generateOrigin);
         try (FileOutputStream fos = new FileOutputStream(descriptor.getFile()))
         {
             generator.generateQuickStartWebXml(fos);
         }
-    }
-
-
+    } 
 }
diff --git a/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/FooContextListener.java b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/FooContextListener.java
new file mode 100644
index 0000000..f6bf7ef
--- /dev/null
+++ b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/FooContextListener.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.quickstart;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.Set;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRegistration;
+
+/**
+ * FooContextListener
+ *
+ *
+ */
+public class FooContextListener implements ServletContextListener
+{
+    @Override
+    public void contextInitialized(ServletContextEvent sce)
+    {
+        ServletRegistration defaultRego = sce.getServletContext().getServletRegistration("default");
+        Collection<String> mappings = defaultRego.getMappings();
+        assertTrue(mappings.contains("/"));
+        
+        Set<String> otherMappings = sce.getServletContext().getServletRegistration("foo").addMapping("/");
+        assertTrue(otherMappings.isEmpty());
+        Collection<String> fooMappings = sce.getServletContext().getServletRegistration("foo").getMappings();
+        assertTrue(fooMappings.contains("/"));
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent sce)
+    {
+        // TODO Auto-generated method stub
+        
+    }
+}
\ No newline at end of file
diff --git a/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/FooServlet.java b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/FooServlet.java
new file mode 100644
index 0000000..8f84548
--- /dev/null
+++ b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/FooServlet.java
@@ -0,0 +1,42 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.quickstart;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class FooServlet extends HttpServlet
+{
+
+    /** 
+     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+    {
+        resp.setContentType("text/html");
+        resp.getWriter().println("FOO");
+    }
+    
+}
\ No newline at end of file
diff --git a/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/TestQuickStart.java b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/TestQuickStart.java
new file mode 100644
index 0000000..e3fd862
--- /dev/null
+++ b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/TestQuickStart.java
@@ -0,0 +1,99 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.quickstart;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.nio.file.Path;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * TestQuickStart
+ *
+ *
+ */
+public class TestQuickStart
+{
+    File testDir;
+    File webInf;
+    
+    
+    @Before
+    public void setUp()
+    {
+        testDir = MavenTestingUtils.getTargetTestingDir("foo");
+        FS.ensureEmpty(testDir); 
+        webInf = new File(testDir, "WEB-INF");
+        FS.ensureDirExists(webInf);
+    }
+    
+
+    
+    @Test
+    public void testProgrammaticOverrideOfDefaultServletMapping() throws Exception
+    {
+        
+        File quickstartXml = new File(webInf, "quickstart-web.xml");
+        assertFalse(quickstartXml.exists());
+        
+        Server server = new Server();
+        
+        //generate a quickstart-web.xml
+        QuickStartWebApp quickstart = new QuickStartWebApp();
+        quickstart.setResourceBase(testDir.getAbsolutePath());
+        quickstart.setPreconfigure(true);
+        quickstart.setGenerateOrigin(true);
+        ServletHolder fooHolder = new ServletHolder();
+        fooHolder.setServlet(new FooServlet());
+        fooHolder.setName("foo");
+        quickstart.getServletHandler().addServlet(fooHolder);   
+        quickstart.addEventListener(new FooContextListener());      
+        server.setHandler(quickstart);
+        server.start();
+        server.stop();
+        
+        assertTrue(quickstartXml.exists());
+        
+        //now run the webapp again purely from the generated quickstart
+        QuickStartWebApp webapp = new QuickStartWebApp();
+        webapp.setResourceBase(testDir.getAbsolutePath());
+        webapp.setPreconfigure(false);
+        webapp.setClassLoader(Thread.currentThread().getContextClassLoader()); //only necessary for junit testing
+        server.setHandler(webapp);
+        
+        server.start();
+        
+        //verify that FooServlet is now mapped to / and not the DefaultServlet
+        ServletHolder sh = webapp.getServletHandler().getHolderEntry("/").getValue();
+        assertNotNull(sh);
+        assertEquals("foo", sh.getName());
+        server.stop();
+    }
+
+}
diff --git a/jetty-rewrite/pom.xml b/jetty-rewrite/pom.xml
index 015bbfd..86e3784 100644
--- a/jetty-rewrite/pom.xml
+++ b/jetty-rewrite/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-rewrite</artifactId>
@@ -15,52 +15,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",*</Import-Package>
-              </instructions>
-            </configuration>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-rewrite/src/main/config/etc/jetty-rewrite-customizer.xml b/jetty-rewrite/src/main/config/etc/jetty-rewrite-customizer.xml
new file mode 100644
index 0000000..f8584e3
--- /dev/null
+++ b/jetty-rewrite/src/main/config/etc/jetty-rewrite-customizer.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+<Configure id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
+  <!-- =========================================================== -->
+  <!-- configure rewrite rule container as a customizer            -->
+  <!-- =========================================================== -->
+  <Call name="addCustomizer">
+    <Arg>
+      <New id="Rewrite" class="org.eclipse.jetty.rewrite.RewriteCustomizer">
+        <Set name="rewriteRequestURI"><Property name="jetty.rewrite.rewriteRequestURI" default="true"/></Set>
+        <Set name="rewritePathInfo"><Property name="jetty.rewrite.rewritePathInfo" default="true"/></Set>
+        <Set name="originalPathAttribute"><Property name="jetty.rewrite.originalPathAttribute" default="requestedPath"/></Set>
+      </New>
+    </Arg>
+  </Call>  
+</Configure>
+
diff --git a/jetty-rewrite/src/main/config/etc/jetty-rewrite.xml b/jetty-rewrite/src/main/config/etc/jetty-rewrite.xml
index 706a932..cb6e2b0 100644
--- a/jetty-rewrite/src/main/config/etc/jetty-rewrite.xml
+++ b/jetty-rewrite/src/main/config/etc/jetty-rewrite.xml
@@ -1,41 +1,31 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<!-- =============================================================== -->
-<!-- Mixin the RewriteHandler                                        -->
-<!-- =============================================================== -->
-
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
-    <!-- =========================================================== -->
-    <!-- configure rewrite handler                                   -->
-    <!-- =========================================================== -->
-    <Get id="oldhandler" name="handler"/>
+  <!-- =========================================================== -->
+  <!-- configure rewrite handler                                   -->
+  <!-- =========================================================== -->
+  <Call name="insertHandler">
+    <Arg>
+      <New class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
+	<Set name="rewriteRequestURI"><Property name="jetty.rewrite.rewriteRequestURI" deprecated="rewrite.rewriteRequestURI" default="true"/></Set>
+	<Set name="rewritePathInfo"><Property name="jetty.rewrite.rewritePathInfo" deprecated="rewrite.rewritePathInfo" default="false"/></Set>
+	<Set name="originalPathAttribute"><Property name="jetty.rewrite.originalPathAttribute" deprecated="rewrite.originalPathAttribute" default="requestedPath"/></Set>
+     
+	<!-- Set DispatcherTypes  -->
+	<Set name="dispatcherTypes">
+	  <Array type="javax.servlet.DispatcherType">
+	    <Item><Call class="javax.servlet.DispatcherType" name="valueOf"><Arg>REQUEST</Arg></Call></Item>
+	    <Item><Call class="javax.servlet.DispatcherType" name="valueOf"><Arg>ASYNC</Arg></Call></Item>
+	  </Array>
+	</Set>
 
-    <Set name="handler">
-     <New id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
-      <Set name="handler"><Ref refid="oldhandler"/></Set>
-      <Set name="rewriteRequestURI"><Property name="rewrite.rewriteRequestURI" default="true"/></Set>
-      <Set name="rewritePathInfo"><Property name="rewrite.rewritePathInfo" default="false"/></Set>
-      <Set name="originalPathAttribute"><Property name="rewrite.originalPathAttribute" default="requestedPath"/></Set>
-     </New>
-    </Set>
+        <Get id="Rewrite" name="ruleContainer"/>
+        
+	<!-- see rewrite-compactpath.xml for example how to add a rule -->
 
-    <!-- example rule -->
-    <!--
-    <Call name="addRule">
-      <Arg>
-	<New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
-	  <Set name="pattern">/favicon.ico</Set>
-	  <Set name="name">Cache-Control</Set>
-	  <Set name="value">Max-Age=3600,public</Set>
-	  <Set name="terminating">true</Set>
-	</New>
-      </Arg>
-    </Call>
-    -->
-
-    <!-- for example rules see jetty-demo.xml -->
-
+      </New>
+    </Arg>
+  </Call>
 </Configure>
diff --git a/jetty-rewrite/src/main/config/etc/rewrite-compactpath.xml b/jetty-rewrite/src/main/config/etc/rewrite-compactpath.xml
new file mode 100644
index 0000000..7cd53e6
--- /dev/null
+++ b/jetty-rewrite/src/main/config/etc/rewrite-compactpath.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+<Configure id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RuleContainer">
+  <Call name="addRule">
+    <Arg>
+      <New class="org.eclipse.jetty.rewrite.handler.CompactPathRule"/>
+    </Arg>
+  </Call>
+</Configure>
+
diff --git a/jetty-rewrite/src/main/config/modules/rewrite-compactpath.mod b/jetty-rewrite/src/main/config/modules/rewrite-compactpath.mod
new file mode 100644
index 0000000..a02e732
--- /dev/null
+++ b/jetty-rewrite/src/main/config/modules/rewrite-compactpath.mod
@@ -0,0 +1,10 @@
+#
+# Jetty Rewrite CompactPath module
+#
+[xml]
+etc/rewrite-compactpath.xml
+
+[ini-template]
+## Requires either rewrite or rewrite-customizer module
+## with rewritePathInfo==true
+jetty.rewrite.rewritePathInfo=true
\ No newline at end of file
diff --git a/jetty-rewrite/src/main/config/modules/rewrite-customizer.mod b/jetty-rewrite/src/main/config/modules/rewrite-customizer.mod
new file mode 100644
index 0000000..102939d
--- /dev/null
+++ b/jetty-rewrite/src/main/config/modules/rewrite-customizer.mod
@@ -0,0 +1,24 @@
+#
+# Jetty Rewrite Customizer module
+#
+# Apply rewrite rules as a request customizer applied to all
+# connectors sharing a HttpConfiguration
+#
+[depend]
+server
+
+[lib]
+lib/jetty-rewrite-${jetty.version}.jar
+
+[xml]
+etc/jetty-rewrite-customizer.xml
+
+[ini-template]
+## Whether to rewrite the request URI
+# jetty.rewrite.rewriteRequestURI=true
+
+## Whether to rewrite the path info
+# jetty.rewrite.rewritePathInfo=true
+
+## Request attribute key under with the original path is stored
+# jetty.rewrite.originalPathAttribute=requestedPath
diff --git a/jetty-rewrite/src/main/config/modules/rewrite.mod b/jetty-rewrite/src/main/config/modules/rewrite.mod
index d2e00c8..3c54257 100644
--- a/jetty-rewrite/src/main/config/modules/rewrite.mod
+++ b/jetty-rewrite/src/main/config/modules/rewrite.mod
@@ -1,7 +1,8 @@
 #
 # Jetty Rewrite module
 #
-
+# Install rewrite rules as a handler applied to all requests on a server
+#
 [depend]
 server
 
@@ -10,3 +11,13 @@
 
 [xml]
 etc/jetty-rewrite.xml
+
+[ini-template]
+## Whether to rewrite the request URI
+# jetty.rewrite.rewriteRequestURI=true
+
+## Whether to rewrite the path info
+# jetty.rewrite.rewritePathInfo=false
+
+## Request attribute key under with the original path is stored
+# jetty.rewrite.originalPathAttribute=requestedPath
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/RewriteCustomizer.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/RewriteCustomizer.java
new file mode 100644
index 0000000..6b8f48d
--- /dev/null
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/RewriteCustomizer.java
@@ -0,0 +1,44 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.rewrite;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.rewrite.handler.RuleContainer;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConfiguration.Customizer;
+import org.eclipse.jetty.server.Request;
+
+public class RewriteCustomizer extends RuleContainer implements Customizer 
+{
+    @Override
+    public void customize(Connector connector, HttpConfiguration channelConfig, Request request)
+    {
+        try
+        {
+            matchAndApply(request.getPathInfo(), request, request.getResponse());
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeIOException(e);
+        }
+    }
+}
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/CompactPathRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/CompactPathRule.java
index e939c74..3a1eeae 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/CompactPathRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/CompactPathRule.java
@@ -43,7 +43,7 @@
         String uri = request.getRequestURI();
         if (uri.startsWith("/"))
             uri = URIUtil.compactPath(uri);
-        request.setRequestURI(uri);
+        request.setURIPathQuery(uri);
     }
 
     @Override
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/CookiePatternRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/CookiePatternRule.java
index 5ba911b..a9387b5 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/CookiePatternRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/CookiePatternRule.java
@@ -24,6 +24,8 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.util.annotation.Name;
+
 
 /**
  * Sets the cookie in the response whenever the rule finds a match.
@@ -38,8 +40,17 @@
     /* ------------------------------------------------------------ */
     public CookiePatternRule()
     {
+        this(null,null,null);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public CookiePatternRule(@Name("pattern") String pattern, @Name("name") String name, @Name("value") String value)
+    {
+        super(pattern);
         _handling = false;
         _terminating = false;
+        setName(name);
+        setValue(value);
     }
 
     /* ------------------------------------------------------------ */
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/HeaderPatternRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/HeaderPatternRule.java
index 7daa302..96abf92 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/HeaderPatternRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/HeaderPatternRule.java
@@ -23,6 +23,8 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.util.annotation.Name;
+
 
 /**
  * Sets the header in the response whenever the rule finds a match.
@@ -31,13 +33,23 @@
 {
     private String _name;
     private String _value;
-    private boolean _add=false;
+    private boolean _add;
 
     /* ------------------------------------------------------------ */
     public HeaderPatternRule()
     {
+        this(null,null,null);
+    }
+
+    /* ------------------------------------------------------------ */
+    public HeaderPatternRule(@Name("pattern") String pattern, @Name("name") String name, @Name("value") String value)
+    {
+        super(pattern);
         _handling = false;
         _terminating = false;
+        _add=false;
+        setName(name);
+        setValue(value);
     }
 
     /* ------------------------------------------------------------ */
@@ -116,6 +128,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Returns the add flag value.
+     * @return true if add flag set
      */
     public boolean isAdd()
     {
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRule.java
index 339307a..2af5a73 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRule.java
@@ -24,13 +24,14 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.util.annotation.Name;
+
 
 /* ------------------------------------------------------------ */
 /** Rule to add a header based on a Regex match
  */
 public class HeaderRegexRule extends RegexRule
 {
-
     private String _name;
     private String _value;
     private boolean _add=false;
@@ -38,11 +39,20 @@
     /* ------------------------------------------------------------ */
     public HeaderRegexRule()
     {
-        _handling = false;
-        _terminating = false;
+        this(null,null,null);
     }
 
     /* ------------------------------------------------------------ */
+    public HeaderRegexRule(@Name("regex") String regex, @Name("name") String name, @Name("value") String value)
+    {
+        super(regex);
+        setHandling(false);
+        setTerminating(false);
+        setName(name);
+        setValue(value);
+    }
+    
+    /* ------------------------------------------------------------ */
     /**
      * Sets the header name.
      * 
@@ -109,7 +119,7 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * Returns the add flag value.
+     * @return the add flag value.
      */
     public boolean isAdd()
     {
@@ -118,7 +128,7 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * Returns the header contents.
+     * @return the header contents.
      */
     @Override
     public String toString()
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/LegacyRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/LegacyRule.java
deleted file mode 100644
index 2cad415..0000000
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/LegacyRule.java
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rewrite.handler;
-
-import java.io.IOException;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.http.PathMap;
-import org.eclipse.jetty.util.URIUtil;
-
-/**
- * Rule implementing the legacy API of RewriteHandler
- * 
- *
- */
-public class LegacyRule extends Rule
-{
-    private PathMap _rewrite = new PathMap(true);
-    
-    public LegacyRule()
-    {
-        _handling = false;
-        _terminating = false;
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public String matchAndApply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException
-    {
-        Map.Entry<?,?> rewrite =_rewrite.getMatch(target);
-        
-        if (rewrite!=null && rewrite.getValue()!=null)
-        {
-            target=URIUtil.addPaths(rewrite.getValue().toString(),
-                    PathMap.pathInfo(rewrite.getKey().toString(),target));
-
-            return target;
-        }
-        
-        return null;
-    }
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * Returns the map of rewriting rules.
-     * @return A {@link PathMap} of the rewriting rules.
-     */
-    public PathMap getRewrite()
-    {
-        return _rewrite;
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Sets the map of rewriting rules.
-     * @param rewrite A {@link PathMap} of the rewriting rules. Only 
-     * prefix paths should be included.
-     */
-    public void setRewrite(PathMap rewrite)
-    {
-        _rewrite=rewrite;
-    }
-    
-    
-    /* ------------------------------------------------------------ */
-    /** Add a path rewriting rule
-     * @param pattern The path pattern to match. The pattern must start with / and may use
-     * a trailing /* as a wildcard.
-     * @param prefix The path prefix which will replace the matching part of the path.
-     */
-    public void addRewriteRule(String pattern, String prefix)
-    {
-        if (pattern==null || pattern.length()==0 || !pattern.startsWith("/"))
-            throw new IllegalArgumentException();
-        if (_rewrite==null)
-            _rewrite=new PathMap(true);
-        _rewrite.put(pattern,prefix);
-    }
-
-
-}
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/PatternRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/PatternRule.java
index 8a5ce8e..3ea6309 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/PatternRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/PatternRule.java
@@ -33,8 +33,19 @@
 {
     protected String _pattern;
 
+    /* ------------------------------------------------------------ */
+    protected PatternRule()
+    {
+    }
 
     /* ------------------------------------------------------------ */
+    protected PatternRule(String pattern)
+    {
+        this();
+        setPattern(pattern);
+    }
+    
+    /* ------------------------------------------------------------ */
     public String getPattern()
     {
         return _pattern;
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectPatternRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectPatternRule.java
index 1006243..0263087 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectPatternRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectPatternRule.java
@@ -23,21 +23,34 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.util.annotation.Name;
+
 /**
- * Redirects the response whenever the rule finds a match.
+ * Issues a (3xx) Redirect response whenever the rule finds a match.
+ * <p>
+ * All redirects are part of the <a href="http://tools.ietf.org/html/rfc7231#section-6.4"><code>3xx Redirection</code> status code set</a>.
+ * <p>
+ * Defaults to <a href="http://tools.ietf.org/html/rfc7231#section-6.4.3"><code>302 Found</code></a>
  */
 public class RedirectPatternRule extends PatternRule
 {
     private String _location;
-
-    /* ------------------------------------------------------------ */
+    private int _statusCode = HttpStatus.FOUND_302;
+    
     public RedirectPatternRule()
     {
-        _handling = true;
-        _terminating = true;
+        this(null,null);
     }
 
-    /* ------------------------------------------------------------ */
+    public RedirectPatternRule(@Name("pattern") String pattern, @Name("location") String location)
+    {
+        super(pattern);
+        _handling = true;
+        _terminating = true;
+        _location=location;
+    }
+    
     /**
      * Sets the redirect location.
      * 
@@ -47,26 +60,46 @@
     {
         _location = value;
     }
-
-    /* ------------------------------------------------------------ */
-    /*
-     * (non-Javadoc)
-     * @see org.eclipse.jetty.server.server.handler.rules.RuleBase#apply(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+    
+    /**
+     * Sets the redirect status code.
+     * 
+     * @param statusCode the 3xx redirect status code
      */
+    public void setStatusCode(int statusCode)
+    {
+        if ((300 <= statusCode) || (statusCode >= 399))
+        {
+            _statusCode = statusCode;
+        }
+        else
+        {
+            throw new IllegalArgumentException("Invalid redirect status code " + statusCode + " (must be a value between 300 and 399)");
+        }
+    }
+
     @Override
     public String apply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException
     {
-        response.sendRedirect(response.encodeRedirectURL(_location));
+        String location = response.encodeRedirectURL(_location);
+        response.setHeader("Location",RedirectUtil.toRedirectURL(request,location));
+        response.setStatus(_statusCode);
+        response.getOutputStream().flush(); // no output / content
+        response.getOutputStream().close();
         return target;
     }
 
-    /* ------------------------------------------------------------ */
     /**
-     * Returns the redirect location.
+     * Returns the redirect status code and location.
      */
     @Override
     public String toString()
     {
-        return super.toString()+"["+_location+"]";
+        StringBuilder str = new StringBuilder();
+        str.append(super.toString());
+        str.append('[').append(_statusCode);
+        str.append('>').append(_location);
+        str.append(']');
+        return str.toString();
     }
 }
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java
index 15159e9..b16d007 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java
@@ -24,42 +24,95 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.util.annotation.Name;
+
 /**
- * Redirects the response by matching with a regular expression.
+ * Issues a (3xx) Redirect response whenever the rule finds a match via regular expression.
+ * <p>
  * The replacement string may use $n" to replace the nth capture group.
+ * <p>
+ * All redirects are part of the <a href="http://tools.ietf.org/html/rfc7231#section-6.4"><code>3xx Redirection</code> status code set</a>.
+ * <p>
+ * Defaults to <a href="http://tools.ietf.org/html/rfc7231#section-6.4.3"><code>302 Found</code></a>
  */
 public class RedirectRegexRule extends RegexRule
 {
-    private String _replacement;
-    
+    protected String _location;
+    private int _statusCode = HttpStatus.FOUND_302;
+
     public RedirectRegexRule()
     {
-        _handling = true;
-        _terminating = true;
+        this(null,null);
+    }
+    
+    public RedirectRegexRule(@Name("regex") String regex, @Name("location") String location)
+    {
+        super(regex);
+        setHandling(true);
+        setTerminating(true);
+        setLocation(location);
     }
 
-    /**
-     * Whenever a match is found, it replaces with this value.
-     * 
-     * @param replacement the replacement string.
-     */
+    @Deprecated
     public void setReplacement(String replacement)
     {
-        _replacement = replacement;
+        _location = replacement;
+    }
+    
+    public void setLocation(String location)
+    {
+        _location = location;
+    }
+    
+    /**
+     * Sets the redirect status code.
+     * 
+     * @param statusCode the 3xx redirect status code
+     */
+    public void setStatusCode(int statusCode)
+    {
+        if ((300 <= statusCode) || (statusCode >= 399))
+        {
+            _statusCode = statusCode;
+        }
+        else
+        {
+            throw new IllegalArgumentException("Invalid redirect status code " + statusCode + " (must be a value between 300 and 399)");
+        }
     }
     
     @Override
     protected String apply(String target, HttpServletRequest request, HttpServletResponse response, Matcher matcher)
             throws IOException
     {
-        target=_replacement;
+        target=_location;
         for (int g=1;g<=matcher.groupCount();g++)
         {
             String group = matcher.group(g);
             target=target.replaceAll("\\$"+g,group);
         }
-
-        response.sendRedirect(response.encodeRedirectURL(target));
+        
+        target = response.encodeRedirectURL(target);
+        response.setHeader("Location",RedirectUtil.toRedirectURL(request,target));
+        response.setStatus(_statusCode);
+        response.getOutputStream().flush(); // no output / content
+        response.getOutputStream().close();
         return target;
     }
+    
+    /**
+     * Returns the redirect status code and replacement.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        str.append(super.toString());
+        str.append('[').append(_statusCode);
+        str.append('>').append(_location);
+        str.append(']');
+        return str.toString();
+    }
+
 }
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectUtil.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectUtil.java
new file mode 100644
index 0000000..dd2363b
--- /dev/null
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectUtil.java
@@ -0,0 +1,71 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.rewrite.handler;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.util.URIUtil;
+
+/**
+ * Utility for managing redirect based rules
+ */
+public final class RedirectUtil
+{
+    /**
+     * Common point to generate a proper "Location" header for redirects.
+     * 
+     * @param request
+     *            the request the redirect should be based on (needed when relative locations are provided, so that
+     *            server name, scheme, port can be built out properly)
+     * @param location
+     *            the location URL to redirect to (can be a relative path)
+     * @return the full redirect "Location" URL (including scheme, host, port, path, etc...)
+     */
+    public static String toRedirectURL(final HttpServletRequest request, String location)
+    {
+        if (!URIUtil.hasScheme(location))
+        {
+            StringBuilder url = new StringBuilder(128);
+            URIUtil.appendSchemeHostPort(url,request.getScheme(),request.getServerName(),request.getServerPort());
+
+            if (location.startsWith("/"))
+            {
+                // absolute in context
+                location = URIUtil.canonicalPath(location);
+            }
+            else
+            {
+                // relative to request
+                String path = request.getRequestURI();
+                String parent = (path.endsWith("/")) ? path : URIUtil.parentPath(path);
+                location = URIUtil.canonicalPath(URIUtil.addEncodedPaths(parent,location));
+                if (!location.startsWith("/"))
+                    url.append('/');
+            }
+
+            if (location == null)
+                throw new IllegalStateException("path cannot be above root");
+            url.append(location);
+
+            location = url.toString();
+        }
+
+        return location;
+    }
+}
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RegexRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RegexRule.java
index cd189cc..f4e0f2e 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RegexRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RegexRule.java
@@ -32,7 +32,18 @@
 public abstract class RegexRule extends Rule
 {
     protected Pattern _regex; 
-
+    
+    /* ------------------------------------------------------------ */
+    protected RegexRule()
+    {
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected RegexRule(String pattern)
+    {
+        setRegex(pattern);
+    }
+    
     /* ------------------------------------------------------------ */
     /**
      * Sets the regular expression string used to match with string URI.
@@ -41,7 +52,7 @@
      */
     public void setRegex(String regex)
     {
-        _regex=Pattern.compile(regex);
+        _regex=regex==null?null:Pattern.compile(regex);
     }
 
     /* ------------------------------------------------------------ */
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ResponsePatternRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ResponsePatternRule.java
index 09c5a5b..e805399 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ResponsePatternRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ResponsePatternRule.java
@@ -23,19 +23,30 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.util.annotation.Name;
+
 /**
  * Sends the response code whenever the rule finds a match.
  */
 public class ResponsePatternRule extends PatternRule
 {
     private String _code;
-    private String _reason = "";
+    private String _reason;
 
     /* ------------------------------------------------------------ */
     public ResponsePatternRule()
     {
+        this(null,null,"");
+    }
+
+    /* ------------------------------------------------------------ */
+    public ResponsePatternRule(@Name("pattern") String pattern, @Name("code") String code, @Name("reason") String reason)
+    {
+        super(pattern);
         _handling = true;
         _terminating = true;
+        setCode(code);
+        setReason(reason);
     }
 
     /* ------------------------------------------------------------ */
@@ -53,7 +64,7 @@
      * Sets the reason for the response status code. Reasons will only reflect
      * if the code value is greater or equal to 400.
      * 
-     * @param reason
+     * @param reason the reason
      */
     public void setReason(String reason)
     {
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteHandler.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteHandler.java
index 2ddf156..0be9a76 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteHandler.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteHandler.java
@@ -54,7 +54,6 @@
  * <li> {@link RewritePatternRule} - rewrites the requested URI. </li>
  * <li> {@link RewriteRegexRule} - rewrites the requested URI using regular expression for pattern matching. </li>
  * <li> {@link MsieSslRule} - disables the keep alive on SSL for IE5 and IE6. </li>
- * <li> {@link LegacyRule} - the old version of rewrite. </li>
  * <li> {@link ForwardedSchemeHeaderRule} - set the scheme according to the headers present. </li>
  * <li> {@link VirtualHostRuleContainer} - checks whether the request matches one of a set of virtual host names.</li>
  * </ul>
@@ -174,7 +173,7 @@
 public class RewriteHandler extends HandlerWrapper
 {
     private RuleContainer _rules;
-    private EnumSet<DispatcherType> _dispatchTypes = EnumSet.of(DispatcherType.REQUEST);
+    private EnumSet<DispatcherType> _dispatchTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC);
 
     /* ------------------------------------------------------------ */
     public RewriteHandler()
@@ -184,19 +183,6 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * To enable configuration from jetty.xml on rewriteRequestURI, rewritePathInfo and
-     * originalPathAttribute
-     *
-     * @param legacyRule old style rewrite rule
-     */
-    @Deprecated
-    public void setLegacyRule(LegacyRule legacyRule)
-    {
-        _rules.setLegacyRule(legacyRule);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
      * Returns the list of rules.
      * @return an array of {@link Rule}.
      */
@@ -220,11 +206,28 @@
      * Assigns the rules to process.
      * @param rules a {@link RuleContainer} containing other rules to process
      */
+    @Deprecated
     public void setRules(RuleContainer rules)
     {
         _rules = rules;
     }
+    
+    /*------------------------------------------------------------ */
+    /**
+     * Assigns the rules to process.
+     * @param rules a {@link RuleContainer} containing other rules to process
+     */
+    public void setRuleContainer(RuleContainer rules)
+    {
+        _rules = rules;
+    }
 
+    /*------------------------------------------------------------ */
+    public RuleContainer getRuleContainer()
+    {
+        return _rules;
+    }
+    
     /* ------------------------------------------------------------ */
     /**
      * Add a Rule
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewritePatternRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewritePatternRule.java
index 81f0c06..4898266 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewritePatternRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewritePatternRule.java
@@ -23,10 +23,10 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jetty.http.HttpURI;
 import org.eclipse.jetty.http.PathMap;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.annotation.Name;
 
 /**
  * Rewrite the URI by replacing the matched {@link PathMap} path with a fixed string.
@@ -39,10 +39,19 @@
     /* ------------------------------------------------------------ */
     public RewritePatternRule()
     {
+        this(null,null);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public RewritePatternRule(@Name("pattern") String pattern, @Name("replacement") String replacement)
+    {
+        super(pattern);
         _handling = false;
         _terminating = false;
+        setReplacement(replacement);
     }
-
+    
+    
     /* ------------------------------------------------------------ */
     /**
      * Whenever a match is found, it replaces with this value.
@@ -51,18 +60,20 @@
      */
     public void setReplacement(String replacement)
     {
-        String[] split = replacement.split("\\?", 2);
-        _replacement = split[0];
-        _query = split.length == 2 ? split[1] : null;
+        if (replacement==null)
+        {
+            _replacement=null;
+            _query=null;
+        }
+        else
+        {
+            String[] split = replacement.split("\\?", 2);
+            _replacement = split[0];
+            _query = split.length == 2 ? split[1] : null;
+        }
     }
 
     /* ------------------------------------------------------------ */
-    /*
-     * (non-Javadoc)
-     *
-     * @see org.eclipse.jetty.server.handler.rules.RuleBase#apply(javax.servlet.http.HttpServletRequest,
-     * javax.servlet.http.HttpServletResponse)
-     */
     @Override
     public String apply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException
     {
@@ -74,21 +85,21 @@
     /**
      * This method will add _query to the requests's queryString and also combine it with existing queryStrings in
      * the request. However it won't take care for duplicate. E.g. if request.getQueryString contains a parameter
-     * "param1 = true" and _query will contain "param1=false" the result will be param1=true&param1=false.
+     * <code>param1 = true</code> and _query will contain <code>param1=false</code> the result will be <code>param1=true&amp;param1=false</code>.
      * To cover this use case some more complex pattern matching is necessary. We can implement this if there's use
      * cases.
      *
-     * @param request
-     * @param oldURI
-     * @param newURI
-     * @throws IOException
+     * @param request the request
+     * @param oldURI the old URI
+     * @param newURI the new URI
+     * @throws IOException if unable to apply the URI
      */
     @Override
     public void applyURI(Request request, String oldURI, String newURI) throws IOException
     {
         if (_query == null)
         {
-            request.setRequestURI(newURI);
+            request.setURIPathQuery(newURI);
         }
         else
         {
@@ -97,9 +108,7 @@
                 queryString = queryString + "&" + _query;
             else
                 queryString = _query;
-            HttpURI uri = new HttpURI(newURI + "?" + queryString);
-            request.setUri(uri);
-            request.setRequestURI(newURI);
+            request.setURIPathQuery(newURI);
             request.setQueryString(queryString);
         }
     }
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java
index a1c9668..637789a 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java
@@ -24,8 +24,8 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jetty.http.HttpURI;
 import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.annotation.Name;
 
 /**
  * Rewrite the URI by matching with a regular expression. 
@@ -44,8 +44,16 @@
     /* ------------------------------------------------------------ */
     public RewriteRegexRule()
     {
-        _handling = false;
-        _terminating = false;
+        this(null,null);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public RewriteRegexRule(@Name("regex") String regex, @Name("replacement") String replacement)
+    {
+        super(regex);
+        setHandling(false);
+        setTerminating(false);
+        setReplacement(replacement);
     }
 
     /* ------------------------------------------------------------ */
@@ -56,10 +64,19 @@
      */
     public void setReplacement(String replacement)
     {
-        String[] split=replacement.split("\\?",2);
-        _replacement = split[0];
-        _query=split.length==2?split[1]:null;
-        _queryGroup=_query!=null && _query.contains("$Q");
+        if (replacement==null)
+        {
+            _replacement=null;
+            _query=null;
+            _queryGroup=false;
+        }
+        else
+        {
+            String[] split=replacement.split("\\?",2);
+            _replacement = split[0];
+            _query=split.length==2?split[1]:null;
+            _queryGroup=_query!=null && _query.contains("$Q");
+        }
     }
 
 
@@ -100,7 +117,7 @@
     {
         if (_query==null)
         {
-            request.setRequestURI(newURI);
+            request.setURIPathQuery(newURI);
         }
         else
         {
@@ -108,9 +125,7 @@
             
             if (!_queryGroup && request.getQueryString()!=null)
                 query=request.getQueryString()+"&"+query;
-            HttpURI uri=new HttpURI(newURI+"?"+query);
-            request.setUri(uri);
-            request.setRequestURI(newURI);
+            request.setURIPathQuery(newURI);
             request.setQueryString(query);
         }
     }
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/Rule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/Rule.java
index 6d22ff7..e9eacd4 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/Rule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/Rule.java
@@ -44,20 +44,18 @@
     /**
      * This method calls tests the rule against the request/response pair and if the Rule 
      * applies, then the rule's action is triggered.
-     * @param target The target of the request
-     * @param request
-     * @param response
      * 
+     * @param target The target of the request
+     * @param request the request
+     * @param response the response
      * @return The new target if the rule has matched, else null
-     * @throws IOException
+     * @throws IOException if unable to match the rule
      */
     public abstract String matchAndApply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException;   
     
     /**
      * Sets terminating to true or false.
-     * If true, this rule will terminate the loop if this rule has been applied.
-     * 
-     * @param terminating
+     * @param terminating If true, this rule will terminate the loop if this rule has been applied.
      */    
     public void setTerminating(boolean terminating)
     {
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java
index b8f6afb..afaac89 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java
@@ -23,7 +23,6 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jetty.server.HttpChannel;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.util.ArrayUtil;
 import org.eclipse.jetty.util.URIUtil;
@@ -33,48 +32,19 @@
 /**
  * Base container to group rules. Can be extended so that the contained rules
  * will only be applied under certain conditions
- * 
- * 
  */
-
 public class RuleContainer extends Rule
 {
+    public static final String ORIGINAL_QUERYSTRING_ATTRIBUTE_SUFFIX = ".QUERYSTRING";
     private static final Logger LOG = Log.getLogger(RuleContainer.class);
 
     protected Rule[] _rules;
     
     protected String _originalPathAttribute;
+    protected String _originalQueryStringAttribute;
     protected boolean _rewriteRequestURI=true;
     protected boolean _rewritePathInfo=true;
-    
-    protected LegacyRule _legacy;
-
-    /* ------------------------------------------------------------ */
-    @Deprecated
-    public LegacyRule getLegacyRule()
-    {
-        if (_legacy==null)
-        {
-            _legacy= new LegacyRule();
-            addRule(_legacy);
-        }
-        return _legacy;
-    }
-    
-
-    /* ------------------------------------------------------------ */
-    /**
-     * To enable configuration from jetty.xml on rewriteRequestURI, rewritePathInfo and
-     * originalPathAttribute
-     * 
-     * @param legacyRule old style rewrite rule
-     */
-    @Deprecated
-    public void setLegacyRule(LegacyRule legacyRule)
-    {
-        _legacy = legacyRule;
-    }
-
+     
     /* ------------------------------------------------------------ */
     /**
      * Returns the list of rules.
@@ -92,16 +62,7 @@
      */
     public void setRules(Rule[] rules)
     {
-        if (_legacy==null)
-            _rules = rules;
-        else
-        {
-            _rules=null;
-            addRule(_legacy);
-            if (rules!=null)
-                for (Rule rule:rules)
-                    addRule(rule);
-        }
+        _rules = rules;
     }
 
     /* ------------------------------------------------------------ */
@@ -173,6 +134,7 @@
     public void setOriginalPathAttribute(String originalPathAttribte)
     {
         _originalPathAttribute=originalPathAttribte;
+        _originalQueryStringAttribute = originalPathAttribte + ORIGINAL_QUERYSTRING_ATTRIBUTE_SUFFIX;
     }
     
     /**
@@ -192,22 +154,31 @@
      * @param target target field to pass on to the contained rules
      * @param request request object to pass on to the contained rules
      * @param response response object to pass on to the contained rules
+     * @return the target
+     * @throws IOException if unable to apply the rule
      */
     protected String apply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException
     {
         boolean original_set=_originalPathAttribute==null;
-                
+        
+        if (_rules==null)
+            return target;
+        
         for (Rule rule : _rules)
         {
             String applied=rule.matchAndApply(target,request, response);
             if (applied!=null)
-            {       
+            {
                 LOG.debug("applied {}",rule);
                 LOG.debug("rewrote {} to {}",target,applied);
                 if (!original_set)
                 {
                     original_set=true;
                     request.setAttribute(_originalPathAttribute, target);
+                    
+                    String query = request.getQueryString();
+                    if (query != null)
+                        request.setAttribute(_originalQueryStringAttribute,query);
                 }     
 
                 if (_rewriteRequestURI)
@@ -216,7 +187,7 @@
                     if (rule instanceof Rule.ApplyURI)
                         ((Rule.ApplyURI)rule).applyURI((Request)request,((Request)request).getRequestURI(), encoded);
                     else
-                        ((Request)request).setRequestURI(encoded);
+                        ((Request)request).setURIPathQuery(encoded);
                 }
 
                 if (_rewritePathInfo)
@@ -227,7 +198,7 @@
                 if (rule.isHandling())
                 {
                     LOG.debug("handling {}",rule);
-                    (request instanceof Request?(Request)request:HttpChannel.getCurrentHttpChannel().getRequest()).setHandled(true);
+                    Request.getBaseRequest(request).setHandled(true);
                 }
 
                 if (rule.isTerminating())
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/TerminatingPatternRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/TerminatingPatternRule.java
new file mode 100644
index 0000000..92cd5c3
--- /dev/null
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/TerminatingPatternRule.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.rewrite.handler;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * If this rule matches, terminate the processing of other rules.
+ * Allowing the request to be processed by the handlers after the rewrite rules.
+ */
+public class TerminatingPatternRule extends PatternRule
+{
+    public TerminatingPatternRule()
+    {
+        this(null);
+    }
+    
+    public TerminatingPatternRule(String pattern)
+    {
+        super(pattern);
+        super.setTerminating(true);
+    }
+
+    @Override
+    public void setTerminating(boolean terminating)
+    {
+        if (!terminating)
+        {
+            throw new RuntimeException("Not allowed to disable terminating on a " + TerminatingPatternRule.class.getName());
+        }
+    }
+
+    @Override
+    protected String apply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException
+    {
+        return target;
+    }
+}
\ No newline at end of file
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/TerminatingRegexRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/TerminatingRegexRule.java
new file mode 100644
index 0000000..9ac4b4c
--- /dev/null
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/TerminatingRegexRule.java
@@ -0,0 +1,60 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.rewrite.handler;
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.util.annotation.Name;
+
+/**
+ * If this rule matches, terminate the processing of other rules.
+ * Allowing the request to be processed by the handlers after the rewrite rules.
+ */
+public class TerminatingRegexRule extends RegexRule
+{
+    public TerminatingRegexRule()
+    {
+        this(null);
+    }
+    
+    public TerminatingRegexRule(@Name("regex") String regex)
+    {
+        super(regex);
+        super.setTerminating(true);
+    }
+
+    @Override
+    public void setTerminating(boolean terminating)
+    {
+        if (!terminating)
+        {
+            throw new RuntimeException("Not allowed to disable terminating on a " + TerminatingRegexRule.class.getName());
+        }
+    }
+
+    @Override
+    public String apply(String target, HttpServletRequest request, HttpServletResponse response, Matcher matcher) throws IOException
+    {
+        return target;
+    }
+}
\ No newline at end of file
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ValidUrlRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ValidUrlRule.java
index c4ac0af..d9719be 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ValidUrlRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ValidUrlRule.java
@@ -29,12 +29,13 @@
 
 /**
  * This rule can be used to protect against invalid unicode characters in a url making it into applications.
- *
+ * <p>
  * The logic is as follows.
- * 
- * - if decoded uri character is an iso control character return code/reason
- * - if no UnicodeBlock is found for character return code/reason
- * - if character is in UnicodeBlock.SPECIALS return code/reason
+ * <ul>
+ * <li>if decoded uri character is an iso control character return code/reason</li>
+ * <li>if no UnicodeBlock is found for character return code/reason</li>
+ * <li>if character is in UnicodeBlock.SPECIALS return code/reason</li>
+ * </ul>
  */
 public class ValidUrlRule extends Rule
 {
@@ -65,7 +66,7 @@
     /**
      * Sets the reason for the response status code. Reasons will only reflect if the code value is greater or equal to 400.
      * 
-     * @param reason
+     * @param reason the reason
      */
     public void setReason(String reason)
     {
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ForwardedSchemeHeaderRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ForwardedSchemeHeaderRuleTest.java
index 74b676c..c04a6a3 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ForwardedSchemeHeaderRuleTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ForwardedSchemeHeaderRuleTest.java
@@ -73,11 +73,11 @@
         _rule.matchAndApply("/",_request,_response);
         assertEquals("https",_request.getScheme());
 
-        _request.setScheme(null);
+        _request.setScheme("other");
         // header value doesn't match rule's value
         setRequestHeader("Front-End-Https", "off");
         _rule.matchAndApply("/",_request,_response);
-        assertEquals(null,_request.getScheme());
+        assertEquals("other",_request.getScheme());
 
         _request.setScheme(null);
         // header value can be any value
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRuleTest.java
index 668635b..1b87da9 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRuleTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/HeaderRegexRuleTest.java
@@ -18,13 +18,14 @@
 
 package org.eclipse.jetty.rewrite.handler;
 
-import org.junit.Before;
-import org.junit.Test;
 import static org.junit.Assert.assertEquals;
 
 import java.io.IOException;
 import java.util.Iterator;
 
+import org.junit.Before;
+import org.junit.Test;
+
 public class HeaderRegexRuleTest extends AbstractRuleTestCase
 {
 
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/LegacyRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/LegacyRuleTest.java
deleted file mode 100644
index 902e0a3..0000000
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/LegacyRuleTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rewrite.handler;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class LegacyRuleTest extends AbstractRuleTestCase
-{
-    private String[][] _tests=
-    {
-            {"/foo/bar","/*","/replace/foo/bar"},
-            {"/foo/bar","/foo/*","/replace/bar"},
-            {"/foo/bar","/foo/bar","/replace"}
-    };
-    private LegacyRule _rule;
-
-    @Before
-    public void init() throws Exception
-    {
-        start(false);
-        _rule = new LegacyRule();
-    }
-
-    @After
-    public void destroy()
-    {
-        _rule = null;
-    }
-
-    @Test
-    public void testMatchAndApply() throws Exception
-    {
-        for (String[] _test : _tests)
-        {
-            _rule.addRewriteRule(_test[1], "/replace");
-            String result = _rule.matchAndApply(_test[0], _request, _response);
-            assertEquals(_test[1], _test[2], result);
-        }
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testAddRewrite()
-    {
-        _rule.addRewriteRule("*.txt", "/replace");
-    }
-}
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/PatternRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/PatternRuleTest.java
index 6586793..fed73de 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/PatternRuleTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/PatternRuleTest.java
@@ -25,6 +25,10 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
 import org.eclipse.jetty.server.Request;
 import org.junit.After;
 import org.junit.Before;
@@ -138,7 +142,7 @@
         new Request(null,null)
         {
             {
-                setRequestURI(uri);
+                setMetaData(new MetaData.Request("GET",new HttpURI(uri),HttpVersion.HTTP_1_0,new HttpFields()));
             }
         }, null
         );
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RedirectPatternRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RedirectPatternRuleTest.java
index 4b8249f..afca925 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RedirectPatternRuleTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RedirectPatternRuleTest.java
@@ -18,39 +18,54 @@
 
 package org.eclipse.jetty.rewrite.handler;
 
-import static org.junit.Assert.assertEquals;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
 
 import java.io.IOException;
 
 import org.eclipse.jetty.http.HttpHeader;
-import org.junit.After;
+import org.eclipse.jetty.http.HttpStatus;
 import org.junit.Before;
 import org.junit.Test;
 
 public class RedirectPatternRuleTest extends AbstractRuleTestCase
 {
-    private RedirectPatternRule _rule;
-
     @Before
     public void init() throws Exception
     {
         start(false);
-        _rule = new RedirectPatternRule();
-        _rule.setPattern("*");
     }
 
-    @After
-    public void destroy()
+    private void assertRedirectResponse(int expectedStatusCode, String expectedLocation) throws IOException
     {
-        _rule = null;
+        assertThat("Response status code",_response.getStatus(),is(expectedStatusCode));
+        assertThat("Response location",_response.getHeader(HttpHeader.LOCATION.asString()),is(expectedLocation));
     }
 
     @Test
-    public void testLocation() throws IOException
+    public void testGlobPattern() throws IOException
     {
         String location = "http://eclipse.com";
-        _rule.setLocation(location);
-        _rule.apply(null, _request, _response);
-        assertEquals(location, _response.getHeader(HttpHeader.LOCATION.asString()));
+
+        RedirectPatternRule rule = new RedirectPatternRule();
+        rule.setPattern("*");
+        rule.setLocation(location);
+
+        rule.apply("/",_request,_response);
+        assertRedirectResponse(HttpStatus.FOUND_302,location);
+    }
+
+    @Test
+    public void testPrefixPattern() throws IOException
+    {
+        String location = "http://api.company.com/";
+
+        RedirectPatternRule rule = new RedirectPatternRule();
+        rule.setPattern("/api/*");
+        rule.setLocation(location);
+        rule.setStatusCode(HttpStatus.MOVED_PERMANENTLY_301);
+
+        rule.apply("/api/rest?foo=1",_request,_response);
+        assertRedirectResponse(HttpStatus.MOVED_PERMANENTLY_301,location);
     }
 }
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRuleTest.java
index f3c9a03..f24cdfc 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRuleTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRuleTest.java
@@ -18,62 +18,88 @@
 
 package org.eclipse.jetty.rewrite.handler;
 
-import static org.junit.Assert.assertEquals;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
 
 import java.io.IOException;
 
 import org.eclipse.jetty.http.HttpHeader;
-import org.junit.After;
+import org.eclipse.jetty.http.HttpStatus;
 import org.junit.Before;
 import org.junit.Test;
 
 public class RedirectRegexRuleTest extends AbstractRuleTestCase
 {
-    private RedirectRegexRule _rule;
-
     @Before
     public void init() throws Exception
     {
         start(false);
-        _rule = new RedirectRegexRule();
     }
-
-    @After
-    public void destroy()
+    
+    private void assertRedirectResponse(int expectedStatusCode, String expectedLocation) throws IOException
     {
-        _rule = null;
+        assertThat("Response status code", _response.getStatus(), is(expectedStatusCode));
+        assertThat("Response location", _response.getHeader(HttpHeader.LOCATION.asString()), is(expectedLocation));
     }
 
     @Test
     public void testLocationWithReplacementGroupEmpty() throws IOException
     {
-        _rule.setRegex("/my/dir/file/(.*)$");
-        _rule.setReplacement("http://www.mortbay.org/$1");
+        RedirectRegexRule rule = new RedirectRegexRule();
+        rule.setRegex("/my/dir/file/(.*)$");
+        rule.setReplacement("http://www.mortbay.org/$1");
 
         // Resource is dir
-        _rule.matchAndApply("/my/dir/file/", _request, _response);
-        assertEquals("http://www.mortbay.org/", _response.getHeader(HttpHeader.LOCATION.asString()));
+        rule.matchAndApply("/my/dir/file/", _request, _response);
+        assertRedirectResponse(HttpStatus.FOUND_302,"http://www.mortbay.org/");
+    }
+    
+    @Test
+    public void testLocationWithPathReplacement() throws IOException
+    {
+        RedirectRegexRule rule = new RedirectRegexRule();
+        rule.setRegex("/documentation/(.*)$");
+        rule.setReplacement("/docs/$1");
+
+        // Resource is dir
+        rule.matchAndApply("/documentation/top.html", _request, _response);
+        assertRedirectResponse(HttpStatus.FOUND_302,"http://0.0.0.0/docs/top.html");
     }
 
     @Test
     public void testLocationWithReplacmentGroupSimple() throws IOException
     {
-        _rule.setRegex("/my/dir/file/(.*)$");
-        _rule.setReplacement("http://www.mortbay.org/$1");
+        RedirectRegexRule rule = new RedirectRegexRule();
+        rule.setRegex("/my/dir/file/(.*)$");
+        rule.setReplacement("http://www.mortbay.org/$1");
 
         // Resource is an image
-        _rule.matchAndApply("/my/dir/file/image.png", _request, _response);
-        assertEquals("http://www.mortbay.org/image.png", _response.getHeader(HttpHeader.LOCATION.asString()));
+        rule.matchAndApply("/my/dir/file/image.png", _request, _response);
+        assertRedirectResponse(HttpStatus.FOUND_302,"http://www.mortbay.org/image.png");
     }
 
     @Test
     public void testLocationWithReplacementGroupDeepWithParams() throws IOException
     {
-        _rule.setRegex("/my/dir/file/(.*)$");
-        _rule.setReplacement("http://www.mortbay.org/$1");
+        RedirectRegexRule rule = new RedirectRegexRule();
+        rule.setRegex("/my/dir/file/(.*)$");
+        rule.setReplacement("http://www.mortbay.org/$1");
 
         // Resource is api with parameters
-        _rule.matchAndApply("/my/dir/file/api/rest/foo?id=100&sort=date", _request, _response);
-        assertEquals("http://www.mortbay.org/api/rest/foo?id=100&sort=date", _response.getHeader(HttpHeader.LOCATION.asString()));
+        rule.matchAndApply("/my/dir/file/api/rest/foo?id=100&sort=date", _request, _response);
+        assertRedirectResponse(HttpStatus.FOUND_302,"http://www.mortbay.org/api/rest/foo?id=100&sort=date");
+    }
+    
+    @Test
+    public void testMovedPermanently() throws IOException
+    {
+        RedirectRegexRule rule = new RedirectRegexRule();
+        rule.setRegex("/api/(.*)$");
+        rule.setReplacement("http://api.company.com/$1");
+        rule.setStatusCode(HttpStatus.MOVED_PERMANENTLY_301);
+
+        // Resource is api with parameters
+        rule.matchAndApply("/api/rest/foo?id=100&sort=date", _request, _response);
+        assertRedirectResponse(HttpStatus.MOVED_PERMANENTLY_301,"http://api.company.com/rest/foo?id=100&sort=date");
     }
 }
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteHandlerTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteHandlerTest.java
index d6e1090..d0695fd 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteHandlerTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteHandlerTest.java
@@ -85,7 +85,7 @@
         _handler.setOriginalPathAttribute("/before");
         _handler.setRewriteRequestURI(true);
         _handler.setRewritePathInfo(true);
-        _request.setRequestURI("/xxx/bar");
+        _request.setURIPathQuery("/xxx/bar");
         _request.setPathInfo("/xxx/bar");
         _handler.handle("/xxx/bar",_request,_request, _response);
         assertEquals(201,_response.getStatus());
@@ -99,7 +99,7 @@
         _handler.setOriginalPathAttribute("/before");
         _handler.setRewriteRequestURI(false);
         _handler.setRewritePathInfo(false);
-        _request.setRequestURI("/foo/bar");
+        _request.setURIPathQuery("/foo/bar");
         _request.setPathInfo("/foo/bar");
         
         _handler.handle("/foo/bar",_request,_request, _response);
@@ -112,7 +112,7 @@
         _response.setStatus(200);
         _request.setHandled(false);
         _handler.setOriginalPathAttribute(null);
-        _request.setRequestURI("/aaa/bar");
+        _request.setURIPathQuery("/aaa/bar");
         _request.setPathInfo("/aaa/bar");
         _handler.handle("/aaa/bar",_request,_request, _response);
         assertEquals(201,_response.getStatus());
@@ -126,7 +126,7 @@
         _handler.setOriginalPathAttribute("before");
         _handler.setRewriteRequestURI(true);
         _handler.setRewritePathInfo(true);
-        _request.setRequestURI("/aaa/bar");
+        _request.setURIPathQuery("/aaa/bar");
         _request.setPathInfo("/aaa/bar");
         _handler.handle("/aaa/bar",_request,_request, _response);
         assertEquals(201,_response.getStatus());
@@ -138,7 +138,7 @@
         _response.setStatus(200);
         _request.setHandled(false);
         _rule2.setTerminating(true);
-        _request.setRequestURI("/aaa/bar");
+        _request.setURIPathQuery("/aaa/bar");
         _request.setPathInfo("/aaa/bar");
         _handler.handle("/aaa/bar",_request,_request, _response);
         assertEquals(201,_response.getStatus());
@@ -154,7 +154,7 @@
         _request.setAttribute("target",null);
         _request.setAttribute("URI",null);
         _request.setAttribute("info",null);
-        _request.setRequestURI("/aaa/bar");
+        _request.setURIPathQuery("/aaa/bar");
         _request.setPathInfo("/aaa/bar");
         _handler.handle("/aaa/bar",_request,_request, _response);
         assertEquals(200,_response.getStatus());
@@ -174,7 +174,7 @@
         _handler.setOriginalPathAttribute("/before");
         _handler.setRewriteRequestURI(true);
         _handler.setRewritePathInfo(false);
-        _request.setRequestURI("/ccc/x%20y");
+        _request.setURIPathQuery("/ccc/x%20y");
         _request.setPathInfo("/ccc/x y");
         _handler.handle("/ccc/x y",_request,_request, _response);
         assertEquals(201,_response.getStatus());
@@ -193,7 +193,7 @@
         _handler.setOriginalPathAttribute("/before");
         _handler.setRewriteRequestURI(true);
         _handler.setRewritePathInfo(false);
-        _request.setRequestURI("/xxx/x%20y");
+        _request.setURIPathQuery("/xxx/x%20y");
         _request.setPathInfo("/xxx/x y");
         _handler.handle("/xxx/x y",_request,_request, _response);
         assertEquals(201,_response.getStatus());
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewritePatternRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewritePatternRuleTest.java
index 19a54a3..1aea98e 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewritePatternRuleTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewritePatternRuleTest.java
@@ -23,7 +23,6 @@
 
 import java.io.IOException;
 
-import org.eclipse.jetty.http.HttpURI;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -88,7 +87,7 @@
     {
         String replacement = "/replace";
         String queryString = "request=parameter";
-        _request.setUri(new HttpURI("/old/context"));
+        _request.setURIPathQuery("/old/context");
         _request.setQueryString(queryString);
 
         RewritePatternRule rewritePatternRule = new RewritePatternRule();
@@ -111,7 +110,7 @@
         String[] split = replacement.split("\\?", 2);
         String path = split[0];
         String queryString = split[1];
-        _request.setUri(new HttpURI("/old/context"));
+        _request.setURIPathQuery("/old/context");
         _request.setQueryString(requestQueryString);
 
         RewritePatternRule rewritePatternRule = new RewritePatternRule();
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRuleTest.java
index b4a46c5..b3bea8f 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRuleTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRuleTest.java
@@ -23,7 +23,6 @@
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 
-import org.eclipse.jetty.http.HttpURI;
 import org.eclipse.jetty.util.MultiMap;
 import org.eclipse.jetty.util.URIUtil;
 import org.eclipse.jetty.util.UrlEncoded;
@@ -66,15 +65,13 @@
         for (String[] test : _tests)
         {
             reset();
-            _request.setRequestURI(null);
+            _request.setURIPathQuery(null);
             
             String t=test[0]+"?"+test[1]+">"+test[2]+"|"+test[3];
             _rule.setRegex(test[2]);
             _rule.setReplacement(test[3]);
 
-            _request.setUri(new HttpURI(test[0]+(test[1]==null?"":("?"+test[1]))));
-            _request.getRequestURI();
-
+            _request.setURIPathQuery(test[0]+(test[1]==null?"":("?"+test[1])));
             
             String result = _rule.matchAndApply(test[0], _request, _response);
             assertEquals(t, test[4], result);
@@ -89,7 +86,7 @@
             if (test[5]!=null)
             {
                 MultiMap<String> params=new MultiMap<String>();
-                UrlEncoded.decodeTo(test[5],params, StandardCharsets.UTF_8,-1);
+                UrlEncoded.decodeTo(test[5],params, StandardCharsets.UTF_8);
                                
                 for (String n:params.keySet())
                     assertEquals(params.getString(n),_request.getParameter(n));
@@ -110,7 +107,7 @@
             _rule.setRegex(test[2]);
             _rule.setReplacement(test[3]);
 
-            _request.setRequestURI(test[0]);
+            _request.setURIPathQuery(test[0]);
             _request.setQueryString(test[1]);
             _request.getAttributes().clearAttributes();
             
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/TerminatingPatternRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/TerminatingPatternRuleTest.java
new file mode 100644
index 0000000..cddd998
--- /dev/null
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/TerminatingPatternRuleTest.java
@@ -0,0 +1,103 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.rewrite.handler;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TerminatingPatternRuleTest extends AbstractRuleTestCase
+{
+    private RewriteHandler rewriteHandler;
+
+    @Before
+    public void init() throws Exception
+    {
+        rewriteHandler = new RewriteHandler();
+        rewriteHandler.setServer(_server);
+        rewriteHandler.setHandler(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException,
+                    ServletException
+            {
+                response.setStatus(HttpStatus.CREATED_201);
+                request.setAttribute("target",target);
+                request.setAttribute("URI",request.getRequestURI());
+                request.setAttribute("info",request.getPathInfo());
+            }
+        });
+        rewriteHandler.start();
+
+        TerminatingPatternRule rule1 = new TerminatingPatternRule();
+        rule1.setPattern("/login.jsp");
+        rewriteHandler.addRule(rule1);
+        RedirectRegexRule rule2 = new RedirectRegexRule();
+        rule2.setRegex("^/login.*$");
+        rule2.setReplacement("http://login.company.com/");
+        rewriteHandler.addRule(rule2);
+
+        start(false);
+    }
+
+    private void assertIsRedirect(int expectedStatus, String expectedLocation)
+    {
+        assertThat("Response Status",_response.getStatus(),is(expectedStatus));
+        assertThat("Response Location Header",_response.getHeader(HttpHeader.LOCATION.asString()),is(expectedLocation));
+    }
+
+    private void assertIsRequest(String expectedRequestPath)
+    {
+        assertThat("Response Status",_response.getStatus(),is(HttpStatus.CREATED_201));
+        assertThat("Request Target",_request.getAttribute("target"),is(expectedRequestPath));
+    }
+
+    @Test
+    public void testTerminatingEarly() throws IOException, ServletException
+    {
+        rewriteHandler.handle("/login.jsp",_request,_request,_response);
+        assertIsRequest("/login.jsp");
+    }
+
+    @Test
+    public void testNoTerminationDo() throws IOException, ServletException
+    {
+        rewriteHandler.handle("/login.do",_request,_request,_response);
+        assertIsRedirect(HttpStatus.MOVED_TEMPORARILY_302,"http://login.company.com/");
+    }
+
+    @Test
+    public void testNoTerminationDir() throws IOException, ServletException
+    {
+        rewriteHandler.handle("/login/",_request,_request,_response);
+        assertIsRedirect(HttpStatus.MOVED_TEMPORARILY_302,"http://login.company.com/");
+    }
+}
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/TerminatingRegexRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/TerminatingRegexRuleTest.java
new file mode 100644
index 0000000..aa029f8
--- /dev/null
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/TerminatingRegexRuleTest.java
@@ -0,0 +1,103 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.rewrite.handler;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TerminatingRegexRuleTest extends AbstractRuleTestCase
+{
+    private RewriteHandler rewriteHandler;
+
+    @Before
+    public void init() throws Exception
+    {
+        rewriteHandler = new RewriteHandler();
+        rewriteHandler.setServer(_server);
+        rewriteHandler.setHandler(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException,
+                    ServletException
+            {
+                response.setStatus(HttpStatus.CREATED_201);
+                request.setAttribute("target",target);
+                request.setAttribute("URI",request.getRequestURI());
+                request.setAttribute("info",request.getPathInfo());
+            }
+        });
+        rewriteHandler.start();
+
+        TerminatingRegexRule rule1 = new TerminatingRegexRule();
+        rule1.setRegex("^/login.jsp$");
+        rewriteHandler.addRule(rule1);
+        RedirectRegexRule rule2 = new RedirectRegexRule();
+        rule2.setRegex("^/login.*$");
+        rule2.setReplacement("http://login.company.com/");
+        rewriteHandler.addRule(rule2);
+
+        start(false);
+    }
+
+    private void assertIsRedirect(int expectedStatus, String expectedLocation)
+    {
+        assertThat("Response Status",_response.getStatus(),is(expectedStatus));
+        assertThat("Response Location Header",_response.getHeader(HttpHeader.LOCATION.asString()),is(expectedLocation));
+    }
+
+    private void assertIsRequest(String expectedRequestPath)
+    {
+        assertThat("Response Status",_response.getStatus(),is(HttpStatus.CREATED_201));
+        assertThat("Request Target",_request.getAttribute("target"),is(expectedRequestPath));
+    }
+
+    @Test
+    public void testTerminatingEarly() throws IOException, ServletException
+    {
+        rewriteHandler.handle("/login.jsp",_request,_request,_response);
+        assertIsRequest("/login.jsp");
+    }
+
+    @Test
+    public void testNoTerminationDo() throws IOException, ServletException
+    {
+        rewriteHandler.handle("/login.do",_request,_request,_response);
+        assertIsRedirect(HttpStatus.MOVED_TEMPORARILY_302,"http://login.company.com/");
+    }
+
+    @Test
+    public void testNoTerminationDir() throws IOException, ServletException
+    {
+        rewriteHandler.handle("/login/",_request,_request,_response);
+        assertIsRedirect(HttpStatus.MOVED_TEMPORARILY_302,"http://login.company.com/");
+    }
+}
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ValidUrlRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ValidUrlRuleTest.java
index 005bae6..9d602be 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ValidUrlRuleTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ValidUrlRuleTest.java
@@ -41,7 +41,7 @@
     public void testValidUrl() throws Exception
     {
         _rule.setCode("404");
-        _request.setRequestURI("/valid/uri.html");
+        _request.setURIPathQuery("/valid/uri.html");
         
         _rule.matchAndApply(_request.getRequestURI(), _request, _response);
 
@@ -52,7 +52,7 @@
     public void testInvalidUrl() throws Exception
     {
         _rule.setCode("404");
-        _request.setRequestURI("/invalid%0c/uri.html");
+        _request.setURIPathQuery("/invalid%0c/uri.html");
         
         String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response);
 
@@ -64,7 +64,7 @@
     {
         _rule.setCode("405");
         _rule.setReason("foo");
-        _request.setRequestURI("/%00/");
+        _request.setURIPathQuery("/%00/");
         
         String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response);
 
@@ -77,7 +77,7 @@
     {
         _rule.setCode("405");
         _rule.setReason("foo");
-        _request.setRequestURI("/jsp/bean1.jsp%00");
+        _request.setURIPathQuery("/jsp/bean1.jsp%00");
         
         String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response);
 
@@ -91,7 +91,7 @@
     {
         _rule.setCode("405");
         _rule.setReason("foo");
-        _request.setRequestURI("/jsp/shamrock-%00%E2%98%98.jsp");
+        _request.setURIPathQuery("/jsp/shamrock-%00%E2%98%98.jsp");
         
         String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response);
 
@@ -105,7 +105,7 @@
     {
         _rule.setCode("405");
         _rule.setReason("foo");
-        _request.setRequestURI("/jsp/shamrock-%E2%98%98.jsp");
+        _request.setURIPathQuery("/jsp/shamrock-%E2%98%98.jsp");
         
         String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response);
 
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/VirtualHostRuleContainerTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/VirtualHostRuleContainerTest.java
index 8e05964..4708815 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/VirtualHostRuleContainerTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/VirtualHostRuleContainerTest.java
@@ -49,7 +49,7 @@
         _fooContainerRule.setRules(new Rule[] { _fooRule });
 
         start(false);
-        _request.setRequestURI("/cheese/bar");
+        _request.setURIPathQuery("/cheese/bar");
         
         _handler.setServer(_server);
         _handler.start();
@@ -58,7 +58,7 @@
     @Test
     public void testArbitraryHost() throws Exception
     {
-        _request.setServerName("cheese.com");
+        _request.setAuthority("cheese.com",0);
         _handler.setRules(new Rule[] { _rule, _fooContainerRule });
         handleRequest();
         assertEquals("{_rule, _fooContainerRule, Host: cheese.com}: applied _rule", "/rule/bar", _request.getRequestURI());
@@ -67,7 +67,7 @@
     @Test
     public void testVirtualHost() throws Exception
     {
-        _request.setServerName("foo.com");
+        _request.setAuthority("foo.com",0);
         _handler.setRules(new Rule[] { _fooContainerRule });
         handleRequest();
         assertEquals("{_fooContainerRule, Host: foo.com}: applied _fooRule", "/cheese/fooRule", _request.getRequestURI());
@@ -76,8 +76,8 @@
     @Test
     public void testCascadingRules() throws Exception
     {
-        _request.setServerName("foo.com");
-        _request.setRequestURI("/cheese/bar");
+        _request.setAuthority("foo.com",0);
+        _request.setURIPathQuery("/cheese/bar");
 
         _rule.setTerminating(false);
         _fooRule.setTerminating(false);
@@ -87,17 +87,17 @@
         handleRequest();
         assertEquals("{_rule, _fooContainerRule}: applied _rule, didn't match _fooRule", "/rule/bar", _request.getRequestURI());
 
-        _request.setRequestURI("/cheese/bar");
+        _request.setURIPathQuery("/cheese/bar");
         _handler.setRules(new Rule[] { _fooContainerRule, _rule });
         handleRequest();
         assertEquals("{_fooContainerRule, _rule}: applied _fooRule, _rule","/rule/fooRule", _request.getRequestURI());
 
-        _request.setRequestURI("/cheese/bar");
+        _request.setURIPathQuery("/cheese/bar");
         _fooRule.setTerminating(true);
         handleRequest();
         assertEquals("{_fooContainerRule, _rule}: (_fooRule is terminating); applied _fooRule, _rule", "/rule/fooRule", _request.getRequestURI());
 
-        _request.setRequestURI("/cheese/bar");
+        _request.setURIPathQuery("/cheese/bar");
         _fooRule.setTerminating(false);
         _fooContainerRule.setTerminating(true);
         handleRequest();
@@ -107,7 +107,7 @@
     @Test
     public void testCaseInsensitiveHostname() throws Exception
     {
-        _request.setServerName("Foo.com");
+        _request.setAuthority("Foo.com",0);
         _fooContainerRule.setVirtualHosts(new String[] {"foo.com"} );
 
         _handler.setRules(new Rule[]{ _fooContainerRule });
@@ -118,21 +118,21 @@
     @Test
     public void testEmptyVirtualHost() throws Exception
     {
-        _request.setServerName("cheese.com");
+        _request.setAuthority("cheese.com",0);
 
         _handler.setRules(new Rule[] { _fooContainerRule });
         _fooContainerRule.setVirtualHosts(null);
         handleRequest();
         assertEquals("{_fooContainerRule: virtual hosts array is null, Host: cheese.com}: apply _fooRule", "/cheese/fooRule", _request.getRequestURI());
 
-        _request.setRequestURI("/cheese/bar");
-        _request.setRequestURI("/cheese/bar");
+        _request.setURIPathQuery("/cheese/bar");
+        _request.setURIPathQuery("/cheese/bar");
         _fooContainerRule.setVirtualHosts(new String[] {});
         handleRequest();
         assertEquals("{_fooContainerRule: virtual hosts array is empty, Host: cheese.com}: apply _fooRule", "/cheese/fooRule", _request.getRequestURI());
 
-        _request.setRequestURI("/cheese/bar");
-        _request.setRequestURI("/cheese/bar");
+        _request.setURIPathQuery("/cheese/bar");
+        _request.setURIPathQuery("/cheese/bar");
         _fooContainerRule.setVirtualHosts(new String[] {null});
         handleRequest();
         assertEquals("{_fooContainerRule: virtual host is null, Host: cheese.com}: apply _fooRule", "/cheese/fooRule", _request.getRequestURI());
@@ -142,14 +142,14 @@
     @Test
     public void testMultipleVirtualHosts() throws Exception
     {
-        _request.setServerName("foo.com");
+        _request.setAuthority("foo.com",0);
         _handler.setRules(new Rule[] {_fooContainerRule });
 
         _fooContainerRule.setVirtualHosts(new String[]{ "cheese.com" });
         handleRequest();
         assertEquals("{_fooContainerRule: vhosts[cheese.com], Host: foo.com}: no effect", "/cheese/bar", _request.getRequestURI());
 
-        _request.setRequestURI("/cheese/bar");
+        _request.setURIPathQuery("/cheese/bar");
         _fooContainerRule.addVirtualHost( "foo.com" );
         handleRequest();
         assertEquals("{_fooContainerRule: vhosts[cheese.com, foo.com], Host: foo.com}: apply _fooRule", "/cheese/fooRule", _request.getRequestURI());
@@ -182,8 +182,8 @@
 
         for(String host: requestHosts)
         {
-            _request.setServerName(host);
-            _request.setRequestURI("/cheese/bar");
+            _request.setAuthority(host,0);
+            _request.setURIPathQuery("/cheese/bar");
             handleRequest();
             if(succeed)
                 assertEquals("{_fooContainerRule, Host: "+host+"}: should apply _fooRule", "/cheese/fooRule", _request.getRequestURI());
diff --git a/jetty-rewrite/src/test/resources/org.mortbay.jetty.rewrite.handler/jetty-rewrite.xml b/jetty-rewrite/src/test/resources/org.mortbay.jetty.rewrite.handler/jetty-rewrite.xml
index b54d24f..9ac5109 100644
--- a/jetty-rewrite/src/test/resources/org.mortbay.jetty.rewrite.handler/jetty-rewrite.xml
+++ b/jetty-rewrite/src/test/resources/org.mortbay.jetty.rewrite.handler/jetty-rewrite.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Configure the Jetty Server                                      -->
@@ -39,12 +39,9 @@
     <Call name="addConnector">
       <Arg>
           <New class="org.eclipse.jetty.server.ServerConnector">
-            <Set name="host"><SystemProperty name="jetty.host" /></Set>
-            <Set name="port"><SystemProperty name="jetty.port" default="8080"/></Set>
+            <Set name="host"><SystemProperty name="jetty.http.host" /></Set>
+            <Set name="port"><SystemProperty name="jetty.http.port" default="8080"/></Set>
             <Set name="idleTimeout">30000</Set>
-            <Set name="Acceptors">2</Set>
-            <Set name="statsOn">false</Set>
-            <Set name="confidentialPort">8443</Set>
           </New>
       </Arg>
     </Call>
@@ -259,7 +256,7 @@
     <Ref refid="RequestLog">
       <Set name="requestLog">
         <New id="RequestLogImpl" class="org.eclipse.jetty.server.NCSARequestLog">
-          <Set name="filename"><SystemProperty name="jetty.logs" default="./logs"/>/yyyy_mm_dd.request.log</Set>
+          <Set name="filename"><SystemProperty name="jetty.requestlog.dir" default="./logs"/>/yyyy_mm_dd.request.log</Set>
           <Set name="filenameDateFormat">yyyy_MM_dd</Set>
           <Set name="retainDays">90</Set>
           <Set name="append">true</Set>
diff --git a/jetty-rhttp/README.TXT b/jetty-rhttp/README.TXT
deleted file mode 100644
index 46ca4e7..0000000
--- a/jetty-rhttp/README.TXT
+++ /dev/null
@@ -1,33 +0,0 @@
-Reverse HTTP
-
-The HTTP server paradigm is a valuable abstraction for browsing and accessing data and applications in a RESTful fashion from thin clients or
-other applications.  However, when it comes to mobile devices, the server paradigm is often not available because those devices exist on
-restricted networks that do not allow inbound connections.    These devices (eg. phones, tablets, industrial controllers, etc.) often have
-signficant content (eg. photos, video, music, contacts, etc.) and services (eg. GPS, phone, modem, camera, sound) that are worthwile to access
-remotely and often the HTTP server model is very applicable.
-
-The Jetty reverse HTTP module provides a gateway that efficiently allows HTTP connectivety to servers running in outbound-only networks.  There are two key components:
-
-The reverse HTTP connector is a jetty connector (like the HTTP, SSL, AJP connectors) that accepts HTTP requests for the Jetty server instance.  However, the reverse HTTP connector does not accept inbound TCP/IP connections.  Instead it makes an outbound HTTP connection to the reverse HTTP gateway and uses a long polling mechanism to efficiently and asynchronously fetch requests and send responses.
-
-The reverse HTTP gateway is a jetty server that accepts inbound connections from one or more Reverse HTTP connectors and makes them available as normal HTTP targets.
-
-To demonstrate this from a source release, first run a gateway instance:
-
-    cd jetty-reverse-http/reverse-http-gateway
-    mvn exec:java
-
-In another window, you can run 3 test servers with reverse connectors with:
-
-    cd jetty-reverse-http/reverse-http-connector
-    mvn exec:java
-
-
-The three servers are using context path ID's at the gateway (virtual host and cookie based mappings can also be done), so you can access the
-three servers via the gateway at:
-
-    http://localhost:8080/gw/A
-    http://localhost:8080/gw/B
-    http://localhost:8080/gw/C
-
-
diff --git a/jetty-rhttp/jetty-rhttp-client/pom.xml b/jetty-rhttp/jetty-rhttp-client/pom.xml
deleted file mode 100644
index 14dc5b3..0000000
--- a/jetty-rhttp/jetty-rhttp-client/pom.xml
+++ /dev/null
@@ -1,95 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.rhttp</groupId>
-        <artifactId>jetty-rhttp-project</artifactId>
-        <version>9.0.0-SNAPSHOT</version>
-    </parent>
-
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>reverse-http-client</artifactId>
-    <packaging>jar</packaging>
-    <name>Jetty :: Reverse HTTP :: Client</name>
-
-    <properties>
-        <bundle-symbolic-name>${project.groupId}.rhttp.client</bundle-symbolic-name>
-    </properties>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>manifest</goal>
-                        </goals>
-                        <configuration>
-                            <instructions>
-                                 <Import-Package>*</Import-Package>
-                            </instructions>
-                          </configuration>
-                       </execution>
-                  </executions>
-            </plugin>
-            <plugin>
-              <!--
-              Required for OSGI
-              -->
-              <groupId>org.apache.maven.plugins</groupId>
-              <artifactId>maven-jar-plugin</artifactId>
-              <configuration>
-                  <archive>
-                      <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-                  </archive>
-              </configuration>
-            </plugin>
-        </plugins>
-    </build>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-util</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-io</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-client</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-http</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.httpcomponents</groupId>
-            <artifactId>httpclient</artifactId>
-            <version>4.0</version>
-        </dependency>
-        <dependency>
-            <groupId>net.jcip</groupId>
-            <artifactId>jcip-annotations</artifactId>
-            <version>1.0</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty.toolchain</groupId>
-          <artifactId>jetty-test-helper</artifactId>
-          <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-server</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-
-</project>
diff --git a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/AbstractClient.java b/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/AbstractClient.java
deleted file mode 100644
index 1886253..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/AbstractClient.java
+++ /dev/null
@@ -1,270 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import org.eclipse.jetty.util.component.AbstractLifeCycle;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * @version $Revision$ $Date$
- */
-public abstract class AbstractClient extends AbstractLifeCycle implements RHTTPClient
-{
-    private final Logger logger = Log.getLogger("org.mortbay.jetty.rhttp.client");
-    private final List<RHTTPListener> listeners = new CopyOnWriteArrayList<RHTTPListener>();
-    private final List<ClientListener> clientListeners = new CopyOnWriteArrayList<ClientListener>();
-    private final String targetId;
-    private volatile Status status = Status.DISCONNECTED;
-
-    public AbstractClient(String targetId)
-    {
-        this.targetId = targetId;
-    }
-
-    public String getGatewayURI()
-    {
-        return "http://"+getHost()+":"+getPort()+getPath();
-    }
-
-    public String getTargetId()
-    {
-        return targetId;
-    }
-
-    public Logger getLogger()
-    {
-        return logger;
-    }
-
-    public void addListener(RHTTPListener listener)
-    {
-        listeners.add(listener);
-    }
-
-    public void removeListener(RHTTPListener listener)
-    {
-        listeners.remove(listener);
-    }
-
-    public void addClientListener(ClientListener listener)
-    {
-        clientListeners.add(listener);
-    }
-
-    public void removeClientListener(ClientListener listener)
-    {
-        clientListeners.remove(listener);
-    }
-
-    protected void notifyRequests(List<RHTTPRequest> requests)
-    {
-        for (RHTTPRequest request : requests)
-        {
-            for (RHTTPListener listener : listeners)
-            {
-                try
-                {
-                    listener.onRequest(request);
-                }
-                catch (Throwable x)
-                {
-                    logger.warn("Listener " + listener + " threw", x);
-                    try
-                    {
-                        deliver(newExceptionResponse(request.getId(), x));
-                    }
-                    catch (IOException xx)
-                    {
-                        logger.debug("Could not deliver exception response", xx);
-                    }
-                }
-            }
-        }
-    }
-
-    protected RHTTPResponse newExceptionResponse(int requestId, Throwable x)
-    {
-        try
-        {
-            int statusCode = 500;
-            String statusMessage = "Internal Server Error";
-            Map<String, String> headers = new HashMap<String, String>();
-            byte[] body = x.toString().getBytes("UTF-8");
-            return new RHTTPResponse(requestId, statusCode, statusMessage, headers, body);
-        }
-        catch (UnsupportedEncodingException xx)
-        {
-            throw new AssertionError(xx);
-        }
-    }
-
-    protected void notifyConnectRequired()
-    {
-        for (ClientListener listener : clientListeners)
-        {
-            try
-            {
-                listener.connectRequired();
-            }
-            catch (Throwable x)
-            {
-                logger.warn("ClientListener " + listener + " threw", x);
-            }
-        }
-    }
-
-    protected void notifyConnectException()
-    {
-        for (ClientListener listener : clientListeners)
-        {
-            try
-            {
-                listener.connectException();
-            }
-            catch (Throwable x)
-            {
-                logger.warn("ClientListener " + listener + " threw", x);
-            }
-        }
-    }
-
-    protected void notifyConnectClosed()
-    {
-        for (ClientListener listener : clientListeners)
-        {
-            try
-            {
-                listener.connectClosed();
-            }
-            catch (Throwable xx)
-            {
-                logger.warn("ClientListener " + listener + " threw", xx);
-            }
-        }
-    }
-
-    protected void notifyDeliverException(RHTTPResponse response)
-    {
-        for (ClientListener listener : clientListeners)
-        {
-            try
-            {
-                listener.deliverException(response);
-            }
-            catch (Throwable x)
-            {
-                logger.warn("ClientListener " + listener + " threw", x);
-            }
-        }
-    }
-
-    protected String urlEncode(String value)
-    {
-        try
-        {
-            return URLEncoder.encode(value, "UTF-8");
-        }
-        catch (UnsupportedEncodingException x)
-        {
-            getLogger().debug("", x);
-            return null;
-        }
-    }
-
-    protected boolean isConnected()
-    {
-        return status == Status.CONNECTED;
-    }
-
-    protected boolean isDisconnecting()
-    {
-        return status == Status.DISCONNECTING;
-    }
-
-    protected boolean isDisconnected()
-    {
-        return status == Status.DISCONNECTED;
-    }
-
-    public void connect() throws IOException
-    {
-        if (isDisconnected())
-            status = Status.CONNECTING;
-
-        syncHandshake();
-        this.status = Status.CONNECTED;
-
-        asyncConnect();
-    }
-
-    public void disconnect() throws IOException
-    {
-        if (isConnected())
-        {
-            status = Status.DISCONNECTING;
-            try
-            {
-                syncDisconnect();
-            }
-            finally
-            {
-                status = Status.DISCONNECTED;
-            }
-        }
-    }
-
-    public void deliver(RHTTPResponse response) throws IOException
-    {
-        asyncDeliver(response);
-    }
-
-    protected abstract void syncHandshake() throws IOException;
-
-    protected abstract void asyncConnect();
-
-    protected abstract void syncDisconnect() throws IOException;
-
-    protected abstract void asyncDeliver(RHTTPResponse response);
-
-    protected void connectComplete(byte[] responseContent) throws IOException
-    {
-        List<RHTTPRequest> requests = RHTTPRequest.fromFrameBytes(responseContent);
-        getLogger().debug("Client {} connect returned from gateway, requests {}", getTargetId(), requests);
-
-        // Requests are arrived, reconnect while we process them
-        if (!isDisconnecting() && !isDisconnected())
-            asyncConnect();
-
-        notifyRequests(requests);
-    }
-
-    protected enum Status
-    {
-        CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/ApacheClient.java b/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/ApacheClient.java
deleted file mode 100644
index cc964f1..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/ApacheClient.java
+++ /dev/null
@@ -1,156 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-import java.io.IOException;
-
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.NoHttpResponseException;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.util.EntityUtils;
-
-/**
- * Implementation of {@link RHTTPClient} that uses Apache's HttpClient.
- *
- * @version $Revision$ $Date$
- */
-public class ApacheClient extends AbstractClient
-{
-    private final HttpClient httpClient;
-    private final String gatewayPath;
-
-    public ApacheClient(HttpClient httpClient, String gatewayPath, String targetId)
-    {
-        super(targetId);
-        this.httpClient = httpClient;
-        this.gatewayPath = gatewayPath;
-    }
-
-    public String getHost()
-    {
-        return ((HttpHost)httpClient.getParams().getParameter("http.default-host")).getHostName();
-    }
-
-    public int getPort()
-    {
-        return ((HttpHost)httpClient.getParams().getParameter("http.default-host")).getPort();
-    }
-    
-    public String getPath()
-    {
-        return gatewayPath;
-    }
-
-    protected void syncHandshake() throws IOException
-    {
-        HttpPost handshake = new HttpPost(gatewayPath + "/" + urlEncode(getTargetId()) + "/handshake");
-        HttpResponse response = httpClient.execute(handshake);
-        int statusCode = response.getStatusLine().getStatusCode();
-        HttpEntity entity = response.getEntity();
-        if (entity != null)
-            entity.consumeContent();
-        if (statusCode != HttpStatus.SC_OK)
-            throw new IOException("Handshake failed");
-        getLogger().debug("Client {} handshake returned from gateway", getTargetId(), null);
-    }
-
-    protected void asyncConnect()
-    {
-        new Thread()
-        {
-            @Override
-            public void run()
-            {
-                try
-                {
-                    HttpPost connect = new HttpPost(gatewayPath + "/" + urlEncode(getTargetId()) + "/connect");
-                    getLogger().debug("Client {} connect sent to gateway", getTargetId(), null);
-                    HttpResponse response = httpClient.execute(connect);
-                    int statusCode = response.getStatusLine().getStatusCode();
-                    HttpEntity entity = response.getEntity();
-                    byte[] responseContent = EntityUtils.toByteArray(entity);
-                    if (statusCode == HttpStatus.SC_OK)
-                        connectComplete(responseContent);
-                    else if (statusCode == HttpStatus.SC_UNAUTHORIZED)
-                        notifyConnectRequired();
-                    else
-                        notifyConnectException();
-                }
-                catch (NoHttpResponseException x)
-                {
-                    notifyConnectClosed();
-                }
-                catch (IOException x)
-                {
-                    getLogger().debug("", x);
-                    notifyConnectException();
-                }
-            }
-        }.start();
-    }
-
-    protected void syncDisconnect() throws IOException
-    {
-        HttpPost disconnect = new HttpPost(gatewayPath + "/" + urlEncode(getTargetId()) + "/disconnect");
-        HttpResponse response = httpClient.execute(disconnect);
-        int statusCode = response.getStatusLine().getStatusCode();
-        HttpEntity entity = response.getEntity();
-        if (entity != null)
-            entity.consumeContent();
-        if (statusCode != HttpStatus.SC_OK)
-            throw new IOException("Disconnect failed");
-        getLogger().debug("Client {} disconnect returned from gateway", getTargetId(), null);
-    }
-
-    protected void asyncDeliver(final RHTTPResponse response)
-    {
-        new Thread()
-        {
-            @Override
-            public void run()
-            {
-                try
-                {
-                    HttpPost deliver = new HttpPost(gatewayPath + "/" + urlEncode(getTargetId()) + "/deliver");
-                    deliver.setEntity(new ByteArrayEntity(response.getFrameBytes()));
-                    getLogger().debug("Client {} deliver sent to gateway, response {}", getTargetId(), response);
-                    HttpResponse httpResponse = httpClient.execute(deliver);
-                    int statusCode = httpResponse.getStatusLine().getStatusCode();
-                    HttpEntity entity = httpResponse.getEntity();
-                    if (entity != null)
-                        entity.consumeContent();
-                    if (statusCode == HttpStatus.SC_UNAUTHORIZED)
-                        notifyConnectRequired();
-                    else if (statusCode != HttpStatus.SC_OK)
-                        notifyDeliverException(response);
-                }
-                catch (IOException x)
-                {
-                    getLogger().debug("", x);
-                    notifyDeliverException(response);
-                }
-            }
-        }.start();
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/ClientListener.java b/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/ClientListener.java
deleted file mode 100644
index bb39551..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/ClientListener.java
+++ /dev/null
@@ -1,67 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-/**
- * A listener for network-related events happening on the gateway client.
- *
- * @version $Revision$ $Date$
- */
-public interface ClientListener
-{
-    /**
-     * Called when the client detects that the server requested a new connect.
-     */
-    public void connectRequired();
-
-    /**
-     * Called when the client detects that the connection has been closed by the server.
-     */
-    public void connectClosed();
-
-    /**
-     * Called when the client detects a generic exception while trying to connect to the server.
-     */
-    public void connectException();
-
-    /**
-     * Called when the client detects a generic exception while tryint to deliver to the server.
-     * @param response the Response object that should have been sent to the server
-     */
-    public void deliverException(RHTTPResponse response);
-
-    public static class Adapter implements ClientListener
-    {
-        public void connectRequired()
-        {
-        }
-
-        public void connectClosed()
-        {
-        }
-
-        public void connectException()
-        {
-        }
-
-        public void deliverException(RHTTPResponse response)
-        {
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/JettyClient.java b/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/JettyClient.java
deleted file mode 100644
index 36c9b23..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/JettyClient.java
+++ /dev/null
@@ -1,306 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.IOException;
-
-import org.eclipse.jetty.client.Address;
-import org.eclipse.jetty.client.ContentExchange;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpExchange;
-import org.eclipse.jetty.http.HttpMethods;
-import org.eclipse.jetty.http.HttpURI;
-import org.eclipse.jetty.io.Buffer;
-import org.eclipse.jetty.io.ByteArrayBuffer;
-import org.eclipse.jetty.io.EofException;
-
-/**
- * Implementation of {@link RHTTPClient} that uses Jetty's HttpClient.
- *
- * @version $Revision$ $Date$
- */
-public class JettyClient extends AbstractClient
-{
-    private final HttpClient httpClient;
-    private final Address gatewayAddress;
-    private final String gatewayPath;
-
-    public JettyClient(HttpClient httpClient, Address gatewayAddress, String gatewayPath, String targetId)
-    {
-        super(targetId);
-        this.httpClient = httpClient;
-        this.gatewayAddress = gatewayAddress;
-        this.gatewayPath = gatewayPath;
-    }
-    
-    public JettyClient(HttpClient httpClient, String gatewayURI, String targetId)
-    {
-        super(targetId);
-        
-        HttpURI uri = new HttpURI(gatewayURI);
-        
-        this.httpClient = httpClient;
-        this.gatewayAddress = new Address(uri.getHost(),uri.getPort());
-        this.gatewayPath = uri.getPath();
-    }
-    
-    public String getHost()
-    {
-        return gatewayAddress.getHost();
-    }
-
-    public int getPort()
-    {
-        return gatewayAddress.getPort();
-    }
-
-    public String getPath()
-    {
-        return gatewayPath;
-    }
-    
-    @Override
-    protected void doStart() throws Exception
-    {
-        httpClient.start();
-        super.doStart();
-    }
-
-    @Override
-    protected void doStop() throws Exception
-    {
-        super.doStop();
-        httpClient.stop();
-    }
-
-    protected void syncHandshake() throws IOException
-    {
-        HandshakeExchange exchange = new HandshakeExchange();
-        exchange.setMethod(HttpMethods.POST);
-        exchange.setAddress(gatewayAddress);
-        exchange.setURI(gatewayPath + "/" + urlEncode(getTargetId()) + "/handshake");
-        httpClient.send(exchange);
-        getLogger().debug("Client {} handshake sent to gateway", getTargetId(), null);
-
-        try
-        {
-            int exchangeStatus = exchange.waitForDone();
-            if (exchangeStatus != HttpExchange.STATUS_COMPLETED)
-                throw new IOException("Handshake failed");
-            if (exchange.getResponseStatus() != 200)
-                throw new IOException("Handshake failed");
-            getLogger().debug("Client {} handshake returned from gateway", getTargetId(), null);
-        }
-        catch (InterruptedException x)
-        {
-            Thread.currentThread().interrupt();
-            throw newIOException(x);
-        }
-    }
-
-    private IOException newIOException(Throwable x)
-    {
-        return (IOException)new IOException().initCause(x);
-    }
-
-    protected void asyncConnect()
-    {
-        try
-        {
-            ConnectExchange exchange = new ConnectExchange();
-            exchange.setMethod(HttpMethods.POST);
-            exchange.setAddress(gatewayAddress);
-            exchange.setURI(gatewayPath + "/" + urlEncode(getTargetId()) + "/connect");
-            httpClient.send(exchange);
-            getLogger().debug("Client {} connect sent to gateway", getTargetId(), null);
-        }
-        catch (IOException x)
-        {
-            getLogger().debug("Could not send exchange", x);
-            throw new RuntimeException(x);
-        }
-    }
-
-    protected void syncDisconnect() throws IOException
-    {
-        DisconnectExchange exchange = new DisconnectExchange();
-        exchange.setMethod(HttpMethods.POST);
-        exchange.setAddress(gatewayAddress);
-        exchange.setURI(gatewayPath + "/" + urlEncode(getTargetId()) + "/disconnect");
-        httpClient.send(exchange);
-        getLogger().debug("Client {} disconnect sent to gateway", getTargetId(), null);
-        try
-        {
-            int status = exchange.waitForDone();
-            if (status != HttpExchange.STATUS_COMPLETED)
-                throw new IOException("Disconnect failed");
-            if (exchange.getResponseStatus() != 200)
-                throw new IOException("Disconnect failed");
-            getLogger().debug("Client {} disconnect returned from gateway", getTargetId(), null);
-        }
-        catch (InterruptedException x)
-        {
-            Thread.currentThread().interrupt();
-            throw newIOException(x);
-        }
-    }
-
-    protected void asyncDeliver(RHTTPResponse response)
-    {
-        try
-        {
-            DeliverExchange exchange = new DeliverExchange(response);
-            exchange.setMethod(HttpMethods.POST);
-            exchange.setAddress(gatewayAddress);
-            exchange.setURI(gatewayPath + "/" + urlEncode(getTargetId()) + "/deliver");
-            exchange.setRequestContent(new ByteArrayBuffer(response.getFrameBytes()));
-            httpClient.send(exchange);
-            getLogger().debug("Client {} deliver sent to gateway, response {}", getTargetId(), response);
-        }
-        catch (IOException x)
-        {
-            getLogger().debug("Could not send exchange", x);
-            throw new RuntimeException(x);
-        }
-    }
-
-    protected class HandshakeExchange extends ContentExchange
-    {
-        protected HandshakeExchange()
-        {
-            super(true);
-        }
-        
-        @Override
-        protected void onConnectionFailed(Throwable x)
-        {
-            getLogger().warn(x.toString());
-            getLogger().debug(x);
-        }
-    }
-
-    protected class ConnectExchange extends ContentExchange
-    {
-        private final ByteArrayOutputStream content = new ByteArrayOutputStream();
-
-        protected ConnectExchange()
-        {
-            super(true);
-        }
-
-        @Override
-        protected void onResponseContent(Buffer buffer) throws IOException
-        {
-            buffer.writeTo(content);
-        }
-
-        @Override
-        protected void onResponseComplete()
-        {
-            int responseStatus = getResponseStatus();
-            if (responseStatus == 200)
-            {
-                try
-                {
-                    connectComplete(content.toByteArray());
-                }
-                catch (IOException x)
-                {
-                    onException(x);
-                }
-            }
-            else if (responseStatus == 401)
-            {
-                notifyConnectRequired();
-            }
-            else
-            {
-                notifyConnectException();
-            }
-        }
-
-        @Override
-        protected void onException(Throwable x)
-        {
-            getLogger().debug(x);
-            if (x instanceof EofException || x instanceof EOFException)
-            {
-                notifyConnectClosed();
-            }
-            else
-            {
-                notifyConnectException();
-            }
-        }
-        
-        @Override
-        protected void onConnectionFailed(Throwable x)
-        {
-            getLogger().debug(x);
-        }
-    }
-
-    protected class DisconnectExchange extends ContentExchange
-    {
-        protected DisconnectExchange()
-        {
-            super(true);
-        }
-    }
-
-    protected class DeliverExchange extends ContentExchange
-    {
-        private final RHTTPResponse response;
-
-        protected DeliverExchange(RHTTPResponse response)
-        {
-            super(true);
-            this.response = response;
-        }
-
-        @Override
-        protected void onResponseComplete() throws IOException
-        {
-            int responseStatus = getResponseStatus();
-            if (responseStatus == 401)
-            {
-                notifyConnectRequired();
-            }
-            else if (responseStatus != 200)
-            {
-                notifyDeliverException(response);
-            }
-        }
-
-        @Override
-        protected void onException(Throwable x)
-        {
-            getLogger().debug(x);
-            notifyDeliverException(response);
-        }
-
-        @Override
-        protected void onConnectionFailed(Throwable x)
-        {
-            getLogger().debug(x);
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RHTTPClient.java b/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RHTTPClient.java
deleted file mode 100644
index c933ded..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RHTTPClient.java
+++ /dev/null
@@ -1,133 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-import java.io.IOException;
-
-/**
- * <p><tt>RHTTPClient</tt> represent a client of the gateway server.</p>
- * <p>A <tt>Client</tt> has a server side counterpart with which communicates
- * using a comet protocol.<br /> The <tt>Client</tt>, its server-side
- * counterpart and the comet protocol form the <em>Half-Object plus Protocol</em>
- * pattern.</p>
- * <p>A <tt>Client</tt> must first connect to the gateway server, to let the gateway
- * server know its targetId, an identifier that uniquely distinguish this
- * <tt>Client</tt> from other <tt>Client</tt>s.</p>
- * <p>Once connected, the gateway server will use a comet procotol to notify the
- * <tt>Client</tt> of server-side events, and the <tt>Client</tt> can send
- * information to the gateway server to notify it of client-side events.</p>
- * <p>Server-side event are notified to {@link RHTTPListener}s, while relevant
- * network events are communicated to {@link ClientListener}s.</p>
- *
- * @version $Revision$ $Date$
- */
-public interface RHTTPClient
-{
-    /**
-     * @return The gateway uri, typically "http://gatewayhost:gatewayport/gatewaypath".
-     */
-    public String getGatewayURI();
-    
-    /**
-     * @return The gateway host
-     */
-    public String getHost();
-    
-    /**
-     * @return The gateway port
-     */
-    public int getPort();
-    
-    /**
-     * @return The gateway path
-     */
-    public String getPath();
-    
-    /**
-     * @return the targetId that uniquely identifies this client.
-     */
-    public String getTargetId();
-
-    /**
-     * <p>Connects to the gateway server, establishing the long poll communication
-     * with the gateway server to be notified of server-side events.</p>
-     * <p>The connect is performed in two steps:
-     * <ul>
-     * <li>first, a connect message is sent to the gateway server; the gateway server
-     * will notice this is a first connect message and reply immediately with
-     * an empty response</li>
-     * <li>second, another connect message is sent to the gateway server which interprets
-     * it as a long poll request</li>
-     * </ul>
-     * The long poll request may return either because one or more server-side events
-     * happened, or because it expired. </p>
-     * <p>Any connect message after the first is treated as a long poll request.</p>
-     *
-     * @throws IOException if it is not possible to connect to the gateway server
-     * @see #disconnect()
-     */
-    public void connect() throws IOException;
-
-    /**
-     * <p>Disconnects from the gateway server.</p>
-     * <p>Just after the disconnect request is processed by to the gateway server, it will
-     * return the currently outstanding long poll request.</p>
-     * <p>If this client is not connected, it does nothing</p>
-     *
-     * @throws IOException if it is not possible to contact the gateway server to disconnect
-     * @see #connect()
-     */
-    public void disconnect() throws IOException;
-
-    /**
-     * <p>Sends a response to the gateway server.</p>
-     *
-     * @param response the response to send
-     * @throws IOException if it is not possible to contact the gateway server
-     */
-    public void deliver(RHTTPResponse response) throws IOException;
-
-    /**
-     * <p>Adds the given listener to this client.</p>
-     * @param listener the listener to add
-     * @see #removeListener(RHTTPListener)
-     */
-    public void addListener(RHTTPListener listener);
-
-    /**
-     * <p>Removes the given listener from this client.</p>
-     * @param listener the listener to remove
-     * @see #addListener(RHTTPListener)
-     */
-    public void removeListener(RHTTPListener listener);
-
-    /**
-     * <p>Adds the given client listener to this client.</p>
-     * @param listener the client listener to add
-     * @see #removeClientListener(ClientListener)
-     */
-    public void addClientListener(ClientListener listener);
-
-    /**
-     * <p>Removes the given client listener from this client.</p>
-     * @param listener the client listener to remove
-     * @see #addClientListener(ClientListener)
-     */
-    public void removeClientListener(ClientListener listener);
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RHTTPListener.java b/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RHTTPListener.java
deleted file mode 100644
index 2d70399..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RHTTPListener.java
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-/**
- * <p>Implementations of this class listen for requests arriving from the gateway server
- * and notified by {@link RHTTPClient}.</p>
- *
- * @version $Revision$ $Date$
- */
-public interface RHTTPListener
-{
-    /**
-     * Callback method called by {@link RHTTPClient} to inform that the gateway server
-     * sent a request to the gateway client.
-     * @param request the request sent by the gateway server.
-     * @throws Exception allowed to be thrown by implementations
-     */
-    public void onRequest(RHTTPRequest request) throws Exception;
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RHTTPRequest.java b/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RHTTPRequest.java
deleted file mode 100644
index 774eac8..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RHTTPRequest.java
+++ /dev/null
@@ -1,266 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.jetty.http.HttpParser;
-import org.eclipse.jetty.io.Buffer;
-import org.eclipse.jetty.io.ByteArrayBuffer;
-
-/**
- * <p>Represents the external request information that is carried over the comet protocol.</p>
- * <p>Instances of this class are converted into an opaque byte array of the form:</p>
- * <pre>
- * &lt;request-id&gt; SPACE &lt;request-length&gt; CRLF
- * &lt;external-request&gt;
- * </pre>
- * <p>The byte array form is carried as body of a normal HTTP response returned by the gateway server
- * to the gateway client.</p>
- * @see RHTTPResponse
- * @version $Revision$ $Date$
- */
-public class RHTTPRequest
-{
-    private static final String CRLF = "\r\n";
-    private static final byte[] CRLF_BYTES = CRLF.getBytes();
-
-    private final int id;
-    private final byte[] requestBytes;
-    private final byte[] frameBytes;
-    private volatile String method;
-    private volatile String uri;
-    private volatile Map<String, String> headers;
-    private volatile byte[] body;
-
-    public static List<RHTTPRequest> fromFrameBytes(byte[] bytes)
-    {
-        List<RHTTPRequest> result = new ArrayList<RHTTPRequest>();
-        int start = 0;
-        while (start < bytes.length)
-        {
-            // Scan until we find the space
-            int end = start;
-            while (bytes[end] != ' ') ++end;
-            int requestId = Integer.parseInt(new String(bytes, start, end - start));
-            start = end + 1;
-
-            // Scan until end of line
-            while (bytes[end] != '\n') ++end;
-            int length = Integer.parseInt(new String(bytes, start, end - start - 1));
-            start = end + 1;
-
-            byte[] requestBytes = new byte[length];
-            System.arraycopy(bytes, start, requestBytes, 0, length);
-            RHTTPRequest request = fromRequestBytes(requestId, requestBytes);
-            result.add(request);
-            start += length;
-        }
-        return result;
-    }
-
-    public static RHTTPRequest fromRequestBytes(int requestId, byte[] requestBytes)
-    {
-        return new RHTTPRequest(requestId, requestBytes);
-    }
-
-    public RHTTPRequest(int id, String method, String uri, Map<String, String> headers, byte[] body)
-    {
-        this.id = id;
-        this.method = method;
-        this.uri = uri;
-        this.headers = headers;
-        this.body = body;
-        this.requestBytes = toRequestBytes();
-        this.frameBytes = toFrameBytes(requestBytes);
-    }
-
-    private RHTTPRequest(int id, byte[] requestBytes)
-    {
-        this.id = id;
-        this.requestBytes = requestBytes;
-        this.frameBytes = toFrameBytes(requestBytes);
-        // Other fields are lazily initialized
-    }
-
-    private void initialize()
-    {
-        try
-        {
-            final ByteArrayOutputStream body = new ByteArrayOutputStream();
-            HttpParser parser = new HttpParser(new ByteArrayBuffer(requestBytes), new HttpParser.EventHandler()
-            {
-                @Override
-                public void startRequest(Buffer method, Buffer uri, Buffer httpVersion) throws IOException
-                {
-                    RHTTPRequest.this.method = method.toString("UTF-8");
-                    RHTTPRequest.this.uri = uri.toString("UTF-8");
-                    RHTTPRequest.this.headers = new LinkedHashMap<String, String>();
-                }
-
-                @Override
-                public void startResponse(Buffer httpVersion, int statusCode, Buffer statusMessage) throws IOException
-                {
-                }
-
-                @Override
-                public void parsedHeader(Buffer name, Buffer value) throws IOException
-                {
-                    RHTTPRequest.this.headers.put(name.toString("UTF-8"), value.toString("UTF-8"));
-                }
-
-                @Override
-                public void content(Buffer content) throws IOException
-                {
-                    content.writeTo(body);
-                }
-            });
-            parser.parse();
-            this.body = body.toByteArray();
-        }
-        catch (IOException x)
-        {
-            // Cannot happen: we're parsing from a byte[], not from an I/O stream
-            throw new AssertionError(x);
-        }
-    }
-
-    public int getId()
-    {
-        return id;
-    }
-
-    public byte[] getRequestBytes()
-    {
-        return requestBytes;
-    }
-
-    public byte[] getFrameBytes()
-    {
-        return frameBytes;
-    }
-
-    public String getMethod()
-    {
-        if (method == null)
-            initialize();
-        return method;
-    }
-
-    public String getURI()
-    {
-        if (uri == null)
-            initialize();
-        return uri;
-    }
-
-    public Map<String, String> getHeaders()
-    {
-        if (headers == null)
-            initialize();
-        return headers;
-    }
-
-    public byte[] getBody()
-    {
-        if (body == null)
-            initialize();
-        return body;
-    }
-
-    private byte[] toRequestBytes()
-    {
-        try
-        {
-            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-            bytes.write(method.getBytes("UTF-8"));
-            bytes.write(' ');
-            bytes.write(uri.getBytes("UTF-8"));
-            bytes.write(' ');
-            bytes.write("HTTP/1.1".getBytes("UTF-8"));
-            bytes.write(CRLF_BYTES);
-            for (Map.Entry<String, String> entry : headers.entrySet())
-            {
-                bytes.write(entry.getKey().getBytes("UTF-8"));
-                bytes.write(':');
-                bytes.write(' ');
-                bytes.write(entry.getValue().getBytes("UTF-8"));
-                bytes.write(CRLF_BYTES);
-            }
-            bytes.write(CRLF_BYTES);
-            bytes.write(body);
-            bytes.close();
-            return bytes.toByteArray();
-        }
-        catch (IOException x)
-        {
-            throw new AssertionError(x);
-        }
-    }
-
-    private byte[] toFrameBytes(byte[] requestBytes)
-    {
-        try
-        {
-            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-            bytes.write(String.valueOf(id).getBytes("UTF-8"));
-            bytes.write(' ');
-            bytes.write(String.valueOf(requestBytes.length).getBytes("UTF-8"));
-            bytes.write(CRLF_BYTES);
-            bytes.write(requestBytes);
-            bytes.close();
-            return bytes.toByteArray();
-        }
-        catch (IOException x)
-        {
-            throw new AssertionError(x);
-        }
-    }
-
-    @Override
-    public String toString()
-    {
-        // Use fields to avoid initialization
-        StringBuilder builder = new StringBuilder();
-        builder.append(id).append(" ");
-        builder.append(method).append(" ");
-        builder.append(uri).append(" ");
-        builder.append(requestBytes.length).append("/");
-        builder.append(frameBytes.length);
-        return builder.toString();
-    }
-
-    public String toLongString()
-    {
-        // Use getters to trigger initialization
-        StringBuilder builder = new StringBuilder();
-        builder.append(id).append(" ");
-        builder.append(getMethod()).append(" ");
-        builder.append(getURI()).append(CRLF);
-        for (Map.Entry<String, String> header : getHeaders().entrySet())
-            builder.append(header.getKey()).append(": ").append(header.getValue()).append(CRLF);
-        builder.append(getBody().length).append(" body bytes").append(CRLF);
-        return builder.toString();
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RHTTPResponse.java b/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RHTTPResponse.java
deleted file mode 100644
index 0d2eb9e..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RHTTPResponse.java
+++ /dev/null
@@ -1,256 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.eclipse.jetty.http.HttpParser;
-import org.eclipse.jetty.io.Buffer;
-import org.eclipse.jetty.io.ByteArrayBuffer;
-
-/**
- * <p>Represents the resource provider response information that is carried over the comet protocol.</p>
- * <p>Instances of this class are converted into an opaque byte array of the form:</p>
- * <pre>
- * &lt;request-id&gt; SPACE &lt;response-length&gt; CRLF
- * &lt;resource-response&gt;
- * </pre>
- * <p>The byte array form is carried as body of a normal HTTP request made by the gateway client to
- * the gateway server.</p>
- * @see RHTTPRequest
- * @version $Revision$ $Date$
- */
-public class RHTTPResponse
-{
-    private static final String CRLF = "\r\n";
-    private static final byte[] CRLF_BYTES = CRLF.getBytes();
-
-    private final int id;
-    private final byte[] responseBytes;
-    private final byte[] frameBytes;
-    private volatile int code;
-    private volatile String message;
-    private volatile Map<String, String> headers;
-    private volatile byte[] body;
-
-    public static RHTTPResponse fromFrameBytes(byte[] bytes)
-    {
-        int start = 0;
-        // Scan until we find the space
-        int end = start;
-        while (bytes[end] != ' ') ++end;
-        int responseId = Integer.parseInt(new String(bytes, start, end - start));
-        start = end + 1;
-
-        // Scan until end of line
-        while (bytes[end] != '\n') ++end;
-        int length = Integer.parseInt(new String(bytes, start, end - start - 1));
-        start = end + 1;
-
-        byte[] responseBytes = new byte[length];
-        System.arraycopy(bytes, start, responseBytes, 0, length);
-        return fromResponseBytes(responseId, responseBytes);
-    }
-
-    public static RHTTPResponse fromResponseBytes(int id, byte[] responseBytes)
-    {
-        return new RHTTPResponse(id, responseBytes);
-    }
-
-    public RHTTPResponse(int id, int code, String message, Map<String, String> headers, byte[] body)
-    {
-        this.id = id;
-        this.code = code;
-        this.message = message;
-        this.headers = headers;
-        this.body = body;
-        this.responseBytes = toResponseBytes();
-        this.frameBytes = toFrameBytes(responseBytes);
-    }
-
-    private RHTTPResponse(int id, byte[] responseBytes)
-    {
-        this.id = id;
-        this.responseBytes = responseBytes;
-        this.frameBytes = toFrameBytes(responseBytes);
-        // Other fields are lazily initialized
-    }
-
-    private void initialize()
-    {
-        try
-        {
-            final ByteArrayOutputStream body = new ByteArrayOutputStream();
-            HttpParser parser = new HttpParser(new ByteArrayBuffer(responseBytes), new HttpParser.EventHandler()
-            {
-                @Override
-                public void startRequest(Buffer method, Buffer uri, Buffer httpVersion) throws IOException
-                {
-                }
-
-                @Override
-                public void startResponse(Buffer httpVersion, int statusCode, Buffer statusMessage) throws IOException
-                {
-                    RHTTPResponse.this.code = statusCode;
-                    RHTTPResponse.this.message = statusMessage.toString("UTF-8");
-                    RHTTPResponse.this.headers = new LinkedHashMap<String, String>();
-                }
-
-                @Override
-                public void parsedHeader(Buffer name, Buffer value) throws IOException
-                {
-                    RHTTPResponse.this.headers.put(name.toString("UTF-8"), value.toString("UTF-8"));
-                }
-
-                @Override
-                public void content(Buffer content) throws IOException
-                {
-                    content.writeTo(body);
-                }
-            });
-            parser.parse();
-            this.body = body.toByteArray();
-        }
-        catch (IOException x)
-        {
-            // Cannot happen: we're parsing from a byte[], not from an I/O stream
-            throw new AssertionError(x);
-        }
-    }
-
-    public int getId()
-    {
-        return id;
-    }
-
-    public byte[] getResponseBytes()
-    {
-        return responseBytes;
-    }
-
-    public byte[] getFrameBytes()
-    {
-        return frameBytes;
-    }
-
-    public int getStatusCode()
-    {
-        if (code == 0)
-            initialize();
-        return code;
-    }
-
-    public String getStatusMessage()
-    {
-        if (message == null)
-            initialize();
-        return message;
-    }
-
-    public Map<String, String> getHeaders()
-    {
-        if (headers == null)
-            initialize();
-        return headers;
-    }
-
-    public byte[] getBody()
-    {
-        if (body == null)
-            initialize();
-        return body;
-    }
-
-    private byte[] toResponseBytes()
-    {
-        try
-        {
-            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-            bytes.write("HTTP/1.1".getBytes("UTF-8"));
-            bytes.write(' ');
-            bytes.write(String.valueOf(code).getBytes("UTF-8"));
-            bytes.write(' ');
-            bytes.write(message.getBytes("UTF-8"));
-            bytes.write(CRLF_BYTES);
-            for (Map.Entry<String, String> entry : headers.entrySet())
-            {
-                bytes.write(entry.getKey().getBytes("UTF-8"));
-                bytes.write(':');
-                bytes.write(' ');
-                bytes.write(entry.getValue().getBytes("UTF-8"));
-                bytes.write(CRLF_BYTES);
-            }
-            bytes.write(CRLF_BYTES);
-            bytes.write(body);
-            bytes.close();
-            return bytes.toByteArray();
-        }
-        catch (IOException x)
-        {
-            throw new AssertionError(x);
-        }
-    }
-
-    private byte[] toFrameBytes(byte[] responseBytes)
-    {
-        try
-        {
-            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-            bytes.write(String.valueOf(id).getBytes("UTF-8"));
-            bytes.write(' ');
-            bytes.write(String.valueOf(responseBytes.length).getBytes("UTF-8"));
-            bytes.write(CRLF_BYTES);
-            bytes.write(responseBytes);
-            return bytes.toByteArray();
-        }
-        catch (IOException x)
-        {
-            throw new AssertionError(x);
-        }
-    }
-
-    @Override
-    public String toString()
-    {
-        // Use fields to avoid initialization
-        StringBuilder builder = new StringBuilder();
-        builder.append(id).append(" ");
-        builder.append(code).append(" ");
-        builder.append(message).append(" ");
-        builder.append(responseBytes.length).append("/");
-        builder.append(frameBytes.length);
-        return builder.toString();
-    }
-
-    public String toLongString()
-    {
-        // Use getters to trigger initialization
-        StringBuilder builder = new StringBuilder();
-        builder.append(id).append(" ");
-        builder.append(getStatusCode()).append(" ");
-        builder.append(getStatusMessage()).append(CRLF);
-        for (Map.Entry<String, String> header : getHeaders().entrySet())
-            builder.append(header.getKey()).append(": ").append(header.getValue()).append(CRLF);
-        builder.append(getBody().length).append(" body bytes").append(CRLF);
-        return builder.toString();
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RetryingApacheClient.java b/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RetryingApacheClient.java
deleted file mode 100644
index 0e475ee..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/main/java/org/eclipse/jetty/rhttp/client/RetryingApacheClient.java
+++ /dev/null
@@ -1,112 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-import java.io.IOException;
-
-import org.apache.http.client.HttpClient;
-
-/**
- * @version $Revision$ $Date$
- */
-public class RetryingApacheClient extends ApacheClient
-{
-    public RetryingApacheClient(HttpClient httpClient, String gatewayURI, String targetId)
-    {
-        super(httpClient, gatewayURI, targetId);
-        addClientListener(new RetryClientListener());
-    }
-
-    @Override
-    protected void syncHandshake() throws IOException
-    {
-        while (true)
-        {
-            try
-            {
-                super.syncHandshake();
-                break;
-            }
-            catch (IOException x)
-            {
-                getLogger().debug("Handshake failed, backing off and retrying");
-                try
-                {
-                    Thread.sleep(1000);
-                }
-                catch (InterruptedException xx)
-                {
-                    throw (IOException)new IOException().initCause(xx);
-                }
-            }
-        }
-    }
-
-    private class RetryClientListener implements ClientListener
-    {
-        public void connectRequired()
-        {
-            getLogger().debug("Connect requested by server");
-            try
-            {
-                connect();
-            }
-            catch (IOException x)
-            {
-                // The connect() method is retried, so if it fails, it's a hard failure
-                getLogger().debug("Connect failed after server required connect, giving up");
-            }
-        }
-
-        public void connectClosed()
-        {
-            connectException();
-        }
-
-        public void connectException()
-        {
-            getLogger().debug("Connect failed, backing off and retrying");
-            try
-            {
-                Thread.sleep(1000);
-                asyncConnect();
-            }
-            catch (InterruptedException x)
-            {
-                // Ignore and stop retrying
-                Thread.currentThread().interrupt();
-            }
-        }
-
-        public void deliverException(RHTTPResponse response)
-        {
-            getLogger().debug("Deliver failed, backing off and retrying");
-            try
-            {
-                Thread.sleep(1000);
-                asyncDeliver(response);
-            }
-            catch (InterruptedException x)
-            {
-                // Ignore and stop retrying
-                Thread.currentThread().interrupt();
-            }
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/ApacheClientTest.java b/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/ApacheClientTest.java
deleted file mode 100644
index df4f21f..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/ApacheClientTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-import java.io.IOException;
-
-import org.apache.http.HttpHost;
-import org.apache.http.client.HttpRequestRetryHandler;
-import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.conn.scheme.PlainSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpParams;
-import org.apache.http.protocol.HttpContext;
-import org.eclipse.jetty.rhttp.client.ApacheClient;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
-
-
-/**
- * @version $Revision$ $Date$
- */
-public class ApacheClientTest extends ClientTest
-{
-    {
-        ((StdErrLog)Log.getLog()).setHideStacks(!Log.getLog().isDebugEnabled());
-    }
-    
-    private ClientConnectionManager connectionManager;
-
-    protected RHTTPClient createClient(int port, String targetId) throws Exception
-    {
-        SchemeRegistry schemeRegistry = new SchemeRegistry();
-        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), port));
-        connectionManager = new ThreadSafeClientConnManager(new BasicHttpParams(), schemeRegistry);
-        HttpParams httpParams = new BasicHttpParams();
-        httpParams.setParameter("http.default-host", new HttpHost("localhost", port));
-        DefaultHttpClient httpClient = new DefaultHttpClient(connectionManager, httpParams);
-        httpClient.setHttpRequestRetryHandler(new NoRetryHandler());
-        return new ApacheClient(httpClient, "", targetId);
-    }
-
-    protected void destroyClient(RHTTPClient client) throws Exception
-    {
-        connectionManager.shutdown();
-    }
-
-    private class NoRetryHandler implements HttpRequestRetryHandler
-    {
-        public boolean retryRequest(IOException x, int failedAttempts, HttpContext httpContext)
-        {
-            return false;
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/ClientTest.java b/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/ClientTest.java
deleted file mode 100644
index 5cd714c..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/ClientTest.java
+++ /dev/null
@@ -1,299 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-import java.io.IOException;
-import java.util.LinkedHashMap;
-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 junit.framework.TestCase;
-
-import org.eclipse.jetty.rhttp.client.ClientListener;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.bio.SocketConnector;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
-import org.eclipse.jetty.util.log.Log;
-
-
-/**
- * @version $Revision$ $Date$
- */
-public abstract class ClientTest extends TestCase
-{
-    protected abstract RHTTPClient createClient(int port, String targetId) throws Exception;
-
-    protected abstract void destroyClient(RHTTPClient client) throws Exception;
-
-    public void testConnectNoServer() throws Exception
-    {
-        RHTTPClient client = createClient(8080, "test1");
-        try
-        {
-            client.connect();
-            fail();
-        }
-        catch (IOException x)
-        {
-        }
-        finally
-        {
-            destroyClient(client);
-        }
-    }
-
-    public void testServerExceptionOnHandshake() throws Exception
-    {
-        final CountDownLatch serverLatch = new CountDownLatch(1);
-
-        Server server = new Server();
-        Connector connector = new SelectChannelConnector();
-        server.addConnector(connector);
-        server.setHandler(new AbstractHandler()
-        {
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
-            {
-                request.setHandled(true);
-                if (target.endsWith("/handshake"))
-                {
-                    serverLatch.countDown();
-                    throw new TestException();
-                }
-            }
-        });
-        server.start();
-        try
-        {
-            RHTTPClient client = createClient(connector.getLocalPort(), "test2");
-            try
-            {
-                try
-                {
-                    client.connect();
-                    fail();
-                }
-                catch (IOException x)
-                {
-                }
-
-                assertTrue(serverLatch.await(1000, TimeUnit.MILLISECONDS));
-            }
-            finally
-            {
-                destroyClient(client);
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-
-    public void testServerExceptionOnConnect() throws Exception
-    {
-        final CountDownLatch serverLatch = new CountDownLatch(1);
-
-        Server server = new Server();
-        Connector connector = new SelectChannelConnector();
-        server.addConnector(connector);
-        server.setHandler(new AbstractHandler()
-        {
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
-            {
-                request.setHandled(true);
-                if (target.endsWith("/connect"))
-                {
-                    serverLatch.countDown();
-                    throw new TestException();
-                }
-            }
-        });
-        server.start();
-        try
-        {
-            RHTTPClient client = createClient(connector.getLocalPort(), "test3");
-            try
-            {
-                final CountDownLatch connectLatch = new CountDownLatch(1);
-                client.addClientListener(new ClientListener.Adapter()
-                {
-                    @Override
-                    public void connectException()
-                    {
-                        connectLatch.countDown();
-                    }
-                });
-                client.connect();
-
-                assertTrue(serverLatch.await(1000, TimeUnit.MILLISECONDS));
-                assertTrue(connectLatch.await(1000, TimeUnit.MILLISECONDS));
-            }
-            finally
-            {
-                destroyClient(client);
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-
-    public void testServerExceptionOnDeliver() throws Exception
-    {
-        final CountDownLatch serverLatch = new CountDownLatch(1);
-
-        Server server = new Server();
-        Connector connector = new SelectChannelConnector();
-        server.addConnector(connector);
-        server.setHandler(new AbstractHandler()
-        {
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
-            {
-                request.setHandled(true);
-                if (target.endsWith("/connect"))
-                {
-                    serverLatch.countDown();
-                    try
-                    {
-                        // Simulate a long poll timeout
-                        Thread.sleep(10000);
-                    }
-                    catch (InterruptedException x)
-                    {
-                        Thread.currentThread().interrupt();
-                    }
-                }
-                else if (target.endsWith("/deliver"))
-                {
-                    // Throw an exception on deliver
-                    throw new TestException();
-                }
-            }
-        });
-        server.start();
-        try
-        {
-            RHTTPClient client = createClient(connector.getLocalPort(), "test4");
-            try
-            {
-                final CountDownLatch deliverLatch = new CountDownLatch(1);
-                client.addClientListener(new ClientListener.Adapter()
-                {
-                    @Override
-                    public void deliverException(RHTTPResponse response)
-                    {
-                        deliverLatch.countDown();
-                    }
-                });
-                client.connect();
-
-                assertTrue(serverLatch.await(1000, TimeUnit.MILLISECONDS));
-
-                client.deliver(new RHTTPResponse(1, 200, "OK", new LinkedHashMap<String, String>(), new byte[0]));
-
-                assertTrue(deliverLatch.await(1000, TimeUnit.MILLISECONDS));
-            }
-            finally
-            {
-                destroyClient(client);
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-
-    public void testServerShutdownAfterConnect() throws Exception
-    {
-        final CountDownLatch connectLatch = new CountDownLatch(1);
-        final CountDownLatch stopLatch = new CountDownLatch(1);
-
-        Server server = new Server();
-        Connector connector = new SocketConnector();
-        server.addConnector(connector);
-        server.setHandler(new AbstractHandler()
-        {
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
-            {
-                request.setHandled(true);
-                if (target.endsWith("/connect"))
-                {
-                    connectLatch.countDown();
-                    try
-                    {
-                        Thread.sleep(10000);
-                    }
-                    catch (InterruptedException e)
-                    {
-                        stopLatch.countDown();
-                    }
-                }
-            }
-        });
-        server.start();
-        try
-        {
-            RHTTPClient client = createClient(connector.getLocalPort(), "test5");
-            try
-            {
-                final CountDownLatch serverLatch = new CountDownLatch(1);
-                client.addClientListener(new ClientListener.Adapter()
-                {
-                    @Override
-                    public void connectClosed()
-                    {
-                        serverLatch.countDown();
-                    }
-                });
-                client.connect();
-
-                assertTrue(connectLatch.await(2000, TimeUnit.MILLISECONDS));
-
-                server.stop();
-                assertTrue(stopLatch.await(2000, TimeUnit.MILLISECONDS));
-
-                assertTrue(serverLatch.await(2000, TimeUnit.MILLISECONDS));
-            }
-            finally
-            {
-                destroyClient(client);
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-    
-    public static class TestException extends NullPointerException
-    {
-        
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/JettyClientTest.java b/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/JettyClientTest.java
deleted file mode 100644
index 0e1db24..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/JettyClientTest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-import org.eclipse.jetty.client.Address;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.rhttp.client.JettyClient;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
-
-
-/**
- * @version $Revision$ $Date$
- */
-public class JettyClientTest extends ClientTest
-{
-    {
-        ((StdErrLog)Log.getLog()).setHideStacks(!Log.getLog().isDebugEnabled());
-    }
-    
-    private HttpClient httpClient;
-
-    protected RHTTPClient createClient(int port, String targetId) throws Exception
-    {
-        ((StdErrLog)Log.getLog()).setSource(true);
-        httpClient = new HttpClient();
-        httpClient.start();
-        return new JettyClient(httpClient, new Address("localhost", port), "", targetId);
-    }
-
-    protected void destroyClient(RHTTPClient client) throws Exception
-    {
-        httpClient.stop();
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/RequestTest.java b/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/RequestTest.java
deleted file mode 100644
index beca853..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/RequestTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-
-import junit.framework.TestCase;
-
-/**
- * @version $Revision$ $Date$
- */
-public class RequestTest extends TestCase
-{
-    public void testRequestConversions() throws Exception
-    {
-        int id = 1;
-        String method = "GET";
-        String uri = "/test";
-        Map<String, String> headers = new LinkedHashMap<String, String>();
-        headers.put("X", "X");
-        headers.put("Y", "Y");
-        headers.put("Z", "Z");
-        byte[] body = "BODY".getBytes("UTF-8");
-        headers.put("Content-Length", String.valueOf(body.length));
-        RHTTPRequest request1 = new RHTTPRequest(id, method, uri, headers, body);
-        byte[] requestBytes1 = request1.getRequestBytes();
-        RHTTPRequest request2 = RHTTPRequest.fromRequestBytes(id, requestBytes1);
-        assertEquals(id, request2.getId());
-        assertEquals(method, request2.getMethod());
-        assertEquals(uri, request2.getURI());
-        assertEquals(headers, request2.getHeaders());
-        assertTrue(Arrays.equals(request2.getBody(), body));
-
-        byte[] requestBytes2 = request2.getRequestBytes();
-        assertTrue(Arrays.equals(requestBytes1, requestBytes2));
-    }
-
-    public void testFrameConversions() throws Exception
-    {
-        int id = 1;
-        String method = "GET";
-        String uri = "/test";
-        Map<String, String> headers = new LinkedHashMap<String, String>();
-        headers.put("X", "X");
-        headers.put("Y", "Y");
-        headers.put("Z", "Z");
-        byte[] body = "BODY".getBytes("UTF-8");
-        headers.put("Content-Length", String.valueOf(body.length));
-        RHTTPRequest request1 = new RHTTPRequest(id, method, uri, headers, body);
-        byte[] frameBytes1 = request1.getFrameBytes();
-        List<RHTTPRequest> requests = RHTTPRequest.fromFrameBytes(frameBytes1);
-        assertNotNull(requests);
-        assertEquals(1, requests.size());
-        RHTTPRequest request2 = requests.get(0);
-        assertEquals(id, request2.getId());
-        assertEquals(method, request2.getMethod());
-        assertEquals(uri, request2.getURI());
-        assertEquals(headers, request2.getHeaders());
-        assertTrue(Arrays.equals(request2.getBody(), body));
-
-        byte[] frameBytes2 = request2.getFrameBytes();
-        assertTrue(Arrays.equals(frameBytes1, frameBytes2));
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/ResponseTest.java b/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/ResponseTest.java
deleted file mode 100644
index 6fdfe3e..0000000
--- a/jetty-rhttp/jetty-rhttp-client/src/test/java/org/eclipse/jetty/rhttp/client/ResponseTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.client;
-
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
-
-import junit.framework.TestCase;
-
-/**
- * @version $Revision$ $Date$
- */
-public class ResponseTest extends TestCase
-{
-    {
-        ((StdErrLog)Log.getLog()).setHideStacks(!Log.getLog().isDebugEnabled());
-    }
-    
-    public void testResponseConversions() throws Exception
-    {
-        int id = 1;
-        int statusCode = 200;
-        String statusMessage = "OK";
-        Map<String, String> headers = new LinkedHashMap<String, String>();
-        headers.put("X", "X");
-        headers.put("Y", "Y");
-        headers.put("Z", "Z");
-        byte[] body = "BODY".getBytes("UTF-8");
-        RHTTPResponse response1 = new RHTTPResponse(id, statusCode, statusMessage, headers, body);
-        byte[] responseBytes1 = response1.getResponseBytes();
-        RHTTPResponse response2 = RHTTPResponse.fromResponseBytes(id, responseBytes1);
-        assertEquals(id, response2.getId());
-        assertEquals(statusCode, response2.getStatusCode());
-        assertEquals(statusMessage, response2.getStatusMessage());
-        assertEquals(headers, response2.getHeaders());
-        assertTrue(Arrays.equals(response2.getBody(), body));
-
-        byte[] responseBytes2 = response2.getResponseBytes();
-        assertTrue(Arrays.equals(responseBytes1, responseBytes2));
-    }
-
-    public void testFrameConversions() throws Exception
-    {
-        int id = 1;
-        int statusCode = 200;
-        String statusMessage = "OK";
-        Map<String, String> headers = new LinkedHashMap<String, String>();
-        headers.put("X", "X");
-        headers.put("Y", "Y");
-        headers.put("Z", "Z");
-        byte[] body = "BODY".getBytes("UTF-8");
-        RHTTPResponse response1 = new RHTTPResponse(id, statusCode, statusMessage, headers, body);
-        byte[] frameBytes1 = response1.getFrameBytes();
-        RHTTPResponse response2 = RHTTPResponse.fromFrameBytes(frameBytes1);
-        assertEquals(id, response2.getId());
-        assertEquals(statusCode, response2.getStatusCode());
-        assertEquals(response2.getStatusMessage(), statusMessage);
-        assertEquals(headers, response2.getHeaders());
-        assertTrue(Arrays.equals(response2.getBody(), body));
-
-        byte[] frameBytes2 = response2.getFrameBytes();
-        assertTrue(Arrays.equals(frameBytes1, frameBytes2));
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-connector/pom.xml b/jetty-rhttp/jetty-rhttp-connector/pom.xml
deleted file mode 100644
index b248c48..0000000
--- a/jetty-rhttp/jetty-rhttp-connector/pom.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <parent>
-    <groupId>org.eclipse.jetty.rhttp</groupId>
-    <artifactId>jetty-rhttp-project</artifactId>
-    <version>9.0.0-SNAPSHOT</version>
-  </parent>
-
-  <modelVersion>4.0.0</modelVersion>
-  <artifactId>reverse-http-connector</artifactId>
-  <packaging>jar</packaging>
-  <name>Jetty :: Reverse HTTP :: Connector</name>
-
-  <properties>
-      <bundle-symbolic-name>${project.groupId}.rhttp.connector</bundle-symbolic-name>
-  </properties>
-
-  <build>
-      <plugins>
-          <plugin>
-              <groupId>org.codehaus.mojo</groupId>
-              <artifactId>exec-maven-plugin</artifactId>
-              <configuration>
-                <classpathScope>test</classpathScope>
-                <mainClass>org.eclipse.jetty.rhttp.connector.TestReverseServer</mainClass>
-              </configuration>
-          </plugin>
-          <plugin>
-              <groupId>org.apache.felix</groupId>
-              <artifactId>maven-bundle-plugin</artifactId>
-              <extensions>true</extensions>
-              <executions>
-                  <execution>
-                      <goals>
-                          <goal>manifest</goal>
-                      </goals>
-                      <configuration>
-                          <instructions>
-                               <Import-Package>*</Import-Package>
-                          </instructions>
-                        </configuration>
-                     </execution>
-                </executions>
-            </plugin>
-            <plugin>
-              <!--
-              Required for OSGI
-              -->
-              <groupId>org.apache.maven.plugins</groupId>
-              <artifactId>maven-jar-plugin</artifactId>
-              <configuration>
-                  <archive>
-                      <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-                  </archive>
-              </configuration>
-            </plugin>
-      </plugins>
-  </build>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>reverse-http-client</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-util</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-io</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-server</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>example-jetty-embedded</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.toolchain</groupId>
-      <artifactId>jetty-test-helper</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>javax.servlet</groupId>
-      <artifactId>javax.servlet-api</artifactId>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-
-</project>
diff --git a/jetty-rhttp/jetty-rhttp-connector/src/main/config/etc/jetty-rhttp.xml b/jetty-rhttp/jetty-rhttp-connector/src/main/config/etc/jetty-rhttp.xml
deleted file mode 100644
index 2141794..0000000
--- a/jetty-rhttp/jetty-rhttp-connector/src/main/config/etc/jetty-rhttp.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<!-- =============================================================== -->
-<!-- Configure the Jetty Reverse HTTP Connector                      -->
-<!-- =============================================================== -->
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
-
-  <Call name="addConnector">
-    <Arg>
-      <New class="org.eclipse.jetty.rhttp.connector.ReverseHTTPConnector">
-        <New class="org.eclipse.jetty.rhttp.client.JettyClient">
-	  <Arg>
-	    <New class="HttpClient">
-	    </New>
-	  </Arg>
-	  <Arg>http://localhost:8888/</Arg>
-	  <Arg>nodeA</Arg>
-        </New>
-      </New>
-    </Arg>
-  </Call>
-</Configure>
diff --git a/jetty-rhttp/jetty-rhttp-connector/src/main/java/org/eclipse/jetty/rhttp/connector/ReverseHTTPConnector.java b/jetty-rhttp/jetty-rhttp-connector/src/main/java/org/eclipse/jetty/rhttp/connector/ReverseHTTPConnector.java
deleted file mode 100644
index ecb6be1..0000000
--- a/jetty-rhttp/jetty-rhttp-connector/src/main/java/org/eclipse/jetty/rhttp/connector/ReverseHTTPConnector.java
+++ /dev/null
@@ -1,170 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.connector;
-
-import java.io.IOException;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-import org.eclipse.jetty.io.ByteArrayEndPoint;
-import org.eclipse.jetty.io.Connection;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.client.RHTTPListener;
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-import org.eclipse.jetty.server.AbstractConnector;
-import org.eclipse.jetty.server.AbstractHttpConnection;
-import org.eclipse.jetty.server.BlockingHttpConnection;
-import org.eclipse.jetty.util.component.LifeCycle;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * An implementation of a Jetty connector that uses a {@link RHTTPClient} connected
- * to a gateway server to receive requests, feed them to the Jetty server, and
- * forward responses from the Jetty server to the gateway server.
- *
- * @version $Revision$ $Date$
- */
-public class ReverseHTTPConnector extends AbstractConnector implements RHTTPListener
-{
-    private static final Logger LOG = Log.getLogger(ReverseHTTPConnector.class);
-
-    private final BlockingQueue<RHTTPRequest> requests = new LinkedBlockingQueue<RHTTPRequest>();
-    private final RHTTPClient client;
-
-    public ReverseHTTPConnector(RHTTPClient client)
-    {
-        this.client = client;
-        super.setHost(client.getHost());
-        super.setPort(client.getPort());
-    }
-
-    @Override
-    public void setHost(String host)
-    {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void setPort(int port)
-    {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    protected void doStart() throws Exception
-    {
-        if (client instanceof LifeCycle)
-            ((LifeCycle)client).start();
-        super.doStart();
-        client.connect();
-    }
-
-    @Override
-    protected void doStop() throws Exception
-    {
-        client.disconnect();
-        super.doStop();
-        if (client instanceof LifeCycle)
-            ((LifeCycle)client).stop();
-    }
-
-    public void open()
-    {
-        client.addListener(this);
-    }
-
-    public void close()
-    {
-        client.removeListener(this);
-    }
-
-    public int getLocalPort()
-    {
-        return -1;
-    }
-
-    public Object getConnection()
-    {
-        return this;
-    }
-
-    @Override
-    protected void accept(int acceptorId) throws IOException, InterruptedException
-    {
-        RHTTPRequest request = requests.take();
-        IncomingRequest incomingRequest = new IncomingRequest(request);
-        getThreadPool().dispatch(incomingRequest);
-    }
-
-    @Override
-    public void persist(EndPoint endpoint) throws IOException
-    {
-        // Signals that the connection should not be closed
-        // Do nothing in this case, as we run from memory
-    }
-
-    public void onRequest(RHTTPRequest request) throws Exception
-    {
-        requests.add(request);
-    }
-
-    private class IncomingRequest implements Runnable
-    {
-        private final RHTTPRequest request;
-
-        private IncomingRequest(RHTTPRequest request)
-        {
-            this.request = request;
-        }
-
-        public void run()
-        {
-            byte[] requestBytes = request.getRequestBytes();
-
-            ByteArrayEndPoint endPoint = new ByteArrayEndPoint(requestBytes, 1024);
-            endPoint.setGrowOutput(true);
-
-            AbstractHttpConnection connection = new BlockingHttpConnection(ReverseHTTPConnector.this, endPoint, getServer());
-            
-            connectionOpened(connection);
-            try
-            {
-                // Loop over the whole content, since handle() only
-                // reads up to the connection buffer's capacities
-                while (endPoint.getIn().length() > 0)
-                    connection.handle();
-
-                byte[] responseBytes = endPoint.getOut().asArray();
-                RHTTPResponse response = RHTTPResponse.fromResponseBytes(request.getId(), responseBytes);
-                client.deliver(response);
-            }
-            catch (Exception x)
-            {
-                LOG.debug(x);
-            }
-            finally
-            {
-                connectionClosed(connection);
-            }
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-connector/src/test/java/org/eclipse/jetty/rhttp/connector/ReverseHTTPConnectorTest.java b/jetty-rhttp/jetty-rhttp-connector/src/test/java/org/eclipse/jetty/rhttp/connector/ReverseHTTPConnectorTest.java
deleted file mode 100644
index 0e7b8b7..0000000
--- a/jetty-rhttp/jetty-rhttp-connector/src/test/java/org/eclipse/jetty/rhttp/connector/ReverseHTTPConnectorTest.java
+++ /dev/null
@@ -1,187 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.connector;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import junit.framework.TestCase;
-
-import org.eclipse.jetty.rhttp.client.ClientListener;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.client.RHTTPListener;
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-import org.eclipse.jetty.rhttp.connector.ReverseHTTPConnector;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-
-/**
- * @version $Revision$ $Date$
- */
-public class ReverseHTTPConnectorTest extends TestCase
-{
-    public void testGatewayConnectorWithoutRequestBody() throws Exception
-    {
-        testGatewayConnector(false);
-    }
-
-    public void testGatewayConnectorWithRequestBody() throws Exception
-    {
-        testGatewayConnector(true);
-    }
-
-    private void testGatewayConnector(boolean withRequestBody) throws Exception
-    {
-        Server server = new Server();
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        CountDownLatch clientLatch = new CountDownLatch(1);
-        AtomicReference<RHTTPResponse> responseRef = new AtomicReference<RHTTPResponse>();
-        ReverseHTTPConnector connector = new ReverseHTTPConnector(new TestClient(clientLatch, responseRef));
-        server.addConnector(connector);
-        final String method = "POST";
-        final String uri = "/test";
-        final byte[] requestBody = withRequestBody ? "REQUEST-BODY".getBytes("UTF-8") : new byte[0];
-        final int statusCode = HttpServletResponse.SC_CREATED;
-        final String headerName = "foo";
-        final String headerValue = "bar";
-        final byte[] responseBody = "RESPONSE-BODY".getBytes("UTF-8");
-        server.setHandler(new AbstractHandler()
-        {
-            public void handle(String pathInfo, org.eclipse.jetty.server.Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
-            {
-                assertEquals(method, httpRequest.getMethod());
-                assertEquals(uri, httpRequest.getRequestURI());
-                assertEquals(headerValue, httpRequest.getHeader(headerName));
-                ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                InputStream input = httpRequest.getInputStream();
-                int read;
-                while ((read = input.read()) >= 0)
-                    baos.write(read);
-                baos.close();
-                assertTrue(Arrays.equals(requestBody, baos.toByteArray()));
-
-                httpResponse.setStatus(statusCode);
-                httpResponse.setHeader(headerName, headerValue);
-                OutputStream output = httpResponse.getOutputStream();
-                output.write(responseBody);
-                output.flush();
-                request.setHandled(true);
-                handlerLatch.countDown();
-            }
-        });
-        server.start();
-
-        HashMap<String, String> headers = new HashMap<String, String>();
-        headers.put("Host", "localhost");
-        headers.put(headerName, headerValue);
-        headers.put("Content-Length", String.valueOf(requestBody.length));
-        RHTTPRequest request = new RHTTPRequest(1, method, uri, headers, requestBody);
-        request = RHTTPRequest.fromRequestBytes(request.getId(), request.getRequestBytes());
-        connector.onRequest(request);
-
-        assertTrue(handlerLatch.await(1000, TimeUnit.MILLISECONDS));
-        assertTrue(clientLatch.await(1000, TimeUnit.MILLISECONDS));
-        RHTTPResponse response = responseRef.get();
-        assertEquals(request.getId(), response.getId());
-        assertEquals(statusCode, response.getStatusCode());
-        assertEquals(headerValue, response.getHeaders().get(headerName));
-        assertTrue(Arrays.equals(response.getBody(), responseBody));
-    }
-
-    private class TestClient implements RHTTPClient
-    {
-        private final CountDownLatch latch;
-        private final AtomicReference<RHTTPResponse> responseRef;
-
-        private TestClient(CountDownLatch latch, AtomicReference<RHTTPResponse> response)
-        {
-            this.latch = latch;
-            this.responseRef = response;
-        }
-
-        public String getTargetId()
-        {
-            return null;
-        }
-
-        public void connect() throws IOException
-        {
-        }
-
-        public void disconnect() throws IOException
-        {
-        }
-
-        public void deliver(RHTTPResponse response) throws IOException
-        {
-            responseRef.set(response);
-            latch.countDown();
-        }
-
-        public void addListener(RHTTPListener listener)
-        {
-        }
-
-        public void removeListener(RHTTPListener listener)
-        {
-        }
-
-        public void addClientListener(ClientListener listener)
-        {
-        }
-
-        public void removeClientListener(ClientListener listener)
-        {
-        }
-
-        public String getHost()
-        {
-            return null;
-        }
-
-        public int getPort()
-        {
-            return 0;
-        }
-
-        public String getGatewayURI()
-        {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public String getPath()
-        {
-            // TODO Auto-generated method stub
-            return null;
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-connector/src/test/java/org/eclipse/jetty/rhttp/connector/TestReverseServer.java b/jetty-rhttp/jetty-rhttp-connector/src/test/java/org/eclipse/jetty/rhttp/connector/TestReverseServer.java
deleted file mode 100644
index 05c641b..0000000
--- a/jetty-rhttp/jetty-rhttp-connector/src/test/java/org/eclipse/jetty/rhttp/connector/TestReverseServer.java
+++ /dev/null
@@ -1,58 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.connector;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.embedded.HelloHandler;
-import org.eclipse.jetty.rhttp.client.JettyClient;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.connector.ReverseHTTPConnector;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.util.log.Log;
-
-/**
- * A Test content server that uses a {@link ReverseHTTPConnector}.
- * The main of this class starts 3 TestReversionServers with IDs A, B and C.
- */
-public class TestReverseServer extends Server
-{
-    TestReverseServer(String targetId)
-    {
-        setHandler(new HelloHandler("Hello "+targetId,"Hi from "+targetId));
-        
-        HttpClient httpClient = new HttpClient();
-        RHTTPClient client = new JettyClient(httpClient,"http://localhost:8080/__rhttp",targetId);
-        ReverseHTTPConnector connector = new ReverseHTTPConnector(client);
-        
-        addConnector(connector);
-    }
-    
-    public static void main(String... args) throws Exception
-    {
-        Log.getLogger("org.mortbay.jetty.rhttp.client").setDebugEnabled(true);
-        
-        TestReverseServer[] node = new TestReverseServer[] { new TestReverseServer("A"),new TestReverseServer("B"),new TestReverseServer("C") };
-        
-        for (TestReverseServer s : node)
-            s.start();
-
-        for (TestReverseServer s : node)
-            s.join();
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/pom.xml b/jetty-rhttp/jetty-rhttp-gateway/pom.xml
deleted file mode 100644
index 054bbd5..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/pom.xml
+++ /dev/null
@@ -1,99 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.rhttp</groupId>
-        <artifactId>jetty-rhttp-project</artifactId>
-        <version>9.0.0-SNAPSHOT</version>
-    </parent>
-
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>reverse-http-gateway</artifactId>
-    <packaging>jar</packaging>
-    <name>Jetty :: Reverse HTTP :: Gateway</name>
-
-    <properties>
-        <bundle-symbolic-name>${project.groupId}.rhttp.gateway</bundle-symbolic-name>
-    </properties>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>exec-maven-plugin</artifactId>
-                <configuration>
-                  <mainClass>org.mortbay.jetty.rhttp.gateway.Main</mainClass>
-                  <arguments>
-                  </arguments>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>manifest</goal>
-                        </goals>
-                        <configuration>
-                            <instructions>
-                                 <Import-Package>*</Import-Package>
-                            </instructions>
-                          </configuration>
-                       </execution>
-                  </executions>
-            </plugin>
-            <plugin>
-              <!--
-              Required for OSGI
-              -->
-              <groupId>org.apache.maven.plugins</groupId>
-              <artifactId>maven-jar-plugin</artifactId>
-              <configuration>
-                  <archive>
-                      <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-                  </archive>
-              </configuration>
-            </plugin>
-        </plugins>
-    </build>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>reverse-http-client</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>javax.servlet</groupId>
-            <artifactId>javax.servlet-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-io</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-continuation</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-server</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-servlet</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-client</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.toolchain</groupId>
-            <artifactId>jetty-test-helper</artifactId>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-</project>
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/ClientDelegate.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/ClientDelegate.java
deleted file mode 100644
index dd77c09..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/ClientDelegate.java
+++ /dev/null
@@ -1,92 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.IOException;
-import java.util.List;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-
-
-/**
- * <p>A <tt>ClientDelegate</tt> is the server-side counterpart of a gateway client.</p>
- * <p>The gateway client, the comet protocol and the <tt>ClientDelegate</tt> form the
- * <em>Half-Object plus Protocol</em> pattern that is used between the gateway server
- * and the gateway client.</p>
- * <p><tt>ClientDelegate</tt> offers a server-side API on top of the comet communication.<br />
- * The API allows to enqueue server-side events to the gateway client, allows to
- * flush them to the gateway client, and allows to close and dispose server-side
- * resources when the gateway client disconnects.</p>
- *
- * @version $Revision$ $Date$
- */
-public interface ClientDelegate
-{
-    /**
-     * @return the targetId that uniquely identifies this client delegate.
-     */
-    public String getTargetId();
-
-    /**
-     * <p>Enqueues the given request to the delivery queue so that it will be sent to the
-     * gateway client on the first flush occasion.</p>
-     * <p>Requests may fail to be queued, for example because the gateway client disconnected
-     * concurrently.</p>
-     *
-     * @param request the request to add to the delivery queue
-     * @return whether the request has been queued or not
-     * @see #process(HttpServletRequest)
-     */
-    public boolean enqueue(RHTTPRequest request);
-
-    /**
-     * <p>Flushes the requests that have been {@link #enqueue(RHTTPRequest) enqueued}.</p>
-     * <p>If no requests have been enqueued, then this method may suspend the current request for
-     * the long poll timeout. <br />
-     * The request is suspended only if all these conditions holds true:
-     * <ul>
-     * <li>it is not the first time that this method is called for this client delegate</li>
-     * <li>no requests have been enqueued</li>
-     * <li>this client delegate is not closed</li>
-     * <li>the previous call to this method did not suspend the request</li>
-     * </ul>
-     * In all other cases, a response if sent to the gateway client, possibly containing no requests.
-     *
-     * @param httpRequest the HTTP request for the long poll request from the gateway client
-     * @return the list of requests to send to the gateway client, or null if no response should be sent
-     * to the gateway client
-     * @throws IOException in case of I/O exception while flushing content to the gateway client
-     * @see #enqueue(RHTTPRequest)
-     */
-    public List<RHTTPRequest> process(HttpServletRequest httpRequest) throws IOException;
-
-    /**
-     * <p>Closes this client delegate, in response to a gateway client request to disconnect.</p>
-     * @see #isClosed()
-     */
-    public void close();
-
-    /**
-     * @return whether this delegate client is closed
-     * @see #close()
-     */
-    public boolean isClosed();
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/ConnectorServlet.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/ConnectorServlet.java
deleted file mode 100644
index b849a98..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/ConnectorServlet.java
+++ /dev/null
@@ -1,224 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * The servlet that handles the communication with the gateway clients.
- * @version $Revision$ $Date$
- */
-public class ConnectorServlet extends HttpServlet
-{
-    private final Logger logger = Log.getLogger(getClass().toString());
-    private final TargetIdRetriever targetIdRetriever = new StandardTargetIdRetriever();
-    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
-    private final ConcurrentMap<String, Future<?>> expirations = new ConcurrentHashMap<String, Future<?>>();
-    private final Gateway gateway;
-    private long clientTimeout=15000;
-
-    public ConnectorServlet(Gateway gateway)
-    {
-        this.gateway = gateway;
-    }
-
-    @Override
-    public void init() throws ServletException 
-    {
-        String t = getInitParameter("clientTimeout");
-        if (t!=null && !"".equals(t))
-            clientTimeout=Long.parseLong(t);
-    }
-
-    @Override
-    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        String targetId = targetIdRetriever.retrieveTargetId(request);
-
-        String uri = request.getRequestURI();
-        String path = uri.substring(request.getServletPath().length());
-        String[] segments = path.split("/");
-        if (segments.length < 3)
-            throw new ServletException("Invalid request to " + getClass().getSimpleName() + ": " + uri);
-
-        String action = segments[2];
-        if ("handshake".equals(action))
-            serviceHandshake(targetId, request, response);
-        else if ("connect".equals(action))
-            serviceConnect(targetId, request, response);
-        else if ("deliver".equals(action))
-            serviceDeliver(targetId, request, response);
-        else if ("disconnect".equals(action))
-            serviceDisconnect(targetId, request, response);
-        else
-            throw new ServletException("Invalid request to " + getClass().getSimpleName() + ": " + uri);
-    }
-
-    private void serviceHandshake(String targetId, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException
-    {
-        ClientDelegate client = gateway.getClientDelegate(targetId);
-        if (client != null)
-            throw new IOException("Client with targetId " + targetId + " is already connected");
-
-        client = gateway.newClientDelegate(targetId);
-        ClientDelegate existing = gateway.addClientDelegate(targetId, client);
-        if (existing != null)
-            throw new IOException("Client with targetId " + targetId + " is already connected");
-
-        flush(client, httpRequest, httpResponse);
-    }
-
-    private void flush(ClientDelegate client, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException
-    {
-        List<RHTTPRequest> requests = client.process(httpRequest);
-        if (requests != null)
-        {
-            // Schedule before sending the requests, to avoid that the remote client
-            // reconnects before we have scheduled the expiration timeout.
-            if (!client.isClosed())
-                schedule(client);
-
-            ServletOutputStream output = httpResponse.getOutputStream();
-            for (RHTTPRequest request : requests)
-                output.write(request.getFrameBytes());
-            // I could count the framed bytes of all requests and set a Content-Length header,
-            // but the implementation of ServletOutputStream takes care of everything:
-            // if the request was HTTP/1.1, then flushing result in a chunked response, but the
-            // client know how to handle it; if the request was HTTP/1.0, then no chunking.
-            // To avoid chunking in HTTP/1.1 I must set the Content-Length header.
-            output.flush();
-            logger.debug("Delivered to device {} requests {} ", client.getTargetId(), requests);
-        }
-    }
-
-    private void schedule(ClientDelegate client)
-    {
-        Future<?> task = scheduler.schedule(new ClientExpirationTask(client), clientTimeout, TimeUnit.MILLISECONDS);
-        Future<?> existing = expirations.put(client.getTargetId(), task);
-        assert existing == null;
-    }
-
-    private void unschedule(String targetId)
-    {
-        Future<?> task = expirations.remove(targetId);
-        if (task != null)
-            task.cancel(false);
-    }
-
-    private void serviceConnect(String targetId, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException
-    {
-        unschedule(targetId);
-
-        ClientDelegate client = gateway.getClientDelegate(targetId);
-        if (client == null)
-        {
-            // Expired client tries to connect without handshake
-            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
-            return;
-        }
-
-        flush(client, httpRequest, httpResponse);
-
-        if (client.isClosed())
-            gateway.removeClientDelegate(targetId);
-    }
-
-    private void expireConnect(ClientDelegate client, long time)
-    {
-        String targetId = client.getTargetId();
-        logger.info("Client with targetId {} missing, last seen {} ms ago, closing it", targetId, System.currentTimeMillis() - time);
-        client.close();
-        // If the client expired, means that it did not connect,
-        // so there no request to resume, and we cleanup here
-        // (while normally this cleanup is done in serviceConnect())
-        unschedule(targetId);
-        gateway.removeClientDelegate(targetId);
-    }
-
-    private void serviceDeliver(String targetId, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws ServletException, IOException
-    {
-        if (gateway.getClientDelegate(targetId) == null)
-        {
-            // Expired client tries to deliver without handshake
-            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
-            return;
-        }
-
-        byte[] body = Utils.read(httpRequest.getInputStream());
-
-        RHTTPResponse response = RHTTPResponse.fromFrameBytes(body);
-
-        ExternalRequest externalRequest = gateway.removeExternalRequest(response.getId());
-        if (externalRequest != null)
-        {
-            externalRequest.respond(response);
-            logger.debug("Deliver request from device {}, gateway request {}, response {}", new Object[] {targetId, externalRequest, response});
-        }
-        else
-        {
-            // We can arrive here for a race with the continuation expiration, which expired just before
-            // the gateway client responded with a valid response; log this case ignore it.
-            logger.debug("Deliver request from device {}, missing gateway request, response {}", targetId, response);
-        }
-    }
-
-    private void serviceDisconnect(String targetId, HttpServletRequest request, HttpServletResponse response)
-    {
-        // Do not remove the ClientDelegate from the gateway here,
-        // since closing the ClientDelegate will resume the connect request
-        // and we remove the ClientDelegate from the gateway there
-        ClientDelegate client = gateway.getClientDelegate(targetId);
-        if (client != null)
-            client.close();
-    }
-
-    private class ClientExpirationTask implements Runnable
-    {
-        private final long time = System.currentTimeMillis();
-        private final ClientDelegate client;
-
-        public ClientExpirationTask(ClientDelegate client)
-        {
-            this.client = client;
-        }
-
-        public void run()
-        {
-            expireConnect(client, time);
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/ExternalRequest.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/ExternalRequest.java
deleted file mode 100644
index ff597f6..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/ExternalRequest.java
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.IOException;
-
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-
-
-/**
- * <p><tt>ExternalRequest</tt> represent an external request made to the gateway server.</p>
- * <p><tt>ExternalRequest</tt>s that arrive to the gateway server are suspended, waiting
- * for a response from the corresponding gateway client.</p>
- *
- * @version $Revision$ $Date$
- */
-public interface ExternalRequest
-{
-    /**
-     * <p>Suspends this <tt>ExternalRequest</tt> waiting for a response from the gateway client.</p>
-     * @return true if the <tt>ExternalRequest</tt> has been suspended, false if the
-     * <tt>ExternalRequest</tt> has already been responded.
-     */
-    public boolean suspend();
-
-    /**
-     * <p>Responds to the original external request with the response arrived from the gateway client.</p>
-     * @param response the response arrived from the gateway client
-     * @throws IOException if responding to the original external request fails
-     */
-    public void respond(RHTTPResponse response) throws IOException;
-
-    /**
-     * @return the request to be sent to the gateway client
-     */
-    public RHTTPRequest getRequest();
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/ExternalServlet.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/ExternalServlet.java
deleted file mode 100644
index 731a24b..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/ExternalServlet.java
+++ /dev/null
@@ -1,88 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * The servlet that handles external requests.
- *
- * @version $Revision$ $Date$
- */
-public class ExternalServlet extends HttpServlet
-{
-    private final Logger logger = Log.getLogger(getClass().toString());
-    private final Gateway gateway;
-    private TargetIdRetriever targetIdRetriever;
-
-    public ExternalServlet(Gateway gateway, TargetIdRetriever targetIdRetriever)
-    {
-        this.gateway = gateway;
-        this.targetIdRetriever = targetIdRetriever;
-    }
-
-    public TargetIdRetriever getTargetIdRetriever()
-    {
-        return targetIdRetriever;
-    }
-
-    public void setTargetIdRetriever(TargetIdRetriever targetIdRetriever)
-    {
-        this.targetIdRetriever = targetIdRetriever;
-    }
-
-    @Override
-    protected void service(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws ServletException, IOException
-    {
-        logger.debug("External http request: {}", httpRequest.getRequestURL());
-
-        String targetId = targetIdRetriever.retrieveTargetId(httpRequest);
-        if (targetId == null)
-            throw new ServletException("Invalid request to " + getClass().getSimpleName() + ": " + httpRequest.getRequestURI());
-
-        ClientDelegate client = gateway.getClientDelegate(targetId);
-        if (client == null) throw new ServletException("Client with targetId " + targetId + " is not connected");
-
-        ExternalRequest externalRequest = gateway.newExternalRequest(httpRequest, httpResponse);
-        RHTTPRequest request = externalRequest.getRequest();
-        ExternalRequest existing = gateway.addExternalRequest(request.getId(), externalRequest);
-        assert existing == null;
-        logger.debug("External request {} for device {}", request, targetId);
-
-        boolean delivered = client.enqueue(request);
-        if (delivered)
-        {
-            externalRequest.suspend();
-        }
-        else
-        {
-            // TODO: improve this: we can temporarly queue this request elsewhere and wait for the client to reconnect ?
-            throw new ServletException("Could not enqueue request to client with targetId " + targetId);
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/Gateway.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/Gateway.java
deleted file mode 100644
index 4523cce..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/Gateway.java
+++ /dev/null
@@ -1,99 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.IOException;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * <p>Gateway instances are responsible of holding the state of the gateway server.</p>
- * <p>The state is composed by:
- * <ul>
- * <li>{@link ExternalRequest external requests} that are suspended waiting for the response</li>
- * <li>{@link ClientDelegate gateway clients} that are connected with the gateway server</li>
- * </ul></p>
- * <p>Instances of this class are created by the {@link GatewayServer}.</p>
- *
- * @version $Revision$ $Date$
- */
-public interface Gateway
-{
-    /**
-     * <p>Returns the {@link ClientDelegate} with the given targetId.<br />
-     * If there is no such ClientDelegate returns null.</p>
-     *
-     * @param targetId the targetId of the ClientDelegate to return
-     * @return the ClientDelegate associated with the given targetId
-     */
-    public ClientDelegate getClientDelegate(String targetId);
-
-    /**
-     * <p>Creates and configures a new {@link ClientDelegate} with the given targetId.</p>
-     * @param targetId the targetId of the ClientDelegate to create
-     * @return a newly created ClientDelegate
-     * @see #addClientDelegate(String, ClientDelegate)
-     */
-    public ClientDelegate newClientDelegate(String targetId);
-
-    /**
-     * <p>Maps the given ClientDelegate to the given targetId.</p>
-     * @param targetId the targetId of the given ClientDelegate
-     * @param client the ClientDelegate to map
-     * @return the previously existing ClientDelegate mapped to the same targetId
-     * @see #removeClientDelegate(String)
-     */
-    public ClientDelegate addClientDelegate(String targetId, ClientDelegate client);
-
-    /**
-     * <p>Removes the {@link ClientDelegate} associated with the given targetId.</p>
-     * @param targetId the targetId of the ClientDelegate to remove
-     * @return the removed ClientDelegate, or null if no ClientDelegate was removed
-     * @see #addClientDelegate(String, ClientDelegate)
-     */
-    public ClientDelegate removeClientDelegate(String targetId);
-
-    /**
-     * <p>Creates a new {@link ExternalRequest} from the given HTTP request and HTTP response.</p>
-     * @param httpRequest the HTTP request of the external request
-     * @param httpResponse the HTTP response of the external request
-     * @return a newly created ExternalRequest
-     * @throws IOException in case of failures creating the ExternalRequest
-     * @see #addExternalRequest(int, ExternalRequest)
-     */
-    public ExternalRequest newExternalRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException;
-
-    /**
-     * Maps the given ExternalRequest with the given requestId into the gateway state.
-     * @param requestId the id of the ExternalRequest
-     * @param externalRequest the ExternalRequest to map
-     * @return the previously existing ExternalRequest mapped to the same requestId
-     * @see #removeExternalRequest(int)
-     */
-    public ExternalRequest addExternalRequest(int requestId, ExternalRequest externalRequest);
-
-    /**
-     * Removes the ExternalRequest mapped to the given requestId from the gateway state.
-     * @param requestId the id of the ExternalRequest
-     * @return the removed ExternalRequest
-     * @see #addExternalRequest(int, ExternalRequest)
-     */
-    public ExternalRequest removeExternalRequest(int requestId);
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/GatewayProxyServer.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/GatewayProxyServer.java
deleted file mode 100644
index 0f657ea..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/GatewayProxyServer.java
+++ /dev/null
@@ -1,228 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.client.Address;
-import org.eclipse.jetty.client.ContentExchange;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpExchange;
-import org.eclipse.jetty.io.Buffer;
-import org.eclipse.jetty.io.ByteArrayBuffer;
-import org.eclipse.jetty.rhttp.client.JettyClient;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.client.RHTTPListener;
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * <p>This class combines a gateway server and a gateway client to obtain the functionality of a simple proxy server.</p>
- * <p>This gateway proxy server starts on port 8080 and can be set as http proxy in browsers such as Firefox, and used
- * to browse the internet.</p>
- * <p>Its functionality is limited (for example, it only supports http, and not https).</p>
- * @version $Revision$ $Date$
- */
-public class GatewayProxyServer
-{
-    private static final Logger logger = Log.getLogger(GatewayProxyServer.class.toString());
-
-    public static void main(String[] args) throws Exception
-    {
-        GatewayServer server = new GatewayServer();
-
-        Connector plainConnector = new SelectChannelConnector();
-        plainConnector.setPort(8080);
-        server.addConnector(plainConnector);
-
-        ((StandardGateway)server.getGateway()).setExternalTimeout(180000);
-        ((StandardGateway)server.getGateway()).setGatewayTimeout(20000);
-        server.setTargetIdRetriever(new ProxyTargetIdRetriever());
-        server.start();
-
-        HttpClient httpClient = new HttpClient();
-        httpClient.setConnectorType(HttpClient.CONNECTOR_SOCKET);
-        httpClient.start();
-
-        RHTTPClient client = new JettyClient(httpClient, new Address("localhost", plainConnector.getPort()), server.getContext().getContextPath() + "/gw", "proxy");
-        client.addListener(new ProxyListener(httpClient, client));
-        client.connect();
-
-        Runtime.getRuntime().addShutdownHook(new Shutdown(server, httpClient, client));
-        logger.info("{} started", GatewayProxyServer.class.getSimpleName());
-    }
-
-    private static class Shutdown extends Thread
-    {
-        private final GatewayServer server;
-        private final HttpClient httpClient;
-        private final RHTTPClient client;
-
-        public Shutdown(GatewayServer server, HttpClient httpClient, RHTTPClient client)
-        {
-            this.server = server;
-            this.httpClient = httpClient;
-            this.client = client;
-        }
-
-        @Override
-        public void run()
-        {
-            try
-            {
-                client.disconnect();
-                httpClient.stop();
-                server.stop();
-                logger.info("{} stopped", GatewayProxyServer.class.getSimpleName());
-            }
-            catch (Exception x)
-            {
-                logger.debug("Exception while stopping " + GatewayProxyServer.class.getSimpleName(), x);
-            }
-        }
-    }
-
-    private static class ProxyListener implements RHTTPListener
-    {
-        private final HttpClient httpClient;
-        private final RHTTPClient client;
-
-        private ProxyListener(HttpClient httpClient, RHTTPClient client)
-        {
-            this.httpClient = httpClient;
-            this.client = client;
-        }
-
-        public void onRequest(RHTTPRequest request) throws Exception
-        {
-            ProxyExchange exchange = new ProxyExchange();
-            Address address = Address.from(request.getHeaders().get("Host"));
-            if (address.getPort() == 0) address = new Address(address.getHost(), 80);
-            exchange.setAddress(address);
-            exchange.setMethod(request.getMethod());
-            exchange.setURI(request.getURI());
-            for (Map.Entry<String, String> header : request.getHeaders().entrySet())
-                exchange.setRequestHeader(header.getKey(), header.getValue());
-            exchange.setRequestContent(new ByteArrayBuffer(request.getBody()));
-            int status = syncSend(exchange);
-            if (status == HttpExchange.STATUS_COMPLETED)
-            {
-                int statusCode = exchange.getResponseStatus();
-                String statusMessage = exchange.getResponseMessage();
-                Map<String, String> responseHeaders = exchange.getResponseHeaders();
-                byte[] responseBody = exchange.getResponseBody();
-                RHTTPResponse response = new RHTTPResponse(request.getId(), statusCode, statusMessage, responseHeaders, responseBody);
-                client.deliver(response);
-            }
-            else
-            {
-                int statusCode = HttpServletResponse.SC_SERVICE_UNAVAILABLE;
-                String statusMessage = "Gateway error";
-                HashMap<String, String> responseHeaders = new HashMap<String, String>();
-                responseHeaders.put("Connection", "close");
-                byte[] responseBody = new byte[0];
-                RHTTPResponse response = new RHTTPResponse(request.getId(), statusCode, statusMessage, responseHeaders, responseBody);
-                client.deliver(response);
-            }
-        }
-
-        private int syncSend(ProxyExchange exchange) throws Exception
-        {
-            long start = System.nanoTime();
-            httpClient.send(exchange);
-            int status = exchange.waitForDone();
-            long end = System.nanoTime();
-            long millis = TimeUnit.NANOSECONDS.toMillis(end - start);
-            long micros = TimeUnit.NANOSECONDS.toMicros(end - start - TimeUnit.MILLISECONDS.toNanos(millis));
-            logger.debug("Proxied request took {}.{} ms", millis, micros);
-            return status;
-        }
-    }
-
-    private static class ProxyExchange extends ContentExchange
-    {
-        private String responseMessage;
-        private Map<String, String> responseHeaders = new HashMap<String, String>();
-        private ByteArrayOutputStream responseBody = new ByteArrayOutputStream();
-
-        private ProxyExchange()
-        {
-            super(true);
-        }
-
-        public String getResponseMessage()
-        {
-            return responseMessage;
-        }
-
-        public Map<String, String> getResponseHeaders()
-        {
-            return responseHeaders;
-        }
-
-        public byte[] getResponseBody()
-        {
-            return responseBody.toByteArray();
-        }
-
-        @Override
-        protected void onResponseStatus(Buffer version, int code, Buffer message) throws IOException
-        {
-            super.onResponseStatus(version, code, message);
-            this.responseMessage = message.toString("UTF-8");
-        }
-
-        @Override
-        protected void onResponseHeader(Buffer nameBuffer, Buffer valueBuffer) throws IOException
-        {
-            super.onResponseHeader(nameBuffer, valueBuffer);
-            String name = nameBuffer.toString("UTF-8");
-            String value = valueBuffer.toString("UTF-8");
-            // Skip chunked header, since we read the whole body and will not re-chunk it
-            if (!name.equalsIgnoreCase("Transfer-Encoding") || !value.equalsIgnoreCase("chunked"))
-                responseHeaders.put(name, value);
-        }
-
-        @Override
-        protected void onResponseContent(Buffer buffer) throws IOException
-        {
-            responseBody.write(buffer.asArray());
-            super.onResponseContent(buffer);
-        }
-    }
-
-    public static class ProxyTargetIdRetriever implements TargetIdRetriever
-    {
-        public String retrieveTargetId(HttpServletRequest httpRequest)
-        {
-            return "proxy";
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/GatewayServer.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/GatewayServer.java
deleted file mode 100644
index c405b4a..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/GatewayServer.java
+++ /dev/null
@@ -1,171 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.jetty.http.MimeTypes;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.HandlerCollection;
-import org.eclipse.jetty.servlet.DefaultServlet;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * <p>The gateway server is a server component that acts as intermediary between
- * <em>external clients</em> which perform requests for resources, and the
- * <em>resource providers</em>.</p>
- * <p>The particularity of the gateway server is that the resource providers
- * connect to the gateway using a comet protocol. <br />
- * The comet procotol functionality is implemented by a gateway client. <br />
- * This is quite different from a normal proxy server where it is the proxy that
- * connects to the resource providers.</p>
- * <p>Schematically, this is how the gateway server works:</p>
- * <pre>
- * External Client       Gateway Server         Gateway Client         Resource Provider
- *                              |                      |
- *                              | &lt;-- comet req. 1 --- |
- *        | --- ext. req. 1 --&gt; |                      |
- *        |                     | --- comet res. 1 --&gt; |
- *        |                     | &lt;-- comet req. 2 --- |
- *        |                                            | --- ext. req. 1 --&gt; |
- *                                                                           |
- *        |                                            | &lt;-- ext. res. 1 --- |
- *        |                     | &lt;-- ext.  res. 1 --- |
- *        | &lt;-- ext. res. 1 --- |
- *
- *        | --- ext. req. 2 --&gt; |
- *        |                     | --- comet res. 2 --&gt; |
- *        .                     .                      .
- * </pre>
- * <p>The gateway server is made of two servlets:
- * <ul>
- * <li>the external servlet, that handles external requests</li>
- * <li>the gateway servlet, that handles the communication with the gateway client</li>
- * </ul>
- * </p>
- * <p>External requests are suspended using Jetty continuations until a response for
- * that request arrives from the resource provider, or a
- * {@link #getExternalTimeout() configurable timeout} expires. <br />
- * Comet requests made by the gateway client also expires after a (different)
- * {@link #getGatewayTimeout() configurable timeout}.</p>
- * <p>External requests are packed into {@link RHTTPRequest} objects, converted into an
- * opaque byte array and sent as the body of the comet reponse to the gateway
- * {@link RHTTPClient}.</p>
- * <p>The gateway client uses a notification mechanism to alert listeners interested
- * in external requests that have been forwarded through the gateway. It is up to the
- * listeners to connect to the resource provider however they like.</p>
- * <p>When the gateway client receives a response from the resource provider, it packs
- * the response into a {@link RHTTPResponse} object, converts it into an opaque byte array
- * and sends it as the body of a normal HTTP request to the gateway server.</p>
- * <p>It is possible to connect more than one gateway client to a gateway server; each
- * gateway client is identified by a unique <em>targetId</em>. <br />
- * External requests must specify a targetId that allows the gateway server to forward
- * the requests to the specific gateway client; how the targetId is retrieved from an
- * external request is handled by {@link TargetIdRetriever} implementations.</p>
- *
- * @version $Revision$ $Date$
- */
-public class GatewayServer extends Server
-{
-    public final static String DFT_EXT_PATH="/gw";
-    public final static String DFT_CONNECT_PATH="/__rhttp";
-    private final Logger logger = Log.getLogger(getClass().toString());
-    private final Gateway gateway;
-    private final ServletHolder externalServletHolder;
-    private final ServletHolder connectorServletHolder;
-    private final ServletContextHandler context;
-    
-    public GatewayServer()
-    {
-        this("",DFT_EXT_PATH,DFT_CONNECT_PATH,new StandardTargetIdRetriever());
-    }
-
-    public GatewayServer(String contextPath, String externalServletPath,String gatewayServletPath, TargetIdRetriever targetIdRetriever)
-    {
-        HandlerCollection handlers = new HandlerCollection();
-        setHandler(handlers);
-        context = new ServletContextHandler(handlers, contextPath, ServletContextHandler.SESSIONS);
-        
-        // Setup the gateway
-        gateway = createGateway();
-        
-        // Setup external servlet
-        ExternalServlet externalServlet = new ExternalServlet(gateway, targetIdRetriever);
-        externalServletHolder = new ServletHolder(externalServlet);
-        context.addServlet(externalServletHolder, externalServletPath + "/*");
-        logger.debug("External servlet mapped to {}/*", externalServletPath);
-
-        // Setup gateway servlet
-        ConnectorServlet gatewayServlet = new ConnectorServlet(gateway);
-        connectorServletHolder = new ServletHolder(gatewayServlet);
-        connectorServletHolder.setInitParameter("clientTimeout", "15000");
-        context.addServlet(connectorServletHolder, gatewayServletPath + "/*");
-        logger.debug("Gateway servlet mapped to {}/*", gatewayServletPath);
-    }
-
-    /**
-     * Creates and configures a {@link Gateway} object.
-     * @return the newly created and configured Gateway object.
-     */
-    protected Gateway createGateway()
-    {
-        StandardGateway gateway = new StandardGateway();
-        return gateway;
-    }
-    
-    public ServletContextHandler getContext()
-    {
-        return context;
-    }
-    
-    public Gateway getGateway()
-    {
-        return gateway;
-    }
-    
-    public ServletHolder getExternalServlet()
-    {
-        return externalServletHolder;
-    }
-    
-    public ServletHolder getConnectorServlet()
-    {
-        return connectorServletHolder;
-    }
-
-    public void setTargetIdRetriever(TargetIdRetriever retriever)
-    {
-        ((ExternalServlet)externalServletHolder.getServletInstance()).setTargetIdRetriever(retriever);
-    }
-
-    public TargetIdRetriever getTargetIdRetriever()
-    {
-        return ((ExternalServlet)externalServletHolder.getServletInstance()).getTargetIdRetriever();
-    }
-    
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/HostTargetIdRetriever.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/HostTargetIdRetriever.java
deleted file mode 100644
index c7348c6..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/HostTargetIdRetriever.java
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * @version $Revision$ $Date$
- */
-public class HostTargetIdRetriever implements TargetIdRetriever
-{
-    private final String suffix;
-
-    public HostTargetIdRetriever(String suffix)
-    {
-        this.suffix = suffix;
-    }
-
-    public String retrieveTargetId(HttpServletRequest httpRequest)
-    {
-        String host = httpRequest.getHeader("Host");
-        if (host != null)
-        {
-            // Strip the port
-            int colon = host.indexOf(':');
-            if (colon > 0)
-            {
-                host = host.substring(0, colon);
-            }
-
-            if (suffix != null && host.endsWith(suffix))
-            {
-                return host.substring(0, host.length() - suffix.length());
-            }
-        }
-        return host;
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/Main.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/Main.java
deleted file mode 100644
index 3963a4c..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/Main.java
+++ /dev/null
@@ -1,128 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
-import org.eclipse.jetty.servlet.DefaultServlet;
-import org.eclipse.jetty.servlet.ServletHolder;
-
-/**
- * <p>Main class that starts the gateway server.</p>
- * <p>This class supports the following arguments:</p>
- * <ul>
- * <li>--port=&lt;port&gt; specifies the port on which the gateway server listens to, by default 8080</li>
- * <li>--retriever=&lt;retriever&gt; specifies the
- * {@link GatewayServer#setTargetIdRetriever(TargetIdRetriever) target id retriever}</li>
- * <li>--resources=&lt;resources file path&gt; specifies the resource file path for the gateway</li>
- * </ul>
- * <p>Examples</p>
- * <p> <tt>java --port=8080</tt> </p>
- * <p> <tt>java --port=8080 --resources=/tmp/gateway-resources</tt> </p>
- * <p> <tt>java --port=8080 --retriever=standard</tt> </p>
- * <p> <tt>java --port=8080 --retriever=host,.rhttp.example.com</tt> </p>
- * <p>The latter example specifies the {@link HostTargetIdRetriever} with a suffix of <tt>.rhttp.example.com</tt></p>
- *
- * @see GatewayServer
- * @version $Revision$ $Date$
- */
-public class Main
-{
-    private static final String PORT_ARG = "port";
-    private static final String RESOURCES_ARG = "resources";
-    private static final String RETRIEVER_ARG = "retriever";
-
-    public static void main(String[] args) throws Exception
-    {
-        Map<String, Object> arguments = parse(args);
-
-        int port = 8080;
-        if (arguments.containsKey(PORT_ARG))
-            port = (Integer)arguments.get(PORT_ARG);
-
-        String resources = null;
-        if (arguments.containsKey(RESOURCES_ARG))
-            resources = (String)arguments.get(RESOURCES_ARG);
-
-        TargetIdRetriever retriever = null;
-        if (arguments.containsKey(RETRIEVER_ARG))
-            retriever = (TargetIdRetriever)arguments.get(RETRIEVER_ARG);
-
-        GatewayServer server = new GatewayServer();
-
-        Connector connector = new SelectChannelConnector();
-        connector.setPort(port);
-        server.addConnector(connector);
-
-        if (resources != null)
-        {
-            server.getContext().setResourceBase(resources);
-            ServletHolder resourcesServletHolder = server.getContext().addServlet(DefaultServlet.class,"__r/*");
-            resourcesServletHolder.setInitParameter("dirAllowed", "true");
-        }
-
-        if (retriever != null)
-            server.setTargetIdRetriever(retriever);
-
-        server.start();
-    }
-
-    private static Map<String, Object> parse(String[] args)
-    {
-        Map<String, Object> result = new HashMap<String, Object>();
-
-        Pattern pattern = Pattern.compile("--([^=]+)=(.+)");
-        for (String arg : args)
-        {
-            Matcher matcher = pattern.matcher(arg);
-            if (matcher.matches())
-            {
-                String argName = matcher.group(1);
-                if (PORT_ARG.equals(argName))
-                {
-                    result.put(PORT_ARG, Integer.parseInt(matcher.group(2)));
-                }
-                else if (RESOURCES_ARG.equals(argName))
-                {
-                    String argValue = matcher.group(2);
-                    result.put(RESOURCES_ARG, argValue);
-                }
-                else if (RETRIEVER_ARG.equals(argName))
-                {
-                    String argValue = matcher.group(2);
-                    if (argValue.startsWith("host,"))
-                    {
-                        String[] typeAndSuffix = StringUtil.split(argValue);
-                        if (typeAndSuffix.length != 2)
-                            throw new IllegalArgumentException("Invalid option " + arg + ", must be of the form --" + RETRIEVER_ARG + "=host,suffix");
-
-                        result.put(RETRIEVER_ARG, new HostTargetIdRetriever(typeAndSuffix[1]));
-                    }
-                }
-            }
-        }
-
-        return result;
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/StandardClientDelegate.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/StandardClientDelegate.java
deleted file mode 100644
index c520710..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/StandardClientDelegate.java
+++ /dev/null
@@ -1,172 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.eclipse.jetty.continuation.Continuation;
-import org.eclipse.jetty.continuation.ContinuationSupport;
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * <p>Default implementation of {@link ClientDelegate}.</p>
- *
- * @version $Revision$ $Date$
- */
-public class StandardClientDelegate implements ClientDelegate
-{
-    private final Logger logger = Log.getLogger(getClass().toString());
-    private final Object lock = new Object();
-    private final List<RHTTPRequest> requests = new ArrayList<RHTTPRequest>();
-    private final String targetId;
-    private volatile boolean firstFlush = true;
-    private volatile long timeout;
-    private volatile boolean closed;
-    private Continuation continuation;
-
-    public StandardClientDelegate(String targetId)
-    {
-        this.targetId = targetId;
-    }
-
-    public String getTargetId()
-    {
-        return targetId;
-    }
-
-    public long getTimeout()
-    {
-        return timeout;
-    }
-
-    public void setTimeout(long timeout)
-    {
-        this.timeout = timeout;
-    }
-
-    public boolean enqueue(RHTTPRequest request)
-    {
-        if (isClosed())
-            return false;
-
-        synchronized (lock)
-        {
-            requests.add(request);
-            resume();
-        }
-
-        return true;
-    }
-
-    private void resume()
-    {
-        synchronized (lock)
-        {
-            // Continuation may be null in several cases:
-            // 1. there always is something to deliver so we never suspend
-            // 2. concurrent calls to add() and close()
-            // 3. concurrent close() with a long poll that expired
-            // 4. concurrent close() with a long poll that resumed
-            if (continuation != null)
-            {
-                continuation.resume();
-                // Null the continuation, as there is no point is resuming multiple times
-                continuation = null;
-            }
-        }
-    }
-
-    public List<RHTTPRequest> process(HttpServletRequest httpRequest) throws IOException
-    {
-        // We want to respond in the following cases:
-        // 1. It's the first time we process: the client will wait for a response before issuing another connect.
-        // 2. The client disconnected, so we want to return from this connect before it times out.
-        // 3. We've been woken up because there are responses to send.
-        // 4. The continuation was suspended but timed out.
-        //    The timeout case is different from a non-first connect, in that we want to return
-        //    a (most of the times empty) response and we do not want to wait again.
-        // The order of these if statements is important, as the continuation timed out only if
-        // the client is not closed and there are no responses to send
-        List<RHTTPRequest> result = Collections.emptyList();
-        if (firstFlush)
-        {
-            firstFlush = false;
-            logger.debug("Connect request (first) from device {}, delivering requests {}", targetId, result);
-        }
-        else
-        {
-            // Synchronization is crucial here, since we don't want to suspend if there is something to deliver
-            synchronized (lock)
-            {
-                int size = requests.size();
-                if (size > 0)
-                {
-                    assert continuation == null;
-                    result = new ArrayList<RHTTPRequest>(size);
-                    result.addAll(requests);
-                    requests.clear();
-                    logger.debug("Connect request (resumed) from device {}, delivering requests {}", targetId, result);
-                }
-                else
-                {
-                    if (continuation != null)
-                    {
-                        continuation = null;
-                        logger.debug("Connect request (expired) from device {}, delivering requests {}", targetId, result);
-                    }
-                    else
-                    {
-                        if (isClosed())
-                        {
-                            logger.debug("Connect request (closed) from device {}, delivering requests {}", targetId, result);
-                        }
-                        else
-                        {
-                            // Here we need to suspend
-                            continuation = ContinuationSupport.getContinuation(httpRequest);
-                            continuation.setTimeout(getTimeout());
-                            continuation.suspend();
-                            result = null;
-                            logger.debug("Connect request (suspended) from device {}", targetId);
-                        }
-                    }
-                }
-            }
-        }
-        return result;
-    }
-
-    public void close()
-    {
-        closed = true;
-        resume();
-    }
-
-    public boolean isClosed()
-    {
-        return closed;
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/StandardExternalRequest.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/StandardExternalRequest.java
deleted file mode 100644
index 3da7d12..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/StandardExternalRequest.java
+++ /dev/null
@@ -1,189 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.IOException;
-import java.util.Map;
-
-import javax.servlet.ServletOutputStream;
-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.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * Default implementation of {@link ExternalRequest}.
- *
- * @version $Revision$ $Date$
- */
-public class StandardExternalRequest implements ExternalRequest
-{
-    private final Logger logger = Log.getLogger(getClass().toString());
-    private final RHTTPRequest request;
-    private final HttpServletRequest httpRequest;
-    private final HttpServletResponse httpResponse;
-    private final Gateway gateway;
-    private final Object lock = new Object();
-    private volatile long timeout;
-    private Continuation continuation;
-    private boolean responded;
-
-    public StandardExternalRequest(RHTTPRequest request, HttpServletRequest httpRequest, HttpServletResponse httpResponse, Gateway gateway)
-    {
-        this.request = request;
-        this.httpRequest = httpRequest;
-        this.httpResponse = httpResponse;
-        this.gateway = gateway;
-    }
-
-    public long getTimeout()
-    {
-        return timeout;
-    }
-
-    public void setTimeout(long timeout)
-    {
-        this.timeout = timeout;
-    }
-
-    public boolean suspend()
-    {
-        synchronized (lock)
-        {
-            // We suspend only if we have no responded yet
-            if (!responded)
-            {
-                assert continuation == null;
-                continuation = ContinuationSupport.getContinuation(httpRequest);
-                continuation.setTimeout(getTimeout());
-                continuation.addContinuationListener(new TimeoutListener());
-                continuation.suspend(httpResponse);
-                logger.debug("Request {} suspended", getRequest());
-            }
-            else
-            {
-                logger.debug("Request {} already responded", getRequest());
-            }
-            return !responded;
-        }
-    }
-
-    public void respond(RHTTPResponse response) throws IOException
-    {
-        responseCompleted(response);
-    }
-
-    private void responseCompleted(RHTTPResponse response) throws IOException
-    {
-        synchronized (lock)
-        {
-            // Could be that we complete exactly when the response is being expired
-            if (!responded)
-            {
-                httpResponse.setStatus(response.getStatusCode());
-
-                for (Map.Entry<String, String> header : response.getHeaders().entrySet())
-                    httpResponse.setHeader(header.getKey(), header.getValue());
-
-                ServletOutputStream output = httpResponse.getOutputStream();
-                output.write(response.getBody());
-                output.flush();
-
-                // It may happen that the continuation is null,
-                // because the response arrived before we had the chance to suspend
-                if (continuation != null)
-                {
-                    continuation.complete();
-                    continuation = null;
-                }
-
-                // Mark as responded, so we know we don't have to suspend
-                // or respond with an expired response
-                responded = true;
-
-                if (logger.isDebugEnabled())
-                {
-                    String eol = System.getProperty("line.separator");
-                    logger.debug("Request {} responded {}{}{}{}{}", new Object[]{request, response, eol, request.toLongString(), eol, response.toLongString()});
-                }
-            }
-        }
-    }
-
-    private void responseExpired() throws IOException
-    {
-        synchronized (lock)
-        {
-            // Could be that we expired exactly when the response is being completed
-            if (!responded)
-            {
-                httpResponse.sendError(HttpServletResponse.SC_GATEWAY_TIMEOUT, "Gateway Time-out");
-
-                continuation.complete();
-                continuation = null;
-
-                // Mark as responded, so we know we don't have to respond with a completed response
-                responded = true;
-
-                logger.debug("Request {} expired", getRequest());
-            }
-        }
-    }
-
-    public RHTTPRequest getRequest()
-    {
-        return request;
-    }
-
-    @Override
-    public String toString()
-    {
-        return request.toString();
-    }
-
-    private class TimeoutListener implements ContinuationListener
-    {
-        public void onComplete(Continuation continuation)
-        {
-        }
-
-        public void onTimeout(Continuation continuation)
-        {
-            ExternalRequest externalRequest = gateway.removeExternalRequest(getRequest().getId());
-            // The gateway request can be null for a race with delivery
-            if (externalRequest != null)
-            {
-                try
-                {
-                    responseExpired();
-                }
-                catch (Exception x)
-                {
-                    logger.warn("Request " + getRequest() + " expired but failed", x);
-                }
-            }
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/StandardGateway.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/StandardGateway.java
deleted file mode 100644
index 6414de6..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/StandardGateway.java
+++ /dev/null
@@ -1,131 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * Default implementation of {@link Gateway}.
- *
- * @version $Revision$ $Date$
- */
-public class StandardGateway implements Gateway
-{
-    private final Logger logger = Log.getLogger(getClass().toString());
-    private final ConcurrentMap<String, ClientDelegate> clients = new ConcurrentHashMap<String, ClientDelegate>();
-    private final ConcurrentMap<Integer, ExternalRequest> requests = new ConcurrentHashMap<Integer, ExternalRequest>();
-    private final AtomicInteger requestIds = new AtomicInteger();
-    private volatile long gatewayTimeout=20000;
-    private volatile long externalTimeout=60000;
-
-    public long getGatewayTimeout()
-    {
-        return gatewayTimeout;
-    }
-
-    public void setGatewayTimeout(long timeout)
-    {
-        this.gatewayTimeout = timeout;
-    }
-
-    public long getExternalTimeout()
-    {
-        return externalTimeout;
-    }
-
-    public void setExternalTimeout(long externalTimeout)
-    {
-        this.externalTimeout = externalTimeout;
-    }
-
-    public ClientDelegate getClientDelegate(String targetId)
-    {
-        return clients.get(targetId);
-    }
-
-    public ClientDelegate newClientDelegate(String targetId)
-    {
-        StandardClientDelegate client = new StandardClientDelegate(targetId);
-        client.setTimeout(getGatewayTimeout());
-        return client;
-    }
-
-    public ClientDelegate addClientDelegate(String targetId, ClientDelegate client)
-    {
-        return clients.putIfAbsent(targetId, client);
-    }
-
-    public ClientDelegate removeClientDelegate(String targetId)
-    {
-        return clients.remove(targetId);
-    }
-
-    public ExternalRequest newExternalRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException
-    {
-        int requestId = requestIds.incrementAndGet();
-        RHTTPRequest request = convertHttpRequest(requestId, httpRequest);
-        StandardExternalRequest gatewayRequest = new StandardExternalRequest(request, httpRequest, httpResponse, this);
-        gatewayRequest.setTimeout(getExternalTimeout());
-        return gatewayRequest;
-    }
-
-    protected RHTTPRequest convertHttpRequest(int requestId, HttpServletRequest httpRequest) throws IOException
-    {
-        Map<String, String> headers = new HashMap<String, String>();
-        for (Enumeration headerNames = httpRequest.getHeaderNames(); headerNames.hasMoreElements();)
-        {
-            String name = (String)headerNames.nextElement();
-            // TODO: improve by supporting getHeaders(name)
-            String value = httpRequest.getHeader(name);
-            headers.put(name, value);
-        }
-
-        byte[] body = Utils.read(httpRequest.getInputStream());
-        return new RHTTPRequest(requestId, httpRequest.getMethod(), httpRequest.getRequestURI(), headers, body);
-    }
-
-    public ExternalRequest addExternalRequest(int requestId, ExternalRequest externalRequest)
-    {
-        ExternalRequest existing = requests.putIfAbsent(requestId, externalRequest);
-        if (existing == null)
-            logger.debug("Added external request {}/{} - {}", new Object[]{requestId, requests.size(), externalRequest});
-        return existing;
-    }
-
-    public ExternalRequest removeExternalRequest(int requestId)
-    {
-        ExternalRequest externalRequest = requests.remove(requestId);
-        if (externalRequest != null)
-            logger.debug("Removed external request {}/{} - {}", new Object[]{requestId, requests.size(), externalRequest});
-        return externalRequest;
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/StandardTargetIdRetriever.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/StandardTargetIdRetriever.java
deleted file mode 100644
index 9cbcf0b..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/StandardTargetIdRetriever.java
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * <p>This implementation retrieves the targetId from the request URI following this pattern:</p>
- * <pre>
- * /contextPath/servletPath/&lt;targetId&gt;/other/paths
- * </pre>
- * @version $Revision$ $Date$
- */
-public class StandardTargetIdRetriever implements TargetIdRetriever
-{
-    public String retrieveTargetId(HttpServletRequest httpRequest)
-    {
-        String uri = httpRequest.getRequestURI();
-        String path = uri.substring(httpRequest.getServletPath().length());
-        String[] segments = path.split("/");
-        if (segments.length < 2) return null;
-        return segments[1];
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/TargetIdRetriever.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/TargetIdRetriever.java
deleted file mode 100644
index bfdc764..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/TargetIdRetriever.java
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * <p>Implementations should retrieve a <em>targetId</em> from an external request.</p>
- * <p>Implementations of this class may return a fixed value, or inspect the request
- * looking for URL patterns (e.g. "/&lt;targetId&gt;/resource.jsp"), or looking for request
- * parameters (e.g. "/resource.jsp?targetId=&lt;targetId&gt;), or looking for virtual host
- * naming patterns (e.g. "http://&lt;targetId&gt;.host.com/resource.jsp"), etc.</p>
- *
- * @version $Revision$ $Date$
- */
-public interface TargetIdRetriever
-{
-    /**
-     * Extracts and returns the targetId.
-     * @param httpRequest the external request from where the targetId could be extracted
-     * @return the extracted targetId
-     */
-    public String retrieveTargetId(HttpServletRequest httpRequest);
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/Utils.java b/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/Utils.java
deleted file mode 100644
index b97557d..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/main/java/org/eclipse/jetty/rhttp/gateway/Utils.java
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * @version $Revision$ $Date$
- */
-class Utils
-{
-    static byte[] read(InputStream input) throws IOException
-    {
-        ByteArrayOutputStream body = new ByteArrayOutputStream();
-        byte[] buffer = new byte[1024];
-        int read;
-        while ((read = input.read(buffer)) >= 0)
-            body.write(buffer, 0, read);
-        body.close();
-        return body.toByteArray();
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/ClientTimeoutTest.java b/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/ClientTimeoutTest.java
deleted file mode 100644
index c9c2d44..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/ClientTimeoutTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import junit.framework.TestCase;
-
-import org.eclipse.jetty.client.Address;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.rhttp.client.ClientListener;
-import org.eclipse.jetty.rhttp.client.JettyClient;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.gateway.GatewayServer;
-import org.eclipse.jetty.rhttp.gateway.StandardGateway;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
-
-
-/**
- * @version $Revision$ $Date$
- */
-public class ClientTimeoutTest extends TestCase
-{
-    public void testClientTimeout() throws Exception
-    {
-        GatewayServer server = new GatewayServer();
-        Connector connector = new SelectChannelConnector();
-        server.addConnector(connector);
-        final long clientTimeout = 2000L;
-        server.getConnectorServlet().setInitParameter("clientTimeout",""+clientTimeout);
-        final long gatewayTimeout = 4000L;
-        ((StandardGateway)server.getGateway()).setGatewayTimeout(gatewayTimeout);
-        server.start();
-        try
-        {
-            Address address = new Address("localhost", connector.getLocalPort());
-
-            HttpClient httpClient = new HttpClient();
-            httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
-            httpClient.start();
-            try
-            {
-                String targetId = "1";
-                final RHTTPClient client = new JettyClient(httpClient, address, server.getContext().getContextPath()+GatewayServer.DFT_CONNECT_PATH, targetId)
-                {
-                    private final AtomicInteger connects = new AtomicInteger();
-
-                    @Override
-                    protected void asyncConnect()
-                    {
-                        if (connects.incrementAndGet() == 2)
-                        {
-                            try
-                            {
-                                // Wait here instead of connecting, so that the client expires on the server
-                                Thread.sleep(clientTimeout * 2);
-                            }
-                            catch (InterruptedException x)
-                            {
-                                throw new RuntimeException(x);
-                            }
-                        }
-                        super.asyncConnect();
-                    }
-                };
-
-                final CountDownLatch connectLatch = new CountDownLatch(1);
-                client.addClientListener(new ClientListener.Adapter()
-                {
-                    @Override
-                    public void connectRequired()
-                    {
-                        connectLatch.countDown();
-                    }
-                });
-                client.connect();
-                try
-                {
-                    assertTrue(connectLatch.await(gatewayTimeout + clientTimeout * 3, TimeUnit.MILLISECONDS));
-                }
-                finally
-                {
-                    client.disconnect();
-                }
-            }
-            finally
-            {
-                httpClient.stop();
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/DisconnectClientTest.java b/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/DisconnectClientTest.java
deleted file mode 100644
index 038c97a..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/DisconnectClientTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import junit.framework.TestCase;
-
-import org.eclipse.jetty.client.Address;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.rhttp.client.JettyClient;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.gateway.GatewayServer;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
-
-
-/**
- * @version $Revision$ $Date$
- */
-public class DisconnectClientTest extends TestCase
-{
-    public void testDifferentClientDisconnects() throws Exception
-    {
-        GatewayServer server = new GatewayServer();
-        Connector connector = new SelectChannelConnector();
-        server.addConnector(connector);
-        server.start();
-        try
-        {
-            Address address = new Address("localhost", connector.getLocalPort());
-
-            HttpClient httpClient = new HttpClient();
-            httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
-            httpClient.start();
-            try
-            {
-                final CountDownLatch latch = new CountDownLatch(1);
-                String targetId = "1";
-                final RHTTPClient client1 = new JettyClient(httpClient, address, server.getContext().getContextPath()+GatewayServer.DFT_CONNECT_PATH, targetId)
-                {
-                    @Override
-                    protected void connectComplete(byte[] responseContent) throws IOException
-                    {
-                        // If the other client can disconnect this one, this method is called soon after it disconnected
-                        latch.countDown();
-                        super.connectComplete(responseContent);
-                    }
-                };
-                client1.connect();
-                try
-                {
-                    final RHTTPClient client2 = new JettyClient(httpClient, address, server.getContext().getContextPath()+GatewayServer.DFT_CONNECT_PATH, targetId);
-                    // Disconnect client 2, this should not disconnect client1
-                    client2.disconnect();
-
-                    // We want the await() to expire, it means it has not disconnected
-                    assertFalse(latch.await(1000, TimeUnit.MILLISECONDS));
-                }
-                finally
-                {
-                    client1.disconnect();
-                }
-            }
-            finally
-            {
-                httpClient.stop();
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/DuplicateClientTest.java b/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/DuplicateClientTest.java
deleted file mode 100644
index 298a74c..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/DuplicateClientTest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.IOException;
-
-import junit.framework.TestCase;
-
-import org.eclipse.jetty.client.Address;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.rhttp.client.JettyClient;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.gateway.GatewayServer;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
-
-
-/**
- * @version $Revision$ $Date$
- */
-public class DuplicateClientTest extends TestCase
-{
-    public void testDuplicateClient() throws Exception
-    {
-        GatewayServer server = new GatewayServer();
-        Connector connector = new SelectChannelConnector();
-        server.addConnector(connector);
-        server.start();
-        try
-        {
-            Address address = new Address("localhost", connector.getLocalPort());
-
-            HttpClient httpClient = new HttpClient();
-            httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
-            httpClient.start();
-            try
-            {
-                String targetId = "1";
-                final RHTTPClient client1 = new JettyClient(httpClient, address, server.getContext().getContextPath()+GatewayServer.DFT_CONNECT_PATH, targetId);
-                client1.connect();
-                try
-                {
-                    final RHTTPClient client2 = new JettyClient(httpClient, address, server.getContext().getContextPath()+GatewayServer.DFT_CONNECT_PATH, targetId);
-                    try
-                    {
-                        client2.connect();
-                        fail();
-                    }
-                    catch (IOException x)
-                    {
-                    }
-                }
-                finally
-                {
-                    client1.disconnect();
-                }
-            }
-            finally
-            {
-                httpClient.stop();
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/ExternalRequestNotSuspendedTest.java b/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/ExternalRequestNotSuspendedTest.java
deleted file mode 100644
index 7b02d64..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/ExternalRequestNotSuspendedTest.java
+++ /dev/null
@@ -1,186 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.IOException;
-import java.net.URLEncoder;
-import java.util.HashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import junit.framework.TestCase;
-
-import org.eclipse.jetty.client.Address;
-import org.eclipse.jetty.client.ContentExchange;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpExchange;
-import org.eclipse.jetty.http.HttpMethods;
-import org.eclipse.jetty.io.ByteArrayBuffer;
-import org.eclipse.jetty.rhttp.client.JettyClient;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.client.RHTTPListener;
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-import org.eclipse.jetty.rhttp.gateway.ExternalRequest;
-import org.eclipse.jetty.rhttp.gateway.Gateway;
-import org.eclipse.jetty.rhttp.gateway.GatewayServer;
-import org.eclipse.jetty.rhttp.gateway.StandardGateway;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
-
-
-/**
- * @version $Revision$ $Date$
- */
-public class ExternalRequestNotSuspendedTest extends TestCase
-{
-    public void testExternalRequestNotSuspended() throws Exception
-    {
-        final CountDownLatch respondLatch = new CountDownLatch(1);
-        final CountDownLatch suspendLatch = new CountDownLatch(1);
-        final AtomicBoolean suspended = new AtomicBoolean(true);
-        GatewayServer server = new GatewayServer()
-        {
-            @Override
-            protected Gateway createGateway()
-            {
-                StandardGateway gateway = new StandardGateway()
-                {
-                    @Override
-                    public ExternalRequest newExternalRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException
-                    {
-                        return new SlowToSuspendExternalRequest(super.newExternalRequest(httpRequest, httpResponse), respondLatch, suspendLatch, suspended);
-                    }
-                };
-                return gateway;
-            }
-        };
-        SelectChannelConnector connector = new SelectChannelConnector();
-        server.addConnector(connector);
-        server.start();
-        try
-        {
-            Address address = new Address("localhost", connector.getLocalPort());
-
-            HttpClient httpClient = new HttpClient();
-            httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
-            httpClient.start();
-            try
-            {
-                String targetId = "1";
-                final RHTTPClient client = new JettyClient(httpClient, address, server.getContext().getContextPath()+GatewayServer.DFT_CONNECT_PATH, targetId);
-                final AtomicReference<Exception> exception = new AtomicReference<Exception>();
-                client.addListener(new RHTTPListener()
-                {
-                    public void onRequest(RHTTPRequest request)
-                    {
-                        try
-                        {
-                            RHTTPResponse response = new RHTTPResponse(request.getId(), 200, "OK", new HashMap<String, String>(), request.getBody());
-                            client.deliver(response);
-                        }
-                        catch (Exception x)
-                        {
-                            exception.set(x);
-                        }
-                    }
-                });
-
-                client.connect();
-                try
-                {
-                    // Make a request to the gateway and check response
-                    ContentExchange exchange = new ContentExchange(true);
-                    exchange.setMethod(HttpMethods.POST);
-                    exchange.setAddress(address);
-                    exchange.setURI(server.getContext().getContextPath()+GatewayServer.DFT_EXT_PATH + "/" + URLEncoder.encode(targetId, "UTF-8"));
-                    String requestContent = "body";
-                    exchange.setRequestContent(new ByteArrayBuffer(requestContent.getBytes("UTF-8")));
-                    httpClient.send(exchange);
-
-                    int status = exchange.waitForDone();
-                    assertEquals(HttpExchange.STATUS_COMPLETED, status);
-                    assertEquals(HttpServletResponse.SC_OK, exchange.getResponseStatus());
-                    assertNull(exception.get());
-
-                    suspendLatch.await();
-                    assertFalse(suspended.get());
-                }
-                finally
-                {
-                    client.disconnect();
-                }
-            }
-            finally
-            {
-                httpClient.stop();
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-
-    private class SlowToSuspendExternalRequest implements ExternalRequest
-    {
-        private final ExternalRequest delegate;
-        private final CountDownLatch respondLatch;
-        private final CountDownLatch suspendLatch;
-        private final AtomicBoolean suspended;
-
-        private SlowToSuspendExternalRequest(ExternalRequest delegate, CountDownLatch respondLatch, CountDownLatch suspendLatch, AtomicBoolean suspended)
-        {
-            this.delegate = delegate;
-            this.respondLatch = respondLatch;
-            this.suspendLatch = suspendLatch;
-            this.suspended = suspended;
-        }
-
-        public boolean suspend()
-        {
-            try
-            {
-                respondLatch.await();
-                boolean result = delegate.suspend();
-                suspended.set(result);
-                suspendLatch.countDown();
-                return result;
-            }
-            catch (InterruptedException x)
-            {
-                throw new AssertionError(x);
-            }
-        }
-
-        public void respond(RHTTPResponse response) throws IOException
-        {
-            delegate.respond(response);
-            respondLatch.countDown();
-        }
-
-        public RHTTPRequest getRequest()
-        {
-            return delegate.getRequest();
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/ExternalTimeoutTest.java b/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/ExternalTimeoutTest.java
deleted file mode 100644
index ed12923..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/ExternalTimeoutTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.net.URLEncoder;
-import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.servlet.http.HttpServletResponse;
-
-import junit.framework.TestCase;
-
-import org.eclipse.jetty.client.Address;
-import org.eclipse.jetty.client.ContentExchange;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpExchange;
-import org.eclipse.jetty.http.HttpMethods;
-import org.eclipse.jetty.io.ByteArrayBuffer;
-import org.eclipse.jetty.rhttp.client.JettyClient;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.client.RHTTPListener;
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-import org.eclipse.jetty.rhttp.gateway.GatewayServer;
-import org.eclipse.jetty.rhttp.gateway.StandardGateway;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
-
-
-/**
- * @version $Revision$ $Date$
- */
-public class ExternalTimeoutTest extends TestCase
-{
-    public void testExternalTimeout() throws Exception
-    {
-        GatewayServer server = new GatewayServer();
-        SelectChannelConnector connector = new SelectChannelConnector();
-        server.addConnector(connector);
-        final long externalTimeout = 5000L;
-        ((StandardGateway)server.getGateway()).setExternalTimeout(externalTimeout);
-        server.start();
-        try
-        {
-            Address address = new Address("localhost", connector.getLocalPort());
-
-            HttpClient httpClient = new HttpClient();
-            httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
-            httpClient.start();
-            try
-            {
-                String targetId = "1";
-                final RHTTPClient client = new JettyClient(httpClient, address, server.getContext().getContextPath()+GatewayServer.DFT_CONNECT_PATH, targetId);
-                final AtomicReference<Integer> requestId = new AtomicReference<Integer>();
-                final AtomicReference<Exception> exceptionRef = new AtomicReference<Exception>();
-                client.addListener(new RHTTPListener()
-                {
-                    public void onRequest(RHTTPRequest request)
-                    {
-                        try
-                        {
-                            // Save the request id
-                            requestId.set(request.getId());
-
-                            // Wait until external timeout expires
-                            Thread.sleep(externalTimeout + 1000L);
-                            RHTTPResponse response = new RHTTPResponse(request.getId(), 200, "OK", new HashMap<String, String>(), request.getBody());
-                            client.deliver(response);
-                        }
-                        catch (Exception x)
-                        {
-                            exceptionRef.set(x);
-                        }
-                    }
-                });
-
-                client.connect();
-                try
-                {
-                    // Make a request to the gateway and check response
-                    ContentExchange exchange = new ContentExchange(true);
-                    exchange.setMethod(HttpMethods.POST);
-                    exchange.setAddress(address);
-                    exchange.setURI(server.getContext().getContextPath()+GatewayServer.DFT_EXT_PATH + "/" + URLEncoder.encode(targetId, "UTF-8"));
-                    String requestContent = "body";
-                    exchange.setRequestContent(new ByteArrayBuffer(requestContent.getBytes("UTF-8")));
-                    httpClient.send(exchange);
-
-                    int status = exchange.waitForDone();
-                    assertEquals(HttpExchange.STATUS_COMPLETED, status);
-                    assertEquals(HttpServletResponse.SC_GATEWAY_TIMEOUT, exchange.getResponseStatus());
-                    assertNull(exceptionRef.get());
-
-                    assertNull(server.getGateway().removeExternalRequest(requestId.get()));
-                }
-                finally
-                {
-                    client.disconnect();
-                }
-            }
-            finally
-            {
-                httpClient.stop();
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/GatewayEchoServer.java b/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/GatewayEchoServer.java
deleted file mode 100644
index dda32eb..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/GatewayEchoServer.java
+++ /dev/null
@@ -1,109 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.util.HashMap;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.eclipse.jetty.client.Address;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.rhttp.client.JettyClient;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.client.RHTTPListener;
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-import org.eclipse.jetty.rhttp.gateway.GatewayServer;
-import org.eclipse.jetty.rhttp.gateway.TargetIdRetriever;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
-
-
-/**
- * @version $Revision$ $Date$
- */
-public class GatewayEchoServer
-{
-    private volatile GatewayServer server;
-    private volatile Address address;
-    private volatile String uri;
-    private volatile HttpClient httpClient;
-    private volatile RHTTPClient client;
-
-    public void start() throws Exception
-    {
-        server = new GatewayServer();
-        Connector connector = new SelectChannelConnector();
-        server.addConnector(connector);
-        server.setTargetIdRetriever(new EchoTargetIdRetriever());
-        server.start();
-        server.dumpStdErr();
-        address = new Address("localhost", connector.getLocalPort());
-        uri = server.getContext().getContextPath()+GatewayServer.DFT_EXT_PATH;
-
-        httpClient = new HttpClient();
-        httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
-        httpClient.start();
-
-        client = new JettyClient(httpClient, new Address("localhost", connector.getLocalPort()), server.getContext().getContextPath()+GatewayServer.DFT_CONNECT_PATH, "echo");
-        client.addListener(new EchoListener(client));
-        client.connect();
-    }
-
-    public void stop() throws Exception
-    {
-        client.disconnect();
-        httpClient.stop();
-        server.stop();
-    }
-
-    public Address getAddress()
-    {
-        return address;
-    }
-
-    public String getURI()
-    {
-        return uri;
-    }
-
-    public static class EchoTargetIdRetriever implements TargetIdRetriever
-    {
-        public String retrieveTargetId(HttpServletRequest httpRequest)
-        {
-            return "echo";
-        }
-    }
-
-    private static class EchoListener implements RHTTPListener
-    {
-        private final RHTTPClient client;
-
-        public EchoListener(RHTTPClient client)
-        {
-            this.client = client;
-        }
-
-        public void onRequest(RHTTPRequest request) throws Exception
-        {
-            RHTTPResponse response = new RHTTPResponse(request.getId(), 200, "OK", new HashMap<String, String>(), request.getBody());
-            client.deliver(response);
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/GatewayEchoTest.java b/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/GatewayEchoTest.java
deleted file mode 100644
index 4f252b2..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/GatewayEchoTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import javax.servlet.http.HttpServletResponse;
-
-import junit.framework.TestCase;
-
-import org.eclipse.jetty.client.ContentExchange;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpExchange;
-import org.eclipse.jetty.http.HttpMethods;
-import org.eclipse.jetty.io.ByteArrayBuffer;
-
-/**
- * @version $Revision$ $Date$
- */
-public class GatewayEchoTest extends TestCase
-{
-    /**
-     * Tests that the basic functionality of the gateway works,
-     * by issuing a request and by replying with the same body.
-     *
-     * @throws Exception in case of test exceptions
-     */
-    public void testEcho() throws Exception
-    {
-        GatewayEchoServer server = new GatewayEchoServer();
-        server.start();
-        try
-        {
-            HttpClient httpClient = new HttpClient();
-            httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
-            httpClient.start();
-            try
-            {
-                // Make a request to the gateway and check response
-                ContentExchange exchange = new ContentExchange(true);
-                exchange.setMethod(HttpMethods.POST);
-                exchange.setAddress(server.getAddress());
-                exchange.setURI(server.getURI() + "/");
-                String requestBody = "body";
-                exchange.setRequestContent(new ByteArrayBuffer(requestBody.getBytes("UTF-8")));
-                httpClient.send(exchange);
-                int status = exchange.waitForDone();
-                assertEquals(HttpExchange.STATUS_COMPLETED, status);
-                assertEquals(HttpServletResponse.SC_OK, exchange.getResponseStatus());
-                String responseContent = exchange.getResponseContent();
-                assertEquals(responseContent, requestBody);
-            }
-            finally
-            {
-                httpClient.stop();
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/GatewayLoadTest.java b/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/GatewayLoadTest.java
deleted file mode 100644
index ba72715..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/GatewayLoadTest.java
+++ /dev/null
@@ -1,202 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-
-import javax.servlet.http.HttpServletResponse;
-
-import junit.framework.TestCase;
-
-import org.eclipse.jetty.client.ContentExchange;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.http.HttpMethods;
-import org.eclipse.jetty.io.ByteArrayBuffer;
-
-/**
- * @version $Revision$ $Date$
- */
-public class GatewayLoadTest extends TestCase
-{
-    private final ConcurrentMap<Long, AtomicLong> latencies = new ConcurrentHashMap<Long, AtomicLong>();
-    private final AtomicLong responses = new AtomicLong(0L);
-    private final AtomicLong failures = new AtomicLong(0L);
-    private final AtomicLong minLatency = new AtomicLong(Long.MAX_VALUE);
-    private final AtomicLong maxLatency = new AtomicLong(0L);
-    private final AtomicLong totLatency = new AtomicLong(0L);
-
-    public void testEcho() throws Exception
-    {
-        GatewayEchoServer server = new GatewayEchoServer();
-        server.start();
-
-        HttpClient httpClient = new HttpClient();
-        httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
-        httpClient.start();
-
-        String uri = server.getURI() + "/";
-
-        char[] chars = new char[1024];
-        Arrays.fill(chars, 'x');
-        String requestBody = new String(chars);
-
-        int count = 1000;
-        CountDownLatch latch = new CountDownLatch(count);
-        for (int i = 0; i < count; ++i)
-        {
-            GatewayLoadTestExchange exchange = new GatewayLoadTestExchange(latch);
-            exchange.setMethod(HttpMethods.POST);
-            exchange.setAddress(server.getAddress());
-            exchange.setURI(uri + i);
-            exchange.setRequestContent(new ByteArrayBuffer(requestBody.getBytes("UTF-8")));
-            exchange.setStartNanos(System.nanoTime());
-            httpClient.send(exchange);
-            Thread.sleep(5);
-        }
-        latch.await();
-        printLatencies(count);
-        assertEquals(count, responses.get() + failures.get());
-    }
-
-    private void updateLatencies(long start, long end)
-    {
-        long latency = end - start;
-
-        // Update the latencies using a non-blocking algorithm
-        long oldMinLatency = minLatency.get();
-        while (latency < oldMinLatency)
-        {
-            if (minLatency.compareAndSet(oldMinLatency, latency)) break;
-            oldMinLatency = minLatency.get();
-        }
-        long oldMaxLatency = maxLatency.get();
-        while (latency > oldMaxLatency)
-        {
-            if (maxLatency.compareAndSet(oldMaxLatency, latency)) break;
-            oldMaxLatency = maxLatency.get();
-        }
-        totLatency.addAndGet(latency);
-
-        latencies.putIfAbsent(latency, new AtomicLong(0L));
-        latencies.get(latency).incrementAndGet();
-    }
-
-    public void printLatencies(long expectedCount)
-    {
-        if (latencies.size() > 1)
-        {
-            long maxLatencyBucketFrequency = 0L;
-            long[] latencyBucketFrequencies = new long[20];
-            long latencyRange = maxLatency.get() - minLatency.get();
-            for (Iterator<Map.Entry<Long, AtomicLong>> entries = latencies.entrySet().iterator(); entries.hasNext();)
-            {
-                Map.Entry<Long, AtomicLong> entry = entries.next();
-                long latency = entry.getKey();
-                Long bucketIndex = (latency - minLatency.get()) * latencyBucketFrequencies.length / latencyRange;
-                int index = bucketIndex.intValue() == latencyBucketFrequencies.length ? latencyBucketFrequencies.length - 1 : bucketIndex.intValue();
-                long value = entry.getValue().get();
-                latencyBucketFrequencies[index] += value;
-                if (latencyBucketFrequencies[index] > maxLatencyBucketFrequency) maxLatencyBucketFrequency = latencyBucketFrequencies[index];
-                entries.remove();
-            }
-
-            System.out.println("Messages - Latency Distribution Curve (X axis: Frequency, Y axis: Latency):");
-            for (int i = 0; i < latencyBucketFrequencies.length; i++)
-            {
-                long latencyBucketFrequency = latencyBucketFrequencies[i];
-                int value = Math.round(latencyBucketFrequency * (float) latencyBucketFrequencies.length / maxLatencyBucketFrequency);
-                if (value == latencyBucketFrequencies.length) value = value - 1;
-                for (int j = 0; j < value; ++j) System.out.print(" ");
-                System.out.print("@");
-                for (int j = value + 1; j < latencyBucketFrequencies.length; ++j) System.out.print(" ");
-                System.out.print("  _  ");
-                System.out.print(TimeUnit.NANOSECONDS.toMillis((latencyRange * (i + 1) / latencyBucketFrequencies.length) + minLatency.get()));
-                System.out.print(" (" + latencyBucketFrequency + ")");
-                System.out.println(" ms");
-            }
-        }
-
-        long responseCount = responses.get();
-        System.out.print("Messages success/failed/expected = ");
-        System.out.print(responseCount);
-        System.out.print("/");
-        System.out.print(failures.get());
-        System.out.print("/");
-        System.out.print(expectedCount);
-        System.out.print(" - Latency min/ave/max = ");
-        System.out.print(TimeUnit.NANOSECONDS.toMillis(minLatency.get()) + "/");
-        System.out.print(responseCount == 0 ? "-/" : TimeUnit.NANOSECONDS.toMillis(totLatency.get() / responseCount) + "/");
-        System.out.println(TimeUnit.NANOSECONDS.toMillis(maxLatency.get()) + " ms");
-    }
-
-    private class GatewayLoadTestExchange extends ContentExchange
-    {
-        private final CountDownLatch latch;
-        private volatile long start;
-
-        private GatewayLoadTestExchange(CountDownLatch latch)
-        {
-            super(true);
-            this.latch = latch;
-        }
-
-        @Override
-        protected void onResponseComplete() throws IOException
-        {
-            if (getResponseStatus() == HttpServletResponse.SC_OK)
-            {
-                long end = System.nanoTime();
-                responses.incrementAndGet();
-                updateLatencies(start, end);
-            }
-            else
-            {
-                failures.incrementAndGet();
-            }
-            latch.countDown();
-        }
-
-        @Override
-        protected void onException(Throwable throwable)
-        {
-            failures.incrementAndGet();
-            latch.countDown();
-        }
-
-        @Override
-        protected void onExpire()
-        {
-            failures.incrementAndGet();
-            latch.countDown();
-        }
-
-        public void setStartNanos(long value)
-        {
-            start = value;
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/GatewayTimeoutTest.java b/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/GatewayTimeoutTest.java
deleted file mode 100644
index 40e0498..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/GatewayTimeoutTest.java
+++ /dev/null
@@ -1,130 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.net.URLEncoder;
-import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.servlet.http.HttpServletResponse;
-
-import junit.framework.TestCase;
-
-import org.eclipse.jetty.client.Address;
-import org.eclipse.jetty.client.ContentExchange;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpExchange;
-import org.eclipse.jetty.http.HttpMethods;
-import org.eclipse.jetty.io.ByteArrayBuffer;
-import org.eclipse.jetty.rhttp.client.JettyClient;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.client.RHTTPListener;
-import org.eclipse.jetty.rhttp.client.RHTTPRequest;
-import org.eclipse.jetty.rhttp.client.RHTTPResponse;
-import org.eclipse.jetty.rhttp.gateway.GatewayServer;
-import org.eclipse.jetty.rhttp.gateway.StandardGateway;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
-
-
-/**
- * @version $Revision$ $Date$
- */
-public class GatewayTimeoutTest extends TestCase
-{
-    /**
-     * Tests a forwarded request that lasts longer than the gateway timeout.
-     * The gateway client will perform 2 long polls before the forwarded request's response is returned.
-     *
-     * @throws Exception in case of test exceptions
-     */
-    public void testGatewayTimeout() throws Exception
-    {
-        GatewayServer server = new GatewayServer();
-        Connector connector = new SelectChannelConnector();
-        server.addConnector(connector);
-        final long gatewayTimeout = 5000L;
-        ((StandardGateway)server.getGateway()).setGatewayTimeout(gatewayTimeout);
-        final long externalTimeout = gatewayTimeout * 2;
-        ((StandardGateway)server.getGateway()).setExternalTimeout(externalTimeout);
-        server.start();
-        try
-        {
-            Address address = new Address("localhost", connector.getLocalPort());
-
-            HttpClient httpClient = new HttpClient();
-            httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
-            httpClient.start();
-            try
-            {
-                String targetId = "1";
-                final RHTTPClient client = new JettyClient(httpClient, address, server.getContext().getContextPath()+GatewayServer.DFT_CONNECT_PATH, targetId);
-                final AtomicReference<Exception> exceptionRef = new AtomicReference<Exception>();
-                client.addListener(new RHTTPListener()
-                {
-                    public void onRequest(RHTTPRequest request)
-                    {
-                        try
-                        {
-                            // Wait until gateway timeout expires
-                            Thread.sleep(gatewayTimeout + 1000L);
-                            RHTTPResponse response = new RHTTPResponse(request.getId(), 200, "OK", new HashMap<String, String>(), request.getBody());
-                            client.deliver(response);
-                        }
-                        catch (Exception x)
-                        {
-                            exceptionRef.set(x);
-                        }
-                    }
-                });
-                client.connect();
-                try
-                {
-                    // Make a request to the gateway and check response
-                    ContentExchange exchange = new ContentExchange(true);
-                    exchange.setMethod(HttpMethods.POST);
-                    exchange.setAddress(address);
-                    exchange.setURI(server.getContext().getContextPath()+GatewayServer.DFT_EXT_PATH + "/" + URLEncoder.encode(targetId, "UTF-8"));
-                    String requestContent = "body";
-                    exchange.setRequestContent(new ByteArrayBuffer(requestContent.getBytes("UTF-8")));
-                    httpClient.send(exchange);
-
-                    int status = exchange.waitForDone();
-                    assertEquals(HttpExchange.STATUS_COMPLETED, status);
-                    assertEquals(HttpServletResponse.SC_OK, exchange.getResponseStatus());
-                    assertNull(exceptionRef.get());
-                    String responseContent = exchange.getResponseContent();
-                    assertEquals(responseContent, requestContent);
-                }
-                finally
-                {
-                    client.disconnect();
-                }
-            }
-            finally
-            {
-                httpClient.stop();
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/HandshakeClientTest.java b/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/HandshakeClientTest.java
deleted file mode 100644
index d8e65b2..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/HandshakeClientTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import junit.framework.TestCase;
-
-import org.eclipse.jetty.client.Address;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.rhttp.client.JettyClient;
-import org.eclipse.jetty.rhttp.client.RHTTPClient;
-import org.eclipse.jetty.rhttp.gateway.GatewayServer;
-import org.eclipse.jetty.rhttp.gateway.StandardGateway;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
-
-
-/**
- * @version $Revision$ $Date$
- */
-public class HandshakeClientTest extends TestCase
-{
-    public void testConnectReturnsImmediately() throws Exception
-    {
-        GatewayServer server = new GatewayServer();
-        SelectChannelConnector connector = new SelectChannelConnector();
-        server.addConnector(connector);
-        long gwt=5000L;
-        ((StandardGateway)server.getGateway()).setGatewayTimeout(gwt);
-        server.start();
-        try
-        {
-            HttpClient httpClient = new HttpClient();
-            httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
-            httpClient.start();
-            try
-            {
-                RHTTPClient client = new JettyClient(httpClient, new Address("localhost", connector.getLocalPort()), server.getContext().getContextPath()+GatewayServer.DFT_CONNECT_PATH, "test1");
-                long start = System.currentTimeMillis();
-                client.connect();
-                try
-                {
-                    long end = System.currentTimeMillis();
-                    assertTrue(end - start < gwt / 2);
-                }
-                finally
-                {
-                    client.disconnect();
-                }
-            }
-            finally
-            {
-                httpClient.stop();
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/HostTargetIdRetrieverTest.java b/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/HostTargetIdRetrieverTest.java
deleted file mode 100644
index 861fcd3..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/test/java/org/eclipse/jetty/rhttp/gateway/HostTargetIdRetrieverTest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.gateway;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.eclipse.jetty.rhttp.gateway.HostTargetIdRetriever;
-
-import junit.framework.TestCase;
-
-/**
- * @version $Revision$ $Date$
- */
-public class HostTargetIdRetrieverTest extends TestCase
-{
-    public void testHostTargetIdRetrieverNoSuffix()
-    {
-        String host = "test";
-        Class<HttpServletRequest> klass = HttpServletRequest.class;
-        HttpServletRequest request = (HttpServletRequest)Proxy.newProxyInstance(klass.getClassLoader(), new Class<?>[]{klass}, new Request(host));
-
-        HostTargetIdRetriever retriever = new HostTargetIdRetriever(null);
-        String result = retriever.retrieveTargetId(request);
-
-        assertEquals(host, result);
-    }
-
-    public void testHostTargetIdRetrieverWithSuffix()
-    {
-        String suffix = ".rhttp.example.com";
-        String host = "test";
-        Class<HttpServletRequest> klass = HttpServletRequest.class;
-        HttpServletRequest request = (HttpServletRequest)Proxy.newProxyInstance(klass.getClassLoader(), new Class<?>[]{klass}, new Request(host + suffix));
-
-        HostTargetIdRetriever retriever = new HostTargetIdRetriever(suffix);
-        String result = retriever.retrieveTargetId(request);
-
-        assertEquals(host, result);
-    }
-
-    public void testHostTargetIdRetrieverWithSuffixAndPort()
-    {
-        String suffix = ".rhttp.example.com";
-        String host = "test";
-        Class<HttpServletRequest> klass = HttpServletRequest.class;
-        HttpServletRequest request = (HttpServletRequest)Proxy.newProxyInstance(klass.getClassLoader(), new Class<?>[]{klass}, new Request(host + suffix + ":8080"));
-
-        HostTargetIdRetriever retriever = new HostTargetIdRetriever(suffix);
-        String result = retriever.retrieveTargetId(request);
-
-        assertEquals(host, result);
-    }
-
-    public void testHostTargetIdRetrieverNullHost()
-    {
-        Class<HttpServletRequest> klass = HttpServletRequest.class;
-        HttpServletRequest request = (HttpServletRequest)Proxy.newProxyInstance(klass.getClassLoader(), new Class<?>[]{klass}, new Request(null));
-
-        HostTargetIdRetriever retriever = new HostTargetIdRetriever(".rhttp.example.com");
-        String result = retriever.retrieveTargetId(request);
-
-        assertNull(result);
-    }
-
-    private static class Request implements InvocationHandler
-    {
-        private final String host;
-
-        private Request(String host)
-        {
-            this.host = host;
-        }
-
-        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
-        {
-            if ("getHeader".equals(method.getName()))
-            {
-                if (args.length == 1 && "Host".equals(args[0]))
-                {
-                    return host;
-                }
-            }
-            return null;
-        }
-    }
-
-}
diff --git a/jetty-rhttp/jetty-rhttp-gateway/src/test/resources/log4j.properties b/jetty-rhttp/jetty-rhttp-gateway/src/test/resources/log4j.properties
deleted file mode 100644
index 8e6eddb..0000000
--- a/jetty-rhttp/jetty-rhttp-gateway/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-# LOG4J levels: OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL
-#
-log4j.rootLogger=ALL,CONSOLE
-
-log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
-#log4j.appender.CONSOLE.threshold=INFO
-log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
-#log4j.appender.CONSOLE.layout.ConversionPattern=%d %t [%5p][%c{1}] %m%n
-log4j.appender.CONSOLE.layout.ConversionPattern=%d [%5p][%c] %m%n
-
-# Level tuning
-log4j.logger.org.eclipse.jetty=INFO
-log4j.logger.org.mortbay.jetty.rhttp=INFO
diff --git a/jetty-rhttp/jetty-rhttp-loadtest/pom.xml b/jetty-rhttp/jetty-rhttp-loadtest/pom.xml
deleted file mode 100644
index bd36d4e..0000000
--- a/jetty-rhttp/jetty-rhttp-loadtest/pom.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.rhttp</groupId>
-        <artifactId>jetty-rhttp-project</artifactId>
-        <version>9.0.0-SNAPSHOT</version>
-    </parent>
-
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>reverse-http-loadtest</artifactId>
-    <packaging>jar</packaging>
-    <name>Jetty :: Reverse HTTP :: Load Test</name>
-
-    <profiles>
-        <profile>
-            <id>loader</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>exec-maven-plugin</artifactId>
-                        <configuration>
-                            <mainClass>org.eclipse.jetty.rhttp.loadtest.Loader</mainClass>
-                            <classpathScope>runtime</classpathScope>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>server</id>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>exec-maven-plugin</artifactId>
-                        <configuration>
-                            <mainClass>org.eclipse.jetty.rhttp.loadtest.Server</mainClass>
-                            <classpathScope>runtime</classpathScope>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>reverse-http-gateway</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-    </dependencies>
-
-</project>
diff --git a/jetty-rhttp/jetty-rhttp-loadtest/src/main/java/org/eclipse/jetty/rhttp/loadtest/Loader.java b/jetty-rhttp/jetty-rhttp-loadtest/src/main/java/org/eclipse/jetty/rhttp/loadtest/Loader.java
deleted file mode 100644
index 1db0250..0000000
--- a/jetty-rhttp/jetty-rhttp-loadtest/src/main/java/org/eclipse/jetty/rhttp/loadtest/Loader.java
+++ /dev/null
@@ -1,429 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.loadtest;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.eclipse.jetty.client.Address;
-import org.eclipse.jetty.client.ContentExchange;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.io.ByteArrayBuffer;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.mortbay.jetty.rhttp.client.RHTTPClient;
-import org.mortbay.jetty.rhttp.client.JettyClient;
-import org.mortbay.jetty.rhttp.client.RHTTPListener;
-import org.mortbay.jetty.rhttp.client.RHTTPRequest;
-import org.mortbay.jetty.rhttp.client.RHTTPResponse;
-
-/**
- * @version $Revision$ $Date$
- */
-public class Loader
-{
-    private final List<RHTTPClient> clients = new ArrayList<RHTTPClient>();
-    private final AtomicLong start = new AtomicLong();
-    private final AtomicLong end = new AtomicLong();
-    private final AtomicLong responses = new AtomicLong();
-    private final AtomicLong failures = new AtomicLong();
-    private final AtomicLong minLatency = new AtomicLong();
-    private final AtomicLong maxLatency = new AtomicLong();
-    private final AtomicLong totLatency = new AtomicLong();
-    private final ConcurrentMap<Long, AtomicLong> latencies = new ConcurrentHashMap<Long, AtomicLong>();
-    private final String nodeName;
-
-    public static void main(String[] args) throws Exception
-    {
-        String nodeName = "";
-        if (args.length > 0)
-            nodeName = args[0];
-
-        Loader loader = new Loader(nodeName);
-        loader.run();
-    }
-
-    public Loader(String nodeName)
-    {
-        this.nodeName = nodeName;
-    }
-
-    private void run() throws Exception
-    {
-        HttpClient httpClient = new HttpClient();
-        httpClient.setMaxConnectionsPerAddress(40000);
-        QueuedThreadPool threadPool = new QueuedThreadPool();
-        threadPool.setMaxThreads(500);
-        threadPool.setDaemon(true);
-        httpClient.setThreadPool(threadPool);
-        httpClient.setIdleTimeout(5000);
-        httpClient.start();
-
-        Random random = new Random();
-
-        BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
-
-        System.err.print("server [localhost]: ");
-        String value = console.readLine().trim();
-        if (value.length() == 0)
-            value = "localhost";
-        String host = value;
-
-        System.err.print("port [8080]: ");
-        value = console.readLine().trim();
-        if (value.length() == 0)
-            value = "8080";
-        int port = Integer.parseInt(value);
-
-        System.err.print("context []: ");
-        value = console.readLine().trim();
-        if (value.length() == 0)
-            value = "";
-        String context = value;
-
-        System.err.print("external path [/]: ");
-        value = console.readLine().trim();
-        if (value.length() == 0)
-            value = "/";
-        String externalPath = value;
-
-        System.err.print("gateway path [/__gateway]: ");
-        value = console.readLine().trim();
-        if (value.length() == 0)
-            value = "/__gateway";
-        String gatewayPath = value;
-
-        int clients = 100;
-        int batchCount = 1000;
-        int batchSize = 5;
-        long batchPause = 5;
-        int requestSize = 50;
-
-        while (true)
-        {
-            System.err.println("-----");
-
-            System.err.print("clients [" + clients + "]: ");
-            value = console.readLine();
-            if (value == null)
-                break;
-            value = value.trim();
-            if (value.length() == 0)
-                value = "" + clients;
-            clients = Integer.parseInt(value);
-
-            System.err.println("Waiting for clients to be ready...");
-
-            Address gatewayAddress = new Address(host, port);
-            String gatewayURI = context + gatewayPath;
-
-            // Create or remove the necessary clients
-            int currentClients = this.clients.size();
-            if (currentClients < clients)
-            {
-                for (int i = 0; i < clients - currentClients; ++i)
-                {
-                    final RHTTPClient client = new JettyClient(httpClient, gatewayAddress, gatewayURI, nodeName + (currentClients + i));
-                    client.addListener(new EchoListener(client));
-                    client.connect();
-                    this.clients.add(client);
-
-                    // Give some time to the server to accept connections and
-                    // reply to handshakes and connects
-                    if (i % 10 == 0)
-                    {
-                        Thread.sleep(100);
-                    }
-                }
-            }
-            else if (currentClients > clients)
-            {
-                for (int i = 0; i < currentClients - clients; ++i)
-                {
-                    RHTTPClient client = this.clients.remove(currentClients - i - 1);
-                    client.disconnect();
-                }
-            }
-
-            System.err.println("Clients ready");
-
-            currentClients = this.clients.size();
-            if (currentClients > 0)
-            {
-                System.err.print("batch count [" + batchCount + "]: ");
-                value = console.readLine().trim();
-                if (value.length() == 0)
-                    value = "" + batchCount;
-                batchCount = Integer.parseInt(value);
-
-                System.err.print("batch size [" + batchSize + "]: ");
-                value = console.readLine().trim();
-                if (value.length() == 0)
-                    value = "" + batchSize;
-                batchSize = Integer.parseInt(value);
-
-                System.err.print("batch pause [" + batchPause + "]: ");
-                value = console.readLine().trim();
-                if (value.length() == 0)
-                    value = "" + batchPause;
-                batchPause = Long.parseLong(value);
-
-                System.err.print("request size [" + requestSize + "]: ");
-                value = console.readLine().trim();
-                if (value.length() == 0)
-                    value = "" + requestSize;
-                requestSize = Integer.parseInt(value);
-                String requestBody = "";
-                for (int i = 0; i < requestSize; i++)
-                    requestBody += "x";
-
-                String externalURL = "http://" + host + ":" + port + context + externalPath;
-                if (!externalURL.endsWith("/"))
-                    externalURL += "/";
-
-                reset();
-
-                long start = System.nanoTime();
-                long expected = 0;
-                for (int i = 0; i < batchCount; ++i)
-                {
-                    for (int j = 0; j < batchSize; ++j)
-                    {
-                        int clientIndex = random.nextInt(this.clients.size());
-                        RHTTPClient client = this.clients.get(clientIndex);
-                        String targetId = client.getTargetId();
-                        String url = externalURL + targetId;
-
-                        ExternalExchange exchange = new ExternalExchange();
-                        exchange.setMethod("GET");
-                        exchange.setURL(url);
-                        exchange.setRequestContent(new ByteArrayBuffer(requestBody, "UTF-8"));
-                        exchange.send(httpClient);
-                        ++expected;
-                    }
-
-                    if (batchPause > 0)
-                        Thread.sleep(batchPause);
-                }
-                long end = System.nanoTime();
-                long elapsedNanos = end - start;
-                if (elapsedNanos > 0)
-                {
-                    System.err.print("Messages - Elapsed | Rate = ");
-                    System.err.print(TimeUnit.NANOSECONDS.toMillis(elapsedNanos));
-                    System.err.print(" ms | ");
-                    System.err.print(expected * 1000 * 1000 * 1000 / elapsedNanos);
-                    System.err.println(" requests/s ");
-                }
-
-                waitForResponses(expected);
-                printReport(expected);
-            }
-        }
-    }
-
-    private void reset()
-    {
-        start.set(0L);
-        end.set(0L);
-        responses.set(0L);
-        failures.set(0L);
-        minLatency.set(Long.MAX_VALUE);
-        maxLatency.set(0L);
-        totLatency.set(0L);
-    }
-
-    private void updateLatencies(long start, long end)
-    {
-        long latency = end - start;
-
-        // Update the latencies using a non-blocking algorithm
-        long oldMinLatency = minLatency.get();
-        while (latency < oldMinLatency)
-        {
-            if (minLatency.compareAndSet(oldMinLatency, latency)) break;
-            oldMinLatency = minLatency.get();
-        }
-        long oldMaxLatency = maxLatency.get();
-        while (latency > oldMaxLatency)
-        {
-            if (maxLatency.compareAndSet(oldMaxLatency, latency)) break;
-            oldMaxLatency = maxLatency.get();
-        }
-        totLatency.addAndGet(latency);
-
-        latencies.putIfAbsent(latency, new AtomicLong(0L));
-        latencies.get(latency).incrementAndGet();
-    }
-
-    private boolean waitForResponses(long expected) throws InterruptedException
-    {
-        long arrived = responses.get() + failures.get();
-        long lastArrived = 0;
-        int maxRetries = 20;
-        int retries = maxRetries;
-        while (arrived < expected)
-        {
-            System.err.println("Waiting for responses to arrive " + arrived + "/" + expected);
-            Thread.sleep(500);
-            if (lastArrived == arrived)
-            {
-                --retries;
-                if (retries == 0) break;
-            }
-            else
-            {
-                lastArrived = arrived;
-                retries = maxRetries;
-            }
-            arrived = responses.get() + failures.get();
-        }
-        if (arrived < expected)
-        {
-            System.err.println("Interrupting wait for responses " + arrived + "/" + expected);
-            return false;
-        }
-        else
-        {
-            System.err.println("All responses arrived " + arrived + "/" + expected);
-            return true;
-        }
-    }
-
-    public void printReport(long expectedCount)
-    {
-        long responseCount = responses.get() + failures.get();
-        System.err.print("Messages - Success/Failures/Expected = ");
-        System.err.print(responses.get());
-        System.err.print("/");
-        System.err.print(failures.get());
-        System.err.print("/");
-        System.err.println(expectedCount);
-
-        long elapsedNanos = end.get() - start.get();
-        if (elapsedNanos > 0)
-        {
-            System.err.print("Messages - Elapsed | Rate = ");
-            System.err.print(TimeUnit.NANOSECONDS.toMillis(elapsedNanos));
-            System.err.print(" ms | ");
-            System.err.print(responseCount * 1000 * 1000 * 1000 / elapsedNanos);
-            System.err.println(" responses/s ");
-        }
-
-        if (latencies.size() > 1)
-        {
-            long maxLatencyBucketFrequency = 0L;
-            long[] latencyBucketFrequencies = new long[20];
-            long latencyRange = maxLatency.get() - minLatency.get();
-            for (Iterator<Map.Entry<Long, AtomicLong>> entries = latencies.entrySet().iterator(); entries.hasNext();)
-            {
-                Map.Entry<Long, AtomicLong> entry = entries.next();
-                long latency = entry.getKey();
-                Long bucketIndex = (latency - minLatency.get()) * latencyBucketFrequencies.length / latencyRange;
-                int index = bucketIndex.intValue() == latencyBucketFrequencies.length ? latencyBucketFrequencies.length - 1 : bucketIndex.intValue();
-                long value = entry.getValue().get();
-                latencyBucketFrequencies[index] += value;
-                if (latencyBucketFrequencies[index] > maxLatencyBucketFrequency) maxLatencyBucketFrequency = latencyBucketFrequencies[index];
-                entries.remove();
-            }
-
-            System.err.println("Messages - Latency Distribution Curve (X axis: Frequency, Y axis: Latency):");
-            for (int i = 0; i < latencyBucketFrequencies.length; i++)
-            {
-                long latencyBucketFrequency = latencyBucketFrequencies[i];
-                int value = Math.round(latencyBucketFrequency * (float) latencyBucketFrequencies.length / maxLatencyBucketFrequency);
-                if (value == latencyBucketFrequencies.length) value = value - 1;
-                for (int j = 0; j < value; ++j) System.err.print(" ");
-                System.err.print("@");
-                for (int j = value + 1; j < latencyBucketFrequencies.length; ++j) System.err.print(" ");
-                System.err.print("  _  ");
-                System.err.print(TimeUnit.NANOSECONDS.toMillis((latencyRange * (i + 1) / latencyBucketFrequencies.length) + minLatency.get()));
-                System.err.println(" ms (" + latencyBucketFrequency + ")");
-            }
-        }
-
-        System.err.print("Messages - Latency Min/Ave/Max = ");
-        System.err.print(TimeUnit.NANOSECONDS.toMillis(minLatency.get()) + "/");
-        System.err.print(responseCount == 0 ? "-/" : TimeUnit.NANOSECONDS.toMillis(totLatency.get() / responseCount) + "/");
-        System.err.println(TimeUnit.NANOSECONDS.toMillis(maxLatency.get()) + " ms");
-    }
-
-    private class ExternalExchange extends ContentExchange
-    {
-        private volatile long sendTime;
-
-        private ExternalExchange()
-        {
-            super(true);
-        }
-
-        private void send(HttpClient httpClient) throws IOException
-        {
-            this.sendTime = System.nanoTime();
-            httpClient.send(this);
-        }
-
-        @Override
-        protected void onResponseComplete() throws IOException
-        {
-            if (getResponseStatus() == 200)
-                responses.incrementAndGet();
-            else
-                failures.incrementAndGet();
-
-            long arrivalTime = System.nanoTime();
-            if (start.get() == 0L)
-                start.set(arrivalTime);
-            end.set(arrivalTime);
-            updateLatencies(sendTime, arrivalTime);
-        }
-
-        @Override
-        protected void onException(Throwable x)
-        {
-            failures.incrementAndGet();
-        }
-    }
-
-    private static class EchoListener implements RHTTPListener
-    {
-        private final RHTTPClient client;
-
-        public EchoListener(RHTTPClient client)
-        {
-            this.client = client;
-        }
-
-        public void onRequest(RHTTPRequest request) throws Exception
-        {
-            RHTTPResponse response = new RHTTPResponse(request.getId(), 200, "OK", new HashMap<String, String>(), request.getBody());
-            client.deliver(response);
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-loadtest/src/main/java/org/eclipse/jetty/rhttp/loadtest/Server.java b/jetty-rhttp/jetty-rhttp-loadtest/src/main/java/org/eclipse/jetty/rhttp/loadtest/Server.java
deleted file mode 100644
index 56601b0..0000000
--- a/jetty-rhttp/jetty-rhttp-loadtest/src/main/java/org/eclipse/jetty/rhttp/loadtest/Server.java
+++ /dev/null
@@ -1,69 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.rhttp.loadtest;
-
-import org.eclipse.jetty.rhttp.gateway.GatewayServer;
-import org.eclipse.jetty.rhttp.gateway.StandardTargetIdRetriever;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
-
-/**
- * @version $Revision$ $Date$
- */
-public class Server
-{
-    public static void main(String[] args) throws Exception
-    {
-        int port = 8080;
-        if (args.length > 0)
-            port = Integer.parseInt(args[0]);
-
-        GatewayServer server = new GatewayServer();
-        Connector connector = new SelectChannelConnector();
-        connector.setLowResourceMaxIdleTime(connector.getMaxIdleTime());
-        connector.setPort(port);
-        server.addConnector(connector);
-        server.setTargetIdRetriever(new StandardTargetIdRetriever());
-        server.start();
-        server.getServer().dumpStdErr();
-        Runtime.getRuntime().addShutdownHook(new Shutdown(server));
-    }
-
-    private static class Shutdown extends Thread
-    {
-        private final GatewayServer server;
-
-        public Shutdown(GatewayServer server)
-        {
-            this.server = server;
-        }
-
-        @Override
-        public void run()
-        {
-            try
-            {
-                server.stop();
-            }
-            catch (Exception ignored)
-            {
-            }
-        }
-    }
-}
diff --git a/jetty-rhttp/jetty-rhttp-loadtest/src/main/resources/log4j.properties b/jetty-rhttp/jetty-rhttp-loadtest/src/main/resources/log4j.properties
deleted file mode 100644
index 8e6eddb..0000000
--- a/jetty-rhttp/jetty-rhttp-loadtest/src/main/resources/log4j.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-# LOG4J levels: OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL
-#
-log4j.rootLogger=ALL,CONSOLE
-
-log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
-#log4j.appender.CONSOLE.threshold=INFO
-log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
-#log4j.appender.CONSOLE.layout.ConversionPattern=%d %t [%5p][%c{1}] %m%n
-log4j.appender.CONSOLE.layout.ConversionPattern=%d [%5p][%c] %m%n
-
-# Level tuning
-log4j.logger.org.eclipse.jetty=INFO
-log4j.logger.org.mortbay.jetty.rhttp=INFO
diff --git a/jetty-rhttp/pom.xml b/jetty-rhttp/pom.xml
deleted file mode 100644
index 0284a0f..0000000
--- a/jetty-rhttp/pom.xml
+++ /dev/null
@@ -1,107 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty</groupId>
-        <artifactId>jetty-project</artifactId>
-        <version>9.0.0-SNAPSHOT</version>
-    </parent>
-
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>org.eclipse.jetty.rhttp</groupId>
-    <artifactId>jetty-rhttp-project</artifactId>
-    <packaging>pom</packaging>
-    <name>Jetty :: Reverse HTTP</name>
-
-    <properties>
-    </properties>
-
-    <modules>
-        <module>reverse-http-client</module>
-        <module>reverse-http-connector</module>
-        <module>reverse-http-gateway</module>
-        <module>reverse-http-loadtest</module>
-    </modules>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.sonatype.maven.plugin</groupId>
-                <artifactId>emma-maven-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <phase>process-classes</phase>
-                        <goals>
-                            <goal>instrument</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <classesDirectory>${project.build.directory}/generated-classes/emma/classes</classesDirectory>
-                    <systemProperties>
-                        <property>
-                            <name>emma.coverage.out.file</name>
-                            <value>${project.build.directory}/coverage.ec</value>
-                        </property>
-                    </systemProperties>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.sonatype.maven.plugin</groupId>
-                <artifactId>emma4it-maven-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>report</id>
-                        <phase>post-integration-test</phase>
-                        <goals>
-                            <goal>report</goal>
-                        </goals>
-                        <configuration>
-                            <sourceSets>
-                                <sourceSet>
-                                    <directory>${project.build.sourceDirectory}</directory>
-                                </sourceSet>
-                            </sourceSets>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>javax.servlet</groupId>
-                <artifactId>servlet-api</artifactId>
-            </dependency>
-            <dependency>
-                <groupId>org.eclipse.jetty</groupId>
-                <artifactId>jetty-server</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.eclipse.jetty</groupId>
-                <artifactId>jetty-client</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.eclipse.jetty</groupId>
-                <artifactId>jetty-io</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.eclipse.jetty</groupId>
-                <artifactId>jetty-util</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.eclipse.jetty.toolchain</groupId>
-                <artifactId>jetty-test-helper</artifactId>
-                <scope>test</scope>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-</project>
diff --git a/jetty-runner/pom.xml b/jetty-runner/pom.xml
index 5170540..4a2489a 100644
--- a/jetty-runner/pom.xml
+++ b/jetty-runner/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-runner</artifactId>
@@ -10,6 +10,7 @@
 
  <properties>
     <assembly-directory>target/distribution</assembly-directory>
+    <bundle-symbolic-name>${project.groupId}.runner</bundle-symbolic-name>
   </properties>
   <url>http://www.eclipse.org/jetty</url>
   <build>
@@ -20,7 +21,7 @@
         <executions>
           <execution>
             <id>unpack-dependencies</id>
-            <phase>package</phase>
+            <phase>prepare-package</phase>
             <goals>
               <goal>unpack-dependencies</goal>
             </goals>
@@ -35,39 +36,31 @@
         </executions>
       </plugin>
       <plugin>
-        <groupId>org.apache.maven.plugins </groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>package</id>
-            <phase>package</phase>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-            <configuration>
-              <archive>
-                <manifest>
-                  <mainClass>org.eclipse.jetty.runner.Runner</mainClass>
-                </manifest>
-                <manifestEntries>
-                  <mode>development</mode>
-                  <url>http://eclipse.org/jetty</url>
-                  <Built-By>${user.name}</Built-By>
-                  <package>org.eclipse.jetty.runner</package>
-                  <Bundle-Name>Jetty Runner</Bundle-Name>
-                  <Bundle-Vendor>Mort Bay Consulting</Bundle-Vendor>
-                </manifestEntries>
-              </archive>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
+          <groupId>org.apache.felix</groupId>
+          <artifactId>maven-bundle-plugin</artifactId>
+          <extensions>true</extensions>
+          <executions>
+              <execution>
+                  <goals>
+                      <goal>manifest</goal>
+                  </goals>
+              </execution>
+          </executions>
+          <configuration>
+            <instructions>
+              <Main-Class>org.eclipse.jetty.runner.Runner</Main-Class>
+              <Import-Package>!*</Import-Package>
+              <Export-Package />
+            </instructions>
+          </configuration>
+        </plugin>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
         <configuration>
-          <skip>true</skip>
+         <archive>
+            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+         </archive>
         </configuration>
       </plugin>
     </plugins>
@@ -99,10 +92,15 @@
       <artifactId>jetty-jndi</artifactId>
       <version>${project.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-jsp</artifactId>
-      <version>${project.version}</version>
-    </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>apache-jsp</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>apache-jstl</artifactId>
+        <version>${project.version}</version>
+      </dependency>
   </dependencies>
 </project>
diff --git a/jetty-runner/src/main/java/org/eclipse/jetty/runner/Runner.java b/jetty-runner/src/main/java/org/eclipse/jetty/runner/Runner.java
index ee022a2..14122a8 100644
--- a/jetty-runner/src/main/java/org/eclipse/jetty/runner/Runner.java
+++ b/jetty-runner/src/main/java/org/eclipse/jetty/runner/Runner.java
@@ -27,13 +27,13 @@
 import java.util.List;
 import java.util.Locale;
 
+import org.eclipse.jetty.io.ConnectionStatistics;
 import org.eclipse.jetty.security.ConstraintMapping;
 import org.eclipse.jetty.security.ConstraintSecurityHandler;
 import org.eclipse.jetty.security.HashLoginService;
 import org.eclipse.jetty.security.authentication.BasicAuthenticator;
 import org.eclipse.jetty.server.AbstractConnector;
 import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.ConnectorStatistics;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.NCSARequestLog;
 import org.eclipse.jetty.server.Server;
@@ -50,19 +50,19 @@
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.servlet.StatisticsServlet;
 import org.eclipse.jetty.util.RolloverFileOutputStream;
+import org.eclipse.jetty.util.StringUtil;
 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.Constraint;
 import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.webapp.WebInfConfiguration;
 import org.eclipse.jetty.xml.XmlConfiguration;
 
-
 /**
  * Runner
- *
+ * <p>
  * Combine jetty classes into a single executable jar and run webapps based on the args to it.
- *
  */
 public class Runner
 {
@@ -92,12 +92,8 @@
     protected boolean _enableStats=false;
     protected String _statsPropFile;
 
-
-
     /**
      * Classpath
-     *
-     *
      */
     public class Classpath
     {
@@ -150,22 +146,14 @@
         }
     }
 
-
-
-
-    /**
-     *
-     */
     public Runner()
     {
-
     }
 
-
     /**
      * Generate helpful usage message and exit
      *
-     * @param error
+     * @param error the error header
      */
     public void usage(String error)
     {
@@ -200,13 +188,11 @@
         System.exit(1);
     }
 
-
-
     /**
      * Configure a jetty instance and deploy the webapps presented as args
      *
-     * @param args
-     * @throws Exception
+     * @param args the command line arguments
+     * @throws Exception if unable to configure
      */
     public void configure(String[] args) throws Exception
     {
@@ -260,7 +246,7 @@
 
         for (int i=0;i<args.length;i++)
         {
-            switch (args[i])
+            switch (args[i]) 
             {
                 case "--port":
                     port = Integer.parseInt(args[++i]);
@@ -322,9 +308,9 @@
                         }
 
                         //apply jetty config files if there are any
-                        if (_configFiles != null)
+                        if (_configFiles != null) 
                         {
-                            for (String cfg : _configFiles)
+                            for (String cfg : _configFiles) 
                             {
                                 try (Resource resource = Resource.newResource(cfg)) {
                                     XmlConfiguration xmlConfiguration = new XmlConfiguration(resource.getURL());
@@ -335,7 +321,7 @@
 
                         //check that everything got configured, and if not, make the handlers
                         HandlerCollection handlers = (HandlerCollection) _server.getChildHandlerByClass(HandlerCollection.class);
-                        if (handlers == null)
+                        if (handlers == null) 
                         {
                             handlers = new HandlerCollection();
                             _server.setHandler(handlers);
@@ -343,14 +329,14 @@
 
                         //check if contexts already configured
                         _contexts = (ContextHandlerCollection) handlers.getChildHandlerByClass(ContextHandlerCollection.class);
-                        if (_contexts == null)
+                        if (_contexts == null) 
                         {
                             _contexts = new ContextHandlerCollection();
                             prependHandler(_contexts, handlers);
                         }
 
 
-                        if (_enableStats)
+                        if (_enableStats) 
                         {
                             //if no stats handler already configured
                             if (handlers.getChildHandlerByClass(StatisticsHandler.class) == null) {
@@ -365,7 +351,7 @@
                                 ServletContextHandler statsContext = new ServletContextHandler(_contexts, "/stats");
                                 statsContext.addServlet(new ServletHolder(new StatisticsServlet()), "/");
                                 statsContext.setSessionHandler(new SessionHandler());
-                                if (_statsPropFile != null)
+                                if (_statsPropFile != null) 
                                 {
                                     HashLoginService loginService = new HashLoginService("StatsRealm", _statsPropFile);
                                     Constraint constraint = new Constraint();
@@ -387,14 +373,14 @@
                         }
 
                         //ensure a DefaultHandler is present
-                        if (handlers.getChildHandlerByClass(DefaultHandler.class) == null)
+                        if (handlers.getChildHandlerByClass(DefaultHandler.class) == null) 
                         {
                             handlers.addHandler(new DefaultHandler());
                         }
 
                         //ensure a log handler is present
                         _logHandler = (RequestLogHandler) handlers.getChildHandlerByClass(RequestLogHandler.class);
-                        if (_logHandler == null)
+                        if (_logHandler == null) 
                         {
                             _logHandler = new RequestLogHandler();
                             handlers.addHandler(_logHandler);
@@ -403,7 +389,7 @@
 
                         //check a connector is configured to listen on
                         Connector[] connectors = _server.getConnectors();
-                        if (connectors == null || connectors.length == 0)
+                        if (connectors == null || connectors.length == 0) 
                         {
                             ServerConnector connector = new ServerConnector(_server);
                             connector.setPort(port);
@@ -411,15 +397,15 @@
                                 connector.setHost(host);
                             _server.addConnector(connector);
                             if (_enableStats)
-                                connector.addBean(new ConnectorStatistics());
-                        }
-                        else
+                                connector.addBean(new ConnectionStatistics());
+                        } 
+                        else 
                         {
-                            if (_enableStats)
+                            if (_enableStats) 
                             {
                                 for (Connector connector : connectors)
                                 {
-                                    ((AbstractConnector) connector).addBean(new ConnectorStatistics());
+                                    ((AbstractConnector) connector).addBean(new ConnectionStatistics());
                                 }
                             }
                         }
@@ -428,7 +414,7 @@
                     }
 
                     // Create a context
-                    try (Resource ctx = Resource.newResource(args[i]))
+                    try (Resource ctx = Resource.newResource(args[i])) 
                     {
                         if (!ctx.exists())
                             usage("Context '" + ctx + "' does not exist");
@@ -446,14 +432,33 @@
                             if (contextPathSet)
                                 handler.setContextPath(contextPath);
                             _contexts.addHandler(handler);
-                            handler.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", __containerIncludeJarPattern);
+                            String containerIncludeJarPattern = (String)handler.getAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN);
+                            if (containerIncludeJarPattern == null)
+                                containerIncludeJarPattern = __containerIncludeJarPattern;
+                            else
+                            {
+                                if (!containerIncludeJarPattern.contains(__containerIncludeJarPattern))
+                                {
+                                    containerIncludeJarPattern = containerIncludeJarPattern+(StringUtil.isBlank(containerIncludeJarPattern)?"":"|")+ __containerIncludeJarPattern;
+                                }
+                            }
+
+                            handler.setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN, containerIncludeJarPattern);
+                            
+                            //check the configurations, if not explicitly set up, then configure all of them
+                            if (handler instanceof WebAppContext)
+                            {
+                                WebAppContext wac = (WebAppContext)handler;
+                                if (wac.getConfigurationClasses() == null || wac.getConfigurationClasses().length == 0)
+                                    wac.setConfigurationClasses(__plusConfigurationClasses);
+                            }
                         }
-                        else
+                        else 
                         {
                             // assume it is a WAR file
                             WebAppContext webapp = new WebAppContext(_contexts, ctx.toString(), contextPath);
                             webapp.setConfigurationClasses(__plusConfigurationClasses);
-                            webapp.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
+                            webapp.setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN,
                                     __containerIncludeJarPattern);
                         }
                     }
@@ -493,12 +498,9 @@
             _logHandler.setRequestLog(requestLog);
         }
     }
+    
 
 
-    /**
-     * @param handler
-     * @param handlers
-     */
     protected void prependHandler (Handler handler, HandlerCollection handlers)
     {
         if (handler == null || handlers == null)
@@ -511,19 +513,12 @@
        handlers.setHandlers(children);
     }
 
-
-
-
-    /**
-     * @throws Exception
-     */
     public void run() throws Exception
     {
         _server.start();
         _server.join();
     }
 
-
     /**
      * Establish a classloader with custom paths (if any)
      */
@@ -544,12 +539,6 @@
         }
     }
 
-
-
-
-    /**
-     * @param args
-     */
     public static void main(String[] args)
     {
         Runner runner = new Runner();
diff --git a/jetty-security/pom.xml b/jetty-security/pom.xml
index f2bbb9b..22787f9 100644
--- a/jetty-security/pom.xml
+++ b/jetty-security/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-security</artifactId>
@@ -25,7 +25,7 @@
             </goals>
             <configuration>
               <instructions>
-                <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",javax.security.cert,*</Import-Package>
+                <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",javax.security.cert,org.eclipse.jetty*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",*</Import-Package>
               </instructions>
             </configuration>
 
@@ -33,35 +33,6 @@
         </executions>
       </plugin>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-security/src/main/config/etc/krb5.ini b/jetty-security/src/main/config/etc/krb5.ini
index 9cea63c..283880c 100644
--- a/jetty-security/src/main/config/etc/krb5.ini
+++ b/jetty-security/src/main/config/etc/krb5.ini
@@ -9,9 +9,9 @@
 
 [realms]
 MORTBAY.ORG = {
- 		kdc = 192.168.2.30
- 		admin_server = 192.168.2.30
- 		default_domain = MORTBAY.ORG
+    kdc = 192.168.2.30
+    admin_server = 192.168.2.30
+    default_domain = MORTBAY.ORG
 }
 
 [domain_realm]
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/Authenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/Authenticator.java
index 78c1990..722ad68 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/Authenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/Authenticator.java
@@ -43,7 +43,8 @@
     /* ------------------------------------------------------------ */
     /**
      * Configure the Authenticator
-     * @param configuration
+     * 
+     * @param configuration the configuration
      */
     void setConfiguration(AuthConfiguration configuration);
 
@@ -64,13 +65,16 @@
      * where the http method of the original request causing authentication
      * is not the same as the http method resulting from the redirect
      * after authentication.
-     * @param request
+     * 
+     * @param request the request to manipulate
      */
     void prepareRequest(ServletRequest request);
     
 
     /* ------------------------------------------------------------ */
-    /** Validate a request
+    /** 
+     * Validate a request
+     * 
      * @param request The request
      * @param response The response
      * @param mandatory True if authentication is mandatory.
@@ -79,18 +83,20 @@
      * implement {@link org.eclipse.jetty.server.Authentication.ResponseSent}.  If Authentication is not manditory, then a
      * {@link org.eclipse.jetty.server.Authentication.Deferred} may be returned.
      *
-     * @throws ServerAuthException
+     * @throws ServerAuthException if unable to validate request
      */
     Authentication validateRequest(ServletRequest request, ServletResponse response, boolean mandatory) throws ServerAuthException;
 
     /* ------------------------------------------------------------ */
     /**
-     * @param request
-     * @param response
-     * @param mandatory
-     * @param validatedUser
+     * is response secure
+     * 
+     * @param request the request 
+     * @param response the response
+     * @param mandatory if security is mandator
+     * @param validatedUser the user that was validated
      * @return true if response is secure
-     * @throws ServerAuthException
+     * @throws ServerAuthException if unable to test response
      */
     boolean secureResponse(ServletRequest request, ServletResponse response, boolean mandatory, User validatedUser) throws ServerAuthException;
 
@@ -106,7 +112,8 @@
         String getAuthMethod();
         String getRealmName();
 
-        /** Get a SecurityHandler init parameter
+        /** 
+         * Get a SecurityHandler init parameter
          * @see SecurityHandler#getInitParameter(String)
          * @param param parameter name
          * @return Parameter value or null
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintAware.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintAware.java
index 8fd942d..2b3389c 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintAware.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintAware.java
@@ -21,9 +21,6 @@
 import java.util.List;
 import java.util.Set;
 
-/**
- * @version $Rev: 4466 $ $Date: 2009-02-10 23:42:54 +0100 (Tue, 10 Feb 2009) $
- */
 public interface ConstraintAware
 {
     List<ConstraintMapping> getConstraintMappings();
@@ -32,15 +29,15 @@
     /* ------------------------------------------------------------ */
     /** Set Constraint Mappings and roles.
      * Can only be called during initialization.
-     * @param constraintMappings
-     * @param roles
+     * @param constraintMappings the mappings
+     * @param roles the roles
      */
     void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles);
     
     /* ------------------------------------------------------------ */
     /** Add a Constraint Mapping.
      * May be called for running webapplication as an annotated servlet is instantiated.
-     * @param mapping
+     * @param mapping the mapping
      */
     void addConstraintMapping(ConstraintMapping mapping);
     
@@ -48,7 +45,7 @@
     /* ------------------------------------------------------------ */
     /** Add a Role definition.
      * May be called on running webapplication as an annotated servlet is instantiated.
-     * @param role
+     * @param role the role
      */
     void addRole(String role);
     
@@ -56,7 +53,7 @@
      * See Servlet Spec 31, sec 13.8.4, pg 145
      * When true, requests with http methods not explicitly covered either by inclusion or omissions
      * in constraints, will have access denied.
-     * @param deny
+     * @param deny true for denied method access
      */
     void setDenyUncoveredHttpMethods(boolean deny);
     
@@ -65,6 +62,7 @@
     /**
      * See Servlet Spec 31, sec 13.8.4, pg 145
      * Container must check if there are urls with uncovered http methods
+     * @return true if urls with uncovered http methods
      */
     boolean checkPathsWithUncoveredHttpMethods();
 }
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
index a0d9f7e..88bdcd0 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
@@ -40,7 +40,6 @@
 
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.http.PathMap;
-import org.eclipse.jetty.server.HttpChannel;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Response;
@@ -51,14 +50,12 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.security.Constraint;
 
-/* ------------------------------------------------------------ */
 /**
  * ConstraintSecurityHandler
- * 
+ * <p> 
  * Handler to enforce SecurityConstraints. This implementation is servlet spec
  * 3.1 compliant and pre-computes the constraint combinations for runtime
  * efficiency.
- *
  */
 public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware
 {
@@ -79,9 +76,6 @@
     }
     
     /* ------------------------------------------------------------ */
-    /**
-     * @param constraint
-     */
     public static Constraint createConstraint(Constraint constraint)
     {
         try
@@ -98,10 +92,11 @@
     /**
      * Create a security constraint
      * 
-     * @param name
-     * @param authenticate
-     * @param roles
-     * @param dataConstraint
+     * @param name the name of the constraint
+     * @param authenticate true to authenticate
+     * @param roles list of roles
+     * @param dataConstraint the data constraint
+     * @return the constraint
      */
     public static Constraint createConstraint (String name, boolean authenticate, String[] roles, int dataConstraint)
     {
@@ -117,8 +112,11 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @param name
-     * @param element
+     * Create a Constraint
+     * 
+     * @param name the name
+     * @param element the http constraint element
+     * @return the created constraint
      */
     public static Constraint createConstraint (String name, HttpConstraintElement element)
     {
@@ -128,10 +126,13 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @param name
-     * @param rolesAllowed
-     * @param permitOrDeny
-     * @param transport
+     * Create Constraint
+     * 
+     * @param name the name
+     * @param rolesAllowed the list of allowed roles
+     * @param permitOrDeny the permission semantic
+     * @param transport the transport guarantee
+     * @return the created constraint
      */
     public static Constraint createConstraint (String name, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
     {
@@ -168,10 +169,6 @@
     
 
     /* ------------------------------------------------------------ */
-    /**
-     * @param pathSpec
-     * @param constraintMappings
-     */
     public static List<ConstraintMapping> getConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
     {
         if (pathSpec == null || "".equals(pathSpec.trim()) || constraintMappings == null || constraintMappings.size() == 0)
@@ -193,8 +190,9 @@
     /** Take out of the constraint mappings those that match the 
      * given path.
      * 
-     * @param pathSpec
+     * @param pathSpec the path spec
      * @param constraintMappings a new list minus the matching constraints
+     * @return the list of constraint mappings
      */
     public static List<ConstraintMapping> removeConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
     {
@@ -216,12 +214,13 @@
     
     
     /* ------------------------------------------------------------ */
-    /** Generate Constraints and ContraintMappings for the given url pattern and ServletSecurityElement
+    /** 
+     * Generate Constraints and ContraintMappings for the given url pattern and ServletSecurityElement
      * 
-     * @param name
-     * @param pathSpec
-     * @param securityElement
-     * @return
+     * @param name the name
+     * @param pathSpec the path spec
+     * @param securityElement the servlet security element
+     * @return the list of constraint mappings
      */
     public static List<ConstraintMapping> createConstraintsWithMappingsForPath (String name, String pathSpec, ServletSecurityElement securityElement)
     {
@@ -464,7 +463,7 @@
      * Create and combine the constraint with the existing processed
      * constraints.
      * 
-     * @param mapping
+     * @param mapping the constraint mapping
      */
     protected void processConstraintMapping(ConstraintMapping mapping)
     {
@@ -522,8 +521,8 @@
      * the mappings: an entry that names the method of the Request specifically, an
      * entry that names constraints that apply to all methods, entries of the form
      * &lt;method&gt;.omission, where the method of the Request is not named in the omission.
-     * @param mapping
-     * @param mappings
+     * @param mapping the constraint mapping
+     * @param mappings the mappings of roles
      */
     protected void processConstraintMappingWithMethodOmissions (ConstraintMapping mapping, Map<String, RoleInfo> mappings)
     {
@@ -545,8 +544,8 @@
     /* ------------------------------------------------------------ */
     /**
      * Initialize or update the RoleInfo from the constraint
-     * @param ri
-     * @param mapping
+     * @param ri the role info
+     * @param mapping the constraint mapping
      */
     protected void configureRoleInfo (RoleInfo ri, ConstraintMapping mapping)
     { 
@@ -675,7 +674,7 @@
         if (dataConstraint == null || dataConstraint == UserDataConstraint.None)
             return true;
 
-        HttpConfiguration httpConfig = HttpChannel.getCurrentHttpChannel().getHttpConfiguration();
+        HttpConfiguration httpConfig = Request.getBaseRequest(request).getHttpChannel().getHttpConfiguration();
 
         if (dataConstraint == UserDataConstraint.Confidential || dataConstraint == UserDataConstraint.Integral)
         {
@@ -871,9 +870,9 @@
      * Check if any http method omissions exist in the list of method
      * to auth info mappings.
      * 
-     * @param path
-     * @param methodMappings
-     * @return
+     * @param path the path
+     * @param methodMappings the method mappings
+     * @return true if ommision exist
      */
     protected boolean omissionsExist (String path, Map<String, RoleInfo> methodMappings)
     {
@@ -891,11 +890,11 @@
     
     /* ------------------------------------------------------------ */
     /**
-     * Given a string of the form &lt;method&gt;.&lt;method&gt;.omission
+     * Given a string of the form <code>&lt;method&gt;.&lt;method&gt;.omission</code>
      * split out the individual method names.
      * 
-     * @param omission
-     * @return
+     * @param omission the method
+     * @return the list of strings
      */
     protected Set<String> getOmittedMethods (String omission)
     {
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/CrossContextPsuedoSession.java b/jetty-security/src/main/java/org/eclipse/jetty/security/CrossContextPsuedoSession.java
deleted file mode 100644
index b1e063c..0000000
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/CrossContextPsuedoSession.java
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.security;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * @version $Rev: 4466 $ $Date: 2009-02-10 23:42:54 +0100 (Tue, 10 Feb 2009) $
- * @deprecated
- */
-public interface CrossContextPsuedoSession<T>
-{
-
-    T fetch(HttpServletRequest request);
-
-    void store(T data, HttpServletResponse response);
-
-    void clear(HttpServletRequest request);
-
-}
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/HashCrossContextPsuedoSession.java b/jetty-security/src/main/java/org/eclipse/jetty/security/HashCrossContextPsuedoSession.java
deleted file mode 100644
index b2039d7..0000000
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/HashCrossContextPsuedoSession.java
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.security;
-
-import java.security.SecureRandom;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Random;
-
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * @version $Rev: 4660 $ $Date: 2009-02-25 17:29:53 +0100 (Wed, 25 Feb 2009) $
- * @deprecated
- */
-public class HashCrossContextPsuedoSession<T> implements CrossContextPsuedoSession<T>
-{
-    private final String _cookieName;
-
-    private final String _cookiePath;
-
-    private final Random _random = new SecureRandom();
-
-    private final Map<String, T> _data = new HashMap<String, T>();
-
-    public HashCrossContextPsuedoSession(String cookieName, String cookiePath)
-    {
-        this._cookieName = cookieName;
-        this._cookiePath = cookiePath == null ? "/" : cookiePath;
-    }
-
-    public T fetch(HttpServletRequest request)
-    {
-        Cookie[] cookies = request.getCookies();
-        if (cookies == null)
-            return null;
-        
-        for (Cookie cookie : cookies)
-        {
-            if (_cookieName.equals(cookie.getName()))
-            {
-                String key = cookie.getValue();
-                return _data.get(key);
-            }
-        }
-        return null;
-    }
-
-    public void store(T datum, HttpServletResponse response)
-    {
-        String key;
-
-        synchronized (_data)
-        {
-            // Create new ID
-            while (true)
-            {
-                key = Long.toString(Math.abs(_random.nextLong()), 30 + (int) (System.currentTimeMillis() % 7));
-                if (!_data.containsKey(key)) break;
-            }
-
-            _data.put(key, datum);
-        }
-
-        Cookie cookie = new Cookie(_cookieName, key);
-        cookie.setPath(_cookiePath);
-        response.addCookie(cookie);
-    }
-
-    public void clear(HttpServletRequest request)
-    {
-        for (Cookie cookie : request.getCookies())
-        {
-            if (_cookieName.equals(cookie.getName()))
-            {
-                String key = cookie.getValue();
-                _data.remove(key);
-                break;
-            }
-        }
-    }
-}
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java
index 29d7dd5..64fbde3 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java
@@ -18,11 +18,14 @@
 
 package org.eclipse.jetty.security;
 
+import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
 
 import org.eclipse.jetty.security.PropertyUserStore.UserListener;
 import org.eclipse.jetty.server.UserIdentity;
-import org.eclipse.jetty.util.Scanner;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.resource.Resource;
@@ -31,18 +34,18 @@
 /* ------------------------------------------------------------ */
 /**
  * Properties User Realm.
- * 
+ * <p>
  * An implementation of UserRealm that stores users and roles in-memory in HashMaps.
- * <P>
+ * <p>
  * Typically these maps are populated by calling the load() method or passing a properties resource to the constructor. The format of the properties file is:
  * 
- * <PRE>
+ * <pre>
  *  username: password [,rolename ...]
- * </PRE>
+ * </pre>
  * 
  * Passwords may be clear text, obfuscated or checksummed. The class com.eclipse.Util.Password should be used to generate obfuscated passwords or password
  * checksums.
- * 
+ * <p>
  * If DIGEST Authentication is used, the password must be in a recoverable format, either plain text or OBF:.
  */
 public class HashLoginService extends MappedLoginService implements UserListener
@@ -51,8 +54,29 @@
 
     private PropertyUserStore _propertyUserStore;
     private String _config;
-    private Resource _configResource;
-    private int _refreshInterval = 0;// default is not to reload
+    private boolean hotReload = false; // default is not to reload
+    
+    
+    
+    public class HashKnownUser extends KnownUser
+    {
+        String[] _roles;
+        
+        public HashKnownUser(String name, Credential credential)
+        {
+            super(name, credential);
+        }
+        
+        public void setRoles (String[] roles)
+        {
+            _roles = roles;
+        }
+        
+        public String[] getRoles()
+        {
+            return _roles;
+        }
+    }
 
     /* ------------------------------------------------------------ */
     public HashLoginService()
@@ -78,40 +102,72 @@
         return _config;
     }
 
-    /* ------------------------------------------------------------ */
-    public void getConfig(String config)
-    {
-        _config = config;
-    }
 
     /* ------------------------------------------------------------ */
+    @Deprecated
     public Resource getConfigResource()
     {
-        return _configResource;
+        return null;
     }
 
     /* ------------------------------------------------------------ */
     /**
-     * Load realm users from properties file. The property file maps usernames to password specs followed by an optional comma separated list of role names.
+     * Load realm users from properties file.
+     * <p>
+     * The property file maps usernames to password specs followed by an optional comma separated list of role names.
+     * </p>
      * 
-     * @param config
-     *            Filename or url of user properties file.
+     * @param config uri or url or path to realm properties file
      */
     public void setConfig(String config)
     {
-        _config = config;
+        _config=config;
     }
-
-    /* ------------------------------------------------------------ */
-    public void setRefreshInterval(int msec)
+    
+    /**
+     * Is hot reload enabled on this user store
+     * 
+     * @return true if hot reload was enabled before startup
+     */
+    public boolean isHotReload()
     {
-        _refreshInterval = msec;
+        return hotReload;
+    }
+
+    /**
+     * Enable Hot Reload of the Property File
+     * 
+     * @param enable true to enable, false to disable
+     */
+    public void setHotReload(boolean enable)
+    {
+        if (isRunning())
+        {
+            throw new IllegalStateException("Cannot set hot reload while user store is running");
+        }
+        this.hotReload = enable;
     }
 
     /* ------------------------------------------------------------ */
+    /**
+     * sets the refresh interval (in seconds)
+     * @param sec the refresh interval
+     * @deprecated use {@link #setHotReload(boolean)} instead
+     */
+    @Deprecated
+    public void setRefreshInterval(int sec)
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return refresh interval in seconds for how often the properties file should be checked for changes
+     * @deprecated use {@link #isHotReload()} instead
+     */
+    @Deprecated
     public int getRefreshInterval()
     {
-        return _refreshInterval;
+        return (hotReload)?1:0;
     }
 
     /* ------------------------------------------------------------ */
@@ -128,6 +184,41 @@
         // TODO: Consider refactoring MappedLoginService to not have to override with unused methods
     }
 
+
+
+    @Override
+    protected String[] loadRoleInfo(KnownUser user)
+    {
+        UserIdentity id = _propertyUserStore.getUserIdentity(user.getName());
+        if (id == null)
+            return null;
+
+
+        Set<RolePrincipal> roles = id.getSubject().getPrincipals(RolePrincipal.class);
+        if (roles == null)
+            return null;
+
+        List<String> list = new ArrayList<>();
+        for (RolePrincipal r:roles)
+            list.add(r.getName());
+
+        return list.toArray(new String[roles.size()]);
+    }
+
+    @Override
+    protected KnownUser loadUserInfo(String userName)
+    {
+        UserIdentity id = _propertyUserStore.getUserIdentity(userName);
+        if (id != null)
+        {
+            return (KnownUser)id.getUserPrincipal();
+        }
+        
+        return null;
+    }
+    
+    
+
     /* ------------------------------------------------------------ */
     /**
      * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
@@ -140,11 +231,10 @@
         if (_propertyUserStore == null)
         {
             if(LOG.isDebugEnabled())
-                LOG.debug("doStart: Starting new PropertyUserStore. PropertiesFile: " + _config + " refreshInterval: " + _refreshInterval);
-            
+                LOG.debug("doStart: Starting new PropertyUserStore. PropertiesFile: " + _config + " hotReload: " + hotReload);
             _propertyUserStore = new PropertyUserStore();
-            _propertyUserStore.setRefreshInterval(_refreshInterval);
-            _propertyUserStore.setConfig(_config);
+            _propertyUserStore.setHotReload(hotReload);
+            _propertyUserStore.setConfigPath(_config);
             _propertyUserStore.registerUserListener(this);
             _propertyUserStore.start();
         }
@@ -169,9 +259,11 @@
     {
         if (LOG.isDebugEnabled())
             LOG.debug("update: " + userName + " Roles: " + roleArray.length);
-        putUser(userName,credential,roleArray);
+       //TODO need to remove and replace the authenticated user?
     }
 
+    
+    
     /* ------------------------------------------------------------ */
     @Override
     public void remove(String userName)
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/IdentityService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/IdentityService.java
index fb53bb6..9cccd0a 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/IdentityService.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/IdentityService.java
@@ -24,10 +24,8 @@
 
 import org.eclipse.jetty.server.UserIdentity;
 
-/* ------------------------------------------------------------ */
 /**
  * Associates UserIdentities from with threads and UserIdentity.Contexts.
- *
  */
 public interface IdentityService
 {
@@ -37,7 +35,7 @@
     /**
      * Associate a user identity with the current thread.
      * This is called with as a thread enters the
-     * {@link SecurityHandler#handle(String, Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
+     * {@link SecurityHandler#handle(String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
      * method and then again with a null argument as that call exits.
      * @param user The current user or null for no user to associated.
      * @return an object representing the previous associated state
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java
index 3981e92..9d41276 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java
@@ -29,6 +29,8 @@
 import java.util.List;
 import java.util.Properties;
 
+import javax.servlet.ServletRequest;
+
 import org.eclipse.jetty.server.UserIdentity;
 import org.eclipse.jetty.util.Loader;
 import org.eclipse.jetty.util.log.Log;
@@ -39,24 +41,17 @@
 /* ------------------------------------------------------------ */
 /**
  * HashMapped User Realm with JDBC as data source. 
- * The login() method checks the inherited Map for the user. If the user is not
+ * The {@link #login(String, Object, ServletRequest)} method checks the inherited Map for the user. If the user is not
  * found, it will fetch details from the database and populate the inherited
- * Map. It then calls the superclass login() method to perform the actual
+ * Map. It then calls the superclass {@link #login(String, Object, ServletRequest)} method to perform the actual
  * authentication. Periodically (controlled by configuration parameter),
  * internal hashes are cleared. Caching can be disabled by setting cache refresh
  * interval to zero. Uses one database connection that is initialized at
- * startup. Reconnect on failures. authenticate() is 'synchronized'.
- * 
+ * startup. Reconnect on failures.
+ * <p> 
  * An example properties file for configuration is in
- * $JETTY_HOME/etc/jdbcRealm.properties
- * 
- * @version $Id: JDBCLoginService.java 4792 2009-03-18 21:55:52Z gregw $
- * 
- * 
- * 
- * 
+ * <code>${jetty.home}/etc/jdbcRealm.properties</code>
  */
-
 public class JDBCLoginService extends MappedLoginService
 {
     private static final Logger LOG = Log.getLogger(JDBCLoginService.class);
@@ -75,6 +70,26 @@
     protected String _userSql;
     protected String _roleSql;
 
+    
+    /**
+     * JDBCKnownUser
+     */
+    public class JDBCKnownUser extends KnownUser
+    {
+        int _userKey;
+        
+        public JDBCKnownUser(String name, Credential credential, int key)
+        {
+            super(name, credential);
+            _userKey = key;
+        }
+        
+        
+        public int getUserKey ()
+        {
+            return _userKey;
+        }
+    }
 
     /* ------------------------------------------------------------ */
     public JDBCLoginService()
@@ -209,7 +224,7 @@
 
     /* ------------------------------------------------------------ */
     @Override
-    public UserIdentity login(String username, Object credentials)
+    public UserIdentity login(String username, Object credentials, ServletRequest request)
     {
         long now = System.currentTimeMillis();
         if (now - _lastHashPurge > _cacheTime || _cacheTime == 0)
@@ -219,7 +234,7 @@
             closeConnection();
         }
         
-        return super.login(username,credentials);
+        return super.login(username,credentials, request);
     }
 
     /* ------------------------------------------------------------ */
@@ -229,7 +244,7 @@
     }
     
     /* ------------------------------------------------------------ */
-    @Override
+    @Deprecated
     protected UserIdentity loadUser(String username)
     {
         try
@@ -249,6 +264,8 @@
                     {
                         int key = rs1.getInt(_userTableKey);
                         String credentials = rs1.getString(_userTablePasswordField);
+
+
                         List<String> roles = new ArrayList<String>();
 
                         try (PreparedStatement stat2 = _con.prepareStatement(_roleSql))
@@ -260,7 +277,7 @@
                                     roles.add(rs2.getString(_roleTableRoleField));
                             }
                         }
-                        return putUser(username, credentials, roles.toArray(new String[roles.size()]));
+                        return putUser(username, Credential.getCredential(credentials), roles.toArray(new String[roles.size()]));
                     }
                 }
             }
@@ -272,11 +289,83 @@
         }
         return null;
     }
-    
-    /* ------------------------------------------------------------ */
-    protected UserIdentity putUser (String username, String credentials, String[] roles)
+
+
+    /** 
+     * @see org.eclipse.jetty.security.MappedLoginService#loadUserInfo(java.lang.String)
+     */
+    public KnownUser loadUserInfo (String username)
     {
-        return putUser(username, Credential.getCredential(credentials),roles);
+        try
+        {
+            if (null == _con) 
+                connectDatabase();
+
+            if (null == _con) 
+                throw new SQLException("Can't connect to database");
+
+            try (PreparedStatement stat1 = _con.prepareStatement(_userSql))
+            {
+                stat1.setObject(1, username);
+                try (ResultSet rs1 = stat1.executeQuery())
+                {
+                    if (rs1.next())
+                    {
+                        int key = rs1.getInt(_userTableKey);
+                        String credentials = rs1.getString(_userTablePasswordField);
+
+                        return new JDBCKnownUser (username, Credential.getCredential(credentials), key);
+                    }
+                }
+            }
+        }
+        catch (SQLException e)
+        {
+            LOG.warn("UserRealm " + getName() + " could not load user information from database", e);
+            closeConnection();
+        }
+        
+        return null;
+    }
+
+    
+    
+    /** 
+     * @see org.eclipse.jetty.security.MappedLoginService#loadRoleInfo(org.eclipse.jetty.security.MappedLoginService.KnownUser)
+     */
+    public String[] loadRoleInfo (KnownUser user)
+    {
+        JDBCKnownUser jdbcUser = (JDBCKnownUser)user;
+        
+        try
+        {
+            if (null == _con) 
+                connectDatabase();
+
+            if (null == _con) 
+                throw new SQLException("Can't connect to database");
+            
+            
+            List<String> roles = new ArrayList<String>();
+
+            try (PreparedStatement stat2 = _con.prepareStatement(_roleSql))
+            {
+                stat2.setInt(1, jdbcUser.getUserKey());
+                try (ResultSet rs2 = stat2.executeQuery())
+                {
+                    while (rs2.next())
+                        roles.add(rs2.getString(_roleTableRoleField));
+                    return roles.toArray(new String[roles.size()]);
+                }
+            }
+        }
+        catch (SQLException e)
+        {
+            LOG.warn("UserRealm " + getName() + " could not load user information from database", e);
+            closeConnection();
+        }
+        
+        return null;
     }
     
 
@@ -292,5 +381,4 @@
         }
         _con = null;
     }
-
 }
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/LoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/LoginService.java
index 281fed2..4b719cd 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/LoginService.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/LoginService.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.security;
 
+import javax.servlet.ServletRequest;
+
 import org.eclipse.jetty.server.UserIdentity;
 
 
@@ -42,14 +44,15 @@
     /** Login a user.
      * @param username The user name
      * @param credentials The users credentials
+     * @param request TODO
      * @return A UserIdentity if the credentials matched, otherwise null
      */
-    UserIdentity login(String username,Object credentials);
+    UserIdentity login(String username,Object credentials, ServletRequest request);
     
     /* ------------------------------------------------------------ */
     /** Validate a user identity.
      * Validate that a UserIdentity previously created by a call 
-     * to {@link #login(String, Object)} is still valid.
+     * to {@link #login(String, Object, ServletRequest)} is still valid.
      * @param user The user to validate
      * @return true if authentication has not been revoked for the user.
      */
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java
index 616b20a..9008fc9 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java
@@ -27,6 +27,7 @@
 import java.util.concurrent.ConcurrentMap;
 
 import javax.security.auth.Subject;
+import javax.servlet.ServletRequest;
 
 import org.eclipse.jetty.server.UserIdentity;
 import org.eclipse.jetty.util.component.AbstractLifeCycle;
@@ -138,6 +139,8 @@
     public void logout(UserIdentity identity)
     {
         LOG.debug("logout {}",identity);
+        
+        //TODO should remove the user?????
     }
 
     /* ------------------------------------------------------------ */
@@ -199,6 +202,24 @@
         _users.put(userName,identity);
         return identity;
     }
+    
+    
+
+    
+    public synchronized UserIdentity putUser (KnownUser userPrincipal, String[] roles)
+    {
+        Subject subject = new Subject();
+        subject.getPrincipals().add(userPrincipal);
+        subject.getPrivateCredentials().add(userPrincipal._credential);
+        if (roles!=null)
+            for (String role : roles)
+                subject.getPrincipals().add(new RolePrincipal(role));
+        subject.setReadOnly();
+        UserIdentity identity=_identityService.newUserIdentity(subject,userPrincipal,roles);
+        _users.put(userPrincipal._name,identity);
+        return identity;
+    }
+    
 
     /* ------------------------------------------------------------ */
     public void removeUser(String username)
@@ -208,9 +229,9 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @see org.eclipse.jetty.security.LoginService#login(java.lang.String, java.lang.Object)
+     * @see org.eclipse.jetty.security.LoginService#login(java.lang.String, java.lang.Object, ServletRequest)
      */
-    public UserIdentity login(String username, Object credentials)
+    public UserIdentity login(String username, Object credentials, ServletRequest request)
     {
         if (username == null)
             return null;
@@ -218,9 +239,17 @@
         UserIdentity user = _users.get(username);
 
         if (user==null)
-            user = loadUser(username);
-
-        if (user!=null)
+        {
+            KnownUser userPrincipal = loadUserInfo(username);
+            if (userPrincipal != null && userPrincipal.authenticate(credentials))
+            {
+                //safe to load the roles
+                String[] roles = loadRoleInfo(userPrincipal);
+                user = putUser(userPrincipal, roles);
+                return user;
+            }
+        }
+        else
         {
             UserPrincipal principal = (UserPrincipal)user.getUserPrincipal();
             if (principal.authenticate(credentials))
@@ -240,7 +269,10 @@
 
         return false;
     }
-
+    /* ------------------------------------------------------------ */
+    protected abstract String[] loadRoleInfo (KnownUser user);
+    /* ------------------------------------------------------------ */
+    protected abstract KnownUser loadUserInfo (String username);
     /* ------------------------------------------------------------ */
     protected abstract UserIdentity loadUser(String username);
 
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java b/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java
index d16df90..64c5d63 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java
@@ -19,8 +19,9 @@
 package org.eclipse.jetty.security;
 
 import java.io.File;
-import java.io.FilenameFilter;
 import java.io.IOException;
+import java.net.MalformedURLException;
+import java.nio.file.Path;
 import java.security.Principal;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -36,37 +37,39 @@
 import org.eclipse.jetty.security.MappedLoginService.KnownUser;
 import org.eclipse.jetty.security.MappedLoginService.RolePrincipal;
 import org.eclipse.jetty.server.UserIdentity;
-import org.eclipse.jetty.util.Scanner;
-import org.eclipse.jetty.util.Scanner.BulkListener;
+import org.eclipse.jetty.util.PathWatcher;
+import org.eclipse.jetty.util.PathWatcher.PathWatchEvent;
 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;
+import org.eclipse.jetty.util.resource.PathResource;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.util.security.Credential;
 
 /**
  * PropertyUserStore
- *
+ * <p>
  * This class monitors a property file of the format mentioned below and notifies registered listeners of the changes to the the given file.
  *
- * <PRE>
+ * <pre>
  *  username: password [,rolename ...]
- * </PRE>
+ * </pre>
  *
  * Passwords may be clear text, obfuscated or checksummed. The class com.eclipse.Util.Password should be used to generate obfuscated passwords or password
  * checksums.
  *
  * If DIGEST Authentication is used, the password must be in a recoverable format, either plain text or OBF:.
  */
-public class PropertyUserStore extends AbstractLifeCycle
+public class PropertyUserStore extends AbstractLifeCycle implements PathWatcher.Listener
 {
     private static final Logger LOG = Log.getLogger(PropertyUserStore.class);
 
-    private String _config;
+    private Path _configPath;
     private Resource _configResource;
-    private Scanner _scanner;
-    private int _refreshInterval = 0;// default is not to reload
+    
+    private PathWatcher pathWatcher;
+    private boolean hotReload = false; // default is not to reload
 
     private IdentityService _identityService = new DefaultIdentityService();
     private boolean _firstLoad = true; // true if first load, false from that point on
@@ -74,67 +77,183 @@
     private final Map<String, UserIdentity> _knownUserIdentities = new HashMap<String, UserIdentity>();
     private List<UserListener> _listeners;
 
-    /* ------------------------------------------------------------ */
+    /**
+     * Get the config (as a string)
+     * @return the config path as a string
+     * @deprecated use {@link #getConfigPath()} instead
+     */
+    @Deprecated
     public String getConfig()
     {
-        return _config;
+        if (_configPath != null)
+            return _configPath.toString();
+        return null;
     }
 
-    /* ------------------------------------------------------------ */
+    /**
+     * Set the Config Path from a String reference to a file
+     * @param config the config file
+     */
     public void setConfig(String config)
     {
-        _config = config;
+        try
+        {
+            Resource configResource = Resource.newResource(config);
+            if (configResource.getFile() != null)
+                setConfigPath(configResource.getFile());
+            else
+                throw new IllegalArgumentException(config+" is not a file");
+        }
+        catch (Exception e)
+        {
+            throw new IllegalStateException(e);
+        }
+
+    }
+    
+    /**
+     * Get the Config {@link Path} reference.
+     * @return the config path
+     */
+    public Path getConfigPath()
+    {
+        return _configPath;
     }
 
-    /* ------------------------------------------------------------ */
-        public UserIdentity getUserIdentity(String userName)
+    /**
+     * Set the Config Path from a String reference to a file
+     * @param configFile the config file
+     */
+    public void setConfigPath(String configFile)
+    {
+        if (configFile == null)
         {
-            return _knownUserIdentities.get(userName);
+            _configPath = null;
         }
+        else
+        {
+            _configPath = new File(configFile).toPath();
+        }
+    }
+
+    /**
+     * Set the Config Path from a {@link File} reference
+     * @param configFile the config file
+     */
+    public void setConfigPath(File configFile)
+    {
+        if(configFile == null)
+        {
+            _configPath = null;
+            return;
+        }
+        
+        _configPath = configFile.toPath();
+    }
+
+    /**
+     * Set the Config Path
+     * @param configPath the config path
+     */
+    public void setConfigPath(Path configPath)
+    {
+        _configPath = configPath;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public UserIdentity getUserIdentity(String userName)
+    {
+        return _knownUserIdentities.get(userName);
+    }
 
     /* ------------------------------------------------------------ */
     /**
-     * returns the resource associated with the configured properties file, creating it if necessary
+     * @return the resource associated with the configured properties file, creating it if necessary
+     * @throws IOException if unable to get the resource
      */
     public Resource getConfigResource() throws IOException
     {
         if (_configResource == null)
         {
-            _configResource = Resource.newResource(_config);
+            _configResource = new PathResource(_configPath);
         }
 
         return _configResource;
     }
+    
+    /**
+     * Is hot reload enabled on this user store
+     * 
+     * @return true if hot reload was enabled before startup
+     */
+    public boolean isHotReload()
+    {
+        return hotReload;
+    }
+
+    /**
+     * Enable Hot Reload of the Property File
+     * 
+     * @param enable true to enable, false to disable
+     */
+    public void setHotReload(boolean enable)
+    {
+        if (isRunning())
+        {
+            throw new IllegalStateException("Cannot set hot reload while user store is running");
+        }
+        this.hotReload = enable;
+    }
 
     /* ------------------------------------------------------------ */
     /**
      * sets the refresh interval (in seconds)
+     * @param sec the refresh interval
+     * @deprecated use {@link #setHotReload(boolean)} instead
      */
-    public void setRefreshInterval(int msec)
+    @Deprecated
+    public void setRefreshInterval(int sec)
     {
-        _refreshInterval = msec;
     }
 
     /* ------------------------------------------------------------ */
     /**
-     * refresh interval in seconds for how often the properties file should be checked for changes
+     * @return refresh interval in seconds for how often the properties file should be checked for changes
+     * @deprecated use {@link #isHotReload()} instead
      */
+    @Deprecated
     public int getRefreshInterval()
     {
-        return _refreshInterval;
+        return (hotReload)?1:0;
+    }
+    
+    @Override
+    public String toString()
+    {
+        StringBuilder s = new StringBuilder();
+        s.append(this.getClass().getName());
+        s.append("[");
+        s.append("users.count=").append(this._knownUsers.size());
+        s.append("identityService=").append(this._identityService);
+        s.append("]");
+        return s.toString();
     }
 
     /* ------------------------------------------------------------ */
     private void loadUsers() throws IOException
     {
-        if (_config == null)
+        if (_configPath == null)
             return;
 
         if (LOG.isDebugEnabled())
-            LOG.debug("Load " + this + " from " + _config);
+        {
+            LOG.debug("Loading " + this + " from " + _configPath);
+        }
+        
         Properties properties = new Properties();
         if (getConfigResource().exists())
             properties.load(getConfigResource().getInputStream());
+        
         Set<String> known = new HashSet<String>();
 
         for (Map.Entry<Object, Object> entry : properties.entrySet())
@@ -211,8 +330,13 @@
          * set initial load to false as there should be no more initial loads
          */
         _firstLoad = false;
+        
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("Loaded " + this + " from " + _configPath);
+        }
     }
-
+    
     /* ------------------------------------------------------------ */
     /**
      * Depending on the value of the refresh interval, this method will either start up a scanner thread that will monitor the properties file for changes after
@@ -225,66 +349,29 @@
     {
         super.doStart();
 
-        if (getRefreshInterval() > 0)
+        loadUsers();
+        if ( isHotReload() && (_configPath != null) )
         {
-            _scanner = new Scanner();
-            _scanner.setScanInterval(getRefreshInterval());
-            List<File> dirList = new ArrayList<File>(1);
-            dirList.add(getConfigResource().getFile().getParentFile());
-            _scanner.setScanDirs(dirList);
-            _scanner.setFilenameFilter(new FilenameFilter()
-            {
-                public boolean accept(File dir, String name)
-                {
-                    File f = new File(dir,name);
-                    try
-                    {
-                        if (f.compareTo(getConfigResource().getFile()) == 0)
-                        {
-                            return true;
-                        }
-                    }
-                    catch (IOException e)
-                    {
-                        return false;
-                    }
-
-                    return false;
-                }
-
-            });
-
-            _scanner.addListener(new BulkListener()
-            {
-                public void filesChanged(List<String> filenames) throws Exception
-                {
-                    if (filenames == null)
-                        return;
-                    if (filenames.isEmpty())
-                        return;
-                    if (filenames.size() == 1)
-                    {
-                        Resource r = Resource.newResource(filenames.get(0));
-                        if (r.getFile().equals(_configResource.getFile()))
-                            loadUsers();
-                    }
-                }
-
-                public String toString()
-                {
-                    return "PropertyUserStore$Scanner";
-                }
-
-            });
-
-            _scanner.setReportExistingFilesOnStartup(true);
-            _scanner.setRecursive(false);
-            _scanner.start();
+            this.pathWatcher = new PathWatcher();
+            this.pathWatcher.watch(_configPath);
+            this.pathWatcher.addListener(this);
+            this.pathWatcher.setNotifyExistingOnStart(false);
+            this.pathWatcher.start();
         }
-        else
+       
+    }
+    
+    @Override
+    public void onPathWatchEvent(PathWatchEvent event)
+    {
+        try
         {
             loadUsers();
         }
+        catch (IOException e)
+        {
+            LOG.warn(e);
+        }
     }
 
     /* ------------------------------------------------------------ */
@@ -294,9 +381,9 @@
     protected void doStop() throws Exception
     {
         super.doStop();
-        if (_scanner != null)
-            _scanner.stop();
-        _scanner = null;
+        if (this.pathWatcher != null)
+            this.pathWatcher.stop();
+        this.pathWatcher = null;
     }
 
     /**
@@ -335,6 +422,7 @@
 
     /**
      * registers a listener to be notified of the contents of the property file
+     * @param listener the user listener
      */
     public void registerUserListener(UserListener listener)
     {
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java
index 4da0bfc..0d5925c 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java
@@ -29,25 +29,22 @@
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSessionEvent;
-import javax.servlet.http.HttpSessionListener;
 
 import org.eclipse.jetty.security.authentication.DeferredAuthentication;
 import org.eclipse.jetty.server.Authentication;
 import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.HttpChannel;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Response;
 import org.eclipse.jetty.server.UserIdentity;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.ContextHandler.Context;
 import org.eclipse.jetty.server.handler.HandlerWrapper;
-import org.eclipse.jetty.server.session.AbstractSession;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
 /**
  * Abstract SecurityHandler.
+ * <p>
  * Select and apply an {@link Authenticator} to a request.
  * <p>
  * The Authenticator may either be directly set on the handler
@@ -58,7 +55,6 @@
  * Authentication.Configuration. At startup, any context init parameters
  * that start with "org.eclipse.jetty.security." that do not have
  * values in the SecurityHandler init parameters, are copied.
- *
  */
 public abstract class SecurityHandler extends HandlerWrapper implements Authenticator.AuthConfiguration
 {
@@ -133,8 +129,9 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** Set the authenticator.
-     * @param authenticator
+    /** 
+     * Set the authenticator.
+     * @param authenticator the authenticator
      * @throws IllegalStateException if the SecurityHandler is running
      */
     public void setAuthenticator(Authenticator authenticator)
@@ -251,8 +248,8 @@
 
     /* ------------------------------------------------------------ */
     /** Set an initialization parameter.
-     * @param key
-     * @param value
+     * @param key the init key
+     * @param value the init value
      * @return previous value
      * @throws IllegalStateException if the SecurityHandler is running
      */
@@ -309,33 +306,6 @@
                         getInitParameter(name)==null)
                     setInitParameter(name,context.getInitParameter(name));
             }
-            
-            //register a session listener to handle securing sessions when authentication is performed
-            context.getContextHandler().addEventListener(new HttpSessionListener()
-            {
-                @Override
-                public void sessionDestroyed(HttpSessionEvent se)
-                {
-                }
-
-                @Override
-                public void sessionCreated(HttpSessionEvent se)
-                {                    
-                    //if current request is authenticated, then as we have just created the session, mark it as secure, as it has not yet been returned to a user
-                    HttpChannel<?> channel = HttpChannel.getCurrentHttpChannel();              
-                    
-                    if (channel == null)
-                        return;
-                    Request request = channel.getRequest();
-                    if (request == null)
-                        return;
-                    
-                    if (request.isSecure())
-                    {
-                        se.getSession().setAttribute(AbstractSession.SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
-                    }
-                }
-            });
         }
 
         // complicated resolution of login and identity service to handle
@@ -445,6 +415,7 @@
     /** Set renew the session on Authentication.
      * <p>
      * If set to true, then on authentication, the session associated with a reqeuest is invalidated and replaced with a new session.
+     * @param renew true to renew the authentication on session
      * @see org.eclipse.jetty.security.Authenticator.AuthConfiguration#isSessionRenewedOnAuthentication()
      */
     public void setSessionRenewedOnAuthentication(boolean renew)
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/SpnegoLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/SpnegoLoginService.java
index a6b1d3f..98bbcde 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/SpnegoLoginService.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/SpnegoLoginService.java
@@ -21,6 +21,7 @@
 import java.util.Properties;
 
 import javax.security.auth.Subject;
+import javax.servlet.ServletRequest;
 
 import org.eclipse.jetty.server.UserIdentity;
 import org.eclipse.jetty.util.B64Code;
@@ -112,7 +113,7 @@
      * username will be null since the credentials will contain all the relevant info
      */
     @Override
-    public UserIdentity login(String username, Object credentials)
+    public UserIdentity login(String username, Object credentials, ServletRequest request)
     {
         String encodedAuthToken = (String)credentials;
 
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java
index 6522cfa..ec7eeae 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java
@@ -36,14 +36,12 @@
 import org.eclipse.jetty.server.Authentication.User;
 import org.eclipse.jetty.server.UserIdentity;
 import org.eclipse.jetty.util.B64Code;
+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.Constraint;
 import org.eclipse.jetty.util.security.Password;
 
-/**
- * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $
- */
 public class ClientCertAuthenticator extends LoginAuthenticator
 {
     /** String name of keystore password property. */
@@ -82,12 +80,6 @@
         return Constraint.__CERT_AUTH;
     }
 
-    
-
-    /**
-     * @return Authentication for request
-     * @throws ServerAuthException
-     */
     @Override
     public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
     {
@@ -106,7 +98,7 @@
 
                 if (_validateCerts)
                 {
-                    KeyStore trustStore = getKeyStore(null,
+                    KeyStore trustStore = getKeyStore(
                             _trustStorePath, _trustStoreType, _trustStoreProvider,
                             _trustStorePassword == null ? null :_trustStorePassword.toString());
                     Collection<? extends CRL> crls = loadCRL(_crlPath);
@@ -147,6 +139,11 @@
         }
     }
 
+    @Deprecated
+    protected KeyStore getKeyStore(InputStream storeStream, String storePath, String storeType, String storeProvider, String storePassword) throws Exception
+    {
+        return getKeyStore(storePath, storeType, storeProvider, storePassword);
+    }
     /* ------------------------------------------------------------ */
     /**
      * Loads keystore using an input stream or a file path in the same
@@ -155,17 +152,16 @@
      * 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
+     * @throws Exception if unable to get keystore
      */
-    protected KeyStore getKeyStore(InputStream storeStream, String storePath, String storeType, String storeProvider, String storePassword) throws Exception
+    protected KeyStore getKeyStore(String storePath, String storeType, String storeProvider, String storePassword) throws Exception
     {
-        return CertificateUtils.getKeyStore(storeStream, storePath, storeType, storeProvider, storePassword);
+        return CertificateUtils.getKeyStore(Resource.newResource(storePath), storeType, storeProvider, storePassword);
     }
 
     /* ------------------------------------------------------------ */
@@ -178,7 +174,7 @@
      * @param crlPath path of certificate revocation list file
      * @return a (possibly empty) collection view of java.security.cert.CRL objects initialized with the data from the
      *         input stream.
-     * @throws Exception
+     * @throws Exception if unable to load CRL
      */
     protected Collection<? extends CRL> loadCRL(String crlPath) throws Exception
     {
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DeferredAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DeferredAuthentication.java
index 2a3555d..2c95459 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DeferredAuthentication.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DeferredAuthentication.java
@@ -140,7 +140,7 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @param response
+     * @param response the response
      * @return true if this response is from a deferred call to {@link #authenticate(ServletRequest)}
      */
     public static boolean isDeferred(HttpServletResponse response)
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java
index a982f56..e81544c 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java
@@ -23,6 +23,7 @@
 import java.security.MessageDigest;
 import java.security.SecureRandom;
 import java.util.BitSet;
+import java.util.Objects;
 import java.util.Queue;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -34,6 +35,7 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.security.SecurityHandler;
 import org.eclipse.jetty.security.ServerAuthException;
 import org.eclipse.jetty.security.UserAuthentication;
 import org.eclipse.jetty.server.Authentication;
@@ -49,8 +51,6 @@
 import org.eclipse.jetty.util.security.Credential;
 
 /**
- * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $
- *
  * The nonce max age in ms can be set with the {@link SecurityHandler#setInitParameter(String, String)}
  * using the name "maxNonceAge".  The nonce max count can be set with {@link SecurityHandler#setInitParameter(String, String)}
  * using the name "maxNonceCount".  When the age or count is exceeded, the nonce is considered stale.
@@ -58,105 +58,58 @@
 public class DigestAuthenticator extends LoginAuthenticator
 {
     private static final Logger LOG = Log.getLogger(DigestAuthenticator.class);
-    SecureRandom _random = new SecureRandom();
-    private long _maxNonceAgeMs = 60*1000;
-    private int _maxNC=1024;
-    private ConcurrentMap<String, Nonce> _nonceMap = new ConcurrentHashMap<String, Nonce>();
-    private Queue<Nonce> _nonceQueue = new ConcurrentLinkedQueue<Nonce>();
-    private static class Nonce
-    {
-        final String _nonce;
-        final long _ts;
-        final BitSet _seen; 
 
-        public Nonce(String nonce, long ts, int size)
-        {
-            _nonce=nonce;
-            _ts=ts;
-            _seen = new BitSet(size);
-        }
+    private final SecureRandom _random = new SecureRandom();
+    private long _maxNonceAgeMs = 60 * 1000;
+    private int _maxNC = 1024;
+    private ConcurrentMap<String, Nonce> _nonceMap = new ConcurrentHashMap<>();
+    private Queue<Nonce> _nonceQueue = new ConcurrentLinkedQueue<>();
 
-        public boolean seen(int count)
-        {
-            synchronized (this)
-            {
-                if (count>=_seen.size())
-                    return true;
-                boolean s=_seen.get(count);
-                _seen.set(count);
-                return s;
-            }
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    public DigestAuthenticator()
-    {
-        super();
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.security.authentication.LoginAuthenticator#setConfiguration(org.eclipse.jetty.security.Authenticator.AuthConfiguration)
-     */
     @Override
     public void setConfiguration(AuthConfiguration configuration)
     {
         super.setConfiguration(configuration);
 
-        String mna=configuration.getInitParameter("maxNonceAge");
-        if (mna!=null)
-        {
-            _maxNonceAgeMs=Long.valueOf(mna);
-        }
-        String mnc=configuration.getInitParameter("maxNonceCount");
-        if (mnc!=null)
-        {
-            _maxNC=Integer.valueOf(mnc);
-        }
+        String mna = configuration.getInitParameter("maxNonceAge");
+        if (mna != null)
+            setMaxNonceAge(Long.valueOf(mna));
+        String mnc = configuration.getInitParameter("maxNonceCount");
+        if (mnc != null)
+            setMaxNonceCount(Integer.valueOf(mnc));
     }
 
-    /* ------------------------------------------------------------ */
     public int getMaxNonceCount()
     {
         return _maxNC;
     }
 
-    /* ------------------------------------------------------------ */
     public void setMaxNonceCount(int maxNC)
     {
         _maxNC = maxNC;
     }
 
-    /* ------------------------------------------------------------ */
     public long getMaxNonceAge()
     {
         return _maxNonceAgeMs;
     }
 
-    /* ------------------------------------------------------------ */
-    public synchronized void setMaxNonceAge(long maxNonceAgeInMillis)
+    public void setMaxNonceAge(long maxNonceAgeInMillis)
     {
         _maxNonceAgeMs = maxNonceAgeInMillis;
     }
 
-    /* ------------------------------------------------------------ */
     @Override
     public String getAuthMethod()
     {
         return Constraint.__DIGEST_AUTH;
     }
 
-    /* ------------------------------------------------------------ */
     @Override
     public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, User validatedUser) throws ServerAuthException
     {
         return true;
     }
-    
 
-
-    /* ------------------------------------------------------------ */
     @Override
     public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
     {
@@ -216,20 +169,20 @@
                                     digest.uri = tok;
                                 else if ("response".equalsIgnoreCase(name))
                                     digest.response = tok;
-                                name=null;
+                                name = null;
                             }
                     }
                 }
 
-                int n = checkNonce(digest,(Request)request);
+                int n = checkNonce(digest, (Request)request);
 
                 if (n > 0)
                 {
                     //UserIdentity user = _loginService.login(digest.username,digest);
                     UserIdentity user = login(digest.username, digest, req);
-                    if (user!=null)
+                    if (user != null)
                     {
-                        return new UserAuthentication(getAuthMethod(),user);
+                        return new UserAuthentication(getAuthMethod(), user);
                     }
                 }
                 else if (n == 0)
@@ -260,10 +213,17 @@
         {
             throw new ServerAuthException(e);
         }
-
     }
 
-    /* ------------------------------------------------------------ */
+    @Override
+    public UserIdentity login(String username, Object credentials, ServletRequest request)
+    {
+        Digest digest = (Digest)credentials;
+        if (!Objects.equals(digest.realm, _loginService.getName()))
+            return null;
+        return super.login(username, credentials, request);
+    }
+
     public String newNonce(Request request)
     {
         Nonce nonce;
@@ -273,43 +233,42 @@
             byte[] nounce = new byte[24];
             _random.nextBytes(nounce);
 
-            nonce = new Nonce(new String(B64Code.encode(nounce)),request.getTimeStamp(),_maxNC);
+            nonce = new Nonce(new String(B64Code.encode(nounce)), request.getTimeStamp(), getMaxNonceCount());
         }
-        while (_nonceMap.putIfAbsent(nonce._nonce,nonce)!=null);
+        while (_nonceMap.putIfAbsent(nonce._nonce, nonce) != null);
         _nonceQueue.add(nonce);
 
         return nonce._nonce;
     }
 
     /**
-     * @param nstring nonce to check
-     * @param request
+     * @param digest  the digest data to check
+     * @param request the request object
      * @return -1 for a bad nonce, 0 for a stale none, 1 for a good nonce
      */
-    /* ------------------------------------------------------------ */
     private int checkNonce(Digest digest, Request request)
     {
         // firstly let's expire old nonces
-        long expired = request.getTimeStamp()-_maxNonceAgeMs;
-        Nonce nonce=_nonceQueue.peek();
-        while (nonce!=null && nonce._ts<expired)
+        long expired = request.getTimeStamp() - getMaxNonceAge();
+        Nonce nonce = _nonceQueue.peek();
+        while (nonce != null && nonce._ts < expired)
         {
             _nonceQueue.remove(nonce);
             _nonceMap.remove(nonce._nonce);
-            nonce=_nonceQueue.peek();
+            nonce = _nonceQueue.peek();
         }
 
         // Now check the requested nonce
         try
         {
             nonce = _nonceMap.get(digest.nonce);
-            if (nonce==null)
+            if (nonce == null)
                 return 0;
 
-            long count = Long.parseLong(digest.nc,16);
-            if (count>=_maxNC)
+            long count = Long.parseLong(digest.nc, 16);
+            if (count >= _maxNC)
                 return 0;
-            
+
             if (nonce.seen((int)count))
                 return -1;
 
@@ -322,9 +281,32 @@
         return -1;
     }
 
-    /* ------------------------------------------------------------ */
-    /* ------------------------------------------------------------ */
-    /* ------------------------------------------------------------ */
+    private static class Nonce
+    {
+        final String _nonce;
+        final long _ts;
+        final BitSet _seen;
+
+        public Nonce(String nonce, long ts, int size)
+        {
+            _nonce = nonce;
+            _ts = ts;
+            _seen = new BitSet(size);
+        }
+
+        public boolean seen(int count)
+        {
+            synchronized (this)
+            {
+                if (count >= _seen.size())
+                    return true;
+                boolean s = _seen.get(count);
+                _seen.set(count);
+                return s;
+            }
+        }
+    }
+
     private static class Digest extends Credential
     {
         private static final long serialVersionUID = -2484639019549527724L;
@@ -349,8 +331,8 @@
         public boolean check(Object credentials)
         {
             if (credentials instanceof char[])
-                credentials=new String((char[])credentials);
-            String password = (credentials instanceof String) ? (String) credentials : credentials.toString();
+                credentials = new String((char[])credentials);
+            String password = (credentials instanceof String) ? (String)credentials : credentials.toString();
 
             try
             {
@@ -361,22 +343,22 @@
                     // Credentials are already a MD5 digest - assume it's in
                     // form user:realm:password (we have no way to know since
                     // it's a digest, alright?)
-                    ha1 = ((Credential.MD5) credentials).getDigest();
+                    ha1 = ((Credential.MD5)credentials).getDigest();
                 }
                 else
                 {
                     // calc A1 digest
                     md.update(username.getBytes(StandardCharsets.ISO_8859_1));
-                    md.update((byte) ':');
+                    md.update((byte)':');
                     md.update(realm.getBytes(StandardCharsets.ISO_8859_1));
-                    md.update((byte) ':');
+                    md.update((byte)':');
                     md.update(password.getBytes(StandardCharsets.ISO_8859_1));
                     ha1 = md.digest();
                 }
                 // calc A2 digest
                 md.reset();
                 md.update(method.getBytes(StandardCharsets.ISO_8859_1));
-                md.update((byte) ':');
+                md.update((byte)':');
                 md.update(uri.getBytes(StandardCharsets.ISO_8859_1));
                 byte[] ha2 = md.digest();
 
@@ -388,15 +370,15 @@
                 // ) > <">
 
                 md.update(TypeUtil.toString(ha1, 16).getBytes(StandardCharsets.ISO_8859_1));
-                md.update((byte) ':');
+                md.update((byte)':');
                 md.update(nonce.getBytes(StandardCharsets.ISO_8859_1));
-                md.update((byte) ':');
+                md.update((byte)':');
                 md.update(nc.getBytes(StandardCharsets.ISO_8859_1));
-                md.update((byte) ':');
+                md.update((byte)':');
                 md.update(cnonce.getBytes(StandardCharsets.ISO_8859_1));
-                md.update((byte) ':');
+                md.update((byte)':');
                 md.update(qop.getBytes(StandardCharsets.ISO_8859_1));
-                md.update((byte) ':');
+                md.update((byte)':');
                 md.update(TypeUtil.toString(ha2, 16).getBytes(StandardCharsets.ISO_8859_1));
                 byte[] digest = md.digest();
 
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java
index 28a7b8a..684724e 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java
@@ -22,6 +22,7 @@
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.Locale;
+
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
@@ -41,7 +42,6 @@
 import org.eclipse.jetty.security.UserAuthentication;
 import org.eclipse.jetty.server.Authentication;
 import org.eclipse.jetty.server.Authentication.User;
-import org.eclipse.jetty.server.HttpChannel;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Response;
 import org.eclipse.jetty.server.UserIdentity;
@@ -64,8 +64,6 @@
  * to the /j_security_check URI within the context.  FormAuthentication uses
  * {@link SessionAuthentication} to wrap Authentication results so that they
  * are  associated with the session.</p>
- *
- *
  */
 public class FormAuthenticator extends LoginAuthenticator
 {
@@ -109,7 +107,7 @@
      * be remembered. If false, only the first uri that leads to a login
      * page redirect is remembered.
      * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=379909
-     * @param alwaysSave
+     * @param alwaysSave true to always save the uri
      */
     public void setAlwaysSaveUri (boolean alwaysSave)
     {
@@ -235,9 +233,8 @@
         
         //restore the original request's method on this request
         if (LOG.isDebugEnabled()) LOG.debug("Restoring original method {} for {} with method {}", method, juri,httpRequest.getMethod());
-        Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
-        HttpMethod m = HttpMethod.fromString(method);
-        base_request.setMethod(m,m.asString());
+        Request base_request = Request.getBaseRequest(request);
+        base_request.setMethod(method);
     }
 
     /* ------------------------------------------------------------ */
@@ -246,6 +243,9 @@
     {
         HttpServletRequest request = (HttpServletRequest)req;
         HttpServletResponse response = (HttpServletResponse)res;
+        Request base_request = Request.getBaseRequest(request);
+        Response base_response = base_request.getResponse();
+        
         String uri = request.getRequestURI();
         if (uri==null)
             uri=URIUtil.SLASH;
@@ -304,8 +304,6 @@
                     LOG.debug("authenticated {}->{}",form_auth,nuri);
 
                     response.setContentLength(0);
-                    Response base_response = HttpChannel.getCurrentHttpChannel().getResponse();
-                    Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
                     int redirectCode = (base_request.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
                     base_response.sendRedirect(redirectCode, response.encodeRedirectURL(nuri));
                     return form_auth;
@@ -331,8 +329,6 @@
                 else
                 {
                     LOG.debug("auth failed {}->{}",username,_formErrorPage);
-                    Response base_response = HttpChannel.getCurrentHttpChannel().getResponse();
-                    Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
                     int redirectCode = (base_request.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
                     base_response.sendRedirect(redirectCode, response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formErrorPage)));
                 }
@@ -372,7 +368,6 @@
                                 if (j_post!=null)
                                 {
                                     LOG.debug("auth rePOST {}->{}",authentication,j_uri);
-                                    Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
                                     base_request.setContentParameters(j_post);
                                 }
                                 session.removeAttribute(__J_URI);
@@ -407,7 +402,6 @@
 
                     if (MimeTypes.Type.FORM_ENCODED.is(req.getContentType()) && HttpMethod.POST.is(request.getMethod()))
                     {
-                        Request base_request = (req instanceof Request)?(Request)req:HttpChannel.getCurrentHttpChannel().getRequest();
                         MultiMap<String> formParameters = new MultiMap<>();
                         base_request.extractFormParameters(formParameters);
                         session.setAttribute(__J_POST, formParameters);
@@ -427,8 +421,6 @@
             else
             {
                 LOG.debug("challenge {}->{}",session.getId(),_formLoginPage);
-                Response base_response = HttpChannel.getCurrentHttpChannel().getResponse();
-                Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
                 int redirectCode = (base_request.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
                 base_response.sendRedirect(redirectCode, response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formLoginPage)));
             }
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java
index 03e7e7d..64b5246 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java
@@ -58,7 +58,7 @@
     /* ------------------------------------------------------------ */
     public UserIdentity login(String username, Object password, ServletRequest request)
     {
-        UserIdentity user = _loginService.login(username,password);
+        UserIdentity user = _loginService.login(username,password, request);
         if (user!=null)
         {
             renewSession((HttpServletRequest)request, (request instanceof Request? ((Request)request).getResponse() : null));
@@ -92,11 +92,11 @@
     /** Change the session id.
      * The session is changed to a new instance with a new ID if and only if:<ul>
      * <li>A session exists.
-     * <li>The {@link AuthConfiguration#isSessionRenewedOnAuthentication()} returns true.
+     * <li>The {@link org.eclipse.jetty.security.Authenticator.AuthConfiguration#isSessionRenewedOnAuthentication()} returns true.
      * <li>The session ID has been given to unauthenticated responses
      * </ul>
-     * @param request
-     * @param response
+     * @param request the request
+     * @param response the response
      * @return The new session.
      */
     protected HttpSession renewSession(HttpServletRequest request, HttpServletResponse response)
@@ -109,14 +109,14 @@
             {
                 //if we should renew sessions, and there is an existing session that may have been seen by non-authenticated users
                 //(indicated by SESSION_SECURED not being set on the session) then we should change id
-                if (httpSession.getAttribute(AbstractSession.SESSION_KNOWN_ONLY_TO_AUTHENTICATED)!=Boolean.TRUE)
+                if (httpSession.getAttribute(AbstractSession.SESSION_CREATED_SECURE)!=Boolean.TRUE)
                 {
                     if (httpSession instanceof AbstractSession)
                     {
                         AbstractSession abstractSession = (AbstractSession)httpSession;
                         String oldId = abstractSession.getId();
                         abstractSession.renewId(request);
-                        abstractSession.setAttribute(AbstractSession.SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
+                        abstractSession.setAttribute(AbstractSession.SESSION_CREATED_SECURE, Boolean.TRUE);
                         if (abstractSession.isIdChanged() && response != null && (response instanceof Response))
                             ((Response)response).addCookie(abstractSession.getSessionManager().getSessionCookie(abstractSession, request.getContextPath(), request.isSecure()));
                         LOG.debug("renew {}->{}",oldId,abstractSession.getId());
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
index 546a0c0..380da1e 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
@@ -71,7 +71,7 @@
         if (login_service==null)
             throw new IllegalStateException("!LoginService");
 
-        _userIdentity=login_service.login(_name,_credentials);
+        _userIdentity=login_service.login(_name,_credentials, null);
         LOG.debug("Deserialized and relogged in {}",this);
     }
 
@@ -89,7 +89,7 @@
         if (security!=null)
             security.logout(this);
         if (_session!=null)
-            _session.removeAttribute(AbstractSession.SESSION_KNOWN_ONLY_TO_AUTHENTICATED);
+            _session.removeAttribute(AbstractSession.SESSION_CREATED_SECURE);
     }
 
     @Override
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java
index d04521a..93e0592 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java
@@ -46,7 +46,7 @@
 
     /**
      * Allow for a custom authMethod value to be set for instances where SPENGO may not be appropriate
-     * @param authMethod
+     * @param authMethod the auth method
      */
     public SpnegoAuthenticator( String authMethod )
     {
diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/AliasedConstraintTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/AliasedConstraintTest.java
index 996d2ca..ad7c80d 100644
--- a/jetty-security/src/test/java/org/eclipse/jetty/security/AliasedConstraintTest.java
+++ b/jetty-security/src/test/java/org/eclipse/jetty/security/AliasedConstraintTest.java
@@ -18,8 +18,10 @@
 

 package org.eclipse.jetty.security;

 

-import static org.hamcrest.Matchers.*;

-import static org.junit.Assert.*;

+import static org.hamcrest.Matchers.containsString;

+import static org.hamcrest.Matchers.startsWith;

+import static org.junit.Assert.assertThat;

+import static org.junit.Assert.fail;

 

 import java.util.ArrayList;

 import java.util.Collection;

@@ -29,9 +31,12 @@
 

 import org.eclipse.jetty.http.HttpStatus;

 import org.eclipse.jetty.server.Connector;

+import org.eclipse.jetty.server.Handler;

 import org.eclipse.jetty.server.LocalConnector;

 import org.eclipse.jetty.server.Server;

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

+import org.eclipse.jetty.server.handler.DefaultHandler;

+import org.eclipse.jetty.server.handler.HandlerList;

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

 import org.eclipse.jetty.server.session.SessionHandler;

 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;

@@ -77,7 +82,10 @@
 

         context.setContextPath("/ctx");

         context.setResourceBase(MavenTestingUtils.getTestResourceDir("docroot").getAbsolutePath());

-        server.setHandler(context);

+        

+        HandlerList handlers = new HandlerList();

+        handlers.setHandlers(new Handler[]{context,new DefaultHandler()});

+        server.setHandler(handlers);

         context.setHandler(session);

         // context.addAliasCheck(new AllowSymLinkAliasChecker());

 

diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java
index 33d02f5..fd80d46 100644
--- a/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java
+++ b/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java
@@ -32,6 +32,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+
 import javax.servlet.HttpConstraintElement;
 import javax.servlet.HttpMethodConstraintElement;
 import javax.servlet.ServletException;
@@ -219,8 +220,8 @@
 
     /**
      * Equivalent of Servlet Spec 3.1 pg 132, sec 13.4.1.1, Example 13-1
-     * @ServletSecurity
-     * @throws Exception
+     * &#064;ServletSecurity
+     * @throws Exception if test fails
      */
     @Test
     public void testSecurityElementExample13_1() throws Exception
@@ -233,9 +234,9 @@
    
     /**
      * Equivalent of Servlet Spec 3.1 pg 132, sec 13.4.1.1, Example 13-2
-     * @ServletSecurity(@HttpConstraint(transportGuarantee = TransportGuarantee.CONFIDENTIAL))
+     * &#064;ServletSecurity(@HttpConstraint(transportGuarantee = TransportGuarantee.CONFIDENTIAL))
      * 
-     * @throws Exception
+     * @throws Exception if test fails
      */
     @Test
     public void testSecurityElementExample13_2() throws Exception
@@ -252,7 +253,7 @@
     /**
      * Equivalent of Servlet Spec 3.1 pg 132, sec 13.4.1.1, Example 13-3
      * @ServletSecurity(@HttpConstraint(EmptyRoleSemantic.DENY))
-     * @throws Exception
+     * @throws Exception if test fails
      */
     @Test
     public void testSecurityElementExample13_3() throws Exception
@@ -269,7 +270,7 @@
     /**
      * Equivalent of Servlet Spec 3.1 pg 132, sec 13.4.1.1, Example 13-4
      * @ServletSecurity(@HttpConstraint(rolesAllowed = "R1"))
-     * @throws Exception
+     * @throws Exception if test fails
      */
     @Test
     public void testSecurityElementExample13_4() throws Exception
@@ -293,7 +294,7 @@
      * @HttpMethodConstraint(value = "GET", rolesAllowed = "R1"),
      * @HttpMethodConstraint(value = "POST", rolesAllowed = "R1",
      *         transportGuarantee = TransportGuarantee.CONFIDENTIAL)})
-     * @throws Exception
+     * @throws Exception if test fails
      */ 
     @Test
     public void testSecurityElementExample13_5() throws Exception
@@ -318,7 +319,7 @@
     /**
      * Equivalent of Servlet Spec 3.1 pg 132, sec 13.4.1.1, Example 13-6
      * @ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"), httpMethodConstraints = @HttpMethodConstraint("GET"))
-     * @throws Exception
+     * @throws Exception if test fails
      */
     @Test
     public void testSecurityElementExample13_6 () throws Exception
@@ -344,7 +345,7 @@
      * @ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"), 
      *                  httpMethodConstraints = @HttpMethodConstraint(value="TRACE",
      *                  emptyRoleSemantic = EmptyRoleSemantic.DENY))
-     * @throws Exception
+     * @throws Exception if test fails
      */
     @Test
     public void testSecurityElementExample13_7() throws Exception
diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/DataConstraintsTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/DataConstraintsTest.java
index 6fa28f9..3af9420 100644
--- a/jetty-security/src/test/java/org/eclipse/jetty/security/DataConstraintsTest.java
+++ b/jetty-security/src/test/java/org/eclipse/jetty/security/DataConstraintsTest.java
@@ -20,7 +20,9 @@
 
 import java.io.IOException;
 import java.util.Arrays;
+
 import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -375,10 +377,10 @@
         response = _connectorS.getResponses("GET /ctx/restricted/info HTTP/1.0\r\n\r\n");
         Assert.assertThat(response, Matchers.containsString("HTTP/1.1 403 Forbidden"));
 
-        response = _connector.getResponses("GET /ctx/restricted/info HTTP/1.0\r\n Authorization: Basic YWRtaW46cGFzc3dvcmQ=\r\n\r\n");
+        response = _connector.getResponses("GET /ctx/restricted/info HTTP/1.0\r\nAuthorization: Basic YWRtaW46cGFzc3dvcmQ=\r\n\r\n");
         Assert.assertThat(response, Matchers.containsString("HTTP/1.1 403 Forbidden"));
 
-        response = _connectorS.getResponses("GET /ctx/restricted/info HTTP/1.0\r\n Authorization: Basic YWRtaW46cGFzc3dvcmQ=\r\n\r\n");
+        response = _connectorS.getResponses("GET /ctx/restricted/info HTTP/1.0\r\nAuthorization: Basic YWRtaW46cGFzc3dvcmQ=\r\n\r\n");
         Assert.assertThat(response, Matchers.containsString("HTTP/1.1 403 Forbidden"));
 
     }
@@ -436,7 +438,7 @@
         }
 
         @Override
-        public UserIdentity login(String username, Object credentials)
+        public UserIdentity login(String username, Object credentials, ServletRequest request)
         {
             if("admin".equals(username) && "password".equals(credentials))
                     return new DefaultUserIdentity(null,null,new String[] { "admin" } );
diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/PropertyUserStoreTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/PropertyUserStoreTest.java
index cb72b30..935df6f 100644
--- a/jetty-security/src/test/java/org/eclipse/jetty/security/PropertyUserStoreTest.java
+++ b/jetty-security/src/test/java/org/eclipse/jetty/security/PropertyUserStoreTest.java
@@ -18,178 +18,176 @@
 
 package org.eclipse.jetty.security;
 
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.Writer;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.OS;
+import org.eclipse.jetty.toolchain.test.TestingDir;
 import org.eclipse.jetty.util.security.Credential;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
+import org.hamcrest.Matcher;
+import org.junit.Rule;
 import org.junit.Test;
 
 public class PropertyUserStoreTest
 {
-    String testFileDir = "target" + File.separator + "property-user-store-test";
-    String testFile = testFileDir + File.separator + "users.txt";
-
-    @Before
-    public void before() throws Exception
+    private final class UserCount implements PropertyUserStore.UserListener
     {
-        File file = new File(testFileDir);
-        file.mkdirs();
+        private final AtomicInteger userCount = new AtomicInteger();
+        private final List<String> users = new ArrayList<String>();
 
-        writeInitialUsers(testFile);
+        private UserCount()
+        {
+        }
+
+        public void update(String username, Credential credential, String[] roleArray)
+        {
+            if (!users.contains(username))
+            {
+                users.add(username);
+                userCount.getAndIncrement();
+            }
+        }
+
+        public void remove(String username)
+        {
+            users.remove(username);
+            userCount.getAndDecrement();
+        }
+
+        public void awaitCount(int expectedCount) throws InterruptedException
+        {
+            long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10);
+            
+            while (userCount.get() != expectedCount && (System.currentTimeMillis() < timeout))
+            {
+                TimeUnit.MILLISECONDS.sleep(100);
+            }
+            
+            assertThatCount(is(expectedCount));
+        }
+
+        public void assertThatCount(Matcher<Integer> matcher)
+        {
+            assertThat("User count",userCount.get(),matcher);
+        }
+
+        public void assertThatUsers(Matcher<Iterable<? super String>> matcher)
+        {
+            assertThat("Users list",users,matcher);
+        }
     }
 
-    @After
-    public void after() throws Exception
-    {
-        File file = new File(testFile);
+    @Rule
+    public TestingDir testdir = new TestingDir();
 
-        file.delete();
-    }
-
-    private void writeInitialUsers(String testFile) throws Exception
+    private File initUsersText() throws Exception
     {
-        try (Writer writer = new BufferedWriter(new FileWriter(testFile)))
+        Path dir = testdir.getPath().toRealPath();
+        FS.ensureDirExists(dir.toFile());
+        File users = dir.resolve("users.txt").toFile();
+        
+        try (Writer writer = new BufferedWriter(new FileWriter(users)))
         {
             writer.append("tom: tom, roleA\n");
             writer.append("dick: dick, roleB\n");
             writer.append("harry: harry, roleA, roleB\n");
         }
+        
+        return users;
     }
 
-    private void writeAdditionalUser(String testFile) throws Exception
+    private void addAdditionalUser(File usersFile, String userRef) throws Exception
     {
         Thread.sleep(1001);
-        try (Writer writer = new BufferedWriter(new FileWriter(testFile,true)))
+        try (Writer writer = new BufferedWriter(new FileWriter(usersFile,true)))
         {
-            writer.append("skip: skip, roleA\n");
+            writer.append(userRef);
         }
     }
 
     @Test
     public void testPropertyUserStoreLoad() throws Exception
     {
-        final AtomicInteger userCount = new AtomicInteger();
+        final UserCount userCount = new UserCount();
+        final File usersFile = initUsersText();
 
         PropertyUserStore store = new PropertyUserStore();
+        store.setConfigPath(usersFile);
 
-        store.setConfig(testFile);
-
-        store.registerUserListener(new PropertyUserStore.UserListener()
-        {
-
-            public void update(String username, Credential credential, String[] roleArray)
-            {
-                userCount.getAndIncrement();
-            }
-
-            public void remove(String username)
-            {
-
-            }
-        });
+        store.registerUserListener(userCount);
 
         store.start();
 
-        Assert.assertNotNull("Failed to retrieve UserIdentity directly from PropertyUserStore", store.getUserIdentity("tom"));
-        Assert.assertNotNull("Failed to retrieve UserIdentity directly from PropertyUserStore", store.getUserIdentity("dick"));
-        Assert.assertNotNull("Failed to retrieve UserIdentity directly from PropertyUserStore", store.getUserIdentity("harry"));
-        Assert.assertEquals(3,userCount.get());
+        assertThat("Failed to retrieve UserIdentity directly from PropertyUserStore", store.getUserIdentity("tom"), notNullValue());
+        assertThat("Failed to retrieve UserIdentity directly from PropertyUserStore", store.getUserIdentity("dick"), notNullValue());
+        assertThat("Failed to retrieve UserIdentity directly from PropertyUserStore", store.getUserIdentity("harry"), notNullValue());
+        userCount.assertThatCount(is(3));
+        userCount.awaitCount(3);
     }
 
     @Test
     public void testPropertyUserStoreLoadUpdateUser() throws Exception
     {
-
-        final AtomicInteger userCount = new AtomicInteger();
-
-        final List<String> users = new ArrayList<String>();
+        assumeThat("Skipping on OSX", OS.IS_OSX, is(false));
+        final UserCount userCount = new UserCount();
+        final File usersFile = initUsersText();
 
         PropertyUserStore store = new PropertyUserStore();
-        store.setRefreshInterval(1);
-        store.setConfig(testFile);
+        store.setHotReload(true);
+        store.setConfigPath(usersFile);
 
-        store.registerUserListener(new PropertyUserStore.UserListener()
-        {
-            public void update(String username, Credential credential, String[] roleArray)
-            {
-                if (!users.contains(username))
-                {
-                    users.add(username);
-                    userCount.getAndIncrement();
-                }
-            }
-
-            public void remove(String username)
-            {
-
-            }
-        });
+        store.registerUserListener(userCount);
 
         store.start();
-        Assert.assertEquals(3,userCount.get());
+        
+        userCount.assertThatCount(is(3));
 
-        writeAdditionalUser(testFile);
+        addAdditionalUser(usersFile,"skip: skip, roleA\n");
 
-        long start = System.currentTimeMillis();
-        while (userCount.get() < 4 && (System.currentTimeMillis() - start) < 10000)
-        {
-            Thread.sleep(10);
-        }
+        userCount.awaitCount(4);
 
-        Assert.assertNotNull("Failed to retrieve UserIdentity from PropertyUserStore directly", store.getUserIdentity("skip"));
-        Assert.assertEquals(4,userCount.get());
-
-        Assert.assertTrue(users.contains("skip"));
+        assertThat("Failed to retrieve UserIdentity from PropertyUserStore directly", store.getUserIdentity("skip"), notNullValue());
+        
+        userCount.assertThatCount(is(4));
+        userCount.assertThatUsers(hasItem("skip"));
     }
 
     @Test
     public void testPropertyUserStoreLoadRemoveUser() throws Exception
     {
-        writeAdditionalUser(testFile);
-
-        final AtomicInteger userCount = new AtomicInteger();
-
-        final List<String> users = new ArrayList<String>();
+        assumeThat("Skipping on OSX", OS.IS_OSX, is(false));
+        final UserCount userCount = new UserCount();
+        // initial user file (3) users
+        final File usersFile = initUsersText();
+        
+        // adding 4th user
+        addAdditionalUser(usersFile,"skip: skip, roleA\n");
 
         PropertyUserStore store = new PropertyUserStore();
-        store.setRefreshInterval(2);
-        store.setConfig(testFile);
+        store.setHotReload(true);
+        store.setConfigPath(usersFile);
 
-        store.registerUserListener(new PropertyUserStore.UserListener()
-        {
-
-            public void update(String username, Credential credential, String[] roleArray)
-            {
-                if (!users.contains(username))
-                {
-                    users.add(username);
-                    userCount.getAndIncrement();
-                }
-            }
-
-            public void remove(String username)
-            {
-                users.remove(username);
-                userCount.getAndDecrement();
-            }
-        });
+        store.registerUserListener(userCount);
 
         store.start();
 
-        Assert.assertEquals(4,userCount.get());
+        userCount.assertThatCount(is(4));
 
-        Thread.sleep(2000);
-        writeInitialUsers(testFile);
-        Thread.sleep(3000);
-        Assert.assertEquals(3,userCount.get());
+        // rewrite file with original 3 users
+        initUsersText();
+        
+        userCount.awaitCount(3);
     }
-
 }
diff --git a/jetty-security/src/test/resources/jetty-logging.properties b/jetty-security/src/test/resources/jetty-logging.properties
new file mode 100755
index 0000000..24d5e3a
--- /dev/null
+++ b/jetty-security/src/test/resources/jetty-logging.properties
@@ -0,0 +1,7 @@
+# Setup default logging implementation for during testing
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+
+#org.eclipse.jetty.LEVEL=DEBUG
+
+#org.eclipse.jetty.util.PathWatcher.LEVEL=DEBUG
+#org.eclipse.jetty.util.PathWatcher.Noisy.LEVEL=OFF
diff --git a/jetty-server/pom.xml b/jetty-server/pom.xml
index 64fc45b..aa4ca89 100644
--- a/jetty-server/pom.xml
+++ b/jetty-server/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-server</artifactId>
@@ -15,63 +15,16 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <id>generate-manifest</id>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",org.eclipse.jetty.jmx.*;version="9.1";resolution:=optional,*</Import-Package>
-                <_nouses>true</_nouses>
-              </instructions>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
         <executions>
           <execution>
-            <id>artifact-jar</id>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-          <execution>
             <id>test-jar</id>
             <goals>
               <goal>test-jar</goal>
             </goals>
           </execution>
         </executions>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
       </plugin>
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
@@ -84,11 +37,6 @@
   </build>
   <dependencies>
     <dependency>
-      <groupId>org.eclipse.jetty.toolchain</groupId>
-      <artifactId>jetty-test-helper</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>javax.servlet-api</artifactId>
 <!--
@@ -119,9 +67,16 @@
       <optional>true</optional>
     </dependency>
     <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-core</artifactId>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
       <scope>test</scope>
-      </dependency>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-http</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
diff --git a/jetty-server/src/main/config/etc/home-base-warning.xml b/jetty-server/src/main/config/etc/home-base-warning.xml
index b83f36f..4d568f1 100644
--- a/jetty-server/src/main/config/etc/home-base-warning.xml
+++ b/jetty-server/src/main/config/etc/home-base-warning.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- ============================================================= -->
 <!-- Display a Warning Message if {jetty.home} == {jetty.base}     -->
 <!-- ============================================================= -->
 <Configure id="homeBaseWarning" class="org.eclipse.jetty.server.HomeBaseWarning">
-</Configure>
\ No newline at end of file
+</Configure>
diff --git a/jetty-server/src/main/config/etc/jetty-debug.xml b/jetty-server/src/main/config/etc/jetty-debug.xml
index 52b4bdb..2e47a5f 100644
--- a/jetty-server/src/main/config/etc/jetty-debug.xml
+++ b/jetty-server/src/main/config/etc/jetty-debug.xml
@@ -1,23 +1,36 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
-<!-- Mixin the DebugHandler                                          -->
+<!-- The DebugListener                                               -->
 <!-- =============================================================== -->
 
-
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
-    <Get id="oldhandler" name="handler"/>
-    <Set name="handler">
-      <New id="DebugHandler" class="org.eclipse.jetty.server.handler.DebugHandler">
-        <Set name="handler"><Ref refid="oldhandler"/></Set>
-	<Set name="outputStream">
-	  <New class="org.eclipse.jetty.util.RolloverFileOutputStream">
-	    <Arg type="String"><Property name="jetty.logs" default="./logs"/>/yyyy_mm_dd.debug.log</Arg>
-	    <Arg type="boolean">true</Arg> <!-- append -->
-	    <Arg type="int">90</Arg> <!-- retain days -->
-	  </New>
-	</Set>
+  <New id="DebugListener" class="org.eclipse.jetty.server.DebugListener">
+    <Arg name="outputStream">
+      <New class="org.eclipse.jetty.util.RolloverFileOutputStream">
+        <Arg type="String"><Property name="jetty.logs" default="./logs"/>/yyyy_mm_dd.debug.log</Arg>
+        <Arg type="boolean"><Property name="jetty.debug.append" default="true"/></Arg>
+        <Arg type="int"><Property name="jetty.debug.retainDays" default="14"/></Arg>
+        <Arg>
+          <Call class="java.util.TimeZone" name="getTimeZone"><Arg><Property name="jetty.debug.timezone" default="GMT"/></Arg></Call>
+        </Arg>
       </New>
-    </Set>
+    </Arg>
+    <Arg name="showHeaders" type="boolean"><Property name="jetty.debug.showHeaders" default="true"/></Arg>
+    <Arg name="renameThread" type="boolean"><Property name="jetty.debug.renameThread" default="false"/></Arg>
+    <Arg name="dumpContext" type="boolean"><Property name="jetty.debug.dumpContext" default="true"/></Arg>
+  </New>
+ 
+  <Call name="addBean"><Arg><Ref refid="DebugListener"/></Arg></Call>
+ 
+  <Ref refid="DeploymentManager">
+    <Call name="addLifeCycleBinding">
+      <Arg>
+        <New class="org.eclipse.jetty.deploy.bindings.DebugListenerBinding">
+          <Arg><Ref refid="DebugListener"/></Arg>
+        </New>
+      </Arg>
+    </Call>
+  </Ref>
 </Configure>
diff --git a/jetty-server/src/main/config/etc/jetty-debuglog.xml b/jetty-server/src/main/config/etc/jetty-debuglog.xml
new file mode 100644
index 0000000..0a082ce
--- /dev/null
+++ b/jetty-server/src/main/config/etc/jetty-debuglog.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<!-- =============================================================== -->
+<!-- The DebugHandler                                                -->
+<!-- =============================================================== -->
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+  <Call name="insertHandler">
+    <Arg>
+      <New id="DebugHandler" class="org.eclipse.jetty.server.handler.DebugHandler">
+	<Set name="outputStream">
+	  <New class="org.eclipse.jetty.util.RolloverFileOutputStream">
+	    <Arg type="String"><Property name="jetty.debuglog.dir" deprecated="jetty.logs" default="./logs"/>/yyyy_mm_dd.debug.log</Arg>
+	    <Arg type="boolean"><Property name="jetty.debuglog.append" default="true"/></Arg>
+	    <Arg type="int"><Property name="jetty.debuglog.retainDays" default="90"/></Arg>
+	    <Arg>
+	      <Call class="java.util.TimeZone" name="getTimeZone"><Arg><Property name="jetty.debuglog.timezone" default="GMT"/></Arg></Call>
+	    </Arg>
+	  </New>
+	</Set>
+      </New>
+    </Arg>
+  </Call>
+</Configure>
diff --git a/jetty-server/src/main/config/etc/jetty-gzip.xml b/jetty-server/src/main/config/etc/jetty-gzip.xml
new file mode 100644
index 0000000..93b41fd
--- /dev/null
+++ b/jetty-server/src/main/config/etc/jetty-gzip.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<!-- =============================================================== -->
+<!-- Mixin the GZIP Handler                                          -->
+<!-- This applies the GZIP Handler to the entire server              -->
+<!-- If a GZIP handler is required for an individual context, then   -->
+<!-- use a context XML (see test.xml example in distribution)        -->
+<!-- =============================================================== -->
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+  <Call name="insertHandler">
+    <Arg>
+      <New id="GzipHandler" class="org.eclipse.jetty.server.handler.gzip.GzipHandler">
+	<Set name="minGzipSize"><Property name="jetty.gzip.minGzipSize" deprecated="gzip.minGzipSize" default="2048"/></Set>
+	<Set name="checkGzExists"><Property name="jetty.gzip.checkGzExists" deprecated="gzip.checkGzExists" default="false"/></Set>
+	<Set name="compressionLevel"><Property name="jetty.gzip.compressionLevel" deprecated="gzip.compressionLevel" default="-1"/></Set>
+	<Set name="excludedAgentPatterns">
+	  <Array type="String">
+	    <Item><Property name="jetty.gzip.excludedUserAgent" deprecated="gzip.excludedUserAgent" default=".*MSIE.6\.0.*"/></Item>
+	  </Array>
+	</Set>
+
+	<Set name="includedMethods">
+	  <Array type="String">
+	    <Item>GET</Item>
+	  </Array>
+	</Set>
+
+	<!--
+	<Set name="includedPaths">
+	  <Array type="String">
+	    <Item>/*</Item>
+	  </Array>
+	</Set>
+	-->
+
+	<!--
+	<Set name="excludedPaths">
+	  <Array type="String">
+	    <Item>*.gz</Item>
+	  </Array>
+	</Set>
+	-->
+
+	<!--
+	<Call name="addIncludedMimeTypes">
+	  <Arg><Array type="String">
+	    <Item>some/type</Item>
+	  </Array></Arg>
+	</Call>
+	-->
+
+	<!--
+	<Call name="addExcludedMimeTypes">
+	  <Arg><Array type="String">
+	    <Item>some/type</Item>
+	  </Array></Arg>
+	</Call>
+	-->
+
+      </New>
+    </Arg>
+  </Call>
+</Configure>
+
+
diff --git a/jetty-server/src/main/config/etc/jetty-http-forwarded.xml b/jetty-server/src/main/config/etc/jetty-http-forwarded.xml
new file mode 100644
index 0000000..50b8097
--- /dev/null
+++ b/jetty-server/src/main/config/etc/jetty-http-forwarded.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+<Configure id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
+  <Call name="addCustomizer">
+    <Arg>
+      <New class="org.eclipse.jetty.server.ForwardedRequestCustomizer">
+        <Set name="forwardedOnly"><Property name="jetty.httpConfig.forwardedOnly" default="false"/></Set>
+        <Set name="proxyAsAuthority"><Property name="jetty.httpConfig.forwardedProxyAsAuthority" default="false"/></Set>
+        <Set name="forwardedHeader"><Property name="jetty.httpConfig.forwardedHeader" default="Forwarded"/></Set>
+        <Set name="forwardedHostHeader"><Property name="jetty.httpConfig.forwardedHostHeader" default="X-Forwarded-Host"/></Set>
+        <Set name="forwardedServerHeader"><Property name="jetty.httpConfig.forwardedServerHeader" default="X-Forwarded-Server"/></Set>
+        <Set name="forwardedProtoHeader"><Property name="jetty.httpConfig.forwardedProtoHeader" default="X-Forwarded-Proto"/></Set>
+        <Set name="forwardedForHeader"><Property name="jetty.httpConfig.forwardedForHeader" default="X-Forwarded-For"/></Set>
+        <Set name="forwardedHttpsHeader"><Property name="jetty.httpConfig.forwardedHttpsHeader" default="X-Proxied-Https"/></Set>
+        <Set name="forwardedSslSessionIdHeader"><Property name="jetty.httpConfig.forwardedSslSessionIdHeader" default="Proxy-ssl-id" /></Set>
+        <Set name="forwardedCipherSuiteHeader"><Property name="jetty.httpConfig.forwardedCipherSuiteHeader" default="Proxy-auth-cert"/></Set>
+      </New>
+    </Arg>
+  </Call>
+</Configure>
diff --git a/jetty-server/src/main/config/etc/jetty-http.xml b/jetty-server/src/main/config/etc/jetty-http.xml
index 1459ba8..70d317b 100644
--- a/jetty-server/src/main/config/etc/jetty-http.xml
+++ b/jetty-server/src/main/config/etc/jetty-http.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- ============================================================= -->
 <!-- Configure the Jetty Server instance with an ID "Server"       -->
@@ -20,26 +20,26 @@
   <!-- =========================================================== -->
   <Call name="addConnector">
     <Arg>
-      <New class="org.eclipse.jetty.server.ServerConnector">
+      <New id="httpConnector" class="org.eclipse.jetty.server.ServerConnector">
         <Arg name="server"><Ref refid="Server" /></Arg>
-        <Arg name="acceptors" type="int"><Property name="http.acceptors" default="-1"/></Arg>
-        <Arg name="selectors" type="int"><Property name="http.selectors" default="-1"/></Arg>
+        <Arg name="acceptors" type="int"><Property name="jetty.http.acceptors" deprecated="http.acceptors" default="-1"/></Arg>
+        <Arg name="selectors" type="int"><Property name="jetty.http.selectors" deprecated="http.selectors" default="-1"/></Arg>
         <Arg name="factories">
           <Array type="org.eclipse.jetty.server.ConnectionFactory">
             <Item>
               <New class="org.eclipse.jetty.server.HttpConnectionFactory">
                 <Arg name="config"><Ref refid="httpConfig" /></Arg>
+                <Arg name="compliance"><Call class="org.eclipse.jetty.http.HttpCompliance" name="valueOf"><Arg><Property name="jetty.http.compliance" default="RFC7230"/></Arg></Call></Arg>
               </New>
             </Item>
           </Array>
         </Arg>
-        <Set name="host"><Property name="jetty.host" /></Set>
-        <Set name="port"><Property name="jetty.port" default="80" /></Set>
-        <Set name="idleTimeout"><Property name="http.timeout" default="30000"/></Set>
-        <Set name="soLingerTime"><Property name="http.soLingerTime" default="-1"/></Set>
-        <Set name="acceptorPriorityDelta"><Property name="http.acceptorPriorityDelta" default="0"/></Set>
-        <Set name="selectorPriorityDelta"><Property name="http.selectorPriorityDelta" default="0"/></Set>
-        <Set name="acceptQueueSize"><Property name="http.acceptQueueSize" default="0"/></Set>
+        <Set name="host"><Property name="jetty.http.host" deprecated="jetty.host" /></Set>
+        <Set name="port"><Property name="jetty.http.port" deprecated="jetty.port" default="8080" /></Set>
+        <Set name="idleTimeout"><Property name="jetty.http.idleTimeout" deprecated="http.timeout" default="30000"/></Set>
+        <Set name="soLingerTime"><Property name="jetty.http.soLingerTime" deprecated="http.soLingerTime" default="-1"/></Set>
+        <Set name="acceptorPriorityDelta"><Property name="jetty.http.acceptorPriorityDelta" deprecated="http.acceptorPriorityDelta" default="0"/></Set>
+        <Set name="acceptQueueSize"><Property name="jetty.http.acceptQueueSize" deprecated="http.acceptQueueSize" default="0"/></Set>
       </New>
     </Arg>
   </Call>
diff --git a/jetty-server/src/main/config/etc/jetty-https.xml b/jetty-server/src/main/config/etc/jetty-https.xml
index 5eab5c5..71a0837 100644
--- a/jetty-server/src/main/config/etc/jetty-https.xml
+++ b/jetty-server/src/main/config/etc/jetty-https.xml
@@ -1,53 +1,29 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- ============================================================= -->
 <!-- Configure a HTTPS connector.                                  -->
 <!-- This configuration must be used in conjunction with jetty.xml -->
 <!-- and jetty-ssl.xml.                                            -->
 <!-- ============================================================= -->
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
+<Configure id="sslConnector" class="org.eclipse.jetty.server.ServerConnector">
 
-  <!-- =========================================================== -->
-  <!-- Add a HTTPS Connector.                                      -->
-  <!-- Configure an o.e.j.server.ServerConnector with connection   -->
-  <!-- factories for TLS (aka SSL) and HTTP to provide HTTPS.      -->
-  <!-- All accepted TLS connections are wired to a HTTP connection.-->
-  <!--                                                             -->
-  <!-- Consult the javadoc of o.e.j.server.ServerConnector,        -->
-  <!-- o.e.j.server.SslConnectionFactory and                       -->
-  <!-- o.e.j.server.HttpConnectionFactory for all configuration    -->
-  <!-- that may be set here.                                       -->
-  <!-- =========================================================== -->
-  <Call id="httpsConnector" name="addConnector">
+  <Call name="addIfAbsentConnectionFactory">
     <Arg>
-      <New class="org.eclipse.jetty.server.ServerConnector">
-        <Arg name="server"><Ref refid="Server" /></Arg>
-        <Arg name="acceptors" type="int"><Property name="ssl.acceptors" default="-1"/></Arg>
-        <Arg name="selectors" type="int"><Property name="ssl.selectors" default="-1"/></Arg>
-        <Arg name="factories">
-          <Array type="org.eclipse.jetty.server.ConnectionFactory">
-            <Item>
-              <New class="org.eclipse.jetty.server.SslConnectionFactory">
-                <Arg name="next">http/1.1</Arg>
-                <Arg name="sslContextFactory"><Ref refid="sslContextFactory"/></Arg>
-              </New>
-            </Item>
-            <Item>
-              <New class="org.eclipse.jetty.server.HttpConnectionFactory">
-                <Arg name="config"><Ref refid="sslHttpConfig"/></Arg>
-              </New>
-            </Item>
-          </Array>
-        </Arg>
-        <Set name="host"><Property name="jetty.host" /></Set>
-        <Set name="port"><Property name="https.port" default="443" /></Set>
-        <Set name="idleTimeout"><Property name="https.timeout" default="30000"/></Set>
-        <Set name="soLingerTime"><Property name="https.soLingerTime" default="-1"/></Set>
-        <Set name="acceptorPriorityDelta"><Property name="ssl.acceptorPriorityDelta" default="0"/></Set>
-        <Set name="selectorPriorityDelta"><Property name="ssl.selectorPriorityDelta" default="0"/></Set>
-        <Set name="acceptQueueSize"><Property name="https.acceptQueueSize" default="0"/></Set>
+      <New class="org.eclipse.jetty.server.SslConnectionFactory">
+        <Arg name="next">http/1.1</Arg>
+        <Arg name="sslContextFactory"><Ref refid="sslContextFactory"/></Arg>
       </New>
     </Arg>
   </Call>
+
+  <Call name="addConnectionFactory">
+    <Arg>
+      <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+        <Arg name="config"><Ref refid="sslHttpConfig" /></Arg>
+        <Arg name="compliance"><Call class="org.eclipse.jetty.http.HttpCompliance" name="valueOf"><Arg><Property name="jetty.http.compliance" default="RFC7230"/></Arg></Call></Arg>
+      </New>
+    </Arg>
+  </Call>
+  
 </Configure>
diff --git a/jetty-server/src/main/config/etc/jetty-ipaccess.xml b/jetty-server/src/main/config/etc/jetty-ipaccess.xml
index a44aa1d..832565c 100644
--- a/jetty-server/src/main/config/etc/jetty-ipaccess.xml
+++ b/jetty-server/src/main/config/etc/jetty-ipaccess.xml
@@ -1,31 +1,28 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
-<!-- Mixin the Statistics Handler                                    -->
+<!-- The IP Access Handler                                           -->
 <!-- =============================================================== -->
 
-
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
-
-    <Get id="oldhandler" name="handler"/>
-
-    <Set name="handler">
-     <New id="IPAccessHandler" class="org.eclipse.jetty.server.handler.IPAccessHandler">
-      <Set name="handler"><Ref refid="oldhandler"/></Set>
-      <Set name="white">
-        <Array type="String">
-	      <Item>127.0.0.1</Item>
-	      <Item>127.0.0.2/*.html</Item>
-	    </Array>
-      </Set>
-      <Set name="black">
-        <Array type="String">
-	      <Item>127.0.0.1/blacklisted</Item>
-	      <Item>127.0.0.2/black.html</Item>
-	    </Array>
-      </Set>
-      <Set name="whiteListByPath">false</Set>
-     </New>
-    </Set>
+  <Call name="insertHandler">
+    <Arg>
+      <New id="IPAccessHandler" class="org.eclipse.jetty.server.handler.IPAccessHandler">
+	<Set name="white">
+	  <Array type="String">
+	    <Item>127.0.0.1</Item>
+	    <Item>127.0.0.2/*.html</Item>
+	  </Array>
+	</Set>
+	<Set name="black">
+	  <Array type="String">
+	    <Item>127.0.0.1/blacklisted</Item>
+	    <Item>127.0.0.2/black.html</Item>
+	  </Array>
+	</Set>
+	<Set name="whiteListByPath">false</Set>
+      </New>
+    </Arg>
+  </Call>
 </Configure>
diff --git a/jetty-server/src/main/config/etc/jetty-jdbc-sessions.xml b/jetty-server/src/main/config/etc/jetty-jdbc-sessions.xml
new file mode 100644
index 0000000..46beaf2
--- /dev/null
+++ b/jetty-server/src/main/config/etc/jetty-jdbc-sessions.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+
+  <!-- ===================================================================== -->
+  <!-- Configure a SessionIdManager                                          -->
+  <!-- ===================================================================== -->
+  <Set name="sessionIdManager">
+    <New id="idMgr" class="org.eclipse.jetty.server.session.JDBCSessionIdManager">
+      <Arg>
+        <Ref refid="Server"/>
+      </Arg>
+      <Set name="workerName"><Property name="jetty.jdbcSession.workerName" default="node1"/></Set>
+      <Set name="scavengeInterval"><Property name="jetty.jdbcSession.scavenge" default="1800"/></Set>
+
+      <!-- ===================================================================== -->
+      <!-- Uncomment either the datasource or driver setup and configure         -->
+      <!-- ===================================================================== -->
+
+      <!--
+          <Set name="DatasourceName"><Property name="jetty.jdbcSession.datasource" default="javax.sql.DataSource/default"/></Set>
+      -->
+      <!--
+        <Call name="setDriverInfo">
+          <Arg><Property name="jetty.jdbcSession.driverClass"/></Arg>
+          <Arg><Property name="jetty.jdbcSession.connectionURL"/></Arg>
+        </Call>
+      -->
+    </New>
+  </Set>
+
+</Configure>
diff --git a/jetty-server/src/main/config/etc/jetty-lowresources.xml b/jetty-server/src/main/config/etc/jetty-lowresources.xml
index 060919a..f6442c8 100644
--- a/jetty-server/src/main/config/etc/jetty-lowresources.xml
+++ b/jetty-server/src/main/config/etc/jetty-lowresources.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Mixin the Low Resources Monitor                                 -->
@@ -10,12 +10,12 @@
     <Arg>
       <New class="org.eclipse.jetty.server.LowResourceMonitor">
         <Arg name="server"><Ref refid='Server'/></Arg>
-        <Set name="period"><Property name="lowresources.period" default="1000"/></Set>
-        <Set name="lowResourcesIdleTimeout"><Property name="lowresources.lowResourcesIdleTimeout" default="200"/></Set>
-        <Set name="monitorThreads"><Property name="lowresources.monitorThreads" default="true"/></Set>
-        <Set name="maxConnections"><Property name="lowresources.maxConnections" default="0"/></Set>
-        <Set name="maxMemory"><Property name="lowresources.maxMemory" default="0"/></Set>
-        <Set name="maxLowResourcesTime"><Property name="lowresources.maxLowResourcesTime" default="5000"/></Set>
+        <Set name="period"><Property name="jetty.lowresources.period" deprecated="lowresources.period" default="1000"/></Set>
+        <Set name="lowResourcesIdleTimeout"><Property name="jetty.lowresources.idleTimeout" deprecated="lowresources.lowResourcesIdleTimeout" default="1000"/></Set>
+        <Set name="monitorThreads"><Property name="jetty.lowresources.monitorThreads" deprecated="lowresources.monitorThreads" default="true"/></Set>
+        <Set name="maxConnections"><Property name="jetty.lowresources.maxConnections" deprecated="lowresources.maxConnections" default="0"/></Set>
+        <Set name="maxMemory"><Property name="jetty.lowresources.maxMemory" deprecated="lowresources.maxMemory" default="0"/></Set>
+        <Set name="maxLowResourcesTime"><Property name="jetty.lowresources.maxLowResourcesTime" deprecated="lowresources.maxLowResourcesTime" default="5000"/></Set>
       </New>
     </Arg>
   </Call>
diff --git a/jetty-server/src/main/config/etc/jetty-proxy-protocol-ssl.xml b/jetty-server/src/main/config/etc/jetty-proxy-protocol-ssl.xml
new file mode 100644
index 0000000..91452f2
--- /dev/null
+++ b/jetty-server/src/main/config/etc/jetty-proxy-protocol-ssl.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure id="sslConnector" class="org.eclipse.jetty.server.ServerConnector">
+  <Call name="addFirstConnectionFactory">
+    <Arg>
+      <New class="org.eclipse.jetty.server.ProxyConnectionFactory"/>
+    </Arg>
+  </Call>
+</Configure>
diff --git a/jetty-server/src/main/config/etc/jetty-proxy-protocol.xml b/jetty-server/src/main/config/etc/jetty-proxy-protocol.xml
new file mode 100644
index 0000000..5169c4f
--- /dev/null
+++ b/jetty-server/src/main/config/etc/jetty-proxy-protocol.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure id="httpConnector" class="org.eclipse.jetty.server.ServerConnector">
+  <Call name="addFirstConnectionFactory">
+    <Arg>
+      <New class="org.eclipse.jetty.server.ProxyConnectionFactory"/>
+    </Arg>
+  </Call>
+</Configure>
diff --git a/jetty-server/src/main/config/etc/jetty-requestlog.xml b/jetty-server/src/main/config/etc/jetty-requestlog.xml
index 135885e..89a4112 100644
--- a/jetty-server/src/main/config/etc/jetty-requestlog.xml
+++ b/jetty-server/src/main/config/etc/jetty-requestlog.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Configure the Jetty Request Log                                 -->
@@ -7,26 +7,24 @@
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
   <!-- =========================================================== -->
-  <!-- Configure Request Log -->
+  <!-- Configure Request Log for Server                            -->
+  <!-- (Use RequestLogHandler for a context specific RequestLog    -->
   <!-- =========================================================== -->
-  <Ref refid="Handlers">
-    <Call name="addHandler">
-      <Arg>
-        <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler">
-          <Set name="requestLog">
-            <New id="RequestLogImpl" class="org.eclipse.jetty.server.AsyncNCSARequestLog">
-              <Set name="filename"><Property name="jetty.base" default="." /><Property name="requestlog.filename" default="/logs/yyyy_mm_dd.request.log"/></Set>
-              <Set name="filenameDateFormat"><Property name="requestlog.filenameDateFormat" default="yyyy_MM_dd"/></Set>
-              <Set name="retainDays"><Property name="requestlog.retain" default="90"/></Set>
-              <Set name="append"><Property name="requestlog.append" default="false"/></Set>
-              <Set name="extended"><Property name="requestlog.extended" default="false"/></Set>
-              <Set name="logCookies"><Property name="requestlog.cookies" default="false"/></Set>
-              <Set name="LogTimeZone"><Property name="requestlog.timezone" default="GMT"/></Set>
-            </New>
-          </Set>
-        </New>
-      </Arg>
-    </Call>
-  </Ref>
-
+  <Set name="RequestLog">
+    <New id="RequestLog" class="org.eclipse.jetty.server.AsyncNCSARequestLog">
+      <Set name="filename"><Property name="jetty.base" default="." />/<Property>
+          <Name>jetty.requestlog.filePath</Name>
+          <Deprecated>requestlog.filename</Deprecated>
+          <Default><Property name="jetty.requestlog.dir" default="logs"/>/yyyy_mm_dd.request.log</Default>
+        </Property>
+      </Set>
+      <Set name="filenameDateFormat"><Property name="jetty.requestlog.filenameDateFormat" deprecated="requestlog.filenameDateFormat" default="yyyy_MM_dd"/></Set>
+      <Set name="retainDays"><Property name="jetty.requestlog.retainDays" deprecated="requestlog.retain" default="90"/></Set>
+      <Set name="append"><Property name="jetty.requestlog.append" deprecated="requestlog.append" default="false"/></Set>
+      <Set name="extended"><Property name="jetty.requestlog.extended" deprecated="requestlog.extended" default="false"/></Set>
+      <Set name="logCookies"><Property name="jetty.requestlog.cookies" deprecated="requestlog.cookies" default="false"/></Set>
+      <Set name="LogTimeZone"><Property name="jetty.requestlog.timezone" deprecated="requestlog.timezone" default="GMT"/></Set>
+      <Set name="LogLatency"><Property name="jetty.requestlog.loglatency" default="false"/></Set>
+    </New>
+  </Set>
 </Configure>
diff --git a/jetty-server/src/main/config/etc/jetty-ssl-context.xml b/jetty-server/src/main/config/etc/jetty-ssl-context.xml
new file mode 100644
index 0000000..3e84a9e
--- /dev/null
+++ b/jetty-server/src/main/config/etc/jetty-ssl-context.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<!-- ============================================================= -->
+<!-- SSL ContextFactory configuration                              -->
+<!-- ============================================================= -->
+
+<!-- 
+  To configure Includes / Excludes for Cipher Suites or Protocols see tweak-ssl.xml example at 
+     https://www.eclipse.org/jetty/documentation/current/configuring-ssl.html#configuring-sslcontextfactory-cipherSuites
+-->
+
+<Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
+  <Set name="KeyStorePath"><Property name="jetty.base" default="." />/<Property name="jetty.sslContext.keyStorePath" deprecated="jetty.keystore" default="etc/keystore"/></Set>
+  <Set name="KeyStorePassword"><Property name="jetty.sslContext.keyStorePassword" deprecated="jetty.keystore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
+  <Set name="KeyStoreType"><Property name="jetty.sslContext.keyStoreType" default="JKS"/></Set>
+  <Set name="KeyStoreProvider"><Property name="jetty.sslContext.keyStoreProvider"/></Set>
+  <Set name="KeyManagerPassword"><Property name="jetty.sslContext.keyManagerPassword" deprecated="jetty.keymanager.password" default="OBF:1u2u1wml1z7s1z7a1wnl1u2g"/></Set>
+  <Set name="TrustStorePath"><Property name="jetty.base" default="." />/<Property name="jetty.sslContext.trustStorePath" deprecated="jetty.truststore" default="etc/keystore"/></Set>
+  <Set name="TrustStorePassword"><Property name="jetty.sslContext.trustStorePassword" deprecated="jetty.truststore.password"/></Set>
+  <Set name="TrustStoreType"><Property name="jetty.sslContext.trustStoreType"/></Set>
+  <Set name="TrustStoreProvider"><Property name="jetty.sslContext.trustStoreProvider"/></Set>
+  <Set name="EndpointIdentificationAlgorithm"></Set>
+  <Set name="NeedClientAuth"><Property name="jetty.sslContext.needClientAuth" deprecated="jetty.ssl.needClientAuth" default="false"/></Set>
+  <Set name="WantClientAuth"><Property name="jetty.sslContext.wantClientAuth" deprecated="jetty.ssl.wantClientAuth" default="false"/></Set>
+  <Set name="useCipherSuitesOrder"><Property name="jetty.sslContext.useCipherSuitesOrder" default="true"/></Set>
+  <Set name="sslSessionCacheSize"><Property name="jetty.sslContext.sslSessionCacheSize" default="-1"/></Set>
+  <Set name="sslSessionTimeout"><Property name="jetty.sslContext.sslSessionTimeout" default="-1"/></Set>
+  <Set name="RenegotiationAllowed"><Property name="jetty.sslContext.renegotiationAllowed" default="true"/></Set>
+  <Set name="RenegotiationLimit"><Property name="jetty.sslContext.renegotiationLimit" default="5"/></Set>
+</Configure>
diff --git a/jetty-server/src/main/config/etc/jetty-ssl.xml b/jetty-server/src/main/config/etc/jetty-ssl.xml
index 4ac2d3e..a079c1f 100644
--- a/jetty-server/src/main/config/etc/jetty-ssl.xml
+++ b/jetty-server/src/main/config/etc/jetty-ssl.xml
@@ -1,31 +1,40 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- ============================================================= -->
-<!-- Configure a TLS (SSL) Context Factory                         -->
-<!-- This configuration must be used in conjunction with jetty.xml -->
-<!-- and either jetty-https.xml or jetty-spdy.xml (but not both)   -->
+<!-- Base SSL configuration                                        -->
+<!-- This configuration needs to be used together with 1 or more   -->
+<!-- of jetty-https.xml or jetty-http2.xml                         -->
 <!-- ============================================================= -->
-<Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
-  <Set name="KeyStorePath"><Property name="jetty.base" default="." />/<Property name="jetty.keystore" default="etc/keystore"/></Set>
-  <Set name="KeyStorePassword"><Property name="jetty.keystore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
-  <Set name="KeyManagerPassword"><Property name="jetty.keymanager.password" default="OBF:1u2u1wml1z7s1z7a1wnl1u2g"/></Set>
-  <Set name="TrustStorePath"><Property name="jetty.base" default="." />/<Property name="jetty.truststore" default="etc/keystore"/></Set>
-  <Set name="TrustStorePassword"><Property name="jetty.truststore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
-  <Set name="EndpointIdentificationAlgorithm"></Set>
-  <Set name="NeedClientAuth"><Property name="jetty.ssl.needClientAuth" default="false"/></Set>
-  <Set name="WantClientAuth"><Property name="jetty.ssl.wantClientAuth" default="false"/></Set>
-  <Set name="ExcludeCipherSuites">
-    <Array type="String">
-      <Item>SSL_RSA_WITH_DES_CBC_SHA</Item>
-      <Item>SSL_DHE_RSA_WITH_DES_CBC_SHA</Item>
-      <Item>SSL_DHE_DSS_WITH_DES_CBC_SHA</Item>
-      <Item>SSL_RSA_EXPORT_WITH_RC4_40_MD5</Item>
-      <Item>SSL_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
-      <Item>SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
-      <Item>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</Item>
-    </Array>
-  </Set>
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+  <!-- =========================================================== -->
+  <!-- Add a SSL Connector with no protocol factories              -->
+  <!-- =========================================================== -->
+  <Call  name="addConnector">
+    <Arg>
+      <New id="sslConnector" class="org.eclipse.jetty.server.ServerConnector">
+        <Arg name="server"><Ref refid="Server" /></Arg>
+        <Arg name="acceptors" type="int"><Property name="jetty.ssl.acceptors" deprecated="ssl.acceptors" default="-1"/></Arg>
+        <Arg name="selectors" type="int"><Property name="jetty.ssl.selectors" deprecated="ssl.selectors" default="-1"/></Arg>
+        <Arg name="factories">
+          <Array type="org.eclipse.jetty.server.ConnectionFactory">
+            <!-- uncomment to support proxy protocol
+            <Item>
+              <New class="org.eclipse.jetty.server.ProxyConnectionFactory"/>
+            </Item>-->
+          </Array>
+        </Arg>
+
+        <Set name="host"><Property name="jetty.ssl.host" deprecated="jetty.host" /></Set>
+        <Set name="port"><Property name="jetty.ssl.port" deprecated="ssl.port" default="8443" /></Set>
+        <Set name="idleTimeout"><Property name="jetty.ssl.idleTimeout" deprecated="ssl.timeout" default="30000"/></Set>
+        <Set name="soLingerTime"><Property name="jetty.ssl.soLingerTime" deprecated="ssl.soLingerTime" default="-1"/></Set>
+        <Set name="acceptorPriorityDelta"><Property name="jetty.ssl.acceptorPriorityDelta" deprecated="ssl.acceptorPriorityDelta" default="0"/></Set>
+        <Set name="acceptQueueSize"><Property name="jetty.ssl.acceptQueueSize" deprecated="ssl.acceptQueueSize" default="0"/></Set>
+      </New>
+    </Arg>
+  </Call>
 
   <!-- =========================================================== -->
   <!-- Create a TLS specific HttpConfiguration based on the        -->
@@ -36,7 +45,13 @@
   <New id="sslHttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
     <Arg><Ref refid="httpConfig"/></Arg>
     <Call name="addCustomizer">
-      <Arg><New class="org.eclipse.jetty.server.SecureRequestCustomizer"/></Arg>
+      <Arg>
+        <New class="org.eclipse.jetty.server.SecureRequestCustomizer">
+          <Arg name="sniHostCheck" type="boolean"><Property name="jetty.ssl.sniHostCheck" default="true"/></Arg>
+          <Arg name="stsMaxAgeSeconds" type="int"><Property name="jetty.ssl.stsMaxAgeSeconds" default="-1"/></Arg>
+          <Arg name="stsIncludeSubdomains" type="boolean"><Property name="jetty.ssl.stsIncludeSubdomains" default="false"/></Arg>
+        </New>
+      </Arg>
     </Call>
   </New>
 
diff --git a/jetty-server/src/main/config/etc/jetty-stats.xml b/jetty-server/src/main/config/etc/jetty-stats.xml
index 2e7a57c..954acd7 100644
--- a/jetty-server/src/main/config/etc/jetty-stats.xml
+++ b/jetty-server/src/main/config/etc/jetty-stats.xml
@@ -1,18 +1,18 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Mixin the Statistics Handler                                    -->
 <!-- =============================================================== -->
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
-  <Get id="oldhandler" name="handler" />
-  <Set name="handler">
-    <New id="StatsHandler" class="org.eclipse.jetty.server.handler.StatisticsHandler">
-      <Set name="handler"><Ref refid="oldhandler" /></Set>
-    </New>
-  </Set>
-  <Call class="org.eclipse.jetty.server.ConnectorStatistics" name="addToAllConnectors">
+  <Call name="insertHandler">
+    <Arg>
+      <New id="StatsHandler" class="org.eclipse.jetty.server.handler.StatisticsHandler">
+      </New>
+    </Arg>
+  </Call>
+  <Call class="org.eclipse.jetty.server.ServerConnectionStatistics" name="addToAllConnectors">
     <Arg><Ref refid="Server"/></Arg>
   </Call>
 </Configure>
diff --git a/jetty-server/src/main/config/etc/jetty-threadlimit.xml b/jetty-server/src/main/config/etc/jetty-threadlimit.xml
new file mode 100644
index 0000000..11376e0
--- /dev/null
+++ b/jetty-server/src/main/config/etc/jetty-threadlimit.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<!-- =============================================================== -->
+<!-- Mixin the Thread Limit Handler to the entire server             -->
+<!-- =============================================================== -->
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+  <Call name="insertHandler">
+    <Arg>
+      <New id="ThreadLimitHandler" class="org.eclipse.jetty.server.handler.ThreadLimitHandler">
+        <Arg name="forwardedHeader"><Property name="jetty.threadlimit.forwardedHeader"/></Arg> 
+        <Set name="enabled"><Property name="jetty.threadlimit.enabled" default="true"/></Set> 
+        <Set name="threadLimit"><Property name="jetty.threadlimit.threadLimit" default="10"/></Set> 
+      </New>
+    </Arg>
+  </Call>
+</Configure>
+
+
diff --git a/jetty-server/src/main/config/etc/jetty-xinetd.xml b/jetty-server/src/main/config/etc/jetty-xinetd.xml
deleted file mode 100644
index a4750de..0000000
--- a/jetty-server/src/main/config/etc/jetty-xinetd.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<!-- =============================================================== -->
-<!-- Configuration for starting up Jetty using inetd/xinetd          -->
-<!-- This feature requires at least Java 5                           -->
-<!--                                                                 -->
-<!-- Making it a mixin for convenience, but note that if used        -->
-<!-- with jetty.xml, Jetty will use multiple connectors              -->
-<!-- =============================================================== -->
-
-<!-- Sample xinetd configuration (restart xinetd after adding the configuration file)
-
-service jetty
-{
-    disable     = no
-
-    id          = jetty
-    type        = UNLISTED
-    wait        = yes
-    socket_type = stream
-
-    # change this
-    user        = username
-    group       = groupname
-    port        = 2001
-
-    # sample script for running jetty as a service
-    # replace $JETTY_HOME with /path/to/jetty_home/
-    server      = $JETTY_HOME/bin/jetty-xinetd.sh
-}
-
--->
-
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
-    <Call name="addConnector">
-      <Arg>
-          <!-- Inherited channel (from inetd/xinetd) -->
-          <New class="org.eclipse.jetty.server.nio.InheritedChannelConnector">
-
-
-            <!-- Optional. Fallback in case System.inheritedChannel() does not give a ServerSocketChannel
-            <Set name="port"><Property name="jetty.service.port" default="8082"/></Set>
-            -->
-
-            <!-- sane defaults -->
-            <Set name="idleTimeout"><Property name="jetty.xinetd.idleTimeout" default="300000"/></Set>
-            <Set name="Acceptors"><Property name="jetty.xinetd.acceptors" default="2"/></Set>
-            <Set name="statsOn"><Property name="jetty.xinetd.statsOn" default="false"/></Set>
-          </New>
-      </Arg>
-    </Call>
-</Configure>
-
diff --git a/jetty-server/src/main/config/etc/jetty.xml b/jetty-server/src/main/config/etc/jetty.xml
index d8708ef..f85a955 100644
--- a/jetty-server/src/main/config/etc/jetty.xml
+++ b/jetty-server/src/main/config/etc/jetty.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Documentation of this file format can be found at:              -->
@@ -46,9 +46,9 @@
     <Arg name="threadpool"><New id="threadpool" class="org.eclipse.jetty.util.thread.QueuedThreadPool"/></Arg>
     -->
     <Get name="ThreadPool">
-      <Set name="minThreads" type="int"><Property name="threads.min" default="10"/></Set>
-      <Set name="maxThreads" type="int"><Property name="threads.max" default="200"/></Set>
-      <Set name="idleTimeout" type="int"><Property name="threads.timeout" default="60000"/></Set>
+      <Set name="minThreads" type="int"><Property name="jetty.threadPool.minThreads" deprecated="threads.min" default="10"/></Set>
+      <Set name="maxThreads" type="int"><Property name="jetty.threadPool.maxThreads" deprecated="threads.max" default="200"/></Set>
+      <Set name="idleTimeout" type="int"><Property name="jetty.threadPool.idleTimeout" deprecated="threads.timeout" default="60000"/></Set>
       <Set name="detailedDump">false</Set>
     </Get>
 
@@ -64,37 +64,34 @@
     <!-- =========================================================== -->
     <!-- Http Configuration.                                         -->
     <!-- This is a common configuration instance used by all         -->
-    <!-- connectors that can carry HTTP semantics (HTTP, HTTPS, SPDY)-->
+    <!-- connectors that can carry HTTP semantics (HTTP, HTTPS, etc.)-->
     <!-- It configures the non wire protocol aspects of the HTTP     -->
     <!-- semantic.                                                   -->
     <!--                                                             -->
     <!-- This configuration is only defined here and is used by      -->
-    <!-- reference from the jetty-http.xml, jetty-https.xml and      -->
-    <!-- jetty-spdy.xml configuration files which instantiate the    -->
-    <!-- connectors.                                                 -->
+    <!-- reference from other XML files such as jetty-http.xml,      -->
+    <!-- jetty-https.xml and other configuration files which         -->
+    <!-- instantiate the connectors.                                 -->
     <!--                                                             -->
     <!-- Consult the javadoc of o.e.j.server.HttpConfiguration       -->
     <!-- for all configuration that may be set here.                 -->
     <!-- =========================================================== -->
     <New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
-      <Set name="secureScheme">https</Set>
-      <Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
-      <Set name="outputBufferSize"><Property name="jetty.output.buffer.size" default="32768" /></Set>
-      <Set name="outputAggregationSize"><Property name="jetty.output.aggregation.size" default="8192" /></Set>
-      <Set name="requestHeaderSize"><Property name="jetty.request.header.size" default="8192" /></Set>
-      <Set name="responseHeaderSize"><Property name="jetty.response.header.size" default="8192" /></Set>
-      <Set name="sendServerVersion"><Property name="jetty.send.server.version" default="true" /></Set>
-      <Set name="sendDateHeader"><Property name="jetty.send.date.header" default="false" /></Set>
-      <Set name="headerCacheSize">512</Set>
-      <Set name="delayDispatchUntilContent"><Property name="jetty.delayDispatchUntilContent" default="false"/></Set>
-      <!-- Uncomment to enable handling of X-Forwarded- style headers
-      <Call name="addCustomizer">
-        <Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
-      </Call>
-      -->
+      <Set name="secureScheme"><Property name="jetty.httpConfig.secureScheme" default="https" /></Set>
+      <Set name="securePort"><Property name="jetty.httpConfig.securePort" deprecated="jetty.secure.port" default="8443" /></Set>
+      <Set name="outputBufferSize"><Property name="jetty.httpConfig.outputBufferSize" deprecated="jetty.output.buffer.size" default="32768" /></Set>
+      <Set name="outputAggregationSize"><Property name="jetty.httpConfig.outputAggregationSize" deprecated="jetty.output.aggregation.size" default="8192" /></Set>
+      <Set name="requestHeaderSize"><Property name="jetty.httpConfig.requestHeaderSize" deprecated="jetty.request.header.size" default="8192" /></Set>
+      <Set name="responseHeaderSize"><Property name="jetty.httpConfig.responseHeaderSize" deprecated="jetty.response.header.size" default="8192" /></Set>
+      <Set name="sendServerVersion"><Property name="jetty.httpConfig.sendServerVersion" deprecated="jetty.send.server.version" default="true" /></Set>
+      <Set name="sendDateHeader"><Property name="jetty.httpConfig.sendDateHeader" deprecated="jetty.send.date.header" default="false" /></Set>
+      <Set name="headerCacheSize"><Property name="jetty.httpConfig.headerCacheSize" default="512" /></Set>
+      <Set name="delayDispatchUntilContent"><Property name="jetty.httpConfig.delayDispatchUntilContent" deprecated="jetty.delayDispatchUntilContent" default="true"/></Set>
+      <Set name="maxErrorDispatches"><Property name="jetty.httpConfig.maxErrorDispatches" default="10"/></Set>
+      <Set name="blockingTimeout"><Property name="jetty.httpConfig.blockingTimeout" default="-1"/></Set>
+      <Set name="persistentConnectionsEnabled"><Property name="jetty.httpConfig.persistentConnectionsEnabled" default="true"/></Set>
     </New>
 
-
     <!-- =========================================================== -->
     <!-- Set the default handler structure for the Server            -->
     <!-- A handler collection is used to pass received requests to   -->
@@ -124,9 +121,9 @@
     <!-- =========================================================== -->
     <!-- extra server options                                        -->
     <!-- =========================================================== -->
-    <Set name="stopAtShutdown">true</Set>
-    <Set name="stopTimeout">5000</Set>
-    <Set name="dumpAfterStart"><Property name="jetty.dump.start" default="false"/></Set>
-    <Set name="dumpBeforeStop"><Property name="jetty.dump.stop" default="false"/></Set>
+    <Set name="stopAtShutdown"><Property name="jetty.server.stopAtShutdown" default="true"/></Set>
+    <Set name="stopTimeout"><Property name="jetty.server.stopTimeout" default="5000"/></Set>
+    <Set name="dumpAfterStart"><Property name="jetty.server.dumpAfterStart" deprecated="jetty.dump.start" default="false"/></Set>
+    <Set name="dumpBeforeStop"><Property name="jetty.server.dumpBeforeStop" deprecated="jetty.dump.stop" default="false"/></Set>
 
 </Configure>
diff --git a/jetty-server/src/main/config/etc/keystore b/jetty-server/src/main/config/etc/keystore
index 08f6cda..d6592f9 100644
--- a/jetty-server/src/main/config/etc/keystore
+++ b/jetty-server/src/main/config/etc/keystore
Binary files differ
diff --git a/jetty-server/src/main/config/modules/debug.mod b/jetty-server/src/main/config/modules/debug.mod
index f740ea2..0141699 100644
--- a/jetty-server/src/main/config/modules/debug.mod
+++ b/jetty-server/src/main/config/modules/debug.mod
@@ -3,7 +3,27 @@
 #
 
 [depend]
-server
+deploy
+
+[files]
+logs/
 
 [xml]
 etc/jetty-debug.xml
+
+[ini-template]
+
+## How many days to retain old log files
+# jetty.debug.retainDays=14
+
+## Timezone of the log entries
+# jetty.debug.timezone=GMT
+
+## Show Request/Response headers
+# jetty.debug.showHeaders=true
+
+## Rename threads while in context scope
+# jetty.debug.renameThread=false
+
+## Dump context as deployed
+# jetty.debug.dumpContext=true
diff --git a/jetty-server/src/main/config/modules/debuglog.mod b/jetty-server/src/main/config/modules/debuglog.mod
new file mode 100644
index 0000000..ba8b60a
--- /dev/null
+++ b/jetty-server/src/main/config/modules/debuglog.mod
@@ -0,0 +1,25 @@
+#
+# Debug module
+#
+
+[depend]
+server
+
+[files]
+logs/
+
+[xml]
+etc/jetty-debuglog.xml
+
+[ini-template]
+## Logging directory (relative to $jetty.base)
+# jetty.debuglog.dir=logs
+
+## Whether to append to existing file
+# jetty.debuglog.append=false
+
+## How many days to retain old log files
+# jetty.debuglog.retainDays=90
+
+## Timezone of the log entries
+# jetty.debuglog.timezone=GMT
diff --git a/jetty-server/src/main/config/modules/flight-recorder.mod b/jetty-server/src/main/config/modules/flight-recorder.mod
new file mode 100644
index 0000000..219173d
--- /dev/null
+++ b/jetty-server/src/main/config/modules/flight-recorder.mod
@@ -0,0 +1,13 @@
+# Enables Java Mission Control's Flight Recorder for low overhead profiling.
+
+[depend]
+server
+
+[exec]
+-XX:+UnlockCommercialFeatures
+-XX:+FlightRecorder
+
+[license]
+Java Flight Recorder requires a commercial license for use in production.
+To learn more about commercial features and how to enable them please visit
+http://www.oracle.com/technetwork/java/javaseproducts/
diff --git a/jetty-server/src/main/config/modules/gzip.mod b/jetty-server/src/main/config/modules/gzip.mod
new file mode 100644
index 0000000..1efc834
--- /dev/null
+++ b/jetty-server/src/main/config/modules/gzip.mod
@@ -0,0 +1,23 @@
+#
+# GZIP module
+# Applies GzipHandler to entire server
+#
+
+[depend]
+server
+
+[xml]
+etc/jetty-gzip.xml
+
+[ini-template]
+## Minimum content length after which gzip is enabled
+# jetty.gzip.minGzipSize=2048
+
+## Check whether a file with *.gz extension exists
+# jetty.gzip.checkGzExists=false
+
+## Gzip compression level (-1 for default)
+# jetty.gzip.compressionLevel=-1
+
+## User agents for which gzip is disabled
+# jetty.gzip.excludedUserAgent=.*MSIE.6\.0.*
diff --git a/jetty-server/src/main/config/modules/http-forwarded.mod b/jetty-server/src/main/config/modules/http-forwarded.mod
new file mode 100644
index 0000000..435a686
--- /dev/null
+++ b/jetty-server/src/main/config/modules/http-forwarded.mod
@@ -0,0 +1,24 @@
+#
+# Jetty HTTP Connector
+#
+
+[depend]
+http
+
+[xml]
+etc/jetty-http-forwarded.xml
+
+[ini-template]
+### ForwardedRequestCustomizer Configuration
+
+# jetty.httpConfig.forwardedOnly=false
+# jetty.httpConfig.forwardedProxyAsAuthority=false
+# jetty.httpConfig.forwardedHeader=Forwarded
+# jetty.httpConfig.forwardedHostHeader=X-Forwarded-Host
+# jetty.httpConfig.forwardedServerHeader=X-Forwarded-Server
+# jetty.httpConfig.forwardedProtoHeader=X-Forwarded-Proto
+# jetty.httpConfig.forwardedForHeader=X-Forwarded-For
+# jetty.httpConfig.forwardedHttpsHeader=X-Proxied-Https
+# jetty.httpConfig.forwardedSslSessionIdHeader=Proxy-ssl-id
+# jetty.httpConfig.forwardedCipherSuiteHeader=Proxy-auth-cert
+
diff --git a/jetty-server/src/main/config/modules/http.mod b/jetty-server/src/main/config/modules/http.mod
index dc34bc3..d729bc2 100644
--- a/jetty-server/src/main/config/modules/http.mod
+++ b/jetty-server/src/main/config/modules/http.mod
@@ -11,17 +11,29 @@
 [ini-template]
 ### HTTP Connector Configuration
 
-## HTTP port to listen on
-jetty.port=8080
+## Connector host/address to bind to
+# jetty.http.host=0.0.0.0
 
-## HTTP idle timeout in milliseconds
-http.timeout=30000
+## Connector port to listen on
+# jetty.http.port=8080
 
-## HTTP Socket.soLingerTime in seconds. (-1 to disable)
-# http.soLingerTime=-1
+## Connector idle timeout in milliseconds
+# jetty.http.idleTimeout=30000
 
-## Parameters to control the number and priority of acceptors and selectors
-# http.selectors=1
-# http.acceptors=1
-# http.selectorPriorityDelta=0
-# http.acceptorPriorityDelta=0
+## Connector socket linger time in seconds (-1 to disable)
+# jetty.http.soLingerTime=-1
+
+## Number of acceptors (-1 picks default based on number of cores)
+# jetty.http.acceptors=-1
+
+## Number of selectors (-1 picks default based on number of cores)
+# jetty.http.selectors=-1
+
+## ServerSocketChannel backlog (0 picks platform default)
+# jetty.http.acceptorQueueSize=0
+
+## Thread priority delta to give to acceptor threads
+# jetty.http.acceptorPriorityDelta=0
+
+## HTTP Compliance: RFC7230, RFC2616, LEGACY
+# jetty.http.compliance=RFC7230
diff --git a/jetty-server/src/main/config/modules/https.mod b/jetty-server/src/main/config/modules/https.mod
index bd1b718..a481079 100644
--- a/jetty-server/src/main/config/modules/https.mod
+++ b/jetty-server/src/main/config/modules/https.mod
@@ -5,15 +5,10 @@
 [depend]
 ssl
 
+[optional]
+http2
+http-forwarded
+
 [xml]
 etc/jetty-https.xml
 
-[ini-template]
-## HTTPS Configuration
-# HTTP port to listen on
-https.port=8443
-# HTTPS idle timeout in milliseconds
-https.timeout=30000
-# HTTPS Socket.soLingerTime in seconds. (-1 to disable)
-# https.soLingerTime=-1
-
diff --git a/jetty-server/src/main/config/modules/jdbc-sessions.mod b/jetty-server/src/main/config/modules/jdbc-sessions.mod
new file mode 100644
index 0000000..d77ff04
--- /dev/null
+++ b/jetty-server/src/main/config/modules/jdbc-sessions.mod
@@ -0,0 +1,27 @@
+#
+# Jetty JDBC Session module
+#
+
+[depend]
+annotations
+webapp
+
+[xml]
+etc/jetty-jdbc-sessions.xml
+
+
+[ini-template]
+## JDBC Session config
+
+## Unique identifier for this node in the cluster
+# jetty.jdbcSession.workerName=node1
+
+## The interval in seconds between sweeps of the scavenger
+# jetty.jdbcSession.scavenge=600
+
+## Uncomment either the datasource name or driverClass and connectionURL
+# jetty.jdbcSession.datasource=sessions
+# jetty.jdbcSession.driverClass=changeme
+# jetty.jdbcSession.connectionURL=changeme
+
+
diff --git a/jetty-server/src/main/config/modules/jvm.mod b/jetty-server/src/main/config/modules/jvm.mod
index 195521c..c1b6381 100644
--- a/jetty-server/src/main/config/modules/jvm.mod
+++ b/jetty-server/src/main/config/modules/jvm.mod
@@ -20,4 +20,3 @@
 # -XX:+PrintTenuringDistribution
 # -XX:+PrintCommandLineFlags
 # -XX:+DisableExplicitGC
-# -Dorg.apache.jasper.compiler.disablejsr199=true
diff --git a/jetty-server/src/main/config/modules/lowresources.mod b/jetty-server/src/main/config/modules/lowresources.mod
index 99112d5..2f765d9 100644
--- a/jetty-server/src/main/config/modules/lowresources.mod
+++ b/jetty-server/src/main/config/modules/lowresources.mod
@@ -9,10 +9,20 @@
 etc/jetty-lowresources.xml
 
 [ini-template]
-## Low Resources Configuration
-# lowresources.period=1050
-# lowresources.lowResourcesIdleTimeout=200
-# lowresources.monitorThreads=true
-# lowresources.maxConnections=0
-# lowresources.maxMemory=0
-# lowresources.maxLowResourcesTime=5000
+## Scan period to look for low resources (in milliseconds)
+# jetty.lowresources.period=1000
+
+## The idle timeout to apply to low resources (in milliseconds)
+# jetty.lowresources.idleTimeout=1000
+
+## Whether to monitor ThreadPool threads for low resources
+# jetty.lowresources.monitorThreads=true
+
+## Max number of connections allowed before being in low resources mode
+# jetty.lowresources.maxConnections=0
+
+## Max memory allowed before being in low resources mode (in bytes)
+# jetty.lowresources.maxMemory=0
+
+## Max time a resource may stay in low resource mode before actions are taken (in milliseconds)
+# jetty.lowresources.maxLowResourcesTime=5000
diff --git a/jetty-server/src/main/config/modules/proxy-protocol-ssl.mod b/jetty-server/src/main/config/modules/proxy-protocol-ssl.mod
new file mode 100644
index 0000000..764d24b
--- /dev/null
+++ b/jetty-server/src/main/config/modules/proxy-protocol-ssl.mod
@@ -0,0 +1,9 @@
+#
+# PROXY Protocol Module - SSL
+#
+
+[depend]
+ssl
+
+[xml]
+etc/jetty-proxy-protocol-ssl.xml
diff --git a/jetty-server/src/main/config/modules/proxy-protocol.mod b/jetty-server/src/main/config/modules/proxy-protocol.mod
new file mode 100644
index 0000000..9df2700
--- /dev/null
+++ b/jetty-server/src/main/config/modules/proxy-protocol.mod
@@ -0,0 +1,9 @@
+#
+# PROXY Protocol Module - HTTP
+#
+
+[depend]
+http
+
+[xml]
+etc/jetty-proxy-protocol.xml
diff --git a/jetty-server/src/main/config/modules/requestlog.mod b/jetty-server/src/main/config/modules/requestlog.mod
index f5e0614..3f4ee13 100644
--- a/jetty-server/src/main/config/modules/requestlog.mod
+++ b/jetty-server/src/main/config/modules/requestlog.mod
@@ -12,19 +12,29 @@
 logs/
 
 [ini-template]
-## Request Log Configuration
-# Filename for Request Log output (relative to jetty.base)
-# requestlog.filename=/logs/yyyy_mm_dd.request.log
-# Date format for rollovered files (uses SimpleDateFormat syntax)
-# requestlog.filenameDateFormat=yyyy_MM_dd
-# How many days to retain the logs
-# requestlog.retain=90
-# If an existing log with the same name is found, just append to it
-# requestlog.append=true
-# Use the extended log output
-# requestlog.extended=true
-# Log http cookie information as well
-# requestlog.cookies=true
-# Set the log output timezone
-# requestlog.timezone=GMT
+## Logging directory (relative to $jetty.base)
+# jetty.requestlog.dir=logs
 
+## File path
+# jetty.requestlog.filePath=${jetty.requestlog.dir}/yyyy_mm_dd.request.log
+
+## Date format for rollovered files (uses SimpleDateFormat syntax)
+# jetty.requestlog.filenameDateFormat=yyyy_MM_dd
+
+## How many days to retain old log files
+# jetty.requestlog.retainDays=90
+
+## Whether to append to existing file
+# jetty.requestlog.append=true
+
+## Whether to use the extended log output
+# jetty.requestlog.extended=true
+
+## Whether to log http cookie information
+# jetty.requestlog.cookies=true
+
+## Timezone of the log entries
+# jetty.requestlog.timezone=GMT
+
+## Whether to log LogLatency
+# jetty.requestlog.loglatency=false
\ No newline at end of file
diff --git a/jetty-server/src/main/config/modules/server.mod b/jetty-server/src/main/config/modules/server.mod
index b3f87de..a3bbecf 100644
--- a/jetty-server/src/main/config/modules/server.mod
+++ b/jetty-server/src/main/config/modules/server.mod
@@ -20,30 +20,63 @@
 etc/jetty.xml
 
 [ini-template]
-##
-## Server Threading Configuration
-##
-# minimum number of threads
-threads.min=10
-# maximum number of threads
-threads.max=200
-# thread idle timeout in milliseconds
-threads.timeout=60000
-# buffer size for output
-jetty.output.buffer.size=32768
-# request header buffer size
-jetty.request.header.size=8192
-# response header buffer size
-jetty.response.header.size=8192
-# should jetty send the server version header?
-jetty.send.server.version=true
-# should jetty send the date header?
-jetty.send.date.header=false
-# What host to listen on (leave commented to listen on all interfaces)
-#jetty.host=myhost.com
-# Dump the state of the Jetty server, components, and webapps after startup
-jetty.dump.start=false
-# Dump the state of the Jetty server, before stop
-jetty.dump.stop=false
-# Enable delayed dispatch optimisation
-jetty.delayDispatchUntilContent=false
+### ThreadPool configuration
+## Minimum number of threads
+# jetty.threadPool.minThreads=10
+
+## Maximum number of threads
+# jetty.threadPool.maxThreads=200
+
+## Thread idle timeout (in milliseconds)
+# jetty.threadPool.idleTimeout=60000
+
+### Common HTTP configuration
+## Scheme to use to build URIs for secure redirects
+# jetty.httpConfig.secureScheme=https
+
+## Port to use to build URIs for secure redirects
+# jetty.httpConfig.securePort=8443
+
+## Response content buffer size (in bytes)
+# jetty.httpConfig.outputBufferSize=32768
+
+## Max response content write length that is buffered (in bytes)
+# jetty.httpConfig.outputAggregationSize=8192
+
+## Max request headers size (in bytes)
+# jetty.httpConfig.requestHeaderSize=8192
+
+## Max response headers size (in bytes)
+# jetty.httpConfig.responseHeaderSize=8192
+
+## Whether to send the Server: header
+# jetty.httpConfig.sendServerVersion=true
+
+## Whether to send the Date: header
+# jetty.httpConfig.sendDateHeader=false
+
+## Max per-connection header cache size (in nodes)
+# jetty.httpConfig.headerCacheSize=512
+
+## Whether, for requests with content, delay dispatch until some content has arrived
+# jetty.httpConfig.delayDispatchUntilContent=true
+
+## Maximum number of error dispatches to prevent looping
+# jetty.httpConfig.maxErrorDispatches=10
+
+## Maximum time to block in total for a blocking IO operation (default -1 is to use idleTimeout on progress)
+# jetty.httpConfig.blockingTimeout=-1
+
+### Server configuration
+## Whether ctrl+c on the console gracefully stops the Jetty server
+# jetty.server.stopAtShutdown=true
+
+## Timeout in ms to apply when stopping the server gracefully
+# jetty.server.stopTimeout=5000
+
+## Dump the state of the Jetty server, components, and webapps after startup
+# jetty.server.dumpAfterStart=false
+
+## Dump the state of the Jetty server, components, and webapps before shutdown
+# jetty.server.dumpBeforeStop=false
+
diff --git a/jetty-server/src/main/config/modules/ssl.mod b/jetty-server/src/main/config/modules/ssl.mod
index a2fdfd1..7638d25 100644
--- a/jetty-server/src/main/config/modules/ssl.mod
+++ b/jetty-server/src/main/config/modules/ssl.mod
@@ -2,39 +2,100 @@
 # SSL Keystore module
 #
 
+[name]
+ssl
+
 [depend]
 server
 
 [xml]
 etc/jetty-ssl.xml
+etc/jetty-ssl-context.xml
 
 [files]
 https://raw.githubusercontent.com/eclipse/jetty.project/master/jetty-server/src/test/config/etc/keystore?id=${jetty.tag.version}|etc/keystore
 
 [ini-template]
-### SSL Keystore Configuration
-# define the port to use for secure redirection
-jetty.secure.port=8443
+### TLS(SSL) Connector Configuration
 
-## Setup a demonstration keystore and truststore
-jetty.keystore=etc/keystore
-jetty.truststore=etc/keystore
+## Connector host/address to bind to
+# jetty.ssl.host=0.0.0.0
 
-## Set the demonstration passwords.
+## Connector port to listen on
+# jetty.ssl.port=8443
+
+## Connector idle timeout in milliseconds
+# jetty.ssl.idleTimeout=30000
+
+## Connector socket linger time in seconds (-1 to disable)
+# jetty.ssl.soLingerTime=-1
+
+## Number of acceptors (-1 picks default based on number of cores)
+# jetty.ssl.acceptors=-1
+
+## Number of selectors (-1 picks default based on number of cores)
+# jetty.ssl.selectors=-1
+
+## ServerSocketChannel backlog (0 picks platform default)
+# jetty.ssl.acceptorQueueSize=0
+
+## Thread priority delta to give to acceptor threads
+# jetty.ssl.acceptorPriorityDelta=0
+
+## Whether request host names are checked to match any SNI names
+# jetty.ssl.sniHostCheck=true
+
+## max age in seconds for a Strict-Transport-Security response header (default -1)
+# jetty.ssl.stsMaxAgeSeconds=31536000
+
+## include subdomain property in any Strict-Transport-Security header (default false)
+# jetty.ssl.stsIncludeSubdomains=true
+
+### SslContextFactory Configuration
 ## Note that OBF passwords are not secure, just protected from casual observation
 ## See http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html
-jetty.keystore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
-jetty.keymanager.password=OBF:1u2u1wml1z7s1z7a1wnl1u2g
-jetty.truststore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
 
-### Set the client auth behavior
-## Set to true if client certificate authentication is required
-# jetty.ssl.needClientAuth=true
-## Set to true if client certificate authentication is desired
-# jetty.ssl.wantClientAuth=true
+## Keystore file path (relative to $jetty.base)
+# jetty.sslContext.keyStorePath=etc/keystore
 
-## Parameters to control the number and priority of acceptors and selectors
-# ssl.selectors=1
-# ssl.acceptors=1
-# ssl.selectorPriorityDelta=0
-# ssl.acceptorPriorityDelta=0
+## Truststore file path (relative to $jetty.base)
+# jetty.sslContext.trustStorePath=etc/keystore
+
+## Keystore password
+# jetty.sslContext.keyStorePassword=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
+
+## Keystore type and provider
+# jetty.sslContext.keyStoreType=JKS
+# jetty.sslContext.keyStoreProvider=
+
+## KeyManager password
+# jetty.sslContext.keyManagerPassword=OBF:1u2u1wml1z7s1z7a1wnl1u2g
+
+## Truststore password
+# jetty.sslContext.trustStorePassword=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
+
+## Truststore type and provider
+# jetty.sslContext.trustStoreType=JKS
+# jetty.sslContext.trustStoreProvider=
+
+## whether client certificate authentication is required
+# jetty.sslContext.needClientAuth=false
+
+## Whether client certificate authentication is desired
+# jetty.sslContext.wantClientAuth=false
+
+## Whether cipher order is significant (since java 8 only)
+# jetty.sslContext.useCipherSuitesOrder=true
+
+## To configure Includes / Excludes for Cipher Suites or Protocols see tweak-ssl.xml example at
+## https://www.eclipse.org/jetty/documentation/current/configuring-ssl.html#configuring-sslcontextfactory-cipherSuites
+
+## Set the size of the SslSession cache
+# jetty.sslContext.sslSessionCacheSize=-1
+
+## Set the timeout (in seconds) of the SslSession cache timeout
+# jetty.sslContext.sslSessionTimeout=-1
+
+## Allow SSL renegotiation
+# jetty.sslContext.renegotiationAllowed=true
+# jetty.sslContext.renegotiationLimit=5
diff --git a/jetty-server/src/main/config/modules/threadlimit.mod b/jetty-server/src/main/config/modules/threadlimit.mod
new file mode 100644
index 0000000..da07292
--- /dev/null
+++ b/jetty-server/src/main/config/modules/threadlimit.mod
@@ -0,0 +1,22 @@
+#
+# Thread Limit module
+# Applies ThreadLimiteHandler to entire server
+#
+
+[depend]
+server
+
+[xml]
+etc/jetty-threadlimit.xml
+
+[ini-template]
+## Select style of proxy forwarded header
+#jetty.threadlimit.forwardedHeader=X-Forwarded-For
+#jetty.threadlimit.forwardedHeader=Forwarded
+
+## Enabled by default?
+#jetty.threadlimit.enabled=true
+
+## Thread limit per remote IP
+#jetty.threadlimit.threadLimit=10
+
diff --git a/jetty-server/src/main/config/modules/xinetd.mod b/jetty-server/src/main/config/modules/xinetd.mod
deleted file mode 100644
index e53618e..0000000
--- a/jetty-server/src/main/config/modules/xinetd.mod
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# Xinetd module
-#
-
-[depend]
-server
-
-[xml]
-etc/jetty-xinetd.xml
-
-[ini-template]
-## Xinetd Configuration
-## See ${jetty.home}/etc/jetty-xinetd.xml for example service entry
-jetty.xinetd.idleTimeout=300000
-jetty.xinetd.acceptors=2
-jetty.xinetd.statsOn=false
-
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java
index daeebff..0b9d527 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java
@@ -18,29 +18,65 @@
 
 package org.eclipse.jetty.server;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.util.ArrayUtil;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 
+/**
+ * <p>Provides the common handling for {@link ConnectionFactory} implementations including:</p>
+ * <ul>
+ * <li>Protocol identification</li>
+ * <li>Configuration of new Connections:
+ *     <ul>
+ *     <li>Setting inputbuffer size</li>
+ *     <li>Calling {@link Connection#addListener(Connection.Listener)} for all
+ *     Connection.Listener instances found as beans on the {@link Connector}
+ *     and this {@link ConnectionFactory}</li>
+ *     </ul>
+ * </ul>
+ */
+@ManagedObject
 public abstract class AbstractConnectionFactory extends ContainerLifeCycle implements ConnectionFactory
 {
     private final String _protocol;
+    private final List<String> _protocols;
     private int _inputbufferSize = 8192;
 
     protected AbstractConnectionFactory(String protocol)
     {
         _protocol=protocol;
+        _protocols=Collections.unmodifiableList(Arrays.asList(new String[]{protocol}));
+    }
+
+    protected AbstractConnectionFactory(String... protocols)
+    {
+        _protocol=protocols[0];
+        _protocols=Collections.unmodifiableList(Arrays.asList(protocols));
     }
 
     @Override
+    @ManagedAttribute(value = "The protocol name", readonly = true)
     public String getProtocol()
     {
         return _protocol;
     }
 
+    @Override
+    public List<String> getProtocols()
+    {
+        return _protocols;
+    }
+
+    @ManagedAttribute("The buffer size used to read from the network")
     public int getInputBufferSize()
     {
         return _inputbufferSize;
@@ -55,19 +91,24 @@
     {
         connection.setInputBufferSize(getInputBufferSize());
 
+        // Add Connection.Listeners from Connector
         if (connector instanceof ContainerLifeCycle)
         {
             ContainerLifeCycle aggregate = (ContainerLifeCycle)connector;
             for (Connection.Listener listener : aggregate.getBeans(Connection.Listener.class))
                 connection.addListener(listener);
         }
+        // Add Connection.Listeners from this factory
+        for (Connection.Listener listener : getBeans(Connection.Listener.class))
+            connection.addListener(listener);
+
         return connection;
     }
 
     @Override
     public String toString()
     {
-        return String.format("%s@%x{%s}",this.getClass().getSimpleName(),hashCode(),getProtocol());
+        return String.format("%s@%x%s",this.getClass().getSimpleName(),hashCode(),getProtocols());
     }
 
     public static ConnectionFactory[] getFactories(SslContextFactory sslContextFactory, ConnectionFactory... factories)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java
index 061f226..dfaf1f8 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java
@@ -19,12 +19,14 @@
 package org.eclipse.jetty.server;
 
 import java.io.IOException;
+import java.net.Socket;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -36,19 +38,22 @@
 import org.eclipse.jetty.io.ArrayByteBufferPool;
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ssl.SslConnection;
 import org.eclipse.jetty.util.FutureCallback;
+import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.component.Dumpable;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
 import org.eclipse.jetty.util.thread.Scheduler;
 
 /**
  * <p>An abstract implementation of {@link Connector} that provides a {@link ConnectionFactory} mechanism
- * for creating {@link Connection} instances for various protocols (HTTP, SSL, SPDY, etc).</p>
+ * for creating {@link org.eclipse.jetty.io.Connection} instances for various protocols (HTTP, SSL, etc).</p>
  *
  * <h2>Connector Services</h2>
  * The abstract connector manages the dependent services needed by all specific connector instances:
@@ -69,12 +74,12 @@
  *
  * <h2>Connection Factories</h2>
  * The connector keeps a collection of {@link ConnectionFactory} instances, each of which are known by their
- * protocol name.  The protocol name may be a real protocol (eg http/1.1 or spdy/3) or it may be a private name
+ * protocol name.  The protocol name may be a real protocol (e.g. "http/1.1" or "h2") or it may be a private name
  * that represents a special connection factory. For example, the name "SSL-http/1.1" is used for
  * an {@link SslConnectionFactory} that has been instantiated with the {@link HttpConnectionFactory} as it's
  * next protocol.
  *
- * <h4>Configuring Connection Factories</h4>
+ * <h2>Configuring Connection Factories</h2>
  * The collection of available {@link ConnectionFactory} may be constructor injected or modified with the
  * methods {@link #addConnectionFactory(ConnectionFactory)}, {@link #removeConnectionFactory(String)} and
  * {@link #setConnectionFactories(Collection)}.  Only a single {@link ConnectionFactory} instance may be configured
@@ -86,53 +91,52 @@
  * <p>
  * Each Connection factory type is responsible for the configuration of the protocols that it accepts. Thus to
  * configure the HTTP protocol, you pass a {@link HttpConfiguration} instance to the {@link HttpConnectionFactory}
- * (or the SPDY factories that can also provide HTTP Semantics).  Similarly the {@link SslConnectionFactory} is
+ * (or other factories that can also provide HTTP Semantics).  Similarly the {@link SslConnectionFactory} is
  * configured by passing it a {@link SslContextFactory} and a next protocol name.
  *
- * <h4>Connection Factory Operation</h4>
- * {@link ConnectionFactory}s may simply create a {@link Connection} instance to support a specific
+ * <h2>Connection Factory Operation</h2>
+ * {@link ConnectionFactory}s may simply create a {@link org.eclipse.jetty.io.Connection} instance to support a specific
  * protocol.  For example, the {@link HttpConnectionFactory} will create a {@link HttpConnection} instance
  * that can handle http/1.1, http/1.0 and http/0.9.
  * <p>
- * {@link ConnectionFactory}s may also create a chain of {@link Connection} instances, using other {@link ConnectionFactory} instances.
+ * {@link ConnectionFactory}s may also create a chain of {@link org.eclipse.jetty.io.Connection} instances, using other {@link ConnectionFactory} instances.
  * For example, the {@link SslConnectionFactory} is configured with a next protocol name, so that once it has accepted
  * a connection and created an {@link SslConnection}, it then used the next {@link ConnectionFactory} from the
- * connector using the {@link #getConnectionFactory(String)} method, to create a {@link Connection} instance that
- * will handle the unecrypted bytes from the {@link SslConnection}.   If the next protocol is "http/1.1", then the
+ * connector using the {@link #getConnectionFactory(String)} method, to create a {@link org.eclipse.jetty.io.Connection} instance that
+ * will handle the unencrypted bytes from the {@link SslConnection}.   If the next protocol is "http/1.1", then the
  * {@link SslConnectionFactory} will have a protocol name of "SSL-http/1.1" and lookup "http/1.1" for the protocol
  * to run over the SSL connection.
  * <p>
- * {@link ConnectionFactory}s may also create temporary {@link Connection} instances that will exchange bytes
- * over the connection to determine what is the next protocol to use.  For example the NPN protocol is an extension
- * of SSL to allow a protocol to be specified during the SSL handshake. NPN is used by the SPDY protocol to
- * negotiate the version of SPDY or HTTP that the client and server will speak.  Thus to accept a SPDY connection, the
- * connector will be configured with {@link ConnectionFactory}s for "SSL-NPN", "NPN", "spdy/3", "spdy/2", "http/1.1"
- * with the default protocol being "SSL-NPN".  Thus a newly accepted connection uses "SSL-NPN", which specifies a
- * SSLConnectionFactory with "NPN" as the next protocol.  Thus an SslConnection instance is created chained to an NPNConnection
- * instance.  The NPN connection then negotiates with the client to determined the next protocol, which could be
- * "spdy/3", "spdy/2" or the default of "http/1.1".  Once the next protocol is determined, the NPN connection
- * calls {@link #getConnectionFactory(String)} to create a connection instance that will replace the NPN connection as
- * the connection chained to the SSLConnection.
- * <p>
+ * {@link ConnectionFactory}s may also create temporary {@link org.eclipse.jetty.io.Connection} instances that will exchange bytes
+ * over the connection to determine what is the next protocol to use.  For example the ALPN protocol is an extension
+ * of SSL to allow a protocol to be specified during the SSL handshake. ALPN is used by the HTTP/2 protocol to
+ * negotiate the protocol that the client and server will speak.  Thus to accept a HTTP/2 connection, the
+ * connector will be configured with {@link ConnectionFactory}s for "SSL-ALPN", "h2", "http/1.1"
+ * with the default protocol being "SSL-ALPN".  Thus a newly accepted connection uses "SSL-ALPN", which specifies a
+ * SSLConnectionFactory with "ALPN" as the next protocol.  Thus an SSL connection instance is created chained to an ALPN
+ * connection instance.  The ALPN connection then negotiates with the client to determined the next protocol, which
+ * could be "h2" or the default of "http/1.1".  Once the next protocol is determined, the ALPN connection
+ * calls {@link #getConnectionFactory(String)} to create a connection instance that will replace the ALPN connection as
+ * the connection chained to the SSL connection.
  * <h2>Acceptors</h2>
  * The connector will execute a number of acceptor tasks to the {@link Exception} service passed to the constructor.
  * The acceptor tasks run in a loop while the connector is running and repeatedly call the abstract {@link #accept(int)} method.
  * The implementation of the accept method must:
- * <nl>
- * <li>block waiting for new connections
- * <li>accept the connection (eg socket accept)
- * <li>perform any configuration of the connection (eg. socket linger times)
+ * <ol>
+ * <li>block waiting for new connections</li>
+ * <li>accept the connection (eg socket accept)</li>
+ * <li>perform any configuration of the connection (eg. socket linger times)</li>
  * <li>call the {@link #getDefaultConnectionFactory()} {@link ConnectionFactory#newConnection(Connector, org.eclipse.jetty.io.EndPoint)}
- * method to create a new Connection instance.
- * </nl>
+ * method to create a new Connection instance.</li>
+ * </ol>
  * The default number of acceptor tasks is the minimum of 1 and half the number of available CPUs. Having more acceptors may reduce
  * the latency for servers that see a high rate of new connections (eg HTTP/1.0 without keep-alive).  Typically the default is
- * sufficient for modern persistent protocols (HTTP/1.1, SPDY etc.)
+ * sufficient for modern persistent protocols (HTTP/1.1, HTTP/2 etc.)
  */
 @ManagedObject("Abstract implementation of the Connector Interface")
 public abstract class AbstractConnector extends ContainerLifeCycle implements Connector, Dumpable
 {
-    protected final Logger LOG = Log.getLogger(getClass());
+    protected final Logger LOG = Log.getLogger(AbstractConnector.class);
     // Order is important on server side, so we use a LinkedHashMap
     private final Map<String, ConnectionFactory> _factories = new LinkedHashMap<>();
     private final Server _server;
@@ -140,7 +144,7 @@
     private final Scheduler _scheduler;
     private final ByteBufferPool _byteBufferPool;
     private final Thread[] _acceptors;
-    private final Set<EndPoint> _endpoints = Collections.newSetFromMap(new ConcurrentHashMap<EndPoint, Boolean>());
+    private final Set<EndPoint> _endpoints = Collections.newSetFromMap(new ConcurrentHashMap<>());
     private final Set<EndPoint> _immutableEndPoints = Collections.unmodifiableSet(_endpoints);
     private volatile CountDownLatch _stopping;
     private long _idleTimeout = 30000;
@@ -187,7 +191,7 @@
 
         int cores = Runtime.getRuntime().availableProcessors();
         if (acceptors < 0)
-            acceptors=Math.max(1, Math.min(4,cores/8));        
+            acceptors=Math.max(1, Math.min(4,cores/8));
         if (acceptors > cores)
             LOG.warn("Acceptors should be <= availableProcessors: " + this);
         _acceptors = new Thread[acceptors];
@@ -252,6 +256,14 @@
         _defaultConnectionFactory = getConnectionFactory(_defaultProtocol);
         if(_defaultConnectionFactory==null)
             throw new IllegalStateException("No protocol factory for default protocol: "+_defaultProtocol);
+        SslConnectionFactory ssl = getConnectionFactory(SslConnectionFactory.class);
+        if (ssl != null)
+        {
+            String next = ssl.getNextProtocol();
+            ConnectionFactory cf = getConnectionFactory(next);
+            if (cf == null)
+                throw new IllegalStateException("No protocol factory for SSL next protocol: " + next);
+        }
 
         super.doStart();
 
@@ -299,7 +311,7 @@
         _stopping=null;
 
         super.doStop();
-        
+
         for (Acceptor a : getBeans(Acceptor.class))
             removeBean(a);
 
@@ -338,7 +350,7 @@
     {
         synchronized (_factories)
         {
-            return _factories.get(protocol.toLowerCase(Locale.ENGLISH));
+            return _factories.get(StringUtil.asciiToLowerCase(protocol));
         }
     }
 
@@ -358,13 +370,73 @@
     {
         synchronized (_factories)
         {
-            ConnectionFactory old=_factories.remove(factory.getProtocol());
-            if (old!=null)
+            Set<ConnectionFactory> to_remove = new HashSet<>();
+            for (String key:factory.getProtocols())
+            {
+                key=StringUtil.asciiToLowerCase(key);
+                ConnectionFactory old=_factories.remove(key);
+                if (old!=null)
+                {
+                    if (old.getProtocol().equals(_defaultProtocol))
+                        _defaultProtocol=null;
+                    to_remove.add(old);
+                }
+                _factories.put(key, factory);
+            }
+
+            // keep factories still referenced
+            for (ConnectionFactory f : _factories.values())
+                to_remove.remove(f);
+
+            // remove old factories
+            for (ConnectionFactory old: to_remove)
+            {
                 removeBean(old);
-            _factories.put(factory.getProtocol().toLowerCase(Locale.ENGLISH), factory);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} removed {}", this, old);
+            }
+
+            // add new Bean
             addBean(factory);
             if (_defaultProtocol==null)
                 _defaultProtocol=factory.getProtocol();
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} added {}", this, factory);
+        }
+    }
+
+    public void addFirstConnectionFactory(ConnectionFactory factory)
+    {
+        synchronized (_factories)
+        {
+            List<ConnectionFactory> existings = new ArrayList<>(_factories.values());
+            _factories.clear();
+            addConnectionFactory(factory);
+            for (ConnectionFactory existing : existings)
+                addConnectionFactory(existing);
+            _defaultProtocol = factory.getProtocol();
+        }
+    }
+
+    public void addIfAbsentConnectionFactory(ConnectionFactory factory)
+    {
+        synchronized (_factories)
+        {
+            String key=StringUtil.asciiToLowerCase(factory.getProtocol());
+            if (_factories.containsKey(key))
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} addIfAbsent ignored {}", this, factory);
+            }
+            else
+            {
+                _factories.put(key, factory);
+                addBean(factory);
+                if (_defaultProtocol==null)
+                    _defaultProtocol=factory.getProtocol();
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} addIfAbsent added {}", this, factory);
+            }
         }
     }
 
@@ -372,7 +444,7 @@
     {
         synchronized (_factories)
         {
-            ConnectionFactory factory= _factories.remove(protocol.toLowerCase(Locale.ENGLISH));
+            ConnectionFactory factory= _factories.remove(StringUtil.asciiToLowerCase(protocol));
             removeBean(factory);
             return factory;
         }
@@ -409,10 +481,10 @@
     /* ------------------------------------------------------------ */
     /** Set the acceptor thread priority delta.
      * <p>This allows the acceptor thread to run at a different priority.
-     * Typically this would be used to lower the priority to give preference 
-     * to handling previously accepted connections rather than accepting 
+     * Typically this would be used to lower the priority to give preference
+     * to handling previously accepted connections rather than accepting
      * new connections</p>
-     * @param acceptorPriorityDelta
+     * @param acceptorPriorityDelta the acceptor priority delta
      */
     public void setAcceptorPriorityDelta(int acceptorPriorityDelta)
     {
@@ -451,7 +523,7 @@
 
     public void setDefaultProtocol(String defaultProtocol)
     {
-        _defaultProtocol = defaultProtocol.toLowerCase(Locale.ENGLISH);
+        _defaultProtocol = StringUtil.asciiToLowerCase(defaultProtocol);
         if (isRunning())
             _defaultConnectionFactory=getConnectionFactory(_defaultProtocol);
     }
@@ -464,14 +536,42 @@
         return getConnectionFactory(_defaultProtocol);
     }
 
+    protected boolean handleAcceptFailure(Throwable previous, Throwable current)
+    {
+        if (isAccepting())
+        {
+            if (previous == null)
+                LOG.warn(current);
+            else
+                LOG.debug(current);
+            try
+            {
+                // Arbitrary sleep to avoid spin looping.
+                // Subclasses may decide for a different
+                // sleep policy or closing the connector.
+                Thread.sleep(1000);
+                return true;
+            }
+            catch (Throwable x)
+            {
+                return false;
+            }
+        }
+        else
+        {
+            LOG.ignore(current);
+            return false;
+        }
+    }
+
     private class Acceptor implements Runnable
     {
-        private final int _acceptor;
+        private final int _id;
         private String _name;
 
         private Acceptor(int id)
         {
-            _acceptor = id;
+            _id = id;
         }
 
         @Override
@@ -479,32 +579,34 @@
         {
             final Thread thread = Thread.currentThread();
             String name=thread.getName();
-            _name=String.format("%s-acceptor-%d@%x-%s",name,_acceptor,hashCode(),AbstractConnector.this.toString());
+            _name=String.format("%s-acceptor-%d@%x-%s",name,_id,hashCode(),AbstractConnector.this.toString());
             thread.setName(_name);
-            
+
             int priority=thread.getPriority();
             if (_acceptorPriorityDelta!=0)
                 thread.setPriority(Math.max(Thread.MIN_PRIORITY,Math.min(Thread.MAX_PRIORITY,priority+_acceptorPriorityDelta)));
 
             synchronized (AbstractConnector.this)
             {
-                _acceptors[_acceptor] = thread;
+                _acceptors[_id] = thread;
             }
 
             try
             {
+                Throwable exception = null;
                 while (isAccepting())
                 {
                     try
                     {
-                        accept(_acceptor);
+                        accept(_id);
+                        exception = null;
                     }
-                    catch (Throwable e)
+                    catch (Throwable x)
                     {
-                        if (isAccepting())
-                            LOG.warn(e);
+                        if (handleAcceptFailure(exception, x))
+                            exception = x;
                         else
-                            LOG.ignore(e);
+                            break;
                     }
                 }
             }
@@ -516,23 +618,23 @@
 
                 synchronized (AbstractConnector.this)
                 {
-                    _acceptors[_acceptor] = null;
+                    _acceptors[_id] = null;
                 }
                 CountDownLatch stopping=_stopping;
                 if (stopping!=null)
                     stopping.countDown();
             }
         }
-        
+
         @Override
         public String toString()
         {
             String name=_name;
             if (name==null)
-                return String.format("acceptor-%d@%x", _acceptor, hashCode());
+                return String.format("acceptor-%d@%x", _id, hashCode());
             return name;
         }
-        
+
     }
 
 
@@ -585,7 +687,7 @@
     {
         return _name;
     }
-    
+
     /* ------------------------------------------------------------ */
     /**
      * Set a connector name.   A context may be configured with
@@ -597,13 +699,13 @@
     {
         _name=name;
     }
-    
+
     @Override
     public String toString()
     {
-        return String.format("%s@%x{%s}",
+        return String.format("%s@%x{%s,%s}",
                 _name==null?getClass().getSimpleName():_name,
                 hashCode(),
-                getDefaultProtocol());
+                getDefaultProtocol(),getProtocols());
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
index 6ab3ff5..64f05d9 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
@@ -25,6 +25,7 @@
 
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.PathMap;
+import org.eclipse.jetty.server.handler.StatisticsHandler;
 import org.eclipse.jetty.util.DateCache;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.component.AbstractLifeCycle;
@@ -67,23 +68,33 @@
 
     /**
      * Is logging enabled
+     * @return true if logging is enabled
      */
     protected abstract boolean isEnabled();
-    
+
     /* ------------------------------------------------------------ */
 
     /**
      * Write requestEntry out. (to disk or slf4j log)
+     * @param requestEntry the request entry
+     * @throws IOException if unable to write the entry
      */
     public abstract void write(String requestEntry) throws IOException;
 
     /* ------------------------------------------------------------ */
 
+    private void append(StringBuilder buf,String s)
+    {
+        if (s==null || s.length()==0)
+            buf.append('-');
+        else
+            buf.append(s);
+    }
+
     /**
      * 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)
+     * @see org.eclipse.jetty.server.RequestLog#log(Request, Response)
      */
     @Override
     public void log(Request request, Response response)
@@ -101,7 +112,7 @@
 
             if (_logServer)
             {
-                buf.append(request.getServerName());
+                append(buf,request.getServerName());
                 buf.append(' ');
             }
 
@@ -117,10 +128,7 @@
             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("-");
+            append(buf,(authentication instanceof Authentication.User)?((Authentication.User)authentication).getUserIdentity().getUserPrincipal().getName():null);
 
             buf.append(" [");
             if (_logDateCache != null)
@@ -129,37 +137,40 @@
                 buf.append(request.getTimeStamp());
 
             buf.append("] \"");
-            buf.append(request.getMethod());
+            append(buf,request.getMethod());
             buf.append(' ');
-            buf.append(request.getUri().toString());
+            append(buf,request.getHttpURI().toString());
             buf.append(' ');
-            buf.append(request.getProtocol());
+            append(buf,request.getProtocol());
             buf.append("\" ");
 
-            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)));
+            int status = response.getCommittedMetaData().getStatus();
+            if (status >=0)
+            {
+                buf.append((char)('0' + ((status / 100) % 10)));
+                buf.append((char)('0' + ((status / 10) % 10)));
+                buf.append((char)('0' + (status % 10)));
+            }
+            else
+                buf.append(status);
 
-            long responseLength = response.getLongContentLength();
-            if (responseLength >= 0)
+            long written = response.getHttpChannel().getBytesWritten();
+            if (written >= 0)
             {
                 buf.append(' ');
-                if (responseLength > 99999)
-                    buf.append(responseLength);
+                if (written > 99999)
+                    buf.append(written);
                 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));
+                    if (written > 9999)
+                        buf.append((char)('0' + ((written / 10000) % 10)));
+                    if (written > 999)
+                        buf.append((char)('0' + ((written / 1000) % 10)));
+                    if (written > 99)
+                        buf.append((char)('0' + ((written / 100) % 10)));
+                    if (written > 9)
+                        buf.append((char)('0' + ((written / 10) % 10)));
+                    buf.append((char)('0' + (written) % 10));
                 }
                 buf.append(' ');
             }
@@ -168,7 +179,7 @@
 
 
             if (_extended)
-                logExtended(request, response, buf);
+                logExtended(buf, request, response);
 
             if (_logCookies)
             {
@@ -209,20 +220,15 @@
             LOG.warn(e);
         }
     }
-    
-    /* ------------------------------------------------------------ */
 
     /**
-     * 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
+     * @param request the HTTP request to log
+     * @param b the build to write logs into
+     * @throws IOException for no reason
+     * @deprecated override {@link #logExtended(StringBuilder, Request, Response)} instead
      */
-    protected void logExtended(Request request,
-                               Response response,
-                               StringBuilder b) throws IOException
+    @Deprecated
+    protected void logExtended(Request request, StringBuilder b) throws IOException
     {
         String referer = request.getHeader(HttpHeader.REFERER.toString());
         if (referer == null)
@@ -236,7 +242,7 @@
 
         String agent = request.getHeader(HttpHeader.USER_AGENT.toString());
         if (agent == null)
-            b.append("\"-\" ");
+            b.append("\"-\"");
         else
         {
             b.append('"');
@@ -245,6 +251,18 @@
         }
     }
 
+    /**
+     * Writes extended request and response information to the output stream.
+     *
+     * @param b        StringBuilder to write to
+     * @param request  request object
+     * @param response response object
+     * @throws IOException if unable to log the extended information
+     */
+    protected void logExtended(StringBuilder b, Request request, Response response) throws IOException
+    {
+        logExtended(request, b);
+    }
 
     /**
      * Set request paths that will not be logged.
@@ -329,15 +347,19 @@
     }
 
     /**
+     * @param value true to log dispatch
      * @deprecated use {@link StatisticsHandler}
      */
+    @Deprecated
     public void setLogDispatch(boolean value)
     {
     }
 
     /**
+     * @return true if logging dispatches
      * @deprecated use {@link StatisticsHandler}
      */
+    @Deprecated
     public boolean isLogDispatch()
     {
         return false;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java
index 36b1a8a..900aa57 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java
@@ -77,7 +77,7 @@
     {
         return _context;
     }
-    
+
     public Context getContext()
     {
         return _context;
@@ -94,18 +94,18 @@
     }
 
     /**
-     * @return The path in the context
+     * @return The path in the context (encoded with possible query string)
      */
     public String getPath()
     {
         return _dispatchPath;
     }
-    
+
     public void setTimeoutTask(Scheduler.Task task)
     {
         _timeoutTask = task;
     }
-    
+
     public void cancelTimeoutTask()
     {
         Scheduler.Task task=_timeoutTask;
@@ -119,28 +119,31 @@
     {
         return _asyncContext;
     }
-    
+
     @Override
     public Throwable getThrowable()
     {
         return _throwable;
     }
-    
-    public void setThrowable(Throwable throwable)
-    {
-        _throwable=throwable;
-    }
+
+//    public void setThrowable(Throwable throwable)
+//    {
+//        _throwable=throwable;
+//    }
 
     public void setDispatchContext(ServletContext context)
     {
         _dispatchContext=context;
     }
-    
+
+    /**
+     * @param path encoded URI
+     */
     public void setDispatchPath(String path)
     {
         _dispatchPath=path;
     }
-    
+
     public void completed()
     {
         _timeoutTask=null;
@@ -158,7 +161,15 @@
         Scheduler.Task task=_timeoutTask;
         _timeoutTask=null;
         if (task!=null)
-            _state.expired();
+            _state.getHttpChannel().execute(() -> _state.onTimeout());
+    }
+
+    public void addThrowable(Throwable e)
+    {
+        if (_throwable==null)
+            _throwable=e;
+        else
+            _throwable.addSuppressed(e);
     }
 
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java
index 03c7bc6..606af96 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java
@@ -33,18 +33,25 @@
 
 public class AsyncContextState implements AsyncContext
 {
+    private final HttpChannel _channel;
     volatile HttpChannelState _state;
 
     public AsyncContextState(HttpChannelState state)
     {
         _state=state;
+        _channel=_state.getHttpChannel();
+    }
+    
+    public HttpChannel getHttpChannel()
+    {
+        return _channel;
     }
     
     HttpChannelState state()
     {
         HttpChannelState state=_state;
         if (state==null)
-            throw new IllegalStateException("AsyncContext completed");
+            throw new IllegalStateException("AsyncContext completed and/or Request lifecycle recycled");
         return state;
     }
 
@@ -68,7 +75,7 @@
             @Override
             public void onError(AsyncEvent event) throws IOException
             {
-                listener.onComplete(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
+                listener.onError(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
             }
             
             @Override
@@ -147,7 +154,7 @@
     @Override
     public boolean hasOriginalRequestAndResponse()
     {
-        HttpChannel<?> channel=state().getHttpChannel();
+        HttpChannel channel=state().getHttpChannel();
         return channel.getRequest()==getRequest() && channel.getResponse()==getResponse();
     }
 
@@ -160,12 +167,13 @@
     @Override
     public void start(final Runnable task)
     {
-        state().getHttpChannel().execute(new Runnable()
+        final HttpChannel channel = state().getHttpChannel();
+        channel.execute(new Runnable()
         {
             @Override
             public void run()
             {
-                state().getAsyncContextEvent().getContext().getContextHandler().handle(task);
+                state().getAsyncContextEvent().getContext().getContextHandler().handle(channel.getRequest(),task);
             }
         });
     }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Authentication.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Authentication.java
index d42e0a6..ae9ebd7 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Authentication.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Authentication.java
@@ -24,14 +24,12 @@
 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
 {
@@ -75,6 +73,7 @@
         /** Authenticate if possible without sending a challenge.
          * This is used to check credentials that have been sent for 
          * non-manditory authentication.
+         * @param request the request
          * @return The new Authentication state.
          */
         Authentication authenticate(ServletRequest request);
@@ -83,6 +82,8 @@
         /** Authenticate and possibly send a challenge.
          * This is used to initiate authentication for previously 
          * non-manditory authentication.
+         * @param request the request
+         * @param response the response
          * @return The new Authentication state.
          */
         Authentication authenticate(ServletRequest request,ServletResponse response);
@@ -90,8 +91,9 @@
         
         /* ------------------------------------------------------------ */
         /** Login with the LOGIN authenticator
-         * @param username
-         * @param password
+         * @param username the username
+         * @param password the password
+         * @param request the request
          * @return The new Authentication state
          */
         Authentication login(String username,Object password,ServletRequest request);
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ByteBufferQueuedHttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ByteBufferQueuedHttpInput.java
deleted file mode 100644
index 6d11136..0000000
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ByteBufferQueuedHttpInput.java
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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.nio.ByteBuffer;
-
-/**
- * <p>An implementation of HttpInput using {@link ByteBuffer} as items.</p>
- */
-public class ByteBufferQueuedHttpInput extends QueuedHttpInput<ByteBuffer>
-{
-    @Override
-    protected int remaining(ByteBuffer item)
-    {
-        return item.remaining();
-    }
-
-    @Override
-    protected int get(ByteBuffer item, byte[] buffer, int offset, int length)
-    {
-        int l = Math.min(item.remaining(), length);
-        item.get(buffer, offset, l);
-        return l;
-    }
-    
-    @Override
-    protected void consume(ByteBuffer item, int length)
-    {
-        item.position(item.position()+length);
-    }
-
-    @Override
-    protected void onContentConsumed(ByteBuffer item)
-    {
-    }
-
-}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ClassLoaderDump.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ClassLoaderDump.java
index 8f947c0..1acc307 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ClassLoaderDump.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ClassLoaderDump.java
@@ -46,21 +46,33 @@
     {
         if (_loader==null)
             out.append("No ClassLoader\n");
+        else if (_loader instanceof Dumpable)
+        {
+            ContainerLifeCycle.dump(out,indent,Collections.singleton(_loader));
+        }
+        else if (_loader instanceof URLClassLoader)
+        {
+            out.append(String.valueOf(_loader)).append("\n");
+            ClassLoader parent = _loader.getParent();
+            if (parent==null)
+                ContainerLifeCycle.dump(out,indent,TypeUtil.asList(((URLClassLoader)_loader).getURLs()));
+            else if (parent == Server.class.getClassLoader())
+                ContainerLifeCycle.dump(out,indent,TypeUtil.asList(((URLClassLoader)_loader).getURLs()),Collections.singleton(parent.toString()));
+            else if (parent instanceof Dumpable)
+                ContainerLifeCycle.dump(out,indent,TypeUtil.asList(((URLClassLoader)_loader).getURLs()),Collections.singleton(parent));
+            else
+                ContainerLifeCycle.dump(out,indent,TypeUtil.asList(((URLClassLoader)_loader).getURLs()),Collections.singleton(new ClassLoaderDump(parent)));
+        }
         else
         {
             out.append(String.valueOf(_loader)).append("\n");
-
-            Object parent = _loader.getParent();
-            if (parent != null)
-            {
-                if (!(parent instanceof Dumpable))
-                    parent = new ClassLoaderDump((ClassLoader)parent);
-
-                if (_loader instanceof URLClassLoader)
-                    ContainerLifeCycle.dump(out,indent,TypeUtil.asList(((URLClassLoader)_loader).getURLs()),Collections.singleton(parent));
-                else
-                    ContainerLifeCycle.dump(out,indent,Collections.singleton(parent));
-            }
+            ClassLoader parent = _loader.getParent();
+            if (parent==Server.class.getClassLoader())
+                ContainerLifeCycle.dump(out,indent,Collections.singleton(parent.toString()));
+            else if (parent instanceof Dumpable)
+                ContainerLifeCycle.dump(out,indent,Collections.singleton(parent));
+            else if (parent!=null)
+                ContainerLifeCycle.dump(out,indent,Collections.singleton(new ClassLoaderDump(parent)));
         }
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionFactory.java
index 68cf910..07752ac 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionFactory.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionFactory.java
@@ -19,22 +19,29 @@
 package org.eclipse.jetty.server;
 
 
+import java.util.List;
+
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.MetaData;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
 
 /**
- * <p>A Factory to create {@link Connection} instances for {@link Connector}s.</p>
- * <p>A Connection factory is responsible for instantiating and configuring a {@link Connection} instance
- * to handle an {@link EndPoint} accepted by a {@link Connector}.</p>
+ * A Factory to create {@link Connection} instances for {@link Connector}s.
+ * <p>
+ * A Connection factory is responsible for instantiating and configuring a {@link Connection} instance
+ * to handle an {@link EndPoint} accepted by a {@link Connector}.
  * <p>
  * A ConnectionFactory has a protocol name that represents the protocol of the Connections
- * created.  Example of protocol names include:<dl>
+ * created.  Example of protocol names include:
+ * <dl>
  * <dt>http</dt><dd>Creates a HTTP connection that can handle multiple versions of HTTP from 0.9 to 1.1</dd>
- * <dt>spdy/2</dt><dd>Creates a HTTP connection that handles a specific version of the SPDY protocol</dd>
+ * <dt>h2</dt><dd>Creates a HTTP/2 connection that handles the HTTP/2 protocol</dd>
  * <dt>SSL-XYZ</dt><dd>Create an SSL connection chained to a connection obtained from a connection factory 
  * with a protocol "XYZ".</dd>
  * <dt>SSL-http</dt><dd>Create an SSL connection chained to a HTTP connection (aka https)</dd>
- * <dt>SSL-npn</dt><dd>Create an SSL connection chained to a NPN connection, that uses a negotiation with
+ * <dt>SSL-ALPN</dt><dd>Create an SSL connection chained to a ALPN connection, that uses a negotiation with
  * the client to determine the next protocol.</dd>
  * </dl>
  */
@@ -42,9 +49,15 @@
 {
     /* ------------------------------------------------------------ */
     /**
-     * @return A string representing the protocol name.
+     * @return A string representing the primary protocol name.
      */
     public String getProtocol();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return A list of alternative protocol names/versions including the primary protocol.
+     */
+    public List<String> getProtocols();
     
     /**
      * <p>Creates a new {@link Connection} with the given parameters</p>
@@ -54,4 +67,23 @@
      */
     public Connection newConnection(Connector connector, EndPoint endPoint);
     
+    
+    public interface Upgrading extends ConnectionFactory
+    {
+        /* ------------------------------------------------------------ */
+        /** Create a connection for an upgrade request.
+         * <p>This is a variation of {@link #newConnection(Connector, EndPoint)} that can create (and/or customise)
+         * a connection for an upgrade request.  Implementations may call {@link #newConnection(Connector, EndPoint)} or 
+         * may construct the connection instance themselves.</p>
+         *  
+         * @param connector  The connector to upgrade for.
+         * @param endPoint The endpoint of the connection.
+         * @param upgradeRequest The meta data of the upgrade request.
+         * @param responseFields  The fields to be sent with the 101 response
+         * @return Null to indicate that request processing should continue normally without upgrading. A new connection instance to
+         * indicate that the upgrade should proceed.
+         * @throws BadMessageException Thrown to indicate the upgrade attempt was illegal and that a bad message response should be sent.
+         */
+        public Connection upgradeConnection(Connector connector, EndPoint endPoint, MetaData.Request upgradeRequest,HttpFields responseFields) throws BadMessageException;
+    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java
index c3ed0ce..8b2f370 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java
@@ -24,6 +24,7 @@
 
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.component.Graceful;
@@ -59,6 +60,7 @@
     public ByteBufferPool getByteBufferPool();
 
     /**
+     * @param nextProtocol the next protocol
      * @return the {@link ConnectionFactory} associated with the protocol name
      */
     public ConnectionFactory getConnectionFactory(String nextProtocol);
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectorStatistics.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectorStatistics.java
index f885a72..8a4337b 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectorStatistics.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectorStatistics.java
@@ -24,8 +24,8 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.LongAdder;
 
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
@@ -43,7 +43,10 @@
 /** A Connector.Listener that gathers Connector and Connections Statistics.
  * Adding an instance of this class as with {@link AbstractConnector#addBean(Object)} 
  * will register the listener with all connections accepted by that connector.
+ *
+ * @deprecated use {@link ServerConnectionStatistics} instead.
  */
+@Deprecated
 @ManagedObject("Connector Statistics")
 public class ConnectorStatistics extends AbstractLifeCycle implements Dumpable, Connection.Listener
 {
@@ -54,8 +57,8 @@
     private final SampleStatistic _messagesOut = new SampleStatistic();
     private final SampleStatistic _connectionDurationStats = new SampleStatistic();
     private final ConcurrentMap<Connection, Sample> _samples = new ConcurrentHashMap<>();
-    private final AtomicInteger _closedIn = new AtomicInteger();
-    private final AtomicInteger _closedOut = new AtomicInteger();
+    private final LongAdder _closedIn = new LongAdder();
+    private final LongAdder _closedOut = new LongAdder();
     private AtomicLong _nanoStamp=new AtomicLong();
     private volatile int _messagesInPerSecond;
     private volatile int _messagesOutPerSecond;
@@ -85,8 +88,8 @@
             Sample sample=_samples.remove(connection);
             if (sample!=null)
             {
-                _closedIn.addAndGet(msgsIn-sample._messagesIn);
-                _closedOut.addAndGet(msgsOut-sample._messagesOut);
+                _closedIn.add(msgsIn-sample._messagesIn);
+                _closedOut.add(msgsOut-sample._messagesOut);
             }
         }
     }
@@ -267,8 +270,8 @@
         {
             if (_nanoStamp.compareAndSet(then,now))
             {
-                long msgsIn=_closedIn.getAndSet(0);
-                long msgsOut=_closedOut.getAndSet(0);
+                long msgsIn=_closedIn.sumThenReset();
+                long msgsOut=_closedOut.sumThenReset();
 
                 for (Map.Entry<Connection, Sample> entry : _samples.entrySet())
                 {
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/CookieCutter.java b/jetty-server/src/main/java/org/eclipse/jetty/server/CookieCutter.java
index ef7ab0b..05f55b8 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/CookieCutter.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/CookieCutter.java
@@ -23,7 +23,7 @@
 
 import javax.servlet.http.Cookie;
 
-import org.eclipse.jetty.util.QuotedStringTokenizer;
+import org.eclipse.jetty.http.QuotedCSV;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
@@ -35,7 +35,6 @@
  * 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
@@ -274,8 +273,9 @@
                 // If after processing the current character we have a value and a name, then it is a cookie
                 if (value!=null && name!=null)
                 {
-                    name=QuotedStringTokenizer.unquoteOnly(name);
-                    value=QuotedStringTokenizer.unquoteOnly(value);
+                   
+                    name=QuotedCSV.unquote(name);
+                    value=QuotedCSV.unquote(value);
                     
                     try
                     {
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java b/jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java
new file mode 100644
index 0000000..9a937c7
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java
@@ -0,0 +1,334 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.PrintStream;
+import java.util.Locale;
+
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandler.Context;
+import org.eclipse.jetty.server.handler.ContextHandler.ContextScopeListener;
+import org.eclipse.jetty.util.DateCache;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.annotation.Name;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/** A Context Listener that produces additional debug.
+ * This listener if added to a ContextHandler, will produce additional debug information to
+ * either/or a specific log stream or the standard debug log.
+ * The events produced by {@link ServletContextListener}, {@link ServletRequestListener}, 
+ * {@link AsyncListener} and {@link ContextScopeListener} are logged.
+ */
+@ManagedObject("Debug Listener")
+public class DebugListener extends AbstractLifeCycle implements ServletContextListener
+{
+    private static final Logger LOG = Log.getLogger(DebugListener.class);
+    private static final DateCache __date=new DateCache("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
+    
+    private final String _attr = String.format("__R%s@%x",this.getClass().getSimpleName(),System.identityHashCode(this));
+
+    private final PrintStream _out;
+    private boolean _renameThread;
+    private boolean _showHeaders;
+    private boolean _dumpContext;
+
+    public DebugListener()
+    {
+        this(null,false,false,false);
+    }
+    
+    public DebugListener(@Name("renameThread") boolean renameThread, @Name("showHeaders") boolean showHeaders, @Name("dumpContext") boolean dumpContext)
+    {
+        this(null,renameThread,showHeaders,dumpContext);
+    }
+    
+    public DebugListener(@Name("outputStream") OutputStream out, @Name("renameThread") boolean renameThread, @Name("showHeaders") boolean showHeaders, @Name("dumpContext") boolean dumpContext)
+    {
+        _out=out==null?null:new PrintStream(out);
+        _renameThread=renameThread;
+        _showHeaders=showHeaders;
+        _dumpContext=dumpContext;
+    }
+    
+    @ManagedAttribute("Rename thread within context scope")
+    public boolean isRenameThread()
+    {
+        return _renameThread;
+    }
+
+    public void setRenameThread(boolean renameThread)
+    {
+        _renameThread = renameThread;
+    }
+
+    @ManagedAttribute("Show request headers")
+    public boolean isShowHeaders()
+    {
+        return _showHeaders;
+    }
+
+    public void setShowHeaders(boolean showHeaders)
+    {
+        _showHeaders = showHeaders;
+    }
+
+    @ManagedAttribute("Dump contexts at start")
+    public boolean isDumpContext()
+    {
+        return _dumpContext;
+    }
+
+    public void setDumpContext(boolean dumpContext)
+    {
+        _dumpContext = dumpContext;
+    }
+
+    @Override
+    public void contextInitialized(ServletContextEvent sce)
+    {
+        sce.getServletContext().addListener(_servletRequestListener);        
+        ContextHandler handler =  ContextHandler.getContextHandler(sce.getServletContext());
+        handler.addEventListener(_contextScopeListener);
+        String cname=findContextName(sce.getServletContext());
+        log("^  ctx=%s %s",cname,sce.getServletContext());
+        if (_dumpContext)
+        {
+            if (_out==null)
+                handler.dumpStdErr();
+            else
+            {
+                try
+                {
+                    handler.dump(_out);
+                }
+                catch(Exception e)
+                {
+                    LOG.warn(e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent sce)
+    {
+        String cname=findContextName(sce.getServletContext());
+        log("v  ctx=%s %s",cname,sce.getServletContext());
+    }
+    
+    protected String findContextName(ServletContext context)
+    {
+        if (context==null)
+            return null;
+        String n = (String)context.getAttribute(_attr);
+        if (n==null)
+        {
+            n=String.format("%s@%x",context.getContextPath(),context.hashCode());
+            context.setAttribute(_attr,n);
+        }
+        return n;
+    }
+
+    protected String findRequestName(ServletRequest request)
+    {
+        if (request==null)
+            return null;
+        HttpServletRequest r = (HttpServletRequest)request;
+        String n = (String)request.getAttribute(_attr);
+        if (n==null)
+        {
+            n=String.format("%s@%x",r.getRequestURI(),request.hashCode());
+            request.setAttribute(_attr,n);
+        }
+        return n;
+    }
+    
+    protected void log(String format, Object... arg)
+    {
+        if (!isRunning())
+            return;
+        
+        String s=String.format(format,arg);
+        
+        long now = System.currentTimeMillis();
+        long ms = now%1000;
+        if (_out!=null)
+            _out.printf("%s.%03d:%s%n",__date.formatNow(now),ms,s);
+        if (LOG.isDebugEnabled())
+            LOG.info(s);
+    }
+    
+    final AsyncListener _asyncListener = new AsyncListener()
+    { 
+        @Override
+        public void onTimeout(AsyncEvent event) throws IOException
+        {
+            String cname=findContextName(((AsyncContextEvent)event).getServletContext());
+            String rname=findRequestName(event.getAsyncContext().getRequest());
+            log("!  ctx=%s r=%s onTimeout %s",cname,rname,((AsyncContextEvent)event).getHttpChannelState());
+        }
+        
+        @Override
+        public void onStartAsync(AsyncEvent event) throws IOException
+        {
+            String cname=findContextName(((AsyncContextEvent)event).getServletContext());
+            String rname=findRequestName(event.getAsyncContext().getRequest());
+            log("!  ctx=%s r=%s onStartAsync %s",cname,rname,((AsyncContextEvent)event).getHttpChannelState());
+        }
+        
+        @Override
+        public void onError(AsyncEvent event) throws IOException
+        {
+            String cname=findContextName(((AsyncContextEvent)event).getServletContext());
+            String rname=findRequestName(event.getAsyncContext().getRequest());
+            log("!! ctx=%s r=%s onError %s %s",cname,rname,event.getThrowable(),((AsyncContextEvent)event).getHttpChannelState());
+        }
+        
+        @Override
+        public void onComplete(AsyncEvent event) throws IOException
+        {
+            AsyncContextEvent ace=(AsyncContextEvent)event;
+            String cname=findContextName(ace.getServletContext());
+            String rname=findRequestName(ace.getAsyncContext().getRequest());
+            
+            Request br=Request.getBaseRequest(ace.getAsyncContext().getRequest());
+            Response response = br.getResponse();
+            String headers=_showHeaders?("\n"+response.getHttpFields().toString()):"";
+            
+            log("!  ctx=%s r=%s onComplete %s %d%s",cname,rname,ace.getHttpChannelState(),response.getStatus(),headers);
+        }
+    };
+    
+    final ServletRequestListener _servletRequestListener = new ServletRequestListener()
+    {
+        @Override
+        public void requestInitialized(ServletRequestEvent sre)
+        {
+            String cname=findContextName(sre.getServletContext());
+            HttpServletRequest r = (HttpServletRequest)sre.getServletRequest();
+           
+            String rname=findRequestName(r);
+            DispatcherType d = r.getDispatcherType();
+            if (d==DispatcherType.REQUEST)
+            {
+                Request br=Request.getBaseRequest(r);
+
+                String headers=_showHeaders?("\n"+br.getMetaData().getFields().toString()):"";
+                
+                
+                StringBuffer url=r.getRequestURL();
+                if (r.getQueryString()!=null)
+                    url.append('?').append(r.getQueryString());
+                log(">> %s ctx=%s r=%s %s %s %s %s %s%s",d,
+                        cname,
+                        rname,
+                        d,
+                        r.getMethod(),
+                        url.toString(),
+                        r.getProtocol(),
+                        br.getHttpChannel(),
+                        headers);
+            }
+            else
+                log(">> %s ctx=%s r=%s",d,cname,rname);
+        }
+        
+        @Override
+        public void requestDestroyed(ServletRequestEvent sre)
+        {
+            String cname=findContextName(sre.getServletContext());
+            HttpServletRequest r = (HttpServletRequest)sre.getServletRequest();
+            String rname=findRequestName(r);
+            DispatcherType d = r.getDispatcherType();
+            if (sre.getServletRequest().isAsyncStarted())
+            {
+                sre.getServletRequest().getAsyncContext().addListener(_asyncListener);
+                log("<< %s ctx=%s r=%s async=true",d,cname,rname);
+            }
+            else
+            {
+                Request br=Request.getBaseRequest(r);
+                String headers=_showHeaders?("\n"+br.getResponse().getHttpFields().toString()):"";
+                log("<< %s ctx=%s r=%s async=false %d%s",d,cname,rname,Request.getBaseRequest(r).getResponse().getStatus(),headers);
+            }
+        }
+    };
+    
+    final ContextHandler.ContextScopeListener _contextScopeListener = new ContextHandler.ContextScopeListener()
+    {
+        @Override
+        public void enterScope(Context context, Request request, Object reason)
+        {
+            String cname=findContextName(context);
+            if (request==null)
+                log(">  ctx=%s %s",cname,reason);
+            else
+            {
+                String rname=findRequestName(request);
+
+                if (_renameThread)
+                {
+                    Thread thread=Thread.currentThread();
+                    thread.setName(String.format("%s#%s",thread.getName(),rname));
+                }
+            
+                log(">  ctx=%s r=%s %s",cname,rname,reason);
+            }
+        }
+        
+
+        @Override
+        public void exitScope(Context context, Request request)
+        {
+            String cname=findContextName(context);
+            if (request==null)
+                log("<  ctx=%s",cname);
+            else
+            {
+                String rname=findRequestName(request);
+
+                log("<  ctx=%s r=%s",cname,rname);
+                if (_renameThread)
+                {
+                    Thread thread=Thread.currentThread();
+                    if (thread.getName().endsWith(rname))
+                        thread.setName(thread.getName().substring(0,thread.getName().length()-rname.length()-1));
+                }
+            }
+        }   
+    };
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java
index 0d153d5..a5447a3 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java
@@ -22,6 +22,7 @@
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashSet;
+
 import javax.servlet.DispatcherType;
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletException;
@@ -30,6 +31,10 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.MetaData;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.util.Attributes;
 import org.eclipse.jetty.util.MultiMap;
@@ -43,27 +48,24 @@
     public final static String __FORWARD_PREFIX="javax.servlet.forward.";
 
     private final ContextHandler _contextHandler;
-    private final String _uri;
-    private final String _path;
-    private final String _query;
+    private final HttpURI _uri;
+    private final String _pathInContext;
     private final String _named;
 
-    public Dispatcher(ContextHandler contextHandler, String uri, String pathInContext, String query)
+    public Dispatcher(ContextHandler contextHandler, HttpURI uri, String pathInContext)
     {
         _contextHandler=contextHandler;
         _uri=uri;
-        _path=pathInContext;
-        _query=query;
+        _pathInContext=pathInContext;
         _named=null;
     }
 
     public Dispatcher(ContextHandler contextHandler, String name) throws IllegalStateException
     {
         _contextHandler=contextHandler;
-        _named=name;
         _uri=null;
-        _path=null;
-        _query=null;
+        _pathInContext=null;
+        _named=name;
     }
 
     @Override
@@ -80,7 +82,7 @@
     @Override
     public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException
     {
-        Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
+        Request baseRequest=Request.getBaseRequest(request);
 
         if (!(request instanceof HttpServletRequest))
             request = new ServletRequestHttpWrapper(request);
@@ -102,17 +104,17 @@
             {
                 IncludeAttributes attr = new IncludeAttributes(old_attr);
 
-                attr._requestURI=_uri;
+                attr._requestURI=_uri.getPath();
                 attr._contextPath=_contextHandler.getContextPath();
                 attr._servletPath=null; // set by ServletHandler
-                attr._pathInfo=_path;
-                attr._query=_query;
+                attr._pathInfo=_pathInContext;
+                attr._query=_uri.getQuery();
 
-                if (_query!=null)
-                    baseRequest.mergeQueryParameters(_query, false);
+                if (attr._query!=null)
+                    baseRequest.mergeQueryParameters(baseRequest.getQueryString(),attr._query, false);
                 baseRequest.setAttributes(attr);
 
-                _contextHandler.handle(_path, baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
+                _contextHandler.handle(_pathInContext, baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
             }
         }
         finally
@@ -127,7 +129,7 @@
 
     protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException
     {
-        Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
+        Request baseRequest=Request.getBaseRequest(request);
         Response base_response=baseRequest.getResponse();
         base_response.resetForForward();
 
@@ -137,11 +139,12 @@
             response = new ServletResponseHttpWrapper(response);
 
         final boolean old_handled=baseRequest.isHandled();
-        final String old_uri=baseRequest.getRequestURI();
+
+        final HttpURI old_uri=baseRequest.getHttpURI();
         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 MultiMap<String> old_query_params=baseRequest.getQueryParameters();
         final Attributes old_attr=baseRequest.getAttributes();
         final DispatcherType old_type=baseRequest.getDispatcherType();
@@ -174,21 +177,26 @@
                 else
                 {
                     attr._pathInfo=old_path_info;
-                    attr._query=old_query;
-                    attr._requestURI=old_uri;
+                    attr._query=old_uri.getQuery();
+                    attr._requestURI=old_uri.getPath();
                     attr._contextPath=old_context_path;
                     attr._servletPath=old_servlet_path;
                 }
 
-                baseRequest.setRequestURI(_uri);
+                HttpURI uri = new HttpURI(old_uri.getScheme(),old_uri.getHost(),old_uri.getPort(),
+                        _uri.getPath(),_uri.getParam(),_uri.getQuery(),_uri.getFragment());
+
+                baseRequest.setHttpURI(uri);
+
                 baseRequest.setContextPath(_contextHandler.getContextPath());
                 baseRequest.setServletPath(null);
-                baseRequest.setPathInfo(_uri);
-                if (_query!=null)
-                    baseRequest.mergeQueryParameters(_query, true);
+                baseRequest.setPathInfo(_pathInContext);
+                if (_uri.getQuery()!=null || old_uri.getQuery()!=null)
+                    baseRequest.mergeQueryParameters(old_uri.getQuery(),_uri.getQuery(), true);
+
                 baseRequest.setAttributes(attr);
 
-                _contextHandler.handle(_path, baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
+                _contextHandler.handle(_pathInContext, baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
 
                 if (!baseRequest.getHttpChannelState().isAsync())
                     commitResponse(response,baseRequest);
@@ -197,11 +205,10 @@
         finally
         {
             baseRequest.setHandled(old_handled);
-            baseRequest.setRequestURI(old_uri);
+            baseRequest.setHttpURI(old_uri);
             baseRequest.setContextPath(old_context_path);
             baseRequest.setServletPath(old_servlet_path);
             baseRequest.setPathInfo(old_path_info);
-            baseRequest.setQueryString(old_query);
             baseRequest.setQueryParameters(old_query_params);
             baseRequest.resetParameters();
             baseRequest.setAttributes(old_attr);
@@ -209,6 +216,40 @@
         }
     }
 
+    /**
+     * <p>Pushes a secondary resource identified by this dispatcher.</p>
+     *
+     * @param request the primary request
+     * @deprecated Use {@link Request#getPushBuilder()} instead
+     */
+    @Deprecated
+    public void push(ServletRequest request)
+    {
+        Request baseRequest = Request.getBaseRequest(request);
+        HttpFields fields = new HttpFields(baseRequest.getHttpFields());
+
+        String query=baseRequest.getQueryString();
+        if (_uri.hasQuery())
+        {
+            if (query==null)
+                query=_uri.getQuery();
+            else
+                query=query+"&"+_uri.getQuery(); // TODO is this correct semantic?
+        }
+
+        HttpURI uri = HttpURI.createHttpURI(request.getScheme(),request.getServerName(),request.getServerPort(),_uri.getPath(),baseRequest.getHttpURI().getParam(),query,null);
+
+        MetaData.Request push = new MetaData.Request(HttpMethod.GET.asString(),uri,baseRequest.getHttpVersion(),fields);
+
+        baseRequest.getHttpChannel().getHttpTransport().push(push);
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("Dispatcher@0x%x{%s,%s}",hashCode(),_named,_uri);
+    }
+
     private void commitResponse(ServletResponse response, Request baseRequest) throws IOException
     {
         if (baseRequest.getResponse().isWriting())
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java
index 19247a5..650b585 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java
@@ -20,22 +20,30 @@
 
 import java.net.InetSocketAddress;
 
+import javax.servlet.ServletRequest;
+
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.QuotedCSV;
 import org.eclipse.jetty.server.HttpConfiguration.Customizer;
+import org.eclipse.jetty.util.StringUtil;
 
 
 /* ------------------------------------------------------------ */
 /** Customize Requests for Proxy Forwarding.
  * <p>
  * This customizer looks at at HTTP request for headers that indicate
- * it has been forwarded by one or more proxies.  Specifically handled are:
+ * it has been forwarded by one or more proxies.  Specifically handled are
  * <ul>
- * <li>X-Forwarded-Host</li>
- * <li>X-Forwarded-Server</li>
- * <li>X-Forwarded-For</li>
- * <li>X-Forwarded-Proto</li>
+ * <li>{@code Forwarded}, as defined by <a href="https://tools.ietf.org/html/rfc7239">rfc7239</a>
+ * <li>{@code X-Forwarded-Host}</li>
+ * <li>{@code X-Forwarded-Server}</li>
+ * <li>{@code X-Forwarded-For}</li>
+ * <li>{@code X-Forwarded-Proto}</li>
+ * <li>{@code X-Proxied-Https}</li>
  * </ul>
  * <p>If these headers are present, then the {@link Request} object is updated
  * so that the proxy is not seen as the other end point of the connection on which
@@ -46,54 +54,118 @@
  */
 public class ForwardedRequestCustomizer implements Customizer
 {
-    private String _hostHeader;
+    private HostPortHttpField _forcedHost;
+    private String _forwardedHeader = HttpHeader.FORWARDED.toString();
     private String _forwardedHostHeader = HttpHeader.X_FORWARDED_HOST.toString();
     private String _forwardedServerHeader = HttpHeader.X_FORWARDED_SERVER.toString();
     private String _forwardedForHeader = HttpHeader.X_FORWARDED_FOR.toString();
     private String _forwardedProtoHeader = HttpHeader.X_FORWARDED_PROTO.toString();
-    private String _forwardedCipherSuiteHeader;
-    private String _forwardedSslSessionIdHeader;
+    private String _forwardedHttpsHeader = "X-Proxied-Https";
+    private String _forwardedCipherSuiteHeader = "Proxy-auth-cert";
+    private String _forwardedSslSessionIdHeader = "Proxy-ssl-id";
+    private boolean _proxyAsAuthority=false;
+    private boolean _sslIsSecure=true;
     
-
-    /* ------------------------------------------------------------ */
-    public String getHostHeader()
+    /**
+     * @return true if the proxy address obtained via
+     * {@code X-Forwarded-Server} or RFC7239 "by" is used as
+     * the request authority. Default false
+     */
+    public boolean getProxyAsAuthority()
     {
-        return _hostHeader;
+        return _proxyAsAuthority;
     }
 
-    /* ------------------------------------------------------------ */
+    /**
+     * @param proxyAsAuthority if true, use the proxy address obtained via
+     * {@code X-Forwarded-Server} or RFC7239 "by" as the request authority.
+     */
+    public void setProxyAsAuthority(boolean proxyAsAuthority)
+    {
+        _proxyAsAuthority = proxyAsAuthority;
+    }
+
+    /**
+     * Configure to only support the RFC7239 Forwarded header and to
+     * not support any {@code X-Forwarded-} headers.   This convenience method
+     * clears all the non RFC headers if passed true and sets them to
+     * the default values (if not already set) if passed false.
+     */
+    public void setForwardedOnly(boolean rfc7239only)
+    {
+        if (rfc7239only)
+        {
+            if (_forwardedHeader==null)
+                _forwardedHeader=HttpHeader.FORWARDED.toString();
+            _forwardedHostHeader=null;
+            _forwardedHostHeader=null;
+            _forwardedServerHeader=null;
+            _forwardedForHeader=null;
+            _forwardedProtoHeader=null;
+            _forwardedHttpsHeader=null;
+        }
+        else
+        {
+            if (_forwardedHostHeader==null)
+                _forwardedHostHeader = HttpHeader.X_FORWARDED_HOST.toString();
+            if (_forwardedServerHeader==null)
+                _forwardedServerHeader = HttpHeader.X_FORWARDED_SERVER.toString();
+            if (_forwardedForHeader==null)
+                _forwardedForHeader = HttpHeader.X_FORWARDED_FOR.toString();
+            if (_forwardedProtoHeader==null)
+                _forwardedProtoHeader = HttpHeader.X_FORWARDED_PROTO.toString();
+            if (_forwardedHttpsHeader==null)
+                _forwardedHttpsHeader = "X-Proxied-Https";
+        }
+    }
+    
+    public String getForcedHost()
+    {
+        return _forcedHost.getValue();
+    }
+    
     /**
      * Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}.
      *
-     * @param hostHeader
+     * @param hostAndPort
      *            The value of the host header to force.
      */
-    public void setHostHeader(String hostHeader)
+    public void setForcedHost(String hostAndPort)
     {
-        _hostHeader = hostHeader;
+        _forcedHost = new HostPortHttpField(hostAndPort);
     }
 
-    /* ------------------------------------------------------------ */
-    /*
-     *
-     * @see #setForwarded(boolean)
+    /**
+     * @return The header name for RFC forwarded (default Forwarded)
      */
+    public String getForwardedHeader()
+    {
+        return _forwardedHeader;
+    }
+
+    /**
+     * @param forwardedHeader 
+     *            The header name for RFC forwarded (default Forwarded)
+     */
+    public void setForwardedHeader(String forwardedHeader)
+    {
+        _forwardedHeader = forwardedHeader;
+    }
+
     public String getForwardedHostHeader()
     {
         return _forwardedHostHeader;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @param forwardedHostHeader
-     *            The header name for forwarded hosts (default x-forwarded-host)
+     *            The header name for forwarded hosts (default {@code X-Forwarded-Host})
      */
     public void setForwardedHostHeader(String forwardedHostHeader)
     {
         _forwardedHostHeader = forwardedHostHeader;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @return the header name for forwarded server.
      */
@@ -102,17 +174,15 @@
         return _forwardedServerHeader;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @param forwardedServerHeader
-     *            The header name for forwarded server (default x-forwarded-server)
+     *            The header name for forwarded server (default {@code X-Forwarded-Server})
      */
     public void setForwardedServerHeader(String forwardedServerHeader)
     {
         _forwardedServerHeader = forwardedServerHeader;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @return the forwarded for header
      */
@@ -121,149 +191,231 @@
         return _forwardedForHeader;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @param forwardedRemoteAddressHeader
-     *            The header name for forwarded for (default x-forwarded-for)
+     *            The header name for forwarded for (default {@code X-Forwarded-For})
      */
     public void setForwardedForHeader(String forwardedRemoteAddressHeader)
     {
         _forwardedForHeader = forwardedRemoteAddressHeader;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * Get the forwardedProtoHeader.
      *
-     * @return the forwardedProtoHeader (default X-Forwarded-For)
+     * @return the forwardedProtoHeader (default {@code X-Forwarded-Proto})
      */
     public String getForwardedProtoHeader()
     {
         return _forwardedProtoHeader;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * Set the forwardedProtoHeader.
      *
      * @param forwardedProtoHeader
-     *            the forwardedProtoHeader to set (default X-Forwarded-For)
+     *            the forwardedProtoHeader to set (default {@code X-Forwarded-Proto})
      */
     public void setForwardedProtoHeader(String forwardedProtoHeader)
     {
         _forwardedProtoHeader = forwardedProtoHeader;
     }
 
-    /* ------------------------------------------------------------ */
     /**
-     * @return The header name holding a forwarded cipher suite (default null)
+     * @return The header name holding a forwarded cipher suite (default {@code Proxy-auth-cert})
      */
     public String getForwardedCipherSuiteHeader()
     {
         return _forwardedCipherSuiteHeader;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @param forwardedCipherSuite
-     *            The header name holding a forwarded cipher suite (default null)
+     *            The header name holding a forwarded cipher suite (default {@code Proxy-auth-cert})
      */
     public void setForwardedCipherSuiteHeader(String forwardedCipherSuite)
     {
         _forwardedCipherSuiteHeader = forwardedCipherSuite;
     }
 
-    /* ------------------------------------------------------------ */
     /**
-     * @return The header name holding a forwarded SSL Session ID (default null)
+     * @return The header name holding a forwarded SSL Session ID (default {@code Proxy-ssl-id})
      */
     public String getForwardedSslSessionIdHeader()
     {
         return _forwardedSslSessionIdHeader;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @param forwardedSslSessionId
-     *            The header name holding a forwarded SSL Session ID (default null)
+     *            The header name holding a forwarded SSL Session ID (default {@code Proxy-ssl-id})
      */
     public void setForwardedSslSessionIdHeader(String forwardedSslSessionId)
     {
         _forwardedSslSessionIdHeader = forwardedSslSessionId;
     }
 
-    /* ------------------------------------------------------------ */
+    /**
+     * @return The header name holding a forwarded Https status indicator (on|off true|false) (default {@code X-Proxied-Https})
+     */
+    public String getForwardedHttpsHeader()
+    {
+        return _forwardedHttpsHeader;
+    }
+
+    /**
+     * @param forwardedHttpsHeader the header name holding a forwarded Https status indicator(default {@code X-Proxied-Https})
+     */
+    public void setForwardedHttpsHeader(String forwardedHttpsHeader)
+    {
+        _forwardedHttpsHeader = forwardedHttpsHeader;
+    }
+    
+    /**
+     * @return true if the presence of a SSL session or certificate header is sufficient
+     * to indicate a secure request (default is true)
+     */
+    public boolean isSslIsSecure()
+    {
+        return _sslIsSecure;
+    }
+
+    /**
+     * @param sslIsSecure true if the presence of a SSL session or certificate header is sufficient
+     * to indicate a secure request (default is true)
+     */
+    public void setSslIsSecure(boolean sslIsSecure)
+    {
+        _sslIsSecure = sslIsSecure;
+    }
+
     @Override
     public void customize(Connector connector, HttpConfiguration config, Request request)
     {
         HttpFields httpFields = request.getHttpFields();
 
-        // Do SSL first
-        if (getForwardedCipherSuiteHeader()!=null)
+        RFC7239 rfc7239 = null;
+        String forwardedHost = null;
+        String forwardedServer = null;
+        String forwardedFor = null;
+        String forwardedProto = null;
+        String forwardedHttps = null;
+        
+        // Do a single pass through the header fields as it is a more efficient single iteration.
+        for (HttpField field : httpFields)
         {
-            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)
+            String name = field.getName();
+            
+            if (getForwardedCipherSuiteHeader()!=null && getForwardedCipherSuiteHeader().equalsIgnoreCase(name))
             {
-                request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id);
-                request.setScheme(HttpScheme.HTTPS.asString());
+                request.setAttribute("javax.servlet.request.cipher_suite",field.getValue());
+                if (isSslIsSecure())
+                {
+                    request.setSecure(true);
+                    request.setScheme(config.getSecureScheme());
+                }
+            }
+            
+            if (getForwardedSslSessionIdHeader()!=null && getForwardedSslSessionIdHeader().equalsIgnoreCase(name))
+            {
+                request.setAttribute("javax.servlet.request.ssl_session_id", field.getValue());
+                if (isSslIsSecure())
+                {
+                    request.setSecure(true);
+                    request.setScheme(config.getSecureScheme());
+                }
+            }
+            
+            if (forwardedHost==null && _forwardedHostHeader!=null && _forwardedHostHeader.equalsIgnoreCase(name))
+                forwardedHost = getLeftMost(field.getValue());
+            
+            if (forwardedServer==null && _forwardedServerHeader!=null && _forwardedServerHeader.equalsIgnoreCase(name))
+                forwardedServer = getLeftMost(field.getValue());
+            
+            if (forwardedFor==null && _forwardedForHeader!=null && _forwardedForHeader.equalsIgnoreCase(name))
+                forwardedFor = getLeftMost(field.getValue());
+            
+            if (forwardedProto==null && _forwardedProtoHeader!=null && _forwardedProtoHeader.equalsIgnoreCase(name))
+                forwardedProto = getLeftMost(field.getValue());
+            
+            if (forwardedHttps==null && _forwardedHttpsHeader!=null && _forwardedHttpsHeader.equalsIgnoreCase(name))
+                forwardedHttps = getLeftMost(field.getValue());
+            
+            if (_forwardedHeader!=null && _forwardedHeader.equalsIgnoreCase(name))
+            {
+                if (rfc7239==null)
+                    rfc7239= new RFC7239();
+                rfc7239.addValue(field.getValue());
             }
         }
-
-        // 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)
+        
+        // Handle host header if if not available any RFC7230.by or X-ForwardedServer header      
+        if (_forcedHost != null)
         {
             // Update host header
-            httpFields.put(HttpHeader.HOST.toString(),_hostHeader);
-            request.setServerName(null);
-            request.setServerPort(-1);
-            request.getServerName();
+            httpFields.put(_forcedHost);
+            request.setAuthority(_forcedHost.getHost(),_forcedHost.getPort());
+        }
+        else if (rfc7239!=null && rfc7239._host!=null)
+        {
+            HostPortHttpField auth = rfc7239._host;
+            httpFields.put(auth);
+            request.setAuthority(auth.getHost(),auth.getPort());
         }
         else if (forwardedHost != null)
         {
-            // Update host header
-            httpFields.put(HttpHeader.HOST.toString(),forwardedHost);
-            request.setServerName(null);
-            request.setServerPort(-1);
-            request.getServerName();
+            HostPortHttpField auth = new HostPortHttpField(forwardedHost);
+            httpFields.put(auth);
+            request.setAuthority(auth.getHost(),auth.getPort());
         }
-        else if (forwardedServer != null)
+        else if (_proxyAsAuthority)
         {
-            // Use provided server name
-            request.setServerName(forwardedServer);
+            if (rfc7239!=null && rfc7239._by!=null)
+            {
+                HostPortHttpField auth = rfc7239._by;
+                httpFields.put(auth);
+                request.setAuthority(auth.getHost(),auth.getPort());
+            }
+            else if (forwardedServer != null)
+            {
+                request.setAuthority(forwardedServer,request.getServerPort());
+            }
         }
 
-        if (forwardedFor != null)
+        // handle remote end identifier
+        if (rfc7239!=null && rfc7239._for!=null)
+        {
+            request.setRemoteAddr(InetSocketAddress.createUnresolved(rfc7239._for.getHost(),rfc7239._for.getPort()));
+        }
+        else if (forwardedFor != null)
         {
             request.setRemoteAddr(InetSocketAddress.createUnresolved(forwardedFor,request.getRemotePort()));
         }
 
-        if (forwardedProto != null)
+        // handle protocol identifier
+        if (rfc7239!=null && rfc7239._proto!=null)
+        {
+            request.setScheme(rfc7239._proto);
+            if (rfc7239._proto.equals(config.getSecureScheme()))
+                request.setSecure(true);
+        }
+        else if (forwardedProto != null)
         {
             request.setScheme(forwardedProto);
             if (forwardedProto.equals(config.getSecureScheme()))
                 request.setSecure(true);
         }
+        else if (forwardedHttps !=null && ("on".equalsIgnoreCase(forwardedHttps)||"true".equalsIgnoreCase(forwardedHttps)))
+        {
+            request.setScheme(HttpScheme.HTTPS.asString());
+            if (HttpScheme.HTTPS.asString().equals(config.getSecureScheme()))
+                request.setSecure(true);
+        }
     }
 
     /* ------------------------------------------------------------ */
-    protected String getLeftMostFieldValue(HttpFields fields, String header)
+    protected String getLeftMost(String headerValue)
     {
-        if (header == null)
-            return null;
-
-        String headerValue = fields.getStringField(header);
-
         if (headerValue == null)
             return null;
 
@@ -276,14 +428,72 @@
         }
 
         // The left-most value is the farthest downstream client
-        return headerValue.substring(0,commaIndex);
+        return headerValue.substring(0,commaIndex).trim();
     }
-
-
-    /* ------------------------------------------------------------ */
+    
     @Override
     public String toString()
     {
         return String.format("%s@%x",this.getClass().getSimpleName(),hashCode());
     }
+
+    @Deprecated
+    public String getHostHeader()
+    {
+        return _forcedHost.getValue();
+    }
+    
+    /**
+     * Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}.
+     *
+     * @param hostHeader
+     *            The value of the host header to force.
+     */
+    @Deprecated
+    public void setHostHeader(String hostHeader)
+    {
+        _forcedHost = new HostPortHttpField(hostHeader);
+    }
+
+    private final class RFC7239 extends QuotedCSV
+    {
+        HostPortHttpField _by;
+        HostPortHttpField _for;
+        HostPortHttpField _host;
+        String _proto;
+        
+        private RFC7239()
+        {
+            super(false);
+        }
+
+        @Override
+        protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue)
+        {
+            if (valueLength==0 && paramValue>paramName)
+            {
+                String name=StringUtil.asciiToLowerCase(buffer.substring(paramName,paramValue-1));
+                String value=buffer.substring(paramValue);
+                switch(name)
+                {
+                    case "by":
+                        if (_by==null && !value.startsWith("_") && !"unknown".equals(value))
+                            _by=new HostPortHttpField(value);
+                        break;
+                    case "for":
+                        if (_for==null && !value.startsWith("_") && !"unknown".equals(value))
+                            _for=new HostPortHttpField(value);
+                        break;
+                    case "host":
+                        if (_host==null)
+                            _host=new HostPortHttpField(value);
+                        break;
+                    case "proto":
+                        if (_proto==null)
+                            _proto=value;
+                        break;
+                }
+            }
+        }
+    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Handler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Handler.java
index 0de77b6..1ebdc54 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Handler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Handler.java
@@ -24,17 +24,21 @@
 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.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.annotation.ManagedOperation;
 import org.eclipse.jetty.util.component.Destroyable;
 import org.eclipse.jetty.util.component.LifeCycle;
 
-/* ------------------------------------------------------------ */
 /** A Jetty Server Handler.
- *
+ * <p>
  * A Handler instance is required by a {@link Server} to handle incoming
- * HTTP requests.  A Handler may: <ul>
+ * HTTP requests.
+ * <p>  
+ * 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}).
@@ -49,18 +53,25 @@
 @ManagedObject("Jetty Handler")
 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 HttpChannel#getCurrentHttpChannel()}
-     * 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 HttpChannel#getCurrentHttpChannel()}
-     * method can be used access the Response object if required.
+    /**
+     * 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
+     *            <code>{@link HttpConnection#getCurrentConnection()}.{@link HttpConnection#getHttpChannel() getHttpChannel()}.{@link HttpChannel#getRequest() getRequest()}</code>
+     *            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
+     *            <code>{@link HttpConnection#getCurrentConnection()}.{@link HttpConnection#getHttpChannel() getHttpChannel()}.{@link HttpChannel#getResponse() getResponse()}</code>
+     *            method can be used access the Response object if required.
      * @throws IOException
+     *             if unable to handle the request or response processing
      * @throws ServletException
+     *             if unable to handle the request or response due to underlying servlet issue
      */
     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
         throws IOException, ServletException;
@@ -72,6 +83,5 @@
 
     @ManagedOperation(value="destroy associated resources", impact="ACTION")
     public void destroy();
-
 }
 
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HandlerContainer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HandlerContainer.java
index a18006c..4f03b7a 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HandlerContainer.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HandlerContainer.java
@@ -48,15 +48,16 @@
     
     /* ------------------------------------------------------------ */
     /**
-     * @param byclass
+     * @param byclass the child handler class to get
      * @return array of all handlers contained by this handler and it's children of the passed type.
      */
     public Handler[] getChildHandlersByClass(Class<?> byclass);
     
     /* ------------------------------------------------------------ */
     /**
-     * @param byclass
+     * @param byclass the child handler class to get
      * @return first handler of all handlers contained by this handler and it's children of the passed type.
+     * @param <T> the type of handler
      */
     public <T extends Handler> T getChildHandlerByClass(Class<T> byclass);
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HostHeaderCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HostHeaderCustomizer.java
index fefa413..a823faa 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HostHeaderCustomizer.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HostHeaderCustomizer.java
@@ -20,15 +20,17 @@
 
 import java.util.Objects;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * Customizes requests that lack the {@code Host} header (for example, HTTP 1.0 requests).
- * <p />
+ * <p>
  * In case of HTTP 1.0 requests that lack the {@code Host} header, the application may issue
  * a redirect, and the {@code Location} header is usually constructed from the {@code Host}
  * header; if the {@code Host} header is missing, the server may query the connector for its
  * IP address in order to construct the {@code Location} header, and thus leak to clients
  * internal IP addresses.
- * <p />
+ * <p>
  * This {@link HttpConfiguration.Customizer} is configured with a {@code serverName} and
  * optionally a {@code serverPort}.
  * If the {@code Host} header is absent, the configured {@code serverName} will be set on
@@ -62,10 +64,6 @@
     public void customize(Connector connector, HttpConfiguration channelConfig, Request request)
     {
         if (request.getHeader("Host") == null)
-        {
-            request.setServerName(serverName);
-            if (serverPort > 0)
-                request.setServerPort(serverPort);
-        }
+            request.setAuthority(serverName,serverPort);  // TODO set the field as well?
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
index e52d769..0965cf9 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
@@ -21,28 +21,25 @@
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
-import java.nio.channels.ClosedChannelException;
-import java.nio.charset.StandardCharsets;
 import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.servlet.DispatcherType;
 import javax.servlet.RequestDispatcher;
+import javax.servlet.UnavailableException;
 import javax.servlet.http.HttpServletRequest;
 
-import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.BadMessageException;
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpGenerator;
-import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpHeaderValue;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.HttpParser;
 import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.HttpURI;
 import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http.MetaData;
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.ChannelEndPoint;
 import org.eclipse.jetty.io.EndPoint;
@@ -50,17 +47,16 @@
 import org.eclipse.jetty.server.HttpChannelState.Action;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.SharedBlockingCallback.Blocker;
-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.Scheduler;
 
 
-/* ------------------------------------------------------------ */
-/** HttpChannel.
- * Represents a single endpoint for HTTP semantic processing.
+/**
+ * HttpChannel represents a single endpoint for HTTP semantic processing.
  * The HttpChannel is both a HttpParser.RequestHandler, where it passively receives events from
  * an incoming HTTP request, and a Runnable, where it actively takes control of the request/response
  * life cycle and calls the application (perhaps suspending and resuming with multiple calls to run).
@@ -69,69 +65,64 @@
  * HttpTransport.completed().
  *
  */
-public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, HttpParser.ProxyHandler
+public class HttpChannel implements Runnable, HttpOutput.Interceptor
 {
     private static final Logger LOG = Log.getLogger(HttpChannel.class);
-    private static final ThreadLocal<HttpChannel<?>> __currentChannel = new ThreadLocal<>();
-
-    /* ------------------------------------------------------------ */
-    /** Get the current channel that this thread is dispatched to.
-     * @see Request#getAttribute(String) for a more general way to access the HttpChannel
-     * @return the current HttpChannel or null
-     */
-    public static HttpChannel<?> getCurrentHttpChannel()
-    {
-        return __currentChannel.get();
-    }
-
-    protected static HttpChannel<?> setCurrentHttpChannel(HttpChannel<?> channel)
-    {
-        HttpChannel<?> last=__currentChannel.get();
-        __currentChannel.set(channel);
-        return last;
-    }
-
     private final AtomicBoolean _committed = new AtomicBoolean();
     private final AtomicInteger _requests = new AtomicInteger();
     private final Connector _connector;
+    private final Executor _executor;
     private final HttpConfiguration _configuration;
     private final EndPoint _endPoint;
     private final HttpTransport _transport;
-    private final HttpURI _uri;
     private final HttpChannelState _state;
     private final Request _request;
     private final Response _response;
-    private HttpVersion _version = HttpVersion.HTTP_1_1;
-    private boolean _expect = false;
-    private boolean _expect100Continue = false;
-    private boolean _expect102Processing = false;
+    private MetaData.Response _committedMetaData;
+    private RequestLog _requestLog;
+    private long _oldIdleTimeout;
 
-    public HttpChannel(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<T> input)
+    /** Bytes written after interception (eg after compression) */
+    private long _written;
+
+    public HttpChannel(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport)
     {
         _connector = connector;
         _configuration = configuration;
         _endPoint = endPoint;
         _transport = transport;
 
-        _uri = new HttpURI(URIUtil.__CHARSET);
         _state = new HttpChannelState(this);
-        input.init(_state);
-        _request = new Request(this, input);
-        _response = new Response(this, new HttpOutput(this));
+        _request = new Request(this, newHttpInput(_state));
+        _response = new Response(this, newHttpOutput());
+
+        _executor = connector == null ? null : connector.getServer().getThreadPool();
+        _requestLog = connector == null ? null : connector.getServer().getRequestLog();
 
         if (LOG.isDebugEnabled())
             LOG.debug("new {} -> {},{},{}",this,_endPoint,_endPoint.getConnection(),_state);
     }
 
+    protected HttpInput newHttpInput(HttpChannelState state)
+    {
+        return new HttpInput(state);
+    }
+
+    protected HttpOutput newHttpOutput()
+    {
+        return new HttpOutput(this);
+    }
+
     public HttpChannelState getState()
     {
         return _state;
     }
 
-    public HttpVersion getHttpVersion()
+    public long getBytesWritten()
     {
-        return _version;
+        return _written;
     }
+
     /**
      * @return the number of requests handled by this connection
      */
@@ -150,10 +141,36 @@
         return _transport;
     }
 
+    public RequestLog getRequestLog()
+    {
+        return _requestLog;
+    }
+
+    public void setRequestLog(RequestLog requestLog)
+    {
+        _requestLog = requestLog;
+    }
+
+    public void addRequestLog(RequestLog requestLog)
+    {
+        if (_requestLog==null)
+            _requestLog = requestLog;
+        else if (_requestLog instanceof RequestLogCollection)
+            ((RequestLogCollection) _requestLog).add(requestLog);
+        else
+            _requestLog = new RequestLogCollection(_requestLog, requestLog);
+    }
+
+    public MetaData.Response getCommittedMetaData()
+    {
+        return _committedMetaData;
+    }
+
     /**
      * Get the idle timeout.
      * <p>This is implemented as a call to {@link EndPoint#getIdleTimeout()}, but may be
      * overridden by channels that have timeouts different from their connections.
+     * @return the idle timeout (in milliseconds)
      */
     public long getIdleTimeout()
     {
@@ -164,6 +181,7 @@
      * Set the idle timeout.
      * <p>This is implemented as a call to {@link EndPoint#setIdleTimeout(long)}, but may be
      * overridden by channels that have timeouts different from their connections.
+     * @param timeoutMs the idle timeout in milliseconds
      */
     public void setIdleTimeout(long timeoutMs)
     {
@@ -180,6 +198,12 @@
         return _configuration;
     }
 
+    @Override
+    public boolean isOptimizedForDirectBuffers()
+    {
+        return getHttpTransport().isOptimizedForDirectBuffers();
+    }
+
     public Server getServer()
     {
         return _connector.getServer();
@@ -210,50 +234,31 @@
         return _endPoint.getRemoteAddress();
     }
 
-    @Override
-    public int getHeaderCacheSize()
-    {
-        return _configuration.getHeaderCacheSize();
-    }
-
     /**
      * 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.
      *
+     * @param available estimate of the number of bytes that are available
      * @throws IOException if the InputStream cannot be created
      */
     public void continue100(int available) throws IOException
     {
-        // If the client is expecting 100 CONTINUE, then send it now.
-        // TODO: consider using an AtomicBoolean ?
-        if (isExpecting100Continue())
-        {
-            _expect100Continue = false;
-
-            // is content missing?
-            if (available == 0)
-            {
-                if (_response.isCommitted())
-                    throw new IOException("Committed before 100 Continues");
-
-                // TODO: break this dependency with HttpGenerator
-                boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false);
-                if (!committed)
-                    throw new IOException("Concurrent commit while trying to send 100-Continue");
-            }
-        }
+        throw new UnsupportedOperationException();
     }
 
-    public void reset()
+    public void recycle()
     {
         _committed.set(false);
-        _expect = false;
-        _expect100Continue = false;
-        _expect102Processing = false;
         _request.recycle();
         _response.recycle();
-        _uri.clear();
+        _committedMetaData=null;
+        _requestLog=_connector==null?null:_connector.getServer().getRequestLog();
+        _written=0;
+    }
+
+    public void asyncReadFillInterested()
+    {
     }
 
     @Override
@@ -262,192 +267,237 @@
         handle();
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @return True if the channel is ready to continue handling (ie it is not suspended)
      */
     public boolean handle()
     {
         if (LOG.isDebugEnabled())
-            LOG.debug("{} handle enter", this);
-
-        final HttpChannel<?>last = setCurrentHttpChannel(this);
-
-        String threadName = null;
-        if (LOG.isDebugEnabled())
-        {
-            threadName = Thread.currentThread().getName();
-            Thread.currentThread().setName(threadName + " - " + _uri);
-        }
+            LOG.debug("{} handle {} ", this,_request.getHttpURI());
 
         HttpChannelState.Action action = _state.handling();
-        try
-        {
-            // Loop here to handle async request redispatches.
-            // The loop is controlled by the call to async.unhandle in the
-            // finally block below.  Unhandle will return false only if an async dispatch has
-            // already happened when unhandle is called.
-            loop: while (action.ordinal()<HttpChannelState.Action.WAIT.ordinal() && getServer().isRunning())
-            {
-                boolean error=false;
-                try
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("{} action {}",this,action);
 
-                    switch(action)
+        // Loop here to handle async request redispatches.
+        // The loop is controlled by the call to async.unhandle in the
+        // finally block below.  Unhandle will return false only if an async dispatch has
+        // already happened when unhandle is called.
+        loop: while (!getServer().isStopped())
+        {
+            try
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} action {}",this,action);
+
+                switch(action)
+                {
+                    case TERMINATED:
+                    case WAIT:
+                        break loop;
+
+                    case DISPATCH:
                     {
-                        case REQUEST_DISPATCH:
-                            _request.setHandled(false);
-                            _response.getHttpOutput().reopen();
+                        if (!_request.hasMetaData())
+                            throw new IllegalStateException("state=" + _state);
+                        _request.setHandled(false);
+                        _response.getHttpOutput().reopen();
+
+                        try
+                        {
                             _request.setDispatcherType(DispatcherType.REQUEST);
 
                             List<HttpConfiguration.Customizer> customizers = _configuration.getCustomizers();
                             if (!customizers.isEmpty())
                             {
                                 for (HttpConfiguration.Customizer customizer : customizers)
+                                {
                                     customizer.customize(getConnector(), _configuration, _request);
+                                    if (_request.isHandled())
+                                        break;
+                                }
                             }
-                            getServer().handle(this);
-                            break;
 
-                        case ASYNC_DISPATCH:
-                            _request.setHandled(false);
-                            _response.getHttpOutput().reopen();
+                            if (!_request.isHandled())
+                                getServer().handle(this);
+                        }
+                        finally
+                        {
+                            _request.setDispatcherType(null);
+                        }
+                        break;
+                    }
+
+                    case ASYNC_DISPATCH:
+                    {
+                        _request.setHandled(false);
+                        _response.getHttpOutput().reopen();
+
+                        try
+                        {
                             _request.setDispatcherType(DispatcherType.ASYNC);
                             getServer().handleAsync(this);
-                            break;
+                        }
+                        finally
+                        {
+                            _request.setDispatcherType(null);
+                        }
+                        break;
+                    }
 
-                        case ASYNC_EXPIRED:
-                            _request.setHandled(false);
-                            _response.getHttpOutput().reopen();
-                            _request.setDispatcherType(DispatcherType.ERROR);
+                    case ERROR_DISPATCH:
+                    {
+                        Throwable ex = _state.getAsyncContextEvent().getThrowable();
 
-                            Throwable ex=_state.getAsyncContextEvent().getThrowable();
-                            String reason="Async Timeout";
-                            if (ex!=null)
+                        // Check for error dispatch loops
+                        Integer loop_detect = (Integer)_request.getAttribute("org.eclipse.jetty.server.ERROR_DISPATCH");
+                        if (loop_detect==null)
+                            loop_detect=1;
+                        else
+                            loop_detect=loop_detect+1;
+                        _request.setAttribute("org.eclipse.jetty.server.ERROR_DISPATCH",loop_detect);
+                        if (loop_detect > getHttpConfiguration().getMaxErrorDispatches())
+                        {
+                            LOG.warn("ERROR_DISPATCH loop detected on {} {}",_request,ex);
+                            try
                             {
-                                reason="Async Exception";
-                                _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,ex);
+                                _response.sendError(HttpStatus.INTERNAL_SERVER_ERROR_500);
                             }
-                            _request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(500));
-                            _request.setAttribute(RequestDispatcher.ERROR_MESSAGE,reason);
-                            _request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,_request.getRequestURI());
+                            finally
+                            {
+                                _state.errorComplete();
+                            }
+                            break loop;
+                        }
 
-                            _response.setStatusWithReason(500,reason);
+                        if (_response.isCommitted())
+                        {
+                            if (LOG.isDebugEnabled())
+                                LOG.debug("Could not perform Error Dispatch because the response is already committed, aborting");
+                            _transport.abort(ex);
+                        }
+                        else
+                        {
+                            _request.setHandled(false);
+                            _response.resetBuffer();
+                            _response.getHttpOutput().reopen();
 
+                            String reason;
+                            if (ex == null || ex instanceof TimeoutException)
+                            {
+                                reason = "Async Timeout";
+                            }
+                            else
+                            {
+                                reason = HttpStatus.Code.INTERNAL_SERVER_ERROR.getMessage();
+                                _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ex);
+                            }
 
-                            ErrorHandler eh = ErrorHandler.getErrorHandler(getServer(),_state.getContextHandler());
+                            _request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, 500);
+                            _request.setAttribute(RequestDispatcher.ERROR_MESSAGE, reason);
+                            _request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, _request.getRequestURI());
+                            _response.setStatusWithReason(HttpStatus.INTERNAL_SERVER_ERROR_500, reason);
+
+                            ErrorHandler eh = ErrorHandler.getErrorHandler(getServer(), _state.getContextHandler());
                             if (eh instanceof ErrorHandler.ErrorPageMapper)
                             {
-                                String error_page=((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_state.getAsyncContextEvent().getSuppliedRequest());
-                                if (error_page!=null)
+                                String error_page = ((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_state.getAsyncContextEvent().getSuppliedRequest());
+                                if (error_page != null)
                                     _state.getAsyncContextEvent().setDispatchPath(error_page);
                             }
 
-                            getServer().handleAsync(this);
-                            break;
-
-                        case READ_CALLBACK:
-                        {
-                            ContextHandler handler=_state.getContextHandler();
-                            if (handler!=null)
-                                handler.handle(_request.getHttpInput());
-                            else
-                                _request.getHttpInput().run();
-                            break;
+                            try
+                            {
+                                _request.setDispatcherType(DispatcherType.ERROR);
+                                getServer().handleAsync(this);
+                            }
+                            finally
+                            {
+                                _request.setDispatcherType(null);
+                            }
                         }
-
-                        case WRITE_CALLBACK:
-                        {
-                            ContextHandler handler=_state.getContextHandler();
-
-                            if (handler!=null)
-                                handler.handle(_response.getHttpOutput());
-                            else
-                                _response.getHttpOutput().run();
-                            break;
-                        }
-
-                        default:
-                            break loop;
-
+                        break;
                     }
-                }
-                catch (Error e)
-                {
-                    if ("ContinuationThrowable".equals(e.getClass().getSimpleName()))
-                        LOG.ignore(e);
-                    else
+
+                    case READ_CALLBACK:
                     {
-                        error=true;
-                        LOG.warn(String.valueOf(_uri), e);
-                        _state.error(e);
+                        ContextHandler handler=_state.getContextHandler();
+                        if (handler!=null)
+                            handler.handle(_request,_request.getHttpInput());
+                        else
+                            _request.getHttpInput().run();
+                        break;
+                    }
+
+                    case WRITE_CALLBACK:
+                    {
+                        ContextHandler handler=_state.getContextHandler();
+                        if (handler!=null)
+                            handler.handle(_request,_response.getHttpOutput());
+                        else
+                            _response.getHttpOutput().run();
+                        break;
+                    }
+
+                    case ASYNC_ERROR:
+                    {
+                        _state.onError();
+                        break;
+                    }
+
+                    case COMPLETE:
+                    {
+                        // TODO do onComplete here for continuations to work
+//                        _state.onComplete();
+
+                        if (!_response.isCommitted() && !_request.isHandled())
+                            _response.sendError(404);
+                        else
+                            _response.closeOutput();
                         _request.setHandled(true);
-                        handleException(e);
+
+                        // TODO do onComplete here to detect errors in final flush
+                         _state.onComplete();
+
+                        onCompleted();
+
+                        break loop;
+                    }
+
+                    default:
+                    {
+                        throw new IllegalStateException("state="+_state);
                     }
                 }
-                catch (Exception e)
+            }
+            catch (EofException|QuietServletException|BadMessageException e)
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug(e);
+                handleException(e);
+            }
+            catch (Throwable e)
+            {
+                if ("ContinuationThrowable".equals(e.getClass().getSimpleName()))
                 {
-                    error=true;
-                    if (e instanceof EofException)
-                        LOG.debug(e);
+                    LOG.ignore(e);
+                }
+                else
+                {
+                    if (_connector.isStarted())
+                        LOG.warn(String.valueOf(_request.getHttpURI()), e);
                     else
-                        LOG.warn(String.valueOf(_uri), e);
-                    _state.error(e);
-                    _request.setHandled(true);
+                        LOG.debug(String.valueOf(_request.getHttpURI()), e);
                     handleException(e);
                 }
-                finally
-                {
-                    if (error && _state.isAsyncStarted())
-                        _state.errorComplete();
-                    action = _state.unhandle();
-                }
             }
 
-            if (action==Action.COMPLETE)
-            {
-                try
-                {
-                    _state.completed();
-
-                    if (!_response.isCommitted() && !_request.isHandled())
-                    {
-                        _response.sendError(404);
-                    }
-                    else
-                    {
-                        // Complete generating the response
-                        _response.closeOutput();
-                    }
-                }
-                catch(EofException|ClosedChannelException e)
-                {
-                    LOG.debug(e);
-                }
-                catch(Exception e)
-                {
-                    LOG.warn("complete failed",e);
-                }
-                finally
-                {
-                    _request.setHandled(true);
-                    _transport.completed();
-                }
-            }
-        }
-        finally
-        {
-            setCurrentHttpChannel(last);
-            if (threadName != null && LOG.isDebugEnabled())
-                Thread.currentThread().setName(threadName);
+            action = _state.unhandle();
         }
 
         if (LOG.isDebugEnabled())
             LOG.debug("{} handle exit, result {}", this, action);
 
-        return action!=Action.WAIT;
+        boolean suspended=action==Action.WAIT;
+        return !suspended;
     }
 
     /**
@@ -461,47 +511,76 @@
      */
     protected void handleException(Throwable x)
     {
-        try
+        if (_state.isAsyncStarted())
         {
-            _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,x);
-            _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,x.getClass());
-            if (_state.isSuspended())
+            // Handle exception via AsyncListener onError
+            Throwable root = _state.getAsyncContextEvent().getThrowable();
+            if (root==null)
             {
-                HttpFields fields = new HttpFields();
-                fields.add(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);
-                ResponseInfo info = new ResponseInfo(_request.getHttpVersion(), fields, 0, HttpStatus.INTERNAL_SERVER_ERROR_500, null, _request.isHead());
-                boolean committed = sendResponse(info, null, true);
-                if (!committed)
-                    LOG.warn("Could not send response error 500: "+x);
-                _request.getAsyncContext().complete();
-            }
-            else if (isCommitted())
-            {
-                abort();
-                if (!(x instanceof EofException))
-                    LOG.warn("Could not send response error 500: "+x);
+                _state.error(x);
             }
             else
             {
-                _response.setHeader(HttpHeader.CONNECTION.asString(),HttpHeaderValue.CLOSE.asString());
-                _response.sendError(500, x.getMessage());
+                // TODO Can this happen?  Should this just be ISE???
+                // We've already processed an error before!
+                root.addSuppressed(x);
+                LOG.warn("Error while handling async error: ", root);
+                abort(x);
+                _state.errorComplete();
             }
         }
-        catch (IOException e)
+        else
         {
-            // We tried our best, just log
-            LOG.debug("Could not commit response error 500", e);
+            try
+            {
+                // Handle error normally
+                _request.setHandled(true);
+                _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, x);
+                _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, x.getClass());
+
+                if (isCommitted())
+                {
+                    abort(x);
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Could not send response error 500, already committed", x);
+                }
+                else
+                {
+                    _response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
+
+                    if (x instanceof BadMessageException)
+                    {
+                        BadMessageException bme = (BadMessageException)x;
+                        _response.sendError(bme.getCode(), bme.getReason());
+                    }
+                    else if (x instanceof UnavailableException)
+                    {
+                        if (((UnavailableException)x).isPermanent())
+                            _response.sendError(HttpStatus.NOT_FOUND_404);
+                        else
+                            _response.sendError(HttpStatus.SERVICE_UNAVAILABLE_503);
+                    }
+                    else
+                        _response.sendError(HttpStatus.INTERNAL_SERVER_ERROR_500);
+                }
+            }
+            catch (Throwable e)
+            {
+                abort(e);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Could not commit response error 500", e);
+            }
         }
     }
 
     public boolean isExpecting100Continue()
     {
-        return _expect100Continue;
+        return false;
     }
 
     public boolean isExpecting102Processing()
     {
-        return _expect102Processing;
+        return false;
     }
 
     @Override
@@ -513,212 +592,93 @@
                 _requests,
                 _committed.get(),
                 _state.getState(),
-                _uri);
+                _request.getHttpURI());
     }
 
-    @Override
-    public void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort)
-    {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean startRequest(HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version)
-    {
-        _expect = false;
-        _expect100Continue = false;
-        _expect102Processing = false;
-
-        _request.setTimeStamp(System.currentTimeMillis());
-        _request.setMethod(httpMethod, method);
-
-        if (httpMethod == HttpMethod.CONNECT)
-            _uri.parseConnect(uri.array(),uri.arrayOffset()+uri.position(),uri.remaining());
-        else
-            _uri.parse(uri.array(),uri.arrayOffset()+uri.position(),uri.remaining());
-        _request.setUri(_uri);
-
-        String path;
-        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(StandardCharsets.ISO_8859_1);
-        }
-
-        String info = URIUtil.canonicalPath(path);
-
-        if (info == null)
-        {
-            if( path==null && _uri.getScheme()!=null &&_uri.getHost()!=null)
-            {
-                info = "/";
-                _request.setRequestURI("");
-            }
-            else
-            {
-                badMessage(400,null);
-                return true;
-            }
-        }
-        _request.setPathInfo(info);
-        _version = version == null ? HttpVersion.HTTP_0_9 : version;
-        _request.setHttpVersion(_version);
-
-        return false;
-    }
-
-    @Override
-    public boolean parsedHeader(HttpField field)
-    {
-        HttpHeader header=field.getHeader();
-        String value=field.getValue();
-        if (value == null)
-            value = "";
-        if (header != null)
-        {
-            switch (header)
-            {
-                case EXPECT:
-                    if (_version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
-                    {
-                        HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
-                        switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
-                        {
-                            case CONTINUE:
-                                _expect100Continue = true;
-                                break;
-
-                            case PROCESSING:
-                                _expect102Processing = true;
-                                break;
-
-                            default:
-                                String[] values = value.split(",");
-                                for (int i = 0; i < values.length; i++)
-                                {
-                                    expect = HttpHeaderValue.CACHE.get(values[i].trim());
-                                    if (expect == null)
-                                        _expect = true;
-                                    else
-                                    {
-                                        switch (expect)
-                                        {
-                                            case CONTINUE:
-                                                _expect100Continue = true;
-                                                break;
-                                            case PROCESSING:
-                                                _expect102Processing = true;
-                                                break;
-                                            default:
-                                                _expect = true;
-                                        }
-                                    }
-                                }
-                        }
-                    }
-                    break;
-
-                case CONTENT_TYPE:
-                    MimeTypes.Type mime = MimeTypes.CACHE.get(value);
-                    String charset = (mime == null || mime.getCharset() == null) ? MimeTypes.getCharsetFromContentType(value) : mime.getCharset().toString();
-                    if (charset != null)
-                        _request.setCharacterEncodingUnchecked(charset);
-                    break;
-                default:
-            }
-        }
-
-        if (field.getName()!=null)
-            _request.getHttpFields().add(field);
-        return false;
-    }
-
-    @Override
-    public boolean parsedHostHeader(String host, int port)
-    {
-        if (_uri.getHost()==null)
-        {
-            _request.setServerName(host);
-            _request.setServerPort(port);
-        }
-        return false;
-    }
-
-    @Override
-    public boolean headerComplete()
+    public void onRequest(MetaData.Request request)
     {
         _requests.incrementAndGet();
+        _request.setTimeStamp(System.currentTimeMillis());
         HttpFields fields = _response.getHttpFields();
-        switch (_version)
-        {
-            case HTTP_0_9:
-                break;
+        if (_configuration.getSendDateHeader() && !fields.contains(HttpHeader.DATE))
+            fields.put(_connector.getServer().getDateField());
 
-            case HTTP_1_0:
-                if (_configuration.getSendDateHeader() && !fields.contains(HttpHeader.DATE))
-                    _response.getHttpFields().add(_connector.getServer().getDateField());
-                break;
+        long idleTO=_configuration.getIdleTimeout();
+        _oldIdleTimeout=getIdleTimeout();
+        if (idleTO>=0 && _oldIdleTimeout!=idleTO)
+            setIdleTimeout(idleTO);
+        
+        _request.setMetaData(request);
 
-            case HTTP_1_1:
-                if (_configuration.getSendDateHeader() && !fields.contains(HttpHeader.DATE))
-                    _response.getHttpFields().add(_connector.getServer().getDateField());
-
-                if (_expect)
-                {
-                    badMessage(HttpStatus.EXPECTATION_FAILED_417,null);
-                    return true;
-                }
-
-                break;
-
-            default:
-                throw new IllegalStateException();
-        }
-
-        return true;
+        if (LOG.isDebugEnabled())
+            LOG.debug("REQUEST for {} on {}{}{} {} {}{}{}",request.getURIString(),this,System.lineSeparator(),
+                request.getMethod(),request.getURIString(),request.getHttpVersion(),System.lineSeparator(),
+                request.getFields());
     }
 
-    @Override
-    public boolean content(T item)
+    public boolean onContent(HttpInput.Content content)
     {
         if (LOG.isDebugEnabled())
-            LOG.debug("{} content {}", this, item);
-        @SuppressWarnings("unchecked")
-        HttpInput<T> input = (HttpInput<T>)_request.getHttpInput();
-        input.content(item);
+            LOG.debug("{} content {}", this, content);
 
+        return _request.getHttpInput().addContent(content);
+    }
+
+    public boolean onContentComplete()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} onContentComplete", this);
         return false;
     }
-
-    @Override
-    public boolean messageComplete()
+    
+    public boolean onRequestComplete()
     {
         if (LOG.isDebugEnabled())
-            LOG.debug("{} messageComplete", this);
-        _request.getHttpInput().messageComplete();
-        return true;
+            LOG.debug("{} onRequestComplete", this);
+        return _request.getHttpInput().eof();
     }
 
-    @Override
-    public void earlyEOF()
+    public void onCompleted()
     {
-        _request.getHttpInput().earlyEOF();
+        if (LOG.isDebugEnabled())
+            LOG.debug("COMPLETE for {} written={}",getRequest().getRequestURI(),getBytesWritten());
+        
+        if (_requestLog!=null )
+            _requestLog.log(_request, _response);
+
+        long idleTO=_configuration.getIdleTimeout();
+        if (idleTO>=0 && getIdleTimeout()!=_oldIdleTimeout)
+            setIdleTimeout(_oldIdleTimeout);
+        
+        _transport.onCompleted();
     }
 
-    @Override
-    public void badMessage(int status, String reason)
+    public boolean onEarlyEOF()
+    {
+        return _request.getHttpInput().earlyEOF();
+    }
+
+    public void onBadMessage(int status, String reason)
     {
         if (status < 400 || status > 599)
             status = HttpStatus.BAD_REQUEST_400;
 
+        Action action;
         try
         {
-            if (_state.handling()==Action.REQUEST_DISPATCH)
+           action=_state.handling();
+        }
+        catch(IllegalStateException e)
+        {
+            // The bad message cannot be handled in the current state, so throw
+            // to hopefull somebody that can handle
+            abort(e);
+            throw new BadMessageException(status,reason);
+        }
+
+        try
+        {
+            if (action==Action.DISPATCH)
             {
                 ByteBuffer content=null;
                 HttpFields fields=new HttpFields();
@@ -727,7 +687,7 @@
                 if (handler!=null)
                     content=handler.badMessageError(status,reason,fields);
 
-                sendResponse(new ResponseInfo(HttpVersion.HTTP_1_1,fields,0,status,reason,false),content ,true);
+                sendResponse(new MetaData.Response(HttpVersion.HTTP_1_1,status,reason,fields,BufferUtil.length(content)),content ,true);
             }
         }
         catch (IOException e)
@@ -736,33 +696,45 @@
         }
         finally
         {
+            // TODO: review whether it's the right state to check.
             if (_state.unhandle()==Action.COMPLETE)
-                _state.completed();
+                _state.onComplete();
             else
-                throw new IllegalStateException();
+                throw new IllegalStateException(); // TODO: don't throw from finally blocks !
+            onCompleted();
         }
     }
 
-    protected boolean sendResponse(ResponseInfo info, ByteBuffer content, boolean complete, final Callback callback)
+    protected boolean sendResponse(MetaData.Response info, ByteBuffer content, boolean complete, final Callback callback)
     {
         boolean committing = _committed.compareAndSet(false, true);
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("sendResponse info={} content={} complete={} committing={} callback={}",
+                info,
+                BufferUtil.toDetailString(content),
+                complete,
+                committing,
+                callback);
+        
         if (committing)
         {
             // We need an info to commit
             if (info==null)
-                info = _response.newResponseInfo();
+                info = _response.newResponseMetaData();
+            commit(info);
 
             // wrap callback to process 100 responses
             final int status=info.getStatus();
             final Callback committed = (status<200&&status>=100)?new Commit100Callback(callback):new CommitCallback(callback);
 
             // committing write
-            _transport.send(info, content, complete, committed);
+            _transport.send(info, _request.isHead(), content, complete, committed);
         }
         else if (info==null)
         {
             // This is a normal write
-            _transport.send(content, complete, callback);
+            _transport.send(null,_request.isHead(), content, complete, callback);
         }
         else
         {
@@ -771,7 +743,7 @@
         return committing;
     }
 
-    protected boolean sendResponse(ResponseInfo info, ByteBuffer content, boolean complete) throws IOException
+    protected boolean sendResponse(MetaData.Response info, ByteBuffer content, boolean complete) throws IOException
     {
         try(Blocker blocker = _response.getHttpOutput().acquireWriteBlockingCallback())
         {
@@ -779,6 +751,22 @@
             blocker.block();
             return committing;
         }
+        catch (Throwable failure)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug(failure);
+            abort(failure);
+            throw failure;
+        }
+    }
+
+    protected void commit (MetaData.Response info)
+    {
+        _committedMetaData=info;
+        if (LOG.isDebugEnabled())
+            LOG.debug("COMMIT for {} on {}{}{} {} {}{}{}",getRequest().getRequestURI(),this,System.lineSeparator(),
+                info.getStatus(),info.getReason(),info.getHttpVersion(),System.lineSeparator(),
+                info.getFields());
     }
 
     public boolean isCommitted()
@@ -788,19 +776,33 @@
 
     /**
      * <p>Non-Blocking write, committing the response if needed.</p>
-     *
+     * Called as last link in HttpOutput.Filter chain
      * @param content  the content buffer to write
      * @param complete whether the content is complete for the response
      * @param callback Callback when complete or failed
      */
-    protected void write(ByteBuffer content, boolean complete, Callback callback)
+    @Override
+    public void write(ByteBuffer content, boolean complete, Callback callback)
     {
+        _written+=BufferUtil.length(content);
         sendResponse(null,content,complete,callback);
     }
+    
+    @Override 
+    public void resetBuffer()
+    {
+        if(isCommitted())
+            throw new IllegalStateException("Committed");
+    }
+
+    public HttpOutput.Interceptor getNextInterceptor()
+    {
+        return null;
+    }
 
     protected void execute(Runnable task)
     {
-        _connector.getExecutor().execute(task);
+        _executor.execute(task);
     }
 
     public Scheduler getScheduler()
@@ -808,7 +810,6 @@
         return _connector.getScheduler();
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @return true if the HttpChannel can efficiently use direct buffer (typically this means it is not over SSL or a multiplexed protocol)
      */
@@ -818,59 +819,55 @@
     }
 
     /**
-     * If a write or similar to this channel fails this method should be called. The standard implementation
-     * is to call {@link HttpTransport#abort()}
+     * If a write or similar operation to this channel fails,
+     * then this method should be called.
+     * <p>
+     * The standard implementation calls {@link HttpTransport#abort(Throwable)}.
+     *
+     * @param failure the failure that caused the abort.
      */
-    public void abort()
+    public void abort(Throwable failure)
     {
-        _transport.abort();
+        _transport.abort(failure);
     }
 
-    private class CommitCallback implements Callback
+    private class CommitCallback extends Callback.Nested
     {
-        private final Callback _callback;
-
         private CommitCallback(Callback callback)
         {
-            _callback = callback;
-        }
-
-        @Override
-        public void succeeded()
-        {
-            _callback.succeeded();
+            super(callback);
         }
 
         @Override
         public void failed(final Throwable x)
         {
-            if (x instanceof EofException || x instanceof ClosedChannelException)
+            if (LOG.isDebugEnabled())
+                LOG.debug("Commit failed", x);
+
+            if (x instanceof BadMessageException)
             {
-                LOG.debug(x);
-                _callback.failed(x);
-                _response.getHttpOutput().closed();
-            }
-            else
-            {
-                LOG.warn("Commit failed",x);
-                _transport.send(HttpGenerator.RESPONSE_500_INFO,null,true,new Callback()
+                _transport.send(HttpGenerator.RESPONSE_500_INFO, false, null, true, new Callback.Nested(this)
                 {
                     @Override
                     public void succeeded()
                     {
-                        _callback.failed(x);
+                        super.failed(x);
                         _response.getHttpOutput().closed();
                     }
 
                     @Override
                     public void failed(Throwable th)
                     {
-                        LOG.ignore(th);
-                        _callback.failed(x);
-                        _response.getHttpOutput().closed();
+                        _transport.abort(x);
+                        super.failed(x);
                     }
                 });
             }
+            else
+            {
+                _transport.abort(x);
+                super.failed(x);
+            }
         }
     }
 
@@ -892,4 +889,5 @@
 
     }
 
+
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java
new file mode 100644
index 0000000..4cde234
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java
@@ -0,0 +1,495 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpCompliance;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpHeaderValue;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpParser;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+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;
+
+/**
+ * A HttpChannel customized to be transported over the HTTP/1 protocol
+ */
+public class HttpChannelOverHttp extends HttpChannel implements HttpParser.RequestHandler, HttpParser.ComplianceHandler
+{
+    private static final Logger LOG = Log.getLogger(HttpChannelOverHttp.class);
+    private final static HttpField PREAMBLE_UPGRADE_H2C = new HttpField(HttpHeader.UPGRADE, "h2c");
+    private static final String ATTR_COMPLIANCE_VIOLATIONS = "org.eclipse.jetty.http.compliance.violations";
+
+    private final HttpFields _fields = new HttpFields();
+    private final MetaData.Request _metadata = new MetaData.Request(_fields);
+    private final HttpConnection _httpConnection;
+    private HttpField _connection;
+    private HttpField _upgrade = null;
+    private boolean _delayedForContent;
+    private boolean _unknownExpectation = false;
+    private boolean _expect100Continue = false;
+    private boolean _expect102Processing = false;
+    private List<String> _complianceViolations;
+
+    public HttpChannelOverHttp(HttpConnection httpConnection, Connector connector, HttpConfiguration config, EndPoint endPoint, HttpTransport transport)
+    {
+        super(connector, config, endPoint, transport);
+        _httpConnection = httpConnection;
+        _metadata.setURI(new HttpURI());
+    }
+
+    @Override
+    protected HttpInput newHttpInput(HttpChannelState state)
+    {
+        return new HttpInputOverHTTP(state);
+    }
+
+    @Override
+    public void recycle()
+    {
+        super.recycle();
+        _unknownExpectation = false;
+        _expect100Continue = false;
+        _expect102Processing = false;
+        _metadata.recycle();
+        _connection = null;
+        _fields.clear();
+        _upgrade = null;
+    }
+
+    @Override
+    public boolean isExpecting100Continue()
+    {
+        return _expect100Continue;
+    }
+
+    @Override
+    public boolean isExpecting102Processing()
+    {
+        return _expect102Processing;
+    }
+
+    @Override
+    public boolean startRequest(String method, String uri, HttpVersion version)
+    {
+        _metadata.setMethod(method);
+        _metadata.getURI().parseRequestTarget(method, uri);
+        _metadata.setHttpVersion(version);
+        _unknownExpectation = false;
+        _expect100Continue = false;
+        _expect102Processing = false;
+        return false;
+    }
+
+    @Override
+    public void parsedHeader(HttpField field)
+    {
+        HttpHeader header = field.getHeader();
+        String value = field.getValue();
+        if (header != null)
+        {
+            switch (header)
+            {
+                case CONNECTION:
+                    _connection = field;
+                    break;
+
+                case HOST:
+                    if (!_metadata.getURI().isAbsolute() && field instanceof HostPortHttpField)
+                    {
+                        HostPortHttpField hp = (HostPortHttpField)field;
+                        _metadata.getURI().setAuthority(hp.getHost(), hp.getPort());
+                    }
+                    break;
+
+                case EXPECT:
+                {
+                    if (_metadata.getHttpVersion() == HttpVersion.HTTP_1_1)
+                    {
+                        HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
+                        switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
+                        {
+                            case CONTINUE:
+                                _expect100Continue = true;
+                                break;
+
+                            case PROCESSING:
+                                _expect102Processing = true;
+                                break;
+
+                            default:
+                                String[] values = field.getValues();
+                                for (int i = 0; values != null && i < values.length; i++)
+                                {
+                                    expect = HttpHeaderValue.CACHE.get(values[i].trim());
+                                    if (expect == null)
+                                        _unknownExpectation = true;
+                                    else
+                                    {
+                                        switch (expect)
+                                        {
+                                            case CONTINUE:
+                                                _expect100Continue = true;
+                                                break;
+                                            case PROCESSING:
+                                                _expect102Processing = true;
+                                                break;
+                                            default:
+                                                _unknownExpectation = true;
+                                        }
+                                    }
+                                }
+                        }
+                    }
+                    break;
+                }
+
+                case UPGRADE:
+                    _upgrade = field;
+                    break;
+
+                default:
+                    break;
+            }
+        }
+        _fields.add(field);
+    }
+
+    /**
+     * 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.
+     *
+     * @throws IOException if the InputStream cannot be created
+     */
+    @Override
+    public void continue100(int available) throws IOException
+    {
+        // If the client is expecting 100 CONTINUE, then send it now.
+        // TODO: consider using an AtomicBoolean ?
+        if (isExpecting100Continue())
+        {
+            _expect100Continue = false;
+
+            // is content missing?
+            if (available == 0)
+            {
+                if (getResponse().isCommitted())
+                    throw new IOException("Committed before 100 Continues");
+
+                boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false);
+                if (!committed)
+                    throw new IOException("Concurrent commit while trying to send 100-Continue");
+            }
+        }
+    }
+
+    @Override
+    public void earlyEOF()
+    {
+        _httpConnection.getGenerator().setPersistent(false);
+        // If we have no request yet, just close
+        if (_metadata.getMethod() == null)
+            _httpConnection.close();
+        else if (onEarlyEOF() || _delayedForContent)
+        { 
+            _delayedForContent = false;
+            handle();
+        }
+    }
+
+    @Override
+    public boolean content(ByteBuffer content)
+    {
+        HttpInput.Content c = _httpConnection.newContent(content);
+        boolean handle = onContent(c) || _delayedForContent;
+        _delayedForContent = false;
+        return handle;
+    }
+
+    public void asyncReadFillInterested()
+    {
+        _httpConnection.asyncReadFillInterested();
+    }
+
+    @Override
+    public void badMessage(int status, String reason)
+    {
+        _httpConnection.getGenerator().setPersistent(false);
+        try
+        {
+            // Need to call onRequest, so RequestLog can reports as much as possible
+            onRequest(_metadata);
+            getRequest().getHttpInput().earlyEOF();
+        }
+        catch (Exception e)
+        {
+            LOG.ignore(e);
+        }
+
+        onBadMessage(status, reason);
+    }
+
+    @Override
+    public boolean headerComplete()
+    {
+        if (_complianceViolations != null)
+            this.getRequest().setAttribute(ATTR_COMPLIANCE_VIOLATIONS, _complianceViolations);
+
+        boolean persistent;
+
+        switch (_metadata.getHttpVersion())
+        {
+            case HTTP_0_9:
+            {
+                persistent = false;
+                break;
+            }
+            case HTTP_1_0:
+            {
+                if (getHttpConfiguration().isPersistentConnectionsEnabled())
+                {
+                    if (_connection != null)
+                    {
+                        if (_connection.contains(HttpHeaderValue.KEEP_ALIVE.asString()))
+                            persistent = true;
+                        else
+                            persistent = _fields.contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString());
+                    }
+                    else
+                        persistent = false;
+                }
+                else
+                    persistent = false;
+
+                if (!persistent)
+                    persistent = HttpMethod.CONNECT.is(_metadata.getMethod());
+                if (persistent)
+                    getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
+
+                break;
+            }
+
+            case HTTP_1_1:
+            {
+                if (_unknownExpectation)
+                {
+                    badMessage(HttpStatus.EXPECTATION_FAILED_417, null);
+                    return false;
+                }
+
+                if (getHttpConfiguration().isPersistentConnectionsEnabled())
+                {
+                    if (_connection != null)
+                    {
+                        if (_connection.contains(HttpHeaderValue.CLOSE.asString()))
+                            persistent = false;
+                        else
+                            persistent = !_fields.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()); // handle multiple connection fields
+                    }
+                    else
+                        persistent = true;
+                }
+                else
+                    persistent = false;
+
+                if (!persistent)
+                    persistent = HttpMethod.CONNECT.is(_metadata.getMethod());
+                if (!persistent)
+                    getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE);
+
+                if (_upgrade != null && upgrade())
+                    return true;
+
+                break;
+            }
+
+            case HTTP_2:
+            {
+                // Allow direct "upgrade" to HTTP_2_0 only if the connector supports h2c.
+                _upgrade = PREAMBLE_UPGRADE_H2C;
+
+                if (HttpMethod.PRI.is(_metadata.getMethod()) &&
+                        "*".equals(_metadata.getURI().toString()) &&
+                        _fields.size() == 0 &&
+                        upgrade())
+                    return true;
+
+                badMessage(HttpStatus.UPGRADE_REQUIRED_426, null);
+                _httpConnection.getParser().close();
+                return false;
+            }
+
+            default:
+            {
+                throw new IllegalStateException("unsupported version " + _metadata.getHttpVersion());
+            }
+        }
+
+        if (!persistent)
+            _httpConnection.getGenerator().setPersistent(false);
+
+        onRequest(_metadata);
+
+        // Should we delay dispatch until we have some content?
+        // We should not delay if there is no content expect or client is expecting 100 or the response is already committed or the request buffer already has something in it to parse
+        _delayedForContent = (getHttpConfiguration().isDelayDispatchUntilContent()
+                && (_httpConnection.getParser().getContentLength() > 0 || _httpConnection.getParser().isChunking())
+                && !isExpecting100Continue()
+                && !isCommitted()
+                && _httpConnection.isRequestBufferEmpty());
+
+        return !_delayedForContent;
+    }
+
+
+    /**
+     * <p>Attempts to perform a HTTP/1.1 upgrade.</p>
+     * <p>The upgrade looks up a {@link ConnectionFactory.Upgrading} from the connector
+     * matching the protocol specified in the {@code Upgrade} header.</p>
+     * <p>The upgrade may succeed, be ignored (which can allow a later handler to implement)
+     * or fail with a {@link BadMessageException}.</p>
+     *
+     * @return true if the upgrade was performed, false if it was ignored
+     * @throws BadMessageException if the upgrade failed
+     */
+    private boolean upgrade() throws BadMessageException
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("upgrade {} {}", this, _upgrade);
+
+        if (_upgrade != PREAMBLE_UPGRADE_H2C && (_connection == null || !_connection.contains("upgrade")))
+            throw new BadMessageException(HttpStatus.BAD_REQUEST_400);
+
+        // Find the upgrade factory
+        ConnectionFactory.Upgrading factory = null;
+        for (ConnectionFactory f : getConnector().getConnectionFactories())
+        {
+            if (f instanceof ConnectionFactory.Upgrading)
+            {
+                if (f.getProtocols().contains(_upgrade.getValue()))
+                {
+                    factory = (ConnectionFactory.Upgrading)f;
+                    break;
+                }
+            }
+        }
+
+        if (factory == null)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("No factory for {} in {}", _upgrade, getConnector());
+            return false;
+        }
+
+        // Create new connection
+        HttpFields response101 = new HttpFields();
+        Connection upgrade_connection = factory.upgradeConnection(getConnector(), getEndPoint(), _metadata, response101);
+        if (upgrade_connection == null)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Upgrade ignored for {} by {}", _upgrade, factory);
+            return false;
+        }
+
+        // Send 101 if needed
+        try
+        {
+            if (_upgrade != PREAMBLE_UPGRADE_H2C)
+                sendResponse(new MetaData.Response(HttpVersion.HTTP_1_1, HttpStatus.SWITCHING_PROTOCOLS_101, response101, 0), null, true);
+        }
+        catch (IOException e)
+        {
+            throw new BadMessageException(HttpStatus.INTERNAL_SERVER_ERROR_500, null, e);
+        }
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("Upgrade from {} to {}", getEndPoint().getConnection(), upgrade_connection);
+        getRequest().setAttribute(HttpConnection.UPGRADE_CONNECTION_ATTRIBUTE, upgrade_connection);
+        getResponse().setStatus(101);
+        getHttpTransport().onCompleted();
+        return true;
+    }
+
+    @Override
+    protected void handleException(Throwable x)
+    {
+        _httpConnection.getGenerator().setPersistent(false);
+        super.handleException(x);
+    }
+
+    @Override
+    public void abort(Throwable failure)
+    {
+        super.abort(failure);
+        _httpConnection.getGenerator().setPersistent(false);
+    }
+
+    @Override
+    public boolean contentComplete()
+    {
+        boolean handle = onContentComplete() || _delayedForContent;
+        _delayedForContent = false;
+        return handle;
+    }
+
+    @Override
+    public boolean messageComplete()
+    {
+        return onRequestComplete();
+    }
+
+    @Override
+    public int getHeaderCacheSize()
+    {
+        return getHttpConfiguration().getHeaderCacheSize();
+    }
+
+    @Override
+    public void onComplianceViolation(HttpCompliance compliance, HttpCompliance required, String reason)
+    {
+        if (_httpConnection.isRecordHttpComplianceViolations())
+        {
+            if (_complianceViolations == null)
+            {
+                _complianceViolations = new ArrayList<>();
+            }
+            String violation = String.format("%s<%s: %s for %s", compliance, required, reason, getHttpTransport());
+            _complianceViolations.add(violation);
+            if (LOG.isDebugEnabled())
+                LOG.debug(violation);
+        }
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java
index 115ae1d..02940d2 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java
@@ -21,6 +21,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import javax.servlet.AsyncListener;
 import javax.servlet.RequestDispatcher;
@@ -31,6 +32,7 @@
 import org.eclipse.jetty.server.handler.ContextHandler.Context;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Locker;
 import org.eclipse.jetty.util.thread.Scheduler;
 
 /**
@@ -42,17 +44,18 @@
 
     private final static long DEFAULT_TIMEOUT=Long.getLong("org.eclipse.jetty.server.HttpChannelState.DEFAULT_TIMEOUT",30000L);
 
-    /** The dispatched state of the HttpChannel, used to control the overall livecycle
+    /**
+     * The dispatched state of the HttpChannel, used to control the overall lifecycle
      */
     public enum State
     {
         IDLE,             // Idle request
         DISPATCHED,       // Request dispatched to filter/servlet
-        ASYNC_WAIT,       // Suspended and parked
-        ASYNC_WOKEN,      // A thread has been dispatch to handle from ASYNCWAIT
-        ASYNC_IO,         // Has been dispatched for async IO
-        COMPLETING,       // Request is completable
-        COMPLETED,        // Request is complete
+        ASYNC_WAIT,       // Suspended and waiting
+        ASYNC_WOKEN,      // Dispatch to handle from ASYNC_WAIT
+        ASYNC_IO,         // Dispatched for async IO
+        COMPLETING,       // Response is completable
+        COMPLETED,        // Response is completed
         UPGRADED          // Request upgraded the connection
     }
 
@@ -61,52 +64,74 @@
      */
     public enum Action
     {
-        REQUEST_DISPATCH, // handle a normal request dispatch  
+        DISPATCH,         // handle a normal request dispatch
         ASYNC_DISPATCH,   // handle an async request dispatch
-        ASYNC_EXPIRED,    // handle an async timeout
+        ERROR_DISPATCH,   // handle a normal error
+        ASYNC_ERROR,      // handle an async error
         WRITE_CALLBACK,   // handle an IO write callback
         READ_CALLBACK,    // handle an IO read callback
-        WAIT,             // Wait for further events 
-        COMPLETE          // Complete the channel
+        COMPLETE,         // Complete the response
+        TERMINATED,       // No further actions
+        WAIT,             // Wait for further events
     }
-    
+
     /**
-     * The state of the servlet async API.  This can lead or follow the 
+     * The state of the servlet async API.  This can lead or follow the
      * channel dispatch state and also includes reasons such as expired,
      * dispatched or completed.
      */
     public enum Async
     {
-        STARTED,
-        DISPATCH,
-        COMPLETE,
-        EXPIRING,
-        EXPIRED
+        NOT_ASYNC,
+        STARTED,          // AsyncContext.startAsync() has been called
+        DISPATCH,         //
+        COMPLETE,         // AsyncContext.complete() has been called
+        EXPIRING,         // AsyncContext timeout just happened
+        EXPIRED,          // AsyncContext timeout has been processed
+        ERRORING,         // An error just happened
+        ERRORED           // The error has been processed
     }
 
+    public enum Interest
+    {
+        NONE(false),
+        NEEDED(true),
+        REGISTERED(true);
+        
+        final boolean _interested;
+        boolean isInterested() { return _interested;}
+        
+        Interest(boolean interest)
+        {
+            _interested = interest;
+        }
+    }
+    
     private final boolean DEBUG=LOG.isDebugEnabled();
-    private final HttpChannel<?> _channel;
+    private final Locker _locker=new Locker();
+    private final HttpChannel _channel;
 
     private List<AsyncListener> _asyncListeners;
     private State _state;
     private Async _async;
     private boolean _initial;
-    private boolean _asyncRead;
-    private boolean _asyncWrite;
+    private boolean _asyncReadPossible;
+    private Interest _asyncRead=Interest.NONE;
+    private boolean _asyncWritePossible;
     private long _timeoutMs=DEFAULT_TIMEOUT;
     private AsyncContextEvent _event;
 
-    protected HttpChannelState(HttpChannel<?> channel)
+    protected HttpChannelState(HttpChannel channel)
     {
         _channel=channel;
         _state=State.IDLE;
-        _async=null;
+        _async=Async.NOT_ASYNC;
         _initial=true;
     }
 
     public State getState()
     {
-        synchronized(this)
+        try(Locker.Lock lock= _locker.lock())
         {
             return _state;
         }
@@ -114,7 +139,7 @@
 
     public void addListener(AsyncListener listener)
     {
-        synchronized(this)
+        try(Locker.Lock lock= _locker.lock())
         {
             if (_asyncListeners==null)
                 _asyncListeners=new ArrayList<>();
@@ -124,7 +149,7 @@
 
     public void setTimeout(long ms)
     {
-        synchronized(this)
+        try(Locker.Lock lock= _locker.lock())
         {
             _timeoutMs=ms;
         }
@@ -132,7 +157,7 @@
 
     public long getTimeout()
     {
-        synchronized(this)
+        try(Locker.Lock lock= _locker.lock())
         {
             return _timeoutMs;
         }
@@ -140,7 +165,7 @@
 
     public AsyncContextEvent getAsyncContextEvent()
     {
-        synchronized(this)
+        try(Locker.Lock lock= _locker.lock())
         {
             return _event;
         }
@@ -149,17 +174,30 @@
     @Override
     public String toString()
     {
-        synchronized (this)
+        try(Locker.Lock lock= _locker.lock())
         {
-            return String.format("%s@%x{s=%s i=%b a=%s}",getClass().getSimpleName(),hashCode(),_state,_initial,_async);
+            return String.format("%s@%x{s=%s a=%s i=%b r=%s/%s w=%b}",
+                getClass().getSimpleName(),
+                hashCode(),
+                _state,
+                _async,
+                _initial,
+                _asyncRead,
+                _asyncReadPossible,
+                _asyncWritePossible);
         }
     }
 
+    private String getStatusStringLocked()
+    {
+        return String.format("s=%s i=%b a=%s",_state,_initial,_async);
+    }
+
     public String getStatusString()
     {
-        synchronized (this)
+        try(Locker.Lock lock= _locker.lock())
         {
-            return String.format("s=%s i=%b a=%s",_state,_initial,_async);
+            return getStatusStringLocked();
         }
     }
 
@@ -168,68 +206,71 @@
      */
     protected Action handling()
     {
-        synchronized (this)
+        if(DEBUG)
+            LOG.debug("{} handling {}",this,_state);
+        try(Locker.Lock lock= _locker.lock())
         {
-            if(DEBUG)
-                LOG.debug("{} handling {}",this,_state);
             switch(_state)
             {
                 case IDLE:
                     _initial=true;
                     _state=State.DISPATCHED;
-                    return Action.REQUEST_DISPATCH;
+                    return Action.DISPATCH;
 
                 case COMPLETING:
-                    return Action.COMPLETE;
-
                 case COMPLETED:
-                    return Action.WAIT;
+                    return Action.TERMINATED;
 
                 case ASYNC_WOKEN:
-                    if (_asyncRead)
+                    if (_asyncRead.isInterested() && _asyncReadPossible)
                     {
                         _state=State.ASYNC_IO;
-                        _asyncRead=false;
+                        _asyncRead=Interest.NONE;
                         return Action.READ_CALLBACK;
                     }
-                    if (_asyncWrite)
+
+                    if (_asyncWritePossible)
                     {
                         _state=State.ASYNC_IO;
-                        _asyncWrite=false;
+                        _asyncWritePossible=false;
                         return Action.WRITE_CALLBACK;
                     }
-                    
-                    if (_async!=null)
+
+                    switch(_async)
                     {
-                        Async async=_async;
-                        switch(async)
-                        {
-                            case COMPLETE:
-                                _state=State.COMPLETING;
-                                return Action.COMPLETE;
-                            case DISPATCH:
-                                _state=State.DISPATCHED;
-                                _async=null;
-                                return Action.ASYNC_DISPATCH;
-                            case EXPIRING:
-                                break;
-                            case EXPIRED:
-                                _state=State.DISPATCHED;
-                                _async=null;
-                                return Action.ASYNC_EXPIRED;
-                            case STARTED:
-                                // TODO
-                                if (DEBUG)
-                                    LOG.debug("TODO Fix this double dispatch",new IllegalStateException(this
-                                            .getStatusString()));
-                                return Action.WAIT;
-                        }
+                        case COMPLETE:
+                            _state=State.COMPLETING;
+                            return Action.COMPLETE;
+                        case DISPATCH:
+                            _state=State.DISPATCHED;
+                            _async=Async.NOT_ASYNC;
+                            return Action.ASYNC_DISPATCH;
+                        case EXPIRING:
+                            break;
+                        case EXPIRED:
+                            _state=State.DISPATCHED;
+                            _async=Async.NOT_ASYNC;
+                            return Action.ERROR_DISPATCH;
+                        case STARTED:
+                            return Action.WAIT;
+                        case ERRORING:
+                            _state=State.DISPATCHED;
+                            return Action.ASYNC_ERROR;
+                        case NOT_ASYNC:
+                            break;
+                        default:
+                            throw new IllegalStateException(getStatusStringLocked());
                     }
-                    
+
                     return Action.WAIT;
 
+                case ASYNC_IO:
+                case ASYNC_WAIT:
+                case DISPATCHED:
+                case UPGRADED:
                 default:
-                    throw new IllegalStateException(this.getStatusString());
+                    throw new IllegalStateException(getStatusStringLocked());
+
             }
         }
     }
@@ -237,40 +278,95 @@
     public void startAsync(AsyncContextEvent event)
     {
         final List<AsyncListener> lastAsyncListeners;
-        
-        synchronized (this)
+
+        try(Locker.Lock lock= _locker.lock())
         {
-            if (_state!=State.DISPATCHED || _async!=null)
-                throw new IllegalStateException(this.getStatusString());
-            
+            if (_state!=State.DISPATCHED || _async!=Async.NOT_ASYNC)
+                throw new IllegalStateException(this.getStatusStringLocked());
+
             _async=Async.STARTED;
             _event=event;
             lastAsyncListeners=_asyncListeners;
-            _asyncListeners=null;
+            _asyncListeners=null;            
         }
 
         if (lastAsyncListeners!=null)
         {
-            for (AsyncListener listener : lastAsyncListeners)
+            Runnable callback=new Runnable()
             {
-                try
+                @Override
+                public void run()
                 {
-                    listener.onStartAsync(event);
+                    for (AsyncListener listener : lastAsyncListeners)
+                    {
+                        try
+                        {
+                            listener.onStartAsync(event);
+                        }
+                        catch(Throwable e)
+                        {
+                            // TODO Async Dispatch Error
+                            LOG.warn(e);
+                        }
+                    }
                 }
-                catch(Exception e)
+                @Override
+                public String toString()
                 {
-                    LOG.warn(e);
+                    return "startAsync";
                 }
-            }
+            };
+                  
+            runInContext(event,callback);
         }
     }
 
     protected void error(Throwable th)
     {
-        synchronized (this)
+        try(Locker.Lock lock= _locker.lock())
         {
             if (_event!=null)
-                _event.setThrowable(th);
+                _event.addThrowable(th);
+            _async=Async.ERRORING;
+        }
+    }
+
+    public void asyncError(Throwable failure)
+    {
+        AsyncContextEvent event = null;
+        try (Locker.Lock lock= _locker.lock())
+        {
+            switch (_state)
+            {
+                case IDLE:
+                case DISPATCHED:
+                case COMPLETING:
+                case COMPLETED:
+                case UPGRADED:
+                case ASYNC_IO:
+                case ASYNC_WOKEN:
+                {
+                    break;
+                }
+                case ASYNC_WAIT:
+                {
+                    _event.addThrowable(failure);
+                    _state=State.ASYNC_WOKEN;
+                    _async=Async.ERRORING;
+                    event=_event;
+                    break;
+                }
+                default:
+                {
+                    throw new IllegalStateException(getStatusStringLocked());
+                }
+            }
+        }
+
+        if (event != null)
+        {
+            cancelTimeout(event);
+            runInContext(event, _channel);
         }
     }
 
@@ -283,182 +379,288 @@
      */
     protected Action unhandle()
     {
-        synchronized (this)
+        Action action;
+        boolean read_interested=false;
+
+        if(DEBUG)
+            LOG.debug("{} unhandle {}",this,_state);
+
+        try(Locker.Lock lock= _locker.lock())
         {
-            if(DEBUG)
-                LOG.debug("{} unhandle {}",this,_state);
-            
             switch(_state)
             {
+                case COMPLETING:
+                case COMPLETED:
+                    return Action.TERMINATED;
+
                 case DISPATCHED:
                 case ASYNC_IO:
                     break;
+
                 default:
-                    throw new IllegalStateException(this.getStatusString());
+                    throw new IllegalStateException(this.getStatusStringLocked());
             }
 
-            if (_asyncRead)
+            _initial=false;
+            switch(_async)
             {
-                _state=State.ASYNC_IO;
-                _asyncRead=false;
-                return Action.READ_CALLBACK;
-            }
-            
-            if (_asyncWrite)
-            {
-                _asyncWrite=false;
-                _state=State.ASYNC_IO;
-                return Action.WRITE_CALLBACK;
-            }
+                case COMPLETE:
+                    _state=State.COMPLETING;
+                    _async=Async.NOT_ASYNC;
+                    action=Action.COMPLETE;
+                    break;
 
-            if (_async!=null)
-            {
-                _initial=false;
-                switch(_async)
-                {
-                    case COMPLETE:
-                        _state=State.COMPLETING;
-                        _async=null;
-                        return Action.COMPLETE;
-                    case DISPATCH:
-                        _state=State.DISPATCHED;
-                        _async=null;
-                        return Action.ASYNC_DISPATCH;
-                    case EXPIRED:
-                        _state=State.DISPATCHED;
-                        _async=null;
-                        return Action.ASYNC_EXPIRED;
-                    case EXPIRING:
-                    case STARTED:
-                        scheduleTimeout();
+                case DISPATCH:
+                    _state=State.DISPATCHED;
+                    _async=Async.NOT_ASYNC;
+                    action=Action.ASYNC_DISPATCH;
+                    break;
+
+                case EXPIRED:
+                    _state=State.DISPATCHED;
+                    _async=Async.NOT_ASYNC;
+                    action=Action.ERROR_DISPATCH;
+                    break;
+
+                case STARTED:
+                    if (_asyncRead.isInterested() && _asyncReadPossible)
+                    {
+                        _state=State.ASYNC_IO;
+                        _asyncRead=Interest.NONE;
+                        action=Action.READ_CALLBACK;
+                    }
+                    else if (_asyncWritePossible)
+                    {
+                        _state=State.ASYNC_IO;
+                        _asyncWritePossible=false;
+                        action=Action.WRITE_CALLBACK;
+                    }
+                    else
+                    {
                         _state=State.ASYNC_WAIT;
-                        return Action.WAIT;
-                }
+                        action=Action.WAIT; 
+                        if (_asyncRead==Interest.NEEDED)
+                        {
+                            _asyncRead=Interest.REGISTERED;
+                            read_interested=true;
+                        }
+                        Scheduler scheduler=_channel.getScheduler();
+                        if (scheduler!=null && _timeoutMs>0)
+                            _event.setTimeoutTask(scheduler.schedule(_event,_timeoutMs,TimeUnit.MILLISECONDS));
+                    }
+                    break;
+
+                case EXPIRING:
+                    _state=State.ASYNC_WAIT;
+                    action=Action.WAIT;
+                    break;
+
+                case ERRORING:
+                    _state=State.DISPATCHED;
+                    action=Action.ASYNC_ERROR;
+                    break;
+
+                case ERRORED:
+                    _state=State.DISPATCHED;
+                    action=Action.ERROR_DISPATCH;
+                    _async=Async.NOT_ASYNC;
+                    break;
+
+                case NOT_ASYNC:
+                    _state=State.COMPLETING;
+                    action=Action.COMPLETE;
+                    break;
+
+                default:
+                    _state=State.COMPLETING;
+                    action=Action.COMPLETE;
+                    break;
             }
-            
-            _state=State.COMPLETING;
-            return Action.COMPLETE;
         }
+
+        if (read_interested)
+            _channel.asyncReadFillInterested();
+
+        return action;
     }
 
     public void dispatch(ServletContext context, String path)
     {
-        boolean dispatch;
-        synchronized (this)
+        boolean dispatch=false;
+        AsyncContextEvent event=null;
+        try(Locker.Lock lock= _locker.lock())
         {
-            if (_async!=Async.STARTED && _async!=Async.EXPIRING)
-                throw new IllegalStateException("AsyncContext#dispath "+this.getStatusString());
+            boolean started=false;
+            event=_event;
+            switch(_async)
+            {
+                case STARTED:
+                    started=true;
+                    break;
+                case EXPIRING:
+                case ERRORED:
+                    break;
+                default:
+                    throw new IllegalStateException(this.getStatusStringLocked());
+            }
             _async=Async.DISPATCH;
-            
+
             if (context!=null)
                 _event.setDispatchContext(context);
             if (path!=null)
                 _event.setDispatchPath(path);
-           
-            switch(_state)
+
+            if (started)
             {
-                case DISPATCHED:
-                case ASYNC_IO:
-                    dispatch=false;
-                    break;
-                case ASYNC_WAIT:
-                    _state=State.ASYNC_WOKEN;
-                    dispatch=true;
-                    break;
-                case ASYNC_WOKEN:
-                    dispatch=false;
-                    break;
-                default:
-                    LOG.warn("async dispatched when complete {}",this);
-                    dispatch=false;
-                    break;
+                switch(_state)
+                {
+                    case DISPATCHED:
+                    case ASYNC_IO:
+                    case ASYNC_WOKEN:
+                        break;
+                    case ASYNC_WAIT:
+                        _state=State.ASYNC_WOKEN;
+                        dispatch=true;
+                        break;
+                    default:
+                        LOG.warn("async dispatched when complete {}",this);
+                        break;
+                }
             }
         }
 
-        cancelTimeout();
+        cancelTimeout(event);
         if (dispatch)
             scheduleDispatch();
     }
 
-    protected void expired()
+    protected void onTimeout()
     {
-        final List<AsyncListener> aListeners;
+        final List<AsyncListener> listeners;
         AsyncContextEvent event;
-        synchronized (this)
+        try(Locker.Lock lock= _locker.lock())
         {
             if (_async!=Async.STARTED)
                 return;
             _async=Async.EXPIRING;
             event=_event;
-            aListeners=_asyncListeners;
+            listeners=_asyncListeners;
+
         }
 
-        if (aListeners!=null)
+        if (LOG.isDebugEnabled())
+            LOG.debug("Async timeout {}",this);
+
+        if (listeners!=null)
         {
-            for (AsyncListener listener : aListeners)
+            Runnable callback=new Runnable()
             {
-                try
+                @Override
+                public void run()
                 {
-                    listener.onTimeout(event);
+                    for (AsyncListener listener : listeners)
+                    {
+                        try
+                        {
+                            listener.onTimeout(event);
+                        }
+                        catch(Throwable e)
+                        {
+                            LOG.debug(e);
+                            event.addThrowable(e);
+                            _channel.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable());
+                            break;
+                        }
+                    }
                 }
-                catch(Exception e)
+                @Override
+                public String toString()
                 {
-                    LOG.debug(e);
-                    event.setThrowable(e);
-                    _channel.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
-                    break;
+                    return "onTimeout";
                 }
-            }
+            };
+            
+            runInContext(event,callback);
         }
-        
+
         boolean dispatch=false;
-        synchronized (this)
+        try(Locker.Lock lock= _locker.lock())
         {
-            if (_async==Async.EXPIRING)
+            switch(_async)
             {
-                _async=Async.EXPIRED;
-                if (_state==State.ASYNC_WAIT)
-                {
-                    _state=State.ASYNC_WOKEN;
-                    dispatch=true;
-                }
+                case EXPIRING:
+                    if (event.getThrowable()==null)
+                    {
+                        _async=Async.EXPIRED;
+                        _event.addThrowable(new TimeoutException("Async API violation"));
+                    }
+                    else
+                    {
+                        _async=Async.ERRORING;
+                    }
+                    break;
+                    
+                case COMPLETE:
+                case DISPATCH:
+                    break;
+                    
+                default:
+                    throw new IllegalStateException();
+            }
+
+            if (_state==State.ASYNC_WAIT)
+            {
+                _state=State.ASYNC_WOKEN;
+                dispatch=true;
             }
         }
 
         if (dispatch)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Dispatch after async timeout {}",this);
             scheduleDispatch();
+        }
     }
 
     public void complete()
     {
         // just like resume, except don't set _dispatched=true;
         boolean handle=false;
-        synchronized (this)
+        AsyncContextEvent event=null;
+        try(Locker.Lock lock= _locker.lock())
         {
-            if (_async!=Async.STARTED && _async!=Async.EXPIRING)
-                throw new IllegalStateException(this.getStatusString());
+            boolean started=false;
+            event=_event;
+            
+            switch(_async)
+            {
+                case STARTED:
+                    started=true;
+                    break;
+                case EXPIRING:
+                case ERRORED:
+                    break;
+                default:
+                    throw new IllegalStateException(this.getStatusStringLocked());
+            }
             _async=Async.COMPLETE;
-            if (_state==State.ASYNC_WAIT)
+            
+            if (started && _state==State.ASYNC_WAIT)
             {
                 handle=true;
                 _state=State.ASYNC_WOKEN;
             }
         }
 
-        cancelTimeout();
+        cancelTimeout(event);
         if (handle)
-        {
-            ContextHandler handler=getContextHandler();
-            if (handler!=null)
-                handler.handle(_channel);
-            else
-                _channel.handle();
-        }
+            runInContext(event,_channel);
     }
 
     public void errorComplete()
     {
-        synchronized (this)
+        try(Locker.Lock lock= _locker.lock())
         {
             _async=Async.COMPLETE;
             _event.setDispatchContext(null);
@@ -468,22 +670,58 @@
         cancelTimeout();
     }
 
-    protected void completed()
+    protected void onError()
     {
         final List<AsyncListener> aListeners;
         final AsyncContextEvent event;
-        synchronized (this)
+
+        try(Locker.Lock lock= _locker.lock())
+        {
+            if (_state!=State.DISPATCHED/* || _async!=Async.ERRORING*/)
+                throw new IllegalStateException(this.getStatusStringLocked());
+
+            aListeners=_asyncListeners;
+            event=_event;
+            _async=Async.ERRORED;
+        }
+
+        if (event!=null && aListeners!=null)
+        {
+            event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable());
+            event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,event.getThrowable().getMessage());
+            for (AsyncListener listener : aListeners)
+            {
+                try
+                {
+                    listener.onError(event);
+                }
+                catch(Throwable x)
+                {
+                    LOG.info("Exception while invoking listener " + listener, x);
+                }
+            }
+        }
+    }
+
+
+    protected void onComplete()
+    {
+        final List<AsyncListener> aListeners;
+        final AsyncContextEvent event;
+
+        try(Locker.Lock lock= _locker.lock())
         {
             switch(_state)
             {
                 case COMPLETING:
-                    _state=State.COMPLETED;
                     aListeners=_asyncListeners;
                     event=_event;
+                    _state=State.COMPLETED;
+                    _async=Async.NOT_ASYNC;
                     break;
 
                 default:
-                    throw new IllegalStateException(this.getStatusString());
+                    throw new IllegalStateException(this.getStatusStringLocked());
             }
         }
 
@@ -491,41 +729,46 @@
         {
             if (aListeners!=null)
             {
-                if (event.getThrowable()!=null)
+                Runnable callback = new Runnable()
                 {
-                    event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable());
-                    event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,event.getThrowable().getMessage());
-                }
-
-                for (AsyncListener listener : aListeners)
-                {
-                    try
+                    @Override
+                    public void run()
                     {
-                        if (event.getThrowable()!=null)
-                            listener.onError(event);
-                        else
-                            listener.onComplete(event);
-                    }
-                    catch(Exception e)
+                        for (AsyncListener listener : aListeners)
+                        {
+                            try
+                            {
+                                listener.onComplete(event);
+                            }
+                            catch(Throwable e)
+                            {
+                                LOG.warn(e);
+                            }
+                        }
+                    }    
+                    @Override
+                    public String toString()
                     {
-                        LOG.warn(e);
+                        return "onComplete";
                     }
-                }
+                };
+                
+                runInContext(event,callback);                
             }
-
             event.completed();
         }
     }
 
     protected void recycle()
     {
-        synchronized (this)
+        cancelTimeout();
+        try(Locker.Lock lock= _locker.lock())
         {
             switch(_state)
             {
                 case DISPATCHED:
                 case ASYNC_IO:
-                    throw new IllegalStateException(getStatusString());
+                    throw new IllegalStateException(getStatusStringLocked());
                 case UPGRADED:
                     return;
                 default:
@@ -533,19 +776,20 @@
             }
             _asyncListeners=null;
             _state=State.IDLE;
-            _async=null;
+            _async=Async.NOT_ASYNC;
             _initial=true;
-            _asyncRead=false;
-            _asyncWrite=false;
+            _asyncReadPossible=false;
+            _asyncRead=Interest.NONE;
+            _asyncWritePossible=false;
             _timeoutMs=DEFAULT_TIMEOUT;
-            cancelTimeout();
             _event=null;
         }
     }
-    
+
     public void upgrade()
     {
-        synchronized (this)
+        cancelTimeout();
+        try(Locker.Lock lock= _locker.lock())
         {
             switch(_state)
             {
@@ -553,47 +797,52 @@
                 case COMPLETED:
                     break;
                 default:
-                    throw new IllegalStateException(getStatusString());
+                    throw new IllegalStateException(getStatusStringLocked());
             }
             _asyncListeners=null;
             _state=State.UPGRADED;
-            _async=null;
+            _async=Async.NOT_ASYNC;
             _initial=true;
-            _asyncRead=false;
-            _asyncWrite=false;
+            _asyncReadPossible=false;
+            _asyncRead=Interest.NONE;
+            _asyncWritePossible=false;
             _timeoutMs=DEFAULT_TIMEOUT;
-            cancelTimeout();
             _event=null;
         }
     }
 
-
     protected void scheduleDispatch()
     {
         _channel.execute(_channel);
     }
 
-    protected void scheduleTimeout()
-    {
-        Scheduler scheduler = _channel.getScheduler();
-        if (scheduler!=null && _timeoutMs>0)
-            _event.setTimeoutTask(scheduler.schedule(_event,_timeoutMs,TimeUnit.MILLISECONDS));
-    }
-
     protected void cancelTimeout()
     {
         final AsyncContextEvent event;
-        synchronized (this)
-        { 
+        try(Locker.Lock lock= _locker.lock())
+        {
             event=_event;
         }
+        cancelTimeout(event);
+    }
+
+    protected void cancelTimeout(AsyncContextEvent event)
+    {
         if (event!=null)
             event.cancelTimeoutTask();
     }
+    
+    public boolean isIdle()
+    {
+        try(Locker.Lock lock= _locker.lock())
+        {
+            return _state==State.IDLE;
+        }
+    }
 
     public boolean isExpired()
     {
-        synchronized (this)
+        try(Locker.Lock lock= _locker.lock())
         {
             return _async==Async.EXPIRED;
         }
@@ -601,7 +850,7 @@
 
     public boolean isInitial()
     {
-        synchronized(this)
+        try(Locker.Lock lock= _locker.lock())
         {
             return _initial;
         }
@@ -609,7 +858,7 @@
 
     public boolean isSuspended()
     {
-        synchronized(this)
+        try(Locker.Lock lock= _locker.lock())
         {
             return _state==State.ASYNC_WAIT || _state==State.DISPATCHED && _async==Async.STARTED;
         }
@@ -617,7 +866,7 @@
 
     boolean isCompleting()
     {
-        synchronized (this)
+        try(Locker.Lock lock= _locker.lock())
         {
             return _state==State.COMPLETING;
         }
@@ -625,7 +874,7 @@
 
     boolean isCompleted()
     {
-        synchronized (this)
+        try(Locker.Lock lock= _locker.lock())
         {
             return _state == State.COMPLETED;
         }
@@ -633,19 +882,19 @@
 
     public boolean isAsyncStarted()
     {
-        synchronized (this)
-        {    
+        try(Locker.Lock lock= _locker.lock())
+        {
             if (_state==State.DISPATCHED)
-                return _async!=null;
+                return _async!=Async.NOT_ASYNC;
             return _async==Async.STARTED || _async==Async.EXPIRING;
         }
     }
 
     public boolean isAsync()
     {
-        synchronized (this)
+        try(Locker.Lock lock= _locker.lock())
         {
-            return !_initial || _async!=null;
+            return !_initial || _async!=Async.NOT_ASYNC;
         }
     }
 
@@ -654,7 +903,7 @@
         return _channel.getRequest();
     }
 
-    public HttpChannel<?> getHttpChannel()
+    public HttpChannel getHttpChannel()
     {
         return _channel;
     }
@@ -662,11 +911,15 @@
     public ContextHandler getContextHandler()
     {
         final AsyncContextEvent event;
-        synchronized (this)
-        { 
+        try(Locker.Lock lock= _locker.lock())
+        {
             event=_event;
         }
-       
+        return getContextHandler(event);
+    }
+
+    ContextHandler getContextHandler(AsyncContextEvent event)
+    {
         if (event!=null)
         {
             Context context=((Context)event.getServletContext());
@@ -679,15 +932,29 @@
     public ServletResponse getServletResponse()
     {
         final AsyncContextEvent event;
-        synchronized (this)
-        { 
+        try(Locker.Lock lock= _locker.lock())
+        {
             event=_event;
         }
+        return getServletResponse(event);
+    }
+    
+    public ServletResponse getServletResponse(AsyncContextEvent event)
+    {
         if (event!=null && event.getSuppliedResponse()!=null)
             return event.getSuppliedResponse();
         return _channel.getResponse();
     }
-
+    
+    void runInContext(AsyncContextEvent event,Runnable runnable)
+    {
+        ContextHandler contextHandler = getContextHandler(event);
+        if (contextHandler==null)
+            runnable.run();
+        else
+            contextHandler.handle(_channel.getRequest(),runnable);
+    }
+    
     public Object getAttribute(String name)
     {
         return _channel.getRequest().getAttribute(name);
@@ -703,13 +970,97 @@
         _channel.getRequest().setAttribute(name,attribute);
     }
 
-    public void onReadPossible()
+
+    /* ------------------------------------------------------------ */
+    /** Called to signal async read isReady() has returned false.
+     * This indicates that there is no content available to be consumed
+     * and that once the channel enteres the ASYNC_WAIT state it will
+     * register for read interest by calling {@link HttpChannel#asyncReadFillInterested()}
+     * either from this method or from a subsequent call to {@link #unhandle()}.
+     */
+    public void onReadUnready()
+    {
+        boolean interested=false;
+        try(Locker.Lock lock= _locker.lock())
+        {
+            // We were already unready, this is not a state change, so do nothing
+            if (_asyncRead!=Interest.REGISTERED)
+            {
+                _asyncReadPossible=false; // Assumes this has been checked in isReady() with lock held
+                if (_state==State.ASYNC_WAIT)
+                {
+                    interested=true;
+                    _asyncRead=Interest.REGISTERED;
+                }
+                else
+                    _asyncRead=Interest.NEEDED;
+            }
+        }
+
+        if (interested)
+            _channel.asyncReadFillInterested();
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Called to signal that content is now available to read.
+     * If the channel is in ASYNC_WAIT state and unready (ie isReady() has
+     * returned false), then the state is changed to ASYNC_WOKEN and true
+     * is returned.
+     * @return True IFF the channel was unready and in ASYNC_WAIT state
+     */
+    public boolean onReadPossible()
+    {
+        boolean woken=false;
+        try(Locker.Lock lock= _locker.lock())
+        {
+            _asyncReadPossible=true;
+            if (_state==State.ASYNC_WAIT && _asyncRead.isInterested())
+            {
+                woken=true;
+                _state=State.ASYNC_WOKEN;
+            }
+        }
+        return woken;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Called to signal that the channel is ready for a callback.
+     * This is similar to calling {@link #onReadUnready()} followed by
+     * {@link #onReadPossible()}, except that as content is already
+     * available, read interest is never set.
+     * @return true if woken
+     */
+    public boolean onReadReady()
+    {
+        boolean woken=false;
+        try(Locker.Lock lock= _locker.lock())
+        {
+            _asyncRead=Interest.REGISTERED;
+            _asyncReadPossible=true;
+            if (_state==State.ASYNC_WAIT)
+            {
+                woken=true;
+                _state=State.ASYNC_WOKEN;
+            }
+        }
+        return woken;
+    }
+
+    public boolean isReadPossible()
+    {
+        try(Locker.Lock lock= _locker.lock())
+        {
+            return _asyncReadPossible;
+        }
+    }
+
+    public boolean onWritePossible()
     {
         boolean handle=false;
 
-        synchronized (this)
+        try(Locker.Lock lock= _locker.lock())
         {
-            _asyncRead=true;
+            _asyncWritePossible=true;
             if (_state==State.ASYNC_WAIT)
             {
                 _state=State.ASYNC_WOKEN;
@@ -717,26 +1068,7 @@
             }
         }
 
-        if (handle)
-            _channel.execute(_channel);
+        return handle;
     }
-    
-    public void onWritePossible()
-    {
-        boolean handle=false;
 
-        synchronized (this)
-        {
-            _asyncWrite=true;
-            if (_state==State.ASYNC_WAIT)
-            {
-                _state=State.ASYNC_WOKEN;
-                handle=true;
-            }
-        }
-
-        if (handle)
-            _channel.execute(_channel);
-    }
-    
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
index 9e7c7ec..24571fb 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
@@ -18,11 +18,16 @@
 
 package org.eclipse.jetty.server;
 
+import java.io.IOException;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
+import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpScheme;
 import org.eclipse.jetty.util.Jetty;
+import org.eclipse.jetty.util.TreeTrie;
+import org.eclipse.jetty.util.Trie;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 
@@ -32,7 +37,7 @@
  * <p>This class is a holder of HTTP configuration for use by the 
  * {@link HttpChannel} class.  Typically a HTTPConfiguration instance
  * is instantiated and passed to a {@link ConnectionFactory} that can 
- * create HTTP channels (eg HTTP, AJP or SPDY).</p>
+ * create HTTP channels (e.g. HTTP, AJP or FCGI).</p>
  * <p>The configuration held by this class is not for the wire protocol,
  * but for the interpretation and handling of HTTP requests that could
  * be transported by a variety of protocols.
@@ -43,18 +48,24 @@
 {
     public static final String SERVER_VERSION = "Jetty(" + Jetty.VERSION + ")";
 
-    private List<Customizer> _customizers=new CopyOnWriteArrayList<>();
+    private final List<Customizer> _customizers=new CopyOnWriteArrayList<>();
+    private final Trie<Boolean> _formEncodedMethods = new TreeTrie<>();
     private int _outputBufferSize=32*1024;
     private int _outputAggregationSize=_outputBufferSize/4;
     private int _requestHeaderSize=8*1024;
     private int _responseHeaderSize=8*1024;
     private int _headerCacheSize=512;
     private int _securePort;
+    private long _idleTimeout=-1;
+    private long _blockingTimeout=-1;
     private String _secureScheme = HttpScheme.HTTPS.asString();
     private boolean _sendServerVersion = true;
     private boolean _sendXPoweredBy = false;
     private boolean _sendDateHeader = true;
-    private boolean _delayDispatchUntilContent = false;
+    private boolean _delayDispatchUntilContent = true;
+    private boolean _persistentConnectionsEnabled = true;
+    private int _maxErrorDispatches = 10;
+    private long _minRequestDataRate;
 
     /* ------------------------------------------------------------ */
     /** 
@@ -84,6 +95,8 @@
     
     public HttpConfiguration()
     {
+        _formEncodedMethods.put(HttpMethod.POST.asString(),Boolean.TRUE);
+        _formEncodedMethods.put(HttpMethod.PUT.asString(),Boolean.TRUE);
     }
     
     /* ------------------------------------------------------------ */
@@ -93,15 +106,24 @@
     public HttpConfiguration(HttpConfiguration config)
     {
         _customizers.addAll(config._customizers);
+        for (String s:config._formEncodedMethods.keySet())
+            _formEncodedMethods.put(s,Boolean.TRUE);
         _outputBufferSize=config._outputBufferSize;
         _outputAggregationSize=config._outputAggregationSize;
         _requestHeaderSize=config._requestHeaderSize;
         _responseHeaderSize=config._responseHeaderSize;
-        _securePort=config._securePort;
+        _headerCacheSize=config._headerCacheSize;
         _secureScheme=config._secureScheme;
+        _securePort=config._securePort;
+        _idleTimeout=config._idleTimeout;
+        _blockingTimeout=config._blockingTimeout;
         _sendDateHeader=config._sendDateHeader;
         _sendServerVersion=config._sendServerVersion;
-        _headerCacheSize=config._headerCacheSize;
+        _sendXPoweredBy=config._sendXPoweredBy;
+        _delayDispatchUntilContent=config._delayDispatchUntilContent;
+        _persistentConnectionsEnabled=config._persistentConnectionsEnabled;
+        _maxErrorDispatches=config._maxErrorDispatches;
+        _minRequestDataRate=config._minRequestDataRate;
     }
     
     /* ------------------------------------------------------------ */
@@ -182,26 +204,104 @@
     }
 
     /* ------------------------------------------------------------ */
+    @ManagedAttribute("Whether persistent connections are enabled")
+    public boolean isPersistentConnectionsEnabled()
+    {
+        return _persistentConnectionsEnabled;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the max idle time in ms.
+     * <p>The max idle time is applied to a HTTP request for IO operations and
+     * delayed dispatch. 
+     * @return the max idle time in ms or if == 0 implies an infinite timeout, &lt;0 
+     * implies no HTTP channel timeout and the connection timeout is used instead.
+     */
+    @ManagedAttribute("The idle timeout in ms for I/O operations during the handling of a HTTP request")
+    public long getIdleTimeout()
+    {
+        return _idleTimeout;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the max idle time in ms.
+     * <p>The max idle time is applied to a HTTP request for IO operations and
+     * delayed dispatch. 
+     * @param timeoutMs the max idle time in ms or if == 0 implies an infinite timeout, &lt;0 
+     * implies no HTTP channel timeout and the connection timeout is used instead.
+     */
+    public void setIdleTimeout(long timeoutMs)
+    {
+        _idleTimeout=timeoutMs;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the timeout applied to blocking operations.
+     * <p>This timeout is in addition to the {@link Connector#getIdleTimeout()}, and applies
+     * to the total operation (as opposed to the idle timeout that applies to the time no 
+     * data is being sent).
+     * @return -1, for no blocking timeout (default), 0 for a blocking timeout equal to the 
+     * idle timeout; &gt;0 for a timeout in ms applied to the total blocking operation.
+     */
+    @ManagedAttribute("Total timeout in ms for blocking I/O operations.")
+    public long getBlockingTimeout()
+    {
+        return _blockingTimeout;
+    }
+
+    /**
+     * Set the timeout applied to blocking operations.
+     * <p>This timeout is in addition to the {@link Connector#getIdleTimeout()}, and applies
+     * to the total operation (as opposed to the idle timeout that applies to the time no 
+     * data is being sent).
+     * @param blockingTimeout -1, for no blocking timeout (default), 0 for a blocking timeout equal to the 
+     * idle timeout; &gt;0 for a timeout in ms applied to the total blocking operation.
+     */
+    public void setBlockingTimeout(long blockingTimeout)
+    {
+        _blockingTimeout = blockingTimeout;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setPersistentConnectionsEnabled(boolean persistentConnectionsEnabled)
+    {
+        _persistentConnectionsEnabled = persistentConnectionsEnabled;
+    }
+
+    /* ------------------------------------------------------------ */
     public void setSendServerVersion (boolean sendServerVersion)
     {
         _sendServerVersion = sendServerVersion;
     }
 
     /* ------------------------------------------------------------ */
-    @ManagedAttribute("if true, send the Server header in responses")
+    @ManagedAttribute("Whether to send the Server header in responses")
     public boolean getSendServerVersion()
     {
         return _sendServerVersion;
     }
 
     /* ------------------------------------------------------------ */
+    public void writePoweredBy(Appendable out,String preamble,String postamble) throws IOException
+    {
+        if (getSendServerVersion())
+        {
+            if (preamble!=null)
+                out.append(preamble);
+            out.append(Jetty.POWERED_BY);
+            if (postamble!=null)
+                out.append(postamble);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
     public void setSendXPoweredBy (boolean sendXPoweredBy)
     {
         _sendXPoweredBy=sendXPoweredBy;
     }
 
     /* ------------------------------------------------------------ */
-    @ManagedAttribute("if true, send the X-Powered-By header in responses")
+    @ManagedAttribute("Whether to send the X-Powered-By header in responses")
     public boolean getSendXPoweredBy()
     {
         return _sendXPoweredBy;
@@ -214,7 +314,7 @@
     }
 
     /* ------------------------------------------------------------ */
-    @ManagedAttribute("if true, include the date in HTTP headers")
+    @ManagedAttribute("Whether to send the Date header in responses")
     public boolean getSendDateHeader()
     {
         return _sendDateHeader;
@@ -222,7 +322,7 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @param delay if true, delay the application dispatch until content is available
+     * @param delay if true, delay the application dispatch until content is available (default false)
      */
     public void setDelayDispatchUntilContent(boolean delay)
     {
@@ -230,7 +330,7 @@
     }
 
     /* ------------------------------------------------------------ */
-    @ManagedAttribute("if true, delay the application dispatch until content is available")
+    @ManagedAttribute("Whether to delay the application dispatch until content is available")
     public boolean isDelayDispatchUntilContent()
     {
         return _delayDispatchUntilContent;
@@ -328,6 +428,7 @@
         _secureScheme = secureScheme;
     }
 
+    /* ------------------------------------------------------------ */
     @Override
     public String toString()
     {
@@ -339,4 +440,91 @@
                 _secureScheme,_securePort,
                 _customizers);
     }
+
+    /* ------------------------------------------------------------ */
+    /** Set the form encoded methods.
+     * @param methods HTTP Methods of requests that can be decoded as 
+     * x-www-form-urlencoded content to be made available via the 
+     * {@link Request#getParameter(String)} and associated APIs 
+     */
+    public void setFormEncodedMethods(String... methods)
+    {
+        _formEncodedMethods.clear();
+        for (String method:methods)
+            addFormEncodedMethod(method);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Set of HTTP Methods of requests that can be decoded as 
+     * x-www-form-urlencoded content to be made available via the 
+     * {@link Request#getParameter(String)} and associated APIs
+     */
+    public Set<String> getFormEncodedMethods()
+    {
+        return _formEncodedMethods.keySet();
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Add a form encoded HTTP Method 
+     * @param method HTTP Method of requests that can be decoded as 
+     * x-www-form-urlencoded content to be made available via the 
+     * {@link Request#getParameter(String)} and associated APIs
+     */
+    public void addFormEncodedMethod(String method)
+    {
+        _formEncodedMethods.put(method,Boolean.TRUE);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Test if the method type supports <code>x-www-form-urlencoded</code> content
+     * 
+     * @param method the method type
+     * @return True of the requests of this method type can be
+     * decoded as <code>x-www-form-urlencoded</code> content to be made available via the 
+     * {@link Request#getParameter(String)} and associated APIs
+     */
+    public boolean isFormEncodedMethod(String method)
+    {
+        return Boolean.TRUE.equals(_formEncodedMethods.get(method));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The maximum error dispatches for a request to prevent looping on an error
+     */
+    @ManagedAttribute("The maximum ERROR dispatches for a request for loop prevention (default 10)")
+    public int getMaxErrorDispatches()
+    {
+        return _maxErrorDispatches;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param max The maximum error dispatches for a request to prevent looping on an error
+     */
+    public void setMaxErrorDispatches(int max)
+    {
+        _maxErrorDispatches=max;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The minimum request data rate in bytes per second; or &lt;=0 for no limit
+     */
+    @ManagedAttribute("The minimum request content data rate in bytes per second")
+    public long getMinRequestDataRate()
+    {
+        return _minRequestDataRate;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param bytesPerSecond The minimum request data rate in bytes per second; or &lt;=0 for no limit
+     */
+    public void setMinRequestDataRate(long bytesPerSecond)
+    {
+        _minRequestDataRate=bytesPerSecond;
+    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java
index 7334be6..f6eb012 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java
@@ -19,19 +19,21 @@
 package org.eclipse.jetty.server;
 
 import java.io.IOException;
-import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.channels.WritePendingException;
 import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
 
+import org.eclipse.jetty.http.HttpCompliance;
+import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpGenerator;
-import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpHeaderValue;
-import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpParser;
+import org.eclipse.jetty.http.HttpParser.RequestHandler;
 import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http.PreEncodedHttpField;
 import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.Connection;
@@ -48,30 +50,35 @@
  */
 public class HttpConnection extends AbstractConnection implements Runnable, HttpTransport, Connection.UpgradeFrom
 {
+    private static final Logger LOG = Log.getLogger(HttpConnection.class);
+    public static final HttpField CONNECTION_CLOSE = new PreEncodedHttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE.asString());
     public static final String UPGRADE_CONNECTION_ATTRIBUTE = "org.eclipse.jetty.server.HttpConnection.UPGRADE";
     private static final boolean REQUEST_BUFFER_DIRECT=false;
     private static final boolean HEADER_BUFFER_DIRECT=false;
     private static final boolean CHUNK_BUFFER_DIRECT=false;
-    private static final Logger LOG = Log.getLogger(HttpConnection.class);
     private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<>();
 
     private final HttpConfiguration _config;
     private final Connector _connector;
     private final ByteBufferPool _bufferPool;
+    private final HttpInput _input;
     private final HttpGenerator _generator;
     private final HttpChannelOverHttp _channel;
     private final HttpParser _parser;
+    private final AtomicInteger _contentBufferReferences=new AtomicInteger();
     private volatile ByteBuffer _requestBuffer = null;
     private volatile ByteBuffer _chunk = null;
+    private final BlockingReadCallback _blockingReadCallback = new BlockingReadCallback();
+    private final AsyncReadCallback _asyncReadCallback = new AsyncReadCallback();
     private final SendCallback _sendCallback = new SendCallback();
+    private final boolean _recordHttpComplianceViolations;
 
-
-    /* ------------------------------------------------------------ */
-    /** Get the current connection that this thread is dispatched to.
+    /**
+     * Get the current connection that this thread is dispatched to.
      * Note that a thread may be processing a request asynchronously and
      * thus not be dispatched to the connection.
-     * @see Request#getAttribute(String) for a more general way to access the HttpConnection
      * @return the current HttpConnection or null
+     * @see Request#getAttribute(String) for a more general way to access the HttpConnection
      */
     public static HttpConnection getCurrentConnection()
     {
@@ -85,26 +92,29 @@
         return last;
     }
 
+    public HttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint, HttpCompliance compliance, boolean recordComplianceViolations)
+    {
+        super(endPoint, connector.getExecutor());
+        _config = config;
+        _connector = connector;
+        _bufferPool = _connector.getByteBufferPool();
+        _generator = newHttpGenerator();
+        _channel = newHttpChannel();
+        _input = _channel.getRequest().getHttpInput();
+        _parser = newHttpParser(compliance);
+        _recordHttpComplianceViolations=recordComplianceViolations;
+        if (LOG.isDebugEnabled())
+            LOG.debug("New HTTP Connection {}", this);
+    }
+
     public HttpConfiguration getHttpConfiguration()
     {
         return _config;
     }
 
-    public HttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint)
+    public boolean isRecordHttpComplianceViolations()
     {
-        // Tell AbstractConnector executeOnFillable==true because we want the same thread that
-        // does the HTTP parsing to handle the request so its cache is hot
-        super(endPoint, connector.getExecutor(),true);
-
-        _config = config;
-        _connector = connector;
-        _bufferPool = _connector.getByteBufferPool();
-        _generator = newHttpGenerator();
-        HttpInput<ByteBuffer> input = newHttpInput();
-        _channel = newHttpChannel(input);
-        _parser = newHttpParser();
-        if (LOG.isDebugEnabled())
-            LOG.debug("New HTTP Connection {}", this);
+        return _recordHttpComplianceViolations;
     }
 
     protected HttpGenerator newHttpGenerator()
@@ -112,22 +122,17 @@
         return new HttpGenerator(_config.getSendServerVersion(),_config.getSendXPoweredBy());
     }
 
-    protected HttpInput<ByteBuffer> newHttpInput()
+    protected HttpChannelOverHttp newHttpChannel()
     {
-        return new HttpInputOverHTTP(this);
+        return new HttpChannelOverHttp(this, _connector, _config, getEndPoint(), this);
     }
 
-    protected HttpChannelOverHttp newHttpChannel(HttpInput<ByteBuffer> httpInput)
+    protected HttpParser newHttpParser(HttpCompliance compliance)
     {
-        return new HttpChannelOverHttp(_connector, _config, getEndPoint(), this, httpInput);
+        return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize(), compliance);
     }
 
-    protected HttpParser newHttpParser()
-    {
-        return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize());
-    }
-
-    protected HttpParser.RequestHandler<ByteBuffer> newRequestHandler()
+    protected HttpParser.RequestHandler newRequestHandler()
     {
         return _channel;
     }
@@ -142,7 +147,7 @@
         return _connector;
     }
 
-    public HttpChannel<?> getHttpChannel()
+    public HttpChannel getHttpChannel()
     {
         return _channel;
     }
@@ -152,6 +157,17 @@
         return _parser;
     }
 
+    public HttpGenerator getGenerator()
+    {
+        return _generator;
+    }
+
+    @Override
+    public boolean isOptimizedForDirectBuffers()
+    {
+        return getEndPoint().isOptimizedForDirectBuffers();
+    }
+
     @Override
     public int getMessagesIn()
     {
@@ -180,6 +196,8 @@
     {
         if (_requestBuffer != null && !_requestBuffer.hasRemaining())
         {
+            if (LOG.isDebugEnabled())
+                LOG.debug("releaseRequestBuffer {}",this);
             ByteBuffer buffer=_requestBuffer;
             _requestBuffer=null;
             _bufferPool.release(buffer);
@@ -193,148 +211,157 @@
         return _requestBuffer;
     }
 
-    /**
-     * <p>Parses and handles HTTP messages.</p>
-     * <p>This method is called when this {@link Connection} is ready to read bytes from the {@link EndPoint}.
-     * However, it can also be called if there is unconsumed data in the _requestBuffer, as a result of
-     * resuming a suspended request when there is a pipelined request already read into the buffer.</p>
-     * <p>This method fills bytes and parses them until either: EOF is filled; 0 bytes are filled;
-     * the HttpChannel finishes handling; or the connection has changed.</p>
-     */
+    public boolean isRequestBufferEmpty()
+    {
+        return BufferUtil.isEmpty(_requestBuffer);
+    }
+
     @Override
     public void onFillable()
     {
         if (LOG.isDebugEnabled())
-            LOG.debug("{} onFillable {}", this, _channel.getState());
+            LOG.debug("{} onFillable enter {} {}", this, _channel.getState(),BufferUtil.toDetailString(_requestBuffer));
 
-        final HttpConnection last=setCurrentConnection(this);
-        int filled=Integer.MAX_VALUE;
-        boolean suspended=false;
+        HttpConnection last=setCurrentConnection(this);
         try
         {
-            // while not suspended and not upgraded
-            while (!suspended && getEndPoint().getConnection()==this)
+            while (getEndPoint().isOpen())
             {
-                // Do we need some data to parse
-                if (BufferUtil.isEmpty(_requestBuffer))
+                // Fill the request buffer (if needed).
+                int filled = fillRequestBuffer();
+
+                // Parse the request buffer.
+                boolean handle = parseRequestBuffer();
+
+                // If there was a connection upgrade, the other
+                // connection took over, nothing more to do here.
+                if (getEndPoint().getConnection()!=this)
+                    break;
+
+                // Handle closed parser.
+                if (_parser.isClose() || _parser.isClosed())
                 {
-                    // If the previous iteration filled 0 bytes or saw a close, then break here
-                    if (filled<=0)
-                        break;
-
-                    // Can we fill?
-                    if(getEndPoint().isInputShutdown())
-                    {
-                        // No pretend we read -1
-                        filled=-1;
-                        _parser.atEOF();
-                    }
-                    else
-                    {
-                        // Get a buffer
-                        // We are not in a race here for the request buffer as we have not yet received a request,
-                        // so there are not an possible legal threads calling #parseContent or #completed.
-                        _requestBuffer = getRequestBuffer();
-
-                        // fill
-                        filled = getEndPoint().fill(_requestBuffer);
-                        if (filled==0) // Do a retry on fill 0 (optimization for SSL connections)
-                            filled = getEndPoint().fill(_requestBuffer);
-
-                        // tell parser
-                        if (filled < 0)
-                            _parser.atEOF();
-                    }
+                    close();
+                    break;
                 }
 
-                // Parse the buffer
-                if (_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer))
+                // Handle channel event
+                if (handle)
                 {
-                    // The parser returned true, which indicates the channel is ready to handle a request.
-                    // Call the channel and this will either handle the request/response to completion OR,
-                    // if the request suspends, the request/response will be incomplete so the outer loop will exit.
-                    // Not that onFillable no longer manipulates the request buffer from this point and that is
-                    // left to threads calling #completed or #parseContent (which may be this thread inside handle())
-                    suspended = !_channel.handle();
+                    boolean suspended = !_channel.handle();
+
+                    // We should break iteration if we have suspended or changed connection or this is not the handling thread.
+                    if (suspended || getEndPoint().getConnection() != this)
+                        break;
                 }
                 else
                 {
-                    // We parsed what we could, recycle the request buffer
-                    // We are not in a race here for the request buffer as we have not yet received a request,
-                    // so there are not an possible legal threads calling #parseContent or #completed.
-                    releaseRequestBuffer();
+                    if (filled <= 0)
+                    {
+                        if (filled == 0)
+                            fillInterested();
+                        break;
+                    }
                 }
             }
         }
-        catch (EofException e)
-        {
-            LOG.debug(e);
-        }
-        catch (Exception e)
-        {
-            if (_parser.isIdle())
-                LOG.debug(e);
-            else
-                LOG.warn(this.toString(), e);
-            close();
-        }
         finally
         {
             setCurrentConnection(last);
-            if (!suspended && getEndPoint().isOpen() && getEndPoint().getConnection()==this)
-            {
-                fillInterested();
-            }
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} onFillable exit {} {}", this, _channel.getState(),BufferUtil.toDetailString(_requestBuffer));
         }
     }
 
     /* ------------------------------------------------------------ */
     /** Fill and parse data looking for content
-     * @throws IOException
+     * @return true if an {@link RequestHandler} method was called and it returned true;
      */
-    protected void parseContent() throws IOException
+    protected boolean fillAndParseForContent()
     {
-        // Not in a race here for the request buffer with #onFillable because an async consumer of
-        // content would only be started after onFillable has given up control.
-        // In a little bit of a race with #completed, but then not sure if it is legal to be doing
-        // async calls to IO and have a completed call at the same time.
-        ByteBuffer requestBuffer = getRequestBuffer();
-
+        boolean handled=false;
         while (_parser.inContentState())
         {
-            // Can the parser progress (even with an empty buffer)
-            boolean parsed = _parser.parseNext(requestBuffer==null?BufferUtil.EMPTY_BUFFER:requestBuffer);
-
-            // No, we can we try reading some content?
-            if (BufferUtil.isEmpty(requestBuffer) && getEndPoint().isInputShutdown())
-            {
-                _parser.atEOF();
-                if (parsed)
-                    break;
-                continue;
-            }
-
-            if (parsed)
+            int filled = fillRequestBuffer();
+            handled = parseRequestBuffer();
+            if (handled || filled<=0 || _input.hasContent())
                 break;
-
-            // OK lets read some data
-            int filled=getEndPoint().fill(requestBuffer);
-            if (LOG.isDebugEnabled()) // Avoid boxing of variable 'filled'
-                LOG.debug("{} filled {}",this,filled);
-            if (filled<=0)
-            {
-                if (filled<0)
-                {
-                    _parser.atEOF();
-                    continue;
-                }
-                break;
-            }
         }
+        return handled;
     }
 
+    /* ------------------------------------------------------------ */
+    private int fillRequestBuffer()
+    {
+        if (_contentBufferReferences.get()>0)
+        {
+            LOG.warn("{} fill with unconsumed content!",this);
+            return 0;
+        }
+
+        if (BufferUtil.isEmpty(_requestBuffer))
+        {
+            // Can we fill?
+            if(getEndPoint().isInputShutdown())
+            {
+                // No pretend we read -1
+                _parser.atEOF();
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} filled -1 {}",this,BufferUtil.toDetailString(_requestBuffer));
+                return -1;
+            }
+
+            // Get a buffer
+            // We are not in a race here for the request buffer as we have not yet received a request,
+            // so there are not an possible legal threads calling #parseContent or #completed.
+            _requestBuffer = getRequestBuffer();
+
+            // fill
+            try
+            {
+                int filled = getEndPoint().fill(_requestBuffer);
+                if (filled==0) // Do a retry on fill 0 (optimization for SSL connections)
+                    filled = getEndPoint().fill(_requestBuffer);
+
+                // tell parser
+                if (filled < 0)
+                    _parser.atEOF();
+
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} filled {} {}",this,filled,BufferUtil.toDetailString(_requestBuffer));
+
+                return filled;
+            }
+            catch (IOException e)
+            {
+                LOG.debug(e);
+                return -1;
+            }
+        }
+        return 0;
+    }
+
+    /* ------------------------------------------------------------ */
+    private boolean parseRequestBuffer()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} parse {} {}",this,BufferUtil.toDetailString(_requestBuffer));
+
+        boolean handle = _parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer);
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} parsed {} {}",this,handle,_parser);
+
+        // recycle buffer ?
+        if (_contentBufferReferences.get()==0)
+            releaseRequestBuffer();
+
+        return handle;
+    }
+
+    /* ------------------------------------------------------------ */
     @Override
-    public void completed()
+    public void onCompleted()
     {
         // Handle connection upgrades
         if (_channel.getResponse().getStatus() == HttpStatus.SWITCHING_PROTOCOLS_101)
@@ -342,12 +369,21 @@
             Connection connection = (Connection)_channel.getRequest().getAttribute(UPGRADE_CONNECTION_ATTRIBUTE);
             if (connection != null)
             {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Upgrade from {} to {}", this, connection);
                 _channel.getState().upgrade();
                 getEndPoint().upgrade(connection);
-                _channel.reset();
+                _channel.recycle();
                 _parser.reset();
                 _generator.reset();
-                releaseRequestBuffer();
+                if (_contentBufferReferences.get()==0)
+                    releaseRequestBuffer();
+                else
+                {
+                    LOG.warn("{} lingering content references?!?!",this);
+                    _requestBuffer=null; // Not returned to pool!
+                    _contentBufferReferences.set(0);
+                }
                 return;
             }
         }
@@ -362,24 +398,24 @@
         else if (_parser.inContentState() && _generator.isPersistent())
         {
             // If we are async, then we have problems to complete neatly
-            if (_channel.getRequest().getHttpInput().isAsync())
+            if (_input.isAsync())
             {
                 if (LOG.isDebugEnabled())
                     LOG.debug("unconsumed async input {}", this);
-                _channel.abort();
+                _channel.abort(new IOException("unconsumed input"));
             }
             else
             {
                 if (LOG.isDebugEnabled())
                     LOG.debug("unconsumed input {}", this);
                 // Complete reading the request
-                if (!_channel.getRequest().getHttpInput().consumeAll())
-                    _channel.abort();
+                if (!_input.consumeAll())
+                    _channel.abort(new IOException("unconsumed input"));
             }
         }
 
         // Reset the channel, parsers and generator
-        _channel.reset();
+        _channel.recycle();
         if (_generator.isPersistent() && !_parser.isClosed())
             _parser.reset();
         else
@@ -387,7 +423,6 @@
 
         // Not in a race here with onFillable, because it has given up control before calling handle.
         // in a slight race with #completed, but not sure what to do with that anyway.
-        releaseRequestBuffer();
         if (_chunk!=null)
             _bufferPool.release(_chunk);
         _chunk=null;
@@ -461,28 +496,36 @@
     }
 
     @Override
-    public void send(ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
+    public void send(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback)
     {
-        // If we are still expecting a 100 continues when we commit
-        if (info!=null && _channel.isExpecting100Continue())
-            // then we can't be persistent
-            _generator.setPersistent(false);
+        if (info == null)
+        {
+            if (!lastContent && BufferUtil.isEmpty(content))
+            {
+                callback.succeeded();
+                return;
+            }
+        }
+        else
+        {
+            // If we are still expecting a 100 continues when we commit
+            if (_channel.isExpecting100Continue())
+                // then we can't be persistent
+                _generator.setPersistent(false);
+        }
 
-        if(_sendCallback.reset(info,content,lastContent,callback))
+        if(_sendCallback.reset(info,head,content,lastContent,callback))
             _sendCallback.iterate();
     }
 
-    @Override
-    public void send(ByteBuffer content, boolean lastContent, Callback callback)
+
+    HttpInput.Content newContent(ByteBuffer c)
     {
-        if (!lastContent && BufferUtil.isEmpty(content))
-            callback.succeeded();
-        else if (_sendCallback.reset(null,content,lastContent,callback))
-            _sendCallback.iterate();
+        return new Content(c);
     }
 
     @Override
-    public void abort()
+    public void abort(Throwable failure)
     {
         // Do a direct close of the output, as this may indicate to a client that the
         // response is bad either with RST or by abnormal completion of chunked response.
@@ -490,6 +533,33 @@
     }
 
     @Override
+    public boolean isPushSupported()
+    {
+        return false;
+    }
+
+    @Override
+    public void push(org.eclipse.jetty.http.MetaData.Request request)
+    {
+        LOG.debug("ignore push in {}",this);
+    }
+
+    public void asyncReadFillInterested()
+    {
+        getEndPoint().fillInterested(_asyncReadCallback);
+    }
+
+    public void blockingReadFillInterested()
+    {
+        getEndPoint().fillInterested(_blockingReadCallback);
+    }
+
+    public void blockingReadException(Throwable e)
+    {
+        _blockingReadCallback.failed(e);
+    }
+
+    @Override
     public String toString()
     {
         return String.format("%s[p=%s,g=%s,c=%s]",
@@ -499,146 +569,74 @@
                 _channel);
     }
 
-    protected class HttpChannelOverHttp extends HttpChannel<ByteBuffer>
+    private class Content extends HttpInput.Content
     {
-        private InetSocketAddress _localAddr;
-        private InetSocketAddress _remoteAddr;
-
-        public HttpChannelOverHttp(Connector connector, HttpConfiguration config, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
+        public Content(ByteBuffer content)
         {
-            super(connector,config,endPoint,transport,input);
+            super(content);
+            _contentBufferReferences.incrementAndGet();
         }
 
         @Override
-        public void proxied(String protocol, String remoteAddress, String localAddress, int remotePort, int localPort)
+        public void succeeded()
         {
-            _localAddr = InetSocketAddress.createUnresolved(localAddress, localPort);
-            _remoteAddr = InetSocketAddress.createUnresolved(remoteAddress, remotePort);
+            if (_contentBufferReferences.decrementAndGet()==0)
+                releaseRequestBuffer();
         }
 
         @Override
-        public InetSocketAddress getLocalAddress()
+        public void failed(Throwable x)
         {
-            if (_localAddr != null)
-                return _localAddr;
-            return super.getLocalAddress();
+            succeeded();
+        }
+    }
+
+    private class BlockingReadCallback implements Callback
+    {
+        @Override
+        public void succeeded()
+        {
+            _input.unblock();
         }
 
         @Override
-        public InetSocketAddress getRemoteAddress()
+        public void failed(Throwable x)
         {
-            if (_remoteAddr != null)
-                return _remoteAddr;
-            return super.getRemoteAddress();
+            _input.failed(x);
         }
 
         @Override
-        public void earlyEOF()
+        public boolean isNonBlocking()
         {
-            // If we have no request yet, just close
-            if (getRequest().getMethod()==null)
-                close();
-            else
-                super.earlyEOF();
-        }
-
-        @Override
-        public boolean content(ByteBuffer item)
-        {
-            super.content(item);
+            // This callback does not block, rather it wakes up the
+            // thread that is blocked waiting on the read.
             return true;
         }
+    }
 
+    private class AsyncReadCallback implements Callback
+    {
         @Override
-        public void badMessage(int status, String reason)
+        public void succeeded()
         {
-            _generator.setPersistent(false);
-            super.badMessage(status,reason);
+            if (fillAndParseForContent())
+                _channel.handle();
+            else if (!_input.isFinished() && !_input.hasContent())
+                asyncReadFillInterested();
         }
 
         @Override
-        public boolean headerComplete()
+        public void failed(Throwable x)
         {
-            boolean persistent;
-            HttpVersion version = getHttpVersion();
-
-            switch (version)
-            {
-                case HTTP_0_9:
-                {
-                    persistent = false;
-                    break;
-                }
-                case HTTP_1_0:
-                {
-                    persistent = getRequest().getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString());
-                    if (!persistent)
-                        persistent = HttpMethod.CONNECT.is(getRequest().getMethod());
-                    if (persistent)
-                        getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
-                    break;
-                }
-                case HTTP_1_1:
-                {
-                    persistent = !getRequest().getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
-                    if (!persistent)
-                        persistent = HttpMethod.CONNECT.is(getRequest().getMethod());
-                    if (!persistent)
-                        getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE);
-                    break;
-                }
-                case HTTP_2:
-                {
-                    persistent=false;
-                    badMessage(400,null);
-                    return true;
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-
-            if (!persistent)
-                _generator.setPersistent(false);
-
-            if (!super.headerComplete())
-                return false;
-
-            // Should we delay dispatch until we have some content?
-            // We should not delay if there is no content expect or client is expecting 100 or the response is already committed or the request buffer already has something in it to parse
-            if (getHttpConfiguration().isDelayDispatchUntilContent() && _parser.getContentLength() > 0 &&
-                    !isExpecting100Continue() && !isCommitted() && BufferUtil.isEmpty(_requestBuffer))
-                return false;
-
-            return true;
-        }
-
-        @Override
-        protected void handleException(Throwable x)
-        {
-            _generator.setPersistent(false);
-            super.handleException(x);
-        }
-
-        @Override
-        public void abort()
-        {
-            super.abort();
-            _generator.setPersistent(false);
-        }
-
-        @Override
-        public boolean messageComplete()
-        {
-            super.messageComplete();
-            return false;
+            if (_input.failed(x))
+                _channel.handle();
         }
     }
 
     private class SendCallback extends IteratingCallback
     {
-        private ResponseInfo _info;
+        private MetaData.Response _info;
+        private boolean _head;
         private ByteBuffer _content;
         private boolean _lastContent;
         private Callback _callback;
@@ -650,11 +648,18 @@
             super(true);
         }
 
-        private boolean reset(ResponseInfo info, ByteBuffer content, boolean last, Callback callback)
+        @Override
+        public boolean isNonBlocking()
+        {
+            return _callback.isNonBlocking();
+        }
+
+        private boolean reset(MetaData.Response info, boolean head, ByteBuffer content, boolean last, Callback callback)
         {
             if (reset())
             {
                 _info = info;
+                _head = head;
                 _content = content;
                 _lastContent = last;
                 _callback = callback;
@@ -679,7 +684,7 @@
             ByteBuffer chunk = _chunk;
             while (true)
             {
-                HttpGenerator.Result result = _generator.generateResponse(_info, _header, chunk, _content, _lastContent);
+                HttpGenerator.Result result = _generator.generateResponse(_info, _head, _header, chunk, _content, _lastContent);
                 if (LOG.isDebugEnabled())
                     LOG.debug("{} generate: {} ({},{},{})@{}",
                         this,
@@ -691,9 +696,13 @@
 
                 switch (result)
                 {
+                    case NEED_INFO:
+                        throw new EofException("request lifecycle violation");
+                        
                     case NEED_HEADER:
                     {
                         _header = _bufferPool.acquire(_config.getResponseHeaderSize(), HEADER_BUFFER_DIRECT);
+
                         continue;
                     }
                     case NEED_CHUNK:
@@ -704,7 +713,7 @@
                     case FLUSH:
                     {
                         // Don't write the chunk or the content if this is a HEAD response, or any other type of response that should have no content
-                        if (_channel.getRequest().isHead() || _generator.isNoContent())
+                        if (_head || _generator.isNoContent())
                         {
                             BufferUtil.clear(chunk);
                             BufferUtil.clear(_content);
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java
index 61651c2..e341991 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java
@@ -16,36 +16,42 @@
 //  ========================================================================
 //
 
-
 package org.eclipse.jetty.server;
 
-
+import org.eclipse.jetty.http.HttpCompliance;
 import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.util.annotation.Name;
 
-
-/* ------------------------------------------------------------ */
 /** A Connection Factory for HTTP Connections.
- * <p>Accepts connections either directly or via SSL and/or NPN chained connection factories.  The accepted 
+ * <p>Accepts connections either directly or via SSL and/or ALPN chained connection factories.  The accepted
  * {@link HttpConnection}s are configured by a {@link HttpConfiguration} instance that is either created by
  * default or passed in to the constructor.
  */
 public class HttpConnectionFactory extends AbstractConnectionFactory implements HttpConfiguration.ConnectionFactory
 {
     private final HttpConfiguration _config;
+    private HttpCompliance _httpCompliance;
+    private boolean _recordHttpComplianceViolations = false;
 
     public HttpConnectionFactory()
     {
         this(new HttpConfiguration());
-        setInputBufferSize(16384);
     }
-
+    
     public HttpConnectionFactory(@Name("config") HttpConfiguration config)
     {
-        super(HttpVersion.HTTP_1_1.toString());
+        this(config,null);
+    }
+
+    public HttpConnectionFactory(@Name("config") HttpConfiguration config, @Name("compliance") HttpCompliance compliance)
+    {
+        super(HttpVersion.HTTP_1_1.asString());
         _config=config;
+        _httpCompliance=compliance==null?HttpCompliance.RFC7230:compliance;
+        if (config==null)
+            throw new IllegalArgumentException("Null HttpConfiguration");
         addBean(_config);
     }
 
@@ -55,10 +61,34 @@
         return _config;
     }
 
+    public HttpCompliance getHttpCompliance()
+    {
+        return _httpCompliance;
+    }
+
+    public boolean isRecordHttpComplianceViolations()
+    {
+        return _recordHttpComplianceViolations;
+    }
+
+    /**
+     * @param httpCompliance String value of {@link HttpCompliance}
+     */
+    public void setHttpCompliance(HttpCompliance httpCompliance)
+    {
+        _httpCompliance = httpCompliance;
+    }
+
     @Override
     public Connection newConnection(Connector connector, EndPoint endPoint)
     {
-        return configure(new HttpConnection(_config, connector, endPoint), connector, endPoint);
+        HttpConnection conn = new HttpConnection(_config, connector, endPoint, _httpCompliance,isRecordHttpComplianceViolations());
+        return configure(conn, connector, endPoint);
     }
-
+    
+    
+    public void setRecordHttpComplianceViolations(boolean recordHttpComplianceViolations)
+    {
+        this._recordHttpComplianceViolations = recordHttpComplianceViolations;
+    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java
index 7d5f41f..8573878 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java
@@ -19,284 +19,515 @@
 package org.eclipse.jetty.server;
 
 import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.Deque;
 import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import javax.servlet.ReadListener;
 import javax.servlet.ServletInputStream;
 
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.io.EofException;
 import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
 /**
  * {@link HttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.
- * <p/>
+ * <p>
  * Content may arrive in patterns such as [content(), content(), messageComplete()] so that this class
  * maintains two states: the content state that tells whether there is content to consume and the EOF
  * state that tells whether an EOF has arrived.
  * Only once the content has been consumed the content state is moved to the EOF state.
  */
-public abstract class HttpInput<T> extends ServletInputStream implements Runnable
+public class HttpInput extends ServletInputStream implements Runnable
 {
     private final static Logger LOG = Log.getLogger(HttpInput.class);
+    private final static Content EOF_CONTENT = new EofContent("EOF");
+    private final static Content EARLY_EOF_CONTENT = new EofContent("EARLY_EOF");
 
     private final byte[] _oneByteBuffer = new byte[1];
-    private final Object _lock;
-    private HttpChannelState _channelState;
+    private final Deque<Content> _inputQ = new ArrayDeque<>();
+    private final HttpChannelState _channelState;
     private ReadListener _listener;
-    private Throwable _onError;
-    private boolean _notReady;
-    private State _contentState = STREAM;
-    private State _eofState;
-    private long _contentRead;
+    private State _state = STREAM;
+    private long _firstByteTimeStamp = -1;
+    private long _contentArrived;
+    private long _contentConsumed;
+    private long _blockUntil;
 
-    protected HttpInput()
+    public HttpInput(HttpChannelState state)
     {
-        this(null);
+        _channelState = state;
     }
 
-    protected HttpInput(Object lock)
+    protected HttpChannelState getHttpChannelState()
     {
-        _lock = lock == null ? this : lock;
-    }
-
-    public void init(HttpChannelState state)
-    {
-        synchronized (lock())
-        {
-            _channelState = state;
-        }
-    }
-
-    public final Object lock()
-    {
-        return _lock;
+        return _channelState;
     }
 
     public void recycle()
     {
-        synchronized (lock())
+        synchronized (_inputQ)
         {
+            Content item = _inputQ.poll();
+            while (item != null)
+            {
+                item.failed(null);
+                item = _inputQ.poll();
+            }
             _listener = null;
-            _onError = null;
-            _notReady = false;
-            _contentState = STREAM;
-            _eofState = null;
-            _contentRead = 0;
+            _state = STREAM;
+            _contentArrived = 0;
+            _contentConsumed = 0;
+            _firstByteTimeStamp = -1;
+            _blockUntil = 0;
         }
     }
 
     @Override
     public int available()
     {
-        try
+        int available = 0;
+        boolean woken = false;
+        synchronized (_inputQ)
         {
-            synchronized (lock())
+            Content content = _inputQ.peek();
+            if (content == null)
             {
-                T item = getNextContent();
-                return item == null ? 0 : remaining(item);
+                try
+                {
+                    produceContent();
+                }
+                catch (IOException e)
+                {
+                    woken = failed(e);
+                }
+                content = _inputQ.peek();
             }
+
+            if (content != null)
+                available = remaining(content);
         }
-        catch (IOException e)
-        {
-            throw new RuntimeIOException(e);
-        }
+
+        if (woken)
+            wake();
+        return available;
+    }
+
+    private void wake()
+    {
+        HttpChannel channel = _channelState.getHttpChannel();
+        Executor executor = channel.getConnector().getServer().getThreadPool();
+        executor.execute(channel);
+    }
+
+    private long getBlockingTimeout()
+    {
+        return getHttpChannelState().getHttpChannel().getHttpConfiguration().getBlockingTimeout();
     }
 
     @Override
     public int read() throws IOException
     {
         int read = read(_oneByteBuffer, 0, 1);
+        if (read == 0)
+            throw new IllegalStateException("unready read=0");
         return read < 0 ? -1 : _oneByteBuffer[0] & 0xFF;
     }
 
     @Override
     public int read(byte[] b, int off, int len) throws IOException
     {
-        synchronized (lock())
+        synchronized (_inputQ)
         {
-            T item = getNextContent();
-            if (item == null)
+            if (!isAsync())
             {
-                _contentState.waitForContent(this);
-                item = getNextContent();
-                if (item == null)
-                    return _contentState.noContent();
+                if (_blockUntil == 0)
+                {
+                    long blockingTimeout = getBlockingTimeout();
+                    if (blockingTimeout > 0)
+                        _blockUntil = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(blockingTimeout);
+                }
             }
-            int l = get(item, b, off, len);
-            _contentRead += l;
-            return l;
+
+            long minRequestDataRate = _channelState.getHttpChannel().getHttpConfiguration().getMinRequestDataRate();
+            if (minRequestDataRate > 0 && _firstByteTimeStamp != -1)
+            {
+                long period = System.nanoTime() - _firstByteTimeStamp;
+                if (period > 0)
+                {
+                    long minimum_data = minRequestDataRate * TimeUnit.NANOSECONDS.toMillis(period) / TimeUnit.SECONDS.toMillis(1);
+                    if (_contentArrived < minimum_data)
+                        throw new BadMessageException(HttpStatus.REQUEST_TIMEOUT_408, String.format("Request data rate < %d B/s", minRequestDataRate));
+                }
+            }
+
+            while (true)
+            {
+                Content item = nextContent();
+                if (item != null)
+                {
+                    int l = get(item, b, off, len);
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("{} read {} from {}", this, l, item);
+
+                    consumeNonContent();
+
+                    return l;
+                }
+
+                if (!_state.blockForContent(this))
+                    return _state.noContent();
+            }
         }
     }
 
     /**
-     * A convenience method to call nextContent and to check the return value, which if null then the
-     * a check is made for EOF and the state changed accordingly.
+     * Called when derived implementations should attempt to
+     * produce more Content and add it via {@link #addContent(Content)}.
+     * For protocols that are constantly producing (eg HTTP2) this can
+     * be left as a noop;
      *
-     * @return Content or null if none available.
-     * @throws IOException
-     * @see #nextContent()
+     * @throws IOException if unable to produce content
      */
-    protected T getNextContent() throws IOException
+    protected void produceContent() throws IOException
     {
-        T content = nextContent();
-        if (content == null)
+    }
+
+    /**
+     * Get the next content from the inputQ, calling {@link #produceContent()}
+     * if need be.  EOF is processed and state changed.
+     *
+     * @return the content or null if none available.
+     * @throws IOException if retrieving the content fails
+     */
+    protected Content nextContent() throws IOException
+    {
+        Content content = pollContent();
+        if (content == null && !isFinished())
         {
-            synchronized (lock())
-            {
-                if (_eofState != null)
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("{} eof {}", this, _eofState);
-                    _contentState = _eofState;
-                }
-            }
+            produceContent();
+            content = pollContent();
         }
         return content;
     }
 
     /**
-     * Access the next content to be consumed from.   Returning the next item does not consume it
-     * and it may be returned multiple times until it is consumed.
-     * <p/>
-     * Calls to {@link #get(Object, byte[], int, int)}
-     * or {@link #consume(Object, int)} are required to consume data from the content.
+     * Poll the inputQ for Content.
+     * Consumed buffers and {@link PoisonPillContent}s are removed and
+     * EOF state updated if need be.
      *
-     * @return the content or null if none available.
+     * @return Content or null
+     */
+    protected Content pollContent()
+    {
+        // Items are removed only when they are fully consumed.
+        Content content = _inputQ.peek();
+        // Skip consumed items at the head of the queue.
+        while (content != null && remaining(content) == 0)
+        {
+            _inputQ.poll();
+            content.succeeded();
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} consumed {}", this, content);
+
+            if (content == EOF_CONTENT)
+            {
+                if (_listener == null)
+                    _state = EOF;
+                else
+                {
+                    _state = AEOF;
+                    boolean woken = _channelState.onReadReady(); // force callback?
+                    if (woken)
+                        wake();
+                }
+            }
+            else if (content == EARLY_EOF_CONTENT)
+                _state = EARLY_EOF;
+
+            content = _inputQ.peek();
+        }
+
+        return content;
+    }
+
+    /**
+     */
+    protected void consumeNonContent()
+    {
+        // Items are removed only when they are fully consumed.
+        Content content = _inputQ.peek();
+        // Skip consumed items at the head of the queue.
+        while (content != null && remaining(content) == 0)
+        {
+            // Defer EOF until read
+            if (content instanceof EofContent)
+                break;
+
+            // Consume all other empty content
+            _inputQ.poll();
+            content.succeeded();
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} consumed {}", this, content);
+            content = _inputQ.peek();
+        }
+    }
+
+    /**
+     * Get the next readable from the inputQ, calling {@link #produceContent()}
+     * if need be. EOF is NOT processed and state is not changed.
+     *
+     * @return the content or EOF or null if none available.
      * @throws IOException if retrieving the content fails
      */
-    protected abstract T nextContent() throws IOException;
+    protected Content nextReadable() throws IOException
+    {
+        Content content = pollReadable();
+        if (content == null && !isFinished())
+        {
+            produceContent();
+            content = pollReadable();
+        }
+        return content;
+    }
+
+    /**
+     * Poll the inputQ for Content or EOF.
+     * Consumed buffers and non EOF {@link PoisonPillContent}s are removed.
+     * EOF state is not updated.
+     *
+     * @return Content, EOF or null
+     */
+    protected Content pollReadable()
+    {
+        // Items are removed only when they are fully consumed.
+        Content content = _inputQ.peek();
+
+        // Skip consumed items at the head of the queue except EOF
+        while (content != null)
+        {
+            if (content == EOF_CONTENT || content == EARLY_EOF_CONTENT || remaining(content) > 0)
+                return content;
+
+            _inputQ.poll();
+            content.succeeded();
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} consumed {}", this, content);
+            content = _inputQ.peek();
+        }
+
+        return null;
+    }
 
     /**
      * @param item the content
      * @return how many bytes remain in the given content
      */
-    protected abstract int remaining(T item);
+    protected int remaining(Content item)
+    {
+        return item.remaining();
+    }
 
     /**
      * Copies the given content into the given byte buffer.
      *
-     * @param item   the content to copy from
-     * @param buffer the buffer to copy into
-     * @param offset the buffer offset to start copying from
-     * @param length the space available in the buffer
+     * @param content the content to copy from
+     * @param buffer  the buffer to copy into
+     * @param offset  the buffer offset to start copying from
+     * @param length  the space available in the buffer
      * @return the number of bytes actually copied
      */
-    protected abstract int get(T item, byte[] buffer, int offset, int length);
+    protected int get(Content content, byte[] buffer, int offset, int length)
+    {
+        int l = Math.min(content.remaining(), length);
+        content.getContent().get(buffer, offset, l);
+        _contentConsumed += l;
+        return l;
+    }
 
     /**
      * Consumes the given content.
+     * Calls the content succeeded if all content consumed.
      *
-     * @param item   the content to consume
-     * @param length the number of bytes to consume
+     * @param content the content to consume
+     * @param length  the number of bytes to consume
      */
-    protected abstract void consume(T item, int length);
+    protected void skip(Content content, int length)
+    {
+        int l = Math.min(content.remaining(), length);
+        ByteBuffer buffer = content.getContent();
+        buffer.position(buffer.position() + l);
+        _contentConsumed += l;
+        if (l > 0 && !content.hasContent())
+            pollContent(); // hungry succeed
+
+    }
 
     /**
      * Blocks until some content or some end-of-file event arrives.
      *
      * @throws IOException if the wait is interrupted
      */
-    protected abstract void blockForContent() throws IOException;
+    protected void blockForContent() throws IOException
+    {
+        try
+        {
+            long timeout = 0;
+            if (_blockUntil != 0)
+            {
+                timeout = TimeUnit.NANOSECONDS.toMillis(_blockUntil - System.nanoTime());
+                if (timeout <= 0)
+                    throw new TimeoutException();
+            }
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} blocking for content timeout={}", this, timeout);
+            if (timeout > 0)
+                _inputQ.wait(timeout);
+            else
+                _inputQ.wait();
+
+            // TODO: cannot return unless there is content or timeout,
+            // TODO: so spurious wakeups are not handled correctly.
+
+            if (_blockUntil != 0 && TimeUnit.NANOSECONDS.toMillis(_blockUntil - System.nanoTime()) <= 0)
+                throw new TimeoutException(String.format("Blocking timeout %d ms", getBlockingTimeout()));
+        }
+        catch (Throwable e)
+        {
+            throw (IOException)new InterruptedIOException().initCause(e);
+        }
+    }
+
+    /**
+     * Adds some content to the start of this input stream.
+     * <p>Typically used to push back content that has
+     * been read, perhaps mutated.  The bytes prepended are
+     * deducted for the contentConsumed total</p>
+     *
+     * @param item the content to add
+     * @return true if content channel woken for read
+     */
+    public boolean prependContent(Content item)
+    {
+        boolean woken = false;
+        synchronized (_inputQ)
+        {
+            _inputQ.push(item);
+            _contentConsumed -= item.remaining();
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} prependContent {}", this, item);
+
+            if (_listener == null)
+                _inputQ.notify();
+            else
+                woken = _channelState.onReadPossible();
+        }
+
+        return woken;
+    }
 
     /**
      * Adds some content to this input stream.
      *
      * @param item the content to add
+     * @return true if content channel woken for read
      */
-    public abstract void content(T item);
-
-    protected boolean onAsyncRead()
+    public boolean addContent(Content item)
     {
-        synchronized (lock())
+        boolean woken = false;
+        synchronized (_inputQ)
         {
+            if (_firstByteTimeStamp == -1)
+                _firstByteTimeStamp = System.nanoTime();
+            _contentArrived += item.remaining();
+            _inputQ.offer(item);
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} addContent {}", this, item);
+
             if (_listener == null)
-                return false;
+                _inputQ.notify();
+            else
+                woken = _channelState.onReadPossible();
         }
-        _channelState.onReadPossible();
-        return true;
+
+        return woken;
     }
 
-    public long getContentRead()
+    public boolean hasContent()
     {
-        synchronized (lock())
+        synchronized (_inputQ)
         {
-            return _contentRead;
+            return _inputQ.size() > 0;
+        }
+    }
+
+    public void unblock()
+    {
+        synchronized (_inputQ)
+        {
+            _inputQ.notify();
+        }
+    }
+
+    public long getContentConsumed()
+    {
+        synchronized (_inputQ)
+        {
+            return _contentConsumed;
         }
     }
 
     /**
      * This method should be called to signal that an EOF has been
      * detected before all the expected content arrived.
-     * <p/>
+     * <p>
      * Typically this will result in an EOFException being thrown
      * from a subsequent read rather than a -1 return.
+     *
+     * @return true if content channel woken for read
      */
-    public void earlyEOF()
+    public boolean earlyEOF()
     {
-        synchronized (lock())
-        {
-            if (!isEOF())
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("{} early EOF", this);
-                _eofState = EARLY_EOF;
-                if (_listener == null)
-                    return;
-            }
-        }
-        _channelState.onReadPossible();
-    }
-
-
-    public boolean isEarlyEOF()
-    {
-        synchronized (lock())
-        {
-            return _contentState==EARLY_EOF;
-        }
+        return addContent(EARLY_EOF_CONTENT);
     }
 
     /**
      * This method should be called to signal that all the expected
      * content arrived.
+     *
+     * @return true if content channel woken for read
      */
-    public void messageComplete()
+    public boolean eof()
     {
-        synchronized (lock())
-        {
-            if (!isEOF())
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("{} EOF", this);
-                _eofState = EOF;
-                if (_listener == null)
-                    return;
-            }
-        }
-        _channelState.onReadPossible();
+        return addContent(EOF_CONTENT);
     }
 
     public boolean consumeAll()
     {
-        synchronized (lock())
+        synchronized (_inputQ)
         {
-            // Don't bother reading if we already know there was an error.
-            if (_onError != null)
-                return false;
-
             try
             {
                 while (!isFinished())
                 {
-                    T item = getNextContent();
+                    Content item = nextContent();
                     if (item == null)
-                        _contentState.waitForContent(this);
-                    else
-                        consume(item, remaining(item));
+                        break; // Let's not bother blocking
+
+                    skip(item, remaining(item));
                 }
-                return true;
+                return isFinished() && !isError();
             }
             catch (IOException e)
             {
@@ -306,187 +537,314 @@
         }
     }
 
-    public boolean isAsync()
+    public boolean isError()
     {
-        synchronized (lock())
+        synchronized (_inputQ)
         {
-            return _contentState==ASYNC;
+            return _state instanceof ErrorState;
         }
     }
 
-    /**
-     * @return whether an EOF has been detected, even though there may be content to consume.
-     */
-    public boolean isEOF()
+    public boolean isAsync()
     {
-        synchronized (lock())
+        synchronized (_inputQ)
         {
-            return _eofState != null && _eofState.isEOF();
+            return _state == ASYNC;
         }
     }
 
     @Override
     public boolean isFinished()
     {
-        synchronized (lock())
+        synchronized (_inputQ)
         {
-            return _contentState.isEOF();
+            return _state instanceof EOFState;
         }
     }
 
-
     @Override
     public boolean isReady()
     {
-        boolean finished;
-        synchronized (lock())
+        try
         {
-            if (_contentState.isEOF())
-                return true;
-            if (_listener == null )
-                return true;
-            if (available() > 0)
-                return true;
-            if (_notReady)
-                return false;
-            _notReady = true;
-            finished = isFinished();
-        }
-        if (finished)
-            _channelState.onReadPossible();
-        else
-            unready();
-        return false;
-    }
+            synchronized (_inputQ)
+            {
+                if (_listener == null)
+                    return true;
+                if (_state instanceof EOFState)
+                    return true;
+                if (nextReadable() != null)
+                    return true;
 
-    protected void unready()
-    {
+                _channelState.onReadUnready();
+            }
+            return false;
+        }
+        catch (IOException e)
+        {
+            LOG.ignore(e);
+            return true;
+        }
     }
 
     @Override
     public void setReadListener(ReadListener readListener)
     {
+        readListener = Objects.requireNonNull(readListener);
+        boolean woken = false;
         try
         {
-            readListener = Objects.requireNonNull(readListener);
-            boolean content;
-            synchronized (lock())
+            synchronized (_inputQ)
             {
-                if (_contentState != STREAM)
-                    throw new IllegalStateException("state=" + _contentState);
-                _contentState = ASYNC;
+                if (_listener != null)
+                    throw new IllegalStateException("ReadListener already set");
+                if (_state != STREAM)
+                    throw new IllegalStateException("State " + STREAM + " != " + _state);
+
+                _state = ASYNC;
                 _listener = readListener;
-                _notReady = true;
+                boolean content = nextContent() != null;
 
-                content = getNextContent()!=null || isEOF();
-
+                if (content)
+                    woken = _channelState.onReadReady();
+                else
+                    _channelState.onReadUnready();
             }
-            if (content)
-                _channelState.onReadPossible();
-            else
-                unready();
         }
-        catch(IOException e)
+        catch (IOException e)
         {
             throw new RuntimeIOException(e);
         }
+
+        if (woken)
+            wake();
     }
 
-    public void failed(Throwable x)
+    public boolean failed(Throwable x)
     {
-        synchronized (lock())
+        boolean woken = false;
+        synchronized (_inputQ)
         {
-            if (_onError != null)
+            if (_state instanceof ErrorState)
                 LOG.warn(x);
             else
-                _onError = x;
+                _state = new ErrorState(x);
+
+            if (_listener == null)
+                _inputQ.notify();
+            else
+                woken = _channelState.onReadPossible();
         }
+
+        return woken;
     }
 
+    /*
+     * <p>
+     * While this class is-a Runnable, it should never be dispatched in it's own thread. It is a
+     * runnable only so that the calling thread can use {@link ContextHandler#handle(Runnable)}
+     * to setup classloaders etc.
+     * </p>
+     */
     @Override
     public void run()
     {
         final Throwable error;
         final ReadListener listener;
-        boolean available = false;
-        final boolean eof;
+        boolean aeof = false;
 
-        synchronized (lock())
+        synchronized (_inputQ)
         {
-            if (!_notReady || _listener == null)
+            if (_state == EOF)
                 return;
 
-            error = _onError;
+            if (_state == AEOF)
+            {
+                _state = EOF;
+                aeof = true;
+            }
+
             listener = _listener;
-
-            try
-            {
-                T item = getNextContent();
-                available = item != null && remaining(item) > 0;
-            }
-            catch (Exception e)
-            {
-                failed(e);
-            }
-
-            eof = !available && isFinished();
-            _notReady = !available && !eof;
+            error = _state instanceof ErrorState ? ((ErrorState)_state).getError() : null;
         }
 
         try
         {
             if (error != null)
+            {
+                _channelState.getHttpChannel().getResponse().getHttpFields().add(HttpConnection.CONNECTION_CLOSE);
                 listener.onError(error);
-            else if (available)
-                listener.onDataAvailable();
-            else if (eof)
+            }
+            else if (aeof)
+            {
                 listener.onAllDataRead();
+            }
             else
-                unready();
+            {
+                listener.onDataAvailable();
+            }
         }
         catch (Throwable e)
         {
             LOG.warn(e.toString());
             LOG.debug(e);
-            listener.onError(e);
+            try
+            {
+                if (aeof || error == null)
+                {
+                    _channelState.getHttpChannel().getResponse().getHttpFields().add(HttpConnection.CONNECTION_CLOSE);
+                    listener.onError(e);
+                }
+            }
+            catch (Throwable e2)
+            {
+                LOG.warn(e2.toString());
+                LOG.debug(e2);
+                throw new RuntimeIOException(e2);
+            }
         }
     }
 
     @Override
     public String toString()
     {
-        return String.format("%s@%x[r=%d,s=%s,e=%s,f=%s]",
+        State state;
+        long consumed;
+        int q;
+        Content content;
+        synchronized (_inputQ)
+        {
+            state = _state;
+            consumed = _contentConsumed;
+            q = _inputQ.size();
+            content = _inputQ.peekFirst();
+        }
+        return String.format("%s@%x[c=%d,q=%d,[0]=%s,s=%s]",
                 getClass().getSimpleName(),
                 hashCode(),
-                _contentRead,
-                _contentState,
-                _eofState,
-                _onError);
+                consumed,
+                q,
+                content,
+                state);
     }
 
+    public static class PoisonPillContent extends Content
+    {
+        private final String _name;
+
+        public PoisonPillContent(String name)
+        {
+            super(BufferUtil.EMPTY_BUFFER);
+            _name = name;
+        }
+
+        @Override
+        public String toString()
+        {
+            return _name;
+        }
+    }
+
+    public static class EofContent extends PoisonPillContent
+    {
+        EofContent(String name)
+        {
+            super(name);
+        }
+    }
+
+    public static class Content implements Callback
+    {
+        private final ByteBuffer _content;
+
+        public Content(ByteBuffer content)
+        {
+            _content = content;
+        }
+
+        @Override
+        public boolean isNonBlocking()
+        {
+            return true;
+        }
+
+
+        public ByteBuffer getContent()
+        {
+            return _content;
+        }
+
+        public boolean hasContent()
+        {
+            return _content.hasRemaining();
+        }
+
+        public int remaining()
+        {
+            return _content.remaining();
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("Content@%x{%s}", hashCode(), BufferUtil.toDetailString(_content));
+        }
+    }
+
+
     protected static abstract class State
     {
-        public void waitForContent(HttpInput<?> in) throws IOException
+        public boolean blockForContent(HttpInput in) throws IOException
         {
+            return false;
         }
 
         public int noContent() throws IOException
         {
             return -1;
         }
+    }
 
-        public boolean isEOF()
+    protected static class EOFState extends State
+    {
+    }
+
+    protected class ErrorState extends EOFState
+    {
+        final Throwable _error;
+
+        ErrorState(Throwable error)
         {
-            return false;
+            _error = error;
+        }
+
+        public Throwable getError()
+        {
+            return _error;
+        }
+
+        @Override
+        public int noContent() throws IOException
+        {
+            if (_error instanceof IOException)
+                throw (IOException)_error;
+            throw new IOException(_error);
+        }
+
+        @Override
+        public String toString()
+        {
+            return "ERROR:" + _error;
         }
     }
 
     protected static final State STREAM = new State()
     {
         @Override
-        public void waitForContent(HttpInput<?> input) throws IOException
+        public boolean blockForContent(HttpInput input) throws IOException
         {
             input.blockForContent();
+            return true;
         }
 
         @Override
@@ -511,7 +869,7 @@
         }
     };
 
-    protected static final State EARLY_EOF = new State()
+    protected static final State EARLY_EOF = new EOFState()
     {
         @Override
         public int noContent() throws IOException
@@ -520,30 +878,27 @@
         }
 
         @Override
-        public boolean isEOF()
-        {
-            return true;
-        }
-
-        @Override
         public String toString()
         {
             return "EARLY_EOF";
         }
     };
 
-    protected static final State EOF = new State()
+    protected static final State EOF = new EOFState()
     {
         @Override
-        public boolean isEOF()
-        {
-            return true;
-        }
-
-        @Override
         public String toString()
         {
             return "EOF";
         }
     };
+
+    protected static final State AEOF = new EOFState()
+    {
+        @Override
+        public String toString()
+        {
+            return "AEOF";
+        }
+    };
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInputOverHTTP.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInputOverHTTP.java
index 48d242a..204c064 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInputOverHTTP.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInputOverHTTP.java
@@ -19,128 +19,31 @@
 package org.eclipse.jetty.server;
 
 import java.io.IOException;
-import java.nio.ByteBuffer;
 
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.SharedBlockingCallback;
-import org.eclipse.jetty.util.SharedBlockingCallback.Blocker;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-public class HttpInputOverHTTP extends HttpInput<ByteBuffer> implements Callback
+public class HttpInputOverHTTP extends HttpInput
 {
-    private static final Logger LOG = Log.getLogger(HttpInputOverHTTP.class);
-    private final SharedBlockingCallback _readBlocker = new SharedBlockingCallback();
-    private final HttpConnection _httpConnection;
-    private ByteBuffer _content;
-
-    /**
-     * @param httpConnection
-     */
-    public HttpInputOverHTTP(HttpConnection httpConnection)
+    public HttpInputOverHTTP(HttpChannelState state)
     {
-        _httpConnection = httpConnection;
+        super(state);
     }
 
     @Override
-    public void recycle()
+    protected void produceContent() throws IOException
     {
-        synchronized (lock())
-        {
-            super.recycle();
-            _content=null;
-        }
+        ((HttpConnection)getHttpChannelState().getHttpChannel().getEndPoint().getConnection()).fillAndParseForContent();
     }
 
     @Override
     protected void blockForContent() throws IOException
     {
-        while(true)
+        ((HttpConnection)getHttpChannelState().getHttpChannel().getEndPoint().getConnection()).blockingReadFillInterested();
+        try
         {
-            try (Blocker blocker=_readBlocker.acquire())
-            {            
-                _httpConnection.fillInterested(blocker);
-                if (LOG.isDebugEnabled())
-                    LOG.debug("{} block readable on {}",this,blocker);
-                blocker.block();
-            }
-
-            Object content=getNextContent();
-            if (content!=null || isFinished())
-                break;
+            super.blockForContent();
         }
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("%s@%x",getClass().getSimpleName(),hashCode());
-    }
-
-    @Override
-    protected ByteBuffer nextContent() throws IOException
-    {
-        // If we have some content available, return it
-        if (BufferUtil.hasContent(_content))
-            return _content;
-
-        // No - then we are going to need to parse some more content
-        _content=null;
-        _httpConnection.parseContent();
-        
-        // If we have some content available, return it
-        if (BufferUtil.hasContent(_content))
-            return _content;
-
-        return null;
-
-    }
-
-    @Override
-    protected int remaining(ByteBuffer item)
-    {
-        return item.remaining();
-    }
-
-    @Override
-    protected int get(ByteBuffer item, byte[] buffer, int offset, int length)
-    {
-        int l = Math.min(item.remaining(), length);
-        item.get(buffer, offset, l);
-        return l;
-    }
-
-    @Override
-    protected void consume(ByteBuffer item, int length)
-    {
-        item.position(item.position()+length);
-    }
-
-    @Override
-    public void content(ByteBuffer item)
-    {
-        if (BufferUtil.hasContent(_content))
-            throw new IllegalStateException();
-        _content=item;
-    }
-
-    @Override
-    protected void unready()
-    {
-        _httpConnection.fillInterested(this);
-    }
-
-    @Override
-    public void succeeded()
-    {
-        _httpConnection.getHttpChannel().getState().onReadPossible();
-    }
-
-    @Override
-    public void failed(Throwable x)
-    {
-        super.failed(x);
-        _httpConnection.getHttpChannel().getState().onReadPossible();
+        catch(Throwable e)
+        {
+            ((HttpConnection)getHttpChannelState().getHttpChannel().getEndPoint().getConnection()).blockingReadException(e);
+        }
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java
index bf02f48..0831963 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java
@@ -18,12 +18,14 @@
 
 package org.eclipse.jetty.server;
 
+import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.WritePendingException;
 import java.util.concurrent.atomic.AtomicReference;
+
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.ServletRequest;
@@ -53,17 +55,79 @@
  */
 public class HttpOutput extends ServletOutputStream implements Runnable
 {
-    private static Logger LOG = Log.getLogger(HttpOutput.class);
-    private final HttpChannel<?> _channel;
-    private final SharedBlockingCallback _writeblock=new SharedBlockingCallback()
+    /**
+     * The HttpOutput.Interceptor is a single intercept point for all
+     * output written to the HttpOutput: via writer; via output stream;
+     * asynchronously; or blocking.
+     * <p>
+     * The Interceptor can be used to implement translations (eg Gzip) or
+     * additional buffering that acts on all output.  Interceptors are
+     * created in a chain, so that multiple concerns may intercept.
+     * <p>
+     * The {@link HttpChannel} is an {@link Interceptor} and is always the
+     * last link in any Interceptor chain.
+     * <p>
+     * Responses are committed by the first call to
+     * {@link #write(ByteBuffer, boolean, Callback)}
+     * and closed by a call to {@link #write(ByteBuffer, boolean, Callback)}
+     * with the last boolean set true.  If no content is available to commit
+     * or close, then a null buffer is passed.
+     */
+    public interface Interceptor
     {
-        @Override
-        protected long getIdleTimeout()
+        /**
+         * Write content.
+         * The response is committed by the first call to write and is closed by
+         * a call with last == true. Empty content buffers may be passed to
+         * force a commit or close.
+         *
+         * @param content  The content to be written or an empty buffer.
+         * @param last     True if this is the last call to write
+         * @param callback The callback to use to indicate {@link Callback#succeeded()}
+         *                 or {@link Callback#failed(Throwable)}.
+         */
+        void write(ByteBuffer content, boolean last, Callback callback);
+
+        /**
+         * @return The next Interceptor in the chain or null if this is the
+         * last Interceptor in the chain.
+         */
+        Interceptor getNextInterceptor();
+
+        /**
+         * @return True if the Interceptor is optimized to receive direct
+         * {@link ByteBuffer}s in the {@link #write(ByteBuffer, boolean, Callback)}
+         * method.   If false is returned, then passing direct buffers may cause
+         * inefficiencies.
+         */
+        boolean isOptimizedForDirectBuffers();
+
+        /**
+         * Reset the buffers.
+         * <p>If the Interceptor contains buffers then reset them.
+         *
+         * @throws IllegalStateException Thrown if the response has been
+         *                               committed and buffers and/or headers cannot be reset.
+         */
+        default void resetBuffer() throws IllegalStateException
         {
-            return _channel.getIdleTimeout();
+            Interceptor next = getNextInterceptor();
+            if (next != null)
+                next.resetBuffer();
         }
-    };
+    }
+
+    private static Logger LOG = Log.getLogger(HttpOutput.class);
+
+    private final HttpChannel _channel;
+    private final SharedBlockingCallback _writeBlocker;
+    private Interceptor _interceptor;
+
+    /**
+     * Bytes written via the write API (excludes bytes written via sendContent). Used to autocommit once content length is written.
+     */
     private long _written;
+
     private ByteBuffer _aggregate;
     private int _bufferSize;
     private int _commitSize;
@@ -72,36 +136,51 @@
 
     /*
     ACTION             OPEN       ASYNC      READY      PENDING       UNREADY       CLOSED
-    -----------------------------------------------------------------------------------------------------
+    -------------------------------------------------------------------------------------------
     setWriteListener() READY->owp ise        ise        ise           ise           ise
     write()            OPEN       ise        PENDING    wpe           wpe           eof
     flush()            OPEN       ise        PENDING    wpe           wpe           eof
     close()            CLOSED     CLOSED     CLOSED     CLOSED        wpe           CLOSED
     isReady()          OPEN:true  READY:true READY:true UNREADY:false UNREADY:false CLOSED:true
     write completed    -          -          -          ASYNC         READY->owp    -
-    
     */
-    enum OutputState { OPEN, ASYNC, READY, PENDING, UNREADY, ERROR, CLOSED }
-    private final AtomicReference<OutputState> _state=new AtomicReference<>(OutputState.OPEN);
+    private enum OutputState
+    {
+        OPEN, ASYNC, READY, PENDING, UNREADY, ERROR, CLOSED
+    }
 
-    public HttpOutput(HttpChannel<?> channel)
+    private final AtomicReference<OutputState> _state = new AtomicReference<>(OutputState.OPEN);
+
+    public HttpOutput(HttpChannel channel)
     {
         _channel = channel;
+        _interceptor = channel;
+        _writeBlocker = new WriteBlocker(channel);
         HttpConfiguration config = channel.getHttpConfiguration();
         _bufferSize = config.getOutputBufferSize();
         _commitSize = config.getOutputAggregationSize();
-        if (_commitSize>_bufferSize)
+        if (_commitSize > _bufferSize)
         {
-            LOG.warn("OutputAggregationSize {} exceeds bufferSize {}",_commitSize,_bufferSize);
-            _commitSize=_bufferSize;
+            LOG.warn("OutputAggregationSize {} exceeds bufferSize {}", _commitSize, _bufferSize);
+            _commitSize = _bufferSize;
         }
     }
-    
-    public HttpChannel<?> getHttpChannel()
+
+    public HttpChannel getHttpChannel()
     {
         return _channel;
     }
-    
+
+    public Interceptor getInterceptor()
+    {
+        return _interceptor;
+    }
+
+    public void setInterceptor(Interceptor filter)
+    {
+        _interceptor = filter;
+    }
+
     public boolean isWritten()
     {
         return _written > 0;
@@ -112,25 +191,17 @@
         return _written;
     }
 
-    public void reset()
-    {
-        HttpConfiguration config = _channel.getHttpConfiguration();
-        _bufferSize = config.getOutputBufferSize();
-        _commitSize = config.getOutputAggregationSize();
-        if (_commitSize>_bufferSize)
-        {
-            LOG.warn("OutputAggregationSize {} exceeds bufferSize {}",_commitSize,_bufferSize);
-            _commitSize=_bufferSize;
-        }
-        _written = 0;
-        reopen();
-    }
-
     public void reopen()
     {
         _state.set(OutputState.OPEN);
     }
 
+    private boolean isLastContentToWrite(int len)
+    {
+        _written += len;
+        return _channel.getResponse().isAllContentWritten(_written);
+    }
+
     public boolean isAllContentWritten()
     {
         return _channel.getResponse().isAllContentWritten(_written);
@@ -138,89 +209,123 @@
 
     protected Blocker acquireWriteBlockingCallback() throws IOException
     {
-        return _writeblock.acquire();
+        return _writeBlocker.acquire();
     }
-    
-    protected void write(ByteBuffer content, boolean complete) throws IOException
+
+    private void write(ByteBuffer content, boolean complete) throws IOException
     {
-        try (Blocker blocker=_writeblock.acquire())
-        {        
-            write(content,complete,blocker);
+        try (Blocker blocker = _writeBlocker.acquire())
+        {
+            write(content, complete, blocker);
             blocker.block();
         }
+        catch (Exception failure)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug(failure);
+            abort(failure);
+            if (failure instanceof IOException)
+                throw failure;
+            throw new IOException(failure);
+        }
     }
-    
+
     protected void write(ByteBuffer content, boolean complete, Callback callback)
     {
-        _channel.write(content,complete,callback);
+        _interceptor.write(content, complete, callback);
     }
-    
+
+    private void abort(Throwable failure)
+    {
+        closed();
+        _channel.abort(failure);
+    }
+
     @Override
     public void close()
     {
-        loop: while(true)
+        while (true)
         {
-            OutputState state=_state.get();
+            OutputState state = _state.get();
             switch (state)
             {
                 case CLOSED:
-                    break loop;
-                    
+                {
+                    return;
+                }
                 case UNREADY:
-                    if (_state.compareAndSet(state,OutputState.ERROR))
-                        _writeListener.onError(_onError==null?new EofException("Async close"):_onError);
-                    continue;
-                    
+                {
+                    if (_state.compareAndSet(state, OutputState.ERROR))
+                        _writeListener.onError(_onError == null ? new EofException("Async close") : _onError);
+                    break;
+                }
                 default:
-                    if (_state.compareAndSet(state,OutputState.CLOSED))
+                {
+                    if (!_state.compareAndSet(state, OutputState.CLOSED))
+                        break;
+
+                    try
                     {
-                        try
-                        {
-                            write(BufferUtil.hasContent(_aggregate)?_aggregate:BufferUtil.EMPTY_BUFFER,!_channel.getResponse().isIncluding());
-                        }
-                        catch(IOException e)
-                        {
-                            LOG.debug(e);
-                            _channel.abort();
-                        }
-                        releaseBuffer();
-                        return;
+                        write(BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER, !_channel.getResponse().isIncluding());
                     }
+                    catch (IOException x)
+                    {
+                        // Ignore it, it's been already logged in write().
+                    }
+                    finally
+                    {
+                        releaseBuffer();
+                    }
+                    // Return even if an exception is thrown by write().
+                    return;
+                }
             }
         }
     }
 
-    /* Called to indicated that the output is already closed (write with last==true performed) and the state needs to be updated to match */
+    /**
+     * Called to indicate that the last write has been performed.
+     * It updates the state and performs cleanup operations.
+     */
     void closed()
     {
-        loop: while(true)
+        while (true)
         {
-            OutputState state=_state.get();
+            OutputState state = _state.get();
             switch (state)
             {
                 case CLOSED:
-                    break loop;
-                    
+                {
+                    return;
+                }
                 case UNREADY:
-                    if (_state.compareAndSet(state,OutputState.ERROR))
-                        _writeListener.onError(_onError==null?new EofException("Async closed"):_onError);
-                    continue;
-                    
+                {
+                    if (_state.compareAndSet(state, OutputState.ERROR))
+                        _writeListener.onError(_onError == null ? new EofException("Async closed") : _onError);
+                    break;
+                }
                 default:
-                    if (_state.compareAndSet(state,OutputState.CLOSED))
+                {
+                    if (!_state.compareAndSet(state, OutputState.CLOSED))
+                        break;
+
+                    try
                     {
-                        try
-                        {
-                            _channel.getResponse().closeOutput();
-                        }
-                        catch(IOException e)
-                        {
-                            LOG.debug(e);
-                            _channel.abort();
-                        }
-                        releaseBuffer();
-                        return;
+                        _channel.getResponse().closeOutput();
                     }
+                    catch (Throwable x)
+                    {
+                        if (LOG.isDebugEnabled())
+                            LOG.debug(x);
+                        abort(x);
+                    }
+                    finally
+                    {
+                        releaseBuffer();
+                    }
+                    // Return even if an exception is thrown by closeOutput().
+                    return;
+                }
             }
         }
     }
@@ -236,18 +341,18 @@
 
     public boolean isClosed()
     {
-        return _state.get()==OutputState.CLOSED;
+        return _state.get() == OutputState.CLOSED;
     }
 
     @Override
     public void flush() throws IOException
     {
-        while(true)
+        while (true)
         {
-            switch(_state.get())
+            switch (_state.get())
             {
                 case OPEN:
-                    write(BufferUtil.hasContent(_aggregate)?_aggregate:BufferUtil.EMPTY_BUFFER, false);
+                    write(BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER, false);
                     return;
 
                 case ASYNC:
@@ -265,25 +370,23 @@
 
                 case ERROR:
                     throw new EofException(_onError);
-                    
+
                 case CLOSED:
                     return;
+
+                default:
+                    throw new IllegalStateException();
             }
-            break;
         }
     }
 
-
     @Override
     public void write(byte[] b, int off, int len) throws IOException
     {
-        _written+=len;
-        boolean complete=_channel.getResponse().isAllContentWritten(_written);
-
         // Async or Blocking ?
-        while(true)
+        while (true)
         {
-            switch(_state.get())
+            switch (_state.get())
             {
                 case OPEN:
                     // process blocking below
@@ -297,16 +400,17 @@
                         continue;
 
                     // Should we aggregate?
-                    if (!complete && len<=_commitSize)
+                    boolean last = isLastContentToWrite(len);
+                    if (!last && len <= _commitSize)
                     {
                         if (_aggregate == null)
-                            _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false);
+                            _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), _interceptor.isOptimizedForDirectBuffers());
 
                         // YES - fill the aggregate with content from the buffer
                         int filled = BufferUtil.fill(_aggregate, b, off, len);
 
                         // return if we are not complete, not full and filled all the content
-                        if (filled==len && !BufferUtil.isFull(_aggregate))
+                        if (filled == len && !BufferUtil.isFull(_aggregate))
                         {
                             if (!_state.compareAndSet(OutputState.PENDING, OutputState.ASYNC))
                                 throw new IllegalStateException();
@@ -314,12 +418,12 @@
                         }
 
                         // adjust offset/length
-                        off+=filled;
-                        len-=filled;
+                        off += filled;
+                        len -= filled;
                     }
 
                     // Do the asynchronous writing from the callback
-                    new AsyncWrite(b,off,len,complete).iterate();
+                    new AsyncWrite(b, off, len, last).iterate();
                     return;
 
                 case PENDING:
@@ -328,42 +432,45 @@
 
                 case ERROR:
                     throw new EofException(_onError);
-                    
+
                 case CLOSED:
                     throw new EofException("Closed");
+
+                default:
+                    throw new IllegalStateException();
             }
             break;
         }
 
-
         // handle blocking write
 
         // Should we aggregate?
         int capacity = getBufferSize();
-        if (!complete && len<=_commitSize)
+        boolean last = isLastContentToWrite(len);
+        if (!last && len <= _commitSize)
         {
             if (_aggregate == null)
-                _aggregate = _channel.getByteBufferPool().acquire(capacity, false);
+                _aggregate = _channel.getByteBufferPool().acquire(capacity, _interceptor.isOptimizedForDirectBuffers());
 
             // YES - fill the aggregate with content from the buffer
             int filled = BufferUtil.fill(_aggregate, b, off, len);
 
             // return if we are not complete, not full and filled all the content
-            if (filled==len && !BufferUtil.isFull(_aggregate))
+            if (filled == len && !BufferUtil.isFull(_aggregate))
                 return;
 
             // adjust offset/length
-            off+=filled;
-            len-=filled;
+            off += filled;
+            len -= filled;
         }
 
         // flush any content from the aggregate
         if (BufferUtil.hasContent(_aggregate))
         {
-            write(_aggregate, complete && len==0);
+            write(_aggregate, last && len == 0);
 
             // should we fill aggregate again from the buffer?
-            if (len>0 && !complete && len<=_commitSize && len<=BufferUtil.space(_aggregate))
+            if (len > 0 && !last && len <= _commitSize && len <= BufferUtil.space(_aggregate))
             {
                 BufferUtil.append(_aggregate, b, off, len);
                 return;
@@ -371,42 +478,38 @@
         }
 
         // write any remaining content in the buffer directly
-        if (len>0)
+        if (len > 0)
         {
-            ByteBuffer wrap = ByteBuffer.wrap(b, off, len);
-            ByteBuffer view = wrap.duplicate();
-
             // write a buffer capacity at a time to avoid JVM pooling large direct buffers
             // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6210541
-            while (len>getBufferSize())
+            ByteBuffer view = ByteBuffer.wrap(b, off, len);
+            while (len > getBufferSize())
             {
-                int p=view.position();
-                int l=p+getBufferSize();
-                view.limit(p+getBufferSize());
-                write(view,false);
-                len-=getBufferSize();
-                view.limit(l+Math.min(len,getBufferSize()));
+                int p = view.position();
+                int l = p + getBufferSize();
+                view.limit(p + getBufferSize());
+                write(view, false);
+                len -= getBufferSize();
+                view.limit(l + Math.min(len, getBufferSize()));
                 view.position(l);
             }
-            write(view,complete);
+            write(view, last);
         }
-        else if (complete)
-            write(BufferUtil.EMPTY_BUFFER,complete);
+        else if (last)
+        {
+            write(BufferUtil.EMPTY_BUFFER, true);
+        }
 
-        if (complete)
+        if (last)
             closed();
-
     }
 
     public void write(ByteBuffer buffer) throws IOException
     {
-        _written+=buffer.remaining();
-        boolean complete=_channel.getResponse().isAllContentWritten(_written);
-
         // Async or Blocking ?
-        while(true)
+        while (true)
         {
-            switch(_state.get())
+            switch (_state.get())
             {
                 case OPEN:
                     // process blocking below
@@ -420,7 +523,8 @@
                         continue;
 
                     // Do the asynchronous writing from the callback
-                    new AsyncWrite(buffer,complete).iterate();
+                    boolean last = isLastContentToWrite(buffer.remaining());
+                    new AsyncWrite(buffer, last).iterate();
                     return;
 
                 case PENDING:
@@ -429,55 +533,54 @@
 
                 case ERROR:
                     throw new EofException(_onError);
-                    
+
                 case CLOSED:
                     throw new EofException("Closed");
+
+                default:
+                    throw new IllegalStateException();
             }
             break;
         }
 
-
         // handle blocking write
-        int len=BufferUtil.length(buffer);
+        int len = BufferUtil.length(buffer);
+        boolean last = isLastContentToWrite(len);
 
         // flush any content from the aggregate
         if (BufferUtil.hasContent(_aggregate))
-            write(_aggregate, complete && len==0);
+            write(_aggregate, last && len == 0);
 
         // write any remaining content in the buffer directly
-        if (len>0)
-            write(buffer, complete);
-        else if (complete)
-            write(BufferUtil.EMPTY_BUFFER,complete);
+        if (len > 0)
+            write(buffer, last);
+        else if (last)
+            write(BufferUtil.EMPTY_BUFFER, true);
 
-        if (complete)
+        if (last)
             closed();
     }
 
     @Override
     public void write(int b) throws IOException
     {
-        _written+=1;
-        boolean complete=_channel.getResponse().isAllContentWritten(_written);
+        _written += 1;
+        boolean complete = _channel.getResponse().isAllContentWritten(_written);
 
         // Async or Blocking ?
-        while(true)
+        while (true)
         {
-            switch(_state.get())
+            switch (_state.get())
             {
                 case OPEN:
                     if (_aggregate == null)
-                        _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false);
+                        _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), _interceptor.isOptimizedForDirectBuffers());
                     BufferUtil.append(_aggregate, (byte)b);
 
                     // Check if all written or full
                     if (complete || BufferUtil.isFull(_aggregate))
                     {
-                        try(Blocker blocker=_writeblock.acquire())
-                        {
-                            write(_aggregate, complete, blocker);
-                            blocker.block();
-                        }
+                        write(_aggregate, complete);
                         if (complete)
                             closed();
                     }
@@ -491,7 +594,7 @@
                         continue;
 
                     if (_aggregate == null)
-                        _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false);
+                        _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), _interceptor.isOptimizedForDirectBuffers());
                     BufferUtil.append(_aggregate, (byte)b);
 
                     // Check if all written or full
@@ -512,9 +615,12 @@
 
                 case ERROR:
                     throw new EofException(_onError);
-                    
+
                 case CLOSED:
                     throw new EofException("Closed");
+
+                default:
+                    throw new IllegalStateException();
             }
             break;
         }
@@ -529,116 +635,157 @@
         write(s.getBytes(_channel.getResponse().getCharacterEncoding()));
     }
 
-    /* ------------------------------------------------------------ */
-    /** Blocking send of content.
-     * @param content The content to send.
-     * @throws IOException
+    /**
+     * Blocking send of whole content.
+     *
+     * @param content The whole content to send
+     * @throws IOException if the send fails
      */
     public void sendContent(ByteBuffer content) throws IOException
     {
-        try(Blocker blocker=_writeblock.acquire())
-        {
-            write(content,true,blocker);
-            blocker.block();
-        }
+        if (LOG.isDebugEnabled())
+            LOG.debug("sendContent({})", BufferUtil.toDetailString(content));
+
+        write(content, true);
+        closed();
     }
 
-    /* ------------------------------------------------------------ */
-    /** Blocking send of content.
-     * @param in The content to send
-     * @throws IOException
+    /**
+     * Blocking send of stream content.
+     *
+     * @param in The stream content to send
+     * @throws IOException if the send fails
      */
     public void sendContent(InputStream in) throws IOException
     {
-        try(Blocker blocker=_writeblock.acquire())
+        try (Blocker blocker = _writeBlocker.acquire())
         {
-            new InputStreamWritingCB(in,blocker).iterate();
+            new InputStreamWritingCB(in, blocker).iterate();
             blocker.block();
         }
+        catch (Throwable failure)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug(failure);
+            abort(failure);
+            throw failure;
+        }
     }
 
-    /* ------------------------------------------------------------ */
-    /** Blocking send of content.
-     * @param in The content to send
-     * @throws IOException
+    /**
+     * Blocking send of channel content.
+     *
+     * @param in The channel content to send
+     * @throws IOException if the send fails
      */
     public void sendContent(ReadableByteChannel in) throws IOException
     {
-        try(Blocker blocker=_writeblock.acquire())
+        try (Blocker blocker = _writeBlocker.acquire())
         {
-            new ReadableByteChannelWritingCB(in,blocker).iterate();
+            new ReadableByteChannelWritingCB(in, blocker).iterate();
             blocker.block();
         }
+        catch (Throwable failure)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug(failure);
+            abort(failure);
+            throw failure;
+        }
     }
 
-
-    /* ------------------------------------------------------------ */
-    /** Blocking send of content.
-     * @param content The content to send
-     * @throws IOException
+    /**
+     * Blocking send of HTTP content.
+     *
+     * @param content The HTTP content to send
+     * @throws IOException if the send fails
      */
     public void sendContent(HttpContent content) throws IOException
     {
-        try(Blocker blocker=_writeblock.acquire())
+        try (Blocker blocker = _writeBlocker.acquire())
         {
-            sendContent(content,blocker);
+            sendContent(content, blocker);
             blocker.block();
         }
+        catch (Throwable failure)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug(failure);
+            abort(failure);
+            throw failure;
+        }
     }
 
-    /* ------------------------------------------------------------ */
-    /** Asynchronous send of content.
-     * @param content The content to send
+    /**
+     * Asynchronous send of whole content.
+     *
+     * @param content  The whole content to send
      * @param callback The callback to use to notify success or failure
      */
     public void sendContent(ByteBuffer content, final Callback callback)
     {
-        write(content,true,new Callback()
+        if (LOG.isDebugEnabled())
+            LOG.debug("sendContent(buffer={},{})", BufferUtil.toDetailString(content), callback);
+
+        write(content, true, new Callback.Nested(callback)
         {
             @Override
             public void succeeded()
             {
                 closed();
-                callback.succeeded();
+                super.succeeded();
             }
 
             @Override
             public void failed(Throwable x)
             {
-                callback.failed(x);
+                abort(x);
+                super.failed(x);
             }
         });
     }
 
-    /* ------------------------------------------------------------ */
-    /** Asynchronous send of content.
-     * @param in The content to send as a stream.  The stream will be closed
-     * after reading all content.
+    /**
+     * Asynchronous send of stream content.
+     * The stream will be closed after reading all content.
+     *
+     * @param in       The stream content to send
      * @param callback The callback to use to notify success or failure
      */
     public void sendContent(InputStream in, Callback callback)
     {
-        new InputStreamWritingCB(in,callback).iterate();
+        if (LOG.isDebugEnabled())
+            LOG.debug("sendContent(stream={},{})", in, callback);
+
+        new InputStreamWritingCB(in, callback).iterate();
     }
 
-    /* ------------------------------------------------------------ */
-    /** Asynchronous send of content.
-     * @param in The content to send as a channel.  The channel will be closed
-     * after reading all content.
+    /**
+     * Asynchronous send of channel content.
+     * The channel will be closed after reading all content.
+     *
+     * @param in       The channel content to send
      * @param callback The callback to use to notify success or failure
      */
     public void sendContent(ReadableByteChannel in, Callback callback)
     {
-        new ReadableByteChannelWritingCB(in,callback).iterate();
+        if (LOG.isDebugEnabled())
+            LOG.debug("sendContent(channel={},{})", in, callback);
+
+        new ReadableByteChannelWritingCB(in, callback).iterate();
     }
 
-    /* ------------------------------------------------------------ */
-    /** Asynchronous send of content.
-     * @param httpContent The content to send
-     * @param callback The callback to use to notify success or failure
+    /**
+     * Asynchronous send of HTTP content.
+     *
+     * @param httpContent The HTTP content to send
+     * @param callback    The callback to use to notify success or failure
      */
     public void sendContent(HttpContent httpContent, Callback callback)
     {
+        if (LOG.isDebugEnabled())
+            LOG.debug("sendContent(http={},{})", httpContent, callback);
+
         if (BufferUtil.hasContent(_aggregate))
         {
             callback.failed(new IOException("cannot sendContent() after write()"));
@@ -646,71 +793,67 @@
         }
         if (_channel.isCommitted())
         {
-            callback.failed(new IOException("committed"));
+            callback.failed(new IOException("cannot sendContent(), output already committed"));
             return;
         }
 
         while (true)
         {
-            switch(_state.get())
+            switch (_state.get())
             {
                 case OPEN:
                     if (!_state.compareAndSet(OutputState.OPEN, OutputState.PENDING))
                         continue;
                     break;
+
                 case ERROR:
                     callback.failed(new EofException(_onError));
                     return;
-                    
+
                 case CLOSED:
                     callback.failed(new EofException("Closed"));
                     return;
+
                 default:
                     throw new IllegalStateException();
             }
             break;
         }
-        ByteBuffer buffer= _channel.useDirectBuffers()?httpContent.getDirectBuffer():null;
+
+        ByteBuffer buffer = _channel.useDirectBuffers() ? httpContent.getDirectBuffer() : null;
         if (buffer == null)
             buffer = httpContent.getIndirectBuffer();
 
-        if (buffer!=null)
+        if (buffer != null)
         {
-            if (LOG.isDebugEnabled())
-                LOG.debug("sendContent({}=={},{},direct={})",httpContent,BufferUtil.toDetailString(buffer),callback,_channel.useDirectBuffers());
-            
-            sendContent(buffer,callback);
+            sendContent(buffer, callback);
             return;
         }
 
         try
         {
-            ReadableByteChannel rbc=httpContent.getReadableByteChannel();
-            if (rbc!=null)
+            ReadableByteChannel rbc = httpContent.getReadableByteChannel();
+            if (rbc != null)
             {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("sendContent({}=={},{},direct={})",httpContent,rbc,callback,_channel.useDirectBuffers());
                 // Close of the rbc is done by the async sendContent
-                sendContent(rbc,callback);
+                sendContent(rbc, callback);
                 return;
             }
 
             InputStream in = httpContent.getInputStream();
-            if ( in!=null )
+            if (in != null)
             {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("sendContent({}=={},{},direct={})",httpContent,in,callback,_channel.useDirectBuffers());
-                sendContent(in,callback);
+                sendContent(in, callback);
                 return;
             }
-        }
-        catch(Throwable th)
-        {
-            callback.failed(th);
-            return;
-        }
 
-        callback.failed(new IllegalArgumentException("unknown content for "+httpContent));
+            throw new IllegalArgumentException("unknown content for " + httpContent);
+        }
+        catch (Throwable th)
+        {
+            abort(th);
+            callback.failed(th);
+        }
     }
 
     public int getBufferSize()
@@ -724,10 +867,28 @@
         _commitSize = size;
     }
 
+    public void recycle()
+    {
+        _interceptor = _channel;
+        HttpConfiguration config = _channel.getHttpConfiguration();
+        _bufferSize = config.getOutputBufferSize();
+        _commitSize = config.getOutputAggregationSize();
+        if (_commitSize > _bufferSize)
+            _commitSize = _bufferSize;
+        releaseBuffer();
+        _written = 0;
+        _writeListener = null;
+        _onError = null;
+        reopen();
+    }
+
     public void resetBuffer()
     {
+        _interceptor.resetBuffer();
         if (BufferUtil.hasContent(_aggregate))
             BufferUtil.clear(_aggregate);
+        _written = 0;
+        reopen();
     }
 
     @Override
@@ -739,7 +900,8 @@
         if (_state.compareAndSet(OutputState.OPEN, OutputState.READY))
         {
             _writeListener = writeListener;
-            _channel.getState().onWritePossible();
+            if (_channel.getState().onWritePossible())
+                _channel.execute(_channel);
         }
         else
             throw new IllegalStateException();
@@ -753,28 +915,35 @@
     {
         while (true)
         {
-            switch(_state.get())
+            switch (_state.get())
             {
                 case OPEN:
                     return true;
+
                 case ASYNC:
                     if (!_state.compareAndSet(OutputState.ASYNC, OutputState.READY))
                         continue;
                     return true;
+
                 case READY:
                     return true;
+
                 case PENDING:
                     if (!_state.compareAndSet(OutputState.PENDING, OutputState.UNREADY))
                         continue;
                     return false;
+
                 case UNREADY:
                     return false;
 
                 case ERROR:
                     return true;
-                    
+
                 case CLOSED:
                     return true;
+
+                default:
+                    throw new IllegalStateException();
             }
         }
     }
@@ -782,44 +951,48 @@
     @Override
     public void run()
     {
-        loop: while (true)
+        loop:
+        while (true)
         {
             OutputState state = _state.get();
 
-            if(_onError!=null)
+            if (_onError != null)
             {
-                switch(state)
+                switch (state)
                 {
                     case CLOSED:
                     case ERROR:
-                        _onError=null;
+                    {
+                        _onError = null;
                         break loop;
-
+                    }
                     default:
+                    {
                         if (_state.compareAndSet(state, OutputState.ERROR))
                         {
-                            Throwable th=_onError;
-                            _onError=null;
+                            Throwable th = _onError;
+                            _onError = null;
                             if (LOG.isDebugEnabled())
-                                LOG.debug("onError",th);
+                                LOG.debug("onError", th);
                             _writeListener.onError(th);
                             close();
-
                             break loop;
                         }
-
+                    }
                 }
                 continue;
             }
-            
-            switch(_state.get())
+
+            switch (_state.get())
             {
-                case CLOSED:
-                    // even though a write is not possible, because a close has 
-                    // occurred, we need to call onWritePossible to tell async
-                    // producer that the last write completed.
-                    // so fall through
+                case ASYNC:
                 case READY:
+                case PENDING:
+                case UNREADY:
+                    // Even though a write is not possible, because a close has
+                    // occurred, we need to call onWritePossible to tell async
+                    // producer that the last write completed, so fall through.
+                case CLOSED:
                     try
                     {
                         _writeListener.onWritePossible();
@@ -827,31 +1000,50 @@
                     }
                     catch (Throwable e)
                     {
-                        _onError=e;
+                        _onError = e;
                     }
                     break;
-                    
+
                 default:
-                    _onError=new IllegalStateException("state="+_state.get());
+                    _onError = new IllegalStateException("state=" + _state.get());
             }
         }
     }
-    
+
+    private void close(Closeable resource)
+    {
+        try
+        {
+            resource.close();
+        }
+        catch (Throwable x)
+        {
+            LOG.ignore(x);
+        }
+    }
+
     @Override
     public String toString()
     {
-        return String.format("%s@%x{%s}",this.getClass().getSimpleName(),hashCode(),_state.get());
+        return String.format("%s@%x{%s}", this.getClass().getSimpleName(), hashCode(), _state.get());
     }
-    
+
     private abstract class AsyncICB extends IteratingCallback
     {
+        final boolean _last;
+
+        AsyncICB(boolean last)
+        {
+            _last = last;
+        }
+
         @Override
         protected void onCompleteSuccess()
         {
-            while(true)
+            while (true)
             {
-                OutputState last=_state.get();
-                switch(last)
+                OutputState last = _state.get();
+                switch (last)
                 {
                     case PENDING:
                         if (!_state.compareAndSet(OutputState.PENDING, OutputState.ASYNC))
@@ -861,7 +1053,10 @@
                     case UNREADY:
                         if (!_state.compareAndSet(OutputState.UNREADY, OutputState.READY))
                             continue;
-                        _channel.getState().onWritePossible();
+                        if (_last)
+                            closed();
+                        if (_channel.getState().onWritePossible())
+                            _channel.execute(_channel);
                         break;
 
                     case CLOSED:
@@ -877,18 +1072,19 @@
         @Override
         public void onCompleteFailure(Throwable e)
         {
-            _onError=e==null?new IOException():e;
-            _channel.getState().onWritePossible();
+            _onError = e == null ? new IOException() : e;
+            if (_channel.getState().onWritePossible())
+                _channel.execute(_channel);
         }
     }
-    
-    
+
     private class AsyncFlush extends AsyncICB
     {
         protected volatile boolean _flushed;
 
         public AsyncFlush()
         {
+            super(false);
         }
 
         @Override
@@ -896,15 +1092,15 @@
         {
             if (BufferUtil.hasContent(_aggregate))
             {
-                _flushed=true;
+                _flushed = true;
                 write(_aggregate, false, this);
                 return Action.SCHEDULED;
             }
 
             if (!_flushed)
             {
-                _flushed=true;
-                write(BufferUtil.EMPTY_BUFFER,false,this);
+                _flushed = true;
+                write(BufferUtil.EMPTY_BUFFER, false, this);
                 return Action.SCHEDULED;
             }
 
@@ -912,32 +1108,34 @@
         }
     }
 
-
-
     private class AsyncWrite extends AsyncICB
     {
         private final ByteBuffer _buffer;
         private final ByteBuffer _slice;
-        private final boolean _complete;
         private final int _len;
         protected volatile boolean _completed;
 
-        public AsyncWrite(byte[] b, int off, int len, boolean complete)
+        public AsyncWrite(byte[] b, int off, int len, boolean last)
         {
-            _buffer=ByteBuffer.wrap(b, off, len);
-            _len=len;
+            super(last);
+            _buffer = ByteBuffer.wrap(b, off, len);
+            _len = len;
             // always use a view for large byte arrays to avoid JVM pooling large direct buffers
-            _slice=_len<getBufferSize()?null:_buffer.duplicate();
-            _complete=complete;
+            _slice = _len < getBufferSize() ? null : _buffer.duplicate();
         }
 
-        public AsyncWrite(ByteBuffer buffer, boolean complete)
+        public AsyncWrite(ByteBuffer buffer, boolean last)
         {
-            _buffer=buffer;
-            _len=buffer.remaining();
+            super(last);
+            _buffer = buffer;
+            _len = buffer.remaining();
             // Use a slice buffer for large indirect to avoid JVM pooling large direct buffers
-            _slice=_buffer.isDirect()||_len<getBufferSize()?null:_buffer.duplicate();
-            _complete=complete;
+            if (_buffer.isDirect() || _len < getBufferSize())
+                _slice = null;
+            else
+            {
+                _slice = _buffer.duplicate();
+            }
         }
 
         @Override
@@ -946,69 +1144,60 @@
             // flush any content from the aggregate
             if (BufferUtil.hasContent(_aggregate))
             {
-                _completed=_len==0;
-                write(_aggregate, _complete && _completed, this);
+                _completed = _len == 0;
+                write(_aggregate, _last && _completed, this);
                 return Action.SCHEDULED;
             }
 
             // Can we just aggregate the remainder?
-            if (!_complete && _len<BufferUtil.space(_aggregate) && _len<_commitSize)
+            if (!_last && _len < BufferUtil.space(_aggregate) && _len < _commitSize)
             {
                 int position = BufferUtil.flipToFill(_aggregate);
-                BufferUtil.put(_buffer,_aggregate);
+                BufferUtil.put(_buffer, _aggregate);
                 BufferUtil.flipToFlush(_aggregate, position);
                 return Action.SUCCEEDED;
             }
-            
+
             // Is there data left to write?
             if (_buffer.hasRemaining())
             {
                 // if there is no slice, just write it
-                if (_slice==null)
+                if (_slice == null)
                 {
-                    _completed=true;
-                    write(_buffer, _complete, this);
+                    _completed = true;
+                    write(_buffer, _last, this);
                     return Action.SCHEDULED;
                 }
-                
+
                 // otherwise take a slice
-                int p=_buffer.position();
-                int l=Math.min(getBufferSize(),_buffer.remaining());
-                int pl=p+l;
+                int p = _buffer.position();
+                int l = Math.min(getBufferSize(), _buffer.remaining());
+                int pl = p + l;
                 _slice.limit(pl);
                 _buffer.position(pl);
                 _slice.position(p);
-                _completed=!_buffer.hasRemaining();
-                write(_slice, _complete && _completed, this);
+                _completed = !_buffer.hasRemaining();
+                write(_slice, _last && _completed, this);
                 return Action.SCHEDULED;
             }
-            
+
             // all content written, but if we have not yet signal completion, we
             // need to do so
-            if (_complete && !_completed)
+            if (_last && !_completed)
             {
-                _completed=true;
-                write(BufferUtil.EMPTY_BUFFER, _complete, this);
+                _completed = true;
+                write(BufferUtil.EMPTY_BUFFER, true, this);
                 return Action.SCHEDULED;
             }
 
+            if (LOG.isDebugEnabled() && _completed)
+                LOG.debug("EOF of {}", this);
             return Action.SUCCEEDED;
         }
-
-        @Override
-        protected void onCompleteSuccess()
-        {
-            super.onCompleteSuccess();
-            if (_complete)
-                closed();
-        }
-        
-        
     }
 
-
-    /* ------------------------------------------------------------ */
-    /** An iterating callback that will take content from an
+    /**
+     * An iterating callback that will take content from an
      * InputStream and write it to the associated {@link HttpChannel}.
      * A non direct buffer of size {@link HttpOutput#getBufferSize()} is used.
      * This callback is passed to the {@link HttpChannel#write(ByteBuffer, boolean, Callback)} to
@@ -1024,7 +1213,7 @@
         public InputStreamWritingCB(InputStream in, Callback callback)
         {
             super(callback);
-            _in=in;
+            _in = in;
             _buffer = _channel.getByteBufferPool().acquire(getBufferSize(), false);
         }
 
@@ -1035,50 +1224,45 @@
             // a write done with EOF=true
             if (_eof)
             {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("EOF of {}", this);
                 // Handle EOF
                 _in.close();
                 closed();
                 _channel.getByteBufferPool().release(_buffer);
                 return Action.SUCCEEDED;
             }
-            
+
             // Read until buffer full or EOF
-            int len=0;
-            while (len<_buffer.capacity() && !_eof)
+            int len = 0;
+            while (len < _buffer.capacity() && !_eof)
             {
-                int r=_in.read(_buffer.array(),_buffer.arrayOffset()+len,_buffer.capacity()-len);
-                if (r<0)
-                    _eof=true;
+                int r = _in.read(_buffer.array(), _buffer.arrayOffset() + len, _buffer.capacity() - len);
+                if (r < 0)
+                    _eof = true;
                 else
-                    len+=r;
+                    len += r;
             }
 
             // write what we have
             _buffer.position(0);
             _buffer.limit(len);
-            write(_buffer,_eof,this);
+            write(_buffer, _eof, this);
             return Action.SCHEDULED;
         }
 
         @Override
         public void onCompleteFailure(Throwable x)
         {
-            super.onCompleteFailure(x);
+            abort(x);
             _channel.getByteBufferPool().release(_buffer);
-            try
-            {
-                _in.close();
-            }
-            catch (IOException e)
-            {
-                LOG.ignore(e);
-            }
+            HttpOutput.this.close(_in);
+            super.onCompleteFailure(x);
         }
-
     }
 
-    /* ------------------------------------------------------------ */
-    /** An iterating callback that will take content from a
+    /**
+     * An iterating callback that will take content from a
      * ReadableByteChannel and write it to the {@link HttpChannel}.
      * A {@link ByteBuffer} of size {@link HttpOutput#getBufferSize()} is used that will be direct if
      * {@link HttpChannel#useDirectBuffers()} is true.
@@ -1095,7 +1279,7 @@
         public ReadableByteChannelWritingCB(ReadableByteChannel in, Callback callback)
         {
             super(callback);
-            _in=in;
+            _in = in;
             _buffer = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.useDirectBuffers());
         }
 
@@ -1106,20 +1290,22 @@
             // a write done with EOF=true
             if (_eof)
             {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("EOF of {}", this);
                 _in.close();
                 closed();
                 _channel.getByteBufferPool().release(_buffer);
                 return Action.SUCCEEDED;
             }
-            
+
             // Read from stream until buffer full or EOF
-            _buffer.clear();
+            BufferUtil.clearToFill(_buffer);
             while (_buffer.hasRemaining() && !_eof)
-              _eof = (_in.read(_buffer)) <  0;
+                _eof = (_in.read(_buffer)) < 0;
 
             // write what we have
-            _buffer.flip();
-            write(_buffer,_eof,this);
+            BufferUtil.flipToFlush(_buffer, 0);
+            write(_buffer, _eof, this);
 
             return Action.SCHEDULED;
         }
@@ -1127,16 +1313,29 @@
         @Override
         public void onCompleteFailure(Throwable x)
         {
-            super.onCompleteFailure(x);
+            abort(x);
             _channel.getByteBufferPool().release(_buffer);
-            try
-            {
-                _in.close();
-            }
-            catch (IOException e)
-            {
-                LOG.ignore(e);
-            }
+            HttpOutput.this.close(_in);
+            super.onCompleteFailure(x);
+        }
+    }
+
+    private static class WriteBlocker extends SharedBlockingCallback
+    {
+        private final HttpChannel _channel;
+
+        private WriteBlocker(HttpChannel channel)
+        {
+            _channel = channel;
+        }
+
+        @Override
+        protected long getIdleTimeout()
+        {
+            long blockingTimeout = _channel.getHttpConfiguration().getBlockingTimeout();
+            if (blockingTimeout == 0)
+                return _channel.getIdleTimeout();
+            return blockingTimeout;
         }
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java
index 12b5820..bdb3a9d 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java
@@ -20,21 +20,60 @@
 
 import java.nio.ByteBuffer;
 
-import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.http.MetaData;
 import org.eclipse.jetty.util.Callback;
 
-public interface HttpTransport
-{    
-    void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback);
 
-    void send(ByteBuffer content, boolean lastContent, Callback callback);
-    
-    void completed();
-    
-    /* ------------------------------------------------------------ */
-    /** Abort transport.
-     * This is called when an error response needs to be sent, but the response is already committed.
-     * Abort to should terminate the transport in a way that can indicate abnormal response to the client. 
+/* ------------------------------------------------------------ */
+/** Abstraction of the outbound HTTP transport.
+ */
+public interface HttpTransport
+{
+    /** Asynchronous call to send a response (or part) over the transport
+     * @param info The header info to send, or null if just sending more data.
+     *             The first call to send for a response must have a non null info.
+     * @param head True if the response if for a HEAD request (and the data should not be sent).
+     * @param content A buffer of content to be sent.
+     * @param lastContent True if the content is the last content for the current response.
+     * @param callback The Callback instance that success or failure of the send is notified on
      */
-    void abort();
+    void send(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback);
+
+    /**
+     * @return true if responses can be pushed over this transport
+     */
+    boolean isPushSupported();
+
+    /**
+     * @param request A request to use as the basis for generating a pushed response.
+     */
+    void push(MetaData.Request request);
+
+    /**
+     * Called to indicated the end of the current request/response cycle (which may be
+     * some time after the last content is sent).
+     */
+    void onCompleted();
+    
+    /**
+     * Aborts this transport.
+     * <p>
+     * This method should terminate the transport in a way that
+     * can indicate an abnormal response to the client, for example
+     * by abruptly close the connection.
+     * <p>
+     * This method is called when an error response needs to be sent,
+     * but the response is already committed, or when a write failure
+     * is detected.  If abort is called, {@link #onCompleted()} is not
+     * called
+     *
+     * @param failure the failure that caused the abort.
+     */
+    void abort(Throwable failure);
+
+    /* ------------------------------------------------------------ */
+    /** Is the underlying transport optimized for DirectBuffer usage
+     * @return True if direct buffers can be used optimally.
+     */
+    boolean isOptimizedForDirectBuffers();
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java
index 5cd8ef7..7788ec4 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java
@@ -27,18 +27,31 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpParser;
+import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.io.ByteArrayEndPoint;
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.ByteArrayOutputStream2;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.Scheduler;
 
+/**
+ * A local connector, mostly for testing purposes.
+ * <pre>
+ *  HttpTester.Request request = HttpTester.newRequest();
+ *  request.setURI("/some/resource");
+ *  HttpTester.Response response = 
+ *      HttpTester.parseResponse(HttpTester.from(localConnector.getResponse(request.generate())));
+ * </pre>
+ *
+ */
 public class LocalConnector extends AbstractConnector
 {
     private final BlockingQueue<LocalEndPoint> _connects = new LinkedBlockingQueue<>();
 
-
     public LocalConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, ConnectionFactory... factories)
     {
         super(server,executor,scheduler,pool,acceptors,factories);
@@ -76,7 +89,8 @@
      * returned to the level it was before the requests.
      * <p>
      * This methods waits until the connection is closed or
-     * is idle for 1s before returning the responses.
+     * is idle for 5s before returning the responses.
+     * <p>Use {@link #getResponse(String)} for an alternative that does not wait for idle.
      * @param requests the requests
      * @return the responses
      * @throws Exception if the requests fail
@@ -92,6 +106,7 @@
      * <p>
      * This methods waits until the connection is closed or
      * an idle period before returning the responses.
+     * <p>Use {@link #getResponse(String)} for an alternative that does not wait for idle.
      * @param requests the requests
      * @param idleFor The time the response stream must be idle for before returning
      * @param units The units of idleFor
@@ -109,7 +124,8 @@
      * returned to the level it was before the requests.
      * <p>
      * This methods waits until the connection is closed or
-     * is idle for 1s before returning the responses.
+     * is idle for 5s before returning the responses.
+     * <p>Use {@link #getResponse(ByteBuffer)} for an alternative that does not wait for idle.
      * @param requestsBuffer the requests
      * @return the responses
      * @throws Exception if the requests fail
@@ -147,7 +163,7 @@
 
     /**
      * Execute a request and return the EndPoint through which
-     * responses can be received.
+     * multiple responses can be received or more input provided.
      * @param rawRequest the request
      * @return the local endpoint
      */
@@ -161,11 +177,18 @@
         if (!isStarted())
             throw new IllegalStateException("!STARTED");
         LocalEndPoint endp = new LocalEndPoint();
-        endp.setInput(rawRequest);
+        endp.addInput(rawRequest);
         _connects.add(endp);
         return endp;
     }
 
+    public LocalEndPoint connect()
+    {
+        LocalEndPoint endp = new LocalEndPoint();
+        _connects.add(endp);
+        return endp;
+    }
+    
     @Override
     protected void accept(int acceptorID) throws IOException, InterruptedException
     {
@@ -181,22 +204,113 @@
         connection.onOpen();
     }
 
+
+    /** Get a single response using a parser to search for the end of the message.
+     * @param requestsBuffer The request to send
+     * @return ByteBuffer containing response or null.
+     * @throws Exception If there is a problem
+     */
+    public ByteBuffer getResponse(ByteBuffer requestsBuffer) throws Exception
+    {
+        return getResponse(requestsBuffer,false,10,TimeUnit.SECONDS);
+    }
+
+    /** Get a single response using a parser to search for the end of the message.
+     * @param requestBuffer The request to send
+     * @param time The time to wait
+     * @param unit The units of the wait
+     * @return ByteBuffer containing response or null.
+     * @throws Exception If there is a problem
+     */
+    public ByteBuffer getResponse(ByteBuffer requestBuffer, long time,TimeUnit unit) throws Exception
+    {
+        boolean head = BufferUtil.toString(requestBuffer).toLowerCase().startsWith("head ");
+        if (LOG.isDebugEnabled())
+            LOG.debug("requests {}", BufferUtil.toUTF8String(requestBuffer));
+        LocalEndPoint endp = executeRequest(requestBuffer);
+        return endp.waitForResponse(head,time,unit);
+    }
+    
+    /** Get a single response using a parser to search for the end of the message.
+     * @param requestBuffer The request to send
+     * @param head True if the response is for a head request
+     * @param time The time to wait
+     * @param unit The units of the wait
+     * @return ByteBuffer containing response or null.
+     * @throws Exception If there is a problem
+     */
+    public ByteBuffer getResponse(ByteBuffer requestBuffer,boolean head, long time,TimeUnit unit) throws Exception
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("requests {}", BufferUtil.toUTF8String(requestBuffer));
+        LocalEndPoint endp = executeRequest(requestBuffer);
+        return endp.waitForResponse(head,time,unit);
+    }
+
+    
+    /** Get a single response using a parser to search for the end of the message.
+     * @param rawRequest The request to send
+     * @return ByteBuffer containing response or null.
+     * @throws Exception If there is a problem
+     */
+    public String getResponse(String rawRequest) throws Exception
+    {
+        return getResponse(rawRequest,false,30,TimeUnit.SECONDS);
+    }
+
+    /** Get a single response using a parser to search for the end of the message.
+     * @param rawRequest The request to send
+     * @param time The time to wait
+     * @param unit The units of the wait
+     * @return ByteBuffer containing response or null.
+     * @throws Exception If there is a problem
+     */
+    public String getResponse(String rawRequest,long time,TimeUnit unit) throws Exception
+    {
+        boolean head = rawRequest.toLowerCase().startsWith("head ");
+        ByteBuffer requestsBuffer = BufferUtil.toBuffer(rawRequest, StandardCharsets.ISO_8859_1);
+        if (LOG.isDebugEnabled())
+            LOG.debug("request {}", BufferUtil.toUTF8String(requestsBuffer));
+        LocalEndPoint endp = executeRequest(requestsBuffer);
+        
+        return BufferUtil.toString(endp.waitForResponse(head,time,unit), StandardCharsets.ISO_8859_1);
+    }
+    
+    
+    /** Get a single response using a parser to search for the end of the message.
+     * @param rawRequest The request to send
+     * @param head True if the response is for a head request
+     * @param time The time to wait
+     * @param unit The units of the wait
+     * @return ByteBuffer containing response or null.
+     * @throws Exception If there is a problem
+     */
+    public String getResponse(String rawRequest, boolean head, long time,TimeUnit unit) throws Exception
+    {
+        ByteBuffer requestsBuffer = BufferUtil.toBuffer(rawRequest, StandardCharsets.ISO_8859_1);
+        if (LOG.isDebugEnabled())
+            LOG.debug("request {}", BufferUtil.toUTF8String(requestsBuffer));
+        LocalEndPoint endp = executeRequest(requestsBuffer);
+        
+        return BufferUtil.toString(endp.waitForResponse(head,time,unit), StandardCharsets.ISO_8859_1);
+    }
+    
+    /** Local EndPoint
+     */
     public class LocalEndPoint extends ByteArrayEndPoint
     {
         private final CountDownLatch _closed = new CountDownLatch(1);
+        private ByteBuffer _responseData;
 
         public LocalEndPoint()
         {
-            super(getScheduler(), LocalConnector.this.getIdleTimeout());
+            super(LocalConnector.this.getScheduler(), LocalConnector.this.getIdleTimeout());
             setGrowOutput(true);
         }
-
-        public void addInput(String s)
+        
+        protected void execute(Runnable task)
         {
-            // TODO this is a busy wait
-            while(getIn()==null || BufferUtil.hasContent(getIn()))
-                Thread.yield();
-            setInput(BufferUtil.toBuffer(s, StandardCharsets.UTF_8));
+            getExecutor().execute(task);
         }
 
         @Override
@@ -206,7 +320,6 @@
             super.close();
             if (wasOpen)
             {
-//                connectionClosed(getConnection());
                 getConnection().onClose();
                 onClose();
             }
@@ -268,5 +381,148 @@
                 }
             }
         }
+        
+        /** 
+         * Wait for a response using a parser to detect the end of message
+         * @return Buffer containing full response or null for EOF;
+         * @throws Exception if the response cannot be parsed
+         */
+        public String getResponse() throws Exception
+        {
+            return getResponse(false,30,TimeUnit.SECONDS);
+        }
+        
+        /** 
+         * Wait for a response using a parser to detect the end of message
+         * @param head whether the request is a HEAD request
+         * @param time the maximum time to wait
+         * @param unit the time unit of the {@code timeout} argument
+         * @return Buffer containing full response or null for EOF;
+         * @throws Exception if the response cannot be parsed
+         */
+        public String getResponse(boolean head, long time,TimeUnit unit) throws Exception
+        {
+            ByteBuffer response = waitForResponse(head,time,unit);
+            if (response!=null)
+                return BufferUtil.toString(response);
+            return null;
+        }
+        
+        /** 
+         * Wait for a response using a parser to detect the end of message
+         * @param head whether the request is a HEAD request
+         * @param time the maximum time to wait
+         * @param unit the time unit of the {@code timeout} argument
+         * @return Buffer containing full response or null for EOF;
+         * @throws Exception if the response cannot be parsed
+         */
+        public ByteBuffer waitForResponse(boolean head, long time,TimeUnit unit) throws Exception
+        {
+            HttpParser.ResponseHandler handler = new HttpParser.ResponseHandler()
+            {
+                @Override
+                public void parsedHeader(HttpField field)
+                {
+                }
+
+                @Override
+                public boolean contentComplete()
+                {
+                    return false;
+                }
+                
+                @Override
+                public boolean messageComplete()
+                {
+                    return true;
+                }
+                
+                @Override
+                public boolean headerComplete()
+                {
+                    return false;
+                }
+                
+                @Override
+                public int getHeaderCacheSize()
+                {
+                    return 0;
+                }
+                
+                @Override
+                public void earlyEOF()
+                {                
+                }
+                
+                @Override
+                public boolean content(ByteBuffer item)
+                {
+                    return false;
+                }
+                
+                @Override
+                public void badMessage(int status, String reason)
+                {
+                }
+                
+                @Override
+                public boolean startResponse(HttpVersion version, int status, String reason)
+                {
+                    return false;
+                }
+            };
+            
+            HttpParser parser = new HttpParser(handler);
+            parser.setHeadResponse(head);
+            try(ByteArrayOutputStream2 bout = new ByteArrayOutputStream2();)
+            {
+                loop: while(true)
+                {
+                    // read a chunk of response
+                    ByteBuffer chunk;
+                    if (BufferUtil.hasContent(_responseData))
+                        chunk = _responseData;
+                    else 
+                    {
+                        chunk = waitForOutput(time,unit);
+                        if (BufferUtil.isEmpty(chunk) && (!isOpen() || isOutputShutdown()))
+                        {
+                            parser.atEOF();
+                            parser.parseNext(BufferUtil.EMPTY_BUFFER);
+                            break loop;
+                        }
+                    }
+                    
+                    // Parse the content of this chunk
+                    while (BufferUtil.hasContent(chunk))
+                    {
+                        int pos=chunk.position();
+                        boolean complete=parser.parseNext(chunk);
+                        if (chunk.position()==pos)
+                        {
+                            // Nothing consumed
+                            if (BufferUtil.isEmpty(chunk))
+                                break;
+                            return null;
+                        }
+
+                        // Add all consumed bytes to the output stream
+                        bout.write(chunk.array(),chunk.arrayOffset()+pos,chunk.position()-pos);
+
+                        // If we are complete then break the outer loop
+                        if (complete)
+                        {
+                            if (BufferUtil.hasContent(chunk))
+                                _responseData=chunk;
+                            break loop;
+                        }
+                    }
+                }
+            
+                if (bout.getCount()==0 && isOutputShutdown())
+                    return null;
+                return ByteBuffer.wrap(bout.getBuf(),0,bout.getCount()); 
+            }
+        }
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/LowResourceMonitor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/LowResourceMonitor.java
index c91a8bf..5a0b9ea 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/LowResourceMonitor.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/LowResourceMonitor.java
@@ -37,11 +37,14 @@
 import org.eclipse.jetty.util.thread.ThreadPool;
 
 
-/* ------------------------------------------------------------ */
-/** A monitor for low resources
- * <p>An instance of this class will monitor all the connectors of a server (or a set of connectors
+/**
+ * A monitor for low resources
+ * <p>
+ * An instance of this class will monitor all the connectors of a server (or a set of connectors
  * configured with {@link #setMonitoredConnectors(Collection)}) for a low resources state.
- * Low resources can be detected by:<ul>
+ * <p>
+ * Low resources can be detected by:
+ * <ul>
  * <li>{@link ThreadPool#isLowOnThreads()} if {@link Connector#getExecutor()} is
  * an instance of {@link ThreadPool} and {@link #setMonitorThreads(boolean)} is true.<li>
  * <li>If {@link #setMaxMemory(long)} is non zero then low resources is detected if the JVMs
@@ -50,14 +53,13 @@
  * <li>If {@link #setMaxConnections(int)} is non zero then low resources is dected if the total number
  * of connections exceeds {@link #getMaxConnections()}</li>
  * </ul>
- * </p>
- * <p>Once low resources state is detected, the cause is logged and all existing connections returned
+ * <p>
+ * Once low resources state is detected, the cause is logged and all existing connections returned
  * by {@link Connector#getConnectedEndPoints()} have {@link EndPoint#setIdleTimeout(long)} set
  * to {@link #getLowResourcesIdleTimeout()}.  New connections are not affected, however if the low
  * resources state persists for more than {@link #getMaxLowResourcesTime()}, then the
  * {@link #getLowResourcesIdleTimeout()} to all connections again.  Once the low resources state is
  * cleared, the idle timeout is reset to the connector default given by {@link Connector#getIdleTimeout()}.
- * </p>
  */
 @ManagedObject ("Monitor for low resource conditions and activate a low resource mode if detected")
 public class LowResourceMonitor extends AbstractLifeCycle
@@ -77,7 +79,6 @@
     private String _reasons;
     private long _lowStarted;
 
-
     private final Runnable _monitor = new Runnable()
     {
         @Override
@@ -254,17 +255,24 @@
         String cause="";
         int connections=0;
 
+        ThreadPool serverThreads = _server.getThreadPool();
+        if (_monitorThreads && serverThreads.isLowOnThreads())
+        {
+            reasons=low(reasons,"Server low on threads: "+serverThreads);
+            cause+="S";
+        }
+
         for(Connector connector : getMonitoredOrServerConnectors())
         {
             connections+=connector.getConnectedEndPoints().size();
 
             Executor executor = connector.getExecutor();
-            if (executor instanceof ThreadPool)
+            if (executor instanceof ThreadPool && executor!=serverThreads)
             {
-                ThreadPool threadpool=(ThreadPool) executor;
-                if (_monitorThreads && threadpool.isLowOnThreads())
+                ThreadPool connectorThreads=(ThreadPool)executor;
+                if (_monitorThreads && connectorThreads.isLowOnThreads())
                 {
-                    reasons=low(reasons,"Low on threads: "+threadpool);
+                    reasons=low(reasons,"Connector low on threads: "+connectorThreads);
                     cause+="T";
                 }
             }
@@ -283,7 +291,6 @@
             cause+="M";
         }
 
-
         if (reasons!=null)
         {
             // Log the reasons if there is any change in the cause
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartCleanerListener.java b/jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartCleanerListener.java
new file mode 100644
index 0000000..c06d374
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartCleanerListener.java
@@ -0,0 +1,67 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.MultiException;
+import org.eclipse.jetty.util.MultiPartInputStreamParser;
+
+public class MultiPartCleanerListener implements ServletRequestListener
+{
+    public final static MultiPartCleanerListener INSTANCE = new MultiPartCleanerListener();
+    
+    protected MultiPartCleanerListener()
+    {
+    }
+    
+    @Override
+    public void requestDestroyed(ServletRequestEvent sre)
+    {
+        //Clean up any tmp files created by MultiPartInputStream
+        MultiPartInputStreamParser mpis = (MultiPartInputStreamParser)sre.getServletRequest().getAttribute(Request.__MULTIPART_INPUT_STREAM);
+        if (mpis != null)
+        {
+            ContextHandler.Context context = (ContextHandler.Context)sre.getServletRequest().getAttribute(Request.__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()
+    }
+    
+}
\ No newline at end of file
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java
index 7067e55..42d928c 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java
@@ -38,7 +38,7 @@
  * formats.
  */
 @ManagedObject("NCSA standard format request log")
-public class NCSARequestLog extends AbstractNCSARequestLog implements RequestLog
+public class NCSARequestLog extends AbstractNCSARequestLog
 {
     private String _filename;
     private boolean _append;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnection.java
index b133209..cb45294 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnection.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnection.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.server;
 
 import java.io.IOException;
-import java.nio.ByteBuffer;
 import java.util.List;
 
 import javax.net.ssl.SSLEngine;
@@ -36,6 +35,11 @@
 {
     private static final Logger LOG = Log.getLogger(NegotiatingServerConnection.class);
 
+    public interface CipherDiscriminator
+    {
+        boolean isAcceptable(String protocol, String tlsProtocol, String tlsCipher);
+    }
+    
     private final Connector connector;
     private final SSLEngine engine;
     private final List<String> protocols;
@@ -61,6 +65,11 @@
         return defaultProtocol;
     }
 
+    protected Connector getConnector()
+    {
+        return connector;
+    }
+    
     protected SSLEngine getSSLEngine()
     {
         return engine;
@@ -111,9 +120,8 @@
                 ConnectionFactory connectionFactory = connector.getConnectionFactory(protocol);
                 if (connectionFactory == null)
                 {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("{} application selected protocol '{}', but no correspondent {} has been configured",
-                            this, protocol, ConnectionFactory.class.getName());
+                    LOG.info("{} application selected protocol '{}', but no correspondent {} has been configured",
+                             this, protocol, ConnectionFactory.class.getName());
                     close();
                 }
                 else
@@ -128,7 +136,7 @@
         {
             // Something went bad, we need to close.
             if (LOG.isDebugEnabled())
-                LOG.debug("{} closing on client close", this);
+                LOG.debug("{} detected close on client side", this);
             close();
         }
         else
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnectionFactory.java
index 9c87081..2fbbb64 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnectionFactory.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnectionFactory.java
@@ -18,27 +18,60 @@
 
 package org.eclipse.jetty.server;
 
-import java.util.Arrays;
-import java.util.Iterator;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
+
 import javax.net.ssl.SSLEngine;
 
+import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.io.ssl.SslConnection;
-import org.eclipse.jetty.server.AbstractConnectionFactory;
-import org.eclipse.jetty.server.Connector;
 
 public abstract class NegotiatingServerConnectionFactory extends AbstractConnectionFactory
 {
-    private final List<String> protocols;
+    public static void checkProtocolNegotiationAvailable()
+    {
+        try
+        {
+            String javaVersion = System.getProperty("java.version");
+            String alpnClassName = "org.eclipse.jetty.alpn.ALPN";
+            if (javaVersion.startsWith("1."))
+            {
+                Class<?> klass = ClassLoader.getSystemClassLoader().loadClass(alpnClassName);
+                if (klass.getClassLoader() != null)
+                    throw new IllegalStateException(alpnClassName + " must be on JVM boot classpath");
+            }
+            else
+            {
+                NegotiatingServerConnectionFactory.class.getClassLoader().loadClass(alpnClassName);
+            }
+        }
+        catch (ClassNotFoundException x)
+        {
+            throw new IllegalStateException("No ALPN classes available");
+        }
+    }
+
+    private final List<String> negotiatedProtocols;
     private String defaultProtocol;
 
-    public NegotiatingServerConnectionFactory(String protocol, String... protocols)
+    public NegotiatingServerConnectionFactory(String protocol, String... negotiatedProtocols)
     {
         super(protocol);
-        this.protocols = Arrays.asList(protocols);
+        this.negotiatedProtocols = new ArrayList<>();
+        if (negotiatedProtocols != null)
+        {
+            // Trim the values, as they may come from XML configuration.
+            for (String p : negotiatedProtocols)
+            {
+                p = p.trim();
+                if (!p.isEmpty())
+                    this.negotiatedProtocols.add(p.trim());
+            }
+        }
     }
 
     public String getDefaultProtocol()
@@ -48,36 +81,42 @@
 
     public void setDefaultProtocol(String defaultProtocol)
     {
-        this.defaultProtocol = defaultProtocol;
+        // Trim the value, as it may come from XML configuration.
+        String dft = defaultProtocol == null ? "" : defaultProtocol.trim();
+        this.defaultProtocol = dft.isEmpty() ? null : dft;
     }
 
-    public List<String> getProtocols()
+    public List<String> getNegotiatedProtocols()
     {
-        return protocols;
+        return negotiatedProtocols;
     }
-
+    
     @Override
     public Connection newConnection(Connector connector, EndPoint endPoint)
     {
-        List<String> protocols = this.protocols;
-        if (protocols.isEmpty())
+        List<String> negotiated = this.negotiatedProtocols;
+        if (negotiated.isEmpty())
         {
-            protocols = connector.getProtocols();
-            Iterator<String> i = protocols.iterator();
-            while (i.hasNext())
+            // Generate list of protocols that we can negotiate
+            negotiated = connector.getProtocols().stream()
+            .filter(p->
             {
-                String protocol = i.next();
-                String prefix = "ssl-";
-                if (protocol.regionMatches(true, 0, prefix, 0, prefix.length()) || protocol.equalsIgnoreCase("alpn"))
-                {
-                    i.remove();
-                }
-            }
+                ConnectionFactory f=connector.getConnectionFactory(p);
+                return !(f instanceof SslConnectionFactory)&&!(f instanceof NegotiatingServerConnectionFactory);
+            })
+            .collect(Collectors.toList());            
         }
 
+        // if default protocol is not set, then it is either HTTP/1.1 or 
+        // the first protocol given
         String dft = defaultProtocol;
-        if (dft == null && !protocols.isEmpty())
-            dft = protocols.get(0);
+        if (dft == null && !negotiated.isEmpty())
+        {
+            if (negotiated.contains(HttpVersion.HTTP_1_1.asString()))
+                dft = HttpVersion.HTTP_1_1.asString();
+            else
+                dft = negotiated.get(0);
+        }
 
         SSLEngine engine = null;
         EndPoint ep = endPoint;
@@ -90,7 +129,7 @@
                 ep = null;
         }
 
-        return configure(newServerConnection(connector, endPoint, engine, protocols, dft), connector, endPoint);
+        return configure(newServerConnection(connector, endPoint, engine, negotiated, dft), connector, endPoint);
     }
 
     protected abstract AbstractConnection newServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol);
@@ -98,6 +137,6 @@
     @Override
     public String toString()
     {
-        return String.format("%s@%x{%s,%s,%s}", getClass().getSimpleName(), hashCode(), getProtocol(), getDefaultProtocol(), getProtocols());
+        return String.format("%s@%x{%s,%s,%s}", getClass().getSimpleName(), hashCode(), getProtocols(), getDefaultProtocol(), getNegotiatedProtocols());
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java
index e9e877b..dfc431c 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java
@@ -26,10 +26,10 @@
 import java.util.concurrent.Executor;
 
 import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.ManagedSelector;
 import org.eclipse.jetty.io.NetworkTrafficListener;
 import org.eclipse.jetty.io.NetworkTrafficSelectChannelEndPoint;
 import org.eclipse.jetty.io.SelectChannelEndPoint;
-import org.eclipse.jetty.io.SelectorManager;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.Scheduler;
 
@@ -84,7 +84,7 @@
     }
 
     @Override
-    protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectorManager.ManagedSelector selectSet, SelectionKey key) throws IOException
+    protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
     {
         NetworkTrafficSelectChannelEndPoint endPoint = new NetworkTrafficSelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout(), listeners);
         return endPoint;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java
new file mode 100644
index 0000000..a5ebbab
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java
@@ -0,0 +1,348 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.nio.ByteBuffer;
+import java.nio.channels.ReadPendingException;
+import java.nio.channels.WritePendingException;
+import java.util.Iterator;
+
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * ConnectionFactory for the PROXY Protocol.
+ * <p>This factory can be placed in front of any other connection factory
+ * to process the proxy line before the normal protocol handling</p>
+ *
+ * @see <a href="http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt">http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt</a>
+ */
+public class ProxyConnectionFactory extends AbstractConnectionFactory
+{
+    private static final Logger LOG = Log.getLogger(ProxyConnectionFactory.class);
+    private final String _next;
+
+    /* ------------------------------------------------------------ */
+    /** Proxy Connection Factory that uses the next ConnectionFactory
+     * on the connector as the next protocol
+     */
+    public ProxyConnectionFactory()
+    {
+        super("proxy");
+        _next=null;
+    }
+
+    public ProxyConnectionFactory(String nextProtocol)
+    {
+        super("proxy");
+        _next=nextProtocol;
+    }
+
+    @Override
+    public Connection newConnection(Connector connector, EndPoint endp)
+    {
+        String next=_next;
+        if (next==null)
+        {
+            for (Iterator<String> i = connector.getProtocols().iterator();i.hasNext();)
+            {
+                String p=i.next();
+                if (getProtocol().equalsIgnoreCase(p))
+                {
+                    next=i.next();
+                    break;
+                }
+            }
+        }
+
+        return new ProxyConnection(endp,connector,next);
+    }
+
+    public static class ProxyConnection extends AbstractConnection
+    {
+        // 0     1 2       3       4 5 6
+        // 98765432109876543210987654321
+        // PROXY P R.R.R.R L.L.L.L R Lrn
+
+        private final int[] __size = {29,23,21,13,5,3,1};
+        private final Connector _connector;
+        private final String _next;
+        private final StringBuilder _builder=new StringBuilder();
+        private final String[] _field=new String[6];
+        private int _fields;
+        private int _length;
+
+        protected ProxyConnection(EndPoint endp, Connector connector, String next)
+        {
+            super(endp,connector.getExecutor());
+            _connector=connector;
+            _next=next;
+        }
+
+        @Override
+        public void onOpen()
+        {
+            super.onOpen();
+            fillInterested();
+        }
+
+        @Override
+        public void onFillable()
+        {
+            try
+            {
+                ByteBuffer buffer=null;
+                loop: while(true)
+                {
+                    // Create a buffer that will not read too much data
+                    int size=Math.max(1,__size[_fields]-_builder.length());
+                    if (buffer==null || buffer.capacity()!=size)
+                        buffer=BufferUtil.allocate(size);
+                    else
+                        BufferUtil.clear(buffer);
+
+                    // Read data
+                    int fill=getEndPoint().fill(buffer);
+                    if (fill<0)
+                    {
+                        getEndPoint().shutdownOutput();
+                        return;
+                    }
+                    if (fill==0)
+                    {
+                        fillInterested();
+                        return;
+                    }
+
+                    _length+=fill;
+                    if (_length>=108)
+                    {
+                        LOG.warn("PROXY line too long {} for {}",_length,getEndPoint());
+                        close();
+                        return;
+                    }
+
+                    // parse fields
+                    while (buffer.hasRemaining())
+                    {
+                        byte b = buffer.get();
+                        if (_fields<6)
+                        {
+                            if (b==' ' || b=='\r' && _fields==5)
+                            {
+                                _field[_fields++]=_builder.toString();
+                                _builder.setLength(0);
+                            }
+                            else if (b<' ')
+                            {
+                                LOG.warn("Bad character {} for {}",b&0xFF,getEndPoint());
+                                close();
+                                return;
+                            }
+                            else
+                            {
+                                _builder.append((char)b);
+                            }
+                        }
+                        else
+                        {
+                            if (b=='\n')
+                                break loop;
+
+                            LOG.warn("Bad CRLF for {}",getEndPoint());
+                            close();
+                            return;
+                        }
+                    }
+                }
+
+                // Check proxy
+                if (!"PROXY".equals(_field[0]))
+                {
+                    LOG.warn("Not PROXY protocol for {}",getEndPoint());
+                    close();
+                    return;
+                }
+
+                // Extract Addresses
+                InetSocketAddress remote=new InetSocketAddress(_field[2],Integer.parseInt(_field[4]));
+                InetSocketAddress local =new InetSocketAddress(_field[3],Integer.parseInt(_field[5]));
+
+                // Create the next protocol
+                ConnectionFactory connectionFactory = _connector.getConnectionFactory(_next);
+                if (connectionFactory == null)
+                {
+                    LOG.info("Next protocol '{}' for {}",_next,getEndPoint());
+                    close();
+                    return;
+                }
+
+                EndPoint endPoint = new ProxyEndPoint(getEndPoint(),remote,local);
+                Connection newConnection = connectionFactory.newConnection(_connector, endPoint);
+                endPoint.upgrade(newConnection);
+            }
+            catch (Throwable x)
+            {
+                LOG.warn("PROXY error for "+getEndPoint(),x);
+                close();
+            }
+        }
+    }
+
+    public static class ProxyEndPoint implements EndPoint
+    {
+        private final EndPoint _endp;
+        private final InetSocketAddress _remote;
+        private final InetSocketAddress _local;
+
+        public ProxyEndPoint(EndPoint endp, InetSocketAddress remote, InetSocketAddress local)
+        {
+            _endp=endp;
+            _remote=remote;
+            _local=local;
+        }
+
+        @Override
+        public boolean isOptimizedForDirectBuffers()
+        {
+            return _endp.isOptimizedForDirectBuffers();
+        }
+
+        public InetSocketAddress getLocalAddress()
+        {
+            return _local;
+        }
+
+        public InetSocketAddress getRemoteAddress()
+        {
+            return _remote;
+        }
+
+        public boolean isOpen()
+        {
+            return _endp.isOpen();
+        }
+
+        public long getCreatedTimeStamp()
+        {
+            return _endp.getCreatedTimeStamp();
+        }
+
+        public void shutdownOutput()
+        {
+            _endp.shutdownOutput();
+        }
+
+        public boolean isOutputShutdown()
+        {
+            return _endp.isOutputShutdown();
+        }
+
+        public boolean isInputShutdown()
+        {
+            return _endp.isInputShutdown();
+        }
+
+        public void close()
+        {
+            _endp.close();
+        }
+
+        public int fill(ByteBuffer buffer) throws IOException
+        {
+            return _endp.fill(buffer);
+        }
+
+        public boolean flush(ByteBuffer... buffer) throws IOException
+        {
+            return _endp.flush(buffer);
+        }
+
+        public Object getTransport()
+        {
+            return _endp.getTransport();
+        }
+
+        public long getIdleTimeout()
+        {
+            return _endp.getIdleTimeout();
+        }
+
+        public void setIdleTimeout(long idleTimeout)
+        {
+            _endp.setIdleTimeout(idleTimeout);
+        }
+
+        public void fillInterested(Callback callback) throws ReadPendingException
+        {
+            _endp.fillInterested(callback);
+        }
+
+        public boolean tryFillInterested(Callback callback)
+        {
+            return _endp.tryFillInterested(callback);
+        }
+
+        @Override
+        public boolean isFillInterested()
+        {
+            return _endp.isFillInterested();
+        }
+
+        public void write(Callback callback, ByteBuffer... buffers) throws WritePendingException
+        {
+            _endp.write(callback,buffers);
+        }
+
+        public Connection getConnection()
+        {
+            return _endp.getConnection();
+        }
+
+        public void setConnection(Connection connection)
+        {
+            _endp.setConnection(connection);
+        }
+
+        public void onOpen()
+        {
+            _endp.onOpen();
+        }
+
+        public void onClose()
+        {
+            _endp.onClose();
+        }
+
+        @Override
+        public void upgrade(Connection newConnection)
+        {
+            _endp.upgrade(newConnection);
+        }
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java
new file mode 100644
index 0000000..6f585f3
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java
@@ -0,0 +1,185 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.Set;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+/** Build a request to be pushed.
+ * <p>
+ * A PushBuilder is obtained by calling {@link Request#getPushBuilder()}
+ * which creates an initializes the builder as follows:
+ * <ul>
+ * <li> Each call to getPushBuilder() will return a new instance of a 
+ * PushBuilder based off the Request.  Any mutations to the
+ * returned PushBuilder are not reflected on future returns.</li>
+ * <li>The method is initialized to "GET"</li>
+ * <li>The requests headers are added to the Builder, except for:<ul>
+ *   <li>Conditional headers (eg. If-Modified-Since)
+ *   <li>Range headers
+ *   <li>Expect headers
+ *   <li>Authorization headers
+ *   <li>Referrer headers
+ * </ul></li>
+ * <li>If the request was Authenticated, an Authorization header will 
+ * be set with a container generated token that will result in equivalent
+ * Authorization for the pushed request</li>
+ * <li>The query string from {@link HttpServletRequest#getQueryString()}
+ * <li>The {@link HttpServletRequest#getRequestedSessionId()} value, unless at the time
+ * of the call {@link HttpServletRequest#getSession(boolean)}
+ * has previously been called to create a new {@link HttpSession}, in 
+ * which case the new session ID will be used as the PushBuilders 
+ * requested session ID. The source of the requested session id will be the 
+ * same as for the request</li>
+ * <li>The Referer header will be set to {@link HttpServletRequest#getRequestURL()} 
+ * plus any {@link HttpServletRequest#getQueryString()} </li>
+ * <li>If {@link HttpServletResponse#addCookie(Cookie)} has been called
+ * on the associated response, then a corresponding Cookie header will be added
+ * to the PushBuilder, unless the {@link Cookie#getMaxAge()} is &lt;=0, in which
+ * case the Cookie will be removed from the builder.</li>
+ * <li>If this request has has the conditional headers If-Modified-Since or
+ * If-None-Match then the {@link #isConditional()} header is set to true.</li> 
+ * </ul>
+ * <p>A PushBuilder can be customized by chained calls to mutator methods before the 
+ * {@link #push()} method is called to initiate a push request with the current state
+ * of the builder.  After the call to {@link #push()}, the builder may be reused for
+ * another push, however the {@link #path(String)}, {@link #etag(String)} and
+ * {@link #lastModified(String)} values will have been nulled.  All other 
+ * values are retained over calls to {@link #push()}. 
+ */
+public interface PushBuilder
+{
+    /** Set the method to be used for the push.  
+     * Defaults to GET.
+     * @param method the method to be used for the push.  
+     * @return this builder.
+     */
+    public abstract PushBuilder method(String method);
+    
+    /** Set the query string to be used for the push.  
+     * Defaults to the requests query string.
+     * Will be appended to any query String included in a call to {@link #path(String)}.  This 
+     * method should be used instead of a query in {@link #path(String)} when multiple
+     * {@link #push()} calls are to be made with the same query string, or to remove a 
+     * query string obtained from the associated request.
+     * @param  queryString the query string to be used for the push. 
+     * @return this builder.
+     */
+    public abstract PushBuilder queryString(String queryString);
+    
+    /** Set the SessionID to be used for the push.
+     * The session ID will be set in the same way it was on the associated request (ie
+     * as a cookie if the associated request used a cookie, or as a url parameter if
+     * the associated request used a url parameter).
+     * Defaults to the requested session ID or any newly assigned session id from
+     * a newly created session.
+     * @param sessionId the SessionID to be used for the push.
+     * @return this builder.
+     */
+    public abstract PushBuilder sessionId(String sessionId);
+    
+    /** Set if the request is to be conditional.
+     * If the request is conditional, any available values from {@link #etag(String)} or 
+     * {@link #lastModified(String)} will be set in the appropriate headers. If the request
+     * is not conditional, then etag and lastModified values are ignored.  
+     * Defaults to true if the associated request was conditional.
+     * @param  conditional true if the push request is conditional
+     * @return this builder.
+     */
+    public abstract PushBuilder conditional(boolean conditional);
+    
+    /** Set a header to be used for the push.  
+     * @param name The header name to set
+     * @param value The header value to set
+     * @return this builder.
+     */
+    public abstract PushBuilder setHeader(String name, String value);
+    
+    /** Add a header to be used for the push.  
+     * @param name The header name to add
+     * @param value The header value to add
+     * @return this builder.
+     */
+    public abstract PushBuilder addHeader(String name, String value);
+    
+    /** Set the URI path to be used for the push.  
+     * The path may start with "/" in which case it is treated as an
+     * absolute path, otherwise it is relative to the context path of
+     * the associated request.
+     * There is no path default and {@link #path(String)} must be called
+     * before every call to {@link #push()}
+     * @param path the URI path to be used for the push, which may include a
+     * query string.
+     * @return this builder.
+     */
+    public abstract PushBuilder path(String path);
+    
+    /** Set the etag to be used for conditional pushes.  
+     * The etag will be used only if {@link #isConditional()} is true.
+     * Defaults to no etag.  The value is nulled after every call to 
+     * {@link #push()}
+     * @param etag the etag to be used for the push.
+     * @return this builder.
+     */
+    public abstract PushBuilder etag(String etag);
+
+    /** Set the last modified date to be used for conditional pushes.  
+     * The last modified date will be used only if {@link #isConditional()} is true.
+     * Defaults to no date.  The value is nulled after every call to 
+     * {@link #push()}
+     * @param lastModified the last modified date to be used for the push.
+     * @return this builder.
+     * */
+    public abstract PushBuilder lastModified(String lastModified);
+
+
+    /** Push a resource.
+     * Push a resource based on the current state of the PushBuilder.  If {@link #isConditional()}
+     * is true and an etag or lastModified value is provided, then an appropriate conditional header
+     * will be generated. If both an etag and lastModified value are provided only an If-None-Match header
+     * will be generated. If the builder has a session ID, then the pushed request
+     * will include the session ID either as a Cookie or as a URI parameter as appropriate. The builders
+     * query string is merged with any passed query string.
+     * After initiating the push, the builder has its path, etag and lastModified fields nulled. All 
+     * other fields are left as is for possible reuse in another push.
+     * @throws IllegalArgumentException if the method set expects a request body (eg POST)
+     */
+    public abstract void push();
+    
+    
+    
+    
+    
+    public abstract String getMethod();
+    public abstract String getQueryString();
+    public abstract String getSessionId();
+    public abstract boolean isConditional();
+    public abstract Set<String> getHeaderNames();
+    public abstract String getHeader(String name);
+    public abstract String getPath();
+    public abstract String getEtag();
+    public abstract String getLastModified();
+
+
+
+}
\ No newline at end of file
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java
new file mode 100644
index 0000000..12df995
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java
@@ -0,0 +1,311 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.Set;
+
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** 
+ */
+public class PushBuilderImpl implements PushBuilder
+{    
+    private static final Logger LOG = Log.getLogger(PushBuilderImpl.class);
+
+    private final static HttpField JettyPush = new HttpField("x-http2-push","PushBuilder");
+    
+    private final Request _request;
+    private final HttpFields _fields;
+    private String _method;
+    private String _queryString;
+    private String _sessionId;
+    private boolean _conditional;
+    private String _path;
+    private String _etag;
+    private String _lastModified;
+    
+    public PushBuilderImpl(Request request, HttpFields fields, String method, String queryString, String sessionId, boolean conditional)
+    {
+        super();
+        _request = request;
+        _fields = fields;
+        _method = method;
+        _queryString = queryString;
+        _sessionId = sessionId;
+        _conditional = conditional;
+        _fields.add(JettyPush);
+        if (LOG.isDebugEnabled())
+            LOG.debug("PushBuilder({} {}?{} s={} c={})",_method,_request.getRequestURI(),_queryString,_sessionId,_conditional);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#getMethod()
+     */
+    @Override
+    public String getMethod()
+    {
+        return _method;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#method(java.lang.String)
+     */
+    @Override
+    public PushBuilder method(String method)
+    {
+        _method = method;
+        return this;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#getQueryString()
+     */
+    @Override
+    public String getQueryString()
+    {
+        return _queryString;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#queryString(java.lang.String)
+     */
+    @Override
+    public PushBuilder queryString(String queryString)
+    {
+        _queryString = queryString;
+        return this;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#getSessionId()
+     */
+    @Override
+    public String getSessionId()
+    {
+        return _sessionId;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#sessionId(java.lang.String)
+     */
+    @Override
+    public PushBuilder sessionId(String sessionId)
+    {
+        _sessionId = sessionId;
+        return this;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#isConditional()
+     */
+    @Override
+    public boolean isConditional()
+    {
+        return _conditional;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#conditional(boolean)
+     */
+    @Override
+    public PushBuilder conditional(boolean conditional)
+    {
+        _conditional = conditional;
+        return this;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#getHeaderNames()
+     */
+    @Override
+    public Set<String> getHeaderNames()
+    {
+        return _fields.getFieldNamesCollection();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#getHeader(java.lang.String)
+     */
+    @Override
+    public String getHeader(String name)
+    {
+        return _fields.get(name);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#setHeader(java.lang.String, java.lang.String)
+     */
+    @Override
+    public PushBuilder setHeader(String name,String value)
+    {
+        _fields.put(name,value);
+        return this;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#addHeader(java.lang.String, java.lang.String)
+     */
+    @Override
+    public PushBuilder addHeader(String name,String value)
+    {
+        _fields.add(name,value);
+        return this;
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#getPath()
+     */
+    @Override
+    public String getPath()
+    {
+        return _path;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#path(java.lang.String)
+     */
+    @Override
+    public PushBuilder path(String path)
+    {
+        _path = path;
+        return this;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#getEtag()
+     */
+    @Override
+    public String getEtag()
+    {
+        return _etag;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#etag(java.lang.String)
+     */
+    @Override
+    public PushBuilder etag(String etag)
+    {
+        _etag = etag;
+        return this;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#getLastModified()
+     */
+    @Override
+    public String getLastModified()
+    {
+        return _lastModified;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#lastModified(java.lang.String)
+     */
+    @Override
+    public PushBuilder lastModified(String lastModified)
+    {
+        _lastModified = lastModified;
+        return this;
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.PushBuilder#push()
+     */
+    @Override
+    public void push()
+    {
+        if (HttpMethod.POST.is(_method) || HttpMethod.PUT.is(_method))
+            throw new IllegalStateException("Bad Method "+_method);
+        
+        if (_path==null || _path.length()==0)
+            throw new IllegalStateException("Bad Path "+_path);
+        
+        String path=_path;
+        String query=_queryString;
+        int q=path.indexOf('?');
+        if (q>=0)
+        {
+            query=(query!=null && query.length()>0)?(_path.substring(q+1)+'&'+query):_path.substring(q+1);
+            path=_path.substring(0,q);
+        }
+        
+        if (!path.startsWith("/"))
+            path=URIUtil.addPaths(_request.getContextPath(),path);
+        
+        String param=null;
+        if (_sessionId!=null)
+        {
+            if (_request.isRequestedSessionIdFromURL())
+                param="jsessionid="+_sessionId;
+            // TODO else 
+            //      _fields.add("Cookie","JSESSIONID="+_sessionId);
+        }
+        
+        if (_conditional)
+        {
+            if (_etag!=null)
+                _fields.add(HttpHeader.IF_NONE_MATCH,_etag);
+            else if (_lastModified!=null)
+                _fields.add(HttpHeader.IF_MODIFIED_SINCE,_lastModified);
+        }
+        
+        HttpURI uri = HttpURI.createHttpURI(_request.getScheme(),_request.getServerName(),_request.getServerPort(),path,param,query,null);
+        MetaData.Request push = new MetaData.Request(_method,uri,_request.getHttpVersion(),_fields);
+        
+        if (LOG.isDebugEnabled())
+            LOG.debug("Push {} {} inm={} ims={}",_method,uri,_fields.get(HttpHeader.IF_NONE_MATCH),_fields.get(HttpHeader.IF_MODIFIED_SINCE));
+        
+        _request.getHttpChannel().getHttpTransport().push(push);
+        _path=null;
+        _etag=null;
+        _lastModified=null;
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/QueuedHttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/QueuedHttpInput.java
deleted file mode 100644
index 2903aa4..0000000
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/QueuedHttpInput.java
+++ /dev/null
@@ -1,146 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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.InterruptedIOException;
-
-import org.eclipse.jetty.util.ArrayQueue;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * {@link QueuedHttpInput} holds a queue of items passed to it by calls to {@link #content(Object)}.
- * <p/>
- * {@link QueuedHttpInput} stores the items directly; if the items contain byte buffers, it does not copy them
- * but simply holds references to the item, thus the caller must organize for those buffers to valid while
- * held by this class.
- * <p/>
- * To assist the caller, subclasses may override methods {@link #onAsyncRead()}, {@link #onContentConsumed(Object)}
- * that can be implemented so that the caller will know when buffers are queued and consumed.
- */
-public abstract class QueuedHttpInput<T> extends HttpInput<T>
-{
-    private final static Logger LOG = Log.getLogger(QueuedHttpInput.class);
-
-    private final ArrayQueue<T> _inputQ = new ArrayQueue<>(lock());
-
-    public QueuedHttpInput()
-    {
-    }
-
-    public void content(T item)
-    {
-        // The buffer is not copied here.  This relies on the caller not recycling the buffer
-        // until the it is consumed.  The onContentConsumed and onAllContentConsumed() callbacks are
-        // the signals to the caller that the buffers can be recycled.
-
-        synchronized (lock())
-        {
-            boolean wasEmpty = _inputQ.isEmpty();
-            _inputQ.add(item);
-            if (LOG.isDebugEnabled())
-                LOG.debug("{} queued {}", this, item);
-            if (wasEmpty)
-            {
-                if (!onAsyncRead())
-                    lock().notify();
-            }
-        }
-    }
-
-    public void recycle()
-    {
-        synchronized (lock())
-        {
-            T item = _inputQ.pollUnsafe();
-            while (item != null)
-            {
-                onContentConsumed(item);
-                item = _inputQ.pollUnsafe();
-            }
-            super.recycle();
-        }
-    }
-
-    @Override
-    protected T nextContent()
-    {
-        synchronized (lock())
-        {
-            // Items are removed only when they are fully consumed.
-            T item = _inputQ.peekUnsafe();
-            // Skip consumed items at the head of the queue.
-            while (item != null && remaining(item) == 0)
-            {
-                _inputQ.pollUnsafe();
-                onContentConsumed(item);
-                if (LOG.isDebugEnabled())
-                    LOG.debug("{} consumed {}", this, item);
-                item = _inputQ.peekUnsafe();
-            }
-            return item;
-        }
-    }
-
-    protected void blockForContent() throws IOException
-    {
-        synchronized (lock())
-        {
-            while (_inputQ.isEmpty() && !isFinished() && !isEOF())
-            {
-                try
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("{} waiting for content", this);
-                    lock().wait();
-                }
-                catch (InterruptedException e)
-                {
-                    throw (IOException)new InterruptedIOException().initCause(e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Callback that signals that the given content has been consumed.
-     *
-     * @param item the consumed content
-     */
-    protected abstract void onContentConsumed(T item);
-
-    public void earlyEOF()
-    {
-        synchronized (lock())
-        {
-            super.earlyEOF();
-            lock().notify();
-        }
-    }
-
-    public void messageComplete()
-    {
-        synchronized (lock())
-        {
-            super.messageComplete();
-            lock().notify();
-        }
-    }
-}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
index 3f0fa2f..8b5b145 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
@@ -40,6 +40,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import javax.servlet.AsyncContext;
 import javax.servlet.AsyncListener;
@@ -52,8 +53,7 @@
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletRequestAttributeEvent;
 import javax.servlet.ServletRequestAttributeListener;
-import javax.servlet.ServletRequestEvent;
-import javax.servlet.ServletRequestListener;
+import javax.servlet.ServletRequestWrapper;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
@@ -62,22 +62,25 @@
 import javax.servlet.http.HttpUpgradeHandler;
 import javax.servlet.http.Part;
 
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HostPortHttpField;
 import org.eclipse.jetty.http.HttpCookie;
+import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpScheme;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.http.HttpURI;
 import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
 import org.eclipse.jetty.http.MimeTypes;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.ContextHandler.Context;
 import org.eclipse.jetty.server.session.AbstractSession;
 import org.eclipse.jetty.util.Attributes;
 import org.eclipse.jetty.util.AttributesMap;
-import org.eclipse.jetty.util.HostPort;
 import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.MultiException;
 import org.eclipse.jetty.util.MultiMap;
 import org.eclipse.jetty.util.MultiPartInputStreamParser;
 import org.eclipse.jetty.util.StringUtil;
@@ -86,7 +89,6 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-/* ------------------------------------------------------------ */
 /**
  * Jetty Request.
  * <p>
@@ -107,16 +109,22 @@
  * and the pathInfo matched against the servlet URL patterns and {@link Request#setServletPath(String)} called as a result.</li>
  * </ul>
  *
+ * <p>
  * A request instance is created for each connection 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.
- *
+ * An effort is made to avoid reparsing headers and cookies that are likely to be the same for requests from the same connection. 
+ * </p>
+ * <p>
+ * Request instances are recycled, which combined with badly written asynchronous applications can result in calls on requests that have been reset.
+ * The code is written in a style to avoid NPE and ISE when such calls are made, as this has often proved generate exceptions that distraction 
+ * from debugging such bad asynchronous applications.  Instead, request methods attempt to not fail when called in an illegal state, so that hopefully
+ * the bad application will proceed to a major state event (eg calling AsyncContext.onComplete) which has better asynchronous guards, true atomic state
+ * and better failure behaviour that will assist in debugging.
+ * </p>
  * <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
 {
@@ -128,91 +136,75 @@
     private static final Collection<Locale> __defaultLocale = Collections.singleton(Locale.getDefault());
     private static final int __NONE = 0, _STREAM = 1, __READER = 2;
 
-    private final HttpChannel<?> _channel;
-    private final HttpFields _fields=new HttpFields();
-    private final List<ServletRequestAttributeListener>  _requestAttributeListeners=new ArrayList<>();
-    private final HttpInput<?> _input;
+    private static final MultiMap<String> NO_PARAMS = new MultiMap<>();
 
-    public static class MultiPartCleanerListener implements ServletRequestListener
+    /* ------------------------------------------------------------ */
+    /**
+     * Obtain the base {@link Request} instance of a {@link ServletRequest}, by
+     * coercion, unwrapping or special attribute.
+     * @param request The request
+     * @return the base {@link Request} instance of a {@link ServletRequest}.
+     */
+    public static Request getBaseRequest(ServletRequest request)
     {
-        @Override
-        public void requestDestroyed(ServletRequestEvent sre)
-        {
-            //Clean up any tmp files created by MultiPartInputStream
-            MultiPartInputStreamParser mpis = (MultiPartInputStreamParser)sre.getServletRequest().getAttribute(__MULTIPART_INPUT_STREAM);
-            if (mpis != null)
-            {
-                ContextHandler.Context context = (ContextHandler.Context)sre.getServletRequest().getAttribute(__MULTIPART_CONTEXT);
+        if (request instanceof Request)
+            return (Request)request;
 
-                //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);
-                    }
-                }
-            }
-        }
+        Object channel = request.getAttribute(HttpChannel.class.getName());
+        if (channel instanceof HttpChannel)
+            return ((HttpChannel)channel).getRequest();
 
-        @Override
-        public void requestInitialized(ServletRequestEvent sre)
-        {
-            //nothing to do, multipart config set up by ServletHolder.handle()
-        }
+        while (request instanceof ServletRequestWrapper)
+            request=((ServletRequestWrapper)request).getRequest();
 
+        if (request instanceof Request)
+            return (Request)request;
+
+        return null;
     }
+    
+    private final HttpChannel _channel;
+    private final List<ServletRequestAttributeListener>  _requestAttributeListeners=new ArrayList<>();
+    private final HttpInput _input;
 
+    private MetaData.Request _metaData;
 
+    private String _contextPath;
+    private String _servletPath;
+    private String _pathInfo;
 
     private boolean _secure;
-    private boolean _asyncSupported = true;
+    private String _asyncNotSupportedSource = null;
     private boolean _newContext;
     private boolean _cookiesExtracted = false;
     private boolean _handled = false;
-    private boolean _paramsExtracted;
+    private boolean _contentParamsExtracted;
     private boolean _requestedSessionIdFromCookie = false;
-    private volatile Attributes _attributes;
+    private Attributes _attributes;
     private Authentication _authentication;
     private String _characterEncoding;
     private ContextHandler.Context _context;
-    private String _contextPath;
     private CookieCutter _cookies;
     private DispatcherType _dispatcherType;
     private int _inputState = __NONE;
-    private HttpMethod _httpMethod;
-    private String _httpMethodString;
     private MultiMap<String> _queryParameters;
     private MultiMap<String> _contentParameters;
     private MultiMap<String> _parameters;
-    private String _pathInfo;
-    private int _port;
-    private HttpVersion _httpVersion = HttpVersion.HTTP_1_1;
     private String _queryEncoding;
-    private String _queryString;
     private BufferedReader _reader;
     private String _readerEncoding;
     private InetSocketAddress _remote;
     private String _requestedSessionId;
-    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 HttpURI _uri;
     private MultiPartInputStreamParser _multiPartInputStream; //if the request is a multi-part mime
     private AsyncContextState _async;
 
     /* ------------------------------------------------------------ */
-    public Request(HttpChannel<?> channel, HttpInput<?> input)
+    public Request(HttpChannel channel, HttpInput input)
     {
         _channel = channel;
         _input = input;
@@ -221,16 +213,134 @@
     /* ------------------------------------------------------------ */
     public HttpFields getHttpFields()
     {
-        return _fields;
+        MetaData.Request metadata=_metaData;
+        if (metadata==null)
+            throw new IllegalStateException();
+        return metadata.getFields();
     }
 
     /* ------------------------------------------------------------ */
-    public HttpInput<?> getHttpInput()
+    public HttpInput getHttpInput()
     {
         return _input;
     }
 
     /* ------------------------------------------------------------ */
+    public boolean isPush()
+    {
+        return Boolean.TRUE.equals(getAttribute("org.eclipse.jetty.pushed"));
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isPushSupported()
+    {
+        return getHttpChannel().getHttpTransport().isPushSupported();
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get a PushBuilder associated with this request initialized as follows:<ul>
+     * <li>The method is initialized to "GET"</li>
+     * <li>The headers from this request are copied to the Builder, except for:<ul>
+     *   <li>Conditional headers (eg. If-Modified-Since)
+     *   <li>Range headers
+     *   <li>Expect headers
+     *   <li>Authorization headers
+     *   <li>Referrer headers
+     * </ul></li>
+     * <li>If the request was Authenticated, an Authorization header will
+     * be set with a container generated token that will result in equivalent
+     * Authorization</li>
+     * <li>The query string from {@link #getQueryString()}
+     * <li>The {@link #getRequestedSessionId()} value, unless at the time
+     * of the call {@link #getSession(boolean)}
+     * has previously been called to create a new {@link HttpSession}, in
+     * which case the new session ID will be used as the PushBuilders
+     * requested session ID.</li>
+     * <li>The source of the requested session id will be the same as for
+     * this request</li>
+     * <li>The builders Referer header will be set to {@link #getRequestURL()}
+     * plus any {@link #getQueryString()} </li>
+     * <li>If {@link HttpServletResponse#addCookie(Cookie)} has been called
+     * on the associated response, then a corresponding Cookie header will be added
+     * to the PushBuilder, unless the {@link Cookie#getMaxAge()} is &lt;=0, in which
+     * case the Cookie will be removed from the builder.</li>
+     * <li>If this request has has the conditional headers If-Modified-Since or
+     * If-None-Match then the {@link PushBuilderImpl#isConditional()} header is set
+     * to true.
+     * </ul>
+     *
+     * <p>Each call to getPushBuilder() will return a new instance
+     * of a PushBuilder based off this Request.  Any mutations to the
+     * returned PushBuilder are not reflected on future returns.
+     * @return A new PushBuilder or null if push is not supported
+     */
+    public PushBuilder getPushBuilder()
+    {
+        if (!isPushSupported())
+            throw new IllegalStateException();
+
+        HttpFields fields = new HttpFields(getHttpFields().size()+5);
+        boolean conditional=false;
+
+        for (HttpField field : getHttpFields())
+        {
+            HttpHeader header = field.getHeader();
+            if (header==null)
+                fields.add(field);
+            else
+            {
+                switch(header)
+                {
+                    case IF_MATCH:
+                    case IF_RANGE:
+                    case IF_UNMODIFIED_SINCE:
+                    case RANGE:
+                    case EXPECT:
+                    case REFERER:
+                    case COOKIE:
+                        continue;
+
+                    case AUTHORIZATION:
+                        continue;
+
+                    case IF_NONE_MATCH:
+                    case IF_MODIFIED_SINCE:
+                        conditional=true;
+                        continue;
+
+                    default:
+                        fields.add(field);
+                }
+            }
+        }
+
+        String id=null;
+        try
+        {
+            HttpSession session = getSession();
+            if (session!=null)
+            {
+                session.getLastAccessedTime(); // checks if session is valid
+                id=session.getId();
+            }
+            else
+                id=getRequestedSessionId();
+        }
+        catch(IllegalStateException e)
+        {
+            id=getRequestedSessionId();
+        }
+
+        PushBuilder builder = new PushBuilderImpl(this,fields,getMethod(),getQueryString(),id,conditional);
+        builder.addHeader("referer",getRequestURL().toString());
+
+        // TODO process any set cookies
+        // TODO process any user_identity
+
+        return builder;
+    }
+
+    /* ------------------------------------------------------------ */
     public void addEventListener(final EventListener listener)
     {
         if (listener instanceof ServletRequestAttributeListener)
@@ -239,41 +349,79 @@
             throw new IllegalArgumentException(listener.getClass().toString());
     }
 
-    public void extractParameters()
+    /* ------------------------------------------------------------ */
+    private MultiMap<String> getParameters()
     {
-        if (_paramsExtracted)
-            return;
+        if (!_contentParamsExtracted) 
+        {
+            // content parameters need boolean protection as they can only be read
+            // once, but may be reset to null by a reset
+            _contentParamsExtracted = true;
 
-        _paramsExtracted = true;
-
+            // Extract content parameters; these cannot be replaced by a forward()
+            // once extracted and may have already been extracted by getParts() or
+            // by a processing happening after a form-based authentication.
+            if (_contentParameters == null)
+            {
+                try
+                {
+                    extractContentParameters();
+                }
+                catch(IllegalStateException | IllegalArgumentException e)
+                {
+                    throw new BadMessageException("Unable to parse form content", e);
+                }
+            }
+        }
+        
         // Extract query string parameters; these may be replaced by a forward()
         // and may have already been extracted by mergeQueryParameters().
         if (_queryParameters == null)
-            _queryParameters = extractQueryParameters();
+        {
+            try
+            {
+                extractQueryParameters();
+            }
+            catch(IllegalStateException | IllegalArgumentException e)
+            {
+                throw new BadMessageException("Unable to parse URI query", e);
+            }
+        }
 
-        // Extract content parameters; these cannot be replaced by a forward()
-        // once extracted and may have already been extracted by getParts() or
-        // by a processing happening after a form-based authentication.
-        if (_contentParameters == null)
-            _contentParameters = extractContentParameters();
-
-        _parameters = restoreParameters();
+        // Do parameters need to be combined?
+        if (_queryParameters==NO_PARAMS || _queryParameters.size()==0)
+            _parameters=_contentParameters;
+        else if (_contentParameters==NO_PARAMS || _contentParameters.size()==0)
+            _parameters=_queryParameters;
+        else
+        {
+            _parameters = new MultiMap<>();
+            _parameters.addAllValues(_queryParameters);
+            _parameters.addAllValues(_contentParameters);
+        }
+        
+        // protect against calls to recycled requests (which is illegal, but
+        // this gives better failures 
+        MultiMap<String> parameters=_parameters;
+        return parameters==null?NO_PARAMS:parameters;
     }
 
-    private MultiMap<String> extractQueryParameters()
+    /* ------------------------------------------------------------ */
+    private void extractQueryParameters()
     {
-        MultiMap<String> result = new MultiMap<>();
-        if (_uri != null && _uri.hasQuery())
+        MetaData.Request metadata = _metaData;
+        if (metadata==null || metadata.getURI() == null || !metadata.getURI().hasQuery())
+            _queryParameters=NO_PARAMS;
+        else
         {
+            _queryParameters = new MultiMap<>();
             if (_queryEncoding == null)
-            {
-                _uri.decodeQueryTo(result);
-            }
+                metadata.getURI().decodeQueryTo(_queryParameters);
             else
             {
                 try
                 {
-                    _uri.decodeQueryTo(result, _queryEncoding);
+                    metadata.getURI().decodeQueryTo(_queryParameters, _queryEncoding);
                 }
                 catch (UnsupportedEncodingException e)
                 {
@@ -284,37 +432,38 @@
                 }
             }
         }
-        return result;
     }
 
-    private MultiMap<String> extractContentParameters()
+    /* ------------------------------------------------------------ */
+    private void extractContentParameters()
     {
-        MultiMap<String> result = new MultiMap<>();
-
         String contentType = getContentType();
-        if (contentType != null && !contentType.isEmpty())
+        if (contentType == null || contentType.isEmpty())
+            _contentParameters=NO_PARAMS;
+        else
         {
+            _contentParameters=new MultiMap<>();
             contentType = HttpFields.valueParameters(contentType, null);
             int contentLength = getContentLength();
             if (contentLength != 0)
             {
                 if (MimeTypes.Type.FORM_ENCODED.is(contentType) && _inputState == __NONE &&
-                        (HttpMethod.POST.is(getMethod()) || HttpMethod.PUT.is(getMethod())))
+                    _channel.getHttpConfiguration().isFormEncodedMethod(getMethod()))
                 {
-                    extractFormParameters(result);
+                    extractFormParameters(_contentParameters);
                 }
                 else if (contentType.startsWith("multipart/form-data") &&
                         getAttribute(__MULTIPART_CONFIG_ELEMENT) != null &&
                         _multiPartInputStream == null)
                 {
-                    extractMultipartParameters(result);
+                    extractMultipartParameters(_contentParameters);
                 }
             }
         }
 
-        return result;
     }
 
+    /* ------------------------------------------------------------ */
     public void extractFormParameters(MultiMap<String> params)
     {
         try
@@ -380,6 +529,7 @@
         }
     }
 
+    /* ------------------------------------------------------------ */
     private void extractMultipartParameters(MultiMap<String> result)
     {
         try
@@ -413,7 +563,8 @@
     /* ------------------------------------------------------------ */
     /**
      * Get Request Attribute.
-     * <p>Also supports jetty specific attributes to gain access to Jetty APIs:
+     * <p>
+     * Also supports jetty specific attributes to gain access to Jetty APIs:
      * <dl>
      * <dt>org.eclipse.jetty.server.Server</dt><dd>The Jetty Server instance</dd>
      * <dt>org.eclipse.jetty.server.HttpChannel</dt><dd>The HttpChannel for this request</dd>
@@ -421,7 +572,6 @@
      * </dl>
      * While these attributes may look like security problems, they are exposing nothing that is not already
      * available via reflection from a Request instance.
-     * </p>
      * @see javax.servlet.ServletRequest#getAttribute(java.lang.String)
      */
     @Override
@@ -429,11 +579,11 @@
     {
         if (name.startsWith("org.eclipse.jetty"))
         {
-            if ("org.eclipse.jetty.server.Server".equals(name))
+            if (Server.class.getName().equals(name))
                 return _channel.getServer();
-            if ("org.eclipse.jetty.server.HttpChannel".equals(name))
+            if (HttpChannel.class.getName().equals(name))
                 return _channel;
-            if ("org.eclipse.jetty.server.HttpConnection".equals(name) &&
+            if (HttpConnection.class.getName().equals(name) &&
                 _channel.getHttpTransport() instanceof HttpConnection)
                 return _channel.getHttpTransport();
         }
@@ -496,6 +646,8 @@
     @Override
     public String getCharacterEncoding()
     {
+        if (_characterEncoding==null)
+            getContentType();
         return _characterEncoding;
     }
 
@@ -503,7 +655,7 @@
     /**
      * @return Returns the connection.
      */
-    public HttpChannel<?> getHttpChannel()
+    public HttpChannel getHttpChannel()
     {
         return _channel;
     }
@@ -515,7 +667,12 @@
     @Override
     public int getContentLength()
     {
-        return (int)_fields.getLongField(HttpHeader.CONTENT_LENGTH.toString());
+        MetaData.Request metadata = _metaData;
+        if(metadata==null)
+            return -1;
+        if (metadata.getContentLength()!=Long.MIN_VALUE)
+            return (int)metadata.getContentLength();
+        return (int)metadata.getFields().getLongField(HttpHeader.CONTENT_LENGTH.toString());
     }
 
     /* ------------------------------------------------------------ */
@@ -525,13 +682,18 @@
     @Override
     public long getContentLengthLong()
     {
-        return _fields.getLongField(HttpHeader.CONTENT_LENGTH.toString());
+        MetaData.Request metadata = _metaData;
+        if(metadata==null)
+            return -1L;
+        if (metadata.getContentLength()!=Long.MIN_VALUE)
+            return metadata.getContentLength();
+        return metadata.getFields().getLongField(HttpHeader.CONTENT_LENGTH.toString());
     }
 
     /* ------------------------------------------------------------ */
     public long getContentRead()
     {
-        return _input.getContentRead();
+        return _input.getContentConsumed();
     }
 
     /* ------------------------------------------------------------ */
@@ -541,7 +703,18 @@
     @Override
     public String getContentType()
     {
-        return _fields.getStringField(HttpHeader.CONTENT_TYPE);
+        MetaData.Request metadata = _metaData;
+        if (metadata==null)
+            return null;
+        String content_type = metadata.getFields().get(HttpHeader.CONTENT_TYPE);
+        if (_characterEncoding==null && content_type!=null)
+        {
+            MimeTypes.Type mime = MimeTypes.CACHE.get(content_type);
+            String charset = (mime == null || mime.getCharset() == null) ? MimeTypes.getCharsetFromContentType(content_type) : mime.getCharset().toString();
+            if (charset != null)
+                _characterEncoding=charset;
+        }
+        return content_type;
     }
 
     /* ------------------------------------------------------------ */
@@ -570,7 +743,8 @@
     @Override
     public Cookie[] getCookies()
     {
-        if (_cookiesExtracted)
+        MetaData.Request metadata = _metaData;
+        if (metadata==null || _cookiesExtracted)
         {
             if (_cookies == null || _cookies.getCookies().length == 0)
                 return null;
@@ -579,20 +753,12 @@
         }
 
         _cookiesExtracted = true;
-
-        Enumeration<?> enm = _fields.getValues(HttpHeader.COOKIE.toString());
-
-        // Handle no cookies
-        if (enm != null)
+        
+        for (String c : metadata.getFields().getValuesList(HttpHeader.COOKIE))
         {
             if (_cookies == null)
                 _cookies = new CookieCutter();
-
-            while (enm.hasMoreElements())
-            {
-                String c = (String)enm.nextElement();
-                _cookies.addCookieField(c);
-            }
+            _cookies.addCookieField(c);
         }
 
         //Javadoc for Request.getCookies() stipulates null for no cookies
@@ -609,7 +775,8 @@
     @Override
     public long getDateHeader(String name)
     {
-        return _fields.getDateField(name);
+        MetaData.Request metadata = _metaData;
+        return metadata==null?-1:metadata.getFields().getDateField(name);
     }
 
     /* ------------------------------------------------------------ */
@@ -626,7 +793,8 @@
     @Override
     public String getHeader(String name)
     {
-        return _fields.getStringField(name);
+        MetaData.Request metadata = _metaData;
+        return metadata==null?null:metadata.getFields().get(name);
     }
 
     /* ------------------------------------------------------------ */
@@ -636,7 +804,8 @@
     @Override
     public Enumeration<String> getHeaderNames()
     {
-        return _fields.getFieldNames();
+        MetaData.Request metadata=_metaData;
+        return metadata==null?Collections.emptyEnumeration():metadata.getFields().getFieldNames();
     }
 
     /* ------------------------------------------------------------ */
@@ -646,7 +815,10 @@
     @Override
     public Enumeration<String> getHeaders(String name)
     {
-        Enumeration<String> e = _fields.getValues(name);
+        MetaData.Request metadata = _metaData;
+        if (metadata==null)
+            return Collections.emptyEnumeration();
+        Enumeration<String> e = metadata.getFields().getValues(name);
         if (e == null)
             return Collections.enumeration(Collections.<String>emptyList());
         return e;
@@ -685,7 +857,8 @@
     @Override
     public int getIntHeader(String name)
     {
-        return (int)_fields.getLongField(name);
+        MetaData.Request metadata = _metaData;
+        return metadata==null?-1:(int)metadata.getFields().getLongField(name);
     }
 
 
@@ -696,34 +869,26 @@
     @Override
     public Locale getLocale()
     {
-        Enumeration<String> enm = _fields.getValues(HttpHeader.ACCEPT_LANGUAGE.toString(),HttpFields.__separators);
+        MetaData.Request metadata = _metaData;
+        if (metadata==null)
+            return Locale.getDefault();
+
+        List<String> acceptable = metadata.getFields().getQualityCSV(HttpHeader.ACCEPT_LANGUAGE);
 
         // handle no locale
-        if (enm == null || !enm.hasMoreElements())
+        if (acceptable.isEmpty())
             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 = acceptable.get(0);
+        language = HttpFields.stripParameters(language);
+        String country = "";
+        int dash = language.indexOf('-');
+        if (dash > -1)
         {
-            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);
+            country = language.substring(dash + 1).trim();
+            language = language.substring(0,dash).trim();
         }
-
-        return Locale.getDefault();
+        return new Locale(language,country);        
     }
 
     /* ------------------------------------------------------------ */
@@ -733,39 +898,30 @@
     @Override
     public Enumeration<Locale> getLocales()
     {
+        MetaData.Request metadata = _metaData;
+        if (metadata==null)
+            return Collections.enumeration(__defaultLocale);
 
-        Enumeration<String> enm = _fields.getValues(HttpHeader.ACCEPT_LANGUAGE.toString(),HttpFields.__separators);
+        List<String> acceptable = metadata.getFields().getQualityCSV(HttpHeader.ACCEPT_LANGUAGE);
 
         // handle no locale
-        if (enm == null || !enm.hasMoreElements())
+        if (acceptable.isEmpty())
             return Collections.enumeration(__defaultLocale);
 
-        // sort the list in quality order
-        List<String> acceptLanguage = HttpFields.qualityList(enm);
-
-        if (acceptLanguage.size() == 0)
-            return Collections.enumeration(__defaultLocale);
-
-        List<Locale> langs = new ArrayList<>();
-
-        // convert to locals
-        for (String language : acceptLanguage)
+        List<Locale> locales = acceptable.stream().map(language->
         {
-            language = HttpFields.valueParameters(language, null);
+            language = HttpFields.stripParameters(language);
             String country = "";
             int dash = language.indexOf('-');
             if (dash > -1)
             {
                 country = language.substring(dash + 1).trim();
-                language = language.substring(0, dash).trim();
+                language = language.substring(0,dash).trim();
             }
-            langs.add(new Locale(language, country));
-        }
-
-        if (langs.size() == 0)
-            return Collections.enumeration(__defaultLocale);
-
-        return Collections.enumeration(langs);
+            return new Locale(language,country);
+        }).collect(Collectors.toList());
+        
+        return Collections.enumeration(locales);
     }
 
     /* ------------------------------------------------------------ */
@@ -775,6 +931,21 @@
     @Override
     public String getLocalAddr()
     {
+        if (_channel==null)
+        {
+            try
+            {
+                String name =InetAddress.getLocalHost().getHostAddress();
+                if (StringUtil.ALL_INTERFACES.equals(name))
+                    return null;
+                return name;
+            }
+            catch (java.net.UnknownHostException e)
+            {
+                LOG.ignore(e);
+            }
+        }
+
         InetSocketAddress local=_channel.getLocalAddress();
         if (local==null)
             return "";
@@ -791,6 +962,20 @@
     @Override
     public String getLocalName()
     {
+        if (_channel==null)
+        {
+            try
+            {
+                String name =InetAddress.getLocalHost().getHostName();
+                if (StringUtil.ALL_INTERFACES.equals(name))
+                    return null;
+                return name;
+            }
+            catch (java.net.UnknownHostException e)
+            {
+                LOG.ignore(e);
+            }
+        }
         InetSocketAddress local=_channel.getLocalAddress();
         return local.getHostString();
     }
@@ -802,6 +987,8 @@
     @Override
     public int getLocalPort()
     {
+        if (_channel==null)
+            return 0;
         InetSocketAddress local=_channel.getLocalAddress();
         return local.getPort();
     }
@@ -813,7 +1000,8 @@
     @Override
     public String getMethod()
     {
-        return _httpMethodString;
+        MetaData.Request metadata = _metaData;
+        return metadata==null?null:metadata.getMethod();
     }
 
     /* ------------------------------------------------------------ */
@@ -823,11 +1011,7 @@
     @Override
     public String getParameter(String name)
     {
-        if (!_paramsExtracted)
-            extractParameters();
-        if (_parameters == null)
-            _parameters = restoreParameters();
-        return _parameters.getValue(name,0);
+        return getParameters().getValue(name,0);
     }
 
     /* ------------------------------------------------------------ */
@@ -837,11 +1021,7 @@
     @Override
     public Map<String, String[]> getParameterMap()
     {
-        if (!_paramsExtracted)
-            extractParameters();
-        if (_parameters == null)
-            _parameters = restoreParameters();
-        return Collections.unmodifiableMap(_parameters.toStringArrayMap());
+        return Collections.unmodifiableMap(getParameters().toStringArrayMap());
     }
 
     /* ------------------------------------------------------------ */
@@ -851,11 +1031,7 @@
     @Override
     public Enumeration<String> getParameterNames()
     {
-        if (!_paramsExtracted)
-            extractParameters();
-        if (_parameters == null)
-            _parameters = restoreParameters();
-        return Collections.enumeration(_parameters.keySet());
+        return Collections.enumeration(getParameters().keySet());
     }
 
     /* ------------------------------------------------------------ */
@@ -865,41 +1041,31 @@
     @Override
     public String[] getParameterValues(String name)
     {
-        if (!_paramsExtracted)
-            extractParameters();
-        if (_parameters == null)
-            _parameters = restoreParameters();
-        List<String> vals = _parameters.getValues(name);
+        List<String> vals = getParameters().getValues(name);
         if (vals == null)
             return null;
         return vals.toArray(new String[vals.size()]);
     }
 
-    private MultiMap<String> restoreParameters()
-    {
-        MultiMap<String> result = new MultiMap<>();
-        if (_queryParameters == null)
-            _queryParameters = extractQueryParameters();
-        result.addAllValues(_queryParameters);
-        result.addAllValues(_contentParameters);
-        return result;
-    }
-
+    /* ------------------------------------------------------------ */
     public MultiMap<String> getQueryParameters()
     {
         return _queryParameters;
     }
 
+    /* ------------------------------------------------------------ */
     public void setQueryParameters(MultiMap<String> queryParameters)
     {
         _queryParameters = queryParameters;
     }
 
+    /* ------------------------------------------------------------ */
     public void setContentParameters(MultiMap<String> contentParameters)
     {
         _contentParameters = contentParameters;
     }
 
+    /* ------------------------------------------------------------ */
     public void resetParameters()
     {
         _parameters = null;
@@ -934,7 +1100,13 @@
     @Override
     public String getProtocol()
     {
-        return _httpVersion.toString();
+        MetaData.Request metadata = _metaData;
+        if (metadata==null)
+            return null;
+        HttpVersion version = metadata.getHttpVersion();
+        if (version==null)
+            return null;
+        return version.toString();
     }
 
     /* ------------------------------------------------------------ */
@@ -943,7 +1115,8 @@
      */
     public HttpVersion getHttpVersion()
     {
-        return _httpVersion;
+        MetaData.Request metadata = _metaData;
+        return metadata==null?null:metadata.getHttpVersion();
     }
 
     /* ------------------------------------------------------------ */
@@ -959,14 +1132,8 @@
     @Override
     public String getQueryString()
     {
-        if (_queryString == null && _uri != null)
-        {
-            if (_queryEncoding == null)
-                _queryString = _uri.getQuery();
-            else
-                _queryString = _uri.getQuery(_queryEncoding);
-        }
-        return _queryString;
+        MetaData.Request metadata = _metaData;
+        return metadata.getURI().getQuery();
     }
 
     /* ------------------------------------------------------------ */
@@ -1098,6 +1265,8 @@
     @Override
     public RequestDispatcher getRequestDispatcher(String path)
     {
+        // path is encoded, potentially with query
+        
         if (path == null || _context == null)
             return null;
 
@@ -1133,9 +1302,8 @@
     @Override
     public String getRequestURI()
     {
-        if (_requestURI == null && _uri != null)
-            _requestURI = _uri.getPathAndParam();
-        return _requestURI;
+        MetaData.Request metadata = _metaData;
+        return (metadata==null)?null:metadata.getURI().getPath();
     }
 
     /* ------------------------------------------------------------ */
@@ -1182,7 +1350,9 @@
     @Override
     public String getScheme()
     {
-        return _scheme;
+        MetaData.Request metadata = _metaData;
+        String scheme=metadata==null?null:metadata.getURI().getScheme();
+        return scheme==null?HttpScheme.HTTP.asString():scheme;
     }
 
     /* ------------------------------------------------------------ */
@@ -1192,63 +1362,49 @@
     @Override
     public String getServerName()
     {
+        MetaData.Request metadata = _metaData;
+        String name = metadata==null?null:metadata.getURI().getHost();
+
         // Return already determined host
-        if (_serverName != null)
-            return _serverName;
+        if (name != null)
+            return name;
 
-        if (_uri == null)
-            throw new IllegalStateException("No uri");
+        return findServerName();
+    }
 
-        // Return host from absolute URI
-        _serverName = _uri.getHost();
-        if (_serverName != null)
-        {
-            _port = _uri.getPort();
-            return _serverName;
-        }
-
+    /* ------------------------------------------------------------ */
+    private String findServerName()
+    {
+        MetaData.Request metadata = _metaData;
         // Return host from header field
-        String hostPort = _fields.getStringField(HttpHeader.HOST);
-
-        _port=0;
-        if (hostPort != null)
+        HttpField host = metadata==null?null:metadata.getFields().getField(HttpHeader.HOST);
+        if (host!=null)
         {
-            try
+            if (!(host instanceof HostPortHttpField) && host.getValue()!=null && !host.getValue().isEmpty())
+                host=new HostPortHttpField(host.getValue());    
+            if (host instanceof HostPortHttpField)
             {
-                HostPort authority = new HostPort(hostPort);
-                _serverName=authority.getHost();
-                _port=authority.getPort();
+                HostPortHttpField authority = (HostPortHttpField)host;
+                metadata.getURI().setAuthority(authority.getHost(),authority.getPort());
+                return authority.getHost();
             }
-            catch (Exception e)
-            {
-                LOG.warn(e);
-                _serverName=hostPort;
-                _port=0;
-                return _serverName;
-            }
-
-            return _serverName;
         }
 
         // Return host from connection
-        if (_channel != null)
-        {
-            _serverName = getLocalName();
-            _port = getLocalPort();
-            if (_serverName != null && !StringUtil.ALL_INTERFACES.equals(_serverName))
-                return _serverName;
-        }
+        String name=getLocalName();
+        if (name != null)
+            return name;
 
         // Return the local host
         try
         {
-            _serverName = InetAddress.getLocalHost().getHostAddress();
+            return InetAddress.getLocalHost().getHostAddress();
         }
         catch (java.net.UnknownHostException e)
         {
             LOG.ignore(e);
         }
-        return _serverName;
+        return null;
     }
 
     /* ------------------------------------------------------------ */
@@ -1258,30 +1414,43 @@
     @Override
     public int getServerPort()
     {
-        if (_port <= 0)
-        {
-            if (_serverName == null)
-                getServerName();
+        MetaData.Request metadata = _metaData;
+        HttpURI uri = metadata==null?null:metadata.getURI();
+        int port = (uri==null||uri.getHost()==null)?findServerPort():uri.getPort();
 
-            if (_port <= 0)
-            {
-                if (_serverName != null && _uri != null)
-                    _port = _uri.getPort();
-                else
-                {
-                    InetSocketAddress local = _channel.getLocalAddress();
-                    _port = local == null?0:local.getPort();
-                }
-            }
-        }
-
-        if (_port <= 0)
+        // If no port specified, return the default port for the scheme
+        if (port <= 0)
         {
             if (getScheme().equalsIgnoreCase(URIUtil.HTTPS))
                 return 443;
             return 80;
         }
-        return _port;
+
+        // return a specific port
+        return port;
+    }
+
+    /* ------------------------------------------------------------ */
+    private int findServerPort()
+    {
+        MetaData.Request metadata = _metaData;
+        // Return host from header field
+        HttpField host = metadata==null?null:metadata.getFields().getField(HttpHeader.HOST);
+        if (host!=null)
+        {
+            // TODO is this needed now?
+            HostPortHttpField authority = (host instanceof HostPortHttpField)
+                ?((HostPortHttpField)host)
+                :new HostPortHttpField(host.getValue());
+            metadata.getURI().setAuthority(authority.getHost(),authority.getPort());
+            return authority.getPort();
+        }
+
+        // Return host from connection
+        if (_channel != null)
+            return getLocalPort();
+
+        return -1;
     }
 
     /* ------------------------------------------------------------ */
@@ -1320,9 +1489,7 @@
     }
 
     /* ------------------------------------------------------------ */
-    /*
-     * Add @override when 3.1 api is available
-     */
+    @Override
     public String changeSessionId()
     {
         HttpSession session = getSession(false);
@@ -1334,7 +1501,7 @@
             AbstractSession abstractSession =  ((AbstractSession)session);
             abstractSession.renewId(this);
             if (getRemoteUser() != null)
-                abstractSession.setAttribute(AbstractSession.SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
+                abstractSession.setAttribute(AbstractSession.SESSION_CREATED_SECURE, Boolean.TRUE);
             if (abstractSession.isIdChanged())
                 _channel.getResponse().addCookie(_sessionManager.getSessionCookie(abstractSession, getContextPath(), isSecure()));
         }
@@ -1408,9 +1575,20 @@
     /**
      * @return Returns the uri.
      */
-    public HttpURI getUri()
+    public HttpURI getHttpURI()
     {
-        return _uri;
+        MetaData.Request metadata = _metaData;
+        return metadata==null?null:metadata.getURI();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param uri the URI to set
+     */
+    public void setHttpURI(HttpURI uri)
+    {
+        MetaData.Request metadata = _metaData;
+        metadata.setURI(uri);
     }
 
     /* ------------------------------------------------------------ */
@@ -1479,7 +1657,7 @@
     @Override
     public boolean isAsyncSupported()
     {
-        return _asyncSupported;
+        return _asyncNotSupportedSource==null;
     }
 
     /* ------------------------------------------------------------ */
@@ -1565,9 +1743,71 @@
         return _savedNewSessions.get(key);
     }
 
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param request the Request metadata
+     */
+    public void setMetaData(org.eclipse.jetty.http.MetaData.Request request)
+    {
+        _metaData=request;
+        setMethod(request.getMethod());
+        HttpURI uri = request.getURI();
+
+        String path = uri.getDecodedPath();
+        String info;
+        if (path==null || path.length()==0)
+        {
+            if (uri.isAbsolute())
+            {
+                path="/";
+                uri.setPath(path);
+            }
+            else
+            {
+                setPathInfo("");
+                throw new BadMessageException(400,"Bad URI");
+            }
+            info=path;
+        }
+        else if (!path.startsWith("/"))
+        {
+            if (!"*".equals(path) && !HttpMethod.CONNECT.is(getMethod()))
+            {
+                setPathInfo(path);
+                throw new BadMessageException(400,"Bad URI");
+            }
+            info=path;
+        }
+        else
+            info = URIUtil.canonicalPath(path);// TODO should this be done prior to decoding???
+
+        if (info == null)
+        {
+            setPathInfo(path);
+            throw new BadMessageException(400,"Bad URI");
+        }
+
+        setPathInfo(info);
+    }
+
+    /* ------------------------------------------------------------ */
+    public org.eclipse.jetty.http.MetaData.Request getMetaData()
+    {
+        return _metaData;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean hasMetaData()
+    {
+        return _metaData!=null;
+    }
+
     /* ------------------------------------------------------------ */
     protected void recycle()
     {
+        _metaData=null;
+
         if (_context != null)
             throw new IllegalStateException("Request in context!");
 
@@ -1592,7 +1832,7 @@
         if (_async!=null)
             _async.reset();
         _async=null;
-        _asyncSupported = true;
+        _asyncNotSupportedSource = null;
         _handled = false;
         if (_attributes != null)
             _attributes.clearAttributes();
@@ -1603,29 +1843,20 @@
         _cookiesExtracted = false;
         _context = null;
         _newContext=false;
-        _serverName = null;
-        _httpMethod=null;
-        _httpMethodString = null;
         _pathInfo = null;
-        _port = 0;
-        _httpVersion = HttpVersion.HTTP_1_1;
         _queryEncoding = null;
-        _queryString = null;
         _requestedSessionId = null;
         _requestedSessionIdFromCookie = false;
         _secure=false;
         _session = null;
         _sessionManager = null;
-        _requestURI = null;
         _scope = null;
-        _scheme = URIUtil.HTTP;
         _servletPath = null;
         _timeStamp = 0;
-        _uri = null;
         _queryParameters = null;
         _contentParameters = null;
         _parameters = null;
-        _paramsExtracted = false;
+        _contentParamsExtracted = false;
         _inputState = __NONE;
 
         if (_savedNewSessions != null)
@@ -1633,7 +1864,6 @@
         _savedNewSessions=null;
         _multiPartInputStream = null;
         _remote=null;
-        _fields.clear();
         _input.recycle();
     }
 
@@ -1672,9 +1902,9 @@
     }
 
     /* ------------------------------------------------------------ */
-    public void setAsyncSupported(boolean supported)
+    public void setAsyncSupported(boolean supported,String source)
     {
-        _asyncSupported = supported;
+        _asyncNotSupportedSource = supported?null:(source==null?"unknown":source);
     }
 
     /* ------------------------------------------------------------ */
@@ -1775,9 +2005,10 @@
      * @see javax.servlet.ServletRequest#getContentType()
      */
     public void setContentType(String contentType)
-    {
-        _fields.put(HttpHeader.CONTENT_TYPE,contentType);
-
+    {        
+        MetaData.Request metadata = _metaData;
+        if (metadata!=null)
+            metadata.getFields().put(HttpHeader.CONTENT_TYPE,contentType);
     }
 
     /* ------------------------------------------------------------ */
@@ -1808,7 +2039,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Sets the "context path" for this request
-     *
+     * @param contextPath the context path for this request
      * @see HttpServletRequest#getContextPath()
      */
     public void setContextPath(String contextPath)
@@ -1845,16 +2076,25 @@
      * @param method
      *            The method to set.
      */
-    public void setMethod(HttpMethod httpMethod, String method)
+    public void setMethod(String method)
     {
-        _httpMethod=httpMethod;
-        _httpMethodString = method;
+        MetaData.Request metadata = _metaData;
+        if (metadata!=null)
+            metadata.setMethod(method);
+    }
+
+    public void setHttpVersion(HttpVersion version)
+    {
+        MetaData.Request metadata = _metaData;
+        if (metadata!=null)
+            metadata.setHttpVersion(version);
     }
 
     /* ------------------------------------------------------------ */
     public boolean isHead()
     {
-        return HttpMethod.HEAD==_httpMethod;
+        MetaData.Request metadata = _metaData;
+        return metadata!=null && HttpMethod.HEAD.is(metadata.getMethod());
     }
 
     /* ------------------------------------------------------------ */
@@ -1869,27 +2109,16 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @param version
-     *            The protocol to set.
-     */
-    public void setHttpVersion(HttpVersion version)
-    {
-        _httpVersion = version;
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
      * 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
      * getParameter methods.
      *
      * The request attribute "org.eclipse.jetty.server.server.Request.queryEncoding" may be set as an alternate method of calling setQueryEncoding.
      *
-     * @param queryEncoding
+     * @param queryEncoding the URI query character encoding
      */
     public void setQueryEncoding(String queryEncoding)
     {
         _queryEncoding = queryEncoding;
-        _queryString = null;
     }
 
     /* ------------------------------------------------------------ */
@@ -1899,7 +2128,9 @@
      */
     public void setQueryString(String queryString)
     {
-        _queryString = queryString;
+        MetaData.Request metadata = _metaData;
+        if (metadata!=null)
+            metadata.getURI().setQuery(queryString);
         _queryEncoding = null; //assume utf-8
     }
 
@@ -1934,13 +2165,11 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @param requestURI
-     *            The requestURI to set.
-     */
-    public void setRequestURI(String requestURI)
+    public void setURIPathQuery(String requestURI)
     {
-        _requestURI = requestURI;
+        MetaData.Request metadata = _metaData;
+        if (metadata!=null)
+            metadata.getURI().setPathQuery(requestURI);
     }
 
     /* ------------------------------------------------------------ */
@@ -1950,27 +2179,23 @@
      */
     public void setScheme(String scheme)
     {
-        _scheme = scheme;
+        MetaData.Request metadata = _metaData;
+        if (metadata!=null)
+            metadata.getURI().setScheme(scheme);
     }
 
     /* ------------------------------------------------------------ */
     /**
      * @param host
      *            The host to set.
-     */
-    public void setServerName(String host)
-    {
-        _serverName = host;
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
      * @param port
-     *            The port to set.
+     *            the port to set
      */
-    public void setServerPort(int port)
+    public void setAuthority(String host,int port)
     {
-        _port = port;
+        MetaData.Request metadata = _metaData;
+        if (metadata!=null)
+            metadata.getURI().setAuthority(host,port);
     }
 
     /* ------------------------------------------------------------ */
@@ -2010,16 +2235,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @param uri
-     *            The uri to set.
-     */
-    public void setUri(HttpURI uri)
-    {
-        _uri = uri;
-    }
-
-    /* ------------------------------------------------------------ */
     public void setUserIdentityScope(UserIdentity.Scope scope)
     {
         _scope = scope;
@@ -2029,8 +2244,8 @@
     @Override
     public AsyncContext startAsync() throws IllegalStateException
     {
-        if (!_asyncSupported)
-            throw new IllegalStateException("!asyncSupported");
+        if (_asyncNotSupportedSource!=null)
+            throw new IllegalStateException("!asyncSupported: "+_asyncNotSupportedSource);
         HttpChannelState state = getHttpChannelState();
         if (_async==null)
             _async=new AsyncContextState(state);
@@ -2043,14 +2258,14 @@
     @Override
     public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException
     {
-        if (!_asyncSupported)
-            throw new IllegalStateException("!asyncSupported");
+        if (_asyncNotSupportedSource!=null)
+            throw new IllegalStateException("!asyncSupported: "+_asyncNotSupportedSource);
         HttpChannelState state = getHttpChannelState();
         if (_async==null)
             _async=new AsyncContextState(state);
         AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,servletRequest,servletResponse);
         event.setDispatchContext(getServletContext());
-        event.setDispatchPath(URIUtil.addPaths(getServletPath(),getPathInfo()));
+        event.setDispatchPath(URIUtil.encodePath(URIUtil.addPaths(getServletPath(),getPathInfo())));
         state.startAsync(event);
         return _async;
     }
@@ -2063,7 +2278,7 @@
                 getClass().getSimpleName(),
                 _handled ? "[" : "(",
                 getMethod(),
-                _uri,
+                getHttpURI(),
                 _handled ? "]" : ")",
                 hashCode());
     }
@@ -2136,7 +2351,7 @@
                         IO.copy(is, os);
                         String content=new String(os.toByteArray(),charset==null?StandardCharsets.UTF_8:Charset.forName(charset));
                         if (_contentParameters == null)
-                            _contentParameters = params == null ? new MultiMap<String>() : params;
+                            _contentParameters = params == null ? new MultiMap<>() : params;
                         _contentParameters.add(mp.getName(), content);
                     }
                     os.reset();
@@ -2172,24 +2387,35 @@
         _authentication=Authentication.UNAUTHENTICATED;
     }
 
-    public void mergeQueryParameters(String newQuery, boolean updateQueryString)
+    /* ------------------------------------------------------------ */
+    public void mergeQueryParameters(String oldQuery,String newQuery, boolean updateQueryString)
     {
-        MultiMap<String> newQueryParams = new MultiMap<>();
-        // Have to assume ENCODING because we can't know otherwise.
-        UrlEncoded.decodeTo(newQuery, newQueryParams, UrlEncoded.ENCODING, -1);
+        // TODO  This is seriously ugly
 
-        MultiMap<String> oldQueryParams = _queryParameters;
-        if (oldQueryParams == null && _queryString != null)
+        MultiMap<String> newQueryParams = null;
+        // Have to assume ENCODING because we can't know otherwise.
+        if (newQuery!=null)
         {
-            oldQueryParams = new MultiMap<>();
-            UrlEncoded.decodeTo(_queryString, oldQueryParams, getQueryEncoding(), -1);
+            newQueryParams = new MultiMap<>();
+            UrlEncoded.decodeTo(newQuery, newQueryParams, UrlEncoded.ENCODING);
         }
 
-        MultiMap<String> mergedQueryParams = newQueryParams;
-        if (oldQueryParams != null)
+        MultiMap<String> oldQueryParams = _queryParameters;
+        if (oldQueryParams == null && oldQuery != null)
+        {
+            oldQueryParams = new MultiMap<>();
+            UrlEncoded.decodeTo(oldQuery, oldQueryParams, getQueryEncoding());
+        }
+
+        MultiMap<String> mergedQueryParams;
+        if (newQueryParams==null || newQueryParams.size()==0)
+            mergedQueryParams=oldQueryParams==null?NO_PARAMS:oldQueryParams;
+        else if (oldQueryParams==null || oldQueryParams.size()==0)
+            mergedQueryParams=newQueryParams==null?NO_PARAMS:newQueryParams;
+        else
         {
             // Parameters values are accumulated.
-            mergedQueryParams = new MultiMap<>(newQueryParams);
+            mergedQueryParams=new MultiMap<>(newQueryParams);
             mergedQueryParams.addAllValues(oldQueryParams);
         }
 
@@ -2198,18 +2424,32 @@
 
         if (updateQueryString)
         {
-            // Build the new merged query string, parameters in the
-            // new query string hide parameters in the old query string.
-            StringBuilder mergedQuery = new StringBuilder(newQuery);
-            for (Map.Entry<String, List<String>> entry : mergedQueryParams.entrySet())
+            if (newQuery==null)
+                setQueryString(oldQuery);
+            else if (oldQuery==null)
+                setQueryString(newQuery);
+            else
             {
-                if (newQueryParams.containsKey(entry.getKey()))
-                    continue;
-                for (String value : entry.getValue())
-                    mergedQuery.append("&").append(entry.getKey()).append("=").append(value);
+                // Build the new merged query string, parameters in the
+                // new query string hide parameters in the old query string.
+                StringBuilder mergedQuery = new StringBuilder();
+                if (newQuery!=null)
+                    mergedQuery.append(newQuery);
+                for (Map.Entry<String, List<String>> entry : mergedQueryParams.entrySet())
+                {
+                    if (newQueryParams!=null && newQueryParams.containsKey(entry.getKey()))
+                        continue;
+                    for (String value : entry.getValue())
+                    {
+                        if (mergedQuery.length()>0)
+                            mergedQuery.append("&");
+                        URIUtil.encodePath(mergedQuery,entry.getKey());
+                        mergedQuery.append('=');
+                        URIUtil.encodePath(mergedQuery,value);
+                    }
+                }
+                setQueryString(mergedQuery.toString());
             }
-
-            setQueryString(mergedQuery.toString());
         }
     }
 
@@ -2219,23 +2459,6 @@
     @Override
     public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException
     {
-        if (getContext() == null)
-            throw new ServletException ("Unable to instantiate "+handlerClass);
-
-        try
-        {
-            //Instantiate an instance and inject it
-            T h = getContext().createInstance(handlerClass);
-
-            //TODO handle the rest of the upgrade process
-
-            return h;
-        }
-        catch (Exception e)
-        {
-            if (e instanceof ServletException)
-                throw (ServletException)e;
-            throw new ServletException(e);
-        }
+        throw new ServletException("HttpServletRequest.upgrade() not supported in Jetty");
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLog.java
index 14bb8b0..7cbeea9 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLog.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLog.java
@@ -18,13 +18,24 @@
 
 package org.eclipse.jetty.server; 
 
-import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.server.handler.RequestLogHandler;
 
 /** 
  * A <code>RequestLog</code> can be attached to a {@link org.eclipse.jetty.server.handler.RequestLogHandler} to enable 
  * logging of requests/responses.
+ * @see RequestLogHandler#setRequestLog(RequestLog)
+ * @see Server#setRequestLog(RequestLog)
  */
-public interface RequestLog extends LifeCycle
+public interface RequestLog
 {
+    /* ------------------------------------------------------------ */
+    /**
+     * @param request The request to log.
+     * @param response The response to log.  Note that for some requests
+     * the response instance may not have been fully populated (Eg 400 bad request
+     * responses are sent without a servlet response object).  Thus for basic
+     * log information it is best to consult {@link Response#getCommittedMetaData()}
+     * and {@link Response#getHttpChannel()} directly.
+     */
     public void log(Request request, Response response);
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLogCollection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLogCollection.java
new file mode 100644
index 0000000..60a0e5b
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLogCollection.java
@@ -0,0 +1,46 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.ArrayList;
+
+import static java.util.Arrays.asList;
+
+class RequestLogCollection
+    implements RequestLog
+{
+    private final ArrayList<RequestLog> delegates;
+
+    public RequestLogCollection(RequestLog... requestLogs)
+    {
+        delegates = new ArrayList<>(asList(requestLogs));
+    }
+
+    public void add(RequestLog requestLog)
+    {
+        delegates.add(requestLog);
+    }
+
+    @Override
+    public void log(Request request, Response response)
+    {
+        for (RequestLog delegate:delegates)
+            delegate.log(request, response);
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java
index cdedb2a..cfdec4b 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java
@@ -22,6 +22,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
 import java.nio.channels.ReadableByteChannel;
 import java.util.Comparator;
 import java.util.SortedSet;
@@ -32,30 +33,33 @@
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.eclipse.jetty.http.DateGenerator;
+import org.eclipse.jetty.http.GzipHttpContent;
 import org.eclipse.jetty.http.HttpContent;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http.MimeTypes.Type;
+import org.eclipse.jetty.http.PreEncodedHttpField;
+import org.eclipse.jetty.http.ResourceHttpContent;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.util.resource.ResourceFactory;
 
-
-/* ------------------------------------------------------------ */
-/** 
- * 
- */
-public class ResourceCache
+// TODO rename to ContentCache
+public class ResourceCache implements HttpContent.Factory
 {
     private static final Logger LOG = Log.getLogger(ResourceCache.class);
 
-    private final ConcurrentMap<String,Content> _cache;
+    private final ConcurrentMap<String,CachedHttpContent> _cache;
     private final AtomicInteger _cachedSize;
     private final AtomicInteger _cachedFiles;
     private final ResourceFactory _factory;
     private final ResourceCache _parent;
     private final MimeTypes _mimeTypes;
-    private final boolean _etagSupported;
+    private final boolean _etags;
+    private final boolean _gzip;
     private final boolean  _useFileMappedBuffer;
     
     private int _maxCachedFileSize =128*1024*1024;
@@ -64,18 +68,24 @@
     
     /* ------------------------------------------------------------ */
     /** Constructor.
+     * @param parent the parent resource cache
+     * @param factory the resource factory
      * @param mimeTypes Mimetype to use for meta data
+     * @param useFileMappedBuffer true to file memory mapped buffers
+     * @param etags true to support etags 
+     * @param gzip true to support gzip 
      */
-    public ResourceCache(ResourceCache parent, ResourceFactory factory, MimeTypes mimeTypes,boolean useFileMappedBuffer,boolean etags)
+    public ResourceCache(ResourceCache parent, ResourceFactory factory, MimeTypes mimeTypes,boolean useFileMappedBuffer,boolean etags,boolean gzip)
     {
         _factory = factory;
-        _cache=new ConcurrentHashMap<String,Content>();
+        _cache=new ConcurrentHashMap<String,CachedHttpContent>();
         _cachedSize=new AtomicInteger();
         _cachedFiles=new AtomicInteger();
         _mimeTypes=mimeTypes;
         _parent=parent;
         _useFileMappedBuffer=useFileMappedBuffer;
-        _etagSupported=etags;
+        _etags=etags;
+        _gzip=gzip;
     }
 
     /* ------------------------------------------------------------ */
@@ -150,7 +160,7 @@
             {
                 for (String path : _cache.keySet())
                 {
-                    Content content = _cache.remove(path);
+                    CachedHttpContent content = _cache.remove(path);
                     if (content!=null)
                         content.invalidate();
                 }
@@ -159,34 +169,45 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Deprecated
+    public HttpContent lookup(String pathInContext)
+        throws IOException
+    {
+        return getContent(pathInContext,_maxCachedFileSize);
+    }
+
+    /* ------------------------------------------------------------ */
     /** 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
+     * @param maxBufferSize The maximum buffer to allocated for this request.  For cached content, a larger buffer may have
+     * previously been allocated and returned by the {@link HttpContent#getDirectBuffer()} or {@link HttpContent#getIndirectBuffer()} calls.
      * @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 
+     * then a {@link ResourceHttpContent} instance is return. If 
      * the resource does not exist, then null is returned.
      * @throws IOException Problem loading the resource
      */
-    public HttpContent lookup(String pathInContext)
+    @Override
+    public HttpContent getContent(String pathInContext,int maxBufferSize)
         throws IOException
     {
         // Is the content in this cache?
-        Content content =_cache.get(pathInContext);
+        CachedHttpContent 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);
+        HttpContent loaded = load(pathInContext,resource,maxBufferSize);
         if (loaded!=null)
             return loaded;
         
         // Is the content in the parent cache?
         if (_parent!=null)
         {
-            HttpContent httpContent=_parent.lookup(pathInContext);
+            HttpContent httpContent=_parent.getContent(pathInContext,maxBufferSize);
             if (httpContent!=null)
                 return httpContent;
         }
@@ -196,48 +217,89 @@
     
     /* ------------------------------------------------------------ */
     /**
-     * @param resource
+     * @param resource the resource to test
      * @return True if the resource is cacheable. The default implementation tests the cache sizes.
      */
     protected boolean isCacheable(Resource resource)
     {
+        if (_maxCachedFiles<=0)
+            return false;
+        
         long len = resource.length();
 
         // Will it fit in the cache?
-        return  (len>0 && len<_maxCachedFileSize && len<_maxCacheSize);
+        return  (len>0 && (_useFileMappedBuffer || (len<_maxCachedFileSize && len<_maxCacheSize)));
     }
     
     /* ------------------------------------------------------------ */
-    private HttpContent load(String pathInContext, Resource resource)
+    private HttpContent load(String pathInContext, Resource resource, int maxBufferSize)
         throws IOException
     {
-        Content content=null;
-        
         if (resource==null || !resource.exists())
             return null;
         
+        if (resource.isDirectory())
+            return new ResourceHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),getMaxCachedFileSize());
+        
         // Will it fit in the cache?
-        if (!resource.isDirectory() && isCacheable(resource))
+        if (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();
+            CachedHttpContent content=null;
+            
+            // Look for a gzip resource
+            if (_gzip)
+            {
+                String pathInContextGz=pathInContext+".gz";
+                CachedHttpContent contentGz = _cache.get(pathInContextGz);
+                if (contentGz==null || !contentGz.isValid())
+                {
+                    contentGz=null;
+                    Resource resourceGz=_factory.getResource(pathInContextGz);
+                    if (resourceGz.exists() && resourceGz.lastModified()>=resource.lastModified() && resourceGz.length()<resource.length())
+                    {
+                        contentGz = new CachedHttpContent(pathInContextGz,resourceGz,null);
+                        CachedHttpContent added = _cache.putIfAbsent(pathInContextGz,contentGz);
+                        if (added!=null)
+                        {
+                            contentGz.invalidate();
+                            contentGz=added;
+                        }
+                    }
+                }
+                content = new CachedHttpContent(pathInContext,resource,contentGz);
+            }
+            else 
+                content = new CachedHttpContent(pathInContext,resource,null);
 
             // Add it to the cache.
-            Content added = _cache.putIfAbsent(pathInContext,content);
+            CachedHttpContent added = _cache.putIfAbsent(pathInContext,content);
             if (added!=null)
             {
                 content.invalidate();
                 content=added;
             }
-
+            
             return content;
         }
         
-        return new HttpContent.ResourceAsHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),getMaxCachedFileSize(),_etagSupported);
+        // Look for non Cacheable gzip resource or content
+        String mt = _mimeTypes.getMimeByExtension(pathInContext);
+        if (_gzip)
+        {
+            // Is the gzip content cached?
+            String pathInContextGz=pathInContext+".gz";
+            CachedHttpContent contentGz = _cache.get(pathInContextGz);
+            if (contentGz!=null && contentGz.isValid() && contentGz.getResource().lastModified()>=resource.lastModified())
+                return new ResourceHttpContent(resource,mt,maxBufferSize,contentGz);
+            
+            // Is there a gzip resource?
+            Resource resourceGz=_factory.getResource(pathInContextGz);
+            if (resourceGz.exists() && resourceGz.lastModified()>=resource.lastModified() && resourceGz.length()<resource.length())
+                return new ResourceHttpContent(resource,mt,maxBufferSize,
+                       new ResourceHttpContent(resourceGz,_mimeTypes.getMimeByExtension(pathInContextGz),maxBufferSize));
+        }
         
+        return new ResourceHttpContent(resource,mt,maxBufferSize);
     }
     
     /* ------------------------------------------------------------ */
@@ -247,10 +309,10 @@
         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>()
+            SortedSet<CachedHttpContent> sorted= new TreeSet<CachedHttpContent>(
+                    new Comparator<CachedHttpContent>()
                     {
-                        public int compare(Content c1, Content c2)
+                        public int compare(CachedHttpContent c1, CachedHttpContent c2)
                         {
                             if (c1._lastAccessed<c2._lastAccessed)
                                 return -1;
@@ -258,17 +320,17 @@
                             if (c1._lastAccessed>c2._lastAccessed)
                                 return 1;
 
-                            if (c1._length<c2._length)
+                            if (c1._contentLengthValue<c2._contentLengthValue)
                                 return -1;
                             
                             return c1._key.compareTo(c2._key);
                         }
                     });
-            for (Content content : _cache.values())
+            for (CachedHttpContent content : _cache.values())
                 sorted.add(content);
             
             // Invalidate least recently used first
-            for (Content content : sorted)
+            for (CachedHttpContent content : sorted)
             {
                 if (_cachedFiles.get()<=_maxCachedFiles && _cachedSize.get()<=_maxCacheSize)
                     break;
@@ -292,21 +354,36 @@
         }
     }
 
+
+    /* ------------------------------------------------------------ */
+    protected ByteBuffer getMappedBuffer(Resource resource)
+    {
+        // Only use file mapped buffers for cached resources, otherwise too much virtual memory commitment for
+        // a non shared resource.  Also ignore max buffer size
+        try
+        {
+            if (_useFileMappedBuffer && resource.getFile()!=null && resource.length()<Integer.MAX_VALUE) 
+                return BufferUtil.toMappedBuffer(resource.getFile());            
+        }
+        catch(IOException|IllegalArgumentException e)
+        {
+            LOG.warn(e);
+        }
+        return null;
+    }
+    
     /* ------------------------------------------------------------ */
     protected ByteBuffer getDirectBuffer(Resource resource)
     {
         try
         {
-            if (_useFileMappedBuffer && resource.getFile()!=null && resource.length()<Integer.MAX_VALUE) 
-                return BufferUtil.toMappedBuffer(resource.getFile());
-            
             return BufferUtil.toBuffer(resource,true);
         }
         catch(IOException|IllegalArgumentException e)
         {
             LOG.warn(e);
-            return null;
         }
+        return null;
     }
 
     /* ------------------------------------------------------------ */
@@ -320,40 +397,53 @@
     /* ------------------------------------------------------------ */
     /** MetaData associated with a context Resource.
      */
-    public class Content implements HttpContent
+    public class CachedHttpContent implements HttpContent
     {
-        final Resource _resource;
-        final int _length;
         final String _key;
-        final long _lastModified;
-        final ByteBuffer _lastModifiedBytes;
-        final ByteBuffer _contentType;
-        final String _etag;
+        final Resource _resource;
+        final int _contentLengthValue;
+        final HttpField _contentType;
+        final String _characterEncoding;
+        final MimeTypes.Type _mimeType;
+        final HttpField _contentLength;
+        final HttpField _lastModified;
+        final long _lastModifiedValue;
+        final HttpField _etag;
+        final CachedGzipHttpContent _gzipped;
         
         volatile long _lastAccessed;
         AtomicReference<ByteBuffer> _indirectBuffer=new AtomicReference<ByteBuffer>();
         AtomicReference<ByteBuffer> _directBuffer=new AtomicReference<ByteBuffer>();
 
         /* ------------------------------------------------------------ */
-        Content(String pathInContext,Resource resource)
+        CachedHttpContent(String pathInContext,Resource resource,CachedHttpContent gzipped)
         {
             _key=pathInContext;
             _resource=resource;
 
-            String mimeType = _mimeTypes.getMimeByExtension(_resource.toString());
-            _contentType=(mimeType==null?null:BufferUtil.toBuffer(mimeType));
-            boolean exists=resource.exists();
-            _lastModified=exists?resource.lastModified():-1;
-            _lastModifiedBytes=_lastModified<0?null:BufferUtil.toBuffer(DateGenerator.formatDate(_lastModified));
+            String contentType = _mimeTypes.getMimeByExtension(_resource.toString());
+            _contentType=contentType==null?null:new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,contentType);
+            _characterEncoding = _contentType==null?null:MimeTypes.getCharsetFromContentType(contentType);
+            _mimeType = _contentType==null?null:MimeTypes.CACHE.get(MimeTypes.getContentTypeWithoutCharset(contentType));
             
-            _length=exists?(int)resource.length():0;
-            _cachedSize.addAndGet(_length);
-            _cachedFiles.incrementAndGet();
+            boolean exists=resource.exists();
+            _lastModifiedValue=exists?resource.lastModified():-1L;
+            _lastModified=_lastModifiedValue==-1?null
+                :new PreEncodedHttpField(HttpHeader.LAST_MODIFIED,DateGenerator.formatDate(_lastModifiedValue));
+            
+            _contentLengthValue=exists?(int)resource.length():0;
+            _contentLength=new PreEncodedHttpField(HttpHeader.CONTENT_LENGTH,Long.toString(_contentLengthValue));
+            
+            if (_cachedFiles.incrementAndGet()>_maxCachedFiles)
+                shrinkCache();
+            
             _lastAccessed=System.currentTimeMillis();
             
-            _etag=ResourceCache.this._etagSupported?resource.getWeakETag():null;
+            _etag=ResourceCache.this._etags?new PreEncodedHttpField(HttpHeader.ETAG,resource.getWeakETag()):null;
+            
+            _gzipped=gzipped==null?null:new CachedGzipHttpContent(this,gzipped);        
         }
-
+        
 
         /* ------------------------------------------------------------ */
         public String getKey()
@@ -382,15 +472,22 @@
 
         /* ------------------------------------------------------------ */
         @Override
-        public String getETag()
+        public HttpField getETag()
         {
             return _etag;
         }
+
+        /* ------------------------------------------------------------ */
+        @Override
+        public String getETagValue()
+        {
+            return _etag.getValue();
+        }
         
         /* ------------------------------------------------------------ */
         boolean isValid()
         {
-            if (_lastModified==_resource.lastModified() && _length==_resource.length())
+            if (_lastModifiedValue==_resource.lastModified() && _contentLengthValue==_resource.length())
             {
                 _lastAccessed=System.currentTimeMillis();
                 return true;
@@ -404,31 +501,79 @@
         /* ------------------------------------------------------------ */
         protected void invalidate()
         {
-            // Invalidate it
-            _cachedSize.addAndGet(-_length);
+            ByteBuffer indirect=_indirectBuffer.get();
+            if (indirect!=null && _indirectBuffer.compareAndSet(indirect,null))
+                _cachedSize.addAndGet(-BufferUtil.length(indirect));
+            
+            ByteBuffer direct=_directBuffer.get();
+            if (direct!=null && !BufferUtil.isMappedBuffer(direct) && _directBuffer.compareAndSet(direct,null))
+                _cachedSize.addAndGet(-BufferUtil.length(direct));
+
             _cachedFiles.decrementAndGet();
-            _resource.close(); 
+            _resource.close();
         }
 
         /* ------------------------------------------------------------ */
         @Override
-        public String getLastModified()
+        public HttpField getLastModified()
         {
-            return BufferUtil.toString(_lastModifiedBytes);
+            return _lastModified;
+        }
+        
+        /* ------------------------------------------------------------ */
+        @Override
+        public String getLastModifiedValue()
+        {
+            return _lastModified==null?null:_lastModified.getValue();
         }
 
         /* ------------------------------------------------------------ */
         @Override
-        public String getContentType()
+        public HttpField getContentType()
         {
-            return BufferUtil.toString(_contentType);
+            return _contentType;
         }
+        
+        /* ------------------------------------------------------------ */
+        @Override
+        public String getContentTypeValue()
+        {
+            return _contentType==null?null:_contentType.getValue();
+        }
+
+        /* ------------------------------------------------------------ */
+        @Override
+        public HttpField getContentEncoding()
+        {
+            return null;
+        }
+
+        /* ------------------------------------------------------------ */
+        @Override
+        public String getContentEncodingValue()
+        {
+            return null;
+        }   
+        
+        /* ------------------------------------------------------------ */
+        @Override
+        public String getCharacterEncoding()
+        {
+            return _characterEncoding;
+        }
+
+        /* ------------------------------------------------------------ */
+        @Override
+        public Type getMimeType()
+        {
+            return _mimeType;
+        }
+
 
         /* ------------------------------------------------------------ */
         @Override
         public void release()
         {
-            // don't release while cached. Release when invalidated.
         }
 
         /* ------------------------------------------------------------ */
@@ -443,7 +588,11 @@
                 if (buffer2==null)
                     LOG.warn("Could not load "+this);
                 else if (_indirectBuffer.compareAndSet(null,buffer2))
+                {
                     buffer=buffer2;
+                    if (_cachedSize.addAndGet(BufferUtil.length(buffer))>_maxCacheSize)
+                        shrinkCache();
+                }
                 else
                     buffer=_indirectBuffer.get();
             }
@@ -452,7 +601,6 @@
             return buffer.slice();
         }
         
-
         /* ------------------------------------------------------------ */
         @Override
         public ByteBuffer getDirectBuffer()
@@ -460,12 +608,17 @@
             ByteBuffer buffer = _directBuffer.get();
             if (buffer==null)
             {
-                ByteBuffer buffer2=ResourceCache.this.getDirectBuffer(_resource);
-
-                if (buffer2==null)
+                ByteBuffer mapped = ResourceCache.this.getMappedBuffer(_resource);
+                ByteBuffer direct = mapped==null?ResourceCache.this.getDirectBuffer(_resource):mapped;
+                    
+                if (direct==null)
                     LOG.warn("Could not load "+this);
-                else if (_directBuffer.compareAndSet(null,buffer2))
-                    buffer=buffer2;
+                else if (_directBuffer.compareAndSet(null,direct))
+                {
+                    buffer=direct;
+                    if (mapped==null && _cachedSize.addAndGet(BufferUtil.length(buffer))>_maxCacheSize)
+                        shrinkCache(); 
+                }
                 else
                     buffer=_directBuffer.get();
             }
@@ -473,12 +626,19 @@
                 return null;
             return buffer.asReadOnlyBuffer();
         }
+
+        /* ------------------------------------------------------------ */
+        @Override
+        public HttpField getContentLength()
+        {
+            return _contentLength;
+        }
         
         /* ------------------------------------------------------------ */
         @Override
-        public long getContentLength()
+        public long getContentLengthValue()
         {
-            return _length;
+            return _contentLengthValue;
         }
 
         /* ------------------------------------------------------------ */
@@ -499,12 +659,65 @@
             return _resource.getReadableByteChannel();
         }
 
-
         /* ------------------------------------------------------------ */
         @Override
         public String toString()
         {
-            return String.format("CachedContent@%x{r=%s,e=%b,lm=%s,ct=%s}",hashCode(),_resource,_resource.exists(),BufferUtil.toString(_lastModifiedBytes),_contentType);
-        }   
+            return String.format("CachedContent@%x{r=%s,e=%b,lm=%s,ct=%s,gz=%b}",hashCode(),_resource,_resource.exists(),_lastModified,_contentType,_gzipped!=null);
+        }
+
+        /* ------------------------------------------------------------ */
+        @Override
+        public HttpContent getGzipContent()
+        {
+            return (_gzipped!=null && _gzipped.isValid())?_gzipped:null;
+        }
     }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public class CachedGzipHttpContent extends GzipHttpContent
+    {
+        private final CachedHttpContent _content; 
+        private final CachedHttpContent _contentGz;
+        private final HttpField _etag;
+        
+        CachedGzipHttpContent(CachedHttpContent content, CachedHttpContent contentGz)
+        {
+            super(content,contentGz);
+            _content=content;
+            _contentGz=contentGz;
+            
+            _etag=(ResourceCache.this._etags)?new PreEncodedHttpField(HttpHeader.ETAG,_content.getResource().getWeakETag("--gzip")):null;
+        }
+
+        public boolean isValid()
+        {
+            return _contentGz.isValid() && _content.isValid() && _content.getResource().lastModified() <= _contentGz.getResource().lastModified();
+        }
+
+        @Override
+        public HttpField getETag()
+        {
+            if (_etag!=null)
+                return _etag;
+            return super.getETag();
+        }
+
+        @Override
+        public String getETagValue()
+        {
+            if (_etag!=null)
+                return _etag.getValue();
+            return super.getETagValue();
+        }
+        
+        @Override
+        public String toString()
+        {
+            return "Cached"+super.toString();
+        }
+    }
+
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceContentFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceContentFactory.java
new file mode 100644
index 0000000..b1dbd09
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceContentFactory.java
@@ -0,0 +1,96 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.HttpContent;
+import org.eclipse.jetty.http.HttpContent.Factory;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http.ResourceHttpContent;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.resource.ResourceFactory;
+
+
+/**
+ * A HttpContent.Factory for transient content.  The HttpContent's created by 
+ * this factory are not intended to be cached, so memory limits for individual
+ * HttpOutput streams are enforced.
+ */
+public class ResourceContentFactory implements Factory
+{
+    private final ResourceFactory _factory;
+    private final MimeTypes _mimeTypes;
+    private final boolean _gzip;
+    
+    /* ------------------------------------------------------------ */
+    public ResourceContentFactory(ResourceFactory factory, MimeTypes mimeTypes, boolean gzip)
+    {
+        _factory=factory;
+        _mimeTypes=mimeTypes;
+        _gzip=gzip;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public HttpContent getContent(String pathInContext,int maxBufferSize)
+        throws IOException
+    {
+        // try loading the content from our factory.
+        Resource resource=_factory.getResource(pathInContext);
+        HttpContent loaded = load(pathInContext,resource,maxBufferSize);
+        return loaded;
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    private HttpContent load(String pathInContext, Resource resource, int maxBufferSize)
+        throws IOException
+    {   
+        if (resource==null || !resource.exists())
+            return null;
+        
+        if (resource.isDirectory())
+            return new ResourceHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),maxBufferSize);
+        
+        // Look for a gzip resource or content
+        String mt = _mimeTypes.getMimeByExtension(pathInContext);
+        if (_gzip)
+        {
+            // Is there a gzip resource? 
+            String pathInContextGz=pathInContext+".gz";
+            Resource resourceGz=_factory.getResource(pathInContextGz);
+            if (resourceGz.exists() && resourceGz.lastModified()>=resource.lastModified() && resourceGz.length()<resource.length())
+                return new ResourceHttpContent(resource,mt,maxBufferSize,
+                       new ResourceHttpContent(resourceGz,_mimeTypes.getMimeByExtension(pathInContextGz),maxBufferSize));
+        }
+        
+        return new ResourceHttpContent(resource,mt,maxBufferSize);
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return "ResourceContentFactory["+_factory+"]@"+hashCode();
+    }
+    
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
index 526c662..984209c 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
@@ -18,18 +18,16 @@
 
 package org.eclipse.jetty.server;
 
-import static org.eclipse.jetty.util.QuotedStringTokenizer.isQuoted;
-
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.channels.IllegalSelectorException;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Enumeration;
-import java.util.Iterator;
+import java.util.EnumSet;
+import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletOutputStream;
@@ -43,14 +41,15 @@
 import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpGenerator;
-import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpHeaderValue;
 import org.eclipse.jetty.http.HttpScheme;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.http.HttpURI;
 import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
 import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http.PreEncodedHttpField;
 import org.eclipse.jetty.io.RuntimeIOException;
 import org.eclipse.jetty.server.handler.ErrorHandler;
 import org.eclipse.jetty.util.ByteArrayISO8859Writer;
@@ -69,6 +68,7 @@
     private static final String __COOKIE_DELIM="\",;\\ \t";
     private final static String __01Jan1970_COOKIE = DateGenerator.formatCookieDate(0).trim();
     private final static int __MIN_BUFFER_SIZE = 1;
+    private final static HttpField __EXPIRES_01JAN1970 = new PreEncodedHttpField(HttpHeader.EXPIRES,DateGenerator.__01Jan1970);
     
 
     // Cookie building buffer. Reduce garbage for cookie using applications
@@ -80,15 +80,6 @@
           return new StringBuilder(128);
        }
     };
-
-    /* ------------------------------------------------------------ */
-    public static Response getResponse(HttpServletResponse response)
-    {
-        if (response instanceof Response)
-            return (Response)response;
-        return HttpChannel.getCurrentHttpChannel().getResponse();
-    }
-    
     
     public enum OutputType
     {
@@ -108,29 +99,32 @@
      */
     public final static String HTTP_ONLY_COMMENT = "__HTTP_ONLY__";
 
-    private final HttpChannel<?> _channel;
+    private final HttpChannel _channel;
     private final HttpFields _fields = new HttpFields();
     private final AtomicInteger _include = new AtomicInteger();
-    private HttpOutput _out;
+    private final HttpOutput _out;
     private int _status = HttpStatus.OK_200;
     private String _reason;
     private Locale _locale;
     private MimeTypes.Type _mimeType;
     private String _characterEncoding;
-    private boolean _explicitEncoding;
+    private EncodingFrom _encodingFrom=EncodingFrom.NOT_SET;
     private String _contentType;
     private OutputType _outputType = OutputType.NONE;
     private ResponseWriter _writer;
     private long _contentLength = -1;
     
+    private enum EncodingFrom { NOT_SET, INFERRED, SET_LOCALE, SET_CONTENT_TYPE, SET_CHARACTER_ENCODING };
+    private static final EnumSet<EncodingFrom> __localeOverride = EnumSet.of(EncodingFrom.NOT_SET,EncodingFrom.INFERRED);
+    
 
-    public Response(HttpChannel<?> channel, HttpOutput out)
+    public Response(HttpChannel channel, HttpOutput out)
     {
         _channel = channel;
         _out = out;
     }
 
-    protected HttpChannel<?> getHttpChannel()
+    public HttpChannel getHttpChannel()
     {
         return _channel;
     }
@@ -145,45 +139,15 @@
         _contentType = null;
         _outputType = OutputType.NONE;
         _contentLength = -1;
-        _out.reset();
+        _out.recycle();
         _fields.clear();
-        _explicitEncoding=false;
-    }
-
-    public void setHeaders(HttpContent httpContent)
-    {
-        Response response = _channel.getResponse();
-        String contentType = httpContent.getContentType();
-        if (contentType != null && !response.getHttpFields().containsKey(HttpHeader.CONTENT_TYPE.asString()))
-            setContentType(contentType);
-        
-        if (httpContent.getContentLength() > 0)
-            setLongContentLength(httpContent.getContentLength());
-
-        String lm = httpContent.getLastModified();
-        if (lm != null)
-            response.getHttpFields().put(HttpHeader.LAST_MODIFIED, lm);
-        else if (httpContent.getResource() != null)
-        {
-            long lml = httpContent.getResource().lastModified();
-            if (lml != -1)
-                response.getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, lml);
-        }
-
-        String etag=httpContent.getETag();
-        if (etag!=null)
-            response.getHttpFields().put(HttpHeader.ETAG,etag);
+        _encodingFrom=EncodingFrom.NOT_SET;
     }
     
     public HttpOutput getHttpOutput()
     {
         return _out;
     }
-    
-    public void setHttpOutput(HttpOutput out)
-    {
-        _out=out;
-    }
 
     public boolean isIncluding()
     {
@@ -256,7 +220,7 @@
      * @param domain the domain
      * @param path the path
      * @param maxAge the maximum age
-     * @param comment the comment (only present on versions > 0)
+     * @param comment the comment (only present on versions &gt; 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)
@@ -285,10 +249,7 @@
         quoteOnlyOrAppend(buf,name,quote_name);
         
         buf.append('=');
-        
-        // Remember name= part to look for other matching set-cookie
-        String name_equals=buf.toString();
-
+       
         // Append the value
         boolean quote_value=isQuoteNeededForCookie(value);
         quoteOnlyOrAppend(buf,value,quote_value);
@@ -300,7 +261,9 @@
         boolean quote_path = has_path && isQuoteNeededForCookie(path);
         
         // Upgrade the version if we have a comment or we need to quote value/path/domain or if they were already quoted
-        if (version==0 && ( comment!=null || quote_name || quote_value || quote_domain || quote_path || isQuoted(name) || isQuoted(value) || isQuoted(path) || isQuoted(domain)))
+        if (version==0 && ( comment!=null || quote_name || quote_value || quote_domain || quote_path ||
+                QuotedStringTokenizer.isQuoted(name) || QuotedStringTokenizer.isQuoted(value) ||
+                QuotedStringTokenizer.isQuoted(path) || QuotedStringTokenizer.isQuoted(domain)))
             version=1;
 
         // Append version
@@ -352,32 +315,12 @@
             buf.append(";Comment=");
             quoteOnlyOrAppend(buf,comment,isQuoteNeededForCookie(comment));
         }
-
-        // remove any existing set-cookie fields of same name
-        Iterator<HttpField> i=_fields.iterator();
-        while (i.hasNext())
-        {
-            HttpField field=i.next();
-            if (field.getHeader()==HttpHeader.SET_COOKIE)
-            {
-                String val = field.getValue();
-                if (val!=null && val.startsWith(name_equals))
-                {
-                    //existing cookie has same name, does it also match domain and path?
-                    if (((!has_domain && !val.contains("Domain")) || (has_domain && val.contains(domain))) &&
-                        ((!has_path && !val.contains("Path")) || (has_path && val.contains(path))))
-                    {
-                        i.remove();
-                    }
-                }
-            }
-        }
         
         // add the set cookie
-        _fields.add(HttpHeader.SET_COOKIE.toString(), buf.toString());
+        _fields.add(HttpHeader.SET_COOKIE, buf.toString());
 
         // Expire responses with set-cookie headers so they do not get cached.
-        _fields.put(HttpHeader.EXPIRES.toString(), DateGenerator.__01Jan1970);
+        _fields.put(__EXPIRES_01JAN1970);
     }
 
 
@@ -440,9 +383,13 @@
             int port = uri.getPort();
             if (port < 0)
                 port = HttpScheme.HTTPS.asString().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
+            
+            // Is it the same server?
+            if (!request.getServerName().equalsIgnoreCase(uri.getHost()))
+                return url;
+            if (request.getServerPort() != port)
+                return url;
+            if (!path.startsWith(request.getContextPath())) //TODO the root context path is "", with which every non null string starts
                 return url;
         }
 
@@ -550,10 +497,17 @@
         if (isIncluding())
             return;
 
+        if (isCommitted())
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Aborting on sendError on committed response {} {}",code,message);
+            code=-1;
+        }
+        
         switch(code)
         {
             case -1:
-                _channel.abort();
+                _channel.abort(new IOException());
                 return;
             case 102:
                 sendProcessing();
@@ -562,9 +516,10 @@
         }
 
         if (isCommitted())
-            LOG.warn("Committed before "+code+" "+message);
-
-        resetBuffer();
+            LOG.warn("cannot sendError("+code+", "+message+") response already committed");
+        else
+            resetBuffer();
+        
         _characterEncoding=null;
         setHeader(HttpHeader.EXPIRES,null);
         setHeader(HttpHeader.LAST_MODIFIED,null);
@@ -619,7 +574,9 @@
                     writer.write(". Reason:\n<pre>    ");
                     writer.write(message);
                     writer.write("</pre>");
-                    writer.write("</p>\n<hr /><i><small>Powered by Jetty://</small></i>");
+                    writer.write("</p>\n<hr />");
+
+                    getHttpChannel().getHttpConfiguration().writePoweredBy(writer,null,"<hr/>");
                     writer.write("\n</body>\n</html>\n");
 
                     writer.flush();
@@ -650,6 +607,7 @@
      * 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.
+     * @throws IOException if unable to send the 102 response
      * @see javax.servlet.http.HttpServletResponse#sendError(int)
      */
     public void sendProcessing() throws IOException
@@ -662,9 +620,9 @@
     
     /**
      * Sends a response with one of the 300 series redirection codes.
-     * @param code
-     * @param location
-     * @throws IOException
+     * @param code the redirect status code
+     * @param location the location to send in <code>Location</code> headers
+     * @throws IOException if unable to send the redirect
      */
     public void sendRedirect(int code, String location) throws IOException
     {
@@ -690,7 +648,7 @@
                 // relative to request
                 String path=_channel.getRequest().getRequestURI();
                 String parent=(path.endsWith("/"))?path:URIUtil.parentPath(path);
-                location=URIUtil.canonicalPath(URIUtil.addPaths(parent,location));
+                location=URIUtil.canonicalPath(URIUtil.addEncodedPaths(parent,location));
                 if (!location.startsWith("/"))
                     buf.append('/');
             }
@@ -784,7 +742,7 @@
     @Override
     public String getHeader(String name)
     {
-        return _fields.getStringField(name);
+        return _fields.get(name);
     }
 
     @Override
@@ -844,7 +802,7 @@
                 _contentLength = value;
         }
     }
-
+    
     @Override
     public void setStatus(int sc)
     {
@@ -879,7 +837,15 @@
     public String getCharacterEncoding()
     {
         if (_characterEncoding == null)
-            _characterEncoding = StringUtil.__ISO_8859_1;
+        {
+            String encoding = MimeTypes.getCharsetAssumedFromContentType(_contentType);
+            if (encoding!=null)
+                return encoding;
+            encoding = MimeTypes.getCharsetInferredFromContentType(_contentType);
+            if (encoding!=null)
+                return encoding;
+            return StringUtil.__ISO_8859_1;
+        }
         return _characterEncoding;
     }
 
@@ -916,26 +882,32 @@
             if (encoding == null)
             {
                 if (_mimeType!=null && _mimeType.isCharsetAssumed())
-                    encoding=_mimeType.getCharset().toString();
+                    encoding=_mimeType.getCharsetString();
                 else
                 {
-                    encoding = MimeTypes.inferCharsetFromContentType(_contentType);
+                    encoding = MimeTypes.getCharsetAssumedFromContentType(_contentType);
                     if (encoding == null)
-                        encoding = StringUtil.__ISO_8859_1;
-                    setCharacterEncoding(encoding,false);
+                    {
+                        encoding = MimeTypes.getCharsetInferredFromContentType(_contentType);
+                        if (encoding == null)
+                            encoding = StringUtil.__ISO_8859_1;
+                        setCharacterEncoding(encoding,EncodingFrom.INFERRED);
+                    }
                 }
             }
             
-            if (_writer != null && _writer.isFor(encoding))
+            Locale locale = getLocale();
+            
+            if (_writer != null && _writer.isFor(locale,encoding))
                 _writer.reopen();
             else
             {
                 if (StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding))
-                    _writer = new ResponseWriter(new Iso88591HttpWriter(_out),encoding);
+                    _writer = new ResponseWriter(new Iso88591HttpWriter(_out),locale,encoding);
                 else if (StringUtil.__UTF8.equalsIgnoreCase(encoding))
-                    _writer = new ResponseWriter(new Utf8HttpWriter(_out),encoding);
+                    _writer = new ResponseWriter(new Utf8HttpWriter(_out),locale,encoding);
                 else
-                    _writer = new ResponseWriter(new EncodingHttpWriter(_out, encoding),encoding);
+                    _writer = new ResponseWriter(new EncodingHttpWriter(_out, encoding),locale,encoding);
             }
             
             // Set the output type at the end, because setCharacterEncoding() checks for it
@@ -1036,19 +1008,19 @@
     @Override
     public void setCharacterEncoding(String encoding)
     {
-        setCharacterEncoding(encoding,true);
+        setCharacterEncoding(encoding,EncodingFrom.SET_CHARACTER_ENCODING);
     }
     
-    private void setCharacterEncoding(String encoding, boolean explicit)
+    private void setCharacterEncoding(String encoding, EncodingFrom from)
     {
         if (isIncluding() || isWriting())
             return;
 
-        if (_outputType == OutputType.NONE && !isCommitted())
+        if (_outputType != OutputType.WRITER && !isCommitted())
         {
             if (encoding == null)
             {
-                _explicitEncoding=false;
+                _encodingFrom=EncodingFrom.NOT_SET;
                 
                 // Clear any encoding.
                 if (_characterEncoding != null)
@@ -1071,11 +1043,11 @@
             else
             {
                 // No, so just add this one to the mimetype
-                _explicitEncoding = explicit;
+                _encodingFrom = from;
                 _characterEncoding = HttpGenerator.__STRICT?encoding:StringUtil.normalizeCharset(encoding);
                 if (_mimeType!=null)
                 {
-                    _contentType=_mimeType.getBaseType().asString()+ "; charset=" + _characterEncoding;
+                    _contentType=_mimeType.getBaseType().asString()+ ";charset=" + _characterEncoding;
                     _mimeType = MimeTypes.CACHE.get(_contentType);
                     if (_mimeType==null || HttpGenerator.__STRICT)
                         _fields.put(HttpHeader.CONTENT_TYPE, _contentType);
@@ -1084,13 +1056,13 @@
                 }
                 else if (_contentType != null)
                 {
-                    _contentType = MimeTypes.getContentTypeWithoutCharset(_contentType) + "; charset=" + _characterEncoding;
+                    _contentType = MimeTypes.getContentTypeWithoutCharset(_contentType) + ";charset=" + _characterEncoding;
                     _fields.put(HttpHeader.CONTENT_TYPE, _contentType);
                 }
             }
         }
     }
-
+    
     @Override
     public void setContentType(String contentType)
     {
@@ -1115,30 +1087,49 @@
             
             String charset;
             if (_mimeType!=null && _mimeType.getCharset()!=null && !_mimeType.isCharsetAssumed())
-                charset=_mimeType.getCharset().toString();
+                charset=_mimeType.getCharsetString();
             else
                 charset = MimeTypes.getCharsetFromContentType(contentType);
 
             if (charset == null)
             {
-                if (_characterEncoding != null)
+                switch (_encodingFrom)
                 {
-                    _contentType = contentType + "; charset=" + _characterEncoding;
-                    _mimeType = null;
+                    case NOT_SET:
+                        break;
+                    case INFERRED:
+                    case SET_CONTENT_TYPE:
+                        if (isWriting())
+                        {
+                            _mimeType=null;
+                            _contentType = _contentType + ";charset=" + _characterEncoding;
+                        }
+                        else
+                        {
+                            _encodingFrom=EncodingFrom.NOT_SET;
+                            _characterEncoding=null;
+                        }
+                        break;
+                    case SET_LOCALE:
+                    case SET_CHARACTER_ENCODING:
+                    {
+                        _contentType = contentType + ";charset=" + _characterEncoding;
+                        _mimeType = null;
+                    }
                 }
             }
-            else if (isWriting() && !charset.equals(_characterEncoding))
+            else if (isWriting() && !charset.equalsIgnoreCase(_characterEncoding))
             {
                 // too late to change the character encoding;
                 _mimeType = null;
                 _contentType = MimeTypes.getContentTypeWithoutCharset(_contentType);
                 if (_characterEncoding != null)
-                    _contentType = _contentType + "; charset=" + _characterEncoding;
+                    _contentType = _contentType + ";charset=" + _characterEncoding;
             }
             else
             {
                 _characterEncoding = charset;
-                _explicitEncoding = true;
+                _encodingFrom = EncodingFrom.SET_CONTENT_TYPE;
             }
 
             if (HttpGenerator.__STRICT || _mimeType==null)
@@ -1149,15 +1140,16 @@
                 _fields.put(_mimeType.getContentTypeField());
             }
         }
-        
     }
 
     @Override
     public void setBufferSize(int size)
     {
-        if (isCommitted() || getContentCount() > 0)
-            throw new IllegalStateException("Committed or content written");
-        if (size <= 0)
+        if (isCommitted())
+            throw new IllegalStateException("cannot set buffer size after response is in committed state");
+        if (getContentCount() > 0)
+            throw new IllegalStateException("cannot set buffer size after response has " + getContentCount() + " bytes already written");
+        if (size < __MIN_BUFFER_SIZE)
             size = __MIN_BUFFER_SIZE;
         _out.setBufferSize(size);
     }
@@ -1178,13 +1170,24 @@
     @Override
     public void reset()
     {
+        reset(false);
+    }
+
+    public void reset(boolean preserveCookies)
+    { 
         resetForForward();
         _status = 200;
         _reason = null;
         _contentLength = -1;
+        
+        List<HttpField> cookies = preserveCookies
+            ?_fields.stream()
+            .filter(f->f.getHeader()==HttpHeader.SET_COOKIE)
+            .collect(Collectors.toList()):null;
+        
         _fields.clear();
 
-        String connection = _channel.getRequest().getHttpFields().getStringField(HttpHeader.CONNECTION);
+        String connection = _channel.getRequest().getHeader(HttpHeader.CONNECTION.asString());  
         if (connection != null)
         {
             for (String value: StringUtil.csvSplit(null,connection,0,connection.length()))
@@ -1211,21 +1214,23 @@
                 }
             }
         }
-    }
 
-    public void reset(boolean preserveCookies)
-    { 
-        if (!preserveCookies)
-            reset();
+        if (preserveCookies)
+            cookies.forEach(f->_fields.add(f));
         else
         {
-            ArrayList<String> cookieValues = new ArrayList<String>(5);
-            Enumeration<String> vals = _fields.getValues(HttpHeader.SET_COOKIE.asString());
-            while (vals.hasMoreElements())
-                cookieValues.add(vals.nextElement());
-            reset();
-            for (String v:cookieValues)
-                _fields.add(HttpHeader.SET_COOKIE, v);
+            Request request = getHttpChannel().getRequest();
+            HttpSession session = request.getSession(false);
+            if (session!=null && session.isNew())
+            {
+                SessionManager sm = request.getSessionManager();
+                if (sm!=null)
+                {
+                    HttpCookie c=sm.getSessionCookie(session,request.getContextPath(),request.isSecure());
+                    if (c!=null)
+                        addCookie(c);
+                }
+            }
         }
     }
 
@@ -1238,24 +1243,28 @@
     @Override
     public void resetBuffer()
     {
-        if (isCommitted())
-            throw new IllegalStateException("Committed");
-
-        switch (_outputType)
-        {
-            case STREAM:
-            case WRITER:
-                _out.reset();
-                break;
-            default:
-        }
-
         _out.resetBuffer();
     }
 
-    protected ResponseInfo newResponseInfo()
+    protected MetaData.Response newResponseMetaData()
     {
-        return new ResponseInfo(_channel.getRequest().getHttpVersion(), _fields, getLongContentLength(), getStatus(), getReason(), _channel.getRequest().isHead());
+        return new MetaData.Response(_channel.getRequest().getHttpVersion(), getStatus(), getReason(), _fields, getLongContentLength());
+    }
+    
+    /** Get the MetaData.Response committed for this response.
+     * This may differ from the meta data in this response for 
+     * exceptional responses (eg 4xx and 5xx responses generated
+     * by the container) and the committedMetaData should be used 
+     * for logging purposes.
+     * @return The committed MetaData or a {@link #newResponseMetaData()}
+     * if not yet committed.
+     */
+    public MetaData.Response getCommittedMetaData()
+    {
+        MetaData.Response meta = _channel.getCommittedMetaData();
+        if (meta==null)
+            return newResponseMetaData();
+        return meta;
     }
 
     @Override
@@ -1281,8 +1290,8 @@
 
         String charset = _channel.getRequest().getContext().getContextHandler().getLocaleEncoding(locale);
 
-        if (charset != null && charset.length() > 0 && !_explicitEncoding)
-            setCharacterEncoding(charset,false);
+        if (charset != null && charset.length() > 0 && __localeOverride.contains(_encodingFrom))
+            setCharacterEncoding(charset,EncodingFrom.SET_LOCALE);
     }
 
     @Override
@@ -1321,27 +1330,73 @@
     }
     
 
-    private static class ResponseWriter extends PrintWriter
+    public void putHeaders(HttpContent content,long contentLength, boolean etag)
     {
-        private final String _encoding;
-        private final HttpWriter _httpWriter;
-        
-        public ResponseWriter(HttpWriter httpWriter,String encoding)
+        HttpField lm = content.getLastModified();
+        if (lm!=null)
+            _fields.put(lm);
+
+        if (contentLength==0)
         {
-            super(httpWriter);
-            _httpWriter=httpWriter;
-            _encoding=encoding;
+            _fields.put(content.getContentLength());
+            _contentLength=content.getContentLengthValue();
+        }
+        else if (contentLength>0)
+        {
+            _fields.putLongField(HttpHeader.CONTENT_LENGTH,contentLength);
+            _contentLength=contentLength;
         }
 
-        public boolean isFor(String encoding)
+        HttpField ct=content.getContentType();
+        if (ct!=null)
         {
-            return _encoding.equalsIgnoreCase(encoding);
+            _fields.put(ct);
+            _contentType=ct.getValue();
+            _characterEncoding=content.getCharacterEncoding();
+            _mimeType=content.getMimeType();
         }
         
-        protected void reopen()
+        HttpField ce=content.getContentEncoding();
+        if (ce!=null)
+            _fields.put(ce);
+        
+        if (etag)
         {
-            super.clearError();
-            out=_httpWriter;
+            HttpField et = content.getETag();
+            if (et!=null)
+                _fields.put(et);
+        }
+    }
+    
+    public static void putHeaders(HttpServletResponse response, HttpContent content, long contentLength, boolean etag)
+    {   
+        long lml=content.getResource().lastModified();
+        if (lml>=0)
+            response.setDateHeader(HttpHeader.LAST_MODIFIED.asString(),lml);
+
+        if (contentLength==0)
+            contentLength=content.getContentLengthValue();
+        if (contentLength >=0)
+        {
+            if (contentLength<Integer.MAX_VALUE)
+                response.setContentLength((int)contentLength);
+            else
+                response.setHeader(HttpHeader.CONTENT_LENGTH.asString(),Long.toString(contentLength));
+        }
+
+        String ct=content.getContentTypeValue();
+        if (ct!=null && response.getContentType()==null)
+            response.setContentType(ct);
+
+        String ce=content.getContentEncodingValue();
+        if (ce!=null)
+            response.setHeader(HttpHeader.CONTENT_ENCODING.asString(),ce);
+        
+        if (etag)
+        {
+            String et=content.getETagValue();
+            if (et!=null)
+                response.setHeader(HttpHeader.ETAG.asString(),et);
         }
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ResponseWriter.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ResponseWriter.java
new file mode 100644
index 0000000..2fc9706
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ResponseWriter.java
@@ -0,0 +1,481 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.InterruptedIOException;
+import java.io.PrintWriter;
+import java.util.Formatter;
+import java.util.Locale;
+
+import javax.servlet.ServletResponse;
+
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** Specialized PrintWriter for servlet Responses
+ * <p>An instance of ResponseWriter is the {@link PrintWriter} subclass returned by {@link Response#getWriter()}.
+ * It differs from the standard {@link PrintWriter} in that:<ul>
+ * <li>It does not support autoflush</li>
+ * <li>The default Locale for {@link #format(String, Object...)} is the locale obtained by {@link ServletResponse#getLocale()}</li>
+ * <li>If a write or print method is called while {@link #checkError()}  returns true, then a {@link RuntimeIOException} is thrown to stop needless iterations.</li>
+ * <li>The writer may be reopen to allow for recycling</li>
+ * </ul>
+ * 
+ */
+public class ResponseWriter extends PrintWriter
+{
+    private static final Logger LOG = Log.getLogger(ResponseWriter.class);
+    private final static String __lineSeparator = System.getProperty("line.separator");
+    private final static String __trueln = "true"+__lineSeparator;
+    private final static String __falseln = "false"+__lineSeparator;
+    
+    private final HttpWriter _httpWriter;
+    private final Locale _locale;
+    private final String _encoding;
+    private IOException _ioException;
+    private boolean _isClosed = false;
+    private Formatter _formatter;
+
+    public ResponseWriter(HttpWriter httpWriter,Locale locale,String encoding)
+    {
+        super(httpWriter,false);
+        _httpWriter=httpWriter;
+        _locale=locale;
+        _encoding=encoding;
+    }
+
+    public boolean isFor(Locale locale, String encoding)
+    {
+        if (_locale==null && locale!=null)
+            return false;
+        if (_encoding==null && encoding!=null)
+            return false;
+        return _encoding.equalsIgnoreCase(encoding) && _locale.equals(locale);
+    }
+
+    protected void reopen()
+    {
+        synchronized (lock)
+        {
+            _isClosed=false;
+            clearError();
+            out=_httpWriter;
+        }
+    }
+
+    @Override
+    protected void clearError()
+    {
+        synchronized (lock)
+        {
+            _ioException=null;
+            super.clearError();
+        }
+    }
+    
+    @Override
+    public boolean checkError()
+    {
+        synchronized (lock)
+        {
+            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);
+        }
+
+        if (LOG.isDebugEnabled())
+            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 EofException("Stream closed");
+    }
+
+    @Override
+    public void flush()
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.flush();
+            }
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    @Override
+    public void close()
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                out.close();
+                _isClosed = true;
+            }
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    @Override
+    public void write(int c)
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.write(c);
+            }
+        }
+        catch (InterruptedIOException ex)
+        {
+            LOG.debug(ex);
+            Thread.currentThread().interrupt();
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+    
+    @Override
+    public void write(char buf[], int off, int len)
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.write(buf,off,len);
+            }
+        }
+        catch (InterruptedIOException ex)
+        {
+            LOG.debug(ex);
+            Thread.currentThread().interrupt();
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    @Override
+    public void write(char buf[])
+    { 
+        this.write(buf,0,buf.length);
+    }
+
+    @Override
+    public void write(String s, int off, int len)
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.write(s,off,len);
+            }
+        }
+        catch (InterruptedIOException ex)
+        {
+            LOG.debug(ex);
+            Thread.currentThread().interrupt();
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    @Override
+    public void write(String s)
+    {
+        this.write(s,0,s.length());
+    }
+
+    @Override
+    public void print(boolean b)
+    {
+        this.write(b?"true":"false");
+    }
+    
+    @Override
+    public void print(char c)
+    {
+        this.write(c);
+    }
+
+    @Override
+    public void print(int i)
+    {
+        this.write(String.valueOf(i));
+    }
+
+    @Override
+    public void print(long l)
+    {
+        this.write(String.valueOf(l));
+    }
+
+    @Override
+    public void print(float f)
+    {
+        this.write(String.valueOf(f));
+    }
+
+    @Override
+    public void print(double d)
+    {
+        this.write(String.valueOf(d));
+    }
+
+    @Override
+    public void print(char s[])
+    {
+        this.write(s);
+    }
+
+    @Override
+    public void print(String s)
+    {
+        if (s == null)
+            s = "null";
+        this.write(s);
+    }
+
+    @Override
+    public void print(Object obj)
+    {
+        this.write(String.valueOf(obj));
+    }
+
+    @Override
+    public void println()
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.write(__lineSeparator);
+            }
+        }
+        catch (InterruptedIOException ex)
+        {
+            LOG.debug(ex);
+            Thread.currentThread().interrupt();
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    @Override
+    public void println(boolean b)
+    {
+        println(b?__trueln:__falseln);
+    }
+
+    @Override
+    public void println(char c)
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.write(c);
+            }
+        }
+        catch (InterruptedIOException ex)
+        {
+            LOG.debug(ex);
+            Thread.currentThread().interrupt();
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    @Override
+    public void println(int x)
+    {
+        this.println(String.valueOf(x));
+    }
+
+    @Override
+    public void println(long x)
+    {
+        this.println(String.valueOf(x));
+    }
+    
+    @Override
+    public void println(float x)
+    {
+        this.println(String.valueOf(x));
+    }
+
+    @Override
+    public void println(double x)
+    {
+        this.println(String.valueOf(x));
+    }
+
+    @Override
+    public void println(char s[])
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.write(s,0,s.length);
+                out.write(__lineSeparator);
+            }
+        }
+        catch (InterruptedIOException ex)
+        {
+            LOG.debug(ex);
+            Thread.currentThread().interrupt();
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    @Override
+    public void println(String s)
+    {
+        if (s == null)
+            s = "null";
+        
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.write(s,0,s.length());
+                out.write(__lineSeparator);
+            }
+        }
+        catch (InterruptedIOException ex)
+        {
+            LOG.debug(ex);
+            Thread.currentThread().interrupt();
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    @Override
+    public void println(Object x)
+    {
+        this.println(String.valueOf(x));
+    }
+
+    @Override
+    public PrintWriter printf(String format, Object... args)
+    {
+        return format(_locale,format,args);
+    }
+
+    @Override
+    public PrintWriter printf(Locale l, String format, Object... args)
+    {
+        return format(l,format,args);
+    }
+
+    @Override
+    public PrintWriter format(String format, Object... args)
+    {
+        return format(_locale,format,args);
+    }
+
+    @Override
+    public PrintWriter format(Locale l, String format, Object... args)
+    { 
+        try 
+        {
+            synchronized (lock) 
+            {
+                isOpen();
+                if ((_formatter == null) || (_formatter.locale() != l))
+                    _formatter = new Formatter(this, l);
+                _formatter.format(l, format, args);
+            }
+        } 
+        catch (InterruptedIOException ex)
+        {
+            LOG.debug(ex);
+            Thread.currentThread().interrupt();
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+        return this;
+    }
+
+    
+    
+}
\ No newline at end of file
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java
index eac2fac..c683ab1 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java
@@ -19,30 +19,37 @@
 package org.eclipse.jetty.server;
 
 import java.security.cert.X509Certificate;
+import java.util.concurrent.TimeUnit;
 
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLSession;
 import javax.servlet.ServletRequest;
 
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.PreEncodedHttpField;
 import org.eclipse.jetty.io.ssl.SslConnection;
 import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
 import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.annotation.Name;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.ssl.SniX509ExtendedKeyManager;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.ssl.X509;
 
-
-/* ------------------------------------------------------------ */
-/** Customizer that extracts the attribute from an {@link SSLContext}
+/**
+ * <p>Customizer that extracts the attribute from an {@link SSLContext}
  * and sets them on the request with {@link ServletRequest#setAttribute(String, Object)}
- * according to Servlet Specification Requirements.
+ * according to Servlet Specification Requirements.</p>
  */
 public class SecureRequestCustomizer implements HttpConfiguration.Customizer
 {
     private static final Logger LOG = Log.getLogger(SecureRequestCustomizer.class);
-    
+
     /**
      * The name of the SSLSession attribute that will contain any cached information.
      */
@@ -50,46 +57,184 @@
 
     private String sslSessionAttribute = "org.eclipse.jetty.servlet.request.ssl_session";
 
+    private boolean _sniHostCheck;
+    private long _stsMaxAge=-1;
+    private boolean _stsIncludeSubDomains;
+    private HttpField _stsField;
+
+    public SecureRequestCustomizer()
+    {
+        this(true);
+    }
+
+    public SecureRequestCustomizer(@Name("sniHostCheck")boolean sniHostCheck)
+    {
+        this(sniHostCheck,-1,false);
+    }
+
+    /**
+     * @param sniHostCheck True if the SNI Host name must match.
+     * @param stsMaxAgeSeconds The max age in seconds for a Strict-Transport-Security response header. If set less than zero then no header is sent.
+     * @param stsIncludeSubdomains If true, a include subdomain property is sent with any Strict-Transport-Security header
+     */
+    public SecureRequestCustomizer(
+            @Name("sniHostCheck")boolean sniHostCheck,
+            @Name("stsMaxAgeSeconds")long stsMaxAgeSeconds,
+            @Name("stsIncludeSubdomains")boolean stsIncludeSubdomains)
+    {
+        _sniHostCheck=sniHostCheck;
+        _stsMaxAge=stsMaxAgeSeconds;
+        _stsIncludeSubDomains=stsIncludeSubdomains;
+        formatSTS();
+    }
+
+    /**
+     * @return True if the SNI Host name must match.
+     */
+    public boolean isSniHostCheck()
+    {
+        return _sniHostCheck;
+    }
+
+    /**
+     * @param sniHostCheck  True if the SNI Host name must match.
+     */
+    public void setSniHostCheck(boolean sniHostCheck)
+    {
+        _sniHostCheck = sniHostCheck;
+    }
+
+    /**
+     * @return The max age in seconds for a Strict-Transport-Security response header. If set less than zero then no header is sent.
+     */
+    public long getStsMaxAge()
+    {
+        return _stsMaxAge;
+    }
+
+    /**
+     * Set the Strict-Transport-Security max age.
+     * @param stsMaxAgeSeconds The max age in seconds for a Strict-Transport-Security response header. If set less than zero then no header is sent.
+     */
+    public void setStsMaxAge(long stsMaxAgeSeconds)
+    {
+        _stsMaxAge = stsMaxAgeSeconds;
+        formatSTS();
+    }
+
+    /**
+     * Convenience method to call {@link #setStsMaxAge(long)}
+     * @param period The period in units
+     * @param units The {@link TimeUnit} of the period
+     */
+    public void setStsMaxAge(long period,TimeUnit units)
+    {
+        _stsMaxAge = units.toSeconds(period);
+        formatSTS();
+    }
+
+    /**
+     * @return true if a include subdomain property is sent with any Strict-Transport-Security header
+     */
+    public boolean isStsIncludeSubDomains()
+    {
+        return _stsIncludeSubDomains;
+    }
+
+    /**
+     * @param stsIncludeSubDomains If true, a include subdomain property is sent with any Strict-Transport-Security header
+     */
+    public void setStsIncludeSubDomains(boolean stsIncludeSubDomains)
+    {
+        _stsIncludeSubDomains = stsIncludeSubDomains;
+        formatSTS();
+    }
+
+    private void formatSTS()
+    {
+        if (_stsMaxAge<0)
+            _stsField=null;
+        else
+            _stsField=new PreEncodedHttpField(HttpHeader.STRICT_TRANSPORT_SECURITY,String.format("max-age=%d%s",_stsMaxAge,_stsIncludeSubDomains?"; includeSubDomains":""));
+    }
+
     @Override
     public void customize(Connector connector, HttpConfiguration channelConfig, Request request)
     {
         if (request.getHttpChannel().getEndPoint() instanceof DecryptedEndPoint)
         {
-            request.setScheme(HttpScheme.HTTPS.asString());
-            request.setSecure(true);
+
+            if (request.getHttpURI().getScheme()==null)
+                request.setScheme(HttpScheme.HTTPS.asString());
+
             SslConnection.DecryptedEndPoint ssl_endp = (DecryptedEndPoint)request.getHttpChannel().getEndPoint();
             SslConnection sslConnection = ssl_endp.getSslConnection();
             SSLEngine sslEngine=sslConnection.getSSLEngine();
             customize(sslEngine,request);
         }
+
+        if (HttpScheme.HTTPS.is(request.getScheme()))
+            customizeSecure(request);
     }
 
-    /* ------------------------------------------------------------ */
-    /*
-     * Customise the request attributes to be set for SSL requests. <br>
+
+    /**
+     * Customizes the request attributes for general secure settings.
+     * The default impl calls {@link Request#setSecure(boolean)} with true
+     * and sets a response header if the Strict-Transport-Security options
+     * are set.
+     * @param request the request being customized
+     */
+    protected void customizeSecure(Request request)
+    {
+        request.setSecure(true);
+
+        if (_stsField!=null)
+            request.getResponse().getHttpFields().add(_stsField);
+    }
+
+    /**
+     * <p>
+     * Customizes the request attributes to be set for SSL requests.
+     * </p>
+     * <p>
      * The requirements of the Servlet specs are:
+     * </p>
      * <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>
+     * <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 sslEngine
+     *            the sslEngine to be customized.
      * @param request
-     *                HttpRequest to be customised.
+     *            HttpRequest to be customized.
      */
-    public void customize(SSLEngine sslEngine, Request request)
+    protected void customize(SSLEngine sslEngine, Request request)
     {
         request.setScheme(HttpScheme.HTTPS.asString());
         SSLSession sslSession = sslEngine.getSession();
 
+        if (_sniHostCheck)
+        {
+            String name = request.getServerName();
+            X509 x509 = (X509)sslSession.getValue(SniX509ExtendedKeyManager.SNI_X509);
+
+            if (x509!=null && !x509.matches(name))
+            {
+                LOG.warn("Host {} does not match SNI {}",name,x509);
+                throw new BadMessageException(400,"Host does not match SNI");
+            }
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("Host {} matched SNI {}",name,x509);
+        }
+
         try
         {
             String cipherSuite=sslSession.getCipherSuite();
@@ -104,9 +249,9 @@
                 certs=cachedInfo.getCerts();
                 idStr=cachedInfo.getIdStr();
             }
-            else 
+            else
             {
-                keySize=new Integer(SslContextFactory.deduceKeyLength(cipherSuite));
+                keySize=SslContextFactory.deduceKeyLength(cipherSuite);
                 certs=SslContextFactory.getCertChain(sslSession);
                 byte[] bytes = sslSession.getId();
                 idStr = TypeUtil.toHexString(bytes);
@@ -120,14 +265,16 @@
             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);
-            request.setAttribute(getSslSessionAttribute(), sslSession);
+            String sessionAttribute = getSslSessionAttribute();
+            if (sessionAttribute != null && !sessionAttribute.isEmpty())
+                request.setAttribute(sessionAttribute, sslSession);
         }
         catch (Exception e)
         {
             LOG.warn(Log.EXCEPTION,e);
         }
     }
-    
+
     public void setSslSessionAttribute(String attribute)
     {
         this.sslSessionAttribute = attribute;
@@ -143,10 +290,7 @@
     {
         return String.format("%s@%x",this.getClass().getSimpleName(),hashCode());
     }
-    
-    /* ------------------------------------------------------------ */
-    /* ------------------------------------------------------------ */
-    /* ------------------------------------------------------------ */
+
     /**
      * Simple bundle of information that is cached in the SSLSession. Stores the
      * effective keySize and the client certificate chain.
@@ -179,7 +323,4 @@
             return _idStr;
         }
     }
-
-
-
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
index a762b3a..6ff8e40 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
@@ -29,6 +29,7 @@
 import java.util.Enumeration;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
@@ -44,6 +45,7 @@
 import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.PreEncodedHttpField;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.HandlerWrapper;
 import org.eclipse.jetty.server.handler.StatisticsHandler;
@@ -60,6 +62,7 @@
 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.Locker;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.eclipse.jetty.util.thread.ShutdownThread;
 import org.eclipse.jetty.util.thread.ThreadPool;
@@ -84,9 +87,12 @@
     private boolean _stopAtShutdown;
     private boolean _dumpAfterStart=false;
     private boolean _dumpBeforeStop=false;
-    
+    private RequestLog _requestLog;
+
+    private final Locker _dateLocker = new Locker();
     private volatile DateField _dateField;
-    
+
+
     /* ------------------------------------------------------------ */
     public Server()
     {
@@ -108,8 +114,11 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** Convenience constructor
+    /**
+     * Convenience constructor
+     * <p>
      * Creates server and a {@link ServerConnector} at the passed address.
+     * @param addr the inet socket address to create the connector from
      */
     public Server(@Name("address")InetSocketAddress addr)
     {
@@ -120,8 +129,6 @@
         setConnectors(new Connector[]{connector});
     }
 
-
-
     /* ------------------------------------------------------------ */
     public Server(@Name("threadpool") ThreadPool pool)
     {
@@ -130,6 +137,18 @@
         setServer(this);
     }
 
+    /* ------------------------------------------------------------ */
+    public RequestLog getRequestLog()
+    {
+        return _requestLog;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setRequestLog(RequestLog requestLog)
+    {
+        updateBean(_requestLog,requestLog);
+        _requestLog = requestLog;
+    }
 
     /* ------------------------------------------------------------ */
     @ManagedAttribute("version of this server")
@@ -143,8 +162,8 @@
     {
         return _stopAtShutdown;
     }
-   
-    
+
+
     /* ------------------------------------------------------------ */
     /**
      * Set a graceful stop time.
@@ -291,15 +310,15 @@
         long now=System.currentTimeMillis();
         long seconds = now/1000;
         DateField df = _dateField;
-        
+
         if (df==null || df._seconds!=seconds)
         {
-            synchronized (this) // Trade some contention for less garbage
+            try(Locker.Lock lock = _dateLocker.lock())
             {
                 df = _dateField;
                 if (df==null || df._seconds!=seconds)
                 {
-                    HttpField field=new HttpGenerator.CachedHttpField(HttpHeader.DATE,DateGenerator.formatDate(now));
+                    HttpField field=new PreEncodedHttpField(HttpHeader.DATE,DateGenerator.formatDate(now));
                     _dateField=new DateField(seconds,field);
                     return field;
                 }
@@ -320,35 +339,51 @@
         //Register the Server with the handler thread for receiving
         //remote stop commands
         ShutdownMonitor.register(this);
-        
+
         //Start a thread waiting to receive "stop" commands.
         ShutdownMonitor.getInstance().start(); // initialize
 
         LOG.info("jetty-" + getVersion());
+        if (!Jetty.STABLE)
+        {
+            LOG.warn("THIS IS NOT A STABLE RELEASE! DO NOT USE IN PRODUCTION!");
+            LOG.warn("Download a stable release from http://download.eclipse.org/jetty/");
+        }
+        
         HttpGenerator.setJettyVersion(HttpConfiguration.SERVER_VERSION);
-        MultiException mex=new MultiException();
+
 
         // check size of thread pool
         SizedThreadPool pool = getBean(SizedThreadPool.class);
         int max=pool==null?-1:pool.getMaxThreads();
         int selectors=0;
         int acceptors=0;
-        if (mex.size()==0)
+
+        for (Connector connector : _connectors)
         {
-            for (Connector connector : _connectors)
-            {
-                if (connector instanceof AbstractConnector)
-                    acceptors+=((AbstractConnector)connector).getAcceptors();
-                    
-                if (connector instanceof ServerConnector)
-                    selectors+=((ServerConnector)connector).getSelectorManager().getSelectorCount();
-            }
+            if (!(connector instanceof AbstractConnector))
+                continue;
+
+            AbstractConnector abstractConnector = (AbstractConnector) connector;
+            Executor connectorExecutor = connector.getExecutor();
+
+            if (connectorExecutor != pool)
+                // Do not count the selectors and acceptors from this connector at server level, because connector uses dedicated executor.
+                continue;
+
+            acceptors += abstractConnector.getAcceptors();
+
+            if (connector instanceof ServerConnector)
+                selectors+=((ServerConnector)connector).getSelectorManager().getSelectorCount();
+
         }
 
+
         int needed=1+selectors+acceptors;
         if (max>0 && needed>max)
             throw new IllegalStateException(String.format("Insufficient threads: max=%d < needed(acceptors=%d + selectors=%d + request=1)",max,acceptors,selectors));
-        
+
+        MultiException mex=new MultiException();
         try
         {
             super.doStart();
@@ -362,7 +397,7 @@
         for (Connector connector : _connectors)
         {
             try
-            {   
+            {
                 connector.start();
             }
             catch(Throwable e)
@@ -370,7 +405,7 @@
                 mex.add(e);
             }
         }
-        
+
         if (isDumpAfterStart())
             dumpStdErr();
 
@@ -394,6 +429,9 @@
         if (isDumpBeforeStop())
             dumpStdErr();
 
+        if (LOG.isDebugEnabled())
+            LOG.debug("doStop {}",this);
+
         MultiException mex=new MultiException();
 
         // list if graceful futures
@@ -404,7 +442,6 @@
             futures.add(connector.shutdown());
 
         // Then tell the contexts that we are shutting down
-        
         Handler[] gracefuls = getChildHandlersByClass(Graceful.class);
         for (Handler graceful : gracefuls)
             futures.add(((Graceful)graceful).shutdown());
@@ -462,14 +499,12 @@
 
         if (getStopAtShutdown())
             ShutdownThread.deregister(this);
-        
+
         //Unregister the Server with the handler thread for receiving
         //remote stop commands as we are stopped already
         ShutdownMonitor.deregister(this);
-        
 
         mex.ifExceptionThrow();
-
     }
 
     /* ------------------------------------------------------------ */
@@ -478,14 +513,14 @@
      * 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(HttpChannel<?> connection) throws IOException, ServletException
+    public void handle(HttpChannel channel) throws IOException, ServletException
     {
-        final String target=connection.getRequest().getPathInfo();
-        final Request request=connection.getRequest();
-        final Response response=connection.getResponse();
+        final String target=channel.getRequest().getPathInfo();
+        final Request request=channel.getRequest();
+        final Response response=channel.getResponse();
 
         if (LOG.isDebugEnabled())
-            LOG.debug(request.getDispatcherType()+" "+request.getMethod()+" "+target+" on "+connection);
+            LOG.debug("{} {} {} on {}", request.getDispatcherType(), request.getMethod(), target, channel);
 
         if (HttpMethod.OPTIONS.is(request.getMethod()) || "*".equals(target))
         {
@@ -499,7 +534,7 @@
             handle(target, request, request, response);
 
         if (LOG.isDebugEnabled())
-            LOG.debug("RESPONSE "+target+"  "+connection.getResponse().getStatus()+" handled="+request.isHandled());
+            LOG.debug("handled={} async={} committed={} on {}", request.isHandled(),request.isAsyncStarted(),response.isCommitted(),channel);
     }
 
     /* ------------------------------------------------------------ */
@@ -515,24 +550,24 @@
      * 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(HttpChannel<?> connection) throws IOException, ServletException
+    public void handleAsync(HttpChannel channel) throws IOException, ServletException
     {
-        final HttpChannelState state = connection.getRequest().getHttpChannelState();
+        final HttpChannelState state = channel.getRequest().getHttpChannelState();
         final AsyncContextEvent event = state.getAsyncContextEvent();
 
-        final Request baseRequest=connection.getRequest();
+        final Request baseRequest=channel.getRequest();
         final String path=event.getPath();
-        
+
         if (path!=null)
         {
             // this is a dispatch with a path
             ServletContext context=event.getServletContext();
-            HttpURI uri = new HttpURI(URIUtil.addPaths(context==null?null:context.getContextPath(), path));            
-            baseRequest.setUri(uri);
-            baseRequest.setRequestURI(null);
+            String query=baseRequest.getQueryString();
+            baseRequest.setURIPathQuery(URIUtil.addEncodedPaths(context==null?null:URIUtil.encodePath(context.getContextPath()), path));
+            HttpURI uri = baseRequest.getHttpURI();
             baseRequest.setPathInfo(uri.getDecodedPath());
             if (uri.getQuery()!=null)
-                baseRequest.mergeQueryParameters(uri.getQuery(), true); //we have to assume dispatch path and query are UTF8
+                baseRequest.mergeQueryParameters(query,uri.getQuery(), true); //we have to assume dispatch path and query are UTF8
         }
 
         final String target=baseRequest.getPathInfo();
@@ -540,14 +575,10 @@
         final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
 
         if (LOG.isDebugEnabled())
-        {
-            LOG.debug(request.getDispatcherType()+" "+request.getMethod()+" "+target+" on "+connection);
-            handle(target, baseRequest, request, response);
-            LOG.debug("RESPONSE "+target+"  "+connection.getResponse().getStatus());
-        }
-        else
-            handle(target, baseRequest, request, response);
-
+            LOG.debug("{} {} {} on {}", request.getDispatcherType(), request.getMethod(), target, channel);
+        handle(target, baseRequest, request, response);
+        if (LOG.isDebugEnabled())
+            LOG.debug("handledAsync={} async={} committed={} on {}", channel.getRequest().isHandled(),request.isAsyncStarted(),response.isCommitted(),channel);
     }
 
     /* ------------------------------------------------------------ */
@@ -628,7 +659,8 @@
     @Override
     public void setAttribute(String name, Object attribute)
     {
-        addBean(attribute);
+        Object old=_attributes.getAttribute(name);
+        updateBean(old,attribute);        
         _attributes.setAttribute(name, attribute);
     }
 
@@ -636,7 +668,6 @@
     /**
      * @return The URI of the first {@link NetworkConnector} and first {@link ContextHandler}, or null
      */
-    @SuppressWarnings("resource")
     public URI getURI()
     {
         NetworkConnector connector=null;
@@ -656,7 +687,10 @@
 
         try
         {
-            String scheme=connector.getDefaultConnectionFactory().getProtocol().startsWith("SSL-")?"https":"http";
+            String protocol = connector.getDefaultConnectionFactory().getProtocol();
+            String scheme="http";
+            if (protocol.startsWith("SSL-") || protocol.equals("SSL"))
+                scheme = "https";
 
             String host=connector.getHost();
             if (context!=null && context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
@@ -708,6 +742,6 @@
             _seconds = seconds;
             _dateField = dateField;
         }
-        
+
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnectionStatistics.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnectionStatistics.java
new file mode 100644
index 0000000..33f021b
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnectionStatistics.java
@@ -0,0 +1,34 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.io.ConnectionStatistics;
+import org.eclipse.jetty.util.component.Container;
+
+public class ServerConnectionStatistics extends ConnectionStatistics
+{
+    public static void addToAllConnectors(Server server)
+    {
+        for (Connector connector : server.getConnectors())
+        {
+            if (connector instanceof Container)
+                ((Container)connector).addBean(new ConnectionStatistics());
+        }
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java
index 128b449..49bd3e5 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java
@@ -20,10 +20,12 @@
 
 import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.SocketException;
 import java.nio.channels.Channel;
 import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
 import java.nio.channels.ServerSocketChannel;
 import java.nio.channels.SocketChannel;
 import java.util.concurrent.Executor;
@@ -32,25 +34,26 @@
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ManagedSelector;
 import org.eclipse.jetty.io.SelectChannelEndPoint;
 import org.eclipse.jetty.io.SelectorManager;
-import org.eclipse.jetty.io.SelectorManager.ManagedSelector;
+import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.annotation.Name;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
 import org.eclipse.jetty.util.thread.Scheduler;
 
 /**
  * This {@link Connector} implementation is the primary connector for the
  * Jetty server over TCP/IP.  By the use of various {@link ConnectionFactory} instances it is able
- * to accept connections for HTTP, SPDY and WebSocket, either directly or over SSL.
+ * to accept connections for HTTP, HTTP/2 and WebSocket, either directly or over SSL.
  * <p>
  * The connector is a fully asynchronous NIO based implementation that by default will
  * use all the commons services (eg {@link Executor}, {@link Scheduler})  of the
  * passed {@link Server} instance, but all services may also be constructor injected
  * into the connector so that it may operate with dedicated or otherwise shared services.
- * <p>
  * <h2>Connection Factories</h2>
  * Various convenience constructors are provided to assist with common configurations of
  * ConnectionFactories, whose generic use is described in {@link AbstractConnector}.
@@ -58,7 +61,6 @@
  * default to use a {@link HttpConnectionFactory}.  If an non null {@link SslContextFactory}
  * instance is passed, then this used to instantiate a {@link SslConnectionFactory} which is
  * prepended to the other passed or default factories.
- * <p>
  * <h2>Selectors</h2>
  * The connector will use the {@link Executor} service to execute a number of Selector Tasks,
  * which are implemented to each use a NIO {@link Selector} instance to asynchronously
@@ -71,7 +73,6 @@
  * The default number of selectors is equal to the number of processors available to the JVM,
  * which should allow optimal performance even if all the connections used are performing
  * significant non-blocking work in the callback tasks.
- *
  */
 @ManagedObject("HTTP connector using NIO ByteChannels and Selectors")
 public class ServerConnector extends AbstractNetworkConnector
@@ -84,27 +85,24 @@
     private volatile boolean _reuseAddress = true;
     private volatile int _lingerTime = -1;
 
-
-    /* ------------------------------------------------------------ */
-    /** HTTP Server Connection.
+    /**
      * <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the only factory.</p>
-     * @param server The {@link Server} this connector will accept connection for. 
+     * @param server The {@link Server} this connector will accept connection for.
      */
     public ServerConnector(
         @Name("server") Server server)
     {
         this(server,null,null,null,-1,-1,new HttpConnectionFactory());
     }
-    
-    /* ------------------------------------------------------------ */
-    /** HTTP Server Connection.
+
+    /**
      * <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the only factory.</p>
-     * @param server The {@link Server} this connector will accept connection for. 
-     * @param acceptors 
-     *          the number of acceptor threads to use, or -1 for a default value. Acceptors accept new TCP/IP connections.  If 0, then 
+     * @param server The {@link Server} this connector will accept connection for.
+     * @param acceptors
+     *          the number of acceptor threads to use, or -1 for a default value. Acceptors accept new TCP/IP connections.  If 0, then
      *          the selector threads are used to accept connections.
      * @param selectors
-     *          the number of selector threads, or <=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
+     *          the number of selector threads, or &lt;=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
      */
     public ServerConnector(
         @Name("server") Server server,
@@ -113,16 +111,15 @@
     {
         this(server,null,null,null,acceptors,selectors,new HttpConnectionFactory());
     }
-    
-    /* ------------------------------------------------------------ */
-    /** HTTP Server Connection.
+
+    /**
      * <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the only factory.</p>
-     * @param server The {@link Server} this connector will accept connection for. 
-     * @param acceptors 
-     *          the number of acceptor threads to use, or -1 for a default value. Acceptors accept new TCP/IP connections.  If 0, then 
+     * @param server The {@link Server} this connector will accept connection for.
+     * @param acceptors
+     *          the number of acceptor threads to use, or -1 for a default value. Acceptors accept new TCP/IP connections.  If 0, then
      *          the selector threads are used to accept connections.
      * @param selectors
-     *          the number of selector threads, or <=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
+     *          the number of selector threads, or &lt;=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
      * @param factories Zero or more {@link ConnectionFactory} instances used to create and configure connections.
      */
     public ServerConnector(
@@ -134,10 +131,9 @@
         this(server,null,null,null,acceptors,selectors,factories);
     }
 
-    /* ------------------------------------------------------------ */
-    /** Generic Server Connection with default configuration.
+    /**
      * <p>Construct a Server Connector with the passed Connection factories.</p>
-     * @param server The {@link Server} this connector will accept connection for. 
+     * @param server The {@link Server} this connector will accept connection for.
      * @param factories Zero or more {@link ConnectionFactory} instances used to create and configure connections.
      */
     public ServerConnector(
@@ -147,11 +143,10 @@
         this(server,null,null,null,-1,-1,factories);
     }
 
-    /* ------------------------------------------------------------ */
-    /** HTTP Server Connection.
+    /**
      * <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the primary protocol</p>.
-     * @param server The {@link Server} this connector will accept connection for. 
-     * @param sslContextFactory If non null, then a {@link SslConnectionFactory} is instantiated and prepended to the 
+     * @param server The {@link Server} this connector will accept connection for.
+     * @param sslContextFactory If non null, then a {@link SslConnectionFactory} is instantiated and prepended to the
      * list of HTTP Connection Factory.
      */
     public ServerConnector(
@@ -161,17 +156,16 @@
         this(server,null,null,null,-1,-1,AbstractConnectionFactory.getFactories(sslContextFactory,new HttpConnectionFactory()));
     }
 
-    /* ------------------------------------------------------------ */
-    /** HTTP Server Connection.
+    /**
      * <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the primary protocol</p>.
-     * @param server The {@link Server} this connector will accept connection for. 
-     * @param sslContextFactory If non null, then a {@link SslConnectionFactory} is instantiated and prepended to the 
+     * @param server The {@link Server} this connector will accept connection for.
+     * @param sslContextFactory If non null, then a {@link SslConnectionFactory} is instantiated and prepended to the
      * list of HTTP Connection Factory.
-     * @param acceptors 
-     *          the number of acceptor threads to use, or -1 for a default value. Acceptors accept new TCP/IP connections.  If 0, then 
+     * @param acceptors
+     *          the number of acceptor threads to use, or -1 for a default value. Acceptors accept new TCP/IP connections.  If 0, then
      *          the selector threads are used to accept connections.
      * @param selectors
-     *          the number of selector threads, or <=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
+     *          the number of selector threads, or &lt;=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
      */
     public ServerConnector(
         @Name("server") Server server,
@@ -182,10 +176,9 @@
         this(server,null,null,null,acceptors,selectors,AbstractConnectionFactory.getFactories(sslContextFactory,new HttpConnectionFactory()));
     }
 
-    /* ------------------------------------------------------------ */
-    /** Generic SSL Server Connection.
-     * @param server The {@link Server} this connector will accept connection for. 
-     * @param sslContextFactory If non null, then a {@link SslConnectionFactory} is instantiated and prepended to the 
+    /**
+     * @param server The {@link Server} this connector will accept connection for.
+     * @param sslContextFactory If non null, then a {@link SslConnectionFactory} is instantiated and prepended to the
      * list of ConnectionFactories, with the first factory being the default protocol for the SslConnectionFactory.
      * @param factories Zero or more {@link ConnectionFactory} instances used to create and configure connections.
      */
@@ -194,25 +187,25 @@
         @Name("sslContextFactory") SslContextFactory sslContextFactory,
         @Name("factories") ConnectionFactory... factories)
     {
-        this(server,null,null,null,-1,-1,AbstractConnectionFactory.getFactories(sslContextFactory,factories));
+        this(server, null, null, null, -1, -1, AbstractConnectionFactory.getFactories(sslContextFactory, factories));
     }
 
-    /** Generic Server Connection.
-     * @param server    
-     *          The server this connector will be accept connection for.  
-     * @param executor  
+    /**
+     * @param server
+     *          The server this connector will be accept connection for.
+     * @param executor
      *          An executor used to run tasks for handling requests, acceptors and selectors.
      *          If null then use the servers executor
-     * @param scheduler 
+     * @param scheduler
      *          A scheduler used to schedule timeouts. If null then use the servers scheduler
      * @param bufferPool
      *          A ByteBuffer pool used to allocate buffers.  If null then create a private pool with default configuration.
-     * @param acceptors 
-     *          the number of acceptor threads to use, or -1 for a default value. Acceptors accept new TCP/IP connections.  If 0, then 
+     * @param acceptors
+     *          the number of acceptor threads to use, or -1 for a default value. Acceptors accept new TCP/IP connections.  If 0, then
      *          the selector threads are used to accept connections.
      * @param selectors
-     *          the number of selector threads, or <=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
-     * @param factories 
+     *          the number of selector threads, or &lt;=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
+     * @param factories
      *          Zero or more {@link ConnectionFactory} instances used to create and configure connections.
      */
     public ServerConnector(
@@ -225,9 +218,15 @@
         @Name("factories") ConnectionFactory... factories)
     {
         super(server,executor,scheduler,bufferPool,acceptors,factories);
-        _manager = new ServerConnectorManager(getExecutor(), getScheduler(), 
+        _manager = newSelectorManager(getExecutor(), getScheduler(),
             selectors>0?selectors:Math.max(1,Math.min(4,Runtime.getRuntime().availableProcessors()/2)));
         addBean(_manager, true);
+        setAcceptorPriorityDelta(-2);
+    }
+
+    protected SelectorManager newSelectorManager(Executor executor, Scheduler scheduler, int selectors)
+    {
+        return new ServerConnectorManager(executor, scheduler, selectors);
     }
 
     @Override
@@ -249,29 +248,26 @@
         return channel!=null && channel.isOpen();
     }
 
-
-    @ManagedAttribute("The priority delta to apply to selector threads")
+    /**
+     * @return the selector priority delta
+     * @deprecated not implemented
+     */
+    @Deprecated
     public int getSelectorPriorityDelta()
     {
         return _manager.getSelectorPriorityDelta();
     }
 
     /**
-     * Sets the selector thread priority delta to the given amount.
-     * <p>This allows the selector threads to run at a different priority.
-     * Typically this would be used to lower the priority to give preference 
-     * to handling previously accepted connections rather than accepting
-     * new connections.</p>
-     *
-     * @param selectorPriorityDelta the amount to set the thread priority delta to
-     *                              (may be negative)
-     * @see Thread#getPriority()
+     * @param selectorPriorityDelta the selector priority delta
+     * @deprecated not implemented
      */
+    @Deprecated
     public void setSelectorPriorityDelta(int selectorPriorityDelta)
     {
         _manager.setSelectorPriorityDelta(selectorPriorityDelta);
     }
-    
+
     /**
      * @return whether this connector uses a channel inherited from the JVM.
      * @see System#inheritedChannel()
@@ -323,8 +319,6 @@
                 _localPort = serverChannel.socket().getLocalPort();
                 if (_localPort <= 0)
                     throw new IOException("Server channel not bound");
-
-                addBean(serverChannel);
             }
 
             serverChannel.configureBlocking(true);
@@ -337,7 +331,7 @@
     @Override
     public Future<Void> shutdown()
     {
-        // TODO shutdown all the connections
+        // shutdown all the connections
         return super.shutdown();
     }
 
@@ -378,7 +372,7 @@
             accepted(channel);
         }
     }
-    
+
     private void accepted(SocketChannel channel) throws IOException
     {
         channel.configureBlocking(false);
@@ -480,9 +474,25 @@
         _reuseAddress = reuseAddress;
     }
 
-    private final class ServerConnectorManager extends SelectorManager
+    /**
+     * @return the ExecutionStrategy factory to use for SelectorManager
+     */
+    public ExecutionStrategy.Factory getExecutionStrategyFactory()
     {
-        private ServerConnectorManager(Executor executor, Scheduler scheduler, int selectors)
+        return _manager.getExecutionStrategyFactory();
+    }
+
+    /**
+     * @param executionFactory the ExecutionStrategy factory to use for SelectorManager
+     */
+    public void setExecutionStrategyFactory(ExecutionStrategy.Factory executionFactory)
+    {
+        _manager.setExecutionStrategyFactory(executionFactory);
+    }
+
+    protected class ServerConnectorManager extends SelectorManager
+    {
+        public ServerConnectorManager(Executor executor, Scheduler scheduler, int selectors)
         {
             super(executor, scheduler, selectors);
         }
@@ -518,7 +528,5 @@
             onEndPointClosed(endpoint);
             super.endPointClosed(endpoint);
         }
-        
-        
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionIdManager.java
index a26b540..b1d6a8b 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionIdManager.java
@@ -42,7 +42,7 @@
     
     /**
      * Remove session from the list of known sessions for a given ID.
-     * @param session
+     * @param session the session to remove
      */
     public void removeSession(HttpSession session);
     
@@ -53,8 +53,10 @@
     public void invalidateAll(String id);
     
     /**
-     * @param request
-     * @param created
+     * Create a new Session ID.
+     * 
+     * @param request the request with the sesion
+     * @param created the timestamp for when the session was created
      * @return the new session id
      */
     public String newSessionId(HttpServletRequest request,long created);
@@ -67,7 +69,7 @@
     /* ------------------------------------------------------------ */
     /** Get a cluster ID from a node ID.
      * Strip node identifier from a located session ID.
-     * @param nodeId
+     * @param nodeId the node id
      * @return the cluster id
      */
     public String getClusterId(String nodeId);
@@ -84,9 +86,9 @@
     /* ------------------------------------------------------------ */
     /** Change the existing session id.
     * 
-    * @param oldClusterId
-    * @param oldNodeId
-    * @param request
+    * @param oldClusterId the old cluster id
+    * @param oldNodeId the old node id
+    * @param request the request containing the session
     */
     public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request);    
 
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java
index 93dc19f..e095426 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java
@@ -23,6 +23,7 @@
 
 import javax.servlet.SessionCookieConfig;
 import javax.servlet.SessionTrackingMode;
+import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
 
@@ -307,10 +308,10 @@
     /* ------------------------------------------------------------ */
     /** Change the existing session id.
     * 
-    * @param oldClusterId
-    * @param oldNodeId
-    * @param newClusterId
-    * @param newNodeId
+    * @param oldClusterId the old cluster id
+    * @param oldNodeId the old node id
+    * @param newClusterId the new cluster id
+    * @param newNodeId the new node id
     */
     public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId);  
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java
index ec262f2..e8ad45d 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java
@@ -27,11 +27,14 @@
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Properties;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.function.Predicate;
 
+import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.component.Destroyable;
 import org.eclipse.jetty.util.component.LifeCycle;
 import org.eclipse.jetty.util.thread.ShutdownThread;
@@ -39,19 +42,20 @@
 /**
  * Shutdown/Stop Monitor thread.
  * <p>
- * This thread listens on the host/port specified by the STOP.HOST/STOP.PORT system parameter (defaults to 127.0.0.1/-1 for not listening) for 
- * request authenticated with the key given by the STOP.KEY system parameter (defaults to "eclipse") for admin requests.
+ * This thread listens on the host/port specified by the STOP.HOST/STOP.PORT
+ * system parameter (defaults to 127.0.0.1/-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.
+ * 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 
+public class ShutdownMonitor
 {
-    private final Set<LifeCycle> _lifeCycles = new CopyOnWriteArraySet<LifeCycle>();
-    
     // Implementation of safe lazy init, using Initialization on Demand Holder technique.
-    static class Holder
+    private static class Holder
     {
         static ShutdownMonitor instance = new ShutdownMonitor();
     }
@@ -60,283 +64,37 @@
     {
         return Holder.instance;
     }
-    
-    /* ------------------------------------------------------------ */
-    public static synchronized void register(LifeCycle... lifeCycles)
-    {
-        getInstance()._lifeCycles.addAll(Arrays.asList(lifeCycles));
-    }
 
-   
-    /* ------------------------------------------------------------ */
-    public static synchronized void deregister(LifeCycle lifeCycle)
+    protected static void reset()
     {
-        getInstance()._lifeCycles.remove(lifeCycle);
+        Holder.instance = new ShutdownMonitor();
     }
     
-    /* ------------------------------------------------------------ */
-    public static synchronized boolean isRegistered(LifeCycle lifeCycle)
+    public static void register(LifeCycle... lifeCycles)
     {
-        return getInstance()._lifeCycles.contains(lifeCycle);
+        getInstance().addLifeCycles(lifeCycles);
     }
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * ShutdownMonitorRunnable
-     *
-     * 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.
-     *
-     */
-    private class ShutdownMonitorRunnable implements Runnable
+
+    public static void deregister(LifeCycle lifeCycle)
     {
-        public ShutdownMonitorRunnable()
-        {
-            startListenSocket();
-        }
-        
-        @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".equalsIgnoreCase(cmd)) //historic, for backward compatibility
-                    {
-                        //Stop the lifecycles, only if they are registered with the ShutdownThread, only destroying if vm is exiting
-                        debug("Issuing stop...");
-                        
-                        for (LifeCycle l:_lifeCycles)
-                        {
-                            try
-                            {
-                                if (l.isStarted() && ShutdownThread.isRegistered(l))
-                                {
-                                    l.stop();
-                                }
-                                
-                                if ((l instanceof Destroyable) && exitVm)
-                                    ((Destroyable)l).destroy();
-                            }
-                            catch (Exception e)
-                            {
-                                debug(e);
-                            }
-                        }
-
-                        //Stop accepting any more commands
-                        stopInput(socket);
-
-                        // Reply to client
-                        debug("Informing client that we are stopped.");
-                        informClient(out, "Stopped\r\n");
-
-                        //Stop the output and close the monitor socket
-                        stopOutput(socket);
-
-                        if (exitVm)
-                        {
-                            // Kill JVM
-                            debug("Killing JVM");
-                            System.exit(0);
-                        }
-                    }
-                    else if ("forcestop".equalsIgnoreCase(cmd))
-                    {
-                        debug("Issuing force stop...");
-                        
-                        //Ensure that objects are stopped, destroyed only if vm is forcibly exiting
-                        stopLifeCycles(exitVm);
-
-                        //Stop accepting any more commands
-                        stopInput(socket);
-
-                        // Reply to client
-                        debug("Informing client that we are stopped.");
-                        informClient(out, "Stopped\r\n");
-
-                        //Stop the output and close the monitor socket
-                        stopOutput(socket);
-                        
-                        //Honour any pre-setup config to stop the jvm when this command is given
-                        if (exitVm)
-                        {
-                            // Kill JVM
-                            debug("Killing JVM");
-                            System.exit(0);
-                        }
-                    }
-                    else if ("stopexit".equalsIgnoreCase(cmd))
-                    {
-                        debug("Issuing stop and exit...");
-                        //Make sure that objects registered with the shutdown thread will be stopped
-                        stopLifeCycles(true);
-                        
-                        //Stop accepting any more input
-                        stopInput(socket);
-
-                        // Reply to client
-                        debug("Informing client that we are stopped.");                       
-                        informClient(out, "Stopped\r\n");              
-
-                        //Stop the output and close the monitor socket
-                        stopOutput(socket);
-                        
-                        debug("Killing JVM");
-                        System.exit(0);
-                    }
-                    else if ("exit".equalsIgnoreCase(cmd))
-                    {
-                        debug("Killing JVM");
-                        System.exit(0);
-                    }
-                    else if ("status".equalsIgnoreCase(cmd))
-                    {
-                        // Reply to client
-                        informClient(out, "OK\r\n");
-                    }
-                }
-                catch (Exception e)
-                {
-                    debug(e);
-                    System.err.println(e.toString());
-                }
-                finally
-                {
-                    close(socket);
-                    socket = null;
-                }
-            }
-        }
-        
-        public void stopInput (Socket socket)
-        {
-            //Stop accepting any more input
-            close(serverSocket);
-            serverSocket = null;
-            //Shutdown input from client
-            shutdownInput(socket);  
-        }
-        
-        public void stopOutput (Socket socket) throws IOException
-        {
-            socket.shutdownOutput();
-            close(socket);
-            socket = null;                        
-            debug("Shutting down monitor");
-            serverSocket = null;
-        }
-        
-        public void informClient (OutputStream out, String message) throws IOException
-        {
-            out.write(message.getBytes(StandardCharsets.UTF_8));
-            out.flush();
-        }
-
-        /**
-         * Stop the registered lifecycles, optionally
-         * calling destroy on them.
-         * 
-         * @param destroy
-         */
-        public void stopLifeCycles (boolean destroy)
-        {
-            for (LifeCycle l:_lifeCycles)
-            {
-                try
-                {
-                    if (l.isStarted())
-                    {
-                        l.stop();
-                    }
-                    
-                    if ((l instanceof Destroyable) && destroy)
-                        ((Destroyable)l).destroy();
-                }
-                catch (Exception e)
-                {
-                    debug(e);
-                }
-            }
-        }
-
-        public void startListenSocket()
-        {
-            if (port < 0)
-            {            
-                if (DEBUG)
-                    System.err.println("ShutdownMonitor not in use (port < 0): " + port);
-                return;
-            }
-
-            try
-            {
-                serverSocket = new ServerSocket();
-                serverSocket.setReuseAddress(true);
-                serverSocket.bind(new InetSocketAddress(InetAddress.getByName(host), port), 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);
-            }
-        }
-
+        getInstance().removeLifeCycle(lifeCycle);
     }
-    
-    private boolean DEBUG;
-    private String host;
+
+    public static boolean isRegistered(LifeCycle lifeCycle)
+    {
+        return getInstance().containsLifeCycle(lifeCycle);
+    }
+
+    private final Set<LifeCycle> _lifeCycles = new LinkedHashSet<>();
+    private boolean debug;
+    private final String host;
     private int port;
     private String key;
     private boolean exitVm;
-    private ServerSocket serverSocket;
-    private Thread thread;
+    private boolean alive;
 
     /**
-     * Create a ShutdownMonitor using configuration from the System properties.
+     * Creates 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>
@@ -345,119 +103,87 @@
      */
     private ShutdownMonitor()
     {
-        this.DEBUG = System.getProperty("DEBUG") != null;
-
-        // Use values passed thru via /jetty-start/
-        this.host = System.getProperty("STOP.HOST","127.0.0.1");
-        this.port = Integer.parseInt(System.getProperty("STOP.PORT","-1"));
-        this.key = System.getProperty("STOP.KEY",null);
+        this.debug = System.getProperty("DEBUG") != null;
+        this.host = System.getProperty("STOP.HOST", "127.0.0.1");
+        this.port = Integer.parseInt(System.getProperty("STOP.PORT", "-1"));
+        this.key = System.getProperty("STOP.KEY", null);
         this.exitVm = true;
     }
 
-    private void close(ServerSocket server)
+    private void addLifeCycles(LifeCycle... lifeCycles)
     {
-        if (server == null)
+        synchronized (this)
         {
-            return;
-        }
-
-        try
-        {
-            server.close();
-        }
-        catch (IOException ignore)
-        {
-            debug(ignore);
+            _lifeCycles.addAll(Arrays.asList(lifeCycles));
         }
     }
 
-    private void close(Socket socket)
+    private void removeLifeCycle(LifeCycle lifeCycle)
     {
-        if (socket == null)
+        synchronized (this)
         {
-            return;
-        }
-
-        try
-        {
-            socket.close();
-        }
-        catch (IOException ignore)
-        {
-            debug(ignore);
+            _lifeCycles.remove(lifeCycle);
         }
     }
 
-    
-    private void shutdownInput(Socket socket)
+    private boolean containsLifeCycle(LifeCycle lifeCycle)
     {
-        if (socket == null)
-            return;
-        
-        try
+        synchronized (this)
         {
-            socket.shutdownInput();
-        }   
-        catch (IOException ignore)
-        {
-            debug(ignore);
+            return _lifeCycles.contains(lifeCycle);
         }
     }
-    
-    
+
     private void debug(String format, Object... args)
     {
-        if (DEBUG)
-        {
-            System.err.printf("[ShutdownMonitor] " + format + "%n",args);
-        }
+        if (debug)
+            System.err.printf("[ShutdownMonitor] " + format + "%n", args);
     }
 
     private void debug(Throwable t)
     {
-        if (DEBUG)
-        {
+        if (debug)
             t.printStackTrace(System.err);
-        }
     }
 
     public String getKey()
     {
-        return key;
+        synchronized (this)
+        {
+            return key;
+        }
     }
 
     public int getPort()
     {
-        return port;
-    }
-
-    public ServerSocket getServerSocket()
-    {
-        return serverSocket;
+        synchronized (this)
+        {
+            return port;
+        }
     }
 
     public boolean isExitVm()
     {
-        return exitVm;
+        synchronized (this)
+        {
+            return exitVm;
+        }
     }
 
-
     public void setDebug(boolean flag)
     {
-        this.DEBUG = flag;
+        this.debug = flag;
     }
 
     /**
-     * @param exitVm
+     * @param exitVm true to exit the VM on shutdown
      */
     public void setExitVm(boolean exitVm)
     {
         synchronized (this)
         {
-            if (thread != null && thread.isAlive())
-            {
-                throw new IllegalStateException("ShutdownMonitorThread already started");
-            }
+            if (alive)
+                throw new IllegalStateException("ShutdownMonitor already started");
             this.exitVm = exitVm;
         }
     }
@@ -466,10 +192,8 @@
     {
         synchronized (this)
         {
-            if (thread != null && thread.isAlive())
-            {
-                throw new IllegalStateException("ShutdownMonitorThread already started");
-            }
+            if (alive)
+                throw new IllegalStateException("ShutdownMonitor already started");
             this.key = key;
         }
     }
@@ -478,52 +202,254 @@
     {
         synchronized (this)
         {
-            if (thread != null && thread.isAlive())
-            {
-                throw new IllegalStateException("ShutdownMonitorThread already started");
-            }
+            if (alive)
+                throw new IllegalStateException("ShutdownMonitor already started");
             this.port = port;
         }
     }
 
     protected void start() throws Exception
     {
-        Thread t = null;
-        
         synchronized (this)
         {
-            if (thread != null && thread.isAlive())
+            if (alive)
             {
-                if (DEBUG)
-                    System.err.printf("ShutdownMonitorThread already started");
+                debug("Already started");
                 return; // cannot start it again
             }
-         
-            thread = new Thread(new ShutdownMonitorRunnable());
-            thread.setDaemon(true);
-            thread.setName("ShutdownMonitor");
-            t = thread;
+            ServerSocket serverSocket = listen();
+            if (serverSocket != null)
+            {
+                alive = true;
+                Thread thread = new Thread(new ShutdownMonitorRunnable(serverSocket));
+                thread.setDaemon(true);
+                thread.setName("ShutdownMonitor");
+                thread.start();
+            }
         }
-         
-        if (t != null)
-            t.start();
     }
 
-
-    protected boolean isAlive ()
+    private void stop()
     {
-        boolean result = false;
         synchronized (this)
         {
-            result = (thread != null && thread.isAlive());
+            alive = false;
+            notifyAll();
         }
-        return result;
     }
-    
- 
+
+    // For test purposes only.
+    void await() throws InterruptedException
+    {
+        synchronized (this)
+        {
+            while (alive)
+            {
+                wait();
+            }
+        }
+    }
+
+    protected boolean isAlive()
+    {
+        synchronized (this)
+        {
+            return alive;
+        }
+    }
+
+    private ServerSocket listen()
+    {
+        int port = getPort();
+        if (port < 0)
+        {
+            debug("Not enabled (port < 0): %d", port);
+            return null;
+        }
+
+        String key = getKey();
+        try
+        {
+            ServerSocket serverSocket = new ServerSocket();
+            serverSocket.setReuseAddress(true);
+            serverSocket.bind(new InetSocketAddress(InetAddress.getByName(host), port));
+            if (port == 0)
+            {
+                port = serverSocket.getLocalPort();
+                System.out.printf("STOP.PORT=%d%n", port);
+                setPort(port);
+            }
+
+            if (key == null)
+            {
+                key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()), 36);
+                System.out.printf("STOP.KEY=%s%n", key);
+                setKey(key);
+            }
+
+            return serverSocket;
+        }
+        catch (Throwable x)
+        {
+            debug(x);
+            System.err.println("Error binding ShutdownMonitor to port " + port + ": " + x.toString());
+            return null;
+        }
+        finally
+        {
+            // establish the port and key that are in use
+            debug("STOP.PORT=%d", port);
+            debug("STOP.KEY=%s", key);
+        }
+    }
+
     @Override
     public String toString()
     {
-        return String.format("%s[port=%d]",this.getClass().getName(),port);
+        return String.format("%s[port=%d,alive=%b]", this.getClass().getName(), getPort(), isAlive());
+    }
+
+    /**
+     * Thread for listening to STOP.PORT for command to stop Jetty.
+     * If ShutdownMonitor.exitVm is true, then System.exit will also be
+     * called after the stop.
+     */
+    private class ShutdownMonitorRunnable implements Runnable
+    {
+        private final ServerSocket serverSocket;
+
+        private ShutdownMonitorRunnable(ServerSocket serverSocket)
+        {
+            this.serverSocket = serverSocket;
+        }
+
+        @Override
+        public void run()
+        {
+            debug("Started");
+            try
+            {
+                String key = getKey();
+                while (true)
+                {
+                    try (Socket socket = serverSocket.accept())
+                    {
+                        LineNumberReader reader = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
+                        String receivedKey = reader.readLine();
+                        if (!key.equals(receivedKey))
+                        {
+                            debug("Ignoring command with incorrect key: %s", receivedKey);
+                            continue;
+                        }
+
+                        String cmd = reader.readLine();
+                        debug("command=%s", cmd);
+                        OutputStream out = socket.getOutputStream();
+                        boolean exitVm = isExitVm();
+
+                        if ("stop".equalsIgnoreCase(cmd)) //historic, for backward compatibility
+                        {
+                            //Stop the lifecycles, only if they are registered with the ShutdownThread, only destroying if vm is exiting
+                            debug("Performing stop command");
+                            stopLifeCycles(ShutdownThread::isRegistered, exitVm);
+
+                            // Reply to client
+                            debug("Informing client that we are stopped");
+                            informClient(out, "Stopped\r\n");
+
+                            if (!exitVm)
+                                break;
+
+                            // Kill JVM
+                            debug("Killing JVM");
+                            System.exit(0);
+                        }
+                        else if ("forcestop".equalsIgnoreCase(cmd))
+                        {
+                            debug("Performing forced stop command");
+                            stopLifeCycles(l -> true, exitVm);
+
+                            // Reply to client
+                            debug("Informing client that we are stopped");
+                            informClient(out, "Stopped\r\n");
+
+                            if (!exitVm)
+                                break;
+
+                            // Kill JVM
+                            debug("Killing JVM");
+                            System.exit(0);
+                        }
+                        else if ("stopexit".equalsIgnoreCase(cmd))
+                        {
+                            debug("Performing stop and exit commands");
+                            stopLifeCycles(ShutdownThread::isRegistered, true);
+
+                            // Reply to client
+                            debug("Informing client that we are stopped");
+                            informClient(out, "Stopped\r\n");
+
+                            debug("Killing JVM");
+                            System.exit(0);
+                        }
+                        else if ("exit".equalsIgnoreCase(cmd))
+                        {
+                            debug("Killing JVM");
+                            System.exit(0);
+                        }
+                        else if ("status".equalsIgnoreCase(cmd))
+                        {
+                            // Reply to client
+                            informClient(out, "OK\r\n");
+                        }
+                    }
+                    catch (Throwable x)
+                    {
+                        debug(x);
+                    }
+                }
+            }
+            catch (Throwable x)
+            {
+                debug(x);
+            }
+            finally
+            {
+                IO.close(serverSocket);
+                stop();
+                debug("Stopped");
+            }
+        }
+
+        private void informClient(OutputStream out, String message) throws IOException
+        {
+            out.write(message.getBytes(StandardCharsets.UTF_8));
+            out.flush();
+        }
+
+        private void stopLifeCycles(Predicate<LifeCycle> predicate, boolean destroy)
+        {
+            List<LifeCycle> lifeCycles = new ArrayList<>();
+            synchronized (this)
+            {
+                lifeCycles.addAll(_lifeCycles);
+            }
+
+            for (LifeCycle l : lifeCycles)
+            {
+                try
+                {
+                    if (l.isStarted() && predicate.test(l))
+                        l.stop();
+
+                    if ((l instanceof Destroyable) && destroy)
+                        ((Destroyable)l).destroy();
+                }
+                catch (Throwable x)
+                {
+                    debug(x);
+                }
+            }
+        }
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Slf4jRequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Slf4jRequestLog.java
index cabff42..03404ab 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Slf4jRequestLog.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Slf4jRequestLog.java
@@ -27,7 +27,7 @@
  * Implementation of NCSARequestLog where output is sent as a SLF4J INFO Log message on the named logger "org.eclipse.jetty.server.RequestLog"
  */
 @ManagedObject("NCSA standard format request log to slf4j bridge")
-public class Slf4jRequestLog extends AbstractNCSARequestLog implements RequestLog
+public class Slf4jRequestLog extends AbstractNCSARequestLog
 {
     private Slf4jLog logger;
     private String loggerName;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SocketCustomizationListener.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SocketCustomizationListener.java
new file mode 100644
index 0000000..a4b21c2
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SocketCustomizationListener.java
@@ -0,0 +1,97 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.net.Socket;
+
+import org.eclipse.jetty.io.ChannelEndPoint;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.Connection.Listener;
+import org.eclipse.jetty.io.ssl.SslConnection;
+import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
+import org.eclipse.jetty.io.EndPoint;
+
+
+/* ------------------------------------------------------------ */
+/** 
+ * A Connection Lister for customization of SocketConnections.
+ * <p>
+ * Instances of this listener may be added to a {@link Connector} (or 
+ * {@link ConnectionFactory}) so that they are applied to all connections
+ * for that connector (or protocol) and thus allow additional Socket
+ * configuration to be applied by implementing {@link #customize(Socket, Class, boolean)}
+ */
+public class SocketCustomizationListener implements Listener
+{
+    private final boolean _ssl;
+    
+    /**
+     * Construct with SSL unwrapping on.
+     */
+    public SocketCustomizationListener()
+    {
+        this(true);
+    }
+    
+    /**
+     * @param ssl If True, then a Socket underlying an SSLConnection is unwrapped
+     * and notified.
+     */
+    public SocketCustomizationListener(boolean ssl)
+    {
+        _ssl=ssl;
+    }
+
+    @Override
+    public void onOpened(Connection connection)
+    {
+        EndPoint endp = connection.getEndPoint();
+        boolean ssl=false;
+        
+        if (_ssl && endp instanceof DecryptedEndPoint)
+        {
+            endp = ((DecryptedEndPoint)endp).getSslConnection().getEndPoint();
+            ssl=true;
+        }
+        
+        if (endp instanceof ChannelEndPoint) 
+        {
+            Socket socket = ((ChannelEndPoint)endp).getSocket();
+            customize(socket,connection.getClass(),ssl);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** This method may be extended to configure a socket on open 
+     * events.
+     * @param socket The Socket to configure
+     * @param connection The class of the connection (The socket may be wrapped
+     * by an {@link SslConnection} prior to this connection).
+     * @param ssl True if the socket is wrapped with an SslConnection
+     */
+    protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl)
+    {
+    }
+
+    @Override
+    public void onClosed(Connection connection)
+    {
+    }
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java
index fa9bb3f..cdb6360 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java
@@ -24,10 +24,13 @@
 import javax.net.ssl.SSLSession;
 
 import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.io.ssl.SslConnection;
+import org.eclipse.jetty.io.ssl.SslHandshakeListener;
 import org.eclipse.jetty.util.annotation.Name;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 
 public class SslConnectionFactory extends AbstractConnectionFactory
@@ -47,7 +50,7 @@
 
     public SslConnectionFactory(@Name("sslContextFactory") SslContextFactory factory, @Name("next") String nextProtocol)
     {
-        super("SSL-"+nextProtocol);
+        super("SSL");
         _sslContextFactory=factory==null?new SslContextFactory():factory;
         _nextProtocol=nextProtocol;
         addBean(_sslContextFactory);
@@ -58,6 +61,11 @@
         return _sslContextFactory;
     }
 
+    public String getNextProtocol()
+    {
+        return _nextProtocol;
+    }
+
     @Override
     protected void doStart() throws Exception
     {
@@ -79,6 +87,7 @@
 
         SslConnection sslConnection = newSslConnection(connector, endPoint, engine);
         sslConnection.setRenegotiationAllowed(_sslContextFactory.isRenegotiationAllowed());
+        sslConnection.setRenegotiationLimit(_sslContextFactory.getRenegotiationLimit());
         configure(sslConnection, connector, endPoint);
 
         ConnectionFactory next = connector.getConnectionFactory(_nextProtocol);
@@ -95,8 +104,25 @@
     }
 
     @Override
+    protected AbstractConnection configure(AbstractConnection connection, Connector connector, EndPoint endPoint)
+    {
+        if (connection instanceof SslConnection)
+        {
+            SslConnection sslConnection = (SslConnection)connection;
+            if (connector instanceof ContainerLifeCycle)
+            {
+                ContainerLifeCycle container = (ContainerLifeCycle)connector;
+                container.getBeans(SslHandshakeListener.class).forEach(sslConnection::addHandshakeListener);
+            }
+            getBeans(SslHandshakeListener.class).forEach(sslConnection::addHandshakeListener);
+        }
+        return super.configure(connection, connector, endPoint);
+    }
+
+    @Override
     public String toString()
     {
-        return String.format("%s@%x{%s}",this.getClass().getSimpleName(),hashCode(),getProtocol());
+        return String.format("%s@%x{%s->%s}",this.getClass().getSimpleName(),hashCode(),getProtocol(),_nextProtocol);
     }
+
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/UserIdentity.java b/jetty-server/src/main/java/org/eclipse/jetty/server/UserIdentity.java
index 635ecc5..c0d20ed 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/UserIdentity.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/UserIdentity.java
@@ -23,13 +23,12 @@
 
 import javax.security.auth.Subject;
 
-/* ------------------------------------------------------------ */
-/** User object that encapsulates user identity and operations such as run-as-role actions,
+/** 
+ * User object that encapsulates user identity and operations such as run-as-role actions,
  * checking isUserInRole and getUserPrincipal.
- *
+ * <p>
  * Implementations of UserIdentity should be immutable so that they may be
  * cached by Authenticators and LoginServices.
- *
  */
 public interface UserIdentity
 {
@@ -50,7 +49,7 @@
      * 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
+     * @param scope the scope
      * @return True if the user can act in that role.
      */
     boolean isUserInRole(String role, Scope scope);
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java
index e7507ce..fca72a9 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java
@@ -25,6 +25,7 @@
 
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HandlerContainer;
+import org.eclipse.jetty.server.Server;
 
 
 /* ------------------------------------------------------------ */
@@ -117,4 +118,21 @@
         }
         return null;
     }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void setServer(Server server)
+    {
+        if (server==getServer())
+            return;
+        
+        if (isStarted())
+            throw new IllegalStateException(STARTED);
+
+        super.setServer(server);
+        Handler[] handlers=getHandlers();
+        if (handlers!=null)
+            for (Handler h : handlers)
+                h.setServer(server);
+    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AllowSymLinkAliasChecker.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AllowSymLinkAliasChecker.java
index 26d7d90..be83281 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AllowSymLinkAliasChecker.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AllowSymLinkAliasChecker.java
@@ -18,21 +18,22 @@
 
 package org.eclipse.jetty.server.handler;
 
-import java.io.File;
-import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.Path;
 
 import org.eclipse.jetty.server.handler.ContextHandler.AliasCheck;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.PathResource;
 import org.eclipse.jetty.util.resource.Resource;
 
 
 /* ------------------------------------------------------------ */
-/** Symbolic Link AliasChecker.
+
+/**
+ * Symbolic Link AliasChecker.
  * <p>An instance of this class can be registered with {@link ContextHandler#addAliasCheck(AliasCheck)}
- * to check resources that are aliased to other locations.   The checker uses the 
+ * to check resources that are aliased to other locations.   The checker uses the
  * Java {@link Files#readSymbolicLink(Path)} and {@link Path#toRealPath(java.nio.file.LinkOption...)}
  * APIs to check if a file is aliased with symbolic links.</p>
  */
@@ -41,56 +42,56 @@
     private static final Logger LOG = Log.getLogger(AllowSymLinkAliasChecker.class);
     
     @Override
-    public boolean check(String path, Resource resource)
+    public boolean check(String uri, Resource resource)
     {
+        // Only support PathResource alias checking
+        if (!(resource instanceof PathResource))
+            return false;
+        
+        PathResource pathResource = (PathResource) resource;
+
         try
         {
-            File file =resource.getFile();
-            if (file==null)
-                return false;
-            
-            // If the file exists
-            if (file.exists())
+            Path path = pathResource.getPath();
+            Path alias = pathResource.getAliasPath();
+
+            if (path.equals(alias))
+                return false; // Unknown why this is an alias
+
+            if (hasSymbolicLink(path) && Files.isSameFile(path, alias))
             {
-                // we can use the real path method to check the symlinks resolve to the alias
-                URI real = file.toPath().toRealPath().toUri();
-                if (real.equals(resource.getAlias()))
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Allow symlink {} --> {}",resource,real);
-                    return true;
-                }
-            }
-            else
-            {
-                // file does not exists, so we have to walk the path and links ourselves.
-                Path p = file.toPath().toAbsolutePath();
-                File d = p.getRoot().toFile();
-                for (Path e:p)
-                {
-                    d=new File(d,e.toString());
-                    
-                    while (d.exists() && Files.isSymbolicLink(d.toPath()))
-                    {
-                        Path link=Files.readSymbolicLink(d.toPath());
-                        if (!link.isAbsolute())
-                            link=link.resolve(d.toPath());
-                        d=link.toFile().getAbsoluteFile().getCanonicalFile();
-                    }
-                }
-                if (resource.getAlias().equals(d.toURI()))
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Allow symlink {} --> {}",resource,d);
-                    return true;
-                }
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Allow symlink {} --> {}", resource, pathResource.getAliasPath());
+                return true;
             }
         }
-        catch(Exception e)
+        catch (Exception e)
         {
-            e.printStackTrace();
             LOG.ignore(e);
         }
+        
+        return false;
+    }
+
+    private boolean hasSymbolicLink(Path path)
+    {
+        // Is file itself a symlink?
+        if (Files.isSymbolicLink(path))
+        {
+            return true;
+        }
+
+        // Lets try each path segment
+        Path base = path.getRoot();
+        for (Path segment : path)
+        {
+            base = base.resolve(segment);
+            if (Files.isSymbolicLink(base))
+            {
+                return true;
+            }
+        }
+
         return false;
     }
 
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java
new file mode 100644
index 0000000..f33b6b9
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java
@@ -0,0 +1,332 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.nio.ByteBuffer;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http.pathmap.PathSpecSet;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.HttpOutput;
+import org.eclipse.jetty.server.HttpOutput.Interceptor;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IncludeExclude;
+import org.eclipse.jetty.util.IteratingCallback;
+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;
+
+/**
+ * Buffered Response Handler
+ * <p>
+ * A Handler that can apply a {@link org.eclipse.jetty.server.HttpOutput.Interceptor} 
+ * mechanism to buffer the entire response content until the output is closed.
+ * This allows the commit to be delayed until the response is complete and thus
+ * headers and response status can be changed while writing the body.
+ * <p>
+ * Note that the decision to buffer is influenced by the headers and status at the
+ * first write, and thus subsequent changes to those headers will not influence the
+ * decision to buffer or not.
+ * <p>
+ * Note also that there are no memory limits to the size of the buffer, thus
+ * this handler can represent an unbounded memory commitment if the content
+ * generated can also be unbounded.
+ * </p>
+ */
+public class BufferedResponseHandler extends HandlerWrapper
+{
+    static final Logger LOG = Log.getLogger(BufferedResponseHandler.class);
+
+    private final IncludeExclude<String> _methods = new IncludeExclude<>();
+    private final IncludeExclude<String> _paths = new IncludeExclude<>(PathSpecSet.class);
+    private final IncludeExclude<String> _mimeTypes = new IncludeExclude<>();
+
+    /* ------------------------------------------------------------ */
+    public BufferedResponseHandler()
+    {
+        // include only GET requests
+        
+        _methods.include(HttpMethod.GET.asString());
+        // Exclude images, aduio and video from buffering
+        for (String type:MimeTypes.getKnownMimeTypes())
+        {
+            if (type.startsWith("image/")||
+                type.startsWith("audio/")||
+                type.startsWith("video/"))
+                _mimeTypes.exclude(type);
+        }
+        LOG.debug("{} mime types {}",this,_mimeTypes);
+    }
+
+    /* ------------------------------------------------------------ */
+    public IncludeExclude<String> getMethodIncludeExclude()
+    {
+        return _methods;
+    }
+
+    /* ------------------------------------------------------------ */
+    public IncludeExclude<String> getPathIncludeExclude()
+    {
+        return _paths;
+    }
+
+    /* ------------------------------------------------------------ */
+    public IncludeExclude<String> getMimeIncludeExclude()
+    {
+        return _mimeTypes;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @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
+    {
+        ServletContext context = baseRequest.getServletContext();
+        String path = context==null?baseRequest.getRequestURI():URIUtil.addPaths(baseRequest.getServletPath(),baseRequest.getPathInfo());
+        LOG.debug("{} handle {} in {}",this,baseRequest,context);
+
+        HttpOutput out = baseRequest.getResponse().getHttpOutput();
+        
+        // Are we already being gzipped?
+        HttpOutput.Interceptor interceptor = out.getInterceptor();
+        while (interceptor!=null)
+        {
+            if (interceptor instanceof BufferedInterceptor)
+            {
+                LOG.debug("{} already intercepting {}",this,request);
+                _handler.handle(target,baseRequest, request, response);
+                return;
+            }
+            interceptor=interceptor.getNextInterceptor();
+        }
+
+        // If not a supported method - no Vary because no matter what client, this URI is always excluded
+        if (!_methods.matches(baseRequest.getMethod()))
+        {
+            LOG.debug("{} excluded by method {}",this,request);
+            _handler.handle(target,baseRequest, request, response);
+            return;
+        }
+
+        // If not a supported URI- no Vary because no matter what client, this URI is always excluded
+        // Use pathInfo because this is be
+        if (!isPathBufferable(path))
+        {
+            LOG.debug("{} excluded by path {}",this,request);
+            _handler.handle(target,baseRequest, request, response);
+            return;
+        }
+
+        // If the mime type is known from the path, then apply mime type filtering 
+        String mimeType = context==null?MimeTypes.getDefaultMimeByExtension(path):context.getMimeType(path);
+        if (mimeType!=null)
+        {
+            mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
+            if (!isMimeTypeBufferable(mimeType))
+            {
+                LOG.debug("{} excluded by path suffix mime type {}",this,request);
+                // handle normally without setting vary header
+                _handler.handle(target,baseRequest, request, response);
+                return;
+            }
+        }
+
+        // install interceptor and handle
+        out.setInterceptor(new BufferedInterceptor(baseRequest.getHttpChannel(),out.getInterceptor()));
+
+        if (_handler!=null)
+            _handler.handle(target,baseRequest, request, response);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected boolean isMimeTypeBufferable(String mimetype)
+    {
+        return _mimeTypes.matches(mimetype);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected boolean isPathBufferable(String requestURI)
+    {
+        if (requestURI == null)
+            return true;
+
+        return _paths.matches(requestURI);
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private class BufferedInterceptor implements HttpOutput.Interceptor
+    {
+        final Interceptor _next;
+        final HttpChannel _channel;
+        final Queue<ByteBuffer> _buffers=new ConcurrentLinkedQueue<>();
+        Boolean _aggregating;
+        ByteBuffer _aggregate;
+
+        public BufferedInterceptor(HttpChannel httpChannel, Interceptor interceptor)
+        {
+            _next=interceptor;
+            _channel=httpChannel;
+        }
+        
+        @Override
+        public void resetBuffer() 
+        {
+            _buffers.clear();
+            _aggregating=null;
+            _aggregate=null;
+        };
+
+        @Override
+        public void write(ByteBuffer content, boolean last, Callback callback)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} write last={} {}",this,last,BufferUtil.toDetailString(content));
+            // if we are not committed, have to decide if we should aggregate or not
+            if (_aggregating==null)
+            {
+                Response response = _channel.getResponse();
+                int sc = response.getStatus();
+                if (sc>0 && (sc<200 || sc==204 || sc==205 || sc>=300))
+                    _aggregating=Boolean.FALSE;  // No body
+                else
+                {
+                    String ct = response.getContentType();
+                    if (ct==null)
+                        _aggregating=Boolean.TRUE;
+                    else
+                    {
+                        ct=MimeTypes.getContentTypeWithoutCharset(ct);
+                        _aggregating=isMimeTypeBufferable(StringUtil.asciiToLowerCase(ct));
+                    }
+                }
+            }
+
+            // If we are not aggregating, then handle normally 
+            if (!_aggregating.booleanValue())
+            {
+                getNextInterceptor().write(content,last,callback);
+                return;
+            }
+            
+            // If last
+            if (last)
+            {
+                // Add the current content to the buffer list without a copy
+                if (BufferUtil.length(content)>0)
+                    _buffers.add(content);
+
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} committing {}",this,_buffers.size());
+                commit(_buffers,callback);
+            }
+            else
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} aggregating",this);
+                
+                // Aggregate the content into buffer chain
+                while (BufferUtil.hasContent(content))
+                {
+                    // Do we need a new aggregate buffer
+                    if (BufferUtil.space(_aggregate)==0)
+                    {
+                        int size = Math.max(_channel.getHttpConfiguration().getOutputBufferSize(),BufferUtil.length(content));
+                        _aggregate=BufferUtil.allocate(size); // TODO use a buffer pool
+                        _buffers.add(_aggregate);
+                    }
+
+                    BufferUtil.append(_aggregate,content);
+                }
+                callback.succeeded();
+            }
+        }
+
+        @Override
+        public Interceptor getNextInterceptor()
+        {
+            return _next;
+        }
+
+        @Override
+        public boolean isOptimizedForDirectBuffers()
+        {
+            return false;
+        }
+
+        protected void commit(Queue<ByteBuffer> buffers, Callback callback)
+        {
+            // If only 1 buffer
+            if (_buffers.size()==0)
+                getNextInterceptor().write(BufferUtil.EMPTY_BUFFER,true,callback);
+            else if (_buffers.size()==1)
+                // just flush it with the last callback
+                getNextInterceptor().write(_buffers.remove(),true,callback);
+            else
+            {
+                // Create an iterating callback to do the writing
+                IteratingCallback icb = new IteratingCallback()
+                {
+                    @Override
+                    protected Action process() throws Exception
+                    {
+                        ByteBuffer buffer = _buffers.poll();
+                        if (buffer==null)
+                            return Action.SUCCEEDED;
+
+                        getNextInterceptor().write(buffer,_buffers.isEmpty(),this);
+                        return Action.SCHEDULED;
+                    }
+
+                    @Override
+                    protected void onCompleteSuccess()
+                    {
+                        // Signal last callback
+                        callback.succeeded();
+                    }
+
+                    @Override
+                    protected void onCompleteFailure(Throwable cause)
+                    {
+                        // Signal last callback
+                        callback.failed(cause);
+                    }
+                };
+                icb.iterate();
+            }
+        }
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
index c059eaf..df66f62 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
@@ -64,8 +64,10 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpURI;
 import org.eclipse.jetty.http.MimeTypes;
 import org.eclipse.jetty.server.ClassLoaderDump;
+import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Dispatcher;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HandlerContainer;
@@ -79,6 +81,7 @@
 import org.eclipse.jetty.util.URIUtil;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.component.DumpableCollection;
 import org.eclipse.jetty.util.component.Graceful;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -97,9 +100,10 @@
  * 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)}
  * <p>
- * This servers executore is made available via a context attributed "org.eclipse.jetty.server.Executor".
- *
- * @org.apache.xbean.XBean description="Creates a basic HTTP context"
+ * This servers executor is made available via a context attributed "org.eclipse.jetty.server.Executor".
+ * <p>
+ * By default, the context is created with alias checkers for {@link AllowSymLinkAliasChecker} (unix only) and {@link ApproveNonExistentDirectoryAliases}.
+ * If these alias checkers are not required, then {@link #clearAliasChecks()} or {@link #setAliasChecks(List)} should be called.
  */
 @ManagedObject("URI Context")
 public class ContextHandler extends ScopedHandler implements Attributes, Graceful
@@ -107,9 +111,9 @@
     public final static int SERVLET_MAJOR_VERSION=3;
     public final static int SERVLET_MINOR_VERSION=1;
     public static final Class<?>[] SERVLET_LISTENER_TYPES = new Class[] {ServletContextListener.class,
-                                                                      ServletContextAttributeListener.class,
-                                                                      ServletRequestListener.class,
-                                                                      ServletRequestAttributeListener.class};
+        ServletContextAttributeListener.class,
+        ServletRequestListener.class,
+        ServletRequestAttributeListener.class};
 
     public static final int DEFAULT_LISTENER_TYPE_INDEX = 1;
     public static final int EXTENDED_LISTENER_TYPE_INDEX = 0;
@@ -121,6 +125,8 @@
 
     private static final ThreadLocal<Context> __context = new ThreadLocal<Context>();
 
+    private static String __serverInfo = "jetty/" + Server.getVersion();
+
     /**
      * 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
@@ -150,6 +156,19 @@
         return null;
     }
 
+    /* ------------------------------------------------------------ */
+    public static String getServerInfo()
+    {
+        return __serverInfo;
+    }
+
+    /* ------------------------------------------------------------ */
+    public static void setServerInfo(String serverInfo)
+    {
+        __serverInfo = serverInfo;
+    }
+
+
 
     protected Context _scontext;
     private final AttributesMap _attributes;
@@ -171,13 +190,15 @@
     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 _usingSecurityManager = System.getSecurityManager()!=null;
 
     private final List<EventListener> _eventListeners=new CopyOnWriteArrayList<>();
     private final List<EventListener> _programmaticListeners=new CopyOnWriteArrayList<>();
-    private final List<ServletContextListener> _contextListeners=new CopyOnWriteArrayList<>();
-    private final List<ServletContextAttributeListener> _contextAttributeListeners=new CopyOnWriteArrayList<>();
-    private final List<ServletRequestListener> _requestListeners=new CopyOnWriteArrayList<>();
-    private final List<ServletRequestAttributeListener> _requestAttributeListeners=new CopyOnWriteArrayList<>();
+    private final List<ServletContextListener> _servletContextListeners=new CopyOnWriteArrayList<>();
+    private final List<ServletContextAttributeListener> _servletContextAttributeListeners=new CopyOnWriteArrayList<>();
+    private final List<ServletRequestListener> _servletRequestListeners=new CopyOnWriteArrayList<>();
+    private final List<ServletRequestAttributeListener> _servletRequestAttributeListeners=new CopyOnWriteArrayList<>();
+    private final List<ContextScopeListener> _contextListeners = new CopyOnWriteArrayList<>();
     private final List<EventListener> _durableListeners = new CopyOnWriteArrayList<>();
     private Map<String, Object> _managedAttributes;
     private String[] _protectedTargets;
@@ -187,49 +208,41 @@
     private volatile Availability _availability;
 
     /* ------------------------------------------------------------ */
-    /**
-     *
-     */
     public ContextHandler()
     {
-        super();
-        _scontext = new Context();
-        _attributes = new AttributesMap();
-        _initParams = new HashMap<String, String>();
-        addAliasCheck(new ApproveNonExistentDirectoryAliases());
+        this(null,null,null);
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     *
-     */
     protected ContextHandler(Context context)
     {
-        super();
-        _scontext = context;
+        this(context,null,null);
+    }
+
+    /* ------------------------------------------------------------ */
+    public ContextHandler(String contextPath)
+    {
+        this(null,null,contextPath);
+    }
+
+    /* ------------------------------------------------------------ */
+    public ContextHandler(HandlerContainer parent, String contextPath)
+    {
+        this(null,parent,contextPath);
+    }
+
+    /* ------------------------------------------------------------ */
+    private ContextHandler(Context context, HandlerContainer parent, String contextPath)
+    {
+        _scontext = context==null?new Context():context;
         _attributes = new AttributesMap();
         _initParams = new HashMap<String, String>();
         addAliasCheck(new ApproveNonExistentDirectoryAliases());
-    }
+        if (File.separatorChar=='/')
+            addAliasCheck(new AllowSymLinkAliasChecker());
 
-    /* ------------------------------------------------------------ */
-    /**
-     *
-     */
-    public ContextHandler(String contextPath)
-    {
-        this();
-        setContextPath(contextPath);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     *
-     */
-    public ContextHandler(HandlerContainer parent, String contextPath)
-    {
-        this();
-        setContextPath(contextPath);
+        if (contextPath!=null)
+            setContextPath(contextPath);
         if (parent instanceof HandlerWrapper)
             ((HandlerWrapper)parent).setHandler(this);
         else if (parent instanceof HandlerCollection)
@@ -241,10 +254,11 @@
     public void dump(Appendable out, String indent) throws IOException
     {
         dumpBeans(out,indent,
-            Collections.singletonList(new ClassLoaderDump(getClassLoader())),
-            _initParams.entrySet(),
-            _attributes.getAttributeEntrySet(),
-            _scontext.getAttributeEntrySet());
+                Collections.singletonList(new ClassLoaderDump(getClassLoader())),
+                Collections.singletonList(new DumpableCollection("Handler attributes "+this,((AttributesMap)getAttributes()).getAttributeEntrySet())),
+                Collections.singletonList(new DumpableCollection("Context attributes "+this,((Context)getServletContext()).getAttributeEntrySet())),
+                Collections.singletonList(new DumpableCollection("Initparams "+this,getInitParams().entrySet()))
+                );
     }
 
     /* ------------------------------------------------------------ */
@@ -283,6 +297,18 @@
     }
 
     /* ------------------------------------------------------------ */
+    public boolean isUsingSecurityManager()
+    {
+        return _usingSecurityManager;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setUsingSecurityManager(boolean usingSecurityManager)
+    {
+        _usingSecurityManager = usingSecurityManager;
+    }
+
+    /* ------------------------------------------------------------ */
     /**
      * 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
@@ -553,9 +579,10 @@
     public void setEventListeners(EventListener[] eventListeners)
     {
         _contextListeners.clear();
-        _contextAttributeListeners.clear();
-        _requestListeners.clear();
-        _requestAttributeListeners.clear();
+        _servletContextListeners.clear();
+        _servletContextAttributeListeners.clear();
+        _servletRequestListeners.clear();
+        _servletRequestAttributeListeners.clear();
         _eventListeners.clear();
 
         if (eventListeners!=null)
@@ -566,6 +593,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Add a context event listeners.
+     * @param listener the event listener to add
      *
      * @see ServletContextListener
      * @see ServletContextAttributeListener
@@ -579,22 +607,26 @@
         if (!(isStarted() || isStarting()))
             _durableListeners.add(listener);
 
+        if (listener instanceof ContextScopeListener)
+            _contextListeners.add((ContextScopeListener)listener);
+
         if (listener instanceof ServletContextListener)
-            _contextListeners.add((ServletContextListener)listener);
+            _servletContextListeners.add((ServletContextListener)listener);
 
         if (listener instanceof ServletContextAttributeListener)
-            _contextAttributeListeners.add((ServletContextAttributeListener)listener);
+            _servletContextAttributeListeners.add((ServletContextAttributeListener)listener);
 
         if (listener instanceof ServletRequestListener)
-            _requestListeners.add((ServletRequestListener)listener);
+            _servletRequestListeners.add((ServletRequestListener)listener);
 
         if (listener instanceof ServletRequestAttributeListener)
-            _requestAttributeListeners.add((ServletRequestAttributeListener)listener);
+            _servletRequestAttributeListeners.add((ServletRequestAttributeListener)listener);
     }
 
     /* ------------------------------------------------------------ */
     /**
      * Remove a context event listeners.
+     * @param listener the event listener to remove
      *
      * @see ServletContextListener
      * @see ServletContextAttributeListener
@@ -605,24 +637,27 @@
     {
         _eventListeners.remove(listener);
 
-        if (listener instanceof ServletContextListener)
+        if (listener instanceof ContextScopeListener)
             _contextListeners.remove(listener);
 
+        if (listener instanceof ServletContextListener)
+            _servletContextListeners.remove(listener);
+
         if (listener instanceof ServletContextAttributeListener)
-            _contextAttributeListeners.remove(listener);
+            _servletContextAttributeListeners.remove(listener);
 
         if (listener instanceof ServletRequestListener)
-            _requestListeners.remove(listener);
+            _servletRequestListeners.remove(listener);
 
         if (listener instanceof ServletRequestAttributeListener)
-            _requestAttributeListeners.remove(listener);
+            _servletRequestAttributeListeners.remove(listener);
     }
 
     /* ------------------------------------------------------------ */
     /**
      * Apply any necessary restrictions on a programmatic added listener.
      *
-     * @param listener
+     * @param listener the programmatic listener to add
      */
     protected void addProgrammaticListener (EventListener listener)
     {
@@ -678,6 +713,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Set Available status.
+     * @param available true to set as enabled
      */
     public void setAvailable(boolean available)
     {
@@ -714,28 +750,29 @@
         if (_contextPath == null)
             throw new IllegalStateException("Null contextPath");
 
-        _logger = Log.getLogger(getDisplayName() == null?getContextPath():getDisplayName());
+        if (_logger==null)
+            _logger = Log.getLogger(getDisplayName() == null?getContextPath():getDisplayName());
         ClassLoader old_classloader = null;
         Thread current_thread = null;
         Context old_context = null;
 
         _attributes.setAttribute("org.eclipse.jetty.server.Executor",getServer().getThreadPool());
+
+        if (_mimeTypes == null)
+            _mimeTypes = new MimeTypes();
         
         try
         {
-            // Set the classloader
+            // Set the classloader, context and enter scope
             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);
+            enterScope(null, getState());
 
             // defers the calling of super.doStart()
             startContext();
@@ -745,8 +782,10 @@
         }
         finally
         {
+            if (_availability==Availability.STARTING)
+                _availability=Availability.UNAVAILABLE;
+            exitScope(null);
             __context.set(old_context);
-
             // reset the classloader
             if (_classLoader != null && current_thread!=null)
                 current_thread.setContextClassLoader(old_classloader);
@@ -757,6 +796,7 @@
     /**
      * 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.
+     * @throws Exception if unable to start the context
      *
      * @see org.eclipse.jetty.server.handler.ContextHandler.Context
      */
@@ -764,34 +804,37 @@
     {
         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.trim(),null);
-            }
-
-            Enumeration<String> e = _scontext.getAttributeNames();
-            while (e.hasMoreElements())
-            {
-                String name = e.nextElement();
-                Object value = _scontext.getAttribute(name);
-                checkManagedAttribute(name,value);
-            }
-        }
+            addEventListener(new ManagedAttributeListener(this,StringUtil.csvSplit(managedAttributes)));
 
         super.doStart();
 
         // Call context listeners
-        if (!_contextListeners.isEmpty())
+        if (!_servletContextListeners.isEmpty())
         {
             ServletContextEvent event = new ServletContextEvent(_scontext);
-            for (ServletContextListener listener:_contextListeners)
+            for (ServletContextListener listener:_servletContextListeners)
                 callContextInitialized(listener, event);
         }
     }
 
+
+    /* ------------------------------------------------------------ */
+    protected void stopContext () throws Exception
+    {
+        //stop all the handler hierarchy
+        super.doStop();
+
+        //Call the context listeners
+        if (!_servletContextListeners.isEmpty())
+        {
+            ServletContextEvent event = new ServletContextEvent(_scontext);
+            for (int i = _servletContextListeners.size(); i-->0;)
+                callContextDestroyed(_servletContextListeners.get(i),event);
+        }
+    }
+
+
+
     /* ------------------------------------------------------------ */
     protected void callContextInitialized (ServletContextListener l, ServletContextEvent e)
     {
@@ -818,29 +861,23 @@
         _availability = Availability.UNAVAILABLE;
 
         ClassLoader old_classloader = null;
+        ClassLoader old_webapploader = null;
         Thread current_thread = null;
-
         Context old_context = __context.get();
+        enterScope(null,"doStop");
         __context.set(_scontext);
         try
         {
             // Set the classloader
             if (_classLoader != null)
             {
+                old_webapploader = _classLoader;
                 current_thread = Thread.currentThread();
                 old_classloader = current_thread.getContextClassLoader();
                 current_thread.setContextClassLoader(_classLoader);
             }
 
-            super.doStop();
-
-            // Context listeners
-            if (!_contextListeners.isEmpty())
-            {
-                ServletContextEvent event = new ServletContextEvent(_scontext);
-                for (int i = _contextListeners.size(); i-->0;)
-                    callContextDestroyed(_contextListeners.get(i),event);
-            }
+            stopContext();
 
             //retain only durable listeners
             setEventListeners(_durableListeners.toArray(new EventListener[_durableListeners.size()]));
@@ -849,29 +886,38 @@
             if (_errorHandler != null)
                 _errorHandler.stop();
 
-            Enumeration<String> e = _scontext.getAttributeNames();
-            while (e.hasMoreElements())
-            {
-                String name = e.nextElement();
-                checkManagedAttribute(name,null);
-            }
-
             for (EventListener l : _programmaticListeners)
+            {
                 removeEventListener(l);
+                if (l instanceof ContextScopeListener)
+                {
+                    try
+                    {
+                        ((ContextScopeListener)l).exitScope(_scontext,null);
+                    }
+                    catch(Throwable e)
+                    {
+                        LOG.warn(e);
+                    }
+                }
+            }
             _programmaticListeners.clear();
         }
         finally
         {
-            LOG.info("Stopped {}", this);
             __context.set(old_context);
+            exitScope(null);
+            LOG.info("Stopped {}", this);
             // reset the classloader
-            if (_classLoader != null && current_thread!=null)
+            if ((old_classloader == null || (old_classloader != old_webapploader)) && current_thread != null)
                 current_thread.setContextClassLoader(old_classloader);
         }
 
         _scontext.clearAttributes();
     }
 
+
+    /* ------------------------------------------------------------ */
     public boolean checkVirtualHost(final Request baseRequest)
     {
         if (_vhosts != null && _vhosts.length > 0)
@@ -911,7 +957,9 @@
         }
         return true;
     }
-    
+
+
+    /* ------------------------------------------------------------ */
     public boolean checkContextPath(String uri)
     {
         // Are we not the root context?
@@ -925,7 +973,7 @@
         }
         return true;
     }
-    
+
     /* ------------------------------------------------------------ */
     /*
      * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
@@ -940,7 +988,7 @@
 
         if (!checkContextPath(target))
             return false;
-        
+
         // Are we not the root context?
         // redirect null path infos
         if (!_allowNullPathInfo && _contextPath.length() == target.length() && _contextPath.length()>1)
@@ -948,9 +996,9 @@
             // context request must end with /
             baseRequest.setHandled(true);
             if (baseRequest.getQueryString() != null)
-                response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
+                response.sendRedirect(baseRequest.getRequestURI() + "/?" + baseRequest.getQueryString());
             else
-                response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
+                response.sendRedirect(baseRequest.getRequestURI() + "/");
             return false;
         }
 
@@ -1051,6 +1099,9 @@
                 baseRequest.setPathInfo(pathInfo);
             }
 
+            if (old_context != _scontext)
+                enterScope(baseRequest,dispatch);
+
             if (LOG.isDebugEnabled())
                 LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this);
 
@@ -1069,6 +1120,8 @@
         {
             if (old_context != _scontext)
             {
+                exitScope(baseRequest);
+
                 // reset the classloader
                 if (_classLoader != null && current_thread!=null)
                 {
@@ -1100,14 +1153,14 @@
             if (new_context)
             {
                 // Handle the REALLY SILLY request events!
-                if (!_requestAttributeListeners.isEmpty())
-                    for (ServletRequestAttributeListener l :_requestAttributeListeners)
+                if (!_servletRequestAttributeListeners.isEmpty())
+                    for (ServletRequestAttributeListener l :_servletRequestAttributeListeners)
                         baseRequest.addEventListener(l);
 
-                if (!_requestListeners.isEmpty())
+                if (!_servletRequestListeners.isEmpty())
                 {
                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
-                    for (ServletRequestListener l : _requestListeners)
+                    for (ServletRequestListener l : _servletRequestListeners)
                         l.requestInitialized(sre);
                 }
             }
@@ -1134,34 +1187,91 @@
             // Handle more REALLY SILLY request events!
             if (new_context)
             {
-                if (!_requestListeners.isEmpty())
+                if (!_servletRequestListeners.isEmpty())
                 {
                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
-                    for (int i=_requestListeners.size();i-->0;)
-                        _requestListeners.get(i).requestDestroyed(sre);
+                    for (int i=_servletRequestListeners.size();i-->0;)
+                        _servletRequestListeners.get(i).requestDestroyed(sre);
                 }
 
-                if (!_requestAttributeListeners.isEmpty())
+                if (!_servletRequestAttributeListeners.isEmpty())
                 {
-                    for (int i=_requestAttributeListeners.size();i-->0;)
-                        baseRequest.removeEventListener(_requestAttributeListeners.get(i));
+                    for (int i=_servletRequestAttributeListeners.size();i-->0;)
+                        baseRequest.removeEventListener(_servletRequestAttributeListeners.get(i));
+                }
+            }
+        }
+    }
+
+
+
+
+    /**
+     * @param request A request that is applicable to the scope, or null
+     * @param reason An object that indicates the reason the scope is being entered.
+     */
+    protected void enterScope(Request request, Object reason)
+    {
+        if (!_contextListeners.isEmpty())
+        {
+            for (ContextScopeListener listener:_contextListeners)
+            {
+                try
+                {
+                    listener.enterScope(_scontext,request,reason);
+                }
+                catch(Throwable e)
+                {
+                    LOG.warn(e);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * @param request A request that is applicable to the scope, or null
+     */
+    protected void exitScope(Request request)
+    {
+        if (!_contextListeners.isEmpty())
+        {
+            for (int i = _contextListeners.size(); i-->0;)
+            {
+                try
+                {
+                    _contextListeners.get(i).exitScope(_scontext,request);
+                }
+                catch(Throwable e)
+                {
+                    LOG.warn(e);
                 }
             }
         }
     }
 
     /* ------------------------------------------------------------ */
-    /*
-     * Handle a runnable in this context
+    /**
+     * Handle a runnable in the scope of this context and a particular request
+     * @param request The request to scope the thread to (may be null if no particular request is in scope)
+     * @param runnable The runnable to run.
      */
-    public void handle(Runnable runnable)
+    public void handle(Request request, Runnable runnable)
     {
         ClassLoader old_classloader = null;
         Thread current_thread = null;
-        Context old_context = null;
+        Context old_context = __context.get();
+
+        // Are we already in the scope?
+        if (old_context==_scontext)
+        {
+            runnable.run();
+            return;
+        }
+
+        // Nope, so enter the scope and then exit
         try
         {
-            old_context = __context.get();
             __context.set(_scontext);
 
             // Set the classloader
@@ -1172,12 +1282,15 @@
                 current_thread.setContextClassLoader(_classLoader);
             }
 
+            enterScope(request,runnable);
             runnable.run();
         }
         finally
         {
+            exitScope(request);
+
             __context.set(old_context);
-            if (old_classloader != null && current_thread!=null)
+            if (old_classloader != null)
             {
                 current_thread.setContextClassLoader(old_classloader);
             }
@@ -1185,11 +1298,21 @@
     }
 
     /* ------------------------------------------------------------ */
+    /*
+     * Handle a runnable in the scope of this context
+     */
+    public void handle(Runnable runnable)
+    {
+        handle(null,runnable);
+    }
+
+    /* ------------------------------------------------------------ */
     /**
      * 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.
+     * @param target the target to test
+     * @return true if target is a protected target
      */
-    /* ------------------------------------------------------------ */
     public boolean isProtectedTarget(String target)
     {
         if (target == null || _protectedTargets == null)
@@ -1205,7 +1328,7 @@
             {
                 if (target.length()==t.length())
                     return true;
-                
+
                 // Check that the target prefix really is a path segment, thus
                 // it can end with /, a query, a target or a parameter
                 char c=target.charAt(t.length());
@@ -1229,7 +1352,7 @@
             _protectedTargets = null;
             return;
         }
-        
+
         _protectedTargets = Arrays.copyOf(targets, targets.length);
     }
 
@@ -1250,7 +1373,6 @@
     @Override
     public void removeAttribute(String name)
     {
-        checkManagedAttribute(name,null);
         _attributes.removeAttribute(name);
     }
 
@@ -1264,7 +1386,6 @@
     @Override
     public void setAttribute( String name, Object value)
     {
-        checkManagedAttribute(name,value);
         _attributes.setAttribute(name,value);
     }
 
@@ -1277,37 +1398,16 @@
     {
         _attributes.clearAttributes();
         _attributes.addAll(attributes);
-        Enumeration<String> e = _attributes.getAttributeNames();
-        while (e.hasMoreElements())
-        {
-            String name = e.nextElement();
-            checkManagedAttribute(name,attributes.getAttribute(name));
-        }
     }
 
     /* ------------------------------------------------------------ */
     @Override
     public void clearAttributes()
     {
-        Enumeration<String> e = _attributes.getAttributeNames();
-        while (e.hasMoreElements())
-        {
-            String name = 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);
@@ -1406,10 +1506,10 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** 
+    /**
      * Set the base resource for this context.
-     * @param resourceBase A string representing the base resource for the context. Any string accepted 
-     * by {@link Resource#newResource(String)} may be passed and the call is equivalent to 
+     * @param resourceBase A string representing the base resource for the context. Any string accepted
+     * by {@link Resource#newResource(String)} may be passed and the call is equivalent to
      * <code>setBaseResource(newResource(resourceBase));</code>
      */
     public void setResourceBase(String resourceBase)
@@ -1448,8 +1548,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     */
     public void setWelcomeFiles(String[] files)
     {
         _welcomeFiles = files;
@@ -1486,7 +1584,7 @@
     {
         if (errorHandler != null)
             errorHandler.setServer(getServer());
-        updateBean(_errorHandler,errorHandler);
+        updateBean(_errorHandler,errorHandler,true);
         _errorHandler = errorHandler;
     }
 
@@ -1500,7 +1598,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Set the maximum size of a form post, to protect against DOS attacks from large forms.
-     * @param maxSize
+     * @param maxSize the maximum size of the form content (in bytes)
      */
     public void setMaxFormContentSize(int maxSize)
     {
@@ -1516,7 +1614,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Set the maximum number of form Keys to protect against DOS attack from crafted hash keys.
-     * @param max
+     * @param max the maximum number of form keys
      */
     public void setMaxFormKeys(int max)
     {
@@ -1618,11 +1716,11 @@
             encoding = _localeEncodingMap.get(locale.getLanguage());
         return encoding;
     }
-    
+
     /* ------------------------------------------------------------ */
-    /** 
+    /**
      * Get all of the locale encodings
-     * 
+     *
      * @return a map of all the locale encodings: key is name of the locale and value is the char encoding
      */
     public Map<String,String> getLocaleEncodings()
@@ -1647,7 +1745,7 @@
         {
             path = URIUtil.canonicalPath(path);
             Resource resource = _baseResource.addPath(path);
-            
+
             if (checkAlias(path,resource))
                 return resource;
             return null;
@@ -1662,14 +1760,14 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @param path
-     * @param resource
+     * @param path the path to check the alias for
+     * @param resource the resource
      * @return True if the alias is OK
      */
     public boolean checkAlias(String path, Resource resource)
     {
         // Is the resource aliased?
-        if (resource.getAlias() != null)
+        if (resource.isAlias())
         {
             if (LOG.isDebugEnabled())
                 LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
@@ -1689,19 +1787,25 @@
         }
         return true;
     }
-    
+
     /* ------------------------------------------------------------ */
     /**
      * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
+     * @param url the url to convert to a Resource
+     * @return the Resource for that url
+     * @throws IOException if unable to create a Resource from the URL
      */
     public Resource newResource(URL url) throws IOException
     {
         return Resource.newResource(url);
     }
-    
+
     /* ------------------------------------------------------------ */
     /**
      * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
+     * @param uri the URI to convert to a Resource
+     * @return the Resource for that URI
+     * @throws IOException if unable to create a Resource from the URL
      */
     public Resource newResource(URI uri) throws IOException
     {
@@ -1785,7 +1889,7 @@
     {
         return _aliasChecks;
     }
-    
+
     /* ------------------------------------------------------------ */
     /**
      * @param checks list of AliasCheck instances
@@ -1798,6 +1902,16 @@
 
     /* ------------------------------------------------------------ */
     /**
+     * clear the list of AliasChecks
+     */
+    public void clearAliasChecks()
+    {
+        _aliasChecks.clear();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
      * Context.
      * <p>
      * A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
@@ -1805,7 +1919,7 @@
      *
      *
      */
-    public class Context extends NoContext
+    public class Context extends StaticContext
     {
         protected boolean _enabled = true; //whether or not the dynamic API is enabled for callers
         protected boolean _extendedListenerTypes = false;
@@ -1927,6 +2041,8 @@
         @Override
         public RequestDispatcher getRequestDispatcher(String uriInContext)
         {
+            // uriInContext is encoded, potentially with query
+
             if (uriInContext == null)
                 return null;
 
@@ -1935,21 +2051,17 @@
 
             try
             {
-                String query = null;
-                int q = 0;
-                if ((q = uriInContext.indexOf('?')) > 0)
-                {
-                    query = uriInContext.substring(q + 1);
-                    uriInContext = uriInContext.substring(0,q);
-                }
+                HttpURI uri = new HttpURI(null,null,0,uriInContext);
 
-                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);
-                }
+                String pathInfo=URIUtil.canonicalPath(uri.getDecodedPath());
+                if (pathInfo==null)
+                    return null;
+
+                String contextPath=getContextPath();
+                if (contextPath!=null && contextPath.length()>0)
+                    uri.setPath(URIUtil.addPaths(contextPath,uri.getPath()));
+
+                return new Dispatcher(ContextHandler.this,uri,pathInfo);
             }
             catch (Exception e)
             {
@@ -1996,7 +2108,7 @@
         {
             Resource resource = ContextHandler.this.getResource(path);
             if (resource != null && resource.exists())
-                return resource.getURL();
+                return resource.getURI().toURL();
             return null;
         }
 
@@ -2013,6 +2125,9 @@
                 if (url == null)
                     return null;
                 Resource r = Resource.newResource(url);
+                // Cannot serve directories as an InputStream
+                if(r.isDirectory())
+                    return null;
                 return r.getInputStream();
             }
             catch (Exception e)
@@ -2120,7 +2235,6 @@
         @Override
         public synchronized void setAttribute(String name, Object value)
         {
-            checkManagedAttribute(name,value);
             Object old_value = super.getAttribute(name);
 
             if (value == null)
@@ -2128,11 +2242,11 @@
             else
                 super.setAttribute(name,value);
 
-            if (!_contextAttributeListeners.isEmpty())
+            if (!_servletContextAttributeListeners.isEmpty())
             {
                 ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value == null?value:old_value);
 
-                for (ServletContextAttributeListener l : _contextAttributeListeners)
+                for (ServletContextAttributeListener l : _servletContextAttributeListeners)
                 {
                     if (old_value == null)
                         l.attributeAdded(event);
@@ -2151,15 +2265,13 @@
         @Override
         public synchronized void removeAttribute(String name)
         {
-            checkManagedAttribute(name,null);
-
             Object old_value = super.getAttribute(name);
             super.removeAttribute(name);
-            if (old_value != null &&!_contextAttributeListeners.isEmpty())
+            if (old_value != null &&!_servletContextAttributeListeners.isEmpty())
             {
                 ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value);
 
-                for (ServletContextAttributeListener l : _contextAttributeListeners)
+                for (ServletContextAttributeListener l : _servletContextAttributeListeners)
                     l.attributeRemoved(event);
             }
         }
@@ -2212,8 +2324,8 @@
 
             try
             {
-                @SuppressWarnings("unchecked")
-                Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className);
+                @SuppressWarnings({ "unchecked", "rawtypes" })
+                Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):(Class)_classLoader.loadClass(className);
                 addListener(clazz);
             }
             catch (ClassNotFoundException e)
@@ -2283,55 +2395,54 @@
 
         public void setExtendedListenerTypes (boolean extended)
         {
-           _extendedListenerTypes = extended;
+            _extendedListenerTypes = extended;
         }
 
-       public boolean isExtendedListenerTypes()
-       {
-           return _extendedListenerTypes;
-       }
+        public boolean isExtendedListenerTypes()
+        {
+            return _extendedListenerTypes;
+        }
 
+        @Override
+        public ClassLoader getClassLoader()
+        {
+            if (!_enabled)
+                throw new UnsupportedOperationException();
 
-       @Override
-       public ClassLoader getClassLoader()
-       {
-           if (!_enabled)
-               throw new UnsupportedOperationException();
-           
-           //no security manager just return the classloader
-           if (System.getSecurityManager() == null)
-               return _classLoader;
-           else
-           {
-               //check to see if the classloader of the caller is the same as the context
-               //classloader, or a parent of it
-               try
-               {
-                   Class<?> reflect = Loader.loadClass(getClass(), "sun.reflect.Reflection");
-                   Method getCallerClass = reflect.getMethod("getCallerClass", Integer.TYPE);
-                   Class<?> caller = (Class<?>)getCallerClass.invoke(null, 2);
+            //no security manager just return the classloader
+            if (!_usingSecurityManager)
+                return _classLoader;
+            else
+            {
+                //check to see if the classloader of the caller is the same as the context
+                //classloader, or a parent of it
+                try
+                {
+                    Class<?> reflect = Loader.loadClass(getClass(), "sun.reflect.Reflection");
+                    Method getCallerClass = reflect.getMethod("getCallerClass", Integer.TYPE);
+                    Class<?> caller = (Class<?>)getCallerClass.invoke(null, 2);
 
-                   boolean ok = false;
-                   ClassLoader callerLoader = caller.getClassLoader();
-                   while (!ok && callerLoader != null)
-                   {
-                       if (callerLoader == _classLoader) 
-                           ok = true;
-                       else
-                           callerLoader = callerLoader.getParent();    
-                   }
+                    boolean ok = false;
+                    ClassLoader callerLoader = caller.getClassLoader();
+                    while (!ok && callerLoader != null)
+                    {
+                        if (callerLoader == _classLoader)
+                            ok = true;
+                        else
+                            callerLoader = callerLoader.getParent();
+                    }
 
-                   if (ok)
-                       return _classLoader;
-               }
-               catch (Exception e)      
-               {
-                   LOG.warn("Unable to check classloader of caller",e);
-               }
-              
-               AccessController.checkPermission(new RuntimePermission("getClassLoader"));
-               return _classLoader;
-           }
+                    if (ok)
+                        return _classLoader;
+                }
+                catch (Exception e)
+                {
+                    LOG.warn("Unable to check classloader of caller",e);
+                }
+
+                AccessController.checkPermission(new RuntimePermission("getClassLoader"));
+                return _classLoader;
+            }
         }
 
         @Override
@@ -2365,23 +2476,29 @@
             return _enabled;
         }
 
-
-
         public <T> T createInstance (Class<T> clazz) throws Exception
         {
             T o = clazz.newInstance();
             return o;
         }
+
+        @Override
+        public String getVirtualServerName()
+        {
+            String[] hosts = getVirtualHosts();
+            if (hosts!=null && hosts.length>0)
+                return hosts[0];
+            return null;
+        }
     }
 
-
-    public static class NoContext extends AttributesMap implements ServletContext
+    public static class StaticContext extends AttributesMap implements ServletContext
     {
         private int _effectiveMajorVersion = SERVLET_MAJOR_VERSION;
         private int _effectiveMinorVersion = SERVLET_MINOR_VERSION;
 
         /* ------------------------------------------------------------ */
-        public NoContext()
+        public StaticContext()
         {
         }
 
@@ -2448,12 +2565,7 @@
         @Override
         public String getServerInfo()
         {
-            // NOTE: DO NOT CHANGE
-            //   this is used by weld to detect Jetty
-            //   implementation version
-            // See: https://github.com/weld/core/blob/master/environments/servlet/core/src/main/java/org/jboss/weld/environment/jetty/JettyContainer.java
-            //   and its touch(ContainerContext) method
-            return "jetty/" + Server.getVersion();
+            return __serverInfo;
         }
 
         @Override
@@ -2523,7 +2635,6 @@
             return null;
         }
 
-
         @Override
         public boolean setInitParameter(String name, String value)
         {
@@ -2679,7 +2790,6 @@
         @Override
         public ClassLoader getClassLoader()
         {
-            AccessController.checkPermission(new RuntimePermission("getClassLoader"));
             return ContextHandler.class.getClassLoader();
         }
 
@@ -2718,13 +2828,9 @@
             LOG.warn(__unimplmented);
         }
 
-        /**
-         * @see javax.servlet.ServletContext#getVirtualServerName()
-         */
         @Override
         public String getVirtualServerName()
         {
-            // TODO 3.1 Auto-generated method stub
             return null;
         }
     }
@@ -2758,53 +2864,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** 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");
-        }
-        
-        @Override
-        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");
-        }
-        
-        @Override
-        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.
@@ -2816,17 +2875,36 @@
         {
             if (resource.exists())
                 return false;
-            
+
             String a=resource.getAlias().toString();
-            String r=resource.getURL().toString();
-            
+            String r=resource.getURI().toString();
+
             if (a.length()>r.length())
                 return a.startsWith(r) && a.length()==r.length()+1 && a.endsWith("/");
             if (a.length()<r.length())
                 return r.startsWith(a) && r.length()==a.length()+1 && r.endsWith("/");
-            
-            return a.equals(r); 
+
+            return a.equals(r);
         }
     }
 
+
+    /** Listener for all threads entering context scope, including async IO callbacks
+     */
+    public static interface ContextScopeListener extends EventListener
+    {
+        /**
+         * @param context The context being entered
+         * @param request A request that is applicable to the scope, or null
+         * @param reason An object that indicates the reason the scope is being entered
+         */
+        void enterScope(Context context, Request request, Object reason);
+
+
+        /**
+         * @param context The context being exited
+         * @param request A request that is applicable to the scope, or null
+         */
+        void exitScope(Context context, Request request);
+    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java
index c9413c2..9362f4f 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java
@@ -19,11 +19,9 @@
 package org.eclipse.jetty.server.handler;
 
 import java.io.IOException;
-import java.lang.reflect.Array;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -49,7 +47,7 @@
 /** ContextHandlerCollection.
  *
  * This {@link org.eclipse.jetty.server.handler.HandlerCollection} is creates a
- * {@link org.eclipse.jetty.http.PathMap} to it's contained handlers based
+ * Map of contexts 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
@@ -236,6 +234,7 @@
     /* ------------------------------------------------------------ */
     /** Add a context handler.
      * @param contextPath  The context path to add
+     * @param resourceBase the base (root) Resource
      * @return the ContextHandler just added
      */
     public ContextHandler addContext(String contextPath,String resourceBase)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java
index 8ec3050..c856280 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java
@@ -27,9 +27,11 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpURI;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.server.AbstractConnector;
 import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.DebugListener;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Response;
 import org.eclipse.jetty.util.DateCache;
@@ -42,6 +44,7 @@
  * 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.
+ * @deprecated Use {@link DebugListener}
  */
 public class DebugHandler extends HandlerWrapper implements Connection.Listener
 {
@@ -64,8 +67,8 @@
         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();
+        if (name == null)
+            name = old_name + ":" + baseRequest.getHttpURI();
         else
             retry=true;
 
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java
index 734d788..71af677 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java
@@ -48,7 +48,6 @@
  * For all other requests a normal 404 is served.
  *
  *
- * @org.apache.xbean.XBean
  */
 public class DefaultHandler extends AbstractHandler
 {
@@ -134,7 +133,7 @@
                 {
                     writer.write("<li><a href=\"");
                     if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
-                        writer.write("http://"+context.getVirtualHosts()[0]+":"+request.getLocalPort());
+                        writer.write(request.getScheme()+"://"+context.getVirtualHosts()[0]+":"+request.getLocalPort());
                     writer.write(context.getContextPath());
                     if (context.getContextPath().length()>1 && context.getContextPath().endsWith("/"))
                         writer.write("/");
@@ -163,8 +162,9 @@
             }
 
             writer.write("</ul><hr>");
-            writer.write("<a href=\"http://eclipse.org/jetty\"><img border=0 src=\"/favicon.ico\"/></a>&nbsp;");
-            writer.write("<a href=\"http://eclipse.org/jetty\">Powered by Jetty:// Java Web Server</a><hr/>\n");
+
+            baseRequest.getHttpChannel().getHttpConfiguration()
+                .writePoweredBy(writer,"<a href=\"http://eclipse.org/jetty\"><img border=0 src=\"/favicon.ico\"/></a>&nbsp;","<hr/>\n");
 
             writer.write("\n</BODY>\n</HTML>\n");
             writer.flush();
@@ -173,7 +173,7 @@
             {
                 writer.writeTo(out);
             }
-        } 
+        }
     }
 
     /* ------------------------------------------------------------ */
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java
index 9ecd978..9d4c760 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java
@@ -23,7 +23,11 @@
 import java.io.StringWriter;
 import java.io.Writer;
 import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
 
+import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -35,10 +39,8 @@
 import org.eclipse.jetty.http.MimeTypes;
 import org.eclipse.jetty.server.Dispatcher;
 import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Response;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.ByteArrayISO8859Writer;
 import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -100,22 +102,129 @@
                         return;
                     }
                 }
+            } 
+            else 
+            {
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("No Error Page mapping for request({} {}) (using default)",request.getMethod(),request.getRequestURI());
+                }
             }
         }
-        
-        baseRequest.setHandled(true);
-        response.setContentType(MimeTypes.Type.TEXT_HTML_8859_1.asString());    
-        if (_cacheControl!=null)
+
+        if (_cacheControl != null)
             response.setHeader(HttpHeader.CACHE_CONTROL.asString(), _cacheControl);
-        ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(4096);
-        String reason=(response instanceof Response)?((Response)response).getReason():null;
-        handleErrorPage(request, writer, response.getStatus(), reason);
-        writer.flush();
-        response.setContentLength(writer.size());
-        writer.writeTo(response.getOutputStream());
-        writer.destroy();
+        generateAcceptableResponse(baseRequest,request,response,response.getStatus(),baseRequest.getResponse().getReason());
     }
 
+    /** 
+     * Generate an acceptable error response.
+     * <p>This method is called to generate an Error page of a mime type that is 
+     * acceptable to the user-agent.  The Accept header is evaluated in 
+     * quality order and the method 
+     * {@link #generateAcceptableResponse(Request, HttpServletRequest, HttpServletResponse, int, String, String)}
+     * is called for each mimetype until {@link Request#isHandled()} is true.</p>
+     * @param baseRequest The base request
+     * @param request The servlet request (may be wrapped)
+     * @param response The response (may be wrapped)
+     * @throws IOException if the response cannot be generated
+     */
+    protected void generateAcceptableResponse(Request baseRequest, HttpServletRequest request, HttpServletResponse response, int code, String message)
+        throws IOException
+    {
+        List<String> acceptable=baseRequest.getHttpFields().getQualityCSV(HttpHeader.ACCEPT);
+        
+        if (acceptable.isEmpty() && !baseRequest.getHttpFields().contains(HttpHeader.ACCEPT))
+            generateAcceptableResponse(baseRequest,request,response,code,message,MimeTypes.Type.TEXT_HTML.asString());
+        else
+        {
+            for (String mimeType:acceptable)
+            {
+                generateAcceptableResponse(baseRequest,request,response,code,message,mimeType);
+                if (baseRequest.isHandled())
+                    return;
+            }
+        }
+        baseRequest.setHandled(true);
+    }
+
+    /** 
+     * Returns an acceptable writer for an error page.
+     * <p>Uses the user-agent's <code>Accept-Charset</code> to get response
+     * {@link Writer}.  The acceptable charsets are tested in quality order 
+     * if they are known to the JVM and the first known is set on
+     * {@link HttpServletResponse#setCharacterEncoding(String)} and the 
+     * {@link HttpServletResponse#getWriter()} method used to return a writer.
+     * If there is no <code>Accept-Charset</code> header then 
+     * <code>ISO-8859-1</code> is used.  If '*' is the highest quality known
+     * charset, then <code>utf-8</code> is used.
+     * </p>     
+     * @param baseRequest The base request
+     * @param request The servlet request (may be wrapped)
+     * @param response The response (may be wrapped)
+     * @return A {@link Writer} if there is a known acceptable charset or null
+     * @throws IOException if a Writer cannot be returned
+     */
+    protected Writer getAcceptableWriter(Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+        throws IOException
+    {
+        List<String> acceptable=baseRequest.getHttpFields().getQualityCSV(HttpHeader.ACCEPT_CHARSET);
+        if (acceptable.isEmpty())
+        {
+            response.setCharacterEncoding(StandardCharsets.ISO_8859_1.name());
+            return response.getWriter();
+        }
+        
+        for (String charset:acceptable)
+        {
+            try
+            {
+                if ("*".equals(charset))
+                    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
+                else
+                    response.setCharacterEncoding(Charset.forName(charset).name());
+                return response.getWriter();
+            }
+            catch(Exception e)
+            {
+                LOG.ignore(e);
+            }
+        }
+        return null;
+    }
+        
+    /* ------------------------------------------------------------ */
+    /** Generate an acceptable error response for a mime type.
+     * <p>This method is called for each mime type in the users agent's
+     * <code>Accept</code> header, until {@link Request#isHandled()} is true and a 
+     * response of the appropriate type is generated.
+     * 
+     * @param baseRequest The base request
+     * @param request The servlet request (may be wrapped)
+     * @param response The response (may be wrapped)
+     * @param mimeType The mimetype to generate (may be *&#47;*or other wildcard)
+     * @throws IOException if a response cannot be generated
+     */
+    protected void generateAcceptableResponse(Request baseRequest, HttpServletRequest request, HttpServletResponse response, int code, String message, String mimeType)
+        throws IOException
+    {
+        switch(mimeType)
+        {
+            case "text/html":
+            case "text/*":
+            case "*/*":
+            {
+                baseRequest.setHandled(true);
+                Writer writer = getAcceptableWriter(baseRequest,request,response);
+                if (writer!=null)
+                {
+                    response.setContentType(MimeTypes.Type.TEXT_HTML.asString());
+                    handleErrorPage(request, writer, code, message);
+                }
+            }
+        }        
+    }
+    
     /* ------------------------------------------------------------ */
     protected void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message)
         throws IOException
@@ -141,7 +250,7 @@
     protected void writeErrorPageHead(HttpServletRequest request, Writer writer, int code, String message)
         throws IOException
         {
-        writer.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n");
+        writer.write("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/>\n");
         writer.write("<title>Error ");
         writer.write(Integer.toString(code));
 
@@ -162,7 +271,9 @@
         writeErrorPageMessage(request,writer,code,message,uri);
         if (showStacks)
             writeErrorPageStacks(request,writer);
-        writer.write("<hr><i><small>Powered by Jetty://</small></i><hr/>\n");
+
+        Request.getBaseRequest(request).getHttpChannel().getHttpConfiguration()
+            .writePoweredBy(writer,"<hr>","<hr/>\n");
     }
 
     /* ------------------------------------------------------------ */
@@ -182,7 +293,7 @@
     protected void writeErrorPageStacks(HttpServletRequest request, Writer writer)
         throws IOException
     {
-        Throwable th = (Throwable)request.getAttribute("javax.servlet.error.exception");
+        Throwable th = (Throwable)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
         while(th!=null)
         {
             writer.write("<h3>Caused by:</h3><pre>");
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java
index cf54347..899a22e 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java
@@ -27,6 +27,7 @@
 import javax.servlet.http.HttpServletResponse;
 
 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.ArrayUtil;
@@ -35,14 +36,13 @@
 import org.eclipse.jetty.util.annotation.ManagedObject;
 
 /* ------------------------------------------------------------ */
-/** A collection of handlers.
+/** 
+ * 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>
- *
  */
 @ManagedObject("Handler of multiple handlers")
 public class HandlerCollection extends AbstractHandlerContainer
@@ -83,9 +83,18 @@
             throw new IllegalStateException(STARTED);
 
         if (handlers!=null)
+        {
+            // check for loops
+            for (Handler handler:handlers)
+                if (handler == this || (handler instanceof HandlerContainer &&
+                    Arrays.asList(((HandlerContainer)handler).getChildHandlers()).contains(this)))
+                        throw new IllegalStateException("setHandler loop");
+          
+            // Set server
             for (Handler handler:handlers)
                 if (handler.getServer()!=getServer())
                     handler.setServer(getServer());
+        }
         Handler[] old=_handlers;;
         _handlers = handlers;
         updateBeans(old, handlers);
@@ -136,17 +145,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    @Override
-    public void setServer(Server server)
-    {
-        super.setServer(server);
-        Handler[] handlers=getHandlers();
-        if (handlers!=null)
-            for (Handler h : handlers)
-                h.setServer(server);
-    }
-
-    /* ------------------------------------------------------------ */
     /* 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)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java
index da37ab7..5a4887c 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java
@@ -19,17 +19,21 @@
 package org.eclipse.jetty.server.handler;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.List;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpStatus;
 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.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
+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
@@ -80,42 +84,56 @@
         if (isStarted())
             throw new IllegalStateException(STARTED);
 
+        // check for loops
+        if (handler==this || (handler instanceof HandlerContainer &&
+            Arrays.asList(((HandlerContainer)handler).getChildHandlers()).contains(this)))
+            throw new IllegalStateException("setHandler loop");
+        
         if (handler!=null)
             handler.setServer(getServer());
         
         Handler old=_handler;
         _handler=handler;
-        updateBean(old,_handler);
+        updateBean(old,_handler,true);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * Replace the current handler with another HandlerWrapper
+     * linked to the current handler.  
+     * <p>
+     * This is equivalent to:
+     * <pre>
+     *   wrapper.setHandler(getHandler());
+     *   setHandler(wrapper);
+     * </pre>
+     * @param wrapper the wrapper to insert
+     */
+    public void insertHandler(HandlerWrapper wrapper)
+    {
+        if (wrapper==null)
+            throw new IllegalArgumentException();
+        
+        HandlerWrapper tail = wrapper;
+        while(tail.getHandler() instanceof HandlerWrapper)
+            tail=(HandlerWrapper)tail.getHandler();
+        if (tail.getHandler()!=null)
+            throw new IllegalArgumentException("bad tail of inserted wrapper chain");
+        
+        Handler next=getHandler();
+        setHandler(wrapper);
+        tail.setHandler(next);
     }
 
     /* ------------------------------------------------------------ */
     @Override
     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
     {
-        if (_handler!=null && isStarted())
-        {
-            _handler.handle(target,baseRequest, request, response);
-        }
+        Handler handler=_handler;
+        if (handler!=null)
+            handler.handle(target,baseRequest, request, response);
     }
 
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public void setServer(Server server)
-    {
-        if (server==getServer())
-            return;
-        
-        if (isStarted())
-            throw new IllegalStateException(STARTED);
-
-        super.setServer(server);
-        Handler h=getHandler();
-        if (h!=null)
-            h.setServer(server);
-    }
-
-
     /* ------------------------------------------------------------ */
     @Override
     protected void expandChildren(List<Handler> list, Class<?> byClass)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HotSwapHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HotSwapHandler.java
index 4685e20..bfdd66c 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HotSwapHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HotSwapHandler.java
@@ -62,8 +62,10 @@
     @Override
     public Handler[] getHandlers()
     {
-        return new Handler[]
-        { _handler };
+        Handler handler=_handler;
+        if (handler==null)
+            return new Handler[0];
+        return new Handler[] { handler };
     }
 
     /* ------------------------------------------------------------ */
@@ -73,14 +75,13 @@
      */
     public void setHandler(Handler handler)
     {
-        if (handler == null)
-            throw new IllegalArgumentException("Parameter handler is null.");
         try
         {
-            updateBean(_handler,handler);
-            _handler=handler;
             Server server = getServer();
-            handler.setServer(server);
+            if (handler!=null)
+                handler.setServer(server);
+            updateBean(_handler,handler,true);
+            _handler=handler;
 
         }
         catch (Exception e)
@@ -116,31 +117,20 @@
     @Override
     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
     {
-        if (_handler != null && isStarted())
+        Handler handler=_handler;
+        if (handler != null && isStarted() && handler.isStarted())
         {
-            _handler.handle(target,baseRequest,request,response);
+            handler.handle(target,baseRequest,request,response);
         }
     }
 
     /* ------------------------------------------------------------ */
     @Override
-    public void setServer(Server server)
-    {
-        if (isRunning())
-            throw new IllegalStateException(RUNNING);
-
-        super.setServer(server);
-
-        Handler h = getHandler();
-        if (h != null)
-            h.setServer(server);
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
     protected void expandChildren(List<Handler> list, Class<?> byClass)
     {
-        expandHandler(_handler,list,byClass);
+        Handler handler=_handler;
+        if (handler!=null)
+            expandHandler(handler,list,byClass);
     }
 
     /* ------------------------------------------------------------ */
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java
index c5f6d5e..111012a 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java
@@ -57,12 +57,11 @@
  * are always applied, so that even if an entry matches the white list, a black list
  * entry will override it.
  * <p>
- * <p>
  * You can change white list policy setting whiteListByPath to true. In this mode a request will be white listed
  * IF it has a matching URL in the white list, otherwise the black list applies, e.g. in default mode when
  * whiteListByPath = false and wl = "127.0.0.1|/foo", /bar request from 127.0.0.1 will be blacklisted,
  * if whiteListByPath=true then not.
- * </p>
+ * <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>
@@ -70,9 +69,9 @@
  * 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
+ *           nnn- =&gt; nnn-255
+ *           -nnn =&gt; 0-nnn
+ *           -    =&gt; 0-255
  * a,b,... - a list of wildcard specifications
  * </pre>
  * <p>
@@ -203,7 +202,7 @@
     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))
-        HttpChannel<?> channel = baseRequest.getHttpChannel();
+        HttpChannel channel = baseRequest.getHttpChannel();
         if (channel!=null)
         {
             EndPoint endp=channel.getEndPoint();
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java
index 49d1eb0..52e08bf 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java
@@ -26,22 +26,20 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.server.HttpChannel;
-import org.eclipse.jetty.server.HttpConnection;
 import org.eclipse.jetty.server.Request;
 
 /**
  * Handler to adjust the idle timeout of requests while dispatched.
  * 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>
+ *   &lt;Get id='handler' name='Handler'/&gt;
+ *   &lt;Set name='Handler'&gt;
+ *     &lt;New id='idleTimeoutHandler' class='org.eclipse.jetty.server.handler.IdleTimeoutHandler'&gt;
+ *       &lt;Set name='Handler'&gt;&lt;Ref id='handler'/&gt;&lt;/Set&gt;
+ *       &lt;Set name='IdleTimeoutMs'&gt;5000&lt;/Set&gt;
+ *     &lt;/New&gt;
+ *   &lt;/Set&gt;
  * </pre>
  */
 public class IdleTimeoutHandler extends HandlerWrapper
@@ -80,7 +78,7 @@
     @Override
     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
     {
-        final HttpChannel<?> channel = baseRequest.getHttpChannel();
+        final HttpChannel channel = baseRequest.getHttpChannel();
         final long idle_timeout=baseRequest.getHttpChannel().getIdleTimeout();
         channel.setIdleTimeout(_idleTimeoutMs);
         
@@ -90,35 +88,35 @@
         }
         finally
         {
-                if (_applyToAsync && request.isAsyncStarted())
+            if (_applyToAsync && request.isAsyncStarted())
+            {
+                request.getAsyncContext().addListener(new AsyncListener()
                 {
-                    request.getAsyncContext().addListener(new AsyncListener()
+                    @Override
+                    public void onTimeout(AsyncEvent event) throws IOException
+                    {                            
+                    }
+
+                    @Override
+                    public void onStartAsync(AsyncEvent event) throws IOException
                     {
-                        @Override
-                        public void onTimeout(AsyncEvent event) throws IOException
-                        {                            
-                        }
-                        
-                        @Override
-                        public void onStartAsync(AsyncEvent event) throws IOException
-                        {
-                        }
-                        
-                        @Override
-                        public void onError(AsyncEvent event) throws IOException
-                        {
-                            channel.setIdleTimeout(idle_timeout);
-                        }
-                        
-                        @Override
-                        public void onComplete(AsyncEvent event) throws IOException
-                        {
-                            channel.setIdleTimeout(idle_timeout);
-                        }
-                    });
-                }
-                else 
-                    channel.setIdleTimeout(idle_timeout);
+                    }
+
+                    @Override
+                    public void onError(AsyncEvent event) throws IOException
+                    {
+                        channel.setIdleTimeout(idle_timeout);
+                    }
+
+                    @Override
+                    public void onComplete(AsyncEvent event) throws IOException
+                    {
+                        channel.setIdleTimeout(idle_timeout);
+                    }
+                });
+            }
+            else 
+                channel.setIdleTimeout(idle_timeout);
         }
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/InetAccessHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/InetAccessHandler.java
index 270d848..cdbdd78 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/InetAccessHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/InetAccessHandler.java
@@ -35,94 +35,79 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-
 /**
- * Inet Address Access Handler
+ * InetAddress Access Handler
  * <p>
- * Controls access to the wrapped handler by the real remote IP. Control is provided
+ * Controls access to the wrapped handler using the real remote IP. Control is provided
  * by and {@link IncludeExcludeSet} over a {@link InetAddressSet}. 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>
-
  */
 public class InetAccessHandler extends HandlerWrapper
 {
     private static final Logger LOG = Log.getLogger(InetAccessHandler.class);
-    IncludeExcludeSet<String, InetAddress> _set = new IncludeExcludeSet<>(InetAddressSet.class);
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Creates new handler object
-     */
-    public InetAccessHandler()
-    {
-        super();
-    }
+    private final IncludeExcludeSet<String, InetAddress> _set = new IncludeExcludeSet<>(InetAddressSet.class);
 
-    /* ------------------------------------------------------------ */
     /**
-     * Include a InetAddress pattern
+     * Includes an InetAddress pattern
+     *
+     * @param pattern InetAddress pattern to include
      * @see InetAddressSet
-     * @param pattern InetAddress pattern to exclude
      */
     public void include(String pattern)
     {
         _set.include(pattern);
     }
-    
-    /* ------------------------------------------------------------ */
+
     /**
-     * Include a InetAddress pattern
+     * Includes InetAddress patterns
+     *
+     * @param patterns InetAddress patterns to include
      * @see InetAddressSet
-     * @param patterns InetAddress patterns to exclude
      */
     public void include(String... patterns)
     {
         _set.include(patterns);
     }
-    
-    /* ------------------------------------------------------------ */
+
     /**
-     * Exclude a InetAddress pattern
-     * @see InetAddressSet
+     * Excludes an InetAddress pattern
+     *
      * @param pattern InetAddress pattern to exclude
+     * @see InetAddressSet
      */
     public void exclude(String pattern)
     {
         _set.exclude(pattern);
     }
-    
-    /* ------------------------------------------------------------ */
+
     /**
-     * Include a InetAddress pattern
-     * @see InetAddressSet
+     * Excludes InetAddress patterns
+     *
      * @param patterns InetAddress patterns to exclude
+     * @see InetAddressSet
      */
     public void exclude(String... patterns)
     {
         _set.exclude(patterns);
     }
 
-
-    /* ------------------------------------------------------------ */
     /**
      * 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))
-        HttpChannel<?> channel = baseRequest.getHttpChannel();
-        if (channel!=null)
+        HttpChannel channel = baseRequest.getHttpChannel();
+        if (channel != null)
         {
-            EndPoint endp=channel.getEndPoint();
-            if (endp!=null)
+            EndPoint endp = channel.getEndPoint();
+            if (endp != null)
             {
                 InetSocketAddress address = endp.getRemoteAddress();
-                if (address!=null && !isAllowed(address.getAddress()))
+                if (address != null && !isAllowed(address.getAddress(), request))
                 {
                     response.sendError(HttpStatus.FORBIDDEN_403);
                     baseRequest.setHandled(true);
@@ -131,26 +116,36 @@
             }
         }
 
-        getHandler().handle(target,baseRequest, request, response);
+        getHandler().handle(target, baseRequest, request, response);
     }
 
-    /* ------------------------------------------------------------ */
     /**
-     * Check if specified request is allowed by current IPAccess rules.
+     * Checks if specified address and request are allowed by current InetAddress rules.
      *
-     * @param address internet address
-     * @return true if address is allowed
-     *
+     * @param address the inetAddress to check
+     * @param request the request to check
+     * @return true if inetAddress and request are allowed
      */
+    protected boolean isAllowed(InetAddress address, HttpServletRequest request)
+    {
+        return isAllowed(address);
+    }
+
+    /**
+     * @deprecated use {@link #isAllowed(InetAddress, HttpServletRequest)} instead
+     */
+    @Deprecated
     protected boolean isAllowed(InetAddress address)
     {
-        return _set.test(address);
+        boolean allowed = _set.test(address);
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} {} {}", this, allowed ? "allowed" : "denied", address);
+        return allowed;
     }
 
-    /* ------------------------------------------------------------ */
     @Override
     public void dump(Appendable out, String indent) throws IOException
     {
-        dumpBeans(out,indent,_set.getIncluded(),_set.getExcluded());
+        dumpBeans(out, indent, _set.getIncluded(), _set.getExcluded());
     }
- }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ManagedAttributeListener.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ManagedAttributeListener.java
new file mode 100644
index 0000000..e8ddbef
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ManagedAttributeListener.java
@@ -0,0 +1,107 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/** Enable Jetty style JMX MBeans from within a Context 
+ */
+public class ManagedAttributeListener implements  ServletContextListener, ServletContextAttributeListener
+{
+    private static final Logger LOG = Log.getLogger(ManagedAttributeListener.class);
+
+    final Set<String> _managedAttributes=new HashSet<>();
+    final ContextHandler _context;
+    
+    public ManagedAttributeListener(ContextHandler context,String... managedAttributes)
+    {
+        _context=context;
+
+        for (String attr:managedAttributes)
+            _managedAttributes.add(attr);
+        
+        if (LOG.isDebugEnabled())
+            LOG.debug("managedAttributes {}",_managedAttributes);
+    }
+
+    @Override
+    public void attributeReplaced(ServletContextAttributeEvent event)
+    {
+        if (_managedAttributes.contains(event.getName()))
+            updateBean(event.getName(),event.getValue(),event.getServletContext().getAttribute(event.getName()));
+    }
+    
+    @Override
+    public void attributeRemoved(ServletContextAttributeEvent event)
+    {
+        if (_managedAttributes.contains(event.getName()))
+            updateBean(event.getName(),event.getValue(),null);                    
+    }
+    
+    @Override
+    public void attributeAdded(ServletContextAttributeEvent event)
+    {
+        if (_managedAttributes.contains(event.getName()))
+            updateBean(event.getName(),null,event.getValue());    
+    }
+
+    @Override
+    public void contextInitialized(ServletContextEvent event)
+    {                 
+        // Update existing attributes
+        Enumeration<String> e = event.getServletContext().getAttributeNames();
+        while (e.hasMoreElements())
+        {
+            String name = e.nextElement();
+            if (_managedAttributes.contains(name))
+                updateBean(name,null,event.getServletContext().getAttribute(name));
+        }
+    }
+    
+    @Override
+    public void contextDestroyed(ServletContextEvent event)
+    {
+        Enumeration<String> e = _context.getServletContext().getAttributeNames();
+        while (e.hasMoreElements())
+        {
+            String name = e.nextElement();
+            if (_managedAttributes.contains(name))
+                updateBean(name,event.getServletContext().getAttribute(name),null);
+        }
+    }
+    
+    protected void updateBean(String name,Object oldBean,Object newBean)
+    {
+        LOG.info("update {} {}->{} on {}",name,oldBean,newBean,_context);
+        if (LOG.isDebugEnabled())
+            LOG.debug("update {} {}->{} on {}",name,oldBean,newBean,_context);
+        _context.updateBean(oldBean,newBean,false);
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java
index f9e0cee..5ea1e0b 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java
@@ -20,67 +20,27 @@
 
 import java.io.IOException;
 
-import javax.servlet.AsyncEvent;
-import javax.servlet.AsyncListener;
 import javax.servlet.DispatcherType;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jetty.server.AsyncContextState;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.RequestLog;
-import org.eclipse.jetty.server.Response;
-import org.eclipse.jetty.util.component.AbstractLifeCycle;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
+import org.eclipse.jetty.server.Server;
 
 
 /**
  * RequestLogHandler.
  * This handler can be used to wrap an individual context for context logging.
+ * To set a {@link RequestLog} instance for the entire {@link Server}, use 
+ * {@link Server#setRequestLog(RequestLog)} instead of this handler.
  *
- *
- * @org.apache.xbean.XBean
+ * @see Server#setRequestLog(RequestLog)
  */
 public class RequestLogHandler extends HandlerWrapper
 {
-    private static final Logger LOG = Log.getLogger(RequestLogHandler.class);
     private RequestLog _requestLog;
-    private final AsyncListener _listener = new AsyncListener()
-    {
-        
-        @Override
-        public void onTimeout(AsyncEvent event) throws IOException
-        {
-            
-        }
-        
-        @Override
-        public void onStartAsync(AsyncEvent event) throws IOException
-        {
-            event.getAsyncContext().addListener(this);
-        }
-        
-        @Override
-        public void onError(AsyncEvent event) throws IOException
-        {
-            HttpServletResponse response = (HttpServletResponse)event.getAsyncContext().getResponse();
-            if (!response.isCommitted())
-                response.setStatus(500);
-            
-        }
-        
-        @Override
-        public void onComplete(AsyncEvent event) throws IOException
-        {
-            AsyncContextState context = (AsyncContextState)event.getAsyncContext();
-            Request request=context.getHttpChannelState().getBaseRequest();
-            Response response=request.getResponse();
-            _requestLog.log(request,response);
-        }
-    };
 
     /* ------------------------------------------------------------ */
     /*
@@ -90,29 +50,10 @@
     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
             throws IOException, ServletException
     {
-        try
-        {
-            super.handle(target, baseRequest, request, response);
-        }
-        catch(Error|IOException|ServletException|RuntimeException e)
-        {
-            if (!response.isCommitted() && !baseRequest.getHttpChannelState().isAsync())
-                response.setStatus(500);
-            throw e;
-        }
-        finally
-        {
-            if (_requestLog != null && baseRequest.getDispatcherType().equals(DispatcherType.REQUEST))
-            {
-                if (baseRequest.getHttpChannelState().isAsync())
-                {
-                    if (baseRequest.getHttpChannelState().isInitial())
-                        baseRequest.getAsyncContext().addListener(_listener);
-                }
-                else
-                    _requestLog.log(baseRequest, (Response)response);
-            }
-        }
+        if (baseRequest.getDispatcherType()==DispatcherType.REQUEST)
+            baseRequest.getHttpChannel().addRequestLog(_requestLog);
+        if (_handler!=null)
+            _handler.handle(target,baseRequest, request, response);
     }
 
     /* ------------------------------------------------------------ */
@@ -128,35 +69,5 @@
         return _requestLog;
     }
     
-    /* ------------------------------------------------------------ */
-    @Override
-    protected void doStart() throws Exception
-    {
-        if (_requestLog==null)
-        {
-            LOG.warn("!RequestLog");
-            _requestLog=new NullRequestLog();
-        }
-        super.doStart();
-    }
-    
-    /* ------------------------------------------------------------ */
-    @Override
-    protected void doStop() throws Exception
-    {
-        super.doStop();
-        if (_requestLog instanceof NullRequestLog)
-            _requestLog=null;
-    }
 
-    /* ------------------------------------------------------------ */
-    /* ------------------------------------------------------------ */
-    /* ------------------------------------------------------------ */
-    private static class NullRequestLog extends AbstractLifeCycle implements RequestLog
-    {
-        @Override
-        public void log(Request request, Response response)
-        {            
-        }
-    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java
index 5d459a8..93776a1 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java
@@ -45,8 +45,9 @@
 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.PathResource;
 import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.resource.ResourceFactory;
 
 
 /* ------------------------------------------------------------ */
@@ -57,9 +58,8 @@
  * Requests for resources that do not exist are let pass (Eg no 404's).
  *
  *
- * @org.apache.xbean.XBean
  */
-public class ResourceHandler extends HandlerWrapper
+public class ResourceHandler extends HandlerWrapper implements ResourceFactory
 {
     private static final Logger LOG = Log.getLogger(ResourceHandler.class);
 
@@ -68,12 +68,13 @@
     Resource _defaultStylesheet;
     Resource _stylesheet;
     String[] _welcomeFiles={"index.html"};
-    MimeTypes _mimeTypes = new MimeTypes();
+    MimeTypes _mimeTypes;
     String _cacheControl;
     boolean _directory;
+    boolean _gzip;
     boolean _etags;
-    int _minMemoryMappedContentLength=1024;
-    int _minAsyncContentLength=0;
+    int _minMemoryMappedContentLength=0;
+    int _minAsyncContentLength=16*1024;
 
     /* ------------------------------------------------------------ */
     public ResourceHandler()
@@ -180,7 +181,8 @@
     {
         Context scontext = ContextHandler.getCurrentContext();
         _context = (scontext==null?null:scontext.getContextHandler());
-
+        _mimeTypes = _context==null?new MimeTypes():_context.getMimeTypes();
+        
         super.doStart();
     }
 
@@ -298,28 +300,28 @@
     /* ------------------------------------------------------------ */
     /*
      */
-    public Resource getResource(String path) throws MalformedURLException
+    @Override
+    public Resource getResource(String path)
     {
-        if (path==null || !path.startsWith("/"))
-            throw new MalformedURLException(path);
-
         if (LOG.isDebugEnabled())
             LOG.debug("{} getResource({})",_context==null?_baseResource:_context,_baseResource,path);
-        
-        Resource base = _baseResource;
-        if (base==null)
-        {
-            if (_context==null)
-                return null;
-            return _context.getResource(path);
-        }
 
+        if (path==null || !path.startsWith("/"))
+            return null;
+        
         try
         {
+            Resource base = _baseResource;
+            if (base==null)
+            {
+                if (_context==null)
+                    return null;
+                return _context.getResource(path);
+            }
+
             path=URIUtil.canonicalPath(path);
             Resource r = base.addPath(path);
-            
-            if (r!=null && r.getAlias()!=null && (_context==null || !_context.checkAlias(path, r)))
+            if (r!=null && r.isAlias() && (_context==null || !_context.checkAlias(path, r)))
             {
                 if (LOG.isDebugEnabled())
                     LOG.debug("resource={} alias={}",r,r.getAlias());
@@ -329,7 +331,7 @@
         }
         catch(Exception e)
         {
-            LOG.ignore(e);
+            LOG.debug(e);
         }
 
         return null;
@@ -451,7 +453,7 @@
             boolean endsWithSlash=(pathInfo==null?request.getServletPath():pathInfo).endsWith(URIUtil.SLASH);
             if (!endsWithSlash)
             {
-                response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)));
+                response.sendRedirect(response.encodeRedirectURL(request.getRequestURI()+URIUtil.SLASH));
                 return;
             }
 
@@ -500,11 +502,12 @@
         doResponseHeaders(response,resource,mime);
         if (_etags)
             baseRequest.getResponse().getHttpFields().put(HttpHeader.ETAG,etag);
+        if (last_modified>0)
+            response.setDateHeader(HttpHeader.LAST_MODIFIED.asString(),last_modified);
         
         if(skipContentBody)
             return;
         
-        
         // Send the content
         OutputStream out =null;
         try {out = response.getOutputStream();}
@@ -543,10 +546,10 @@
                 };
 
                 // Can we use a memory mapped file?
-                if (_minMemoryMappedContentLength>0 && 
+                if (_minMemoryMappedContentLength>=0 && 
                     resource.length()>_minMemoryMappedContentLength &&
                     resource.length()<Integer.MAX_VALUE &&
-                    resource instanceof FileResource)
+                    resource instanceof PathResource)
                 {
                     ByteBuffer buffer = BufferUtil.toMappedBuffer(resource.getFile());
                     ((HttpOutput)out).sendContent(buffer,callback);
@@ -566,7 +569,7 @@
                 // Can we use a memory mapped file?
                 if (_minMemoryMappedContentLength>0 && 
                     resource.length()>_minMemoryMappedContentLength &&
-                    resource instanceof FileResource)
+                    resource instanceof PathResource)
                 {
                     ByteBuffer buffer = BufferUtil.toMappedBuffer(resource.getFile());
                     ((HttpOutput)out).sendContent(buffer);
@@ -590,7 +593,7 @@
         if (_directory)
         {
             String listing = resource.getListHTML(request.getRequestURI(),request.getPathInfo().lastIndexOf("/") > 0);
-            response.setContentType("text/html; charset=UTF-8");
+            response.setContentType("text/html;charset=utf-8");
             response.getWriter().println(listing);
         }
         else
@@ -601,9 +604,9 @@
     /** 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
+     * @param response the http response
+     * @param resource the resource
+     * @param mimeType the mime type
      */
     protected void doResponseHeaders(HttpServletResponse response, Resource resource, String mimeType)
     {
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ScopedHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ScopedHandler.java
index 7f4372c..38acb47 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ScopedHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ScopedHandler.java
@@ -40,7 +40,7 @@
  * {@link #doHandle(String, Request, HttpServletRequest, HttpServletResponse)} method
  * is called on all contained handlers.</p>
  *
- * <p>For example if Scoped handlers A, B & C were chained together, then
+ * <p>For example if Scoped handlers A, B &amp; C were chained together, then
  * the calling order would be:</p>
  * <pre>
  * A.handle(...)
@@ -52,7 +52,7 @@
  *              C.doHandle(...)
  * </pre>
  *
- * <p>If non scoped handler X was in the chained A, B, X & C, then
+ * <p>If non scoped handler X was in the chained A, B, X &amp; C, then
  * the calling order would be:</p>
  * <pre>
  * A.handle(...)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
index c302962..433844e 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
@@ -27,7 +27,6 @@
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.server.HttpChannel;
 import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpConnection;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.util.URIUtil;
 
@@ -42,7 +41,7 @@
     @Override
     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
     {
-        HttpChannel<?> channel = baseRequest.getHttpChannel();
+        HttpChannel channel = baseRequest.getHttpChannel();
         if (baseRequest.isSecure() || (channel == null))
         {
             // nothing to do
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java
index ecaded6..6a278b5 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java
@@ -198,8 +198,10 @@
         doShutdown(baseRequest, response);
     }
 
-    protected void doShutdown(Request baseRequest, HttpServletResponse response) throws IOException {
-        for (Connector connector : getServer().getConnectors()) {
+    protected void doShutdown(Request baseRequest, HttpServletResponse response) throws IOException 
+    {
+        for (Connector connector : getServer().getConnectors()) 
+        {
             connector.shutdown();
         }
 
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java
index 43d2ed0..f494766 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java
@@ -21,9 +21,10 @@
 import java.io.IOException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.LongAdder;
 
 import javax.servlet.AsyncEvent;
 import javax.servlet.AsyncListener;
@@ -31,7 +32,9 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.server.AsyncContextEvent;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HttpChannelState;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Response;
@@ -40,12 +43,15 @@
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.annotation.ManagedOperation;
 import org.eclipse.jetty.util.component.Graceful;
+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;
 
 @ManagedObject("Request Statistics Gathering")
 public class StatisticsHandler extends HandlerWrapper implements Graceful
 {
+    private static final Logger LOG = Log.getLogger(StatisticsHandler.class);
     private final AtomicLong _statsStartedAt = new AtomicLong();
 
     private final CounterStatistic _requestStats = new CounterStatistic();
@@ -54,24 +60,26 @@
     private final SampleStatistic _dispatchedTimeStats = new SampleStatistic();
     private final CounterStatistic _asyncWaitStats = new CounterStatistic();
 
-    private final AtomicInteger _asyncDispatches = new AtomicInteger();
-    private final AtomicInteger _expires = new AtomicInteger();
+    private final LongAdder _asyncDispatches = new LongAdder();
+    private final LongAdder _expires = new LongAdder();
 
-    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 LongAdder _responses1xx = new LongAdder();
+    private final LongAdder _responses2xx = new LongAdder();
+    private final LongAdder _responses3xx = new LongAdder();
+    private final LongAdder _responses4xx = new LongAdder();
+    private final LongAdder _responses5xx = new LongAdder();
+    private final LongAdder _responsesTotalBytes = new LongAdder();
 
     private final AtomicReference<FutureCallback> _shutdown=new AtomicReference<>();
     
+    private final AtomicBoolean _wrapWarning = new AtomicBoolean();
+    
     private final AsyncListener _onCompletion = new AsyncListener()
     {
         @Override
         public void onTimeout(AsyncEvent event) throws IOException
         {
-            _expires.incrementAndGet();
+            _expires.increment();
         }
         
         @Override
@@ -124,39 +132,51 @@
         _dispatchedTimeStats.reset();
         _asyncWaitStats.reset();
 
-        _asyncDispatches.set(0);
-        _expires.set(0);
-        _responses1xx.set(0);
-        _responses2xx.set(0);
-        _responses3xx.set(0);
-        _responses4xx.set(0);
-        _responses5xx.set(0);
-        _responsesTotalBytes.set(0L);
+        _asyncDispatches.reset();
+        _expires.reset();
+        _responses1xx.reset();
+        _responses2xx.reset();
+        _responses3xx.reset();
+        _responses4xx.reset();
+        _responses5xx.reset();
+        _responsesTotalBytes.reset();
     }
 
     @Override
-    public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
+    public void handle(String path, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
     {
         _dispatchedStats.increment();
 
         final long start;
-        HttpChannelState state = request.getHttpChannelState();
+        HttpChannelState state = baseRequest.getHttpChannelState();
         if (state.isInitial())
         {
             // new request
             _requestStats.increment();
-            start = request.getTimeStamp();
+            start = baseRequest.getTimeStamp();
         }
         else
         {
             // resumed request
             start = System.currentTimeMillis();
-            _asyncDispatches.incrementAndGet();
+            _asyncDispatches.increment();
         }
 
         try
         {
-            super.handle(path, request, httpRequest, httpResponse);
+            Handler handler = getHandler();
+            if (handler!=null && _shutdown.get()==null && isStarted())
+                handler.handle(path, baseRequest, request, response);
+            else if (baseRequest.isHandled())
+            {
+                if (_wrapWarning.compareAndSet(false,true))
+                    LOG.warn("Bad statistics configuration. Latencies will be incorrect in {}",this);
+            }
+            else
+            {
+                baseRequest.setHandled(true);
+                response.sendError(HttpStatus.SERVICE_UNAVAILABLE_503);
+            }
         }
         finally
         {
@@ -178,13 +198,13 @@
             {
                 long d=_requestStats.decrement();
                 _requestTimeStats.set(dispatched);
-                updateResponse(request);
+                updateResponse(baseRequest);
                 
                 // If we have no more dispatches, should we signal shutdown?
                 FutureCallback shutdown = _shutdown.get();
                 if (shutdown!=null)
                 {
-                    httpResponse.flushBuffer();
+                    response.flushBuffer();
                     if (d==0)
                         shutdown.succeeded();
                 }   
@@ -201,19 +221,19 @@
             switch (response.getStatus() / 100)
             {
                 case 1:
-                    _responses1xx.incrementAndGet();
+                    _responses1xx.increment();
                     break;
                 case 2:
-                    _responses2xx.incrementAndGet();
+                    _responses2xx.increment();
                     break;
                 case 3:
-                    _responses3xx.incrementAndGet();
+                    _responses3xx.increment();
                     break;
                 case 4:
-                    _responses4xx.incrementAndGet();
+                    _responses4xx.increment();
                     break;
                 case 5:
-                    _responses5xx.incrementAndGet();
+                    _responses5xx.increment();
                     break;
                 default:
                     break;
@@ -221,8 +241,8 @@
         }
         else
             // will fall through to not found handler
-            _responses4xx.incrementAndGet();
-        _responsesTotalBytes.addAndGet(response.getContentCount());
+            _responses4xx.increment();
+        _responsesTotalBytes.add(response.getContentCount());
     }
 
     @Override
@@ -434,7 +454,7 @@
     @ManagedAttribute("number of requested that have been asynchronously dispatched")
     public int getAsyncDispatches()
     {
-        return _asyncDispatches.get();
+        return _asyncDispatches.intValue();
     }
 
     /**
@@ -444,7 +464,7 @@
     @ManagedAttribute("number of async requests requests that have expired")
     public int getExpires()
     {
-        return _expires.get();
+        return _expires.intValue();
     }
 
     /**
@@ -454,7 +474,7 @@
     @ManagedAttribute("number of requests with 1xx response status")
     public int getResponses1xx()
     {
-        return _responses1xx.get();
+        return _responses1xx.intValue();
     }
 
     /**
@@ -464,7 +484,7 @@
     @ManagedAttribute("number of requests with 2xx response status")
     public int getResponses2xx()
     {
-        return _responses2xx.get();
+        return _responses2xx.intValue();
     }
 
     /**
@@ -474,7 +494,7 @@
     @ManagedAttribute("number of requests with 3xx response status")
     public int getResponses3xx()
     {
-        return _responses3xx.get();
+        return _responses3xx.intValue();
     }
 
     /**
@@ -484,7 +504,7 @@
     @ManagedAttribute("number of requests with 4xx response status")
     public int getResponses4xx()
     {
-        return _responses4xx.get();
+        return _responses4xx.intValue();
     }
 
     /**
@@ -494,7 +514,7 @@
     @ManagedAttribute("number of requests with 5xx response status")
     public int getResponses5xx()
     {
-        return _responses5xx.get();
+        return _responses5xx.intValue();
     }
 
     /**
@@ -512,7 +532,7 @@
     @ManagedAttribute("total number of bytes across all responses")
     public long getResponsesBytesTotal()
     {
-        return _responsesTotalBytes.get();
+        return _responsesTotalBytes.longValue();
     }
 
     public String toStatsHTML()
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ThreadLimitHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ThreadLimitHandler.java
new file mode 100644
index 0000000..02ef79f
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ThreadLimitHandler.java
@@ -0,0 +1,440 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.Closeable;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.QuotedCSV;
+import org.eclipse.jetty.server.ForwardedRequestCustomizer;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.IncludeExcludeSet;
+import org.eclipse.jetty.util.InetAddressSet;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedOperation;
+import org.eclipse.jetty.util.annotation.Name;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Locker;
+
+
+/**
+ * <p>Handler to limit the threads per IP address for DOS protection</p>
+ * <p>The ThreadLimitHandler applies a limit to the number of Threads 
+ * that can be used simultaneously per remote IP address.
+ * </p>
+ * <p>The handler makes a determination of the remote IP separately to
+ * any that may be made by the {@link ForwardedRequestCustomizer} or similar:
+ * <ul>
+ * <li>This handler will use either only a single style
+ * of forwarded header.   This is on the assumption that a trusted local proxy
+ * will produce only a single forwarded header and that any additional
+ * headers are likely from untrusted client side proxies.</li>
+ * <li>If multiple instances of a forwarded header are provided, this
+ * handler will use the right-most instance, which will have been set from
+ * the trusted local proxy</li>
+ * </ul>
+ * Requests in excess of the limit will be asynchronously suspended until
+ * a thread is available.  
+ * <p>This is a simpler alternative to DosFilter</p>
+ */
+public class ThreadLimitHandler extends HandlerWrapper
+{
+    private static final Logger LOG = Log.getLogger(ThreadLimitHandler.class);
+
+    private final static String REMOTE = "o.e.j.s.h.TLH.REMOTE";
+    private final static String PERMIT = "o.e.j.s.h.TLH.PASS";
+    private final boolean _rfc7239;
+    private final String _forwardedHeader;
+    private final IncludeExcludeSet<String, InetAddress> _includeExcludeSet = new IncludeExcludeSet<>(InetAddressSet.class);
+    private final ConcurrentMap<String, Remote> _remotes = new ConcurrentHashMap<>();
+    private volatile boolean _enabled;
+    private int _threadLimit=10;
+
+    public ThreadLimitHandler()
+    {
+        this(null,false);
+    }
+    
+    public ThreadLimitHandler(@Name("forwardedHeader") String forwardedHeader)
+    {
+        this(forwardedHeader,HttpHeader.FORWARDED.is(forwardedHeader));
+    }
+
+    public ThreadLimitHandler(@Name("forwardedHeader") String forwardedHeader, @Name("rfc7239")  boolean rfc7239)
+    {
+        super();
+        _rfc7239 = rfc7239;
+        _forwardedHeader = forwardedHeader;
+        _enabled = true;
+    }
+    
+    @Override
+    protected void doStart() throws Exception
+    {
+        super.doStart();
+        LOG.info(String.format("ThreadLimitHandler enable=%b limit=%d include=%s",_enabled,_threadLimit,_includeExcludeSet));
+    }
+
+    @ManagedAttribute("true if this handler is enabled")
+    public boolean isEnabled()
+    {
+        return _enabled;
+    }
+
+    public void setEnabled(boolean enabled)
+    {
+        _enabled = enabled;
+        LOG.info(String.format("ThreadLimitHandler enable=%b limit=%d include=%s",_enabled,_threadLimit,_includeExcludeSet));
+    }
+
+    @ManagedAttribute("The maximum threads that can be dispatched per remote IP")
+    public int getThreadLimit()
+    {
+        return _threadLimit;
+    }
+
+    public void setThreadLimit(int threadLimit)
+    {
+        if (threadLimit<=0)
+            throw new IllegalArgumentException("limit must be >0");
+        _threadLimit = threadLimit;
+    }
+    
+    @ManagedOperation("Include IP in thread limits")
+    public void include(String inetAddressPattern)
+    {
+        _includeExcludeSet.include(inetAddressPattern);
+    }
+
+    @ManagedOperation("Exclude IP from thread limits")
+    public void exclude(String inetAddressPattern)
+    {
+        _includeExcludeSet.exclude(inetAddressPattern);
+    }
+    
+    @Override
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        // Allow ThreadLimit to be enabled dynamically without restarting server
+        if (!_enabled)
+        {
+            // if disabled, handle normally
+            super.handle(target,baseRequest,request,response);
+        }
+        else
+        {
+            // Get the remote address of the request
+            Remote remote = getRemote(baseRequest);
+            if (remote==null)
+            {
+                // if remote is not known, handle normally
+                super.handle(target,baseRequest,request,response);
+            }
+            else
+            {
+                // Do we already have a future permit from a previous invocation?
+                Closeable permit = (Closeable)baseRequest.getAttribute(PERMIT);
+                try
+                {
+                    if (permit!=null)
+                    {
+                        // Yes, remove it from any future async cycles.
+                        baseRequest.removeAttribute(PERMIT);
+                    }
+                    else
+                    {
+                        // No, then lets try to acquire one
+                        CompletableFuture<Closeable> future_permit=remote.acquire();
+
+                        // Did we get a permit?
+                        if (future_permit.isDone())
+                        {
+                            // yes
+                            permit=future_permit.get();
+                        }
+                        else
+                        {
+                            if (LOG.isDebugEnabled())
+                                LOG.debug("Threadlimited {} {}",remote,target);
+                            // No, lets asynchronously suspend the request
+                            AsyncContext async = baseRequest.startAsync();
+                            // let's never timeout the async.  If this is a DOS, then good to make them wait, if this is not
+                            // then give them maximum time to get a thread.
+                            async.setTimeout(0);
+
+                            // dispatch the request when we do eventually get a pass
+                            future_permit.thenAccept(c->
+                            {
+                                baseRequest.setAttribute(PERMIT,c);
+                                async.dispatch();
+                            });
+                            return;
+                        }
+                    }
+
+                    // Use the permit
+                    super.handle(target,baseRequest,request,response);
+                }
+                catch (InterruptedException | ExecutionException e)
+                {
+                    throw new ServletException(e);
+                }
+                finally
+                {
+                    if (permit!=null)
+                        permit.close();
+                }
+            }
+        }
+    }
+    
+    protected int getThreadLimit(String ip)
+    {
+        if (!_includeExcludeSet.isEmpty())
+        {
+            try
+            {
+                if (!_includeExcludeSet.test(InetAddress.getByName(ip)))
+                {
+                    LOG.debug("excluded {}",ip);
+                    return 0;
+                }
+            }
+            catch(Exception e)
+            {
+                LOG.ignore(e);
+            }
+        }
+        return _threadLimit;
+    }
+
+    protected Remote getRemote(Request baseRequest)
+    {
+        Remote remote = (Remote)baseRequest.getAttribute(REMOTE);
+        if (remote!=null)
+            return remote;
+        
+        String ip=getRemoteIP(baseRequest);
+        LOG.debug("ip={}",ip);
+        if (ip==null)
+            return null;
+        
+        int limit = getThreadLimit(ip);
+        if (limit<=0)
+            return null;
+
+        remote = _remotes.get(ip);
+        if (remote==null)
+        {
+            Remote r = new Remote(ip,limit);
+            remote = _remotes.putIfAbsent(ip,r);
+            if (remote==null)
+                remote = r;
+        }
+        
+        baseRequest.setAttribute(REMOTE,remote);
+
+        return remote;
+    }
+    
+    
+    protected String getRemoteIP(Request baseRequest)
+    {
+        // Do we have a forwarded header set?
+        if (_forwardedHeader!=null && !_forwardedHeader.isEmpty())
+        {
+            // Yes, then try to get the remote IP from the header
+            String remote = _rfc7239?getForwarded(baseRequest):getXForwardedFor(baseRequest);
+            if (remote!=null && !remote.isEmpty())
+                return remote;
+        }
+        
+        // If no remote IP from a header, determine it directly from the channel
+        // Do not use the request methods, as they may have been lied to by the 
+        // RequestCustomizer!
+        InetSocketAddress inet_addr = baseRequest.getHttpChannel().getRemoteAddress();
+        if (inet_addr!=null && inet_addr.getAddress()!=null)
+            return inet_addr.getAddress().getHostAddress();
+        return null;
+    }
+
+    private String getForwarded(Request request)
+    {
+        // Get the right most Forwarded for value.
+        // This is the value from the closest proxy and the only one that
+        // can be trusted.
+        RFC7239 rfc7239 = new RFC7239();
+        HttpFields httpFields = request.getHttpFields();
+        for (HttpField field : httpFields)
+            if (_forwardedHeader.equalsIgnoreCase(field.getName()))
+                rfc7239.addValue(field.getValue());
+        
+        if (rfc7239.getFor()!=null)
+            return new HostPortHttpField(rfc7239.getFor()).getHost();
+        
+        return null;
+    }
+    
+    private String getXForwardedFor(Request request)
+    {
+        // Get the right most XForwarded-For for value.
+        // This is the value from the closest proxy and the only one that
+        // can be trusted.
+        String forwarded_for = null;
+        HttpFields httpFields = request.getHttpFields();
+        for (HttpField field : httpFields)
+            if (_forwardedHeader.equalsIgnoreCase(field.getName()))
+                forwarded_for = field.getValue();
+        
+        if (forwarded_for==null || forwarded_for.isEmpty())
+            return null;
+        
+        int comma = forwarded_for.lastIndexOf(',');
+        return (comma>=0)?forwarded_for.substring(comma+1).trim():forwarded_for;
+    }
+
+
+    private final class Remote implements Closeable
+    {
+        private final String _ip;
+        private final int _limit;
+        private final Locker _locker = new Locker();
+        private int _permits;
+        private Deque<CompletableFuture<Closeable>> _queue = new ArrayDeque<>();
+        private final CompletableFuture<Closeable> _permitted = CompletableFuture.completedFuture(this);
+        
+        public Remote(String ip, int limit)
+        {
+            _ip=ip;
+            _limit=limit;
+        }
+        
+        public CompletableFuture<Closeable> acquire()
+        {
+            try(Locker.Lock lock = _locker.lock())
+            {
+                // Do we have available passes?
+                if (_permits<_limit)
+                {
+                    // Yes - increment the allocated passes
+                    _permits++;
+                    // return the already completed future
+                    return _permitted; // TODO is it OK to share/reuse this?
+                }
+                
+                // No pass available, so queue a new future 
+                CompletableFuture<Closeable> pass = new CompletableFuture<Closeable>();
+                _queue.addLast(pass);
+                return pass;
+            }
+        }
+        
+        @Override
+        public void close() throws IOException
+        {
+            try(Locker.Lock lock = _locker.lock())
+            {
+                // reduce the allocated passes
+                _permits--;
+                while(true)
+                {
+                    // Are there any future passes waiting?
+                    CompletableFuture<Closeable> permit = _queue.pollFirst();
+                    
+                    // No - we are done
+                    if (permit==null)
+                        break;
+                    
+                    // Yes - if we can complete them, we are done
+                    if (permit.complete(this))
+                    {
+                        _permits++;
+                        break;
+                    }
+                    
+                    // Somebody else must have completed/failed that future pass,
+                    // so let's try for another.
+                }
+            }
+        }
+        
+        @Override
+        public String toString()
+        {
+            try(Locker.Lock lock = _locker.lock())
+            {
+                return String.format("R[ip=%s,p=%d,l=%d,q=%d]",_ip,_permits,_limit,_queue.size());
+            }
+        }
+    }
+
+    private final class RFC7239 extends QuotedCSV
+    {
+        String _for;
+        
+        private RFC7239()
+        {
+            super(false);
+        }
+        
+        String getFor()
+        {
+            return _for;
+        }
+
+        @Override
+        protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue)
+        {
+            if (valueLength==0 && paramValue>paramName)
+            {
+                String name=StringUtil.asciiToLowerCase(buffer.substring(paramName,paramValue-1));
+                if ("for".equalsIgnoreCase(name))
+                {
+                    String value=buffer.substring(paramValue);
+                    
+                    // if unknown, clear any leftward values
+                    if ("unknown".equalsIgnoreCase(value))
+                        _for = null;
+                    // Otherwise accept IP or token(starting with '_') as remote keys
+                    else
+                        _for=value;
+                }
+            }
+        }
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipFactory.java
new file mode 100644
index 0000000..ad60d93
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipFactory.java
@@ -0,0 +1,32 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.util.zip.Deflater;
+
+import org.eclipse.jetty.server.Request;
+
+public interface GzipFactory
+{
+    Deflater getDeflater(Request request, long content_length);
+
+    boolean isMimeTypeGzipable(String mimetype);
+
+    void recycle(Deflater deflater);
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java
new file mode 100644
index 0000000..d645c8d
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java
@@ -0,0 +1,671 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.zip.Deflater;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.GzipHttpContent;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http.pathmap.PathSpecSet;
+import org.eclipse.jetty.server.HttpOutput;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
+import org.eclipse.jetty.util.IncludeExclude;
+import org.eclipse.jetty.util.RegexSet;
+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;
+
+/**
+ * A Handler that can dynamically GZIP compress responses.   Unlike
+ * previous and 3rd party GzipFilters, this mechanism works with asynchronously
+ * generated responses and does not need to wrap the response or it's output
+ * stream.  Instead it uses the efficient {@link org.eclipse.jetty.server.HttpOutput.Interceptor} mechanism.
+ * <p>
+ * The handler can be applied to the entire server (a gzip.mod is included in
+ * the distribution) or it may be applied to individual contexts.
+ * </p>
+ */
+public class GzipHandler extends HandlerWrapper implements GzipFactory
+{
+    private static final Logger LOG = Log.getLogger(GzipHandler.class);
+
+    public final static String GZIP = "gzip";
+    public final static String DEFLATE = "deflate";
+    public final static int DEFAULT_MIN_GZIP_SIZE=16;
+    private int _minGzipSize=DEFAULT_MIN_GZIP_SIZE;
+    private int _compressionLevel=Deflater.DEFAULT_COMPRESSION;
+    private boolean _checkGzExists = true;
+    private boolean _syncFlush = false;
+    private EnumSet<DispatcherType> _dispatchers = EnumSet.of(DispatcherType.REQUEST);
+
+    // non-static, as other GzipHandler instances may have different configurations
+    private final ThreadLocal<Deflater> _deflater = new ThreadLocal<>();
+
+    private final IncludeExclude<String> _agentPatterns=new IncludeExclude<>(RegexSet.class);
+    private final IncludeExclude<String> _methods = new IncludeExclude<>();
+    private final IncludeExclude<String> _paths = new IncludeExclude<>(PathSpecSet.class);
+    private final IncludeExclude<String> _mimeTypes = new IncludeExclude<>();
+
+    private HttpField _vary;
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Instantiates a new gzip handler.
+     * The excluded Mime Types are initialized to common known
+     * images, audio, video and other already compressed types.
+     * The included methods is initialized to GET.
+     * The excluded agent patterns are set to exclude MSIE 6.0
+     */
+    public GzipHandler()
+    {
+        _methods.include(HttpMethod.GET.asString());
+        for (String type:MimeTypes.getKnownMimeTypes())
+        {
+            if ("image/svg+xml".equals(type))
+                _paths.exclude("*.svgz");
+            else if (type.startsWith("image/")||
+                type.startsWith("audio/")||
+                type.startsWith("video/"))
+                _mimeTypes.exclude(type);
+        }
+        _mimeTypes.exclude("application/compress");
+        _mimeTypes.exclude("application/zip");
+        _mimeTypes.exclude("application/gzip");
+        _mimeTypes.exclude("application/bzip2");
+        _mimeTypes.exclude("application/x-rar-compressed");
+        LOG.debug("{} mime types {}",this,_mimeTypes);
+
+        _agentPatterns.exclude(".*MSIE 6.0.*");
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param patterns Regular expressions matching user agents to exclude
+     */
+    public void addExcludedAgentPatterns(String... patterns)
+    {
+        _agentPatterns.exclude(patterns);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param methods The methods to exclude in compression
+     */
+    public void addExcludedMethods(String... methods)
+    {
+        for (String m : methods)
+            _methods.exclude(m);
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    public EnumSet<DispatcherType> getDispatcherTypes()
+    {
+        return _dispatchers;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setDispatcherTypes(EnumSet<DispatcherType> dispatchers)
+    {
+        _dispatchers = dispatchers;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setDispatcherTypes(DispatcherType... dispatchers)
+    {
+        _dispatchers = EnumSet.copyOf(Arrays.asList(dispatchers));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the mime types.
+     * @param types The mime types to exclude (without charset or other parameters).
+     * For backward compatibility the mimetypes may be comma separated strings, but this
+     * will not be supported in future versions.
+     */
+    public void addExcludedMimeTypes(String... types)
+    {
+        for (String t : types)
+            _mimeTypes.exclude(StringUtil.csvSplit(t));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param pathspecs Path specs (as per servlet spec) to exclude. If a
+     * ServletContext is available, the paths are relative to the context path,
+     * otherwise they are absolute.
+     * For backward compatibility the pathspecs may be comma separated strings, but this
+     * will not be supported in future versions.
+     */
+    public void addExcludedPaths(String... pathspecs)
+    {
+        for (String p : pathspecs)
+            _paths.exclude(StringUtil.csvSplit(p));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param patterns Regular expressions matching user agents to exclude
+     */
+    public void addIncludedAgentPatterns(String... patterns)
+    {
+        _agentPatterns.include(patterns);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param methods The methods to include in compression
+     */
+    public void addIncludedMethods(String... methods)
+    {
+        for (String m : methods)
+            _methods.include(m);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if {@link Deflater#SYNC_FLUSH} is used, else {@link Deflater#NO_FLUSH}
+     */
+    public boolean isSyncFlush()
+    {
+        return _syncFlush;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * <p>Set the {@link Deflater} flush mode to use.  {@link Deflater#SYNC_FLUSH}
+     * should be used if the application wishes to stream the data, but this may
+     * hurt compression performance.
+     * @param syncFlush True if {@link Deflater#SYNC_FLUSH} is used, else {@link Deflater#NO_FLUSH}
+     */
+    public void setSyncFlush(boolean syncFlush)
+    {
+        _syncFlush = syncFlush;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Add included mime types. Inclusion takes precedence over
+     * exclusion.
+     * @param types The mime types to include (without charset or other parameters)
+     * For backward compatibility the mimetypes may be comma separated strings, but this
+     * will not be supported in future versions.
+     */
+    public void addIncludedMimeTypes(String... types)
+    {
+        for (String t : types)
+            _mimeTypes.include(StringUtil.csvSplit(t));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Add path specs to include. Inclusion takes precedence over exclusion.
+     * @param pathspecs Path specs (as per servlet spec) to include. If a
+     * ServletContext is available, the paths are relative to the context path,
+     * otherwise they are absolute
+     * For backward compatibility the pathspecs may be comma separated strings, but this
+     * will not be supported in future versions.
+     */
+    public void addIncludedPaths(String... pathspecs)
+    {
+        for (String p : pathspecs)
+            _paths.include(StringUtil.csvSplit(p));
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStart() throws Exception
+    {
+        _vary=(_agentPatterns.size()>0)?GzipHttpOutputInterceptor.VARY_ACCEPT_ENCODING_USER_AGENT:GzipHttpOutputInterceptor.VARY_ACCEPT_ENCODING;
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean getCheckGzExists()
+    {
+        return _checkGzExists;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getCompressionLevel()
+    {
+        return _compressionLevel;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public Deflater getDeflater(Request request, long content_length)
+    {
+        String ua = request.getHttpFields().get(HttpHeader.USER_AGENT);
+        if (ua!=null && !isAgentGzipable(ua))
+        {
+            LOG.debug("{} excluded user agent {}",this,request);
+            return null;
+        }
+
+        if (content_length>=0 && content_length<_minGzipSize)
+        {
+            LOG.debug("{} excluded minGzipSize {}",this,request);
+            return null;
+        }
+
+        // check the accept encoding header
+        HttpField accept = request.getHttpFields().getField(HttpHeader.ACCEPT_ENCODING);
+
+        if (accept==null)
+        {
+            LOG.debug("{} excluded !accept {}",this,request);
+            return null;
+        }
+        boolean gzip = accept.contains("gzip");
+
+        if (!gzip)
+        {
+            LOG.debug("{} excluded not gzip accept {}",this,request);
+            return null;
+        }
+
+        Deflater df = _deflater.get();
+        if (df==null)
+            df=new Deflater(_compressionLevel,true);
+        else
+            _deflater.set(null);
+
+        return df;
+    }
+
+    /* ------------------------------------------------------------ */
+    public String[] getExcludedAgentPatterns()
+    {
+        Set<String> excluded=_agentPatterns.getExcluded();
+        return excluded.toArray(new String[excluded.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    public String[] getExcludedMethods()
+    {
+        Set<String> excluded=_methods.getExcluded();
+        return excluded.toArray(new String[excluded.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    public String[] getExcludedMimeTypes()
+    {
+        Set<String> excluded=_mimeTypes.getExcluded();
+        return excluded.toArray(new String[excluded.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    public String[] getExcludedPaths()
+    {
+        Set<String> excluded=_paths.getExcluded();
+        return excluded.toArray(new String[excluded.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    public String[] getIncludedAgentPatterns()
+    {
+        Set<String> includes=_agentPatterns.getIncluded();
+        return includes.toArray(new String[includes.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    public String[] getIncludedMethods()
+    {
+        Set<String> includes=_methods.getIncluded();
+        return includes.toArray(new String[includes.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    public String[] getIncludedMimeTypes()
+    {
+        Set<String> includes=_mimeTypes.getIncluded();
+        return includes.toArray(new String[includes.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    public String[] getIncludedPaths()
+    {
+        Set<String> includes=_paths.getIncluded();
+        return includes.toArray(new String[includes.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Deprecated
+    public String[] getMethods()
+    {
+        return getIncludedMethods();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get the minimum response size.
+     *
+     * @return minimum response size
+     */
+    public int getMinGzipSize()
+    {
+        return _minGzipSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected HttpField getVaryField()
+    {
+        return _vary;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @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
+    {
+        ServletContext context = baseRequest.getServletContext();
+        String path = context==null?baseRequest.getRequestURI():URIUtil.addPaths(baseRequest.getServletPath(),baseRequest.getPathInfo());
+        LOG.debug("{} handle {} in {}",this,baseRequest,context);
+
+        if (!_dispatchers.contains(baseRequest.getDispatcherType()))
+        {
+            LOG.debug("{} excluded by dispatcherType {}",this,baseRequest.getDispatcherType());
+            _handler.handle(target,baseRequest, request, response);
+            return;
+        }
+
+        // Are we already being gzipped?
+        HttpOutput out = baseRequest.getResponse().getHttpOutput();
+        HttpOutput.Interceptor interceptor = out.getInterceptor();
+        while (interceptor!=null)
+        {
+            if (interceptor instanceof GzipHttpOutputInterceptor)
+            {
+                LOG.debug("{} already intercepting {}",this,request);
+                _handler.handle(target,baseRequest, request, response);
+                return;
+            }
+            interceptor=interceptor.getNextInterceptor();
+        }
+
+        // If not a supported method - no Vary because no matter what client, this URI is always excluded
+        if (!_methods.matches(baseRequest.getMethod()))
+        {
+            LOG.debug("{} excluded by method {}",this,request);
+            _handler.handle(target,baseRequest, request, response);
+            return;
+        }
+
+        // If not a supported URI- no Vary because no matter what client, this URI is always excluded
+        // Use pathInfo because this is be
+        if (!isPathGzipable(path))
+        {
+            LOG.debug("{} excluded by path {}",this,request);
+            _handler.handle(target,baseRequest, request, response);
+            return;
+        }
+
+        // Exclude non compressible mime-types known from URI extension. - no Vary because no matter what client, this URI is always excluded
+        String mimeType = context==null?MimeTypes.getDefaultMimeByExtension(path):context.getMimeType(path);
+        if (mimeType!=null)
+        {
+            mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
+            if (!isMimeTypeGzipable(mimeType))
+            {
+                LOG.debug("{} excluded by path suffix mime type {}",this,request);
+                // handle normally without setting vary header
+                _handler.handle(target,baseRequest, request, response);
+                return;
+            }
+        }
+
+        if (_checkGzExists && context!=null)
+        {
+            String realpath=request.getServletContext().getRealPath(path);
+            if (realpath!=null)
+            {
+                File gz=new File(realpath+".gz");
+                if (gz.exists())
+                {
+                    LOG.debug("{} gzip exists {}",this,request);
+                    // allow default servlet to handle
+                    _handler.handle(target,baseRequest, request, response);
+                    return;
+                }
+            }
+        }
+
+        // Special handling for etags
+        String etag = baseRequest.getHttpFields().get(HttpHeader.IF_NONE_MATCH);
+        if (etag!=null)
+        {
+            int i=etag.indexOf(GzipHttpContent.ETAG_GZIP_QUOTE);
+            if (i>0)
+            {
+                baseRequest.setAttribute("o.e.j.s.h.gzip.GzipHandler.etag",etag);
+                while (i>=0)
+                {
+                    etag=etag.substring(0,i)+etag.substring(i+GzipHttpContent.ETAG_GZIP.length());
+                    i=etag.indexOf(GzipHttpContent.ETAG_GZIP_QUOTE,i);
+                }
+                baseRequest.getHttpFields().put(new HttpField(HttpHeader.IF_NONE_MATCH,etag));
+            }
+        }
+
+        HttpOutput.Interceptor orig_interceptor = out.getInterceptor();
+        try
+        {
+            // install interceptor and handle
+            out.setInterceptor(new GzipHttpOutputInterceptor(this,getVaryField(),baseRequest.getHttpChannel(),orig_interceptor,isSyncFlush()));
+
+            if (_handler!=null)
+                _handler.handle(target,baseRequest, request, response);
+        }
+        finally
+        {
+            // reset interceptor if request not handled
+            if (!baseRequest.isHandled() && !baseRequest.isAsyncStarted())
+                out.setInterceptor(orig_interceptor);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Checks to see if the userAgent is excluded
+     *
+     * @param ua the user agent
+     * @return boolean true if excluded
+     */
+    protected boolean isAgentGzipable(String ua)
+    {
+        if (ua == null)
+            return false;
+
+        return _agentPatterns.matches(ua);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isMimeTypeGzipable(String mimetype)
+    {
+        return _mimeTypes.matches(mimetype);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Checks to see if the path is included or not excluded
+     *
+     * @param requestURI
+     *            the request uri
+     * @return boolean true if gzipable
+     */
+    protected boolean isPathGzipable(String requestURI)
+    {
+        if (requestURI == null)
+            return true;
+
+        return _paths.matches(requestURI);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void recycle(Deflater deflater)
+    {
+        if (_deflater.get()==null)
+        {
+            deflater.reset();
+            _deflater.set(deflater);
+        }
+        else
+            deflater.end();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param checkGzExists If true, check if a static gz file exists for
+     * the resource that the DefaultServlet may serve as precompressed.
+     */
+    public void setCheckGzExists(boolean checkGzExists)
+    {
+        _checkGzExists = checkGzExists;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param compressionLevel  The compression level to use to initialize {@link Deflater#setLevel(int)}
+     */
+    public void setCompressionLevel(int compressionLevel)
+    {
+        _compressionLevel = compressionLevel;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param patterns Regular expressions matching user agents to exclude
+     */
+    public void setExcludedAgentPatterns(String... patterns)
+    {
+        _agentPatterns.getExcluded().clear();
+        addExcludedAgentPatterns(patterns);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param method to exclude
+     */
+    public void setExcludedMethods(String... method)
+    {
+        _methods.getExcluded().clear();
+        _methods.exclude(method);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the mime types.
+     * @param types The mime types to exclude (without charset or other parameters)
+     */
+    public void setExcludedMimeTypes(String... types)
+    {
+        _mimeTypes.getExcluded().clear();
+        _mimeTypes.exclude(types);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param pathspecs Path specs (as per servlet spec) to exclude. If a
+     * ServletContext is available, the paths are relative to the context path,
+     * otherwise they are absolute.
+     */
+    public void setExcludedPaths(String... pathspecs)
+    {
+        _paths.getExcluded().clear();
+        _paths.exclude(pathspecs);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param patterns Regular expressions matching user agents to include
+     */
+    public void setIncludedAgentPatterns(String... patterns)
+    {
+        _agentPatterns.getIncluded().clear();
+        addIncludedAgentPatterns(patterns);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param methods The methods to include in compression
+     */
+    public void setIncludedMethods(String... methods)
+    {
+        _methods.getIncluded().clear();
+        _methods.include(methods);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set included mime types. Inclusion takes precedence over
+     * exclusion.
+     * @param types The mime types to include (without charset or other parameters)
+     */
+    public void setIncludedMimeTypes(String... types)
+    {
+        _mimeTypes.getIncluded().clear();
+        _mimeTypes.include(types);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the path specs to include. Inclusion takes precedence over exclusion.
+     * @param pathspecs Path specs (as per servlet spec) to include. If a
+     * ServletContext is available, the paths are relative to the context path,
+     * otherwise they are absolute
+     */
+    public void setIncludedPaths(String... pathspecs)
+    {
+        _paths.getIncluded().clear();
+        _paths.include(pathspecs);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the minimum response size to trigger dynamic compresssion
+     *
+     * @param minGzipSize minimum response size in bytes
+     */
+    public void setMinGzipSize(int minGzipSize)
+    {
+        _minGzipSize = minGzipSize;
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java
new file mode 100644
index 0000000..c0885f3
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java
@@ -0,0 +1,394 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.WritePendingException;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+
+import org.eclipse.jetty.http.GzipHttpContent;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http.PreEncodedHttpField;
+import org.eclipse.jetty.http.QuotedCSV;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.HttpOutput;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IteratingNestedCallback;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
+{
+    public static Logger LOG = Log.getLogger(GzipHttpOutputInterceptor.class);
+    private final static byte[] GZIP_HEADER = new byte[] { (byte)0x1f, (byte)0x8b, Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0 };
+
+    public final static HttpField VARY_ACCEPT_ENCODING_USER_AGENT=new PreEncodedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING+", "+HttpHeader.USER_AGENT);
+    public final static HttpField VARY_ACCEPT_ENCODING=new PreEncodedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING.asString());
+
+    private enum GZState {  MIGHT_COMPRESS, NOT_COMPRESSING, COMMITTING, COMPRESSING, FINISHED};
+    private final AtomicReference<GZState> _state = new AtomicReference<>(GZState.MIGHT_COMPRESS);
+    private final CRC32 _crc = new CRC32();
+
+    private final GzipFactory _factory;
+    private final HttpOutput.Interceptor _interceptor;
+    private final HttpChannel _channel;
+    private final HttpField _vary;
+    private final int _bufferSize;
+    private final boolean _syncFlush;
+
+    private Deflater _deflater;
+    private ByteBuffer _buffer;
+
+    public GzipHttpOutputInterceptor(GzipFactory factory, HttpChannel channel, HttpOutput.Interceptor next,boolean syncFlush)
+    {
+        this(factory,VARY_ACCEPT_ENCODING_USER_AGENT,channel.getHttpConfiguration().getOutputBufferSize(),channel,next,syncFlush);
+    }
+
+    public GzipHttpOutputInterceptor(GzipFactory factory, HttpField vary, HttpChannel channel, HttpOutput.Interceptor next,boolean syncFlush)
+    {
+        this(factory,vary,channel.getHttpConfiguration().getOutputBufferSize(),channel,next,syncFlush);
+    }
+
+    public GzipHttpOutputInterceptor(GzipFactory factory, HttpField vary, int bufferSize, HttpChannel channel, HttpOutput.Interceptor next,boolean syncFlush)
+    {
+        _factory=factory;
+        _channel=channel;
+        _interceptor=next;
+        _vary=vary;
+        _bufferSize=bufferSize;
+        _syncFlush=syncFlush;
+    }
+
+    public HttpOutput.Interceptor getNextInterceptor()
+    {
+        return _interceptor;
+    }
+
+    @Override
+    public boolean isOptimizedForDirectBuffers()
+    {
+        return false; // No point as deflator is in user space.
+    }
+
+
+    @Override
+    public void write(ByteBuffer content, boolean complete, Callback callback)
+    {
+        switch (_state.get())
+        {
+            case MIGHT_COMPRESS:
+                commit(content,complete,callback);
+                break;
+
+            case NOT_COMPRESSING:
+                _interceptor.write(content, complete, callback);
+                return;
+
+            case COMMITTING:
+                callback.failed(new WritePendingException());
+                break;
+
+            case COMPRESSING:
+                gzip(content,complete,callback);
+                break;
+
+            default:
+                callback.failed(new IllegalStateException("state="+_state.get()));
+                break;
+        }
+    }
+
+    private void addTrailer()
+    {
+        int i=_buffer.limit();
+        _buffer.limit(i+8);
+
+        int v=(int)_crc.getValue();
+        _buffer.put(i++,(byte)(v & 0xFF));
+        _buffer.put(i++,(byte)((v>>>8) & 0xFF));
+        _buffer.put(i++,(byte)((v>>>16) & 0xFF));
+        _buffer.put(i++,(byte)((v>>>24) & 0xFF));
+
+        v=_deflater.getTotalIn();
+        _buffer.put(i++,(byte)(v & 0xFF));
+        _buffer.put(i++,(byte)((v>>>8) & 0xFF));
+        _buffer.put(i++,(byte)((v>>>16) & 0xFF));
+        _buffer.put(i++,(byte)((v>>>24) & 0xFF));
+    }
+
+
+    private void gzip(ByteBuffer content, boolean complete, final Callback callback)
+    {
+        if (content.hasRemaining() || complete)
+            new GzipBufferCB(content,complete,callback).iterate();
+        else
+            callback.succeeded();
+    }
+
+    protected void commit(ByteBuffer content, boolean complete, Callback callback)
+    {
+        // Are we excluding because of status?
+        Response response = _channel.getResponse();
+        int sc = response.getStatus();
+        if (sc>0 && (sc<200 || sc==204 || sc==205 || sc>=300))
+        {
+            LOG.debug("{} exclude by status {}",this,sc);
+            noCompression();
+
+            if (sc==304)
+            {
+                String request_etags = (String)_channel.getRequest().getAttribute("o.e.j.s.h.gzip.GzipHandler.etag");
+                String response_etag = response.getHttpFields().get(HttpHeader.ETAG);
+                if (request_etags!=null && response_etag!=null)
+                {
+                    String response_etag_gzip=etagGzip(response_etag);
+                    if (request_etags.contains(response_etag_gzip))
+                        response.getHttpFields().put(HttpHeader.ETAG,response_etag_gzip);
+                }
+            }
+            
+            _interceptor.write(content, complete, callback);
+            return;
+        }
+
+        // Are we excluding because of mime-type?
+        String ct = response.getContentType();
+        if (ct!=null)
+        {
+            ct=MimeTypes.getContentTypeWithoutCharset(ct);
+            if (!_factory.isMimeTypeGzipable(StringUtil.asciiToLowerCase(ct)))
+            {
+                LOG.debug("{} exclude by mimeType {}",this,ct);
+                noCompression();
+                _interceptor.write(content, complete, callback);
+                return;
+            }
+        }
+
+        // Has the Content-Encoding header already been set?
+        HttpFields fields = response.getHttpFields();
+        String ce=fields.get(HttpHeader.CONTENT_ENCODING);
+        if (ce != null)
+        {
+            LOG.debug("{} exclude by content-encoding {}",this,ce);
+            noCompression();
+            _interceptor.write(content, complete, callback);
+            return;
+        }
+
+        // Are we the thread that commits?
+        if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING))
+        {
+            // We are varying the response due to accept encoding header.
+            if (_vary != null)
+            {
+                if (fields.contains(HttpHeader.VARY))
+                    fields.addCSV(HttpHeader.VARY,_vary.getValues());
+                else
+                    fields.add(_vary);
+            }
+
+            long content_length = response.getContentLength();
+            if (content_length<0 && complete)
+                content_length=content.remaining();
+
+            _deflater = _factory.getDeflater(_channel.getRequest(),content_length);
+
+            if (_deflater==null)
+            {
+                LOG.debug("{} exclude no deflater",this);
+                _state.set(GZState.NOT_COMPRESSING);
+                _interceptor.write(content, complete, callback);
+                return;
+            }
+
+            fields.put(GzipHttpContent.CONTENT_ENCODING_GZIP);
+            _crc.reset();
+            _buffer=_channel.getByteBufferPool().acquire(_bufferSize,false);
+            BufferUtil.fill(_buffer,GZIP_HEADER,0,GZIP_HEADER.length);
+
+            // Adjust headers
+            response.setContentLength(-1);
+            String etag=fields.get(HttpHeader.ETAG);
+            if (etag!=null)
+                fields.put(HttpHeader.ETAG,etagGzip(etag));
+
+            LOG.debug("{} compressing {}",this,_deflater);
+            _state.set(GZState.COMPRESSING);
+
+            gzip(content,complete,callback);
+        }
+        else
+            callback.failed(new WritePendingException());
+    }
+
+    private String etagGzip(String etag)
+    {
+        int end = etag.length()-1;
+        return (etag.charAt(end)=='"')?etag.substring(0,end)+GzipHttpContent.ETAG_GZIP+'"':etag+GzipHttpContent.ETAG_GZIP;
+    }
+    
+    public void noCompression()
+    {
+        while (true)
+        {
+            switch (_state.get())
+            {
+                case NOT_COMPRESSING:
+                    return;
+
+                case MIGHT_COMPRESS:
+                    if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.NOT_COMPRESSING))
+                        return;
+                    break;
+
+                default:
+                    throw new IllegalStateException(_state.get().toString());
+            }
+        }
+    }
+
+    public void noCompressionIfPossible()
+    {
+        while (true)
+        {
+            switch (_state.get())
+            {
+                case COMPRESSING:
+                case NOT_COMPRESSING:
+                    return;
+
+                case MIGHT_COMPRESS:
+                    if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.NOT_COMPRESSING))
+                        return;
+                    break;
+
+                default:
+                    throw new IllegalStateException(_state.get().toString());
+            }
+        }
+    }
+
+    public boolean mightCompress()
+    {
+        return _state.get()==GZState.MIGHT_COMPRESS;
+    }
+
+    private class GzipBufferCB extends IteratingNestedCallback
+    {
+        private ByteBuffer _copy;
+        private final ByteBuffer _content;
+        private final boolean _last;
+        public GzipBufferCB(ByteBuffer content, boolean complete, Callback callback)
+        {
+            super(callback);
+            _content=content;
+            _last=complete;
+        }
+
+        @Override
+        protected Action process() throws Exception
+        {
+            if (_deflater==null)
+                return Action.SUCCEEDED;
+
+            if (_deflater.needsInput())
+            {
+                if (BufferUtil.isEmpty(_content))
+                {
+                    if (_deflater.finished())
+                    {
+                        _factory.recycle(_deflater);
+                        _deflater=null;
+                        _channel.getByteBufferPool().release(_buffer);
+                        _buffer=null;
+                        if (_copy!=null)
+                        {
+                            _channel.getByteBufferPool().release(_copy);
+                            _copy=null;
+                        }
+                        return Action.SUCCEEDED;
+                    }
+
+                    if (!_last)
+                    {
+                        return Action.SUCCEEDED;
+                    }
+
+                    _deflater.finish();
+                }
+                else if (_content.hasArray())
+                {
+                    byte[] array=_content.array();
+                    int off=_content.arrayOffset()+_content.position();
+                    int len=_content.remaining();
+                    BufferUtil.clear(_content);
+
+                    _crc.update(array,off,len);
+                    _deflater.setInput(array,off,len);
+                    if (_last)
+                        _deflater.finish();
+                }
+                else
+                {
+                    if (_copy==null)
+                        _copy=_channel.getByteBufferPool().acquire(_bufferSize,false);
+                    BufferUtil.clearToFill(_copy);
+                    int took=BufferUtil.put(_content,_copy);
+                    BufferUtil.flipToFlush(_copy,0);
+                    if (took==0)
+                        throw new IllegalStateException();
+
+                    byte[] array=_copy.array();
+                    int off=_copy.arrayOffset()+_copy.position();
+                    int len=_copy.remaining();
+
+                    _crc.update(array,off,len);
+                    _deflater.setInput(array,off,len);
+                    if (_last && BufferUtil.isEmpty(_content))
+                        _deflater.finish();
+                }
+            }
+
+            BufferUtil.compact(_buffer);
+            int off=_buffer.arrayOffset()+_buffer.limit();
+            int len=_buffer.capacity()-_buffer.limit() - (_last?8:0);
+            if (len>0)
+            {
+                int produced=_deflater.deflate(_buffer.array(),off,len,_syncFlush?Deflater.SYNC_FLUSH:Deflater.NO_FLUSH);
+                _buffer.limit(_buffer.limit()+produced);
+            }
+            boolean finished=_deflater.finished();
+
+            if (finished)
+                addTrailer();
+
+            _interceptor.write(_buffer,finished,this);
+            return Action.SCHEDULED;
+        }
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/package-info.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/package-info.java
new file mode 100644
index 0000000..69cf9f0
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/package-info.java
@@ -0,0 +1,23 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+/**
+ * Jetty GZIP Handler 
+ */
+package org.eclipse.jetty.server.handler.gzip;
+
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/jmx/AbstractConnectorMBean.java b/jetty-server/src/main/java/org/eclipse/jetty/server/jmx/AbstractConnectorMBean.java
index 83c1442..f37764d 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/jmx/AbstractConnectorMBean.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/jmx/AbstractConnectorMBean.java
@@ -20,6 +20,7 @@
 
 import org.eclipse.jetty.jmx.ObjectMBean;
 import org.eclipse.jetty.server.AbstractConnector;
+import org.eclipse.jetty.server.ConnectionFactory;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 
 @ManagedObject("MBean Wrapper for Connectors")
@@ -31,10 +32,23 @@
         super(managedObject);
         _connector=(AbstractConnector)managedObject;
     }
+    
     @Override
     public String getObjectContextBasis()
     {
-        return String.format("%s@%x",_connector.getDefaultProtocol(),_connector.hashCode());
+        StringBuilder buffer = new StringBuilder();
+        for (ConnectionFactory f:_connector.getConnectionFactories())
+        {
+            String protocol=f.getProtocol();
+            if (protocol!=null)
+            {
+                if (buffer.length()>0)
+                    buffer.append("|");
+                buffer.append(protocol);
+            }
+        }
+        
+        return String.format("%s@%x",buffer.toString(),_connector.hashCode());
     }
 
 
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java
index 0cbc42a..e9e4c81 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java
@@ -23,6 +23,7 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSessionActivationListener;
@@ -45,7 +46,7 @@
 public abstract class AbstractSession implements AbstractSessionManager.SessionIf
 {
     final static Logger LOG = SessionHandler.LOG;
-    public final static String SESSION_KNOWN_ONLY_TO_AUTHENTICATED="org.eclipse.jetty.security.sessionKnownOnlytoAuthenticated";
+    public final static String SESSION_CREATED_SECURE="org.eclipse.jetty.security.sessionCreatedSecure";
     private  String _clusterId; // ID without any node (ie "worker") id appended
     private  String _nodeId;    // ID of session with node(ie "worker") id appended
     private final AbstractSessionManager _manager;
@@ -76,7 +77,7 @@
         _requests=1;
         _maxIdleMs=_manager._dftMaxIdleSecs>0?_manager._dftMaxIdleSecs*1000L:-1;
         if (LOG.isDebugEnabled())
-            LOG.debug("new session & id "+_nodeId+" "+_clusterId);
+            LOG.debug("New session & id "+_nodeId+" "+_clusterId);
     }
 
     /* ------------------------------------------------------------- */
@@ -91,23 +92,24 @@
         _requests=1;
         _maxIdleMs=_manager._dftMaxIdleSecs>0?_manager._dftMaxIdleSecs*1000L:-1;
         if (LOG.isDebugEnabled())
-            LOG.debug("new session "+_nodeId+" "+_clusterId);
+            LOG.debug("Restored session "+_nodeId+" "+_clusterId);
     }
 
     /* ------------------------------------------------------------- */
     /**
      * asserts that the session is valid
+     * @throws IllegalStateException if the sesion is invalid
      */
     protected void checkValid() throws IllegalStateException
     {
         if (_invalid)
-            throw new IllegalStateException();
+            throw new IllegalStateException("id="+_clusterId+" created="+_created+" accessed="+_accessed+" lastaccessed="+_lastAccessed+" maxInactiveMs="+_maxIdleMs);
     }
     
     /* ------------------------------------------------------------- */
     /** Check to see if session has expired as at the time given.
-     * @param time
-     * @return
+     * @param time the time in milliseconds
+     * @return true if expired
      */
     protected boolean checkExpiry(long time)
     {
@@ -154,6 +156,12 @@
     {
         return _cookieSet;
     }
+    
+    /* ------------------------------------------------------------- */
+    public void setCookieSetTime(long time)
+    {
+        _cookieSet = time;
+    }
 
     /* ------------------------------------------------------------- */
     @Override
@@ -463,11 +471,12 @@
     
     /* ------------------------------------------------------------ */
     /**
-     * @param name
-     * @param value
+     * @param name the name of the attribute
+     * @param value the value of the attribute
+     * @return true if attribute changed
      * @deprecated use changeAttribute(String,Object) instead
-     * @return
      */
+    @Deprecated
     protected boolean updateAttribute (String name, Object value)
     {
         Object old=null;
@@ -497,9 +506,9 @@
      * in the session. The appropriate session attribute listeners are
      * also called.
      * 
-     * @param name
-     * @param value
-     * @return
+     * @param name the name of the attribute
+     * @param value the value of the attribute
+     * @return the old value for the attribute
      */
     protected Object changeAttribute (String name, Object value)
     {
@@ -548,6 +557,13 @@
     @Override
     public void setMaxInactiveInterval(int secs)
     {
+        if (LOG.isDebugEnabled())
+        {
+            if (secs <= 0)
+                LOG.debug("Session {} is now immortal (maxInactiveInterval={})", _clusterId, secs);
+            else 
+                LOG.debug("Session {} maxInactiveInterval={}", _clusterId, secs);
+        }
         _maxIdleMs=(long)secs*1000L;
     }
 
@@ -559,7 +575,11 @@
     }
 
     /* ------------------------------------------------------------- */
-    /** If value implements HttpSessionBindingListener, call valueBound() */
+    /** 
+     * Bind value if value implements {@link HttpSessionBindingListener} (calls {@link HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)}) 
+     * @param name the name with which the object is bound or unbound  
+     * @param value the bound value
+     */
     public void bindValue(java.lang.String name, Object value)
     {
         if (value!=null&&value instanceof HttpSessionBindingListener)
@@ -600,7 +620,11 @@
     }
 
     /* ------------------------------------------------------------- */
-    /** If value implements HttpSessionBindingListener, call valueUnbound() */
+    /**
+     * Unbind value if value implements {@link HttpSessionBindingListener} (calls {@link HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)}) 
+     * @param name the name with which the object is bound or unbound  
+     * @param value the bound value
+     */
     public void unbindValue(java.lang.String name, Object value)
     {
         if (value!=null&&value instanceof HttpSessionBindingListener)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java
index ebf8a6ce..24880ad 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java
@@ -67,13 +67,13 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * Set the workname. If set, the workername is dot appended to the session
+     * Set the workername. If set, the workername is dot appended to the session
      * ID and can be used to assist session affinity in a load balancer.
      * A worker name starting with $ is used as a request attribute name to
      * lookup the worker name that can be dynamically set by a request
-     * customiser.
+     * Customizer.
      *
-     * @param workerName
+     * @param workerName the name of the worker
      */
     public void setWorkerName(String workerName)
     {
@@ -244,8 +244,8 @@
 
     /** Get the session ID with any worker ID.
      *
-     * @param clusterId
-     * @param request
+     * @param clusterId the cluster id
+     * @param request the request
      * @return sessionId plus any worker ID.
      */
     @Override
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java
index a397079..85850c4 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java
@@ -53,15 +53,12 @@
 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.
+ * An Abstract implementation of SessionManager.
  * <p>
+ * 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 specialized version of the Session inner class that provides an attribute Map.
  */
 @SuppressWarnings("deprecation")
 @ManagedObject("Abstract Session Manager")
@@ -78,6 +75,14 @@
 
     /* ------------------------------------------------------------ */
     public final static int __distantFuture=60*60*24*7*52*20;
+    
+    
+    /**
+     * Web.xml session-timeout is set in minutes, but is stored as an int in seconds by HttpSession and
+     * the sessionmanager. Thus MAX_INT is the max number of seconds that can be set, and MAX_INT/60 is the
+     * max number of minutes that you can set.
+     */
+    public final static java.math.BigDecimal MAX_INACTIVE_MINUTES = new java.math.BigDecimal(Integer.MAX_VALUE/60);
 
     static final HttpSessionContext __nullSessionContext=new HttpSessionContext()
     {
@@ -405,6 +410,7 @@
     /**
      * 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.
+     * @param secureRequestOnly true to set Session Cookie Config as secure
      */
     public void setSecureRequestOnly(boolean secureRequestOnly)
     {
@@ -423,7 +429,7 @@
      * 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>
+     * <li>SessionCookieConfig.setSecure == false &amp;&amp; _secureRequestOnly==true &amp;&amp; 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,
@@ -431,14 +437,16 @@
      * 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."
-     *
+     * <p>
      * For case 2, you can use _secureRequestOnly to determine if you want the
-     * Servlet Spec 3.0  default behaviour when SessionCookieConfig.setSecure==false,
+     * Servlet Spec 3.0  default behavior when SessionCookieConfig.setSecure==false,
      * which is:
+     * <cite>
      * "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
+     * </cite>
+     * <p>
+     * The default for _secureRequestOnly is true, which gives the above behavior. If
      * you set it to false, then a session cookie is NEVER marked as secure, even if
      * the initiating request was secure.
      *
@@ -563,6 +571,8 @@
     {
         AbstractSession session=newSession(request);
         session.setMaxInactiveInterval(_dftMaxIdleSecs);
+        if (request.isSecure())
+            session.setAttribute(AbstractSession.SESSION_CREATED_SECURE, Boolean.TRUE);
         addSession(session,true);
         return session;
     }
@@ -613,13 +623,18 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @param seconds
-     */
     @Override
     public void setMaxInactiveInterval(int seconds)
     {
         _dftMaxIdleSecs=seconds;
+        if (__log.isDebugEnabled())
+        {
+            if (_dftMaxIdleSecs <= 0)
+                __log.debug("Sessions created by this manager are immortal (default maxInactiveInterval={})",_dftMaxIdleSecs);
+            else
+                __log.debug("SessionManager default maxInactiveInterval={}", _dftMaxIdleSecs);
+        }
+
     }
 
     /* ------------------------------------------------------------ */
@@ -670,6 +685,8 @@
     /**
      * Add the session Registers the session with this manager and registers the
      * session ID with the sessionIDManager;
+     * @param session the session
+     * @param created true if session was created
      */
     protected void addSession(AbstractSession session, boolean created)
     {
@@ -702,7 +719,7 @@
     /**
      * Prepare sessions for session manager shutdown
      * 
-     * @throws Exception
+     * @throws Exception if unable to shutdown sesssions
      */
     protected abstract void shutdownSessions() throws Exception;
 
@@ -710,7 +727,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Create a new session instance
-     * @param request
+     * @param request the request to build the session from
      * @return the new session
      */
     protected abstract AbstractSession newSession(HttpServletRequest request);
@@ -747,10 +764,12 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** Remove session from manager
+    /** 
+     * 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.
+     * @return if the session was removed 
      */
     public boolean removeSession(AbstractSession session, boolean invalidate)
     {
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java
index e1ecf94..90fd4a8 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java
@@ -32,6 +32,8 @@
 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.
@@ -62,6 +64,7 @@
 
     /* ------------------------------------------------------------ */
     /**
+     * @param id the id of the session
      * @return Collection of Sessions for the passed session ID
      */
     public Collection<HttpSession> getSession(String id)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
index 8539498..1449445 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
@@ -182,8 +182,13 @@
             _saveTask=null;
             if (_task!=null)
                 _task.cancel();
-
+            
             _task=null;
+            
+            //if we're managing our own timer, remove it
+            if (isManaged(_timer))
+               removeBean(_timer);
+
             _timer=null;
         }
        
@@ -192,7 +197,6 @@
         super.doStop();
 
         _sessions.clear();
-
     }
 
     /* ------------------------------------------------------------ */
@@ -599,16 +603,18 @@
                 if (isDeleteUnrestorableSessions() && file.exists() && file.getParentFile().equals(_storeDir) )
                 {
                     file.delete();
-                    LOG.warn("Deleting file for unrestorable session "+idInCuster, error);
+                    LOG.warn("Deleting file for unrestorable session {} {}",idInCuster,error);
+                    __log.debug(error);
                 }
                 else
                 {
-                    __log.warn("Problem restoring session "+idInCuster, error);
+                    __log.warn("Problem restoring session {} {}",idInCuster, error);
+                    __log.debug(error);
                 }
             }
-            else
+            else if (_savePeriodMs == 0)
             {
-                // delete successfully restored file
+                // delete successfully restored file if not saving periodically
                 file.delete();
             }
         }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java
index 599b738..7ff394e 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java
@@ -52,6 +52,12 @@
     private transient boolean _saveFailed = false;
     
     /**
+    * Last time session was saved to prevent periodic saves to sessions
+    * that have not changed
+    */
+    private transient long _lastSaved = 0;
+    
+    /**
      * True if an attempt has been made to de-idle a session and it failed. Once
      * true, the session will not be attempted to be de-idled again.
      */
@@ -145,7 +151,7 @@
     throws Exception
     {   
         File file = null;
-        if (!_saveFailed && _hashSessionManager._storeDir != null)
+        if (!_saveFailed && _hashSessionManager._storeDir != null && _lastSaved < getAccessed())
         {
             file = new File(_hashSessionManager._storeDir, super.getId());
             if (file.exists())
@@ -155,6 +161,7 @@
 
             try(FileOutputStream fos = new FileOutputStream(file,false))
             {
+                _lastSaved = System.currentTimeMillis();
                 save(fos);
             }
             catch (Exception e)
@@ -245,6 +252,7 @@
      *
      * The session is idled by persisting it, then clearing the session values attribute map and finally setting
      * it to an idled state.
+     * @throws Exception if unable to save session
      */
     public synchronized void idle()
     throws Exception
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
index d7fe947..1e6bf63 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
@@ -111,6 +111,7 @@
     {        
         protected DatabaseAdaptor _dbAdaptor;
         protected String _tableName = "JettySessions";
+        protected String _schemaName = null;
         protected String _rowIdColumn = "rowId";
         protected String _idColumn = "sessionId";
         protected String _contextPathColumn = "contextPath";
@@ -141,6 +142,15 @@
             checkNotNull(tableName);
             _tableName = tableName;
         }
+        public String getSchemaName()
+        {
+            return _schemaName;
+        }
+        public void setSchemaName(String schemaName)
+        {
+            checkNotNull(schemaName);
+            _schemaName = schemaName;
+        }
         public String getRowIdColumn()
         {       
             if ("rowId".equals(_rowIdColumn) && _dbAdaptor.isRowIdReserved())
@@ -275,7 +285,7 @@
             String blobType = _dbAdaptor.getBlobType();
             String longType = _dbAdaptor.getLongType();
             
-            return "create table "+_tableName+" ("+getRowIdColumn()+" varchar(120), "+_idColumn+" varchar(120), "+
+            return "create table "+getSchemaTableName()+" ("+getRowIdColumn()+" varchar(120), "+_idColumn+" varchar(120), "+
                     _contextPathColumn+" varchar(60), "+_virtualHostColumn+" varchar(60), "+_lastNodeColumn+" varchar(60), "+_accessTimeColumn+" "+longType+", "+
                     _lastAccessTimeColumn+" "+longType+", "+_createTimeColumn+" "+longType+", "+_cookieTimeColumn+" "+longType+", "+
                     _lastSavedTimeColumn+" "+longType+", "+_expiryTimeColumn+" "+longType+", "+_maxIntervalColumn+" "+longType+", "+
@@ -284,12 +294,12 @@
         
         public String getCreateIndexOverExpiryStatementAsString (String indexName)
         {
-            return "create index "+indexName+" on "+getTableName()+" ("+getExpiryTimeColumn()+")";
+            return "create index "+indexName+" on "+getSchemaTableName()+" ("+getExpiryTimeColumn()+")";
         }
         
         public String getCreateIndexOverSessionStatementAsString (String indexName)
         {
-            return "create index "+indexName+" on "+getTableName()+" ("+getIdColumn()+", "+getContextPathColumn()+")";
+            return "create index "+indexName+" on "+getSchemaTableName()+" ("+getIdColumn()+", "+getContextPathColumn()+")";
         }
         
         public String getAlterTableForMaxIntervalAsString ()
@@ -297,12 +307,17 @@
             if (_dbAdaptor == null)
                 throw new IllegalStateException ("No DBAdaptor");
             String longType = _dbAdaptor.getLongType();
-            String stem = "alter table "+getTableName()+" add "+getMaxIntervalColumn()+" "+longType;
+            String stem = "alter table "+getSchemaTableName()+" add "+getMaxIntervalColumn()+" "+longType;
             if (_dbAdaptor.getDBName().contains("oracle"))
                 return stem + " default "+ MAX_INTERVAL_NOT_SET + " not null";
             else
                 return stem +" not null default "+ MAX_INTERVAL_NOT_SET;
         }
+
+        private String getSchemaTableName()
+        {
+            return (getSchemaName()!=null?getSchemaName()+".":"")+getTableName();
+        }
         
         private void checkNotNull(String s)
         {
@@ -311,7 +326,7 @@
         }
         public String getInsertSessionStatementAsString()
         {
-           return "insert into "+getTableName()+
+           return "insert into "+getSchemaTableName()+
             " ("+getRowIdColumn()+", "+getIdColumn()+", "+getContextPathColumn()+", "+getVirtualHostColumn()+", "+getLastNodeColumn()+
             ", "+getAccessTimeColumn()+", "+getLastAccessTimeColumn()+", "+getCreateTimeColumn()+", "+getCookieTimeColumn()+
             ", "+getLastSavedTimeColumn()+", "+getExpiryTimeColumn()+", "+getMaxIntervalColumn()+", "+getMapColumn()+") "+
@@ -319,36 +334,36 @@
         }
         public String getDeleteSessionStatementAsString()
         {
-            return "delete from "+getTableName()+
+            return "delete from "+getSchemaTableName()+
             " where "+getRowIdColumn()+" = ?";
         }
         public String getUpdateSessionStatementAsString()
         {
-            return "update "+getTableName()+
+            return "update "+getSchemaTableName()+
                     " set "+getIdColumn()+" = ?, "+getLastNodeColumn()+" = ?, "+getAccessTimeColumn()+" = ?, "+
                     getLastAccessTimeColumn()+" = ?, "+getLastSavedTimeColumn()+" = ?, "+getExpiryTimeColumn()+" = ?, "+
                     getMaxIntervalColumn()+" = ?, "+getMapColumn()+" = ? where "+getRowIdColumn()+" = ?";
         }
         public String getUpdateSessionNodeStatementAsString()
         {
-            return "update "+getTableName()+
+            return "update "+getSchemaTableName()+
                     " set "+getLastNodeColumn()+" = ? where "+getRowIdColumn()+" = ?";
         }
         public String getUpdateSessionAccessTimeStatementAsString()
         {
-           return "update "+getTableName()+
+           return "update "+getSchemaTableName()+
             " set "+getLastNodeColumn()+" = ?, "+getAccessTimeColumn()+" = ?, "+getLastAccessTimeColumn()+" = ?, "+
                    getLastSavedTimeColumn()+" = ?, "+getExpiryTimeColumn()+" = ?, "+getMaxIntervalColumn()+" = ? where "+getRowIdColumn()+" = ?";
         }
         
         public String getBoundedExpiredSessionsStatementAsString()
         {
-            return "select * from "+getTableName()+" where "+getLastNodeColumn()+" = ? and "+getExpiryTimeColumn()+" >= ? and "+getExpiryTimeColumn()+" <= ?";
+            return "select * from "+getSchemaTableName()+" where "+getLastNodeColumn()+" = ? and "+getExpiryTimeColumn()+" >= ? and "+getExpiryTimeColumn()+" <= ?";
         }
         
         public String getSelectExpiredSessionsStatementAsString()
         {
-            return "select * from "+getTableName()+" where "+getExpiryTimeColumn()+" >0 and "+getExpiryTimeColumn()+" <= ?";
+            return "select * from "+getSchemaTableName()+" where "+getExpiryTimeColumn()+" >0 and "+getExpiryTimeColumn()+" <= ?";
         }
      
         public PreparedStatement getLoadStatement (Connection connection, String rowId, String contextPath, String virtualHosts)
@@ -362,7 +377,7 @@
             {
                 if (_dbAdaptor.isEmptyStringNull())
                 {
-                    PreparedStatement statement = connection.prepareStatement("select * from "+getTableName()+
+                    PreparedStatement statement = connection.prepareStatement("select * from "+getSchemaTableName()+
                                                                               " where "+getIdColumn()+" = ? and "+
                                                                               getContextPathColumn()+" is null and "+
                                                                               getVirtualHostColumn()+" = ?");
@@ -373,7 +388,7 @@
                 }
             }
 
-            PreparedStatement statement = connection.prepareStatement("select * from "+getTableName()+
+            PreparedStatement statement = connection.prepareStatement("select * from "+getSchemaTableName()+
                                                                       " where "+getIdColumn()+" = ? and "+getContextPathColumn()+
                                                                       " = ? and "+getVirtualHostColumn()+" = ?");
             statement.setString(1, rowId);
@@ -394,6 +409,7 @@
     {
         protected DatabaseAdaptor _dbAdaptor;
         protected String _tableName = "JettySessionIds";
+        protected String _schemaName = null;
         protected String _idColumn = "id";
 
         public void setDatabaseAdaptor(DatabaseAdaptor dbAdaptor)
@@ -422,24 +438,40 @@
             _tableName = tableName;
         }
 
+        public String getSchemaName()
+        {
+            return _schemaName;
+        }
+        
+        public void setSchemaName(String schemaName)
+        {
+            checkNotNull(schemaName);
+            _schemaName = schemaName;
+        }
+        
         public String getInsertStatementAsString ()
         {
-            return "insert into "+_tableName+" ("+_idColumn+")  values (?)";
+            return "insert into "+getSchemaTableName()+" ("+_idColumn+")  values (?)";
         }
 
         public String getDeleteStatementAsString ()
         {
-            return "delete from "+_tableName+" where "+_idColumn+" = ?";
+            return "delete from "+getSchemaTableName()+" where "+_idColumn+" = ?";
         }
 
         public String getSelectStatementAsString ()
         {
-            return  "select * from "+_tableName+" where "+_idColumn+" = ?";
+            return  "select * from "+getSchemaTableName()+" where "+_idColumn+" = ?";
         }
         
         public String getCreateStatementAsString ()
         {
-            return "create table "+_tableName+" ("+_idColumn+" varchar(120), primary key("+_idColumn+"))";
+            return "create table "+getSchemaTableName()+" ("+_idColumn+" varchar(120), primary key("+_idColumn+"))";
+        }
+        
+        private String getSchemaTableName()
+        {
+            return (getSchemaName()!=null?getSchemaName()+".":"")+getTableName();
         }
         
         private void checkNotNull(String s)
@@ -530,7 +562,7 @@
          * Convert a camel case identifier into either upper or lower
          * depending on the way the db stores identifiers.
          *
-         * @param identifier
+         * @param identifier the raw identifier
          * @return the converted identifier
          */
         public String convertIdentifier (String identifier)
@@ -629,8 +661,8 @@
     /**
      * Configure jdbc connection information via a jdbc Driver
      *
-     * @param driverClassName
-     * @param connectionUrl
+     * @param driverClassName the driver classname
+     * @param connectionUrl the driver connection url
      */
     public void setDriverInfo (String driverClassName, String connectionUrl)
     {
@@ -641,8 +673,8 @@
     /**
      * Configure jdbc connection information via a jdbc Driver
      *
-     * @param driverClass
-     * @param connectionUrl
+     * @param driverClass the driver class
+     * @param connectionUrl the driver connection url
      */
     public void setDriverInfo (Driver driverClass, String connectionUrl)
     {
@@ -682,9 +714,10 @@
     }
 
     /**
-     * @param name
+     * @param name the name of the blob
      * @deprecated see DbAdaptor.setBlobType
      */
+    @Deprecated
     public void setBlobType (String name)
     {
         _dbAdaptor.setBlobType(name);
@@ -704,27 +737,30 @@
     }
 
     /**
-     * @return
+     * @return the blob type
      * @deprecated see DbAdaptor.getBlobType
      */
+    @Deprecated
     public String getBlobType ()
     {
         return _dbAdaptor.getBlobType();
     }
 
     /**
-     * @return
+     * @return the long type
      * @deprecated see DbAdaptor.getLogType
      */
+    @Deprecated
     public String getLongType()
     {
         return _dbAdaptor.getLongType();
     }
 
     /**
-     * @param longType
+     * @param longType the long type
      * @deprecated see DbAdaptor.setLongType
      */
+    @Deprecated
     public void setLongType(String longType)
     {
        _dbAdaptor.setLongType(longType);
@@ -1026,7 +1062,7 @@
      * Get a connection from the driver or datasource.
      *
      * @return the connection for the datasource
-     * @throws SQLException
+     * @throws SQLException if unable to get the connection
      */
     protected Connection getConnection ()
     throws SQLException
@@ -1036,11 +1072,6 @@
         else
             return DriverManager.getConnection(_connectionUrl);
     }
-    
-
-
-
-
 
     /**
      * Set up the tables in the database
@@ -1075,7 +1106,8 @@
             
             //checking for table existence is case-sensitive, but table creation is not
             String tableName = _dbAdaptor.convertIdentifier(_sessionIdTableSchema.getTableName());
-            try (ResultSet result = metaData.getTables(null, null, tableName, null))
+            String schemaName = _sessionIdTableSchema.getSchemaName()!=null?_dbAdaptor.convertIdentifier(_sessionIdTableSchema.getSchemaName()):null;
+            try (ResultSet result = metaData.getTables(null, schemaName, tableName, null))
             {
                 if (!result.next())
                 {
@@ -1086,7 +1118,8 @@
             
             //make the session table if necessary
             tableName = _dbAdaptor.convertIdentifier(_sessionTableSchema.getTableName());
-            try (ResultSet result = metaData.getTables(null, null, tableName, null))
+            schemaName = _sessionTableSchema.getSchemaName()!=null?_dbAdaptor.convertIdentifier(_sessionTableSchema.getSchemaName()):null;
+            try (ResultSet result = metaData.getTables(null, schemaName, tableName, null))
             {
                 if (!result.next())
                 {
@@ -1100,8 +1133,7 @@
                     ResultSet colResult = null;
                     try
                     {
-                        colResult = metaData.getColumns(null, null,
-                                                        _dbAdaptor.convertIdentifier(_sessionTableSchema.getTableName()), 
+                        colResult = metaData.getColumns(null, schemaName, tableName, 
                                                         _dbAdaptor.convertIdentifier(_sessionTableSchema.getMaxIntervalColumn()));
                     }
                     catch (SQLException s)
@@ -1141,7 +1173,7 @@
 
             boolean index1Exists = false;
             boolean index2Exists = false;
-            try (ResultSet result = metaData.getIndexInfo(null, null, tableName, false, false))
+            try (ResultSet result = metaData.getIndexInfo(null, schemaName, tableName, false, true))
             {
                 while (result.next())
                 {
@@ -1251,6 +1283,8 @@
      */
     private void scavenge ()
     {
+        Set<String> candidateIds = getAllCandidateExpiredSessionIds();
+        
         Connection connection = null;
         try
         {
@@ -1284,7 +1318,7 @@
                         }
                     }
                 }
-                scavengeSessions(expiredSessionIds, false);
+                scavengeSessions(candidateIds, expiredSessionIds, false);
 
 
                 //Pass 2: find sessions that have expired a while ago for which this node was their last manager
@@ -1307,7 +1341,7 @@
                                 if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId+" last managed by "+getWorkerName());
                             }
                         }
-                        scavengeSessions(expiredSessionIds, false);
+                        scavengeSessions(candidateIds, expiredSessionIds, false);
                     }
 
 
@@ -1330,9 +1364,13 @@
                                 if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId);
                             }
                         }
-                        scavengeSessions(expiredSessionIds, true);
+                        scavengeSessions(candidateIds, expiredSessionIds, true);
                     }
                 }
+                
+                //Tell session managers to check remaining sessions in memory that may have expired 
+                //but are no longer in the database
+                scavengeSessions(candidateIds);
             }
         }
         catch (Exception e)
@@ -1364,24 +1402,20 @@
     /**
      * @param expiredSessionIds
      */
-    private void scavengeSessions (Set<String> expiredSessionIds, boolean forceDelete)
+    private void scavengeSessions (Set<String> candidateIds, Set<String> expiredSessionIds, boolean forceDelete)
     {       
         Set<String> remainingIds = new HashSet<String>(expiredSessionIds);
-        Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
-        for (int i=0; contexts!=null && i<contexts.length; i++)
+        Set<SessionManager> managers = getAllSessionManagers();
+        for (SessionManager m:managers)
         {
-            SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
-            if (sessionHandler != null)
+            Set<String> successfullyExpiredIds = ((JDBCSessionManager)m).expire(expiredSessionIds);
+            if (successfullyExpiredIds != null)
             {
-                SessionManager manager = sessionHandler.getSessionManager();
-                if (manager != null && manager instanceof JDBCSessionManager)
-                {
-                    Set<String> successfullyExpiredIds = ((JDBCSessionManager)manager).expire(expiredSessionIds);
-                    if (successfullyExpiredIds != null)
-                        remainingIds.removeAll(successfullyExpiredIds);
-                }
+                remainingIds.removeAll(successfullyExpiredIds);
+                candidateIds.removeAll(successfullyExpiredIds);
             }
         }
+    
 
         //Any remaining ids are of those sessions that no context removed
         if (!remainingIds.isEmpty() && forceDelete)
@@ -1403,6 +1437,63 @@
             }
         }
     }
+    
+    /**
+     * These are the session ids that the session managers thought had 
+     * expired, but were not expired in the database. This could be
+     * because the session is live on another node, or that the
+     * session no longer exists in the database because some other
+     * node removed it.
+     * @param candidateIds
+     */
+    private void scavengeSessions (Set<String> candidateIds)
+    {
+        if (candidateIds.isEmpty())
+            return;
+        
+        
+        Set<SessionManager> managers = getAllSessionManagers();
+        
+        for (SessionManager m:managers)
+        {
+            //tell the session managers to check the sessions that have expired in memory
+            //if they are no longer in the database, they should be removed
+            ((JDBCSessionManager)m).expireCandidates(candidateIds);
+        }
+    }
+    
+    private Set<String>  getAllCandidateExpiredSessionIds()
+    {
+        HashSet<String> candidateIds = new HashSet<>();
+        
+        Set<SessionManager> managers = getAllSessionManagers();
+        
+        for (SessionManager m:managers)
+        {
+            candidateIds.addAll(((JDBCSessionManager)m).getCandidateExpiredIds());
+        }
+        
+        return candidateIds;
+    }
+    
+    
+    private Set<SessionManager> getAllSessionManagers()
+    {
+        HashSet<SessionManager> managers = new HashSet<>();
+    
+        Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
+        for (int i=0; contexts!=null && i<contexts.length; i++)
+        {
+            SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+            if (sessionHandler != null)
+            {
+                SessionManager manager = sessionHandler.getSessionManager();
+                if (manager != null && manager instanceof JDBCSessionManager)
+                    managers.add(manager);
+            }
+        }
+        return managers;
+    }
 
 
    
@@ -1412,7 +1503,7 @@
     {
         if (expiredIds == null || expiredIds.isEmpty())
             return;
-
+        
         String[] ids = expiredIds.toArray(new String[expiredIds.size()]);
         try (Connection con = getConnection())
         {
@@ -1435,9 +1526,9 @@
                         end = ids.length;
 
                     //take them out of the sessionIds table
-                    statement.executeUpdate(fillInClause("delete from "+_sessionIdTableSchema.getTableName()+" where "+_sessionIdTableSchema.getIdColumn()+" in ", ids, start, end));
+                    statement.executeUpdate(fillInClause("delete from "+_sessionIdTableSchema.getSchemaTableName()+" where "+_sessionIdTableSchema.getIdColumn()+" in ", ids, start, end));
                     //take them out of the sessions table
-                    statement.executeUpdate(fillInClause("delete from "+_sessionTableSchema.getTableName()+" where "+_sessionTableSchema.getIdColumn()+" in ", ids, start, end));
+                    statement.executeUpdate(fillInClause("delete from "+_sessionTableSchema.getSchemaTableName()+" where "+_sessionTableSchema.getIdColumn()+" in ", ids, start, end));
                     block++;
                 }
             }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java
index 087b07e..ecc5ff9 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java
@@ -37,6 +37,10 @@
 import java.util.concurrent.atomic.AtomicReference;
 
 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.server.session.JDBCSessionIdManager.SessionTableSchema;
 import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
@@ -44,23 +48,24 @@
 import org.eclipse.jetty.util.log.Logger;
 
 /**
- * JDBCSessionManager
- *
+ * JDBCSessionManager.
+ * <p>
  * SessionManager that persists sessions to a database to enable clustering.
- *
+ * <p>
  * 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)
+ * <dl>
+ * <dt>rowId</dt><dd>(unique in cluster: webapp name/path + virtualhost + sessionId)</dd>
+ * <dt>contextPath</dt><dd>(of the context owning the session)</dd>
+ * <dt>sessionId</dt><dd>(unique in a context)</dd>
+ * <dt>lastNode</dt><dd>(name of node last handled session)</dd>
+ * <dt>accessTime</dt><dd>(time in milliseconds session was accessed)</dd>
+ * <dt>lastAccessTime</dt><dd>(previous time in milliseconds session was accessed)</dd>
+ * <dt>createTime</dt><dd>(time in milliseconds session created)</dd>
+ * <dt>cookieTime</dt><dd>(time in milliseconds session cookie created)</dd>
+ * <dt>lastSavedTime</dt><dd>(last time in milliseconds session access times were saved)</dd>
+ * <dt>expiryTime</dt><dd>(time in milliseconds that the session is due to expire)</dd>
+ * <dt>map</dt><dd>(attribute map)</dd>
+ * </dl>
  *
  * As an optimization, to prevent thrashing the database, we do not persist
  * the accessTime and lastAccessTime every time the session is accessed. Rather,
@@ -94,10 +99,7 @@
         protected boolean _dirty=false;
         
         
-        /**
-         * Time in msec since the epoch that a session cookie was set for this session
-         */
-        protected long _cookieSet;
+     
         
         
         /**
@@ -139,7 +141,7 @@
         /**
          * Session from a request.
          *
-         * @param request
+         * @param request the request
          */
         protected Session (HttpServletRequest request)
         {
@@ -154,10 +156,11 @@
         
         /**
          * Session restored from database
-         * @param sessionId
-         * @param rowId
-         * @param created
-         * @param accessed
+         * @param sessionId the session id
+         * @param rowId the row id
+         * @param created the created timestamp
+         * @param accessed the access timestamp
+         * @param maxInterval the max inactive interval (in seconds)
          */
         protected Session (String sessionId, String rowId, long created, long accessed, long maxInterval)
         {
@@ -219,16 +222,7 @@
             return _canonicalContext;
         }
         
-        public void setCookieSet (long ms)
-        {
-            _cookieSet = ms;
-        }
-
-        public synchronized long getCookieSet ()
-        {
-            return _cookieSet;
-        }
-
+       
         public synchronized void setLastNode (String node)
         {
             _lastNode=node;
@@ -257,11 +251,6 @@
                 _dirty=true;
         }
 
-        @Override
-        protected void cookieSet()
-        {
-            _cookieSet = getAccessed();
-        }
 
         /**
          * Entry to session.
@@ -396,7 +385,7 @@
         {
             return "Session rowId="+_rowId+",id="+getId()+",lastNode="+_lastNode+
                             ",created="+getCreationTime()+",accessed="+getAccessed()+
-                            ",lastAccessed="+getLastAccessedTime()+",cookieSet="+_cookieSet+
+                            ",lastAccessed="+getLastAccessedTime()+",cookieSet="+getCookieSetTime()+
                             ",maxInterval="+getMaxInactiveInterval()+",lastSaved="+_lastSaved+",expiry="+_expiryTime;
         }
     }
@@ -420,7 +409,7 @@
      * If any session attribute does change, then the attributes and
      * the accessed time are persisted.
      *
-     * @param sec
+     * @param sec the save interval in seconds
      */
     public void setSaveInterval (long sec)
     {
@@ -443,7 +432,7 @@
      * 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
+     * @param session the session to invalidate
      */
     public void cacheInvalidate (Session session)
     {
@@ -580,6 +569,11 @@
             }
             else
             {
+                if (memSession != null)
+                {
+                    //Session must have been removed from db by another node
+                    removeSession(memSession, true);
+                }
                 //No session in db with matching id and context path.
                 LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
             }
@@ -706,7 +700,7 @@
     /**
      * Invalidate a session.
      *
-     * @param idInCluster
+     * @param idInCluster the id in the cluster
      */
     protected void invalidateSession (String idInCluster)
     {
@@ -781,15 +775,6 @@
         return new Session(request);
     }
     
-    
-    /**
-     * @param sessionId
-     * @param rowId
-     * @param created
-     * @param accessed
-     * @param maxInterval
-     * @return
-     */
     protected AbstractSession newSession (String sessionId, String rowId, long created, long accessed, long maxInterval)
     {
         return new Session(sessionId, rowId, created, accessed, maxInterval);
@@ -823,7 +808,8 @@
      * Expire any Sessions we have in memory matching the list of
      * expired Session ids.
      *
-     * @param sessionIds
+     * @param sessionIds the session ids to expire
+     * @return the set of successfully expired ids
      */
     protected Set<String> expire (Set<String> sessionIds)
     {
@@ -885,12 +871,61 @@
         }
     }
     
-  
+    protected void expireCandidates (Set<String> candidateIds)
+    {
+        Iterator<String> itor = candidateIds.iterator();
+        long now = System.currentTimeMillis();
+        while (itor.hasNext())
+        {
+            String id = itor.next();
+
+            //check if expired in db
+            try
+            {
+                Session memSession = _sessions.get(id);
+                if (memSession == null)
+                {
+                    continue; //no longer in memory
+                }
+
+                Session s = loadSession(id,  canonicalize(_context.getContextPath()), getVirtualHost(_context));
+                if (s == null)
+                {
+                    //session no longer exists, can be safely expired
+                    memSession.timeout();
+                }
+            }
+            catch (Exception e)
+            {
+                LOG.warn("Error checking db for expiry for session {}", id);
+            }
+        }
+    }
+    
+    protected Set<String> getCandidateExpiredIds ()
+    {
+        HashSet<String> expiredIds = new HashSet<>();
+
+        Iterator<String> itor = _sessions.keySet().iterator();
+        while (itor.hasNext())
+        {
+            String id = itor.next();
+            //check to see if session should have expired
+            Session session = _sessions.get(id);
+            if (session._expiryTime > 0 &&  System.currentTimeMillis() > session._expiryTime)
+                expiredIds.add(id);           
+        }
+        return expiredIds;
+    }
+
+
     /**
      * Load a session from the database
-     * @param id
+     * @param id the id
+     * @param canonicalContextPath the canonical context path
+     * @param vhost the virtual host
      * @return the session data that was loaded
-     * @throws Exception
+     * @throws Exception if unable to load the session
      */
     protected Session loadSession (final String id, final String canonicalContextPath, final String vhost)
     throws Exception
@@ -921,7 +956,7 @@
                                                   result.getLong(_sessionTableSchema.getCreateTimeColumn()), 
                                                   result.getLong(_sessionTableSchema.getAccessTimeColumn()), 
                                                   maxInterval);
-                        session.setCookieSet(result.getLong(_sessionTableSchema.getCookieTimeColumn()));
+                        session.setCookieSetTime(result.getLong(_sessionTableSchema.getCookieTimeColumn()));
                         session.setLastAccessedTime(result.getLong(_sessionTableSchema.getLastAccessTimeColumn()));
                         session.setLastNode(result.getString(_sessionTableSchema.getLastNodeColumn()));
                         session.setLastSaved(result.getLong(_sessionTableSchema.getLastSavedTimeColumn()));
@@ -954,7 +989,7 @@
         if (_context==null)
             load.run();
         else
-            _context.getContextHandler().handle(load);
+            _context.getContextHandler().handle(null,load);
 
         if (_exception.get()!=null)
         {
@@ -970,8 +1005,8 @@
     /**
      * Insert a session into the database.
      *
-     * @param session
-     * @throws Exception
+     * @param session the session
+     * @throws Exception if unable to store the session
      */
     protected void storeSession (Session session)
     throws Exception
@@ -995,7 +1030,7 @@
             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(9, session.getCookieSetTime());//time cookie was set
             statement.setLong(10, now); //last saved time
             statement.setLong(11, session.getExpiryTime());
             statement.setLong(12, session.getMaxInactiveInterval());
@@ -1023,7 +1058,7 @@
      * Update data on an existing persisted session.
      *
      * @param data the session
-     * @throws Exception
+     * @throws Exception if unable to update the session
      */
     protected void updateSession (Session data)
     throws Exception
@@ -1066,7 +1101,7 @@
      * Update the node on which the session was last seen to be my node.
      *
      * @param data the session
-     * @throws Exception
+     * @throws Exception if unable to update the session node
      */
     protected void updateSessionNode (Session data)
     throws Exception
@@ -1120,8 +1155,8 @@
      * Delete a session from the database. Should only be called
      * when the session has been invalidated.
      *
-     * @param data
-     * @throws Exception
+     * @param data the session data
+     * @throws Exception if unable to delete the session
      */
     protected void deleteSession (Session data)
     throws Exception
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
index 969cd22..f8dd24b 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
@@ -158,7 +158,7 @@
                 session = baseRequest.getSession(false);
                 if (session != null)
                 {
-                    if (session != old_session)
+                    if ((session != old_session) && (request.getDispatcherType() == DispatcherType.ASYNC || request.getDispatcherType() == DispatcherType.REQUEST))
                     {
                         access = session;
                         HttpCookie cookie = _sessionManager.access(session,request.isSecure());
@@ -192,11 +192,14 @@
         }
         finally
         {
+            //if we accessed an existing session entering this context, then complete it
             if (access != null)
                 _sessionManager.complete(access);
 
+            
+            //if there is a session that was created during handling this context, then complete it
             HttpSession session = baseRequest.getSession(false);
-            if (session != null && old_session == null && session != access)
+            if ((session != null && old_session == null && session != access) && (request.getDispatcherType() == DispatcherType.ASYNC || request.getDispatcherType() == DispatcherType.REQUEST))
                 _sessionManager.complete(session);
 
             if (old_session_manager != null && old_session_manager != _sessionManager)
@@ -228,8 +231,8 @@
     /**
      * Look for a requested session ID in cookies and URI parameters
      *
-     * @param baseRequest
-     * @param request
+     * @param baseRequest the request to check
+     * @param request the request to check
      */
     protected void checkRequestedSessionId(Request baseRequest, HttpServletRequest request)
     {
@@ -321,9 +324,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @param listener
-     */
     public void addEventListener(EventListener listener)
     {
         if (_sessionManager != null)
@@ -331,9 +331,6 @@
     }
     
     /* ------------------------------------------------------------ */
-    /**
-     * @param listener
-     */
     public void removeEventListener(EventListener listener)
     {
         if (_sessionManager != null)
diff --git a/jetty-server/src/test/config/etc/keystore b/jetty-server/src/test/config/etc/keystore
new file mode 100644
index 0000000..d6592f9
--- /dev/null
+++ b/jetty-server/src/test/config/etc/keystore
Binary files differ
diff --git a/jetty-server/src/main/config/etc/keystore.pkf b/jetty-server/src/test/config/etc/keystore.pkf
similarity index 100%
rename from jetty-server/src/main/config/etc/keystore.pkf
rename to jetty-server/src/test/config/etc/keystore.pkf
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java
index 78f3896..1599c36 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java
@@ -21,34 +21,40 @@
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertThat;
 
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.net.Socket;
 import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.io.ArrayByteBufferPool;
 import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpParser;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 
 public abstract class AbstractHttpTest
 {
+    private final static Set<String> __noBodyCodes = new HashSet<>(Arrays.asList(new String[]{"100","101","102","204","304"}));
+    
+    @Rule
+    public TestTracker tracker = new TestTracker();
+
     protected static Server server;
     protected static ServerConnector connector;
     protected String httpVersion;
-    protected SimpleHttpParser httpParser;
+    private StacklessLogging stacklessChannelLogging;
+
 
     public AbstractHttpTest(String httpVersion)
     {
@@ -63,46 +69,53 @@
         connector.setIdleTimeout(10000);
         
         server.addConnector(connector);
-        httpParser = new SimpleHttpParser();
-        ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(true);
+        stacklessChannelLogging =new StacklessLogging(HttpChannel.class);
     }
 
     @After
     public void tearDown() throws Exception
     {
         server.stop();
-        ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false);
+        stacklessChannelLogging.close();
     }
 
-    protected SimpleHttpResponse executeRequest() throws URISyntaxException, IOException
+    protected HttpTester.Response executeRequest() throws URISyntaxException, IOException
     {
-        Socket socket = new Socket("localhost", connector.getLocalPort());
-        socket.setSoTimeout((int)connector.getIdleTimeout());
-        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
-        PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
-        String request = "GET / " + httpVersion + "\r\n";
-
-        writer.write(request);
-        writer.write("Host: localhost");
-        writer.println("\r\n");
-        writer.flush();
-
-        SimpleHttpResponse response = httpParser.readResponse(reader);
-        if ("HTTP/1.1".equals(httpVersion) && response.getHeaders().get("content-length") == null && response
-                .getHeaders().get("transfer-encoding") == null)
-            assertThat("If HTTP/1.1 response doesn't contain transfer-encoding or content-length headers, " +
-                    "it should contain connection:close", response.getHeaders().get("connection"), is("close"));
-        return response;
+        try(Socket socket = new Socket("localhost", connector.getLocalPort()))
+        {
+            socket.setSoTimeout((int)connector.getIdleTimeout());
+            
+            try(PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())))
+            {
+                writer.write("GET / " + httpVersion + "\r\n");
+                writer.write("Host: localhost\r\n");
+                writer.write("\r\n");
+                writer.flush();
+    
+                HttpTester.Response response = new HttpTester.Response();
+                HttpTester.Input input = HttpTester.from(socket.getInputStream());
+                HttpTester.parseResponse(input, response);
+                
+                if ("HTTP/1.1".equals(httpVersion)
+                        && response.isComplete()
+                        && response.get("content-length") == null
+                        && response.get("transfer-encoding") == null
+                        && !__noBodyCodes.contains(response.getStatus()))
+                    assertThat("If HTTP/1.1 response doesn't contain transfer-encoding or content-length headers, " +
+                            "it should contain connection:close", response.get("connection"), is("close"));
+                return response;
+            }
+        }
     }
 
-    protected void assertResponseBody(SimpleHttpResponse response, String expectedResponseBody)
+    protected void assertResponseBody(HttpTester.Response response, String expectedResponseBody)
     {
-        assertThat("response body is" + expectedResponseBody, response.getBody(), is(expectedResponseBody));
+        assertThat("response body is" + expectedResponseBody, response.getContent(), is(expectedResponseBody));
     }
 
-    protected void assertHeader(SimpleHttpResponse response, String headerName, String expectedValue)
+    protected void assertHeader(HttpTester.Response response, String headerName, String expectedValue)
     {
-        assertThat(headerName + "=" + expectedValue, response.getHeaders().get(headerName), is(expectedValue));
+        assertThat(headerName + "=" + expectedValue, response.get(headerName), is(expectedValue));
     }
 
     protected static class TestCommitException extends IllegalStateException
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java
index b885a42..fb8a502 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java
@@ -18,11 +18,6 @@
 
 package org.eclipse.jetty.server;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
@@ -47,6 +42,11 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
 public class AsyncRequestReadTest
 {
     private static Server server;
@@ -75,11 +75,11 @@
     {
         server.setHandler(new AsyncStreamHandler());
         server.start();
-        
+
         try (final Socket socket =  new Socket("localhost",connector.getLocalPort()))
         {
             socket.setSoTimeout(1000);
-            
+
             byte[] content = new byte[32*4096];
             Arrays.fill(content, (byte)120);
 
@@ -93,8 +93,8 @@
             byte[] h=header.getBytes(StandardCharsets.ISO_8859_1);
             out.write(h);
             out.write(content);
-            
-            
+
+
             header=
                 "POST / HTTP/1.1\r\n"+
                     "Host: localhost\r\n"+
@@ -123,7 +123,7 @@
     {
         server.setHandler(new AsyncStreamHandler());
         server.start();
-        
+
         asyncReadTest(64,4,4,20);
         asyncReadTest(256,16,16,50);
         asyncReadTest(256,1,128,10);
@@ -184,7 +184,7 @@
 
             final AsyncContext async = request.startAsync();
             // System.err.println("handle "+request.getContentLength());
-            
+
             new Thread()
             {
                 @Override
@@ -194,7 +194,7 @@
                     try(InputStream in = request.getInputStream();)
                     {
                         // System.err.println("reading...");
-                        
+
                         byte[] b = new byte[4*4096];
                         int read;
                         while((read =in.read(b))>=0)
@@ -216,18 +216,18 @@
             }.start();
         }
     }
-    
+
 
     @Test
     public void testPartialRead() throws Exception
     {
         server.setHandler(new PartialReaderHandler());
         server.start();
-        
+
         try (final Socket socket =  new Socket("localhost",connector.getLocalPort()))
         {
-            socket.setSoTimeout(1000);
-            
+            socket.setSoTimeout(10000);
+
             byte[] content = new byte[32*4096];
             Arrays.fill(content, (byte)88);
 
@@ -241,7 +241,7 @@
             byte[] h=header.getBytes(StandardCharsets.ISO_8859_1);
             out.write(h);
             out.write(content);
-            
+
             header= "POST /?read=10 HTTP/1.1\r\n"+
                     "Host: localhost\r\n"+
                     "Content-Length: "+content.length+"\r\n"+
@@ -252,7 +252,7 @@
             out.write(h);
             out.write(content);
             out.flush();
-            
+
             BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             assertThat(in.readLine(),containsString("HTTP/1.1 200 OK"));
             assertThat(in.readLine(),containsString("Content-Length:"));
@@ -273,11 +273,11 @@
     {
         server.setHandler(new PartialReaderHandler());
         server.start();
-        
+
         try (final Socket socket =  new Socket("localhost",connector.getLocalPort()))
         {
             socket.setSoTimeout(10000);
-            
+
             byte[] content = new byte[32*4096];
             Arrays.fill(content, (byte)88);
 
@@ -297,6 +297,7 @@
             BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             assertThat(in.readLine(),containsString("HTTP/1.1 200 OK"));
             assertThat(in.readLine(),containsString("Content-Length:"));
+            assertThat(in.readLine(),containsString("Connection: close"));
             assertThat(in.readLine(),containsString("Server:"));
             in.readLine();
             assertThat(in.readLine(),containsString("XXXXXXX"));
@@ -308,11 +309,11 @@
     {
         server.setHandler(new PartialReaderHandler());
         server.start();
-        
+
         try (final Socket socket =  new Socket("localhost",connector.getLocalPort()))
         {
             socket.setSoTimeout(1000);
-            
+
             byte[] content = new byte[32*4096];
             Arrays.fill(content, (byte)88);
 
@@ -334,7 +335,7 @@
             assertThat(in.readLine(),containsString("Server:"));
             in.readLine();
             assertThat(in.readLine(),containsString("XXXXXXX"));
-            
+
             socket.close();
         }
     }
@@ -346,7 +347,7 @@
         {
             httpResponse.setStatus(200);
             request.setHandled(true);
-                        
+
             BufferedReader in = request.getReader();
             PrintWriter out =httpResponse.getWriter();
             int read=Integer.valueOf(request.getParameter("read"));
@@ -358,7 +359,7 @@
                     break;
                 out.write(c);
             }
-            out.println();
+            out.write('\n');
         }
     }
 }
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java
index 0613e9e..bc424a7 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java
@@ -202,7 +202,7 @@
             long resume_after=-1;
             long complete_after=-1;
 
-            final String uri=baseRequest.getUri().toString();
+            final String uri=baseRequest.getHttpURI().toString();
 
             if (request.getParameter("read")!=null)
                 read_before=Integer.parseInt(request.getParameter("read"));
@@ -255,7 +255,7 @@
                                     Request br=(Request)asyncContext.getRequest();
                                     System.err.println("\n"+e.toString());
                                     System.err.println(baseRequest+"=="+br);
-                                    System.err.println(uri+"=="+br.getUri());
+                                    System.err.println(uri+"=="+br.getHttpURI());
                                     System.err.println(asyncContext+"=="+br.getHttpChannelState());
 
                                     LOG.warn(e);
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ClassLoaderDumptTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ClassLoaderDumptTest.java
new file mode 100644
index 0000000..5e18ec1
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ClassLoaderDumptTest.java
@@ -0,0 +1,200 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import org.eclipse.jetty.util.component.Dumpable;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ClassLoaderDumptTest
+{
+    @Test
+    public void testSimple() throws Exception
+    {
+        Server server = new Server();
+        ClassLoader loader = new ClassLoader()
+        {
+            public String toString()
+            {
+                return "SimpleLoader";
+            }
+        };
+
+        server.addBean(new ClassLoaderDump(loader));
+
+        StringBuilder out = new StringBuilder();
+        server.dump(out);
+        String dump = out.toString();
+        assertThat(dump,containsString("+- SimpleLoader"));
+        assertThat(dump,containsString("+> "+Server.class.getClassLoader()));
+    }
+    
+    @Test
+    public void testParent() throws Exception
+    {
+        Server server = new Server();
+        ClassLoader loader = new ClassLoader(Server.class.getClassLoader())
+        {
+            public String toString()
+            {
+                return "ParentedLoader";
+            }
+        };
+
+        server.addBean(new ClassLoaderDump(loader));
+
+        StringBuilder out = new StringBuilder();
+        server.dump(out);
+        String dump = out.toString();
+        assertThat(dump,containsString("+- ParentedLoader"));
+        assertThat(dump,containsString("|   +- "+Server.class.getClassLoader()));
+        assertThat(dump,containsString("+> "+Server.class.getClassLoader()));
+    }
+    
+    @Test
+    public void testNested() throws Exception
+    {
+        Server server = new Server();
+        ClassLoader middleLoader = new ClassLoader(Server.class.getClassLoader())
+        {
+            public String toString()
+            {
+                return "MiddleLoader";
+            }
+        };
+        ClassLoader loader = new ClassLoader(middleLoader)
+        {
+            public String toString()
+            {
+                return "TopLoader";
+            }
+        };
+
+        server.addBean(new ClassLoaderDump(loader));
+
+        StringBuilder out = new StringBuilder();
+        server.dump(out);
+        String dump = out.toString();
+        assertThat(dump,containsString("+- TopLoader"));
+        assertThat(dump,containsString("|   +- MiddleLoader"));
+        assertThat(dump,containsString("|       +- "+Server.class.getClassLoader()));
+        assertThat(dump,containsString("+> "+Server.class.getClassLoader()));
+    }
+    
+    @Test
+    public void testDumpable() throws Exception
+    {
+        Server server = new Server();
+        ClassLoader middleLoader = new DumpableClassLoader(Server.class.getClassLoader());
+        ClassLoader loader = new ClassLoader(middleLoader)
+        {
+            public String toString()
+            {
+                return "TopLoader";
+            }
+        };
+
+        server.addBean(new ClassLoaderDump(loader));
+
+        StringBuilder out = new StringBuilder();
+        server.dump(out);
+        String dump = out.toString();
+        assertThat(dump,containsString("+- TopLoader"));
+        assertThat(dump,containsString("|   +- DumpableClassLoader"));
+        assertThat(dump,not(containsString("|       +- "+Server.class.getClassLoader())));
+        assertThat(dump,containsString("+> "+Server.class.getClassLoader()));
+    }
+    
+    public static class DumpableClassLoader extends ClassLoader implements Dumpable
+    {
+        public DumpableClassLoader(ClassLoader parent)
+        {
+            super(parent);
+        }
+
+        @Override
+        public String dump()
+        {
+            return "DumpableClassLoader";
+        }
+
+        @Override
+        public void dump(Appendable out, String indent) throws IOException
+        {
+            out.append(dump()).append('\n');
+        }
+
+        public String toString()
+        {
+            return "DumpableClassLoader";
+        }
+    }
+    
+
+    
+    @Test
+    public void testUrlClassLoaders() throws Exception
+    {
+        Server server = new Server();
+        ClassLoader middleLoader = new URLClassLoader(new URL[] 
+            {new URL("file:/one"),new URL("file:/two"),new URL("file:/three"),},
+            Server.class.getClassLoader())
+        {
+            public String toString()
+            {
+                return "MiddleLoader";
+            }
+        };
+        ClassLoader loader = new URLClassLoader(new URL[] 
+            {new URL("file:/ONE"),new URL("file:/TWO"),new URL("file:/THREE"),},
+            middleLoader)
+        {
+            public String toString()
+            {
+                return "TopLoader";
+            }
+        };
+
+        server.addBean(new ClassLoaderDump(loader));
+
+        StringBuilder out = new StringBuilder();
+        server.dump(out);
+        String dump = out.toString();
+        System.err.println(dump);
+        assertThat(dump,containsString("+- TopLoader"));
+        assertThat(dump,containsString("|   +- file:/ONE"));
+        assertThat(dump,containsString("|   +- file:/TWO"));
+        assertThat(dump,containsString("|   +- file:/THREE"));
+        assertThat(dump,containsString("|   +- MiddleLoader"));
+        assertThat(dump,containsString("|       +- file:/one"));
+        assertThat(dump,containsString("|       +- file:/two"));
+        assertThat(dump,containsString("|       +- file:/three"));
+        assertThat(dump,containsString("|       +- "+Server.class.getClassLoader()));
+        assertThat(dump,containsString("+> "+Server.class.getClassLoader()));
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectionOpenCloseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectionOpenCloseTest.java
index 0db2c1a..a1e5a86 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectionOpenCloseTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectionOpenCloseTest.java
@@ -18,10 +18,12 @@
 
 package org.eclipse.jetty.server;
 
-import java.io.BufferedReader;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStreamReader;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
@@ -33,12 +35,12 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.toolchain.test.annotation.Slow;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -136,30 +138,32 @@
             }
         });
 
-        Socket socket = new Socket("localhost", connector.getLocalPort());
-        socket.setSoTimeout((int)connector.getIdleTimeout());
-        OutputStream output = socket.getOutputStream();
-        output.write((
-                "GET / HTTP/1.1\r\n" +
-                "Host: localhost:" + connector.getLocalPort() + "\r\n" +
-                "Connection: close\r\n" +
-                "\r\n").getBytes(StandardCharsets.UTF_8));
-        output.flush();
-
-        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
-        SimpleHttpResponse response = httpParser.readResponse(reader);
-        Assert.assertEquals("200", response.getCode());
-
-        Assert.assertEquals(-1, reader.read());
-        socket.close();
-
-        Assert.assertTrue(openLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
-
-        // Wait some time to see if the callbacks are called too many times
-        TimeUnit.SECONDS.sleep(1);
-
-        Assert.assertEquals(2, callbacks.get());
+        try( Socket socket = new Socket("localhost", connector.getLocalPort()) )
+        {
+            socket.setSoTimeout((int) connector.getIdleTimeout());
+            OutputStream output = socket.getOutputStream();
+            output.write((
+                    "GET / HTTP/1.1\r\n" +
+                            "Host: localhost:" + connector.getLocalPort() + "\r\n" +
+                            "Connection: close\r\n" +
+                            "\r\n").getBytes(StandardCharsets.UTF_8));
+            output.flush();
+            
+            InputStream inputStream = socket.getInputStream();
+            HttpTester.Response response = HttpTester.parseResponse(inputStream);
+            assertThat("Status Code", response.getStatus(), is(200));
+    
+            Assert.assertEquals(-1, inputStream.read());
+            socket.close();
+    
+            Assert.assertTrue(openLatch.await(5, TimeUnit.SECONDS));
+            Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
+    
+            // Wait some time to see if the callbacks are called too many times
+            TimeUnit.SECONDS.sleep(1);
+    
+            Assert.assertEquals(2, callbacks.get());
+        }
     }
 
     @Slow
@@ -188,8 +192,8 @@
         server.start();
 
         final AtomicInteger callbacks = new AtomicInteger();
-        final CountDownLatch openLatch = new CountDownLatch(1);
-        final CountDownLatch closeLatch = new CountDownLatch(1);
+        final CountDownLatch openLatch = new CountDownLatch(2);
+        final CountDownLatch closeLatch = new CountDownLatch(2);
         connector.addBean(new Connection.Listener.Adapter()
         {
             @Override
@@ -217,11 +221,11 @@
                 "\r\n").getBytes(StandardCharsets.UTF_8));
         output.flush();
 
-        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
-        SimpleHttpResponse response = httpParser.readResponse(reader);
-        Assert.assertEquals("200", response.getCode());
+        InputStream inputStream = socket.getInputStream();
+        HttpTester.Response response = HttpTester.parseResponse(inputStream);
+        Assert.assertEquals(200, response.getStatus());
 
-        Assert.assertEquals(-1, reader.read());
+        Assert.assertEquals(-1, inputStream.read());
         socket.close();
 
         Assert.assertTrue(openLatch.await(5, TimeUnit.SECONDS));
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorCloseTestBase.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorCloseTestBase.java
index c906994..5248347 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorCloseTestBase.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorCloseTestBase.java
@@ -30,11 +30,14 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.toolchain.test.AdvancedRunner;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * HttpServer Tester.
  */
+@RunWith(AdvancedRunner.class)
 public abstract class ConnectorCloseTestBase extends HttpServerTestFixture
 {
     private static String __content =
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorStatisticsTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorStatisticsTest.java
deleted file mode 100644
index 1507c3f..0000000
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorStatisticsTest.java
+++ /dev/null
@@ -1,266 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.server;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.net.Socket;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.CyclicBarrier;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.server.handler.HandlerWrapper;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Ignore;
-import org.junit.Test;
-
-@Ignore("Ignored while refactoring the connection events and statistics")
-public class ConnectorStatisticsTest
-{
-    private static final Logger LOG = Log.getLogger(ConnectorStatisticsTest.class);
-
-    private static Server _server;
-    private static ConnectorStatistics _statistics;
-    private static AbstractNetworkConnector _connector;
-    private static CyclicBarrier _connect;
-    private static CountDownLatch _closed;
-
-    private Socket[] _socket;
-    private PrintWriter[] _out;
-    private BufferedReader[] _in;
-
-    @BeforeClass
-    public static void initClass() throws Exception
-    {
-        _connect = new CyclicBarrier(2);
-
-        _server = new Server();
-        _connector = new ServerConnector(_server);
-        _statistics = new ConnectorStatistics();
-        _connector.addBean(_statistics);
-        _server.addConnector(_connector);
-
-        HandlerWrapper wrapper = new HandlerWrapper()
-        {
-            @Override
-            public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
-            {
-                try
-                {
-                    _connect.await();
-                }
-                catch (Exception ex)
-                {
-                    LOG.debug(ex);
-                }
-                finally
-                {
-                    super.handle(path, request, httpRequest, httpResponse);
-                }
-            }
-        };
-        _server.setHandler(wrapper);
-
-        Handler handler = new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-                throws IOException, ServletException
-            {
-                try{Thread.sleep(1);} catch(Exception e){}
-                baseRequest.setHandled(true);
-                PrintWriter out = response.getWriter();
-                out.write("Server response\n");
-                out.close();
-
-                response.setStatus(HttpServletResponse.SC_OK);
-            }
-        };
-        wrapper.setHandler(handler);
-
-        _server.start();
-    }
-
-    @AfterClass
-    public static void destroy() throws Exception
-    {
-        _server.stop();
-        _server.join();
-    }
-
-    @Before
-    public void init() throws Exception
-    {
-        _statistics.reset();
-    }
-
-    @After
-    public void tini() throws Exception
-    {
-    }
-
-    @Test
-    public void testSingleRequest() throws Exception
-    {
-        doInit(1);
-
-        sendRequest(1, 1);
-
-        doClose(1);
-
-        assertEquals(1, _statistics.getConnections());
-        assertEquals(0, _statistics.getConnectionsOpen());
-        assertEquals(1, _statistics.getConnectionsOpenMax());
-        assertTrue(_statistics.getConnectionsOpen() <= _statistics.getConnectionsOpenMax());
-
-        assertTrue(_statistics.getConnectionDurationMean() > 0);
-        assertTrue(_statistics.getConnectionDurationMax() > 0);
-        assertTrue(_statistics.getConnectionDurationMean() <= _statistics.getConnectionDurationMax());
-
-        assertEquals(1, _statistics.getMessagesIn());
-        assertEquals(1.0, _statistics.getMessagesInPerConnectionMean(), 0.01);
-        assertEquals(1, _statistics.getMessagesInPerConnectionMax());
-        assertTrue(_statistics.getMessagesInPerConnectionMean() <= _statistics.getMessagesInPerConnectionMax());
-    }
-
-    @Test
-    public void testMultipleRequests() throws Exception
-    {
-        doInit(1);
-
-        sendRequest(1, 1);
-
-        sendRequest(1, 1);
-
-        doClose(1);
-
-        assertEquals(1, _statistics.getConnections());
-        assertEquals(0, _statistics.getConnectionsOpen());
-        assertEquals(1, _statistics.getConnectionsOpenMax());
-        assertTrue(_statistics.getConnectionsOpen() <= _statistics.getConnectionsOpenMax());
-
-        assertTrue(_statistics.getConnectionDurationMean() > 0);
-        assertTrue(_statistics.getConnectionDurationMax() > 0);
-        assertTrue(_statistics.getConnectionDurationMean() <= _statistics.getConnectionDurationMax());
-
-        assertEquals(2, _statistics.getMessagesIn());
-        assertEquals(2.0, _statistics.getMessagesInPerConnectionMean(), 0.01);
-        assertEquals(2, _statistics.getMessagesInPerConnectionMax());
-        assertTrue(_statistics.getMessagesInPerConnectionMean() <= _statistics.getMessagesInPerConnectionMax());
-    }
-
-    @Test
-    public void testMultipleConnections() throws Exception
-    {
-        doInit(3);
-
-        sendRequest(1, 1); // request 1 connection 1
-
-        sendRequest(2, 2); // request 1 connection 2
-
-        sendRequest(3, 3); // request 1 connection 3
-
-        sendRequest(2, 3); // request 2 connection 2
-
-        sendRequest(3, 3); // request 2 connection 3
-
-        sendRequest(3, 3); // request 3 connection 3
-
-        doClose(3);
-
-        assertEquals(3, _statistics.getConnections());
-        assertEquals(0, _statistics.getConnectionsOpen());
-        assertEquals(3, _statistics.getConnectionsOpenMax());
-        assertTrue(_statistics.getConnectionsOpen() <= _statistics.getConnectionsOpenMax());
-
-        assertTrue(_statistics.getConnectionDurationMean() > 0);
-        assertTrue(_statistics.getConnectionDurationMax() > 0);
-        assertTrue(_statistics.getConnectionDurationMean() <= _statistics.getConnectionDurationMax());
-
-        assertEquals(6, _statistics.getMessagesIn());
-        assertEquals(2.0, _statistics.getMessagesInPerConnectionMean(), 0.01);
-        assertEquals(3, _statistics.getMessagesInPerConnectionMax());
-        assertTrue(_statistics.getMessagesInPerConnectionMean() <= _statistics.getMessagesInPerConnectionMax());
-    }
-
-    protected void doInit(int count)
-    {
-        _socket = new Socket[count];
-        _out = new PrintWriter[count];
-        _in = new BufferedReader[count];
-
-        _closed = new CountDownLatch(count);
-    }
-
-    private void doClose(int count) throws Exception
-    {
-        for (int idx=0; idx < count; idx++)
-        {
-            if (_socket[idx] != null)
-            {
-                _socket[idx].close();
-            }
-        }
-
-        _closed.await();
-    }
-
-    private void sendRequest(int id, int count) throws Exception
-    {
-        int idx = id - 1;
-
-        if (idx < 0)
-            throw new IllegalArgumentException("Connection ID <= 0");
-
-        _socket[idx]  = _socket[idx] == null ? new Socket("localhost", _connector.getLocalPort()) : _socket[idx];
-        _out[idx] = _out[idx] == null ? new PrintWriter(_socket[idx].getOutputStream(), true) : _out[idx];
-        _in[idx] = _in[idx] == null ? new BufferedReader(new InputStreamReader(_socket[idx].getInputStream())) : _in[idx];
-
-        _connect.reset();
-
-        _out[idx].write("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n");
-        _out[idx].flush();
-
-        _connect.await();
-
-        assertEquals(count, _statistics.getConnectionsOpen());
-
-        String line=_in[idx].readLine();
-        while(line!=null)
-        {
-            if ("Server response".equals(line))
-                break;
-            line=_in[idx].readLine();
-        }
-    }
-}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java
index e8dc129..024e35c 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java
@@ -18,11 +18,16 @@
 
 package org.eclipse.jetty.server;
 
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.net.Socket;
 import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.concurrent.Exchanger;
 import java.util.concurrent.TimeUnit;
 
@@ -31,28 +36,52 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.io.ssl.SslConnection;
 import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.AdvancedRunner;
+import org.eclipse.jetty.toolchain.test.TestTracker;
 import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.hamcrest.Matchers;
 import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
+@RunWith(AdvancedRunner.class)
 public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
 {
-    protected static final int MAX_IDLE_TIME=500;
+    @Rule
+    public TestTracker tracker = new TestTracker();
+    
+    protected static final int MAX_IDLE_TIME=2000;
     private int sleepTime = MAX_IDLE_TIME + MAX_IDLE_TIME/5;
     private int minimumTestRuntime = MAX_IDLE_TIME-MAX_IDLE_TIME/5;
     private int maximumTestRuntime = MAX_IDLE_TIME*10;
 
     static
     {
-        System.setProperty("org.eclipse.jetty.io.nio.IDLE_TICK","100");
+        System.setProperty("org.eclipse.jetty.io.nio.IDLE_TICK","500");
     }
 
+    @Before
+    public void before()
+    {
+        super.before();
+        if (_httpConfiguration!=null)
+        {
+            _httpConfiguration.setBlockingTimeout(-1L);
+            _httpConfiguration.setMinRequestDataRate(-1);
+            _httpConfiguration.setIdleTimeout(-1);
+        }
+        
+    }
 
-    @Test
+    @Test(timeout=60000)
     public void testMaxIdleWithRequest10() throws Exception
     {
         configureServer(new HelloWorldHandler());
@@ -82,7 +111,7 @@
         Assert.assertTrue(System.currentTimeMillis() - start < maximumTestRuntime);
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testMaxIdleWithRequest11() throws Exception
     {
         configureServer(new EchoHandler());
@@ -115,7 +144,7 @@
         Assert.assertTrue(System.currentTimeMillis() - start < maximumTestRuntime);
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testMaxIdleWithRequest10NoClientClose() throws Exception
     {
         final Exchanger<EndPoint> exchanger = new Exchanger<>();
@@ -189,7 +218,7 @@
         Assert.assertFalse(endPoint.isOpen());
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testMaxIdleWithRequest10ClientIgnoresClose() throws Exception
     {
         final Exchanger<EndPoint> exchanger = new Exchanger<>();
@@ -265,7 +294,7 @@
         Assert.assertFalse(endPoint.isOpen());
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testMaxIdleWithRequest11NoClientClose() throws Exception
     {
         final Exchanger<EndPoint> exchanger = new Exchanger<>();
@@ -342,7 +371,220 @@
         Assert.assertFalse(endPoint.isOpen());
     }
 
-    @Test
+    @Test(timeout=60000)
+    @Ignore // TODO make more stable
+    public void testNoBlockingTimeoutRead() throws Exception
+    {
+        _httpConfiguration.setBlockingTimeout(-1L);
+        
+        configureServer(new EchoHandler());
+        Socket client=newSocket(_serverURI.getHost(),_serverURI.getPort());
+        client.setSoTimeout(10000);
+        InputStream is=client.getInputStream();
+        Assert.assertFalse(client.isClosed());
+
+        OutputStream os=client.getOutputStream();
+        os.write(("GET / HTTP/1.1\r\n"+
+                "host: "+_serverURI.getHost()+":"+_serverURI.getPort()+"\r\n"+
+                "Transfer-Encoding: chunked\r\n" +
+                "Content-Type: text/plain\r\n" +
+                "Connection: close\r\n" +
+                "\r\n"+
+                "5\r\n"+
+                "LMNOP\r\n")
+                .getBytes("utf-8"));
+        os.flush();
+
+        long start = System.currentTimeMillis();
+        try
+        {
+            Thread.sleep(250);
+            os.write("1".getBytes("utf-8"));
+            os.flush();
+            Thread.sleep(250);
+            os.write("0".getBytes("utf-8"));
+            os.flush();
+            Thread.sleep(250);
+            os.write("\r".getBytes("utf-8"));
+            os.flush();
+            Thread.sleep(250);
+            os.write("\n".getBytes("utf-8"));
+            os.flush();
+            Thread.sleep(250);
+            os.write("0123456789ABCDEF\r\n".getBytes("utf-8"));
+            os.write("0\r\n".getBytes("utf-8"));
+            os.write("\r\n".getBytes("utf-8"));
+            os.flush();   
+        }
+        catch(Exception e)
+        {
+            e.printStackTrace();
+        }
+        long duration=System.currentTimeMillis() - start;
+        Assert.assertThat(duration,Matchers.greaterThan(500L));
+
+        // read the response
+        String response = IO.toString(is);
+        Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 200 OK"));
+        Assert.assertThat(response,Matchers.containsString("LMNOP0123456789ABCDEF"));
+
+    }
+    
+    @Test(timeout=60000)
+    @Ignore // TODO make more stable
+    public void testBlockingTimeoutRead() throws Exception
+    {
+        _httpConfiguration.setBlockingTimeout(750L);
+        
+        configureServer(new EchoHandler());
+        Socket client=newSocket(_serverURI.getHost(),_serverURI.getPort());
+        client.setSoTimeout(10000);
+        InputStream is=client.getInputStream();
+        Assert.assertFalse(client.isClosed());
+
+        OutputStream os=client.getOutputStream();
+        os.write(("GET / HTTP/1.1\r\n"+
+                "host: "+_serverURI.getHost()+":"+_serverURI.getPort()+"\r\n"+
+                "Transfer-Encoding: chunked\r\n" +
+                "Content-Type: text/plain\r\n" +
+                "Connection: close\r\n" +
+                "\r\n"+
+                "5\r\n"+
+                "LMNOP\r\n")
+                .getBytes("utf-8"));
+        os.flush();
+
+        long start = System.currentTimeMillis();
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            Thread.sleep(300);
+            os.write("1".getBytes("utf-8"));
+            os.flush();
+            Thread.sleep(300);
+            os.write("0".getBytes("utf-8"));
+            os.flush();
+            Thread.sleep(300);
+            os.write("\r".getBytes("utf-8"));
+            os.flush();
+            Thread.sleep(300);
+            os.write("\n".getBytes("utf-8"));
+            os.flush();
+            Thread.sleep(300);
+            os.write("0123456789ABCDEF\r\n".getBytes("utf-8"));
+            os.write("0\r\n".getBytes("utf-8"));
+            os.write("\r\n".getBytes("utf-8"));
+            os.flush();   
+        }
+        catch(Exception e)
+        {
+        }
+        long duration=System.currentTimeMillis() - start;
+        Assert.assertThat(duration,Matchers.greaterThan(500L));
+        
+        try
+        {
+            // read the response
+            String response = IO.toString(is);
+            Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 500 "));
+            Assert.assertThat(response,Matchers.containsString("InterruptedIOException"));
+        }
+        catch(SSLException e)
+        {
+        }
+
+    }
+
+    @Test(timeout=60000)
+    @Ignore // TODO make more stable
+    public void testNoBlockingTimeoutWrite() throws Exception
+    {
+        configureServer(new HugeResponseHandler());
+        Socket client=newSocket(_serverURI.getHost(),_serverURI.getPort());
+        client.setSoTimeout(10000);
+
+        Assert.assertFalse(client.isClosed());
+
+        OutputStream os=client.getOutputStream();
+        BufferedReader is=new BufferedReader(new InputStreamReader(client.getInputStream(),StandardCharsets.ISO_8859_1),2048);
+
+        os.write((
+                "GET / HTTP/1.0\r\n"+
+                "host: "+_serverURI.getHost()+":"+_serverURI.getPort()+"\r\n"+
+                "connection: keep-alive\r\n"+
+                "Connection: close\r\n"+
+        "\r\n").getBytes("utf-8"));
+        os.flush();
+        
+        // read the header
+        String line=is.readLine();
+        Assert.assertThat(line,Matchers.startsWith("HTTP/1.1 200 OK"));
+        while(line.length()!=0)
+            line=is.readLine();
+        
+        for (int i=0;i<(128*1024);i++)
+        {
+            if (i%1028==0)
+            {
+                Thread.sleep(20);
+                // System.err.println("read "+System.currentTimeMillis());
+            }
+            line=is.readLine();
+            Assert.assertThat(line,Matchers.notNullValue());
+            Assert.assertEquals(1022,line.length());
+        }
+    }
+
+    @Test(timeout=60000)
+    @Ignore // TODO make more stable
+    public void testBlockingTimeoutWrite() throws Exception
+    {
+        _httpConfiguration.setBlockingTimeout(750L);
+        configureServer(new HugeResponseHandler());
+        Socket client=newSocket(_serverURI.getHost(),_serverURI.getPort());
+        client.setSoTimeout(10000);
+
+        Assert.assertFalse(client.isClosed());
+
+        OutputStream os=client.getOutputStream();
+        BufferedReader is=new BufferedReader(new InputStreamReader(client.getInputStream(),StandardCharsets.ISO_8859_1),2048);
+
+        os.write((
+                "GET / HTTP/1.0\r\n"+
+                "host: "+_serverURI.getHost()+":"+_serverURI.getPort()+"\r\n"+
+                "connection: keep-alive\r\n"+
+                "Connection: close\r\n"+
+        "\r\n").getBytes("utf-8"));
+        os.flush();
+        
+        // read the header
+        String line=is.readLine();
+        Assert.assertThat(line,Matchers.startsWith("HTTP/1.1 200 OK"));
+        while(line.length()!=0)
+            line=is.readLine();
+
+        long start=System.currentTimeMillis();
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class,AbstractConnection.class))
+        {
+            for (int i=0;i<(128*1024);i++)
+            {
+                if (i%1028==0)
+                {
+                    Thread.sleep(20);
+                    // System.err.println("read "+System.currentTimeMillis());
+                }
+                line=is.readLine();
+                if (line==null)
+                    break;
+            }
+        }
+        catch(Throwable e)
+        {}
+        long end=System.currentTimeMillis();
+        long duration = end-start;
+        Assert.assertThat(duration,Matchers.lessThan(20L*128L));
+    }
+    
+    @Test(timeout=60000)
     public void testMaxIdleNoRequest() throws Exception
     {
         configureServer(new EchoHandler());
@@ -351,6 +593,38 @@
         InputStream is=client.getInputStream();
         Assert.assertFalse(client.isClosed());
 
+        OutputStream os=client.getOutputStream();
+        os.write("GET ".getBytes("utf-8"));
+        os.flush();
+
+        Thread.sleep(sleepTime);
+        long start = System.currentTimeMillis();
+        try
+        {
+            IO.toString(is);
+            Assert.assertEquals(-1, is.read());
+        }
+        catch(SSLException e)
+        {
+            e.printStackTrace();
+        }
+        catch(Exception e)
+        {
+            e.printStackTrace();
+        }
+        Assert.assertTrue(System.currentTimeMillis() - start < maximumTestRuntime);
+
+    }
+
+    @Test(timeout=60000)
+    public void testMaxIdleNothingSent() throws Exception
+    {
+        configureServer(new EchoHandler());
+        Socket client=newSocket(_serverURI.getHost(),_serverURI.getPort());
+        client.setSoTimeout(10000);
+        InputStream is=client.getInputStream();
+        Assert.assertFalse(client.isClosed());
+
         Thread.sleep(sleepTime);
         long start = System.currentTimeMillis();
         try
@@ -360,7 +634,7 @@
         }
         catch(SSLException e)
         {
-
+            // e.printStackTrace();
         }
         catch(Exception e)
         {
@@ -370,7 +644,7 @@
 
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testMaxIdleWithSlowRequest() throws Exception
     {
         configureServer(new EchoHandler());
@@ -410,7 +684,7 @@
         }
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testMaxIdleWithSlowResponse() throws Exception
     {
         configureServer(new SlowResponseHandler());
@@ -439,7 +713,7 @@
         }
     }
 
-    @Test
+    @Test(timeout=60000)
     public void testMaxIdleWithWait() throws Exception
     {
         configureServer(new WaitHandler());
@@ -482,6 +756,27 @@
             out.close();
         }
     }
+    
+    protected static class HugeResponseHandler extends AbstractHandler
+    {
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            response.setStatus(200);
+            OutputStream out = response.getOutputStream();
+            byte[] buffer = new byte[128*1024*1024];
+            Arrays.fill(buffer,(byte)'x');
+            for (int i=0;i<128*1024;i++)
+            {
+                buffer[i*1024+1022]='\r';
+                buffer[i*1024+1023]='\n';
+            }
+            ByteBuffer bb = ByteBuffer.wrap(buffer);
+            ((HttpOutput)out).sendContent(bb);
+            out.close();
+        }
+    }
 
     protected static class WaitHandler extends AbstractHandler
     {
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/DumpHandler.java b/jetty-server/src/test/java/org/eclipse/jetty/server/DumpHandler.java
index 216875b..120cf64 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/DumpHandler.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/DumpHandler.java
@@ -71,6 +71,16 @@
         if (!isStarted())
             return;
 
+        if (Boolean.valueOf(request.getParameter("flush")))
+            response.flushBuffer();
+        
+        if (Boolean.valueOf(request.getParameter("empty")))
+        {
+            baseRequest.setHandled(true);
+            response.setStatus(200);
+            return;
+        }            
+            
         StringBuilder read = null;
         if (request.getParameter("read")!=null)
         {
@@ -106,6 +116,8 @@
         writer.write("<pre>\ncontentType="+request.getContentType()+"\n</pre>\n");
         writer.write("<pre>\nencoding="+request.getCharacterEncoding()+"\n</pre>\n");
         writer.write("<pre>\nservername="+request.getServerName()+"\n</pre>\n");
+        writer.write("<pre>\nlocal="+request.getLocalAddr()+":"+request.getLocalPort()+"\n</pre>\n");
+        writer.write("<pre>\nremote="+request.getRemoteAddr()+":"+request.getRemotePort()+"\n</pre>\n");
         writer.write("<h3>Header:</h3><pre>");
         writer.write(request.getMethod()+" "+request.getRequestURI()+" "+request.getProtocol()+"\n");
         Enumeration<String> headers = request.getHeaderNames();
@@ -213,6 +225,7 @@
             }
             catch(IOException e)
             {
+                e.printStackTrace();
                 writer.write(e.toString());
             }
         }
@@ -222,7 +235,8 @@
         writer.flush();
 
         // commit now
-        response.setContentLength(buf.size()+1000);
+        if (!Boolean.valueOf(request.getParameter("no-content-length")))
+            response.setContentLength(buf.size()+1000);
         response.addHeader("Before-Flush",response.isCommitted()?"Committed???":"Not Committed");
         buf.writeTo(out);
         out.flush();
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ErrorHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ErrorHandlerTest.java
new file mode 100644
index 0000000..6e8aee0
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ErrorHandlerTest.java
@@ -0,0 +1,260 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ErrorHandlerTest
+{
+    Server server;
+    LocalConnector connector;
+    
+    @Before
+    public void before() throws Exception
+    {
+        server = new Server();
+        connector = new LocalConnector(server);
+        server.addConnector(connector);
+        server.addBean(new ErrorHandler()
+        {
+            @Override
+            protected void generateAcceptableResponse(
+                Request baseRequest, 
+                HttpServletRequest request, 
+                HttpServletResponse response, 
+                int code, 
+                String message,
+                String mimeType) throws IOException
+            {
+                switch(mimeType)
+                {
+                    case "text/json":
+                    case "application/json":
+                    {
+                        baseRequest.setHandled(true);
+                        response.setContentType(mimeType);
+                        response.getWriter()
+                         .append("{")
+                         .append("code: \"").append(Integer.toString(code)).append("\",")
+                         .append("message: \"").append(message).append('"')
+                         .append("}");
+                        break;
+                    }
+                    default:
+                        super.generateAcceptableResponse(baseRequest,request,response,code,message,mimeType);
+                }
+            }
+            
+        });
+        server.start();
+    }
+    
+    @After
+    public void after() throws Exception
+    {
+        server.stop();
+    }
+    
+    @Test
+    public void test404NoAccept() throws Exception
+    {
+        String response = connector.getResponse(
+            "GET / HTTP/1.1\r\n"+
+            "Host: Localhost\r\n"+
+            "\r\n");
+
+        assertThat(response,startsWith("HTTP/1.1 404 "));
+        assertThat(response,not(containsString("Content-Length: 0")));
+        assertThat(response,containsString("Content-Type: text/html;charset=iso-8859-1"));
+    }
+    
+    @Test
+    public void test404EmptyAccept() throws Exception
+    {
+        String response = connector.getResponse(
+            "GET / HTTP/1.1\r\n"+
+            "Accept: \r\n"+
+            "Host: Localhost\r\n"+
+            "\r\n");
+        assertThat(response,startsWith("HTTP/1.1 404 "));
+        assertThat(response,containsString("Content-Length: 0"));
+        assertThat(response,not(containsString("Content-Type")));
+    }
+    
+    @Test
+    public void test404UnAccept() throws Exception
+    {
+        String response = connector.getResponse(
+            "GET / HTTP/1.1\r\n"+
+            "Accept: text/*;q=0\r\n"+
+            "Host: Localhost\r\n"+
+            "\r\n");
+
+        assertThat(response,startsWith("HTTP/1.1 404 "));
+        assertThat(response,containsString("Content-Length: 0"));
+        assertThat(response,not(containsString("Content-Type")));
+    }
+    
+    @Test
+    public void test404AllAccept() throws Exception
+    {
+        String response = connector.getResponse(
+            "GET / HTTP/1.1\r\n"+
+            "Host: Localhost\r\n"+
+            "Accept: */*\r\n"+
+            "\r\n");
+        
+        assertThat(response,startsWith("HTTP/1.1 404 "));
+        assertThat(response,not(containsString("Content-Length: 0")));
+        assertThat(response,containsString("Content-Type: text/html;charset=iso-8859-1"));
+    }
+    
+    @Test
+    public void test404HtmlAccept() throws Exception
+    {
+        String response = connector.getResponse(
+            "GET / HTTP/1.1\r\n"+
+            "Host: Localhost\r\n"+
+            "Accept: text/html\r\n"+
+            "\r\n");
+        
+        assertThat(response,startsWith("HTTP/1.1 404 "));
+        assertThat(response,not(containsString("Content-Length: 0")));
+        assertThat(response,containsString("Content-Type: text/html;charset=iso-8859-1"));
+    }
+    
+    @Test
+    public void test404HtmlAcceptAnyCharset() throws Exception
+    {
+        String response = connector.getResponse(
+            "GET / HTTP/1.1\r\n"+
+            "Host: Localhost\r\n"+
+            "Accept: text/html\r\n"+
+            "Accept-Charset: *\r\n"+
+            "\r\n");
+        
+        assertThat(response,startsWith("HTTP/1.1 404 "));
+        assertThat(response,not(containsString("Content-Length: 0")));
+        assertThat(response,containsString("Content-Type: text/html;charset=utf-8"));
+    }
+    
+    @Test
+    public void test404HtmlAcceptUtf8Charset() throws Exception
+    {
+        String response = connector.getResponse(
+            "GET / HTTP/1.1\r\n"+
+            "Host: Localhost\r\n"+
+            "Accept: text/html\r\n"+
+            "Accept-Charset: utf-8\r\n"+
+            "\r\n");
+        
+        assertThat(response,startsWith("HTTP/1.1 404 "));
+        assertThat(response,not(containsString("Content-Length: 0")));
+        assertThat(response,containsString("Content-Type: text/html;charset=utf-8"));
+    }
+    
+    @Test
+    public void test404HtmlAcceptNotUtf8Charset() throws Exception
+    {
+        String response = connector.getResponse(
+            "GET / HTTP/1.1\r\n"+
+            "Host: Localhost\r\n"+
+            "Accept: text/html\r\n"+
+            "Accept-Charset: utf-8;q=0\r\n"+
+            "\r\n");
+        
+        assertThat(response,startsWith("HTTP/1.1 404 "));
+        assertThat(response,not(containsString("Content-Length: 0")));
+        assertThat(response,containsString("Content-Type: text/html;charset=iso-8859-1"));
+    }
+    
+    @Test
+    public void test404HtmlAcceptNotUtf8UnknownCharset() throws Exception
+    {
+        String response = connector.getResponse(
+            "GET / HTTP/1.1\r\n"+
+            "Host: Localhost\r\n"+
+            "Accept: text/html\r\n"+
+            "Accept-Charset: utf-8;q=0,unknown\r\n"+
+            "\r\n");
+
+        assertThat(response,startsWith("HTTP/1.1 404 "));
+        assertThat(response,containsString("Content-Length: 0"));
+        assertThat(response,not(containsString("Content-Type")));
+    }
+    
+    @Test
+    public void test404HtmlAcceptUnknownUtf8Charset() throws Exception
+    {
+        String response = connector.getResponse(
+            "GET / HTTP/1.1\r\n"+
+            "Host: Localhost\r\n"+
+            "Accept: text/html\r\n"+
+            "Accept-Charset: utf-8;q=0.1,unknown\r\n"+
+            "\r\n");
+
+        assertThat(response,startsWith("HTTP/1.1 404 "));
+        assertThat(response,not(containsString("Content-Length: 0")));
+        assertThat(response,containsString("Content-Type: text/html;charset=utf-8"));
+    }
+    
+    @Test
+    public void test404PreferHtml() throws Exception
+    {
+        String response = connector.getResponse(
+            "GET / HTTP/1.1\r\n"+
+            "Host: Localhost\r\n"+
+            "Accept: text/html;q=1.0,text/json;q=0.5,*/*\r\n"+
+            "Accept-Charset: *\r\n"+
+            "\r\n");
+        
+        assertThat(response,startsWith("HTTP/1.1 404 "));
+        assertThat(response,not(containsString("Content-Length: 0")));
+        assertThat(response,containsString("Content-Type: text/html;charset=utf-8"));
+    }
+    
+    @Test
+    public void test404PreferJson() throws Exception
+    {
+        String response = connector.getResponse(
+            "GET / HTTP/1.1\r\n"+
+            "Host: Localhost\r\n"+
+            "Accept: text/html;q=0.5,text/json;q=1.0,*/*\r\n"+
+            "Accept-Charset: *\r\n"+
+            "\r\n");
+        
+        assertThat(response,startsWith("HTTP/1.1 404 "));
+        assertThat(response,not(containsString("Content-Length: 0")));
+        assertThat(response,containsString("Content-Type: text/json"));
+    }
+
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ExtendedServerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ExtendedServerTest.java
index 5159690..c12307d 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ExtendedServerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ExtendedServerTest.java
@@ -21,7 +21,6 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.net.Socket;
-import java.nio.ByteBuffer;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.SocketChannel;
 import java.nio.charset.StandardCharsets;
@@ -30,12 +29,12 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpCompliance;
 import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ManagedSelector;
 import org.eclipse.jetty.io.SelectChannelEndPoint;
-import org.eclipse.jetty.io.SelectorManager.ManagedSelector;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.util.thread.Scheduler;
 import org.hamcrest.Matchers;
@@ -80,10 +79,10 @@
         }
 
         @Override
-        public void onSelected()
+        public Runnable onSelected()
         {
             _lastSelected=System.currentTimeMillis();
-            super.onSelected();
+            return super.onSelected();
         }
 
         long getLastSelected()
@@ -96,19 +95,19 @@
     {
         public ExtendedHttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint)
         {
-            super(config,connector,endPoint);
+            super(config,connector,endPoint,HttpCompliance.RFC7230,false);
         }
 
         @Override
-        protected HttpChannelOverHttp newHttpChannel(HttpInput<ByteBuffer> httpInput)
+        protected HttpChannelOverHttp newHttpChannel()
         {
-            return new HttpChannelOverHttp(getConnector(), getHttpConfiguration(), getEndPoint(), this, httpInput)
+            return new HttpChannelOverHttp(this, getConnector(), getHttpConfiguration(), getEndPoint(), this)
             {
                 @Override
-                public boolean startRequest(HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version)
+                public boolean startRequest(String method, String uri, HttpVersion version)
                 {
                     getRequest().setAttribute("DispatchedAt",((ExtendedEndPoint)getEndPoint()).getLastSelected());
-                    return super.startRequest(httpMethod,method,uri,version);
+                    return super.startRequest(method,uri,version);
                 }
             };
         }
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java
new file mode 100644
index 0000000..8723b99
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java
@@ -0,0 +1,340 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.IO;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ForwardedRequestCustomizerTest
+{
+    private Server _server;
+    private LocalConnector _connector;
+    private RequestHandler _handler;
+    final Deque<String> _results = new ArrayDeque<>();
+    final AtomicBoolean _wasSecure = new AtomicBoolean(false);
+    final AtomicReference<String> _sslSession = new AtomicReference<>();
+    final AtomicReference<String> _sslCertificate = new AtomicReference<>();
+    
+    ForwardedRequestCustomizer _customizer;
+
+    @Before
+    public void init() throws Exception
+    {
+        _server = new Server();
+        HttpConnectionFactory http = new HttpConnectionFactory();
+        http.setInputBufferSize(1024);
+        http.getHttpConfiguration().setRequestHeaderSize(512);
+        http.getHttpConfiguration().setResponseHeaderSize(512);
+        http.getHttpConfiguration().setOutputBufferSize(2048);
+        http.getHttpConfiguration().addCustomizer(_customizer=new ForwardedRequestCustomizer());
+        _connector = new LocalConnector(_server,http);
+        _server.addConnector(_connector);
+        _handler = new RequestHandler();
+        _server.setHandler(_handler);
+
+        _handler._checker = new RequestTester()
+        {
+            @Override
+            public boolean check(HttpServletRequest request,HttpServletResponse response)
+            {
+                _wasSecure.set(request.isSecure());
+                _sslSession.set(String.valueOf(request.getAttribute("javax.servlet.request.ssl_session_id")));
+                _sslCertificate.set(String.valueOf(request.getAttribute("javax.servlet.request.cipher_suite")));
+                _results.add(request.getScheme());
+                _results.add(request.getServerName());
+                _results.add(Integer.toString(request.getServerPort()));
+                _results.add(request.getRemoteAddr());
+                _results.add(Integer.toString(request.getRemotePort()));
+                return true;
+            }
+        };
+        
+        _server.start();
+    }
+
+    @After
+    public void destroy() throws Exception
+    {
+        _server.stop();
+        _server.join();
+    }
+
+
+    @Test
+    public void testRFC7239_Examples_4() throws Exception
+    {
+        String response=_connector.getResponse(
+            "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "Forwarded: for=\"_gazonk\"\n"+
+             "Forwarded: For=\"[2001:db8:cafe::17]:4711\"\n"+
+             "Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43\n"+
+             "Forwarded: for=192.0.2.43, for=198.51.100.17\n"+
+            "\n");
+        assertThat(response, Matchers.containsString("200 OK"));
+        assertEquals("http",_results.poll());
+        assertEquals("myhost",_results.poll());
+        assertEquals("80",_results.poll());
+        assertEquals("[2001:db8:cafe::17]",_results.poll());
+        assertEquals("4711",_results.poll());
+    }
+    
+    @Test
+    public void testRFC7239_Examples_7_1() throws Exception
+    {
+        _connector.getResponse(
+            "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "Forwarded: for=192.0.2.43,for=\"[2001:db8:cafe::17]\",for=unknown\n"+
+            "\n");
+        _connector.getResponse(
+            "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "Forwarded: for=192.0.2.43, for=\"[2001:db8:cafe::17]\", for=unknown\n"+
+            "\n");
+        _connector.getResponse(
+            "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "Forwarded: for=192.0.2.43\n"+
+             "Forwarded: for=\"[2001:db8:cafe::17]\", for=unknown\n"+
+            "\n");
+
+        assertEquals("http",_results.poll());
+        assertEquals("myhost",_results.poll());
+        assertEquals("80",_results.poll());
+        assertEquals("192.0.2.43",_results.poll());
+        assertEquals("0",_results.poll());
+        assertEquals("http",_results.poll());
+        assertEquals("myhost",_results.poll());
+        assertEquals("80",_results.poll());
+        assertEquals("192.0.2.43",_results.poll());
+        assertEquals("0",_results.poll());
+        assertEquals("http",_results.poll());
+        assertEquals("myhost",_results.poll());
+        assertEquals("80",_results.poll());
+        assertEquals("192.0.2.43",_results.poll());
+        assertEquals("0",_results.poll());
+    }
+
+    @Test
+    public void testRFC7239_Examples_7_4() throws Exception
+    {
+        _connector.getResponse(
+            "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "Forwarded: for=192.0.2.43, for=\"[2001:db8:cafe::17]\"\n"+
+            "\n");
+
+        assertEquals("http",_results.poll());
+        assertEquals("myhost",_results.poll());
+        assertEquals("80",_results.poll());
+        assertEquals("192.0.2.43",_results.poll());
+        assertEquals("0",_results.poll());
+    }
+
+    @Test
+    public void testRFC7239_Examples_7_5() throws Exception
+    {
+        _connector.getResponse(
+            "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "Forwarded: for=192.0.2.43,for=198.51.100.17;by=203.0.113.60;proto=http;host=example.com\n"+
+            "\n");
+
+        assertEquals("http",_results.poll());
+        assertEquals("example.com",_results.poll());
+        assertEquals("80",_results.poll());
+        assertEquals("192.0.2.43",_results.poll());
+        assertEquals("0",_results.poll());
+    }
+
+    @Test
+    public void testProto() throws Exception
+    {
+        String response=_connector.getResponse(
+            "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "X-Forwarded-Proto: foobar\n"+
+             "Forwarded: proto=https\n"+
+            "\n");
+        assertThat(response, Matchers.containsString("200 OK"));
+        assertEquals("https",_results.poll());
+        assertEquals("myhost",_results.poll());
+        assertEquals("443",_results.poll());
+        assertEquals("0.0.0.0",_results.poll());
+        assertEquals("0",_results.poll());
+    }
+
+    @Test
+    public void testFor() throws Exception
+    {
+        String response=_connector.getResponse(
+            "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "X-Forwarded-For: 10.9.8.7,6.5.4.3\n"+
+            "\n");
+        assertThat(response, Matchers.containsString("200 OK"));
+        assertEquals("http",_results.poll());
+        assertEquals("myhost",_results.poll());
+        assertEquals("80",_results.poll());
+        assertEquals("10.9.8.7",_results.poll());
+        assertEquals("0",_results.poll());
+    }
+
+    @Test
+    public void testLegacyProto() throws Exception
+    {
+        String response=_connector.getResponse(
+            "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "X-Proxied-Https: on\n"+
+            "\n");
+        assertThat(response, Matchers.containsString("200 OK"));
+        assertEquals("https",_results.poll());
+        assertEquals("myhost",_results.poll());
+        assertEquals("443",_results.poll());
+        assertEquals("0.0.0.0",_results.poll());
+        assertEquals("0",_results.poll());
+        assertTrue(_wasSecure.get());
+    }
+
+    @Test
+    public void testSslSession() throws Exception
+    {
+        _customizer.setSslIsSecure(false);
+        String response=_connector.getResponse(
+             "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "Proxy-Ssl-Id: Wibble\n"+
+             "\n");
+        
+        assertThat(response, Matchers.containsString("200 OK"));
+        assertEquals("http",_results.poll());
+        assertEquals("myhost",_results.poll());
+        assertEquals("80",_results.poll());
+        assertEquals("0.0.0.0",_results.poll());
+        assertEquals("0",_results.poll());
+        assertFalse(_wasSecure.get());
+        assertEquals("Wibble",_sslSession.get());
+      
+        _customizer.setSslIsSecure(true);  
+        response=_connector.getResponse(
+             "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "Proxy-Ssl-Id: 0123456789abcdef\n"+
+             "\n");
+        
+        assertThat(response, Matchers.containsString("200 OK"));
+        assertEquals("https",_results.poll());
+        assertEquals("myhost",_results.poll());
+        assertEquals("443",_results.poll());
+        assertEquals("0.0.0.0",_results.poll());
+        assertEquals("0",_results.poll());
+        assertTrue(_wasSecure.get());
+        assertEquals("0123456789abcdef",_sslSession.get());
+    }
+    
+    @Test
+    public void testSslCertificate() throws Exception
+    {
+        _customizer.setSslIsSecure(false);
+        String response=_connector.getResponse(
+             "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "Proxy-auth-cert: Wibble\n"+
+             "\n");
+        
+        assertThat(response, Matchers.containsString("200 OK"));
+        assertEquals("http",_results.poll());
+        assertEquals("myhost",_results.poll());
+        assertEquals("80",_results.poll());
+        assertEquals("0.0.0.0",_results.poll());
+        assertEquals("0",_results.poll());
+        assertFalse(_wasSecure.get());
+        assertEquals("Wibble",_sslCertificate.get());
+        
+      
+        _customizer.setSslIsSecure(true);  
+        response=_connector.getResponse(
+             "GET / HTTP/1.1\n"+
+             "Host: myhost\n"+
+             "Proxy-auth-cert: 0123456789abcdef\n"+
+             "\n");
+        
+        assertThat(response, Matchers.containsString("200 OK"));
+        assertEquals("https",_results.poll());
+        assertEquals("myhost",_results.poll());
+        assertEquals("443",_results.poll());
+        assertEquals("0.0.0.0",_results.poll());
+        assertEquals("0",_results.poll());
+        assertTrue(_wasSecure.get());
+        assertEquals("0123456789abcdef",_sslCertificate.get());
+    }
+    
+    
+    
+    interface RequestTester
+    {
+        boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException;
+    }
+
+    private class RequestHandler extends AbstractHandler
+    {
+        private RequestTester _checker;
+        @SuppressWarnings("unused")
+        private String _content;
+
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            ((Request)request).setHandled(true);
+
+            if (request.getContentLength()>0
+                    && !MimeTypes.Type.FORM_ENCODED.asString().equals(request.getContentType())
+                    && !request.getContentType().startsWith("multipart/form-data"))
+                _content=IO.toString(request.getInputStream());
+
+            if (_checker!=null && _checker.check(request,response))
+                response.setStatus(200);
+            else
+                response.sendError(500);
+        }
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/GracefulStopTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/GracefulStopTest.java
index d05a09b..010023e 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/GracefulStopTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/GracefulStopTest.java
@@ -18,116 +18,289 @@
 
 package org.eclipse.jetty.server;
 
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.lessThan;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.EOFException;
 import java.io.IOException;
+import java.io.InputStream;
+import java.net.ConnectException;
 import java.net.Socket;
-import java.nio.charset.StandardCharsets;
+import java.nio.channels.ClosedChannelException;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.io.EofException;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.server.handler.StatisticsHandler;
+import org.eclipse.jetty.toolchain.test.OS;
 import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
 import org.hamcrest.Matchers;
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Test;
 
-public class GracefulStopTest
+public class GracefulStopTest 
 {
-    private Server server;
-
-    @Before
-    public void setup() throws Exception
+    /**
+     * Test of standard graceful timeout mechanism when a block request does
+     * not complete
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testGracefulNoWaiter() throws Exception
     {
-        server = new Server(0);
-        StatisticsHandler stats = new StatisticsHandler();
-        TestHandler test=new TestHandler();
-        server.setHandler(stats);
-        stats.setHandler(test);
-        server.setStopTimeout(10 * 1000);
-        
+        Server server= new Server();
+        server.setStopTimeout(1000);
+
+        ServerConnector connector = new ServerConnector(server);
+        connector.setPort(0);
+        server.addConnector(connector);
+
+        TestHandler handler = new TestHandler();
+        server.setHandler(handler);
+
         server.start();
+        final int port=connector.getLocalPort();
+        Socket client = new Socket("127.0.0.1", port);
+        client.getOutputStream().write((
+                "POST / HTTP/1.0\r\n"+
+                        "Host: localhost:"+port+"\r\n" +
+                        "Content-Type: plain/text\r\n" +
+                        "Content-Length: 10\r\n" +
+                        "\r\n"+
+                        "12345"
+                ).getBytes());
+        client.getOutputStream().flush();
+        handler.latch.await();
+
+        long start = System.nanoTime();
+        server.stop();
+        long stop = System.nanoTime();
+        
+        // No Graceful waiters
+        assertThat(TimeUnit.NANOSECONDS.toMillis(stop-start),lessThan(900L));
+
+        assertThat(client.getInputStream().read(),Matchers.is(-1));
+
+        assertThat(handler.handling.get(),Matchers.is(false));
+        assertThat(handler.thrown.get(),
+                Matchers.anyOf(
+                instanceOf(ClosedChannelException.class),
+                instanceOf(EofException.class),
+                instanceOf(EOFException.class))
+                );
+
+        client.close();
     }
 
+    /**
+     * Test of standard graceful timeout mechanism when a block request does
+     * not complete
+     * @throws Exception on test failure
+     */
     @Test
-    public void testGraceful() throws Exception
+    public void testGracefulTimeout() throws Exception
     {
-        new Thread()
-        {
-            @Override
-            public void run()
-            {
-                try
-                {
-                    TimeUnit.SECONDS.sleep(1);
-                    server.stop();
-                }
-                catch (Exception e)
-                {
-                    e.printStackTrace();
-                }
-            }
-        }.start();
+        Server server= new Server();
+        server.setStopTimeout(1000);
 
-        try(Socket socket = new Socket("localhost",server.getBean(NetworkConnector.class).getLocalPort());)
+        ServerConnector connector = new ServerConnector(server);
+        connector.setPort(0);
+        server.addConnector(connector);
+
+        TestHandler handler = new TestHandler();
+        StatisticsHandler stats = new StatisticsHandler();
+        server.setHandler(stats);
+        stats.setHandler(handler);
+
+        server.start();
+        final int port=connector.getLocalPort();
+        Socket client = new Socket("127.0.0.1", port);
+        client.getOutputStream().write((
+                "POST / HTTP/1.0\r\n"+
+                        "Host: localhost:"+port+"\r\n" +
+                        "Content-Type: plain/text\r\n" +
+                        "Content-Length: 10\r\n" +
+                        "\r\n"+
+                        "12345"
+                ).getBytes());
+        client.getOutputStream().flush();
+        handler.latch.await();
+
+        long start = System.nanoTime();
+        try
         {
-            socket.getOutputStream().write("GET / HTTP/1.0\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1));
-            String out = IO.toString(socket.getInputStream());
-            Assert.assertThat(out,Matchers.containsString("200 OK"));
+            server.stop();
+            Assert.fail();
         }
-    }
-    
-    @Test
-    public void testGracefulTimout() throws Exception
-    {
-        server.setStopTimeout(100);
-        new Thread()
+        catch(TimeoutException e)
         {
-            @Override
-            public void run()
-            {
-                try
-                {
-                    TimeUnit.SECONDS.sleep(1);
-                    server.stop();
-                }
-                catch (Exception e)
-                {
-                    //e.printStackTrace();
-                }
-            }
-        }.start();
+            long stop = System.nanoTime();
+            // No Graceful waiters
+            assertThat(TimeUnit.NANOSECONDS.toMillis(stop-start),greaterThan(900L));
+        }
 
-        try(Socket socket = new Socket("localhost",server.getBean(NetworkConnector.class).getLocalPort());)
+        assertThat(client.getInputStream().read(),Matchers.is(-1));
+
+        assertThat(handler.handling.get(),Matchers.is(false));
+        assertThat(handler.thrown.get(),instanceOf(ClosedChannelException.class));
+
+        client.close();
+    }
+
+
+    /**
+     * Test of standard graceful timeout mechanism when a block request does
+     * complete. Note that even though the request completes after 100ms, the
+     * stop always takes 1000ms
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testGracefulComplete() throws Exception
+    {
+        assumeTrue(!OS.IS_WINDOWS);
+        Server server= new Server();
+        server.setStopTimeout(10000);
+
+        ServerConnector connector = new ServerConnector(server);
+        connector.setPort(0);
+        server.addConnector(connector);
+
+        TestHandler handler = new TestHandler();
+        StatisticsHandler stats = new StatisticsHandler();
+        server.setHandler(stats);
+        stats.setHandler(handler);
+
+        server.start();
+        final int port=connector.getLocalPort();
+
+        try(final Socket client1 = new Socket("127.0.0.1", port);final Socket client2 = new Socket("127.0.0.1", port);)
         {
-            socket.getOutputStream().write("GET / HTTP/1.0\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1));
-            String out = IO.toString(socket.getInputStream());
-            Assert.assertEquals("",out);
+            client1.getOutputStream().write((
+                    "POST / HTTP/1.0\r\n"+
+                            "Host: localhost:"+port+"\r\n" +
+                            "Content-Type: plain/text\r\n" +
+                            "Content-Length: 10\r\n" +
+                            "\r\n"+
+                            "12345"
+                    ).getBytes());
+            client1.getOutputStream().flush();
+            handler.latch.await();
+
+            new Thread()
+            {
+                @Override
+                public void run() 
+                {
+                    long now = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
+                    long end = now+500;
+                    
+
+                    
+                    try
+                    {
+                        Thread.sleep(100);
+
+                        // Try creating a new connection
+                        try
+                        {
+                            new Socket("127.0.0.1", port);
+                            throw new IllegalStateException();
+                        }
+                        catch(ConnectException e)
+                        {
+                            
+                        }
+                        
+                        // Try another request on existing connection
+
+                        client2.getOutputStream().write((
+                                "GET / HTTP/1.0\r\n"+
+                                        "Host: localhost:"+port+"\r\n" +
+                                        "\r\n"
+                                ).getBytes());
+                        client2.getOutputStream().flush();
+                        String response2 = IO.toString(client2.getInputStream());
+                        assertThat(response2, containsString(" 503 "));
+
+                        now = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
+                        Thread.sleep(Math.max(1,end-now));
+                        client1.getOutputStream().write("567890".getBytes());
+                    }
+                    catch(Exception e)
+                    {
+                        e.printStackTrace();
+                    }
+                }
+            }.start();
+
+            long start = System.nanoTime();
+            server.stop();
+            long stop = System.nanoTime();
+            assertThat(TimeUnit.NANOSECONDS.toMillis(stop-start),greaterThan(490L));
+            assertThat(TimeUnit.NANOSECONDS.toMillis(stop-start),lessThan(10000L));
+
+            String response = IO.toString(client1.getInputStream());
+
+            assertThat(handler.handling.get(),Matchers.is(false));
+            assertThat(response, containsString(" 200 OK"));
+            assertThat(response, containsString("read 10/10"));
+            
+            assertThat(stats.getRequests(),Matchers.is(2));
+            assertThat(stats.getResponses5xx(),Matchers.is(1));
         }
     }
 
-    private static class TestHandler extends AbstractHandler
-    {
+
+    static class TestHandler extends AbstractHandler 
+    {		
+        final CountDownLatch latch = new CountDownLatch(1);
+        final AtomicReference<Throwable> thrown = new AtomicReference<Throwable>();
+        final AtomicBoolean handling = new AtomicBoolean(false);
+
         @Override
-        public void handle(final String s, final Request request, final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse)
-            throws IOException, ServletException
+        public void handle(String target, Request baseRequest,
+                HttpServletRequest request, HttpServletResponse response)
+                        throws IOException, ServletException 
         {
+            handling.set(true);
+            latch.countDown();
+            int c=0;
             try
             {
-                TimeUnit.SECONDS.sleep(2);
-            }
-            catch (InterruptedException e)
-            {
-            }
+                int content_length = request.getContentLength();
+                InputStream in = request.getInputStream();
 
-            httpServletResponse.getWriter().write("OK");
-            httpServletResponse.setStatus(200);
-            request.setHandled(true);
+                while(true)
+                {
+                    if (in.read()<0)
+                        break;
+                    c++;
+                }
+
+                baseRequest.setHandled(true);
+                response.setStatus(200);
+                response.getWriter().printf("read %d/%d%n",c,content_length);
+            }
+            catch(Throwable th)
+            {
+                thrown.set(th);
+            }
+            finally
+            {
+                handling.set(false);
+            }
         }
     }
 
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HostHeaderCustomizerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HostHeaderCustomizerTest.java
index b50382d..2f95f49 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HostHeaderCustomizerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HostHeaderCustomizerTest.java
@@ -18,9 +18,7 @@
 
 package org.eclipse.jetty.server;
 
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
@@ -29,10 +27,9 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpParser;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
@@ -69,24 +66,25 @@
         {
             try (Socket socket = new Socket("localhost", connector.getLocalPort()))
             {
-                OutputStream output = socket.getOutputStream();
-                String request = "" +
-                        "GET / HTTP/1.0\r\n" +
-                        "\r\n";
-                output.write(request.getBytes(StandardCharsets.UTF_8));
-                output.flush();
-
-                BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
-                SimpleHttpParser parser = new SimpleHttpParser();
-                SimpleHttpResponse response = parser.readResponse(input);
-
-                String location = response.getHeaders().get("location");
-                Assert.assertNotNull(location);
-                String schemePrefix = "http://";
-                Assert.assertTrue(location.startsWith(schemePrefix));
-                Assert.assertTrue(location.endsWith(redirectPath));
-                String hostPort = location.substring(schemePrefix.length(), location.length() - redirectPath.length());
-                Assert.assertEquals(serverName + ":" + serverPort, hostPort);
+                try(OutputStream output = socket.getOutputStream())
+                {
+                    String request = "" +
+                            "GET / HTTP/1.0\r\n" +
+                            "\r\n";
+                    output.write(request.getBytes(StandardCharsets.UTF_8));
+                    output.flush();
+    
+                    HttpTester.Input input = HttpTester.from(socket.getInputStream());
+                    HttpTester.Response response = HttpTester.parseResponse(input);
+    
+                    String location = response.get("location");
+                    Assert.assertNotNull(location);
+                    String schemePrefix = "http://";
+                    Assert.assertTrue(location.startsWith(schemePrefix));
+                    Assert.assertTrue(location.endsWith(redirectPath));
+                    String hostPort = location.substring(schemePrefix.length(), location.length() - redirectPath.length());
+                    Assert.assertEquals(serverName + ":" + serverPort, hostPort);
+                }
             }
         }
         finally
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
index 9e4977e..c72fd1a 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
@@ -24,23 +24,10 @@
  */
 package org.eclipse.jetty.server;
 
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpParser;
-import org.eclipse.jetty.http.MimeTypes;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.server.handler.ErrorHandler;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.log.StdErrLog;
-import org.hamcrest.Matchers;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -51,13 +38,25 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
-/**
- *
- */
+import org.eclipse.jetty.http.HttpCompliance;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpParser;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 public class HttpConnectionTest
 {
     private Server server;
@@ -68,13 +67,14 @@
     {
         server = new Server();
 
-        HttpConnectionFactory http = new HttpConnectionFactory();
-        http.getHttpConfiguration().setRequestHeaderSize(1024);
-        http.getHttpConfiguration().setResponseHeaderSize(1024);
-        
+        HttpConfiguration config = new HttpConfiguration();
+        config.setRequestHeaderSize(1024);
+        config.setResponseHeaderSize(1024);
+        config.setSendDateHeader(true);
+        HttpConnectionFactory http = new HttpConnectionFactory(config);
+
         connector = new LocalConnector(server,http,null);
         connector.setIdleTimeout(5000);
-        connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().setSendDateHeader(true);
         server.addConnector(connector);
         server.setHandler(new DumpHandler());
         ErrorHandler eh=new ErrorHandler();
@@ -99,33 +99,34 @@
             int offset=0;
 
             // Chunk last
-            offset=0;
-            response=connector.getResponses("GET /R1 HTTP/1.1\n"+
-                                           "Host: localhost\n"+
-                                           "Transfer-Encoding: chunked\n"+
-                                           "Content-Type: text/plain\n"+
-                                           "Connection: close\n"+
-                                           "\015\012"+
-                                           "5;\015\012"+
-                                           "12345\015\012"+
-                                           "0;\015\012\015\012");
+            response=connector.getResponses("GET /R1 HTTP/1.1\r\n"+
+                                           "Host: localhost\r\n"+
+                                           "Transfer-Encoding: chunked\r\n"+
+                                           "Content-Type: text/plain\r\n"+
+                                           "Connection: close\r\n"+
+                                           "\r\n"+
+                                           "5;\r\n"+
+                                           "12345\r\n"+
+                                           "0;\r\n" +
+                                           "\r\n");
             offset = checkContains(response,offset,"HTTP/1.1 200");
             offset = checkContains(response,offset,"/R1");
-            offset = checkContains(response,offset,"12345");
+            checkContains(response,offset,"12345");
 
             offset = 0;
-            response=connector.getResponses("GET /R2 HTTP/1.1\n"+
-                                           "Host: localhost\n"+
-                                           "Transfer-Encoding: chunked\n"+
-                                           "Content-Type: text/plain\n"+
-                                           "Connection: close\n"+
-                                           "\015\012"+
-                                           "5;\015\012"+
-                                           "ABCDE\015\012"+
-                                           "0;\015\012\015\012");
+            response=connector.getResponses("GET /R2 HTTP/1.1\r\n"+
+                                           "Host: localhost\r\n"+
+                                           "Transfer-Encoding: chunked\r\n"+
+                                           "Content-Type: text/plain\r\n"+
+                                           "Connection: close\r\n"+
+                                           "\r\n"+
+                                           "5;\r\n"+
+                                           "ABCDE\r\n"+
+                                           "0;\r\n" +
+                                           "\r\n");
             offset = checkContains(response,offset,"HTTP/1.1 200");
             offset = checkContains(response,offset,"/R2");
-            offset = checkContains(response,offset,"ABCDE");
+            checkContains(response,offset,"ABCDE");
         }
         catch(Exception e)
         {
@@ -138,10 +139,10 @@
     @Test
     public void testNoPath() throws Exception
     {
-        String response=connector.getResponses("GET http://localhost:80 HTTP/1.1\n"+
-                "Host: localhost:80\n"+
-                "Connection: close\n"+
-                "\n");
+        String response=connector.getResponses("GET http://localhost:80 HTTP/1.1\r\n"+
+                "Host: localhost:80\r\n"+
+                "Connection: close\r\n"+
+                "\r\n");
 
         int offset=0;
         offset = checkContains(response,offset,"HTTP/1.1 200");
@@ -151,10 +152,10 @@
     @Test
     public void testDate() throws Exception
     {
-        String response=connector.getResponses("GET / HTTP/1.1\n"+
-                "Host: localhost:80\n"+
-                "Connection: close\n"+
-                "\n");
+        String response=connector.getResponse("GET / HTTP/1.1\r\n"+
+                "Host: localhost:80\r\n"+
+                "Connection: close\r\n"+
+                "\r\n");
 
         int offset=0;
         offset = checkContains(response,offset,"HTTP/1.1 200");
@@ -165,10 +166,10 @@
     @Test
     public void testSetDate() throws Exception
     {
-        String response=connector.getResponses("GET /?date=1+Jan+1970 HTTP/1.1\n"+
-                "Host: localhost:80\n"+
-                "Connection: close\n"+
-                "\n");
+        String response=connector.getResponses("GET /?date=1+Jan+1970 HTTP/1.1\r\n"+
+                "Host: localhost:80\r\n"+
+                "Connection: close\r\n"+
+                "\r\n");
 
         int offset=0;
         offset = checkContains(response,offset,"HTTP/1.1 200");
@@ -179,71 +180,82 @@
     @Test
     public void testBadNoPath() throws Exception
     {
-        String response=connector.getResponses("GET http://localhost:80/../cheat HTTP/1.1\n"+
-                "Host: localhost:80\n"+
-                "\n");
-        int offset=0;
-        offset = checkContains(response,offset,"HTTP/1.1 400");
+        String response=connector.getResponses("GET http://localhost:80/../cheat HTTP/1.1\r\n"+
+                "Host: localhost:80\r\n"+
+                "\r\n");
+        checkContains(response,0,"HTTP/1.1 400");
     }
 
     @Test
     public void testOKPathDotDotPath() throws Exception
     {
-        String response=connector.getResponses("GET /ooops/../path HTTP/1.0\nHost: localhost:80\n\n");
+        String response=connector.getResponses("GET /ooops/../path HTTP/1.0\r\nHost: localhost:80\r\n\n");
         checkContains(response,0,"HTTP/1.1 200 OK");
         checkContains(response,0,"pathInfo=/path");
     }
-    
+
     @Test
     public void testBadPathDotDotPath() throws Exception
     {
-        String response=connector.getResponses("GET /ooops/../../path HTTP/1.0\nHost: localhost:80\n\n");
-        checkContains(response,0,"HTTP/1.1 400 Bad Request");
+        String response=connector.getResponses("GET /ooops/../../path HTTP/1.0\r\nHost: localhost:80\r\n\n");
+        checkContains(response,0,"HTTP/1.1 400 Bad URI");
     }
-    
+
     @Test
     public void testOKPathEncodedDotDotPath() throws Exception
     {
-        String response=connector.getResponses("GET /ooops/%2e%2e/path HTTP/1.0\nHost: localhost:80\n\n");
+        String response=connector.getResponses("GET /ooops/%2e%2e/path HTTP/1.0\r\nHost: localhost:80\r\n\n");
         checkContains(response,0,"HTTP/1.1 200 OK");
         checkContains(response,0,"pathInfo=/path");
     }
-    
+
     @Test
     public void testBadPathEncodedDotDotPath() throws Exception
     {
-        String response=connector.getResponses("GET /ooops/%2e%2e/%2e%2e/path HTTP/1.0\nHost: localhost:80\n\n");
-        checkContains(response,0,"HTTP/1.1 400 Bad Request");
+        String response=connector.getResponses("GET /ooops/%2e%2e/%2e%2e/path HTTP/1.0\r\nHost: localhost:80\r\n\n");
+        checkContains(response,0,"HTTP/1.1 400 Bad URI");
     }
-    
+
     @Test
     public void testBadDotDotPath() throws Exception
     {
-        String response=connector.getResponses("GET ../path HTTP/1.0\nHost: localhost:80\n\n");
-        checkContains(response,0,"HTTP/1.1 400 Bad Request");
+        String response=connector.getResponses("GET ../path HTTP/1.0\r\nHost: localhost:80\r\n\n");
+        checkContains(response,0,"HTTP/1.1 400 Bad URI");
     }
-    
+
     @Test
     public void testBadSlashDotDotPath() throws Exception
     {
-        String response=connector.getResponses("GET /../path HTTP/1.0\nHost: localhost:80\n\n");
-        checkContains(response,0,"HTTP/1.1 400 Bad Request");
+        String response=connector.getResponses("GET /../path HTTP/1.0\r\nHost: localhost:80\r\n\n");
+        checkContains(response,0,"HTTP/1.1 400 Bad URI");
     }
 
     @Test
     public void testEncodedBadDotDotPath() throws Exception
     {
-        String response=connector.getResponses("GET %2e%2e/path HTTP/1.0\nHost: localhost:80\n\n");
-        checkContains(response,0,"HTTP/1.1 400 Bad Request");
+        String response=connector.getResponses("GET %2e%2e/path HTTP/1.0\r\nHost: localhost:80\r\n\n");
+        checkContains(response,0,"HTTP/1.1 400 Bad URI");
     }
 
     @Test
+    public void test_0_9() throws Exception
+    {
+        connector.getConnectionFactory(HttpConnectionFactory.class).setHttpCompliance(HttpCompliance.RFC2616);
+        String response=connector.getResponses("GET /R1\n");
+
+        int offset=0;
+        checkNotContained(response,offset,"HTTP/1.1");
+        checkNotContained(response,offset,"200");
+        checkContains(response,offset,"pathInfo=/R1");
+    }
+    
+    @Test
     public void testSimple() throws Exception
     {
-        String response=connector.getResponses("GET /R1 HTTP/1.1\n"+
-                "Host: localhost\n"+
-                "Connection: close\n"+
-                "\n");
+        String response=connector.getResponses("GET /R1 HTTP/1.1\r\n"+
+                "Host: localhost\r\n"+
+                "Connection: close\r\n"+
+                "\r\n");
 
         int offset=0;
         offset = checkContains(response,offset,"HTTP/1.1 200");
@@ -253,13 +265,14 @@
     @Test
     public void testEmptyChunk() throws Exception
     {
-        String response=connector.getResponses("GET /R1 HTTP/1.1\n"+
-                "Host: localhost\n"+
-                "Transfer-Encoding: chunked\n"+
-                "Content-Type: text/plain\n"+
-                "Connection: close\n"+
-                "\015\012"+
-        "0\015\012\015\012");
+        String response=connector.getResponse("GET /R1 HTTP/1.1\r\n"+
+                "Host: localhost\r\n"+
+                "Transfer-Encoding: chunked\r\n"+
+                "Content-Type: text/plain\r\n"+
+                "Connection: close\r\n"+
+                "\r\n"+
+                "0\r\n" +
+                "\r\n");
 
         int offset=0;
         offset = checkContains(response,offset,"HTTP/1.1 200");
@@ -267,17 +280,77 @@
     }
 
     @Test
+    public void testChunk() throws Exception
+    {
+        String response=connector.getResponse("GET /R1 HTTP/1.1\r\n"+
+                "Host: localhost\r\n"+
+                "Transfer-Encoding: chunked\r\n"+
+                "Content-Type: text/plain\r\n"+
+                "Connection: close\r\n"+
+                "\r\n"+
+                "A\r\n" +
+                "0123456789\r\n"+
+                "0\r\n" +
+                "\r\n");
+
+        int offset=0;
+        offset = checkContains(response,offset,"HTTP/1.1 200");
+        offset = checkContains(response,offset,"/R1");
+        checkContains(response,offset,"0123456789");
+    }
+
+    @Test
+    public void testChunkTrailer() throws Exception
+    {
+        String response=connector.getResponse("GET /R1 HTTP/1.1\r\n"+
+                "Host: localhost\r\n"+
+                "Transfer-Encoding: chunked\r\n"+
+                "Content-Type: text/plain\r\n"+
+                "Connection: close\r\n"+
+                "\r\n"+
+                "A\r\n" +
+                "0123456789\r\n"+
+                "0\r\n" +
+                "Trailer: ignored\r\n" +
+                "\r\n");
+
+        int offset=0;
+        offset = checkContains(response,offset,"HTTP/1.1 200");
+        offset = checkContains(response,offset,"/R1");
+        checkContains(response,offset,"0123456789");
+    }
+
+    @Test
+    public void testChunkNoTrailer() throws Exception
+    {
+        String response=connector.getResponse("GET /R1 HTTP/1.1\r\n"+
+                "Host: localhost\r\n"+
+                "Transfer-Encoding: chunked\r\n"+
+                "Content-Type: text/plain\r\n"+
+                "Connection: close\r\n"+
+                "\r\n"+
+                "A\r\n" +
+                "0123456789\r\n"+
+                "0\r\n");
+
+        int offset=0;
+        offset = checkContains(response,offset,"HTTP/1.1 200");
+        offset = checkContains(response,offset,"/R1");
+        checkContains(response,offset,"0123456789");
+    }
+
+    @Test
     public void testHead() throws Exception
     {
-        String responsePOST=connector.getResponses("POST /R1 HTTP/1.1\015\012"+
-                "Host: localhost\015\012"+
-                "Connection: close\015\012"+
-                "\015\012");
-        
-        String responseHEAD=connector.getResponses("HEAD /R1 HTTP/1.1\015\012"+
-            "Host: localhost\015\012"+
-            "Connection: close\015\012"+
-            "\015\012");
+        String responsePOST=connector.getResponses("POST /R1 HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "Connection: close\r\n"+
+            "\r\n");
+
+        String responseHEAD=connector.getResponses("HEAD /R1 HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "Connection: close\r\n"+
+            "\r\n");
 
         String postLine;
         boolean postDate=false;
@@ -287,7 +360,7 @@
             postLine = in.readLine();
             String line=in.readLine();
             while (line!=null && line.length()>0)
-            {    
+            {
                 if (line.startsWith("Date:"))
                     postDate=true;
                 else
@@ -303,7 +376,7 @@
             headLine = in.readLine();
             String line=in.readLine();
             while (line!=null && line.length()>0)
-            {    
+            {
                 if (line.startsWith("Date:"))
                     headDate=true;
                 else
@@ -311,11 +384,59 @@
                 line=in.readLine();
             }
         }
-        
+
         assertThat(postLine,equalTo(headLine));
         assertThat(postDate,equalTo(headDate));
         assertTrue(postHeaders.equals(headHeaders));
-        
+    }
+
+    @Test
+    public void testHeadChunked() throws Exception
+    {
+        String responsePOST=connector.getResponse("POST /R1?no-content-length=true HTTP/1.1\r\n"+
+                "Host: localhost\r\n"+
+                "\r\n",false,1,TimeUnit.SECONDS);
+
+        String responseHEAD=connector.getResponse("HEAD /R1?no-content-length=true HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "\r\n",true,1,TimeUnit.SECONDS);
+
+        String postLine;
+        boolean postDate=false;
+        Set<String> postHeaders = new HashSet<>();
+        try(BufferedReader in = new BufferedReader(new StringReader(responsePOST)))
+        {
+            postLine = in.readLine();
+            String line=in.readLine();
+            while (line!=null && line.length()>0)
+            {
+                if (line.startsWith("Date:"))
+                    postDate=true;
+                else
+                    postHeaders.add(line);
+                line=in.readLine();
+            }
+        }
+        String headLine;
+        boolean headDate=false;
+        Set<String> headHeaders = new HashSet<>();
+        try(BufferedReader in = new BufferedReader(new StringReader(responseHEAD)))
+        {
+            headLine = in.readLine();
+            String line=in.readLine();
+            while (line!=null && line.length()>0)
+            {
+                if (line.startsWith("Date:"))
+                    headDate=true;
+                else
+                    headHeaders.add(line);
+                line=in.readLine();
+            }
+        }
+
+        assertThat(postLine,equalTo(headLine));
+        assertThat(postDate,equalTo(headDate));
+        assertTrue(postHeaders.equals(headHeaders));
     }
 
     @Test
@@ -324,34 +445,32 @@
         Log.getLogger(HttpParser.class).info("badMessage: Number formate exception expected ...");
         String response;
 
-        response=connector.getResponses("GET http://localhost:EXPECTED_NUMBER_FORMAT_EXCEPTION/ HTTP/1.1\n"+
-            "Host: localhost\n"+
-            "Connection: close\015\012"+
-            "\015\012");
+        response=connector.getResponses("GET http://localhost:EXPECTED_NUMBER_FORMAT_EXCEPTION/ HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "Connection: close\r\n"+
+            "\r\n");
         checkContains(response,0,"HTTP/1.1 400");
     }
 
     @Test
-    public void testNoHost()
-        throws Exception
+    public void testNoHost() throws Exception
     {
         String response;
 
-        response = connector.getResponses( "GET / HTTP/1.1\r\n"
-                                               + "\r\n" );
-        checkContains( response, 0, "HTTP/1.1 400" );
+        response=connector.getResponse("GET / HTTP/1.1\r\n"+
+            "\r\n");
+        checkContains(response,0,"HTTP/1.1 400");
     }
 
     @Test
-    public void testEmptyHost()
-        throws Exception
+    public void testEmptyHost() throws Exception
     {
         String response;
 
-        response = connector.getResponses( "GET / HTTP/1.1\r\n"
-                                               + "Host:\r\n"
-                                               + "\r\n" );
-        checkContains( response, 0, "HTTP/1.1 200" );
+        response=connector.getResponse("GET / HTTP/1.1\r\n"+
+            "Host:\r\n"+
+            "\r\n");
+        checkContains(response,0,"HTTP/1.1 200");
     }
 
     @Test
@@ -360,11 +479,14 @@
         Log.getLogger(HttpParser.class).info("badMessage: bad encoding expected ...");
         String response;
 
-        response=connector.getResponses("GET /bad/encoding%1 HTTP/1.1\n"+
-            "Host: localhost\n"+
-            "Connection: close\n"+
-            "\015\012");
-        checkContains(response,0,"HTTP/1.1 400");
+        try(StacklessLogging stackless = new StacklessLogging(HttpParser.class))
+        {
+            response=connector.getResponse("GET /bad/encoding%1 HTTP/1.1\r\n"+
+                    "Host: localhost\r\n"+
+                    "Connection: close\r\n"+
+                    "\r\n");
+            checkContains(response,0,"HTTP/1.1 400");
+        }
     }
 
     @Test
@@ -373,41 +495,40 @@
         Log.getLogger(HttpParser.class).info("badMessage: bad encoding expected ...");
         String response;
 
-        response=connector.getResponses("GET /foo/bar%c0%00 HTTP/1.1\n"+
-            "Host: localhost\n"+
-            "Connection: close\n"+
-            "\015\012");
+        response=connector.getResponses("GET /foo/bar%c0%00 HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "Connection: close\r\n"+
+            "\r\n");
         checkContains(response,0,"HTTP/1.1 200"); //now fallback to iso-8859-1
 
-        response=connector.getResponses("GET /bad/utf8%c1 HTTP/1.1\n"+
-            "Host: localhost\n"+
-            "Connection: close\n"+
-            "\015\012");
+        response=connector.getResponses("GET /bad/utf8%c1 HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "Connection: close\r\n"+
+            "\r\n");
         checkContains(response,0,"HTTP/1.1 200"); //now fallback to iso-8859-1
     }
 
     @Test
     public void testAutoFlush() throws Exception
     {
-        String response=null;
         int offset=0;
 
-        offset=0;
-        response=connector.getResponses("GET /R1 HTTP/1.1\n"+
-            "Host: localhost\n"+
-            "Transfer-Encoding: chunked\n"+
-            "Content-Type: text/plain\n"+
-            "Connection: close\n"+
-            "\015\012"+
-            "5;\015\012"+
-            "12345\015\012"+
-            "0;\015\012\015\012");
+        String response = connector.getResponses("GET /R1 HTTP/1.1\r\n" +
+                "Host: localhost\r\n" +
+                "Transfer-Encoding: chunked\r\n" +
+                "Content-Type: text/plain\r\n" +
+                "Connection: close\r\n" +
+                "\r\n" +
+                "5;\r\n" +
+                "12345\r\n" +
+                "0;\r\n" +
+                "\r\n");
         offset = checkContains(response,offset,"HTTP/1.1 200");
         checkNotContained(response,offset,"IgnoreMe");
         offset = checkContains(response,offset,"/R1");
-        offset = checkContains(response,offset,"12345");
+        checkContains(response,offset,"12345");
     }
-    
+
     @Test
     public void testEmptyFlush() throws Exception
     {
@@ -424,70 +545,68 @@
             }
         });
         server.start();
-        
-        String response=connector.getResponses("GET / HTTP/1.1\n"+
-            "Host: localhost\n"+
-            "Connection: close\n"+
-            "\n");
-        
+
+        String response=connector.getResponses("GET / HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "Connection: close\r\n"+
+            "\r\n");
+
         assertThat(response, Matchers.containsString("200 OK"));
     }
 
     @Test
     public void testCharset() throws Exception
     {
-
         String response=null;
         try
         {
             int offset=0;
-
-            offset=0;
-            response=connector.getResponses("GET /R1 HTTP/1.1\n"+
-                                           "Host: localhost\n"+
-                                           "Transfer-Encoding: chunked\n"+
-                                           "Content-Type: text/plain; charset=utf-8\n"+
-                                           "Connection: close\n"+
-                                           "\015\012"+
-                                           "5;\015\012"+
-                                           "12345\015\012"+
-                                           "0;\015\012\015\012");
+            response=connector.getResponses("GET /R1 HTTP/1.1\r\n"+
+                                           "Host: localhost\r\n"+
+                                           "Transfer-Encoding: chunked\r\n"+
+                                           "Content-Type: text/plain; charset=utf-8\r\n"+
+                                           "Connection: close\r\n"+
+                                           "\r\n"+
+                                           "5;\r\n"+
+                                           "12345\r\n"+
+                                           "0;\r\n" +
+                                           "\r\n");
             offset = checkContains(response,offset,"HTTP/1.1 200");
             offset = checkContains(response,offset,"/R1");
             offset = checkContains(response,offset,"encoding=UTF-8");
-            offset = checkContains(response,offset,"12345");
+            checkContains(response,offset,"12345");
 
             offset=0;
-            response=connector.getResponses("GET /R1 HTTP/1.1\n"+
-                                           "Host: localhost\n"+
-                                           "Transfer-Encoding: chunked\n"+
-                                           "Content-Type: text/plain; charset =  iso-8859-1 ; other=value\n"+
-                                           "Connection: close\n"+
-                                           "\015\012"+
-                                           "5;\015\012"+
-                                           "12345\015\012"+
-                                           "0;\015\012\015\012");
+            response=connector.getResponses("GET /R1 HTTP/1.1\r\n"+
+                                           "Host: localhost\r\n"+
+                                           "Transfer-Encoding: chunked\r\n"+
+                                           "Content-Type: text/plain; charset =  iso-8859-1 ; other=value\r\n"+
+                                           "Connection: close\r\n"+
+                                           "\r\n"+
+                                           "5;\r\n"+
+                                           "12345\r\n"+
+                                           "0;\r\n" +
+                                           "\r\n");
             offset = checkContains(response,offset,"HTTP/1.1 200");
-            offset = checkContains(response,offset,"encoding=ISO-8859-1");
+            offset = checkContains(response,offset,"encoding=iso-8859-1");
             offset = checkContains(response,offset,"/R1");
-            offset = checkContains(response,offset,"12345");
+            checkContains(response,offset,"12345");
 
             offset=0;
-            response=connector.getResponses("GET /R1 HTTP/1.1\n"+
-                                           "Host: localhost\n"+
-                                           "Transfer-Encoding: chunked\n"+
-                                           "Content-Type: text/plain; charset=unknown\n"+
-                                           "Connection: close\n"+
-                                           "\015\012"+
-                                           "5;\015\012"+
-                                           "12345\015\012"+
-                                           "0;\015\012\015\012");
+            response=connector.getResponses("GET /R1 HTTP/1.1\r\n"+
+                                           "Host: localhost\r\n"+
+                                           "Transfer-Encoding: chunked\r\n"+
+                                           "Content-Type: text/plain; charset=unknown\r\n"+
+                                           "Connection: close\r\n"+
+                                           "\r\n"+
+                                           "5;\r\n"+
+                                           "12345\r\n"+
+                                           "0;\r\n" +
+                                           "\r\n");
             offset = checkContains(response,offset,"HTTP/1.1 200");
             offset = checkContains(response,offset,"encoding=unknown");
             offset = checkContains(response,offset,"/R1");
-            offset = checkContains(response,offset,"UnsupportedEncodingException");
-
-
+            checkContains(response,offset,"UnsupportedEncodingException");
         }
         catch(Exception e)
         {
@@ -500,32 +619,29 @@
     @Test
     public void testUnconsumed() throws Exception
     {
-        String response=null;
-        String requests=null;
         int offset=0;
+        String requests =
+        "GET /R1?read=4 HTTP/1.1\r\n" +
+        "Host: localhost\r\n" +
+        "Transfer-Encoding: chunked\r\n" +
+        "Content-Type: text/plain; charset=utf-8\r\n" +
+        "\r\n" +
+        "5;\r\n" +
+        "12345\r\n" +
+        "5;\r\n" +
+        "67890\r\n" +
+        "0;\r\n" +
+        "\r\n" +
+        "GET /R2 HTTP/1.1\r\n" +
+        "Host: localhost\r\n" +
+        "Content-Type: text/plain; charset=utf-8\r\n" +
+        "Content-Length: 10\r\n" +
+        "Connection: close\r\n" +
+        "\r\n" +
+        "abcdefghij\r\n";
 
-        offset=0;
-        requests=
-        "GET /R1?read=4 HTTP/1.1\n"+
-        "Host: localhost\n"+
-        "Transfer-Encoding: chunked\n"+
-        "Content-Type: text/plain; charset=utf-8\n"+
-        "\015\012"+
-        "5;\015\012"+
-        "12345\015\012"+
-        "5;\015\012"+
-        "67890\015\012"+
-        "0;\015\012\015\012"+
-        "GET /R2 HTTP/1.1\n"+
-        "Host: localhost\n"+
-        "Content-Type: text/plain; charset=utf-8\n"+
-        "Content-Length: 10\n"+
-        "Connection: close\n"+
-        "\n"+
-        "abcdefghij\n";
+        String response = connector.getResponses(requests);
 
-        response=connector.getResponses(requests);
-        
         offset = checkContains(response,offset,"HTTP/1.1 200");
         offset = checkContains(response,offset,"pathInfo=/R1");
         offset = checkContains(response,offset,"1234");
@@ -533,149 +649,131 @@
         offset = checkContains(response,offset,"HTTP/1.1 200");
         offset = checkContains(response,offset,"pathInfo=/R2");
         offset = checkContains(response,offset,"encoding=UTF-8");
-        offset = checkContains(response,offset,"abcdefghij");
+        checkContains(response,offset,"abcdefghij");
     }
 
     @Test
     public void testUnconsumedTimeout() throws Exception
     {
         connector.setIdleTimeout(500);
-        String response=null;
-        String requests=null;
         int offset=0;
-
-        offset=0;
-        requests=
-        "GET /R1?read=4 HTTP/1.1\n"+
-        "Host: localhost\n"+
-        "Transfer-Encoding: chunked\n"+
-        "Content-Type: text/plain; charset=utf-8\n"+
-        "\015\012"+
-        "5;\015\012"+
-        "12345\015\012";
+        String requests=
+        "GET /R1?read=4 HTTP/1.1\r\n"+
+        "Host: localhost\r\n"+
+        "Transfer-Encoding: chunked\r\n"+
+        "Content-Type: text/plain; charset=utf-8\r\n"+
+        "\r\n"+
+        "5;\r\n"+
+        "12345\r\n";
 
         long start=System.currentTimeMillis();
-        response=connector.getResponses(requests,2000,TimeUnit.MILLISECONDS);
+        String response = connector.getResponses(requests, 2000, TimeUnit.MILLISECONDS);
         if ((System.currentTimeMillis()-start)>=2000)
             Assert.fail();
-        
+
         offset = checkContains(response,offset,"HTTP/1.1 200");
         offset = checkContains(response,offset,"pathInfo=/R1");
         offset = checkContains(response,offset,"1234");
         checkNotContained(response,offset,"56789");
     }
-    
+
     @Test
     public void testUnconsumedErrorRead() throws Exception
     {
-        String response=null;
-        String requests=null;
         int offset=0;
+        String requests=
+        "GET /R1?read=1&error=499 HTTP/1.1\r\n"+
+        "Host: localhost\r\n"+
+        "Transfer-Encoding: chunked\r\n"+
+        "Content-Type: text/plain; charset=utf-8\r\n"+
+        "\r\n"+
+        "5;\r\n"+
+        "12345\r\n"+
+        "5;\r\n"+
+        "67890\r\n"+
+        "0;\r\n" +
+        "\r\n"+
+        "GET /R2 HTTP/1.1\r\n"+
+        "Host: localhost\r\n"+
+        "Content-Type: text/plain; charset=utf-8\r\n"+
+        "Content-Length: 10\r\n"+
+        "Connection: close\r\n"+
+        "\r\n"+
+        "abcdefghij\r\n";
 
-        offset=0;
-        requests=
-        "GET /R1?read=1&error=499 HTTP/1.1\n"+
-        "Host: localhost\n"+
-        "Transfer-Encoding: chunked\n"+
-        "Content-Type: text/plain; charset=utf-8\n"+
-        "\015\012"+
-        "5;\015\012"+
-        "12345\015\012"+
-        "5;\015\012"+
-        "67890\015\012"+
-        "0;\015\012\015\012"+
-        "GET /R2 HTTP/1.1\n"+
-        "Host: localhost\n"+
-        "Content-Type: text/plain; charset=utf-8\n"+
-        "Content-Length: 10\n"+
-        "Connection: close\n"+
-        "\n"+
-        "abcdefghij\n";
-
-        response=connector.getResponses(requests);
+        String response = connector.getResponses(requests);
 
         offset = checkContains(response,offset,"HTTP/1.1 499");
         offset = checkContains(response,offset,"HTTP/1.1 200");
         offset = checkContains(response,offset,"/R2");
         offset = checkContains(response,offset,"encoding=UTF-8");
-        offset = checkContains(response,offset,"abcdefghij");
+        checkContains(response,offset,"abcdefghij");
     }
-    
+
     @Test
     public void testUnconsumedErrorStream() throws Exception
     {
-        String response=null;
-        String requests=null;
         int offset=0;
+        String requests=
+        "GET /R1?error=599 HTTP/1.1\r\n"+
+        "Host: localhost\r\n"+
+        "Transfer-Encoding: chunked\r\n"+
+        "Content-Type: application/data; charset=utf-8\r\n"+
+        "\r\n"+
+        "5;\r\n"+
+        "12345\r\n"+
+        "5;\r\n"+
+        "67890\r\n"+
+        "0;\r\n" +
+        "\r\n"+
+        "GET /R2 HTTP/1.1\r\n"+
+        "Host: localhost\r\n"+
+        "Content-Type: text/plain; charset=utf-8\r\n"+
+        "Content-Length: 10\r\n"+
+        "Connection: close\r\n"+
+        "\r\n"+
+        "abcdefghij\r\n";
 
-        offset=0;
-        requests=
-        "GET /R1?error=599 HTTP/1.1\n"+
-        "Host: localhost\n"+
-        "Transfer-Encoding: chunked\n"+
-        "Content-Type: application/data; charset=utf-8\n"+
-        "\015\012"+
-        "5;\015\012"+
-        "12345\015\012"+
-        "5;\015\012"+
-        "67890\015\012"+
-        "0;\015\012\015\012"+
-        "GET /R2 HTTP/1.1\n"+
-        "Host: localhost\n"+
-        "Content-Type: text/plain; charset=utf-8\n"+
-        "Content-Length: 10\n"+
-        "Connection: close\n"+
-        "\n"+
-        "abcdefghij\n";
-
-        response=connector.getResponses(requests);
+        String response = connector.getResponses(requests);
 
         offset = checkContains(response,offset,"HTTP/1.1 599");
         offset = checkContains(response,offset,"HTTP/1.1 200");
         offset = checkContains(response,offset,"/R2");
         offset = checkContains(response,offset,"encoding=UTF-8");
-        offset = checkContains(response,offset,"abcdefghij");
+        checkContains(response,offset,"abcdefghij");
     }
 
     @Test
     public void testUnconsumedException() throws Exception
     {
-        String response=null;
-        String requests=null;
         int offset=0;
-
-        offset=0;
-        requests="GET /R1?read=1&ISE=true HTTP/1.1\n"+
-        "Host: localhost\n"+
-        "Transfer-Encoding: chunked\n"+
-        "Content-Type: text/plain; charset=utf-8\n"+
-        "\015\012"+
-        "5;\015\012"+
-        "12345\015\012"+
-        "5;\015\012"+
-        "67890\015\012"+
-        "0;\015\012\015\012"+
-        "GET /R2 HTTP/1.1\n"+
-        "Host: localhost\n"+
-        "Content-Type: text/plain; charset=utf-8\n"+
-        "Content-Length: 10\n"+
-        "\n"+
-        "abcdefghij\n";
+        String requests="GET /R1?read=1&ISE=true HTTP/1.1\r\n"+
+        "Host: localhost\r\n"+
+        "Transfer-Encoding: chunked\r\n"+
+        "Content-Type: text/plain; charset=utf-8\r\n"+
+        "\r\n"+
+        "5;\r\n"+
+        "12345\r\n"+
+        "5;\r\n"+
+        "67890\r\n"+
+        "0;\r\n" +
+        "\r\n"+
+        "GET /R2 HTTP/1.1\r\n"+
+        "Host: localhost\r\n"+
+        "Content-Type: text/plain; charset=utf-8\r\n"+
+        "Content-Length: 10\r\n"+
+        "\r\n"+
+        "abcdefghij\r\n";
 
         Logger logger = Log.getLogger(HttpChannel.class);
-        try
+        try (StacklessLogging stackless = new StacklessLogging(logger))
         {
             logger.info("EXPECTING: java.lang.IllegalStateException...");
-            ((StdErrLog)logger).setHideStacks(true);
-            response=connector.getResponses(requests);
+            String response = connector.getResponses(requests);
             offset = checkContains(response,offset,"HTTP/1.1 500");
             offset = checkContains(response,offset,"Connection: close");
             checkNotContained(response,offset,"HTTP/1.1 200");
         }
-        finally
-        {
-            ((StdErrLog)logger).setHideStacks(false);
-        }
     }
 
     @Test
@@ -685,17 +783,16 @@
         try
         {
             int offset=0;
-
-            offset=0;
-            response=connector.getResponses("GET /R1 HTTP/1.1\n"+
-                                           "Host: localhost\n"+
-                                           "Connection: TE, close\n"+
-                                           "Transfer-Encoding: chunked\n"+
-                                           "Content-Type: text/plain; charset=utf-8\n"+
-                                           "\015\012"+
-                                           "5;\015\012"+
-                                           "12345\015\012"+
-                                           "0;\015\012\015\012");
+            response=connector.getResponse("GET /R1 HTTP/1.1\r\n"+
+                                           "Host: localhost\r\n"+
+                                           "Connection: TE, close\r\n"+
+                                           "Transfer-Encoding: chunked\r\n"+
+                                           "Content-Type: text/plain; charset=utf-8\r\n"+
+                                           "\r\n"+
+                                           "5;\r\n"+
+                                           "12345\r\n"+
+                                           "0;\r\n" +
+                                           "\r\n");
             checkContains(response,offset,"Connection: close");
         }
         catch (Exception e)
@@ -708,6 +805,7 @@
 
     /**
      * Creates a request header over 1k in size, by creating a single header entry with an huge value.
+     * @throws Exception if test failure
      */
     @Test
     public void testOversizedBuffer() throws Exception
@@ -719,12 +817,12 @@
             String cookie = "thisisastringthatshouldreachover1kbytes";
             for (int i=0;i<100;i++)
                 cookie+="xxxxxxxxxxxx";
-            response = connector.getResponses("GET / HTTP/1.1\n"+
-                "Host: localhost\n" +
-                "Cookie: "+cookie+"\n"+
-                "\015\012"
+            response = connector.getResponses("GET / HTTP/1.1\r\n"+
+                "Host: localhost\r\n" +
+                "Cookie: "+cookie+"\r\n"+
+                "\r\n"
              );
-            checkContains(response, offset, "HTTP/1.1 413");
+            checkContains(response, offset, "HTTP/1.1 431");
         }
         catch(Exception e)
         {
@@ -736,25 +834,25 @@
 
     /**
      * Creates a request header with over 1000 entries.
+     * @throws Exception if test failure
      */
     @Test
     public void testExcessiveHeader() throws Exception
     {
-        String response = null;
         int offset = 0;
 
         StringBuilder request = new StringBuilder();
-        request.append("GET / HTTP/1.1\n");
-        request.append("Host: localhost\n");
-        request.append("Cookie: thisisastring\n");
+        request.append("GET / HTTP/1.1\r\n");
+        request.append("Host: localhost\r\n");
+        request.append("Cookie: thisisastring\r\n");
         for(int i=0; i<1000; i++) {
-            request.append(String.format("X-Header-%04d: %08x\n", i, i));
+            request.append(String.format("X-Header-%04d: %08x\r\n", i, i));
         }
-        request.append("\015\012");
+        request.append("\r\n");
 
-        response = connector.getResponses(request.toString());
-        offset = checkContains(response, offset, "HTTP/1.1 413");
-        checkContains(response, offset, "<h1>Bad Message 413</h1><pre>reason: Request Entity Too Large</pre>");
+        String response = connector.getResponses(request.toString());
+        offset = checkContains(response, offset, "HTTP/1.1 431");
+        checkContains(response, offset, "<h1>Bad Message 431</h1>");
     }
 
     @Test
@@ -785,18 +883,16 @@
         });
         server.start();
 
-        try
+        Logger logger = Log.getLogger(HttpChannel.class);
+        try (StacklessLogging stackless = new StacklessLogging(logger))
         {
-            ((StdErrLog)Log.getLogger(HttpChannel.class)).info("Excpect IOException: Response header too large...");
-            ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(true);
-            int offset = 0;
-
-            response = connector.getResponses("GET / HTTP/1.1\n"+
-                "Host: localhost\n" +
-                "\015\012"
+            logger.info("Expect IOException: Response header too large...");
+            response = connector.getResponses("GET / HTTP/1.1\r\n"+
+                "Host: localhost\r\n" +
+                "\r\n"
              );
 
-            checkContains(response, offset, "HTTP/1.1 500");
+            checkContains(response, 0, "HTTP/1.1 500");
             assertTrue(checkError.await(1,TimeUnit.SECONDS));
         }
         catch(Exception e)
@@ -805,57 +901,53 @@
                 System.err.println(response);
             throw e;
         }
-        finally
-        {
-
-            ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false);
-        }
     }
 
     @Test
     public void testAsterisk() throws Exception
     {
         String response = null;
-
-        try
+        try (StacklessLogging stackless = new StacklessLogging(HttpParser.LOG))
         {
-            ((StdErrLog)HttpParser.LOG).setHideStacks(true);
             int offset=0;
 
-            response=connector.getResponses("OPTIONS * HTTP/1.1\n"+
-                                           "Host: localhost\n"+
-                                           "Transfer-Encoding: chunked\n"+
-                                           "Content-Type: text/plain; charset=utf-8\n"+
-                                           "Connection: close\n"+
-                                           "\015\012"+
-                                           "5;\015\012"+
-                                           "12345\015\012"+
-                                           "0;\015\012\015\012");
-            offset = checkContains(response,offset,"HTTP/1.1 200");
+            response=connector.getResponses("OPTIONS * HTTP/1.1\r\n"+
+                                           "Host: localhost\r\n"+
+                                           "Transfer-Encoding: chunked\r\n"+
+                                           "Content-Type: text/plain; charset=utf-8\r\n"+
+                                           "Connection: close\r\n"+
+                                           "\r\n"+
+                                           "5;\r\n"+
+                                           "12345\r\n"+
+                                           "0;\r\n" +
+                                           "\r\n");
+            checkContains(response,offset,"HTTP/1.1 200");
 
             offset=0;
-            response=connector.getResponses("GET * HTTP/1.1\n"+
-                                           "Host: localhost\n"+
-                                           "Transfer-Encoding: chunked\n"+
-                                           "Content-Type: text/plain; charset=utf-8\n"+
-                                           "Connection: close\n"+
-                                           "\015\012"+
-                                           "5;\015\012"+
-                                           "12345\015\012"+
-                                           "0;\015\012\015\012");
-            offset = checkContains(response,offset,"HTTP/1.1 400");
+            response=connector.getResponses("GET * HTTP/1.1\r\n"+
+                                           "Host: localhost\r\n"+
+                                           "Transfer-Encoding: chunked\r\n"+
+                                           "Content-Type: text/plain; charset=utf-8\r\n"+
+                                           "Connection: close\r\n"+
+                                           "\r\n"+
+                                           "5;\r\n"+
+                                           "12345\r\n"+
+                                           "0;\r\n" +
+                                           "\r\n");
+            checkContains(response,offset,"HTTP/1.1 400");
 
             offset=0;
-            response=connector.getResponses("GET ** HTTP/1.1\n"+
-                                           "Host: localhost\n"+
-                                           "Transfer-Encoding: chunked\n"+
-                                           "Content-Type: text/plain; charset=utf-8\n"+
-                                           "Connection: close\n"+
-                                           "\015\012"+
-                                           "5;\015\012"+
-                                           "12345\015\012"+
-                                           "0;\015\012\015\012");
-            offset = checkContains(response,offset,"HTTP/1.1 400 Bad Request");
+            response=connector.getResponses("GET ** HTTP/1.1\r\n"+
+                                           "Host: localhost\r\n"+
+                                           "Transfer-Encoding: chunked\r\n"+
+                                           "Content-Type: text/plain; charset=utf-8\r\n"+
+                                           "Connection: close\r\n"+
+                                           "\r\n"+
+                                           "5;\r\n"+
+                                           "12345\r\n"+
+                                           "0;\r\n" +
+                                           "\r\n");
+            checkContains(response,offset,"HTTP/1.1 400 Bad Request");
         }
         catch (Exception e)
         {
@@ -863,27 +955,20 @@
                 System.err.println(response);
             throw e;
         }
-        finally
-        {
-            ((StdErrLog)HttpParser.LOG).setHideStacks(false);
-        }
-
     }
 
     @Test
     public void testCONNECT() throws Exception
     {
         String response = null;
-
         try
         {
             int offset=0;
 
-            response=connector.getResponses("CONNECT www.webtide.com:8080 HTTP/1.1\n"+
-                                           "Host: myproxy:8888\015\012"+
-                                           "\015\012",200,TimeUnit.MILLISECONDS);
+            response=connector.getResponses("CONNECT www.webtide.com:8080 HTTP/1.1\r\n"+
+                                           "Host: myproxy:8888\r\n"+
+                                           "\r\n",200,TimeUnit.MILLISECONDS);
             checkContains(response,offset,"HTTP/1.1 200");
-
         }
         catch (Exception e)
         {
@@ -891,7 +976,6 @@
                 System.err.println(response);
             throw e;
         }
-
     }
 
     private int checkContains(String s,int offset,String c)
@@ -905,5 +989,3 @@
         Assert.assertThat(s.substring(offset),Matchers.not(Matchers.containsString(c)));
     }
 }
-
-
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpInputTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpInputTest.java
new file mode 100644
index 0000000..ef41eac
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpInputTest.java
@@ -0,0 +1,628 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.EOFException;
+import java.io.IOException;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeoutException;
+
+import javax.servlet.ReadListener;
+
+import org.eclipse.jetty.util.BufferUtil;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class HttpInputTest
+{
+    private final Queue<String> _history = new LinkedBlockingQueue<>();
+    private final Queue<String> _fillAndParseSimulate = new LinkedBlockingQueue<>();
+    private final ReadListener _listener = new ReadListener()
+    {
+        @Override
+        public void onError(Throwable t)
+        {
+            _history.add("onError:" + t);
+        }
+
+        @Override
+        public void onDataAvailable() throws IOException
+        {
+            _history.add("onDataAvailable");
+        }
+
+        @Override
+        public void onAllDataRead() throws IOException
+        {
+            _history.add("onAllDataRead");
+        }
+    };
+    private HttpInput _in;
+
+    public class TContent extends HttpInput.Content
+    {
+        private final String _content;
+
+        public TContent(String content)
+        {
+            super(BufferUtil.toBuffer(content));
+            _content = content;
+        }
+
+        @Override
+        public void succeeded()
+        {
+            _history.add("Content succeeded " + _content);
+            super.succeeded();
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            _history.add("Content failed " + _content);
+            super.failed(x);
+        }
+    }
+
+    @Before
+    public void before()
+    {
+        _in = new HttpInput(new HttpChannelState(new HttpChannel(null, new HttpConfiguration(), null, null)
+        {
+            @Override
+            public void asyncReadFillInterested()
+            {
+                _history.add("asyncReadFillInterested");
+            }
+        })
+        {
+            @Override
+            public void onReadUnready()
+            {
+                _history.add("unready");
+                super.onReadUnready();
+            }
+
+            @Override
+            public boolean onReadPossible()
+            {
+                _history.add("onReadPossible");
+                return super.onReadPossible();
+            }
+
+            @Override
+            public boolean onReadReady()
+            {
+                _history.add("ready");
+                return super.onReadReady();
+            }
+        })
+        {
+            @Override
+            protected void produceContent() throws IOException
+            {
+                _history.add("produceContent " + _fillAndParseSimulate.size());
+
+                for (String s = _fillAndParseSimulate.poll(); s != null; s = _fillAndParseSimulate.poll())
+                {
+                    if ("_EOF_".equals(s))
+                        _in.eof();
+                    else
+                        _in.addContent(new TContent(s));
+                }
+            }
+
+            @Override
+            protected void blockForContent() throws IOException
+            {
+                _history.add("blockForContent");
+                super.blockForContent();
+            }
+        };
+    }
+
+    @After
+    public void after()
+    {
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testEmpty() throws Exception
+    {
+        Assert.assertThat(_in.available(), Matchers.equalTo(0));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testRead() throws Exception
+    {
+        _in.addContent(new TContent("AB"));
+        _in.addContent(new TContent("CD"));
+        _fillAndParseSimulate.offer("EF");
+        _fillAndParseSimulate.offer("GH");
+        Assert.assertThat(_in.available(), Matchers.equalTo(2));
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(0L));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'A'));
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(1L));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'B'));
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(2L));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded AB"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'C'));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'D'));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded CD"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'E'));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'F'));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 2"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded EF"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'G'));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'H'));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded GH"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(8L));
+
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testReRead() throws Exception
+    {
+        _in.addContent(new TContent("AB"));
+        _in.addContent(new TContent("CD"));
+        _fillAndParseSimulate.offer("EF");
+        _fillAndParseSimulate.offer("GH");
+        Assert.assertThat(_in.available(), Matchers.equalTo(2));
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(0L));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'A'));
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(1L));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'B'));
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(2L));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded AB"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'C'));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'D'));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded CD"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'E'));
+
+        _in.prependContent(new HttpInput.Content(BufferUtil.toBuffer("abcde")));
+
+        Assert.assertThat(_in.available(), Matchers.equalTo(5));
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(0L));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'a'));
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(1L));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'b'));
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(2L));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'c'));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'d'));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'e'));
+
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'F'));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 2"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded EF"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'G'));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'H'));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded GH"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(8L));
+
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testBlockingRead() throws Exception
+    {
+        new Thread()
+        {
+            public void run()
+            {
+                try
+                {
+                    Thread.sleep(500);
+                    _in.addContent(new TContent("AB"));
+                }
+                catch (Throwable th)
+                {
+                    th.printStackTrace();
+                }
+            }
+        }.start();
+
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'A'));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("blockForContent"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'B'));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded AB"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testReadEOF() throws Exception
+    {
+        _in.addContent(new TContent("AB"));
+        _in.addContent(new TContent("CD"));
+        _in.eof();
+
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+        Assert.assertThat(_in.available(), Matchers.equalTo(2));
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'A'));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'B'));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded AB"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'C'));
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'D'));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded CD"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+
+        Assert.assertThat(_in.read(), Matchers.equalTo(-1));
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(true));
+
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testReadEarlyEOF() throws Exception
+    {
+        _in.addContent(new TContent("AB"));
+        _in.addContent(new TContent("CD"));
+        _in.earlyEOF();
+
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+        Assert.assertThat(_in.available(), Matchers.equalTo(2));
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'A'));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'B'));
+
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'C'));
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'D'));
+
+        try
+        {
+            _in.read();
+            Assert.fail();
+        }
+        catch (EOFException eof)
+        {
+            Assert.assertThat(_in.isFinished(), Matchers.equalTo(true));
+        }
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded AB"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded CD"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testBlockingEOF() throws Exception
+    {
+        new Thread()
+        {
+            public void run()
+            {
+                try
+                {
+                    Thread.sleep(500);
+                    _in.eof();
+                }
+                catch (Throwable th)
+                {
+                    th.printStackTrace();
+                }
+            }
+        }.start();
+
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+        Assert.assertThat(_in.read(), Matchers.equalTo(-1));
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(true));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("blockForContent"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testAsyncEmpty() throws Exception
+    {
+        _in.setReadListener(_listener);
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("unready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        _in.run();
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onDataAvailable"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(false));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("unready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(false));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("unready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testAsyncRead() throws Exception
+    {
+        _in.setReadListener(_listener);
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("unready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        _in.run();
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onDataAvailable"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(false));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("unready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        _in.addContent(new TContent("AB"));
+        _fillAndParseSimulate.add("CD");
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onReadPossible"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+        _in.run();
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onDataAvailable"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'A'));
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'B'));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded AB"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 1"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onReadPossible"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'C'));
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'D'));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded CD"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(false));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("unready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testAsyncEOF() throws Exception
+    {
+        _in.setReadListener(_listener);
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("unready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        _in.run();
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onDataAvailable"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        _in.eof();
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onReadPossible"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.read(), Matchers.equalTo(-1));
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(true));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("ready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testAsyncReadEOF() throws Exception
+    {
+        _in.setReadListener(_listener);
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("unready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        _in.run();
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onDataAvailable"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(false));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("unready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        _in.addContent(new TContent("AB"));
+        _fillAndParseSimulate.add("_EOF_");
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onReadPossible"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        _in.run();
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onDataAvailable"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'A'));
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'B'));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded AB"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 1"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onReadPossible"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(false));
+        Assert.assertThat(_in.read(), Matchers.equalTo(-1));
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(true));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("ready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testAsyncError() throws Exception
+    {
+        _in.setReadListener(_listener);
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("unready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+        _in.run();
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onDataAvailable"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(false));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("unready"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        _in.failed(new TimeoutException());
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onReadPossible"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        _in.run();
+        Assert.assertThat(_in.isFinished(), Matchers.equalTo(true));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("onError:java.util.concurrent.TimeoutException"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+
+        Assert.assertThat(_in.isReady(), Matchers.equalTo(true));
+        try
+        {
+            _in.read();
+            Assert.fail();
+        }
+        catch (IOException e)
+        {
+            Assert.assertThat(e.getCause(), Matchers.instanceOf(TimeoutException.class));
+            Assert.assertThat(_in.isFinished(), Matchers.equalTo(true));
+        }
+
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testRecycle() throws Exception
+    {
+        testAsyncRead();
+        _in.recycle();
+        testAsyncRead();
+        _in.recycle();
+        testReadEOF();
+    }
+
+    @Test
+    public void testConsumeAll() throws Exception
+    {
+        _in.addContent(new TContent("AB"));
+        _in.addContent(new TContent("CD"));
+        _fillAndParseSimulate.offer("EF");
+        _fillAndParseSimulate.offer("GH");
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'A'));
+
+        Assert.assertFalse(_in.consumeAll());
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(8L));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded AB"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded CD"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 2"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded EF"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded GH"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 0"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+
+    @Test
+    public void testConsumeAllEOF() throws Exception
+    {
+        _in.addContent(new TContent("AB"));
+        _in.addContent(new TContent("CD"));
+        _fillAndParseSimulate.offer("EF");
+        _fillAndParseSimulate.offer("GH");
+        _fillAndParseSimulate.offer("_EOF_");
+        Assert.assertThat(_in.read(), Matchers.equalTo((int)'A'));
+
+        Assert.assertTrue(_in.consumeAll());
+        Assert.assertThat(_in.getContentConsumed(), Matchers.equalTo(8L));
+
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded AB"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded CD"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("produceContent 3"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded EF"));
+        Assert.assertThat(_history.poll(), Matchers.equalTo("Content succeeded GH"));
+        Assert.assertThat(_history.poll(), Matchers.nullValue());
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitBadBehaviourTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitBadBehaviourTest.java
index dab1733..7cafee1 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitBadBehaviourTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitBadBehaviourTest.java
@@ -30,12 +30,13 @@
 import java.util.concurrent.TimeoutException;
 
 import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -70,9 +71,9 @@
         server.setHandler(new SetHandledWriteSomeDataHandler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 500", response.getCode(), is("500"));
+        assertThat("response code is 500", response.getStatus(), is(500));
     }
 
     private class SetHandledWriteSomeDataHandler extends ThrowExceptionOnDemandHandler
@@ -86,6 +87,13 @@
         public void handle(String target, Request baseRequest, final HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
         {
             final CyclicBarrier resumeBarrier = new CyclicBarrier(1);
+            
+            if (baseRequest.getDispatcherType()==DispatcherType.ERROR)
+            {
+                response.sendError(500);
+                return;
+            }
+            
             if (request.getAttribute(CONTEXT_ATTRIBUTE) == null)
             {
                 final AsyncContext asyncContext = baseRequest.startAsync();
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitTest.java
index 540b25e..0d5b775 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitTest.java
@@ -32,8 +32,8 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -46,7 +46,7 @@
     private final String CONTEXT_ATTRIBUTE = getClass().getName() + ".asyncContext";
     private boolean dispatch; // if true we dispatch, otherwise we complete
 
-    @Parameterized.Parameters
+    @Parameterized.Parameters(name = "{0} dispatch={1}")
     public static Collection<Object[]> data()
     {
         Object[][] data = new Object[][]
@@ -73,9 +73,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 404", response.getCode(), is("404"));
+        assertThat("response code", response.getStatus(), is(404));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
 
@@ -86,9 +86,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 500", response.getCode(), is("500"));
+        assertThat("response code", response.getStatus(), is(500));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
 
@@ -129,9 +129,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertHeader(response, "content-length", "0");
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
@@ -143,9 +143,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 500", response.getCode(), is("500"));
+        assertThat("response code", response.getStatus(), is(500));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
 
@@ -187,9 +187,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertHeader(response, "content-length", "6");
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
@@ -201,9 +201,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 500", response.getCode(), is("500"));
+        assertThat("response code", response.getStatus(), is(500));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
 
@@ -253,10 +253,10 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
@@ -269,9 +269,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
@@ -325,9 +325,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
@@ -340,9 +340,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
@@ -394,9 +394,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked"); // HTTP/1.0 does not do chunked.  it will just send content and close
@@ -409,9 +409,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked"); 
@@ -466,9 +466,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertResponseBody(response, "foobar");
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
@@ -482,10 +482,10 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         // Buffer size is too small, so the content is written directly producing a 200 response
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertResponseBody(response, "foobar");
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
@@ -540,10 +540,10 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
-        assertThat("response body is foo", response.getBody(), is("foo"));
+        assertThat("response code", response.getStatus(), is(200));
+        assertThat("response body", response.getContent(), is("foo"));
         assertHeader(response, "content-length", "3");
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
@@ -555,11 +555,11 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         //TODO: should we expect 500 here?
-        assertThat("response code is 200", response.getCode(), is("200"));
-        assertThat("response body is foo", response.getBody(), is("foo"));
+        assertThat("response code", response.getStatus(), is(200));
+        assertThat("response body", response.getContent(), is("foo"));
         assertHeader(response, "content-length", "3");
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
@@ -612,11 +612,11 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         // jetty truncates the body when content-length is reached.! This is correct and desired behaviour?
-        assertThat("response body is foo", response.getBody(), is("foo"));
+        assertThat("response body", response.getContent(), is("foo"));
         assertHeader(response, "content-length", "3");
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
@@ -628,11 +628,11 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         // TODO: we throw before response is committed. should we expect 500?
-        assertThat("response code is 200", response.getCode(), is("200"));
-        assertThat("response body is foo", response.getBody(), is("foo"));
+        assertThat("response code", response.getStatus(), is(200));
+        assertThat("response body", response.getContent(), is("foo"));
         assertHeader(response, "content-length", "3");
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
@@ -685,9 +685,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
         //TODO: jetty ignores setContentLength and sends transfer-encoding header. Correct?
     }
@@ -699,9 +699,9 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
 
@@ -753,10 +753,10 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         // Setting a content-length too small throws an IllegalStateException
-        assertThat("response code is 500", response.getCode(), is("500"));
+        assertThat("response code", response.getStatus(), is(500));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
 
@@ -767,10 +767,10 @@
         server.setHandler(handler);
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         // Setting a content-length too small throws an IllegalStateException
-        assertThat(response.getCode(), is("500"));
+        assertThat("response code", response.getStatus(), is(500));
         assertThat("no exceptions", handler.failure(), is(nullValue()));
     }
 
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToCommitTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToCommitTest.java
index c2b497b..2fb52dc 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToCommitTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToCommitTest.java
@@ -21,6 +21,7 @@
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.util.Arrays;
@@ -30,18 +31,20 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 //TODO: reset buffer tests
 //TODO: add protocol specific tests for connection: close and/or chunking
-@RunWith(value = Parameterized.class)
+@RunWith(Parameterized.class)
 public class HttpManyWaysToCommitTest extends AbstractHttpTest
 {
-    @Parameterized.Parameters
+    @Parameterized.Parameters(name = "{0}")
     public static Collection<Object[]> data()
     {
         Object[][] data = new Object[][]{{HttpVersion.HTTP_1_0.asString()}, {HttpVersion.HTTP_1_1.asString()}};
@@ -58,10 +61,10 @@
     {
         server.setHandler(new DoesNotSetHandledHandler(false));
         server.start();
+    
+        HttpTester.Response response = executeRequest();
 
-        SimpleHttpResponse response = executeRequest();
-
-        assertThat("response code is 404", response.getCode(), is("404"));
+        assertThat("response code", response.getStatus(), is(404));
     }
 
     @Test
@@ -69,10 +72,10 @@
     {
         server.setHandler(new DoesNotSetHandledHandler(true));
         server.start();
+    
+        HttpTester.Response response = executeRequest();
 
-        SimpleHttpResponse response = executeRequest();
-
-        assertThat("response code is 500", response.getCode(), is("500"));
+        assertThat("response code", response.getStatus(), is(500));
     }
 
     private class DoesNotSetHandledHandler extends ThrowExceptionOnDemandHandler
@@ -96,9 +99,9 @@
         server.setHandler(new OnlySetHandledHandler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertHeader(response, "content-length", "0");
     }
 
@@ -108,9 +111,9 @@
         server.setHandler(new OnlySetHandledHandler(true));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 500", response.getCode(), is("500"));
+        assertThat("response code", response.getStatus(), is(500));
     }
 
     private class OnlySetHandledHandler extends ThrowExceptionOnDemandHandler
@@ -134,9 +137,9 @@
         server.setHandler(new SetHandledWriteSomeDataHandler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertResponseBody(response, "foobar");
         assertHeader(response, "content-length", "6");
     }
@@ -147,10 +150,10 @@
         server.setHandler(new SetHandledWriteSomeDataHandler(true));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 500", response.getCode(), is("500"));
-        assertThat("response body is not foobar", response.getBody(), not(is("foobar")));
+        assertThat("response code", response.getStatus(), is(500));
+        assertThat("response body", response.getContent(), not(is("foobar")));
     }
 
     private class SetHandledWriteSomeDataHandler extends ThrowExceptionOnDemandHandler
@@ -175,9 +178,9 @@
         server.setHandler(new ExplicitFlushHandler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertResponseBody(response, "foobar");
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
@@ -189,11 +192,11 @@
         server.setHandler(new ExplicitFlushHandler(true));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         // Since the 200 was committed, the 500 did not get the chance to be written
-        assertThat("response code is 200", response.getCode(), is("200"));
-        assertThat("response body is foobar", response.getBody(), is("foobar"));
+        assertThat("response code", response.getStatus(), is(200));
+        assertThat("response body", response.getContent(), is("foobar"));
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
     }
@@ -221,9 +224,9 @@
         server.setHandler(new SetHandledAndFlushWithoutContentHandler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
     }
@@ -234,9 +237,9 @@
         server.setHandler(new SetHandledAndFlushWithoutContentHandler(true));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
     }
@@ -263,9 +266,9 @@
         server.setHandler(new WriteFlushWriteMoreHandler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertResponseBody(response, "foobar");
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
@@ -277,11 +280,10 @@
         server.setHandler(new WriteFlushWriteMoreHandler(true));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         // Since the 200 was committed, the 500 did not get the chance to be written
-        assertThat("response code is 200", response.getCode(), is("200"));
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
     }
@@ -310,9 +312,9 @@
         server.setHandler(new OverflowHandler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertResponseBody(response, "foobar");
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
@@ -324,9 +326,9 @@
         server.setHandler(new Overflow2Handler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertResponseBody(response, "foobarfoobar");
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
@@ -338,9 +340,9 @@
         server.setHandler(new Overflow3Handler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertResponseBody(response, "foobarfoobar");
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
@@ -352,10 +354,10 @@
         server.setHandler(new OverflowHandler(true));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         // Response was committed when we throw, so 200 expected
-        assertThat("response code is 200", response.getCode(), is("200"));
+        assertThat("response code", response.getStatus(), is(200));
         assertResponseBody(response, "foobar");
         if (!"HTTP/1.0".equals(httpVersion))
             assertHeader(response, "transfer-encoding", "chunked");
@@ -419,15 +421,43 @@
     }
 
     @Test
+    @Ignore("Not implemented in Jetty 9.3 (Proper support present in Jetty 9.4+)")
+    public void testSetContentLengthFlushAndWriteInsufficientBytes() throws Exception
+    {
+        server.setHandler(new SetContentLengthAndWriteInsufficientBytesHandler(true));
+        server.start();
+        
+        HttpTester.Response response = executeRequest();
+        System.out.println(response.toString());
+        assertThat("response code", response.getStatus(), is(200));
+        assertHeader(response, "content-length", "6");
+        byte content[] = response.getContentBytes();
+        assertThat("content bytes", content.length, is(0));
+        assertTrue("response eof", response.isEarlyEOF());
+    }
+
+    @Test
+    @Ignore("Not implemented in Jetty 9.3 (Proper support present in Jetty 9.4+)")
+    public void testSetContentLengthAndWriteInsufficientBytes() throws Exception
+    {
+        server.setHandler(new SetContentLengthAndWriteInsufficientBytesHandler(false));
+        server.start();
+
+        HttpTester.Response response = executeRequest();
+        assertThat("response has no status", response.getStatus(), is(0));
+        assertTrue("response eof", response.isEarlyEOF());
+    }
+    
+    @Test
     public void testSetContentLengthAndWriteExactlyThatAmountOfBytes() throws Exception
     {
         server.setHandler(new SetContentLengthAndWriteThatAmountOfBytesHandler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
-        assertThat("response body is foo", response.getBody(), is("foo"));
+        assertThat("response code", response.getStatus(), is(200));
+        assertThat("response body", response.getContent(), is("foo"));
         assertHeader(response, "content-length", "3");
     }
 
@@ -437,13 +467,32 @@
         server.setHandler(new SetContentLengthAndWriteThatAmountOfBytesHandler(true));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         // Setting the content-length and then writing the bytes commits the response
-        assertThat("response code is 200", response.getCode(), is("200"));
-        assertThat("response body is foo", response.getBody(), is("foo"));
+        assertThat("response code", response.getStatus(), is(200));
+        assertThat("response body", response.getContent(), is("foo"));
     }
-
+    
+    private class SetContentLengthAndWriteInsufficientBytesHandler extends AbstractHandler
+    {
+        boolean flush;
+        private SetContentLengthAndWriteInsufficientBytesHandler(boolean flush)
+        {
+            this.flush = flush;
+        }
+        
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            response.setContentLength(6);
+            if (flush)
+                response.flushBuffer();
+            response.getWriter().write("foo");
+        }
+    }
+    
     private class SetContentLengthAndWriteThatAmountOfBytesHandler extends ThrowExceptionOnDemandHandler
     {
         private SetContentLengthAndWriteThatAmountOfBytesHandler(boolean throwException)
@@ -467,10 +516,10 @@
         server.setHandler(new SetContentLengthAndWriteMoreBytesHandler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
-        assertThat("response body is foo", response.getBody(), is("foo"));
+        assertThat("response code", response.getStatus(), is(200));
+        assertThat("response body", response.getContent(), is("foo"));
         assertHeader(response, "content-length", "3");
     }
 
@@ -480,11 +529,11 @@
         server.setHandler(new SetContentLengthAndWriteMoreBytesHandler(true));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         // Setting the content-length and then writing the bytes commits the response
-        assertThat("response code is 200", response.getCode(), is("200"));
-        assertThat("response body is foo", response.getBody(), is("foo"));
+        assertThat("response code", response.getStatus(), is(200));
+        assertThat("response body", response.getContent(), is("foo"));
     }
 
     private class SetContentLengthAndWriteMoreBytesHandler extends ThrowExceptionOnDemandHandler
@@ -511,10 +560,10 @@
         server.setHandler(new WriteAndSetContentLengthHandler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
-        assertThat("response code is 200", response.getCode(), is("200"));
-        assertThat("response body is foo", response.getBody(), is("foo"));
+        assertThat("response code", response.getStatus(), is(200));
+        assertThat("response body", response.getContent(), is("foo"));
         assertHeader(response, "content-length", "3");
     }
 
@@ -524,11 +573,11 @@
         server.setHandler(new WriteAndSetContentLengthHandler(true));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         // Writing the bytes and then setting the content-length commits the response
-        assertThat("response code is 200", response.getCode(), is("200"));
-        assertThat("response body is foo", response.getBody(), is("foo"));
+        assertThat("response code", response.getStatus(), is(200));
+        assertThat("response body", response.getContent(), is("foo"));
     }
 
     private class WriteAndSetContentLengthHandler extends ThrowExceptionOnDemandHandler
@@ -554,11 +603,11 @@
         server.setHandler(new WriteAndSetContentLengthTooSmallHandler(false));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         // Setting a content-length too small throws an IllegalStateException
-        assertThat("response code is 500", response.getCode(), is("500"));
-        assertThat("response body is not foo", response.getBody(), not(is("foo")));
+        assertThat("response code", response.getStatus(), is(500));
+        assertThat("response body", response.getContent(), not(is("foo")));
     }
 
     @Test
@@ -567,11 +616,11 @@
         server.setHandler(new WriteAndSetContentLengthTooSmallHandler(true));
         server.start();
 
-        SimpleHttpResponse response = executeRequest();
+        HttpTester.Response response = executeRequest();
 
         // Setting a content-length too small throws an IllegalStateException
-        assertThat("response code is 500", response.getCode(), is("500"));
-        assertThat("response body is not foo", response.getBody(), not(is("foo")));
+        assertThat("response code", response.getStatus(), is(500));
+        assertThat("response body", response.getContent(), not(is("foo")));
     }
 
     private class WriteAndSetContentLengthTooSmallHandler extends ThrowExceptionOnDemandHandler
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java
index 8e077ef..779281a 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.server;
 
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.endsWith;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
@@ -36,8 +37,11 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.server.HttpOutput.Interceptor;
 import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.handler.HotSwapHandler;
 import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.resource.Resource;
 import org.hamcrest.Matchers;
 import org.junit.After;
@@ -53,6 +57,7 @@
     private Server _server;
     private LocalConnector _connector;
     private ContentHandler _handler;
+    private HotSwapHandler _swap;
 
     @Before
     public void init() throws Exception
@@ -66,8 +71,10 @@
         
         _connector = new LocalConnector(_server,http,null);
         _server.addConnector(_connector);
+        _swap=new HotSwapHandler();
         _handler=new ContentHandler();
-        _server.setHandler(_handler);
+        _swap.setHandler(_handler);
+        _server.setHandler(_swap);
         _server.start();
     }
 
@@ -133,6 +140,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     @Test
@@ -156,6 +164,7 @@
         
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,containsString("Transfer-Encoding: chunked"));
+        assertThat(response, containsString("1\tThis is a big file"));
         assertThat(response,containsString("400\tThis is a big file"));
         assertThat(response,containsString("\r\n0\r\n"));
     }
@@ -178,7 +187,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     @Test
@@ -189,7 +198,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,containsString("Content-Length"));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     @Test
@@ -200,7 +209,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,containsString("Content-Length"));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     
@@ -248,6 +257,8 @@
         
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,containsString("Transfer-Encoding: chunked"));
+        assertThat(response, containsString("1\tThis is a big file"));
+        assertThat(response, containsString("400\tThis is a big file"));
         assertThat(response,containsString("\r\n0\r\n"));
     }
 
@@ -262,7 +273,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
 
     @Test
@@ -276,7 +287,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     @Test
@@ -290,7 +301,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     @Test
@@ -304,7 +315,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
 
     @Test
@@ -318,7 +329,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,containsString("Content-Length"));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
 
     @Test
@@ -332,7 +343,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,containsString("Content-Length"));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     @Test
@@ -346,7 +357,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,containsString("Content-Length"));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
 
     @Test
@@ -360,7 +371,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,containsString("Content-Length"));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
 
     
@@ -391,7 +402,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
 
     @Test
@@ -405,7 +416,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     @Test
@@ -419,7 +430,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
 
 
@@ -435,7 +446,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     @Test
@@ -450,7 +461,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     @Test
@@ -465,7 +476,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     @Test
@@ -480,7 +491,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
 
     
@@ -514,7 +525,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
 
     @Test
@@ -529,7 +540,7 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     @Test
@@ -544,7 +555,23 @@
         String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,containsString("400\tThis is a big file"));
+        assertThat(response, endsWith(toUTF8String(big)));
+    }
+
+    @Test
+    public void testAsyncWriteBufferLargeDirect()
+            throws Exception
+    {
+        final Resource big = Resource.newClassPathResource("simple/big.txt");
+        _handler._writeLengthIfKnown = false;
+        _handler._content = BufferUtil.toBuffer(big, true);
+        _handler._byteBuffer = BufferUtil.allocateDirect(8192);
+        _handler._async = true;
+
+        String response = _connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
+        assertThat(response, containsString("HTTP/1.1 200 OK"));
+        assertThat(response, Matchers.not(containsString("Content-Length")));
+        assertThat(response, endsWith(toUTF8String(big)));
     }
     
     @Test
@@ -561,7 +588,8 @@
         assertThat(_handler._owp.get()-start,Matchers.greaterThan(0));
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,Matchers.not(containsString("Content-Length")));
-        assertThat(response,Matchers.not(containsString("400\tThis is a big file")));
+        assertThat(response, Matchers.not(containsString("1\tThis is a big file")));
+        assertThat(response, Matchers.not(containsString("400\tThis is a big file")));
     }
 
     @Test
@@ -598,6 +626,61 @@
         assertThat(response,Matchers.not(containsString("simple text")));
     }
     
+
+    @Test
+    public void testWriteInterception() throws Exception
+    {
+        final Resource big = Resource.newClassPathResource("simple/big.txt");
+        _handler._writeLengthIfKnown=false;
+        _handler._content=BufferUtil.toBuffer(big,false);
+        _handler._arrayBuffer=new byte[1024];
+        _handler._interceptor = new ChainedInterceptor()
+        {
+            Interceptor _next;
+            @Override
+            public void write(ByteBuffer content, boolean complete, Callback callback)
+            {
+                String s = BufferUtil.toString(content).toUpperCase().replaceAll("BIG","BIGGER");
+                _next.write(BufferUtil.toBuffer(s),complete,callback);
+            }
+            
+            @Override
+            public boolean isOptimizedForDirectBuffers()
+            {
+                return _next.isOptimizedForDirectBuffers();
+            }
+            
+            @Override
+            public Interceptor getNextInterceptor()
+            {
+                return _next;
+            }
+            
+            @Override
+            public void setNext(Interceptor interceptor)
+            {
+                _next=interceptor;
+            }
+        };
+           
+        String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,Matchers.not(containsString("Content-Length")));
+        assertThat(response,containsString("400\tTHIS IS A BIGGER FILE"));
+    }
+
+    private static String toUTF8String(Resource resource)
+            throws IOException
+    {
+        return BufferUtil.toUTF8String(BufferUtil.toBuffer(resource, false));
+    }
+    
+    interface ChainedInterceptor extends HttpOutput.Interceptor 
+    {
+        default void init(Request baseRequest) {};
+        void setNext(Interceptor interceptor);
+    }
+    
     static class ContentHandler extends AbstractHandler
     {
         AtomicInteger _owp = new AtomicInteger();
@@ -608,11 +691,19 @@
         InputStream _contentInputStream;
         ReadableByteChannel _contentChannel;
         ByteBuffer _content;
+        ChainedInterceptor _interceptor;
         
         @Override
         public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
         {
             baseRequest.setHandled(true);
+            if (_interceptor!=null)
+            {
+                _interceptor.init(baseRequest);
+                _interceptor.setNext(baseRequest.getResponse().getHttpOutput().getInterceptor());
+                baseRequest.getResponse().getHttpOutput().setInterceptor(_interceptor);
+            }
+            
             response.setContentType("text/plain");
             
             final HttpOutput out = (HttpOutput) response.getOutputStream();
@@ -701,6 +792,8 @@
                     final AsyncContext async = request.startAsync();
                     out.setWriteListener(new WriteListener()
                     {
+                        private boolean isFirstWrite = true;
+
                         @Override
                         public void onWritePossible() throws IOException
                         {
@@ -708,6 +801,7 @@
                             
                             while (out.isReady())
                             {
+                                Assert.assertTrue(isFirstWrite || !_byteBuffer.hasRemaining());
                                 Assert.assertTrue(out.isReady());
                                 if(BufferUtil.isEmpty(_content))
                                 {
@@ -719,6 +813,7 @@
                                 BufferUtil.put(_content,_byteBuffer);
                                 BufferUtil.flipToFlush(_byteBuffer,0);
                                 out.write(_byteBuffer);
+                                isFirstWrite = false;
                             }
                         }
 
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
index 6179c08..80b3b45 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
@@ -18,8 +18,19 @@
 
 package org.eclipse.jetty.server;
 
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -31,36 +42,32 @@
 import java.util.Arrays;
 import java.util.concurrent.Exchanger;
 import java.util.concurrent.atomic.AtomicBoolean;
+
 import javax.servlet.ServletException;
 import javax.servlet.ServletInputStream;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.io.EofException;
 import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.AdvancedRunner;
 import org.eclipse.jetty.toolchain.test.annotation.Slow;
 import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.AbstractLogger;
 import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Test;
-
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.greaterThanOrEqualTo;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.startsWith;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
+import org.junit.runner.RunWith;
 
 /**
  *
  */
+@RunWith(AdvancedRunner.class)
 public abstract class HttpServerTestBase extends HttpServerTestFixture
 {
     private static final String REQUEST1_HEADER = "POST / HTTP/1.0\n" + "Host: localhost\n" + "Content-Type: text/xml; charset=utf-8\n" + "Connection: close\n" + "Content-Length: ";
@@ -105,7 +112,7 @@
                     + "</nimbus>\n";
     protected static final String RESPONSE2 =
             "HTTP/1.1 200 OK\n" +
-                    "Content-Type: text/xml; charset=ISO-8859-1\n" +
+                    "Content-Type: text/xml;charset=iso-8859-1\n" +
                     "Content-Length: " + RESPONSE2_CONTENT.getBytes().length + "\n" +
                     "Server: Jetty(" + Server.getVersion() + ")\n" +
                     "\n" +
@@ -179,11 +186,11 @@
     {
         configureServer(new HelloWorldHandler());
 
-        try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()))
+        try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
+             StacklessLogging stackless = new StacklessLogging(HttpConnection.class))
         {
             client.setSoTimeout(10000);
-            ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(true);
-            ((StdErrLog) Log.getLogger(HttpConnection.class)).info("expect request is too large, then ISE extra data ...");
+            ((AbstractLogger) Log.getLogger(HttpConnection.class)).info("expect request is too large, then ISE extra data ...");
             OutputStream os = client.getOutputStream();
 
             byte[] buffer = new byte[64 * 1024];
@@ -195,11 +202,7 @@
             // Read the response.
             String response = readResponse(client);
 
-            Assert.assertThat(response, Matchers.containsString("HTTP/1.1 413 "));
-        }
-        finally
-        {
-            ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(true);
+            Assert.assertThat(response, Matchers.containsString("HTTP/1.1 431 "));
         }
     }
 
@@ -211,10 +214,10 @@
     {
         configureServer(new HelloWorldHandler());
 
-        try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()))
+        try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
+             StacklessLogging stackless = new StacklessLogging(HttpConnection.class))
         {
-            ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(true);
-            ((StdErrLog)Log.getLogger(HttpConnection.class)).info("expect URI is too large, then ISE extra data ...");
+            ((AbstractLogger)Log.getLogger(HttpConnection.class)).info("expect URI is too large, then ISE extra data ...");
             OutputStream os = client.getOutputStream();
 
             byte[] buffer = new byte[64 * 1024];
@@ -233,10 +236,6 @@
 
             Assert.assertThat(response, Matchers.containsString("HTTP/1.1 414 "));
         }
-        finally
-        {
-            ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(true);
-        }
     }
 
     @Test
@@ -257,9 +256,8 @@
         Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
         OutputStream os = client.getOutputStream();
 
-        try
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
         { 
-            ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(true);
             Log.getLogger(HttpChannel.class).info("Expecting ServletException: TEST handler exception...");
             os.write(request.toString().getBytes());
             os.flush();
@@ -267,10 +265,6 @@
             String response = readResponse(client);
             assertThat("response code is 500", response.contains("500"), is(true));
         }
-        finally
-        {
-            ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false);
-        }
     }
 
     @Test
@@ -331,10 +325,10 @@
 
         configureServer(new HelloWorldHandler());
 
-        try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()))
+        try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
+             StacklessLogging stackless = new StacklessLogging(HttpConnection.class))
         {
-            ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(true);
-            ((StdErrLog)Log.getLogger(HttpConnection.class)).info("expect header is too large, then ISE extra data ...");
+            Log.getLogger(HttpConnection.class).info("expect header is too large, then ISE extra data ...");
             OutputStream os = client.getOutputStream();
 
             byte[] buffer = new byte[64 * 1024];
@@ -363,11 +357,7 @@
             // Read the response.
             String response = readResponse(client);
 
-            Assert.assertThat(response, Matchers.containsString("HTTP/1.1 413 "));
-        }
-        finally
-        {
-            ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(true);
+            Assert.assertThat(response, Matchers.containsString("HTTP/1.1 431 "));
         }
     }
 
@@ -678,6 +668,54 @@
     }
 
     @Test
+    public void testBlockingReadBadChunk() throws Exception
+    {
+        configureServer(new ReadHandler());
+
+        try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()))
+        {
+            client.setSoTimeout(600000);
+            OutputStream os = client.getOutputStream();
+            InputStream is = client.getInputStream();
+
+            os.write((
+                "GET /data HTTP/1.1\r\n" +
+                "host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
+                "content-type: unknown\r\n" +
+                "transfer-encoding: chunked\r\n" +
+                "\r\n"
+            ).getBytes());
+            os.flush();
+            Thread.sleep(50);
+            os.write((
+                "a\r\n" +
+                "123456890\r\n"
+                ).getBytes());
+            os.flush();
+            
+            Thread.sleep(50);
+            os.write((
+                "4\r\n" +
+                "abcd\r\n"
+                ).getBytes());
+            os.flush();
+            
+            Thread.sleep(50);
+            os.write((
+                "X\r\n" +
+                "abcd\r\n"
+                ).getBytes());
+            os.flush();
+            
+            HttpTester.Response response = HttpTester.parseResponse(HttpTester.from(is));
+            
+            assertThat(response.getStatus(),is(200));
+            assertThat(response.getContent(),containsString("EofException"));
+            assertThat(response.getContent(),containsString("Early EOF"));
+        }
+    }
+    
+    @Test
     public void testBlockingWhileWritingResponseContent() throws Exception
     {
         configureServer(new DataHandler());
@@ -1025,8 +1063,8 @@
             }
 
             String in = new String(b, 0, i, StandardCharsets.UTF_8);
-            assertTrue(in.contains("123456789"));
-            assertTrue(in.contains("abcdefghZ"));
+            assertThat(in,containsString("123456789"));
+            assertThat(in,containsString("abcdefghZ"));
             assertFalse(in.contains("Wibble"));
 
             in = new String(b, i, b.length - i, StandardCharsets.UTF_16);
@@ -1125,9 +1163,9 @@
             os.flush();
 
             String in = IO.toString(is);
-            assertTrue(in.contains("123456789"));
-            assertTrue(in.contains("abcdefghi"));
-            assertTrue(in.contains("Wibble"));
+            assertThat(in,containsString("123456789"));
+            assertThat(in,containsString("abcdefghi"));
+            assertThat(in,containsString("Wibble"));
         }
     }
 
@@ -1172,11 +1210,10 @@
         CommittedErrorHandler handler = new CommittedErrorHandler();
         configureServer(handler);
 
-        Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
-        try
+        try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
+             StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
         {
-            ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(true);
-            ((StdErrLog)Log.getLogger(HttpChannel.class)).info("Expecting exception after commit then could not send 500....");
+            ((AbstractLogger)Log.getLogger(HttpChannel.class)).info("Expecting exception after commit then could not send 500....");
             OutputStream os = client.getOutputStream();
             InputStream is = client.getInputStream();
 
@@ -1193,7 +1230,7 @@
 
             assertEquals(-1, is.read()); // Closed by error!
 
-            assertTrue(in.contains("HTTP/1.1 200 OK"));
+            assertThat(in,containsString("HTTP/1.1 200 OK"));
             assertTrue(in.indexOf("Transfer-Encoding: chunked") > 0);
             assertTrue(in.indexOf("Now is the time for all good men to come to the aid of the party") > 0);
             assertThat(in, Matchers.not(Matchers.containsString("\r\n0\r\n")));
@@ -1203,13 +1240,6 @@
 
             assertTrue(!handler._endp.isOpen());
         }
-        finally
-        {
-            ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false);
-
-            if (!client.isClosed())
-                client.close();
-        }
     }
 
     public static class CommittedErrorHandler extends AbstractHandler
@@ -1423,6 +1453,7 @@
         {
             int point = points[j];
 
+            // System.err.println("write: "+new String(bytes, last, point - last));
             os.write(bytes, last, point - last);
             last = point;
             os.flush();
@@ -1433,6 +1464,7 @@
         }
 
         // Write the last fragment
+        // System.err.println("Write: "+new String(bytes, last, bytes.length - last));
         os.write(bytes, last, bytes.length - last);
         os.flush();
         Thread.sleep(PAUSE);
@@ -1443,9 +1475,7 @@
     {
         configureServer(new NoopHandler());
         final int REQS = 2;
-        char[] fill = new char[65 * 1024];
-        Arrays.fill(fill, '.');
-        String content = "This is a loooooooooooooooooooooooooooooooooo" +
+        final String content = "This is a coooooooooooooooooooooooooooooooooo" +
                 "ooooooooooooooooooooooooooooooooooooooooooooo" +
                 "ooooooooooooooooooooooooooooooooooooooooooooo" +
                 "ooooooooooooooooooooooooooooooooooooooooooooo" +
@@ -1454,9 +1484,8 @@
                 "ooooooooooooooooooooooooooooooooooooooooooooo" +
                 "ooooooooooooooooooooooooooooooooooooooooooooo" +
                 "ooooooooooooooooooooooooooooooooooooooooooooo" +
-                "oooooooooooonnnnnnnnnnnnnnnnggggggggg content" +
-                new String(fill);
-        final byte[] bytes = content.getBytes();
+                "oooooooooooonnnnnnnnnnnnnnnntent";
+        final int cl = content.getBytes().length;
 
         Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
         final OutputStream out = client.getOutputStream();
@@ -1468,12 +1497,15 @@
             {
                 try
                 {
+                    byte[] bytes=(
+                            "GET / HTTP/1.1\r\n"+
+                                    "Host: localhost\r\n"
+                                    +"Content-Length: " + cl + "\r\n" +
+                                    "\r\n"+
+                                    content).getBytes(StandardCharsets.ISO_8859_1);
+                                    
                     for (int i = 0; i < REQS; i++)
-                    {
-                        out.write("GET / HTTP/1.1\r\nHost: localhost\r\n".getBytes(StandardCharsets.ISO_8859_1));
-                        out.write(("Content-Length: " + bytes.length + "\r\n" + "\r\n").getBytes(StandardCharsets.ISO_8859_1));
                         out.write(bytes, 0, bytes.length);
-                    }
                     out.write("GET / HTTP/1.1\r\nHost: last\r\nConnection: close\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1));
                     out.flush();
                 }
@@ -1485,7 +1517,7 @@
         }.start();
 
         String resps = readResponse(client);
-
+                
         int offset = 0;
         for (int i = 0; i < (REQS + 1); i++)
         {
@@ -1495,6 +1527,71 @@
         }
     }
 
+    @Test
+    public void testWriteBodyAfterNoBodyResponse() throws Exception
+    {
+        configureServer(new WriteBodyAfterNoBodyResponseHandler());
+        Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
+        final OutputStream out=client.getOutputStream();
+
+        out.write("GET / HTTP/1.1\r\nHost: test\r\n\r\n".getBytes());
+        out.write("GET / HTTP/1.1\r\nHost: test\r\nConnection: close\r\n\r\n".getBytes());
+        out.flush();
+        
+        
+        BufferedReader in =new BufferedReader(new InputStreamReader(client.getInputStream()));
+
+        String line=in.readLine();
+        assertThat(line, containsString(" 304 "));
+        while (true)
+        {
+                line=in.readLine();
+                if (line==null)
+                        throw new EOFException();
+                if (line.length()==0)
+                        break;
+                
+            assertThat(line, not(containsString("Content-Length")));
+            assertThat(line, not(containsString("Content-Type")));
+            assertThat(line, not(containsString("Transfer-Encoding")));
+        }
+
+        line=in.readLine();
+        assertThat(line, containsString(" 304 "));
+        while (true)
+        {
+                line=in.readLine();
+                if (line==null)
+                        throw new EOFException();
+                if (line.length()==0)
+                        break;
+                
+            assertThat(line, not(containsString("Content-Length")));
+            assertThat(line, not(containsString("Content-Type")));
+            assertThat(line, not(containsString("Transfer-Encoding")));
+        }
+
+        do 
+        {
+                line=in.readLine();
+        }
+        while (line!=null);
+        
+    }
+
+    private class WriteBodyAfterNoBodyResponseHandler extends AbstractHandler
+    {
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            response.setStatus(304);
+            response.getOutputStream().print("yuck");
+            response.flushBuffer();
+        }
+    }
+    
+    
     public class NoopHandler extends AbstractHandler
     {
         @Override
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestFixture.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestFixture.java
index 0922f36..f787222 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestFixture.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestFixture.java
@@ -35,8 +35,10 @@
 
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.server.handler.HandlerWrapper;
+import org.eclipse.jetty.server.handler.HotSwapHandler;
 import org.eclipse.jetty.toolchain.test.PropertyFlag;
 import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.junit.After;
 import org.junit.Before;
 
@@ -45,8 +47,10 @@
     protected static final long PAUSE=10L;
     protected static final int LOOPS= PropertyFlag.isEnabled("test.stress")?250:50;
 
+    protected QueuedThreadPool _threadPool;
     protected Server _server;
     protected URI _serverURI;
+    protected HttpConfiguration _httpConfiguration;
     protected ServerConnector _connector;
     protected String _scheme="http";
 
@@ -62,15 +66,23 @@
     @Before
     public void before()
     {
-        _server = new Server();
+        _threadPool = new QueuedThreadPool();
+        _server = new Server(_threadPool);
     }
 
     protected void startServer(ServerConnector connector) throws Exception
     {
+        startServer(connector,new HotSwapHandler());
+    }
+    
+    protected void startServer(ServerConnector connector, Handler handler) throws Exception
+    {
         _connector = connector;
-        _connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
+        _httpConfiguration=_connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration();
+        _httpConfiguration.setBlockingTimeout(-1);
+        _httpConfiguration.setSendDateHeader(false);
         _server.addConnector(_connector);
-        _server.setHandler(new HandlerWrapper());
+        _server.setHandler(handler);
         _server.start();
         _serverURI = _server.getURI();
     }
@@ -85,10 +97,9 @@
 
     protected void configureServer(Handler handler) throws Exception
     {
-        HandlerWrapper current = (HandlerWrapper)_server.getHandler();
-        current.stop();
-        current.setHandler(handler);
-        current.start();
+        HotSwapHandler swapper = (HotSwapHandler)_server.getHandler();
+        swapper.setHandler(handler);
+        handler.start();
     }
 
 
@@ -175,7 +186,28 @@
             response.getOutputStream().print("Hello world\r\n");
         }
     }
+    
+    protected static class ReadHandler extends AbstractHandler
+    {
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            response.setStatus(200);
 
+            try
+            {
+                InputStream in = request.getInputStream();
+                String input= IO.toString(in);
+                response.getWriter().printf("read %d%n",input.length());
+            }
+            catch(Exception e)
+            {
+                response.getWriter().printf("caught %s%n",e); 
+            }
+        }
+    }
+    
     protected static class DataHandler extends AbstractHandler
     {
         @Override
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java
deleted file mode 100644
index 6ecd67e..0000000
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java
+++ /dev/null
@@ -1,402 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.server;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.net.URLEncoder;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-
-import org.eclipse.jetty.http.HttpURI;
-import org.eclipse.jetty.util.MultiMap;
-import org.eclipse.jetty.util.TypeUtil;
-import org.eclipse.jetty.util.Utf8Appendable;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class HttpURITest
-{
-    private final String[][] partial_tests=
-    {
-       /* 0*/ {"/path/info",null,null,null,null,"/path/info",null,null,null},
-       /* 1*/ {"/path/info#fragment",null,null,null,null,"/path/info",null,null,"fragment"},
-       /* 2*/ {"/path/info?query",null,null,null,null,"/path/info",null,"query",null},
-       /* 3*/ {"/path/info?query#fragment",null,null,null,null,"/path/info",null,"query","fragment"},
-       /* 4*/ {"/path/info;param",null,null,null,null,"/path/info","param",null,null},
-       /* 5*/ {"/path/info;param#fragment",null,null,null,null,"/path/info","param",null,"fragment"},
-       /* 6*/ {"/path/info;param?query",null,null,null,null,"/path/info","param","query",null},
-       /* 7*/ {"/path/info;param?query#fragment",null,null,null,null,"/path/info","param","query","fragment"},
-       /* 8*/ {"//host/path/info",null,"//host","host",null,"/path/info",null,null,null},
-       /* 9*/ {"//user@host/path/info",null,"//user@host","host",null,"/path/info",null,null,null},
-       /*10*/ {"//user@host:8080/path/info",null,"//user@host:8080","host","8080","/path/info",null,null,null},
-       /*11*/ {"//host:8080/path/info",null,"//host:8080","host","8080","/path/info",null,null,null},
-       /*12*/ {"http:/path/info","http",null,null,null,"/path/info",null,null,null},
-       /*13*/ {"http:/path/info#fragment","http",null,null,null,"/path/info",null,null,"fragment"},
-       /*14*/ {"http:/path/info?query","http",null,null,null,"/path/info",null,"query",null},
-       /*15*/ {"http:/path/info?query#fragment","http",null,null,null,"/path/info",null,"query","fragment"},
-       /*16*/ {"http:/path/info;param","http",null,null,null,"/path/info","param",null,null},
-       /*17*/ {"http:/path/info;param#fragment","http",null,null,null,"/path/info","param",null,"fragment"},
-       /*18*/ {"http:/path/info;param?query","http",null,null,null,"/path/info","param","query",null},
-       /*19*/ {"http:/path/info;param?query#fragment","http",null,null,null,"/path/info","param","query","fragment"},
-       /*20*/ {"http://user@host:8080/path/info;param?query#fragment","http","//user@host:8080","host","8080","/path/info","param","query","fragment"},
-       /*21*/ {"xxxxx://user@host:8080/path/info;param?query#fragment","xxxxx","//user@host:8080","host","8080","/path/info","param","query","fragment"},
-       /*22*/ {"http:///;?#","http","//",null,null,"/","","",""},
-       /*23*/ {"/path/info?a=?query",null,null,null,null,"/path/info",null,"a=?query",null},
-       /*24*/ {"/path/info?a=;query",null,null,null,null,"/path/info",null,"a=;query",null},
-       /*25*/ {"//host:8080//",null,"//host:8080","host","8080","//",null,null,null},
-       /*26*/ {"file:///path/info","file","//",null,null,"/path/info",null,null,null},
-       /*27*/ {"//",null,"//",null,null,null,null,null,null},
-       /*28*/ {"/;param",null, null, null,null,"/", "param",null,null},
-       /*29*/ {"/?x=y",null, null, null,null,"/", null,"x=y",null},
-       /*30*/ {"/?abc=test",null, null, null,null,"/", null,"abc=test",null},
-       /*31*/ {"/#fragment",null, null, null,null,"/", null,null,"fragment"},
-       /*32*/ {"http://localhost:8080", "http", "//localhost:8080", "localhost", "8080", null, null, null, null},
-       /*33*/ {"./?foo:bar=:1:1::::",null,null,null,null,"./",null,"foo:bar=:1:1::::",null}
-    };
-
-    @Test
-    public void testPartialURIs() throws Exception
-    {
-        HttpURI uri = new HttpURI(true);
-
-        for (int t=0;t<partial_tests.length;t++)
-        {
-            uri.parse(partial_tests[t][0].getBytes(),0,partial_tests[t][0].length());
-            assertEquals(t+" "+partial_tests[t][0],partial_tests[t][1],uri.getScheme());
-            assertEquals(t+" "+partial_tests[t][0],partial_tests[t][2],uri.getAuthority());
-            assertEquals(t+" "+partial_tests[t][0],partial_tests[t][3],uri.getHost());
-            assertEquals(t+" "+partial_tests[t][0],partial_tests[t][4]==null?-1:Integer.parseInt(partial_tests[t][4]),uri.getPort());
-            assertEquals(t+" "+partial_tests[t][0],partial_tests[t][5],uri.getPath());
-            assertEquals(t+" "+partial_tests[t][0],partial_tests[t][6],uri.getParam());
-            assertEquals(t+" "+partial_tests[t][0],partial_tests[t][7],uri.getQuery());
-            assertEquals(t+" "+partial_tests[t][0],partial_tests[t][8],uri.getFragment());
-            assertEquals(partial_tests[t][0], uri.toString());
-        }
-
-    }
-
-    private final String[][] path_tests=
-    {
-       /* 0*/ {"/path/info",null,null,null,null,"/path/info",null,null,null},
-       /* 1*/ {"/path/info#fragment",null,null,null,null,"/path/info",null,null,"fragment"},
-       /* 2*/ {"/path/info?query",null,null,null,null,"/path/info",null,"query",null},
-       /* 3*/ {"/path/info?query#fragment",null,null,null,null,"/path/info",null,"query","fragment"},
-       /* 4*/ {"/path/info;param",null,null,null,null,"/path/info","param",null,null},
-       /* 5*/ {"/path/info;param#fragment",null,null,null,null,"/path/info","param",null,"fragment"},
-       /* 6*/ {"/path/info;param?query",null,null,null,null,"/path/info","param","query",null},
-       /* 7*/ {"/path/info;param?query#fragment",null,null,null,null,"/path/info","param","query","fragment"},
-       /* 8*/ {"//host/path/info",null,null,null,null,"//host/path/info",null,null,null},
-       /* 9*/ {"//user@host/path/info",null,null,null,null,"//user@host/path/info",null,null,null},
-       /*10*/ {"//user@host:8080/path/info",null,null,null,null,"//user@host:8080/path/info",null,null,null},
-       /*11*/ {"//host:8080/path/info",null,null,null,null,"//host:8080/path/info",null,null,null},
-       /*12*/ {"http:/path/info","http",null,null,null,"/path/info",null,null,null},
-       /*13*/ {"http:/path/info#fragment","http",null,null,null,"/path/info",null,null,"fragment"},
-       /*14*/ {"http:/path/info?query","http",null,null,null,"/path/info",null,"query",null},
-       /*15*/ {"http:/path/info?query#fragment","http",null,null,null,"/path/info",null,"query","fragment"},
-       /*16*/ {"http:/path/info;param","http",null,null,null,"/path/info","param",null,null},
-       /*17*/ {"http:/path/info;param#fragment","http",null,null,null,"/path/info","param",null,"fragment"},
-       /*18*/ {"http:/path/info;param?query","http",null,null,null,"/path/info","param","query",null},
-       /*19*/ {"http:/path/info;param?query#fragment","http",null,null,null,"/path/info","param","query","fragment"},
-       /*20*/ {"http://user@host:8080/path/info;param?query#fragment","http","//user@host:8080","host","8080","/path/info","param","query","fragment"},
-       /*21*/ {"xxxxx://user@host:8080/path/info;param?query#fragment","xxxxx","//user@host:8080","host","8080","/path/info","param","query","fragment"},
-       /*22*/ {"http:///;?#","http","//",null,null,"/","","",""},
-       /*23*/ {"/path/info?a=?query",null,null,null,null,"/path/info",null,"a=?query",null},
-       /*24*/ {"/path/info?a=;query",null,null,null,null,"/path/info",null,"a=;query",null},
-       /*25*/ {"//host:8080//",null,null,null,null,"//host:8080//",null,null,null},
-       /*26*/ {"file:///path/info","file","//",null,null,"/path/info",null,null,null},
-       /*27*/ {"//",null,null,null,null,"//",null,null,null},
-       /*28*/ {"http://localhost/","http","//localhost","localhost",null,"/",null,null,null},
-       /*29*/ {"http://localhost:8080/", "http", "//localhost:8080", "localhost","8080","/", null, null,null},
-       /*30*/ {"http://localhost/?x=y", "http", "//localhost", "localhost",null,"/", null,"x=y",null},
-       /*31*/ {"/;param",null, null, null,null,"/", "param",null,null},
-       /*32*/ {"/?x=y",null, null, null,null,"/", null,"x=y",null},
-       /*33*/ {"/?abc=test",null, null, null,null,"/", null,"abc=test",null},
-       /*34*/ {"/#fragment",null, null, null,null,"/", null,null,"fragment"},
-       /*35*/ {"http://192.0.0.1:8080/","http","//192.0.0.1:8080","192.0.0.1","8080","/",null,null,null},
-       /*36*/ {"http://[2001:db8::1]:8080/","http","//[2001:db8::1]:8080","[2001:db8::1]","8080","/",null,null,null},
-       /*37*/ {"http://user@[2001:db8::1]:8080/","http","//user@[2001:db8::1]:8080","[2001:db8::1]","8080","/",null,null,null},
-       /*38*/ {"http://[2001:db8::1]/","http","//[2001:db8::1]","[2001:db8::1]",null,"/",null,null,null},
-       /*39*/ {"//[2001:db8::1]:8080/",null,null,null,null,"//[2001:db8::1]:8080/",null,null,null},
-       /*40*/ {"http://user@[2001:db8::1]:8080/","http","//user@[2001:db8::1]:8080","[2001:db8::1]","8080","/",null,null,null},
-       /*41*/ {"*",null,null,null,null,"*",null, null,null}
-    };
-
-    @Test
-    public void testPathURIs() throws Exception
-    {
-        HttpURI uri = new HttpURI();
-
-        for (int t=0;t<path_tests.length;t++)
-        {
-            uri.parse(path_tests[t][0].getBytes(),0,path_tests[t][0].length());
-            assertEquals(t+" "+path_tests[t][0],path_tests[t][1],uri.getScheme());
-            assertEquals(t+" "+path_tests[t][0],path_tests[t][2],uri.getAuthority());
-            assertEquals(t+" "+path_tests[t][0],path_tests[t][3],uri.getHost());
-            assertEquals(t+" "+path_tests[t][0],path_tests[t][4]==null?-1:Integer.parseInt(path_tests[t][4]),uri.getPort());
-            assertEquals(t+" "+path_tests[t][0],path_tests[t][5],uri.getPath());
-            assertEquals(t+" "+path_tests[t][0],path_tests[t][6],uri.getParam());
-            assertEquals(t+" "+path_tests[t][0],path_tests[t][7],uri.getQuery());
-            assertEquals(t+" "+path_tests[t][0],path_tests[t][8],uri.getFragment());
-            assertEquals(path_tests[t][0], uri.toString());
-        }
-
-    }
-
-    @Test
-    public void testInvalidAddress() throws Exception
-    {
-        assertInvalidURI("http://[ffff::1:8080/", "Invalid URL; no closing ']' -- should throw exception");
-        assertInvalidURI("**", "only '*', not '**'");
-        assertInvalidURI("*/", "only '*', not '*/'");
-    }
-
-    private void assertInvalidURI(String invalidURI, String message)
-    {
-        HttpURI uri = new HttpURI();
-        try
-        {
-            uri.parse(invalidURI);
-            fail(message);
-        }
-        catch (IllegalArgumentException e)
-        {
-            assertTrue(true);
-        }
-    }
-
-    private final String[][] encoding_tests=
-    {
-       /* 0*/ {"/path/info","/path/info", "UTF-8"},
-       /* 1*/ {"/path/%69nfo","/path/info", "UTF-8"},
-       /* 2*/ {"http://host/path/%69nfo","/path/info", "UTF-8"},
-       /* 3*/ {"http://host/path/%69nf%c2%a4","/path/inf\u00a4", "UTF-8"},
-       /* 4*/ {"http://host/path/%E5", "/path/\u00e5", "ISO-8859-1"},
-       /* 5*/ {"/foo/%u30ED/bar%3Fabc%3D123%26xyz%3D456","/foo/\u30ed/bar?abc=123&xyz=456","UTF-8"}
-    };
-
-    @Test
-    public void testEncoded()
-    {
-        HttpURI uri = new HttpURI();
-
-        for (int t=0;t<encoding_tests.length;t++)
-        {
-            uri.parse(encoding_tests[t][0]);
-            assertEquals(""+t,encoding_tests[t][1],uri.getDecodedPath(encoding_tests[t][2]));
-            
-            if ("UTF-8".equalsIgnoreCase(encoding_tests[t][2]))
-                assertEquals(""+t,encoding_tests[t][1],uri.getDecodedPath());
-        }
-    }
-    
-    @Test
-    public void testNoPercentEncodingOfQueryUsingNonUTF8() throws Exception
-    {
-        byte[] utf8_bytes = "/%D0%A1%D1%82%D1%80%D0%BE%D0%BD%D0%B3-%D1%84%D0%B8%D0%BB%D1%8C%D1%82%D1%80/%D0%BA%D0%B0%D1%82%D0%B0%D0%BB%D0%BE%D0%B3?".getBytes("UTF-8");
-        byte[] cp1251_bytes = TypeUtil.fromHexString("e2fbe1f0e0edee3dd2e5ecefe5f0e0f2f3f0e0");
-        String expectedCP1251String = new String(cp1251_bytes, "cp1251");
-        String expectedCP1251Key = new String(cp1251_bytes, 0, 7, "cp1251");
-        String expectedCP1251Value = new String(cp1251_bytes, 8, cp1251_bytes.length-8, "cp1251");
-       
-        //paste both byte arrays together to form the uri
-        byte[] allbytes = new byte[utf8_bytes.length+cp1251_bytes.length];
-        int i=0;
-        for (;i<utf8_bytes.length;i++) {
-            allbytes[i] = utf8_bytes[i];
-        }
-        for (int j=0; j< cp1251_bytes.length;j++)
-            allbytes[i+j] = cp1251_bytes[j];
-        
-        //Test using a HttpUri that expects a particular charset encoding. See URIUtil.__CHARSET
-        HttpURI uri = new HttpURI(Charset.forName("cp1251"));
-        uri.parse(allbytes, 0, allbytes.length);
-        assertEquals(expectedCP1251String, uri.getQuery("cp1251"));
-        
-        //Test params decoded correctly
-        MultiMap params = new MultiMap();
-        uri.decodeQueryTo(params);
-        String val = params.getString(expectedCP1251Key);
-        assertNotNull(val);
-        assertEquals(expectedCP1251Value, val);
-        
-        //Test using HttpURI where you pass in the charset encoding.
-        HttpURI httpuri = new HttpURI();
-        httpuri.parse(allbytes,0,allbytes.length);
-        assertNotNull(httpuri.getQuery("UTF-8")); //still get back a query string, just incorrectly encoded
-        assertEquals(expectedCP1251String, httpuri.getQuery("cp1251"));
-        
-        //Test params decoded correctly
-        params.clear();
-        httpuri.decodeQueryTo(params, "cp1251");
-        val = params.getString(expectedCP1251Key);
-        assertNotNull(val);
-        assertEquals(expectedCP1251Value, val);
-        
-        //test able to set the query encoding and call getQueryString multiple times
-        Request request = new Request(null,null);
-        request.setUri(httpuri);    
-        request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "ISO-8859-1");
-        assertNotNull (request.getQueryString()); //will be incorrect encoding but not null
-        request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "cp1251");
-        assertEquals(expectedCP1251String, request.getQueryString());
-    }
-    
-    @Test
-    public void testPercentEncodingOfQueryStringUsingNonUTF8() throws UnsupportedEncodingException
-    {
-    
-      byte[] utf8_bytes = "/%D0%A1%D1%82%D1%80%D0%BE%D0%BD%D0%B3-%D1%84%D0%B8%D0%BB%D1%8C%D1%82%D1%80/%D0%BA%D0%B0%D1%82%D0%B0%D0%BB%D0%BE%D0%B3?".getBytes("UTF-8");
-      byte[] cp1251_bytes = "%e2%fb%e1%f0%e0%ed%ee=%d2%e5%ec%ef%e5%f0%e0%f2%f3%f0%e0".getBytes("cp1251");
-      
-      byte[] key_bytes = TypeUtil.fromHexString("e2fbe1f0e0edee");
-      byte[] val_bytes = TypeUtil.fromHexString("d2e5ecefe5f0e0f2f3f0e0");
-      String expectedCP1251String = new String(cp1251_bytes, "cp1251");
-      String expectedCP1251Key = new String(key_bytes, "cp1251");
-      String expectedCP1251Value = new String(val_bytes, "cp1251");
-      
-      byte[] allbytes = new byte[utf8_bytes.length+cp1251_bytes.length];
-      
-      //stick both arrays together to form uri
-      int i=0;
-      for (;i<utf8_bytes.length;i++) {
-          allbytes[i] = utf8_bytes[i];
-      }
-      for (int j=0; j< cp1251_bytes.length;j++)
-          allbytes[i+j] = cp1251_bytes[j];
-
-
-      HttpURI httpuri = new HttpURI();
-      httpuri.parse(allbytes,0,allbytes.length);
-      assertNotNull(httpuri.getQuery("UTF-8")); //will be incorrectly encoded, but no errors
-      assertEquals(expectedCP1251String, httpuri.getQuery("cp1251"));
-
-      //test params decoded correctly
-      MultiMap params = new MultiMap();
-      httpuri.decodeQueryTo(params, "cp1251");
-      String val = params.getString(expectedCP1251Key);
-      assertNotNull(val);
-      assertEquals(expectedCP1251Value, val);
-      
-      //test able to set the query encoding and call getQueryString multiple times
-      Request request = new Request(null,null);
-      request.setUri(httpuri);    
-      request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "ISO-8859-1");
-      assertNotNull (request.getQueryString()); //will be incorrect encoding but not null
-      request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "cp1251");
-      assertEquals(expectedCP1251String, request.getQueryString());
-      
-    }
-
-    @Test
-    public void testUnicodeErrors() throws UnsupportedEncodingException
-    {
-        String uri="http://server/path?invalid=data%uXXXXhere%u000";
-        try
-        {
-            URLDecoder.decode(uri,"UTF-8");
-            Assert.assertTrue(false);
-        }
-        catch (IllegalArgumentException e)
-        {
-        }
-
-        HttpURI huri=new HttpURI(uri);
-        MultiMap<String> params = new MultiMap<>();
-        huri.decodeQueryTo(params);
-        assertEquals("data"+Utf8Appendable.REPLACEMENT+"here"+Utf8Appendable.REPLACEMENT,params.getValue("invalid",0));
-
-        huri=new HttpURI(uri);
-        params = new MultiMap<>();
-        huri.decodeQueryTo(params,StandardCharsets.UTF_8);
-        assertEquals("data"+Utf8Appendable.REPLACEMENT+"here"+Utf8Appendable.REPLACEMENT,params.getValue("invalid",0));
-
-    }
-
-    @Test
-    public void testExtB() throws Exception
-    {
-        for (String value: new String[]{"a","abcdABCD","\u00C0","\u697C","\uD869\uDED5","\uD840\uDC08"} )
-        {
-            HttpURI uri = new HttpURI("/path?value="+URLEncoder.encode(value,"UTF-8"));
-
-            MultiMap<String> parameters = new MultiMap<>();
-            uri.decodeQueryTo(parameters,StandardCharsets.UTF_8);
-            assertEquals(value,parameters.getString("value"));
-        }
-    }
-
-
-    private final String[][] connect_tests=
-    {
-       /* 0*/ {"  localhost:8080  ","localhost","8080"},
-       /* 1*/ {"  127.0.0.1:8080  ","127.0.0.1","8080"},
-       /* 2*/ {"  [127::0::0::1]:8080  ","[127::0::0::1]","8080"},
-       /* 3*/ {"  error  ",null,null},
-       /* 4*/ {"  http://localhost:8080/  ",null,null},
-    };
-
-    @Test
-    public void testCONNECT() throws Exception
-    {
-        HttpURI uri = new HttpURI();
-        for (int i=0;i<connect_tests.length;i++)
-        {
-            try
-            {
-                byte[] buf = connect_tests[i][0].getBytes(StandardCharsets.UTF_8);
-
-                uri.parseConnect(buf,2,buf.length-4);
-                assertEquals("path"+i,connect_tests[i][0].trim(),uri.getPath());
-                assertEquals("host"+i,connect_tests[i][1],uri.getHost());
-                assertEquals("port"+i,Integer.parseInt(connect_tests[i][2]),uri.getPort());
-            }
-            catch(Exception e)
-            {
-                assertNull("error"+i,connect_tests[i][1]);
-            }
-        }
-    }
-
-    @Test
-    public void testNonURIAscii() throws Exception
-    {
-        String url = "http://www.foo.com/ma\u00F1ana";
-        byte[] asISO = url.getBytes(StandardCharsets.ISO_8859_1);
-        new String(asISO, StandardCharsets.ISO_8859_1);
-
-        //use a non UTF-8 charset as the encoding and url-escape as per
-        //http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars
-        String s = URLEncoder.encode(url, "ISO-8859-1");
-        HttpURI uri = new HttpURI(StandardCharsets.ISO_8859_1);
-
-        //parse it, using the same encoding
-        uri.parse(s);
-
-        //decode the url encoding
-        String d = URLDecoder.decode(uri.getCompletePath(), "ISO-8859-1");
-        assertEquals(url, d);
-    }
-}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpVersionCustomizerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpVersionCustomizerTest.java
new file mode 100644
index 0000000..3c26ae7
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpVersionCustomizerTest.java
@@ -0,0 +1,81 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.nio.channels.SocketChannel;
+
+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.HttpTester;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class HttpVersionCustomizerTest
+{
+    @Rule
+    public TestTracker tracker = new TestTracker();
+
+    @Test
+    public void testCustomizeHttpVersion() throws Exception
+    {
+        Server server = new Server();
+        HttpConfiguration httpConfig = new HttpConfiguration();
+        httpConfig.addCustomizer((connector, config, request) -> request.setHttpVersion(HttpVersion.HTTP_1_1));
+        ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConfig));
+        server.addConnector(connector);
+        server.setHandler(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                Assert.assertEquals(HttpVersion.HTTP_1_1.asString(), request.getProtocol());
+            }
+        });
+        server.start();
+
+        try
+        {
+            try (SocketChannel socket = SocketChannel.open(new InetSocketAddress("localhost", connector.getLocalPort())))
+            {
+                HttpTester.Request request = HttpTester.newRequest();
+                request.setVersion(HttpVersion.HTTP_1_0);
+                socket.write(request.generate());
+
+                HttpTester.Response response = HttpTester.parseResponse(HttpTester.from(socket));
+                Assert.assertNotNull(response);
+                Assert.assertThat(response.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
+            }
+        }
+        finally
+        {
+            server.stop();
+        }
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpWriterTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpWriterTest.java
index 995b6d7..e1a21a2 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpWriterTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpWriterTest.java
@@ -24,8 +24,8 @@
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 
+import org.eclipse.jetty.io.ArrayByteBufferPool;
 import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.MappedByteBufferPool;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.TypeUtil;
@@ -43,16 +43,17 @@
     {
         _bytes = BufferUtil.allocate(2048);
 
-        final ByteBufferPool bufferPool = new MappedByteBufferPool();
-        HttpChannel<?> channel = new HttpChannel<ByteBuffer>(null,new HttpConfiguration(),null,null,new ByteBufferQueuedHttpInput())
+        final ByteBufferPool pool = new ArrayByteBufferPool();
+        
+        HttpChannel channel = new HttpChannel(null,new HttpConfiguration(),null,null)
         {
             @Override
             public ByteBufferPool getByteBufferPool()
             {
-                return bufferPool;
+                return pool;
             }
         };
-
+        
         _httpOut = new HttpOutput(channel)
         {
             @Override
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/InsufficientThreadsDetectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/InsufficientThreadsDetectionTest.java
new file mode 100644
index 0000000..62a388a
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/InsufficientThreadsDetectionTest.java
@@ -0,0 +1,86 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.thread.QueuedThreadPool;
+import org.eclipse.jetty.util.thread.ThreadPool;
+import org.junit.After;
+import org.junit.Test;
+
+public class InsufficientThreadsDetectionTest {
+
+    private Server _server;
+
+    @After
+    public void dispose() throws Exception
+    {
+        _server.stop();
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testConnectorUsesServerExecutorWithNotEnoughThreads() throws Exception
+    {
+        // server has 3 threads in the executor
+        _server = new Server(new QueuedThreadPool(3));
+
+        // connector will use executor from server because connectorPool is null
+        ThreadPool connectorPool = null;
+        // connector requires 7 threads(2 + 4 + 1)
+        ServerConnector connector = new ServerConnector(_server, connectorPool, null, null, 2, 4, new HttpConnectionFactory());
+        connector.setPort(0);
+        _server.addConnector(connector);
+
+        // should throw IllegalStateException because there are no required threads in server pool
+        _server.start();
+    }
+
+    @Test
+    public void testConnectorWithDedicatedExecutor() throws Exception
+    {
+        // server has 3 threads in the executor
+        _server = new Server(new QueuedThreadPool(3));
+
+        // connector pool has 100 threads
+        ThreadPool connectorPool = new QueuedThreadPool(100);
+        // connector requires 7 threads(2 + 4 + 1)
+        ServerConnector connector = new ServerConnector(_server, connectorPool, null, null, 2, 4, new HttpConnectionFactory());
+        connector.setPort(0);
+        _server.addConnector(connector);
+
+        // should not throw exception because connector uses own executor, so its threads should not be counted
+        _server.start();
+    }
+
+    @Test // Github issue #586
+    public void testCaseForMultipleConnectors() throws Exception {
+        // server has 3 threads in the executor
+        _server = new Server(new QueuedThreadPool(3));
+
+        // first connector consumes all 3 threads from server pool
+        _server.addConnector(new ServerConnector(_server, null, null, null, 1, 1, new HttpConnectionFactory()));
+
+        // second connect also require 3 threads but uses own executor, so its threads should not be counted
+        final QueuedThreadPool connectorPool = new QueuedThreadPool(3, 3);
+        _server.addConnector(new ServerConnector(_server, connectorPool, null, null, 1, 1, new HttpConnectionFactory()));
+
+        // should not throw exception because limit was not overflown
+        _server.start();
+    }
+
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java
index 94e380b..97cd56c 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java
@@ -18,11 +18,11 @@
 
 package org.eclipse.jetty.server;
 
-import static org.junit.Assert.assertEquals;
-
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
 
 import javax.servlet.AsyncContext;
 import javax.servlet.AsyncEvent;
@@ -34,6 +34,7 @@
 
 import org.eclipse.jetty.server.handler.HandlerWrapper;
 import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.util.thread.Locker;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
@@ -42,24 +43,33 @@
 
 public class LocalAsyncContextTest
 {
-    protected Server _server = new Server();
-    protected SuspendHandler _handler = new SuspendHandler();
+    private final AtomicReference<Throwable> _completed0 = new AtomicReference<>();
+    private final AtomicReference<Throwable> _completed1 = new AtomicReference<>();
+    protected Server _server;
+    protected SuspendHandler _handler;
     protected Connector _connector;
 
     @Before
     public void init() throws Exception
     {
+        _server = new Server();
         _connector = initConnector();
-        _server.setConnectors(new Connector[]{ _connector });
+        _server.addConnector(_connector);
 
         SessionHandler session = new SessionHandler();
+        _handler = new SuspendHandler();
         session.setHandler(_handler);
 
         _server.setHandler(session);
         _server.start();
 
-        __completed.set(0);
-        __completed1.set(0);
+        reset();
+    }
+    
+    public void reset()
+    {
+        _completed0.set(null);
+        _completed1.set(null);
     }
 
     protected Connector initConnector()
@@ -82,10 +92,11 @@
         _handler.setSuspendFor(1000);
         _handler.setResumeAfter(-1);
         _handler.setCompleteAfter(-1);
-        response=process(null);
-        check(response,"TIMEOUT");
-        assertEquals(1,__completed.get());
-        assertEquals(1,__completed1.get());
+        response = process(null);
+        check(response, "TIMEOUT");
+
+        spinAssertEquals(1, () -> _completed0.get() == null ? 0 : 1);
+        spinAssertEquals(1, () -> _completed1.get() == null ? 0 : 1);
     }
 
     @Test
@@ -96,8 +107,8 @@
         _handler.setSuspendFor(10000);
         _handler.setResumeAfter(0);
         _handler.setCompleteAfter(-1);
-        response=process(null);
-        check(response,"STARTASYNC","DISPATCHED");
+        response = process(null);
+        check(response, "STARTASYNC", "DISPATCHED");
     }
 
     @Test
@@ -108,8 +119,8 @@
         _handler.setSuspendFor(10000);
         _handler.setResumeAfter(100);
         _handler.setCompleteAfter(-1);
-        response=process(null);
-        check(response,"STARTASYNC","DISPATCHED");
+        response = process(null);
+        check(response, "STARTASYNC", "DISPATCHED");
     }
 
     @Test
@@ -120,8 +131,8 @@
         _handler.setSuspendFor(10000);
         _handler.setResumeAfter(-1);
         _handler.setCompleteAfter(0);
-        response=process(null);
-        check(response,"STARTASYNC","COMPLETED");
+        response = process(null);
+        check(response, "STARTASYNC", "COMPLETED");
     }
 
     @Test
@@ -132,9 +143,8 @@
         _handler.setSuspendFor(10000);
         _handler.setResumeAfter(-1);
         _handler.setCompleteAfter(200);
-        response=process(null);
-        check(response,"STARTASYNC","COMPLETED");
-
+        response = process(null);
+        check(response, "STARTASYNC", "COMPLETED");
     }
 
     @Test
@@ -145,8 +155,8 @@
         _handler.setRead(-1);
         _handler.setResumeAfter(0);
         _handler.setCompleteAfter(-1);
-        response=process("wibble");
-        check(response,"STARTASYNC","DISPATCHED");
+        response = process("wibble");
+        check(response, "STARTASYNC", "DISPATCHED");
     }
 
     @Test
@@ -157,9 +167,8 @@
         _handler.setRead(-1);
         _handler.setResumeAfter(100);
         _handler.setCompleteAfter(-1);
-        response=process("wibble");
-        check(response,"DISPATCHED");
-
+        response = process("wibble");
+        check(response, "DISPATCHED");
     }
 
     @Test
@@ -170,35 +179,36 @@
         _handler.setRead(-1);
         _handler.setResumeAfter(-1);
         _handler.setCompleteAfter(0);
-        response=process("wibble");
-        check(response,"COMPLETED");
+        response = process("wibble");
+        check(response, "COMPLETED");
 
         _handler.setResumeAfter(-1);
         _handler.setCompleteAfter(100);
-        response=process("wibble");
-        check(response,"COMPLETED");
-
+        response = process("wibble");
+        check(response, "COMPLETED");
+        
         _handler.setRead(6);
-
         _handler.setResumeAfter(0);
         _handler.setCompleteAfter(-1);
-        response=process("wibble");
-        check(response,"DISPATCHED");
+        response = process("wibble");
+        check(response, "DISPATCHED");
 
         _handler.setResumeAfter(100);
         _handler.setCompleteAfter(-1);
-        response=process("wibble");
-        check(response,"DISPATCHED");
+        response = process("wibble");
+        
+        check(response, "DISPATCHED");
 
         _handler.setResumeAfter(-1);
         _handler.setCompleteAfter(0);
-        response=process("wibble");
-        check(response,"COMPLETED");
+        response = process("wibble");
+        check(response, "COMPLETED");
 
         _handler.setResumeAfter(-1);
         _handler.setCompleteAfter(100);
-        response=process("wibble");
-        check(response,"COMPLETED");
+        response = process("wibble");
+        check(response, "COMPLETED");
+        
     }
 
     @Test
@@ -206,9 +216,6 @@
     {
         String response;
 
-        __completed.set(0);
-        __completed1.set(0);
-
         _handler.setRead(0);
         _handler.setSuspendFor(1000);
         _handler.setResumeAfter(100);
@@ -216,346 +223,287 @@
         _handler.setSuspendFor2(1000);
         _handler.setResumeAfter2(200);
         _handler.setCompleteAfter2(-1);
-        response=process(null);
-        check(response,"STARTASYNC","DISPATCHED","startasync","STARTASYNC","DISPATCHED");
-        assertEquals(1,__completed.get());
-        assertEquals(0,__completed1.get());
+        response = process(null);
+        check(response, "STARTASYNC", "DISPATCHED", "startasync", "STARTASYNC2", "DISPATCHED");
 
+        spinAssertEquals(1, () -> _completed0.get() == null ? 0 : 1);
+        spinAssertEquals(0, () -> _completed1.get() == null ? 0 : 1);
     }
 
-    protected void check(String response,String... content)
+    protected void check(String response, String... content)
     {
-        Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 200 OK"));
-        int i=0;
-        for (String m:content)
+        Assert.assertThat(response, Matchers.startsWith("HTTP/1.1 200 OK"));
+        int i = 0;
+        for (String m : content)
         {
-            Assert.assertThat(response,Matchers.containsString(m));
-            i=response.indexOf(m,i);
-            i+=m.length();
+            Assert.assertThat(response, Matchers.containsString(m));
+            i = response.indexOf(m, i);
+            i += m.length();
         }
     }
 
     private synchronized String process(String content) throws Exception
     {
+        reset();
         String request = "GET / HTTP/1.1\r\n" +
-        "Host: localhost\r\n"+
-        "Connection: close\r\n";
+                "Host: localhost\r\n" +
+                "Connection: close\r\n";
 
-        if (content==null)
-            request+="\r\n";
+        if (content == null)
+            request += "\r\n";
         else
-            request+="Content-Length: "+content.length()+"\r\n" +"\r\n" + content;
+            request += "Content-Length: " + content.length() + "\r\n" + "\r\n" + content;
 
-        String response=getResponse(request);
-        return response;
+        return getResponse(request);
     }
 
     protected String getResponse(String request) throws Exception
     {
-        LocalConnector connector=(LocalConnector)_connector;
+        LocalConnector connector = (LocalConnector)_connector;
         LocalConnector.LocalEndPoint endp = connector.executeRequest(request);
         endp.waitUntilClosed();
         return endp.takeOutputString();
     }
 
-    private static class SuspendHandler extends HandlerWrapper
+    private class SuspendHandler extends HandlerWrapper
     {
         private int _read;
-        private long _suspendFor=-1;
-        private long _resumeAfter=-1;
-        private long _completeAfter=-1;
-        private long _suspendFor2=-1;
-        private long _resumeAfter2=-1;
-        private long _completeAfter2=-1;
+        private long _suspendFor = -1;
+        private long _resumeAfter = -1;
+        private long _completeAfter = -1;
+        private long _suspendFor2 = -1;
+        private long _resumeAfter2 = -1;
+        private long _completeAfter2 = -1;
 
         public SuspendHandler()
         {
         }
 
-        public int getRead()
-        {
-            return _read;
-        }
-
         public void setRead(int read)
         {
             _read = read;
         }
 
-        public long getSuspendFor()
-        {
-            return _suspendFor;
-        }
-
         public void setSuspendFor(long suspendFor)
         {
             _suspendFor = suspendFor;
         }
 
-        public long getResumeAfter()
-        {
-            return _resumeAfter;
-        }
-
         public void setResumeAfter(long resumeAfter)
         {
             _resumeAfter = resumeAfter;
         }
 
-        public long getCompleteAfter()
-        {
-            return _completeAfter;
-        }
-
         public void setCompleteAfter(long completeAfter)
         {
             _completeAfter = completeAfter;
         }
 
-
-
-        /* ------------------------------------------------------------ */
-        /** Get the suspendFor2.
-         * @return the suspendFor2
-         */
-        public long getSuspendFor2()
-        {
-            return _suspendFor2;
-        }
-
-
-        /* ------------------------------------------------------------ */
-        /** Set the suspendFor2.
-         * @param suspendFor2 the suspendFor2 to set
-         */
         public void setSuspendFor2(long suspendFor2)
         {
             _suspendFor2 = suspendFor2;
         }
 
-
-        /* ------------------------------------------------------------ */
-        /** Get the resumeAfter2.
-         * @return the resumeAfter2
-         */
-        public long getResumeAfter2()
-        {
-            return _resumeAfter2;
-        }
-
-
-        /* ------------------------------------------------------------ */
-        /** Set the resumeAfter2.
-         * @param resumeAfter2 the resumeAfter2 to set
-         */
         public void setResumeAfter2(long resumeAfter2)
         {
             _resumeAfter2 = resumeAfter2;
         }
 
-
-        /* ------------------------------------------------------------ */
-        /** Get the completeAfter2.
-         * @return the completeAfter2
-         */
-        public long getCompleteAfter2()
-        {
-            return _completeAfter2;
-        }
-
-
-        /* ------------------------------------------------------------ */
-        /** Set the completeAfter2.
-         * @param completeAfter2 the completeAfter2 to set
-         */
         public void setCompleteAfter2(long completeAfter2)
         {
             _completeAfter2 = completeAfter2;
         }
 
-
-        /* ------------------------------------------------------------ */
         @Override
         public void handle(String target, final Request baseRequest, final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException
         {
-            try
+            if (DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
             {
-                if (DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
+                if (_read > 0)
                 {
-                    if (_read>0)
-                    {
-                        byte[] buf=new byte[_read];
-                        request.getInputStream().read(buf);
-                    }
-                    else if (_read<0)
-                    {
-                        InputStream in = request.getInputStream();
-                        int b=in.read();
-                        while(b!=-1)
-                            b=in.read();
-                    }
+                    int read = _read;
+                    byte[] buf = new byte[read];
+                    while (read > 0)
+                        read -= request.getInputStream().read(buf);
+                }
+                else if (_read < 0)
+                {
+                    InputStream in = request.getInputStream();
+                    int b = in.read();
+                    while (b != -1)
+                        b = in.read();
+                }
 
+                final AsyncContext asyncContext = baseRequest.startAsync();
+                response.getOutputStream().println("STARTASYNC");
+                asyncContext.addListener(__asyncListener);
+                asyncContext.addListener(__asyncListener1);
+                if (_suspendFor > 0)
+                    asyncContext.setTimeout(_suspendFor);
+
+                if (_completeAfter > 0)
+                {
+                    new Thread()
+                    {
+                        @Override
+                        public void run()
+                        {
+                            try
+                            {
+                                Thread.sleep(_completeAfter);
+                                response.getOutputStream().println("COMPLETED");
+                                response.setStatus(200);
+                                baseRequest.setHandled(true);
+                                asyncContext.complete();
+                            }
+                            catch (Exception e)
+                            {
+                                e.printStackTrace();
+                            }
+                        }
+                    }.start();
+                }
+                else if (_completeAfter == 0)
+                {
+                    response.getOutputStream().println("COMPLETED");
+                    response.setStatus(200);
+                    baseRequest.setHandled(true);
+                    asyncContext.complete();
+                }
+
+                if (_resumeAfter > 0)
+                {
+                    new Thread()
+                    {
+                        @Override
+                        public void run()
+                        {
+                            try
+                            {
+                                Thread.sleep(_resumeAfter);
+                                if (((HttpServletRequest)asyncContext.getRequest()).getSession(true).getId() != null)
+                                    asyncContext.dispatch();
+                            }
+                            catch (Exception e)
+                            {
+                                e.printStackTrace();
+                            }
+                        }
+                    }.start();
+                }
+                else if (_resumeAfter == 0)
+                {
+                    asyncContext.dispatch();
+                }
+            }
+            else
+            {
+                if (request.getAttribute("TIMEOUT") != null)
+                    response.getOutputStream().println("TIMEOUT");
+                else
+                    response.getOutputStream().println("DISPATCHED");
+
+                if (_suspendFor2 >= 0)
+                {
                     final AsyncContext asyncContext = baseRequest.startAsync();
-                    response.getOutputStream().println("STARTASYNC");
-                    asyncContext.addListener(__asyncListener);
-                    asyncContext.addListener(__asyncListener1);
-                    if (_suspendFor>0)
-                        asyncContext.setTimeout(_suspendFor);
+                    response.getOutputStream().println("STARTASYNC2");
+                    if (_suspendFor2 > 0)
+                        asyncContext.setTimeout(_suspendFor2);
+                    _suspendFor2 = -1;
 
-
-                    if (_completeAfter>0)
+                    if (_completeAfter2 > 0)
                     {
-                        new Thread() {
+                        new Thread()
+                        {
                             @Override
                             public void run()
                             {
                                 try
                                 {
-                                    Thread.sleep(_completeAfter);
-                                    response.getOutputStream().println("COMPLETED");
+                                    Thread.sleep(_completeAfter2);
+                                    response.getOutputStream().println("COMPLETED2");
                                     response.setStatus(200);
                                     baseRequest.setHandled(true);
                                     asyncContext.complete();
                                 }
-                                catch(Exception e)
+                                catch (Exception e)
                                 {
                                     e.printStackTrace();
                                 }
                             }
                         }.start();
                     }
-                    else if (_completeAfter==0)
+                    else if (_completeAfter2 == 0)
                     {
-                        response.getOutputStream().println("COMPLETED");
+                        response.getOutputStream().println("COMPLETED2");
                         response.setStatus(200);
                         baseRequest.setHandled(true);
                         asyncContext.complete();
                     }
 
-                    if (_resumeAfter>0)
+                    if (_resumeAfter2 > 0)
                     {
-                        new Thread() {
+                        new Thread()
+                        {
                             @Override
                             public void run()
                             {
                                 try
                                 {
-                                    Thread.sleep(_resumeAfter);
-                                    if(((HttpServletRequest)asyncContext.getRequest()).getSession(true).getId()!=null)
-                                        asyncContext.dispatch();
+                                    Thread.sleep(_resumeAfter2);
+                                    asyncContext.dispatch();
                                 }
-                                catch(Exception e)
+                                catch (Exception e)
                                 {
                                     e.printStackTrace();
                                 }
                             }
                         }.start();
                     }
-                    else if (_resumeAfter==0)
+                    else if (_resumeAfter2 == 0)
                     {
                         asyncContext.dispatch();
                     }
                 }
                 else
                 {
-                    if (request.getAttribute("TIMEOUT")!=null)
-                    {
-                        response.getOutputStream().println("TIMEOUT");
-                    }
-                    else
-                    {
-                        response.getOutputStream().println("DISPATCHED");
-                    }
-
-                    if (_suspendFor2>=0)
-                    {
-                        final AsyncContext asyncContext = baseRequest.startAsync();
-                        response.getOutputStream().println("STARTASYNC2");
-                        if (_suspendFor2>0)
-                            asyncContext.setTimeout(_suspendFor2);
-                        _suspendFor2=-1;
-
-                        if (_completeAfter2>0)
-                        {
-                            new Thread() {
-                                @Override
-                                public void run()
-                                {
-                                    try
-                                    {
-                                        Thread.sleep(_completeAfter2);
-                                        response.getOutputStream().println("COMPLETED2");
-                                        response.setStatus(200);
-                                        baseRequest.setHandled(true);
-                                        asyncContext.complete();
-                                    }
-                                    catch(Exception e)
-                                    {
-                                        e.printStackTrace();
-                                    }
-                                }
-                            }.start();
-                        }
-                        else if (_completeAfter2==0)
-                        {
-                            response.getOutputStream().println("COMPLETED2");
-                            response.setStatus(200);
-                            baseRequest.setHandled(true);
-                            asyncContext.complete();
-                        }
-
-                        if (_resumeAfter2>0)
-                        {
-                            new Thread() {
-                                @Override
-                                public void run()
-                                {
-                                    try
-                                    {
-                                        Thread.sleep(_resumeAfter2);
-                                        asyncContext.dispatch();
-                                    }
-                                    catch(Exception e)
-                                    {
-                                        e.printStackTrace();
-                                    }
-                                }
-                            }.start();
-                        }
-                        else if (_resumeAfter2==0)
-                        {
-                            asyncContext.dispatch();
-                        }
-                    }
-                    else
-                    {
-                        response.setStatus(200);
-                        baseRequest.setHandled(true);
-                    }
+                    response.setStatus(200);
+                    baseRequest.setHandled(true);
                 }
             }
-            finally
-            {
-            }
         }
     }
 
-    static AtomicInteger __completed = new AtomicInteger();
-    static AtomicInteger __completed1 = new AtomicInteger();
-
-    private static AsyncListener __asyncListener = new AsyncListener()
+    private AsyncListener __asyncListener = new AsyncListener()
     {
-
         @Override
         public void onComplete(AsyncEvent event) throws IOException
         {
-            __completed.incrementAndGet();
+            Throwable complete = new Throwable();
+            if (!_completed0.compareAndSet(null, complete))
+            {
+              System.err.println("First onCompleted:");
+              _completed0.get().printStackTrace();
+              System.err.println("First onCompleted:");
+              complete.printStackTrace();
+              _completed0.set(null);
+              throw new IllegalStateException();
+            }
         }
 
         @Override
         public void onError(AsyncEvent event) throws IOException
         {
-            __completed.incrementAndGet();
+          Throwable complete = new Throwable();
+          if (!_completed0.compareAndSet(null, complete))
+          {
+            System.err.println("First onCompleted:");
+            _completed0.get().printStackTrace();
+            System.err.println("First onCompleted:");
+            complete.printStackTrace();
+            _completed0.set(null);
+            throw new IllegalStateException();
+          }
         }
 
         @Override
@@ -568,23 +516,39 @@
         @Override
         public void onTimeout(AsyncEvent event) throws IOException
         {
-            event.getSuppliedRequest().setAttribute("TIMEOUT",Boolean.TRUE);
+            event.getSuppliedRequest().setAttribute("TIMEOUT", Boolean.TRUE);
             event.getAsyncContext().dispatch();
         }
     };
 
-    private static AsyncListener __asyncListener1 = new AsyncListener()
+    private AsyncListener __asyncListener1 = new AsyncListener()
     {
         @Override
         public void onComplete(AsyncEvent event) throws IOException
         {
-            __completed1.incrementAndGet();
+            Throwable complete = new Throwable();
+            if (!_completed1.compareAndSet(null, complete))
+            {
+                _completed1.get().printStackTrace();
+                complete.printStackTrace();
+                _completed1.set(null);
+                throw new IllegalStateException();
+            }
         }
 
         @Override
         public void onError(AsyncEvent event) throws IOException
         {
+            Throwable complete = new Throwable();
+            if (!_completed1.compareAndSet(null, complete))
+            {
+                _completed1.get().printStackTrace();
+                complete.printStackTrace();
+                _completed1.set(null);
+                throw new IllegalStateException();
+            }
         }
+
         @Override
         public void onStartAsync(AsyncEvent event) throws IOException
         {
@@ -594,6 +558,35 @@
         public void onTimeout(AsyncEvent event) throws IOException
         {
         }
-
     };
+
+    static <T> void spinAssertEquals(T expected, Supplier<T> actualSupplier)
+    {
+        spinAssertEquals(expected, actualSupplier, 10, TimeUnit.SECONDS);
+    }
+
+    static <T> void spinAssertEquals(T expected, Supplier<T> actualSupplier, long waitFor, TimeUnit units)
+    {
+        long now = System.nanoTime();
+        long end = now + units.toNanos(waitFor);
+        T actual = null;
+        while (now < end)
+        {
+            actual = actualSupplier.get();
+            if (actual == null && expected == null ||
+                    actual != null && actual.equals(expected))
+                break;
+            try
+            {
+                Thread.sleep(10);
+            }
+            catch (InterruptedException e)
+            {
+                // Ignored
+            }
+            now = System.nanoTime();
+        }
+
+        Assert.assertEquals(expected, actual);
+    }
 }
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/LocalConnectorTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalConnectorTest.java
index dede647..06e508e 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/LocalConnectorTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalConnectorTest.java
@@ -19,13 +19,18 @@
 package org.eclipse.jetty.server;
 
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
+import java.nio.charset.StandardCharsets;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.server.LocalConnector.LocalEndPoint;
+import org.eclipse.jetty.util.BufferUtil;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -91,8 +96,209 @@
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,containsString("pathInfo=/R1"));
     }
+    
+    @Test
+    public void testOneResponse_10() throws Exception
+    {
+        String response=_connector.getResponse("GET /R1 HTTP/1.0\r\n\r\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R1"));
+    }
 
     @Test
+    public void testOneResponse_10_keep_alive() throws Exception
+    {
+        String response=_connector.getResponse(
+            "GET /R1 HTTP/1.0\r\n" +
+            "Connection: keep-alive\r\n" +
+            "\r\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R1"));
+    }
+
+    @Test
+    public void testOneResponse_10_keep_alive_empty() throws Exception
+    {
+        String response=_connector.getResponse(
+            "GET /R1?empty=true HTTP/1.0\r\n" +
+            "Connection: keep-alive\r\n" +
+            "\r\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,not(containsString("pathInfo=/R1")));
+    }
+
+    @Test
+    public void testOneResponse_11() throws Exception
+    {
+        String response=_connector.getResponse(
+            "GET /R1 HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "\r\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R1"));
+    }
+
+    @Test
+    public void testOneResponse_11_close() throws Exception
+    {
+        String response=_connector.getResponse(
+            "GET /R1 HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "Connection: close\r\n" +
+            "\r\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R1"));
+    }
+
+    @Test
+    public void testOneResponse_11_empty() throws Exception
+    {
+        String response=_connector.getResponse(
+            "GET /R1?empty=true HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "Connection: close\r\n" +
+            "\r\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,not(containsString("pathInfo=/R1")));
+    }
+
+    @Test
+    public void testOneResponse_11_chunked() throws Exception
+    {
+        String response=_connector.getResponse(
+            "GET /R1?flush=true HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "\r\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R1"));
+        assertThat(response,containsString("\r\n0\r\n"));
+    }
+
+    @Test
+    public void testThreeResponsePipeline_11() throws Exception
+    {
+        LocalEndPoint endp = _connector.connect();
+        endp.addInput(
+            "GET /R1 HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "\r\n"+
+            "GET /R2 HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "\r\n"+
+            "GET /R3 HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "\r\n"
+            );
+        String response=endp.getResponse();
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R1"));
+        response=endp.getResponse();
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R2"));
+        response=endp.getResponse();
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R3"));
+    }
+    
+    @Test
+    public void testThreeResponse_11() throws Exception
+    {
+        LocalEndPoint endp = _connector.connect();
+        endp.addInput(
+            "GET /R1 HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "\r\n");
+
+        String response=endp.getResponse();
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R1"));
+        
+        endp.addInput(
+            "GET /R2 HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "\r\n");
+
+        response=endp.getResponse();
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R2"));
+
+        endp.addInput(
+            "GET /R3 HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "\r\n"
+            );
+        
+        response=endp.getResponse();
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R3"));
+    }
+
+
+    @Test
+    public void testThreeResponseClosed_11() throws Exception
+    {
+        LocalEndPoint endp = _connector.connect();
+        endp.addInput(
+            "GET /R1 HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "\r\n"+
+            "GET /R2 HTTP/1.1\r\n" +
+            "Connection: close\r\n" +
+            "Host: localhost\r\n" +
+            "\r\n"+
+            "GET /R3 HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "\r\n"
+            );
+        String response=endp.getResponse();
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R1"));
+        response=endp.getResponse();
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R2"));
+        response=endp.getResponse();
+        assertThat(response,nullValue());
+    }
+    
+    
+    @Test
+    public void testExpectContinuesAvailable() throws Exception
+    {
+        LocalEndPoint endp = _connector.connect();
+        endp.addInput(
+            "GET /R1 HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "Content-Type: text/plain; charset=UTF-8\r\n" +
+            "Expect: 100-Continue\r\n" +
+            "Content-Length: 10\r\n" +
+            "\r\n"+
+            "01234567890\r\n");
+        String response = endp.getResponse();
+        assertThat(response,containsString("HTTP/1.1 200 OK"));       
+        assertThat(response,containsString("pathInfo=/R1"));     
+        assertThat(response,containsString("0123456789"));
+    }
+
+    @Test
+    public void testExpectContinues() throws Exception
+    {
+        LocalEndPoint endp = _connector.executeRequest(
+            "GET /R1 HTTP/1.1\r\n" +
+            "Host: localhost\r\n" +
+            "Content-Type: text/plain; charset=UTF-8\r\n" +
+            "Expect: 100-Continue\r\n" +
+            "Content-Length: 10\r\n" +
+            "\r\n");
+        String response = endp.getResponse();
+        assertThat(response,containsString("HTTP/1.1 100 Continue"));
+        endp.addInput("01234567890\r\n");
+        response = endp.getResponse();
+        assertThat(response,containsString("HTTP/1.1 200 OK"));       
+        assertThat(response,containsString("pathInfo=/R1"));     
+        assertThat(response,containsString("0123456789"));
+    }
+    
+    @Test
     public void testStopStart() throws Exception
     {
         String response=_connector.getResponses("GET /R1 HTTP/1.0\r\n\r\n");
@@ -126,6 +332,27 @@
     }
     
     @Test
+    public void testTwoGETsParsed() throws Exception
+    {
+        LocalConnector.LocalEndPoint endp = _connector.executeRequest(
+            "GET /R1 HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "\r\n"+
+            "GET /R2 HTTP/1.1\r\n"+
+            "Host: localhost\r\n"+
+            "\r\n");
+
+        String response = BufferUtil.toString(endp.waitForResponse(false,10,TimeUnit.SECONDS),StandardCharsets.ISO_8859_1);
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R1"));
+        
+        response = BufferUtil.toString(endp.waitForResponse(false,10,TimeUnit.SECONDS),StandardCharsets.ISO_8859_1);
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("pathInfo=/R2"));
+    }
+    
+    
+    @Test
     public void testManyGETs() throws Exception
     {
         String response=_connector.getResponses(
@@ -162,11 +389,11 @@
     @Test
     public void testGETandGET() throws Exception
     {
-        String response=_connector.getResponses("GET /R1 HTTP/1.0\r\n\r\n");
+        String response=_connector.getResponse("GET /R1 HTTP/1.0\r\n\r\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,containsString("pathInfo=/R1"));
 
-        response=_connector.getResponses("GET /R2 HTTP/1.0\r\n\r\n");
+        response=_connector.getResponse("GET /R2 HTTP/1.0\r\n\r\n");
         assertThat(response,containsString("HTTP/1.1 200 OK"));
         assertThat(response,containsString("pathInfo=/R2"));
     }
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java b/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java
index 889231a..1ac944e 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java
@@ -16,12 +16,6 @@
 //  ========================================================================
 //
 
-/*
- * Created on 9/01/2004
- *
- * To change the template for this generated file go to
- * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
- */
 package org.eclipse.jetty.server;
 
 import static org.hamcrest.Matchers.containsString;
@@ -35,16 +29,18 @@
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpParser;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.HandlerCollection;
+import org.eclipse.jetty.toolchain.test.AdvancedRunner;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-/**
- *
- */
+@RunWith(AdvancedRunner.class)
 public class PartialRFC2616Test
 {
     private Server server;
@@ -102,7 +98,7 @@
             assertEquals("3.3.1 RFC 850 ANSI C",d3,d2);
 
             fields.putDateField("Date",d1.getTime());
-            assertEquals("3.3.1 RFC 822 preferred","Sun, 06 Nov 1994 08:49:37 GMT",fields.getStringField("Date"));
+            assertEquals("3.3.1 RFC 822 preferred","Sun, 06 Nov 1994 08:49:37 GMT",fields.get("Date"));
         }
         catch (Exception e)
         {
@@ -532,21 +528,24 @@
     @Test
     public void test14_23() throws Exception
     {
-        int offset=0;
-        String response = connector.getResponses("GET /R1 HTTP/1.0\n" + "Connection: close\n" + "\n");
-        offset=checkContains(response,offset,"HTTP/1.1 200","200")+1;
+        try (StacklessLogging stackless = new StacklessLogging(HttpParser.class))
+        {
+            int offset=0;
+            String response = connector.getResponses("GET /R1 HTTP/1.0\n" + "Connection: close\n" + "\n");
+            offset=checkContains(response,offset,"HTTP/1.1 200","200")+1;
 
-        offset=0;
-        response=connector.getResponses("GET /R1 HTTP/1.1\n"+"Connection: close\n"+"\n");
-        offset=checkContains(response,offset,"HTTP/1.1 400","400")+1;
+            offset=0;
+            response=connector.getResponses("GET /R1 HTTP/1.1\n"+"Connection: close\n"+"\n");
+            offset=checkContains(response,offset,"HTTP/1.1 400","400")+1;
 
-        offset=0;
-        response=connector.getResponses("GET /R1 HTTP/1.1\n"+"Host: localhost\n"+"Connection: close\n"+"\n");
-        offset=checkContains(response,offset,"HTTP/1.1 200","200")+1;
+            offset=0;
+            response=connector.getResponses("GET /R1 HTTP/1.1\n"+"Host: localhost\n"+"Connection: close\n"+"\n");
+            offset=checkContains(response,offset,"HTTP/1.1 200","200")+1;
 
-        offset=0;
-        response=connector.getResponses("GET /R1 HTTP/1.1\n"+"Host:\n"+"Connection: close\n"+"\n");
-        offset=checkContains(response,offset,"HTTP/1.1 200","200")+1;
+            offset=0;
+            response=connector.getResponses("GET /R1 HTTP/1.1\n"+"Host:\n"+"Connection: close\n"+"\n");
+            offset=checkContains(response,offset,"HTTP/1.1 200","200")+1;
+        }
     }
 
     @Test
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ProxyConnectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ProxyConnectionTest.java
new file mode 100644
index 0000000..01df8ad
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ProxyConnectionTest.java
@@ -0,0 +1,176 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.http.HttpParser;
+import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ProxyConnectionTest
+{
+    private Server _server;
+    private LocalConnector _connector;
+
+    @Before
+    public void init() throws Exception
+    {
+        _server = new Server();
+
+        
+        HttpConnectionFactory http = new HttpConnectionFactory();
+        http.getHttpConfiguration().setRequestHeaderSize(1024);
+        http.getHttpConfiguration().setResponseHeaderSize(1024);
+        
+        ProxyConnectionFactory proxy = new ProxyConnectionFactory();
+        
+        _connector = new LocalConnector(_server,null,null,null,1,proxy,http);
+        _connector.setIdleTimeout(1000);
+        _server.addConnector(_connector);
+        _server.setHandler(new DumpHandler());
+        ErrorHandler eh=new ErrorHandler();
+        eh.setServer(_server);
+        _server.addBean(eh);
+        _server.start();
+    }
+
+    @After
+    public void destroy() throws Exception
+    {
+        _server.stop();
+        _server.join();
+    }
+
+    @Test
+    public void testSimple() throws Exception
+    {
+        String response=_connector.getResponses("PROXY TCP 1.2.3.4 5.6.7.8 111 222\r\n"+
+                "GET /path HTTP/1.1\n"+
+                "Host: server:80\n"+
+                "Connection: close\n"+
+                "\n");
+        
+        Assert.assertThat(response,Matchers.containsString("HTTP/1.1 200"));
+        Assert.assertThat(response,Matchers.containsString("pathInfo=/path"));
+        Assert.assertThat(response,Matchers.containsString("local=5.6.7.8:222"));
+        Assert.assertThat(response,Matchers.containsString("remote=1.2.3.4:111"));
+    }
+    
+    @Test
+    public void testIPv6() throws Exception
+    {
+        String response=_connector.getResponses("PROXY UNKNOWN eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n"+
+                "GET /path HTTP/1.1\n"+
+                "Host: server:80\n"+
+                "Connection: close\n"+
+                "\n");
+        
+        Assert.assertThat(response,Matchers.containsString("HTTP/1.1 200"));
+        Assert.assertThat(response,Matchers.containsString("pathInfo=/path"));
+        Assert.assertThat(response,Matchers.containsString("remote=eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee:65535"));
+        Assert.assertThat(response,Matchers.containsString("local=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:65535"));
+    }
+    
+    @Test
+    public void testTooLong() throws Exception
+    {
+        String response=_connector.getResponses("PROXY TOOLONG!!! eeee:eeee:eeee:eeee:0000:0000:0000:0000 ffff:ffff:ffff:ffff:0000:0000:0000:0000 65535 65535\r\n"+
+                "GET /path HTTP/1.1\n"+
+                "Host: server:80\n"+
+                "Connection: close\n"+
+                "\n");
+        
+        Assert.assertThat(response,Matchers.equalTo(""));
+    }
+    
+    @Test
+    public void testNotComplete() throws Exception
+    {
+        _connector.setIdleTimeout(100);
+        String response=_connector.getResponses("PROXY TIMEOUT");
+        Assert.assertThat(response,Matchers.equalTo(""));
+    }
+    
+    @Test
+    public void testBadChar() throws Exception
+    {
+        String response=_connector.getResponses("PROXY\tTCP 1.2.3.4 5.6.7.8 111 222\r\n"+
+                "GET /path HTTP/1.1\n"+
+                "Host: server:80\n"+
+                "Connection: close\n"+
+                "\n");
+        Assert.assertThat(response,Matchers.equalTo(""));
+    }
+    
+    @Test
+    public void testBadCRLF() throws Exception
+    {
+        String response=_connector.getResponses("PROXY TCP 1.2.3.4 5.6.7.8 111 222\r \n"+
+                "GET /path HTTP/1.1\n"+
+                "Host: server:80\n"+
+                "Connection: close\n"+
+                "\n");
+        Assert.assertThat(response,Matchers.equalTo(""));
+    }
+    
+    @Test
+    public void testBadPort() throws Exception
+    {
+        try(StacklessLogging stackless = new StacklessLogging(ProxyConnectionFactory.class))
+        {
+            String response=_connector.getResponses("PROXY TCP 1.2.3.4 5.6.7.8 9999999999999 222\r\n"+
+                    "GET /path HTTP/1.1\n"+
+                    "Host: server:80\n"+
+                    "Connection: close\n"+
+                    "\n");
+        Assert.assertThat(response,Matchers.equalTo(""));
+        }
+    }
+    
+    @Test
+    public void testMissingField() throws Exception
+    {
+        String response=_connector.getResponses("PROXY TCP 1.2.3.4 5.6.7.8 222\r\n"+
+                "GET /path HTTP/1.1\n"+
+                "Host: server:80\n"+
+                "Connection: close\n"+
+                "\n");
+        Assert.assertThat(response,Matchers.equalTo(""));
+    }
+    
+    @Test
+    public void testHTTP() throws Exception
+    {
+        String response=_connector.getResponses(
+                "GET /path HTTP/1.1\n"+
+                "Host: server:80\n"+
+                "Connection: close\n"+
+                "\n");
+        Assert.assertThat(response,Matchers.equalTo(""));
+    }
+}
+
+
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ProxyProtocolTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ProxyProtocolTest.java
index 031ae07..ff63e92 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ProxyProtocolTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ProxyProtocolTest.java
@@ -43,7 +43,7 @@
     private void start(Handler handler) throws Exception
     {
         server = new Server();
-        connector = new ServerConnector(server);
+        connector = new ServerConnector(server, new ProxyConnectionFactory(), new HttpConnectionFactory());
         server.addConnector(connector);
         server.setHandler(handler);
         server.start();
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/QueuedHttpInputTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/QueuedHttpInputTest.java
deleted file mode 100644
index 558ea98..0000000
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/QueuedHttpInputTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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.junit.Test;
-
-public class QueuedHttpInputTest
-{
-    @Test
-    public void testNoContentMessageComplete() throws Exception
-    {
-        ByteBufferQueuedHttpInput input = new ByteBufferQueuedHttpInput();
-        input.messageComplete();
-
-        input.getNextContent();
-    }
-}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
index 41a9b8e..95ab411 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.server;
 
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.startsWith;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -31,6 +32,7 @@
 import static org.junit.Assert.fail;
 
 import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
@@ -40,10 +42,13 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import javax.servlet.DispatcherType;
 import javax.servlet.MultipartConfigElement;
 import javax.servlet.ServletException;
 import javax.servlet.ServletInputStream;
@@ -53,19 +58,23 @@
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.Part;
 
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.http.MimeTypes;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.MultiPartInputStreamParser;
-import org.eclipse.jetty.util.StringUtil;
-import org.eclipse.jetty.util.Utf8Appendable;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 public class RequestTest
@@ -89,6 +98,10 @@
         _server.addConnector(_connector);
         _handler = new RequestHandler();
         _server.setHandler(_handler);
+        
+        ErrorHandler errors = new ErrorHandler();
+        errors.setShowStacks(true);
+        _server.addBean(errors);
         _server.start();
     }
 
@@ -107,13 +120,19 @@
             @Override
             public boolean check(HttpServletRequest request,HttpServletResponse response)
             {
-                Map<String,String[]> map = null;
-                //do the parse
-                map = request.getParameterMap();
-                assertEquals("aaa"+Utf8Appendable.REPLACEMENT+"bbb",map.get("param")[0]);
-                assertEquals("value",map.get("other")[0]);
-
-                return true;
+                try
+                {
+                    Map<String, String[]> map = null;
+                    // do the parse
+                    map = request.getParameterMap();
+                    return false;
+                }
+                catch(BadMessageException e)
+                {
+                    // Should be able to retrieve the raw query
+                    String rawQuery = request.getQueryString();
+                    return rawQuery.equals("param=aaa%ZZbbb&other=value");
+                }
             }
         };
 
@@ -127,7 +146,6 @@
 
         String responses=_connector.getResponses(request);
         assertTrue(responses.startsWith("HTTP/1.1 200"));
-
     }
 
     @Test
@@ -139,15 +157,24 @@
             public boolean check(HttpServletRequest request,HttpServletResponse response)
             {
                 assertNotNull(request.getLocale());
-                assertTrue(request.getLocales().hasMoreElements());
-                assertNull(request.getContentType());
+                assertTrue(request.getLocales().hasMoreElements()); // Default locale
+                assertEquals("",request.getContentType());
                 assertNull(request.getCharacterEncoding());
                 assertEquals(0,request.getQueryString().length());
                 assertEquals(-1,request.getContentLength());
                 assertNull(request.getCookies());
-                assertNull(request.getHeader("Name"));
-                assertFalse(request.getHeaders("Name").hasMoreElements());
-                assertEquals(-1,request.getDateHeader("Name"));
+                assertEquals("",request.getHeader("Name"));
+                assertTrue(request.getHeaders("Name").hasMoreElements()); // empty
+                try
+                {
+                    request.getDateHeader("Name");
+                    assertTrue(false);
+                }
+                catch(IllegalArgumentException e)
+                {
+                    
+                }
+                assertEquals(-1,request.getDateHeader("Other"));
                 return true;
             }
         };
@@ -214,6 +241,43 @@
         assertTrue(responses.startsWith("HTTP/1.1 200"));
     }
 
+    @Test
+    public void testLocale() throws Exception
+    {
+        _handler._checker = new RequestTester()
+        {
+            @Override
+            public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException
+            {
+                assertThat(request.getLocale().getLanguage(),is("da"));
+                Enumeration<Locale> locales = request.getLocales();
+                Locale locale=locales.nextElement();
+                assertThat(locale.getLanguage(),is("da"));
+                assertThat(locale.getCountry(),is(""));
+                locale=locales.nextElement();
+                assertThat(locale.getLanguage(),is("en"));
+                assertThat(locale.getCountry(),is("AU"));
+                locale=locales.nextElement();
+                assertThat(locale.getLanguage(),is("en"));
+                assertThat(locale.getCountry(),is("GB"));
+                locale=locales.nextElement();
+                assertThat(locale.getLanguage(),is("en"));
+                assertThat(locale.getCountry(),is(""));
+                assertFalse(locales.hasMoreElements());
+                return true;
+            }
+        };
+
+        String request="GET / HTTP/1.1\r\n"+
+            "Host: whatever\r\n"+
+            "Connection: close\r\n"+
+            "Accept-Language: da, en-gb;q=0.8, en;q=0.7\r\n"+
+            "Accept-Language: XX;q=0, en-au;q=0.9\r\n"+
+            "\r\n";
+        String response = _connector.getResponses(request);
+        assertThat(response, containsString(" 200 OK"));
+    }
+
 
     @Test
     public void testMultiPart() throws Exception
@@ -229,7 +293,7 @@
         contextHandler.setContextPath("/foo");
         contextHandler.setResourceBase(".");
         contextHandler.setHandler(new MultiPartRequestHandler(testTmpDir));
-        contextHandler.addEventListener(new Request.MultiPartCleanerListener()
+        contextHandler.addEventListener(new MultiPartCleanerListener()
         {
 
             @Override
@@ -291,7 +355,7 @@
         contextHandler.setContextPath("/foo");
         contextHandler.setResourceBase(".");
         contextHandler.setHandler(new BadMultiPartRequestHandler(testTmpDir));
-        contextHandler.addEventListener(new Request.MultiPartCleanerListener()
+        contextHandler.addEventListener(new MultiPartCleanerListener()
         {
 
             @Override
@@ -331,9 +395,12 @@
         "\r\n"+
         multipart;
 
-        String responses=_connector.getResponses(request);
-        //System.err.println(responses);
-        assertTrue(responses.startsWith("HTTP/1.1 500"));
+        try(StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            String responses=_connector.getResponses(request);
+            //System.err.println(responses);
+            assertTrue(responses.startsWith("HTTP/1.1 500"));
+        }
     }
 
 
@@ -345,8 +412,18 @@
             @Override
             public boolean check(HttpServletRequest request,HttpServletResponse response)
             {
-                String value=request.getParameter("param");
-                return value.startsWith("aaa") && value.endsWith("bb");
+                try
+                {
+                    // This throws an exception if attempted
+                    request.getParameter("param");
+                    return false;
+                }
+                catch(BadMessageException e)
+                {
+                    // Should still be able to get the raw query.
+                    String rawQuery = request.getQueryString();
+                    return rawQuery.equals("param=aaa%E7bbb");
+                }
             }
         };
 
@@ -358,9 +435,9 @@
         "Connection: close\n"+
         "\n";
 
-        LOG.info("Expecting NotUtf8Exception byte 62 in state 3...");
+        LOG.info("Expecting NotUtf8Exception in state 36...");
         String responses=_connector.getResponses(request);
-        assertTrue(responses.startsWith("HTTP/1.1 200"));
+        assertThat(responses,startsWith("HTTP/1.1 200"));
     }
 
     @Test
@@ -382,13 +459,13 @@
         "\n";
 
         String responses=_connector.getResponses(request);
-        assertThat(responses,Matchers.startsWith("HTTP/1.1 400"));
+        assertThat(responses, Matchers.startsWith("HTTP/1.1 400"));
     }
 
     @Test
     public void testContentTypeEncoding() throws Exception
     {
-        final ArrayList<String> results = new ArrayList<String>();
+        final ArrayList<String> results = new ArrayList<>();
         _handler._checker = new RequestTester()
         {
             @Override
@@ -428,10 +505,10 @@
         assertEquals(null,results.get(i++));
 
         assertEquals("text/html;charset=utf8",results.get(i++));
-        assertEquals("UTF-8",results.get(i++));
+        assertEquals("utf-8",results.get(i++));
 
         assertEquals("text/html; charset=\"utf8\"",results.get(i++));
-        assertEquals("UTF-8",results.get(i++));
+        assertEquals("utf-8",results.get(i++));
 
         assertTrue(results.get(i++).startsWith("text/html"));
         assertEquals(" x=z; ",results.get(i++));
@@ -440,7 +517,7 @@
     @Test
     public void testHostPort() throws Exception
     {
-        final ArrayList<String> results = new ArrayList<String>();
+        final ArrayList<String> results = new ArrayList<>();
         _handler._checker = new RequestTester()
         {
             @Override
@@ -461,7 +538,7 @@
                 "Connection: close\n"+
                 "\n");
         int i=0;
-        assertThat(response,Matchers.containsString("200 OK"));
+        assertThat(response, containsString("200 OK"));
         assertEquals("http://myhost/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("myhost",results.get(i++));
@@ -475,7 +552,7 @@
                 "Connection: close\n"+
                 "\n");
         i=0;
-        assertThat(response,Matchers.containsString("200 OK"));
+        assertThat(response, containsString("200 OK"));
         assertEquals("http://myhost:8888/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("myhost",results.get(i++));
@@ -487,7 +564,7 @@
                 "GET http://myhost:8888/ HTTP/1.0\n"+
                 "\n");
         i=0;
-        assertThat(response,Matchers.containsString("200 OK"));
+        assertThat(response, containsString("200 OK"));
         assertEquals("http://myhost:8888/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("myhost",results.get(i++));
@@ -500,7 +577,7 @@
                 "Connection: close\n"+
                 "\n");
         i=0;
-        assertThat(response,Matchers.containsString("200 OK"));
+        assertThat(response, containsString("200 OK"));
         assertEquals("http://myhost:8888/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("myhost",results.get(i++));
@@ -515,7 +592,7 @@
                 "\n");
         i=0;
 
-        assertThat(response,Matchers.containsString("200 OK"));
+        assertThat(response, containsString("200 OK"));
         assertEquals("http://1.2.3.4/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("1.2.3.4",results.get(i++));
@@ -529,7 +606,7 @@
                 "Connection: close\n"+
                 "\n");
         i=0;
-        assertThat(response,Matchers.containsString("200 OK"));
+        assertThat(response, containsString("200 OK"));
         assertEquals("http://1.2.3.4:8888/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("1.2.3.4",results.get(i++));
@@ -543,7 +620,7 @@
                 "Connection: close\n"+
                 "\n");
         i=0;
-        assertThat(response,Matchers.containsString("200 OK"));
+        assertThat(response, containsString("200 OK"));
         assertEquals("http://[::1]/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("[::1]",results.get(i++));
@@ -557,7 +634,7 @@
                 "Connection: close\n"+
                 "\n");
         i=0;
-        assertThat(response,Matchers.containsString("200 OK"));
+        assertThat(response, containsString("200 OK"));
         assertEquals("http://[::1]:8888/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("[::1]",results.get(i++));
@@ -573,7 +650,7 @@
                 "Connection: close\n"+
                 "\n");
         i=0;
-        assertThat(response,Matchers.containsString("200 OK"));
+        assertThat(response, containsString("200 OK"));
         assertEquals("https://[::1]/",results.get(i++));
         assertEquals("remote",results.get(i++));
         assertEquals("[::1]",results.get(i++));
@@ -589,7 +666,7 @@
                 "x-forwarded-proto: https\n"+
                 "\n");
         i=0;
-        assertThat(response,Matchers.containsString("200 OK"));
+        assertThat(response, containsString("200 OK"));
         assertEquals("https://[::1]:8888/",results.get(i++));
         assertEquals("remote",results.get(i++));
         assertEquals("[::1]",results.get(i++));
@@ -637,13 +714,89 @@
             Log.getRootLogger().debug("test l={}",l);
             String response = _connector.getResponses(request);
             Log.getRootLogger().debug(response);
-            assertThat(response,Matchers.containsString(" 200 OK"));
+            assertThat(response, containsString(" 200 OK"));
             assertEquals(l,length.get());
             content+="x";
         }
     }
 
     @Test
+    public void testEncodedForm() throws Exception
+    {
+        _handler._checker = new RequestTester()
+        {
+            @Override
+            public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException
+            {
+                String actual = request.getParameter("name2");
+                return "test2".equals(actual);
+            }
+        };
+
+
+        String content="name1=test&name2=test2&name3=&name4=test";
+        String request="POST / HTTP/1.1\r\n"+
+            "Host: whatever\r\n"+
+            "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n" +
+            "Content-Length: "+content.length()+"\r\n"+
+            "Connection: close\r\n"+
+            "\r\n"+
+            content;
+        String response = _connector.getResponses(request);
+        assertThat(response, containsString(" 200 OK"));
+    }
+
+    @Test
+    public void testEncodedFormUnknownMethod() throws Exception
+    {
+        _handler._checker = new RequestTester()
+        {
+            @Override
+            public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException
+            {
+                return request.getParameter("name1")==null && request.getParameter("name2")==null && request.getParameter("name3")==null;
+            }
+        };
+
+        String content="name1=test&name2=test2&name3=&name4=test";
+        String request="UNKNOWN / HTTP/1.1\r\n"+
+            "Host: whatever\r\n"+
+            "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n" +
+            "Content-Length: "+content.length()+"\r\n"+
+            "Connection: close\r\n"+
+            "\r\n"+
+            content;
+        String response = _connector.getResponses(request);
+        assertThat(response, containsString(" 200 OK"));
+    }
+
+    @Test
+    public void testEncodedFormExtraMethod() throws Exception
+    {
+        _handler._checker = new RequestTester()
+        {
+            @Override
+            public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException
+            {
+                String actual = request.getParameter("name2");
+                return "test2".equals(actual);
+            }
+        };
+
+        _connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().addFormEncodedMethod("Extra");
+        String content="name1=test&name2=test2&name3=&name4=test";
+        String request="EXTRA / HTTP/1.1\r\n"+
+            "Host: whatever\r\n"+
+            "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n" +
+            "Content-Length: "+content.length()+"\r\n"+
+            "Connection: close\r\n"+
+            "\r\n"+
+            content;
+        String response = _connector.getResponses(request);
+        assertThat(response, containsString(" 200 OK"));
+    }
+    
+    @Test
     public void test8859EncodedForm() throws Exception
     {
         _handler._checker = new RequestTester()
@@ -659,7 +812,6 @@
             }
         };
 
-
         String content="name1=test&name2=test%E4&name3=&name4=test";
         String request="POST / HTTP/1.1\r\n"+
             "Host: whatever\r\n"+
@@ -669,7 +821,7 @@
             "\r\n"+
             content;
         String response = _connector.getResponses(request);
-        assertThat(response,Matchers.containsString(" 200 OK"));
+        assertThat(response, containsString(" 200 OK"));
     }
     
     @Test
@@ -697,11 +849,83 @@
             "\r\n"+
             content;
         String response = _connector.getResponses(request);
-        assertThat(response,Matchers.containsString(" 200 OK"));
+        assertThat(response, containsString(" 200 OK"));
     }
     
     
     @Test
+    @Ignore("See issue #1175")
+    public void testMultiPartFormDataReadInputThenParams() throws Exception
+    {
+        final File tmpdir = MavenTestingUtils.getTargetTestingDir("multipart");
+        FS.ensureEmpty(tmpdir);
+    
+        Handler handler = new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException,
+                    ServletException
+            {
+                if (baseRequest.getDispatcherType() != DispatcherType.REQUEST)
+                    return;
+                
+                // Fake a MultiPartConfig'd servlet endpoint
+                MultipartConfigElement multipartConfig = new MultipartConfigElement(tmpdir.getAbsolutePath());
+                request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, multipartConfig);
+            
+                // Normal processing
+                baseRequest.setHandled(true);
+            
+                // Fake the commons-fileupload behavior
+                int length = request.getContentLength();
+                InputStream in = request.getInputStream();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                IO.copy(in, out, length); // commons-fileupload does not read to EOF
+            
+                LOG.info("input stream = " + in);
+            
+                // Record what happened as servlet response headers
+                response.setIntHeader("x-request-content-length", request.getContentLength());
+                response.setIntHeader("x-request-content-read", out.size());
+                String foo = request.getParameter("foo"); // uri query parameter
+                String bar = request.getParameter("bar"); // form-data content parameter
+                response.setHeader("x-foo", foo == null ? "null" : foo);
+                response.setHeader("x-bar", bar == null ? "null" : bar);
+            }
+        };
+        _server.stop();
+        _server.setHandler(handler);
+        _server.start();
+    
+        String multipart =  "--AaBbCc\r\n"+
+                "content-disposition: form-data; name=\"bar\"\r\n"+
+                "\r\n"+
+                "BarContent\r\n"+
+                "--AaBbCc\r\n"+
+                "content-disposition: form-data; name=\"stuff\"\r\n"+
+                "Content-Type: text/plain;charset=ISO-8859-1\r\n"+
+                "\r\n"+
+                "000000000000000000000000000000000000000000000000000\r\n"+
+                "--AaBbCc--\r\n";
+    
+        String request="POST /?foo=FooUri HTTP/1.1\r\n"+
+                "Host: whatever\r\n"+
+                "Content-Type: multipart/form-data; boundary=\"AaBbCc\"\r\n"+
+                "Content-Length: "+multipart.getBytes().length+"\r\n"+
+                "Connection: close\r\n"+
+                "\r\n"+
+                multipart;
+    
+    
+        HttpTester.Response response = HttpTester.parseResponse(_connector.getResponse(request));
+    
+        // It should always be possible to read query string
+        assertThat("response.x-foo", response.get("x-foo"), is("FooUri"));
+        // Not possible to read request content parameters?
+        assertThat("response.x-bar", response.get("x-bar"), is("null")); // TODO: should this work?
+    }
+    
+    @Test
     public void testPartialRead() throws Exception
     {
         Handler handler = new AbstractHandler()
@@ -717,35 +941,35 @@
                 response.getOutputStream().write(b);
                 response.flushBuffer();
             }
-
+            
         };
         _server.stop();
         _server.setHandler(handler);
         _server.start();
-
+        
         String request="GET / HTTP/1.1\r\n"+
-        "Host: whatever\r\n"+
-        "Content-Type: text/plane\r\n"+
-        "Content-Length: "+10+"\r\n"+
-        "\r\n"+
-        "0123456789\r\n"+
-        "GET / HTTP/1.1\r\n"+
-        "Host: whatever\r\n"+
-        "Content-Type: text/plane\r\n"+
-        "Content-Length: "+10+"\r\n"+
-        "Connection: close\r\n"+
-        "\r\n"+
-        "ABCDEFGHIJ\r\n";
-
+                "Host: whatever\r\n"+
+                "Content-Type: text/plane\r\n"+
+                "Content-Length: "+10+"\r\n"+
+                "\r\n"+
+                "0123456789\r\n"+
+                "GET / HTTP/1.1\r\n"+
+                "Host: whatever\r\n"+
+                "Content-Type: text/plane\r\n"+
+                "Content-Length: "+10+"\r\n"+
+                "Connection: close\r\n"+
+                "\r\n"+
+                "ABCDEFGHIJ\r\n";
+        
         String responses = _connector.getResponses(request);
-
+        
         int index=responses.indexOf("read="+(int)'0');
         assertTrue(index>0);
-
+        
         index=responses.indexOf("read="+(int)'A',index+7);
         assertTrue(index>0);
     }
-
+    
     @Test
     public void testQueryAfterRead()
         throws Exception
@@ -818,8 +1042,8 @@
                                                 "Host: myhost\n"+
                                                 "Connection: close\n"+
                                                 "\n");
-        assertThat(response,Matchers.containsString(" 302 Found"));
-        assertThat(response,Matchers.containsString("Location: http://myhost/foo"));
+        assertThat(response, containsString(" 302 Found"));
+        assertThat(response, containsString("Location: http://myhost/foo"));
     }
 
     @Test
@@ -888,9 +1112,9 @@
                     "\n",
                     200, TimeUnit.MILLISECONDS
                     );
-        assertTrue(response.indexOf("200")>0);
-        assertFalse(response.indexOf("Connection: close")>0);
-        assertTrue(response.indexOf("Hello World")>0);
+        assertThat(response, containsString("200"));
+        assertThat(response, Matchers.not(containsString("Connection: close")));
+        assertThat(response, containsString("Hello World"));
 
         response=_connector.getResponses(
                     "GET / HTTP/1.1\n"+
@@ -898,9 +1122,9 @@
                     "Connection: close\n"+
                     "\n"
                     );
-        assertTrue(response.indexOf("200")>0);
-        assertTrue(response.indexOf("Connection: close")>0);
-        assertTrue(response.indexOf("Hello World")>0);
+        assertThat(response, containsString("200"));
+        assertThat(response, containsString("Connection: close"));
+        assertThat(response, containsString("Hello World"));
 
         response=_connector.getResponses(
                     "GET / HTTP/1.1\n"+
@@ -909,18 +1133,18 @@
                     "\n"
                     );
 
-        assertTrue(response.indexOf("200")>0);
-        assertTrue(response.indexOf("Connection: close")>0);
-        assertTrue(response.indexOf("Hello World")>0);
+        assertThat(response, containsString("200"));
+        assertThat(response, containsString("Connection: close"));
+        assertThat(response, containsString("Hello World"));
 
         response=_connector.getResponses(
                     "GET / HTTP/1.0\n"+
                     "Host: whatever\n"+
                     "\n"
                     );
-        assertTrue(response.indexOf("200")>0);
-        assertFalse(response.indexOf("Connection: close")>0);
-        assertTrue(response.indexOf("Hello World")>0);
+        assertThat(response, containsString("200"));
+        assertThat(response, Matchers.not(containsString("Connection: close")));
+        assertThat(response, containsString("Hello World"));
 
         response=_connector.getResponses(
                     "GET / HTTP/1.0\n"+
@@ -928,8 +1152,8 @@
                     "Connection: Other, close\n"+
                     "\n"
                     );
-        assertTrue(response.indexOf("200")>0);
-        assertTrue(response.indexOf("Hello World")>0);
+        assertThat(response, containsString("200"));
+        assertThat(response, containsString("Hello World"));
 
         response=_connector.getResponses(
                     "GET / HTTP/1.0\n"+
@@ -938,9 +1162,9 @@
                     "\n",
                     200, TimeUnit.MILLISECONDS
                     );
-        assertTrue(response.indexOf("200")>0);
-        assertTrue(response.indexOf("Connection: keep-alive")>0);
-        assertTrue(response.indexOf("Hello World")>0);
+        assertThat(response, containsString("200"));
+        assertThat(response, containsString("Connection: keep-alive"));
+        assertThat(response, containsString("Hello World"));
 
         _handler._checker = new RequestTester()
         {
@@ -960,9 +1184,9 @@
                     "\n",
                     200, TimeUnit.MILLISECONDS
                     );
-        assertTrue(response.indexOf("200")>0);
-        assertTrue(response.indexOf("Connection: TE,Other")>0);
-        assertTrue(response.indexOf("Hello World")>0);
+        assertThat(response, containsString("200"));
+        assertThat(response, containsString("Connection: TE,Other"));
+        assertThat(response, containsString("Hello World"));
 
         response=_connector.getResponses(
                     "GET / HTTP/1.1\n"+
@@ -970,15 +1194,15 @@
                     "Connection: close\n"+
                     "\n"
                     );
-        assertThat(response,Matchers.containsString("200 OK"));
-        assertThat(response,Matchers.containsString("Connection: close"));
-        assertThat(response,Matchers.containsString("Hello World"));
+        assertThat(response, containsString("200 OK"));
+        assertThat(response, containsString("Connection: close"));
+        assertThat(response, containsString("Hello World"));
     }
 
     @Test
     public void testCookies() throws Exception
     {
-        final ArrayList<Cookie> cookies = new ArrayList<Cookie>();
+        final ArrayList<Cookie> cookies = new ArrayList<>();
 
         _handler._checker = new RequestTester()
         {
@@ -1010,14 +1234,14 @@
         response=_connector.getResponses(
                     "GET / HTTP/1.1\n"+
                     "Host: whatever\n"+
-                    "Cookie: name=quoted=\\\"value\\\"\n" +
+                    "Cookie: name=quoted=\"\\\"value\\\"\"\n" +
                     "Connection: close\n"+
                     "\n"
         );
         assertTrue(response.startsWith("HTTP/1.1 200 OK"));
         assertEquals(1,cookies.size());
         assertEquals("name", cookies.get(0).getName());
-        assertEquals("quoted=\\\"value\\\"", cookies.get(0).getValue());
+        assertEquals("quoted=\"value\"", cookies.get(0).getValue());
 
         cookies.clear();
         response=_connector.getResponses(
@@ -1048,8 +1272,8 @@
                 "Connection: close\n"+
                 "\n"
         );
-        assertThat(response,startsWith("HTTP/1.1 200 OK"));
-        assertThat(response.substring(15),containsString("HTTP/1.1 200 OK"));
+        assertThat(response, Matchers.startsWith("HTTP/1.1 200 OK"));
+        assertThat(response.substring(15), containsString("HTTP/1.1 200 OK"));
         assertEquals(4,cookies.size());
         assertEquals("name", cookies.get(0).getName());
         assertEquals("value", cookies.get(0).getValue());
@@ -1073,8 +1297,8 @@
                 "Connection: close\n"+
                 "\n"
         );
-        assertThat(response,startsWith("HTTP/1.1 200 OK"));
-        assertThat(response.substring(15),containsString("HTTP/1.1 200 OK"));
+        assertThat(response, Matchers.startsWith("HTTP/1.1 200 OK"));
+        assertThat(response.substring(15), containsString("HTTP/1.1 200 OK"));
         assertEquals(4,cookies.size());
         assertEquals("name", cookies.get(0).getName());
         assertEquals("value", cookies.get(0).getValue());
@@ -1133,6 +1357,7 @@
 
     }
 
+    @Ignore("No longer relevant")
     @Test
     public void testCookieLeak() throws Exception
     {
@@ -1178,7 +1403,7 @@
         +
         "POST / HTTP/1.1\r\n"+
         "Host: whatever\r\n"+
-        "Cookie:\r\n"+
+        "Cookie: \r\n"+
         "Connection: close\r\n"+
         "\r\n";
 
@@ -1209,111 +1434,106 @@
     @Test
     public void testHashDOSKeys() throws Exception
     {
-        ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(true);
-        LOG.info("Expecting maxFormKeys limit and Closing HttpParser exceptions...");
-        _server.setAttribute("org.eclipse.jetty.server.Request.maxFormContentSize",-1);
-        _server.setAttribute("org.eclipse.jetty.server.Request.maxFormKeys",1000);
-
-
-        StringBuilder buf = new StringBuilder(4000000);
-        buf.append("a=b");
-
-        // The evil keys file is not distributed - as it is dangerous
-        File evil_keys = new File("/tmp/keys_mapping_to_zero_2m");
-        if (evil_keys.exists())
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
         {
-            LOG.info("Using real evil keys!");
-            try (BufferedReader in = new BufferedReader(new FileReader(evil_keys)))
+            // Expecting maxFormKeys limit and Closing HttpParser exceptions...
+            _server.setAttribute("org.eclipse.jetty.server.Request.maxFormContentSize",-1);
+            _server.setAttribute("org.eclipse.jetty.server.Request.maxFormKeys",1000);
+
+            StringBuilder buf = new StringBuilder(4000000);
+            buf.append("a=b");
+
+            // The evil keys file is not distributed - as it is dangerous
+            File evil_keys = new File("/tmp/keys_mapping_to_zero_2m");
+            if (evil_keys.exists())
             {
-                String key=null;
-                while((key=in.readLine())!=null)
-                    buf.append("&").append(key).append("=").append("x");
+                // Using real evil keys!
+                try (BufferedReader in = new BufferedReader(new FileReader(evil_keys)))
+                {
+                    String key=null;
+                    while((key=in.readLine())!=null)
+                        buf.append("&").append(key).append("=").append("x");
+                }
             }
-        }
-        else
-        {
-            // we will just create a lot of keys and make sure the limit is applied
-            for (int i=0;i<2000;i++)
-                buf.append("&").append("K").append(i).append("=").append("x");
-        }
-        buf.append("&c=d");
-
-
-        _handler._checker = new RequestTester()
-        {
-            @Override
-            public boolean check(HttpServletRequest request,HttpServletResponse response)
+            else
             {
-                return "b".equals(request.getParameter("a")) && request.getParameter("c")==null;
+                // we will just create a lot of keys and make sure the limit is applied
+                for (int i=0;i<2000;i++)
+                    buf.append("&").append("K").append(i).append("=").append("x");
             }
-        };
+            buf.append("&c=d");
 
-        String request="POST / HTTP/1.1\r\n"+
-        "Host: whatever\r\n"+
-        "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n"+
-        "Content-Length: "+buf.length()+"\r\n"+
-        "Connection: close\r\n"+
-        "\r\n"+
-        buf;
 
-        try
-        {
+            _handler._checker = new RequestTester()
+            {
+                @Override
+                public boolean check(HttpServletRequest request,HttpServletResponse response)
+                {
+                    return "b".equals(request.getParameter("a")) && request.getParameter("c")==null;
+                }
+            };
+
+            String request="POST / HTTP/1.1\r\n"+
+                    "Host: whatever\r\n"+
+                    "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n"+
+                    "Content-Length: "+buf.length()+"\r\n"+
+                    "Connection: close\r\n"+
+                    "\r\n"+
+                    buf;
+
             long start=System.currentTimeMillis();
-            String response = _connector.getResponses(request);
-            assertTrue(response.contains("Form too many keys"));
+            String rawResponse = _connector.getResponses(request);
+            HttpTester.Response response = HttpTester.parseResponse(rawResponse);
+            assertThat("Response.status", response.getStatus(), is(400));
+            assertThat("Response body content", response.getContent(),containsString(BadMessageException.class.getName()));
+            assertThat("Response body content", response.getContent(),containsString(IllegalStateException.class.getName()));
             long now=System.currentTimeMillis();
             assertTrue((now-start)<5000);
         }
-        finally
-        {
-            ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false);
-        }
     }
-    
+
     @Test
     public void testHashDOSSize() throws Exception
-    {
-        ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(true);
-        LOG.info("Expecting maxFormSize limit and too much data exceptions...");
-        _server.setAttribute("org.eclipse.jetty.server.Request.maxFormContentSize",3396);
-        _server.setAttribute("org.eclipse.jetty.server.Request.maxFormKeys",1000);
-
-        StringBuilder buf = new StringBuilder(4000000);
-        buf.append("a=b");
-        // we will just create a lot of keys and make sure the limit is applied
-        for (int i=0;i<500;i++)
-            buf.append("&").append("K").append(i).append("=").append("x");
-        buf.append("&c=d");
-
-        _handler._checker = new RequestTester()
+    {        
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
         {
-            @Override
-            public boolean check(HttpServletRequest request,HttpServletResponse response)
+            LOG.info("Expecting maxFormSize limit and too much data exceptions...");
+            _server.setAttribute("org.eclipse.jetty.server.Request.maxFormContentSize",3396);
+            _server.setAttribute("org.eclipse.jetty.server.Request.maxFormKeys",1000);
+
+            StringBuilder buf = new StringBuilder(4000000);
+            buf.append("a=b");
+            // we will just create a lot of keys and make sure the limit is applied
+            for (int i=0;i<500;i++)
+                buf.append("&").append("K").append(i).append("=").append("x");
+            buf.append("&c=d");
+
+            _handler._checker = new RequestTester()
             {
-                return "b".equals(request.getParameter("a")) && request.getParameter("c")==null;
-            }
-        };
+                @Override
+                public boolean check(HttpServletRequest request,HttpServletResponse response)
+                {
+                    return "b".equals(request.getParameter("a")) && request.getParameter("c")==null;
+                }
+            };
 
-        String request="POST / HTTP/1.1\r\n"+
-        "Host: whatever\r\n"+
-        "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n"+
-        "Content-Length: "+buf.length()+"\r\n"+
-        "Connection: close\r\n"+
-        "\r\n"+
-        buf;
+            String request="POST / HTTP/1.1\r\n"+
+                    "Host: whatever\r\n"+
+                    "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n"+
+                    "Content-Length: "+buf.length()+"\r\n"+
+                    "Connection: close\r\n"+
+                    "\r\n"+
+                    buf;
 
-        try
-        {
             long start=System.currentTimeMillis();
-            String response = _connector.getResponses(request);
-            assertTrue(response.contains("Form too large:"));
+            String rawResponse = _connector.getResponses(request);
+            HttpTester.Response response = HttpTester.parseResponse(rawResponse);
+            assertThat("Response.status", response.getStatus(), is(400));
+            assertThat("Response body content", response.getContent(),containsString(BadMessageException.class.getName()));
+            assertThat("Response body content", response.getContent(),containsString(IllegalStateException.class.getName()));
             long now=System.currentTimeMillis();
             assertTrue((now-start)<5000);
         }
-        finally
-        {
-            ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false);
-        }
     }
 
     @Test(expected = UnsupportedEncodingException.class)
@@ -1413,14 +1633,13 @@
                 request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
 
                 //We should get an error when we getParams if there was a problem parsing the multipart
-                String field1 = request.getParameter("xxx");
+                request.getPart("xxx");
                 //A 200 response is actually wrong here
             }
             catch (RuntimeException e)
             {
                 response.sendError(500);
             }
-           
         }
     }
     
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java
index effb6a7..363cefa 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -29,6 +30,8 @@
 
 import org.eclipse.jetty.http.HttpContent;
 import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http.ResourceHttpContent;
+import org.eclipse.jetty.toolchain.test.OS;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.util.resource.ResourceCollection;
@@ -48,9 +51,9 @@
         Resource[] r = rc.getResources();
         MimeTypes mime = new MimeTypes();
 
-        ResourceCache rc3 = new ResourceCache(null,r[2],mime,false,false);
-        ResourceCache rc2 = new ResourceCache(rc3,r[1],mime,false,false);
-        ResourceCache rc1 = new ResourceCache(rc2,r[0],mime,false,false);
+        ResourceCache rc3 = new ResourceCache(null,r[2],mime,false,false,false);
+        ResourceCache rc2 = new ResourceCache(rc3,r[1],mime,false,false,false);
+        ResourceCache rc1 = new ResourceCache(rc2,r[0],mime,false,false,false);
 
         assertEquals("1 - one", getContent(rc1, "1.txt"));
         assertEquals("2 - two", getContent(rc1, "2.txt"));
@@ -78,8 +81,8 @@
         Resource[] r = rc.getResources();
         MimeTypes mime = new MimeTypes();
 
-        ResourceCache rc3 = new ResourceCache(null,r[2],mime,false,false);
-        ResourceCache rc2 = new ResourceCache(rc3,r[1],mime,false,false)
+        ResourceCache rc3 = new ResourceCache(null,r[2],mime,false,false,false);
+        ResourceCache rc2 = new ResourceCache(rc3,r[1],mime,false,false,false)
         {
             @Override
             public boolean isCacheable(Resource resource)
@@ -88,7 +91,7 @@
             }
         };
 
-        ResourceCache rc1 = new ResourceCache(rc2,r[0],mime,false,false);
+        ResourceCache rc1 = new ResourceCache(rc2,r[0],mime,false,false,false);
 
         assertEquals("1 - one", getContent(rc1, "1.txt"));
         assertEquals("2 - two", getContent(rc1, "2.txt"));
@@ -129,56 +132,89 @@
         directory=Resource.newResource(files[0].getParentFile().getAbsolutePath());
 
 
-        cache=new ResourceCache(null,directory,new MimeTypes(),false,false);
+        cache=new ResourceCache(null,directory,new MimeTypes(),false,false,false);
 
         cache.setMaxCacheSize(95);
         cache.setMaxCachedFileSize(85);
         cache.setMaxCachedFiles(4);
 
-        assertTrue(cache.lookup("does not exist")==null);
-        assertTrue(cache.lookup(names[9]) instanceof HttpContent.ResourceAsHttpContent);
+        assertTrue(cache.getContent("does not exist",4096)==null);
+        assertTrue(cache.getContent(names[9],4096) instanceof ResourceHttpContent);
+        assertTrue(cache.getContent(names[9],4096).getIndirectBuffer()!=null);
 
         HttpContent content;
-        content=cache.lookup(names[8]);
+        content=cache.getContent(names[8],4096);
         assertTrue(content!=null);
-        assertEquals(80,content.getContentLength());
+        assertEquals(80,content.getContentLengthValue());
+        assertEquals(0,cache.getCachedSize());
+        
+        if (OS.IS_LINUX)
+        {
+            // Initially not using memory mapped files
+            content.getDirectBuffer();
+            assertEquals(80,cache.getCachedSize());
 
+            // with both types of buffer loaded, this is too large for cache
+            content.getIndirectBuffer();
+            assertEquals(0,cache.getCachedSize());
+            assertEquals(0,cache.getCachedFiles());
+
+            cache=new ResourceCache(null,directory,new MimeTypes(),true,false,false);
+            cache.setMaxCacheSize(95);
+            cache.setMaxCachedFileSize(85);
+            cache.setMaxCachedFiles(4);
+            
+            content=cache.getContent(names[8],4096);
+            content.getDirectBuffer();
+            assertEquals(cache.isUseFileMappedBuffer()?0:80,cache.getCachedSize());
+
+            // with both types of buffer loaded, this is not too large for cache because
+            // mapped buffers don't count, so we can continue
+        }
+        content.getIndirectBuffer();
         assertEquals(80,cache.getCachedSize());
         assertEquals(1,cache.getCachedFiles());
 
         Thread.sleep(200);
 
-        content=cache.lookup(names[1]);
+        content=cache.getContent(names[1],4096);
+        assertEquals(80,cache.getCachedSize());
+        content.getIndirectBuffer();
         assertEquals(90,cache.getCachedSize());
         assertEquals(2,cache.getCachedFiles());
 
         Thread.sleep(200);
 
-        content=cache.lookup(names[2]);
+        content=cache.getContent(names[2],4096);
+        content.getIndirectBuffer();
         assertEquals(30,cache.getCachedSize());
         assertEquals(2,cache.getCachedFiles());
 
         Thread.sleep(200);
 
-        content=cache.lookup(names[3]);
+        content=cache.getContent(names[3],4096);
+        content.getIndirectBuffer();
         assertEquals(60,cache.getCachedSize());
         assertEquals(3,cache.getCachedFiles());
 
         Thread.sleep(200);
 
-        content=cache.lookup(names[4]);
+        content=cache.getContent(names[4],4096);
+        content.getIndirectBuffer();
         assertEquals(90,cache.getCachedSize());
         assertEquals(3,cache.getCachedFiles());
 
         Thread.sleep(200);
 
-        content=cache.lookup(names[5]);
+        content=cache.getContent(names[5],4096);
+        content.getIndirectBuffer();
         assertEquals(90,cache.getCachedSize());
         assertEquals(2,cache.getCachedFiles());
 
         Thread.sleep(200);
 
-        content=cache.lookup(names[6]);
+        content=cache.getContent(names[6],4096);
+        content.getIndirectBuffer();
         assertEquals(60,cache.getCachedSize());
         assertEquals(1,cache.getCachedFiles());
 
@@ -188,37 +224,43 @@
         {
             out.write(' ');
         }
-        content=cache.lookup(names[7]);
+        content=cache.getContent(names[7],4096);
+        content.getIndirectBuffer();
         assertEquals(70,cache.getCachedSize());
         assertEquals(1,cache.getCachedFiles());
 
         Thread.sleep(200);
 
-        content=cache.lookup(names[6]);
+        content=cache.getContent(names[6],4096);
+        content.getIndirectBuffer();
         assertEquals(71,cache.getCachedSize());
         assertEquals(2,cache.getCachedFiles());
 
         Thread.sleep(200);
 
-        content=cache.lookup(names[0]);
+        content=cache.getContent(names[0],4096);
+        content.getIndirectBuffer();
         assertEquals(72,cache.getCachedSize());
         assertEquals(3,cache.getCachedFiles());
 
         Thread.sleep(200);
 
-        content=cache.lookup(names[1]);
+        content=cache.getContent(names[1],4096);
+        content.getIndirectBuffer();
         assertEquals(82,cache.getCachedSize());
         assertEquals(4,cache.getCachedFiles());
 
         Thread.sleep(200);
 
-        content=cache.lookup(names[2]);
+        content=cache.getContent(names[2],4096);
+        content.getIndirectBuffer();
         assertEquals(32,cache.getCachedSize());
         assertEquals(4,cache.getCachedFiles());
 
         Thread.sleep(200);
 
-        content=cache.lookup(names[3]);
+        content=cache.getContent(names[3],4096);
+        content.getIndirectBuffer();
         assertEquals(61,cache.getCachedSize());
         assertEquals(4,cache.getCachedFiles());
 
@@ -242,7 +284,7 @@
         Resource[] resources = rc.getResources();
         MimeTypes mime = new MimeTypes();
 
-        ResourceCache cache = new ResourceCache(null,resources[0],mime,false,false);
+        ResourceCache cache = new ResourceCache(null,resources[0],mime,false,false,false);
 
         assertEquals("4 - four", getContent(cache, "four.txt"));
         assertEquals("4 - four (no extension)", getContent(cache, "four"));
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
index dab9c5b..ab62496 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
@@ -18,18 +18,26 @@
 
 package org.eclipse.jetty.server;
 
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.startsWith;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.LineNumberReader;
 import java.io.PrintWriter;
+import java.net.HttpCookie;
 import java.net.Socket;
+import java.net.URLEncoder;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -42,18 +50,23 @@
 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.HttpField;
 import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
 import org.eclipse.jetty.io.AbstractEndPoint;
 import org.eclipse.jetty.io.ByteArrayEndPoint;
+import org.eclipse.jetty.io.RuntimeIOException;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.session.HashSessionIdManager;
 import org.eclipse.jetty.server.session.HashSessionManager;
 import org.eclipse.jetty.server.session.HashedSession;
+import org.eclipse.jetty.server.session.SessionHandler;
 import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.thread.Scheduler;
 import org.eclipse.jetty.util.thread.TimerScheduler;
@@ -66,7 +79,7 @@
 public class ResponseTest
 {
     private Server _server;
-    private HttpChannel<ByteBuffer> _channel;
+    private HttpChannel _channel;
 
     @Before
     public void init() throws Exception
@@ -80,33 +93,47 @@
         _server.start();
 
         AbstractEndPoint endp = new ByteArrayEndPoint(_scheduler, 5000);
-        ByteBufferQueuedHttpInput input = new ByteBufferQueuedHttpInput();
-        _channel = new HttpChannel<ByteBuffer>(connector, new HttpConfiguration(), endp, new HttpTransport()
+        _channel = new HttpChannel(connector, new HttpConfiguration(), endp, new HttpTransport()
         {
+            private Throwable _channelError;
+
             @Override
-            public void send(ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
+            public void send(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback)
             {
-                callback.succeeded();
-            }
-            
-            @Override
-            public void send(ByteBuffer responseBodyContent, boolean lastContent, Callback callback)
-            {
-                send(null,responseBodyContent, lastContent, callback);
+                if (_channelError==null)
+                    callback.succeeded();
+                else
+                    callback.failed(_channelError);
             }
 
             @Override
-            public void completed()
+            public boolean isPushSupported()
+            {
+                return false;
+            }
+
+            @Override
+            public void push(org.eclipse.jetty.http.MetaData.Request request)
             {
             }
 
             @Override
-            public void abort()
+            public void onCompleted()
             {
-                
             }
 
-        }, input);
+            @Override
+            public void abort(Throwable failure)
+            {
+                _channelError=failure;
+            }
+
+            @Override
+            public boolean isOptimizedForDirectBuffers()
+            {
+                return false;
+            }
+        });
     }
 
     @After
@@ -119,7 +146,7 @@
     @Test
     public void testContentType() throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
 
         assertEquals(null, response.getContentType());
 
@@ -129,9 +156,9 @@
         response.setContentType("foo/bar");
         assertEquals("foo/bar", response.getContentType());
         response.getWriter();
-        assertEquals("foo/bar; charset=ISO-8859-1", response.getContentType());
+        assertEquals("foo/bar;charset=iso-8859-1", response.getContentType());
         response.setContentType("foo2/bar2");
-        assertEquals("foo2/bar2; charset=ISO-8859-1", response.getContentType());
+        assertEquals("foo2/bar2;charset=iso-8859-1", response.getContentType());
         response.setHeader("name", "foo");
 
         Iterator<String> en = response.getHeaders("name").iterator();
@@ -148,16 +175,16 @@
         response.setContentType("text/html");
         assertEquals("text/html", response.getContentType());
         response.getWriter();
-        assertEquals("text/html; charset=ISO-8859-1", response.getContentType());
-        response.setContentType("foo2/bar2");
-        assertEquals("foo2/bar2; charset=ISO-8859-1", response.getContentType());
+        assertEquals("text/html;charset=utf-8", response.getContentType());
+        response.setContentType("foo2/bar2;charset=utf-8");
+        assertEquals("foo2/bar2;charset=utf-8", response.getContentType());
 
         response.recycle();
         response.setContentType("text/xml;charset=ISO-8859-7");
         response.getWriter();
         assertEquals("text/xml;charset=ISO-8859-7", response.getContentType());
         response.setContentType("text/html;charset=UTF-8");
-        assertEquals("text/html; charset=ISO-8859-7", response.getContentType());
+        assertEquals("text/html;charset=ISO-8859-7", response.getContentType());
 
         response.recycle();
         response.setContentType("text/html;charset=US-ASCII");
@@ -165,30 +192,30 @@
         assertEquals("text/html;charset=US-ASCII", response.getContentType());
 
         response.recycle();
-        response.setContentType("text/html; charset=utf-8");
+        response.setContentType("text/html; charset=UTF-8");
         response.getWriter();
-        assertEquals("text/html; charset=UTF-8", response.getContentType());
+        assertEquals("text/html;charset=utf-8", response.getContentType());
 
         response.recycle();
         response.setContentType("text/json");
         response.getWriter();
         assertEquals("text/json", response.getContentType());
-        
+
         response.recycle();
         response.setContentType("text/json");
         response.setCharacterEncoding("UTF-8");
         response.getWriter();
-        assertEquals("text/json; charset=UTF-8", response.getContentType());
+        assertEquals("text/json;charset=utf-8", response.getContentType());
 
         response.recycle();
         response.setCharacterEncoding("xyz");
         response.setContentType("foo/bar");
-        assertEquals("foo/bar; charset=xyz", response.getContentType());
+        assertEquals("foo/bar;charset=xyz", response.getContentType());
 
         response.recycle();
         response.setContentType("foo/bar");
         response.setCharacterEncoding("xyz");
-        assertEquals("foo/bar; charset=xyz", response.getContentType());
+        assertEquals("foo/bar;charset=xyz", response.getContentType());
 
         response.recycle();
         response.setCharacterEncoding("xyz");
@@ -198,7 +225,7 @@
         response.recycle();
         response.setContentType("foo/bar;charset=abc");
         response.setCharacterEncoding("xyz");
-        assertEquals("foo/bar; charset=xyz", response.getContentType());
+        assertEquals("foo/bar;charset=xyz", response.getContentType());
 
         response.recycle();
         response.setCharacterEncoding("xyz");
@@ -214,18 +241,70 @@
         response.recycle();
         response.addHeader("Content-Type","text/something");
         assertEquals("text/something",response.getContentType());
-        
+
         response.recycle();
         response.addHeader("Content-Type","application/json");
         response.getWriter();
         assertEquals("application/json",response.getContentType());
+    }
+    
+    @Test
+    public void testInferredCharset() throws Exception
+    {
+        // Inferred from encoding.properties
+        Response response = getResponse();
 
+        assertEquals(null, response.getContentType());
+
+        response.setHeader("Content-Type", "application/xhtml+xml");
+        assertEquals("application/xhtml+xml", response.getContentType());
+        response.getWriter();
+        assertEquals("application/xhtml+xml;charset=utf-8", response.getContentType());
+        assertEquals("utf-8", response.getCharacterEncoding());
+    }
+    
+    @Test
+    public void testAssumedCharset() throws Exception
+    {
+        Response response = getResponse();
+
+        // Assumed from known types
+        assertEquals(null, response.getContentType());
+        response.setHeader("Content-Type", "text/json");
+        assertEquals("text/json", response.getContentType());
+        response.getWriter();
+        assertEquals("text/json", response.getContentType());
+        assertEquals("utf-8", response.getCharacterEncoding());
+
+        response.recycle();
+
+        // Assumed from encoding.properties
+        assertEquals(null, response.getContentType());
+        response.setHeader("Content-Type", "application/vnd.api+json");
+        assertEquals("application/vnd.api+json", response.getContentType());
+        response.getWriter();
+        assertEquals("application/vnd.api+json", response.getContentType());
+        assertEquals("utf-8", response.getCharacterEncoding());
+    }
+
+    @Test
+    public void testStrangeContentType() throws Exception
+    {
+        Response response = getResponse();
+
+        assertEquals(null, response.getContentType());
+
+        response.recycle();
+        response.setContentType("text/html;charset=utf-8;charset=UTF-8");
+        response.getWriter();
+        assertEquals("text/html;charset=utf-8;charset=UTF-8",response.getContentType());
+        assertEquals("utf-8",response.getCharacterEncoding().toLowerCase(Locale.ENGLISH));
     }
 
     @Test
     public void testLocale() throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
 
         ContextHandler context = new ContextHandler();
         context.addLocaleEncoding(Locale.ENGLISH.toString(), "ISO-8859-1");
@@ -235,126 +314,203 @@
         response.setLocale(java.util.Locale.ITALIAN);
         assertEquals(null, response.getContentType());
         response.setContentType("text/plain");
-        assertEquals("text/plain; charset=ISO-8859-2", response.getContentType());
+        assertEquals("text/plain;charset=ISO-8859-2", response.getContentType());
 
         response.recycle();
         response.setContentType("text/plain");
         response.setCharacterEncoding("utf-8");
         response.setLocale(java.util.Locale.ITALIAN);
-        assertEquals("text/plain; charset=UTF-8", response.getContentType());
-        assertTrue(response.toString().indexOf("charset=UTF-8") > 0);
+        assertEquals("text/plain;charset=utf-8", response.getContentType());
+        assertTrue(response.toString().indexOf("charset=utf-8") > 0);
     }
 
     @Test
     public void testContentTypeCharacterEncoding() throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
 
         response.setContentType("foo/bar");
         response.setCharacterEncoding("utf-8");
-        assertEquals("foo/bar; charset=UTF-8", response.getContentType());
+        assertEquals("foo/bar;charset=utf-8", response.getContentType());
         response.getWriter();
-        assertEquals("foo/bar; charset=UTF-8", response.getContentType());
+        assertEquals("foo/bar;charset=utf-8", response.getContentType());
         response.setContentType("foo2/bar2");
-        assertEquals("foo2/bar2; charset=UTF-8", response.getContentType());
+        assertEquals("foo2/bar2;charset=utf-8", response.getContentType());
         response.setCharacterEncoding("ISO-8859-1");
-        assertEquals("foo2/bar2; charset=UTF-8", response.getContentType());
+        assertEquals("foo2/bar2;charset=utf-8", response.getContentType());
 
         response.recycle();
 
         response.setContentType("text/html");
-        response.setCharacterEncoding("utf-8");
-        assertEquals("text/html; charset=UTF-8", response.getContentType());
+        response.setCharacterEncoding("UTF-8");
+        assertEquals("text/html;charset=utf-8", response.getContentType());
         response.getWriter();
-        assertEquals("text/html; charset=UTF-8", response.getContentType());
+        assertEquals("text/html;charset=utf-8", response.getContentType());
         response.setContentType("text/xml");
-        assertEquals("text/xml; charset=UTF-8", response.getContentType());
+        assertEquals("text/xml;charset=utf-8", response.getContentType());
         response.setCharacterEncoding("ISO-8859-1");
-        assertEquals("text/xml; charset=UTF-8", response.getContentType());
+        assertEquals("text/xml;charset=utf-8", response.getContentType());
     }
 
     @Test
     public void testCharacterEncodingContentType() throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
         response.setCharacterEncoding("utf-8");
         response.setContentType("foo/bar");
-        assertEquals("foo/bar; charset=UTF-8", response.getContentType());
+        assertEquals("foo/bar;charset=utf-8", response.getContentType());
         response.getWriter();
-        assertEquals("foo/bar; charset=UTF-8", response.getContentType());
+        assertEquals("foo/bar;charset=utf-8", response.getContentType());
         response.setContentType("foo2/bar2");
-        assertEquals("foo2/bar2; charset=UTF-8", response.getContentType());
+        assertEquals("foo2/bar2;charset=utf-8", response.getContentType());
         response.setCharacterEncoding("ISO-8859-1");
-        assertEquals("foo2/bar2; charset=UTF-8", response.getContentType());
+        assertEquals("foo2/bar2;charset=utf-8", response.getContentType());
 
         response.recycle();
 
         response.setCharacterEncoding("utf-8");
         response.setContentType("text/html");
-        assertEquals("text/html; charset=UTF-8", response.getContentType());
+        assertEquals("text/html;charset=utf-8", response.getContentType());
         response.getWriter();
-        assertEquals("text/html; charset=UTF-8", response.getContentType());
+        assertEquals("text/html;charset=utf-8", response.getContentType());
         response.setContentType("text/xml");
-        assertEquals("text/xml; charset=UTF-8", response.getContentType());
+        assertEquals("text/xml;charset=utf-8", response.getContentType());
         response.setCharacterEncoding("iso-8859-1");
-        assertEquals("text/xml; charset=UTF-8", response.getContentType());
+        assertEquals("text/xml;charset=utf-8", response.getContentType());
     }
 
     @Test
     public void testContentTypeWithCharacterEncoding() throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
 
         response.setCharacterEncoding("utf16");
-        response.setContentType("foo/bar; charset=utf-8");
-        assertEquals("foo/bar; charset=utf-8", response.getContentType());
+        response.setContentType("foo/bar; charset=UTF-8");
+        assertEquals("foo/bar; charset=UTF-8", response.getContentType());
         response.getWriter();
-        assertEquals("foo/bar; charset=utf-8", response.getContentType());
+        assertEquals("foo/bar; charset=UTF-8", response.getContentType());
         response.setContentType("foo2/bar2");
-        assertEquals("foo2/bar2; charset=UTF-8", response.getContentType());
+        assertEquals("foo2/bar2;charset=utf-8", response.getContentType());
         response.setCharacterEncoding("ISO-8859-1");
-        assertEquals("foo2/bar2; charset=UTF-8", response.getContentType());
+        assertEquals("foo2/bar2;charset=utf-8", response.getContentType());
 
         response.recycle();
 
         response.setCharacterEncoding("utf16");
         response.setContentType("text/html; charset=utf-8");
-        assertEquals("text/html; charset=UTF-8", response.getContentType());
+        assertEquals("text/html;charset=utf-8", response.getContentType());
         response.getWriter();
-        assertEquals("text/html; charset=UTF-8", response.getContentType());
+        assertEquals("text/html;charset=utf-8", response.getContentType());
         response.setContentType("text/xml");
-        assertEquals("text/xml; charset=UTF-8", response.getContentType());
+        assertEquals("text/xml;charset=utf-8", response.getContentType());
         response.setCharacterEncoding("iso-8859-1");
-        assertEquals("text/xml; charset=UTF-8", response.getContentType());
+        assertEquals("text/xml;charset=utf-8", response.getContentType());
+        
+        response.recycle();
+        response.setCharacterEncoding("utf-16");
+        response.setContentType("foo/bar");
+        assertEquals("foo/bar;charset=utf-16", response.getContentType());
+        response.getOutputStream();
+        response.setCharacterEncoding("utf-8");
+        assertEquals("foo/bar;charset=utf-8", response.getContentType());
+        response.flushBuffer();
+        response.setCharacterEncoding("utf-16");
+        assertEquals("foo/bar;charset=utf-8", response.getContentType());
+    }
+
+    @Test
+    public void testResetWithNewSession() throws Exception
+    {
+        Response response = getResponse();
+        Request request = response.getHttpChannel().getRequest();
+        
+        SessionHandler session_handler = new SessionHandler();
+        session_handler.setServer(_server);
+        HashSessionManager session_manager = new HashSessionManager();
+        session_handler.setSessionManager(session_manager);
+        session_manager.setUsingCookies(true);
+        session_handler.start();
+        request.setSessionManager(session_manager);
+        HttpSession session = request.getSession(true);
+        
+        assertThat(session,not(nullValue()));
+        assertTrue(session.isNew());
+        
+        HttpField set_cookie = response.getHttpFields().getField(HttpHeader.SET_COOKIE);
+        assertThat(set_cookie,not(nullValue()));
+        assertThat(set_cookie.getValue(),startsWith("JSESSIONID"));
+        assertThat(set_cookie.getValue(),containsString(session.getId()));
+        response.setHeader("Some","Header");
+        response.addCookie(new Cookie("Some","Cookie"));
+        response.getOutputStream().print("X");
+        assertThat(response.getHttpFields().size(),is(4));
+        
+        response.reset();
+        
+        set_cookie = response.getHttpFields().getField(HttpHeader.SET_COOKIE);
+        assertThat(set_cookie,not(nullValue()));
+        assertThat(set_cookie.getValue(),startsWith("JSESSIONID"));
+        assertThat(set_cookie.getValue(),containsString(session.getId()));
+        assertThat(response.getHttpFields().size(),is(2));
+        response.getWriter();
+    }
+    
+    @Test
+    public void testResetContentTypeWithoutCharacterEncoding() throws Exception
+    {
+        Response response = getResponse();
+
+        response.setCharacterEncoding("utf-8");
+        response.setContentType("wrong/answer");
+        response.setContentType("foo/bar");
+        assertEquals("foo/bar;charset=utf-8", response.getContentType());
+        response.getWriter();
+        response.setContentType("foo2/bar2");
+        assertEquals("foo2/bar2;charset=utf-8", response.getContentType());
+    }
+
+
+    @Test
+    public void testResetContentTypeWithCharacterEncoding() throws Exception
+    {
+        Response response = getResponse();
+
+        response.setContentType("wrong/answer;charset=utf-8");
+        response.setContentType("foo/bar");
+        assertEquals("foo/bar", response.getContentType());
+        response.setContentType("wrong/answer;charset=utf-8");
+        response.getWriter();
+        response.setContentType("foo2/bar2;charset=utf-16");
+        assertEquals("foo2/bar2;charset=utf-8", response.getContentType());
     }
 
     @Test
     public void testContentTypeWithOther() throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
 
         response.setContentType("foo/bar; other=xyz");
         assertEquals("foo/bar; other=xyz", response.getContentType());
         response.getWriter();
-        assertEquals("foo/bar; other=xyz; charset=ISO-8859-1", response.getContentType());
+        assertEquals("foo/bar; other=xyz;charset=iso-8859-1", response.getContentType());
         response.setContentType("foo2/bar2");
-        assertEquals("foo2/bar2; charset=ISO-8859-1", response.getContentType());
+        assertEquals("foo2/bar2;charset=iso-8859-1", response.getContentType());
 
         response.recycle();
 
-        response.setCharacterEncoding("utf-8");
+        response.setCharacterEncoding("uTf-8");
         response.setContentType("text/html; other=xyz");
-        assertEquals("text/html; other=xyz; charset=UTF-8", response.getContentType());
+        assertEquals("text/html; other=xyz;charset=utf-8", response.getContentType());
         response.getWriter();
-        assertEquals("text/html; other=xyz; charset=UTF-8", response.getContentType());
+        assertEquals("text/html; other=xyz;charset=utf-8", response.getContentType());
         response.setContentType("text/xml");
-        assertEquals("text/xml; charset=UTF-8", response.getContentType());
+        assertEquals("text/xml;charset=utf-8", response.getContentType());
     }
 
     @Test
     public void testContentTypeWithCharacterEncodingAndOther() throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
 
         response.setCharacterEncoding("utf16");
         response.setContentType("foo/bar; charset=utf-8 other=xyz");
@@ -366,42 +522,42 @@
 
         response.setCharacterEncoding("utf16");
         response.setContentType("text/html; other=xyz charset=utf-8");
-        assertEquals("text/html; other=xyz charset=utf-8; charset=UTF-16", response.getContentType());
+        assertEquals("text/html; other=xyz charset=utf-8;charset=utf-16", response.getContentType());
         response.getWriter();
-        assertEquals("text/html; other=xyz charset=utf-8; charset=UTF-16", response.getContentType());
+        assertEquals("text/html; other=xyz charset=utf-8;charset=utf-16", response.getContentType());
 
         response.recycle();
 
         response.setCharacterEncoding("utf16");
         response.setContentType("foo/bar; other=pq charset=utf-8 other=xyz");
-        assertEquals("foo/bar; other=pq charset=utf-8 other=xyz; charset=UTF-16", response.getContentType());
+        assertEquals("foo/bar; other=pq charset=utf-8 other=xyz;charset=utf-16", response.getContentType());
         response.getWriter();
-        assertEquals("foo/bar; other=pq charset=utf-8 other=xyz; charset=UTF-16", response.getContentType());
+        assertEquals("foo/bar; other=pq charset=utf-8 other=xyz;charset=utf-16", response.getContentType());
     }
 
     @Test
     public void testStatusCodes() throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
 
         response.sendError(404);
         assertEquals(404, response.getStatus());
         assertEquals(null, response.getReason());
 
-        response = newResponse();
+        response = getResponse();
 
         response.sendError(500, "Database Error");
         assertEquals(500, response.getStatus());
         assertEquals("Database Error", response.getReason());
         assertEquals("must-revalidate,no-cache,no-store", response.getHeader(HttpHeader.CACHE_CONTROL.asString()));
 
-        response = newResponse();
+        response = getResponse();
 
         response.setStatus(200);
         assertEquals(200, response.getStatus());
         assertEquals(null, response.getReason());
 
-        response = newResponse();
+        response = getResponse();
 
         response.sendError(406, "Super Nanny");
         assertEquals(406, response.getStatus());
@@ -410,13 +566,38 @@
     }
 
     @Test
+    public void testWriteRuntimeIOException() throws Exception
+    {
+        Response response = getResponse();
+
+        PrintWriter writer = response.getWriter();
+        writer.println("test");
+        writer.flush();
+        Assert.assertFalse(writer.checkError());
+
+        Throwable cause = new IOException("problem at mill");
+        _channel.abort(cause);
+        writer.println("test");
+        Assert.assertTrue(writer.checkError());
+        try
+        {
+            writer.println("test");
+            Assert.fail();
+        }
+        catch(RuntimeIOException e)
+        {
+            Assert.assertEquals(cause,e.getCause());
+        }
+
+    }
+
+    @Test
     public void testEncodeRedirect()
             throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
         Request request = response.getHttpChannel().getRequest();
-        request.setServerName("myhost");
-        request.setServerPort(8888);
+        request.setAuthority("myhost",8888);
         request.setContextPath("/path");
 
         assertEquals("http://myhost:8888/path/info;param?query=0&more=1#target", response.encodeURL("http://myhost:8888/path/info;param?query=0&more=1#target"));
@@ -489,12 +670,12 @@
             {
                 for (int i=0;i<tests.length;i++)
                 {
-                    Response response = newResponse();
+                    Response response = getResponse();
                     Request request = response.getHttpChannel().getRequest();
 
-                    request.setServerName(host);
-                    request.setServerPort(port);
-                    request.setUri(new HttpURI("/path/info;param;jsessionid=12345?query=0&more=1#target"));
+                    request.setScheme("http");
+                    request.setAuthority(host,port);
+                    request.setURIPathQuery("/path/info;param;jsessionid=12345?query=0&more=1#target");
                     request.setContextPath("/path");
                     request.setRequestedSessionId("12345");
                     request.setRequestedSessionIdFromCookie(i>2);
@@ -518,7 +699,7 @@
     @Test
     public void testSetBufferSizeAfterHavingWrittenContent() throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
         response.setBufferSize(20 * 1024);
         response.getWriter().print("hello");
         try
@@ -535,7 +716,7 @@
     @Test
     public void testZeroContent() throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
         PrintWriter writer = response.getWriter();
         response.setContentLength(0);
         assertTrue(!response.isCommitted());
@@ -604,7 +785,7 @@
     @Test
     public void testAddCookie() throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
 
         Cookie cookie = new Cookie("name", "value");
         cookie.setDomain("domain");
@@ -614,16 +795,47 @@
 
         response.addCookie(cookie);
 
-        String set = response.getHttpFields().getStringField("Set-Cookie");
+        String set = response.getHttpFields().get("Set-Cookie");
 
         assertEquals("name=value;Version=1;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment", set);
     }
-
+    
+    /**
+     * Testing behavior documented in Chrome bug
+     * https://bugs.chromium.org/p/chromium/issues/detail?id=700618
+     */
+    @Test
+    public void testAddCookie_JavaxServletHttp() throws Exception
+    {
+        Response response = getResponse();
+    
+        Cookie cookie = new Cookie("foo", URLEncoder.encode("bar;baz", UTF_8.toString()));
+        cookie.setPath("/secure");
+    
+        response.addCookie(cookie);
+    
+        String set = response.getHttpFields().get("Set-Cookie");
+    
+        assertEquals("foo=bar%3Bbaz;Path=/secure", set);
+    }
+    
+    /**
+     * Testing behavior documented in Chrome bug
+     * https://bugs.chromium.org/p/chromium/issues/detail?id=700618
+     */
+    @Test
+    public void testAddCookie_JavaNet() throws Exception
+    {
+        HttpCookie cookie = new HttpCookie("foo", URLEncoder.encode("bar;baz", UTF_8.toString()));
+        cookie.setPath("/secure");
+        
+        assertEquals("foo=\"bar%3Bbaz\";$Path=\"/secure\"", cookie.toString());
+    }
 
     @Test
     public void testCookiesWithReset() throws Exception
     {
-        Response response = newResponse();
+        Response response = getResponse();
 
         Cookie cookie=new Cookie("name","value");
         cookie.setDomain("domain");
@@ -658,7 +870,7 @@
     @Test
     public void testFlushAfterFullContent() throws Exception
     {
-        Response response = _channel.getResponse();
+        Response response = getResponse();
         byte[] data = new byte[]{(byte)0xCA, (byte)0xFE};
         ServletOutputStream output = response.getOutputStream();
         response.setContentLength(data.length);
@@ -668,7 +880,6 @@
         output.flush();
     }
 
-
     @Test
     public void testSetCookie() throws Exception
     {
@@ -676,25 +887,25 @@
         HttpFields fields = response.getHttpFields();
 
         response.addSetCookie("null",null,null,null,-1,null,false,false,-1);
-        assertEquals("null=",fields.getStringField("Set-Cookie"));
+        assertEquals("null=",fields.get("Set-Cookie"));
 
         fields.clear();
-        
+
         response.addSetCookie("minimal","value",null,null,-1,null,false,false,-1);
-        assertEquals("minimal=value",fields.getStringField("Set-Cookie"));
+        assertEquals("minimal=value",fields.get("Set-Cookie"));
 
         fields.clear();
-        //test cookies with same name, domain and path, only 1 allowed
-        response.addSetCookie("everything","wrong","domain","path",0,"to be replaced",true,true,0);
+        //test cookies with same name, domain and path
+        response.addSetCookie("everything","something","domain","path",0,"noncomment",true,true,0);
         response.addSetCookie("everything","value","domain","path",0,"comment",true,true,0);
-        assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",fields.getStringField("Set-Cookie"));
         Enumeration<String> e =fields.getValues("Set-Cookie");
         assertTrue(e.hasMoreElements());
+        assertEquals("everything=something;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=noncomment",e.nextElement());
         assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
         assertFalse(e.hasMoreElements());
-        assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Expires"));
+        assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.get("Expires"));
         assertFalse(e.hasMoreElements());
-        
+
         //test cookies with same name, different domain
         fields.clear();
         response.addSetCookie("everything","other","domain1","path",0,"blah",true,true,0);
@@ -705,7 +916,7 @@
         assertTrue(e.hasMoreElements());
         assertEquals("everything=value;Version=1;Path=path;Domain=domain2;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
         assertFalse(e.hasMoreElements());
-        
+
         //test cookies with same name, same path, one with domain, one without
         fields.clear();
         response.addSetCookie("everything","other","domain1","path",0,"blah",true,true,0);
@@ -716,8 +927,8 @@
         assertTrue(e.hasMoreElements());
         assertEquals("everything=value;Version=1;Path=path;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
         assertFalse(e.hasMoreElements());
-        
-        
+
+
         //test cookies with same name, different path
         fields.clear();
         response.addSetCookie("everything","other","domain1","path1",0,"blah",true,true,0);
@@ -728,7 +939,7 @@
         assertTrue(e.hasMoreElements());
         assertEquals("everything=value;Version=1;Path=path2;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
         assertFalse(e.hasMoreElements());
-        
+
         //test cookies with same name, same domain, one with path, one without
         fields.clear();
         response.addSetCookie("everything","other","domain1","path1",0,"blah",true,true,0);
@@ -739,59 +950,61 @@
         assertTrue(e.hasMoreElements());
         assertEquals("everything=value;Version=1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
         assertFalse(e.hasMoreElements());
-        
+
         //test cookies same name only, no path, no domain
         fields.clear();
         response.addSetCookie("everything","other","","",0,"blah",true,true,0);
         response.addSetCookie("everything","value","","",0,"comment",true,true,0);
         e =fields.getValues("Set-Cookie");
         assertTrue(e.hasMoreElements());
+        assertEquals("everything=other;Version=1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
         assertEquals("everything=value;Version=1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
         assertFalse(e.hasMoreElements());
 
         fields.clear();
         response.addSetCookie("ev erything","va lue","do main","pa th",1,"co mment",true,true,1);
-        String setCookie=fields.getStringField("Set-Cookie");
+        String setCookie=fields.get("Set-Cookie");
         assertThat(setCookie,Matchers.startsWith("\"ev erything\"=\"va lue\";Version=1;Path=\"pa th\";Domain=\"do main\";Expires="));
         assertThat(setCookie,Matchers.endsWith(" GMT;Max-Age=1;Secure;HttpOnly;Comment=\"co mment\""));
 
         fields.clear();
         response.addSetCookie("name","value",null,null,-1,null,false,false,0);
-        setCookie=fields.getStringField("Set-Cookie");
+        setCookie=fields.get("Set-Cookie");
         assertEquals(-1,setCookie.indexOf("Version="));
         fields.clear();
         response.addSetCookie("name","v a l u e",null,null,-1,null,false,false,0);
-        setCookie=fields.getStringField("Set-Cookie");
+        setCookie=fields.get("Set-Cookie");
 
         fields.clear();
         response.addSetCookie("json","{\"services\":[\"cwa\", \"aa\"]}",null,null,-1,null,false,false,-1);
-        assertEquals("json=\"{\\\"services\\\":[\\\"cwa\\\", \\\"aa\\\"]}\"",fields.getStringField("Set-Cookie"));
+        assertEquals("json=\"{\\\"services\\\":[\\\"cwa\\\", \\\"aa\\\"]}\"",fields.get("Set-Cookie"));
 
         fields.clear();
         response.addSetCookie("name","value","domain",null,-1,null,false,false,-1);
         response.addSetCookie("name","other","domain",null,-1,null,false,false,-1);
-        assertEquals("name=other;Domain=domain",fields.getStringField("Set-Cookie"));
         response.addSetCookie("name","more","domain",null,-1,null,false,false,-1);
-        assertEquals("name=more;Domain=domain",fields.getStringField("Set-Cookie"));
+        e = fields.getValues("Set-Cookie");
+        assertTrue(e.hasMoreElements());
+        assertThat(e.nextElement(), Matchers.startsWith("name=value"));
+        assertThat(e.nextElement(), Matchers.startsWith("name=other"));
+        assertThat(e.nextElement(), Matchers.startsWith("name=more"));
+
         response.addSetCookie("foo","bar","domain",null,-1,null,false,false,-1);
         response.addSetCookie("foo","bob","domain",null,-1,null,false,false,-1);
-        assertEquals("name=more;Domain=domain",fields.getStringField("Set-Cookie"));
+        assertThat(fields.get("Set-Cookie"), Matchers.startsWith("name=value"));
 
-        e=fields.getValues("Set-Cookie");
-        assertEquals("name=more;Domain=domain",e.nextElement());
-        assertEquals("foo=bob;Domain=domain",e.nextElement());
 
         fields.clear();
         response.addSetCookie("name","value%=",null,null,-1,null,false,false,0);
-        setCookie=fields.getStringField("Set-Cookie");
+        setCookie=fields.get("Set-Cookie");
         assertEquals("name=value%=",setCookie);
-
     }
-    
-    private Response newResponse()
+
+    private Response getResponse()
     {
-        _channel.reset();
-        return new Response(_channel, _channel.getResponse().getHttpOutput());
+        _channel.recycle();
+        _channel.getRequest().setMetaData(new MetaData.Request("GET",new HttpURI("/path/info"),HttpVersion.HTTP_1_0,new HttpFields()));
+        return _channel.getResponse();
     }
 
     private static class TestSession extends HashedSession
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorHttpServerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorHttpServerTest.java
index 5bb3c83..c96071b 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorHttpServerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorHttpServerTest.java
@@ -18,11 +18,14 @@
 
 package org.eclipse.jetty.server;
 
+import org.eclipse.jetty.toolchain.test.AdvancedRunner;
 import org.junit.Before;
+import org.junit.runner.RunWith;
 
 /**
  * HttpServer Tester.
  */
+@RunWith(AdvancedRunner.class)
 public class ServerConnectorHttpServerTest extends HttpServerTestBase
 {
     @Before
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTest.java
index f1c804b..6783a8d 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTest.java
@@ -18,19 +18,17 @@
 
 package org.eclipse.jetty.server;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.lang.reflect.Field;
 import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
 import java.net.Socket;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.concurrent.atomic.AtomicLong;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
@@ -41,9 +39,21 @@
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.server.handler.DefaultHandler;
 import org.eclipse.jetty.server.handler.HandlerList;
+import org.eclipse.jetty.toolchain.test.OS;
 import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.hamcrest.Matchers;
 import org.junit.Test;
 
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+
 public class ServerConnectorTest
 {
     public static class ReuseInfoHandler extends AbstractHandler
@@ -74,9 +84,9 @@
             {
                 t.printStackTrace(out);
             }
-            
+
             out.printf("socket.getReuseAddress() = %b%n",socket.getReuseAddress());
-            
+
             baseRequest.setHandled(true);
         }
     }
@@ -92,7 +102,7 @@
         return new URI(String.format("http://%s:%d/",host,port));
     }
 
-    private String getResponse(URI uri) throws MalformedURLException, IOException
+    private String getResponse(URI uri) throws IOException
     {
         HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection();
         assertThat("Valid Response Code",http.getResponseCode(),anyOf(is(200),is(404)));
@@ -125,7 +135,12 @@
             String response = getResponse(uri);
             assertThat("Response",response,containsString("connector.getReuseAddress() = true"));
             assertThat("Response",response,containsString("connector._reuseAddress() = true"));
-            assertThat("Response",response,containsString("socket.getReuseAddress() = true"));
+
+            // Java on Windows is incapable of propagating reuse-address this to the opened socket.
+            if (!OS.IS_WINDOWS)
+            {
+                assertThat("Response",response,containsString("socket.getReuseAddress() = true"));
+            }
         }
         finally
         {
@@ -156,7 +171,12 @@
             String response = getResponse(uri);
             assertThat("Response",response,containsString("connector.getReuseAddress() = true"));
             assertThat("Response",response,containsString("connector._reuseAddress() = true"));
-            assertThat("Response",response,containsString("socket.getReuseAddress() = true"));
+
+            // Java on Windows is incapable of propagating reuse-address this to the opened socket.
+            if (!OS.IS_WINDOWS)
+            {
+                assertThat("Response",response,containsString("socket.getReuseAddress() = true"));
+            }
         }
         finally
         {
@@ -187,7 +207,59 @@
             String response = getResponse(uri);
             assertThat("Response",response,containsString("connector.getReuseAddress() = false"));
             assertThat("Response",response,containsString("connector._reuseAddress() = false"));
-            assertThat("Response",response,containsString("socket.getReuseAddress() = false"));
+
+            // Java on Windows is incapable of propagating reuse-address this to the opened socket.
+            if (!OS.IS_WINDOWS)
+            {
+                assertThat("Response",response,containsString("socket.getReuseAddress() = false"));
+            }
+        }
+        finally
+        {
+            server.stop();
+        }
+    }
+
+    @Test
+    public void testAddFirstConnectionFactory() throws Exception
+    {
+        Server server = new Server();
+        ServerConnector connector = new ServerConnector(server);
+        server.addConnector(connector);
+
+        HttpConnectionFactory http = new HttpConnectionFactory();
+        connector.addConnectionFactory(http);
+        ProxyConnectionFactory proxy = new ProxyConnectionFactory();
+        connector.addFirstConnectionFactory(proxy);
+
+        Collection<ConnectionFactory> factories = connector.getConnectionFactories();
+        assertEquals(2, factories.size());
+        assertSame(proxy, factories.iterator().next());
+        assertEquals(2, connector.getBeans(ConnectionFactory.class).size());
+        assertEquals(proxy.getProtocol(), connector.getDefaultProtocol());
+    }
+
+    @Test
+    public void testExceptionWhileAccepting() throws Exception
+    {
+        Server server = new Server();
+        try (StacklessLogging stackless = new StacklessLogging(AbstractConnector.class))
+        {
+            AtomicLong spins = new AtomicLong();
+            ServerConnector connector = new ServerConnector(server)
+            {
+                @Override
+                public void accept(int acceptorID) throws IOException
+                {
+                    spins.incrementAndGet();
+                    throw new IOException("explicitly_thrown_by_test");
+                }
+            };
+            server.addConnector(connector);
+            server.start();
+
+            Thread.sleep(1000);
+            assertThat(spins.get(), Matchers.lessThan(5L));
         }
         finally
         {
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTimeoutTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTimeoutTest.java
index 19c95f9..144f192 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTimeoutTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTimeoutTest.java
@@ -18,17 +18,30 @@
 
 package org.eclipse.jetty.server;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.hamcrest.Matchers.containsString;
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.net.Socket;
-import java.nio.charset.StandardCharsets;
 import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
 
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.server.session.SessionHandler;
 import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -38,7 +51,7 @@
     public void init() throws Exception
     {
         ServerConnector connector = new ServerConnector(_server,1,1);
-        connector.setIdleTimeout(MAX_IDLE_TIME); // 250 msec max idle
+        connector.setIdleTimeout(MAX_IDLE_TIME);
         startServer(connector);
     }
 
@@ -100,15 +113,91 @@
     private String getResponse(String request) throws UnsupportedEncodingException, IOException, InterruptedException
     {
         ServerConnector connector = (ServerConnector)_connector;
-        Socket socket = new Socket((String)null,connector.getLocalPort());
-        socket.setSoTimeout(10 * MAX_IDLE_TIME);
-        socket.getOutputStream().write(request.getBytes(StandardCharsets.UTF_8));
-        InputStream inputStream = socket.getInputStream();
-        long start = System.currentTimeMillis();
-        String response = IO.toString(inputStream);
-        long timeElapsed = System.currentTimeMillis() - start;
-        assertTrue("Time elapsed should be at least MAX_IDLE_TIME",timeElapsed > MAX_IDLE_TIME);
-        return response;
+
+        try (Socket socket = new Socket((String)null,connector.getLocalPort()))
+        {
+            socket.setSoTimeout(10 * MAX_IDLE_TIME);
+            socket.getOutputStream().write(request.getBytes(UTF_8));
+            InputStream inputStream = socket.getInputStream();
+            long start = System.currentTimeMillis();
+            String response = IO.toString(inputStream);
+            long timeElapsed = System.currentTimeMillis() - start;
+            assertTrue("Time elapsed should be at least MAX_IDLE_TIME",timeElapsed > MAX_IDLE_TIME);
+            return response;
+        }
     }
 
+    @Test
+    public void testHttpWriteIdleTimeout() throws Exception
+    {
+        _httpConfiguration.setBlockingTimeout(500);
+        configureServer(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+        Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
+        client.setSoTimeout(10000);
+    
+        Assert.assertFalse(client.isClosed());
+    
+        final OutputStream os = client.getOutputStream();
+        final InputStream is = client.getInputStream();
+        final StringBuilder response = new StringBuilder();
+    
+        CompletableFuture<Void> responseFuture = CompletableFuture.runAsync(() ->
+        {
+            try (InputStreamReader reader = new InputStreamReader(is, UTF_8))
+            {
+                int c;
+                while ((c = reader.read()) != -1)
+                {
+                    response.append((char) c);
+                }
+            }
+            catch (IOException e)
+            {
+                // Valid path (as connection is forcibly closed)
+                // t.printStackTrace(System.err);
+            }
+        });
+    
+        CompletableFuture<Void> requestFuture = CompletableFuture.runAsync(() ->
+        {
+            try
+            {
+                os.write((
+                        "POST /echo HTTP/1.0\r\n" +
+                                "host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
+                                "content-type: text/plain; charset=utf-8\r\n" +
+                                "content-length: 20\r\n" +
+                                "\r\n").getBytes("utf-8"));
+                os.flush();
+            
+                os.write("123456789\n".getBytes("utf-8"));
+                os.flush();
+                TimeUnit.SECONDS.sleep(1);
+                os.write("=========\n".getBytes("utf-8"));
+                os.flush();
+            }
+            catch (InterruptedException | IOException e)
+            {
+                // Valid path, as write of second half of content can fail
+                // e.printStackTrace(System.err);
+            }
+        });
+    
+        try (StacklessLogging scope = new StacklessLogging(HttpChannel.class))
+        {
+            requestFuture.get(2, TimeUnit.SECONDS);
+            responseFuture.get(3, TimeUnit.SECONDS);
+        
+            Assert.assertThat(response.toString(), containsString(" 500 "));
+            Assert.assertThat(response.toString(), Matchers.not(containsString("=========")));
+        }
+    }
 }
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java
index 8c3c3d2..baae4bb 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java
@@ -18,196 +18,217 @@
 
 package org.eclipse.jetty.server;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
+import java.io.BufferedReader;
+import java.io.Closeable;
 import java.io.InputStreamReader;
 import java.io.LineNumberReader;
 import java.io.OutputStream;
 import java.net.InetAddress;
 import java.net.Socket;
-import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jetty.util.thread.ShutdownThread;
+import org.junit.After;
+import org.junit.Assert;
 import org.junit.Test;
 
-/**
- * ShutdownMonitorTest
- */
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 public class ShutdownMonitorTest
 {
-    public class TestableServer extends Server
+    @After
+    public void dispose()
     {
-        boolean destroyed = false;
-        boolean stopped = false;
-        @Override
-        protected void doStop() throws Exception
-        {
-            stopped = true;
-            super.doStop();
-        }
-        @Override
-        public void destroy()
-        {
-            destroyed = true;
-            super.destroy();
-        }
-        @Override
-        protected void doStart() throws Exception
-        {
-            stopped = false;
-            destroyed  = false;
-            super.doStart();
-        }    
+        ShutdownMonitor.reset();
     }
-    
-    
+
     @Test
-    public void testShutdownMonitor() throws Exception
+    public void testStatus() throws Exception
     {
-        // test port and key assignment
-        ShutdownMonitor.getInstance().setPort(0);
-        ShutdownMonitor.getInstance().setExitVm(false);
-        ShutdownMonitor.getInstance().start();
-        String key = ShutdownMonitor.getInstance().getKey();
-        int port = ShutdownMonitor.getInstance().getPort();
+        ShutdownMonitor monitor = ShutdownMonitor.getInstance();
+        monitor.setDebug(true);
+        monitor.setPort(0);
+        monitor.setExitVm(false);
+        monitor.start();
+        String key = monitor.getKey();
+        int port = monitor.getPort();
+
+        // Try more than once to be sure that the ServerSocket has not been closed.
+        for (int i = 0; i < 2; ++i)
+        {
+            try (Socket socket = new Socket("localhost", port))
+            {
+                OutputStream output = socket.getOutputStream();
+                String command = "status";
+                output.write((key + "\r\n" + command + "\r\n").getBytes());
+                output.flush();
+
+                BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+                String reply = input.readLine();
+                assertEquals("OK", reply);
+                // Socket must be closed afterwards.
+                Assert.assertNull(input.readLine());
+            }
+        }
+    }
+
+    @Test
+    public void testStartStopDifferentPortDifferentKey() throws Exception
+    {
+        testStartStop(false);
+    }
+
+    @Test
+    public void testStartStopSamePortDifferentKey() throws Exception
+    {
+        testStartStop(true);
+    }
+
+    private void testStartStop(boolean reusePort) throws Exception
+    {
+        ShutdownMonitor monitor = ShutdownMonitor.getInstance();
+        monitor.setDebug(true);
+        monitor.setPort(0);
+        monitor.setExitVm(false);
+        monitor.start();
+        String key = monitor.getKey();
+        int port = monitor.getPort();
 
         // try starting a 2nd time (should be ignored)
-        ShutdownMonitor.getInstance().start();
+        monitor.start();
 
-        stop("stop", port,key,true);
-        assertTrue(!ShutdownMonitor.getInstance().isAlive());
+        stop("stop", port, key, true);
+        monitor.await();
+        assertTrue(!monitor.isAlive());
 
-        // should be able to change port and key because it is stopped
-        ShutdownMonitor.getInstance().setPort(0);
-        ShutdownMonitor.getInstance().setKey("foo");
-        ShutdownMonitor.getInstance().start();
+        // Should be able to change port and key because it is stopped.
+        monitor.setPort(reusePort ? port : 0);
+        String newKey = "foo";
+        monitor.setKey(newKey);
+        monitor.start();
 
-        key = ShutdownMonitor.getInstance().getKey();
-        port = ShutdownMonitor.getInstance().getPort();
-        assertTrue(ShutdownMonitor.getInstance().isAlive());
+        key = monitor.getKey();
+        assertEquals(newKey, key);
+        port = monitor.getPort();
+        assertTrue(monitor.isAlive());
 
-        stop("stop", port,key,true);
-        assertTrue(!ShutdownMonitor.getInstance().isAlive());
+        stop("stop", port, key, true);
+        monitor.await();
+        assertTrue(!monitor.isAlive());
     }
-    
-    
+
     @Test
     public void testForceStopCommand() throws Exception
     {
-        //create a testable Server with stop(), destroy() overridden to instrument
-        //start server
-        //call "forcestop" and check that server stopped but not destroyed
-        // test port and key assignment
-        System.setProperty("DEBUG", "true");
-        ShutdownMonitor.getInstance().setPort(0);
-        TestableServer server = new TestableServer();
-        server.start();
-       
-        //shouldn't be registered for shutdown on jvm
-        assertTrue(!ShutdownThread.isRegistered(server));
-        assertTrue(ShutdownMonitor.isRegistered(server));
-        
-        String key = ShutdownMonitor.getInstance().getKey();
-        int port = ShutdownMonitor.getInstance().getPort();
-        
-        stop("forcestop", port,key,true);
-        
-        assertTrue(!ShutdownMonitor.getInstance().isAlive());
-        assertTrue(server.stopped);
-        assertTrue(!server.destroyed);
-        assertTrue(!ShutdownThread.isRegistered(server));
-        assertTrue(!ShutdownMonitor.isRegistered(server));
+        ShutdownMonitor monitor = ShutdownMonitor.getInstance();
+        monitor.setDebug(true);
+        monitor.setPort(0);
+        monitor.setExitVm(false);
+        monitor.start();
+
+        try (CloseableServer server = new CloseableServer())
+        {
+            server.start();
+
+            //shouldn't be registered for shutdown on jvm
+            assertTrue(!ShutdownThread.isRegistered(server));
+            assertTrue(ShutdownMonitor.isRegistered(server));
+
+            String key = monitor.getKey();
+            int port = monitor.getPort();
+
+            stop("forcestop", port, key, true);
+            monitor.await();
+
+            assertTrue(!monitor.isAlive());
+            assertTrue(server.stopped);
+            assertTrue(!server.destroyed);
+            assertTrue(!ShutdownThread.isRegistered(server));
+            assertTrue(!ShutdownMonitor.isRegistered(server));
+        }
     }
-    
+
     @Test
     public void testOldStopCommandWithStopOnShutdownTrue() throws Exception
     {
-        
-        //create a testable Server with stop(), destroy() overridden to instrument
-        //call server.setStopAtShudown(true);
-        //start server
-        //call "stop" and check that server stopped but not destroyed
-        
-        //stop server
-        
-        //call server.setStopAtShutdown(false);
-        //start server
-        //call "stop" and check that the server is not stopped and not destroyed
-        System.setProperty("DEBUG", "true");
-        ShutdownMonitor.getInstance().setExitVm(false);
-      
-        ShutdownMonitor.getInstance().setPort(0);
-        TestableServer server = new TestableServer();
-        server.setStopAtShutdown(true);
-        server.start();
-        
-        //should be registered for shutdown on exit
-        assertTrue(ShutdownThread.isRegistered(server));
-        assertTrue(ShutdownMonitor.isRegistered(server));
-        
-        String key = ShutdownMonitor.getInstance().getKey();
-        int port = ShutdownMonitor.getInstance().getPort();
-        
-        stop("stop", port, key, true);
-        assertTrue(!ShutdownMonitor.getInstance().isAlive());
-        assertTrue(server.stopped);
-        assertTrue(!server.destroyed);
-        assertTrue(!ShutdownThread.isRegistered(server));
-        assertTrue(!ShutdownMonitor.isRegistered(server));
+        ShutdownMonitor monitor = ShutdownMonitor.getInstance();
+        monitor.setDebug(true);
+        monitor.setPort(0);
+        monitor.setExitVm(false);
+        monitor.start();
+
+        try (CloseableServer server = new CloseableServer())
+        {
+            server.setStopAtShutdown(true);
+            server.start();
+
+            //should be registered for shutdown on exit
+            assertTrue(ShutdownThread.isRegistered(server));
+            assertTrue(ShutdownMonitor.isRegistered(server));
+
+            String key = monitor.getKey();
+            int port = monitor.getPort();
+
+            stop("stop", port, key, true);
+            monitor.await();
+
+            assertTrue(!monitor.isAlive());
+            assertTrue(server.stopped);
+            assertTrue(!server.destroyed);
+            assertTrue(!ShutdownThread.isRegistered(server));
+            assertTrue(!ShutdownMonitor.isRegistered(server));
+        }
     }
-    
+
     @Test
     public void testOldStopCommandWithStopOnShutdownFalse() throws Exception
     {
-        //change so stopatshutdown is false, so stop does nothing in this case (as exitVm is false otherwise we couldn't run test)
-        ShutdownMonitor.getInstance().setExitVm(false);
-        System.setProperty("DEBUG", "true");
-        ShutdownMonitor.getInstance().setPort(0);
-        TestableServer server = new TestableServer();
-        server.setStopAtShutdown(false);
-        server.start();
-        
-        assertTrue(!ShutdownThread.isRegistered(server));
-        assertTrue(ShutdownMonitor.isRegistered(server));
-        
-        String key = ShutdownMonitor.getInstance().getKey();
-        int port = ShutdownMonitor.getInstance().getPort();
-        
-        stop ("stop", port, key, true);
-        assertTrue(!ShutdownMonitor.getInstance().isAlive());
-        assertTrue(!server.stopped);
-        assertTrue(!server.destroyed);
-        assertTrue(!ShutdownThread.isRegistered(server));
-        assertTrue(ShutdownMonitor.isRegistered(server));
+        ShutdownMonitor monitor = ShutdownMonitor.getInstance();
+        monitor.setDebug(true);
+        monitor.setPort(0);
+        monitor.setExitVm(false);
+        monitor.start();
+
+        try (CloseableServer server = new CloseableServer())
+        {
+            server.setStopAtShutdown(false);
+            server.start();
+
+            assertTrue(!ShutdownThread.isRegistered(server));
+            assertTrue(ShutdownMonitor.isRegistered(server));
+
+            String key = monitor.getKey();
+            int port = monitor.getPort();
+
+            stop("stop", port, key, true);
+            monitor.await();
+
+            assertTrue(!monitor.isAlive());
+            assertTrue(!server.stopped);
+            assertTrue(!server.destroyed);
+            assertTrue(!ShutdownThread.isRegistered(server));
+            assertTrue(ShutdownMonitor.isRegistered(server));
+        }
     }
-    
-    
-  
 
     public void stop(String command, int port, String key, boolean check) throws Exception
     {
-        System.out.printf("Attempting to send "+command+" to localhost:%d (%b)%n",port,check);
-        try (Socket s = new Socket(InetAddress.getByName("127.0.0.1"),port))
+        System.out.printf("Attempting to send " + command + " to localhost:%d (%b)%n", port, check);
+        try (Socket s = new Socket(InetAddress.getByName("127.0.0.1"), port))
         {
             // send stop command
             try (OutputStream out = s.getOutputStream())
             {
-                out.write((key + "\r\n"+command+"\r\n").getBytes());
+                out.write((key + "\r\n" + command + "\r\n").getBytes());
                 out.flush();
 
                 if (check)
                 {
-                    // wait a little
-                    TimeUnit.MILLISECONDS.sleep(600);
-
                     // check for stop confirmation
                     LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));
                     String response;
                     if ((response = lin.readLine()) != null)
-                    {
-                        assertEquals("Stopped",response);
-                    }
+                        assertEquals("Stopped", response);
                     else
                         throw new IllegalStateException("No stop confirmation");
                 }
@@ -215,4 +236,44 @@
         }
     }
 
+    public class CloseableServer extends Server implements Closeable
+    {
+        boolean destroyed = false;
+        boolean stopped = false;
+
+        @Override
+        protected void doStop() throws Exception
+        {
+            stopped = true;
+            super.doStop();
+        }
+
+        @Override
+        public void destroy()
+        {
+            destroyed = true;
+            super.destroy();
+        }
+
+        @Override
+        protected void doStart() throws Exception
+        {
+            stopped = false;
+            destroyed = false;
+            super.doStart();
+        }
+
+        @Override
+        public void close()
+        {
+            try
+            {
+                stop();
+            }
+            catch (Exception e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+    }
 }
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java
index 4f643b4..b69b83d 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java
@@ -54,7 +54,7 @@
             @Override
             public Connection newConnection(Connector connector, EndPoint endPoint)
             {
-                return configure(new HttpConnection(new HttpConfiguration(),connector,endPoint)
+                return configure(new HttpConnection(getHttpConfiguration(),connector,endPoint,getHttpCompliance(),isRecordHttpComplianceViolations())
                 {
                     @Override
                     public void onFillable()
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ThreadStarvationTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ThreadStarvationTest.java
new file mode 100644
index 0000000..f0086fd
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ThreadStarvationTest.java
@@ -0,0 +1,351 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.LeakTrackingByteBufferPool;
+import org.eclipse.jetty.io.ManagedSelector;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.eclipse.jetty.util.thread.Scheduler;
+import org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume;
+import org.eclipse.jetty.util.thread.strategy.ProduceExecuteConsume;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ThreadStarvationTest
+{
+    @Rule
+    public TestTracker tracker = new TestTracker();
+    
+    interface ConnectorProvider {
+        ServerConnector newConnector(Server server, int acceptors, int selectors);
+    }
+    
+    interface ClientSocketProvider {
+        Socket newSocket(String host, int port) throws IOException;
+    }
+    
+    @Parameterized.Parameters(name = "{0}")
+    public static List<Object[]> params()
+    {
+        List<Object[]> params = new ArrayList<>();
+        
+        // HTTP
+        ConnectorProvider http = (server, acceptors, selectors) -> new ServerConnector(server, acceptors, selectors);
+        ClientSocketProvider httpClient = (host, port) -> new Socket(host, port);
+        params.add(new Object[]{ "http", http, httpClient });
+        
+        // HTTPS/SSL/TLS
+        ConnectorProvider https = (server, acceptors, selectors) -> {
+            Path keystorePath = MavenTestingUtils.getTestResourcePath("keystore");
+            SslContextFactory sslContextFactory = new SslContextFactory();
+            sslContextFactory.setKeyStorePath(keystorePath.toString());
+            sslContextFactory.setKeyStorePassword("storepwd");
+            sslContextFactory.setKeyManagerPassword("keypwd");
+            sslContextFactory.setTrustStorePath(keystorePath.toString());
+            sslContextFactory.setTrustStorePassword("storepwd");
+            ByteBufferPool pool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged());
+    
+            HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory();
+            ServerConnector connector = new ServerConnector(server,(Executor)null,(Scheduler)null,
+                    pool, acceptors, selectors,
+                    AbstractConnectionFactory.getFactories(sslContextFactory,httpConnectionFactory));
+            SecureRequestCustomizer secureRequestCustomer = new SecureRequestCustomizer();
+            secureRequestCustomer.setSslSessionAttribute("SSL_SESSION");
+            httpConnectionFactory.getHttpConfiguration().addCustomizer(secureRequestCustomer);
+            return connector;
+        };
+        ClientSocketProvider httpsClient = new ClientSocketProvider()
+        {
+            private SSLContext sslContext;
+            {
+                try
+                {
+                    HttpsURLConnection.setDefaultHostnameVerifier((hostname, session)-> true);
+                    sslContext = SSLContext.getInstance("TLS");
+                    sslContext.init(null, SslContextFactory.TRUST_ALL_CERTS, new java.security.SecureRandom());
+                    HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
+                }
+                catch(Exception e)
+                {
+                    e.printStackTrace();
+                    throw new RuntimeException(e);
+                }
+            }
+            
+            @Override
+            public Socket newSocket(String host, int port) throws IOException
+            {
+                return sslContext.getSocketFactory().createSocket(host,port);
+            }
+        };
+        params.add(new Object[]{ "https/ssl/tls", https, httpsClient });
+        
+        return params;
+    }
+    
+    private final ConnectorProvider connectorProvider;
+    private final ClientSocketProvider clientSocketProvider;
+    private QueuedThreadPool _threadPool;
+    private Server _server;
+    private ServerConnector _connector;
+    private int _availableThreads;
+    
+    public ThreadStarvationTest(String testType, ConnectorProvider connectorProvider, ClientSocketProvider clientSocketProvider)
+    {
+        this.connectorProvider = connectorProvider;
+        this.clientSocketProvider = clientSocketProvider;
+    }
+    
+    private Server prepareServer(Handler handler)
+    {
+        int threads = 4;
+        _threadPool = new QueuedThreadPool();
+        _threadPool.setMinThreads(threads);
+        _threadPool.setMaxThreads(threads);
+        _threadPool.setDetailedDump(true);
+        _server = new Server(_threadPool);
+        int acceptors = 1;
+        int selectors = 1;
+        _connector = connectorProvider.newConnector(_server, acceptors, selectors);
+        _server.addConnector(_connector);
+        _server.setHandler(handler);
+        _availableThreads = threads - acceptors - selectors;
+        return _server;
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        _server.stop();
+    }
+
+    @Test
+    public void testReadInput() throws Exception
+    {
+        prepareServer(new ReadHandler()).start();
+
+        Socket client = clientSocketProvider.newSocket("localhost", _connector.getLocalPort());
+
+        OutputStream os = client.getOutputStream();
+        InputStream is = client.getInputStream();
+
+        String request = "" +
+                "GET / HTTP/1.0\r\n" +
+                "Host: localhost\r\n" +
+                "Content-Length: 10\r\n" +
+                "\r\n" +
+                "0123456789\r\n";
+        os.write(request.getBytes(StandardCharsets.UTF_8));
+        os.flush();
+
+        String response = IO.toString(is);
+        assertEquals(-1, is.read());
+        assertThat(response, containsString("200 OK"));
+        assertThat(response, containsString("Read Input 10"));
+    }
+
+    @Test
+    public void testEPCStarvation() throws Exception
+    {
+        testStarvation(new ExecuteProduceConsume.Factory());
+    }
+
+    @Test
+    public void testPECStarvation() throws Exception
+    {
+        testStarvation(new ProduceExecuteConsume.Factory());
+    }
+
+    private void testStarvation(ExecutionStrategy.Factory executionFactory) throws Exception
+    {
+        prepareServer(new ReadHandler());
+        _connector.setExecutionStrategyFactory(executionFactory);
+        _server.start();
+
+        Socket[] client = new Socket[_availableThreads + 1];
+        OutputStream[] os = new OutputStream[client.length];
+        InputStream[] is = new InputStream[client.length];
+
+        for (int i = 0; i < client.length; i++)
+        {
+            client[i] = clientSocketProvider.newSocket("localhost", _connector.getLocalPort());
+            client[i].setSoTimeout(10000);
+
+            os[i] = client[i].getOutputStream();
+            is[i] = client[i].getInputStream();
+
+            String request = "" +
+                    "PUT / HTTP/1.0\r\n" +
+                    "host: localhost\r\n" +
+                    "content-length: 10\r\n" +
+                    "\r\n" +
+                    "1";
+            os[i].write(request.getBytes(StandardCharsets.UTF_8));
+            os[i].flush();
+        }
+
+        Thread.sleep(500);
+
+        for (int i = 0; i < client.length; i++)
+        {
+            os[i].write(("234567890\r\n").getBytes(StandardCharsets.UTF_8));
+            os[i].flush();
+        }
+
+        Thread.sleep(500);
+
+        for (int i = 0; i < client.length; i++)
+        {
+            String response = IO.toString(is[i]);
+            assertEquals(-1, is[i].read());
+            assertThat(response, containsString("200 OK"));
+            assertThat(response, containsString("Read Input 10"));
+        }
+    }
+
+    @Test
+    public void testEPCExitsLowThreadsMode() throws Exception
+    {
+        prepareServer(new ReadHandler());
+        _threadPool.setMaxThreads(5);
+        _connector.setExecutionStrategyFactory(new ExecuteProduceConsume.Factory());
+        _server.start();
+
+        // Three idle threads in the pool here.
+        // The server will accept the socket in normal mode.
+        Socket client = clientSocketProvider.newSocket("localhost", _connector.getLocalPort());
+        client.setSoTimeout(10000);
+
+        Thread.sleep(500);
+
+        // Now steal two threads.
+        CountDownLatch[] latches = new CountDownLatch[2];
+        for (int i = 0; i < latches.length; ++i)
+        {
+            CountDownLatch latch = latches[i] = new CountDownLatch(1);
+            _threadPool.execute(() ->
+            {
+                try
+                {
+                    latch.await();
+                }
+                catch (InterruptedException ignored)
+                {
+                }
+            });
+        }
+
+        InputStream is = client.getInputStream();
+        OutputStream os = client.getOutputStream();
+
+        String request = "" +
+                "PUT / HTTP/1.0\r\n" +
+                "Host: localhost\r\n" +
+                "Content-Length: 10\r\n" +
+                "\r\n" +
+                "1";
+        os.write(request.getBytes(StandardCharsets.UTF_8));
+        os.flush();
+
+        Thread.sleep(500);
+
+        // Request did not send the whole body, Handler
+        // is blocked reading, zero idle threads here,
+        // EPC is in low threads mode.
+        for (ManagedSelector selector : _connector.getSelectorManager().getBeans(ManagedSelector.class))
+        {
+            ExecuteProduceConsume executionStrategy = (ExecuteProduceConsume)selector.getExecutionStrategy();
+            assertTrue(executionStrategy.isLowOnThreads());
+        }
+
+        // Release the stolen threads.
+        for (CountDownLatch latch : latches)
+            latch.countDown();
+        Thread.sleep(500);
+
+        // Send the rest of the body to unblock the reader thread.
+        // This will be run directly by the selector thread,
+        // which then will exit the low threads mode.
+        os.write("234567890".getBytes(StandardCharsets.UTF_8));
+        os.flush();
+
+        Thread.sleep(500);
+
+        for (ManagedSelector selector : _connector.getSelectorManager().getBeans(ManagedSelector.class))
+        {
+            ExecuteProduceConsume executionStrategy = (ExecuteProduceConsume)selector.getExecutionStrategy();
+            assertFalse(executionStrategy.isLowOnThreads());
+        }
+    }
+
+    protected static class ReadHandler extends AbstractHandler
+    {
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            response.setStatus(200);
+
+            int l = request.getContentLength();
+            int r = 0;
+            while (r < l)
+            {
+                if (request.getInputStream().read() >= 0)
+                    r++;
+            }
+
+            response.getOutputStream().write(("Read Input " + r + "\r\n").getBytes());
+        }
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/AllowSymLinkAliasCheckerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/AllowSymLinkAliasCheckerTest.java
new file mode 100644
index 0000000..65bdd8b
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/AllowSymLinkAliasCheckerTest.java
@@ -0,0 +1,210 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assume.assumeNoException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystemException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.resource.PathResource;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AllowSymLinkAliasCheckerTest
+{
+    @Parameterized.Parameters(name = "{0}")
+    public static List<Object[]> params()
+    {
+        List<Object[]> data = new ArrayList<>();
+
+        String dirs[] = {"/testdir/", "/testdirlnk/", "/testdirprefixlnk/", "/testdirsuffixlnk/",
+                "/testdirwraplnk/"};
+
+        for (String dirname : dirs)
+        {
+            data.add(new Object[]{dirname, 200, "text/html", "Directory: " + dirname});
+            data.add(new Object[]{dirname + "testfile.txt", 200, "text/plain", "Hello TestFile"});
+            data.add(new Object[]{dirname + "testfilelnk.txt", 200, "text/plain", "Hello TestFile"});
+            data.add(new Object[]{dirname + "testfileprefixlnk.txt", 200, "text/plain", "Hello TestFile"});
+        }
+
+        return data;
+    }
+
+    private Server server;
+    private LocalConnector localConnector;
+    private Path rootPath;
+
+    @Before
+    public void setup() throws Exception
+    {
+        setupRoot();
+        setupServer();
+    }
+
+    @After
+    public void teardown() throws Exception
+    {
+        if( server != null )
+        {
+            server.stop();
+        }
+    }
+
+    private void setupRoot() throws IOException
+    {
+        rootPath = MavenTestingUtils.getTargetTestingPath(AllowSymLinkAliasCheckerTest.class.getSimpleName());
+        FS.ensureEmpty(rootPath);
+
+        Path testdir = rootPath.resolve("testdir");
+        FS.ensureDirExists(testdir);
+
+        try
+        {
+            // If we used testdir (Path) from above, these symlinks
+            // would point to an absolute path.
+
+            // Create a relative symlink testdirlnk -> testdir
+            Files.createSymbolicLink(rootPath.resolve("testdirlnk"), new File("testdir").toPath());
+            // Create a relative symlink testdirprefixlnk -> ./testdir
+            Files.createSymbolicLink(rootPath.resolve("testdirprefixlnk"), new File("./testdir").toPath());
+            // Create a relative symlink testdirsuffixlnk -> testdir/
+            Files.createSymbolicLink(rootPath.resolve("testdirsuffixlnk"), new File("testdir/").toPath());
+            // Create a relative symlink testdirwraplnk -> ./testdir/
+            Files.createSymbolicLink(rootPath.resolve("testdirwraplnk"), new File("./testdir/").toPath());
+        }
+        catch (UnsupportedOperationException | FileSystemException e)
+        {
+            // If unable to create symlink, no point testing the rest.
+            // This is the path that Microsoft Windows takes.
+            assumeNoException(e);
+        }
+
+        Path testfileTxt = testdir.resolve("testfile.txt");
+        Files.createFile(testfileTxt);
+        try (OutputStream out = Files.newOutputStream(testfileTxt))
+        {
+            out.write("Hello TestFile".getBytes(StandardCharsets.UTF_8));
+        }
+
+        try
+        {
+            Path testfileTxtLnk = testdir.resolve("testfilelnk.txt");
+            // Create a relative symlink testfilelnk.txt -> testfile.txt
+            // If we used testfileTxt (Path) from above, this symlink
+            // would point to an absolute path.
+            Files.createSymbolicLink(testfileTxtLnk, new File("testfile.txt").toPath());
+        }
+        catch (UnsupportedOperationException | FileSystemException e)
+        {
+            // If unable to create symlink, no point testing the rest.
+            // This is the path that Microsoft Windows takes.
+            assumeNoException(e);
+        }
+
+        try
+        {
+            Path testfilePrefixTxtLnk = testdir.resolve("testfileprefixlnk.txt");
+            // Create a relative symlink testfileprefixlnk.txt -> ./testfile.txt
+            // If we used testfileTxt (Path) from above, this symlink
+            // would point to an absolute path.
+            Files.createSymbolicLink(testfilePrefixTxtLnk, new File("./testfile.txt").toPath());
+        }
+        catch (UnsupportedOperationException | FileSystemException e)
+        {
+            // If unable to create symlink, no point testing the rest.
+            // This is the path that Microsoft Windows takes.
+            assumeNoException(e);
+        }
+    }
+
+    private void setupServer() throws Exception
+    {
+        // Setup server
+        server = new Server();
+        localConnector = new LocalConnector(server);
+        server.addConnector(localConnector);
+
+        ResourceHandler fileResourceHandler = new ResourceHandler();
+        fileResourceHandler.setDirectoriesListed(true);
+        fileResourceHandler.setWelcomeFiles(new String[]{"index.html"});
+        fileResourceHandler.setEtags(true);
+
+        ContextHandler fileResourceContext = new ContextHandler();
+        fileResourceContext.setContextPath("/");
+        fileResourceContext.setAllowNullPathInfo(true);
+        fileResourceContext.setHandler(fileResourceHandler);
+        fileResourceContext.setBaseResource(new PathResource(rootPath));
+
+        fileResourceContext.clearAliasChecks();
+        fileResourceContext.addAliasCheck(new AllowSymLinkAliasChecker());
+
+        server.setHandler(fileResourceContext);
+        server.start();
+    }
+
+    @Parameterized.Parameter(0)
+    public String requestURI;
+    @Parameterized.Parameter(1)
+    public int expectedResponseStatus;
+    @Parameterized.Parameter(2)
+    public String expectedResponseContentType;
+    @Parameterized.Parameter(3)
+    public String expectedResponseContentContains;
+
+    public AllowSymLinkAliasCheckerTest()
+    {
+    }
+
+    @Test(timeout = 5000)
+    public void testAccess() throws Exception
+    {
+        HttpTester.Request request = HttpTester.newRequest();
+
+        request.setMethod("GET");
+        request.setHeader("Host", "tester");
+        request.setURI(requestURI);
+
+        String responseString = localConnector.getResponse(BufferUtil.toString(request.generate()));
+        assertThat("Response status code", responseString, startsWith("HTTP/1.1 " + expectedResponseStatus + " "));
+        assertThat("Response Content-Type", responseString, containsString("\nContent-Type: " + expectedResponseContentType));
+        assertThat("Response", responseString, containsString(expectedResponseContentContains));
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/BadRequestLogHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/BadRequestLogHandlerTest.java
index e7998fa..4df7fba 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/BadRequestLogHandlerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/BadRequestLogHandlerTest.java
@@ -18,8 +18,8 @@
 
 package org.eclipse.jetty.server.handler;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -74,7 +74,8 @@
         @Override
         public void log(Request request, Response response)
         {
-            captured.add(String.format("%s %s %s %03d",request.getMethod(),request.getUri().toString(),request.getProtocol(),response.getStatus()));
+            int status = response.getCommittedMetaData().getStatus();
+            captured.add(String.format("%s %s %s %03d",request.getMethod(),request.getHttpURI(),request.getProtocol(),status));
         }
     }
     
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/BufferedResponseHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/BufferedResponseHandlerTest.java
new file mode 100644
index 0000000..4d6804f
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/BufferedResponseHandlerTest.java
@@ -0,0 +1,273 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Resource Handler test
+ */
+public class BufferedResponseHandlerTest
+{
+    private static Server _server;
+    private static HttpConfiguration _config;
+    private static LocalConnector _local;
+    private static ContextHandler _contextHandler;
+    private static BufferedResponseHandler _bufferedHandler;
+    private static TestHandler _test;
+
+    @BeforeClass
+    public static void setUp() throws Exception
+    {
+        _server = new Server();
+        _config = new HttpConfiguration();
+        _config.setOutputBufferSize(1024);
+        _config.setOutputAggregationSize(256);
+        _local = new LocalConnector(_server,new HttpConnectionFactory(_config));
+        _server.addConnector(_local);
+
+        _bufferedHandler = new BufferedResponseHandler();
+        _bufferedHandler.getPathIncludeExclude().include("/include/*");
+        _bufferedHandler.getPathIncludeExclude().exclude("*.exclude");
+        _bufferedHandler.getMimeIncludeExclude().exclude("text/excluded");
+        _bufferedHandler.setHandler(_test=new TestHandler());
+        
+        _contextHandler = new ContextHandler("/ctx");
+        _contextHandler.setHandler(_bufferedHandler);
+
+        _server.setHandler(_contextHandler);
+        _server.start();
+        
+        // BufferedResponseHandler.LOG.setDebugEnabled(true);
+    }
+
+    @AfterClass
+    public static void tearDown() throws Exception
+    {
+        _server.stop();
+    }
+
+    @Before
+    public void before()
+    {
+        _test._bufferSize=-1;
+        _test._mimeType=null;
+        _test._content=new byte[128];
+        Arrays.fill(_test._content,(byte)'X');
+        _test._content[_test._content.length-1]='\n';
+        _test._writes=10;
+        _test._flush=false;
+        _test._close=false;
+        _test._reset=false;
+    }
+
+    @Test
+    public void testNormal() throws Exception
+    {
+        String response = _local.getResponse("GET /ctx/path HTTP/1.1\r\nHost: localhost\r\n\r\n");
+        assertThat(response,containsString(" 200 OK"));
+        assertThat(response,containsString("Write: 0"));
+        assertThat(response,containsString("Write: 7"));
+        assertThat(response,not(containsString("Content-Length: ")));
+        assertThat(response,not(containsString("Write: 8")));
+        assertThat(response,not(containsString("Write: 9")));
+        assertThat(response,not(containsString("Written: true")));
+    }
+
+    @Test
+    public void testIncluded() throws Exception
+    {
+        String response = _local.getResponse("GET /ctx/include/path HTTP/1.1\r\nHost: localhost\r\n\r\n");
+        assertThat(response,containsString(" 200 OK"));
+        assertThat(response,containsString("Write: 0"));
+        assertThat(response,containsString("Write: 9"));
+        assertThat(response,containsString("Written: true"));
+    }
+
+    @Test
+    public void testExcludedByPath() throws Exception
+    {
+        String response = _local.getResponse("GET /ctx/include/path.exclude HTTP/1.1\r\nHost: localhost\r\n\r\n");
+        assertThat(response,containsString(" 200 OK"));
+        assertThat(response,containsString("Write: 0"));
+        assertThat(response,containsString("Write: 7"));
+        assertThat(response,not(containsString("Content-Length: ")));
+        assertThat(response,not(containsString("Write: 8")));
+        assertThat(response,not(containsString("Write: 9")));
+        assertThat(response,not(containsString("Written: true")));
+    }
+
+    @Test
+    public void testExcludedByMime() throws Exception
+    {
+        _test._mimeType="text/excluded";
+        String response = _local.getResponse("GET /ctx/include/path HTTP/1.1\r\nHost: localhost\r\n\r\n");
+        assertThat(response,containsString(" 200 OK"));
+        assertThat(response,containsString("Write: 0"));
+        assertThat(response,containsString("Write: 7"));
+        assertThat(response,not(containsString("Content-Length: ")));
+        assertThat(response,not(containsString("Write: 8")));
+        assertThat(response,not(containsString("Write: 9")));
+        assertThat(response,not(containsString("Written: true")));
+    }
+
+    @Test
+    public void testFlushed() throws Exception
+    {
+        _test._flush=true;
+        String response = _local.getResponse("GET /ctx/include/path HTTP/1.1\r\nHost: localhost\r\n\r\n");
+        assertThat(response,containsString(" 200 OK"));
+        assertThat(response,containsString("Write: 0"));
+        assertThat(response,containsString("Write: 9"));
+        assertThat(response,containsString("Written: true"));
+    }
+
+    @Test
+    public void testClosed() throws Exception
+    {
+        _test._close=true;
+        String response = _local.getResponse("GET /ctx/include/path HTTP/1.1\r\nHost: localhost\r\n\r\n");
+        assertThat(response,containsString(" 200 OK"));
+        assertThat(response,containsString("Write: 0"));
+        assertThat(response,containsString("Write: 9"));
+        assertThat(response,not(containsString("Written: true")));
+    }
+
+    @Test
+    public void testBufferSizeSmall() throws Exception
+    {
+        _test._bufferSize=16;
+        String response = _local.getResponse("GET /ctx/include/path HTTP/1.1\r\nHost: localhost\r\n\r\n");
+        assertThat(response,containsString(" 200 OK"));
+        assertThat(response,containsString("Write: 0"));
+        assertThat(response,containsString("Write: 9"));
+        assertThat(response,containsString("Written: true"));
+    }
+
+    @Test
+    public void testBufferSizeBig() throws Exception
+    {
+        _test._bufferSize=4096;
+        String response = _local.getResponse("GET /ctx/include/path HTTP/1.1\r\nHost: localhost\r\n\r\n");
+        assertThat(response,containsString(" 200 OK"));
+        assertThat(response,containsString("Content-Length: "));
+        assertThat(response,containsString("Write: 0"));
+        assertThat(response,containsString("Write: 9"));
+        assertThat(response,containsString("Written: true"));
+    }
+
+    @Test
+    public void testOne() throws Exception
+    {
+        _test._writes=1;
+        String response = _local.getResponse("GET /ctx/include/path HTTP/1.1\r\nHost: localhost\r\n\r\n");
+        assertThat(response,containsString(" 200 OK"));
+        assertThat(response,containsString("Content-Length: "));
+        assertThat(response,containsString("Write: 0"));
+        assertThat(response,not(containsString("Write: 1")));
+        assertThat(response,containsString("Written: true"));
+    }
+
+    @Test
+    public void testFlushEmpty() throws Exception
+    {
+        _test._writes=1;
+        _test._flush=true;
+        _test._close=false;
+        _test._content = new byte[0];
+        String response = _local.getResponse("GET /ctx/include/path HTTP/1.1\r\nHost: localhost\r\n\r\n");
+        assertThat(response,containsString(" 200 OK"));
+        assertThat(response,containsString("Content-Length: "));
+        assertThat(response,containsString("Write: 0"));
+        assertThat(response,not(containsString("Write: 1")));
+        assertThat(response,containsString("Written: true"));
+    }
+
+    @Test
+    public void testReset() throws Exception
+    {
+        _test._reset=true;
+        String response = _local.getResponse("GET /ctx/include/path HTTP/1.1\r\nHost: localhost\r\n\r\n");
+        assertThat(response,containsString(" 200 OK"));
+        assertThat(response,containsString("Write: 0"));
+        assertThat(response,containsString("Write: 9"));
+        assertThat(response,containsString("Written: true"));
+        assertThat(response,not(containsString("RESET")));
+    }
+    
+    public static class TestHandler extends AbstractHandler
+    {
+        int _bufferSize;
+        String _mimeType;
+        byte[] _content;
+        int _writes;
+        boolean _flush;
+        boolean _close;
+        boolean _reset;
+
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            
+            if (_bufferSize>0)
+                response.setBufferSize(_bufferSize);
+            if (_mimeType!=null)
+                response.setContentType(_mimeType);
+
+            if (_reset)
+            {
+                response.getOutputStream().print("THIS WILL BE RESET");
+                response.getOutputStream().flush();
+                response.getOutputStream().print("THIS WILL BE RESET");
+                response.resetBuffer();
+            }
+            for (int i=0;i<_writes;i++)
+            {
+                response.addHeader("Write",Integer.toString(i));
+                response.getOutputStream().write(_content);
+                if (_flush)
+                    response.getOutputStream().flush();
+            }
+
+            if (_close)
+                response.getOutputStream().close();
+            response.addHeader("Written","true");
+        }  
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerGetResourceTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerGetResourceTest.java
index e3cc042..e074b23 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerGetResourceTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerGetResourceTest.java
@@ -18,10 +18,12 @@
 
 package org.eclipse.jetty.server.handler;
 
+import static org.hamcrest.Matchers.nullValue;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -83,12 +85,26 @@
             
             Files.createSymbolicLink(new File(docroot,"other").toPath(),new File("../transit").toPath());
             Files.createSymbolicLink(transit.toPath(),otherroot.toPath());
+            
+            // /web/logs -> /var/logs -> /media/internal/logs
+            // where /media/internal -> /media/internal-physical/
+            new File(docroot,"media/internal-physical/logs").mkdirs();
+            Files.createSymbolicLink(new File(docroot,"media/internal").toPath(),new File(docroot,"media/internal-physical").toPath());
+            new File(docroot,"var").mkdir();
+            Files.createSymbolicLink(new File(docroot,"var/logs").toPath(),new File(docroot,"media/internal/logs").toPath());
+            new File(docroot,"web").mkdir();
+            Files.createSymbolicLink(new File(docroot,"web/logs").toPath(),new File(docroot,"var/logs").toPath()); 
+            new File(docroot,"media/internal-physical/logs/file.log").createNewFile();
+            
+            System.err.println("docroot="+docroot);
         }
         
         OS_ALIAS_SUPPORTED = new File(sub, "TEXTFI~1.TXT").exists(); 
         
         server = new Server();
         context =new ContextHandler("/");
+        context.clearAliasChecks();
+        context.addAliasCheck(new ContextHandler.ApproveNonExistentDirectoryAliases());
         context.setBaseResource(Resource.newResource(docroot));
         context.addAliasCheck(new ContextHandler.AliasCheck()
         {
@@ -122,7 +138,7 @@
         try
         {
             context.getResource(path);
-            fail();
+            fail("Expected " + MalformedURLException.class);
         }
         catch(MalformedURLException e)
         {
@@ -131,7 +147,7 @@
         try
         {
             context.getServletContext().getResource(path);
-            fail();
+            fail("Expected " + MalformedURLException.class);
         }
         catch(MalformedURLException e)
         {
@@ -300,17 +316,20 @@
     @Test
     public void testSlashSlash() throws Exception
     {
+        File expected = new File(docroot, OS.separators("subdir/data.txt"));
+        URL expectedUrl = expected.toURI().toURL();
+        
         String path="//subdir/data.txt";
         Resource resource=context.getResource(path);
-        assertNull(resource);
+        assertThat("Resource: " + resource, resource, nullValue());
         URL url=context.getServletContext().getResource(path);
-        assertNull(url);
-        
+        assertThat("Resource: " + url, url, nullValue());
+
         path="/subdir//data.txt";
         resource=context.getResource(path);
-        assertNull(resource);
+        assertThat("Resource: " + resource, resource, nullValue());
         url=context.getServletContext().getResource(path);
-        assertNull(url);
+        assertThat("Resource: " + url, url, nullValue());
     }
 
     @Test
@@ -353,8 +372,8 @@
     @Test
     public void testSymlinkKnown() throws Exception
     {
-        if (!OS.IS_UNIX)
-            return;
+        Assume.assumeTrue(OS.IS_UNIX);
+        
         try
         {
             allowSymlinks.set(true);
@@ -376,6 +395,29 @@
         } 
         
     }
+    
+    @Test
+    public void testSymlinkNested() throws Exception
+    {
+        Assume.assumeTrue(OS.IS_UNIX);
+        
+        try
+        {
+            allowSymlinks.set(true);
+
+            final String path="/web/logs/file.log";
+
+            Resource resource=context.getResource(path);
+            assertNotNull(resource);
+            assertEquals("file.log",resource.getFile().getName());
+            assertTrue(resource.exists());
+        }
+        finally
+        {
+            allowSymlinks.set(false);
+        } 
+
+    }
 
     @Test
     public void testSymlinkUnknown() throws Exception
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DebugHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DebugHandlerTest.java
new file mode 100644
index 0000000..2aa4c25
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DebugHandlerTest.java
@@ -0,0 +1,183 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyStore;
+import java.util.concurrent.Executor;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManagerFactory;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.LeakTrackingByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.server.AbstractConnectionFactory;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.Scheduler;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DebugHandlerTest
+{
+    public final static HostnameVerifier __hostnameverifier = new HostnameVerifier()
+    {
+        public boolean verify(String hostname, SSLSession session)
+        {
+            return true;
+        }
+    };
+    
+    private SSLContext sslContext;
+    private Server server;
+    private URI serverURI;
+    private URI secureServerURI;
+    
+    @SuppressWarnings("deprecation")
+    private DebugHandler debugHandler;
+    private ByteArrayOutputStream capturedLog;
+    
+    @SuppressWarnings("deprecation")
+    @Before
+    public void startServer() throws Exception
+    {
+        server = new Server();
+        
+        ServerConnector httpConnector = new ServerConnector(server);
+        httpConnector.setPort(0);
+        server.addConnector(httpConnector);
+        
+        File keystorePath = MavenTestingUtils.getTestResourceFile("keystore");
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath(keystorePath.getAbsolutePath());
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setKeyManagerPassword("keypwd");
+        sslContextFactory.setTrustStorePath(keystorePath.getAbsolutePath());
+        sslContextFactory.setTrustStorePassword("storepwd");
+        ByteBufferPool pool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged());
+        ServerConnector sslConnector = new ServerConnector(server,
+                (Executor)null,
+                (Scheduler)null, pool, 1, 1, 
+                AbstractConnectionFactory.getFactories(sslContextFactory,new HttpConnectionFactory()));
+        
+        server.addConnector(sslConnector);
+        
+        debugHandler = new DebugHandler();
+        capturedLog = new ByteArrayOutputStream();
+        debugHandler.setOutputStream(capturedLog);
+        debugHandler.setHandler(new AbstractHandler()
+            {
+                @Override
+                public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+                {
+                    baseRequest.setHandled(true);
+                    response.setStatus(HttpStatus.OK_200);
+                }
+            });
+        server.setHandler(debugHandler);
+        server.start();
+        
+        String host = httpConnector.getHost();
+        if(host == null) host = "localhost";
+        
+        serverURI = URI.create(String.format("http://%s:%d/", host, httpConnector.getLocalPort()));
+        secureServerURI = URI.create(String.format("https://%s:%d/", host, sslConnector.getLocalPort()));
+        
+        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
+        try (InputStream stream = sslContextFactory.getKeyStoreResource().getInputStream())
+        {
+            keystore.load(stream, "storepwd".toCharArray());
+        }
+        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        trustManagerFactory.init(keystore);
+        sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
+
+        try
+        {
+            HttpsURLConnection.setDefaultHostnameVerifier(__hostnameverifier);
+            SSLContext sc = SSLContext.getInstance("TLS");
+            sc.init(null, SslContextFactory.TRUST_ALL_CERTS, new java.security.SecureRandom());
+            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+        }
+        catch(Exception e)
+        {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+    
+    @After
+    public void stopServer() throws Exception
+    {
+        server.stop();
+    }
+    
+    @Test
+    public void testThreadName() throws IOException
+    {
+        HttpURLConnection http = (HttpURLConnection) serverURI.resolve("/foo/bar?a=b").toURL().openConnection();
+        assertThat("Response Code", http.getResponseCode(), is(200));
+        
+        String log = capturedLog.toString(StandardCharsets.UTF_8.name());
+        String expectedThreadName = String.format("//%s:%s/foo/bar?a=b",serverURI.getHost(),serverURI.getPort());
+        assertThat("ThreadName", log, containsString(expectedThreadName));
+        // Look for bad/mangled/duplicated schemes
+        assertThat("ThreadName", log, not(containsString("http:"+expectedThreadName)));
+        assertThat("ThreadName", log, not(containsString("https:"+expectedThreadName)));
+    }
+    
+    @Test
+    public void testSecureThreadName() throws IOException
+    {
+        HttpURLConnection http = (HttpURLConnection) secureServerURI.resolve("/foo/bar?a=b").toURL().openConnection();
+        assertThat("Response Code", http.getResponseCode(), is(200));
+        
+        String log = capturedLog.toString(StandardCharsets.UTF_8.name());
+        String expectedThreadName = String.format("https://%s:%s/foo/bar?a=b",secureServerURI.getHost(),secureServerURI.getPort());
+        assertThat("ThreadName", log, containsString(expectedThreadName));
+        // Look for bad/mangled/duplicated schemes
+        assertThat("ThreadName", log, not(containsString("http:"+expectedThreadName)));
+        assertThat("ThreadName", log, not(containsString("https:"+expectedThreadName)));
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/HandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/HandlerTest.java
new file mode 100644
index 0000000..dbe0a1b
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/HandlerTest.java
@@ -0,0 +1,342 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+
+public class HandlerTest
+{
+
+    @Test
+    public void testWrapperSetServer()
+    {
+        Server s=new Server();
+        HandlerWrapper a = new HandlerWrapper();
+        HandlerWrapper b = new HandlerWrapper();
+        HandlerWrapper c = new HandlerWrapper();
+        a.setHandler(b);
+        b.setHandler(c);
+        
+        a.setServer(s);
+        assertThat(b.getServer(),equalTo(s));
+        assertThat(c.getServer(),equalTo(s));
+    }
+
+    @Test
+    public void testWrapperServerSet()
+    {
+        Server s=new Server();
+        HandlerWrapper a = new HandlerWrapper();
+        HandlerWrapper b = new HandlerWrapper();
+        HandlerWrapper c = new HandlerWrapper();
+        a.setServer(s);
+        b.setHandler(c);
+        a.setHandler(b);
+        
+        assertThat(b.getServer(),equalTo(s));
+        assertThat(c.getServer(),equalTo(s));
+    }
+
+    @Test
+    public void testWrapperThisLoop()
+    {
+        HandlerWrapper a = new HandlerWrapper();
+        
+        try
+        {
+            a.setHandler(a);
+            fail();
+        }
+        catch(IllegalStateException e)
+        {
+            assertThat(e.getMessage(),containsString("loop"));
+        }
+    }
+    
+    @Test
+    public void testWrapperSimpleLoop()
+    {
+        HandlerWrapper a = new HandlerWrapper();
+        HandlerWrapper b = new HandlerWrapper();
+        
+        a.setHandler(b);
+        
+        try
+        {
+            b.setHandler(a);
+            fail();
+        }
+        catch(IllegalStateException e)
+        {
+            assertThat(e.getMessage(),containsString("loop"));
+        }
+    }
+    
+    @Test
+    public void testWrapperDeepLoop()
+    {
+        HandlerWrapper a = new HandlerWrapper();
+        HandlerWrapper b = new HandlerWrapper();
+        HandlerWrapper c = new HandlerWrapper();
+        
+        a.setHandler(b);
+        b.setHandler(c);
+        
+        try
+        {
+            c.setHandler(a);
+            fail();
+        }
+        catch(IllegalStateException e)
+        {
+            assertThat(e.getMessage(),containsString("loop"));
+        }
+    }
+    
+    @Test
+    public void testWrapperChainLoop()
+    {
+        HandlerWrapper a = new HandlerWrapper();
+        HandlerWrapper b = new HandlerWrapper();
+        HandlerWrapper c = new HandlerWrapper();
+        
+        a.setHandler(b);
+        c.setHandler(a);
+        
+        try
+        {
+            b.setHandler(c);
+            fail();
+        }
+        catch(IllegalStateException e)
+        {
+            assertThat(e.getMessage(),containsString("loop"));
+        }
+    }
+
+
+    @Test
+    public void testCollectionSetServer()
+    {
+        Server s=new Server();
+        HandlerCollection a = new HandlerCollection();
+        HandlerCollection b = new HandlerCollection();
+        HandlerCollection b1 = new HandlerCollection();
+        HandlerCollection b2 = new HandlerCollection();
+        HandlerCollection c = new HandlerCollection();
+        HandlerCollection c1 = new HandlerCollection();
+        HandlerCollection c2 = new HandlerCollection();
+        
+        a.addHandler(b);
+        a.addHandler(c);
+        b.setHandlers(new Handler[]{b1,b2});
+        c.setHandlers(new Handler[]{c1,c2});
+        a.setServer(s);
+        
+        assertThat(b.getServer(),equalTo(s));
+        assertThat(c.getServer(),equalTo(s));
+        assertThat(b1.getServer(),equalTo(s));
+        assertThat(b2.getServer(),equalTo(s));
+        assertThat(c1.getServer(),equalTo(s));
+        assertThat(c2.getServer(),equalTo(s));
+    }
+
+    @Test
+    public void testCollectionServerSet()
+    {
+        Server s=new Server();
+        HandlerCollection a = new HandlerCollection();
+        HandlerCollection b = new HandlerCollection();
+        HandlerCollection b1 = new HandlerCollection();
+        HandlerCollection b2 = new HandlerCollection();
+        HandlerCollection c = new HandlerCollection();
+        HandlerCollection c1 = new HandlerCollection();
+        HandlerCollection c2 = new HandlerCollection();
+        
+        a.setServer(s);
+        a.addHandler(b);
+        a.addHandler(c);
+        b.setHandlers(new Handler[]{b1,b2});
+        c.setHandlers(new Handler[]{c1,c2});
+        
+        assertThat(b.getServer(),equalTo(s));
+        assertThat(c.getServer(),equalTo(s));
+        assertThat(b1.getServer(),equalTo(s));
+        assertThat(b2.getServer(),equalTo(s));
+        assertThat(c1.getServer(),equalTo(s));
+        assertThat(c2.getServer(),equalTo(s));
+    }
+    
+    @Test
+    public void testCollectionThisLoop()
+    {
+        HandlerCollection a = new HandlerCollection();
+        
+        try
+        {
+            a.addHandler(a);
+            fail();
+        }
+        catch(IllegalStateException e)
+        {
+            assertThat(e.getMessage(),containsString("loop"));
+        }
+    }
+    
+    @Test
+    public void testCollectionDeepLoop()
+    {
+        HandlerCollection a = new HandlerCollection();
+        HandlerCollection b = new HandlerCollection();
+        HandlerCollection b1 = new HandlerCollection();
+        HandlerCollection b2 = new HandlerCollection();
+        HandlerCollection c = new HandlerCollection();
+        HandlerCollection c1 = new HandlerCollection();
+        HandlerCollection c2 = new HandlerCollection();
+        
+        a.addHandler(b);
+        a.addHandler(c);
+        b.setHandlers(new Handler[]{b1,b2});
+        c.setHandlers(new Handler[]{c1,c2});
+
+        try
+        {
+            b2.addHandler(a);
+            fail();
+        }
+        catch(IllegalStateException e)
+        {
+            assertThat(e.getMessage(),containsString("loop"));
+        }
+    }
+    
+    @Test
+    public void testCollectionChainLoop()
+    {
+        HandlerCollection a = new HandlerCollection();
+        HandlerCollection b = new HandlerCollection();
+        HandlerCollection b1 = new HandlerCollection();
+        HandlerCollection b2 = new HandlerCollection();
+        HandlerCollection c = new HandlerCollection();
+        HandlerCollection c1 = new HandlerCollection();
+        HandlerCollection c2 = new HandlerCollection();
+        
+        a.addHandler(c);
+        b.setHandlers(new Handler[]{b1,b2});
+        c.setHandlers(new Handler[]{c1,c2});
+        b2.addHandler(a);
+
+        try
+        {
+            a.addHandler(b);
+            fail();
+        }
+        catch(IllegalStateException e)
+        {
+            assertThat(e.getMessage(),containsString("loop"));
+        }
+    }
+    
+    @Test
+    public void testInsertWrapperTail()
+    {
+        HandlerWrapper a = new HandlerWrapper();
+        HandlerWrapper b = new HandlerWrapper();
+        
+        a.insertHandler(b);
+        assertThat(a.getHandler(),equalTo(b));
+        assertThat(b.getHandler(),nullValue());
+    }
+    
+    @Test
+    public void testInsertWrapper()
+    {
+        HandlerWrapper a = new HandlerWrapper();
+        HandlerWrapper b = new HandlerWrapper();
+        HandlerWrapper c = new HandlerWrapper();
+        
+        a.insertHandler(c);
+        a.insertHandler(b);
+        assertThat(a.getHandler(),equalTo(b));
+        assertThat(b.getHandler(),equalTo(c));
+        assertThat(c.getHandler(),nullValue());
+    }
+    
+    @Test
+    public void testInsertWrapperChain()
+    {
+        HandlerWrapper a = new HandlerWrapper();
+        HandlerWrapper b = new HandlerWrapper();
+        HandlerWrapper c = new HandlerWrapper();
+        HandlerWrapper d = new HandlerWrapper();
+        
+        a.insertHandler(d);
+        b.insertHandler(c);
+        a.insertHandler(b);
+        assertThat(a.getHandler(),equalTo(b));
+        assertThat(b.getHandler(),equalTo(c));
+        assertThat(c.getHandler(),equalTo(d));
+        assertThat(d.getHandler(),nullValue());
+    }
+    
+    @Test
+    public void testInsertWrapperBadChain()
+    {
+        HandlerWrapper a = new HandlerWrapper();
+        HandlerWrapper b = new HandlerWrapper();
+        HandlerWrapper c = new HandlerWrapper();
+        HandlerWrapper d = new HandlerWrapper();
+        
+        a.insertHandler(d);
+        b.insertHandler(c);
+        c.setHandler(new AbstractHandler()
+        {   
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {                
+            }
+        });
+        
+        try
+        {
+            a.insertHandler(b);
+            fail();
+        }
+        catch(IllegalArgumentException e)
+        {
+            assertThat(e.getMessage(),containsString("bad tail"));
+        }
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/RequestLogHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/RequestLogHandlerTest.java
index 2a34ecf..7ac269f 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/RequestLogHandlerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/RequestLogHandlerTest.java
@@ -18,8 +18,9 @@
 
 package org.eclipse.jetty.server.handler;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -41,6 +42,8 @@
 
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.HttpChannelState;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.RequestLog;
 import org.eclipse.jetty.server.Response;
@@ -50,6 +53,7 @@
 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.log.StacklessLogging;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -75,7 +79,8 @@
         @Override
         public void log(Request request, Response response)
         {
-            captured.add(String.format("%s %s %s %03d",request.getMethod(),request.getRequestURI(),request.getProtocol(),response.getStatus()));
+            int status = response.getCommittedMetaData().getStatus();
+            captured.add(String.format("%s %s %s %03d",request.getMethod(),request.getRequestURI(),request.getProtocol(),status));
         }
     }
 
@@ -372,6 +377,7 @@
 
     /**
      * Test a RequestLogHandler at the end of a HandlerCollection. all other configuration on server at defaults.
+     * @throws Exception if test failure
      */
     @Test(timeout = 4000)
     public void testLogHandlerCollection() throws Exception
@@ -433,8 +439,77 @@
         }
     }
 
+    @Test(timeout = 4000)
+    public void testMultipleLogHandlers() throws Exception
+    {
+        Server server = new Server();
+        ServerConnector connector = new ServerConnector(server);
+        connector.setPort(0);
+        server.setConnectors(new Connector[]{connector});
+
+        List<CaptureLog> captureLogs = new ArrayList<>();
+        List<Handler> handlerList = new ArrayList<>();
+        handlerList.add(testHandler);
+
+        for (int i = 0; i < 4; ++i) {
+            CaptureLog captureLog = new CaptureLog();
+            captureLogs.add(captureLog);
+            RequestLogHandler requestLog = new RequestLogHandler();
+            requestLog.setRequestLog(captureLog);
+            handlerList.add(requestLog);
+        }
+
+        HandlerCollection handlers = new HandlerCollection();
+        handlers.setHandlers(handlerList.toArray(new Handler[0]));
+        server.setHandler(handlers);
+
+        try
+        {
+            server.start();
+
+            String host = connector.getHost();
+            if (host == null)
+            {
+                host = "localhost";
+            }
+            int port = connector.getLocalPort();
+
+            URI serverUri = new URI("http",null,host,port,requestPath,null,null);
+
+            // Make call to test handler
+            HttpURLConnection connection = (HttpURLConnection)serverUri.toURL().openConnection();
+            try
+            {
+                connection.setAllowUserInteraction(false);
+
+                // log response status code
+                int statusCode = connection.getResponseCode();
+                LOG.debug("Response Status Code: {}",statusCode);
+
+                if (statusCode == 200)
+                {
+                    // collect response message and log it
+                    String content = getResponseContent(connection);
+                    LOG.debug("Response Content: {}",content);
+                }
+            }
+            finally
+            {
+                connection.disconnect();
+            }
+
+            for (CaptureLog captureLog:captureLogs)
+                assertRequestLog(captureLog);
+        }
+        finally
+        {
+            server.stop();
+        }
+    }
+
     /**
      * Test a RequestLogHandler at the end of a HandlerCollection and also with the default ErrorHandler as server bean in place.
+     * @throws Exception if test failure
      */
     @Test(timeout = 4000)
     public void testLogHandlerCollection_ErrorHandler_ServerBean() throws Exception
@@ -501,6 +576,7 @@
 
     /**
      * Test a RequestLogHandler at the end of a HandlerCollection and also with the ErrorHandler in place.
+     * @throws Exception if test failure
      */
     @Test(timeout=4000)
     public void testLogHandlerCollection_AltErrorHandler() throws Exception
@@ -574,6 +650,7 @@
     
     /**
      * Test a RequestLogHandler at the end of a HandlerCollection and also with the ErrorHandler in place.
+     * @throws Exception if test failure
      */
     @Test(timeout=4000)
     public void testLogHandlerCollection_OKErrorHandler() throws Exception
@@ -647,6 +724,7 @@
     
     /**
      * Test a RequestLogHandler at the end of a HandlerCollection and also with the ErrorHandler in place.
+     * @throws Exception if test failure
      */
     @Test(timeout=4000)
     public void testLogHandlerCollection_DispatchErrorHandler() throws Exception
@@ -660,9 +738,9 @@
         server.addBean(errorDispatcher);
         
         ContextHandlerCollection contexts = new ContextHandlerCollection();
-        ContextHandler errorContext = new ContextHandler("errorok");
+        ContextHandler errorContext = new ContextHandler("/errorok");
         errorContext.setHandler(new OKErrorHandler());
-        ContextHandler testContext = new ContextHandler("test");
+        ContextHandler testContext = new ContextHandler("/test");
         testContext.setHandler(testHandler);
         contexts.addHandler(errorContext);
         contexts.addHandler(testContext);
@@ -734,8 +812,8 @@
         requestLog.setHandler(testHandler);
 
         server.setHandler(requestLog);
-
-        try
+        
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class,HttpChannelState.class))
         {
             server.start();
 
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/RequestLogTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/RequestLogTest.java
new file mode 100644
index 0000000..daea4f9
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/RequestLogTest.java
@@ -0,0 +1,314 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.concurrent.Exchanger;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.AbstractNCSARequestLog;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RequestLogTest
+{
+    Exchanger<String> _log;
+    Server _server;
+    LocalConnector _connector;
+    
+
+    @Before
+    public void before() throws Exception
+    {
+        _log = new Exchanger<String>();
+        _server = new Server();
+        _connector = new LocalConnector(_server);
+        _server.addConnector(_connector);
+        _server.setRequestLog(new Log());
+        _server.setHandler(new TestHandler());
+        _server.start();
+    }
+    
+    @After
+    public void after() throws Exception
+    {
+
+        _server.stop();
+    }
+    
+    
+    @Test
+    public void testNotHandled() throws Exception
+    {
+        _connector.getResponses("GET /foo HTTP/1.0\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("GET /foo HTTP/1.0\" 404 "));
+    }
+
+    @Test
+    public void testRequestLine() throws Exception
+    {
+        _connector.getResponses("GET /foo?data=1 HTTP/1.0\nhost: host:80\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        // TODO should be without host (https://bugs.eclipse.org/bugs/show_bug.cgi?id=480276)
+        // assertThat(log,containsString("GET /foo?data=1 HTTP/1.0\" 200 "));
+        assertThat(log,containsString("GET //host:80/foo?data=1 HTTP/1.0\" 200 "));
+        
+        _connector.getResponses("GET //bad/foo?data=1 HTTP/1.0\n\n");
+        log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("GET //bad/foo?data=1 HTTP/1.0\" 200 "));
+                
+        _connector.getResponses("GET http://host:80/foo?data=1 HTTP/1.0\n\n");
+        log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("GET http://host:80/foo?data=1 HTTP/1.0\" 200 "));   
+    }
+    
+    @Test
+    public void testSmallData() throws Exception
+    {
+        _connector.getResponses("GET /foo?data=42 HTTP/1.0\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("GET /foo?"));
+        assertThat(log,containsString(" 200 42 "));
+    }
+    
+    @Test
+    public void testBigData() throws Exception
+    {
+        _connector.getResponses("GET /foo?data=102400 HTTP/1.0\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("GET /foo?"));
+        assertThat(log,containsString(" 200 102400 "));
+    }
+    
+    @Test
+    public void testStatus() throws Exception
+    {
+        _connector.getResponses("GET /foo?status=206 HTTP/1.0\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("GET /foo?"));
+        assertThat(log,containsString(" 206 0 "));
+    }
+    
+    @Test
+    public void testStatusData() throws Exception
+    {
+        _connector.getResponses("GET /foo?status=206&data=42 HTTP/1.0\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("GET /foo?"));
+        assertThat(log,containsString(" 206 42 "));
+    }
+    
+    @Test
+    public void testBadRequest() throws Exception
+    {
+        _connector.getResponses("XXXXXXXXXXXX\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("\"- - -\""));
+        assertThat(log,containsString(" 400 0 "));
+    }
+    
+    @Test
+    public void testBadCharacter() throws Exception
+    {
+        _connector.getResponses("METHOD /f\00o HTTP/1.0\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("\"- - -\""));
+        assertThat(log,containsString(" 400 0 "));
+    }
+    
+    @Test
+    public void testBadVersion() throws Exception
+    {
+        _connector.getResponses("METHOD /foo HTTP/9\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("\"- - -\""));
+        assertThat(log,containsString(" 400 0 "));
+    }
+    
+    @Test
+    public void testLongURI() throws Exception
+    {
+        char[] chars = new char[10000];
+        Arrays.fill(chars,'o');
+        String ooo = new String(chars);
+        _connector.getResponses("METHOD /f"+ooo+" HTTP/1.0\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("\"- - -\""));
+        assertThat(log,containsString(" 414 0 "));
+    }
+    
+    @Test
+    public void testLongHeader() throws Exception
+    {
+        char[] chars = new char[10000];
+        Arrays.fill(chars,'o');
+        String ooo = new String(chars);
+        _connector.getResponses("METHOD /foo HTTP/1.0\name: f+"+ooo+"\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("\"METHOD /foo HTTP/1.0\""));
+        assertThat(log,containsString(" 431 0 "));
+    }
+    
+    @Test
+    public void testBadRequestNoHost() throws Exception
+    {
+        _connector.getResponses("GET /foo HTTP/1.1\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("GET /foo "));
+        assertThat(log,containsString(" 400 0 "));
+    }
+
+    @Test
+    public void testUseragentWithout() throws Exception
+    {
+        _connector.getResponses("GET http://[:1]/foo HTTP/1.1\nReferer: http://other.site\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("GET http://[:1]/foo "));
+        assertThat(log,containsString(" 400 0 \"http://other.site\" \"-\" - "));
+    }
+
+    @Test
+    public void testUseragentWith() throws Exception
+    {
+        _connector.getResponses("GET http://[:1]/foo HTTP/1.1\nReferer: http://other.site\nUser-Agent: Mozilla/5.0 (test)\n\n");
+        String log = _log.exchange(null,5,TimeUnit.SECONDS);
+        assertThat(log,containsString("GET http://[:1]/foo "));
+        assertThat(log,containsString(" 400 0 \"http://other.site\" \"Mozilla/5.0 (test)\" - "));
+    }
+
+    private class Log extends AbstractNCSARequestLog
+    {
+        {
+            super.setExtended(true);
+            super.setLogLatency(true);
+            super.setLogCookies(true);
+        }
+
+        @Override
+        protected boolean isEnabled()
+        {
+            return true;
+        }
+
+        @Override
+        public void write(String requestEntry) throws IOException
+        {
+            try
+            {
+                _log.exchange(requestEntry);
+            }
+            catch(Exception e)
+            {
+                e.printStackTrace();
+            }
+        }
+    }
+    
+    private class TestHandler extends AbstractHandler
+    {
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            String q = request.getQueryString();
+            if (q==null)
+                return;
+            
+            baseRequest.setHandled(true);
+            for (String action : q.split("\\&"))
+            {
+                String[] param = action.split("=");
+                String name=param[0];
+                String value=param.length>1?param[1]:null;
+                switch(name)
+                {
+                    case "status":
+                    {
+                        response.setStatus(Integer.parseInt(value));
+                        break;
+                    }
+                        
+                    case "data":
+                    {
+                        int data = Integer.parseInt(value);
+                        PrintWriter out = response.getWriter();
+                        
+                        int w=0;
+                        while (w<data)
+                        {
+                            if ((data-w)>17)
+                            {
+                                w+=17;
+                                out.print("0123456789ABCDEF\n");
+                            }
+                            else
+                            {
+                                w++;
+                                out.print("\n");
+                            }
+                        }
+                        break;
+                    }
+
+                    case "throw":
+                    {
+                        try
+                        {
+                            throw (Throwable)(Class.forName(value).newInstance());
+                        }
+                        catch(ServletException | IOException | Error | RuntimeException e)
+                        {
+                            throw e;
+                        }
+                        catch(Throwable e)
+                        {
+                            throw new ServletException(e);
+                        }
+                    }
+                    case "flush":
+                    {
+                        response.flushBuffer();
+                        break;
+                    }
+                    
+                    case "read":
+                    {
+                        InputStream in = request.getInputStream();
+                        while (in.read()>=0);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerRangeTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerRangeTest.java
index 00b973e..e3fd501 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerRangeTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerRangeTest.java
@@ -18,8 +18,6 @@
 
 package org.eclipse.jetty.server.handler;
 
-import static org.hamcrest.Matchers.is;
-
 import java.io.File;
 import java.io.FileWriter;
 import java.io.InputStream;
@@ -38,7 +36,9 @@
 import org.junit.Ignore;
 import org.junit.Test;
 
-@Ignore("Unfixed range bug")
+import static org.hamcrest.Matchers.is;
+
+@Ignore("Unfixed range bug - Issue #107")
 public class ResourceHandlerRangeTest
 {
     private static Server server;
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java
index cefd479..6920b8f 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java
@@ -18,6 +18,12 @@
 
 package org.eclipse.jetty.server.handler;
 
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
@@ -25,22 +31,21 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.Socket;
-import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.LocalConnector;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
-import org.eclipse.jetty.toolchain.test.SimpleRequest;
 import org.eclipse.jetty.toolchain.test.annotation.Slow;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.IO;
-import org.hamcrest.Matchers;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.Before;
@@ -58,6 +63,7 @@
     private static Server _server;
     private static HttpConfiguration _config;
     private static ServerConnector _connector;
+    private static LocalConnector _local;
     private static ContextHandler _contextHandler;
     private static ResourceHandler _resourceHandler;
 
@@ -111,7 +117,9 @@
         _config.setOutputBufferSize(2048);
         _connector = new ServerConnector(_server,new HttpConnectionFactory(_config));
 
-        _server.setConnectors(new Connector[] { _connector });
+        _local = new LocalConnector(_server);
+        
+        _server.setConnectors(new Connector[] { _connector, _local });
 
         _resourceHandler = new ResourceHandler();
         _resourceHandler.setMinAsyncContentLength(4096);
@@ -140,45 +148,64 @@
     @Test
     public void testMissing() throws Exception
     {
-        SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
-        Assert.assertNotNull("missing jetty.css",sr.getString("/resource/jetty-dir.css"));
+        String rawResponse = _local.getResponse("GET /resource/jetty-dir.css HTTP/1.0\r\n\r\n");
+        HttpTester.Response response = HttpTester.parseResponse(rawResponse);
+        Assert.assertNotNull("missing jetty-dir.css",response.getContent());
     }
 
     @Test
     public void testSimple() throws Exception
     {
-        SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
-        Assert.assertEquals("simple text",sr.getString("/resource/simple.txt"));
+        String rawResponse = _local.getResponse("GET /resource/simple.txt HTTP/1.0\r\n\r\n");
+        HttpTester.Response response = HttpTester.parseResponse(rawResponse);
+        assertEquals("simple text",response.getContent());
+    }
+
+    @Test
+    public void testHeaders() throws Exception
+    {
+        String response = _local.getResponses("GET /resource/simple.txt HTTP/1.0\r\n\r\n");
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("Content-Type: text/plain"));
+        assertThat(response,containsString("Last-Modified: "));
+        assertThat(response,containsString("Content-Length: 11"));
+        assertThat(response,containsString("Server: Jetty"));
+        assertThat(response,containsString("simple text"));
     }
 
     @Test
     public void testBigFile() throws Exception
     {
         _config.setOutputBufferSize(2048);
-        SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
-        String response = sr.getString("/resource/big.txt");
-        Assert.assertThat(response,Matchers.startsWith("     1\tThis is a big file"));
-        Assert.assertThat(response,Matchers.endsWith("   400\tThis is a big file" + LN));
+    
+        String rawResponse = _local.getResponse("GET /resource/big.txt HTTP/1.0\r\n\r\n");
+        HttpTester.Response response = HttpTester.parseResponse(rawResponse);
+        String content = response.getContent();
+        
+        assertThat(content,startsWith("     1\tThis is a big file"));
+        assertThat(content,endsWith("   400\tThis is a big file" + LN));
     }
 
     @Test
     public void testBigFileBigBuffer() throws Exception
     {
         _config.setOutputBufferSize(16 * 1024);
-        SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
-        String response = sr.getString("/resource/big.txt");
-        Assert.assertThat(response,Matchers.startsWith("     1\tThis is a big file"));
-        Assert.assertThat(response,Matchers.endsWith("   400\tThis is a big file" + LN));
+        String rawResponse = _local.getResponse("GET /resource/big.txt HTTP/1.0\r\n\r\n");
+        HttpTester.Response response = HttpTester.parseResponse(rawResponse);
+        String content = response.getContent();
+        assertThat(content,startsWith("     1\tThis is a big file"));
+        assertThat(content,endsWith("   400\tThis is a big file" + LN));
     }
 
     @Test
     public void testBigFileLittleBuffer() throws Exception
     {
         _config.setOutputBufferSize(8);
-        SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
-        String response = sr.getString("/resource/big.txt");
-        Assert.assertThat(response,Matchers.startsWith("     1\tThis is a big file"));
-        Assert.assertThat(response,Matchers.endsWith("   400\tThis is a big file" + LN));
+        String rawResponse = _local.getResponse("GET /resource/big.txt HTTP/1.0\r\n\r\n");
+        HttpTester.Response response = HttpTester.parseResponse(rawResponse);
+        String content = response.getContent();
+        assertThat(content,startsWith("     1\tThis is a big file"));
+        assertThat(content,endsWith("   400\tThis is a big file" + LN));
     }
 
     @Test
@@ -189,9 +216,9 @@
             socket.getOutputStream().write("GET /resource/bigger.txt HTTP/1.0\n\n".getBytes());
             Thread.sleep(1000);
             String response = IO.toString(socket.getInputStream());
-            Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 200 OK"));
-            Assert.assertThat(response,Matchers.containsString("   400\tThis is a big file" + LN + "     1\tThis is a big file"));
-            Assert.assertThat(response,Matchers.endsWith("   400\tThis is a big file" + LN));
+            assertThat(response,startsWith("HTTP/1.1 200 OK"));
+            assertThat(response,containsString("   400\tThis is a big file" + LN + "     1\tThis is a big file"));
+            assertThat(response,endsWith("   400\tThis is a big file" + LN));
         }
     }
     
@@ -234,9 +261,9 @@
                 // System.err.println(++i+": "+BufferUtil.toDetailString(buffer));
             }
 
-            Assert.assertEquals('E',buffer.get(buffer.limit()-4));
-            Assert.assertEquals('N',buffer.get(buffer.limit()-3));
-            Assert.assertEquals('D',buffer.get(buffer.limit()-2));
+            assertEquals('E',buffer.get(buffer.limit()-4));
+            assertEquals('N',buffer.get(buffer.limit()-3));
+            assertEquals('D',buffer.get(buffer.limit()-2));
             
         }
     }
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ScopedHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ScopedHandlerTest.java
index 8c251db..220af52 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ScopedHandlerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ScopedHandlerTest.java
@@ -27,6 +27,7 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -82,6 +83,9 @@
     @Test
     public void testDouble() throws Exception
     {
+        Request request = new Request(null,null);
+        Response response = new Response(null,null);
+        
         TestHandler handler0 = new TestHandler("0");
         OtherHandler handlerA = new OtherHandler("A");
         TestHandler handler1 = new TestHandler("1");
@@ -90,7 +94,7 @@
         handlerA.setHandler(handler1);
         handler1.setHandler(handlerB);
         handler0.start();
-        handler0.handle("target",null,null,null);
+        handler0.handle("target",request,request,response);
         handler0.stop();
         String history=_history.toString();
         assertEquals(">S0>S1>W0>HA>W1>HB<HB<W1<HA<W0<S1<S0",history);
@@ -99,6 +103,9 @@
     @Test
     public void testTriple() throws Exception
     {
+        Request request = new Request(null,null);
+        Response response = new Response(null,null);
+        
         TestHandler handler0 = new TestHandler("0");
         OtherHandler handlerA = new OtherHandler("A");
         TestHandler handler1 = new TestHandler("1");
@@ -111,7 +118,7 @@
         handlerB.setHandler(handler2);
         handler2.setHandler(handlerC);
         handler0.start();
-        handler0.handle("target",null,null,null);
+        handler0.handle("target",request,request,response);
         handler0.stop();
         String history=_history.toString();
         assertEquals(">S0>S1>S2>W0>HA>W1>HB>W2>HC<HC<W2<HB<W1<HA<W0<S2<S1<S0",history);
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/SecuredRedirectHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/SecuredRedirectHandlerTest.java
index 4737751..700208c 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/SecuredRedirectHandlerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/SecuredRedirectHandlerTest.java
@@ -18,8 +18,9 @@
 
 package org.eclipse.jetty.server.handler;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java
index b1368ba..3d8836f 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java
@@ -18,110 +18,119 @@
 
 package org.eclipse.jetty.server.handler;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.when;
-
-import java.net.Inet4Address;
+import java.io.IOException;
+import java.io.OutputStream;
 import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
 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.HttpStatus;
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.util.component.AbstractLifeCycle;
 import org.eclipse.jetty.util.component.LifeCycle;
-import org.junit.Before;
+import org.junit.Assert;
 import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 public class ShutdownHandlerTest
 {
-    @Mock private Request baseRequest;
-    @Mock private HttpServletRequest request;
-    @Mock private HttpServletResponse response;
-
-    private Server server  = new Server(0);
+    private Server server;
+    private ServerConnector connector;
     private String shutdownToken = "asdlnsldgnklns";
 
-    // class under test
-    private ShutdownHandler shutdownHandler;
-
-    @Before
-    public void startServer() throws Exception
+    public void start(HandlerWrapper wrapper) throws Exception
     {
-        MockitoAnnotations.initMocks(this);
-        shutdownHandler = new ShutdownHandler(shutdownToken);
-        server.setHandler(shutdownHandler);
+        server = new Server();
+        connector = new ServerConnector(server);
+        server.addConnector(connector);
+        Handler shutdown = new ShutdownHandler(shutdownToken);
+        Handler handler = shutdown;
+        if (wrapper != null)
+        {
+            wrapper.setHandler(shutdown);
+            handler = wrapper;
+        }
+        server.setHandler(handler);
         server.start();
     }
 
     @Test
-    public void shutdownServerWithCorrectTokenAndIPTest() throws Exception
+    public void testShutdownServerWithCorrectTokenAndIP() throws Exception
     {
-        setDefaultExpectations();
-        final CountDownLatch countDown = new CountDownLatch(1);
-        server.addLifeCycleListener(new AbstractLifeCycle.Listener ()
+        start(null);
+
+        CountDownLatch stopLatch = new CountDownLatch(1);
+        server.addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener()
         {
-
-            public void lifeCycleStarting(LifeCycle event)
-            {
-            }
-
-            public void lifeCycleStarted(LifeCycle event)
-            {
-            }
-
-            public void lifeCycleFailure(LifeCycle event, Throwable cause)
-            {
-            }
-
-            public void lifeCycleStopping(LifeCycle event)
-            {
-            }
-
+            @Override
             public void lifeCycleStopped(LifeCycle event)
             {
-                countDown.countDown();
+                stopLatch.countDown();
             }
-
         });
-        when(baseRequest.getRemoteInetSocketAddress()).thenReturn(new InetSocketAddress(Inet4Address.getLoopbackAddress(),45454));
-        shutdownHandler.handle("/shutdown",baseRequest,request,response);
-        boolean stopped = countDown.await(1000, TimeUnit.MILLISECONDS); //wait up to 1 sec to stop
-        assertTrue("Server lifecycle stop listener called", stopped);
-        assertEquals("Server should be stopped","STOPPED",server.getState());
+
+        HttpTester.Response response = shutdown(shutdownToken);
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        Assert.assertTrue(stopLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertEquals(AbstractLifeCycle.STOPPED, server.getState());
     }
 
     @Test
-    public void wrongTokenTest() throws Exception
+    public void testWrongToken() throws Exception
     {
-        setDefaultExpectations();
-        when(request.getParameter("token")).thenReturn("anothertoken");
-        when(baseRequest.getRemoteInetSocketAddress()).thenReturn(new InetSocketAddress(Inet4Address.getLoopbackAddress(),45454));
-        shutdownHandler.handle("/shutdown",baseRequest,request,response);
-        assertEquals("Server should be running","STARTED",server.getState());
+        start(null);
+
+        HttpTester.Response response = shutdown("wrongToken");
+        Assert.assertEquals(HttpStatus.UNAUTHORIZED_401, response.getStatus());
+
+        Thread.sleep(1000);
+        Assert.assertEquals(AbstractLifeCycle.STARTED, server.getState());
     }
 
-     @Test
-     public void shutdownRequestNotFromLocalhostTest() throws Exception
-     {
-         setDefaultExpectations();
-         when(request.getRemoteAddr()).thenReturn("192.168.3.3");
-         when(baseRequest.getRemoteInetSocketAddress()).thenReturn(new InetSocketAddress(Inet4Address.getByName("192.168.3.3"),45454));
-         shutdownHandler.handle("/shutdown",baseRequest,request,response);
-         assertEquals("Server should be running","STARTED",server.getState());
-     }
+    @Test
+    public void testShutdownRequestNotFromLocalhost() throws Exception
+    {
+        start(new HandlerWrapper()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setRemoteAddr(new InetSocketAddress("192.168.0.1", 12345));
+                super.handle(target, baseRequest, request, response);
+            }
+        });
 
-     private void setDefaultExpectations()
-     {
-         when(request.getMethod()).thenReturn("POST");
-         when(request.getParameter("token")).thenReturn(shutdownToken);
-         when(request.getRemoteAddr()).thenReturn("127.0.0.1");
-     }
+        HttpTester.Response response = shutdown(shutdownToken);
+        Assert.assertEquals(HttpStatus.UNAUTHORIZED_401, response.getStatus());
 
+        Thread.sleep(1000);
+        Assert.assertEquals(AbstractLifeCycle.STARTED, server.getState());
+    }
+
+    private HttpTester.Response shutdown(String shutdownToken) throws IOException
+    {
+        try (Socket socket = new Socket("localhost", connector.getLocalPort()))
+        {
+            String request = "" +
+                    "POST /shutdown?token=" + shutdownToken + " HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "\r\n";
+            OutputStream output = socket.getOutputStream();
+            output.write(request.getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            HttpTester.Input input = HttpTester.from(socket.getInputStream());
+            return HttpTester.parseResponse(input);
+        }
+    }
 }
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/StatisticsHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/StatisticsHandlerTest.java
index 409fec7..b52b225 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/StatisticsHandlerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/StatisticsHandlerTest.java
@@ -18,12 +18,6 @@
 
 package org.eclipse.jetty.server.handler;
 
-import static org.hamcrest.Matchers.greaterThanOrEqualTo;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
 import java.io.IOException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.CyclicBarrier;
@@ -37,7 +31,7 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jetty.server.ConnectorStatistics;
+import org.eclipse.jetty.io.ConnectionStatistics;
 import org.eclipse.jetty.server.LocalConnector;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Server;
@@ -45,10 +39,16 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
 public class StatisticsHandlerTest
 {
     private Server _server;
-    private ConnectorStatistics _statistics;
+    private ConnectionStatistics _statistics;
     private LocalConnector _connector;
     private LatchHandler _latchHandler;
     private StatisticsHandler _statsHandler;
@@ -59,7 +59,7 @@
         _server = new Server();
 
         _connector = new LocalConnector(_server);
-        _statistics = new ConnectorStatistics();
+        _statistics = new ConnectionStatistics();
         _connector.addBean(_statistics);
         _server.addConnector(_connector);
 
@@ -110,7 +110,7 @@
 
         barrier[0].await();
 
-        assertEquals(1, _statistics.getConnectionsOpen());
+        assertEquals(1, _statistics.getConnections());
 
         assertEquals(1, _statsHandler.getRequests());
         assertEquals(1, _statsHandler.getRequestsActive());
@@ -145,7 +145,7 @@
 
         barrier[0].await();
 
-        assertEquals(2, _statistics.getConnectionsOpen());
+        assertEquals(2, _statistics.getConnections());
 
         assertEquals(2, _statsHandler.getRequests());
         assertEquals(1, _statsHandler.getRequestsActive());
@@ -171,42 +171,68 @@
         assertEquals(0, _statsHandler.getAsyncDispatches());
         assertEquals(0, _statsHandler.getExpires());
         assertEquals(2, _statsHandler.getResponses2xx());
+    }
 
+
+    @Test
+    public void testTwoRequests() throws Exception
+    {
+        final CyclicBarrier barrier[] = {new CyclicBarrier(3), new CyclicBarrier(3)};
         _latchHandler.reset(2);
-        barrier[0] = new CyclicBarrier(3);
-        barrier[1] = new CyclicBarrier(3);
+        _statsHandler.setHandler(new AbstractHandler()
+        {
+            @Override
+            public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
+            {
+                request.setHandled(true);
+                try
+                {
+                    barrier[0].await();
+                    barrier[1].await();
+                }
+                catch (Exception x)
+                {
+                    Thread.currentThread().interrupt();
+                    throw (IOException)new IOException().initCause(x);
+                }
+            }
+        });
+        _server.start();
 
+        String request = "GET / HTTP/1.1\r\n" +
+                "Host: localhost\r\n" +
+                "\r\n";
+    
         _connector.executeRequest(request);
         _connector.executeRequest(request);
 
         barrier[0].await();
 
-        assertEquals(4, _statistics.getConnectionsOpen());
+        assertEquals(2, _statistics.getConnections());
 
-        assertEquals(4, _statsHandler.getRequests());
+        assertEquals(2, _statsHandler.getRequests());
         assertEquals(2, _statsHandler.getRequestsActive());
         assertEquals(2, _statsHandler.getRequestsActiveMax());
 
-        assertEquals(4, _statsHandler.getDispatched());
+        assertEquals(2, _statsHandler.getDispatched());
         assertEquals(2, _statsHandler.getDispatchedActive());
         assertEquals(2, _statsHandler.getDispatchedActiveMax());
 
-
         barrier[1].await();
         assertTrue(_latchHandler.await());
 
-        assertEquals(4, _statsHandler.getRequests());
+        assertEquals(2, _statsHandler.getRequests());
         assertEquals(0, _statsHandler.getRequestsActive());
         assertEquals(2, _statsHandler.getRequestsActiveMax());
 
-        assertEquals(4, _statsHandler.getDispatched());
+        assertEquals(2, _statsHandler.getDispatched());
         assertEquals(0, _statsHandler.getDispatchedActive());
         assertEquals(2, _statsHandler.getDispatchedActiveMax());
 
         assertEquals(0, _statsHandler.getAsyncRequests());
         assertEquals(0, _statsHandler.getAsyncDispatches());
         assertEquals(0, _statsHandler.getExpires());
-        assertEquals(4, _statsHandler.getResponses2xx());
+        assertEquals(2, _statsHandler.getResponses2xx());
     }
 
     @Test
@@ -256,7 +282,7 @@
 
         barrier[0].await();
 
-        assertEquals(1, _statistics.getConnectionsOpen());
+        assertEquals(1, _statistics.getConnections());
         assertEquals(1, _statsHandler.getRequests());
         assertEquals(1, _statsHandler.getRequestsActive());
         assertEquals(1, _statsHandler.getDispatched());
@@ -310,7 +336,7 @@
 
         barrier[0].await(); // entered app handler
 
-        assertEquals(1, _statistics.getConnectionsOpen());
+        assertEquals(1, _statistics.getConnections());
         assertEquals(1, _statsHandler.getRequests());
         assertEquals(1, _statsHandler.getRequestsActive());
         assertEquals(2, _statsHandler.getDispatched());
@@ -390,7 +416,7 @@
 
         barrier[0].await();
 
-        assertEquals(1, _statistics.getConnectionsOpen());
+        assertEquals(1, _statistics.getConnections());
         assertEquals(1, _statsHandler.getRequests());
         assertEquals(1, _statsHandler.getRequestsActive());
         assertEquals(1, _statsHandler.getDispatched());
@@ -506,7 +532,7 @@
 
         barrier[0].await();
 
-        assertEquals(1, _statistics.getConnectionsOpen());
+        assertEquals(1, _statistics.getConnections());
         assertEquals(1, _statsHandler.getRequests());
         assertEquals(1, _statsHandler.getRequestsActive());
         assertEquals(1, _statsHandler.getDispatched());
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ThreadLimitHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ThreadLimitHandlerTest.java
new file mode 100644
index 0000000..08e9826
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ThreadLimitHandlerTest.java
@@ -0,0 +1,251 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.NetworkConnector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ThreadLimitHandlerTest
+{
+    private Server _server;
+    private NetworkConnector _connector;
+    private LocalConnector _local;
+
+
+    @Before
+    public void before()
+        throws Exception
+    {
+        _server = new Server();
+        _connector = new ServerConnector(_server);
+        _local = new LocalConnector(_server);
+        _server.setConnectors(new Connector[] { _local,_connector });
+
+    }
+
+    @After
+    public void after()
+        throws Exception
+    {
+        _server.stop();
+    }
+    
+    
+    @Test
+    public void testNoForwardHeaders() throws Exception
+    {
+        AtomicReference<String> last = new AtomicReference<>();
+        ThreadLimitHandler handler = new ThreadLimitHandler(null,false)
+        {
+            @Override
+            protected int getThreadLimit(String ip)
+            {
+                last.set(ip);
+                return super.getThreadLimit(ip);
+            }
+        };
+        handler.setHandler(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.setStatus(HttpStatus.OK_200);
+            }
+        });
+        _server.setHandler(handler);
+        _server.start();
+
+        last.set(null);
+        _local.getResponse("GET / HTTP/1.0\r\n\r\n");
+        Assert.assertThat(last.get(),Matchers.is("0.0.0.0"));
+        
+        last.set(null);
+        _local.getResponse("GET / HTTP/1.0\r\nX-Forwarded-For: 1.2.3.4\r\n\r\n");
+        Assert.assertThat(last.get(),Matchers.is("0.0.0.0"));
+        
+        last.set(null);
+        _local.getResponse("GET / HTTP/1.0\r\nForwarded: for=1.2.3.4\r\n\r\n");
+        Assert.assertThat(last.get(),Matchers.is("0.0.0.0"));
+    }
+    
+    @Test
+    public void testXForwardForHeaders() throws Exception
+    {
+        AtomicReference<String> last = new AtomicReference<>();
+        ThreadLimitHandler handler = new ThreadLimitHandler("X-Forwarded-For")
+        {
+            @Override
+            protected int getThreadLimit(String ip)
+            {
+                last.set(ip);
+                return super.getThreadLimit(ip);
+            }
+        };
+        _server.setHandler(handler);
+        _server.start();
+
+        last.set(null);
+        _local.getResponse("GET / HTTP/1.0\r\n\r\n");
+        Assert.assertThat(last.get(),Matchers.is("0.0.0.0"));
+        
+        last.set(null);
+        _local.getResponse("GET / HTTP/1.0\r\nX-Forwarded-For: 1.2.3.4\r\n\r\n");
+        Assert.assertThat(last.get(),Matchers.is("1.2.3.4"));
+        
+        last.set(null);
+        _local.getResponse("GET / HTTP/1.0\r\nForwarded: for=1.2.3.4\r\n\r\n");
+        Assert.assertThat(last.get(),Matchers.is("0.0.0.0"));
+        
+        last.set(null);
+        _local.getResponse("GET / HTTP/1.0\r\nX-Forwarded-For: 1.1.1.1\r\nX-Forwarded-For: 6.6.6.6,1.2.3.4\r\nForwarded: for=1.2.3.4\r\n\r\n");
+        Assert.assertThat(last.get(),Matchers.is("1.2.3.4"));
+
+    }
+
+    @Test
+    public void testForwardHeaders() throws Exception
+    {
+        AtomicReference<String> last = new AtomicReference<>();
+        ThreadLimitHandler handler = new ThreadLimitHandler("Forwarded")
+        {
+            @Override
+            protected int getThreadLimit(String ip)
+            {
+                last.set(ip);
+                return super.getThreadLimit(ip);
+            }
+        };
+        _server.setHandler(handler);
+        _server.start();
+
+        last.set(null);
+        _local.getResponse("GET / HTTP/1.0\r\n\r\n");
+        Assert.assertThat(last.get(),Matchers.is("0.0.0.0"));
+        
+        last.set(null);
+        _local.getResponse("GET / HTTP/1.0\r\nX-Forwarded-For: 1.2.3.4\r\n\r\n");
+        Assert.assertThat(last.get(),Matchers.is("0.0.0.0"));
+        
+        last.set(null);
+        _local.getResponse("GET / HTTP/1.0\r\nForwarded: for=1.2.3.4\r\n\r\n");
+        Assert.assertThat(last.get(),Matchers.is("1.2.3.4"));
+        
+        last.set(null);
+        _local.getResponse("GET / HTTP/1.0\r\nX-Forwarded-For: 1.1.1.1\r\nForwarded: for=6.6.6.6; for=1.2.3.4\r\nX-Forwarded-For: 6.6.6.6\r\nForwarded: proto=https\r\n\r\n");
+        Assert.assertThat(last.get(),Matchers.is("1.2.3.4"));
+    }
+    
+
+    
+    @Test
+    public void testLimit() throws Exception
+    {
+        ThreadLimitHandler handler = new ThreadLimitHandler("Forwarded");
+        
+        handler.setThreadLimit(4);
+
+        AtomicInteger count = new AtomicInteger(0);
+        AtomicInteger total = new AtomicInteger(0);
+        CountDownLatch latch = new CountDownLatch(1);
+        handler.setHandler(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.setStatus(HttpStatus.OK_200);
+                if ("/other".equals(target))
+                    return;
+                
+                try
+                {
+                    count.incrementAndGet();
+                    total.incrementAndGet();
+                    latch.await();
+                }
+                catch (InterruptedException e)
+                {
+                    throw new ServletException(e);
+                }
+                finally
+                {
+                    count.decrementAndGet();
+                }
+                
+            }
+        });
+        _server.setHandler(handler);
+        _server.start();
+
+        Socket[] client = new Socket[10];
+        for (int i=0;i<client.length;i++)
+        {
+            client[i]=new Socket("127.0.0.1",_connector.getLocalPort());
+            client[i].getOutputStream().write(("GET /"+i+" HTTP/1.0\r\nForwarded: for=1.2.3.4\r\n\r\n").getBytes());  
+            client[i].getOutputStream().flush();  
+        }
+        
+        long wait = System.nanoTime() + TimeUnit.SECONDS.toNanos(10);
+        while(count.get()<4 && System.nanoTime()<wait) 
+            Thread.sleep(1);
+        assertThat(count.get(),is(4));
+        
+        // check that other requests are not blocked
+        assertThat(_local.getResponse("GET /other HTTP/1.0\r\nForwarded: for=6.6.6.6\r\n\r\n"),Matchers.containsString(" 200 OK"));
+        
+        // let the other requests go
+        latch.countDown();
+
+        while(total.get()<10 && System.nanoTime()<wait) 
+            Thread.sleep(10);
+        assertThat(total.get(),is(10));
+
+        while(count.get()>0 && System.nanoTime()<wait) 
+            Thread.sleep(10);
+        assertThat(count.get(),is(0));
+        
+    }
+    
+    
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/HashSessionManagerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/HashSessionManagerTest.java
index 8144803..c86a7a3 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/HashSessionManagerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/HashSessionManagerTest.java
@@ -25,11 +25,8 @@
 import org.eclipse.jetty.toolchain.test.FS;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
-import org.junit.After;
+import org.eclipse.jetty.util.thread.Scheduler;
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Test;
 
 public class HashSessionManagerTest
@@ -84,7 +81,25 @@
         Server server = new Server();
         SessionHandler handler = new SessionHandler();
         handler.setServer(server);
-        HashSessionManager manager = new HashSessionManager();
+        HashSessionManager manager = new HashSessionManager()
+        {
+            @Override
+            public void doStart() throws Exception
+            {
+                super.doStart();
+                Scheduler timerBean = getBean(Scheduler.class);
+                Assert.assertNotNull(timerBean);
+            }
+
+            @Override
+            public void doStop() throws Exception
+            {
+                super.doStop();
+                Scheduler timerBean = getBean(Scheduler.class);
+                Assert.assertNull(timerBean);
+            }
+
+        };
         manager.setStoreDirectory(testDir);
         manager.setMaxInactiveInterval(5);
         Assert.assertTrue(testDir.exists());
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
index 04daecb..66a1e2e 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
@@ -31,24 +31,14 @@
 
 import org.eclipse.jetty.http.HttpCookie;
 import org.junit.Test;
+
 /**
  * SessionCookieTest
- *
- *
  */
 public class SessionCookieTest
 {
-
     public class MockSession extends AbstractSession
     {
-
-
-        /**
-         * @param abstractSessionManager
-         * @param created
-         * @param accessed
-         * @param clusterId
-         */
         protected MockSession(AbstractSessionManager abstractSessionManager, long created, long accessed, String clusterId)
         {
             super(abstractSessionManager, created, accessed, clusterId);
@@ -72,9 +62,6 @@
             return null;
         }
 
-        /** 
-         * @see javax.servlet.http.HttpSession#getValueNames()
-         */
         @Override
         public String[] getValueNames()
         {
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLCloseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLCloseTest.java
index a302934..83ab75b 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLCloseTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLCloseTest.java
@@ -27,6 +27,7 @@
 import java.nio.charset.StandardCharsets;
 
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -36,6 +37,7 @@
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.TypeUtil;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.junit.Test;
@@ -58,8 +60,8 @@
         server.addConnector(connector);
         server.setHandler(new WriteHandler());
         server.start();
-
-        SSLContext ctx=SSLContext.getInstance("SSLv3");
+        
+        SSLContext ctx=SSLContext.getInstance("TLSv1.2");
         ctx.init(null,SslContextFactory.TRUST_ALL_CERTS,new java.security.SecureRandom());
 
         int port=connector.getLocalPort();
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java
index 892ff5f..99bf66e 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java
@@ -189,7 +189,7 @@
 
         Socket[] client=new Socket[numConns];
 
-        SSLContext ctx=SSLContext.getInstance("SSLv3");
+        SSLContext ctx=SSLContext.getInstance("TLSv1.2");
         ctx.init(null,SslContextFactory.TRUST_ALL_CERTS,new java.security.SecureRandom());
 
         int port=connector.getLocalPort();
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLSelectChannelConnectorLoadTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLSelectChannelConnectorLoadTest.java
index 3db7673..15b5ba1 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLSelectChannelConnectorLoadTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLSelectChannelConnectorLoadTest.java
@@ -18,12 +18,16 @@
 
 package org.eclipse.jetty.server.ssl;
 
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
 import java.io.BufferedReader;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
+import java.net.URI;
 import java.security.KeyStore;
 import java.util.Arrays;
 import java.util.Random;
@@ -91,6 +95,15 @@
         server.stop();
         server.join();
     }
+    
+    @Test
+    public void testGetURI()
+    {
+        URI uri = server.getURI();
+        assertThat("Server.uri.scheme", uri.getScheme(), is("https"));
+        assertThat("Server.uri.port", uri.getPort(), is(connector.getLocalPort()));
+        assertThat("Server.uri.path", uri.getPath(), is("/"));
+    }
 
     @Test
     public void testLongLivedConnections() throws Exception
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java
index 7796b07..dfa4375 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java
@@ -18,21 +18,33 @@
 
 package org.eclipse.jetty.server.ssl;
 
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isEmptyOrNullString;
+import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 
-import java.io.FileInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.Socket;
 import java.net.SocketException;
 import java.net.URI;
+import java.nio.charset.StandardCharsets;
 import java.security.KeyStore;
 import java.util.Arrays;
 import java.util.concurrent.Executor;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
 import javax.net.ssl.TrustManagerFactory;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.LeakTrackingByteBufferPool;
@@ -41,11 +53,16 @@
 import org.eclipse.jetty.server.AbstractConnectionFactory;
 import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.HttpServerTestBase;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
 import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.toolchain.test.OS;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.Scheduler;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -122,13 +139,17 @@
         sslContextFactory.setTrustStorePath(keystorePath);
         sslContextFactory.setTrustStorePassword("storepwd");
         ByteBufferPool pool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged());
-        ServerConnector connector = new ServerConnector(_server,(Executor)null,(Scheduler)null,pool, 1, 1, AbstractConnectionFactory.getFactories(sslContextFactory,new HttpConnectionFactory()));
-
+        
+        HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory();
+        ServerConnector connector = new ServerConnector(_server,(Executor)null,(Scheduler)null,pool, 1, 1, AbstractConnectionFactory.getFactories(sslContextFactory,httpConnectionFactory));
+        SecureRequestCustomizer secureRequestCustomer = new SecureRequestCustomizer();
+        secureRequestCustomer.setSslSessionAttribute("SSL_SESSION");
+        httpConnectionFactory.getHttpConfiguration().addCustomizer(secureRequestCustomer);
         
         startServer(connector);
 
         KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
-        try (InputStream stream = new FileInputStream(sslContextFactory.getKeyStorePath()))
+        try (InputStream stream = sslContextFactory.getKeyStoreResource().getInputStream())
         {
             keystore.load(stream, "storepwd".toCharArray());
         }
@@ -223,5 +244,67 @@
     {
     }
 
+    @Test
+    public void testSecureRequestCustomizer() throws Exception
+    {
+        configureServer(new SecureRequestHandler());
 
+        try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()))
+        {
+            OutputStream os = client.getOutputStream();
+
+            os.write("GET / HTTP/1.0\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1));
+            os.flush();
+
+            // Read the response.
+            String response = readResponse(client);
+
+            System.err.println(response);
+            
+            assertThat(response, containsString("HTTP/1.1 200 OK"));
+            assertThat(response, containsString("Hello world"));
+            assertThat(response, containsString("scheme='https'"));
+            assertThat(response, containsString("isSecure='true'"));
+            assertThat(response, containsString("X509Certificate='null'"));
+
+            Matcher matcher=Pattern.compile("cipher_suite='([^']*)'").matcher(response);
+            matcher.find();
+            assertThat(matcher.group(1), Matchers.allOf(not(isEmptyOrNullString()),not(is("null"))));
+           
+            matcher=Pattern.compile("key_size='([^']*)'").matcher(response);
+            matcher.find();
+            assertThat(matcher.group(1), Matchers.allOf(not(isEmptyOrNullString()),not(is("null"))));
+            
+            matcher=Pattern.compile("ssl_session_id='([^']*)'").matcher(response);
+            matcher.find();
+            assertThat(matcher.group(1), Matchers.allOf(not(isEmptyOrNullString()),not(is("null"))));
+            
+            matcher=Pattern.compile("ssl_session='([^']*)'").matcher(response);
+            matcher.find();
+            assertThat(matcher.group(1), Matchers.allOf(not(isEmptyOrNullString()),not(is("null"))));
+        }
+    }
+
+    public static class SecureRequestHandler extends AbstractHandler
+    {
+
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            response.setStatus(200);
+            response.getOutputStream().println("Hello world");
+            response.getOutputStream().println("scheme='"+request.getScheme()+"'");
+            response.getOutputStream().println("isSecure='"+request.isSecure()+"'");
+            response.getOutputStream().println("X509Certificate='"+request.getAttribute("javax.servlet.request.X509Certificate")+"'");
+            response.getOutputStream().println("cipher_suite='"+request.getAttribute("javax.servlet.request.cipher_suite")+"'");
+            response.getOutputStream().println("key_size='"+request.getAttribute("javax.servlet.request.key_size")+"'");
+            response.getOutputStream().println("ssl_session_id='"+request.getAttribute("javax.servlet.request.ssl_session_id")+"'");
+            SSLSession sslSession=(SSLSession)request.getAttribute("SSL_SESSION");
+            response.getOutputStream().println("ssl_session='"+sslSession+"'");
+            
+        }
+        
+    }
+    
 }
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SlowClientsTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SlowClientsTest.java
new file mode 100644
index 0000000..baec8f9
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SlowClientsTest.java
@@ -0,0 +1,138 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+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.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.Ignore;
+import org.junit.Test;
+
+@Ignore
+public class SlowClientsTest
+{
+    private Logger logger = Log.getLogger(getClass());
+    
+    @Test(timeout = 10000)
+    public void testSlowClientsWithSmallThreadPool() throws Exception
+    {
+        File keystore = MavenTestingUtils.getTestResourceFile("keystore");
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath(keystore.getAbsolutePath());
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setKeyManagerPassword("keypwd");
+
+        int maxThreads = 6;
+        int contentLength = 8 * 1024 * 1024;
+        QueuedThreadPool serverThreads = new QueuedThreadPool(maxThreads);
+        serverThreads.setDetailedDump(true);
+        Server server = new Server(serverThreads);
+        try
+        {
+            ServerConnector connector = new ServerConnector(server, 1, 1, sslContextFactory);
+            connector.setPort(8888);
+            server.addConnector(connector);
+            server.setHandler(new AbstractHandler()
+            {
+                @Override
+                public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+                {
+                    baseRequest.setHandled(true);
+                    logger.info("SERVING {}", target);
+                    // Write some big content.
+                    response.getOutputStream().write(new byte[contentLength]);
+                    logger.info("SERVED {}", target);
+                }
+            });
+            server.start();
+
+            SSLContext sslContext = sslContextFactory.getSslContext();
+
+            CompletableFuture[] futures = new CompletableFuture[2 * maxThreads];
+            ExecutorService executor = Executors.newFixedThreadPool(futures.length);
+            for (int i = 0; i < futures.length; i++)
+            {
+                int k = i;
+                futures[i] = CompletableFuture.runAsync(() ->
+                {
+                    try (SSLSocket socket = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", connector.getLocalPort()))
+                    {
+                        socket.setSoTimeout(contentLength / 1024);
+                        OutputStream output = socket.getOutputStream();
+                        String target = "/" + k;
+                        String request = "GET " + target + " HTTP/1.1\r\n" +
+                                "Host: localhost\r\n" +
+                                "Connection: close\r\n" +
+                                "\r\n";
+                        output.write(request.getBytes(StandardCharsets.UTF_8));
+                        output.flush();
+
+                        while (serverThreads.getIdleThreads() > 0)
+                            Thread.sleep(50);
+
+                        InputStream input = socket.getInputStream();
+                        while (true)
+                        {
+                            int read = input.read();
+                            if (read < 0)
+                                break;
+                        }
+                        logger.info("FINISHED {}", target);
+                    }
+                    catch (IOException x)
+                    {
+                        throw new UncheckedIOException(x);
+                    }
+                    catch (InterruptedException x)
+                    {
+                        throw new UncheckedIOException(new InterruptedIOException());
+                    }
+                }, executor);
+            }
+            CompletableFuture.allOf(futures).join();
+        }
+        finally
+        {
+            server.stop();
+        }
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SniSslConnectionFactoryTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SniSslConnectionFactoryTest.java
new file mode 100644
index 0000000..5d8b539
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SniSslConnectionFactoryTest.java
@@ -0,0 +1,404 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SocketCustomizationListener;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.Utf8StringBuilder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SniSslConnectionFactoryTest
+{
+    private Server _server;
+    private ServerConnector _connector;
+    private HttpConfiguration _https_config;
+    private int _port;
+
+    @Before
+    public void before() throws Exception
+    {
+        String keystorePath = "src/test/resources/snikeystore";
+        File keystoreFile = new File(keystorePath);
+        if (!keystoreFile.exists())
+            throw new FileNotFoundException(keystoreFile.getAbsolutePath());
+
+        _server = new Server();
+
+        HttpConfiguration http_config = new HttpConfiguration();
+        http_config.setSecureScheme("https");
+        http_config.setSecurePort(8443);
+        http_config.setOutputBufferSize(32768);
+        _https_config = new HttpConfiguration(http_config);
+        _https_config.addCustomizer(new SecureRequestCustomizer());
+
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath());
+        sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
+        sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
+
+        ServerConnector https = _connector = new ServerConnector(_server,
+                new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
+                new HttpConnectionFactory(_https_config));
+        _server.addConnector(https);
+
+        _server.setHandler(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.setStatus(200);
+                response.setHeader("X-URL", request.getRequestURI());
+                response.setHeader("X-HOST", request.getServerName());
+            }
+        });
+
+        _server.start();
+        _port = https.getLocalPort();
+    }
+
+    @After
+    public void after() throws Exception
+    {
+        if (_server != null)
+            _server.stop();
+    }
+
+    @Test
+    public void testConnect() throws Exception
+    {
+        String response = getResponse("127.0.0.1", null);
+        Assert.assertThat(response, Matchers.containsString("X-HOST: 127.0.0.1"));
+    }
+
+    @Test
+    public void testSNIConnectNoWild() throws Exception
+    {
+        // Use the alternate keystore without wildcard certificates.
+        _server.stop();
+        _server.removeConnector(_connector);
+
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath("src/test/resources/snikeystore_nowild");
+        sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
+        sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
+
+        _connector = new ServerConnector(_server,
+                new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
+                new HttpConnectionFactory(_https_config));
+        _server.addConnector(_connector);
+        _server.start();
+        _port = _connector.getLocalPort();
+
+        // The first entry in the keystore is www.example.com, and it will
+        // be returned by default, so make sure that here we don't ask for it.
+        String response = getResponse("jetty.eclipse.org", "jetty.eclipse.org");
+        Assert.assertThat(response, Matchers.containsString("X-HOST: jetty.eclipse.org"));
+    }
+
+    @Test
+    public void testSNIConnect() throws Exception
+    {
+        String response = getResponse("jetty.eclipse.org", "jetty.eclipse.org");
+        Assert.assertThat(response, Matchers.containsString("X-HOST: jetty.eclipse.org"));
+
+        response = getResponse("www.example.com", "www.example.com");
+        Assert.assertThat(response, Matchers.containsString("X-HOST: www.example.com"));
+
+        response = getResponse("foo.domain.com", "*.domain.com");
+        Assert.assertThat(response, Matchers.containsString("X-HOST: foo.domain.com"));
+
+        response = getResponse("m.san.com", "san example");
+        Assert.assertThat(response, Matchers.containsString("X-HOST: m.san.com"));
+
+        response = getResponse("www.san.com", "san example");
+        Assert.assertThat(response, Matchers.containsString("X-HOST: www.san.com"));
+    }
+
+    @Test
+    public void testWildSNIConnect() throws Exception
+    {
+        String response = getResponse("domain.com", "www.domain.com", "*.domain.com");
+        Assert.assertThat(response, Matchers.containsString("X-HOST: www.domain.com"));
+
+        response = getResponse("domain.com", "domain.com", "*.domain.com");
+        Assert.assertThat(response, Matchers.containsString("X-HOST: domain.com"));
+
+        response = getResponse("www.domain.com", "www.domain.com", "*.domain.com");
+        Assert.assertThat(response, Matchers.containsString("X-HOST: www.domain.com"));
+    }
+
+    @Test
+    public void testBadSNIConnect() throws Exception
+    {
+        String response = getResponse("www.example.com", "some.other.com", "www.example.com");
+        Assert.assertThat(response, Matchers.containsString("HTTP/1.1 400 "));
+        Assert.assertThat(response, Matchers.containsString("Host does not match SNI"));
+    }
+
+    @Test
+    public void testSameConnectionRequestsForManyDomains() throws Exception
+    {
+        SslContextFactory clientContextFactory = new SslContextFactory(true);
+        clientContextFactory.start();
+        SSLSocketFactory factory = clientContextFactory.getSslContext().getSocketFactory();
+        try (SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port))
+        {
+            SNIHostName serverName = new SNIHostName("m.san.com");
+            SSLParameters params = sslSocket.getSSLParameters();
+            params.setServerNames(Collections.singletonList(serverName));
+            sslSocket.setSSLParameters(params);
+            sslSocket.startHandshake();
+
+            // The first request binds the socket to an alias.
+            String request = "" +
+                    "GET /ctx/path HTTP/1.1\r\n" +
+                    "Host: m.san.com\r\n" +
+                    "\r\n";
+            OutputStream output = sslSocket.getOutputStream();
+            output.write(request.getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            InputStream input = sslSocket.getInputStream();
+            String response = response(input);
+            Assert.assertTrue(response.startsWith("HTTP/1.1 200 "));
+
+            // Same socket, send a request for a different domain but same alias.
+            request = "" +
+                    "GET /ctx/path HTTP/1.1\r\n" +
+                    "Host: www.san.com\r\n" +
+                    "\r\n";
+            output.write(request.getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            response = response(input);
+            Assert.assertTrue(response.startsWith("HTTP/1.1 200 "));
+
+            // Same socket, send a request for a different domain but different alias.
+            request = "" +
+                    "GET /ctx/path HTTP/1.1\r\n" +
+                    "Host: www.example.com\r\n" +
+                    "\r\n";
+            output.write(request.getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            response = response(input);
+            Assert.assertTrue(response.startsWith("HTTP/1.1 400 "));
+            Assert.assertThat(response, Matchers.containsString("Host does not match SNI"));
+        }
+        finally
+        {
+            clientContextFactory.stop();
+        }
+    }
+
+    @Test
+    public void testSameConnectionRequestsForManyWildDomains() throws Exception
+    {
+        SslContextFactory clientContextFactory = new SslContextFactory(true);
+        clientContextFactory.start();
+        SSLSocketFactory factory = clientContextFactory.getSslContext().getSocketFactory();
+        try (SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port))
+        {
+            SNIHostName serverName = new SNIHostName("www.domain.com");
+            SSLParameters params = sslSocket.getSSLParameters();
+            params.setServerNames(Collections.singletonList(serverName));
+            sslSocket.setSSLParameters(params);
+            sslSocket.startHandshake();
+
+            String request = "" +
+                    "GET /ctx/path HTTP/1.1\r\n" +
+                    "Host: www.domain.com\r\n" +
+                    "\r\n";
+            OutputStream output = sslSocket.getOutputStream();
+            output.write(request.getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            InputStream input = sslSocket.getInputStream();
+            String response = response(input);
+            Assert.assertTrue(response.startsWith("HTTP/1.1 200 "));
+
+            // Now, on the same socket, send a request for a different valid domain.
+            request = "" +
+                    "GET /ctx/path HTTP/1.1\r\n" +
+                    "Host: assets.domain.com\r\n" +
+                    "\r\n";
+            output.write(request.getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            response = response(input);
+            Assert.assertTrue(response.startsWith("HTTP/1.1 200 "));
+
+            // Now make a request for an invalid domain for this connection.
+            request = "" +
+                    "GET /ctx/path HTTP/1.1\r\n" +
+                    "Host: www.example.com\r\n" +
+                    "\r\n";
+            output.write(request.getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            response = response(input);
+            Assert.assertTrue(response.startsWith("HTTP/1.1 400 "));
+            Assert.assertThat(response, Matchers.containsString("Host does not match SNI"));
+        }
+        finally
+        {
+            clientContextFactory.stop();
+        }
+    }
+
+    private String response(InputStream input) throws IOException
+    {
+        Utf8StringBuilder buffer = new Utf8StringBuilder();
+        int crlfs = 0;
+        while (true)
+        {
+            int read = input.read();
+            Assert.assertTrue(read >= 0);
+            buffer.append((byte)read);
+            crlfs = (read == '\r' || read == '\n') ? crlfs + 1 : 0;
+            if (crlfs == 4)
+                break;
+        }
+        return buffer.toString();
+    }
+
+    private String getResponse(String host, String cn) throws Exception
+    {
+        String response = getResponse(host, host, cn);
+        Assert.assertThat(response, Matchers.startsWith("HTTP/1.1 200 "));
+        Assert.assertThat(response, Matchers.containsString("X-URL: /ctx/path"));
+        return response;
+    }
+
+    private String getResponse(String sniHost, String reqHost, String cn) throws Exception
+    {
+        SslContextFactory clientContextFactory = new SslContextFactory(true);
+        clientContextFactory.start();
+        SSLSocketFactory factory = clientContextFactory.getSslContext().getSocketFactory();
+        try (SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port))
+        {
+            if (cn != null)
+            {
+                SNIHostName serverName = new SNIHostName(sniHost);
+                List<SNIServerName> serverNames = new ArrayList<>();
+                serverNames.add(serverName);
+
+                SSLParameters params = sslSocket.getSSLParameters();
+                params.setServerNames(serverNames);
+                sslSocket.setSSLParameters(params);
+            }
+            sslSocket.startHandshake();
+
+            if (cn != null)
+            {
+                X509Certificate cert = ((X509Certificate)sslSocket.getSession().getPeerCertificates()[0]);
+                Assert.assertThat(cert.getSubjectX500Principal().getName("CANONICAL"), Matchers.startsWith("cn=" + cn));
+            }
+
+            String response = "GET /ctx/path HTTP/1.0\r\nHost: " + reqHost + ":" + _port + "\r\n\r\n";
+            sslSocket.getOutputStream().write(response.getBytes(StandardCharsets.ISO_8859_1));
+            return IO.toString(sslSocket.getInputStream());
+        }
+        finally
+        {
+            clientContextFactory.stop();
+        }
+    }
+
+    @Test
+    public void testSocketCustomization() throws Exception
+    {
+        final Queue<String> history = new LinkedBlockingQueue<>();
+
+        _connector.addBean(new SocketCustomizationListener()
+        {
+            @Override
+            protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl)
+            {
+                history.add("customize connector " + connection + "," + ssl);
+            }
+        });
+
+        _connector.getBean(SslConnectionFactory.class).addBean(new SocketCustomizationListener()
+        {
+            @Override
+            protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl)
+            {
+                history.add("customize ssl " + connection + "," + ssl);
+            }
+        });
+
+        _connector.getBean(HttpConnectionFactory.class).addBean(new SocketCustomizationListener()
+        {
+            @Override
+            protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl)
+            {
+                history.add("customize http " + connection + "," + ssl);
+            }
+        });
+
+        String response = getResponse("127.0.0.1", null);
+        Assert.assertThat(response, Matchers.containsString("X-HOST: 127.0.0.1"));
+
+        Assert.assertEquals("customize connector class org.eclipse.jetty.io.ssl.SslConnection,false", history.poll());
+        Assert.assertEquals("customize ssl class org.eclipse.jetty.io.ssl.SslConnection,false", history.poll());
+        Assert.assertEquals("customize connector class org.eclipse.jetty.server.HttpConnection,true", history.poll());
+        Assert.assertEquals("customize http class org.eclipse.jetty.server.HttpConnection,true", history.poll());
+        Assert.assertEquals(0, history.size());
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslConnectionFactoryTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslConnectionFactoryTest.java
new file mode 100644
index 0000000..fa809e6
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslConnectionFactoryTest.java
@@ -0,0 +1,237 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SocketCustomizationListener;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SslConnectionFactoryTest
+{
+    private Server _server;
+    private ServerConnector _connector;
+    private int _port;
+
+    @Before
+    public void before() throws Exception
+    {
+        String keystorePath = "src/test/resources/keystore";
+        File keystoreFile = new File(keystorePath);
+        if (!keystoreFile.exists())
+            throw new FileNotFoundException(keystoreFile.getAbsolutePath());
+
+        _server = new Server();
+
+        HttpConfiguration http_config = new HttpConfiguration();
+        http_config.setSecureScheme("https");
+        http_config.setSecurePort(8443);
+        http_config.setOutputBufferSize(32768);
+        HttpConfiguration https_config = new HttpConfiguration(http_config);
+        https_config.addCustomizer(new SecureRequestCustomizer());
+
+
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath());
+        sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
+        sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
+
+        ServerConnector https = _connector = new ServerConnector(_server,
+                new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
+                new HttpConnectionFactory(https_config));
+        https.setPort(0);
+        https.setIdleTimeout(30000);
+
+        _server.addConnector(https);
+
+        _server.setHandler(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                response.setStatus(200);
+                response.getWriter().write("url=" + request.getRequestURI() + "\nhost=" + request.getServerName());
+                response.flushBuffer();
+            }
+        });
+
+        _server.start();
+        _port = https.getLocalPort();
+    }
+
+    @After
+    public void after() throws Exception
+    {
+        _server.stop();
+        _server = null;
+    }
+
+    @Test
+    public void testConnect() throws Exception
+    {
+        String response = getResponse("127.0.0.1", null);
+        Assert.assertThat(response, Matchers.containsString("host=127.0.0.1"));
+    }
+
+    @Test
+    public void testSNIConnect() throws Exception
+    {
+        String response = getResponse("localhost", "localhost", "jetty.eclipse.org");
+        Assert.assertThat(response, Matchers.containsString("host=localhost"));
+    }
+
+    @Test
+    public void testBadHandshake() throws Exception
+    {
+        try (Socket socket = new Socket("127.0.0.1", _port); OutputStream out = socket.getOutputStream())
+        {
+            out.write("Rubbish".getBytes());
+            out.flush();
+            // Expect TLS message type == 21: Alert
+            Assert.assertThat(socket.getInputStream().read(), Matchers.equalTo(21));
+        }
+    }
+
+    @Test
+    public void testSocketCustomization() throws Exception
+    {
+        final Queue<String> history = new LinkedBlockingQueue<>();
+
+        _connector.addBean(new SocketCustomizationListener()
+        {
+            @Override
+            protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl)
+            {
+                history.add("customize connector " + connection + "," + ssl);
+            }
+        });
+
+        _connector.getBean(SslConnectionFactory.class).addBean(new SocketCustomizationListener()
+        {
+            @Override
+            protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl)
+            {
+                history.add("customize ssl " + connection + "," + ssl);
+            }
+        });
+
+        _connector.getBean(HttpConnectionFactory.class).addBean(new SocketCustomizationListener()
+        {
+            @Override
+            protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl)
+            {
+                history.add("customize http " + connection + "," + ssl);
+            }
+        });
+
+        String response = getResponse("127.0.0.1", null);
+        Assert.assertThat(response, Matchers.containsString("host=127.0.0.1"));
+
+        Assert.assertEquals("customize connector class org.eclipse.jetty.io.ssl.SslConnection,false", history.poll());
+        Assert.assertEquals("customize ssl class org.eclipse.jetty.io.ssl.SslConnection,false", history.poll());
+        Assert.assertEquals("customize connector class org.eclipse.jetty.server.HttpConnection,true", history.poll());
+        Assert.assertEquals("customize http class org.eclipse.jetty.server.HttpConnection,true", history.poll());
+        Assert.assertEquals(0, history.size());
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testServerWithoutHttpConnectionFactory() throws Exception
+    {
+        _server.stop();
+        Assert.assertNotNull(_connector.removeConnectionFactory(HttpVersion.HTTP_1_1.asString()));
+        _server.start();
+    }
+
+    private String getResponse(String host, String cn) throws Exception
+    {
+        String response = getResponse(host, host, cn);
+        Assert.assertThat(response, Matchers.startsWith("HTTP/1.1 200 OK"));
+        Assert.assertThat(response, Matchers.containsString("url=/ctx/path"));
+        return response;
+    }
+
+    private String getResponse(String sniHost, String reqHost, String cn) throws Exception
+    {
+        SslContextFactory clientContextFactory = new SslContextFactory(true);
+        clientContextFactory.start();
+        SSLSocketFactory factory = clientContextFactory.getSslContext().getSocketFactory();
+
+        SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port);
+
+        if (cn != null)
+        {
+            SNIHostName serverName = new SNIHostName(sniHost);
+            List<SNIServerName> serverNames = new ArrayList<>();
+            serverNames.add(serverName);
+
+            SSLParameters params = sslSocket.getSSLParameters();
+            params.setServerNames(serverNames);
+            sslSocket.setSSLParameters(params);
+        }
+        sslSocket.startHandshake();
+
+        if (cn != null)
+        {
+            X509Certificate cert = ((X509Certificate)sslSocket.getSession().getPeerCertificates()[0]);
+            Assert.assertThat(cert.getSubjectX500Principal().getName("CANONICAL"), Matchers.startsWith("cn=" + cn));
+        }
+
+        sslSocket.getOutputStream().write(("GET /ctx/path HTTP/1.0\r\nHost: " + reqHost + ":" + _port + "\r\n\r\n").getBytes(StandardCharsets.ISO_8859_1));
+        String response = IO.toString(sslSocket.getInputStream());
+
+        sslSocket.close();
+        clientContextFactory.stop();
+        return response;
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslContextFactoryReloadTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslContextFactoryReloadTest.java
new file mode 100644
index 0000000..d55f749
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslContextFactoryReloadTest.java
@@ -0,0 +1,265 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.ssl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
+import org.eclipse.jetty.util.thread.Scheduler;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SslContextFactoryReloadTest
+{
+    public static final String KEYSTORE_1 = "src/test/resources/reload_keystore_1.jks";
+    public static final String KEYSTORE_2 = "src/test/resources/reload_keystore_2.jks";
+
+    private Server server;
+    private SslContextFactory sslContextFactory;
+    private ServerConnector connector;
+
+    private void start(Handler handler) throws Exception
+    {
+        server = new Server();
+
+        sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath(KEYSTORE_1);
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setKeyStoreType("JKS");
+        sslContextFactory.setKeyStoreProvider(null);
+
+        HttpConfiguration httpsConfig = new HttpConfiguration();
+        httpsConfig.addCustomizer(new SecureRequestCustomizer());
+        connector = new ServerConnector(server,
+                new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
+                new HttpConnectionFactory(httpsConfig));
+        server.addConnector(connector);
+
+        server.setHandler(handler);
+
+        server.start();
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        if (server != null)
+            server.stop();
+    }
+
+    @Test
+    public void testReload() throws Exception
+    {
+        start(new EchoHandler());
+
+        SSLContext ctx = SSLContext.getInstance("TLSv1.2");
+        ctx.init(null, SslContextFactory.TRUST_ALL_CERTS, null);
+        SSLSocketFactory socketFactory = ctx.getSocketFactory();
+        try (SSLSocket client1 = (SSLSocket)socketFactory.createSocket("localhost", connector.getLocalPort()))
+        {
+            String serverDN1 = client1.getSession().getPeerPrincipal().getName();
+            Assert.assertThat(serverDN1, Matchers.startsWith("CN=localhost1"));
+
+            String request = "" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost\r\n" +
+                    "\r\n";
+
+            OutputStream output1 = client1.getOutputStream();
+            output1.write(request.getBytes(StandardCharsets.UTF_8));
+            output1.flush();
+
+            HttpTester.Response response1 = HttpTester.parseResponse(HttpTester.from(client1.getInputStream()));
+            Assert.assertNotNull(response1);
+            Assert.assertThat(response1.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
+
+            // Reconfigure SslContextFactory.
+            sslContextFactory.reload(sslContextFactory ->
+            {
+                sslContextFactory.setKeyStorePath(KEYSTORE_2);
+                sslContextFactory.setKeyStorePassword("storepwd");
+            });
+
+            // New connection should use the new keystore.
+            try (SSLSocket client2 = (SSLSocket)socketFactory.createSocket("localhost", connector.getLocalPort()))
+            {
+                String serverDN2 = client2.getSession().getPeerPrincipal().getName();
+                Assert.assertThat(serverDN2, Matchers.startsWith("CN=localhost2"));
+
+                OutputStream output2 = client1.getOutputStream();
+                output2.write(request.getBytes(StandardCharsets.UTF_8));
+                output2.flush();
+
+                HttpTester.Response response2 = HttpTester.parseResponse(HttpTester.from(client1.getInputStream()));
+                Assert.assertNotNull(response2);
+                Assert.assertThat(response2.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
+            }
+
+            // Must still be possible to make requests with the first connection.
+            output1.write(request.getBytes(StandardCharsets.UTF_8));
+            output1.flush();
+
+            response1 = HttpTester.parseResponse(HttpTester.from(client1.getInputStream()));
+            Assert.assertNotNull(response1);
+            Assert.assertThat(response1.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
+        }
+    }
+
+    @Test
+    public void testReloadWhileServing() throws Exception
+    {
+        start(new EchoHandler());
+
+        Scheduler scheduler = new ScheduledExecutorScheduler();
+        scheduler.start();
+        try
+        {
+            SSLContext ctx = SSLContext.getInstance("TLSv1.2");
+            ctx.init(null, SslContextFactory.TRUST_ALL_CERTS, null);
+            SSLSocketFactory socketFactory = ctx.getSocketFactory();
+
+            // Perform 4 reloads while connections are being served.
+            AtomicInteger reloads = new AtomicInteger(4);
+            long reloadPeriod = 500;
+            AtomicBoolean running = new AtomicBoolean(true);
+            scheduler.schedule(new Runnable()
+            {
+                @Override
+                public void run()
+                {
+                    if (reloads.decrementAndGet() == 0)
+                    {
+                        running.set(false);
+                    }
+                    else
+                    {
+                        try
+                        {
+                            sslContextFactory.reload(sslContextFactory ->
+                            {
+                                if (sslContextFactory.getKeyStorePath().endsWith(KEYSTORE_1))
+                                    sslContextFactory.setKeyStorePath(KEYSTORE_2);
+                                else
+                                    sslContextFactory.setKeyStorePath(KEYSTORE_1);
+                            });
+                            scheduler.schedule(this, reloadPeriod, TimeUnit.MILLISECONDS);
+                        }
+                        catch (Exception x)
+                        {
+                            running.set(false);
+                            reloads.set(-1);
+                        }
+                    }
+                }
+            }, reloadPeriod, TimeUnit.MILLISECONDS);
+
+            byte[] content = new byte[16 * 1024];
+            while (running.get())
+            {
+                try (SSLSocket client = (SSLSocket)socketFactory.createSocket("localhost", connector.getLocalPort()))
+                {
+                    // We need to invalidate the session every time we open a new SSLSocket.
+                    // This is because when the client uses session resumption, it caches
+                    // the server certificates and then checks that it is the same during
+                    // a new TLS handshake. If the SslContextFactory is reloaded during the
+                    // TLS handshake, the client will see the new certificate and blow up.
+                    // Note that browsers can handle this case better: they will just not
+                    // use session resumption and fallback to the normal TLS handshake.
+                    client.getSession().invalidate();
+
+                    String request1 = "" +
+                            "POST / HTTP/1.1\r\n" +
+                            "Host: localhost\r\n" +
+                            "Content-Length: " + content.length + "\r\n" +
+                            "\r\n";
+                    OutputStream outputStream = client.getOutputStream();
+                    outputStream.write(request1.getBytes(StandardCharsets.UTF_8));
+                    outputStream.write(content);
+                    outputStream.flush();
+
+                    InputStream inputStream = client.getInputStream();
+                    HttpTester.Response response1 = HttpTester.parseResponse(HttpTester.from(inputStream));
+                    Assert.assertNotNull(response1);
+                    Assert.assertThat(response1.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
+
+                    String request2 = "" +
+                            "GET / HTTP/1.1\r\n" +
+                            "Host: localhost\r\n" +
+                            "Connection: close\r\n" +
+                            "\r\n";
+                    outputStream.write(request2.getBytes(StandardCharsets.UTF_8));
+                    outputStream.flush();
+
+                    HttpTester.Response response2 = HttpTester.parseResponse(HttpTester.from(inputStream));
+                    Assert.assertNotNull(response2);
+                    Assert.assertThat(response2.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
+                }
+            }
+
+            Assert.assertEquals(0, reloads.get());
+        }
+        finally
+        {
+            scheduler.stop();
+        }
+    }
+
+    private static class EchoHandler extends AbstractHandler
+    {
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            if (HttpMethod.POST.is(request.getMethod()))
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            else
+                response.setContentLength(0);
+        }
+    }
+}
diff --git a/jetty-server/src/test/resources/reload_keystore_1.jks b/jetty-server/src/test/resources/reload_keystore_1.jks
new file mode 100644
index 0000000..d615c22
--- /dev/null
+++ b/jetty-server/src/test/resources/reload_keystore_1.jks
Binary files differ
diff --git a/jetty-server/src/test/resources/reload_keystore_2.jks b/jetty-server/src/test/resources/reload_keystore_2.jks
new file mode 100644
index 0000000..3707c3a
--- /dev/null
+++ b/jetty-server/src/test/resources/reload_keystore_2.jks
Binary files differ
diff --git a/jetty-server/src/test/resources/snikeystore b/jetty-server/src/test/resources/snikeystore
new file mode 100644
index 0000000..3c69266
--- /dev/null
+++ b/jetty-server/src/test/resources/snikeystore
Binary files differ
diff --git a/jetty-server/src/test/resources/snikeystore_nowild b/jetty-server/src/test/resources/snikeystore_nowild
new file mode 100644
index 0000000..91c6166
--- /dev/null
+++ b/jetty-server/src/test/resources/snikeystore_nowild
Binary files differ
diff --git a/jetty-servlet/pom.xml b/jetty-servlet/pom.xml
index a11c377..bd8a6b9 100644
--- a/jetty-servlet/pom.xml
+++ b/jetty-servlet/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <artifactId>jetty-project</artifactId>
     <groupId>org.eclipse.jetty</groupId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-servlet</artifactId>
@@ -16,34 +16,8 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",org.eclipse.jetty.jmx.*;version="9.1";resolution:=optional,*</Import-Package>
-                <_nouses>true</_nouses>
-              </instructions>
-            </configuration>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
         <executions>
           <execution>
             <id>tests</id>
@@ -54,23 +28,6 @@
         </executions>
       </plugin>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
@@ -96,5 +53,12 @@
       <artifactId>jetty-test-helper</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-http</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/BaseHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/BaseHolder.java
index 6545b2f..a732389 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/BaseHolder.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/BaseHolder.java
@@ -38,7 +38,7 @@
  * Base class for all servlet-related classes that may be lazily instantiated  (eg servlet, filter, 
  * listener), and/or require metadata to be held regarding their origin 
  * (web.xml, annotation, programmatic api etc).
- * 
+ * @param <T> the type of holder
  */
 public abstract class BaseHolder<T> extends AbstractLifeCycle implements Dumpable
 {
@@ -68,7 +68,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Do any setup necessary after starting
-     * @throws Exception
+     * @throws Exception if unable to initialize
      */
     public void initialize()
     throws Exception
@@ -85,7 +85,7 @@
     {
         //if no class already loaded and no classname, make permanently unavailable
         if (_class==null && (_className==null || _className.equals("")))
-            throw new UnavailableException("No class in holder");
+            throw new UnavailableException("No class in holder "+toString());
         
         //try to load class
         if (_class==null)
@@ -99,7 +99,7 @@
             catch (Exception e)
             {
                 LOG.warn(e);
-                throw new UnavailableException(e.getMessage());
+                throw new UnavailableException("Class loading error for holder "+toString());
             }
         }
     }
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java
index 22689e0..38234b9 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java
@@ -18,6 +18,9 @@
 
 package org.eclipse.jetty.servlet;
 
+import static org.eclipse.jetty.http.GzipHttpContent.ETAG_GZIP_QUOTE;
+import static org.eclipse.jetty.http.GzipHttpContent.removeGzipFromETag;
+
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -39,25 +42,28 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.DateParser;
 import org.eclipse.jetty.http.HttpContent;
 import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpGenerator.CachedHttpField;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.MimeTypes;
 import org.eclipse.jetty.http.PathMap.MappedEntry;
+import org.eclipse.jetty.http.PreEncodedHttpField;
+import org.eclipse.jetty.http.QuotedCSV;
 import org.eclipse.jetty.io.WriterOutputStream;
 import org.eclipse.jetty.server.HttpOutput;
 import org.eclipse.jetty.server.InclusiveByteRange;
+import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.ResourceCache;
+import org.eclipse.jetty.server.ResourceContentFactory;
 import org.eclipse.jetty.server.Response;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.MultiPartOutputStream;
-import org.eclipse.jetty.util.QuotedStringTokenizer;
 import org.eclipse.jetty.util.URIUtil;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -66,16 +72,15 @@
 import org.eclipse.jetty.util.resource.ResourceFactory;
 
 
-
-/* ------------------------------------------------------------ */
-/** The default servlet.
- * 
+/** 
+ * The default servlet.
+ * <p>
  * This servlet, normally mapped to /, provides the handling for static
  * content, OPTION and TRACE methods for the context.
  * The following initParameters are supported, these can be set either
  * on the servlet itself or as ServletContext initParameters with a prefix
  * of org.eclipse.jetty.servlet.Default. :
- * <PRE>
+ * <pre>
  *  acceptRanges      If true, range requests and responses are
  *                    supported
  *
@@ -134,10 +139,7 @@
  *                    Other file extensions that signify that a file is gzip compressed. Eg ".svgz"
  *
  *
- * </PRE>
- *
- *
- *
+ * </pre>
  *
  */
 public class DefaultServlet extends HttpServlet implements ResourceFactory
@@ -146,7 +148,7 @@
 
     private static final long serialVersionUID = 4930458713846881193L;
     
-    private static final CachedHttpField ACCEPT_RANGES = new CachedHttpField(HttpHeader.ACCEPT_RANGES, "bytes");
+    private static final PreEncodedHttpField ACCEPT_RANGES = new PreEncodedHttpField(HttpHeader.ACCEPT_RANGES, "bytes");
     
     private ServletContext _servletContext;
     private ContextHandler _contextHandler;
@@ -162,6 +164,7 @@
 
     private Resource _resourceBase;
     private ResourceCache _cache;
+    private HttpContent.Factory _contentFactory;
 
     private MimeTypes _mimeTypes;
     private String[] _welcomes;
@@ -243,8 +246,8 @@
 
         String cc=getInitParameter("cacheControl");
         if (cc!=null)
-            _cacheControl=new CachedHttpField(HttpHeader.CACHE_CONTROL, cc);
-        
+            _cacheControl=new PreEncodedHttpField(HttpHeader.CACHE_CONTROL, cc);
+
         String resourceCache = getInitParameter("resourceCache");
         int max_cache_size=getInitInt("maxCacheSize", -2);
         int max_cached_file_size=getInitInt("maxCachedFileSize", -2);
@@ -258,23 +261,23 @@
             _cache=(ResourceCache)_servletContext.getAttribute(resourceCache);
 
             if (LOG.isDebugEnabled())
-                LOG.debug("Cache {}={}",resourceCache,_cache);
+                LOG.debug("Cache {}={}",resourceCache,_contentFactory);
         }
 
         _etags = getInitBoolean("etags",_etags);
-        
+
         try
         {
             if (_cache==null && (max_cached_files!=-2 || max_cache_size!=-2 || max_cached_file_size!=-2))
             {
-                _cache= new ResourceCache(null,this,_mimeTypes,_useFileMappedBuffer,_etags);
-
+                _cache = new ResourceCache(null,this,_mimeTypes,_useFileMappedBuffer,_etags,_gzip);
                 if (max_cache_size>=0)
                     _cache.setMaxCacheSize(max_cache_size);
                 if (max_cached_file_size>=-1)
                     _cache.setMaxCachedFileSize(max_cached_file_size);
                 if (max_cached_files>=-1)
                     _cache.setMaxCachedFiles(max_cached_files);
+                _servletContext.setAttribute(resourceCache==null?"resourceCache":resourceCache,_cache);
             }
         }
         catch (Exception e)
@@ -282,37 +285,45 @@
             LOG.warn(Log.EXCEPTION,e);
             throw new UnavailableException(e.toString());
         }
+
+        if (_cache!=null)
+            _contentFactory=_cache;
+        else
+        {
+            _contentFactory=new ResourceContentFactory(this,_mimeTypes,_gzip);
+            if (resourceCache!=null)
+                _servletContext.setAttribute(resourceCache,_contentFactory);
+        }
         
-       _gzipEquivalentFileExtensions = new ArrayList<String>();
-       String otherGzipExtensions = getInitParameter("otherGzipFileExtensions");
-       if (otherGzipExtensions != null)
-       {
-           //comma separated list
-           StringTokenizer tok = new StringTokenizer(otherGzipExtensions,",",false);
-           while (tok.hasMoreTokens())
-           {
-               String s = tok.nextToken().trim();
-               _gzipEquivalentFileExtensions.add((s.charAt(0)=='.'?s:"."+s));
-           }
-       }
-       else
-       {
-           //.svgz files are gzipped svg files and must be served with Content-Encoding:gzip
-           _gzipEquivalentFileExtensions.add(".svgz");   
-       }
+        _gzipEquivalentFileExtensions = new ArrayList<String>();
+        String otherGzipExtensions = getInitParameter("otherGzipFileExtensions");
+        if (otherGzipExtensions != null)
+        {
+            //comma separated list
+            StringTokenizer tok = new StringTokenizer(otherGzipExtensions,",",false);
+            while (tok.hasMoreTokens())
+            {
+                String s = tok.nextToken().trim();
+                _gzipEquivalentFileExtensions.add((s.charAt(0)=='.'?s:"."+s));
+            }
+        }
+        else
+        {
+            //.svgz files are gzipped svg files and must be served with Content-Encoding:gzip
+            _gzipEquivalentFileExtensions.add(".svgz");   
+        }
 
-       _servletHandler= _contextHandler.getChildHandlerByClass(ServletHandler.class);
-       for (ServletHolder h :_servletHandler.getServlets())
-           if (h.getServletInstance()==this)
-               _defaultHolder=h;
+        _servletHandler= _contextHandler.getChildHandlerByClass(ServletHandler.class);
+        for (ServletHolder h :_servletHandler.getServlets())
+            if (h.getServletInstance()==this)
+                _defaultHolder=h;
 
-
-       if (LOG.isDebugEnabled())
-           LOG.debug("resource base = "+_resourceBase);
+        if (LOG.isDebugEnabled())
+            LOG.debug("resource base = "+_resourceBase);
     }
 
     /**
-     * Compute the field _contextHandler.<br/>
+     * Compute the field _contextHandler.<br>
      * In the case where the DefaultServlet is deployed on the HttpService it is likely that
      * this method needs to be overwritten to unwrap the ServletContext facade until we reach
      * the original jetty's ContextHandler.
@@ -423,8 +434,8 @@
         String servletPath=null;
         String pathInfo=null;
         Enumeration<String> reqRanges = null;
-        Boolean included =request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI)!=null;
-        if (included!=null && included.booleanValue())
+        boolean included =request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI)!=null;
+        if (included)
         {
             servletPath=(String)request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
             pathInfo=(String)request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
@@ -436,7 +447,6 @@
         }
         else
         {
-            included = Boolean.FALSE;
             servletPath = _pathInfoOnly?"/":request.getServletPath();
             pathInfo = request.getPathInfo();
 
@@ -448,155 +458,73 @@
 
         String pathInContext=URIUtil.addPaths(servletPath,pathInfo);
         boolean endsWithSlash=(pathInfo==null?request.getServletPath():pathInfo).endsWith(URIUtil.SLASH);
-
-
-        // Find the resource and content
-        Resource resource=null;
+        boolean gzippable=_gzip && !endsWithSlash && !included && reqRanges==null;
+        
         HttpContent content=null;
-        boolean close_content=true;
+        boolean release_content=true;
         try
         {
-            // is gzip enabled?
-            String pathInContextGz=null;
-            boolean gzip=false;
-            if (!included.booleanValue() && _gzip && reqRanges==null && !endsWithSlash )
-            {
-                // Look for a gzip resource
-                pathInContextGz=pathInContext+".gz";
-                if (_cache==null)
-                    resource=getResource(pathInContextGz);
-                else
-                {
-                    content=_cache.lookup(pathInContextGz);
-                    resource=(content==null)?null:content.getResource();
-                }
-
-                // Does a gzip resource exist?
-                if (resource!=null && resource.exists() && !resource.isDirectory())
-                {
-                    // Tell caches that response may vary by accept-encoding
-                    response.addHeader(HttpHeader.VARY.asString(),HttpHeader.ACCEPT_ENCODING.asString());
-                    
-                    // Does the client accept gzip?
-                    String accept=request.getHeader(HttpHeader.ACCEPT_ENCODING.asString());
-                    if (accept!=null && accept.indexOf("gzip")>=0)
-                        gzip=true;
-                }
-            }
-
-            // find resource
-            if (!gzip)
-            {
-                if (_cache==null)
-                    resource=getResource(pathInContext);
-                else
-                {
-                    content=_cache.lookup(pathInContext);
-                    resource=content==null?null:content.getResource();
-                }
-            }
-
+            // Find the content
+            content=_contentFactory.getContent(pathInContext,response.getBufferSize());
             if (LOG.isDebugEnabled())
-                LOG.debug(String.format("uri=%s, resource=%s, content=%s",request.getRequestURI(),resource,content));
-
-            // Handle resource
-            if (resource==null || !resource.exists())
+                LOG.info("content={}",content);
+            
+            // Not found?
+            if (content==null || !content.getResource().exists())
             {
                 if (included)
                     throw new FileNotFoundException("!" + pathInContext);
                 response.sendError(HttpServletResponse.SC_NOT_FOUND);
+                return;
             }
-            else if (!resource.isDirectory())
+            
+            // Directory?
+            if (content.getResource().isDirectory())
             {
-                if (endsWithSlash && pathInContext.length()>1)
-                {
-                    String q=request.getQueryString();
-                    pathInContext=pathInContext.substring(0,pathInContext.length()-1);
-                    if (q!=null&&q.length()!=0)
-                        pathInContext+="?"+q;
-                    response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(_servletContext.getContextPath(),pathInContext)));
-                }
-                else
-                {
-                    // ensure we have content
-                    if (content==null)
-                        content=new HttpContent.ResourceAsHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),response.getBufferSize(),_etags);
-
-                    if (included.booleanValue() || passConditionalHeaders(request,response, resource,content))
-                    {
-                        if (gzip || isGzippedContent(pathInContext))
-                        {
-                            response.setHeader(HttpHeader.CONTENT_ENCODING.asString(),"gzip");
-                            String mt=_servletContext.getMimeType(pathInContext);
-                            if (mt!=null)
-                                response.setContentType(mt);
-                        }
-                        close_content=sendData(request,response,included.booleanValue(),resource,content,reqRanges);
-                    }
-                }
+                sendWelcome(content,pathInContext,endsWithSlash,included,request,response);
+                return;
             }
-            else
+            
+            // Strip slash?
+            if (endsWithSlash && pathInContext.length()>1)
             {
-                String welcome=null;
-
-                if (!endsWithSlash || (pathInContext.length()==1 && request.getAttribute("org.eclipse.jetty.server.nullPathInfo")!=null))
-                {
-                    StringBuffer buf=request.getRequestURL();
-                    synchronized(buf)
-                    {
-                        int param=buf.lastIndexOf(";");
-                        if (param<0)
-                            buf.append('/');
-                        else
-                            buf.insert(param,'/');
-                        String q=request.getQueryString();
-                        if (q!=null&&q.length()!=0)
-                        {
-                            buf.append('?');
-                            buf.append(q);
-                        }
-                        response.setContentLength(0);
-                        response.sendRedirect(response.encodeRedirectURL(buf.toString()));
-                    }
-                }
-                // else look for a welcome file
-                else if (null!=(welcome=getWelcomeFile(pathInContext)))
+                String q=request.getQueryString();
+                pathInContext=pathInContext.substring(0,pathInContext.length()-1);
+                String uri = URIUtil.addPaths(_servletContext.getContextPath(),pathInContext);
+                if (q!=null&&q.length()!=0)
+                    uri+="?"+q;
+                response.sendRedirect(response.encodeRedirectURL(uri));
+                return;
+            }
+            
+            // Conditional response?
+            if (!included && !passConditionalHeaders(request,response,content))
+                return;
+                
+            // Gzip?
+            HttpContent gzip_content = gzippable?content.getGzipContent():null;
+            if (gzip_content!=null)
+            {
+                // Tell caches that response may vary by accept-encoding
+                response.addHeader(HttpHeader.VARY.asString(),HttpHeader.ACCEPT_ENCODING.asString());
+                
+                // Does the client accept gzip?
+                String accept=request.getHeader(HttpHeader.ACCEPT_ENCODING.asString());
+                if (accept!=null && accept.indexOf("gzip")>=0)
                 {
                     if (LOG.isDebugEnabled())
-                        LOG.debug("welcome={}",welcome);
-                    if (_redirectWelcome)
-                    {
-                        // Redirect to the index
-                        response.setContentLength(0);
-                        String q=request.getQueryString();
-                        if (q!=null&&q.length()!=0)
-                            response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)+"?"+q));
-                        else
-                            response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)));
-                    }
-                    else
-                    {
-                        // Forward to the index
-                        RequestDispatcher dispatcher=request.getRequestDispatcher(welcome);
-                        if (dispatcher!=null)
-                        {
-                            if (included.booleanValue())
-                                dispatcher.include(request,response);
-                            else
-                            {
-                                request.setAttribute("org.eclipse.jetty.server.welcome",welcome);
-                                dispatcher.forward(request,response);
-                            }
-                        }
-                    }
-                }
-                else
-                {
-                    content=new HttpContent.ResourceAsHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),_etags);
-                    if (included.booleanValue() || passConditionalHeaders(request,response, resource,content))
-                        sendDirectory(request,response,resource,pathInContext);
+                        LOG.debug("gzip={}",gzip_content);
+                    content=gzip_content;
                 }
             }
+
+            // TODO this should be done by HttpContent#getContentEncoding
+            if (isGzippedContent(pathInContext))
+                response.setHeader(HttpHeader.CONTENT_ENCODING.asString(),"gzip");
+                
+            // Send the data
+            release_content=sendData(request,response,included,content,reqRanges);
+            
         }
         catch(IllegalArgumentException e)
         {
@@ -606,21 +534,80 @@
         }
         finally
         {
-            if (close_content)
+            if (release_content)
             {
                 if (content!=null)
                     content.release();
-                else if (resource!=null)
-                    resource.close();
             }
         }
 
     }
 
-    /**
-     * @param resource
-     * @return
-     */
+    protected void sendWelcome(HttpContent content, String pathInContext, boolean endsWithSlash, boolean included, HttpServletRequest request, HttpServletResponse response)
+        throws ServletException, IOException
+    {                
+        // Redirect to directory
+        if (!endsWithSlash || (pathInContext.length()==1 && request.getAttribute("org.eclipse.jetty.server.nullPathInfo")!=null))
+        {
+            StringBuffer buf=request.getRequestURL();
+            synchronized(buf)
+            {
+                int param=buf.lastIndexOf(";");
+                if (param<0)
+                    buf.append('/');
+                else
+                    buf.insert(param,'/');
+                String q=request.getQueryString();
+                if (q!=null&&q.length()!=0)
+                {
+                    buf.append('?');
+                    buf.append(q);
+                }
+                response.setContentLength(0);
+                response.sendRedirect(response.encodeRedirectURL(buf.toString()));
+            }
+            return;
+        }
+        
+        // look for a welcome file
+        String welcome=getWelcomeFile(pathInContext);
+        if (welcome!=null)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("welcome={}",welcome);
+            if (_redirectWelcome)
+            {
+                // Redirect to the index
+                response.setContentLength(0);
+                String uri=URIUtil.encodePath(URIUtil.addPaths( _servletContext.getContextPath(),welcome));
+                String q=request.getQueryString();
+                if (q!=null&&!q.isEmpty())
+                    uri+="?"+q;
+                response.sendRedirect(response.encodeRedirectURL(uri));
+            }
+            else
+            {
+                // Forward to the index
+                RequestDispatcher dispatcher=_servletContext.getRequestDispatcher(welcome);
+                if (dispatcher!=null)
+                {
+                    if (included)
+                        dispatcher.include(request,response);
+                    else
+                    {
+                        request.setAttribute("org.eclipse.jetty.server.welcome",welcome);
+                        dispatcher.forward(request,response);
+                    }
+                }
+            }
+            return;
+        }
+         
+        if (included || passConditionalHeaders(request,response, content))
+            sendDirectory(request,response,content.getResource(),pathInContext);
+    }
+
+    /* ------------------------------------------------------------ */
     protected boolean isGzippedContent(String path)
     {
         if (path == null) return false;
@@ -687,7 +674,7 @@
             String welcome_in_context=URIUtil.addPaths(pathInContext,_welcomes[i]);
             Resource welcome=getResource(welcome_in_context);
             if (welcome!=null && welcome.exists())
-                return _welcomes[i];
+                return welcome_in_context;
 
             if ((_welcomeServlets || _welcomeExactServlets) && welcome_servlet==null)
             {
@@ -704,27 +691,70 @@
     /* ------------------------------------------------------------ */
     /* Check modification date headers.
      */
-    protected boolean passConditionalHeaders(HttpServletRequest request,HttpServletResponse response, Resource resource, HttpContent content)
+    protected boolean passConditionalHeaders(HttpServletRequest request,HttpServletResponse response, HttpContent content)
     throws IOException
     {
         try
         {
+            String ifm=null;
+            String ifnm=null;
+            String ifms=null;
+            long ifums=-1;
+            
+            if (request instanceof Request)
+            {
+                // Find multiple fields by iteration as an optimization 
+                HttpFields fields = ((Request)request).getHttpFields();
+                for (int i=fields.size();i-->0;)
+                {
+                    HttpField field=fields.getField(i);
+                    if (field.getHeader() != null)
+                    {
+                        switch (field.getHeader())
+                        {
+                            case IF_MATCH:
+                                ifm=field.getValue();
+                                break;
+                            case IF_NONE_MATCH:
+                                ifnm=field.getValue();
+                                break;
+                            case IF_MODIFIED_SINCE:
+                                ifms=field.getValue();
+                                break;
+                            case IF_UNMODIFIED_SINCE:
+                                ifums=DateParser.parseDate(field.getValue());
+                                break;
+                            default:
+                        }
+                    }
+                }
+            }
+            else
+            {
+                ifm=request.getHeader(HttpHeader.IF_MATCH.asString());
+                ifnm=request.getHeader(HttpHeader.IF_NONE_MATCH.asString());
+                ifms=request.getHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
+                ifums=request.getDateHeader(HttpHeader.IF_UNMODIFIED_SINCE.asString());
+            }
+            
             if (!HttpMethod.HEAD.is(request.getMethod()))
             {
                 if (_etags)
                 {
-                    String ifm=request.getHeader(HttpHeader.IF_MATCH.asString());
+                    String etag=content.getETagValue();
                     if (ifm!=null)
                     {
                         boolean match=false;
-                        if (content.getETag()!=null)
+                        if (etag!=null)
                         {
-                            QuotedStringTokenizer quoted = new QuotedStringTokenizer(ifm,", ",false,true);
-                            while (!match && quoted.hasMoreTokens())
+                            QuotedCSV quoted = new QuotedCSV(true,ifm);
+                            for (String tag : quoted)
                             {
-                                String tag = quoted.nextToken();
-                                if (content.getETag().equals(tag))
+                                if (etag.equals(tag) || tag.endsWith(ETAG_GZIP_QUOTE) && etag.equals(removeGzipFromETag(tag)))
+                                {
                                     match=true;
+                                    break;
+                                }
                             }
                         }
 
@@ -735,34 +765,24 @@
                         }
                     }
                     
-                    String if_non_match_etag=request.getHeader(HttpHeader.IF_NONE_MATCH.asString());
-                    if (if_non_match_etag!=null && content.getETag()!=null)
+                    if (ifnm!=null && etag!=null)
                     {
-                        // Look for GzipFiltered version of etag
-                        if (content.getETag().equals(request.getAttribute("o.e.j.s.GzipFilter.ETag")))
+                        // Handle special case of exact match OR gzip exact match
+                        if (etag.equals(ifnm) || ifnm.endsWith(ETAG_GZIP_QUOTE) && ifnm.indexOf(',')<0 && etag.equals(removeGzipFromETag(ifnm)))
                         {
                             response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
-                            response.setHeader(HttpHeader.ETAG.asString(),if_non_match_etag);
+                            response.setHeader(HttpHeader.ETAG.asString(),ifnm);
                             return false;
                         }
                         
-                        // Handle special case of exact match.
-                        if (content.getETag().equals(if_non_match_etag))
-                        {
-                            response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
-                            response.setHeader(HttpHeader.ETAG.asString(),content.getETag());
-                            return false;
-                        }
-
                         // Handle list of tags
-                        QuotedStringTokenizer quoted = new QuotedStringTokenizer(if_non_match_etag,", ",false,true);
-                        while (quoted.hasMoreTokens())
+                        QuotedCSV quoted = new QuotedCSV(true,ifnm);
+                        for (String tag : quoted)
                         {
-                            String tag = quoted.nextToken();
-                            if (content.getETag().equals(tag))
+                            if (etag.equals(tag) || tag.endsWith(ETAG_GZIP_QUOTE) && etag.equals(removeGzipFromETag(tag))) 
                             {
                                 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
-                                response.setHeader(HttpHeader.ETAG.asString(),content.getETag());
+                                response.setHeader(HttpHeader.ETAG.asString(),tag);
                                 return false;
                             }
                         }
@@ -773,34 +793,32 @@
                 }
                 
                 // Handle if modified since
-                String ifms=request.getHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
                 if (ifms!=null)
                 {
                     //Get jetty's Response impl
-                    String mdlm=content.getLastModified();
+                    String mdlm=content.getLastModifiedValue();
                     if (mdlm!=null && ifms.equals(mdlm))
                     {
                         response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                         if (_etags)
-                            response.setHeader(HttpHeader.ETAG.asString(),content.getETag());
+                            response.setHeader(HttpHeader.ETAG.asString(),content.getETagValue());
                         response.flushBuffer();
                         return false;
                     }
 
                     long ifmsl=request.getDateHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
-                    if (ifmsl!=-1 && resource.lastModified()/1000 <= ifmsl/1000)
+                    if (ifmsl!=-1 && content.getResource().lastModified()/1000 <= ifmsl/1000)
                     { 
                         response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                         if (_etags)
-                            response.setHeader(HttpHeader.ETAG.asString(),content.getETag());
+                            response.setHeader(HttpHeader.ETAG.asString(),content.getETagValue());
                         response.flushBuffer();
                         return false;
                     }
                 }
 
                 // Parse the if[un]modified dates and compare to resource
-                long date=request.getDateHeader(HttpHeader.IF_UNMODIFIED_SINCE.asString());
-                if (date!=-1 && resource.lastModified()/1000 > date/1000)
+                if (ifums!=-1 && content.getResource().lastModified()/1000 > ifums/1000)
                 {
                     response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
                     return false;
@@ -853,8 +871,8 @@
             return;
         }
 
-        data=dir.getBytes("UTF-8");
-        response.setContentType("text/html; charset=UTF-8");
+        data=dir.getBytes("utf-8");
+        response.setContentType("text/html;charset=utf-8");
         response.setContentLength(data.length);
         response.getOutputStream().write(data);
     }
@@ -863,12 +881,11 @@
     protected boolean sendData(HttpServletRequest request,
             HttpServletResponse response,
             boolean include,
-            Resource resource,
             final HttpContent content,
             Enumeration<String> reqRanges)
     throws IOException
     {
-        final long content_length = (content==null)?resource.length():content.getContentLength();
+        final long content_length = content.getContentLengthValue();
         
         // Get the output stream (or writer)
         OutputStream out =null;
@@ -877,7 +894,7 @@
         {
             out = response.getOutputStream();
 
-            // has a filter already written to the response?
+            // has something already written to the response?
             written = out instanceof HttpOutput
                 ? ((HttpOutput)out).isWritten()
                 : true;
@@ -896,31 +913,25 @@
             //  if there were no ranges, send entire entity
             if (include)
             {
-                resource.writeTo(out,0,content_length);
+                // write without headers
+                content.getResource().writeTo(out,0,content_length);
             }
             // else if we can't do a bypass write because of wrapping
-            else if (content==null || written || !(out instanceof HttpOutput))
+            else if (written || !(out instanceof HttpOutput))
             {
                 // write normally
-                writeHeaders(response,content,written?-1:content_length);
-                ByteBuffer buffer = (content==null)?null:content.getIndirectBuffer();
+                putHeaders(response,content,written?-1:0);
+                ByteBuffer buffer = content.getIndirectBuffer();
                 if (buffer!=null)
                     BufferUtil.writeTo(buffer,out);
                 else
-                    resource.writeTo(out,0,content_length);
+                    content.getResource().writeTo(out,0,content_length);
             }
             // else do a bypass write
             else
             {
                 // write the headers
-                if (response instanceof Response)
-                {
-                    Response r = (Response)response;
-                    writeOptionHeaders(r.getHttpFields());
-                    r.setHeaders(content);
-                }
-                else
-                    writeHeaders(response,content,content_length);
+                putHeaders(response,content,0);
 
                 // write the content asynchronously if supported
                 if (request.isAsyncSupported())
@@ -958,7 +969,6 @@
                 }
                 // otherwise write content blocking
                 ((HttpOutput)out).sendContent(content);
-                
             }
         }
         else
@@ -969,11 +979,11 @@
             //  if there are no satisfiable ranges, send 416 response
             if (ranges==null || ranges.size()==0)
             {
-                writeHeaders(response, content, content_length);
+                putHeaders(response,content,0);
                 response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                 response.setHeader(HttpHeader.CONTENT_RANGE.asString(),
                         InclusiveByteRange.to416HeaderRangeString(content_length));
-                resource.writeTo(out,0,content_length);
+                content.getResource().writeTo(out,0,content_length);
                 return true;
             }
 
@@ -983,13 +993,13 @@
             {
                 InclusiveByteRange singleSatisfiableRange = ranges.get(0);
                 long singleLength = singleSatisfiableRange.getSize(content_length);
-                writeHeaders(response,content,singleLength                     );
+                putHeaders(response,content,singleLength);
                 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                 if (!response.containsHeader(HttpHeader.DATE.asString()))
                     response.addDateHeader(HttpHeader.DATE.asString(),System.currentTimeMillis());
                 response.setHeader(HttpHeader.CONTENT_RANGE.asString(),
                         singleSatisfiableRange.toHeaderRangeString(content_length));
-                resource.writeTo(out,singleSatisfiableRange.getFirst(content_length),singleLength);
+                content.getResource().writeTo(out,singleSatisfiableRange.getFirst(content_length),singleLength);
                 return true;
             }
 
@@ -997,8 +1007,8 @@
             //  216 response which does not require an overall
             //  content-length header
             //
-            writeHeaders(response,content,-1);
-            String mimetype=(content==null?null:content.getContentType());
+            putHeaders(response,content,-1);
+            String mimetype=(content==null?null:content.getContentTypeValue());
             if (mimetype==null)
                 LOG.warn("Unknown mimetype for "+request.getRequestURI());
             MultiPartOutputStream multi = new MultiPartOutputStream(out);
@@ -1016,7 +1026,7 @@
                 ctp = "multipart/byteranges; boundary=";
             response.setContentType(ctp+multi.getBoundary());
 
-            InputStream in=resource.getInputStream();
+            InputStream in=content.getResource().getInputStream();
             long pos=0;
 
             // calculate the content-length
@@ -1050,7 +1060,7 @@
                     if (start<pos)
                     {
                         in.close();
-                        in=resource.getInputStream();
+                        in=content.getResource().getInputStream();
                         pos=0;
                     }
                     if (pos<start)
@@ -1064,7 +1074,7 @@
                 }
                 else
                     // Handle cached resource
-                    (resource).writeTo(multi,start,size);
+                    content.getResource().writeTo(multi,start,size);
             }
             if (in!=null)
                 in.close();
@@ -1074,83 +1084,31 @@
     }
 
     /* ------------------------------------------------------------ */
-    protected void writeHeaders(HttpServletResponse response,HttpContent content,long count)
+    protected void putHeaders(HttpServletResponse response,HttpContent content, long contentLength)
     {
-        if (content == null)
-        {
-            // No content, then no headers to process
-            // This is possible during bypass write because of wrapping
-            // See .sendData() for more details.
-            return;
-        }
-        
-        if (content.getContentType()!=null && response.getContentType()==null)
-            response.setContentType(content.getContentType().toString());
-
         if (response instanceof Response)
         {
-            Response r=(Response)response;
-            HttpFields fields = r.getHttpFields();
+            Response r = (Response)response;
+            r.putHeaders(content,contentLength,_etags);
+            HttpFields f = r.getHttpFields();
+            if (_acceptRanges)
+                f.put(ACCEPT_RANGES);
 
-            if (content.getLastModified()!=null)
-                fields.put(HttpHeader.LAST_MODIFIED,content.getLastModified());
-            else if (content.getResource()!=null)
-            {
-                long lml=content.getResource().lastModified();
-                if (lml!=-1)
-                    fields.putDateField(HttpHeader.LAST_MODIFIED,lml);
-            }
-
-            if (count != -1)
-                r.setLongContentLength(count);
-
-            writeOptionHeaders(fields);
-            
-            if (_etags)
-                fields.put(HttpHeader.ETAG,content.getETag());
+            if (_cacheControl!=null)
+                f.put(_cacheControl);
         }
         else
         {
-            long lml=content.getResource().lastModified();
-            if (lml>=0)
-                response.setDateHeader(HttpHeader.LAST_MODIFIED.asString(),lml);
+            Response.putHeaders(response,content,contentLength,_etags);
+            if (_acceptRanges)
+                response.setHeader(ACCEPT_RANGES.getName(),ACCEPT_RANGES.getValue());
 
-            if (count != -1)
-            {
-                if (count<Integer.MAX_VALUE)
-                    response.setContentLength((int)count);
-                else
-                    response.setHeader(HttpHeader.CONTENT_LENGTH.asString(),Long.toString(count));
-            }
-
-            writeOptionHeaders(response);
-
-            if (_etags)
-                response.setHeader(HttpHeader.ETAG.asString(),content.getETag());
+            if (_cacheControl!=null)
+                response.setHeader(_cacheControl.getName(),_cacheControl.getValue());
         }
     }
 
     /* ------------------------------------------------------------ */
-    protected void writeOptionHeaders(HttpFields fields)
-    {
-        if (_acceptRanges)
-            fields.put(ACCEPT_RANGES);
-
-        if (_cacheControl!=null)
-            fields.put(_cacheControl);
-    }
-
-    /* ------------------------------------------------------------ */
-    protected void writeOptionHeaders(HttpServletResponse response)
-    {
-        if (_acceptRanges)
-            response.setHeader(HttpHeader.ACCEPT_RANGES.asString(),"bytes");
-
-        if (_cacheControl!=null)
-            response.setHeader(HttpHeader.CACHE_CONTROL.asString(),_cacheControl.getValue());
-    }
-
-    /* ------------------------------------------------------------ */
     /*
      * @see javax.servlet.Servlet#destroy()
      */
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java
index d02f8b2..8c03a95 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java
@@ -30,17 +30,20 @@
 import org.eclipse.jetty.server.Dispatcher;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
 
 /* ------------------------------------------------------------ */
 /** Error Page Error Handler
  *
  * An ErrorHandler that maps exceptions and status codes to URIs for dispatch using
  * the internal ERROR style of dispatch.
- *
  */
 public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler.ErrorPageMapper
 {
     public final static String GLOBAL_ERROR_PAGE = "org.eclipse.jetty.server.error_page.global";
+    private static final Logger LOG = Log.getLogger(ErrorPageErrorHandler.class);
+    enum PageLookupTechnique{ THROWABLE, STATUS_CODE, GLOBAL } 
 
     protected ServletContext _servletContext;
     private final Map<String,String> _errorPages= new HashMap<String,String>(); // code or exception to URL
@@ -56,13 +59,18 @@
     {
         String error_page= null;
 
+        PageLookupTechnique pageSource = null;
+
+        Class<?> matchedThrowable = null;
         Throwable th= (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION);
 
         // Walk the cause hierarchy
         while (error_page == null && th != null )
         {
+            pageSource = PageLookupTechnique.THROWABLE;
+            
             Class<?> exClass=th.getClass();
-            error_page= (String)_errorPages.get(exClass.getName());
+            error_page= _errorPages.get(exClass.getName());
 
             // walk the inheritance hierarchy
             while (error_page == null)
@@ -70,19 +78,26 @@
                 exClass= exClass.getSuperclass();
                 if (exClass==null)
                     break;
-                error_page= (String)_errorPages.get(exClass.getName());
+                error_page = _errorPages.get(exClass.getName());
             }
 
+            if(error_page != null)
+                matchedThrowable = exClass;
+
             th=(th instanceof ServletException)?((ServletException)th).getRootCause():null;
         }
 
+        Integer errorStatusCode = null;
+        
         if (error_page == null)
         {
+            pageSource = PageLookupTechnique.STATUS_CODE;
+            
             // look for an exact code match
-            Integer code=(Integer)request.getAttribute(Dispatcher.ERROR_STATUS_CODE);
-            if (code!=null)
+            errorStatusCode = (Integer)request.getAttribute(Dispatcher.ERROR_STATUS_CODE);
+            if (errorStatusCode!=null)
             {
-                error_page= (String)_errorPages.get(Integer.toString(code));
+                error_page= (String)_errorPages.get(Integer.toString(errorStatusCode));
 
                 // if still not found
                 if ((error_page == null) && (_errorPageList != null))
@@ -91,7 +106,7 @@
                     for (int i = 0; i < _errorPageList.size(); i++)
                     {
                         ErrorCodeRange errCode = (ErrorCodeRange) _errorPageList.get(i);
-                        if (errCode.isInRange(code))
+                        if (errCode.isInRange(errorStatusCode))
                         {
                             error_page = errCode.getUri();
                             break;
@@ -103,7 +118,41 @@
 
         //try servlet 3.x global error page
         if (error_page == null)
+        {
+            pageSource = PageLookupTechnique.GLOBAL;
             error_page = _errorPages.get(GLOBAL_ERROR_PAGE);
+        }
+        
+        if (LOG.isDebugEnabled())
+        {
+            StringBuilder dbg = new StringBuilder();
+            dbg.append("getErrorPage(");
+            dbg.append(request.getMethod()).append(' ');
+            dbg.append(request.getRequestURI());
+            dbg.append(") => error_page=").append(error_page);
+            switch (pageSource)
+            {
+                case THROWABLE:
+                    dbg.append(" (using matched Throwable ");
+                    dbg.append(matchedThrowable.getName());
+                    dbg.append(" / actually thrown as ");
+                    Throwable originalThrowable = (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION);
+                    dbg.append(originalThrowable.getClass().getName());
+                    dbg.append(')');
+                    LOG.debug(dbg.toString(),th);
+                    break;
+                case STATUS_CODE:
+                    dbg.append(" (from status code ");
+                    dbg.append(errorStatusCode);
+                    dbg.append(')');
+                    LOG.debug(dbg.toString());
+                    break;
+                case GLOBAL:
+                    dbg.append(" (from global default)");
+                    LOG.debug(dbg.toString());
+                    break;
+            }
+        }
         
         return error_page;
     }
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java
index b11373d..773cd74 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java
@@ -37,10 +37,6 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-/* --------------------------------------------------------------------- */
-/**
- *
- */
 public class FilterHolder extends Holder<Filter>
 {
     private static final Logger LOG = Log.getLogger(FilterHolder.class);
@@ -61,6 +57,7 @@
 
     /* ---------------------------------------------------------------- */
     /** Constructor
+     * @param source the holder source
      */
     public FilterHolder(Holder.Source source)
     {
@@ -69,6 +66,7 @@
 
     /* ---------------------------------------------------------------- */
     /** Constructor
+     * @param filter the filter class
      */
     public FilterHolder(Class<? extends Filter> filter)
     {
@@ -78,6 +76,7 @@
 
     /* ---------------------------------------------------------------- */
     /** Constructor for existing filter.
+     * @param filter the filter
      */
     public FilterHolder(Filter filter)
     {
@@ -110,32 +109,37 @@
     @Override
     public void initialize() throws Exception
     {
-        super.initialize();
-        
-        if (_filter==null)
+        if (!_initialized)
         {
-            try
-            {
-                ServletContext context=_servletHandler.getServletContext();
-                _filter=(context instanceof ServletContextHandler.Context)
-                    ?((ServletContextHandler.Context)context).createFilter(getHeldClass())
-                    :getHeldClass().newInstance();
-            }
-            catch (ServletException se)
-            {
-                Throwable cause = se.getRootCause();
-                if (cause instanceof InstantiationException)
-                    throw (InstantiationException)cause;
-                if (cause instanceof IllegalAccessException)
-                    throw (IllegalAccessException)cause;
-                throw se;
-            }
-        }
+            super.initialize();
 
-        _config=new Config();
-        if (LOG.isDebugEnabled())
-            LOG.debug("Filter.init {}",_filter);
-        _filter.init(_config);
+            if (_filter==null)
+            {
+                try
+                {
+                    ServletContext context=_servletHandler.getServletContext();
+                    _filter=(context instanceof ServletContextHandler.Context)
+                            ?((ServletContextHandler.Context)context).createFilter(getHeldClass())
+                            :getHeldClass().newInstance();
+                }
+                catch (ServletException se)
+                {
+                    Throwable cause = se.getRootCause();
+                    if (cause instanceof InstantiationException)
+                        throw (InstantiationException)cause;
+                    if (cause instanceof IllegalAccessException)
+                        throw (IllegalAccessException)cause;
+                    throw se;
+                }
+            }
+
+            _config=new Config();
+            if (LOG.isDebugEnabled())
+                LOG.debug("Filter.init {}",_filter);
+            _filter.init(_config);
+        }
+        
+        _initialized = true;
     }
 
 
@@ -159,6 +163,7 @@
             _filter=null;
 
         _config=null;
+        _initialized = false;
         super.doStop();
     }
 
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java
index ca74285..c91a55b 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java
@@ -45,6 +45,8 @@
 
     /* ------------------------------------------------------------ */
     /** Dispatch type from name
+     * @param type the type name
+     * @return the dispatcher type
      */
     public static DispatcherType dispatch(String type)
     {
@@ -63,6 +65,8 @@
 
     /* ------------------------------------------------------------ */
     /** Dispatch type from name
+     * @param type the dispatcher type
+     * @return the type constant ({@link #REQUEST}, {@link #ASYNC}, {@link #FORWARD}, {@link #INCLUDE}, or {@link #ERROR})
      */
     public static int dispatch(DispatcherType type)
     {
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java
index 0eeb1a1..6509125 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java
@@ -42,7 +42,7 @@
  * 
  * Specialization of AbstractHolder for servlet-related classes that 
  * have init-params etc
- * 
+ * @param <T> the type of holder
  */
 @ManagedObject("Holder - a container for servlets and the like")
 public class Holder<T> extends BaseHolder<T>
@@ -53,6 +53,7 @@
     protected String _displayName;
     protected boolean _asyncSupported;
     protected String _name;
+    protected boolean _initialized = false;
 
 
     /* ---------------------------------------------------------------- */
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Invoker.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Invoker.java
index 7f26b2b..9bf5281 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Invoker.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Invoker.java
@@ -34,7 +34,6 @@
 
 import org.eclipse.jetty.server.Dispatcher;
 import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.HttpChannel;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.HandlerWrapper;
@@ -227,7 +226,7 @@
 
         if (holder!=null)
         {
-            final Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
+            final Request baseRequest=Request.getBaseRequest(request);
             holder.handle(baseRequest,
                     new InvokedRequest(request,included,servlet,servlet_path,path_info),
                           response);
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
index 9997077..1f864cb 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
@@ -35,6 +35,7 @@
 import javax.servlet.FilterRegistration;
 import javax.servlet.RequestDispatcher;
 import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
 import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
 import javax.servlet.ServletException;
@@ -57,39 +58,49 @@
 import org.eclipse.jetty.server.handler.ErrorHandler;
 import org.eclipse.jetty.server.handler.HandlerCollection;
 import org.eclipse.jetty.server.handler.HandlerWrapper;
+import org.eclipse.jetty.server.handler.gzip.GzipHandler;
 import org.eclipse.jetty.server.session.SessionHandler;
 import org.eclipse.jetty.servlet.BaseHolder.Source;
+import org.eclipse.jetty.util.DecoratedObjectFactory;
+import org.eclipse.jetty.util.DeprecationWarning;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
 
-
-/* ------------------------------------------------------------ */
-/** Servlet Context.
+/** 
+ * Servlet Context.
+ * <p>
  * This extension to the ContextHandler allows for
  * simple construction of a context with ServletHandler and optionally
- * session and security handlers, et.<pre>
+ * session and security handlers, et.
+ * <pre>
  *   new ServletContext("/context",Context.SESSIONS|Context.NO_SECURITY);
  * </pre>
- * <p/>
+ * <p>
  * This class should have been called ServletContext, but this would have
  * cause confusion with {@link ServletContext}.
  */
 @ManagedObject("Servlet Context Handler")
 public class ServletContextHandler extends ContextHandler
 {
+    private static final Logger LOG = Log.getLogger(ServletContextHandler.class);
+    
     public final static int SESSIONS=1;
     public final static int SECURITY=2;
+    public final static int GZIP=4;
     public final static int NO_SESSIONS=0;
     public final static int NO_SECURITY=0;
     
     public interface ServletContainerInitializerCaller extends LifeCycle {};
 
-    protected final List<Decorator> _decorators= new ArrayList<>();
+    protected final DecoratedObjectFactory _objFactory;
     protected Class<? extends SecurityHandler> _defaultSecurityHandlerClass=org.eclipse.jetty.security.ConstraintSecurityHandler.class;
     protected SessionHandler _sessionHandler;
     protected SecurityHandler _securityHandler;
     protected ServletHandler _servletHandler;
+    protected GzipHandler _gzipHandler;
     protected int _options;
     protected JspConfigDescriptor _jspConfig;
 
@@ -135,6 +146,7 @@
         this(parent,contextPath,sessionHandler,securityHandler,servletHandler,errorHandler,0);
     }
     
+    /* ------------------------------------------------------------ */
     public ServletContextHandler(HandlerContainer parent, String contextPath, SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler,int options)
     {
         super((ContextHandler.Context)null);
@@ -143,6 +155,9 @@
         _sessionHandler = sessionHandler;
         _securityHandler = securityHandler;
         _servletHandler = servletHandler;
+        
+        _objFactory = new DecoratedObjectFactory();
+        _objFactory.addDecorator(new DeprecationWarning());
 
         if (contextPath!=null)
             setContextPath(contextPath);
@@ -158,7 +173,22 @@
         
         if (errorHandler!=null)
             setErrorHandler(errorHandler);
+    }
+    
+    @Override
+    public void setHandler(Handler handler)
+    {
+        if (handler!=null)
+            LOG.warn("ServletContextHandler.setHandler should not be called directly. Use insertHandler or setSessionHandler etc.");
+        super.setHandler(handler);
+    }
 
+    private void doSetHandler(HandlerWrapper wrapper, Handler handler)
+    {
+        if (wrapper==this)
+            super.setHandler(handler);
+        else
+            wrapper.setHandler(handler);
     }
     
     /* ------------------------------------------------------------ */
@@ -166,43 +196,70 @@
     {
         HandlerWrapper handler=this;
         
-        // Skip any injected handlers
-        while (handler.getHandler() instanceof HandlerWrapper)
-        {
-            HandlerWrapper wrapper = (HandlerWrapper)handler.getHandler();
-            if (wrapper instanceof SessionHandler ||
-                wrapper instanceof SecurityHandler ||
-                wrapper instanceof ServletHandler)
-                break;
-            handler=wrapper;
-        }
-        
+        // link session handler
         if (getSessionHandler()!=null)
         {
-            if (handler==this)
-                super.setHandler(_sessionHandler);
-            else
-                handler.setHandler(_sessionHandler);
+            
+            while (!(handler.getHandler() instanceof SessionHandler) && 
+                   !(handler.getHandler() instanceof SecurityHandler) && 
+                   !(handler.getHandler() instanceof GzipHandler) && 
+                   !(handler.getHandler() instanceof ServletHandler) && 
+                   handler.getHandler() instanceof HandlerWrapper)
+                handler=(HandlerWrapper)handler.getHandler();
+            
+            if (handler.getHandler()!=_sessionHandler)
+                doSetHandler(handler,_sessionHandler);
             handler=_sessionHandler;
         }
-
+        
+        // link security handler
         if (getSecurityHandler()!=null)
         {
-            if (handler==this)
-                super.setHandler(_securityHandler);
-            else
-                handler.setHandler(_securityHandler);
+            while (!(handler.getHandler() instanceof SecurityHandler) && 
+                   !(handler.getHandler() instanceof GzipHandler) && 
+                   !(handler.getHandler() instanceof ServletHandler) && 
+                   handler.getHandler() instanceof HandlerWrapper)
+                handler=(HandlerWrapper)handler.getHandler();
+
+            if (handler.getHandler()!=_securityHandler)
+                doSetHandler(handler,_securityHandler);
             handler=_securityHandler;
         }
 
+        // link gzip handler
+        if (getGzipHandler()!=null)
+        {
+            while (!(handler.getHandler() instanceof GzipHandler) && 
+                   !(handler.getHandler() instanceof ServletHandler) && 
+                   handler.getHandler() instanceof HandlerWrapper)
+                handler=(HandlerWrapper)handler.getHandler();
+
+            if (handler.getHandler()!=_gzipHandler)
+                doSetHandler(handler,_gzipHandler);
+            handler=_gzipHandler;
+        }
+
+        
+        // link servlet handler
         if (getServletHandler()!=null)
         {
-            if (handler==this)
-                super.setHandler(_servletHandler);
-            else
-                handler.setHandler(_servletHandler);
+            while (!(handler.getHandler() instanceof ServletHandler) && 
+                   handler.getHandler() instanceof HandlerWrapper)
+                handler=(HandlerWrapper)handler.getHandler();
+
+            if (handler.getHandler()!=_servletHandler)
+                doSetHandler(handler,_servletHandler);
             handler=_servletHandler;
-        } 
+        }
+        
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStart() throws Exception
+    {
+        getServletContext().setAttribute(DecoratedObjectFactory.ATTR, _objFactory);
+        super.doStart();
     }
     
     /* ------------------------------------------------------------ */
@@ -213,8 +270,7 @@
     protected void doStop() throws Exception
     {
         super.doStop();
-        if (_decorators != null)
-            _decorators.clear();
+        _objFactory.clear();
     }
 
     /* ------------------------------------------------------------ */
@@ -275,18 +331,13 @@
 
         if (_servletHandler != null)
         {
-            //Call decorators on all holders, and also on any EventListeners before
-            //decorators are called on any other classes (like servlets and filters)
-            for (int i=_decorators.size()-1;i>=0; i--)
+            // Call decorators on all holders, and also on any EventListeners before
+            // decorators are called on any other classes (like servlets and filters)
+            if(_servletHandler.getListeners() != null)
             {
-                Decorator decorator = _decorators.get(i);
-                //Do any decorations on the ListenerHolders AND the listener instances first up
-                if (_servletHandler.getListeners()!=null)
-                {
-                    for (ListenerHolder holder:_servletHandler.getListeners())
-                    {             
-                        decorator.decorate(holder.getListener());
-                    }
+                for (ListenerHolder holder:_servletHandler.getListeners())
+                {             
+                    _objFactory.decorate(holder.getListener());
                 }
             }
         }
@@ -297,6 +348,13 @@
         if (_servletHandler != null)
             _servletHandler.initialize();
     }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void stopContext() throws Exception
+    {
+        super.stopContext();
+    }
 
     /* ------------------------------------------------------------ */
     /**
@@ -336,7 +394,22 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** convenience method to add a servlet.
+    /**
+     * @return Returns the gzipHandler.
+     */
+    @ManagedAttribute(value="context gzip handler", readonly=true)
+    public GzipHandler getGzipHandler()
+    {
+        if (_gzipHandler==null && (_options&GZIP)!=0 && !isStarted())
+            _gzipHandler=new GzipHandler();
+        return _gzipHandler;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Convenience method to add a servlet.
+     * @param className the servlet class name
+     * @param pathSpec the path spec to map servlet to
+     * @return the ServletHolder for the added servlet
      */
     public ServletHolder addServlet(String className,String pathSpec)
     {
@@ -344,7 +417,10 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** convenience method to add a servlet.
+    /** Convenience method to add a servlet.
+     * @param servlet the servlet class
+     * @param pathSpec the path spec to map servlet to
+     * @return the ServletHolder for the added servlet
      */
     public ServletHolder addServlet(Class<? extends Servlet> servlet,String pathSpec)
     {
@@ -352,7 +428,9 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** convenience method to add a servlet.
+    /** Convenience method to add a servlet.
+     * @param servlet the servlet holder
+     * @param pathSpec the path spec
      */
     public void addServlet(ServletHolder servlet,String pathSpec)
     {
@@ -360,7 +438,10 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** convenience method to add a filter
+    /** Convenience method to add a filter
+     * @param holder the filter holder
+     * @param pathSpec the path spec
+     * @param dispatches the dispatcher types for this filter
      */
     public void addFilter(FilterHolder holder,String pathSpec,EnumSet<DispatcherType> dispatches)
     {
@@ -368,7 +449,11 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** convenience method to add a filter
+    /** Convenience method to add a filter
+     * @param filterClass the filter class
+     * @param pathSpec the path spec
+     * @param dispatches the dispatcher types for this filter
+     * @return the FilterHolder that was created
      */
     public FilterHolder addFilter(Class<? extends Filter> filterClass,String pathSpec,EnumSet<DispatcherType> dispatches)
     {
@@ -376,7 +461,11 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** convenience method to add a filter
+    /** Convenience method to add a filter
+     * @param filterClass the filter class name 
+     * @param pathSpec the path spec
+     * @param dispatches the dispatcher types for this filter
+     * @return the FilterHolder that was created
      */
     public FilterHolder addFilter(String filterClass,String pathSpec,EnumSet<DispatcherType> dispatches)
     {
@@ -457,6 +546,23 @@
         super.callContextDestroyed(l, e);
     }
 
+    private boolean replaceHandler(Handler handler,Handler replace)
+    {
+        HandlerWrapper wrapper=this;
+        while(true)
+        {
+            if (wrapper.getHandler()==handler)
+            {
+                doSetHandler(wrapper,replace);
+                return true;
+            }
+            
+            if (!(wrapper.getHandler() instanceof HandlerWrapper))
+                return false;
+            wrapper = (HandlerWrapper)wrapper.getHandler();
+        }
+    }
+    
     /* ------------------------------------------------------------ */
     /**
      * @param sessionHandler The sessionHandler to set.
@@ -466,10 +572,17 @@
         if (isStarted())
             throw new IllegalStateException("STARTED");
 
+        Handler next=null;
         if (_sessionHandler!=null)
+        {
+            next=_sessionHandler.getHandler();
             _sessionHandler.setHandler(null);
+            replaceHandler(_sessionHandler,sessionHandler);
+        }
 
         _sessionHandler = sessionHandler;
+        if (next!=null && _sessionHandler.getHandler()==null)
+            _sessionHandler.setHandler(next);
         relinkHandlers();
     }
 
@@ -482,12 +595,44 @@
         if (isStarted())
             throw new IllegalStateException("STARTED");
 
+        Handler next=null;
         if (_securityHandler!=null)
+        {
+            next=_securityHandler.getHandler();
             _securityHandler.setHandler(null);
+            replaceHandler(_securityHandler,securityHandler);
+        }
+        
         _securityHandler = securityHandler;
+        if (next!=null && _securityHandler.getHandler()==null)
+            _securityHandler.setHandler(next);
         relinkHandlers();
     }
 
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param gzipHandler The {@link GzipHandler} to set on this context.
+     */
+    public void setGzipHandler(GzipHandler gzipHandler)
+    {
+        if (isStarted())
+            throw new IllegalStateException("STARTED");
+
+        Handler next=null;
+        if (_gzipHandler!=null)
+        {
+            next=_gzipHandler.getHandler();
+            _gzipHandler.setHandler(null);
+            replaceHandler(_gzipHandler,gzipHandler);
+        }
+        
+        _gzipHandler = gzipHandler;
+        if (next!=null && _gzipHandler.getHandler()==null)
+            _gzipHandler.setHandler(next);
+        relinkHandlers();
+    }
+    
     /* ------------------------------------------------------------ */
     /**
      * @param servletHandler The servletHandler to set.
@@ -502,29 +647,12 @@
         {
             next=_servletHandler.getHandler();
             _servletHandler.setHandler(null);
+            replaceHandler(_servletHandler,servletHandler);
         }
         _servletHandler = servletHandler;
+        if (next!=null && _servletHandler.getHandler()==null)
+            _servletHandler.setHandler(next);
         relinkHandlers();
-        _servletHandler.setHandler(next);
-    }
-    
-    /* ------------------------------------------------------------ */
-    @Override
-    public void setHandler(Handler handler)
-    {
-        if (handler instanceof ServletHandler)
-            setServletHandler((ServletHandler) handler);
-        else if (handler instanceof SessionHandler)
-            setSessionHandler((SessionHandler) handler);
-        else if (handler instanceof SecurityHandler)
-            setSecurityHandler((SecurityHandler)handler);
-        else if (handler == null || handler instanceof HandlerWrapper)
-        {
-            super.setHandler(handler);
-            relinkHandlers();
-        }
-        else
-            throw new IllegalArgumentException();
     }
     
     
@@ -535,63 +663,100 @@
      */
     public void insertHandler(HandlerWrapper handler)
     {
-        HandlerWrapper h=this;
-        
-        // Skip any injected handlers
-        while (h.getHandler() instanceof HandlerWrapper)
+        if (handler instanceof SessionHandler)
+            setSessionHandler((SessionHandler)handler);
+        else if (handler instanceof SecurityHandler)
+            setSecurityHandler((SecurityHandler)handler);
+        else if (handler instanceof GzipHandler)
+            setGzipHandler((GzipHandler)handler);
+        else if (handler instanceof ServletHandler)
+            setServletHandler((ServletHandler)handler);
+        else
         {
-            HandlerWrapper wrapper = (HandlerWrapper)h.getHandler();
-            if (wrapper instanceof SessionHandler ||
-                wrapper instanceof SecurityHandler ||
-                wrapper instanceof ServletHandler)
-                break;
-            h=wrapper;
+            HandlerWrapper tail = handler;
+            while(tail.getHandler() instanceof HandlerWrapper)
+                tail=(HandlerWrapper)tail.getHandler();
+            if (tail.getHandler()!=null)
+                throw new IllegalArgumentException("bad tail of inserted wrapper chain");
+            
+            // Skip any injected handlers
+            HandlerWrapper h=this;
+            while (h.getHandler() instanceof HandlerWrapper)
+            {
+                HandlerWrapper wrapper = (HandlerWrapper)h.getHandler();
+                if (wrapper instanceof SessionHandler ||
+                        wrapper instanceof SecurityHandler ||
+                        wrapper instanceof ServletHandler)
+                    break;
+                h=wrapper;
+            }
+            
+            Handler next=h.getHandler();
+            doSetHandler(h,handler);
+            doSetHandler(tail,next);
         }
-        
-        h.setHandler(handler);
         relinkHandlers();
     }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * The DecoratedObjectFactory for use by IoC containers (weld / spring / etc)
+     * 
+     * @return The DecoratedObjectFactory
+     */
+    public DecoratedObjectFactory getObjectFactory()
+    {
+        return _objFactory;
+    }
 
     /* ------------------------------------------------------------ */
     /**
      * @return The decorator list used to resource inject new Filters, Servlets and EventListeners
+     * @deprecated use the {@link DecoratedObjectFactory} from getAttribute("org.eclipse.jetty.util.DecoratedObjectFactory") or {@link #getObjectFactory()} instead
      */
+    @Deprecated
     public List<Decorator> getDecorators()
     {
-        return Collections.unmodifiableList(_decorators);
+        List<Decorator> ret = new ArrayList<ServletContextHandler.Decorator>();
+        for (org.eclipse.jetty.util.Decorator decorator : _objFactory)
+        {
+            ret.add(new LegacyDecorator(decorator));
+        }
+        return Collections.unmodifiableList(ret);
     }
 
     /* ------------------------------------------------------------ */
     /**
-     * @param decorators The lis of {@link Decorator}s
+     * @param decorators The list of {@link Decorator}s
+     * @deprecated use the {@link DecoratedObjectFactory} from getAttribute("org.eclipse.jetty.util.DecoratedObjectFactory") or {@link #getObjectFactory()} instead
      */
+    @Deprecated
     public void setDecorators(List<Decorator> decorators)
     {
-        _decorators.clear();
-        _decorators.addAll(decorators);
+        _objFactory.setDecorators(decorators);
     }
 
     /* ------------------------------------------------------------ */
     /**
      * @param decorator The decorator to add
+     * @deprecated use the {@link DecoratedObjectFactory} from getAttribute("org.eclipse.jetty.util.DecoratedObjectFactory") or {@link #getObjectFactory()} instead
      */
+    @Deprecated
     public void addDecorator(Decorator decorator)
     {
-        _decorators.add(decorator);
+        _objFactory.addDecorator(decorator);
     }
-
+    
     /* ------------------------------------------------------------ */
     void destroyServlet(Servlet servlet)
     {
-        for (Decorator decorator : _decorators)
-            decorator.destroy(servlet);
+        _objFactory.destroy(servlet);
     }
 
     /* ------------------------------------------------------------ */
     void destroyFilter(Filter filter)
     {
-        for (Decorator decorator : _decorators)
-            decorator.destroy(filter);
+        _objFactory.destroy(filter);
     }
 
     /* ------------------------------------------------------------ */
@@ -1144,11 +1309,7 @@
             try
             {
                 T f = createInstance(c);
-                for (int i=_decorators.size()-1; i>=0; i--)
-                {
-                    Decorator decorator = _decorators.get(i);
-                    f=decorator.decorate(f);
-                }
+                f = _objFactory.decorate(f);
                 return f;
             }
             catch (Exception e)
@@ -1164,11 +1325,7 @@
             try
             {
                 T s = createInstance(c);
-                for (int i=_decorators.size()-1; i>=0; i--)
-                {
-                    Decorator decorator = _decorators.get(i);
-                    s=decorator.decorate(s);
-                }
+                s = _objFactory.decorate(s);
                 return s;
             }
             catch (Exception e)
@@ -1311,11 +1468,7 @@
             try
             {
                 T l = createInstance(clazz);
-                for (int i=_decorators.size()-1; i>=0; i--)
-                {
-                    Decorator decorator = _decorators.get(i);
-                    l=decorator.decorate(l);
-                }
+                l = _objFactory.decorate(l);
                 return l;
             }            
             catch (Exception e)
@@ -1355,11 +1508,39 @@
 
 
     /* ------------------------------------------------------------ */
-    /** Interface to decorate loaded classes.
+    /** 
+     * Legacy Interface to decorate loaded classes.
+     * <p>
+     * Left for backwards compatibility with Weld / CDI
+     * @deprecated use new {@link org.eclipse.jetty.util.Decorator} 
      */
-    public interface Decorator
+    @Deprecated
+    public interface Decorator extends org.eclipse.jetty.util.Decorator
     {
-        <T> T decorate (T o);
-        void destroy (Object o);
+    }
+    
+    /**
+     * Implementation of the legacy interface to decorate loaded classes.
+     */
+    private static class LegacyDecorator implements Decorator
+    {
+        private org.eclipse.jetty.util.Decorator decorator;
+        
+        public LegacyDecorator(org.eclipse.jetty.util.Decorator decorator)
+        {
+            this.decorator = decorator;
+        }
+
+        @Override
+        public <T> T decorate(T o)
+        {
+            return decorator.decorate(o);
+        }
+
+        @Override
+        public void destroy(Object o)
+        {
+            decorator.destroy(o);
+        }
     }
 }
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
index b659d8b..db4b9e4 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
@@ -57,7 +57,6 @@
 import org.eclipse.jetty.io.RuntimeIOException;
 import org.eclipse.jetty.security.IdentityService;
 import org.eclipse.jetty.security.SecurityHandler;
-import org.eclipse.jetty.server.HttpChannel;
 import org.eclipse.jetty.server.QuietServletException;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.ServletRequestHttpWrapper;
@@ -77,8 +76,9 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-/* --------------------------------------------------------------------- */
-/** Servlet HttpHandler.
+/** 
+ * Servlet HttpHandler.
+ * <p>
  * This handler maps requests to servlets that implement the
  * javax.servlet.http.HttpServlet API.
  * <P>
@@ -89,10 +89,6 @@
  * Unless run as part of a {@link ServletContextHandler} or derivative, the {@link #initialize()}
  * method must be called manually after start().
  */
-
-/* ------------------------------------------------------------ */
-/**
- */
 @ManagedObject("Servlet Handler")
 public class ServletHandler extends ScopedHandler
 {
@@ -113,6 +109,7 @@
     private boolean _startWithUnavailable=false;
     private boolean _ensureDefaultServlet=true;
     private IdentityService _identityService;
+    private boolean _allowDuplicateMappings=false;
 
     private ServletHolder[] _servlets=new ServletHolder[0];
     private ServletMapping[] _servletMappings;
@@ -146,7 +143,7 @@
         throws Exception
     {
         ContextHandler.Context context=ContextHandler.getCurrentContext();
-        _servletContext=context==null?new ContextHandler.NoContext():context;
+        _servletContext=context==null?new ContextHandler.StaticContext():context;
         _contextHandler=(ServletContextHandler)(context==null?null:context.getContextHandler());
 
         if (_contextHandler!=null)
@@ -410,8 +407,8 @@
     /**
      * Get the ServletMapping matching the path
      * 
-     * @param pathSpec
-     * @return
+     * @param pathSpec the path spec
+     * @return the servlet mapping for the path spec (or null if not found)
      */
     public ServletMapping getServletMapping(String pathSpec)
     {
@@ -593,11 +590,18 @@
         }
         catch(RuntimeIOException e)
         {
+            if (e.getCause() instanceof IOException)
+            {
+                LOG.debug(e);
+                throw (IOException)e.getCause();
+            }
             throw e;
         }
         catch(Exception e)
         {
-            if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type)))
+            //TODO, can we let all error handling fall through to HttpChannel?
+            
+            if (baseRequest.isAsyncStarted() || !(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type)))
             {
                 if (e instanceof IOException)
                     throw (IOException)e;
@@ -822,6 +826,22 @@
         _startWithUnavailable=start;
     }
 
+    /**
+     * @return the allowDuplicateMappings
+     */
+    public boolean isAllowDuplicateMappings()
+    {
+        return _allowDuplicateMappings;
+    }
+
+    /**
+     * @param allowDuplicateMappings the allowDuplicateMappings to set
+     */
+    public void setAllowDuplicateMappings(boolean allowDuplicateMappings)
+    {
+        _allowDuplicateMappings = allowDuplicateMappings;
+    }
+
     /* ------------------------------------------------------------ */
     /**
      * @return True if this handler will start with unavailable servlets
@@ -835,6 +855,7 @@
 
     /* ------------------------------------------------------------ */
     /** Initialize filters and load-on-startup servlets.
+     * @throws Exception if unable to initialize
      */
     public void initialize()
         throws Exception
@@ -909,7 +930,7 @@
     
     /* ------------------------------------------------------------ */
     /** Add a holder for a listener
-     * @param filter
+     * @param listener the listener for the holder
      */
     public void addListener (ListenerHolder listener)
     {
@@ -943,7 +964,9 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * see also newServletHolder(Class)
+     * Add a new servlet holder
+     * @param source the holder source
+     * @return the servlet holder
      */
     public ServletHolder newServletHolder(Holder.Source source)
     {
@@ -952,6 +975,8 @@
 
     /* ------------------------------------------------------------ */
     /** Convenience method to add a servlet.
+     * @param className the class name
+     * @param pathSpec the path spec
      * @return The servlet holder.
      */
     public ServletHolder addServletWithMapping (String className,String pathSpec)
@@ -963,7 +988,9 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** convenience method to add a servlet.
+    /** Convenience method to add a servlet.
+     * @param servlet the servlet class
+     * @param pathSpec the path spec
      * @return The servlet holder.
      */
     public ServletHolder addServletWithMapping (Class<? extends Servlet> servlet,String pathSpec)
@@ -976,7 +1003,7 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** convenience method to add a servlet.
+    /** Convenience method to add a servlet.
      * @param servlet servlet holder to add
      * @param pathSpec servlet mappings for the servletHolder
      */
@@ -988,7 +1015,11 @@
 
         try
         {
-            setServlets(ArrayUtil.addToArray(holders, servlet, ServletHolder.class));
+            synchronized (this)
+            {
+                if (servlet != null && !containsServletHolder(servlet))
+                    setServlets(ArrayUtil.addToArray(holders, servlet, ServletHolder.class));
+            }
 
             ServletMapping mapping = new ServletMapping();
             mapping.setServletName(servlet.getName());
@@ -1006,17 +1037,26 @@
 
 
     /* ------------------------------------------------------------ */
-    /**Convenience method to add a pre-constructed ServletHolder.
-     * @param holder
+    /**
+     * Convenience method to add a pre-constructed ServletHolder.
+     * @param holder the servlet holder
      */
     public void addServlet(ServletHolder holder)
     {
-        setServlets(ArrayUtil.addToArray(getServlets(), holder, ServletHolder.class));
+        if (holder == null)
+            return;
+        
+        synchronized (this)
+        {
+            if (!containsServletHolder(holder))
+                setServlets(ArrayUtil.addToArray(getServlets(), holder, ServletHolder.class));
+        }
     }
 
     /* ------------------------------------------------------------ */
-    /** Convenience method to add a pre-constructed ServletMapping.
-     * @param mapping
+    /** 
+     * Convenience method to add a pre-constructed ServletMapping.
+     * @param mapping the servlet mapping
      */
     public void addServletMapping (ServletMapping mapping)
     {
@@ -1092,7 +1132,11 @@
 
         try
         {
-            setFilters(ArrayUtil.addToArray(holders, holder, FilterHolder.class));
+            synchronized (this)
+            {
+                if (holder != null && !containsFilterHolder(holder))
+                    setFilters(ArrayUtil.addToArray(holders, holder, FilterHolder.class));
+            }
 
             FilterMapping mapping = new FilterMapping();
             mapping.setFilterName(holder.getName());
@@ -1160,7 +1204,11 @@
 
         try
         {
-            setFilters(ArrayUtil.addToArray(holders, holder, FilterHolder.class));
+            synchronized (this)
+            {
+                if (holder != null && !containsFilterHolder(holder))
+                    setFilters(ArrayUtil.addToArray(holders, holder, FilterHolder.class));
+            }
 
             FilterMapping mapping = new FilterMapping();
             mapping.setFilterName(holder.getName());
@@ -1182,13 +1230,15 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** Convenience method to add a filter with a mapping
-     * @param className
-     * @param pathSpec
-     * @param dispatches
+    /** 
+     * Convenience method to add a filter with a mapping
+     * @param className the filter class name
+     * @param pathSpec the path spec
+     * @param dispatches the dispatcher types for this filter
      * @return the filter holder created
      * @deprecated use {@link #addFilterWithMapping(Class, String, EnumSet)} instead
      */
+    @Deprecated
     public FilterHolder addFilter (String className,String pathSpec,EnumSet<DispatcherType> dispatches)
     {
         return addFilterWithMapping(className, pathSpec, dispatches);
@@ -1196,31 +1246,45 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * convenience method to add a filter and mapping
-     * @param filter
-     * @param filterMapping
+     * Convenience method to add a filter and mapping
+     * @param filter the filter holder
+     * @param filterMapping the filter mapping
      */
     public void addFilter (FilterHolder filter, FilterMapping filterMapping)
     {
         if (filter != null)
-            setFilters(ArrayUtil.addToArray(getFilters(), filter, FilterHolder.class));
+        {
+            synchronized (this)
+            {
+                if (!containsFilterHolder(filter))
+                    setFilters(ArrayUtil.addToArray(getFilters(), filter, FilterHolder.class));
+            }
+        }
         if (filterMapping != null)
             addFilterMapping(filterMapping);
     }
 
     /* ------------------------------------------------------------ */
-    /** Convenience method to add a preconstructed FilterHolder
-     * @param filter
+    /** 
+     * Convenience method to add a preconstructed FilterHolder
+     * @param filter the filter holder
      */
     public void addFilter (FilterHolder filter)
     {
-        if (filter != null)
-            setFilters(ArrayUtil.addToArray(getFilters(), filter, FilterHolder.class));
+        if (filter == null)
+            return;
+
+        synchronized (this)
+        {
+            if (!containsFilterHolder(filter))
+                setFilters(ArrayUtil.addToArray(getFilters(), filter, FilterHolder.class));
+        }
     }
 
     /* ------------------------------------------------------------ */
-    /** Convenience method to add a preconstructed FilterMapping
-     * @param mapping
+    /** 
+     * Convenience method to add a preconstructed FilterMapping
+     * @param mapping the filter mapping
      */
     public void addFilterMapping (FilterMapping mapping)
     {
@@ -1263,15 +1327,15 @@
     
 
     /* ------------------------------------------------------------ */
-    /** Convenience method to add a preconstructed FilterMapping
-     * @param mapping
+    /** 
+     * Convenience method to add a preconstructed FilterMapping
+     * @param mapping the filter mapping
      */
     public void prependFilterMapping (FilterMapping mapping)
     {
         if (mapping != null)
         {
-            Source source = mapping.getFilterHolder().getSource();
-            
+            Source source = (mapping.getFilterHolder()==null?null:mapping.getFilterHolder().getSource());
             FilterMapping[] mappings = getFilterMappings();
             if (mappings==null || mappings.length==0)
             {
@@ -1322,7 +1386,7 @@
      * @param mapping the FilterMapping to add
      * @param pos the position in the existing arry at which to add it
      * @param before if true, insert before  pos, if false insert after it
-     * @return
+     * @return the new FilterMappings post-insert
      */
     protected FilterMapping[] insertFilterMapping (FilterMapping mapping, int pos, boolean before)
     {
@@ -1436,7 +1500,7 @@
             Map<String,ServletMapping> servletPathMappings = new HashMap<String,ServletMapping>();
             
             //create a map of paths to set of ServletMappings that define that mapping
-            HashMap<String, Set<ServletMapping>> sms = new HashMap<String, Set<ServletMapping>>();
+            HashMap<String, List<ServletMapping>> sms = new HashMap<String, List<ServletMapping>>();
             for (ServletMapping servletMapping : _servletMappings)
             {
                 String[] pathSpecs = servletMapping.getPathSpecs();
@@ -1444,10 +1508,10 @@
                 {
                     for (String pathSpec : pathSpecs)
                     {
-                        Set<ServletMapping> mappings = sms.get(pathSpec);
+                        List<ServletMapping> mappings = sms.get(pathSpec);
                         if (mappings == null)
                         {
-                            mappings = new HashSet<ServletMapping>();
+                            mappings = new ArrayList<ServletMapping>();
                             sms.put(pathSpec, mappings);
                         }
                         mappings.add(servletMapping);
@@ -1460,7 +1524,7 @@
             {
                 //for each path, look at the mappings where it is referenced
                 //if a mapping is for a servlet that is not enabled, skip it
-                Set<ServletMapping> mappings = sms.get(pathSpec);
+                List<ServletMapping> mappings = sms.get(pathSpec);
 
                 ServletMapping finalMapping = null;
                 for (ServletMapping mapping : mappings)
@@ -1478,9 +1542,15 @@
                         finalMapping = mapping;
                     else
                     {
-                        //already have a candidate - only accept another one if the candidate is a default
+                        //already have a candidate - only accept another one 
+                        //if the candidate is a default, or we're allowing duplicate mappings
                         if (finalMapping.isDefault())
                             finalMapping = mapping;
+                        else if (isAllowDuplicateMappings())
+                        {
+                            LOG.warn("Multiple servlets map to path {}: {} and {}, choosing {}", pathSpec, finalMapping.getServletName(), mapping.getServletName(), mapping);
+                            finalMapping = mapping;
+                        }
                         else
                         {
                             //existing candidate isn't a default, if the one we're looking at isn't a default either, then its an error
@@ -1539,7 +1609,36 @@
         if (getHandler()!=null)
             nextHandle(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()),baseRequest,request,response);
     }
+    
+    
+    protected synchronized boolean containsFilterHolder (FilterHolder holder)
+    {
+        if (_filters == null)
+            return false;
+        boolean found = false;
+        for (FilterHolder f:_filters)
+        {
+            if (f == holder)
+                found = true;
+        }
+        return found;
+    }
 
+    
+    protected synchronized boolean containsServletHolder (ServletHolder holder)
+    {
+        if (_servlets == null)
+            return false;
+        boolean found = false;
+        for (ServletHolder s:_servlets)
+        {
+            if (s == holder)
+                found = true;
+        }
+        return found;
+    }
+    
+    
     /* ------------------------------------------------------------ */
     /**
      * @param filterChainsCached The filterChainsCached to set.
@@ -1632,7 +1731,7 @@
         public void doFilter(ServletRequest request, ServletResponse response)
             throws IOException, ServletException
         {
-            final Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
+            final Request baseRequest=Request.getBaseRequest(request);
 
             // pass to next filter
             if (_filterHolder!=null)
@@ -1644,17 +1743,21 @@
                 //if the request already does not support async, then the setting for the filter
                 //is irrelevant. However if the request supports async but this filter does not
                 //temporarily turn it off for the execution of the filter
-                boolean requestAsyncSupported = baseRequest.isAsyncSupported();
-                try
-                {
-                    if (!_filterHolder.isAsyncSupported() && requestAsyncSupported)
-                        baseRequest.setAsyncSupported(false);
+                if (baseRequest.isAsyncSupported() && !_filterHolder.isAsyncSupported())
+                { 
+                    try
+                    {
+                        baseRequest.setAsyncSupported(false,_filterHolder.toString());
+                        filter.doFilter(request, response, _next);
+                    }
+                    finally
+                    {
+                        baseRequest.setAsyncSupported(true,null);
+                    }
+                }
+                else
                     filter.doFilter(request, response, _next);
-                }
-                finally
-                {
-                    baseRequest.setAsyncSupported(requestAsyncSupported);
-                }
+
                 return;
             }
 
@@ -1717,24 +1820,28 @@
                 //if the request already does not support async, then the setting for the filter
                 //is irrelevant. However if the request supports async but this filter does not
                 //temporarily turn it off for the execution of the filter
-                boolean requestAsyncSupported = _baseRequest.isAsyncSupported();
-                try
+                if (!holder.isAsyncSupported() && _baseRequest.isAsyncSupported())
                 {
-                    if (!holder.isAsyncSupported() && requestAsyncSupported)
-                        _baseRequest.setAsyncSupported(false);
+                    try
+                    {
+                        _baseRequest.setAsyncSupported(false,holder.toString());
+                        filter.doFilter(request, response, this);
+                    }
+                    finally
+                    {
+                        _baseRequest.setAsyncSupported(true,null);
+                    }
+                }
+                else
                     filter.doFilter(request, response, this);
-                }
-                finally
-                {
-                    _baseRequest.setAsyncSupported(requestAsyncSupported);
-                }
+
                 return;
             }
 
             // Call servlet
             HttpServletRequest srequest = (HttpServletRequest)request;
             if (_servletHolder == null)
-                notFound((request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest(), srequest, (HttpServletResponse)response);
+                notFound(Request.getBaseRequest(request), srequest, (HttpServletResponse)response);
             else
             {
                 if (LOG.isDebugEnabled())
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
index ab58c73..c0e7b26 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
@@ -33,6 +33,7 @@
 import java.util.Stack;
 
 import javax.servlet.MultipartConfigElement;
+import javax.servlet.RequestDispatcher;
 import javax.servlet.Servlet;
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletContext;
@@ -46,26 +47,24 @@
 
 import org.eclipse.jetty.security.IdentityService;
 import org.eclipse.jetty.security.RunAsToken;
+import org.eclipse.jetty.server.MultiPartCleanerListener;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.UserIdentity;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-
-
-
-/* --------------------------------------------------------------------- */
-/** Servlet Instance and Context Holder.
+/**
+ * Servlet Instance and Context Holder.
+ * <p>
  * Holds the name, params and some state of a javax.servlet.Servlet
  * instance. It implements the ServletConfig interface.
  * This class will organise the loading of the servlet when needed or
  * requested.
- *
- *
  */
 @ManagedObject("Servlet Holder")
 public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope, Comparable<ServletHolder>
@@ -75,7 +74,6 @@
     private static final Logger LOG = Log.getLogger(ServletHolder.class);
     private int _initOrder = -1;
     private boolean _initOnStartup=false;
-    private boolean _initialized = false;
     private Map<String, String> _roleMap;
     private String _forcedPath;
     private String _runAsRole;
@@ -90,11 +88,11 @@
     private transient boolean _enabled = true;
     private transient UnavailableException _unavailableEx;
 
-    public static final String GLASSFISH_SENTINEL_CLASS = "org.glassfish.jsp.api.ResourceInjector";
+
     public static final String APACHE_SENTINEL_CLASS = "org.apache.tomcat.InstanceManager";
     public static final  String JSP_GENERATED_PACKAGE_NAME = "org.eclipse.jetty.servlet.jspPackagePrefix";
     public static final Map<String,String> NO_MAPPED_ROLES = Collections.emptyMap();
-    public static enum JspContainer {GLASSFISH, APACHE, OTHER}; 
+    public static enum JspContainer {APACHE, OTHER};
 
     /* ---------------------------------------------------------------- */
     /** Constructor .
@@ -106,6 +104,7 @@
 
     /* ---------------------------------------------------------------- */
     /** Constructor .
+     * @param creator the holder source
      */
     public ServletHolder(Holder.Source creator)
     {
@@ -114,6 +113,7 @@
 
     /* ---------------------------------------------------------------- */
     /** Constructor for existing servlet.
+     * @param servlet the servlet
      */
     public ServletHolder(Servlet servlet)
     {
@@ -123,6 +123,8 @@
 
     /* ---------------------------------------------------------------- */
     /** Constructor for servlet class.
+     * @param name the name of the servlet
+     * @param servlet the servlet class
      */
     public ServletHolder(String name, Class<? extends Servlet> servlet)
     {
@@ -133,6 +135,8 @@
 
     /* ---------------------------------------------------------------- */
     /** Constructor for servlet class.
+     * @param name the servlet name
+     * @param servlet the servlet
      */
     public ServletHolder(String name, Servlet servlet)
     {
@@ -143,6 +147,7 @@
 
     /* ---------------------------------------------------------------- */
     /** Constructor for servlet class.
+     * @param servlet the servlet class
      */
     public ServletHolder(Class<? extends Servlet> servlet)
     {
@@ -180,10 +185,13 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** Set the initialize order.
-     * Holders with order<0, are initialized on use. Those with
-     * order>=0 are initialized in increasing order when the handler
+    /**
+     * Set the initialize order.
+     * <p>
+     * Holders with order&lt;0, are initialized on use. Those with
+     * order&gt;=0 are initialized in increasing order when the handler
      * is started.
+     * @param order the servlet init order
      */
     public void setInitOrder(int order)
     {
@@ -192,22 +200,37 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** Comparitor by init order.
+    /**
+     * Comparator by init order.
      */
     @Override
     public int compareTo(ServletHolder sh)
     {
         if (sh==this)
             return 0;
+
         if (sh._initOrder<_initOrder)
             return 1;
+
         if (sh._initOrder>_initOrder)
             return -1;
 
-        int c=(_className!=null && sh._className!=null)?_className.compareTo(sh._className):0;
+        // consider _className, need to position properly when one is configured but not the other
+        int c;
+        if (_className==null && sh._className==null)
+            c=0;
+        else if (_className==null)
+            c=-1;
+        else if (sh._className==null)
+            c=1;
+        else
+            c=_className.compareTo(sh._className);
+
+        // if _initOrder and _className are the same, consider the _name
         if (c==0)
             c=_name.compareTo(sh._name);
-            return c;
+
+        return c;
     }
 
     /* ------------------------------------------------------------ */
@@ -280,7 +303,7 @@
         _enabled = enabled;
     }
 
-    
+
     /* ------------------------------------------------------------ */
     public void doStart()
         throws Exception
@@ -288,32 +311,32 @@
         _unavailable=0;
         if (!_enabled)
             return;
-        
+
         // Handle JSP file forced paths
         if (_forcedPath != null)
         {
             // Look for a precompiled JSP Servlet
             String precompiled=getClassNameForJsp(_forcedPath);
-            if (LOG.isDebugEnabled())
-                LOG.debug("Checking for precompiled servlet {} for jsp {}", precompiled, _forcedPath);
-            ServletHolder jsp=getServletHandler().getServlet(precompiled);
-            if (jsp!=null && jsp.getClassName() !=  null)
+            if (!StringUtil.isBlank(precompiled))
             {
                 if (LOG.isDebugEnabled())
-                    LOG.debug("JSP file {} for {} mapped to Servlet {}",_forcedPath, getName(),jsp.getClassName());
-                // set the className for this servlet to the precompiled one
-                setClassName(jsp.getClassName());
-            }
-            else
-            { 
-                if (getClassName() == null)
+                    LOG.debug("Checking for precompiled servlet {} for jsp {}", precompiled, _forcedPath);
+                ServletHolder jsp = getServletHandler().getServlet(precompiled);
+                if (jsp!=null && jsp.getClassName() !=  null)
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("JSP file {} for {} mapped to Servlet {}",_forcedPath, getName(),jsp.getClassName());
+                    // set the className for this servlet to the precompiled one
+                    setClassName(jsp.getClassName());
+                } 
+                else
                 {
                     // Look for normal JSP servlet
                     jsp=getServletHandler().getServlet("jsp");
                     if (jsp!=null)
                     {
                         if (LOG.isDebugEnabled())
-                            LOG.debug("JSP file {} for {} mapped to Servlet class {}",_forcedPath, getName(),jsp.getClassName());
+                            LOG.debug("JSP file {} for {} mapped to JspServlet class {}",_forcedPath, getName(),jsp.getClassName());
                         setClassName(jsp.getClassName());
                         //copy jsp init params that don't exist for this servlet
                         for (Map.Entry<String, String> entry:jsp.getInitParameters().entrySet())
@@ -326,12 +349,14 @@
                         //container does not support startup precompilation, it will be compiled at runtime when handling a request for this jsp.
                         //See also adaptForcedPathToJspContainer
                         setInitParameter("jspFile", _forcedPath);
-                    }                       
+                    }
                 }
             }
+            else
+                LOG.warn("Bad jsp-file {} conversion to classname in holder {}", _forcedPath, getName());
         }
-        
-        
+
+
         //check servlet has a class (ie is not a preliminary registration). If preliminary, fail startup.
         try
         {
@@ -380,14 +405,15 @@
             _servlet = new SingleThreadedWrapper();
 
     }
-    
-    
+
+
     /* ------------------------------------------------------------ */
     @Override
     public void initialize ()
     throws Exception
     {
-        if(!_initialized){
+        if(!_initialized)
+        {
             super.initialize();
             if (_extInstance || _initOnStartup)
             {
@@ -406,8 +432,8 @@
         }
         _initialized = true;
     }
-    
-    
+
+
     /* ------------------------------------------------------------ */
     public void doStop()
         throws Exception
@@ -455,6 +481,7 @@
     /* ------------------------------------------------------------ */
     /** Get the servlet.
      * @return The servlet
+     * @throws ServletException if unable to init the servlet on first use
      */
     public synchronized Servlet getServlet()
         throws ServletException
@@ -485,7 +512,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Check to ensure class of servlet is acceptable.
-     * @throws UnavailableException
+     * @throws UnavailableException if Servlet class is not of type {@link javax.servlet.Servlet}
      */
     public void checkServletType ()
         throws UnavailableException
@@ -515,22 +542,22 @@
 
         return isStarted()&& _unavailable==0;
     }
-    
+
     /* ------------------------------------------------------------ */
     /**
      * Check if there is a javax.servlet.annotation.ServletSecurity
      * annotation on the servlet class. If there is, then we force
-     * it to be loaded on startup, because all of the security 
+     * it to be loaded on startup, because all of the security
      * constraints must be calculated as the container starts.
-     * 
+     *
      */
     private void checkInitOnStartup()
     {
         if (_class==null)
             return;
-        
+
         if ((_class.getAnnotation(javax.servlet.annotation.ServletSecurity.class) != null) && !_initOnStartup)
-            setInitOrder(Integer.MAX_VALUE);    
+            setInitOrder(Integer.MAX_VALUE);
     }
 
     /* ------------------------------------------------------------ */
@@ -588,8 +615,8 @@
                 _servlet=newInstance();
             if (_config==null)
                 _config=new Config();
-            
-          
+
+
 
             // Handle run as
             if (_identityService!=null)
@@ -647,18 +674,15 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @throws Exception
+     * @throws Exception if unable to init the JSP Servlet
      */
     protected void initJspServlet () throws Exception
     {
         ContextHandler ch = ContextHandler.getContextHandler(getServletHandler().getServletContext());
-            
+
         /* Set the webapp's classpath for Jasper */
         ch.setAttribute("org.apache.catalina.jsp_classpath", ch.getClassPath());
 
-        /* Set the system classpath for Jasper */
-        setInitParameter("com.sun.appserv.jsp.classpath", Loader.getClassPath(ch.getClassLoader().getParent()));
-
         /* Set up other classpath attribute */
         if ("?".equals(getInitParameter("classpath")))
         {
@@ -668,7 +692,7 @@
             if (classpath != null)
                 setInitParameter("classpath", classpath);
         }
-        
+
         /* ensure scratch dir */
         File scratch = null;
         if (getInitParameter("scratchdir") == null)
@@ -677,7 +701,7 @@
             scratch = new File(tmp, "jsp");
             setInitParameter("scratchdir", scratch.getAbsolutePath());
         }
-     
+
         scratch = new File (getInitParameter("scratchdir"));
         if (!scratch.exists()) scratch.mkdir();
     }
@@ -686,8 +710,8 @@
     /**
      * Register a ServletRequestListener that will ensure tmp multipart
      * files are deleted when the request goes out of scope.
-     * 
-     * @throws Exception
+     *
+     * @throws Exception if unable to init the multipart
      */
     protected void initMultiPart () throws Exception
     {
@@ -699,7 +723,7 @@
             //servlet calling Request.getPart() or Request.getParts()
 
             ContextHandler ch = ContextHandler.getContextHandler(getServletHandler().getServletContext());
-            ch.addEventListener(new Request.MultiPartCleanerListener());
+            ch.addEventListener(MultiPartCleanerListener.INSTANCE);
         }
     }
 
@@ -735,16 +759,16 @@
     {
         _runAsRole = role;
     }
-    
+
     /* ------------------------------------------------------------ */
     /**
      * Prepare to service a request.
-     * 
-     * @param baseRequest
-     * @param request
-     * @param response
-     * @throws ServletException
-     * @throws UnavailableException
+     *
+     * @param baseRequest the base request
+     * @param request the request
+     * @param response the response
+     * @throws ServletException if unable to prepare the servlet
+     * @throws UnavailableException if not available
      */
     protected void prepare (Request baseRequest, ServletRequest request, ServletResponse response)
     throws ServletException, UnavailableException
@@ -754,7 +778,7 @@
         if (mpce != null)
             baseRequest.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
     }
-    
+
     public synchronized Servlet ensureInstance()
     throws ServletException, UnavailableException
     {
@@ -766,19 +790,21 @@
         if (_unavailable!=0 || (!_initOnStartup && servlet==null))
             servlet=getServlet();
         if (servlet==null)
-            throw new UnavailableException("Could not instantiate "+_class);    
-        
+            throw new UnavailableException("Could not instantiate "+_class);
+
         return servlet;
     }
 
     /* ------------------------------------------------------------ */
-    /** Service a request with this servlet.
-     * @param baseRequest
-     * @param request
-     * @param response
-     * @throws ServletException
-     * @throws UnavailableException
-     * @throws IOException
+    /**
+     * Service a request with this servlet.
+     *
+     * @param baseRequest the base request
+     * @param request the request
+     * @param response the response
+     * @throws ServletException if unable to process the servlet
+     * @throws UnavailableException if servlet is unavailable
+     * @throws IOException if unable to process the request or response
      */
     public void handle(Request baseRequest,
                        ServletRequest request,
@@ -806,10 +832,20 @@
             if (_identityService!=null)
                 old_run_as=_identityService.setRunAs(baseRequest.getResolvedUserIdentity(),_runAsToken);
 
-            if (!isAsyncSupported())
-                baseRequest.setAsyncSupported(false);
-
-            servlet.service(request,response);
+            if (baseRequest.isAsyncSupported() && !isAsyncSupported())
+            {
+                try
+                {
+                    baseRequest.setAsyncSupported(false,this.toString());
+                    servlet.service(request,response);
+                }
+                finally
+                {
+                    baseRequest.setAsyncSupported(true,null);
+                }
+            }
+            else
+                servlet.service(request,response);
             servlet_error=false;
         }
         catch(UnavailableException e)
@@ -819,15 +855,13 @@
         }
         finally
         {
-            baseRequest.setAsyncSupported(suspendable);
-
             // pop run-as role
             if (_identityService!=null)
                 _identityService.unsetRunAs(old_run_as);
 
             // Handle error params.
             if (servlet_error)
-                request.setAttribute("javax.servlet.error.servlet_name",getName());
+                request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,getName());
         }
     }
 
@@ -862,13 +896,7 @@
     /* ------------------------------------------------------------ */
     private void adaptForcedPathToJspContainer (ServletRequest request)
     {
-        if (_forcedPath != null && _jspContainer != null && JspContainer.GLASSFISH.equals(_jspContainer))
-        {
-            //if container is glassfish, set the request attribute org.apache.catalina.jsp_file to
-            //ensure the delegate jsp will be compiled
-
-            request.setAttribute("org.apache.catalina.jsp_file",_forcedPath);
-        }
+        //no-op for apache jsp
     }
 
     /* ------------------------------------------------------------ */
@@ -876,41 +904,40 @@
     {
         if (_jspContainer == null)
         {
-            //check for glassfish
             try
             {
-                //if container is glassfish, set the request attribute org.apache.catalina.jsp_file to
-                //ensure the delegate jsp will be compiled
-                Loader.loadClass(Holder.class, GLASSFISH_SENTINEL_CLASS);
-                if (LOG.isDebugEnabled())LOG.debug("Glassfish jasper detected");
-                _jspContainer = JspContainer.GLASSFISH;
+                //check for apache
+                Loader.loadClass(Holder.class, APACHE_SENTINEL_CLASS);
+                if (LOG.isDebugEnabled())LOG.debug("Apache jasper detected");
+                _jspContainer = JspContainer.APACHE;
             }
-            catch (ClassNotFoundException e)
+            catch (ClassNotFoundException x)
             {
-                try
-                {
-                    //check for apache
-                    Loader.loadClass(Holder.class, APACHE_SENTINEL_CLASS);
-                    if (LOG.isDebugEnabled())LOG.debug("Apache jasper detected");
-                    _jspContainer = JspContainer.APACHE;
-                }
-                catch (ClassNotFoundException x)
-                {
-                    if (LOG.isDebugEnabled())LOG.debug("Other jasper detected");
-                    _jspContainer = JspContainer.OTHER;
-                }
+                if (LOG.isDebugEnabled())LOG.debug("Other jasper detected");
+                _jspContainer = JspContainer.OTHER;
             }
         }
     }
 
     /* ------------------------------------------------------------ */
-    private String getNameOfJspClass (String jsp)
+    /**
+     * @param jsp the jsp-file
+     * @return the simple classname of the jsp
+     */
+    public String getNameOfJspClass (String jsp)
     {
-        if (jsp == null)
-            return "";
-        
-        int i = jsp.lastIndexOf('/') + 1;
-        jsp = jsp.substring(i);
+        if (StringUtil.isBlank(jsp))
+            return ""; //empty
+
+        jsp = jsp.trim();
+        if ("/".equals(jsp))
+            return ""; //only slash
+
+        int i = jsp.lastIndexOf('/');
+        if (i == jsp.length()-1)
+            return ""; //ends with slash
+
+        jsp = jsp.substring(i+1);
         try
         {
             Class<?> jspUtil = Loader.loadClass(Holder.class, "org.apache.jasper.compiler.JspUtil");
@@ -920,20 +947,22 @@
         catch (Exception e)
         {
             String tmp = jsp.replace('.','_');
-            LOG.warn("Unable to make identifier for jsp "+jsp +" trying "+tmp+" instead");
             if (LOG.isDebugEnabled())
+            {
+                LOG.warn("JspUtil.makeJavaIdentifier failed for jsp "+jsp +" using "+tmp+" instead");
                 LOG.warn(e);
+            }
             return tmp;
         }
     }
-    
-    
+
+
     /* ------------------------------------------------------------ */
-    private String getPackageOfJspClass (String jsp)
+    public String getPackageOfJspClass (String jsp)
     {
         if (jsp == null)
             return "";
-        
+
         int i = jsp.lastIndexOf('/');
         if (i <= 0)
             return "";
@@ -941,37 +970,86 @@
         {
             Class<?> jspUtil = Loader.loadClass(Holder.class, "org.apache.jasper.compiler.JspUtil");
             Method makeJavaPackage = jspUtil.getMethod("makeJavaPackage", String.class);
-            return (String)makeJavaPackage.invoke(null, jsp.substring(0,i));
-        } 
+            String p = (String)makeJavaPackage.invoke(null, jsp.substring(0,i));
+            return p;
+        }
         catch (Exception e)
         {
-            String tmp = jsp.substring(1).replace('/','.');
-            LOG.warn("Unable to make package for jsp "+jsp +" trying "+tmp+" instead");
+            String tmp = jsp;
+            
+            //remove any leading slash
+            int s = 0;
+            if ('/' == (tmp.charAt(0)))
+                s = 1;
+            
+            //remove the element after last slash, which should be name of jsp
+            tmp = tmp.substring(s,i);
+
+            tmp = tmp.replace('/','.').trim();
+            tmp = (".".equals(tmp)? "": tmp);
             if (LOG.isDebugEnabled())
+            {
+                LOG.warn("JspUtil.makeJavaPackage failed for "+jsp +" using "+tmp+" instead");
                 LOG.warn(e);
+            }
             return tmp;
         }
     }
-    
-    
+
+
     /* ------------------------------------------------------------ */
-    private String getJspPackagePrefix ()
+    /**
+     * @return the package for all jsps
+     */
+    public String getJspPackagePrefix ()
     {
-        String jspPackageName = (String)getServletHandler().getServletContext().getInitParameter(JSP_GENERATED_PACKAGE_NAME );
+        String jspPackageName = null;
+
+        if (getServletHandler() != null && getServletHandler().getServletContext() != null)
+            jspPackageName = (String)getServletHandler().getServletContext().getInitParameter(JSP_GENERATED_PACKAGE_NAME );
+
         if (jspPackageName == null)
             jspPackageName = "org.apache.jsp";
-        
+
         return jspPackageName;
     }
-    
-    
+
+
     /* ------------------------------------------------------------ */
-    private String getClassNameForJsp (String jsp)
+    /**
+     * @param jsp the jsp-file from web.xml
+     * @return the fully qualified classname
+     */
+    public String getClassNameForJsp (String jsp)
     {
         if (jsp == null)
-            return null; 
-        
-        return getJspPackagePrefix() + "." +getPackageOfJspClass(jsp) + "." + getNameOfJspClass(jsp);
+            return null;
+
+        String name = getNameOfJspClass(jsp);
+        if (StringUtil.isBlank(name))
+            return null;
+
+        StringBuffer fullName = new StringBuffer();
+        appendPath(fullName, getJspPackagePrefix());
+        appendPath(fullName, getPackageOfJspClass(jsp));
+        appendPath(fullName, name);
+        return fullName.toString();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Concatenate an element on to fully qualified classname.
+     * 
+     * @param path the path under construction
+     * @param element the element of the name to add
+     */
+    protected void appendPath (StringBuffer path, String element)
+    {
+        if (StringUtil.isBlank(element))
+            return;
+        if (path.length() > 0)
+            path.append(".");
+        path.append(element);
     }
 
 
@@ -1194,9 +1272,9 @@
     /* ------------------------------------------------------------ */
     /**
      * @return the newly created Servlet instance
-     * @throws ServletException
-     * @throws IllegalAccessException
-     * @throws InstantiationException
+     * @throws ServletException if unable to create a new instance
+     * @throws IllegalAccessException if not allowed to create a new instance
+     * @throws InstantiationException if creating new instance resulted in error
      */
     protected Servlet newInstance() throws ServletException, IllegalAccessException, InstantiationException
     {
@@ -1217,12 +1295,12 @@
             throw se;
         }
     }
-    
+
 
     /* ------------------------------------------------------------ */
     @Override
     public String toString()
     {
-        return String.format("%s@%x==%s,%d,%b",_name,hashCode(),_className,_initOrder,_servlet!=null);
+        return String.format("%s@%x==%s,jsp=%s,order=%d,inst=%b",_name,hashCode(),_className,_forcedPath,_initOrder,_servlet!=null);
     }
 }
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java
index 8332a75..9ce2dc1 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java
@@ -65,6 +65,25 @@
     {
         _pathSpecs = pathSpecs;
     }
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Test if the list of path specs contains a particular one.
+     * @param pathSpec the test pathspec
+     * @return true if pathspec contains test spec
+     */
+    public boolean containsPathSpec (String pathSpec)
+    {
+        if (_pathSpecs == null || _pathSpecs.length == 0)
+            return false;
+        
+        for (String p:_pathSpecs)
+        {
+            if (p.equals(pathSpec))
+                return true;
+        }
+        return false;
+    }
 
     /* ------------------------------------------------------------ */
     /**
@@ -86,9 +105,6 @@
     
     
     /* ------------------------------------------------------------ */
-    /**
-     * @return
-     */
     @ManagedAttribute(value="default", readonly=true)
     public boolean isDefault()
     {
@@ -97,9 +113,6 @@
     
     
     /* ------------------------------------------------------------ */
-    /**
-     * @param fromDefault
-     */
     public void setDefault(boolean fromDefault)
     {
         _default = fromDefault;
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletTester.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletTester.java
deleted file mode 100644
index 29b4dc3..0000000
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletTester.java
+++ /dev/null
@@ -1,259 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlet;
-
-import java.net.InetAddress;
-import java.nio.ByteBuffer;
-import java.util.EnumSet;
-import java.util.Enumeration;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import javax.servlet.DispatcherType;
-import javax.servlet.Filter;
-import javax.servlet.Servlet;
-
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.LocalConnector;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.util.Attributes;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.component.ContainerLifeCycle;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.resource.Resource;
-
-public class ServletTester extends ContainerLifeCycle
-{
-    private static final Logger LOG = Log.getLogger(ServletTester.class);
-    
-    private final Server _server=new Server();
-    private final LocalConnector _connector=new LocalConnector(_server);
-    private final ServletContextHandler _context;
-    
-    public Server getServer()
-    {
-        return _server;
-    }
-    
-    public LocalConnector getConnector()
-    {
-        return _connector;
-    }
-    
-    public void setVirtualHosts(String[] vhosts)
-    {
-        _context.setVirtualHosts(vhosts);
-    }
-
-    public void addVirtualHosts(String[] virtualHosts)
-    {
-        _context.addVirtualHosts(virtualHosts);
-    }
-
-    public ServletHolder addServlet(String className, String pathSpec)
-    {
-        return _context.addServlet(className,pathSpec);
-    }
-
-    public ServletHolder addServlet(Class<? extends Servlet> servlet, String pathSpec)
-    {
-        return _context.addServlet(servlet,pathSpec);
-    }
-
-    public void addServlet(ServletHolder servlet, String pathSpec)
-    {
-        _context.addServlet(servlet,pathSpec);
-    }
-
-    public void addFilter(FilterHolder holder, String pathSpec, EnumSet<DispatcherType> dispatches)
-    {
-        _context.addFilter(holder,pathSpec,dispatches);
-    }
-
-    public FilterHolder addFilter(Class<? extends Filter> filterClass, String pathSpec, EnumSet<DispatcherType> dispatches)
-    {
-        return _context.addFilter(filterClass,pathSpec,dispatches);
-    }
-
-    public FilterHolder addFilter(String filterClass, String pathSpec, EnumSet<DispatcherType> dispatches)
-    {
-        return _context.addFilter(filterClass,pathSpec,dispatches);
-    }
-
-    public Object getAttribute(String name)
-    {
-        return _context.getAttribute(name);
-    }
-
-    public Enumeration<String> getAttributeNames()
-    {
-        return _context.getAttributeNames();
-    }
-
-    public Attributes getAttributes()
-    {
-        return _context.getAttributes();
-    }
-
-    public String getContextPath()
-    {
-        return _context.getContextPath();
-    }
-
-    public String getInitParameter(String name)
-    {
-        return _context.getInitParameter(name);
-    }
-
-    public String setInitParameter(String name, String value)
-    {
-        return _context.setInitParameter(name,value);
-    }
-
-    public Enumeration<String> getInitParameterNames()
-    {
-        return _context.getInitParameterNames();
-    }
-
-    public Map<String, String> getInitParams()
-    {
-        return _context.getInitParams();
-    }
-
-    public void removeAttribute(String name)
-    {
-        _context.removeAttribute(name);
-    }
-
-    public void setAttribute(String name, Object value)
-    {
-        _context.setAttribute(name,value);
-    }
-
-    public void setContextPath(String contextPath)
-    {
-        _context.setContextPath(contextPath);
-    }
-
-    public Resource getBaseResource()
-    {
-        return _context.getBaseResource();
-    }
-
-    public String getResourceBase()
-    {
-        return _context.getResourceBase();
-    }
-
-    public void setResourceBase(String resourceBase)
-    {
-        _context.setResourceBase(resourceBase);
-    }
-
-    public ServletTester()
-    {
-        this("/",ServletContextHandler.SECURITY|ServletContextHandler.SESSIONS);
-    }
-
-    public ServletTester(String ctxPath)
-    {
-        this(ctxPath,ServletContextHandler.SECURITY|ServletContextHandler.SESSIONS);
-    }
-
-    public ServletTester(String contextPath,int options)
-    {
-        _context=new ServletContextHandler(_server,contextPath,options);
-        _server.setConnectors(new Connector[]{_connector});
-        addBean(_server);
-    }
-
-    public ServletContextHandler getContext()
-    {
-        return _context;
-    }
-
-    public String getResponses(String request) throws Exception
-    {
-        if (LOG.isDebugEnabled())
-        {
-            LOG.debug("Request: {}",request);
-        }
-        return _connector.getResponses(request);
-    }
-    
-    public String getResponses(String request, long idleFor,TimeUnit units) throws Exception
-    {
-        if (LOG.isDebugEnabled())
-        {
-            LOG.debug("Request: {}",request);
-        }
-        return _connector.getResponses(request, idleFor, units);
-    }
-    
-    public ByteBuffer getResponses(ByteBuffer request) throws Exception
-    {
-        if (LOG.isDebugEnabled())
-        {
-            LOG.debug("Request (Buffer): {}",BufferUtil.toUTF8String(request));
-        }
-        return _connector.getResponses(request);
-    }
-    
-    public ByteBuffer getResponses(ByteBuffer requestsBuffer,long idleFor,TimeUnit units) throws Exception
-    {
-        if (LOG.isDebugEnabled())
-        {
-            LOG.debug("Requests (Buffer): {}",BufferUtil.toUTF8String(requestsBuffer));
-        }
-        return _connector.getResponses(requestsBuffer, idleFor, units);
-    }
-
-    /** Create a port based connector.
-     * This methods adds a port connector to the server
-     * @return A URL to access the server via the connector.
-     * @throws Exception
-     */
-    public String createConnector(boolean localhost) throws Exception
-    {
-        ServerConnector connector = new ServerConnector(_server);
-        if (localhost)
-            connector.setHost("127.0.0.1");
-        _server.addConnector(connector);
-        if (_server.isStarted())
-            connector.start();
-        else
-            connector.open();
-
-        return "http://"+(localhost?"127.0.0.1":
-            InetAddress.getLocalHost().getHostAddress()
-        )+":"+connector.getLocalPort();
-    }
-
-    public LocalConnector createLocalConnector()
-    {
-        LocalConnector connector = new LocalConnector(_server);
-        _server.addConnector(connector);
-        return connector;
-    }
-
-
-
-}
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/StatisticsServlet.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/StatisticsServlet.java
index 8d037e9..43949a7 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/StatisticsServlet.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/StatisticsServlet.java
@@ -31,6 +31,7 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.io.ConnectionStatistics;
 import org.eclipse.jetty.server.AbstractConnector;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.ConnectorStatistics;
@@ -38,14 +39,10 @@
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.StatisticsHandler;
+import org.eclipse.jetty.util.component.Container;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-/**
- * StatisticsServlet
- *
- *
- */
 public class StatisticsServlet extends HttpServlet
 {
     private static final Logger LOG = Log.getLogger(StatisticsServlet.class);
@@ -55,11 +52,6 @@
     private MemoryMXBean _memoryBean;
     private Connector[] _connectors;
 
-    
-    
-    /** 
-     * @see javax.servlet.GenericServlet#init()
-     */
     public void init() throws ServletException
     {
         ServletContext context = getServletContext();
@@ -87,21 +79,11 @@
         }
     }
 
-    
-    
-    /** 
-     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
-     */
     public void doPost(HttpServletRequest sreq, HttpServletResponse sres) throws ServletException, IOException
     {
         doGet(sreq, sres);
     }
 
-    
-    
-    /** 
-     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
-     */
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
     {
         if (_statsHandler == null)
@@ -119,11 +101,17 @@
             }
         }
 
+        if (Boolean.valueOf( req.getParameter("statsReset")))
+        {
+            _statsHandler.statsReset();
+            return;
+        }
+
         String wantXml = req.getParameter("xml");
         if (wantXml == null)
           wantXml = req.getParameter("XML");
 
-        if (wantXml != null && "true".equalsIgnoreCase(wantXml))
+        if (Boolean.valueOf(wantXml))
         {
             sendXmlResponse(resp);
         }
@@ -131,7 +119,6 @@
         {
             sendTextResponse(resp);
         }
-
     }
 
     private boolean isLoopbackAddress(String address)
@@ -199,24 +186,45 @@
                 sb.append("      <protocol>").append(protocol).append("</protocol>\n");
             sb.append("      </protocols>\n");
 
-            ConnectorStatistics connectorStats = null;
-
+            ConnectionStatistics connectionStats = null;
             if (connector instanceof AbstractConnector)
-                connectorStats = ((AbstractConnector)connector).getBean(ConnectorStatistics.class);
-            if (connectorStats == null)
-                sb.append("      <statsOn>false</statsOn>\n");
-            else
+                connectionStats = ((AbstractConnector)connector).getBean(ConnectionStatistics.class);
+            if (connectionStats != null)
             {
                 sb.append("      <statsOn>true</statsOn>\n");
-                sb.append("      <connections>").append(connectorStats.getConnections()).append("</connections>\n");
-                sb.append("      <connectionsOpen>").append(connectorStats.getConnectionsOpen()).append("</connectionsOpen>\n");
-                sb.append("      <connectionsOpenMax>").append(connectorStats.getConnectionsOpenMax()).append("</connectionsOpenMax>\n");
-                sb.append("      <connectionsDurationMean>").append(connectorStats.getConnectionDurationMean()).append("</connectionsDurationMean>\n");
-                sb.append("      <connectionsDurationMax>").append(connectorStats.getConnectionDurationMax()).append("</connectionsDurationMax>\n");
-                sb.append("      <connectionsDurationStdDev>").append(connectorStats.getConnectionDurationStdDev()).append("</connectionsDurationStdDev>\n");
-                sb.append("      <messagesIn>").append(connectorStats.getMessagesIn()).append("</messagesIn>\n");
-                sb.append("      <messagesOut>").append(connectorStats.getMessagesIn()).append("</messagesOut>\n");
-                sb.append("      <elapsedMs>").append(connectorStats.getStartedMillis()).append("</elapsedMs>\n");
+                sb.append("      <connections>").append(connectionStats.getConnectionsTotal()).append("</connections>\n");
+                sb.append("      <connectionsOpen>").append(connectionStats.getConnections()).append("</connectionsOpen>\n");
+                sb.append("      <connectionsOpenMax>").append(connectionStats.getConnectionsMax()).append("</connectionsOpenMax>\n");
+                sb.append("      <connectionsDurationMean>").append(connectionStats.getConnectionDurationMean()).append("</connectionsDurationMean>\n");
+                sb.append("      <connectionsDurationMax>").append(connectionStats.getConnectionDurationMax()).append("</connectionsDurationMax>\n");
+                sb.append("      <connectionsDurationStdDev>").append(connectionStats.getConnectionDurationStdDev()).append("</connectionsDurationStdDev>\n");
+                sb.append("      <bytesIn>").append(connectionStats.getReceivedBytes()).append("</bytesIn>\n");
+                sb.append("      <bytesOut>").append(connectionStats.getSentBytes()).append("</connectorStats>\n");
+                sb.append("      <messagesIn>").append(connectionStats.getReceivedMessages()).append("</messagesIn>\n");
+                sb.append("      <messagesOut>").append(connectionStats.getSentMessages()).append("</messagesOut>\n");
+            }
+            else
+            {
+                ConnectorStatistics connectorStats = null;
+                if (connector instanceof AbstractConnector)
+                    connectorStats = ((AbstractConnector)connector).getBean(ConnectorStatistics.class);
+                if (connectorStats != null)
+                {
+                    sb.append("      <statsOn>true</statsOn>\n");
+                    sb.append("      <connections>").append(connectorStats.getConnections()).append("</connections>\n");
+                    sb.append("      <connectionsOpen>").append(connectorStats.getConnectionsOpen()).append("</connectionsOpen>\n");
+                    sb.append("      <connectionsOpenMax>").append(connectorStats.getConnectionsOpenMax()).append("</connectionsOpenMax>\n");
+                    sb.append("      <connectionsDurationMean>").append(connectorStats.getConnectionDurationMean()).append("</connectionsDurationMean>\n");
+                    sb.append("      <connectionsDurationMax>").append(connectorStats.getConnectionDurationMax()).append("</connectionsDurationMax>\n");
+                    sb.append("      <connectionsDurationStdDev>").append(connectorStats.getConnectionDurationStdDev()).append("</connectionsDurationStdDev>\n");
+                    sb.append("      <messagesIn>").append(connectorStats.getMessagesIn()).append("</messagesIn>\n");
+                    sb.append("      <messagesOut>").append(connectorStats.getMessagesIn()).append("</messagesOut>\n");
+                    sb.append("      <elapsedMs>").append(connectorStats.getStartedMillis()).append("</elapsedMs>\n");
+                }
+                else
+                {
+                    sb.append("      <statsOn>false</statsOn>\n");
+                }
             }
             sb.append("    </connector>\n");
         }
@@ -234,12 +242,6 @@
         pout.write(sb.toString());
     }
 
-    
-    
-    /**
-     * @param response
-     * @throws IOException
-     */
     private void sendTextResponse(HttpServletResponse response) throws IOException
     {
         StringBuilder sb = new StringBuilder();
@@ -254,28 +256,44 @@
                 sb.append(protocol).append("&nbsp;");
             sb.append("    <br />\n");
 
-            ConnectorStatistics connectorStats = null;
-
-            if (connector instanceof AbstractConnector)
-                connectorStats = ((AbstractConnector)connector).getBean(ConnectorStatistics.class);
-
-            if (connectorStats != null)
+            ConnectionStatistics connectionStats = null;
+            if (connector instanceof Container)
+                connectionStats = ((Container)connector).getBean(ConnectionStatistics.class);
+            if (connectionStats != null)
             {
-                sb.append("Statistics gathering started ").append(connectorStats.getStartedMillis()).append("ms ago").append("<br />\n");
-                sb.append("Total connections: ").append(connectorStats.getConnections()).append("<br />\n");
-                sb.append("Current connections open: ").append(connectorStats.getConnectionsOpen()).append("<br />\n");;
-                sb.append("Max concurrent connections open: ").append(connectorStats.getConnectionsOpenMax()).append("<br />\n");
-                sb.append("Mean connection duration: ").append(connectorStats.getConnectionDurationMean()).append("<br />\n");
-                sb.append("Max connection duration: ").append(connectorStats.getConnectionDurationMax()).append("<br />\n");
-                sb.append("Connection duration standard deviation: ").append(connectorStats.getConnectionDurationStdDev()).append("<br />\n");
-                sb.append("Total messages in: ").append(connectorStats.getMessagesIn()).append("<br />\n");                
-                sb.append("Total messages out: ").append(connectorStats.getMessagesOut()).append("<br />\n");
+                sb.append("Total connections: ").append(connectionStats.getConnectionsTotal()).append("<br />\n");
+                sb.append("Current connections open: ").append(connectionStats.getConnections()).append("<br />\n");
+                sb.append("Max concurrent connections open: ").append(connectionStats.getConnectionsMax()).append("<br />\n");
+                sb.append("Mean connection duration: ").append(connectionStats.getConnectionDurationMean()).append("<br />\n");
+                sb.append("Max connection duration: ").append(connectionStats.getConnectionDurationMax()).append("<br />\n");
+                sb.append("Connection duration standard deviation: ").append(connectionStats.getConnectionDurationStdDev()).append("<br />\n");
+                sb.append("Total bytes received: ").append(connectionStats.getReceivedBytes()).append("<br />\n");
+                sb.append("Total bytes sent: ").append(connectionStats.getSentBytes()).append("<br />\n");
+                sb.append("Total messages received: ").append(connectionStats.getReceivedMessages()).append("<br />\n");
+                sb.append("Total messages sent: ").append(connectionStats.getSentMessages()).append("<br />\n");
             }
             else
             {
-                sb.append("Statistics gathering off.\n");
+                ConnectorStatistics connectorStats = null;
+                if (connector instanceof AbstractConnector)
+                    connectorStats = ((AbstractConnector)connector).getBean(ConnectorStatistics.class);
+                if (connectorStats != null)
+                {
+                    sb.append("Statistics gathering started ").append(connectorStats.getStartedMillis()).append("ms ago").append("<br />\n");
+                    sb.append("Total connections: ").append(connectorStats.getConnections()).append("<br />\n");
+                    sb.append("Current connections open: ").append(connectorStats.getConnectionsOpen()).append("<br />\n");
+                    sb.append("Max concurrent connections open: ").append(connectorStats.getConnectionsOpenMax()).append("<br />\n");
+                    sb.append("Mean connection duration: ").append(connectorStats.getConnectionDurationMean()).append("<br />\n");
+                    sb.append("Max connection duration: ").append(connectorStats.getConnectionDurationMax()).append("<br />\n");
+                    sb.append("Connection duration standard deviation: ").append(connectorStats.getConnectionDurationStdDev()).append("<br />\n");
+                    sb.append("Total messages in: ").append(connectorStats.getMessagesIn()).append("<br />\n");
+                    sb.append("Total messages out: ").append(connectorStats.getMessagesOut()).append("<br />\n");
+                }
+                else
+                {
+                    sb.append("Statistics gathering off.\n");
+                }
             }
-
         }
 
         sb.append("<h2>Memory:</h2>\n");
@@ -285,6 +303,5 @@
         response.setContentType("text/html");
         PrintWriter pout = response.getWriter();
         pout.write(sb.toString());
-
     }
 }
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextDispatchWithQueryStrings.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextDispatchWithQueryStrings.java
index f2a190d..ea2f541 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextDispatchWithQueryStrings.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextDispatchWithQueryStrings.java
@@ -18,8 +18,9 @@
 
 package org.eclipse.jetty.servlet;
 
+import static org.hamcrest.Matchers.startsWith;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertThat;
 
 import java.io.IOException;
 
@@ -43,69 +44,77 @@
  * This tests verifies that merging of queryStrings works when dispatching
  * Requests via {@link AsyncContext} multiple times.
  */
-public class AsyncContextDispatchWithQueryStrings {
+public class AsyncContextDispatchWithQueryStrings 
+{
+    private Server _server = new Server();
+    private ServletContextHandler _contextHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
+    private LocalConnector _connector = new LocalConnector(_server);
 
-        private Server _server = new Server();
-        private ServletContextHandler _contextHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
-        private LocalConnector _connector = new LocalConnector(_server);
+    @Before
+    public void setUp() throws Exception 
+    {
+        _connector.setIdleTimeout(30000);
+        _server.setConnectors(new Connector[] { _connector });
 
-        @Before
-        public void setUp() throws Exception {
-                _connector.setIdleTimeout(30000);
-                _server.setConnectors(new Connector[] { _connector });
+        _contextHandler.setContextPath("/");
+        _contextHandler.addServlet(new ServletHolder(new TestServlet()), "/initialCall");
+        _contextHandler.addServlet(new ServletHolder(new TestServlet()), "/firstDispatchWithNewQueryString");
+        _contextHandler.addServlet(new ServletHolder(new TestServlet()), "/secondDispatchNewValueForExistingQueryString");
 
-                _contextHandler.setContextPath("/");
-                _contextHandler.addServlet(new ServletHolder(new TestServlet()), "/initialCall");
-                _contextHandler.addServlet(new ServletHolder(new TestServlet()), "/firstDispatchWithNewQueryString");
-                _contextHandler.addServlet(new ServletHolder(new TestServlet()), "/secondDispatchNewValueForExistingQueryString");
+        HandlerList handlers = new HandlerList();
+        handlers.setHandlers(new Handler[] { _contextHandler, new DefaultHandler() });
 
-                HandlerList handlers = new HandlerList();
-                handlers.setHandlers(new Handler[] { _contextHandler, new DefaultHandler() });
+        _server.setHandler(handlers);
+        _server.start();
+    }
 
-                _server.setHandler(handlers);
-                _server.start();
+    @Test
+    public void testMultipleDispatchesWithNewQueryStrings() throws Exception 
+    {
+        String request = 
+                "GET /initialCall?initialParam=right HTTP/1.1\r\n" + 
+                        "Host: localhost\r\n" + 
+                        "Content-Type: application/x-www-form-urlencoded\r\n" +
+                        "Connection: close\r\n" + "\r\n";
+        String responseString = _connector.getResponses(request);
+        assertThat(responseString,startsWith("HTTP/1.1 200"));
+    }
+
+    @After
+    public void tearDown() throws Exception 
+    {
+        _server.stop();
+        _server.join();
+    }
+
+    private class TestServlet extends HttpServlet 
+    {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
+        {
+            String uri = request.getRequestURI();
+            String queryString = request.getQueryString();
+            if ("/initialCall".equals(uri)) 
+            {
+                AsyncContext async = request.startAsync();
+                async.dispatch("/firstDispatchWithNewQueryString?newQueryString=initialValue");
+                assertEquals("initialParam=right", queryString);
+            } 
+            else if ("/firstDispatchWithNewQueryString".equals(uri)) 
+            {
+                AsyncContext async = request.startAsync();
+                async.dispatch("/secondDispatchNewValueForExistingQueryString?newQueryString=newValue");
+                assertEquals("newQueryString=initialValue&initialParam=right", queryString);
+            } 
+            else 
+            {
+                response.setContentType("text/html");
+                response.setStatus(HttpServletResponse.SC_OK);
+                response.getWriter().println("<h1>woohhooooo</h1>");
+                assertEquals("newQueryString=newValue&initialParam=right", queryString);
+            }
         }
-
-        @Test
-        public void testMultipleDispatchesWithNewQueryStrings() throws Exception {
-                String request = "GET /initialCall?initialParam=right HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
-                                + "Connection: close\r\n" + "\r\n";
-                String responseString = _connector.getResponses(request);
-                assertTrue("Not the expected response. Check STDOUT for details.", responseString.startsWith("HTTP/1.1 200"));
-        }
-
-        @After
-        public void tearDown() throws Exception {
-                _server.stop();
-                _server.join();
-        }
-
-        private class TestServlet extends HttpServlet {
-                private static final long serialVersionUID = 1L;
-
-                @Override
-                protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-                        String path = request.getRequestURI();
-                        String queryString = request.getQueryString();
-                        if ("/initialCall".equals(path)) 
-                        {
-                            AsyncContext async = request.startAsync();
-                            async.dispatch("/firstDispatchWithNewQueryString?newQueryString=initialValue");
-                            assertEquals("initialParam=right", queryString);
-                        } 
-                        else if ("/firstDispatchWithNewQueryString".equals(path)) 
-                        {
-                            AsyncContext async = request.startAsync();
-                            async.dispatch("/secondDispatchNewValueForExistingQueryString?newQueryString=newValue");
-                            assertEquals("newQueryString=initialValue&initialParam=right", queryString);
-                        } 
-                        else 
-                        {
-                            response.setContentType("text/html");
-                            response.setStatus(HttpServletResponse.SC_OK);
-                            response.getWriter().println("<h1>woohhooooo</h1>");
-                            assertEquals("newQueryString=newValue&initialParam=right", queryString);
-                        }
-                }
-        }
+    }
 }
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextListenersTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextListenersTest.java
index 4ea8271..a540748 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextListenersTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextListenersTest.java
@@ -18,13 +18,13 @@
 
 package org.eclipse.jetty.servlet;
 
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.servlet.AsyncContext;
 import javax.servlet.AsyncEvent;
@@ -34,10 +34,9 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpParser;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Test;
@@ -64,11 +63,12 @@
     {
         _server.stop();
     }
-
+    
+    @SuppressWarnings("Duplicates")
     @Test
     public void testListenerClearedOnSecondRequest() throws Exception
     {
-        final AtomicInteger completes = new AtomicInteger();
+        final AtomicReference<CountDownLatch> completes = new AtomicReference<>(new CountDownLatch(1));
         String path = "/path";
         prepare(path, new HttpServlet()
         {
@@ -86,7 +86,7 @@
                     @Override
                     public void onComplete(AsyncEvent event) throws IOException
                     {
-                        completes.incrementAndGet();
+                        completes.get().countDown();
                     }
 
                     @Override
@@ -102,7 +102,7 @@
                 asyncContext.complete();
             }
         });
-
+    
         try (Socket socket = new Socket("localhost", _connector.getLocalPort()))
         {
             OutputStream output = socket.getOutputStream();
@@ -113,28 +113,28 @@
                     "\r\n";
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
-
-            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
-            SimpleHttpParser parser = new SimpleHttpParser();
-            SimpleHttpResponse response = parser.readResponse(reader);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals(1, completes.get());
+    
+            HttpTester.Input input = HttpTester.from(socket.getInputStream());
+            HttpTester.Response response = HttpTester.parseResponse(input);
+            Assert.assertEquals(200, response.getStatus());
+            completes.get().await(10,TimeUnit.SECONDS);
 
             // Send a second request
-            completes.set(0);
+            completes.set(new CountDownLatch(1));
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
-            response = parser.readResponse(reader);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals(1, completes.get());
+            response = HttpTester.parseResponse(input);
+            Assert.assertEquals(200, response.getStatus());
+            completes.get().await(10,TimeUnit.SECONDS);
         }
     }
 
+    @SuppressWarnings("Duplicates")
     @Test
     public void testListenerAddedFromListener() throws Exception
     {
-        final AtomicInteger completes = new AtomicInteger();
+        final AtomicReference<CountDownLatch> completes = new AtomicReference<>(new CountDownLatch(1));
         String path = "/path";
         prepare(path, new HttpServlet()
         {
@@ -157,7 +157,7 @@
                     @Override
                     public void onComplete(AsyncEvent event) throws IOException
                     {
-                        completes.incrementAndGet();
+                        completes.get().countDown();
                     }
 
                     @Override
@@ -185,26 +185,25 @@
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
-            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
-            SimpleHttpParser parser = new SimpleHttpParser();
-            SimpleHttpResponse response = parser.readResponse(reader);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals(1, completes.get());
+            HttpTester.Input input = HttpTester.from(socket.getInputStream());
+            HttpTester.Response response = HttpTester.parseResponse(input);
+            Assert.assertEquals(200, response.getStatus());
+            completes.get().await(10,TimeUnit.SECONDS);
 
             // Send a second request
-            completes.set(0);
+            completes.set(new CountDownLatch(1));
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
-
-            response = parser.readResponse(reader);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals(1, completes.get());
+    
+            response = HttpTester.parseResponse(input);
+            Assert.assertEquals(200, response.getStatus());
+            completes.get().await(10,TimeUnit.SECONDS);
         }
     }
     @Test
     public void testAsyncDispatchAsyncCompletePreservesListener() throws Exception
     {
-        final AtomicInteger completes = new AtomicInteger();
+        final AtomicReference<CountDownLatch> completes = new AtomicReference<>(new CountDownLatch(1));
         final String path = "/path";
         prepare(path + "/*", new HttpServlet()
         {
@@ -226,7 +225,7 @@
                         @Override
                         public void onComplete(AsyncEvent event) throws IOException
                         {
-                            completes.incrementAndGet();
+                            completes.get().countDown();
                         }
 
                         @Override
@@ -262,21 +261,20 @@
                     "\r\n";
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
-
-            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
-            SimpleHttpParser parser = new SimpleHttpParser();
-            SimpleHttpResponse response = parser.readResponse(reader);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals(1, completes.get());
+    
+            HttpTester.Input input = HttpTester.from(socket.getInputStream());
+            HttpTester.Response response = HttpTester.parseResponse(input);
+            Assert.assertEquals(200, response.getStatus());
+            completes.get().await(10,TimeUnit.SECONDS);
 
             // Send a second request
-            completes.set(0);
+            completes.set(new CountDownLatch(1));
             output.write(request.getBytes(StandardCharsets.UTF_8));
             output.flush();
 
-            response = parser.readResponse(reader);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertEquals(1, completes.get());
+            response = HttpTester.parseResponse(input);
+            Assert.assertEquals(200, response.getStatus());
+            completes.get().await(10,TimeUnit.SECONDS);
         }
     }
 }
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
index 68f85da..f8be053 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
@@ -38,8 +38,10 @@
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponseWrapper;
 
+import org.eclipse.jetty.http.HttpParser;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpChannel;
 import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.LocalConnector;
 import org.eclipse.jetty.server.QuietServletException;
@@ -47,6 +49,7 @@
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.DefaultHandler;
 import org.eclipse.jetty.server.handler.HandlerList;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -188,21 +191,24 @@
     @Test
     public void testStartFlushCompleteThrow() throws Exception
     {
-        String request = "GET /ctx/startthrow?flush=true&complete=true HTTP/1.1\r\n" + 
-           "Host: localhost\r\n" + 
-           "Content-Type: application/x-www-form-urlencoded\r\n" + 
-           "Connection: close\r\n" + 
-           "\r\n";
-        String responseString = _connector.getResponses(request);
+        try(StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            String request = "GET /ctx/startthrow?flush=true&complete=true HTTP/1.1\r\n" + 
+                    "Host: localhost\r\n" + 
+                    "Content-Type: application/x-www-form-urlencoded\r\n" + 
+                    "Connection: close\r\n" + 
+                    "\r\n";
+            String responseString = _connector.getResponses(request);
 
-        BufferedReader br = new BufferedReader(new StringReader(responseString));
+            BufferedReader br = new BufferedReader(new StringReader(responseString));
 
-        assertEquals("HTTP/1.1 200 OK",br.readLine());
-        br.readLine();// connection close
-        br.readLine();// server
-        br.readLine();// empty
+            assertEquals("HTTP/1.1 200 OK",br.readLine());
+            br.readLine();// connection close
+            br.readLine();// server
+            br.readLine();// empty
 
-        Assert.assertEquals("error servlet","completeBeforeThrow",br.readLine());
+            Assert.assertEquals("error servlet","completeBeforeThrow",br.readLine());
+        }
     }
     
     @Test
@@ -221,16 +227,6 @@
         Assert.assertEquals("query string attr is correct","async:run:attr:queryString:dispatch=true",br.readLine());
         Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:/ctx",br.readLine());
         Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/ctx/servletPath",br.readLine());
-
-        try
-        {
-            __asyncContext.getRequest();
-            Assert.fail();
-        }
-        catch (IllegalStateException e)
-        {
-            
-        }
     }
 
     @Test
@@ -342,8 +338,6 @@
         }
     }
 
-    public static volatile AsyncContext __asyncContext; 
-    
     private class AsyncDispatchingServlet extends HttpServlet
     {
         private static final long serialVersionUID = 1L;
@@ -364,12 +358,10 @@
                 {
                     wrapped = true;
                     asyncContext = request.startAsync(request, new Wrapper(response));
-                    __asyncContext=asyncContext;
                 }
                 else
                 {
                     asyncContext = request.startAsync();
-                    __asyncContext=asyncContext;
                 }
 
                 new Thread(new DispatchingRunnable(asyncContext, wrapped)).start();
@@ -410,7 +402,7 @@
         
         BufferedReader br = new BufferedReader(new StringReader(responseString));
 
-        assertEquals("HTTP/1.1 500 Async Exception",br.readLine());
+        assertEquals("HTTP/1.1 500 Server Error",br.readLine());
         br.readLine();// connection close
         br.readLine();// server
         br.readLine();// empty
@@ -524,14 +516,12 @@
             if (request.getParameter("dispatch") != null)
             {
                 AsyncContext asyncContext = request.startAsync(request,response);
-                __asyncContext=asyncContext;
                 asyncContext.dispatch("/servletPath2");
             }
             else
             {
                 response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n");
                 AsyncContext asyncContext = request.startAsync(request,response);
-                __asyncContext=asyncContext;
                 response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)asyncContext.getRequest()).getServletPath() + "\n");
                 asyncContext.start(new AsyncRunnable(asyncContext));
 
@@ -548,7 +538,6 @@
         {
             response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n");
             AsyncContext asyncContext = request.startAsync(request, response);
-            __asyncContext=asyncContext;
             response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)asyncContext.getRequest()).getServletPath() + "\n");
             asyncContext.start(new AsyncRunnable(asyncContext));
         }
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncIOServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncIOServletTest.java
deleted file mode 100644
index 5caddc4..0000000
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncIOServletTest.java
+++ /dev/null
@@ -1,560 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlet;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.Socket;
-import java.nio.charset.StandardCharsets;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.servlet.AsyncContext;
-import javax.servlet.ReadListener;
-import javax.servlet.ServletException;
-import javax.servlet.ServletInputStream;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.WriteListener;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpParser;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
-import org.eclipse.jetty.util.IO;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-public class AsyncIOServletTest
-{
-    private Server server;
-    private ServerConnector connector;
-    private ServletContextHandler context;
-    private String path = "/path";
-
-    public void startServer(HttpServlet servlet) throws Exception
-    {
-        server = new Server();
-        connector = new ServerConnector(server);
-        server.addConnector(connector);
-
-        context = new ServletContextHandler(server, "/", false, false);
-        ServletHolder holder = new ServletHolder(servlet);
-        holder.setAsyncSupported(true);
-        context.addServlet(holder, path);
-
-        server.start();
-    }
-
-    @After
-    public void stopServer() throws Exception
-    {
-        server.stop();
-    }
-
-    @Test
-    public void testAsyncReadThrowsException() throws Exception
-    {
-        testAsyncReadThrows(new NullPointerException("explicitly_thrown_by_test"));
-    }
-
-    @Test
-    public void testAsyncReadThrowsError() throws Exception
-    {
-        testAsyncReadThrows(new Error("explicitly_thrown_by_test"));
-    }
-
-    private void testAsyncReadThrows(final Throwable throwable) throws Exception
-    {
-        final CountDownLatch latch = new CountDownLatch(1);
-        startServer(new HttpServlet()
-        {
-            @Override
-            protected void service(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
-            {
-                final AsyncContext asyncContext = request.startAsync(request, response);
-                request.getInputStream().setReadListener(new ReadListener()
-                {
-                    @Override
-                    public void onDataAvailable() throws IOException
-                    {
-                        if (throwable instanceof RuntimeException)
-                            throw (RuntimeException)throwable;
-                        if (throwable instanceof Error)
-                            throw (Error)throwable;
-                        throw new IOException(throwable);
-                    }
-
-                    @Override
-                    public void onAllDataRead() throws IOException
-                    {
-                    }
-
-                    @Override
-                    public void onError(Throwable t)
-                    {
-                        Assert.assertThat("onError type",t,instanceOf(throwable.getClass()));
-                        Assert.assertThat("onError message",t.getMessage(),is(throwable.getMessage()));
-                        latch.countDown();
-                        response.setStatus(500);
-                        asyncContext.complete();
-                    }
-                });
-            }
-        });
-
-        String data = "0123456789";
-        String request = "GET " + path + " HTTP/1.1\r\n" +
-                "Host: localhost:" + connector.getLocalPort() + "\r\n" +
-                "Content-Length: " + data.length() + "\r\n" +
-                "\r\n" +
-                data;
-
-        try (Socket client = new Socket("localhost", connector.getLocalPort()))
-        {
-            OutputStream output = client.getOutputStream();
-            output.write(request.getBytes("UTF-8"));
-            output.flush();
-
-            SimpleHttpParser parser = new SimpleHttpParser();
-            SimpleHttpResponse response = parser.readResponse(new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")));
-
-            Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-            Assert.assertEquals("500", response.getCode());
-        }
-    }
-
-    @Test
-    public void testAsyncReadIdleTimeout() throws Exception
-    {
-        final int status = 567;
-        startServer(new HttpServlet()
-        {
-            @Override
-            protected void service(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
-            {
-                final AsyncContext asyncContext = request.startAsync(request, response);
-                asyncContext.setTimeout(0);
-                final ServletInputStream inputStream = request.getInputStream();
-                inputStream.setReadListener(new ReadListener()
-                {
-                    @Override
-                    public void onDataAvailable() throws IOException
-                    {
-                        while (inputStream.isReady() && !inputStream.isFinished())
-                            inputStream.read();
-                    }
-
-                    @Override
-                    public void onAllDataRead() throws IOException
-                    {
-                    }
-
-                    @Override
-                    public void onError(Throwable t)
-                    {
-                        response.setStatus(status);
-                        // Do not put Connection: close header here, the test
-                        // verifies that the server closes no matter what.
-                        asyncContext.complete();
-                    }
-                });
-            }
-        });
-        server.stop();
-        long idleTimeout = 1000;
-        connector.setIdleTimeout(idleTimeout);
-        server.start();
-
-        String data1 = "0123456789";
-        String data2 = "ABCDEF";
-        // Only send the first chunk of data and then let it idle timeout.
-        String request = "GET " + path + " HTTP/1.1\r\n" +
-                "Host: localhost:" + connector.getLocalPort() + "\r\n" +
-                "Content-Length: " + (data1.length() + data2.length()) + "\r\n" +
-                "\r\n" +
-                data1;
-
-        try (Socket client = new Socket("localhost", connector.getLocalPort()))
-        {
-            OutputStream output = client.getOutputStream();
-            output.write(request.getBytes("UTF-8"));
-            output.flush();
-
-            SimpleHttpParser parser = new SimpleHttpParser();
-            SimpleHttpResponse response = parser.readResponse(new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")));
-
-            assertEquals(String.valueOf(status), response.getCode());
-
-            // Make sure the connection was closed by the server.
-            assertEquals(-1, client.getInputStream().read());
-        }
-    }
-
-    @Test
-    public void testOnErrorThrows() throws Exception
-    {
-        final AtomicInteger errors = new AtomicInteger();
-        startServer(new HttpServlet()
-        {
-            @Override
-            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-            {
-                final AsyncContext asyncContext = request.startAsync(request, response);
-                request.getInputStream().setReadListener(new ReadListener()
-                {
-                    @Override
-                    public void onDataAvailable() throws IOException
-                    {
-                        throw new NullPointerException("explicitly_thrown_by_test_1");
-                    }
-
-                    @Override
-                    public void onAllDataRead() throws IOException
-                    {
-                    }
-
-                    @Override
-                    public void onError(Throwable t)
-                    {
-                        errors.incrementAndGet();
-                        throw new NullPointerException("explicitly_thrown_by_test_2");
-                    }
-                });
-            }
-        });
-
-        String data = "0123456789";
-        String request = "GET " + path + " HTTP/1.1\r\n" +
-                "Host: localhost:" + connector.getLocalPort() + "\r\n" +
-                "Content-Length: " + data.length() + "\r\n" +
-                "\r\n" +
-                data;
-
-        try (Socket client = new Socket("localhost", connector.getLocalPort()))
-        {
-            OutputStream output = client.getOutputStream();
-            output.write(request.getBytes("UTF-8"));
-            output.flush();
-
-            SimpleHttpParser parser = new SimpleHttpParser();
-            SimpleHttpResponse response = parser.readResponse(new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")));
-
-            Assert.assertEquals("500", response.getCode());
-            Assert.assertEquals(1, errors.get());
-        }
-    }
-
-    @Test
-    public void testAsyncWriteThrowsException() throws Exception
-    {
-        testAsyncWriteThrows(new NullPointerException("explicitly_thrown_by_test"));
-    }
-
-    @Test
-    public void testAsyncWriteThrowsError() throws Exception
-    {
-        testAsyncWriteThrows(new Error("explicitly_thrown_by_test"));
-    }
-
-    private void testAsyncWriteThrows(final Throwable throwable) throws Exception
-    {
-        final CountDownLatch latch = new CountDownLatch(1);
-        startServer(new HttpServlet()
-        {
-            @Override
-            protected void service(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
-            {
-                final AsyncContext asyncContext = request.startAsync(request, response);
-                response.getOutputStream().setWriteListener(new WriteListener()
-                {
-                    @Override
-                    public void onWritePossible() throws IOException
-                    {
-                        if (throwable instanceof RuntimeException)
-                            throw (RuntimeException)throwable;
-                        if (throwable instanceof Error)
-                            throw (Error)throwable;
-                        throw new IOException(throwable);
-                    }
-
-                    @Override
-                    public void onError(Throwable t)
-                    {
-                        latch.countDown();
-                        response.setStatus(500);
-                        asyncContext.complete();
-                        Assert.assertSame(throwable, t);
-                    }
-                });
-            }
-        });
-
-        String request = "GET " + path + " HTTP/1.1\r\n" +
-                "Host: localhost:" + connector.getLocalPort() + "\r\n" +
-                "\r\n";
-
-        try (Socket client = new Socket("localhost", connector.getLocalPort()))
-        {
-            OutputStream output = client.getOutputStream();
-            output.write(request.getBytes("UTF-8"));
-            output.flush();
-
-            SimpleHttpParser parser = new SimpleHttpParser();
-            SimpleHttpResponse response = parser.readResponse(new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")));
-
-            Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-            Assert.assertEquals("500", response.getCode());
-        }
-    }
-    
-
-    @Test
-    public void testAsyncWriteClosed() throws Exception
-    {
-        final CountDownLatch latch = new CountDownLatch(1);
-        String text = "Now is the winter of our discontent. How Now Brown Cow. The quick brown fox jumped over the lazy dog.\n";
-        for (int i=0;i<10;i++)
-            text=text+text;
-        final byte[] data = text.getBytes(StandardCharsets.ISO_8859_1);
-        
-        startServer(new HttpServlet()
-        {
-            @Override
-            protected void service(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
-            {
-                response.flushBuffer();
-                
-                final AsyncContext async = request.startAsync();
-                final ServletOutputStream out = response.getOutputStream();
-                out.setWriteListener(new WriteListener()
-                {
-                    @Override
-                    public void onWritePossible() throws IOException
-                    {
-                        while (out.isReady())
-                        {
-                            try
-                            {
-                                Thread.sleep(100);
-                                out.write(data);
-                            }
-                            catch(IOException e)
-                            {
-                                throw e;
-                            }
-                            catch(Exception e)
-                            {
-                                e.printStackTrace();
-                            }
-                        }
-                    }
-
-                    @Override
-                    public void onError(Throwable t)
-                    {
-                        async.complete();
-                        latch.countDown();
-                    }
-                });
-            }
-        });
-
-        String request = "GET " + path + " HTTP/1.1\r\n" +
-                "Host: localhost:" + connector.getLocalPort() + "\r\n" +
-                "\r\n";
-
-        try (Socket client = new Socket("localhost", connector.getLocalPort()))
-        {
-            OutputStream output = client.getOutputStream();
-            output.write(request.getBytes("UTF-8"));
-            output.flush();
-
-            BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
-            String line=in.readLine();
-            assertThat(line, containsString("200 OK"));
-            while (line.length()>0)
-                line=in.readLine();
-            line=in.readLine();
-            assertThat(line, not(containsString(" ")));
-            line=in.readLine();
-            assertThat(line, containsString("discontent. How Now Brown Cow. The "));
-        }
-        
-        if (!latch.await(5, TimeUnit.SECONDS))
-            Assert.fail();
-    }
-    
-
-    @Test
-    public void testIsNotReadyAtEOF() throws Exception
-    {
-        String text = "Test\n";
-        final byte[] data = text.getBytes(StandardCharsets.ISO_8859_1);
-        
-        startServer(new HttpServlet()
-        {
-            @Override
-            protected void service(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
-            {
-                response.flushBuffer();
-                
-                final AsyncContext async = request.startAsync();
-                final ServletInputStream in = request.getInputStream();
-                final ServletOutputStream out = response.getOutputStream();
-                
-                in.setReadListener(new ReadListener()
-                {
-                    transient int _i=0;
-                    transient boolean _minusOne=false;;
-                    transient boolean _finished=false;;
-                    
-                    @Override
-                    public void onError(Throwable t)
-                    {
-                        t.printStackTrace();
-                        async.complete();
-                    }
-                    
-                    @Override
-                    public void onDataAvailable() throws IOException
-                    {
-                        while(in.isReady() && !in.isFinished())
-                        {
-                            int b = in.read();
-                            if (b==-1)
-                                _minusOne=true;
-                            else if (data[_i++]!=b)
-                                throw new IllegalStateException();
-                        }
-                        
-                        if (in.isFinished())
-                            _finished=true;
-                    }
-                    
-                    @Override
-                    public void onAllDataRead() throws IOException
-                    {
-                        out.write(String.format("i=%d eof=%b finished=%b",_i,_minusOne,_finished).getBytes(StandardCharsets.ISO_8859_1));
-                        async.complete();                        
-                    }
-                });
-            }
-        });
-
-        String request = "GET " + path + " HTTP/1.1\r\n" +
-                "Host: localhost:" + connector.getLocalPort() + "\r\n" +
-                "Content-Type: text/plain\r\n"+
-                "Content-Length: "+data.length+"\r\n" +
-                "Connection: close\r\n" +
-                "\r\n";
-
-        try (Socket client = new Socket("localhost", connector.getLocalPort()))
-        {
-            OutputStream output = client.getOutputStream();
-            output.write(request.getBytes("UTF-8"));
-            output.write(data);
-            output.flush();
-
-            BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
-            String line=in.readLine();
-            assertThat(line, containsString("200 OK"));
-            while (line.length()>0)
-                line=in.readLine();
-            line=in.readLine();
-            assertThat(line, containsString("i="+data.length+" eof=false finished=true"));
-        }
-    }
-    
-
-    @Test
-    public void testEmptyAsyncRead() throws Exception
-    {
-        final AtomicBoolean oda = new AtomicBoolean();
-        final CountDownLatch latch = new CountDownLatch(1);
-        
-        startServer(new HttpServlet()
-        {
-            @Override
-            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-            {
-                final AsyncContext asyncContext = request.startAsync(request, response);
-                response.setStatus(200);
-                response.getOutputStream().close();
-                request.getInputStream().setReadListener(new ReadListener()
-                {
-                    @Override
-                    public void onDataAvailable() throws IOException 
-                    {
-                        oda.set(true);
-                    }
-
-                    @Override
-                    public void onAllDataRead() throws IOException 
-                    {
-                        asyncContext.complete();
-                        latch.countDown();
-                    }
-
-                    @Override
-                    public void onError(Throwable t) 
-                    {
-                        t.printStackTrace();
-                        asyncContext.complete();
-                    }        
-                });
-            }
-        });
-
-        String request = "GET " + path + " HTTP/1.1\r\n" +
-                "Host: localhost:" + connector.getLocalPort() + "\r\n" +
-                "Connection: close\r\n" +
-                "\r\n";
-
-        try (Socket client = new Socket("localhost", connector.getLocalPort()))
-        {
-            OutputStream output = client.getOutputStream();
-            output.write(request.getBytes("UTF-8"));
-            output.flush();
-
-            String response = IO.toString(client.getInputStream());
-            assertThat(response,containsString(" 200 OK"));
-            // wait for onAllDataRead BEFORE closing client
-            latch.await();
-        }
-        
-        // ODA not called at all!
-        Assert.assertFalse(oda.get());
-    }
-
-}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncListenerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncListenerTest.java
new file mode 100644
index 0000000..518a04f
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncListenerTest.java
@@ -0,0 +1,253 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.servlet;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.HttpChannelState;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class AsyncListenerTest
+{
+    private QueuedThreadPool threadPool;
+    private Server server;
+    private LocalConnector connector;
+    private String servletPath;
+
+    private void start(HttpServlet servlet) throws Exception
+    {
+        server = threadPool == null ? new Server() : new Server(threadPool);
+        connector = new LocalConnector(server);
+        server.addConnector(connector);
+        ServletContextHandler context = new ServletContextHandler();
+        context.setContextPath("/");
+        ServletHolder holder = new ServletHolder(servlet);
+        holder.setAsyncSupported(true);
+        servletPath = "/async_listener";
+        context.addServlet(holder, servletPath + "/*");
+        server.setHandler(context);
+        server.start();
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        if (server != null)
+            server.stop();
+    }
+
+    @Test
+    public void testOnTimeoutCalledByPooledThread() throws Exception
+    {
+        String threadNamePrefix = "async_listener";
+        threadPool = new QueuedThreadPool();
+        threadPool.setName(threadNamePrefix);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                AsyncContext asyncContext = request.startAsync();
+                asyncContext.setTimeout(1000);
+                asyncContext.addListener(new AsyncListenerAdapter()
+                {
+                    @Override
+                    public void onTimeout(AsyncEvent event) throws IOException
+                    {
+                        if (Thread.currentThread().getName().startsWith(threadNamePrefix))
+                            response.setStatus(HttpStatus.OK_200);
+                        else
+                            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
+                        asyncContext.complete();
+                    }
+                });
+            }
+        });
+
+        HttpTester.Response response = HttpTester.parseResponse(connector.getResponse(newRequest("")));
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    @Test
+    public void testOnErrorCalledForExceptionAfterStartAsync() throws Exception
+    {
+        RuntimeException exception = new RuntimeException();
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                AsyncContext asyncContext = request.startAsync();
+                asyncContext.addListener(new AsyncListenerAdapter()
+                {
+                    @Override
+                    public void onError(AsyncEvent event) throws IOException
+                    {
+                        if (exception == event.getThrowable())
+                            response.setStatus(HttpStatus.OK_200);
+                        else
+                            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
+                        asyncContext.complete();
+                    }
+                });
+                throw exception;
+            }
+        });
+
+        try (StacklessLogging suppressor = new StacklessLogging(HttpChannel.class))
+        {
+            HttpTester.Response response = HttpTester.parseResponse(connector.getResponse(newRequest("")));
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+        }
+    }
+
+    @Test
+    public void testOnErrorCalledForExceptionThrownByOnTimeout() throws Exception
+    {
+        RuntimeException exception = new RuntimeException();
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                AsyncContext asyncContext = request.startAsync();
+                asyncContext.setTimeout(1000);
+                asyncContext.addListener(new AsyncListenerAdapter()
+                {
+                    @Override
+                    public void onTimeout(AsyncEvent event) throws IOException
+                    {
+                        throw exception;
+                    }
+
+                    @Override
+                    public void onError(AsyncEvent event) throws IOException
+                    {
+                        if (exception == event.getThrowable())
+                            response.setStatus(HttpStatus.OK_200);
+                        else
+                            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
+                        asyncContext.complete();
+                    }
+                });
+            }
+        });
+
+        HttpTester.Response response = HttpTester.parseResponse(connector.getResponse(newRequest("")));
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    @Test
+    public void testOnErrorNotCalledForExceptionThrownByOnComplete() throws Exception
+    {
+        CountDownLatch errorLatch = new CountDownLatch(1);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                AsyncContext asyncContext = request.startAsync();
+                asyncContext.addListener(new AsyncListenerAdapter()
+                {
+                    @Override
+                    public void onComplete(AsyncEvent event) throws IOException
+                    {
+                        // Way too late to handle this exception, should only be logged.
+                        throw new Error();
+                    }
+
+                    @Override
+                    public void onError(AsyncEvent event) throws IOException
+                    {
+                        errorLatch.countDown();
+                    }
+                });
+                new Thread(() ->
+                {
+                    try
+                    {
+                        Thread.sleep(1000);
+                        response.setStatus(HttpStatus.OK_200);
+                        asyncContext.complete();
+                    }
+                    catch (InterruptedException ignored)
+                    {
+                    }
+                }).start();
+            }
+        });
+
+        try (StacklessLogging suppressor = new StacklessLogging(HttpChannelState.class))
+        {
+            HttpTester.Response response = HttpTester.parseResponse(connector.getResponse(newRequest("")));
+            Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+            Assert.assertFalse(errorLatch.await(1, TimeUnit.SECONDS));
+        }
+    }
+
+    private String newRequest(String pathInfo)
+    {
+        return "" +
+                "GET " + servletPath + pathInfo + " HTTP/1.1\r\n" +
+                "Host: localhost\r\n" +
+                "\r\n";
+    }
+
+    private static class AsyncListenerAdapter implements AsyncListener
+    {
+        @Override
+        public void onTimeout(AsyncEvent event) throws IOException
+        {
+        }
+
+        @Override
+        public void onComplete(AsyncEvent event) throws IOException
+        {
+        }
+
+        @Override
+        public void onError(AsyncEvent event) throws IOException
+        {
+        }
+
+        @Override
+        public void onStartAsync(AsyncEvent event) throws IOException
+        {
+        }
+    }
+}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java
index 88dbba5..773593d 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java
@@ -18,6 +18,10 @@
 
 package org.eclipse.jetty.servlet;
 
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertEquals;
+
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
@@ -46,6 +50,7 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.DebugListener;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.Server;
@@ -57,10 +62,7 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.assertEquals;
-
-// TODO need  these on SPDY as well!
+// TODO need  these on HTTP2 as well!
 public class AsyncServletIOTest 
 {    
     private static final Logger LOG = Log.getLogger(AsyncServletIOTest.class);
@@ -82,6 +84,7 @@
         _server.setConnectors(new Connector[]{ _connector });
         ServletContextHandler context = new ServletContextHandler();
         context.setContextPath("/ctx");
+        context.addEventListener(new DebugListener());
         _server.setHandler(context);
         _servletHandler=context.getServletHandler();
         
@@ -223,7 +226,8 @@
         .append("Host: localhost\r\n")
         .append("Content-Type: text/plain\r\n")
         .append("Content-Length: 10\r\n")
-        .append("\r\n");
+        .append("\r\n")
+        .append("0");
         
         int port=_port;
         try (Socket socket = new Socket("localhost",port))
@@ -403,19 +407,18 @@
                         if (!onDataAvailable.compareAndSet(false,true))
                             throw new IllegalStateException();
                         
-                        // System.err.println("ODA");
+                        //System.err.println("ODA");
                         while (in.isReady() && !in.isFinished())
                         {
                             _oda.incrementAndGet();
                             int len=in.read(_buf);
-                            // System.err.println("read "+len);
+                            //System.err.println("read "+len);
                             if (len>0)
                                 _read.addAndGet(len);
                         }
 
                         if (!onDataAvailable.compareAndSet(true,false))
                             throw new IllegalStateException();
-                        
                     }
                     
                     @Override
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletLongPollTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletLongPollTest.java
index 2ff3f4b..9275557 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletLongPollTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletLongPollTest.java
@@ -18,9 +18,7 @@
 
 package org.eclipse.jetty.servlet;
 
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
@@ -33,11 +31,10 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpParser;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Rule;
@@ -129,7 +126,7 @@
 
             Assert.assertTrue(asyncLatch.await(5, TimeUnit.SECONDS));
 
-            String error = "408";
+            int error = 408;
             try (Socket socket2 = new Socket("localhost", connector.getLocalPort()))
             {
                 String request2 = "DELETE " + uri + "?error=" + error + " HTTP/1.1\r\n" +
@@ -139,17 +136,16 @@
                 output2.write(request2.getBytes(StandardCharsets.UTF_8));
                 output2.flush();
 
-                SimpleHttpParser parser2 = new SimpleHttpParser();
-                BufferedReader input2 = new BufferedReader(new InputStreamReader(socket2.getInputStream(), StandardCharsets.UTF_8));
-                SimpleHttpResponse response2 = parser2.readResponse(input2);
-                Assert.assertEquals("200", response2.getCode());
+                HttpTester.Input input2 = HttpTester.from(socket2.getInputStream());
+                HttpTester.Response response2 = HttpTester.parseResponse(input2);
+                Assert.assertEquals(200, response2.getStatus());
             }
 
             socket1.setSoTimeout(2 * wait);
-            SimpleHttpParser parser1 = new SimpleHttpParser();
-            BufferedReader input1 = new BufferedReader(new InputStreamReader(socket1.getInputStream(), StandardCharsets.UTF_8));
-            SimpleHttpResponse response1 = parser1.readResponse(input1);
-            Assert.assertEquals(error, response1.getCode());
+            
+            HttpTester.Input input1 = HttpTester.from(socket1.getInputStream());
+            HttpTester.Response response1 = HttpTester.parseResponse(input1);
+            Assert.assertEquals(error, response1.getStatus());
 
             // Now try to make another request on the first connection
             // to verify that we set correctly the read interest (#409842)
@@ -159,8 +155,8 @@
             output1.write(request3.getBytes(StandardCharsets.UTF_8));
             output1.flush();
 
-            SimpleHttpResponse response3 = parser1.readResponse(input1);
-            Assert.assertEquals("200", response3.getCode());
+            HttpTester.Response response3 = HttpTester.parseResponse(input1);
+            Assert.assertEquals(200, response3.getStatus());
         }
     }
 }
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java
index d6df520..0465d78 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java
@@ -18,7 +18,11 @@
 
 package org.eclipse.jetty.servlet;
 
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.startsWith;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -28,6 +32,9 @@
 import java.util.List;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 import javax.servlet.AsyncContext;
 import javax.servlet.AsyncEvent;
@@ -43,6 +50,8 @@
 import javax.servlet.http.HttpServletResponseWrapper;
 
 import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.DebugListener;
+import org.eclipse.jetty.server.QuietServletException;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.RequestLog;
 import org.eclipse.jetty.server.Response;
@@ -53,6 +62,7 @@
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.URIUtil;
 import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
@@ -73,13 +83,15 @@
     protected List<String> _log;
     protected int _expectedLogs;
     protected String _expectedCode;
+    protected static List<String> __history=new CopyOnWriteArrayList<>();
+    protected static CountDownLatch __latch;
 
     @Before
     public void setUp() throws Exception
     {
         _connector = new ServerConnector(_server);
         _server.setConnectors(new Connector[]{ _connector });
-        
+
         _log=new ArrayList<>();
         RequestLog log=new Log();
         RequestLogHandler logHandler = new RequestLogHandler();
@@ -91,7 +103,8 @@
         ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
         context.setContextPath("/ctx");
         logHandler.setHandler(context);
-        
+        context.addEventListener(new DebugListener());
+
         _servletHandler=context.getServletHandler();
         ServletHolder holder=new ServletHolder(_servlet);
         holder.setAsyncSupported(true);
@@ -100,291 +113,421 @@
         _servletHandler.addServletWithMapping(holder,"/path2/*");
         _servletHandler.addServletWithMapping(holder,"/p th3/*");
         _servletHandler.addServletWithMapping(new ServletHolder(new FwdServlet()),"/fwd/*");
+        ServletHolder holder2=new ServletHolder("NoAsync",_servlet);
+        holder2.setAsyncSupported(false);
+        _servletHandler.addServletWithMapping(holder2,"/noasync/*");
         _server.start();
         _port=_connector.getLocalPort();
+        __history.clear();
+        __latch=new CountDownLatch(1);
     }
 
     @After
     public void tearDown() throws Exception
     {
+        _server.stop();
         assertEquals(_expectedLogs,_log.size());
         Assert.assertThat(_log.get(0), Matchers.containsString(_expectedCode));
-        _server.stop();
     }
 
     @Test
     public void testNormal() throws Exception
     {
         String response=process(null,null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-                "history: REQUEST /ctx/path/info\r\n"+
-                "history: initial\r\n",response);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+                "REQUEST /ctx/path/info",
+                "initial"));
         assertContains("NORMAL",response);
-        assertNotContains("history: onTimeout",response);
-        assertNotContains("history: onComplete",response);
+        assertFalse(__history.contains("onTimeout"));
+        assertFalse(__history.contains("onComplete"));
     }
 
     @Test
     public void testSleep() throws Exception
     {
         String response=process("sleep=200",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-                "history: REQUEST /ctx/path/info\r\n"+
-                "history: initial\r\n",response);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+                "REQUEST /ctx/path/info",
+                "initial"));
         assertContains("SLEPT",response);
-        assertNotContains("history: onTimeout",response);
-        assertNotContains("history: onComplete",response);
+        assertFalse(__history.contains("onTimeout"));
+        assertFalse(__history.contains("onComplete"));
     }
 
     @Test
-    public void testSuspend() throws Exception
+    public void testNonAsync() throws Exception
+    {
+        String response=process("",null);
+        Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial"));
+
+        assertContains("NORMAL",response);
+    }
+
+    @Test
+    public void testAsyncNotSupportedNoAsync() throws Exception
+    {
+        _expectedCode="200 ";
+        String response=process("noasync","",null);
+        Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/noasync/info",
+            "initial"
+            ));
+
+        assertContains("NORMAL",response);
+    }
+    
+    @Test
+    public void testAsyncNotSupportedAsync() throws Exception
+    {
+        try (StacklessLogging stackless = new StacklessLogging(ServletHandler.class))
+        {
+            _expectedCode="500 ";
+            String response=process("noasync","start=200",null);
+            Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 500 "));
+            assertThat(__history,contains(
+                    "REQUEST /ctx/noasync/info",
+                    "initial"
+                    ));
+
+            assertContains("HTTP ERROR: 500",response);
+            assertContains("!asyncSupported",response);
+            assertContains("AsyncServletTest$AsyncServlet",response);
+        }
+    }
+
+    @Test
+    public void testStart() throws Exception
     {
         _expectedCode="500 ";
-        String response=process("suspend=200",null);
+        String response=process("start=200",null);
+        Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 500 Async Timeout"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "onTimeout",
+            "ERROR /ctx/path/info",
+            "!initial",
+            "onComplete"));
+
+        assertContains("ERROR DISPATCH: /ctx/path/info",response);
+    }
+
+    @Test
+    public void testStartOnTimeoutDispatch() throws Exception
+    {
+        String response=process("start=200&timeout=dispatch",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "onTimeout",
+            "dispatch",
+            "ASYNC /ctx/path/info",
+            "!initial",
+            "onComplete"));
+
+        assertContains("DISPATCHED",response);
+    }
+
+    @Test
+    public void testStartOnTimeoutError() throws Exception
+    {
+        _expectedCode="500 ";
+        String response=process("start=200&timeout=error",null);
+        assertThat(response,startsWith("HTTP/1.1 500 Server Error"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "onTimeout",
+            "error",
+            "onError",
+            "ERROR /ctx/path/info",
+            "!initial",
+            "onComplete"));
+
+        assertContains("ERROR DISPATCH",response);
+    }
+
+    @Test
+    public void testStartOnTimeoutErrorComplete() throws Exception
+    {
+        String response=process("start=200&timeout=error&error=complete",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "onTimeout",
+            "error",
+            "onError",
+            "complete",
+            "onComplete"));
+
+        assertContains("COMPLETED",response);
+    }
+
+    @Test
+    public void testStartOnTimeoutErrorDispatch() throws Exception
+    {
+        String response=process("start=200&timeout=error&error=dispatch",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "onTimeout",
+            "error",
+            "onError",
+            "dispatch",
+            "ASYNC /ctx/path/info",
+            "!initial",
+            "onComplete"));
+
+        assertContains("DISPATCHED",response);
+    }
+
+    @Test
+    public void testStartOnTimeoutComplete() throws Exception
+    {
+        String response=process("start=200&timeout=complete",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "onTimeout",
+            "complete",
+            "onComplete"));
+
+        assertContains("COMPLETED",response);
+    }
+
+    @Test
+    public void testStartWaitDispatch() throws Exception
+    {
+        String response=process("start=200&dispatch=10",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "dispatch",
+            "ASYNC /ctx/path/info",
+            "!initial",
+            "onComplete"));
+        assertFalse(__history.contains("onTimeout"));
+    }
+
+    @Test
+    public void testStartDispatch() throws Exception
+    {
+        String response=process("start=200&dispatch=0",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "dispatch",
+            "ASYNC /ctx/path/info",
+            "!initial",
+            "onComplete"));
+    }
+
+    @Test
+    public void testStartError() throws Exception
+    {
+        _expectedCode="500 ";
+        String response=process("start=200&throw=1",null);
+        assertThat(response,startsWith("HTTP/1.1 500 Server Error"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "onError",
+            "ERROR /ctx/path/info",
+            "!initial",
+            "onComplete"));
+        assertContains("ERROR DISPATCH: /ctx/path/info",response);
+    }
+
+    @Test
+    public void testStartWaitComplete() throws Exception
+    {
+        String response=process("start=200&complete=50",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "complete",
+            "onComplete"));
+        assertContains("COMPLETED",response);
+        assertFalse(__history.contains("onTimeout"));
+        assertFalse(__history.contains("!initial"));
+    }
+
+    @Test
+    public void testStartComplete() throws Exception
+    {
+        String response=process("start=200&complete=0",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "complete",
+            "onComplete"));
+        assertContains("COMPLETED",response);
+        assertFalse(__history.contains("onTimeout"));
+        assertFalse(__history.contains("!initial"));
+    }
+
+    @Test
+    public void testStartWaitDispatchStartWaitDispatch() throws Exception
+    {
+        String response=process("start=1000&dispatch=10&start2=1000&dispatch2=10",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "dispatch",
+            "ASYNC /ctx/path/info",
+            "!initial",
+            "onStartAsync",
+            "start",
+            "dispatch",
+            "ASYNC /ctx/path/info",
+            "!initial",
+            "onComplete"));
+        assertContains("DISPATCHED",response);
+    }
+
+    @Test
+    public void testStartWaitDispatchStartComplete() throws Exception
+    {
+        String response=process("start=1000&dispatch=10&start2=1000&complete2=10",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "dispatch",
+            "ASYNC /ctx/path/info",
+            "!initial",
+            "onStartAsync",
+            "start",
+            "complete",
+            "onComplete"));
+        assertContains("COMPLETED",response);
+    }
+
+    @Test
+    public void testStartWaitDispatchStart() throws Exception
+    {
+        _expectedCode="500 ";
+        String response=process("start=1000&dispatch=10&start2=10",null);
         assertEquals("HTTP/1.1 500 Async Timeout",response.substring(0,26));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: onTimeout\r\n"+
-            "history: ERROR /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
-
-        assertContains("ERROR: /ctx/path/info",response);
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "dispatch",
+            "ASYNC /ctx/path/info",
+            "!initial",
+            "onStartAsync",
+            "start",
+            "onTimeout",
+            "ERROR /ctx/path/info",
+            "!initial",
+            "onComplete"));
+        assertContains("ERROR DISPATCH: /ctx/path/info",response);
     }
 
     @Test
-    public void testSuspendOnTimeoutDispatch() throws Exception
+    public void testStartTimeoutStartDispatch() throws Exception
     {
-        String response=process("suspend=200&timeout=dispatch",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: onTimeout\r\n"+
-            "history: dispatch\r\n"+
-            "history: ASYNC /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
-
+        String response=process("start=10&start2=1000&dispatch2=10",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "onTimeout",
+            "ERROR /ctx/path/info",
+            "!initial",
+            "onStartAsync",
+            "start",
+            "dispatch",
+            "ASYNC /ctx/path/info",
+            "!initial",
+            "onComplete"));
         assertContains("DISPATCHED",response);
     }
 
     @Test
-    public void testSuspendOnTimeoutComplete() throws Exception
+    public void testStartTimeoutStartComplete() throws Exception
     {
-        String response=process("suspend=200&timeout=complete",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: onTimeout\r\n"+
-            "history: complete\r\n"+
-            "history: onComplete\r\n",response);
-
+        String response=process("start=10&start2=1000&complete2=10",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "onTimeout",
+            "ERROR /ctx/path/info",
+            "!initial",
+            "onStartAsync",
+            "start",
+            "complete",
+            "onComplete"));
         assertContains("COMPLETED",response);
     }
 
     @Test
-    public void testSuspendWaitResume() throws Exception
-    {
-        String response=process("suspend=200&resume=10",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: ASYNC /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
-        assertNotContains("history: onTimeout",response);
-    }
-
-    @Test
-    public void testSuspendResume() throws Exception
-    {
-        String response=process("suspend=200&resume=0",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: ASYNC /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
-        assertContains("history: onComplete",response);
-    }
-
-    @Test
-    public void testSuspendWaitComplete() throws Exception
-    {
-        String response=process("suspend=200&complete=50",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: complete\r\n"+
-            "history: onComplete\r\n",response);
-        assertContains("COMPLETED",response);
-        assertNotContains("history: onTimeout",response);
-        assertNotContains("history: !initial",response);
-    }
-
-    @Test
-    public void testSuspendComplete() throws Exception
-    {
-        String response=process("suspend=200&complete=0",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: complete\r\n"+
-            "history: onComplete\r\n",response);
-        assertContains("COMPLETED",response);
-        assertNotContains("history: onTimeout",response);
-        assertNotContains("history: !initial",response);
-    }
-
-    @Test
-    public void testSuspendWaitResumeSuspendWaitResume() throws Exception
-    {
-        String response=process("suspend=1000&resume=10&suspend2=1000&resume2=10",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: ASYNC /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: ASYNC /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
-        assertContains("DISPATCHED",response);
-    }
-
-    @Test
-    public void testSuspendWaitResumeSuspendComplete() throws Exception
-    {
-        String response=process("suspend=1000&resume=10&suspend2=1000&complete2=10",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: ASYNC /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: suspend\r\n"+
-            "history: complete\r\n"+
-            "history: onComplete\r\n",response);
-        assertContains("COMPLETED",response);
-    }
-
-    @Test
-    public void testSuspendWaitResumeSuspend() throws Exception
+    public void testStartTimeoutStart() throws Exception
     {
         _expectedCode="500 ";
-        String response=process("suspend=1000&resume=10&suspend2=10",null);
-        assertEquals("HTTP/1.1 500 Async Timeout",response.substring(0,26));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: ASYNC /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: suspend\r\n"+
-            "history: onTimeout\r\n"+
-            "history: ERROR /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
-        assertContains("ERROR: /ctx/path/info",response);
-    }
-
-    @Test
-    public void testSuspendTimeoutSuspendResume() throws Exception
-    {
-        String response=process("suspend=10&suspend2=1000&resume2=10",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: onTimeout\r\n"+
-            "history: ERROR /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: ASYNC /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
-        assertContains("DISPATCHED",response);
-    }
-
-    @Test
-    public void testSuspendTimeoutSuspendComplete() throws Exception
-    {
-        String response=process("suspend=10&suspend2=1000&complete2=10",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: onTimeout\r\n"+
-            "history: ERROR /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: suspend\r\n"+
-            "history: complete\r\n"+
-            "history: onComplete\r\n",response);
-        assertContains("COMPLETED",response);
-    }
-
-    @Test
-    public void testSuspendTimeoutSuspend() throws Exception
-    {
-        _expectedCode="500 ";
-        String response=process("suspend=10&suspend2=10",null);
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: onTimeout\r\n"+
-            "history: ERROR /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: suspend\r\n"+
-            "history: onTimeout\r\n"+
-            "history: ERROR /ctx/path/info\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
-        assertContains("ERROR: /ctx/path/info",response);
+        String response=process("start=10&start2=10",null);
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "onTimeout",
+            "ERROR /ctx/path/info",
+            "!initial",
+            "onStartAsync",
+            "start",
+            "onTimeout",
+            "ERROR /ctx/path/info",
+            "!initial",
+            "onComplete"));
+        assertContains("ERROR DISPATCH: /ctx/path/info",response);
     }
 
     @Test
     public void testWrapStartDispatch() throws Exception
     {
-        String response=process("wrap=true&suspend=200&resume=20",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: ASYNC /ctx/path/info\r\n"+
-            "history: wrapped REQ RSP\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
+        String response=process("wrap=true&start=200&dispatch=20",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "dispatch",
+            "ASYNC /ctx/path/info",
+            "wrapped REQ RSP",
+            "!initial",
+            "onComplete"));
         assertContains("DISPATCHED",response);
     }
 
@@ -392,127 +535,122 @@
     @Test
     public void testStartDispatchEncodedPath() throws Exception
     {
-        String response=process("suspend=200&resume=20&path=/p%20th3",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: REQUEST /ctx/path/info\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: ASYNC /ctx/p%20th3\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
+        String response=process("start=200&dispatch=20&path=/p%20th3",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "REQUEST /ctx/path/info",
+            "initial",
+            "start",
+            "dispatch",
+            "ASYNC /ctx/p%20th3",
+            "!initial",
+            "onComplete"));
         assertContains("DISPATCHED",response);
     }
-    
-    
+
+
     @Test
     public void testFwdStartDispatch() throws Exception
     {
-        String response=process("fwd","suspend=200&resume=20",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: FWD REQUEST /ctx/fwd/info\r\n"+
-            "history: FORWARD /ctx/path1\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: FWD ASYNC /ctx/fwd/info\r\n"+
-            "history: FORWARD /ctx/path1\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
+        String response=process("fwd","start=200&dispatch=20",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "FWD REQUEST /ctx/fwd/info",
+            "FORWARD /ctx/path1",
+            "initial",
+            "start",
+            "dispatch",
+            "FWD ASYNC /ctx/fwd/info",
+            "FORWARD /ctx/path1",
+            "!initial",
+            "onComplete"));
         assertContains("DISPATCHED",response);
     }
-    
+
     @Test
     public void testFwdStartDispatchPath() throws Exception
     {
-        String response=process("fwd","suspend=200&resume=20&path=/path2",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: FWD REQUEST /ctx/fwd/info\r\n"+
-            "history: FORWARD /ctx/path1\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: ASYNC /ctx/path2\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
+        String response=process("fwd","start=200&dispatch=20&path=/path2",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "FWD REQUEST /ctx/fwd/info",
+            "FORWARD /ctx/path1",
+            "initial",
+            "start",
+            "dispatch",
+            "ASYNC /ctx/path2",
+            "!initial",
+            "onComplete"));
         assertContains("DISPATCHED",response);
     }
 
     @Test
     public void testFwdWrapStartDispatch() throws Exception
     {
-        String response=process("fwd","wrap=true&suspend=200&resume=20",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: FWD REQUEST /ctx/fwd/info\r\n"+
-            "history: FORWARD /ctx/path1\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: ASYNC /ctx/path1\r\n"+
-            "history: wrapped REQ RSP\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
+        String response=process("fwd","wrap=true&start=200&dispatch=20",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "FWD REQUEST /ctx/fwd/info",
+            "FORWARD /ctx/path1",
+            "initial",
+            "start",
+            "dispatch",
+            "ASYNC /ctx/path1",
+            "wrapped REQ RSP",
+            "!initial",
+            "onComplete"));
         assertContains("DISPATCHED",response);
     }
-    
+
     @Test
     public void testFwdWrapStartDispatchPath() throws Exception
     {
-        String response=process("fwd","wrap=true&suspend=200&resume=20&path=/path2",null);
-        assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-        assertContains(
-            "history: FWD REQUEST /ctx/fwd/info\r\n"+
-            "history: FORWARD /ctx/path1\r\n"+
-            "history: initial\r\n"+
-            "history: suspend\r\n"+
-            "history: resume\r\n"+
-            "history: ASYNC /ctx/path2\r\n"+
-            "history: wrapped REQ RSP\r\n"+
-            "history: !initial\r\n"+
-            "history: onComplete\r\n",response);
+        String response=process("fwd","wrap=true&start=200&dispatch=20&path=/path2",null);
+        assertThat(response,startsWith("HTTP/1.1 200 OK"));
+        assertThat(__history,contains(
+            "FWD REQUEST /ctx/fwd/info",
+            "FORWARD /ctx/path1",
+            "initial",
+            "start",
+            "dispatch",
+            "ASYNC /ctx/path2",
+            "wrapped REQ RSP",
+            "!initial",
+            "onComplete"));
         assertContains("DISPATCHED",response);
     }
-    
-    
+
+
     @Test
     public void testAsyncRead() throws Exception
     {
-        _expectedLogs=2;
-        String header="GET /ctx/path/info?suspend=2000&resume=1500 HTTP/1.1\r\n"+
+        String header="GET /ctx/path/info?start=2000&dispatch=1500 HTTP/1.1\r\n"+
             "Host: localhost\r\n"+
             "Content-Length: 10\r\n"+
-            "\r\n";
-        String body="12345678\r\n";
-        String close="GET /ctx/path/info HTTP/1.1\r\n"+
-            "Host: localhost\r\n"+
             "Connection: close\r\n"+
             "\r\n";
+        String body="12345678\r\n";
 
         try (Socket socket = new Socket("localhost",_port))
         {
             socket.setSoTimeout(10000);
             socket.getOutputStream().write(header.getBytes(StandardCharsets.ISO_8859_1));
-            Thread.sleep(500);
             socket.getOutputStream().write(body.getBytes(StandardCharsets.ISO_8859_1),0,2);
             Thread.sleep(500);
             socket.getOutputStream().write(body.getBytes(StandardCharsets.ISO_8859_1),2,8);
-            socket.getOutputStream().write(close.getBytes(StandardCharsets.ISO_8859_1));
 
             String response = IO.toString(socket.getInputStream());
-            assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
-            assertContains(
-                "history: REQUEST /ctx/path/info\r\n"+
-                "history: initial\r\n"+
-                "history: suspend\r\n"+
-                "history: async-read=10\r\n"+
-                "history: resume\r\n"+
-                "history: ASYNC /ctx/path/info\r\n"+
-                "history: !initial\r\n"+
-                "history: onComplete\r\n",response);
+            __latch.await(1,TimeUnit.SECONDS);
+            assertThat(response,startsWith("HTTP/1.1 200 OK"));
+            assertThat(__history,contains(
+                "REQUEST /ctx/path/info",
+                "initial",
+                "start",
+                "async-read=10",
+                "dispatch",
+                "ASYNC /ctx/path/info",
+                "!initial",
+                "onComplete"));
         }
     }
 
@@ -520,7 +658,7 @@
     {
         return process("path",query,content);
     }
-    
+
     public synchronized String process(String path,String query,String content) throws Exception
     {
         String request = "GET /ctx/"+path+"/info";
@@ -543,7 +681,10 @@
         {
             socket.setSoTimeout(1000000);
             socket.getOutputStream().write(request.getBytes(StandardCharsets.UTF_8));
-            return IO.toString(socket.getInputStream());
+            socket.getOutputStream().flush();
+            String response = IO.toString(socket.getInputStream());
+            __latch.await(1,TimeUnit.SECONDS);
+            return response;
         }
         catch(Exception e)
         {
@@ -551,6 +692,7 @@
             e.printStackTrace();
             throw e;
         }
+        
     }
 
     protected void assertContains(String content,String response)
@@ -568,13 +710,13 @@
         @Override
         public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
         {
-            response.addHeader("history","FWD "+request.getDispatcherType()+" "+request.getRequestURI());
+            __history.add("FWD "+request.getDispatcherType()+" "+request.getRequestURI());
             if (request instanceof ServletRequestWrapper || response instanceof ServletResponseWrapper)
-                response.addHeader("history","wrapped"+((request instanceof ServletRequestWrapper)?" REQ":"")+((response instanceof ServletResponseWrapper)?" RSP":""));
+                __history.add("wrapped"+((request instanceof ServletRequestWrapper)?" REQ":"")+((response instanceof ServletResponseWrapper)?" RSP":""));
             request.getServletContext().getRequestDispatcher("/path1").forward(request,response);
         }
     }
-    
+
     private static class AsyncServlet extends HttpServlet
     {
         private static final long serialVersionUID = -8161977157098646562L;
@@ -593,36 +735,36 @@
             {
                 // ignored
             }
-            
+
             // System.err.println(request.getDispatcherType()+" "+request.getRequestURI());
-            response.addHeader("history",request.getDispatcherType()+" "+request.getRequestURI());
+            __history.add(request.getDispatcherType()+" "+request.getRequestURI());
             if (request instanceof ServletRequestWrapper || response instanceof ServletResponseWrapper)
-                response.addHeader("history","wrapped"+((request instanceof ServletRequestWrapper)?" REQ":"")+((response instanceof ServletResponseWrapper)?" RSP":""));
-                
+                __history.add("wrapped"+((request instanceof ServletRequestWrapper)?" REQ":"")+((response instanceof ServletResponseWrapper)?" RSP":""));
+
             boolean wrap="true".equals(request.getParameter("wrap"));
             int read_before=0;
             long sleep_for=-1;
-            long suspend_for=-1;
-            long suspend2_for=-1;
-            long resume_after=-1;
-            long resume2_after=-1;
+            long start_for=-1;
+            long start2_for=-1;
+            long dispatch_after=-1;
+            long dispatch2_after=-1;
             long complete_after=-1;
             long complete2_after=-1;
 
-            
+
             if (request.getParameter("read")!=null)
                 read_before=Integer.parseInt(request.getParameter("read"));
             if (request.getParameter("sleep")!=null)
                 sleep_for=Integer.parseInt(request.getParameter("sleep"));
-            if (request.getParameter("suspend")!=null)
-                suspend_for=Integer.parseInt(request.getParameter("suspend"));
-            if (request.getParameter("suspend2")!=null)
-                suspend2_for=Integer.parseInt(request.getParameter("suspend2"));
-            if (request.getParameter("resume")!=null)
-                resume_after=Integer.parseInt(request.getParameter("resume"));
+            if (request.getParameter("start")!=null)
+                start_for=Integer.parseInt(request.getParameter("start"));
+            if (request.getParameter("start2")!=null)
+                start2_for=Integer.parseInt(request.getParameter("start2"));
+            if (request.getParameter("dispatch")!=null)
+                dispatch_after=Integer.parseInt(request.getParameter("dispatch"));
             final String path=request.getParameter("path");
-            if (request.getParameter("resume2")!=null)
-                resume2_after=Integer.parseInt(request.getParameter("resume2"));
+            if (request.getParameter("dispatch2")!=null)
+                dispatch2_after=Integer.parseInt(request.getParameter("dispatch2"));
             if (request.getParameter("complete")!=null)
                 complete_after=Integer.parseInt(request.getParameter("complete"));
             if (request.getParameter("complete2")!=null)
@@ -631,7 +773,7 @@
             if (request.getAttribute("State")==null)
             {
                 request.setAttribute("State",new Integer(1));
-                response.addHeader("history","initial");
+                __history.add("initial");
                 if (read_before>0)
                 {
                     byte[] buf=new byte[read_before];
@@ -659,7 +801,7 @@
                                 while(b!=-1)
                                     if((b=in.read())>=0)
                                         c++;
-                                response.addHeader("history","async-read="+c);
+                                __history.add("async-read="+c);
                             }
                             catch(Exception e)
                             {
@@ -669,13 +811,16 @@
                     }.start();
                 }
 
-                if (suspend_for>=0)
+                if (start_for>=0)
                 {
                     final AsyncContext async=wrap?request.startAsync(new HttpServletRequestWrapper(request),new HttpServletResponseWrapper(response)):request.startAsync();
-                    if (suspend_for>0)
-                        async.setTimeout(suspend_for);
+                    if (start_for>0)
+                        async.setTimeout(start_for);
                     async.addListener(__listener);
-                    response.addHeader("history","suspend");
+                    __history.add("start");
+
+                    if ("1".equals(request.getParameter("throw")))
+                        throw new QuietServletException(new Exception("test throw in async 1"));
 
                     if (complete_after>0)
                     {
@@ -688,7 +833,7 @@
                                 {
                                     response.setStatus(200);
                                     response.getOutputStream().println("COMPLETED\n");
-                                    response.addHeader("history","complete");
+                                    __history.add("complete");
                                     async.complete();
                                 }
                                 catch(Exception e)
@@ -706,18 +851,18 @@
                     {
                         response.setStatus(200);
                         response.getOutputStream().println("COMPLETED\n");
-                        response.addHeader("history","complete");
+                        __history.add("complete");
                         async.complete();
                     }
-                    else if (resume_after>0)
+                    else if (dispatch_after>0)
                     {
-                        TimerTask resume = new TimerTask()
+                        TimerTask dispatch = new TimerTask()
                         {
                             @Override
                             public void run()
                             {
-                                ((HttpServletResponse)async.getResponse()).addHeader("history","resume");
-                                if (path!=null)             
+                                __history.add("dispatch");
+                                if (path!=null)
                                 {
                                     int q=path.indexOf('?');
                                     String uriInContext=(q>=0)
@@ -731,13 +876,13 @@
                         };
                         synchronized (_timer)
                         {
-                            _timer.schedule(resume,resume_after);
+                            _timer.schedule(dispatch,dispatch_after);
                         }
                     }
-                    else if (resume_after==0)
+                    else if (dispatch_after==0)
                     {
-                        ((HttpServletResponse)async.getResponse()).addHeader("history","resume");
-                        if (path!=null)             
+                        __history.add("dispatch");
+                        if (path!=null)
                             async.dispatch(path);
                         else
                             async.dispatch();
@@ -765,20 +910,22 @@
             }
             else
             {
-                response.addHeader("history","!initial");
+                __history.add("!initial");
 
-                if (suspend2_for>=0 && request.getAttribute("2nd")==null)
+                if (start2_for>=0 && request.getAttribute("2nd")==null)
                 {
                     final AsyncContext async=wrap?request.startAsync(new HttpServletRequestWrapper(request),new HttpServletResponseWrapper(response)):request.startAsync();
                     async.addListener(__listener);
                     request.setAttribute("2nd","cycle");
 
-                    if (suspend2_for>0)
+                    if (start2_for>0)
                     {
-                        async.setTimeout(suspend2_for);
+                        async.setTimeout(start2_for);
                     }
-                    // continuation.addContinuationListener(__listener);
-                    response.addHeader("history","suspend");
+                    __history.add("start");
+
+                    if ("2".equals(request.getParameter("throw")))
+                        throw new QuietServletException(new Exception("test throw in async 2"));
 
                     if (complete2_after>0)
                     {
@@ -791,7 +938,7 @@
                                 {
                                     response.setStatus(200);
                                     response.getOutputStream().println("COMPLETED\n");
-                                    response.addHeader("history","complete");
+                                    __history.add("complete");
                                     async.complete();
                                 }
                                 catch(Exception e)
@@ -809,34 +956,34 @@
                     {
                         response.setStatus(200);
                         response.getOutputStream().println("COMPLETED\n");
-                        response.addHeader("history","complete");
+                        __history.add("complete");
                         async.complete();
                     }
-                    else if (resume2_after>0)
+                    else if (dispatch2_after>0)
                     {
-                        TimerTask resume = new TimerTask()
+                        TimerTask dispatch = new TimerTask()
                         {
                             @Override
                             public void run()
                             {
-                                response.addHeader("history","resume");
+                                __history.add("dispatch");
                                 async.dispatch();
                             }
                         };
                         synchronized (_timer)
                         {
-                            _timer.schedule(resume,resume2_after);
+                            _timer.schedule(dispatch,dispatch2_after);
                         }
                     }
-                    else if (resume2_after==0)
+                    else if (dispatch2_after==0)
                     {
-                        response.addHeader("history","dispatch");
+                        __history.add("dispatch");
                         async.dispatch();
                     }
                 }
                 else if(request.getDispatcherType()==DispatcherType.ERROR)
                 {
-                    response.getOutputStream().println("ERROR: "+request.getContextPath()+request.getServletPath()+request.getPathInfo());
+                    response.getOutputStream().println("ERROR DISPATCH: "+request.getContextPath()+request.getServletPath()+request.getPathInfo());
                 }
                 else
                 {
@@ -853,17 +1000,25 @@
         @Override
         public void onTimeout(AsyncEvent event) throws IOException
         {
-            ((HttpServletResponse)event.getSuppliedResponse()).addHeader("history","onTimeout");
+            __history.add("onTimeout");
             String action=event.getSuppliedRequest().getParameter("timeout");
             if (action!=null)
             {
-                ((HttpServletResponse)event.getSuppliedResponse()).addHeader("history",action);
-                if ("dispatch".equals(action))
-                    event.getAsyncContext().dispatch();
-                if ("complete".equals(action))
+                __history.add(action);
+
+                switch(action)
                 {
-                    event.getSuppliedResponse().getOutputStream().println("COMPLETED\n");
-                    event.getAsyncContext().complete();
+                    case "dispatch":
+                        event.getAsyncContext().dispatch();
+                        break;
+
+                    case "complete":
+                        event.getSuppliedResponse().getOutputStream().println("COMPLETED\n");
+                        event.getAsyncContext().complete();
+                        break;
+
+                    case "error":
+                        throw new RuntimeException("error in onTimeout");
                 }
             }
         }
@@ -871,17 +1026,37 @@
         @Override
         public void onStartAsync(AsyncEvent event) throws IOException
         {
+            __history.add("onStartAsync");
         }
 
         @Override
         public void onError(AsyncEvent event) throws IOException
         {
+            __history.add("onError");
+            String action=event.getSuppliedRequest().getParameter("error");
+            if (action!=null)
+            {
+                __history.add(action);
+
+                switch(action)
+                {
+                    case "dispatch":
+                        event.getAsyncContext().dispatch();
+                        break;
+
+                    case "complete":
+                        event.getSuppliedResponse().getOutputStream().println("COMPLETED\n");
+                        event.getAsyncContext().complete();
+                        break;
+                }
+            }
         }
 
         @Override
         public void onComplete(AsyncEvent event) throws IOException
         {
-            ((HttpServletResponse)event.getSuppliedResponse()).addHeader("history","onComplete");
+            __history.add("onComplete");
+            __latch.countDown();
         }
     };
 
@@ -889,8 +1064,10 @@
     {
         @Override
         public void log(Request request, Response response)
-        {            
-            _log.add(response.getStatus()+" "+response.getContentCount()+" "+request.getRequestURI());
+        {
+            int status = response.getCommittedMetaData().getStatus();
+            long written = response.getHttpChannel().getBytesWritten();
+            _log.add(status+" "+written+" "+request.getRequestURI());
         }
     }
 }
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ComplianceViolations2616Test.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ComplianceViolations2616Test.java
new file mode 100644
index 0000000..5e94397
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ComplianceViolations2616Test.java
@@ -0,0 +1,191 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpCompliance;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Server;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+
+public class ComplianceViolations2616Test
+{
+    private static Server server;
+    private static LocalConnector connector;
+
+    public static class ReportViolationsFilter implements Filter
+    {
+        @Override
+        public void init(FilterConfig filterConfig) throws ServletException
+        {
+        }
+
+        @Override
+        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+        {
+            if (request instanceof HttpServletRequest)
+            {
+                List<String> violations = (List<String>) request.getAttribute("org.eclipse.jetty.http.compliance.violations");
+                if (violations != null)
+                {
+                    HttpServletResponse httpResponse = (HttpServletResponse) response;
+                    int i = 0;
+                    for (String violation : violations)
+                    {
+                        httpResponse.setHeader("X-Http-Violation-" + (i++), violation);
+                    }
+                }
+            }
+            chain.doFilter(request, response);
+        }
+
+        @Override
+        public void destroy()
+        {
+        }
+    }
+
+    public static class DumpRequestHeadersServlet extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            resp.setContentType("text/plain");
+            PrintWriter out = resp.getWriter();
+            List<String> headerNames = new ArrayList<>();
+            headerNames.addAll(Collections.list(req.getHeaderNames()));
+            Collections.sort(headerNames);
+            for (String name : headerNames)
+            {
+                out.printf("[%s] = [%s]%n", name, req.getHeader(name));
+            }
+        }
+    }
+
+    @BeforeClass
+    public static void startServer() throws Exception
+    {
+        server = new Server();
+
+        HttpConfiguration config = new HttpConfiguration();
+        config.setSendServerVersion(false);
+
+        HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(config, HttpCompliance.RFC2616);
+        httpConnectionFactory.setRecordHttpComplianceViolations(true);
+        connector = new LocalConnector(server, null, null, null, -1, httpConnectionFactory);
+
+        ServletContextHandler context = new ServletContextHandler();
+        context.setContextPath("/");
+        context.setWelcomeFiles(new String[]{"index.html", "index.jsp", "index.htm"});
+
+        context.addServlet(DumpRequestHeadersServlet.class, "/dump/*");
+        context.addFilter(ReportViolationsFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
+
+        server.setHandler(context);
+        server.addConnector(connector);
+
+        server.start();
+    }
+
+    @AfterClass
+    public static void stopServer() throws Exception
+    {
+        server.stop();
+        server.join();
+    }
+
+    @Test
+    public void testNoColonHeader_Middle() throws Exception
+    {
+        StringBuffer req1 = new StringBuffer();
+        req1.append("GET /dump/ HTTP/1.1\r\n");
+        req1.append("Name\r\n");
+        req1.append("Host: local\r\n");
+        req1.append("Accept: */*\r\n");
+        req1.append("Connection: close\r\n");
+        req1.append("\r\n");
+
+        String response = connector.getResponses(req1.toString());
+        assertThat("Response status", response, containsString("HTTP/1.1 200 OK"));
+        assertThat("Response headers", response, containsString("X-Http-Violation-0: RFC2616<RFC7230: name only header"));
+
+        assertThat("Response body", response, containsString("[Name] = []"));
+    }
+
+    @Test
+    public void testNoColonHeader_End() throws Exception
+    {
+        StringBuffer req1 = new StringBuffer();
+        req1.append("GET /dump/ HTTP/1.1\r\n");
+        req1.append("Host: local\r\n");
+        req1.append("Connection: close\r\n");
+        req1.append("Accept: */*\r\n");
+        req1.append("Name\r\n");
+        req1.append("\r\n");
+
+        String response = connector.getResponses(req1.toString());
+        assertThat("Response status", response, containsString("HTTP/1.1 200"));
+        assertThat("Response headers", response, containsString("X-Http-Violation-0: RFC2616<RFC7230: name only header"));
+
+        assertThat("Response body", response, containsString("[Name] = []"));
+    }
+
+    @Test
+    public void testFoldedHeader() throws Exception
+    {
+        StringBuffer req1 = new StringBuffer();
+        req1.append("GET /dump/ HTTP/1.1\r\n");
+        req1.append("Host: local\r\n");
+        req1.append("Name: Some\r\n");
+        req1.append(" Value\r\n");
+        req1.append("Connection: close\r\n");
+        req1.append("Accept: */*\r\n");
+        req1.append("\r\n");
+
+        String response = connector.getResponses(req1.toString());
+        assertThat("Response status", response, containsString("HTTP/1.1 200"));
+        assertThat("Response headers", response, containsString("X-Http-Violation-0: RFC2616<RFC7230: header folding"));
+
+        assertThat("Response body", response, containsString("[Name] = [Some Value]"));
+    }
+}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletRangesTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletRangesTest.java
index 0a56e66..d2881a9 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletRangesTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletRangesTest.java
@@ -67,7 +67,7 @@
 
 
         testdir.ensureEmpty();
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         FS.ensureDirExists(resBase);
         File data = new File(resBase, "data.txt");
         createFile(data, DATA);
@@ -208,7 +208,7 @@
                 "Connection: close\r\n"+
                 "Range: bytes=100-110\r\n" +
                 "\r\n");
-        assertResponseContains("416 Requested Range Not Satisfiable", response);
+        assertResponseContains("416 Range Not Satisfiable", response);
     }
 
 
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java
index 98569c7..072f6c7 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java
@@ -23,6 +23,7 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.util.EnumSet;
@@ -38,15 +39,18 @@
 import javax.servlet.ServletResponse;
 
 import org.eclipse.jetty.http.DateGenerator;
+import org.eclipse.jetty.http.HttpContent;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.ResourceContentFactory;
 import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker;
 import org.eclipse.jetty.toolchain.test.FS;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.toolchain.test.OS;
 import org.eclipse.jetty.toolchain.test.TestingDir;
 import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.resource.Resource;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
@@ -99,7 +103,7 @@
         testdir.ensureEmpty();
 
         /* create some content in the docroot */
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         assertTrue(resBase.mkdirs());
         assertTrue(new File(resBase, "one").mkdir());
         assertTrue(new File(resBase, "two").mkdir());
@@ -131,7 +135,7 @@
         testdir.ensureEmpty();
 
         /* create some content in the docroot */
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         FS.ensureDirExists(resBase);
         assertTrue(new File(resBase, "one").mkdir());
         assertTrue(new File(resBase, "two").mkdir());
@@ -154,14 +158,6 @@
 
         String response = connector.getResponses(req1.toString());
 
-        assertResponseContains("/one/", response);
-        assertResponseContains("/two/", response);
-        assertResponseContains("/three/", response);
-        if (!OS.IS_WINDOWS)
-        {
-            assertResponseContains("/f%3F%3Fr", response);
-        }
-
         assertResponseNotContains("<script>", response);
     }
 
@@ -176,7 +172,7 @@
         testdir.ensureEmpty();
 
         /* create some content in the docroot */
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         assertTrue(resBase.mkdirs());
         File wackyDir = new File(resBase, "dir;"); // this should not be double-encoded.
         assertTrue(wackyDir.mkdirs());
@@ -228,7 +224,7 @@
         testdir.ensureEmpty();
 
         /* create some content in the docroot */
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         assertTrue(resBase.mkdirs());
 
         File index = new File(resBase, "index.html");
@@ -244,7 +240,7 @@
         assertTrue(wackyDir.mkdirs());
 
         /* create some content outside of the docroot */
-        File sekret = testdir.getFile("sekret");
+        File sekret = testdir.getPathFile("sekret").toFile();
         assertTrue(sekret.mkdirs());
         File pass = new File(sekret, "pass");
         createFile(pass, "Sssh, you shouldn't be seeing this");
@@ -328,13 +324,15 @@
     public void testWelcome() throws Exception
     {
         testdir.ensureEmpty();
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         FS.ensureDirExists(resBase);
-        File inde = new File(resBase, "index.htm");
-        File index = new File(resBase, "index.html");
+
+        File dir = new File(resBase, "dir");
+        assertTrue(dir.mkdirs());
+        File inde = new File(dir, "index.htm");
+        File index = new File(dir, "index.html");
 
         String resBasePath = resBase.getAbsolutePath();
-
         ServletHolder defholder = context.addServlet(DefaultServlet.class, "/");
         defholder.setInitParameter("dirAllowed", "false");
         defholder.setInitParameter("redirectWelcome", "false");
@@ -348,31 +346,104 @@
         @SuppressWarnings("unused")
         ServletHolder jspholder = context.addServlet(NoJspServlet.class, "*.jsp");
 
-        String response = connector.getResponses("GET /context/ HTTP/1.0\r\n\r\n");
+        String response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
         assertResponseContains("403", response);
 
         createFile(index, "<h1>Hello Index</h1>");
-        response = connector.getResponses("GET /context/ HTTP/1.0\r\n\r\n");
+        response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
         assertResponseContains("<h1>Hello Index</h1>", response);
 
         createFile(inde, "<h1>Hello Inde</h1>");
-        response = connector.getResponses("GET /context/ HTTP/1.0\r\n\r\n");
+        response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
         assertResponseContains("<h1>Hello Index</h1>", response);
 
         assertTrue(index.delete());
-        response = connector.getResponses("GET /context/ HTTP/1.0\r\n\r\n");
+        response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
         assertResponseContains("<h1>Hello Inde</h1>", response);
 
         assertTrue(inde.delete());
-        response = connector.getResponses("GET /context/ HTTP/1.0\r\n\r\n");
+        response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
         assertResponseContains("403", response);
     }
+    
+    @Test
+    public void testWelcomeRedirect() throws Exception
+    {
+        testdir.ensureEmpty();
+        File resBase = testdir.getPathFile("docroot").toFile();
+        FS.ensureDirExists(resBase);
+
+        File dir = new File(resBase, "dir");
+        assertTrue(dir.mkdirs());
+        File inde = new File(dir, "index.htm");
+        File index = new File(dir, "index.html");
+
+        String resBasePath = resBase.getAbsolutePath();
+        ServletHolder defholder = context.addServlet(DefaultServlet.class, "/");
+        defholder.setInitParameter("dirAllowed", "false");
+        defholder.setInitParameter("redirectWelcome", "true");
+        defholder.setInitParameter("welcomeServlets", "false");
+        defholder.setInitParameter("gzip", "false");
+        defholder.setInitParameter("resourceBase", resBasePath);
+        defholder.setInitParameter("maxCacheSize", "1024000");
+        defholder.setInitParameter("maxCachedFileSize", "512000");
+        defholder.setInitParameter("maxCachedFiles", "100");
+
+        @SuppressWarnings("unused")
+        ServletHolder jspholder = context.addServlet(NoJspServlet.class, "*.jsp");
+
+        String response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
+        assertResponseContains("403", response);
+
+        createFile(index, "<h1>Hello Index</h1>");
+        response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
+        assertResponseContains("Location: http://0.0.0.0/context/dir/index.html", response);
+
+        createFile(inde, "<h1>Hello Inde</h1>");
+        response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
+        assertResponseContains("Location: http://0.0.0.0/context/dir/index.html", response);
+
+        assertTrue(index.delete());
+        response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
+        assertResponseContains("Location: http://0.0.0.0/context/dir/index.htm", response);
+        
+        assertTrue(inde.delete());
+        response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
+        assertResponseContains("403", response);
+    }
+
+    @Test
+    public void testWelcomeDirWithQuestion() throws Exception
+    {
+        testdir.ensureEmpty();
+        File resBase = testdir.getPathFile("docroot").toFile();
+        FS.ensureDirExists(resBase);
+        context.setBaseResource(Resource.newResource(resBase));
+
+        File dir = new File(resBase, "dir?");
+        assertTrue(dir.mkdirs());
+        File index = new File(dir, "index.html");
+        createFile(index, "<h1>Hello Index</h1>");
+
+        ServletHolder defholder = context.addServlet(DefaultServlet.class, "/");
+        defholder.setInitParameter("dirAllowed", "false");
+        defholder.setInitParameter("redirectWelcome", "true");
+        defholder.setInitParameter("welcomeServlets", "false");
+        defholder.setInitParameter("gzip", "false");
+
+        String response = connector.getResponse("GET /context/dir%3F HTTP/1.0\r\n\r\n");
+        assertResponseContains("Location: http://0.0.0.0/context/dir%3F/", response);
+
+        response = connector.getResponse("GET /context/dir%3F/ HTTP/1.0\r\n\r\n");
+        assertResponseContains("Location: http://0.0.0.0/context/dir%3F/index.html", response);
+    }
+    
 
     @Test
     public void testWelcomeServlet() throws Exception
     {
         testdir.ensureEmpty();
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         FS.ensureDirExists(resBase);
         File inde = new File(resBase, "index.htm");
         File index = new File(resBase, "index.html");
@@ -417,13 +488,18 @@
     }
 
     @Test
-    public void testResourceBase() throws Exception
+    public void testSymLinks() throws Exception
     {
         testdir.ensureEmpty();
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         FS.ensureDirExists(resBase);
-        File foobar = new File(resBase, "foobar.txt");
-        File link = new File(resBase, "link.txt");
+        File dir = new File(resBase,"dir");
+        File dirLink = new File(resBase,"dirlink");
+        File dirRLink = new File(resBase,"dirrlink");
+        FS.ensureDirExists(dir);
+        File foobar = new File(dir, "foobar.txt");
+        File link = new File(dir, "link.txt");
+        File rLink = new File(dir,"rlink.txt");
         createFile(foobar, "Foo Bar");
 
         String resBasePath = resBase.getAbsolutePath();
@@ -434,18 +510,43 @@
 
         String response;
 
-        response = connector.getResponses("GET /context/foobar.txt HTTP/1.0\r\n\r\n");
+        response = connector.getResponses("GET /context/dir/foobar.txt HTTP/1.0\r\n\r\n");
         assertResponseContains("Foo Bar", response);
 
         if (!OS.IS_WINDOWS)
         {
+            context.clearAliasChecks();
+            
+            Files.createSymbolicLink(dirLink.toPath(),dir.toPath());
+            Files.createSymbolicLink(dirRLink.toPath(),new File("dir").toPath());
             Files.createSymbolicLink(link.toPath(),foobar.toPath());
-            response = connector.getResponses("GET /context/link.txt HTTP/1.0\r\n\r\n");
+            Files.createSymbolicLink(rLink.toPath(),new File("foobar.txt").toPath());
+            response = connector.getResponses("GET /context/dir/link.txt HTTP/1.0\r\n\r\n");
+            assertResponseContains("404", response);
+            response = connector.getResponses("GET /context/dir/rlink.txt HTTP/1.0\r\n\r\n");
+            assertResponseContains("404", response);
+            response = connector.getResponses("GET /context/dirlink/foobar.txt HTTP/1.0\r\n\r\n");
+            assertResponseContains("404", response);
+            response = connector.getResponses("GET /context/dirrlink/foobar.txt HTTP/1.0\r\n\r\n");
+            assertResponseContains("404", response);
+            response = connector.getResponses("GET /context/dirlink/link.txt HTTP/1.0\r\n\r\n");
+            assertResponseContains("404", response);
+            response = connector.getResponses("GET /context/dirrlink/rlink.txt HTTP/1.0\r\n\r\n");
             assertResponseContains("404", response);
             
-            context.addAliasCheck(new ContextHandler.ApproveAliases());
+            context.addAliasCheck(new AllowSymLinkAliasChecker());
             
-            response = connector.getResponses("GET /context/link.txt HTTP/1.0\r\n\r\n");
+            response = connector.getResponses("GET /context/dir/link.txt HTTP/1.0\r\n\r\n");
+            assertResponseContains("Foo Bar", response);
+            response = connector.getResponses("GET /context/dir/rlink.txt HTTP/1.0\r\n\r\n");
+            assertResponseContains("Foo Bar", response);
+            response = connector.getResponses("GET /context/dirlink/foobar.txt HTTP/1.0\r\n\r\n");
+            assertResponseContains("Foo Bar", response);
+            response = connector.getResponses("GET /context/dirrlink/foobar.txt HTTP/1.0\r\n\r\n");
+            assertResponseContains("Foo Bar", response);
+            response = connector.getResponses("GET /context/dirlink/link.txt HTTP/1.0\r\n\r\n");
+            assertResponseContains("Foo Bar", response);
+            response = connector.getResponses("GET /context/dirrlink/link.txt HTTP/1.0\r\n\r\n");
             assertResponseContains("Foo Bar", response);
         }
     }
@@ -454,7 +555,7 @@
     public void testWelcomeExactServlet() throws Exception
     {
         testdir.ensureEmpty();
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         FS.ensureDirExists(resBase);
         File inde = new File(resBase, "index.htm");
         File index = new File(resBase, "index.html");
@@ -499,10 +600,49 @@
     }
 
     @Test
+    public void testDirectFromResourceHttpContent() throws Exception
+    {
+        if (!OS.IS_LINUX)
+            return;
+        
+        testdir.ensureEmpty();
+        File resBase = testdir.getPathFile("docroot").toFile();
+        FS.ensureDirExists(resBase);
+        context.setBaseResource(Resource.newResource(resBase));
+        
+        File index = new File(resBase, "index.html");
+        createFile(index, "<h1>Hello World</h1>");
+
+        ServletHolder defholder = context.addServlet(DefaultServlet.class, "/");
+        defholder.setInitParameter("dirAllowed", "true");
+        defholder.setInitParameter("redirectWelcome", "false");
+        defholder.setInitParameter("useFileMappedBuffer", "true");
+        defholder.setInitParameter("welcomeServlets", "exact");
+        defholder.setInitParameter("gzip", "false");
+        defholder.setInitParameter("resourceCache","resourceCache");
+
+        String response;
+
+        response = connector.getResponses("GET /context/index.html HTTP/1.0\r\n\r\n");
+        assertResponseContains("<h1>Hello World</h1>", response);
+        
+        ResourceContentFactory factory = (ResourceContentFactory)context.getServletContext().getAttribute("resourceCache");
+        
+        HttpContent content = factory.getContent("/index.html",200);
+        ByteBuffer buffer = content.getDirectBuffer();
+        Assert.assertTrue(buffer.isDirect());        
+        content = factory.getContent("/index.html",5);
+        buffer = content.getDirectBuffer();
+        Assert.assertTrue(buffer==null);        
+    }
+    
+    
+    
+    @Test
     public void testRangeRequests() throws Exception
     {
         testdir.ensureEmpty();
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         FS.ensureDirExists(resBase);
         File data = new File(resBase, "data.txt");
         createFile(data, "01234567890123456789012345678901234567890123456789012345678901234567890123456789");
@@ -649,7 +789,7 @@
     public void testFiltered() throws Exception
     {
         testdir.ensureEmpty();
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         FS.ensureDirExists(resBase);
         File file0 = new File(resBase, "data0.txt");
         createFile(file0, "Hello Text 0");
@@ -667,16 +807,21 @@
         assertResponseContains("Content-Length: 12", response);
         assertResponseNotContains("Extra Info", response);
 
+        server.stop();
         context.addFilter(OutputFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
+        server.start();
+        
         response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\n\r\n");
         assertResponseContains("Content-Length: 2", response); // 20 something long
         assertResponseContains("Extra Info", response);
         assertResponseNotContains("Content-Length: 12", response);
 
+        server.stop();
         context.getServletHandler().setFilterMappings(new FilterMapping[]{});
         context.getServletHandler().setFilters(new FilterHolder[]{});
-
         context.addFilter(WriterFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
+        server.start();
+        
         response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\n\r\n");
         assertResponseContains("Content-Length: 2", response); // 20 something long
         assertResponseContains("Extra Info", response);
@@ -688,7 +833,7 @@
     public void testGzip() throws Exception
     {
         testdir.ensureEmpty();
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         FS.ensureDirExists(resBase);
         File file0 = new File(resBase, "data0.txt");
         createFile(file0, "Hello Text 0");
@@ -702,23 +847,132 @@
         defholder.setInitParameter("redirectWelcome", "false");
         defholder.setInitParameter("welcomeServlets", "false");
         defholder.setInitParameter("gzip", "true");
+        defholder.setInitParameter("etags", "true");
         defholder.setInitParameter("resourceBase", resBasePath);
 
         String response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\n\r\n");
         assertResponseContains("Content-Length: 12", response);
+        assertResponseContains("Content-Type: text/plain",response);
         assertResponseContains("Hello Text 0",response);
         assertResponseContains("Vary: Accept-Encoding",response);
+        assertResponseContains("ETag: ",response);
         assertResponseNotContains("Content-Encoding: gzip",response);
+        int e=response.indexOf("ETag: ");
+        String etag = response.substring(e+6,response.indexOf('"',e+11)+1);
+        String etag_gzip = etag.substring(0,etag.length()-1)+"--gzip\"";
         
         response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
         assertResponseContains("Content-Length: 9", response);
         assertResponseContains("fake gzip",response);
+        assertResponseContains("Content-Type: text/plain",response);
         assertResponseContains("Vary: Accept-Encoding",response);
         assertResponseContains("Content-Encoding: gzip",response);
+        assertResponseContains("ETag: "+etag_gzip,response);
+        
+        response = connector.getResponses("GET /context/data0.txt.gz HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
+        assertResponseContains("Content-Length: 9", response);
+        assertResponseContains("fake gzip",response);
+        assertResponseContains("Content-Type: application/gzip",response);
+        assertResponseNotContains("Vary: Accept-Encoding",response);
+        assertResponseNotContains("Content-Encoding: gzip",response);
+        assertResponseNotContains("ETag: "+etag_gzip,response);
+        assertResponseContains("ETag: ",response);   
+        
+        response = connector.getResponses("GET /context/data0.txt.gz HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"wobble\"\r\n\r\n");
+        assertResponseContains("Content-Length: 9", response);
+        assertResponseContains("fake gzip",response);
+        assertResponseContains("Content-Type: application/gzip",response);
+        assertResponseNotContains("Vary: Accept-Encoding",response);
+        assertResponseNotContains("Content-Encoding: gzip",response);
+        assertResponseNotContains("ETag: "+etag_gzip,response);
+        assertResponseContains("ETag: ",response);   
+        
+        response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: "+etag_gzip+"\r\n\r\n");
+        assertResponseContains("304 Not Modified", response);
+        assertResponseContains("ETag: "+etag_gzip,response);
+
+        response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: "+etag+"\r\n\r\n");
+        assertResponseContains("304 Not Modified", response);
+        assertResponseContains("ETag: "+etag,response);
+        
+        response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"foobar\","+etag_gzip+"\r\n\r\n");
+        assertResponseContains("304 Not Modified", response);
+        assertResponseContains("ETag: "+etag_gzip,response);
+
+        response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"foobar\","+etag+"\r\n\r\n");
+        assertResponseContains("304 Not Modified", response);
+        assertResponseContains("ETag: "+etag,response);
         
     }
 
+    @Test
+    public void testCachedGzip() throws Exception
+    {
+        testdir.ensureEmpty();
+        File resBase = testdir.getPathFile("docroot").toFile();                
+        FS.ensureDirExists(resBase);
+        File file0 = new File(resBase, "data0.txt");
+        createFile(file0, "Hello Text 0");
+        File file0gz = new File(resBase, "data0.txt.gz");
+        createFile(file0gz, "fake gzip");
 
+        String resBasePath = resBase.getAbsolutePath();
+
+        ServletHolder defholder = context.addServlet(DefaultServlet.class, "/");
+        defholder.setInitParameter("dirAllowed", "false");
+        defholder.setInitParameter("redirectWelcome", "false");
+        defholder.setInitParameter("welcomeServlets", "false");
+        defholder.setInitParameter("gzip", "true");
+        defholder.setInitParameter("etags", "true");
+        defholder.setInitParameter("resourceBase", resBasePath);
+        defholder.setInitParameter("maxCachedFiles", "1024");
+        defholder.setInitParameter("maxCachedFileSize", "200000000");
+        defholder.setInitParameter("maxCacheSize", "256000000"); 
+
+        String response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\n\r\n");
+        assertResponseContains("Content-Length: 12", response);
+        assertResponseContains("Content-Type: text/plain",response);
+        assertResponseContains("Hello Text 0",response);
+        assertResponseContains("Vary: Accept-Encoding",response);
+        assertResponseContains("ETag: ",response);
+        assertResponseNotContains("Content-Encoding: gzip",response);
+        int e=response.indexOf("ETag: ");
+        String etag = response.substring(e+6,response.indexOf('"',e+11)+1);
+        String etag_gzip = etag.substring(0,etag.length()-1)+"--gzip\"";
+        
+        response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
+        assertResponseContains("Content-Length: 9", response);
+        assertResponseContains("fake gzip",response);
+        assertResponseContains("Content-Type: text/plain",response);
+        assertResponseContains("Vary: Accept-Encoding",response);
+        assertResponseContains("Content-Encoding: gzip",response);
+        assertResponseContains("ETag: "+etag_gzip,response);
+        
+        response = connector.getResponses("GET /context/data0.txt.gz HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
+        assertResponseContains("Content-Length: 9", response);
+        assertResponseContains("fake gzip",response);
+        assertResponseContains("Content-Type: application/gzip",response);
+        assertResponseNotContains("Vary: Accept-Encoding",response);
+        assertResponseNotContains("Content-Encoding: gzip",response);
+        assertResponseNotContains("ETag: "+etag_gzip,response);
+        assertResponseContains("ETag: ",response);
+        
+        response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: "+etag_gzip+"\r\n\r\n");
+        assertResponseContains("304 Not Modified", response);
+        assertResponseContains("ETag: "+etag_gzip,response);
+
+        response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: "+etag+"\r\n\r\n");
+        assertResponseContains("304 Not Modified", response);
+        assertResponseContains("ETag: "+etag,response);
+        
+        response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"foobar\","+etag_gzip+"\r\n\r\n");
+        assertResponseContains("304 Not Modified", response);
+        assertResponseContains("ETag: "+etag_gzip,response);
+
+        response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"foobar\","+etag+"\r\n\r\n");
+        assertResponseContains("304 Not Modified", response);
+        assertResponseContains("ETag: "+etag,response);
+    }
 
     @Test
     public void testIfModifiedSmall() throws Exception
@@ -735,7 +989,7 @@
     public void testIfModified(String content) throws Exception
     {
         testdir.ensureEmpty();
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         FS.ensureDirExists(resBase);
         File file = new File(resBase, "file.txt");
 
@@ -788,7 +1042,7 @@
     public void testIfETag(String content) throws Exception
     {
         testdir.ensureEmpty();
-        File resBase = testdir.getFile("docroot");
+        File resBase = testdir.getPathFile("docroot").toFile();
         FS.ensureDirExists(resBase);
         File file = new File(resBase, "file.txt");
 
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherForwardTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherForwardTest.java
index e10d7d4..5dfb2c4 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherForwardTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherForwardTest.java
@@ -21,6 +21,9 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 import javax.servlet.ServletException;
 import javax.servlet.ServletInputStream;
 import javax.servlet.http.HttpServlet;
@@ -35,13 +38,15 @@
 import org.junit.Assert;
 import org.junit.Test;
 
+
+@SuppressWarnings("serial")
 public class DispatcherForwardTest
 {
     private Server server;
     private LocalConnector connector;
     private HttpServlet servlet1;
     private HttpServlet servlet2;
-    private List<Exception> failures = new ArrayList<>();
+    private List<Throwable> failures = new ArrayList<>();
 
     public void prepare() throws Exception
     {
@@ -57,9 +62,9 @@
     }
 
     @After
-    public void dispose() throws Exception
+    public void dispose() throws Throwable
     {
-        for (Exception failure : failures)
+        for (Throwable failure : failures)
             throw failure;
         server.stop();
     }
@@ -67,30 +72,38 @@
     // Replacement for Assert that allows to check failures after the response has been sent.
     private <S> void checkThat(S item, Matcher<S> matcher)
     {
-        if (!matcher.matches(item))
-            failures.add(new Exception());
+        try
+        {
+            Assert.assertThat(item,matcher);
+        }
+        catch(Throwable th)
+        {
+            failures.add(th);
+        }
     }
 
     @Test
     public void testQueryRetainedByForwardWithoutQuery() throws Exception
     {
-        // 1. request /one?a=1
+        // 1. request /one?a=1%20one
         // 1. forward /two
-        // 2. assert query => a=1
-        // 1. assert query => a=1
+        // 2. assert query => a=1 one
+        // 1. assert query => a=1 one
 
-        final String query1 = "a=1";
+        CountDownLatch latch = new CountDownLatch(1);
+        final String query1 = "a=1%20one";
         servlet1 = new HttpServlet()
         {
             @Override
             protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
 
                 req.getRequestDispatcher("/two").forward(req, resp);
 
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
-                checkThat("1", Matchers.equalTo(req.getParameter("a")));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
+                checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -98,8 +111,8 @@
             @Override
             protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
-                checkThat("1", Matchers.equalTo(req.getParameter("a")));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
+                checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
             }
         };
 
@@ -111,6 +124,7 @@
                 "Connection: close\r\n" +
                 "\r\n";
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
@@ -122,21 +136,23 @@
         // 2. assert query => a=2
         // 1. assert query => a=1
 
-        final String query1 = "a=1&b=2";
-        final String query2 = "a=3";
-        final String query3 = "a=3&b=2";
+        CountDownLatch latch = new CountDownLatch(1);
+        final String query1 = "a=1%20one&b=2%20two";
+        final String query2 = "a=3%20three";
+        final String query3 = "a=3%20three&b=2%20two";
         servlet1 = new HttpServlet()
         {
             @Override
             protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
 
                 req.getRequestDispatcher("/two?" + query2).forward(req, resp);
 
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
-                checkThat("1", Matchers.equalTo(req.getParameter("a")));
-                checkThat("2", Matchers.equalTo(req.getParameter("b")));
+                checkThat(req.getQueryString(), Matchers.equalTo(query1));
+                checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
+                checkThat(req.getParameter("b"),Matchers.equalTo("2 two"));
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -144,9 +160,9 @@
             @Override
             protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query3, Matchers.equalTo(req.getQueryString()));
-                checkThat("3", Matchers.equalTo(req.getParameter("a")));
-                checkThat("2", Matchers.equalTo(req.getParameter("b")));
+                checkThat(req.getQueryString(),Matchers.equalTo(query3));
+                checkThat(req.getParameter("a"),Matchers.equalTo("3 three"));
+                checkThat(req.getParameter("b"),Matchers.equalTo("2 two"));
             }
         };
 
@@ -158,6 +174,7 @@
                 "Connection: close\r\n" +
                 "\r\n";
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
@@ -169,20 +186,22 @@
         // 2. assert query => a=1&b=2
         // 1. assert query => a=1
 
-        final String query1 = "a=1";
-        final String query2 = "b=2";
-        final String query3 = "b=2&a=1";
+        CountDownLatch latch = new CountDownLatch(1);
+        final String query1 = "a=1%20one";
+        final String query2 = "b=2%20two";
+        final String query3 = "b=2%20two&a=1%20one";
         servlet1 = new HttpServlet()
         {
             @Override
             protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
 
                 req.getRequestDispatcher("/two?" + query2).forward(req, resp);
 
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
-                checkThat("1", Matchers.equalTo(req.getParameter("a")));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
+                checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -190,9 +209,9 @@
             @Override
             protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query3, Matchers.equalTo(req.getQueryString()));
-                checkThat("1", Matchers.equalTo(req.getParameter("a")));
-                checkThat("2", Matchers.equalTo(req.getParameter("b")));
+                checkThat(req.getQueryString(),Matchers.equalTo(query3));
+                checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
+                checkThat(req.getParameter("b"),Matchers.equalTo("2 two"));
             }
         };
 
@@ -204,6 +223,7 @@
                 "Connection: close\r\n" +
                 "\r\n";
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
@@ -215,22 +235,24 @@
         // 2. assert query => a=1 + params => a=1,2
         // 1. assert query => a=1 + params => a=1,2
 
-        final String query1 = "a=1";
-        final String form = "a=2";
+        CountDownLatch latch = new CountDownLatch(1);
+        final String query1 = "a=1%20one";
+        final String form = "a=2%20two";
         servlet1 = new HttpServlet()
         {
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
 
                 req.getRequestDispatcher("/two").forward(req, resp);
 
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
                 String[] values = req.getParameterValues("a");
                 checkThat(values, Matchers.notNullValue());
                 checkThat(2, Matchers.equalTo(values.length));
-                checkThat(values, Matchers.arrayContainingInAnyOrder("1", "2"));
+                checkThat(values, Matchers.arrayContainingInAnyOrder("1 one", "2 two"));
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -238,11 +260,11 @@
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
                 String[] values = req.getParameterValues("a");
                 checkThat(values, Matchers.notNullValue());
                 checkThat(2, Matchers.equalTo(values.length));
-                checkThat(values, Matchers.arrayContainingInAnyOrder("1", "2"));
+                checkThat(values, Matchers.arrayContainingInAnyOrder("1 one", "2 two"));
             }
         };
 
@@ -257,6 +279,7 @@
                 "\r\n" +
                 form;
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
@@ -268,23 +291,25 @@
         // 2. assert query => a=3 + params => a=3,2,1
         // 1. assert query => a=1 + params => a=1,2
 
-        final String query1 = "a=1";
-        final String query2 = "a=3";
-        final String form = "a=2";
+        CountDownLatch latch = new CountDownLatch(1);
+        final String query1 = "a=1%20one";
+        final String query2 = "a=3%20three";
+        final String form = "a=2%20two";
         servlet1 = new HttpServlet()
         {
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
 
                 req.getRequestDispatcher("/two?" + query2).forward(req, resp);
 
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
                 String[] values = req.getParameterValues("a");
                 checkThat(values, Matchers.notNullValue());
                 checkThat(2, Matchers.equalTo(values.length));
-                checkThat(values, Matchers.arrayContainingInAnyOrder("1", "2"));
+                checkThat(values, Matchers.arrayContainingInAnyOrder("1 one", "2 two"));
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -292,11 +317,11 @@
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query2, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query2));
                 String[] values = req.getParameterValues("a");
                 checkThat(values, Matchers.notNullValue());
                 checkThat(3, Matchers.equalTo(values.length));
-                checkThat(values, Matchers.arrayContainingInAnyOrder("3", "2", "1"));
+                checkThat(values, Matchers.arrayContainingInAnyOrder("3 three", "2 two", "1 one"));
             }
         };
 
@@ -311,6 +336,7 @@
                 "\r\n" +
                 form;
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
@@ -322,23 +348,25 @@
         // 2. assert query => a=1&c=3 + params => a=1&b=2&c=3
         // 1. assert query => a=1 + params => a=1&b=2
 
-        final String query1 = "a=1";
-        final String query2 = "c=3";
-        final String query3 = "c=3&a=1";
-        final String form = "b=2";
+        CountDownLatch latch = new CountDownLatch(1);
+        final String query1 = "a=1%20one";
+        final String query2 = "c=3%20three";
+        final String query3 = "c=3%20three&a=1%20one";
+        final String form = "b=2%20two";
         servlet1 = new HttpServlet()
         {
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
 
                 req.getRequestDispatcher("/two?" + query2).forward(req, resp);
 
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
-                checkThat("1", Matchers.equalTo(req.getParameter("a")));
-                checkThat("2", Matchers.equalTo(req.getParameter("b")));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
+                checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
+                checkThat(req.getParameter("b"),Matchers.equalTo("2 two"));
                 checkThat(req.getParameter("c"), Matchers.nullValue());
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -346,10 +374,10 @@
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query3, Matchers.equalTo(req.getQueryString()));
-                checkThat("1", Matchers.equalTo(req.getParameter("a")));
-                checkThat("2", Matchers.equalTo(req.getParameter("b")));
-                checkThat("3", Matchers.equalTo(req.getParameter("c")));
+                checkThat(req.getQueryString(),Matchers.equalTo(query3));
+                checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
+                checkThat(req.getParameter("b"),Matchers.equalTo("2 two"));
+                checkThat(req.getParameter("c"),Matchers.equalTo("3 three"));
             }
         };
 
@@ -364,6 +392,7 @@
                 "\r\n" +
                 form;
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
@@ -376,25 +405,27 @@
         // 2. assert query => a=1&c=3 + params => a=1&b=2&c=3
         // 1. assert query => a=1 + params => a=1&b=2
 
-        final String query1 = "a=1";
-        final String query2 = "c=3";
-        final String query3 = "c=3&a=1";
-        final String form = "b=2";
+        CountDownLatch latch = new CountDownLatch(1);
+        final String query1 = "a=1%20one";
+        final String query2 = "c=3%20three";
+        final String query3 = "c=3%20three&a=1%20one";
+        final String form = "b=2%20two";
         servlet1 = new HttpServlet()
         {
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
-                checkThat("1", Matchers.equalTo(req.getParameter("a")));
-                checkThat("2", Matchers.equalTo(req.getParameter("b")));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
+                checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
+                checkThat(req.getParameter("b"),Matchers.equalTo("2 two"));
 
                 req.getRequestDispatcher("/two?" + query2).forward(req, resp);
 
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
-                checkThat("1", Matchers.equalTo(req.getParameter("a")));
-                checkThat("2", Matchers.equalTo(req.getParameter("b")));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
+                checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
+                checkThat(req.getParameter("b"),Matchers.equalTo("2 two"));
                 checkThat(req.getParameter("c"), Matchers.nullValue());
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -402,10 +433,10 @@
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query3, Matchers.equalTo(req.getQueryString()));
-                checkThat("1", Matchers.equalTo(req.getParameter("a")));
-                checkThat("2", Matchers.equalTo(req.getParameter("b")));
-                checkThat("3", Matchers.equalTo(req.getParameter("c")));
+                checkThat(req.getQueryString(),Matchers.equalTo(query3));
+                checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
+                checkThat(req.getParameter("b"),Matchers.equalTo("2 two"));
+                checkThat(req.getParameter("c"),Matchers.equalTo("3 three"));
             }
         };
 
@@ -420,25 +451,28 @@
                 "\r\n" +
                 form;
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
     @Test
     public void testContentCanBeReadViaInputStreamAfterForwardWithoutQuery() throws Exception
     {
-        final String query1 = "a=1";
-        final String form = "c=3";
+        CountDownLatch latch = new CountDownLatch(1);
+        final String query1 = "a=1%20one";
+        final String form = "c=3%20three";
         servlet1 = new HttpServlet()
         {
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
 
                 req.getRequestDispatcher("/two").forward(req, resp);
 
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
                 checkThat(req.getParameter("c"), Matchers.nullValue());
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -446,7 +480,7 @@
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
                 ServletInputStream input = req.getInputStream();
                 for (int i = 0; i < form.length(); ++i)
                     checkThat(form.charAt(i) & 0xFFFF, Matchers.equalTo(input.read()));
@@ -464,27 +498,30 @@
                 "\r\n" +
                 form;
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
     @Test
     public void testContentCanBeReadViaInputStreamAfterForwardWithQuery() throws Exception
     {
-        final String query1 = "a=1";
-        final String query2 = "b=2";
-        final String query3 = "b=2&a=1";
-        final String form = "c=3";
+        CountDownLatch latch = new CountDownLatch(1);
+        final String query1 = "a=1%20one";
+        final String query2 = "b=2%20two";
+        final String query3 = "b=2%20two&a=1%20one";
+        final String form = "c=3%20three";
         servlet1 = new HttpServlet()
         {
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
 
                 req.getRequestDispatcher("/two?" + query2).forward(req, resp);
 
-                checkThat(query1, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query1));
                 checkThat(req.getParameter("c"), Matchers.nullValue());
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -492,7 +529,7 @@
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
-                checkThat(query3, Matchers.equalTo(req.getQueryString()));
+                checkThat(req.getQueryString(),Matchers.equalTo(query3));
                 ServletInputStream input = req.getInputStream();
                 for (int i = 0; i < form.length(); ++i)
                     checkThat(form.charAt(i) & 0xFFFF, Matchers.equalTo(input.read()));
@@ -511,6 +548,7 @@
                 "\r\n" +
                 form;
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java
index ee9bd6e..f504e6d 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java
@@ -18,11 +18,18 @@
 
 package org.eclipse.jetty.servlet;
 
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
+
 import javax.servlet.DispatcherType;
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
@@ -57,12 +64,6 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.startsWith;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
 public class DispatcherTest
 {
     private Server _server;
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/EncodedURITest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/EncodedURITest.java
new file mode 100644
index 0000000..3038d4f
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/EncodedURITest.java
@@ -0,0 +1,228 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.servlet;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.GenericServlet;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestWrapper;
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletResponseWrapper;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.eclipse.jetty.server.Dispatcher;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.server.handler.ResourceHandler;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.UrlEncoded;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class EncodedURITest
+{
+    private Server _server;
+    private LocalConnector _connector;
+    private ContextHandlerCollection _contextCollection;
+    private ServletContextHandler _context0;
+    private ServletContextHandler _context1;
+    private ResourceHandler _resourceHandler;
+
+    @Before
+    public void init() throws Exception
+    {
+        _server = new Server();
+        _connector = new LocalConnector(_server);
+        _connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
+        _connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
+        _server.addConnector( _connector );
+
+        _contextCollection = new ContextHandlerCollection();
+        _server.setHandler(_contextCollection);
+
+        _context0 = new ServletContextHandler();
+        _context0.setContextPath("/context path");
+        _contextCollection.addHandler(_context0);
+        _context0.addFilter(AsyncFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
+        _context0.addServlet(TestServlet.class,"/test servlet/*");
+        _context0.addServlet(AsyncServlet.class,"/async servlet/*");
+        
+        _context1 = new ServletContextHandler();
+        _context1.setContextPath("/redirecting context");
+        _contextCollection.addHandler(_context1);
+
+        _server.start();
+    }
+
+    @After
+    public void destroy() throws Exception
+    {
+        _server.stop();
+        _server.join();
+    }
+
+    @Test
+    public void testTestServlet() throws Exception
+    {
+        String response = _connector.getResponse("GET /c%6Fntext%20path/test%20servlet/path%20info HTTP/1.0\n\n");
+        assertThat(response,startsWith("HTTP/1.1 200 "));
+        assertThat(response,Matchers.containsString("requestURI=/c%6Fntext%20path/test%20servlet/path%20info"));
+        assertThat(response,Matchers.containsString("contextPath=/context path"));
+        assertThat(response,Matchers.containsString("servletPath=/test servlet"));
+        assertThat(response,Matchers.containsString("pathInfo=/path info"));
+    }
+
+    @Test
+    public void testAsyncFilterTestServlet() throws Exception
+    {
+        String response = _connector.getResponse("GET /context%20path/test%20servlet/path%20info?async=true HTTP/1.0\n\n");
+        assertThat(response,startsWith("HTTP/1.1 200 "));
+        assertThat(response,Matchers.containsString("requestURI=/context%20path/test%20servlet/path%20info"));
+        assertThat(response,Matchers.containsString("contextPath=/context path"));
+        assertThat(response,Matchers.containsString("servletPath=/test servlet"));
+        assertThat(response,Matchers.containsString("pathInfo=/path info"));
+    }
+
+    @Test
+    public void testAsyncFilterWrapTestServlet() throws Exception
+    {
+        String response = _connector.getResponse("GET /context%20path/test%20servlet/path%20info?async=true&wrap=true HTTP/1.0\n\n");
+        assertThat(response,startsWith("HTTP/1.1 200 "));
+        assertThat(response,Matchers.containsString("requestURI=/context%20path/test%20servlet/path%20info"));
+        assertThat(response,Matchers.containsString("contextPath=/context path"));
+        assertThat(response,Matchers.containsString("servletPath=/test servlet"));
+        assertThat(response,Matchers.containsString("pathInfo=/path info"));
+    }
+
+    @Test
+    public void testAsyncServletTestServlet() throws Exception
+    {
+        String response = _connector.getResponse("GET /context%20path/async%20servlet/path%20info HTTP/1.0\n\n");
+        assertThat(response,startsWith("HTTP/1.1 200 "));
+        assertThat(response,Matchers.containsString("requestURI=/context%20path/test servlet/path info"));
+        assertThat(response,Matchers.containsString("contextPath=/context path"));
+        assertThat(response,Matchers.containsString("servletPath=/test servlet"));
+        assertThat(response,Matchers.containsString("pathInfo=/path info"));
+    }
+
+    @Test
+    public void testAsyncServletTestServletEncoded() throws Exception
+    {
+        String response = _connector.getResponse("GET /context%20path/async%20servlet/path%20info?encode=true HTTP/1.0\n\n");
+        assertThat(response,startsWith("HTTP/1.1 200 "));
+        assertThat(response,Matchers.containsString("requestURI=/context%20path/test%20servlet/path%20info"));
+        assertThat(response,Matchers.containsString("contextPath=/context path"));
+        assertThat(response,Matchers.containsString("servletPath=/test servlet"));
+        assertThat(response,Matchers.containsString("pathInfo=/path info"));
+    }
+    
+
+    public static class TestServlet extends HttpServlet
+    {
+        @Override
+        public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {
+            response.setContentType("text/plain");
+            response.getWriter().println("requestURI="+request.getRequestURI());
+            response.getWriter().println("contextPath="+request.getContextPath());
+            response.getWriter().println("servletPath="+request.getServletPath());
+            response.getWriter().println("pathInfo="+request.getPathInfo());
+        }
+    }
+
+    
+    public static class AsyncServlet extends HttpServlet
+    {
+        @Override
+        public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {
+            AsyncContext async = Boolean.parseBoolean(request.getParameter("wrap"))
+                ?request.startAsync(request,response)
+                :request.startAsync();
+
+            if (Boolean.parseBoolean(request.getParameter("encode")))
+                async.dispatch("/test%20servlet"+URIUtil.encodePath(request.getPathInfo()));
+            else
+                async.dispatch("/test servlet/path info"+request.getPathInfo());
+            return;
+        }
+    }
+    
+    public static class AsyncFilter implements Filter
+    {
+        @Override
+        public void init(FilterConfig filterConfig) throws ServletException
+        {            
+        }
+
+        @Override
+        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+        {
+            if (Boolean.parseBoolean(request.getParameter("async")) && !Boolean.parseBoolean((String)request.getAttribute("async")))
+            {
+                request.setAttribute("async","true");
+                AsyncContext async = Boolean.parseBoolean(request.getParameter("wrap"))
+                    ?request.startAsync(request,response)
+                    :request.startAsync();
+                async.dispatch();
+                return;
+            }
+            chain.doFilter(request,response);
+        }
+
+        @Override
+        public void destroy()
+        {            
+        }
+    }
+
+}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ErrorPageTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ErrorPageTest.java
index b720840..d0a97e1 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ErrorPageTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ErrorPageTest.java
@@ -31,8 +31,7 @@
 import org.eclipse.jetty.server.Dispatcher;
 import org.eclipse.jetty.server.LocalConnector;
 import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Before;
@@ -45,6 +44,7 @@
 {
     private Server _server;
     private LocalConnector _connector;
+    private StacklessLogging _stackless;
 
     @Before
     public void init() throws Exception
@@ -69,13 +69,13 @@
         error.addErrorPage(ErrorPageErrorHandler.GLOBAL_ERROR_PAGE,"/error/GlobalErrorPage");
         
         _server.start();
-        ((StdErrLog)Log.getLogger(ServletHandler.class)).setHideStacks(true);
+        _stackless=new StacklessLogging(ServletHandler.class);
     }
 
     @After
     public void destroy() throws Exception
     {
-        ((StdErrLog)Log.getLogger(ServletHandler.class)).setHideStacks(false);
+        _stackless.close();
         _server.stop();
         _server.join();
     }
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/FilterHolderTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/FilterHolderTest.java
new file mode 100644
index 0000000..6d0242f
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/FilterHolderTest.java
@@ -0,0 +1,103 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.servlet;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.junit.Test;
+
+/**
+ * FilterHolderTest
+ *
+ *
+ */
+public class FilterHolderTest
+{
+
+    @Test
+    public void testInitialize()
+    throws Exception
+    {
+        ServletHandler handler = new ServletHandler();
+        
+        final AtomicInteger counter = new AtomicInteger(0);
+        Filter filter = new Filter ()
+                {
+                    @Override
+                    public void init(FilterConfig filterConfig) throws ServletException
+                    {   
+                        counter.incrementAndGet();
+                    }
+
+                    @Override
+                    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+                    {  
+                    }
+
+                    @Override
+                    public void destroy()
+                    { 
+                    }
+            
+                };
+
+      FilterHolder fh = new FilterHolder();
+      fh.setServletHandler(handler);
+     
+      fh.setName("xx");
+      fh.setFilter(filter);
+
+      try (StacklessLogging stackless = new StacklessLogging(FilterHolder.class))
+      {
+          fh.initialize();
+          fail("Not started");
+      }
+      catch (Exception e)
+      {
+          //expected
+      }
+
+       fh.start();
+       fh.initialize();
+       assertEquals(1, counter.get());
+       
+       fh.initialize();
+       assertEquals(1, counter.get());
+       
+       fh.stop();
+       assertEquals(1, counter.get());
+       fh.start();
+       assertEquals(1, counter.get());
+       fh.initialize();
+       assertEquals(2, counter.get());
+    }
+
+}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java
new file mode 100644
index 0000000..4baa82c
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java
@@ -0,0 +1,395 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.servlet;
+
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalToIgnoringCase;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.zip.GZIPInputStream;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.gzip.GzipHandler;
+import org.eclipse.jetty.util.IO;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GzipHandlerTest
+{
+    private static final String __content =
+        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In quis felis nunc. "+
+        "Quisque suscipit mauris et ante auctor ornare rhoncus lacus aliquet. Pellentesque "+
+        "habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. "+
+        "Vestibulum sit amet felis augue, vel convallis dolor. Cras accumsan vehicula diam "+
+        "at faucibus. Etiam in urna turpis, sed congue mi. Morbi et lorem eros. Donec vulputate "+
+        "velit in risus suscipit lobortis. Aliquam id urna orci, nec sollicitudin ipsum. "+
+        "Cras a orci turpis. Donec suscipit vulputate cursus. Mauris nunc tellus, fermentum "+
+        "eu auctor ut, mollis at diam. Quisque porttitor ultrices metus, vitae tincidunt massa "+
+        "sollicitudin a. Vivamus porttitor libero eget purus hendrerit cursus. Integer aliquam "+
+        "consequat mauris quis luctus. Cras enim nibh, dignissim eu faucibus ac, mollis nec neque. "+
+        "Aliquam purus mauris, consectetur nec convallis lacinia, porta sed ante. Suspendisse "+
+        "et cursus magna. Donec orci enim, molestie a lobortis eu, imperdiet vitae neque.";
+
+    private static final String __micro = __content.substring(0,10);
+
+    private static final String __contentETag = String.format("W/\"%x\"",__content.hashCode());
+    private static final String __contentETagGzip = String.format("W/\"%x--gzip\"",__content.hashCode());
+    private static final String __icontent = "BEFORE"+__content+"AFTER";
+            
+    private Server _server;
+    private LocalConnector _connector;
+
+    @Before
+    public void init() throws Exception
+    {
+        _server = new Server();
+        _connector = new LocalConnector(_server);
+        _server.addConnector(_connector);
+
+        GzipHandler gzipHandler = new GzipHandler();
+        gzipHandler.setExcludedAgentPatterns();
+        gzipHandler.setMinGzipSize(16);
+
+        ServletContextHandler context = new ServletContextHandler(gzipHandler,"/ctx");
+        ServletHandler servlets = context.getServletHandler();
+        
+        _server.setHandler(gzipHandler);
+        gzipHandler.setHandler(context);
+        servlets.addServletWithMapping(MicroServlet.class,"/micro");
+        servlets.addServletWithMapping(MicroChunkedServlet.class,"/microchunked");
+        servlets.addServletWithMapping(TestServlet.class,"/content");
+        servlets.addServletWithMapping(ForwardServlet.class,"/forward");
+        servlets.addServletWithMapping(IncludeServlet.class,"/include");
+        
+        _server.start();
+    }
+
+    public static class MicroServlet extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException
+        {
+            response.setHeader("ETag",__contentETag);
+            String ifnm = req.getHeader("If-None-Match");
+            if (ifnm!=null && ifnm.equals(__contentETag))
+                response.sendError(304);
+            else
+            {
+                PrintWriter writer = response.getWriter();
+                writer.write(__micro);
+            }
+        }
+    }
+
+    public static class MicroChunkedServlet extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException
+        {
+            PrintWriter writer = response.getWriter();
+            writer.write(__micro);
+            response.flushBuffer();
+        }
+    }
+    
+    public static class TestServlet extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException
+        {
+            if (req.getParameter("vary")!=null)
+                response.addHeader("Vary",req.getParameter("vary"));
+            response.setHeader("ETag",__contentETag);
+            String ifnm = req.getHeader("If-None-Match");    
+            if (ifnm!=null && ifnm.equals(__contentETag))
+                response.sendError(304);
+            else
+            {
+                PrintWriter writer = response.getWriter();
+                writer.write(__content);
+            }
+        }
+    }
+
+    public static class ForwardServlet extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {
+            getServletContext().getRequestDispatcher("/content").forward(request,response);
+        }
+    }
+
+    public static class IncludeServlet extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {
+            response.getWriter().write("BEFORE");
+            getServletContext().getRequestDispatcher("/content").include(request,response);
+            response.getWriter().write("AFTER");
+        }
+    }
+
+    @After
+    public void destroy() throws Exception
+    {
+        _server.stop();
+        _server.join();
+    }
+
+    @Test
+    public void testNotGzipHandler() throws Exception
+    {
+        // generated and parsed test
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        request.setMethod("GET");
+        request.setURI("/ctx/content?vary=Other");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+
+        response = HttpTester.parseResponse(_connector.getResponses(request.generate()));
+        
+        assertThat(response.getStatus(),is(200));
+        assertThat(response.get("Content-Encoding"),not(equalToIgnoringCase("gzip")));
+        assertThat(response.get("ETag"),is(__contentETag));
+        assertThat(response.getValuesList("Vary"),Matchers.contains("Other","Accept-Encoding"));
+
+        InputStream testIn = new ByteArrayInputStream(response.getContentBytes());
+        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
+        IO.copy(testIn,testOut);
+
+        assertEquals(__content, testOut.toString("UTF8"));
+    }
+    
+    
+    @Test
+    public void testGzipHandler() throws Exception
+    {
+        // generated and parsed test
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        request.setMethod("GET");
+        request.setURI("/ctx/content?vary=Accept-Encoding,Other");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setHeader("accept-encoding","gzip");
+
+        response = HttpTester.parseResponse(_connector.getResponses(request.generate()));
+
+        assertThat(response.getStatus(),is(200));
+        assertThat(response.get("Content-Encoding"),Matchers.equalToIgnoringCase("gzip"));
+        assertThat(response.get("ETag"),is(__contentETagGzip));
+        assertThat(response.getCSV("Vary",false),Matchers.contains("Accept-Encoding","Other"));
+
+        InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes()));
+        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
+        IO.copy(testIn,testOut);
+
+        assertEquals(__content, testOut.toString("UTF8"));
+    }
+    
+    @Test
+    public void testGzipNotMicro() throws Exception
+    {
+        // generated and parsed test
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        request.setMethod("GET");
+        request.setURI("/ctx/micro");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setHeader("Accept-Encoding","gzip");
+
+        response = HttpTester.parseResponse(_connector.getResponses(request.generate()));
+
+        assertThat(response.getStatus(),is(200));
+        assertThat(response.get("Content-Encoding"),not(containsString("gzip")));
+        assertThat(response.get("ETag"),is(__contentETag));
+        assertThat(response.get("Vary"),is("Accept-Encoding"));
+
+        InputStream testIn = new ByteArrayInputStream(response.getContentBytes());
+        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
+        IO.copy(testIn,testOut);
+
+        assertEquals(__micro, testOut.toString("UTF8"));
+    }
+
+    @Test
+    public void testGzipNotMicroChunked() throws Exception
+    {
+        // generated and parsed test
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        request.setMethod("GET");
+        request.setURI("/ctx/microchunked");
+        request.setVersion("HTTP/1.1");
+        request.setHeader("Host","tester");
+        request.setHeader("Accept-Encoding","gzip");
+
+        ByteBuffer rawresponse = _connector.getResponse(request.generate());
+        // System.err.println(BufferUtil.toUTF8String(rawresponse));
+        response = HttpTester.parseResponse(rawresponse);
+
+        assertThat(response.getStatus(),is(200));
+        assertThat(response.get("Transfer-Encoding"),containsString("chunked"));
+        assertThat(response.get("Content-Encoding"),containsString("gzip"));
+        assertThat(response.get("Vary"),is("Accept-Encoding"));
+
+        InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes()));
+        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
+        IO.copy(testIn,testOut);
+
+        assertEquals(__micro, testOut.toString("UTF8"));
+    }
+
+    @Test
+    public void testETagNotGzipHandler() throws Exception
+    {
+        // generated and parsed test
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        request.setMethod("GET");
+        request.setURI("/ctx/content");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setHeader("If-None-Match",__contentETag);
+        request.setHeader("accept-encoding","gzip");
+
+        response = HttpTester.parseResponse(_connector.getResponses(request.generate()));
+
+        assertThat(response.getStatus(),is(304));
+        assertThat(response.get("Content-Encoding"),not(Matchers.equalToIgnoringCase("gzip")));
+        assertThat(response.get("ETag"),is(__contentETag));
+    }
+    
+    @Test
+    public void testETagGzipHandler() throws Exception
+    {
+        // generated and parsed test
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        request.setMethod("GET");
+        request.setURI("/ctx/content");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setHeader("If-None-Match",__contentETagGzip);
+        request.setHeader("accept-encoding","gzip");
+
+        response = HttpTester.parseResponse(_connector.getResponses(request.generate()));
+
+        assertThat(response.getStatus(),is(304));
+        assertThat(response.get("Content-Encoding"),not(Matchers.equalToIgnoringCase("gzip")));
+        assertThat(response.get("ETag"),is(__contentETagGzip));
+    }
+    
+    @Test
+    public void testForwardGzipHandler() throws Exception
+    {
+        // generated and parsed test
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        request.setMethod("GET");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setHeader("accept-encoding","gzip");
+        request.setURI("/ctx/forward");
+
+        response = HttpTester.parseResponse(_connector.getResponses(request.generate()));
+
+        assertThat(response.getStatus(),is(200));
+        assertThat(response.get("Content-Encoding"),Matchers.equalToIgnoringCase("gzip"));
+        assertThat(response.get("ETag"),is(__contentETagGzip));
+        assertThat(response.get("Vary"),is("Accept-Encoding"));
+
+        InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes()));
+        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
+        IO.copy(testIn,testOut);
+
+        assertEquals(__content, testOut.toString("UTF8"));
+    }
+    
+    @Test
+    public void testIncludeGzipHandler() throws Exception
+    {
+        // generated and parsed test
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        request.setMethod("GET");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setHeader("accept-encoding","gzip");
+        request.setURI("/ctx/include");
+
+        response = HttpTester.parseResponse(_connector.getResponses(request.generate()));
+
+        assertThat(response.getStatus(),is(200));
+        assertThat(response.get("Content-Encoding"),Matchers.equalToIgnoringCase("gzip"));
+        assertThat(response.get("ETag"),nullValue());
+        assertThat(response.get("Vary"),is("Accept-Encoding"));
+
+        InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes()));
+        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
+        IO.copy(testIn,testOut);
+
+        assertEquals(__icontent, testOut.toString("UTF8"));
+    }
+    
+    @Test
+    public void testAddGetPaths()
+    {
+        GzipHandler gzip = new GzipHandler();
+        gzip.addIncludedPaths("/foo");
+        gzip.addIncludedPaths("^/bar.*$");
+        
+        String[] includedPaths = gzip.getIncludedPaths();
+        assertThat("Included Paths.size", includedPaths.length, is(2));
+        assertThat("Included Paths", Arrays.asList(includedPaths), contains("/foo","^/bar.*$"));
+    }
+}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/IncludedServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/IncludedServletTest.java
new file mode 100644
index 0000000..642e6a9
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/IncludedServletTest.java
@@ -0,0 +1,177 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.servlet;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.net.URI;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.IO;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class IncludedServletTest
+{
+    public static class TopServlet extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            req.getRequestDispatcher("/included").include(req, resp);
+            resp.setHeader("main-page-key", "main-page-value");
+
+            PrintWriter out = resp.getWriter();
+            out.println("<h2> Hello, this is the top page.");
+        }
+    }
+
+    public static class IncludedServlet extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            String headerPrefix = "";
+            if (req.getDispatcherType() == DispatcherType.INCLUDE)
+                headerPrefix = "org.eclipse.jetty.server.include.";
+
+            resp.setHeader(headerPrefix + "included-page-key", "included-page-value");
+            resp.getWriter().println("<h3> This is the included page");
+        }
+    }
+
+    private Server server;
+    private URI baseUri;
+
+    @Before
+    public void startServer() throws Exception
+    {
+        this.server = new Server();
+        ServerConnector connector = new ServerConnector(server);
+        connector.setHost("localhost");
+        connector.setPort(0);
+        server.addConnector(connector);
+
+        ServletContextHandler context = new ServletContextHandler();
+        context.setContextPath("/");
+        context.addServlet(TopServlet.class, "/top");
+        context.addServlet(IncludedServlet.class, "/included");
+
+        server.setHandler(context);
+
+        server.start();
+
+        int port = connector.getLocalPort();
+        String host = connector.getHost();
+
+        baseUri = URI.create("http://" + host + ":" + port + "/");
+    }
+
+    @After
+    public void stopServer() throws Exception
+    {
+        this.server.stop();
+    }
+
+    @Test
+    public void testTopWithIncludedHeader() throws IOException
+    {
+        URI uri = baseUri.resolve("/top");
+        System.out.println("GET (String): " + uri.toASCIIString());
+
+        InputStream in = null;
+        InputStreamReader reader = null;
+        HttpURLConnection connection = null;
+
+        try
+        {
+            connection = (HttpURLConnection) uri.toURL().openConnection();
+            connection.connect();
+            if (HttpURLConnection.HTTP_OK != connection.getResponseCode())
+            {
+                String body = getPotentialBody(connection);
+                String err = String.format("GET request failed (%d %s) %s%n%s", connection.getResponseCode(), connection.getResponseMessage(),
+                        uri.toASCIIString(), body);
+                throw new IOException(err);
+            }
+            in = connection.getInputStream();
+            reader = new InputStreamReader(in);
+            StringWriter writer = new StringWriter();
+            IO.copy(reader, writer);
+
+            String response = writer.toString();
+            // System.out.printf("Response%n%s",response);
+            assertThat("Response", response, containsString("<h2> Hello, this is the top page."));
+            assertThat("Response", response, containsString("<h3> This is the included page"));
+
+            assertThat("Response Header[main-page-key]", connection.getHeaderField("main-page-key"), is("main-page-value"));
+            assertThat("Response Header[included-page-key]", connection.getHeaderField("included-page-key"), is("included-page-value"));
+        }
+        finally
+        {
+            IO.close(reader);
+            IO.close(in);
+        }
+    }
+
+    /**
+     * Attempt to obtain the body text if available. Do not throw an exception if body is unable to be fetched.
+     *
+     * @param connection the connection to fetch the body content from.
+     * @return the body content, if present.
+     */
+    private String getPotentialBody(HttpURLConnection connection)
+    {
+        InputStream in = null;
+        InputStreamReader reader = null;
+        try
+        {
+            in = connection.getInputStream();
+            reader = new InputStreamReader(in);
+            StringWriter writer = new StringWriter();
+            IO.copy(reader, writer);
+            return writer.toString();
+        }
+        catch (IOException e)
+        {
+            return "<no body:" + e.getMessage() + ">";
+        }
+        finally
+        {
+            IO.close(reader);
+            IO.close(in);
+        }
+    }
+}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/PostServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/PostServletTest.java
new file mode 100644
index 0000000..e21d2df
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/PostServletTest.java
@@ -0,0 +1,239 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.servlet;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.hamcrest.Matchers;
+import org.junit.*;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class PostServletTest
+{
+    private static final Logger LOG = Log.getLogger(PostServletTest.class);
+    private static final AtomicBoolean posted = new AtomicBoolean(false);
+    private static final AtomicReference<Throwable> ex0=new AtomicReference<>();
+    private static final AtomicReference<Throwable> ex1=new AtomicReference<>();
+    private static CountDownLatch complete;
+
+    public static class BasicReadPostServlet extends HttpServlet
+    {
+        protected void doPost(HttpServletRequest request, HttpServletResponse response)
+        {
+            posted.set(true);
+            byte[] buffer = new byte[1024];
+            try
+            {
+                int len=request.getInputStream().read(buffer);
+                while(len>0)
+                {
+                    response.getOutputStream().println("read "+len);
+                    response.getOutputStream().flush();
+                    len = request.getInputStream().read(buffer);
+                }
+            }
+            catch (Exception e0)
+            {
+                ex0.set(e0);
+                try
+                {
+                    // this read-call should fail immediately
+                    request.getInputStream().read(buffer);
+                }
+                catch (Exception e1)
+                {
+                    ex1.set(e1);
+                    LOG.warn(e1.toString());
+                }
+            }
+            finally
+            {
+                complete.countDown();
+            }
+        }
+    }
+
+    private Server server;
+    private LocalConnector connector;
+
+    @Before
+    public void startServer() throws Exception
+    {
+        complete=new CountDownLatch(1);
+        ex0.set(null);
+        ex1.set(null);
+        posted.set(false);
+        server = new Server();
+        connector = new LocalConnector(server);
+        server.addConnector(connector);
+
+        ServletContextHandler context = new ServletContextHandler();
+        context.setContextPath("/");
+        context.addServlet(BasicReadPostServlet.class, "/post");
+
+        server.setHandler(context);
+
+        server.start();
+    }
+
+    @After
+    public void stopServer() throws Exception
+    {
+        this.server.stop();
+    }
+
+    @Test
+    public void testGoodPost() throws Exception
+    {
+        StringBuilder req = new StringBuilder();
+        req.append("POST /post HTTP/1.1\r\n");
+        req.append("Host: localhost\r\n");
+        req.append("Transfer-Encoding: chunked\r\n");
+        req.append("\r\n");
+        req.append("6\r\n");
+        req.append("Hello ");
+        req.append("\r\n");
+        req.append("7\r\n");
+        req.append("World!\n");
+        req.append("\r\n");
+        req.append("0\r\n");
+        req.append("\r\n");
+
+        String resp = connector.getResponses(req.toString(),1,TimeUnit.SECONDS);
+
+        assertThat("resp", resp, containsString("HTTP/1.1 200 OK"));
+        assertThat("resp", resp, containsString("chunked"));
+        assertThat("resp", resp, containsString("read 6"));
+        assertThat("resp", resp, containsString("read 7"));
+        assertThat("resp", resp, containsString("\r\n0\r\n"));
+
+        assertThat(ex0.get(),nullValue());
+        assertThat(ex1.get(),nullValue());
+    }
+
+    @Test
+    public void testBadPost() throws Exception
+    {
+        StringBuilder req = new StringBuilder(16*1024);
+        req.append("POST /post HTTP/1.1\r\n");
+        req.append("Host: localhost\r\n");
+        req.append("Transfer-Encoding: chunked\r\n");
+        req.append("\r\n");
+        // intentionally bad (not a valid chunked char here)
+        for (int i=1024;i-->0;)
+            req.append("xxxxxxxxxxxx");
+        req.append("\r\n");
+        req.append("\r\n");
+
+        String resp = connector.getResponse(req.toString());
+        assertThat(resp,startsWith("HTTP/1.1 200 OK")); // exception eaten by handler
+        assertTrue(complete.await(5,TimeUnit.SECONDS));
+        assertThat(ex0.get(),not(nullValue()));
+        assertThat(ex1.get(),not(nullValue()));
+    }
+
+    @Test
+    public void testDeferredBadPost() throws Exception
+    {
+        StringBuilder req = new StringBuilder(16*1024);
+        req.append("POST /post HTTP/1.1\r\n");
+        req.append("Host: localhost\r\n");
+        req.append("Transfer-Encoding: chunked\r\n");
+        req.append("\r\n");
+
+        LocalConnector.LocalEndPoint endp=connector.executeRequest(req.toString());
+        Thread.sleep(1000);
+        assertFalse(posted.get());
+
+        req.setLength(0);
+        // intentionally bad (not a valid chunked char here)
+        for (int i=1024;i-->0;)
+            req.append("xxxxxxxxxxxx");
+        req.append("\r\n");
+        req.append("\r\n");
+
+        endp.addInput(req.toString());
+
+        endp.waitUntilClosedOrIdleFor(1,TimeUnit.SECONDS);
+        String resp = endp.takeOutputString();
+
+        assertThat(resp,startsWith("HTTP/1.1 200 OK")); // exception eaten by handler
+        assertTrue(complete.await(5,TimeUnit.SECONDS));
+        assertThat(ex0.get(),not(nullValue()));
+        assertThat(ex1.get(),not(nullValue()));
+    }
+
+
+    @Test
+    public void testBadSplitPost() throws Exception
+    {
+        StringBuilder req = new StringBuilder();
+        req.append("POST /post HTTP/1.1\r\n");
+        req.append("Host: localhost\r\n");
+        req.append("Connection: close\r\n");
+        req.append("Transfer-Encoding: chunked\r\n");
+        req.append("\r\n");
+        req.append("6\r\n");
+        req.append("Hello ");
+        req.append("\r\n");
+
+        try (StacklessLogging scope = new StacklessLogging(ServletHandler.class))
+        {
+            LocalConnector.LocalEndPoint endp=connector.executeRequest(req.toString());
+            req.setLength(0);
+
+            while(!posted.get())
+                Thread.sleep(100);
+            Thread.sleep(100);
+            req.append("x\r\n");
+            req.append("World\n");
+            req.append("\r\n");
+            req.append("0\r\n");
+            req.append("\r\n");
+            endp.addInput(req.toString());
+
+            endp.waitUntilClosedOrIdleFor(1,TimeUnit.SECONDS);
+            String resp = endp.takeOutputString();
+            assertThat("resp", resp, containsString("HTTP/1.1 200 "));
+            assertThat("resp", resp, not(containsString("\r\n0\r\n"))); // aborted
+        }
+        assertTrue(complete.await(5,TimeUnit.SECONDS));
+        assertThat(ex0.get(),not(nullValue()));
+        assertThat(ex1.get(),not(nullValue()));
+    }
+
+}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java
index 372b929..e46aab4 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java
@@ -18,37 +18,29 @@
 
 package org.eclipse.jetty.servlet;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.startsWith;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
 
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.URI;
+import java.net.URLDecoder;
+import java.nio.ByteBuffer;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.server.LocalConnector;
 import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.util.IO;
 import org.junit.AfterClass;
-import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class ResponseHeadersTest
 {
-    /** Pretend to be a WebSocket Upgrade (not real) */
-    @SuppressWarnings("serial")
-    private static class SimulateUpgradeServlet extends HttpServlet
+    public static class SimulateUpgradeServlet extends HttpServlet
     {
         @Override
         protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException
@@ -61,35 +53,44 @@
         }
     }
 
+    public static class MultilineResponseValueServlet extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException
+        {
+            // The bad use-case
+            String pathInfo = req.getPathInfo();
+            if(pathInfo != null && pathInfo.length() > 1 && pathInfo.startsWith("/"))
+            {
+                pathInfo = pathInfo.substring(1);
+            }
+            response.setHeader("X-example", pathInfo);
+
+            // The correct use
+            response.setContentType("text/plain");
+            response.setCharacterEncoding("utf-8");
+            response.getWriter().println("Got request uri - " + req.getRequestURI());
+        }
+    }
+
     private static Server server;
-    private static ServerConnector connector;
-    private static URI serverUri;
+    private static LocalConnector connector;
 
     @BeforeClass
     public static void startServer() throws Exception
     {
-        // Configure Server
         server = new Server();
-        connector = new ServerConnector(server);
+        connector = new LocalConnector(server);
         server.addConnector(connector);
 
         ServletContextHandler context = new ServletContextHandler();
         context.setContextPath("/");
         server.setHandler(context);
 
-        // Serve capture servlet
-        context.addServlet(new ServletHolder(new SimulateUpgradeServlet()),"/*");
+        context.addServlet(new ServletHolder(new SimulateUpgradeServlet()),"/ws/*");
+        context.addServlet(new ServletHolder(new MultilineResponseValueServlet()),"/multiline/*");
 
-        // Start Server
         server.start();
-
-        String host = connector.getHost();
-        if (host == null)
-        {
-            host = "localhost";
-        }
-        int port = connector.getLocalPort();
-        serverUri = new URI(String.format("http://%s:%d/",host,port));
     }
 
     @AfterClass
@@ -106,62 +107,46 @@
     }
 
     @Test
-    public void testResponseHeaderFormat() throws IOException
+    public void testResponseWebSocketHeaderFormat() throws Exception
     {
-        Socket socket = new Socket();
-        SocketAddress endpoint = new InetSocketAddress(serverUri.getHost(),serverUri.getPort());
-        socket.connect(endpoint);
+        HttpTester.Request request = new HttpTester.Request();
+        request.setMethod("GET");
+        request.setURI("/ws/");
+        request.setVersion(HttpVersion.HTTP_1_1);
+        request.setHeader("Host", "test");
 
-        StringBuilder req = new StringBuilder();
-        req.append("GET / HTTP/1.1\r\n");
-        req.append(String.format("Host: %s:%d\r\n",serverUri.getHost(),serverUri.getPort()));
-        req.append("\r\n");
+        ByteBuffer responseBuffer = connector.getResponse(request.generate());
+        HttpTester.Response response = HttpTester.parseResponse(responseBuffer);
 
-        OutputStream out = null;
-        InputStream in = null;
-        try
-        {
-            out = socket.getOutputStream();
-            in = socket.getInputStream();
-
-            // Write request
-            out.write(req.toString().getBytes());
-            out.flush();
-
-            // Read response
-            String respHeader = readResponseHeader(in);
-
-            // Now test for properly formatted HTTP Response Headers.
-            Assert.assertThat("Response Code",respHeader,startsWith("HTTP/1.1 101 Switching Protocols"));
-            Assert.assertThat("Response Header Upgrade",respHeader,containsString("Upgrade: WebSocket\r\n"));
-            Assert.assertThat("Response Header Connection",respHeader,containsString("Connection: Upgrade\r\n"));
-        }
-        finally
-        {
-            IO.close(in);
-            IO.close(out);
-            socket.close();
-        }
+        // Now test for properly formatted HTTP Response Headers.
+        assertThat("Response Code",response.getStatus(),is(101));
+        assertThat("Response Header Upgrade",response.get("Upgrade"),is("WebSocket"));
+        assertThat("Response Header Connection",response.get("Connection"),is("Upgrade"));
     }
 
-    private String readResponseHeader(InputStream in) throws IOException
+    @Test
+    public void testMultilineResponseHeaderValue() throws Exception
     {
-        InputStreamReader isr = new InputStreamReader(in);
-        BufferedReader reader = new BufferedReader(isr);
-        StringBuilder header = new StringBuilder();
-        // Read the response header
-        String line = reader.readLine();
-        Assert.assertNotNull(line);
-        Assert.assertThat(line,startsWith("HTTP/1.1 "));
-        header.append(line).append("\r\n");
-        while ((line = reader.readLine()) != null)
-        {
-            if (line.trim().length() == 0)
-            {
-                break;
-            }
-            header.append(line).append("\r\n");
-        }
-        return header.toString();
+        String actualPathInfo = "%0A%20Content-Type%3A%20image/png%0A%20Content-Length%3A%208%0A%20%0A%20yuck<!--";
+
+        HttpTester.Request request = new HttpTester.Request();
+        request.setMethod("GET");
+        request.setURI("/multiline/" + actualPathInfo);
+        request.setVersion(HttpVersion.HTTP_1_1);
+        request.setHeader("Connection", "close");
+        request.setHeader("Host", "test");
+
+        ByteBuffer responseBuffer = connector.getResponse(request.generate());
+        // System.err.println(BufferUtil.toUTF8String(responseBuffer));
+        HttpTester.Response response = HttpTester.parseResponse(responseBuffer);
+
+        // Now test for properly formatted HTTP Response Headers.
+        assertThat("Response Code",response.getStatus(),is(200));
+        assertThat("Response Header Content-Type",response.get("Content-Type"),is("text/plain;charset=UTF-8"));
+
+        String expected = actualPathInfo.replaceAll("%0A", " "); // replace OBS fold with space
+        expected = URLDecoder.decode(expected, "utf-8"); // decode the rest
+        expected = expected.trim(); // trim whitespace at start/end
+        assertThat("Response Header X-example", response.get("X-Example"), is(expected));
     }
 }
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/SSLAsyncIOServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/SSLAsyncIOServletTest.java
index c33284c..fb21077 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/SSLAsyncIOServletTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/SSLAsyncIOServletTest.java
@@ -18,14 +18,14 @@
 
 package org.eclipse.jetty.servlet;
 
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStreamReader;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.Socket;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Random;
+
 import javax.servlet.AsyncContext;
 import javax.servlet.ServletException;
 import javax.servlet.ServletOutputStream;
@@ -34,10 +34,9 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpParser;
-import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.junit.After;
 import org.junit.Assert;
@@ -48,7 +47,7 @@
 @RunWith(Parameterized.class)
 public class SSLAsyncIOServletTest
 {
-    @Parameterized.Parameters
+    @Parameterized.Parameters(name = "ssl={0}")
     public static Collection<SslContextFactory[]> parameters()
     {
         return Arrays.asList(new SslContextFactory[]{null}, new SslContextFactory[]{new SslContextFactory()});
@@ -158,16 +157,16 @@
             String request = "" +
                     "GET " + contextPath + servletPath + " HTTP/1.1\r\n" +
                     "Host: localhost\r\n" +
+                    "Connection: close\r\n" +
                     "\r\n";
             OutputStream output = client.getOutputStream();
             output.write(request.getBytes("UTF-8"));
             output.flush();
-
-            BufferedReader input = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8"));
-            SimpleHttpParser parser = new SimpleHttpParser();
-            SimpleHttpResponse response = parser.readResponse(input);
-            Assert.assertEquals("200", response.getCode());
-            Assert.assertArrayEquals(content, response.getBody().getBytes("UTF-8"));
+    
+            InputStream inputStream = client.getInputStream();
+            HttpTester.Response response = HttpTester.parseResponse(inputStream);
+            Assert.assertEquals(200, response.getStatus());
+            Assert.assertArrayEquals(content, response.getContent().getBytes("UTF-8"));
         }
     }
 
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java
index c1e3490..0fa8420 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java
@@ -27,6 +27,7 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -48,8 +49,12 @@
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
 import org.eclipse.jetty.server.handler.HandlerList;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
 import org.eclipse.jetty.server.handler.ResourceHandler;
+import org.eclipse.jetty.server.handler.gzip.GzipHandler;
 import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.util.DecoratedObjectFactory;
+import org.eclipse.jetty.util.Decorator;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
@@ -172,6 +177,55 @@
         response = _connector.getResponses(request.toString());
         assertResponseContains("Hello World", response);
     }
+    
+    @Test
+    public void testHandlerBeforeServletHandler() throws Exception
+    {
+        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
+        
+        HandlerWrapper extra = new HandlerWrapper();
+        
+        context.getSessionHandler().insertHandler(extra);
+        
+        context.addServlet(TestServlet.class,"/test");
+        context.setContextPath("/");
+        _server.setHandler(context);
+        _server.start();
+
+        StringBuffer request = new StringBuffer();
+        request.append("GET /test HTTP/1.0\n");
+        request.append("Host: localhost\n");
+        request.append("\n");
+
+        String response = _connector.getResponses(request.toString());
+        assertResponseContains("Test", response);
+        
+        assertEquals(extra,context.getSessionHandler().getHandler());
+    }
+    
+    @Test
+    public void testGzipHandlerOption() throws Exception
+    {
+        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS|ServletContextHandler.GZIP);
+        GzipHandler gzip = context.getGzipHandler();        
+        _server.start();
+        assertEquals(context.getSessionHandler(),context.getHandler());
+        assertEquals(gzip,context.getSessionHandler().getHandler());
+        assertEquals(context.getServletHandler(),gzip.getHandler());
+    }
+    
+    @Test
+    public void testGzipHandlerSet() throws Exception
+    {
+        ServletContextHandler context = new ServletContextHandler();
+        context.setSessionHandler(new SessionHandler());
+        context.setGzipHandler(new GzipHandler());
+        GzipHandler gzip = context.getGzipHandler();        
+        _server.start();
+        assertEquals(context.getSessionHandler(),context.getHandler());
+        assertEquals(gzip,context.getSessionHandler().getHandler());
+        assertEquals(context.getServletHandler(),gzip.getHandler());
+    }
 
     @Test
     public void testReplaceServletHandlerWithServlet() throws Exception
@@ -268,7 +322,7 @@
 
         ResourceHandler rh = new ResourceHandler();
 
-        servletContextHandler.setHandler(rh);    
+        servletContextHandler.insertHandler(rh);    
         assertEquals(shandler, servletContextHandler.getServletHandler());
         assertEquals(rh, servletContextHandler.getHandler());
         assertEquals(rh.getHandler(), shandler);
@@ -310,6 +364,61 @@
         Assert.assertThat(response, Matchers.containsString("404 Fell Through"));
         
     }
+    
+    /**
+     * Test behavior of legacy ServletContextHandler.Decorator, with
+     * new DecoratedObjectFactory class
+     * @throws Exception on test failure
+     */
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testLegacyDecorator() throws Exception
+    {
+        ServletContextHandler context = new ServletContextHandler();
+        context.addDecorator(new DummyLegacyDecorator());
+        _server.setHandler(context);
+        
+        context.addServlet(DecoratedObjectFactoryServlet.class, "/objfactory/*");
+        _server.start();
+
+        String response= _connector.getResponses("GET /objfactory/ HTTP/1.0\r\n\r\n");
+        assertThat("Response status code", response, containsString("200 OK"));
+        
+        String expected = String.format("Attribute[%s] = %s", DecoratedObjectFactory.ATTR, DecoratedObjectFactory.class.getName());
+        assertThat("Has context attribute", response, containsString(expected));
+        
+        assertThat("Decorators size", response, containsString("Decorators.size = [2]"));
+        
+        expected = String.format("decorator[] = %s", DummyLegacyDecorator.class.getName());
+        assertThat("Specific Legacy Decorator", response, containsString(expected));
+    }
+    
+    /**
+     * Test behavior of new {@link org.eclipse.jetty.util.Decorator}, with
+     * new DecoratedObjectFactory class
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testUtilDecorator() throws Exception
+    {
+        ServletContextHandler context = new ServletContextHandler();
+        context.getObjectFactory().addDecorator(new DummyUtilDecorator());
+        _server.setHandler(context);
+        
+        context.addServlet(DecoratedObjectFactoryServlet.class, "/objfactory/*");
+        _server.start();
+
+        String response= _connector.getResponses("GET /objfactory/ HTTP/1.0\r\n\r\n");
+        assertThat("Response status code", response, containsString("200 OK"));
+        
+        String expected = String.format("Attribute[%s] = %s", DecoratedObjectFactory.ATTR, DecoratedObjectFactory.class.getName());
+        assertThat("Has context attribute", response, containsString(expected));
+        
+        assertThat("Decorators size", response, containsString("Decorators.size = [2]"));
+        
+        expected = String.format("decorator[] = %s", DummyUtilDecorator.class.getName());
+        assertThat("Specific Legacy Decorator", response, containsString(expected));
+    }
 
     private int assertResponseContains(String expected, String response)
     {
@@ -340,6 +449,66 @@
             writer.write("Hello World");
         }
     }
+    
+    public static class DummyUtilDecorator implements org.eclipse.jetty.util.Decorator
+    {
+        @Override
+        public <T> T decorate(T o)
+        {
+            return o;
+        }
+
+        @Override
+        public void destroy(Object o)
+        {
+        }
+    }
+    
+    public static class DummyLegacyDecorator implements org.eclipse.jetty.servlet.ServletContextHandler.Decorator
+    {
+        @Override
+        public <T> T decorate(T o)
+        {
+            return o;
+        }
+
+        @Override
+        public void destroy(Object o)
+        {
+        }
+    }
+
+    public static class DecoratedObjectFactoryServlet extends HttpServlet
+    {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            resp.setContentType("text/plain");
+            resp.setStatus(HttpServletResponse.SC_OK);
+            PrintWriter out = resp.getWriter();
+            
+            Object obj = req.getServletContext().getAttribute(DecoratedObjectFactory.ATTR);
+            out.printf("Attribute[%s] = %s%n",DecoratedObjectFactory.ATTR,obj.getClass().getName());
+            
+            if (obj instanceof DecoratedObjectFactory)
+            {
+                out.printf("Object is a DecoratedObjectFactory%n");
+                DecoratedObjectFactory objFactory = (DecoratedObjectFactory)obj;
+                List<Decorator> decorators = objFactory.getDecorators();
+                out.printf("Decorators.size = [%d]%n",decorators.size());
+                for (Decorator decorator : decorators)
+                {
+                    out.printf(" decorator[] = %s%n",decorator.getClass().getName());
+                }
+            }
+            else
+            {
+                out.printf("Object is NOT a DecoratedObjectFactory%n");
+            }
+        }
+    }
 
     public static class TestServlet extends HttpServlet
     {
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHandlerTest.java
index 53cff6b..ec931c8 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHandlerTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHandlerTest.java
@@ -26,6 +26,8 @@
 
 import javax.servlet.DispatcherType;
 
+import org.eclipse.jetty.http.PathMap;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
 import org.eclipse.jetty.servlet.BaseHolder.Source;
 import org.junit.Before;
 import org.junit.Test;
@@ -48,6 +50,15 @@
     FilterMapping fm5 = new FilterMapping();
     
     
+    ServletHolder sh1 = new ServletHolder(Source.DESCRIPTOR);
+    ServletMapping sm1 = new ServletMapping();
+    
+    ServletHolder sh2 = new ServletHolder(Source.DESCRIPTOR);
+    ServletMapping sm2 = new ServletMapping();
+    
+    ServletHolder sh3 = new ServletHolder(Source.DESCRIPTOR);
+    ServletMapping sm3 = new ServletMapping();
+    
 
     @Before
     public void initMappings()
@@ -71,6 +82,241 @@
         fh5.setName("fh5");
         fm5.setPathSpec("/*");
         fm5.setFilterHolder(fh5);
+        
+        sh1.setName("s1");
+        sm1.setDefault(false);
+        sm1.setPathSpec("/foo/*");
+        sm1.setServletName("s1");
+        
+        sh2.setName("s2");
+        sm2.setDefault(false);
+        sm2.setPathSpec("/foo/*");
+        sm2.setServletName("s2");
+        
+        sh3.setName("s3");
+        sm3.setDefault(true);
+        sm3.setPathSpec("/foo/*");
+        sm3.setServletName("s3");
+        
+        
+    }
+    
+    @Test
+    public void testAddFilterIgnoresDuplicates() throws Exception
+    {
+
+        ServletHandler handler = new ServletHandler();
+        FilterHolder h = new FilterHolder();
+        h.setName("x");
+        handler.addFilter(h);
+        FilterHolder[] holders = handler.getFilters();
+        assertNotNull(holders);
+        assertTrue(holders[0]==h);
+        
+        handler.addFilter(h);
+        holders = handler.getFilters();
+        assertNotNull(holders);
+        assertTrue(holders.length == 1);
+        assertTrue(holders[0] == h);
+        
+        FilterHolder h2 = new FilterHolder();
+        h2.setName("x"); //not allowed by servlet spec, just here to test object equality
+        handler.addFilter(h2);
+        holders = handler.getFilters();
+        assertNotNull(holders);
+        assertTrue(holders.length == 2);
+        assertTrue(holders[1] == h2);
+    }
+    
+    @Test
+    public void testAddFilterIgnoresDuplicates2() throws Exception
+    {
+
+        ServletHandler handler = new ServletHandler();
+        FilterHolder h = new FilterHolder();
+        h.setName("x");
+        FilterMapping m = new FilterMapping();
+        m.setPathSpec("/*");
+        m.setFilterHolder(h);
+        
+        
+        handler.addFilter(h,m);
+        FilterHolder[] holders = handler.getFilters();
+        assertNotNull(holders);
+        assertTrue(holders[0]==h);
+        
+        
+        FilterMapping m2 = new FilterMapping();
+        m2.setPathSpec("/*");
+        m2.setFilterHolder(h);
+        handler.addFilter(h, m2);
+        holders = handler.getFilters();
+        assertNotNull(holders);
+        assertTrue(holders.length == 1);
+        assertTrue(holders[0] == h);
+        
+        FilterHolder h2 = new FilterHolder();
+        h2.setName("x"); //not allowed by servlet spec, just here to test object equality
+        FilterMapping m3 = new FilterMapping();
+        m3.setPathSpec("/*");
+        m3.setFilterHolder(h);
+        
+        handler.addFilter(h2, m3);
+        holders = handler.getFilters();
+        assertNotNull(holders);
+        assertTrue(holders.length == 2);
+        assertTrue(holders[1] == h2);
+    }
+    
+    
+    @Test
+    public void testAddFilterWithMappingIgnoresDuplicateFilters() throws Exception
+    {
+        ServletHandler handler = new ServletHandler();
+        FilterHolder h = new FilterHolder();
+        h.setName("x");
+ 
+        
+        
+        handler.addFilterWithMapping(h,"/*", 0);
+        FilterHolder[] holders = handler.getFilters();
+        assertNotNull(holders);
+        assertTrue(holders[0]==h);
+        
+        handler.addFilterWithMapping(h, "/*", 1);
+        holders = handler.getFilters();
+        assertNotNull(holders);
+        assertTrue(holders.length == 1);
+        assertTrue(holders[0] == h);
+        
+        FilterHolder h2 = new FilterHolder();
+        h2.setName("x"); //not allowed by servlet spec, just here to test object equality
+        
+        handler.addFilterWithMapping(h2, "/*", 0);
+        holders = handler.getFilters();
+        assertNotNull(holders);
+        assertTrue(holders.length == 2);
+        assertTrue(holders[1] == h2);
+    }
+    
+    
+    @Test
+    public void testAddFilterWithMappingIngoresDuplicateFilters2 () throws Exception
+    {
+        ServletHandler handler = new ServletHandler();
+        FilterHolder h = new FilterHolder();
+        h.setName("x");
+ 
+        
+        
+        handler.addFilterWithMapping(h,"/*", EnumSet.allOf(DispatcherType.class));
+        FilterHolder[] holders = handler.getFilters();
+        assertNotNull(holders);
+        assertTrue(holders[0]==h);
+        
+        handler.addFilterWithMapping(h, "/x", EnumSet.allOf(DispatcherType.class));
+        holders = handler.getFilters();
+        assertNotNull(holders);
+        assertTrue(holders.length == 1);
+        assertTrue(holders[0] == h);
+        
+        FilterHolder h2 = new FilterHolder();
+        h2.setName("x"); //not allowed by servlet spec, just here to test object equality
+        
+        handler.addFilterWithMapping(h2, "/*", EnumSet.allOf(DispatcherType.class));
+        holders = handler.getFilters();
+        assertNotNull(holders);
+        assertTrue(holders.length == 2);
+        assertTrue(holders[1] == h2);
+    }
+    
+    
+    @Test
+    public void testDuplicateMappingsForbidden() throws Exception
+    {
+        ServletHandler handler = new ServletHandler();
+        handler.setAllowDuplicateMappings(false);
+        handler.addServlet(sh1);
+        handler.addServlet(sh2);
+        handler.updateNameMappings();
+        
+        handler.addServletMapping(sm1);
+        handler.addServletMapping(sm2);
+
+        try
+        {
+            handler.updateMappings();
+        }
+        catch (IllegalStateException e)
+        {
+            //expected error
+        }
+    }
+
+
+    @Test
+    public void testDuplicateMappingsWithDefaults() throws Exception
+    {
+        ServletHandler handler = new ServletHandler();
+        handler.setAllowDuplicateMappings(false);
+        handler.addServlet(sh1);
+        handler.addServlet(sh3);
+        handler.updateNameMappings();
+
+        handler.addServletMapping(sm3);
+        handler.addServletMapping(sm1);
+       
+
+        handler.updateMappings();
+
+        PathMap.MappedEntry<ServletHolder> entry=handler.getHolderEntry("/foo/*");
+        assertNotNull(entry);
+        assertEquals("s1", entry.getValue().getName());
+    }
+    
+    
+    @Test
+    public void testDuplicateMappingsSameServlet() throws Exception
+    {
+        ServletHolder sh4 = new ServletHolder();
+       
+        sh4.setName("s1");
+        
+        ServletMapping sm4 = new ServletMapping();
+        sm4.setPathSpec("/foo/*");
+        sm4.setServletName("s1");
+        
+        ServletHandler handler = new ServletHandler();
+        handler.setAllowDuplicateMappings(true);
+        handler.addServlet(sh1);
+        handler.addServlet(sh4);
+        handler.updateNameMappings();
+
+        handler.addServletMapping(sm1);
+        handler.addServletMapping(sm4);
+       
+
+        handler.updateMappings();
+    }
+
+
+    
+    @Test
+    public void testDuplicateMappingsAllowed() throws Exception
+    {
+        ServletHandler handler = new ServletHandler();
+        handler.setAllowDuplicateMappings(true);
+        handler.addServlet(sh1);
+        handler.addServlet(sh2);
+        handler.updateNameMappings();
+        
+        handler.addServletMapping(sm1);
+        handler.addServletMapping(sm2);
+        handler.updateMappings();
+        
+       PathMap.MappedEntry<ServletHolder> entry=handler.getHolderEntry("/foo/*");
+       assertNotNull(entry);
+       assertEquals("s2", entry.getValue().getName());
     }
 
     @Test
@@ -428,5 +674,4 @@
     }
   
     
-    
 }
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHolderTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHolderTest.java
new file mode 100644
index 0000000..bbc14b4
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHolderTest.java
@@ -0,0 +1,140 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.servlet;
+
+import javax.servlet.UnavailableException;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.MultiException;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ServletHolderTest {
+
+    @Test
+    public void testTransitiveCompareTo() throws Exception
+    {
+        // example of jsp-file referenced in web.xml
+        final ServletHolder one = new ServletHolder();
+        one.setInitOrder(-1);
+        one.setName("Login");
+        one.setClassName(null);
+
+        // example of pre-compiled jsp
+        final ServletHolder two = new ServletHolder();
+        two.setInitOrder(-1);
+        two.setName("org.my.package.jsp.WEB_002dINF.pages.precompiled_002dpage_jsp");
+        two.setClassName("org.my.package.jsp.WEB_002dINF.pages.precompiled_002dpage_jsp");
+
+        // example of servlet referenced in web.xml
+        final ServletHolder three = new ServletHolder();
+        three.setInitOrder(-1);
+        three.setName("Download");
+        three.setClassName("org.my.package.web.DownloadServlet");
+
+        // verify compareTo transitivity
+        Assert.assertTrue(one.compareTo(two) < 0);
+        Assert.assertTrue(two.compareTo(three) < 0);
+        Assert.assertTrue(one.compareTo(three) < 0);
+    }
+    
+
+    @Test
+    public void testJspFileNameToClassName() throws Exception
+    {
+        ServletHolder h = new ServletHolder();
+        h.setName("test");
+
+
+        Assert.assertEquals(null,  h.getClassNameForJsp(null));
+
+        Assert.assertEquals(null,  h.getClassNameForJsp(""));
+
+        Assert.assertEquals(null,  h.getClassNameForJsp("/blah/"));
+
+        Assert.assertEquals(null,  h.getClassNameForJsp("//blah///"));
+
+        Assert.assertEquals(null,  h.getClassNameForJsp("/a/b/c/blah/"));
+
+        Assert.assertEquals("org.apache.jsp.a.b.c.blah",  h.getClassNameForJsp("/a/b/c/blah"));
+
+        Assert.assertEquals("org.apache.jsp.blah_jsp", h.getClassNameForJsp("/blah.jsp"));
+
+        Assert.assertEquals("org.apache.jsp.blah_jsp", h.getClassNameForJsp("//blah.jsp"));
+
+        Assert.assertEquals("org.apache.jsp.blah_jsp", h.getClassNameForJsp("blah.jsp"));
+
+        Assert.assertEquals("org.apache.jsp.a.b.c.blah_jsp", h.getClassNameForJsp("/a/b/c/blah.jsp"));
+
+        Assert.assertEquals("org.apache.jsp.a.b.c.blah_jsp", h.getClassNameForJsp("a/b/c/blah.jsp"));
+    }
+
+
+    @Test
+    public void testNoClassName() throws Exception
+    {
+        try (StacklessLogging stackless = new StacklessLogging(ServletHandler.class, ContextHandler.class, ServletContextHandler.class))
+        {
+            ServletContextHandler context = new ServletContextHandler(); 
+            ServletHandler handler = context.getServletHandler();
+            ServletHolder holder = new ServletHolder();
+            holder.setName("foo");
+            holder.setForcedPath("/blah/");
+            handler.addServlet(holder);
+            handler.start();
+            Assert.fail("No class in ServletHolder");
+        }
+        catch (UnavailableException e)
+        {
+            Assert.assertTrue(e.getMessage().contains("foo"));
+        }
+        catch (MultiException e)
+        {
+            MultiException m = (MultiException)e;
+            Assert.assertTrue(m.getCause().getMessage().contains("foo"));
+        }
+    }
+    
+    @Test
+    public void testUnloadableClassName() throws Exception
+    {
+        try (StacklessLogging stackless = new StacklessLogging(BaseHolder.class, ServletHandler.class, ContextHandler.class, ServletContextHandler.class))
+        {
+            ServletContextHandler context = new ServletContextHandler(); 
+            ServletHandler handler = context.getServletHandler();
+            ServletHolder holder = new ServletHolder();
+            holder.setName("foo");
+            holder.setClassName("a.b.c.class");
+            handler.addServlet(holder);
+            handler.start();
+            Assert.fail("Unloadable class");
+        }
+        catch (UnavailableException e)
+        {
+            Assert.assertTrue(e.getMessage().contains("foo"));
+        }
+        catch (MultiException e)
+        {
+            MultiException m = (MultiException)e;
+            Assert.assertTrue(m.getCause().getMessage().contains("foo"));
+        }
+    }
+   
+}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletRequestLogTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletRequestLogTest.java
index ea7dfdc..c5cdbf4 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletRequestLogTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletRequestLogTest.java
@@ -18,8 +18,8 @@
 
 package org.eclipse.jetty.servlet;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -79,7 +79,8 @@
         @Override
         public void log(Request request, Response response)
         {
-            captured.add(String.format("%s %s %s %03d",request.getMethod(),request.getUri().toString(),request.getProtocol(),response.getStatus()));
+            int status = response.getCommittedMetaData().getStatus();
+            captured.add(String.format("%s %s %s %03d",request.getMethod(),request.getRequestURI(),request.getProtocol(),status));
         }
     }
     
@@ -298,6 +299,7 @@
      * Test a RequestLogHandler at the end of a HandlerCollection.
      * This handler chain is setup to look like Jetty versions up to 9.2. 
      * Default configuration.
+     * @throws Exception on test failure
      */
     @Test(timeout=4000)
     public void testLogHandlerCollection() throws Exception
@@ -382,6 +384,7 @@
     /**
      * Test a RequestLogHandler at the end of a HandlerCollection.
      * and also with the default ErrorHandler as server bean in place.
+     * @throws Exception on test failure
      */
     @Test(timeout=4000)
     public void testLogHandlerCollection_ErrorHandler_ServerBean() throws Exception
@@ -469,6 +472,7 @@
     /**
      * Test a RequestLogHandler at the end of a HandlerCollection
      * using servlet specific error page mapping.
+     * @throws Exception on test failure
      */
     @Test(timeout=4000)
     public void testLogHandlerCollection_SimpleErrorPageMapping() throws Exception
@@ -558,6 +562,7 @@
     
     /**
      * Test an alternate (proposed) setup for using RequestLogHandler in a wrapped style
+     * @throws Exception on test failure
      */
     @Test(timeout=4000)
     public void testLogHandlerWrapped() throws Exception
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletTester.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletTester.java
new file mode 100644
index 0000000..a122857
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletTester.java
@@ -0,0 +1,260 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.servlet;
+
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.EnumSet;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.util.Attributes;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+
+public class ServletTester extends ContainerLifeCycle
+{
+    private static final Logger LOG = Log.getLogger(ServletTester.class);
+    
+    private final Server _server=new Server();
+    private final LocalConnector _connector=new LocalConnector(_server);
+    private final ServletContextHandler _context;
+    
+    public Server getServer()
+    {
+        return _server;
+    }
+    
+    public LocalConnector getConnector()
+    {
+        return _connector;
+    }
+    
+    public void setVirtualHosts(String[] vhosts)
+    {
+        _context.setVirtualHosts(vhosts);
+    }
+
+    public void addVirtualHosts(String[] virtualHosts)
+    {
+        _context.addVirtualHosts(virtualHosts);
+    }
+
+    public ServletHolder addServlet(String className, String pathSpec)
+    {
+        return _context.addServlet(className,pathSpec);
+    }
+
+    public ServletHolder addServlet(Class<? extends Servlet> servlet, String pathSpec)
+    {
+        return _context.addServlet(servlet,pathSpec);
+    }
+
+    public void addServlet(ServletHolder servlet, String pathSpec)
+    {
+        _context.addServlet(servlet,pathSpec);
+    }
+
+    public void addFilter(FilterHolder holder, String pathSpec, EnumSet<DispatcherType> dispatches)
+    {
+        _context.addFilter(holder,pathSpec,dispatches);
+    }
+
+    public FilterHolder addFilter(Class<? extends Filter> filterClass, String pathSpec, EnumSet<DispatcherType> dispatches)
+    {
+        return _context.addFilter(filterClass,pathSpec,dispatches);
+    }
+
+    public FilterHolder addFilter(String filterClass, String pathSpec, EnumSet<DispatcherType> dispatches)
+    {
+        return _context.addFilter(filterClass,pathSpec,dispatches);
+    }
+
+    public Object getAttribute(String name)
+    {
+        return _context.getAttribute(name);
+    }
+
+    public Enumeration<String> getAttributeNames()
+    {
+        return _context.getAttributeNames();
+    }
+
+    public Attributes getAttributes()
+    {
+        return _context.getAttributes();
+    }
+
+    public String getContextPath()
+    {
+        return _context.getContextPath();
+    }
+
+    public String getInitParameter(String name)
+    {
+        return _context.getInitParameter(name);
+    }
+
+    public String setInitParameter(String name, String value)
+    {
+        return _context.setInitParameter(name,value);
+    }
+
+    public Enumeration<String> getInitParameterNames()
+    {
+        return _context.getInitParameterNames();
+    }
+
+    public Map<String, String> getInitParams()
+    {
+        return _context.getInitParams();
+    }
+
+    public void removeAttribute(String name)
+    {
+        _context.removeAttribute(name);
+    }
+
+    public void setAttribute(String name, Object value)
+    {
+        _context.setAttribute(name,value);
+    }
+
+    public void setContextPath(String contextPath)
+    {
+        _context.setContextPath(contextPath);
+    }
+
+    public Resource getBaseResource()
+    {
+        return _context.getBaseResource();
+    }
+
+    public String getResourceBase()
+    {
+        return _context.getResourceBase();
+    }
+
+    public void setResourceBase(String resourceBase)
+    {
+        _context.setResourceBase(resourceBase);
+    }
+
+    public ServletTester()
+    {
+        this("/",ServletContextHandler.SECURITY|ServletContextHandler.SESSIONS);
+    }
+
+    public ServletTester(String ctxPath)
+    {
+        this(ctxPath,ServletContextHandler.SECURITY|ServletContextHandler.SESSIONS);
+    }
+
+    public ServletTester(String contextPath,int options)
+    {
+        _context=new ServletContextHandler(_server,contextPath,options);
+        _server.setConnectors(new Connector[]{_connector});
+        addBean(_server);
+    }
+
+    public ServletContextHandler getContext()
+    {
+        return _context;
+    }
+
+    public String getResponses(String request) throws Exception
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("Request: {}",request);
+        }
+        return _connector.getResponses(request);
+    }
+    
+    public String getResponses(String request, long idleFor,TimeUnit units) throws Exception
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("Request: {}",request);
+        }
+        return _connector.getResponses(request, idleFor, units);
+    }
+    
+    public ByteBuffer getResponses(ByteBuffer request) throws Exception
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("Request (Buffer): {}",BufferUtil.toUTF8String(request));
+        }
+        return _connector.getResponses(request);
+    }
+    
+    public ByteBuffer getResponses(ByteBuffer requestsBuffer,long idleFor,TimeUnit units) throws Exception
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("Requests (Buffer): {}",BufferUtil.toUTF8String(requestsBuffer));
+        }
+        return _connector.getResponses(requestsBuffer, idleFor, units);
+    }
+
+    /** Create a port based connector.
+     * This methods adds a port connector to the server
+     * @param localhost true if connector should use localhost, false for default host behavior.
+     * @return A URL to access the server via the connector.
+     * @throws Exception on test failure
+     */
+    public String createConnector(boolean localhost) throws Exception
+    {
+        ServerConnector connector = new ServerConnector(_server);
+        if (localhost)
+            connector.setHost("127.0.0.1");
+        _server.addConnector(connector);
+        if (_server.isStarted())
+            connector.start();
+        else
+            connector.open();
+
+        return "http://"+(localhost?"127.0.0.1":
+            InetAddress.getLocalHost().getHostAddress()
+        )+":"+connector.getLocalPort();
+    }
+
+    public LocalConnector createLocalConnector()
+    {
+        LocalConnector connector = new LocalConnector(_server);
+        _server.addConnector(connector);
+        return connector;
+    }
+
+
+
+}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/StatisticsServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/StatisticsServletTest.java
new file mode 100644
index 0000000..5ccbc27
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/StatisticsServletTest.java
@@ -0,0 +1,154 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.servlet;
+
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.StatisticsHandler;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.xml.sax.InputSource;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathFactory;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.nio.ByteBuffer;
+
+public class StatisticsServletTest
+{
+    private Server _server;
+
+    private LocalConnector _connector;
+
+    @Before
+    public void createServer()
+    {
+        _server = new Server();
+        _connector = new LocalConnector( _server );
+        _server.addConnector( _connector );
+    }
+
+
+    @After
+    public void destroyServer()
+        throws Exception
+    {
+        _server.stop();
+        _server.join();
+    }
+
+    @Test
+    public void getStats()
+        throws Exception
+    {
+        StatisticsHandler statsHandler = new StatisticsHandler();
+        _server.setHandler(statsHandler);
+        ServletContextHandler statsContext = new ServletContextHandler(statsHandler, "/");
+        statsContext.addServlet( new ServletHolder( new TestServlet() ), "/test1" );
+        ServletHolder servletHolder = new ServletHolder( new StatisticsServlet() );
+        servletHolder.setInitParameter( "restrictToLocalhost", "false" );
+        statsContext.addServlet( servletHolder, "/stats" );
+        statsContext.setSessionHandler( new SessionHandler() );
+        _server.start();
+
+        getResponse("/test1" );
+        String response = getResponse("/stats?xml=true" );
+        Stats stats = parseStats( response );
+
+        Assert.assertEquals(1, stats.responses2xx);
+
+        getResponse("/stats?statsReset=true" );
+        response = getResponse("/stats?xml=true" );
+        stats = parseStats( response );
+
+        Assert.assertEquals(1, stats.responses2xx);
+
+        getResponse("/test1" );
+        getResponse("/nothing" );
+        response = getResponse("/stats?xml=true" );
+        stats = parseStats( response );
+
+        Assert.assertEquals(3, stats.responses2xx);
+        Assert.assertEquals(1, stats.responses4xx);
+    }
+
+    public String getResponse( String path )
+        throws Exception
+    {
+        HttpTester.Request request = new HttpTester.Request();
+        request.setMethod( "GET" );
+        request.setURI( path );
+        request.setVersion( HttpVersion.HTTP_1_1 );
+        request.setHeader( "Host", "test" );
+
+        ByteBuffer responseBuffer = _connector.getResponse( request.generate() );
+        return HttpTester.parseResponse( responseBuffer ).getContent();
+    }
+
+
+    public Stats parseStats( String xml )
+        throws Exception
+    {
+        XPath xPath = XPathFactory.newInstance().newXPath();
+
+        String responses4xx = xPath.evaluate( "//responses4xx", new InputSource( new StringReader( xml ) ) );
+
+        String responses2xx = xPath.evaluate( "//responses2xx", new InputSource( new StringReader( xml ) ) );
+
+        return new Stats(Integer.parseInt( responses2xx), Integer.parseInt( responses4xx ));
+    }
+
+    public static class Stats
+    {
+        int responses2xx,responses4xx;
+
+        public Stats( int responses2xx, int responses4xx )
+        {
+            this.responses2xx = responses2xx;
+            this.responses4xx = responses4xx;
+        }
+    }
+
+
+    public static class TestServlet
+        extends HttpServlet
+    {
+
+        @Override
+        protected void doGet( HttpServletRequest req, HttpServletResponse resp )
+            throws ServletException, IOException
+        {
+            resp.setStatus( HttpServletResponse.SC_OK );
+            PrintWriter writer = resp.getWriter();
+            writer.write( "Yup!!" );
+        }
+    }
+
+}
diff --git a/jetty-servlet/src/test/resources/jetty-logging.properties b/jetty-servlet/src/test/resources/jetty-logging.properties
index 50696d6..ed4316e 100644
--- a/jetty-servlet/src/test/resources/jetty-logging.properties
+++ b/jetty-servlet/src/test/resources/jetty-logging.properties
@@ -1,5 +1,7 @@
 org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+org.eclipse.jetty.LEVEL=INFO
 #org.eclipse.jetty.LEVEL=DEBUG
 #org.eclipse.jetty.server.LEVEL=DEBUG
 #org.eclipse.jetty.servlet.LEVEL=DEBUG
 #org.eclipse.jetty.io.ChannelEndPoint.LEVEL=DEBUG
+#org.eclipse.jetty.server.DebugListener.LEVEL=DEBUG
\ No newline at end of file
diff --git a/jetty-servlets/pom.xml b/jetty-servlets/pom.xml
index 266e73f..229ab3f 100644
--- a/jetty-servlets/pom.xml
+++ b/jetty-servlets/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <artifactId>jetty-project</artifactId>
     <groupId>org.eclipse.jetty</groupId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-servlets</artifactId>
@@ -16,52 +16,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",*</Import-Package>
-              </instructions>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
@@ -109,6 +63,20 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-http</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlet</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.eclipse.jetty.toolchain</groupId>
       <artifactId>jetty-test-helper</artifactId>
       <scope>test</scope>
diff --git a/jetty-servlets/src/main/config/modules/servlets.mod b/jetty-servlets/src/main/config/modules/servlets.mod
index e8724b8..2e977c0 100644
--- a/jetty-servlets/src/main/config/modules/servlets.mod
+++ b/jetty-servlets/src/main/config/modules/servlets.mod
@@ -2,6 +2,7 @@
 # Jetty Servlets Module
 #
 
+
 [depend]
 servlet
 
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/AsyncGzipFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/AsyncGzipFilter.java
index d46eecf..a5b911b 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/AsyncGzipFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/AsyncGzipFilter.java
@@ -18,555 +18,7 @@
 
 package org.eclipse.jetty.servlets;
 
-import java.io.File;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.regex.Pattern;
-import java.util.zip.Deflater;
-
-import javax.servlet.DispatcherType;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRegistration;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpGenerator;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.MimeTypes;
-import org.eclipse.jetty.server.HttpChannel;
-import org.eclipse.jetty.server.HttpOutput;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.servlets.gzip.GzipFactory;
-import org.eclipse.jetty.servlets.gzip.GzipHttpOutput;
-import org.eclipse.jetty.util.URIUtil;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/* ------------------------------------------------------------ */
-/** Async GZIP Filter
- * This filter is a gzip filter using jetty internal mechanism to apply gzip compression
- * to output that is compatible with async IO and does not need to wrap the response nor output stream.
- * The filter will gzip the content of a response if: <ul>
- * <li>The filter is mapped to a matching path</li>
- * <li>accept-encoding header is set to either gzip, deflate or a combination of those</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>If a list of mimeTypes is set by the <code>mimeTypes</code> init parameter, then the Content-Type is in the list.</li>
- * <li>If no mimeType list is set, then the content-type is not in the list defined by <code>excludedMimeTypes</code></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 filter is mapped for static content, then use of efficient direct NIO may be
- * prevented, thus use of the gzip mechanism of the {@link org.eclipse.jetty.servlet.DefaultServlet} is
- * advised instead.
- * </p>
- * <p>
- * This filter extends {@link UserAgentFilter} and if the the initParameter <code>excludedAgents</code>
- * is set to a comma separated list of user agents, then these agents will be excluded from gzip content.
- * </p>
- * <p>Init Parameters:</p>
- * <dl>
- * <dt>bufferSize</dt>       <dd>The output buffer size. Defaults to 8192. Be careful as values <= 0 will lead to an
- *                            {@link IllegalArgumentException}.
- *                            See: {@link java.util.zip.GZIPOutputStream#GZIPOutputStream(java.io.OutputStream, int)}
- *                            and: {@link java.util.zip.DeflaterOutputStream#DeflaterOutputStream(java.io.OutputStream, Deflater, int)}
- * </dd>
- * <dt>minGzipSize</dt>       <dd>Content will only be compressed if content length is either unknown or greater
- *                            than <code>minGzipSize</code>.
- * </dd>
- * <dt>deflateCompressionLevel</dt>       <dd>The compression level used for deflate compression. (0-9).
- *                            See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
- * </dd>
- * <dt>deflateNoWrap</dt>       <dd>The noWrap setting for deflate compression. Defaults to true. (true/false)
- *                            See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
- * </dd>
- * <dt>methods</dt>       <dd>Comma separated list of HTTP methods to compress. If not set, only GET requests are compressed.
- *  </dd>
- * <dt>mimeTypes</dt>       <dd>Comma separated list of mime types to compress. If it is not set, then the excludedMimeTypes list is used.
- * </dd>
- * <dt>excludedMimeTypes</dt>       <dd>Comma separated list of mime types to never compress. If not set, then the default is the commonly known
- * image, video, audio and compressed types.
- * </dd>
-
- * <dt>excludedAgents</dt>       <dd>Comma separated list of user agents to exclude from compression. Does a
- *                            {@link String#contains(CharSequence)} to check if the excluded agent occurs
- *                            in the user-agent header. If it does -> no compression
- * </dd>
- * <dt>excludeAgentPatterns</dt>       <dd>Same as excludedAgents, but accepts regex patterns for more complex matching.
- * </dd>
- * <dt>excludePaths</dt>       <dd>Comma separated list of paths to exclude from compression.
- *                            Does a {@link String#startsWith(String)} comparison to check if the path matches.
- *                            If it does match -> no compression. To match subpaths use <code>excludePathPatterns</code>
- *                            instead.
- * </dd>
- * <dt>excludePathPatterns</dt>       <dd>Same as excludePath, but accepts regex patterns for more complex matching.
- * </dd>
- * <dt>vary</dt>       <dd>Set to the value of the Vary header sent with responses that could be compressed.  By default it is 
- *                            set to 'Vary: 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 'Vary: Accept-Encoding'.  Note also 
- *                            that shared caches may cache 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.
- * </dd>                         
- * <dt>checkGzExists</dt>       <dd>If set to true, the filter check if a static resource with ".gz" appended exists.  If so then
- *                            the normal processing is done so that the default servlet can send  the pre existing gz content. If not
- *                            set, defaults to the same as the default servlet "gzip" parameter.
- *  </dd>
- *  </dl>
- */
-public class AsyncGzipFilter extends UserAgentFilter implements GzipFactory
+@Deprecated
+public class AsyncGzipFilter extends GzipFilter
 {
-    private static final Logger LOG = Log.getLogger(GzipFilter.class);
-    public final static String GZIP = "gzip";
-    public static final String DEFLATE = "deflate";
-    public final static String ETAG_GZIP="--gzip";
-    public final static String ETAG = "o.e.j.s.GzipFilter.ETag";
-    public final static int DEFAULT_MIN_GZIP_SIZE=256;
-
-    protected ServletContext _context;
-    protected final Set<String> _mimeTypes=new HashSet<>();
-    protected boolean _excludeMimeTypes;
-    protected int _bufferSize=32*1024;
-    protected int _minGzipSize=DEFAULT_MIN_GZIP_SIZE;
-    protected int _deflateCompressionLevel=Deflater.DEFAULT_COMPRESSION;
-    protected boolean _deflateNoWrap = true;
-    protected boolean _checkGzExists = true;
-    
-    // non-static, as other GzipFilter instances may have different configurations
-    protected final ThreadLocal<Deflater> _deflater = new ThreadLocal<Deflater>();
-
-    protected final static ThreadLocal<byte[]> _buffer= new ThreadLocal<byte[]>();
-
-    protected final Set<String> _methods=new HashSet<String>();
-    protected Set<String> _excludedAgents;
-    protected Set<Pattern> _excludedAgentPatterns;
-    protected Set<String> _excludedPaths;
-    protected Set<Pattern> _excludedPathPatterns;
-    protected HttpField _vary=new HttpGenerator.CachedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING+", "+HttpHeader.USER_AGENT);
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.UserAgentFilter#init(javax.servlet.FilterConfig)
-     */
-    @Override
-    public void init(FilterConfig filterConfig) throws ServletException
-    {
-        super.init(filterConfig);
-
-        _context=filterConfig.getServletContext();
-        
-        String tmp=filterConfig.getInitParameter("bufferSize");
-        if (tmp!=null)
-            _bufferSize=Integer.parseInt(tmp);
-        LOG.debug("{} bufferSize={}",this,_bufferSize);
-
-        tmp=filterConfig.getInitParameter("minGzipSize");
-        if (tmp!=null)
-            _minGzipSize=Integer.parseInt(tmp);
-        LOG.debug("{} minGzipSize={}",this,_minGzipSize);
-
-        tmp=filterConfig.getInitParameter("deflateCompressionLevel");
-        if (tmp!=null)
-            _deflateCompressionLevel=Integer.parseInt(tmp);
-        LOG.debug("{} deflateCompressionLevel={}",this,_deflateCompressionLevel);
-
-        tmp=filterConfig.getInitParameter("deflateNoWrap");
-        if (tmp!=null)
-            _deflateNoWrap=Boolean.parseBoolean(tmp);
-        LOG.debug("{} deflateNoWrap={}",this,_deflateNoWrap);
-
-        tmp=filterConfig.getInitParameter("checkGzExists");
-        if (tmp!=null)
-            _checkGzExists=Boolean.parseBoolean(tmp);
-        else
-        {
-            // Look to Default servlet for default
-            ServletRegistration dftServlet = _context.getServletRegistration("default");
-            if (dftServlet!=null && dftServlet.getInitParameter("gzip")!=null)
-                _checkGzExists=Boolean.parseBoolean(dftServlet.getInitParameter("gzip"));
-        }
-        LOG.debug("{} checkGzExists={}",this,_checkGzExists);
-        
-        tmp=filterConfig.getInitParameter("methods");
-        if (tmp!=null)
-        {
-            StringTokenizer tok = new StringTokenizer(tmp,",",false);
-            while (tok.hasMoreTokens())
-                _methods.add(tok.nextToken().trim().toUpperCase(Locale.ENGLISH));
-        }
-        else
-            _methods.add(HttpMethod.GET.asString());
-        LOG.debug("{} methods={}",this,_methods);
-        
-        tmp=filterConfig.getInitParameter("mimeTypes");
-        if (tmp==null)
-        {
-            _excludeMimeTypes=true;
-            tmp=filterConfig.getInitParameter("excludedMimeTypes");
-            if (tmp==null)
-            {
-                for (String type:MimeTypes.getKnownMimeTypes())
-                {
-                    if (type.equals("image/svg+xml")) //always compressable (unless .svgz file)
-                        continue;
-                    if (type.startsWith("image/")||
-                        type.startsWith("audio/")||
-                        type.startsWith("video/"))
-                        _mimeTypes.add(type);
-                }
-                _mimeTypes.add("application/compress");
-                _mimeTypes.add("application/zip");
-                _mimeTypes.add("application/gzip");
-            }
-            else
-            {
-                StringTokenizer tok = new StringTokenizer(tmp,",",false);
-                while (tok.hasMoreTokens())
-                    _mimeTypes.add(tok.nextToken().trim());
-            }
-        }
-        else
-        {
-            StringTokenizer tok = new StringTokenizer(tmp,",",false);
-            while (tok.hasMoreTokens())
-                _mimeTypes.add(tok.nextToken().trim());
-        }
-        LOG.debug("{} mimeTypes={}",this,_mimeTypes);
-        LOG.debug("{} excludeMimeTypes={}",this,_excludeMimeTypes);
-        tmp=filterConfig.getInitParameter("excludedAgents");
-        if (tmp!=null)
-        {
-            _excludedAgents=new HashSet<String>();
-            StringTokenizer tok = new StringTokenizer(tmp,",",false);
-            while (tok.hasMoreTokens())
-               _excludedAgents.add(tok.nextToken().trim());
-        }
-        LOG.debug("{} excludedAgents={}",this,_excludedAgents);
-
-        tmp=filterConfig.getInitParameter("excludeAgentPatterns");
-        if (tmp!=null)
-        {
-            _excludedAgentPatterns=new HashSet<Pattern>();
-            StringTokenizer tok = new StringTokenizer(tmp,",",false);
-            while (tok.hasMoreTokens())
-                _excludedAgentPatterns.add(Pattern.compile(tok.nextToken().trim()));
-        }
-        LOG.debug("{} excludedAgentPatterns={}",this,_excludedAgentPatterns);
-
-        tmp=filterConfig.getInitParameter("excludePaths");
-        if (tmp!=null)
-        {
-            _excludedPaths=new HashSet<String>();
-            StringTokenizer tok = new StringTokenizer(tmp,",",false);
-            while (tok.hasMoreTokens())
-                _excludedPaths.add(tok.nextToken().trim());
-        }
-        LOG.debug("{} excludedPaths={}",this,_excludedPaths);
-
-        tmp=filterConfig.getInitParameter("excludePathPatterns");
-        if (tmp!=null)
-        {
-            _excludedPathPatterns=new HashSet<Pattern>();
-            StringTokenizer tok = new StringTokenizer(tmp,",",false);
-            while (tok.hasMoreTokens())
-                _excludedPathPatterns.add(Pattern.compile(tok.nextToken().trim()));
-        }
-        LOG.debug("{} excludedPathPatterns={}",this,_excludedPathPatterns);
-        
-        tmp=filterConfig.getInitParameter("vary");
-        if (tmp!=null)
-            _vary=new HttpGenerator.CachedHttpField(HttpHeader.VARY,tmp);
-        LOG.debug("{} vary={}",this,_vary);
-        
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.UserAgentFilter#destroy()
-     */
-    @Override
-    public void destroy()
-    {
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.UserAgentFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
-     */
-    @Override
-    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
-        throws IOException, ServletException
-    {
-        LOG.debug("{} doFilter {}",this,req);
-        HttpServletRequest request=(HttpServletRequest)req;
-        HttpServletResponse response=(HttpServletResponse)res;
-        HttpChannel<?> channel = HttpChannel.getCurrentHttpChannel();
-        
-        // Have we already started compressing this response?
-        if (req.getDispatcherType()!=DispatcherType.REQUEST)
-        {
-            HttpOutput out = channel.getResponse().getHttpOutput();
-            if (out instanceof GzipHttpOutput && ((GzipHttpOutput)out).mightCompress())
-            {
-                LOG.debug("{} already might compress {}",this,request);
-                super.doFilter(request,response,chain);
-                return;
-            }
-        }
-
-        // If not a supported method or it is an Excluded URI or an excluded UA - no Vary because no matter what client, this URI is always excluded
-        String requestURI = request.getRequestURI();
-        if (!_methods.contains(request.getMethod()))
-        {
-            LOG.debug("{} excluded by method {}",this,request);
-            super.doFilter(request,response,chain);
-            return;
-        }
-        
-        if (isExcludedPath(requestURI))
-        {
-            LOG.debug("{} excluded by path {}",this,request);
-            super.doFilter(request,response,chain);
-            return;
-        }
-
-        // Exclude non compressible mime-types known from URI extension. - no Vary because no matter what client, this URI is always excluded
-        if (_mimeTypes.size()>0 && _excludeMimeTypes)
-        {
-            String mimeType = _context.getMimeType(request.getRequestURI());
-
-            if (mimeType!=null)
-            {
-                mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
-                if (_mimeTypes.contains(mimeType))
-                {
-                    LOG.debug("{} excluded by path suffix {}",this,request);
-                    // handle normally without setting vary header
-                    super.doFilter(request,response,chain);
-                    return;
-                }
-            }
-        }
-
-        //If the Content-Encoding is already set, then we won't compress
-        if (response.getHeader("Content-Encoding") != null)
-        {
-            super.doFilter(request,response,chain);
-            return;
-        }
-        
-        if (_checkGzExists && request.getServletContext()!=null)
-        {
-            String path=request.getServletContext().getRealPath(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()));
-            if (path!=null)
-            {
-                File gz=new File(path+".gz");
-                if (gz.exists())
-                {
-                    LOG.debug("{} gzip exists {}",this,request);
-                    // allow default servlet to handle
-                    super.doFilter(request,response,chain);
-                    return;
-                }
-            }
-        }
-        
-        // Special handling for etags
-        String etag = request.getHeader("If-None-Match"); 
-        if (etag!=null)
-        {
-            if (etag.contains(ETAG_GZIP))
-                request.setAttribute(ETAG,etag.replace(ETAG_GZIP,""));
-        }
-
-        HttpOutput out = channel.getResponse().getHttpOutput();
-        if (!(out instanceof GzipHttpOutput))
-        {
-            if (out.getClass()!=HttpOutput.class)
-                throw new IllegalStateException();
-            channel.getResponse().setHttpOutput(out = new GzipHttpOutput(channel));
-        }
-        
-        GzipHttpOutput cout=(GzipHttpOutput)out;
-        
-        try
-        {
-            cout.mightCompress(this);
-            super.doFilter(request,response,chain);
-        }
-        catch(Throwable e)
-        {
-            LOG.debug("{} excepted {}",this,request,e);
-            if (!response.isCommitted())
-            {
-                cout.resetBuffer();
-                cout.noCompressionIfPossible();
-            }
-            throw e;
-        }
-    }
-
-
-    /**
-     * Checks to see if the userAgent is excluded
-     *
-     * @param ua
-     *            the user agent
-     * @return boolean true if excluded
-     */
-    private boolean isExcludedAgent(String ua)
-    {
-        if (ua == null)
-            return false;
-
-        if (_excludedAgents != null)
-        {
-            if (_excludedAgents.contains(ua))
-            {
-                return true;
-            }
-        }
-        if (_excludedAgentPatterns != null)
-        {
-            for (Pattern pattern : _excludedAgentPatterns)
-            {
-                if (pattern.matcher(ua).matches())
-                {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Checks to see if the path is excluded
-     *
-     * @param requestURI
-     *            the request uri
-     * @return boolean true if excluded
-     */
-    private boolean isExcludedPath(String requestURI)
-    {
-        if (requestURI == null)
-            return false;
-        if (_excludedPaths != null)
-        {
-            for (String excludedPath : _excludedPaths)
-            {
-                if (requestURI.startsWith(excludedPath))
-                {
-                    return true;
-                }
-            }
-        }
-        if (_excludedPathPatterns != null)
-        {
-            for (Pattern pattern : _excludedPathPatterns)
-            {
-                if (pattern.matcher(requestURI).matches())
-                {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public HttpField getVaryField()
-    {
-        return _vary;
-    }
-
-    @Override
-    public Deflater getDeflater(Request request, long content_length)
-    {
-        String ua = getUserAgent(request);
-        if (ua!=null && isExcludedAgent(ua))
-        {
-            LOG.debug("{} excluded user agent {}",this,request);
-            return null;
-        }
-        
-        if (content_length>=0 && content_length<_minGzipSize)
-        {
-            LOG.debug("{} excluded minGzipSize {}",this,request);
-            return null;
-        }
-        
-        String accept = request.getHttpFields().get(HttpHeader.ACCEPT_ENCODING);
-        if (accept==null)
-        {
-            LOG.debug("{} excluded !accept {}",this,request);
-            return null;
-        }
-        
-        boolean gzip=false;
-        if (GZIP.equals(accept) || accept.startsWith("gzip,"))
-            gzip=true;
-        else
-        {
-            List<String> list=HttpFields.qualityList(request.getHttpFields().getValues(HttpHeader.ACCEPT_ENCODING.asString(),","));
-            for (String a:list)
-            {
-                if (GZIP.equalsIgnoreCase(HttpFields.valueParameters(a,null)))
-                {
-                    gzip=true;
-                    break;
-                }
-            }
-        }
-        
-        if (!gzip)
-        {
-            LOG.debug("{} excluded not gzip accept {}",this,request);
-            return null;
-        }
-        
-        Deflater df = _deflater.get();
-        if (df==null)
-            df=new Deflater(_deflateCompressionLevel,_deflateNoWrap);        
-        else
-            _deflater.set(null);
-        
-        return df;
-    }
-
-    @Override
-    public void recycle(Deflater deflater)
-    {
-        deflater.reset();
-        if (_deflater.get()==null)
-            _deflater.set(deflater);
-        
-    }
-    
-    @Override
-    public boolean isExcludedMimeType(String mimetype)
-    {
-        return _mimeTypes.contains(mimetype) == _excludeMimeTypes;
-    }
-
-    @Override
-    public int getBufferSize()
-    {
-        return _bufferSize;
-    }
-    
-    
 }
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java
index aff697e..4b9ceea 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java
@@ -541,14 +541,19 @@
 
         /**
          * Set a name/value pair, null values will be treated as an empty String
+         *
+         * @param name the name
+         * @param value the value
          */
         public void set(String name, String value)
         {
             envMap.put(name, name + "=" + StringUtil.nonNull(value));
         }
 
-        /**
-         * Get representation suitable for passing to exec.
+        /** 
+         * Get representation suitable for passing to exec. 
+         *
+         * @return the env map as an array 
          */
         public String[] getEnvArray()
         {
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CloseableDoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CloseableDoSFilter.java
index 377c0c6..7b70c94 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CloseableDoSFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CloseableDoSFilter.java
@@ -21,21 +21,19 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jetty.server.HttpChannel;
 import org.eclipse.jetty.server.Request;
 
-/* ------------------------------------------------------------ */
-/** Closeable DoS Filter.
- * This is an extension to the {@link DoSFilter} that uses Jetty APIs to allow
- * connections to be closed cleanly.
+/**
+ * This is an extension to {@link DoSFilter} that uses Jetty APIs to
+ * abruptly close the connection when the request times out.
  */
 
 public class CloseableDoSFilter extends DoSFilter
 {
     @Override
-    protected void closeConnection(HttpServletRequest request, HttpServletResponse response, Thread thread)
+    protected void onRequestTimeout(HttpServletRequest request, HttpServletResponse response, Thread handlingThread)
     {
-        Request base_request=(request instanceof Request)?(Request)request:HttpChannel.getCurrentHttpChannel().getRequest();
+        Request base_request=Request.getBaseRequest(request);
         base_request.getHttpChannel().getEndPoint().close();
     }
 }
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ConcatServlet.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ConcatServlet.java
index 40c1891..977fbda 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ConcatServlet.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ConcatServlet.java
@@ -44,13 +44,13 @@
  * relative to the context root.  So these script tags:</p>
  * <pre>
  * &lt;script type="text/javascript" src="../js/behaviour.js"&gt;&lt;/script&gt;
- * &lt;script type="text/javascript" src="../js/ajax.js&/chat/chat.js"&gt;&lt;/script&gt;
+ * &lt;script type="text/javascript" src="../js/ajax.js"&gt;&lt;/script&gt;
  * &lt;script type="text/javascript" src="../chat/chat.js"&gt;&lt;/script&gt;
  * </pre>
  * <p>can be replaced with the single tag (with the {@code ConcatServlet}
  * mapped to {@code /concat}):</p>
  * <pre>
- * &lt;script type="text/javascript" src="../concat?/js/behaviour.js&/js/ajax.js&/chat/chat.js"&gt;&lt;/script&gt;
+ * &lt;script type="text/javascript" src="../concat?/js/behaviour.js&amp;/js/ajax.js&amp;/chat/chat.js"&gt;&lt;/script&gt;
  * </pre>
  * <p>The {@link ServletContext#getMimeType(String)} method is used to determine the
  * mime type of each resource. If the types of all resources do not match, then a 415
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java
index 3107570..2a394dc 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java
@@ -26,6 +26,7 @@
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
@@ -40,44 +41,71 @@
 import org.eclipse.jetty.util.log.Logger;
 
 /**
- * <p>Implementation of the
- * <a href="http://www.w3.org/TR/cors/">cross-origin resource sharing</a>.</p>
- * <p>A typical example is to use this filter to allow cross-domain
+ * Implementation of the
+ * <a href="http://www.w3.org/TR/cors/">cross-origin resource sharing</a>.
+ * <p>
+ * A typical example is to use this filter to allow cross-domain
  * <a href="http://cometd.org">cometd</a> communication using the standard
  * long polling transport instead of the JSONP transport (that is less
- * efficient and less reactive to failures).</p>
- * <p>This filter allows the following configuration parameters:
- * <ul>
- * <li><b>allowedOrigins</b>, a comma separated list of origins that are
+ * efficient and less reactive to failures).
+ * <p>
+ * This filter allows the following configuration parameters:
+ * <dl>
+ * <dt>allowedOrigins</dt>
+ * <dd>a comma separated list of origins that are
  * allowed to access the resources. Default value is <b>*</b>, meaning all
- * origins.<br />
+ * origins.
+ * <p>
  * If an allowed origin contains one or more * characters (for example
  * http://*.domain.com), then "*" characters are converted to ".*", "."
  * characters are escaped to "\." and the resulting allowed origin
- * interpreted as a regular expression.<br />
+ * interpreted as a regular expression.
+ * <p>
  * Allowed origins can therefore be more complex expressions such as
  * https?://*.domain.[a-z]{3} that matches http or https, multiple subdomains
- * and any 3 letter top-level domain (.com, .net, .org, etc.).</li>
- * <li><b>allowedMethods</b>, a comma separated list of HTTP methods that
+ * and any 3 letter top-level domain (.com, .net, .org, etc.).</dd>
+ * 
+ * <dt>allowedTimingOrigins</dt>
+ * <dd>a comma separated list of origins that are
+ * allowed to time the resource. Default value is the empty string, meaning
+ * no origins.
+ * <p>
+ * The check whether the timing header is set, will be performed only if
+ * the user gets general access to the resource using the <b>allowedOrigins</b>.
+ *
+ * <dt>allowedMethods</dt>
+ * <dd>a comma separated list of HTTP methods that
  * are allowed to be used when accessing the resources. Default value is
- * <b>GET,POST,HEAD</b></li>
- * <li><b>allowedHeaders</b>, a comma separated list of HTTP headers that
+ * <b>GET,POST,HEAD</b></dd>
+ * 
+ * 
+ * <dt>allowedHeaders</dt>
+ * <dd>a comma separated list of HTTP headers that
  * are allowed to be specified when accessing the resources. Default value
  * is <b>X-Requested-With,Content-Type,Accept,Origin</b>. If the value is a single "*",
- * this means that any headers will be accepted.</li>
- * <li><b>preflightMaxAge</b>, the number of seconds that preflight requests
+ * this means that any headers will be accepted.</dd>
+ * 
+ * <dt>preflightMaxAge</dt>
+ * <dd>the number of seconds that preflight requests
  * can be cached by the client. Default value is <b>1800</b> seconds, or 30
- * minutes</li>
- * <li><b>allowCredentials</b>, a boolean indicating if the resource allows
- * requests with credentials. Default value is <b>true</b></li>
- * <li><b>exposedHeaders</b>, a comma separated list of HTTP headers that
+ * minutes</dd>
+ * 
+ * <dt>allowCredentials</dt>
+ * <dd>a boolean indicating if the resource allows
+ * requests with credentials. Default value is <b>true</b></dd>
+ * 
+ * <dt>exposedHeaders</dt>
+ * <dd>a comma separated list of HTTP headers that
  * are allowed to be exposed on the client. Default value is the
- * <b>empty list</b></li>
- * <li><b>chainPreflight</b>, if true preflight requests are chained to their
+ * <b>empty list</b></dd>
+ * 
+ * <dt>chainPreflight</dt>
+ * <dd>if true preflight requests are chained to their
  * target resource for normal handling (as an OPTION request).  Otherwise the
- * filter will response to the preflight. Default is <b>true</b>.</li>
- * </ul></p>
- * <p>A typical configuration could be:</p>
+ * filter will response to the preflight. Default is <b>true</b>.</dd>
+ * 
+ * </dl>
+ * A typical configuration could be:
  * <pre>
  * &lt;web-app ...&gt;
  *     ...
@@ -108,8 +136,10 @@
     public static final String ACCESS_CONTROL_MAX_AGE_HEADER = "Access-Control-Max-Age";
     public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER = "Access-Control-Allow-Credentials";
     public static final String ACCESS_CONTROL_EXPOSE_HEADERS_HEADER = "Access-Control-Expose-Headers";
+    public static final String TIMING_ALLOW_ORIGIN_HEADER = "Timing-Allow-Origin";
     // Implementation constants
     public static final String ALLOWED_ORIGINS_PARAM = "allowedOrigins";
+    public static final String ALLOWED_TIMING_ORIGINS_PARAM = "allowedTimingOrigins";
     public static final String ALLOWED_METHODS_PARAM = "allowedMethods";
     public static final String ALLOWED_HEADERS_PARAM = "allowedHeaders";
     public static final String PREFLIGHT_MAX_AGE_PARAM = "preflightMaxAge";
@@ -118,13 +148,17 @@
     public static final String OLD_CHAIN_PREFLIGHT_PARAM = "forwardPreflight";
     public static final String CHAIN_PREFLIGHT_PARAM = "chainPreflight";
     private static final String ANY_ORIGIN = "*";
+    private static final String DEFAULT_ALLOWED_ORIGINS = "*";
+    private static final String DEFAULT_ALLOWED_TIMING_ORIGINS = "";
     private static final List<String> SIMPLE_HTTP_METHODS = Arrays.asList("GET", "POST", "HEAD");
     private static final List<String> DEFAULT_ALLOWED_METHODS = Arrays.asList("GET", "POST", "HEAD");
     private static final List<String> DEFAULT_ALLOWED_HEADERS = Arrays.asList("X-Requested-With", "Content-Type", "Accept", "Origin");
 
     private boolean anyOriginAllowed;
+    private boolean anyTimingOriginAllowed;
     private boolean anyHeadersAllowed;
     private List<String> allowedOrigins = new ArrayList<String>();
+    private List<String> allowedTimingOrigins = new ArrayList<String>();
     private List<String> allowedMethods = new ArrayList<String>();
     private List<String> allowedHeaders = new ArrayList<String>();
     private List<String> exposedHeaders = new ArrayList<String>();
@@ -135,26 +169,10 @@
     public void init(FilterConfig config) throws ServletException
     {
         String allowedOriginsConfig = config.getInitParameter(ALLOWED_ORIGINS_PARAM);
-        if (allowedOriginsConfig == null)
-            allowedOriginsConfig = "*";
-        String[] allowedOrigins = StringUtil.csvSplit(allowedOriginsConfig);
-        for (String allowedOrigin : allowedOrigins)
-        {
-            allowedOrigin = allowedOrigin.trim();
-            if (allowedOrigin.length() > 0)
-            {
-                if (ANY_ORIGIN.equals(allowedOrigin))
-                {
-                    anyOriginAllowed = true;
-                    this.allowedOrigins.clear();
-                    break;
-                }
-                else
-                {
-                    this.allowedOrigins.add(allowedOrigin);
-                }
-            }
-        }
+        String allowedTimingOriginsConfig = config.getInitParameter(ALLOWED_TIMING_ORIGINS_PARAM);
+        
+        anyOriginAllowed = generateAllowedOrigins(allowedOrigins, allowedOriginsConfig, DEFAULT_ALLOWED_ORIGINS);
+        anyTimingOriginAllowed = generateAllowedOrigins(allowedTimingOrigins, allowedTimingOriginsConfig, DEFAULT_ALLOWED_TIMING_ORIGINS);
 
         String allowedMethodsConfig = config.getInitParameter(ALLOWED_METHODS_PARAM);
         if (allowedMethodsConfig == null)
@@ -205,6 +223,7 @@
         {
             LOG.debug("Cross-origin filter configuration: " +
                             ALLOWED_ORIGINS_PARAM + " = " + allowedOriginsConfig + ", " +
+                            ALLOWED_TIMING_ORIGINS_PARAM + " = " + allowedTimingOriginsConfig + ", " +
                             ALLOWED_METHODS_PARAM + " = " + allowedMethodsConfig + ", " +
                             ALLOWED_HEADERS_PARAM + " = " + allowedHeadersConfig + ", " +
                             PREFLIGHT_MAX_AGE_PARAM + " = " + preflightMaxAgeConfig + ", " +
@@ -215,6 +234,29 @@
         }
     }
 
+    private boolean generateAllowedOrigins(List<String> allowedOriginStore, String allowedOriginsConfig, String defaultOrigin) 
+    {
+        if (allowedOriginsConfig == null)
+            allowedOriginsConfig = defaultOrigin;
+        String[] allowedOrigins = StringUtil.csvSplit(allowedOriginsConfig);
+        for (String allowedOrigin : allowedOrigins)
+        {
+            if (allowedOrigin.length() > 0)
+            {
+                if (ANY_ORIGIN.equals(allowedOrigin))
+                {
+                    allowedOriginStore.clear();
+                    return true;
+                }
+                else
+                {
+                    allowedOriginStore.add(allowedOrigin);
+                }
+            }
+        }
+        return false;
+    }
+    
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
     {
         handle((HttpServletRequest)request, (HttpServletResponse)response, chain);
@@ -226,7 +268,7 @@
         // Is it a cross origin request ?
         if (origin != null && isEnabled(request))
         {
-            if (originMatches(origin))
+            if (anyOriginAllowed || originMatches(allowedOrigins, origin))
             {
                 if (isSimpleRequest(request))
                 {
@@ -247,6 +289,15 @@
                     LOG.debug("Cross-origin request to {} is a non-simple cross-origin request", request.getRequestURI());
                     handleSimpleResponse(request, response, origin);
                 }
+
+                if (anyTimingOriginAllowed || originMatches(allowedTimingOrigins, origin))
+                {
+                    response.setHeader(TIMING_ALLOW_ORIGIN_HEADER, origin);
+                }
+                else
+                {
+                    LOG.debug("Cross-origin request to " + request.getRequestURI() + " with origin " + origin + " does not match allowed timing origins " + allowedTimingOrigins);
+                }
             }
             else
             {
@@ -261,12 +312,12 @@
     {
         // WebSocket clients such as Chrome 5 implement a version of the WebSocket
         // protocol that does not accept extra response headers on the upgrade response
-        for (Enumeration connections = request.getHeaders("Connection"); connections.hasMoreElements();)
+        for (Enumeration<String> connections = request.getHeaders("Connection"); connections.hasMoreElements();)
         {
             String connection = (String)connections.nextElement();
             if ("Upgrade".equalsIgnoreCase(connection))
             {
-                for (Enumeration upgrades = request.getHeaders("Upgrade"); upgrades.hasMoreElements();)
+                for (Enumeration<String>  upgrades = request.getHeaders("Upgrade"); upgrades.hasMoreElements();)
                 {
                     String upgrade = (String)upgrades.nextElement();
                     if ("WebSocket".equalsIgnoreCase(upgrade))
@@ -277,11 +328,8 @@
         return true;
     }
 
-    private boolean originMatches(String originList)
+    private boolean originMatches(List<String> allowedOrigins, String originList)
     {
-        if (anyOriginAllowed)
-            return true;
-
         if (originList.trim().length() == 0)
             return false;
 
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DataRateLimitedServlet.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DataRateLimitedServlet.java
index 429ad29..a3c7f49 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DataRateLimitedServlet.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DataRateLimitedServlet.java
@@ -63,7 +63,7 @@
 {
     private static final long serialVersionUID = -4771757707068097025L;
     private int buffersize=8192;
-    private int pause=100;
+    private long pauseNS=TimeUnit.MILLISECONDS.toNanos(100);
     ScheduledThreadPoolExecutor scheduler;
     private final ConcurrentHashMap<String, ByteBuffer> cache=new ConcurrentHashMap<>();
     
@@ -76,7 +76,7 @@
             buffersize=Integer.parseInt(tmp);
         tmp = getInitParameter("pause");
         if (tmp!=null)
-            pause=Integer.parseInt(tmp);
+            pauseNS=TimeUnit.MILLISECONDS.toNanos(Integer.parseInt(tmp));
         tmp = getInitParameter("pool");
         int pool=tmp==null?Runtime.getRuntime().availableProcessors():Integer.parseInt(tmp);
         
@@ -205,7 +205,7 @@
                 
                 // Schedule a timer callback to pause writing.  Because isReady() is not called,
                 // a onWritePossible callback is no scheduled.
-                scheduler.schedule(this,pause,TimeUnit.MILLISECONDS);
+                scheduler.schedule(this,pauseNS,TimeUnit.NANOSECONDS);
             }
         }
         
@@ -261,7 +261,7 @@
         {            
             // If we are able to write
             if(out.isReady())
-            {
+            {   
                 // Position our buffers limit to allow only buffersize bytes to be written
                 int l=content.position()+buffersize;
                 // respect the ultimate limit
@@ -276,7 +276,7 @@
                     async.complete();
                     return;
                 }
-
+                
                 // write our limited buffer.  This will be an asynchronous write
                 // and will always return immediately without blocking.  If a subsequent
                 // call to out.isReady() returns false, then this onWritePossible method
@@ -284,8 +284,8 @@
                 out.write(content);
 
                 // Schedule a timer callback to pause writing.  Because isReady() is not called,
-                // a onWritePossible callback is no scheduled.
-                scheduler.schedule(this,pause,TimeUnit.MILLISECONDS);
+                // a onWritePossible callback is not scheduled.
+                scheduler.schedule(this,pauseNS,TimeUnit.NANOSECONDS);
             }
         }
         
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java
index 0067d4d..a5dab44 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java
@@ -51,6 +51,7 @@
 import javax.servlet.http.HttpSessionBindingListener;
 import javax.servlet.http.HttpSessionEvent;
 
+import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
@@ -64,7 +65,6 @@
 
 /**
  * Denial of Service filter
- * <p/>
  * <p>
  * This filter is useful for limiting
  * exposure to abuse from request flooding, whether malicious, or as a result of
@@ -82,55 +82,46 @@
  * The {@link #extractUserId(ServletRequest request)} function should be
  * implemented, in order to uniquely identify authenticated users.
  * <p>
- * The following init parameters control the behavior of the filter:<dl>
- * <p/>
+ * The following init parameters control the behavior of the filter:
+ * <dl>
  * <dt>maxRequestsPerSec</dt>
  * <dd>the maximum number of requests from a connection per
  * second. Requests in excess of this are first delayed,
  * then throttled.</dd>
- * <p/>
  * <dt>delayMs</dt>
  * <dd>is the delay given to all requests over the rate limit,
  * before they are considered at all. -1 means just reject request,
  * 0 means no delay, otherwise it is the delay.</dd>
- * <p/>
  * <dt>maxWaitMs</dt>
  * <dd>how long to blocking wait for the throttle semaphore.</dd>
- * <p/>
  * <dt>throttledRequests</dt>
  * <dd>is the number of requests over the rate limit able to be
  * considered at once.</dd>
- * <p/>
  * <dt>throttleMs</dt>
  * <dd>how long to async wait for semaphore.</dd>
- * <p/>
  * <dt>maxRequestMs</dt>
  * <dd>how long to allow this request to run.</dd>
- * <p/>
  * <dt>maxIdleTrackerMs</dt>
  * <dd>how long to keep track of request rates for a connection,
  * before deciding that the user has gone away, and discarding it</dd>
- * <p/>
  * <dt>insertHeaders</dt>
  * <dd>if true , insert the DoSFilter headers into the response. Defaults to true.</dd>
- * <p/>
  * <dt>trackSessions</dt>
  * <dd>if true, usage rate is tracked by session if a session exists. Defaults to true.</dd>
- * <p/>
  * <dt>remotePort</dt>
  * <dd>if true and session tracking is not used, then rate is tracked by IP+port (effectively connection). Defaults to false.</dd>
- * <p/>
  * <dt>ipWhitelist</dt>
  * <dd>a comma-separated list of IP addresses that will not be rate limited</dd>
- * <p/>
  * <dt>managedAttr</dt>
  * <dd>if set to true, then this servlet is set as a {@link ServletContext} attribute with the
  * filter name as the attribute name.  This allows context external mechanism (eg JMX via {@link ContextHandler#MANAGED_ATTRIBUTES}) to
  * manage the configuration of the filter.</dd>
+ * <dt>tooManyCode</dt>
+ * <dd>The status code to send if there are too many requests.  By default is 429 (too many requests), but 503 (Unavailable) is
+ * another option</dd>
  * </dl>
- * </p>
  * <p>
- * This filter should be configured for {@link DispatcherType#REQUEST} and {@link DispatcherType#ASYNC} and with 
+ * This filter should be configured for {@link DispatcherType#REQUEST} and {@link DispatcherType#ASYNC} and with
  * <code>&lt;async-supported&gt;true&lt;/async-supported&gt;</code>.
  * </p>
  */
@@ -169,6 +160,7 @@
     static final String REMOTE_PORT_INIT_PARAM = "remotePort";
     static final String IP_WHITELIST_INIT_PARAM = "ipWhitelist";
     static final String ENABLED_INIT_PARAM = "enabled";
+    static final String TOO_MANY_CODE = "tooManyCode";
 
     private static final int USER_AUTH = 2;
     private static final int USER_SESSION = 2;
@@ -179,6 +171,7 @@
     private final String _resumed = "DoSFilter@" + Integer.toHexString(hashCode()) + ".RESUMED";
     private final ConcurrentHashMap<String, RateTracker> _rateTrackers = new ConcurrentHashMap<>();
     private final List<String> _whitelist = new CopyOnWriteArrayList<>();
+    private int _tooManyCode;
     private volatile long _delayMs;
     private volatile long _throttleMs;
     private volatile long _maxWaitMs;
@@ -267,6 +260,9 @@
         parameter = filterConfig.getInitParameter(ENABLED_INIT_PARAM);
         setEnabled(parameter == null || Boolean.parseBoolean(parameter));
 
+        parameter = filterConfig.getInitParameter(TOO_MANY_CODE);
+        setTooManyCode(parameter==null?429:Integer.parseInt(parameter));
+
         _scheduler = startScheduler();
 
         ServletContext context = filterConfig.getServletContext();
@@ -337,7 +333,7 @@
                     LOG.warn("DOS ALERT: Request rejected ip={}, session={}, user={}", request.getRemoteAddr(), request.getRequestedSessionId(), request.getUserPrincipal());
                     if (insertHeaders)
                         response.addHeader("DoSFilter", "unavailable");
-                    response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+                    response.sendError(getTooManyCode());
                     return;
                 }
                 case 0:
@@ -420,36 +416,43 @@
                     LOG.debug("Rejecting {}", request);
                 if (isInsertHeaders())
                     response.addHeader("DoSFilter", "unavailable");
-                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+                response.sendError(getTooManyCode());
             }
         }
         catch (InterruptedException e)
         {
-            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+            LOG.ignore(e);
+            response.sendError(getTooManyCode());
         }
         finally
         {
             if (accepted)
             {
-                // Wake up the next highest priority request.
-                for (int p = _queues.length - 1; p >= 0; --p)
+                try
                 {
-                    AsyncContext asyncContext = _queues[p].poll();
-                    if (asyncContext != null)
+                    // Wake up the next highest priority request.
+                    for (int p = _queues.length - 1; p >= 0; --p)
                     {
-                        ServletRequest candidate = asyncContext.getRequest();
-                        Boolean suspended = (Boolean)candidate.getAttribute(_suspended);
-                        if (suspended == Boolean.TRUE)
+                        AsyncContext asyncContext = _queues[p].poll();
+                        if (asyncContext != null)
                         {
-                            if (LOG.isDebugEnabled())
-                                LOG.debug("Resuming {}", request);
-                            candidate.setAttribute(_resumed, Boolean.TRUE);
-                            asyncContext.dispatch();
-                            break;
+                            ServletRequest candidate = asyncContext.getRequest();
+                            Boolean suspended = (Boolean)candidate.getAttribute(_suspended);
+                            if (suspended == Boolean.TRUE)
+                            {
+                                if (LOG.isDebugEnabled())
+                                    LOG.debug("Resuming {}", request);
+                                candidate.setAttribute(_resumed, Boolean.TRUE);
+                                asyncContext.dispatch();
+                                break;
+                            }
                         }
                     }
                 }
-                _passes.release();
+                finally
+                {
+                    _passes.release();
+                }
             }
         }
     }
@@ -477,38 +480,37 @@
     }
 
     /**
-     * Takes drastic measures to return this response and stop this thread.
-     * Due to the way the connection is interrupted, may return mixed up headers.
+     * Invoked when the request handling exceeds {@link #getMaxRequestMs()}.
+     * <p>
+     * By default, a HTTP 503 response is returned and the handling thread is interrupted.
      *
-     * @param request  current request
-     * @param response current response, which must be stopped
-     * @param thread   the handling thread
+     * @param request  the current request
+     * @param response the current response
+     * @param handlingThread the handling thread
      */
-    protected void closeConnection(HttpServletRequest request, HttpServletResponse response, Thread thread)
+    protected void onRequestTimeout(HttpServletRequest request, HttpServletResponse response, Thread handlingThread)
     {
-        // take drastic measures to return this response and stop this thread.
-        if (!response.isCommitted())
-        {
-            response.setHeader("Connection", "close");
-        }
         try
         {
-            try
-            {
-                response.getWriter().close();
-            }
-            catch (IllegalStateException e)
-            {
-                response.getOutputStream().close();
-            }
+            if (LOG.isDebugEnabled())
+                LOG.debug("Timing out {}", request);
+            response.sendError(HttpStatus.SERVICE_UNAVAILABLE_503);
         }
-        catch (IOException e)
+        catch (Throwable x)
         {
-            LOG.warn(e);
+            LOG.info(x);
         }
 
-        // interrupt the handling thread
-        thread.interrupt();
+        handlingThread.interrupt();
+    }
+
+    /**
+     * @deprecated use {@link #onRequestTimeout(HttpServletRequest, HttpServletResponse, Thread)} instead
+     */
+    @Deprecated
+    protected void closeConnection(HttpServletRequest request, HttpServletResponse response, Thread thread)
+    {
+        onRequestTimeout(request, response, thread);
     }
 
     /**
@@ -540,12 +542,12 @@
      * track of this connection's request rate. If this is not the first request
      * from this connection, return the existing object with the stored stats.
      * If it is the first request, then create a new request tracker.
-     * <p/>
+     * <p>
      * Assumes that each connection has an identifying characteristic, and goes
      * through them in order, taking the first that matches: user id (logged
      * in), session id, client IP address. Unidentifiable connections are lumped
      * into one.
-     * <p/>
+     * <p>
      * When a session expires, its rate tracker is automatically deleted.
      *
      * @param request the current request
@@ -724,10 +726,10 @@
             prefix -= 8;
             ++index;
         }
-        
+
         if (index == result.length)
             return result;
-               
+
         // Sets the _prefix_ most significant bits to 1
         result[index] = (byte)~((1 << (8 - prefix)) - 1);
         return result;
@@ -793,6 +795,7 @@
     /**
      * Get delay (in milliseconds) that is applied to all requests
      * over the rate limit, before they are considered at all.
+     * @return the delay in milliseconds
      */
     @ManagedAttribute("delay applied to all requests over the rate limit (in ms)")
     public long getDelayMs()
@@ -1010,6 +1013,16 @@
         _enabled = enabled;
     }
 
+    public int getTooManyCode()
+    {
+        return _tooManyCode;
+    }
+
+    public void setTooManyCode(int tooManyCode)
+    {
+        _tooManyCode = tooManyCode;
+    }
+
     /**
      * Get a list of IP addresses that will not be rate limited.
      *
@@ -1094,10 +1107,10 @@
     {
         private static final long serialVersionUID = 3534663738034577872L;
 
-        protected transient final String _id;
-        protected transient final int _type;
-        protected transient final long[] _timestamps;
-        protected transient int _next;
+        protected final String _id;
+        protected final int _type;
+        protected final long[] _timestamps;
+        protected int _next;
 
         public RateTracker(String id, int type, int maxRequestsPerSecond)
         {
@@ -1108,6 +1121,7 @@
         }
 
         /**
+         * @param now the time now (in milliseconds)
          * @return the current calculated request rate over the last second
          */
         public boolean isRateExceeded(long now)
@@ -1150,16 +1164,14 @@
         public void sessionWillPassivate(HttpSessionEvent se)
         {
             //take the tracker of the list of trackers (if its still there)
-            //and ensure that we take ourselves out of the session so we are not saved
             _rateTrackers.remove(_id);
-            se.getSession().removeAttribute(__TRACKER);
-            if (LOG.isDebugEnabled()) 
-                LOG.debug("Value removed: {}", getId());
         }
 
         public void sessionDidActivate(HttpSessionEvent se)
         {
-            LOG.warn("Unexpected session activation");
+            RateTracker tracker = (RateTracker)se.getSession().getAttribute(__TRACKER);
+            if (tracker!=null)
+                _rateTrackers.put(tracker.getId(),tracker);
         }
 
         @Override
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java
index 1c94f57..d0d19f1 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java
@@ -18,641 +18,42 @@
 
 package org.eclipse.jetty.servlets;
 
-import java.io.File;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.util.HashSet;
-import java.util.Locale;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.regex.Pattern;
-import java.util.zip.Deflater;
 
-import javax.servlet.AsyncEvent;
-import javax.servlet.AsyncListener;
+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;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.MimeTypes;
-import org.eclipse.jetty.servlets.gzip.AbstractCompressedStream;
-import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
-import org.eclipse.jetty.servlets.gzip.DeflatedOutputStream;
-import org.eclipse.jetty.servlets.gzip.GzipOutputStream;
-import org.eclipse.jetty.util.IncludeExclude;
-import org.eclipse.jetty.util.URIUtil;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
 /* ------------------------------------------------------------ */
-/** GZIP Filter
- * This filter will gzip or deflate the content of a response if: <ul>
- * <li>The filter is mapped to a matching path</li>
- * <li>accept-encoding header is set to either gzip, deflate or a combination of those</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>If a list of mimeTypes is set by the <code>mimeTypes</code> init parameter, then the Content-Type is in the list.</li>
- * <li>If no mimeType list is set, then the content-type is not in the list defined by <code>excludedMimeTypes</code></li>
- * <li>No content-encoding is specified by the resource</li>
- * </ul>
- *
- * <p>
- * If both gzip and deflate are specified in the accept-encoding header, then gzip will be used.
- * </p>
- * <p>
- * Compressing the content can greatly improve the network bandwidth usage, but at a cost of memory and
- * CPU cycles. If this filter is mapped for static content, then use of efficient direct NIO may be
- * prevented, thus use of the gzip mechanism of the {@link org.eclipse.jetty.servlet.DefaultServlet} is
- * advised instead.
- * </p>
- * <p>
- * This filter extends {@link UserAgentFilter} and if the the initParameter <code>excludedAgents</code>
- * is set to a comma separated list of user agents, then these agents will be excluded from gzip content.
- * </p>
- * <p>Init Parameters:</p>
- * <dl>
- * <dt>bufferSize</dt>       <dd>The output buffer size. Defaults to 8192. Be careful as values <= 0 will lead to an
- *                            {@link IllegalArgumentException}.
- *                            See: {@link java.util.zip.GZIPOutputStream#GZIPOutputStream(java.io.OutputStream, int)}
- *                            and: {@link java.util.zip.DeflaterOutputStream#DeflaterOutputStream(java.io.OutputStream, Deflater, int)}
- * </dd>
- * <dt>minGzipSize</dt>       <dd>Content will only be compressed if content length is either unknown or greater
- *                            than <code>minGzipSize</code>.
- * </dd>
- * <dt>deflateCompressionLevel</dt>       <dd>The compression level used for deflate compression. (0-9).
- *                            See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
- * </dd>
- * <dt>deflateNoWrap</dt>       <dd>The noWrap setting for deflate compression. Defaults to true. (true/false)
- *                            See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
- * </dd>
- * <dt>methods</dt>       <dd>Comma separated list of HTTP methods to compress. If not set, only GET requests are compressed.
- *  </dd>
- * <dt>mimeTypes</dt>       <dd>Comma separated list of mime types to compress. If it is not set, then the excludedMimeTypes list is used.
- * </dd>
- * <dt>excludedMimeTypes</dt>       <dd>Comma separated list of mime types to never compress. If not set, then the default is the commonly known
- * image, video, audio and compressed types.
- * </dd>
-
- * <dt>excludedAgents</dt>       <dd>Comma separated list of user agents to exclude from compression. Does a
- *                            {@link String#contains(CharSequence)} to check if the excluded agent occurs
- *                            in the user-agent header. If it does -> no compression
- * </dd>
- * <dt>excludeAgentPatterns</dt>       <dd>Same as excludedAgents, but accepts regex patterns for more complex matching.
- * </dd>
- * <dt>excludePaths</dt>       <dd>Comma separated list of paths to exclude from compression.
- *                            Does a {@link String#startsWith(String)} comparison to check if the path matches.
- *                            If it does match -> no compression. To match subpaths use <code>excludePathPatterns</code>
- *                            instead.
- * </dd>
- * <dt>excludePathPatterns</dt>       <dd>Same as excludePath, but accepts regex patterns for more complex matching.
- * </dd>
- * <dt>vary</dt>       <dd>Set to the value of the Vary header sent with responses that could be compressed.  By default it is 
- *                            set to 'Vary: 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 'Vary: Accept-Encoding'.  Note also 
- *                            that shared caches may cache 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.
- * </dd>                         
- * <dt>checkGzExists</dt>       <dd>If set to true, the filter check if a static resource with ".gz" appended exists.  If so then
- *                            the normal processing is done so that the default servlet can send  the pre existing gz content.
- *  </dd>
- *  </dl>
+/** 
  */
-public class GzipFilter extends UserAgentFilter
+
+@Deprecated
+public class GzipFilter implements Filter
 {
     private static final Logger LOG = Log.getLogger(GzipFilter.class);
-    public final static String GZIP="gzip";
-    public final static String ETAG_GZIP="--gzip\"";
-    public final static String DEFLATE="deflate";
-    public final static String ETAG_DEFLATE="--deflate\"";
-    public final static String ETAG="o.e.j.s.GzipFilter.ETag";
 
-    protected ServletContext _context;
-    protected final Set<String> _mimeTypes=new HashSet<>();
-    protected boolean _excludeMimeTypes;
-    protected int _bufferSize=8192;
-    protected int _minGzipSize=256;
-    protected int _deflateCompressionLevel=Deflater.DEFAULT_COMPRESSION;
-    protected boolean _deflateNoWrap = true;
-    protected boolean _checkGzExists = true;
-    
-    // non-static, as other GzipFilter instances may have different configurations
-    protected final ThreadLocal<Deflater> _deflater = new ThreadLocal<Deflater>();
-
-    protected final static ThreadLocal<byte[]> _buffer= new ThreadLocal<byte[]>();
-
-    protected final Set<String> _methods=new HashSet<String>();
-    protected Set<String> _excludedAgents;
-    protected Set<Pattern> _excludedAgentPatterns;
-    protected Set<String> _excludedPaths;
-    protected Set<Pattern> _excludedPathPatterns;
-    protected String _vary="Accept-Encoding, User-Agent";
-    
-    private static final int STATE_SEPARATOR = 0;
-    private static final int STATE_Q = 1;
-    private static final int STATE_QVALUE = 2;
-    private static final int STATE_DEFAULT = 3;
-
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.UserAgentFilter#init(javax.servlet.FilterConfig)
-     */
     @Override
     public void init(FilterConfig filterConfig) throws ServletException
-    {
-        super.init(filterConfig);
-
-        _context=filterConfig.getServletContext();
-        
-        String tmp=filterConfig.getInitParameter("bufferSize");
-        if (tmp!=null)
-            _bufferSize=Integer.parseInt(tmp);
-
-        tmp=filterConfig.getInitParameter("minGzipSize");
-        if (tmp!=null)
-            _minGzipSize=Integer.parseInt(tmp);
-
-        tmp=filterConfig.getInitParameter("deflateCompressionLevel");
-        if (tmp!=null)
-            _deflateCompressionLevel=Integer.parseInt(tmp);
-
-        tmp=filterConfig.getInitParameter("deflateNoWrap");
-        if (tmp!=null)
-            _deflateNoWrap=Boolean.parseBoolean(tmp);
-
-        tmp=filterConfig.getInitParameter("checkGzExists");
-        if (tmp!=null)
-            _checkGzExists=Boolean.parseBoolean(tmp);
-        
-        tmp=filterConfig.getInitParameter("methods");
-        if (tmp!=null)
-        {
-            StringTokenizer tok = new StringTokenizer(tmp,",",false);
-            while (tok.hasMoreTokens())
-                _methods.add(tok.nextToken().trim().toUpperCase(Locale.ENGLISH));
-        }
-        else
-            _methods.add(HttpMethod.GET.asString());
-        
-        tmp=filterConfig.getInitParameter("mimeTypes");
-        if (tmp==null)
-        {
-            _excludeMimeTypes=true;
-            tmp=filterConfig.getInitParameter("excludedMimeTypes");
-            if (tmp==null)
-            {
-                for (String type:MimeTypes.getKnownMimeTypes())
-                {
-                    if (type.equals("image/svg+xml")) //always compressable (unless .svgz file)
-                        continue;
-                    if (type.startsWith("image/")||
-                        type.startsWith("audio/")||
-                        type.startsWith("video/"))
-                        _mimeTypes.add(type);
-                }
-                _mimeTypes.add("application/compress");
-                _mimeTypes.add("application/zip");
-                _mimeTypes.add("application/gzip");
-            }
-            else
-            {
-                StringTokenizer tok = new StringTokenizer(tmp,",",false);
-                while (tok.hasMoreTokens())
-                    _mimeTypes.add(tok.nextToken().trim());
-            }
-        }
-        else
-        {
-            StringTokenizer tok = new StringTokenizer(tmp,",",false);
-            while (tok.hasMoreTokens())
-                _mimeTypes.add(tok.nextToken().trim());
-        }
-        tmp=filterConfig.getInitParameter("excludedAgents");
-        if (tmp!=null)
-        {
-            _excludedAgents=new HashSet<String>();
-            StringTokenizer tok = new StringTokenizer(tmp,",",false);
-            while (tok.hasMoreTokens())
-               _excludedAgents.add(tok.nextToken().trim());
-        }
-
-        tmp=filterConfig.getInitParameter("excludeAgentPatterns");
-        if (tmp!=null)
-        {
-            _excludedAgentPatterns=new HashSet<Pattern>();
-            StringTokenizer tok = new StringTokenizer(tmp,",",false);
-            while (tok.hasMoreTokens())
-                _excludedAgentPatterns.add(Pattern.compile(tok.nextToken().trim()));
-        }
-
-        tmp=filterConfig.getInitParameter("excludePaths");
-        if (tmp!=null)
-        {
-            _excludedPaths=new HashSet<String>();
-            StringTokenizer tok = new StringTokenizer(tmp,",",false);
-            while (tok.hasMoreTokens())
-                _excludedPaths.add(tok.nextToken().trim());
-        }
-
-        tmp=filterConfig.getInitParameter("excludePathPatterns");
-        if (tmp!=null)
-        {
-            _excludedPathPatterns=new HashSet<Pattern>();
-            StringTokenizer tok = new StringTokenizer(tmp,",",false);
-            while (tok.hasMoreTokens())
-                _excludedPathPatterns.add(Pattern.compile(tok.nextToken().trim()));
-        }
-        
-        tmp=filterConfig.getInitParameter("vary");
-        if (tmp!=null)
-            _vary=tmp;
+    {        
+        LOG.warn("GzipFilter is deprecated. Use GzipHandler");
     }
 
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.UserAgentFilter#destroy()
-     */
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+    {
+        chain.doFilter(request,response);
+    }
+
     @Override
     public void destroy()
-    {
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.UserAgentFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
-     */
-    @Override
-    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
-        throws IOException, ServletException
-    {
-        HttpServletRequest request=(HttpServletRequest)req;
-        HttpServletResponse response=(HttpServletResponse)res;
-
-        // If not a supported method or it is an Excluded URI - no Vary because no matter what client, this URI is always excluded
-        String requestURI = request.getRequestURI();
-        if (!_methods.contains(request.getMethod()) || isExcludedPath(requestURI))
-        {
-            super.doFilter(request,response,chain);
-            return;
-        }
-
-        // Exclude non compressible mime-types known from URI extension. - no Vary because no matter what client, this URI is always excluded
-        if (_mimeTypes.size()>0 && _excludeMimeTypes)
-        {
-            String mimeType = _context.getMimeType(request.getRequestURI());
-
-            if (mimeType!=null)
-            {
-                mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
-                if (_mimeTypes.contains(mimeType))
-                {
-                    // handle normally without setting vary header
-                    super.doFilter(request,response,chain);
-                    return;
-                }
-            }
-        }
-        
-        //If the Content-Encoding is already set, then we won't compress
-        if (response.getHeader("Content-Encoding") != null)
-        {
-            super.doFilter(request,response,chain);
-            return;
-        }
-
-        if (_checkGzExists && request.getServletContext()!=null)
-        {
-            String path=request.getServletContext().getRealPath(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()));
-            if (path!=null)
-            {
-                File gz=new File(path+".gz");
-                if (gz.exists())
-                {
-                    // allow default servlet to handle
-                    super.doFilter(request,response,chain);
-                    return;
-                }
-            }
-        }
-        
-        // Excluded User-Agents
-        String ua = getUserAgent(request);
-        boolean ua_excluded=ua!=null&&isExcludedAgent(ua);
-        
-        // Acceptable compression type
-        String compressionType = ua_excluded?null:selectCompression(request.getHeader("accept-encoding"));
-
-        // Special handling for etags
-        String etag = request.getHeader("If-None-Match"); 
-        if (etag!=null)
-        {
-            int dd=etag.indexOf("--");
-            if (dd>0)
-                request.setAttribute(ETAG,etag.substring(0,dd)+(etag.endsWith("\"")?"\"":""));
-        }
-
-        CompressedResponseWrapper wrappedResponse = createWrappedResponse(request,response,compressionType);
-
-        boolean exceptional=true;
-        try
-        {
-            super.doFilter(request,wrappedResponse,chain);
-            exceptional=false;
-        }
-        finally
-        {
-            if (request.isAsyncStarted())
-            {
-                request.getAsyncContext().addListener(new FinishOnCompleteListener(wrappedResponse));
-            }
-            else if (exceptional && !response.isCommitted())
-            {
-                wrappedResponse.resetBuffer();
-                wrappedResponse.noCompression();
-            }
-            else
-                wrappedResponse.finish();
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    private String selectCompression(String encodingHeader)
-    {
-        // TODO, this could be a little more robust.
-        // prefer gzip over deflate
-        String compression = null;
-        if (encodingHeader!=null)
-        {
-            
-            String[] encodings = getEncodings(encodingHeader);
-            if (encodings != null)
-            {
-                for (int i=0; i< encodings.length; i++)
-                {
-                    if (encodings[i].toLowerCase(Locale.ENGLISH).contains(GZIP))
-                    {
-                        if (isEncodingAcceptable(encodings[i]))
-                        {
-                            compression = GZIP;
-                            break; //prefer Gzip over deflate
-                        }
-                    }
-
-                    if (encodings[i].toLowerCase(Locale.ENGLISH).contains(DEFLATE))
-                    {
-                        if (isEncodingAcceptable(encodings[i]))
-                        {
-                            compression = DEFLATE; //Keep checking in case gzip is acceptable
-                        }
-                    }
-                }
-            }
-        }
-        return compression;
+    {        
     }
     
-    
-    private String[] getEncodings (String encodingHeader)
-    {
-        if (encodingHeader == null)
-            return null;
-        return encodingHeader.split(",");
-    }
-    
-    private boolean isEncodingAcceptable(String encoding)
-    {    
-        int state = STATE_DEFAULT;
-        int qvalueIdx = -1;
-        for (int i=0;i<encoding.length();i++)
-        {
-            char c = encoding.charAt(i);
-            switch (state)
-            {
-                case STATE_DEFAULT:
-                {
-                    if (';' == c)
-                        state = STATE_SEPARATOR;
-                    break;
-                }
-                case STATE_SEPARATOR:
-                {
-                    if ('q' == c || 'Q' == c)
-                        state = STATE_Q;
-                    break;
-                }
-                case STATE_Q:
-                {
-                    if ('=' == c)
-                        state = STATE_QVALUE;
-                    break;
-                }
-                case STATE_QVALUE:
-                {
-                    if (qvalueIdx < 0 && '0' == c || '1' == c)
-                        qvalueIdx = i;
-                    break;
-                }
-            }
-        }
-        
-        if (qvalueIdx < 0)
-            return true;
-               
-        if ("0".equals(encoding.substring(qvalueIdx).trim()))
-            return false;
-        return true;
-    }
-    
-
-    protected CompressedResponseWrapper createWrappedResponse(HttpServletRequest request, HttpServletResponse response, final String compressionType)
-    {
-        CompressedResponseWrapper wrappedResponse = null;
-        wrappedResponse = new CompressedResponseWrapper(request,response)
-        {
-            @Override
-            protected AbstractCompressedStream newCompressedStream(HttpServletRequest request, HttpServletResponse response) throws IOException
-            {
-                return new AbstractCompressedStream(compressionType,request,this,_vary)
-                {
-                    private Deflater _allocatedDeflater;
-                    private byte[] _allocatedBuffer;
-
-                    @Override
-                    protected OutputStream createStream() throws IOException
-                    {
-                        if (compressionType == null)
-                        {
-                            return null;
-                        }
-                        
-                        // acquire deflater instance
-                        _allocatedDeflater = _deflater.get();   
-                        if (_allocatedDeflater==null)
-                            _allocatedDeflater = new Deflater(_deflateCompressionLevel,_deflateNoWrap);
-                        else
-                        {
-                            _deflater.set(null);
-                            _allocatedDeflater.reset();
-                        }
-                        
-                        // acquire buffer
-                        _allocatedBuffer = _buffer.get();
-                        if (_allocatedBuffer==null)
-                            _allocatedBuffer = new byte[_bufferSize];
-                        else
-                            _buffer.set(null);
-                        
-                        switch (compressionType)
-                        {
-                            case GZIP:
-                                return new GzipOutputStream(_response.getOutputStream(),_allocatedDeflater,_allocatedBuffer);
-                            case DEFLATE:
-                                return new DeflatedOutputStream(_response.getOutputStream(),_allocatedDeflater,_allocatedBuffer);
-                        }
-                        throw new IllegalStateException(compressionType + " not supported");
-                    }
-
-                    @Override
-                    public void finish() throws IOException
-                    {
-                        super.finish();
-                        if (_allocatedDeflater != null && _deflater.get() == null)
-                        {
-                            _deflater.set(_allocatedDeflater);
-                        }
-                        if (_allocatedBuffer != null && _buffer.get() == null)
-                        {
-                            _buffer.set(_allocatedBuffer);
-                        }
-                    }
-                };
-            }
-        };
-        configureWrappedResponse(wrappedResponse);
-        return wrappedResponse;
-    }
-
-    protected void configureWrappedResponse(CompressedResponseWrapper wrappedResponse)
-    {
-        IncludeExclude<String> mimeTypeExclusions = new IncludeExclude<>();
-        if(_excludeMimeTypes) 
-            mimeTypeExclusions.getExcluded().addAll(_mimeTypes);
-        else
-            mimeTypeExclusions.getIncluded().addAll(_mimeTypes);
-        
-        wrappedResponse.setMimeTypes(mimeTypeExclusions);
-        wrappedResponse.setBufferSize(_bufferSize);
-        wrappedResponse.setMinCompressSize(_minGzipSize);
-    }
-
-    private class FinishOnCompleteListener implements AsyncListener
-    {    
-        private CompressedResponseWrapper wrappedResponse;
-
-        public FinishOnCompleteListener(CompressedResponseWrapper wrappedResponse)
-        {
-            this.wrappedResponse = wrappedResponse;
-        }
-
-        @Override
-        public void onComplete(AsyncEvent event) throws IOException
-        {          
-            try
-            {
-                wrappedResponse.finish();
-            }
-            catch (IOException e)
-            {
-                LOG.warn(e);
-            }
-        }
-
-        @Override
-        public void onTimeout(AsyncEvent event) throws IOException
-        {
-        }
-
-        @Override
-        public void onError(AsyncEvent event) throws IOException
-        {
-        }
-
-        @Override
-        public void onStartAsync(AsyncEvent event) throws IOException
-        {
-        }
-    }
-
-    /**
-     * Checks to see if the userAgent is excluded
-     *
-     * @param ua
-     *            the user agent
-     * @return boolean true if excluded
-     */
-    private boolean isExcludedAgent(String ua)
-    {
-        if (ua == null)
-            return false;
-
-        if (_excludedAgents != null)
-        {
-            if (_excludedAgents.contains(ua))
-            {
-                return true;
-            }
-        }
-        if (_excludedAgentPatterns != null)
-        {
-            for (Pattern pattern : _excludedAgentPatterns)
-            {
-                if (pattern.matcher(ua).matches())
-                {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Checks to see if the path is excluded
-     *
-     * @param requestURI
-     *            the request uri
-     * @return boolean true if excluded
-     */
-    private boolean isExcludedPath(String requestURI)
-    {
-        if (requestURI == null)
-            return false;
-        if (_excludedPaths != null)
-        {
-            for (String excludedPath : _excludedPaths)
-            {
-                if (requestURI.startsWith(excludedPath))
-                {
-                    return true;
-                }
-            }
-        }
-        if (_excludedPathPatterns != null)
-        {
-            for (Pattern pattern : _excludedPathPatterns)
-            {
-                if (pattern.matcher(requestURI).matches())
-                {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
 }
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/IncludableGzipFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/IncludableGzipFilter.java
index 196245a..5317300 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/IncludableGzipFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/IncludableGzipFilter.java
@@ -18,161 +18,7 @@
 
 package org.eclipse.jetty.servlets;
 
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
-import java.util.zip.Deflater;
-import java.util.zip.DeflaterOutputStream;
-import java.util.zip.GZIPOutputStream;
-
-import javax.servlet.DispatcherType;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.io.UncheckedPrintWriter;
-import org.eclipse.jetty.servlets.gzip.AbstractCompressedStream;
-import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
-
-/* ------------------------------------------------------------ */
-/** Includable GZip Filter.
- * This extension to the {@link GzipFilter} that uses Jetty features to allow
- * headers to be set during calls to
- * {@link javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}.
- * This allows the gzip filter to function correct during includes and to make a decision to gzip or not
- * at the time the buffer fills and on the basis of all response headers.
- *
- * If the init parameter "uncheckedPrintWriter" is set to "true", then the PrintWriter used by
- * the wrapped getWriter will be {@link UncheckedPrintWriter}.
- *
- */
+@Deprecated
 public class IncludableGzipFilter extends GzipFilter
 {
-    boolean _uncheckedPrintWriter=false;
-
-    @Override
-    public void init(FilterConfig filterConfig) throws ServletException
-    {
-        super.init(filterConfig);
-
-        String tmp=filterConfig.getInitParameter("uncheckedPrintWriter");
-        if (tmp!=null)
-            _uncheckedPrintWriter=Boolean.valueOf(tmp).booleanValue();
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.GzipFilter#createWrappedResponse(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String)
-     */
-    @Override
-    protected CompressedResponseWrapper createWrappedResponse(HttpServletRequest request, HttpServletResponse response, final String compressionType)
-    {
-        CompressedResponseWrapper wrappedResponse = null;
-        if (compressionType==null)
-        {
-            wrappedResponse = new IncludableResponseWrapper(request,response)
-            {
-                @Override
-                protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
-                {
-                    return new AbstractCompressedStream(null,request,this,_vary)
-                    {
-                        @Override
-                        protected DeflaterOutputStream createStream() throws IOException
-                        {
-                            return null;
-                        }
-                    };
-                }
-            };
-        }
-        else if (compressionType.equals(GZIP))
-        {
-            wrappedResponse = new IncludableResponseWrapper(request,response)
-            {
-                @Override
-                protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
-                {
-                    return new AbstractCompressedStream(compressionType,request,this,_vary)
-                    {
-                        @Override
-                        protected DeflaterOutputStream createStream() throws IOException
-                        {
-                            return new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
-                        }
-                    };
-                }
-            };
-        }
-        else if (compressionType.equals(DEFLATE))
-        {
-            wrappedResponse = new IncludableResponseWrapper(request,response)
-            {
-                @Override
-                protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
-                {
-                    return new AbstractCompressedStream(compressionType,request,this,_vary)
-                    {
-                        @Override
-                        protected DeflaterOutputStream createStream() throws IOException
-                        {
-                            return new DeflaterOutputStream(_response.getOutputStream(),new Deflater(_deflateCompressionLevel, _deflateNoWrap));
-                        }
-                    };
-                }
-            };
-        }
-        else
-        {
-            throw new IllegalStateException(compressionType + " not supported");
-        }
-        configureWrappedResponse(wrappedResponse);
-        return wrappedResponse;
-    }
-
-
-    // Extend CompressedResponseWrapper to be able to set headers during include and to create unchecked printwriters
-    private abstract class IncludableResponseWrapper extends CompressedResponseWrapper
-    {
-        public IncludableResponseWrapper(HttpServletRequest request, HttpServletResponse response)
-        {
-            super(request,response);
-        }
-
-        @Override
-        public void setHeader(String name,String value)
-        {
-            if (getRequest().getDispatcherType()==DispatcherType.INCLUDE)
-            {
-                if (!"etag".equalsIgnoreCase(name) && !name.startsWith("content-"))
-                {
-                    HttpServletResponse response = (HttpServletResponse)getResponse();
-                    response.setHeader("org.eclipse.jetty.server.include."+name,value);
-                }
-            }
-            else
-                super.setHeader(name,value);
-        }
-
-        @Override
-        public void addHeader(String name, String value)
-        {
-            super.addHeader(name, value);
-            HttpServletResponse response = (HttpServletResponse)getResponse();
-            if (!response.containsHeader(name))
-                setHeader(name,value);
-        }
-        
-        @Override
-        protected PrintWriter newWriter(OutputStream out, String encoding) throws UnsupportedEncodingException
-        {
-            if (_uncheckedPrintWriter)
-                return encoding == null?new UncheckedPrintWriter(out):new UncheckedPrintWriter(new OutputStreamWriter(out,encoding));
-            return super.newWriter(out,encoding);
-        }
-    }
-
 }
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java
index 1ef178c..e3ad5c1 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java
@@ -56,7 +56,6 @@
 import org.eclipse.jetty.util.log.Logger;
 
 
-/* ------------------------------------------------------------ */
 /**
  * Multipart Form Data Filter.
  * <p>
@@ -95,6 +94,7 @@
  * </dl>
  * @deprecated See servlet 3.0 apis like javax.servlet.http.HttpServletRequest.getParts()
  */
+@Deprecated
 public class MultiPartFilter implements Filter
 {
     private static final Logger LOG = Log.getLogger(MultiPartFilter.class);
@@ -104,6 +104,7 @@
     private boolean _deleteFiles;
     private ServletContext _context;
     private int _fileOutputBuffer = 0;
+    private boolean _writeFilesWithFilenames = false;
     private long _maxFileSize = -1L;
     private long _maxRequestSize = -1L;
     private int _maxFormKeys = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormKeys", 1000);
@@ -130,6 +131,7 @@
         String mfks = filterConfig.getInitParameter("maxFormKeys");
         if (mfks!=null)
             _maxFormKeys=Integer.parseInt(mfks);
+        _writeFilesWithFilenames = "true".equalsIgnoreCase(filterConfig.getInitParameter("writeFilesWithFilenames"));
     }
 
     /* ------------------------------------------------------------------------------- */
@@ -147,7 +149,6 @@
             return;
         }
 
-        InputStream in = new BufferedInputStream(request.getInputStream());
         String content_type=srequest.getContentType();
 
         //Get current parameters so we can merge into them
@@ -162,11 +163,12 @@
         }
 
         MultipartConfigElement config = new MultipartConfigElement(tempdir.getCanonicalPath(), _maxFileSize, _maxRequestSize, _fileOutputBuffer);
-        MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(in, content_type, config, tempdir);
+        MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(request.getInputStream(), content_type, config, tempdir);
         mpis.setDeleteOnExit(_deleteFiles);
+        mpis.setWriteFilesWithFilenames(_writeFilesWithFilenames);
         request.setAttribute(MULTIPART, mpis);
         try
-        {
+        {  
             Collection<Part> parts = mpis.getParts();
             if (parts != null)
             {
@@ -174,6 +176,8 @@
                 while (itor.hasNext() && params.size() < _maxFormKeys)
                 {
                     Part p = itor.next();
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("{}",p);
                     MultiPartInputStreamParser.MultiPart mp = (MultiPartInputStreamParser.MultiPart)p;
                     if (mp.getFile() != null)
                     {
@@ -243,9 +247,6 @@
         MultiMap<Object> _params;
 
         /* ------------------------------------------------------------------------------- */
-        /** Constructor.
-         * @param request
-         */
         public Wrapper(HttpServletRequest request, MultiMap map)
         {
             super(request);
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushCacheFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushCacheFilter.java
new file mode 100644
index 0000000..b88a273
--- /dev/null
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushCacheFilter.java
@@ -0,0 +1,335 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.servlets;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.server.Dispatcher;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.annotation.ManagedOperation;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * <p>A filter that builds a cache of secondary resources associated
+ * to primary resources.</p>
+ * <p>A typical request for a primary resource such as {@code index.html}
+ * is immediately followed by a number of requests for secondary resources.
+ * Secondary resource requests will have a {@code Referer} HTTP header
+ * that points to {@code index.html}, which is used to associate the secondary
+ * resource to the primary resource.</p>
+ * <p>Only secondary resources that are requested within a (small) time period
+ * from the request of the primary resource are associated with the primary
+ * resource.</p>
+ * <p>This allows to build a cache of secondary resources associated with
+ * primary resources. When a request for a primary resource arrives, associated
+ * secondary resources are pushed to the client, unless the request carries
+ * {@code If-xxx} header that hint that the client has the resources in its
+ * cache.</p>
+ * <p>If the init param useQueryInKey is set, then the query string is used as
+ * as part of the key to identify a resource</p>
+ */
+@ManagedObject("Push cache based on the HTTP 'Referer' header")
+public class PushCacheFilter implements Filter
+{
+    private static final Logger LOG = Log.getLogger(PushCacheFilter.class);
+
+    private final Set<Integer> _ports = new HashSet<>();
+    private final Set<String> _hosts = new HashSet<>();
+    private final ConcurrentMap<String, PrimaryResource> _cache = new ConcurrentHashMap<>();
+    private long _associatePeriod = 4000L;
+    private int _maxAssociations = 16;
+    private long _renew = System.nanoTime();
+    private boolean _useQueryInKey;
+
+    @Override
+    public void init(FilterConfig config) throws ServletException
+    {
+        String associatePeriod = config.getInitParameter("associatePeriod");
+        if (associatePeriod != null)
+            _associatePeriod = Long.parseLong(associatePeriod);
+
+        String maxAssociations = config.getInitParameter("maxAssociations");
+        if (maxAssociations != null)
+            _maxAssociations = Integer.parseInt(maxAssociations);
+
+        String hosts = config.getInitParameter("hosts");
+        if (hosts != null)
+            Collections.addAll(_hosts, StringUtil.csvSplit(hosts));
+
+        String ports = config.getInitParameter("ports");
+        if (ports != null)
+            for (String p : StringUtil.csvSplit(ports))
+                _ports.add(Integer.parseInt(p));
+
+        _useQueryInKey = Boolean.parseBoolean(config.getInitParameter("useQueryInKey"));
+
+        // Expose for JMX.
+        config.getServletContext().setAttribute(config.getFilterName(), this);
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("period={} max={} hosts={} ports={}", _associatePeriod, _maxAssociations, _hosts, _ports);
+    }
+
+    @Override
+    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException
+    {
+        HttpServletRequest request = (HttpServletRequest)req;
+
+        if (HttpVersion.fromString(req.getProtocol()).getVersion() < 20 ||
+                !HttpMethod.GET.is(request.getMethod()))
+        {
+            chain.doFilter(req, resp);
+            return;
+        }
+
+        long now = System.nanoTime();
+
+        // Iterating over fields is more efficient than multiple gets
+        Request jettyRequest = Request.getBaseRequest(request);
+        HttpFields fields = jettyRequest.getHttpFields();
+        boolean conditional = false;
+        String referrer = null;
+        loop:
+        for (int i = 0; i < fields.size(); i++)
+        {
+            HttpField field = fields.getField(i);
+            HttpHeader header = field.getHeader();
+            if (header == null)
+                continue;
+
+            switch (header)
+            {
+                case IF_MATCH:
+                case IF_MODIFIED_SINCE:
+                case IF_NONE_MATCH:
+                case IF_UNMODIFIED_SINCE:
+                    conditional = true;
+                    break loop;
+
+                case REFERER:
+                    referrer = field.getValue();
+                    break;
+
+                default:
+                    break;
+            }
+        }
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} {} referrer={} conditional={} synthetic={}", request.getMethod(), request.getRequestURI(), referrer, conditional, isPushRequest(request));
+
+        String key = URIUtil.addPaths(request.getServletPath(), request.getPathInfo());
+        if (_useQueryInKey)
+        {
+            String query = request.getQueryString();
+            if (query != null)
+                key += "?" + query;
+        }
+        if (referrer != null)
+        {
+            HttpURI referrerURI = new HttpURI(referrer);
+            String host = referrerURI.getHost();
+            int port = referrerURI.getPort();
+            if (port <= 0)
+                port = request.isSecure() ? 443 : 80;
+
+            boolean referredFromHere = _hosts.size() > 0 ? _hosts.contains(host) : host.equals(request.getServerName());
+            referredFromHere &= _ports.size() > 0 ? _ports.contains(port) : port == request.getServerPort();
+
+            if (referredFromHere)
+            {
+                if (HttpMethod.GET.is(request.getMethod()))
+                {
+                    String referrerPath = _useQueryInKey?referrerURI.getPathQuery():referrerURI.getPath();
+                    if (referrerPath == null)
+                        referrerPath = "/";
+                    if (referrerPath.startsWith(request.getContextPath()))
+                    {
+                        String referrerPathNoContext = referrerPath.substring(request.getContextPath().length());
+                        if (!referrerPathNoContext.equals(key))
+                        {
+                            PrimaryResource primaryResource = _cache.get(referrerPathNoContext);
+                            if (primaryResource != null)
+                            {
+                                long primaryTimestamp = primaryResource._timestamp.get();
+                                if (primaryTimestamp != 0)
+                                {
+                                    RequestDispatcher dispatcher = request.getServletContext().getRequestDispatcher(key);
+                                    if (now - primaryTimestamp < TimeUnit.MILLISECONDS.toNanos(_associatePeriod))
+                                    {
+                                        ConcurrentMap<String, RequestDispatcher> associated = primaryResource._associated;
+                                        // Not strictly concurrent-safe, just best effort to limit associations.
+                                        if (associated.size() <= _maxAssociations)
+                                        {
+                                            if (associated.putIfAbsent(key, dispatcher) == null)
+                                            {
+                                                if (LOG.isDebugEnabled())
+                                                    LOG.debug("Associated {} to {}", key, referrerPathNoContext);
+                                            }
+                                        }
+                                        else
+                                        {
+                                            if (LOG.isDebugEnabled())
+                                                LOG.debug("Not associated {} to {}, exceeded max associations of {}", key, referrerPathNoContext, _maxAssociations);
+                                        }
+                                    }
+                                    else
+                                    {
+                                        if (LOG.isDebugEnabled())
+                                            LOG.debug("Not associated {} to {}, outside associate period of {}ms", key, referrerPathNoContext, _associatePeriod);
+                                    }
+                                }
+                            }
+                        }
+                        else
+                        {
+                            if (LOG.isDebugEnabled())
+                                LOG.debug("Not associated {} to {}, referring to self", key, referrerPathNoContext);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("External referrer {}", referrer);
+            }
+        }
+
+        PrimaryResource primaryResource = _cache.get(key);
+        if (primaryResource == null)
+        {
+            PrimaryResource r = new PrimaryResource();
+            primaryResource = _cache.putIfAbsent(key, r);
+            primaryResource = primaryResource == null ? r : primaryResource;
+            primaryResource._timestamp.compareAndSet(0, now);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Cached primary resource {}", key);
+        }
+        else
+        {
+            long last = primaryResource._timestamp.get();
+            if (last < _renew && primaryResource._timestamp.compareAndSet(last, now))
+            {
+                primaryResource._associated.clear();
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Clear associated resources for {}", key);
+            }
+        }
+
+        // Push associated resources.
+        if (!isPushRequest(request) && !conditional && !primaryResource._associated.isEmpty())
+        {
+            // Breadth-first push of associated resources.
+            Queue<PrimaryResource> queue = new ArrayDeque<>();
+            queue.offer(primaryResource);
+            while (!queue.isEmpty())
+            {
+                PrimaryResource parent = queue.poll();
+                for (Map.Entry<String, RequestDispatcher> entry : parent._associated.entrySet())
+                {
+                    PrimaryResource child = _cache.get(entry.getKey());
+                    if (child != null)
+                        queue.offer(child);
+
+                    Dispatcher dispatcher = (Dispatcher)entry.getValue();
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Pushing {} for {}", dispatcher, key);
+                    dispatcher.push(request);
+                }
+            }
+        }
+
+        chain.doFilter(request, resp);
+    }
+
+    private boolean isPushRequest(HttpServletRequest request)
+    {
+        return Boolean.TRUE.equals(request.getAttribute("org.eclipse.jetty.pushed"));
+    }
+
+    @Override
+    public void destroy()
+    {
+        clearPushCache();
+    }
+
+    @ManagedAttribute("The push cache contents")
+    public Map<String, String> getPushCache()
+    {
+        Map<String, String> result = new HashMap<>();
+        for (Map.Entry<String, PrimaryResource> entry : _cache.entrySet())
+        {
+            PrimaryResource resource = entry.getValue();
+            String value = String.format("size=%d: %s", resource._associated.size(), new TreeSet<>(resource._associated.keySet()));
+            result.put(entry.getKey(), value);
+        }
+        return result;
+    }
+
+    @ManagedOperation(value = "Renews the push cache contents", impact = "ACTION")
+    public void renewPushCache()
+    {
+        _renew = System.nanoTime();
+    }
+
+    @ManagedOperation(value = "Clears the push cache contents", impact = "ACTION")
+    public void clearPushCache()
+    {
+        _cache.clear();
+    }
+
+    private static class PrimaryResource
+    {
+        private final ConcurrentMap<String, RequestDispatcher> _associated = new ConcurrentHashMap<>();
+        private final AtomicLong _timestamp = new AtomicLong();
+    }
+}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java
new file mode 100644
index 0000000..7d86dca
--- /dev/null
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java
@@ -0,0 +1,206 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.servlets;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.server.PushBuilder;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class PushSessionCacheFilter implements Filter
+{
+    private static final String TARGET_ATTR = "PushCacheFilter.target";
+    private static final String TIMESTAMP_ATTR = "PushCacheFilter.timestamp";
+    private static final Logger LOG = Log.getLogger(PushSessionCacheFilter.class);
+    private final ConcurrentMap<String, Target> _cache = new ConcurrentHashMap<>();
+    private long _associateDelay = 5000L;
+
+    @Override
+    public void init(FilterConfig config) throws ServletException
+    {
+        if (config.getInitParameter("associateDelay") != null)
+            _associateDelay = Long.valueOf(config.getInitParameter("associateDelay"));
+
+        // Add a listener that is used to collect information about associated resource,
+        // etags and modified dates
+        config.getServletContext().addListener(new ServletRequestListener()
+        {
+            // Collect information when request is destroyed.
+            @Override
+            public void requestDestroyed(ServletRequestEvent sre)
+            {
+                Request request = Request.getBaseRequest(sre.getServletRequest());
+                Target target = (Target)request.getAttribute(TARGET_ATTR);
+                if (target == null)
+                    return;
+
+                // Update conditional data
+                Response response = request.getResponse();
+                target._etag = response.getHttpFields().get(HttpHeader.ETAG);
+                target._lastModified = response.getHttpFields().get(HttpHeader.LAST_MODIFIED);
+
+                // Don't associate pushes
+                if (request.isPush())
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Pushed {} for {}", request.getResponse().getStatus(), request.getRequestURI());
+                    return;
+                }
+                else if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("Served {} for {}", request.getResponse().getStatus(), request.getRequestURI());
+                }
+
+                // Does this request have a referer?
+                String referer = request.getHttpFields().get(HttpHeader.REFERER);
+
+                if (referer != null)
+                {
+                    // Is the referer from this contexts?
+                    HttpURI referer_uri = new HttpURI(referer);
+                    if (request.getServerName().equals(referer_uri.getHost()))
+                    {
+                        Target referer_target = _cache.get(referer_uri.getPath());
+                        if (referer_target != null)
+                        {
+                            HttpSession session = request.getSession();
+                            ConcurrentHashMap<String, Long> timestamps = (ConcurrentHashMap<String, Long>)session.getAttribute(TIMESTAMP_ATTR);
+                            Long last = timestamps.get(referer_target._path);
+                            if (last != null && (System.currentTimeMillis() - last) < _associateDelay)
+                            {
+                                if (referer_target._associated.putIfAbsent(target._path, target) == null)
+                                {
+                                    if (LOG.isDebugEnabled())
+                                        LOG.debug("ASSOCIATE {}->{}", referer_target._path, target._path);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            @Override
+            public void requestInitialized(ServletRequestEvent sre)
+            {
+            }
+        });
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+    {
+        // Get Jetty request as these APIs are not yet standard
+        Request baseRequest = Request.getBaseRequest(request);
+        String uri = baseRequest.getRequestURI();
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} {} push={}", baseRequest.getMethod(), uri, baseRequest.isPush());
+
+        HttpSession session = baseRequest.getSession(true);
+
+        // find the target for this resource
+        Target target = _cache.get(uri);
+        if (target == null)
+        {
+            Target t = new Target(uri);
+            target = _cache.putIfAbsent(uri, t);
+            target = target == null ? t : target;
+        }
+        request.setAttribute(TARGET_ATTR, target);
+
+        // Set the timestamp for this resource in this session
+        ConcurrentHashMap<String, Long> timestamps = (ConcurrentHashMap<String, Long>)session.getAttribute(TIMESTAMP_ATTR);
+        if (timestamps == null)
+        {
+            timestamps = new ConcurrentHashMap<>();
+            session.setAttribute(TIMESTAMP_ATTR, timestamps);
+        }
+        timestamps.put(uri, System.currentTimeMillis());
+
+        // push any associated resources
+        if (baseRequest.isPushSupported() && !baseRequest.isPush() && !target._associated.isEmpty())
+        {
+            // Breadth-first push of associated resources.
+            Queue<Target> queue = new ArrayDeque<>();
+            queue.offer(target);
+            while (!queue.isEmpty())
+            {
+                Target parent = queue.poll();
+                PushBuilder builder = baseRequest.getPushBuilder();
+                builder.addHeader("X-Pusher", PushSessionCacheFilter.class.toString());
+                for (Target child : parent._associated.values())
+                {
+                    queue.offer(child);
+
+                    String path = child._path;
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("PUSH {} <- {}", path, uri);
+
+                    builder.path(path).etag(child._etag).lastModified(child._lastModified).push();
+                }
+            }
+        }
+
+        chain.doFilter(request, response);
+    }
+
+    @Override
+    public void destroy()
+    {
+        _cache.clear();
+    }
+
+    private static class Target
+    {
+        private final String _path;
+        private final ConcurrentMap<String, Target> _associated = new ConcurrentHashMap<>();
+        private volatile String _etag;
+        private volatile String _lastModified;
+
+        private Target(String path)
+        {
+            _path = path;
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("Target{p=%s,e=%s,m=%s,a=%d}", _path, _etag, _lastModified, _associated.size());
+        }
+    }
+}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/QoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/QoSFilter.java
index 779362b..eee4b7a 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/QoSFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/QoSFilter.java
@@ -23,6 +23,7 @@
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
+
 import javax.servlet.AsyncContext;
 import javax.servlet.AsyncEvent;
 import javax.servlet.AsyncListener;
@@ -45,19 +46,19 @@
 
 /**
  * Quality of Service Filter.
- * <p/>
+ * <p>
  * This filter limits the number of active requests to the number set by the "maxRequests" init parameter (default 10).
  * If more requests are received, they are suspended and placed on priority queues.  Priorities are determined by
  * the {@link #getPriority(ServletRequest)} method and are a value between 0 and the value given by the "maxPriority"
  * init parameter (default 10), with higher values having higher priority.
- * <p/>
+ * <p>
  * This filter is ideal to prevent wasting threads waiting for slow/limited
  * resources such as a JDBC connection pool.  It avoids the situation where all of a
  * containers thread pool may be consumed blocking on such a slow resource.
  * By limiting the number of active threads, a smaller thread pool may be used as
  * the threads are not wasted waiting.  Thus more memory may be available for use by
  * the active threads.
- * <p/>
+ * <p>
  * Furthermore, this filter uses a priority when resuming waiting requests. So that if
  * a container is under load, and there are many requests waiting for resources,
  * the {@link #getPriority(ServletRequest)} method is used, so that more important
@@ -65,12 +66,12 @@
  * maxRequest limit slightly smaller than the containers thread pool and a high priority
  * allocated to admin users.  Thus regardless of load, admin users would always be
  * able to access the web application.
- * <p/>
+ * <p>
  * The maxRequest limit is policed by a {@link Semaphore} and the filter will wait a short while attempting to acquire
  * the semaphore. This wait is controlled by the "waitMs" init parameter and allows the expense of a suspend to be
  * avoided if the semaphore is shortly available.  If the semaphore cannot be obtained, the request will be suspended
  * for the default suspend period of the container or the valued set as the "suspendMs" init parameter.
- * <p/>
+ * <p>
  * If the "managedAttr" init parameter is set to true, then this servlet is set as a {@link ServletContext} attribute with the
  * filter name as the attribute name.  This allows context external mechanism (eg JMX via {@link ContextHandler#MANAGED_ATTRIBUTES}) to
  * manage the configuration of the filter.
@@ -236,7 +237,7 @@
 
     /**
      * Computes the request priority.
-     * <p/>
+     * <p>
      * The default implementation assigns the following priorities:
      * <ul>
      * <li> 2 - for an authenticated request
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/UserAgentFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/UserAgentFilter.java
deleted file mode 100644
index 15013d6..0000000
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/UserAgentFilter.java
+++ /dev/null
@@ -1,158 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets;
-
-import java.io.IOException;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-
-/* ------------------------------------------------------------ */
-/** User Agent Filter.
- * <p>
- * This filter allows efficient matching of user agent strings for
- * downstream or extended filters to use for browser specific logic.
- * </p>
- * <p>
- * The filter is configured with the following init parameters:
- * <dl>
- * <dt>attribute</dt><dd>If set, then the request attribute of this name is set with the matched user agent string</dd>
- * <dt>cacheSize</dt><dd>The size of the user-agent cache, used to avoid reparsing of user agent strings. The entire cache is flushed
- * when this size is reached</dd>
- * <dt>userAgent</dt><dd>A regex {@link Pattern} to extract the essential elements of the user agent.
- * The concatenation of matched pattern groups is used as the user agent name</dd>
- * <dl>
- * An example value for pattern is <code>(?:Mozilla[^\(]*\(compatible;\s*+([^;]*);.*)|(?:.*?([^\s]+/[^\s]+).*)</code>. These two
- * pattern match the common compatibility user-agent strings and extract the real user agent, failing that, the first
- * element of the agent string is returned.
- *
- *
- */
-public class UserAgentFilter implements Filter
-{
-    private static final String __defaultPattern = "(?:Mozilla[^\\(]*\\(compatible;\\s*+([^;]*);.*)|(?:.*?([^\\s]+/[^\\s]+).*)";
-    private Pattern _pattern = Pattern.compile(__defaultPattern);
-    private Map<String, String> _agentCache = new ConcurrentHashMap<String, String>();
-    private int _agentCacheSize=1024;
-    private String _attribute;
-
-    /* ------------------------------------------------------------ */
-    /* (non-Javadoc)
-     * @see javax.servlet.Filter#destroy()
-     */
-    public void destroy()
-    {
-    }
-
-    /* ------------------------------------------------------------ */
-    /* (non-Javadoc)
-     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
-     */
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
-    {
-        if (_attribute!=null && _pattern!=null)
-        {
-            String ua=getUserAgent(request);
-            request.setAttribute(_attribute,ua);
-        }
-        chain.doFilter(request,response);
-    }
-
-    /* ------------------------------------------------------------ */
-    /* (non-Javadoc)
-     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
-     */
-    public void init(FilterConfig filterConfig) throws ServletException
-    {
-        _attribute=filterConfig.getInitParameter("attribute");
-
-        String p=filterConfig.getInitParameter("userAgent");
-        if (p!=null)
-            _pattern=Pattern.compile(p);
-
-        String size=filterConfig.getInitParameter("cacheSize");
-        if (size!=null)
-            _agentCacheSize=Integer.parseInt(size);
-    }
-
-    /* ------------------------------------------------------------ */
-    public String getUserAgent(ServletRequest request)
-    {
-        String ua=((HttpServletRequest)request).getHeader("User-Agent");
-        return getUserAgent(ua);
-    }
-
-    /* ------------------------------------------------------------ */
-    /** Get UserAgent.
-     * The configured agent patterns are used to match against the passed user agent string.
-     * If any patterns match, the concatenation of pattern groups is returned as the user agent
-     * string. Match results are cached.
-     * @param ua A user agent string
-     * @return The matched pattern groups or the original user agent string
-     */
-    public String getUserAgent(String ua)
-    {
-        if (ua == null)
-            return null;
-
-        String tag = _agentCache.get(ua);
-
-        if (tag == null)
-        {
-            if (_pattern != null)
-            {
-                Matcher matcher = _pattern.matcher(ua);
-                if (matcher.matches())
-                {
-                    if (matcher.groupCount() > 0)
-                    {
-                        for (int g = 1; g <= matcher.groupCount(); g++)
-                        {
-                            String group = matcher.group(g);
-                            if (group != null)
-                                tag = tag == null ? group : tag + group;
-                        }
-                    }
-                    else
-                    {
-                        tag = matcher.group();
-                    }
-                }
-            }
-
-            if (tag == null)
-                tag = ua;
-
-            if (_agentCache.size() >= _agentCacheSize)
-                _agentCache.clear();
-            _agentCache.put(ua, tag);
-        }
-
-        return tag;
-    }
-}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/AbstractCompressedStream.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/AbstractCompressedStream.java
deleted file mode 100644
index 2c3e04a..0000000
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/AbstractCompressedStream.java
+++ /dev/null
@@ -1,409 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
-
-import javax.servlet.DispatcherType;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.WriteListener;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.server.Response;
-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;
-    private final HttpServletRequest _request;
-    protected final CompressedResponseWrapper _wrapper;
-    protected final HttpServletResponse _response;
-    protected OutputStream _out;
-    protected ByteArrayOutputStream2 _bOut;
-    protected OutputStream _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;
-        _request = request;
-        _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 &&_bOut==null || 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)
-            {
-                String prefix = "";
-                if (_request.getDispatcherType() == DispatcherType.INCLUDE)
-                    prefix = Response.SET_INCLUDE_HEADER_PREFIX;
-                setHeader(prefix+"Content-Encoding", _encoding);
-
-                if (_response.containsHeader("Content-Encoding"))
-                {
-                    addHeader(prefix+"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)
-                        {
-                            int end = etag.length()-1;
-                            if (etag.charAt(end)=='"')
-                                setHeader(prefix+"ETag",etag.substring(0,end)+"--"+_encoding+'"');
-                            else
-                                setHeader(prefix+"ETag",etag+"--"+_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();
-            }
-        }
-    }
-
-    public OutputStream getOutputStream()
-    {
-        return _out;
-    }
-
-    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);
-    }
-
-    @Override
-    public void setWriteListener(WriteListener writeListener)
-    {
-        throw new UnsupportedOperationException("Use AsyncGzipFilter");
-    }
-
-
-    @Override
-    public boolean isReady()
-    {
-        throw new UnsupportedOperationException("Use AsyncGzipFilter");
-    }
-
-    /**
-     * Create the stream fitting to the underlying compression type.
-     *
-     * @throws IOException
-     *             Signals that an I/O exception has occurred.
-     */
-    protected abstract OutputStream createStream() throws IOException;
-
-
-}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/CompressedResponseWrapper.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/CompressedResponseWrapper.java
deleted file mode 100644
index dad943e..0000000
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/CompressedResponseWrapper.java
+++ /dev/null
@@ -1,483 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
-
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpServletResponseWrapper;
-
-import org.eclipse.jetty.util.IncludeExclude;
-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 IncludeExclude<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;
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public int getBufferSize()
-    {
-        return _bufferSize;
-    }
-    
-    /* ------------------------------------------------------------ */
-    public int getMinCompressSize()
-    {
-        return _minCompressSize;
-    }
-    
-    /* ------------------------------------------------------------ */
-    public String getETag()
-    {
-        return _etag;
-    }
-
-    /* ------------------------------------------------------------ */
-    public HttpServletRequest getRequest()
-    {
-        return _request;
-    }
-    
-    /* ------------------------------------------------------------ */
-    /**
-     */
-    public void setMimeTypes(IncludeExclude<String> mimeTypes)
-    {
-        _mimeTypes = mimeTypes;
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     */
-    @Override
-    public void setBufferSize(int bufferSize)
-    {
-        _bufferSize = bufferSize;
-        if (_compressedStream!=null)
-            _compressedStream.setBufferSize(bufferSize);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setMinCompressSize(int)
-     */
-    public void setMinCompressSize(int minCompressSize)
-    {
-        _minCompressSize = minCompressSize;
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setContentType(java.lang.String)
-     */
-    @Override
-    public void setContentType(String ct)
-    {
-        super.setContentType(ct);
-
-        if (!_noCompression && (_compressedStream==null || _compressedStream.getOutputStream()==null))
-        {
-            if (ct!=null)
-            {
-                int colon=ct.indexOf(";");
-                if (colon>0)
-                    ct=ct.substring(0,colon);
-
-                if (!_mimeTypes.matches(StringUtil.asciiToLowerCase(ct)))
-                    noCompression();
-            }
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setStatus(int, java.lang.String)
-     */
-    @SuppressWarnings("deprecation")
-    @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.servlets.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.servlets.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.servlets.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.servlets.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.servlets.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.servlets.gzip.CompressedResponseWrapper#resetBuffer()
-     */
-    @Override
-    public void resetBuffer()
-    {
-        super.resetBuffer();
-        if (_compressedStream!=null)
-            _compressedStream.resetBuffer();
-        _writer=null;
-        _compressedStream=null;
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.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.servlets.gzip.CompressedResponseWrapper#sendError(int)
-     */
-    @Override
-    public void sendError(int sc) throws IOException
-    {
-        resetBuffer();
-        super.sendError(sc);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#sendRedirect(java.lang.String)
-     */
-    @Override
-    public void sendRedirect(String location) throws IOException
-    {
-        resetBuffer();
-        super.sendRedirect(location);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.servlets.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.servlets.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.servlets.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.servlets.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.servlets.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.servlets.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;
-
-}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/DeflatedOutputStream.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/DeflatedOutputStream.java
deleted file mode 100644
index 2f7cce5..0000000
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/DeflatedOutputStream.java
+++ /dev/null
@@ -1,101 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.zip.Deflater;
-
-/**
- * Reimplementation of {@link java.util.zip.DeflaterOutputStream} that supports reusing the buffer.
- */
-public class DeflatedOutputStream extends FilterOutputStream
-{
-    protected final Deflater _def;
-    protected final byte[] _buf;
-    protected boolean closed = false;
-
-    public DeflatedOutputStream(OutputStream out, Deflater deflater, byte[] buffer)
-    {
-        super(out);
-        _def = deflater;
-        _buf = buffer;
-    }
-
-    @Override
-    public void write(int b) throws IOException
-    {
-        byte[] buf = new byte[1];
-        buf[0] = (byte)(b & 0xff);
-        write(buf,0,1);
-    }
-
-    @Override
-    public void write(byte[] b, int off, int len) throws IOException
-    {
-        if (_def.finished())
-            throw new IOException("Stream already finished");
-        if ((off | len | (off + len) | (b.length - (off + len))) < 0)
-            throw new IndexOutOfBoundsException();
-        if (len == 0)
-            return;
-        if (!_def.finished())
-        {
-            _def.setInput(b,off,len);
-            while (!_def.needsInput())
-            {
-                deflate();
-            }
-        }
-    }
-
-    private void deflate() throws IOException
-    {
-        int len = _def.deflate(_buf,0,_buf.length);
-        if (len > 0)
-        {
-            out.write(_buf,0,len);
-        }
-    }
-
-    public synchronized void finish() throws IOException
-    {
-        if (!_def.finished())
-        {
-            _def.finish();
-            while (!_def.finished())
-            {
-                deflate();
-            }
-        }
-    }
-
-    @Override
-    public synchronized void close() throws IOException
-    {
-        if (!closed)
-        {
-            finish();
-            out.close();
-            closed = true;
-        }
-    }
-
-}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipFactory.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipFactory.java
deleted file mode 100644
index ff1a143..0000000
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.util.zip.Deflater;
-
-import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.server.Request;
-
-public interface GzipFactory
-{
-    int getBufferSize();
-    
-    HttpField getVaryField();
-
-    Deflater getDeflater(Request request, long content_length);
-
-    boolean isExcludedMimeType(String asciiToLowerCase);
-
-    void recycle(Deflater deflater);
-
-}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHandler.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHandler.java
deleted file mode 100644
index 5ffaba1..0000000
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHandler.java
+++ /dev/null
@@ -1,647 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
-import java.util.Set;
-import java.util.zip.DeflaterOutputStream;
-import java.util.zip.GZIPOutputStream;
-
-import javax.servlet.AsyncEvent;
-import javax.servlet.AsyncListener;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.MimeTypes;
-import org.eclipse.jetty.http.pathmap.PathSpecSet;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.HandlerWrapper;
-import org.eclipse.jetty.util.IncludeExclude;
-import org.eclipse.jetty.util.RegexSet;
-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;
-
-/* ------------------------------------------------------------ */
-/**
- * GZIP Handler This handler will gzip the content of a response if:
- * <ul>
- * <li>The handler 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 matches one of the set of mimetypes to be compressed</li>
- * <li>The content-type does NOT match one of the set of mimetypes AND setExcludeMimeTypes is <code>true</code></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 int _bufferSize = 8192;
-    protected int _minGzipSize = 256;
-    protected String _vary = "Accept-Encoding, User-Agent";
-    
-    private final IncludeExclude<String> _agentPatterns=new IncludeExclude<>(RegexSet.class);
-    private final IncludeExclude<String> _methods = new IncludeExclude<>();
-    private final IncludeExclude<String> _paths = new IncludeExclude<String>(PathSpecSet.class);
-    private final IncludeExclude<String> _mimeTypes = new IncludeExclude<>();
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Instantiates a new gzip handler.
-     */
-    public GzipHandler()
-    {
-        _methods.include(HttpMethod.GET.asString());
-        for (String type:MimeTypes.getKnownMimeTypes())
-        {
-            if ("image/svg+xml".equals(type))
-                _paths.exclude("*.svgz");
-            else if (type.startsWith("image/")||
-                type.startsWith("audio/")||
-                type.startsWith("video/"))
-                _mimeTypes.exclude(type);
-        }
-        _mimeTypes.exclude("application/compress");
-        _mimeTypes.exclude("application/zip");
-        _mimeTypes.exclude("application/gzip");
-        _mimeTypes.exclude("application/bzip2");
-        _mimeTypes.exclude("application/x-rar-compressed");
-        LOG.debug("{} mime types {}",this,_mimeTypes);
-        
-        _agentPatterns.exclude(".*MSIE 6.0.*");
-    }
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * @param patterns Regular expressions matching user agents to exclude
-     */
-    public void addExcludedAgentPatterns(String... patterns)
-    {
-        _agentPatterns.exclude(patterns);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @param methods The methods to exclude in compression
-     */
-    public void addExcludedMethods(String... methods)
-    {
-        for (String m : methods)
-            _methods.exclude(m);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Set the mime types.
-     * @param types The mime types to exclude (without charset or other parameters).
-     * For backward compatibility the mimetypes may be comma separated strings, but this
-     * will not be supported in future versions.
-     */
-    public void addExcludedMimeTypes(String... types)
-    {
-        for (String t : types)
-            _mimeTypes.exclude(StringUtil.csvSplit(t));
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Add path to excluded paths list.
-     * <p>
-     * There are 2 syntaxes supported, Servlet <code>url-pattern</code> based, and
-     * Regex based.  This means that the initial characters on the path spec
-     * line are very strict, and determine the behavior of the path matching.
-     * <ul>
-     *  <li>If the spec starts with <code>'^'</code> the spec is assumed to be
-     *      a regex based path spec and will match with normal Java regex rules.</li>
-     *  <li>If the spec starts with <code>'/'</code> then spec is assumed to be
-     *      a Servlet url-pattern rules path spec for either an exact match
-     *      or prefix based match.</li>
-     *  <li>If the spec starts with <code>'*.'</code> then spec is assumed to be
-     *      a Servlet url-pattern rules path spec for a suffix based match.</li>
-     *  <li>All other syntaxes are unsupported</li> 
-     * </ul>
-     * <p>
-     * Note: inclusion takes precedence over exclude.
-     * 
-     * @param pathspecs Path specs (as per servlet spec) to exclude. If a 
-     * ServletContext is available, the paths are relative to the context path,
-     * otherwise they are absolute.<br>
-     * For backward compatibility the pathspecs may be comma separated strings, but this
-     * will not be supported in future versions.
-     */
-    public void addExcludedPaths(String... pathspecs)
-    {
-        for (String p : pathspecs)
-            _paths.exclude(StringUtil.csvSplit(p));
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @param patterns Regular expressions matching user agents to exclude
-     */
-    public void addIncludedAgentPatterns(String... patterns)
-    {
-        _agentPatterns.include(patterns);
-    }
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * @param methods The methods to include in compression
-     */
-    public void addIncludedMethods(String... methods)
-    {
-        for (String m : methods)
-            _methods.include(m);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Add included mime types. Inclusion takes precedence over
-     * exclusion.
-     * @param types The mime types to include (without charset or other parameters)
-     * For backward compatibility the mimetypes may be comma separated strings, but this
-     * will not be supported in future versions.
-     */
-    public void addIncludedMimeTypes(String... types)
-    {
-        for (String t : types)
-            _mimeTypes.include(StringUtil.csvSplit(t));
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Add path specs to include.
-     * <p>
-     * There are 2 syntaxes supported, Servlet <code>url-pattern</code> based, and
-     * Regex based.  This means that the initial characters on the path spec
-     * line are very strict, and determine the behavior of the path matching.
-     * <ul>
-     *  <li>If the spec starts with <code>'^'</code> the spec is assumed to be
-     *      a regex based path spec and will match with normal Java regex rules.</li>
-     *  <li>If the spec starts with <code>'/'</code> then spec is assumed to be
-     *      a Servlet url-pattern rules path spec for either an exact match
-     *      or prefix based match.</li>
-     *  <li>If the spec starts with <code>'*.'</code> then spec is assumed to be
-     *      a Servlet url-pattern rules path spec for a suffix based match.</li>
-     *  <li>All other syntaxes are unsupported</li> 
-     * </ul>
-     * <p>
-     * Note: inclusion takes precedence over exclude.
-     * 
-     * @param pathspecs Path specs (as per servlet spec) to include. If a 
-     * ServletContext is available, the paths are relative to the context path,
-     * otherwise they are absolute
-     */
-    public void addIncludedPaths(String... pathspecs)
-    {
-        for (String p : pathspecs)
-            _paths.include(StringUtil.csvSplit(p));
-    }
-    
-    /* ------------------------------------------------------------ */
-    public String[] getExcludedAgentPatterns()
-    {
-        Set<String> excluded=_agentPatterns.getExcluded();
-        return excluded.toArray(new String[excluded.size()]);
-    }
-
-    /* ------------------------------------------------------------ */
-    public String[] getExcludedMethods()
-    {
-        Set<String> excluded=_methods.getExcluded();
-        return excluded.toArray(new String[excluded.size()]);
-    }
-
-    /* ------------------------------------------------------------ */
-    public String[] getExcludedMimeTypes()
-    {
-        Set<String> excluded=_mimeTypes.getExcluded();
-        return excluded.toArray(new String[excluded.size()]);
-    }
-
-    /* ------------------------------------------------------------ */
-    public String[] getExcludedPaths()
-    {
-        Set<String> excluded=_paths.getExcluded();
-        return excluded.toArray(new String[excluded.size()]);
-    }
-
-    /* ------------------------------------------------------------ */
-    public String[] getIncludedAgentPatterns()
-    {
-        Set<String> includes=_agentPatterns.getIncluded();
-        return includes.toArray(new String[includes.size()]);
-    }
-    
-    /* ------------------------------------------------------------ */
-    public String[] getIncludedMethods()
-    {
-        Set<String> includes=_methods.getIncluded();
-        return includes.toArray(new String[includes.size()]);
-    }
-
-    /* ------------------------------------------------------------ */
-    public String[] getIncludedMimeTypes()
-    {
-        Set<String> includes=_mimeTypes.getIncluded();
-        return includes.toArray(new String[includes.size()]);
-    }
-
-    /* ------------------------------------------------------------ */
-    public String[] getIncludedPaths()
-    {
-        Set<String> includes=_paths.getIncluded();
-        return includes.toArray(new String[includes.size()]);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Get the mime types.
-     *
-     * @return mime types to set
-     * @deprecated use {@link #getExcludedMimeTypes()} or {@link #getIncludedMimeTypes()} instead
-     */
-    @Deprecated
-    public Set<String> getMimeTypes()
-    {
-        throw new UnsupportedOperationException("Use getIncludedMimeTypes or getExcludedMimeTypes instead");
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Set the mime types.
-     *
-     * @param mimeTypes
-     *            the mime types to set
-     * @deprecated use {@link #setExcludedMimeTypes()} or {@link #setIncludedMimeTypes()} instead
-     */
-    @Deprecated
-    public void setMimeTypes(Set<String> mimeTypes)
-    {
-        throw new UnsupportedOperationException("Use setIncludedMimeTypes or setExcludedMimeTypes instead");
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Set the mime types.
-     *
-     * @param mimeTypes
-     *            the mime types to set
-     * @deprecated use {@link #setExcludedMimeTypes()} or {@link #setIncludedMimeTypes()} instead
-     */
-    @Deprecated
-    public void setMimeTypes(String mimeTypes)
-    {
-        throw new UnsupportedOperationException("Use setIncludedMimeTypes or setExcludedMimeTypes instead");
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Set the mime types.
-     * @deprecated use {@link #setExcludedMimeTypes()} instead
-     */
-    @Deprecated
-    public void setExcludeMimeTypes(boolean exclude)
-    {
-        throw new UnsupportedOperationException("Use setExcludedMimeTypes instead");
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Get the excluded user agents.
-     *
-     * @return excluded user agents
-     */
-    public Set<String> getExcluded()
-    {
-        return _agentPatterns.getExcluded();
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Set the excluded user agents.
-     *
-     * @param excluded
-     *            excluded user agents to set
-     */
-    public void setExcluded(Set<String> excluded)
-    {
-        _agentPatterns.getExcluded().clear();
-        _agentPatterns.getExcluded().addAll(excluded);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Set the excluded user agents.
-     *
-     * @param excluded
-     *            excluded user agents to set
-     */
-    public void setExcluded(String excluded)
-    {
-        _agentPatterns.getExcluded().clear();
-
-        if (excluded != null)
-        {
-            _agentPatterns.exclude(StringUtil.csvSplit(excluded));
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @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;
-    }
-    
-    /* ------------------------------------------------------------ */
-    @Override
-    protected void doStart() throws Exception
-    {
-        super.doStart();
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @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())
-        {
-            // do nothing
-            return;
-        }
-        
-        if(isGzippable(baseRequest, request, response))
-        {
-            final CompressedResponseWrapper wrappedResponse = newGzipResponseWrapper(request,response);
-
-            boolean exceptional=true;
-            try
-            {
-                _handler.handle(target, baseRequest, request, wrappedResponse);
-                exceptional=false;
-            }
-            finally
-            {
-                if (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
-                        {
-                        }
-                        
-                        @Override
-                        public void onComplete(AsyncEvent event) throws IOException
-                        {
-                            try
-                            {
-                                wrappedResponse.finish();
-                            }
-                            catch(IOException e)
-                            {
-                                LOG.warn(e);
-                            }
-                        }
-                    });
-                }
-                else if (exceptional && !response.isCommitted())
-                {
-                    wrappedResponse.resetBuffer();
-                    wrappedResponse.noCompression();
-                }
-                else
-                    wrappedResponse.finish();
-            }
-        }
-        else
-        {
-            _handler.handle(target,baseRequest, request, response);
-        }
-    }
-
-    private boolean isGzippable(Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-    {
-        String ae = request.getHeader("accept-encoding");
-        if (ae == null || !ae.contains("gzip"))
-        {
-            // Request not indicated for Gzip
-            return false;
-        }
-        
-        if(response.containsHeader("Content-Encoding"))
-        {
-            // Response is already declared, can't gzip
-            LOG.debug("{} excluded as Content-Encoding already declared {}",this,request);
-            return false;
-        }
-        
-        if(HttpMethod.HEAD.is(request.getMethod()))
-        {
-            // HEAD is never Gzip'd
-            LOG.debug("{} excluded by method {}",this,request);
-            return false;
-        }
-        
-        // Exclude based on Request Method
-        if (!_methods.matches(baseRequest.getMethod()))
-        {
-            LOG.debug("{} excluded by method {}",this,request);
-            return false;
-        }
-        
-        // Exclude based on Request Path
-        ServletContext context = baseRequest.getServletContext();
-        String path = context==null?baseRequest.getRequestURI():URIUtil.addPaths(baseRequest.getServletPath(),baseRequest.getPathInfo());
-
-        if(path != null && !_paths.matches(path))
-        {
-            LOG.debug("{} excluded by path {}",this,request);
-            return false;
-        }
-        
-        
-        // Exclude non compressible mime-types known from URI extension. - no Vary because no matter what client, this URI is always excluded
-        String mimeType = context==null?null:context.getMimeType(path);
-        if (mimeType!=null)
-        {
-            mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
-            if (!_mimeTypes.matches(mimeType))
-            {
-                LOG.debug("{} excluded by path suffix mime type {}",this,request);
-                return false;
-            }
-        }
-        
-        // Exclude on User Agent
-        String ua = request.getHeader("User-Agent");
-        if(ua != null && !_agentPatterns.matches(ua))
-        {
-            LOG.debug("{} excluded by user-agent {}",this,request);
-            return false;
-        }
-        
-        return true;
-    }
-
-    /**
-     * 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));
-    }
-}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java
deleted file mode 100644
index 6fd6114..0000000
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java
+++ /dev/null
@@ -1,405 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.nio.ByteBuffer;
-import java.nio.channels.WritePendingException;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.zip.CRC32;
-import java.util.zip.Deflater;
-
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpGenerator;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.MimeTypes;
-import org.eclipse.jetty.server.HttpChannel;
-import org.eclipse.jetty.server.HttpOutput;
-import org.eclipse.jetty.server.Response;
-import org.eclipse.jetty.servlets.AsyncGzipFilter;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.IteratingNestedCallback;
-import org.eclipse.jetty.util.StringUtil;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-public class GzipHttpOutput extends HttpOutput
-{
-    public static Logger LOG = Log.getLogger(GzipHttpOutput.class);
-    private final static HttpGenerator.CachedHttpField CONTENT_ENCODING_GZIP=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_ENCODING,"gzip");
-    private final static byte[] GZIP_HEADER = new byte[] { (byte)0x1f, (byte)0x8b, Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0 };
-    
-    private enum GZState { NOT_COMPRESSING, MIGHT_COMPRESS, COMMITTING, COMPRESSING, FINISHED};
-    private final AtomicReference<GZState> _state = new AtomicReference<>(GZState.NOT_COMPRESSING);
-    private final CRC32 _crc = new CRC32();
-    
-    private Deflater _deflater;
-    private GzipFactory _factory;
-    private ByteBuffer _buffer;
-    
-    public GzipHttpOutput(HttpChannel<?> channel)
-    {
-        super(channel);
-    }
-
-    @Override
-    public void reset()
-    {
-        _state.set(GZState.NOT_COMPRESSING);
-        super.reset();
-    }
-
-    @Override
-    protected void write(ByteBuffer content, boolean complete, Callback callback)
-    {
-        switch (_state.get())
-        {
-            case NOT_COMPRESSING:
-                super.write(content,complete,callback);
-                return;
-
-            case MIGHT_COMPRESS:
-                commit(content,complete,callback);
-                break;
-                
-            case COMMITTING:
-                callback.failed(new WritePendingException());
-                break;
-
-            case COMPRESSING:
-                gzip(content,complete,callback);
-                break;
-
-            default:
-                callback.failed(new IllegalStateException("state="+_state.get()));
-                break;
-        }
-    }
-
-    private void superWrite(ByteBuffer content, boolean complete, Callback callback)
-    {
-        super.write(content,complete,callback);
-    }
-    
-    private void addTrailer()
-    {
-        int i=_buffer.limit();
-        _buffer.limit(i+8);
-        
-        int v=(int)_crc.getValue();
-        _buffer.put(i++,(byte)(v & 0xFF));
-        _buffer.put(i++,(byte)((v>>>8) & 0xFF));
-        _buffer.put(i++,(byte)((v>>>16) & 0xFF));
-        _buffer.put(i++,(byte)((v>>>24) & 0xFF));
-        
-        v=_deflater.getTotalIn();
-        _buffer.put(i++,(byte)(v & 0xFF));
-        _buffer.put(i++,(byte)((v>>>8) & 0xFF));
-        _buffer.put(i++,(byte)((v>>>16) & 0xFF));
-        _buffer.put(i++,(byte)((v>>>24) & 0xFF));
-    }
-    
-    
-    private void gzip(ByteBuffer content, boolean complete, final Callback callback)
-    {
-        if (content.hasRemaining() || complete)
-        {
-            if (content.hasArray())
-                new GzipArrayCB(content,complete,callback).iterate();
-            else
-                new GzipBufferCB(content,complete,callback).iterate();
-        }
-        else
-            callback.succeeded();
-    }
-
-    protected void commit(ByteBuffer content, boolean complete, Callback callback)
-    {
-        // Are we excluding because of status?
-        Response response=getHttpChannel().getResponse();
-        int sc = response.getStatus();
-        if (sc>0 && (sc<200 || sc==204 || sc==205 || sc>=300))
-        {
-            LOG.debug("{} exclude by status {}",this,sc);
-            noCompression();
-            super.write(content,complete,callback);
-            return;
-        }
-        
-        // Are we excluding because of mime-type?
-        String ct = getHttpChannel().getResponse().getContentType();
-        if (ct!=null)
-        {
-            ct=MimeTypes.getContentTypeWithoutCharset(ct);
-            if (_factory.isExcludedMimeType(StringUtil.asciiToLowerCase(ct)))
-            {
-                LOG.debug("{} exclude by mimeType {}",this,ct);
-                noCompression();
-                super.write(content,complete,callback);
-                return;
-            }
-        }
-        
-        // Has the Content-Encoding header already been set?
-        String ce=getHttpChannel().getResponse().getHeader("Content-Encoding");
-        if (ce != null)
-        {
-            LOG.debug("{} exclude by content-encoding {}",this,ce);
-            noCompression();
-            super.write(content,complete,callback);
-            return;
-        }
-        
-        // Are we the thread that commits?
-        if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING))
-        {
-            // We are varying the response due to accept encoding header.
-            HttpFields fields = response.getHttpFields();
-            fields.add(_factory.getVaryField());
-
-            long content_length = response.getContentLength();
-            if (content_length<0 && complete)
-                content_length=content.remaining();
-            
-            _deflater = _factory.getDeflater(getHttpChannel().getRequest(),content_length);
-            
-            if (_deflater==null)
-            {
-                LOG.debug("{} exclude no deflater",this);
-                _state.set(GZState.NOT_COMPRESSING);
-                super.write(content,complete,callback);
-                return;
-            }
-
-            fields.put(CONTENT_ENCODING_GZIP);
-            _crc.reset();
-            _buffer=getHttpChannel().getByteBufferPool().acquire(_factory.getBufferSize(),false);
-            BufferUtil.fill(_buffer,GZIP_HEADER,0,GZIP_HEADER.length);
-
-            // Adjust headers
-            response.setContentLength(-1);
-            String etag=fields.get(HttpHeader.ETAG);
-            if (etag!=null)
-                fields.put(HttpHeader.ETAG,etag.substring(0,etag.length()-1)+AsyncGzipFilter.ETAG_GZIP+ '"');
-
-            LOG.debug("{} compressing {}",this,_deflater);
-            _state.set(GZState.COMPRESSING);
-            
-            gzip(content,complete,callback);
-        }
-        else
-            callback.failed(new WritePendingException());
-    }
-
-    public void noCompression()
-    {
-        while (true)
-        {
-            switch (_state.get())
-            {
-                case NOT_COMPRESSING:
-                    return;
-
-                case MIGHT_COMPRESS:
-                    if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.NOT_COMPRESSING))
-                        return;
-                    break;
-
-                default:
-                    throw new IllegalStateException(_state.get().toString());
-            }
-        }
-    }
-
-    public void noCompressionIfPossible()
-    {
-        while (true)
-        {
-            switch (_state.get())
-            {
-                case COMPRESSING:
-                case NOT_COMPRESSING:
-                    return;
-
-                case MIGHT_COMPRESS:
-                    if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.NOT_COMPRESSING))
-                        return;
-                    break;
-
-                default:
-                    throw new IllegalStateException(_state.get().toString());
-            }
-        }
-    }
-
-    public boolean mightCompress()
-    {
-        return _state.get()==GZState.MIGHT_COMPRESS;
-    }
-
-    public void mightCompress(GzipFactory factory)
-    {
-        while (true)
-        {
-            switch (_state.get())
-            {
-                case NOT_COMPRESSING:
-                    _factory=factory;
-                    if (_state.compareAndSet(GZState.NOT_COMPRESSING,GZState.MIGHT_COMPRESS))
-                    {
-                        LOG.debug("{} might compress",this);
-                        return;
-                    }
-                    _factory=null;
-                    break;
-                    
-                default:
-                    throw new IllegalStateException(_state.get().toString());
-            }
-        }
-    }
-    
-    private class GzipArrayCB extends IteratingNestedCallback
-    {        
-        private final boolean _complete;
-        public GzipArrayCB(ByteBuffer content, boolean complete, Callback callback)
-        {
-            super(callback);
-            _complete=complete;
-
-             byte[] array=content.array();
-             int off=content.arrayOffset()+content.position();
-             int len=content.remaining();
-             _crc.update(array,off,len);
-             _deflater.setInput(array,off,len);
-             if (complete)
-                 _deflater.finish();
-             content.position(content.limit());
-        }
-
-        @Override
-        protected Action process() throws Exception
-        {
-            if (_deflater.needsInput())
-            {
-                if (_deflater.finished())
-                {
-                    _factory.recycle(_deflater);
-                    _deflater=null;
-                    getHttpChannel().getByteBufferPool().release(_buffer);
-                    _buffer=null;
-                    return Action.SUCCEEDED;
-                }
-
-                if (!_complete)
-                    return Action.SUCCEEDED;
-                
-                _deflater.finish();
-            }
-
-            BufferUtil.compact(_buffer);
-            int off=_buffer.arrayOffset()+_buffer.limit();
-            int len=_buffer.capacity()-_buffer.limit()- (_complete?8:0);
-            if (len>0)
-            {
-                int produced=_deflater.deflate(_buffer.array(),off,len,Deflater.NO_FLUSH);
-                _buffer.limit(_buffer.limit()+produced);
-            }
-            boolean complete=_deflater.finished();
-            if (complete)
-                addTrailer();
-            
-            superWrite(_buffer,complete,this);
-            return Action.SCHEDULED;
-        }
-    }
-    
-    private class GzipBufferCB extends IteratingNestedCallback
-    {        
-        private final ByteBuffer _input;
-        private final ByteBuffer _content;
-        private final boolean _last;
-        public GzipBufferCB(ByteBuffer content, boolean complete, Callback callback)
-        {
-            super(callback);
-            _input=getHttpChannel().getByteBufferPool().acquire(Math.min(_factory.getBufferSize(),content.remaining()),false);
-            _content=content;
-            _last=complete;
-        }
-
-        @Override
-        protected Action process() throws Exception
-        {
-            if (_deflater.needsInput())
-            {                
-                if (BufferUtil.isEmpty(_content))
-                {                    
-                    if (_deflater.finished())
-                    {
-                        _factory.recycle(_deflater);
-                        _deflater=null;
-                        getHttpChannel().getByteBufferPool().release(_buffer);
-                        _buffer=null;
-                        return Action.SUCCEEDED;
-                    }
-                    
-                    if (!_last)
-                    {
-                        return Action.SUCCEEDED;
-                    }
-                    
-                    _deflater.finish();
-                }
-                else
-                {
-                    BufferUtil.clearToFill(_input);
-                    int took=BufferUtil.put(_content,_input);
-                    BufferUtil.flipToFlush(_input,0);
-                    if (took==0)
-                        throw new IllegalStateException();
-                   
-                    byte[] array=_input.array();
-                    int off=_input.arrayOffset()+_input.position();
-                    int len=_input.remaining();
-
-                    _crc.update(array,off,len);
-                    _deflater.setInput(array,off,len);                
-                    if (_last && BufferUtil.isEmpty(_content))
-                        _deflater.finish();
-                }
-            }
-
-            BufferUtil.compact(_buffer);
-            int off=_buffer.arrayOffset()+_buffer.limit();
-            int len=_buffer.capacity()-_buffer.limit() - (_last?8:0);
-            if (len>0)
-            {
-                int produced=_deflater.deflate(_buffer.array(),off,len,Deflater.NO_FLUSH);
-                _buffer.limit(_buffer.limit()+produced);
-            }
-            boolean finished=_deflater.finished();
-            
-            if (finished)
-                addTrailer();
-                
-            superWrite(_buffer,finished,this);
-            return Action.SCHEDULED;
-        }
-    }
-}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipOutputStream.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipOutputStream.java
deleted file mode 100644
index c2d0409..0000000
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipOutputStream.java
+++ /dev/null
@@ -1,72 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.zip.CRC32;
-import java.util.zip.Deflater;
-
-/**
- * Reimplementation of {@link java.util.zip.GZIPOutputStream} that supports reusing a {@link Deflater} instance.
- */
-public class GzipOutputStream extends DeflatedOutputStream
-{
-
-    private final static byte[] GZIP_HEADER = new byte[]
-    { (byte)0x1f, (byte)0x8b, Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0 };
-
-    private final CRC32 _crc = new CRC32();
-
-    public GzipOutputStream(OutputStream out, Deflater deflater, byte[] buffer) throws IOException
-    {
-        super(out,deflater,buffer);
-        out.write(GZIP_HEADER);
-    }
-
-    @Override
-    public synchronized void write(byte[] buf, int off, int len) throws IOException
-    {
-        super.write(buf,off,len);
-        _crc.update(buf,off,len);
-    }
-
-    @Override
-    public synchronized void finish() throws IOException
-    {
-        if (!_def.finished())
-        {
-            super.finish();
-            byte[] trailer = new byte[8];
-            writeInt((int)_crc.getValue(),trailer,0);
-            writeInt(_def.getTotalIn(),trailer,4);
-            out.write(trailer);
-        }
-    }
-
-    private void writeInt(int i, byte[] buf, int offset)
-    {
-        int o = offset;
-        buf[o++] = (byte)(i & 0xFF);
-        buf[o++] = (byte)((i >>> 8) & 0xFF);
-        buf[o++] = (byte)((i >>> 16) & 0xFF);
-        buf[o++] = (byte)((i >>> 24) & 0xFF);
-    }
-
-}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/package-info.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/package-info.java
deleted file mode 100644
index 4618532..0000000
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/package-info.java
+++ /dev/null
@@ -1,23 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-/**
- * Jetty Servlets : GZIP Filter Classes
- */
-package org.eclipse.jetty.servlets.gzip;
-
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/AsyncManipFilter.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/AsyncManipFilter.java
new file mode 100644
index 0000000..5a41e39
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/AsyncManipFilter.java
@@ -0,0 +1,103 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Filter that merely manipulates the AsyncContext.
+ * <p>
+ * The pattern of manipulation is modeled after how DOSFilter behaves. The purpose of this filter is to test arbitrary filter chains that could see unintended
+ * side-effects of async context manipulation.
+ */
+public class AsyncManipFilter implements Filter, AsyncListener
+{
+    private static final Logger LOG = Log.getLogger(AsyncManipFilter.class);
+    private static final String MANIP_KEY = AsyncManipFilter.class.getName();
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException
+    {
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+    {
+        LOG.debug("doFilter() - {}", chain);
+        AsyncContext ctx = (AsyncContext)request.getAttribute(MANIP_KEY);
+        if (ctx == null)
+        {
+            LOG.debug("Initial pass through: {}", chain);
+            ctx = request.startAsync();
+            ctx.addListener(this);
+            ctx.setTimeout(1000);
+            LOG.debug("AsyncContext: {}", ctx);
+            request.setAttribute(MANIP_KEY,ctx);
+            return;
+        }
+        else
+        {
+            LOG.debug("Second pass through: {}", chain);
+            chain.doFilter(request,response);
+        }
+    }
+
+    @Override
+    public void destroy()
+    {
+    }
+
+    @Override
+    public void onComplete(AsyncEvent event) throws IOException
+    {
+        LOG.debug("onComplete() {}",event);
+    }
+
+    @Override
+    public void onTimeout(AsyncEvent event) throws IOException
+    {
+        LOG.debug("onTimeout() {}",event.getAsyncContext());
+        event.getAsyncContext().dispatch();
+    }
+
+    @Override
+    public void onError(AsyncEvent event) throws IOException
+    {
+        LOG.debug("onError()",event.getThrowable());
+    }
+
+    @Override
+    public void onStartAsync(AsyncEvent event) throws IOException
+    {
+        LOG.debug("onTimeout() {}",event);
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/AsyncScheduledDispatchWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/AsyncScheduledDispatchWrite.java
new file mode 100644
index 0000000..54e311e
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/AsyncScheduledDispatchWrite.java
@@ -0,0 +1,121 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@SuppressWarnings("serial")
+public abstract class AsyncScheduledDispatchWrite extends TestDirContentServlet
+{
+    public static class Default extends AsyncScheduledDispatchWrite
+    {
+        public Default()
+        {
+            super(true);
+        }
+    }
+    
+    public static class Passed extends AsyncScheduledDispatchWrite
+    {
+        public Passed()
+        {
+            super(false);
+        }
+    }
+
+    private static class DispatchBack implements Runnable
+    {
+        private final AsyncContext ctx;
+
+        public DispatchBack(AsyncContext ctx)
+        {
+            this.ctx = ctx;
+        }
+
+        @Override
+        public void run()
+        {
+            ctx.dispatch();
+        }
+    }
+
+    private final boolean originalReqResp;
+    private ScheduledExecutorService scheduler;
+
+    public AsyncScheduledDispatchWrite(boolean originalReqResp)
+    {
+        this.originalReqResp = originalReqResp;
+    }
+
+    public void init(ServletConfig config) throws ServletException
+    {
+        super.init(config);
+        scheduler = Executors.newScheduledThreadPool(3);
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        Boolean suspended = (Boolean)request.getAttribute("SUSPENDED");
+        if (suspended == null || !suspended)
+        {
+            request.setAttribute("SUSPENDED",Boolean.TRUE);
+            AsyncContext ctx;
+            if (originalReqResp)
+            {
+                // Use Original Request & Response
+                ctx = request.startAsync();
+            }
+            else
+            {
+                // Pass Request & Response
+                ctx = request.startAsync(request,response);
+            }
+            ctx.setTimeout(0);
+            scheduler.schedule(new DispatchBack(ctx),500,TimeUnit.MILLISECONDS);
+        }
+        else
+        {
+            String fileName = request.getServletPath();
+            byte[] dataBytes = loadContentFileBytes(fileName);
+
+            response.setContentLength(dataBytes.length);
+
+            ServletOutputStream out = response.getOutputStream();
+
+            if (fileName.endsWith("txt"))
+                response.setContentType("text/plain");
+            else if (fileName.endsWith("mp3"))
+                response.setContentType("audio/mpeg");
+            response.setHeader("ETag","W/etag-" + fileName);
+
+            out.write(dataBytes);
+        }
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/AsyncTimeoutCompleteWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/AsyncTimeoutCompleteWrite.java
new file mode 100644
index 0000000..26bc34e
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/AsyncTimeoutCompleteWrite.java
@@ -0,0 +1,137 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Respond with requested content, but via AsyncContext manipulation.
+ * <p>
+ * 
+ * <pre>
+ *   1) startAsync
+ *   2) AsyncContext.setTimeout()
+ *   3) onTimeout()
+ *   4) send-response
+ *   5) AsyncContext.complete()
+ * </pre>
+ */
+@SuppressWarnings("serial")
+public abstract class AsyncTimeoutCompleteWrite extends TestDirContentServlet implements AsyncListener
+{
+    public static class Default extends AsyncTimeoutCompleteWrite
+    {
+        public Default()
+        {
+            super(true);
+        }
+    }
+    
+    public static class Passed extends AsyncTimeoutCompleteWrite
+    {
+        public Passed()
+        {
+            super(false);
+        }
+    }
+
+    private final boolean originalReqResp;
+
+    public AsyncTimeoutCompleteWrite(boolean originalReqResp)
+    {
+        this.originalReqResp = originalReqResp;
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        assertThat("'filename' request attribute shouldn't be declared",request.getAttribute("filename"),nullValue());
+
+        AsyncContext ctx = (AsyncContext)request.getAttribute(this.getClass().getName());
+        assertThat("AsyncContext (shouldn't be in request attribute)", ctx, nullValue());
+        
+        if (originalReqResp)
+        {
+            // Use Original Request & Response
+            ctx = request.startAsync();
+        }
+        else
+        {
+            // Pass Request & Response
+            ctx = request.startAsync(request,response);
+        }
+        String fileName = request.getServletPath();
+        request.setAttribute("filename",fileName);
+        ctx.addListener(this);
+        ctx.setTimeout(20);
+        
+        // Setup indication of a redispatch (which this scenario shouldn't do)
+        request.setAttribute(this.getClass().getName(),ctx);
+    }
+
+    @Override
+    public void onComplete(AsyncEvent event) throws IOException
+    {
+    }
+
+    @Override
+    public void onTimeout(AsyncEvent event) throws IOException
+    {
+        HttpServletRequest request = (HttpServletRequest)event.getSuppliedRequest();
+        HttpServletResponse response = (HttpServletResponse)event.getSuppliedResponse();
+
+        String fileName = (String)request.getAttribute("filename");
+        byte[] dataBytes = loadContentFileBytes(fileName);
+
+        response.setContentLength(dataBytes.length);
+
+        ServletOutputStream out = response.getOutputStream();
+
+        if (fileName.endsWith("txt"))
+            response.setContentType("text/plain");
+        else if (fileName.endsWith("mp3"))
+            response.setContentType("audio/mpeg");
+        response.setHeader("ETag","W/etag-" + fileName);
+
+        out.write(dataBytes);
+
+        event.getAsyncContext().complete();
+    }
+
+    @Override
+    public void onError(AsyncEvent event) throws IOException
+    {
+    }
+
+    @Override
+    public void onStartAsync(AsyncEvent event) throws IOException
+    {
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/AsyncTimeoutDispatchWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/AsyncTimeoutDispatchWrite.java
new file mode 100644
index 0000000..5d928ef
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/AsyncTimeoutDispatchWrite.java
@@ -0,0 +1,121 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@SuppressWarnings("serial")
+public class AsyncTimeoutDispatchWrite extends TestDirContentServlet implements AsyncListener
+{
+    public static class Default extends AsyncTimeoutDispatchWrite
+    {
+        public Default()
+        {
+            super(true);
+        }
+    }
+    
+    public static class Passed extends AsyncTimeoutDispatchWrite
+    {
+        public Passed()
+        {
+            super(false);
+        }
+    }
+
+    private final boolean originalReqResp;
+
+    public AsyncTimeoutDispatchWrite(boolean originalReqResp)
+    {
+        this.originalReqResp = originalReqResp;
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        AsyncContext ctx = (AsyncContext)request.getAttribute(AsyncContext.class.getName());
+        if (ctx == null)
+        {
+            // First pass through
+            if (originalReqResp)
+            {
+                // Use Original Request & Response
+                ctx = request.startAsync();
+            }
+            else
+            {
+                // Pass Request & Response
+                ctx = request.startAsync(request,response);
+            }
+            ctx.addListener(this);
+            ctx.setTimeout(200);
+            request.setAttribute(AsyncContext.class.getName(),ctx);
+        }
+        else
+        {
+            // second pass through, as result of timeout -> dispatch
+            String fileName = request.getServletPath();
+            byte[] dataBytes = loadContentFileBytes(fileName);
+
+            response.setContentLength(dataBytes.length);
+
+            ServletOutputStream out = response.getOutputStream();
+
+            if (fileName.endsWith("txt"))
+                response.setContentType("text/plain");
+            else if (fileName.endsWith("mp3"))
+                response.setContentType("audio/mpeg");
+            response.setHeader("ETag","W/etag-" + fileName);
+
+            out.write(dataBytes);
+            // no need to call AsyncContext.complete() from here
+            // in fact, it will cause an IllegalStateException if we do
+            // ctx.complete();
+        }
+    }
+
+    @Override
+    public void onComplete(AsyncEvent event) throws IOException
+    {
+    }
+
+    @Override
+    public void onTimeout(AsyncEvent event) throws IOException
+    {
+        event.getAsyncContext().dispatch();
+    }
+
+    @Override
+    public void onError(AsyncEvent event) throws IOException
+    {
+    }
+
+    @Override
+    public void onStartAsync(AsyncEvent event) throws IOException
+    {
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipContentLengthTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipContentLengthTest.java
new file mode 100644
index 0000000..b62a98e
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipContentLengthTest.java
@@ -0,0 +1,329 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.handler.gzip.GzipTester.ContentMetadata;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Test the GzipHandler support for Content-Length setting variations.
+ *
+ * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
+ */
+@RunWith(Parameterized.class)
+public class GzipContentLengthTest
+{
+    @Rule
+    public final TestTracker tracker = new TestTracker();
+
+    @Rule
+    public TestingDir testingdir = new TestingDir();
+    
+    private static final HttpConfiguration defaultHttp = new HttpConfiguration();
+    private static final int LARGE = defaultHttp.getOutputBufferSize() * 8;
+    private static final int MEDIUM = defaultHttp.getOutputBufferSize();
+    private static final int SMALL = defaultHttp.getOutputBufferSize() / 4;
+    private static final int TINY = GzipHandler.DEFAULT_MIN_GZIP_SIZE / 2;
+    private static final boolean EXPECT_COMPRESSED = true;
+
+    @Parameters(name = "{0} bytes - {1} - compressed({2})")
+    public static List<Object[]> data()
+    {
+        List<Object[]> ret = new ArrayList<Object[]>();
+
+        ret.add(new Object[] { 0, "empty.txt", !EXPECT_COMPRESSED});
+        ret.add(new Object[] { TINY, "file-tiny.txt", !EXPECT_COMPRESSED});
+        ret.add(new Object[] { SMALL, "file-small.txt", EXPECT_COMPRESSED});
+        ret.add(new Object[] { SMALL, "file-small.mp3", !EXPECT_COMPRESSED});
+        ret.add(new Object[] { MEDIUM, "file-med.txt", EXPECT_COMPRESSED});
+        ret.add(new Object[] { MEDIUM, "file-medium.mp3", !EXPECT_COMPRESSED});
+        ret.add(new Object[] { LARGE, "file-large.txt", EXPECT_COMPRESSED});
+        ret.add(new Object[] { LARGE, "file-large.mp3", !EXPECT_COMPRESSED});
+
+        return ret;
+    }
+
+    @Parameter(0)
+    public int fileSize;
+    @Parameter(1)
+    public String fileName;
+    @Parameter(2)
+    public boolean expectCompressed;
+    
+    private void testWithGzip(Class<? extends TestDirContentServlet> contentServlet) throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir, GzipHandler.GZIP);
+        
+        // Add AsyncGzip Configuration
+        tester.getGzipHandler().setIncludedMimeTypes("text/plain");
+        tester.getGzipHandler().setIncludedPaths("*.txt","*.mp3");
+
+        // Add content servlet
+        tester.setContentServlet(contentServlet);
+        
+        try
+        {
+            String testFilename = String.format("%s-%s", contentServlet.getSimpleName(), fileName);
+            File testFile = tester.prepareServerFile(testFilename,fileSize);
+            
+            tester.start();
+            
+            HttpTester.Response response = tester.executeRequest("GET","/context/" + testFile.getName(),5,TimeUnit.SECONDS);
+            
+            if (response.getStatus()!=200)
+                System.err.println("DANG!!!! "+response);
+            
+            assertThat("Response status", response.getStatus(), is(HttpStatus.OK_200));
+            
+            if (expectCompressed)
+            {
+                // Must be gzip compressed
+                assertThat("Content-Encoding",response.get("Content-Encoding"),containsString(GzipHandler.GZIP));
+            } else
+            {
+                assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(GzipHandler.GZIP)));
+            }
+            
+            // Uncompressed content Size
+            ContentMetadata content = tester.getResponseMetadata(response);
+            assertThat("(Uncompressed) Content Length", content.size, is((long)fileSize));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    /**
+     * Test with content servlet that does:  
+     * AsyncContext create -> timeout -> onTimeout -> write-response -> complete
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testAsyncTimeoutCompleteWrite_Default() throws Exception
+    {
+        testWithGzip(AsyncTimeoutCompleteWrite.Default.class);
+    }
+    
+    /**
+     * Test with content servlet that does:  
+     * AsyncContext create -> timeout -> onTimeout -> write-response -> complete
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testAsyncTimeoutCompleteWrite_Passed() throws Exception
+    {
+        testWithGzip(AsyncTimeoutCompleteWrite.Passed.class);
+    }
+    
+    /**
+     * Test with content servlet that does:  
+     * AsyncContext create -> timeout -> onTimeout -> dispatch -> write-response
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testAsyncTimeoutDispatchWrite_Default() throws Exception
+    {
+        testWithGzip(AsyncTimeoutDispatchWrite.Default.class);
+    }
+    
+    /**
+     * Test with content servlet that does:  
+     * AsyncContext create -> timeout -> onTimeout -> dispatch -> write-response
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testAsyncTimeoutDispatchWrite_Passed() throws Exception
+    {
+        testWithGzip(AsyncTimeoutDispatchWrite.Passed.class);
+    }
+
+    /**
+     * Test with content servlet that does:  
+     * AsyncContext create -> no-timeout -> scheduler.schedule -> dispatch -> write-response
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testAsyncScheduledDispatchWrite_Default() throws Exception
+    {
+        testWithGzip(AsyncScheduledDispatchWrite.Default.class);
+    }
+    
+    /**
+     * Test with content servlet that does:  
+     * AsyncContext create -> no-timeout -> scheduler.schedule -> dispatch -> write-response
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testAsyncScheduledDispatchWrite_Passed() throws Exception
+    {
+        testWithGzip(AsyncScheduledDispatchWrite.Passed.class);
+    }
+
+    /**
+     * Test with content servlet that does:  
+     * 1) setHeader(content-length)
+     * 2) getOutputStream()
+     * 3) setHeader(content-type)
+     * 4) outputStream.write()
+     * 
+     * @throws Exception on test failure
+     * @see <a href="http://bugs.eclipse.org/354014">Eclipse Bug 354014</a>
+     */
+    @Test
+    public void testServletLengthStreamTypeWrite() throws Exception
+    {
+        testWithGzip(TestServletLengthStreamTypeWrite.class);
+    }
+
+    /**
+     * Test with content servlet that does:  
+     * 1) setHeader(content-length)
+     * 2) setHeader(content-type)
+     * 3) getOutputStream()
+     * 4) outputStream.write()
+     * 
+     * @throws Exception on test failure
+     * @see <a href="http://bugs.eclipse.org/354014">Eclipse Bug 354014</a>
+     */
+    @Test
+    public void testServletLengthTypeStreamWrite() throws Exception
+    {
+        testWithGzip(TestServletLengthTypeStreamWrite.class);
+    }
+
+    /**
+     * Test with content servlet that does:  
+     * 1) getOutputStream()
+     * 2) setHeader(content-length)
+     * 3) setHeader(content-type)
+     * 4) outputStream.write()
+     * 
+     * @throws Exception on test failure
+     * @see <a href="http://bugs.eclipse.org/354014">Eclipse Bug 354014</a>
+     */
+    @Test
+    public void testServletStreamLengthTypeWrite() throws Exception
+    {
+        testWithGzip(TestServletStreamLengthTypeWrite.class);
+    }
+
+    /**
+     * Test with content servlet that does:  
+     * 1) getOutputStream()
+     * 2) setHeader(content-length)
+     * 3) setHeader(content-type)
+     * 4) outputStream.write() (with frequent response flush)
+     * 
+     * @throws Exception on test failure
+     * @see <a href="http://bugs.eclipse.org/354014">Eclipse Bug 354014</a>
+     */
+    @Test
+    public void testServletStreamLengthTypeWriteWithFlush() throws Exception
+    {
+        testWithGzip(TestServletStreamLengthTypeWriteWithFlush.class);
+    }
+
+    /**
+     * Test with content servlet that does:  
+     * 1) getOutputStream()
+     * 2) setHeader(content-type)
+     * 3) setHeader(content-length)
+     * 4) outputStream.write()
+     * 
+     * @throws Exception on test failure
+     * @see <a href="http://bugs.eclipse.org/354014">Eclipse Bug 354014</a>
+     */
+    @Test
+    public void testServletStreamTypeLengthWrite() throws Exception
+    {
+        testWithGzip(TestServletStreamTypeLengthWrite.class);
+    }
+
+    /**
+     * Test with content servlet that does:  
+     * 1) setHeader(content-type)
+     * 2) setHeader(content-length)
+     * 3) getOutputStream()
+     * 4) outputStream.write()
+     * 
+     * @throws Exception on test failure
+     * @see <a href="http://bugs.eclipse.org/354014">Eclipse Bug 354014</a>
+     */
+    @Test
+    public void testServletTypeLengthStreamWrite() throws Exception
+    {
+        testWithGzip(TestServletTypeLengthStreamWrite.class);
+    }
+
+    /**
+     * Test with content servlet that does:  
+     * 1) setHeader(content-type)
+     * 2) getOutputStream()
+     * 3) setHeader(content-length)
+     * 4) outputStream.write()
+     * 
+     * @throws Exception on test failure
+     * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
+     */
+    @Test
+    public void testServletTypeStreamLengthWrite() throws Exception
+    {
+        testWithGzip(TestServletTypeStreamLengthWrite.class);
+    }
+
+    /**
+     * Test with content servlet that does:  
+     * 2) getOutputStream()
+     * 1) setHeader(content-type)
+     * 3) setHeader(content-length)
+     * 4) (unwrapped) HttpOutput.write(ByteBuffer)
+     * 
+     * This is done to demonstrate a bug with using HttpOutput.write()
+     * while also using GzipFilter
+     * 
+     * @throws Exception on test failure
+     * @see <a href="http://bugs.eclipse.org/450873">Eclipse Bug 450873</a>
+     */
+    @Test
+    public void testHttpOutputWrite() throws Exception
+    {
+        testWithGzip(TestServletBufferTypeLengthWrite.class);
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipDefaultNoRecompressTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipDefaultNoRecompressTest.java
new file mode 100644
index 0000000..5e93791
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipDefaultNoRecompressTest.java
@@ -0,0 +1,111 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.toolchain.test.IO;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Tests {@link GzipHandler} in combination with {@link DefaultServlet} for ability to configure {@link GzipHandler} to
+ * ignore recompress situations from upstream.
+ */
+@RunWith(Parameterized.class)
+public class GzipDefaultNoRecompressTest
+{
+    @Parameters
+    public static List<Object[]> data()
+    {
+        return Arrays.asList(new Object[][]
+        {
+            // Some already compressed files
+                { "test_quotes.gz", "application/gzip"  , GzipHandler.GZIP },
+                { "test_quotes.bz2", "application/bzip2", GzipHandler.GZIP },
+                { "test_quotes.zip", "application/zip"  , GzipHandler.GZIP },
+                { "test_quotes.rar", "application/x-rar-compressed", GzipHandler.GZIP },
+            // Some images (common first)
+                { "jetty_logo.png", "image/png", GzipHandler.GZIP},
+                { "jetty_logo.gif", "image/gif", GzipHandler.GZIP},
+                { "jetty_logo.jpeg", "image/jpeg", GzipHandler.GZIP},
+                { "jetty_logo.jpg", "image/jpeg", GzipHandler.GZIP},
+            // Lesser encountered images (usually found being requested from non-browser clients)
+                { "jetty_logo.bmp", "image/bmp", GzipHandler.GZIP },
+                { "jetty_logo.tif", "image/tiff", GzipHandler.GZIP },
+                { "jetty_logo.tiff", "image/tiff", GzipHandler.GZIP },
+                { "jetty_logo.xcf", "image/xcf", GzipHandler.GZIP },
+                { "jetty_logo.jp2", "image/jpeg2000", GzipHandler.GZIP },
+            //qvalue disables compression
+                { "test_quotes.txt", "text/plain", GzipHandler.GZIP+";q=0"},
+                { "test_quotes.txt", "text/plain", GzipHandler.GZIP+"; q =    0 "},
+                
+        });
+    }
+
+    @Rule
+    public TestingDir testingdir = new TestingDir();
+
+    private String alreadyCompressedFilename;
+    private String expectedContentType;
+    private String compressionType;
+
+    public GzipDefaultNoRecompressTest(String testFilename, String expectedContentType, String compressionType)
+    {
+        this.alreadyCompressedFilename = testFilename;
+        this.expectedContentType = expectedContentType;
+        this.compressionType = compressionType;
+    }
+
+    @Test
+    public void testNotGzipHandlered_Default_AlreadyCompressed() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir, compressionType);
+
+        copyTestFileToServer(alreadyCompressedFilename);
+
+        tester.setContentServlet(TestStaticMimeTypeServlet.class);
+
+        try
+        {
+            tester.start();
+            tester.assertIsResponseNotGziped(alreadyCompressedFilename,alreadyCompressedFilename + ".sha1",expectedContentType);
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    private void copyTestFileToServer(String testFilename) throws IOException
+    {
+        File testFile = MavenTestingUtils.getTestResourceFile(testFilename);
+        File outFile = testingdir.getPathFile(testFilename).toFile();
+        IO.copy(testFile,outFile);
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipDefaultTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipDefaultTest.java
new file mode 100644
index 0000000..86cd473
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipDefaultTest.java
@@ -0,0 +1,767 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isEmptyOrNullString;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.GzipHttpContent;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.toolchain.test.IO;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.eclipse.jetty.util.StringUtil;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Test the GzipHandler support built into the {@link DefaultServlet}
+ */
+public class GzipDefaultTest
+{
+    private String compressionType;
+
+    public GzipDefaultTest()
+    {
+        this.compressionType = GzipHandler.GZIP;
+    }
+
+    @SuppressWarnings("serial")
+    public static class HttpStatusServlet extends HttpServlet
+    {
+        private int _status = 204;
+
+        public HttpStatusServlet()
+        {
+            super();
+        }
+
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            resp.setStatus(_status);
+            resp.setHeader("ETag","W/\"204\"");
+        }
+
+    }
+
+    @SuppressWarnings("serial")
+    public static class HttpErrorServlet extends HttpServlet
+    {
+        private int _status = 400;
+
+        public HttpErrorServlet()
+        {
+            super();
+        }
+
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            resp.getOutputStream().write("error message".getBytes());
+            resp.setStatus(_status);
+        }
+    }
+
+    @SuppressWarnings("serial")
+    public static class HttpContentTypeWithEncoding extends HttpServlet
+    {
+        public static final String COMPRESSED_CONTENT = "<html><head></head><body><h1>COMPRESSABLE CONTENT</h1>"
+                + "This content must be longer than the default min gzip length, which is 256 bytes. "
+                + "The moon is blue to a fish in love. How now brown cow. The quick brown fox jumped over the lazy dog. A woman needs a man like a fish needs a bicycle!"
+                + "</body></html>";
+
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            resp.setContentType("text/plain;charset=UTF8");
+            resp.setStatus(200);
+            ServletOutputStream out = resp.getOutputStream();
+            out.print(COMPRESSED_CONTENT);
+        }
+
+    }
+
+    @Rule
+    public TestingDir testingdir = new TestingDir();
+
+    @Test
+    public void testIsGzipByMethod() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        // Configure Gzip Handler
+        tester.getGzipHandler().setIncludedMethods("POST","WIBBLE", "HEAD");
+
+        // Prepare Server File
+        int filesize = tester.getOutputBufferSize() * 2;
+        tester.prepareServerFile("file.txt",filesize);
+
+        // Content Servlet
+        tester.setContentServlet(GetServlet.class);
+
+        try
+        {
+            tester.start();
+            HttpTester.Response response;
+
+            //These methods have content bodies of the compressed response
+            tester.assertIsResponseGzipCompressed("POST","file.txt");
+            tester.assertIsResponseGzipCompressed("WIBBLE","file.txt");
+            
+            //A HEAD request should have similar headers, but no body
+            response = tester.executeRequest("HEAD","/context/file.txt",5,TimeUnit.SECONDS);
+            assertThat("Response status",response.getStatus(),is(HttpStatus.OK_200));
+            assertThat("ETag", response.get("ETag"), containsString(GzipHttpContent.ETAG_GZIP));
+            assertThat("Content encoding", response.get("Content-Encoding"), containsString("gzip"));
+            assertNull("Content length", response.get("Content-Length"));
+   
+            response = tester.executeRequest("GET","/context/file.txt",5,TimeUnit.SECONDS);
+
+            assertThat("Response status",response.getStatus(),is(HttpStatus.OK_200));
+            assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(compressionType)));
+
+            String content = tester.readResponse(response);
+            assertThat("Response content size",content.length(),is(filesize));
+            String expectedContent = IO.readToString(testingdir.getPathFile("file.txt").toFile());
+            assertThat("Response content",content,is(expectedContent));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @SuppressWarnings("serial")
+    public static class GetServlet extends DefaultServlet
+    {
+        public GetServlet()
+        {
+            super();
+        }
+
+        @Override
+        public void service(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException
+        {
+            String uri = req.getRequestURI();
+            if (uri.endsWith(".deferred"))
+            {
+                // System.err.println("type for "+uri.substring(0,uri.length()-9)+" is "+getServletContext().getMimeType(uri.substring(0,uri.length()-9)));
+                resp.setContentType(getServletContext().getMimeType(uri.substring(0,uri.length() - 9)));
+            }
+
+            doGet(req,resp);
+        }
+    }
+
+    @Test
+    public void testIsGzipCompressedEmpty() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        // Configure Gzip Handler
+        tester.getGzipHandler().addIncludedMimeTypes("text/plain");
+
+        // Prepare server file
+        tester.prepareServerFile("empty.txt",0);
+
+        // Set content servlet
+        tester.setContentServlet(DefaultServlet.class);
+
+        try
+        {
+            tester.start();
+
+            HttpTester.Response response;
+
+            response = tester.executeRequest("GET","/context/empty.txt",5,TimeUnit.SECONDS);
+
+            assertThat("Response status",response.getStatus(),is(HttpStatus.OK_200));
+            assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(compressionType)));
+
+            String content = tester.readResponse(response);
+            assertThat("Response content size",content.length(),is(0));
+            String expectedContent = IO.readToString(testingdir.getPathFile("empty.txt").toFile());
+            assertThat("Response content",content,is(expectedContent));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testIsGzipCompressedTiny() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        int filesize = tester.getOutputBufferSize() / 4;
+        tester.prepareServerFile("file.txt",filesize);
+
+        tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
+
+        try
+        {
+            tester.start();
+            HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt");
+            Assert.assertEquals("Accept-Encoding, User-Agent",http.get("Vary"));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testIsGzipCompressedLarge() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        int filesize = tester.getOutputBufferSize() * 4;
+        tester.prepareServerFile("file.txt",filesize);
+
+        tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
+        tester.getGzipHandler().setExcludedAgentPatterns();
+
+        try
+        {
+            tester.start();
+            HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt");
+            Assert.assertEquals("Accept-Encoding",http.get("Vary"));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testGzipedIfModified() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        int filesize = tester.getOutputBufferSize() * 4;
+        tester.prepareServerFile("file.txt",filesize);
+
+        tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
+
+        try
+        {
+            tester.start();
+            HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt",System.currentTimeMillis() - 4000);
+            Assert.assertEquals("Accept-Encoding, User-Agent",http.get("Vary"));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testGzippedIfSVG() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+        tester.copyTestServerFile("test.svg");
+        tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
+
+        tester.getGzipHandler().addIncludedMimeTypes("image/svg+xml");
+
+        try
+        {
+            tester.start();
+            HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","test.svg",System.currentTimeMillis() - 4000);
+            Assert.assertEquals("Accept-Encoding, User-Agent",http.get("Vary"));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testNotGzipedIfNotModified() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        int filesize = tester.getOutputBufferSize() * 4;
+        tester.prepareServerFile("file.txt",filesize);
+
+        tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
+
+        try
+        {
+            tester.start();
+            tester.assertIsResponseNotModified("GET","file.txt",System.currentTimeMillis() + 4000);
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testIsNotGzipCompressedWithZeroQ() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType + "; q=0");
+
+        // Configure Gzip Handler
+        tester.getGzipHandler().addIncludedMimeTypes("text/plain");
+
+        // Prepare server file
+        int filesize = tester.getOutputBufferSize() / 4;
+        tester.prepareServerFile("file.txt",filesize);
+
+        // Add content servlet
+        tester.setContentServlet(DefaultServlet.class);
+
+        try
+        {
+            tester.start();
+            HttpTester.Response http = assertIsResponseNotGzipCompressed(tester,"GET","file.txt",filesize,HttpStatus.OK_200);
+            assertThat("Response[Vary]",http.get("Vary"),containsString("Accept-Encoding"));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testIsGzipCompressedWithQ() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType,"something;q=0.1," + compressionType + ";q=0.5");
+
+        int filesize = tester.getOutputBufferSize() / 4;
+        tester.prepareServerFile("file.txt",filesize);
+
+        tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
+        tester.getGzipHandler().setExcludedAgentPatterns();
+
+        try
+        {
+            tester.start();
+            HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt");
+            Assert.assertEquals("Accept-Encoding",http.get("Vary"));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testIsNotGzipCompressedByContentType() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        // Prepare server file
+        int filesize = tester.getOutputBufferSize() * 4;
+        tester.prepareServerFile("file.mp3",filesize);
+
+        // Add content servlet
+        tester.setContentServlet(DefaultServlet.class);
+
+        try
+        {
+            tester.start();
+            HttpTester.Response http = assertIsResponseNotGzipCompressed(tester,"GET","file.mp3",filesize,HttpStatus.OK_200);
+            Assert.assertNull(http.get("Vary"));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testIsNotGzipCompressedByExcludedContentType() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        // Configure Gzip Handler
+        tester.getGzipHandler().addExcludedMimeTypes("text/plain");
+
+        // Prepare server file
+        int filesize = tester.getOutputBufferSize() * 4;
+        tester.prepareServerFile("test_quotes.txt",filesize);
+
+        // Add content servlet
+        tester.setContentServlet(DefaultServlet.class);
+
+        try
+        {
+            tester.start();
+            HttpTester.Response http = assertIsResponseNotGzipCompressed(tester,"GET","test_quotes.txt",filesize,HttpStatus.OK_200);
+            Assert.assertNull(http.get("Vary"));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testIsNotGzipCompressedByExcludedContentTypeWithCharset() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        // Configure Gzip Handler
+        tester.getGzipHandler().addExcludedMimeTypes("text/plain");
+
+        // Prepare server file
+        int filesize = tester.getOutputBufferSize() * 4;
+        tester.prepareServerFile("test_quotes.txt",filesize);
+        tester.addMimeType("txt","text/plain;charset=UTF-8");
+
+        // Add content servlet
+        tester.setContentServlet(DefaultServlet.class);
+
+        try
+        {
+            tester.start();
+            HttpTester.Response http = assertIsResponseNotGzipCompressed(tester,"GET","test_quotes.txt",filesize,HttpStatus.OK_200);
+            Assert.assertNull(http.get("Vary"));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testGzipCompressedByContentTypeWithEncoding() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+        tester.setContentServlet(HttpContentTypeWithEncoding.class);
+        tester.getGzipHandler().addIncludedMimeTypes("text/plain");
+        tester.getGzipHandler().setExcludedAgentPatterns();
+        try
+        {
+            tester.start();
+            HttpTester.Response http = tester.assertNonStaticContentIsResponseGzipCompressed("GET","xxx",HttpContentTypeWithEncoding.COMPRESSED_CONTENT);
+            Assert.assertEquals("Accept-Encoding",http.get("Vary"));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testIsNotGzipCompressedByDeferredContentType() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        // Configure Gzip Handler
+        tester.getGzipHandler().addIncludedMimeTypes("text/plain");
+
+        // Prepare server file
+        int filesize = tester.getOutputBufferSize() * 4;
+        tester.prepareServerFile("file.mp3.deferred",filesize);
+
+        // Add content servlet
+        tester.setContentServlet(GetServlet.class);
+
+        try
+        {
+            tester.start();
+            HttpTester.Response response = assertIsResponseNotGzipCompressed(tester,"GET","file.mp3.deferred",filesize,HttpStatus.OK_200);
+            assertThat("Response[Vary]", response.get("Vary"), isEmptyOrNullString());
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testIsNotGzipCompressedHttpStatus() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        // Configure Gzip Handler
+        tester.getGzipHandler().addIncludedMimeTypes("text/plain");
+
+        // Test error code 204
+        tester.setContentServlet(HttpStatusServlet.class);
+
+        try
+        {
+            tester.start();
+
+            HttpTester.Response response = tester.executeRequest("GET","/context/",5,TimeUnit.SECONDS);
+
+            assertThat("Response status",response.getStatus(),is(HttpStatus.NO_CONTENT_204));
+            assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(compressionType)));
+        }
+        finally
+        {
+            tester.stop();
+        }
+
+    }
+
+    @Test
+    public void testIsNotGzipCompressedHttpBadRequestStatus() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        // Configure Gzip Handler
+        tester.getGzipHandler().addIncludedMimeTypes("text/plain");
+
+        // Test error code 400
+        tester.setContentServlet(HttpErrorServlet.class);
+
+        try
+        {
+            tester.start();
+
+            HttpTester.Response response = tester.executeRequest("GET","/context/",5,TimeUnit.SECONDS);
+
+            assertThat("Response status",response.getStatus(),is(HttpStatus.BAD_REQUEST_400));
+            assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(compressionType)));
+
+            String content = tester.readResponse(response);
+            assertThat("Response content",content,is("error message"));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testUserAgentExclusion() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+        tester.setUserAgent("foo");
+
+        // Configure Gzip Handler
+        tester.getGzipHandler().addIncludedMimeTypes("text/plain");
+        tester.getGzipHandler().setExcludedAgentPatterns("bar","foo");
+        
+        // Prepare server file
+        int filesize = tester.getOutputBufferSize() * 4;
+        tester.prepareServerFile("file.txt",filesize);
+
+        // Add content servlet
+        tester.setContentServlet(DefaultServlet.class);
+
+        try
+        {
+            tester.start();
+            assertIsResponseNotGzipCompressed(tester,"GET","file.txt",filesize,HttpStatus.OK_200);
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+    
+    @Test
+    public void testUserAgentExclusionDefault() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+        tester.setContentServlet(DefaultServlet.class);
+        tester.setUserAgent("Some MSIE 6.0 user-agent");
+
+        int filesize = tester.getOutputBufferSize() * 4;
+        tester.prepareServerFile("file.txt",filesize);
+
+        try
+        {
+            tester.start();
+            HttpTester.Response http = assertIsResponseNotGzipCompressed(tester,"GET","file.txt",filesize,HttpStatus.OK_200);
+            Assert.assertEquals("Accept-Encoding, User-Agent",http.get("Vary"));
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testUserAgentExclusionByExcludedAgentPatterns() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+        tester.setUserAgent("foo");
+
+        // Configure Gzip Handler
+        tester.getGzipHandler().setExcludedAgentPatterns("bar","fo.*");
+
+        // Prepare server file
+        int filesize = tester.getOutputBufferSize() * 4;
+        tester.prepareServerFile("file.txt",filesize);
+
+        // Set content servlet
+        tester.setContentServlet(DefaultServlet.class);
+
+        try
+        {
+            tester.start();
+            assertIsResponseNotGzipCompressed(tester,"GET","file.txt",filesize,HttpStatus.OK_200);
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testExcludePaths() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        // Configure Gzip Handler
+        tester.getGzipHandler().setExcludedPaths("*.txt");
+
+        // Prepare server file
+        int filesize = tester.getOutputBufferSize() * 4;
+        tester.prepareServerFile("file.txt",filesize);
+
+        // Set content servlet
+        tester.setContentServlet(DefaultServlet.class);
+
+        try
+        {
+            tester.start();
+            assertIsResponseNotGzipCompressed(tester,"GET","file.txt",filesize,HttpStatus.OK_200);
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testIncludedPaths() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        // Configure Gzip Handler
+        tester.getGzipHandler().setExcludedPaths("/bad.txt");
+        tester.getGzipHandler().setIncludedPaths("*.txt");
+
+        // Prepare server file
+        int filesize = tester.getOutputBufferSize() * 4;
+        tester.prepareServerFile("file.txt",filesize);
+        tester.prepareServerFile("bad.txt",filesize);
+
+        // Set content servlet
+        tester.setContentServlet(DefaultServlet.class);
+
+        try
+        {
+            tester.start();
+            tester.assertIsResponseGzipCompressed("GET","file.txt");
+        }
+        finally
+        {
+            tester.stop();
+        }
+        
+        try
+        {
+            tester.start();
+            assertIsResponseNotGzipCompressed(tester,"GET","bad.txt",filesize,HttpStatus.OK_200);
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+
+    public HttpTester.Response assertIsResponseNotGzipCompressed(GzipTester tester, String method, String filename, int expectedFilesize, int status)
+            throws Exception
+    {
+        HttpTester.Response response = tester.executeRequest(method,"/context/" + filename,5,TimeUnit.SECONDS);
+
+        assertThat("Response status",response.getStatus(),is(status));
+        assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(compressionType)));
+
+        assertResponseContent(tester,response,status,filename,expectedFilesize);
+
+        return response;
+    }
+
+    private void assertResponseContent(GzipTester tester, HttpTester.Response response, int status, String filename, int expectedFilesize) throws IOException,
+            UnsupportedEncodingException
+    {
+        if (expectedFilesize >= 0)
+        {
+            assertThat("filename",filename,notNullValue());
+            assertThat("Response contentBytes.length",response.getContentBytes().length,is(expectedFilesize));
+            String contentLength = response.get("Content-Length");
+            if (StringUtil.isNotBlank(contentLength))
+            {
+                assertThat("Content-Length",response.get("Content-Length"),is(Integer.toString(expectedFilesize)));
+            }
+
+            if (status >= 200 && status < 300)
+            {
+                assertThat("ETag",response.get("ETAG"),startsWith("W/"));
+            }
+
+            File serverFile = testingdir.getPathFile(filename).toFile();
+            String expectedResponse = IO.readToString(serverFile);
+
+            String actual = tester.readResponse(response);
+            Assert.assertEquals("Expected response equals actual response",expectedResponse,actual);
+        }
+    }
+
+    
+    @Test
+    public void testIsNotGzipCompressedSVGZ() throws Exception
+    {
+        GzipTester tester = new GzipTester(testingdir,compressionType);
+
+        tester.setContentServlet(DefaultServlet.class);
+        tester.copyTestServerFile("test.svgz");
+        
+        try
+        {
+            tester.start();
+            tester.assertIsResponseNotGzipFiltered("test.svgz","test.svgz.sha1","image/svg+xml","gzip");
+        }
+        finally
+        {
+            tester.stop();
+        }
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipTester.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipTester.java
new file mode 100644
index 0000000..f7ec3f0
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipTester.java
@@ -0,0 +1,657 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isEmptyOrNullString;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.util.EnumSet;
+import java.util.Enumeration;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Servlet;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.DateGenerator;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.http.HttpTester.Response;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.servlet.ServletTester;
+import org.eclipse.jetty.toolchain.test.IO;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+
+public class GzipTester
+{
+    private static final Logger LOG = Log.getLogger(GzipTester.class);
+
+    public static class ContentMetadata
+    {
+        public final long size;
+        public final String sha1;
+
+        public ContentMetadata(long size, String sha1checksum)
+        {
+            this.size = size;
+            this.sha1 = sha1checksum;
+        }
+    }
+
+    private String encoding = "ISO8859_1";
+    private String userAgent = null;
+    private final ServletTester tester = new ServletTester("/context",ServletContextHandler.GZIP);
+    private TestingDir testdir;
+    private String accept;
+    private String compressionType;
+    
+
+    public GzipTester(TestingDir testingdir, String compressionType, String accept)
+    {
+        this.testdir = testingdir;
+        this.compressionType = compressionType;
+        this.accept = accept;
+        
+    }
+
+    public GzipTester(TestingDir testingdir, String compressionType)
+    {
+        this.testdir = testingdir;
+        this.compressionType = compressionType;
+        this.accept = compressionType;
+    }
+    
+    public GzipHandler getGzipHandler()
+    {
+        return tester.getContext().getGzipHandler();
+    }
+
+    public int getOutputBufferSize()
+    {
+        return tester.getConnector().getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().getOutputBufferSize();
+    }
+    
+    public ContentMetadata getResponseMetadata(Response response) throws Exception
+    {
+        long size = response.getContentBytes().length;
+
+        String contentEncoding = response.get("Content-Encoding");
+
+        ByteArrayInputStream bais = null;
+        InputStream in = null;
+        DigestOutputStream digester = null;
+        ByteArrayOutputStream uncompressedStream = null;
+        try
+        {
+            MessageDigest digest = MessageDigest.getInstance("SHA1");
+            bais = new ByteArrayInputStream(response.getContentBytes());
+
+            if (contentEncoding == null)
+            {
+                LOG.debug("No response content-encoding");
+                in = new PassThruInputStream(bais);
+            }
+            else if (contentEncoding.contains(GzipHandler.GZIP))
+            {
+                in = new GZIPInputStream(bais);
+            }
+            else if (contentEncoding.contains(GzipHandler.DEFLATE))
+            {
+                in = new InflaterInputStream(bais,new Inflater(true));
+            }
+            else
+            {
+                assertThat("Unexpected response content-encoding", contentEncoding, isEmptyOrNullString());
+            }
+            
+            uncompressedStream = new ByteArrayOutputStream((int)size); 
+
+            digester = new DigestOutputStream(uncompressedStream,digest);
+            IO.copy(in,digester);
+            
+            byte output[] = uncompressedStream.toByteArray();
+            String actualSha1Sum = Hex.asHex(digest.digest());
+            return new ContentMetadata(output.length,actualSha1Sum);
+        }
+        finally
+        {
+            IO.close(digester);
+            IO.close(in);
+            IO.close(bais);
+            IO.close(uncompressedStream);
+        }
+    }
+
+    public HttpTester.Response assertIsResponseGzipCompressed(String method, String filename) throws Exception
+    {
+        return assertIsResponseGzipCompressed(method,filename,filename,-1);
+    }
+
+    public HttpTester.Response assertIsResponseGzipCompressed(String method, String filename, long ifmodifiedsince) throws Exception
+    {
+        return assertIsResponseGzipCompressed(method,filename,filename,ifmodifiedsince);
+    }
+
+    public HttpTester.Response assertIsResponseGzipCompressed(String method, String requestedFilename, String serverFilename) throws Exception
+    {
+        return assertIsResponseGzipCompressed(method,requestedFilename,serverFilename,-1);
+    }
+
+    public HttpTester.Response assertNonStaticContentIsResponseGzipCompressed(String method, String path, String expected) throws Exception
+    {
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        request.setMethod(method);
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setHeader("Accept-Encoding",accept);
+
+        if (this.userAgent != null)
+            request.setHeader("User-Agent",this.userAgent);
+        request.setURI("/context/" + path);
+
+        // Issue the request
+        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+
+        int qindex = compressionType.indexOf(";");
+        if (qindex < 0)
+            Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType));
+        else
+            Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType.substring(0,qindex)));
+
+        ByteArrayInputStream bais = null;
+        InputStream in = null;
+        ByteArrayOutputStream out = null;
+        String actual = null;
+
+        try
+        {
+            bais = new ByteArrayInputStream(response.getContentBytes());
+            if (compressionType.startsWith(GzipHandler.GZIP))
+            {
+                in = new GZIPInputStream(bais);
+            }
+            else if (compressionType.startsWith(GzipHandler.DEFLATE))
+            {
+                in = new InflaterInputStream(bais,new Inflater(true));
+            }
+            out = new ByteArrayOutputStream();
+            IO.copy(in,out);
+
+            actual = out.toString(encoding);
+            assertThat("Uncompressed contents",actual,equalTo(expected));
+        }
+        finally
+        {
+            IO.close(out);
+            IO.close(in);
+            IO.close(bais);
+        }
+
+        return response;
+    }
+
+    public HttpTester.Response assertIsResponseGzipCompressed(String method, String requestedFilename, String serverFilename, long ifmodifiedsince)
+            throws Exception
+    {
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        request.setMethod(method);
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setHeader("Accept-Encoding",compressionType);
+        if (ifmodifiedsince > 0)
+            request.setHeader(HttpHeader.IF_MODIFIED_SINCE.asString(),DateGenerator.formatDate(ifmodifiedsince));
+        if (this.userAgent != null)
+            request.setHeader("User-Agent",this.userAgent);
+        request.setURI("/context/" + requestedFilename);
+
+        // Issue the request
+        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+
+        // Assert the response headers
+        // Assert.assertThat("Response.status",response.getStatus(),is(HttpServletResponse.SC_OK));
+
+        // Response headers should have either a Transfer-Encoding indicating chunked OR a Content-Length
+        /*
+         * TODO need to check for the 3rd option of EOF content. To do this properly you might need to look at both HTTP/1.1 and HTTP/1.0 requests String
+         * contentLength = response.get("Content-Length"); String transferEncoding = response.get("Transfer-Encoding");
+         * 
+         * boolean chunked = (transferEncoding != null) && (transferEncoding.indexOf("chunk") >= 0); if(!chunked) {
+         * Assert.assertThat("Response.header[Content-Length]",contentLength,notNullValue()); } else {
+         * Assert.assertThat("Response.header[Transfer-Encoding]",transferEncoding,notNullValue()); }
+         */
+
+        int qindex = compressionType.indexOf(";");
+        if (qindex < 0)
+            Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType));
+        else
+            Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType.substring(0,qindex)));
+
+        Assert.assertThat(response.get("ETag"),Matchers.startsWith("W/"));
+
+        // Assert that the decompressed contents are what we expect.
+        File serverFile = testdir.getPathFile(serverFilename).toFile();
+        String expected = IO.readToString(serverFile);
+        String actual = null;
+
+        ByteArrayInputStream bais = null;
+        InputStream in = null;
+        ByteArrayOutputStream out = null;
+        try
+        {
+            bais = new ByteArrayInputStream(response.getContentBytes());
+            if (compressionType.startsWith(GzipHandler.GZIP))
+            {
+                in = new GZIPInputStream(bais);
+            }
+            else if (compressionType.startsWith(GzipHandler.DEFLATE))
+            {
+                in = new InflaterInputStream(bais,new Inflater(true));
+            }
+            out = new ByteArrayOutputStream();
+            IO.copy(in,out);
+
+            actual = out.toString(encoding);
+            assertThat("Uncompressed contents",actual,equalTo(expected));
+        }
+        finally
+        {
+            IO.close(out);
+            IO.close(in);
+            IO.close(bais);
+        }
+
+        return response;
+    }
+
+    public HttpTester.Response assertIsResponseNotModified(String method, String requestedFilename, long ifmodifiedsince) throws Exception
+    {
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        request.setMethod(method);
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setHeader("Accept-Encoding",compressionType);
+        if (ifmodifiedsince > 0)
+            request.setHeader(HttpHeader.IF_MODIFIED_SINCE.asString(),DateGenerator.formatDate(ifmodifiedsince));
+        if (this.userAgent != null)
+            request.setHeader("User-Agent",this.userAgent);
+        request.setURI("/context/" + requestedFilename);
+
+        // Issue the request
+        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+
+        Assert.assertThat(response.getStatus(),Matchers.equalTo(304));
+        Assert.assertThat(response.get("ETag"),Matchers.startsWith("W/"));
+
+        return response;
+    }
+
+    /**
+     * Makes sure that the response contains an unfiltered file contents.
+     * <p>
+     * This is used to test exclusions and passthroughs in the GzipHandler.
+     * <p>
+     * An example is to test that it is possible to configure GzipFilter to not recompress content that shouldn't be compressed by the GzipFilter.
+     *
+     * @param requestedFilename
+     *            the filename used to on the GET request,.
+     * @param testResourceSha1Sum
+     *            the sha1sum file that contains the SHA1SUM checksum that will be used to verify that the response contents are what is intended.
+     * @param expectedContentType the expected content type
+     * @throws Exception on test failure
+     */
+    public void assertIsResponseNotGziped(String requestedFilename, String testResourceSha1Sum, String expectedContentType) throws Exception
+    {
+        assertIsResponseNotGzipFiltered(requestedFilename,testResourceSha1Sum,expectedContentType,null);
+    }
+
+    /**
+     * Makes sure that the response contains an unfiltered file contents.
+     * <p>
+     * This is used to test exclusions and passthroughs in the GzipHandler.
+     * <p>
+     * An example is to test that it is possible to configure GzipFilter to not recompress content that shouldn't be compressed by the GzipFilter.
+     *
+     * @param requestedFilename
+     *            the filename used to on the GET request,.
+     * @param testResourceSha1Sum
+     *            the sha1sum file that contains the SHA1SUM checksum that will be used to verify that the response contents are what is intended.
+     * @param expectedContentType the expected content type
+     * @param expectedContentEncoding
+     *            can be non-null in some circumstances, eg when dealing with pre-gzipped .svgz files
+     * @throws Exception on test failure
+     */
+    public void assertIsResponseNotGzipFiltered(String requestedFilename, String testResourceSha1Sum, String expectedContentType, String expectedContentEncoding)
+            throws Exception
+    {
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        request.setMethod("GET");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setHeader("Accept-Encoding",compressionType);
+        if (this.userAgent != null)
+            request.setHeader("User-Agent",this.userAgent);
+        request.setURI("/context/" + requestedFilename);
+
+        // Issue the request
+        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+
+        dumpHeaders(requestedFilename + " / Response Headers",response);
+
+        // Assert the response headers
+        String prefix = requestedFilename + " / Response";
+        Assert.assertThat(prefix + ".status",response.getStatus(),is(HttpServletResponse.SC_OK));
+        Assert.assertThat(prefix + ".header[Content-Length]",response.get("Content-Length"),notNullValue());
+        Assert.assertThat(prefix + ".header[Content-Encoding] (should not be recompressed by GzipHandler)",response.get("Content-Encoding"),
+                expectedContentEncoding == null?nullValue():notNullValue());
+        if (expectedContentEncoding != null)
+            Assert.assertThat(prefix + ".header[Content-Encoding]",response.get("Content-Encoding"),is(expectedContentEncoding));
+        Assert.assertThat(prefix + ".header[Content-Type] (should have a Content-Type associated with it)",response.get("Content-Type"),notNullValue());
+        Assert.assertThat(prefix + ".header[Content-Type]",response.get("Content-Type"),is(expectedContentType));
+
+        Assert.assertThat(response.get("ETAG"),Matchers.startsWith("W/"));
+
+        ByteArrayInputStream bais = null;
+        DigestOutputStream digester = null;
+        try
+        {
+            MessageDigest digest = MessageDigest.getInstance("SHA1");
+            bais = new ByteArrayInputStream(response.getContentBytes());
+            digester = new DigestOutputStream(new NoOpOutputStream(),digest);
+            IO.copy(bais,digester);
+
+            String actualSha1Sum = Hex.asHex(digest.digest());
+            String expectedSha1Sum = loadExpectedSha1Sum(testResourceSha1Sum);
+            Assert.assertEquals(requestedFilename + " / SHA1Sum of content",expectedSha1Sum,actualSha1Sum);
+        }
+        finally
+        {
+            IO.close(digester);
+            IO.close(bais);
+        }
+    }
+
+    private void dumpHeaders(String prefix, HttpTester.Message message)
+    {
+        LOG.debug("dumpHeaders: {}",prefix);
+        Enumeration<String> names = message.getFieldNames();
+        while (names.hasMoreElements())
+        {
+            String name = names.nextElement();
+            String value = message.get(name);
+            LOG.debug("dumpHeaders:   {} = {}",name,value);
+        }
+    }
+
+    private String loadExpectedSha1Sum(String testResourceSha1Sum) throws IOException
+    {
+        File sha1File = MavenTestingUtils.getTestResourceFile(testResourceSha1Sum);
+        String contents = IO.readToString(sha1File);
+        Pattern pat = Pattern.compile("^[0-9A-Fa-f]*");
+        Matcher mat = pat.matcher(contents);
+        Assert.assertTrue("Should have found HEX code in SHA1 file: " + sha1File,mat.find());
+        return mat.group();
+    }
+
+    public HttpTester.Response executeRequest(String method, String path, int idleFor, TimeUnit idleUnit) throws Exception
+    {
+        HttpTester.Request request = HttpTester.newRequest();
+
+        request.setMethod(method);
+        request.setVersion("HTTP/1.1");
+        request.setHeader("Host","tester");
+        request.setHeader("Accept-Encoding",accept);
+        request.setHeader("Connection","close");
+
+        if (this.userAgent != null)
+        {
+            request.setHeader("User-Agent",this.userAgent);
+        }
+        
+        request.setURI(path);
+
+        // Issue the request
+        return HttpTester.parseResponse(tester.getResponses(request.generate(),idleFor,idleUnit));
+    }
+
+    public String readResponse(HttpTester.Response response) throws IOException, UnsupportedEncodingException
+    {
+        String actual = null;
+        InputStream in = null;
+        ByteArrayOutputStream out = null;
+        try
+        {
+            byte[] content = response.getContentBytes();
+            if (content != null)
+                actual = new String(response.getContentBytes(),encoding);
+            else
+                actual = "";
+        }
+        finally
+        {
+            IO.close(out);
+            IO.close(in);
+        }
+        return actual;
+    }
+
+    /**
+     * Generate string content of arbitrary length.
+     *
+     * @param length
+     *            the length of the string to generate.
+     * @return the string content.
+     */
+    public String generateContent(int length)
+    {
+        StringBuilder builder = new StringBuilder();
+        do
+        {
+            builder.append("Lorem ipsum dolor sit amet, consectetur adipiscing elit. In quis felis nunc.\n");
+            builder.append("Quisque suscipit mauris et ante auctor ornare rhoncus lacus aliquet. Pellentesque\n");
+            builder.append("habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.\n");
+            builder.append("Vestibulum sit amet felis augue, vel convallis dolor. Cras accumsan vehicula diam\n");
+            builder.append("at faucibus. Etiam in urna turpis, sed congue mi. Morbi et lorem eros. Donec vulputate\n");
+            builder.append("velit in risus suscipit lobortis. Aliquam id urna orci, nec sollicitudin ipsum.\n");
+            builder.append("Cras a orci turpis. Donec suscipit vulputate cursus. Mauris nunc tellus, fermentum\n");
+            builder.append("eu auctor ut, mollis at diam. Quisque porttitor ultrices metus, vitae tincidunt massa\n");
+            builder.append("sollicitudin a. Vivamus porttitor libero eget purus hendrerit cursus. Integer aliquam\n");
+            builder.append("consequat mauris quis luctus. Cras enim nibh, dignissim eu faucibus ac, mollis nec neque.\n");
+            builder.append("Aliquam purus mauris, consectetur nec convallis lacinia, porta sed ante. Suspendisse\n");
+            builder.append("et cursus magna. Donec orci enim, molestie a lobortis eu, imperdiet vitae neque.\n");
+        }
+        while (builder.length() < length);
+
+        // Make sure we are exactly at requested length. (truncate the extra)
+        if (builder.length() > length)
+        {
+            builder.setLength(length);
+        }
+
+        return builder.toString();
+    }
+
+    public String getEncoding()
+    {
+        return encoding;
+    }
+
+    /**
+     * Create a file on the server resource path of a specified filename and size.
+     *
+     * @param filename
+     *            the filename to create
+     * @param filesize
+     *            the file size to create (Note: this isn't suitable for creating large multi-megabyte files)
+     * @return the prepared file
+     * @throws IOException if unable to create file
+     */
+    public File prepareServerFile(String filename, int filesize) throws IOException
+    {
+        File dir = testdir.getPath().toFile();
+        File testFile = new File(dir,filename);
+        // Make sure we have a uniq filename (to work around windows File.delete bug)
+        int i = 0;
+        while (testFile.exists())
+        {
+            testFile = new File(dir,(i++) + "-" + filename);
+        }
+
+        FileOutputStream fos = null;
+        ByteArrayInputStream in = null;
+        try
+        {
+            fos = new FileOutputStream(testFile,false);
+            in = new ByteArrayInputStream(generateContent(filesize).getBytes(encoding));
+            IO.copy(in,fos);
+            return testFile;
+        }
+        finally
+        {
+            IO.close(in);
+            IO.close(fos);
+        }
+    }
+
+    /**
+     * Copy a src/test/resource file into the server tree for eventual serving.
+     *
+     * @param filename
+     *            the filename to look for in src/test/resources
+     * @throws IOException if unable to copy file
+     */
+    public void copyTestServerFile(String filename) throws IOException
+    {
+        File srcFile = MavenTestingUtils.getTestResourceFile(filename);
+        File testFile = testdir.getPathFile(filename).toFile();
+
+        IO.copy(srcFile,testFile);
+    }
+
+    /**
+     * Set the servlet that provides content for the GzipHandler in being tested.
+     *
+     * @param servletClass
+     *            the servlet that will provide content.
+     * @throws IOException if unable to set content servlet
+     */
+    public void setContentServlet(Class<? extends Servlet> servletClass) throws IOException
+    {
+        String resourceBase = testdir.getPath().toString();
+        tester.setContextPath("/context");
+        tester.setResourceBase(resourceBase);
+        ServletHolder servletHolder = tester.addServlet(servletClass,"/");
+        servletHolder.setInitParameter("baseDir",resourceBase);
+        servletHolder.setInitParameter("etags","true");
+    }
+
+    public void setEncoding(String encoding)
+    {
+        this.encoding = encoding;
+    }
+
+    public void setUserAgent(String ua)
+    {
+        this.userAgent = ua;
+    }
+
+    public void addMimeType(String extension, String mimetype)
+    {
+        this.tester.getContext().getMimeTypes().addMimeMapping(extension,mimetype);
+    }
+
+    /**
+     * Add an arbitrary filter to the test case.
+     * 
+     * @param holder
+     *            the filter to add
+     * @param pathSpec
+     *            the path spec for this filter
+     * @param dispatches
+     *            the set of {@link DispatcherType} to associate with this filter
+     * @throws IOException if unable to add filter
+     */
+    public void addFilter(FilterHolder holder, String pathSpec, EnumSet<DispatcherType> dispatches) throws IOException
+    {
+        tester.addFilter(holder,pathSpec,dispatches);
+    }
+
+    public void start() throws Exception
+    {
+        Assert.assertThat("No servlet defined yet.  Did you use #setContentServlet()?",tester,notNullValue());
+        
+        if (LOG.isDebugEnabled())
+        {
+            tester.dumpStdErr();
+        }
+        tester.start();
+    }
+
+    public void stop()
+    {
+        // NOTE: Do not cleanup the testdir. Failures can't be diagnosed if you do that.
+        // IO.delete(testdir.getDir()):
+        try
+        {
+            tester.stop();
+        }
+        catch (Exception e)
+        {
+            // Don't toss this out into Junit as this would be the last exception
+            // that junit will report as being the cause of the test failure.
+            // when in reality, the earlier setup issue is the real cause.
+            e.printStackTrace(System.err);
+        }
+    }
+
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/Hex.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/Hex.java
new file mode 100644
index 0000000..f524e9a
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/Hex.java
@@ -0,0 +1,76 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+public final class Hex
+{
+    private static final char[] hexcodes = "0123456789abcdef".toCharArray();
+
+    public static byte[] asByteArray(String id, int size)
+    {
+        if ((id.length() < 0) || (id.length() > (size * 2)))
+        {
+            throw new IllegalArgumentException(String.format("Invalid ID length of <%d> expected range of <0> to <%d>",id.length(),(size * 2)));
+        }
+
+        byte buf[] = new byte[size];
+        byte hex;
+        int len = id.length();
+
+        int idx = (int)Math.floor(((size * 2) - (double)len) / 2);
+        int i = 0;
+        if ((len % 2) != 0)
+        { // deal with odd numbered chars
+            i -= 1;
+        }
+
+        for (; i < len; i++)
+        {
+            hex = 0;
+            if (i >= 0)
+            {
+                hex = (byte)(Character.digit(id.charAt(i),16) << 4);
+            }
+            i++;
+            hex += (byte)(Character.digit(id.charAt(i),16));
+
+            buf[idx] = hex;
+            idx++;
+        }
+
+        return buf;
+    }
+
+    public static String asHex(byte buf[])
+    {
+        int len = buf.length;
+        char out[] = new char[len * 2];
+        for (int i = 0; i < len; i++)
+        {
+            out[i * 2] = hexcodes[(buf[i] & 0xF0) >> 4];
+            out[(i * 2) + 1] = hexcodes[(buf[i] & 0x0F)];
+        }
+        return String.valueOf(out);
+    }
+
+    private Hex()
+    {
+        /* prevent instantiation */
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/IncludedGzipMinSizeTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/IncludedGzipMinSizeTest.java
new file mode 100644
index 0000000..06005ed
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/IncludedGzipMinSizeTest.java
@@ -0,0 +1,87 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import javax.servlet.Servlet;
+
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Perform specific tests on the IncludableGzipHandler's ability to manage
+ * minGzipSize initialization parameter.
+ *
+ * @see <a href="Eclipse Bug 366106">http://bugs.eclipse.org/366106</a>
+ */
+public class IncludedGzipMinSizeTest
+{
+    public IncludedGzipMinSizeTest()
+    {
+        this.compressionType = GzipHandler.GZIP;
+    }
+
+    @Rule
+    public TestingDir testdir = new TestingDir();
+
+    private String compressionType;
+    private Class<? extends Servlet> testServlet = TestMinGzipSizeServlet.class;
+
+    @Test
+    public void testUnderMinSize() throws Exception
+    {
+        GzipTester tester = new GzipTester(testdir, compressionType);
+
+        tester.setContentServlet(testServlet);
+        // A valid mime type that we will never use in this test.
+        // configured here to prevent mimeType==null logic
+        tester.getGzipHandler().addIncludedMimeTypes("application/soap+xml");
+        tester.getGzipHandler().setMinGzipSize(2048);
+
+        tester.copyTestServerFile("small_script.js");
+
+        try {
+            tester.start();
+            tester.assertIsResponseNotGziped("small_script.js",
+                    "small_script.js.sha1",
+                    "text/javascript; charset=utf-8");
+        } finally {
+            tester.stop();
+        }
+    }
+
+    @Test
+    public void testOverMinSize() throws Exception
+    {
+        GzipTester tester = new GzipTester(testdir, compressionType);
+
+        tester.setContentServlet(testServlet);
+        tester.getGzipHandler().addIncludedMimeTypes("application/soap+xml","text/javascript","application/javascript");
+        tester.getGzipHandler().setMinGzipSize(2048);
+
+        tester.copyTestServerFile("big_script.js");
+
+        try {
+            tester.start();
+            tester.assertIsResponseGzipCompressed("GET","big_script.js");
+        } finally {
+            tester.stop();
+        }
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/IncludedGzipTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/IncludedGzipTest.java
new file mode 100644
index 0000000..7be81ae
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/IncludedGzipTest.java
@@ -0,0 +1,134 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.servlet.ServletTester;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.IO;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class IncludedGzipTest
+{
+
+    @Rule
+    public TestingDir testdir = new TestingDir();
+
+    private static String __content =
+        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In quis felis nunc. "+
+        "Quisque suscipit mauris et ante auctor ornare rhoncus lacus aliquet. Pellentesque "+
+        "habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. "+
+        "Vestibulum sit amet felis augue, vel convallis dolor. Cras accumsan vehicula diam "+
+        "at faucibus. Etiam in urna turpis, sed congue mi. Morbi et lorem eros. Donec vulputate "+
+        "velit in risus suscipit lobortis. Aliquam id urna orci, nec sollicitudin ipsum. "+
+        "Cras a orci turpis. Donec suscipit vulputate cursus. Mauris nunc tellus, fermentum "+
+        "eu auctor ut, mollis at diam. Quisque porttitor ultrices metus, vitae tincidunt massa "+
+        "sollicitudin a. Vivamus porttitor libero eget purus hendrerit cursus. Integer aliquam "+
+        "consequat mauris quis luctus. Cras enim nibh, dignissim eu faucibus ac, mollis nec neque. "+
+        "Aliquam purus mauris, consectetur nec convallis lacinia, porta sed ante. Suspendisse "+
+        "et cursus magna. Donec orci enim, molestie a lobortis eu, imperdiet vitae neque.";
+
+    private ServletTester tester;
+    private String compressionType;
+
+    public IncludedGzipTest()
+    {
+        this.compressionType = GzipHandler.GZIP;
+    }
+
+    @Before
+    public void setUp() throws Exception
+    {
+        testdir.ensureEmpty();
+
+        File testFile = testdir.getPathFile("file.txt").toFile();
+        try (OutputStream testOut = new BufferedOutputStream(new FileOutputStream(testFile)))
+        {
+            ByteArrayInputStream testIn = new ByteArrayInputStream(__content.getBytes("ISO8859_1"));
+            IO.copy(testIn,testOut);
+        }
+
+        tester=new ServletTester("/context");
+        tester.getContext().setResourceBase(testdir.getPath().toString());
+        tester.getContext().addServlet(org.eclipse.jetty.servlet.DefaultServlet.class, "/");
+        
+        GzipHandler gzipHandler = new GzipHandler();
+        tester.getContext().insertHandler(gzipHandler);
+        tester.start();
+    }
+
+    @After
+    public void tearDown() throws Exception
+    {
+        tester.stop();
+    }
+
+    @Test
+    public void testGzip() throws Exception
+    {
+        // generated and parsed test
+
+        ByteBuffer request=BufferUtil.toBuffer(
+            "GET /context/file.txt HTTP/1.0\r\n"+
+            "Host: tester\r\n"+
+            "Accept-Encoding: "+compressionType+"\r\n"+
+            "\r\n");
+
+
+        HttpTester.Response response=HttpTester.parseResponse(tester.getResponses(request));
+
+        assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+        assertEquals(compressionType,response.get("Content-Encoding"));
+
+        InputStream testIn = null;
+        ByteArrayInputStream compressedResponseStream = new ByteArrayInputStream(response.getContentBytes());
+        if (compressionType.equals(GzipHandler.GZIP))
+        {
+            testIn = new GZIPInputStream(compressedResponseStream);
+        }
+        else if (compressionType.equals(GzipHandler.DEFLATE))
+        {
+            testIn = new InflaterInputStream(compressedResponseStream, new Inflater(true));
+        }
+        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
+        IO.copy(testIn,testOut);
+
+        assertEquals(__content, testOut.toString("ISO8859_1"));
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/NoOpOutputStream.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/NoOpOutputStream.java
new file mode 100644
index 0000000..5616287
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/NoOpOutputStream.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Stream that does nothing. (Used by SHA1SUM routines)
+ */
+public class NoOpOutputStream extends OutputStream
+{
+    @Override
+    public void close() throws IOException
+    {
+        /* noop */
+    }
+    
+    @Override
+    public void flush() throws IOException
+    {
+        /* noop */
+    }
+    
+    @Override
+    public void write(byte[] b) throws IOException
+    {
+        /* noop */
+    }
+    
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException
+    {
+        /* noop */
+    }
+    
+    @Override
+    public void write(int b) throws IOException
+    {
+        /* noop */
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/PassThruInputStream.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/PassThruInputStream.java
new file mode 100644
index 0000000..6c214c7
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/PassThruInputStream.java
@@ -0,0 +1,36 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+
+/**
+ * A simple pass-through input stream.
+ * <p>
+ * Used in some test cases where a proper resource open/close is needed for
+ * some potentially optional layers of the input stream.
+ */
+public class PassThruInputStream extends FilterInputStream
+{
+    public PassThruInputStream(InputStream in)
+    {
+        super(in);
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestDirContentServlet.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestDirContentServlet.java
new file mode 100644
index 0000000..3a5b0a9
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestDirContentServlet.java
@@ -0,0 +1,74 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+
+import org.eclipse.jetty.toolchain.test.PathAssert;
+import org.eclipse.jetty.util.IO;
+
+@SuppressWarnings("serial")
+public class TestDirContentServlet extends HttpServlet
+{
+    private File basedir;
+
+    @Override
+    public void init(ServletConfig config) throws ServletException
+    {
+        basedir = new File(config.getInitParameter("baseDir"));
+    }
+
+    public File getTestFile(String filename)
+    {
+        File testfile = new File(basedir,filename);
+        PathAssert.assertFileExists("Content File should exist",testfile);
+        return testfile;
+    }
+
+    protected byte[] loadContentFileBytes(final String fileName) throws IOException
+    {
+        String relPath = fileName;
+        relPath = relPath.replaceFirst("^/context/","");
+        relPath = relPath.replaceFirst("^/","");
+
+        File contentFile =  getTestFile(relPath);
+
+        FileInputStream in = null;
+        ByteArrayOutputStream out = null;
+        try
+        {
+            in = new FileInputStream(contentFile);
+            out = new ByteArrayOutputStream();
+            IO.copy(in,out);
+            return out.toByteArray();
+        }
+        finally
+        {
+            IO.close(out);
+            IO.close(in);
+        }
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestMinGzipSizeServlet.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestMinGzipSizeServlet.java
new file mode 100644
index 0000000..db7abe9
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestMinGzipSizeServlet.java
@@ -0,0 +1,68 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.MimeTypes;
+
+/**
+ * Test servlet for testing against unusual minGzip configurable.
+ */
+@SuppressWarnings("serial")
+public class TestMinGzipSizeServlet extends TestDirContentServlet
+{
+    private MimeTypes mimeTypes;
+
+    @Override
+    public void init(ServletConfig config) throws ServletException
+    {
+        super.init(config);
+        mimeTypes = new MimeTypes();
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String fileName = request.getServletPath();
+        byte[] dataBytes = loadContentFileBytes(fileName);
+
+        response.setContentLength(dataBytes.length);
+        response.setHeader("ETag","W/etag-"+fileName);
+        if (fileName.endsWith(".js"))
+        {
+            // intentionally long-form content type to test ";" splitting in code
+            response.setContentType("text/javascript; charset=utf-8");
+        }
+        else
+        {
+            String mime = mimeTypes.getMimeByExtension(fileName);
+            if (mime != null)
+                response.setContentType(mime);
+        }
+        ServletOutputStream out = response.getOutputStream();
+        out.write(dataBytes);
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletBufferTypeLengthWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletBufferTypeLengthWrite.java
new file mode 100644
index 0000000..fd5f60e
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletBufferTypeLengthWrite.java
@@ -0,0 +1,67 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.HttpOutput;
+
+/**
+ * A sample servlet to serve static content, using a order of construction that has caused problems for
+ * {@link GzipHandler} in the past.
+ *
+ * Using a real-world pattern of:
+ *
+ * <pre>
+ *  1) get stream
+ *  2) set content type
+ *  2) set content length
+ *  4) write
+ * </pre>
+ *
+ * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
+ */
+@SuppressWarnings("serial")
+public class TestServletBufferTypeLengthWrite extends TestDirContentServlet
+{
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String fileName = request.getServletPath();
+        byte[] dataBytes = loadContentFileBytes(fileName);
+
+        ServletOutputStream out = response.getOutputStream();
+
+        if (fileName.endsWith("txt"))
+            response.setContentType("text/plain");
+        else if (fileName.endsWith("mp3"))
+            response.setContentType("audio/mpeg");
+        response.setHeader("ETag","W/etag-"+fileName);
+
+        response.setContentLength(dataBytes.length);
+        
+        ((HttpOutput)out).write(ByteBuffer.wrap(dataBytes).asReadOnlyBuffer());
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletLengthStreamTypeWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletLengthStreamTypeWrite.java
new file mode 100644
index 0000000..92cb4fc
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletLengthStreamTypeWrite.java
@@ -0,0 +1,64 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A sample servlet to serve static content, using a order of construction that has caused problems for
+ * {@link GzipHandler} in the past.
+ *
+ * Using a real-world pattern of:
+ *
+ * <pre>
+ *  1) set content length
+ *  2) get stream
+ *  3) set content type
+ *  4) write
+ * </pre>
+ *
+ * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
+ */
+@SuppressWarnings("serial")
+public class TestServletLengthStreamTypeWrite extends TestDirContentServlet
+{
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String fileName = request.getServletPath();
+        byte[] dataBytes = loadContentFileBytes(fileName);
+
+        response.setContentLength(dataBytes.length);
+
+        ServletOutputStream out = response.getOutputStream();
+
+        if (fileName.endsWith("txt"))
+            response.setContentType("text/plain");
+        else if (fileName.endsWith("mp3"))
+            response.setContentType("audio/mpeg");
+        response.setHeader("ETag","W/etag-"+fileName);
+
+        out.write(dataBytes);
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletLengthTypeStreamWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletLengthTypeStreamWrite.java
new file mode 100644
index 0000000..6b55e6e
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletLengthTypeStreamWrite.java
@@ -0,0 +1,63 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A sample servlet to serve static content, using a order of construction that has caused problems for
+ * {@link GzipHandler} in the past.
+ *
+ * Using a real-world pattern of:
+ *
+ * <pre>
+ *  1) set content length
+ *  2) set content type
+ *  3) get stream
+ *  4) write
+ * </pre>
+ *
+ * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
+ */
+@SuppressWarnings("serial")
+public class TestServletLengthTypeStreamWrite extends TestDirContentServlet
+{
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String fileName = request.getServletPath();
+        byte[] dataBytes = loadContentFileBytes(fileName);
+
+        response.setContentLength(dataBytes.length);
+
+        if (fileName.endsWith("txt"))
+            response.setContentType("text/plain");
+        else if (fileName.endsWith("mp3"))
+            response.setContentType("audio/mpeg");
+        response.setHeader("ETag","W/etag-"+fileName);
+
+        ServletOutputStream out = response.getOutputStream();
+        out.write(dataBytes);
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletStreamLengthTypeWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletStreamLengthTypeWrite.java
new file mode 100644
index 0000000..0e3845d
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletStreamLengthTypeWrite.java
@@ -0,0 +1,64 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A sample servlet to serve static content, using a order of construction that has caused problems for
+ * {@link GzipHandler} in the past.
+ *
+ * Using a real-world pattern of:
+ *
+ * <pre>
+ *  1) get stream
+ *  2) set content length
+ *  3) set content type
+ *  4) write
+ * </pre>
+ *
+ * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
+ */
+@SuppressWarnings("serial")
+public class TestServletStreamLengthTypeWrite extends TestDirContentServlet
+{
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String fileName = request.getServletPath();
+        byte[] dataBytes = loadContentFileBytes(fileName);
+
+        ServletOutputStream out = response.getOutputStream();
+
+        response.setContentLength(dataBytes.length);
+
+        if (fileName.endsWith("txt"))
+            response.setContentType("text/plain");
+        else if (fileName.endsWith("mp3"))
+            response.setContentType("audio/mpeg");
+        response.setHeader("ETag","W/etag-"+fileName);
+
+        out.write(dataBytes);
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletStreamLengthTypeWriteWithFlush.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletStreamLengthTypeWriteWithFlush.java
new file mode 100644
index 0000000..a8960b6
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletStreamLengthTypeWriteWithFlush.java
@@ -0,0 +1,70 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A sample servlet to serve static content, using a order of construction that has caused problems for
+ * {@link GzipHandler} in the past.
+ * 
+ * Using a real-world pattern of:
+ * 
+ * <pre>
+ *  1) get stream
+ *  2) set content length
+ *  3) set content type
+ *  4) write and flush
+ * </pre>
+ * 
+ * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
+ */
+@SuppressWarnings("serial")
+public class TestServletStreamLengthTypeWriteWithFlush extends TestDirContentServlet
+{
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String fileName = request.getServletPath();
+        byte[] dataBytes = loadContentFileBytes(fileName);
+
+        ServletOutputStream out = response.getOutputStream();
+
+        // set content-length of uncompressed content (GzipHandler should handle this)
+        response.setContentLength(dataBytes.length);
+        
+        if (fileName.endsWith("txt"))
+            response.setContentType("text/plain");
+        else if (fileName.endsWith("mp3"))
+            response.setContentType("audio/mpeg");
+        response.setHeader("ETag","W/etag-"+fileName);
+
+        for ( int i = 0 ; i < dataBytes.length ; i++)
+        {
+            out.write(dataBytes[i]);
+            // flush using response object (not the stream itself)
+            response.flushBuffer();
+        }
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletStreamTypeLengthWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletStreamTypeLengthWrite.java
new file mode 100644
index 0000000..2cd2924
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletStreamTypeLengthWrite.java
@@ -0,0 +1,64 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A sample servlet to serve static content, using a order of construction that has caused problems for
+ * {@link GzipHandler} in the past.
+ *
+ * Using a real-world pattern of:
+ *
+ * <pre>
+ *  1) get stream
+ *  2) set content type
+ *  2) set content length
+ *  4) write
+ * </pre>
+ *
+ * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
+ */
+@SuppressWarnings("serial")
+public class TestServletStreamTypeLengthWrite extends TestDirContentServlet
+{
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String fileName = request.getServletPath();
+        byte[] dataBytes = loadContentFileBytes(fileName);
+
+        ServletOutputStream out = response.getOutputStream();
+
+        if (fileName.endsWith("txt"))
+            response.setContentType("text/plain");
+        else if (fileName.endsWith("mp3"))
+            response.setContentType("audio/mpeg");
+        response.setHeader("ETag","W/etag-"+fileName);
+
+        response.setContentLength(dataBytes.length);
+
+        out.write(dataBytes);
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletTypeLengthStreamWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletTypeLengthStreamWrite.java
new file mode 100644
index 0000000..76ec2a4
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletTypeLengthStreamWrite.java
@@ -0,0 +1,63 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A sample servlet to serve static content, using a order of construction that has caused problems for
+ * {@link GzipHandler} in the past.
+ *
+ * Using a real-world pattern of:
+ *
+ * <pre>
+ *  1) set content type
+ *  2) set content length
+ *  3) get stream
+ *  4) write
+ * </pre>
+ *
+ * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
+ */
+@SuppressWarnings("serial")
+public class TestServletTypeLengthStreamWrite extends TestDirContentServlet
+{
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String fileName = request.getServletPath();
+        byte[] dataBytes = loadContentFileBytes(fileName);
+
+        if (fileName.endsWith("txt"))
+            response.setContentType("text/plain");
+        else if (fileName.endsWith("mp3"))
+            response.setContentType("audio/mpeg");
+        response.setHeader("ETag","W/etag-"+fileName);
+
+        response.setContentLength(dataBytes.length);
+
+        ServletOutputStream out = response.getOutputStream();
+        out.write(dataBytes);
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletTypeStreamLengthWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletTypeStreamLengthWrite.java
new file mode 100644
index 0000000..094d690
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestServletTypeStreamLengthWrite.java
@@ -0,0 +1,64 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A sample servlet to serve static content, using a order of construction that has caused problems for
+ * {@link GzipHandler} in the past.
+ *
+ * Using a real-world pattern of:
+ *
+ * <pre>
+ *  1) set content type
+ *  2) get stream
+ *  3) set content length
+ *  4) write
+ * </pre>
+ *
+ * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
+ */
+@SuppressWarnings("serial")
+public class TestServletTypeStreamLengthWrite extends TestDirContentServlet
+{
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String fileName = request.getServletPath();
+        byte[] dataBytes = loadContentFileBytes(fileName);
+
+        if (fileName.endsWith("txt"))
+            response.setContentType("text/plain");
+        else if (fileName.endsWith("mp3"))
+            response.setContentType("audio/mpeg");
+        response.setHeader("ETag","W/etag-"+fileName);
+
+        ServletOutputStream out = response.getOutputStream();
+
+        response.setContentLength(dataBytes.length);
+
+        out.write(dataBytes);
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestStaticMimeTypeServlet.java b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestStaticMimeTypeServlet.java
new file mode 100644
index 0000000..5c3e8f2
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/TestStaticMimeTypeServlet.java
@@ -0,0 +1,81 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.handler.gzip;
+
+import java.io.IOException;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.MimeTypes;
+
+/**
+ * Test servlet for testing against unusual MimeTypes and Content-Types.
+ */
+@SuppressWarnings("serial")
+public class TestStaticMimeTypeServlet extends TestDirContentServlet
+{
+    private MimeTypes mimeTypes;
+
+    @Override
+    public void init(ServletConfig config) throws ServletException
+    {
+        super.init(config);
+        mimeTypes = new MimeTypes();
+        // Some real world, yet not terribly common, mime type mappings.
+        mimeTypes.addMimeMapping("bz2","application/bzip2");
+        mimeTypes.addMimeMapping("bmp","image/bmp");
+        mimeTypes.addMimeMapping("tga","application/tga");
+        mimeTypes.addMimeMapping("xcf","image/xcf");
+        mimeTypes.addMimeMapping("jp2","image/jpeg2000");
+
+        // Some of the other gzip mime-types seen in the wild.
+        // NOTE: Using oddball extensions just so that the calling request can specify
+        //       which strange mime type to use.
+        mimeTypes.addMimeMapping("x-gzip","application/x-gzip");
+        mimeTypes.addMimeMapping("x-gunzip","application/x-gunzip");
+        mimeTypes.addMimeMapping("gzipped","application/gzippped");
+        mimeTypes.addMimeMapping("gzip-compressed","application/gzip-compressed");
+        mimeTypes.addMimeMapping("x-compressed","application/x-compressed");
+        mimeTypes.addMimeMapping("x-compress","application/x-compress");
+        mimeTypes.addMimeMapping("gzipdoc","gzip/document");
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String fileName = request.getServletPath();
+        byte[] dataBytes = loadContentFileBytes(fileName);
+
+        response.setContentLength(dataBytes.length);
+        response.setHeader("ETag","W/etag-"+fileName);
+
+        String mime = mimeTypes.getMimeByExtension(fileName);
+        if (mime == null)
+            response.setContentType("application/octet-stream");
+        else
+            response.setContentType(mime);
+
+        ServletOutputStream out = response.getOutputStream();
+        out.write(dataBytes);
+    }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AbstractDoSFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AbstractDoSFilterTest.java
index 6d4cef5..74141dd 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AbstractDoSFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AbstractDoSFilterTest.java
@@ -18,11 +18,6 @@
 
 package org.eclipse.jetty.servlets;
 
-import static org.hamcrest.Matchers.greaterThan;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -44,23 +39,17 @@
 import org.eclipse.jetty.util.IO;
 import org.hamcrest.Matchers;
 import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
+import org.junit.Assert;
 import org.junit.Test;
 
-/**
- * @version $Revision$ $Date$
- */
 public abstract class AbstractDoSFilterTest
 {
-    private static ServletTester _tester;
-    private static String _host;
-    private static int _port;
-    private static long _requestMaxTime = 200;
-    private static FilterHolder _dosFilter;
-    private static FilterHolder _timeoutFilter;
+    protected ServletTester _tester;
+    protected String _host;
+    protected int _port;
+    protected long _requestMaxTime = 200;
 
-    public static void startServer(Class<? extends Filter> filter) throws Exception
+    public void startServer(Class<? extends Filter> filter) throws Exception
     {
         _tester = new ServletTester("/ctx");
         HttpURI uri = new HttpURI(_tester.createConnector(true));
@@ -69,51 +58,35 @@
 
         _tester.getContext().addServlet(TestServlet.class, "/*");
 
-        _dosFilter = _tester.getContext().addFilter(filter, "/dos/*", EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-        _dosFilter.setInitParameter("maxRequestsPerSec", "4");
-        _dosFilter.setInitParameter("delayMs", "200");
-        _dosFilter.setInitParameter("throttledRequests", "1");
-        _dosFilter.setInitParameter("waitMs", "10");
-        _dosFilter.setInitParameter("throttleMs", "4000");
-        _dosFilter.setInitParameter("remotePort", "false");
-        _dosFilter.setInitParameter("insertHeaders", "true");
+        FilterHolder dosFilter = _tester.getContext().addFilter(filter, "/dos/*", EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC));
+        dosFilter.setInitParameter("maxRequestsPerSec", "4");
+        dosFilter.setInitParameter("delayMs", "200");
+        dosFilter.setInitParameter("throttledRequests", "1");
+        dosFilter.setInitParameter("waitMs", "10");
+        dosFilter.setInitParameter("throttleMs", "4000");
+        dosFilter.setInitParameter("remotePort", "false");
+        dosFilter.setInitParameter("insertHeaders", "true");
 
-        _timeoutFilter = _tester.getContext().addFilter(filter, "/timeout/*", EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-        _timeoutFilter.setInitParameter("maxRequestsPerSec", "4");
-        _timeoutFilter.setInitParameter("delayMs", "200");
-        _timeoutFilter.setInitParameter("throttledRequests", "1");
-        _timeoutFilter.setInitParameter("waitMs", "10");
-        _timeoutFilter.setInitParameter("throttleMs", "4000");
-        _timeoutFilter.setInitParameter("remotePort", "false");
-        _timeoutFilter.setInitParameter("insertHeaders", "true");
-        _timeoutFilter.setInitParameter("maxRequestMs", _requestMaxTime + "");
+        FilterHolder timeoutFilter = _tester.getContext().addFilter(filter, "/timeout/*", EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC));
+        timeoutFilter.setInitParameter("maxRequestsPerSec", "4");
+        timeoutFilter.setInitParameter("delayMs", "200");
+        timeoutFilter.setInitParameter("throttledRequests", "1");
+        timeoutFilter.setInitParameter("waitMs", "10");
+        timeoutFilter.setInitParameter("throttleMs", "4000");
+        timeoutFilter.setInitParameter("remotePort", "false");
+        timeoutFilter.setInitParameter("insertHeaders", "true");
+        timeoutFilter.setInitParameter("maxRequestMs", _requestMaxTime + "");
 
         _tester.start();
     }
 
-    @AfterClass
-    public static void stopServer() throws Exception
+    @After
+    public void stopServer() throws Exception
     {
         _tester.stop();
     }
 
-    @Before
-    public void startFilters() throws Exception
-    {
-        _dosFilter.start();
-        _dosFilter.initialize();
-        _timeoutFilter.start();
-        _timeoutFilter.initialize();
-    }
-
-    @After
-    public void stopFilters() throws Exception
-    {
-        _timeoutFilter.stop();
-        _dosFilter.stop();
-    }
-
-    private String doRequests(String loopRequests, int loops, long pauseBetweenLoops, long pauseBeforeLast, String lastRequest) throws Exception
+    protected String doRequests(String loopRequests, int loops, long pauseBetweenLoops, long pauseBeforeLast, String lastRequest) throws Exception
     {
         try (Socket socket = new Socket(_host,_port))
         {
@@ -143,8 +116,7 @@
                 // don't read in anything, forcing the request to time out
                 Thread.sleep(_requestMaxTime * 2);
             }
-            String response = IO.toString(in,StandardCharsets.UTF_8);
-            return response;
+            return IO.toString(in,StandardCharsets.UTF_8);
         }
     }
 
@@ -167,8 +139,8 @@
         String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
         String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
         String responses = doRequests(request,11,300,300,last);
-        assertEquals(12,count(responses,"HTTP/1.1 200 OK"));
-        assertEquals(0,count(responses,"DoSFilter:"));
+        Assert.assertEquals(12,count(responses,"HTTP/1.1 200 OK"));
+        Assert.assertEquals(0,count(responses,"DoSFilter:"));
     }
 
     @Test
@@ -178,8 +150,8 @@
         String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
         String responses = doRequests(request+request+request+request,2,1100,1100,last);
 
-        assertEquals(9,count(responses,"HTTP/1.1 200 OK"));
-        assertEquals(0,count(responses,"DoSFilter:"));
+        Assert.assertEquals(9,count(responses,"HTTP/1.1 200 OK"));
+        Assert.assertEquals(0,count(responses,"DoSFilter:"));
     }
 
     @Test
@@ -189,8 +161,8 @@
         String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
         String responses = doRequests(request+request+request+request+request,2,1100,1100,last);
 
-        assertEquals(2,count(responses,"DoSFilter: delayed"));
-        assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
+        Assert.assertEquals(2,count(responses,"DoSFilter: delayed"));
+        Assert.assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
     }
 
     @Test
@@ -206,7 +178,7 @@
                     // Cause a delay, then sleep while holding pass
                     String request="GET /ctx/dos/sleeper HTTP/1.1\r\nHost: localhost\r\n\r\n";
                     String last="GET /ctx/dos/sleeper?sleep=2000 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
-                    String responses = doRequests(request+request+request+request,1,0,0,last);
+                    doRequests(request+request+request+request,1,0,0,last);
                 }
                 catch(Exception e)
                 {
@@ -221,10 +193,10 @@
         String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
         String responses = doRequests(request+request+request+request,1,0,0,last);
         // System.out.println("responses are " + responses);
-        assertEquals("200 OK responses", 5,count(responses,"HTTP/1.1 200 OK"));
-        assertEquals("delayed responses", 1,count(responses,"DoSFilter: delayed"));
-        assertEquals("throttled responses", 1,count(responses,"DoSFilter: throttled"));
-        assertEquals("unavailable responses", 0,count(responses,"DoSFilter: unavailable"));
+        Assert.assertEquals("200 OK responses", 5,count(responses,"HTTP/1.1 200 OK"));
+        Assert.assertEquals("delayed responses", 1,count(responses,"DoSFilter: delayed"));
+        Assert.assertEquals("throttled responses", 1,count(responses,"DoSFilter: throttled"));
+        Assert.assertEquals("unavailable responses", 0,count(responses,"DoSFilter: unavailable"));
 
         other.join();
     }
@@ -242,7 +214,7 @@
                     // Cause a delay, then sleep while holding pass
                     String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
                     String last="GET /ctx/dos/test?sleep=5000 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
-                    String responses = doRequests(request+request+request+request,1,0,0,last);
+                    doRequests(request+request+request+request,1,0,0,last);
                 }
                 catch(Exception e)
                 {
@@ -259,11 +231,11 @@
 
         // System.err.println("RESPONSES: \n"+responses);
 
-        assertEquals(4,count(responses,"HTTP/1.1 200 OK"));
-        assertEquals(1,count(responses,"HTTP/1.1 503"));
-        assertEquals(1,count(responses,"DoSFilter: delayed"));
-        assertEquals(1,count(responses,"DoSFilter: throttled"));
-        assertEquals(1,count(responses,"DoSFilter: unavailable"));
+        Assert.assertEquals(4,count(responses,"HTTP/1.1 200 OK"));
+        Assert.assertEquals(1,count(responses,"HTTP/1.1 429"));
+        Assert.assertEquals(1,count(responses,"DoSFilter: delayed"));
+        Assert.assertEquals(1,count(responses,"DoSFilter: throttled"));
+        Assert.assertEquals(1,count(responses,"DoSFilter: unavailable"));
 
         other.join();
     }
@@ -281,8 +253,8 @@
         String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\nCookie: " + sessionId + "\r\n\r\n";
         String responses = doRequests(request+request+request+request+request,2,1100,1100,last);
 
-        assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
-        assertEquals(2,count(responses,"DoSFilter: delayed"));
+        Assert.assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
+        Assert.assertEquals(2,count(responses,"DoSFilter: delayed"));
     }
 
     @Test
@@ -304,21 +276,21 @@
         String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\nCookie: " + sessionId2 + "\r\n\r\n";
 
         // ensure the sessions are new
-        String responses = doRequests(request1+request2,1,1100,1100,last);
+        doRequests(request1+request2,1,1100,1100,last);
         Thread.sleep(1000);
 
-        responses = doRequests(request1+request2+request1+request2+request1,2,1100,1100,last);
+        String responses = doRequests(request1+request2+request1+request2+request1,2,1100,1100,last);
 
-        assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
-        assertEquals(0,count(responses,"DoSFilter: delayed"));
+        Assert.assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
+        Assert.assertEquals(0,count(responses,"DoSFilter: delayed"));
 
         // alternate between sessions
         responses = doRequests(request1+request2+request1+request2+request1,2,250,250,last);
 
         // System.err.println(responses);
-        assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
+        Assert.assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
         int delayedRequests = count(responses,"DoSFilter: delayed");
-        assertTrue("delayedRequests: " + delayedRequests + " is not between 2 and 5",delayedRequests >= 2 && delayedRequests <= 5);
+        Assert.assertTrue("delayedRequests: " + delayedRequests + " is not between 2 and 5",delayedRequests >= 2 && delayedRequests <= 5);
     }
 
     @Test
@@ -330,9 +302,8 @@
         String responses = doRequests("",0,0,0,last);
         // was expired, and stopped before reaching the end of the requests
         int responseLines = count(responses, "Line:");
-        assertTrue(responses.contains("DoSFilter: timeout"));
-        assertThat(responseLines,greaterThan(0));
-        assertThat(responseLines,Matchers.lessThan(numRequests));
+        Assert.assertThat(responseLines,Matchers.greaterThan(0));
+        Assert.assertThat(responseLines,Matchers.lessThan(numRequests));
     }
 
     public static class TestServlet extends HttpServlet implements Servlet
@@ -358,7 +329,7 @@
                 int count = Integer.parseInt(request.getParameter("lines"));
                 for(int i = 0; i < count; ++i)
                 {
-                    response.getWriter().append("Line: " + i+"\n");
+                    response.getWriter().append("Line: " + i + "\n");
                     response.flushBuffer();
 
                     try
@@ -368,7 +339,6 @@
                     catch(InterruptedException e)
                     {
                     }
-
                 }
             }
 
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CloseableDoSFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CloseableDoSFilterTest.java
index 6b0a488..f572bb7 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CloseableDoSFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CloseableDoSFilterTest.java
@@ -18,45 +18,13 @@
 
 package org.eclipse.jetty.servlets;
 
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.junit.BeforeClass;
+import org.junit.Before;
 
 public class CloseableDoSFilterTest extends AbstractDoSFilterTest
 {
-    private static final Logger LOG = Log.getLogger(CloseableDoSFilterTest.class);
-
-    @BeforeClass
-    public static void setUp() throws Exception
+    @Before
+    public void setUp() throws Exception
     {
-        startServer(CloseableDoSFilter2.class);
-    }
-
-    public static class CloseableDoSFilter2 extends CloseableDoSFilter
-    {
-        @Override
-        public void closeConnection(HttpServletRequest request, HttpServletResponse response, Thread thread)
-        {
-            try
-            {
-                response.getWriter().append("DoSFilter: timeout");
-                response.flushBuffer();
-                super.closeConnection(request, response, thread);
-            }
-            catch (Exception e)
-            {
-                LOG.warn(e);
-            }
-        }
-    }
-
-    public void testUnresponsiveClient() throws Exception
-    {
-        // TODO work out why this intermittently fails
-        LOG.warn("Ignored Closeable testUnresponsiveClient");
+        startServer(CloseableDoSFilter.class);
     }
 }
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ConcatServletTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ConcatServletTest.java
index f443131..e69bcf7 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ConcatServletTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ConcatServletTest.java
@@ -27,6 +27,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 
+import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -76,7 +77,7 @@
             @Override
             protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
             {
-                String includedURI = (String)request.getAttribute("javax.servlet.include.request_uri");
+                String includedURI = (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
                 response.getOutputStream().println(includedURI);
             }
         });
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java
index 620885a..ee92478 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java
@@ -173,7 +173,7 @@
     }
 
     @Test
-    public void testSimpleRequestWithMatchingOrigin() throws Exception
+    public void testSimpleRequestWithMatchingOriginAndWithoutTimingOrigin() throws Exception
     {
         FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
         String origin = "http://localhost";
@@ -193,11 +193,67 @@
         Assert.assertTrue(response.contains("HTTP/1.1 200"));
         Assert.assertTrue(response.contains(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER));
         Assert.assertTrue(response.contains(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER));
+        Assert.assertFalse(response.contains(CrossOriginFilter.TIMING_ALLOW_ORIGIN_HEADER));
         Assert.assertTrue(response.contains("Vary"));
         Assert.assertTrue(latch.await(1, TimeUnit.SECONDS));
     }
 
     @Test
+    public void testSimpleRequestWithMatchingOriginAndNonMatchingTimingOrigin() throws Exception
+    {
+    	FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
+    	String origin = "http://localhost";
+    	String timingOrigin = "http://127.0.0.1";
+    	filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, origin);
+    	filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_TIMING_ORIGINS_PARAM, timingOrigin);
+    	tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
+    	
+    	CountDownLatch latch = new CountDownLatch(1);
+    	tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
+    	
+    	String request = "" +
+    			"GET / HTTP/1.1\r\n" +
+    			"Host: localhost\r\n" +
+    			"Connection: close\r\n" +
+    			"Origin: " + origin + "\r\n" +
+    			"\r\n";
+    	String response = tester.getResponses(request);
+    	Assert.assertTrue(response.contains("HTTP/1.1 200"));
+    	Assert.assertTrue(response.contains(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER));
+    	Assert.assertTrue(response.contains(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER));
+        Assert.assertFalse(response.contains(CrossOriginFilter.TIMING_ALLOW_ORIGIN_HEADER));
+    	Assert.assertTrue(response.contains("Vary"));
+    	Assert.assertTrue(latch.await(1, TimeUnit.SECONDS));
+    }
+    
+    @Test
+    public void testSimpleRequestWithMatchingOriginAndMatchingTimingOrigin() throws Exception
+    {
+    	FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
+    	String origin = "http://localhost";
+    	filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, origin);
+    	filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_TIMING_ORIGINS_PARAM, origin);
+    	tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
+    	
+    	CountDownLatch latch = new CountDownLatch(1);
+    	tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
+    	
+    	String request = "" +
+    			"GET / HTTP/1.1\r\n" +
+    			"Host: localhost\r\n" +
+    			"Connection: close\r\n" +
+    			"Origin: " + origin + "\r\n" +
+    			"\r\n";
+    	String response = tester.getResponses(request);
+    	Assert.assertTrue(response.contains("HTTP/1.1 200"));
+    	Assert.assertTrue(response.contains(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER));
+    	Assert.assertTrue(response.contains(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER));
+    	Assert.assertTrue(response.contains(CrossOriginFilter.TIMING_ALLOW_ORIGIN_HEADER));
+    	Assert.assertTrue(response.contains("Vary"));
+    	Assert.assertTrue(latch.await(1, TimeUnit.SECONDS));
+    }
+    
+    @Test
     public void testSimpleRequestWithMatchingMultipleOrigins() throws Exception
     {
         FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
@@ -455,7 +511,7 @@
                 "Upgrade: WebSocket\r\n" +
                 "Origin: http://localhost\r\n" +
                 "\r\n";
-        String response = tester.getResponses(request);
+        String response = tester.getResponses(request,1,TimeUnit.SECONDS);
         Assert.assertTrue(response.contains("HTTP/1.1 200"));
         Assert.assertFalse(response.contains(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER));
         Assert.assertFalse(response.contains(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER));
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DataRateLimitedServletTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DataRateLimitedServletTest.java
index 4651e1d..c63c84b 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DataRateLimitedServletTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DataRateLimitedServletTest.java
@@ -25,6 +25,8 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
 import java.util.Arrays;
 
 import org.eclipse.jetty.server.HttpConfiguration;
@@ -63,7 +65,12 @@
  
         context.setContextPath("/context");
         context.setWelcomeFiles(new String[]{"index.html", "index.jsp", "index.htm"});
-        context.setBaseResource(Resource.newResource(testdir.getEmptyDir()));
+        
+        File baseResourceDir = testdir.getEmptyPathDir().toFile();
+        // Use resolved real path for Windows and OSX
+        Path baseResourcePath = baseResourceDir.toPath().toRealPath();
+        
+        context.setBaseResource(Resource.newResource(baseResourcePath.toFile()));
         
         ServletHolder holder =context.addServlet(DataRateLimitedServlet.class,"/stream/*");
         holder.setInitParameter("buffersize",""+BUFFER);
@@ -84,16 +91,20 @@
     @Test
     public void testStream() throws Exception
     {
-        File content = testdir.getFile("content.txt");
+        File content = testdir.getPathFile("content.txt").toFile();
+        String[] results=new String[10];
         try(OutputStream out = new FileOutputStream(content);)
         {
             byte[] b= new byte[1024];
             
             for (int i=1024;i-->0;)
             {
-                Arrays.fill(b,(byte)('0'+(i%10)));
+                int index=i%10;
+                Arrays.fill(b,(byte)('0'+(index)));
                 out.write(b);
                 out.write('\n');
+                if (results[index]==null)
+                    results[index]=new String(b,StandardCharsets.US_ASCII);
             }
         }
         
@@ -101,9 +112,11 @@
         String response = connector.getResponses("GET /context/stream/content.txt HTTP/1.0\r\n\r\n");
         long duration=System.currentTimeMillis()-start;
         
-        assertThat(response.length(),greaterThan(1024*1024));
-        assertThat(response,containsString("200 OK"));
-        assertThat(duration,greaterThan(PAUSE*1024L*1024/BUFFER));
+        assertThat("Response",response,containsString("200 OK"));
+        assertThat("Response Length",response.length(),greaterThan(1024*1024));
+        assertThat("Duration",duration,greaterThan(PAUSE*1024L*1024/BUFFER));
         
+        for (int i=0;i<10;i++)
+            assertThat(response,containsString(results[i]));
     }
 }
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java
index 0e99b96..72bec40 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java
@@ -18,46 +18,18 @@
 
 package org.eclipse.jetty.servlets;
 
-import java.util.ArrayList;
-import java.util.List;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
 import org.eclipse.jetty.servlets.DoSFilter.RateTracker;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
+import org.hamcrest.Matchers;
 import org.junit.Assert;
-import org.junit.BeforeClass;
+import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 public class DoSFilterTest extends AbstractDoSFilterTest
 {
-    private static final Logger LOG = Log.getLogger(DoSFilterTest.class);
-
-    @BeforeClass
-    public static void setUp() throws Exception
+    @Before
+    public void setUp() throws Exception
     {
-        startServer(DoSFilter2.class);
-    }
-
-    public static class DoSFilter2 extends DoSFilter
-    {
-        @Override
-        public void closeConnection(HttpServletRequest request, HttpServletResponse response, Thread thread)
-        {
-            try
-            {
-                response.getWriter().append("DoSFilter: timeout");
-                super.closeConnection(request,response,thread);
-            }
-            catch (Exception e)
-            {
-                LOG.warn(e);
-            }
-        }
+        startServer(DoSFilter.class);
     }
 
     @Test
@@ -66,22 +38,17 @@
         DoSFilter doSFilter = new DoSFilter();
 
         boolean exceeded = hitRateTracker(doSFilter,0);
-        assertTrue("Last hit should have exceeded",exceeded);
+        Assert.assertTrue("Last hit should have exceeded",exceeded);
 
         int sleep = 250;
         exceeded = hitRateTracker(doSFilter,sleep);
-        assertFalse("Should not exceed as we sleep 300s for each hit and thus do less than 4 hits/s",exceeded);
+        Assert.assertFalse("Should not exceed as we sleep 300s for each hit and thus do less than 4 hits/s",exceeded);
     }
 
     @Test
     public void testWhitelist() throws Exception
     {
         DoSFilter filter = new DoSFilter();
-        List<String> whitelist = new ArrayList<String>();
-        whitelist.add("192.168.0.1/32");
-        whitelist.add("10.0.0.0/8");
-        whitelist.add("4d8:0:a:1234:ABc:1F:b18:17");
-        whitelist.add("4d8:0:a:1234:ABc:1F:0:0/96");
         filter.setWhitelist("192.168.0.1/32,10.0.0.0/8,4d8:0:a:1234:ABc:1F:b18:17,4d8:0:a:1234:ABc:1F:0:0/96");
         Assert.assertTrue(filter.checkWhitelist("192.168.0.1"));
         Assert.assertFalse(filter.checkWhitelist("192.168.0.2"));
@@ -94,6 +61,14 @@
         Assert.assertFalse(filter.checkWhitelist("4d8:0:a:1234:ABc:1D:0:0"));
     }
 
+    @Test
+    public void testUnresponsiveServer() throws Exception
+    {
+        String last="GET /ctx/timeout/?sleep="+2*_requestMaxTime+" HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
+        String responses = doRequests("",0,0,0,last);
+        Assert.assertThat(responses, Matchers.containsString(" 503 "));
+    }
+
     private boolean hitRateTracker(DoSFilter doSFilter, int sleep) throws InterruptedException
     {
         boolean exceeded = false;
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java
deleted file mode 100644
index 632a31c..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java
+++ /dev/null
@@ -1,352 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import javax.servlet.DispatcherType;
-
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.HttpTester;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlets.gzip.AsyncScheduledDispatchWrite;
-import org.eclipse.jetty.servlets.gzip.AsyncTimeoutDispatchWrite;
-import org.eclipse.jetty.servlets.gzip.AsyncTimeoutCompleteWrite;
-import org.eclipse.jetty.servlets.gzip.GzipTester;
-import org.eclipse.jetty.servlets.gzip.GzipTester.ContentMetadata;
-import org.eclipse.jetty.servlets.gzip.TestDirContentServlet;
-import org.eclipse.jetty.servlets.gzip.TestServletBufferTypeLengthWrite;
-import org.eclipse.jetty.servlets.gzip.TestServletLengthStreamTypeWrite;
-import org.eclipse.jetty.servlets.gzip.TestServletLengthTypeStreamWrite;
-import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWrite;
-import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWriteWithFlush;
-import org.eclipse.jetty.servlets.gzip.TestServletStreamTypeLengthWrite;
-import org.eclipse.jetty.servlets.gzip.TestServletTypeLengthStreamWrite;
-import org.eclipse.jetty.servlets.gzip.TestServletTypeStreamLengthWrite;
-import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.toolchain.test.TestingDir;
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-/**
- * Test the GzipFilter support for Content-Length setting variations.
- *
- * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
- */
-@RunWith(Parameterized.class)
-public class GzipFilterContentLengthTest
-{
-    @Rule
-    public final TestTracker tracker = new TestTracker();
-
-    @Rule
-    public TestingDir testingdir = new TestingDir();
-    
-    private static final HttpConfiguration defaultHttp = new HttpConfiguration();
-    private static final int LARGE = defaultHttp.getOutputBufferSize() * 8;
-    private static final int MEDIUM = defaultHttp.getOutputBufferSize();
-    private static final int SMALL = defaultHttp.getOutputBufferSize() / 4;
-    private static final int TINY = AsyncGzipFilter.DEFAULT_MIN_GZIP_SIZE / 2;
-    private static final boolean EXPECT_COMPRESSED = true;
-
-    @Parameters(name = "{0} bytes - {1} - compressed({2}) - type({3}) - filter({4})")
-    public static List<Object[]> data()
-    {
-        List<Object[]> ret = new ArrayList<Object[]>();
-        
-        String compressionTypes[] = new String[] { GzipFilter.GZIP, GzipFilter.DEFLATE };
-        Class<?> gzipFilters[] = new Class<?>[] { GzipFilter.class, AsyncGzipFilter.class };
-        
-        for(String compressionType: compressionTypes)
-        {
-            for(Class<?> gzipFilter: gzipFilters)
-            {
-                ret.add(new Object[] { 0, "empty.txt", !EXPECT_COMPRESSED, compressionType, gzipFilter });
-                ret.add(new Object[] { TINY, "file-tiny.txt", !EXPECT_COMPRESSED, compressionType, gzipFilter });
-                ret.add(new Object[] { SMALL, "file-small.txt", EXPECT_COMPRESSED, compressionType, gzipFilter });
-                ret.add(new Object[] { SMALL, "file-small.mp3", !EXPECT_COMPRESSED, compressionType, gzipFilter });
-                ret.add(new Object[] { MEDIUM, "file-med.txt", EXPECT_COMPRESSED, compressionType, gzipFilter });
-                ret.add(new Object[] { MEDIUM, "file-medium.mp3", !EXPECT_COMPRESSED, compressionType, gzipFilter });
-                ret.add(new Object[] { LARGE, "file-large.txt", EXPECT_COMPRESSED, compressionType, gzipFilter });
-                ret.add(new Object[] { LARGE, "file-large.mp3", !EXPECT_COMPRESSED, compressionType, gzipFilter });
-            }
-        }
-
-        return ret;
-    }
-
-    @Parameter(0)
-    public int fileSize;
-    @Parameter(1)
-    public String fileName;
-    @Parameter(2)
-    public boolean expectCompressed;
-    @Parameter(3)
-    public String compressionType;
-    @Parameter(4)
-    public Class<? extends GzipFilter> gzipFilterClass;
-    
-    private void testWithGzip(Class<? extends TestDirContentServlet> contentServlet) throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir, GzipFilter.GZIP);
-        
-        // Add AsyncGzip Filter
-        FilterHolder gzipHolder = new FilterHolder(gzipFilterClass);
-        gzipHolder.setAsyncSupported(true);
-        tester.addFilter(gzipHolder,"*.txt",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-        tester.addFilter(gzipHolder,"*.mp3",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-        gzipHolder.setInitParameter("mimeTypes","text/plain");
-
-        // Add content servlet
-        tester.setContentServlet(contentServlet);
-        
-        try
-        {
-            String testFilename = String.format("%s-%s-%s", gzipFilterClass.getSimpleName(), contentServlet.getSimpleName(), fileName);
-            File testFile = tester.prepareServerFile(testFilename,fileSize);
-            
-            tester.start();
-            
-            HttpTester.Response response = tester.executeRequest("GET","/context/" + testFile.getName(),5,TimeUnit.SECONDS);
-            
-            if (response.getStatus()!=200)
-                System.err.println("DANG!!!! "+response);
-            
-            assertThat("Response status", response.getStatus(), is(HttpStatus.OK_200));
-            
-            if (expectCompressed)
-            {
-                // Must be gzip compressed
-                assertThat("Content-Encoding",response.get("Content-Encoding"),containsString(GzipFilter.GZIP));
-            } else
-            {
-                assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(GzipFilter.GZIP)));
-            }
-            
-            // Uncompressed content Size
-            ContentMetadata content = tester.getResponseMetadata(response);
-            assertThat("(Uncompressed) Content Length", content.size, is((long)fileSize));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    /**
-     * Test with content servlet that does:  
-     * AsyncContext create -> timeout -> onTimeout -> write-response -> complete
-     */
-    @Test
-    public void testAsyncTimeoutCompleteWrite_Default() throws Exception
-    {
-        if (expectCompressed && gzipFilterClass==GzipFilter.class)
-            return; // Default startAsync will never work with GzipFilter, which needs wrapping
-        testWithGzip(AsyncTimeoutCompleteWrite.Default.class);
-    }
-    
-    /**
-     * Test with content servlet that does:  
-     * AsyncContext create -> timeout -> onTimeout -> write-response -> complete
-     */
-    @Test
-    public void testAsyncTimeoutCompleteWrite_Passed() throws Exception
-    {
-        testWithGzip(AsyncTimeoutCompleteWrite.Passed.class);
-    }
-    
-    /**
-     * Test with content servlet that does:  
-     * AsyncContext create -> timeout -> onTimeout -> dispatch -> write-response
-     */
-    @Test
-    public void testAsyncTimeoutDispatchWrite_Default() throws Exception
-    {
-        testWithGzip(AsyncTimeoutDispatchWrite.Default.class);
-    }
-    
-    /**
-     * Test with content servlet that does:  
-     * AsyncContext create -> timeout -> onTimeout -> dispatch -> write-response
-     */
-    @Test
-    public void testAsyncTimeoutDispatchWrite_Passed() throws Exception
-    {
-        testWithGzip(AsyncTimeoutDispatchWrite.Passed.class);
-    }
-
-    /**
-     * Test with content servlet that does:  
-     * AsyncContext create -> no-timeout -> scheduler.schedule -> dispatch -> write-response
-     */
-    @Test
-    public void testAsyncScheduledDispatchWrite_Default() throws Exception
-    {
-        testWithGzip(AsyncScheduledDispatchWrite.Default.class);
-    }
-    
-    /**
-     * Test with content servlet that does:  
-     * AsyncContext create -> no-timeout -> scheduler.schedule -> dispatch -> write-response
-     */
-    @Test
-    public void testAsyncScheduledDispatchWrite_Passed() throws Exception
-    {
-        testWithGzip(AsyncScheduledDispatchWrite.Passed.class);
-    }
-
-    /**
-     * Test with content servlet that does:  
-     * 1) setHeader(content-length)
-     * 2) getOutputStream()
-     * 3) setHeader(content-type)
-     * 4) outputStream.write()
-     * 
-     * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
-     */
-    @Test
-    public void testServletLengthStreamTypeWrite() throws Exception
-    {
-        testWithGzip(TestServletLengthStreamTypeWrite.class);
-    }
-
-    /**
-     * Test with content servlet that does:  
-     * 1) setHeader(content-length)
-     * 2) setHeader(content-type)
-     * 3) getOutputStream()
-     * 4) outputStream.write()
-     * 
-     * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
-     */
-    @Test
-    public void testServletLengthTypeStreamWrite() throws Exception
-    {
-        testWithGzip(TestServletLengthTypeStreamWrite.class);
-    }
-
-    /**
-     * Test with content servlet that does:  
-     * 1) getOutputStream()
-     * 2) setHeader(content-length)
-     * 3) setHeader(content-type)
-     * 4) outputStream.write()
-     * 
-     * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
-     */
-    @Test
-    public void testServletStreamLengthTypeWrite() throws Exception
-    {
-        testWithGzip(TestServletStreamLengthTypeWrite.class);
-    }
-
-    /**
-     * Test with content servlet that does:  
-     * 1) getOutputStream()
-     * 2) setHeader(content-length)
-     * 3) setHeader(content-type)
-     * 4) outputStream.write() (with frequent response flush)
-     * 
-     * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
-     */
-    @Test
-    public void testServletStreamLengthTypeWriteWithFlush() throws Exception
-    {
-        testWithGzip(TestServletStreamLengthTypeWriteWithFlush.class);
-    }
-
-    /**
-     * Test with content servlet that does:  
-     * 1) getOutputStream()
-     * 2) setHeader(content-type)
-     * 3) setHeader(content-length)
-     * 4) outputStream.write()
-     * 
-     * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
-     */
-    @Test
-    public void testServletStreamTypeLengthWrite() throws Exception
-    {
-        testWithGzip(TestServletStreamTypeLengthWrite.class);
-    }
-
-    /**
-     * Test with content servlet that does:  
-     * 1) setHeader(content-type)
-     * 2) setHeader(content-length)
-     * 3) getOutputStream()
-     * 4) outputStream.write()
-     * 
-     * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
-     */
-    @Test
-    public void testServletTypeLengthStreamWrite() throws Exception
-    {
-        testWithGzip(TestServletTypeLengthStreamWrite.class);
-    }
-
-    /**
-     * Test with content servlet that does:  
-     * 1) setHeader(content-type)
-     * 2) getOutputStream()
-     * 3) setHeader(content-length)
-     * 4) outputStream.write()
-     * 
-     * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
-     */
-    @Test
-    public void testServletTypeStreamLengthWrite() throws Exception
-    {
-        testWithGzip(TestServletTypeStreamLengthWrite.class);
-    }
-
-    /**
-     * Test with content servlet that does:  
-     * 2) getOutputStream()
-     * 1) setHeader(content-type)
-     * 3) setHeader(content-length)
-     * 4) (unwrapped) HttpOutput.write(ByteBuffer)
-     * 
-     * This is done to demonstrate a bug with using HttpOutput.write()
-     * while also using GzipFilter
-     * 
-     * @see <a href="Eclipse Bug 450873">http://bugs.eclipse.org/450873</a>
-     */
-    @Test
-    public void testHttpOutputWrite() throws Exception
-    {
-        if (gzipFilterClass == GzipFilter.class)
-            return;  // Can't downcaste output stream when wrapper is used
-        testWithGzip(TestServletBufferTypeLengthWrite.class);
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java
deleted file mode 100644
index 2080f00..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-
-import javax.servlet.Filter;
-
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlets.gzip.GzipTester;
-import org.eclipse.jetty.servlets.gzip.TestStaticMimeTypeServlet;
-import org.eclipse.jetty.toolchain.test.IO;
-import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
-import org.eclipse.jetty.toolchain.test.TestingDir;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-/**
- * Tests {@link GzipFilter} in combination with {@link DefaultServlet} for ability to configure {@link GzipFilter} to
- * ignore recompress situations from upstream.
- */
-@RunWith(Parameterized.class)
-public class GzipFilterDefaultNoRecompressTest
-{
-    @Parameters
-    public static List<Object[]> data()
-    {
-        return Arrays.asList(new Object[][]
-        {
-                // Some already compressed files
-                { GzipFilter.class, "test_quotes.gz", "application/gzip", GzipFilter.GZIP },
-                { GzipFilter.class, "test_quotes.bz2", "application/bzip2", GzipFilter.GZIP },
-                { GzipFilter.class, "test_quotes.zip", "application/zip", GzipFilter.GZIP },
-                { GzipFilter.class, "test_quotes.rar", "application/octet-stream", GzipFilter.GZIP },
-                // Some images (common first)
-                { GzipFilter.class, "jetty_logo.png", "image/png", GzipFilter.GZIP },
-                { GzipFilter.class, "jetty_logo.gif", "image/gif", GzipFilter.GZIP },
-                { GzipFilter.class, "jetty_logo.jpeg", "image/jpeg", GzipFilter.GZIP },
-                { GzipFilter.class, "jetty_logo.jpg", "image/jpeg", GzipFilter.GZIP },
-                // Lesser encountered images (usually found being requested from non-browser clients)
-                { GzipFilter.class, "jetty_logo.bmp", "image/bmp", GzipFilter.GZIP },
-                { GzipFilter.class, "jetty_logo.tga", "application/tga", GzipFilter.GZIP },
-                { GzipFilter.class, "jetty_logo.tif", "image/tiff", GzipFilter.GZIP },
-                { GzipFilter.class, "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
-                { GzipFilter.class, "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
-                { GzipFilter.class, "jetty_logo.jp2", "image/jpeg2000", GzipFilter.GZIP },
-                //qvalue disables compression
-                { GzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+";q=0"},
-                { GzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+"; q =    0 "},
-                
-                
-                // Some already compressed files
-                { AsyncGzipFilter.class, "test_quotes.gz", "application/gzip", GzipFilter.GZIP },
-                { AsyncGzipFilter.class, "test_quotes.bz2", "application/bzip2", GzipFilter.GZIP },
-                { AsyncGzipFilter.class, "test_quotes.zip", "application/zip", GzipFilter.GZIP },
-                { AsyncGzipFilter.class, "test_quotes.rar", "application/octet-stream", GzipFilter.GZIP },
-                // Some images (common first)
-                { AsyncGzipFilter.class, "jetty_logo.png", "image/png", GzipFilter.GZIP },
-                { AsyncGzipFilter.class, "jetty_logo.gif", "image/gif", GzipFilter.GZIP },
-                { AsyncGzipFilter.class, "jetty_logo.jpeg", "image/jpeg", GzipFilter.GZIP },
-                { AsyncGzipFilter.class, "jetty_logo.jpg", "image/jpeg", GzipFilter.GZIP },
-                // Lesser encountered images (usually found being requested from non-browser clients)
-                { AsyncGzipFilter.class, "jetty_logo.bmp", "image/bmp", GzipFilter.GZIP },
-                { AsyncGzipFilter.class, "jetty_logo.tga", "application/tga", GzipFilter.GZIP },
-                { AsyncGzipFilter.class, "jetty_logo.tif", "image/tiff", GzipFilter.GZIP },
-                { AsyncGzipFilter.class, "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
-                { AsyncGzipFilter.class, "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
-                { AsyncGzipFilter.class, "jetty_logo.jp2", "image/jpeg2000", GzipFilter.GZIP },
-                //qvalue disables compression
-                { AsyncGzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+";q=0"},
-                { AsyncGzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+"; q =    0 "}
-        });
-    }
-
-    @Rule
-    public TestingDir testingdir = new TestingDir();
-
-    private Class<? extends Filter> testFilter;
-    private String alreadyCompressedFilename;
-    private String expectedContentType;
-    private String compressionType;
-
-    public GzipFilterDefaultNoRecompressTest(Class<? extends Filter> testFilter,String testFilename, String expectedContentType, String compressionType)
-    {
-        this.testFilter = testFilter;
-        this.alreadyCompressedFilename = testFilename;
-        this.expectedContentType = expectedContentType;
-        this.compressionType = compressionType;
-    }
-
-    @Test
-    public void testNotGzipFiltered_Default_AlreadyCompressed() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir, compressionType);
-        tester.setGzipFilterClass(testFilter);
-
-        copyTestFileToServer(alreadyCompressedFilename);
-
-        FilterHolder holder = tester.setContentServlet(TestStaticMimeTypeServlet.class);
-        StringBuilder mimeTypes = new StringBuilder();
-        mimeTypes.append("text/plain");
-        holder.setInitParameter("mimeTypes",mimeTypes.toString());
-
-        try
-        {
-            tester.start();
-            tester.assertIsResponseNotGzipFiltered(alreadyCompressedFilename,alreadyCompressedFilename + ".sha1",expectedContentType);
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    private void copyTestFileToServer(String testFilename) throws IOException
-    {
-        File testFile = MavenTestingUtils.getTestResourceFile(testFilename);
-        File outFile = testingdir.getFile(testFilename);
-        IO.copy(testFile,outFile);
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java
deleted file mode 100644
index 77afc98..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java
+++ /dev/null
@@ -1,794 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import javax.servlet.DispatcherType;
-import javax.servlet.Filter;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.HttpTester;
-import org.eclipse.jetty.servlet.DefaultServlet;
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlets.gzip.GzipTester;
-import org.eclipse.jetty.toolchain.test.IO;
-import org.eclipse.jetty.toolchain.test.TestingDir;
-import org.eclipse.jetty.util.StringUtil;
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-/**
- * Test the GzipFilter support built into the {@link DefaultServlet}
- */
-@RunWith(Parameterized.class)
-public class GzipFilterDefaultTest
-{
-    @Parameters(name="{1} - {0}")
-    public static List<Object[]> data()
-    {
-        return Arrays.asList(new Object[][]
-        { 
-            { AsyncGzipFilter.class, GzipFilter.GZIP },
-            { GzipFilter.class, GzipFilter.GZIP },
-            { GzipFilter.class, GzipFilter.DEFLATE },
-        });
-    }
-
-    private Class<? extends Filter> testFilter;
-    private String compressionType;
-
-    public GzipFilterDefaultTest(Class<? extends Filter> testFilter, String compressionType)
-    {
-        this.testFilter = testFilter;
-        this.compressionType = compressionType;
-    }
-
-    @SuppressWarnings("serial")
-    public static class HttpStatusServlet extends HttpServlet
-    {
-        private int _status = 204;
-
-        public HttpStatusServlet()
-        {
-            super();
-        }
-
-        @Override
-        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
-        {
-            resp.setStatus(_status);
-            resp.setHeader("ETag","W/\"204\"");
-        }
-
-    }
-
-    @SuppressWarnings("serial")
-    public static class HttpErrorServlet extends HttpServlet
-    {
-        private int _status = 400;
-
-        public HttpErrorServlet()
-        {
-            super();
-        }
-
-        @Override
-        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
-        {
-            resp.getOutputStream().write("error message".getBytes());
-            resp.setStatus(_status);
-        }
-    }
-
-    @SuppressWarnings("serial")
-    public static class HttpContentTypeWithEncoding extends HttpServlet
-    {
-        public static final String COMPRESSED_CONTENT = "<html><head></head><body><h1>COMPRESSABLE CONTENT</h1>"
-                + "This content must be longer than the default min gzip length, which is 256 bytes. "
-                + "The moon is blue to a fish in love. How now brown cow. The quick brown fox jumped over the lazy dog. A woman needs a man like a fish needs a bicycle!"
-                + "</body></html>";
-
-        @Override
-        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
-        {
-            resp.setContentType("text/plain;charset=UTF8");
-            resp.setStatus(200);
-            ServletOutputStream out = resp.getOutputStream();
-            out.print(COMPRESSED_CONTENT);
-        }
-
-    }
-
-    @Rule
-    public TestingDir testingdir = new TestingDir();
-
-    @Test
-    public void testIsGzipByMethod() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-        gzipHolder.setInitParameter("mimeTypes","text/plain");
-        gzipHolder.setInitParameter("methods","POST, WIBBLE");
-
-        // Prepare Server File
-        int filesize = tester.getOutputBufferSize() * 2;
-        tester.prepareServerFile("file.txt",filesize);
-
-        // Content Servlet
-        tester.setContentServlet(GetServlet.class);
-
-        try
-        {
-            tester.start();
-            HttpTester.Response response;
-
-            tester.assertIsResponseGzipCompressed("POST","file.txt");
-            tester.assertIsResponseGzipCompressed("WIBBLE","file.txt");
-
-            response = tester.executeRequest("GET","/context/file.txt",5,TimeUnit.SECONDS);
-
-            assertThat("Response status",response.getStatus(),is(HttpStatus.OK_200));
-            assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(compressionType)));
-
-            String content = tester.readResponse(response);
-            assertThat("Response content size",content.length(),is(filesize));
-            String expectedContent = IO.readToString(testingdir.getFile("file.txt"));
-            assertThat("Response content",content,is(expectedContent));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @SuppressWarnings("serial")
-    public static class GetServlet extends DefaultServlet
-    {
-        public GetServlet()
-        {
-            super();
-        }
-
-        @Override
-        public void service(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException
-        {
-            String uri = req.getRequestURI();
-            if (uri.endsWith(".deferred"))
-            {
-                // System.err.println("type for "+uri.substring(0,uri.length()-9)+" is "+getServletContext().getMimeType(uri.substring(0,uri.length()-9)));
-                resp.setContentType(getServletContext().getMimeType(uri.substring(0,uri.length() - 9)));
-            }
-
-            doGet(req,resp);
-        }
-    }
-
-    @Test
-    public void testIsGzipCompressedEmpty() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-        gzipHolder.setInitParameter("mimeTypes","text/plain");
-
-        // Prepare server file
-        tester.prepareServerFile("empty.txt",0);
-
-        // Set content servlet
-        tester.setContentServlet(DefaultServlet.class);
-
-        try
-        {
-            tester.start();
-
-            HttpTester.Response response;
-
-            response = tester.executeRequest("GET","/context/empty.txt",5,TimeUnit.SECONDS);
-
-            assertThat("Response status",response.getStatus(),is(HttpStatus.OK_200));
-            assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(compressionType)));
-
-            String content = tester.readResponse(response);
-            assertThat("Response content size",content.length(),is(0));
-            String expectedContent = IO.readToString(testingdir.getFile("empty.txt"));
-            assertThat("Response content",content,is(expectedContent));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testIsGzipCompressedTiny() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-        tester.setGzipFilterClass(testFilter);
-
-        int filesize = tester.getOutputBufferSize() / 4;
-        tester.prepareServerFile("file.txt",filesize);
-
-        FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
-        holder.setInitParameter("mimeTypes","text/plain");
-
-        try
-        {
-            tester.start();
-            HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt");
-            Assert.assertEquals("Accept-Encoding",http.get("Vary"));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testIsGzipCompressedLarge() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-        tester.setGzipFilterClass(testFilter);
-
-        int filesize = tester.getOutputBufferSize() * 4;
-        tester.prepareServerFile("file.txt",filesize);
-
-        FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
-        holder.setInitParameter("mimeTypes","text/plain");
-
-        try
-        {
-            tester.start();
-            HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt");
-            Assert.assertEquals("Accept-Encoding",http.get("Vary"));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testGzipedIfModified() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-        tester.setGzipFilterClass(testFilter);
-
-        int filesize = tester.getOutputBufferSize() * 4;
-        tester.prepareServerFile("file.txt",filesize);
-
-        FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
-        holder.setInitParameter("mimeTypes","text/plain");
-
-        try
-        {
-            tester.start();
-            HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt",System.currentTimeMillis() - 4000);
-            Assert.assertEquals("Accept-Encoding",http.get("Vary"));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testGzippedIfSVG() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-        tester.setGzipFilterClass(testFilter);
-        tester.copyTestServerFile("test.svg");
-        @SuppressWarnings("unused")
-        FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
-        try
-        {
-            tester.start();
-            HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","test.svg",System.currentTimeMillis() - 4000);
-            Assert.assertEquals("Accept-Encoding",http.get("Vary"));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testNotGzipedIfNotModified() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-        tester.setGzipFilterClass(testFilter);
-
-        int filesize = tester.getOutputBufferSize() * 4;
-        tester.prepareServerFile("file.txt",filesize);
-
-        FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
-        holder.setInitParameter("mimeTypes","text/plain");
-        holder.setInitParameter("etags","true");
-
-        try
-        {
-            tester.start();
-            tester.assertIsResponseNotModified("GET","file.txt",System.currentTimeMillis() + 4000);
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testIsNotGzipCompressedWithZeroQ() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType + "; q=0");
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-        gzipHolder.setInitParameter("mimeTypes","text/plain");
-
-        // Prepare server file
-        int filesize = tester.getOutputBufferSize() / 4;
-        tester.prepareServerFile("file.txt",filesize);
-
-        // Add content servlet
-        tester.setContentServlet(DefaultServlet.class);
-
-        try
-        {
-            tester.start();
-            HttpTester.Response http = assertIsResponseNotGzipCompressed(tester,"GET","file.txt",filesize,HttpStatus.OK_200);
-            assertThat("Response[Vary]",http.get("Vary"),containsString("Accept-Encoding"));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testIsGzipCompressedWithQ() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType,"something;q=0.1," + compressionType + ";q=0.5");
-        tester.setGzipFilterClass(testFilter);
-
-        int filesize = tester.getOutputBufferSize() / 4;
-        tester.prepareServerFile("file.txt",filesize);
-
-        FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
-        holder.setInitParameter("mimeTypes","text/plain");
-
-        try
-        {
-            tester.start();
-            HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt");
-            Assert.assertEquals("Accept-Encoding",http.get("Vary"));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testIsNotGzipCompressedByContentType() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-        gzipHolder.setInitParameter("mimeTypes","text/plain");
-
-        // Prepare server file
-        int filesize = tester.getOutputBufferSize() * 4;
-        tester.prepareServerFile("file.mp3",filesize);
-
-        // Add content servlet
-        tester.setContentServlet(DefaultServlet.class);
-
-        try
-        {
-            tester.start();
-            HttpTester.Response http = assertIsResponseNotGzipCompressed(tester,"GET","file.mp3",filesize,HttpStatus.OK_200);
-            Assert.assertNull(http.get("Vary"));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testIsNotGzipCompressedByExcludedContentType() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-        gzipHolder.setInitParameter("excludedMimeTypes","text/plain");
-
-        // Prepare server file
-        int filesize = tester.getOutputBufferSize() * 4;
-        tester.prepareServerFile("test_quotes.txt",filesize);
-
-        // Add content servlet
-        tester.setContentServlet(DefaultServlet.class);
-
-        try
-        {
-            tester.start();
-            HttpTester.Response http = assertIsResponseNotGzipCompressed(tester,"GET","test_quotes.txt",filesize,HttpStatus.OK_200);
-            Assert.assertNull(http.get("Vary"));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testIsNotGzipCompressedByExcludedContentTypeWithCharset() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-        gzipHolder.setInitParameter("excludedMimeTypes","text/plain");
-
-        // Prepare server file
-        int filesize = tester.getOutputBufferSize() * 4;
-        tester.prepareServerFile("test_quotes.txt",filesize);
-        tester.addMimeType("txt","text/plain;charset=UTF-8");
-
-        // Add content servlet
-        tester.setContentServlet(DefaultServlet.class);
-
-        try
-        {
-            tester.start();
-            HttpTester.Response http = assertIsResponseNotGzipCompressed(tester,"GET","test_quotes.txt",filesize,HttpStatus.OK_200);
-            Assert.assertNull(http.get("Vary"));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testGzipCompressedByContentTypeWithEncoding() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-        tester.setGzipFilterClass(testFilter);
-        FilterHolder holder = tester.setContentServlet(HttpContentTypeWithEncoding.class);
-        holder.setInitParameter("mimeTypes","text/plain");
-        try
-        {
-            tester.start();
-            HttpTester.Response http = tester.assertNonStaticContentIsResponseGzipCompressed("GET","xxx",HttpContentTypeWithEncoding.COMPRESSED_CONTENT);
-            Assert.assertEquals("Accept-Encoding",http.get("Vary"));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testIsNotGzipCompressedByDeferredContentType() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-        gzipHolder.setInitParameter("mimeTypes","text/plain");
-
-        // Prepare server file
-        int filesize = tester.getOutputBufferSize() * 4;
-        tester.prepareServerFile("file.mp3.deferred",filesize);
-
-        // Add content servlet
-        tester.setContentServlet(GetServlet.class);
-
-        try
-        {
-            tester.start();
-            HttpTester.Response response = assertIsResponseNotGzipCompressed(tester,"GET","file.mp3.deferred",filesize,HttpStatus.OK_200);
-            assertThat("Response[Vary]", response.get("Vary"), isEmptyOrNullString());
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testIsNotGzipCompressedHttpStatus() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-        gzipHolder.setInitParameter("mimeTypes","text/plain");
-
-        // Test error code 204
-        tester.setContentServlet(HttpStatusServlet.class);
-
-        try
-        {
-            tester.start();
-
-            HttpTester.Response response = tester.executeRequest("GET","/context/",5,TimeUnit.SECONDS);
-
-            assertThat("Response status",response.getStatus(),is(HttpStatus.NO_CONTENT_204));
-            assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(compressionType)));
-        }
-        finally
-        {
-            tester.stop();
-        }
-
-    }
-
-    @Test
-    public void testIsNotGzipCompressedHttpBadRequestStatus() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        gzipHolder.setInitParameter("mimeTypes","text/plain");
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-
-        // Test error code 400
-        tester.setContentServlet(HttpErrorServlet.class);
-
-        try
-        {
-            tester.start();
-
-            HttpTester.Response response = tester.executeRequest("GET","/context/",5,TimeUnit.SECONDS);
-
-            assertThat("Response status",response.getStatus(),is(HttpStatus.BAD_REQUEST_400));
-            assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(compressionType)));
-
-            String content = tester.readResponse(response);
-            assertThat("Response content",content,is("error message"));
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testUserAgentExclusion() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-        tester.setUserAgent("foo");
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        gzipHolder.setInitParameter("mimeTypes","text/plain");
-        gzipHolder.setInitParameter("excludedAgents","bar, foo");
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-
-        // Prepare server file
-        int filesize = tester.getOutputBufferSize() * 4;
-        tester.prepareServerFile("file.txt",filesize);
-
-        // Add content servlet
-        tester.setContentServlet(DefaultServlet.class);
-
-        try
-        {
-            tester.start();
-            assertIsResponseNotGzipCompressed(tester,"GET","file.txt",filesize,HttpStatus.OK_200);
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testUserAgentExclusionByExcludedAgentPatterns() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-        tester.setUserAgent("foo");
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        gzipHolder.setInitParameter("excludedAgents","bar");
-        gzipHolder.setInitParameter("excludeAgentPatterns","fo.*");
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-
-        // Prepare server file
-        int filesize = tester.getOutputBufferSize() * 4;
-        tester.prepareServerFile("file.txt",filesize);
-
-        // Set content servlet
-        tester.setContentServlet(DefaultServlet.class);
-
-        try
-        {
-            tester.start();
-            assertIsResponseNotGzipCompressed(tester,"GET","file.txt",filesize,HttpStatus.OK_200);
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testExcludePaths() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        gzipHolder.setInitParameter("excludePaths","/bar/, /context/");
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-
-        // Prepare server file
-        int filesize = tester.getOutputBufferSize() * 4;
-        tester.prepareServerFile("file.txt",filesize);
-
-        // Set content servlet
-        tester.setContentServlet(DefaultServlet.class);
-
-        try
-        {
-            tester.start();
-            assertIsResponseNotGzipCompressed(tester,"GET","file.txt",filesize,HttpStatus.OK_200);
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testExcludePathPatterns() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-
-        // Add Gzip Filter first
-        FilterHolder gzipHolder = new FilterHolder(testFilter);
-        gzipHolder.setAsyncSupported(true);
-        gzipHolder.setInitParameter("excludePathPatterns","/cont.*");
-        tester.addFilter(gzipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
-
-        // Prepare server file
-        int filesize = tester.getOutputBufferSize() * 4;
-        tester.prepareServerFile("file.txt",filesize);
-
-        // Set content servlet
-        tester.setContentServlet(DefaultServlet.class);
-
-        try
-        {
-            tester.start();
-            assertIsResponseNotGzipCompressed(tester,"GET","file.txt",filesize,HttpStatus.OK_200);
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-
-    public HttpTester.Response assertIsResponseNotGzipCompressed(GzipTester tester, String method, String filename, int expectedFilesize, int status)
-            throws Exception
-    {
-        HttpTester.Response response = tester.executeRequest(method,"/context/" + filename,5,TimeUnit.SECONDS);
-
-        assertThat("Response status",response.getStatus(),is(status));
-        assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(compressionType)));
-
-        assertResponseContent(tester,response,status,filename,expectedFilesize);
-
-        return response;
-    }
-
-    private void assertResponseContent(GzipTester tester, HttpTester.Response response, int status, String filename, int expectedFilesize) throws IOException,
-            UnsupportedEncodingException
-    {
-        if (expectedFilesize >= 0)
-        {
-            assertThat("filename",filename,notNullValue());
-            assertThat("Response contentBytes.length",response.getContentBytes().length,is(expectedFilesize));
-            String contentLength = response.get("Content-Length");
-            if (StringUtil.isNotBlank(contentLength))
-            {
-                assertThat("Content-Length",response.get("Content-Length"),is(Integer.toString(expectedFilesize)));
-            }
-
-            if (status >= 200 && status < 300)
-            {
-                assertThat("ETag",response.get("ETAG"),startsWith("W/"));
-            }
-
-            File serverFile = testingdir.getFile(filename);
-            String expectedResponse = IO.readToString(serverFile);
-
-            String actual = tester.readResponse(response);
-            Assert.assertEquals("Expected response equals actual response",expectedResponse,actual);
-        }
-    }
-
-    @Test
-    public void testIsNotGzipCompressedSVGZ() throws Exception
-    {
-        GzipTester tester = new GzipTester(testingdir,compressionType);
-        tester.setGzipFilterClass(testFilter);
-
-        @SuppressWarnings("unused")
-        FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
-        tester.copyTestServerFile("test.svgz");
-        try
-        {
-            tester.start();
-            tester.assertIsResponseNotGzipFiltered("test.svgz","test.svgz.sha1","image/svg+xml","gzip");
-        }
-        finally
-        {
-            tester.stop();
-        }
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterLayeredTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterLayeredTest.java
index 06deb31..a46bb7f 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterLayeredTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterLayeredTest.java
@@ -18,8 +18,10 @@
 
 package org.eclipse.jetty.servlets;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -32,15 +34,16 @@
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.handler.gzip.AsyncManipFilter;
+import org.eclipse.jetty.server.handler.gzip.AsyncScheduledDispatchWrite;
+import org.eclipse.jetty.server.handler.gzip.AsyncTimeoutCompleteWrite;
+import org.eclipse.jetty.server.handler.gzip.AsyncTimeoutDispatchWrite;
+import org.eclipse.jetty.server.handler.gzip.GzipHandler;
+import org.eclipse.jetty.server.handler.gzip.GzipTester;
+import org.eclipse.jetty.server.handler.gzip.GzipTester.ContentMetadata;
+import org.eclipse.jetty.server.handler.gzip.TestDirContentServlet;
+import org.eclipse.jetty.server.handler.gzip.TestServletLengthStreamTypeWrite;
 import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlets.gzip.AsyncManipFilter;
-import org.eclipse.jetty.servlets.gzip.AsyncScheduledDispatchWrite;
-import org.eclipse.jetty.servlets.gzip.AsyncTimeoutCompleteWrite;
-import org.eclipse.jetty.servlets.gzip.AsyncTimeoutDispatchWrite;
-import org.eclipse.jetty.servlets.gzip.GzipTester;
-import org.eclipse.jetty.servlets.gzip.GzipTester.ContentMetadata;
-import org.eclipse.jetty.servlets.gzip.TestDirContentServlet;
-import org.eclipse.jetty.servlets.gzip.TestServletLengthStreamTypeWrite;
 import org.eclipse.jetty.toolchain.test.TestTracker;
 import org.eclipse.jetty.toolchain.test.TestingDir;
 import org.junit.Ignore;
@@ -64,7 +67,7 @@
     private static final HttpConfiguration defaultHttp = new HttpConfiguration();
     private static final int LARGE = defaultHttp.getOutputBufferSize() * 8;
     private static final int SMALL = defaultHttp.getOutputBufferSize() / 4;
-    private static final int TINY = AsyncGzipFilter.DEFAULT_MIN_GZIP_SIZE / 2;
+    private static final int TINY = GzipHandler.DEFAULT_MIN_GZIP_SIZE / 2;
     private static final boolean EXPECT_COMPRESSED = true;
 
     @Parameters(name = "{0} bytes - {1} - compressed({2}) - filter({3}) - servlet({4}")
@@ -115,7 +118,7 @@
     @Test
     public void testGzipDos() throws Exception
     {
-        GzipTester tester = new GzipTester(testingdir, GzipFilter.GZIP);
+        GzipTester tester = new GzipTester(testingdir, GzipHandler.GZIP);
         
         // Add Gzip Filter first
         FilterHolder gzipHolder = new FilterHolder(gzipFilterClass);
@@ -146,7 +149,7 @@
             if (expectCompressed)
             {
                 // Must be gzip compressed
-                assertThat("Content-Encoding",response.get("Content-Encoding"),containsString(GzipFilter.GZIP));
+                assertThat("Content-Encoding",response.get("Content-Encoding"),containsString(GzipHandler.GZIP));
             }
             
             // Uncompressed content Size
@@ -162,7 +165,7 @@
     @Test
     public void testDosGzip() throws Exception
     {
-        GzipTester tester = new GzipTester(testingdir, GzipFilter.GZIP);
+        GzipTester tester = new GzipTester(testingdir, GzipHandler.GZIP);
         
         // Add (DoSFilter-like) manip filter
         FilterHolder manipHolder = new FilterHolder(AsyncManipFilter.class);
@@ -193,10 +196,10 @@
             if (expectCompressed)
             {
                 // Must be gzip compressed
-                assertThat("Content-Encoding",response.get("Content-Encoding"),containsString(GzipFilter.GZIP));
+                assertThat("Content-Encoding",response.get("Content-Encoding"),containsString(GzipHandler.GZIP));
             } else
             {
-                assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(GzipFilter.GZIP)));
+                assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(GzipHandler.GZIP)));
             }
             
             // Uncompressed content Size
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterMinSizeTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterMinSizeTest.java
deleted file mode 100644
index 795ad63..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterMinSizeTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets;
-
-import java.util.Arrays;
-import java.util.Collection;
-
-import javax.servlet.Servlet;
-
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlets.gzip.GzipTester;
-import org.eclipse.jetty.servlets.gzip.TestMinGzipSizeServlet;
-import org.eclipse.jetty.toolchain.test.TestingDir;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-/**
- * Perform specific tests on the IncludableGzipFilter's ability to manage
- * minGzipSize initialization parameter.
- *
- * @see <a href="Eclipse Bug 366106">http://bugs.eclipse.org/366106</a>
- */
-@RunWith(Parameterized.class)
-public class IncludableGzipFilterMinSizeTest
-{
-    @Parameters
-    public static Collection<String[]> data()
-    {
-        String[][] data = new String[][]
-                {
-                { GzipFilter.GZIP },
-                { GzipFilter.DEFLATE }
-                };
-
-        return Arrays.asList(data);
-    }
-
-    public IncludableGzipFilterMinSizeTest(String compressionType)
-    {
-        this.compressionType = compressionType;
-    }
-
-    @Rule
-    public TestingDir testdir = new TestingDir();
-
-    private String compressionType;
-    private Class<? extends Servlet> testServlet = TestMinGzipSizeServlet.class;
-
-    @Test
-    public void testUnderMinSize() throws Exception
-    {
-        GzipTester tester = new GzipTester(testdir, compressionType);
-        // Use IncludableGzipFilter
-        tester.setGzipFilterClass(IncludableGzipFilter.class);
-
-        FilterHolder holder = tester.setContentServlet(testServlet);
-        // A valid mime type that we will never use in this test.
-        // configured here to prevent mimeType==null logic
-        holder.setInitParameter("mimeTypes","application/soap+xml");
-        holder.setInitParameter("minGzipSize", "2048");
-        holder.setInitParameter("uncheckedPrintWriter","true");
-
-        tester.copyTestServerFile("small_script.js");
-
-        try {
-            tester.start();
-            tester.assertIsResponseNotGzipFiltered("small_script.js",
-                    "small_script.js.sha1",
-                    "text/javascript; charset=utf-8");
-        } finally {
-            tester.stop();
-        }
-    }
-
-    @Test
-    public void testOverMinSize() throws Exception
-    {
-        GzipTester tester = new GzipTester(testdir, compressionType);
-        // Use IncludableGzipFilter
-        tester.setGzipFilterClass(IncludableGzipFilter.class);
-
-        FilterHolder holder = tester.setContentServlet(testServlet);
-        holder.setInitParameter("mimeTypes","application/soap+xml,text/javascript,application/javascript");
-        holder.setInitParameter("minGzipSize", "2048");
-        holder.setInitParameter("uncheckedPrintWriter","true");
-
-        tester.copyTestServerFile("big_script.js");
-
-        try {
-            tester.start();
-            tester.assertIsResponseGzipCompressed("GET","big_script.js");
-        } finally {
-            tester.stop();
-        }
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterTest.java
deleted file mode 100644
index cdd7e5e..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets;
-
-import static org.junit.Assert.assertEquals;
-
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.Inflater;
-import java.util.zip.InflaterInputStream;
-
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.http.HttpTester;
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlet.ServletTester;
-import org.eclipse.jetty.toolchain.test.TestingDir;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.IO;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class IncludableGzipFilterTest
-{
-    @Parameters
-    public static Collection<String[]> data()
-    {
-        String[][] data = new String[][]
-                {
-                { GzipFilter.GZIP },
-                { GzipFilter.DEFLATE }
-                };
-
-        return Arrays.asList(data);
-    }
-
-    @Rule
-    public TestingDir testdir = new TestingDir();
-
-    private static String __content =
-        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In quis felis nunc. "+
-        "Quisque suscipit mauris et ante auctor ornare rhoncus lacus aliquet. Pellentesque "+
-        "habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. "+
-        "Vestibulum sit amet felis augue, vel convallis dolor. Cras accumsan vehicula diam "+
-        "at faucibus. Etiam in urna turpis, sed congue mi. Morbi et lorem eros. Donec vulputate "+
-        "velit in risus suscipit lobortis. Aliquam id urna orci, nec sollicitudin ipsum. "+
-        "Cras a orci turpis. Donec suscipit vulputate cursus. Mauris nunc tellus, fermentum "+
-        "eu auctor ut, mollis at diam. Quisque porttitor ultrices metus, vitae tincidunt massa "+
-        "sollicitudin a. Vivamus porttitor libero eget purus hendrerit cursus. Integer aliquam "+
-        "consequat mauris quis luctus. Cras enim nibh, dignissim eu faucibus ac, mollis nec neque. "+
-        "Aliquam purus mauris, consectetur nec convallis lacinia, porta sed ante. Suspendisse "+
-        "et cursus magna. Donec orci enim, molestie a lobortis eu, imperdiet vitae neque.";
-
-    private ServletTester tester;
-    private String compressionType;
-
-    public IncludableGzipFilterTest(String compressionType)
-    {
-        this.compressionType = compressionType;
-    }
-
-    @Before
-    public void setUp() throws Exception
-    {
-        testdir.ensureEmpty();
-
-        File testFile = testdir.getFile("file.txt");
-        try (OutputStream testOut = new BufferedOutputStream(new FileOutputStream(testFile)))
-        {
-            ByteArrayInputStream testIn = new ByteArrayInputStream(__content.getBytes("ISO8859_1"));
-            IO.copy(testIn,testOut);
-        }
-
-        tester=new ServletTester("/context");
-        tester.getContext().setResourceBase(testdir.getDir().getCanonicalPath());
-        tester.getContext().addServlet(org.eclipse.jetty.servlet.DefaultServlet.class, "/");
-        FilterHolder holder = tester.getContext().addFilter(IncludableGzipFilter.class,"/*",null);
-        holder.setInitParameter("mimeTypes","text/plain");
-        tester.start();
-    }
-
-    @After
-    public void tearDown() throws Exception
-    {
-        tester.stop();
-        IO.delete(testdir.getDir());
-    }
-
-    @Test
-    public void testGzipFilter() throws Exception
-    {
-        // generated and parsed test
-
-        ByteBuffer request=BufferUtil.toBuffer(
-            "GET /context/file.txt HTTP/1.0\r\n"+
-            "Host: tester\r\n"+
-            "Accept-Encoding: "+compressionType+"\r\n"+
-            "\r\n");
-
-
-        HttpTester.Response response=HttpTester.parseResponse(tester.getResponses(request));
-
-        assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-        assertEquals(compressionType,response.get("Content-Encoding"));
-
-        InputStream testIn = null;
-        ByteArrayInputStream compressedResponseStream = new ByteArrayInputStream(response.getContentBytes());
-        if (compressionType.equals(GzipFilter.GZIP))
-        {
-            testIn = new GZIPInputStream(compressedResponseStream);
-        }
-        else if (compressionType.equals(GzipFilter.DEFLATE))
-        {
-            testIn = new InflaterInputStream(compressedResponseStream, new Inflater(true));
-        }
-        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
-        IO.copy(testIn,testOut);
-
-        assertEquals(__content, testOut.toString("ISO8859_1"));
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java
index 7f38663..9811fb5 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java
@@ -31,21 +31,38 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.EnumSet;
 import java.util.Map;
 
 import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.http.MimeTypes;
 import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.FilterMapping;
+import org.eclipse.jetty.servlet.ServletHandler;
 import org.eclipse.jetty.servlet.ServletTester;
 import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.MultiPartInputStreamParser;
+import org.eclipse.jetty.util.ReadLineInputStream;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.eclipse.jetty.util.log.StdErrLog;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -54,7 +71,55 @@
 {
     private File _dir;
     private ServletTester tester;
+    FilterHolder multipartFilter;
+    
+    public static class ReadAllFilter implements Filter
+    {
 
+        @Override
+        public void init(FilterConfig filterConfig) throws ServletException
+        {   
+        }
+
+        @Override
+        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+        {
+            ServletInputStream is = request.getInputStream();
+            ReadLineInputStream rlis = new ReadLineInputStream(request.getInputStream());
+            String line = "";
+            while (line != null)
+            {
+                line = rlis.readLine();
+            }
+          chain.doFilter(request, response);
+        }
+
+        @Override
+        public void destroy()
+        {
+        }
+    }
+    
+    
+    public static class NullServlet extends HttpServlet
+    {
+        @Override
+        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            resp.setStatus(200);
+        }
+        
+    }
+    
+    public static class FilenameServlet extends TestServlet
+    {
+        @Override
+        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            assertNotNull(req.getAttribute("fileup"));
+            super.doPost(req, resp);
+        }
+    }
 
     public static class BoundaryServlet extends TestServlet
     {
@@ -90,7 +155,7 @@
         protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
         {
             assertNotNull(req.getParameter("fileup"));
-            System.err.println("Fileup="+req.getParameter("fileup"));
+            // System.err.println("Fileup="+req.getParameter("fileup"));
             assertNotNull(req.getParameter("fileup"+MultiPartFilter.CONTENT_TYPE_SUFFIX));
             assertEquals(req.getParameter("fileup"+MultiPartFilter.CONTENT_TYPE_SUFFIX), "application/octet-stream");
             super.doPost(req, resp);
@@ -113,7 +178,7 @@
         tester.getContext().setResourceBase(_dir.getCanonicalPath());
         tester.getContext().addServlet(TestServlet.class, "/");
         tester.getContext().setAttribute("javax.servlet.context.tempdir", _dir);
-        FilterHolder multipartFilter = tester.getContext().addFilter(MultiPartFilter.class,"/*", EnumSet.of(DispatcherType.REQUEST));
+        multipartFilter = tester.getContext().addFilter(MultiPartFilter.class,"/*", EnumSet.of(DispatcherType.REQUEST));
         multipartFilter.setInitParameter("deleteFiles", "true");
         multipartFilter.setInitParameter("fileOutputBuffer", "1"); //write a file if  there's more than 1 byte content
         tester.start();
@@ -125,6 +190,70 @@
         tester.stop();
         tester=null;
     }
+    
+    @Test
+    public void testFinalBoundaryOnly() throws Exception
+    {
+        
+        tester.addServlet(NullServlet.class,"/null");       
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        // test GET
+        request.setMethod("POST");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setURI("/context/null");
+        
+        String delimiter = "\r\n";
+        final String boundary = "MockMultiPartTestBoundary";
+        String content = 
+                delimiter +
+                "Hello world" +
+                delimiter +        // Two delimiter markers, which make an empty line.
+                delimiter +
+                "--" + boundary + "--" + delimiter;
+        
+        request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
+        request.setContent(content);
+        
+        try(StacklessLogging stackless = new StacklessLogging(ServletHandler.class))
+        {
+            response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+            assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+        }
+    } 
+    
+    @Test
+    public void testEmpty() throws Exception
+    {
+        
+        tester.addServlet(NullServlet.class,"/null");
+        
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+
+        // test GET
+        request.setMethod("POST");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setURI("/context/null");
+        
+        String delimiter = "\r\n";
+        final String boundary = "MockMultiPartTestBoundary";
+        String content = 
+                delimiter +
+                "--" + boundary + "--" + delimiter;
+        
+        request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
+        request.setContent(content);
+        
+        try(StacklessLogging stackless = new StacklessLogging(ServletHandler.class))
+        {
+            response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+            assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+        }
+    }
 
     @Test
     public void testBadPost() throws Exception
@@ -152,8 +281,11 @@
         request.setContent(content);
 
 
-        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
-        assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,response.getStatus());
+        try(StacklessLogging stackless = new StacklessLogging(ServletHandler.class))
+        {
+            response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+            assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,response.getStatus());
+        }
     }
 
 
@@ -601,11 +733,50 @@
         request.setHeader("Host","tester");
         request.setURI("/context/dump");
         request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
-        
-        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
-        assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
-        assertTrue(response.getContent().indexOf("Missing content")>=0);
+
+        try(StacklessLogging stackless = new StacklessLogging(ServletHandler.class))
+        {
+            response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+            assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
+            assertTrue(response.getContent().indexOf("Missing content")>=0);
+        }
     }
+    
+    
+    @Test
+    public void testBodyAlreadyConsumed()
+    throws Exception
+    {
+        tester.addServlet(NullServlet.class,"/null");    
+        
+        FilterHolder holder = new FilterHolder();
+        holder.setName("reader");
+        holder.setFilter(new ReadAllFilter());
+        tester.getContext().getServletHandler().addFilter(holder);
+        FilterMapping mapping = new FilterMapping();
+        mapping.setFilterName("reader");
+        mapping.setPathSpec("/*");
+        tester.getContext().getServletHandler().prependFilterMapping(mapping);
+        String boundary="XyXyXy";
+        // generated and parsed test
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+        
+        request.setMethod("POST");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setURI("/context/null");
+        request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
+        request.setContent("How now brown cow");
+
+        try(StacklessLogging stackless = new StacklessLogging(ServletHandler.class))
+        {
+            response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+            assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+        }
+    }
+
+
 
     @Test
     public void testWhitespaceBodyWithCRLF()
@@ -624,9 +795,12 @@
         request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
         request.setContent(whitespace);
         
-        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
-        assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
-        assertTrue(response.getContent().indexOf("Missing initial")>=0);
+        try(StacklessLogging stackless = new StacklessLogging(ServletHandler.class))
+        {
+            response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+            assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
+            assertTrue(response.getContent().indexOf("Missing initial")>=0);
+        }
     }
     
   
@@ -646,10 +820,13 @@
         request.setURI("/context/dump");
         request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
         request.setContent(whitespace);
-        
-        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
-        assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
-        assertTrue(response.getContent().indexOf("Missing initial")>=0);
+
+        try(StacklessLogging stackless = new StacklessLogging(ServletHandler.class))
+        {
+            response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+            assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
+            assertTrue(response.getContent().indexOf("Missing initial")>=0);
+        }
     }
 
     @Test
@@ -775,15 +952,14 @@
         }
         request.setContent(baos.toString());
 
-        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
-        assertTrue(response.getContent().contains("Buffer size exceeded"));
-        assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
+        try(StacklessLogging stackless = new StacklessLogging(ServletHandler.class))
+        {
+            response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+            assertTrue(response.getContent().contains("Buffer size exceeded"));
+            assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
+        }
     }
 
-    /*
-     * see the testParameterMap test
-     *
-     */
     public static class TestServletParameterMap extends DumpServlet
     {
         @Override
@@ -798,7 +974,7 @@
     /**
      * Validate that the getParameterMap() call is correctly unencoding the parameters in the
      * map that it returns.
-     * @throws Exception
+     * @throws Exception on test failure
      */
     @Test
     public void testParameterMap() throws Exception
@@ -838,30 +1014,34 @@
 
     public static class TestServletCharSet extends HttpServlet
     {
-
         @Override
         protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
         {
             //test that the multipart content bytes were converted correctly from their charset to unicode
-            String content = (String)req.getParameter("ttt");
-            assertNotNull(content);
-            assertEquals("ttt\u01FCzzz",content);       
-            assertEquals("application/octet-stream; charset=UTF-8",req.getParameter("ttt"+MultiPartFilter.CONTENT_TYPE_SUFFIX));
-                  
+            String filename = req.getParameter("ttt");
+            assertEquals("ttt.txt",filename);  
+            String contentType = (String)req.getParameter("ttt"+MultiPartFilter.CONTENT_TYPE_SUFFIX);
+            assertEquals("application/octet-stream; charset=UTF-8",contentType);  
+            String charset=MimeTypes.getCharsetFromContentType(contentType);
+            assertEquals("utf-8",charset);  
             
-            //test that the parameter map retrieves values as String[]
-            Map map = req.getParameterMap();
-            Object o = map.get("ttt");
-            assertTrue(o.getClass().isArray());
-            super.doPost(req, resp);
+            File file = (File)req.getAttribute("ttt");
+            String content=IO.toString(new InputStreamReader(new FileInputStream(file),charset));
+            assertEquals("ttt\u01FCzzz",content);       
+            
+            resp.setStatus(200);
+            resp.setContentType(contentType);
+            resp.getWriter().print(content);
         } 
     }
     
-    
     @Test
     public void testWithCharSet()
     throws Exception
     {
+        ((StdErrLog)Log.getLogger(MultiPartFilter.class)).setDebugEnabled(true);
+        ((StdErrLog)Log.getLogger(MultiPartInputStreamParser.class)).setDebugEnabled(true);
+        
         // generated and parsed test
         HttpTester.Request request = HttpTester.newRequest();
         HttpTester.Response response;
@@ -879,16 +1059,74 @@
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         
         baos.write(("--" + boundary + "\r\n"+
-                "Content-Disposition: form-data; name=\"ttt\"\r\n"+
+                "Content-Disposition: form-data; name=\"ttt\"; filename=\"ttt.txt\"\r\n"+
                 "Content-Type: application/octet-stream; charset=UTF-8\r\n\r\n").getBytes());
         baos.write("ttt\u01FCzzz".getBytes(StandardCharsets.UTF_8));
         baos.write(("\r\n--" + boundary + "--\r\n\r\n").getBytes());
   
         
         request.setContent(baos.toByteArray());   
+
         response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+        assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+        assertEquals("ttt\u01FCzzz",response.getContent());
+        
     }
 
+    
+    @Test
+    public void testFilesWithFilenames ()
+            throws Exception
+    {       
+        multipartFilter.setInitParameter("fileOutputBuffer", "0");
+        multipartFilter.setInitParameter("writeFilesWithFilenames", "true");
+        
+        
+        String boundary="XyXyXy";
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+        tester.addServlet(FilenameServlet.class,"/testf");
+        // test GET
+        request.setMethod("POST");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setURI("/context/testf");
+        request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
+
+        String content = "--XyXyXy\r"+
+        "Content-Disposition: form-data; name=\"fileName\"\r"+
+        "Content-Type: text/plain; charset=US-ASCII\r"+
+        "Content-Transfer-Encoding: 8bit\r"+
+        "\r"+
+        "abc\r"+
+        "--XyXyXy\r"+
+        "Content-Disposition: form-data; name=\"desc\"\r"+ 
+        "Content-Type: text/plain; charset=US-ASCII\r"+ 
+        "Content-Transfer-Encoding: 8bit\r"+
+        "\r"+
+        "123\r"+ 
+        "--XyXyXy\r"+ 
+        "Content-Disposition: form-data; name=\"title\"\r"+
+        "Content-Type: text/plain; charset=US-ASCII\r"+
+        "Content-Transfer-Encoding: 8bit\r"+ 
+        "\r"+
+        "ttt\r"+ 
+        "--XyXyXy\r"+
+        "Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r"+
+        "Content-Type: application/octet-stream\r"+
+        "Content-Transfer-Encoding: binary\r"+ 
+        "\r"+
+        "000\r"+ 
+        "--XyXyXy--\r";
+        request.setContent(content);
+
+        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+        assertEquals(HttpServletResponse.SC_OK,response.getStatus()); 
+        assertTrue(response.getContent().indexOf("000")>=0);
+    }
+    
+    
+    
     public static class DumpServlet extends HttpServlet
     {
         private static final long serialVersionUID = 201012011130L;
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/QoSFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/QoSFilterTest.java
index a59e0c1..25fbced 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/QoSFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/QoSFilterTest.java
@@ -18,11 +18,20 @@
 
 package org.eclipse.jetty.servlets;
 
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+
 import java.io.IOException;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.EnumSet;
-import java.util.concurrent.CountDownLatch;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
+
 import javax.servlet.DispatcherType;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
@@ -49,7 +58,6 @@
 
     private ServletTester _tester;
     private LocalConnector[] _connectors;
-    private CountDownLatch _doneRequests;
     private final int NUM_CONNECTIONS = 8;
     private final int NUM_LOOPS = 6;
     private final int MAX_QOS = 4;
@@ -67,8 +75,6 @@
         for (int i = 0; i < _connectors.length; ++i)
             _connectors[i] = _tester.createLocalConnector();
 
-        _doneRequests = new CountDownLatch(NUM_CONNECTIONS * NUM_LOOPS);
-
         _tester.start();
     }
 
@@ -81,17 +87,21 @@
     @Test
     public void testNoFilter() throws Exception
     {
+        List<Worker> workers = new ArrayList<>();
         for (int i = 0; i < NUM_CONNECTIONS; ++i)
         {
-            new Thread(new Worker(i)).start();
+            workers.add(new Worker(i));
         }
 
-        _doneRequests.await(10, TimeUnit.SECONDS);
+        ExecutorService executor = Executors.newFixedThreadPool(NUM_CONNECTIONS);
+        List<Future<Void>> futures = executor.invokeAll(workers, 10, TimeUnit.SECONDS);
+
+        rethrowExceptions(futures);
 
         if (TestServlet.__maxSleepers <= MAX_QOS)
             LOG.warn("TEST WAS NOT PARALLEL ENOUGH!");
         else
-            Assert.assertThat(TestServlet.__maxSleepers, Matchers.lessThanOrEqualTo(NUM_CONNECTIONS));
+            assertThat(TestServlet.__maxSleepers, Matchers.lessThanOrEqualTo(NUM_CONNECTIONS));
     }
 
     @Test
@@ -102,12 +112,17 @@
         holder.setInitParameter(QoSFilter.MAX_REQUESTS_INIT_PARAM, "" + MAX_QOS);
         _tester.getContext().getServletHandler().addFilterWithMapping(holder, "/*", EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC));
 
+        List<Worker> workers = new ArrayList<>();
         for (int i = 0; i < NUM_CONNECTIONS; ++i)
         {
-            new Thread(new Worker(i)).start();
+            workers.add(new Worker(i));
         }
 
-        _doneRequests.await(10, TimeUnit.SECONDS);
+        ExecutorService executor = Executors.newFixedThreadPool(NUM_CONNECTIONS);
+        List<Future<Void>> futures = executor.invokeAll(workers, 10, TimeUnit.SECONDS);
+
+        rethrowExceptions(futures);
+
         if (TestServlet.__maxSleepers < MAX_QOS)
             LOG.warn("TEST WAS NOT PARALLEL ENOUGH!");
         else
@@ -122,19 +137,32 @@
         holder.setInitParameter(QoSFilter.MAX_REQUESTS_INIT_PARAM, String.valueOf(MAX_QOS));
         _tester.getContext().getServletHandler().addFilterWithMapping(holder, "/*", EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC));
 
+        List<Worker2> workers = new ArrayList<>();
         for (int i = 0; i < NUM_CONNECTIONS; ++i)
         {
-            new Thread(new Worker2(i)).start();
+            workers.add(new Worker2(i));
         }
 
-        _doneRequests.await(20, TimeUnit.SECONDS);
+        ExecutorService executor = Executors.newFixedThreadPool(NUM_CONNECTIONS);
+        List<Future<Void>> futures = executor.invokeAll(workers, 20, TimeUnit.SECONDS);
+
+        rethrowExceptions(futures);
+
         if (TestServlet.__maxSleepers < MAX_QOS)
             LOG.warn("TEST WAS NOT PARALLEL ENOUGH!");
         else
             Assert.assertEquals(TestServlet.__maxSleepers, MAX_QOS);
     }
 
-    class Worker implements Runnable
+    private void rethrowExceptions(List<Future<Void>> futures) throws Exception
+    {
+        for (Future<Void> future : futures)
+        {
+            future.get();
+        }
+    }
+
+    class Worker implements Callable<Void>
     {
         private int _num;
 
@@ -144,7 +172,7 @@
         }
 
         @Override
-        public void run()
+        public Void call() throws Exception
         {
             for (int i = 0; i < NUM_LOOPS; i++)
             {
@@ -154,23 +182,17 @@
                 request.setHeader("host", "tester");
                 request.setURI("/context/test?priority=" + (_num % QoSFilter.__DEFAULT_MAX_PRIORITY));
                 request.setHeader("num", _num + "");
-                try
-                {
-                    String responseString = _connectors[_num].getResponses(BufferUtil.toString(request.generate()));
-                    if (responseString.contains("HTTP"))
-                    {
-                        _doneRequests.countDown();
-                    }
-                }
-                catch (Exception x)
-                {
-                    Assert.assertTrue(false);
-                }
+
+                String responseString = _connectors[_num].getResponse(BufferUtil.toString(request.generate()));
+
+                assertThat("Response contains", responseString, containsString("HTTP"));
             }
+
+            return null;
         }
     }
 
-    class Worker2 implements Runnable
+    class Worker2 implements Callable<Void>
     {
         private int _num;
 
@@ -180,7 +202,7 @@
         }
 
         @Override
-        public void run()
+        public Void call() throws Exception
         {
             URL url = null;
             try
@@ -190,13 +212,14 @@
                 {
                     url = new URL(addr + "/context/test?priority=" + (_num % QoSFilter.__DEFAULT_MAX_PRIORITY) + "&n=" + _num + "&l=" + i);
                     url.getContent();
-                    _doneRequests.countDown();
                 }
             }
             catch (Exception e)
             {
                 LOG.debug("Request " + url + " failed", e);
             }
+
+            return null;
         }
     }
 
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ThreadStarvationTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ThreadStarvationTest.java
new file mode 100644
index 0000000..440fdb1
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ThreadStarvationTest.java
@@ -0,0 +1,409 @@
+//

+//  ========================================================================

+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.

+//  ------------------------------------------------------------------------

+//  All rights reserved. This program and the accompanying materials

+//  are made available under the terms of the Eclipse Public License v1.0

+//  and Apache License v2.0 which accompanies this distribution.

+//

+//      The Eclipse Public License is available at

+//      http://www.eclipse.org/legal/epl-v10.html

+//

+//      The Apache License v2.0 is available at

+//      http://www.opensource.org/licenses/apache2.0.php

+//

+//  You may elect to redistribute this code under either of these licenses.

+//  ========================================================================

+//

+

+package org.eclipse.jetty.servlets;

+

+import java.io.File;

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+import java.net.Socket;

+import java.nio.ByteBuffer;

+import java.nio.channels.SelectionKey;

+import java.nio.channels.SocketChannel;

+import java.nio.charset.StandardCharsets;

+import java.nio.file.Files;

+import java.nio.file.Path;

+import java.nio.file.Paths;

+import java.nio.file.StandardOpenOption;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.List;

+import java.util.concurrent.BrokenBarrierException;

+import java.util.concurrent.CountDownLatch;

+import java.util.concurrent.CyclicBarrier;

+import java.util.concurrent.Exchanger;

+import java.util.concurrent.TimeUnit;

+import java.util.concurrent.TimeoutException;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+

+import org.eclipse.jetty.io.ManagedSelector;

+import org.eclipse.jetty.io.SelectChannelEndPoint;

+import org.eclipse.jetty.server.HttpChannel;

+import org.eclipse.jetty.server.Request;

+import org.eclipse.jetty.server.Server;

+import org.eclipse.jetty.server.ServerConnector;

+import org.eclipse.jetty.server.handler.AbstractHandler;

+import org.eclipse.jetty.servlet.DefaultServlet;

+import org.eclipse.jetty.servlet.ServletContextHandler;

+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;

+import org.eclipse.jetty.toolchain.test.TestTracker;

+import org.eclipse.jetty.toolchain.test.annotation.Slow;

+import org.eclipse.jetty.util.log.StacklessLogging;

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

+import org.junit.After;

+import org.junit.Assert;

+import org.junit.Rule;

+import org.junit.Test;

+

+public class ThreadStarvationTest

+{

+    @Rule

+    public TestTracker tracker = new TestTracker();

+    private Server _server;

+

+    @After

+    public void dispose() throws Exception

+    {

+        if (_server != null)

+            _server.stop();

+    }

+

+    @Test

+    @Slow

+    public void testDefaultServletSuccess() throws Exception

+    {

+        int maxThreads = 10;

+        QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads, maxThreads);

+        threadPool.setDetailedDump(true);

+        _server = new Server(threadPool);

+

+        // Prepare a big file to download.

+        File directory = MavenTestingUtils.getTargetTestingDir();

+        Files.createDirectories(directory.toPath());

+        String resourceName = "resource.bin";

+        Path resourcePath = Paths.get(directory.getPath(), resourceName);

+        try (OutputStream output = Files.newOutputStream(resourcePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE))

+        {

+            byte[] chunk = new byte[1024];

+            Arrays.fill(chunk,(byte)'X');

+            chunk[chunk.length-2]='\r';

+            chunk[chunk.length-1]='\n';

+            for (int i = 0; i < 256 * 1024; ++i)

+                output.write(chunk);

+        }

+

+        final CountDownLatch writePending = new CountDownLatch(1);

+        ServerConnector connector = new ServerConnector(_server, 0, 1)

+        {

+            @Override

+            protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException

+            {

+                return new SelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout())

+                {

+                    @Override

+                    protected void onIncompleteFlush()

+                    {

+                        super.onIncompleteFlush();

+                        writePending.countDown();

+                    }

+                };

+            }

+        };

+        connector.setIdleTimeout(Long.MAX_VALUE);

+        _server.addConnector(connector);

+

+        ServletContextHandler context = new ServletContextHandler(_server, "/");

+        context.setResourceBase(directory.toURI().toString());

+        context.addServlet(DefaultServlet.class, "/*").setAsyncSupported(false);

+        _server.setHandler(context);

+

+        _server.start();

+

+        List<Socket> sockets = new ArrayList<>();

+        for (int i = 0; i < maxThreads*2; ++i)

+        {

+            Socket socket = new Socket("localhost", connector.getLocalPort());

+            sockets.add(socket);

+            OutputStream output = socket.getOutputStream();

+            String request = "" +

+                    "GET /" + resourceName + " HTTP/1.1\r\n" +

+                    "Host: localhost\r\n" +

+                    "\r\n";

+            output.write(request.getBytes(StandardCharsets.UTF_8));

+            output.flush();

+            Thread.sleep(100);

+        }

+

+        // Wait for a the servlet to block.

+        Assert.assertTrue(writePending.await(5, TimeUnit.SECONDS));

+

+        long expected = Files.size(resourcePath);

+        byte[] buffer = new byte[48 * 1024];

+        List<Exchanger<Long>> totals = new ArrayList<>();

+        for (Socket socket : sockets)

+        {

+            final Exchanger<Long> x = new Exchanger<>();

+            totals.add(x);

+            final InputStream input = socket.getInputStream();

+

+            new Thread()

+            {

+                @Override

+                public void run()

+                {

+                    long total=0;

+                    try

+                    {

+                        // look for CRLFCRLF

+                        StringBuilder header = new StringBuilder();

+                        int state=0;

+                        while (state<4 && header.length()<2048)

+                        {

+                            int ch=input.read();

+                            if (ch<0)

+                                break;

+                            header.append((char)ch);

+                            switch(state)

+                            {

+                                case 0:

+                                    if (ch=='\r')

+                                        state=1;

+                                    break;

+                                case 1:

+                                    if (ch=='\n')

+                                        state=2;

+                                    else

+                                        state=0;

+                                    break;

+                                case 2:

+                                    if (ch=='\r')

+                                        state=3;

+                                    else

+                                        state=0;

+                                    break;

+                                case 3:

+                                    if (ch=='\n')

+                                        state=4;

+                                    else

+                                        state=0;

+                                    break;

+                            }

+                        }

+

+                        while (total<expected)

+                        {

+                            int read=input.read(buffer);

+                            if (read<0)

+                                break;

+                            total+=read;

+                        }

+                    }

+                    catch (IOException e)

+                    {

+                        e.printStackTrace();

+                    }

+                    finally

+                    {

+                        try

+                        {

+                            x.exchange(total);

+                        }

+                        catch (InterruptedException e)

+                        {

+                            e.printStackTrace();

+                        }

+                    }

+                }

+            }.start();

+        }

+

+        for (Exchanger<Long> x : totals)

+        {

+            Long total = x.exchange(-1L,10000,TimeUnit.SECONDS);

+            Assert.assertEquals(expected,total.longValue());

+        }

+        

+        // We could read everything, good.

+        for (Socket socket : sockets)

+            socket.close();

+    }

+    

+    @Test

+    public void testFailureStarvation() throws Exception

+    {

+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))

+        {

+            int acceptors = 0;

+            int selectors = 1;

+            int maxThreads = 10;

+            final int barried=maxThreads-acceptors-selectors;

+            final CyclicBarrier barrier = new CyclicBarrier(barried);

+

+

+            QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads, maxThreads);

+            threadPool.setDetailedDump(true);

+            _server = new Server(threadPool);

+

+

+            ServerConnector connector = new ServerConnector(_server, acceptors, selectors)

+            {

+                @Override

+                protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException

+                {

+                    return new SelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout())

+                    {

+

+                        @Override

+                        public boolean flush(ByteBuffer... buffers) throws IOException

+                        {

+                            super.flush(buffers[0]);

+                            throw new IOException("TEST FAILURE");

+                        }

+

+                    };

+                }

+            };

+            connector.setIdleTimeout(Long.MAX_VALUE);

+            _server.addConnector(connector);

+

+            final AtomicInteger count = new AtomicInteger(0);

+            _server.setHandler(new AbstractHandler()

+            {

+                @Override

+                public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException

+                {

+                    int c=count.getAndIncrement();

+                    try

+                    {

+                        if (c<barried)

+                        {

+                            barrier.await(10,TimeUnit.SECONDS);

+                        }

+                    }

+                    catch (InterruptedException | BrokenBarrierException | TimeoutException e)

+                    {

+                        throw new ServletException(e);

+                    }

+                    baseRequest.setHandled(true);

+                    response.setStatus(200);

+                    response.setContentLength(13);

+                    response.getWriter().print("Hello World!\n");

+                    response.getWriter().flush();

+                }

+            });

+

+            _server.start();

+

+            List<Socket> sockets = new ArrayList<>();

+            for (int i = 0; i < maxThreads*2; ++i)

+            {

+                Socket socket = new Socket("localhost", connector.getLocalPort());

+                sockets.add(socket);

+                OutputStream output = socket.getOutputStream();

+                String request = "" +

+                        "GET / HTTP/1.1\r\n" +

+                        "Host: localhost\r\n" +

+                        //                    "Connection: close\r\n" +

+                        "\r\n";

+                output.write(request.getBytes(StandardCharsets.UTF_8));

+                output.flush();

+            }

+

+

+            byte[] buffer = new byte[48 * 1024];

+            List<Exchanger<Integer>> totals = new ArrayList<>();

+            for (Socket socket : sockets)

+            {

+                final Exchanger<Integer> x = new Exchanger<>();

+                totals.add(x);

+                final InputStream input = socket.getInputStream();

+

+                new Thread()

+                {

+                    @Override

+                    public void run()

+                    {

+                        int read=0;

+                        try

+                        {

+                            // look for CRLFCRLF

+                            StringBuilder header = new StringBuilder();

+                            int state=0;

+                            while (state<4 && header.length()<2048)

+                            {

+                                int ch=input.read();

+                                if (ch<0)

+                                    break;

+                                header.append((char)ch);

+                                switch(state)

+                                {

+                                    case 0:

+                                        if (ch=='\r')

+                                            state=1;

+                                        break;

+                                    case 1:

+                                        if (ch=='\n')

+                                            state=2;

+                                        else

+                                            state=0;

+                                        break;

+                                    case 2:

+                                        if (ch=='\r')

+                                            state=3;

+                                        else

+                                            state=0;

+                                        break;

+                                    case 3:

+                                        if (ch=='\n')

+                                            state=4;

+                                        else

+                                            state=0;

+                                        break;

+                                }

+                            }

+

+                            read=input.read(buffer);

+                        }

+                        catch (IOException e)

+                        {

+                            // e.printStackTrace();

+                        }

+                        finally

+                        {

+                            try

+                            {

+                                x.exchange(read);

+                            }

+                            catch (InterruptedException e)

+                            {

+                                e.printStackTrace();

+                            }

+                        }

+                    }

+                }.start();

+            }

+

+            for (Exchanger<Integer> x : totals)

+            {

+                Integer read = x.exchange(-1,10,TimeUnit.SECONDS);

+                Assert.assertEquals(-1,read.intValue());

+            }

+

+            // We could read everything, good.

+            for (Socket socket : sockets)

+                socket.close();

+            

+            _server.stop();

+        }

+    }

+}

diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/AsyncManipFilter.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/AsyncManipFilter.java
deleted file mode 100644
index 113ebf5..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/AsyncManipFilter.java
+++ /dev/null
@@ -1,103 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-
-import javax.servlet.AsyncContext;
-import javax.servlet.AsyncEvent;
-import javax.servlet.AsyncListener;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * Filter that merely manipulates the AsyncContext.
- * <p>
- * The pattern of manipulation is modeled after how DOSFilter behaves. The purpose of this filter is to test arbitrary filter chains that could see unintended
- * side-effects of async context manipulation.
- */
-public class AsyncManipFilter implements Filter, AsyncListener
-{
-    private static final Logger LOG = Log.getLogger(AsyncManipFilter.class);
-    private static final String MANIP_KEY = AsyncManipFilter.class.getName();
-
-    @Override
-    public void init(FilterConfig filterConfig) throws ServletException
-    {
-    }
-
-    @Override
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
-    {
-        LOG.debug("doFilter() - {}", chain);
-        AsyncContext ctx = (AsyncContext)request.getAttribute(MANIP_KEY);
-        if (ctx == null)
-        {
-            LOG.debug("Initial pass through: {}", chain);
-            ctx = request.startAsync();
-            ctx.addListener(this);
-            ctx.setTimeout(1000);
-            LOG.debug("AsyncContext: {}", ctx);
-            request.setAttribute(MANIP_KEY,ctx);
-            return;
-        }
-        else
-        {
-            LOG.debug("Second pass through: {}", chain);
-            chain.doFilter(request,response);
-        }
-    }
-
-    @Override
-    public void destroy()
-    {
-    }
-
-    @Override
-    public void onComplete(AsyncEvent event) throws IOException
-    {
-        LOG.debug("onComplete() {}",event);
-    }
-
-    @Override
-    public void onTimeout(AsyncEvent event) throws IOException
-    {
-        LOG.debug("onTimeout() {}",event.getAsyncContext());
-        event.getAsyncContext().dispatch();
-    }
-
-    @Override
-    public void onError(AsyncEvent event) throws IOException
-    {
-        LOG.debug("onError()",event.getThrowable());
-    }
-
-    @Override
-    public void onStartAsync(AsyncEvent event) throws IOException
-    {
-        LOG.debug("onTimeout() {}",event);
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/AsyncScheduledDispatchWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/AsyncScheduledDispatchWrite.java
deleted file mode 100644
index 1b5e270..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/AsyncScheduledDispatchWrite.java
+++ /dev/null
@@ -1,121 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import javax.servlet.AsyncContext;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-@SuppressWarnings("serial")
-public abstract class AsyncScheduledDispatchWrite extends TestDirContentServlet
-{
-    public static class Default extends AsyncScheduledDispatchWrite
-    {
-        public Default()
-        {
-            super(true);
-        }
-    }
-    
-    public static class Passed extends AsyncScheduledDispatchWrite
-    {
-        public Passed()
-        {
-            super(false);
-        }
-    }
-
-    private static class DispatchBack implements Runnable
-    {
-        private final AsyncContext ctx;
-
-        public DispatchBack(AsyncContext ctx)
-        {
-            this.ctx = ctx;
-        }
-
-        @Override
-        public void run()
-        {
-            ctx.dispatch();
-        }
-    }
-
-    private final boolean originalReqResp;
-    private ScheduledExecutorService scheduler;
-
-    public AsyncScheduledDispatchWrite(boolean originalReqResp)
-    {
-        this.originalReqResp = originalReqResp;
-    }
-
-    public void init(ServletConfig config) throws ServletException
-    {
-        super.init(config);
-        scheduler = Executors.newScheduledThreadPool(3);
-    }
-
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        Boolean suspended = (Boolean)request.getAttribute("SUSPENDED");
-        if (suspended == null || !suspended)
-        {
-            request.setAttribute("SUSPENDED",Boolean.TRUE);
-            AsyncContext ctx;
-            if (originalReqResp)
-            {
-                // Use Original Request & Response
-                ctx = request.startAsync();
-            }
-            else
-            {
-                // Pass Request & Response
-                ctx = request.startAsync(request,response);
-            }
-            ctx.setTimeout(0);
-            scheduler.schedule(new DispatchBack(ctx),500,TimeUnit.MILLISECONDS);
-        }
-        else
-        {
-            String fileName = request.getServletPath();
-            byte[] dataBytes = loadContentFileBytes(fileName);
-
-            response.setContentLength(dataBytes.length);
-
-            ServletOutputStream out = response.getOutputStream();
-
-            if (fileName.endsWith("txt"))
-                response.setContentType("text/plain");
-            else if (fileName.endsWith("mp3"))
-                response.setContentType("audio/mpeg");
-            response.setHeader("ETag","W/etag-" + fileName);
-
-            out.write(dataBytes);
-        }
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/AsyncTimeoutCompleteWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/AsyncTimeoutCompleteWrite.java
deleted file mode 100644
index 7dada2c..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/AsyncTimeoutCompleteWrite.java
+++ /dev/null
@@ -1,137 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.io.IOException;
-
-import javax.servlet.AsyncContext;
-import javax.servlet.AsyncEvent;
-import javax.servlet.AsyncListener;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Respond with requested content, but via AsyncContext manipulation.
- * <p>
- * 
- * <pre>
- *   1) startAsync
- *   2) AsyncContext.setTimeout()
- *   3) onTimeout()
- *   4) send-response
- *   5) AsyncContext.complete()
- * </pre>
- */
-@SuppressWarnings("serial")
-public abstract class AsyncTimeoutCompleteWrite extends TestDirContentServlet implements AsyncListener
-{
-    public static class Default extends AsyncTimeoutCompleteWrite
-    {
-        public Default()
-        {
-            super(true);
-        }
-    }
-    
-    public static class Passed extends AsyncTimeoutCompleteWrite
-    {
-        public Passed()
-        {
-            super(false);
-        }
-    }
-
-    private final boolean originalReqResp;
-
-    public AsyncTimeoutCompleteWrite(boolean originalReqResp)
-    {
-        this.originalReqResp = originalReqResp;
-    }
-
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        assertThat("'filename' request attribute shouldn't be declared",request.getAttribute("filename"),nullValue());
-
-        AsyncContext ctx = (AsyncContext)request.getAttribute(this.getClass().getName());
-        assertThat("AsyncContext (shouldn't be in request attribute)", ctx, nullValue());
-        
-        if (originalReqResp)
-        {
-            // Use Original Request & Response
-            ctx = request.startAsync();
-        }
-        else
-        {
-            // Pass Request & Response
-            ctx = request.startAsync(request,response);
-        }
-        String fileName = request.getServletPath();
-        request.setAttribute("filename",fileName);
-        ctx.addListener(this);
-        ctx.setTimeout(20);
-        
-        // Setup indication of a redispatch (which this scenario shouldn't do)
-        request.setAttribute(this.getClass().getName(),ctx);
-    }
-
-    @Override
-    public void onComplete(AsyncEvent event) throws IOException
-    {
-    }
-
-    @Override
-    public void onTimeout(AsyncEvent event) throws IOException
-    {
-        HttpServletRequest request = (HttpServletRequest)event.getSuppliedRequest();
-        HttpServletResponse response = (HttpServletResponse)event.getSuppliedResponse();
-
-        String fileName = (String)request.getAttribute("filename");
-        byte[] dataBytes = loadContentFileBytes(fileName);
-
-        response.setContentLength(dataBytes.length);
-
-        ServletOutputStream out = response.getOutputStream();
-
-        if (fileName.endsWith("txt"))
-            response.setContentType("text/plain");
-        else if (fileName.endsWith("mp3"))
-            response.setContentType("audio/mpeg");
-        response.setHeader("ETag","W/etag-" + fileName);
-
-        out.write(dataBytes);
-
-        event.getAsyncContext().complete();
-    }
-
-    @Override
-    public void onError(AsyncEvent event) throws IOException
-    {
-    }
-
-    @Override
-    public void onStartAsync(AsyncEvent event) throws IOException
-    {
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/AsyncTimeoutDispatchWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/AsyncTimeoutDispatchWrite.java
deleted file mode 100644
index 6cc744c..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/AsyncTimeoutDispatchWrite.java
+++ /dev/null
@@ -1,121 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-
-import javax.servlet.AsyncContext;
-import javax.servlet.AsyncEvent;
-import javax.servlet.AsyncListener;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-@SuppressWarnings("serial")
-public class AsyncTimeoutDispatchWrite extends TestDirContentServlet implements AsyncListener
-{
-    public static class Default extends AsyncTimeoutDispatchWrite
-    {
-        public Default()
-        {
-            super(true);
-        }
-    }
-    
-    public static class Passed extends AsyncTimeoutDispatchWrite
-    {
-        public Passed()
-        {
-            super(false);
-        }
-    }
-
-    private final boolean originalReqResp;
-
-    public AsyncTimeoutDispatchWrite(boolean originalReqResp)
-    {
-        this.originalReqResp = originalReqResp;
-    }
-
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        AsyncContext ctx = (AsyncContext)request.getAttribute(AsyncContext.class.getName());
-        if (ctx == null)
-        {
-            // First pass through
-            if (originalReqResp)
-            {
-                // Use Original Request & Response
-                ctx = request.startAsync();
-            }
-            else
-            {
-                // Pass Request & Response
-                ctx = request.startAsync(request,response);
-            }
-            ctx.addListener(this);
-            ctx.setTimeout(200);
-            request.setAttribute(AsyncContext.class.getName(),ctx);
-        }
-        else
-        {
-            // second pass through, as result of timeout -> dispatch
-            String fileName = request.getServletPath();
-            byte[] dataBytes = loadContentFileBytes(fileName);
-
-            response.setContentLength(dataBytes.length);
-
-            ServletOutputStream out = response.getOutputStream();
-
-            if (fileName.endsWith("txt"))
-                response.setContentType("text/plain");
-            else if (fileName.endsWith("mp3"))
-                response.setContentType("audio/mpeg");
-            response.setHeader("ETag","W/etag-" + fileName);
-
-            out.write(dataBytes);
-            // no need to call AsyncContext.complete() from here
-            // in fact, it will cause an IllegalStateException if we do
-            // ctx.complete();
-        }
-    }
-
-    @Override
-    public void onComplete(AsyncEvent event) throws IOException
-    {
-    }
-
-    @Override
-    public void onTimeout(AsyncEvent event) throws IOException
-    {
-        event.getAsyncContext().dispatch();
-    }
-
-    @Override
-    public void onError(AsyncEvent event) throws IOException
-    {
-    }
-
-    @Override
-    public void onStartAsync(AsyncEvent event) throws IOException
-    {
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipHandlerTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipHandlerTest.java
deleted file mode 100644
index ddb962b..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipHandlerTest.java
+++ /dev/null
@@ -1,223 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import static org.hamcrest.Matchers.contains;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.zip.GZIPInputStream;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.http.HttpTester;
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.LocalConnector;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHandler;
-import org.eclipse.jetty.servlets.gzip.GzipHandler;
-import org.eclipse.jetty.util.IO;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class GzipHandlerTest
-{
-    private static String __content =
-        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In quis felis nunc. "+
-        "Quisque suscipit mauris et ante auctor ornare rhoncus lacus aliquet. Pellentesque "+
-        "habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. "+
-        "Vestibulum sit amet felis augue, vel convallis dolor. Cras accumsan vehicula diam "+
-        "at faucibus. Etiam in urna turpis, sed congue mi. Morbi et lorem eros. Donec vulputate "+
-        "velit in risus suscipit lobortis. Aliquam id urna orci, nec sollicitudin ipsum. "+
-        "Cras a orci turpis. Donec suscipit vulputate cursus. Mauris nunc tellus, fermentum "+
-        "eu auctor ut, mollis at diam. Quisque porttitor ultrices metus, vitae tincidunt massa "+
-        "sollicitudin a. Vivamus porttitor libero eget purus hendrerit cursus. Integer aliquam "+
-        "consequat mauris quis luctus. Cras enim nibh, dignissim eu faucibus ac, mollis nec neque. "+
-        "Aliquam purus mauris, consectetur nec convallis lacinia, porta sed ante. Suspendisse "+
-        "et cursus magna. Donec orci enim, molestie a lobortis eu, imperdiet vitae neque.";
-
-    private static String __icontent = "BEFORE"+__content+"AFTER";
-            
-    private Server _server;
-    private LocalConnector _connector;
-
-    @Before
-    public void init() throws Exception
-    {
-        _server = new Server();
-        _connector = new LocalConnector(_server);
-        _server.addConnector(_connector);
-
-        GzipHandler gzipHandler = new GzipHandler();
-
-        ServletContextHandler context = new ServletContextHandler(gzipHandler,"/ctx");
-        ServletHandler servlets = context.getServletHandler();
-        
-        _server.setHandler(gzipHandler);
-        gzipHandler.setHandler(context);
-        context.setHandler(servlets);
-        servlets.addServletWithMapping(TestServlet.class,"/content");
-        servlets.addServletWithMapping(ForwardServlet.class,"/forward");
-        servlets.addServletWithMapping(IncludeServlet.class,"/include");
-        
-        _server.start();
-    }
-    
-    public static class TestServlet extends HttpServlet
-    {
-
-        @Override
-        protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException
-        {
-            PrintWriter writer = response.getWriter();
-            writer.write(__content);
-        }
-    }
-
-    public static class ForwardServlet extends HttpServlet
-    {
-        @Override
-        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-        {
-            getServletContext().getRequestDispatcher("/content").forward(request,response);
-        }
-    }
-
-    public static class IncludeServlet extends HttpServlet
-    {
-        @Override
-        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-        {
-            response.getWriter().write("BEFORE");
-            getServletContext().getRequestDispatcher("/content").include(request,response);
-            response.getWriter().write("AFTER");
-        }
-    }
-
-    @After
-    public void destroy() throws Exception
-    {
-        _server.stop();
-        _server.join();
-    }
-
-    @Test
-    public void testGzipHandler() throws Exception
-    {
-        // generated and parsed test
-        HttpTester.Request request = HttpTester.newRequest();
-        HttpTester.Response response;
-
-        request.setMethod("GET");
-        request.setVersion("HTTP/1.0");
-        request.setHeader("Host","tester");
-        request.setHeader("accept-encoding","gzip");
-        request.setURI("/ctx/content");
-
-        response = HttpTester.parseResponse(_connector.getResponses(request.generate()));
-
-        assertTrue(response.get("Content-Encoding").equalsIgnoreCase("gzip"));
-        assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-
-        InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes()));
-        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
-        IO.copy(testIn,testOut);
-
-        assertEquals(__content, testOut.toString("UTF8"));
-
-    }
-    
-    @Test
-    public void testForwardGzipHandler() throws Exception
-    {
-        // generated and parsed test
-        HttpTester.Request request = HttpTester.newRequest();
-        HttpTester.Response response;
-
-        request.setMethod("GET");
-        request.setVersion("HTTP/1.0");
-        request.setHeader("Host","tester");
-        request.setHeader("accept-encoding","gzip");
-        request.setURI("/ctx/forward");
-
-        response = HttpTester.parseResponse(_connector.getResponses(request.generate()));
-
-        assertTrue(response.get("Content-Encoding").equalsIgnoreCase("gzip"));
-        assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-
-        InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes()));
-        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
-        IO.copy(testIn,testOut);
-
-        assertEquals(__content, testOut.toString("UTF8"));
-    }
-    
-    @Test
-    public void testIncludeGzipHandler() throws Exception
-    {
-        // generated and parsed test
-        HttpTester.Request request = HttpTester.newRequest();
-        HttpTester.Response response;
-
-        request.setMethod("GET");
-        request.setVersion("HTTP/1.0");
-        request.setHeader("Host","tester");
-        request.setHeader("accept-encoding","gzip");
-        request.setURI("/ctx/include");
-
-        response = HttpTester.parseResponse(_connector.getResponses(request.generate()));
-        
-        assertTrue(response.get("Content-Encoding").equalsIgnoreCase("gzip"));
-        assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-
-        InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes()));
-        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
-        IO.copy(testIn,testOut);
-
-        assertEquals(__icontent, testOut.toString("UTF8"));
-    }
-    
-    @Test
-    public void testAddGetPaths()
-    {
-        GzipHandler gzip = new GzipHandler();
-        gzip.addIncludedPaths("/foo");
-        gzip.addIncludedPaths("^/bar.*$");
-        
-        String[] includedPaths = gzip.getIncludedPaths();
-        assertThat("Included Paths.size", includedPaths.length, is(2));
-        assertThat("Included Paths", Arrays.asList(includedPaths), contains("/foo","^/bar.*$"));
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
deleted file mode 100644
index aa61f3b..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
+++ /dev/null
@@ -1,662 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.security.DigestOutputStream;
-import java.security.MessageDigest;
-import java.util.EnumSet;
-import java.util.Enumeration;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.Inflater;
-import java.util.zip.InflaterInputStream;
-
-import javax.servlet.DispatcherType;
-import javax.servlet.Filter;
-import javax.servlet.Servlet;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.http.DateGenerator;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpTester;
-import org.eclipse.jetty.http.HttpTester.Response;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.servlet.ServletTester;
-import org.eclipse.jetty.servlets.GzipFilter;
-import org.eclipse.jetty.toolchain.test.IO;
-import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
-import org.eclipse.jetty.toolchain.test.TestingDir;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.hamcrest.Matchers;
-import org.junit.Assert;
-
-public class GzipTester
-{
-    private static final Logger LOG = Log.getLogger(GzipTester.class);
-
-    public static class ContentMetadata
-    {
-        public final long size;
-        public final String sha1;
-
-        public ContentMetadata(long size, String sha1checksum)
-        {
-            this.size = size;
-            this.sha1 = sha1checksum;
-        }
-    }
-
-    private Class<? extends Filter> gzipFilterClass = null;
-    private String encoding = "ISO8859_1";
-    private String userAgent = null;
-    private final ServletTester tester = new ServletTester();;
-    private TestingDir testdir;
-    private String accept;
-    private String compressionType;
-
-    public GzipTester(TestingDir testingdir, String compressionType, String accept)
-    {
-        this.testdir = testingdir;
-        this.compressionType = compressionType;
-        this.accept = accept;
-    }
-
-    public GzipTester(TestingDir testingdir, String compressionType)
-    {
-        this.testdir = testingdir;
-        this.compressionType = compressionType;
-        this.accept = compressionType;
-    }
-
-    public int getOutputBufferSize()
-    {
-        return tester.getConnector().getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().getOutputBufferSize();
-    }
-    
-    public ContentMetadata getResponseMetadata(Response response) throws Exception
-    {
-        long size = response.getContentBytes().length;
-
-        String contentEncoding = response.get("Content-Encoding");
-
-        ByteArrayInputStream bais = null;
-        InputStream in = null;
-        DigestOutputStream digester = null;
-        ByteArrayOutputStream uncompressedStream = null;
-        try
-        {
-            MessageDigest digest = MessageDigest.getInstance("SHA1");
-            bais = new ByteArrayInputStream(response.getContentBytes());
-
-            if (contentEncoding == null)
-            {
-                LOG.debug("No response content-encoding");
-                in = new PassThruInputStream(bais);
-            }
-            else if (contentEncoding.contains(GzipFilter.GZIP))
-            {
-                in = new GZIPInputStream(bais);
-            }
-            else if (contentEncoding.contains(GzipFilter.DEFLATE))
-            {
-                in = new InflaterInputStream(bais,new Inflater(true));
-            }
-            else
-            {
-                assertThat("Unexpected response content-encoding", contentEncoding, isEmptyOrNullString());
-            }
-            
-            uncompressedStream = new ByteArrayOutputStream((int)size); 
-
-            digester = new DigestOutputStream(uncompressedStream,digest);
-            IO.copy(in,digester);
-            
-            byte output[] = uncompressedStream.toByteArray();
-            String actualSha1Sum = Hex.asHex(digest.digest());
-            return new ContentMetadata(output.length,actualSha1Sum);
-        }
-        finally
-        {
-            IO.close(digester);
-            IO.close(in);
-            IO.close(bais);
-            IO.close(uncompressedStream);
-        }
-    }
-
-    public HttpTester.Response assertIsResponseGzipCompressed(String method, String filename) throws Exception
-    {
-        return assertIsResponseGzipCompressed(method,filename,filename,-1);
-    }
-
-    public HttpTester.Response assertIsResponseGzipCompressed(String method, String filename, long ifmodifiedsince) throws Exception
-    {
-        return assertIsResponseGzipCompressed(method,filename,filename,ifmodifiedsince);
-    }
-
-    public HttpTester.Response assertIsResponseGzipCompressed(String method, String requestedFilename, String serverFilename) throws Exception
-    {
-        return assertIsResponseGzipCompressed(method,requestedFilename,serverFilename,-1);
-    }
-
-    public HttpTester.Response assertNonStaticContentIsResponseGzipCompressed(String method, String path, String expected) throws Exception
-    {
-        HttpTester.Request request = HttpTester.newRequest();
-        HttpTester.Response response;
-
-        request.setMethod(method);
-        request.setVersion("HTTP/1.0");
-        request.setHeader("Host","tester");
-        request.setHeader("Accept-Encoding",accept);
-
-        if (this.userAgent != null)
-            request.setHeader("User-Agent",this.userAgent);
-        request.setURI("/context/" + path);
-
-        // Issue the request
-        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
-
-        int qindex = compressionType.indexOf(";");
-        if (qindex < 0)
-            Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType));
-        else
-            Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType.substring(0,qindex)));
-
-        ByteArrayInputStream bais = null;
-        InputStream in = null;
-        ByteArrayOutputStream out = null;
-        String actual = null;
-
-        try
-        {
-            bais = new ByteArrayInputStream(response.getContentBytes());
-            if (compressionType.startsWith(GzipFilter.GZIP))
-            {
-                in = new GZIPInputStream(bais);
-            }
-            else if (compressionType.startsWith(GzipFilter.DEFLATE))
-            {
-                in = new InflaterInputStream(bais,new Inflater(true));
-            }
-            out = new ByteArrayOutputStream();
-            IO.copy(in,out);
-
-            actual = out.toString(encoding);
-            assertThat("Uncompressed contents",actual,equalTo(expected));
-        }
-        finally
-        {
-            IO.close(out);
-            IO.close(in);
-            IO.close(bais);
-        }
-
-        return response;
-    }
-
-    public HttpTester.Response assertIsResponseGzipCompressed(String method, String requestedFilename, String serverFilename, long ifmodifiedsince)
-            throws Exception
-    {
-        HttpTester.Request request = HttpTester.newRequest();
-        HttpTester.Response response;
-
-        request.setMethod(method);
-        request.setVersion("HTTP/1.0");
-        request.setHeader("Host","tester");
-        request.setHeader("Accept-Encoding",compressionType);
-        if (ifmodifiedsince > 0)
-            request.setHeader(HttpHeader.IF_MODIFIED_SINCE.asString(),DateGenerator.formatDate(ifmodifiedsince));
-        if (this.userAgent != null)
-            request.setHeader("User-Agent",this.userAgent);
-        request.setURI("/context/" + requestedFilename);
-
-        // Issue the request
-        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
-
-        // Assert the response headers
-        // Assert.assertThat("Response.status",response.getStatus(),is(HttpServletResponse.SC_OK));
-
-        // Response headers should have either a Transfer-Encoding indicating chunked OR a Content-Length
-        /*
-         * TODO need to check for the 3rd option of EOF content. To do this properly you might need to look at both HTTP/1.1 and HTTP/1.0 requests String
-         * contentLength = response.get("Content-Length"); String transferEncoding = response.get("Transfer-Encoding");
-         * 
-         * boolean chunked = (transferEncoding != null) && (transferEncoding.indexOf("chunk") >= 0); if(!chunked) {
-         * Assert.assertThat("Response.header[Content-Length]",contentLength,notNullValue()); } else {
-         * Assert.assertThat("Response.header[Transfer-Encoding]",transferEncoding,notNullValue()); }
-         */
-
-        int qindex = compressionType.indexOf(";");
-        if (qindex < 0)
-            Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType));
-        else
-            Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType.substring(0,qindex)));
-
-        Assert.assertThat(response.get("ETag"),Matchers.startsWith("W/"));
-
-        // Assert that the decompressed contents are what we expect.
-        File serverFile = testdir.getFile(serverFilename);
-        String expected = IO.readToString(serverFile);
-        String actual = null;
-
-        ByteArrayInputStream bais = null;
-        InputStream in = null;
-        ByteArrayOutputStream out = null;
-        try
-        {
-            bais = new ByteArrayInputStream(response.getContentBytes());
-            if (compressionType.startsWith(GzipFilter.GZIP))
-            {
-                in = new GZIPInputStream(bais);
-            }
-            else if (compressionType.startsWith(GzipFilter.DEFLATE))
-            {
-                in = new InflaterInputStream(bais,new Inflater(true));
-            }
-            out = new ByteArrayOutputStream();
-            IO.copy(in,out);
-
-            actual = out.toString(encoding);
-            assertThat("Uncompressed contents",actual,equalTo(expected));
-        }
-        finally
-        {
-            IO.close(out);
-            IO.close(in);
-            IO.close(bais);
-        }
-
-        return response;
-    }
-
-    public HttpTester.Response assertIsResponseNotModified(String method, String requestedFilename, long ifmodifiedsince) throws Exception
-    {
-        HttpTester.Request request = HttpTester.newRequest();
-        HttpTester.Response response;
-
-        request.setMethod(method);
-        request.setVersion("HTTP/1.0");
-        request.setHeader("Host","tester");
-        request.setHeader("Accept-Encoding",compressionType);
-        if (ifmodifiedsince > 0)
-            request.setHeader(HttpHeader.IF_MODIFIED_SINCE.asString(),DateGenerator.formatDate(ifmodifiedsince));
-        if (this.userAgent != null)
-            request.setHeader("User-Agent",this.userAgent);
-        request.setURI("/context/" + requestedFilename);
-
-        // Issue the request
-        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
-
-        Assert.assertThat(response.getStatus(),Matchers.equalTo(304));
-        Assert.assertThat(response.get("ETag"),Matchers.startsWith("W/"));
-
-        return response;
-    }
-
-    /**
-     * Makes sure that the response contains an unfiltered file contents.
-     * <p>
-     * This is used to test exclusions and passthroughs in the GzipFilter.
-     * <p>
-     * An example is to test that it is possible to configure GzipFilter to not recompress content that shouldn't be compressed by the GzipFilter.
-     *
-     * @param requestedFilename
-     *            the filename used to on the GET request,.
-     * @param testResourceSha1Sum
-     *            the sha1sum file that contains the SHA1SUM checksum that will be used to verify that the response contents are what is intended.
-     * @param expectedContentType
-     */
-    public void assertIsResponseNotGzipFiltered(String requestedFilename, String testResourceSha1Sum, String expectedContentType) throws Exception
-    {
-        assertIsResponseNotGzipFiltered(requestedFilename,testResourceSha1Sum,expectedContentType,null);
-    }
-
-    /**
-     * Makes sure that the response contains an unfiltered file contents.
-     * <p>
-     * This is used to test exclusions and passthroughs in the GzipFilter.
-     * <p>
-     * An example is to test that it is possible to configure GzipFilter to not recompress content that shouldn't be compressed by the GzipFilter.
-     *
-     * @param requestedFilename
-     *            the filename used to on the GET request,.
-     * @param testResourceSha1Sum
-     *            the sha1sum file that contains the SHA1SUM checksum that will be used to verify that the response contents are what is intended.
-     * @param expectedContentType
-     * @param expectedContentEncoding
-     *            can be non-null in some circumstances, eg when dealing with pre-gzipped .svgz files
-     */
-    public void assertIsResponseNotGzipFiltered(String requestedFilename, String testResourceSha1Sum, String expectedContentType, String expectedContentEncoding)
-            throws Exception
-    {
-        HttpTester.Request request = HttpTester.newRequest();
-        HttpTester.Response response;
-
-        request.setMethod("GET");
-        request.setVersion("HTTP/1.0");
-        request.setHeader("Host","tester");
-        request.setHeader("Accept-Encoding",compressionType);
-        if (this.userAgent != null)
-            request.setHeader("User-Agent",this.userAgent);
-        request.setURI("/context/" + requestedFilename);
-
-        // Issue the request
-        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
-
-        dumpHeaders(requestedFilename + " / Response Headers",response);
-
-        // Assert the response headers
-        String prefix = requestedFilename + " / Response";
-        Assert.assertThat(prefix + ".status",response.getStatus(),is(HttpServletResponse.SC_OK));
-        Assert.assertThat(prefix + ".header[Content-Length]",response.get("Content-Length"),notNullValue());
-        Assert.assertThat(prefix + ".header[Content-Encoding] (should not be recompressed by GzipFilter)",response.get("Content-Encoding"),
-                expectedContentEncoding == null?nullValue():notNullValue());
-        if (expectedContentEncoding != null)
-            Assert.assertThat(prefix + ".header[Content-Encoding]",response.get("Content-Encoding"),is(expectedContentEncoding));
-        Assert.assertThat(prefix + ".header[Content-Type] (should have a Content-Type associated with it)",response.get("Content-Type"),notNullValue());
-        Assert.assertThat(prefix + ".header[Content-Type]",response.get("Content-Type"),is(expectedContentType));
-
-        Assert.assertThat(response.get("ETAG"),Matchers.startsWith("W/"));
-
-        ByteArrayInputStream bais = null;
-        DigestOutputStream digester = null;
-        try
-        {
-            MessageDigest digest = MessageDigest.getInstance("SHA1");
-            bais = new ByteArrayInputStream(response.getContentBytes());
-            digester = new DigestOutputStream(new NoOpOutputStream(),digest);
-            IO.copy(bais,digester);
-
-            String actualSha1Sum = Hex.asHex(digest.digest());
-            String expectedSha1Sum = loadExpectedSha1Sum(testResourceSha1Sum);
-            Assert.assertEquals(requestedFilename + " / SHA1Sum of content",expectedSha1Sum,actualSha1Sum);
-        }
-        finally
-        {
-            IO.close(digester);
-            IO.close(bais);
-        }
-    }
-
-    private void dumpHeaders(String prefix, HttpTester.Message message)
-    {
-        LOG.debug("dumpHeaders: {}",prefix);
-        Enumeration<String> names = message.getFieldNames();
-        while (names.hasMoreElements())
-        {
-            String name = names.nextElement();
-            String value = message.get(name);
-            LOG.debug("dumpHeaders:   {} = {}",name,value);
-        }
-    }
-
-    private String loadExpectedSha1Sum(String testResourceSha1Sum) throws IOException
-    {
-        File sha1File = MavenTestingUtils.getTestResourceFile(testResourceSha1Sum);
-        String contents = IO.readToString(sha1File);
-        Pattern pat = Pattern.compile("^[0-9A-Fa-f]*");
-        Matcher mat = pat.matcher(contents);
-        Assert.assertTrue("Should have found HEX code in SHA1 file: " + sha1File,mat.find());
-        return mat.group();
-    }
-
-    public HttpTester.Response executeRequest(String method, String path, int idleFor, TimeUnit idleUnit) throws Exception
-    {
-        HttpTester.Request request = HttpTester.newRequest();
-
-        request.setMethod(method);
-        request.setVersion("HTTP/1.1");
-        request.setHeader("Host","tester");
-        request.setHeader("Accept-Encoding",accept);
-        request.setHeader("Connection","close");
-
-        if (this.userAgent != null)
-        {
-            request.setHeader("User-Agent",this.userAgent);
-        }
-        
-        request.setURI(path);
-
-        // Issue the request
-        return HttpTester.parseResponse(tester.getResponses(request.generate(),idleFor,idleUnit));
-    }
-
-    public String readResponse(HttpTester.Response response) throws IOException, UnsupportedEncodingException
-    {
-        String actual = null;
-        InputStream in = null;
-        ByteArrayOutputStream out = null;
-        try
-        {
-            byte[] content = response.getContentBytes();
-            if (content != null)
-                actual = new String(response.getContentBytes(),encoding);
-            else
-                actual = "";
-        }
-        finally
-        {
-            IO.close(out);
-            IO.close(in);
-        }
-        return actual;
-    }
-
-    /**
-     * Generate string content of arbitrary length.
-     *
-     * @param length
-     *            the length of the string to generate.
-     * @return the string content.
-     */
-    public String generateContent(int length)
-    {
-        StringBuilder builder = new StringBuilder();
-        do
-        {
-            builder.append("Lorem ipsum dolor sit amet, consectetur adipiscing elit. In quis felis nunc.\n");
-            builder.append("Quisque suscipit mauris et ante auctor ornare rhoncus lacus aliquet. Pellentesque\n");
-            builder.append("habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.\n");
-            builder.append("Vestibulum sit amet felis augue, vel convallis dolor. Cras accumsan vehicula diam\n");
-            builder.append("at faucibus. Etiam in urna turpis, sed congue mi. Morbi et lorem eros. Donec vulputate\n");
-            builder.append("velit in risus suscipit lobortis. Aliquam id urna orci, nec sollicitudin ipsum.\n");
-            builder.append("Cras a orci turpis. Donec suscipit vulputate cursus. Mauris nunc tellus, fermentum\n");
-            builder.append("eu auctor ut, mollis at diam. Quisque porttitor ultrices metus, vitae tincidunt massa\n");
-            builder.append("sollicitudin a. Vivamus porttitor libero eget purus hendrerit cursus. Integer aliquam\n");
-            builder.append("consequat mauris quis luctus. Cras enim nibh, dignissim eu faucibus ac, mollis nec neque.\n");
-            builder.append("Aliquam purus mauris, consectetur nec convallis lacinia, porta sed ante. Suspendisse\n");
-            builder.append("et cursus magna. Donec orci enim, molestie a lobortis eu, imperdiet vitae neque.\n");
-        }
-        while (builder.length() < length);
-
-        // Make sure we are exactly at requested length. (truncate the extra)
-        if (builder.length() > length)
-        {
-            builder.setLength(length);
-        }
-
-        return builder.toString();
-    }
-
-    public String getEncoding()
-    {
-        return encoding;
-    }
-
-    /**
-     * Create a file on the server resource path of a specified filename and size.
-     *
-     * @param filename
-     *            the filename to create
-     * @param filesize
-     *            the file size to create (Note: this isn't suitable for creating large multi-megabyte files)
-     */
-    public File prepareServerFile(String filename, int filesize) throws IOException
-    {
-        File dir = testdir.getDir();
-        File testFile = new File(dir,filename);
-        // Make sure we have a uniq filename (to work around windows File.delete bug)
-        int i = 0;
-        while (testFile.exists())
-        {
-            testFile = new File(dir,(i++) + "-" + filename);
-        }
-
-        FileOutputStream fos = null;
-        ByteArrayInputStream in = null;
-        try
-        {
-            fos = new FileOutputStream(testFile,false);
-            in = new ByteArrayInputStream(generateContent(filesize).getBytes(encoding));
-            IO.copy(in,fos);
-            return testFile;
-        }
-        finally
-        {
-            IO.close(in);
-            IO.close(fos);
-        }
-    }
-
-    /**
-     * Copy a src/test/resource file into the server tree for eventual serving.
-     *
-     * @param filename
-     *            the filename to look for in src/test/resources
-     */
-    public void copyTestServerFile(String filename) throws IOException
-    {
-        File srcFile = MavenTestingUtils.getTestResourceFile(filename);
-        File testFile = testdir.getFile(filename);
-
-        IO.copy(srcFile,testFile);
-    }
-
-    /**
-     * Set the servlet that provides content for the GzipFilter in being tested.
-     *
-     * @param servletClass
-     *            the servlet that will provide content.
-     * @return the FilterHolder for configuring the GzipFilter's initParameters with
-     */
-    public FilterHolder setContentServlet(Class<? extends Servlet> servletClass) throws IOException
-    {
-        tester.setContextPath("/context");
-        tester.setResourceBase(testdir.getDir().getCanonicalPath());
-        ServletHolder servletHolder = tester.addServlet(servletClass,"/");
-        servletHolder.setInitParameter("baseDir",testdir.getDir().getAbsolutePath());
-        servletHolder.setInitParameter("etags","true");
-
-        if (gzipFilterClass != null)
-        {
-            FilterHolder holder = tester.addFilter(gzipFilterClass,"/*",EnumSet.of(DispatcherType.REQUEST));
-            holder.setInitParameter("vary","Accept-Encoding");
-            return holder;
-        }
-        else
-        {
-            return null;
-        }
-    }
-
-    public Class<? extends Filter> getGzipFilterClass()
-    {
-        return gzipFilterClass;
-    }
-    
-    @Deprecated
-    public void setGzipFilterClass(Class<? extends Filter> gzipFilterClass)
-    {
-        this.gzipFilterClass = gzipFilterClass;
-    }
-
-    public void setEncoding(String encoding)
-    {
-        this.encoding = encoding;
-    }
-
-    public void setUserAgent(String ua)
-    {
-        this.userAgent = ua;
-    }
-
-    public void addMimeType(String extension, String mimetype)
-    {
-        this.tester.getContext().getMimeTypes().addMimeMapping(extension,mimetype);
-    }
-
-    /**
-     * Add an arbitrary filter to the test case.
-     * 
-     * @param holder
-     *            the filter to add
-     * @param pathSpec
-     *            the path spec for this filter
-     * @param dispatches
-     *            the set of {@link DispatcherType} to associate with this filter
-     */
-    public void addFilter(FilterHolder holder, String pathSpec, EnumSet<DispatcherType> dispatches) throws IOException
-    {
-        tester.addFilter(holder,pathSpec,dispatches);
-    }
-
-    public void start() throws Exception
-    {
-        Assert.assertThat("No servlet defined yet.  Did you use #setContentServlet()?",tester,notNullValue());
-        
-        if (LOG.isDebugEnabled())
-        {
-            tester.dumpStdErr();
-        }
-        tester.start();
-    }
-
-    public void stop()
-    {
-        // NOTE: Do not cleanup the testdir. Failures can't be diagnosed if you do that.
-        // IO.delete(testdir.getDir()):
-        try
-        {
-            tester.stop();
-        }
-        catch (Exception e)
-        {
-            // Don't toss this out into Junit as this would be the last exception
-            // that junit will report as being the cause of the test failure.
-            // when in reality, the earlier setup issue is the real cause.
-            e.printStackTrace(System.err);
-        }
-    }
-
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/Hex.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/Hex.java
deleted file mode 100644
index 70f3ec6..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/Hex.java
+++ /dev/null
@@ -1,76 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-public final class Hex
-{
-    private static final char[] hexcodes = "0123456789abcdef".toCharArray();
-
-    public static byte[] asByteArray(String id, int size)
-    {
-        if ((id.length() < 0) || (id.length() > (size * 2)))
-        {
-            throw new IllegalArgumentException(String.format("Invalid ID length of <%d> expected range of <0> to <%d>",id.length(),(size * 2)));
-        }
-
-        byte buf[] = new byte[size];
-        byte hex;
-        int len = id.length();
-
-        int idx = (int)Math.floor(((size * 2) - (double)len) / 2);
-        int i = 0;
-        if ((len % 2) != 0)
-        { // deal with odd numbered chars
-            i -= 1;
-        }
-
-        for (; i < len; i++)
-        {
-            hex = 0;
-            if (i >= 0)
-            {
-                hex = (byte)(Character.digit(id.charAt(i),16) << 4);
-            }
-            i++;
-            hex += (byte)(Character.digit(id.charAt(i),16));
-
-            buf[idx] = hex;
-            idx++;
-        }
-
-        return buf;
-    }
-
-    public static String asHex(byte buf[])
-    {
-        int len = buf.length;
-        char out[] = new char[len * 2];
-        for (int i = 0; i < len; i++)
-        {
-            out[i * 2] = hexcodes[(buf[i] & 0xF0) >> 4];
-            out[(i * 2) + 1] = hexcodes[(buf[i] & 0x0F)];
-        }
-        return String.valueOf(out);
-    }
-
-    private Hex()
-    {
-        /* prevent instantiation */
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/NoOpOutputStream.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/NoOpOutputStream.java
deleted file mode 100644
index 2493c6d..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/NoOpOutputStream.java
+++ /dev/null
@@ -1,58 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * Stream that does nothing. (Used by SHA1SUM routines)
- */
-public class NoOpOutputStream extends OutputStream
-{
-    @Override
-    public void close() throws IOException
-    {
-        /* noop */
-    }
-    
-    @Override
-    public void flush() throws IOException
-    {
-        /* noop */
-    }
-    
-    @Override
-    public void write(byte[] b) throws IOException
-    {
-        /* noop */
-    }
-    
-    @Override
-    public void write(byte[] b, int off, int len) throws IOException
-    {
-        /* noop */
-    }
-    
-    @Override
-    public void write(int b) throws IOException
-    {
-        /* noop */
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/PassThruInputStream.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/PassThruInputStream.java
deleted file mode 100644
index 83ee08e..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/PassThruInputStream.java
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.FilterInputStream;
-import java.io.InputStream;
-
-/**
- * A simple pass-through input stream.
- * <p>
- * Used in some test cases where a proper resource open/close is needed for
- * some potentially optional layers of the input stream.
- */
-public class PassThruInputStream extends FilterInputStream
-{
-    public PassThruInputStream(InputStream in)
-    {
-        super(in);
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestDirContentServlet.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestDirContentServlet.java
deleted file mode 100644
index 979cd70..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestDirContentServlet.java
+++ /dev/null
@@ -1,74 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-
-import org.eclipse.jetty.toolchain.test.PathAssert;
-import org.eclipse.jetty.util.IO;
-
-@SuppressWarnings("serial")
-public class TestDirContentServlet extends HttpServlet
-{
-    private File basedir;
-
-    @Override
-    public void init(ServletConfig config) throws ServletException
-    {
-        basedir = new File(config.getInitParameter("baseDir"));
-    }
-
-    public File getTestFile(String filename)
-    {
-        File testfile = new File(basedir,filename);
-        PathAssert.assertFileExists("Content File should exist",testfile);
-        return testfile;
-    }
-
-    protected byte[] loadContentFileBytes(final String fileName) throws IOException
-    {
-        String relPath = fileName;
-        relPath = relPath.replaceFirst("^/context/","");
-        relPath = relPath.replaceFirst("^/","");
-
-        File contentFile =  getTestFile(relPath);
-
-        FileInputStream in = null;
-        ByteArrayOutputStream out = null;
-        try
-        {
-            in = new FileInputStream(contentFile);
-            out = new ByteArrayOutputStream();
-            IO.copy(in,out);
-            return out.toByteArray();
-        }
-        finally
-        {
-            IO.close(out);
-            IO.close(in);
-        }
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestMinGzipSizeServlet.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestMinGzipSizeServlet.java
deleted file mode 100644
index 33aaa4f..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestMinGzipSizeServlet.java
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.http.MimeTypes;
-
-/**
- * Test servlet for testing against unusual minGzip configurable.
- */
-@SuppressWarnings("serial")
-public class TestMinGzipSizeServlet extends TestDirContentServlet
-{
-    private MimeTypes mimeTypes;
-
-    @Override
-    public void init(ServletConfig config) throws ServletException
-    {
-        super.init(config);
-        mimeTypes = new MimeTypes();
-    }
-
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        String fileName = request.getServletPath();
-        byte[] dataBytes = loadContentFileBytes(fileName);
-
-        response.setContentLength(dataBytes.length);
-        response.setHeader("ETag","W/etag-"+fileName);
-        if (fileName.endsWith(".js"))
-        {
-            // intentionally long-form content type to test ";" splitting in code
-            response.setContentType("text/javascript; charset=utf-8");
-        }
-        else
-        {
-            String mime = mimeTypes.getMimeByExtension(fileName);
-            if (mime != null)
-                response.setContentType(mime);
-        }
-        ServletOutputStream out = response.getOutputStream();
-        out.write(dataBytes);
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletBufferTypeLengthWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletBufferTypeLengthWrite.java
deleted file mode 100644
index fbda12f..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletBufferTypeLengthWrite.java
+++ /dev/null
@@ -1,67 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.server.HttpOutput;
-
-/**
- * A sample servlet to serve static content, using a order of construction that has caused problems for
- * {@link GzipFilter} in the past.
- *
- * Using a real-world pattern of:
- *
- * <pre>
- *  1) get stream
- *  2) set content type
- *  2) set content length
- *  4) write
- * </pre>
- *
- * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
- */
-@SuppressWarnings("serial")
-public class TestServletBufferTypeLengthWrite extends TestDirContentServlet
-{
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        String fileName = request.getServletPath();
-        byte[] dataBytes = loadContentFileBytes(fileName);
-
-        ServletOutputStream out = response.getOutputStream();
-
-        if (fileName.endsWith("txt"))
-            response.setContentType("text/plain");
-        else if (fileName.endsWith("mp3"))
-            response.setContentType("audio/mpeg");
-        response.setHeader("ETag","W/etag-"+fileName);
-
-        response.setContentLength(dataBytes.length);
-        
-        ((HttpOutput)out).write(ByteBuffer.wrap(dataBytes).asReadOnlyBuffer());
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletLengthStreamTypeWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletLengthStreamTypeWrite.java
deleted file mode 100644
index d5a09c8..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletLengthStreamTypeWrite.java
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * A sample servlet to serve static content, using a order of construction that has caused problems for
- * {@link GzipFilter} in the past.
- *
- * Using a real-world pattern of:
- *
- * <pre>
- *  1) set content length
- *  2) get stream
- *  3) set content type
- *  4) write
- * </pre>
- *
- * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
- */
-@SuppressWarnings("serial")
-public class TestServletLengthStreamTypeWrite extends TestDirContentServlet
-{
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        String fileName = request.getServletPath();
-        byte[] dataBytes = loadContentFileBytes(fileName);
-
-        response.setContentLength(dataBytes.length);
-
-        ServletOutputStream out = response.getOutputStream();
-
-        if (fileName.endsWith("txt"))
-            response.setContentType("text/plain");
-        else if (fileName.endsWith("mp3"))
-            response.setContentType("audio/mpeg");
-        response.setHeader("ETag","W/etag-"+fileName);
-
-        out.write(dataBytes);
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletLengthTypeStreamWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletLengthTypeStreamWrite.java
deleted file mode 100644
index 5a94ee9..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletLengthTypeStreamWrite.java
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * A sample servlet to serve static content, using a order of construction that has caused problems for
- * {@link GzipFilter} in the past.
- *
- * Using a real-world pattern of:
- *
- * <pre>
- *  1) set content length
- *  2) set content type
- *  3) get stream
- *  4) write
- * </pre>
- *
- * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
- */
-@SuppressWarnings("serial")
-public class TestServletLengthTypeStreamWrite extends TestDirContentServlet
-{
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        String fileName = request.getServletPath();
-        byte[] dataBytes = loadContentFileBytes(fileName);
-
-        response.setContentLength(dataBytes.length);
-
-        if (fileName.endsWith("txt"))
-            response.setContentType("text/plain");
-        else if (fileName.endsWith("mp3"))
-            response.setContentType("audio/mpeg");
-        response.setHeader("ETag","W/etag-"+fileName);
-
-        ServletOutputStream out = response.getOutputStream();
-        out.write(dataBytes);
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletStreamLengthTypeWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletStreamLengthTypeWrite.java
deleted file mode 100644
index f0b4ddb..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletStreamLengthTypeWrite.java
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * A sample servlet to serve static content, using a order of construction that has caused problems for
- * {@link GzipFilter} in the past.
- *
- * Using a real-world pattern of:
- *
- * <pre>
- *  1) get stream
- *  2) set content length
- *  3) set content type
- *  4) write
- * </pre>
- *
- * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
- */
-@SuppressWarnings("serial")
-public class TestServletStreamLengthTypeWrite extends TestDirContentServlet
-{
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        String fileName = request.getServletPath();
-        byte[] dataBytes = loadContentFileBytes(fileName);
-
-        ServletOutputStream out = response.getOutputStream();
-
-        response.setContentLength(dataBytes.length);
-
-        if (fileName.endsWith("txt"))
-            response.setContentType("text/plain");
-        else if (fileName.endsWith("mp3"))
-            response.setContentType("audio/mpeg");
-        response.setHeader("ETag","W/etag-"+fileName);
-
-        out.write(dataBytes);
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletStreamLengthTypeWriteWithFlush.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletStreamLengthTypeWriteWithFlush.java
deleted file mode 100644
index 92ad235..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletStreamLengthTypeWriteWithFlush.java
+++ /dev/null
@@ -1,70 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * A sample servlet to serve static content, using a order of construction that has caused problems for
- * {@link GzipFilter} in the past.
- * 
- * Using a real-world pattern of:
- * 
- * <pre>
- *  1) get stream
- *  2) set content length
- *  3) set content type
- *  4) write and flush
- * </pre>
- * 
- * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
- */
-@SuppressWarnings("serial")
-public class TestServletStreamLengthTypeWriteWithFlush extends TestDirContentServlet
-{
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        String fileName = request.getServletPath();
-        byte[] dataBytes = loadContentFileBytes(fileName);
-
-        ServletOutputStream out = response.getOutputStream();
-
-        // set content-length of uncompressed content (GzipFilter should handle this)
-        response.setContentLength(dataBytes.length);
-        
-        if (fileName.endsWith("txt"))
-            response.setContentType("text/plain");
-        else if (fileName.endsWith("mp3"))
-            response.setContentType("audio/mpeg");
-        response.setHeader("ETag","W/etag-"+fileName);
-
-        for ( int i = 0 ; i < dataBytes.length ; i++)
-        {
-            out.write(dataBytes[i]);
-            // flush using response object (not the stream itself)
-            response.flushBuffer();
-        }
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletStreamTypeLengthWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletStreamTypeLengthWrite.java
deleted file mode 100644
index c4743a0..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletStreamTypeLengthWrite.java
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * A sample servlet to serve static content, using a order of construction that has caused problems for
- * {@link GzipFilter} in the past.
- *
- * Using a real-world pattern of:
- *
- * <pre>
- *  1) get stream
- *  2) set content type
- *  2) set content length
- *  4) write
- * </pre>
- *
- * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
- */
-@SuppressWarnings("serial")
-public class TestServletStreamTypeLengthWrite extends TestDirContentServlet
-{
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        String fileName = request.getServletPath();
-        byte[] dataBytes = loadContentFileBytes(fileName);
-
-        ServletOutputStream out = response.getOutputStream();
-
-        if (fileName.endsWith("txt"))
-            response.setContentType("text/plain");
-        else if (fileName.endsWith("mp3"))
-            response.setContentType("audio/mpeg");
-        response.setHeader("ETag","W/etag-"+fileName);
-
-        response.setContentLength(dataBytes.length);
-
-        out.write(dataBytes);
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletTypeLengthStreamWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletTypeLengthStreamWrite.java
deleted file mode 100644
index c1ec9f9..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletTypeLengthStreamWrite.java
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * A sample servlet to serve static content, using a order of construction that has caused problems for
- * {@link GzipFilter} in the past.
- *
- * Using a real-world pattern of:
- *
- * <pre>
- *  1) set content type
- *  2) set content length
- *  3) get stream
- *  4) write
- * </pre>
- *
- * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
- */
-@SuppressWarnings("serial")
-public class TestServletTypeLengthStreamWrite extends TestDirContentServlet
-{
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        String fileName = request.getServletPath();
-        byte[] dataBytes = loadContentFileBytes(fileName);
-
-        if (fileName.endsWith("txt"))
-            response.setContentType("text/plain");
-        else if (fileName.endsWith("mp3"))
-            response.setContentType("audio/mpeg");
-        response.setHeader("ETag","W/etag-"+fileName);
-
-        response.setContentLength(dataBytes.length);
-
-        ServletOutputStream out = response.getOutputStream();
-        out.write(dataBytes);
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletTypeStreamLengthWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletTypeStreamLengthWrite.java
deleted file mode 100644
index f95e3f6..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletTypeStreamLengthWrite.java
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * A sample servlet to serve static content, using a order of construction that has caused problems for
- * {@link GzipFilter} in the past.
- *
- * Using a real-world pattern of:
- *
- * <pre>
- *  1) set content type
- *  2) get stream
- *  3) set content length
- *  4) write
- * </pre>
- *
- * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
- */
-@SuppressWarnings("serial")
-public class TestServletTypeStreamLengthWrite extends TestDirContentServlet
-{
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        String fileName = request.getServletPath();
-        byte[] dataBytes = loadContentFileBytes(fileName);
-
-        if (fileName.endsWith("txt"))
-            response.setContentType("text/plain");
-        else if (fileName.endsWith("mp3"))
-            response.setContentType("audio/mpeg");
-        response.setHeader("ETag","W/etag-"+fileName);
-
-        ServletOutputStream out = response.getOutputStream();
-
-        response.setContentLength(dataBytes.length);
-
-        out.write(dataBytes);
-    }
-}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestStaticMimeTypeServlet.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestStaticMimeTypeServlet.java
deleted file mode 100644
index aced4b4..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestStaticMimeTypeServlet.java
+++ /dev/null
@@ -1,81 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.servlets.gzip;
-
-import java.io.IOException;
-
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.http.MimeTypes;
-
-/**
- * Test servlet for testing against unusual MimeTypes and Content-Types.
- */
-@SuppressWarnings("serial")
-public class TestStaticMimeTypeServlet extends TestDirContentServlet
-{
-    private MimeTypes mimeTypes;
-
-    @Override
-    public void init(ServletConfig config) throws ServletException
-    {
-        super.init(config);
-        mimeTypes = new MimeTypes();
-        // Some real world, yet not terribly common, mime type mappings.
-        mimeTypes.addMimeMapping("bz2","application/bzip2");
-        mimeTypes.addMimeMapping("bmp","image/bmp");
-        mimeTypes.addMimeMapping("tga","application/tga");
-        mimeTypes.addMimeMapping("xcf","image/xcf");
-        mimeTypes.addMimeMapping("jp2","image/jpeg2000");
-
-        // Some of the other gzip mime-types seen in the wild.
-        // NOTE: Using oddball extensions just so that the calling request can specify
-        //       which strange mime type to use.
-        mimeTypes.addMimeMapping("x-gzip","application/x-gzip");
-        mimeTypes.addMimeMapping("x-gunzip","application/x-gunzip");
-        mimeTypes.addMimeMapping("gzipped","application/gzippped");
-        mimeTypes.addMimeMapping("gzip-compressed","application/gzip-compressed");
-        mimeTypes.addMimeMapping("x-compressed","application/x-compressed");
-        mimeTypes.addMimeMapping("x-compress","application/x-compress");
-        mimeTypes.addMimeMapping("gzipdoc","gzip/document");
-    }
-
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-    {
-        String fileName = request.getServletPath();
-        byte[] dataBytes = loadContentFileBytes(fileName);
-
-        response.setContentLength(dataBytes.length);
-        response.setHeader("ETag","W/etag-"+fileName);
-
-        String mime = mimeTypes.getMimeByExtension(fileName);
-        if (mime == null)
-            response.setContentType("application/octet-stream");
-        else
-            response.setContentType(mime);
-
-        ServletOutputStream out = response.getOutputStream();
-        out.write(dataBytes);
-    }
-}
diff --git a/jetty-servlets/src/test/resources/jetty-logging.properties b/jetty-servlets/src/test/resources/jetty-logging.properties
index 6502639..3ceebb8 100644
--- a/jetty-servlets/src/test/resources/jetty-logging.properties
+++ b/jetty-servlets/src/test/resources/jetty-logging.properties
@@ -2,6 +2,5 @@
 #org.eclipse.jetty.LEVEL=DEBUG
 #org.eclipse.jetty.servlets.LEVEL=DEBUG
 #org.eclipse.jetty.servlet.ServletTester.LEVEL=DEBUG
-#org.eclipse.jetty.servlets.GzipFilter.LEVEL=DEBUG
 #org.eclipse.jetty.servlets.QoSFilter.LEVEL=DEBUG
 #org.eclipse.jetty.servlets.DoSFilter.LEVEL=DEBUG
diff --git a/jetty-spdy/pom.xml b/jetty-spdy/pom.xml
deleted file mode 100644
index 7de0413..0000000
--- a/jetty-spdy/pom.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty</groupId>
-        <artifactId>jetty-project</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
-    </parent>
-
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>org.eclipse.jetty.spdy</groupId>
-    <artifactId>spdy-parent</artifactId>
-    <packaging>pom</packaging>
-    <name>Jetty :: SPDY :: Parent</name>
-    <url>http://www.eclipse.org/jetty</url>
-
-    <modules>
-        <module>spdy-core</module>
-        <module>spdy-client</module>
-        <module>spdy-server</module>
-        <module>spdy-http-common</module>
-        <module>spdy-http-server</module>
-        <module>spdy-http-client-transport</module>
-        <module>spdy-example-webapp</module>
-        <module>spdy-alpn-tests</module>
-    </modules>
-
-    <profiles>
-      <profile>
-        <id>npn</id>
-        <activation>
-          <jdk>1.7</jdk>
-        </activation>
-        <modules>
-<!--
-          <module>spdy-npn-tests</module>
--->
-        </modules>
-      </profile>
-    </profiles>
-
-    <build>
-        <plugins>
-            <plugin>
-                <artifactId>maven-pmd-plugin</artifactId>
-                <configuration>
-                    <skip>true</skip>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>manifest</goal>
-                        </goals>
-                        <configuration>
-                            <instructions>
-                                <Export-Package>org.eclipse.jetty.spdy.*;version="9.1"</Export-Package>
-                                <Import-Package>org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
-                                <_nouses>true</_nouses>
-                            </instructions>
-                          </configuration>
-                       </execution>
-                  </executions>
-            </plugin>
-            <plugin>
-              <groupId>org.apache.maven.plugins</groupId>
-              <artifactId>maven-jar-plugin</artifactId>
-              <configuration>
-                  <archive>
-                      <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-                  </archive>
-              </configuration>
-            </plugin>
-        </plugins>
-    </build>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.eclipse.jetty.toolchain</groupId>
-            <artifactId>jetty-test-helper</artifactId>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-
-</project>
diff --git a/jetty-spdy/spdy-alpn-tests/pom.xml b/jetty-spdy/spdy-alpn-tests/pom.xml
deleted file mode 100644
index 5268db2..0000000
--- a/jetty-spdy/spdy-alpn-tests/pom.xml
+++ /dev/null
@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.spdy</groupId>
-        <artifactId>spdy-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
-    </parent>
-
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>spdy-alpn-tests</artifactId>
-    <name>Jetty :: SPDY :: ALPN Tests</name>
-
-    <build>
-        <plugins>
-            <plugin>
-                <artifactId>maven-dependency-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>copy</id>
-                        <phase>generate-resources</phase>
-                        <goals>
-                            <goal>copy</goal>
-                        </goals>
-                        <configuration>
-                            <artifactItems>
-                                <artifactItem>
-                                    <groupId>org.mortbay.jetty.alpn</groupId>
-                                    <artifactId>alpn-boot</artifactId>
-                                    <version>${alpn.version}</version>
-                                    <type>jar</type>
-                                    <overWrite>false</overWrite>
-                                    <outputDirectory>${project.build.directory}/alpn</outputDirectory>
-                                </artifactItem>
-                            </artifactItems>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <argLine>-Xbootclasspath/p:${project.build.directory}/alpn/alpn-boot-${alpn.version}.jar</argLine>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.eclipse.jetty.alpn</groupId>
-            <artifactId>alpn-api</artifactId>
-            <version>${alpn.api.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-alpn-server</artifactId>
-            <version>${project.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-server</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-server</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-http-server</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-http-server</artifactId>
-            <version>${project.version}</version>
-            <classifier>tests</classifier>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-    
-</project>
diff --git a/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/ALPNNegotiationTest.java b/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/ALPNNegotiationTest.java
deleted file mode 100644
index 007e52f..0000000
--- a/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/ALPNNegotiationTest.java
+++ /dev/null
@@ -1,198 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.List;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocket;
-
-import org.eclipse.jetty.alpn.ALPN;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class ALPNNegotiationTest extends AbstractALPNTest
-{
-    @Test
-    public void testClientAdvertisingHTTPServerSpeaksHTTP() throws Exception
-    {
-        InetSocketAddress address = prepare();
-        connector.addConnectionFactory(new HttpConnectionFactory());
-
-        SslContextFactory sslContextFactory = newSslContextFactory();
-        sslContextFactory.start();
-        SSLContext sslContext = sslContextFactory.getSslContext();
-
-        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()))
-        {
-            client.setUseClientMode(true);
-            client.setSoTimeout(5000);
-
-            ALPN.put(client, new ALPN.ClientProvider()
-            {
-                @Override
-                public void unsupported()
-                {
-                }
-
-                @Override
-                public List<String> protocols()
-                {
-                    return Arrays.asList("http/1.1");
-                }
-
-                @Override
-                public void selected(String protocol)
-                {
-                    Assert.assertEquals("http/1.1", protocol);
-                }
-            });
-
-            client.startHandshake();
-
-            // Verify that the server really speaks http/1.1
-
-            OutputStream output = client.getOutputStream();
-            output.write(("" +
-                    "GET / HTTP/1.1\r\n" +
-                    "Host: localhost:" + address.getPort() + "\r\n" +
-                    "\r\n" +
-                    "").getBytes(StandardCharsets.UTF_8));
-            output.flush();
-
-            InputStream input = client.getInputStream();
-            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
-            String line = reader.readLine();
-            Assert.assertTrue(line.contains(" 404 "));
-        }
-    }
-
-    @Test
-    public void testClientAdvertisingMultipleProtocolsServerSpeaksHTTPWhenNegotiated() throws Exception
-    {
-        InetSocketAddress address = prepare();
-        connector.addConnectionFactory(new HttpConnectionFactory());
-
-        SslContextFactory sslContextFactory = newSslContextFactory();
-        sslContextFactory.start();
-        SSLContext sslContext = sslContextFactory.getSslContext();
-        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()))
-        {
-            client.setUseClientMode(true);
-            client.setSoTimeout(5000);
-
-            ALPN.put(client, new ALPN.ClientProvider()
-            {
-                @Override
-                public void unsupported()
-                {
-                }
-
-                @Override
-                public List<String> protocols()
-                {
-                    return Arrays.asList("unknown/1.0", "http/1.1");
-                }
-
-                @Override
-                public void selected(String protocol)
-                {
-                    Assert.assertEquals("http/1.1", protocol);
-                }
-            });
-
-            client.startHandshake();
-
-            // Verify that the server really speaks http/1.1
-
-            OutputStream output = client.getOutputStream();
-            output.write(("" +
-                    "GET / HTTP/1.1\r\n" +
-                    "Host: localhost:" + address.getPort() + "\r\n" +
-                    "\r\n" +
-                    "").getBytes(StandardCharsets.UTF_8));
-            output.flush();
-
-            InputStream input = client.getInputStream();
-            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
-            String line = reader.readLine();
-            Assert.assertTrue(line.contains(" 404 "));
-        }
-    }
-
-    @Test
-    public void testClientNotSupportingALPNServerSpeaksDefaultProtocol() throws Exception
-    {
-        InetSocketAddress address = prepare();
-        connector.addConnectionFactory(new HttpConnectionFactory());
-
-        SslContextFactory sslContextFactory = newSslContextFactory();
-        sslContextFactory.start();
-        SSLContext sslContext = sslContextFactory.getSslContext();
-        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()))
-        {
-            client.setUseClientMode(true);
-            client.setSoTimeout(5000);
-
-            ALPN.put(client, new ALPN.ClientProvider()
-            {
-                @Override
-                public void unsupported()
-                {
-                }
-
-                @Override
-                public List<String> protocols()
-                {
-                    return null;
-                }
-
-                @Override
-                public void selected(String s)
-                {
-                }
-            });
-
-            client.startHandshake();
-
-            // Verify that the server really speaks http/1.1
-
-            OutputStream output = client.getOutputStream();
-            output.write(("" +
-                    "GET / HTTP/1.1\r\n" +
-                    "Host: localhost:" + address.getPort() + "\r\n" +
-                    "\r\n" +
-                    "").getBytes(StandardCharsets.UTF_8));
-            output.flush();
-
-            InputStream input = client.getInputStream();
-            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
-            String line = reader.readLine();
-            Assert.assertTrue(line.contains(" 404 "));
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/ALPNSynReplyTest.java b/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/ALPNSynReplyTest.java
deleted file mode 100644
index 4fbc7bc..0000000
--- a/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/ALPNSynReplyTest.java
+++ /dev/null
@@ -1,149 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server;
-
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.SocketChannel;
-import java.util.Arrays;
-import java.util.List;
-import javax.net.ssl.SSLEngine;
-
-import org.eclipse.jetty.alpn.ALPN;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class ALPNSynReplyTest extends AbstractALPNTest
-{
-    @Test
-    public void testGentleCloseDuringHandshake() throws Exception
-    {
-        InetSocketAddress address = prepare();
-        SslContextFactory sslContextFactory = newSslContextFactory();
-        sslContextFactory.start();
-        SSLEngine sslEngine = sslContextFactory.newSSLEngine(address);
-        sslEngine.setUseClientMode(true);
-        ALPN.put(sslEngine, new ALPN.ClientProvider()
-        {
-            @Override
-            public void unsupported()
-            {
-            }
-
-            @Override
-            public List<String> protocols()
-            {
-                return Arrays.asList("test");
-            }
-
-            @Override
-            public void selected(String protocol)
-            {
-            }
-        });
-        sslEngine.beginHandshake();
-
-        ByteBuffer encrypted = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
-        sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
-        encrypted.flip();
-
-        try (SocketChannel channel = SocketChannel.open(address))
-        {
-            // Send ClientHello, immediately followed by TLS Close Alert and then by FIN
-            channel.write(encrypted);
-            sslEngine.closeOutbound();
-            encrypted.clear();
-            sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
-            encrypted.flip();
-            channel.write(encrypted);
-            channel.shutdownOutput();
-
-            // Read ServerHello from server
-            encrypted.clear();
-            int read = channel.read(encrypted);
-            encrypted.flip();
-            Assert.assertTrue(read > 0);
-            // Cannot decrypt, as the SSLEngine has been already closed
-
-            // Now if we read more, we should either read the TLS Close Alert, or directly -1
-            encrypted.clear();
-            read = channel.read(encrypted);
-            // Sending a TLS Close Alert during handshake results in an exception when
-            // unwrapping that the server react to by closing the connection abruptly.
-            Assert.assertTrue(read < 0);
-        }
-    }
-
-    @Test
-    public void testAbruptCloseDuringHandshake() throws Exception
-    {
-        InetSocketAddress address = prepare();
-        SslContextFactory sslContextFactory = newSslContextFactory();
-        sslContextFactory.start();
-        SSLEngine sslEngine = sslContextFactory.newSSLEngine(address);
-        sslEngine.setUseClientMode(true);
-        ALPN.put(sslEngine, new ALPN.ClientProvider()
-        {
-            @Override
-            public void unsupported()
-            {
-            }
-
-            @Override
-            public List<String> protocols()
-            {
-                return Arrays.asList("test");
-            }
-
-            @Override
-            public void selected(String s)
-            {
-            }
-        });
-        sslEngine.beginHandshake();
-
-        ByteBuffer encrypted = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
-        sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
-        encrypted.flip();
-
-        try (SocketChannel channel = SocketChannel.open(address))
-        {
-            // Send ClientHello, immediately followed by FIN (no TLS Close Alert)
-            channel.write(encrypted);
-            channel.shutdownOutput();
-
-            // Read ServerHello from server
-            encrypted.clear();
-            int read = channel.read(encrypted);
-            encrypted.flip();
-            Assert.assertTrue(read > 0);
-            ByteBuffer decrypted = ByteBuffer.allocate(sslEngine.getSession().getApplicationBufferSize());
-            sslEngine.unwrap(encrypted, decrypted);
-
-            // Now if we read more, we should either read the TLS Close Alert, or directly -1
-            encrypted.clear();
-            read = channel.read(encrypted);
-            // Since we have close the connection abruptly, the server also does so
-            Assert.assertTrue(read < 0);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/AbstractALPNTest.java b/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/AbstractALPNTest.java
deleted file mode 100644
index e8f61e9..0000000
--- a/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/AbstractALPNTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.net.InetSocketAddress;
-
-import org.eclipse.jetty.alpn.ALPN;
-import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.junit.After;
-import org.junit.Rule;
-
-public class AbstractALPNTest
-{
-    @Rule
-    public final TestTracker tracker = new TestTracker();
-    protected Server server;
-    protected SPDYServerConnector connector;
-    protected SPDYClient.Factory clientFactory;
-
-    protected InetSocketAddress prepare() throws Exception
-    {
-        server = new Server();
-        connector = new SPDYServerConnector(server, newSslContextFactory(), null, new ALPNServerConnectionFactory("spdy/3", "spdy/2", "http/1.1"));
-        connector.setPort(0);
-        connector.setIdleTimeout(30000);
-        server.addConnector(connector);
-        server.start();
-
-        QueuedThreadPool threadPool = new QueuedThreadPool();
-        threadPool.setName(threadPool.getName() + "-client");
-        clientFactory = new SPDYClient.Factory(threadPool);
-        clientFactory.start();
-
-        ALPN.debug = true;
-
-        return new InetSocketAddress("localhost", connector.getLocalPort());
-    }
-
-    protected SslContextFactory newSslContextFactory()
-    {
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
-        sslContextFactory.setKeyStorePassword("storepwd");
-        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
-        sslContextFactory.setTrustStorePassword("storepwd");
-        sslContextFactory.setProtocol("TLSv1");
-        sslContextFactory.setIncludeProtocols("TLSv1");
-        return sslContextFactory;
-    }
-
-    @After
-    public void dispose() throws Exception
-    {
-        clientFactory.stop();
-        server.stop();
-    }
-}
diff --git a/jetty-spdy/spdy-alpn-tests/src/test/resources/jetty-logging.properties b/jetty-spdy/spdy-alpn-tests/src/test/resources/jetty-logging.properties
deleted file mode 100644
index ead13ec..0000000
--- a/jetty-spdy/spdy-alpn-tests/src/test/resources/jetty-logging.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
-#org.eclipse.jetty.spdy.LEVEL=DEBUG
diff --git a/jetty-spdy/spdy-client/pom.xml b/jetty-spdy/spdy-client/pom.xml
deleted file mode 100644
index 04a2ba3..0000000
--- a/jetty-spdy/spdy-client/pom.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.spdy</groupId>
-        <artifactId>spdy-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
-    </parent>
-
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>spdy-client</artifactId>
-    <name>Jetty :: SPDY :: Client Binding</name>
-
-    <properties>
-        <bundle-symbolic-name>${project.groupId}.client</bundle-symbolic-name>
-    </properties>
-
-    <url>http://www.eclipse.org/jetty</url>
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>manifest</goal>
-                        </goals>
-                        <configuration>
-                            <instructions>
-                                <Export-Package>org.eclipse.jetty.spdy.client;version="9.1"</Export-Package>
-                                <Import-Package>!org.eclipse.jetty.npn,!org.eclipse.jetty.alpn,org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
-                            </instructions>
-                          </configuration>
-                       </execution>
-                  </executions>
-            </plugin>
-        </plugins>
-    </build>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-core</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-alpn-client</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.alpn</groupId>
-            <artifactId>alpn-api</artifactId>
-            <version>${alpn.api.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.npn</groupId>
-            <artifactId>npn-api</artifactId>
-            <version>${npn.api.version}</version>
-            <scope>provided</scope>
-        </dependency>
-    </dependencies>
-
-</project>
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/FlowControlStrategyFactory.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/FlowControlStrategyFactory.java
deleted file mode 100644
index 4f346fa..0000000
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/FlowControlStrategyFactory.java
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client;
-
-import org.eclipse.jetty.spdy.FlowControlStrategy;
-import org.eclipse.jetty.spdy.SPDYv3FlowControlStrategy;
-import org.eclipse.jetty.spdy.api.SPDY;
-
-public class FlowControlStrategyFactory
-{
-    private FlowControlStrategyFactory()
-    {
-    }
-
-    public static FlowControlStrategy newFlowControlStrategy(short version)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-                return new FlowControlStrategy.None();
-            case SPDY.V3:
-                return new SPDYv3FlowControlStrategy();
-            default:
-                throw new IllegalStateException();
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnection.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnection.java
deleted file mode 100644
index 7e2ae7f..0000000
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnection.java
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client;
-
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Executor;
-import javax.net.ssl.SSLEngine;
-
-import org.eclipse.jetty.io.ClientConnectionFactory;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.NegotiatingClientConnection;
-import org.eclipse.jetty.npn.NextProtoNego;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-public class NPNClientConnection extends NegotiatingClientConnection implements NextProtoNego.ClientProvider
-{
-    private static final Logger LOG = Log.getLogger(NPNClientConnection.class);
-
-    private final String protocol;
-
-    public NPNClientConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, SSLEngine sslEngine, Map<String, Object> context, String protocol)
-    {
-        super(endPoint, executor, sslEngine, connectionFactory, context);
-        this.protocol = protocol;
-        NextProtoNego.put(sslEngine, this);
-    }
-
-    @Override
-    public boolean supports()
-    {
-        return true;
-    }
-
-    @Override
-    public void unsupported()
-    {
-        NextProtoNego.remove(getSSLEngine());
-        completed();
-    }
-
-    @Override
-    public String selectProtocol(List<String> protocols)
-    {
-        if (protocols.contains(protocol))
-        {
-            NextProtoNego.remove(getSSLEngine());
-            completed();
-            return protocol;
-        }
-        else
-        {
-            LOG.info("Could not negotiate protocol: server {} - client {}", protocols, protocol);
-            close();
-            return null;
-        }
-    }
-
-    @Override
-    public void close()
-    {
-        NextProtoNego.remove(getSSLEngine());
-        super.close();
-    }
-}
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnectionFactory.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnectionFactory.java
deleted file mode 100644
index 86d4179..0000000
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnectionFactory.java
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client;
-
-import java.io.IOException;
-import java.util.Map;
-import java.util.concurrent.Executor;
-import javax.net.ssl.SSLEngine;
-
-import org.eclipse.jetty.io.ClientConnectionFactory;
-import org.eclipse.jetty.io.Connection;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
-import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
-
-public class NPNClientConnectionFactory extends NegotiatingClientConnectionFactory
-{
-    private final Executor executor;
-    private final String protocol;
-
-    public NPNClientConnectionFactory(Executor executor, ClientConnectionFactory connectionFactory, String protocol)
-    {
-        super(connectionFactory);
-        this.executor = executor;
-        this.protocol = protocol;
-    }
-
-    @Override
-    public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
-    {
-        return new NPNClientConnection(endPoint, executor, getClientConnectionFactory(),
-                (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY), context, protocol);
-    }
-}
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java
deleted file mode 100644
index 24f37db..0000000
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java
+++ /dev/null
@@ -1,440 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.SocketChannel;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.ClientConnectionFactory;
-import org.eclipse.jetty.io.Connection;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
-import org.eclipse.jetty.io.SelectChannelEndPoint;
-import org.eclipse.jetty.io.SelectorManager;
-import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
-import org.eclipse.jetty.spdy.FlowControlStrategy;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.FuturePromise;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.component.ContainerLifeCycle;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
-import org.eclipse.jetty.util.thread.Scheduler;
-
-/**
- * A {@link SPDYClient} allows applications to connect to one or more SPDY servers,
- * obtaining {@link Session} objects that can be used to send/receive SPDY frames.
- * <p/>
- * {@link SPDYClient} instances are created through a {@link Factory}:
- * <pre>
- * SPDYClient.Factory factory = new SPDYClient.Factory();
- * SPDYClient client = factory.newSPDYClient(SPDY.V3);
- * </pre>
- * and then used to connect to the server:
- * <pre>
- * FuturePromise&lt;Session&gt; promise = new FuturePromise&lt;&gt;();
- * client.connect("server.com", null, promise);
- * Session session = promise.get();
- * </pre>
- */
-public class SPDYClient
-{
-    private final short version;
-    private final Factory factory;
-    private volatile SocketAddress bindAddress;
-    private volatile long idleTimeout = -1;
-    private volatile int initialWindowSize;
-    private volatile boolean dispatchIO;
-    private volatile ClientConnectionFactory connectionFactory;
-
-    protected SPDYClient(short version, Factory factory)
-    {
-        this.version = version;
-        this.factory = factory;
-        setInitialWindowSize(65536);
-        setDispatchIO(true);
-    }
-
-    public short getVersion()
-    {
-        return version;
-    }
-
-    public Factory getFactory()
-    {
-        return factory;
-    }
-
-    /**
-     * Equivalent to:
-     * <pre>
-     * Future&lt;Session&gt; promise = new FuturePromise&lt;&gt;();
-     * connect(address, listener, promise);
-     * </pre>
-     *
-     * @param address  the address to connect to
-     * @param listener the session listener that will be notified of session events
-     * @return a {@link Session} when connected
-     */
-    public Session connect(SocketAddress address, SessionFrameListener listener) throws ExecutionException, InterruptedException
-    {
-        FuturePromise<Session> promise = new FuturePromise<>();
-        connect(address, listener, promise);
-        return promise.get();
-    }
-
-    /**
-     * Equivalent to:
-     * <pre>
-     * connect(address, listener, promise, null);
-     * </pre>
-     *
-     * @param address  the address to connect to
-     * @param listener the session listener that will be notified of session events
-     * @param promise  the promise notified of connection success/failure
-     */
-    public void connect(SocketAddress address, SessionFrameListener listener, Promise<Session> promise)
-    {
-        connect(address, listener, promise, new HashMap<String, Object>());
-    }
-
-    /**
-     * Connects to the given {@code address}, binding the given {@code listener} to session events,
-     * and notified the given {@code promise} of the connect result.
-     * <p/>
-     * If the connect operation is successful, the {@code promise} will be invoked with the {@link Session}
-     * object that applications can use to perform SPDY requests.
-     *
-     * @param address  the address to connect to
-     * @param listener the session listener that will be notified of session events
-     * @param promise  the promise notified of connection success/failure
-     * @param context  a context object passed to the {@link #getClientConnectionFactory() ConnectionFactory}
-     *                 for the creation of the connection
-     */
-    public void connect(final SocketAddress address, final SessionFrameListener listener, final Promise<Session> promise, Map<String, Object> context)
-    {
-        if (!factory.isStarted())
-            throw new IllegalStateException(Factory.class.getSimpleName() + " is not started");
-
-        try
-        {
-            SocketChannel channel = SocketChannel.open();
-            if (bindAddress != null)
-                channel.bind(bindAddress);
-            configure(channel);
-            channel.configureBlocking(false);
-
-            context.put(SslClientConnectionFactory.SSL_PEER_HOST_CONTEXT_KEY, ((InetSocketAddress)address).getHostString());
-            context.put(SslClientConnectionFactory.SSL_PEER_PORT_CONTEXT_KEY, ((InetSocketAddress)address).getPort());
-            context.put(SPDYClientConnectionFactory.SPDY_CLIENT_CONTEXT_KEY, this);
-            context.put(SPDYClientConnectionFactory.SPDY_SESSION_LISTENER_CONTEXT_KEY, listener);
-            context.put(SPDYClientConnectionFactory.SPDY_SESSION_PROMISE_CONTEXT_KEY, promise);
-
-            if (channel.connect(address))
-                factory.selector.accept(channel, context);
-            else
-                factory.selector.connect(channel, context);
-        }
-        catch (IOException x)
-        {
-            promise.failed(x);
-        }
-    }
-
-    protected void configure(SocketChannel channel) throws IOException
-    {
-        channel.socket().setTcpNoDelay(true);
-    }
-
-    /**
-     * @return the address to bind the socket channel to
-     * @see #setBindAddress(SocketAddress)
-     */
-    public SocketAddress getBindAddress()
-    {
-        return bindAddress;
-    }
-
-    /**
-     * @param bindAddress the address to bind the socket channel to
-     * @see #getBindAddress()
-     */
-    public void setBindAddress(SocketAddress bindAddress)
-    {
-        this.bindAddress = bindAddress;
-    }
-
-    public long getIdleTimeout()
-    {
-        return idleTimeout;
-    }
-
-    public void setIdleTimeout(long idleTimeout)
-    {
-        this.idleTimeout = idleTimeout;
-    }
-
-    public int getInitialWindowSize()
-    {
-        return initialWindowSize;
-    }
-
-    public void setInitialWindowSize(int initialWindowSize)
-    {
-        this.initialWindowSize = initialWindowSize;
-    }
-
-    public boolean isDispatchIO()
-    {
-        return dispatchIO;
-    }
-
-    public void setDispatchIO(boolean dispatchIO)
-    {
-        this.dispatchIO = dispatchIO;
-    }
-
-    public ClientConnectionFactory getClientConnectionFactory()
-    {
-        return connectionFactory;
-    }
-
-    public void setClientConnectionFactory(ClientConnectionFactory connectionFactory)
-    {
-        this.connectionFactory = connectionFactory;
-    }
-
-    protected FlowControlStrategy newFlowControlStrategy()
-    {
-        return FlowControlStrategyFactory.newFlowControlStrategy(version);
-    }
-
-    public static class Factory extends ContainerLifeCycle
-    {
-        private final Queue<Session> sessions = new ConcurrentLinkedQueue<>();
-        private final Scheduler scheduler;
-        private final Executor executor;
-        private final ByteBufferPool bufferPool;
-        private final SslContextFactory sslContextFactory;
-        private final SelectorManager selector;
-        private final long idleTimeout;
-        private long connectTimeout;
-
-        public Factory()
-        {
-            this(null, null);
-        }
-
-        public Factory(SslContextFactory sslContextFactory)
-        {
-            this(null, null, sslContextFactory);
-        }
-
-        public Factory(Executor executor)
-        {
-            this(executor, null);
-        }
-
-        public Factory(Executor executor, Scheduler scheduler)
-        {
-            this(executor, scheduler, null);
-        }
-
-        public Factory(Executor executor, Scheduler scheduler, SslContextFactory sslContextFactory)
-        {
-            this(executor, scheduler, sslContextFactory, 30000);
-        }
-
-        public Factory(Executor executor, Scheduler scheduler, SslContextFactory sslContextFactory, long idleTimeout)
-        {
-            this(executor, scheduler, null, sslContextFactory, idleTimeout);
-        }
-
-        public Factory(Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, SslContextFactory sslContextFactory, long idleTimeout)
-        {
-            this.idleTimeout = idleTimeout;
-            setConnectTimeout(15000);
-
-            if (executor == null)
-                executor = new QueuedThreadPool();
-            this.executor = executor;
-            addBean(executor);
-
-            if (scheduler == null)
-                scheduler = new ScheduledExecutorScheduler();
-            this.scheduler = scheduler;
-            addBean(scheduler);
-
-            if (bufferPool == null)
-                bufferPool = new MappedByteBufferPool();
-            this.bufferPool = bufferPool;
-            addBean(bufferPool);
-
-            this.sslContextFactory = sslContextFactory;
-            if (sslContextFactory != null)
-                addBean(sslContextFactory);
-
-            selector = new ClientSelectorManager(executor, scheduler);
-            selector.setConnectTimeout(getConnectTimeout());
-            addBean(selector);
-        }
-
-        public ByteBufferPool getByteBufferPool()
-        {
-            return bufferPool;
-        }
-
-        public Scheduler getScheduler()
-        {
-            return scheduler;
-        }
-
-        public Executor getExecutor()
-        {
-            return executor;
-        }
-
-        public SslContextFactory getSslContextFactory()
-        {
-            return sslContextFactory;
-        }
-
-        public long getConnectTimeout()
-        {
-            return connectTimeout;
-        }
-
-        public void setConnectTimeout(long connectTimeout)
-        {
-            this.connectTimeout = connectTimeout;
-        }
-
-        public SPDYClient newSPDYClient(short version)
-        {
-            return newSPDYClient(version, new NPNClientConnectionFactory(getExecutor(), new SPDYClientConnectionFactory(), "spdy/" + version));
-        }
-
-        public SPDYClient newSPDYClient(short version, NegotiatingClientConnectionFactory negotiatingFactory)
-        {
-            SPDYClient client = new SPDYClient(version, this);
-
-            ClientConnectionFactory connectionFactory = negotiatingFactory.getClientConnectionFactory();
-            if (sslContextFactory != null)
-                connectionFactory = new SslClientConnectionFactory(getSslContextFactory(), getByteBufferPool(), getExecutor(), negotiatingFactory);
-
-            client.setClientConnectionFactory(connectionFactory);
-            return client;
-        }
-
-        @Override
-        protected void doStop() throws Exception
-        {
-            closeConnections();
-            super.doStop();
-        }
-
-        boolean sessionOpened(Session session)
-        {
-            // Add sessions only if the factory is not stopping
-            return isRunning() && sessions.offer(session);
-        }
-
-        boolean sessionClosed(Session session)
-        {
-            // Remove sessions only if the factory is not stopping
-            // to avoid concurrent removes during iterations
-            return isRunning() && sessions.remove(session);
-        }
-
-        private void closeConnections()
-        {
-            for (Session session : sessions)
-                session.goAway(new GoAwayInfo(), Callback.Adapter.INSTANCE);
-            sessions.clear();
-        }
-
-        public Collection<Session> getSessions()
-        {
-            return Collections.unmodifiableCollection(sessions);
-        }
-
-        @Override
-        protected void dumpThis(Appendable out) throws IOException
-        {
-            super.dumpThis(out);
-            dump(out, "", sessions);
-        }
-
-        private class ClientSelectorManager extends SelectorManager
-        {
-            private ClientSelectorManager(Executor executor, Scheduler scheduler)
-            {
-                super(executor, scheduler);
-            }
-
-            @Override
-            protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
-            {
-                @SuppressWarnings("unchecked")
-                Map<String, Object> context = (Map<String, Object>)key.attachment();
-                SPDYClient client = (SPDYClient)context.get(SPDYClientConnectionFactory.SPDY_CLIENT_CONTEXT_KEY);
-                long clientIdleTimeout = client.getIdleTimeout();
-                if (clientIdleTimeout < 0)
-                    clientIdleTimeout = idleTimeout;
-                return new SelectChannelEndPoint(channel, selectSet, key, getScheduler(), clientIdleTimeout);
-            }
-
-            @Override
-            public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) throws IOException
-            {
-                @SuppressWarnings("unchecked")
-                Map<String, Object> context = (Map<String, Object>)attachment;
-                try
-                {
-                    SPDYClient client = (SPDYClient)context.get(SPDYClientConnectionFactory.SPDY_CLIENT_CONTEXT_KEY);
-                    return client.getClientConnectionFactory().newConnection(endPoint, context);
-                }
-                catch (Throwable x)
-                {
-                    @SuppressWarnings("unchecked")
-                    Promise<Session> promise = (Promise<Session>)context.get(SPDYClientConnectionFactory.SPDY_SESSION_PROMISE_CONTEXT_KEY);
-                    promise.failed(x);
-                    throw x;
-                }
-            }
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClientConnectionFactory.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClientConnectionFactory.java
deleted file mode 100644
index 00bfc77..0000000
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClientConnectionFactory.java
+++ /dev/null
@@ -1,98 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client;
-
-import java.io.IOException;
-import java.util.Map;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.ClientConnectionFactory;
-import org.eclipse.jetty.io.Connection;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.spdy.CompressionFactory;
-import org.eclipse.jetty.spdy.FlowControlStrategy;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.StandardSession;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.client.SPDYClient.Factory;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.eclipse.jetty.util.Promise;
-
-public class SPDYClientConnectionFactory implements ClientConnectionFactory
-{
-    public static final String SPDY_CLIENT_CONTEXT_KEY = "spdy.client";
-    public static final String SPDY_SESSION_LISTENER_CONTEXT_KEY = "spdy.session.listener";
-    public static final String SPDY_SESSION_PROMISE_CONTEXT_KEY = "spdy.session.promise";
-
-    @Override
-    public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
-    {
-        SPDYClient client = (SPDYClient)context.get(SPDY_CLIENT_CONTEXT_KEY);
-        SPDYClient.Factory factory = client.getFactory();
-        ByteBufferPool byteBufferPool = factory.getByteBufferPool();
-        CompressionFactory compressionFactory = new StandardCompressionFactory();
-        Parser parser = new Parser(compressionFactory.newDecompressor());
-        Generator generator = new Generator(byteBufferPool, compressionFactory.newCompressor());
-
-        SPDYConnection connection = new ClientSPDYConnection(endPoint, byteBufferPool, parser, factory, client.isDispatchIO());
-
-        FlowControlStrategy flowControlStrategy = client.newFlowControlStrategy();
-
-        SessionFrameListener listener = (SessionFrameListener)context.get(SPDY_SESSION_LISTENER_CONTEXT_KEY);
-        StandardSession session = new StandardSession(client.getVersion(), byteBufferPool,
-                factory.getScheduler(), connection, endPoint, connection, 1, listener, generator, flowControlStrategy);
-
-        session.setWindowSize(client.getInitialWindowSize());
-        parser.addListener(session);
-        connection.setSession(session);
-
-        @SuppressWarnings("unchecked")
-        Promise<Session> promise = (Promise<Session>)context.get(SPDY_SESSION_PROMISE_CONTEXT_KEY);
-        promise.succeeded(session);
-
-        return connection;
-    }
-
-    private class ClientSPDYConnection extends SPDYConnection
-    {
-        private final Factory factory;
-
-        public ClientSPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Factory factory, boolean dispatchIO)
-        {
-            super(endPoint, bufferPool, parser, factory.getExecutor(), dispatchIO);
-            this.factory = factory;
-        }
-
-        @Override
-        public void onOpen()
-        {
-            super.onOpen();
-            factory.sessionOpened(getSession());
-        }
-
-        @Override
-        public void onClose()
-        {
-            super.onClose();
-            factory.sessionClosed(getSession());
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYConnection.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYConnection.java
deleted file mode 100644
index dfc621e..0000000
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYConnection.java
+++ /dev/null
@@ -1,191 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.Executor;
-
-import org.eclipse.jetty.io.AbstractConnection;
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.RuntimeIOException;
-import org.eclipse.jetty.spdy.Controller;
-import org.eclipse.jetty.spdy.ISession;
-import org.eclipse.jetty.spdy.IdleListener;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-public class SPDYConnection extends AbstractConnection implements Controller, IdleListener
-{
-    private static final Logger LOG = Log.getLogger(SPDYConnection.class);
-    private final ByteBufferPool bufferPool;
-    private final Parser parser;
-    private final int bufferSize;
-    private volatile ISession session;
-    private volatile boolean idle = false;
-
-    public SPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Executor executor, boolean dispatchIO)
-    {
-        this(endPoint, bufferPool, parser, executor, dispatchIO, 8192);
-    }
-
-    public SPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Executor executor, boolean dispatchIO, int bufferSize)
-    {
-        // Since SPDY is multiplexed, onFillable() must never block while calling application code. In fact,
-        // the SPDY code always dispatches to a new thread when calling application code,
-        // so here we can safely pass false as last parameter, and avoid to dispatch to onFillable().
-        // The IO operation (read, parse, etc.) will not block and will be fast in almost all cases.
-        // Big uploads to a server, however, might occupy the Selector thread for a long time and
-        // therefore starve other connections, so by default dispatchIO is true.
-        super(endPoint, executor, dispatchIO);
-        this.bufferPool = bufferPool;
-        this.parser = parser;
-        onIdle(true);
-        this.bufferSize = bufferSize;
-    }
-
-    @Override
-    public void onOpen()
-    {
-        super.onOpen();
-        fillInterested();
-    }
-
-    @Override
-    public void onFillable()
-    {
-        ByteBuffer buffer = bufferPool.acquire(bufferSize, false);
-        boolean readMore = read(buffer) == 0;
-        bufferPool.release(buffer);
-        if (readMore)
-            fillInterested();
-    }
-
-    protected int read(ByteBuffer buffer)
-    {
-        EndPoint endPoint = getEndPoint();
-        while (true)
-        {
-            int filled = fill(endPoint, buffer);
-            if (LOG.isDebugEnabled()) // Avoid boxing of variable 'filled'
-                LOG.debug("Read {} bytes", filled);
-            if (filled == 0)
-            {
-                return 0;
-            }
-            else if (filled < 0)
-            {
-                shutdown(session);
-                return -1;
-            }
-            else
-            {
-                parser.parse(buffer);
-            }
-        }
-    }
-
-    private int fill(EndPoint endPoint, ByteBuffer buffer)
-    {
-        try
-        {
-            if (endPoint.isInputShutdown())
-                return -1;
-            return endPoint.fill(buffer);
-        }
-        catch (IOException x)
-        {
-            endPoint.close();
-            throw new RuntimeIOException(x);
-        }
-    }
-
-    @Override
-    public void write(final Callback callback, ByteBuffer... buffers)
-    {
-        EndPoint endPoint = getEndPoint();
-        endPoint.write(callback, buffers);
-    }
-
-    @Override
-    public void close()
-    {
-        goAway(session);
-    }
-
-    @Override
-    public void close(boolean onlyOutput)
-    {
-        EndPoint endPoint = getEndPoint();
-        // We need to gently close first, to allow
-        // SSL close alerts to be sent by Jetty
-        if (LOG.isDebugEnabled())
-            LOG.debug("Shutting down output {}", endPoint);
-        endPoint.shutdownOutput();
-        if (!onlyOutput)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Closing {}", endPoint);
-            endPoint.close();
-        }
-    }
-
-    @Override
-    public void onIdle(boolean idle)
-    {
-        this.idle = idle;
-    }
-
-    @Override
-    protected boolean onReadTimeout()
-    {
-        boolean idle = this.idle;
-        if (LOG.isDebugEnabled())
-            LOG.debug("Idle timeout on {}, idle={}", this, idle);
-        if (idle)
-            goAway(session);
-        return false;
-    }
-
-    protected void goAway(ISession session)
-    {
-        if (session != null)
-            session.goAway(new GoAwayInfo(), Callback.Adapter.INSTANCE);
-    }
-
-    private void shutdown(ISession session)
-    {
-        if (session != null && !getEndPoint().isOutputShutdown())
-            session.shutdown();
-    }
-
-    protected ISession getSession()
-    {
-        return session;
-    }
-
-    public void setSession(ISession session)
-    {
-        this.session = session;
-    }
-}
diff --git a/jetty-spdy/spdy-core/pom.xml b/jetty-spdy/spdy-core/pom.xml
deleted file mode 100644
index 3573dd8..0000000
--- a/jetty-spdy/spdy-core/pom.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.spdy</groupId>
-        <artifactId>spdy-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
-    </parent>
-
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>spdy-core</artifactId>
-    <name>Jetty :: SPDY :: Core</name>
-
-    <properties>
-        <bundle-symbolic-name>${project.groupId}.core</bundle-symbolic-name>
-    </properties>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-util</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-io</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-
-</project>
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionDictionary.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionDictionary.java
deleted file mode 100644
index 820695c..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionDictionary.java
+++ /dev/null
@@ -1,87 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import org.eclipse.jetty.spdy.api.SPDY;
-
-public class CompressionDictionary
-{
-    private static final byte[] DICTIONARY_V2 = ("" +
-            "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-" +
-            "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi" +
-            "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser" +
-            "-agent10010120020120220320420520630030130230330430530630740040140240340440" +
-            "5406407408409410411412413414415416417500501502503504505accept-rangesageeta" +
-            "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic" +
-            "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran" +
-            "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati" +
-            "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo" +
-            "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe" +
-            "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic" +
-            "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1" +
-            ".1statusversionurl" +
-            // Must be NUL terminated
-            "\u0000").getBytes();
-
-    private static final byte[] DICTIONARY_V3 = ("" +
-            "\u0000\u0000\u0000\u0007options\u0000\u0000\u0000\u0004head\u0000\u0000\u0000\u0004post" +
-            "\u0000\u0000\u0000\u0003put\u0000\u0000\u0000\u0006delete\u0000\u0000\u0000\u0005trace" +
-            "\u0000\u0000\u0000\u0006accept\u0000\u0000\u0000\u000Eaccept-charset" +
-            "\u0000\u0000\u0000\u000Faccept-encoding\u0000\u0000\u0000\u000Faccept-language" +
-            "\u0000\u0000\u0000\raccept-ranges\u0000\u0000\u0000\u0003age\u0000\u0000\u0000\u0005allow" +
-            "\u0000\u0000\u0000\rauthorization\u0000\u0000\u0000\rcache-control" +
-            "\u0000\u0000\u0000\nconnection\u0000\u0000\u0000\fcontent-base\u0000\u0000\u0000\u0010content-encoding" +
-            "\u0000\u0000\u0000\u0010content-language\u0000\u0000\u0000\u000Econtent-length" +
-            "\u0000\u0000\u0000\u0010content-location\u0000\u0000\u0000\u000Bcontent-md5" +
-            "\u0000\u0000\u0000\rcontent-range\u0000\u0000\u0000\fcontent-type\u0000\u0000\u0000\u0004date" +
-            "\u0000\u0000\u0000\u0004etag\u0000\u0000\u0000\u0006expect\u0000\u0000\u0000\u0007expires" +
-            "\u0000\u0000\u0000\u0004from\u0000\u0000\u0000\u0004host\u0000\u0000\u0000\bif-match" +
-            "\u0000\u0000\u0000\u0011if-modified-since\u0000\u0000\u0000\rif-none-match\u0000\u0000\u0000\bif-range" +
-            "\u0000\u0000\u0000\u0013if-unmodified-since\u0000\u0000\u0000\rlast-modified" +
-            "\u0000\u0000\u0000\blocation\u0000\u0000\u0000\fmax-forwards\u0000\u0000\u0000\u0006pragma" +
-            "\u0000\u0000\u0000\u0012proxy-authenticate\u0000\u0000\u0000\u0013proxy-authorization" +
-            "\u0000\u0000\u0000\u0005range\u0000\u0000\u0000\u0007referer\u0000\u0000\u0000\u000Bretry-after" +
-            "\u0000\u0000\u0000\u0006server\u0000\u0000\u0000\u0002te\u0000\u0000\u0000\u0007trailer" +
-            "\u0000\u0000\u0000\u0011transfer-encoding\u0000\u0000\u0000\u0007upgrade\u0000\u0000\u0000\nuser-agent" +
-            "\u0000\u0000\u0000\u0004vary\u0000\u0000\u0000\u0003via\u0000\u0000\u0000\u0007warning" +
-            "\u0000\u0000\u0000\u0010www-authenticate\u0000\u0000\u0000\u0006method\u0000\u0000\u0000\u0003get" +
-            "\u0000\u0000\u0000\u0006status\u0000\u0000\u0000\u0006200 OK\u0000\u0000\u0000\u0007version" +
-            "\u0000\u0000\u0000\bHTTP/1.1\u0000\u0000\u0000\u0003url\u0000\u0000\u0000\u0006public" +
-            "\u0000\u0000\u0000\nset-cookie\u0000\u0000\u0000\nkeep-alive\u0000\u0000\u0000\u0006origin" +
-            "100101201202205206300302303304305306307402405406407408409410411412413414415416417502504505" +
-            "203 Non-Authoritative Information204 No Content301 Moved Permanently400 Bad Request401 Unauthorized" +
-            "403 Forbidden404 Not Found500 Internal Server Error501 Not Implemented503 Service Unavailable" +
-            "Jan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec 00:00:00 Mon, Tue, Wed, Thu, Fri, Sat, Sun, GMT" +
-            "chunked,text/html,image/png,image/jpg,image/gif,application/xml,application/xhtml+xml,text/plain," +
-            "text/javascript,publicprivatemax-age=gzip,deflate,sdchcharset=utf-8charset=iso-8859-1,utf-,*,enq=0.")
-            .getBytes();
-
-    public static byte[] get(short version)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-                return DICTIONARY_V2;
-            case SPDY.V3:
-                return DICTIONARY_V3;
-            default:
-                throw new IllegalStateException();
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionFactory.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionFactory.java
deleted file mode 100644
index 18a67c9..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/CompressionFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import java.util.zip.ZipException;
-
-public interface CompressionFactory
-{
-    public Compressor newCompressor();
-
-    public Decompressor newDecompressor();
-
-    public interface Compressor
-    {
-        public void setInput(byte[] input);
-
-        public void setDictionary(byte[] dictionary);
-
-        public int compress(byte[] output);
-    }
-
-    public interface Decompressor
-    {
-        public void setDictionary(byte[] dictionary);
-
-        public void setInput(byte[] input);
-
-        public int decompress(byte[] output) throws ZipException;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Controller.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Controller.java
deleted file mode 100644
index fa8c734..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Controller.java
+++ /dev/null
@@ -1,30 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.util.Callback;
-
-public interface Controller
-{
-    public void write(Callback callback, ByteBuffer... buffers);
-
-    public void close(boolean onlyOutput);
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/FlowControlStrategy.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/FlowControlStrategy.java
deleted file mode 100644
index d624216..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/FlowControlStrategy.java
+++ /dev/null
@@ -1,92 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import org.eclipse.jetty.spdy.api.DataInfo;
-
-// TODO: add methods that tell how much written and whether we're TCP congested ?
-public interface FlowControlStrategy
-{
-    public int getWindowSize(ISession session);
-
-    public void setWindowSize(ISession session, int windowSize);
-
-    public void onNewStream(ISession session, IStream stream);
-
-    public void onWindowUpdate(ISession session, IStream stream, int delta);
-
-    public void updateWindow(ISession session, IStream stream, int delta);
-
-    public void onDataReceived(ISession session, IStream stream, DataInfo dataInfo);
-
-    public void onDataConsumed(ISession session, IStream stream, DataInfo dataInfo, int delta);
-
-    public static class None implements FlowControlStrategy
-    {
-        private volatile int windowSize;
-
-        public None()
-        {
-            this(65536);
-        }
-
-        public None(int windowSize)
-        {
-            this.windowSize = windowSize;
-        }
-
-        @Override
-        public int getWindowSize(ISession session)
-        {
-            return windowSize;
-        }
-
-        @Override
-        public void setWindowSize(ISession session, int windowSize)
-        {
-            this.windowSize = windowSize;
-        }
-
-        @Override
-        public void onNewStream(ISession session, IStream stream)
-        {
-            stream.updateWindowSize(windowSize);
-        }
-
-        @Override
-        public void onWindowUpdate(ISession session, IStream stream, int delta)
-        {
-        }
-
-        @Override
-        public void updateWindow(ISession session, IStream stream, int delta)
-        {
-        }
-
-        @Override
-        public void onDataReceived(ISession session, IStream stream, DataInfo dataInfo)
-        {
-        }
-
-        @Override
-        public void onDataConsumed(ISession session, IStream stream, DataInfo dataInfo, int delta)
-        {
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Flusher.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Flusher.java
deleted file mode 100644
index f19598e..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Flusher.java
+++ /dev/null
@@ -1,266 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.eclipse.jetty.spdy.StandardSession.FrameBytes;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.util.ArrayQueue;
-import org.eclipse.jetty.util.IteratingCallback;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-public class Flusher
-{
-    private static final Logger LOG = Log.getLogger(Flusher.class);
-
-    private final IteratingCallback callback = new FlusherCallback();
-    private final Object lock = new Object();
-    private final ArrayQueue<StandardSession.FrameBytes> queue = new ArrayQueue<>(ArrayQueue.DEFAULT_CAPACITY, ArrayQueue.DEFAULT_GROWTH, lock);
-    private final Controller controller;
-    private final int maxGather;
-    private Throwable failure;
-
-    public Flusher(Controller controller)
-    {
-        this(controller, 8);
-    }
-
-    public Flusher(Controller controller, int maxGather)
-    {
-        this.controller = controller;
-        this.maxGather = maxGather;
-    }
-
-    public void removeFrameBytesFromQueue(Stream stream)
-    {
-        synchronized (lock)
-        {
-            for (StandardSession.FrameBytes frameBytes : queue)
-                if (frameBytes.getStream() == stream)
-                    queue.remove(frameBytes);
-        }
-    }
-
-    public Throwable prepend(StandardSession.FrameBytes frameBytes)
-    {
-        synchronized (lock)
-        {
-            Throwable failure = this.failure;
-            if (failure == null)
-            {
-                // Scan from the front of the queue looking to skip higher priority messages
-                int index = 0;
-                int size = queue.size();
-                while (index < size)
-                {
-                    StandardSession.FrameBytes element = queue.getUnsafe(index);
-                    if (element.compareTo(frameBytes) <= 0)
-                        break;
-                    ++index;
-                }
-                queue.add(index, frameBytes);
-            }
-            return failure;
-        }
-    }
-
-    public Throwable append(StandardSession.FrameBytes frameBytes)
-    {
-        synchronized (lock)
-        {
-            Throwable failure = this.failure;
-            if (failure == null)
-            {
-                // Non DataFrameBytes are inserted last
-                queue.add(frameBytes);
-            }
-            return failure;
-        }
-    }
-
-    public Throwable append(StandardSession.DataFrameBytes frameBytes)
-    {
-        synchronized (lock)
-        {
-            Throwable failure = this.failure;
-            if (failure == null)
-            {
-                // DataFrameBytes are inserted by priority
-                int index = queue.size();
-                while (index > 0)
-                {
-                    StandardSession.FrameBytes element = queue.getUnsafe(index - 1);
-                    if (element.compareTo(frameBytes) >= 0)
-                        break;
-                    --index;
-                }
-                queue.add(index, frameBytes);
-            }
-            return failure;
-        }
-    }
-
-    public void flush()
-    {
-        callback.iterate();
-    }
-
-    public int getQueueSize()
-    {
-        synchronized (lock)
-        {
-            return queue.size();
-        }
-    }
-
-    private class FlusherCallback extends IteratingCallback
-    {
-        private final List<StandardSession.FrameBytes> active = new ArrayList<>(maxGather);
-        private final List<StandardSession.FrameBytes> succeeded = new ArrayList<>(maxGather);
-        private final Set<IStream> stalled = new HashSet<>();
-
-        @Override
-        protected Action process() throws Exception
-        {
-            synchronized (lock)
-            {
-                // Scan queue for data to write from first non stalled stream.
-                int index = 0; // The index of the first non-stalled frame.
-                int size = queue.size();
-                while (index < size)
-                {
-                    FrameBytes frameBytes = queue.getUnsafe(index);
-                    IStream stream = frameBytes.getStream();
-
-                    if (stream != null)
-                    {
-                        // Is it a frame belonging to an already stalled stream ?
-                        if (stalled.size() > 0 && stalled.contains(stream))
-                        {
-                            ++index;
-                            continue;
-                        }
-
-                        // Has the stream consumed all its flow control window ?
-                        if (stream.getWindowSize() <= 0)
-                        {
-                            stalled.add(stream);
-                            ++index;
-                            continue;
-                        }
-                    }
-
-                    // We will be possibly writing this frame, so take the frame off the queue.
-                    queue.remove(index);
-                    --size;
-
-                    // Has the stream been reset for this data frame ?
-                    if (stream != null && stream.isReset() && frameBytes instanceof StandardSession.DataFrameBytes)
-                    {
-                        // TODO: notify from within sync block !
-                        frameBytes.failed(new StreamException(frameBytes.getStream().getId(),
-                                StreamStatus.INVALID_STREAM, "Stream: " + frameBytes.getStream() + " is reset!"));
-                        continue;
-                    }
-
-                    active.add(frameBytes);
-                }
-                stalled.clear();
-
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Flushing {} of {} frame(s) in queue", active.size(), queue.size());
-            }
-
-            if (active.isEmpty())
-                return Action.IDLE;
-
-            // Get the bytes to write
-            ByteBuffer[] buffers = new ByteBuffer[active.size()];
-            for (int i = 0; i < buffers.length; i++)
-                buffers[i] = active.get(i).getByteBuffer();
-
-            if (controller != null)
-                controller.write(this, buffers);
-
-            // TODO: optimization
-            // If the callback completely immediately, it means that
-            // the connection is not congested, and therefore we can
-            // write more data without blocking.
-            // Therefore we should check this condition and increase
-            // the write window, which means two things: autotune the
-            // maxGather parameter, and/or autotune the buffer returned
-            // by FrameBytes.getByteBuffer() (see also comment there).
-
-            return Action.SCHEDULED;
-        }
-
-        @Override
-        protected void onCompleteSuccess()
-        {
-            // will never be called as process always returns SCHEDULED or IDLE
-            throw new IllegalStateException();
-        }
-
-        @Override
-        public void succeeded()
-        {
-            synchronized (lock)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Succeeded write of {}, q={}", active, queue.size());
-                succeeded.addAll(active);
-                active.clear();
-            }
-            // Notify outside the synchronized block.
-            for (FrameBytes frame : succeeded)
-                frame.succeeded();
-            succeeded.clear();
-            super.succeeded();
-        }
-
-        @Override
-        public void onCompleteFailure(Throwable x)
-        {
-            List<StandardSession.FrameBytes> failed = new ArrayList<>();
-            synchronized (lock)
-            {
-                failure = x;
-                if (LOG.isDebugEnabled())
-                {
-                    String logMessage = String.format("Failed write of %s, failing all %d frame(s) in queue", this, queue.size());
-                    LOG.debug(logMessage, x);
-                }
-                failed.addAll(active);
-                active.clear();
-                failed.addAll(queue);
-                queue.clear();
-            }
-            // Notify outside the synchronized block.
-            for (StandardSession.FrameBytes frame : failed)
-                frame.failed(x);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ISession.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ISession.java
deleted file mode 100644
index 678d41b..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ISession.java
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.util.Callback;
-
-public interface ISession extends Session
-{
-    public void control(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Callback callback);
-
-    public void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Callback callback);
-
-    /**
-     * <p>Gracefully shuts down this session.</p>
-     * <p>A special item is queued that will close the connection when it will be dequeued.</p>
-     */
-    public void shutdown();
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IStream.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IStream.java
deleted file mode 100644
index 07c5a74..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IStream.java
+++ /dev/null
@@ -1,120 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.util.Callback;
-
-/**
- * <p>The internal interface that represents a stream.</p>
- * <p>{@link IStream} contains additional methods used by a SPDY
- * implementation (and not by an application).</p>
- */
-public interface IStream extends Stream, Callback
-{
-    /**
-     * <p>Senders of data frames need to know the current window size
-     * to determine whether they can send more data.</p>
-     *
-     * @return the current window size for this stream.
-     * @see #updateWindowSize(int)
-     */
-    public int getWindowSize();
-
-    /**
-     * <p>Updates the window size for this stream by the given amount,
-     * that can be positive or negative.</p>
-     * <p>Senders and recipients of data frames update the window size,
-     * respectively, with negative values and positive values.</p>
-     *
-     * @param delta the signed amount the window size needs to be updated
-     * @see #getWindowSize()
-     */
-    public void updateWindowSize(int delta);
-
-    /**
-     * @param listener the stream frame listener associated to this stream
-     * as returned by {@link SessionFrameListener#onSyn(Stream, SynInfo)}
-     */
-    public void setStreamFrameListener(StreamFrameListener listener);
-
-    /**
-     * @return the stream frame listener associated to this stream
-     */
-    public StreamFrameListener getStreamFrameListener();
-
-    /**
-     * <p>A stream can be open, {@link #isHalfClosed() half closed} or
-     * {@link #isClosed() closed} and this method updates the close state
-     * of this stream.</p>
-     * <p>If the stream is open, calling this method with a value of true
-     * puts the stream into half closed state.</p>
-     * <p>If the stream is half closed, calling this method with a value
-     * of true puts the stream into closed state.</p>
-     *
-     * @param close whether the close state should be updated
-     * @param local whether the close is local or remote
-     */
-    public void updateCloseState(boolean close, boolean local);
-
-    /**
-     * <p>Processes the given control frame,
-     * for example by updating the stream's state or by calling listeners.</p>
-     *
-     * @param frame the control frame to process
-     * @see #process(DataInfo)
-     */
-    public void process(ControlFrame frame);
-
-    /**
-     * <p>Processes the given {@code dataInfo},
-     * for example by updating the stream's state or by calling listeners.</p>
-     *
-     * @param dataInfo the DataInfo to process
-     * @see #process(ControlFrame)
-     */
-    public void process(DataInfo dataInfo);
-
-    /**
-     * <p>Associate the given {@link IStream} to this {@link IStream}.</p>
-     *
-     * @param stream the stream to associate with this stream
-     */
-    public void associate(IStream stream);
-
-    /**
-     * <p>remove the given associated {@link IStream} from this stream</p>
-     *
-     * @param stream the stream to be removed
-     */
-    public void disassociate(IStream stream);
-
-    /**
-     * <p>Overrides Stream.getAssociatedStream() to return an instance of IStream instead of Stream
-     *
-     * @see Stream#getAssociatedStream()
-     */
-    @Override
-    public IStream getAssociatedStream();
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IdleListener.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IdleListener.java
deleted file mode 100644
index 4470c92..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IdleListener.java
+++ /dev/null
@@ -1,24 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-public interface IdleListener
-{
-    public void onIdle(boolean idle);
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/PushSynInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/PushSynInfo.java
deleted file mode 100644
index 35939c8..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/PushSynInfo.java
+++ /dev/null
@@ -1,60 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-
-/* ------------------------------------------------------------ */
-/**
- * <p>A subclass container of {@link SynInfo} for unidirectional streams</p>
- */
-public class PushSynInfo extends SynInfo
-{
-    public static final byte FLAG_UNIDIRECTIONAL = 2;
-    
-    private int associatedStreamId;
-    
-    public PushSynInfo(int associatedStreamId, PushInfo pushInfo){
-        super(pushInfo.getTimeout(), pushInfo.getUnit(), pushInfo.getHeaders(), pushInfo.isClose(), (byte)0);
-        this.associatedStreamId = associatedStreamId;
-    }
-    
-    /**
-     * @return the close and unidirectional flags as integer
-     * @see #FLAG_CLOSE
-     * @see #FLAG_UNIDIRECTIONAL
-     */
-    @Override
-    public byte getFlags()
-    {
-        byte flags = super.getFlags();
-        flags += FLAG_UNIDIRECTIONAL;
-        return flags;
-    }
-    
-    /**
-     * @return the id of the associated stream
-     */
-    public int getAssociatedStreamId()
-    {
-        return associatedStreamId;
-    }
-
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SPDYv3FlowControlStrategy.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SPDYv3FlowControlStrategy.java
deleted file mode 100644
index 760fd1e..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SPDYv3FlowControlStrategy.java
+++ /dev/null
@@ -1,90 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
-import org.eclipse.jetty.util.Callback;
-
-public class SPDYv3FlowControlStrategy implements FlowControlStrategy
-{
-    private volatile int windowSize;
-
-    @Override
-    public int getWindowSize(ISession session)
-    {
-        return windowSize;
-    }
-
-    @Override
-    public void setWindowSize(ISession session, int windowSize)
-    {
-        int prevWindowSize = this.windowSize;
-        this.windowSize = windowSize;
-        for (Stream stream : session.getStreams())
-            ((IStream)stream).updateWindowSize(windowSize - prevWindowSize);
-    }
-
-    @Override
-    public void onNewStream(ISession session, IStream stream)
-    {
-        stream.updateWindowSize(windowSize);
-    }
-
-    @Override
-    public void onWindowUpdate(ISession session, IStream stream, int delta)
-    {
-        if (stream != null)
-            stream.updateWindowSize(delta);
-    }
-
-    @Override
-    public void updateWindow(ISession session, IStream stream, int delta)
-    {
-        stream.updateWindowSize(delta);
-    }
-
-    @Override
-    public void onDataReceived(ISession session, IStream stream, DataInfo dataInfo)
-    {
-        // Do nothing
-    }
-
-    @Override
-    public void onDataConsumed(ISession session, IStream stream, DataInfo dataInfo, int delta)
-    {
-        // This is the algorithm for flow control.
-        // This method may be called multiple times with delta=1, but we only send a window
-        // update when the whole dataInfo has been consumed.
-        // Other policies may be to send window updates when consumed() is greater than
-        // a certain threshold, etc. but for now the policy is not pluggable for simplicity.
-        // Note that the frequency of window updates depends on the read buffer, that
-        // should not be too smaller than the window size to avoid frequent window updates.
-        // Therefore, a pluggable policy should be able to modify the read buffer capacity.
-        int length = dataInfo.length();
-        if (dataInfo.consumed() == length && !stream.isClosed() && length > 0)
-        {
-            WindowUpdateFrame windowUpdateFrame = new WindowUpdateFrame(session.getVersion(), stream.getId(), length);
-            session.control(stream, windowUpdateFrame, 0, TimeUnit.MILLISECONDS, Callback.Adapter.INSTANCE);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SessionException.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SessionException.java
deleted file mode 100644
index 25531f6..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/SessionException.java
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import org.eclipse.jetty.spdy.api.SessionStatus;
-
-public class SessionException extends RuntimeException
-{
-    private final SessionStatus sessionStatus;
-
-    public SessionException(SessionStatus sessionStatus)
-    {
-        this.sessionStatus = sessionStatus;
-    }
-
-    public SessionException(SessionStatus sessionStatus, String message)
-    {
-        super(message);
-        this.sessionStatus = sessionStatus;
-    }
-
-    public SessionException(SessionStatus sessionStatus, Throwable cause)
-    {
-        super(cause);
-        this.sessionStatus = sessionStatus;
-    }
-
-    public SessionStatus getSessionStatus()
-    {
-        return sessionStatus;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardCompressionFactory.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardCompressionFactory.java
deleted file mode 100644
index 712beff..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardCompressionFactory.java
+++ /dev/null
@@ -1,92 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import java.util.zip.DataFormatException;
-import java.util.zip.Deflater;
-import java.util.zip.Inflater;
-import java.util.zip.ZipException;
-
-public class StandardCompressionFactory implements CompressionFactory
-{
-    @Override
-    public Compressor newCompressor()
-    {
-        return new StandardCompressor();
-    }
-
-    @Override
-    public Decompressor newDecompressor()
-    {
-        return new StandardDecompressor();
-    }
-
-    public static class StandardCompressor implements Compressor
-    {
-        private final Deflater deflater = new Deflater();
-
-        @Override
-        public void setInput(byte[] input)
-        {
-            deflater.setInput(input);
-        }
-
-        @Override
-        public void setDictionary(byte[] dictionary)
-        {
-            deflater.setDictionary(dictionary);
-        }
-
-        @Override
-        public int compress(byte[] output)
-        {
-            return deflater.deflate(output, 0, output.length, Deflater.SYNC_FLUSH);
-        }
-    }
-
-    public static class StandardDecompressor implements CompressionFactory.Decompressor
-    {
-        private final Inflater inflater = new Inflater();
-
-        @Override
-        public void setDictionary(byte[] dictionary)
-        {
-            inflater.setDictionary(dictionary);
-        }
-
-        @Override
-        public void setInput(byte[] input)
-        {
-            inflater.setInput(input);
-        }
-
-        @Override
-        public int decompress(byte[] output) throws ZipException
-        {
-            try
-            {
-                return inflater.inflate(output);
-            }
-            catch (DataFormatException x)
-            {
-                throw (ZipException)new ZipException().initCause(x);
-            }
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java
deleted file mode 100644
index 7fa8ce1..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java
+++ /dev/null
@@ -1,1303 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.InterruptedByTimeoutException;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
-import org.eclipse.jetty.spdy.api.PingInfo;
-import org.eclipse.jetty.spdy.api.PingResultInfo;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.SPDYException;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.SessionStatus;
-import org.eclipse.jetty.spdy.api.Settings;
-import org.eclipse.jetty.spdy.api.SettingsInfo;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.ControlFrameType;
-import org.eclipse.jetty.spdy.frames.CredentialFrame;
-import org.eclipse.jetty.spdy.frames.DataFrame;
-import org.eclipse.jetty.spdy.frames.GoAwayFrame;
-import org.eclipse.jetty.spdy.frames.HeadersFrame;
-import org.eclipse.jetty.spdy.frames.PingFrame;
-import org.eclipse.jetty.spdy.frames.RstStreamFrame;
-import org.eclipse.jetty.spdy.frames.SettingsFrame;
-import org.eclipse.jetty.spdy.frames.SynReplyFrame;
-import org.eclipse.jetty.spdy.frames.SynStreamFrame;
-import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.eclipse.jetty.util.Atomics;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.FutureCallback;
-import org.eclipse.jetty.util.FuturePromise;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.component.ContainerLifeCycle;
-import org.eclipse.jetty.util.component.Dumpable;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.thread.Scheduler;
-
-public class StandardSession implements ISession, Parser.Listener, Dumpable
-{
-    private static final Logger LOG = Log.getLogger(Session.class);
-
-    private final Flusher flusher;
-    private final Map<String, Object> attributes = new ConcurrentHashMap<>();
-    private final List<Listener> listeners = new CopyOnWriteArrayList<>();
-    private final ConcurrentMap<Integer, IStream> streams = new ConcurrentHashMap<>();
-    private final ByteBufferPool bufferPool;
-    private final Scheduler scheduler;
-    private final short version;
-    private final Controller controller;
-    private final EndPoint endPoint;
-    private final IdleListener idleListener;
-    private final AtomicInteger streamIds;
-    private final AtomicInteger pingIds;
-    private final SessionFrameListener listener;
-    private final Generator generator;
-    private final AtomicBoolean goAwaySent = new AtomicBoolean();
-    private final AtomicBoolean goAwayReceived = new AtomicBoolean();
-    private final AtomicInteger lastStreamId = new AtomicInteger();
-    private final AtomicInteger localStreamCount = new AtomicInteger(0);
-    private final FlowControlStrategy flowControlStrategy;
-    private volatile int maxConcurrentLocalStreams = -1;
-
-    public StandardSession(short version, ByteBufferPool bufferPool, Scheduler scheduler,
-                           Controller controller, EndPoint endPoint, IdleListener idleListener, int initialStreamId,
-                           SessionFrameListener listener, Generator generator, FlowControlStrategy flowControlStrategy)
-    {
-        // TODO this should probably be an aggregate lifecycle
-        this.version = version;
-        this.bufferPool = bufferPool;
-        this.scheduler = scheduler;
-        this.controller = controller;
-        this.endPoint = endPoint;
-        this.idleListener = idleListener;
-        this.streamIds = new AtomicInteger(initialStreamId);
-        this.pingIds = new AtomicInteger(initialStreamId);
-        this.listener = listener;
-        this.generator = generator;
-        this.flowControlStrategy = flowControlStrategy;
-        this.flusher = new Flusher(controller);
-    }
-
-    @Override
-    public short getVersion()
-    {
-        return version;
-    }
-
-    @Override
-    public void addListener(Listener listener)
-    {
-        listeners.add(listener);
-    }
-
-    @Override
-    public void removeListener(Listener listener)
-    {
-        listeners.remove(listener);
-    }
-
-    @Override
-    public Stream syn(SynInfo synInfo, StreamFrameListener listener) throws ExecutionException, InterruptedException, TimeoutException
-    {
-        FuturePromise<Stream> result = new FuturePromise<>();
-        syn(synInfo, listener, result);
-        if (synInfo.getTimeout() > 0)
-            return result.get(synInfo.getTimeout(), synInfo.getUnit());
-        else
-            return result.get();
-    }
-
-    @Override
-    public void syn(SynInfo synInfo, StreamFrameListener listener, Promise<Stream> promise)
-    {
-        // Synchronization is necessary.
-        // SPEC v3, 2.3.1 requires that the stream creation be monotonically crescent
-        // so we cannot allow thread1 to create stream1 and thread2 create stream3 and
-        // have stream3 hit the network before stream1, not only to comply with the spec
-        // but also because the compression context for the headers would be wrong, as the
-        // frame with a compression history will come before the first compressed frame.
-        int associatedStreamId = 0;
-        if (synInfo instanceof PushSynInfo)
-            associatedStreamId = ((PushSynInfo)synInfo).getAssociatedStreamId();
-
-        synchronized (this)
-        {
-            int streamId = streamIds.getAndAdd(2);
-            // TODO: for SPDYv3 we need to support the "slot" argument
-            SynStreamFrame synStream = new SynStreamFrame(version, synInfo.getFlags(), streamId, associatedStreamId, synInfo.getPriority(), (short)0, synInfo.getHeaders());
-            IStream stream = createStream(synStream, listener, true, promise);
-            if (stream == null)
-                return;
-            generateAndEnqueueControlFrame(stream, synStream, synInfo.getTimeout(), synInfo.getUnit(), stream);
-        }
-    }
-
-    @Override
-    public void rst(RstInfo rstInfo) throws InterruptedException, ExecutionException, TimeoutException
-    {
-        FutureCallback result = new FutureCallback();
-        rst(rstInfo, result);
-        if (rstInfo.getTimeout() > 0)
-            result.get(rstInfo.getTimeout(), rstInfo.getUnit());
-        else
-            result.get();
-    }
-
-    @Override
-    public void rst(RstInfo rstInfo, Callback callback)
-    {
-        // SPEC v3, 2.2.2
-        if (goAwaySent.get())
-        {
-            complete(callback);
-        }
-        else
-        {
-            int streamId = rstInfo.getStreamId();
-            IStream stream = streams.get(streamId);
-            RstStreamFrame frame = new RstStreamFrame(version, streamId, rstInfo.getStreamStatus().getCode(version));
-            control(stream, frame, rstInfo.getTimeout(), rstInfo.getUnit(), callback);
-            if (stream != null)
-            {
-                stream.process(frame);
-                flusher.removeFrameBytesFromQueue(stream);
-                removeStream(stream);
-            }
-        }
-    }
-
-    @Override
-    public void settings(SettingsInfo settingsInfo) throws ExecutionException, InterruptedException, TimeoutException
-    {
-        FutureCallback result = new FutureCallback();
-        settings(settingsInfo, result);
-        if (settingsInfo.getTimeout() > 0)
-            result.get(settingsInfo.getTimeout(), settingsInfo.getUnit());
-        else
-            result.get();
-    }
-
-    @Override
-    public void settings(SettingsInfo settingsInfo, Callback callback)
-    {
-        SettingsFrame frame = new SettingsFrame(version, settingsInfo.getFlags(), settingsInfo.getSettings());
-        control(null, frame, settingsInfo.getTimeout(), settingsInfo.getUnit(), callback);
-    }
-
-    @Override
-    public PingResultInfo ping(PingInfo pingInfo) throws ExecutionException, InterruptedException, TimeoutException
-    {
-        FuturePromise<PingResultInfo> result = new FuturePromise<>();
-        ping(pingInfo, result);
-        if (pingInfo.getTimeout() > 0)
-            return result.get(pingInfo.getTimeout(), pingInfo.getUnit());
-        else
-            return result.get();
-    }
-
-    @Override
-    public void ping(PingInfo pingInfo, Promise<PingResultInfo> promise)
-    {
-        int pingId = pingIds.getAndAdd(2);
-        PingInfoCallback pingInfoCallback = new PingInfoCallback(pingId, promise);
-        PingFrame frame = new PingFrame(version, pingId);
-        control(null, frame, pingInfo.getTimeout(), pingInfo.getUnit(), pingInfoCallback);
-    }
-
-    @Override
-    public void goAway(GoAwayInfo goAwayInfo) throws ExecutionException, InterruptedException, TimeoutException
-    {
-        goAway(goAwayInfo, SessionStatus.OK);
-    }
-
-    private void goAway(GoAwayInfo goAwayInfo, SessionStatus sessionStatus) throws ExecutionException, InterruptedException, TimeoutException
-    {
-        FutureCallback result = new FutureCallback();
-        goAway(sessionStatus, goAwayInfo.getTimeout(), goAwayInfo.getUnit(), result);
-        if (goAwayInfo.getTimeout() > 0)
-            result.get(goAwayInfo.getTimeout(), goAwayInfo.getUnit());
-        else
-            result.get();
-    }
-
-    @Override
-    public void goAway(GoAwayInfo goAwayInfo, Callback callback)
-    {
-        goAway(SessionStatus.OK, goAwayInfo.getTimeout(), goAwayInfo.getUnit(), callback);
-    }
-
-    private void goAway(SessionStatus sessionStatus, long timeout, TimeUnit unit, Callback callback)
-    {
-        if (goAwaySent.compareAndSet(false, true))
-        {
-            if (!goAwayReceived.get())
-            {
-                GoAwayFrame frame = new GoAwayFrame(version, lastStreamId.get(), sessionStatus.getCode());
-                control(null, frame, timeout, unit, callback);
-                return;
-            }
-        }
-        complete(callback);
-    }
-
-    @Override
-    public Set<Stream> getStreams()
-    {
-        Set<Stream> result = new HashSet<>();
-        result.addAll(streams.values());
-        return result;
-    }
-
-    @Override
-    public IStream getStream(int streamId)
-    {
-        return streams.get(streamId);
-    }
-
-    @Override
-    public Object getAttribute(String key)
-    {
-        return attributes.get(key);
-    }
-
-    @Override
-    public void setAttribute(String key, Object value)
-    {
-        attributes.put(key, value);
-    }
-
-    @Override
-    public Object removeAttribute(String key)
-    {
-        return attributes.remove(key);
-    }
-
-    @Override
-    public InetSocketAddress getLocalAddress()
-    {
-        return endPoint.getLocalAddress();
-    }
-
-    @Override
-    public InetSocketAddress getRemoteAddress()
-    {
-        return endPoint.getRemoteAddress();
-    }
-
-    @Override
-    public void onControlFrame(ControlFrame frame)
-    {
-        notifyIdle(idleListener, false);
-        try
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Processing {}", frame);
-
-            if (goAwaySent.get())
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Skipped processing of {}", frame);
-                return;
-            }
-
-            switch (frame.getType())
-            {
-                case SYN_STREAM:
-                {
-                    onSyn((SynStreamFrame)frame);
-                    break;
-                }
-                case SYN_REPLY:
-                {
-                    onReply((SynReplyFrame)frame);
-                    break;
-                }
-                case RST_STREAM:
-                {
-                    onRst((RstStreamFrame)frame);
-                    break;
-                }
-                case SETTINGS:
-                {
-                    onSettings((SettingsFrame)frame);
-                    break;
-                }
-                case NOOP:
-                {
-                    // Just ignore it
-                    break;
-                }
-                case PING:
-                {
-                    onPing((PingFrame)frame);
-                    break;
-                }
-                case GO_AWAY:
-                {
-                    onGoAway((GoAwayFrame)frame);
-                    break;
-                }
-                case HEADERS:
-                {
-                    onHeaders((HeadersFrame)frame);
-                    break;
-                }
-                case WINDOW_UPDATE:
-                {
-                    onWindowUpdate((WindowUpdateFrame)frame);
-                    break;
-                }
-                case CREDENTIAL:
-                {
-                    onCredential((CredentialFrame)frame);
-                    break;
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-        }
-        finally
-        {
-            notifyIdle(idleListener, true);
-        }
-    }
-
-    @Override
-    public void onDataFrame(DataFrame frame, ByteBuffer data)
-    {
-        notifyIdle(idleListener, false);
-        try
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Processing {}, {} data bytes", frame, data.remaining());
-
-            if (goAwaySent.get())
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Skipped processing of {}", frame);
-                return;
-            }
-
-            int streamId = frame.getStreamId();
-            IStream stream = streams.get(streamId);
-            if (stream == null)
-            {
-                RstInfo rstInfo = new RstInfo(streamId, StreamStatus.INVALID_STREAM);
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Unknown stream {}", rstInfo);
-                rst(rstInfo, Callback.Adapter.INSTANCE);
-            }
-            else
-            {
-                processData(stream, frame, data);
-            }
-        }
-        finally
-        {
-            notifyIdle(idleListener, true);
-        }
-    }
-
-    private void notifyIdle(IdleListener listener, boolean idle)
-    {
-        if (listener != null)
-            listener.onIdle(idle);
-    }
-
-    private void processData(final IStream stream, DataFrame frame, ByteBuffer data)
-    {
-        ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(data, frame.isClose())
-        {
-            @Override
-            public void consume(int delta)
-            {
-                super.consume(delta);
-                flowControlStrategy.onDataConsumed(StandardSession.this, stream, this, delta);
-            }
-        };
-        flowControlStrategy.onDataReceived(this, stream, dataInfo);
-        stream.process(dataInfo);
-        if (stream.isClosed())
-            removeStream(stream);
-    }
-
-    @Override
-    public void onStreamException(StreamException x)
-    {
-        notifyOnFailure(listener, x); // TODO: notify StreamFrameListener if exists?
-        rst(new RstInfo(x.getStreamId(), x.getStreamStatus()), Callback.Adapter.INSTANCE);
-    }
-
-    @Override
-    public void onSessionException(SessionException x)
-    {
-        Throwable cause = x.getCause();
-        notifyOnFailure(listener, cause == null ? x : cause);
-        goAway(x.getSessionStatus(), 0, TimeUnit.SECONDS, Callback.Adapter.INSTANCE);
-    }
-
-    private void onSyn(final SynStreamFrame frame)
-    {
-        IStream stream = createStream(frame, null, false, new Promise.Adapter<Stream>()
-        {
-            @Override
-            public void failed(Throwable x)
-            {
-                LOG.debug("Received: {} but creating new Stream failed: {}", frame, x.getMessage());
-            }
-        });
-        if (stream != null)
-            processSyn(listener, stream, frame);
-    }
-
-    private void processSyn(SessionFrameListener listener, IStream stream, SynStreamFrame frame)
-    {
-        stream.process(frame);
-        // Update the last stream id before calling the application (which may send a GO_AWAY)
-        updateLastStreamId(stream);
-        StreamFrameListener streamListener;
-        if (stream.isUnidirectional())
-        {
-            PushInfo pushInfo = new PushInfo(frame.getHeaders(), frame.isClose());
-            streamListener = notifyOnPush(stream.getAssociatedStream().getStreamFrameListener(), stream, pushInfo);
-        }
-        else
-        {
-            SynInfo synInfo = new SynInfo(frame.getHeaders(), frame.isClose(), frame.getPriority());
-            streamListener = notifyOnSyn(listener, stream, synInfo);
-        }
-        stream.setStreamFrameListener(streamListener);
-        // The onSyn() listener may have sent a frame that closed the stream
-        if (stream.isClosed())
-            removeStream(stream);
-    }
-
-    private IStream createStream(SynStreamFrame frame, StreamFrameListener listener, boolean local, Promise<Stream> promise)
-    {
-        IStream associatedStream = streams.get(frame.getAssociatedStreamId());
-        IStream stream = new StandardStream(frame.getStreamId(), frame.getPriority(), this, associatedStream,
-                scheduler, promise);
-        stream.setIdleTimeout(endPoint.getIdleTimeout());
-        flowControlStrategy.onNewStream(this, stream);
-
-        stream.updateCloseState(frame.isClose(), local);
-        stream.setStreamFrameListener(listener);
-
-        if (stream.isUnidirectional())
-        {
-            // Unidirectional streams are implicitly half closed
-            stream.updateCloseState(true, !local);
-            if (!stream.isClosed())
-                stream.getAssociatedStream().associate(stream);
-        }
-
-        int streamId = stream.getId();
-
-        if (local)
-        {
-            while (true)
-            {
-                int oldStreamCountValue = localStreamCount.get();
-                int maxConcurrentStreams = maxConcurrentLocalStreams;
-                if (maxConcurrentStreams > -1 && oldStreamCountValue >= maxConcurrentStreams)
-                {
-                    String message = String.format("Max concurrent local streams (%d) exceeded.",
-                            maxConcurrentStreams);
-                    LOG.debug(message);
-                    promise.failed(new SPDYException(message));
-                    return null;
-                }
-                if (localStreamCount.compareAndSet(oldStreamCountValue, oldStreamCountValue + 1))
-                    break;
-            }
-        }
-
-        if (streams.putIfAbsent(streamId, stream) != null)
-        {
-            String message = "Duplicate stream id " + streamId;
-            IllegalStateException duplicateIdException = new IllegalStateException(message);
-            promise.failed(duplicateIdException);
-            if (local)
-            {
-                localStreamCount.decrementAndGet();
-                throw duplicateIdException;
-            }
-            RstInfo rstInfo = new RstInfo(streamId, StreamStatus.PROTOCOL_ERROR);
-            if (LOG.isDebugEnabled())
-                LOG.debug("Duplicate stream, {}", rstInfo);
-            rst(rstInfo, Callback.Adapter.INSTANCE); // We don't care (too much) if the reset fails.
-            return null;
-        }
-        else
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Created {}", stream);
-            notifyStreamCreated(stream);
-            return stream;
-        }
-    }
-
-    private void notifyStreamCreated(IStream stream)
-    {
-        for (Listener listener : listeners)
-        {
-            if (listener instanceof StreamListener)
-            {
-                try
-                {
-                    ((StreamListener)listener).onStreamCreated(stream);
-                }
-                catch (Exception x)
-                {
-                    LOG.info("Exception while notifying listener " + listener, x);
-                }
-                catch (Error x)
-                {
-                    LOG.info("Exception while notifying listener " + listener, x);
-                    throw x;
-                }
-            }
-        }
-    }
-
-    private void removeStream(IStream stream)
-    {
-        if (stream.isUnidirectional())
-            stream.getAssociatedStream().disassociate(stream);
-
-        IStream removed = streams.remove(stream.getId());
-        if (removed != null)
-        {
-            assert removed == stream;
-
-            if (streamIds.get() % 2 == stream.getId() % 2)
-                localStreamCount.decrementAndGet();
-
-            if (LOG.isDebugEnabled())
-                LOG.debug("Removed {}", stream);
-            notifyStreamClosed(stream);
-        }
-    }
-
-    private void notifyStreamClosed(IStream stream)
-    {
-        for (Listener listener : listeners)
-        {
-            if (listener instanceof StreamListener)
-            {
-                try
-                {
-                    ((StreamListener)listener).onStreamClosed(stream);
-                }
-                catch (Exception x)
-                {
-                    LOG.info("Exception while notifying listener " + listener, x);
-                }
-                catch (Error x)
-                {
-                    LOG.info("Exception while notifying listener " + listener, x);
-                    throw x;
-                }
-            }
-        }
-    }
-
-    private void onReply(SynReplyFrame frame)
-    {
-        int streamId = frame.getStreamId();
-        IStream stream = streams.get(streamId);
-        if (stream == null)
-        {
-            RstInfo rstInfo = new RstInfo(streamId, StreamStatus.INVALID_STREAM);
-            if (LOG.isDebugEnabled())
-                LOG.debug("Unknown stream {}", rstInfo);
-            rst(rstInfo, Callback.Adapter.INSTANCE);
-        }
-        else
-        {
-            processReply(stream, frame);
-        }
-    }
-
-    private void processReply(IStream stream, SynReplyFrame frame)
-    {
-        stream.process(frame);
-        if (stream.isClosed())
-            removeStream(stream);
-    }
-
-    private void onRst(RstStreamFrame frame)
-    {
-        IStream stream = streams.get(frame.getStreamId());
-
-        if (stream != null)
-            stream.process(frame);
-
-        RstInfo rstInfo = new RstInfo(frame.getStreamId(), StreamStatus.from(frame.getVersion(), frame.getStatusCode()));
-        notifyOnRst(listener, rstInfo);
-
-        if (stream != null)
-            removeStream(stream);
-    }
-
-    private void onSettings(SettingsFrame frame)
-    {
-        Settings.Setting windowSizeSetting = frame.getSettings().get(Settings.ID.INITIAL_WINDOW_SIZE);
-        if (windowSizeSetting != null)
-        {
-            int windowSize = windowSizeSetting.value();
-            setWindowSize(windowSize);
-            if (LOG.isDebugEnabled())
-                LOG.debug("Updated session window size to {}", windowSize);
-        }
-        Settings.Setting maxConcurrentStreamsSetting = frame.getSettings().get(Settings.ID.MAX_CONCURRENT_STREAMS);
-        if (maxConcurrentStreamsSetting != null)
-        {
-            int maxConcurrentStreamsValue = maxConcurrentStreamsSetting.value();
-            maxConcurrentLocalStreams = maxConcurrentStreamsValue;
-            if (LOG.isDebugEnabled())
-                LOG.debug("Updated session maxConcurrentLocalStreams to {}", maxConcurrentStreamsValue);
-        }
-        SettingsInfo settingsInfo = new SettingsInfo(frame.getSettings(), frame.isClearPersisted());
-        notifyOnSettings(listener, settingsInfo);
-    }
-
-    private void onPing(PingFrame frame)
-    {
-        int pingId = frame.getPingId();
-        if (pingId % 2 == pingIds.get() % 2)
-        {
-            PingResultInfo pingResultInfo = new PingResultInfo(frame.getPingId());
-            notifyOnPing(listener, pingResultInfo);
-        }
-        else
-        {
-            control(null, frame, 0, TimeUnit.MILLISECONDS, Callback.Adapter.INSTANCE);
-        }
-    }
-
-    private void onGoAway(GoAwayFrame frame)
-    {
-        if (goAwayReceived.compareAndSet(false, true))
-        {
-            GoAwayResultInfo goAwayResultInfo = new GoAwayResultInfo(frame.getLastStreamId(), SessionStatus.from(frame.getStatusCode()));
-            notifyOnGoAway(listener, goAwayResultInfo);
-            // SPDY does not require to send back a response to a GO_AWAY.
-            // We notified the application of the last good stream id and
-            // tried our best to flush remaining data.
-        }
-    }
-
-    private void onHeaders(HeadersFrame frame)
-    {
-        int streamId = frame.getStreamId();
-        IStream stream = streams.get(streamId);
-        if (stream == null)
-        {
-            RstInfo rstInfo = new RstInfo(streamId, StreamStatus.INVALID_STREAM);
-            if (LOG.isDebugEnabled())
-                LOG.debug("Unknown stream, {}", rstInfo);
-            rst(rstInfo, Callback.Adapter.INSTANCE);
-        }
-        else
-        {
-            processHeaders(stream, frame);
-        }
-    }
-
-    private void processHeaders(IStream stream, HeadersFrame frame)
-    {
-        stream.process(frame);
-        if (stream.isClosed())
-            removeStream(stream);
-    }
-
-    private void onWindowUpdate(WindowUpdateFrame frame)
-    {
-        int streamId = frame.getStreamId();
-        IStream stream = streams.get(streamId);
-        flowControlStrategy.onWindowUpdate(this, stream, frame.getWindowDelta());
-        flusher.flush();
-    }
-
-    private void onCredential(CredentialFrame frame)
-    {
-        LOG.warn("{} frame not yet supported", frame.getType());
-    }
-
-    protected void close()
-    {
-        // Check for null to support tests
-        if (controller != null)
-            controller.close(false);
-    }
-
-    private void notifyOnFailure(SessionFrameListener listener, Throwable x)
-    {
-        try
-        {
-            if (listener != null)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Invoking callback with {} on listener {}", x, listener);
-                listener.onFailure(this, x);
-            }
-        }
-        catch (Exception xx)
-        {
-            LOG.info("Exception while notifying listener " + listener, xx);
-        }
-        catch (Error xx)
-        {
-            LOG.info("Exception while notifying listener " + listener, xx);
-            throw xx;
-        }
-    }
-
-    private StreamFrameListener notifyOnPush(StreamFrameListener listener, Stream stream, PushInfo pushInfo)
-    {
-        try
-        {
-            if (listener == null)
-                return null;
-            if (LOG.isDebugEnabled())
-                LOG.debug("Invoking callback with {} on listener {}", pushInfo, listener);
-            return listener.onPush(stream, pushInfo);
-        }
-        catch (Exception x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-            return null;
-        }
-        catch (Error x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-            throw x;
-        }
-    }
-
-    private StreamFrameListener notifyOnSyn(SessionFrameListener listener, Stream stream, SynInfo synInfo)
-    {
-        try
-        {
-            if (listener == null)
-                return null;
-            if (LOG.isDebugEnabled())
-                LOG.debug("Invoking callback with {} on listener {}", synInfo, listener);
-            return listener.onSyn(stream, synInfo);
-        }
-        catch (Exception x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-            return null;
-        }
-        catch (Error x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-            throw x;
-        }
-    }
-
-    private void notifyOnRst(SessionFrameListener listener, RstInfo rstInfo)
-    {
-        try
-        {
-            if (listener != null)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Invoking callback with {} on listener {}", rstInfo, listener);
-                listener.onRst(this, rstInfo);
-            }
-        }
-        catch (Exception x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-        }
-        catch (Error x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-            throw x;
-        }
-    }
-
-    private void notifyOnSettings(SessionFrameListener listener, SettingsInfo settingsInfo)
-    {
-        try
-        {
-            if (listener != null)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Invoking callback with {} on listener {}", settingsInfo, listener);
-                listener.onSettings(this, settingsInfo);
-            }
-        }
-        catch (Exception x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-        }
-        catch (Error x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-            throw x;
-        }
-    }
-
-    private void notifyOnPing(SessionFrameListener listener, PingResultInfo pingResultInfo)
-    {
-        try
-        {
-            if (listener != null)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Invoking callback with {} on listener {}", pingResultInfo, listener);
-                listener.onPing(this, pingResultInfo);
-            }
-        }
-        catch (Exception x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-        }
-        catch (Error x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-            throw x;
-        }
-    }
-
-    private void notifyOnGoAway(SessionFrameListener listener, GoAwayResultInfo goAwayResultInfo)
-    {
-        try
-        {
-            if (listener != null)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Invoking callback with {} on listener {}", goAwayResultInfo, listener);
-                listener.onGoAway(this, goAwayResultInfo);
-            }
-        }
-        catch (Exception x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-        }
-        catch (Error x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-            throw x;
-        }
-    }
-
-    @Override
-    public void control(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Callback callback)
-    {
-        generateAndEnqueueControlFrame(stream, frame, timeout, unit, callback);
-    }
-
-    private void generateAndEnqueueControlFrame(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Callback callback)
-    {
-        try
-        {
-            // Synchronization is necessary, since we may have concurrent replies
-            // and those needs to be generated and enqueued atomically in order
-            // to maintain a correct compression context
-            ControlFrameBytes frameBytes;
-            Throwable throwable;
-            synchronized (this)
-            {
-                ByteBuffer buffer = generator.control(frame);
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Queuing {} on {}", frame, stream);
-                frameBytes = new ControlFrameBytes(stream, callback, frame, buffer);
-                if (timeout > 0)
-                    frameBytes.task = scheduler.schedule(frameBytes, timeout, unit);
-
-                // Special handling for PING frames, they must be sent as soon as possible
-                if (ControlFrameType.PING == frame.getType())
-                    throwable = flusher.prepend(frameBytes);
-                else
-                    throwable = flusher.append(frameBytes);
-            }
-            // Flush MUST be done outside synchronized blocks
-            flush(frameBytes, throwable);
-        }
-        catch (Exception x)
-        {
-            notifyCallbackFailed(callback, x);
-        }
-    }
-
-    private void updateLastStreamId(IStream stream)
-    {
-        int streamId = stream.getId();
-        if (streamId % 2 != streamIds.get() % 2)
-            Atomics.updateMax(lastStreamId, streamId);
-    }
-
-    @Override
-    public void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Callback callback)
-    {
-        if (LOG.isDebugEnabled())
-            LOG.debug("Queuing {} on {}", dataInfo, stream);
-        DataFrameBytes frameBytes = new DataFrameBytes(stream, callback, dataInfo);
-        if (timeout > 0)
-            frameBytes.task = scheduler.schedule(frameBytes, timeout, unit);
-        flush(frameBytes, flusher.append(frameBytes));
-    }
-
-    @Override
-    public void shutdown()
-    {
-        CloseFrameBytes frameBytes = new CloseFrameBytes();
-        flush(frameBytes, flusher.append(frameBytes));
-    }
-
-    private void flush(FrameBytes frameBytes, Throwable throwable)
-    {
-        if (throwable != null)
-            frameBytes.failed(throwable);
-        else
-            flusher.flush();
-    }
-
-    private void complete(final Callback callback)
-    {
-        try
-        {
-            if (callback != null)
-                callback.succeeded();
-        }
-        catch (Throwable x)
-        {
-            LOG.info("Exception while notifying callback " + callback, x);
-        }
-    }
-
-    private void notifyCallbackFailed(Callback callback, Throwable failure)
-    {
-        try
-        {
-            if (callback != null)
-                callback.failed(failure);
-        }
-        catch (Throwable x)
-        {
-            LOG.info("Exception while notifying callback " + callback, x);
-        }
-    }
-
-    public int getWindowSize()
-    {
-        return flowControlStrategy.getWindowSize(this);
-    }
-
-    public void setWindowSize(int initialWindowSize)
-    {
-        flowControlStrategy.setWindowSize(this, initialWindowSize);
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("%s@%x{v%d,queueSize=%d,windowSize=%d,streams=%d}", getClass().getSimpleName(),
-                hashCode(), version, flusher.getQueueSize(), getWindowSize(), streams.size());
-    }
-
-    @Override
-    public String dump()
-    {
-        return ContainerLifeCycle.dump(this);
-    }
-
-    @Override
-    public void dump(Appendable out, String indent) throws IOException
-    {
-        ContainerLifeCycle.dumpObject(out, this);
-        ContainerLifeCycle.dump(out, indent, Collections.singletonList(controller), streams.values());
-    }
-
-    public interface FrameBytes extends Comparable<FrameBytes>, Callback
-    {
-        public IStream getStream();
-
-        public abstract ByteBuffer getByteBuffer();
-    }
-
-    abstract class AbstractFrameBytes implements FrameBytes, Runnable
-    {
-        private final IStream stream;
-        private final Callback callback;
-        protected volatile Scheduler.Task task;
-
-        protected AbstractFrameBytes(IStream stream, Callback callback)
-        {
-            this.stream = stream;
-            this.callback = Objects.requireNonNull(callback);
-        }
-
-        @Override
-        public IStream getStream()
-        {
-            return stream;
-        }
-
-        @Override
-        public int compareTo(FrameBytes that)
-        {
-            // FrameBytes may have or not have a related stream (for example, PING do not have a related stream)
-            // FrameBytes without related streams have higher priority
-            IStream thisStream = getStream();
-            IStream thatStream = that.getStream();
-            if (thisStream == null)
-                return thatStream == null ? 0 : -1;
-            if (thatStream == null)
-                return 1;
-            // If this.stream.priority > that.stream.priority => this.stream has less priority than that.stream
-            return thatStream.getPriority() - thisStream.getPriority();
-        }
-
-        private void cancelTask()
-        {
-            Scheduler.Task task = this.task;
-            if (task != null)
-                task.cancel();
-        }
-
-        @Override
-        public void run()
-        {
-            close();
-            failed(new InterruptedByTimeoutException());
-        }
-
-        @Override
-        public void succeeded()
-        {
-            cancelTask();
-            StandardSession.this.complete(callback);
-        }
-
-        @Override
-        public void failed(Throwable x)
-        {
-            cancelTask();
-            notifyCallbackFailed(callback, x);
-        }
-    }
-
-    protected class ControlFrameBytes extends AbstractFrameBytes
-    {
-        private final ControlFrame frame;
-        private final ByteBuffer buffer;
-
-        private ControlFrameBytes(IStream stream, Callback callback, ControlFrame frame, ByteBuffer buffer)
-        {
-            super(stream, callback);
-            this.frame = frame;
-            this.buffer = buffer;
-        }
-
-        @Override
-        public ByteBuffer getByteBuffer()
-        {
-            return buffer;
-        }
-
-        @Override
-        public void succeeded()
-        {
-            bufferPool.release(buffer);
-
-            super.succeeded();
-
-            if (frame.getType() == ControlFrameType.GO_AWAY)
-            {
-                // After sending a GO_AWAY we need to hard close the connection.
-                // Recipients will know the last good stream id and act accordingly.
-                close();
-            }
-            IStream stream = getStream();
-            if (stream != null && stream.isClosed())
-                removeStream(stream);
-        }
-
-        @Override
-        public String toString()
-        {
-            return frame.toString();
-        }
-    }
-
-    protected class DataFrameBytes extends AbstractFrameBytes
-    {
-        private final DataInfo dataInfo;
-        private int size;
-        private volatile ByteBuffer buffer;
-
-        private DataFrameBytes(IStream stream, Callback handler, DataInfo dataInfo)
-        {
-            super(stream, handler);
-            this.dataInfo = dataInfo;
-        }
-
-        @Override
-        public ByteBuffer getByteBuffer()
-        {
-            try
-            {
-                IStream stream = getStream();
-                int windowSize = stream.getWindowSize();
-
-                // TODO: optimization
-                // Right now, we use the windowSize to chunk big buffers.
-                // However, if the window size is large, we may congest the
-                // connection, or favor one stream that does a big download,
-                // starving the other streams.
-                // Also, SPDY DATA frames have a maximum of 16 MiB size, which
-                // is not enforced here.
-                // We should have a configurable "preferredDataFrameSize"
-                // (or even better autotuning) that will allow to send chunks
-                // that will not starve other streams and small enough to
-                // not congest the connection, while avoiding to send too many
-                // TCP packets.
-                // See also comment in class Flusher.
-
-                size = dataInfo.available();
-                if (size > windowSize)
-                    size = windowSize;
-
-                buffer = generator.data(stream.getId(), size, dataInfo);
-                return buffer;
-            }
-            catch (Throwable x)
-            {
-                failed(x);
-                return null;
-            }
-        }
-
-        @Override
-        public void succeeded()
-        {
-            bufferPool.release(buffer);
-            IStream stream = getStream();
-            dataInfo.consume(size);
-            flowControlStrategy.updateWindow(StandardSession.this, stream, -size);
-            if (dataInfo.available() > 0)
-            {
-                // We have written a frame out of this DataInfo, but there is more to write.
-                // We need to keep the correct ordering of frames, to avoid that another
-                // DataInfo for the same stream is written before this one is finished.
-                flush(this, flusher.prepend(this));
-            }
-            else
-            {
-                super.succeeded();
-                stream.updateCloseState(dataInfo.isClose(), true);
-                if (stream.isClosed())
-                    removeStream(stream);
-            }
-        }
-
-        @Override
-        public String toString()
-        {
-            return String.format("DATA bytes @%x available=%d consumed=%d on %s", dataInfo.hashCode(), dataInfo.available(), dataInfo.consumed(), getStream());
-        }
-    }
-
-    protected class CloseFrameBytes extends AbstractFrameBytes
-    {
-        private CloseFrameBytes()
-        {
-            super(null, Callback.Adapter.INSTANCE);
-        }
-
-        @Override
-        public ByteBuffer getByteBuffer()
-        {
-            return BufferUtil.EMPTY_BUFFER;
-        }
-
-        @Override
-        public void succeeded()
-        {
-            super.succeeded();
-            close();
-        }
-    }
-
-    private static class PingInfoCallback extends PingResultInfo implements Callback
-    {
-        private final Promise<PingResultInfo> promise;
-
-        public PingInfoCallback(int pingId, Promise<PingResultInfo> promise)
-        {
-            super(pingId);
-            this.promise = promise;
-        }
-
-        @Override
-        public void succeeded()
-        {
-            if (promise != null)
-                promise.succeeded(this);
-        }
-
-        @Override
-        public void failed(Throwable x)
-        {
-            if (promise != null)
-                promise.failed(x);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardStream.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardStream.java
deleted file mode 100644
index 2cb41d9..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardStream.java
+++ /dev/null
@@ -1,602 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.eclipse.jetty.io.IdleTimeout;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.HeadersFrame;
-import org.eclipse.jetty.spdy.frames.SynReplyFrame;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.FutureCallback;
-import org.eclipse.jetty.util.FuturePromise;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.thread.Scheduler;
-
-public class StandardStream extends IdleTimeout implements IStream
-{
-    private static final Logger LOG = Log.getLogger(Stream.class);
-    private final Map<String, Object> attributes = new ConcurrentHashMap<>();
-    private final int id;
-    private final byte priority;
-    private final ISession session;
-    private final IStream associatedStream;
-    private final Promise<Stream> promise;
-    private final AtomicInteger windowSize = new AtomicInteger();
-    private final Set<Stream> pushedStreams = Collections.newSetFromMap(new ConcurrentHashMap<Stream, Boolean>());
-    private volatile StreamFrameListener listener;
-    private volatile OpenState openState = OpenState.SYN_SENT;
-    private volatile CloseState closeState = CloseState.OPENED;
-    private volatile boolean reset = false;
-
-    public StandardStream(int id, byte priority, ISession session, IStream associatedStream, Scheduler scheduler, Promise<Stream> promise)
-    {
-        super(scheduler);
-        this.id = id;
-        this.priority = priority;
-        this.session = session;
-        this.associatedStream = associatedStream;
-        this.promise = promise;
-    }
-
-    @Override
-    public int getId()
-    {
-        return id;
-    }
-
-    @Override
-    public IStream getAssociatedStream()
-    {
-        return associatedStream;
-    }
-
-    @Override
-    public Set<Stream> getPushedStreams()
-    {
-        return pushedStreams;
-    }
-
-    @Override
-    public void associate(IStream stream)
-    {
-        pushedStreams.add(stream);
-    }
-
-    @Override
-    public void disassociate(IStream stream)
-    {
-        pushedStreams.remove(stream);
-    }
-
-    @Override
-    public byte getPriority()
-    {
-        return priority;
-    }
-
-    @Override
-    protected void onIdleExpired(TimeoutException timeout)
-    {
-        StreamFrameListener listener = this.listener;
-        if (listener != null)
-            listener.onFailure(this, timeout);
-        // The stream is now gone, we must close it to
-        // avoid that its idle timeout is rescheduled.
-        close();
-    }
-
-    private void close()
-    {
-        closeState = CloseState.CLOSED;
-        onClose();
-    }
-
-    @Override
-    public boolean isOpen()
-    {
-        return !isClosed();
-    }
-
-    @Override
-    public int getWindowSize()
-    {
-        return windowSize.get();
-    }
-
-    @Override
-    public void updateWindowSize(int delta)
-    {
-        int size = windowSize.addAndGet(delta);
-        if (LOG.isDebugEnabled())
-            LOG.debug("Updated window size {} -> {} for {}", size - delta, size, this);
-    }
-
-    @Override
-    public ISession getSession()
-    {
-        return session;
-    }
-
-    @Override
-    public Object getAttribute(String key)
-    {
-        return attributes.get(key);
-    }
-
-    @Override
-    public void setAttribute(String key, Object value)
-    {
-        attributes.put(key, value);
-    }
-
-    @Override
-    public Object removeAttribute(String key)
-    {
-        return attributes.remove(key);
-    }
-
-    @Override
-    public void setStreamFrameListener(StreamFrameListener listener)
-    {
-        this.listener = listener;
-    }
-
-    @Override
-    public StreamFrameListener getStreamFrameListener()
-    {
-        return listener;
-    }
-
-    @Override
-    public void updateCloseState(boolean close, boolean local)
-    {
-        if (LOG.isDebugEnabled())
-            LOG.debug("{} close={} local={}", this, close, local);
-        if (close)
-        {
-            switch (closeState)
-            {
-                case OPENED:
-                {
-                    closeState = local ? CloseState.LOCALLY_CLOSED : CloseState.REMOTELY_CLOSED;
-                    break;
-                }
-                case LOCALLY_CLOSED:
-                {
-                    if (local)
-                        throw new IllegalStateException();
-                    else
-                        close();
-                    break;
-                }
-                case REMOTELY_CLOSED:
-                {
-                    if (local)
-                        close();
-                    else
-                        throw new IllegalStateException();
-                    break;
-                }
-                default:
-                {
-                    LOG.warn("Already CLOSED! {} local={}", this, local);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void process(ControlFrame frame)
-    {
-        notIdle();
-        switch (frame.getType())
-        {
-            case SYN_STREAM:
-            {
-                openState = OpenState.SYN_RECV;
-                break;
-            }
-            case SYN_REPLY:
-            {
-                openState = OpenState.REPLY_RECV;
-                SynReplyFrame synReply = (SynReplyFrame)frame;
-                updateCloseState(synReply.isClose(), false);
-                ReplyInfo replyInfo = new ReplyInfo(synReply.getHeaders(), synReply.isClose());
-                notifyOnReply(replyInfo);
-                break;
-            }
-            case HEADERS:
-            {
-                HeadersFrame headers = (HeadersFrame)frame;
-                updateCloseState(headers.isClose(), false);
-                HeadersInfo headersInfo = new HeadersInfo(headers.getHeaders(), headers.isClose(), headers.isResetCompression());
-                notifyOnHeaders(headersInfo);
-                break;
-            }
-            case RST_STREAM:
-            {
-                reset = true;
-                break;
-            }
-            default:
-            {
-                throw new IllegalStateException();
-            }
-        }
-    }
-
-    @Override
-    public void process(DataInfo dataInfo)
-    {
-        notIdle();
-        // TODO: in v3 we need to send a rst instead of just ignoring
-        // ignore data frame if this stream is remotelyClosed already
-        if (isRemotelyClosed())
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Stream is remotely closed, ignoring {}", dataInfo);
-            return;
-        }
-
-        if (!canReceive())
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Protocol error receiving {}, resetting", dataInfo);
-            session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR), Callback.Adapter.INSTANCE);
-            return;
-        }
-
-        updateCloseState(dataInfo.isClose(), false);
-        notifyOnData(dataInfo);
-    }
-
-    @Override
-    public void succeeded()
-    {
-        if (promise != null)
-            promise.succeeded(this);
-    }
-
-    @Override
-    public void failed(Throwable x)
-    {
-        if (promise != null)
-            promise.failed(x);
-    }
-
-    private void notifyOnReply(ReplyInfo replyInfo)
-    {
-        final StreamFrameListener listener = this.listener;
-        try
-        {
-            if (listener != null)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Invoking reply callback with {} on listener {}", replyInfo, listener);
-                listener.onReply(this, replyInfo);
-            }
-        }
-        catch (Exception x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-        }
-        catch (Error x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-            throw x;
-        }
-    }
-
-    private void notifyOnHeaders(HeadersInfo headersInfo)
-    {
-        final StreamFrameListener listener = this.listener;
-        try
-        {
-            if (listener != null)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Invoking headers callback with {} on listener {}", headersInfo, listener);
-                listener.onHeaders(this, headersInfo);
-            }
-        }
-        catch (Exception x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-        }
-        catch (Error x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-            throw x;
-        }
-    }
-
-    private void notifyOnData(DataInfo dataInfo)
-    {
-        final StreamFrameListener listener = this.listener;
-        try
-        {
-            if (listener != null)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Invoking data callback with {} on listener {}", dataInfo, listener);
-                listener.onData(this, dataInfo);
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Invoked data callback with {} on listener {}", dataInfo, listener);
-            }
-        }
-        catch (Exception x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-        }
-        catch (Error x)
-        {
-            LOG.info("Exception while notifying listener " + listener, x);
-            throw x;
-        }
-    }
-
-    @Override
-    public Stream push(PushInfo pushInfo) throws InterruptedException, ExecutionException, TimeoutException
-    {
-        FuturePromise<Stream> result = new FuturePromise<>();
-        push(pushInfo, result);
-        if (pushInfo.getTimeout() > 0)
-            return result.get(pushInfo.getTimeout(), pushInfo.getUnit());
-        else
-            return result.get();
-    }
-
-    @Override
-    public void push(PushInfo pushInfo, Promise<Stream> promise)
-    {
-        notIdle();
-        if (isClosed() || isReset())
-        {
-            close();
-            promise.failed(new StreamException(getId(), StreamStatus.STREAM_ALREADY_CLOSED,
-                    "Stream: " + this + " already closed or reset!"));
-            return;
-        }
-        PushSynInfo pushSynInfo = new PushSynInfo(getId(), pushInfo);
-        session.syn(pushSynInfo, null, new StreamPromise(promise));
-    }
-
-    @Override
-    public void reply(ReplyInfo replyInfo) throws InterruptedException, ExecutionException, TimeoutException
-    {
-        FutureCallback result = new FutureCallback();
-        reply(replyInfo, result);
-        if (replyInfo.getTimeout() > 0)
-            result.get(replyInfo.getTimeout(), replyInfo.getUnit());
-        else
-            result.get();
-    }
-
-    @Override
-    public void reply(ReplyInfo replyInfo, Callback callback)
-    {
-        notIdle();
-        if (isUnidirectional())
-        {
-            close();
-            throw new IllegalStateException("Protocol violation: cannot send SYN_REPLY frames in unidirectional streams");
-        }
-        openState = OpenState.REPLY_SENT;
-        updateCloseState(replyInfo.isClose(), true);
-        SynReplyFrame frame = new SynReplyFrame(session.getVersion(), replyInfo.getFlags(), getId(), replyInfo.getHeaders());
-        session.control(this, frame, replyInfo.getTimeout(), replyInfo.getUnit(), new StreamCallback(callback));
-    }
-
-    @Override
-    public void data(DataInfo dataInfo) throws InterruptedException, ExecutionException, TimeoutException
-    {
-        FutureCallback result = new FutureCallback();
-        data(dataInfo, result);
-        if (dataInfo.getTimeout() > 0)
-            result.get(dataInfo.getTimeout(), dataInfo.getUnit());
-        else
-            result.get();
-    }
-
-    @Override
-    public void data(DataInfo dataInfo, Callback callback)
-    {
-        notIdle();
-        if (!canSend())
-        {
-            session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR), new StreamCallback());
-            throw new IllegalStateException("Protocol violation: cannot send a DATA frame before a SYN_REPLY frame");
-        }
-        if (isLocallyClosed())
-        {
-            session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR), new StreamCallback());
-            throw new IllegalStateException("Protocol violation: cannot send a DATA frame on a locally closed stream");
-        }
-
-        // Cannot update the close state here, because the data that we send may
-        // be flow controlled, so we need the stream to update the window size.
-        session.data(this, dataInfo, dataInfo.getTimeout(), dataInfo.getUnit(), new StreamCallback(callback));
-    }
-
-    @Override
-    public void headers(HeadersInfo headersInfo) throws InterruptedException, ExecutionException, TimeoutException
-    {
-        FutureCallback result = new FutureCallback();
-        headers(headersInfo, result);
-        if (headersInfo.getTimeout() > 0)
-            result.get(headersInfo.getTimeout(), headersInfo.getUnit());
-        else
-            result.get();
-    }
-
-    @Override
-    public void headers(HeadersInfo headersInfo, Callback callback)
-    {
-        notIdle();
-        if (!canSend())
-        {
-            session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR), new StreamCallback());
-            throw new IllegalStateException("Protocol violation: cannot send a HEADERS frame before a SYN_REPLY frame");
-        }
-        if (isLocallyClosed())
-        {
-            session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR), new StreamCallback());
-            throw new IllegalStateException("Protocol violation: cannot send a HEADERS frame on a closed stream");
-        }
-
-        updateCloseState(headersInfo.isClose(), true);
-        HeadersFrame frame = new HeadersFrame(session.getVersion(), headersInfo.getFlags(), getId(), headersInfo.getHeaders());
-        session.control(this, frame, headersInfo.getTimeout(), headersInfo.getUnit(), new StreamCallback(callback));
-    }
-
-    @Override
-    public boolean isUnidirectional()
-    {
-        return associatedStream != null;
-    }
-
-    @Override
-    public boolean isReset()
-    {
-        return reset;
-    }
-
-    @Override
-    public boolean isHalfClosed()
-    {
-        CloseState closeState = this.closeState;
-        return closeState == CloseState.LOCALLY_CLOSED || closeState == CloseState.REMOTELY_CLOSED || closeState == CloseState.CLOSED;
-    }
-
-    @Override
-    public boolean isClosed()
-    {
-        return closeState == CloseState.CLOSED;
-    }
-
-    private boolean isLocallyClosed()
-    {
-        CloseState closeState = this.closeState;
-        return closeState == CloseState.LOCALLY_CLOSED || closeState == CloseState.CLOSED;
-    }
-
-    private boolean isRemotelyClosed()
-    {
-        CloseState closeState = this.closeState;
-        return closeState == CloseState.REMOTELY_CLOSED || closeState == CloseState.CLOSED;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("stream=%d v%d windowSize=%d reset=%s prio=%d %s %s", getId(), session.getVersion(),
-                getWindowSize(), isReset(), priority, openState, closeState);
-    }
-
-    private boolean canSend()
-    {
-        OpenState openState = this.openState;
-        return openState == OpenState.SYN_SENT || openState == OpenState.REPLY_RECV || openState == OpenState.REPLY_SENT;
-    }
-
-    private boolean canReceive()
-    {
-        OpenState openState = this.openState;
-        return openState == OpenState.SYN_RECV || openState == OpenState.REPLY_RECV || openState == OpenState.REPLY_SENT;
-    }
-
-    private enum OpenState
-    {
-        SYN_SENT, SYN_RECV, REPLY_SENT, REPLY_RECV
-    }
-
-    private enum CloseState
-    {
-        OPENED, LOCALLY_CLOSED, REMOTELY_CLOSED, CLOSED
-    }
-
-    private class StreamCallback implements Callback
-    {
-        private final Callback callback;
-
-        private StreamCallback()
-        {
-            this(Callback.Adapter.INSTANCE);
-        }
-
-        private StreamCallback(Callback callback)
-        {
-            this.callback = callback;
-        }
-
-        @Override
-        public void succeeded()
-        {
-            callback.succeeded();
-        }
-
-        @Override
-        public void failed(Throwable x)
-        {
-            close();
-            callback.failed(x);
-        }
-    }
-
-    private class StreamPromise implements Promise<Stream>
-    {
-        private final Promise<Stream> promise;
-
-        public StreamPromise(Promise<Stream> promise)
-        {
-            this.promise = promise;
-        }
-
-        @Override
-        public void succeeded(Stream result)
-        {
-            promise.succeeded(result);
-        }
-
-        @Override
-        public void failed(Throwable x)
-        {
-            close();
-            promise.failed(x);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StreamException.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StreamException.java
deleted file mode 100644
index 6bb0bd3..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StreamException.java
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import org.eclipse.jetty.spdy.api.StreamStatus;
-
-public class StreamException extends RuntimeException
-{
-    private final int streamId;
-    private final StreamStatus streamStatus;
-
-    public StreamException(int streamId, StreamStatus streamStatus)
-    {
-        this.streamId = streamId;
-        this.streamStatus = streamStatus;
-    }
-
-    public StreamException(int streamId, StreamStatus streamStatus, String message)
-    {
-        super(message);
-        this.streamId = streamId;
-        this.streamStatus = streamStatus;
-    }
-
-    public int getStreamId()
-    {
-        return streamId;
-    }
-
-    public StreamStatus getStreamStatus()
-    {
-        return streamStatus;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ByteBufferDataInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ByteBufferDataInfo.java
deleted file mode 100644
index 7f54bcb..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ByteBufferDataInfo.java
+++ /dev/null
@@ -1,90 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.TimeUnit;
-
-/**
- * <p>Specialized {@link DataInfo} for {@link ByteBuffer} content.</p>
- */
-public class ByteBufferDataInfo extends DataInfo
-{
-    private final ByteBuffer buffer;
-    private final int length;
-
-    public ByteBufferDataInfo(ByteBuffer buffer, boolean close)
-    {
-        this(0, TimeUnit.SECONDS, buffer, close);
-    }
-
-    public ByteBufferDataInfo(long timeout, TimeUnit unit, ByteBuffer buffer, boolean close)
-    {
-        super(timeout, unit, close);
-        this.buffer = buffer;
-        this.length = buffer.remaining();
-    }
-
-    @Override
-    public int length()
-    {
-        return length;
-    }
-
-    @Override
-    public int available()
-    {
-        return buffer.remaining();
-    }
-
-    @Override
-    public int readInto(ByteBuffer output)
-    {
-        int space = output.remaining();
-        if (available() > space)
-        {
-            int limit = buffer.limit();
-            buffer.limit(buffer.position() + space);
-            output.put(buffer);
-            buffer.limit(limit);
-        }
-        else
-        {
-            space = buffer.remaining();
-            output.put(buffer);
-        }
-        return space;
-    }
-
-    @Override
-    public int readInto(byte[] bytes, int offset, int length)
-    {
-        int available = available();
-        if (available < length)
-            length = available;
-        buffer.get(bytes, offset, length);
-        return length;
-    }
-
-    @Override
-    protected ByteBuffer allocate(int size)
-    {
-        return buffer.isDirect() ? ByteBuffer.allocateDirect(size) : super.allocate(size);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/BytesDataInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/BytesDataInfo.java
deleted file mode 100644
index 4d35dda..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/BytesDataInfo.java
+++ /dev/null
@@ -1,83 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.TimeUnit;
-
-/**
- * <p>Specialized {@link DataInfo} for byte array content.</p>
- */
-public class BytesDataInfo extends DataInfo
-{
-    private final byte[] bytes;
-    private final int offset;
-    private final int length;
-    private int index;
-
-    public BytesDataInfo(byte[] bytes, boolean close)
-    {
-        this(0, TimeUnit.SECONDS, bytes, close);
-    }
-
-    public BytesDataInfo(long timeout, TimeUnit unit, byte[] bytes, boolean close)
-    {
-        this(timeout, unit, bytes, 0, bytes.length, close);
-    }
-
-    public BytesDataInfo(long timeout, TimeUnit unit, byte[] bytes, int offset, int length, boolean close)
-    {
-        super(timeout, unit, close);
-        this.bytes = bytes;
-        this.offset = offset;
-        this.length = length;
-        this.index = offset;
-    }
-
-    @Override
-    public int length()
-    {
-        return length;
-    }
-
-    @Override
-    public int available()
-    {
-        return length - index + offset;
-    }
-
-    @Override
-    public int readInto(ByteBuffer output)
-    {
-        int space = output.remaining();
-        int chunk = Math.min(available(), space);
-        output.put(bytes, index, chunk);
-        index += chunk;
-        return chunk;
-    }
-
-    @Override
-    public int readInto(byte[] bytes, int offset, int length)
-    {
-        int chunk = Math.min(available(), length);
-        System.arraycopy(this.bytes, index, bytes, offset, chunk);
-        index += chunk;
-        return chunk;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/DataInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/DataInfo.java
deleted file mode 100644
index 5ced21a..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/DataInfo.java
+++ /dev/null
@@ -1,264 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * <p>A container for DATA frames metadata and content bytes.</p>
- * <p>Specialized subclasses (like {@link StringDataInfo}) may be used by applications
- * to send specific types of content.</p>
- * <p>Applications may send multiple instances of {@link DataInfo}, usually of the same
- * type, via {@link Stream#data(DataInfo)}. The last instance must have the
- * {@link #isClose() close flag} set, so that the client knows that no more content is
- * expected.</p>
- * <p>Receivers of {@link DataInfo} via {@link StreamFrameListener#onData(Stream, DataInfo)}
- * have two different APIs to read the data content bytes: a {@link #readInto(ByteBuffer) read}
- * API that does not interact with flow control, and a {@link #consumeInto(ByteBuffer) drain}
- * API that interacts with flow control.</p>
- * <p>Flow control is defined so that when the sender wants to sends a number of bytes larger
- * than the {@link Settings.ID#INITIAL_WINDOW_SIZE} value, it will stop sending as soon as it
- * has sent a number of bytes equal to the window size. The receiver has to <em>consume</em>
- * the data bytes that it received in order to tell the sender to send more bytes.</p>
- * <p>Consuming the data bytes can be done only via {@link #consumeInto(ByteBuffer)} or by a combination
- * of {@link #readInto(ByteBuffer)} and {@link #consume(int)} (possibly at different times).</p>
- */
-public abstract class DataInfo extends Info
-{
-    /**
-     * <p>Flag that indicates that this {@link DataInfo} is the last frame in the stream.</p>
-     *
-     * @see #isClose()
-     * @see #getFlags()
-     */
-    public final static byte FLAG_CLOSE = 1;
-
-    private final AtomicInteger consumed = new AtomicInteger();
-    private boolean close;
-
-    /**
-     * <p>Creates a new {@link DataInfo} with the given close flag and no compression flag.</p>
-     *
-     * @param close the value of the close flag
-     */
-    public DataInfo(boolean close)
-    {
-        setClose(close);
-    }
-
-    /**
-     * <p>Creates a new {@link DataInfo} with the given close flag and no compression flag.</p>
-     *
-     * @param timeout
-     * @param unit
-     * @param close the value of the close flag
-     */
-    protected DataInfo(long timeout, TimeUnit unit, boolean close)
-    {
-        super(timeout, unit);
-        this.close = close;
-    }
-
-    /**
-     * @return the value of the close flag
-     * @see #setClose(boolean)
-     */
-    public boolean isClose()
-    {
-        return close;
-    }
-
-    /**
-     * @param close the value of the close flag
-     * @see #isClose()
-     */
-    public void setClose(boolean close)
-    {
-        this.close = close;
-    }
-
-    /**
-     * @return the close and compress flags as integer
-     * @see #FLAG_CLOSE
-     */
-    public byte getFlags()
-    {
-        return isClose() ? FLAG_CLOSE : 0;
-    }
-
-    /**
-     * @return the total number of content bytes
-     * @see #available()
-     */
-    public abstract int length();
-
-    /**
-     * <p>Returns the available content bytes that can be read via {@link #readInto(ByteBuffer)}.</p>
-     * <p>Each invocation to {@link #readInto(ByteBuffer)} modifies the value returned by this method,
-     * until no more content bytes are available.</p>
-     *
-     * @return the available content bytes
-     * @see #readInto(ByteBuffer)
-     */
-    public abstract int available();
-
-    /**
-     * <p>Copies the content bytes of this {@link DataInfo} into the given {@link ByteBuffer}.</p>
-     * <p>If the given {@link ByteBuffer} cannot contain the whole content of this {@link DataInfo}
-     * then after the read {@link #available()} will return a positive value, and further content
-     * may be retrieved by invoking again this method with a new output buffer.</p>
-     *
-     * @param output the {@link ByteBuffer} to copy the bytes into
-     * @return the number of bytes copied
-     * @see #available()
-     * @see #consumeInto(ByteBuffer)
-     */
-    public abstract int readInto(ByteBuffer output);
-
-    /**
-     * <p>Copies the content bytes of this {@link DataInfo} into the given byte array.</p>
-     * <p>If the given byte array cannot contain the whole content of this {@link DataInfo}
-     * then after the read {@link #available()} will return a positive value, and further content
-     * may be retrieved by invoking again this method with a new byte array.</p>
-     *
-     * @param bytes the byte array to copy the bytes into
-     * @param offset the index of the byte array to start copying
-     * @param length the number of bytes to copy
-     * @return the number of bytes copied
-     */
-    public abstract int readInto(byte[] bytes, int offset, int length);
-
-    /**
-     * <p>Reads and consumes the content bytes of this {@link DataInfo} into the given {@link ByteBuffer}.</p>
-     *
-     * @param output the {@link ByteBuffer} to copy the bytes into
-     * @return the number of bytes copied
-     * @see #consume(int)
-     */
-    public int consumeInto(ByteBuffer output)
-    {
-        int read = readInto(output);
-        consume(read);
-        return read;
-    }
-
-    /**
-     * <p>Reads and consumes the content bytes of this {@link DataInfo} into the given byte array,
-     * starting from index {@code offset} for {@code length} bytes.</p>
-     *
-     * @param bytes the byte array to copy the bytes into
-     * @param offset the offset of the byte array to start copying
-     * @param length the number of bytes to copy
-     * @return the number of bytes copied
-     */
-    public int consumeInto(byte[] bytes, int offset, int length)
-    {
-        int read = readInto(bytes, offset, length);
-        consume(read);
-        return read;
-    }
-
-    /**
-     * <p>Consumes the given number of bytes from this {@link DataInfo}.</p>
-     *
-     * @param delta the number of bytes consumed
-     */
-    public void consume(int delta)
-    {
-        if (delta < 0)
-            throw new IllegalArgumentException();
-        int read = length() - available();
-        int newConsumed = consumed() + delta;
-//        if (newConsumed > read)
-//            throw new IllegalStateException("Consuming without reading: consumed " + newConsumed + " but only read " + read);
-        consumed.addAndGet(delta);
-    }
-
-    /**
-     * @return the number of bytes consumed
-     */
-    public int consumed()
-    {
-        return consumed.get();
-    }
-
-    /**
-     *
-     * @param charset the charset used to convert the bytes
-     * @param consume whether to consume the content
-     * @return a String with the content of this {@link DataInfo}
-     */
-    public String asString(String charset, boolean consume)
-    {
-        return asString(Charset.forName(charset), consume);
-    }
-
-    /**
-     *
-     * @param charset the charset used to convert the bytes
-     * @param consume whether to consume the content
-     * @return a String with the content of this {@link DataInfo}
-     */
-    public String asString(Charset charset, boolean consume)
-    {
-        ByteBuffer buffer = asByteBuffer(consume);
-        return charset.decode(buffer).toString();
-    }
-
-    /**
-     * @return a byte array with the content of this {@link DataInfo}
-     * @param consume whether to consume the content
-     */
-    public byte[] asBytes(boolean consume)
-    {
-        ByteBuffer buffer = asByteBuffer(consume);
-        byte[] result = new byte[buffer.remaining()];
-        buffer.get(result);
-        return result;
-    }
-
-    /**
-     * @return a {@link ByteBuffer} with the content of this {@link DataInfo}
-     * @param consume whether to consume the content
-     */
-    public ByteBuffer asByteBuffer(boolean consume)
-    {
-        ByteBuffer buffer = allocate(available());
-        if (consume)
-            consumeInto(buffer);
-        else
-            readInto(buffer);
-        buffer.flip();
-        return buffer;
-    }
-
-    protected ByteBuffer allocate(int size)
-    {
-        return ByteBuffer.allocate(size);
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("DATA @%x available=%d consumed=%d close=%b", hashCode(), available(), consumed(), isClose());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/GoAwayInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/GoAwayInfo.java
deleted file mode 100644
index 01887e8..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/GoAwayInfo.java
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * A GoAwayInfo container. Currently adding nothing to it's base class, but serves to keep the api unchanged in
- * future versions when we need to pass more info to the methods having a {@link GoAwayInfo} parameter.
- */
-public class GoAwayInfo extends Info
-{
-    public GoAwayInfo(long timeout, TimeUnit unit)
-    {
-        super(timeout, unit);
-    }
-
-    public GoAwayInfo()
-    {
-        super();
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/GoAwayResultInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/GoAwayResultInfo.java
deleted file mode 100644
index 9bf8c68..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/GoAwayResultInfo.java
+++ /dev/null
@@ -1,57 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-/**
- * <p>A container for GOAWAY frames metadata: the last good stream id and
- * the session status.</p>
- */
-public class GoAwayResultInfo
-{
-    private final int lastStreamId;
-    private final SessionStatus sessionStatus;
-
-    /**
-     * <p>Creates a new {@link GoAwayResultInfo} with the given last good stream id and session status</p>
-     *
-     * @param lastStreamId  the last good stream id
-     * @param sessionStatus the session status
-     */
-    public GoAwayResultInfo(int lastStreamId, SessionStatus sessionStatus)
-    {
-        this.lastStreamId = lastStreamId;
-        this.sessionStatus = sessionStatus;
-    }
-
-    /**
-     * @return the last good stream id
-     */
-    public int getLastStreamId()
-    {
-        return lastStreamId;
-    }
-
-    /**
-     * @return the session status
-     */
-    public SessionStatus getSessionStatus()
-    {
-        return sessionStatus;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/HeadersInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/HeadersInfo.java
deleted file mode 100644
index 9ac95e7..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/HeadersInfo.java
+++ /dev/null
@@ -1,135 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.util.Fields;
-
-/**
- * <p>A container for HEADERS frame metadata and headers.</p>
- */
-public class HeadersInfo extends Info
-{
-    /**
-     * <p>Flag that indicates that this {@link HeadersInfo} is the last frame in the stream.</p>
-     *
-     * @see #isClose()
-     * @see #getFlags()
-     */
-    public static final byte FLAG_CLOSE = 1;
-    /**
-     * <p>Flag that indicates that the compression of the stream must be reset.</p>
-     *
-     * @see #isResetCompression()
-     * @see #getFlags()
-     */
-    public static final byte FLAG_RESET_COMPRESSION = 2;
-
-    private final boolean close;
-    private final boolean resetCompression;
-    private final Fields headers;
-
-    /**
-     * <p>Creates a new {@link HeadersInfo} instance with the given headers, the given close flag and no reset
-     * compression flag</p>
-     *
-     * @param headers the {@link Fields}
-     * @param close   the value of the close flag
-     */
-    public HeadersInfo(Fields headers, boolean close)
-    {
-        this(headers, close, false);
-    }
-
-    /**
-     * <p>Creates a new {@link HeadersInfo} instance with the given headers, the given close flag and the given reset
-     * compression flag</p>
-     *
-     * @param headers          the {@link Fields}
-     * @param close            the value of the close flag
-     * @param resetCompression the value of the reset compression flag
-     */
-    public HeadersInfo(Fields headers, boolean close, boolean resetCompression)
-    {
-        this.headers = headers;
-        this.close = close;
-        this.resetCompression = resetCompression;
-    }
-
-    /**
-     * <p>Creates a new {@link HeadersInfo} instance with the given headers, the given close flag and the given reset
-     * compression flag</p>
-     *
-     * @param timeout          the operation's timeout
-     * @param unit             the timeout's unit
-     * @param headers          the {@link Fields}
-     * @param close            the value of the close flag
-     * @param resetCompression the value of the reset compression flag
-     */
-    public HeadersInfo(long timeout, TimeUnit unit, boolean close, boolean resetCompression, Fields headers)
-    {
-        super(timeout, unit);
-        this.close = close;
-        this.resetCompression = resetCompression;
-        this.headers = headers;
-    }
-
-    /**
-     * @return the value of the close flag
-     */
-    public boolean isClose()
-    {
-        return close;
-    }
-
-    /**
-     * @return the value of the reset compression flag
-     */
-    public boolean isResetCompression()
-    {
-        return resetCompression;
-    }
-
-    /**
-     * @return the {@link Fields}
-     */
-    public Fields getHeaders()
-    {
-        return headers;
-    }
-
-    /**
-     * @return the close and reset compression flags as integer
-     * @see #FLAG_CLOSE
-     * @see #FLAG_RESET_COMPRESSION
-     */
-    public byte getFlags()
-    {
-        byte flags = isClose() ? FLAG_CLOSE : 0;
-        flags += isResetCompression() ? FLAG_RESET_COMPRESSION : 0;
-        return flags;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("HEADER close=%b %s", close, headers);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Info.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Info.java
deleted file mode 100644
index 93af001..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Info.java
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * A base class for all *Info classes providing timeout and unit and api to access them
- */
-public class Info
-{
-    private final long timeout;
-    private final TimeUnit unit;
-
-    public Info(long timeout, TimeUnit unit)
-    {
-        this.timeout = timeout;
-        this.unit = unit;
-    }
-
-    public Info()
-    {
-        timeout = 0;
-        unit = TimeUnit.SECONDS;
-    }
-
-    public long getTimeout()
-    {
-        return timeout;
-    }
-
-    public TimeUnit getUnit()
-    {
-        return unit;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PingInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PingInfo.java
deleted file mode 100644
index 1ef84ee..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PingInfo.java
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * A PingInfo container. Currently adding nothing to it's base class, but serves to keep the api unchanged in
- * future versions when we need to pass more info to the methods having a {@link PingInfo} parameter.
- */
-public class PingInfo extends Info
-{
-    public PingInfo(long timeout, TimeUnit unit)
-    {
-        super(timeout, unit);
-    }
-
-    public PingInfo()
-    {
-        this(0, TimeUnit.SECONDS);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PingResultInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PingResultInfo.java
deleted file mode 100644
index 401c1d4..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PingResultInfo.java
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-/**
- * <p>A container for PING frames data.</p>
- */
-public class PingResultInfo
-{
-    private final int pingId;
-
-    /**
-     * <p>Creates a {@link PingResultInfo} with the given ping id</p>
-     * @param pingId the ping id
-     */
-    public PingResultInfo(int pingId)
-    {
-        this.pingId = pingId;
-    }
-
-    /**
-     * @return the ping id
-     */
-    public int getPingId()
-    {
-        return pingId;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PushInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PushInfo.java
deleted file mode 100644
index 54c2d85..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/PushInfo.java
+++ /dev/null
@@ -1,101 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.util.Fields;
-
-/**
- * <p>A container for PUSH_SYN_STREAM frames metadata and data.</p>
- */
-public class PushInfo extends Info
-{
-    /**
-     * <p>Flag that indicates that this {@link PushInfo} is the last frame in the stream.</p>
-     *
-     * @see #isClose()
-     * @see #getFlags()
-     */
-    public static final byte FLAG_CLOSE = 1;
-
-    private final boolean close;
-    private final Fields headers;
-
-    /**
-     * <p>Creates a {@link PushInfo} instance with the given headers and the given close flag,
-     * not unidirectional, without associated stream, and with default priority.</p>
-     *
-     * @param headers the {@link Fields}
-     * @param close the value of the close flag
-     */
-    public PushInfo(Fields headers, boolean close)
-    {
-        this(0, TimeUnit.SECONDS, headers, close);
-        // either builder or setters for timeout
-    }
-
-    /**
-     * <p>
-     * Creates a {@link PushInfo} instance with the given headers, the given close flag and with the given priority.
-     * </p>
-     * @param timeout the timeout value
-     * @param unit the TimeUnit of the timeout
-     * @param headers
- *            the {@link Fields}
-     * @param close
-     */
-    public PushInfo(long timeout, TimeUnit unit, Fields headers, boolean close)
-    {
-        super(timeout, unit);
-        this.close = close;
-        this.headers = headers;
-    }
-
-    /**
-     * @return the value of the close flag
-     */
-    public boolean isClose()
-    {
-        return close;
-    }
-
-    /**
-     * @return the {@link Fields}
-     */
-    public Fields getHeaders()
-    {
-        return headers;
-    }
-
-    /**
-     * @return the close flag as integer
-     * @see #FLAG_CLOSE
-     */
-    public byte getFlags()
-    {
-        return isClose() ? FLAG_CLOSE : 0;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("SYN push close=%b headers=%s", close, headers);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ReplyInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ReplyInfo.java
deleted file mode 100644
index a6e3d82..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/ReplyInfo.java
+++ /dev/null
@@ -1,107 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.util.Fields;
-
-/**
- * <p>A container for SYN_REPLY frames metadata and headers.</p>
- */
-public class ReplyInfo extends Info
-{
-    /**
-     * <p>Flag that indicates that this {@link ReplyInfo} is the last frame in the stream.</p>
-     *
-     * @see #isClose()
-     * @see #getFlags()
-     */
-    public static final byte FLAG_CLOSE = 1;
-
-    private final Fields headers;
-    private final boolean close;
-
-    /**
-     * <p>Creates a new {@link ReplyInfo} instance with empty headers and the given close flag.</p>
-     *
-     * @param close the value of the close flag
-     */
-    public ReplyInfo(boolean close)
-    {
-        this(new Fields(), close);
-    }
-
-    /**
-     * <p>Creates a {@link ReplyInfo} instance with the given headers and the given close flag.</p>
-     *
-     * @param headers the {@link Fields}
-     * @param close   the value of the close flag
-     */
-    public ReplyInfo(Fields headers, boolean close)
-    {
-        this(0, TimeUnit.SECONDS, headers, close);
-    }
-
-    /**
-     * <p>Creates a {@link ReplyInfo} instance with the given headers and the given close flag.</p>
-     *
-     * @param timeout the timeout
-     * @param unit    the time unit for the timeout
-     * @param headers the {@link Fields}
-     * @param close   the value of the close flag
-     */
-    public ReplyInfo(long timeout, TimeUnit unit, Fields headers, boolean close)
-    {
-        super(timeout, unit);
-        this.headers = headers;
-        this.close = close;
-    }
-
-    /**
-     * @return the {@link Fields}
-     */
-    public Fields getHeaders()
-    {
-        return headers;
-    }
-
-    /**
-     * @return the value of the close flag
-     */
-    public boolean isClose()
-    {
-        return close;
-    }
-
-    /**
-     * @return the close and reset compression flags as integer
-     * @see #FLAG_CLOSE
-     */
-    public byte getFlags()
-    {
-        return isClose() ? FLAG_CLOSE : 0;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("REPLY close=%b %s", close, headers);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/RstInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/RstInfo.java
deleted file mode 100644
index 260fe5a..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/RstInfo.java
+++ /dev/null
@@ -1,78 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * <p>A container for RST_STREAM frames data: the stream id and the stream status.</p>
- */
-public class RstInfo extends Info
-{
-    private final int streamId;
-    private final StreamStatus streamStatus;
-
-    /**
-     * <p>Creates a new {@link RstInfo} with the given stream id and stream status</p>
-     *
-     * @param timeout      the operation's timeout
-     * @param unit         the timeout's unit
-     * @param streamId     the stream id
-     * @param streamStatus the stream status
-     */
-    public RstInfo(long timeout, TimeUnit unit, int streamId, StreamStatus streamStatus)
-    {
-        super(timeout, unit);
-        this.streamId = streamId;
-        this.streamStatus = streamStatus;
-    }
-
-    /**
-     * <p>Creates a new {@link RstInfo} with the given stream id and stream status</p>
-     *
-     * @param streamId
-     * @param streamStatus
-     */
-    public RstInfo(int streamId, StreamStatus streamStatus)
-    {
-        this(0, TimeUnit.SECONDS, streamId, streamStatus);
-    }
-
-    /**
-     * @return the stream id
-     */
-    public int getStreamId()
-    {
-        return streamId;
-    }
-
-    /**
-     * @return the stream status
-     */
-    public StreamStatus getStreamStatus()
-    {
-        return streamStatus;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("RST stream=%d %s", streamId, streamStatus);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDY.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDY.java
deleted file mode 100644
index ddf7cb6..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDY.java
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-/**
- * <p>Helper class that holds useful SPDY constants.</p>
- */
-public class SPDY
-{
-    /**
-     * <p>Constant that indicates the version 2 of the SPDY protocol</p>
-     */
-    public static final short V2 = 2;
-
-    /**
-     * <p>Constant that indicates the version 3 of the SPDY protocol</p>
-     */
-    public static final short V3 = 3;
-
-    private SPDY()
-    {
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDYException.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDYException.java
deleted file mode 100644
index 5004e68..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SPDYException.java
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-/**
- * <p>An unrecoverable exception that signals to the application that
- * something wrong happened.</p>
- */
-public class SPDYException extends RuntimeException
-{
-    public SPDYException()
-    {
-    }
-
-    public SPDYException(String message)
-    {
-        super(message);
-    }
-
-    public SPDYException(String message, Throwable cause)
-    {
-        super(message, cause);
-    }
-
-    public SPDYException(Throwable cause)
-    {
-        super(cause);
-    }
-
-    public SPDYException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace)
-    {
-        super(message, cause, enableSuppression, writableStackTrace);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Session.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Session.java
deleted file mode 100644
index ac4e543..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Session.java
+++ /dev/null
@@ -1,261 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.net.InetSocketAddress;
-import java.util.EventListener;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Promise;
-
-/**
- * <p>A {@link Session} represents the client-side endpoint of a SPDY connection to a single origin server.</p>
- * <p>Once a {@link Session} has been obtained, it can be used to open SPDY streams:</p>
- * <pre>
- * Session session = ...;
- * SynInfo synInfo = new SynInfo(true);
- * session.push(synInfo, new Stream.FrameListener.Adapter()
- * {
- *     public void onReply(Stream stream, ReplyInfo replyInfo)
- *     {
- *         // Stream reply received
- *     }
- * });
- * </pre>
- * <p>A {@link Session} is the active part of the endpoint, and by calling its API applications can generate
- * events on the connection; conversely {@link SessionFrameListener} is the passive part of the endpoint, and
- * has callbacks that are invoked when events happen on the connection.</p>
- *
- * @see SessionFrameListener
- */
-public interface Session
-{
-    /**
-     * @return the SPDY protocol version used by this session
-     */
-    public short getVersion();
-
-    /**
-     * <p>Registers the given {@code listener} to be notified of session events.</p>
-     *
-     * @param listener the listener to register
-     * @see #removeListener(Listener)
-     */
-    public void addListener(Listener listener);
-
-    /**
-     * <p>Deregisters the give {@code listener} from being notified of session events.</p>
-     *
-     * @param listener the listener to deregister
-     * @see #addListener(Listener)
-     */
-    public void removeListener(Listener listener);
-
-    /**
-     * <p>Sends a SYN_FRAME to create a new {@link Stream SPDY stream}.</p>
-     * <p>Callers may use the returned Stream for example, to send data frames.</p>
-     *
-     * @param synInfo  the metadata to send on stream creation
-     * @param listener the listener to invoke when events happen on the stream just created
-     * @return the stream that will be created
-     * @see #syn(SynInfo, StreamFrameListener, Promise)
-     */
-    public Stream syn(SynInfo synInfo, StreamFrameListener listener) throws ExecutionException, InterruptedException, TimeoutException;
-
-    /**
-     * <p>Sends asynchronously a SYN_FRAME to create a new {@link Stream SPDY stream}.</p>
-     * <p>Callers may pass a non-null completion callback to be notified of when the
-     * stream has been created and use the stream, for example, to send data frames.</p>
-     *
-     *
-     * @param synInfo  the metadata to send on stream creation
-     * @param listener the listener to invoke when events happen on the stream just created
-     * @param promise  the completion callback that gets notified of stream creation
-     * @see #syn(SynInfo, StreamFrameListener)
-     */
-    public void syn(SynInfo synInfo, StreamFrameListener listener, Promise<Stream> promise);
-
-    /**
-     * <p>Sends synchronously a RST_STREAM to abort a stream.</p>
-     *
-     * @param rstInfo the metadata to reset the stream
-     * @see #rst(RstInfo, Callback)
-     */
-    public void rst(RstInfo rstInfo) throws InterruptedException, ExecutionException, TimeoutException;
-
-    /**
-     * <p>Sends asynchronously a RST_STREAM to abort a stream.</p>
-     * <p>Callers may pass a non-null completion callback to be notified of when the
-     * reset has been actually sent.</p>
-     *
-     * @param rstInfo the metadata to reset the stream
-     * @param callback the completion callback that gets notified of reset's send
-     * @see #rst(RstInfo)
-     */
-    public void rst(RstInfo rstInfo, Callback callback);
-
-    /**
-     * <p>Sends synchronously a SETTINGS to configure the SPDY connection.</p>
-     *
-     * @param settingsInfo the metadata to send
-     * @see #settings(SettingsInfo, Callback)
-     */
-    public void settings(SettingsInfo settingsInfo) throws ExecutionException, InterruptedException, TimeoutException;
-
-    /**
-     * <p>Sends asynchronously a SETTINGS to configure the SPDY connection.</p>
-     * <p>Callers may pass a non-null completion callback to be notified of when the
-     * settings has been actually sent.</p>
-     *
-     *
-     * @param settingsInfo the metadata to send
-     * @param callback      the completion callback that gets notified of settings' send
-     * @see #settings(SettingsInfo)
-     */
-    public void settings(SettingsInfo settingsInfo, Callback callback);
-
-    /**
-     * <p>Sends synchronously a PING, normally to measure round-trip time.</p>
-     *
-     * @see #ping(PingInfo, Promise)
-     * @param pingInfo
-     */
-    public PingResultInfo ping(PingInfo pingInfo) throws ExecutionException, InterruptedException, TimeoutException;
-
-    /**
-     * <p>Sends asynchronously a PING, normally to measure round-trip time.</p>
-     * <p>Callers may pass a non-null completion callback to be notified of when the
-     * ping has been actually sent.</p>
-     *
-     * @param pingInfo
-     * @param promise the completion callback that gets notified of ping's send
-     * @see #ping(PingInfo)
-     */
-    public void ping(PingInfo pingInfo, Promise<PingResultInfo> promise);
-
-    /**
-     * <p>Closes gracefully this session, sending a GO_AWAY frame and then closing the TCP connection.</p>
-     *
-     * @see #goAway(GoAwayInfo, Callback)
-     * @param goAwayInfo
-     */
-    public void goAway(GoAwayInfo goAwayInfo) throws ExecutionException, InterruptedException, TimeoutException;
-
-    /**
-     * <p>Closes gracefully this session, sending a GO_AWAY frame and then closing the TCP connection.</p>
-     * <p>Callers may pass a non-null completion callback to be notified of when the
-     * go away has been actually sent.</p>
-     *
-     * @param goAwayInfo
-     * @param callback the completion callback that gets notified of go away's send
-     * @see #goAway(GoAwayInfo)
-     */
-    public void goAway(GoAwayInfo goAwayInfo, Callback callback);
-
-    /**
-     * @return a snapshot of the streams currently active in this session
-     * @see #getStream(int)
-     */
-    public Set<Stream> getStreams();
-
-    /**
-     * @param streamId the id of the stream to retrieve
-     * @return the stream with the given stream id
-     * @see #getStreams()
-     */
-    public Stream getStream(int streamId);
-
-    /**
-     * @param key the attribute key
-     * @return an arbitrary object associated with the given key to this session
-     * @see #setAttribute(String, Object)
-     */
-    public Object getAttribute(String key);
-
-    /**
-     * @param key   the attribute key
-     * @param value an arbitrary object to associate with the given key to this session
-     * @see #getAttribute(String)
-     * @see #removeAttribute(String)
-     */
-    public void setAttribute(String key, Object value);
-
-    /**
-     * @param key the attribute key
-     * @return the arbitrary object associated with the given key to this session
-     * @see #setAttribute(String, Object)
-     */
-    public Object removeAttribute(String key);
-
-    /**
-     * @return the local address of the underlying endpoint
-     */
-    public InetSocketAddress getLocalAddress();
-
-    /**
-     * @return the remote address of the underlying endpoint
-     */
-    public InetSocketAddress getRemoteAddress();
-
-    /**
-     * <p>Super interface for listeners with callbacks that are invoked on specific session events.</p>
-     */
-    public interface Listener extends EventListener
-    {
-    }
-
-    /**
-     * <p>Specialized listener that is invoked upon creation and removal of streams.</p>
-     */
-    public interface StreamListener extends Listener
-    {
-        /**
-         * <p>Callback invoked when a new SPDY stream is created.</p>
-         *
-         * @param stream the stream just created
-         */
-        public void onStreamCreated(Stream stream);
-
-        /**
-         * <p>Callback invoked when a SPDY stream is closed.</p>
-         *
-         * @param stream the stream just closed.
-         */
-        public void onStreamClosed(Stream stream);
-
-        /**
-         * <p>Empty implementation of {@link StreamListener}.</p>
-         */
-        public static class Adapter implements StreamListener
-        {
-            @Override
-            public void onStreamCreated(Stream stream)
-            {
-            }
-
-            @Override
-            public void onStreamClosed(Stream stream)
-            {
-            }
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionFrameListener.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionFrameListener.java
deleted file mode 100644
index b6f6d44..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionFrameListener.java
+++ /dev/null
@@ -1,163 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.EventListener;
-
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * <p>A {@link SessionFrameListener} is the passive counterpart of a {@link Session} and receives events happening
- * on a SPDY session.</p>
- *
- * @see Session
- */
-public interface SessionFrameListener extends EventListener
-{
-    /**
-     * <p>Callback invoked when a request to create a stream has been received.</p>
-     * <p>Application code should implement this method and reply to the stream creation, eventually
-     * sending data:</p>
-     * <pre>
-     * public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-     * {
-     *     // Do something with the metadata contained in synInfo
-     *
-     *     if (stream.isHalfClosed()) // The other peer will not send data
-     *     {
-     *         stream.reply(new ReplyInfo(false));
-     *         stream.data(new StringDataInfo("foo", true));
-     *         return null; // Not interested in further stream events
-     *     }
-     *
-     *     ...
-     * }
-     * </pre>
-     * <p>Alternatively, if the stream creation requires reading data sent from the other peer:</p>
-     * <pre>
-     * public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-     * {
-     *     // Do something with the metadata contained in synInfo
-     *
-     *     if (!stream.isHalfClosed()) // The other peer will send data
-     *     {
-     *         stream.reply(new ReplyInfo(true));
-     *         return new Stream.FrameListener.Adapter() // Interested in stream events
-     *         {
-     *             public void onData(Stream stream, DataInfo dataInfo)
-     *             {
-     *                 // Do something with the incoming data in dataInfo
-     *             }
-     *         };
-     *     }
-     *
-     *     ...
-     * }
-     * </pre>
-     *
-     * @param stream  the stream just created
-     * @param synInfo the metadata sent on stream creation
-     * @return a listener for stream events, or null if there is no interest in being notified of stream events
-     */
-    public StreamFrameListener onSyn(Stream stream, SynInfo synInfo);
-
-    /**
-     * <p>Callback invoked when a stream error happens.</p>
-     *
-     * @param session the session
-     * @param rstInfo the metadata of the stream error
-     */
-    public void onRst(Session session, RstInfo rstInfo);
-
-    /**
-     * <p>Callback invoked when a request to configure the SPDY connection has been received.</p>
-     *
-     * @param session the session
-     * @param settingsInfo the metadata sent to configure
-     */
-    public void onSettings(Session session, SettingsInfo settingsInfo);
-
-    /**
-     * <p>Callback invoked when a ping request has completed its round-trip.</p>
-     *
-     * @param session the session
-     * @param pingResultInfo the metadata received
-     */
-    public void onPing(Session session, PingResultInfo pingResultInfo);
-
-    /**
-     * <p>Callback invoked when the other peer signals that it is closing the connection.</p>
-     *
-     * @param session the session
-     * @param goAwayResultInfo the metadata sent
-     */
-    public void onGoAway(Session session, GoAwayResultInfo goAwayResultInfo);
-
-    /**
-     * <p>Callback invoked when an exception is thrown during the processing of an event on a
-     * SPDY session.</p>
-     * <p>Examples of such conditions are invalid frames received, corrupted headers compression state, etc.</p>
-     *
-     * @param session the session
-     * @param x the exception that caused the event processing failure
-     */
-    public void onFailure(Session session, Throwable x);
-
-
-    /**
-     * <p>Empty implementation of {@link SessionFrameListener}</p>
-     */
-    public static class Adapter implements SessionFrameListener
-    {
-        private static final Logger logger = Log.getLogger(Adapter.class);
-
-        @Override
-        public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-        {
-            return null;
-        }
-
-        @Override
-        public void onRst(Session session, RstInfo rstInfo)
-        {
-        }
-
-        @Override
-        public void onSettings(Session session, SettingsInfo settingsInfo)
-        {
-        }
-
-        @Override
-        public void onPing(Session session, PingResultInfo pingResultInfo)
-        {
-        }
-
-        @Override
-        public void onGoAway(Session session, GoAwayResultInfo goAwayResultInfo)
-        {
-        }
-
-        @Override
-        public void onFailure(Session session, Throwable x)
-        {
-            logger.info("", x);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionStatus.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionStatus.java
deleted file mode 100644
index 1020be8..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionStatus.java
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * <p>An enumeration of session statuses.</p>
- */
-public enum SessionStatus
-{
-    /**
-     * <p>The session status indicating no errors</p>
-     */
-    OK(0),
-    /**
-     * <p>The session status indicating a protocol error</p>
-     */
-    PROTOCOL_ERROR(1);
-
-    /**
-     * @param code the session status code
-     * @return a {@link SessionStatus} from the given code,
-     * or null if no status exists
-     */
-    public static SessionStatus from(int code)
-    {
-        return Codes.codes.get(code);
-    }
-
-    private final int code;
-
-    private SessionStatus(int code)
-    {
-        this.code = code;
-        Codes.codes.put(code, this);
-    }
-
-    /**
-     * @return the code of this {@link SessionStatus}
-     */
-    public int getCode()
-    {
-        return code;
-    }
-
-    private static class Codes
-    {
-        private static final Map<Integer, SessionStatus> codes = new HashMap<>();
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Settings.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Settings.java
deleted file mode 100644
index c1a832a..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Settings.java
+++ /dev/null
@@ -1,228 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-public class Settings implements Iterable<Settings.Setting>
-{
-    private final Map<ID, Settings.Setting> settings;
-
-    public Settings()
-    {
-        settings = new HashMap<>();
-    }
-
-    public Settings(Settings original, boolean immutable)
-    {
-        Map<ID, Settings.Setting> copy = new HashMap<>(original.size());
-        copy.putAll(original.settings);
-        settings = immutable ? Collections.unmodifiableMap(copy) : copy;
-    }
-
-    public Setting get(ID id)
-    {
-        return settings.get(id);
-    }
-
-    public void put(Setting setting)
-    {
-        settings.put(setting.id(), setting);
-    }
-
-    public Setting remove(ID id)
-    {
-        return settings.remove(id);
-    }
-
-    public int size()
-    {
-        return settings.size();
-    }
-
-    public void clear()
-    {
-        settings.clear();
-    }
-
-    @Override
-    public boolean equals(Object obj)
-    {
-        if (this == obj)
-            return true;
-        if (obj == null || getClass() != obj.getClass())
-            return false;
-        Settings that = (Settings)obj;
-        return settings.equals(that.settings);
-    }
-
-    @Override
-    public int hashCode()
-    {
-        return settings.hashCode();
-    }
-
-    @Override
-    public Iterator<Setting> iterator()
-    {
-        return settings.values().iterator();
-    }
-
-    @Override
-    public String toString()
-    {
-        return settings.toString();
-    }
-
-    public static final class ID
-    {
-        public static final ID UPLOAD_BANDWIDTH = new ID(1);
-        public static final ID DOWNLOAD_BANDWIDTH = new ID(2);
-        public static final ID ROUND_TRIP_TIME = new ID(3);
-        public static final ID MAX_CONCURRENT_STREAMS = new ID(4);
-        public static final ID CURRENT_CONGESTION_WINDOW = new ID(5);
-        public static final ID DOWNLOAD_RETRANSMISSION_RATE = new ID(6);
-        public static final ID INITIAL_WINDOW_SIZE = new ID(7);
-
-        public synchronized static ID from(int code)
-        {
-            ID id = Codes.codes.get(code);
-            if (id == null)
-                id = new ID(code);
-            return id;
-        }
-
-        private final int code;
-
-        private ID(int code)
-        {
-            this.code = code;
-            Codes.codes.put(code, this);
-        }
-
-        public int code()
-        {
-            return code;
-        }
-
-        @Override
-        public String toString()
-        {
-            return String.valueOf(code);
-        }
-
-        private static class Codes
-        {
-            private static final Map<Integer, ID> codes = new HashMap<>();
-        }
-    }
-
-    public static enum Flag
-    {
-        NONE((byte)0),
-        PERSIST((byte)1),
-        PERSISTED((byte)2);
-
-        public static Flag from(byte code)
-        {
-            return Codes.codes.get(code);
-        }
-
-        private final byte code;
-
-        private Flag(byte code)
-        {
-            this.code = code;
-            Codes.codes.put(code, this);
-        }
-
-        public byte code()
-        {
-            return code;
-        }
-
-        private static class Codes
-        {
-            private static final Map<Byte, Flag> codes = new HashMap<>();
-        }
-    }
-
-    public static class Setting
-    {
-        private final ID id;
-        private final Flag flag;
-        private final int value;
-
-        public Setting(ID id, int value)
-        {
-            this(id, Flag.NONE, value);
-        }
-
-        public Setting(ID id, Flag flag, int value)
-        {
-            this.id = id;
-            this.flag = flag;
-            this.value = value;
-        }
-
-        public ID id()
-        {
-            return id;
-        }
-
-        public Flag flag()
-        {
-            return flag;
-        }
-
-        public int value()
-        {
-            return value;
-        }
-
-        @Override
-        public boolean equals(Object obj)
-        {
-            if (this == obj)
-                return true;
-            if (obj == null || getClass() != obj.getClass())
-                return false;
-            Setting that = (Setting)obj;
-            return value == that.value && flag == that.flag && id == that.id;
-        }
-
-        @Override
-        public int hashCode()
-        {
-            int result = id.hashCode();
-            result = 31 * result + flag.hashCode();
-            result = 31 * result + value;
-            return result;
-        }
-
-        @Override
-        public String toString()
-        {
-            return String.format("[id=%s,flags=%s:value=%d]", id(), flag(), value());
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SettingsInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SettingsInfo.java
deleted file mode 100644
index 010c100..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SettingsInfo.java
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.concurrent.TimeUnit;
-
-public class SettingsInfo extends Info
-{
-    public static final byte CLEAR_PERSISTED = 1;
-
-    private final Settings settings;
-    private final boolean clearPersisted;
-
-    public SettingsInfo(Settings settings)
-    {
-        this(0, TimeUnit.SECONDS, settings, false);
-    }
-
-    public SettingsInfo(long timeout, TimeUnit unit, Settings settings, boolean clearPersisted)
-    {
-        super(timeout, unit);
-        this.settings = settings;
-        this.clearPersisted = clearPersisted;
-    }
-
-    public SettingsInfo(Settings settings, boolean clearPersisted)
-    {
-        this(0, TimeUnit.SECONDS, settings, clearPersisted);
-    }
-
-    public boolean isClearPersisted()
-    {
-        return clearPersisted;
-    }
-
-    public byte getFlags()
-    {
-        return isClearPersisted() ? CLEAR_PERSISTED : 0;
-    }
-
-    public Settings getSettings()
-    {
-        return settings;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Stream.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Stream.java
deleted file mode 100644
index c9eeaa8..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Stream.java
+++ /dev/null
@@ -1,237 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.nio.channels.WritePendingException;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Promise;
-
-/**
- * <p>A {@link Stream} represents a bidirectional exchange of data on top of a {@link Session}.</p> <p>Differently from
- * socket streams, where the input and output streams are permanently associated with the socket (and hence with the
- * connection that the socket represents), there can be multiple SPDY streams for a SPDY session.</p> <p>SPDY streams
- * may terminate without this implying that the SPDY session is terminated.</p> <p>If SPDY is used to transport the HTTP
- * protocol, then a SPDY stream maps to a HTTP request/response cycle, and after the request/response cycle is
- * completed, the stream is closed, and other streams may be opened. Differently from HTTP, though, multiple SPDY
- * streams may be opened concurrently on the same SPDY session.</p> <p>Like {@link Session}, {@link Stream} is the
- * active part and by calling its API applications can generate events on the stream; conversely, {@link
- * StreamFrameListener} is the passive part, and its callbacks are invoked when events happen on the stream.</p> <p>A
- * {@link Stream} can send multiple data frames one after the other but implementations use a flow control mechanism
- * that only sends the data frames if the other end has signalled that it can accept the frame.</p> <p>Data frames
- * should be sent sequentially only when the previous frame has been completely sent. The reason for this requirement is
- * to avoid potentially confusing code such as:</p>
- * <pre>
- * // WRONG CODE, DO NOT USE IT
- * final Stream stream = ...;
- * stream.data(StringDataInfo("chunk1", false), 5, TimeUnit.SECONDS, new Handler&lt;Void&gt;() { ... });
- * stream.data(StringDataInfo("chunk2", true), 1, TimeUnit.SECONDS, new Handler&lt;Void&gt;() { ... });
- * </pre>
- * <p>where the second call to {@link #data(DataInfo, Callback)} has a timeout smaller than the previous call.</p>
- * <p>The behavior of such style of invocations is unspecified (it may even throw an exception - similar to {@link
- * WritePendingException}).</p> <p>The correct sending of data frames is the following:</p>
- * <pre>
- * final Stream stream = ...;
- * ...
- * // Blocking version
- * stream.data(new StringDataInfo("chunk1", false)).get(1, TimeUnit.SECONDS);
- * stream.data(new StringDataInfo("chunk2", true)).get(1, TimeUnit.SECONDS);
- *
- * // Asynchronous version
- * stream.data(new StringDataInfo("chunk1", false), 1, TimeUnit.SECONDS, new Handler.Adapter&lt;Void&gt;()
- * {
- *     public void completed(Void context)
- *     {
- *         stream.data(new StringDataInfo("chunk2", true));
- *     }
- * });
- * </pre>
- *
- * @see StreamFrameListener
- */
-public interface Stream
-{
-    /**
-     * @return the id of this stream
-     */
-    public int getId();
-
-    /**
-     * @return the priority of this stream
-     */
-    public byte getPriority();
-
-    /**
-     * @return the session this stream is associated to
-     */
-    public Session getSession();
-
-    /**
-     * <p>Initiate a unidirectional spdy pushstream associated to this stream asynchronously<p> <p>Callers may use the
-     * returned future to get the pushstream once it got created</p>
-     *
-     * @param pushInfo the metadata to send on stream creation
-     * @return a future containing the stream once it got established
-     * @see #push(PushInfo, Promise)
-     */
-    public Stream push(PushInfo pushInfo) throws InterruptedException, ExecutionException, TimeoutException;
-
-    /**
-     * <p>Initiate a unidirectional spdy pushstream associated to this stream asynchronously<p> <p>Callers may pass a
-     * non-null completion promise to be notified of when the pushstream has been established.</p>
-     *
-     * @param pushInfo the metadata to send on stream creation
-     * @param promise the completion promise that gets notified once the pushstream is established
-     * @see #push(PushInfo)
-     */
-    public void push(PushInfo pushInfo, Promise<Stream> promise);
-
-    /**
-     * <p>Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.</p> <p>Callers may use the returned
-     * future to wait for the reply to be actually sent.</p>
-     *
-     * @param replyInfo the metadata to send
-     * @see #reply(ReplyInfo, Callback)
-     * @see SessionFrameListener#onSyn(Stream, SynInfo)
-     */
-    public void reply(ReplyInfo replyInfo) throws InterruptedException, ExecutionException, TimeoutException;
-
-    /**
-     * <p>Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.</p> <p>Callers may pass a non-null
-     * completion callback to be notified of when the reply has been actually sent.</p>
-     *
-     * @param replyInfo the metadata to send
-     * @param callback  the completion callback that gets notified of reply sent
-     * @see #reply(ReplyInfo)
-     */
-    public void reply(ReplyInfo replyInfo, Callback callback);
-
-    /**
-     * <p>Sends asynchronously a DATA frame on this stream.</p> <p>DATA frames should always be sent after a SYN_REPLY
-     * frame.</p> <p>Callers may use the returned future to wait for the data to be actually sent.</p>
-     *
-     * @param dataInfo the metadata to send
-     * @see #data(DataInfo, Callback)
-     * @see #reply(ReplyInfo)
-     */
-    public void data(DataInfo dataInfo) throws InterruptedException, ExecutionException, TimeoutException;
-
-    /**
-     * <p>Sends asynchronously a DATA frame on this stream.</p> <p>DATA frames should always be sent after a SYN_REPLY
-     * frame.</p> <p>Callers may pass a non-null completion callback to be notified of when the data has been actually
-     * sent.</p>
-     *
-     * @param dataInfo the metadata to send
-     * @param callback the completion callback that gets notified of data sent
-     * @see #data(DataInfo)
-     */
-    public void data(DataInfo dataInfo, Callback callback);
-
-    /**
-     * <p>Sends asynchronously a HEADER frame on this stream.</p> <p>HEADERS frames should always be sent after a
-     * SYN_REPLY frame.</p> <p>Callers may use the returned future to wait for the headers to be actually sent.</p>
-     *
-     * @param headersInfo the metadata to send
-     * @see #headers(HeadersInfo, Callback)
-     * @see #reply(ReplyInfo)
-     */
-    public void headers(HeadersInfo headersInfo) throws InterruptedException, ExecutionException, TimeoutException;
-
-    /**
-     * <p>Sends asynchronously a HEADER frame on this stream.</p> <p>HEADERS frames should always be sent after a
-     * SYN_REPLY frame.</p> <p>Callers may pass a non-null completion callback to be notified of when the headers have
-     * been actually sent.</p>
-     *
-     * @param headersInfo the metadata to send
-     * @param callback    the completion callback that gets notified of headers sent
-     * @see #headers(HeadersInfo)
-     */
-    public void headers(HeadersInfo headersInfo, Callback callback);
-
-    /**
-     * @return whether this stream is unidirectional or not
-     */
-    public boolean isUnidirectional();
-
-    /**
-     * @return whether this stream has been reset
-     */
-    public boolean isReset();
-
-    /**
-     * @return whether this stream has been closed by both parties
-     * @see #isHalfClosed()
-     */
-    public boolean isClosed();
-
-    /**
-     * @return whether this stream has been closed by one party only
-     * @see #isClosed()
-     */
-    public boolean isHalfClosed();
-
-    /**
-     * @param key the attribute key
-     * @return an arbitrary object associated with the given key to this stream or null if no object can be found for
-     *         the given key.
-     * @see #setAttribute(String, Object)
-     */
-    public Object getAttribute(String key);
-
-    /**
-     * @param key   the attribute key
-     * @param value an arbitrary object to associate with the given key to this stream
-     * @see #getAttribute(String)
-     * @see #removeAttribute(String)
-     */
-    public void setAttribute(String key, Object value);
-
-    /**
-     * @param key the attribute key
-     * @return the arbitrary object associated with the given key to this stream
-     * @see #setAttribute(String, Object)
-     */
-    public Object removeAttribute(String key);
-
-    /**
-     * @return the associated parent stream or null if this is not an associated stream
-     */
-    public Stream getAssociatedStream();
-
-    /**
-     * @return associated child streams or an empty set if no associated streams exist
-     */
-    public Set<Stream> getPushedStreams();
-
-    /**
-     * Get the idle timeout set for this particular stream
-     * @return the idle timeout
-     */
-    public long getIdleTimeout();
-
-    /**
-     * Set an idle timeout for this stream
-     * @param timeout
-     */
-    public void setIdleTimeout(long timeout);
-
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamFrameListener.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamFrameListener.java
deleted file mode 100644
index 2001e45..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamFrameListener.java
+++ /dev/null
@@ -1,110 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.EventListener;
-
-/**
- * <p>A {@link StreamFrameListener} is the passive counterpart of a {@link Stream} and receives
- * events happening on a SPDY stream.</p>
- *
- * @see Stream
- */
-public interface StreamFrameListener extends EventListener
-{
-    /**
-     * <p>Callback invoked when a reply to a stream creation has been received.</p>
-     * <p>Application code may implement this method to send more data to the other end:</p>
-     * <pre>
-     * public void onReply(Stream stream, ReplyInfo replyInfo)
-     * {
-     *     stream.data(new StringDataInfo("content"), true);
-     * }
-     * </pre>
-     * @param stream the stream
-     * @param replyInfo the reply metadata
-     */
-    public void onReply(Stream stream, ReplyInfo replyInfo);
-
-    /**
-     * <p>Callback invoked when headers are received on a stream.</p>
-     *
-     * @param stream the stream
-     * @param headersInfo the headers metadata
-     */
-    public void onHeaders(Stream stream, HeadersInfo headersInfo);
-
-    /**
-     * <p>Callback invoked when a push syn has been received on a stream.</p>
-     *
-     * @param stream the push stream just created
-     * @param pushInfo the push metadata
-     * @return a listener for stream events or null if there is no interest in being notified of stream events
-     */
-    public StreamFrameListener onPush(Stream stream, PushInfo pushInfo);
-
-    /**
-     * <p>Callback invoked when data bytes are received on a stream.</p>
-     * <p>Implementers should be read or consume the content of the
-     * {@link DataInfo} before this method returns.</p>
-     *
-     * @param stream the stream
-     * @param dataInfo the data metadata
-     */
-    public void onData(Stream stream, DataInfo dataInfo);
-
-    /**
-     * <p>Callback invoked on errors.</p>
-     * @param stream the stream
-     * @param x the failure
-     */
-    public void onFailure(Stream stream, Throwable x);
-
-    /**
-     * <p>Empty implementation of {@link StreamFrameListener}</p>
-     */
-    public static class Adapter implements StreamFrameListener
-    {
-        @Override
-        public void onReply(Stream stream, ReplyInfo replyInfo)
-        {
-        }
-
-        @Override
-        public void onHeaders(Stream stream, HeadersInfo headersInfo)
-        {
-        }
-
-        @Override
-        public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-        {
-            return null;
-        }
-
-        @Override
-        public void onData(Stream stream, DataInfo dataInfo)
-        {
-        }
-
-        @Override
-        public void onFailure(Stream stream, Throwable x)
-        {
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamStatus.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamStatus.java
deleted file mode 100644
index 8623d1f..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StreamStatus.java
+++ /dev/null
@@ -1,128 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * <p>An enumeration of stream statuses.</p>
- */
-public enum StreamStatus
-{
-    /**
-     * <p>The stream status indicating a protocol error</p>
-     */
-    PROTOCOL_ERROR(1, 1),
-    /**
-     * <p>The stream status indicating that the stream is not valid</p>
-     */
-    INVALID_STREAM(2, 2),
-    /**
-     * <p>The stream status indicating that the stream has been refused</p>
-     */
-    REFUSED_STREAM(3, 3),
-    /**
-     * <p>The stream status indicating that the implementation does not support the SPDY version of the stream</p>
-     */
-    UNSUPPORTED_VERSION(4, 4),
-    /**
-     * <p>The stream status indicating that the stream is no longer needed</p>
-     */
-    CANCEL_STREAM(5, 5),
-    /**
-     * <p>The stream status indicating an implementation error</p>
-     */
-    INTERNAL_ERROR(6, 6),
-    /**
-     * <p>The stream status indicating a flow control error</p>
-     */
-    FLOW_CONTROL_ERROR(7, 7),
-    /**
-     * <p>The stream status indicating a stream opened more than once</p>
-     */
-    STREAM_IN_USE(-1, 8),
-    /**
-     * <p>The stream status indicating data on a stream already closed</p>
-     */
-    STREAM_ALREADY_CLOSED(-1, 9),
-    /**
-     * <p>The stream status indicating credentials not valid</p>
-     */
-    INVALID_CREDENTIALS(-1, 10),
-    /**
-     * <p>The stream status indicating that the implementation could not support a frame too large</p>
-     */
-    FRAME_TOO_LARGE(-1, 11);
-
-    /**
-     * @param version the SPDY protocol version
-     * @param code the stream status code
-     * @return a {@link StreamStatus} from the given version and code,
-     * or null if no such status exists
-     */
-    public static StreamStatus from(short version, int code)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-                return Codes.v2Codes.get(code);
-            case SPDY.V3:
-                return Codes.v3Codes.get(code);
-            default:
-                throw new IllegalStateException();
-        }
-    }
-
-    private final int v2Code;
-    private final int v3Code;
-
-    private StreamStatus(int v2Code, int v3Code)
-    {
-        this.v2Code = v2Code;
-        if (v2Code >= 0)
-            Codes.v2Codes.put(v2Code, this);
-        this.v3Code = v3Code;
-        if (v3Code >= 0)
-            Codes.v3Codes.put(v3Code, this);
-    }
-
-    /**
-     * @param version the SPDY protocol version
-     * @return the stream status code
-     */
-    public int getCode(short version)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-                return v2Code;
-            case SPDY.V3:
-                return v3Code;
-            default:
-                throw new IllegalStateException();
-        }
-    }
-
-    private static class Codes
-    {
-        private static final Map<Integer, StreamStatus> v2Codes = new HashMap<>();
-        private static final Map<Integer, StreamStatus> v3Codes = new HashMap<>();
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StringDataInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StringDataInfo.java
deleted file mode 100644
index 582bad4..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/StringDataInfo.java
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.nio.charset.StandardCharsets;
-import java.util.concurrent.TimeUnit;
-
-/**
- * <p>Specialized {@link DataInfo} for {@link String} content.</p>
- */
-public class StringDataInfo extends BytesDataInfo
-{
-    public StringDataInfo(String string, boolean close)
-    {
-        super(string.getBytes(StandardCharsets.UTF_8), close);
-    }
-
-    public StringDataInfo(long timeout, TimeUnit unit, String string, boolean close)
-    {
-        super(timeout, unit, string.getBytes(StandardCharsets.UTF_8), close);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SynInfo.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SynInfo.java
deleted file mode 100644
index 3e66e53..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SynInfo.java
+++ /dev/null
@@ -1,128 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.util.Fields;
-
-/**
- * <p>A container for SYN_STREAM frames metadata and data.</p>
- */
-public class SynInfo extends Info
-{
-    /**
-     * <p>Flag that indicates that this {@link SynInfo} is the last frame in the stream.</p>
-     *
-     * @see #isClose()
-     * @see #getFlags()
-     */
-    public static final byte FLAG_CLOSE = 1;
-
-    private final boolean close;
-    private final byte priority;
-    private final Fields headers;
-
-    /**
-     * <p>Creates a {@link SynInfo} instance with the given headers and the given close flag,
-     * not unidirectional, without associated stream, and with default priority.</p>
-     *
-     * @param headers the {@link Fields}
-     * @param close the value of the close flag
-     */
-    public SynInfo(Fields headers, boolean close)
-    {
-        this(0, TimeUnit.SECONDS, headers, close, (byte)0);
-        // either builder or setters for timeout
-    }
-
-    /**
-     * <p>
-     * Creates a {@link SynInfo} instance with the given headers, the given close flag and with the given priority.
-     * </p>
-     * @param headers
-     *            the {@link Fields}
-     * @param close
-     *            the value of the close flag
-     * @param priority
-     */
-    public SynInfo(Fields headers, boolean close, byte priority)
-    {
-        this(0, TimeUnit.SECONDS, headers, close, priority);
-    }
-
-    /**
-     * <p>
-     * Creates a {@link SynInfo} instance with the given headers, the given close flag and with the given priority.
-     * </p>
-     * @param timeout the timeout value
-     * @param unit the TimeUnit of the timeout
-     * @param headers
-     *            the {@link Fields}
-     * @param close
-     *            the value of the close flag
-     * @param priority
-     */
-    public SynInfo(long timeout, TimeUnit unit, Fields headers, boolean close, byte priority)
-    {
-        super(timeout, unit);
-        this.close = close;
-        this.priority = priority;
-        this.headers = headers;
-    }
-
-    /**
-     * @return the value of the close flag
-     */
-    public boolean isClose()
-    {
-        return close;
-    }
-
-    /**
-     * @return the priority
-     */
-    public byte getPriority()
-    {
-        return priority;
-    }
-
-    /**
-     * @return the {@link Fields}
-     */
-    public Fields getHeaders()
-    {
-        return headers;
-    }
-
-    /**
-     * @return the close flag as integer
-     * @see #FLAG_CLOSE
-     */
-    public byte getFlags()
-    {
-        return isClose() ? FLAG_CLOSE : 0;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("SYN close=%b headers=%s", close, headers);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/server/ServerSessionFrameListener.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/server/ServerSessionFrameListener.java
deleted file mode 100644
index 6ebb105..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/server/ServerSessionFrameListener.java
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api.server;
-
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-
-/**
- * <p>Specific, server-side, {@link SessionFrameListener}.</p>
- * <p>In addition to {@link SessionFrameListener}, this listener adds method
- * {@link #onConnect(Session)} that is called when a client first connects to the
- * server and may be used by a server-side application to send a SETTINGS frame
- * to configure the connection before the client can open any stream.</p>
- */
-public interface ServerSessionFrameListener extends SessionFrameListener
-{
-    /**
-     * <p>Callback invoked when a client opens a connection.</p>
-     *
-     * @param session the session
-     */
-    public void onConnect(Session session);
-
-    /**
-     * <p>Empty implementation of {@link ServerSessionFrameListener}</p>
-     */
-    public static class Adapter extends SessionFrameListener.Adapter implements ServerSessionFrameListener
-    {
-        @Override
-        public void onConnect(Session session)
-        {
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrame.java
deleted file mode 100644
index 26e0b36..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrame.java
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-public abstract class ControlFrame
-{
-    public static final int HEADER_LENGTH = 8;
-
-    private final short version;
-    private final ControlFrameType type;
-    private final byte flags;
-
-    public ControlFrame(short version, ControlFrameType type, byte flags)
-    {
-        this.version = version;
-        this.type = type;
-        this.flags = flags;
-    }
-
-    public short getVersion()
-    {
-        return version;
-    }
-
-    public ControlFrameType getType()
-    {
-        return type;
-    }
-
-    public byte getFlags()
-    {
-        return flags;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("%s frame v%s", getType(), getVersion());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrameType.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrameType.java
deleted file mode 100644
index 6ab9f9f..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/ControlFrameType.java
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public enum ControlFrameType
-{
-    SYN_STREAM((short)1),
-    SYN_REPLY((short)2),
-    RST_STREAM((short)3),
-    SETTINGS((short)4),
-    NOOP((short)5),
-    PING((short)6),
-    GO_AWAY((short)7),
-    HEADERS((short)8),
-    WINDOW_UPDATE((short)9),
-    CREDENTIAL((short)10);
-
-    public static ControlFrameType from(short code)
-    {
-        return Codes.codes.get(code);
-    }
-
-    private final short code;
-
-    private ControlFrameType(short code)
-    {
-        this.code = code;
-        Codes.codes.put(code, this);
-    }
-
-    public short getCode()
-    {
-        return code;
-    }
-
-    private static class Codes
-    {
-        private static final Map<Short, ControlFrameType> codes = new HashMap<>();
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/CredentialFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/CredentialFrame.java
deleted file mode 100644
index 2c392eb..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/CredentialFrame.java
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import java.security.cert.Certificate;
-
-public class CredentialFrame extends ControlFrame
-{
-    private final short slot;
-    private final byte[] proof;
-    private final Certificate[] certificateChain;
-
-    public CredentialFrame(short version, short slot, byte[] proof, Certificate[] certificateChain)
-    {
-        super(version, ControlFrameType.CREDENTIAL, (byte)0);
-        this.slot = slot;
-        this.proof = proof;
-        this.certificateChain = certificateChain;
-    }
-
-    public short getSlot()
-    {
-        return slot;
-    }
-
-    public byte[] getProof()
-    {
-        return proof;
-    }
-
-    public Certificate[] getCertificateChain()
-    {
-        return certificateChain;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/DataFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/DataFrame.java
deleted file mode 100644
index da73158..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/DataFrame.java
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import org.eclipse.jetty.spdy.api.DataInfo;
-
-public class DataFrame
-{
-    public static final int HEADER_LENGTH = 8;
-
-    private final int streamId;
-    private final byte flags;
-    private final int length;
-
-    public DataFrame(int streamId, byte flags, int length)
-    {
-        this.streamId = streamId;
-        this.flags = flags;
-        this.length = length;
-    }
-
-    public int getStreamId()
-    {
-        return streamId;
-    }
-
-    public byte getFlags()
-    {
-        return flags;
-    }
-
-    public int getLength()
-    {
-        return length;
-    }
-
-    public boolean isClose()
-    {
-        return (flags & DataInfo.FLAG_CLOSE) == DataInfo.FLAG_CLOSE;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("DATA frame stream=%d length=%d close=%b", getStreamId(), getLength(), isClose());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/GoAwayFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/GoAwayFrame.java
deleted file mode 100644
index 37e4473..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/GoAwayFrame.java
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import org.eclipse.jetty.spdy.api.SessionStatus;
-
-public class GoAwayFrame extends ControlFrame
-{
-    private final int lastStreamId;
-    private final int statusCode;
-
-    public GoAwayFrame(short version, int lastStreamId, int statusCode)
-    {
-        super(version, ControlFrameType.GO_AWAY, (byte)0);
-        this.lastStreamId = lastStreamId;
-        this.statusCode = statusCode;
-    }
-
-    public int getLastStreamId()
-    {
-        return lastStreamId;
-    }
-
-    public int getStatusCode()
-    {
-        return statusCode;
-    }
-
-    @Override
-    public String toString()
-    {
-        SessionStatus sessionStatus = SessionStatus.from(getStatusCode());
-        return String.format("%s last_stream=%d status=%s", super.toString(), getLastStreamId(), sessionStatus == null ? getStatusCode() : sessionStatus);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/HeadersFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/HeadersFrame.java
deleted file mode 100644
index db46f06..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/HeadersFrame.java
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.util.Fields;
-
-public class HeadersFrame extends ControlFrame
-{
-    private final int streamId;
-    private final Fields headers;
-
-    public HeadersFrame(short version, byte flags, int streamId, Fields headers)
-    {
-        super(version, ControlFrameType.HEADERS, flags);
-        this.streamId = streamId;
-        this.headers = headers;
-    }
-
-    public int getStreamId()
-    {
-        return streamId;
-    }
-
-    public Fields getHeaders()
-    {
-        return headers;
-    }
-
-    public boolean isClose()
-    {
-        return (getFlags() & HeadersInfo.FLAG_CLOSE) == HeadersInfo.FLAG_CLOSE;
-    }
-
-    public boolean isResetCompression()
-    {
-        return (getFlags() & HeadersInfo.FLAG_RESET_COMPRESSION) == HeadersInfo.FLAG_RESET_COMPRESSION;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("%s stream=%d close=%b reset_compression=%b", super.toString(), getStreamId(), isClose(), isResetCompression());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/NoOpFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/NoOpFrame.java
deleted file mode 100644
index 4ec30ea..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/NoOpFrame.java
+++ /dev/null
@@ -1,29 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import org.eclipse.jetty.spdy.api.SPDY;
-
-public class NoOpFrame extends ControlFrame
-{
-    public NoOpFrame()
-    {
-        super(SPDY.V2, ControlFrameType.NOOP, (byte)0);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/PingFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/PingFrame.java
deleted file mode 100644
index c941fa6..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/PingFrame.java
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-public class PingFrame extends ControlFrame
-{
-    private final int pingId;
-
-    public PingFrame(short version, int pingId)
-    {
-        super(version, ControlFrameType.PING, (byte)0);
-        this.pingId = pingId;
-    }
-
-    public int getPingId()
-    {
-        return pingId;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("%s ping=%d", super.toString(), getPingId());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/RstStreamFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/RstStreamFrame.java
deleted file mode 100644
index f576c8e..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/RstStreamFrame.java
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import org.eclipse.jetty.spdy.api.StreamStatus;
-
-public class RstStreamFrame extends ControlFrame
-{
-    private final int streamId;
-    private final int statusCode;
-
-    public RstStreamFrame(short version, int streamId, int statusCode)
-    {
-        super(version, ControlFrameType.RST_STREAM, (byte)0);
-        this.streamId = streamId;
-        this.statusCode = statusCode;
-    }
-    
-    public int getStreamId()
-    {
-        return streamId;
-    }
-    
-    public int getStatusCode()
-    {
-        return statusCode;
-    }
-    
-    @Override
-    public String toString()
-    {
-        StreamStatus streamStatus = StreamStatus.from(getVersion(), getStatusCode());
-        return String.format("%s stream=%d status=%s", super.toString(), getStreamId(), streamStatus == null ? getStatusCode() : streamStatus);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SettingsFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SettingsFrame.java
deleted file mode 100644
index 8aec958..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SettingsFrame.java
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import org.eclipse.jetty.spdy.api.Settings;
-import org.eclipse.jetty.spdy.api.SettingsInfo;
-
-public class SettingsFrame extends ControlFrame
-{
-    private final Settings settings;
-
-    public SettingsFrame(short version, byte flags, Settings settings)
-    {
-        super(version, ControlFrameType.SETTINGS, flags);
-        this.settings = settings;
-    }
-
-    public boolean isClearPersisted()
-    {
-        return (getFlags() & SettingsInfo.CLEAR_PERSISTED) == SettingsInfo.CLEAR_PERSISTED;
-    }
-
-    public Settings getSettings()
-    {
-        return settings;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("%s clear_persisted=%b settings=%s", super.toString(), isClearPersisted(), getSettings());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynReplyFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynReplyFrame.java
deleted file mode 100644
index 44d0100..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynReplyFrame.java
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.util.Fields;
-
-public class SynReplyFrame extends ControlFrame
-{
-    private final int streamId;
-    private final Fields headers;
-
-    public SynReplyFrame(short version, byte flags, int streamId, Fields headers)
-    {
-        super(version, ControlFrameType.SYN_REPLY, flags);
-        this.streamId = streamId;
-        this.headers = headers;
-    }
-
-    public int getStreamId()
-    {
-        return streamId;
-    }
-
-    public Fields getHeaders()
-    {
-        return headers;
-    }
-
-    public boolean isClose()
-    {
-        return (getFlags() & ReplyInfo.FLAG_CLOSE) == ReplyInfo.FLAG_CLOSE;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("%s stream=%d close=%b", super.toString(), getStreamId(), isClose());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynStreamFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynStreamFrame.java
deleted file mode 100644
index 256d6e8..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/SynStreamFrame.java
+++ /dev/null
@@ -1,83 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import org.eclipse.jetty.spdy.PushSynInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.util.Fields;
-
-public class SynStreamFrame extends ControlFrame
-{
-    private final int streamId;
-    private final int associatedStreamId;
-    private final byte priority;
-    private final short slot;
-    private final Fields headers;
-
-    public SynStreamFrame(short version, byte flags, int streamId, int associatedStreamId, byte priority, short slot, Fields headers)
-    {
-        super(version, ControlFrameType.SYN_STREAM, flags);
-        this.streamId = streamId;
-        this.associatedStreamId = associatedStreamId;
-        this.priority = priority;
-        this.slot = slot;
-        this.headers = headers;
-    }
-
-    public int getStreamId()
-    {
-        return streamId;
-    }
-
-    public int getAssociatedStreamId()
-    {
-        return associatedStreamId;
-    }
-
-    public byte getPriority()
-    {
-        return priority;
-    }
-
-    public short getSlot()
-    {
-        return slot;
-    }
-
-    public Fields getHeaders()
-    {
-        return headers;
-    }
-
-    public boolean isClose()
-    {
-        return (getFlags() & SynInfo.FLAG_CLOSE) == SynInfo.FLAG_CLOSE;
-    }
-
-    public boolean isUnidirectional()
-    {
-        return (getFlags() & PushSynInfo.FLAG_UNIDIRECTIONAL) == PushSynInfo.FLAG_UNIDIRECTIONAL;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("%s stream=%d close=%b", super.toString(), getStreamId(), isClose());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/WindowUpdateFrame.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/WindowUpdateFrame.java
deleted file mode 100644
index 969063a..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/frames/WindowUpdateFrame.java
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-public class WindowUpdateFrame extends ControlFrame
-{
-    private final int streamId;
-    private final int windowDelta;
-
-    public WindowUpdateFrame(short version, int streamId, int windowDelta)
-    {
-        super(version, ControlFrameType.WINDOW_UPDATE, (byte)0);
-        this.streamId = streamId;
-        this.windowDelta = windowDelta;
-    }
-
-    public int getStreamId()
-    {
-        return streamId;
-    }
-
-    public int getWindowDelta()
-    {
-        return windowDelta;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("%s stream=%d delta=%d", super.toString(), getStreamId(), getWindowDelta());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/ControlFrameGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/ControlFrameGenerator.java
deleted file mode 100644
index c8f63e3..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/ControlFrameGenerator.java
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-
-public abstract class ControlFrameGenerator
-{
-    private final ByteBufferPool bufferPool;
-
-    protected ControlFrameGenerator(ByteBufferPool bufferPool)
-    {
-        this.bufferPool = bufferPool;
-    }
-
-    protected ByteBufferPool getByteBufferPool()
-    {
-        return bufferPool;
-    }
-
-    public abstract ByteBuffer generate(ControlFrame frame);
-
-    protected void generateControlFrameHeader(ControlFrame frame, int frameLength, ByteBuffer buffer)
-    {
-        buffer.putShort((short)(0x8000 + frame.getVersion()));
-        buffer.putShort(frame.getType().getCode());
-        int flagsAndLength = frame.getFlags();
-        flagsAndLength <<= 24;
-        flagsAndLength += frameLength;
-        buffer.putInt(flagsAndLength);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/CredentialGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/CredentialGenerator.java
deleted file mode 100644
index fcaa0e8..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/CredentialGenerator.java
+++ /dev/null
@@ -1,87 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.SessionException;
-import org.eclipse.jetty.spdy.api.SessionStatus;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.CredentialFrame;
-import org.eclipse.jetty.util.BufferUtil;
-
-public class CredentialGenerator extends ControlFrameGenerator
-{
-    public CredentialGenerator(ByteBufferPool bufferPool)
-    {
-        super(bufferPool);
-    }
-
-    @Override
-    public ByteBuffer generate(ControlFrame frame)
-    {
-        CredentialFrame credential = (CredentialFrame)frame;
-
-        byte[] proof = credential.getProof();
-
-        List<byte[]> certificates = serializeCertificates(credential.getCertificateChain());
-        int certificatesLength = 0;
-        for (byte[] certificate : certificates)
-            certificatesLength += certificate.length;
-
-        int frameBodyLength = 2 + 4 + proof.length + certificates.size() * 4 + certificatesLength;
-
-        int totalLength = ControlFrame.HEADER_LENGTH + frameBodyLength;
-        ByteBuffer buffer = getByteBufferPool().acquire(totalLength, Generator.useDirectBuffers);
-        BufferUtil.clearToFill(buffer);
-        generateControlFrameHeader(credential, frameBodyLength, buffer);
-
-        buffer.putShort(credential.getSlot());
-        buffer.putInt(proof.length);
-        buffer.put(proof);
-        for (byte[] certificate : certificates)
-        {
-            buffer.putInt(certificate.length);
-            buffer.put(certificate);
-        }
-
-        buffer.flip();
-        return buffer;
-    }
-
-    private List<byte[]> serializeCertificates(Certificate[] certificates)
-    {
-        try
-        {
-            List<byte[]> result = new ArrayList<>(certificates.length);
-            for (Certificate certificate : certificates)
-                result.add(certificate.getEncoded());
-            return result;
-        }
-        catch (CertificateEncodingException x)
-        {
-            throw new SessionException(SessionStatus.PROTOCOL_ERROR, x);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/DataFrameGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/DataFrameGenerator.java
deleted file mode 100644
index 6951bc2..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/DataFrameGenerator.java
+++ /dev/null
@@ -1,57 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.frames.DataFrame;
-import org.eclipse.jetty.util.BufferUtil;
-
-public class DataFrameGenerator
-{
-    private final ByteBufferPool bufferPool;
-
-    public DataFrameGenerator(ByteBufferPool bufferPool)
-    {
-        this.bufferPool = bufferPool;
-    }
-
-    public ByteBuffer generate(int streamId, int length, DataInfo dataInfo)
-    {
-        ByteBuffer buffer = bufferPool.acquire(DataFrame.HEADER_LENGTH + length, Generator.useDirectBuffers);
-        BufferUtil.clearToFill(buffer);
-        buffer.limit(length + DataFrame.HEADER_LENGTH);
-        buffer.position(DataFrame.HEADER_LENGTH);
-        // Guaranteed to always be >= 0
-        int read = dataInfo.readInto(buffer);
-
-        buffer.putInt(0, streamId & 0x7F_FF_FF_FF);
-        buffer.putInt(4, read & 0x00_FF_FF_FF);
-
-        byte flags = dataInfo.getFlags();
-        if (dataInfo.available() > 0)
-            flags &= ~DataInfo.FLAG_CLOSE;
-        buffer.put(4, flags);
-
-        buffer.flip();
-        return buffer;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/Generator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/Generator.java
deleted file mode 100644
index 22bd527..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/Generator.java
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-import java.util.EnumMap;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.CompressionFactory;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.ControlFrameType;
-
-public class Generator
-{
-    final static boolean useDirectBuffers=false;
-    private final EnumMap<ControlFrameType, ControlFrameGenerator> generators = new EnumMap<>(ControlFrameType.class);
-    private final DataFrameGenerator dataFrameGenerator;
-
-    public Generator(ByteBufferPool bufferPool, CompressionFactory.Compressor compressor)
-    {
-        HeadersBlockGenerator headersBlockGenerator = new HeadersBlockGenerator(compressor);
-        generators.put(ControlFrameType.SYN_STREAM, new SynStreamGenerator(bufferPool, headersBlockGenerator));
-        generators.put(ControlFrameType.SYN_REPLY, new SynReplyGenerator(bufferPool, headersBlockGenerator));
-        generators.put(ControlFrameType.RST_STREAM, new RstStreamGenerator(bufferPool));
-        generators.put(ControlFrameType.SETTINGS, new SettingsGenerator(bufferPool));
-        generators.put(ControlFrameType.NOOP, new NoOpGenerator(bufferPool));
-        generators.put(ControlFrameType.PING, new PingGenerator(bufferPool));
-        generators.put(ControlFrameType.GO_AWAY, new GoAwayGenerator(bufferPool));
-        generators.put(ControlFrameType.HEADERS, new HeadersGenerator(bufferPool, headersBlockGenerator));
-        generators.put(ControlFrameType.WINDOW_UPDATE, new WindowUpdateGenerator(bufferPool));
-        generators.put(ControlFrameType.CREDENTIAL, new CredentialGenerator(bufferPool));
-
-        dataFrameGenerator = new DataFrameGenerator(bufferPool);
-    }
-
-    public ByteBuffer control(ControlFrame frame)
-    {
-        ControlFrameGenerator generator = generators.get(frame.getType());
-        return generator.generate(frame);
-    }
-
-    public ByteBuffer data(int streamId, int length, DataInfo dataInfo)
-    {
-        return dataFrameGenerator.generate(streamId, length, dataInfo);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/GoAwayGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/GoAwayGenerator.java
deleted file mode 100644
index 60c9565..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/GoAwayGenerator.java
+++ /dev/null
@@ -1,67 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.GoAwayFrame;
-import org.eclipse.jetty.util.BufferUtil;
-
-public class GoAwayGenerator extends ControlFrameGenerator
-{
-    public GoAwayGenerator(ByteBufferPool bufferPool)
-    {
-        super(bufferPool);
-    }
-
-    @Override
-    public ByteBuffer generate(ControlFrame frame)
-    {
-        GoAwayFrame goAway = (GoAwayFrame)frame;
-
-        int frameBodyLength = 8;
-        int totalLength = ControlFrame.HEADER_LENGTH + frameBodyLength;
-        ByteBuffer buffer = getByteBufferPool().acquire(totalLength, Generator.useDirectBuffers);
-        BufferUtil.clearToFill(buffer);
-        generateControlFrameHeader(goAway, frameBodyLength, buffer);
-
-        buffer.putInt(goAway.getLastStreamId() & 0x7F_FF_FF_FF);
-        writeStatusCode(goAway, buffer);
-
-        buffer.flip();
-        return buffer;
-    }
-
-    private void writeStatusCode(GoAwayFrame goAway, ByteBuffer buffer)
-    {
-        switch (goAway.getVersion())
-        {
-            case SPDY.V2:
-                break;
-            case SPDY.V3:
-                buffer.putInt(goAway.getStatusCode());
-                break;
-            default:
-                throw new IllegalStateException();
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersBlockGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersBlockGenerator.java
deleted file mode 100644
index 29cb663..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersBlockGenerator.java
+++ /dev/null
@@ -1,150 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.io.ByteArrayOutputStream;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-
-import org.eclipse.jetty.spdy.CompressionDictionary;
-import org.eclipse.jetty.spdy.CompressionFactory;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.util.Fields;
-
-public class HeadersBlockGenerator
-{
-    private final CompressionFactory.Compressor compressor;
-    private boolean needsDictionary = true;
-
-    public HeadersBlockGenerator(CompressionFactory.Compressor compressor)
-    {
-        this.compressor = compressor;
-    }
-
-    public ByteBuffer generate(short version, Fields headers)
-    {
-        // TODO: ByteArrayOutputStream is quite inefficient, but grows on demand; optimize using ByteBuffer ?
-        final Charset iso1 = StandardCharsets.ISO_8859_1;
-        ByteArrayOutputStream buffer = new ByteArrayOutputStream(headers.getSize() * 64);
-        writeCount(version, buffer, headers.getSize());
-        for (Fields.Field header : headers)
-        {
-            String name = header.getName().toLowerCase(Locale.ENGLISH);
-            byte[] nameBytes = name.getBytes(iso1);
-            writeNameLength(version, buffer, nameBytes.length);
-            buffer.write(nameBytes, 0, nameBytes.length);
-
-            // Most common path first
-            String value = header.getValue();
-            byte[] valueBytes = value.getBytes(iso1);
-            if (header.hasMultipleValues())
-            {
-                List<String> values = header.getValues();
-                for (int i = 1; i < values.size(); ++i)
-                {
-                    byte[] moreValueBytes = values.get(i).getBytes(iso1);
-                    byte[] newValueBytes = Arrays.copyOf(valueBytes,valueBytes.length + 1 + moreValueBytes.length);
-                    newValueBytes[valueBytes.length] = 0;
-                    System.arraycopy(moreValueBytes, 0, newValueBytes, valueBytes.length + 1, moreValueBytes.length);
-                    valueBytes = newValueBytes;
-                }
-            }
-
-            writeValueLength(version, buffer, valueBytes.length);
-            buffer.write(valueBytes, 0, valueBytes.length);
-        }
-
-        return compress(version, buffer.toByteArray());
-    }
-
-    private ByteBuffer compress(short version, byte[] bytes)
-    {
-        ByteArrayOutputStream buffer = new ByteArrayOutputStream(bytes.length);
-
-        // The headers compression context is per-session, so we need to synchronize
-        synchronized (compressor)
-        {
-            if (needsDictionary)
-            {
-                compressor.setDictionary(CompressionDictionary.get(version));
-                needsDictionary = false;
-            }
-
-            compressor.setInput(bytes);
-
-            // Compressed bytes may be bigger than input bytes, so we need to loop and accumulate them
-            // Beware that the minimum amount of bytes generated by the compressor is few bytes, so we
-            // need to use an output buffer that is big enough to exit the compress loop
-            buffer.reset();
-            int compressed;
-            byte[] output = new byte[Math.max(256, bytes.length)];
-            while (true)
-            {
-                // SPDY uses the SYNC_FLUSH mode
-                compressed = compressor.compress(output);
-                buffer.write(output, 0, compressed);
-                if (compressed < output.length)
-                    break;
-            }
-        }
-
-        return ByteBuffer.wrap(buffer.toByteArray());
-    }
-
-    private void writeCount(short version, ByteArrayOutputStream buffer, int value)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-            {
-                buffer.write((value & 0xFF_00) >>> 8);
-                buffer.write(value & 0x00_FF);
-                break;
-            }
-            case SPDY.V3:
-            {
-                buffer.write((value & 0xFF_00_00_00) >>> 24);
-                buffer.write((value & 0x00_FF_00_00) >>> 16);
-                buffer.write((value & 0x00_00_FF_00) >>> 8);
-                buffer.write(value & 0x00_00_00_FF);
-                break;
-            }
-            default:
-            {
-                // Here the version is trusted to be correct; if it's not
-                // then it's a bug rather than an application error
-                throw new IllegalStateException();
-            }
-        }
-    }
-
-    private void writeNameLength(short version, ByteArrayOutputStream buffer, int length)
-    {
-        writeCount(version, buffer, length);
-    }
-
-    private void writeValueLength(short version, ByteArrayOutputStream buffer, int length)
-    {
-        writeCount(version, buffer, length);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersGenerator.java
deleted file mode 100644
index 9224476..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersGenerator.java
+++ /dev/null
@@ -1,76 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.SessionException;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.SessionStatus;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.HeadersFrame;
-import org.eclipse.jetty.util.BufferUtil;
-
-public class HeadersGenerator extends ControlFrameGenerator
-{
-    private final HeadersBlockGenerator headersBlockGenerator;
-
-    public HeadersGenerator(ByteBufferPool bufferPool, HeadersBlockGenerator headersBlockGenerator)
-    {
-        super(bufferPool);
-        this.headersBlockGenerator = headersBlockGenerator;
-    }
-
-    @Override
-    public ByteBuffer generate(ControlFrame frame)
-    {
-        HeadersFrame headers = (HeadersFrame)frame;
-        short version = headers.getVersion();
-
-        ByteBuffer headersBuffer = headersBlockGenerator.generate(version, headers.getHeaders());
-
-        int frameBodyLength = 4;
-        if (frame.getVersion() == SPDY.V2)
-            frameBodyLength += 2;
-
-        int frameLength = frameBodyLength + headersBuffer.remaining();
-        if (frameLength > 0xFF_FF_FF)
-        {
-            // Too many headers, but unfortunately we have already modified the compression
-            // context, so we have no other choice than tear down the connection.
-            throw new SessionException(SessionStatus.PROTOCOL_ERROR, "Too many headers");
-        }
-
-        int totalLength = ControlFrame.HEADER_LENGTH + frameLength;
-
-        ByteBuffer buffer = getByteBufferPool().acquire(totalLength, Generator.useDirectBuffers);
-        BufferUtil.clearToFill(buffer);
-        generateControlFrameHeader(headers, frameLength, buffer);
-
-        buffer.putInt(headers.getStreamId() & 0x7F_FF_FF_FF);
-        if (frame.getVersion() == SPDY.V2)
-            buffer.putShort((short)0);
-
-        buffer.put(headersBuffer);
-
-        buffer.flip();
-        return buffer;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/NoOpGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/NoOpGenerator.java
deleted file mode 100644
index b303424..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/NoOpGenerator.java
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.NoOpFrame;
-import org.eclipse.jetty.util.BufferUtil;
-
-public class NoOpGenerator extends ControlFrameGenerator
-{
-    public NoOpGenerator(ByteBufferPool bufferPool)
-    {
-        super(bufferPool);
-    }
-
-    @Override
-    public ByteBuffer generate(ControlFrame frame)
-    {
-        NoOpFrame noOp = (NoOpFrame)frame;
-
-        int frameBodyLength = 0;
-        int totalLength = ControlFrame.HEADER_LENGTH + frameBodyLength;
-        ByteBuffer buffer = getByteBufferPool().acquire(totalLength, Generator.useDirectBuffers);
-        BufferUtil.clearToFill(buffer);
-        generateControlFrameHeader(noOp, frameBodyLength, buffer);
-
-        buffer.flip();
-        return buffer;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/PingGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/PingGenerator.java
deleted file mode 100644
index c5cb794..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/PingGenerator.java
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.PingFrame;
-import org.eclipse.jetty.util.BufferUtil;
-
-public class PingGenerator extends ControlFrameGenerator
-{
-    public PingGenerator(ByteBufferPool bufferPool)
-    {
-        super(bufferPool);
-    }
-
-    @Override
-    public ByteBuffer generate(ControlFrame frame)
-    {
-        PingFrame ping = (PingFrame)frame;
-
-        int frameBodyLength = 4;
-        int totalLength = ControlFrame.HEADER_LENGTH + frameBodyLength;
-        ByteBuffer buffer = getByteBufferPool().acquire(totalLength, Generator.useDirectBuffers);
-        BufferUtil.clearToFill(buffer);
-        generateControlFrameHeader(ping, frameBodyLength, buffer);
-
-        buffer.putInt(ping.getPingId());
-
-        buffer.flip();
-        return buffer;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/RstStreamGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/RstStreamGenerator.java
deleted file mode 100644
index 1c21803..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/RstStreamGenerator.java
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.RstStreamFrame;
-import org.eclipse.jetty.util.BufferUtil;
-
-public class RstStreamGenerator extends ControlFrameGenerator
-{
-    public RstStreamGenerator(ByteBufferPool bufferPool)
-    {
-        super(bufferPool);
-    }
-
-    @Override
-    public ByteBuffer generate(ControlFrame frame)
-    {
-        RstStreamFrame rstStream = (RstStreamFrame)frame;
-
-        int frameBodyLength = 8;
-        int totalLength = ControlFrame.HEADER_LENGTH + frameBodyLength;
-        ByteBuffer buffer = getByteBufferPool().acquire(totalLength, Generator.useDirectBuffers);
-        BufferUtil.clearToFill(buffer);
-        generateControlFrameHeader(rstStream, frameBodyLength, buffer);
-
-        buffer.putInt(rstStream.getStreamId() & 0x7F_FF_FF_FF);
-        buffer.putInt(rstStream.getStatusCode());
-
-        buffer.flip();
-        return buffer;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SettingsGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SettingsGenerator.java
deleted file mode 100644
index d690cea..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SettingsGenerator.java
+++ /dev/null
@@ -1,91 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Settings;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.SettingsFrame;
-import org.eclipse.jetty.util.BufferUtil;
-
-public class SettingsGenerator extends ControlFrameGenerator
-{
-    public SettingsGenerator(ByteBufferPool bufferPool)
-    {
-        super(bufferPool);
-    }
-
-    @Override
-    public ByteBuffer generate(ControlFrame frame)
-    {
-        SettingsFrame settingsFrame = (SettingsFrame)frame;
-
-        Settings settings = settingsFrame.getSettings();
-        int size = settings.size();
-        int frameBodyLength = 4 + 8 * size;
-        int totalLength = ControlFrame.HEADER_LENGTH + frameBodyLength;
-        ByteBuffer buffer = getByteBufferPool().acquire(totalLength, Generator.useDirectBuffers);
-        BufferUtil.clearToFill(buffer);
-        generateControlFrameHeader(settingsFrame, frameBodyLength, buffer);
-
-        buffer.putInt(size);
-
-        for (Settings.Setting setting : settings)
-        {
-            int id = setting.id().code();
-            byte flags = setting.flag().code();
-            int idAndFlags = convertIdAndFlags(frame.getVersion(), id, flags);
-            buffer.putInt(idAndFlags);
-            buffer.putInt(setting.value());
-        }
-
-        buffer.flip();
-        return buffer;
-    }
-
-    private int convertIdAndFlags(short version, int id, byte flags)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-            {
-                // In v2 the format is 24 bits of ID + 8 bits of flag
-                int idAndFlags = (id << 8) + (flags & 0xFF);
-                // A bug in the Chromium implementation forces v2 to have
-                // the 3 ID bytes little endian, so we swap first and third
-                int result = idAndFlags & 0x00_FF_00_FF;
-                result += (idAndFlags & 0xFF_00_00_00) >>> 16;
-                result += (idAndFlags & 0x00_00_FF_00) << 16;
-                return result;
-            }
-            case SPDY.V3:
-            {
-                // In v3 the format is 8 bits of flags + 24 bits of ID
-                return (flags << 24) + (id & 0xFF_FF_FF);
-            }
-            default:
-            {
-                throw new IllegalStateException();
-            }
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynReplyGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynReplyGenerator.java
deleted file mode 100644
index 21c4047..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynReplyGenerator.java
+++ /dev/null
@@ -1,104 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.SessionException;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.SessionStatus;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.SynReplyFrame;
-import org.eclipse.jetty.util.BufferUtil;
-
-public class SynReplyGenerator extends ControlFrameGenerator
-{
-    private final HeadersBlockGenerator headersBlockGenerator;
-
-    public SynReplyGenerator(ByteBufferPool bufferPool, HeadersBlockGenerator headersBlockGenerator)
-    {
-        super(bufferPool);
-        this.headersBlockGenerator = headersBlockGenerator;
-    }
-
-    @Override
-    public ByteBuffer generate(ControlFrame frame)
-    {
-        SynReplyFrame synReply = (SynReplyFrame)frame;
-        short version = synReply.getVersion();
-
-        ByteBuffer headersBuffer = headersBlockGenerator.generate(version, synReply.getHeaders());
-
-        int frameBodyLength = getFrameDataLength(version);
-
-        int frameLength = frameBodyLength + headersBuffer.remaining();
-        if (frameLength > 0xFF_FF_FF)
-        {
-            // Too many headers, but unfortunately we have already modified the compression
-            // context, so we have no other choice than tear down the connection.
-            throw new SessionException(SessionStatus.PROTOCOL_ERROR, "Too many headers");
-        }
-
-        int totalLength = ControlFrame.HEADER_LENGTH + frameLength;
-
-        ByteBuffer buffer = getByteBufferPool().acquire(totalLength, Generator.useDirectBuffers);
-        BufferUtil.clearToFill(buffer);
-        generateControlFrameHeader(synReply, frameLength, buffer);
-
-        buffer.putInt(synReply.getStreamId() & 0x7F_FF_FF_FF);
-        writeAdditional(version, buffer);
-
-        buffer.put(headersBuffer);
-
-        buffer.flip();
-        return buffer;
-    }
-
-    private int getFrameDataLength(short version)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-                return 6;
-            case SPDY.V3:
-                return 4;
-            default:
-                // Here the version is trusted to be correct; if it's not
-                // then it's a bug rather than an application error
-                throw new IllegalStateException();
-        }
-    }
-
-    private void writeAdditional(short version, ByteBuffer buffer)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-                buffer.putShort((short)0);
-                break;
-            case SPDY.V3:
-                break;
-            default:
-                // Here the version is trusted to be correct; if it's not
-                // then it's a bug rather than an application error
-                throw new IllegalStateException();
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynStreamGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynStreamGenerator.java
deleted file mode 100644
index 861578c..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SynStreamGenerator.java
+++ /dev/null
@@ -1,94 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.SessionException;
-import org.eclipse.jetty.spdy.StreamException;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.SessionStatus;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.SynStreamFrame;
-import org.eclipse.jetty.util.BufferUtil;
-
-public class SynStreamGenerator extends ControlFrameGenerator
-{
-    private final HeadersBlockGenerator headersBlockGenerator;
-
-    public SynStreamGenerator(ByteBufferPool bufferPool, HeadersBlockGenerator headersBlockGenerator)
-    {
-        super(bufferPool);
-        this.headersBlockGenerator = headersBlockGenerator;
-    }
-
-    @Override
-    public ByteBuffer generate(ControlFrame frame)
-    {
-        SynStreamFrame synStream = (SynStreamFrame)frame;
-        short version = synStream.getVersion();
-
-        ByteBuffer headersBuffer = headersBlockGenerator.generate(version, synStream.getHeaders());
-
-        int frameBodyLength = 10;
-
-        int frameLength = frameBodyLength + headersBuffer.remaining();
-        if (frameLength > 0xFF_FF_FF)
-        {
-            // Too many headers, but unfortunately we have already modified the compression
-            // context, so we have no other choice than tear down the connection.
-            throw new SessionException(SessionStatus.PROTOCOL_ERROR, "Too many headers");
-        }
-
-        int totalLength = ControlFrame.HEADER_LENGTH + frameLength;
-
-        ByteBuffer buffer = getByteBufferPool().acquire(totalLength, Generator.useDirectBuffers);
-        BufferUtil.clearToFill(buffer);
-        generateControlFrameHeader(synStream, frameLength, buffer);
-
-        int streamId = synStream.getStreamId();
-        buffer.putInt(streamId & 0x7F_FF_FF_FF);
-        buffer.putInt(synStream.getAssociatedStreamId() & 0x7F_FF_FF_FF);
-        writePriority(streamId, version, synStream.getPriority(), buffer);
-        buffer.put((byte)synStream.getSlot());
-
-        buffer.put(headersBuffer);
-
-        buffer.flip();
-        return buffer;
-    }
-
-    private void writePriority(int streamId, short version, byte priority, ByteBuffer buffer)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-                priority <<= 6;
-                break;
-            case SPDY.V3:
-                priority <<= 5;
-                break;
-            default:
-                throw new StreamException(streamId, StreamStatus.UNSUPPORTED_VERSION);
-        }
-        buffer.put(priority);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/WindowUpdateGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/WindowUpdateGenerator.java
deleted file mode 100644
index ca1cdb2..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/WindowUpdateGenerator.java
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
-import org.eclipse.jetty.util.BufferUtil;
-
-public class WindowUpdateGenerator extends ControlFrameGenerator
-{
-    public WindowUpdateGenerator(ByteBufferPool bufferPool)
-    {
-        super(bufferPool);
-    }
-
-    @Override
-    public ByteBuffer generate(ControlFrame frame)
-    {
-        WindowUpdateFrame windowUpdate = (WindowUpdateFrame)frame;
-
-        int frameBodyLength = 8;
-        int totalLength = ControlFrame.HEADER_LENGTH + frameBodyLength;
-        ByteBuffer buffer = getByteBufferPool().acquire(totalLength, Generator.useDirectBuffers);
-        BufferUtil.clearToFill(buffer);
-        generateControlFrameHeader(windowUpdate, frameBodyLength, buffer);
-
-        buffer.putInt(windowUpdate.getStreamId() & 0x7F_FF_FF_FF);
-        buffer.putInt(windowUpdate.getWindowDelta() & 0x7F_FF_FF_FF);
-
-        buffer.flip();
-        return buffer;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameBodyParser.java
deleted file mode 100644
index b5ace64..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameBodyParser.java
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-
-public abstract class ControlFrameBodyParser
-{
-    public abstract boolean parse(ByteBuffer buffer);
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameParser.java
deleted file mode 100644
index c4f5cdb..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/ControlFrameParser.java
+++ /dev/null
@@ -1,213 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-import java.util.EnumMap;
-
-import org.eclipse.jetty.spdy.CompressionFactory;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.ControlFrameType;
-
-public abstract class ControlFrameParser
-{
-    private final EnumMap<ControlFrameType, ControlFrameBodyParser> parsers = new EnumMap<>(ControlFrameType.class);
-    private final ControlFrameBodyParser unknownParser = new UnknownControlFrameBodyParser(this);
-    private State state = State.VERSION;
-    private int cursor;
-    private short version;
-    private short type;
-    private byte flags;
-    private int length;
-    private ControlFrameBodyParser bodyParser;
-    private int bytesToSkip = 0;
-
-    public ControlFrameParser(CompressionFactory.Decompressor decompressor)
-    {
-        parsers.put(ControlFrameType.SYN_STREAM, new SynStreamBodyParser(decompressor, this));
-        parsers.put(ControlFrameType.SYN_REPLY, new SynReplyBodyParser(decompressor, this));
-        parsers.put(ControlFrameType.RST_STREAM, new RstStreamBodyParser(this));
-        parsers.put(ControlFrameType.SETTINGS, new SettingsBodyParser(this));
-        parsers.put(ControlFrameType.NOOP, new NoOpBodyParser(this));
-        parsers.put(ControlFrameType.PING, new PingBodyParser(this));
-        parsers.put(ControlFrameType.GO_AWAY, new GoAwayBodyParser(this));
-        parsers.put(ControlFrameType.HEADERS, new HeadersBodyParser(decompressor, this));
-        parsers.put(ControlFrameType.WINDOW_UPDATE, new WindowUpdateBodyParser(this));
-        parsers.put(ControlFrameType.CREDENTIAL, new CredentialBodyParser(this));
-    }
-
-    public short getVersion()
-    {
-        return version;
-    }
-
-    public byte getFlags()
-    {
-        return flags;
-    }
-
-    public int getLength()
-    {
-        return length;
-    }
-
-    public void skip(int bytesToSkip)
-    {
-        state = State.SKIP;
-        this.bytesToSkip = bytesToSkip;
-    }
-
-    public boolean parse(ByteBuffer buffer)
-    {
-        while (buffer.hasRemaining())
-        {
-            switch (state)
-            {
-                case VERSION:
-                {
-                    if (buffer.remaining() >= 2)
-                    {
-                        version = (short)(buffer.getShort() & 0x7F_FF);
-                        state = State.TYPE;
-                    }
-                    else
-                    {
-                        state = State.VERSION_BYTES;
-                        cursor = 2;
-                    }
-                    break;
-                }
-                case VERSION_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    version += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        version &= 0x7F_FF;
-                        state = State.TYPE;
-                    }
-                    break;
-                }
-                case TYPE:
-                {
-                    if (buffer.remaining() >= 2)
-                    {
-                        type = buffer.getShort();
-                        state = State.FLAGS;
-                    }
-                    else
-                    {
-                        state = State.TYPE_BYTES;
-                        cursor = 2;
-                    }
-                    break;
-                }
-                case TYPE_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    type += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                        state = State.FLAGS;
-                    break;
-                }
-                case FLAGS:
-                {
-                    flags = buffer.get();
-                    cursor = 3;
-                    state = State.LENGTH;
-                    break;
-                }
-                case LENGTH:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    length += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor > 0)
-                        break;
-
-                    ControlFrameType controlFrameType = ControlFrameType.from(type);
-
-                    // SPEC v3, 2.2.1: unrecognized control frames must be ignored
-                    if (controlFrameType == null)
-                        bodyParser = unknownParser;
-                    else
-                        bodyParser = parsers.get(controlFrameType);
-
-                    state = State.BODY;
-
-                    // We have to let it fall through the next switch:
-                    // the NOOP frame has no body and we cannot break
-                    // because the buffer may be consumed and we will
-                    // never enter the BODY case.
-                }
-                case BODY:
-                {
-                    if (bodyParser.parse(buffer))
-                    {
-                        reset();
-                        return true;
-                    }
-                    break;
-                }
-                case SKIP:
-                {
-                    int remaining = buffer.remaining();
-                    if (remaining >= bytesToSkip)
-                    {
-                        buffer.position(buffer.position() + bytesToSkip);
-                        reset();
-                        return true;
-                    }
-                    else
-                    {
-                        buffer.position(buffer.limit());
-                        bytesToSkip = bytesToSkip - remaining;
-                        return false;
-                    }
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-        }
-        return false;
-    }
-
-    void reset()
-    {
-        state = State.VERSION;
-        cursor = 0;
-        version = 0;
-        type = 0;
-        flags = 0;
-        length = 0;
-        bodyParser = null;
-        bytesToSkip = 0;
-    }
-
-    protected abstract void onControlFrame(ControlFrame frame);
-
-    private enum State
-    {
-        VERSION, VERSION_BYTES, TYPE, TYPE_BYTES, FLAGS, LENGTH, BODY, SKIP
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/CredentialBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/CredentialBodyParser.java
deleted file mode 100644
index 660240d..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/CredentialBodyParser.java
+++ /dev/null
@@ -1,274 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.io.ByteArrayInputStream;
-import java.nio.ByteBuffer;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.eclipse.jetty.spdy.SessionException;
-import org.eclipse.jetty.spdy.api.SessionStatus;
-import org.eclipse.jetty.spdy.frames.ControlFrameType;
-import org.eclipse.jetty.spdy.frames.CredentialFrame;
-
-public class CredentialBodyParser extends ControlFrameBodyParser
-{
-    private final List<Certificate> certificates = new ArrayList<>();
-    private final ControlFrameParser controlFrameParser;
-    private State state = State.SLOT;
-    private int totalLength;
-    private int cursor;
-    private short slot;
-    private int proofLength;
-    private byte[] proof;
-    private int certificateLength;
-    private byte[] certificate;
-
-    public CredentialBodyParser(ControlFrameParser controlFrameParser)
-    {
-        this.controlFrameParser = controlFrameParser;
-    }
-
-    @Override
-    public boolean parse(ByteBuffer buffer)
-    {
-        while (buffer.hasRemaining())
-        {
-            switch (state)
-            {
-                case SLOT:
-                {
-                    if (buffer.remaining() >= 2)
-                    {
-                        slot = buffer.getShort();
-                        checkSlotValid();
-                        state = State.PROOF_LENGTH;
-                    }
-                    else
-                    {
-                        state = State.SLOT_BYTES;
-                        cursor = 2;
-                    }
-                    break;
-                }
-                case SLOT_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    slot += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        checkSlotValid();
-                        state = State.PROOF_LENGTH;
-                    }
-                    break;
-                }
-                case PROOF_LENGTH:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        proofLength = buffer.getInt() & 0x7F_FF_FF_FF;
-                        state = State.PROOF;
-                    }
-                    else
-                    {
-                        state = State.PROOF_LENGTH_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case PROOF_LENGTH_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    proofLength += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        proofLength &= 0x7F_FF_FF_FF;
-                        state = State.PROOF;
-                    }
-                    break;
-                }
-                case PROOF:
-                {
-                    totalLength = controlFrameParser.getLength() - 2 - 4 - proofLength;
-                    proof = new byte[proofLength];
-                    if (buffer.remaining() >= proofLength)
-                    {
-                        buffer.get(proof);
-                        state = State.CERTIFICATE_LENGTH;
-                        if (totalLength == 0)
-                        {
-                            onCredential();
-                            return true;
-                        }
-                    }
-                    else
-                    {
-                        state = State.PROOF_BYTES;
-                        cursor = proofLength;
-                    }
-                    break;
-                }
-                case PROOF_BYTES:
-                {
-                    proof[proofLength - cursor] = buffer.get();
-                    --cursor;
-                    if (cursor == 0)
-                    {
-                        state = State.CERTIFICATE_LENGTH;
-                        if (totalLength == 0)
-                        {
-                            onCredential();
-                            return true;
-                        }
-                    }
-                    break;
-                }
-                case CERTIFICATE_LENGTH:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        certificateLength = buffer.getInt() & 0x7F_FF_FF_FF;
-                        state = State.CERTIFICATE;
-                    }
-                    else
-                    {
-                        state = State.CERTIFICATE_LENGTH_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case CERTIFICATE_LENGTH_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    certificateLength += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        certificateLength &= 0x7F_FF_FF_FF;
-                        state = State.CERTIFICATE;
-                    }
-                    break;
-                }
-                case CERTIFICATE:
-                {
-                    totalLength -= 4 + certificateLength;
-                    certificate = new byte[certificateLength];
-                    if (buffer.remaining() >= certificateLength)
-                    {
-                        buffer.get(certificate);
-                        if (onCertificate())
-                            return true;
-                    }
-                    else
-                    {
-                        state = State.CERTIFICATE_BYTES;
-                        cursor = certificateLength;
-                    }
-                    break;
-                }
-                case CERTIFICATE_BYTES:
-                {
-                    certificate[certificateLength - cursor] = buffer.get();
-                    --cursor;
-                    if (cursor == 0)
-                    {
-                        if (onCertificate())
-                            return true;
-                    }
-                    break;
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-        }
-        return false;
-    }
-
-    private void checkSlotValid()
-    {
-        if (slot <= 0)
-            throw new SessionException(SessionStatus.PROTOCOL_ERROR,
-                    "Invalid slot " + slot + " for " + ControlFrameType.CREDENTIAL + " frame");
-    }
-
-    private boolean onCertificate()
-    {
-        certificates.add(deserializeCertificate(certificate));
-        if (totalLength == 0)
-        {
-            onCredential();
-            return true;
-        }
-        else
-        {
-            certificateLength = 0;
-            state = State.CERTIFICATE_LENGTH;
-        }
-        return false;
-    }
-
-    private Certificate deserializeCertificate(byte[] bytes)
-    {
-        try
-        {
-            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
-            return certificateFactory.generateCertificate(new ByteArrayInputStream(bytes));
-        }
-        catch (CertificateException x)
-        {
-            throw new SessionException(SessionStatus.PROTOCOL_ERROR, x);
-        }
-    }
-
-    private void onCredential()
-    {
-        CredentialFrame frame = new CredentialFrame(controlFrameParser.getVersion(), slot,
-                Arrays.copyOf(proof, proof.length), certificates.toArray(new Certificate[certificates.size()]));
-        controlFrameParser.onControlFrame(frame);
-        reset();
-    }
-
-    private void reset()
-    {
-        state = State.SLOT;
-        totalLength = 0;
-        cursor = 0;
-        slot = 0;
-        proofLength = 0;
-        proof = null;
-        certificateLength = 0;
-        certificate = null;
-        certificates.clear();
-    }
-
-    public enum State
-    {
-        SLOT, SLOT_BYTES, PROOF_LENGTH, PROOF_LENGTH_BYTES, PROOF, PROOF_BYTES,
-        CERTIFICATE_LENGTH, CERTIFICATE_LENGTH_BYTES, CERTIFICATE, CERTIFICATE_BYTES
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/DataFrameParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/DataFrameParser.java
deleted file mode 100644
index 386d129..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/DataFrameParser.java
+++ /dev/null
@@ -1,155 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.frames.DataFrame;
-
-public abstract class DataFrameParser
-{
-    private State state = State.STREAM_ID;
-    private int cursor;
-    private int streamId;
-    private byte flags;
-    private int length;
-
-    /**
-     * <p>Parses the given {@link ByteBuffer} for a data frame.</p>
-     *
-     * @param buffer the {@link ByteBuffer} to parse
-     * @return true if the data frame has been fully parsed, false otherwise
-     */
-    public boolean parse(ByteBuffer buffer)
-    {
-        while (buffer.hasRemaining())
-        {
-            switch (state)
-            {
-                case STREAM_ID:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        streamId = buffer.getInt() & 0x7F_FF_FF_FF;
-                        state = State.FLAGS;
-                    }
-                    else
-                    {
-                        state = State.STREAM_ID_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case STREAM_ID_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    streamId += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                        state = State.FLAGS;
-                    break;
-                }
-                case FLAGS:
-                {
-                    flags = buffer.get();
-                    cursor = 3;
-                    state = State.LENGTH;
-                    break;
-                }
-                case LENGTH:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    length += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor > 0)
-                        break;
-                    state = State.DATA;
-                    // Fall down if length == 0: we can't loop because the buffer
-                    // may be empty but we need to invoke the application anyway
-                    if (length > 0)
-                        break;
-                }
-                case DATA:
-                {
-                    // Length can only be at most 3 bytes, which is 16_777_215 i.e. 16 MiB.
-                    // However, compliant clients should implement flow control, so it's
-                    // unlikely that we will get that 16 MiB chunk.
-                    // However, TCP may further split the flow control window, so we may
-                    // only have part of the data at this point.
-
-                    int size = Math.min(length, buffer.remaining());
-                    int limit = buffer.limit();
-                    buffer.limit(buffer.position() + size);
-                    ByteBuffer bytes = buffer.slice();
-                    buffer.limit(limit);
-                    buffer.position(buffer.position() + size);
-                    length -= size;
-                    if (length == 0)
-                    {
-                        onDataFrame(bytes);
-                        return true;
-                    }
-                    else
-                    {
-                        // We got only part of the frame data bytes,
-                        // so we generate a synthetic data frame
-                        onDataFragment(bytes);
-                    }
-                    break;
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-        }
-        return false;
-    }
-
-    private void onDataFrame(ByteBuffer bytes)
-    {
-        DataFrame frame = new DataFrame(streamId, flags, bytes.remaining());
-        onDataFrame(frame, bytes);
-        reset();
-    }
-
-    private void onDataFragment(ByteBuffer bytes)
-    {
-        DataFrame frame = new DataFrame(streamId, (byte)(flags & ~DataInfo.FLAG_CLOSE), bytes.remaining());
-        onDataFrame(frame, bytes);
-        // Do not reset, we're expecting more data
-    }
-
-    protected abstract void onDataFrame(DataFrame frame, ByteBuffer data);
-
-    private void reset()
-    {
-        state = State.STREAM_ID;
-        cursor = 0;
-        streamId = 0;
-        flags = 0;
-        length = 0;
-    }
-
-    private enum State
-    {
-        STREAM_ID, STREAM_ID_BYTES, FLAGS, LENGTH, DATA
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/GoAwayBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/GoAwayBodyParser.java
deleted file mode 100644
index bbdcd4c..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/GoAwayBodyParser.java
+++ /dev/null
@@ -1,159 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.frames.GoAwayFrame;
-
-public class GoAwayBodyParser extends ControlFrameBodyParser
-{
-    private final ControlFrameParser controlFrameParser;
-    private State state = State.LAST_GOOD_STREAM_ID;
-    private int cursor;
-    private int lastStreamId;
-    private int statusCode;
-
-    public GoAwayBodyParser(ControlFrameParser controlFrameParser)
-    {
-        this.controlFrameParser = controlFrameParser;
-    }
-
-    @Override
-    public boolean parse(ByteBuffer buffer)
-    {
-        while (buffer.hasRemaining())
-        {
-            switch (state)
-            {
-                case LAST_GOOD_STREAM_ID:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        lastStreamId = buffer.getInt() & 0x7F_FF_FF_FF;
-                        switch (controlFrameParser.getVersion())
-                        {
-                            case SPDY.V2:
-                            {
-                                onGoAway();
-                                return true;
-                            }
-                            case SPDY.V3:
-                            {
-                                state = State.STATUS_CODE;
-                                break;
-                            }
-                            default:
-                            {
-                                throw new IllegalStateException();
-                            }
-                        }
-                    }
-                    else
-                    {
-                        state = State.LAST_GOOD_STREAM_ID_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case LAST_GOOD_STREAM_ID_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    lastStreamId += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        lastStreamId &= 0x7F_FF_FF_FF;
-                        switch (controlFrameParser.getVersion())
-                        {
-                            case SPDY.V2:
-                            {
-                                onGoAway();
-                                return true;
-                            }
-                            case SPDY.V3:
-                            {
-                                state = State.STATUS_CODE;
-                                break;
-                            }
-                            default:
-                            {
-                                throw new IllegalStateException();
-                            }
-                        }
-                    }
-                    break;
-                }
-                case STATUS_CODE:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        statusCode = buffer.getInt();
-                        onGoAway();
-                        return true;
-                    }
-                    else
-                    {
-                        state = State.STATUS_CODE_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case STATUS_CODE_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    statusCode += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        onGoAway();
-                        return true;
-                    }
-                    break;
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-        }
-        return false;
-    }
-
-    private void onGoAway()
-    {
-        GoAwayFrame frame = new GoAwayFrame(controlFrameParser.getVersion(), lastStreamId, statusCode);
-        controlFrameParser.onControlFrame(frame);
-        reset();
-    }
-
-    private void reset()
-    {
-        state = State.LAST_GOOD_STREAM_ID;
-        cursor = 0;
-        lastStreamId = 0;
-        statusCode = 0;
-    }
-
-    private enum State
-    {
-        LAST_GOOD_STREAM_ID, LAST_GOOD_STREAM_ID_BYTES, STATUS_CODE, STATUS_CODE_BYTES
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBlockParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBlockParser.java
deleted file mode 100644
index 5896a5c..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBlockParser.java
+++ /dev/null
@@ -1,230 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.zip.ZipException;
-
-import org.eclipse.jetty.spdy.CompressionDictionary;
-import org.eclipse.jetty.spdy.CompressionFactory;
-import org.eclipse.jetty.spdy.SessionException;
-import org.eclipse.jetty.spdy.StreamException;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.SessionStatus;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-
-public abstract class HeadersBlockParser
-{
-    private final CompressionFactory.Decompressor decompressor;
-    private byte[] data;
-    private boolean needsDictionary = true;
-
-    protected HeadersBlockParser(CompressionFactory.Decompressor decompressor)
-    {
-        this.decompressor = decompressor;
-    }
-
-    public boolean parse(int streamId, short version, int length, ByteBuffer buffer)
-    {
-        // Need to be sure that all the compressed data has arrived
-        // Because SPDY uses SYNC_FLUSH mode, and the Java API
-        // does not expose when decompression is finished with this mode
-        // (but only when using NO_FLUSH), then we need to
-        // accumulate the compressed bytes until we have all of them
-
-        boolean accumulated = accumulate(length, buffer);
-        if (!accumulated)
-            return false;
-
-        byte[] compressedHeaders = data;
-        data = null;
-        ByteBuffer decompressedHeaders = decompress(version, compressedHeaders);
-
-        Charset iso1 = StandardCharsets.ISO_8859_1;
-        // We know the decoded bytes contain the full headers,
-        // so optimize instead of looping byte by byte
-        int count = readCount(version, decompressedHeaders);
-        for (int i = 0; i < count; ++i)
-        {
-            int nameLength = readNameLength(version, decompressedHeaders);
-            if (nameLength == 0)
-                throw new StreamException(streamId, StreamStatus.PROTOCOL_ERROR, "Invalid header name length");
-            byte[] nameBytes = new byte[nameLength];
-            decompressedHeaders.get(nameBytes);
-            String name = new String(nameBytes, iso1);
-
-            int valueLength = readValueLength(version, decompressedHeaders);
-            if (valueLength == 0)
-                throw new StreamException(streamId, StreamStatus.PROTOCOL_ERROR, "Invalid header value length");
-            byte[] valueBytes = new byte[valueLength];
-            decompressedHeaders.get(valueBytes);
-            String value = new String(valueBytes, iso1);
-            // Multi valued headers are separate by NUL
-            String[] values = value.split("\u0000");
-            // Check if there are multiple NULs (section 2.6.9)
-            for (String v : values)
-                if (v.length() == 0)
-                    throw new StreamException(streamId, StreamStatus.PROTOCOL_ERROR, "Invalid multi valued header");
-
-            onHeader(name, values);
-        }
-
-        return true;
-    }
-
-    private boolean accumulate(int length, ByteBuffer buffer)
-    {
-        int remaining = buffer.remaining();
-        if (data == null)
-        {
-            if (remaining < length)
-            {
-                data = new byte[remaining];
-                buffer.get(data);
-                return false;
-            }
-            else
-            {
-                data = new byte[length];
-                buffer.get(data);
-                return true;
-            }
-        }
-        else
-        {
-            int accumulated = data.length;
-            int needed = length - accumulated;
-            if (remaining < needed)
-            {
-                byte[] local = Arrays.copyOf(data,accumulated + remaining);
-                buffer.get(local, accumulated, remaining);
-                data = local;
-                return false;
-            }
-            else
-            {
-                byte[] local = Arrays.copyOf(data,length);
-                buffer.get(local, accumulated, needed);
-                data = local;
-                return true;
-            }
-        }
-    }
-
-    private int readCount(int version, ByteBuffer buffer)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-                return buffer.getShort();
-            case SPDY.V3:
-                return buffer.getInt();
-            default:
-                throw new IllegalStateException();
-        }
-    }
-
-    private int readNameLength(int version, ByteBuffer buffer)
-    {
-        return readCount(version, buffer);
-    }
-
-    private int readValueLength(int version, ByteBuffer buffer)
-    {
-        return readCount(version, buffer);
-    }
-
-    protected abstract void onHeader(String name, String[] values);
-
-    private ByteBuffer decompress(short version, byte[] compressed)
-    {
-        // Differently from compression, decompression always happens
-        // non-concurrently because we read and parse with a single
-        // thread, and therefore there is no need for synchronization.
-
-        try
-        {
-            byte[] decompressed = null;
-            byte[] buffer = new byte[compressed.length * 2];
-            decompressor.setInput(compressed);
-
-            while (true)
-            {
-                int count = decompressor.decompress(buffer);
-                if (count == 0)
-                {
-                    if (decompressed != null)
-                    {
-                        return ByteBuffer.wrap(decompressed);
-                    }
-                    else if (needsDictionary)
-                    {
-                        decompressor.setDictionary(CompressionDictionary.get(version));
-                        needsDictionary = false;
-                    }
-                    else
-                    {
-                        throw new IllegalStateException();
-                    }
-                }
-                else
-                {
-                    if (count < buffer.length)
-                    {
-                        if (decompressed == null)
-                        {
-                            // Only one pass was needed to decompress
-                            return ByteBuffer.wrap(buffer, 0, count);
-                        }
-                        else
-                        {
-                            // Last pass needed to decompress, merge decompressed bytes
-                            byte[] result = Arrays.copyOf(decompressed,decompressed.length+count);
-                            System.arraycopy(buffer, 0, result, decompressed.length, count);
-                            return ByteBuffer.wrap(result);
-                        }
-                    }
-                    else
-                    {
-                        if (decompressed == null)
-                        {
-                            decompressed = buffer;
-                            buffer = new byte[buffer.length];
-                        }
-                        else
-                        {
-                            byte[] result = Arrays.copyOf(decompressed,decompressed.length+buffer.length);
-                            System.arraycopy(buffer, 0, result, decompressed.length, buffer.length);
-                            decompressed = result;
-                        }
-                    }
-                }
-            }
-        }
-        catch (ZipException x)
-        {
-            // We had a compression problem, and since the compression context
-            // is per-connection, we need to tear down the connection
-            throw new SessionException(SessionStatus.PROTOCOL_ERROR, x);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBodyParser.java
deleted file mode 100644
index 75a56e9..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBodyParser.java
+++ /dev/null
@@ -1,173 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.spdy.CompressionFactory;
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.frames.ControlFrameType;
-import org.eclipse.jetty.spdy.frames.HeadersFrame;
-import org.eclipse.jetty.util.Fields;
-
-public class HeadersBodyParser extends ControlFrameBodyParser
-{
-    private final Fields headers = new Fields();
-    private final ControlFrameParser controlFrameParser;
-    private final HeadersBlockParser headersBlockParser;
-    private State state = State.STREAM_ID;
-    private int cursor;
-    private int streamId;
-
-    public HeadersBodyParser(CompressionFactory.Decompressor decompressor, ControlFrameParser controlFrameParser)
-    {
-        this.controlFrameParser = controlFrameParser;
-        this.headersBlockParser = new HeadersHeadersBlockParser(decompressor);
-    }
-
-    @Override
-    public boolean parse(ByteBuffer buffer)
-    {
-        while (buffer.hasRemaining())
-        {
-            switch (state)
-            {
-                case STREAM_ID:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        streamId = buffer.getInt() & 0x7F_FF_FF_FF;
-                        state = State.ADDITIONAL;
-                    }
-                    else
-                    {
-                        state = State.STREAM_ID_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case STREAM_ID_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    streamId += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        streamId &= 0x7F_FF_FF_FF;
-                        state = State.ADDITIONAL;
-                    }
-                    break;
-                }
-                case ADDITIONAL:
-                {
-                    switch (controlFrameParser.getVersion())
-                    {
-                        case SPDY.V2:
-                        {
-                            if (buffer.remaining() >= 2)
-                            {
-                                buffer.getShort();
-                                state = State.HEADERS;
-                            }
-                            else
-                            {
-                                state = State.ADDITIONAL_BYTES;
-                                cursor = 2;
-                            }
-                            break;
-                        }
-                        case SPDY.V3:
-                        {
-                            state = State.HEADERS;
-                            break;
-                        }
-                        default:
-                        {
-                            throw new IllegalStateException();
-                        }
-                    }
-                    break;
-                }
-                case ADDITIONAL_BYTES:
-                {
-                    assert controlFrameParser.getVersion() == SPDY.V2;
-                    buffer.get();
-                    --cursor;
-                    if (cursor == 0)
-                        state = State.HEADERS;
-                    break;
-                }
-                case HEADERS:
-                {
-                    short version = controlFrameParser.getVersion();
-                    int length = controlFrameParser.getLength() - 4;
-                    if (version == SPDY.V2)
-                        length -= 2;
-                    if (headersBlockParser.parse(streamId, version, length, buffer))
-                    {
-                        byte flags = controlFrameParser.getFlags();
-                        if (flags != 0 && flags != HeadersInfo.FLAG_CLOSE && flags != HeadersInfo.FLAG_RESET_COMPRESSION)
-                            throw new IllegalArgumentException("Invalid flag " + flags + " for frame " + ControlFrameType.HEADERS);
-
-                        HeadersFrame frame = new HeadersFrame(version, flags, streamId, new Fields(headers, true));
-                        controlFrameParser.onControlFrame(frame);
-
-                        reset();
-                        return true;
-                    }
-                    break;
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-        }
-        return false;
-    }
-
-    private void reset()
-    {
-        headers.clear();
-        state = State.STREAM_ID;
-        cursor = 0;
-        streamId = 0;
-    }
-
-    private enum State
-    {
-        STREAM_ID, STREAM_ID_BYTES, ADDITIONAL, ADDITIONAL_BYTES, HEADERS
-    }
-
-    private class HeadersHeadersBlockParser extends HeadersBlockParser
-    {
-        public HeadersHeadersBlockParser(CompressionFactory.Decompressor decompressor)
-        {
-            super(decompressor);
-        }
-
-        @Override
-        protected void onHeader(String name, String[] values)
-        {
-            for (String value : values)
-                headers.add(name, value);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/NoOpBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/NoOpBodyParser.java
deleted file mode 100644
index fb733d9..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/NoOpBodyParser.java
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.spdy.frames.NoOpFrame;
-
-public class NoOpBodyParser extends ControlFrameBodyParser
-{
-    private final ControlFrameParser controlFrameParser;
-
-    public NoOpBodyParser(ControlFrameParser controlFrameParser)
-    {
-        this.controlFrameParser = controlFrameParser;
-    }
-
-    @Override
-    public boolean parse(ByteBuffer buffer)
-    {
-        NoOpFrame frame = new NoOpFrame();
-        controlFrameParser.onControlFrame(frame);
-        return true;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/Parser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/Parser.java
deleted file mode 100644
index 666c72a..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/Parser.java
+++ /dev/null
@@ -1,240 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-import java.util.EventListener;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import org.eclipse.jetty.spdy.CompressionFactory;
-import org.eclipse.jetty.spdy.SessionException;
-import org.eclipse.jetty.spdy.StreamException;
-import org.eclipse.jetty.spdy.api.SessionStatus;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.DataFrame;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-public class Parser
-{
-    private static final Logger logger = Log.getLogger(Parser.class);
-    private final List<Listener> listeners = new CopyOnWriteArrayList<>();
-    private final ControlFrameParser controlFrameParser;
-    private final DataFrameParser dataFrameParser;
-    private State state = State.CONTROL_BIT;
-
-    public Parser(CompressionFactory.Decompressor decompressor)
-    {
-        // It is important to allocate one decompression context per
-        // SPDY session for the control frames (to decompress the headers)
-        controlFrameParser = new ControlFrameParser(decompressor)
-        {
-            @Override
-            protected void onControlFrame(ControlFrame frame)
-            {
-                logger.debug("Parsed {}", frame);
-                notifyControlFrame(frame);
-            }
-        };
-        dataFrameParser = new DataFrameParser()
-        {
-            @Override
-            protected void onDataFrame(DataFrame frame, ByteBuffer data)
-            {
-                logger.debug("Parsed {}, {} data bytes", frame, data.remaining());
-                notifyDataFrame(frame, data);
-            }
-        };
-    }
-
-    public void addListener(Listener listener)
-    {
-        listeners.add(listener);
-    }
-
-    public void removeListener(Listener listener)
-    {
-        listeners.remove(listener);
-    }
-
-    protected void notifyControlFrame(ControlFrame frame)
-    {
-        for (Listener listener : listeners)
-        {
-            try
-            {
-                listener.onControlFrame(frame);
-            }
-            catch (Exception x)
-            {
-                logger.info("Exception while notifying listener " + listener, x);
-            }
-        }
-    }
-
-    protected void notifyDataFrame(DataFrame frame, ByteBuffer data)
-    {
-        for (Listener listener : listeners)
-        {
-            try
-            {
-                listener.onDataFrame(frame, data);
-            }
-            catch (Exception x)
-            {
-                logger.info("Exception while notifying listener " + listener, x);
-            }
-        }
-    }
-
-    protected void notifyStreamException(StreamException x)
-    {
-        for (Listener listener : listeners)
-        {
-            try
-            {
-                listener.onStreamException(x);
-            }
-            catch (Exception xx)
-            {
-                logger.debug("Could not notify listener " + listener, xx);
-            }
-        }
-    }
-
-    protected void notifySessionException(SessionException x)
-    {
-        logger.debug("SPDY session exception", x);
-        for (Listener listener : listeners)
-        {
-            try
-            {
-                listener.onSessionException(x);
-            }
-            catch (Exception xx)
-            {
-                logger.debug("Could not notify listener " + listener, xx);
-            }
-        }
-    }
-
-    public void parse(ByteBuffer buffer)
-    {
-        logger.debug("Parsing {} bytes", buffer.remaining());
-        try
-        {
-            while (buffer.hasRemaining())
-            {
-                try
-                {
-                    switch (state)
-                    {
-                        case CONTROL_BIT:
-                        {
-                            // We must only peek the first byte and not advance the buffer
-                            // because the 7 least significant bits may be relevant in data frames
-                            int currByte = buffer.get(buffer.position());
-                            boolean isControlFrame = (currByte & 0x80) == 0x80;
-                            state = isControlFrame ? State.CONTROL_FRAME : State.DATA_FRAME;
-                            break;
-                        }
-                        case CONTROL_FRAME:
-                        {
-                            if (controlFrameParser.parse(buffer))
-                                reset();
-                            break;
-                        }
-                        case DATA_FRAME:
-                        {
-                            if (dataFrameParser.parse(buffer))
-                                reset();
-                            break;
-                        }
-                        default:
-                        {
-                            throw new IllegalStateException();
-                        }
-                    }
-                }
-                catch (StreamException x)
-                {
-                    notifyStreamException(x);
-                }
-            }
-        }
-        catch (SessionException x)
-        {
-            notifySessionException(x);
-        }
-        catch (Throwable x)
-        {
-            notifySessionException(new SessionException(SessionStatus.PROTOCOL_ERROR, x));
-        }
-        finally
-        {
-            // Be sure to consume after exceptions
-            buffer.position(buffer.limit());
-        }
-    }
-
-    private void reset()
-    {
-        state = State.CONTROL_BIT;
-    }
-
-    public interface Listener extends EventListener
-    {
-        public void onControlFrame(ControlFrame frame);
-
-        public void onDataFrame(DataFrame frame, ByteBuffer data);
-
-        public void onStreamException(StreamException x);
-
-        public void onSessionException(SessionException x);
-
-        public static class Adapter implements Listener
-        {
-            @Override
-            public void onControlFrame(ControlFrame frame)
-            {
-            }
-
-            @Override
-            public void onDataFrame(DataFrame frame, ByteBuffer data)
-            {
-            }
-
-            @Override
-            public void onStreamException(StreamException x)
-            {
-            }
-
-            @Override
-            public void onSessionException(SessionException x)
-            {
-            }
-        }
-    }
-
-    private enum State
-    {
-        CONTROL_BIT, CONTROL_FRAME, DATA_FRAME
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/PingBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/PingBodyParser.java
deleted file mode 100644
index d28182a..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/PingBodyParser.java
+++ /dev/null
@@ -1,98 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.spdy.frames.PingFrame;
-
-public class PingBodyParser extends ControlFrameBodyParser
-{
-    private final ControlFrameParser controlFrameParser;
-    private State state = State.PING_ID;
-    private int cursor;
-    private int pingId;
-
-    public PingBodyParser(ControlFrameParser controlFrameParser)
-    {
-        this.controlFrameParser = controlFrameParser;
-    }
-
-    @Override
-    public boolean parse(ByteBuffer buffer)
-    {
-        while (buffer.hasRemaining())
-        {
-            switch (state)
-            {
-                case PING_ID:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        pingId = buffer.getInt() & 0x7F_FF_FF_FF;
-                        onPing();
-                        return true;
-                    }
-                    else
-                    {
-                        state = State.PING_ID_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case PING_ID_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    pingId += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        onPing();
-                        return true;
-                    }
-                    break;
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-        }
-        return false;
-    }
-
-    private void onPing()
-    {
-        PingFrame frame = new PingFrame(controlFrameParser.getVersion(), pingId);
-        controlFrameParser.onControlFrame(frame);
-        reset();
-    }
-
-    private void reset()
-    {
-        state = State.PING_ID;
-        cursor = 0;
-        pingId = 0;
-    }
-
-    private enum State
-    {
-        PING_ID, PING_ID_BYTES
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/RstStreamBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/RstStreamBodyParser.java
deleted file mode 100644
index 0781770..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/RstStreamBodyParser.java
+++ /dev/null
@@ -1,127 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.spdy.frames.RstStreamFrame;
-
-public class RstStreamBodyParser extends ControlFrameBodyParser
-{
-    private final ControlFrameParser controlFrameParser;
-    private State state = State.STREAM_ID;
-    private int cursor;
-    private int streamId;
-    private int statusCode;
-
-    public RstStreamBodyParser(ControlFrameParser controlFrameParser)
-    {
-        this.controlFrameParser = controlFrameParser;
-    }
-
-    @Override
-    public boolean parse(ByteBuffer buffer)
-    {
-        while (buffer.hasRemaining())
-        {
-            switch (state)
-            {
-                case STREAM_ID:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        streamId = buffer.getInt() & 0x7F_FF_FF_FF;
-                        state = State.STATUS_CODE;
-                    }
-                    else
-                    {
-                        state = State.STREAM_ID_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case STREAM_ID_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    streamId += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        streamId &= 0x7F_FF_FF_FF;
-                        state = State.STATUS_CODE;
-                    }
-                    break;
-                }
-                case STATUS_CODE:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        statusCode = buffer.getInt();
-                        onRstStream();
-                        return true;
-                    }
-                    else
-                    {
-                        state = State.STATUS_CODE_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case STATUS_CODE_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    statusCode += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        onRstStream();
-                        return true;
-                    }
-                    break;
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-        }
-        return false;
-    }
-
-    private void onRstStream()
-    {
-        // TODO: check that statusCode is not 0
-        RstStreamFrame frame = new RstStreamFrame(controlFrameParser.getVersion(), streamId, statusCode);
-        controlFrameParser.onControlFrame(frame);
-        reset();
-    }
-
-    private void reset()
-    {
-        state = State.STREAM_ID;
-        cursor = 0;
-        streamId = 0;
-        statusCode = 0;
-    }
-
-    private enum State
-    {
-        STREAM_ID, STREAM_ID_BYTES, STATUS_CODE, STATUS_CODE_BYTES
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SettingsBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SettingsBodyParser.java
deleted file mode 100644
index cb38620..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SettingsBodyParser.java
+++ /dev/null
@@ -1,200 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Settings;
-import org.eclipse.jetty.spdy.frames.SettingsFrame;
-
-public class SettingsBodyParser extends ControlFrameBodyParser
-{
-    private final Settings settings = new Settings();
-    private final ControlFrameParser controlFrameParser;
-    private State state = State.COUNT;
-    private int cursor;
-    private int count;
-    private int idAndFlags;
-    private int value;
-
-    public SettingsBodyParser(ControlFrameParser controlFrameParser)
-    {
-        this.controlFrameParser = controlFrameParser;
-    }
-
-    @Override
-    public boolean parse(ByteBuffer buffer)
-    {
-        while (buffer.hasRemaining())
-        {
-            switch (state)
-            {
-                case COUNT:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        count = buffer.getInt();
-                        state = State.ID_FLAGS;
-                    }
-                    else
-                    {
-                        state = State.COUNT_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case COUNT_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    count += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                        state = State.ID_FLAGS;
-                    break;
-                }
-                case ID_FLAGS:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        idAndFlags = convertIdAndFlags(controlFrameParser.getVersion(), buffer.getInt());
-                        state = State.VALUE;
-                    }
-                    else
-                    {
-                        state = State.ID_FLAGS_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case ID_FLAGS_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    value += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        idAndFlags = convertIdAndFlags(controlFrameParser.getVersion(), value);
-                        state = State.VALUE;
-                    }
-                    break;
-                }
-                case VALUE:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        value = buffer.getInt();
-                        if (onPair())
-                            return true;
-                    }
-                    else
-                    {
-                        state = State.VALUE_BYTES;
-                        cursor = 4;
-                        value = 0;
-                    }
-                    break;
-                }
-                case VALUE_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    value += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        if (onPair())
-                            return true;
-                    }
-                    break;
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-        }
-        return false;
-    }
-
-    private int convertIdAndFlags(short version, int idAndFlags)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-            {
-                // A bug in the Chromium implementation forces v2 to have
-                // 3 ID bytes little endian + 1 byte of flags
-                // Here we normalize this to conform with v3, which is
-                // 1 bytes of flag + 3 ID bytes big endian
-                int result = (idAndFlags & 0x00_00_00_FF) << 24;
-                result += (idAndFlags & 0x00_00_FF_00) << 8;
-                result += (idAndFlags & 0x00_FF_00_00) >>> 8;
-                result += (idAndFlags & 0xFF_00_00_00) >>> 24;
-                return result;
-            }
-            case SPDY.V3:
-            {
-                return idAndFlags;
-            }
-            default:
-            {
-                throw new IllegalStateException();
-            }
-        }
-    }
-
-    private boolean onPair()
-    {
-        int id = idAndFlags & 0x00_FF_FF_FF;
-        byte flags = (byte)((idAndFlags & 0xFF_00_00_00) >>> 24);
-        settings.put(new Settings.Setting(Settings.ID.from(id), Settings.Flag.from(flags), value));
-        state = State.ID_FLAGS;
-        idAndFlags = 0;
-        value = 0;
-        --count;
-        if (count == 0)
-        {
-            onSettings();
-            return true;
-        }
-        return false;
-    }
-
-    private void onSettings()
-    {
-        SettingsFrame frame = new SettingsFrame(controlFrameParser.getVersion(), controlFrameParser.getFlags(), new Settings(settings, true));
-        controlFrameParser.onControlFrame(frame);
-        reset();
-    }
-
-    private void reset()
-    {
-        settings.clear();
-        state = State.COUNT;
-        cursor = 0;
-        count = 0;
-        idAndFlags = 0;
-        value = 0;
-    }
-
-    private enum State
-    {
-        COUNT, COUNT_BYTES, ID_FLAGS, ID_FLAGS_BYTES, VALUE, VALUE_BYTES
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynReplyBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynReplyBodyParser.java
deleted file mode 100644
index 4b47b92..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynReplyBodyParser.java
+++ /dev/null
@@ -1,184 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.spdy.CompressionFactory;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.frames.ControlFrameType;
-import org.eclipse.jetty.spdy.frames.SynReplyFrame;
-import org.eclipse.jetty.util.Fields;
-
-public class SynReplyBodyParser extends ControlFrameBodyParser
-{
-    private final Fields headers = new Fields();
-    private final ControlFrameParser controlFrameParser;
-    private final HeadersBlockParser headersBlockParser;
-    private State state = State.STREAM_ID;
-    private int cursor;
-    private int streamId;
-
-    public SynReplyBodyParser(CompressionFactory.Decompressor decompressor, ControlFrameParser controlFrameParser)
-    {
-        this.controlFrameParser = controlFrameParser;
-        this.headersBlockParser = new SynReplyHeadersBlockParser(decompressor);
-    }
-
-    @Override
-    public boolean parse(ByteBuffer buffer)
-    {
-        while (buffer.hasRemaining())
-        {
-            switch (state)
-            {
-                case STREAM_ID:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        streamId = buffer.getInt() & 0x7F_FF_FF_FF;
-                        state = State.ADDITIONAL;
-                    }
-                    else
-                    {
-                        state = State.STREAM_ID_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case STREAM_ID_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    streamId += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        streamId &= 0x7F_FF_FF_FF;
-                        state = State.ADDITIONAL;
-                    }
-                    break;
-                }
-                case ADDITIONAL:
-                {
-                    switch (controlFrameParser.getVersion())
-                    {
-                        case SPDY.V2:
-                        {
-                            if (buffer.remaining() >= 2)
-                            {
-                                buffer.getShort();
-                                state = State.HEADERS;
-                            }
-                            else
-                            {
-                                state = State.ADDITIONAL_BYTES;
-                                cursor = 2;
-                            }
-                            break;
-                        }
-                        case SPDY.V3:
-                        {
-                            state = State.HEADERS;
-                            break;
-                        }
-                        default:
-                        {
-                            throw new IllegalStateException();
-                        }
-                    }
-                    break;
-                }
-                case ADDITIONAL_BYTES:
-                {
-                    assert controlFrameParser.getVersion() == SPDY.V2;
-                    buffer.get();
-                    --cursor;
-                    if (cursor == 0)
-                        state = State.HEADERS;
-                    break;
-                }
-                case HEADERS:
-                {
-                    short version = controlFrameParser.getVersion();
-                    int length = controlFrameParser.getLength() - getSynReplyDataLength(version);
-                    if (headersBlockParser.parse(streamId, version, length, buffer))
-                    {
-                        byte flags = controlFrameParser.getFlags();
-                        if (flags != 0 && flags != ReplyInfo.FLAG_CLOSE)
-                            throw new IllegalArgumentException("Invalid flag " + flags + " for frame " + ControlFrameType.SYN_REPLY);
-
-                        SynReplyFrame frame = new SynReplyFrame(version, flags, streamId, new Fields(headers, true));
-                        controlFrameParser.onControlFrame(frame);
-
-                        reset();
-                        return true;
-                    }
-                    break;
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-        }
-        return false;
-    }
-
-    private int getSynReplyDataLength(short version)
-    {
-        switch (version)
-        {
-            case 2:
-                return 6;
-            case 3:
-                return 4;
-            default:
-                throw new IllegalStateException();
-        }
-    }
-
-    private void reset()
-    {
-        headers.clear();
-        state = State.STREAM_ID;
-        cursor = 0;
-        streamId = 0;
-    }
-
-    private enum State
-    {
-        STREAM_ID, STREAM_ID_BYTES, ADDITIONAL, ADDITIONAL_BYTES, HEADERS
-    }
-
-    private class SynReplyHeadersBlockParser extends HeadersBlockParser
-    {
-        public SynReplyHeadersBlockParser(CompressionFactory.Decompressor decompressor)
-        {
-            super(decompressor);
-        }
-
-        @Override
-        protected void onHeader(String name, String[] values)
-        {
-            for (String value : values)
-                headers.add(name, value);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynStreamBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynStreamBodyParser.java
deleted file mode 100644
index b2f1be9..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/SynStreamBodyParser.java
+++ /dev/null
@@ -1,236 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.spdy.CompressionFactory;
-import org.eclipse.jetty.spdy.PushSynInfo;
-import org.eclipse.jetty.spdy.StreamException;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.frames.ControlFrameType;
-import org.eclipse.jetty.spdy.frames.SynStreamFrame;
-import org.eclipse.jetty.util.Fields;
-
-public class SynStreamBodyParser extends ControlFrameBodyParser
-{
-    private final Fields headers = new Fields();
-    private final ControlFrameParser controlFrameParser;
-    private final HeadersBlockParser headersBlockParser;
-    private State state = State.STREAM_ID;
-    private int cursor;
-    private int streamId;
-    private int associatedStreamId;
-    private byte priority;
-    private short slot;
-
-    public SynStreamBodyParser(CompressionFactory.Decompressor decompressor, ControlFrameParser controlFrameParser)
-    {
-        this.controlFrameParser = controlFrameParser;
-        this.headersBlockParser = new SynStreamHeadersBlockParser(decompressor);
-    }
-
-    @Override
-    public boolean parse(ByteBuffer buffer)
-    {
-        while (buffer.hasRemaining())
-        {
-            switch (state)
-            {
-                case STREAM_ID:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        streamId = buffer.getInt() & 0x7F_FF_FF_FF;
-                        state = State.ASSOCIATED_STREAM_ID;
-                    }
-                    else
-                    {
-                        state = State.STREAM_ID_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case STREAM_ID_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    streamId += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        streamId &= 0x7F_FF_FF_FF;
-                        state = State.ASSOCIATED_STREAM_ID;
-                    }
-                    break;
-                }
-                case ASSOCIATED_STREAM_ID:
-                {
-                    // Now we know the streamId, we can do the version check
-                    // and if it is wrong, issue a RST_STREAM
-                    try
-                    {
-                        checkVersion(controlFrameParser.getVersion(), streamId);
-                    }
-                    catch (StreamException e)
-                    {
-                        // We've already read 4 bytes of the streamId which are part of controlFrameParser.getLength
-                        // so we need to substract those from the bytesToSkip.
-                        int bytesToSkip = controlFrameParser.getLength() - 4;
-                        int remaining = buffer.remaining();
-                        if (remaining >= bytesToSkip)
-                        {
-                            buffer.position(buffer.position() + bytesToSkip);
-                            controlFrameParser.reset();
-                            reset();
-                        }
-                        else
-                        {
-                            int bytesToSkipInNextBuffer = bytesToSkip - remaining;
-                            buffer.position(buffer.limit());
-                            controlFrameParser.skip(bytesToSkipInNextBuffer);
-                            reset();
-                        }
-                        throw e;
-                    }
-                    if (buffer.remaining() >= 4)
-                    {
-                        associatedStreamId = buffer.getInt() & 0x7F_FF_FF_FF;
-                        state = State.PRIORITY;
-                    }
-                    else
-                    {
-                        state = State.ASSOCIATED_STREAM_ID_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case ASSOCIATED_STREAM_ID_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    associatedStreamId += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        associatedStreamId &= 0x7F_FF_FF_FF;
-                        state = State.PRIORITY;
-                    }
-                    break;
-                }
-                case PRIORITY:
-                {
-                    byte currByte = buffer.get();
-                    ++cursor;
-                    if (cursor == 1)
-                    {
-                        priority = readPriority(controlFrameParser.getVersion(), currByte);
-                    }
-                    else
-                    {
-                        slot = (short)(currByte & 0xFF);
-                        cursor = 0;
-                        state = State.HEADERS;
-                    }
-                    break;
-                }
-                case HEADERS:
-                {
-                    short version = controlFrameParser.getVersion();
-                    int length = controlFrameParser.getLength() - 10;
-                    if (headersBlockParser.parse(streamId, version, length, buffer))
-                    {
-                        byte flags = controlFrameParser.getFlags();
-                        if (flags > (SynInfo.FLAG_CLOSE | PushSynInfo.FLAG_UNIDIRECTIONAL))
-                            throw new IllegalArgumentException("Invalid flag " + flags + " for frame " +
-                                    ControlFrameType.SYN_STREAM);
-
-                        SynStreamFrame frame = new SynStreamFrame(version, flags, streamId, associatedStreamId,
-                                priority, slot, new Fields(headers, false));
-                        controlFrameParser.onControlFrame(frame);
-
-                        reset();
-                        return true;
-                    }
-                    break;
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-        }
-        return false;
-    }
-
-    private void checkVersion(short version, int streamId)
-    {
-        if (version != SPDY.V2 && version != SPDY.V3)
-            throw new StreamException(streamId, StreamStatus.UNSUPPORTED_VERSION);
-    }
-
-    private byte readPriority(short version, byte currByte)
-    {
-        // Right shift retains the sign bit when operated on a byte,
-        // so we use an int to perform the shifts
-        switch (version)
-        {
-            case SPDY.V2:
-                int p2 = currByte & 0b1100_0000;
-                p2 >>>= 6;
-                return (byte)p2;
-            case SPDY.V3:
-                int p3 = currByte & 0b1110_0000;
-                p3 >>>= 5;
-                return (byte)p3;
-            default:
-                throw new IllegalStateException();
-        }
-    }
-
-    private void reset()
-    {
-        headers.clear();
-        state = State.STREAM_ID;
-        cursor = 0;
-        streamId = 0;
-        associatedStreamId = 0;
-        priority = 0;
-    }
-
-    private enum State
-    {
-        STREAM_ID, STREAM_ID_BYTES, ASSOCIATED_STREAM_ID, ASSOCIATED_STREAM_ID_BYTES, PRIORITY, HEADERS
-    }
-
-    private class SynStreamHeadersBlockParser extends HeadersBlockParser
-    {
-        public SynStreamHeadersBlockParser(CompressionFactory.Decompressor decompressor)
-        {
-            super(decompressor);
-        }
-
-        @Override
-        protected void onHeader(String name, String[] values)
-        {
-            for (String value : values)
-                headers.add(name, value);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameBodyParser.java
deleted file mode 100644
index 76be70a..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameBodyParser.java
+++ /dev/null
@@ -1,72 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-
-public class UnknownControlFrameBodyParser extends ControlFrameBodyParser
-{
-    private final ControlFrameParser controlFrameParser;
-    private State state = State.BODY;
-    private int remaining;
-
-    public UnknownControlFrameBodyParser(ControlFrameParser controlFrameParser)
-    {
-        this.controlFrameParser = controlFrameParser;
-    }
-
-    @Override
-    public boolean parse(ByteBuffer buffer)
-    {
-        switch (state)
-        {
-            case BODY:
-            {
-                remaining = controlFrameParser.getLength();
-                state = State.CONSUME;
-                // Fall down
-            }
-            case CONSUME:
-            {
-                int consume = Math.min(remaining, buffer.remaining());
-                buffer.position(buffer.position() + consume);
-                remaining -= consume;
-                if (remaining > 0)
-                    return false;
-                reset();
-                return true;
-            }
-            default:
-            {
-                throw new IllegalStateException();
-            }
-        }
-    }
-
-    private void reset()
-    {
-        state = State.BODY;
-        remaining = 0;
-    }
-
-    private enum State
-    {
-        BODY, CONSUME
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/WindowUpdateBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/WindowUpdateBodyParser.java
deleted file mode 100644
index 0882dd8..0000000
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/WindowUpdateBodyParser.java
+++ /dev/null
@@ -1,127 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
-
-public class WindowUpdateBodyParser extends ControlFrameBodyParser
-{
-    private final ControlFrameParser controlFrameParser;
-    private State state = State.STREAM_ID;
-    private int cursor;
-    private int streamId;
-    private int windowDelta;
-
-    public WindowUpdateBodyParser(ControlFrameParser controlFrameParser)
-    {
-        this.controlFrameParser = controlFrameParser;
-    }
-
-    @Override
-    public boolean parse(ByteBuffer buffer)
-    {
-        while (buffer.hasRemaining())
-        {
-            switch (state)
-            {
-                case STREAM_ID:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        streamId = buffer.getInt() & 0x7F_FF_FF_FF;
-                        state = State.WINDOW_DELTA;
-                    }
-                    else
-                    {
-                        state = State.STREAM_ID_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case STREAM_ID_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    streamId += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        streamId &= 0x7F_FF_FF_FF;
-                        state = State.WINDOW_DELTA;
-                    }
-                    break;
-                }
-                case WINDOW_DELTA:
-                {
-                    if (buffer.remaining() >= 4)
-                    {
-                        windowDelta = buffer.getInt() & 0x7F_FF_FF_FF;
-                        onWindowUpdate();
-                        return true;
-                    }
-                    else
-                    {
-                        state = State.WINDOW_DELTA_BYTES;
-                        cursor = 4;
-                    }
-                    break;
-                }
-                case WINDOW_DELTA_BYTES:
-                {
-                    byte currByte = buffer.get();
-                    --cursor;
-                    windowDelta += (currByte & 0xFF) << 8 * cursor;
-                    if (cursor == 0)
-                    {
-                        windowDelta &= 0x7F_FF_FF_FF;
-                        onWindowUpdate();
-                        return true;
-                    }
-                    break;
-                }
-                default:
-                {
-                    throw new IllegalStateException();
-                }
-            }
-        }
-        return false;
-    }
-
-    private void onWindowUpdate()
-    {
-        WindowUpdateFrame frame = new WindowUpdateFrame(controlFrameParser.getVersion(), streamId, windowDelta);
-        controlFrameParser.onControlFrame(frame);
-        reset();
-    }
-
-    private void reset()
-    {
-        state = State.STREAM_ID;
-        cursor = 0;
-        streamId = 0;
-        windowDelta = 0;
-    }
-
-    private enum State
-    {
-        STREAM_ID, STREAM_ID_BYTES, WINDOW_DELTA, WINDOW_DELTA_BYTES;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/AsyncTimeoutTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/AsyncTimeoutTest.java
deleted file mode 100644
index 086a792..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/AsyncTimeoutTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.io.ByteArrayEndPoint;
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.SPDYException;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.toolchain.test.AdvancedRunner;
-import org.eclipse.jetty.toolchain.test.annotation.Slow;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.thread.Scheduler;
-import org.eclipse.jetty.util.thread.TimerScheduler;
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AdvancedRunner.class)
-//TODO: Uncomment comment lines and reimplement tests to fit new design
-@Ignore("Doesn't work with new Flusher class, needs to be rewritten")
-public class AsyncTimeoutTest
-{
-    EndPoint endPoint = new ByteArrayEndPoint();
-
-    @Slow
-    @Test
-    public void testAsyncTimeoutInControlFrames() throws Exception
-    {
-        final long timeout = 1000;
-        final TimeUnit unit = TimeUnit.MILLISECONDS;
-
-        ByteBufferPool bufferPool = new MappedByteBufferPool();
-        Executor threadPool = Executors.newCachedThreadPool();
-        Scheduler scheduler = new TimerScheduler();
-        scheduler.start(); // TODO need to use jetty lifecycles better here
-        Generator generator = new Generator(bufferPool, new StandardCompressionFactory.StandardCompressor());
-        Session session = new StandardSession(SPDY.V2, bufferPool, scheduler, new TestController(),
-                endPoint, null, 1, null, generator, new FlowControlStrategy.None())
-        {
-//            @Override
-            public void flush()
-            {
-                try
-                {
-                    unit.sleep(2 * timeout);
-//                    super.flush();
-                }
-                catch (InterruptedException x)
-                {
-                    throw new SPDYException(x);
-                }
-            }
-        };
-
-        final CountDownLatch failedLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(timeout, unit, new Fields(), true, (byte)0), null, new Promise.Adapter<Stream>()
-        {
-            @Override
-            public void failed(Throwable x)
-            {
-                failedLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(failedLatch.await(2 * timeout, unit));
-    }
-
-    @Slow
-    @Test
-    public void testAsyncTimeoutInDataFrames() throws Exception
-    {
-        final long timeout = 1000;
-        final TimeUnit unit = TimeUnit.MILLISECONDS;
-
-        ByteBufferPool bufferPool = new MappedByteBufferPool();
-        Executor threadPool = Executors.newCachedThreadPool();
-        Scheduler scheduler = new TimerScheduler();
-        scheduler.start();
-        Generator generator = new Generator(bufferPool, new StandardCompressionFactory.StandardCompressor());
-        Session session = new StandardSession(SPDY.V2, bufferPool, scheduler, new TestController(),
-                endPoint, null, 1, null, generator, new FlowControlStrategy.None())
-        {
-//            @Override
-            protected void write(ByteBuffer buffer, Callback callback)
-            {
-                try
-                {
-                    // Wait if we're writing the data frame (control frame's first byte is 0x80)
-                    if (buffer.get(0) == 0)
-                        unit.sleep(2 * timeout);
-//                    super.write(buffer, callback);
-                }
-                catch (InterruptedException x)
-                {
-                    throw new SPDYException(x);
-                }
-            }
-        };
-
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), false, (byte)0), null);
-        final CountDownLatch failedLatch = new CountDownLatch(1);
-        stream.data(new StringDataInfo(timeout, unit, "data", true), new Callback.Adapter()
-        {
-            @Override
-            public void failed(Throwable x)
-            {
-                failedLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(failedLatch.await(2 * timeout, unit));
-    }
-
-    private static class TestController implements Controller
-    {
-        @Override
-        public void write(Callback callback, ByteBuffer... buffers)
-        {
-            callback.succeeded();
-        }
-
-        @Override
-        public void close(boolean onlyOutput)
-        {
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardSessionTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardSessionTest.java
deleted file mode 100644
index 00c93df..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardSessionTest.java
+++ /dev/null
@@ -1,681 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import java.nio.ByteBuffer;
-import java.nio.channels.ClosedChannelException;
-import java.util.HashSet;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Settings;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.frames.DataFrame;
-import org.eclipse.jetty.spdy.frames.SettingsFrame;
-import org.eclipse.jetty.spdy.frames.SynReplyFrame;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.FuturePromise;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
-import org.eclipse.jetty.util.thread.Scheduler;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.mockito.stubbing.Answer;
-
-import static org.hamcrest.Matchers.greaterThan;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@RunWith(MockitoJUnitRunner.class)
-public class StandardSessionTest
-{
-    private static final Logger LOG = Log.getLogger(StandardSessionTest.class);
-    private static final short VERSION = SPDY.V2;
-
-    @Mock
-    private Controller controller;
-
-    @Mock
-    private EndPoint endPoint;
-
-    private ExecutorService threadPool;
-    private StandardSession session;
-    private Scheduler scheduler;
-    private Fields headers;
-    private final ByteBufferPool bufferPool = new MappedByteBufferPool();
-    private final Generator generator = new Generator(bufferPool, new StandardCompressionFactory.StandardCompressor());
-
-    @Before
-    public void setUp() throws Exception
-    {
-        threadPool = Executors.newCachedThreadPool();
-        scheduler = new ScheduledExecutorScheduler();
-        scheduler.start();
-        session = new StandardSession(VERSION, bufferPool, scheduler, controller, endPoint, null, 1, null,
-                generator, new FlowControlStrategy.None());
-        when(endPoint.getIdleTimeout()).thenReturn(30000L);
-        headers = new Fields();
-    }
-
-    @After
-    public void after() throws Exception
-    {
-        scheduler.stop();
-        threadPool.shutdownNow();
-    }
-
-    @SuppressWarnings("unchecked")
-    private void setControllerWriteExpectation(final boolean fail)
-    {
-        doAnswer(new Answer()
-        {
-            public Object answer(InvocationOnMock invocation)
-            {
-                Object[] args = invocation.getArguments();
-                Callback callback = (Callback)args[0];
-                if (fail)
-                    callback.failed(new ClosedChannelException());
-                else
-                    callback.succeeded();
-                return null;
-            }
-        }).when(controller).write(any(Callback.class), any(ByteBuffer.class));
-    }
-
-    @Test
-    public void testStreamIsRemovedFromSessionWhenReset() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        IStream stream = createStream();
-        assertThatStreamIsInSession(stream);
-        assertThat("stream is not reset", stream.isReset(), is(false));
-        session.rst(new RstInfo(stream.getId(), StreamStatus.STREAM_ALREADY_CLOSED));
-        assertThatStreamIsNotInSession(stream);
-        assertThatStreamIsReset(stream);
-    }
-
-    @Test
-    public void testStreamIsAddedAndRemovedFromSession() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        IStream stream = createStream();
-        assertThatStreamIsInSession(stream);
-        stream.updateCloseState(true, true);
-        session.onControlFrame(new SynReplyFrame(VERSION, SynInfo.FLAG_CLOSE, stream.getId(), null));
-        assertThatStreamIsClosed(stream);
-        assertThatStreamIsNotInSession(stream);
-    }
-
-    @Test
-    public void testStreamIsRemovedWhenHeadersWithCloseFlagAreSent() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        IStream stream = createStream();
-        assertThatStreamIsInSession(stream);
-        stream.updateCloseState(true, false);
-        stream.headers(new HeadersInfo(headers, true));
-        assertThatStreamIsClosed(stream);
-        assertThatStreamIsNotInSession(stream);
-    }
-
-    @Test
-    public void testStreamIsUnidirectional() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        IStream stream = createStream();
-        assertThat("stream is not unidirectional", stream.isUnidirectional(), not(true));
-        Stream pushStream = createPushStream(stream);
-        assertThat("pushStream is unidirectional", pushStream.isUnidirectional(), is(true));
-    }
-
-    @Test
-    public void testPushStreamCreation() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        Stream stream = createStream();
-        IStream pushStream = createPushStream(stream);
-        assertThat("Push stream must be associated to the first stream created", pushStream.getAssociatedStream().getId(), is(stream.getId()));
-        assertThat("streamIds need to be monotonic", pushStream.getId(), greaterThan(stream.getId()));
-    }
-
-    @Test
-    public void testPushStreamIsNotClosedWhenAssociatedStreamIsClosed() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        IStream stream = createStream();
-        Stream pushStream = createPushStream(stream);
-        assertThatStreamIsNotHalfClosed(stream);
-        assertThatStreamIsNotClosed(stream);
-        assertThatPushStreamIsHalfClosed(pushStream);
-        assertThatPushStreamIsNotClosed(pushStream);
-
-        stream.updateCloseState(true, true);
-        assertThatStreamIsHalfClosed(stream);
-        assertThatStreamIsNotClosed(stream);
-        assertThatPushStreamIsHalfClosed(pushStream);
-        assertThatPushStreamIsNotClosed(pushStream);
-
-        session.onControlFrame(new SynReplyFrame(VERSION, SynInfo.FLAG_CLOSE, stream.getId(), null));
-        assertThatStreamIsClosed(stream);
-        assertThatPushStreamIsNotClosed(pushStream);
-    }
-
-    @Test
-    public void testCreatePushStreamOnClosedStream() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        IStream stream = createStream();
-        stream.updateCloseState(true, true);
-        assertThatStreamIsHalfClosed(stream);
-        stream.updateCloseState(true, false);
-        assertThatStreamIsClosed(stream);
-        createPushStreamAndMakeSureItFails(stream);
-    }
-
-    private void createPushStreamAndMakeSureItFails(IStream stream) throws InterruptedException
-    {
-        final CountDownLatch failedLatch = new CountDownLatch(1);
-        PushInfo pushInfo = new PushInfo(5, TimeUnit.SECONDS, headers, false);
-        stream.push(pushInfo, new Promise.Adapter<Stream>()
-        {
-            @Override
-            public void failed(Throwable x)
-            {
-                failedLatch.countDown();
-            }
-        });
-        assertThat("pushStream creation failed", failedLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testPushStreamIsAddedAndRemovedFromParentAndSessionWhenClosed() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        IStream stream = createStream();
-        IStream pushStream = createPushStream(stream);
-        assertThatPushStreamIsHalfClosed(pushStream);
-        assertThatPushStreamIsInSession(pushStream);
-        assertThatStreamIsAssociatedWithPushStream(stream, pushStream);
-        session.data(pushStream, new StringDataInfo("close", true), 5, TimeUnit.SECONDS, new Callback.Adapter());
-        assertThatPushStreamIsClosed(pushStream);
-        assertThatPushStreamIsNotInSession(pushStream);
-        assertThatStreamIsNotAssociatedWithPushStream(stream, pushStream);
-    }
-
-    @Test
-    public void testPushStreamIsRemovedWhenReset() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        IStream stream = createStream();
-        IStream pushStream = (IStream)stream.push(new PushInfo(new Fields(), false));
-        assertThatPushStreamIsInSession(pushStream);
-        session.rst(new RstInfo(pushStream.getId(), StreamStatus.INVALID_STREAM));
-        assertThatPushStreamIsNotInSession(pushStream);
-        assertThatStreamIsNotAssociatedWithPushStream(stream, pushStream);
-        assertThatStreamIsReset(pushStream);
-    }
-
-    @Test
-    public void testPushStreamWithSynInfoClosedTrue() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        IStream stream = createStream();
-        PushInfo pushInfo = new PushInfo(5, TimeUnit.SECONDS, headers, true);
-        IStream pushStream = (IStream)stream.push(pushInfo);
-        assertThatPushStreamIsHalfClosed(pushStream);
-        assertThatPushStreamIsClosed(pushStream);
-        assertThatStreamIsNotAssociatedWithPushStream(stream, pushStream);
-        assertThatStreamIsNotInSession(pushStream);
-    }
-
-    @Test
-    public void testPushStreamSendHeadersWithCloseFlagIsRemovedFromSessionAndDisassociateFromParent() throws InterruptedException, ExecutionException,
-            TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        IStream stream = createStream();
-        PushInfo pushInfo = new PushInfo(5, TimeUnit.SECONDS, headers, false);
-        IStream pushStream = (IStream)stream.push(pushInfo);
-        assertThatStreamIsAssociatedWithPushStream(stream, pushStream);
-        assertThatPushStreamIsInSession(pushStream);
-        pushStream.headers(new HeadersInfo(headers, true));
-        assertThatPushStreamIsNotInSession(pushStream);
-        assertThatPushStreamIsHalfClosed(pushStream);
-        assertThatPushStreamIsClosed(pushStream);
-        assertThatStreamIsNotAssociatedWithPushStream(stream, pushStream);
-    }
-
-    @Test
-    public void testCreatedAndClosedListenersAreCalledForNewStream() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        final CountDownLatch createdListenerCalledLatch = new CountDownLatch(1);
-        final CountDownLatch closedListenerCalledLatch = new CountDownLatch(1);
-        session.addListener(new TestStreamListener(createdListenerCalledLatch, closedListenerCalledLatch));
-        IStream stream = createStream();
-        session.onControlFrame(new SynReplyFrame(VERSION, (byte)0, stream.getId(), new Fields()));
-        session.onDataFrame(new DataFrame(stream.getId(), SynInfo.FLAG_CLOSE, 128), ByteBuffer.allocate(128));
-        stream.data(new StringDataInfo("close", true));
-        assertThat("onStreamCreated listener has been called", createdListenerCalledLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThatOnStreamClosedListenerHasBeenCalled(closedListenerCalledLatch);
-    }
-
-    @Test
-    public void testListenerIsCalledForResetStream() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        final CountDownLatch closedListenerCalledLatch = new CountDownLatch(1);
-        session.addListener(new TestStreamListener(null, closedListenerCalledLatch));
-        IStream stream = createStream();
-        session.rst(new RstInfo(stream.getId(), StreamStatus.CANCEL_STREAM));
-        assertThatOnStreamClosedListenerHasBeenCalled(closedListenerCalledLatch);
-    }
-
-    @Test
-    public void testCreatedAndClosedListenersAreCalledForNewPushStream() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        final CountDownLatch createdListenerCalledLatch = new CountDownLatch(2);
-        final CountDownLatch closedListenerCalledLatch = new CountDownLatch(1);
-        session.addListener(new TestStreamListener(createdListenerCalledLatch, closedListenerCalledLatch));
-        IStream stream = createStream();
-        IStream pushStream = createPushStream(stream);
-        session.data(pushStream, new StringDataInfo("close", true), 5, TimeUnit.SECONDS, new Callback.Adapter());
-        assertThat("onStreamCreated listener has been called twice. Once for the stream and once for the pushStream",
-                createdListenerCalledLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThatOnStreamClosedListenerHasBeenCalled(closedListenerCalledLatch);
-    }
-
-    @Test
-    public void testListenerIsCalledForResetPushStream() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        final CountDownLatch closedListenerCalledLatch = new CountDownLatch(1);
-        session.addListener(new TestStreamListener(null, closedListenerCalledLatch));
-        IStream stream = createStream();
-        IStream pushStream = createPushStream(stream);
-        session.rst(new RstInfo(pushStream.getId(), StreamStatus.CANCEL_STREAM));
-        assertThatOnStreamClosedListenerHasBeenCalled(closedListenerCalledLatch);
-    }
-
-    private class TestStreamListener extends Session.StreamListener.Adapter
-    {
-        private CountDownLatch createdListenerCalledLatch;
-        private CountDownLatch closedListenerCalledLatch;
-
-        public TestStreamListener(CountDownLatch createdListenerCalledLatch, CountDownLatch closedListenerCalledLatch)
-        {
-            this.createdListenerCalledLatch = createdListenerCalledLatch;
-            this.closedListenerCalledLatch = closedListenerCalledLatch;
-        }
-
-        @Override
-        public void onStreamCreated(Stream stream)
-        {
-            if (createdListenerCalledLatch != null)
-                createdListenerCalledLatch.countDown();
-            super.onStreamCreated(stream);
-        }
-
-        @Override
-        public void onStreamClosed(Stream stream)
-        {
-            if (closedListenerCalledLatch != null)
-                closedListenerCalledLatch.countDown();
-            super.onStreamClosed(stream);
-        }
-    }
-
-    @Test
-    @Ignore("In V3 we need to rst the stream if we receive data on a remotely half closed stream.")
-    public void receiveDataOnRemotelyHalfClosedStreamResetsStreamInV3() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        IStream stream = (IStream)session.syn(new SynInfo(new Fields(), false), new StreamFrameListener.Adapter());
-        stream.updateCloseState(true, false);
-        assertThat("stream is half closed from remote side", stream.isHalfClosed(), is(true));
-        stream.process(new ByteBufferDataInfo(ByteBuffer.allocate(256), true));
-    }
-
-    @Test
-    public void testReceiveDataOnRemotelyClosedStreamIsIgnored() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        final CountDownLatch onDataCalledLatch = new CountDownLatch(1);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), false, (byte)0),
-                new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        onDataCalledLatch.countDown();
-                        super.onData(stream, dataInfo);
-                    }
-                });
-        session.onControlFrame(new SynReplyFrame(VERSION, SynInfo.FLAG_CLOSE, stream.getId(), headers));
-        session.onDataFrame(new DataFrame(stream.getId(), (byte)0, 0), ByteBuffer.allocate(128));
-        assertThat("onData is never called", onDataCalledLatch.await(1, TimeUnit.SECONDS), not(true));
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testControllerWriteFails() throws Exception
-    {
-        final AtomicInteger writes = new AtomicInteger();
-        final AtomicBoolean fail = new AtomicBoolean();
-        Controller controller = new Controller()
-        {
-            @Override
-            public void write(Callback callback, ByteBuffer... buffers)
-            {
-                writes.incrementAndGet();
-                if (fail.get())
-                    callback.failed(new ClosedChannelException());
-                else
-                    callback.succeeded();
-            }
-
-            @Override
-            public void close(boolean onlyOutput)
-            {
-
-            }
-        };
-        ISession session = new StandardSession(VERSION, bufferPool, scheduler, controller, endPoint, null, 1, null, generator, null);
-        IStream stream = new StandardStream(1, (byte)0, session, null, scheduler, null);
-        stream.updateWindowSize(8192);
-
-        // Send a reply to comply with the API usage
-        stream.reply(new ReplyInfo(false), new Callback.Adapter());
-
-        // Make the controller fail
-        fail.set(true);
-        final CountDownLatch failedCalledLatch = new CountDownLatch(1);
-        Callback.Adapter callback = new Callback.Adapter()
-        {
-            @Override
-            public void failed(Throwable x)
-            {
-                failedCalledLatch.countDown();
-            }
-        };
-        // Data frame should fail on controller.write()
-        stream.data(new StringDataInfo(5, TimeUnit.SECONDS, "data", false), callback);
-
-        Assert.assertEquals(2, writes.get());
-        Assert.assertTrue(failedCalledLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testControlFramesAreStillSentForResetStreams() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        setControllerWriteExpectation(false);
-
-        // This is necessary to keep the compression context of Headers valid
-        IStream stream = createStream();
-        session.rst(new RstInfo(stream.getId(), StreamStatus.INVALID_STREAM));
-        stream.headers(new HeadersInfo(headers, true));
-
-        verify(controller, times(3)).write(any(Callback.class), any(ByteBuffer.class));
-    }
-
-    @Test
-    public void testMaxConcurrentStreams() throws InterruptedException
-    {
-        final CountDownLatch failedBecauseMaxConcurrentStreamsExceeded = new CountDownLatch(1);
-
-        Settings settings = new Settings();
-        settings.put(new Settings.Setting(Settings.ID.MAX_CONCURRENT_STREAMS, 0));
-        SettingsFrame settingsFrame = new SettingsFrame(VERSION, (byte)0, settings);
-        session.onControlFrame(settingsFrame);
-
-        PushSynInfo pushSynInfo = new PushSynInfo(1, new PushInfo(new Fields(), false));
-        session.syn(pushSynInfo, null, new Promise.Adapter<Stream>()
-        {
-            @Override
-            public void failed(Throwable x)
-            {
-                failedBecauseMaxConcurrentStreamsExceeded.countDown();
-            }
-        });
-
-        assertThat("Opening push stream failed because maxConcurrentStream is exceeded",
-                failedBecauseMaxConcurrentStreamsExceeded.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testHeaderFramesAreSentInTheOrderTheyAreCreated() throws ExecutionException,
-            TimeoutException, InterruptedException
-    {
-        testHeaderFramesAreSentInOrder((byte)0, (byte)0, (byte)0);
-    }
-
-    @Test
-    public void testHeaderFramesAreSentInTheOrderTheyAreCreatedWithPrioritization() throws ExecutionException,
-            TimeoutException, InterruptedException
-    {
-        testHeaderFramesAreSentInOrder((byte)0, (byte)1, (byte)2);
-    }
-
-    private void testHeaderFramesAreSentInOrder(final byte priority0, final byte priority1, final byte priority2) throws InterruptedException, ExecutionException
-    {
-        final StandardSession testLocalSession = new StandardSession(VERSION, bufferPool, scheduler,
-                new ControllerMock(), endPoint, null, 1, null, generator, new FlowControlStrategy.None());
-        HashSet<Future> tasks = new HashSet<>();
-
-        int numberOfTasksToRun = 128;
-        for (int i = 0; i < numberOfTasksToRun; i++)
-        {
-            tasks.add(threadPool.submit(new Runnable()
-            {
-
-                @Override
-                public void run()
-                {
-                    synStream(priority0);
-                    synStream(priority1);
-                    synStream(priority2);
-                }
-
-                private void synStream(byte priority)
-                {
-                    SynInfo synInfo = new SynInfo(headers, false, priority);
-                    testLocalSession.syn(synInfo, new StreamFrameListener.Adapter(), new FuturePromise<Stream>());
-                }
-            }));
-        }
-
-        for (Future task : tasks)
-        {
-            task.get();
-        }
-
-        threadPool.shutdown();
-        threadPool.awaitTermination(60, TimeUnit.SECONDS);
-    }
-
-    private class ControllerMock implements Controller
-    {
-        long lastStreamId = 0;
-
-        @Override
-        public void write(Callback callback, ByteBuffer... buffers)
-        {
-            StandardSession.FrameBytes frameBytes = (StandardSession.FrameBytes)callback;
-
-            int streamId = frameBytes.getStream().getId();
-            if (LOG.isDebugEnabled())
-                LOG.debug("last: {}, current: {}", lastStreamId, streamId);
-            if (lastStreamId < streamId)
-                lastStreamId = streamId;
-            else
-                throw new IllegalStateException("Last streamId: " + lastStreamId + " is not smaller than current StreamId: " +
-                        streamId);
-            frameBytes.succeeded();
-        }
-
-        @Override
-        public void close(boolean onlyOutput)
-        {
-        }
-    }
-
-    private IStream createStream() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        SynInfo synInfo = new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0);
-        return (IStream)session.syn(synInfo, new StreamFrameListener.Adapter());
-    }
-
-    private IStream createPushStream(Stream stream) throws InterruptedException, ExecutionException, TimeoutException
-    {
-        PushInfo pushInfo = new PushInfo(5, TimeUnit.SECONDS, headers, false);
-        return (IStream)stream.push(pushInfo);
-    }
-
-    private void assertThatStreamIsClosed(IStream stream)
-    {
-        assertThat("stream is closed", stream.isClosed(), is(true));
-    }
-
-    private void assertThatStreamIsReset(IStream stream)
-    {
-        assertThat("stream is reset", stream.isReset(), is(true));
-    }
-
-    private void assertThatStreamIsNotInSession(IStream stream)
-    {
-        assertThat("stream is not in session", session.getStreams().contains(stream), not(true));
-    }
-
-    private void assertThatStreamIsInSession(IStream stream)
-    {
-        assertThat("stream is in session", session.getStreams().contains(stream), is(true));
-    }
-
-    private void assertThatStreamIsNotClosed(IStream stream)
-    {
-        assertThat("stream is not closed", stream.isClosed(), not(true));
-    }
-
-    private void assertThatStreamIsNotHalfClosed(IStream stream)
-    {
-        assertThat("stream is not halfClosed", stream.isHalfClosed(), not(true));
-    }
-
-    private void assertThatPushStreamIsNotClosed(Stream pushStream)
-    {
-        assertThat("pushStream is not closed", pushStream.isClosed(), not(true));
-    }
-
-    private void assertThatStreamIsHalfClosed(IStream stream)
-    {
-        assertThat("stream is halfClosed", stream.isHalfClosed(), is(true));
-    }
-
-    private void assertThatStreamIsNotAssociatedWithPushStream(IStream stream, IStream pushStream)
-    {
-        assertThat("pushStream is removed from parent", stream.getPushedStreams().contains(pushStream), not(true));
-    }
-
-    private void assertThatPushStreamIsNotInSession(Stream pushStream)
-    {
-        assertThat("pushStream is not in session", session.getStreams().contains(pushStream), not(true));
-    }
-
-    private void assertThatPushStreamIsInSession(Stream pushStream)
-    {
-        assertThat("pushStream is in session", session.getStreams().contains(pushStream), is(true));
-    }
-
-    private void assertThatStreamIsAssociatedWithPushStream(IStream stream, Stream pushStream)
-    {
-        assertThat("stream is associated with pushStream", stream.getPushedStreams().contains(pushStream), is(true));
-    }
-
-    private void assertThatPushStreamIsClosed(Stream pushStream)
-    {
-        assertThat("pushStream is closed", pushStream.isClosed(), is(true));
-    }
-
-    private void assertThatPushStreamIsHalfClosed(Stream pushStream)
-    {
-        assertThat("pushStream is half closed ", pushStream.isHalfClosed(), is(true));
-    }
-
-    private void assertThatOnStreamClosedListenerHasBeenCalled(final CountDownLatch closedListenerCalledLatch) throws InterruptedException
-    {
-        assertThat("onStreamClosed listener has been called", closedListenerCalledLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardStreamTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardStreamTest.java
deleted file mode 100644
index 73a4263..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/StandardStreamTest.java
+++ /dev/null
@@ -1,258 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy;
-
-import java.nio.channels.ClosedChannelException;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.SynStreamFrame;
-import org.eclipse.jetty.toolchain.test.annotation.Slow;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@RunWith(MockitoJUnitRunner.class)
-public class StandardStreamTest
-{
-    private final ScheduledExecutorScheduler scheduler = new ScheduledExecutorScheduler();
-    @Mock
-    private ISession session;
-    @Mock
-    private SynStreamFrame synStreamFrame;
-
-    @Before
-    public void setUp() throws Exception
-    {
-        scheduler.start();
-    }
-
-    @After
-    public void tearDown() throws Exception
-    {
-        scheduler.stop();
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testSyn()
-    {
-        Stream stream = new StandardStream(synStreamFrame.getStreamId(), synStreamFrame.getPriority(), session, null, null, null);
-        Set<Stream> streams = new HashSet<>();
-        streams.add(stream);
-        when(synStreamFrame.isClose()).thenReturn(false);
-        PushInfo pushInfo = new PushInfo(new Fields(), false);
-        when(session.getStreams()).thenReturn(streams);
-        stream.push(pushInfo, new Promise.Adapter<Stream>());
-        verify(session).syn(argThat(new PushSynInfoMatcher(stream.getId(), pushInfo)),
-                any(StreamFrameListener.class), any(Promise.class));
-    }
-
-    private class PushSynInfoMatcher extends ArgumentMatcher<PushSynInfo>
-    {
-        private int associatedStreamId;
-        private PushInfo pushInfo;
-
-        public PushSynInfoMatcher(int associatedStreamId, PushInfo pushInfo)
-        {
-            this.associatedStreamId = associatedStreamId;
-            this.pushInfo = pushInfo;
-        }
-
-        @Override
-        public boolean matches(Object argument)
-        {
-            PushSynInfo pushSynInfo = (PushSynInfo)argument;
-            return pushSynInfo.getAssociatedStreamId() == associatedStreamId && pushSynInfo.isClose() == pushInfo.isClose();
-        }
-    }
-
-    @Test
-    public void testSynOnClosedStream()
-    {
-        IStream stream = new StandardStream(synStreamFrame.getStreamId(), synStreamFrame.getPriority(), session,
-                null, null , null);
-        stream.updateCloseState(true, true);
-        stream.updateCloseState(true, false);
-        assertThat("stream expected to be closed", stream.isClosed(), is(true));
-        final CountDownLatch failedLatch = new CountDownLatch(1);
-        stream.push(new PushInfo(1, TimeUnit.SECONDS, new Fields(), false), new Promise.Adapter<Stream>()
-        {
-            @Override
-            public void failed(Throwable x)
-            {
-                failedLatch.countDown();
-            }
-        });
-        assertThat("PushStream creation failed", failedLatch.getCount(), equalTo(0L));
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test(expected = IllegalStateException.class)
-    public void testSendDataOnHalfClosedStream() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        SynStreamFrame synStreamFrame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, null);
-        IStream stream = new StandardStream(synStreamFrame.getStreamId(), synStreamFrame.getPriority(), session,
-                null, scheduler, null);
-        stream.updateWindowSize(8192);
-        stream.updateCloseState(synStreamFrame.isClose(), true);
-        assertThat("stream is half closed", stream.isHalfClosed(), is(true));
-        stream.data(new StringDataInfo("data on half closed stream", true));
-        verify(session, never()).data(any(IStream.class), any(DataInfo.class), anyInt(), any(TimeUnit.class), any(Callback.class));
-    }
-
-    @Test
-    @Slow
-    public void testIdleTimeout() throws Exception
-    {
-        IStream stream = new StandardStream(1, (byte)0, session, null, scheduler, null);
-        long idleTimeout = 500;
-        stream.setIdleTimeout(idleTimeout);
-
-        final AtomicInteger failureCount = new AtomicInteger();
-        final CountDownLatch failureLatch = new CountDownLatch(1);
-        stream.setStreamFrameListener(new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onFailure(Stream stream, Throwable x)
-            {
-                assertThat("exception is a TimeoutException", x, is(instanceOf(TimeoutException.class)));
-                failureCount.incrementAndGet();
-                failureLatch.countDown();
-            }
-        });
-        stream.process(new StringDataInfo("string", false));
-
-        // Wait more than (2 * idleTimeout) to be sure to trigger a failureCount > 1
-        Thread.sleep(3 * idleTimeout);
-
-        assertThat("onFailure has been called", failureLatch.await(5, TimeUnit.SECONDS), is(true));
-        Assert.assertEquals(1, failureCount.get());
-    }
-
-    @Test
-    @Slow
-    public void testIdleTimeoutIsInterruptedWhenReceiving() throws Exception
-    {
-        final CountDownLatch failureLatch = new CountDownLatch(1);
-        IStream stream = new StandardStream(1, (byte)0, session, null, scheduler, null);
-        long idleTimeout = 1000;
-        stream.setIdleTimeout(idleTimeout);
-        stream.setStreamFrameListener(new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onFailure(Stream stream, Throwable x)
-            {
-                assertThat("exception is a TimeoutException", x, is(instanceOf(TimeoutException.class)));
-                failureLatch.countDown();
-            }
-        });
-        stream.process(new SynStreamFrame(SPDY.V3, (byte)0, 1, 0, (byte)0, (short)0, null));
-        stream.process(new StringDataInfo("string", false));
-        Thread.sleep(idleTimeout / 2);
-        stream.process(new StringDataInfo("string", false));
-        Thread.sleep(idleTimeout / 2);
-        stream.process(new StringDataInfo("string", false));
-        Thread.sleep(idleTimeout / 2);
-        stream.process(new StringDataInfo("string", true));
-        stream.reply(new ReplyInfo(true), new Callback.Adapter());
-        Thread.sleep(idleTimeout);
-        assertThat("onFailure has not been called", failureLatch.await(idleTimeout, TimeUnit.MILLISECONDS), is(false));
-    }
-
-    @Test
-    @Slow
-    public void testReplyFailureClosesStream() throws Exception
-    {
-        ISession session = new StandardSession(SPDY.V3, null, null, null, null, null, 1, null, null, null)
-        {
-            @Override
-            public void control(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Callback callback)
-            {
-                callback.failed(new ClosedChannelException());
-            }
-        };
-        IStream stream = new StandardStream(1, (byte)0, session, null, scheduler, null);
-        final AtomicInteger failureCount = new AtomicInteger();
-        stream.setStreamFrameListener(new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onFailure(Stream stream, Throwable x)
-            {
-                failureCount.incrementAndGet();
-            }
-        });
-        long idleTimeout = 500;
-        stream.setIdleTimeout(idleTimeout);
-
-        stream.process(new SynStreamFrame(SPDY.V3, (byte)0, 1, 0, (byte)0, (short)0, null));
-
-        final CountDownLatch failureLatch = new CountDownLatch(1);
-        stream.reply(new ReplyInfo(false), new Callback.Adapter()
-        {
-            @Override
-            public void failed(Throwable x)
-            {
-                failureLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS));
-
-        // Make sure that the idle timeout never fires, since the failure above should have closed the stream
-        Thread.sleep(3 * idleTimeout);
-
-        Assert.assertEquals(0, failureCount.get());
-        Assert.assertTrue(stream.isClosed());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ClientUsageTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ClientUsageTest.java
deleted file mode 100644
index 935d5b0..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ClientUsageTest.java
+++ /dev/null
@@ -1,260 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import java.nio.charset.StandardCharsets;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.eclipse.jetty.spdy.StandardSession;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-import org.junit.Ignore;
-import org.junit.Test;
-
-@Ignore
-public class ClientUsageTest
-{
-    @Test
-    public void testClientRequestResponseNoBody() throws Exception
-    {
-        Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
-
-        session.syn(new SynInfo(new Fields(), true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                // Do something with the response
-                replyInfo.getHeaders().get("host");
-
-                // Then issue another similar request
-                try
-                {
-                    stream.getSession().syn(new SynInfo(new Fields(), true), this);
-                }
-                catch (ExecutionException | InterruptedException | TimeoutException e)
-                {
-                    throw new IllegalStateException(e);
-                }
-            }
-        });
-    }
-
-    @Test
-    public void testClientReceivesPush1() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
-
-        session.syn(new SynInfo(new Fields(), true), new StreamFrameListener.Adapter()
-        {
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                return new Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                    }
-                };
-            };
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                // Do something with the response
-                replyInfo.getHeaders().get("host");
-
-                // Then issue another similar request
-                try
-                {
-                    stream.getSession().syn(new SynInfo(new Fields(), true), this);
-                }
-                catch (ExecutionException | InterruptedException | TimeoutException e)
-                {
-                    throw new IllegalStateException(e);
-                }
-            }
-        });
-    }
-
-    @Test
-    public void testClientReceivesPush2() throws InterruptedException, ExecutionException, TimeoutException
-    {
-        Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, new SessionFrameListener.Adapter()
-        {
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                    }
-                };
-            }
-        }, null, null);
-
-        session.syn(new SynInfo(new Fields(), true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                // Do something with the response
-                replyInfo.getHeaders().get("host");
-
-                // Then issue another similar request
-                try
-                {
-                    stream.getSession().syn(new SynInfo(new Fields(), true), this);
-                }
-                catch (ExecutionException | InterruptedException | TimeoutException e)
-                {
-                    throw new IllegalStateException(e);
-                }
-            }
-        });
-    }
-
-    @Test
-    public void testClientRequestWithBodyResponseNoBody() throws Exception
-    {
-        Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
-
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), false, (byte)0),
-                new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onReply(Stream stream, ReplyInfo replyInfo)
-                    {
-                        // Do something with the response
-                        replyInfo.getHeaders().get("host");
-
-                        // Then issue another similar request
-                        try
-                        {
-                            stream.getSession().syn(new SynInfo(new Fields(), true), this);
-                        }
-                        catch (ExecutionException | InterruptedException | TimeoutException e)
-                        {
-                            throw new IllegalStateException(e);
-                        }
-                    }
-                });
-        // Send-and-forget the data
-        stream.data(new StringDataInfo("data", true));
-    }
-
-    @Test
-    public void testAsyncClientRequestWithBodyResponseNoBody() throws Exception
-    {
-        Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
-
-        final String context = "context";
-        session.syn(new SynInfo(new Fields(), false), new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onReply(Stream stream, ReplyInfo replyInfo)
-                    {
-                        // Do something with the response
-                        replyInfo.getHeaders().get("host");
-
-                        // Then issue another similar request
-                        try
-                        {
-                            stream.getSession().syn(new SynInfo(new Fields(), true), this);
-                        }
-                        catch (ExecutionException | InterruptedException | TimeoutException e)
-                        {
-                            throw new IllegalStateException(e);
-                        }
-                    }
-                }, new Promise.Adapter<Stream>()
-                {
-                    @Override
-                    public void succeeded(Stream stream)
-                    {
-                        // Differently from JDK 7 AIO, there is no need to
-                        // have an explicit parameter for the context since
-                        // that is captured while the handler is created anyway,
-                        // and it is used only by the handler as parameter
-
-                        // The style below is fire-and-forget, since
-                        // we do not pass the handler nor we call get()
-                        // to wait for the data to be sent
-                        stream.data(new StringDataInfo(context, true), new Callback.Adapter());
-                    }
-                }
-        );
-    }
-
-    @Test
-    public void testAsyncClientRequestWithBodyAndResponseWithBody() throws Exception
-    {
-        Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
-
-        session.syn(new SynInfo(new Fields(), false), new StreamFrameListener.Adapter()
-                {
-                    // The good of passing the listener to push() is that applications can safely
-                    // accumulate info from the reply headers to be used in the data callback,
-                    // e.g. content-type, charset, etc.
-
-                    @Override
-                    public void onReply(Stream stream, ReplyInfo replyInfo)
-                    {
-                        // Do something with the response
-                        Fields headers = replyInfo.getHeaders();
-                        int contentLength = headers.get("content-length").getValueAsInt();
-                        stream.setAttribute("content-length", contentLength);
-                        if (!replyInfo.isClose())
-                            stream.setAttribute("builder", new StringBuilder());
-
-                        // May issue another similar request while waiting for data
-                        try
-                        {
-                            stream.getSession().syn(new SynInfo(new Fields(), true), this);
-                        }
-                        catch (ExecutionException | InterruptedException | TimeoutException e)
-                        {
-                            throw new IllegalStateException(e);
-                        }
-                    }
-
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        StringBuilder builder = (StringBuilder)stream.getAttribute("builder");
-                        builder.append(dataInfo.asString(StandardCharsets.UTF_8, true));
-
-                    }
-                }, new Promise.Adapter<Stream>()
-                {
-                    @Override
-                    public void succeeded(Stream stream)
-                    {
-                        stream.data(new BytesDataInfo("wee".getBytes(StandardCharsets.UTF_8), false), new Callback.Adapter());
-                        stream.data(new StringDataInfo("foo", false), new Callback.Adapter());
-                        stream.data(new ByteBufferDataInfo(StandardCharsets.UTF_8.encode("bar"), true), new Callback.Adapter());
-                    }
-                }
-        );
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ServerUsageTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ServerUsageTest.java
deleted file mode 100644
index 977b79b..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ServerUsageTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.api;
-
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
-@Ignore
-public class ServerUsageTest
-{
-    @Test
-    public void testServerSynAndReplyWithData() throws Exception
-    {
-        ServerSessionFrameListener ssfl = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo streamInfo)
-            {
-                Fields synHeaders = streamInfo.getHeaders();
-                // Do something with headers, for example extract them and
-                // perform an http request via Jetty's LocalConnector
-
-                // Get the http response, fill headers and data
-                Fields replyHeaders = new Fields();
-                replyHeaders.put(synHeaders.get("host"));
-                // Sends a reply
-                stream.reply(new ReplyInfo(replyHeaders, false), new Callback.Adapter());
-
-                // Sends data
-                StringDataInfo dataInfo = new StringDataInfo("foo", false);
-                stream.data(dataInfo, new Callback.Adapter());
-                // Stream is now closed
-                return null;
-            }
-        };
-        Assert.assertNotNull(ssfl);
-    }
-
-    @Test
-    public void testServerInitiatesStreamAndPushesData() throws Exception
-    {
-        ServerSessionFrameListener ssfl = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public void onConnect(Session session)
-            {
-                // SPDY does not allow the server to initiate a stream without an existing stream
-                // being opened by the client already.
-                // Correct SPDY sequence will be:
-                // C ---       SYN_STREAM(id=1)       --> S
-                // C <--       SYN_REPLY(id=1)        --- S
-                // C <-- SYN_STREAM(id=2,uni,assId=1) --- S
-                //
-                // However, the API may allow to initiate the stream
-
-                session.syn(new SynInfo(new Fields(), false), null, new Promise.Adapter<Stream>()
-                {
-                    @Override
-                    public void succeeded(Stream stream)
-                    {
-                        // The point here is that we have no idea if the client accepted our stream
-                        // So we return a stream, we may be able to send the headers frame, but later
-                        // the client sends a rst frame.
-                        // We have to atomically set some flag on the stream to signal it's closed
-                        // and any operation on it will throw
-                        stream.headers(new HeadersInfo(new Fields(), true), new Callback.Adapter());
-                    }
-                });
-            }
-        };
-        Assert.assertNotNull(ssfl);
-    }
-
-    @Test
-    public void testServerPush() throws Exception
-    {
-        ServerSessionFrameListener ssfl = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo streamInfo)
-            {
-                // Need to send the reply first
-                stream.reply(new ReplyInfo(false), new Callback.Adapter());
-
-                Session session = stream.getSession();
-                // Since it's unidirectional, no need to pass the listener
-                session.syn(new SynInfo(new Fields(), false, (byte)0), null, new Promise.Adapter<Stream>()
-                {
-                    @Override
-                    public void succeeded(Stream pushStream)
-                    {
-                        pushStream.data(new StringDataInfo("foo", false), new Callback.Adapter());
-                    }
-                });
-                return null;
-            }
-        };
-        Assert.assertNotNull(ssfl);
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/CredentialGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/CredentialGenerateParseTest.java
deleted file mode 100644
index a963412..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/CredentialGenerateParseTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.security.KeyStore;
-import java.security.cert.Certificate;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.eclipse.jetty.util.resource.Resource;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class CredentialGenerateParseTest
-{
-    @Test
-    public void testGenerateParse() throws Exception
-    {
-        short slot = 1;
-        byte[] proof = new byte[]{0, 1, 2};
-        Certificate[] temp = loadCertificates();
-        Certificate[] certificates = new Certificate[temp.length * 2];
-        System.arraycopy(temp, 0, certificates, 0, temp.length);
-        System.arraycopy(temp, 0, certificates, temp.length, temp.length);
-        CredentialFrame frame1 = new CredentialFrame(SPDY.V3, slot, proof, certificates);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        parser.parse(buffer);
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.CREDENTIAL, frame2.getType());
-        CredentialFrame credential = (CredentialFrame)frame2;
-        Assert.assertEquals(SPDY.V3, credential.getVersion());
-        Assert.assertEquals(0, credential.getFlags());
-        Assert.assertEquals(slot, credential.getSlot());
-        Assert.assertArrayEquals(proof, credential.getProof());
-        Assert.assertArrayEquals(certificates, credential.getCertificateChain());
-    }
-
-    @Test
-    public void testGenerateParseOneByteAtATime() throws Exception
-    {
-        short slot = 1;
-        byte[] proof = new byte[]{0, 1, 2};
-        Certificate[] certificates = loadCertificates();
-        CredentialFrame frame1 = new CredentialFrame(SPDY.V3, slot, proof, certificates);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        while (buffer.hasRemaining())
-            parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.CREDENTIAL, frame2.getType());
-        CredentialFrame credential = (CredentialFrame)frame2;
-        Assert.assertEquals(SPDY.V3, credential.getVersion());
-        Assert.assertEquals(0, credential.getFlags());
-        Assert.assertEquals(slot, credential.getSlot());
-        Assert.assertArrayEquals(proof, credential.getProof());
-        Assert.assertArrayEquals(certificates, credential.getCertificateChain());
-    }
-
-    private Certificate[] loadCertificates() throws Exception
-    {
-        KeyStore keyStore = KeyStore.getInstance("JKS");
-        InputStream keyStoreStream = Resource.newResource("src/test/resources/keystore.jks").getInputStream();
-        keyStore.load(keyStoreStream, "storepwd".toCharArray());
-        return keyStore.getCertificateChain("mykey");
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/DataGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/DataGenerateParseTest.java
deleted file mode 100644
index 700c624..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/DataGenerateParseTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class DataGenerateParseTest
-{
-    @Test
-    public void testGenerateParse() throws Exception
-    {
-        testGenerateParse("test1");
-    }
-
-    @Test
-    public void testGenerateParseZeroLength() throws Exception
-    {
-        testGenerateParse("");
-    }
-
-    private void testGenerateParse(String content) throws Exception
-    {
-        int length = content.length();
-        DataInfo data = new StringDataInfo(content, true);
-        int streamId = 13;
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.data(streamId, 2 * length, data);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        parser.parse(buffer);
-        DataFrame frame2 = listener.getDataFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(streamId, frame2.getStreamId());
-        Assert.assertEquals(DataInfo.FLAG_CLOSE, frame2.getFlags());
-        Assert.assertEquals(length, frame2.getLength());
-        Assert.assertEquals(length, listener.getData().remaining());
-    }
-
-    @Test
-    public void testGenerateParseOneByteAtATime() throws Exception
-    {
-        String content = "test2";
-        int length = content.length();
-        DataInfo data = new StringDataInfo(content, true);
-        int streamId = 13;
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.data(streamId, 2 * length, data);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        while (buffer.hasRemaining())
-        {
-            parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
-            if (buffer.remaining() < length)
-            {
-                DataFrame frame2 = listener.getDataFrame();
-                Assert.assertNotNull(frame2);
-                Assert.assertEquals(streamId, frame2.getStreamId());
-                Assert.assertEquals(buffer.hasRemaining() ? 0 : DataInfo.FLAG_CLOSE, frame2.getFlags());
-                Assert.assertEquals(1, frame2.getLength());
-                Assert.assertEquals(1, listener.getData().remaining());
-            }
-        }
-    }
-
-    @Test
-    public void testGenerateParseWithSyntheticFrames() throws Exception
-    {
-        String content = "0123456789ABCDEF";
-        int length = content.length();
-        DataInfo data = new StringDataInfo(content, true);
-        int streamId = 13;
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.data(streamId, 2 * length, data);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-
-        // Split the buffer to simulate a split boundary in receiving the bytes
-        int split = 3;
-        ByteBuffer buffer1 = ByteBuffer.allocate(buffer.remaining() - split);
-        buffer.limit(buffer.limit() - split);
-        buffer1.put(buffer);
-        buffer1.flip();
-        ByteBuffer buffer2 = ByteBuffer.allocate(split);
-        buffer.limit(buffer.limit() + split);
-        buffer2.put(buffer);
-        buffer2.flip();
-
-        parser.parse(buffer1);
-        DataFrame frame2 = listener.getDataFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(streamId, frame2.getStreamId());
-        Assert.assertEquals(0, frame2.getFlags());
-        Assert.assertEquals(length - split, frame2.getLength());
-        Assert.assertEquals(length - split, listener.getData().remaining());
-
-        parser.parse(buffer2);
-        DataFrame frame3 = listener.getDataFrame();
-
-        Assert.assertNotNull(frame3);
-        Assert.assertEquals(streamId, frame3.getStreamId());
-        Assert.assertEquals(DataInfo.FLAG_CLOSE, frame3.getFlags());
-        Assert.assertEquals(split, frame3.getLength());
-        Assert.assertEquals(split, listener.getData().remaining());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/GoAwayGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/GoAwayGenerateParseTest.java
deleted file mode 100644
index 094531b..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/GoAwayGenerateParseTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class GoAwayGenerateParseTest
-{
-    @Test
-    public void testGenerateParse() throws Exception
-    {
-        int lastStreamId = 13;
-        int statusCode = 1;
-        GoAwayFrame frame1 = new GoAwayFrame(SPDY.V3, lastStreamId, statusCode);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        parser.parse(buffer);
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.GO_AWAY, frame2.getType());
-        GoAwayFrame goAway = (GoAwayFrame)frame2;
-        Assert.assertEquals(SPDY.V3, goAway.getVersion());
-        Assert.assertEquals(lastStreamId, goAway.getLastStreamId());
-        Assert.assertEquals(0, goAway.getFlags());
-        Assert.assertEquals(statusCode, goAway.getStatusCode());
-    }
-
-    @Test
-    public void testGenerateParseOneByteAtATime() throws Exception
-    {
-        int lastStreamId = 13;
-        int statusCode = 1;
-        GoAwayFrame frame1 = new GoAwayFrame(SPDY.V3, lastStreamId, statusCode);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        while (buffer.hasRemaining())
-            parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.GO_AWAY, frame2.getType());
-        GoAwayFrame goAway = (GoAwayFrame)frame2;
-        Assert.assertEquals(SPDY.V3, goAway.getVersion());
-        Assert.assertEquals(lastStreamId, goAway.getLastStreamId());
-        Assert.assertEquals(0, goAway.getFlags());
-        Assert.assertEquals(statusCode, goAway.getStatusCode());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/HeadersGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/HeadersGenerateParseTest.java
deleted file mode 100644
index 5905337..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/HeadersGenerateParseTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.junit.Assert.assertThat;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Before;
-import org.junit.Test;
-
-public class HeadersGenerateParseTest
-{
-
-    private Fields headers = new Fields();
-    private int streamId = 13;
-    private byte flags = HeadersInfo.FLAG_RESET_COMPRESSION;
-    private final TestSPDYParserListener listener = new TestSPDYParserListener();
-    private final Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-    private ByteBuffer buffer;
-
-    @Before
-    public void setUp()
-    {
-        parser.addListener(listener);
-        headers.put("a", "b");
-        buffer = createHeadersFrameBuffer(headers);
-    }
-
-    private ByteBuffer createHeadersFrameBuffer(Fields headers)
-    {
-        HeadersFrame frame1 = new HeadersFrame(SPDY.V2, flags, streamId, headers);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-        assertThat("Buffer is not null", buffer, notNullValue());
-        return buffer;
-    }
-
-    @Test
-    public void testGenerateParse() throws Exception
-    {
-        parser.parse(buffer);
-        assertExpectationsAreMet(headers);
-    }
-
-    @Test
-    public void testGenerateParseOneByteAtATime() throws Exception
-    {
-        while (buffer.hasRemaining())
-            parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
-
-        assertExpectationsAreMet(headers);
-    }
-
-    @Test
-    public void testHeadersAreTranslatedToLowerCase()
-    {
-        Fields headers = new Fields();
-        headers.put("Via","localhost");
-        parser.parse(createHeadersFrameBuffer(headers));
-        HeadersFrame parsedHeadersFrame = assertExpectationsAreMet(headers);
-        Fields.Field viaHeader = parsedHeadersFrame.getHeaders().get("via");
-        assertThat("Via Header name is lowercase", viaHeader.getName(), is("via"));
-    }
-
-    private HeadersFrame assertExpectationsAreMet(Fields headers)
-    {
-        ControlFrame parsedControlFrame = listener.getControlFrame();
-        assertThat("listener received controlFrame", parsedControlFrame, notNullValue());
-        assertThat("ControlFrame type is HEADERS", ControlFrameType.HEADERS, is(parsedControlFrame.getType()));
-        HeadersFrame headersFrame = (HeadersFrame)parsedControlFrame;
-        assertThat("Version matches", SPDY.V2, is(headersFrame.getVersion()));
-        assertThat("StreamId matches", streamId, is(headersFrame.getStreamId()));
-        assertThat("flags match", flags, is(headersFrame.getFlags()));
-        assertThat("headers match", headers, is(headersFrame.getHeaders()));
-        return headersFrame;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/NoOpGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/NoOpGenerateParseTest.java
deleted file mode 100644
index ff7ff93..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/NoOpGenerateParseTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class NoOpGenerateParseTest
-{
-    @Test
-    public void testGenerateParse() throws Exception
-    {
-        NoOpFrame frame1 = new NoOpFrame();
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        parser.parse(buffer);
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.NOOP, frame2.getType());
-        NoOpFrame noOp = (NoOpFrame)frame2;
-        Assert.assertEquals(0, noOp.getFlags());
-    }
-
-    @Test
-    public void testGenerateParseOneByteAtATime() throws Exception
-    {
-        NoOpFrame frame1 = new NoOpFrame();
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        while (buffer.hasRemaining())
-            parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.NOOP, frame2.getType());
-        NoOpFrame noOp = (NoOpFrame)frame2;
-        Assert.assertEquals(0, noOp.getFlags());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/PingGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/PingGenerateParseTest.java
deleted file mode 100644
index 4836858..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/PingGenerateParseTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class PingGenerateParseTest
-{
-    @Test
-    public void testGenerateParse() throws Exception
-    {
-        int pingId = 13;
-        PingFrame frame1 = new PingFrame(SPDY.V2, pingId);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        parser.parse(buffer);
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.PING, frame2.getType());
-        PingFrame ping = (PingFrame)frame2;
-        Assert.assertEquals(SPDY.V2, ping.getVersion());
-        Assert.assertEquals(pingId, ping.getPingId());
-        Assert.assertEquals(0, ping.getFlags());
-    }
-
-    @Test
-    public void testGenerateParseOneByteAtATime() throws Exception
-    {
-        int pingId = 13;
-        PingFrame frame1 = new PingFrame(SPDY.V2, pingId);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        while (buffer.hasRemaining())
-            parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.PING, frame2.getType());
-        PingFrame ping = (PingFrame)frame2;
-        Assert.assertEquals(SPDY.V2, ping.getVersion());
-        Assert.assertEquals(pingId, ping.getPingId());
-        Assert.assertEquals(0, ping.getFlags());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/RstStreamGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/RstStreamGenerateParseTest.java
deleted file mode 100644
index 3e6f490..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/RstStreamGenerateParseTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertThat;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class RstStreamGenerateParseTest
-{
-    @Test
-    public void testGenerateParse() throws Exception
-    {
-        int streamId = 13;
-        int streamStatus = StreamStatus.UNSUPPORTED_VERSION.getCode(SPDY.V2);
-        RstStreamFrame frame1 = new RstStreamFrame(SPDY.V2, streamId, streamStatus);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        assertThat("buffer is not null", buffer, not(nullValue()));
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        parser.parse(buffer);
-        ControlFrame frame2 = listener.getControlFrame();
-
-        assertThat("frame2 is not null", frame2, not(nullValue()));
-        assertThat("frame2 is type RST_STREAM",ControlFrameType.RST_STREAM, equalTo(frame2.getType()));
-        RstStreamFrame rstStream = (RstStreamFrame)frame2;
-        assertThat("rstStream version is SPDY.V2",SPDY.V2, equalTo(rstStream.getVersion()));
-        assertThat("rstStream id is equal to streamId",streamId, equalTo(rstStream.getStreamId()));
-        assertThat("rstStream flags are 0",(byte)0, equalTo(rstStream.getFlags()));
-        assertThat("stream status is equal to rstStream statuscode",streamStatus, is(rstStream.getStatusCode()));
-    }
-
-    @Test
-    public void testGenerateParseOneByteAtATime() throws Exception
-    {
-        int streamId = 13;
-        int streamStatus = StreamStatus.UNSUPPORTED_VERSION.getCode(SPDY.V2);
-        RstStreamFrame frame1 = new RstStreamFrame(SPDY.V2, streamId, streamStatus);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        while (buffer.hasRemaining())
-            parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.RST_STREAM, frame2.getType());
-        RstStreamFrame rstStream = (RstStreamFrame)frame2;
-        Assert.assertEquals(SPDY.V2, rstStream.getVersion());
-        Assert.assertEquals(streamId, rstStream.getStreamId());
-        Assert.assertEquals(0, rstStream.getFlags());
-        Assert.assertEquals(streamStatus, rstStream.getStatusCode());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SettingsGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SettingsGenerateParseTest.java
deleted file mode 100644
index e0f23fc..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SettingsGenerateParseTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Settings;
-import org.eclipse.jetty.spdy.api.SettingsInfo;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class SettingsGenerateParseTest
-{
-    @Test
-    public void testGenerateParse() throws Exception
-    {
-        byte flags = SettingsInfo.CLEAR_PERSISTED;
-        Settings settings = new Settings();
-        settings.put(new Settings.Setting(Settings.ID.MAX_CONCURRENT_STREAMS, Settings.Flag.PERSIST, 100));
-        settings.put(new Settings.Setting(Settings.ID.ROUND_TRIP_TIME, Settings.Flag.PERSISTED, 500));
-        SettingsFrame frame1 = new SettingsFrame(SPDY.V2, flags, settings);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        parser.parse(buffer);
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.SETTINGS, frame2.getType());
-        SettingsFrame settingsFrame = (SettingsFrame)frame2;
-        Assert.assertEquals(SPDY.V2, settingsFrame.getVersion());
-        Assert.assertEquals(flags, settingsFrame.getFlags());
-        Assert.assertEquals(settings, settingsFrame.getSettings());
-    }
-
-    @Test
-    public void testGenerateParseOneByteAtATime() throws Exception
-    {
-        byte flags = SettingsInfo.CLEAR_PERSISTED;
-        Settings settings = new Settings();
-        settings.put(new Settings.Setting(Settings.ID.DOWNLOAD_RETRANSMISSION_RATE, 100));
-        settings.put(new Settings.Setting(Settings.ID.ROUND_TRIP_TIME, 500));
-        SettingsFrame frame1 = new SettingsFrame(SPDY.V2, flags, settings);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        while (buffer.hasRemaining())
-            parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.SETTINGS, frame2.getType());
-        SettingsFrame settingsFrame = (SettingsFrame)frame2;
-        Assert.assertEquals(SPDY.V2, settingsFrame.getVersion());
-        Assert.assertEquals(flags, settingsFrame.getFlags());
-        Assert.assertEquals(settings, settingsFrame.getSettings());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynReplyGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynReplyGenerateParseTest.java
deleted file mode 100644
index a857b0d..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynReplyGenerateParseTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class SynReplyGenerateParseTest
-{
-    @Test
-    public void testGenerateParse() throws Exception
-    {
-        byte flags = ReplyInfo.FLAG_CLOSE;
-        int streamId = 13;
-        Fields headers = new Fields();
-        headers.put("a", "b");
-        SynReplyFrame frame1 = new SynReplyFrame(SPDY.V2, flags, streamId, headers);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        parser.parse(buffer);
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.SYN_REPLY, frame2.getType());
-        SynReplyFrame synReply = (SynReplyFrame)frame2;
-        Assert.assertEquals(SPDY.V2, synReply.getVersion());
-        Assert.assertEquals(flags, synReply.getFlags());
-        Assert.assertEquals(streamId, synReply.getStreamId());
-        Assert.assertEquals(headers, synReply.getHeaders());
-    }
-
-    @Test
-    public void testGenerateParseOneByteAtATime() throws Exception
-    {
-        byte flags = ReplyInfo.FLAG_CLOSE;
-        int streamId = 13;
-        Fields headers = new Fields();
-        headers.put("a", "b");
-        SynReplyFrame frame1 = new SynReplyFrame(SPDY.V2, flags, streamId, headers);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        while (buffer.hasRemaining())
-            parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.SYN_REPLY, frame2.getType());
-        SynReplyFrame synReply = (SynReplyFrame)frame2;
-        Assert.assertEquals(SPDY.V2, synReply.getVersion());
-        Assert.assertEquals(flags, synReply.getFlags());
-        Assert.assertEquals(streamId, synReply.getStreamId());
-        Assert.assertEquals(headers, synReply.getHeaders());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynStreamGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynStreamGenerateParseTest.java
deleted file mode 100644
index a2131f5..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/SynStreamGenerateParseTest.java
+++ /dev/null
@@ -1,105 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class SynStreamGenerateParseTest
-{
-    @Test
-    public void testGenerateParse() throws Exception
-    {
-        byte flags = SynInfo.FLAG_CLOSE;
-        int streamId = 13;
-        int associatedStreamId = 11;
-        byte priority = 3;
-        short slot = 5;
-        Fields headers = new Fields();
-        headers.put("a", "b");
-        headers.put("c", "d");
-        SynStreamFrame frame1 = new SynStreamFrame(SPDY.V2, flags, streamId, associatedStreamId, priority, slot, headers);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        parser.parse(buffer);
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.SYN_STREAM, frame2.getType());
-        SynStreamFrame synStream = (SynStreamFrame)frame2;
-        Assert.assertEquals(SPDY.V2, synStream.getVersion());
-        Assert.assertEquals(streamId, synStream.getStreamId());
-        Assert.assertEquals(associatedStreamId, synStream.getAssociatedStreamId());
-        Assert.assertEquals(flags, synStream.getFlags());
-        Assert.assertEquals(priority, synStream.getPriority());
-        Assert.assertEquals(slot, synStream.getSlot());
-        Assert.assertEquals(headers, synStream.getHeaders());
-    }
-
-    @Test
-    public void testGenerateParseOneByteAtATime() throws Exception
-    {
-        byte flags = SynInfo.FLAG_CLOSE;
-        int streamId = 13;
-        int associatedStreamId = 11;
-        byte priority = 3;
-        short slot = 5;
-        Fields headers = new Fields();
-        headers.put("a", "b");
-        headers.put("c", "d");
-        SynStreamFrame frame1 = new SynStreamFrame(SPDY.V2, flags, streamId, associatedStreamId, priority, slot, headers);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        while (buffer.hasRemaining())
-            parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.SYN_STREAM, frame2.getType());
-        SynStreamFrame synStream = (SynStreamFrame)frame2;
-        Assert.assertEquals(SPDY.V2, synStream.getVersion());
-        Assert.assertEquals(streamId, synStream.getStreamId());
-        Assert.assertEquals(associatedStreamId, synStream.getAssociatedStreamId());
-        Assert.assertEquals(flags, synStream.getFlags());
-        Assert.assertEquals(priority, synStream.getPriority());
-        Assert.assertEquals(slot, synStream.getSlot());
-        Assert.assertEquals(headers, synStream.getHeaders());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/TestSPDYParserListener.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/TestSPDYParserListener.java
deleted file mode 100644
index 6d5a6a4..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/TestSPDYParserListener.java
+++ /dev/null
@@ -1,70 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.spdy.SessionException;
-import org.eclipse.jetty.spdy.StreamException;
-import org.eclipse.jetty.spdy.parser.Parser;
-
-public class TestSPDYParserListener implements Parser.Listener
-{
-    private ControlFrame controlFrame;
-    private DataFrame dataFrame;
-    private ByteBuffer data;
-
-    @Override
-    public void onControlFrame(ControlFrame frame)
-    {
-        this.controlFrame = frame;
-    }
-
-    @Override
-    public void onDataFrame(DataFrame frame, ByteBuffer data)
-    {
-        this.dataFrame = frame;
-        this.data = data;
-    }
-
-    @Override
-    public void onStreamException(StreamException x)
-    {
-    }
-
-    @Override
-    public void onSessionException(SessionException x)
-    {
-    }
-
-    public ControlFrame getControlFrame()
-    {
-        return controlFrame;
-    }
-
-    public DataFrame getDataFrame()
-    {
-        return dataFrame;
-    }
-
-    public ByteBuffer getData()
-    {
-        return data;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/WindowUpdateGenerateParseTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/WindowUpdateGenerateParseTest.java
deleted file mode 100644
index e500d60..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/frames/WindowUpdateGenerateParseTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.frames;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class WindowUpdateGenerateParseTest
-{
-    @Test
-    public void testGenerateParse() throws Exception
-    {
-        int streamId = 13;
-        int windowDelta = 17;
-        WindowUpdateFrame frame1 = new WindowUpdateFrame(SPDY.V2, streamId, windowDelta);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        parser.parse(buffer);
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.WINDOW_UPDATE, frame2.getType());
-        WindowUpdateFrame windowUpdate = (WindowUpdateFrame)frame2;
-        Assert.assertEquals(SPDY.V2, windowUpdate.getVersion());
-        Assert.assertEquals(streamId, windowUpdate.getStreamId());
-        Assert.assertEquals(0, windowUpdate.getFlags());
-        Assert.assertEquals(windowDelta, windowUpdate.getWindowDelta());
-    }
-
-    @Test
-    public void testGenerateParseOneByteAtATime() throws Exception
-    {
-        int streamId = 13;
-        int windowDelta = 17;
-        WindowUpdateFrame frame1 = new WindowUpdateFrame(SPDY.V2, streamId, windowDelta);
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        ByteBuffer buffer = generator.control(frame1);
-
-        Assert.assertNotNull(buffer);
-
-        TestSPDYParserListener listener = new TestSPDYParserListener();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(listener);
-        while (buffer.hasRemaining())
-            parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
-        ControlFrame frame2 = listener.getControlFrame();
-
-        Assert.assertNotNull(frame2);
-        Assert.assertEquals(ControlFrameType.WINDOW_UPDATE, frame2.getType());
-        WindowUpdateFrame windowUpdate = (WindowUpdateFrame)frame2;
-        Assert.assertEquals(SPDY.V2, windowUpdate.getVersion());
-        Assert.assertEquals(streamId, windowUpdate.getStreamId());
-        Assert.assertEquals(0, windowUpdate.getFlags());
-        Assert.assertEquals(windowDelta, windowUpdate.getWindowDelta());
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/generator/DataFrameGeneratorTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/generator/DataFrameGeneratorTest.java
deleted file mode 100644
index 6c57f45..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/generator/DataFrameGeneratorTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.generator;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.ThreadLocalRandom;
-
-import org.eclipse.jetty.io.ArrayByteBufferPool;
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.frames.DataFrame;
-import org.eclipse.jetty.util.BufferUtil;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class DataFrameGeneratorTest
-{
-    private int increment = 1024;
-    private int streamId = 1;
-    private ArrayByteBufferPool bufferPool;
-    private DataFrameGenerator dataFrameGenerator;
-    private ByteBuffer headerBuffer = ByteBuffer.allocate(DataFrame.HEADER_LENGTH);
-
-    @Before
-    public void setUp()
-    {
-        bufferPool = new ArrayByteBufferPool(64, increment, 8192);
-        dataFrameGenerator = new DataFrameGenerator(bufferPool);
-        headerBuffer.putInt(0, streamId & 0x7F_FF_FF_FF);
-
-    }
-
-    @Test
-    public void testGenerateSmallFrame()
-    {
-        int bufferSize = 256;
-        generateFrame(bufferSize);
-    }
-
-    @Test
-    public void testGenerateFrameWithBufferThatEqualsBucketSize()
-    {
-        int bufferSize = increment;
-        generateFrame(bufferSize);
-    }
-
-    @Test
-    public void testGenerateFrameWithBufferThatEqualsBucketSizeMinusHeaderLength()
-    {
-        int bufferSize = increment - DataFrame.HEADER_LENGTH;
-        generateFrame(bufferSize);
-    }
-
-    private void generateFrame(int bufferSize)
-    {
-        ByteBuffer byteBuffer = createByteBuffer(bufferSize);
-        ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(byteBuffer, true);
-        fillHeaderBuffer(bufferSize);
-        ByteBuffer dataFrameBuffer = dataFrameGenerator.generate(streamId, bufferSize, dataInfo);
-
-        assertThat("The content size in dataFrameBuffer matches the buffersize + header length",
-                dataFrameBuffer.limit(),
-                is(bufferSize + DataFrame.HEADER_LENGTH));
-
-        byte[] headerBytes = new byte[DataFrame.HEADER_LENGTH];
-        dataFrameBuffer.get(headerBytes, 0, DataFrame.HEADER_LENGTH);
-        
-        assertThat("Header bytes are prepended", headerBytes, is(headerBuffer.array()));
-    }
-
-    private ByteBuffer createByteBuffer(int bufferSize)
-    {
-        byte[] bytes = new byte[bufferSize];
-        ThreadLocalRandom.current().nextBytes(bytes);
-        ByteBuffer byteBuffer = bufferPool.acquire(bufferSize, false);
-        BufferUtil.flipToFill(byteBuffer);
-        byteBuffer.put(bytes);
-        BufferUtil.flipToFlush(byteBuffer, 0);
-        return byteBuffer;
-    }
-
-    private void fillHeaderBuffer(int bufferSize)
-    {
-        headerBuffer.putInt(4, bufferSize & 0x00_FF_FF_FF);
-        headerBuffer.put(4, DataInfo.FLAG_CLOSE);
-    }
-
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/BrokenFrameTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/BrokenFrameTest.java
deleted file mode 100644
index 2fa84bb..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/BrokenFrameTest.java
+++ /dev/null
@@ -1,287 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-import java.io.ByteArrayOutputStream;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.zip.ZipException;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.CompressionFactory;
-import org.eclipse.jetty.spdy.StreamException;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.SynStreamFrame;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Test;
-
-public class BrokenFrameTest
-{
-
-    @Test
-    public void testInvalidHeaderNameLength() throws Exception
-    {
-        Fields headers = new Fields();
-        headers.add("broken", "header");
-        SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, headers);
-        Generator generator = new Generator(new MappedByteBufferPool(), new NoCompressionCompressionFactory.NoCompressionCompressor());
-
-        ByteBuffer bufferWithBrokenHeaderNameLength = generator.control(frame);
-        // Break the header name length to provoke the Parser to throw a StreamException
-        bufferWithBrokenHeaderNameLength.put(21, (byte)0);
-
-        ByteBuffer bufferWithValidSynStreamFrame = generator.control(frame);
-
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        outputStream.write(BufferUtil.toArray(bufferWithBrokenHeaderNameLength));
-        outputStream.write(BufferUtil.toArray(bufferWithValidSynStreamFrame));
-
-        byte concatenatedFramesByteArray[] = outputStream.toByteArray();
-        ByteBuffer concatenatedBuffer = BufferUtil.toBuffer(concatenatedFramesByteArray);
-
-        final CountDownLatch latch = new CountDownLatch(2);
-        Parser parser = new Parser(new NoCompressionCompressionFactory.NoCompressionDecompressor());
-        parser.addListener(new Parser.Listener.Adapter()
-        {
-            @Override
-            public void onControlFrame(ControlFrame frame)
-            {
-                latch.countDown();
-            }
-
-            @Override
-            public void onStreamException(StreamException x)
-            {
-                latch.countDown();
-            }
-        });
-        parser.parse(concatenatedBuffer);
-
-        assertThat(latch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testInvalidVersion() throws Exception
-    {
-        Fields headers = new Fields();
-        headers.add("good", "header");
-        headers.add("another","header");
-        SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, headers);
-        Generator generator = new Generator(new MappedByteBufferPool(), new NoCompressionCompressionFactory.NoCompressionCompressor());
-
-        ByteBuffer bufferWithBrokenVersion = generator.control(frame);
-        // Break the header name length to provoke the Parser to throw a StreamException
-        bufferWithBrokenVersion.put(1, (byte)4);
-
-        ByteBuffer bufferWithValidSynStreamFrame = generator.control(frame);
-
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        outputStream.write(BufferUtil.toArray(bufferWithBrokenVersion));
-        outputStream.write(BufferUtil.toArray(bufferWithValidSynStreamFrame));
-
-        byte concatenatedFramesByteArray[] = outputStream.toByteArray();
-        ByteBuffer concatenatedBuffer = BufferUtil.toBuffer(concatenatedFramesByteArray);
-
-        final CountDownLatch latch = new CountDownLatch(2);
-        Parser parser = new Parser(new NoCompressionCompressionFactory.NoCompressionDecompressor());
-        parser.addListener(new Parser.Listener.Adapter()
-        {
-            @Override
-            public void onControlFrame(ControlFrame frame)
-            {
-                latch.countDown();
-            }
-
-            @Override
-            public void onStreamException(StreamException x)
-            {
-                latch.countDown();
-            }
-        });
-        parser.parse(concatenatedBuffer);
-
-        assertThat(latch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testInvalidVersionWithSplitBuffer() throws Exception
-    {
-        Fields headers = new Fields();
-        headers.add("good", "header");
-        headers.add("another","header");
-        SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, headers);
-        Generator generator = new Generator(new MappedByteBufferPool(), new NoCompressionCompressionFactory.NoCompressionCompressor());
-
-        ByteBuffer bufferWithBrokenVersion = generator.control(frame);
-        // Break the header name length to provoke the Parser to throw a StreamException
-        bufferWithBrokenVersion.put(1, (byte)4);
-
-        ByteBuffer bufferWithValidSynStreamFrame = generator.control(frame);
-
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        outputStream.write(BufferUtil.toArray(bufferWithBrokenVersion));
-        outputStream.write(BufferUtil.toArray(bufferWithValidSynStreamFrame));
-
-        byte concatenatedFramesByteArray[] = outputStream.toByteArray();
-        ByteBuffer concatenatedBuffer1 = BufferUtil.toBuffer(Arrays.copyOfRange(concatenatedFramesByteArray,0,20));
-        ByteBuffer concatenatedBuffer2 = BufferUtil.toBuffer(Arrays.copyOfRange(concatenatedFramesByteArray,20,
-                concatenatedFramesByteArray.length));
-
-        final CountDownLatch latch = new CountDownLatch(2);
-        Parser parser = new Parser(new NoCompressionCompressionFactory.NoCompressionDecompressor());
-        parser.addListener(new Parser.Listener.Adapter()
-        {
-            @Override
-            public void onControlFrame(ControlFrame frame)
-            {
-                latch.countDown();
-            }
-
-            @Override
-            public void onStreamException(StreamException x)
-            {
-                latch.countDown();
-            }
-        });
-        parser.parse(concatenatedBuffer1);
-        parser.parse(concatenatedBuffer2);
-
-        assertThat(latch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testInvalidVersionAndGoodFrameSplitInThreeBuffers() throws Exception
-    {
-        Fields headers = new Fields();
-        headers.add("good", "header");
-        headers.add("another","header");
-        SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, headers);
-        Generator generator = new Generator(new MappedByteBufferPool(), new NoCompressionCompressionFactory.NoCompressionCompressor());
-
-        ByteBuffer bufferWithBrokenVersion = generator.control(frame);
-        // Break the header name length to provoke the Parser to throw a StreamException
-        bufferWithBrokenVersion.put(1, (byte)4);
-
-        ByteBuffer bufferWithValidSynStreamFrame = generator.control(frame);
-
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        outputStream.write(BufferUtil.toArray(bufferWithBrokenVersion));
-        outputStream.write(BufferUtil.toArray(bufferWithValidSynStreamFrame));
-
-        byte concatenatedFramesByteArray[] = outputStream.toByteArray();
-        ByteBuffer concatenatedBuffer1 = BufferUtil.toBuffer(Arrays.copyOfRange(concatenatedFramesByteArray,0,20));
-        ByteBuffer concatenatedBuffer2 = BufferUtil.toBuffer(Arrays.copyOfRange(concatenatedFramesByteArray,20, 30));
-        ByteBuffer concatenatedBuffer3 = BufferUtil.toBuffer(Arrays.copyOfRange(concatenatedFramesByteArray,30,
-                concatenatedFramesByteArray.length));
-
-        final CountDownLatch latch = new CountDownLatch(2);
-        Parser parser = new Parser(new NoCompressionCompressionFactory.NoCompressionDecompressor());
-        parser.addListener(new Parser.Listener.Adapter()
-        {
-            @Override
-            public void onControlFrame(ControlFrame frame)
-            {
-                latch.countDown();
-            }
-
-            @Override
-            public void onStreamException(StreamException x)
-            {
-                latch.countDown();
-            }
-        });
-        parser.parse(concatenatedBuffer1);
-        parser.parse(concatenatedBuffer2);
-        parser.parse(concatenatedBuffer3);
-
-        assertThat(latch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    private static class NoCompressionCompressionFactory implements CompressionFactory
-    {
-
-        @Override
-        public Compressor newCompressor()
-        {
-            return null;
-        }
-
-        @Override
-        public Decompressor newDecompressor()
-        {
-            return null;
-        }
-
-        public static class NoCompressionCompressor implements Compressor
-        {
-
-            private byte[] input;
-
-            @Override
-            public void setInput(byte[] input)
-            {
-                this.input = input;
-            }
-
-            @Override
-            public void setDictionary(byte[] dictionary)
-            {
-            }
-
-            @Override
-            public int compress(byte[] output)
-            {
-                System.arraycopy(input, 0, output, 0, input.length);
-                return input.length;
-            }
-        }
-
-        public static class NoCompressionDecompressor implements Decompressor
-        {
-            private byte[] input;
-
-            @Override
-            public void setDictionary(byte[] dictionary)
-            {
-            }
-
-            @Override
-            public void setInput(byte[] input)
-            {
-                this.input = input;
-            }
-
-            @Override
-            public int decompress(byte[] output) throws ZipException
-            {
-                System.arraycopy(input, 0, output, 0, input.length);
-                return input.length;
-            }
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/LiveChromiumRequestParserTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/LiveChromiumRequestParserTest.java
deleted file mode 100644
index 76c3f44..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/LiveChromiumRequestParserTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.ControlFrameType;
-import org.eclipse.jetty.spdy.frames.SynStreamFrame;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class LiveChromiumRequestParserTest
-{
-    @Test
-    public void testSynStream() throws Exception
-    {
-        // Bytes taken with wireshark from a live chromium request
-        byte[] bytes1 = toBytes("" +
-                "800200010100011a0000000100000000000038eadfa251b262e0626083a41706" +
-                "7bb80b75302cd6ae4017cdcdb12eb435d0b3d4d1d2d702b32c18f850732c036f" +
-                "68889bae850e44da94811f2d0b3308821ca80375a14e714a72065c0d2cd619f8" +
-                "52f37443837552f3a076b080b234033f28de73404c2b43630b135306b65c6059" +
-                "929fc2c0ecee1ac2c0560c4c7eb9a940b52525050ccc206f32ea337021f22643" +
-                "bb6f7e55664e4ea2bea99e81824684a1a135400a3e9979a5150a151666f16626" +
-                "9a0a8e40afa686a726796796e89b1a9bea992b68787b84f8fae828e46466a72a" +
-                "b8a72667e76b2a842695e69594ea1b0203d640c1390358e06496e6ea1b9ae901" +
-                "c3c5d048cfdc1c22988a22149c98965894093195811d1a150c1cb01802000000" +
-                "ffff");
-        byte[] bytes2 = toBytes("" +
-                "800200010100002700000003000000008000428a106660d00ee640e5d14f4b2c" +
-                "cb0466313d203154c217000000ffff");
-
-        final AtomicReference<ControlFrame> frameRef = new AtomicReference<>();
-        Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
-        parser.addListener(new Parser.Listener.Adapter()
-        {
-            @Override
-            public void onControlFrame(ControlFrame frame)
-            {
-                frameRef.set(frame);
-            }
-        });
-        parser.parse(ByteBuffer.wrap(bytes1));
-
-        ControlFrame frame = frameRef.get();
-        Assert.assertNotNull(frame);
-        Assert.assertEquals(ControlFrameType.SYN_STREAM, frame.getType());
-        SynStreamFrame synStream = (SynStreamFrame)frame;
-        Assert.assertEquals(2, synStream.getVersion());
-        Assert.assertEquals(1, synStream.getStreamId());
-        Assert.assertEquals(0, synStream.getAssociatedStreamId());
-        Assert.assertEquals(0, synStream.getPriority());
-        Assert.assertNotNull(synStream.getHeaders());
-        Assert.assertFalse(synStream.getHeaders().isEmpty());
-
-        frameRef.set(null);
-        parser.parse(ByteBuffer.wrap(bytes2));
-
-        frame = frameRef.get();
-        Assert.assertNotNull(frame);
-        Assert.assertEquals(ControlFrameType.SYN_STREAM, frame.getType());
-        synStream = (SynStreamFrame)frame;
-        Assert.assertEquals(2, synStream.getVersion());
-        Assert.assertEquals(3, synStream.getStreamId());
-        Assert.assertEquals(0, synStream.getAssociatedStreamId());
-        Assert.assertEquals(2, synStream.getPriority());
-        Assert.assertNotNull(synStream.getHeaders());
-        Assert.assertFalse(synStream.getHeaders().isEmpty());
-    }
-
-    private byte[] toBytes(String hexs)
-    {
-        byte[] bytes = new byte[hexs.length() / 2];
-        for (int i = 0; i < hexs.length(); i += 2)
-        {
-            String hex = hexs.substring(i, i + 2);
-            bytes[i / 2] = (byte)Integer.parseInt(hex, 16);
-        }
-        return bytes;
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/ParseVersusCacheBenchmarkTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/ParseVersusCacheBenchmarkTest.java
deleted file mode 100644
index 10dde4e..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/ParseVersusCacheBenchmarkTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class ParseVersusCacheBenchmarkTest
-{
-    @Ignore
-    @Test
-    public void testParseVersusCache() throws Exception
-    {
-        // The parser knows the header name and value lengths, so it creates strings
-        // out of the bytes; however, this involves creating a byte[] copy the bytes,
-        // and creating a new String.
-        // The alternative is to use a cache<ByteBuffer, String>. Is that faster ?
-        // See also: http://jeremymanson.blogspot.com/2008/04/immutability-in-java.html
-
-        String name = "Content-Type";
-        String value = "application/octect-stream";
-        ByteBuffer buffer = ByteBuffer.wrap((name + value).getBytes(StandardCharsets.ISO_8859_1));
-        int iterations = 100_000_000;
-
-        long begin = System.nanoTime();
-        for (int i = 0; i < iterations; ++i)
-        {
-            byte[] nameBytes = new byte[name.length()];
-            buffer.get(nameBytes);
-            String name2 = new String(nameBytes, StandardCharsets.ISO_8859_1);
-            Assert.assertEquals(name2, name);
-
-            byte[] valueBytes = new byte[value.length()];
-            buffer.get(valueBytes);
-            String value2 = new String(valueBytes, StandardCharsets.ISO_8859_1);
-            Assert.assertEquals(value2, value);
-
-            buffer.flip();
-        }
-        long end = System.nanoTime();
-        System.err.printf("parse time: %d%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
-
-        Map<ByteBuffer, String> map = new HashMap<>();
-        map.put(ByteBuffer.wrap(name.getBytes(StandardCharsets.ISO_8859_1)), name);
-        map.put(ByteBuffer.wrap(value.getBytes(StandardCharsets.ISO_8859_1)), value);
-        final Map<ByteBuffer, String> cache = Collections.unmodifiableMap(map);
-
-        begin = System.nanoTime();
-        for (int i = 0; i < iterations; ++i)
-        {
-            buffer.limit(buffer.position() + name.length());
-            String name2 = cache.get(buffer);
-            Assert.assertEquals(name2, name);
-
-            buffer.position(buffer.limit());
-            buffer.limit(buffer.position() + value.length());
-            String value2 = cache.get(buffer);
-            Assert.assertEquals(value2, value);
-
-            buffer.position(buffer.limit());
-            buffer.flip();
-        }
-        end = System.nanoTime();
-        System.err.printf("cache time: %d%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameTest.java
deleted file mode 100644
index 89c5182..0000000
--- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/parser/UnknownControlFrameTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.parser;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.SessionException;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.StreamException;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.DataFrame;
-import org.eclipse.jetty.spdy.frames.SynStreamFrame;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class UnknownControlFrameTest
-{
-    @Test
-    public void testUnknownControlFrame() throws Exception
-    {
-        SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, new Fields());
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
-        ByteBuffer buffer = generator.control(frame);
-        // Change the frame type to unknown
-        buffer.putShort(2, (short)0);
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        Parser parser = new Parser(new StandardCompressionFactory.StandardDecompressor());
-        parser.addListener(new Parser.Listener.Adapter()
-        {
-            @Override
-            public void onControlFrame(ControlFrame frame)
-            {
-                latch.countDown();
-            }
-
-            @Override
-            public void onDataFrame(DataFrame frame, ByteBuffer data)
-            {
-                latch.countDown();
-            }
-
-            @Override
-            public void onStreamException(StreamException x)
-            {
-                latch.countDown();
-            }
-
-            @Override
-            public void onSessionException(SessionException x)
-            {
-                latch.countDown();
-            }
-        });
-        parser.parse(buffer);
-
-        Assert.assertFalse(latch.await(1, TimeUnit.SECONDS));
-    }
-}
diff --git a/jetty-spdy/spdy-core/src/test/resources/jetty-logging.properties b/jetty-spdy/spdy-core/src/test/resources/jetty-logging.properties
deleted file mode 100644
index 5250a08..0000000
--- a/jetty-spdy/spdy-core/src/test/resources/jetty-logging.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
-org.eclipse.jetty.spdy.LEVEL=WARN
diff --git a/jetty-spdy/spdy-core/src/test/resources/keystore.jks b/jetty-spdy/spdy-core/src/test/resources/keystore.jks
deleted file mode 100644
index 428ba54..0000000
--- a/jetty-spdy/spdy-core/src/test/resources/keystore.jks
+++ /dev/null
Binary files differ
diff --git a/jetty-spdy/spdy-core/src/test/resources/truststore.jks b/jetty-spdy/spdy-core/src/test/resources/truststore.jks
deleted file mode 100644
index 839cb8c..0000000
--- a/jetty-spdy/spdy-core/src/test/resources/truststore.jks
+++ /dev/null
Binary files differ
diff --git a/jetty-spdy/spdy-example-webapp/pom.xml b/jetty-spdy/spdy-example-webapp/pom.xml
deleted file mode 100644
index f0b900a..0000000
--- a/jetty-spdy/spdy-example-webapp/pom.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.spdy</groupId>
-        <artifactId>spdy-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>spdy-example-webapp</artifactId>
-    <packaging>war</packaging>
-    <name>Jetty :: SPDY :: HTTP Web Application</name>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.eclipse.jetty</groupId>
-                <artifactId>jetty-maven-plugin</artifactId>
-                <version>${project.version}</version>
-                <configuration>
-                    <stopPort>8888</stopPort>
-                    <stopKey>quit</stopKey>
-                    <jvmArgs>
-                        -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar
-                    </jvmArgs>
-                    <jettyXml>${basedir}/src/main/config/example-jetty-spdy.xml</jettyXml>
-                    <contextPath>/</contextPath>
-                    <excludedGoals>
-                       <excludedGoal>run</excludedGoal>
-                       <excludedGoal>run-war</excludedGoal>
-                       <excludedGoal>deploy</excludedGoal>
-                       <excludedGoal>start</excludedGoal>
-                       <excludedGoal>stop</excludedGoal>
-                    </excludedGoals>
-                </configuration>
-                <dependencies>
-                    <dependency>
-                        <groupId>org.eclipse.jetty.spdy</groupId>
-                        <artifactId>spdy-http-server</artifactId>
-                        <version>${project.version}</version>
-                    </dependency>
-                </dependencies>
-            </plugin>
-        </plugins>
-    </build>
-
-    <profiles>
-        <profile>
-            <id>proxy</id>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.eclipse.jetty</groupId>
-                        <artifactId>jetty-maven-plugin</artifactId>
-                        <version>${project.version}</version>
-                        <configuration>
-                            <stopPort>8888</stopPort>
-                            <stopKey>quit</stopKey>
-                            <jvmArgs>
-                                -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar
-                            </jvmArgs>
-                            <jettyXml>${basedir}/src/main/config/example-jetty-spdy-proxy.xml</jettyXml>
-                            <contextPath>/</contextPath>
-                            <excludedGoals>
-                               <excludedGoal>run</excludedGoal>
-                               <excludedGoal>run-war</excludedGoal>
-                               <excludedGoal>deploy</excludedGoal>
-                               <excludedGoal>start</excludedGoal>
-                               <excludedGoal>stop</excludedGoal>
-                            </excludedGoals>
-                        </configuration>
-                        <dependencies>
-                            <dependency>
-                                <groupId>org.eclipse.jetty.spdy</groupId>
-                                <artifactId>spdy-http-server</artifactId>
-                                <version>${project.version}</version>
-                            </dependency>
-                        </dependencies>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
-
-</project>
diff --git a/jetty-spdy/spdy-example-webapp/src/main/config/example-jetty-spdy-proxy.xml b/jetty-spdy/spdy-example-webapp/src/main/config/example-jetty-spdy-proxy.xml
deleted file mode 100644
index 7d3d360..0000000
--- a/jetty-spdy/spdy-example-webapp/src/main/config/example-jetty-spdy-proxy.xml
+++ /dev/null
@@ -1,147 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
-
-    <New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
-        <Set name="keyStorePath">src/main/resources/keystore.jks</Set>
-        <Set name="keyStorePassword">storepwd</Set>
-        <Set name="trustStorePath">src/main/resources/truststore.jks</Set>
-        <Set name="trustStorePassword">storepwd</Set>
-        <Set name="protocol">TLSv1</Set>
-    </New>
-
-    <!--
-    <Set class="org.eclipse.jetty.npn.NextProtoNego" name="debug" type="boolean">true</Set>
-    -->
-
-    <New id="tlsHttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
-        <Arg>
-            <New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
-                <Set name="secureScheme">https</Set>
-                <Set name="securePort">
-                    <Property name="jetty.tls.port" default="8443"/>
-                </Set>
-                <Set name="outputBufferSize">32768</Set>
-                <Set name="requestHeaderSize">8192</Set>
-                <Set name="responseHeaderSize">8192</Set>
-
-                <!-- Uncomment to enable handling of X-Forwarded- style headers
-                <Call name="addCustomizer">
-                    <Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
-                </Call>
-                -->
-            </New>
-        </Arg>
-        <Call name="addCustomizer">
-            <Arg>
-                <New class="org.eclipse.jetty.server.SecureRequestCustomizer"/>
-            </Arg>
-        </Call>
-    </New>
-
-    <!--
-    This is the upstream server connector. It speaks non-SSL SPDY/3(HTTP) on port 9090.
-    -->
-    <Call name="addConnector">
-        <Arg>
-            <New class="org.eclipse.jetty.server.ServerConnector">
-                <Arg name="server">
-                    <Ref refid="Server"/>
-                </Arg>
-                <Arg name="factories">
-                    <Array type="org.eclipse.jetty.server.ConnectionFactory">
-                        <!-- SPDY/3 Connection factory -->
-                        <Item>
-                            <New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
-                                <Arg name="version" type="int">3</Arg>
-                                <Arg name="config">
-                                    <Ref refid="tlsHttpConfig"/>
-                                </Arg>
-                            </New>
-                        </Item>
-                    </Array>
-                </Arg>
-                <Set name="port">9090</Set>
-            </New>
-        </Arg>
-    </Call>
-
-    <!--
-    This ProxyEngine translates the incoming SPDY/x(HTTP) request to SPDY/2(HTTP)
-    -->
-    <New id="spdyProxyEngine" class="org.eclipse.jetty.spdy.server.proxy.SPDYProxyEngine">
-        <Arg>
-            <New class="org.eclipse.jetty.spdy.client.SPDYClient$Factory">
-                <Call name="start"/>
-            </New>
-        </Arg>
-    </New>
-
-    <!--
-    The ProxyEngineSelector receives SPDY/x(HTTP) requests from proxy connectors below
-    and is configured to process requests for host "localhost".
-    Such requests are converted from SPDY/x(HTTP) to SPDY/3(HTTP) by the configured ProxyEngine
-    and forwarded to 127.0.0.1:9090, where they are served by the upstream server above.
-    -->
-    <New id="proxyEngineSelector" class="org.eclipse.jetty.spdy.server.proxy.ProxyEngineSelector">
-        <Call name="putProxyEngine">
-            <Arg>spdy/3</Arg>
-            <Arg>
-                <Ref refid="spdyProxyEngine"/>
-            </Arg>
-        </Call>
-        <Set name="proxyServerInfos">
-            <Map>
-                <Entry>
-                    <Item>localhost</Item>
-                    <Item>
-                        <New class="org.eclipse.jetty.spdy.server.proxy.ProxyEngineSelector$ProxyServerInfo">
-                            <Arg type="String">spdy/3</Arg>
-                            <Arg>127.0.0.1</Arg>
-                            <Arg type="int">9090</Arg>
-                        </New>
-                    </Item>
-                </Entry>
-            </Map>
-        </Set>
-    </New>
-
-    <!--
-    These are the reverse proxy connectors accepting requests from clients.
-    They accept non-SSL (on port 8080) and SSL (on port 8443) HTTP,
-    SPDY/2(HTTP) and SPDY/3(HTTP).
-    Non-SPDY HTTP requests are converted to SPDY internally and passed to the
-    ProxyEngine above.
-    -->
-    <Call name="addConnector">
-        <Arg>
-            <New class="org.eclipse.jetty.spdy.server.proxy.HTTPSPDYProxyServerConnector">
-                <Arg>
-                    <Ref refid="Server"/>
-                </Arg>
-                <Arg>
-                    <Ref refid="proxyEngineSelector"/>
-                </Arg>
-                <Set name="Port">8080</Set>
-            </New>
-        </Arg>
-    </Call>
-    <Call name="addConnector">
-        <Arg>
-            <New class="org.eclipse.jetty.spdy.server.proxy.HTTPSPDYProxyServerConnector">
-                <Arg>
-                    <Ref refid="Server"/>
-                </Arg>
-                <Arg>
-                    <Ref refid="sslContextFactory"/>
-                </Arg>
-                <Arg>
-                    <Ref refid="proxyEngineSelector"/>
-                </Arg>
-                <Set name="Port">8443</Set>
-            </New>
-        </Arg>
-    </Call>
-
-</Configure>
diff --git a/jetty-spdy/spdy-example-webapp/src/main/config/example-jetty-spdy.xml b/jetty-spdy/spdy-example-webapp/src/main/config/example-jetty-spdy.xml
deleted file mode 100644
index 47f83be..0000000
--- a/jetty-spdy/spdy-example-webapp/src/main/config/example-jetty-spdy.xml
+++ /dev/null
@@ -1,138 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
-
-    <New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
-        <Set name="keyStorePath">src/main/resources/keystore.jks</Set>
-        <Set name="keyStorePassword">storepwd</Set>
-        <Set name="trustStorePath">src/main/resources/truststore.jks</Set>
-        <Set name="trustStorePassword">storepwd</Set>
-        <Set name="protocol">TLSv1</Set>
-    </New>
-
-    <New id="tlsHttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
-        <Arg>
-            <New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
-                <Set name="secureScheme">https</Set>
-                <Set name="securePort">
-                    <Property name="jetty.tls.port" default="8443"/>
-                </Set>
-                <Set name="outputBufferSize">32768</Set>
-                <Set name="requestHeaderSize">8192</Set>
-                <Set name="responseHeaderSize">8192</Set>
-
-                <!-- Uncomment to enable handling of X-Forwarded- style headers
-                <Call name="addCustomizer">
-                    <Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
-                </Call>
-                -->
-            </New>
-        </Arg>
-        <Call name="addCustomizer">
-            <Arg>
-                <New class="org.eclipse.jetty.server.SecureRequestCustomizer"/>
-            </Arg>
-        </Call>
-    </New>
-
-    <New id="pushStrategy" class="org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy">
-        <!-- Uncomment to blacklist browsers for this push strategy. If one of the blacklisted Strings occurs in the
-             user-agent header sent by the client, push will be disabled for this browser. This is case insensitive" -->
-        <!--
-        <Set name="UserAgentBlacklist">
-            <Array type="String">
-                <Item>.*(?i)firefox/14.*</Item>
-                <Item>.*(?i)firefox/15.*</Item>
-                <Item>.*(?i)firefox/16.*</Item>
-            </Array>
-        </Set>
-        -->
-
-        <!-- Uncomment to override default file extensions to push -->
-        <!--
-        <Set name="PushRegexps">
-            <Array type="String">
-               <Item>.*\.css</Item>
-               <Item>.*\.js</Item>
-               <Item>.*\.png</Item>
-               <Item>.*\.jpg</Item>
-               <Item>.*\.gif</Item>
-           </Array>
-        </Set>
-        -->
-        <Set name="referrerPushPeriod">5000</Set>
-        <Set name="maxAssociatedResources">32</Set>
-    </New>
-
-    <Call id="sslConnector" name="addConnector">
-        <Arg>
-            <New class="org.eclipse.jetty.server.ServerConnector">
-                <Arg name="server"><Ref refid="Server"/></Arg>
-                <Arg name="factories">
-                    <Array type="org.eclipse.jetty.server.ConnectionFactory">
-
-                        <!-- SSL Connection factory with NPN as next protocol -->
-                        <Item>
-                            <New class="org.eclipse.jetty.server.SslConnectionFactory">
-                                <Arg name="next">npn</Arg>
-                                <Arg name="sslContextFactory">
-                                    <Ref refid="sslContextFactory"/>
-                                </Arg>
-                            </New>
-                        </Item>
-
-                        <!-- NPN Connection factory with HTTP as default protocol -->
-                        <Item>
-                            <New class="org.eclipse.jetty.spdy.server.NPNServerConnectionFactory">
-                                <Arg name="protocols">
-                                    <Array type="String">
-                                        <Item>spdy/3</Item>
-                                        <Item>spdy/2</Item>
-                                        <Item>http/1.1</Item>
-                                    </Array>
-                                </Arg>
-                                <Set name="defaultProtocol">http/1.1</Set>
-                            </New>
-                        </Item>
-
-                        <!-- SPDY/3 Connection factory -->
-                        <Item>
-                            <New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
-                                <Arg name="version" type="int">3</Arg>
-                                <Arg name="config">
-                                    <Ref refid="tlsHttpConfig"/>
-                                </Arg>
-                                <Arg name="pushStrategy">
-                                    <Ref refid="pushStrategy"/>
-                                </Arg>
-                            </New>
-                        </Item>
-
-                        <!-- SPDY/2 Connection factory -->
-                        <Item>
-                            <New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
-                                <Arg name="version" type="int">2</Arg>
-                                <Arg name="config">
-                                    <Ref refid="tlsHttpConfig"/>
-                                </Arg>
-                            </New>
-                        </Item>
-
-                        <!-- HTTP Connection factory -->
-                        <Item>
-                            <New class="org.eclipse.jetty.server.HttpConnectionFactory">
-                                <Arg name="config">
-                                    <Ref refid="tlsHttpConfig"/>
-                                </Arg>
-                            </New>
-                        </Item>
-                    </Array>
-                </Arg>
-
-                <Set name="port">8443</Set>
-            </New>
-        </Arg>
-    </Call>
-
-</Configure>
diff --git a/jetty-spdy/spdy-example-webapp/src/main/resources/jetty-logging.properties b/jetty-spdy/spdy-example-webapp/src/main/resources/jetty-logging.properties
deleted file mode 100644
index 5250a08..0000000
--- a/jetty-spdy/spdy-example-webapp/src/main/resources/jetty-logging.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
-org.eclipse.jetty.spdy.LEVEL=WARN
diff --git a/jetty-spdy/spdy-example-webapp/src/main/resources/keystore.jks b/jetty-spdy/spdy-example-webapp/src/main/resources/keystore.jks
deleted file mode 100644
index 428ba54..0000000
--- a/jetty-spdy/spdy-example-webapp/src/main/resources/keystore.jks
+++ /dev/null
Binary files differ
diff --git a/jetty-spdy/spdy-example-webapp/src/main/resources/truststore.jks b/jetty-spdy/spdy-example-webapp/src/main/resources/truststore.jks
deleted file mode 100644
index 839cb8c..0000000
--- a/jetty-spdy/spdy-example-webapp/src/main/resources/truststore.jks
+++ /dev/null
Binary files differ
diff --git a/jetty-spdy/spdy-example-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-spdy/spdy-example-webapp/src/main/webapp/WEB-INF/web.xml
deleted file mode 100644
index eb49319..0000000
--- a/jetty-spdy/spdy-example-webapp/src/main/webapp/WEB-INF/web.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<web-app xmlns="http://java.sun.com/xml/ns/javaee"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
-         version="3.0">
-</web-app>
diff --git a/jetty-spdy/spdy-example-webapp/src/main/webapp/form.jsp b/jetty-spdy/spdy-example-webapp/src/main/webapp/form.jsp
deleted file mode 100644
index 4e41a65..0000000
--- a/jetty-spdy/spdy-example-webapp/src/main/webapp/form.jsp
+++ /dev/null
@@ -1,3 +0,0 @@
-<div>
-    <p>This paragraph has been retrieved via an AJAX call</p>
-</div>
diff --git a/jetty-spdy/spdy-example-webapp/src/main/webapp/included.jsp b/jetty-spdy/spdy-example-webapp/src/main/webapp/included.jsp
deleted file mode 100644
index d69cd4a..0000000
--- a/jetty-spdy/spdy-example-webapp/src/main/webapp/included.jsp
+++ /dev/null
@@ -1,3 +0,0 @@
-<div>
-    <p>This paragraph is an included content via &lt;jsp:include&gt;</p>
-</div>
diff --git a/jetty-spdy/spdy-example-webapp/src/main/webapp/index.jsp b/jetty-spdy/spdy-example-webapp/src/main/webapp/index.jsp
deleted file mode 100644
index f190e57..0000000
--- a/jetty-spdy/spdy-example-webapp/src/main/webapp/index.jsp
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html>
-<head>
-    <title>SPDY TEST PAGE</title>
-    <link rel="stylesheet" href="stylesheet.css" />
-    <script type="text/javascript">
-        function submit()
-        {
-            var xhr = new XMLHttpRequest();
-            xhr.open("POST", "${pageContext.request.contextPath}/form.jsp", false);
-            xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
-            xhr.send("param=1");
-            window.document.getElementById("form").innerHTML = xhr.responseText;
-        }
-    </script>
-</head>
-<body>
-<h2>SPDY TEST PAGE</h2>
-<div>
-    <p><span id="css">This paragraph should have a colored background, meaning that the CSS has been loaded.</span></p>
-</div>
-<div id="image">
-    <p>Below there should be an image</p>
-    <img src="${pageContext.request.contextPath}/logo.jpg"  alt="logo" />
-</div>
-<div>
-    <jsp:include page="included.jsp" />
-</div>
-<div>
-    <p>Click on the button below to perform an AJAX call</p>
-    <button type="button" onclick="submit()">
-        PERFORM AJAX CALL
-    </button>
-    <p id="form"></p>
-</div>
-</body>
-</html>
diff --git a/jetty-spdy/spdy-example-webapp/src/main/webapp/logo.jpg b/jetty-spdy/spdy-example-webapp/src/main/webapp/logo.jpg
deleted file mode 100644
index e8eb8c5..0000000
--- a/jetty-spdy/spdy-example-webapp/src/main/webapp/logo.jpg
+++ /dev/null
Binary files differ
diff --git a/jetty-spdy/spdy-example-webapp/src/main/webapp/stylesheet.css b/jetty-spdy/spdy-example-webapp/src/main/webapp/stylesheet.css
deleted file mode 100644
index 169c339..0000000
--- a/jetty-spdy/spdy-example-webapp/src/main/webapp/stylesheet.css
+++ /dev/null
@@ -1,9 +0,0 @@
-body
-{
-    font-family: Verdana, sans-serif;
-}
-
-#css
-{
-    background: #0FF;
-}
diff --git a/jetty-spdy/spdy-http-client-transport/pom.xml b/jetty-spdy/spdy-http-client-transport/pom.xml
deleted file mode 100644
index 7d2b89b..0000000
--- a/jetty-spdy/spdy-http-client-transport/pom.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.spdy</groupId>
-        <artifactId>spdy-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
-    </parent>
-
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>spdy-http-client-transport</artifactId>
-    <name>Jetty :: SPDY :: HTTP Client Transport</name>
-
-    <properties>
-        <bundle-symbolic-name>${project.groupId}.client.http</bundle-symbolic-name>
-    </properties>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>manifest</goal>
-                        </goals>
-                        <configuration>
-                            <instructions>
-                                <Export-Package>org.eclipse.jetty.spdy.client.http;version="9.1"</Export-Package>
-                                <Import-Package>!org.eclipse.jetty.npn,org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
-                            </instructions>
-                          </configuration>
-                       </execution>
-                  </executions>
-            </plugin>
-        </plugins>
-    </build>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-client</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-client</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-http-common</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-server</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-http-server</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-
-</project>
diff --git a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpChannelOverSPDY.java b/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpChannelOverSPDY.java
deleted file mode 100644
index 0949a05..0000000
--- a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpChannelOverSPDY.java
+++ /dev/null
@@ -1,78 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client.http;
-
-import org.eclipse.jetty.client.HttpChannel;
-import org.eclipse.jetty.client.HttpDestination;
-import org.eclipse.jetty.client.HttpExchange;
-import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.spdy.api.Session;
-
-public class HttpChannelOverSPDY extends HttpChannel
-{
-    private final HttpConnectionOverSPDY connection;
-    private final Session session;
-    private final HttpSenderOverSPDY sender;
-    private final HttpReceiverOverSPDY receiver;
-
-    public HttpChannelOverSPDY(HttpDestination destination, HttpConnectionOverSPDY connection, Session session)
-    {
-        super(destination);
-        this.connection = connection;
-        this.session = session;
-        this.sender = new HttpSenderOverSPDY(this);
-        this.receiver = new HttpReceiverOverSPDY(this);
-    }
-
-    public Session getSession()
-    {
-        return session;
-    }
-
-    public HttpSenderOverSPDY getHttpSender()
-    {
-        return sender;
-    }
-
-    public HttpReceiverOverSPDY getHttpReceiver()
-    {
-        return receiver;
-    }
-
-    @Override
-    public void send()
-    {
-        HttpExchange exchange = getHttpExchange();
-        if (exchange != null)
-            sender.send(exchange);
-    }
-
-    @Override
-    public void release()
-    {
-        connection.release(this);
-    }
-
-    @Override
-    public void exchangeTerminated(HttpExchange exchange, Result result)
-    {
-        super.exchangeTerminated(exchange, result);
-        release();
-    }
-}
diff --git a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpClientTransportOverSPDY.java b/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpClientTransportOverSPDY.java
deleted file mode 100644
index 5fe39ec..0000000
--- a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpClientTransportOverSPDY.java
+++ /dev/null
@@ -1,107 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client.http;
-
-import java.io.IOException;
-import java.net.SocketAddress;
-import java.util.Map;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpClientTransport;
-import org.eclipse.jetty.client.HttpDestination;
-import org.eclipse.jetty.client.Origin;
-import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.io.ClientConnectionFactory;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.util.Promise;
-
-public class HttpClientTransportOverSPDY implements HttpClientTransport
-{
-    private final SPDYClient client;
-    private final ClientConnectionFactory connectionFactory;
-    private HttpClient httpClient;
-
-    public HttpClientTransportOverSPDY(SPDYClient client)
-    {
-        this.client = client;
-        this.connectionFactory = client.getClientConnectionFactory();
-        client.setClientConnectionFactory(new ClientConnectionFactory()
-        {
-            @Override
-            public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
-            {
-                HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
-                return destination.getClientConnectionFactory().newConnection(endPoint, context);
-            }
-        });
-    }
-
-    @Override
-    public void setHttpClient(HttpClient client)
-    {
-        httpClient = client;
-    }
-
-    @Override
-    public HttpDestination newHttpDestination(Origin origin)
-    {
-        return new HttpDestinationOverSPDY(httpClient, origin);
-    }
-
-    @Override
-    public void connect(SocketAddress address, Map<String, Object> context)
-    {
-        final HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
-        @SuppressWarnings("unchecked")
-        final Promise<Connection> promise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
-
-        SessionFrameListener.Adapter listener = new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onFailure(Session session, Throwable x)
-            {
-                destination.abort(x);
-            }
-        };
-
-        client.connect(address, listener, new Promise<Session>()
-        {
-            @Override
-            public void succeeded(Session session)
-            {
-                promise.succeeded(new HttpConnectionOverSPDY(destination, session));
-            }
-
-            @Override
-            public void failed(Throwable x)
-            {
-                promise.failed(x);
-            }
-        }, context);
-    }
-
-    @Override
-    public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
-    {
-        return connectionFactory.newConnection(endPoint, context);
-    }
-}
diff --git a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpConnectionOverSPDY.java b/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpConnectionOverSPDY.java
deleted file mode 100644
index 33f9252..0000000
--- a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpConnectionOverSPDY.java
+++ /dev/null
@@ -1,93 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client.http;
-
-import java.nio.channels.AsynchronousCloseException;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.eclipse.jetty.client.HttpChannel;
-import org.eclipse.jetty.client.HttpConnection;
-import org.eclipse.jetty.client.HttpDestination;
-import org.eclipse.jetty.client.HttpExchange;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.ConcurrentHashSet;
-
-public class HttpConnectionOverSPDY extends HttpConnection
-{
-    private final Set<HttpChannel> channels = new ConcurrentHashSet<>();
-    private final AtomicBoolean closed = new AtomicBoolean();
-    private final Session session;
-
-    public HttpConnectionOverSPDY(HttpDestination destination, Session session)
-    {
-        super(destination);
-        this.session = session;
-    }
-
-    @Override
-    protected void send(HttpExchange exchange)
-    {
-        normalizeRequest(exchange.getRequest());
-        // One connection maps to N channels, so for each exchange we create a new channel
-        HttpChannel channel = new HttpChannelOverSPDY(getHttpDestination(), this, session);
-        channels.add(channel);
-        if (channel.associate(exchange))
-            channel.send();
-        else
-            channel.release();
-    }
-
-    protected void release(HttpChannel channel)
-    {
-        channels.remove(channel);
-    }
-
-    @Override
-    public void close()
-    {
-        if (closed.compareAndSet(false, true))
-        {
-            // First close then abort, to be sure that the connection cannot be reused
-            // from an onFailure() handler or by blocking code waiting for completion.
-            getHttpDestination().close(this);
-            session.goAway(new GoAwayInfo(), Callback.Adapter.INSTANCE);
-            abort(new AsynchronousCloseException());
-        }
-    }
-
-    @Override
-    public boolean isClosed()
-    {
-        return closed.get();
-    }
-
-    private void abort(Throwable failure)
-    {
-        for (HttpChannel channel : channels)
-        {
-            HttpExchange exchange = channel.getHttpExchange();
-            if (exchange != null)
-                exchange.getRequest().abort(failure);
-        }
-        channels.clear();
-    }
-}
diff --git a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpDestinationOverSPDY.java b/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpDestinationOverSPDY.java
deleted file mode 100644
index 144da13..0000000
--- a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpDestinationOverSPDY.java
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client.http;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpExchange;
-import org.eclipse.jetty.client.MultiplexHttpDestination;
-import org.eclipse.jetty.client.Origin;
-
-public class HttpDestinationOverSPDY extends MultiplexHttpDestination<HttpConnectionOverSPDY>
-{
-    public HttpDestinationOverSPDY(HttpClient client, Origin origin)
-    {
-        super(client, origin);
-    }
-
-    @Override
-    protected void send(HttpConnectionOverSPDY connection, HttpExchange exchange)
-    {
-        connection.send(exchange);
-    }
-}
diff --git a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpReceiverOverSPDY.java b/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpReceiverOverSPDY.java
deleted file mode 100644
index f587801..0000000
--- a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpReceiverOverSPDY.java
+++ /dev/null
@@ -1,152 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client.http;
-
-import org.eclipse.jetty.client.HttpExchange;
-import org.eclipse.jetty.client.HttpReceiver;
-import org.eclipse.jetty.client.HttpResponse;
-import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-
-public class HttpReceiverOverSPDY extends HttpReceiver implements StreamFrameListener
-{
-    public HttpReceiverOverSPDY(HttpChannelOverSPDY channel)
-    {
-        super(channel);
-    }
-
-    @Override
-    public HttpChannelOverSPDY getHttpChannel()
-    {
-        return (HttpChannelOverSPDY)super.getHttpChannel();
-    }
-
-    @Override
-    public void onReply(Stream stream, ReplyInfo replyInfo)
-    {
-        HttpExchange exchange = getHttpExchange();
-        if (exchange == null)
-            return;
-
-        try
-        {
-            HttpResponse response = exchange.getResponse();
-
-            Fields fields = replyInfo.getHeaders();
-            short spdy = stream.getSession().getVersion();
-            HttpVersion version = HttpVersion.fromString(fields.get(HTTPSPDYHeader.VERSION.name(spdy)).getValue());
-            response.version(version);
-            String[] status = fields.get(HTTPSPDYHeader.STATUS.name(spdy)).getValue().split(" ", 2);
-
-            Integer code = Integer.parseInt(status[0]);
-            response.status(code);
-            String reason = status.length < 2 ? HttpStatus.getMessage(code) : status[1];
-            response.reason(reason);
-
-            if (responseBegin(exchange))
-            {
-                for (Fields.Field field : fields)
-                {
-                    String name = field.getName();
-                    if (HTTPSPDYHeader.from(spdy, name) != null)
-                        continue;
-                    // TODO: handle multiple values properly
-                    HttpField httpField = new HttpField(name, field.getValue());
-                    responseHeader(exchange, httpField);
-                }
-
-                if (responseHeaders(exchange))
-                {
-                    if (replyInfo.isClose())
-                    {
-                        responseSuccess(exchange);
-                    }
-                }
-            }
-        }
-        catch (Exception x)
-        {
-            responseFailure(x);
-        }
-    }
-
-    @Override
-    public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-    {
-        // SPDY push not supported
-        getHttpChannel().getSession().rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM), Callback.Adapter.INSTANCE);
-        return null;
-    }
-
-    @Override
-    public void onHeaders(Stream stream, HeadersInfo headersInfo)
-    {
-        // TODO: see above handling of headers
-    }
-
-    @Override
-    public void onData(Stream stream, DataInfo dataInfo)
-    {
-        HttpExchange exchange = getHttpExchange();
-        if (exchange == null)
-            return;
-
-        try
-        {
-            int length = dataInfo.length();
-            // TODO: avoid data copy here
-            // TODO: handle callback properly
-            boolean process = responseContent(exchange, dataInfo.asByteBuffer(false), new Callback.Adapter());
-            dataInfo.consume(length);
-
-            if (process)
-            {
-                if (dataInfo.isClose())
-                {
-                    responseSuccess(exchange);
-                }
-            }
-        }
-        catch (Exception x)
-        {
-            responseFailure(x);
-        }
-    }
-
-    @Override
-    public void onFailure(Stream stream, Throwable x)
-    {
-        HttpExchange exchange = getHttpExchange();
-        if (exchange == null)
-            return;
-        exchange.getRequest().abort(x);
-    }
-}
diff --git a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpSenderOverSPDY.java b/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpSenderOverSPDY.java
deleted file mode 100644
index be9cb73..0000000
--- a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpSenderOverSPDY.java
+++ /dev/null
@@ -1,118 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client.http;
-
-import org.eclipse.jetty.client.HttpContent;
-import org.eclipse.jetty.client.HttpExchange;
-import org.eclipse.jetty.client.HttpSender;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-
-public class HttpSenderOverSPDY extends HttpSender
-{
-    private volatile Stream stream;
-
-    public HttpSenderOverSPDY(HttpChannelOverSPDY channel)
-    {
-        super(channel);
-    }
-
-    @Override
-    public HttpChannelOverSPDY getHttpChannel()
-    {
-        return (HttpChannelOverSPDY)super.getHttpChannel();
-    }
-
-    @Override
-    protected void sendHeaders(HttpExchange exchange, final HttpContent content, final Callback callback)
-    {
-        final Request request = exchange.getRequest();
-        final long idleTimeout = request.getIdleTimeout();
-        short spdyVersion = getHttpChannel().getSession().getVersion();
-        Fields fields = new Fields();
-        HttpField hostHeader = null;
-        for (HttpField header : request.getHeaders())
-        {
-            String name = header.getName();
-            // The host header needs a special treatment
-            if (HTTPSPDYHeader.from(spdyVersion, name) != HTTPSPDYHeader.HOST)
-                fields.add(name, header.getValue());
-            else
-                hostHeader = header;
-        }
-
-        // Add special SPDY headers
-        fields.put(HTTPSPDYHeader.METHOD.name(spdyVersion), request.getMethod());
-        String path = request.getPath();
-        String query = request.getQuery();
-        if (query != null)
-            path += "?" + query;
-        fields.put(HTTPSPDYHeader.URI.name(spdyVersion), path);
-        fields.put(HTTPSPDYHeader.VERSION.name(spdyVersion), request.getVersion().asString());
-        if (hostHeader != null)
-            fields.put(HTTPSPDYHeader.HOST.name(spdyVersion), hostHeader.getValue());
-
-        SynInfo synInfo = new SynInfo(fields, !content.hasContent());
-        getHttpChannel().getSession().syn(synInfo, getHttpChannel().getHttpReceiver(), new Promise<Stream>()
-        {
-            @Override
-            public void succeeded(Stream stream)
-            {
-                stream.setIdleTimeout(idleTimeout);
-                if (content.hasContent())
-                    HttpSenderOverSPDY.this.stream = stream;
-                callback.succeeded();
-            }
-
-            @Override
-            public void failed(Throwable failure)
-            {
-                callback.failed(failure);
-            }
-        });
-    }
-
-    @Override
-    protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback)
-    {
-        if (content.isConsumed())
-        {
-            callback.succeeded();
-        }
-        else
-        {
-            ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(content.getByteBuffer(), content.isLast());
-            stream.data(dataInfo, callback);
-        }
-    }
-
-    @Override
-    protected void reset()
-    {
-        super.reset();
-        stream = null;
-    }
-}
diff --git a/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/AbstractHttpClientServerTest.java b/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/AbstractHttpClientServerTest.java
deleted file mode 100644
index a0ee59a..0000000
--- a/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/AbstractHttpClientServerTest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client.http;
-
-import java.util.Arrays;
-import java.util.Collection;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.http.HttpScheme;
-import org.eclipse.jetty.server.AbstractConnectionFactory;
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory;
-import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.junit.After;
-import org.junit.Rule;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public abstract class AbstractHttpClientServerTest
-{
-    @Parameterized.Parameters
-    public static Collection<SslContextFactory[]> parameters()
-    {
-        return Arrays.asList(new SslContextFactory[]{null}, new SslContextFactory[]{new SslContextFactory()});
-    }
-
-    @Rule
-    public final TestTracker tracker = new TestTracker();
-
-    protected SslContextFactory sslContextFactory;
-    protected String scheme;
-    protected Server server;
-    protected ServerConnector connector;
-    protected SPDYClient.Factory factory;
-    protected HttpClient client;
-
-    public AbstractHttpClientServerTest(SslContextFactory sslContextFactory)
-    {
-        this.sslContextFactory = sslContextFactory;
-        this.scheme = (sslContextFactory == null ? HttpScheme.HTTP : HttpScheme.HTTPS).asString();
-    }
-
-    public void start(Handler handler) throws Exception
-    {
-        short version = SPDY.V3;
-
-        HTTPSPDYServerConnectionFactory httpSPDY = new HTTPSPDYServerConnectionFactory(version, new HttpConfiguration());
-        if (sslContextFactory != null)
-        {
-            sslContextFactory.setEndpointIdentificationAlgorithm("");
-            sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
-            sslContextFactory.setKeyStorePassword("storepwd");
-            sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
-            sslContextFactory.setTrustStorePassword("storepwd");
-        }
-
-        server = new Server();
-        connector = new ServerConnector(server, AbstractConnectionFactory.getFactories(sslContextFactory, httpSPDY));
-        server.addConnector(connector);
-        server.setHandler(handler);
-        server.start();
-
-        QueuedThreadPool executor = new QueuedThreadPool();
-        executor.setName(executor.getName() + "-client");
-
-        factory = new SPDYClient.Factory(executor);
-        factory.start();
-        client = new HttpClient(new HttpClientTransportOverSPDY(factory.newSPDYClient(version)), sslContextFactory);
-        client.setExecutor(executor);
-        client.start();
-    }
-
-    @After
-    public void dispose() throws Exception
-    {
-        if (client != null)
-            client.stop();
-        if (factory != null)
-            factory.stop();
-        if (server != null)
-            server.stop();
-    }
-}
diff --git a/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/EmptyServerHandler.java b/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/EmptyServerHandler.java
deleted file mode 100644
index c3030f0..0000000
--- a/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/EmptyServerHandler.java
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client.http;
-
-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.handler.AbstractHandler;
-
-public class EmptyServerHandler extends AbstractHandler
-{
-    @Override
-    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-    {
-        baseRequest.setHandled(true);
-    }
-}
diff --git a/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/HttpClientCustomProxyTest.java b/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/HttpClientCustomProxyTest.java
deleted file mode 100644
index 3ded6c7..0000000
--- a/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/HttpClientCustomProxyTest.java
+++ /dev/null
@@ -1,262 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client.http;
-
-import java.io.IOException;
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.util.Map;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpClientTransport;
-import org.eclipse.jetty.client.HttpDestination;
-import org.eclipse.jetty.client.Origin;
-import org.eclipse.jetty.client.ProxyConfiguration;
-import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.io.AbstractConnection;
-import org.eclipse.jetty.io.ClientConnectionFactory;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.server.AbstractConnectionFactory;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.http.PushStrategy;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class HttpClientCustomProxyTest
-{
-    public static final byte[] CAFE_BABE = new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE};
-
-    private Server server;
-    private ServerConnector connector;
-    private SPDYClient.Factory factory;
-    private HttpClient httpClient;
-
-    public void prepare(Handler handler) throws Exception
-    {
-        server = new Server();
-        connector = new ServerConnector(server, new CAFEBABEServerConnectionFactory(new HTTPSPDYServerConnectionFactory(SPDY.V3, new HttpConfiguration(), new PushStrategy.None())));
-        server.addConnector(connector);
-        server.setHandler(handler);
-        server.start();
-
-        QueuedThreadPool executor = new QueuedThreadPool();
-        executor.setName(executor.getName() + "-client");
-
-        factory = new SPDYClient.Factory(executor);
-        factory.start();
-
-        httpClient = new HttpClient(new HttpClientTransportOverSPDY(factory.newSPDYClient(SPDY.V3)), null);
-        httpClient.setExecutor(executor);
-        httpClient.start();
-    }
-
-    @After
-    public void dispose() throws Exception
-    {
-        if (httpClient != null)
-            httpClient.stop();
-        if (factory != null)
-            factory.stop();
-        if (server != null)
-            server.stop();
-    }
-
-    @Test
-    public void testCustomProxy() throws Exception
-    {
-        final String serverHost = "server";
-        final int status = HttpStatus.NO_CONTENT_204;
-        prepare(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                if (!URI.create(baseRequest.getUri().toString()).isAbsolute())
-                    response.setStatus(HttpServletResponse.SC_USE_PROXY);
-                else if (serverHost.equals(request.getServerName()))
-                    response.setStatus(status);
-                else
-                    response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
-            }
-        });
-
-        // Setup the custom proxy
-        int proxyPort = connector.getLocalPort();
-        int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
-        httpClient.getProxyConfiguration().getProxies().add(new CAFEBABEProxy(new Origin.Address("localhost", proxyPort), false));
-
-        ContentResponse response = httpClient.newRequest(serverHost, serverPort)
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-
-        Assert.assertEquals(status, response.getStatus());
-    }
-
-    private class CAFEBABEProxy extends ProxyConfiguration.Proxy
-    {
-        private CAFEBABEProxy(Origin.Address address, boolean secure)
-        {
-            super(address, secure);
-        }
-
-        @Override
-        public ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory)
-        {
-            return new CAFEBABEClientConnectionFactory(connectionFactory);
-        }
-    }
-
-    private static class CAFEBABEClientConnectionFactory implements ClientConnectionFactory
-    {
-        private final ClientConnectionFactory connectionFactory;
-
-        private CAFEBABEClientConnectionFactory(ClientConnectionFactory connectionFactory)
-        {
-            this.connectionFactory = connectionFactory;
-        }
-
-        @Override
-        public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
-        {
-            HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
-            Executor executor = destination.getHttpClient().getExecutor();
-            return new CAFEBABEConnection(endPoint, executor, connectionFactory, context);
-        }
-    }
-
-    private static class CAFEBABEConnection extends AbstractConnection
-    {
-        private final ClientConnectionFactory connectionFactory;
-        private final Map<String, Object> context;
-
-        public CAFEBABEConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, Map<String, Object> context)
-        {
-            super(endPoint, executor);
-            this.connectionFactory = connectionFactory;
-            this.context = context;
-        }
-
-        @Override
-        public void onOpen()
-        {
-            super.onOpen();
-            fillInterested();
-            getEndPoint().write(new Callback.Adapter(), ByteBuffer.wrap(CAFE_BABE));
-        }
-
-        @Override
-        public void onFillable()
-        {
-            try
-            {
-                ByteBuffer buffer = BufferUtil.allocate(4);
-                int filled = getEndPoint().fill(buffer);
-                Assert.assertEquals(4, filled);
-                Assert.assertArrayEquals(CAFE_BABE, buffer.array());
-
-                // We are good, upgrade the connection
-                ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(getEndPoint(), context));
-            }
-            catch (Throwable x)
-            {
-                close();
-                @SuppressWarnings("unchecked")
-                Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
-                promise.failed(x);
-            }
-        }
-    }
-
-    private class CAFEBABEServerConnectionFactory extends AbstractConnectionFactory
-    {
-        private final org.eclipse.jetty.server.ConnectionFactory connectionFactory;
-
-        private CAFEBABEServerConnectionFactory(org.eclipse.jetty.server.ConnectionFactory connectionFactory)
-        {
-            super("cafebabe");
-            this.connectionFactory = connectionFactory;
-        }
-
-        @Override
-        public org.eclipse.jetty.io.Connection newConnection(Connector connector, EndPoint endPoint)
-        {
-            return new CAFEBABEServerConnection(connector, endPoint, connectionFactory);
-        }
-    }
-
-    private class CAFEBABEServerConnection extends AbstractConnection
-    {
-        private final org.eclipse.jetty.server.ConnectionFactory connectionFactory;
-
-        public CAFEBABEServerConnection(Connector connector, EndPoint endPoint, org.eclipse.jetty.server.ConnectionFactory connectionFactory)
-        {
-            super(endPoint, connector.getExecutor());
-            this.connectionFactory = connectionFactory;
-        }
-
-        @Override
-        public void onOpen()
-        {
-            super.onOpen();
-            fillInterested();
-        }
-
-        @Override
-        public void onFillable()
-        {
-            try
-            {
-                ByteBuffer buffer = BufferUtil.allocate(4);
-                int filled = getEndPoint().fill(buffer);
-                Assert.assertEquals(4, filled);
-                Assert.assertArrayEquals(CAFE_BABE, buffer.array());
-                getEndPoint().write(new Callback.Adapter(), buffer);
-
-                // We are good, upgrade the connection
-                ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(connector, getEndPoint()));
-            }
-            catch (Throwable x)
-            {
-                close();
-            }
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/HttpClientTest.java b/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/HttpClientTest.java
deleted file mode 100644
index 99f0fdb..0000000
--- a/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/HttpClientTest.java
+++ /dev/null
@@ -1,467 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.client.http;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URLEncoder;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.zip.GZIPOutputStream;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.client.util.BytesContentProvider;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.toolchain.test.annotation.Slow;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class HttpClientTest extends AbstractHttpClientServerTest
-{
-    public HttpClientTest(SslContextFactory sslContextFactory)
-    {
-        super(sslContextFactory);
-    }
-
-    @Test
-    public void test_GET_ResponseWithoutContent() throws Exception
-    {
-        start(new EmptyServerHandler());
-
-        Response response = client.GET(scheme + "://localhost:" + connector.getLocalPort());
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-    }
-
-    @Test
-    public void test_GET_ResponseWithContent() throws Exception
-    {
-        final byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                response.getOutputStream().write(data);
-                baseRequest.setHandled(true);
-            }
-        });
-
-        ContentResponse response = client.GET(scheme + "://localhost:" + connector.getLocalPort());
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-        byte[] content = response.getContent();
-        Assert.assertArrayEquals(data, content);
-    }
-
-    @Test
-    public void test_GET_WithParameters_ResponseWithContent() throws Exception
-    {
-        final String paramName1 = "a";
-        final String paramName2 = "b";
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                response.setCharacterEncoding("UTF-8");
-                ServletOutputStream output = response.getOutputStream();
-                String paramValue1 = request.getParameter(paramName1);
-                output.write(paramValue1.getBytes("UTF-8"));
-                String paramValue2 = request.getParameter(paramName2);
-                Assert.assertEquals("", paramValue2);
-                output.write("empty".getBytes("UTF-8"));
-                baseRequest.setHandled(true);
-            }
-        });
-
-        String value1 = "\u20AC";
-        String paramValue1 = URLEncoder.encode(value1, "UTF-8");
-        String query = paramName1 + "=" + paramValue1 + "&" + paramName2;
-        ContentResponse response = client.GET(scheme + "://localhost:" + connector.getLocalPort() + "/?" + query);
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-        String content = new String(response.getContent(), "UTF-8");
-        Assert.assertEquals(value1 + "empty", content);
-    }
-
-    @Test
-    public void test_GET_WithParametersMultiValued_ResponseWithContent() throws Exception
-    {
-        final String paramName1 = "a";
-        final String paramName2 = "b";
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                response.setCharacterEncoding("UTF-8");
-                ServletOutputStream output = response.getOutputStream();
-                String[] paramValues1 = request.getParameterValues(paramName1);
-                for (String paramValue : paramValues1)
-                    output.write(paramValue.getBytes("UTF-8"));
-                String paramValue2 = request.getParameter(paramName2);
-                output.write(paramValue2.getBytes("UTF-8"));
-                baseRequest.setHandled(true);
-            }
-        });
-
-        String value11 = "\u20AC";
-        String value12 = "\u20AA";
-        String value2 = "&";
-        String paramValue11 = URLEncoder.encode(value11, "UTF-8");
-        String paramValue12 = URLEncoder.encode(value12, "UTF-8");
-        String paramValue2 = URLEncoder.encode(value2, "UTF-8");
-        String query = paramName1 + "=" + paramValue11 + "&" + paramName1 + "=" + paramValue12 + "&" + paramName2 + "=" + paramValue2;
-        ContentResponse response = client.GET(scheme + "://localhost:" + connector.getLocalPort() + "/?" + query);
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-        String content = new String(response.getContent(), "UTF-8");
-        Assert.assertEquals(value11 + value12 + value2, content);
-    }
-
-    @Test
-    public void test_POST_WithParameters() throws Exception
-    {
-        final String paramName = "a";
-        final String paramValue = "\u20AC";
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                String value = request.getParameter(paramName);
-                if (paramValue.equals(value))
-                {
-                    response.setCharacterEncoding("UTF-8");
-                    response.setContentType("text/plain");
-                    response.getOutputStream().print(value);
-                }
-            }
-        });
-
-        ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
-                .param(paramName, paramValue)
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-        Assert.assertEquals(paramValue, new String(response.getContent(), "UTF-8"));
-    }
-
-    @Test
-    public void test_PUT_WithParameters() throws Exception
-    {
-        final String paramName = "a";
-        final String paramValue = "\u20AC";
-        final String encodedParamValue = URLEncoder.encode(paramValue, "UTF-8");
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                String value = request.getParameter(paramName);
-                if (paramValue.equals(value))
-                {
-                    response.setCharacterEncoding("UTF-8");
-                    response.setContentType("text/plain");
-                    response.getOutputStream().print(value);
-                }
-            }
-        });
-
-        URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort() + "/path?" + paramName + "=" + encodedParamValue);
-        ContentResponse response = client.newRequest(uri)
-                .method(HttpMethod.PUT)
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-        Assert.assertEquals(paramValue, new String(response.getContent(), "UTF-8"));
-    }
-
-    @Test
-    public void test_POST_WithParameters_WithContent() throws Exception
-    {
-        final byte[] content = {0, 1, 2, 3};
-        final String paramName = "a";
-        final String paramValue = "\u20AC";
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                String value = request.getParameter(paramName);
-                if (paramValue.equals(value))
-                {
-                    response.setCharacterEncoding("UTF-8");
-                    response.setContentType("application/octet-stream");
-                    response.getOutputStream().write(content);
-                }
-            }
-        });
-
-        ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort() + "/?b=1")
-                .param(paramName, paramValue)
-                .content(new BytesContentProvider(content))
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-        Assert.assertArrayEquals(content, response.getContent());
-    }
-
-    @Test
-    public void test_POST_WithContent_NotifiesRequestContentListener() throws Exception
-    {
-        final byte[] content = {0, 1, 2, 3};
-        start(new EmptyServerHandler());
-
-        ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
-                .onRequestContent(new Request.ContentListener()
-                {
-                    @Override
-                    public void onContent(Request request, ByteBuffer buffer)
-                    {
-                        byte[] bytes = new byte[buffer.remaining()];
-                        buffer.get(bytes);
-                        if (!Arrays.equals(content, bytes))
-                            request.abort(new Exception());
-                    }
-                })
-                .content(new BytesContentProvider(content))
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-    }
-
-    @Test
-    public void test_POST_WithContent_TracksProgress() throws Exception
-    {
-        start(new EmptyServerHandler());
-
-        final AtomicInteger progress = new AtomicInteger();
-        ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
-                .onRequestContent(new Request.ContentListener()
-                {
-                    @Override
-                    public void onContent(Request request, ByteBuffer buffer)
-                    {
-                        byte[] bytes = new byte[buffer.remaining()];
-                        Assert.assertEquals(1, bytes.length);
-                        buffer.get(bytes);
-                        Assert.assertEquals(bytes[0], progress.getAndIncrement());
-                    }
-                })
-                .content(new BytesContentProvider(new byte[]{0}, new byte[]{1}, new byte[]{2}, new byte[]{3}, new byte[]{4}))
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-        Assert.assertEquals(5, progress.get());
-    }
-
-    @Test
-    public void test_GZIP_ContentEncoding() throws Exception
-    {
-        final byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                response.setHeader("Content-Encoding", "gzip");
-                GZIPOutputStream gzipOutput = new GZIPOutputStream(response.getOutputStream());
-                gzipOutput.write(data);
-                gzipOutput.finish();
-            }
-        });
-
-        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-
-        Assert.assertEquals(200, response.getStatus());
-        Assert.assertArrayEquals(data, response.getContent());
-    }
-
-    @Slow
-    @Test
-    public void test_Request_IdleTimeout() throws Exception
-    {
-        final long idleTimeout = 1000;
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                try
-                {
-                    baseRequest.setHandled(true);
-                    TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
-                }
-                catch (InterruptedException x)
-                {
-                    throw new ServletException(x);
-                }
-            }
-        });
-
-        final String host = "localhost";
-        final int port = connector.getLocalPort();
-        try
-        {
-            client.newRequest(host, port)
-                    .scheme(scheme)
-                    .idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
-                    .timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
-                    .send();
-            Assert.fail();
-        }
-        catch (ExecutionException expected)
-        {
-            Assert.assertTrue(expected.getCause() instanceof TimeoutException);
-        }
-
-        // Make another request without specifying the idle timeout, should not fail
-        ContentResponse response = client.newRequest(host, port)
-                .scheme(scheme)
-                .timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
-                .send();
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-    }
-
-    @Test
-    public void testSendToIPv6Address() throws Exception
-    {
-        start(new EmptyServerHandler());
-
-        ContentResponse response = client.newRequest("[::1]", connector.getLocalPort())
-                .scheme(scheme)
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-    }
-
-    @Test
-    public void test_HEAD_With_ResponseContentLength() throws Exception
-    {
-        final int length = 1024;
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                response.getOutputStream().write(new byte[length]);
-            }
-        });
-
-        // HEAD requests receive a Content-Length header, but do not
-        // receive the content so they must handle this case properly
-        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .method(HttpMethod.HEAD)
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-        Assert.assertEquals(0, response.getContent().length);
-
-        // Perform a normal GET request to be sure the content is now read
-        response = client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
-
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getStatus());
-        Assert.assertEquals(length, response.getContent().length);
-    }
-
-    @Test
-    public void testLongPollIsAbortedWhenClientIsStopped() throws Exception
-    {
-        final CountDownLatch latch = new CountDownLatch(1);
-        start(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                request.startAsync();
-                latch.countDown();
-            }
-        });
-
-        final CountDownLatch completeLatch = new CountDownLatch(1);
-        client.newRequest("localhost", connector.getLocalPort())
-                .scheme(scheme)
-                .send(new Response.CompleteListener()
-                {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        if (result.isFailed())
-                            completeLatch.countDown();
-                    }
-                });
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-
-        // Stop the client, the complete listener must be invoked.
-        client.stop();
-
-        Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
-    }
-}
diff --git a/jetty-spdy/spdy-http-client-transport/src/test/resources/jetty-logging.properties b/jetty-spdy/spdy-http-client-transport/src/test/resources/jetty-logging.properties
deleted file mode 100644
index 8163013..0000000
--- a/jetty-spdy/spdy-http-client-transport/src/test/resources/jetty-logging.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
-#org.eclipse.jetty.LEVEL=DEBUG
-#org.eclipse.jetty.client.LEVEL=DEBUG
-#org.eclipse.jetty.spdy.LEVEL=DEBUG
diff --git a/jetty-spdy/spdy-http-common/pom.xml b/jetty-spdy/spdy-http-common/pom.xml
deleted file mode 100644
index 95826e6..0000000
--- a/jetty-spdy/spdy-http-common/pom.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.spdy</groupId>
-        <artifactId>spdy-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
-    </parent>
-
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>spdy-http-common</artifactId>
-    <name>Jetty :: SPDY :: HTTP Common</name>
-
-    <properties>
-        <bundle-symbolic-name>${project.groupId}.http.common</bundle-symbolic-name>
-    </properties>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>manifest</goal>
-                        </goals>
-                        <configuration>
-                            <instructions>
-                                <Export-Package>org.eclipse.jetty.spdy.http;version="9.1"</Export-Package>
-                                <Import-Package>!org.eclipse.jetty.npn,org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
-                            </instructions>
-                          </configuration>
-                       </execution>
-                  </executions>
-            </plugin>
-        </plugins>
-    </build>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-core</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-    </dependencies>
-
-</project>
diff --git a/jetty-spdy/spdy-http-common/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYHeader.java b/jetty-spdy/spdy-http-common/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYHeader.java
deleted file mode 100644
index b606479..0000000
--- a/jetty-spdy/spdy-http-common/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYHeader.java
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.http;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.eclipse.jetty.spdy.api.SPDY;
-
-/**
- * <p>{@link HTTPSPDYHeader} defines the SPDY headers that are not also HTTP headers,
- * such as <tt>method</tt>, <tt>version</tt>, etc. or that are treated differently
- * by the SPDY protocol, such as <tt>host</tt>.</p>
- */
-public enum HTTPSPDYHeader
-{
-    METHOD("method", ":method"),
-    URI("url", ":path"),
-    VERSION("version", ":version"),
-    SCHEME("scheme", ":scheme"),
-    HOST("host", ":host"),
-    STATUS("status", ":status");
-
-    public static HTTPSPDYHeader from(short version, String name)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-                return Names.v2Names.get(name);
-            case SPDY.V3:
-                return Names.v3Names.get(name);
-            default:
-                throw new IllegalStateException();
-        }
-    }
-
-    private final String v2Name;
-    private final String v3Name;
-
-    private HTTPSPDYHeader(String v2Name, String v3Name)
-    {
-        this.v2Name = v2Name;
-        Names.v2Names.put(v2Name, this);
-        this.v3Name = v3Name;
-        Names.v3Names.put(v3Name, this);
-    }
-
-    public String name(short version)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-                return v2Name;
-            case SPDY.V3:
-                return v3Name;
-            default:
-                throw new IllegalStateException();
-        }
-    }
-
-    private static class Names
-    {
-        private static final Map<String, HTTPSPDYHeader> v2Names = new HashMap<>();
-        private static final Map<String, HTTPSPDYHeader> v3Names = new HashMap<>();
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/pom.xml b/jetty-spdy/spdy-http-server/pom.xml
deleted file mode 100644
index fcc139f..0000000
--- a/jetty-spdy/spdy-http-server/pom.xml
+++ /dev/null
@@ -1,120 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.spdy</groupId>
-        <artifactId>spdy-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>spdy-http-server</artifactId>
-    <name>Jetty :: SPDY :: HTTP Server</name>
-
-    <properties>
-        <bundle-symbolic-name>${project.groupId}.http.server</bundle-symbolic-name>
-    </properties>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-assembly-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>single</goal>
-                        </goals>
-                        <configuration>
-                            <descriptorRefs>
-                                <descriptorRef>config</descriptorRef>
-                            </descriptorRefs>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>artifact-jars</id>
-                        <goals>
-                            <goal>jar</goal>
-                            <goal>test-jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>manifest</goal>
-                        </goals>
-                        <configuration>
-                            <instructions>
-                                <Export-Package>org.eclipse.jetty.spdy.server.http;version="9.1",
-                                    org.eclipse.jetty.spdy.server.proxy;version="9.1"
-                                </Export-Package>
-                                <Import-Package>!org.eclipse.jetty.npn,org.eclipse.jetty.*;version="[9.0,10.0)",*
-                                </Import-Package>
-                                <_nouses>true</_nouses>
-                            </instructions>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-http-common</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-server</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-client</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-servlet</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-servlets</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.npn</groupId>
-            <artifactId>npn-api</artifactId>
-            <version>${npn.api.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-continuation</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-
-</project>
diff --git a/jetty-spdy/spdy-http-server/src/main/config/etc/jetty-spdy-proxy.xml b/jetty-spdy/spdy-http-server/src/main/config/etc/jetty-spdy-proxy.xml
deleted file mode 100644
index c525bd1..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/etc/jetty-spdy-proxy.xml
+++ /dev/null
@@ -1,158 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<!-- ============================================================= -->
-<!-- Configure the Jetty Server instance with an ID "Server"       -->
-<!-- by adding a SPDY connector.                                   -->
-<!-- This configuration must be used in conjunction with jetty.xml -->
-<!-- It should not be used with jetty-https.xml as this connector  -->
-<!-- can provide both HTTPS and SPDY connections                   -->
-<!-- ============================================================= -->
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
-
-  <!-- =========================================================== -->
-  <!-- Setup the SSL Context factory used to establish all TLS     -->
-  <!-- Connections and session.                                    -->
-  <!--                                                             -->
-  <!-- Consult the javadoc of o.e.j.util.ssl.SslContextFactory     -->
-  <!-- o.e.j.server.HttpConnectionFactory for all configuration    -->
-  <!-- that may be set here.                                       -->
-  <!-- =========================================================== -->
-  <New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
-    <Set name="KeyStorePath"><Property name="jetty.home" default="." />/etc/keystore</Set>
-    <Set name="KeyStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
-    <Set name="KeyManagerPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
-    <Set name="TrustStorePath"><Property name="jetty.home" default="." />/etc/keystore</Set>
-    <Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
-  </New>
-
-  <!-- =========================================================== -->
-  <!-- Enables NPN debugging on System.err                         -->
-  <!-- ===========================================================
-  <Set class="org.eclipse.jetty.npn.NextProtoNego" name="debug" type="boolean">true</Set>
-  -->
-
-  <!-- =========================================================== -->
-  <!-- Create a TLS specific HttpConfiguration based on the        -->
-  <!-- common HttpConfiguration defined in jetty.xml               -->
-  <!-- Add a SecureRequestCustomizer to extract certificate and    -->
-  <!-- session information                                         -->
-  <!-- =========================================================== -->
-  <New id="tlsHttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
-    <Arg><Ref refid="httpConfig"/></Arg>
-    <Call name="addCustomizer">
-      <Arg><New class="org.eclipse.jetty.server.SecureRequestCustomizer"/></Arg>
-    </Call>
-  </New>
-
-  <!-- =========================================================== -->
-  <!-- This is the upstream server connector.                      -->
-  <!-- It speaks non-SSL SPDY/3(HTTP) on port 9090.                -->
-  <!-- =========================================================== -->
-  <Call name="addConnector">
-    <Arg>
-      <New class="org.eclipse.jetty.server.ServerConnector">
-        <Arg name="server">
-          <Ref refid="Server"/>
-        </Arg>
-        <Arg name="factories">
-          <Array type="org.eclipse.jetty.server.ConnectionFactory">
-            <!-- SPDY/3 Connection factory -->
-            <Item>
-              <New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
-                <Arg name="version" type="int">3</Arg>
-                <Arg name="config">
-                  <Ref refid="tlsHttpConfig"/>
-                </Arg>
-              </New>
-            </Item>
-          </Array>
-        </Arg>
-        <Set name="port">9090</Set>
-      </New>
-    </Arg>
-  </Call>
-
-  <!-- =========================================================== -->
-  <!-- This ProxyEngine translates the incoming SPDY/x(HTTP)       -->
-  <!-- requests to SPDY/2(HTTP)                                    -->
-  <!-- =========================================================== -->
-  <New id="spdyProxyEngine" class="org.eclipse.jetty.spdy.server.proxy.SPDYProxyEngine">
-    <Arg>
-      <New class="org.eclipse.jetty.spdy.client.SPDYClient$Factory">
-        <Call name="start"/>
-      </New>
-    </Arg>
-  </New>
-
-  <!-- =========================================================== -->
-  <!-- The ProxyEngineSelector receives SPDY/x(HTTP) requests      -->
-  <!-- from proxy connectors below and is configured to process    -->
-  <!-- requests for host "localhost".                              -->
-  <!-- Such requests are converted from SPDY/x(HTTP) to            -->
-  <!-- SPDY/3(HTTP) by the configured ProxyEngine and forwarded    -->
-  <!-- to 127.0.0.1:9090, where they are served by the upstream    -->
-  <!-- server above.                                               -->
-  <!-- =========================================================== -->
-  <New id="proxyEngineSelector" class="org.eclipse.jetty.spdy.server.proxy.ProxyEngineSelector">
-    <Call name="putProxyEngine">
-      <Arg>spdy/3</Arg>
-      <Arg>
-        <Ref refid="spdyProxyEngine"/>
-      </Arg>
-    </Call>
-    <Set name="proxyServerInfos">
-      <Map>
-        <Entry>
-          <Item>localhost</Item>
-          <Item>
-            <New class="org.eclipse.jetty.spdy.server.proxy.ProxyEngineSelector$ProxyServerInfo">
-              <Arg type="String">spdy/3</Arg>
-              <Arg>127.0.0.1</Arg>
-              <Arg type="int">9090</Arg>
-            </New>
-          </Item>
-        </Entry>
-      </Map>
-    </Set>
-  </New>
-
-  <!-- =========================================================== -->
-  <!-- These are the reverse proxy connectors accepting requests   -->
-  <!-- from clients.                                               -->
-  <!-- They accept non-SSL (on port 8080) and SSL (on port 8443)   -->
-  <!-- HTTP, SPDY/2(HTTP) and SPDY/3(HTTP).                        -->
-  <!-- Non-SPDY HTTP requests are converted to SPDY internally     -->
-  <!-- and passed to the ProxyEngine above.                        -->
-  <!-- =========================================================== -->
-  <Call name="addConnector">
-    <Arg>
-      <New class="org.eclipse.jetty.spdy.server.proxy.HTTPSPDYProxyServerConnector">
-        <Arg>
-          <Ref refid="Server"/>
-        </Arg>
-        <Arg>
-          <Ref refid="proxyEngineSelector"/>
-        </Arg>
-        <Set name="Port">8080</Set>
-      </New>
-    </Arg>
-  </Call>
-  <Call name="addConnector">
-    <Arg>
-      <New class="org.eclipse.jetty.spdy.server.proxy.HTTPSPDYProxyServerConnector">
-        <Arg>
-          <Ref refid="Server"/>
-        </Arg>
-        <Arg>
-          <Ref refid="sslContextFactory"/>
-        </Arg>
-        <Arg>
-          <Ref refid="proxyEngineSelector"/>
-        </Arg>
-        <Set name="Port">8443</Set>
-      </New>
-    </Arg>
-  </Call>
-
-</Configure>
diff --git a/jetty-spdy/spdy-http-server/src/main/config/etc/jetty-spdy.xml b/jetty-spdy/spdy-http-server/src/main/config/etc/jetty-spdy.xml
deleted file mode 100644
index b094d7c..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/etc/jetty-spdy.xml
+++ /dev/null
@@ -1,139 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<!-- ============================================================= -->
-<!-- Configure a SPDY connector.                                   -->
-<!-- This configuration must be used in conjunction with jetty.xml -->
-<!-- and jetty-ssl.xml                                             -->
-<!-- ============================================================= -->
-<Configure id="Server" class="org.eclipse.jetty.server.Server">
-
-    <!-- =========================================================== -->
-    <!-- Create a push strategy which can be used by reference by    -->
-    <!-- individual connection factories below.                      -->
-    <!--                                                             -->
-    <!-- Consult the javadoc of o.e.j.spdy.server.http.ReferrerPushStrategy -->
-    <!-- for all configuration that may be set here.                 -->
-    <!-- =========================================================== -->
-    <New id="pushStrategy" class="org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy">
-        <!-- Uncomment to blacklist browsers for this push strategy. If one of the blacklisted Strings occurs in the
-             user-agent header sent by the client, push will be disabled for this browser. This is case insensitive" -->
-        <!--
-        <Set name="UserAgentBlacklist">
-            <Array type="String">
-                <Item>.*(?i)firefox/14.*</Item>
-                <Item>.*(?i)firefox/15.*</Item>
-                <Item>.*(?i)firefox/16.*</Item>
-            </Array>
-        </Set>
-        -->
-
-        <!-- Uncomment to override default file extensions to push -->
-        <!--
-        <Set name="PushRegexps">
-            <Array type="String">
-               <Item>.*\.css</Item>
-               <Item>.*\.js</Item>
-               <Item>.*\.png</Item>
-               <Item>.*\.jpg</Item>
-               <Item>.*\.gif</Item>
-           </Array>
-        </Set>
-        -->
-        <Set name="referrerPushPeriod">5000</Set>
-        <Set name="maxAssociatedResources">32</Set>
-    </New>
-
-    <!-- =========================================================== -->
-    <!-- Add a SPDY/HTTPS Connector.                                 -->
-    <!-- Configure an o.e.j.server.ServerConnector with connection   -->
-    <!-- factories for TLS (aka SSL), ProtoNego, SPDY and HTTP to    -->
-    <!-- provide a connector that can accept HTTPS or SPDY           -->
-    <!-- connections.                                                -->
-    <!--                                                             -->
-    <!-- All accepted TLS connections are initially wired to a       -->
-    <!-- Protonego connection, which attempts to use a TLS extension -->
-    <!-- to negotiation the protocol.  If it is not supported by     -->
-    <!-- the client, then the connection is replaced by a HTTP       -->
-    <!-- connection.  If a specific protocol version (eg spdy/3) is  -->
-    <!-- negotiated, then the appropriate connection factory         -->
-    <!-- is used to create a connection to replace the connection    -->
-    <!--                                                             -->
-    <!-- The final result is a SPDY or HTTP connection wired behind  -->
-    <!-- a TLS (aka SSL) connection.                                 -->
-    <!--                                                             -->
-    <!-- Consult the javadoc of o.e.j.server.ServerConnector and the -->
-    <!-- specific connection factory types for all configuration     -->
-    <!-- that may be set here.                                       -->
-    <!-- =========================================================== -->
-    <Call id="spdyConnector" name="addConnector">
-        <Arg>
-            <New class="org.eclipse.jetty.server.ServerConnector">
-                <Arg name="server">
-                    <Ref refid="Server"/>
-                </Arg>
-                <Arg name="factories">
-                    <Array type="org.eclipse.jetty.server.ConnectionFactory">
-
-                        <!-- SSL Connection factory with Protonego as next protocol -->
-                        <Item>
-                            <New class="org.eclipse.jetty.server.SslConnectionFactory">
-                                <Arg name="next"><Property name="protonego"/></Arg>
-                                <Arg name="sslContextFactory">
-                                    <Ref refid="sslContextFactory"/>
-                                </Arg>
-                            </New>
-                        </Item>
-
-                        <!-- NPN Connection factory with HTTP as default protocol -->
-                        <Item>
-			    <Ref refid="protonego"/>
-                        </Item>
-
-                        <!-- SPDY/3 Connection factory -->
-                        <Item>
-                            <New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
-                                <Arg name="version" type="int">3</Arg>
-                                <Arg name="config">
-                                    <Ref refid="sslHttpConfig"/>
-                                </Arg>
-                                <!-- Set the initial window size for this SPDY connector. -->
-                                <!-- See: http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-2.6.8-WINDOW_UPDATE -->
-                                <Set name="initialWindowSize"><Property name="spdy.initialWindowSize" default="65536"/></Set>
-                                <!-- Uncomment to enable ReferrerPushStrategy -->
-                                <!--<Arg name="pushStrategy"><Ref refid="pushStrategy"/></Arg>-->
-                            </New>
-                        </Item>
-
-                        <!-- SPDY/2 Connection factory -->
-                        <Item>
-                            <New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
-                                <Arg name="version" type="int">2</Arg>
-                                <Arg name="config">
-                                    <Ref refid="sslHttpConfig"/>
-                                </Arg>
-                                <!-- Set the initial window size for this SPDY connector. -->
-                                <!-- See: http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-2.6.8-WINDOW_UPDATE -->
-                                <Set name="initialWindowSize"><Property name="spdy.initialWindowSize" default="65536"/></Set>
-                            </New>
-                        </Item>
-
-                        <!-- HTTP Connection factory -->
-                        <Item>
-                            <New class="org.eclipse.jetty.server.HttpConnectionFactory">
-                                <Arg name="config">
-                                    <Ref refid="sslHttpConfig"/>
-                                </Arg>
-                            </New>
-                        </Item>
-                    </Array>
-                </Arg>
-
-                <Set name="host"><Property name="jetty.host"/></Set>
-                <Set name="port"><Property name="spdy.port" default="443"/></Set>
-                <Set name="idleTimeout"><Property name="spdy.timeout" default="30000"/></Set>
-            </New>
-        </Arg>
-    </Call>
-
-</Configure>
diff --git a/jetty-spdy/spdy-http-server/src/main/config/etc/protonego-npn.xml b/jetty-spdy/spdy-http-server/src/main/config/etc/protonego-npn.xml
deleted file mode 100644
index 6e30f39..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/etc/protonego-npn.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<Configure id="protonego" class="org.eclipse.jetty.spdy.server.NPNServerConnectionFactory">
-    <Arg name="protocols">
-	<Array type="String">
-	    <Item>spdy/3</Item>
-	    <Item>spdy/2</Item>
-	    <Item>http/1.1</Item>
-	</Array>
-    </Arg>
-   
-    <Set name="defaultProtocol">http/1.1</Set>
-
-    <!-- =========================================================== -->
-    <!-- Enables NPN debugging on System.err                         -->
-    <!-- ===========================================================
-     <Set class="org.eclipse.jetty.npn.NextProtoNego" name="debug" type="boolean">true</Set>
-    -->
-
-</Configure>
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_04.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_04.mod
deleted file mode 100644
index 007570b..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_04.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.0.v20120525/npn-boot-1.1.0.v20120525.jar|lib/npn/npn-boot-1.1.0.v20120525.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.0.v20120525.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_05.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_05.mod
deleted file mode 100644
index 007570b..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_05.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.0.v20120525/npn-boot-1.1.0.v20120525.jar|lib/npn/npn-boot-1.1.0.v20120525.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.0.v20120525.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_06.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_06.mod
deleted file mode 100644
index 868a7a7..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_06.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.1.v20121030/npn-boot-1.1.1.v20121030.jar|lib/npn/npn-boot-1.1.1.v20121030.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.1.v20121030.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_07.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_07.mod
deleted file mode 100644
index 868a7a7..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_07.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.1.v20121030/npn-boot-1.1.1.v20121030.jar|lib/npn/npn-boot-1.1.1.v20121030.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.1.v20121030.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_09.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_09.mod
deleted file mode 100644
index 20c1db2..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_09.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar|lib/npn/npn-boot-1.1.3.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.3.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_10.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_10.mod
deleted file mode 100644
index 20c1db2..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_10.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar|lib/npn/npn-boot-1.1.3.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.3.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_11.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_11.mod
deleted file mode 100644
index 20c1db2..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_11.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar|lib/npn/npn-boot-1.1.3.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.3.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_13.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_13.mod
deleted file mode 100644
index 1645a52..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_13.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.4.v20130313/npn-boot-1.1.4.v20130313.jar|lib/npn/npn-boot-1.1.4.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.4.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_15.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_15.mod
deleted file mode 100644
index 73bc090..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_15.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar|lib/npn/npn-boot-1.1.5.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_17.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_17.mod
deleted file mode 100644
index 73bc090..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_17.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar|lib/npn/npn-boot-1.1.5.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_21.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_21.mod
deleted file mode 100644
index 73bc090..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_21.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar|lib/npn/npn-boot-1.1.5.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_25.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_25.mod
deleted file mode 100644
index 73bc090..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_25.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar|lib/npn/npn-boot-1.1.5.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_40.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_40.mod
deleted file mode 100644
index 465e6f0..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_40.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar|lib/npn/npn-boot-1.1.6.v20130911.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_45.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_45.mod
deleted file mode 100644
index 465e6f0..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_45.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar|lib/npn/npn-boot-1.1.6.v20130911.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_51.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_51.mod
deleted file mode 100644
index 465e6f0..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_51.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar|lib/npn/npn-boot-1.1.6.v20130911.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_55.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_55.mod
deleted file mode 100644
index 5f8704d..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_55.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.8.v20141013/npn-boot-1.1.8.v20141013.jar|lib/npn/npn-boot-1.1.8.v20141013.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.8.v20141013.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_60.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_60.mod
deleted file mode 100644
index 5f8704d..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_60.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.8.v20141013/npn-boot-1.1.8.v20141013.jar|lib/npn/npn-boot-1.1.8.v20141013.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.8.v20141013.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_65.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_65.mod
deleted file mode 100644
index 5f8704d..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_65.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.8.v20141013/npn-boot-1.1.8.v20141013.jar|lib/npn/npn-boot-1.1.8.v20141013.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.8.v20141013.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_67.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_67.mod
deleted file mode 100644
index 5f8704d..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_67.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.8.v20141013/npn-boot-1.1.8.v20141013.jar|lib/npn/npn-boot-1.1.8.v20141013.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.8.v20141013.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_71.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_71.mod
deleted file mode 100644
index 851aca8..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_71.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.9.v20141016/npn-boot-1.1.9.v20141016.jar|lib/npn/npn-boot-1.1.9.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.9.v20141016.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_72.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_72.mod
deleted file mode 100644
index 851aca8..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_72.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.9.v20141016/npn-boot-1.1.9.v20141016.jar|lib/npn/npn-boot-1.1.9.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.9.v20141016.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_75.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_75.mod
deleted file mode 100644
index a5fac11..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_75.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.10.v20150130/npn-boot-1.1.10.v20150130.jar|lib/npn/npn-boot-1.1.10.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.10.v20150130.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_76.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_76.mod
deleted file mode 100644
index a5fac11..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_76.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.10.v20150130/npn-boot-1.1.10.v20150130.jar|lib/npn/npn-boot-1.1.10.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.10.v20150130.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_79.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_79.mod
deleted file mode 100644
index a5fac11..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_79.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.10.v20150130/npn-boot-1.1.10.v20150130.jar|lib/npn/npn-boot-1.1.10.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.10.v20150130.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_80.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_80.mod
deleted file mode 100644
index 2cce5fa..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_80.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.11.v20150415/npn-boot-1.1.11.v20150415.jar|lib/npn/npn-boot-1.1.11.v20150415.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.11.v20150415.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn.mod
deleted file mode 100644
index 1a2c71d..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn.mod
+++ /dev/null
@@ -1,37 +0,0 @@
-# NPN is provided via a -Xbootclasspath that modifies the secure connections
-# in java to support the NPN layer needed for SPDY.
-#
-# This modification has a tight dependency on specific updates of Java 1.7.
-# (No support for Java 8 exists for npn / npn-boot, use alpn instead)
-#
-# The npn module will use an appropriate npn-boot jar for your specific
-# version of Java.
-#
-# IMPORTANT: Versions of Java that exist after this module was created are
-#            not guaranteed to work with existing npn-boot jars, and might
-#            need a new npn-boot to be created / tested / deployed by the
-#            Jetty project in order to provide support for these future
-#            Java versions.
-#
-# All versions of npn-boot can be found at
-# http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/
-
-
-[name]
-protonego-impl
-
-[depend]
-protonego-impl/npn-${java.version}
-
-[xml]
-etc/protonego-npn.xml
-
-[files]
-lib/
-lib/npn/
-
-[license]
-NPN is a hosted at github under the GPL v2 with ClassPath Exception.
-NPN replaces/modifies OpenJDK classes in the java.sun.security.ssl package.
-http://github.com/jetty-project/jetty-npn
-http://openjdk.java.net/legal/gplv2+ce.html
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/spdy.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/spdy.mod
deleted file mode 100644
index cf79dfa..0000000
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/spdy.mod
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# SPDY Support Module
-#
-
-[depend]
-ssl
-protonego
-
-[lib]
-lib/spdy/*.jar
-
-[xml]
-etc/jetty-ssl.xml
-etc/jetty-spdy.xml
-
-[ini-template]
-## SPDY Configuration
-
-# Port for SPDY connections
-spdy.port=8443
-
-# SPDY idle timeout in milliseconds
-spdy.timeout=30000
-
-# Initial Window Size for SPDY
-#spdy.initialWindowSize=65536
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HTTPSPDYServerConnectionFactory.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HTTPSPDYServerConnectionFactory.java
deleted file mode 100644
index 5d03bb7..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HTTPSPDYServerConnectionFactory.java
+++ /dev/null
@@ -1,167 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server.http;
-
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.annotation.Name;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-public class HTTPSPDYServerConnectionFactory extends SPDYServerConnectionFactory implements HttpConfiguration.ConnectionFactory
-{
-    private static final String CHANNEL_ATTRIBUTE = "org.eclipse.jetty.spdy.server.http.HTTPChannelOverSPDY";
-    private static final Logger LOG = Log.getLogger(HTTPSPDYServerConnectionFactory.class);
-
-    private final PushStrategy pushStrategy;
-    private final HttpConfiguration httpConfiguration;
-
-    public HTTPSPDYServerConnectionFactory(
-        @Name("version") int version,
-        @Name("config") HttpConfiguration config)
-    {
-        this(version,config,new PushStrategy.None());
-    }
-
-    public HTTPSPDYServerConnectionFactory(
-        @Name("version") int version,
-        @Name("config") HttpConfiguration config,
-        @Name("pushStrategy") PushStrategy pushStrategy)
-    {
-        super(version);
-        this.pushStrategy = pushStrategy;
-        httpConfiguration = config;
-        addBean(httpConfiguration);
-    }
-
-    @Override
-    public HttpConfiguration getHttpConfiguration()
-    {
-        return httpConfiguration;
-    }
-
-    @Override
-    protected ServerSessionFrameListener provideServerSessionFrameListener(Connector connector, EndPoint endPoint)
-    {
-        return new HTTPServerFrameListener(connector,endPoint);
-    }
-
-    private class HTTPServerFrameListener extends ServerSessionFrameListener.Adapter implements StreamFrameListener
-    {
-        private final Connector connector;
-        private final EndPoint endPoint;
-
-        public HTTPServerFrameListener(Connector connector,EndPoint endPoint)
-        {
-            this.endPoint = endPoint;
-            this.connector=connector;
-        }
-
-        @Override
-        public StreamFrameListener onSyn(final Stream stream, SynInfo synInfo)
-        {
-            // Every time we have a SYN, it maps to a HTTP request.
-            // We can have multiple concurrent SYNs on the same connection,
-            // and this is very different from HTTP, where only one request
-            // can arrive on the same connection, so we need to create an
-            // HttpChannel for each SYN in order to run concurrently.
-
-            if (LOG.isDebugEnabled())
-                LOG.debug("Received {} on {}", synInfo, stream);
-
-            Fields headers = synInfo.getHeaders();
-            // According to SPDY/3 spec section 3.2.1 user-agents MUST support gzip compression. Firefox omits the
-            // accept-encoding header as it is redundant to negotiate gzip compression support with the server,
-            // if clients have to accept it.
-            // So we inject the accept-encoding header here, even if not set by the client. This will enforce SPDY
-            // clients to follow the spec and enable gzip compression if GzipFilter or the like is enabled.
-            if (!(headers.get("accept-encoding") != null && headers.get("accept-encoding").getValue().contains
-                    ("gzip")))
-                headers.add("accept-encoding", "gzip");
-            HttpTransportOverSPDY transport = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint,
-                    pushStrategy, stream, headers);
-            HttpInputOverSPDY input = new HttpInputOverSPDY();
-            HttpChannelOverSPDY channel = new HttpChannelOverSPDY(connector, httpConfiguration, endPoint, transport, input, stream);
-            stream.setAttribute(CHANNEL_ATTRIBUTE, channel);
-
-            channel.requestStart(headers, synInfo.isClose());
-
-            if (headers.isEmpty())
-            {
-                // If the SYN has no headers, they may come later in a HEADERS frame
-                return this;
-            }
-            else
-            {
-                if (synInfo.isClose())
-                    return null;
-                else
-                    return this;
-            }
-        }
-
-        @Override
-        public void onReply(Stream stream, ReplyInfo replyInfo)
-        {
-            // Do nothing, servers cannot get replies
-        }
-
-        @Override
-        public void onHeaders(Stream stream, HeadersInfo headersInfo)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Received {} on {}", headersInfo, stream);
-            HttpChannelOverSPDY channel = (HttpChannelOverSPDY)stream.getAttribute(CHANNEL_ATTRIBUTE);
-            channel.requestHeaders(headersInfo.getHeaders(), headersInfo.isClose());
-        }
-
-        @Override
-        public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-        {
-            return null;
-        }
-
-        @Override
-        public void onData(Stream stream, final DataInfo dataInfo)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Received {} on {}", dataInfo, stream);
-            HttpChannelOverSPDY channel = (HttpChannelOverSPDY)stream.getAttribute(CHANNEL_ATTRIBUTE);
-            channel.requestContent(dataInfo, dataInfo.isClose());
-        }
-
-        @Override
-        public void onFailure(Stream stream, Throwable x)
-        {
-            LOG.debug(x);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HTTPSPDYServerConnector.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HTTPSPDYServerConnector.java
deleted file mode 100644
index abb3d0d..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HTTPSPDYServerConnector.java
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.http;
-
-import java.util.Collections;
-import java.util.Map;
-
-import org.eclipse.jetty.server.AbstractConnectionFactory;
-import org.eclipse.jetty.server.ConnectionFactory;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-
-public class HTTPSPDYServerConnector extends ServerConnector
-{
-    public HTTPSPDYServerConnector(Server server)
-    {
-        this(server, Collections.<Short, PushStrategy>emptyMap());
-    }
-
-    public HTTPSPDYServerConnector(Server server, SslContextFactory sslContextFactory)
-    {
-        this(server, sslContextFactory, Collections.<Short, PushStrategy>emptyMap());
-    }
-
-    public HTTPSPDYServerConnector(Server server, Map<Short, PushStrategy> pushStrategies)
-    {
-        this(server, null, pushStrategies);
-    }
-
-    public HTTPSPDYServerConnector(Server server, SslContextFactory sslContextFactory, Map<Short, PushStrategy> pushStrategies)
-    {
-        this(server, new HttpConfiguration(), sslContextFactory, pushStrategies);
-    }
-
-    public HTTPSPDYServerConnector(Server server, short version, HttpConfiguration httpConfiguration, PushStrategy push)
-    {
-        super(server, new HTTPSPDYServerConnectionFactory(version, httpConfiguration, push));
-    }
-
-    public HTTPSPDYServerConnector(Server server, HttpConfiguration config, SslContextFactory sslContextFactory, Map<Short, PushStrategy> pushStrategies)
-    {
-        super(server, AbstractConnectionFactory.getFactories(sslContextFactory,
-                sslContextFactory == null
-                        ? new ConnectionFactory[]{new HttpConnectionFactory(config)}
-                        : new ConnectionFactory[]{new NPNServerConnectionFactory("spdy/3", "spdy/2", "http/1.1"),
-                        new HttpConnectionFactory(config),
-                        new HTTPSPDYServerConnectionFactory(SPDY.V3, config, getPushStrategy(SPDY.V3, pushStrategies)),
-                        new HTTPSPDYServerConnectionFactory(SPDY.V2, config, getPushStrategy(SPDY.V2, pushStrategies))}));
-        NPNServerConnectionFactory npnConnectionFactory = getConnectionFactory(NPNServerConnectionFactory.class);
-        if (npnConnectionFactory != null)
-            npnConnectionFactory.setDefaultProtocol("http/1.1");
-    }
-
-    private static PushStrategy getPushStrategy(short version, Map<Short, PushStrategy> pushStrategies)
-    {
-        PushStrategy pushStrategy = pushStrategies.get(version);
-        if (pushStrategy == null)
-            pushStrategy = new PushStrategy.None();
-        return pushStrategy;
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpChannelOverSPDY.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpChannelOverSPDY.java
deleted file mode 100644
index 3d94c22..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpChannelOverSPDY.java
+++ /dev/null
@@ -1,246 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.http;
-
-import java.nio.ByteBuffer;
-
-import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.HttpChannel;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpTransport;
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-public class HttpChannelOverSPDY extends HttpChannel<DataInfo>
-{
-    private static final Logger LOG = Log.getLogger(HttpChannelOverSPDY.class);
-
-    private final Stream stream;
-    private boolean dispatched; // Guarded by synchronization on tasks
-    private boolean redispatch; // Guarded by synchronization on tasks
-    private boolean headersComplete;
-
-    public HttpChannelOverSPDY(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInputOverSPDY input, Stream stream)
-    {
-        super(connector, configuration, endPoint, transport, input);
-        this.stream = stream;
-    }
-
-    @Override
-    public long getIdleTimeout()
-    {
-        return stream.getIdleTimeout();
-    }
-    
-    @Override
-    public boolean headerComplete()
-    {
-        headersComplete = true;
-        return super.headerComplete();
-    }
-
-    private void dispatch()
-    {
-        synchronized (this)
-        {
-            if (dispatched)
-                redispatch=true;
-            else
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Dispatch {}", this);
-                dispatched=true;
-                execute(this);
-            }
-        }
-    }
-
-    @Override
-    public void run()
-    {
-        boolean execute=true;
-        
-        while(execute)
-        {
-            try
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Executing {}",this);
-                super.run();
-            }
-            finally
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Completing {}", this);
-                synchronized (this)
-                {
-                    dispatched = redispatch;
-                    redispatch=false;
-                    execute=dispatched;
-                }
-            }
-        }
-    }
-    
-
-    public void requestStart(final Fields headers, final boolean endRequest)
-    {
-        if (!headers.isEmpty())
-            requestHeaders(headers, endRequest);
-    }
-
-    public void requestHeaders(Fields headers, boolean endRequest)
-    {
-        boolean proceed = performBeginRequest(headers);
-        if (!proceed)
-            return;
-
-        performHeaders(headers);
-
-        if (endRequest)
-        {
-            boolean dispatch = headerComplete();
-            if (messageComplete())
-                dispatch=true;
-            if (dispatch)
-                dispatch();
-        }
-    }
-
-    public void requestContent(final DataInfo dataInfo, boolean endRequest)
-    {
-        boolean dispatch=false;
-        if (!headersComplete && headerComplete())
-            dispatch=true;
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("HTTP > {} bytes of content", dataInfo.length());
-
-        // We need to copy the dataInfo since we do not know when its bytes
-        // will be consumed. When the copy is consumed, we consume also the
-        // original, so the implementation can send a window update.
-        ByteBuffer copyByteBuffer = dataInfo.asByteBuffer(false);
-        ByteBufferDataInfo copyDataInfo = new ByteBufferDataInfo(copyByteBuffer, dataInfo.isClose())
-        {
-            @Override
-            public void consume(int delta)
-            {
-                super.consume(delta);
-                dataInfo.consume(delta);
-            }
-        };
-        if (LOG.isDebugEnabled())
-            LOG.debug("Queuing last={} content {}", endRequest, copyDataInfo);
-
-        if (content(copyDataInfo))
-            dispatch=true;
-
-        if (endRequest && messageComplete())
-            dispatch=true;
-        
-        if (dispatch)
-            dispatch();
-    }
-    
-    @Override
-    public boolean messageComplete()
-    {
-        super.messageComplete();
-        return false;
-    }
-
-    private boolean performBeginRequest(Fields headers)
-    {
-        short version = stream.getSession().getVersion();
-        Fields.Field methodHeader = headers.get(HTTPSPDYHeader.METHOD.name(version));
-        Fields.Field uriHeader = headers.get(HTTPSPDYHeader.URI.name(version));
-        Fields.Field versionHeader = headers.get(HTTPSPDYHeader.VERSION.name(version));
-
-        if (methodHeader == null || uriHeader == null || versionHeader == null)
-        {
-            badMessage(400, "Missing required request line elements");
-            return false;
-        }
-
-        HttpMethod httpMethod = HttpMethod.fromString(methodHeader.getValue());
-        HttpVersion httpVersion = HttpVersion.fromString(versionHeader.getValue());
-
-        // TODO should handle URI as byte buffer as some bad clients send WRONG encodings in query string
-        // that  we have to deal with
-        ByteBuffer uri = BufferUtil.toBuffer(uriHeader.getValue());
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("HTTP > {} {} {}", httpMethod, uriHeader.getValue(), httpVersion);
-        startRequest(httpMethod, httpMethod.asString(), uri, httpVersion);
-
-        Fields.Field schemeHeader = headers.get(HTTPSPDYHeader.SCHEME.name(version));
-        if (schemeHeader != null)
-            getRequest().setScheme(schemeHeader.getValue());
-        return true;
-    }
-
-    private void performHeaders(Fields headers)
-    {
-        for (Fields.Field header : headers)
-        {
-            String name = header.getName();
-
-            // Skip special SPDY headers, unless it's the "host" header
-            HTTPSPDYHeader specialHeader = HTTPSPDYHeader.from(stream.getSession().getVersion(), name);
-            if (specialHeader != null)
-            {
-                if (specialHeader == HTTPSPDYHeader.HOST)
-                    name = "host";
-                else
-                    continue;
-            }
-
-            switch (name)
-            {
-                case "connection":
-                case "keep-alive":
-                case "proxy-connection":
-                case "transfer-encoding":
-                {
-                    // Spec says to ignore these headers
-                    continue;
-                }
-                default:
-                {
-                    // Spec says headers must be single valued
-                    String value = header.getValue();
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("HTTP > {}: {}", name, value);
-                    parsedHeader(new HttpField(name,value));
-                    break;
-                }
-            }
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpInputOverSPDY.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpInputOverSPDY.java
deleted file mode 100644
index 894682f..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpInputOverSPDY.java
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.http;
-
-import org.eclipse.jetty.server.QueuedHttpInput;
-import org.eclipse.jetty.spdy.api.DataInfo;
-
-public class HttpInputOverSPDY extends QueuedHttpInput<DataInfo>
-{
-    @Override
-    protected int remaining(DataInfo item)
-    {
-        return item.available();
-    }
-
-    @Override
-    protected int get(DataInfo item, byte[] buffer, int offset, int length)
-    {
-        return item.readInto(buffer, offset, length);
-    }
-    
-    @Override
-    protected void consume(DataInfo item, int length)
-    {
-        item.consume(length);
-    }
-
-    @Override
-    protected void onContentConsumed(DataInfo dataInfo)
-    {
-        dataInfo.consume(dataInfo.length());
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java
deleted file mode 100644
index 743e1ba..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java
+++ /dev/null
@@ -1,423 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.http;
-
-import java.nio.ByteBuffer;
-import java.util.Queue;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpGenerator;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.EofException;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpTransport;
-import org.eclipse.jetty.spdy.StreamException;
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.ConcurrentArrayQueue;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-public class HttpTransportOverSPDY implements HttpTransport
-{
-    private static final Logger LOG = Log.getLogger(HttpTransportOverSPDY.class);
-
-    private final Connector connector;
-    private final HttpConfiguration configuration;
-    private final EndPoint endPoint;
-    private final PushStrategy pushStrategy;
-    private final Stream stream;
-    private final short version;
-    private final Fields requestHeaders;
-    private final AtomicBoolean committed = new AtomicBoolean();
-
-    public HttpTransportOverSPDY(Connector connector, HttpConfiguration configuration, EndPoint endPoint, PushStrategy pushStrategy, Stream stream, Fields requestHeaders)
-    {
-        this.connector = connector;
-        this.configuration = configuration;
-        this.endPoint = endPoint;
-        this.pushStrategy = pushStrategy == null ? new PushStrategy.None() : pushStrategy;
-        this.stream = stream;
-        this.requestHeaders = requestHeaders;
-        Session session = stream.getSession();
-        this.version = session.getVersion();
-    }
-
-    protected Stream getStream()
-    {
-        return stream;
-    }
-
-    protected Fields getRequestHeaders()
-    {
-        return requestHeaders;
-    }
-
-
-    @Override
-    public void send(ByteBuffer responseBodyContent, boolean lastContent, Callback callback)
-    {
-        // TODO can this be more efficient?
-        send(null, responseBodyContent, lastContent, callback);
-    }
-
-    @Override
-    public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, final Callback callback)
-    {
-        if (LOG.isDebugEnabled())
-            LOG.debug("Sending {} {} {} {} last={}", this, stream, info, BufferUtil.toDetailString(content), lastContent);
-
-        if (stream.isClosed() || stream.isReset())
-        {
-            EofException exception = new EofException("stream closed");
-            callback.failed(exception);
-            return;
-        }
-
-        // info==null content==null lastContent==false          should not happen
-        // info==null content==null lastContent==true           signals no more content - complete
-        // info==null content!=null lastContent==false          send data on committed response
-        // info==null content!=null lastContent==true           send last data on committed response - complete
-        // info!=null content==null lastContent==false          reply, commit
-        // info!=null content==null lastContent==true           reply, commit and complete
-        // info!=null content!=null lastContent==false          reply, commit with content
-        // info!=null content!=null lastContent==true           reply, commit with content and complete
-
-        boolean isHeadRequest = HttpMethod.HEAD.name().equalsIgnoreCase(requestHeaders.get(HTTPSPDYHeader.METHOD.name(version)).getValue());
-        boolean hasContent = BufferUtil.hasContent(content) && !isHeadRequest;
-        boolean close = !hasContent && lastContent;
-
-        if (info != null)
-        {
-            if (!committed.compareAndSet(false, true))
-            {
-                StreamException exception = new StreamException(stream.getId(), StreamStatus.PROTOCOL_ERROR,
-                        "Stream already committed!");
-                callback.failed(exception);
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Committed response twice.", exception);
-                return;
-            }
-            sendReply(info, !hasContent ? callback : new Callback.Adapter()
-            {
-                @Override
-                public void failed(Throwable x)
-                {
-                    callback.failed(x);
-                }
-            }, close);
-        }
-
-        // Do we have some content to send as well
-        if (hasContent)
-        {
-            // send the data and let it call the callback
-            if (LOG.isDebugEnabled())
-                LOG.debug("Send content: {} on stream: {} lastContent={}", BufferUtil.toDetailString(content), stream,
-                    lastContent);
-            stream.data(new ByteBufferDataInfo(endPoint.getIdleTimeout(), TimeUnit.MILLISECONDS, content, lastContent
-            ), callback);
-        }
-        // else do we need to close
-        else if (lastContent && info == null)
-        {
-            // send empty data to close and let the send call the callback
-            if (LOG.isDebugEnabled())
-                LOG.debug("No content and lastContent=true. Sending empty ByteBuffer to close stream: {}", stream);
-            stream.data(new ByteBufferDataInfo(endPoint.getIdleTimeout(), TimeUnit.MILLISECONDS,
-                    BufferUtil.EMPTY_BUFFER, lastContent), callback);
-        }
-        else if (!lastContent && !hasContent && info == null)
-            throw new IllegalStateException("not lastContent, no content and no responseInfo!");
-
-    }
-
-    private void sendReply(HttpGenerator.ResponseInfo info, Callback callback, boolean close)
-    {
-        Fields headers = new Fields();
-
-        HttpVersion httpVersion = HttpVersion.HTTP_1_1;
-        headers.put(HTTPSPDYHeader.VERSION.name(version), httpVersion.asString());
-
-        int status = info.getStatus();
-        StringBuilder httpStatus = new StringBuilder().append(status);
-        String reason = info.getReason();
-        if (reason == null)
-            reason = HttpStatus.getMessage(status);
-        if (reason != null)
-            httpStatus.append(" ").append(reason);
-        headers.put(HTTPSPDYHeader.STATUS.name(version), httpStatus.toString());
-        if (LOG.isDebugEnabled())
-            LOG.debug("HTTP < {} {}", httpVersion, httpStatus);
-
-        // TODO merge the two Field classes into one
-        HttpFields fields = info.getHttpFields();
-        if (fields != null)
-        {
-            for (int i = 0; i < fields.size(); ++i)
-            {
-                HttpField field = fields.getField(i);
-                String name = field.getName();
-                String value = field.getValue();
-                headers.add(name, value);
-                if (LOG.isDebugEnabled())
-                    LOG.debug("HTTP < {}: {}", name, value);
-            }
-        }
-
-        if (configuration.getSendServerVersion())
-            headers.add(HttpHeader.SERVER.asString(), HttpConfiguration.SERVER_VERSION);
-        if (configuration.getSendXPoweredBy())
-            headers.add(HttpHeader.X_POWERED_BY.asString(), HttpConfiguration.SERVER_VERSION);
-
-        ReplyInfo reply = new ReplyInfo(headers, close);
-        if (LOG.isDebugEnabled())
-            LOG.debug("Sending reply: {} on stream: {}", reply, stream);
-        reply(stream, reply, callback);
-    }
-
-    @Override
-    public void completed()
-    {
-        if (LOG.isDebugEnabled())
-            LOG.debug("Completed {}", this);
-    }
-
-    private void reply(Stream stream, ReplyInfo replyInfo, Callback callback)
-    {
-        if (!stream.isUnidirectional())
-            stream.reply(replyInfo, callback);
-        else
-            stream.headers(new HeadersInfo(replyInfo.getHeaders(), replyInfo.isClose()), callback);
-
-        Fields responseHeaders = replyInfo.getHeaders();
-        if (responseHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().startsWith("200") && !stream.isClosed())
-        {
-            Set<String> pushResources = pushStrategy.apply(stream, requestHeaders, responseHeaders);
-            if (pushResources.size() > 0)
-            {
-                PushResourceCoordinator pushResourceCoordinator = new PushResourceCoordinator(pushResources);
-                pushResourceCoordinator.coordinate();
-            }
-        }
-    }
-
-    private static class PushHttpTransportOverSPDY extends HttpTransportOverSPDY
-    {
-        private final PushResourceCoordinator coordinator;
-        private final short version;
-
-        private PushHttpTransportOverSPDY(Connector connector, HttpConfiguration configuration, EndPoint endPoint,
-                                          PushStrategy pushStrategy, Stream stream, Fields requestHeaders,
-                                          PushResourceCoordinator coordinator, short version)
-        {
-            super(connector, configuration, endPoint, pushStrategy, stream, requestHeaders);
-            this.coordinator = coordinator;
-            this.version = version;
-        }
-
-        @Override
-        public void completed()
-        {
-            Stream stream = getStream();
-            if (LOG.isDebugEnabled())
-                LOG.debug("Resource pushed for {} on {}",
-                    getRequestHeaders().get(HTTPSPDYHeader.URI.name(version)), stream);
-            coordinator.complete();
-        }
-    }
-
-    private class PushResourceCoordinator
-    {
-        private final Queue<PushResource> queue = new ConcurrentArrayQueue<>();
-        private final Set<String> resources;
-        private AtomicBoolean active = new AtomicBoolean(false);
-
-        private PushResourceCoordinator(Set<String> resources)
-        {
-            this.resources = resources;
-        }
-
-        private void coordinate()
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Pushing resources: {}", resources);
-            // Must send all push frames to the client at once before we
-            // return from this method and send the main resource data
-            for (String pushResource : resources)
-                pushResource(pushResource);
-        }
-
-        private void sendNextResourceData()
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("{} sendNextResourceData active: {}", hashCode(), active.get());
-            if (active.compareAndSet(false, true))
-            {
-                PushResource resource = queue.poll();
-                if (resource != null)
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Opening new push channel for: {}", resource);
-                    HttpChannelOverSPDY pushChannel = newHttpChannelOverSPDY(resource.getPushStream(), resource.getPushRequestHeaders());
-                    pushChannel.requestStart(resource.getPushRequestHeaders(), true);
-                    return;
-                }
-
-                if (active.compareAndSet(true, false))
-                {
-                    if (queue.peek() != null)
-                        sendNextResourceData();
-                }
-                else
-                {
-                    throw new IllegalStateException("active must not be false here! Concurrency bug!");
-                }
-            }
-        }
-
-        private HttpChannelOverSPDY newHttpChannelOverSPDY(Stream pushStream, Fields pushRequestHeaders)
-        {
-            HttpTransport transport = new PushHttpTransportOverSPDY(connector, configuration, endPoint, pushStrategy,
-                    pushStream, pushRequestHeaders, this, version);
-            HttpInputOverSPDY input = new HttpInputOverSPDY();
-            return new HttpChannelOverSPDY(connector, configuration, endPoint, transport, input, pushStream);
-        }
-
-        private void pushResource(String pushResource)
-        {
-            Fields.Field scheme = requestHeaders.get(HTTPSPDYHeader.SCHEME.name(version));
-            Fields.Field host = requestHeaders.get(HTTPSPDYHeader.HOST.name(version));
-            Fields.Field uri = requestHeaders.get(HTTPSPDYHeader.URI.name(version));
-            final Fields pushHeaders = createPushHeaders(scheme, host, pushResource);
-            final Fields pushRequestHeaders = createRequestHeaders(scheme, host, uri, pushResource);
-
-            stream.push(new PushInfo(pushHeaders, false), new Promise<Stream>()
-            {
-                @Override
-                public void succeeded(Stream pushStream)
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Headers pushed for {} on {}", pushHeaders.get(HTTPSPDYHeader.URI.name(version)), pushStream);
-                    queue.offer(new PushResource(pushStream, pushRequestHeaders));
-                    sendNextResourceData();
-                }
-
-                @Override
-                public void failed(Throwable x)
-                {
-                    LOG.debug("Creating push stream failed.", x);
-                    sendNextResourceData();
-                }
-            });
-        }
-
-        private void complete()
-        {
-            if (!active.compareAndSet(true, false))
-                throw new IllegalStateException();
-            sendNextResourceData();
-        }
-
-        private Fields createRequestHeaders(Fields.Field scheme, Fields.Field host, Fields.Field uri, String pushResourcePath)
-        {
-            final Fields newRequestHeaders = new Fields(requestHeaders, false);
-            newRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET");
-            newRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-            newRequestHeaders.put(scheme);
-            newRequestHeaders.put(host);
-            newRequestHeaders.put(HTTPSPDYHeader.URI.name(version), pushResourcePath);
-            String referrer = scheme.getValue() + "://" + host.getValue() + uri.getValue();
-            newRequestHeaders.put("referer", referrer);
-            newRequestHeaders.put("x-spdy-push", "true");
-            return newRequestHeaders;
-        }
-
-        private Fields createPushHeaders(Fields.Field scheme, Fields.Field host, String pushResourcePath)
-        {
-            final Fields pushHeaders = new Fields();
-            if (version == SPDY.V2)
-                pushHeaders.put(HTTPSPDYHeader.URI.name(version), scheme.getValue() + "://" + host.getValue() + pushResourcePath);
-            else
-            {
-                pushHeaders.put(HTTPSPDYHeader.URI.name(version), pushResourcePath);
-                pushHeaders.put(scheme);
-                pushHeaders.put(host);
-            }
-            return pushHeaders;
-        }
-    }
-
-    private static class PushResource
-    {
-        private final Stream pushStream;
-        private final Fields pushRequestHeaders;
-
-        public PushResource(Stream pushStream, Fields pushRequestHeaders)
-        {
-            this.pushStream = pushStream;
-            this.pushRequestHeaders = pushRequestHeaders;
-        }
-
-        public Stream getPushStream()
-        {
-            return pushStream;
-        }
-
-        public Fields getPushRequestHeaders()
-        {
-            return pushRequestHeaders;
-        }
-
-        @Override
-        public String toString()
-        {
-            return "PushResource{" +
-                    "pushStream=" + pushStream +
-                    ", pushRequestHeaders=" + pushRequestHeaders +
-                    '}';
-        }
-    }
-
-    @Override
-    public void abort()
-    {
-        // TODO close the stream in a way to indicate an incomplete response?
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/PushStrategy.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/PushStrategy.java
deleted file mode 100644
index baf089f..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/PushStrategy.java
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server.http;
-
-import java.util.Collections;
-import java.util.Set;
-
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.util.Fields;
-
-/**
- * <p>{@link PushStrategy} encapsulates the decisions about performing
- * SPDY pushes of secondary resources associated with a primary resource.</p>
- */
-public interface PushStrategy
-{
-    /**
-     * <p>Applies the SPDY push logic for the primary resource.</p>
-     *
-     * @param stream the primary resource stream
-     * @param requestHeaders the primary resource request headers
-     * @param responseHeaders the primary resource response headers
-     * @return a list of secondary resource URIs to push
-     */
-    public Set<String> apply(Stream stream, Fields requestHeaders, Fields responseHeaders);
-
-    /**
-     * An implementation that returns an empty list of secondary resources
-     */
-    public static class None implements PushStrategy
-    {
-        @Override
-        public Set<String> apply(Stream stream, Fields requestHeaders, Fields responseHeaders)
-        {
-            return Collections.emptySet();
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategy.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategy.java
deleted file mode 100644
index fb908c4..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategy.java
+++ /dev/null
@@ -1,342 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server.http;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.regex.Pattern;
-
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * <p>A SPDY push strategy that auto-populates push metadata based on referrer URLs.<p>A typical request for a main
- * resource such as {@code index.html} is immediately followed by a number of requests for associated resources.
- * Associated resource requests will have a {@code Referer} HTTP header that points to {@code index.html}, which is used
- * to link the associated resource to the main resource.<p>However, also following a hyperlink generates a HTTP request
- * with a {@code Referer} HTTP header that points to {@code index.html}; therefore a proper value for {@link
- * #setReferrerPushPeriod(int)} has to be set. If the referrerPushPeriod for a main resource has elapsed, no more
- * associated resources will be added for that main resource.<p>This class distinguishes associated main resources by
- * their URL path suffix and content type. CSS stylesheets, images and JavaScript files have recognizable URL path
- * suffixes that are classified as associated resources. The suffix regexs can be configured by constructor argument</p>
- * <p>When CSS stylesheets refer to images, the CSS image request will have the CSS stylesheet as referrer. This
- * implementation will push also the CSS image.<p>The push metadata built by this implementation is limited by the
- * number of pages of the application itself, and by the {@link #setMaxAssociatedResources(int)} max associated
- * resources} parameter. This parameter limits the number of associated resources per each main resource, so that if a
- * main resource has hundreds of associated resources, only up to the number specified by this parameter will be
- * pushed.
- */
-public class ReferrerPushStrategy implements PushStrategy
-{
-    private static final Logger LOG = Log.getLogger(ReferrerPushStrategy.class);
-    private final ConcurrentMap<String, MainResource> mainResources = new ConcurrentHashMap<>();
-    private final Set<Pattern> pushRegexps = new HashSet<>();
-    private final Set<String> pushContentTypes = new HashSet<>();
-    private final Set<Pattern> allowedPushOrigins = new HashSet<>();
-    private final Set<Pattern> userAgentBlacklist = new HashSet<>();
-    private volatile int maxAssociatedResources = 32;
-    private volatile int referrerPushPeriod = 5000;
-
-    public ReferrerPushStrategy()
-    {
-        List<String> defaultPushRegexps = Arrays.asList(".*\\.css", ".*\\.js", ".*\\.png", ".*\\.jpeg", ".*\\.jpg",
-                ".*\\.gif", ".*\\.ico");
-        addPushRegexps(defaultPushRegexps);
-
-        List<String> defaultPushContentTypes = Arrays.asList(
-                "text/css",
-                "text/javascript", "application/javascript", "application/x-javascript",
-                "image/png", "image/x-png",
-                "image/jpeg",
-                "image/gif",
-                "image/x-icon", "image/vnd.microsoft.icon");
-        this.pushContentTypes.addAll(defaultPushContentTypes);
-    }
-
-    public void setPushRegexps(List<String> pushRegexps)
-    {
-        pushRegexps.clear();
-        addPushRegexps(pushRegexps);
-    }
-
-    private void addPushRegexps(List<String> pushRegexps)
-    {
-        for (String pushRegexp : pushRegexps)
-            this.pushRegexps.add(Pattern.compile(pushRegexp));
-    }
-
-    public void setPushContentTypes(List<String> pushContentTypes)
-    {
-        pushContentTypes.clear();
-        pushContentTypes.addAll(pushContentTypes);
-    }
-
-    public void setAllowedPushOrigins(List<String> allowedPushOrigins)
-    {
-        allowedPushOrigins.clear();
-        for (String allowedPushOrigin : allowedPushOrigins)
-            this.allowedPushOrigins.add(Pattern.compile(allowedPushOrigin.replace(".", "\\.").replace("*", ".*")));
-    }
-
-    public void setUserAgentBlacklist(List<String> userAgentPatterns)
-    {
-        userAgentBlacklist.clear();
-        for (String userAgentPattern : userAgentPatterns)
-            userAgentBlacklist.add(Pattern.compile(userAgentPattern));
-    }
-
-    public void setMaxAssociatedResources(int maxAssociatedResources)
-    {
-        this.maxAssociatedResources = maxAssociatedResources;
-    }
-
-    public void setReferrerPushPeriod(int referrerPushPeriod)
-    {
-        this.referrerPushPeriod = referrerPushPeriod;
-    }
-
-    public Set<Pattern> getPushRegexps()
-    {
-        return pushRegexps;
-    }
-
-    public Set<String> getPushContentTypes()
-    {
-        return pushContentTypes;
-    }
-
-    public Set<Pattern> getAllowedPushOrigins()
-    {
-        return allowedPushOrigins;
-    }
-
-    public Set<Pattern> getUserAgentBlacklist()
-    {
-        return userAgentBlacklist;
-    }
-
-    public int getMaxAssociatedResources()
-    {
-        return maxAssociatedResources;
-    }
-
-    public int getReferrerPushPeriod()
-    {
-        return referrerPushPeriod;
-    }
-
-    @Override
-    public Set<String> apply(Stream stream, Fields requestHeaders, Fields responseHeaders)
-    {
-        Set<String> result = Collections.<String>emptySet();
-        short version = stream.getSession().getVersion();
-        if (!isIfModifiedSinceHeaderPresent(requestHeaders) && isValidMethod(requestHeaders.get(HTTPSPDYHeader.METHOD
-                .name(version)).getValue()) && !isUserAgentBlacklisted(requestHeaders))
-        {
-            String scheme = requestHeaders.get(HTTPSPDYHeader.SCHEME.name(version)).getValue();
-            String host = requestHeaders.get(HTTPSPDYHeader.HOST.name(version)).getValue();
-            String origin = scheme + "://" + host;
-            String url = requestHeaders.get(HTTPSPDYHeader.URI.name(version)).getValue();
-            String absoluteURL = origin + url;
-            if (LOG.isDebugEnabled())
-                LOG.debug("Applying push strategy for {}", absoluteURL);
-            if (isMainResource(url, responseHeaders))
-            {
-                MainResource mainResource = getOrCreateMainResource(absoluteURL);
-                result = mainResource.getResources();
-            }
-            else if (isPushResource(url, responseHeaders))
-            {
-                Fields.Field referrerHeader = requestHeaders.get("referer");
-                if (referrerHeader != null)
-                {
-                    String referrer = referrerHeader.getValue();
-                    MainResource mainResource = mainResources.get(referrer);
-                    if (mainResource == null)
-                        mainResource = getOrCreateMainResource(referrer);
-
-                    Set<String> pushResources = mainResource.getResources();
-                    if (!pushResources.contains(url))
-                        mainResource.addResource(url, origin, referrer);
-                    else
-                        result = getPushResources(absoluteURL);
-                }
-            }
-            if (LOG.isDebugEnabled())
-                LOG.debug("Pushing {} resources for {}: {}", result.size(), absoluteURL, result);
-        }
-        return result;
-    }
-
-    private Set<String> getPushResources(String absoluteURL)
-    {
-        Set<String> result = Collections.emptySet();
-        if (mainResources.get(absoluteURL) != null)
-            result = mainResources.get(absoluteURL).getResources();
-        return result;
-    }
-
-    private MainResource getOrCreateMainResource(String absoluteURL)
-    {
-        MainResource mainResource = mainResources.get(absoluteURL);
-        if (mainResource == null)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Creating new main resource for {}", absoluteURL);
-            MainResource value = new MainResource(absoluteURL);
-            mainResource = mainResources.putIfAbsent(absoluteURL, value);
-            if (mainResource == null)
-                mainResource = value;
-        }
-        return mainResource;
-    }
-
-    private boolean isIfModifiedSinceHeaderPresent(Fields headers)
-    {
-        return headers.get("if-modified-since") != null;
-    }
-
-    private boolean isValidMethod(String method)
-    {
-        return "GET".equalsIgnoreCase(method);
-    }
-
-    private boolean isMainResource(String url, Fields responseHeaders)
-    {
-        return !isPushResource(url, responseHeaders);
-    }
-
-    public boolean isUserAgentBlacklisted(Fields headers)
-    {
-        Fields.Field userAgentHeader = headers.get("user-agent");
-        if (userAgentHeader != null)
-            for (Pattern userAgentPattern : userAgentBlacklist)
-                if (userAgentPattern.matcher(userAgentHeader.getValue()).matches())
-                    return true;
-        return false;
-    }
-
-    private boolean isPushResource(String url, Fields responseHeaders)
-    {
-        for (Pattern pushRegexp : pushRegexps)
-        {
-            if (pushRegexp.matcher(url).matches())
-            {
-                Fields.Field header = responseHeaders.get("content-type");
-                if (header == null)
-                    return true;
-
-                String contentType = header.getValue().toLowerCase(Locale.ENGLISH);
-                for (String pushContentType : pushContentTypes)
-                    if (contentType.startsWith(pushContentType))
-                        return true;
-            }
-        }
-        return false;
-    }
-
-    private class MainResource
-    {
-        private final String name;
-        private final CopyOnWriteArraySet<String> resources = new CopyOnWriteArraySet<>();
-        private final AtomicLong firstResourceAdded = new AtomicLong(-1);
-
-        private MainResource(String name)
-        {
-            this.name = name;
-        }
-
-        public boolean addResource(String url, String origin, String referrer)
-        {
-            // We start the push period here and not when initializing the main resource, because a browser with a
-            // prefilled cache won't request the subresources. If the browser with warmed up cache now hits the main
-            // resource after a server restart, the push period shouldn't start until the first subresource is
-            // being requested.
-            firstResourceAdded.compareAndSet(-1, System.nanoTime());
-
-            long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - firstResourceAdded.get());
-            if (!referrer.startsWith(origin) && !isPushOriginAllowed(origin))
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Skipped store of push metadata {} for {}: Origin: {} doesn't match or origin not allowed",
-                        url, name, origin);
-                return false;
-            }
-
-            // This check is not strictly concurrent-safe, but limiting
-            // the number of associated resources is achieved anyway
-            // although in rare cases few more resources will be stored
-            if (resources.size() >= maxAssociatedResources)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Skipped store of push metadata {} for {}: max associated resources ({}) reached",
-                        url, name, maxAssociatedResources);
-                return false;
-            }
-            if (delay > referrerPushPeriod)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Delay: {}ms longer than referrerPushPeriod ({}ms). Not adding resource: {} for: {}",
-                        delay, referrerPushPeriod, url, name);
-                return false;
-            }
-
-            if (LOG.isDebugEnabled())
-                LOG.debug("Adding: {} to: {} with delay: {}ms.", url, this, delay);
-            resources.add(url);
-            return true;
-        }
-
-        public Set<String> getResources()
-        {
-            return Collections.unmodifiableSet(resources);
-        }
-
-        public String toString()
-        {
-            return String.format("%s@%x{name=%s,resources=%s}",
-                    getClass().getSimpleName(),
-                    hashCode(),
-                    name,
-                    resources
-            );
-        }
-
-        private boolean isPushOriginAllowed(String origin)
-        {
-            for (Pattern allowedPushOrigin : allowedPushOrigins)
-                if (allowedPushOrigin.matcher(origin).matches())
-                    return true;
-            return false;
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPProxyEngine.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPProxyEngine.java
deleted file mode 100644
index 91529aa..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPProxyEngine.java
+++ /dev/null
@@ -1,276 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.util.DeferredContentProvider;
-import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.HttpCookieStore;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * <p>{@link HTTPProxyEngine} implements a SPDY to HTTP proxy, that is, converts SPDY events received by clients into
- * HTTP events for the servers.</p>
- */
-public class HTTPProxyEngine extends ProxyEngine
-{
-    private static final Logger LOG = Log.getLogger(HTTPProxyEngine.class);
-    private static final Callback LOGGING_CALLBACK = new LoggingCallback();
-
-    private final HttpClient httpClient;
-
-    public HTTPProxyEngine(HttpClient httpClient)
-    {
-        this.httpClient = httpClient;
-        configureHttpClient(httpClient);
-    }
-
-    private void configureHttpClient(HttpClient httpClient)
-    {
-        // Redirects must be proxied as is, not followed
-        httpClient.setFollowRedirects(false);
-        // Must not store cookies, otherwise cookies of different clients will mix
-        httpClient.setCookieStore(new HttpCookieStore.Empty());
-    }
-
-    public StreamFrameListener proxy(final Stream clientStream, SynInfo clientSynInfo, ProxyEngineSelector.ProxyServerInfo proxyServerInfo)
-    {
-        short version = clientStream.getSession().getVersion();
-        String method = clientSynInfo.getHeaders().get(HTTPSPDYHeader.METHOD.name(version)).getValue();
-        String path = clientSynInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version)).getValue();
-
-        Fields headers = new Fields(clientSynInfo.getHeaders(), false);
-
-        removeHopHeaders(headers);
-        addRequestProxyHeaders(clientStream, headers);
-        customizeRequestHeaders(clientStream, headers);
-
-        String host = proxyServerInfo.getHost();
-        int port = proxyServerInfo.getAddress().getPort();
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("Sending HTTP request to: {}", host + ":" + port);
-        final Request request = httpClient.newRequest(host, port)
-                .path(path)
-                .method(HttpMethod.fromString(method));
-        addNonSpdyHeadersToRequest(version, headers, request);
-
-        if (!clientSynInfo.isClose())
-        {
-            request.content(new DeferredContentProvider());
-        }
-
-        sendRequest(clientStream, request);
-
-        return new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                // We proxy to HTTP so we do not receive replies
-                throw new UnsupportedOperationException("Not Yet Implemented");
-            }
-
-            @Override
-            public void onHeaders(Stream stream, HeadersInfo headersInfo)
-            {
-                throw new UnsupportedOperationException("Not Yet Implemented");
-            }
-
-            @Override
-            public void onData(Stream clientStream, final DataInfo clientDataInfo)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("received clientDataInfo: {} for stream: {}", clientDataInfo, clientStream);
-
-                DeferredContentProvider contentProvider = (DeferredContentProvider)request.getContent();
-                contentProvider.offer(clientDataInfo.asByteBuffer(true));
-
-                if (clientDataInfo.isClose())
-                    contentProvider.close();
-            }
-        };
-    }
-
-    private void sendRequest(final Stream clientStream, Request request)
-    {
-        request.send(new Response.Listener.Adapter()
-        {
-            private volatile boolean committed;
-
-            @Override
-            public void onHeaders(final Response response)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("onHeaders called with response: {}. Sending replyInfo to client.", response);
-                Fields responseHeaders = createResponseHeaders(clientStream, response);
-                removeHopHeaders(responseHeaders);
-                ReplyInfo replyInfo = new ReplyInfo(responseHeaders, false);
-                clientStream.reply(replyInfo, new Callback.Adapter()
-                {
-                    @Override
-                    public void failed(Throwable x)
-                    {
-                        LOG.debug("failed: ", x);
-                        response.abort(x);
-                    }
-
-                    @Override
-                    public void succeeded()
-                    {
-                        committed = true;
-                    }
-                });
-            }
-
-            @Override
-            public void onContent(final Response response, ByteBuffer content)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("onContent called with response: {} and content: {}. Sending response content to client.",
-                        response, content);
-                final ByteBuffer contentCopy = httpClient.getByteBufferPool().acquire(content.remaining(), true);
-                BufferUtil.flipPutFlip(content, contentCopy);
-                ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(contentCopy, false);
-                clientStream.data(dataInfo, new Callback()
-                {
-                    @Override
-                    public void failed(Throwable x)
-                    {
-                        LOG.debug("failed: ", x);
-                        releaseBuffer();
-                        response.abort(x);
-                    }
-
-                    @Override
-                    public void succeeded()
-                    {
-                        releaseBuffer();
-                    }
-
-                    private void releaseBuffer()
-                    {
-                        httpClient.getByteBufferPool().release(contentCopy);
-                    }
-                });
-            }
-
-            @Override
-            public void onSuccess(Response response)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("onSuccess called. Closing client stream.");
-                clientStream.data(new ByteBufferDataInfo(BufferUtil.EMPTY_BUFFER, true), LOGGING_CALLBACK);
-            }
-
-            @Override
-            public void onFailure(Response response, Throwable failure)
-            {
-                LOG.debug("onFailure called: ", failure);
-                if (committed)
-                {
-                    LOG.debug("clientStream already committed. Resetting stream.");
-                    try
-                    {
-                        clientStream.getSession().rst(new RstInfo(clientStream.getId(), StreamStatus.INTERNAL_ERROR));
-                    }
-                    catch (InterruptedException | ExecutionException | TimeoutException e)
-                    {
-                        LOG.debug(e);
-                    }
-                }
-                else
-                {
-                    if (clientStream.isClosed())
-                        return;
-                    Fields responseHeaders = createResponseHeaders(clientStream, response);
-                    if (failure instanceof TimeoutException)
-                        responseHeaders.add(HTTPSPDYHeader.STATUS.name(clientStream.getSession().getVersion()),
-                                String.valueOf(HttpStatus.GATEWAY_TIMEOUT_504));
-                    else
-                        responseHeaders.add(HTTPSPDYHeader.STATUS.name(clientStream.getSession().getVersion()),
-                                String.valueOf(HttpStatus.BAD_GATEWAY_502));
-                    ReplyInfo replyInfo = new ReplyInfo(responseHeaders, true);
-                    clientStream.reply(replyInfo, LOGGING_CALLBACK);
-                }
-            }
-        });
-    }
-
-    private Fields createResponseHeaders(Stream clientStream, Response response)
-    {
-        Fields responseHeaders = new Fields();
-        for (HttpField header : response.getHeaders())
-            responseHeaders.add(header.getName(), header.getValue());
-            short version = clientStream.getSession().getVersion();
-        if (response.getStatus() > 0)
-            responseHeaders.add(HTTPSPDYHeader.STATUS.name(version),
-                    String.valueOf(response.getStatus()));
-        responseHeaders.add(HTTPSPDYHeader.VERSION.name(version), HttpVersion.HTTP_1_1.asString());
-        addResponseProxyHeaders(clientStream, responseHeaders);
-        return responseHeaders;
-    }
-
-    private void addNonSpdyHeadersToRequest(short version, Fields headers, Request request)
-    {
-        for (Fields.Field header : headers)
-            if (HTTPSPDYHeader.from(version, header.getName()) == null)
-                request.header(header.getName(), header.getValue());
-    }
-
-    static class LoggingCallback extends Callback.Adapter
-    {
-        @Override
-        public void failed(Throwable x)
-        {
-            LOG.debug(x);
-        }
-
-        @Override
-        public void succeeded()
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("succeeded");
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPSPDYProxyServerConnector.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPSPDYProxyServerConnector.java
deleted file mode 100644
index 45253c2..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPSPDYProxyServerConnector.java
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-import java.util.Objects;
-
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-
-public class HTTPSPDYProxyServerConnector extends ServerConnector
-{
-    public HTTPSPDYProxyServerConnector(Server server, ProxyEngineSelector proxyEngineSelector)
-    {
-        this(server, new HttpConfiguration(), proxyEngineSelector);
-    }
-
-    public HTTPSPDYProxyServerConnector(Server server, HttpConfiguration config, ProxyEngineSelector proxyEngineSelector)
-    {
-        super(server, (SslContextFactory)null, new ProxyHTTPConnectionFactory(config, SPDY.V2, proxyEngineSelector));
-    }
-
-    public HTTPSPDYProxyServerConnector(Server server, SslContextFactory sslContextFactory, ProxyEngineSelector proxyEngineSelector)
-    {
-        this(server, sslContextFactory, new HttpConfiguration(), proxyEngineSelector);
-    }
-
-    public HTTPSPDYProxyServerConnector(Server server, SslContextFactory sslContextFactory, HttpConfiguration config, ProxyEngineSelector proxyEngineSelector)
-    {
-        this(server, sslContextFactory, config, proxyEngineSelector, new NPNServerConnectionFactory("spdy/3", "spdy/2", "http/1.1"));
-    }
-
-    public HTTPSPDYProxyServerConnector(Server server, SslContextFactory sslContextFactory, HttpConfiguration config, ProxyEngineSelector proxyEngineSelector, NegotiatingServerConnectionFactory negotiatingFactory)
-    {
-        super(server, Objects.requireNonNull(sslContextFactory), negotiatingFactory,
-                new SPDYServerConnectionFactory(SPDY.V3, proxyEngineSelector),
-                new SPDYServerConnectionFactory(SPDY.V2, proxyEngineSelector),
-                new ProxyHTTPConnectionFactory(config, SPDY.V2, proxyEngineSelector));
-        negotiatingFactory.setDefaultProtocol("http/1.1");
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyEngine.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyEngine.java
deleted file mode 100644
index 84bc7b8..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyEngine.java
+++ /dev/null
@@ -1,129 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Fields;
-
-/**
- * <p>{@link ProxyEngine} is the class for SPDY proxy functionalities that receives a SPDY request and converts it to
- * any protocol to its server side.</p>
- * <p>This class listens for SPDY events sent by clients; subclasses are responsible for translating
- * these SPDY client events into appropriate events to forward to the server, in the appropriate
- * protocol that is understood by the server.</p>
- */
-public abstract class ProxyEngine
-{
-    private static final Set<String> HOP_HEADERS = new HashSet<>();
-    static
-    {
-        HOP_HEADERS.add("proxy-connection");
-        HOP_HEADERS.add("connection");
-        HOP_HEADERS.add("keep-alive");
-        HOP_HEADERS.add("transfer-encoding");
-        HOP_HEADERS.add("te");
-        HOP_HEADERS.add("trailer");
-        HOP_HEADERS.add("proxy-authorization");
-        HOP_HEADERS.add("proxy-authenticate");
-        HOP_HEADERS.add("upgrade");
-    }
-
-    private final String name;
-
-    protected ProxyEngine()
-    {
-        this(name());
-    }
-
-    private static String name()
-    {
-        try
-        {
-            return InetAddress.getLocalHost().getHostName();
-        }
-        catch (UnknownHostException x)
-        {
-            return "localhost";
-        }
-    }
-
-    public abstract StreamFrameListener proxy(Stream clientStream, SynInfo clientSynInfo, ProxyEngineSelector.ProxyServerInfo proxyServerInfo);
-
-    protected ProxyEngine(String name)
-    {
-        this.name = name;
-    }
-
-    public String getName()
-    {
-        return name;
-    }
-
-    protected void removeHopHeaders(Fields headers)
-    {
-        // Header names are case-insensitive (RFC2616) and oej.util.Fields.add converts the names to lowercase. So we
-        // need to compare with the lowercase values only
-        for (String hopHeader : HOP_HEADERS)
-            headers.remove(hopHeader);
-    }
-
-    protected void addRequestProxyHeaders(Stream stream, Fields headers)
-    {
-        addViaHeader(headers);
-        Fields.Field schemeField = headers.get(HTTPSPDYHeader.SCHEME.name(stream.getSession().getVersion()));
-        if(schemeField != null)
-            headers.add("X-Forwarded-Proto", schemeField.getValue());
-        InetSocketAddress address = stream.getSession().getRemoteAddress();
-        if (address != null)
-        {
-            headers.add("X-Forwarded-Host", address.getHostName());
-            headers.add("X-Forwarded-For", address.toString());
-        }
-        headers.add("X-Forwarded-Server", name());
-    }
-
-    protected void addResponseProxyHeaders(Stream stream, Fields headers)
-    {
-        addViaHeader(headers);
-    }
-
-    private void addViaHeader(Fields headers)
-    {
-        headers.add("Via", "http/1.1 " + getName());
-    }
-
-    protected void customizeRequestHeaders(Stream stream, Fields headers)
-    {
-    }
-
-    protected void customizeResponseHeaders(Stream stream, Fields headers)
-    {
-    }
-
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyEngineSelector.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyEngineSelector.java
deleted file mode 100644
index 100fa8a..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyEngineSelector.java
+++ /dev/null
@@ -1,203 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-import java.net.InetSocketAddress;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
-import org.eclipse.jetty.spdy.api.PingResultInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * <p>{@link ProxyEngineSelector} is the main entry point for push stream events of a jetty SPDY proxy. It receives the
- * push stream frames from the clients, checks if there's an appropriate {@link ProxyServerInfo} for the given target
- * host and forwards the push to a {@link ProxyEngine} for the protocol defined in {@link ProxyServerInfo}.</p>
- *
- * <p>If no {@link ProxyServerInfo} can be found for the given target host or no {@link ProxyEngine} can be found for
- * the given protocol, it resets the client stream.</p>
- *
- * <p>This class also provides configuration for the proxy rules.</p>
- */
-public class ProxyEngineSelector extends ServerSessionFrameListener.Adapter
-{
-    protected final Logger LOG = Log.getLogger(getClass());
-    private final Map<String, ProxyServerInfo> proxyInfos = new ConcurrentHashMap<>();
-    private final Map<String, ProxyEngine> proxyEngines = new ConcurrentHashMap<>();
-
-    @Override
-    public final StreamFrameListener onSyn(final Stream clientStream, SynInfo clientSynInfo)
-    {
-        if (LOG.isDebugEnabled())
-            LOG.debug("C -> P {} on {}", clientSynInfo, clientStream);
-
-        final Session clientSession = clientStream.getSession();
-        short clientVersion = clientSession.getVersion();
-        Fields headers = new Fields(clientSynInfo.getHeaders(), false);
-
-        Fields.Field hostHeader = headers.get(HTTPSPDYHeader.HOST.name(clientVersion));
-        if (hostHeader == null)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("No host header found: " + headers);
-            rst(clientStream);
-            return null;
-        }
-
-        String host = hostHeader.getValue();
-        int colon = host.indexOf(':');
-        if (colon >= 0)
-            host = host.substring(0, colon);
-
-        ProxyServerInfo proxyServerInfo = getProxyServerInfo(host);
-        if (proxyServerInfo == null)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("No matching ProxyServerInfo found for: " + host);
-            rst(clientStream);
-            return null;
-        }
-
-        String protocol = proxyServerInfo.getProtocol();
-        ProxyEngine proxyEngine = proxyEngines.get(protocol);
-        if (proxyEngine == null)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("No matching ProxyEngine found for: " + protocol);
-            rst(clientStream);
-            return null;
-        }
-        if (LOG.isDebugEnabled())
-            LOG.debug("Forwarding request: {} -> {}", clientSynInfo, proxyServerInfo);
-        return proxyEngine.proxy(clientStream, clientSynInfo, proxyServerInfo);
-    }
-
-    @Override
-    public void onPing(Session clientSession, PingResultInfo pingResultInfo)
-    {
-        // We do not know to which upstream server
-        // to send the PING so we just ignore it
-    }
-
-    @Override
-    public void onGoAway(Session session, GoAwayResultInfo goAwayResultInfo)
-    {
-        // TODO:
-    }
-
-    public Map<String, ProxyEngine> getProxyEngines()
-    {
-        return new HashMap<>(proxyEngines);
-    }
-
-    public void setProxyEngines(Map<String, ProxyEngine> proxyEngines)
-    {
-        this.proxyEngines.clear();
-        this.proxyEngines.putAll(proxyEngines);
-    }
-
-    public ProxyEngine getProxyEngine(String protocol)
-    {
-        return proxyEngines.get(protocol);
-    }
-
-    public void putProxyEngine(String protocol, ProxyEngine proxyEngine)
-    {
-        proxyEngines.put(protocol, proxyEngine);
-    }
-
-    public Map<String, ProxyServerInfo> getProxyServerInfos()
-    {
-        return new HashMap<>(proxyInfos);
-    }
-
-    protected ProxyServerInfo getProxyServerInfo(String host)
-    {
-        return proxyInfos.get(host);
-    }
-
-    public void setProxyServerInfos(Map<String, ProxyServerInfo> proxyServerInfos)
-    {
-        this.proxyInfos.clear();
-        this.proxyInfos.putAll(proxyServerInfos);
-    }
-
-    public void putProxyServerInfo(String host, ProxyServerInfo proxyServerInfo)
-    {
-        proxyInfos.put(host, proxyServerInfo);
-    }
-
-    private void rst(Stream stream)
-    {
-        RstInfo rstInfo = new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM);
-        stream.getSession().rst(rstInfo, Callback.Adapter.INSTANCE);
-    }
-
-    public static class ProxyServerInfo
-    {
-        private final String protocol;
-        private final String host;
-        private final InetSocketAddress address;
-
-        public ProxyServerInfo(String protocol, String host, int port)
-        {
-            this.protocol = protocol;
-            this.host = host;
-            this.address = new InetSocketAddress(host, port);
-        }
-
-        public String getProtocol()
-        {
-            return protocol;
-        }
-
-        public String getHost()
-        {
-            return host;
-        }
-
-        public InetSocketAddress getAddress()
-        {
-            return address;
-        }
-
-        @Override
-        public String toString()
-        {
-            return "ProxyServerInfo{" +
-                    "protocol='" + protocol + '\'' +
-                    ", host='" + host + '\'' +
-                    ", address=" + address +
-                    '}';
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPConnectionFactory.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPConnectionFactory.java
deleted file mode 100644
index 04c2b19..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPConnectionFactory.java
+++ /dev/null
@@ -1,57 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.io.Connection;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.server.AbstractConnectionFactory;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.HttpConfiguration;
-
-public class ProxyHTTPConnectionFactory extends AbstractConnectionFactory implements HttpConfiguration.ConnectionFactory
-{
-    private final short version;
-    private final ProxyEngineSelector proxyEngineSelector;
-    private final HttpConfiguration httpConfiguration;
-
-    public ProxyHTTPConnectionFactory(HttpConfiguration httpConfiguration,short version, ProxyEngineSelector proxyEngineSelector)
-    {
-        // replaces http/1.1
-        super(HttpVersion.HTTP_1_1.asString());
-        this.version = version;
-        this.proxyEngineSelector = proxyEngineSelector;
-        this.httpConfiguration=httpConfiguration;
-    }
-
-    @Override
-    public Connection newConnection(Connector connector, EndPoint endPoint)
-    {
-        return configure(new ProxyHTTPSPDYConnection(connector, httpConfiguration, endPoint, version, proxyEngineSelector),connector,endPoint);
-    }
-
-    @Override
-    public HttpConfiguration getHttpConfiguration()
-    {
-        return httpConfiguration;
-    }
-
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPSPDYConnection.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPSPDYConnection.java
deleted file mode 100644
index 53c9716..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPSPDYConnection.java
+++ /dev/null
@@ -1,385 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-import java.nio.ByteBuffer;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpGenerator;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpHeaderValue;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.HttpParser;
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpConnection;
-import org.eclipse.jetty.server.SslConnectionFactory;
-import org.eclipse.jetty.spdy.ISession;
-import org.eclipse.jetty.spdy.IStream;
-import org.eclipse.jetty.spdy.StandardSession;
-import org.eclipse.jetty.spdy.StandardStream;
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.SessionStatus;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-
-public class ProxyHTTPSPDYConnection extends HttpConnection implements HttpParser.RequestHandler<ByteBuffer>
-{
-    private final short version;
-    private final Fields headers = new Fields();
-    private final ProxyEngineSelector proxyEngineSelector;
-    private final ISession session;
-    private HTTPStream stream;
-    private ByteBuffer content;
-
-    public ProxyHTTPSPDYConnection(Connector connector, HttpConfiguration config, EndPoint endPoint, short version, ProxyEngineSelector proxyEngineSelector)
-    {
-        super(config, connector, endPoint);
-        this.version = version;
-        this.proxyEngineSelector = proxyEngineSelector;
-        this.session = new HTTPSession(version, connector);
-    }
-
-    @Override
-    protected HttpParser.RequestHandler<ByteBuffer> newRequestHandler()
-    {
-        return this;
-    }
-
-    @Override
-    public boolean startRequest(HttpMethod method, String methodString, ByteBuffer uri, HttpVersion httpVersion)
-    {
-        Connector connector = getConnector();
-        String scheme = connector.getConnectionFactory(SslConnectionFactory.class) != null ? "https" : "http";
-        headers.put(HTTPSPDYHeader.SCHEME.name(version), scheme);
-        headers.put(HTTPSPDYHeader.METHOD.name(version), methodString);
-        headers.put(HTTPSPDYHeader.URI.name(version), BufferUtil.toUTF8String(uri)); // TODO handle bad encodings
-        headers.put(HTTPSPDYHeader.VERSION.name(version), httpVersion.asString());
-        return false;
-    }
-
-    @Override
-    public boolean parsedHeader(HttpField field)
-    {
-        if (field.getHeader() == HttpHeader.HOST)
-            headers.put(HTTPSPDYHeader.HOST.name(version), field.getValue());
-        else
-            headers.put(field.getName(), field.getValue());
-        return false;
-    }
-
-    @Override
-    public boolean parsedHostHeader(String host, int port)
-    {
-        return false;
-    }
-
-    @Override
-    public boolean headerComplete()
-    {
-        return false;
-    }
-
-    @Override
-    public boolean content(ByteBuffer item)
-    {
-        if (content == null)
-        {
-            stream = syn(false);
-            content = item;
-        }
-        else
-        {
-            stream.getStreamFrameListener().onData(stream, toDataInfo(item, false));
-        }
-        return false;
-    }
-
-    @Override
-    public boolean messageComplete()
-    {
-        if (stream == null)
-        {
-            assert content == null;
-            if (headers.isEmpty())
-                proxyEngineSelector.onGoAway(session, new GoAwayResultInfo(0, SessionStatus.OK));
-            else
-                syn(true);
-        }
-        else
-        {
-            stream.getStreamFrameListener().onData(stream, toDataInfo(content, true));
-        }
-        return false;
-    }
-
-    @Override
-    public void completed()
-    {
-        headers.clear();
-        stream = null;
-        content = null;
-        super.completed();
-    }
-
-    @Override
-    public int getHeaderCacheSize()
-    {
-        // TODO get from configuration
-        return 256;
-    }
-
-    @Override
-    public void earlyEOF()
-    {
-        // TODO
-    }
-
-    @Override
-    public void badMessage(int status, String reason)
-    {
-        // TODO
-    }
-
-    private HTTPStream syn(boolean close)
-    {
-        HTTPStream stream = new HTTPStream(1, (byte)0, session, null);
-        StreamFrameListener streamFrameListener = proxyEngineSelector.onSyn(stream, new SynInfo(headers, close));
-        stream.setStreamFrameListener(streamFrameListener);
-        return stream;
-    }
-
-    private DataInfo toDataInfo(ByteBuffer buffer, boolean close)
-    {
-        return new ByteBufferDataInfo(buffer, close);
-    }
-
-    private class HTTPSession extends StandardSession
-    {
-        private HTTPSession(short version, Connector connector)
-        {
-            super(version, connector.getByteBufferPool(), connector.getScheduler(), null,
-                    getEndPoint(), null, 1, proxyEngineSelector, null, null);
-        }
-
-        @Override
-        public void rst(RstInfo rstInfo, Callback handler)
-        {
-            HttpGenerator.ResponseInfo info = new HttpGenerator.ResponseInfo(HttpVersion.fromString(headers.get
-                    ("version").getValue()), null, 0, 502, "SPDY reset received from upstream server", false);
-            send(info, null, true, Callback.Adapter.INSTANCE);
-        }
-
-        @Override
-        public void goAway(GoAwayInfo goAwayInfo, Callback handler)
-        {
-            ProxyHTTPSPDYConnection.this.close();
-            handler.succeeded();
-        }
-    }
-
-    /**
-     * <p>This stream will convert the SPDY invocations performed by the proxy into HTTP to be sent to the client.</p>
-     */
-    private class HTTPStream extends StandardStream
-    {
-        private final Pattern statusRegexp = Pattern.compile("(\\d{3})\\s+(.*)");
-
-        private HTTPStream(int id, byte priority, ISession session, IStream associatedStream)
-        {
-            super(id, priority, session, associatedStream, getHttpChannel().getScheduler(), null);
-        }
-
-        @Override
-        public void push(PushInfo pushInfo, Promise<Stream> handler)
-        {
-            // HTTP does not support pushed streams
-            handler.succeeded(new HTTPPushStream(2, getPriority(), getSession(), this));
-        }
-
-        @Override
-        public void headers(HeadersInfo headersInfo, Callback handler)
-        {
-            // TODO
-            throw new UnsupportedOperationException("Not Yet Implemented");
-        }
-
-        @Override
-        public void reply(ReplyInfo replyInfo, final Callback handler)
-        {
-            Fields headers = new Fields(replyInfo.getHeaders(), false);
-
-                addPersistenceHeader(headers);
-
-            headers.remove(HTTPSPDYHeader.SCHEME.name(version));
-
-            String status = headers.remove(HTTPSPDYHeader.STATUS.name(version)).getValue();
-            Matcher matcher = statusRegexp.matcher(status);
-            matcher.matches();
-            int code = Integer.parseInt(matcher.group(1));
-            String reason = matcher.group(2).trim();
-
-            HttpVersion httpVersion = HttpVersion.fromString(headers.remove(HTTPSPDYHeader.VERSION.name(version)).getValue());
-
-            // Convert the Host header from a SPDY special header to a normal header
-            Fields.Field host = headers.remove(HTTPSPDYHeader.HOST.name(version));
-            if (host != null)
-                headers.put("host", host.getValue());
-
-            HttpFields fields = new HttpFields();
-            for (Fields.Field header : headers)
-            {
-                String name = camelize(header.getName());
-                fields.put(name, header.getValue());
-            }
-
-            // TODO: handle better the HEAD last parameter
-            long contentLength = fields.getLongField(HttpHeader.CONTENT_LENGTH.asString());
-            HttpGenerator.ResponseInfo info = new HttpGenerator.ResponseInfo(httpVersion, fields, contentLength, code,
-                    reason, false);
-
-            send(info, null, replyInfo.isClose(), new Adapter()
-            {
-                @Override
-                public void failed(Throwable x)
-                {
-                    handler.failed(x);
-                }
-            });
-
-            if (replyInfo.isClose())
-                completed();
-
-            handler.succeeded();
-        }
-
-        private String camelize(String name)
-        {
-            char[] chars = name.toCharArray();
-            chars[0] = Character.toUpperCase(chars[0]);
-
-            for (int i = 0; i < chars.length; ++i)
-            {
-                char c = chars[i];
-                int j = i + 1;
-                if (c == '-' && j < chars.length)
-                    chars[j] = Character.toUpperCase(chars[j]);
-            }
-            return new String(chars);
-        }
-
-        @Override
-        public void data(DataInfo dataInfo, final Callback handler)
-        {
-            // Data buffer must be copied, as the ByteBuffer is pooled
-            ByteBuffer byteBuffer = dataInfo.asByteBuffer(false);
-
-            send(null, byteBuffer, dataInfo.isClose(), new Adapter()
-            {
-                @Override
-                public void failed(Throwable x)
-                {
-                    handler.failed(x);
-                }
-            });
-
-            if (dataInfo.isClose())
-                completed();
-
-            handler.succeeded();
-        }
-    }
-
-    private void addPersistenceHeader(Fields headersToAddTo)
-    {
-        HttpVersion httpVersion = HttpVersion.fromString(headers.get("version").getValue());
-        boolean persistent = false;
-        switch (httpVersion)
-        {
-            case HTTP_1_0:
-            {
-                Fields.Field keepAliveHeader = headers.get(HttpHeader.KEEP_ALIVE.asString());
-                if(keepAliveHeader!=null)
-                    persistent = HttpHeaderValue.KEEP_ALIVE.asString().equals(keepAliveHeader.getValue());
-                if (!persistent)
-                    persistent = HttpMethod.CONNECT.is(headers.get("method").getValue());
-                if (persistent)
-                    headersToAddTo.add(HttpHeader.CONNECTION.asString(), HttpHeaderValue.KEEP_ALIVE.asString());
-                break;
-            }
-            case HTTP_1_1:
-            {
-                Fields.Field connectionHeader = headers.get(HttpHeader.CONNECTION.asString());
-                if(connectionHeader != null)
-                    persistent = !HttpHeaderValue.CLOSE.asString().equals(connectionHeader.getValue());
-                else
-                    persistent = true;
-                if (!persistent)
-                    persistent = HttpMethod.CONNECT.is(headers.get("method").getValue());
-                if (!persistent)
-                    headersToAddTo.add(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
-                break;
-            }
-            default:
-            {
-                throw new IllegalStateException();
-            }
-        }
-    }
-
-    private class HTTPPushStream extends StandardStream
-    {
-        private HTTPPushStream(int id, byte priority, ISession session, IStream associatedStream)
-        {
-            super(id, priority, session, associatedStream, getHttpChannel().getScheduler(), null);
-        }
-
-        @Override
-        public void headers(HeadersInfo headersInfo, Callback handler)
-        {
-            // Ignore pushed headers
-            handler.succeeded();
-        }
-
-        @Override
-        public void data(DataInfo dataInfo, Callback handler)
-        {
-            // Ignore pushed data
-            handler.succeeded();
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/SPDYProxyEngine.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/SPDYProxyEngine.java
deleted file mode 100644
index a186f9b..0000000
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/SPDYProxyEngine.java
+++ /dev/null
@@ -1,631 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-import java.net.InetSocketAddress;
-import java.util.LinkedList;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.Info;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * <p>{@link SPDYProxyEngine} implements a SPDY to SPDY proxy, that is, converts SPDY events received by clients into
- * SPDY events for the servers.</p>
- */
-public class SPDYProxyEngine extends ProxyEngine implements StreamFrameListener
-{
-    private static final Logger LOG = Log.getLogger(SPDYProxyEngine.class);
-
-    private static final String STREAM_PROMISE_ATTRIBUTE = "org.eclipse.jetty.spdy.server.http.proxy.streamPromise";
-    private static final String CLIENT_STREAM_ATTRIBUTE = "org.eclipse.jetty.spdy.server.http.proxy.clientStream";
-
-    private final ConcurrentMap<String, Session> serverSessions = new ConcurrentHashMap<>();
-    private final SessionFrameListener sessionListener = new ProxySessionFrameListener();
-    private final SPDYClient.Factory factory;
-    private volatile long connectTimeout = 15000;
-    private volatile long timeout = 60000;
-
-    public SPDYProxyEngine(SPDYClient.Factory factory)
-    {
-        this.factory = factory;
-    }
-
-    public long getConnectTimeout()
-    {
-        return connectTimeout;
-    }
-
-    public void setConnectTimeout(long connectTimeout)
-    {
-        this.connectTimeout = connectTimeout;
-    }
-
-    public long getTimeout()
-    {
-        return timeout;
-    }
-
-    public void setTimeout(long timeout)
-    {
-        this.timeout = timeout;
-    }
-
-    public StreamFrameListener proxy(final Stream clientStream, SynInfo clientSynInfo, ProxyEngineSelector.ProxyServerInfo proxyServerInfo)
-    {
-        Fields headers = new Fields(clientSynInfo.getHeaders(), false);
-
-        short serverVersion = getVersion(proxyServerInfo.getProtocol());
-        InetSocketAddress address = proxyServerInfo.getAddress();
-        Session serverSession = produceSession(proxyServerInfo.getHost(), serverVersion, address);
-        if (serverSession == null)
-        {
-            rst(clientStream);
-            return null;
-        }
-
-        final Session clientSession = clientStream.getSession();
-
-        addRequestProxyHeaders(clientStream, headers);
-        customizeRequestHeaders(clientStream, headers);
-        convert(clientSession.getVersion(), serverVersion, headers);
-
-        SynInfo serverSynInfo = new SynInfo(headers, clientSynInfo.isClose());
-        StreamFrameListener listener = new ProxyStreamFrameListener(clientStream);
-        StreamPromise promise = new StreamPromise(clientStream, serverSynInfo);
-        clientStream.setAttribute(STREAM_PROMISE_ATTRIBUTE, promise);
-        serverSession.syn(serverSynInfo, listener, promise);
-        return this;
-    }
-
-    private static short getVersion(String protocol)
-    {
-        switch (protocol)
-        {
-            case "spdy/2":
-                return SPDY.V2;
-            case "spdy/3":
-                return SPDY.V3;
-            default:
-                throw new IllegalArgumentException("Procotol: " + protocol + " is not a known SPDY protocol");
-        }
-    }
-
-    @Override
-    public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-    {
-        throw new IllegalStateException("We shouldn't receive pushes from clients");
-    }
-
-    @Override
-    public void onReply(Stream stream, ReplyInfo replyInfo)
-    {
-        throw new IllegalStateException("Servers do not receive replies");
-    }
-
-    @Override
-    public void onHeaders(Stream stream, HeadersInfo headersInfo)
-    {
-        // TODO
-        throw new UnsupportedOperationException("Not Yet Implemented");
-    }
-
-    @Override
-    public void onData(Stream clientStream, final DataInfo clientDataInfo)
-    {
-        if (LOG.isDebugEnabled())
-            LOG.debug("C -> P {} on {}", clientDataInfo, clientStream);
-
-        ByteBufferDataInfo serverDataInfo = new ByteBufferDataInfo(clientDataInfo.asByteBuffer(false), clientDataInfo.isClose())
-        {
-            @Override
-            public void consume(int delta)
-            {
-                super.consume(delta);
-                clientDataInfo.consume(delta);
-            }
-        };
-
-        StreamPromise streamPromise = (StreamPromise)clientStream.getAttribute(STREAM_PROMISE_ATTRIBUTE);
-        streamPromise.data(serverDataInfo);
-    }
-
-    @Override
-    public void onFailure(Stream stream, Throwable x)
-    {
-        LOG.debug(x);
-    }
-
-    private Session produceSession(String host, short version, InetSocketAddress address)
-    {
-        try
-        {
-            Session session = serverSessions.get(host);
-            if (session == null)
-            {
-                SPDYClient client = factory.newSPDYClient(version);
-                session = client.connect(address, sessionListener);
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Proxy session connected to {}", address);
-                Session existing = serverSessions.putIfAbsent(host, session);
-                if (existing != null)
-                {
-                    session.goAway(new GoAwayInfo(), Callback.Adapter.INSTANCE);
-                    session = existing;
-                }
-            }
-            return session;
-        }
-        catch (Exception x)
-        {
-            LOG.debug(x);
-            return null;
-        }
-    }
-
-    private void convert(short fromVersion, short toVersion, Fields headers)
-    {
-        if (fromVersion != toVersion)
-        {
-            for (HTTPSPDYHeader httpHeader : HTTPSPDYHeader.values())
-            {
-                Fields.Field header = headers.remove(httpHeader.name(fromVersion));
-                if (header != null)
-                {
-                    String toName = httpHeader.name(toVersion);
-                    for (String value : header.getValues())
-                        headers.add(toName, value);
-                }
-            }
-        }
-    }
-
-    private void rst(Stream stream)
-    {
-        RstInfo rstInfo = new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM);
-        stream.getSession().rst(rstInfo, Callback.Adapter.INSTANCE);
-    }
-
-    private class ProxyPushStreamFrameListener implements StreamFrameListener
-    {
-        private PushStreamPromise pushStreamPromise;
-
-        private ProxyPushStreamFrameListener(PushStreamPromise pushStreamPromise)
-        {
-            this.pushStreamPromise = pushStreamPromise;
-        }
-
-        @Override
-        public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("S -> P pushed {} on {}. Opening new PushStream P -> C now.", pushInfo, stream);
-            PushStreamPromise newPushStreamPromise = new PushStreamPromise(stream, pushInfo);
-            this.pushStreamPromise.push(newPushStreamPromise);
-            return new ProxyPushStreamFrameListener(newPushStreamPromise);
-        }
-
-        @Override
-        public void onReply(Stream stream, ReplyInfo replyInfo)
-        {
-            // Push streams never send a reply
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void onHeaders(Stream stream, HeadersInfo headersInfo)
-        {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void onData(Stream serverStream, final DataInfo serverDataInfo)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("S -> P pushed {} on {}", serverDataInfo, serverStream);
-
-            ByteBufferDataInfo clientDataInfo = new ByteBufferDataInfo(serverDataInfo.asByteBuffer(false), serverDataInfo.isClose())
-            {
-                @Override
-                public void consume(int delta)
-                {
-                    super.consume(delta);
-                    serverDataInfo.consume(delta);
-                }
-            };
-
-            pushStreamPromise.data(clientDataInfo);
-        }
-
-        @Override
-        public void onFailure(Stream stream, Throwable x)
-        {
-            LOG.debug(x);
-        }
-    }
-
-    private class ProxyStreamFrameListener extends StreamFrameListener.Adapter
-    {
-        private final Stream receiverStream;
-
-        public ProxyStreamFrameListener(Stream receiverStream)
-        {
-            this.receiverStream = receiverStream;
-        }
-
-        @Override
-        public StreamFrameListener onPush(Stream senderStream, PushInfo pushInfo)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("S -> P {} on {}");
-            PushInfo newPushInfo = convertPushInfo(pushInfo, senderStream, receiverStream);
-            PushStreamPromise pushStreamPromise = new PushStreamPromise(senderStream, newPushInfo);
-            receiverStream.push(newPushInfo, pushStreamPromise);
-            return new ProxyPushStreamFrameListener(pushStreamPromise);
-        }
-
-        @Override
-        public void onReply(final Stream stream, ReplyInfo replyInfo)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("S -> P {} on {}", replyInfo, stream);
-            final ReplyInfo clientReplyInfo = new ReplyInfo(convertHeaders(stream, receiverStream, replyInfo.getHeaders()),
-                    replyInfo.isClose());
-            reply(stream, clientReplyInfo);
-        }
-
-        private void reply(final Stream stream, final ReplyInfo clientReplyInfo)
-        {
-            receiverStream.reply(clientReplyInfo, new Callback()
-            {
-                @Override
-                public void succeeded()
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("P -> C {} from {} to {}", clientReplyInfo, stream, receiverStream);
-                }
-
-                @Override
-                public void failed(Throwable x)
-                {
-                    LOG.debug(x);
-                    rst(receiverStream);
-                }
-            });
-        }
-
-        @Override
-        public void onHeaders(Stream stream, HeadersInfo headersInfo)
-        {
-            // TODO
-            throw new UnsupportedOperationException("Not Yet Implemented");
-        }
-
-        @Override
-        public void onData(final Stream stream, final DataInfo dataInfo)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("S -> P {} on {}", dataInfo, stream);
-            data(stream, dataInfo);
-        }
-
-        private void data(final Stream stream, final DataInfo serverDataInfo)
-        {
-            final ByteBufferDataInfo clientDataInfo = new ByteBufferDataInfo(serverDataInfo.asByteBuffer(false), serverDataInfo.isClose())
-            {
-                @Override
-                public void consume(int delta)
-                {
-                    super.consume(delta);
-                    serverDataInfo.consume(delta);
-                }
-            };
-
-            receiverStream.data(clientDataInfo, new Callback() //TODO: timeout???
-            {
-                @Override
-                public void succeeded()
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("P -> C {} from {} to {}", clientDataInfo, stream, receiverStream);
-                }
-
-                @Override
-                public void failed(Throwable x)
-                {
-                    LOG.debug(x);
-                    rst(receiverStream);
-                }
-            });
-        }
-    }
-
-    /**
-     * <p>{@link StreamPromise} implements the forwarding of DATA frames from the client to the server and vice
-     * versa.</p> <p>Instances of this class buffer DATA frames sent by clients and send them to the server. The
-     * buffering happens between the send of the SYN_STREAM to the server (where DATA frames may arrive from the client
-     * before the SYN_STREAM has been fully sent), and between DATA frames, if the client is a fast producer and the
-     * server a slow consumer, or if the client is a SPDY v2 client (and hence without flow control) while the server is
-     * a SPDY v3 server (and hence with flow control).</p>
-     */
-    private class StreamPromise implements Promise<Stream>
-    {
-        private final Queue<DataInfoCallback> queue = new LinkedList<>();
-        private final Stream senderStream;
-        private final Info info;
-        private Stream receiverStream;
-
-        private StreamPromise(Stream senderStream, Info info)
-        {
-            this.senderStream = senderStream;
-            this.info = info;
-        }
-
-        @Override
-        public void succeeded(Stream stream)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("P -> S {} from {} to {}", info, senderStream, stream);
-
-            stream.setAttribute(CLIENT_STREAM_ATTRIBUTE, senderStream);
-
-            DataInfoCallback dataInfoCallback;
-            synchronized (queue)
-            {
-                this.receiverStream = stream;
-                dataInfoCallback = queue.peek();
-                if (dataInfoCallback != null)
-                {
-                    if (dataInfoCallback.flushing)
-                    {
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("SYN completed, flushing {}, queue size {}", dataInfoCallback.dataInfo, queue.size());
-                        dataInfoCallback = null;
-                    }
-                    else
-                    {
-                        dataInfoCallback.flushing = true;
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("SYN completed, queue size {}", queue.size());
-                    }
-                }
-                else
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("SYN completed, queue empty");
-                }
-            }
-            if (dataInfoCallback != null)
-                flush(stream, dataInfoCallback);
-        }
-
-        @Override
-        public void failed(Throwable x)
-        {
-            LOG.debug(x);
-            rst(senderStream);
-        }
-
-        public void data(DataInfo dataInfo)
-        {
-            Stream receiverStream;
-            DataInfoCallback dataInfoCallbackToFlush = null;
-            DataInfoCallback dataInfoCallBackToQueue = new DataInfoCallback(dataInfo);
-            synchronized (queue)
-            {
-                queue.offer(dataInfoCallBackToQueue);
-                receiverStream = this.receiverStream;
-                if (receiverStream != null)
-                {
-                    dataInfoCallbackToFlush = queue.peek();
-                    if (dataInfoCallbackToFlush.flushing)
-                    {
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("Queued {}, flushing {}, queue size {}", dataInfo, dataInfoCallbackToFlush.dataInfo, queue.size());
-                        receiverStream = null;
-                    }
-                    else
-                    {
-                        dataInfoCallbackToFlush.flushing = true;
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("Queued {}, queue size {}", dataInfo, queue.size());
-                    }
-                }
-                else
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Queued {}, SYN incomplete, queue size {}", dataInfo, queue.size());
-                }
-            }
-            if (receiverStream != null)
-                flush(receiverStream, dataInfoCallbackToFlush);
-        }
-
-        private void flush(Stream receiverStream, DataInfoCallback dataInfoCallback)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("P -> S {} on {}", dataInfoCallback.dataInfo, receiverStream);
-            receiverStream.data(dataInfoCallback.dataInfo, dataInfoCallback); //TODO: timeout???
-        }
-
-        private class DataInfoCallback implements Callback
-        {
-            private final DataInfo dataInfo;
-            private boolean flushing;
-
-            private DataInfoCallback(DataInfo dataInfo)
-            {
-                this.dataInfo = dataInfo;
-            }
-
-            @Override
-            public void succeeded()
-            {
-                Stream serverStream;
-                DataInfoCallback dataInfoCallback;
-                synchronized (queue)
-                {
-                    serverStream = StreamPromise.this.receiverStream;
-                    assert serverStream != null;
-                    dataInfoCallback = queue.poll();
-                    assert dataInfoCallback == this;
-                    dataInfoCallback = queue.peek();
-                    if (dataInfoCallback != null)
-                    {
-                        assert !dataInfoCallback.flushing;
-                        dataInfoCallback.flushing = true;
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("Completed {}, queue size {}", dataInfo, queue.size());
-                    }
-                    else
-                    {
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("Completed {}, queue empty", dataInfo);
-                    }
-                }
-                if (dataInfoCallback != null)
-                    flush(serverStream, dataInfoCallback);
-            }
-
-            @Override
-            public void failed(Throwable x)
-            {
-                LOG.debug(x);
-                rst(senderStream);
-            }
-        }
-
-        public Stream getSenderStream()
-        {
-            return senderStream;
-        }
-
-        public Info getInfo()
-        {
-            return info;
-        }
-
-        public Stream getReceiverStream()
-        {
-            synchronized (queue)
-            {
-                return receiverStream;
-            }
-        }
-    }
-
-    private class PushStreamPromise extends StreamPromise
-    {
-        private volatile PushStreamPromise pushStreamPromise;
-
-        private PushStreamPromise(Stream senderStream, PushInfo pushInfo)
-        {
-            super(senderStream, pushInfo);
-        }
-
-        @Override
-        public void succeeded(Stream receiverStream)
-        {
-            super.succeeded(receiverStream);
-
-            if (LOG.isDebugEnabled())
-                LOG.debug("P -> C PushStreamPromise.succeeded() called with pushStreamPromise: {}", pushStreamPromise);
-
-            PushStreamPromise promise = pushStreamPromise;
-            if (promise != null)
-                receiverStream.push(convertPushInfo((PushInfo)getInfo(), getSenderStream(), receiverStream), pushStreamPromise);
-        }
-
-        public void push(PushStreamPromise pushStreamPromise)
-        {
-            Stream receiverStream = getReceiverStream();
-
-            if (receiverStream != null)
-                receiverStream.push(convertPushInfo((PushInfo)getInfo(), getSenderStream(), receiverStream), pushStreamPromise);
-            else
-                this.pushStreamPromise = pushStreamPromise;
-        }
-    }
-
-    private class ProxySessionFrameListener extends SessionFrameListener.Adapter
-    {
-        @Override
-        public void onRst(Session serverSession, RstInfo serverRstInfo)
-        {
-            Stream serverStream = serverSession.getStream(serverRstInfo.getStreamId());
-            if (serverStream != null)
-            {
-                Stream clientStream = (Stream)serverStream.getAttribute(CLIENT_STREAM_ATTRIBUTE);
-                if (clientStream != null)
-                {
-                    Session clientSession = clientStream.getSession();
-                    RstInfo clientRstInfo = new RstInfo(clientStream.getId(), serverRstInfo.getStreamStatus());
-                    clientSession.rst(clientRstInfo, Callback.Adapter.INSTANCE);
-                }
-            }
-        }
-
-        @Override
-        public void onGoAway(Session serverSession, GoAwayResultInfo goAwayResultInfo)
-        {
-            serverSessions.values().remove(serverSession);
-        }
-    }
-
-    private PushInfo convertPushInfo(PushInfo pushInfo, Stream from, Stream to)
-    {
-        Fields headersToConvert = pushInfo.getHeaders();
-        Fields headers = convertHeaders(from, to, headersToConvert);
-        return new PushInfo(getTimeout(), TimeUnit.MILLISECONDS, headers, pushInfo.isClose());
-    }
-
-    private Fields convertHeaders(Stream from, Stream to, Fields headersToConvert)
-    {
-        Fields headers = new Fields(headersToConvert, false);
-        addResponseProxyHeaders(from, headers);
-        customizeResponseHeaders(from, headers);
-        convert(from.getSession().getVersion(), to.getSession().getVersion(), headers);
-        return headers;
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/AbstractHTTPSPDYTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/AbstractHTTPSPDYTest.java
deleted file mode 100644
index be39ec1..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/AbstractHTTPSPDYTest.java
+++ /dev/null
@@ -1,136 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.http;
-
-import java.net.InetSocketAddress;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.concurrent.Executor;
-
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.junit.After;
-import org.junit.Rule;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public abstract class AbstractHTTPSPDYTest
-{
-    @Parameterized.Parameters
-    public static Collection<Short[]> parameters()
-    {
-        return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3});
-    }
-
-    @Rule
-    public final TestWatcher testName = new TestWatcher()
-    {
-
-        @Override
-        public void starting(Description description)
-        {
-            super.starting(description);
-            System.err.printf("Running %s.%s()%n",
-                    description.getClassName(),
-                    description.getMethodName());
-        }
-    };
-
-    protected final short version;
-    protected Server server;
-    protected SPDYClient.Factory clientFactory;
-    protected HTTPSPDYServerConnector connector;
-
-    protected AbstractHTTPSPDYTest(short version)
-    {
-        this.version = version;
-    }
-
-    protected InetSocketAddress startHTTPServer(short version, Handler handler, long idleTimeout) throws Exception
-    {
-        QueuedThreadPool threadPool = new QueuedThreadPool(256);
-        threadPool.setName("serverQTP");
-        server = new Server(threadPool);
-        connector = newHTTPSPDYServerConnector(version);
-        connector.setPort(0);
-        connector.setIdleTimeout(idleTimeout);
-        server.addConnector(connector);
-        server.setHandler(handler);
-        server.start();
-        return new InetSocketAddress("localhost", connector.getLocalPort());
-    }
-
-    protected Server getServer()
-    {
-        return server;
-    }
-
-    protected HTTPSPDYServerConnector newHTTPSPDYServerConnector(short version)
-    {
-        // For these tests, we need the connector to speak HTTP over SPDY even in non-SSL
-        HttpConfiguration httpConfiguration = new HttpConfiguration();
-        httpConfiguration.setSendServerVersion(true);
-        httpConfiguration.setSendXPoweredBy(true);
-        return new HTTPSPDYServerConnector(server, version, httpConfiguration, new PushStrategy.None());
-    }
-
-    protected Session startClient(short version, InetSocketAddress socketAddress, SessionFrameListener listener) throws Exception
-    {
-        if (clientFactory == null)
-        {
-            QueuedThreadPool threadPool = new QueuedThreadPool();
-            threadPool.setName(threadPool.getName() + "-client");
-            clientFactory = newSPDYClientFactory(threadPool);
-            clientFactory.start();
-        }
-        return clientFactory.newSPDYClient(version).connect(socketAddress, listener);
-    }
-
-    protected SPDYClient.Factory newSPDYClientFactory(Executor threadPool)
-    {
-        return new SPDYClient.Factory(threadPool, null, null, connector.getIdleTimeout());
-    }
-
-    @After
-    public void destroy() throws Exception
-    {
-        ((StdErrLog)Log.getLogger(HTTPSPDYServerConnector.class)).setHideStacks(true);
-        if (clientFactory != null)
-        {
-            clientFactory.stop();
-        }
-        if (server != null)
-        {
-            server.stop();
-            server.join();
-        }
-        ((StdErrLog)Log.getLogger(HTTPSPDYServerConnector.class)).setHideStacks(false);
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ConcurrentStreamsTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ConcurrentStreamsTest.java
deleted file mode 100644
index bfc58e9..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ConcurrentStreamsTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.http;
-
-import java.io.IOException;
-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.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class ConcurrentStreamsTest extends AbstractHTTPSPDYTest
-{
-    public ConcurrentStreamsTest(short version)
-    {
-        super(version);
-    }
-
-    @Test
-    public void testSlowStreamDoesNotBlockOtherStreams() throws Exception
-    {
-        final CountDownLatch slowServerLatch = new CountDownLatch(1);
-        final CountDownLatch fastServerLatch = new CountDownLatch(1);
-        final String fastPath = "/fast";
-        final String slowPath = "/slow";
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                try
-                {
-                    request.setHandled(true);
-                    switch (target)
-                    {
-                        case slowPath:
-                            Assert.assertTrue(fastServerLatch.await(10, TimeUnit.SECONDS));
-                            slowServerLatch.countDown();
-                            break;
-                        case fastPath:
-                            fastServerLatch.countDown();
-                            break;
-                        default:
-                            Assert.fail();
-                            break;
-                    }
-                }
-                catch (InterruptedException x)
-                {
-                    throw new ServletException(x);
-                }
-            }
-        }, 30000), null);
-
-        // Perform slow request. This will wait on server side until the fast request wakes it up
-        Fields headers = createHeaders(slowPath);
-        final CountDownLatch slowClientLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields replyHeaders = replyInfo.getHeaders();
-                Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                slowClientLatch.countDown();
-            }
-        });
-
-        // Perform the fast request. This will wake up the slow request
-        headers = createHeaders(fastPath);
-        final CountDownLatch fastClientLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields replyHeaders = replyInfo.getHeaders();
-                Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                fastClientLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(fastServerLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(slowServerLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(fastClientLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(slowClientLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    private Fields createHeaders(String path)
-    {
-        Fields headers = new Fields();
-        headers.put(HTTPSPDYHeader.METHOD.name(version), "GET");
-        headers.put(HTTPSPDYHeader.URI.name(version), path);
-        headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-        headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + connector.getLocalPort());
-        return headers;
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDYTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDYTest.java
deleted file mode 100644
index ae5e75f..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDYTest.java
+++ /dev/null
@@ -1,288 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.http;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.Random;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.http.HttpGenerator;
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-
-@RunWith(MockitoJUnitRunner.class)
-public class HttpTransportOverSPDYTest
-{
-    @Mock
-    Connector connector;
-    @Mock
-    HttpConfiguration httpConfiguration;
-    @Mock
-    EndPoint endPoint;
-    @Mock
-    PushStrategy pushStrategy;
-    @Mock
-    Stream stream;
-    @Mock
-    Callback callback;
-    @Mock
-    Session session;
-    @Mock
-    HttpGenerator.ResponseInfo responseInfo;
-
-    Random random = new Random();
-    short version = SPDY.V3;
-
-    HttpTransportOverSPDY httpTransportOverSPDY;
-
-    @Before
-    public void setUp() throws Exception
-    {
-        Fields requestHeaders = new Fields();
-        requestHeaders.add(HTTPSPDYHeader.METHOD.name(version), "GET");
-        when(responseInfo.getStatus()).thenReturn(HttpStatus.OK_200);
-        when(stream.getSession()).thenReturn(session);
-        when(session.getVersion()).thenReturn(SPDY.V3);
-        when(stream.isClosed()).thenReturn(false);
-        httpTransportOverSPDY = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint, pushStrategy,
-                stream, requestHeaders);
-    }
-
-    @Test
-    public void testSendWithResponseInfoNullAndContentNullAndLastContentTrue() throws Exception
-    {
-        ByteBuffer content = null;
-        boolean lastContent = true;
-
-        httpTransportOverSPDY.send(null, content, lastContent, callback);
-        ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
-        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
-        verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
-        callbackCaptor.getValue().succeeded();
-        verify(callback, times(1)).succeeded();
-        assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
-        assertThat("ByteBuffer is empty", dataInfoCaptor.getValue().length(), is(0));
-    }
-
-    @Test
-    public void testSendWithResponseInfoNullAndContentAndLastContentTrue() throws Exception
-    {
-        ByteBuffer content = createRandomByteBuffer();
-
-        boolean lastContent = true;
-
-        httpTransportOverSPDY.send(null, content, lastContent, callback);
-        ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
-        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
-        verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
-        callbackCaptor.getValue().succeeded();
-        verify(callback, times(1)).succeeded();
-        assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
-        assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
-    }
-
-    @Test
-    public void testSendWithResponseInfoNullAndEmptyContentAndLastContentTrue() throws Exception
-    {
-        ByteBuffer content = BufferUtil.EMPTY_BUFFER;
-        boolean lastContent = true;
-
-        httpTransportOverSPDY.send(null, content, lastContent, callback);
-        ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
-        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
-        verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
-        callbackCaptor.getValue().succeeded();
-        verify(callback, times(1)).succeeded();
-        assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
-        assertThat("ByteBuffer is empty", dataInfoCaptor.getValue().length(), is(0));
-    }
-
-    @Test
-    public void testSendWithResponseInfoNullAndContentAndLastContentFalse() throws Exception
-    {
-        ByteBuffer content = createRandomByteBuffer();
-        boolean lastContent = false;
-
-        httpTransportOverSPDY.send(null, content, lastContent, callback);
-        ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
-        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
-        verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
-        callbackCaptor.getValue().succeeded();
-        verify(callback, times(1)).succeeded();
-        assertThat("lastContent is false", dataInfoCaptor.getValue().isClose(), is(false));
-        assertThat("ByteBuffer is empty", dataInfoCaptor.getValue().length(), is(4096));
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testSendWithResponseInfoNullAndContentNullAndLastContentFalse() throws Exception
-    {
-        ByteBuffer content = null;
-        boolean lastContent = false;
-        httpTransportOverSPDY.send(null, content, lastContent, callback);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testSendWithResponseInfoNullAndEmptyContentAndLastContentFalse() throws Exception
-    {
-        ByteBuffer content = BufferUtil.EMPTY_BUFFER;
-        boolean lastContent = false;
-        httpTransportOverSPDY.send(null, content, lastContent, callback);
-    }
-
-    @Test
-    public void testSendWithResponseInfoAndContentNullAndLastContentFalse() throws Exception
-    {
-        ByteBuffer content = null;
-        boolean lastContent = false;
-
-        httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
-        ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
-        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
-        verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
-        callbackCaptor.getValue().succeeded();
-        assertThat("ReplyInfo close is true", replyInfoCaptor.getValue().isClose(), is(false));
-
-        verify(stream, times(0)).data(any(ByteBufferDataInfo.class), any(Callback.class));
-        verify(callback, times(1)).succeeded();
-    }
-
-    @Test
-    public void testSendWithResponseInfoAndContentNullAndLastContentTrue() throws Exception
-    {
-        ByteBuffer content = null;
-        boolean lastContent = true;
-
-        // when stream.isClosed() is called a 2nd time, the reply has closed the stream already
-        when(stream.isClosed()).thenReturn(false).thenReturn(true);
-
-        httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
-        ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
-        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
-        verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
-        callbackCaptor.getValue().succeeded();
-        assertThat("ReplyInfo close is true", replyInfoCaptor.getValue().isClose(), is(true));
-
-        verify(callback, times(1)).succeeded();
-    }
-
-    @Test
-    public void testSendWithResponseInfoAndContentAndLastContentTrue() throws Exception
-    {
-        ByteBuffer content = createRandomByteBuffer();
-        boolean lastContent = true;
-
-        httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
-        ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
-        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
-        verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
-        callbackCaptor.getValue().succeeded();
-        assertThat("ReplyInfo close is false", replyInfoCaptor.getValue().isClose(), is(false));
-        ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
-        verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
-        callbackCaptor.getValue().succeeded();
-        assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
-        assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
-        verify(callback, times(1)).succeeded();
-    }
-
-    @Test
-    public void testSendWithResponseInfoAndContentAndLastContentFalse() throws Exception
-    {
-        ByteBuffer content = createRandomByteBuffer();
-        boolean lastContent = false;
-
-        httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
-        ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
-        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
-        verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
-        callbackCaptor.getValue().succeeded();
-        assertThat("ReplyInfo close is false", replyInfoCaptor.getValue().isClose(), is(false));
-
-        ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
-        verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
-        callbackCaptor.getValue().succeeded();
-        assertThat("lastContent is false", dataInfoCaptor.getValue().isClose(), is(false));
-        assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
-
-        verify(callback, times(1)).succeeded();
-    }
-
-    @Test
-    public void testVerifyThatAStreamIsNotCommittedTwice() throws IOException, InterruptedException
-    {
-        final CountDownLatch failedCalledLatch = new CountDownLatch(1);
-        ByteBuffer content = createRandomByteBuffer();
-        boolean lastContent = false;
-
-        httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
-        ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
-        verify(stream, times(1)).reply(replyInfoCaptor.capture(), any(Callback.class));
-        assertThat("ReplyInfo close is false", replyInfoCaptor.getValue().isClose(), is(false));
-
-        httpTransportOverSPDY.send(HttpGenerator.RESPONSE_500_INFO, null, true, new Callback.Adapter()
-        {
-            @Override
-            public void failed(Throwable x)
-            {
-                failedCalledLatch.countDown();
-            }
-        });
-
-        verify(stream, times(1)).data(any(DataInfo.class), any(Callback.class));
-
-        assertThat("callback.failed has been called", failedCalledLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    private ByteBuffer createRandomByteBuffer()
-    {
-        ByteBuffer content = BufferUtil.allocate(8192);
-        BufferUtil.flipToFill(content);
-        byte[] randomBytes = new byte[4096];
-        random.nextBytes(randomBytes);
-        content.put(randomBytes);
-        return content;
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/PushStrategyBenchmarkTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/PushStrategyBenchmarkTest.java
deleted file mode 100644
index 31cab12..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/PushStrategyBenchmarkTest.java
+++ /dev/null
@@ -1,397 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server.http;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.util.Collections;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.server.ConnectionFactory;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
-@Ignore("make this test pass") // TODO
-public class PushStrategyBenchmarkTest extends AbstractHTTPSPDYTest
-{
-    // Sample resources size from webtide.com home page
-    private final int[] htmlResources = new int[]
-            {8 * 1024};
-    private final int[] cssResources = new int[]
-            {12 * 1024, 2 * 1024};
-    private final int[] jsResources = new int[]
-            {75 * 1024, 24 * 1024, 36 * 1024};
-    private final int[] pngResources = new int[]
-            {1024, 45 * 1024, 6 * 1024, 2 * 1024, 2 * 1024, 2 * 1024, 3 * 1024, 512, 512, 19 * 1024, 512, 128, 32};
-    private final Set<String> pushedResources = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
-    private final AtomicReference<CountDownLatch> latch = new AtomicReference<>();
-    private final long roundtrip = 100;
-    private final int runs = 10;
-
-    public PushStrategyBenchmarkTest(short version)
-    {
-        super(version);
-    }
-
-    @Test
-    public void benchmarkPushStrategy() throws Exception
-    {
-        InetSocketAddress address = startHTTPServer(version, new PushStrategyBenchmarkHandler(), 30000);
-
-        // Plain HTTP
-        ConnectionFactory factory = new HttpConnectionFactory(new HttpConfiguration());
-        connector.setDefaultProtocol(factory.getProtocol());
-        HttpClient httpClient = new HttpClient();
-        // Simulate browsers, that open 6 connection per origin
-        httpClient.setMaxConnectionsPerDestination(6);
-        httpClient.start();
-        benchmarkHTTP(httpClient);
-        httpClient.stop();
-
-        // First push strategy
-        PushStrategy pushStrategy = new PushStrategy.None();
-        factory = new HTTPSPDYServerConnectionFactory(version, new HttpConfiguration(), pushStrategy);
-        connector.setDefaultProtocol(factory.getProtocol());
-        Session session = startClient(version, address, new ClientSessionFrameListener());
-        benchmarkSPDY(pushStrategy, session);
-        session.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-
-        // Second push strategy
-        pushStrategy = new ReferrerPushStrategy();
-        factory = new HTTPSPDYServerConnectionFactory(version, new HttpConfiguration(), pushStrategy);
-        connector.setDefaultProtocol(factory.getProtocol());
-        session = startClient(version, address, new ClientSessionFrameListener());
-        benchmarkSPDY(pushStrategy, session);
-        session.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-    }
-
-    private void benchmarkHTTP(HttpClient httpClient) throws Exception
-    {
-        // Warm up
-        performHTTPRequests(httpClient);
-        performHTTPRequests(httpClient);
-
-        long total = 0;
-        for (int i = 0; i < runs; ++i)
-        {
-            long begin = System.nanoTime();
-            int requests = performHTTPRequests(httpClient);
-            long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - begin);
-            total += elapsed;
-            System.err.printf("HTTP: run %d, %d request(s), roundtrip delay %d ms, elapsed = %d%n",
-                    i, requests, roundtrip, elapsed);
-        }
-        System.err.printf("HTTP: roundtrip delay %d ms, average = %d%n%n",
-                roundtrip, total / runs);
-    }
-
-    private int performHTTPRequests(HttpClient httpClient) throws Exception
-    {
-        int result = 0;
-
-        for (int j = 0; j < htmlResources.length; ++j)
-        {
-            latch.set(new CountDownLatch(cssResources.length + jsResources.length + pngResources.length));
-
-            String primaryPath = "/" + j + ".html";
-            String referrer = "http://localhost:" + connector.getLocalPort() + primaryPath;
-            ++result;
-            ContentResponse response = httpClient.newRequest("localhost", connector.getLocalPort())
-                    .path(primaryPath)
-                    .timeout(5, TimeUnit.SECONDS)
-                    .send();
-            Assert.assertEquals(200, response.getStatus());
-
-            for (int i = 0; i < cssResources.length; ++i)
-            {
-                String path = "/" + i + ".css";
-                ++result;
-                httpClient.newRequest("localhost", connector.getLocalPort())
-                        .path(path)
-                        .header(HttpHeader.REFERER, referrer)
-                        .send(new TestListener());
-            }
-            for (int i = 0; i < jsResources.length; ++i)
-            {
-                String path = "/" + i + ".js";
-                ++result;
-                httpClient.newRequest("localhost", connector.getLocalPort())
-                        .path(path)
-                        .header(HttpHeader.REFERER, referrer)
-                        .send(new TestListener());
-            }
-            for (int i = 0; i < pngResources.length; ++i)
-            {
-                String path = "/" + i + ".png";
-                ++result;
-                httpClient.newRequest("localhost", connector.getLocalPort())
-                        .path(path)
-                        .header(HttpHeader.REFERER, referrer)
-                        .send(new TestListener());
-            }
-
-            Assert.assertTrue(latch.get().await(5, TimeUnit.SECONDS));
-        }
-
-        return result;
-    }
-
-    private void benchmarkSPDY(PushStrategy pushStrategy, Session session) throws Exception
-    {
-        // Warm up PushStrategy
-        performRequests(session);
-        performRequests(session);
-
-        long total = 0;
-        for (int i = 0; i < runs; ++i)
-        {
-            long begin = System.nanoTime();
-            int requests = performRequests(session);
-            long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - begin);
-            total += elapsed;
-            System.err.printf("SPDY(%s): run %d, %d request(s), roundtrip delay %d ms, elapsed = %d%n",
-                    pushStrategy.getClass().getSimpleName(), i, requests, roundtrip, elapsed);
-        }
-        System.err.printf("SPDY(%s): roundtrip delay %d ms, average = %d%n%n",
-                pushStrategy.getClass().getSimpleName(), roundtrip, total / runs);
-    }
-
-    private int performRequests(Session session) throws Exception
-    {
-        int result = 0;
-
-        for (int j = 0; j < htmlResources.length; ++j)
-        {
-            latch.set(new CountDownLatch(cssResources.length + jsResources.length + pngResources.length));
-            pushedResources.clear();
-
-            String primaryPath = "/" + j + ".html";
-            String referrer = "http://localhost:" + connector.getLocalPort() + primaryPath;
-            Fields headers = new Fields();
-            headers.put(HTTPSPDYHeader.METHOD.name(version), "GET");
-            headers.put(HTTPSPDYHeader.URI.name(version), primaryPath);
-            headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-            headers.put(HTTPSPDYHeader.SCHEME.name(version), "http");
-            headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + connector.getLocalPort());
-            // Wait for the HTML to simulate browser's behavior
-            ++result;
-            final CountDownLatch htmlLatch = new CountDownLatch(1);
-            session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-            {
-                @Override
-                public void onData(Stream stream, DataInfo dataInfo)
-                {
-                    dataInfo.consume(dataInfo.length());
-                    if (dataInfo.isClose())
-                        htmlLatch.countDown();
-                }
-            });
-            Assert.assertTrue(htmlLatch.await(5, TimeUnit.SECONDS));
-
-            for (int i = 0; i < cssResources.length; ++i)
-            {
-                String path = "/" + i + ".css";
-                if (pushedResources.contains(path))
-                    continue;
-                headers = createRequestHeaders(referrer, path);
-                ++result;
-                session.syn(new SynInfo(headers, true), new DataListener());
-            }
-            for (int i = 0; i < jsResources.length; ++i)
-            {
-                String path = "/" + i + ".js";
-                if (pushedResources.contains(path))
-                    continue;
-                headers = createRequestHeaders(referrer, path);
-                ++result;
-                session.syn(new SynInfo(headers, true), new DataListener());
-            }
-            for (int i = 0; i < pngResources.length; ++i)
-            {
-                String path = "/" + i + ".png";
-                if (pushedResources.contains(path))
-                    continue;
-                headers = createRequestHeaders(referrer, path);
-                ++result;
-                session.syn(new SynInfo(headers, true), new DataListener());
-            }
-
-            Assert.assertTrue(latch.get().await(5, TimeUnit.SECONDS));
-        }
-
-        return result;
-    }
-
-    private Fields createRequestHeaders(String referrer, String path)
-    {
-        Fields headers;
-        headers = new Fields();
-        headers.put(HTTPSPDYHeader.METHOD.name(version), "GET");
-        headers.put(HTTPSPDYHeader.URI.name(version), path);
-        headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-        headers.put(HTTPSPDYHeader.SCHEME.name(version), "http");
-        headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + connector.getLocalPort());
-        headers.put("referer", referrer);
-        return headers;
-    }
-
-    private void sleep(long delay) throws ServletException
-    {
-        try
-        {
-            TimeUnit.MILLISECONDS.sleep(delay);
-        }
-        catch (InterruptedException x)
-        {
-            throw new ServletException(x);
-        }
-    }
-
-    private class PushStrategyBenchmarkHandler extends AbstractHandler
-    {
-        @Override
-        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-        {
-            baseRequest.setHandled(true);
-
-            // Sleep half of the roundtrip time, to simulate the delay of responses, even for pushed resources
-            sleep(roundtrip / 2);
-            // If it's not a pushed resource, sleep half of the roundtrip time, to simulate the delay of requests
-            if (request.getHeader("x-spdy-push") == null)
-                sleep(roundtrip / 2);
-
-            String suffix = target.substring(target.indexOf('.') + 1);
-            int index = Integer.parseInt(target.substring(1, target.length() - suffix.length() - 1));
-
-            int contentLength;
-            String contentType;
-            switch (suffix)
-            {
-                case "html":
-                    contentLength = htmlResources[index];
-                    contentType = "text/html";
-                    break;
-                case "css":
-                    contentLength = cssResources[index];
-                    contentType = "text/css";
-                    break;
-                case "js":
-                    contentLength = jsResources[index];
-                    contentType = "text/javascript";
-                    break;
-                case "png":
-                    contentLength = pngResources[index];
-                    contentType = "image/png";
-                    break;
-                default:
-                    throw new ServletException();
-            }
-
-            response.setContentType(contentType);
-            response.setContentLength(contentLength);
-            response.getOutputStream().write(new byte[contentLength]);
-        }
-    }
-
-    private void addPushedResource(String pushedURI)
-    {
-        switch (version)
-        {
-            case SPDY.V2:
-            {
-                Matcher matcher = Pattern.compile("https?://[^:]+:\\d+(/.*)").matcher(pushedURI);
-                Assert.assertTrue(matcher.matches());
-                pushedResources.add(matcher.group(1));
-                break;
-            }
-            case SPDY.V3:
-            {
-                pushedResources.add(pushedURI);
-                break;
-            }
-            default:
-            {
-                throw new IllegalStateException();
-            }
-        }
-    }
-
-    private class ClientSessionFrameListener extends SessionFrameListener.Adapter
-    {
-        @Override
-        public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-        {
-            String path = synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version)).getValue();
-            addPushedResource(path);
-            return new DataListener();
-        }
-    }
-
-    private class DataListener extends StreamFrameListener.Adapter
-    {
-        @Override
-        public void onData(Stream stream, DataInfo dataInfo)
-        {
-            dataInfo.consume(dataInfo.length());
-            if (dataInfo.isClose())
-                latch.get().countDown();
-        }
-    }
-
-    private class TestListener extends Response.Listener.Adapter
-    {
-        @Override
-        public void onComplete(Result result)
-        {
-            if (!result.isFailed())
-                latch.get().countDown();
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategyTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategyTest.java
deleted file mode 100644
index 64d8024..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategyTest.java
+++ /dev/null
@@ -1,1138 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.http;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.junit.Assert.assertThat;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.net.InetSocketAddress;
-import java.util.Arrays;
-import java.util.Random;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.server.ConnectionFactory;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.servlets.gzip.GzipHandler;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Settings;
-import org.eclipse.jetty.spdy.api.SettingsInfo;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.log.StdErrLog;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
-{
-    private static final Logger LOG = Log.getLogger(ReferrerPushStrategyTest.class);
-
-    private final int referrerPushPeriod = 1000;
-    private final String mainResource = "/index.html";
-    private final String cssResource = "/style.css";
-    private InetSocketAddress serverAddress;
-    private ReferrerPushStrategy pushStrategy;
-    private ConnectionFactory defaultFactory;
-    private Fields mainRequestHeaders;
-    private Fields associatedCSSRequestHeaders;
-    private Fields associatedJSRequestHeaders;
-
-    public ReferrerPushStrategyTest(short version)
-    {
-        super(version);
-    }
-
-    @Override
-    protected HTTPSPDYServerConnector newHTTPSPDYServerConnector(short version)
-    {
-        return new HTTPSPDYServerConnector(server, version, new HttpConfiguration(), new ReferrerPushStrategy());
-    }
-
-    @Before
-    public void setUp() throws Exception
-    {
-        serverAddress = createServer();
-        pushStrategy = new ReferrerPushStrategy();
-        pushStrategy.setReferrerPushPeriod(referrerPushPeriod);
-        defaultFactory = new HTTPSPDYServerConnectionFactory(version, new HttpConfiguration(), pushStrategy);
-        connector.addConnectionFactory(defaultFactory);
-        if (connector.getConnectionFactory(NPNServerConnectionFactory.class) != null)
-            connector.getConnectionFactory(NPNServerConnectionFactory.class).setDefaultProtocol(defaultFactory.getProtocol());
-        else
-            connector.setDefaultProtocol(defaultFactory.getProtocol());
-        mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
-        associatedCSSRequestHeaders = createHeaders(cssResource);
-        associatedJSRequestHeaders = createHeaders("/application.js");
-    }
-
-    @Test
-    public void testPushHeadersAreValid() throws Exception
-    {
-        sendMainRequestAndCSSRequest(null, false);
-        run2ndClientRequests(true, true);
-    }
-
-    @Test
-    public void testClientResetsPushStreams() throws Exception
-    {
-        ((StdErrLog)Log.getLogger("org.eclipse.jetty.server.HttpChannel")).setHideStacks(true);
-        sendMainRequestAndCSSRequest(null, false);
-        final CountDownLatch pushDataLatch = new CountDownLatch(1);
-        final CountDownLatch pushSynHeadersValid = new CountDownLatch(1);
-        Session session = startClient(version, serverAddress, null);
-        // Send main request. That should initiate the push push's which get reset by the client
-        sendRequest(session, mainRequestHeaders, pushSynHeadersValid, pushDataLatch, true);
-
-        assertThat("No push data is received", pushDataLatch.await(1, TimeUnit.SECONDS), is(false));
-        assertThat("Push push headers valid", pushSynHeadersValid.await(5, TimeUnit.SECONDS), is(true));
-
-        sendRequest(session, associatedCSSRequestHeaders, pushSynHeadersValid, pushDataLatch, true);
-        ((StdErrLog)Log.getLogger("org.eclipse.jetty.server.HttpChannel")).setHideStacks(false);
-    }
-
-    @Test
-    public void testUserAgentBlackList() throws Exception
-    {
-        pushStrategy.setUserAgentBlacklist(Arrays.asList(".*(?i)firefox/16.*"));
-        sendMainRequestAndCSSRequest(null, false);
-        run2ndClientRequests(false, false);
-    }
-
-    @Test
-    public void testReferrerPushPeriod() throws Exception
-    {
-        Session session = sendMainRequestAndCSSRequest(null, false);
-
-        // Sleep for pushPeriod This should prevent application.js from being mapped as pushResource
-        Thread.sleep(referrerPushPeriod + 1);
-        sendRequest(session, associatedJSRequestHeaders, null, null, false);
-
-        run2ndClientRequests(false, true);
-    }
-
-    @Test
-    public void testMaxAssociatedResources() throws Exception
-    {
-        pushStrategy.setMaxAssociatedResources(1);
-        Session session = sendMainRequestAndCSSRequest(null, false);
-        sendRequest(session, associatedJSRequestHeaders, null, null, false);
-
-        final CountDownLatch mainStreamLatch = new CountDownLatch(2);
-        final CountDownLatch pushDataLatch = new CountDownLatch(2);
-        final CountDownLatch pushSynHeadersValid = new CountDownLatch(1);
-        final CountDownLatch pushResponseHeaders = new CountDownLatch(1);
-        Session session2 = startClient(version, serverAddress, null);
-        session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                validateHeaders(pushInfo.getHeaders(), pushSynHeadersValid);
-
-                assertThat("Stream is unidirectional", stream.isUnidirectional(), is(true));
-                assertThat("URI header ends with css", pushInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version))
-                        .getValue().endsWith
-                                ("" +
-                                        ".css"),
-                        is(true));
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onHeaders(Stream stream, HeadersInfo headersInfo)
-                    {
-                        Fields headers = headersInfo.getHeaders();
-                        if (validateHeader(headers, HTTPSPDYHeader.STATUS.name(version), "200 OK")
-                                && validateHeader(headers, HTTPSPDYHeader.VERSION.name(version),
-                                "HTTP/1.1") && validateHeader(headers, "content-encoding", "gzip"))
-                            pushResponseHeaders.countDown();
-                    }
-
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataInfo.consume(dataInfo.length());
-                        if (dataInfo.isClose())
-                            pushDataLatch.countDown();
-                    }
-                };
-            }
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertThat("replyInfo.isClose() is false", replyInfo.isClose(), is(false));
-                mainStreamLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    mainStreamLatch.countDown();
-            }
-        });
-
-        assertThat("Main request reply and/or data received", mainStreamLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("Not more than one push is received", pushDataLatch.await(1, TimeUnit.SECONDS), is(false));
-        assertThat("Push push headers valid", pushSynHeadersValid.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("Push response headers are valid", pushResponseHeaders.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testMaxConcurrentStreamsToDisablePush() throws Exception
-    {
-        final CountDownLatch pushReceivedLatch = new CountDownLatch(1);
-
-        Session pushCacheBuildSession = startClient(version, serverAddress, null);
-
-        pushCacheBuildSession.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter());
-        pushCacheBuildSession.syn(new SynInfo(associatedCSSRequestHeaders, true), new StreamFrameListener.Adapter());
-
-        Session session = startClient(version, serverAddress, null);
-
-        Settings settings = new Settings();
-        settings.put(new Settings.Setting(Settings.ID.MAX_CONCURRENT_STREAMS, 0));
-        SettingsInfo settingsInfo = new SettingsInfo(settings);
-        session.settings(settingsInfo);
-
-        ((StdErrLog)Log.getLogger("org.eclipse.jetty.spdy.server.http" +
-                        ".HttpTransportOverSPDY$PushResourceCoordinator$1")).setHideStacks(true);
-        session.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                pushReceivedLatch.countDown();
-                return super.onPush(stream, pushInfo);
-            }
-        });
-
-        assertThat("No push stream is received", pushReceivedLatch.await(1, TimeUnit.SECONDS), is(false));
-        ((StdErrLog)Log.getLogger("org.eclipse.jetty.spdy.server.http" +
-                                ".HttpTransportOverSPDY$PushResourceCoordinator$1")).setHideStacks(false);
-    }
-
-    @Test
-    public void testPushResourceOrder() throws Exception
-    {
-        final CountDownLatch allExpectedPushesReceivedLatch = new CountDownLatch(4);
-        final CountDownLatch allPushDataReceivedLatch = new CountDownLatch(4);
-
-        Session pushCacheBuildSession = startClient(version, serverAddress, null);
-
-        sendRequest(pushCacheBuildSession, mainRequestHeaders, null, null, false);
-        sendRequest(pushCacheBuildSession, associatedCSSRequestHeaders, null, null, false);
-        sendRequest(pushCacheBuildSession, associatedJSRequestHeaders, null, null, false);
-        sendRequest(pushCacheBuildSession, createHeaders("/image1.jpg", mainResource), null, null, false);
-        sendRequest(pushCacheBuildSession, createHeaders("/image2.jpg", mainResource), null, null, false);
-
-        Session session = startClient(version, serverAddress, null);
-
-        session.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                LOG.info("onPush: stream: {}, pushInfo: {}", stream, pushInfo);
-                String uriHeader = pushInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version)).getValue();
-                switch ((int)allExpectedPushesReceivedLatch.getCount())
-                {
-                    case 4:
-                        assertThat("1st pushed resource is the css", uriHeader.endsWith("css"), is(true));
-                        break;
-                    case 3:
-                        assertThat("2nd pushed resource is the js", uriHeader.endsWith("js"), is(true));
-                        break;
-                    case 2:
-                        assertThat("3rd pushed resource is image1", uriHeader.endsWith("image1.jpg"),
-                                is(true));
-                        break;
-                    case 1:
-                        assertThat("4th pushed resource is image2", uriHeader.endsWith("image2.jpg"),
-                                is(true));
-                        break;
-                }
-                allExpectedPushesReceivedLatch.countDown();
-                return new Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        if(dataInfo.isClose())
-                            allPushDataReceivedLatch.countDown();
-                    }
-                };
-            }
-        });
-
-        assertThat("All expected push resources have been received", allExpectedPushesReceivedLatch.await(5,
-                TimeUnit.SECONDS), is(true));
-        assertThat("All push data has been fully received", allPushDataReceivedLatch.await(5, TimeUnit.SECONDS),
-                is(true));
-    }
-
-    @Test
-    public void testThatPushResourcesAreUnique() throws Exception
-    {
-        final CountDownLatch pushReceivedLatch = new CountDownLatch(2);
-        sendMainRequestAndCSSRequest(null, false);
-        sendMainRequestAndCSSRequest(null, false);
-
-        Session session = startClient(version, serverAddress, null);
-
-        session.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                pushReceivedLatch.countDown();
-                LOG.info("Push received: {}", pushInfo);
-                return null;
-            }
-        });
-
-        assertThat("style.css has been pushed only once", pushReceivedLatch.await(1, TimeUnit.SECONDS), is(false));
-    }
-
-    @Test
-    public void testPushResourceAreSentNonInterleaved() throws Exception
-    {
-        final CountDownLatch allExpectedPushesReceivedLatch = new CountDownLatch(4);
-        final CountDownLatch allPushDataReceivedLatch = new CountDownLatch(4);
-        final CopyOnWriteArrayList<Integer> dataReceivedOrder = new CopyOnWriteArrayList<>();
-
-        InetSocketAddress bigResponseServerAddress = startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                byte[] bytes = new byte[32768];
-                new Random().nextBytes(bytes);
-                ServletOutputStream outputStream = response.getOutputStream();
-                outputStream.write(bytes);
-                baseRequest.setHandled(true);
-            }
-        }, 30000);
-        Session pushCacheBuildSession = startClient(version, bigResponseServerAddress, null);
-
-        Fields mainResourceHeaders = createHeadersWithoutReferrer(mainResource);
-        sendRequest(pushCacheBuildSession, mainResourceHeaders, null, null, false);
-        sendRequest(pushCacheBuildSession, createHeaders("/style.css", mainResource), null, null, false);
-        sendRequest(pushCacheBuildSession, createHeaders("/javascript.js", mainResource), null, null, false);
-        sendRequest(pushCacheBuildSession, createHeaders("/image1.jpg", mainResource), null, null, false);
-        sendRequest(pushCacheBuildSession, createHeaders("/image2.jpg", mainResource), null, null, false);
-
-        Session session = startClient(version, bigResponseServerAddress, null);
-
-        session.syn(new SynInfo(mainResourceHeaders, true), new StreamFrameListener.Adapter()
-        {
-            AtomicInteger currentStreamId = new AtomicInteger(2);
-
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                LOG.info("Received push for stream: {} {}", stream.getId(), pushInfo);
-                String uriHeader = pushInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version)).getValue();
-                switch ((int)allExpectedPushesReceivedLatch.getCount())
-                {
-                    case 4:
-                        assertThat("1st pushed resource is the css", uriHeader.endsWith("css"), is(true));
-                        break;
-                    case 3:
-                        assertThat("2nd pushed resource is the js", uriHeader.endsWith("js"), is(true));
-                        break;
-                    case 2:
-                        assertThat("3rd pushed resource is image1", uriHeader.endsWith("image1.jpg"),
-                                is(true));
-                        break;
-                    case 1:
-                        assertThat("4th pushed resource is image2", uriHeader.endsWith("image2.jpg"),
-                                is(true));
-                        break;
-                }
-                allExpectedPushesReceivedLatch.countDown();
-                return new Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        if (stream.getId() != currentStreamId.get())
-                            throw new IllegalStateException("Streams interleaved. Expected StreamId: " +
-                                    currentStreamId + " but was: " + stream.getId());
-                        dataInfo.consume(dataInfo.available());
-                        if (dataInfo.isClose())
-                        {
-                            currentStreamId.compareAndSet(currentStreamId.get(), currentStreamId.get() + 2);
-                            dataReceivedOrder.add(stream.getId());
-                            allPushDataReceivedLatch.countDown();
-                        }
-                        LOG.info(stream.getId() + ":" + dataInfo);
-                    }
-                };
-            }
-        });
-
-        assertThat("All push resources received", allExpectedPushesReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("All pushData received", allPushDataReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("The data for different push streams has not been interleaved",
-                dataReceivedOrder.toString(), equalTo("[2, 4, 6, 8]"));
-        LOG.info(dataReceivedOrder.toString());
-    }
-
-    private InetSocketAddress createServer() throws Exception
-    {
-        GzipHandler gzipHandler = new GzipHandler();
-        gzipHandler.setHandler(new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                String url = request.getRequestURI();
-                PrintWriter output = response.getWriter();
-                if (url.endsWith(".html"))
-                    output.print("<html><head/><body>HELLO</body></html>");
-                else if (url.endsWith(".css"))
-                    output.print("body { background: #FFF; }");
-                else if (url.endsWith(".js"))
-                    output.print("function(){}();");
-                baseRequest.setHandled(true);
-            }
-        });
-        return startHTTPServer(version, gzipHandler, 30000);
-    }
-
-    private Session sendMainRequestAndCSSRequest(SessionFrameListener sessionFrameListener, boolean awaitPush) throws Exception
-    {
-        Session session = startClient(version, serverAddress, sessionFrameListener);
-
-        CountDownLatch pushDataLatch = new CountDownLatch(2);
-        sendRequest(session, mainRequestHeaders, null, pushDataLatch, false);
-        sendRequest(session, associatedCSSRequestHeaders, null, pushDataLatch, false);
-        if (awaitPush)
-            assertThat("pushes have been received", pushDataLatch.await(5, TimeUnit.SECONDS), is(true));
-
-        return session;
-    }
-
-    private void sendRequest(Session session, Fields requestHeaders, final CountDownLatch pushSynHeadersValid,
-                             final CountDownLatch pushDataLatch, final boolean resetPush) throws InterruptedException
-    {
-        LOG.info("sendRequest. headers={},resetPush={}", requestHeaders, resetPush);
-        final CountDownLatch dataReceivedLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(requestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                if (pushSynHeadersValid != null)
-                    validateHeaders(pushInfo.getHeaders(), pushSynHeadersValid);
-
-                assertThat("Stream is unidirectional", stream.isUnidirectional(), is(true));
-                assertThat("URI header ends with css", pushInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version))
-                        .getValue().endsWith
-                                ("" +
-                                        ".css"),
-                        is(true));
-                if (resetPush)
-                    stream.getSession().rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM), new Callback.Adapter());
-                return new StreamFrameListener.Adapter()
-                {
-
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataInfo.consume(dataInfo.length());
-                        pushDataLatch.countDown();
-                    }
-                };
-            }
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertThat(replyInfo.getHeaders().get(HTTPSPDYHeader.STATUS.name(version)).getValue(), is("200 OK"));
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    dataReceivedLatch.countDown();
-            }
-        }, new Promise.Adapter<Stream>());
-        assertThat(dataReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    private void run2ndClientRequests(final boolean validateHeaders,
-                                      boolean expectPushResource) throws Exception
-    {
-        final CountDownLatch mainStreamLatch = new CountDownLatch(2);
-        final CountDownLatch pushDataLatch = new CountDownLatch(1);
-        final CountDownLatch pushSynHeadersValid = new CountDownLatch(1);
-        final CountDownLatch pushResponseHeaders = new CountDownLatch(1);
-        Session session2 = startClient(version, serverAddress, null);
-        session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                if (validateHeaders)
-                    validateHeaders(pushInfo.getHeaders(), pushSynHeadersValid);
-
-                assertThat("Stream is unidirectional", stream.isUnidirectional(), is(true));
-                assertThat("URI header ends with css", pushInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version))
-                        .getValue().endsWith
-                                ("" +
-                                        ".css"),
-                        is(true));
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onHeaders(Stream stream, HeadersInfo headersInfo)
-                    {
-                        Fields headers = headersInfo.getHeaders();
-                        if (validateHeader(headers, HTTPSPDYHeader.STATUS.name(version), "200 OK")
-                                && validateHeader(headers, HTTPSPDYHeader.VERSION.name(version),
-                                "HTTP/1.1") && validateHeader(headers, "content-encoding", "gzip"))
-                            pushResponseHeaders.countDown();
-                    }
-
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataInfo.consume(dataInfo.length());
-                        if (dataInfo.isClose())
-                            pushDataLatch.countDown();
-                    }
-                };
-            }
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertThat("replyInfo.isClose() is false", replyInfo.isClose(), is(false));
-                mainStreamLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    mainStreamLatch.countDown();
-            }
-        });
-
-        assertThat("Main request reply and/or data received", mainStreamLatch.await(5, TimeUnit.SECONDS), is(true));
-        if (expectPushResource)
-            assertThat("Pushed data received", pushDataLatch.await(5, TimeUnit.SECONDS), is(true));
-        else
-            assertThat("No push data is received", pushDataLatch.await(1, TimeUnit.SECONDS), is(false));
-        if (validateHeaders)
-        {
-            assertThat("Push push headers valid", pushSynHeadersValid.await(5, TimeUnit.SECONDS), is(true));
-            assertThat("Push response headers are valid", pushResponseHeaders.await(5, TimeUnit.SECONDS), is(true));
-        }
-    }
-
-    @Test
-    public void testAssociatedResourceIsPushed() throws Exception
-    {
-        InetSocketAddress address = startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                String url = request.getRequestURI();
-                PrintWriter output = response.getWriter();
-                if (url.endsWith(".html"))
-                    output.print("<html><head/><body>HELLO</body></html>");
-                else if (url.endsWith(".css"))
-                    output.print("body { background: #FFF; }");
-                baseRequest.setHandled(true);
-            }
-        }, 30000);
-        Session session1 = startClient(version, address, null);
-
-        final CountDownLatch mainResourceLatch = new CountDownLatch(1);
-        Fields mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
-
-        session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    mainResourceLatch.countDown();
-            }
-        });
-        Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
-
-        sendRequest(session1, createHeaders(cssResource), null, null, false);
-
-        // Create another client, and perform the same request for the main resource, we expect the css being pushed
-
-        final CountDownLatch mainStreamLatch = new CountDownLatch(2);
-        final CountDownLatch pushDataLatch = new CountDownLatch(1);
-        Session session2 = startClient(version, address, null);
-        session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                Assert.assertTrue(stream.isUnidirectional());
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataInfo.consume(dataInfo.length());
-                        if (dataInfo.isClose())
-                            pushDataLatch.countDown();
-                    }
-                };
-            }
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                mainStreamLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    mainStreamLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testAssociatedResourceWithWrongContentTypeIsNotPushed() throws Exception
-    {
-        final String fakeResource = "/fake.png";
-        InetSocketAddress address = startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                String url = request.getRequestURI();
-                PrintWriter output = response.getWriter();
-                if (url.endsWith(".html"))
-                {
-                    response.setContentType("text/html");
-                    output.print("<html><head/><body>HELLO</body></html>");
-                }
-                else if (url.equals(fakeResource))
-                {
-                    response.setContentType("text/html");
-                    output.print("<html><head/><body>IMAGE</body></html>");
-                }
-                else if (url.endsWith(".css"))
-                {
-                    response.setContentType("text/css");
-                    output.print("body { background: #FFF; }");
-                }
-                baseRequest.setHandled(true);
-            }
-        }, 30000);
-        Session session1 = startClient(version, address, null);
-
-        final CountDownLatch mainResourceLatch = new CountDownLatch(1);
-        Fields mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
-
-        session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    mainResourceLatch.countDown();
-            }
-        });
-        Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
-
-        final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
-        String cssResource = "/stylesheet.css";
-        Fields associatedRequestHeaders = createHeaders(cssResource);
-        session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    associatedResourceLatch.countDown();
-            }
-        });
-        Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
-
-        final CountDownLatch fakeAssociatedResourceLatch = new CountDownLatch(1);
-        Fields fakeAssociatedRequestHeaders = createHeaders(fakeResource);
-        session1.syn(new SynInfo(fakeAssociatedRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    fakeAssociatedResourceLatch.countDown();
-            }
-        });
-        Assert.assertTrue(fakeAssociatedResourceLatch.await(5, TimeUnit.SECONDS));
-
-        // Create another client, and perform the same request for the main resource,
-        // we expect the css being pushed but not the fake PNG
-
-        final CountDownLatch mainStreamLatch = new CountDownLatch(2);
-        final CountDownLatch pushDataLatch = new CountDownLatch(1);
-        Session session2 = startClient(version, address, null);
-        session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                Assert.assertTrue(stream.isUnidirectional());
-                Assert.assertTrue(pushInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version)).getValue().endsWith("" +
-                        ".css"));
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataInfo.consume(dataInfo.length());
-                        if (dataInfo.isClose())
-                            pushDataLatch.countDown();
-                    }
-                };
-            }
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                mainStreamLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    mainStreamLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testNestedAssociatedResourceIsPushed() throws Exception
-    {
-        InetSocketAddress address = startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                String url = request.getRequestURI();
-                PrintWriter output = response.getWriter();
-                if (url.endsWith(".html"))
-                    output.print("<html><head/><body>HELLO</body></html>");
-                else if (url.endsWith(".css"))
-                    output.print("body { background: #FFF; }");
-                else if (url.endsWith(".gif"))
-                    output.print("\u0000");
-                baseRequest.setHandled(true);
-            }
-        }, 30000);
-        Session session1 = startClient(version, address, null);
-
-        final CountDownLatch mainResourceLatch = new CountDownLatch(1);
-        Fields mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
-
-        session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    mainResourceLatch.countDown();
-            }
-        });
-        Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
-
-        final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
-        Fields associatedRequestHeaders = createHeaders(cssResource);
-        session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    associatedResourceLatch.countDown();
-            }
-        });
-        Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
-
-        final CountDownLatch nestedResourceLatch = new CountDownLatch(1);
-        String imageUrl = "/image.gif";
-        Fields nestedRequestHeaders = createHeaders(imageUrl, cssResource);
-
-        session1.syn(new SynInfo(nestedRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    nestedResourceLatch.countDown();
-            }
-        });
-        Assert.assertTrue(nestedResourceLatch.await(5, TimeUnit.SECONDS));
-
-        // Create another client, and perform the same request for the main resource, we expect the css and the image being pushed
-
-        final CountDownLatch mainStreamLatch = new CountDownLatch(2);
-        final CountDownLatch pushDataLatch = new CountDownLatch(2);
-        Session session2 = startClient(version, address, null);
-        session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                Assert.assertTrue(stream.isUnidirectional());
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-                    {
-                        return new Adapter()
-                        {
-                            @Override
-                            public void onData(Stream stream, DataInfo dataInfo)
-                            {
-                                dataInfo.consume(dataInfo.length());
-                                if (dataInfo.isClose())
-                                    pushDataLatch.countDown();
-                            }
-                        };
-                    }
-
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataInfo.consume(dataInfo.length());
-                        if (dataInfo.isClose())
-                            pushDataLatch.countDown();
-                    }
-                };
-            }
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                mainStreamLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    mainStreamLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testMainResourceWithReferrerIsNotPushed() throws Exception
-    {
-        InetSocketAddress address = startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                String url = request.getRequestURI();
-                PrintWriter output = response.getWriter();
-                if (url.endsWith(".html"))
-                    output.print("<html><head/><body>HELLO</body></html>");
-                baseRequest.setHandled(true);
-            }
-        }, 30000);
-        Session session1 = startClient(version, address, null);
-
-        final CountDownLatch mainResourceLatch = new CountDownLatch(1);
-        Fields mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
-
-        session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    mainResourceLatch.countDown();
-            }
-        });
-        Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
-
-        final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
-        String associatedResource = "/home.html";
-        Fields associatedRequestHeaders = createHeaders(associatedResource);
-
-        session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    associatedResourceLatch.countDown();
-            }
-        });
-        Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
-
-        // Create another client, and perform the same request for the main resource, we expect nothing being pushed
-
-        final CountDownLatch mainStreamLatch = new CountDownLatch(2);
-        final CountDownLatch pushLatch = new CountDownLatch(1);
-        Session session2 = startClient(version, address, new SessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                pushLatch.countDown();
-                return null;
-            }
-        });
-        session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                mainStreamLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    mainStreamLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertFalse(pushLatch.await(1, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testRequestWithIfModifiedSinceHeaderPreventsPush() throws Exception
-    {
-        InetSocketAddress address = startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                String url = request.getRequestURI();
-                PrintWriter output = response.getWriter();
-                if (url.endsWith(".html"))
-                    output.print("<html><head/><body>HELLO</body></html>");
-                else if (url.endsWith(".css"))
-                    output.print("body { background: #FFF; }");
-                baseRequest.setHandled(true);
-            }
-        }, 30000);
-        Session session1 = startClient(version, address, null);
-
-        final CountDownLatch mainResourceLatch = new CountDownLatch(1);
-        Fields mainRequestHeaders = createHeaders(mainResource);
-        mainRequestHeaders.put("If-Modified-Since", "Tue, 27 Mar 2012 16:36:52 GMT");
-        session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    mainResourceLatch.countDown();
-            }
-        });
-        Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
-
-        final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
-        Fields associatedRequestHeaders = createHeaders(cssResource);
-        session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    associatedResourceLatch.countDown();
-            }
-        });
-        Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
-
-        // Create another client, and perform the same request for the main resource, we expect the css NOT being pushed as the main request contains an
-        // if-modified-since header
-
-        final CountDownLatch mainStreamLatch = new CountDownLatch(2);
-        final CountDownLatch pushDataLatch = new CountDownLatch(1);
-        Session session2 = startClient(version, address, new SessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Assert.assertTrue(stream.isUnidirectional());
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataInfo.consume(dataInfo.length());
-                        if (dataInfo.isClose())
-                            pushDataLatch.countDown();
-                    }
-                };
-            }
-        });
-        session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                mainStreamLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    mainStreamLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertFalse("We don't expect data to be pushed as the main request contained an if-modified-since header", pushDataLatch.await(1, TimeUnit.SECONDS));
-    }
-
-    private void validateHeaders(Fields headers, CountDownLatch pushSynHeadersValid)
-    {
-        if (validateUriHeader(headers))
-            pushSynHeadersValid.countDown();
-    }
-
-    private boolean validateHeader(Fields headers, String name, String expectedValue)
-    {
-        Fields.Field header = headers.get(name);
-        if (header != null && expectedValue.equals(header.getValue()))
-            return true;
-        System.out.println(name + " not valid! Expected: " + expectedValue + " headers received:" + headers);
-        return false;
-    }
-
-    private boolean validateUriHeader(Fields headers)
-    {
-        Fields.Field uriHeader = headers.get(HTTPSPDYHeader.URI.name(version));
-        if (uriHeader != null)
-            if (version == SPDY.V2 && uriHeader.getValue().startsWith("http://"))
-                return true;
-            else if (version == SPDY.V3 && uriHeader.getValue().startsWith("/")
-                    && headers.get(HTTPSPDYHeader.HOST.name(version)) != null && headers.get(HTTPSPDYHeader.SCHEME.name(version)) != null)
-                return true;
-        System.out.println(HTTPSPDYHeader.URI.name(version) + " not valid!");
-        return false;
-    }
-
-    private Fields createHeaders(String resource)
-    {
-        return createHeaders(resource, mainResource);
-    }
-
-    private Fields createHeaders(String resource, String referrer)
-    {
-        Fields associatedRequestHeaders = createHeadersWithoutReferrer(resource);
-        associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + referrer);
-        return associatedRequestHeaders;
-    }
-
-    private Fields createHeadersWithoutReferrer(String resource)
-    {
-        Fields requestHeaders = new Fields();
-        requestHeaders.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:16.0) " +
-                "Gecko/20100101 Firefox/16.0");
-        requestHeaders.put("accept-encoding", "gzip");
-        requestHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET");
-        requestHeaders.put(HTTPSPDYHeader.URI.name(version), resource);
-        requestHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-        requestHeaders.put(HTTPSPDYHeader.SCHEME.name(version), "http");
-        requestHeaders.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + connector.getLocalPort());
-        return requestHeaders;
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategyUnitTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategyUnitTest.java
deleted file mode 100644
index 6d92c3e..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategyUnitTest.java
+++ /dev/null
@@ -1,149 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.http;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.when;
-
-import java.util.Arrays;
-import java.util.Set;
-
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-
-@RunWith(MockitoJUnitRunner.class)
-public class ReferrerPushStrategyUnitTest
-{
-    public static final short VERSION = SPDY.V3;
-    public static final String SCHEME = "http";
-    public static final String HOST = "localhost";
-    public static final String MAIN_URI = "/index.html";
-    public static final String METHOD = "GET";
-
-    // class under test
-    private ReferrerPushStrategy referrerPushStrategy = new ReferrerPushStrategy();
-
-    @Mock
-    Stream stream;
-    @Mock
-    Session session;
-
-
-    @Before
-    public void setup()
-    {
-        referrerPushStrategy.setUserAgentBlacklist(Arrays.asList(".*(?i)firefox/16.*"));
-    }
-
-    @Test
-    public void testReferrerCallsAfterTimeoutAreNotAddedAsPushResources() throws InterruptedException
-    {
-        Fields requestHeaders = getBaseHeaders(VERSION);
-        int referrerCallTimeout = 1000;
-        referrerPushStrategy.setReferrerPushPeriod(referrerCallTimeout);
-        setMockExpectations();
-
-        String referrerUrl = fillPushStrategyCache(requestHeaders);
-
-        // sleep to pretend that the user manually clicked on a linked resource instead the browser requesting sub
-        // resources immediately
-        Thread.sleep(referrerCallTimeout + 1);
-
-        requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), "image2.jpg");
-        requestHeaders.put("referer", referrerUrl);
-        Set<String> pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
-        assertThat("pushResources is empty", pushResources.size(), is(0));
-
-        requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), MAIN_URI);
-        pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
-        // as the image2.jpg request has been a link and not a sub resource, we expect that pushResources.size() is
-        // still 2
-        assertThat("pushResources contains two elements image.jpg and style.css", pushResources.size(), is(2));
-    }
-
-    @Test
-    public void testUserAgentFilter() throws InterruptedException
-    {
-        Fields requestHeaders = getBaseHeaders(VERSION);
-        setMockExpectations();
-
-        fillPushStrategyCache(requestHeaders);
-
-        Set<String> pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
-        assertThat("pushResources contains two elements image.jpg and style.css as no user-agent header is present",
-                pushResources.size(), is(2));
-
-        requestHeaders.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4");
-        pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
-        assertThat("pushResources contains two elements image.jpg and style.css as chrome is not blacklisted",
-                pushResources.size(), is(2));
-
-        requestHeaders.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:16.0) Gecko/20100101 Firefox/16.0");
-        pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
-        assertThat("no resources are returned as we want to filter firefox", pushResources.size(), is(0));
-    }
-
-    private Fields getBaseHeaders(short version)
-    {
-        Fields requestHeaders = new Fields();
-        requestHeaders.put(HTTPSPDYHeader.SCHEME.name(version), SCHEME);
-        requestHeaders.put(HTTPSPDYHeader.HOST.name(version), HOST);
-        requestHeaders.put(HTTPSPDYHeader.URI.name(version), MAIN_URI);
-        requestHeaders.put(HTTPSPDYHeader.METHOD.name(version), METHOD);
-        return requestHeaders;
-    }
-
-    private void setMockExpectations()
-    {
-        when(stream.getSession()).thenReturn(session);
-        when(session.getVersion()).thenReturn(VERSION);
-    }
-
-    private String fillPushStrategyCache(Fields requestHeaders)
-    {
-        Set<String> pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
-        assertThat("pushResources is empty", pushResources.size(), is(0));
-
-        String origin = SCHEME + "://" + HOST;
-        String referrerUrl = origin + MAIN_URI;
-
-        requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), "image.jpg");
-        requestHeaders.put("referer", referrerUrl);
-        pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
-        assertThat("pushResources is empty", pushResources.size(), is(0));
-
-        requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), "style.css");
-        pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
-        assertThat("pushResources is empty", pushResources.size(), is(0));
-
-        requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), MAIN_URI);
-        pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
-        assertThat("pushResources contains two elements image.jpg and style.css", pushResources.size(), is(2));
-        return referrerUrl;
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SPDYTestUtils.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SPDYTestUtils.java
deleted file mode 100644
index 4b69664..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SPDYTestUtils.java
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server.http;
-
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-
-public class SPDYTestUtils
-{
-    public static Fields createHeaders(String host, int port, short version, String httpMethod, String path)
-    {
-        Fields headers = new Fields();
-        headers.put(HTTPSPDYHeader.METHOD.name(version), httpMethod);
-        headers.put(HTTPSPDYHeader.URI.name(version), path);
-        headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-        headers.put(HTTPSPDYHeader.SCHEME.name(version), "http");
-        headers.put(HTTPSPDYHeader.HOST.name(version), host + ":" + port);
-        return headers;
-    }
-
-    public static SslContextFactory newSslContextFactory()
-    {
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.setEndpointIdentificationAlgorithm("");
-        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
-        sslContextFactory.setKeyStorePassword("storepwd");
-        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
-        sslContextFactory.setTrustStorePassword("storepwd");
-        sslContextFactory.setProtocol("TLSv1");
-        sslContextFactory.setIncludeProtocols("TLSv1");
-        return sslContextFactory;
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SSLExternalServerTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SSLExternalServerTest.java
deleted file mode 100644
index aca3ea06ca..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SSLExternalServerTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.http;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.Assert;
-import org.junit.Assume;
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class SSLExternalServerTest extends AbstractHTTPSPDYTest
-{
-    public SSLExternalServerTest(short version)
-    {
-        // Google Servers do not support SPDY/2 anymore
-        super(SPDY.V3);
-    }
-
-    @Override
-    protected SPDYClient.Factory newSPDYClientFactory(Executor threadPool)
-    {
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        // Force TLSv1
-        sslContextFactory.setIncludeProtocols("TLSv1");
-        sslContextFactory.setEndpointIdentificationAlgorithm("");
-        return new SPDYClient.Factory(threadPool, null, sslContextFactory, 30000);
-    }
-
-    @Test
-    @Ignore("Relies on an external server")
-    public void testExternalServer() throws Exception
-    {
-        String host = "encrypted.google.com";
-        int port = 443;
-        InetSocketAddress address = new InetSocketAddress(host, port);
-
-        try
-        {
-            // Test whether there is connectivity to avoid fail the test when offline
-            Socket socket = new Socket();
-            socket.connect(address, 5000);
-            socket.close();
-        }
-        catch (IOException x)
-        {
-            Assume.assumeNoException(x);
-        }
-
-        Session session = startClient(version, address, null);
-        Fields headers = new Fields();
-        headers.put(HTTPSPDYHeader.SCHEME.name(version), "https");
-        headers.put(HTTPSPDYHeader.HOST.name(version), host + ":" + port);
-        headers.put(HTTPSPDYHeader.METHOD.name(version), "GET");
-        headers.put(HTTPSPDYHeader.URI.name(version), "/");
-        headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-        final CountDownLatch latch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields headers = replyInfo.getHeaders();
-                Fields.Field versionHeader = headers.get(HTTPSPDYHeader.STATUS.name(version));
-                if (versionHeader != null)
-                {
-                    Matcher matcher = Pattern.compile("(\\d{3}).*").matcher(versionHeader.getValue());
-                    if (matcher.matches() && Integer.parseInt(matcher.group(1)) < 400)
-                        latch.countDown();
-                }
-            }
-        });
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ServerHTTPSPDYTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ServerHTTPSPDYTest.java
deleted file mode 100644
index c5ef175..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ServerHTTPSPDYTest.java
+++ /dev/null
@@ -1,1625 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.http;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-import javax.servlet.AsyncContext;
-import javax.servlet.AsyncEvent;
-import javax.servlet.AsyncListener;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.server.HttpChannel;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.spdy.api.BytesDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.log.StdErrLog;
-import org.junit.Assert;
-import org.junit.Test;
-
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.core.IsInstanceOf.instanceOf;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
-{
-    public static final String SUSPENDED_ATTRIBUTE = ServerHTTPSPDYTest.class.getName() + ".SUSPENDED";
-
-    public ServerHTTPSPDYTest(short version)
-    {
-        super(version);
-    }
-
-    @Test
-    public void testSimpleGET() throws Exception
-    {
-        final String path = "/foo";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                assertEquals("GET", httpRequest.getMethod());
-                assertEquals(path, target);
-                assertEquals(path, httpRequest.getRequestURI());
-                assertThat("accept-encoding is set to gzip, even if client didn't set it",
-                        httpRequest.getHeader("accept-encoding"), containsString("gzip"));
-                assertThat(httpRequest.getHeader("host"), is("localhost:" + connector.getLocalPort()));
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getLocalPort(), version, "GET", path);
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertTrue(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertThat(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"), is(true));
-                assertThat(replyHeaders.get(HttpHeader.SERVER.asString()), is(notNullValue()));
-                assertThat(replyHeaders.get(HttpHeader.X_POWERED_BY.asString()), is(notNullValue()));
-                replyLatch.countDown();
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithQueryString() throws Exception
-    {
-        final String path = "/foo";
-        final String query = "p=1";
-        final String uri = path + "?" + query;
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                assertEquals("GET", httpRequest.getMethod());
-                assertEquals(path, target);
-                assertEquals(path, httpRequest.getRequestURI());
-                assertEquals(query, httpRequest.getQueryString());
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", uri);
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertTrue(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithCookies() throws Exception
-    {
-        final String path = "/foo";
-        final String uri = path;
-        final String cookie1 = "cookie1";
-        final String cookie2 = "cookie2";
-        final String cookie1Value = "cookie 1 value";
-        final String cookie2Value = "cookie 2 value";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                httpResponse.addCookie(new Cookie(cookie1, cookie1Value));
-                httpResponse.addCookie(new Cookie(cookie2, cookie2Value));
-                assertThat("method is GET", httpRequest.getMethod(), is("GET"));
-                assertThat("target is /foo", target, is(path));
-                assertThat("requestUri is /foo", httpRequest.getRequestURI(), is(path));
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", uri);
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertThat("isClose is true", replyInfo.isClose(), is(true));
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertThat("response code is 200 OK", replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue()
-                        .contains("200"), is(true));
-                assertThat(replyInfo.getHeaders().get("Set-Cookie").getValues().get(0), is(cookie1 + "=\"" + cookie1Value +
-                        "\";Version=1"));
-                assertThat(replyInfo.getHeaders().get("Set-Cookie").getValues().get(1), is(cookie2 + "=\"" + cookie2Value +
-                        "\";Version=1"));
-                replyLatch.countDown();
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testHEAD() throws Exception
-    {
-        final String path = "/foo";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                assertEquals("HEAD", httpRequest.getMethod());
-                assertEquals(path, target);
-                assertEquals(path, httpRequest.getRequestURI());
-                httpResponse.getWriter().write("body that shouldn't be sent on a HEAD request");
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "HEAD", path);
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertTrue(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                fail("HEAD request shouldn't send any data");
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testPOSTWithDelayedContentBody() throws Exception
-    {
-        final String path = "/foo";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                // don't read the request body, reply immediately
-                request.setHandled(true);
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", path);
-        headers.put("content-type", "application/x-www-form-urlencoded");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0),
-                new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onReply(Stream stream, ReplyInfo replyInfo)
-                    {
-                        assertTrue(replyInfo.isClose());
-                        Fields replyHeaders = replyInfo.getHeaders();
-                        assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                        replyLatch.countDown();
-                    }
-                });
-        stream.data(new StringDataInfo("a", false));
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        stream.data(new StringDataInfo("b", true));
-    }
-
-    @Test
-    public void testPOSTWithParameters() throws Exception
-    {
-        final String path = "/foo";
-        final String data = "a=1&b=2";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                assertEquals("POST", httpRequest.getMethod());
-                assertEquals("1", httpRequest.getParameter("a"));
-                assertEquals("2", httpRequest.getParameter("b"));
-                assertNotNull(httpRequest.getRemoteHost());
-                assertNotNull(httpRequest.getRemotePort());
-                assertNotNull(httpRequest.getRemoteAddr());
-                assertNotNull(httpRequest.getLocalPort());
-                assertNotNull(httpRequest.getLocalName());
-                assertNotNull(httpRequest.getLocalAddr());
-                assertNotNull(httpRequest.getServerPort());
-                assertNotNull(httpRequest.getServerName());
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", path);
-        headers.put("content-type", "application/x-www-form-urlencoded");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0),
-                new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onReply(Stream stream, ReplyInfo replyInfo)
-                    {
-                        assertTrue(replyInfo.isClose());
-                        Fields replyHeaders = replyInfo.getHeaders();
-                        assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                        replyLatch.countDown();
-                    }
-                });
-        stream.data(new StringDataInfo(data, true));
-
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testPOSTWithParametersInTwoFramesTwoReads() throws Exception
-    {
-        final String path = "/foo";
-        final String data1 = "a=1&";
-        final String data2 = "b=2";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                assertEquals("POST", httpRequest.getMethod());
-                assertEquals("1", httpRequest.getParameter("a"));
-                assertEquals("2", httpRequest.getParameter("b"));
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", path);
-        headers.put("content-type", "application/x-www-form-urlencoded");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0),
-                new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onReply(Stream stream, ReplyInfo replyInfo)
-                    {
-                        assertTrue(replyInfo.isClose());
-                        Fields replyHeaders = replyInfo.getHeaders();
-                        assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                        replyLatch.countDown();
-                    }
-                });
-        // Sleep between the data frames so that they will be read in 2 reads
-        stream.data(new StringDataInfo(data1, false));
-        Thread.sleep(1000);
-        stream.data(new StringDataInfo(data2, true));
-
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testPOSTWithParametersInTwoFramesOneRead() throws Exception
-    {
-        final String path = "/foo";
-        final String data1 = "a=1&";
-        final String data2 = "b=2";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                assertEquals("POST", httpRequest.getMethod());
-                assertEquals("1", httpRequest.getParameter("a"));
-                assertEquals("2", httpRequest.getParameter("b"));
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", path);
-        headers.put("content-type", "application/x-www-form-urlencoded");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertTrue(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.toString(), replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-        });
-
-        // Send the data frames consecutively, so the server reads both frames in one read
-        stream.data(new StringDataInfo(data1, false));
-        stream.data(new StringDataInfo(data2, true));
-
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithSmallResponseContent() throws Exception
-    {
-        final String data = "0123456789ABCDEF";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                httpResponse.setStatus(HttpServletResponse.SC_OK);
-                ServletOutputStream output = httpResponse.getOutputStream();
-                output.write(data.getBytes(StandardCharsets.UTF_8));
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                assertTrue(dataInfo.isClose());
-                assertEquals(data, dataInfo.asString(StandardCharsets.UTF_8, true));
-                dataLatch.countDown();
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithOneByteResponseContent() throws Exception
-    {
-        final char data = 'x';
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                httpResponse.setStatus(HttpServletResponse.SC_OK);
-                ServletOutputStream output = httpResponse.getOutputStream();
-                output.write(data);
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                assertTrue(dataInfo.isClose());
-                byte[] bytes = dataInfo.asBytes(true);
-                assertEquals(1, bytes.length);
-                assertEquals(data, bytes[0]);
-                dataLatch.countDown();
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithSmallResponseContentInTwoChunks() throws Exception
-    {
-        final String data1 = "0123456789ABCDEF";
-        final String data2 = "FEDCBA9876543210";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                httpResponse.setStatus(HttpServletResponse.SC_OK);
-                ServletOutputStream output = httpResponse.getOutputStream();
-                output.write(data1.getBytes(StandardCharsets.UTF_8));
-                output.flush();
-                output.write(data2.getBytes(StandardCharsets.UTF_8));
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(2);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final AtomicInteger replyFrames = new AtomicInteger();
-            private final AtomicInteger dataFrames = new AtomicInteger();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertEquals(1, replyFrames.incrementAndGet());
-                Assert.assertFalse(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                int data = dataFrames.incrementAndGet();
-                assertTrue(data >= 1 && data <= 2);
-                if (data == 1)
-                    assertEquals(data1, dataInfo.asString(StandardCharsets.UTF_8, true));
-                else
-                    assertEquals(data2, dataInfo.asString(StandardCharsets.UTF_8, true));
-                dataLatch.countDown();
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithBigResponseContentInOneWrite() throws Exception
-    {
-        final byte[] data = new byte[128 * 1024];
-        Arrays.fill(data, (byte)'x');
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                httpResponse.setStatus(HttpServletResponse.SC_OK);
-                ServletOutputStream output = httpResponse.getOutputStream();
-                output.write(data);
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final AtomicInteger contentBytes = new AtomicInteger();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                contentBytes.addAndGet(dataInfo.asByteBuffer(true).remaining());
-                if (dataInfo.isClose())
-                {
-                    assertEquals(data.length, contentBytes.get());
-                    dataLatch.countDown();
-                }
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithBigResponseContentInMultipleWrites() throws Exception
-    {
-        final byte[] data = new byte[4 * 1024];
-        Arrays.fill(data, (byte)'x');
-        final int writeTimes = 16;
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                httpResponse.setStatus(HttpServletResponse.SC_OK);
-                ServletOutputStream output = httpResponse.getOutputStream();
-                for (int i = 0; i < writeTimes; i++)
-                {
-                    output.write(data);
-                }
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final AtomicInteger contentBytes = new AtomicInteger();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                contentBytes.addAndGet(dataInfo.asByteBuffer(true).remaining());
-                if (dataInfo.isClose())
-                {
-                    assertEquals(data.length * writeTimes, contentBytes.get());
-                    dataLatch.countDown();
-                }
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithBigResponseContentInTwoWrites() throws Exception
-    {
-        final byte[] data = new byte[128 * 1024];
-        Arrays.fill(data, (byte)'y');
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                httpResponse.setStatus(HttpServletResponse.SC_OK);
-                ServletOutputStream output = httpResponse.getOutputStream();
-                output.write(data);
-                output.write(data);
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final AtomicInteger contentBytes = new AtomicInteger();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                contentBytes.addAndGet(dataInfo.asByteBuffer(true).remaining());
-                if (dataInfo.isClose())
-                {
-                    assertEquals(2 * data.length, contentBytes.get());
-                    dataLatch.countDown();
-                }
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithOutputStreamFlushedAndClosed() throws Exception
-    {
-        final String data = "0123456789ABCDEF";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                httpResponse.setStatus(HttpServletResponse.SC_OK);
-                ServletOutputStream output = httpResponse.getOutputStream();
-                output.write(data.getBytes(StandardCharsets.UTF_8));
-                output.flush();
-                output.close();
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                ByteBuffer byteBuffer = dataInfo.asByteBuffer(true);
-                while (byteBuffer.hasRemaining())
-                    buffer.write(byteBuffer.get());
-                if (dataInfo.isClose())
-                {
-                    assertEquals(data, new String(buffer.toByteArray(), StandardCharsets.UTF_8));
-                    dataLatch.countDown();
-                }
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithResponseResetBuffer() throws Exception
-    {
-        final String data1 = "0123456789ABCDEF";
-        final String data2 = "FEDCBA9876543210";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                httpResponse.setStatus(HttpServletResponse.SC_OK);
-                ServletOutputStream output = httpResponse.getOutputStream();
-                // Write some
-                output.write(data1.getBytes(StandardCharsets.UTF_8));
-                // But then change your mind and reset the buffer
-                httpResponse.resetBuffer();
-                output.write(data2.getBytes(StandardCharsets.UTF_8));
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                ByteBuffer byteBuffer = dataInfo.asByteBuffer(true);
-                while (byteBuffer.hasRemaining())
-                    buffer.write(byteBuffer.get());
-                if (dataInfo.isClose())
-                {
-                    assertEquals(data2, new String(buffer.toByteArray(), StandardCharsets.UTF_8));
-                    dataLatch.countDown();
-                }
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithRedirect() throws Exception
-    {
-        final String suffix = "/redirect";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                String location = httpResponse.encodeRedirectURL(String.format("%s://%s:%d%s",
-                        request.getScheme(), request.getLocalAddr(), request.getLocalPort(), target + suffix));
-                httpResponse.sendRedirect(location);
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final AtomicInteger replies = new AtomicInteger();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertEquals(1, replies.incrementAndGet());
-                assertTrue(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("302"));
-                assertTrue(replyHeaders.get("location").getValue().endsWith(suffix));
-                replyLatch.countDown();
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithSendError() throws Exception
-    {
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final AtomicInteger replies = new AtomicInteger();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertEquals(1, replies.incrementAndGet());
-                Assert.assertFalse(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("404"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                if (dataInfo.isClose())
-                    dataLatch.countDown();
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithException() throws Exception
-    {
-        StdErrLog log = StdErrLog.getLogger(HttpChannel.class);
-        log.setHideStacks(true);
-
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                throw new NullPointerException("thrown_explicitly_by_the_test");
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch latch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final AtomicInteger replies = new AtomicInteger();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertEquals(1, replies.incrementAndGet());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("500"));
-                replyLatch.countDown();
-                if (replyInfo.isClose())
-                    latch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                if (dataInfo.isClose())
-                    latch.countDown();
-            }
-        });
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(latch.await(5, TimeUnit.SECONDS));
-
-        log.setHideStacks(false);
-    }
-
-    @Test
-    public void testGETWithSmallResponseContentChunked() throws Exception
-    {
-        final String pangram1 = "the quick brown fox jumps over the lazy dog";
-        final String pangram2 = "qualche vago ione tipo zolfo, bromo, sodio";
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                httpResponse.setHeader("Transfer-Encoding", "chunked");
-                ServletOutputStream output = httpResponse.getOutputStream();
-                output.write(pangram1.getBytes(StandardCharsets.UTF_8));
-                httpResponse.setHeader("EXTRA", "X");
-                output.flush();
-                output.write(pangram2.getBytes(StandardCharsets.UTF_8));
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(2);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final AtomicInteger replyFrames = new AtomicInteger();
-            private final AtomicInteger dataFrames = new AtomicInteger();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertEquals(1, replyFrames.incrementAndGet());
-                Assert.assertFalse(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                assertTrue(replyHeaders.get("extra").getValue().contains("X"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                int count = dataFrames.incrementAndGet();
-                if (count == 1)
-                {
-                    Assert.assertFalse(dataInfo.isClose());
-                    assertEquals(pangram1, dataInfo.asString(StandardCharsets.UTF_8, true));
-                }
-                else if (count == 2)
-                {
-                    assertTrue(dataInfo.isClose());
-                    assertEquals(pangram2, dataInfo.asString(StandardCharsets.UTF_8, true));
-                }
-                dataLatch.countDown();
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithMediumContentAsBufferByPassed() throws Exception
-    {
-        final byte[] data = new byte[2048];
-
-        final CountDownLatch handlerLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                request.getResponse().getHttpOutput().sendContent(ByteBuffer.wrap(data));
-                handlerLatch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final AtomicInteger replyFrames = new AtomicInteger();
-            private final AtomicInteger contentLength = new AtomicInteger();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertEquals(1, replyFrames.incrementAndGet());
-                Assert.assertFalse(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                contentLength.addAndGet(dataInfo.asBytes(true).length);
-                if (dataInfo.isClose())
-                {
-                    Assert.assertEquals(data.length, contentLength.get());
-                    dataLatch.countDown();
-                }
-            }
-        });
-        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETWithMultipleMediumContentByPassed() throws Exception
-    {
-        final byte[] data = new byte[2048];
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                // The sequence of write/flush/write/write below triggers a condition where
-                // HttpGenerator._bypass is set to true on the second write(), and the
-                // third write causes an infinite spin loop on the third write().
-                request.setHandled(true);
-                OutputStream output = httpResponse.getOutputStream();
-                output.write(data);
-                output.flush();
-                output.write(data);
-                output.write(data);
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        final AtomicInteger contentLength = new AtomicInteger();
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.available());
-                contentLength.addAndGet(dataInfo.length());
-                if (dataInfo.isClose())
-                    dataLatch.countDown();
-            }
-        });
-        assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-        assertEquals(3 * data.length, contentLength.get());
-    }
-
-    @Test
-    public void testPOSTThenSuspendRequestThenReadOneChunkThenComplete() throws Exception
-    {
-        final byte[] data = new byte[2000];
-        final CountDownLatch latch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                final AsyncContext asyncContext = request.startAsync();
-                new Thread()
-                {
-                    @Override
-                    public void run()
-                    {
-                        try
-                        {
-                            readRequestData(request, data.length);
-                            asyncContext.complete();
-                            latch.countDown();
-                        }
-                        catch (IOException x)
-                        {
-                            x.printStackTrace();
-                        }
-                    }
-                }.start();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-        });
-        stream.data(new BytesDataInfo(data, true));
-
-        assertTrue(latch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testPOSTThenSuspendExpire() throws Exception
-    {
-        final CountDownLatch dispatchedAgainAfterExpire = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                if (request.getAttribute(SUSPENDED_ATTRIBUTE) == Boolean.TRUE)
-                {
-                    dispatchedAgainAfterExpire.countDown();
-                }
-                else
-                {
-                    AsyncContext asyncContext = request.startAsync();
-                    asyncContext.setTimeout(1000);
-                    asyncContext.addListener(new AsyncListenerAdapter());
-                    request.setAttribute(SUSPENDED_ATTRIBUTE, Boolean.TRUE);
-                }
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, true, (byte)0), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-        });
-
-        assertTrue("Not dispatched again after expire", dispatchedAgainAfterExpire.await(5,
-                TimeUnit.SECONDS));
-        assertTrue("Reply not sent", replyLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testPOSTThenSuspendExpireWithRequestData() throws Exception
-    {
-        final byte[] data = new byte[2000];
-        final CountDownLatch dispatchedAgainAfterExpire = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                if (request.getAttribute(SUSPENDED_ATTRIBUTE) == Boolean.TRUE)
-                {
-                    dispatchedAgainAfterExpire.countDown();
-                }
-                else
-                {
-                    readRequestData(request, data.length);
-                    AsyncContext asyncContext = request.startAsync();
-                    asyncContext.setTimeout(1000);
-                    asyncContext.addListener(new AsyncListenerAdapter());
-                    request.setAttribute(SUSPENDED_ATTRIBUTE, Boolean.TRUE);
-                }
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-        });
-        stream.data(new BytesDataInfo(data, true));
-
-        assertTrue("Not dispatched again after expire", dispatchedAgainAfterExpire.await(5,
-                TimeUnit.SECONDS));
-        assertTrue("Reply not sent", replyLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    private void readRequestData(Request request, int expectedDataLength) throws IOException
-    {
-        InputStream input = request.getInputStream();
-        byte[] buffer = new byte[512];
-        int read = 0;
-        while (read < expectedDataLength)
-            read += input.read(buffer);
-    }
-
-    @Test
-    public void testPOSTThenSuspendRequestThenReadTwoChunksThenComplete() throws Exception
-    {
-        final byte[] data = new byte[2000];
-        final CountDownLatch latch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                final AsyncContext asyncContext = request.startAsync();
-                new Thread()
-                {
-                    @Override
-                    public void run()
-                    {
-                        try
-                        {
-                            InputStream input = request.getInputStream();
-                            byte[] buffer = new byte[512];
-                            int read = 0;
-                            while (read < 2 * data.length)
-                                read += input.read(buffer);
-                            asyncContext.complete();
-                            latch.countDown();
-                        }
-                        catch (IOException x)
-                        {
-                            x.printStackTrace();
-                        }
-                    }
-                }.start();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", "/foo");
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                replyLatch.countDown();
-            }
-        });
-        stream.data(new BytesDataInfo(data, false));
-        stream.data(new BytesDataInfo(data, true));
-
-        assertTrue(latch.await(5, TimeUnit.SECONDS));
-        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testPOSTThenSuspendRequestThenResumeThenRespond() throws Exception
-    {
-        final byte[] data = new byte[1000];
-        final CountDownLatch latch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                if (request.getAttribute(SUSPENDED_ATTRIBUTE) == Boolean.TRUE)
-                {
-                    OutputStream output = httpResponse.getOutputStream();
-                    output.write(data);
-                }
-                else
-                {
-                    final AsyncContext asyncContext = request.startAsync();
-                    request.setAttribute(SUSPENDED_ATTRIBUTE, Boolean.TRUE);
-                    InputStream input = request.getInputStream();
-                    byte[] buffer = new byte[256];
-                    int read = 0;
-                    while (read < data.length)
-                        read += input.read(buffer);
-                    new Thread()
-                    {
-                        @Override
-                        public void run()
-                        {
-                            try
-                            {
-                                TimeUnit.SECONDS.sleep(1);
-                                asyncContext.dispatch();
-                                latch.countDown();
-                            }
-                            catch (InterruptedException x)
-                            {
-                                x.printStackTrace();
-                            }
-                        }
-                    }.start();
-                }
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", "/foo");
-        final CountDownLatch responseLatch = new CountDownLatch(2);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                responseLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                if (dataInfo.isClose())
-                    responseLatch.countDown();
-            }
-        });
-        stream.data(new BytesDataInfo(data, true));
-
-        assertTrue(latch.await(5, TimeUnit.SECONDS));
-        assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testPOSTThenResponseWithoutReadingContent() throws Exception
-    {
-        final byte[] data = new byte[1000];
-        final CountDownLatch latch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                latch.countDown();
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", "/foo");
-        final CountDownLatch responseLatch = new CountDownLatch(1);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"));
-                responseLatch.countDown();
-            }
-        });
-        stream.data(new BytesDataInfo(data, false));
-        stream.data(new BytesDataInfo(5, TimeUnit.SECONDS, data, true));
-
-        assertTrue(latch.await(5, TimeUnit.SECONDS));
-        assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testIdleTimeout() throws Exception
-    {
-        final int idleTimeout = 500;
-        final CountDownLatch timeoutReceivedLatch = new CountDownLatch(1);
-
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                try
-                {
-                    Thread.sleep(2 * idleTimeout);
-                }
-                catch (InterruptedException e)
-                {
-                    throw new RuntimeException(e);
-                }
-                request.setHandled(true);
-            }
-        }, 30000), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/");
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, true, (byte)0),
-                new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onFailure(Stream stream, Throwable x)
-                    {
-                        assertThat("we got a TimeoutException", x, instanceOf(TimeoutException.class));
-                        timeoutReceivedLatch.countDown();
-                    }
-                });
-        stream.setIdleTimeout(idleTimeout);
-
-        assertThat("idle timeout hit", timeoutReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testIdleTimeoutSetOnConnectionOnly() throws Exception
-    {
-        final int idleTimeout = 500;
-        final CountDownLatch timeoutReceivedLatch = new CountDownLatch(1);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                try
-                {
-                    Thread.sleep(2 * idleTimeout);
-                }
-                catch (InterruptedException e)
-                {
-                    throw new RuntimeException(e);
-                }
-                request.setHandled(true);
-            }
-        }, idleTimeout), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/");
-        session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, true, (byte)0),
-                new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onFailure(Stream stream, Throwable x)
-                    {
-                        assertThat("we got a TimeoutException", x, instanceOf(TimeoutException.class));
-                        timeoutReceivedLatch.countDown();
-                    }
-                });
-
-        assertThat("idle timeout hit", timeoutReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testSingleStreamIdleTimeout() throws Exception
-    {
-        final int idleTimeout = 500;
-        final CountDownLatch timeoutReceivedLatch = new CountDownLatch(1);
-        final CountDownLatch replyReceivedLatch = new CountDownLatch(3);
-        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                if ("true".equals(request.getHeader("slow")))
-                {
-                    try
-                    {
-                        Thread.sleep(2 * idleTimeout);
-                    }
-                    catch (InterruptedException e)
-                    {
-                        throw new RuntimeException(e);
-                    }
-                }
-                request.setHandled(true);
-            }
-        }, idleTimeout), null);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/");
-        Fields slowHeaders = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/");
-        slowHeaders.add("slow", "true");
-        sendSingleRequestThatIsNotExpectedToTimeout(replyReceivedLatch, session, headers);
-        session.syn(new SynInfo(5, TimeUnit.SECONDS, slowHeaders, true, (byte)0),
-                new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onFailure(Stream stream, Throwable x)
-                    {
-                        assertThat("we got a TimeoutException", x, instanceOf(TimeoutException.class));
-                        timeoutReceivedLatch.countDown();
-                    }
-                });
-        Thread.sleep(idleTimeout / 2);
-        sendSingleRequestThatIsNotExpectedToTimeout(replyReceivedLatch, session, headers);
-        Thread.sleep(idleTimeout / 2);
-        sendSingleRequestThatIsNotExpectedToTimeout(replyReceivedLatch, session, headers);
-        assertThat("idle timeout hit", timeoutReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("received replies on 3 non idle requests", replyReceivedLatch.await(5, TimeUnit.SECONDS),
-                is(true));
-    }
-
-    private void sendSingleRequestThatIsNotExpectedToTimeout(final CountDownLatch replyReceivedLatch, Session session, Fields headers) throws ExecutionException, InterruptedException, TimeoutException
-    {
-        session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, true, (byte)0),
-                new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onReply(Stream stream, ReplyInfo replyInfo)
-                    {
-                        replyReceivedLatch.countDown();
-                    }
-                });
-    }
-
-    private class AsyncListenerAdapter implements AsyncListener
-    {
-        @Override
-        public void onStartAsync(AsyncEvent event) throws IOException
-        {
-        }
-
-        @Override
-        public void onComplete(AsyncEvent event) throws IOException
-        {
-        }
-
-        @Override
-        public void onTimeout(AsyncEvent event) throws IOException
-        {
-            event.getAsyncContext().dispatch();
-        }
-
-        @Override
-        public void onError(AsyncEvent event) throws IOException
-        {
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SimpleHTTPBenchmarkTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SimpleHTTPBenchmarkTest.java
deleted file mode 100644
index d28e739..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SimpleHTTPBenchmarkTest.java
+++ /dev/null
@@ -1,160 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.http;
-
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import java.io.IOException;
-import java.util.Random;
-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.HttpHeader;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.hamcrest.CoreMatchers;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-
-@Ignore("So far only used for testing performance tweaks. So no need to run it in a build")
-public class SimpleHTTPBenchmarkTest extends AbstractHTTPSPDYTest
-{
-    private static final Logger LOG = Log.getLogger(SimpleHTTPBenchmarkTest.class);
-    private final int dataSize = 4096 * 100;
-    private Session session;
-    private int requestCount = 100;
-
-    public SimpleHTTPBenchmarkTest(short version)
-    {
-        super(version);
-    }
-
-    @Before
-    public void setUp() throws Exception
-    {
-        final byte[] data = new byte[dataSize];
-        new Random().nextBytes(data);
-        session = startClient(version, startHTTPServer(version, new AbstractHandler()
-        {
-            @Override
-            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
-                    throws IOException, ServletException
-            {
-                request.setHandled(true);
-                assertEquals("GET", httpRequest.getMethod());
-                assertThat("accept-encoding is set to gzip, even if client didn't set it",
-                        httpRequest.getHeader("accept-encoding"), containsString("gzip"));
-                assertThat(httpRequest.getHeader("host"), is("localhost:" + connector.getLocalPort()));
-                httpResponse.getOutputStream().write(data);
-            }
-        }, 0), null);
-    }
-
-    @Test
-    public void testRunBenchmark() throws Exception
-    {
-        long overallStart = System.nanoTime();
-        int iterations = 20;
-        for (int j = 0; j < iterations; j++)
-        {
-            long start = System.nanoTime();
-            for (int i = 0; i < requestCount; i++)
-                sendGetRequestWithData();
-            long timeElapsed = System.nanoTime() - start;
-            LOG.info("Requests with {}b response took: {}ms", dataSize, timeElapsed / 1000 / 1000);
-        }
-        long timeElapsedOverall = (System.nanoTime() - overallStart) / 1000 / 1000;
-        LOG.info("Time elapsed overall: {}ms avg: {}ms", timeElapsedOverall, timeElapsedOverall / iterations);
-    }
-
-    private void sendGetRequest() throws Exception
-    {
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final String path = "/foo";
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getLocalPort(), version, "GET", path);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertTrue(replyInfo.isClose());
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertThat(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"), CoreMatchers.is(true));
-                assertThat(replyHeaders.get(HttpHeader.SERVER.asString()), CoreMatchers.is(notNullValue()));
-                assertThat(replyHeaders.get(HttpHeader.X_POWERED_BY.asString()), CoreMatchers.is(notNullValue()));
-                replyLatch.countDown();
-            }
-        });
-
-        assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    private void sendGetRequestWithData() throws Exception
-    {
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        final String path = "/foo";
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getLocalPort(), version, "GET", path);
-        session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields replyHeaders = replyInfo.getHeaders();
-                assertThat(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).getValue().contains("200"), CoreMatchers.is(true));
-                assertThat(replyHeaders.get(HttpHeader.SERVER.asString()), CoreMatchers.is(notNullValue()));
-                assertThat(replyHeaders.get(HttpHeader.X_POWERED_BY.asString()), CoreMatchers.is(notNullValue()));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.available());
-                if (dataInfo.isClose())
-                    dataLatch.countDown();
-            }
-        });
-
-        assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("data has been received", dataLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPToSPDYTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPToSPDYTest.java
deleted file mode 100644
index 765dfe5..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPToSPDYTest.java
+++ /dev/null
@@ -1,408 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-import java.net.InetSocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.util.StringContentProvider;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.spdy.api.BytesDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.SPDYServerConnector;
-import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-@RunWith(Parameterized.class)
-public class ProxyHTTPToSPDYTest
-{
-    @Parameterized.Parameters
-    public static Collection<Short[]> parameters()
-    {
-        return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3});
-    }
-
-    @Rule
-    public final TestTracker tracker = new TestTracker();
-    private final short version;
-    private HttpClient httpClient;
-    private HttpClient httpClient2;
-    private SPDYClient.Factory factory;
-    private Server server;
-    private Server proxy;
-    private ServerConnector proxyConnector;
-
-    public ProxyHTTPToSPDYTest(short version)
-    {
-        this.version = version;
-    }
-
-    protected InetSocketAddress startServer(ServerSessionFrameListener listener) throws Exception
-    {
-        server = new Server();
-        SPDYServerConnector serverConnector = new SPDYServerConnector(server, listener);
-        serverConnector.addConnectionFactory(new SPDYServerConnectionFactory(version, listener));
-        serverConnector.setPort(0);
-        server.addConnector(serverConnector);
-        server.start();
-        return new InetSocketAddress("localhost", serverConnector.getLocalPort());
-    }
-
-    protected InetSocketAddress startProxy(InetSocketAddress address) throws Exception
-    {
-        proxy = new Server();
-        ProxyEngineSelector proxyEngineSelector = new ProxyEngineSelector();
-        SPDYProxyEngine spdyProxyEngine = new SPDYProxyEngine(factory);
-        proxyEngineSelector.putProxyEngine("spdy/" + version, spdyProxyEngine);
-        proxyEngineSelector.putProxyServerInfo("localhost", new ProxyEngineSelector.ProxyServerInfo("spdy/" + version, address.getHostName(), address.getPort()));
-        proxyConnector = new HTTPSPDYProxyServerConnector(proxy, proxyEngineSelector);
-        proxyConnector.setPort(9999);
-        proxy.addConnector(proxyConnector);
-        proxy.start();
-        return new InetSocketAddress("localhost", proxyConnector.getLocalPort());
-    }
-
-    @Before
-    public void init() throws Exception
-    {
-        factory = new SPDYClient.Factory();
-        factory.start();
-        httpClient = new HttpClient();
-        httpClient.start();
-        httpClient2 = new HttpClient();
-        httpClient2.start();
-    }
-
-    @After
-    public void destroy() throws Exception
-    {
-        httpClient.stop();
-        httpClient2.stop();
-        if (server != null)
-        {
-            server.stop();
-            server.join();
-        }
-        if (proxy != null)
-        {
-            proxy.stop();
-            proxy.join();
-        }
-        factory.stop();
-    }
-
-    @Test
-    public void testClosingClientDoesNotCloseServer() throws Exception
-    {
-        final CountDownLatch closeLatch = new CountDownLatch(1);
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Fields responseHeaders = new Fields();
-                responseHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-                responseHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200 OK");
-                stream.reply(new ReplyInfo(responseHeaders, true), new Callback.Adapter());
-                return null;
-            }
-
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                closeLatch.countDown();
-            }
-        }));
-
-        Request request = httpClient.newRequest("localhost", proxyAddress.getPort()).method("GET");
-        request.header("Connection", "close");
-        ContentResponse response = request.send();
-
-        assertThat("response status is 200 OK", response.getStatus(), is(200));
-
-        // Must not close, other clients may still be connected
-        Assert.assertFalse(closeLatch.await(1, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testGETThenNoContentFromTwoClients() throws Exception
-    {
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Assert.assertTrue(synInfo.isClose());
-                Fields requestHeaders = synInfo.getHeaders();
-                Assert.assertNotNull(requestHeaders.get("via"));
-
-                Fields responseHeaders = new Fields();
-                responseHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-                responseHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200 OK");
-                ReplyInfo replyInfo = new ReplyInfo(responseHeaders, true);
-                stream.reply(replyInfo, new Callback.Adapter());
-                return null;
-            }
-        }));
-
-        ContentResponse response = httpClient.newRequest("localhost", proxyAddress.getPort()).method(HttpMethod.GET)
-                .send();
-        assertThat("response code is 200 OK", response.getStatus(), is(200));
-
-        // Perform another request with another client
-        ContentResponse response2 = httpClient2.newRequest("localhost", proxyAddress.getPort()).method(HttpMethod.GET)
-                .send();
-        assertThat("response2 code is 200 OK", response2.getStatus(), is(200));
-    }
-
-    @Test
-    public void testHEADRequest() throws Exception
-    {
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Assert.assertTrue(synInfo.isClose());
-                Fields requestHeaders = synInfo.getHeaders();
-                Assert.assertNotNull(requestHeaders.get("via"));
-
-                Fields responseHeaders = new Fields();
-                responseHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-                responseHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200 OK");
-                ReplyInfo replyInfo = new ReplyInfo(responseHeaders, true);
-                stream.reply(replyInfo, new Callback.Adapter());
-
-                return null;
-            }
-        }));
-        ContentResponse response = httpClient.newRequest("localhost", proxyAddress.getPort()).method(HttpMethod.HEAD).send();
-        assertThat("response code is 200 OK", response.getStatus(), is(200));
-    }
-
-    @Test
-    public void testGETThenSmallResponseContent() throws Exception
-    {
-        final byte[] data = "0123456789ABCDEF".getBytes(StandardCharsets.UTF_8);
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Assert.assertTrue(synInfo.isClose());
-                Fields requestHeaders = synInfo.getHeaders();
-                Assert.assertNotNull(requestHeaders.get("via"));
-
-                Fields responseHeaders = new Fields();
-                responseHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-                responseHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200 OK");
-                responseHeaders.put("content-length", String.valueOf(data.length));
-
-                ReplyInfo replyInfo = new ReplyInfo(responseHeaders, false);
-                stream.reply(replyInfo, new Callback.Adapter());
-                stream.data(new BytesDataInfo(data, true), new Callback.Adapter());
-
-                return null;
-            }
-        }));
-
-        ContentResponse response = httpClient.newRequest("localhost", proxyAddress.getPort()).method(HttpMethod.GET)
-                .send();
-        assertThat("response code is 200 OK", response.getStatus(), is(200));
-        assertThat(Arrays.equals(response.getContent(), data), is(true));
-
-        // Perform another request so that we are sure we reset the states of parsers and generators
-        ContentResponse response2 = httpClient.newRequest("localhost", proxyAddress.getPort()).method(HttpMethod.GET)
-                .send();
-        assertThat("response2 code is 200 OK", response2.getStatus(), is(200));
-        assertThat(Arrays.equals(response2.getContent(), data), is(true));
-    }
-
-    @Test
-    public void testPOSTWithSmallRequestContentThenRedirect() throws Exception
-    {
-        final String data = "0123456789ABCDEF";
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataInfo.consume(dataInfo.length());
-                        if (dataInfo.isClose())
-                        {
-                            Fields headers = new Fields();
-                            headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-                            headers.put(HTTPSPDYHeader.STATUS.name(version), "303 See Other");
-                            headers.put(HttpHeader.LOCATION.asString(),"http://other.location");
-                            stream.reply(new ReplyInfo(headers, true), new Callback.Adapter());
-                        }
-                    }
-                };
-            }
-        }));
-
-        ContentResponse response = httpClient.newRequest("localhost", proxyAddress.getPort()).method(HttpMethod.POST).content(new
-                StringContentProvider(data)).followRedirects(false).send();
-        assertThat("response code is 303", response.getStatus(), is(303));
-
-        // Perform another request so that we are sure we reset the states of parsers and generators
-        ContentResponse response2 = httpClient.newRequest("localhost", proxyAddress.getPort()).method(HttpMethod
-                .POST).content(new StringContentProvider(data)).followRedirects(false).send();
-        assertThat("response2 code is 303", response2.getStatus(), is(303));
-    }
-
-    @Test
-    public void testPOSTWithSmallRequestContentThenSmallResponseContent() throws Exception
-    {
-        String dataString = "0123456789ABCDEF";
-        final byte[] data = dataString.getBytes(StandardCharsets.UTF_8);
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataInfo.consume(dataInfo.length());
-                        if (dataInfo.isClose())
-                        {
-                            Fields responseHeaders = new Fields();
-                            responseHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-                            responseHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200 OK");
-                            responseHeaders.put("content-length", String.valueOf(data.length));
-                            ReplyInfo replyInfo = new ReplyInfo(responseHeaders, false);
-                            stream.reply(replyInfo, new Callback.Adapter());
-                            stream.data(new BytesDataInfo(data, true), new Callback.Adapter());
-                        }
-                    }
-                };
-            }
-        }));
-
-        ContentResponse response = httpClient.POST("http://localhost:" + proxyAddress.getPort() + "/").content(new
-                StringContentProvider(dataString)).send();
-        assertThat("response status is 200 OK", response.getStatus(), is(200));
-        assertThat("response content matches expected dataString", response.getContentAsString(), is(dataString));
-
-        // Perform another request so that we are sure we reset the states of parsers and generators
-        response = httpClient.POST("http://localhost:" + proxyAddress.getPort() + "/").content(new
-                StringContentProvider(dataString)).send();
-        assertThat("response status is 200 OK", response.getStatus(), is(200));
-        assertThat("response content matches expected dataString", response.getContentAsString(), is(dataString));
-    }
-
-    @Test
-    public void testGETThenSPDYPushIsIgnored() throws Exception
-    {
-        final byte[] data = "0123456789ABCDEF".getBytes(StandardCharsets.UTF_8);
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Fields responseHeaders = new Fields();
-                responseHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-                responseHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200 OK");
-
-                Fields pushHeaders = new Fields();
-                pushHeaders.put(HTTPSPDYHeader.URI.name(version), "/push");
-                stream.push(new PushInfo(5, TimeUnit.SECONDS, pushHeaders, false), new Promise.Adapter<Stream>()
-                {
-                    @Override
-                    public void succeeded(Stream pushStream)
-                    {
-                        pushStream.data(new BytesDataInfo(data, true), new Callback.Adapter());
-                    }
-                });
-
-                stream.reply(new ReplyInfo(responseHeaders, true), new Callback.Adapter());
-                return null;
-            }
-        }));
-
-        ContentResponse response = httpClient.newRequest("localhost", proxyAddress.getPort()).method(HttpMethod.GET).send();
-        assertThat("response code is 200 OK", response.getStatus(), is(200));
-    }
-
-    @Test
-    public void testGETThenReset() throws Exception
-    {
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Assert.assertTrue(synInfo.isClose());
-                Fields requestHeaders = synInfo.getHeaders();
-                Assert.assertNotNull(requestHeaders.get("via"));
-
-                stream.getSession().rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM), new Callback.Adapter());
-
-                return null;
-            }
-        }));
-
-        ContentResponse response = httpClient.newRequest("localhost", proxyAddress.getPort()).method(HttpMethod.GET).send();
-        assertThat("response code is 502 Gateway Error", response.getStatus(), is(502));
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPLoadTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPLoadTest.java
deleted file mode 100644
index 57a4f25..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPLoadTest.java
+++ /dev/null
@@ -1,319 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.handler.DefaultHandler;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.spdy.server.http.SPDYTestUtils;
-import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-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.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import static junit.framework.Assert.fail;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.junit.Assert.assertThat;
-
-@Ignore
-@RunWith(value = Parameterized.class)
-public abstract class ProxySPDYToHTTPLoadTest
-{
-    private static final Logger LOG = Log.getLogger(ProxySPDYToHTTPLoadTest.class);
-
-    @Parameterized.Parameters
-    public static Collection<Short[]> parameters()
-    {
-        return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3});
-    }
-
-    @Rule
-    public final TestTracker tracker = new TestTracker();
-    private final short version;
-    private final NegotiatingServerConnectionFactory negotiator;
-    private final String server1String = "server1";
-    private final String server2String = "server2";
-    private SPDYClient.Factory factory;
-    private Server server1;
-    private Server server2;
-    private Server proxy;
-    private ServerConnector proxyConnector;
-    private SslContextFactory sslContextFactory = SPDYTestUtils.newSslContextFactory();
-
-    public ProxySPDYToHTTPLoadTest(short version, NegotiatingServerConnectionFactory negotiator)
-    {
-        this.version = version;
-        this.negotiator = negotiator;
-    }
-
-    @Before
-    public void init() throws Exception
-    {
-        // change the ports if you want to trace the network traffic
-        server1 = startServer(new TestServerHandler(server1String, null), 0);
-        server2 = startServer(new TestServerHandler(server2String, null), 0);
-        factory = new SPDYClient.Factory(sslContextFactory);
-        factory.start();
-    }
-
-    @After
-    public void destroy() throws Exception
-    {
-        stopServer(server1);
-        stopServer(server2);
-        if (proxy != null)
-        {
-            proxy.stop();
-            proxy.join();
-        }
-        factory.stop();
-    }
-
-    private void stopServer(Server server) throws Exception
-    {
-        if (server != null)
-        {
-            server.stop();
-            server.join();
-        }
-    }
-
-    protected Server startServer(Handler handler, int port) throws Exception
-    {
-        QueuedThreadPool threadPool = new QueuedThreadPool(256);
-        threadPool.setName("upstreamServerQTP");
-        Server server = new Server(threadPool);
-        ServerConnector connector = new ServerConnector(server);
-        connector.setPort(port);
-        server.setHandler(handler);
-        server.addConnector(connector);
-        server.start();
-        return server;
-    }
-
-    private InetSocketAddress getServerAddress(Server server)
-    {
-        return new InetSocketAddress("localhost", ((ServerConnector)server.getConnectors()[0]).getLocalPort());
-    }
-
-    protected InetSocketAddress startProxy(InetSocketAddress server1, InetSocketAddress server2,
-                                           long proxyConnectorTimeout, long proxyEngineTimeout) throws Exception
-    {
-        QueuedThreadPool threadPool = new QueuedThreadPool(256);
-        threadPool.setName("proxyQTP");
-        proxy = new Server(threadPool);
-        ProxyEngineSelector proxyEngineSelector = new ProxyEngineSelector();
-        HttpClient httpClient = new HttpClient();
-        httpClient.start();
-        httpClient.setIdleTimeout(proxyEngineTimeout);
-        HTTPProxyEngine httpProxyEngine = new HTTPProxyEngine(httpClient);
-        proxyEngineSelector.putProxyEngine("http/1.1", httpProxyEngine);
-
-        proxyEngineSelector.putProxyServerInfo("localhost", new ProxyEngineSelector.ProxyServerInfo("http/1.1",
-                server1.getHostName(), server1.getPort()));
-        // server2 will be available at two different ProxyServerInfos with different hosts
-        proxyEngineSelector.putProxyServerInfo("127.0.0.1", new ProxyEngineSelector.ProxyServerInfo("http/1.1",
-                server2.getHostName(), server2.getPort()));
-        proxyEngineSelector.putProxyServerInfo("127.0.0.2", new ProxyEngineSelector.ProxyServerInfo("http/1.1",
-                server2.getHostName(), server2.getPort()));
-
-        proxyConnector = new HTTPSPDYProxyServerConnector(proxy, sslContextFactory, new HttpConfiguration(), proxyEngineSelector, negotiator);
-        proxyConnector.setPort(0);
-        proxyConnector.setIdleTimeout(proxyConnectorTimeout);
-        proxy.addConnector(proxyConnector);
-        proxy.start();
-        return new InetSocketAddress("localhost", proxyConnector.getLocalPort());
-    }
-
-    @Test
-    public void testSimpleLoadTest() throws Exception
-    {
-        final InetSocketAddress proxyAddress = startProxy(getServerAddress(server1), getServerAddress(server2), 30000,
-                30000);
-
-        final int requestsPerClient = 50;
-
-        ExecutorService executorService = Executors.newFixedThreadPool(3);
-
-        Runnable client1 = createClientRunnable(proxyAddress, requestsPerClient, server1String, "localhost");
-        Runnable client2 = createClientRunnable(proxyAddress, requestsPerClient, server2String, "127.0.0.1");
-        Runnable client3 = createClientRunnable(proxyAddress, requestsPerClient, server2String, "127.0.0.2");
-
-        List<Future> futures = new ArrayList<>();
-
-        futures.add(executorService.submit(client1));
-        futures.add(executorService.submit(client2));
-        futures.add(executorService.submit(client3));
-
-        for (Future future : futures)
-        {
-            future.get(60, TimeUnit.SECONDS);
-        }
-    }
-
-    private Runnable createClientRunnable(final InetSocketAddress proxyAddress, final int requestsPerClient,
-                                          final String serverIdentificationString, final String serverHost)
-    {
-        Runnable client = new Runnable()
-        {
-            @Override
-            public void run()
-            {
-                try
-                {
-                    Session client = factory.newSPDYClient(version).connect(proxyAddress, null);
-                    for (int i = 0; i < requestsPerClient; i++)
-                    {
-                        sendSingleClientRequest(proxyAddress, client, serverIdentificationString, serverHost);
-                    }
-                }
-                catch (InterruptedException | ExecutionException | TimeoutException e)
-                {
-                    e.printStackTrace();
-                    fail();
-                }
-            }
-        };
-        return client;
-    }
-
-    private void sendSingleClientRequest(InetSocketAddress proxyAddress, Session client, final String serverIdentificationString, String serverHost) throws ExecutionException, InterruptedException, TimeoutException
-    {
-        final String data = UUID.randomUUID().toString();
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-
-        Fields headers = SPDYTestUtils.createHeaders(serverHost, proxyAddress.getPort(), version, "POST", "/");
-
-        Stream stream = client.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
-        {
-            private final ByteArrayOutputStream result = new ByteArrayOutputStream();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                LOG.debug("Got reply: {}", replyInfo);
-                Fields headers = replyInfo.getHeaders();
-                assertThat("response comes from the given server", headers.get(serverIdentificationString),
-                        is(notNullValue()));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                result.write(dataInfo.asBytes(true), 0, dataInfo.length());
-                if (dataInfo.isClose())
-                {
-                    LOG.debug("Got last dataFrame: {}", dataInfo);
-                    assertThat("received data matches send data", result.toString(), is(data));
-                    dataLatch.countDown();
-                }
-            }
-        });
-
-        stream.data(new StringDataInfo(data, true), new Callback.Adapter());
-
-        assertThat("reply has been received", replyLatch.await(15, TimeUnit.SECONDS), is(true));
-        assertThat("data has been received", dataLatch.await(15, TimeUnit.SECONDS), is(true));
-        LOG.debug("Successfully received response");
-    }
-
-    private class TestServerHandler extends DefaultHandler
-    {
-        private final String responseHeader;
-        private final byte[] responseData;
-
-        private TestServerHandler(String responseHeader, byte[] responseData)
-        {
-            this.responseHeader = responseHeader;
-            this.responseData = responseData;
-        }
-
-        @Override
-        public void handle(String target, Request baseRequest, HttpServletRequest request,
-                           HttpServletResponse response) throws IOException, ServletException
-        {
-            assertThat("Via Header is set", baseRequest.getHeader("X-Forwarded-For"), is(notNullValue()));
-            assertThat("X-Forwarded-For Header is set", baseRequest.getHeader("X-Forwarded-For"),
-                    is(notNullValue()));
-            assertThat("X-Forwarded-Host Header is set", baseRequest.getHeader("X-Forwarded-Host"),
-                    is(notNullValue()));
-            assertThat("X-Forwarded-Proto Header is set", baseRequest.getHeader("X-Forwarded-Proto"),
-                    is(notNullValue()));
-            assertThat("X-Forwarded-Server Header is set", baseRequest.getHeader("X-Forwarded-Server"),
-                    is(notNullValue()));
-            baseRequest.setHandled(true);
-
-            IO.copy(request.getInputStream(), response.getOutputStream());
-
-            if (responseHeader != null)
-                response.addHeader(responseHeader, "bar");
-            if (responseData != null)
-                response.getOutputStream().write(responseData);
-        }
-    }
-
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPTest.java
deleted file mode 100644
index 067a4db..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPTest.java
+++ /dev/null
@@ -1,545 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Collection;
-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.client.HttpClient;
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.HttpChannel;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.handler.DefaultHandler;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
-import org.eclipse.jetty.spdy.api.PingInfo;
-import org.eclipse.jetty.spdy.api.PingResultInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.spdy.server.http.SPDYTestUtils;
-import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertThat;
-
-@RunWith(value = Parameterized.class)
-public abstract class ProxySPDYToHTTPTest
-{
-    @Parameterized.Parameters
-    public static Collection<Short[]> parameters()
-    {
-        return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3});
-    }
-
-    @Rule
-    public final TestTracker tracker = new TestTracker();
-    private final short version;
-    private SPDYClient.Factory factory;
-    private Server server;
-    private Server proxy;
-    private ServerConnector proxyConnector;
-    private SslContextFactory sslContextFactory = SPDYTestUtils.newSslContextFactory();
-
-    public ProxySPDYToHTTPTest(short version)
-    {
-        this.version = version;
-    }
-
-    protected InetSocketAddress startServer(Handler handler) throws Exception
-    {
-        server = new Server();
-        ServerConnector connector = new ServerConnector(server);
-        server.setHandler(handler);
-        server.addConnector(connector);
-        server.start();
-        return new InetSocketAddress("localhost", connector.getLocalPort());
-    }
-
-    protected InetSocketAddress startProxy(InetSocketAddress address, long proxyConnectorTimeout, long proxyEngineTimeout) throws Exception
-    {
-        proxy = new Server();
-        ProxyEngineSelector proxyEngineSelector = new ProxyEngineSelector();
-        HttpClient httpClient = new HttpClient();
-        httpClient.start();
-        httpClient.setIdleTimeout(proxyEngineTimeout);
-        HTTPProxyEngine httpProxyEngine = new HTTPProxyEngine(httpClient);
-        proxyEngineSelector.putProxyEngine("http/1.1", httpProxyEngine);
-        proxyEngineSelector.putProxyServerInfo("localhost", new ProxyEngineSelector.ProxyServerInfo("http/1.1", address.getHostName(), address.getPort()));
-        proxyConnector = new HTTPSPDYProxyServerConnector(proxy, sslContextFactory, proxyEngineSelector);
-        proxyConnector.setPort(0);
-        proxyConnector.setIdleTimeout(proxyConnectorTimeout);
-        proxy.addConnector(proxyConnector);
-        proxy.start();
-        return new InetSocketAddress("localhost", proxyConnector.getLocalPort());
-    }
-
-    @Before
-    public void init() throws Exception
-    {
-        factory = new SPDYClient.Factory(sslContextFactory);
-        factory.start();
-    }
-
-    @After
-    public void destroy() throws Exception
-    {
-        if (server != null)
-        {
-            server.stop();
-            server.join();
-        }
-        if (proxy != null)
-        {
-            proxy.stop();
-            proxy.join();
-        }
-        factory.stop();
-        ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false);
-    }
-
-    @Test
-    public void testSYNThenREPLY() throws Exception
-    {
-        final String header = "foo";
-
-        InetSocketAddress proxyAddress = startProxy(startServer(new TestServerHandler(header, null)), 30000, 30000);
-
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, null);
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "GET", "/");
-        headers.put(header, "bar");
-        headers.put("connection", "close");
-
-        client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields headers = replyInfo.getHeaders();
-                assertThat("Version header is set", headers.get(HTTPSPDYHeader.VERSION.name(version)), is(notNullValue()));
-                assertThat("Custom set header foo is set on response", headers.get(header), is(notNullValue()));
-                assertThat("HOP headers like connection are removed before forwarding",
-                        headers.get("connection"), is(nullValue()));
-                replyLatch.countDown();
-            }
-        });
-
-        assertThat("Reply is send to SPDY client", replyLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testSYNThenREPLYAndDATA() throws Exception
-    {
-        final byte[] data = "0123456789ABCDEF".getBytes(StandardCharsets.UTF_8);
-        final String header = "foo";
-
-        InetSocketAddress proxyAddress = startProxy(startServer(new TestServerHandler(header, data)), 30000, 30000);
-
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, null);
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "GET", "/");
-        headers.put(header, "bar");
-        headers.put("connection", "close");
-
-        client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final ByteArrayOutputStream result = new ByteArrayOutputStream();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields headers = replyInfo.getHeaders();
-                assertThat("Trailer header has been filtered by proxy", headers.get("trailer"),
-                        is(nullValue()));
-                assertThat("custom header exists in response", headers.get(header), is(notNullValue()));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                result.write(dataInfo.asBytes(true), 0, dataInfo.length());
-                if (dataInfo.isClose())
-                {
-                    assertThat("received data matches send data", result.toByteArray(), is(data));
-                    dataLatch.countDown();
-                }
-            }
-        });
-
-        assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("data has been received", dataLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testHttpServerCommitsResponseTwice() throws Exception
-    {
-        final long timeout = 1000;
-
-        InetSocketAddress proxyAddress = startProxy(startServer(new DefaultHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                response.addHeader("some response", "header");
-                response.flushBuffer();
-                try
-                {
-                    Thread.sleep(timeout * 2);
-                }
-                catch (InterruptedException e)
-                {
-                    e.printStackTrace();
-                }
-
-            }
-        }), 30000, timeout);
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch resetLatch = new CountDownLatch(1);
-
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public void onRst(Session session, RstInfo rstInfo)
-            {
-                resetLatch.countDown();
-            }
-        });
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "GET", "/");
-        headers.put("connection", "close");
-
-        client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                replyLatch.countDown();
-            }
-        });
-
-        assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("stream is reset", resetLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testHttpServerSendsRedirect() throws Exception
-    {
-        InetSocketAddress proxyAddress = startProxy(startServer(new DefaultHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                baseRequest.setHandled(true);
-                response.setStatus(HttpServletResponse.SC_FOUND);
-                response.setHeader("Location", "http://doesnot.exist");
-            }
-        }), 30000, 30000);
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, null);
-        Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "GET", "/");
-
-        client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                assertThat("Status code is 302", replyInfo.getHeaders().get(HTTPSPDYHeader.STATUS.name(version)).getValue(),
-                        is("302"));
-                assertThat("Location header has been received", replyInfo.getHeaders().get("Location"), is(notNullValue()));
-                replyLatch.countDown();
-            }
-        });
-
-        assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testSYNWithRequestContentThenREPLYAndDATA() throws Exception
-    {
-        final String data = "0123456789ABCDEF";
-        final String header = "foo";
-
-        InetSocketAddress proxyAddress = startProxy(startServer(new TestServerHandler(header, null)), 30000, 30000);
-
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, null);
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "POST", "/");
-        headers.put(header, "bar");
-        headers.put("connection", "close");
-
-        Stream stream = client.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
-        {
-            private final ByteArrayOutputStream result = new ByteArrayOutputStream();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields headers = replyInfo.getHeaders();
-                assertThat("custom header exists in response", headers.get(header), is(notNullValue()));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                result.write(dataInfo.asBytes(true), 0, dataInfo.length());
-                if (dataInfo.isClose())
-                {
-                    assertThat("received data matches send data", data, is(result.toString()));
-                    dataLatch.countDown();
-                }
-            }
-        });
-
-        stream.data(new StringDataInfo(data, true), new Callback.Adapter());
-
-        assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("data has been received", dataLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testSYNWithSplitRequestContentThenREPLYAndDATA() throws Exception
-    {
-        final String data = "0123456789ABCDEF";
-        final String data2 = "ABCDEF";
-        final String header = "foo";
-
-        InetSocketAddress proxyAddress = startProxy(startServer(new TestServerHandler(header, null)), 30000, 30000);
-
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, null);
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "POST", "/");
-        headers.put(header, "bar");
-        headers.put("connection", "close");
-
-        Stream stream = client.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
-        {
-            private final ByteArrayOutputStream result = new ByteArrayOutputStream();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields headers = replyInfo.getHeaders();
-                assertThat("custom header exists in response", headers.get(header), is(notNullValue()));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                result.write(dataInfo.asBytes(true), 0, dataInfo.length());
-                if (dataInfo.isClose())
-                {
-                    assertThat("received data matches send data", result.toString(), is(data + data2));
-                    dataLatch.countDown();
-                }
-            }
-        });
-
-        stream.data(new StringDataInfo(data, false), new Callback.Adapter());
-        stream.data(new StringDataInfo(data2, true), new Callback.Adapter());
-
-        assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("data has been received", dataLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testClientTimeout() throws Exception
-    {
-        long timeout = 1000;
-
-        InetSocketAddress proxyAddress = startProxy(startServer(new TestServerHandler(null, null)), timeout, 30000);
-
-        final CountDownLatch goAwayLatch = new CountDownLatch(1);
-
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayReceivedInfo)
-            {
-                goAwayLatch.countDown();
-            }
-        });
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "POST", "/");
-        ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(true);
-        client.syn(new SynInfo(headers, false), null);
-        assertThat("goAway has been received by proxy", goAwayLatch.await(2 * timeout, TimeUnit.MILLISECONDS),
-                is(true));
-    }
-
-    @Test
-    public void testServerTimeout() throws Exception
-    {
-        final int timeout = 1000;
-        final String header = "foo";
-
-        InetSocketAddress proxyAddress = startProxy(startServer(new DefaultHandler()
-        {
-            @Override
-            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
-            {
-                try
-                {
-                    Thread.sleep(2 * timeout);
-                }
-                catch (InterruptedException e)
-                {
-                    e.printStackTrace();
-                }
-            }
-        }), 30000, timeout);
-
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, null);
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "POST", "/");
-        headers.put(header, "bar");
-        headers.put("connection", "close");
-
-        client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields headers = replyInfo.getHeaders();
-                assertThat("status is 504", headers.get(HTTPSPDYHeader.STATUS.name(version)).getValue(), is("504"));
-                replyLatch.countDown();
-            }
-
-        });
-
-        assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testPING() throws Exception
-    {
-        // PING is per hop, and it does not carry the information to which server to ping to
-        // We just verify that it works
-
-        InetSocketAddress proxyAddress = startProxy(startServer(null), 30000, 30000);
-        proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version));
-
-        final CountDownLatch pingLatch = new CountDownLatch(1);
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onPing(Session session, PingResultInfo pingInfo)
-            {
-                pingLatch.countDown();
-            }
-        });
-
-        client.ping(new PingInfo(5, TimeUnit.SECONDS));
-
-        Assert.assertTrue(pingLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    private class TestServerHandler extends DefaultHandler
-    {
-        private final String responseHeader;
-        private final byte[] responseData;
-
-        private TestServerHandler(String responseHeader, byte[] responseData)
-        {
-            this.responseHeader = responseHeader;
-            this.responseData = responseData;
-        }
-
-        @Override
-        public void handle(String target, Request baseRequest, HttpServletRequest request,
-                           HttpServletResponse response) throws IOException, ServletException
-        {
-            assertThat("Via Header is set", baseRequest.getHeader("X-Forwarded-For"), is(notNullValue()));
-            assertThat("X-Forwarded-For Header is set", baseRequest.getHeader("X-Forwarded-For"),
-                    is(notNullValue()));
-            assertThat("X-Forwarded-Host Header is set", baseRequest.getHeader("X-Forwarded-Host"),
-                    is(notNullValue()));
-            assertThat("X-Forwarded-Proto Header is set", baseRequest.getHeader("X-Forwarded-Proto"),
-                    is(notNullValue()));
-            assertThat("X-Forwarded-Server Header is set", baseRequest.getHeader("X-Forwarded-Server"),
-                    is(notNullValue()));
-            baseRequest.setHandled(true);
-            BufferedReader bufferedReader = request.getReader();
-            int read;
-            while ((read = bufferedReader.read()) != -1)
-                response.getOutputStream().write(read);
-
-            // add some hop header to be removed on the proxy
-            response.addHeader("Trailer", "bla");
-            if (responseHeader != null)
-                response.addHeader(responseHeader, "bar");
-            if (responseData != null)
-                response.getOutputStream().write(responseData);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToSPDYLoadTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToSPDYLoadTest.java
deleted file mode 100644
index 75758cf..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToSPDYLoadTest.java
+++ /dev/null
@@ -1,275 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-import java.io.ByteArrayOutputStream;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.SPDYServerConnector;
-import org.eclipse.jetty.spdy.server.http.SPDYTestUtils;
-import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import static junit.framework.Assert.fail;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-@RunWith(value = Parameterized.class)
-public abstract class ProxySPDYToSPDYLoadTest
-{
-    @Parameterized.Parameters
-    public static Collection<Short[]> parameters()
-    {
-        return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3});
-    }
-
-    @Rule
-    public final TestTracker tracker = new TestTracker();
-    private final short version;
-    private static final String UUID_HEADER_NAME = "uuidHeader";
-    private static final String SERVER_ID_HEADER = "serverId";
-    private SPDYClient.Factory factory;
-    private Server server;
-    private Server proxy;
-    private ServerConnector proxyConnector;
-    private SslContextFactory sslContextFactory = SPDYTestUtils.newSslContextFactory();
-
-    public ProxySPDYToSPDYLoadTest(short version)
-    {
-        this.version = version;
-    }
-
-    protected InetSocketAddress startServer(ServerSessionFrameListener listener) throws Exception
-    {
-        server = new Server();
-        SPDYServerConnector serverConnector = new SPDYServerConnector(server, sslContextFactory, listener);
-        serverConnector.addConnectionFactory(new SPDYServerConnectionFactory(version, listener));
-        serverConnector.setPort(0);
-        server.addConnector(serverConnector);
-        server.start();
-        return new InetSocketAddress("localhost", serverConnector.getLocalPort());
-    }
-
-    protected InetSocketAddress startProxy(InetSocketAddress server1, InetSocketAddress server2) throws Exception
-    {
-        proxy = new Server();
-        ProxyEngineSelector proxyEngineSelector = new ProxyEngineSelector();
-
-        SPDYProxyEngine spdyProxyEngine = new SPDYProxyEngine(factory);
-        proxyEngineSelector.putProxyEngine("spdy/" + version, spdyProxyEngine);
-
-        proxyEngineSelector.putProxyServerInfo("localhost", new ProxyEngineSelector.ProxyServerInfo("spdy/" + version,
-                "localhost", server1.getPort()));
-        // server2 will be available at two different ProxyServerInfos with different hosts
-        proxyEngineSelector.putProxyServerInfo("127.0.0.1", new ProxyEngineSelector.ProxyServerInfo("spdy/" + version,
-                "127.0.0.1", server2.getPort()));
-        // ProxyServerInfo is mapped to 127.0.0.2 in the proxyEngineSelector. However to be able to connect the
-        // ProxyServerInfo contains 127.0.0.1 as target host
-        proxyEngineSelector.putProxyServerInfo("127.0.0.2", new ProxyEngineSelector.ProxyServerInfo("spdy/" + version,
-                "127.0.0.1", server2.getPort()));
-
-        proxyConnector = new HTTPSPDYProxyServerConnector(proxy, sslContextFactory, proxyEngineSelector);
-        proxyConnector.setPort(0);
-        proxy.addConnector(proxyConnector);
-        proxy.start();
-        return new InetSocketAddress("localhost", proxyConnector.getLocalPort());
-    }
-
-    @Before
-    public void init() throws Exception
-    {
-        factory = new SPDYClient.Factory(sslContextFactory);
-        factory.start();
-    }
-
-    @After
-    public void destroy() throws Exception
-    {
-        server.stop();
-        server.join();
-        proxy.stop();
-        proxy.join();
-        factory.stop();
-    }
-
-    @Test
-    public void testSimpleLoadTest() throws Exception
-    {
-        String server1String = "server1";
-        String server2String = "server2";
-
-        InetSocketAddress server1 = startServer(new TestServerFrameListener(server1String));
-        InetSocketAddress server2 = startServer(new TestServerFrameListener(server2String));
-        final InetSocketAddress proxyAddress = startProxy(server1, server2);
-
-        final int requestsPerClient = 50;
-
-        ExecutorService executorService = Executors.newFixedThreadPool(3);
-
-        Runnable client1 = createClientRunnable(proxyAddress, requestsPerClient, server1String, "localhost");
-        Runnable client2 = createClientRunnable(proxyAddress, requestsPerClient, server2String, "127.0.0.1");
-        Runnable client3 = createClientRunnable(proxyAddress, requestsPerClient, server2String, "127.0.0.2");
-
-        List<Future> futures = new ArrayList<>();
-
-        futures.add(executorService.submit(client1));
-        futures.add(executorService.submit(client2));
-        futures.add(executorService.submit(client3));
-
-        for (Future future : futures)
-        {
-            future.get(60, TimeUnit.SECONDS);
-        }
-    }
-
-    private Runnable createClientRunnable(final InetSocketAddress proxyAddress, final int requestsPerClient,
-                                          final String serverIdentificationString, final String serverHost)
-    {
-        Runnable client = new Runnable()
-        {
-            @Override
-            public void run()
-            {
-                try
-                {
-                    Session client = factory.newSPDYClient(version).connect(proxyAddress, null);
-                    for (int i = 0; i < requestsPerClient; i++)
-                    {
-                        sendSingleClientRequest(proxyAddress, client, serverIdentificationString, serverHost);
-                    }
-                }
-                catch (InterruptedException | ExecutionException | TimeoutException e)
-                {
-                    fail();
-                    e.printStackTrace();
-                }
-            }
-        };
-        return client;
-    }
-
-    private void sendSingleClientRequest(InetSocketAddress proxyAddress, Session client, final String serverIdentificationString, String serverHost) throws ExecutionException, InterruptedException, TimeoutException
-    {
-        final String uuid = UUID.randomUUID().toString();
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-
-        Fields headers = SPDYTestUtils.createHeaders(serverHost, proxyAddress.getPort(), version, "POST", "/");
-        headers.add(UUID_HEADER_NAME, uuid);
-
-        Stream stream = client.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
-        {
-            private final ByteArrayOutputStream result = new ByteArrayOutputStream();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields headers = replyInfo.getHeaders();
-                assertThat("uuid matches expected uuid", headers.get(UUID_HEADER_NAME).getValue(), is(uuid));
-                assertThat("response comes from the given server", headers.get(SERVER_ID_HEADER).getValue(),
-                        is(serverIdentificationString));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                result.write(dataInfo.asBytes(true), 0, dataInfo.length());
-                if (dataInfo.isClose())
-                {
-                    assertThat("received data matches send data", uuid, is(result.toString()));
-                    dataLatch.countDown();
-                }
-            }
-        });
-
-        stream.data(new StringDataInfo(uuid, true), new Callback.Adapter());
-
-        assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("data has been received", dataLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    private class TestServerFrameListener extends ServerSessionFrameListener.Adapter
-    {
-        private String serverId;
-
-        private TestServerFrameListener(String serverId)
-        {
-            this.serverId = serverId;
-        }
-
-        @Override
-        public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-        {
-            Fields requestHeaders = synInfo.getHeaders();
-            Assert.assertNotNull(requestHeaders.get("via"));
-            Fields.Field uuidHeader = requestHeaders.get(UUID_HEADER_NAME);
-            Assert.assertNotNull(uuidHeader);
-
-            Fields responseHeaders = new Fields();
-            responseHeaders.put(UUID_HEADER_NAME, uuidHeader.getValue());
-            responseHeaders.put(SERVER_ID_HEADER, serverId);
-            stream.reply(new ReplyInfo(responseHeaders, false), new Callback.Adapter());
-            return new StreamFrameListener.Adapter()
-            {
-                @Override
-                public void onData(Stream stream, DataInfo dataInfo)
-                {
-                    stream.data(dataInfo, new Callback.Adapter());
-                }
-            };
-        }
-    }
-
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToSPDYTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToSPDYTest.java
deleted file mode 100644
index ac48daf..0000000
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToSPDYTest.java
+++ /dev/null
@@ -1,553 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-import java.io.ByteArrayOutputStream;
-import java.net.InetSocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.spdy.api.BytesDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.PingInfo;
-import org.eclipse.jetty.spdy.api.PingResultInfo;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
-import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.SPDYServerConnector;
-import org.eclipse.jetty.spdy.server.http.SPDYTestUtils;
-import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertThat;
-
-@RunWith(value = Parameterized.class)
-public abstract class ProxySPDYToSPDYTest
-{
-    @Parameterized.Parameters
-    public static Collection<Short[]> parameters()
-    {
-        return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3});
-    }
-
-    @Rule
-    public final TestTracker tracker = new TestTracker();
-    private final short version;
-    private SPDYClient.Factory factory;
-    private Server server;
-    private Server proxy;
-    private ServerConnector proxyConnector;
-    private SslContextFactory sslContextFactory = SPDYTestUtils.newSslContextFactory();
-
-    public ProxySPDYToSPDYTest(short version)
-    {
-        this.version = version;
-    }
-
-    protected InetSocketAddress startServer(ServerSessionFrameListener listener) throws Exception
-    {
-        server = new Server();
-        SPDYServerConnector serverConnector = new SPDYServerConnector(server, sslContextFactory, listener);
-        serverConnector.addConnectionFactory(new SPDYServerConnectionFactory(version, listener));
-        serverConnector.setPort(0);
-        server.addConnector(serverConnector);
-        server.start();
-        return new InetSocketAddress("localhost", serverConnector.getLocalPort());
-    }
-
-    protected InetSocketAddress startProxy(InetSocketAddress address) throws Exception
-    {
-        proxy = new Server();
-        ProxyEngineSelector proxyEngineSelector = new ProxyEngineSelector();
-        SPDYProxyEngine spdyProxyEngine = new SPDYProxyEngine(factory);
-        proxyEngineSelector.putProxyEngine("spdy/" + version, spdyProxyEngine);
-        proxyEngineSelector.putProxyServerInfo("localhost", new ProxyEngineSelector.ProxyServerInfo("spdy/" + version, address.getHostName(), address.getPort()));
-        proxyConnector = new HTTPSPDYProxyServerConnector(proxy, sslContextFactory, proxyEngineSelector);
-        proxyConnector.setPort(0);
-        proxy.addConnector(proxyConnector);
-        proxy.start();
-        return new InetSocketAddress("localhost", proxyConnector.getLocalPort());
-    }
-
-    @Before
-    public void init() throws Exception
-    {
-        factory = new SPDYClient.Factory(sslContextFactory);
-        factory.start();
-    }
-
-    @After
-    public void destroy() throws Exception
-    {
-        if (server != null)
-        {
-            server.stop();
-            server.join();
-        }
-        if (proxy != null)
-        {
-            proxy.stop();
-            proxy.join();
-        }
-        factory.stop();
-    }
-
-    @Test
-    public void testSYNThenREPLY() throws Exception
-    {
-        final String header = "foo";
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Fields requestHeaders = synInfo.getHeaders();
-                Assert.assertNotNull(requestHeaders.get("via"));
-                Assert.assertNotNull(requestHeaders.get(header));
-
-                Fields responseHeaders = new Fields();
-                responseHeaders.put(header, "baz");
-                stream.reply(new ReplyInfo(responseHeaders, true), new Callback.Adapter());
-                return null;
-            }
-        }));
-        proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version));
-
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, null);
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "GET", "/");
-        headers.put(header, "bar");
-        client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields headers = replyInfo.getHeaders();
-                Assert.assertNotNull(headers.get(header));
-                replyLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-
-        client.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-    }
-@Test
-    public void testSYNThenRSTFromUpstreamServer() throws Exception
-    {
-        final String header = "foo";
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Fields requestHeaders = synInfo.getHeaders();
-                Assert.assertNotNull(requestHeaders.get("via"));
-                Assert.assertNotNull(requestHeaders.get(header));
-                stream.getSession().rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM), new Callback.Adapter());
-                return null;
-            }
-        }));
-        proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version));
-
-        final CountDownLatch resetLatch = new CountDownLatch(1);
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onRst(Session session, RstInfo rstInfo)
-            {
-                resetLatch.countDown();
-            }
-        });
-
-        Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "GET", "/");
-        headers.put(header, "bar");
-        client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter());
-
-        assertThat("reset is received by client", resetLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testSYNThenREPLYAndDATA() throws Exception
-    {
-        final byte[] data = "0123456789ABCDEF".getBytes(StandardCharsets.UTF_8);
-        final String header = "foo";
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Fields requestHeaders = synInfo.getHeaders();
-                Assert.assertNotNull(requestHeaders.get("via"));
-                Assert.assertNotNull(requestHeaders.get(header));
-
-                Fields responseHeaders = new Fields();
-                responseHeaders.put(header, "baz");
-                stream.reply(new ReplyInfo(responseHeaders, false), new Callback.Adapter());
-                stream.data(new BytesDataInfo(data, true), new Callback.Adapter());
-                return null;
-            }
-        }));
-        proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version));
-
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, null);
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        Fields headers = new Fields();
-        headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + proxyAddress.getPort());
-        headers.put(header, "bar");
-        client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            private final ByteArrayOutputStream result = new ByteArrayOutputStream();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields headers = replyInfo.getHeaders();
-                Assert.assertNotNull(headers.get(header));
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                result.write(dataInfo.asBytes(true), 0, dataInfo.length());
-                if (dataInfo.isClose())
-                {
-                    Assert.assertArrayEquals(data, result.toByteArray());
-                    dataLatch.countDown();
-                }
-            }
-        });
-
-        Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-
-        client.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testSYNThenSPDYPushIsReceived() throws Exception
-    {
-        final byte[] data = "0123456789ABCDEF".getBytes(StandardCharsets.UTF_8);
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Fields responseHeaders = new Fields();
-                responseHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-                responseHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200 OK");
-                stream.reply(new ReplyInfo(responseHeaders, false), new Callback.Adapter());
-
-                Fields pushHeaders = new Fields();
-                pushHeaders.put(HTTPSPDYHeader.URI.name(version), "/push");
-                stream.push(new PushInfo(5, TimeUnit.SECONDS, pushHeaders, false), new Promise.Adapter<Stream>()
-                {
-                    @Override
-                    public void succeeded(Stream pushStream)
-                    {
-                        pushStream.data(new BytesDataInfo(data, true), new Callback.Adapter());
-                    }
-                });
-
-                stream.data(new BytesDataInfo(data, true), new Callback.Adapter());
-
-                return null;
-            }
-        }));
-        proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version));
-
-        final CountDownLatch pushSynLatch = new CountDownLatch(1);
-        final CountDownLatch pushDataLatch = new CountDownLatch(1);
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, null);
-
-        Fields headers = new Fields();
-        headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + proxyAddress.getPort());
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                pushSynLatch.countDown();
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataInfo.consume(dataInfo.length());
-                        if (dataInfo.isClose())
-                            pushDataLatch.countDown();
-                    }
-                };
-            }
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    dataLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(pushSynLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-
-        client.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testSYNThenSPDYNestedPushIsReceived() throws Exception
-    {
-        final byte[] data = "0123456789ABCDEF".getBytes(StandardCharsets.UTF_8);
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Fields responseHeaders = new Fields();
-                responseHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
-                responseHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200 OK");
-                stream.reply(new ReplyInfo(responseHeaders, false), new Callback.Adapter());
-
-                final Fields pushHeaders = new Fields();
-                pushHeaders.put(HTTPSPDYHeader.URI.name(version), "/push");
-                stream.push(new PushInfo(5, TimeUnit.SECONDS, pushHeaders, false), new Promise.Adapter<Stream>()
-                {
-                    @Override
-                    public void succeeded(Stream pushStream)
-                    {
-                        pushHeaders.put(HTTPSPDYHeader.URI.name(version), "/nestedpush");
-                        pushStream.push(new PushInfo(5, TimeUnit.SECONDS, pushHeaders, false), new Adapter<Stream>()
-                        {
-                            @Override
-                            public void succeeded(Stream pushStream)
-                            {
-                                pushHeaders.put(HTTPSPDYHeader.URI.name(version), "/anothernestedpush");
-                                pushStream.push(new PushInfo(5, TimeUnit.SECONDS, pushHeaders, false), new Adapter<Stream>()
-                                {
-                                    @Override
-                                    public void succeeded(Stream pushStream)
-                                    {
-                                        pushStream.data(new BytesDataInfo(data, true), new Callback.Adapter());
-                                    }
-                                });
-                                pushStream.data(new BytesDataInfo(data, true), new Callback.Adapter());
-                            }
-                        });
-                        pushStream.data(new BytesDataInfo(data, true), new Callback.Adapter());
-                    }
-                });
-
-                stream.data(new BytesDataInfo(data, true), new Callback.Adapter());
-
-                return null;
-            }
-        }));
-        proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version));
-
-        final CountDownLatch pushSynLatch = new CountDownLatch(3);
-        final CountDownLatch pushDataLatch = new CountDownLatch(3);
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, null);
-
-        Fields headers = new Fields();
-        headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + proxyAddress.getPort());
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
-        {
-            // onPush for 1st push stream
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                pushSynLatch.countDown();
-                return new StreamFrameListener.Adapter()
-                {
-                    // onPush for 2nd nested push stream
-                    @Override
-                    public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-                    {
-                        pushSynLatch.countDown();
-                        return new Adapter()
-                        {
-                            // onPush for 3rd nested push stream
-                            @Override
-                            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-                            {
-                                pushSynLatch.countDown();
-                                return new Adapter()
-                                {
-                                    @Override
-                                    public void onData(Stream stream, DataInfo dataInfo)
-                                    {
-                                        dataInfo.consume(dataInfo.length());
-                                        if (dataInfo.isClose())
-                                            pushDataLatch.countDown();
-                                    }
-                                };
-                            }
-
-                            @Override
-                            public void onData(Stream stream, DataInfo dataInfo)
-                            {
-                                dataInfo.consume(dataInfo.length());
-                                if (dataInfo.isClose())
-                                    pushDataLatch.countDown();
-                            }
-                        };
-                    }
-
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataInfo.consume(dataInfo.length());
-                        if (dataInfo.isClose())
-                            pushDataLatch.countDown();
-                    }
-                };
-            }
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    dataLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(pushSynLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-
-        client.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testPING() throws Exception
-    {
-        // PING is per hop, and it does not carry the information to which server to ping to
-        // We just verify that it works
-
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()));
-        proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version));
-
-        final CountDownLatch pingLatch = new CountDownLatch(1);
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onPing(Session session, PingResultInfo pingInfo)
-            {
-                pingLatch.countDown();
-            }
-        });
-
-        client.ping(new PingInfo(5, TimeUnit.SECONDS));
-
-        Assert.assertTrue(pingLatch.await(5, TimeUnit.SECONDS));
-
-        client.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testSYNThenReset() throws Exception
-    {
-        InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Assert.assertTrue(synInfo.isClose());
-                Fields requestHeaders = synInfo.getHeaders();
-                Assert.assertNotNull(requestHeaders.get("via"));
-
-                stream.getSession().rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM), new Callback.Adapter());
-
-                return null;
-            }
-        }));
-        proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version));
-
-        final CountDownLatch resetLatch = new CountDownLatch(1);
-        Session client = factory.newSPDYClient(version).connect(proxyAddress, new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onRst(Session session, RstInfo rstInfo)
-            {
-                resetLatch.countDown();
-            }
-        });
-
-        Fields headers = new Fields();
-        headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + proxyAddress.getPort());
-        client.syn(new SynInfo(headers, true), null);
-
-        Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
-
-        client.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-    }
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/resources/big_script.js b/jetty-spdy/spdy-http-server/src/test/resources/big_script.js
deleted file mode 100644
index 37202fd..0000000
--- a/jetty-spdy/spdy-http-server/src/test/resources/big_script.js
+++ /dev/null
@@ -1,791 +0,0 @@
-//----------------------------------------------------------------------
-//
-// Silly / Pointless Javascript to test GZIP compression.
-//
-//----------------------------------------------------------------------
-
-var LOGO = {
-  dat: [
-    0x50, 0x89, 0x47, 0x4e, 0x0a, 0x0d, 0x0a, 0x1a, 0x00, 0x00, 0x0d, 0x00, 0x48, 0x49, 0x52, 0x44,
-    0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x78, 0x00, 0x06, 0x08, 0x00, 0x00, 0x2a, 0x00, 0x21, 0x96,
-    0x00, 0x0f, 0x00, 0x00, 0x73, 0x04, 0x49, 0x42, 0x08, 0x54, 0x08, 0x08, 0x7c, 0x08, 0x64, 0x08,
-    0x00, 0x88, 0x00, 0x00, 0x70, 0x09, 0x59, 0x48, 0x00, 0x73, 0x04, 0x00, 0x00, 0x27, 0x04, 0x00,
-    0x01, 0x27, 0x4f, 0xd9, 0x80, 0x1d, 0x00, 0x00, 0x19, 0x00, 0x45, 0x74, 0x74, 0x58, 0x6f, 0x53,
-    0x74, 0x66, 0x61, 0x77, 0x65, 0x72, 0x77, 0x00, 0x77, 0x77, 0x69, 0x2e, 0x6b, 0x6e, 0x63, 0x73,
-    0x70, 0x61, 0x2e, 0x65, 0x72, 0x6f, 0x9b, 0x67, 0x3c, 0xee, 0x00, 0x1a, 0x20, 0x00, 0x49, 0x00,
-    0x41, 0x44, 0x78, 0x54, 0xed, 0x9c, 0x79, 0x9d, 0x1c, 0xd8, 0x95, 0x55, 0x3f, 0xff, 0xfb, 0xa7,
-    0xb2, 0xdd, 0x24, 0xef, 0x24, 0x81, 0x81, 0x2c, 0x20, 0x40, 0xd5, 0x91, 0x8b, 0xb0, 0x3f, 0xbb,
-    0x1d, 0x04, 0x54, 0x1c, 0x46, 0x74, 0x17, 0x18, 0xd1, 0x98, 0x19, 0xd1, 0x05, 0x11, 0x37, 0xf4,
-    0xe2, 0x8f, 0xcc, 0xb8, 0x28, 0x8c, 0x82, 0x02, 0x22, 0x8c, 0xe0, 0xe8, 0xe2, 0x86, 0x02, 0x88,
-    0x8e, 0xa2, 0x08, 0xe8, 0x42, 0x42, 0x4b, 0x08, 0xc2, 0xc8, 0x12, 0x12, 0xf6, 0x42, 0x7d, 0x90,
-    0xf7, 0x7d, 0x3e, 0xee, 0x47, 0xf3, 0x77, 0x75, 0xeb, 0x6a, 0xdf, 0x7e, 0xee, 0xea, 0xab, 0x7b,
-    0xdc, 0x93, 0xf3, 0xcf, 0xd0, 0xf0, 0xa9, 0x55, 0x53, 0xae, 0x6f, 0x5d, 0x3d, 0xd5, 0x9e, 0xf7,
-    0xee, 0x7b, 0x8a, 0xf9, 0xe2, 0xaa, 0x38, 0x70, 0x0e, 0x1c, 0xc3, 0x87, 0x99, 0x2e, 0x2f, 0xb4,
-    0xe1, 0xc0, 0x38, 0x70, 0x8e, 0x1c, 0x11, 0x83, 0x80, 0xe7, 0x0e, 0x1d, 0xc3, 0x87, 0x48, 0xe1,
-    0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, 0x01, 0x48, 0x80, 0xe7, 0x0e, 0x1d, 0xc3, 0x87,
-    0x48, 0xe1, 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, 0x01, 0x48, 0x80, 0xe7, 0x0e, 0x1d,
-    0xc3, 0x87, 0x48, 0xe1, 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, 0x01, 0x48, 0x80, 0xe7,
-    0x0e, 0x1d, 0xc3, 0x87, 0x48, 0xe1, 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, 0x01, 0x48,
-    0x80, 0xe7, 0x0e, 0x1d, 0xc3, 0x87, 0x48, 0xe1, 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3,
-    0x01, 0x48, 0x80, 0xe7, 0x0e, 0x1d, 0xc3, 0x87, 0x48, 0xe1, 0x96, 0x81, 0x2f, 0xb4, 0x44, 0x40,
-    0x2c, 0x3a, 0xe9, 0x98, 0xa7, 0x55, 0xe1, 0x3a, 0x38, 0x70, 0x8e, 0x1c, 0x42, 0x26, 0xf0, 0xd2,
-    0x22, 0x4b, 0x16, 0x32, 0x0c, 0xb8, 0x02, 0xb8, 0xde, 0x38, 0xc9, 0x82, 0xe0, 0x4e, 0x80, 0xbf,
-    0xa9, 0x4f, 0xbf, 0x6a, 0x7b, 0x05, 0x88, 0xb1, 0x6c, 0xc8, 0xc3, 0xe0, 0xfb, 0xc0, 0xd1, 0x81,
-    0xcd, 0x86, 0x81, 0xe5, 0xc0, 0xe5, 0xab, 0xdf, 0x02, 0xea, 0xb6, 0xc3, 0x0e, 0x1c, 0xa3, 0x87,
-    0x44, 0x6e, 0x12, 0x64, 0x29, 0x70, 0x41, 0xf0, 0x7a, 0x60, 0xaf, 0xc2, 0x01, 0x77, 0x80, 0xf3,
-    0x55, 0x9b, 0x21, 0xf5, 0xf6, 0x0b, 0x81, 0xba, 0x81, 0xc7, 0x54, 0x5b, 0x77, 0xf5, 0xbf, 0x09,
-    0xd9, 0xeb, 0xed, 0xb7, 0x45, 0x80, 0x0b, 0x24, 0x04, 0x2c, 0x5b, 0x66, 0xec, 0x35, 0x28, 0xf1,
-    0xd7, 0xf0, 0xba, 0xaa, 0xb6, 0xd5, 0x11, 0x61, 0x23, 0x79, 0x27, 0xf0, 0x76, 0xdb, 0x5e, 0x81,
-    0x27, 0x3c, 0xc3, 0xfc, 0x6c, 0x14, 0x1c, 0x3b, 0xc7, 0x0e, 0x10, 0xa0, 0x23, 0x91, 0x67, 0x81,
-    0x91, 0x81, 0x9e, 0x75, 0x13, 0xaa, 0x4b, 0x38, 0x17, 0x55, 0xb2, 0x5b, 0x05, 0xd7, 0xa3, 0x9c,
-    0x0b, 0xaa, 0x7e, 0x93, 0x8d, 0x31, 0xe0, 0x39, 0x48, 0x2b, 0xf9, 0xc7, 0x9c, 0x02, 0xbc, 0x0b,
-    0xb6, 0xdb, 0x62, 0xd1, 0xe3, 0xa7, 0xdb, 0x66, 0x8b, 0x76, 0x03, 0xb4, 0xa5, 0x37, 0xdb, 0x64,
-    0x70, 0xe1, 0x06, 0x38, 0x2d, 0xcb, 0xef, 0xd4, 0x01, 0x0c, 0x01, 0x86, 0x8b, 0xf7, 0xab, 0x48,
-    0x7b, 0x25, 0x81, 0x43, 0x0d, 0x5f, 0x5e, 0xc2, 0x34, 0x84, 0x80, 0xe6, 0x50, 0x3f, 0x20, 0xfa,
-    0x98, 0x19, 0xfa, 0xfd, 0xd7, 0xc4, 0x0c, 0x9c, 0x96, 0x55, 0x92, 0x3c, 0x0b, 0x43, 0x3d, 0xe5,
-    0xcc, 0x33, 0x8c, 0x1a, 0x0e, 0x65, 0xab, 0x30, 0x31, 0xb4, 0x4d, 0xb9, 0xd6, 0x98, 0xb6, 0x6e,
-    0xf3, 0xef, 0x9f, 0x6a, 0xba, 0xb2, 0xfc, 0xb7, 0xc7, 0xa3, 0xc8, 0x88, 0x55, 0x04, 0x62, 0xdd,
-    0xa8, 0xd4, 0xe1, 0xc3, 0xd4, 0x70, 0x71, 0x40, 0xee, 0x7a, 0xd2, 0x82, 0xb0, 0xf6, 0x30, 0x8c,
-    0x58, 0x6b, 0x36, 0xb2, 0x55, 0x72, 0x81, 0x4f, 0xfd, 0x4d, 0x88, 0xe5, 0x34, 0xee, 0x69, 0xbc,
-    0x63, 0x38, 0xd6, 0xf6, 0x16, 0xf4, 0xd8, 0xd8, 0xb6, 0x57, 0x38, 0x77, 0xa8, 0x50, 0x78, 0x72,
-    0x56, 0x2c, 0xb0, 0x1d, 0xb4, 0x88, 0xa7, 0x03, 0xb6, 0x95, 0x1e, 0xa7, 0xe5, 0x97, 0x9f, 0x9a,
-    0x33, 0x0f, 0x73, 0x6a, 0xba, 0xdb, 0x9f, 0x02, 0x79, 0x3c, 0x7f, 0xb7, 0x34, 0xd7, 0x06, 0xa3,
-    0x39, 0xe3, 0xbf, 0xdb, 0xdd, 0x71, 0x76, 0x94, 0x9f, 0x2e, 0x66, 0xd8, 0xe0, 0xd4, 0xaf, 0x95,
-    0x70, 0xf4, 0xab, 0xeb, 0xfe, 0x7d, 0x87, 0x5d, 0xce, 0x03, 0x3b, 0x01, 0x8e, 0x1c, 0xe4, 0x66,
-    0xff, 0x5a, 0xa7, 0xc6, 0x6d, 0x0e, 0x8b, 0xe3, 0xdb, 0x53, 0xfd, 0x07, 0x02, 0xe5, 0xfc, 0x70,
-    0xbd, 0xc2, 0x07, 0x7e, 0x53, 0xbc, 0xab, 0x55, 0xc4, 0x39, 0xea, 0x6b, 0xa7, 0xb1, 0x89, 0xc0,
-    0xf6, 0x8b, 0x1d, 0xfa, 0xa7, 0x70, 0x56, 0xaa, 0xf8, 0x74, 0xb0, 0x95, 0x82, 0x1d, 0x15, 0x3e,
-    0x24, 0x2f, 0xc0, 0x0a, 0x73, 0x31, 0xfb, 0xcc, 0x65, 0xff, 0xe4, 0x4f, 0xbb, 0xc2, 0x1a, 0x96,
-    0x1a, 0x37, 0xe0, 0x25, 0xcf, 0x80, 0x69, 0x1a, 0x77, 0xfe, 0xdd, 0xcf, 0x78, 0x13, 0xf2, 0x16,
-    0x32, 0xc0, 0x46, 0xe3, 0x0e, 0x1d, 0x23, 0x87, 0x22, 0x21, 0x38, 0x72, 0x49, 0x70, 0x7b, 0x69,
-    0x46, 0x68, 0xc4, 0xf8, 0x64, 0xe4, 0x94, 0x03, 0xb7, 0x7b, 0xb3, 0xf5, 0x27, 0xa2, 0x6f, 0xe0,
-    0xb6, 0x2b, 0x45, 0x77, 0xef, 0x7b, 0xc7, 0xab, 0x83, 0xde, 0x72, 0x3b, 0xdf, 0x3c, 0xb0, 0x15,
-    0x3c, 0xb7, 0x09, 0xd1, 0xd8, 0x8a, 0xc0, 0x76, 0x47, 0x01, 0x63, 0x34, 0xd6, 0x4e, 0xc1, 0xb8,
-    0x16, 0x97, 0x3a, 0x44, 0x8f, 0x25, 0x37, 0x19, 0xe5, 0x1a, 0xd2, 0xac, 0xb1, 0x87, 0xc2, 0x2d,
-    0x21, 0xcc, 0x6f, 0x66, 0xde, 0xfb, 0xb2, 0xbc, 0x2b, 0xb8, 0xbb, 0xf0, 0xa8, 0x97, 0x1e, 0xea,
-    0x46, 0xa3, 0x0e, 0x1d, 0xa3, 0x87, 0x3e, 0x36, 0x2f, 0x8d, 0xfb, 0x1a, 0x43, 0xa1, 0x19, 0x5a,
-    0x22, 0xdf, 0x4e, 0x89, 0xfd, 0x70, 0xbe, 0xfa, 0xae, 0xf0, 0xcd, 0x1b, 0xeb, 0xda, 0xef, 0x0d,
-    0x66, 0xfa, 0x13, 0xa2, 0xb1, 0x14, 0x07, 0x3d, 0x74, 0x1c, 0xa7, 0xc0, 0x37, 0x9b, 0xd2, 0xff,
-    0xc0, 0xfc, 0x38, 0x08, 0xcc, 0x0f, 0x6e, 0x37, 0x87, 0xd4, 0x1c, 0x88, 0x1c, 0x03, 0xda, 0x52,
-    0x63, 0x3e, 0x96, 0x44, 0x7f, 0x64, 0xe4, 0xea, 0xd8, 0x2c, 0x27, 0x9b, 0x4c, 0x1f, 0x9f, 0x6e,
-    0xd8, 0x6b, 0x01, 0xe4, 0x88, 0x83, 0x0d, 0x1c, 0x0e, 0x5c, 0x07, 0x9c, 0xff, 0x46, 0x0a, 0x54,
-    0xc2, 0x6c, 0x32, 0x5b, 0x67, 0xf1, 0x46, 0x53, 0x64, 0x44, 0x5e, 0x08, 0xe1, 0xe2, 0x62, 0xdf,
-    0xe9, 0x7e, 0x37, 0x5b, 0x08, 0xf0, 0x15, 0xf0, 0x8d, 0x55, 0x9e, 0x84, 0x8e, 0x1c, 0x22, 0x30,
-    0x1e, 0x32, 0x48, 0xf8, 0xbb, 0x69, 0xe0, 0x45, 0x43, 0xaa, 0x8d, 0x93, 0xff, 0x46, 0x57, 0x77,
-    0x67, 0x8e, 0x03, 0x3a, 0x8e, 0x03, 0xc0, 0x15, 0x9b, 0x7f, 0x37, 0xb2, 0x4f, 0x77, 0x79, 0x9e,
-    0x08, 0xc1, 0x1a, 0xe3, 0xee, 0xe0, 0x27, 0x44, 0xd9, 0x29, 0xe5, 0xaf, 0x75, 0x4b, 0x1e, 0x50,
-    0x8e, 0x09, 0x98, 0x9e, 0xc2, 0x61, 0xb3, 0x34, 0xc1, 0x23, 0xdd, 0xae, 0xda, 0xca, 0x03, 0x17,
-    0x6a, 0x37, 0xaa, 0x91, 0x35, 0xee, 0x34, 0x6a, 0x30, 0x4a, 0x3c, 0xfc, 0xfc, 0xc2, 0x3f, 0xa8,
-    0x7e, 0x14, 0xe7, 0x06, 0x07, 0x80, 0x88, 0x85, 0x1b, 0xfc, 0x59, 0xf0, 0x3a, 0xcc, 0x30, 0xde,
-    0x88, 0x17, 0xa7, 0xc8, 0xf5, 0x55, 0x8d, 0x5b, 0xb1, 0x3e, 0xcc, 0x88, 0x8b, 0xc2, 0x8c, 0xf8,
-    0xf4, 0x6a, 0xab, 0xb9, 0x7a, 0xf0, 0xf5, 0xe0, 0xf2, 0x22, 0x55, 0x5e, 0x6c, 0xdd, 0xae, 0xd1,
-    0xff, 0x63, 0x9f, 0xe4, 0xb2, 0xf0, 0x01, 0x88, 0xef, 0x78, 0x56, 0xb8, 0x4f, 0x0e, 0xa0, 0x98,
-    0xb5, 0xfa, 0xe8, 0xe8, 0x1b, 0xf7, 0xe6, 0x55, 0xeb, 0x7f, 0x17, 0xb6, 0xfa, 0x37, 0xb5, 0xad,
-    0x69, 0xc3, 0x04, 0x2d, 0x22, 0x2d, 0x80, 0x33, 0xa5, 0x09, 0x1b, 0x6d, 0xe7, 0xe1, 0x4f, 0x15,
-    0xb2, 0x05, 0x21, 0x9f, 0x47, 0x1d, 0x70, 0x14, 0x67, 0xc0, 0x30, 0x8f, 0xe7, 0xdf, 0xe7, 0x99,
-    0x70, 0x1c, 0x44, 0x62, 0x38, 0xe4, 0x6a, 0xe0, 0x3a, 0xec, 0xf0, 0x5f, 0xc1, 0x3a, 0x8b, 0x37,
-    0x39, 0xc8, 0xce, 0x06, 0x13, 0x7d, 0x9d, 0x76, 0x89, 0x6f, 0x80, 0xf3, 0x52, 0xdb, 0xeb, 0xb0,
-    0x8f, 0xd8, 0x91, 0x10, 0xc0, 0x61, 0xfc, 0x27, 0xae, 0xfb, 0x6c, 0x39, 0x89, 0xf0, 0x00, 0x50,
-    0x74, 0xcf, 0xf9, 0xe6, 0xae, 0xd3, 0xef, 0x80, 0x04, 0xdb, 0x65, 0xdc, 0xde, 0xca, 0x5d, 0x73,
-    0x7e, 0x05, 0x23, 0xbb, 0x6f, 0x60, 0x70, 0x1b, 0xa2, 0x47, 0xf8, 0x93, 0x39, 0xb0, 0xb6, 0x02,
-    0x7e, 0x1f, 0xa2, 0x7e, 0xe6, 0x29, 0xcb, 0x7f, 0xbb, 0xbf, 0xe0, 0x55, 0xe1, 0xb4, 0x3b, 0x66,
-    0x05, 0x1e, 0x7e, 0x60, 0x01, 0xd0, 0x53, 0xaf, 0x35, 0xd5, 0x8d, 0x46, 0x18, 0x1e, 0x13, 0xfc,
-    0xf7, 0xbe, 0x3d, 0xa1, 0x3e, 0x63, 0xdc, 0xfe, 0xec, 0x1b, 0xce, 0x1c, 0x81, 0xa4, 0x67, 0xcf,
-    0x99, 0x71, 0x9b, 0xc5, 0xdb, 0x4a, 0xf1, 0x59, 0x3f, 0x9e, 0xf4, 0x93, 0x02, 0x15, 0x30, 0xeb,
-    0x9a, 0x66, 0xe6, 0xb5, 0x00, 0x38, 0x02, 0xb8, 0x5b, 0x18, 0x38, 0xda, 0x4c, 0x7f, 0xb3, 0x0b,
-    0x26, 0x86, 0x8d, 0x1d, 0x46, 0x46, 0x37, 0xbf, 0x6a, 0xa9, 0x85, 0x4f, 0x2f, 0xc3, 0xd7, 0xaf,
-    0xd7, 0xde, 0xf4, 0x37, 0x2a, 0x12, 0x75, 0x5d, 0xaa, 0xab, 0x26, 0x76, 0x89, 0x3a, 0xf4, 0x8f,
-    0x70, 0x1c, 0x19, 0x3a, 0x56, 0xd0, 0x82, 0x47, 0x32, 0x22, 0x38, 0x0e, 0xb4, 0xae, 0xca, 0x7d,
-    0x2c, 0xf0, 0xf3, 0x86, 0x58, 0xaf, 0x99, 0xd2, 0x4f, 0x67, 0x02, 0x70, 0x8d, 0xd8, 0x07, 0x7e,
-    0x79, 0x47, 0xda, 0x04, 0x81, 0x9e, 0xaf, 0xed, 0x89, 0x1d, 0xa5, 0xc9, 0x8b, 0xda, 0xf3, 0x3b,
-    0xb2, 0x9c, 0xf0, 0x38, 0x1e, 0xde, 0x60, 0xd3, 0x81, 0x13, 0xf5, 0x11, 0x87, 0xf5, 0xf0, 0x77,
-    0x84, 0xc9, 0x9e, 0x99, 0x79, 0x49, 0x73, 0xd3, 0x3c, 0x5d, 0xbb, 0xb2, 0xce, 0xfc, 0x17, 0x4d,
-    0x71, 0x11, 0xd8, 0x35, 0x71, 0x1d, 0x8b, 0x14, 0x5c, 0x56, 0xdf, 0xe5, 0xed, 0x77, 0xc9, 0xa1,
-    0xa3, 0x46, 0x7a, 0x2b, 0xdc, 0x0a, 0x2d, 0xbb, 0x59, 0x50, 0x37, 0x78, 0x9f, 0xf0, 0xec, 0x55,
-    0xba, 0x7d, 0x7b, 0x1e, 0x7a, 0x6b, 0xfc, 0x0b, 0x6b, 0x6c, 0x5e, 0xc0, 0x5e, 0x17, 0x8f, 0x31,
-    0xb1, 0x9a, 0x05, 0x99, 0x76, 0x5d, 0x6d, 0xc0, 0x84, 0x43, 0x67, 0xc3, 0x1d, 0x9b, 0xe6, 0x09,
-    0xe1, 0xfb, 0x67, 0xe5, 0x23, 0x02, 0x55, 0xc1, 0xba, 0xaa, 0xa8, 0xde, 0x28, 0xd1, 0xe2, 0x67,
-    0x48, 0x1b, 0x9d, 0x9d, 0xfc, 0xce, 0x3c, 0xef, 0x46, 0xe3, 0x73, 0xf7, 0x64, 0x44, 0xbe, 0x14,
-    0x29, 0x42, 0x0c, 0xa7, 0x1a, 0xcb, 0xbe, 0x75, 0xfb, 0x10, 0x6a, 0x77, 0xb3, 0xf4, 0x70, 0x19,
-    0xa6, 0xc0, 0xbd, 0x9f, 0xc2, 0x9c, 0x7b, 0x93, 0xbf, 0x03, 0xa3, 0x69, 0xbe, 0x73, 0x2a, 0x8e,
-    0x0d, 0xfc, 0xb5, 0x30, 0x71, 0xb4, 0x88, 0xc6, 0xe7, 0x2c, 0x4c, 0x8c, 0x8c, 0xf6, 0x73, 0x7e,
-    0x5f, 0x43, 0xf2, 0xb8, 0x77, 0xc5, 0x75, 0x55, 0x85, 0x57, 0xdf, 0xc3, 0xaf, 0x5f, 0x1b, 0xbd,
-    0xf6, 0x37, 0x0b, 0x91, 0xd1, 0x3e, 0xa8, 0x77, 0xb6, 0xea, 0x27, 0x44, 0x61, 0x09, 0x01, 0xc5,
-    0x47, 0x17, 0xc7, 0x82, 0xb6, 0x96, 0x9f, 0xa7, 0x25, 0x92, 0x78, 0x6b, 0xbc, 0x00, 0x49, 0x6a,
-    0xbe, 0x9e, 0xc0, 0xee, 0x4a, 0xdd, 0xfc, 0x23, 0x68, 0x1c, 0xcf, 0x04, 0xf3, 0x72, 0xcd, 0xbf,
-    0x59, 0xca, 0xde, 0xf8, 0x1b, 0x05, 0x25, 0x1d, 0x7f, 0x0b, 0xf2, 0xa7, 0x37, 0xb3, 0x23, 0x34,
-    0x34, 0x9c, 0xec, 0xcc, 0x7a, 0x6f, 0x33, 0xb6, 0x57, 0x4f, 0x45, 0xc1, 0x1c, 0x5e, 0x23, 0x03,
-    0x42, 0x22, 0x10, 0xa8, 0xb5, 0xc6, 0xe7, 0x09, 0x5f, 0x62, 0x57, 0xed, 0xda, 0xee, 0x08, 0x12,
-    0xf7, 0x3f, 0x61, 0x52, 0x51, 0xe9, 0xec, 0x23, 0xcb, 0x6d, 0x77, 0x29, 0x0a, 0x6e, 0x7e, 0x8c,
-    0xc0, 0x73, 0x89, 0x0d, 0x12, 0x4e, 0xad, 0x83, 0xf0, 0x11, 0xf8, 0x59, 0x82, 0x46, 0xc2, 0x36,
-    0x2f, 0xcf, 0x48, 0x2d, 0xfc, 0x37, 0x24, 0x5c, 0x76, 0x10, 0xd3, 0x3f, 0xc3, 0x7e, 0x89, 0xde,
-    0x6c, 0x57, 0x51, 0xdc, 0x50, 0x9d, 0xba, 0xa6, 0x8e, 0xf5, 0x17, 0x52, 0xfc, 0x0d, 0x89, 0x3b,
-    0x14, 0xc8, 0x15, 0x7c, 0x5e, 0xdf, 0x3c, 0x3b, 0xc2, 0x4b, 0x8e, 0x65, 0x71, 0x89, 0xf7, 0x99,
-    0x04, 0x8e, 0x03, 0xbc, 0x0a, 0x9f, 0xf1, 0xde, 0x32, 0x43, 0x38, 0x5c, 0xd9, 0x26, 0x77, 0x3b,
-    0xc6, 0xcb, 0x70, 0xbe, 0xc5, 0x0e, 0x54, 0x8f, 0x63, 0x75, 0xc3, 0x85, 0x04, 0x2f, 0xa8, 0x4e,
-    0xde, 0xc7, 0x37, 0xb7, 0x85, 0xf6, 0x98, 0x3b, 0x37, 0x77, 0xb7, 0x22, 0x96, 0xc6, 0x0e, 0x03,
-    0x60, 0x25, 0x28, 0x59, 0x11, 0xc0, 0xff, 0x9d, 0x3a, 0xb5, 0x16, 0x02, 0x36, 0x91, 0x65, 0x7c,
-    0x8f, 0x37, 0xc8, 0xee, 0xd1, 0x30, 0x16, 0x70, 0xba, 0xcc, 0x02, 0xbe, 0x82, 0x6b, 0xe5, 0x4f,
-    0xb5, 0x13, 0x47, 0x94, 0x08, 0x3b, 0x44, 0x09, 0x4c, 0x2a, 0x94, 0x77, 0x07, 0xd6, 0x74, 0xeb,
-    0xf7, 0x83, 0x6a, 0x77, 0xba, 0xe4, 0xab, 0x59, 0x67, 0xe1, 0x91, 0x70, 0x47, 0x17, 0x02, 0x22,
-    0x27, 0x65, 0x73, 0x3f, 0x7b, 0x58, 0x84, 0xa2, 0xdd, 0xc7, 0xe5, 0x79, 0xc1, 0x3b, 0xbb, 0x32,
-    0xe0, 0x05, 0xa6, 0xeb, 0x75, 0xec, 0x94, 0x16, 0x07, 0x6f, 0x29, 0xed, 0x7d, 0x70, 0x53, 0x82,
-    0xc4, 0x54, 0x03, 0x96, 0x27, 0x2e, 0x88, 0x98, 0x31, 0xc0, 0xd9, 0xa7, 0x80, 0xc8, 0xb4, 0x0e,
-    0x3b, 0x67, 0xe7, 0x81, 0x1b, 0x8c, 0x32, 0x0d, 0xe8, 0x0d, 0x6d, 0x28, 0x58, 0xd8, 0xeb, 0xff,
-    0xc1, 0x1a, 0x14, 0xe4, 0x33, 0x93, 0x94, 0xe6, 0x67, 0xb6, 0x64, 0x74, 0x60, 0x98, 0xa3, 0xb8,
-    0x6f, 0x34, 0xf1, 0x8f, 0x4c, 0xe8, 0xf4, 0xa8, 0x8d, 0xa9, 0x2f, 0x67, 0x9b, 0xf0, 0x93, 0x76,
-    0xc9, 0x4e, 0x47, 0x57, 0x53, 0x93, 0x6e, 0x5c, 0xae, 0x57, 0x31, 0x07, 0x2d, 0xb5, 0xfb, 0xc3,
-    0x27, 0xc7, 0xe4, 0x13, 0xee, 0xf9, 0x7e, 0xa6, 0x05, 0x76, 0x4d, 0x13, 0xa5, 0x7f, 0x2f, 0xaa,
-    0xb0, 0x55, 0x36, 0x77, 0xf7, 0xbe, 0x16, 0xa1, 0x77, 0x7b, 0xea, 0x6e, 0x5b, 0x67, 0xf2, 0x70,
-    0x41, 0xf7, 0x6d, 0x55, 0xf3, 0xc8, 0x2b, 0x6d, 0xba, 0x0b, 0x54, 0x3c, 0x0c, 0x97, 0xf1, 0x7c,
-    0xb3, 0x7c, 0x30, 0xb5, 0x05, 0x0a, 0xd7, 0x65, 0xc0, 0x96, 0x9b, 0x0d, 0x42, 0x97, 0xc8, 0x42,
-    0xbc, 0x5a, 0x59, 0x9e, 0xe0, 0x06, 0x3b, 0x0f, 0x9c, 0x73, 0xb8, 0xba, 0x6c, 0xe6, 0x41, 0xf5,
-    0xb9, 0xb1, 0xb2, 0x3f, 0x7c, 0xf6, 0x86, 0xc0, 0x58, 0xbd, 0x75, 0x17, 0xe9, 0x22, 0x53, 0x48,
-    0x16, 0x0e, 0xf6, 0x05, 0xad, 0xe0, 0xdb, 0xf1, 0xcb, 0x5e, 0x52, 0xdf, 0x0e, 0x44, 0x5e, 0x25,
-    0x63, 0x88, 0x30, 0x06, 0xb4, 0xa4, 0xc7, 0x61, 0x45, 0x59, 0x4a, 0x3a, 0x45, 0xbb, 0x8c, 0xe4,
-    0x9a, 0x06, 0x43, 0x78, 0x43, 0xe9, 0x38, 0x06, 0xf0, 0xc3, 0xb7, 0xd2, 0x82, 0x57, 0x30, 0xaa,
-    0x55, 0xe0, 0x9a, 0x22, 0x4d, 0xe3, 0xd5, 0xbd, 0x3f, 0x44, 0x52, 0xb0, 0x77, 0x55, 0x48, 0x88,
-    0x38, 0x06, 0x5f, 0x11, 0x79, 0x0d, 0xf4, 0x43, 0x2b, 0x00, 0x16, 0x92, 0x11, 0xea, 0xc3, 0x91,
-    0x25, 0xf0, 0x18, 0xf7, 0x7c, 0xa2, 0xaa, 0x3f, 0x47, 0x5c, 0x70, 0x2b, 0x60, 0x12, 0x01, 0xfa,
-    0x01, 0xee, 0xd7, 0x96, 0xf4, 0xb1, 0xb3, 0xe6, 0x8d, 0xfe, 0x0f, 0x2b, 0x4b, 0x6d, 0x3c, 0xb4,
-    0xb7, 0xae, 0xdf, 0x00, 0x18, 0xda, 0x84, 0x29, 0x35, 0xf1, 0xf6, 0x53, 0x15, 0xfa, 0xdc, 0x6e,
-    0x59, 0x10, 0x54, 0xea, 0x5e, 0xcd, 0x6c, 0x22, 0xe0, 0x39, 0x1f, 0xf2, 0x90, 0xab, 0x0d, 0x87,
-    0xad, 0xcb, 0x45, 0x47, 0x6b, 0xbf, 0x20, 0xdb, 0xdb, 0x5e, 0x03, 0xb7, 0x07, 0x18, 0xd2, 0x5e,
-    0xab, 0xc0, 0x56, 0xfd, 0xf7, 0x7f, 0x40, 0xcb, 0xc4, 0xa4, 0xb1, 0x61, 0xc4, 0xe0, 0xc0, 0x25,
-    0x78, 0x19, 0xf2, 0x21, 0x36, 0xf1, 0xaa, 0x2f, 0xf4, 0x01, 0xc8, 0x8b, 0xc0, 0xab, 0xe0, 0x02,
-    0xc0, 0xd7, 0x55, 0x03, 0x1f, 0x32, 0x91, 0x11, 0x00, 0xb7, 0x0c, 0xff, 0xcf, 0x9c, 0x1a, 0x20,
-    0x1b, 0x27, 0xbf, 0xf3, 0x77, 0x73, 0x9e, 0x47, 0x86, 0x83, 0xb3, 0x84, 0x98, 0xe2, 0x34, 0x39,
-    0x68, 0xc3, 0x13, 0xc3, 0xf3, 0xc0, 0xbf, 0xa2, 0x14, 0xe7, 0xf1, 0xe0, 0x4f, 0x3a, 0x2d, 0xdb,
-    0x77, 0x22, 0xef, 0x00, 0x53, 0xc4, 0xb2, 0xf2, 0x16, 0xc1, 0x79, 0x11, 0xaa, 0x8f, 0x32, 0x3e,
-    0x81, 0xd0, 0xf2, 0x22, 0xe0, 0x0e, 0xc0, 0xe3, 0xb0, 0x5b, 0x79, 0xd3, 0x16, 0xed, 0x2b, 0x91,
-    0xf5, 0x54, 0xa1, 0x27, 0x98, 0xeb, 0x5c, 0x02, 0x5c, 0x09, 0xaf, 0x86, 0x91, 0xd0, 0x36, 0x61,
-    0xc8, 0x89, 0x55, 0xbb, 0xd1, 0x35, 0x5e, 0xb4, 0x32, 0xb1, 0xdb, 0xdb, 0xdb, 0x4b, 0x5b, 0x63,
-    0xcb, 0x84, 0x26, 0x27, 0x8d, 0x1b, 0x68, 0xfe, 0x5f, 0x4b, 0xb3, 0xb8, 0x67, 0xf7, 0x7d, 0x55,
-    0x82, 0xb2, 0x13, 0xbd, 0x49, 0xf0, 0xd6, 0x0e, 0xef, 0x62, 0x5b, 0x67, 0x59, 0xfb, 0x17, 0xdb,
-    0xd8, 0x18, 0x4d, 0xcc, 0xbb, 0xfa, 0x61, 0xab, 0xb5, 0xbc, 0xf6, 0x29, 0xaf, 0x10, 0x6d, 0x34,
-    0x0a, 0xa7, 0x50, 0x7f, 0xfb, 0xd5, 0xb0, 0x53, 0x59, 0xfb, 0xce, 0x8a, 0x25, 0x37, 0xad, 0x3e,
-    0xe5, 0xaa, 0x9b, 0xae, 0x4c, 0x88, 0xbe, 0x04, 0x4f, 0x81, 0x79, 0x8f, 0xa5, 0x3f, 0x6f, 0x15,
-    0x31, 0xc4, 0xb8, 0x15, 0xe8, 0x18, 0x91, 0x13, 0x80, 0x07, 0x54, 0xeb, 0x30, 0x35, 0x20, 0xcd,
-    0x33, 0x22, 0x07, 0x81, 0xdf, 0x8b, 0x14, 0x19, 0xe7, 0xa6, 0x3b, 0x4b, 0xca, 0x0b, 0x2e, 0xa2,
-    0x1d, 0x7b, 0x73, 0xb1, 0x74, 0x6c, 0xa2, 0x28, 0xfa, 0x23, 0x30, 0x46, 0xaf, 0x04, 0x8a, 0x38,
-    0x26, 0x4d, 0x7f, 0x00, 0x91, 0x14, 0x86, 0x0f, 0x9d, 0x1d, 0x11, 0x1f, 0x15, 0x39, 0x0f, 0xb8,
-    0x55, 0xbb, 0x86, 0xd3, 0x3f, 0x00, 0x91, 0x16, 0xaa, 0xe9, 0x25, 0xfa, 0xfe, 0xdf, 0x00, 0x9f,
-    0xb3, 0xaf, 0x1d, 0x78, 0x45, 0xe0, 0xfe, 0xcc, 0x78, 0xb7, 0x7e, 0x9f, 0xe0, 0x9a, 0xd7, 0x7b,
-    0xbb, 0xe2, 0x1f, 0x67, 0xd4, 0x9f, 0xb0, 0xc6, 0xe0, 0xcc, 0x61, 0x6f, 0x6f, 0x01, 0x51, 0xe9,
-    0xff, 0x88, 0xf2, 0xa8, 0x6b, 0x95, 0xb1, 0xea, 0x78, 0xa7, 0x37, 0x85, 0x34, 0x42, 0xf4, 0x6c,
-    0x76, 0x0b, 0x6e, 0x7a, 0x01, 0x5f, 0x8a, 0xcc, 0x56, 0xfe, 0xfa, 0xc7, 0x57, 0xe8, 0x26, 0x44,
-    0x95, 0xe3, 0x4c, 0x35, 0x35, 0x8b, 0x7a, 0xaa, 0xf1, 0x5f, 0x32, 0x5a, 0xf2, 0x22, 0xc0, 0x8f,
-    0xec, 0x8b, 0xce, 0xff, 0x8e, 0x37, 0x3c, 0x36, 0xbc, 0x47, 0x44, 0x50, 0x26, 0xbe, 0x43, 0x22,
-    0xff, 0x7d, 0x4d, 0xf6, 0x38, 0x12, 0xf1, 0xdf, 0xc2, 0x2d, 0x86, 0xb1, 0xa5, 0x2b, 0xb1, 0x3c,
-    0x22, 0x27, 0x61, 0x94, 0x3b, 0x14, 0xc7, 0xb5, 0x28, 0x0f, 0x85, 0xdd, 0xe0, 0x16, 0x01, 0xd2,
-    0xb9, 0x8e, 0xfb, 0x01, 0x4b, 0x25, 0x7c, 0x4b, 0xf4, 0xb8, 0x44, 0x41, 0xc2, 0x2e, 0xf3, 0xbe,
-    0xd1, 0x2d, 0x27, 0x8a, 0x31, 0xf0, 0xe2, 0x28, 0x00, 0x24, 0x9e, 0x3f, 0x30, 0xb0, 0xaf, 0xcc,
-    0xf5, 0xb7, 0x52, 0xf3, 0x72, 0x50, 0xa1, 0x70, 0xfe, 0xaa, 0x82, 0xa1, 0x69, 0xbd, 0x6b, 0x78,
-    0x6b, 0x7f, 0x17, 0xb6, 0x39, 0x23, 0x1c, 0xf8, 0x9b, 0xf0, 0x27, 0x44, 0x00, 0x19, 0x21, 0x1b,
-    0x0b, 0xe8, 0x1f, 0x4b, 0x8f, 0x86, 0xde, 0x15, 0xb9, 0x7a, 0xa9, 0xd9, 0xae, 0x9c, 0xca, 0x5d,
-    0xbf, 0xef, 0xe8, 0x1b, 0xcc, 0xb5, 0x6a, 0x30, 0x9e, 0x30, 0xe1, 0xb7, 0x57, 0x9b, 0xcb, 0x7c,
-    0x16, 0xe8, 0x31, 0x4e, 0x0e, 0xbc, 0x4f, 0xf3, 0x72, 0xbb, 0xb5, 0x3c, 0x10, 0xaf, 0x54, 0xc2,
-    0xe9, 0x11, 0x53, 0xc0, 0xfa, 0x7f, 0xf0, 0x39, 0xb8, 0xae, 0x4c, 0xef, 0x13, 0x6e, 0x19, 0x4e,
-    0xe5, 0x96, 0x21, 0xb8, 0x5a, 0x19, 0x9f, 0x6a, 0x33, 0xaf, 0x02, 0x8e, 0x72, 0xbb, 0x86, 0xca,
-    0x02, 0xbe, 0xfa, 0x1b, 0x95, 0x95, 0x05, 0x3d, 0xe4, 0x76, 0x5e, 0x22, 0x15, 0xac, 0xc7, 0xaf,
-    0x57, 0xfb, 0x72, 0x22, 0xb0, 0x3e, 0x2f, 0x05, 0x08, 0x3c, 0x94, 0xc0, 0x0c, 0xb6, 0x3b, 0x7f,
-    0xba, 0xa1, 0x3e, 0x2f, 0xdd, 0xb2, 0x3d, 0xfc, 0xc8, 0xe8, 0xe2, 0x48, 0x88, 0x35, 0xf0, 0xf5,
-    0x51, 0xc6, 0x5f, 0x66, 0x02, 0xf1, 0xce, 0xf0, 0xad, 0x31, 0x38, 0x5c, 0x71, 0xa6, 0x0c, 0xe7,
-    0xcb, 0x3d, 0x7b, 0xbc, 0x2b, 0x5c, 0x3b, 0xd3, 0x76, 0xcc, 0x7e, 0x8c, 0xad, 0xb1, 0x95, 0x9f,
-    0x1a, 0xfb, 0x8a, 0xf7, 0x86, 0x6d, 0x8c, 0x88, 0xe7, 0xc1, 0x8e, 0xf4, 0xcf, 0x68, 0x49, 0x70,
-    0xa4, 0xc2, 0xa4, 0x9e, 0xbd, 0xc4, 0xc5, 0xdb, 0x8b, 0x72, 0xf9, 0x17, 0xfb, 0x8f, 0x21, 0xc8,
-    0x8c, 0xad, 0xb5, 0x6f, 0x9f, 0x7a, 0x4e, 0x8e, 0xd5, 0xf0, 0xf0, 0xab, 0x9b, 0xf9, 0xd7, 0x0f,
-    0x01, 0x22, 0x1d, 0x18, 0x56, 0xfd, 0xba, 0x9b, 0x0a, 0xb9, 0xe7, 0x5f, 0x16, 0xbb, 0x5f, 0x7b,
-    0x8a, 0x8b, 0x5c, 0x2e, 0xe8, 0xdf, 0x22, 0x5c, 0x6f, 0xa3, 0x31, 0x67, 0x58, 0x04, 0x63, 0x9e,
-    0xfe, 0x3c, 0x16, 0xec, 0xfa, 0xfe, 0x8e, 0xea, 0xdf, 0x2a, 0x9f, 0xa8, 0x1f, 0x67, 0x0f, 0xea,
-    0xe0, 0x3b, 0x36, 0x06, 0x83, 0x0d, 0x90, 0x26, 0xfa, 0x85, 0x0f, 0xff, 0xe7, 0x6b, 0xc3, 0x5c,
-    0x3d, 0xc9, 0xbc, 0xcf, 0x08, 0x10, 0x0a, 0x03, 0xcd, 0x5e, 0x2f, 0xd3, 0x72, 0x11, 0xad, 0xbe,
-    0x57, 0x02, 0xda, 0x1d, 0x27, 0xce, 0xb5, 0x26, 0xb9, 0x31, 0x79, 0xad, 0x6e, 0x9d, 0xf2, 0x35,
-    0x8f, 0x0a, 0xce, 0xec, 0xef, 0x71, 0x1c, 0xb6, 0x6f, 0x77, 0x64, 0x8d, 0x1e, 0x44, 0x3c, 0x0b,
-    0x17, 0x8a, 0x2a, 0x2a, 0xf4, 0xdf, 0xc6, 0x0b, 0xf0, 0xb4, 0x69, 0xd5, 0x1c, 0xf6, 0x25, 0x5f,
-    0x1a, 0xce, 0xb5, 0x91, 0x2a, 0x32, 0x45, 0xb5, 0xf8, 0x06, 0x83, 0x72, 0xeb, 0xef, 0xe0, 0xc2,
-    0xcc, 0xb3, 0x87, 0xf5, 0x33, 0xb7, 0x70, 0xcd, 0xb2, 0x69, 0x83, 0xbb, 0x06, 0x25, 0x4d, 0xab,
-    0x05, 0x1d, 0x1a, 0x6a, 0x34, 0x5c, 0xfc, 0xd6, 0x15, 0x73, 0xb7, 0x7a, 0x78, 0x33, 0xda, 0x6d,
-    0x7c, 0x46, 0x4c, 0xed, 0x06, 0x47, 0x39, 0x6e, 0x08, 0x6a, 0x5f, 0xa6, 0xd0, 0xe9, 0x1a, 0x7d,
-    0xdb, 0x54, 0x2c, 0x5a, 0x74, 0xc4, 0x69, 0x79, 0x45, 0xbb, 0x53, 0xe0, 0x25, 0x09, 0xff, 0x00,
-    0xea, 0x1c, 0x01, 0x94, 0x81, 0x2b, 0x98, 0x5f, 0x37, 0xb2, 0x4f, 0x77, 0xc7, 0x9e, 0x24, 0x1b,
-    0x58, 0x39, 0xd3, 0x0d, 0xe8, 0x21, 0x7a, 0xc0, 0x63, 0xc4, 0xb0, 0xcf, 0x80, 0x51, 0x32, 0x23,
-    0xb5, 0x1f, 0xb8, 0xc0, 0x28, 0xd1, 0x05, 0xd6, 0x9e, 0x18, 0x3e, 0x08, 0x1b, 0x2c, 0xf7, 0x81,
-    0xd3, 0xe2, 0x04, 0xbd, 0x6f, 0x38, 0x0b, 0x64, 0x9c, 0xcf, 0x9c, 0x38, 0x4e, 0xaf, 0xdf, 0x6b,
-    0x9d, 0x6f, 0x78, 0x2f, 0x02, 0xc2, 0x1c, 0x6f, 0xc2, 0xd5, 0x47, 0xad, 0xf0, 0x75, 0xc9, 0xc2,
-    0xb9, 0xc3, 0x42, 0x6c, 0x38, 0x6b, 0x78, 0x1e, 0x10, 0x18, 0xf7, 0x90, 0x90, 0x3b, 0xde, 0x9c,
-    0x4c, 0x81, 0x4c, 0x58, 0x4a, 0x64, 0xc6, 0x5b, 0xf3, 0xb8, 0x59, 0x8d, 0xff, 0x3f, 0x95, 0x54,
-    0xad, 0xc3, 0x8b, 0xca, 0xbc, 0xc6, 0x1b, 0xdb, 0x75, 0x54, 0x31, 0x63, 0xa3, 0xa7, 0x02, 0x3c,
-    0x75, 0x3d, 0xd6, 0x58, 0xf3, 0xb8, 0xb9, 0x8d, 0xb1, 0x4f, 0x4f, 0xc9, 0x04, 0x31, 0x00, 0x24,
-    0x19, 0x2f, 0x83, 0x5f, 0x60, 0x04, 0xf4, 0x74, 0x8d, 0xfb, 0x35, 0x2a, 0x3e, 0x0e, 0x6f, 0x81,
-    0xb3, 0xda, 0x7b, 0x16, 0xa3, 0x31, 0x6f, 0xdf, 0x6a, 0xaa, 0xdc, 0x7f, 0xf5, 0xb1, 0xd5, 0x60,
-    0xdb, 0x01, 0x40, 0x51, 0x5d, 0x4a, 0x61, 0x80, 0x3e, 0x2c, 0x86, 0x01, 0x87, 0x93, 0x17, 0x99,
-    0x58, 0x60, 0x99, 0xda, 0x77, 0x67, 0x14, 0x70, 0xc1, 0x98, 0xb8, 0x4b, 0x73, 0x2a, 0xc4, 0xc5,
-    0xfe, 0x36, 0xc2, 0x74, 0x66, 0x50, 0xbe, 0x9a, 0xcb, 0x18, 0xd2, 0x1c, 0x7c, 0x2a, 0xe8, 0xff,
-    0xfc, 0x21, 0xd8, 0xe2, 0x55, 0xa1, 0x37, 0xeb, 0x58, 0xdb, 0xe4, 0x5e, 0x9f, 0xa8, 0xba, 0xe7,
-    0xcf, 0x82, 0x13, 0x72, 0x9f, 0xae, 0x96, 0x0f, 0x1d, 0x97, 0xb6, 0x69, 0x8b, 0x4d, 0xb7, 0x79,
-    0xd7, 0x4b, 0x96, 0x76, 0xe7, 0x7f, 0x86, 0xd9, 0x03, 0xaf, 0xab, 0x6a, 0x10, 0x33, 0x01, 0x27,
-    0x75, 0x78, 0x11, 0x42, 0x67, 0x84, 0xf3, 0x3b, 0xb4, 0x3c, 0xd0, 0x33, 0x6f, 0xae, 0x7e, 0x06,
-    0xc1, 0x50, 0xd0, 0xde, 0xed, 0x7a, 0xd7, 0x3d, 0xe0, 0x55, 0x0d, 0x77, 0x1c, 0x94, 0x86, 0xac,
-    0x07, 0x35, 0x31, 0xdc, 0x38, 0x4c, 0x96, 0x7c, 0x01, 0x79, 0x95, 0x86, 0x83, 0x0b, 0x0a, 0x61,
-    0x7d, 0x55, 0xa8, 0xd5, 0x28, 0xd1, 0x10, 0x81, 0x4a, 0x55, 0x0c, 0x02, 0xf1, 0x13, 0x5f, 0x85,
-    0x3f, 0x5f, 0x85, 0xaa, 0x1d, 0x6f, 0x36, 0x69, 0x9f, 0xf4, 0x17, 0x36, 0x6d, 0x8d, 0xbe, 0xe1,
-    0x86, 0xe3, 0x96, 0xc6, 0x9c, 0x5c, 0x26, 0xdc, 0x69, 0x1c, 0x5d, 0x5a, 0xd1, 0xc1, 0xc2, 0x10,
-    0xb2, 0xcf, 0x75, 0x95, 0xd1, 0xd9, 0x54, 0x65, 0x8e, 0xb1, 0x58, 0xa5, 0xee, 0xdd, 0xa5, 0xb5,
-    0xa3, 0xed, 0x32, 0x3a, 0x32, 0x4c, 0xb4, 0x5c, 0x4a, 0xa5, 0x4f, 0x89, 0xd5, 0xeb, 0x62, 0xce,
-    0xcf, 0x96, 0x46, 0x0d, 0xe5, 0xe8, 0x7c, 0x72, 0xfd, 0x21, 0x49, 0x06, 0xde, 0x0e, 0x45, 0x5c,
-    0xf0, 0x72, 0xf8, 0x23, 0x3d, 0xa6, 0x41, 0x9b, 0xb0, 0x72, 0xc6, 0x1a, 0xa5, 0x5a, 0xe1, 0x62,
-    0xa3, 0x83, 0xdb, 0x4a, 0x9f, 0x47, 0x25, 0x96, 0xf8, 0x63, 0x59, 0x19, 0x38, 0xb5, 0x5f, 0x4f,
-    0xea, 0x4f, 0xe1, 0x05, 0xa8, 0x53, 0x54, 0x42, 0x19, 0x79, 0xd1, 0xc8, 0x19, 0x1d, 0x99, 0xee,
-    0xc4, 0x31, 0x16, 0xb8, 0x33, 0x6c, 0xe6, 0xf0, 0x2d, 0x51, 0x74, 0x7c, 0x74, 0x62, 0xe7, 0xae,
-    0x0b, 0x4c, 0x71, 0x4b, 0x8d, 0x1c, 0x34, 0x21, 0xc6, 0xef, 0x16, 0x39, 0xcb, 0x1c, 0x41, 0x63,
-    0x05, 0x9b, 0xaf, 0x2a, 0x3e, 0x61, 0x5f, 0x05, 0xbb, 0x75, 0x4b, 0x94, 0x96, 0x7c, 0x19, 0xdb,
-    0x4e, 0xc1, 0xf9, 0x9d, 0x67, 0x50, 0xc7, 0x20, 0xd3, 0x00, 0x8d, 0x7b, 0x1c, 0x90, 0xb9, 0x7c,
-    0xc0, 0xa7, 0xb7, 0x2f, 0x22, 0x07, 0x7b, 0xbf, 0xdb, 0x80, 0xd8, 0x2a, 0xa1, 0x6b, 0xc9, 0x4e,
-    0xd5, 0xc1, 0x24, 0x06, 0xab, 0x07, 0x72, 0x61, 0x90, 0x58, 0xfc, 0xc2, 0xf3, 0x6f, 0x30, 0x09,
-    0xa4, 0xa4, 0x54, 0x80, 0xc6, 0xe6, 0x3a, 0x23, 0x6b, 0x18, 0x12, 0xba, 0x13, 0x7f, 0xdd, 0x71,
-    0xf6, 0xd4, 0xb6, 0x48, 0x9b, 0xa3, 0xdd, 0xff, 0xf0, 0x7f, 0x46, 0x8f, 0x67, 0x87, 0xd9, 0x85,
-    0xcd, 0x43, 0xed, 0xfe, 0xe6, 0xf6, 0x25, 0x95, 0x75, 0xc1, 0x4b, 0xd9, 0x75, 0x54, 0x85, 0x77,
-    0xad, 0xc3, 0xff, 0xce, 0xbc, 0x42, 0x8a, 0xb8, 0x0c, 0x88, 0xeb, 0xc1, 0x00, 0x0c, 0xdc, 0x70,
-    0x8c, 0x90, 0x77, 0xf1, 0x4f, 0x7c, 0x3e, 0xf4, 0x54, 0x2d, 0xae, 0xd5, 0xbe, 0x01, 0x7e, 0xf6,
-    0xd5, 0x7c, 0x6a, 0xe7, 0x1c, 0x95, 0x2d, 0x0c, 0xbb, 0xdb, 0x55, 0x5d, 0x56, 0x77, 0xfc, 0x38,
-    0xf8, 0x7d, 0xb6, 0x8a, 0x62, 0xd4, 0x26, 0xef, 0x92, 0x03, 0xd5, 0x83, 0xe7, 0xb0, 0x6d, 0x80,
-    0xff, 0xcc, 0xec, 0xa6, 0xc3, 0x80, 0x2a, 0x21, 0x02, 0x5b, 0xa1, 0x0c, 0xfe, 0xc2, 0x26, 0x65,
-    0x0a, 0xb5, 0x1d, 0x6f, 0x70, 0x7d, 0xfe, 0x8d, 0xb4, 0xc6, 0xff, 0x08, 0xca, 0x1a, 0xb5, 0x4a,
-    0x52, 0x31, 0xd4, 0x71, 0xc6, 0xcf, 0xe5, 0x3e, 0xdf, 0x15, 0x7c, 0xb3, 0xf0, 0xfa, 0x59, 0x2c,
-    0x7d, 0xc3, 0x27, 0xc7, 0x46, 0xf6, 0x65, 0xd6, 0x7a, 0x0f, 0xf7, 0xfe, 0xc3, 0x6c, 0xeb, 0x9d,
-    0xa2, 0x2a, 0x73, 0x1d, 0x2d, 0xf0, 0xb1, 0xb1, 0xff, 0x31, 0xf0, 0x9b, 0x35, 0x3e, 0x02, 0x44,
-    0x53, 0x70, 0x72, 0x54, 0x8f, 0xb0, 0x92, 0x0a, 0x45, 0x83, 0xb2, 0xea, 0x35, 0xb7, 0x72, 0x5e,
-    0x9b, 0xf0, 0x4e, 0x89, 0x10, 0x92, 0x0e, 0x93, 0xdc, 0xf8, 0x87, 0xd2, 0x0b, 0x6c, 0x3f, 0x4c,
-    0x82, 0xd5, 0x52, 0x02, 0x03, 0x30, 0x0d, 0x3a, 0x86, 0x01, 0x88, 0x40, 0xa5, 0x2a, 0x92, 0x04,
-    0x7f, 0x32, 0xa1, 0x33, 0x78, 0xd5, 0xd6, 0x03, 0xbc, 0x8c, 0xec, 0x6b, 0x75, 0xbe, 0x43, 0x99,
-    0x18, 0x32, 0xc2, 0xd7, 0x34, 0x75, 0xb0, 0x86, 0x45, 0x48, 0xf9, 0x4a, 0xd5, 0x13, 0x4b, 0x1c,
-    0xf1, 0x39, 0x4c, 0x69, 0x30, 0x8b, 0xe1, 0x33, 0x62, 0xdc, 0x62, 0x52, 0x9c, 0x0a, 0xc2, 0x44,
-    0x60, 0xcf, 0x9d, 0xa9, 0xe4, 0x19, 0xb9, 0x3c, 0xba, 0x8f, 0x00, 0x25, 0xd9, 0x6f, 0x47, 0xd8,
-    0xee, 0xb0, 0x3f, 0x06, 0xd5, 0x56, 0x71, 0xf5, 0x8a, 0xc7, 0x05, 0xc8, 0xec, 0xc0, 0xec, 0x7a,
-    0x16, 0xdd, 0x1c, 0x95, 0x99, 0xfc, 0xae, 0xaa, 0x74, 0x4e, 0x84, 0x92, 0x79, 0x18, 0x44, 0x82,
-    0x24, 0x64, 0x9a, 0xf0, 0xf6, 0xd2, 0xb3, 0xe1, 0xb4, 0xb3, 0x35, 0x0f, 0x09, 0xeb, 0xad, 0x36,
-    0xb0, 0x2a, 0x73, 0x6b, 0xaf, 0xe0, 0x78, 0xf5, 0x02, 0x0a, 0xcd, 0x0c, 0x62, 0xf0, 0xb9, 0x94,
-    0x42, 0x6c, 0xb5, 0xb2, 0x07, 0x6f, 0x87, 0x0a, 0x0a, 0xb4, 0x8a, 0x63, 0xb9, 0x21, 0x86, 0xd3,
-    0x0f, 0x67, 0xf9, 0x8a, 0x03, 0xef, 0xf0, 0x81, 0x6f, 0xfc, 0x25, 0x93, 0x1d, 0x60, 0x1c, 0x03,
-    0xda, 0x52, 0x63, 0x3e, 0xd6, 0x44, 0x29, 0x78, 0x2a, 0xac, 0x66, 0xd7, 0x9d, 0xbd, 0x41, 0xa9,
-    0xb9, 0xef, 0x09, 0x2e, 0x3d, 0xc0, 0xe5, 0x79, 0x9b, 0x3b, 0x12, 0x22, 0x46, 0x80, 0x07, 0x25,
-    0x8d, 0x6f, 0x0e, 0x4a, 0xb4, 0x36, 0x64, 0xec, 0xa6, 0x1c, 0x17, 0x62, 0xe2, 0x67, 0xee, 0x73,
-    0xc2, 0x36, 0xcb, 0xcf, 0x5e, 0x53, 0x54, 0x7e, 0x94, 0x24, 0x95, 0x80, 0x1d, 0x5e, 0xe9, 0xde,
-    0x99, 0x1d, 0xd9, 0xc4, 0xf5, 0x7f, 0x6c, 0xf0, 0x9e, 0x67, 0x6d, 0xef, 0x67, 0xea, 0x4f, 0x45,
-    0x97, 0x81, 0x0a, 0x7b, 0x30, 0x28, 0x4d, 0xa5, 0xd6, 0x98, 0xe1, 0x9e, 0xa3, 0x6d, 0xf8, 0x5b,
-    0x00, 0xc0, 0xa8, 0x55, 0x4d, 0x1a, 0x4d, 0x69, 0xfc, 0xe4, 0x79, 0x3d, 0x3f, 0xc6, 0xa7, 0xb9,
-    0xb1, 0xea, 0x1e, 0x37, 0xc1, 0xd1, 0x86, 0xa5, 0xef, 0xaf, 0xcf, 0xcd, 0xf1, 0x77, 0xa7, 0x6c,
-    0x1a, 0x9d, 0xc7, 0xd0, 0xc9, 0x0e, 0x77, 0x30, 0xd9, 0x96, 0xaf, 0xea, 0xde, 0x3f, 0xcf, 0xd2,
-    0xab, 0x95, 0x25, 0x2a, 0xd6, 0xa2, 0xa7, 0x46, 0xc6, 0x2f, 0x15, 0x99, 0x04, 0x2f, 0x92, 0x6d,
-    0x7a, 0xbc, 0x55, 0xa1, 0xeb, 0x1f, 0x8c, 0xab, 0x82, 0x78, 0x8f, 0xcb, 0x3b, 0xec, 0x02, 0xbd,
-    0xbf, 0x30, 0xda, 0x18, 0x8f, 0x2d, 0x27, 0xc4, 0x0a, 0xb6, 0x1b, 0x47, 0x12, 0xae, 0x73, 0x16,
-    0x56, 0x9f, 0xea, 0xa9, 0x4a, 0xc6, 0x37, 0xc7, 0x02, 0x42, 0xf6, 0xf0, 0xfd, 0x4d, 0x0c, 0xec,
-    0xc6, 0x4e, 0x5a, 0xfe, 0x5f, 0x55, 0x60, 0xac, 0x4c, 0xef, 0x11, 0x7c, 0x5a, 0xd7, 0x07, 0x25,
-    0x07, 0xb7, 0xfd, 0xed, 0x51, 0xb6, 0x83, 0x92, 0x30, 0xd5, 0x21, 0xe5, 0xaf, 0xec, 0x6d, 0xff,
-    0x04, 0xb2, 0xb1, 0xac, 0x62, 0x2d, 0xcd, 0x7c, 0xde, 0xed, 0xb2, 0xbc, 0x42, 0xb8, 0xff, 0x61,
-    0x59, 0xd7, 0xf7, 0x5a, 0x5d, 0xda, 0x2f, 0x80, 0xed, 0xae, 0xa6, 0xe1, 0x91, 0x8d, 0x09, 0x64,
-    0xf7, 0x56, 0x8d, 0x7a, 0xdd, 0xc8, 0xfb, 0x5b, 0x7f, 0xb9, 0x8e, 0x47, 0xa7, 0x3b, 0x30, 0x77,
-    0x42, 0xc2, 0x7c, 0x4c, 0x5e, 0x4f, 0xd4, 0xd9, 0x5d, 0xef, 0xbf, 0x4c, 0x7e, 0x12, 0x23, 0x91,
-    0x6a, 0x9c, 0xa1, 0xb8, 0x2a, 0xef, 0x73, 0x28, 0xe7, 0x77, 0xb7, 0xe8, 0x8f, 0x14, 0x9d, 0x31,
-    0x04, 0xb8, 0x97, 0xf0, 0xb9, 0xdd, 0xef, 0x01, 0x3d, 0x6b, 0x1e, 0xcc, 0x35, 0x9a, 0xac, 0xfe,
-    0xe9, 0x2c, 0xb3, 0xca, 0xf8, 0x27, 0x56, 0xd0, 0xb9, 0x53, 0x74, 0x09, 0x4d, 0xae, 0x5c, 0x8b,
-    0xbc, 0xc4, 0x4b, 0xdb, 0x75, 0x55, 0x51, 0x57, 0x68, 0x51, 0x69, 0x74, 0x8d, 0xa7, 0xcf, 0x24,
-    0x5d, 0xc5, 0xf6, 0x79, 0xaf, 0xc6, 0xae, 0x45, 0x5d, 0x44, 0x80, 0x12, 0x0a, 0x7d, 0x27, 0x37,
-    0x1c, 0x93, 0x8c, 0x0c, 0x1b, 0x7e, 0x39, 0x24, 0x4d, 0x58, 0xa1, 0xe4, 0x98, 0x61, 0x10, 0xf2,
-    0xfa, 0xe7, 0x52, 0x37, 0x80, 0xa8, 0x60, 0x35, 0x7c, 0x8a, 0x49, 0xa3, 0x44, 0x23, 0x20, 0xb5,
-    0x18, 0x9a, 0xe6, 0x51, 0x16, 0x75, 0xdf, 0x46, 0x57, 0x00, 0xea, 0xaf, 0x86, 0xe1, 0xa6, 0x68,
-    0x84, 0x62, 0xb7, 0x7b, 0xf3, 0xf5, 0x3b, 0x3f, 0x14, 0xed, 0x5f, 0xed, 0x13, 0xd1, 0x94, 0x9f,
-    0xc7, 0x16, 0x0b, 0x18, 0xa5, 0x1d, 0xf7, 0x27, 0xad, 0xe6, 0x5f, 0x39, 0x2d, 0x48, 0xd9, 0x24,
-    0x9a, 0x50, 0xfe, 0x70, 0x6a, 0xd7, 0xbb, 0x75, 0x77, 0x98, 0x3e, 0x3b, 0x6c, 0xfc, 0xa3, 0xa1,
-    0xe4, 0x9d, 0x35, 0x3e, 0x02, 0x42, 0xc7, 0xf0, 0xfa, 0x5b, 0x10, 0xd9, 0x1c, 0x94, 0x44, 0x7c,
-    0x9f, 0x55, 0x60, 0xa8, 0x26, 0x6f, 0x64, 0x9e, 0xcd, 0x65, 0x62, 0xf6, 0x07, 0x24, 0x35, 0xe7,
-    0x72, 0x52, 0x1a, 0xb0, 0x6f, 0x0d, 0x8b, 0xa5, 0xbf, 0x6b, 0x94, 0xe6, 0x27, 0xb6, 0xcf, 0x4d,
-    0x72, 0x30, 0x59, 0xbc, 0xd4, 0x57, 0x4b, 0xb5, 0xbf, 0x59, 0x71, 0x2c, 0x7c, 0x8a, 0x69, 0xa3,
-    0x79, 0x86, 0x32, 0x99, 0x6b, 0xa6, 0x83, 0x4f, 0x48, 0xd7, 0xbe, 0xdc, 0xd2, 0x29, 0x04, 0x73,
-    0x72, 0xaf, 0x37, 0x4c, 0x09, 0x30, 0x33, 0xdf, 0x60, 0xb6, 0x9e, 0x6f, 0x8f, 0x77, 0x1f, 0x33,
-    0x5e, 0x8a, 0x75, 0x1e, 0x6b, 0xc0, 0xed, 0x81, 0xcf, 0xc5, 0xf0, 0xc3, 0xf0, 0x95, 0xd3, 0x3b,
-    0x47, 0x42, 0xe6, 0x29, 0xb4, 0xef, 0x6f, 0x12, 0x63, 0x3d, 0x98, 0xa3, 0x4f, 0x81, 0xc3, 0xfc,
-    0xb3, 0xf4, 0x35, 0xb7, 0x2c, 0xa7, 0x0e, 0xeb, 0xe3, 0xdc, 0xc0, 0xdd, 0x75, 0x2b, 0x76, 0x9e,
-    0xbe, 0x0a, 0x0d, 0x82, 0x54, 0xcd, 0xab, 0x01, 0x5b, 0x58, 0x6a, 0xd8, 0xbb, 0x75, 0x4e, 0x2a,
-    0xd0, 0x2f, 0x58, 0xce, 0x8b, 0x59, 0x2c, 0x9c, 0xab, 0x01, 0xc9, 0x6e, 0x1b, 0xc1, 0x12, 0xa2,
-    0xb6, 0x80, 0x07, 0x25, 0x8c, 0xad, 0xc1, 0x7e, 0x08, 0x4c, 0x54, 0xfa, 0x8d, 0x7c, 0x95, 0x81,
-    0x47, 0xe5, 0xe6, 0x4f, 0x04, 0xd0, 0xf2, 0x29, 0xb0, 0x86, 0xa2, 0x1a, 0xca, 0x82, 0xd0, 0x19,
-    0xda, 0xd3, 0x1c, 0x33, 0xb8, 0x61, 0x93, 0xf2, 0x1f, 0x02, 0xd9, 0x5d, 0xc8, 0x43, 0xdd, 0xd5,
-    0x7c, 0x07, 0x34, 0xac, 0x54, 0x3f, 0xcb, 0x2c, 0x5e, 0x59, 0xb7, 0x43, 0x73, 0xa8, 0x23, 0xf0,
-    0x7a, 0xd3, 0xde, 0x58, 0xb1, 0x1d, 0x89, 0xf3, 0x26, 0x92, 0x88, 0xb4, 0x10, 0x7c, 0x19, 0x5f,
-    0x94, 0xbb, 0xe5, 0x12, 0xae, 0xde, 0xcb, 0xaa, 0x71, 0x1a, 0xe2, 0xee, 0xe0, 0x9c, 0x8a, 0x36,
-    0xdc, 0xef, 0x16, 0x21, 0x04, 0xe6, 0xc9, 0xc3, 0xc0, 0x52, 0x55, 0xf7, 0x53, 0xf5, 0x9c, 0xf5,
-    0x44, 0x53, 0x8f, 0x7e, 0x22, 0xa7, 0xa4, 0x06, 0x80, 0x92, 0x17, 0xb5, 0x54, 0x58, 0x50, 0xe1,
-    0x4e, 0xfb, 0x10, 0x6f, 0xb0, 0x15, 0x64, 0x86, 0xce, 0xbc, 0xe5, 0x80, 0xd9, 0x6b, 0xc4, 0x0d,
-    0x8d, 0xce, 0x42, 0x36, 0xf0, 0x02, 0xed, 0xbe, 0xe1, 0xfd, 0xdd, 0xce, 0x55, 0x22, 0x7d, 0xfd,
-    0x7b, 0x05, 0xf0, 0x53, 0x22, 0x49, 0x48, 0x36, 0xf0, 0x72, 0x1a, 0x79, 0x39, 0x2c, 0x0d, 0x58,
-    0x5e, 0x13, 0x7e, 0xc2, 0xf9, 0xf8, 0x81, 0x89, 0x68, 0x7b, 0x1a, 0xc5, 0xef, 0xa1, 0xce, 0x01,
-    0xb9, 0x20, 0x67, 0x96, 0x58, 0x01, 0x7c, 0x07, 0x55, 0x5b, 0x55, 0x77, 0xa6, 0x38, 0x92, 0xfc,
-    0x59, 0xb6, 0x60, 0x12, 0x5f, 0x98, 0x01, 0xc1, 0x98, 0xdb, 0x7d, 0xff, 0x6f, 0x7a, 0xc5, 0x9e,
-    0xa4, 0xd1, 0x1e, 0xa2, 0x3a, 0xe0, 0x27, 0xd9, 0x18, 0x6d, 0x7f, 0xa8, 0xc1, 0x39, 0x3a, 0x52,
-    0x4c, 0x72, 0x7a, 0x08, 0xef, 0xb9, 0xa8, 0x73, 0x35, 0x31, 0x7c, 0xfb, 0x30, 0x2a, 0x98, 0x3f,
-    0xb2, 0xc0, 0x51, 0xa5, 0xb7, 0xce, 0xf1, 0xc8, 0x54, 0xf8, 0xec, 0x69, 0x28, 0x74, 0x53, 0x22,
-    0x75, 0x09, 0xeb, 0x85, 0x24, 0x44, 0x97, 0x83, 0x09, 0xb8, 0x49, 0x78, 0x53, 0x34, 0xcc, 0xdb,
-    0x22, 0x76, 0xf6, 0xd6, 0xc2, 0x15, 0xc7, 0x61, 0xaa, 0x27, 0xf7, 0x14, 0x1a, 0xb7, 0xad, 0xf6,
-    0xc0, 0x20, 0xaa, 0xb0, 0x33, 0xa8, 0x64, 0x7e, 0xe7, 0x69, 0x2d, 0x6b, 0xf8, 0x64, 0xee, 0xaf,
-    0xb0, 0x2d, 0x2f, 0xae, 0xba, 0xb2, 0x52, 0xa4, 0xba, 0x4f, 0xc9, 0x7e, 0x68, 0xc1, 0x49, 0xd9,
-    0x92, 0xdb, 0x5f, 0x83, 0xb9, 0xb3, 0xc6, 0x7a, 0xeb, 0x44, 0x3d, 0x63, 0x6b, 0x01, 0x00, 0xf9,
-    0x58, 0x09, 0x72, 0x22, 0x70, 0x15, 0x30, 0x1d, 0x4e, 0xb2, 0x97, 0x53, 0xc8, 0x89, 0x85, 0x5f,
-    0xd0, 0x1b, 0x00, 0x88, 0xf0, 0xc3, 0x86, 0x2c, 0x21, 0x07, 0xe9, 0x2c, 0x24, 0x8c, 0x94, 0x90,
-    0x31, 0xb1, 0x7b, 0xff, 0xf6, 0xdf, 0xf9, 0xd8, 0x4b, 0xdc, 0x7d, 0xaa, 0x18, 0xe7, 0x37, 0xd3,
-    0x48, 0xa5, 0x04, 0x28, 0xc0, 0x5d, 0xf9, 0x0b, 0xb0, 0x96, 0x97, 0x3a, 0x7c, 0xf7, 0x21, 0x57,
-    0xac, 0x9c, 0x98, 0x34, 0xb9, 0xfa, 0xb0, 0x49, 0x79, 0x5e, 0x86, 0xad, 0x29, 0x25, 0x31, 0x03,
-    0xdf, 0x1b, 0xd9, 0xbb, 0xcb, 0x4c, 0x8a, 0x8f, 0xbd, 0x58, 0x31, 0xc7, 0x0c, 0x02, 0x0e, 0xa5,
-    0xd5, 0x8e, 0x23, 0xce, 0x0e, 0x24, 0xd7, 0xd3, 0x01, 0x25, 0xf0, 0xf8, 0x1c, 0xae, 0x86, 0x4f,
-    0xad, 0xb2, 0xf4, 0xf1, 0xe3, 0xcc, 0x8d, 0xec, 0xae, 0x01, 0xc7, 0xa8, 0x8c, 0x5e, 0xe0, 0xe4,
-    0x0c, 0x5a, 0x0e, 0x48, 0xa3, 0x56, 0x2d, 0xa1, 0x31, 0x62, 0x56, 0x1c, 0x48, 0x7e, 0x4d, 0x46,
-    0x26, 0x10, 0x65, 0x1d, 0x45, 0x38, 0x1f, 0xbe, 0x7e, 0x5e, 0x70, 0x26, 0x5f, 0x13, 0xa7, 0xf1,
-    0x8b, 0xc8, 0x0c, 0xc8, 0x98, 0xbc, 0xbd, 0x7e, 0x17, 0xce, 0xad, 0x3c, 0x27, 0xd9, 0x35, 0x8a,
-    0xfd, 0x47, 0x83, 0x34, 0x43, 0x00, 0x1b, 0x19, 0xbf, 0xf3, 0xed, 0xf7, 0xae, 0x48, 0x65, 0xcc,
-    0x34, 0x3a, 0x62, 0x5a, 0x30, 0x45, 0xb5, 0x84, 0x55, 0x56, 0x21, 0x7b, 0xb9, 0xad, 0xc6, 0xbc,
-    0x16, 0x8e, 0xb0, 0x63, 0x94, 0x5a, 0xb7, 0xbf, 0x4f, 0x00, 0x23, 0x07, 0x2f, 0x1f, 0x40, 0x37,
-    0xc4, 0xf0, 0x88, 0x7a, 0xe6, 0x37, 0x89, 0xfd, 0xbb, 0xfd, 0x48, 0x88, 0x70, 0x3b, 0x69, 0x5a,
-    0x46, 0xdb, 0xbb, 0x75, 0x06, 0xc7, 0xff, 0x39, 0xd2, 0x9b, 0x79, 0x3c, 0x0e, 0xa5, 0xdd, 0x64,
-    0x80, 0x12, 0x25, 0x09, 0x3f, 0x07, 0x0c, 0x0e, 0xc7, 0xaf, 0x2d, 0x9e, 0xc1, 0xc9, 0x34, 0x6a,
-    0xf9, 0xba, 0x89, 0x99, 0x91, 0x2f, 0xc6, 0xc2, 0x77, 0xe8, 0x92, 0xd5, 0x05, 0x01, 0x3e, 0x18,
-    0x2f, 0x8b, 0xd1, 0x4c, 0x46, 0x00, 0x13, 0xe1, 0x28, 0x99, 0x34, 0xd2, 0x9f, 0xf5, 0xb7, 0x27,
-    0xf1, 0x89, 0xe6, 0x91, 0x9e, 0x8a, 0x78, 0x42, 0x64, 0xde, 0xd8, 0x50, 0xc0, 0x70, 0xfb, 0x9b,
-    0xad, 0x95, 0x11, 0xc1, 0x3f, 0xa7, 0x1c, 0xfc, 0x28, 0xe8, 0xad, 0x25, 0x5b, 0x13, 0xb5, 0x0b,
-    0xb8, 0x48, 0xa4, 0x25, 0xec, 0xfc, 0xc9, 0x0d, 0x29, 0xc0, 0x47, 0x86, 0xcf, 0xf4, 0xe6, 0x74,
-    0x55, 0x09, 0x6d, 0x3e, 0xef, 0xc4, 0xdb, 0x49, 0xe7, 0x01, 0xf9, 0x81, 0x0e, 0x15, 0x0d, 0x3d,
-    0xd7, 0x28, 0x4c, 0x34, 0x39, 0x39, 0x98, 0xec, 0x0e, 0xd2, 0x02, 0x5b, 0x8f, 0x0c, 0x8e, 0xed,
-    0xa7, 0xdc, 0x36, 0x48, 0x71, 0x72, 0xef, 0x7a, 0xfc, 0x9f, 0x92, 0xfb, 0x00, 0x4a, 0xea, 0x2e,
-    0xf3, 0xcc, 0xe0, 0x87, 0x89, 0x2a, 0xc0, 0x2d, 0xe3, 0xf7, 0x2d, 0x8e, 0xc2, 0xd6, 0xe7, 0xfe,
-    0xec, 0x7a, 0x48, 0xc5, 0x6e, 0x0e, 0xbe, 0x07, 0xe8, 0x97, 0x0d, 0x24, 0xd1, 0xa0, 0xa2, 0x9e,
-    0x05, 0x19, 0x5e, 0x18, 0xfa, 0x5f, 0xd6, 0xd0, 0x7c, 0x21, 0x8f, 0xf9, 0xc9, 0xc3, 0x5c, 0x24,
-    0xf8, 0x5f, 0x6f, 0xe4, 0xb8, 0xfa, 0xab, 0xe7, 0xa2, 0x81, 0x33, 0x03, 0x97, 0x80, 0xdb, 0x7c,
-    0xd0, 0xcd, 0x05, 0x33, 0x84, 0xec, 0x27, 0x9f, 0x0a, 0xb5, 0x9d, 0xcf, 0x7c, 0x34, 0x03, 0xe0,
-    0x1c, 0x43, 0xc1, 0x65, 0x3f, 0x01, 0x9d, 0x1a, 0x7c, 0x9f, 0xf7, 0xc6, 0xed, 0x39, 0x52, 0x8e,
-    0xf0, 0x0f, 0xa3, 0x74, 0x2d, 0xce, 0x93, 0x22, 0x75, 0xf0, 0x6d, 0x06, 0xfd, 0x54, 0xec, 0x1a,
-    0x31, 0xc8, 0xe5, 0x21, 0xcb, 0xbf, 0xac, 0x88, 0xcb, 0x30, 0x3a, 0xf0, 0x3d, 0xe6, 0x65, 0x79,
-    0x54, 0x69, 0xa1, 0x80, 0x56, 0x52, 0xda, 0x7a, 0x76, 0xef, 0xfb, 0x27, 0xc3, 0x92, 0x53, 0xca,
-    0x19, 0x29, 0x73, 0x81, 0x1a, 0x96, 0xc8, 0x51, 0x10, 0x0b, 0x10, 0x93, 0x37, 0xb5, 0xb0, 0xe6,
-    0xe4, 0x80, 0xdb, 0xe0, 0x91, 0x6b, 0x8c, 0x1c, 0x7e, 0x8e, 0x51, 0x6f, 0xee, 0xd5, 0x87, 0x0a,
-    0x18, 0x7f, 0x50, 0x98, 0x3d, 0x9f, 0x92, 0x7b, 0xd5, 0x83, 0xeb, 0x30, 0x6d, 0x80, 0x40, 0x64,
-    0xa9, 0x57, 0x15, 0x80, 0x5a, 0x4e, 0x7e, 0x98, 0x96, 0x4a, 0x8e, 0xc3, 0xfe, 0x4d, 0x6d, 0x02,
-    0x1f, 0x5b, 0x6b, 0x99, 0x2f, 0x2d, 0x8a, 0x71, 0x60, 0x13, 0xc4, 0x38, 0x00, 0x82, 0x9a, 0xc3,
-    0x02, 0x0a, 0x36, 0x0c, 0xb0, 0x12, 0x67, 0x86, 0x63, 0x85, 0x34, 0x87, 0x7d, 0x67, 0x47, 0xc7,
-    0x73, 0xa3, 0x8f, 0x7b, 0x3e, 0xf8, 0x6d, 0x5b, 0x37, 0x4c, 0x29, 0xf6, 0x83, 0x2b, 0xd1, 0xf3,
-    0xaa, 0x4f, 0xec, 0x6a, 0xad, 0xba, 0x49, 0x1a, 0x4e, 0x61, 0xb1, 0xb0, 0x6f, 0x05, 0xe4, 0x44,
-    0x7c, 0x30, 0x36, 0x0a, 0x28, 0x4d, 0x60, 0xc0, 0xba, 0xb5, 0xcc, 0x5d, 0x5d, 0xbb, 0x60, 0x12,
-    0x5f, 0x00, 0x3f, 0xa7, 0xf0, 0xaf, 0x34, 0x62, 0xdf, 0xdb, 0xff, 0x34, 0xde, 0x19, 0x27, 0x11,
-    0x78, 0x01, 0xc2, 0x4d, 0xe8, 0xd1, 0x9e, 0xca, 0xff, 0x02, 0x2d, 0xbd, 0xf9, 0x10, 0x0b, 0xdd,
-    0x1a, 0xdc, 0xac, 0x77, 0x64, 0x88, 0xb5, 0xeb, 0xba, 0xb7, 0xc0, 0xb7, 0x2d, 0xcf, 0x0e, 0x4a,
-    0xa3, 0x56, 0x43, 0xd1, 0xb2, 0x92, 0x6e, 0x03, 0x2a, 0x1f, 0x9b, 0x4c, 0x5d, 0x65, 0x61, 0x80,
-    0x41, 0x17, 0x86, 0x01, 0xcb, 0xc0, 0xcc, 0x73, 0xda, 0x9c, 0x9e, 0x5e, 0x93, 0x90, 0x11, 0xcf,
-    0xc3, 0x0c, 0x95, 0x34, 0x83, 0x00, 0x02, 0xad, 0xcd, 0x1c, 0x23, 0xca, 0x90, 0xd1, 0x63, 0xd9,
-    0xee, 0x50, 0x05, 0x28, 0xf2, 0xe6, 0x77, 0x4c, 0x1a, 0x94, 0xac, 0xed, 0x48, 0x62, 0xf6, 0xbb,
-    0x76, 0xf8, 0x85, 0xe0, 0x4f, 0x3a, 0x2c, 0x19, 0xd8, 0xf1, 0x09, 0xc4, 0x36, 0x58, 0xdb, 0xaa,
-    0x30, 0x0d, 0xf1, 0x65, 0xe0, 0x43, 0xa3, 0x7e, 0x50, 0x17, 0x05, 0x99, 0x87, 0xc4, 0x03, 0x84,
-    0x83, 0x92, 0xd6, 0x67, 0x01, 0x20, 0x63, 0x78, 0x8a, 0xb4, 0xf7, 0xde, 0x75, 0x54, 0x85, 0x47,
-    0x2f, 0xc3, 0x37, 0xc1, 0xaa, 0xa8, 0xde, 0xc9, 0x88, 0x86, 0xe0, 0xe4, 0xaa, 0x9d, 0x35, 0xba,
-    0x49, 0xd1, 0x44, 0x1a, 0x5a, 0xc3, 0x62, 0xeb, 0xb6, 0x4f, 0x63, 0x7c, 0x3e, 0x8e, 0x4b, 0x2d,
-    0xb0, 0xd6, 0xd8, 0x2f, 0x3a, 0xbc, 0x80, 0x56, 0xbf, 0xc1, 0x14, 0x23, 0x4f, 0x12, 0x72, 0xfe,
-    0xed, 0x77, 0xd6, 0x52, 0x8b, 0x05, 0x3c, 0x7c, 0x0a, 0x1b, 0x04, 0x55, 0x21, 0x47, 0x1c, 0x16,
-    0x25, 0x60, 0x86, 0x01, 0xad, 0x09, 0x1b, 0xe6, 0x66, 0x88, 0x47, 0x66, 0x12, 0x2e, 0x5c, 0x5a,
-    0xd3, 0xe8, 0x3d, 0x76, 0xf4, 0x95, 0x4a, 0x3b, 0x4e, 0x8d, 0xb2, 0xc0, 0xca, 0x2a, 0xac, 0xbe,
-    0x10, 0xbb, 0x5f, 0x9e, 0xd7, 0x9f, 0x25, 0x80, 0x69, 0x1b, 0xff, 0xcf, 0xe5, 0x42, 0xde, 0xcc,
-    0xf0, 0xe3, 0x56, 0x85, 0x16, 0x53, 0x40, 0x60, 0x77, 0x55, 0xc8, 0x8b, 0xc0, 0x45, 0x80, 0xe7,
-    0x00, 0x0f, 0x0d, 0x63, 0x0e, 0x5f, 0xb3, 0xc0, 0x73, 0x78, 0xf7, 0xa3, 0x97, 0x87, 0xc5, 0x1f,
-    0x0e, 0x49, 0x93, 0x5e, 0x02, 0x50, 0x73, 0x70, 0xf2, 0xbf, 0xcd, 0x83, 0xa0, 0x81, 0x3f, 0x50,
-    0x25, 0xd5, 0xeb, 0x00, 0xb7, 0xb2, 0xa7, 0x35, 0x7f, 0x7c, 0x44, 0x73, 0xf0, 0x72, 0x44, 0x86,
-    0x69, 0x27, 0x8d, 0x20, 0x16, 0xf4, 0x15, 0x4d, 0x2e, 0x7e, 0xaf, 0xb2, 0x12, 0x81, 0xe6, 0x38,
-    0xe4, 0xb4, 0xb4, 0xd7, 0xa9, 0xfc, 0x5a, 0x1c, 0xe9, 0x3d, 0x7a, 0xc4, 0xf6, 0x91, 0x2c, 0x98,
-    0x00, 0x49, 0x3d, 0xce, 0x47, 0xc8, 0x8f, 0xbf, 0xc9, 0xed, 0xe7, 0x87, 0x1e, 0x75, 0x7d, 0xf6,
-    0xbb, 0x4e, 0x99, 0x52, 0xc0, 0xd2, 0x48, 0x25, 0xc4, 0xc5, 0x93, 0x97, 0xdb, 0x4b, 0x87, 0x33,
-    0xca, 0x64, 0x50, 0x0a, 0x30, 0xa6, 0x50, 0x94, 0xfe, 0x56, 0xa4, 0xbb, 0xc0, 0x24, 0x56, 0xb0,
-    0xd7, 0x55, 0x74, 0x0c, 0x90, 0x6d, 0xe7, 0x4a, 0x00, 0xa0, 0xee, 0x3c, 0x51, 0xdf, 0xf0, 0xec,
-    0x5b, 0xfc, 0xbf, 0xf1, 0xb9, 0xb4, 0x98, 0x00, 0xda, 0x55, 0x45, 0xa8, 0xf0, 0x02, 0xa8, 0xd6,
-    0xe0, 0xe4, 0x54, 0x4f, 0x5d, 0x75, 0xb1, 0xdc, 0xf2, 0x22, 0x80, 0xff, 0xeb, 0x13, 0xf7, 0xb1,
-    0x14, 0xed, 0x07, 0x24, 0xd1, 0xab, 0xe1, 0xc8, 0x53, 0x52, 0x60, 0x25, 0xc8, 0x89, 0xe0, 0x30,
-    0xd2, 0xa4, 0x61, 0xf6, 0x65, 0xc7, 0x3a, 0x19, 0x79, 0x2a, 0x35, 0x03, 0x22, 0x88, 0x65, 0x1f,
-    0x92, 0x65, 0x2d, 0xb1, 0x71, 0xc2, 0x04, 0x16, 0x96, 0x18, 0x2c, 0xa4, 0xd0, 0xc0, 0xdc, 0xac,
-    0x3d, 0xb7, 0x82, 0xb2, 0x3f, 0xc0, 0x1b, 0xff, 0xae, 0x5c, 0xb8, 0x63, 0xd2, 0xb3, 0xd0, 0x55,
-    0xfa, 0xb0, 0xf3, 0xd1, 0xc2, 0x55, 0xb5, 0x6a, 0x5d, 0x60, 0x3d, 0x94, 0xa4, 0x26, 0x6f, 0x5e,
-    0x8e, 0x01, 0xf8, 0xe1, 0xd4, 0x1a, 0x0c, 0xd7, 0x32, 0xcd, 0xf0, 0xdc, 0x70, 0x04, 0x61, 0x42,
-    0xd0, 0x81, 0xaf, 0x12, 0x04, 0x54, 0x76, 0x18, 0x3a, 0x57, 0x09, 0xbe, 0x5b, 0xa8, 0xf0, 0x72,
-    0xa8, 0xb6, 0x60, 0xe4, 0xc2, 0xb5, 0x01, 0x1b, 0x87, 0x91, 0x24, 0x5a, 0xbf, 0x07, 0x95, 0x1d,
-    0xac, 0x1c, 0xcf, 0x66, 0x8d, 0x38, 0x16, 0xf4, 0x0a, 0xe5, 0xd9, 0x58, 0x2f, 0x16, 0x6d, 0x04,
-    0x18, 0x9a, 0x06, 0x01, 0xdc, 0xff, 0x1c, 0xec, 0x23, 0x7c, 0x19, 0xfc, 0x6a, 0x73, 0x9e, 0xbb,
-    0x9c, 0x01, 0x40, 0x7c, 0xc1, 0x80, 0x5d, 0x46, 0x98, 0xe1, 0x92, 0x04, 0xe7, 0x32, 0xc4, 0x1d,
-    0xb8, 0x0e, 0x23, 0x4f, 0x41, 0xc5, 0x81, 0xba, 0xfb, 0x3f, 0xad, 0xb6, 0x94, 0x76, 0xdc, 0x9e,
-    0x0f, 0x9b, 0x33, 0xcf, 0xff, 0xed, 0xbf, 0xf3, 0xd1, 0xc1, 0x1f, 0x59, 0x54, 0xf0, 0xa7, 0x9d,
-    0x1d, 0x3c, 0x5f, 0x28, 0x33, 0x78, 0x9f, 0x85, 0x58, 0x8b, 0x6e, 0xad, 0x23, 0x17, 0x60, 0xc0,
-    0x10, 0x45, 0x16, 0xa0, 0xe4, 0x44, 0x7c, 0x2c, 0x94, 0x1d, 0x24, 0x5a, 0xbf, 0x00, 0x95, 0x17,
-    0x7c, 0x1c, 0x55, 0x40, 0x73, 0x63, 0x44, 0x0a, 0x74, 0xe4, 0xbc, 0xe0, 0xec, 0x7a, 0x11, 0xdd,
-    0x39, 0x2f, 0x24, 0xb8, 0x49, 0xd1, 0x4c, 0x1a, 0xba, 0x43, 0x22, 0x74, 0x0a, 0x32, 0x9a, 0xaf,
-    0x00, 0x0d, 0x66, 0xd3, 0x69, 0x65, 0x6a, 0x1f, 0x80, 0x41, 0xc5, 0xe1, 0xdc, 0xc8, 0x5f, 0xac,
-    0xa1, 0x80, 0x90, 0xee, 0x85, 0x78, 0x1f, 0x22, 0x7c, 0x1b, 0x69, 0xdb, 0x35, 0x87, 0x8c, 0xcb,
-    0x31, 0x69, 0x7c, 0xbf, 0x99, 0xa3, 0xe3, 0xf9, 0x1c, 0xce, 0x82, 0x3b, 0xd7, 0x2f, 0xa5, 0x83,
-    0xfa, 0xb9, 0x31, 0x62, 0xf2, 0x96, 0x85, 0xb2, 0x04, 0x94, 0x8c, 0x18, 0xff, 0xcd, 0x2a, 0x42,
-    0x0c, 0x02, 0xa9, 0x4f, 0xbd, 0x6a, 0xc0, 0x82, 0x9b, 0x4d, 0x25, 0x80, 0x87, 0x22, 0x5b, 0xe0,
-    0x6b, 0x77, 0xba, 0xa3, 0x40, 0xdd, 0x03, 0x9d, 0x19, 0x11, 0xbc, 0x0d, 0xc6, 0x89, 0x35, 0xd6,
-    0xa3, 0x88, 0xaf, 0x17, 0xad, 0x3d, 0xb0, 0x26, 0x42, 0x61, 0xb0, 0x72, 0x08, 0x9a, 0xdd, 0x42,
-    0x83, 0x92, 0x45, 0x37, 0x3a, 0x97, 0x93, 0x55, 0xb4, 0x38, 0xa3, 0x42, 0x2a, 0x62, 0xe2, 0x67,
-    0x4d, 0x1b, 0x98, 0xdb, 0x5d, 0xff, 0xd4, 0xfe, 0x02, 0x80, 0x81, 0x0c, 0xaa, 0x06, 0xa2, 0x96,
-    0xab, 0x20, 0xe4, 0x96, 0xeb, 0xe9, 0x1a, 0xae, 0xb2, 0xae, 0x0c, 0xbf, 0x42, 0xa4, 0xd6, 0x02,
-    0x16, 0x39, 0x4a, 0x9c, 0x73, 0x33, 0xb6, 0xcf, 0xf8, 0x48, 0x1e, 0xf9, 0xe7, 0xdf, 0xb3, 0xb4,
-    0xa1, 0x85, 0x23, 0x81, 0x62, 0xe0, 0x4b, 0xf2, 0x76, 0xb9, 0xc4, 0xf2, 0x61, 0x56, 0x7d, 0xba,
-    0x86, 0x01, 0x17, 0x86, 0x48, 0xe0, 0x14, 0x61, 0x07, 0xbf, 0x5c, 0x58, 0xd0, 0xe1, 0xfc, 0xb3,
-    0x56, 0x1b, 0xe7, 0x46, 0x9e, 0x15, 0x11, 0x19, 0x12, 0x39, 0x57, 0xf8, 0xdd, 0xe0, 0x84, 0xf8,
-    0x0c, 0x6e, 0x27, 0xd3, 0x9f, 0x22, 0xd5, 0x50, 0x71, 0x48, 0xa2, 0x8a, 0xe0, 0xe4, 0xa5, 0x85,
-    0x5a, 0xed, 0x00, 0x24, 0xb2, 0x7f, 0x9f, 0xa5, 0x41, 0xf5, 0xc1, 0xc9, 0x55, 0x47, 0xf1, 0xf5,
-    0x63, 0xb8, 0xc9, 0x8b, 0x17, 0x70, 0x63, 0xd5, 0xee, 0xef, 0xc9, 0x78, 0x47, 0xc1, 0x9d, 0x13,
-    0x00, 0xc4, 0xf2, 0x8d, 0xf6, 0x18, 0x7f, 0xe7, 0xcc, 0xab, 0x86, 0xcd, 0xa8, 0x1b, 0x13, 0xd1,
-    0x4c, 0x33, 0x96, 0x98, 0x81, 0xbc, 0x44, 0x1a, 0x0f, 0x91, 0x21, 0xab, 0x98, 0xaa, 0x92, 0x04,
-    0xaf, 0x32, 0x75, 0x1b, 0x26, 0xf0, 0xf5, 0x60, 0xe0, 0x16, 0x60, 0xfe, 0xb9, 0xf8, 0x78, 0x13,
-    0xb7, 0xd0, 0xb6, 0x9d, 0xc3, 0x00, 0x55, 0x46, 0x59, 0x5d, 0xf0, 0xe1, 0x38, 0x5a, 0xcb, 0x01,
-    0xec, 0xa2, 0x02, 0x31, 0xf5, 0x0c, 0xff, 0xae, 0xe2, 0xcd, 0xa3, 0x09, 0x70, 0x00, 0x7b, 0x78,
-    0x29, 0x86, 0x05, 0x86, 0x62, 0x18, 0x05, 0x84, 0x09, 0xaa, 0x58, 0x30, 0xff, 0x9d, 0x10, 0x2d,
-    0xb7, 0x79, 0xfb, 0xd7, 0x83, 0x92, 0x07, 0x6e, 0x6c, 0xde, 0x02, 0xf4, 0xb4, 0xa2, 0xb7, 0x01,
-    0xc8, 0x8b, 0x55, 0x14, 0x2e, 0xbd, 0x6f, 0xf4, 0x4b, 0x75, 0xc6, 0x00, 0x0e, 0x48, 0x1b, 0x56,
-    0x5e, 0x8d, 0xaf, 0x5d, 0x6f, 0xbd, 0x88, 0xc4, 0x0c, 0x2e, 0x04, 0xfa, 0x69, 0x06, 0x53, 0xd4,
-    0x50, 0x15, 0xb2, 0x40, 0x82, 0x51, 0x9a, 0xb2, 0x54, 0x73, 0x15, 0x55, 0xf2, 0x91, 0x3c, 0xd3,
-    0x92, 0x66, 0xf4, 0xf0, 0x92, 0xef, 0xf8, 0x97, 0x26, 0x7c, 0x1d, 0xfa, 0x93, 0xbf, 0x08, 0x0f,
-    0x9c, 0x30, 0x21, 0x62, 0xb5, 0x44, 0xab, 0xb4, 0x00, 0x8a, 0x85, 0x83, 0xfa, 0x1e, 0xbb, 0xc3,
-    0x54, 0x72, 0x80, 0x18, 0xca, 0xa7, 0xae, 0xe2, 0xf1, 0x48, 0x07, 0xfa, 0xd3, 0x43, 0x56, 0x11,
-    0x4a, 0x3b, 0x02, 0x31, 0x66, 0x0c, 0x7f, 0xe7, 0x4c, 0x9b, 0x7d, 0x94, 0xcc, 0x90, 0x46, 0x06,
-    0x36, 0x94, 0x70, 0x9a, 0x58, 0x59, 0x1e, 0xe0, 0x5e, 0x9b, 0x15, 0xe2, 0xae, 0x23, 0x1a, 0x52,
-    0xbe, 0xfd, 0xfb, 0x15, 0xd7, 0xce, 0xc5, 0xcf, 0x42, 0x78, 0x00, 0x36, 0xc8, 0x88, 0xea, 0x54,
-    0x1c, 0x94, 0x7f, 0xbc, 0x2e, 0x7b, 0x44, 0x5c, 0x09, 0x64, 0xdb, 0xf0, 0x63, 0xb8, 0xcb, 0x8b,
-    0x3f, 0x5b, 0x8f, 0x50, 0xdf, 0xbd, 0xc8, 0xec, 0x7c, 0xf1, 0xde, 0xd0, 0xc0, 0xf3, 0x89, 0x03,
-    0x62, 0x4e, 0xba, 0x88, 0x65, 0x3d, 0xc0, 0xb1, 0x9c, 0xc1, 0xf6, 0xd2, 0xa3, 0xa4, 0x8c, 0x32,
-    0x60, 0x9a, 0x18, 0x36, 0xcf, 0x9a, 0xe6, 0x45, 0x57, 0x66, 0xe6, 0x85, 0x01, 0x66, 0x51, 0x96,
-    0x61, 0xec, 0xb2, 0xbf, 0xcf, 0x28, 0x97, 0x8d, 0x5d, 0xd5, 0x6c, 0x65, 0xc1, 0x23, 0x1f, 0x22,
-    0x05, 0xd6, 0x2a, 0x18, 0x3f, 0x2d, 0xe0, 0x12, 0xc3, 0x78, 0xbf, 0xf3, 0x85, 0x79, 0x96, 0x8b,
-    0x87, 0x75, 0x48, 0x33, 0x95, 0x9b, 0x82, 0xb2, 0x58, 0x4a, 0xc1, 0x80, 0x47, 0x46, 0x46, 0x29,
-    0xc1, 0x80, 0x03, 0xa8, 0xc7, 0x3e, 0x00, 0xbe, 0xb2, 0xc3, 0x14, 0x06, 0x48, 0x2e, 0xfc, 0x3b,
-    0x95, 0x0c, 0x7f, 0xe7, 0xf0, 0x3b, 0xec, 0x0a, 0x9e, 0x00, 0x97, 0x8a, 0xa2, 0xe9, 0x55, 0x2d,
-    0x38, 0x3a, 0xfb, 0xe7, 0x7e, 0x77, 0x72, 0x6a, 0xc7, 0x1b, 0x4e, 0x18, 0xbc, 0x20, 0x4b, 0x6b,
-    0x5f, 0xbf, 0xf1, 0x0e, 0x11, 0x68, 0xf8, 0xc9, 0x00, 0xd6, 0x90, 0x9b, 0xbc, 0x00, 0xca, 0xbe,
-    0xf2, 0x7a, 0xe1, 0x4f, 0xc6, 0x8d, 0xb6, 0x6b, 0x7d, 0x77, 0x7b, 0x32, 0x69, 0x56, 0x50, 0xc4,
-    0x50, 0x35, 0xc0, 0xe0, 0xe8, 0xc6, 0xf5, 0x77, 0x73, 0xc0, 0x00, 0xb3, 0xe1, 0x0f, 0x71, 0x0b,
-    0x80, 0x35, 0x09, 0x7f, 0x44, 0x2d, 0xbb, 0x4f, 0x70, 0xae, 0x69, 0x99, 0x19, 0xed, 0xaa, 0xe3,
-    0x31, 0x3e, 0x37, 0xcb, 0x95, 0x9a, 0xc0, 0x05, 0xbe, 0x7d, 0xb4, 0xed, 0x72, 0xd7, 0x41, 0x82,
-    0xa4, 0x05, 0x19, 0x21, 0xc9, 0x38, 0x28, 0xb0, 0xd3, 0xfb, 0x01, 0x51, 0x86, 0x86, 0x7f, 0xd6,
-    0xa6, 0x86, 0x60, 0x13, 0x83, 0x98, 0xd1, 0xaf, 0xb9, 0x4f, 0x10, 0x73, 0xb6, 0x28, 0x2f, 0xf2,
-    0xda, 0x53, 0x6d, 0x19, 0xbd, 0x78, 0x77, 0xf7, 0x05, 0x36, 0x7f, 0x22, 0x78, 0x2d, 0x14, 0x62,
-    0xc5, 0xdd, 0x5f, 0xa8, 0x12, 0x5d, 0x7f, 0x80, 0x9d, 0xd9, 0x41, 0x67, 0xd4, 0x30, 0x1a, 0xbe,
-    0x59, 0xf8, 0xb1, 0xdc, 0xa4, 0xc5, 0xbf, 0xb3, 0xc7, 0xaf, 0x05, 0x5e, 0xc1, 0xc9, 0x26, 0x9f,
-    0x89, 0x3a, 0x1a, 0x41, 0x6c, 0xd1, 0xc2, 0x07, 0x69, 0xcf, 0xff, 0xaf, 0xa1, 0x2d, 0x5f, 0xaa,
-    0xeb, 0xc0, 0xd5, 0x3d, 0x34, 0x1b, 0x01, 0xdd, 0x08, 0xdc, 0x15, 0xdc, 0x6f, 0xda, 0x44, 0x35,
-    0x95, 0x05, 0xb0, 0x13, 0x58, 0x66, 0x7e, 0xa8, 0x4d, 0x55, 0xb0, 0xfe, 0xc8, 0x49, 0xcd, 0xe3,
-    0x5e, 0xfb, 0x9a, 0x1a, 0x9b, 0xd7, 0x3f, 0xe9, 0x4c, 0xe8, 0x1d, 0x0b, 0x67, 0xa5, 0x0d, 0x82,
-    0x17, 0x4c, 0x35, 0x5e, 0x86, 0xa1, 0x22, 0x20, 0xf1, 0x23, 0x9c, 0x55, 0x21, 0xb3, 0xf0, 0xc0,
-    0x67, 0x4c, 0xc3, 0xc3, 0xe0, 0xcf, 0x44, 0xeb, 0xcc, 0x8c, 0xb3, 0x0a, 0x77, 0x0c, 0x76, 0x22,
-    0x95, 0xe7, 0xc1, 0xa5, 0xb1, 0xdf, 0x82, 0xb8, 0x00, 0xb0, 0xae, 0x58, 0x0c, 0xff, 0x02, 0xf1,
-    0xe1, 0x0c, 0x2b, 0x62, 0x0f, 0xc7, 0x18, 0xcd, 0xbe, 0x77, 0xee, 0x15, 0x49, 0x53, 0xa1, 0x14,
-    0xc9, 0x6e, 0x98, 0xc1, 0xef, 0xd1, 0xbe, 0x0d, 0xd7, 0x5a, 0xfe, 0x61, 0xdf, 0x11, 0xc5, 0x34,
-    0x2a, 0xfe, 0x58, 0x39, 0x46, 0x8d, 0x59, 0x3c, 0x58, 0xcd, 0x0b, 0x01, 0x55, 0x00, 0x49, 0xbd,
-    0xfe, 0x44, 0x37, 0x8c, 0xae, 0x12, 0x7b, 0x25, 0x03, 0x70, 0x09, 0x5e, 0xc0, 0xb6, 0x8b, 0x72,
-    0x00, 0xb0, 0x8d, 0x83, 0xd5, 0x10, 0xbe, 0xda, 0x38, 0x42, 0xaf, 0x8b, 0xb1, 0xcc, 0x54, 0x16,
-    0x62, 0x86, 0xff, 0x9c, 0x03, 0xbf, 0x8d, 0x9b, 0x1e, 0x1b, 0x3d, 0x1c, 0x6b, 0x78, 0xd7, 0x08,
-    0xfc, 0xc7, 0x24, 0xdb, 0xc6, 0xff, 0xed, 0x11, 0xa7, 0x86, 0x14, 0x47, 0x0e, 0x5e, 0xf6, 0x96,
-    0xb1, 0x79, 0x03, 0x4a, 0x0b, 0x53, 0xfd, 0x93, 0x47, 0x1b, 0x6c, 0x25, 0x6a, 0x90, 0xf4, 0x21,
-    0x00, 0x7b, 0xd6, 0x96, 0xbe, 0x73, 0x3c, 0xe2, 0xd4, 0xe2, 0xf6, 0xd2, 0x23, 0xe9, 0x0d, 0xb2,
-    0x4a, 0x2f, 0xe6, 0x1f, 0x3d, 0x89, 0x42, 0x79, 0x62, 0xbf, 0x02, 0xf0, 0x86, 0x0c, 0xd7, 0x3b,
-    0x04, 0x95, 0x44, 0x18, 0x5c, 0xe4, 0x6d, 0x7c, 0x8d, 0xb4, 0x16, 0x55, 0xa3, 0xf3, 0xa9, 0xf7,
-    0x4b, 0x54, 0x20, 0x3d, 0x28, 0x39, 0x97, 0x24, 0x5c, 0x00, 0x99, 0xdc, 0x7f, 0xe7, 0x92, 0x82,
-    0x5b, 0x83, 0x3b, 0x81, 0x8e, 0xe3, 0x4e, 0x2d, 0x7c, 0x03, 0x1e, 0xb2, 0xcd, 0x7b, 0x39, 0x22,
-    0x8d, 0x58, 0xfe, 0xba, 0xc5, 0xa2, 0xe3, 0x6c, 0xc3, 0xf2, 0x72, 0x3a, 0x30, 0xbc, 0xba, 0x69,
-    0x06, 0xf9, 0x46, 0x21, 0xa1, 0x80, 0x83, 0x62, 0xaa, 0xa0, 0xd8, 0x8b, 0xe7, 0x27, 0x28, 0xd5,
-    0xd7, 0xec, 0xae, 0x15, 0xfd, 0x92, 0x63, 0x64, 0x1c, 0x04, 0xab, 0x23, 0x1b, 0x77, 0xf1, 0x5e,
-    0x34, 0x93, 0x01, 0xc9, 0x06, 0xdb, 0xcf, 0x98, 0xc4, 0x39, 0x31, 0xbf, 0x70, 0xde, 0x69, 0x47,
-    0x6f, 0x75, 0xe0, 0x81, 0x0a, 0xea, 0x89, 0x26, 0x4d, 0xc8, 0x3a, 0xc0, 0xf0, 0xbc, 0x06, 0xd7,
-    0xfb, 0xbc, 0xc7, 0x97, 0x7e, 0x4b, 0xfb, 0xcd, 0x8b, 0xfe, 0xee, 0xdb, 0xd5, 0x54, 0xbe, 0x5d,
-    0x05, 0xf3, 0x8f, 0x7e, 0x8e, 0xe9, 0x78, 0x1f, 0xcf, 0xfa, 0xae, 0x21, 0x91, 0x15, 0x80, 0x17,
-    0x78, 0x55, 0xc4, 0x21, 0x44, 0xdd, 0xbf, 0xaf, 0x6f, 0xd2, 0x11, 0xcb, 0x9a, 0x8a, 0x34, 0xa5,
-    0xf0, 0x2d, 0x51, 0x42, 0x31, 0xce, 0xf2, 0x8e, 0xa4, 0x12, 0x16, 0x71, 0xb6, 0x8a, 0x49, 0x54,
-    0x6b, 0x0e, 0x79, 0xa6, 0x8f, 0xf2, 0xe3, 0x00, 0x3b, 0x4b, 0xaf, 0xcf, 0x02, 0x41, 0xeb, 0xf0,
-    0x75, 0xd1, 0xb7, 0xb8, 0x92, 0x86, 0xfd, 0x25, 0x08, 0x7c, 0xb3, 0xdf, 0x8b, 0x5e, 0x66, 0xbd,
-    0x1c, 0x91, 0x46, 0xac, 0x5d, 0xbd, 0xe3, 0xaa, 0x15, 0xf1, 0xb7, 0x04, 0xff, 0x31, 0x65, 0xbb,
-    0x81, 0x4d, 0x9b, 0x9d, 0x0d, 0x02, 0xfc, 0xc2, 0x91, 0x70, 0x0b, 0x70, 0x1d, 0xa4, 0x0a, 0xa2,
-    0xba, 0x60, 0x58, 0x81, 0x5d, 0x13, 0xd1, 0xbe, 0xe7, 0x81, 0x9a, 0x4c, 0xed, 0x81, 0x72, 0x22,
-    0xaa, 0xa9, 0x66, 0xc6, 0x12, 0x51, 0x58, 0x72, 0x5a, 0x13, 0xde, 0xcc, 0x18, 0xab, 0x75, 0x67,
-    0xbe, 0x28, 0x7c, 0x9e, 0x88, 0x2d, 0x42, 0xc8, 0xe2, 0xe0, 0x61, 0x62, 0xe0, 0x84, 0x69, 0xef,
-    0xdf, 0x35, 0x5a, 0xf9, 0xfd, 0x19, 0x17, 0x4d, 0xf2, 0x37, 0x29, 0xfc, 0xe0, 0x39, 0x17, 0x6f,
-    0xab, 0xff, 0x8d, 0x8a, 0x2d, 0xa2, 0xd6, 0x55, 0x37, 0x8c, 0xa1, 0x43, 0xb9, 0x9f, 0x90, 0x8d,
-    0x7c, 0x1c, 0xb7, 0xa5, 0x5e, 0x10, 0xdf, 0xab, 0x75, 0x49, 0xc1, 0xc9, 0xd4, 0xcf, 0xaf, 0x63,
-    0x24, 0x99, 0xab, 0x07, 0xef, 0x51, 0x9b, 0xdb, 0xfa, 0xc2, 0xe3, 0x5f, 0xa6, 0x19, 0x21, 0x83,
-    0x04, 0xed, 0x00, 0x92, 0x47, 0xa6, 0xd5, 0xc0, 0x7f, 0x9c, 0x30, 0x13, 0x78, 0x16, 0x44, 0x40,
-    0xaa, 0xae, 0xef, 0xf0, 0x4b, 0x13, 0x46, 0x1f, 0x05, 0x64, 0x11, 0xd3, 0x13, 0xdb, 0x66, 0x86,
-    0x30, 0x31, 0x77, 0x4d, 0xf0, 0x32, 0x88, 0xa4, 0x8d, 0x9c, 0xf7, 0xef, 0x57, 0x80, 0xc3, 0x84,
-    0x27, 0x34, 0x9c, 0x1b, 0x6d, 0x9f, 0x98, 0x11, 0xf8, 0x63, 0xc9, 0xfd, 0x2c, 0x29, 0xf1, 0xa8,
-    0x4e, 0x19, 0x5c, 0x41, 0x00, 0xa5, 0x17, 0xcc, 0x0c, 0x91, 0x5a, 0xbe, 0x93, 0x06, 0x84, 0xdb,
-    0x0c, 0xa3, 0x35, 0x4f, 0xdc, 0xc5, 0x35, 0xa7, 0x61, 0x45, 0xf7, 0x84, 0xd3, 0xe1, 0xae, 0x6c,
-    0x02, 0x55, 0x94, 0x30, 0x79, 0xc3, 0xaa, 0x87, 0xab, 0x6e, 0xf8, 0x70, 0x80, 0x7b, 0xea, 0xa3,
-    0xd7, 0xb5, 0x92, 0x2c, 0xd5, 0x83, 0xac, 0x68, 0xb6, 0x03, 0xfe, 0xb2, 0x5c, 0xd7, 0xa1, 0x85,
-    0x04, 0x04, 0x54, 0x42, 0x1a, 0xc7, 0x80, 0x76, 0xaa, 0x7b, 0x30, 0x08, 0xcb, 0x0c, 0x87, 0x08,
-    0x2e, 0x19, 0x10, 0x60, 0x05, 0x53, 0xd9, 0xa9, 0xe0, 0xc8, 0xb4, 0x49, 0x51, 0xc5, 0xd0, 0x8e,
-    0x33, 0x8c, 0xc0, 0x9c, 0x70, 0x02, 0x9b, 0x58, 0xdb, 0xf0, 0x43, 0x99, 0x75, 0x39, 0xb6, 0x78,
-    0x99, 0xd1, 0xe3, 0xe9, 0x8f, 0x80, 0x92, 0x12, 0x9b, 0xa0, 0xc1, 0x60, 0xdf, 0x01, 0x64, 0x7c,
-    0x1f, 0x07, 0xc4, 0x3e, 0xe3, 0xbb, 0x00, 0x58, 0x0f, 0x00, 0x49, 0x3b, 0x41, 0x44, 0xa4, 0x54,
-    0x88, 0xd5, 0xd7, 0xad, 0xcd, 0x0c, 0x2a, 0x26, 0x50, 0xaa, 0x8b, 0x0b, 0xf2, 0xbb, 0x16, 0x74,
-    0xcb, 0x6a, 0x37, 0xe0, 0x72, 0x2d, 0x23, 0x11, 0xb2, 0xae, 0x28, 0xa4, 0x30, 0xc0, 0x18, 0x0b,
-    0xda, 0x55, 0x63, 0x69, 0xc5, 0x95, 0xce, 0xa2, 0x7d, 0xc8, 0xb5, 0x2a, 0x75, 0xad, 0x00, 0x49,
-    0x89, 0x6e, 0x0e, 0x4a, 0x38, 0xe6, 0x24, 0x88, 0xab, 0x07, 0xef, 0x51, 0xb5, 0x5f, 0x80, 0xec,
-    0x87, 0xdb, 0x87, 0x0a, 0xb6, 0xcf, 0xc0, 0x20, 0x1c, 0x10, 0x87, 0x01, 0x18, 0x05, 0xd3, 0x8c,
-    0x02, 0x6c, 0x43, 0x0c, 0x18, 0x32, 0x3f, 0xb7, 0x45, 0xb6, 0x24, 0xb8, 0xe1, 0x61, 0x1b, 0x73,
-    0xd7, 0x3c, 0x67, 0x95, 0x57, 0x69, 0x3c, 0x39, 0x5e, 0x3a, 0xc6, 0x44, 0x28, 0xc4, 0x95, 0x0e,
-    0x5d, 0x33, 0x85, 0x43, 0x8d, 0xd8, 0xc6, 0xf1, 0x2d, 0x51, 0x31, 0x3c, 0x7b, 0xdb, 0xb6, 0xb5,
-    0x94, 0xe6, 0xbd, 0x75, 0xd6, 0x05, 0x2a, 0xf4, 0xfa, 0x6b, 0xbc, 0x0a, 0xa7, 0xda, 0x54, 0xe4,
-    0x6b, 0xc9, 0x72, 0x71, 0x8b, 0x54, 0xa3, 0x93, 0x79, 0x0a, 0x7d, 0xd5, 0x81, 0x9f, 0xc2, 0x82,
-    0x77, 0x1f, 0xd8, 0xe5, 0x5f, 0xd2, 0xee, 0xbe, 0x84, 0x19, 0x10, 0x04, 0xfc, 0x6c, 0x71, 0xa6,
-    0xc2, 0x2d, 0xd3, 0xed, 0x1c, 0xbd, 0x9e, 0xc6, 0x97, 0x82, 0xb8, 0x10, 0xb7, 0xae, 0x9a, 0xc0,
-    0x65, 0x3e, 0x6f, 0x6d, 0xce, 0x81, 0xc6, 0xbc, 0x96, 0xfc, 0x67, 0xd2, 0xfc, 0x0d, 0x95, 0x9e,
-    0x05, 0xbd, 0xfb, 0xe6, 0x74, 0x3a, 0x56, 0x36, 0xec, 0x0f, 0x69, 0xca, 0xcf, 0xcd, 0xa9, 0xef,
-    0xb3, 0x86, 0x63, 0xb3, 0xed, 0xde, 0x0b, 0x66, 0x5f, 0x3f, 0xaf, 0x80, 0x7f, 0xce, 0x92, 0x4d,
-    0xaf, 0x83, 0x11, 0xf6, 0x83, 0xac, 0x4f, 0xc1, 0x49, 0x2b, 0x16, 0x40, 0x8f, 0x0b, 0xcb, 0xec,
-    0x6e, 0xfc, 0xe4, 0x8c, 0xcf, 0xe0, 0x94, 0xd3, 0xac, 0x1c, 0xcd, 0x46, 0x58, 0x0e, 0xa6, 0x44,
-    0x47, 0x00, 0xb6, 0x94, 0x9f, 0xa7, 0x25, 0x9a, 0xb8, 0x6b, 0xd6, 0x93, 0x5b, 0xb5, 0xb7, 0x59,
-    0xf0, 0x2c, 0x2c, 0x90, 0x61, 0x4a, 0xbd, 0x52, 0xea, 0x19, 0x97, 0x0a, 0x91, 0xb1, 0x75, 0x05,
-    0xd4, 0xdd, 0x3d, 0x64, 0x5b, 0x50, 0x70, 0x7c, 0xb7, 0x45, 0x01, 0xdf, 0x37, 0x83, 0x1a, 0xdf,
-    0xc0, 0x76, 0x81, 0x2b, 0xc1, 0x63, 0x62, 0x5b, 0x35, 0xb1, 0x89, 0xa7, 0xf2, 0x17, 0xc3, 0xd7,
-    0x16, 0xf8, 0x7c, 0x61, 0x96, 0x4b, 0x86, 0x93, 0x7c, 0x0d, 0x1c, 0x6c, 0x3e, 0x87, 0x27, 0xb5,
-    0x09, 0xb0, 0xf7, 0x2c, 0x88, 0xef, 0x50, 0xc9, 0x4a, 0x36, 0xc6, 0x47, 0xfa, 0xcb, 0xa6, 0x3f,
-    0x70, 0xc6, 0xbf, 0xe8, 0xed, 0xae, 0x38, 0x0d, 0xa4, 0xe0, 0x4a, 0xcb, 0xe1, 0x6a, 0xa3, 0x0d,
-    0x78, 0x5a, 0x93, 0x43, 0x6a, 0x96, 0x52, 0xad, 0x23, 0x80, 0x5d, 0x10, 0x5d, 0x34, 0xa5, 0x6c,
-    0xb5, 0xca, 0xcb, 0x7c, 0x23, 0xbf, 0x04, 0xa9, 0xde, 0xe0, 0x72, 0xbc, 0x32, 0x5b, 0xc0, 0x09,
-    0x4b, 0x46, 0x42, 0x0e, 0x94, 0x93, 0x8c, 0x9d, 0x9e, 0xa3, 0xdf, 0xa7, 0xf8, 0x7a, 0xc5, 0x79,
-    0x39, 0x53, 0xe9, 0x42, 0xe9, 0x56, 0xff, 0xcf, 0x48, 0x5a, 0x8a, 0xc0, 0x31, 0x79, 0x95, 0x36,
-    0xf4, 0x3e, 0x4e, 0xdb, 0x6b, 0x3f, 0x42, 0x22, 0xd3, 0x73, 0xc0, 0xdd, 0x31, 0x4b, 0xad, 0x87,
-    0x6f, 0xf0, 0xdf, 0x3c, 0xff, 0xb5, 0xb0, 0xdc, 0x3d, 0xe5, 0x36, 0x05, 0x07, 0xf7, 0xf2, 0x7a,
-    0x08, 0x8b, 0xc6, 0xfd, 0x89, 0x50, 0xa6, 0xbd, 0xfc, 0x27, 0x5b, 0x2c, 0x0b, 0xec, 0x25, 0x35,
-    0x49, 0x83, 0x1a, 0xc0, 0x11, 0x9a, 0x34, 0x4e, 0x12, 0x3c, 0x88, 0x87, 0xf3, 0xde, 0x60, 0x1e,
-    0xbe, 0x38, 0xf5, 0x8c, 0x24, 0x9a, 0x37, 0x07, 0xc7, 0xf7, 0x94, 0xa9, 0x17, 0x3d, 0xac, 0x77,
-    0xbc, 0x88, 0x78, 0x16, 0x3d, 0x43, 0x9a, 0xf6, 0x72, 0x51, 0x1a, 0xb0, 0x95, 0xfb, 0xae, 0x03,
-    0xc0, 0x26, 0x91, 0x60, 0x5d, 0x40, 0xd3, 0x61, 0xaa, 0x21, 0x01, 0x18, 0xd5, 0x86, 0xb5, 0x78,
-    0x01, 0x4c, 0xf8, 0x4f, 0x52, 0xbd, 0xe8, 0x7d, 0x99, 0x03, 0x7d, 0x8d, 0xce, 0x1a, 0x7d, 0xce,
-    0xc2, 0xb2, 0xfb, 0x02, 0xfc, 0x67, 0xbf, 0x1b, 0xaa, 0x8c, 0x35, 0x26, 0x15, 0x2d, 0x84, 0x96,
-    0xc4, 0xac, 0xf6, 0x5b, 0xc0, 0x03, 0x77, 0x6f, 0xc2, 0xe4, 0xbd, 0xe1, 0x25, 0xa6, 0x84, 0xaf,
-    0x20, 0xab, 0x54, 0xf1, 0x71, 0x05, 0x83, 0x91, 0x98, 0x81, 0x7b, 0x77, 0xaa, 0xbd, 0x2e, 0xae,
-    0x3f, 0xd6, 0x27, 0x2e, 0xcd, 0x3d, 0x61, 0x19, 0xb1, 0x24, 0xc0, 0x2f, 0xbd, 0x2b, 0x36, 0x05,
-    0xd7, 0x06, 0x3d, 0xcf, 0x27, 0x8e, 0x50, 0x82, 0x16, 0xa6, 0xc0, 0x09, 0xa3, 0x9b, 0x8f, 0x4b,
-    0x4b, 0x6c, 0x56, 0x0e, 0x97, 0xb3, 0xf5, 0x3a, 0xe0, 0x38, 0xcf, 0x72, 0x93, 0x28, 0xa3, 0x85,
-    0x6b, 0x5f, 0xfe, 0x61, 0x10, 0x77, 0xb0, 0x15, 0x92, 0x4c, 0x00, 0x86, 0x92, 0xc3, 0x09, 0x68,
-    0xb9, 0xc8, 0xc5, 0x78, 0x01, 0xcb, 0x9e, 0x2f, 0xf4, 0xf3, 0xd8, 0x41, 0xcd, 0x88, 0xd6, 0x8b,
-    0xae, 0x5b, 0x1e, 0x54, 0x7e, 0xf1, 0xec, 0xa0, 0xef, 0x75, 0xd6, 0xd9, 0xce, 0x1f, 0x6c, 0x8e,
-    0xe6, 0x5a, 0x15, 0x46, 0x1e, 0x88, 0x2e, 0x2b, 0xbb, 0xb9, 0xb4, 0xbf, 0x4f, 0x63, 0xf9, 0x5e,
-    0x8e, 0xcd, 0xa3, 0xfd, 0x11, 0x53, 0x2c, 0xf3, 0x54, 0x57, 0x3a, 0xf7, 0x88, 0x18, 0x57, 0x11,
-    0x9d, 0x89, 0xb5, 0xff, 0xca, 0xb1, 0x66, 0x22, 0xbe, 0x24, 0x78, 0x13, 0x69, 0x47, 0x16, 0xa3,
-    0xc0, 0x09, 0x6e, 0x9f, 0x0f, 0xed, 0x5f, 0x2f, 0xab, 0x9c, 0x95, 0xaa, 0x6f, 0xea, 0x00, 0xcf,
-    0x55, 0xde, 0xbd, 0x8f, 0xc9, 0x18, 0xc7, 0xc1, 0x41, 0x9b, 0xb0, 0x72, 0x35, 0x1a, 0x0f, 0xb9,
-    0x19, 0x11, 0x97, 0x85, 0x07, 0xa5, 0xe1, 0xc0, 0xb2, 0xb3, 0x0c, 0x74, 0x2f, 0x33, 0xb0, 0xc0,
-    0xe9, 0xe2, 0x48, 0xe0, 0x42, 0xb0, 0x23, 0xe5, 0x58, 0x93, 0x60, 0x17, 0x30, 0xa8, 0x13, 0x2f,
-    0x3c, 0x48, 0x18, 0x3b, 0xb1, 0x1b, 0x16, 0xc1, 0x57, 0xd6, 0xed, 0xd5, 0x75, 0x78, 0x01, 0xc1,
-    0x23, 0x2f, 0x8e, 0xf3, 0x91, 0xcd, 0x72, 0x5e, 0x12, 0x53, 0x8d, 0xfa, 0xc0, 0x7d, 0xc5, 0x93,
-    0x3f, 0xcf, 0xff, 0xf1, 0xf5, 0xc3, 0x23, 0xeb, 0x2e, 0x4b, 0x92, 0x9a, 0xe5, 0xb8, 0x2f, 0x6c,
-    0xc3, 0x47, 0x07, 0xec, 0x83, 0x0d, 0xff, 0x9d, 0xa9, 0x4d, 0x09, 0x9d, 0x75, 0xb4, 0x24, 0x16,
-    0xaf, 0x00, 0xe7, 0xc6, 0x1a, 0x8f, 0xaf, 0x64, 0x47, 0xa9, 0x50, 0xbf, 0x08, 0xfb, 0x2c, 0xf8,
-    0x77, 0xff, 0x14, 0x6d, 0x58, 0xe0, 0x74, 0xb3, 0x02, 0x50, 0xa6, 0x0c, 0xaa, 0xb1, 0x0b, 0x2b,
-    0x1d, 0x15, 0xa0, 0x4d, 0xfc, 0x88, 0xb7, 0x0f, 0x57, 0xf4, 0x92, 0xcc, 0x50, 0x3e, 0x35, 0x09,
-    0x0a, 0x18, 0xab, 0xcc, 0xf8, 0x72, 0xfd, 0xf7, 0x9f, 0x1b, 0xdd, 0x5d, 0x2b, 0xcb, 0xcd, 0xbd,
-    0xb7, 0x1d, 0x96, 0xdd, 0xb0, 0xd3, 0xdb, 0x64, 0xbe, 0x33, 0xa8, 0x92, 0x01, 0x0f, 0x2c, 0xe5,
-    0x85, 0xd2, 0x79, 0x9d, 0x23, 0xfe, 0xf8, 0xda, 0x1d, 0x34, 0xd6, 0x95, 0x7f, 0x93, 0x4d, 0x7e,
-    0xec, 0x4f, 0x03, 0xfe, 0xc1, 0x9d, 0xc0, 0x54, 0xe0, 0xca, 0xea, 0x95, 0x4d, 0x34, 0x82, 0x8c,
-    0x94, 0xd7, 0x87, 0xd2, 0x24, 0x5a, 0x1f, 0x00, 0x9e, 0xd8, 0x85, 0xe3, 0xf3, 0xe0, 0x1c, 0xfb,
-    0x9b, 0xf0, 0x63, 0xb8, 0x64, 0x45, 0x70, 0x32, 0x3d, 0x69, 0x9a, 0xf6, 0x72, 0x59, 0x1a, 0xb0,
-    0x3a, 0xb5, 0xfb, 0x60, 0x38, 0x05, 0x0e, 0x52, 0x17, 0x3f, 0xfc, 0x09, 0x14, 0xee, 0x18, 0x04,
-    0xe3, 0x76, 0x13, 0xad, 0x09, 0x5c, 0x2c, 0x2c, 0x5c, 0xed, 0x5d, 0xd6, 0xae, 0xe0, 0xfb, 0x2d,
-    0x18, 0x47, 0x11, 0xb2, 0x15, 0x74, 0x67, 0x94, 0x05, 0x83, 0x9f, 0xe3, 0xd5, 0x53, 0x55, 0x9d,
-    0x72, 0xbe, 0xf0, 0x2f, 0x69, 0x48, 0x6f, 0x63, 0xf9, 0x5e, 0xf2, 0xc0, 0xb0, 0xee, 0x6f, 0x66,
-    0x51, 0x53, 0xbb, 0x4d, 0x28, 0xb6, 0x11, 0x36, 0x16, 0xc8, 0xea, 0xfd, 0x5e, 0xba, 0xda, 0x1e,
-    0x76, 0x99, 0xd0, 0x65, 0x54, 0xea, 0xc0, 0x72, 0x6e, 0x77, 0xe7, 0xee, 0xdb, 0xd7, 0xfb, 0x9b,
-    0x4d, 0xda, 0x37, 0x30, 0x6e, 0xf8, 0xc5, 0xef, 0x60, 0x13, 0x0e, 0x18, 0x58, 0x9c, 0x79, 0xda,
-    0xd0, 0xc2, 0xa3, 0x2c, 0x66, 0xb2, 0x8b, 0xa3, 0x73, 0x3b, 0x35, 0x5a, 0xb1, 0x39, 0x09, 0x26,
-    0x0d, 0xc0, 0x24, 0x89, 0xaf, 0x00, 0x53, 0xc4, 0xaa, 0x5c, 0x5e, 0xdd, 0x03, 0xb4, 0xf5, 0xfa,
-    0x91, 0x66, 0xac, 0x1c, 0xad, 0x46, 0x38, 0x0e, 0x80, 0xa8, 0xba, 0x74, 0xf5, 0xf5, 0x60, 0xbf,
-    0xfe, 0x7f, 0x0d, 0xb7, 0xad, 0x38, 0x3d, 0xb4, 0x23, 0xa3, 0xbc, 0x63, 0xc2, 0x08, 0xde, 0x9a,
-    0x00, 0x88, 0xbf, 0x83, 0xe6, 0xec, 0xfc, 0x57, 0xf0, 0xff, 0xd5, 0xb9, 0x07, 0x3d, 0x42, 0x4d,
-    0x82, 0xcb, 0xf9, 0x3d, 0xc8, 0x70, 0xea, 0xb5, 0x50, 0xb3, 0x11, 0x7c, 0x82, 0x3f, 0xfe, 0x57,
-    0xf0, 0x0e, 0xf3, 0x8a, 0xbd, 0x9f, 0x93, 0xb4, 0x96, 0xe5, 0xf1, 0x97, 0x96, 0x0c, 0xd1, 0x79,
-    0xdf, 0xf9, 0x6f, 0xc0, 0xd5, 0x54, 0x81, 0xbb, 0x97, 0x5f, 0xfb, 0xb6, 0x2e, 0x15, 0xb1, 0x7c,
-    0x5b, 0x8b, 0x4b, 0xa3, 0x9a, 0x30, 0xf5, 0x82, 0x1a, 0x7d, 0x1c, 0x16, 0x6c, 0x08, 0xf2, 0x5c,
-    0x17, 0x52, 0xbe, 0xff, 0x52, 0x3e, 0xf7, 0xbb, 0x25, 0x80, 0x5c, 0x46, 0x89, 0xe5, 0xc7, 0x62,
-    0x74, 0xea, 0xdc, 0xa0, 0xda, 0xa0, 0x3f, 0x08, 0xdf, 0xcf, 0xaf, 0x9b, 0xdf, 0x78, 0x91, 0x6b,
-    0x7c, 0x00, 0x77, 0x74, 0xb0, 0x3e, 0x0c, 0xd4, 0x6c, 0xaf, 0xdd, 0xed, 0xc7, 0x71, 0xa7, 0x16,
-    0x3f, 0x33, 0x8f, 0x5a, 0xc7, 0xbd, 0x47, 0x76, 0x07, 0x24, 0x11, 0xd7, 0xa6, 0x9a, 0x56, 0x69,
-    0x3b, 0x12, 0xb0, 0xe0, 0xd0, 0x12, 0x23, 0xc4, 0x8c, 0x32, 0xc4, 0x3a, 0x00, 0xba, 0x6e, 0xc3,
-    0x89, 0x7c, 0x96, 0x48, 0x0a, 0x08, 0x24, 0x30, 0x8d, 0x2c, 0x0b, 0x56, 0xe6, 0xd5, 0x55, 0x39,
-    0x5e, 0xf5, 0xed, 0x3c, 0xc0, 0x5b, 0x59, 0x0b, 0xe9, 0xbe, 0x2e, 0xb9, 0xb5, 0x7e, 0xff, 0xbd,
-    0x6f, 0x80, 0x1e, 0xc4, 0xe4, 0x1b, 0xaf, 0xfc, 0x55, 0x1f, 0x19, 0x7d, 0x4c, 0x4f, 0xcc, 0xbc,
-    0xee, 0xb2, 0x67, 0x02, 0xe9, 0x2f, 0x1b, 0xe4, 0xfa, 0x1b, 0xc5, 0xfc, 0x9a, 0x2e, 0x41, 0x82,
-    0xc6, 0xfe, 0x01, 0x8f, 0x4b, 0x1b, 0x79, 0x1b, 0x2b, 0x85, 0xe9, 0x5f, 0x92, 0xe1, 0xba, 0x97,
-    0xae, 0x9b, 0x68, 0x7c, 0xd2, 0x4c, 0x8d, 0xdc, 0xb2, 0xc0, 0x46, 0xd2, 0xe1, 0x5e, 0x6b, 0xf3,
-    0x79, 0x7a, 0xf3, 0xcb, 0x3c, 0x5d, 0x23, 0xb0, 0x93, 0x47, 0x39, 0xfd, 0x4e, 0x1a, 0xe5, 0x95,
-    0xc1, 0x47, 0x5f, 0xf9, 0xed, 0x0b, 0xdc, 0xcc, 0x75, 0x2a, 0x6a, 0x06, 0x00, 0x91, 0x19, 0x8c,
-    0x56, 0xfd, 0x00, 0x93, 0x18, 0xfc, 0xd2, 0x30, 0xbd, 0xa2, 0xa2, 0xa6, 0xd8, 0x96, 0x78, 0xf1,
-    0x8e, 0x7c, 0x46, 0xc8, 0x79, 0xf8, 0xda, 0xcb, 0x3b, 0x02, 0x6d, 0x5e, 0x01, 0x32, 0xf4, 0x86,
-    0x7f, 0xe6, 0x7c, 0xfd, 0xcf, 0x08, 0x0f, 0x09, 0x6f, 0x03, 0xfa, 0x11, 0x96, 0x7b, 0x73, 0x75,
-    0x90, 0xdc, 0xfe, 0x5e, 0x62, 0x61, 0x67, 0x1b, 0xc8, 0x8c, 0xb9, 0x32, 0x98, 0x4d, 0x9a, 0xd4,
-    0xa0, 0x39, 0x5b, 0xd6, 0x0b, 0x56, 0x56, 0xcf, 0x55, 0x43, 0x2f, 0x6f, 0xb8, 0xf6, 0x83, 0xff,
-    0xe7, 0x62, 0x7b, 0x73, 0xf9, 0x4e, 0xea, 0xec, 0x3e, 0x1e, 0xa6, 0xbf, 0xbf, 0x87, 0xdb, 0x1a,
-    0xdb, 0xca, 0xb4, 0xc7, 0xa5, 0x30, 0x2b, 0xcd, 0x79, 0xef, 0x9b, 0x58, 0xdc, 0x30, 0xf8, 0x70,
-    0x8e, 0x2f, 0x42, 0xde, 0x83, 0x44, 0x25, 0xf7, 0xdd, 0x55, 0x3e, 0x12, 0x55, 0x4e, 0x15, 0x5f,
-    0x37, 0x91, 0xe9, 0xe3, 0x1f, 0x20, 0xda, 0x51, 0xcb, 0xff, 0xfd, 0x6d, 0x72, 0xfc, 0x3f, 0x5b,
-    0x0f, 0xc7, 0x70, 0xcd, 0x84, 0xd9, 0x8e, 0x36, 0x92, 0x1d, 0x4a, 0x61, 0x70, 0x9b, 0x9b, 0x68,
-    0x9b, 0x27, 0xc6, 0x99, 0x10, 0xa3, 0xce, 0x53, 0x21, 0xf4, 0x31, 0x3c, 0x87, 0x84, 0x69, 0x81,
-    0x9d, 0xa5, 0xef, 0x0f, 0xf1, 0xca, 0xae, 0xf0, 0x63, 0x1c, 0x84, 0x5b, 0x8f, 0x77, 0xe5, 0x6b,
-    0x11, 0xec, 0x26, 0x59, 0x9f, 0x16, 0x89, 0xe1, 0xc2, 0xad, 0x56, 0xb8, 0x2e, 0x31, 0x68, 0x5d,
-    0x2a, 0x83, 0xf6, 0xef, 0xfe, 0x61, 0x76, 0x9d, 0x60, 0x2a, 0x87, 0xc5, 0x85, 0xff, 0x22, 0xe4,
-    0x4b, 0x08, 0x48, 0x13, 0x8a, 0x00, 0x3b, 0x48, 0xc9, 0x21, 0x06, 0xc1, 0xbb, 0xd9, 0xd1, 0x3d,
-    0x52, 0x49, 0x6a, 0x44, 0x93, 0x09, 0xc8, 0x8b, 0x8a, 0x5e, 0x7e, 0x8d, 0x0b, 0x26, 0xc3, 0x59,
-    0xbb, 0xad, 0x92, 0xaa, 0x46, 0x0b, 0xbe, 0xd6, 0xaa, 0xa0, 0x36, 0x5f, 0x34, 0x6a, 0x88, 0x84,
-    0x0a, 0xfc, 0xa8, 0xb8, 0xbd, 0xb4, 0xa4, 0xf4, 0xc6, 0xe1, 0x88, 0x55, 0x5d, 0x4e, 0xc9, 0xdc,
-    0x7d, 0xa2, 0x71, 0x0d, 0x18, 0x1e, 0x16, 0x1d, 0x17, 0x11, 0xb3, 0x91, 0x92, 0xf0, 0xc6, 0x1b,
-    0x3a, 0x54, 0x80, 0x8f, 0x11, 0x71, 0x9b, 0x02, 0x16, 0xf4, 0x8f, 0xf0, 0x37, 0xf2, 0xea, 0xa8,
-    0x95, 0x61, 0x8e, 0x8f, 0x22, 0x22, 0xc0, 0xef, 0x51, 0x0b, 0x18, 0x8d, 0xd8, 0xe8, 0x0b, 0x76,
-    0x3e, 0xa5, 0x28, 0xc3, 0x6f, 0xde, 0xf1, 0xf4, 0x55, 0x7d, 0x48, 0xfd, 0xe3, 0xa5, 0x64, 0x45,
-    0x70, 0x3c, 0xbe, 0x0f, 0x06, 0x22, 0xc8, 0x95, 0x89, 0x0a, 0x56, 0xd6, 0x8a, 0x36, 0x42, 0xbe,
-    0xe6, 0x20, 0x00, 0x5c, 0xaa, 0xc6, 0x2e, 0xea, 0x19, 0x11, 0xfc, 0x07, 0x38, 0x08, 0x30, 0x7f,
-    0xb1, 0xe7, 0xfd, 0x71, 0x1c, 0x37, 0xc1, 0xd1, 0x4c, 0x3f, 0x16, 0x4c, 0x4d, 0x06, 0x69, 0xca,
-    0x3b, 0x8b, 0x79, 0xfd, 0x05, 0x0c, 0x8c, 0x60, 0xee, 0xaa, 0x91, 0x16, 0xf1, 0xab, 0x8a, 0x55,
-    0xf1, 0xb2, 0x85, 0xfc, 0xa7, 0xde, 0x6f, 0x32, 0xd3, 0x1f, 0xfd, 0xc2, 0x0d, 0xc7, 0x74, 0x4d,
-    0xcb, 0xae, 0x77, 0x96, 0xd3, 0xf3, 0x0a, 0xa0, 0x97, 0xd1, 0xf5, 0x54, 0xb8, 0xba, 0x45, 0x63,
-    0x1f, 0xe4, 0xef, 0x80, 0x63, 0xd4, 0xef, 0xef, 0x74, 0x56, 0x93, 0xf3, 0xfe, 0x60, 0x97, 0xcb,
-    0xf5, 0x55, 0x89, 0x0b, 0x92, 0x4e, 0xb5, 0x22, 0xe3, 0x0e, 0xc4, 0x5e, 0xc9, 0x0b, 0xc8, 0x52,
-    0xc1, 0x43, 0x0a, 0x6e, 0x6e, 0xee, 0xe9, 0xd2, 0x58, 0xe1, 0x08, 0xd5, 0x93, 0xdd, 0x96, 0x57,
-    0x8b, 0x06, 0x2f, 0xb4, 0x3b, 0x0a, 0x00, 0x5f, 0x9d, 0x55, 0x14, 0x57, 0xfe, 0xee, 0xbe, 0x19,
-    0x61, 0xa5, 0x63, 0x81, 0x6a, 0xf0, 0x1e, 0xf8, 0xdc, 0xa0, 0xf4, 0x98, 0xaa, 0x0b, 0x40, 0xfa,
-    0xea, 0xb1, 0xb7, 0xce, 0xb7, 0x80, 0x3b, 0x56, 0x09, 0xb6, 0xa5, 0x92, 0x84, 0x0b, 0x01, 0xca,
-    0x51, 0x86, 0xad, 0xd5, 0x1a, 0xc5, 0x9f, 0xba, 0x2b, 0xc5, 0x30, 0x66, 0xd2, 0xbc, 0xa5, 0xb1,
-    0xcd, 0x3a, 0x73, 0x29, 0x49, 0x47, 0x58, 0xe3, 0xb7, 0x55, 0xeb, 0x15, 0x7f, 0x0d, 0xf8, 0x12,
-    0x55, 0x02, 0x92, 0x3a, 0xe7, 0x60, 0xdf, 0xfa, 0x3a, 0x3a, 0x01, 0x35, 0xf0, 0x06, 0xe5, 0x85,
-    0xdd, 0x21, 0xef, 0xe7, 0x84, 0xda, 0x08, 0x22, 0xa2, 0xab, 0x83, 0x92, 0x25, 0x15, 0x8b, 0x00,
-    0x4f, 0x6a, 0xc9, 0x01, 0xa4, 0xc1, 0x62, 0xf6, 0x07, 0x24, 0x19, 0xbb, 0x5d, 0xe0, 0x36, 0x69,
-    0xf5, 0x6a, 0xef, 0x22, 0x73, 0xc7, 0xb6, 0xc2, 0x03, 0xc9, 0x53, 0x9f, 0x4d, 0xd5, 0x1e, 0x03,
-    0x00, 0xd9, 0xa4, 0x44, 0x44, 0x45, 0x15, 0x2e, 0xdb, 0x91, 0x09, 0xf0, 0xd8, 0x30, 0x0b, 0x08,
-    0x29, 0x55, 0x1e, 0xd2, 0x58, 0xa1, 0xed, 0xda, 0xe0, 0x64, 0xec, 0x83, 0x2b, 0x5b, 0x30, 0x7a,
-    0x90, 0xb0, 0x5f, 0x1a, 0x55, 0x38, 0xa6, 0x5d, 0x6f, 0xaa, 0x4e, 0x03, 0x6e, 0x00, 0x0b, 0xc6,
-    0x35, 0x5d, 0x37, 0x23, 0xea, 0xa9, 0x81, 0xa2, 0x52, 0x0e, 0x9c, 0xd5, 0x7e, 0xaa, 0x98, 0x05,
-    0x5c, 0x0c, 0xf3, 0x41, 0x07, 0x3e, 0x81, 0x6b, 0xf1, 0x7f, 0x50, 0xef, 0x9b, 0x8f, 0xc2, 0xf0,
-    0x97, 0xaf, 0xbf, 0x00, 0x52, 0x02, 0x57, 0x59, 0xfc, 0x75, 0x0c, 0x90, 0x1a, 0x47, 0x17, 0x0e,
-    0xf0, 0x0d, 0x3f, 0x6e, 0x97, 0x80, 0x99, 0x60, 0x37, 0x6a, 0xcc, 0xe9, 0xd9, 0x11, 0x09, 0xc4,
-    0x09, 0x60, 0x00, 0x25, 0x06, 0x2f, 0xb6, 0x8e, 0xaf, 0x68, 0xa9, 0x29, 0xab, 0xa9, 0xaa, 0xa7,
-    0x45, 0xcb, 0x0c, 0xe4, 0x85, 0xbc, 0x27, 0xd5, 0xa6, 0x00, 0x72, 0xa5, 0xc0, 0x72, 0xf1, 0xef,
-    0x71, 0x42, 0x0c, 0x1b, 0x02, 0xdb, 0x44, 0x40, 0x0b, 0xde, 0x15, 0x7c, 0x43, 0xdf, 0xe2, 0x52,
-    0x0b, 0x2c, 0x9f, 0xf5, 0x24, 0xab, 0xc4, 0x69, 0xaa, 0xa2, 0xe0, 0x39, 0xe0, 0x2e, 0x11, 0x2e,
-    0x11, 0x39, 0x0f, 0x38, 0x5e, 0x6f, 0x2a, 0x70, 0xa3, 0x5e, 0x9c, 0xdc, 0xe8, 0xd5, 0x63, 0x6b,
-    0x70, 0x05, 0xf0, 0x27, 0xbd, 0x70, 0x0d, 0x4b, 0xf5, 0x54, 0xe0, 0x79, 0x11, 0x4a, 0x16, 0xb9,
-    0x03, 0x2f, 0x58, 0xf5, 0x39, 0xbc, 0xa9, 0xc9, 0xff, 0xc5, 0x94, 0x6a, 0xa8, 0x51, 0x05, 0x0b,
-    0x82, 0xfe, 0xa0, 0xd7, 0xd5, 0xc4, 0xae, 0xb7, 0x45, 0xfc, 0x4a, 0x2f, 0x07, 0x72, 0x87, 0x70,
-    0x1c, 0x88, 0x97, 0x8d, 0x59, 0x30, 0x3d, 0xfa, 0x80, 0xd3, 0xd8, 0x09, 0xaa, 0x8f, 0xbc, 0x03,
-    0xfc, 0x0a, 0xaf, 0x00, 0x70, 0x16, 0xa4, 0xec, 0xaa, 0xa3, 0xe3, 0x76, 0xd2, 0x85, 0x11, 0xef,
-    0x91, 0x91, 0x1c, 0xc0, 0x67, 0xbc, 0x50, 0xf8, 0x67, 0xbc, 0x32, 0x78, 0xf5, 0xde, 0xed, 0x27,
-    0x67, 0x01, 0x12, 0xf0, 0x46, 0x7d, 0x49, 0xe3, 0x75, 0xfc, 0x3a, 0x54, 0xe2, 0xf0, 0xe6, 0x71,
-    0xbf, 0x55, 0x14, 0xaa, 0x28, 0xe0, 0xec, 0x65, 0x5e, 0x7f, 0x16, 0x44, 0x97, 0x02, 0x92, 0x13,
-    0x34, 0x9f, 0x4b, 0xc4, 0xd3, 0x78, 0xe5, 0x6b, 0x99, 0x29, 0xa3, 0xa4, 0xad, 0xd1, 0xe5, 0x39,
-    0xc9, 0x07, 0x00, 0x24, 0x65, 0x03, 0x6b, 0x27, 0xb7, 0xb1, 0x48, 0xbf, 0x56, 0x0e, 0xe6, 0xa3,
-    0xb9, 0x46, 0x42, 0x18, 0xe8, 0xfa, 0x07, 0x80, 0x87, 0xee, 0xc8, 0x88, 0xc0, 0x21, 0x89, 0x8f,
-    0x58, 0xe9, 0x08, 0xb4, 0x6b, 0xbc, 0x79, 0xac, 0x56, 0x5f, 0xa6, 0x4b, 0x89, 0x6f, 0x88, 0xe2,
-    0xc0, 0x69, 0x93, 0x51, 0x1f, 0x63, 0xda, 0xaa, 0xe7, 0x8b, 0xfe, 0xf4, 0xf6, 0x92, 0x34, 0xb5,
-    0x55, 0x0a, 0x41, 0x5d, 0x8c, 0x48, 0x2d, 0x22, 0x64, 0x44, 0xf0, 0x22, 0xc0, 0x77, 0x44, 0x67,
-    0xb4, 0xc4, 0x2b, 0x97, 0x85, 0xe0, 0x4b, 0xe1, 0x77, 0x0e, 0x83, 0x04, 0x74, 0xe8, 0x6d, 0x4c,
-    0xf0, 0xcb, 0x84, 0xe9, 0x60, 0x4b, 0x21, 0x6a, 0x73, 0xa6, 0x49, 0x1d, 0x2c, 0x22, 0x72, 0xae,
-    0xd7, 0xb8, 0xc5, 0xf8, 0x22, 0x00, 0x76, 0xf2, 0x85, 0x7c, 0x8e, 0x30, 0x92, 0x1f, 0x02, 0xe1,
-    0x12, 0x83, 0xc5, 0x80, 0xc1, 0xc4, 0x0d, 0x53, 0x5b, 0xd8, 0xe8, 0x95, 0x4d, 0x24, 0x81, 0xc0,
-    0x2a, 0x34, 0x24, 0x6a, 0x47, 0x7f, 0xf3, 0x8c, 0x15, 0x6d, 0xda, 0xf8, 0x1d, 0xe1, 0x6d, 0x1c,
-    0x00, 0xb8, 0x4c, 0x74, 0xfa, 0xd1, 0xaa, 0x35, 0xce, 0xba, 0x51, 0xa8, 0xc3, 0x87, 0x22, 0x10,
-    0x21, 0xf2, 0xbc, 0xbc, 0xa6, 0x8d, 0x6f, 0x6d, 0x74, 0x66, 0xb8, 0x64, 0xe6, 0x7f, 0xe3, 0x50,
-    0xef, 0x59, 0x02, 0x31, 0xeb, 0x0c, 0x69, 0x2a, 0x5a, 0xe3, 0x73, 0xe4, 0x8d, 0xfe, 0xcd, 0xab,
-    0x00, 0x4b, 0x46, 0x06, 0x0d, 0xbf, 0x57, 0xb2, 0xe2, 0x4d, 0x69, 0xb0, 0xda, 0x69, 0x22, 0x17,
-    0xde, 0x65, 0xdf, 0xe4, 0x3b, 0x98, 0x18, 0x6b, 0xda, 0xd3, 0x25, 0xbd, 0x36, 0x1c, 0xa5, 0x56,
-    0x8e, 0x2c, 0x60, 0x17, 0x38, 0x70, 0x3b, 0xf6, 0x64, 0x44, 0x5e, 0x34, 0xd8, 0xe3, 0x6d, 0x74,
-    0xd8, 0xcd, 0xe1, 0x16, 0x31, 0xc2, 0x5c, 0x2d, 0xb6, 0x34, 0xb7, 0x95, 0x6e, 0x8e, 0x2e, 0x31,
-    0x02, 0xac, 0x20, 0x9e, 0x05, 0x45, 0x86, 0x01, 0x10, 0x54, 0xe3, 0x91, 0xad, 0xf0, 0x9e, 0x3d,
-    0x96, 0xda, 0x7d, 0xe1, 0x25, 0x06, 0x45, 0x00, 0x48, 0xe4, 0xcd, 0xe0, 0xec, 0x06, 0x4e, 0x2d,
-    0x92, 0x74, 0xa1, 0x26, 0x5e, 0xe9, 0x26, 0x8a, 0x30, 0x21, 0x34, 0x47, 0x44, 0x67, 0xea, 0xd6,
-    0x59, 0xa4, 0x39, 0xb5, 0x87, 0x22, 0x3f, 0x63, 0x52, 0xe3, 0xda, 0xbc, 0x80, 0x01, 0x45, 0x17,
-    0xdd, 0x32, 0xcd, 0x89, 0x8c, 0x22, 0xc2, 0xc8, 0x16, 0xa8, 0x44, 0x61, 0x18, 0x56, 0x15, 0x99,
-    0x66, 0x46, 0x64, 0x61, 0x18, 0x56, 0x22, 0xd5, 0xce, 0x8c, 0xe7, 0x0a, 0xcc, 0x8e, 0xba, 0xf2,
-    0x2d, 0x91, 0xd7, 0xd6, 0x37, 0xc7, 0xbb, 0xe1, 0xc9, 0x3d, 0xf1, 0xbf, 0x93, 0x8f, 0x28, 0xdb,
-    0xf4, 0x28, 0x32, 0x0e, 0xa2, 0xb3, 0x35, 0x5f, 0x02, 0x4e, 0xda, 0xb0, 0xf7, 0xe8, 0xcb, 0x50,
-    0x9a, 0xf6, 0xe7, 0x1a, 0xe3, 0x80, 0x4f, 0x29, 0xb5, 0x04, 0xfd, 0x8a, 0x95, 0x8c, 0x09, 0xa4,
-    0x0e, 0x58, 0x13, 0x47, 0xa8, 0x13, 0x34, 0xec, 0xf6, 0x7f, 0x2b, 0x30, 0xf2, 0xe2, 0xca, 0xcd,
-    0x44, 0xff, 0x75, 0xeb, 0xfd, 0xa7, 0x8f, 0x6e, 0x6f, 0xf4, 0x6e, 0x7c, 0x0f, 0x75, 0x5b, 0x9f,
-    0x97, 0x5d, 0xc6, 0x38, 0xaa, 0xe3, 0x2d, 0x5a, 0x22, 0x8f, 0xcc, 0xb0, 0x82, 0xcc, 0xa6, 0xbd,
-    0x00, 0xe6, 0xcf, 0x2a, 0x50, 0xd0, 0x0e, 0xca, 0x4d, 0xb8, 0x27, 0xec, 0x86, 0x8c, 0xe4, 0xaa,
-    0xc1, 0xec, 0xd9, 0x7e, 0x87, 0x4d, 0x51, 0xa3, 0x1d, 0x94, 0x88, 0xf0, 0x30, 0xac, 0x20, 0xfb,
-    0xbe, 0x76, 0xf2, 0xbb, 0x1f, 0xca, 0x0e, 0x83, 0x13, 0x78, 0x6c, 0xf0, 0x97, 0x4a, 0x62, 0x53,
-    0xd0, 0x21, 0xa5, 0xc8, 0x5f, 0x3b, 0xe0, 0x1a, 0x17, 0xdf, 0x9b, 0x01, 0x68, 0x1b, 0x6a, 0xcf,
-    0xa6, 0x41, 0xe3, 0x46, 0x70, 0x1c, 0xfb, 0x3c, 0xb0, 0x1c, 0x3b, 0xe5, 0x7c, 0xf4, 0x21, 0x77,
-    0x62, 0x2c, 0x2a, 0x5d, 0xee, 0xd2, 0xec, 0x70, 0x88, 0x57, 0x11, 0xc8, 0x42, 0xf8, 0x67, 0x8e,
-    0xc8, 0x8c, 0xd4, 0x1e, 0xce, 0x0d, 0xdb, 0x03, 0xf4, 0x73, 0x43, 0x05, 0xbf, 0xad, 0xa1, 0x4c,
-    0x6e, 0xa4, 0x55, 0x00, 0x0b, 0x5d, 0x07, 0xbc, 0x19, 0x6f, 0x3d, 0x4f, 0x80, 0xe4, 0x53, 0x7f,
-    0xdf, 0xd5, 0xc2, 0x0f, 0xc5, 0xde, 0xfa, 0x78, 0xf5, 0xd6, 0xfb, 0xda, 0xaa, 0xba, 0xae, 0xfe,
-    0xf3, 0xce, 0x8a, 0xa4, 0x41, 0x0b, 0xe3, 0xc7, 0x01, 0x1b, 0x35, 0xdb, 0x83, 0xfc, 0x91, 0x4d,
-    0xf2, 0xe7, 0x76, 0x0f, 0xc0, 0xaf, 0x68, 0xe1, 0x81, 0x18, 0xb3, 0xf0, 0x02, 0x8d, 0xcd, 0x36,
-    0x01, 0x4a, 0x94, 0xe2, 0x7e, 0xaa, 0xc2, 0x91, 0x44, 0xa5, 0xd5, 0x50, 0x45, 0x87, 0xf5, 0xe4,
-    0x3b, 0xc0, 0xd9, 0x81, 0x1b, 0x24, 0x29, 0x98, 0xd4, 0x9e, 0x7f, 0xe7, 0xea, 0xa9, 0x83, 0xab,
-    0xf7, 0xb4, 0x88, 0x98, 0x87, 0x9c, 0xf6, 0xb7, 0x35, 0xf8, 0xd8, 0x35, 0x4b, 0xfb, 0xde, 0xd1,
-    0x81, 0xc6, 0x6e, 0x0e, 0x9c, 0x76, 0x8e, 0x03, 0x95, 0x27, 0x74, 0x10, 0xc2, 0x9f, 0x82, 0x8f,
-    0x4d, 0x75, 0x78, 0xf3, 0x91, 0xeb, 0x8e, 0x1d, 0x91, 0xfd, 0x80, 0x80, 0x39, 0xc9, 0xb1, 0x07,
-    0xbe, 0x03, 0x63, 0x65, 0x58, 0x5f, 0x74, 0x2e, 0x4d, 0x35, 0xe6, 0xb4, 0x55, 0x5c, 0x62, 0xe7,
-    0x3e, 0x71, 0x55, 0x5a, 0x03, 0xe7, 0x6d, 0xf3, 0x6b, 0xd9, 0x0e, 0x56, 0x88, 0xe6, 0x35, 0x50,
-    0x09, 0x52, 0xdf, 0x41, 0xad, 0xbb, 0xad, 0x9f, 0xf4, 0xc1, 0x07, 0xfa, 0x84, 0x0f, 0x9e, 0x5e,
-    0xa0, 0xe3, 0x3c, 0xa5, 0x6e, 0x02, 0x6f, 0x11, 0xc1, 0x35, 0xc8, 0xc1, 0xce, 0xe2, 0x5f, 0x3c,
-    0x13, 0x58, 0xac, 0xa9, 0xc5, 0xf9, 0xc3, 0xb4, 0x8e, 0xcf, 0x71, 0xf4, 0x38, 0x0e, 0xeb, 0x1e,
-    0xe8, 0x21, 0x39, 0xad, 0x9a, 0xe5, 0x36, 0x68, 0x9d, 0xe0, 0xac, 0x76, 0x1c, 0x3b, 0xa5, 0x8d,
-    0xfe, 0xb8, 0x84, 0xf7, 0xf6, 0xd2, 0xc3, 0x89, 0x0c, 0xb2, 0x1c, 0xcb, 0x9a, 0x40, 0x83, 0x98,
-    0x97, 0xe4, 0xfa, 0xdb, 0x77, 0x39, 0x17, 0x69, 0xc1, 0x9d, 0x4d, 0xaa, 0x54, 0x7f, 0xff, 0xd5,
-    0xe9, 0x4a, 0x1c, 0x92, 0x84, 0x4d, 0x41, 0x0b, 0xb3, 0xc7, 0x19, 0x6f, 0xa5, 0x92, 0xe3, 0x46,
-    0x15, 0x8a, 0x6c, 0xdd, 0x66, 0x08, 0xbc, 0x68, 0xa7, 0x80, 0xea, 0xf3, 0xec, 0x70, 0x9c, 0x8f,
-    0x6f, 0x89, 0x5f, 0x05, 0x42, 0xd2, 0xfb, 0xfb, 0x0b, 0x3b, 0xf3, 0x3b, 0xb2, 0xdc, 0x8f, 0xb1,
-    0x6e, 0x1f, 0xe8, 0x89, 0xef, 0xc2, 0xfe, 0x02, 0x85, 0x3e, 0x72, 0x4b, 0x21, 0x34, 0x01, 0xce,
-    0x63, 0xc7, 0x04, 0x6d, 0x33, 0xbc, 0x7c, 0xa7, 0x55, 0x7a, 0x0f, 0x0f, 0x45, 0x04, 0xf3, 0xa5,
-    0xe5, 0xc0, 0xfb, 0x2e, 0xb1, 0xd9, 0x73, 0x1f, 0x7f, 0x96, 0x40, 0xe3, 0xc0, 0x76, 0xf6, 0xaf,
-    0x2b, 0x2b, 0x0a, 0x7b, 0x7f, 0xde, 0x05, 0xbd, 0xbf, 0x7e, 0x17, 0x23, 0x25, 0x2e, 0x62, 0x5b,
-    0xf0, 0x27, 0x55, 0x4e, 0xc5, 0x7d, 0x25, 0xf2, 0x9a, 0x3a, 0xe7, 0x14, 0xe3, 0x80, 0xea, 0x31,
-    0x0b, 0x80, 0x8b, 0xc0, 0x05, 0x5d, 0xdd, 0x1e, 0xe3, 0x93, 0x6b, 0xcb, 0x79, 0x7b, 0x3f, 0xb5,
-    0x06, 0x52, 0x2b, 0xe6, 0x3a, 0xaa, 0xf1, 0xaf, 0x1d, 0x96, 0x6b, 0x0e, 0x52, 0x04, 0xd7, 0x7e,
-    0x69, 0xf7, 0x2b, 0x58, 0x69, 0xb6, 0xf5, 0x50, 0xbc, 0x7a, 0x85, 0x79, 0x42, 0x9c, 0x95, 0x5e,
-    0x60, 0x5c, 0x72, 0x1b, 0xe4, 0xaa, 0xb6, 0xf1, 0xe6, 0x77, 0xec, 0xb4, 0x5f, 0x6c, 0xd1, 0xe9,
-    0x28, 0xb0, 0x25, 0x40, 0xc2, 0x5e, 0xbe, 0x73, 0x06, 0xcb, 0xd2, 0x3c, 0xd0, 0x71, 0x75, 0x20,
-    0xb9, 0xaa, 0x90, 0x1d, 0xc8, 0x88, 0xe0, 0x4a, 0xf0, 0x48, 0x07, 0x8a, 0x1c, 0xcc, 0x41, 0x92,
-    0x27, 0xd9, 0xa9, 0xd0, 0x2f, 0x78, 0x64, 0x6d, 0x79, 0x5f, 0x06, 0xbf, 0xa1, 0xf6, 0xe3, 0xc1,
-    0xf7, 0xd6, 0xb8, 0x69, 0xac, 0x1e, 0xdf, 0x9f, 0x17, 0x00, 0xd1, 0xb9, 0x63, 0xaf, 0x46, 0x7f,
-    0x2e, 0x44, 0x7e, 0x01, 0xf6, 0x96, 0x34, 0x75, 0xdd, 0x01, 0xf7, 0xc0, 0x2f, 0x80, 0x15, 0x14,
-    0x1c, 0xe4, 0x32, 0x8e, 0x01, 0xce, 0x20, 0xc7, 0x5f, 0x22, 0x3e, 0x02, 0xd9, 0x6f, 0x01, 0x6c,
-    0x18, 0x4f, 0x5a, 0xfe, 0x6d, 0x55, 0xa1, 0xce, 0xc3, 0x82, 0x48, 0x31, 0xe4, 0x44, 0xe0, 0x28,
-    0x3c, 0x19, 0xc0, 0x39, 0x91, 0x83, 0xc0, 0xe5, 0x81, 0x77, 0xa8, 0x1f, 0xf6, 0xea, 0x2f, 0xb4,
-    0xd1, 0xc6, 0x38, 0x9c, 0x1c, 0x07, 0x88, 0x83, 0x07, 0x4c, 0xc6, 0x9e, 0xe3, 0x5e, 0x08, 0xb1,
-    0x98, 0xf8, 0x3e, 0xaa, 0xc9, 0x6e, 0xc3, 0x9e, 0x1c, 0x61, 0x99, 0x11, 0xdc, 0x01, 0x1c, 0x07,
-    0xf6, 0x97, 0x18, 0xb5, 0x13, 0xa2, 0xe9, 0x58, 0xef, 0xfb, 0xe2, 0xe5, 0x97, 0xff, 0xea, 0xa9,
-    0x34, 0xca, 0xcc, 0x2f, 0x7f, 0xb1, 0x1c, 0xe0, 0x05, 0x70, 0x64, 0x44, 0xf0, 0x0c, 0xe0, 0x19,
-    0xc0, 0x63, 0x2a, 0x10, 0x9c, 0x44, 0xdc, 0x6b, 0xaa, 0x57, 0xbc, 0xef, 0x78, 0x00, 0x39, 0xc4,
-    0xc7, 0x5e, 0x8a, 0x81, 0x8c, 0x88, 0xde, 0x00, 0x8c, 0x06, 0xbe, 0xc3, 0xc9, 0xae, 0x51, 0x60,
-    0xd3, 0x8a, 0x78, 0xc0, 0x0e, 0x25, 0xff, 0xfd, 0xdb, 0x8f, 0x09, 0xd7, 0x52, 0xac, 0x4d, 0xd5,
-    0x5c, 0xa9, 0xe3, 0xad, 0xc1, 0x80, 0x60, 0x39, 0xc3, 0x87, 0x70, 0xe1, 0x52, 0x38, 0x15, 0xc0,
-    0x70, 0xe2, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, 0x03, 0x9c, 0x38, 0x76, 0x0e, 0x1c, 0x23, 0x87,
-    0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, 0x03, 0x9c, 0x38, 0x76, 0x0e, 0x1c,
-    0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, 0x03, 0x9c, 0x38, 0x76,
-    0x0e, 0x1c, 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, 0x03, 0x9c,
-    0x38, 0x76, 0x0e, 0x1c, 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23,
-    0x03, 0x9c, 0x38, 0x76, 0x0e, 0x1c, 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e,
-    0x05, 0x23, 0x03, 0x9c, 0x38, 0x76, 0x0e, 0x1c, 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38,
-    0x87, 0x0e, 0x05, 0x23, 0x0f, 0xfe, 0xf6, 0xa7, 0xbf, 0x78, 0xfb, 0x2d, 0xfb, 0xc5, 0x00, 0x00,
-    0x00, 0x00, 0x45, 0x49, 0x44, 0x4e, 0x42, 0xae, 0x82, 0x60,
-  ],
-  disp: function()
-  {
-    // Do Nothing
-
-    throw "Does Nothing!";
-  }
-
-};
-
-try
-{
-  LOGO.disp();
-}
-catch(e)
-{
-  alert("Error: " + e + "\n");
-}
diff --git a/jetty-spdy/spdy-http-server/src/test/resources/jetty-logging.properties b/jetty-spdy/spdy-http-server/src/test/resources/jetty-logging.properties
deleted file mode 100644
index be1b7aa..0000000
--- a/jetty-spdy/spdy-http-server/src/test/resources/jetty-logging.properties
+++ /dev/null
@@ -1,10 +0,0 @@
-org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
-#org.eclipse.jetty.spdy.LEVEL=DEBUG
-#org.eclipse.jetty.server.LEVEL=DEBUG
-#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
-#org.eclipse.jetty.spdy.LEVEL=DEBUG
-#org.eclipse.jetty.client.LEVEL=DEBUG
-#org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy.LEVEL=DEBUG
-#org.eclipse.jetty.spdy.server.proxy.LEVEL=DEBUG
-#org.mortbay.LEVEL=DEBUG
-
diff --git a/jetty-spdy/spdy-http-server/src/test/resources/keystore.jks b/jetty-spdy/spdy-http-server/src/test/resources/keystore.jks
deleted file mode 100644
index 428ba54..0000000
--- a/jetty-spdy/spdy-http-server/src/test/resources/keystore.jks
+++ /dev/null
Binary files differ
diff --git a/jetty-spdy/spdy-http-server/src/test/resources/truststore.jks b/jetty-spdy/spdy-http-server/src/test/resources/truststore.jks
deleted file mode 100644
index 839cb8c..0000000
--- a/jetty-spdy/spdy-http-server/src/test/resources/truststore.jks
+++ /dev/null
Binary files differ
diff --git a/jetty-spdy/spdy-npn-tests/pom.xml b/jetty-spdy/spdy-npn-tests/pom.xml
deleted file mode 100644
index 7b54c55..0000000
--- a/jetty-spdy/spdy-npn-tests/pom.xml
+++ /dev/null
@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.spdy</groupId>
-        <artifactId>spdy-parent</artifactId>
-        <version>9.2.11-SNAPSHOT</version>
-    </parent>
-
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>spdy-npn-tests</artifactId>
-    <name>Jetty :: SPDY :: NPN Tests</name>
-
-    <build>
-        <plugins>
-            <plugin>
-                <artifactId>maven-dependency-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>copy</id>
-                        <phase>generate-resources</phase>
-                        <goals>
-                            <goal>copy</goal>
-                        </goals>
-                        <configuration>
-                            <artifactItems>
-                                <artifactItem>
-                                    <groupId>org.mortbay.jetty.npn</groupId>
-                                    <artifactId>npn-boot</artifactId>
-                                    <version>${npn.version}</version>
-                                    <type>jar</type>
-                                    <overWrite>false</overWrite>
-                                    <outputDirectory>${project.build.directory}/npn</outputDirectory>
-                                </artifactItem>
-                            </artifactItems>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <argLine>-Xbootclasspath/p:${project.build.directory}/npn/npn-boot-${npn.version}.jar</argLine>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.eclipse.jetty.npn</groupId>
-            <artifactId>npn-api</artifactId>
-            <version>${npn.api.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-start</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-server</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-server</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-http-server</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-http-server</artifactId>
-            <version>${project.version}</version>
-            <classifier>tests</classifier>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-    
-</project>
diff --git a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/AbstractNPNTest.java b/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/AbstractNPNTest.java
deleted file mode 100644
index 3bc2d31..0000000
--- a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/AbstractNPNTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.net.InetSocketAddress;
-
-import org.eclipse.jetty.npn.NextProtoNego;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.junit.After;
-import org.junit.Rule;
-
-public class AbstractNPNTest
-{
-    @Rule
-    public final TestTracker tracker = new TestTracker();
-    protected Server server;
-    protected SPDYServerConnector connector;
-    protected SPDYClient.Factory clientFactory;
-
-    protected InetSocketAddress prepare() throws Exception
-    {
-        server = new Server();
-        connector = new SPDYServerConnector(server, newSslContextFactory(), null);
-        connector.setPort(0);
-        connector.setIdleTimeout(30000);
-        server.addConnector(connector);
-        server.start();
-
-        QueuedThreadPool threadPool = new QueuedThreadPool();
-        threadPool.setName(threadPool.getName() + "-client");
-        clientFactory = new SPDYClient.Factory(threadPool);
-        clientFactory.start();
-
-        NextProtoNego.debug = true;
-
-        return new InetSocketAddress("localhost", connector.getLocalPort());
-    }
-
-    protected SslContextFactory newSslContextFactory()
-    {
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
-        sslContextFactory.setKeyStorePassword("storepwd");
-        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
-        sslContextFactory.setTrustStorePassword("storepwd");
-        sslContextFactory.setProtocol("TLSv1");
-        sslContextFactory.setIncludeProtocols("TLSv1");
-        return sslContextFactory;
-    }
-
-    @After
-    public void dispose() throws Exception
-    {
-        clientFactory.stop();
-        server.stop();
-    }
-}
diff --git a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/NPNModuleTest.java b/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/NPNModuleTest.java
deleted file mode 100644
index aa0ddab..0000000
--- a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/NPNModuleTest.java
+++ /dev/null
@@ -1,193 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import org.eclipse.jetty.start.BaseHome;
-import org.eclipse.jetty.start.FileArg;
-import org.eclipse.jetty.start.Module;
-import org.eclipse.jetty.toolchain.test.FS;
-import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
-import org.eclipse.jetty.util.IO;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-@RunWith(Parameterized.class)
-public class NPNModuleTest
-{
-    /** This is here to prevent pointless download attempts */
-    private static final List<String> KNOWN_GOOD_NPN_URLS = new ArrayList<>();
-
-    static
-    {
-        /** The main() method in this test case can be run to validate this list independently */
-        KNOWN_GOOD_NPN_URLS.add("http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.11.v20150415/npn-boot-1.1.11.v20150415.jar");
-        KNOWN_GOOD_NPN_URLS.add("http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.10.v20150130/npn-boot-1.1.10.v20150130.jar");
-        KNOWN_GOOD_NPN_URLS.add("http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.9.v20141016/npn-boot-1.1.9.v20141016.jar");
-        KNOWN_GOOD_NPN_URLS.add("http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.8.v20141013/npn-boot-1.1.8.v20141013.jar");
-        KNOWN_GOOD_NPN_URLS.add("http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar");
-        KNOWN_GOOD_NPN_URLS.add("http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar");
-        KNOWN_GOOD_NPN_URLS.add("http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.4.v20130313/npn-boot-1.1.4.v20130313.jar");
-        KNOWN_GOOD_NPN_URLS.add("http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar");
-        KNOWN_GOOD_NPN_URLS.add("http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.2.v20130305/npn-boot-1.1.2.v20130305.jar");
-        KNOWN_GOOD_NPN_URLS.add("http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.1.v20121030/npn-boot-1.1.1.v20121030.jar");
-        KNOWN_GOOD_NPN_URLS.add("http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.0.v20120525/npn-boot-1.1.0.v20120525.jar");
-    }
-
-    @Parameters(name = "{index}: mod:{0}")
-    public static List<Object[]> data()
-    {
-        File npnBootModDir = MavenTestingUtils.getProjectDir("../spdy-http-server/src/main/config/modules/protonego-impl");
-        List<Object[]> data = new ArrayList<>();
-        for (File file : npnBootModDir.listFiles())
-        {
-            if (Pattern.matches("npn-.*\\.mod",file.getName()))
-            {
-                data.add(new Object[] { file.getName() });
-            }
-        }
-        return data;
-    }
-
-    @Parameter(value = 0)
-    public String modBootFile;
-
-    private static BaseHome basehome;
-
-    @BeforeClass
-    public static void initBaseHome() throws IOException
-    {
-        File homeDir = MavenTestingUtils.getProjectDir("../spdy-http-server/src/main/config");
-        File baseDir = MavenTestingUtils.getTargetTestingDir(NPNModuleTest.class.getName());
-        FS.ensureEmpty(baseDir);
-        
-        String cmdLine[] = { "jetty.home="+homeDir.getAbsolutePath(),"jetty.base="+baseDir.getAbsolutePath() };
-        basehome = new BaseHome(cmdLine);
-    }
-
-    /**
-     * Check the sanity of the npn-boot file module 
-     */
-    @Test
-    public void testModuleValues() throws IOException
-    {
-        Path modFile = basehome.getPath("modules/protonego-impl/" + modBootFile);
-        Module mod = new Module(basehome,modFile);
-        assertNotNull("module",mod);
-        
-        // Validate logical name
-        assertThat("Module name",mod.getName(),is("protonego-boot"));
-
-        List<String> expectedBootClasspath = new ArrayList<>();
-
-        for (String line : mod.getFiles())
-        {
-            FileArg farg = new FileArg(line);
-            if (farg.uri != null)
-            {
-                assertTrue("Not a known good NPN URL: " + farg.uri,KNOWN_GOOD_NPN_URLS.contains(farg.uri));
-                expectedBootClasspath.add("-Xbootclasspath/p:" + farg.location);
-            }
-        }
-
-        for (String line : mod.getJvmArgs())
-        {
-            expectedBootClasspath.remove(line);
-        }
-
-        if (expectedBootClasspath.size() > 0)
-        {
-            StringBuilder err = new StringBuilder();
-            err.append("XBootClasspath mismatch between [files] and [exec]");
-            err.append("\nThe following are inferred from your [files] definition in ");
-            err.append(modFile.toAbsolutePath().toString());
-            err.append("\nbut are not referenced in your [exec] section");
-            for (String entry : expectedBootClasspath)
-            {
-                err.append("\n").append(entry);
-            }
-            fail(err.toString());
-        }
-    }
-
-    public static void main(String[] args)
-    {
-        File outputDir = MavenTestingUtils.getTargetTestingDir(NPNModuleTest.class.getSimpleName() + "-main");
-        FS.ensureEmpty(outputDir);
-        for (String ref : KNOWN_GOOD_NPN_URLS)
-        {
-            try
-            {
-                URL url = new URL(ref);
-                System.err.printf("Attempting: %s%n",ref);
-                HttpURLConnection connection = (HttpURLConnection)url.openConnection();
-                String refname = url.toURI().getPath();
-                int idx = refname.lastIndexOf('/');
-                File outputFile = new File(outputDir,refname.substring(idx));
-                try (InputStream stream = connection.getInputStream(); FileOutputStream out = new FileOutputStream(outputFile))
-                {
-                    assertThat("Response Status Code",connection.getResponseCode(),is(200));
-                    IO.copy(stream,out);
-                    System.err.printf("Downloaded %,d bytes%n",outputFile.length());
-                }
-                catch (IOException e)
-                {
-                    e.printStackTrace(System.err);
-                }
-            }
-            catch (MalformedURLException e)
-            {
-                System.err.printf("Bad Ref: %s%n",ref);
-                e.printStackTrace(System.err);
-            }
-            catch (URISyntaxException e)
-            {
-                System.err.printf("Bad Ref Syntax: %s%n",ref);
-                e.printStackTrace(System.err);
-            }
-            catch (IOException e)
-            {
-                System.err.printf("Bad Connection: %s%n",ref);
-                e.printStackTrace(System.err);
-            }
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/NPNNegotiationTest.java b/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/NPNNegotiationTest.java
deleted file mode 100644
index 23e4de3..0000000
--- a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/NPNNegotiationTest.java
+++ /dev/null
@@ -1,207 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocket;
-
-import org.eclipse.jetty.npn.NextProtoNego;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class NPNNegotiationTest extends AbstractNPNTest
-{
-    @Test
-    public void testServerAdvertisingHTTPSpeaksHTTP() throws Exception
-    {
-        InetSocketAddress address = prepare();
-        connector.addConnectionFactory(new HttpConnectionFactory());
-
-        SslContextFactory sslContextFactory = newSslContextFactory();
-        sslContextFactory.start();
-        SSLContext sslContext = sslContextFactory.getSslContext();
-
-        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()))
-        {
-            client.setUseClientMode(true);
-            client.setSoTimeout(5000);
-
-            NextProtoNego.put(client, new NextProtoNego.ClientProvider()
-            {
-                @Override
-                public boolean supports()
-                {
-                    return true;
-                }
-
-                @Override
-                public void unsupported()
-                {
-                }
-
-                @Override
-                public String selectProtocol(List<String> strings)
-                {
-                    Assert.assertNotNull(strings);
-                    String protocol = "http/1.1";
-                    Assert.assertTrue(strings.contains(protocol));
-                    return protocol;
-                }
-            });
-
-            client.startHandshake();
-
-            // Verify that the server really speaks http/1.1
-
-            OutputStream output = client.getOutputStream();
-            output.write(("" +
-                    "GET / HTTP/1.1\r\n" +
-                    "Host: localhost:" + address.getPort() + "\r\n" +
-                    "\r\n" +
-                    "").getBytes(StandardCharsets.UTF_8));
-            output.flush();
-
-            InputStream input = client.getInputStream();
-            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
-            String line = reader.readLine();
-            Assert.assertTrue(line.contains(" 404 "));
-        }
-    }
-
-    @Test
-    public void testServerAdvertisingSPDYAndHTTPSpeaksHTTPWhenNegotiated() throws Exception
-    {
-        InetSocketAddress address = prepare();
-        connector.addConnectionFactory(new HttpConnectionFactory());
-
-        SslContextFactory sslContextFactory = newSslContextFactory();
-        sslContextFactory.start();
-        SSLContext sslContext = sslContextFactory.getSslContext();
-        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()))
-        {
-            client.setUseClientMode(true);
-            client.setSoTimeout(5000);
-
-            NextProtoNego.put(client, new NextProtoNego.ClientProvider()
-            {
-                @Override
-                public boolean supports()
-                {
-                    return true;
-                }
-
-                @Override
-                public void unsupported()
-                {
-                }
-
-                @Override
-                public String selectProtocol(List<String> strings)
-                {
-                    Assert.assertNotNull(strings);
-                    String spdyProtocol = "spdy/2";
-                    Assert.assertTrue(strings.contains(spdyProtocol));
-                    String httpProtocol = "http/1.1";
-                    Assert.assertTrue(strings.contains(httpProtocol));
-                    Assert.assertTrue(strings.indexOf(spdyProtocol) < strings.indexOf(httpProtocol));
-                    return httpProtocol;
-                }
-            });
-
-            client.startHandshake();
-
-            // Verify that the server really speaks http/1.1
-
-            OutputStream output = client.getOutputStream();
-            output.write(("" +
-                    "GET / HTTP/1.1\r\n" +
-                    "Host: localhost:" + address.getPort() + "\r\n" +
-                    "\r\n" +
-                    "").getBytes(StandardCharsets.UTF_8));
-            output.flush();
-
-            InputStream input = client.getInputStream();
-            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
-            String line = reader.readLine();
-            Assert.assertTrue(line.contains(" 404 "));
-        }
-    }
-
-    @Test
-    public void testServerAdvertisingSPDYAndHTTPSpeaksDefaultProtocolWhenNPNMissing() throws Exception
-    {
-        InetSocketAddress address = prepare();
-        connector.addConnectionFactory(new HttpConnectionFactory());
-
-        SslContextFactory sslContextFactory = newSslContextFactory();
-        sslContextFactory.start();
-        SSLContext sslContext = sslContextFactory.getSslContext();
-        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()))
-        {
-            client.setUseClientMode(true);
-            client.setSoTimeout(5000);
-
-            NextProtoNego.put(client, new NextProtoNego.ClientProvider()
-            {
-                @Override
-                public boolean supports()
-                {
-                    return false;
-                }
-
-                @Override
-                public void unsupported()
-                {
-                }
-
-                @Override
-                public String selectProtocol(List<String> strings)
-                {
-                    return null;
-                }
-            });
-
-            client.startHandshake();
-
-            // Verify that the server really speaks http/1.1
-
-            OutputStream output = client.getOutputStream();
-            output.write(("" +
-                    "GET / HTTP/1.1\r\n" +
-                    "Host: localhost:" + address.getPort() + "\r\n" +
-                    "\r\n" +
-                    "").getBytes(StandardCharsets.UTF_8));
-            output.flush();
-
-            InputStream input = client.getInputStream();
-            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
-            String line = reader.readLine();
-            Assert.assertTrue(line.contains(" 404 "));
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/SSLEngineLeakTest.java b/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/SSLEngineLeakTest.java
deleted file mode 100644
index 19c20a3..0000000
--- a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/SSLEngineLeakTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.lang.reflect.Field;
-import java.net.InetSocketAddress;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.npn.NextProtoNego;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class SSLEngineLeakTest extends AbstractNPNTest
-{
-    @Test
-    public void testSSLEngineLeak() throws Exception
-    {
-        System.gc();
-        Thread.sleep(1000);
-
-        Field field = NextProtoNego.class.getDeclaredField("objects");
-        field.setAccessible(true);
-        @SuppressWarnings("unchecked")
-        Map<Object, NextProtoNego.Provider> objects = (Map<Object, NextProtoNego.Provider>)field.get(null);
-        int initialSize = objects.size();
-
-        avoidStackLocalVariables();
-        // Allow the close to arrive to the server and the selector to process it
-        Thread.sleep(1000);
-
-        // Perform GC to be sure that the map is cleared
-        System.gc();
-        Thread.sleep(1000);
-
-        // Check that the map is empty
-        if (objects.size() != initialSize)
-        {
-            System.err.println(objects);
-            server.dumpStdErr();
-        }
-
-        Assert.assertEquals(initialSize, objects.size());
-    }
-
-    private void avoidStackLocalVariables() throws Exception
-    {
-        InetSocketAddress address = prepare();
-        Session session = clientFactory.newSPDYClient(SPDY.V3).connect(address, null);
-        session.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-    }
-}
diff --git a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/SSLSynReplyTest.java b/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/SSLSynReplyTest.java
deleted file mode 100644
index a55cbb2..0000000
--- a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/SSLSynReplyTest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server;
-
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.SocketChannel;
-import java.util.List;
-import javax.net.ssl.SSLEngine;
-
-import org.eclipse.jetty.npn.NextProtoNego;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class SSLSynReplyTest extends AbstractNPNTest
-{
-    @Test
-    public void testGentleCloseDuringHandshake() throws Exception
-    {
-        InetSocketAddress address = prepare();
-        SslContextFactory sslContextFactory = newSslContextFactory();
-        sslContextFactory.start();
-        SSLEngine sslEngine = sslContextFactory.newSSLEngine(address);
-        sslEngine.setUseClientMode(true);
-        NextProtoNego.put(sslEngine, new NextProtoNego.ClientProvider()
-        {
-            @Override
-            public boolean supports()
-            {
-                return true;
-            }
-
-            @Override
-            public void unsupported()
-            {
-            }
-
-            @Override
-            public String selectProtocol(List<String> protocols)
-            {
-                return null;
-            }
-        });
-        sslEngine.beginHandshake();
-
-        ByteBuffer encrypted = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
-        sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
-        encrypted.flip();
-
-        try (SocketChannel channel = SocketChannel.open(address))
-        {
-            // Send ClientHello, immediately followed by TLS Close Alert and then by FIN
-            channel.write(encrypted);
-            sslEngine.closeOutbound();
-            encrypted.clear();
-            sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
-            encrypted.flip();
-            channel.write(encrypted);
-            channel.shutdownOutput();
-
-            // Read ServerHello from server
-            encrypted.clear();
-            int read = channel.read(encrypted);
-            encrypted.flip();
-            Assert.assertTrue(read > 0);
-            // Cannot decrypt, as the SSLEngine has been already closed
-
-            // Now if we read more, we should either read the TLS Close Alert, or directly -1
-            encrypted.clear();
-            read = channel.read(encrypted);
-            // Sending a TLS Close Alert during handshake results in an exception when
-            // unwrapping that the server react to by closing the connection abruptly.
-            Assert.assertTrue(read < 0);
-        }
-    }
-
-    @Test
-    public void testAbruptCloseDuringHandshake() throws Exception
-    {
-        InetSocketAddress address = prepare();
-        SslContextFactory sslContextFactory = newSslContextFactory();
-        sslContextFactory.start();
-        SSLEngine sslEngine = sslContextFactory.newSSLEngine(address);
-        sslEngine.setUseClientMode(true);
-        NextProtoNego.put(sslEngine, new NextProtoNego.ClientProvider()
-        {
-            @Override
-            public boolean supports()
-            {
-                return true;
-            }
-
-            @Override
-            public void unsupported()
-            {
-            }
-
-            @Override
-            public String selectProtocol(List<String> protocols)
-            {
-                return null;
-            }
-        });
-        sslEngine.beginHandshake();
-
-        ByteBuffer encrypted = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
-        sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
-        encrypted.flip();
-
-        try (SocketChannel channel = SocketChannel.open(address))
-        {
-            // Send ClientHello, immediately followed by FIN (no TLS Close Alert)
-            channel.write(encrypted);
-            channel.shutdownOutput();
-
-            // Read ServerHello from server
-            encrypted.clear();
-            int read = channel.read(encrypted);
-            encrypted.flip();
-            Assert.assertTrue(read > 0);
-            ByteBuffer decrypted = ByteBuffer.allocate(sslEngine.getSession().getApplicationBufferSize());
-            sslEngine.unwrap(encrypted, decrypted);
-
-            // Now if we read more, we should either read the TLS Close Alert, or directly -1
-            encrypted.clear();
-            read = channel.read(encrypted);
-            // Since we have close the connection abruptly, the server also does so
-            Assert.assertTrue(read < 0);
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/proxy/NPNProxySPDYToHTTPLoadTest.java b/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/proxy/NPNProxySPDYToHTTPLoadTest.java
deleted file mode 100644
index ea92414..0000000
--- a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/proxy/NPNProxySPDYToHTTPLoadTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
-
-public class NPNProxySPDYToHTTPLoadTest extends ProxySPDYToHTTPLoadTest
-{
-    public NPNProxySPDYToHTTPLoadTest(short version)
-    {
-        super(version, new NPNServerConnectionFactory("spdy/3", "spdy/2", "http/1.1"));
-    }
-}
diff --git a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/proxy/NPNProxySPDYToHTTPTest.java b/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/proxy/NPNProxySPDYToHTTPTest.java
deleted file mode 100644
index 76b6d70..0000000
--- a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/proxy/NPNProxySPDYToHTTPTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-public class NPNProxySPDYToHTTPTest extends ProxySPDYToHTTPTest
-{
-    public NPNProxySPDYToHTTPTest(short version)
-    {
-        super(version);
-    }
-}
diff --git a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/proxy/NPNProxySPDYToSPDYLoadTest.java b/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/proxy/NPNProxySPDYToSPDYLoadTest.java
deleted file mode 100644
index 5aa61ab..0000000
--- a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/proxy/NPNProxySPDYToSPDYLoadTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-public class NPNProxySPDYToSPDYLoadTest extends ProxySPDYToSPDYLoadTest
-{
-    public NPNProxySPDYToSPDYLoadTest(short version)
-    {
-        super(version);
-    }
-}
diff --git a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/proxy/NPNProxySPDYToSPDYTest.java b/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/proxy/NPNProxySPDYToSPDYTest.java
deleted file mode 100644
index 210d057..0000000
--- a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/proxy/NPNProxySPDYToSPDYTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server.proxy;
-
-public class NPNProxySPDYToSPDYTest extends ProxySPDYToSPDYTest
-{
-    public NPNProxySPDYToSPDYTest(short version)
-    {
-        super(version);
-    }
-}
diff --git a/jetty-spdy/spdy-npn-tests/src/test/resources/jetty-logging.properties b/jetty-spdy/spdy-npn-tests/src/test/resources/jetty-logging.properties
deleted file mode 100644
index ead13ec..0000000
--- a/jetty-spdy/spdy-npn-tests/src/test/resources/jetty-logging.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
-#org.eclipse.jetty.spdy.LEVEL=DEBUG
diff --git a/jetty-spdy/spdy-npn-tests/src/test/resources/keystore.jks b/jetty-spdy/spdy-npn-tests/src/test/resources/keystore.jks
deleted file mode 100644
index 428ba54..0000000
--- a/jetty-spdy/spdy-npn-tests/src/test/resources/keystore.jks
+++ /dev/null
Binary files differ
diff --git a/jetty-spdy/spdy-npn-tests/src/test/resources/truststore.jks b/jetty-spdy/spdy-npn-tests/src/test/resources/truststore.jks
deleted file mode 100644
index 839cb8c..0000000
--- a/jetty-spdy/spdy-npn-tests/src/test/resources/truststore.jks
+++ /dev/null
Binary files differ
diff --git a/jetty-spdy/spdy-server/pom.xml b/jetty-spdy/spdy-server/pom.xml
deleted file mode 100644
index cd035bf..0000000
--- a/jetty-spdy/spdy-server/pom.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>org.eclipse.jetty.spdy</groupId>
-        <artifactId>spdy-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
-    </parent>
-
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>spdy-server</artifactId>
-    <name>Jetty :: SPDY :: Server Binding</name>
-
-    <properties>
-        <bundle-symbolic-name>${project.groupId}.server</bundle-symbolic-name>
-    </properties>
-
-    <url>http://www.eclipse.org/jetty</url>
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>manifest</goal>
-                        </goals>
-                        <configuration>
-                            <instructions>
-                                <Export-Package>org.eclipse.jetty.spdy.server;version="9.1"</Export-Package>
-                                <Import-Package>org.eclipse.jetty.alpn;resolution:=optional,org.eclipse.jetty.alpn.server;resolution:=optional, org.eclipse.jetty.npn;resolution:=optional,org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
-                                <_nouses>true</_nouses>
-                            </instructions>
-                          </configuration>
-                       </execution>
-                  </executions>
-            </plugin>
-        </plugins>
-    </build>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-core</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.spdy</groupId>
-            <artifactId>spdy-client</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-server</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.npn</groupId>
-            <artifactId>npn-api</artifactId>
-            <version>${npn.api.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.alpn</groupId>
-            <artifactId>alpn-api</artifactId>
-            <version>${alpn.api.version}</version>
-            <scope>provided</scope>
-        </dependency>
-    </dependencies>
-
-</project>
diff --git a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnection.java b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnection.java
deleted file mode 100644
index 0699775..0000000
--- a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnection.java
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.util.List;
-import javax.net.ssl.SSLEngine;
-
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.npn.NextProtoNego;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.NegotiatingServerConnection;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-public class NPNServerConnection extends NegotiatingServerConnection implements NextProtoNego.ServerProvider
-{
-    private static final Logger LOG = Log.getLogger(NPNServerConnection.class);
-
-    public NPNServerConnection(EndPoint endPoint, SSLEngine engine, Connector connector, List<String> protocols, String defaultProtocol)
-    {
-        super(connector, endPoint, engine, protocols, defaultProtocol);
-        NextProtoNego.put(engine, this);
-    }
-
-    @Override
-    public void unsupported()
-    {
-        protocolSelected(getDefaultProtocol());
-    }
-
-    @Override
-    public List<String> protocols()
-    {
-        return getProtocols();
-    }
-
-    @Override
-    public void protocolSelected(String protocol)
-    {
-        if (LOG.isDebugEnabled())
-            LOG.debug("{} protocol selected {}", this, protocol);
-        setProtocol(protocol != null ? protocol : getDefaultProtocol());
-        NextProtoNego.remove(getSSLEngine());
-    }
-
-    @Override
-    public void close()
-    {
-        NextProtoNego.remove(getSSLEngine());
-        super.close();
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnectionFactory.java b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnectionFactory.java
deleted file mode 100644
index b60009d..0000000
--- a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnectionFactory.java
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.util.List;
-import javax.net.ssl.SSLEngine;
-
-import org.eclipse.jetty.io.AbstractConnection;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.npn.NextProtoNego;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
-import org.eclipse.jetty.util.annotation.Name;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-public class NPNServerConnectionFactory extends NegotiatingServerConnectionFactory
-{
-    private static final Logger LOG = Log.getLogger(NPNServerConnectionFactory.class);
-
-    public NPNServerConnectionFactory(@Name("protocols") String... protocols)
-    {
-        super("npn", protocols);
-        try
-        {
-            ClassLoader npnClassLoader = NextProtoNego.class.getClassLoader();
-            if (npnClassLoader != null)
-            {
-                LOG.warn("NPN must be in the boot classloader, not in: " + npnClassLoader);
-                throw new IllegalStateException("NPN must be in the boot classloader");
-            }
-        }
-        catch (Throwable x)
-        {
-            LOG.warn("NPN not available: " + x);
-            throw new IllegalStateException("NPN not available", x);
-        }
-    }
-
-    @Override
-    protected AbstractConnection newServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol)
-    {
-        return new NPNServerConnection(endPoint, engine, connector, protocols, defaultProtocol);
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnectionFactory.java b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnectionFactory.java
deleted file mode 100644
index 25c92d9..0000000
--- a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnectionFactory.java
+++ /dev/null
@@ -1,245 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.eclipse.jetty.io.Connection;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.server.AbstractConnectionFactory;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.spdy.CompressionFactory;
-import org.eclipse.jetty.spdy.FlowControlStrategy;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.StandardSession;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.client.FlowControlStrategyFactory;
-import org.eclipse.jetty.spdy.client.SPDYConnection;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.annotation.ManagedAttribute;
-import org.eclipse.jetty.util.annotation.ManagedObject;
-
-@ManagedObject("SPDY Server Connection Factory")
-public class SPDYServerConnectionFactory extends AbstractConnectionFactory
-{
-    /**
-     * @deprecated use {@link #checkProtocolNegotiationAvailable()} instead.
-     */
-    @Deprecated
-    public static void checkNPNAvailable()
-    {
-        checkProtocolNegotiationAvailable();
-    }
-
-    public static void checkProtocolNegotiationAvailable()
-    {
-        if (!isAvailableInBootClassPath("org.eclipse.jetty.alpn.ALPN") &&
-                !isAvailableInBootClassPath("org.eclipse.jetty.npn.NextProtoNego"))
-            throw new IllegalStateException("No ALPN nor NPN classes available");
-    }
-
-    private static boolean isAvailableInBootClassPath(String className)
-    {
-        try
-        {
-            Class<?> klass = ClassLoader.getSystemClassLoader().loadClass(className);
-            if (klass.getClassLoader() != null)
-                throw new IllegalStateException(className + " must be on JVM boot classpath");
-            return true;
-        }
-        catch (ClassNotFoundException x)
-        {
-            return false;
-        }
-    }
-
-    private final Queue<Session> sessions = new ConcurrentLinkedQueue<>();
-    private final short version;
-    private final ServerSessionFrameListener listener;
-    private int initialWindowSize;
-    private boolean dispatchIO;
-
-    public SPDYServerConnectionFactory(int version)
-    {
-        this(version, null);
-    }
-
-    public SPDYServerConnectionFactory(int version, ServerSessionFrameListener listener)
-    {
-        super("spdy/" + version);
-        this.version = (short)version;
-        this.listener = listener;
-        setInitialWindowSize(65536);
-        setDispatchIO(true);
-    }
-
-    @ManagedAttribute("SPDY version")
-    public short getVersion()
-    {
-        return version;
-    }
-
-    public ServerSessionFrameListener getServerSessionFrameListener()
-    {
-        return listener;
-    }
-
-    @Override
-    public Connection newConnection(Connector connector, EndPoint endPoint)
-    {
-        CompressionFactory compressionFactory = new StandardCompressionFactory();
-        Parser parser = new Parser(compressionFactory.newDecompressor());
-        Generator generator = new Generator(connector.getByteBufferPool(), compressionFactory.newCompressor());
-
-        ServerSessionFrameListener listener = provideServerSessionFrameListener(connector, endPoint);
-        SPDYConnection connection = new ServerSPDYConnection(connector, endPoint, parser, listener,
-                isDispatchIO(), getInputBufferSize());
-
-        FlowControlStrategy flowControlStrategy = newFlowControlStrategy(version);
-
-        StandardSession session = new StandardSession(getVersion(), connector.getByteBufferPool(),
-                connector.getScheduler(), connection, endPoint, connection, 2, listener,
-                generator, flowControlStrategy);
-        session.setWindowSize(getInitialWindowSize());
-        parser.addListener(session);
-        connection.setSession(session);
-
-        sessionOpened(session);
-
-        return configure(connection, connector, endPoint);
-    }
-
-    protected FlowControlStrategy newFlowControlStrategy(short version)
-    {
-        return FlowControlStrategyFactory.newFlowControlStrategy(version);
-    }
-
-    protected ServerSessionFrameListener provideServerSessionFrameListener(Connector connector, EndPoint endPoint)
-    {
-        return listener;
-    }
-
-    @ManagedAttribute("Initial Window Size")
-    public int getInitialWindowSize()
-    {
-        return initialWindowSize;
-    }
-
-    public void setInitialWindowSize(int initialWindowSize)
-    {
-        this.initialWindowSize = initialWindowSize;
-    }
-
-    @ManagedAttribute("Dispatch I/O to a pooled thread")
-    public boolean isDispatchIO()
-    {
-        return dispatchIO;
-    }
-
-    public void setDispatchIO(boolean dispatchIO)
-    {
-        this.dispatchIO = dispatchIO;
-    }
-
-    protected boolean sessionOpened(Session session)
-    {
-        // Add sessions only if the connector is not stopping
-        return sessions.offer(session);
-    }
-
-    protected boolean sessionClosed(Session session)
-    {
-        // Remove sessions only if the connector is not stopping
-        // to avoid concurrent removes during iterations
-        return sessions.remove(session);
-    }
-
-    void closeSessions()
-    {
-        for (Session session : sessions)
-            session.goAway(new GoAwayInfo(), Callback.Adapter.INSTANCE);
-        sessions.clear();
-    }
-
-    @Override
-    protected void doStop() throws Exception
-    {
-        closeSessions();
-        super.doStop();
-    }
-
-    public Collection<Session> getSessions()
-    {
-        return Collections.unmodifiableCollection(sessions);
-    }
-
-    @Override
-    protected void dumpThis(Appendable out) throws IOException
-    {
-        super.dumpThis(out);
-        dump(out, "", sessions);
-    }
-
-    private class ServerSPDYConnection extends SPDYConnection implements Runnable
-    {
-        private final ServerSessionFrameListener listener;
-        private final AtomicBoolean connected = new AtomicBoolean();
-
-        private ServerSPDYConnection(Connector connector, EndPoint endPoint, Parser parser,
-                                     ServerSessionFrameListener listener, boolean dispatchIO, int bufferSize)
-        {
-            super(endPoint, connector.getByteBufferPool(), parser, connector.getExecutor(),
-                    dispatchIO, bufferSize);
-            this.listener = listener;
-        }
-
-        @Override
-        public void onOpen()
-        {
-            super.onOpen();
-            if (connected.compareAndSet(false, true))
-                getExecutor().execute(this);
-        }
-
-        @Override
-        public void onClose()
-        {
-            super.onClose();
-            sessionClosed(getSession());
-        }
-
-        @Override
-        public void run()
-        {
-            // NPE guard to support tests
-            if (listener != null)
-                listener.onConnect(getSession());
-        }
-    }
-
-}
diff --git a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnector.java b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnector.java
deleted file mode 100644
index 64f3bdd..0000000
--- a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnector.java
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.util.Objects;
-
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-
-public class SPDYServerConnector extends ServerConnector
-{
-    public SPDYServerConnector(Server server, ServerSessionFrameListener listener)
-    {
-        super(server, (SslContextFactory)null, new SPDYServerConnectionFactory(SPDY.V2, listener));
-    }
-
-    public SPDYServerConnector(Server server, SslContextFactory sslContextFactory, ServerSessionFrameListener listener)
-    {
-        this(server, sslContextFactory, listener, new NPNServerConnectionFactory("spdy/3", "spdy/2", "http/1.1"));
-    }
-
-    public SPDYServerConnector(Server server, SslContextFactory sslContextFactory, ServerSessionFrameListener listener, NegotiatingServerConnectionFactory negotiator)
-    {
-        super(server, Objects.requireNonNull(sslContextFactory),
-                negotiator,
-                new SPDYServerConnectionFactory(SPDY.V3, listener),
-                new SPDYServerConnectionFactory(SPDY.V2, listener),
-                new HttpConnectionFactory());
-        negotiator.setDefaultProtocol("http/1.1");
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/AbstractTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/AbstractTest.java
deleted file mode 100644
index 42c16c8..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/AbstractTest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server;
-
-import java.net.InetSocketAddress;
-import java.util.concurrent.Executor;
-
-import org.eclipse.jetty.server.ConnectionFactory;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.junit.After;
-import org.junit.Rule;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-
-public abstract class AbstractTest
-{
-    @Rule
-    public final TestWatcher testName = new TestWatcher()
-    {
-
-        @Override
-        public void starting(Description description)
-        {
-            super.starting(description);
-            System.err.printf("Running %s.%s()%n",
-                    description.getClassName(),
-                    description.getMethodName());
-        }
-    };
-
-    protected final short version = SPDY.V2;
-
-    protected Server server;
-    protected SPDYClient.Factory clientFactory;
-    protected ServerConnector connector;
-
-    protected InetSocketAddress startServer(ServerSessionFrameListener listener) throws Exception
-    {
-        return startServer(version, listener);
-    }
-
-    protected InetSocketAddress startServer(short version, ServerSessionFrameListener listener) throws Exception
-    {
-        if (server == null)
-            server = newServer();
-        if (connector == null)
-            connector = newSPDYServerConnector(server, listener);
-        if (listener == null)
-            listener = connector.getConnectionFactory(SPDYServerConnectionFactory.class).getServerSessionFrameListener();
-
-        ConnectionFactory spdy = new SPDYServerConnectionFactory(version, listener);
-        connector.addConnectionFactory(spdy);
-        connector.setPort(0);
-        server.addConnector(connector);
-
-        if (connector.getConnectionFactory(NPNServerConnectionFactory.class)!=null)
-            connector.getConnectionFactory(NPNServerConnectionFactory.class).setDefaultProtocol(spdy.getProtocol());
-        else
-            connector.setDefaultProtocol(spdy.getProtocol());
-
-        server.start();
-        return new InetSocketAddress("localhost", connector.getLocalPort());
-    }
-
-    protected Server newServer()
-    {
-        QueuedThreadPool pool = new QueuedThreadPool();
-        pool.setName(pool.getName()+"-server");
-        return new Server(pool);
-    }
-
-    protected ServerConnector newSPDYServerConnector(Server server, ServerSessionFrameListener listener)
-    {
-        return new SPDYServerConnector(server, listener);
-    }
-
-    protected Session startClient(InetSocketAddress socketAddress, SessionFrameListener listener) throws Exception
-    {
-        return startClient(version, socketAddress, listener);
-    }
-
-    protected Session startClient(short version, InetSocketAddress socketAddress, SessionFrameListener listener) throws Exception
-    {
-        if (clientFactory == null)
-        {
-            QueuedThreadPool threadPool = new QueuedThreadPool();
-            threadPool.setName(threadPool.getName() + "-client");
-            clientFactory = newSPDYClientFactory(threadPool);
-        }
-        clientFactory.start();
-
-        return clientFactory.newSPDYClient(version).connect(socketAddress, listener);
-    }
-
-    protected SPDYClient.Factory newSPDYClientFactory(Executor threadPool)
-    {
-        return new SPDYClient.Factory(threadPool);
-    }
-
-    protected SslContextFactory newSslContextFactory()
-    {
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
-        sslContextFactory.setKeyStorePassword("storepwd");
-        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
-        sslContextFactory.setTrustStorePassword("storepwd");
-        sslContextFactory.setProtocol("TLSv1");
-        sslContextFactory.setIncludeProtocols("TLSv1");
-        return sslContextFactory;
-    }
-
-    @After
-    public void destroy() throws Exception
-    {
-        if (clientFactory != null)
-        {
-            clientFactory.stop();
-        }
-        if (server != null)
-        {
-            server.stop();
-            server.join();
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/ClosedStreamTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/ClosedStreamTest.java
deleted file mode 100644
index 91d1b9f..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/ClosedStreamTest.java
+++ /dev/null
@@ -1,273 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.junit.Assert.assertThat;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.ServerSocketChannel;
-import java.nio.channels.SocketChannel;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.BytesDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionStatus;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.GoAwayFrame;
-import org.eclipse.jetty.spdy.frames.RstStreamFrame;
-import org.eclipse.jetty.spdy.frames.SynReplyFrame;
-import org.eclipse.jetty.spdy.frames.SynStreamFrame;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.eclipse.jetty.spdy.parser.Parser.Listener;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class ClosedStreamTest extends AbstractTest
-{
-    //TODO: Right now it sends a rst as the stream is unknown to the session once it's closed.
-    //TODO: But according to the spec we probably should just ignore the data?!
-    @Test
-    public void testDataSentOnClosedStreamIsIgnored() throws Exception
-    {
-        ServerSocketChannel server = ServerSocketChannel.open();
-        server.bind(new InetSocketAddress("localhost", 0));
-
-        Session session = startClient(new InetSocketAddress("localhost", server.socket().getLocalPort()), null);
-        final CountDownLatch dataLatch = new CountDownLatch(2);
-        session.syn(new SynInfo(new Fields(), true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataLatch.countDown();
-            }
-        });
-
-        SocketChannel channel = server.accept();
-        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
-        channel.read(readBuffer);
-        readBuffer.flip();
-        int streamId = readBuffer.getInt(8);
-
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
-
-        ByteBuffer writeBuffer = generator.control(new SynReplyFrame(SPDY.V2, (byte)0, streamId, new Fields()));
-        channel.write(writeBuffer);
-        Assert.assertThat(writeBuffer.hasRemaining(), is(false));
-
-        byte[] bytes = new byte[1];
-        writeBuffer = generator.data(streamId, bytes.length, new BytesDataInfo(bytes, true));
-        channel.write(writeBuffer);
-        Assert.assertThat(writeBuffer.hasRemaining(), is(false));
-
-        // Write again to simulate the faulty condition
-        writeBuffer.flip();
-        channel.write(writeBuffer);
-        Assert.assertThat(writeBuffer.hasRemaining(), is(false));
-
-        Assert.assertFalse(dataLatch.await(1, TimeUnit.SECONDS));
-
-        session.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-
-        server.close();
-    }
-
-    @Test
-    public void testSendDataOnHalfClosedStreamCausesExceptionOnServer() throws Exception
-    {
-        final CountDownLatch replyReceivedLatch = new CountDownLatch(1);
-        final CountDownLatch clientReceivedDataLatch = new CountDownLatch(1);
-        final CountDownLatch exceptionWhenSendingData = new CountDownLatch(1);
-
-        Session clientSession = startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(true), new Callback.Adapter());
-                try
-                {
-                    replyReceivedLatch.await(5,TimeUnit.SECONDS);
-                }
-                catch (InterruptedException e)
-                {
-                    e.printStackTrace();
-                }
-                try
-                {
-                    stream.data(new StringDataInfo("data send after half closed",false), new Callback.Adapter());
-                }
-                catch (RuntimeException e)
-                {
-                    // we expect an exception here, but we don't want it to be logged
-                    exceptionWhenSendingData.countDown();
-                }
-
-                return null;
-            }
-        }),null);
-
-        Stream stream = clientSession.syn(new SynInfo(new Fields(), false),new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                replyReceivedLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                clientReceivedDataLatch.countDown();
-            }
-        });
-        assertThat("reply has been received by client",replyReceivedLatch.await(5,TimeUnit.SECONDS),is(true));
-        assertThat("stream is half closed from server",stream.isHalfClosed(),is(true));
-        assertThat("client has not received any data sent after stream was half closed by server",
-                clientReceivedDataLatch.await(1,TimeUnit.SECONDS), is(false));
-        assertThat("sending data threw an exception",exceptionWhenSendingData.await(5,TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testV2ReceiveDataOnHalfClosedStream() throws Exception
-    {
-        runReceiveDataOnHalfClosedStream(SPDY.V2);
-    }
-
-    @Test
-    @Ignore("until v3 is properly implemented")
-    public void testV3ReceiveDataOnHalfClosedStream() throws Exception
-    {
-        runReceiveDataOnHalfClosedStream(SPDY.V3);
-    }
-
-    private void runReceiveDataOnHalfClosedStream(short version) throws Exception
-    {
-        final CountDownLatch clientResetReceivedLatch = new CountDownLatch(1);
-        final CountDownLatch serverReplySentLatch = new CountDownLatch(1);
-        final CountDownLatch clientReplyReceivedLatch = new CountDownLatch(1);
-        final CountDownLatch serverDataReceivedLatch = new CountDownLatch(1);
-        final CountDownLatch goAwayReceivedLatch = new CountDownLatch(1);
-
-        InetSocketAddress startServer = startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                serverReplySentLatch.countDown();
-                try
-                {
-                    clientReplyReceivedLatch.await(5,TimeUnit.SECONDS);
-                }
-                catch (InterruptedException e)
-                {
-                    e.printStackTrace();
-                }
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        serverDataReceivedLatch.countDown();
-                    }
-                };
-            }
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                goAwayReceivedLatch.countDown();
-            }
-        });
-
-        final Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory().newCompressor());
-        int streamId = 1;
-        ByteBuffer synData = generator.control(new SynStreamFrame(version,SynInfo.FLAG_CLOSE, streamId,0,(byte)0,(short)0,new Fields()));
-
-        final SocketChannel socketChannel = SocketChannel.open(startServer);
-        socketChannel.write(synData);
-        assertThat("synData is fully written", synData.hasRemaining(), is(false));
-
-        assertThat("server: push reply is sent",serverReplySentLatch.await(5,TimeUnit.SECONDS),is(true));
-
-        Parser parser = new Parser(new StandardCompressionFactory.StandardDecompressor());
-        parser.addListener(new Listener.Adapter()
-        {
-            @Override
-            public void onControlFrame(ControlFrame frame)
-            {
-                if (frame instanceof SynReplyFrame)
-                {
-                    SynReplyFrame synReplyFrame = (SynReplyFrame)frame;
-                    clientReplyReceivedLatch.countDown();
-                    int streamId = synReplyFrame.getStreamId();
-                    ByteBuffer data = generator.data(streamId,0,new StringDataInfo("data",false));
-                    try
-                    {
-                        socketChannel.write(data);
-                    }
-                    catch (IOException e)
-                    {
-                        e.printStackTrace();
-                    }
-                }
-                else if (frame instanceof RstStreamFrame)
-                {
-                    clientResetReceivedLatch.countDown();
-                }
-            }
-        });
-        ByteBuffer response = ByteBuffer.allocate(28);
-        socketChannel.read(response);
-        response.flip();
-        parser.parse(response);
-
-        assertThat("server didn't receive data",serverDataReceivedLatch.await(1,TimeUnit.SECONDS),not(true));
-        assertThat("client didn't receive reset",clientResetReceivedLatch.await(1,TimeUnit.SECONDS),not(true));
-
-        ByteBuffer buffer = generator.control(new GoAwayFrame(version, streamId, SessionStatus.OK.getCode()));
-        socketChannel.write(buffer);
-        Assert.assertThat(buffer.hasRemaining(), is(false));
-
-        assertThat("GoAway frame is received by server", goAwayReceivedLatch.await(5,TimeUnit.SECONDS), is(true));
-
-        socketChannel.close();
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/FlowControlTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/FlowControlTest.java
deleted file mode 100644
index 82f2ec7..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/FlowControlTest.java
+++ /dev/null
@@ -1,493 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Exchanger;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.BytesDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.SPDYException;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Settings;
-import org.eclipse.jetty.spdy.api.SettingsInfo;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.FutureCallback;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class FlowControlTest extends AbstractTest
-{
-    @Test
-    public void testFlowControlWithConcurrentSettings() throws Exception
-    {
-        // Initial window is 64 KiB. We allow the client to send 1024 B
-        // then we change the window to 512 B. At this point, the client
-        // must stop sending data (although the initial window allows it)
-
-        final int size = 512;
-        final AtomicReference<DataInfo> dataInfoRef = new AtomicReference<>();
-        final CountDownLatch dataLatch = new CountDownLatch(2);
-        final CountDownLatch settingsLatch = new CountDownLatch(1);
-        Session session = startClient(SPDY.V3, startServer(SPDY.V3, new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(true), new Callback.Adapter());
-                return new StreamFrameListener.Adapter()
-                {
-                    private final AtomicInteger dataFrames = new AtomicInteger();
-
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        int dataFrameCount = dataFrames.incrementAndGet();
-                        if (dataFrameCount == 1)
-                        {
-                            dataInfoRef.set(dataInfo);
-                            Settings settings = new Settings();
-                            settings.put(new Settings.Setting(Settings.ID.INITIAL_WINDOW_SIZE, size));
-                            stream.getSession().settings(new SettingsInfo(settings), new FutureCallback());
-                        }
-                        else if (dataFrameCount > 1)
-                        {
-                            dataInfo.consume(dataInfo.length());
-                            dataLatch.countDown();
-                        }
-                    }
-                };
-            }
-        }), new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onSettings(Session session, SettingsInfo settingsInfo)
-            {
-                settingsLatch.countDown();
-            }
-        });
-
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), false, (byte)0), null);
-        stream.data(new BytesDataInfo(new byte[size * 2], false));
-        settingsLatch.await(5, TimeUnit.SECONDS);
-
-        // Send the second chunk of data, must not arrive since we're flow control stalled now
-        stream.data(new BytesDataInfo(new byte[size * 2], true), new Callback.Adapter());
-        Assert.assertFalse(dataLatch.await(1, TimeUnit.SECONDS));
-
-        // Consume the data arrived to server, this will resume flow control
-        DataInfo dataInfo = dataInfoRef.get();
-        dataInfo.consume(dataInfo.length());
-
-        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testServerFlowControlOneBigWrite() throws Exception
-    {
-        final int windowSize = 1536;
-        final int length = 5 * windowSize;
-        final CountDownLatch settingsLatch = new CountDownLatch(1);
-        Session session = startClient(SPDY.V3, startServer(SPDY.V3, new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public void onSettings(Session session, SettingsInfo settingsInfo)
-            {
-                settingsLatch.countDown();
-            }
-
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                stream.data(new BytesDataInfo(new byte[length], true), new Callback.Adapter());
-                return null;
-            }
-        }), null);
-
-        Settings settings = new Settings();
-        settings.put(new Settings.Setting(Settings.ID.INITIAL_WINDOW_SIZE, windowSize));
-        session.settings(new SettingsInfo(settings));
-
-        Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
-
-        final Exchanger<DataInfo> exchanger = new Exchanger<>();
-        session.syn(new SynInfo(new Fields(), true), new StreamFrameListener.Adapter()
-        {
-            private AtomicInteger dataFrames = new AtomicInteger();
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                try
-                {
-                    int dataFrames = this.dataFrames.incrementAndGet();
-                    if (dataFrames == 1)
-                    {
-                        // Do not consume nor read from the data frame.
-                        // We should then be flow-control stalled
-                        exchanger.exchange(dataInfo);
-                    }
-                    else if (dataFrames == 2)
-                    {
-                        // Read but not consume, we should be flow-control stalled
-                        dataInfo.asByteBuffer(false);
-                        exchanger.exchange(dataInfo);
-                    }
-                    else if (dataFrames == 3)
-                    {
-                        // Consume partially, we should be flow-control stalled
-                        dataInfo.consumeInto(ByteBuffer.allocate(dataInfo.length() / 2));
-                        exchanger.exchange(dataInfo);
-                    }
-                    else if (dataFrames == 4 || dataFrames == 5)
-                    {
-                        // Consume totally
-                        dataInfo.asByteBuffer(true);
-                        exchanger.exchange(dataInfo);
-                    }
-                    else
-                    {
-                        Assert.fail();
-                    }
-                }
-                catch (InterruptedException x)
-                {
-                    throw new SPDYException(x);
-                }
-            }
-        });
-
-        DataInfo dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
-        checkThatWeAreFlowControlStalled(exchanger);
-
-        Assert.assertEquals(windowSize, dataInfo.available());
-        Assert.assertEquals(0, dataInfo.consumed());
-        dataInfo.asByteBuffer(true);
-
-        dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
-        checkThatWeAreFlowControlStalled(exchanger);
-
-        Assert.assertEquals(0, dataInfo.available());
-        Assert.assertEquals(0, dataInfo.consumed());
-        dataInfo.consume(dataInfo.length());
-
-        dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
-        checkThatWeAreFlowControlStalled(exchanger);
-
-        Assert.assertEquals(dataInfo.length() / 2, dataInfo.consumed());
-        dataInfo.asByteBuffer(true);
-
-        dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
-        Assert.assertEquals(dataInfo.length(), dataInfo.consumed());
-        // Check that we are not flow control stalled
-        dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
-        Assert.assertEquals(dataInfo.length(), dataInfo.consumed());
-    }
-
-    @Test
-    public void testClientFlowControlOneBigWrite() throws Exception
-    {
-        final int windowSize = 1536;
-        final Exchanger<DataInfo> exchanger = new Exchanger<>();
-        final CountDownLatch settingsLatch = new CountDownLatch(1);
-        Session session = startClient(SPDY.V3, startServer(SPDY.V3, new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public void onConnect(Session session)
-            {
-                Settings settings = new Settings();
-                settings.put(new Settings.Setting(Settings.ID.INITIAL_WINDOW_SIZE, windowSize));
-                session.settings(new SettingsInfo(settings), new FutureCallback());
-            }
-
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                return new StreamFrameListener.Adapter()
-                {
-                    private AtomicInteger dataFrames = new AtomicInteger();
-
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        try
-                        {
-                            int dataFrames = this.dataFrames.incrementAndGet();
-                            if (dataFrames == 1)
-                            {
-                                // Do not consume nor read from the data frame.
-                                // We should then be flow-control stalled
-                                exchanger.exchange(dataInfo);
-                            }
-                            else if (dataFrames == 2)
-                            {
-                                // Read but not consume, we should be flow-control stalled
-                                dataInfo.asByteBuffer(false);
-                                exchanger.exchange(dataInfo);
-                            }
-                            else if (dataFrames == 3)
-                            {
-                                // Consume partially, we should be flow-control stalled
-                                dataInfo.consumeInto(ByteBuffer.allocate(dataInfo.length() / 2));
-                                exchanger.exchange(dataInfo);
-                            }
-                            else if (dataFrames == 4 || dataFrames == 5)
-                            {
-                                // Consume totally
-                                dataInfo.asByteBuffer(true);
-                                exchanger.exchange(dataInfo);
-                            }
-                            else
-                            {
-                                Assert.fail();
-                            }
-                        }
-                        catch (InterruptedException x)
-                        {
-                            throw new SPDYException(x);
-                        }
-                    }
-                };
-            }
-        }), new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onSettings(Session session, SettingsInfo settingsInfo)
-            {
-                settingsLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
-
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), false, (byte)0), null);
-        final int length = 5 * windowSize;
-        stream.data(new BytesDataInfo(new byte[length], true), new Callback.Adapter());
-
-        DataInfo dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
-        checkThatWeAreFlowControlStalled(exchanger);
-
-        Assert.assertEquals(windowSize, dataInfo.available());
-        Assert.assertEquals(0, dataInfo.consumed());
-        dataInfo.asByteBuffer(true);
-
-        dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
-        checkThatWeAreFlowControlStalled(exchanger);
-
-        Assert.assertEquals(0, dataInfo.available());
-        Assert.assertEquals(0, dataInfo.consumed());
-        dataInfo.consume(dataInfo.length());
-
-        dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
-        checkThatWeAreFlowControlStalled(exchanger);
-
-        Assert.assertEquals(dataInfo.length() / 2, dataInfo.consumed());
-        dataInfo.asByteBuffer(true);
-
-        dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
-        Assert.assertEquals(dataInfo.length(), dataInfo.consumed());
-        // Check that we are not flow control stalled
-        dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
-        Assert.assertEquals(dataInfo.length(), dataInfo.consumed());
-    }
-
-    @Test
-    public void testStreamsStalledDoesNotStallOtherStreams() throws Exception
-    {
-        final int windowSize = 1024;
-        final CountDownLatch settingsLatch = new CountDownLatch(1);
-        Session session = startClient(SPDY.V3, startServer(SPDY.V3, new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public void onSettings(Session session, SettingsInfo settingsInfo)
-            {
-                settingsLatch.countDown();
-            }
-
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                stream.data(new BytesDataInfo(new byte[windowSize * 2], true), new Callback.Adapter());
-                return null;
-            }
-        }), null);
-        Settings settings = new Settings();
-        settings.put(new Settings.Setting(Settings.ID.INITIAL_WINDOW_SIZE, windowSize));
-        session.settings(new SettingsInfo(settings));
-
-        Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
-
-        final CountDownLatch latch = new CountDownLatch(3);
-        final AtomicReference<DataInfo> dataInfoRef1 = new AtomicReference<>();
-        final AtomicReference<DataInfo> dataInfoRef2 = new AtomicReference<>();
-        session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), true, (byte)0), new StreamFrameListener.Adapter()
-        {
-            private final AtomicInteger dataFrames = new AtomicInteger();
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                int frames = dataFrames.incrementAndGet();
-                if (frames == 1)
-                {
-                    // Do not consume it to stall flow control
-                    dataInfoRef1.set(dataInfo);
-                }
-                else
-                {
-                    dataInfo.consume(dataInfo.length());
-                    if (dataInfo.isClose())
-                        latch.countDown();
-                }
-            }
-        });
-        session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), true, (byte)0), new StreamFrameListener.Adapter()
-        {
-            private final AtomicInteger dataFrames = new AtomicInteger();
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                int frames = dataFrames.incrementAndGet();
-                if (frames == 1)
-                {
-                    // Do not consume it to stall flow control
-                    dataInfoRef2.set(dataInfo);
-                }
-                else
-                {
-                    dataInfo.consume(dataInfo.length());
-                    if (dataInfo.isClose())
-                        latch.countDown();
-                }
-            }
-        });
-        session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), true, (byte)0), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                DataInfo dataInfo1 = dataInfoRef1.getAndSet(null);
-                if (dataInfo1 != null)
-                    dataInfo1.consume(dataInfo1.length());
-                DataInfo dataInfo2 = dataInfoRef2.getAndSet(null);
-                if (dataInfo2 != null)
-                    dataInfo2.consume(dataInfo2.length());
-                dataInfo.consume(dataInfo.length());
-                if (dataInfo.isClose())
-                    latch.countDown();
-            }
-        });
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testSendBigFileWithoutFlowControl() throws Exception
-    {
-        testSendBigFile(SPDY.V2);
-    }
-
-    @Test
-    public void testSendBigFileWithFlowControl() throws Exception
-    {
-        testSendBigFile(SPDY.V3);
-    }
-
-    private void testSendBigFile(short version) throws Exception
-    {
-        final int dataSize = 1024 * 1024;
-        final ByteBufferDataInfo bigByteBufferDataInfo = new ByteBufferDataInfo(ByteBuffer.allocate(dataSize),false);
-        final CountDownLatch allDataReceivedLatch = new CountDownLatch(1);
-
-        Session session = startClient(version, startServer(version, new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                stream.data(bigByteBufferDataInfo, new Callback.Adapter());
-                return null;
-            }
-        }),new SessionFrameListener.Adapter());
-
-        session.syn(new SynInfo(new Fields(), false),new StreamFrameListener.Adapter()
-        {
-            private int dataBytesReceived;
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataBytesReceived = dataBytesReceived + dataInfo.length();
-                dataInfo.consume(dataInfo.length());
-                if (dataBytesReceived == dataSize)
-                    allDataReceivedLatch.countDown();
-            }
-        });
-
-        assertThat("all data bytes have been received by the client", allDataReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    private void checkThatWeAreFlowControlStalled(final Exchanger<DataInfo> exchanger)
-    {
-        expectException(TimeoutException.class, new Callable<DataInfo>()
-        {
-            @Override
-            public DataInfo call() throws Exception
-            {
-                return exchanger.exchange(null, 1, TimeUnit.SECONDS);
-            }
-        });
-    }
-
-    private void expectException(Class<? extends Exception> exception, Callable<DataInfo> command)
-    {
-        try
-        {
-            command.call();
-            Assert.fail();
-        }
-        catch (Exception x)
-        {
-            Assert.assertSame(exception, x.getClass());
-        }
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/GoAwayTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/GoAwayTest.java
deleted file mode 100644
index c2cc525..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/GoAwayTest.java
+++ /dev/null
@@ -1,234 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.SessionStatus;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.FutureCallback;
-import org.eclipse.jetty.util.FuturePromise;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class GoAwayTest extends AbstractTest
-{
-    @Test
-    public void testServerReceivesGoAwayOnClientGoAway() throws Exception
-    {
-        final CountDownLatch latch = new CountDownLatch(1);
-        ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(true), new Callback.Adapter());
-                return null;
-            }
-
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                Assert.assertEquals(0, goAwayInfo.getLastStreamId());
-                Assert.assertSame(SessionStatus.OK, goAwayInfo.getSessionStatus());
-                latch.countDown();
-            }
-        };
-        Session session = startClient(startServer(serverSessionFrameListener), null);
-
-        session.syn(new SynInfo(new Fields(), true), null);
-
-        session.goAway(new GoAwayInfo());
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testClientReceivesGoAwayOnServerGoAway() throws Exception
-    {
-        ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(true), new Callback.Adapter());
-                stream.getSession().goAway(new GoAwayInfo(), new FutureCallback());
-                return null;
-            }
-        };
-        final AtomicReference<GoAwayResultInfo> ref = new AtomicReference<>();
-        final CountDownLatch latch = new CountDownLatch(1);
-        SessionFrameListener clientSessionFrameListener = new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                ref.set(goAwayInfo);
-                latch.countDown();
-            }
-        };
-        Session session = startClient(startServer(serverSessionFrameListener), clientSessionFrameListener);
-
-        Stream stream1 = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), true, (byte)0), null);
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-        GoAwayResultInfo goAwayResultInfo = ref.get();
-        Assert.assertNotNull(goAwayResultInfo);
-        Assert.assertEquals(stream1.getId(), goAwayResultInfo.getLastStreamId());
-        Assert.assertSame(SessionStatus.OK, goAwayResultInfo.getSessionStatus());
-    }
-
-    @Test
-    public void testSynStreamIgnoredAfterGoAway() throws Exception
-    {
-        final CountDownLatch latch = new CountDownLatch(1);
-        ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
-        {
-            private final AtomicInteger syns = new AtomicInteger();
-
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                int synCount = syns.incrementAndGet();
-                if (synCount == 1)
-                {
-                    stream.reply(new ReplyInfo(true), new Callback.Adapter());
-                    stream.getSession().goAway(new GoAwayInfo(), new FutureCallback());
-                }
-                else
-                {
-                    latch.countDown();
-                }
-                return null;
-            }
-        };
-        SessionFrameListener clientSessionFrameListener = new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                session.syn(new SynInfo(new Fields(), true), null, new FuturePromise<Stream>());
-            }
-        };
-        Session session = startClient(startServer(serverSessionFrameListener), clientSessionFrameListener);
-
-        session.syn(new SynInfo(new Fields(), true), null);
-
-        Assert.assertFalse(latch.await(1, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testDataNotProcessedAfterGoAway() throws Exception
-    {
-        final CountDownLatch closeLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
-        {
-            private AtomicInteger syns = new AtomicInteger();
-
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(true), new Callback.Adapter());
-                int synCount = syns.incrementAndGet();
-                if (synCount == 1)
-                {
-                    return null;
-                }
-                else
-                {
-                    stream.getSession().goAway(new GoAwayInfo(), new FutureCallback());
-                    closeLatch.countDown();
-                    return new StreamFrameListener.Adapter()
-                    {
-                        @Override
-                        public void onData(Stream stream, DataInfo dataInfo)
-                        {
-                            dataLatch.countDown();
-                        }
-                    };
-                }
-            }
-        };
-        final AtomicReference<GoAwayResultInfo> goAwayRef = new AtomicReference<>();
-        final CountDownLatch goAwayLatch = new CountDownLatch(1);
-        SessionFrameListener clientSessionFrameListener = new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                goAwayRef.set(goAwayInfo);
-                goAwayLatch.countDown();
-            }
-        };
-        Session session = startClient(startServer(serverSessionFrameListener), clientSessionFrameListener);
-
-        // First stream is processed ok
-        final CountDownLatch reply1Latch = new CountDownLatch(1);
-        session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), true, (byte)0), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                reply1Latch.countDown();
-            }
-        });
-        Assert.assertTrue(reply1Latch.await(5, TimeUnit.SECONDS));
-
-        // Second stream is closed in the middle
-        Stream stream2 = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), false, (byte)0), null);
-        Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
-
-        // There is a race between the data we want to send, and the client
-        // closing the connection because the server closed it after the
-        // go_away, so we guard with a try/catch to have the test pass cleanly
-        try
-        {
-            stream2.data(new StringDataInfo("foo", true));
-            Assert.assertFalse(dataLatch.await(1, TimeUnit.SECONDS));
-        }
-        catch (ExecutionException x)
-        {
-            // doesn't matter which exception we get, it's important that the data is not been written and the
-            // previous assertion is true
-        }
-
-        // The last good stream is the second, because it was received by the server
-        Assert.assertTrue(goAwayLatch.await(5, TimeUnit.SECONDS));
-        GoAwayResultInfo goAway = goAwayRef.get();
-        Assert.assertNotNull(goAway);
-        Assert.assertEquals(stream2.getId(), goAway.getLastStreamId());
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/HeadersTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/HeadersTest.java
deleted file mode 100644
index 98eefb0..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/HeadersTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class HeadersTest extends AbstractTest
-{
-    @Test
-    public void testHeaders() throws Exception
-    {
-        ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onHeaders(Stream stream, HeadersInfo headersInfo)
-                    {
-                        Assert.assertTrue(stream.isHalfClosed());
-                        stream.headers(new HeadersInfo(new Fields(), true), new Callback.Adapter());
-                        Assert.assertTrue(stream.isClosed());
-                    }
-                };
-            }
-        };
-
-        Session session = startClient(startServer(serverSessionFrameListener), null);
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        session.syn(new SynInfo(new Fields(), false), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Fields headers = new Fields();
-                headers.put("foo", "bar");
-                headers.put("baz", "woo");
-                stream.headers(new HeadersInfo(headers, true), new Callback.Adapter());
-                Assert.assertTrue(stream.isHalfClosed());
-            }
-
-            @Override
-            public void onHeaders(Stream stream, HeadersInfo headersInfo)
-            {
-                Assert.assertTrue(stream.isClosed());
-                latch.countDown();
-            }
-        });
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/IdleTimeoutTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/IdleTimeoutTest.java
deleted file mode 100644
index f22904c..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/IdleTimeoutTest.java
+++ /dev/null
@@ -1,257 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server;
-
-import java.net.InetSocketAddress;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class IdleTimeoutTest extends AbstractTest
-{
-    private final int idleTimeout = 1000;
-
-    @Test
-    public void testServerEnforcingIdleTimeout() throws Exception
-    {
-        server = newServer();
-        connector = newSPDYServerConnector(server, new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(true), new Callback.Adapter());
-                return null;
-            }
-        });
-        connector.setIdleTimeout(idleTimeout);
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        Session session = startClient(startServer(null), new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                latch.countDown();
-            }
-        });
-
-        session.syn(new SynInfo(new Fields(), true), null);
-
-        Assert.assertTrue(latch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
-    }
-
-    @Test
-    public void testServerEnforcingIdleTimeoutWithUnrespondedStream() throws Exception
-    {
-        server = newServer();
-        connector = newSPDYServerConnector(server, null);
-        connector.setIdleTimeout(idleTimeout);
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        Session session = startClient(startServer(null), new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                latch.countDown();
-            }
-        });
-
-        // The SYN is not replied, and the server should idle timeout
-        session.syn(new SynInfo(new Fields(), true), null);
-
-        Assert.assertTrue(latch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
-    }
-
-    @Test
-    public void testServerNotEnforcingIdleTimeoutWithPendingStream() throws Exception
-    {
-        server = newServer();
-        connector = newSPDYServerConnector(server, new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                try
-                {
-                    Thread.sleep(2 * idleTimeout);
-                    stream.reply(new ReplyInfo(true), new Callback.Adapter());
-                    return null;
-                }
-                catch (InterruptedException x)
-                {
-                    Assert.fail();
-                    return null;
-                }
-            }
-        });
-        connector.setIdleTimeout(idleTimeout);
-
-        final CountDownLatch goAwayLatch = new CountDownLatch(1);
-        Session session = startClient(startServer(null), new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                goAwayLatch.countDown();
-            }
-        });
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(new Fields(), true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                replyLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(replyLatch.await(3 * idleTimeout, TimeUnit.MILLISECONDS));
-
-        // Just make sure onGoAway has never been called, but don't wait too much
-        Assert.assertFalse(goAwayLatch.await(idleTimeout / 2, TimeUnit.MILLISECONDS));
-    }
-
-    @Test
-    public void testClientEnforcingIdleTimeout() throws Exception
-    {
-        final CountDownLatch latch = new CountDownLatch(1);
-        InetSocketAddress address = startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(true), new Callback.Adapter());
-                return null;
-            }
-
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                latch.countDown();
-            }
-        });
-
-        QueuedThreadPool threadPool = new QueuedThreadPool();
-        threadPool.setName(threadPool.getName() + "-client");
-        clientFactory = newSPDYClientFactory(threadPool);
-        clientFactory.start();
-        SPDYClient client = clientFactory.newSPDYClient(SPDY.V2);
-        client.setIdleTimeout(idleTimeout);
-        Session session = client.connect(address, null);
-
-        session.syn(new SynInfo(new Fields(), true), null);
-
-        Assert.assertTrue(latch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
-    }
-
-    @Test
-    public void testClientEnforcingIdleTimeoutWithUnrespondedStream() throws Exception
-    {
-        final CountDownLatch latch = new CountDownLatch(1);
-        InetSocketAddress address = startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                latch.countDown();
-            }
-        });
-
-        QueuedThreadPool threadPool = new QueuedThreadPool();
-        threadPool.setName(threadPool.getName() + "-client");
-        clientFactory = newSPDYClientFactory(threadPool);
-        clientFactory.start();
-        SPDYClient client = clientFactory.newSPDYClient(SPDY.V2);
-        client.setIdleTimeout(idleTimeout);
-        Session session = client.connect(address, null);
-
-        session.syn(new SynInfo(new Fields(), true), null);
-
-        Assert.assertTrue(latch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
-    }
-
-    @Test
-    public void testClientNotEnforcingIdleTimeoutWithPendingStream() throws Exception
-    {
-        final CountDownLatch latch = new CountDownLatch(1);
-        InetSocketAddress address = startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(true), new Callback.Adapter());
-                return null;
-            }
-
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                latch.countDown();
-            }
-        });
-
-        QueuedThreadPool threadPool = new QueuedThreadPool();
-        threadPool.setName(threadPool.getName() + "-client");
-        clientFactory = newSPDYClientFactory(threadPool);
-        clientFactory.start();
-        SPDYClient client = clientFactory.newSPDYClient(SPDY.V2);
-        client.setIdleTimeout(idleTimeout);
-        Session session = client.connect(address, null);
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        session.syn(new SynInfo(new Fields(), true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                try
-                {
-                    Thread.sleep(2 * idleTimeout);
-                    replyLatch.countDown();
-                }
-                catch (InterruptedException e)
-                {
-                    Assert.fail();
-                }
-            }
-        });
-
-        Assert.assertFalse(latch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
-        Assert.assertTrue(replyLatch.await(3 * idleTimeout, TimeUnit.MILLISECONDS));
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/MaxConcurrentStreamTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/MaxConcurrentStreamTest.java
deleted file mode 100644
index 298927d..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/MaxConcurrentStreamTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Settings;
-import org.eclipse.jetty.spdy.api.SettingsInfo;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class MaxConcurrentStreamTest extends AbstractTest
-{
-    @Test
-    public void testMaxConcurrentStreamsSetByServer() throws Exception, ExecutionException
-    {
-        final CountDownLatch settingsReceivedLatch = new CountDownLatch(1);
-        final CountDownLatch dataReceivedLatch = new CountDownLatch(1);
-
-        Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public void onConnect(Session session)
-            {
-                Settings settings = new Settings();
-                settings.put(new Settings.Setting(Settings.ID.MAX_CONCURRENT_STREAMS, 1));
-                try
-                {
-                    session.settings(new SettingsInfo(settings));
-                }
-                catch (ExecutionException | InterruptedException | TimeoutException e)
-                {
-                    e.printStackTrace();
-                }
-            }
-
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                try
-                {
-                    stream.reply(new ReplyInfo(true));
-                }
-                catch (ExecutionException | InterruptedException | TimeoutException e)
-                {
-                    e.printStackTrace();
-                }
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataReceivedLatch.countDown();
-                    }
-                };
-            }
-        }), new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onSettings(Session session, SettingsInfo settingsInfo)
-            {
-                settingsReceivedLatch.countDown();
-            }
-        });
-
-        assertThat("Settings frame received", settingsReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
-
-        SynInfo synInfo = new SynInfo(new Fields(), false);
-        Stream stream = session.syn(synInfo, null);
-
-        boolean failed = false;
-        try
-        {
-            session.syn(synInfo, null);
-        }
-        catch (ExecutionException | InterruptedException | TimeoutException e)
-        {
-            failed = true;
-        }
-
-        assertThat("Opening second stream failed", failed, is(true));
-
-        stream.data(new ByteBufferDataInfo(BufferUtil.EMPTY_BUFFER, true));
-        assertThat("Data has been received on first stream.", dataReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/PingTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/PingTest.java
deleted file mode 100644
index 94ab925..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/PingTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jetty.spdy.api.PingInfo;
-import org.eclipse.jetty.spdy.api.PingResultInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.util.Promise;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class PingTest extends AbstractTest
-{
-    @Test
-    public void testPingPong() throws Exception
-    {
-        final AtomicReference<PingResultInfo> ref = new AtomicReference<>();
-        final CountDownLatch latch = new CountDownLatch(1);
-        SessionFrameListener clientSessionFrameListener = new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onPing(Session session, PingResultInfo pingInfo)
-            {
-                ref.set(pingInfo);
-                latch.countDown();
-            }
-        };
-        Session session = startClient(startServer(null), clientSessionFrameListener);
-        PingResultInfo pingResultInfo = session.ping(new PingInfo(5, TimeUnit.SECONDS));
-        Assert.assertEquals(1, pingResultInfo.getPingId() % 2);
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-        PingResultInfo pongInfo = ref.get();
-        Assert.assertNotNull(pongInfo);
-        Assert.assertEquals(pingResultInfo.getPingId(), pongInfo.getPingId());
-    }
-
-    @Test
-    public void testServerPingPong() throws Exception
-    {
-        final CountDownLatch pingReceived = new CountDownLatch(1);
-        ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
-        {
-            private final CountDownLatch pingSent = new CountDownLatch(1);
-            private int pingId;
-
-            @Override
-            public void onConnect(Session session)
-            {
-                session.ping(new PingInfo(), new Promise.Adapter<PingResultInfo>()
-                {
-                    @Override
-                    public void succeeded(PingResultInfo pingInfo)
-                    {
-                        pingId = pingInfo.getPingId();
-                        pingSent.countDown();
-                    }
-                });
-            }
-
-            @Override
-            public void onPing(Session session, PingResultInfo pingInfo)
-            {
-                try
-                {
-                    // This callback may be notified before the promise above,
-                    // so make sure we wait here to know the pingId
-                    Assert.assertTrue(pingSent.await(5, TimeUnit.SECONDS));
-                    Assert.assertEquals(0, pingInfo.getPingId() % 2);
-                    Assert.assertEquals(pingId, pingInfo.getPingId());
-                    pingReceived.countDown();
-                }
-                catch (InterruptedException x)
-                {
-                    Assert.fail();
-                }
-            }
-        };
-        startClient(startServer(serverSessionFrameListener), null);
-
-        Assert.assertTrue(pingReceived.await(5, TimeUnit.SECONDS));
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/ProtocolViolationsTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/ProtocolViolationsTest.java
deleted file mode 100644
index d3cb3dd..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/ProtocolViolationsTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.ServerSocketChannel;
-import java.nio.channels.SocketChannel;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.BytesDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.HeadersInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.frames.ControlFrameType;
-import org.eclipse.jetty.spdy.frames.SynReplyFrame;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class ProtocolViolationsTest extends AbstractTest
-{
-    @Test
-    public void testSendDataBeforeReplyIsIllegal() throws Exception
-    {
-        final CountDownLatch resetLatch = new CountDownLatch(1);
-        final CountDownLatch latch = new CountDownLatch(1);
-        Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                try
-                {
-                    stream.data(new StringDataInfo("failure", true), new Callback.Adapter());
-                    return null;
-                }
-                catch (IllegalStateException x)
-                {
-                    latch.countDown();
-                    return null;
-                }
-            }
-        }), new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onRst(Session session, RstInfo rstInfo)
-            {
-                Assert.assertSame(StreamStatus.PROTOCOL_ERROR, rstInfo.getStreamStatus());
-                resetLatch.countDown();
-            }
-        });
-        session.syn(new SynInfo(new Fields(), true), null);
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testReceiveDataBeforeReplyIsIllegal() throws Exception
-    {
-        ServerSocketChannel server = ServerSocketChannel.open();
-        server.bind(new InetSocketAddress("localhost", 0));
-
-        Session session = startClient(new InetSocketAddress("localhost", server.socket().getLocalPort()), null);
-        session.syn(new SynInfo(new Fields(), true), null);
-
-        SocketChannel channel = server.accept();
-        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
-        channel.read(readBuffer);
-        readBuffer.flip();
-        int streamId = readBuffer.getInt(8);
-
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
-        byte[] bytes = new byte[1];
-        ByteBuffer writeBuffer = generator.data(streamId, bytes.length, new BytesDataInfo(bytes, true));
-        channel.write(writeBuffer);
-        assertThat("data is fully written", writeBuffer.hasRemaining(),is(false));
-
-        readBuffer.clear();
-        channel.read(readBuffer);
-        readBuffer.flip();
-        Assert.assertEquals(ControlFrameType.RST_STREAM.getCode(), readBuffer.getShort(2));
-        Assert.assertEquals(streamId, readBuffer.getInt(8));
-
-        session.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-
-        server.close();
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testSendDataAfterCloseIsIllegal() throws Exception
-    {
-        Session session = startClient(startServer(null), null);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), true, (byte)0), null);
-        stream.data(new StringDataInfo("test", true));
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testSendHeadersAfterCloseIsIllegal() throws Exception
-    {
-        Session session = startClient(startServer(null), null);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), true, (byte)0), null);
-        stream.headers(new HeadersInfo(new Fields(), true));
-    }
-
-    @Test //TODO: throws an ISException in StandardStream.updateCloseState(). But instead we should send a rst or something to the server probably?!
-    public void testServerClosesStreamTwice() throws Exception
-    {
-        ServerSocketChannel server = ServerSocketChannel.open();
-        server.bind(new InetSocketAddress("localhost", 0));
-
-        Session session = startClient(new InetSocketAddress("localhost", server.socket().getLocalPort()), null);
-        final CountDownLatch dataLatch = new CountDownLatch(2);
-        session.syn(new SynInfo(new Fields(), false), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                dataLatch.countDown();
-            }
-        });
-
-        SocketChannel channel = server.accept();
-        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
-        channel.read(readBuffer);
-        readBuffer.flip();
-        int streamId = readBuffer.getInt(8);
-
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
-
-        ByteBuffer writeBuffer = generator.control(new SynReplyFrame(SPDY.V2, (byte)0, streamId, new Fields()));
-        channel.write(writeBuffer);
-        assertThat("SynReply is fully written", writeBuffer.hasRemaining(), is(false));
-
-        byte[] bytes = new byte[1];
-        writeBuffer = generator.data(streamId, bytes.length, new BytesDataInfo(bytes, true));
-        channel.write(writeBuffer);
-        assertThat("data is fully written", writeBuffer.hasRemaining(), is(false));
-
-        // Write again to simulate the faulty condition
-        writeBuffer.flip();
-        channel.write(writeBuffer);
-        assertThat("data is fully written", writeBuffer.hasRemaining(), is(false));
-
-        Assert.assertFalse(dataLatch.await(1, TimeUnit.SECONDS));
-
-        session.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-
-        server.close();
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/PushStreamTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/PushStreamTest.java
deleted file mode 100644
index b03419c..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/PushStreamTest.java
+++ /dev/null
@@ -1,591 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server;
-
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.hamcrest.Matchers.sameInstance;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.SocketChannel;
-import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.CyclicBarrier;
-import java.util.concurrent.Exchanger;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.BytesDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
-import org.eclipse.jetty.spdy.api.PushInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.SessionStatus;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.DataFrame;
-import org.eclipse.jetty.spdy.frames.GoAwayFrame;
-import org.eclipse.jetty.spdy.frames.RstStreamFrame;
-import org.eclipse.jetty.spdy.frames.SynStreamFrame;
-import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.eclipse.jetty.spdy.parser.Parser.Listener;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class PushStreamTest extends AbstractTest
-{
-    private static final Logger LOG = Log.getLogger(PushStreamTest.class);
-
-    @Test
-    public void testSynPushStream() throws Exception
-    {
-        final AtomicReference<Stream> pushStreamRef = new AtomicReference<>();
-        final CountDownLatch pushStreamLatch = new CountDownLatch(1);
-
-        Session clientSession = startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                stream.push(new PushInfo(new Fields(), true), new Promise.Adapter<Stream>());
-                return null;
-            }
-        }), null);
-
-        Stream stream = clientSession.syn(new SynInfo(new Fields(), true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                assertThat("streamId is even", stream.getId() % 2, is(0));
-                assertThat("stream is unidirectional", stream.isUnidirectional(), is(true));
-                assertThat("stream is closed", stream.isClosed(), is(true));
-                assertThat("stream has associated stream", stream.getAssociatedStream(), notNullValue());
-                try
-                {
-                    stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                    fail("Cannot reply to push streams");
-                }
-                catch (IllegalStateException x)
-                {
-                    // Expected
-                }
-                pushStreamRef.set(stream);
-                pushStreamLatch.countDown();
-                return null;
-            }
-        });
-        assertThat("onSyn has been called", pushStreamLatch.await(5, TimeUnit.SECONDS), is(true));
-        Stream pushStream = pushStreamRef.get();
-        assertThat("main stream and associated stream are the same", stream, sameInstance(pushStream.getAssociatedStream()));
-    }
-
-    @Test
-    public void testSendDataOnPushStreamAfterAssociatedStreamIsClosed() throws Exception
-    {
-        final Exchanger<Stream> streamExchanger = new Exchanger<>();
-        final CountDownLatch pushStreamSynLatch = new CountDownLatch(1);
-        final CyclicBarrier replyBarrier = new CyclicBarrier(3);
-        final CyclicBarrier closeBarrier = new CyclicBarrier(3);
-        final CountDownLatch streamDataSent = new CountDownLatch(2);
-        final CountDownLatch pushStreamDataReceived = new CountDownLatch(2);
-        final CountDownLatch exceptionCountDownLatch = new CountDownLatch(1);
-
-        Session clientSession = startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                try
-                {
-                    replyBarrier.await(5, TimeUnit.SECONDS);
-                    return new StreamFrameListener.Adapter()
-                    {
-                        @Override
-                        public void onData(Stream stream, DataInfo dataInfo)
-                        {
-                            try
-                            {
-                                if (dataInfo.isClose())
-                                {
-                                    stream.data(new StringDataInfo("close stream", true));
-                                    closeBarrier.await(5, TimeUnit.SECONDS);
-                                }
-                                streamDataSent.countDown();
-                                if (pushStreamDataReceived.getCount() == 2)
-                                {
-                                    Stream pushStream = stream.push(new PushInfo(new Fields(), false));
-                                    streamExchanger.exchange(pushStream, 5, TimeUnit.SECONDS);
-                                }
-                            }
-                            catch (Exception e)
-                            {
-                                exceptionCountDownLatch.countDown();
-                            }
-                        }
-                    };
-                }
-                catch (Exception e)
-                {
-                    exceptionCountDownLatch.countDown();
-                    throw new IllegalStateException(e);
-                }
-            }
-
-        }), null);
-
-        Stream stream = clientSession.syn(new SynInfo(new Fields(), false), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                pushStreamSynLatch.countDown();
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        pushStreamDataReceived.countDown();
-                        super.onData(stream, dataInfo);
-                    }
-                };
-            }
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                try
-                {
-                    replyBarrier.await(5, TimeUnit.SECONDS);
-                }
-                catch (Exception e)
-                {
-                    exceptionCountDownLatch.countDown();
-                }
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                try
-                {
-                    closeBarrier.await(5, TimeUnit.SECONDS);
-                }
-                catch (Exception e)
-                {
-                    exceptionCountDownLatch.countDown();
-                }
-            }
-        });
-
-        replyBarrier.await(5, TimeUnit.SECONDS);
-        stream.data(new StringDataInfo("client data", false));
-        Stream pushStream = streamExchanger.exchange(null, 5, TimeUnit.SECONDS);
-        pushStream.data(new StringDataInfo("first push data frame", false));
-        // nasty, but less complex than using another cyclicBarrier for example
-        while (pushStreamDataReceived.getCount() != 1)
-            Thread.sleep(1);
-        stream.data(new StringDataInfo("client close", true));
-        closeBarrier.await(5, TimeUnit.SECONDS);
-        assertThat("stream is closed", stream.isClosed(), is(true));
-        pushStream.data(new StringDataInfo("second push data frame while associated stream has been closed already", false));
-        assertThat("2 pushStream data frames have been received.", pushStreamDataReceived.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("2 data frames have been sent", streamDataSent.await(5, TimeUnit.SECONDS), is(true));
-        assertThatNoExceptionOccurred(exceptionCountDownLatch);
-    }
-
-    @Test
-    public void testSynPushStreamOnClosedStream() throws Exception
-    {
-        final CountDownLatch pushStreamFailedLatch = new CountDownLatch(1);
-
-        Session clientSession = startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(true), new Callback.Adapter());
-                stream.push(new PushInfo(1, TimeUnit.SECONDS, new Fields(), false),
-                        new Promise.Adapter<Stream>()
-                        {
-                            @Override
-                            public void failed(Throwable x)
-                            {
-                                pushStreamFailedLatch.countDown();
-                            }
-                        });
-                return super.onSyn(stream, synInfo);
-            }
-        }), new SessionFrameListener.Adapter());
-
-        clientSession.syn(new SynInfo(new Fields(), true), null);
-        assertThat("pushStream push has failed", pushStreamFailedLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    @Test
-    public void testSendBigDataOnPushStreamWhenAssociatedStreamIsClosed() throws Exception
-    {
-        final CountDownLatch streamClosedLatch = new CountDownLatch(1);
-        final CountDownLatch allDataReceived = new CountDownLatch(1);
-        final CountDownLatch exceptionCountDownLatch = new CountDownLatch(1);
-        final Exchanger<ByteBuffer> exchanger = new Exchanger<>();
-        final int dataSizeInBytes = 1024 * 1024 * 1;
-        final byte[] transferBytes = createHugeByteArray(dataSizeInBytes);
-
-        Session clientSession = startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                try
-                {
-                    Stream pushStream = stream.push(new PushInfo(new Fields(), false));
-                    stream.reply(new ReplyInfo(true));
-                    // wait until stream is closed
-                    streamClosedLatch.await(5, TimeUnit.SECONDS);
-                    pushStream.data(new BytesDataInfo(transferBytes, true), new Callback.Adapter());
-                    return null;
-                }
-                catch (Exception e)
-                {
-                    exceptionCountDownLatch.countDown();
-                    throw new IllegalStateException(e);
-                }
-            }
-        }), null);
-
-        Stream stream = clientSession.syn(new SynInfo(new Fields(), true), new StreamFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-            {
-                return new StreamFrameListener.Adapter()
-                {
-                    ByteBuffer receivedBytes = ByteBuffer.allocate(dataSizeInBytes);
-
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataInfo.consumeInto(receivedBytes);
-                        if (dataInfo.isClose())
-                        {
-                            allDataReceived.countDown();
-                            try
-                            {
-                                receivedBytes.flip();
-                                exchanger.exchange(receivedBytes.slice(), 5, TimeUnit.SECONDS);
-                            }
-                            catch (Exception e)
-                            {
-                                exceptionCountDownLatch.countDown();
-                            }
-                        }
-                    }
-                };
-            }
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                streamClosedLatch.countDown();
-                super.onReply(stream, replyInfo);
-            }
-        });
-
-        ByteBuffer receivedBytes = exchanger.exchange(null, 5, TimeUnit.SECONDS);
-
-        assertThat("received byte array is the same as transferred byte array", Arrays.equals(transferBytes, receivedBytes.array()), is(true));
-        assertThat("onReply has been called to close the stream", streamClosedLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("stream is closed", stream.isClosed(), is(true));
-        assertThat("all data has been received", allDataReceived.await(20, TimeUnit.SECONDS), is(true));
-        assertThatNoExceptionOccurred(exceptionCountDownLatch);
-    }
-
-    private byte[] createHugeByteArray(int sizeInBytes)
-    {
-        byte[] bytes = new byte[sizeInBytes];
-        ThreadLocalRandom.current().nextBytes(bytes);
-        return bytes;
-    }
-
-
-    @Test
-    public void testClientResetsStreamAfterPushSynDoesPreventSendingDataFramesWithFlowControl() throws Exception
-    {
-        final boolean flowControl = true;
-        testNoMoreFramesAreSentOnPushStreamAfterClientResetsThePushStream(flowControl);
-    }
-
-    @Test
-    public void testClientResetsStreamAfterPushSynDoesPreventSendingDataFramesWithoutFlowControl() throws Exception
-    {
-        final boolean flowControl = false;
-        testNoMoreFramesAreSentOnPushStreamAfterClientResetsThePushStream(flowControl);
-    }
-
-    private volatile boolean read = true;
-
-    private void testNoMoreFramesAreSentOnPushStreamAfterClientResetsThePushStream(final boolean flowControl) throws Exception
-    {
-        final short version = SPDY.V3;
-        final AtomicBoolean unexpectedExceptionOccurred = new AtomicBoolean(false);
-        final CountDownLatch resetReceivedLatch = new CountDownLatch(1);
-        final CountDownLatch allDataFramesReceivedLatch = new CountDownLatch(1);
-        final CountDownLatch goAwayReceivedLatch = new CountDownLatch(1);
-        final int dataSizeInBytes = 1024 * 256;
-        final byte[] transferBytes = createHugeByteArray(dataSizeInBytes);
-
-        InetSocketAddress serverAddress = startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(final Stream stream, SynInfo synInfo)
-            {
-                new Thread(new Runnable()
-                {
-
-                    @Override
-                    public void run()
-                    {
-                        Stream pushStream = null;
-                        try
-                        {
-                            stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                            pushStream = stream.push(new PushInfo(new Fields(), false));
-                            resetReceivedLatch.await(5, TimeUnit.SECONDS);
-                        }
-                        catch (InterruptedException | ExecutionException | TimeoutException e)
-                        {
-                            e.printStackTrace();
-                            unexpectedExceptionOccurred.set(true);
-                        }
-                        assert pushStream != null;
-                        try
-                        {
-                            pushStream.data(new BytesDataInfo(transferBytes, true));
-                            stream.data(new StringDataInfo("close", true));
-                        }
-                        catch (InterruptedException | ExecutionException | TimeoutException e)
-                        {
-                            LOG.debug(e.getMessage());
-                        }
-                    }
-                }).start();
-                return null;
-            }
-
-            @Override
-            public void onRst(Session session, RstInfo rstInfo)
-            {
-                resetReceivedLatch.countDown();
-            }
-
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayInfo)
-            {
-                goAwayReceivedLatch.countDown();
-            }
-        }/*TODO, flowControl*/);
-
-        final SocketChannel channel = SocketChannel.open(serverAddress);
-        final Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
-        int streamId = 1;
-        ByteBuffer writeBuffer = generator.control(new SynStreamFrame(version, (byte)0, streamId, 0, (byte)0, (short)0, new Fields()));
-        channel.write(writeBuffer);
-        assertThat("writeBuffer is fully written", writeBuffer.hasRemaining(), is(false));
-
-        final Parser parser = new Parser(new StandardCompressionFactory.StandardDecompressor());
-        parser.addListener(new Listener.Adapter()
-        {
-            int bytesRead = 0;
-
-            @Override
-            public void onControlFrame(ControlFrame frame)
-            {
-                if (frame instanceof SynStreamFrame)
-                {
-                    int pushStreamId = ((SynStreamFrame)frame).getStreamId();
-                    ByteBuffer writeBuffer = generator.control(new RstStreamFrame(version, pushStreamId, StreamStatus.CANCEL_STREAM.getCode(version)));
-                    try
-                    {
-                        channel.write(writeBuffer);
-                    }
-                    catch (IOException e)
-                    {
-                        e.printStackTrace();
-                        unexpectedExceptionOccurred.set(true);
-                    }
-                }
-            }
-
-            @Override
-            public void onDataFrame(DataFrame frame, ByteBuffer data)
-            {
-                if (frame.getStreamId() == 2)
-                    bytesRead = bytesRead + frame.getLength();
-                if (bytesRead == dataSizeInBytes)
-                {
-                    allDataFramesReceivedLatch.countDown();
-                    return;
-                }
-                if (flowControl)
-                {
-                    ByteBuffer writeBuffer = generator.control(new WindowUpdateFrame(version, frame.getStreamId(), frame.getLength()));
-                    try
-                    {
-                        channel.write(writeBuffer);
-                    }
-                    catch (IOException e)
-                    {
-                        e.printStackTrace();
-                        unexpectedExceptionOccurred.set(true);
-                    }
-                }
-            }
-        });
-
-        Thread reader = new Thread(new Runnable()
-        {
-            @Override
-            public void run()
-            {
-                ByteBuffer readBuffer = ByteBuffer.allocate(dataSizeInBytes * 2);
-                while (read)
-                {
-                    try
-                    {
-                        channel.read(readBuffer);
-                    }
-                    catch (IOException e)
-                    {
-                        e.printStackTrace();
-                        unexpectedExceptionOccurred.set(true);
-                    }
-                    readBuffer.flip();
-                    parser.parse(readBuffer);
-                    readBuffer.clear();
-                }
-
-            }
-        });
-        reader.start();
-        read = false;
-
-        assertThat("no unexpected exceptions occurred", unexpectedExceptionOccurred.get(), is(false));
-        assertThat("not all dataframes have been received as the pushstream has been reset by the client.", allDataFramesReceivedLatch.await(streamId, TimeUnit.SECONDS), is(false));
-
-
-        ByteBuffer buffer = generator.control(new GoAwayFrame(version, streamId, SessionStatus.OK.getCode()));
-        channel.write(buffer);
-        Assert.assertThat(buffer.hasRemaining(), is(false));
-
-        assertThat("GoAway frame is received by server", goAwayReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
-        channel.shutdownOutput();
-        channel.close();
-    }
-
-    @Test
-    public void testOddEvenStreamIds() throws Exception
-    {
-        final CountDownLatch pushStreamIdIsEvenLatch = new CountDownLatch(3);
-
-        Session clientSession = startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.push(new PushInfo(new Fields(), false), new Promise.Adapter<Stream>());
-                return null;
-            }
-        }), null);
-
-        Stream stream = clientSession.syn(new SynInfo(new Fields(), false),
-                new VerifyPushStreamIdIsEvenStreamFrameListener(pushStreamIdIsEvenLatch));
-        Stream stream2 = clientSession.syn(new SynInfo(new Fields(), false),
-                new VerifyPushStreamIdIsEvenStreamFrameListener(pushStreamIdIsEvenLatch));
-        Stream stream3 = clientSession.syn(new SynInfo(new Fields(), false),
-                new VerifyPushStreamIdIsEvenStreamFrameListener(pushStreamIdIsEvenLatch));
-        assertStreamIdIsOdd(stream);
-        assertStreamIdIsOdd(stream2);
-        assertStreamIdIsOdd(stream3);
-
-        assertThat("all pushStreams had even ids", pushStreamIdIsEvenLatch.await(5, TimeUnit.SECONDS), is(true));
-    }
-
-    private class VerifyPushStreamIdIsEvenStreamFrameListener extends StreamFrameListener.Adapter
-    {
-        final CountDownLatch pushStreamIdIsEvenLatch;
-
-        private VerifyPushStreamIdIsEvenStreamFrameListener(CountDownLatch pushStreamIdIsEvenLatch)
-        {
-            this.pushStreamIdIsEvenLatch = pushStreamIdIsEvenLatch;
-        }
-
-        @Override
-        public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
-        {
-            assertStreamIdIsEven(stream);
-            pushStreamIdIsEvenLatch.countDown();
-            return super.onPush(stream, pushInfo);
-        }
-    }
-
-    private void assertStreamIdIsEven(Stream stream)
-    {
-        assertThat("streamId is odd", stream.getId() % 2, is(0));
-    }
-
-    private void assertStreamIdIsOdd(Stream stream)
-    {
-        assertThat("streamId is odd", stream.getId() % 2, is(1));
-    }
-
-    private void assertThatNoExceptionOccurred(final CountDownLatch exceptionCountDownLatch) throws InterruptedException
-    {
-        assertThat("No exception occurred", exceptionCountDownLatch.await(1, TimeUnit.SECONDS), is(false));
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/ResetStreamTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/ResetStreamTest.java
deleted file mode 100644
index 0ddd3ea..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/ResetStreamTest.java
+++ /dev/null
@@ -1,204 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.RstInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.FutureCallback;
-import org.junit.Test;
-
-public class ResetStreamTest extends AbstractTest
-{
-    @Test
-    public void testResetStreamIsRemoved() throws Exception
-    {
-        Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()/*TODO, true*/), null);
-
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), false, (byte)0), null);
-        session.rst(new RstInfo(5, TimeUnit.SECONDS, stream.getId(), StreamStatus.CANCEL_STREAM));
-
-        assertEquals("session expected to contain 0 streams", 0, session.getStreams().size());
-    }
-
-    @Test
-    public void testRefusedStreamIsRemoved() throws Exception
-    {
-        final AtomicReference<Session> serverSessionRef = new AtomicReference<>();
-        final CountDownLatch synLatch = new CountDownLatch(1);
-        final CountDownLatch rstLatch = new CountDownLatch(1);
-        Session clientSession = startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Session serverSession = stream.getSession();
-                serverSessionRef.set(serverSession);
-                serverSession.rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM), new FutureCallback());
-                synLatch.countDown();
-                return null;
-            }
-        }), new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onRst(Session session, RstInfo rstInfo)
-            {
-                rstLatch.countDown();
-            }
-        });
-
-        Stream stream = clientSession.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), false, (byte)0), null);
-
-        assertTrue("syncLatch didn't count down", synLatch.await(5, TimeUnit.SECONDS));
-        Session serverSession = serverSessionRef.get();
-        assertEquals("serverSession expected to contain 0 streams", 0, serverSession.getStreams().size());
-
-        assertTrue("rstLatch didn't count down", rstLatch.await(5, TimeUnit.SECONDS));
-        // Need to sleep a while to give the chance to the implementation to remove the stream
-        TimeUnit.SECONDS.sleep(1);
-        assertTrue("stream is expected to be reset", stream.isReset());
-        assertEquals("clientSession expected to contain 0 streams", 0, clientSession.getStreams().size());
-    }
-
-    @Test
-    public void testRefusedStreamIgnoresData() throws Exception
-    {
-        final CountDownLatch synLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        final CountDownLatch rstLatch = new CountDownLatch(1);
-        Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                try
-                {
-                    // Refuse the stream, we must ignore data frames
-                    assertTrue(synLatch.await(5, TimeUnit.SECONDS));
-                    stream.getSession().rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM), new FutureCallback());
-                    return new StreamFrameListener.Adapter()
-                    {
-                        @Override
-                        public void onData(Stream stream, DataInfo dataInfo)
-                        {
-                            dataLatch.countDown();
-                        }
-                    };
-                }
-                catch (InterruptedException x)
-                {
-                    x.printStackTrace();
-                    return null;
-                }
-            }
-        }), new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onRst(Session session, RstInfo rstInfo)
-            {
-                rstLatch.countDown();
-            }
-        });
-
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), false, (byte)0), null);
-        stream.data(new StringDataInfo(5, TimeUnit.SECONDS, "data", true), new Callback.Adapter()
-        {
-            @Override
-            public void succeeded()
-            {
-                synLatch.countDown();
-            }
-        });
-
-        assertTrue("rstLatch didn't count down", rstLatch.await(5, TimeUnit.SECONDS));
-        assertTrue("stream is expected to be reset", stream.isReset());
-        assertFalse("dataLatch shouldn't be count down", dataLatch.await(1, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testResetAfterServerReceivedFirstDataFrameAndSecondDataFrameFails() throws Exception
-    {
-        final CountDownLatch synLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        final CountDownLatch rstLatch = new CountDownLatch(1);
-        final CountDownLatch failLatch = new CountDownLatch(1);
-        Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                synLatch.countDown();
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        dataLatch.countDown();
-                        stream.getSession().rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM), new FutureCallback());
-                    }
-                };
-            }
-        }), new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onRst(Session session, RstInfo rstInfo)
-            {
-                rstLatch.countDown();
-            }
-        });
-
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), false, (byte)0), null);
-        assertThat("push is received by server", synLatch.await(5, TimeUnit.SECONDS), is(true));
-        stream.data(new StringDataInfo(5, TimeUnit.SECONDS, "data", false), new Callback.Adapter());
-        assertThat("stream is reset", rstLatch.await(5, TimeUnit.SECONDS), is(true));
-        stream.data(new StringDataInfo(5, TimeUnit.SECONDS, "2nd dataframe", false), new Callback.Adapter()
-        {
-            @Override
-            public void failed(Throwable x)
-            {
-                failLatch.countDown();
-            }
-        });
-
-        assertThat("2nd data call failed", failLatch.await(5, TimeUnit.SECONDS), is(true));
-        assertThat("stream is reset", stream.isReset(), is(true));
-    }
-
-    // TODO: If server already received 2nd dataframe after it rst, it should ignore it. Not easy to do.
-
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SPDYClientFactoryTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SPDYClientFactoryTest.java
deleted file mode 100644
index b18744f..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SPDYClientFactoryTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class SPDYClientFactoryTest extends AbstractTest
-{
-    @Test
-    public void testStoppingClientFactorySendsGoAway() throws Exception
-    {
-        final CountDownLatch latch = new CountDownLatch(1);
-        startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayResultInfo)
-            {
-                latch.countDown();
-            }
-        }), null);
-
-        // Sleep a while to avoid the factory is
-        // stopped before a session can be opened
-        TimeUnit.SECONDS.sleep(1);
-
-        clientFactory.stop();
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(clientFactory.getSessions().isEmpty());
-    }
-
-    @Test
-    public void testSessionClosedIsRemovedFromClientFactory() throws Exception
-    {
-        Session session = startClient(startServer(null), null);
-
-        session.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-
-        for (int i=0;i<10;i++)
-        {
-            // Sleep a while to allow the factory to remove the session
-            // since it is done asynchronously by the selector thread
-            TimeUnit.SECONDS.sleep(1);
-            if (clientFactory.getSessions().isEmpty())
-                return;
-        }
-
-        Assert.fail(clientFactory.getSessions().toString());
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SPDYServerConnectorTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SPDYServerConnectorTest.java
deleted file mode 100644
index 262a87c..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SPDYServerConnectorTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.spdy.api.GoAwayInfo;
-import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class SPDYServerConnectorTest extends AbstractTest
-{
-    @Test
-    public void testStoppingServerConnectorSendsGoAway() throws Exception
-    {
-        final CountDownLatch latch = new CountDownLatch(1);
-        startClient(startServer(null), new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onGoAway(Session session, GoAwayResultInfo goAwayResultInfo)
-            {
-                latch.countDown();
-            }
-        });
-
-        // Sleep a while to avoid the connector is
-        // stopped before a session can be opened
-        TimeUnit.SECONDS.sleep(1);
-
-        connector.stop();
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(connector.getConnectionFactory(SPDYServerConnectionFactory.class).getSessions().isEmpty());
-    }
-
-    @Test
-    public void testSessionClosedIsRemovedFromServerConnector() throws Exception
-    {
-        Session session = startClient(startServer(null), null);
-
-        session.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
-
-        // Sleep a while to allow the connector to remove the session
-        // since it is done asynchronously by the selector thread
-        TimeUnit.SECONDS.sleep(1);
-
-        Assert.assertTrue(connector.getConnectionFactory(SPDYServerConnectionFactory.class).getSessions().isEmpty());
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SettingsTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SettingsTest.java
deleted file mode 100644
index 5cee5b9..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SettingsTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server;
-
-import java.net.InetSocketAddress;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Settings;
-import org.eclipse.jetty.spdy.api.SettingsInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.util.FutureCallback;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class SettingsTest extends AbstractTest
-{
-    @Test
-    public void testSettingsUsage() throws Exception
-    {
-        Settings settings = new Settings();
-        int streamsValue = 100;
-        settings.put(new Settings.Setting(Settings.ID.MAX_CONCURRENT_STREAMS, Settings.Flag.PERSIST, streamsValue));
-        int windowValue = 32768;
-        settings.put(new Settings.Setting(Settings.ID.INITIAL_WINDOW_SIZE, windowValue));
-        int newCode = 91;
-        Settings.ID newID = Settings.ID.from(newCode);
-        int newValue = 97;
-        settings.put(new Settings.Setting(newID, newValue));
-
-        Settings.Setting setting1 = settings.get(Settings.ID.MAX_CONCURRENT_STREAMS);
-        Assert.assertSame(Settings.ID.MAX_CONCURRENT_STREAMS, setting1.id());
-        Assert.assertSame(Settings.Flag.PERSIST, setting1.flag());
-        Assert.assertEquals(streamsValue, setting1.value());
-
-        Settings.Setting setting2 = settings.get(Settings.ID.INITIAL_WINDOW_SIZE);
-        Assert.assertSame(Settings.ID.INITIAL_WINDOW_SIZE, setting2.id());
-        Assert.assertSame(Settings.Flag.NONE, setting2.flag());
-        Assert.assertEquals(windowValue, setting2.value());
-
-        int size = settings.size();
-        Settings.Setting setting3 = settings.remove(Settings.ID.from(newCode));
-        Assert.assertEquals(size - 1, settings.size());
-        Assert.assertNotNull(setting3);
-        Assert.assertSame(newID, setting3.id());
-        Assert.assertEquals(newValue, setting3.value());
-    }
-
-    @Test
-    public void testSettings() throws Exception
-    {
-        Settings settings = new Settings();
-        settings.put(new Settings.Setting(Settings.ID.UPLOAD_BANDWIDTH, 1024 * 1024));
-        settings.put(new Settings.Setting(Settings.ID.DOWNLOAD_BANDWIDTH, 1024 * 1024));
-        settings.put(new Settings.Setting(Settings.ID.CURRENT_CONGESTION_WINDOW, Settings.Flag.PERSISTED, 1024));
-        final SettingsInfo clientSettingsInfo = new SettingsInfo(settings);
-        final CountDownLatch latch = new CountDownLatch(1);
-        ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public void onSettings(Session session, SettingsInfo serverSettingsInfo)
-            {
-                Assert.assertEquals(clientSettingsInfo.getFlags(), serverSettingsInfo.getFlags());
-                Assert.assertEquals(clientSettingsInfo.getSettings(), serverSettingsInfo.getSettings());
-                latch.countDown();
-            }
-        };
-        Session session = startClient(startServer(serverSessionFrameListener), null);
-
-        session.settings(clientSettingsInfo);
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testServerSettings() throws Exception
-    {
-        Settings settings = new Settings();
-        settings.put(new Settings.Setting(Settings.ID.UPLOAD_BANDWIDTH, 1024 * 1024));
-        settings.put(new Settings.Setting(Settings.ID.DOWNLOAD_BANDWIDTH, 1024 * 1024));
-        settings.put(new Settings.Setting(Settings.ID.CURRENT_CONGESTION_WINDOW, Settings.Flag.PERSIST, 1024));
-        final SettingsInfo serverSettingsInfo = new SettingsInfo(settings);
-        ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public void onConnect(Session session)
-            {
-                session.settings(serverSettingsInfo, new FutureCallback());
-            }
-        };
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        SessionFrameListener clientSessionFrameListener = new SessionFrameListener.Adapter()
-        {
-            @Override
-            public void onSettings(Session session, SettingsInfo clientSettingsInfo)
-            {
-                Assert.assertEquals(serverSettingsInfo.getFlags(), clientSettingsInfo.getFlags());
-                Assert.assertEquals(serverSettingsInfo.getSettings(), clientSettingsInfo.getSettings());
-                latch.countDown();
-            }
-        };
-
-        startClient(startServer(serverSessionFrameListener), clientSessionFrameListener);
-
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testSettingIDIsTheSameInBothV2AndV3() throws Exception
-    {
-        final AtomicReference<SettingsInfo> v2 = new AtomicReference<>();
-        final AtomicReference<SettingsInfo> v3 = new AtomicReference<>();
-        final CountDownLatch settingsLatch = new CountDownLatch(2);
-        InetSocketAddress address = startServer(new ServerSessionFrameListener.Adapter()
-        {
-            private final AtomicInteger count = new AtomicInteger();
-
-            @Override
-            public void onSettings(Session session, SettingsInfo settingsInfo)
-            {
-                int count = this.count.incrementAndGet();
-                if (count == 1)
-                    v2.set(settingsInfo);
-                else if (count == 2)
-                    v3.set(settingsInfo);
-                else
-                    Assert.fail();
-                settingsLatch.countDown();
-            }
-        });
-
-        Settings settings = new Settings();
-        settings.put(new Settings.Setting(Settings.ID.INITIAL_WINDOW_SIZE, Settings.Flag.PERSIST, 0xC0_00));
-        SettingsInfo settingsInfo = new SettingsInfo(settings);
-
-        Session sessionV2 = startClient(address, null);
-        sessionV2.settings(settingsInfo);
-
-        Session sessionV3 = clientFactory.newSPDYClient(SPDY.V3).connect(address, null);
-        sessionV3.settings(settingsInfo);
-
-        Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertEquals(v2.get().getSettings(), v3.get().getSettings());
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SynDataReplyDataLoadTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SynDataReplyDataLoadTest.java
deleted file mode 100644
index 020e9b5..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SynDataReplyDataLoadTest.java
+++ /dev/null
@@ -1,288 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-
-package org.eclipse.jetty.spdy.server;
-
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.eclipse.jetty.io.LeakTrackingByteBufferPool;
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.eclipse.jetty.util.thread.Scheduler;
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-public class SynDataReplyDataLoadTest extends AbstractTest
-{
-    private static final int TIMEOUT = 60 * 1000;
-    private static final Logger logger = Log.getLogger(SynDataReplyDataLoadTest.class);
-
-    @Test(timeout = TIMEOUT)
-    @Ignore("Test needs to be rewritten")
-    public void testSynDataReplyDataLoad() throws Exception
-    {
-        LeakTrackingByteBufferPool serverBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged());
-        LeakTrackingByteBufferPool clientBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged());
-
-        ServerSessionFrameListener listener = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                stream.reply(new ReplyInfo(synInfo.getHeaders(), false), new Callback.Adapter());
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        ByteBuffer buffer = dataInfo.asByteBuffer(true);
-                        stream.data(new ByteBufferDataInfo(buffer, dataInfo.isClose()), new Callback.Adapter());
-                    }
-                };
-            }
-        };
-
-        short spdyVersion = SPDY.V2;
-        long idleTimeout = 2 * TIMEOUT;
-
-        server = newServer();
-        connector = new ServerConnector(server, null, null, serverBufferPool, 1,
-                Math.max(1, Runtime.getRuntime().availableProcessors() / 2),
-                new SPDYServerConnectionFactory(spdyVersion, listener));
-        connector.setIdleTimeout(idleTimeout);
-
-        QueuedThreadPool clientExecutor = new QueuedThreadPool();
-        clientExecutor.setName(clientExecutor.getName() + "-client");
-        clientFactory = new SPDYClient.Factory(clientExecutor, null, clientBufferPool, null, idleTimeout);
-        final Session session = startClient(spdyVersion, startServer(spdyVersion, listener), null);
-
-        final Thread testThread = Thread.currentThread();
-        Runnable timeout = new Runnable()
-        {
-            @Override
-            public void run()
-            {
-                logger.warn("Interrupting test, it is taking too long");
-                logger.warn("SERVER: {}", server.dump());
-                logger.warn("CLIENT: {}", clientFactory.dump());
-                testThread.interrupt();
-            }
-        };
-
-        final int iterations = 500;
-        final int count = 50;
-
-        final Fields headers = new Fields();
-        headers.put("method", "get");
-        headers.put("url", "/");
-        headers.put("version", "http/1.1");
-        headers.put("host", "localhost:8080");
-        headers.put("content-type", "application/octet-stream");
-
-        final CountDownLatch latch = new CountDownLatch(count * iterations);
-        session.addListener(new Session.StreamListener.Adapter()
-        {
-            @Override
-            public void onStreamClosed(Stream stream)
-            {
-                latch.countDown();
-            }
-        });
-
-        ExecutorService threadPool = Executors.newFixedThreadPool(count);
-        List<Callable<Object>> tasks = new ArrayList<>();
-
-        tasks.clear();
-        for (int i = 0; i < count; ++i)
-        {
-            tasks.add(new Callable<Object>()
-            {
-                @Override
-                public Object call() throws Exception
-                {
-                    synGetDataGet(session, headers, iterations);
-                    return null;
-                }
-            });
-        }
-        Scheduler.Task syncTimeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS);
-        {
-            long begin = System.nanoTime();
-            List<Future<Object>> futures = threadPool.invokeAll(tasks);
-            for (Future<Object> future : futures)
-                future.get(iterations, TimeUnit.SECONDS);
-            Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS));
-            long end = System.nanoTime();
-            System.err.printf("SYN+GET+DATA+GET completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
-        }
-        syncTimeoutTask.cancel();
-
-        tasks.clear();
-        for (int i = 0; i < count; ++i)
-        {
-            tasks.add(new Callable<Object>()
-            {
-                @Override
-                public Object call() throws Exception
-                {
-                    synCompletedData(session, headers, iterations);
-                    return null;
-                }
-            });
-        }
-        Scheduler.Task asyncTimeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS);
-        {
-            long begin = System.nanoTime();
-            List<Future<Object>> futures = threadPool.invokeAll(tasks);
-            for (Future<Object> future : futures)
-                future.get(iterations, TimeUnit.SECONDS);
-            Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS));
-            long end = System.nanoTime();
-            System.err.printf("SYN+COMPLETED+DATA completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
-        }
-        asyncTimeoutTask.cancel();
-
-        threadPool.shutdown();
-
-        System.gc();
-
-        assertThat("Server BufferPool - leaked acquires", serverBufferPool.getLeakedAcquires(), is(0L));
-        assertThat("Server BufferPool - leaked releases", serverBufferPool.getLeakedReleases(), is(0L));
-        assertThat("Server BufferPool - unreleased", serverBufferPool.getLeakedResources(), is(0L));
-        
-        assertThat("Client BufferPool - leaked acquires", clientBufferPool.getLeakedAcquires(), is(0L));
-        assertThat("Client BufferPool - leaked releases", clientBufferPool.getLeakedReleases(), is(0L));
-        assertThat("Client BufferPool - unreleased", clientBufferPool.getLeakedResources(), is(0L));
-    }
-
-    private void synCompletedData(Session session, Fields headers, int iterations) throws Exception
-    {
-        final Map<Integer, Integer> counter = new ConcurrentHashMap<>(iterations);
-        final CountDownLatch requestsLatch = new CountDownLatch(2 * iterations);
-        for (int i = 0; i < iterations; ++i)
-        {
-            final AtomicInteger count = new AtomicInteger(2);
-            final int index = i;
-            counter.put(index, index);
-            session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
-                    {
-                        @Override
-                        public void onReply(Stream stream, ReplyInfo replyInfo)
-                        {
-                            Assert.assertEquals(2, count.getAndDecrement());
-                            requestsLatch.countDown();
-                        }
-
-                        @Override
-                        public void onData(Stream stream, DataInfo dataInfo)
-                        {
-                            // TCP can split the data frames, so I may be receiving more than 1 data frame
-                            dataInfo.asBytes(true);
-                            if (dataInfo.isClose())
-                            {
-                                Assert.assertEquals(1, count.getAndDecrement());
-                                counter.remove(index);
-                                requestsLatch.countDown();
-                            }
-                        }
-                    }, new Promise.Adapter<Stream>()
-                    {
-                        @Override
-                        public void succeeded(Stream stream)
-                        {
-                            stream.data(new StringDataInfo("data_" + stream.getId(), true),
-                                    new Callback.Adapter());
-                        }
-                    }
-            );
-        }
-        Assert.assertTrue(requestsLatch.await(iterations, TimeUnit.SECONDS));
-        Assert.assertTrue(counter.toString(), counter.isEmpty());
-    }
-
-    private void synGetDataGet(Session session, Fields headers, int iterations) throws Exception
-    {
-        final Map<Integer, Integer> counter = new ConcurrentHashMap<>(iterations);
-        final CountDownLatch latch = new CountDownLatch(2 * iterations);
-        for (int i = 0; i < iterations; ++i)
-        {
-            final AtomicInteger count = new AtomicInteger(2);
-            final int index = i;
-            counter.put(index, index);
-            Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0),
-                    new StreamFrameListener.Adapter()
-                    {
-                        @Override
-                        public void onReply(Stream stream, ReplyInfo replyInfo)
-                        {
-                            Assert.assertEquals(2, count.getAndDecrement());
-                            latch.countDown();
-                        }
-
-                        @Override
-                        public void onData(Stream stream, DataInfo dataInfo)
-                        {
-                            // TCP can split the data frames, so I may be receiving more than 1 data frame
-                            dataInfo.asBytes(true);
-                            if (dataInfo.isClose())
-                            {
-                                Assert.assertEquals(1, count.getAndDecrement());
-                                counter.remove(index);
-                                latch.countDown();
-                            }
-                        }
-                    });
-            stream.data(new StringDataInfo(5, TimeUnit.SECONDS, "data_" + stream.getId(), true));
-        }
-        Assert.assertTrue(latch.await(iterations, TimeUnit.SECONDS));
-        Assert.assertTrue(counter.toString(), counter.isEmpty());
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SynReplyTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SynReplyTest.java
deleted file mode 100644
index ebc3b3a..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SynReplyTest.java
+++ /dev/null
@@ -1,375 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.io.ByteArrayOutputStream;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jetty.spdy.api.BytesDataInfo;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StringDataInfo;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Promise;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class SynReplyTest extends AbstractTest
-{
-    @Test
-    public void testSynReply() throws Exception
-    {
-        final AtomicReference<Session> sessionRef = new AtomicReference<>();
-        final CountDownLatch sessionLatch = new CountDownLatch(1);
-        final CountDownLatch synLatch = new CountDownLatch(1);
-        ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public void onConnect(Session session)
-            {
-                sessionRef.set(session);
-                sessionLatch.countDown();
-            }
-
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Assert.assertTrue(stream.isHalfClosed());
-                stream.reply(new ReplyInfo(new Fields(), true), new Callback.Adapter());
-                synLatch.countDown();
-                return null;
-            }
-        };
-
-        Session session = startClient(startServer(serverSessionFrameListener), null);
-
-        Assert.assertTrue(sessionLatch.await(5, TimeUnit.SECONDS));
-        Session serverSession = sessionRef.get();
-        Assert.assertNotNull(serverSession);
-
-        final CountDownLatch streamCreatedLatch = new CountDownLatch(1);
-        final CountDownLatch streamRemovedLatch = new CountDownLatch(1);
-        session.addListener(new Session.StreamListener()
-        {
-            @Override
-            public void onStreamCreated(Stream stream)
-            {
-                streamCreatedLatch.countDown();
-            }
-
-            @Override
-            public void onStreamClosed(Stream stream)
-            {
-                streamRemovedLatch.countDown();
-            }
-        });
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), true, (byte)0),
-                new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertTrue(stream.isClosed());
-                replyLatch.countDown();
-            }
-        });
-
-        Assert.assertTrue(synLatch.await(5, TimeUnit.SECONDS));
-
-        Assert.assertTrue(streamCreatedLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(stream.isClosed());
-
-        Assert.assertTrue(streamRemovedLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertEquals(0, session.getStreams().size());
-    }
-
-    @Test
-    public void testSynDataReply() throws Exception
-    {
-        final byte[] dataBytes = "foo".getBytes(StandardCharsets.UTF_8);
-
-        final CountDownLatch synLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch = new CountDownLatch(1);
-        ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Assert.assertFalse(stream.isHalfClosed());
-                Assert.assertFalse(stream.isClosed());
-                synLatch.countDown();
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-                        ByteBuffer buffer = ByteBuffer.allocate(2);
-                        while (dataInfo.available() > 0)
-                        {
-                            dataInfo.readInto(buffer);
-                            buffer.flip();
-                            bytes.write(buffer.array(), buffer.arrayOffset(), buffer.remaining());
-                            buffer.clear();
-                        }
-                        Assert.assertTrue(Arrays.equals(dataBytes, bytes.toByteArray()));
-                        Assert.assertTrue(stream.isHalfClosed());
-                        Assert.assertFalse(stream.isClosed());
-
-                        stream.reply(new ReplyInfo(true), new Callback.Adapter());
-                        Assert.assertTrue(stream.isClosed());
-                        dataLatch.countDown();
-                    }
-                };
-            }
-        };
-
-        Session session = startClient(startServer(serverSessionFrameListener), null);
-
-        final CountDownLatch streamRemovedLatch = new CountDownLatch(1);
-        session.addListener(new Session.StreamListener.Adapter()
-        {
-            @Override
-            public void onStreamClosed(Stream stream)
-            {
-                streamRemovedLatch.countDown();
-            }
-        });
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, new Fields(), false, (byte)0),
-                new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                replyLatch.countDown();
-            }
-        });
-        stream.data(new BytesDataInfo(dataBytes, true));
-
-        Assert.assertTrue(synLatch.await(5, TimeUnit.SECONDS));
-
-        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-
-        Assert.assertTrue(streamRemovedLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertEquals(0, session.getStreams().size());
-    }
-
-    @Test
-    public void testSynReplyDataData() throws Exception
-    {
-        final String data1 = "foo";
-        final String data2 = "bar";
-        Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(final Stream stream, SynInfo synInfo)
-            {
-                Assert.assertTrue(stream.isHalfClosed());
-
-                stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                stream.data(new StringDataInfo(5, TimeUnit.SECONDS, data1, false), new Callback.Adapter()
-                {
-                    @Override
-                    public void succeeded()
-                    {
-                        stream.data(new StringDataInfo(data2, true), new Adapter());
-                    }
-                });
-
-                return null;
-            }
-        }), null);
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch dataLatch1 = new CountDownLatch(1);
-        final CountDownLatch dataLatch2 = new CountDownLatch(1);
-        session.syn(new SynInfo(new Fields(), true), new StreamFrameListener.Adapter()
-        {
-            private AtomicInteger dataCount = new AtomicInteger();
-
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                int dataCount = this.dataCount.incrementAndGet();
-                if (dataCount == 1)
-                {
-                    String chunk1 = dataInfo.asString(StandardCharsets.UTF_8, true);
-                    Assert.assertEquals(data1, chunk1);
-                    dataLatch1.countDown();
-                }
-                else if (dataCount == 2)
-                {
-                    String chunk2 = dataInfo.asString(StandardCharsets.UTF_8, true);
-                    Assert.assertEquals(data2, chunk2);
-                    dataLatch2.countDown();
-                }
-            }
-        });
-
-        Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(dataLatch1.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(dataLatch2.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testServerSynDataReplyData() throws Exception
-    {
-        final String serverData = "server";
-        final String clientData = "client";
-
-        final CountDownLatch replyLatch = new CountDownLatch(1);
-        final CountDownLatch clientDataLatch = new CountDownLatch(1);
-        ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public void onConnect(Session session)
-            {
-                session.syn(new SynInfo(new Fields(), false), new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onReply(Stream stream, ReplyInfo replyInfo)
-                    {
-                        replyLatch.countDown();
-                    }
-
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        String data = dataInfo.asString(StandardCharsets.UTF_8, true);
-                        Assert.assertEquals(clientData, data);
-                        clientDataLatch.countDown();
-                    }
-                }, new Promise.Adapter<Stream>()
-                {
-                    @Override
-                    public void succeeded(Stream stream)
-                    {
-                        stream.data(new StringDataInfo(serverData, true), new Callback.Adapter());
-                    }
-                });
-            }
-        };
-
-        final CountDownLatch synLatch = new CountDownLatch(1);
-        final CountDownLatch serverDataLatch = new CountDownLatch(1);
-        SessionFrameListener clientSessionFrameListener = new SessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Assert.assertEquals(0, stream.getId() % 2);
-
-                stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                stream.data(new StringDataInfo(clientData, true), new Callback.Adapter());
-                synLatch.countDown();
-
-                return new StreamFrameListener.Adapter()
-                {
-                    @Override
-                    public void onData(Stream stream, DataInfo dataInfo)
-                    {
-                        ByteBuffer buffer = dataInfo.asByteBuffer(false);
-                        String data = StandardCharsets.UTF_8.decode(buffer).toString();
-                        Assert.assertEquals(serverData, data);
-                        serverDataLatch.countDown();
-                    }
-                };
-            }
-        };
-
-        startClient(startServer(serverSessionFrameListener), clientSessionFrameListener);
-
-        Assert.assertTrue(synLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(serverDataLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(clientDataLatch.await(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testSynReplyDataSynReplyData() throws Exception
-    {
-        final String data = "foo";
-        ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                Assert.assertTrue(stream.isHalfClosed());
-
-                stream.reply(new ReplyInfo(false), new Callback.Adapter());
-                stream.data(new StringDataInfo(data, true), new Callback.Adapter());
-
-                return null;
-            }
-        };
-
-        Session session = startClient(startServer(serverSessionFrameListener), null);
-
-        final CountDownLatch replyLatch = new CountDownLatch(2);
-        final CountDownLatch dataLatch = new CountDownLatch(2);
-        StreamFrameListener clientStreamFrameListener = new StreamFrameListener.Adapter()
-        {
-            @Override
-            public void onReply(Stream stream, ReplyInfo replyInfo)
-            {
-                Assert.assertFalse(replyInfo.isClose());
-                replyLatch.countDown();
-            }
-
-            @Override
-            public void onData(Stream stream, DataInfo dataInfo)
-            {
-                String chunk = dataInfo.asString(StandardCharsets.UTF_8, true);
-                Assert.assertEquals(data, chunk);
-                dataLatch.countDown();
-            }
-        };
-        session.syn(new SynInfo(new Fields(), true), clientStreamFrameListener);
-        session.syn(new SynInfo(new Fields(), true), clientStreamFrameListener);
-
-        Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
-        Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/UnsupportedVersionTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/UnsupportedVersionTest.java
deleted file mode 100644
index 9e3dbc3..0000000
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/UnsupportedVersionTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.spdy.server;
-
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.SocketChannel;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.spdy.StandardCompressionFactory;
-import org.eclipse.jetty.spdy.api.SPDY;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.StreamStatus;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
-import org.eclipse.jetty.spdy.frames.ControlFrame;
-import org.eclipse.jetty.spdy.frames.ControlFrameType;
-import org.eclipse.jetty.spdy.frames.RstStreamFrame;
-import org.eclipse.jetty.spdy.frames.SynStreamFrame;
-import org.eclipse.jetty.spdy.generator.Generator;
-import org.eclipse.jetty.spdy.parser.Parser;
-import org.eclipse.jetty.util.Fields;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class UnsupportedVersionTest extends AbstractTest
-{
-    @Test
-    public void testSynWithUnsupportedVersion() throws Exception
-    {
-        final CountDownLatch synLatch = new CountDownLatch(1);
-        InetSocketAddress address = startServer(new ServerSessionFrameListener.Adapter()
-        {
-            @Override
-            public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
-            {
-                synLatch.countDown();
-                return null;
-            }
-
-            @Override
-            public void onFailure(Session session, Throwable x)
-            {
-                // Suppress exception logging for this test
-            }
-        });
-
-        SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, new Fields());
-        Generator generator = new Generator(new MappedByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
-        ByteBuffer buffer = generator.control(frame);
-        // Replace the version byte with an unsupported version
-        buffer.putShort(0, (short)0x8001);
-
-        SocketChannel channel = SocketChannel.open(address);
-        channel.write(buffer);
-        Assert.assertFalse(buffer.hasRemaining());
-
-        Assert.assertFalse(synLatch.await(1, TimeUnit.SECONDS));
-
-        buffer = ByteBuffer.allocate(1024);
-        channel.read(buffer);
-        buffer.flip();
-
-        final CountDownLatch rstLatch = new CountDownLatch(1);
-        Parser parser = new Parser(new StandardCompressionFactory.StandardDecompressor());
-        parser.addListener(new Parser.Listener.Adapter()
-        {
-            @Override
-            public void onControlFrame(ControlFrame frame)
-            {
-                Assert.assertSame(ControlFrameType.RST_STREAM, frame.getType());
-                Assert.assertEquals(StreamStatus.UNSUPPORTED_VERSION.getCode(frame.getVersion()), ((RstStreamFrame)frame).getStatusCode());
-                rstLatch.countDown();
-            }
-        });
-        parser.parse(buffer);
-
-        Assert.assertTrue(rstLatch.await(5, TimeUnit.SECONDS));
-    }
-}
diff --git a/jetty-spdy/spdy-server/src/test/resources/jetty-logging.properties b/jetty-spdy/spdy-server/src/test/resources/jetty-logging.properties
deleted file mode 100644
index ead13ec..0000000
--- a/jetty-spdy/spdy-server/src/test/resources/jetty-logging.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
-#org.eclipse.jetty.spdy.LEVEL=DEBUG
diff --git a/jetty-spring/pom.xml b/jetty-spring/pom.xml
index b27a636..20edc67 100644
--- a/jetty-spring/pom.xml
+++ b/jetty-spring/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-spring</artifactId>
@@ -11,29 +11,13 @@
   <properties>
     <spring-version>3.2.8.RELEASE</spring-version>
     <dependencies>target/dependencies</dependencies>
+    <bundle-symbolic-name>${project.groupId}.spring</bundle-symbolic-name>
   </properties>
 
   <build>
     <defaultGoal>install</defaultGoal>
     <plugins>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <groupId>org.jacoco</groupId>
         <artifactId>jacoco-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-spring/src/main/config/modules/spring.mod b/jetty-spring/src/main/config/modules/spring.mod
index 444afb2..39b9b8d 100644
--- a/jetty-spring/src/main/config/modules/spring.mod
+++ b/jetty-spring/src/main/config/modules/spring.mod
@@ -1,6 +1,7 @@
 #
 # Spring
 #
+
 [name]
 spring
 
diff --git a/jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessor.java b/jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessor.java
index 5fd9606..94349af 100644
--- a/jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessor.java
+++ b/jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessor.java
@@ -36,28 +36,27 @@
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
-import org.springframework.beans.factory.xml.XmlBeanFactory;
 import org.springframework.core.io.ByteArrayResource;
 import org.springframework.core.io.Resource;
 import org.springframework.core.io.UrlResource;
 
 /**
  * Spring ConfigurationProcessor
- * <p/>
+ * <p>
  * A {@link ConfigurationProcessor} that uses a spring XML file to emulate the {@link XmlConfiguration} format.
- * <p/>
+ * <p>
  * {@link XmlConfiguration} expects a primary object that is either passed in to a call to {@link #configure(Object)}
  * or that is constructed by a call to {@link #configure()}. This processor looks for a bean definition
  * with an id, name or alias of "Main" as uses that as the primary bean.
- * <p/>
+ * <p>
  * The objects mapped by {@link XmlConfiguration#getIdMap()} are set as singletons before any configuration calls
  * and if the spring configuration file contains a definition for the singleton id, the the singleton is updated
- * with a call to {@link XmlBeanFactory#configureBean(Object, String)}.
- * <p/>
+ * with a call to {@link DefaultListableBeanFactory#configureBean(Object, String)}.
+ * <p>
  * The property map obtained via {@link XmlConfiguration#getProperties()} is set as a singleton called "properties"
  * and values can be accessed by somewhat verbose
  * usage of {@link org.springframework.beans.factory.config.MethodInvokingFactoryBean}.
- * <p/>
+ * <p>
  * This processor is returned by the {@link SpringConfigurationProcessorFactory} for any XML document whos first
  * element is "beans". The factory is discovered by a {@link ServiceLoader} for {@link ConfigurationProcessorFactory}.
  */
diff --git a/jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessorFactory.java b/jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessorFactory.java
index 1d42ab3..faba982 100644
--- a/jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessorFactory.java
+++ b/jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessorFactory.java
@@ -24,7 +24,7 @@
 
 /**
  * Spring ConfigurationProcessor Factory
- * <p/>
+ * <p>
  * Create a {@link SpringConfigurationProcessor} for XML documents with a "beans" element.
  * The factory is discovered by a {@link java.util.ServiceLoader} for {@link ConfigurationProcessorFactory}.
  *
diff --git a/jetty-start/pom.xml b/jetty-start/pom.xml
index 1987c9e..d28c01c 100644
--- a/jetty-start/pom.xml
+++ b/jetty-start/pom.xml
@@ -2,13 +2,17 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-start</artifactId>
   <name>Jetty :: Start</name>
   <description>The start utility</description>
   <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.start</bundle-symbolic-name>
+    <start-jar-file-name>start.jar</start-jar-file-name>
+  </properties>
   <build>
    <plugins>
       <plugin>
@@ -30,9 +34,6 @@
       </plugin>
     </plugins>
   </build>
-  <properties>
-    <start-jar-file-name>start.jar</start-jar-file-name>
-  </properties>
   <dependencies>
     <dependency>
       <groupId>org.eclipse.jetty.toolchain</groupId>
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/BaseBuilder.java b/jetty-start/src/main/java/org/eclipse/jetty/start/BaseBuilder.java
new file mode 100644
index 0000000..4572fdb
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/BaseBuilder.java
@@ -0,0 +1,399 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.start.builders.StartDirBuilder;
+import org.eclipse.jetty.start.builders.StartIniBuilder;
+import org.eclipse.jetty.start.fileinits.MavenLocalRepoFileInitializer;
+import org.eclipse.jetty.start.fileinits.TestFileInitializer;
+import org.eclipse.jetty.start.fileinits.UriFileInitializer;
+import org.eclipse.jetty.start.graph.CriteriaSetPredicate;
+import org.eclipse.jetty.start.graph.UniqueCriteriaPredicate;
+import org.eclipse.jetty.start.graph.Predicate;
+import org.eclipse.jetty.start.graph.Selection;
+
+/**
+ * Build a start configuration in <code>${jetty.base}</code>, including
+ * ini files, directories, and libs. Also handles License management.
+ */
+public class BaseBuilder
+{
+    public static interface Config
+    {
+        /**
+         * Add a module to the start environment in <code>${jetty.base}</code>
+         *
+         * @param module
+         *            the module to add
+         * @return true if module was added, false if module was not added
+         *         (because that module already exists)
+         * @throws IOException if unable to add the module
+         */
+        public boolean addModule(Module module) throws IOException;
+    }
+
+    private static final String EXITING_LICENSE_NOT_ACKNOWLEDGED = "Exiting: license not acknowledged!";
+
+    private final BaseHome baseHome;
+    private final List<FileInitializer> fileInitializers;
+    private final StartArgs startArgs;
+
+    public BaseBuilder(BaseHome baseHome, StartArgs args)
+    {
+        this.baseHome = baseHome;
+        this.startArgs = args;
+        this.fileInitializers = new ArrayList<>();
+
+        // Establish FileInitializers
+        if (args.isTestingModeEnabled())
+        {
+            // No downloads performed
+            fileInitializers.add(new TestFileInitializer());
+        }
+        else if (args.isDownload())
+        {
+            // Downloads are allowed to be performed
+            // Setup Maven Local Repo
+            Path localRepoDir = args.getMavenLocalRepoDir();
+            if (localRepoDir != null)
+            {
+                // Use provided local repo directory
+                fileInitializers.add(new MavenLocalRepoFileInitializer(baseHome,localRepoDir));
+            }
+            else
+            {
+                // No no local repo directory (direct downloads)
+                fileInitializers.add(new MavenLocalRepoFileInitializer(baseHome));
+            }
+
+            // Normal URL downloads
+            fileInitializers.add(new UriFileInitializer(baseHome));
+        }
+    }
+
+    private void ackLicenses() throws IOException
+    {
+        if (startArgs.isLicenseCheckRequired())
+        {
+            if (startArgs.isApproveAllLicenses())
+            {
+                StartLog.info("All Licenses Approved via Command Line Option");
+            }
+            else
+            {
+                Licensing licensing = new Licensing();
+                for (Module module : startArgs.getAllModules().getSelected())
+                {
+                    if (!module.hasFiles(baseHome,startArgs.getProperties()))
+                    {
+                        licensing.addModule(module);
+                    }
+                }
+
+                if (licensing.hasLicenses())
+                {
+                    StartLog.debug("Requesting License Acknowledgement");
+                    if (!licensing.acknowledgeLicenses())
+                    {
+                        StartLog.warn(EXITING_LICENSE_NOT_ACKNOWLEDGED);
+                        System.exit(1);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Build out the Base directory (if needed)
+     * 
+     * @return true if base directory was changed, false if left unchanged.
+     * @throws IOException if unable to build
+     */
+    public boolean build() throws IOException
+    {
+        Modules modules = startArgs.getAllModules();
+        boolean dirty = false;
+
+        String dirCriteria = "<add-to-startd>";
+        String iniCriteria = "<add-to-start-ini>";
+        Selection startDirSelection = new Selection(dirCriteria);
+        Selection startIniSelection = new Selection(iniCriteria);
+        
+        List<String> startDNames = new ArrayList<>();
+        startDNames.addAll(startArgs.getAddToStartdIni());
+        List<String> startIniNames = new ArrayList<>();
+        startIniNames.addAll(startArgs.getAddToStartIni());
+
+        int count = 0;
+        count += modules.selectNodes(startDNames,startDirSelection);
+        count += modules.selectNodes(startIniNames,startIniSelection);
+
+        // look for ambiguous declaration found in both places
+        Predicate ambiguousPredicate = new CriteriaSetPredicate(dirCriteria,iniCriteria);
+        List<Module> ambiguous = modules.getMatching(ambiguousPredicate);
+
+        if (ambiguous.size() > 0)
+        {
+            StringBuilder warn = new StringBuilder();
+            warn.append("Ambiguous module locations detected, defaulting to --add-to-start for the following module selections:");
+            warn.append(" [");
+            
+            for (int i = 0; i < ambiguous.size(); i++)
+            {
+                if (i > 0)
+                {
+                    warn.append(", ");
+                }
+                warn.append(ambiguous.get(i).getName());
+            }
+            warn.append(']');
+            StartLog.warn(warn.toString());
+        }
+
+        StartLog.debug("Adding %s new module(s)",count);
+        
+        // Acknowledge Licenses
+        ackLicenses();
+
+        // Collect specific modules to enable
+        // Should match 'criteria', with no other selections.explicit
+        Predicate startDMatcher = new UniqueCriteriaPredicate(dirCriteria);
+        Predicate startIniMatcher = new UniqueCriteriaPredicate(iniCriteria);
+
+        List<Module> startDModules = modules.getMatching(startDMatcher);
+        List<Module> startIniModules = modules.getMatching(startIniMatcher);
+
+        List<FileArg> files = new ArrayList<FileArg>();
+
+        if (!startDModules.isEmpty())
+        {
+            StartDirBuilder builder = new StartDirBuilder(this);
+            for (Module mod : startDModules)
+            {
+                if (ambiguous.contains(mod))
+                {
+                    // skip ambiguous module
+                    continue;
+                }
+                
+                if (mod.isSkipFilesValidation())
+                {
+                    StartLog.debug("Skipping [files] validation on %s",mod.getName());
+                } 
+                else 
+                {
+                    dirty |= builder.addModule(mod);
+                    for (String file : mod.getFiles())
+                    {
+                        files.add(new FileArg(mod,startArgs.getProperties().expand(file)));
+                    }
+                }
+            }
+        }
+
+        if (!startIniModules.isEmpty())
+        {
+            StartIniBuilder builder = new StartIniBuilder(this);
+            for (Module mod : startIniModules)
+            {
+                if (mod.isSkipFilesValidation())
+                {
+                    StartLog.debug("Skipping [files] validation on %s",mod.getName());
+                } 
+                else 
+                {
+                    dirty |= builder.addModule(mod);
+                    for (String file : mod.getFiles())
+                    {
+                        files.add(new FileArg(mod,startArgs.getProperties().expand(file)));
+                    }
+                }
+            }
+        }
+        
+        // Process files
+        files.addAll(startArgs.getFiles());
+        dirty |= processFileResources(files);
+
+        return dirty;
+    }
+
+    public BaseHome getBaseHome()
+    {
+        return baseHome;
+    }
+
+    public StartArgs getStartArgs()
+    {
+        return startArgs;
+    }
+
+    /**
+     * Process a specific file resource
+     * 
+     * @param arg
+     *            the fileArg to work with
+     * @param file
+     *            the resolved file reference to work with
+     * @return true if change was made as a result of the file, false if no change made.
+     * @throws IOException
+     *             if there was an issue in processing this file
+     */
+    private boolean processFileResource(FileArg arg, Path file) throws IOException
+    {
+        if (startArgs.isDownload() && (arg.uri != null))
+        {
+            // now on copy/download paths (be safe above all else)
+            if (!file.startsWith(baseHome.getBasePath()))
+            {
+                throw new IOException("For security reasons, Jetty start is unable to process maven file resource not in ${jetty.base} - " + file);
+            }
+            
+            // make the directories in ${jetty.base} that we need
+            FS.ensureDirectoryExists(file.getParent());
+            
+            URI uri = URI.create(arg.uri);
+
+            // Process via initializers
+            for (FileInitializer finit : fileInitializers)
+            {
+                if (finit.init(uri,file,arg.location))
+                {
+                    // Completed successfully
+                    return true;
+                }
+            }
+
+            return false;
+        }
+        else
+        {
+            // Process directly
+            boolean isDir = arg.location.endsWith("/");
+
+            if (FS.exists(file))
+            {
+                // Validate existence
+                if (isDir)
+                {
+                    if (!Files.isDirectory(file))
+                    {
+                        throw new IOException("Invalid: path should be a directory (but isn't): " + file);
+                    }
+                    if (!FS.canReadDirectory(file))
+                    {
+                        throw new IOException("Unable to read directory: " + file);
+                    }
+                }
+                else
+                {
+                    if (!FS.canReadFile(file))
+                    {
+                        throw new IOException("Unable to read file: " + file);
+                    }
+                }
+
+                return false;
+            }
+
+            if (isDir)
+            {
+                // Create directory
+                StartLog.log("MKDIR",baseHome.toShortForm(file));
+                return FS.ensureDirectoryExists(file);
+            }
+            else
+            {
+                // Warn on missing file (this has to be resolved manually by user)
+                String shortRef = baseHome.toShortForm(file);
+                if (startArgs.isTestingModeEnabled())
+                {
+                    StartLog.log("TESTING MODE","Skipping required file check on: %s",shortRef);
+                    return true;
+                }
+
+                StartLog.warn("Missing Required File: %s",baseHome.toShortForm(file));
+                startArgs.setRun(false);
+                if (arg.uri != null)
+                {
+                    StartLog.warn("  Can be downloaded From: %s",arg.uri);
+                    StartLog.warn("  Run start.jar --create-files to download");
+                }
+
+                return true;
+            }
+        }
+    }
+
+    /**
+     * Process the {@link FileArg} for startup, assume that all licenses have
+     * been acknowledged at this stage.
+     *
+     * @param files
+     *            the list of {@link FileArg}s to process
+     * @return true if base directory modified, false if left untouched
+     */
+    private boolean processFileResources(List<FileArg> files) throws IOException
+    {
+        if ((files == null) || (files.isEmpty()))
+        {
+            return false;
+        }
+
+        boolean dirty = false;
+
+        List<String> failures = new ArrayList<String>();
+
+        for (FileArg arg : files)
+        {
+            Path file = baseHome.getBasePath(arg.location);
+            try
+            {
+                dirty |= processFileResource(arg,file);
+            }
+            catch (Throwable t)
+            {
+                StartLog.warn(t);
+                failures.add(String.format("[%s] %s - %s",t.getClass().getSimpleName(),t.getMessage(),file.toAbsolutePath().toString()));
+            }
+        }
+
+        if (!failures.isEmpty())
+        {
+            StringBuilder err = new StringBuilder();
+            err.append("Failed to process all file resources.");
+            for (String failure : failures)
+            {
+                err.append(System.lineSeparator()).append(" - ").append(failure);
+            }
+            StartLog.warn(err.toString());
+
+            throw new RuntimeException(err.toString());
+        }
+
+        return dirty;
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/BaseHome.java b/jetty-start/src/main/java/org/eclipse/jetty/start/BaseHome.java
index c1d9583..a46deed 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/BaseHome.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/BaseHome.java
@@ -130,7 +130,6 @@
 
     public BaseHome(CommandLineConfigSource cmdLineSource) throws IOException
     {
-
         sources = new ConfigSources();
         sources.add(cmdLineSource);
         this.homeDir = cmdLineSource.getHomePath();
@@ -201,7 +200,7 @@
      */
     public Path getBasePath(String path)
     {
-        return baseDir.resolve(path);
+        return baseDir.resolve(path).normalize().toAbsolutePath();
     }
 
     public ConfigSources getConfigSources()
@@ -324,20 +323,19 @@
      * <dt><code>lib/**&#47;*-dev.jar</code></dt>
      * <dd>Relative pattern, recursive search <code>${jetty.home}</code> then <code>${jetty.base}</code> for files under <code>lib</code> ending in
      * <code>-dev.jar</code></dd>
-     * </dl>
      * 
      * <dt><code>etc/jetty.xml</code></dt>
      * <dd>Relative pattern, no glob, search for <code>${jetty.home}/etc/jetty.xml</code> then <code>${jetty.base}/etc/jetty.xml</code></dd>
      * 
      * <dt><code>glob:/opt/app/common/*-corp.jar</code></dt>
-     * <dd>PathMapper pattern, glob, search <code>/opt/app/common/</code> for <code>*-corp.jar</code></code></dd>
+     * <dd>PathMapper pattern, glob, search <code>/opt/app/common/</code> for <code>*-corp.jar</code></dd>
      * 
      * </dl>
      * 
      * <p>
      * Notes:
      * <ul>
-     * <li>FileSystem case sensitivity is implementation specific (eg: linux is case-sensitive, windows is case-insensitive).<br/>
+     * <li>FileSystem case sensitivity is implementation specific (eg: linux is case-sensitive, windows is case-insensitive).<br>
      * See {@link java.nio.file.FileSystem#getPathMatcher(String)} for more details</li>
      * <li>Pattern slashes are implementation neutral (use '/' always and you'll be fine)</li>
      * <li>Recursive searching is limited to 30 levels deep (not configurable)</li>
@@ -415,6 +413,8 @@
 
     /**
      * Convenience method for <code>toShortForm(file.toPath())</code>
+     * @param path the path to shorten
+     * @return the short form of the path as a String
      */
     public String toShortForm(final File path)
     {
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Classpath.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Classpath.java
index 653f4ea..baac485 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Classpath.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Classpath.java
@@ -36,6 +36,11 @@
 {
     private static class Loader extends URLClassLoader
     {
+        static
+        {
+            registerAsParallelCapable();
+        }
+
         Loader(URL[] urls, ClassLoader parent)
         {
             super(urls,parent);
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java b/jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
index 4dc0f07..8294938 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
@@ -62,8 +62,8 @@
      * Perform an optional quoting of the argument, being intelligent with spaces and quotes as needed. If a subString is set in quotes it won't the subString
      * won't be escaped.
      * 
-     * @param arg
-     * @return
+     * @param arg the argument to quote
+     * @return the quoted and escaped argument
      */
     public static String quote(String arg)
     {
@@ -126,7 +126,6 @@
     /**
      * Similar to {@link #addArg(String)} but concats both name + value with an "=" sign, quoting were needed, and excluding the "=" portion if the value is
      * undefined or empty.
-     * <p>
      * 
      * <pre>
      *   addEqualsArg("-Dname", "value") = "-Dname=value"
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/FS.java b/jetty-start/src/main/java/org/eclipse/jetty/start/FS.java
index 6335f81..5c731c6 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/FS.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/FS.java
@@ -67,14 +67,15 @@
         return Files.exists(ret);
     }
 
-    public static void ensureDirectoryExists(Path dir) throws IOException
+    public static boolean ensureDirectoryExists(Path dir) throws IOException
     {
         if (exists(dir))
         {
             // exists already, nothing to do
-            return;
+            return false;
         }
         Files.createDirectories(dir);
+        return true;
     }
 
     public static void ensureDirectoryWritable(Path dir) throws IOException
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/FileInitializer.java b/jetty-start/src/main/java/org/eclipse/jetty/start/FileInitializer.java
new file mode 100644
index 0000000..5de0d09
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/FileInitializer.java
@@ -0,0 +1,46 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Path;
+
+/**
+ * Interface for initializing a file resource.
+ */
+public interface FileInitializer
+{
+    /**
+     * Initialize a file resource
+     * 
+     * @param uri
+     *            the remote URI of the resource acting as its source
+     * @param file
+     *            the local file resource to initialize. (often in ${jetty.base} directory) 
+     * @param fileRef
+     *            the simple string reference to the output file, suitable for searching
+     *            for the file in other locations (like ${jetty.home} or ${jetty.dir})
+     * @return true if local file is initialized (resulted in a change on disk), false if this
+     *         {@link FileInitializer} did nothing.
+     * @throws IOException
+     *             if there was an attempt to initialize, but an error occurred.
+     */
+    public boolean init(URI uri, Path file, String fileRef) throws IOException;
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/JavaVersion.java b/jetty-start/src/main/java/org/eclipse/jetty/start/JavaVersion.java
new file mode 100644
index 0000000..59b6675
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/JavaVersion.java
@@ -0,0 +1,153 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class JavaVersion
+{
+    private static final Pattern PRE_JDK9 = Pattern.compile("1\\.(\\d)(\\.(\\d+)(_(\\d+))?)?(-.+)?");
+    // Regexp from JEP 223 (http://openjdk.java.net/jeps/223).
+    private static final Pattern JDK9 = Pattern.compile("(\\d+)(\\.(\\d+))?(\\.(\\d+))?((-.+)?(\\+(\\d+)?(-.+)?)?)");
+
+    public static JavaVersion parse(String version)
+    {
+        if (version.startsWith("1."))
+            return parsePreJDK9(version);
+        return parseJDK9(version);
+    }
+
+    private static JavaVersion parsePreJDK9(String version)
+    {
+        Matcher matcher = PRE_JDK9.matcher(version);
+        if (!matcher.matches())
+            throw new IllegalArgumentException("Invalid Java version " + version);
+        int major = 1;
+        int minor = Integer.parseInt(matcher.group(1));
+        String microGroup = matcher.group(3);
+        int micro = microGroup == null || microGroup.isEmpty() ? 0 : Integer.parseInt(microGroup);
+        String updateGroup = matcher.group(5);
+        int update = updateGroup == null || updateGroup.isEmpty() ? 0 : Integer.parseInt(updateGroup);
+        String suffix = matcher.group(6);
+        return new JavaVersion(version, minor, major, minor, micro, update, suffix);
+    }
+
+    private static JavaVersion parseJDK9(String version)
+    {
+        Matcher matcher = JDK9.matcher(version);
+        if (!matcher.matches())
+            throw new IllegalArgumentException("Invalid Java version " + version);
+        int major = Integer.parseInt(matcher.group(1));
+        String minorGroup = matcher.group(3);
+        int minor = minorGroup == null || minorGroup.isEmpty() ? 0 : Integer.parseInt(minorGroup);
+        String microGroup = matcher.group(5);
+        int micro = microGroup == null || microGroup.isEmpty() ? 0 : Integer.parseInt(microGroup);
+        String suffix = matcher.group(6);
+        return new JavaVersion(version, major, major, minor, micro, 0, suffix);
+    }
+
+    private final String version;
+    private final int platform;
+    private final int major;
+    private final int minor;
+    private final int micro;
+    private final int update;
+    private final String suffix;
+
+    private JavaVersion(String version, int platform, int major, int minor, int micro, int update, String suffix)
+    {
+        this.version = version;
+        this.platform = platform;
+        this.major = major;
+        this.minor = minor;
+        this.micro = micro;
+        this.update = update;
+        this.suffix = suffix;
+    }
+
+    /**
+     * @return the string from which this JavaVersion was created
+     */
+    public String getVersion()
+    {
+        return version;
+    }
+
+    /**
+     * <p>Returns the Java Platform version, such as {@code 8} for JDK 1.8.0_92 and {@code 9} for JDK 9.2.4.</p>
+     *
+     * @return the Java Platform version
+     */
+    public int getPlatform()
+    {
+        return platform;
+    }
+
+    /**
+     * <p>Returns the major number version, such as {@code 1} for JDK 1.8.0_92 and {@code 9} for JDK 9.2.4.</p>
+     *
+     * @return the major number version
+     */
+    public int getMajor()
+    {
+        return major;
+    }
+
+    /**
+     * <p>Returns the minor number version, such as {@code 8} for JDK 1.8.0_92 and {@code 2} for JDK 9.2.4.</p>
+     *
+     * @return the minor number version
+     */
+    public int getMinor()
+    {
+        return minor;
+    }
+
+    /**
+     * <p>Returns the micro number version, such as {@code 0} for JDK 1.8.0_92 and {@code 4} for JDK 9.2.4.</p>
+     *
+     * @return the micro number version
+     */
+    public int getMicro()
+    {
+        return micro;
+    }
+
+    /**
+     * <p>Returns the update number version, such as {@code 92} for JDK 1.8.0_92 and {@code 0} for JDK 9.2.4.</p>
+     *
+     * @return the update number version
+     */
+    public int getUpdate()
+    {
+        return update;
+    }
+
+    /**
+     * <p>Returns the remaining string after the version numbers, such as {@code -internal} for
+     * JDK 1.8.0_92-internal and {@code -ea} for JDK 9-ea, or {@code +13} for JDK 9.2.4+13.</p>
+     *
+     * @return the remaining string after the version numbers
+     */
+    public String getSuffix()
+    {
+        return suffix;
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Licensing.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Licensing.java
new file mode 100644
index 0000000..afd598d
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Licensing.java
@@ -0,0 +1,105 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Handles basic license presentation and acknowledgement.
+ */
+public class Licensing
+{
+    private static final String PROP_ACK_LICENSES = "org.eclipse.jetty.start.ack.licenses";
+    public Map<String, List<String>> licenseMap = new TreeMap<>(new NaturalSort.Strings());
+
+    public void addModule(Module module)
+    {
+        if (!module.hasLicense())
+        {
+            // skip, no license
+            return;
+        }
+
+        if (licenseMap.containsKey(module.getName()))
+        {
+            // skip, already being tracked
+            return;
+        }
+
+        licenseMap.put(module.getName(),module.getLicense());
+    }
+
+    public boolean hasLicenses()
+    {
+        return !licenseMap.isEmpty();
+    }
+
+    public boolean acknowledgeLicenses() throws IOException
+    {
+        if (!hasLicenses())
+        {
+            return true;
+        }
+
+        System.err.printf("%nALERT: There are enabled module(s) with licenses.%n");
+        System.err.printf("The following %d module(s):%n", licenseMap.size());
+        System.err.printf(" + contains software not provided by the Eclipse Foundation!%n");
+        System.err.printf(" + contains software not covered by the Eclipse Public License!%n");
+        System.err.printf(" + has not been audited for compliance with its license%n");
+
+        for (String key : licenseMap.keySet())
+        {
+            System.err.printf("%n Module: %s%n",key);
+            for (String line : licenseMap.get(key))
+            {
+                System.err.printf("  + %s%n",line);
+            }
+        }
+
+        boolean licenseAck = false;
+
+        String propBasedAckValue = System.getProperty(PROP_ACK_LICENSES);
+        if (propBasedAckValue != null)
+        {
+            StartLog.log("TESTING MODE","Programmatic ACK - %s=%s",PROP_ACK_LICENSES,propBasedAckValue);
+            licenseAck = Boolean.parseBoolean(propBasedAckValue);
+        }
+        else
+        {
+            if (Boolean.getBoolean("org.eclipse.jetty.start.testing"))
+            {
+                throw new RuntimeException("Test Configuration Missing - Pre-specify answer to (" + PROP_ACK_LICENSES + ") in test case");
+            }
+
+            BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
+            System.err.printf("%nProceed (y/N)? ");
+            String response = input.readLine();
+
+            licenseAck = (Utils.isNotBlank(response) && response.toLowerCase(Locale.ENGLISH).startsWith("y"));
+        }
+
+        return licenseAck;
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java
index 50dc7c2..8f2e9ff 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java
@@ -18,38 +18,32 @@
 
 package org.eclipse.jetty.start;
 
-import static org.eclipse.jetty.start.UsageException.*;
+import static org.eclipse.jetty.start.UsageException.ERR_BAD_GRAPH;
+import static org.eclipse.jetty.start.UsageException.ERR_BAD_STOP_PROPS;
+import static org.eclipse.jetty.start.UsageException.ERR_INVOKE_MAIN;
+import static org.eclipse.jetty.start.UsageException.ERR_NOT_STOPPED;
+import static org.eclipse.jetty.start.UsageException.ERR_UNKNOWN;
 
 import java.io.BufferedReader;
-import java.io.BufferedWriter;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.LineNumberReader;
 import java.io.OutputStream;
-import java.io.PrintWriter;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.ConnectException;
 import java.net.InetAddress;
 import java.net.Socket;
 import java.net.SocketTimeoutException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
-import java.util.Set;
-import java.util.regex.Pattern;
 
 import org.eclipse.jetty.start.config.CommandLineConfigSource;
+import org.eclipse.jetty.start.graph.GraphException;
+import org.eclipse.jetty.start.graph.Selection;
 
 /**
  * Main start class.
@@ -57,52 +51,22 @@
  * This class is intended to be the main class listed in the MANIFEST.MF of the start.jar archive. It allows the Jetty Application server to be started with the
  * command "java -jar start.jar".
  * <p>
- * Argument processing steps:
+ * <b>Argument processing steps:</b>
  * <ol>
- * <li>Directory Locations:
- * <ul>
- * <li>jetty.home=[directory] (the jetty.home location)</li>
- * <li>jetty.base=[directory] (the jetty.base location)</li>
- * </ul>
- * </li>
- * <li>Start Logging behavior:
- * <ul>
- * <li>--debug (debugging enabled)</li>
- * <li>--start-log-file=logs/start.log (output start logs to logs/start.log location)</li>
- * </ul>
- * </li>
+ * <li>Directory Location: jetty.home=[directory] (the jetty.home location)</li>
+ * <li>Directory Location: jetty.base=[directory] (the jetty.base location)</li>
+ * <li>Start Logging behavior: --debug (debugging enabled)</li>
+ * <li>Start Logging behavior: --start-log-file=logs/start.log (output start logs to logs/start.log location)</li>
  * <li>Module Resolution</li>
  * <li>Properties Resolution</li>
  * <li>Present Optional Informational Options</li>
  * <li>Normal Startup</li>
- * </li>
  * </ol>
  */
 public class Main
 {
-    private static final String EXITING_LICENSE_NOT_ACKNOWLEDGED = "Exiting: license not acknowledged!";
     private static final int EXIT_USAGE = 1;
 
-    public static String join(Collection<?> objs, String delim)
-    {
-        if (objs==null)
-        {
-            return "";
-        }
-        StringBuilder str = new StringBuilder();
-        boolean needDelim = false;
-        for (Object obj : objs)
-        {
-            if (needDelim)
-            {
-                str.append(delim);
-            }
-            str.append(obj);
-            needDelim = true;
-        }
-        return str.toString();
-    }
-
     public static void main(String[] args)
     {
         try
@@ -172,76 +136,6 @@
         }).start();
     }
 
-    private void initFile(StartArgs args, FileArg farg)
-    {
-        try
-        {
-            Path file = baseHome.getBasePath(farg.location);
-            
-            StartLog.debug("[init-file] %s module specified file %s",file.toAbsolutePath(),(FS.exists(file)?"[Exists!]":""));
-            if (FS.exists(file))
-            {
-                // file already initialized / downloaded, skip it
-                return;
-            }
-
-            if (farg.uri!=null)
-            {
-                URL url = new URL(farg.uri);
-
-                StartLog.log("DOWNLOAD", "%s to %s", url, farg.location);
-
-                FS.ensureDirectoryExists(file.getParent());
-                
-                if (args.isTestingModeEnabled())
-                {
-                    StartLog.log("TESTING MODE", "Skipping download of " + url);
-                    return;
-                }
-
-                byte[] buf = new byte[8192];
-                try (InputStream in = url.openStream(); 
-                     OutputStream out = Files.newOutputStream(file,StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE))
-                {
-                    while (true)
-                    {
-                        int len = in.read(buf);
-
-                        if (len > 0)
-                        {
-                            out.write(buf,0,len);
-                        }
-                        if (len < 0)
-                        {
-                            break;
-                        }
-                    }
-                }
-            }
-            else if (farg.location.endsWith("/"))
-            {
-                StartLog.log("MKDIR",baseHome.toShortForm(file));
-                FS.ensureDirectoryExists(file);
-            }
-            else
-            {
-                String shortRef = baseHome.toShortForm(file);
-                if (args.isTestingModeEnabled())
-                {
-                    StartLog.log("TESTING MODE","Skipping required file check on: %s",shortRef);
-                    return;
-                }
-                StartLog.warn("MISSING: Required file %s",shortRef);
-            }
-        }
-        catch (Exception e)
-        {
-            StartLog.warn("ERROR: processing %s%n%s",farg,e);
-            StartLog.warn(e);
-            usageExit(EXIT_USAGE);
-        }
-    }
-
     private void dumpClasspathWithVersions(Classpath classpath)
     {
         StartLog.endStartLog();
@@ -344,7 +238,7 @@
         args.dumpActiveXmls(baseHome);
     }
 
-    private void listModules(StartArgs args)
+    public void listModules(StartArgs args)
     {
         StartLog.endStartLog();
         System.out.println();
@@ -354,227 +248,17 @@
 
         // Dump Enabled Modules
         System.out.println();
-        System.out.println("Jetty Active Module Tree:");
-        System.out.println("-------------------------");
+        System.out.println("Jetty Selected Module Ordering:");
+        System.out.println("-------------------------------");
         Modules modules = args.getAllModules();
-        modules.dumpEnabledTree();
-    }
-
-    /**
-     * Build out INI file.
-     * <p>
-     * This applies equally for either <code>${jetty.base}/start.ini</code> or
-     * <code>${jetty.base}/start.d/${name}.ini</code> 
-     * 
-     * @param args the arguments of what modules are enabled
-     * @param name the name of the module to based the build of the ini
-     * @param topLevel 
-     * @param appendStartIni true to append to <code>${jetty.base}/start.ini</code>, 
-     * false to create a <code>${jetty.base}/start.d/${name}.ini</code> entry instead.
-     * @throws IOException
-     */
-    private void buildIni(StartArgs args, String name, boolean topLevel, boolean appendStartIni) throws IOException
-    {        
-        // Find the start.d relative to the base directory only.
-        Path start_d = baseHome.getBasePath("start.d");
-
-        // Is this a module?
-        Modules modules = args.getAllModules();
-        Module module = modules.get(name);
-        if (module == null)
-        {
-            StartLog.warn("ERROR: No known module for %s",name);
-            return;
-        }
-        
-        boolean transitive = module.isEnabled() && (module.getSources().size() == 0);
-
-        // Find any named ini file and check it follows the convention
-        Path start_ini = baseHome.getBasePath("start.ini");
-        String short_start_ini = baseHome.toShortForm(start_ini);
-        Path startd_ini = start_d.resolve(name + ".ini");
-        String short_startd_ini = baseHome.toShortForm(startd_ini);
-        StartIni module_ini = null;
-        if (FS.exists(startd_ini))
-        {
-            module_ini = new StartIni(startd_ini);
-            if (module_ini.getLineMatches(Pattern.compile("--module=(.*, *)*" + name)).size() == 0)
-            {
-                StartLog.warn("ERROR: %s is not enabled in %s!",name,short_startd_ini);
-                return;
-            }
-        }
-
-        if (!args.isApproveAllLicenses())
-        {
-            if (!module.hasFiles(baseHome) && !module.acknowledgeLicense())
-            {
-                StartLog.warn(EXITING_LICENSE_NOT_ACKNOWLEDGED);
-                System.exit(1);
-            }
-        }
-        
-        boolean buildIni=false;
-        if (module.isEnabled())
-        {
-            // is it an explicit request to create an ini file?
-            if (topLevel && !FS.exists(startd_ini) && !appendStartIni)
-            {
-                buildIni=true;
-            }
-            // else is it transitive
-            else if (transitive)
-            {
-                if (module.hasDefaultConfig())
-                {
-                    buildIni = true;
-                    StartLog.info("%-15s initialised transitively",name);
-                }
-            }
-            // else must be initialized explicitly
-            else 
-            {
-                for (String source : module.getSources())
-                {
-                    StartLog.info("%-15s initialised in %s",name,baseHome.toShortForm(source));
-                }
-            }
-        }
-        else 
-        {
-            buildIni=true;
-        }
-        
-        String source = "<transitive>";
-
-        // If we need an ini
-        if (buildIni)
-        {
-            // File BufferedWriter
-            BufferedWriter writer = null;
-            PrintWriter out = null;
-            try
-            {
-                if (appendStartIni)
-                {
-                    source = short_start_ini;
-                    StartLog.info("%-15s initialised in %s (appended)",name,source);
-                    writer = Files.newBufferedWriter(start_ini,StandardCharsets.UTF_8,StandardOpenOption.CREATE,StandardOpenOption.APPEND);
-                    out = new PrintWriter(writer);
-                }
-                else
-                {
-                    // Create the directory if needed
-                    FS.ensureDirectoryExists(start_d);
-                    FS.ensureDirectoryWritable(start_d);
-                    source = short_startd_ini;
-                    StartLog.info("%-15s initialised in %s (created)",name,source);
-                    writer = Files.newBufferedWriter(startd_ini,StandardCharsets.UTF_8,StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE);
-                    out = new PrintWriter(writer);
-                }
-
-                if (appendStartIni)
-                {
-                    out.println();
-                }
-                out.println("# --------------------------------------- ");
-                out.println("# Module: " + name);
-
-                out.println("--module=" + name);
-                
-                args.parse("--module=" + name,source);
-                args.parseModule(module);
-                
-                for (String line : module.getDefaultConfig())
-                {
-                    out.println(line);
-                }
-            }
-            finally
-            {
-                if (out != null)
-                {
-                    out.close();
-                }
-            }
-        }
-        
-        modules.enable(name,Collections.singletonList(source));
-        
-        // Also list other places this module is enabled
-        for (String src : module.getSources())
-        {
-            StartLog.debug("also enabled in: %s",src);
-            if (!short_start_ini.equals(src))
-            {
-                StartLog.info("%-15s enabled in     %s",name,baseHome.toShortForm(src));
-            }
-        }
-
-        // Do downloads now
-        for (String file : module.getFiles())
-        {
-            initFile(args, new FileArg(module,file));
-        }
-
-        // Process dependencies
-        module.expandProperties(args.getProperties());
-        modules.registerParentsIfMissing(module);
-        modules.buildGraph();
-        
-        // process new ini modules
-        if (topLevel)
-        {
-            List<Module> depends = new ArrayList<>();
-            for (String depend : modules.resolveParentModulesOf(name))
-            {
-                if (!name.equals(depend))
-                {
-                    Module m = modules.get(depend);
-                    m.setEnabled(true);
-                    depends.add(m);
-                }
-            }
-            Collections.sort(depends,Collections.reverseOrder(new Module.DepthComparator()));
-            
-            Set<String> done = new HashSet<>(0);
-            while (true)
-            {
-                // initialize known dependencies
-                boolean complete=true;
-                for (Module m : depends)
-                {
-                    if (!done.contains(m.getName()))
-                    {
-                        complete=false;
-                        buildIni(args,m.getName(),false,appendStartIni);
-                        done.add(m.getName());
-                    }
-                }
-                
-                if (complete)
-                {
-                    break;
-                }
-                
-                // look for any new ones resolved via expansion
-                depends.clear();
-                for (String depend : modules.resolveParentModulesOf(name))
-                {
-                    if (!name.equals(depend))
-                    {
-                        Module m = modules.get(depend);
-                        m.setEnabled(true);
-                        depends.add(m);
-                    }
-                }
-                Collections.sort(depends,Collections.reverseOrder(new Module.DepthComparator()));
-            }
-        }
+        modules.dumpSelected();
     }
 
     /**
      * Convenience for <code>processCommandLine(cmdLine.toArray(new String[cmdLine.size()]))</code>
+     * @param cmdLine the command line
+     * @return the start args parsed from the command line
+     * @throws Exception if unable to process the command line
      */
     public StartArgs processCommandLine(List<String> cmdLine) throws Exception
     {
@@ -601,30 +285,58 @@
         StartArgs args = new StartArgs();
         args.parse(baseHome.getConfigSources());
 
-        // ------------------------------------------------------------
-        // 3) Module Registration
-        Modules modules = new Modules(baseHome,args);
-        StartLog.debug("Registering all modules");
-        modules.registerAll();
-
-        // ------------------------------------------------------------
-        // 4) Active Module Resolution
-        for (String enabledModule : args.getEnabledModules())
+        try
         {
-            List<String> msources = args.getSources(enabledModule);
-            modules.enable(enabledModule,msources);
-        }
-        
-        StartLog.debug("Building Module Graph");
-        modules.buildGraph();
+            // ------------------------------------------------------------
+            // 3) Module Registration
+            Modules modules = new Modules(baseHome,args);
+            StartLog.debug("Registering all modules");
+            modules.registerAll();
 
-        args.setAllModules(modules);
-        List<Module> activeModules = modules.resolveEnabled();
-        
-        // ------------------------------------------------------------
-        // 5) Lib & XML Expansion / Resolution
-        args.expandLibs(baseHome);
-        args.expandModules(baseHome,activeModules);
+            // ------------------------------------------------------------
+            // 4) Active Module Resolution
+            for (String enabledModule : args.getEnabledModules())
+            {
+                for (String source : args.getSources(enabledModule))
+                {
+                    String shortForm = baseHome.toShortForm(source);
+                    modules.selectNode(enabledModule,new Selection(shortForm));
+                }
+            }
+
+            StartLog.debug("Building Module Graph");
+            modules.buildGraph();
+
+            args.setAllModules(modules);
+            List<Module> activeModules = modules.getSelected();
+            
+            final Version START_VERSION = new Version(StartArgs.VERSION);
+            
+            for(Module enabled: activeModules)
+            {
+                if(enabled.getVersion().isNewerThan(START_VERSION))
+                {
+                    throw new UsageException(UsageException.ERR_BAD_GRAPH, "Module [" + enabled.getName() + "] specifies jetty version [" + enabled.getVersion()
+                            + "] which is newer than this version of jetty [" + START_VERSION + "]");
+                }
+            }
+            
+            for(String name: args.getSkipFileValidationModules())
+            {
+                Module module = modules.get(name);
+                module.setSkipFilesValidation(true);
+            }
+
+            // ------------------------------------------------------------
+            // 5) Lib & XML Expansion / Resolution
+            args.expandLibs(baseHome);
+            args.expandModules(baseHome,activeModules);
+
+        }
+        catch (GraphException e)
+        {
+            throw new UsageException(ERR_BAD_GRAPH,e);
+        }
 
         // ------------------------------------------------------------
         // 6) Resolve Extra XMLs
@@ -636,7 +348,7 @@
 
         return args;
     }
-
+    
     public void start(StartArgs args) throws IOException, InterruptedException
     {
         StartLog.debug("StartArgs: %s",args);
@@ -684,7 +396,7 @@
         if (args.isDryRun())
         {
             CommandLineBuilder cmd = args.getMainArgs(baseHome,true);
-            System.out.println(cmd.toString(File.separatorChar=='/'?" \\\n":" "));
+            System.out.println(cmd.toString(StartLog.isDebugEnabled()?" \\\n":" "));
         }
 
         if (args.isStopCommand())
@@ -692,83 +404,12 @@
             doStop(args);
         }
         
-        boolean rebuildGraph = false;
-
-        // Initialize start.ini
-        for (String module : args.getAddToStartIni())
+        BaseBuilder baseBuilder = new BaseBuilder(baseHome,args);
+        if(baseBuilder.build())
         {
-            buildIni(args,module,true,true);
-            rebuildGraph = true;
-        }
-
-        // Initialize start.d
-        for (String module : args.getAddToStartdIni())
-        {
-            buildIni(args,module,true,false);
-            rebuildGraph = true;
-        }
-        
-        if (rebuildGraph)
-        {
-            args.getAllModules().clearMissing();
-            args.getAllModules().buildGraph();
-        }
-        
-        // If in --create-files, check licenses
-        if(args.isDownload())
-        {
-            if (!args.isApproveAllLicenses())
-            {
-                for (Module module : args.getAllModules().resolveEnabled())
-                {
-                    if (!module.hasFiles(baseHome) && !module.acknowledgeLicense())
-                    {
-                        StartLog.warn(EXITING_LICENSE_NOT_ACKNOWLEDGED);
-                        System.exit(1);
-                    }
-                }
-            }
-        }
-
-        // Check ini files for download possibilities
-        for (FileArg arg : args.getFiles())
-        {
-            Path file = baseHome.getBasePath(arg.location);
-            if (!FS.exists(file) && args.isDownload())
-            {
-                initFile(args, arg);
-            }
-
-            if (!FS.exists(file))
-            {
-                boolean isDir = arg.location.endsWith("/");
-                if (isDir)
-                {
-                    StartLog.log("MKDIR", baseHome.toShortForm(file));
-                    FS.ensureDirectoryExists(file);
-                    /* Startup should not fail to run on missing directories.
-                     * See Bug #427204
-                     */
-                    // args.setRun(false);
-                }
-                else
-                {
-                    String shortRef = baseHome.toShortForm(file);
-                    if (args.isTestingModeEnabled())
-                    {
-                        StartLog.log("TESTING MODE","Skipping required file check on: %s",shortRef);
-                        return;
-                    }
-
-                    StartLog.warn("Missing Required File: %s",baseHome.toShortForm(file));
-                    args.setRun(false);
-                    if (arg.uri != null)
-                    {
-                        StartLog.warn("  Can be downloaded From: %s",arg.uri);
-                        StartLog.warn("  Run start.jar --create-files to download");
-                    }
-                }
-            }
+            // base directory changed.
+            StartLog.info("Base directory was modified");
+            return;
         }
         
         // Informational command line, don't run jetty
@@ -824,24 +465,46 @@
 
     private void doStop(StartArgs args)
     {
-        String stopHost = args.getProperties().getString("STOP.HOST");
-        int stopPort = Integer.parseInt(args.getProperties().getString("STOP.PORT"));
-        String stopKey = args.getProperties().getString("STOP.KEY");
-
-        if (args.getProperties().getString("STOP.WAIT") != null)
+        Props.Prop stopHostProp = args.getProperties().getProp("STOP.HOST", true);
+        Props.Prop stopPortProp = args.getProperties().getProp("STOP.PORT", true);
+        Props.Prop stopKeyProp = args.getProperties().getProp("STOP.KEY", true);
+        Props.Prop stopWaitProp = args.getProperties().getProp("STOP.WAIT", true);
+        
+        String stopHost = "127.0.0.1";
+        int stopPort = -1;
+        String stopKey = "";
+    
+        if (stopHostProp != null)
         {
-            int stopWait = Integer.parseInt(args.getProperties().getString("STOP.WAIT"));
+            stopHost = stopHostProp.value;
+        }
+    
+        if (stopPortProp != null)
+        {
+            stopPort = Integer.parseInt(stopPortProp.value);
+        }
+        
+        if(stopKeyProp != null)
+        {
+            stopKey = stopKeyProp.value;
+        }
 
-            stop(stopHost,stopPort,stopKey,stopWait);
+        if (stopWaitProp != null)
+        {
+            int stopWait = Integer.parseInt(stopWaitProp.value);
+            stop(stopHost, stopPort, stopKey, stopWait);
         }
         else
         {
-            stop(stopHost,stopPort,stopKey);
+            stop(stopHost, stopPort, stopKey);
         }
     }
 
     /**
      * Stop a running jetty instance.
+     * @param host the host
+     * @param port the port
+     * @param key the key
      */
     public void stop(String host, int port, String key)
     {
@@ -851,18 +514,21 @@
     public void stop(String host, int port, String key, int timeout)
     {
         if (host==null || host.length()==0)
-            host="127.0.0.1";
+        {
+            host = "127.0.0.1";
+        }
         
         try
         {
-            if (port <= 0)
+            if ( (port <= 0) || (port > 65535) )
             {
-                System.err.println("STOP.PORT system property must be specified");
+                System.err.println("STOP.PORT property must be specified with a valid port number");
+                usageExit(ERR_BAD_STOP_PROPS);
             }
             if (key == null)
             {
                 key = "";
-                System.err.println("STOP.KEY system property must be specified");
+                System.err.println("STOP.KEY property must be specified");
                 System.err.println("Using empty key");
             }
 
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java
index 970d89c..d5ef4f9 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java
@@ -21,46 +21,26 @@
 import java.io.BufferedReader;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.text.CollationKey;
 import java.text.Collator;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
-import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.eclipse.jetty.start.graph.Node;
+
 /**
  * Represents a Module metadata, as defined in Jetty.
  */
-public class Module
+public class Module extends Node<Module>
 {
-    public static class DepthComparator implements Comparator<Module>
-    {
-        private Collator collator = Collator.getInstance();
-
-        @Override
-        public int compare(Module o1, Module o2)
-        {
-            // order by depth first.
-            int diff = o1.depth - o2.depth;
-            if (diff != 0)
-            {
-                return diff;
-            }
-            // then by name (not really needed, but makes for predictable test cases)
-            CollationKey k1 = collator.getCollationKey(o1.fileRef);
-            CollationKey k2 = collator.getCollationKey(o2.fileRef);
-            return k1.compareTo(k2);
-        }
-    }
+    private static final String VERSION_UNSPECIFIED = "9.2";
 
     public static class NameComparator implements Comparator<Module>
     {
@@ -78,84 +58,51 @@
 
     /** The file of the module */
     private Path file;
+    
     /** The name of this Module (as a filesystem reference) */
     private String fileRef;
-    /**
-     * The logical name of this module (for property selected references), And to aid in duplicate detection.
-     */
-    private String logicalName;
-    /** The depth of the module in the tree */
-    private int depth = 0;
-    /** Set of Modules, by name, that this Module depends on */
-    private Set<String> parentNames;
-    /** Set of Modules, by name, that this Module optionally depend on */
-    private Set<String> optionalParentNames;
-    /** The Edges to parent modules */
-    private Set<Module> parentEdges;
-    /** The Edges to child modules */
-    private Set<Module> childEdges;
+    
+    /** The version of Jetty the module supports */
+    private Version version;
+
     /** List of xml configurations for this Module */
     private List<String> xmls;
+    
     /** List of ini template lines */
+    private List<String> iniTemplate;
+    private boolean hasIniTemplate = false;
+    
+    /** List of default config */
     private List<String> defaultConfig;
     private boolean hasDefaultConfig = false;
+    
     /** List of library options for this Module */
     private List<String> libs;
+    
     /** List of files for this Module */
     private List<String> files;
+    /** Skip File Validation (default: false) */
+    private boolean skipFilesValidation = false;
+    
     /** List of jvm Args */
     private List<String> jvmArgs;
+    
     /** License lines */
     private List<String> license;
 
-    /** Is this Module enabled via start.jar command line, start.ini, or start.d/*.ini ? */
-    private boolean enabled = false;
-    /** List of sources that enabled this module */
-    private final Set<String> sources = new HashSet<>();
-    private boolean licenseAck = false;
-
     public Module(BaseHome basehome, Path file) throws FileNotFoundException, IOException
     {
+        super();
         this.file = file;
 
         // Strip .mod
         this.fileRef = Pattern.compile(".mod$",Pattern.CASE_INSENSITIVE).matcher(file.getFileName().toString()).replaceFirst("");
-        this.logicalName = fileRef;
+        this.setName(fileRef);
 
         init(basehome);
         process(basehome);
     }
 
-    public void addChildEdge(Module child)
-    {
-        if (childEdges.contains(child))
-        {
-            // already present, skip
-            return;
-        }
-        this.childEdges.add(child);
-    }
-
-    public void addParentEdge(Module parent)
-    {
-        if (parentEdges.contains(parent))
-        {
-            // already present, skip
-            return;
-        }
-        this.parentEdges.add(parent);
-    }
-
-    public void addSources(List<String> sources)
-    {
-        this.sources.addAll(sources);
-    }
-
-    public void clearSources()
-    {
-        this.sources.clear();
-    }
-
     @Override
     public boolean equals(Object obj)
     {
@@ -189,23 +136,22 @@
     public void expandProperties(Props props)
     {
         // Expand Parents
-        Set<String> parents = new HashSet<>();
-        for (String parent : parentNames)
+        List<String> parents = new ArrayList<>();
+        for (String parent : getParentNames())
         {
             parents.add(props.expand(parent));
         }
-        parentNames.clear();
-        parentNames.addAll(parents);
+        setParentNames(parents);
     }
 
-    public Set<Module> getChildEdges()
+    public List<String> getDefaultConfig()
     {
-        return childEdges;
+        return defaultConfig;
     }
-
-    public int getDepth()
+    
+    public List<String> getIniTemplate()
     {
-        return depth;
+        return iniTemplate;
     }
 
     public List<String> getFiles()
@@ -213,113 +159,50 @@
         return files;
     }
 
+    public boolean isSkipFilesValidation()
+    {
+        return skipFilesValidation;
+    }
+
     public String getFilesystemRef()
     {
         return fileRef;
     }
 
-    public List<String> getDefaultConfig()
-    {
-        return defaultConfig;
-    }
-
-    public boolean hasDefaultConfig()
-    {
-        return hasDefaultConfig;
-    }
-
-    public List<String> getLibs()
-    {
-        return libs;
-    }
-
-    public String getName()
-    {
-        return logicalName;
-    }
-
-    public Set<String> getOptionalParentNames()
-    {
-        return optionalParentNames;
-    }
-
-    public Set<Module> getParentEdges()
-    {
-        return parentEdges;
-    }
-
-    public Set<String> getParentNames()
-    {
-        return parentNames;
-    }
-
-    public Set<String> getSources()
-    {
-        return Collections.unmodifiableSet(sources);
-    }
-
-    public List<String> getXmls()
-    {
-        return xmls;
-    }
-
     public List<String> getJvmArgs()
     {
         return jvmArgs;
     }
 
-    public boolean hasLicense()
+    public List<String> getLibs()
     {
-        return license != null && license.size() > 0;
-    }
-
-    public boolean acknowledgeLicense() throws IOException
-    {
-        if (!hasLicense() || licenseAck)
-        {
-            return true;
-        }
-
-        System.err.printf("%nModule %s:%n",getName());
-        System.err.printf(" + contains software not provided by the Eclipse Foundation!%n");
-        System.err.printf(" + contains software not covered by the Eclipse Public License!%n");
-        System.err.printf(" + has not been audited for compliance with its license%n");
-        System.err.printf("%n");
-        for (String l : getLicense())
-        {
-            System.err.printf("    %s%n",l);
-        }
-
-        String propBasedAckName = "org.eclipse.jetty.start.ack.license." + getName();
-        String propBasedAckValue = System.getProperty(propBasedAckName);
-        if (propBasedAckValue != null)
-        {
-            StartLog.log("TESTING MODE", "Programmatic ACK - %s=%s",propBasedAckName,propBasedAckValue);
-            licenseAck = Boolean.parseBoolean(propBasedAckValue);
-        }
-        else
-        {
-            if (Boolean.getBoolean("org.eclipse.jetty.start.testing"))
-            {
-                throw new RuntimeException("Test Configuration Missing - Pre-specify answer to (" + propBasedAckName + ") in test case");
-            }
-
-            try (BufferedReader input = new BufferedReader(new InputStreamReader(System.in)))
-            {
-                System.err.printf("%nProceed (y/N)? ");
-                String line = input.readLine();
-
-                licenseAck = !(line == null || line.length() == 0 || !line.toLowerCase(Locale.ENGLISH).startsWith("y"));
-            }
-        }
-
-        return licenseAck;
+        return libs;
     }
 
     public List<String> getLicense()
     {
         return license;
     }
+    
+    public List<String> getXmls()
+    {
+        return xmls;
+    }
+    
+    public Version getVersion()
+    {
+        return version;
+    }
+
+    public boolean hasDefaultConfig()
+    {
+        return hasDefaultConfig;
+    }
+    
+    public boolean hasIniTemplate()
+    {
+        return hasIniTemplate;
+    }
 
     @Override
     public int hashCode()
@@ -330,14 +213,16 @@
         return result;
     }
 
+    public boolean hasLicense()
+    {
+        return (license != null) && (license.size() > 0);
+    }
+
     private void init(BaseHome basehome)
     {
-        parentNames = new HashSet<>();
-        optionalParentNames = new HashSet<>();
-        parentEdges = new HashSet<>();
-        childEdges = new HashSet<>();
         xmls = new ArrayList<>();
         defaultConfig = new ArrayList<>();
+        iniTemplate = new ArrayList<>();
         libs = new ArrayList<>();
         files = new ArrayList<>();
         jvmArgs = new ArrayList<>();
@@ -353,19 +238,24 @@
             throw new RuntimeException("Invalid Module location (must be located under /modules/ directory): " + name);
         }
         this.fileRef = mat.group(1).replace('\\','/');
-        this.logicalName = this.fileRef;
+        setName(this.fileRef);
     }
 
-    public boolean isEnabled()
+    /**
+     * Indicates a module that is dynamic in nature
+     * 
+     * @return a module where the declared metadata name does not match the filename reference (aka a dynamic module)
+     */
+    public boolean isDynamic()
     {
-        return enabled;
+        return !getName().equals(fileRef);
     }
 
-    public boolean hasFiles(BaseHome baseHome)
+    public boolean hasFiles(BaseHome baseHome, Props props)
     {
         for (String ref : getFiles())
         {
-            FileArg farg = new FileArg(this,ref);
+            FileArg farg = new FileArg(this,props.expand(ref));
             Path refPath = baseHome.getBasePath(farg.location);
             if (!Files.exists(refPath))
             {
@@ -404,9 +294,12 @@
                     // blank lines and comments are valid for ini-template section
                     if ((line.length() == 0) || line.startsWith("#"))
                     {
+                        // Remember ini comments and whitespace (empty lines)
+                        // for the [ini-template] section
                         if ("INI-TEMPLATE".equals(sectionType))
                         {
-                            defaultConfig.add(line);
+                            iniTemplate.add(line);
+                            hasIniTemplate = true;
                         }
                     }
                     else
@@ -417,16 +310,20 @@
                                 // ignore (this would be entries before first section)
                                 break;
                             case "DEPEND":
-                                parentNames.add(line);
+                                addParentName(line);
                                 break;
                             case "FILES":
                                 files.add(line);
                                 break;
-                            case "DEFAULTS":
-                            case "INI-TEMPLATE":
+                            case "DEFAULTS": // old name introduced in 9.2.x
+                            case "INI": // new name for 9.3+
                                 defaultConfig.add(line);
                                 hasDefaultConfig = true;
                                 break;
+                            case "INI-TEMPLATE":
+                                iniTemplate.add(line);
+                                hasIniTemplate = true;
+                                break;
                             case "LIB":
                                 libs.add(line);
                                 break;
@@ -435,14 +332,21 @@
                                 license.add(line);
                                 break;
                             case "NAME":
-                                logicalName = line;
+                                setName(line);
                                 break;
                             case "OPTIONAL":
-                                optionalParentNames.add(line);
+                                addOptionalParentName(line);
                                 break;
                             case "EXEC":
                                 jvmArgs.add(line);
                                 break;
+                            case "VERSION":
+                                if (version != null)
+                                {
+                                    throw new IOException("[version] already specified");
+                                }
+                                version = new Version(line);
+                                break;
                             case "XML":
                                 xmls.add(line);
                                 break;
@@ -453,40 +357,35 @@
                 }
             }
         }
-    }
-
-    public void setDepth(int depth)
-    {
-        this.depth = depth;
+        
+        if (version == null)
+        {
+            version = new Version(VERSION_UNSPECIFIED);
+        }
     }
 
     public void setEnabled(boolean enabled)
     {
-        this.enabled = enabled;
+        throw new RuntimeException("Don't enable directly");
     }
-
-    public void setParentNames(Set<String> parents)
+    
+    public void setSkipFilesValidation(boolean skipFilesValidation)
     {
-        this.parentNames.clear();
-        this.parentEdges.clear();
-        if (parents != null)
-        {
-            this.parentNames.addAll(parents);
-        }
+        this.skipFilesValidation = skipFilesValidation;
     }
-
+    
     @Override
     public String toString()
     {
         StringBuilder str = new StringBuilder();
-        str.append("Module[").append(logicalName);
-        if (!logicalName.equals(fileRef))
+        str.append("Module[").append(getName());
+        if (isDynamic())
         {
             str.append(",file=").append(fileRef);
         }
-        if (enabled)
+        if (isSelected())
         {
-            str.append(",enabled");
+            str.append(",selected");
         }
         str.append(']');
         return str.toString();
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java b/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java
index 1fcfd58..68e1da2 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java
@@ -28,6 +28,10 @@
 import java.util.Collection;
 import java.util.List;
 
+import org.eclipse.jetty.start.graph.Graph;
+import org.eclipse.jetty.start.graph.Node;
+import org.eclipse.jetty.start.graph.Selection;
+
 /**
  * Generate a graphviz dot graph of the modules found
  */
@@ -103,7 +107,7 @@
             out.println("    ssize = \"20,40\"");
             out.println("  ];");
 
-            List<Module> enabled = modules.resolveEnabled();
+            List<Module> enabled = modules.getSelected();
 
             // Module Nodes
             writeModules(out,modules,enabled);
@@ -164,7 +168,7 @@
     private void writeModuleNode(PrintWriter out, Module module, boolean resolved)
     {
         String color = colorModuleBg;
-        if (module.isEnabled())
+        if (module.isSelected())
         {
             // specifically enabled by config
             color = colorEnabledBg;
@@ -179,12 +183,12 @@
         out.printf("<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"2\">%n");
         out.printf("  <TR><TD ALIGN=\"LEFT\"><B>%s</B></TD></TR>%n",module.getName());
 
-        if (module.isEnabled())
+        if (module.isSelected())
         {
             writeModuleDetailHeader(out,"ENABLED");
-            for (String source : module.getSources())
+            for (Selection selection : module.getSelections())
             {
-                writeModuleDetailLine(out,"via: " + source);
+                writeModuleDetailLine(out,"via: " + selection);
             }
         }
         else if (resolved)
@@ -212,9 +216,9 @@
             }
         }
 
-        if (!module.getDefaultConfig().isEmpty())
+        if (!module.getIniTemplate().isEmpty())
         {
-            List<String> inis = module.getDefaultConfig();
+            List<String> inis = module.getIniTemplate();
             writeModuleDetailHeader(out,"INI Template",inis.size());
         }
 
@@ -247,11 +251,11 @@
         }
     }
 
-    private void writeRelationships(PrintWriter out, Modules modules, List<Module> enabled)
+    private void writeRelationships(PrintWriter out, Graph<Module> modules, List<Module> enabled)
     {
         for (Module module : modules)
         {
-            for (Module parent : module.getParentEdges())
+            for (Node<?> parent : module.getParentEdges())
             {
                 out.printf("    \"%s\" -> \"%s\";%n",module.getName(),parent.getName());
             }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java
index 7281196..a4eb217 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java
@@ -18,409 +18,133 @@
 
 package org.eclipse.jetty.start;
 
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.Arrays;
 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.Set;
-import java.util.Stack;
-import java.util.regex.Pattern;
+
+import org.eclipse.jetty.start.graph.Graph;
+import org.eclipse.jetty.start.graph.GraphException;
+import org.eclipse.jetty.start.graph.OnlyTransitivePredicate;
+import org.eclipse.jetty.start.graph.Selection;
 
 /**
  * Access for all modules declared, as well as what is enabled.
  */
-public class Modules implements Iterable<Module>
+public class Modules extends Graph<Module>
 {
     private final BaseHome baseHome;
     private final StartArgs args;
-    
-    private Map<String, Module> modules = new HashMap<>();
-    /*
-     * modules that may appear in the resolved graph but are undefined in the module system
-     * 
-     * ex: modules/npn/npn-1.7.0_01.mod (property expansion resolves to non-existent file)
-     */
-    private Set<String> missingModules = new HashSet<String>();
 
-    private int maxDepth = -1;
-    
     public Modules(BaseHome basehome, StartArgs args)
     {
         this.baseHome = basehome;
         this.args = args;
-    }
-
-    private Set<String> asNameSet(Set<Module> moduleSet)
-    {
-        Set<String> ret = new HashSet<>();
-        for (Module module : moduleSet)
-        {
-            ret.add(module.getName());
-        }
-        return ret;
-    }
-
-    private void assertNoCycle(Module module, Stack<String> refs)
-    {
-        for (Module parent : module.getParentEdges())
-        {
-            if (refs.contains(parent.getName()))
-            {
-                // Cycle detected.
-                StringBuilder err = new StringBuilder();
-                err.append("A cyclic reference in the modules has been detected: ");
-                for (int i = 0; i < refs.size(); i++)
-                {
-                    if (i > 0)
-                    {
-                        err.append(" -> ");
-                    }
-                    err.append(refs.get(i));
-                }
-                err.append(" -> ").append(parent.getName());
-                throw new IllegalStateException(err.toString());
-            }
-
-            refs.push(parent.getName());
-            assertNoCycle(parent,refs);
-            refs.pop();
-        }
-    }
-
-    private void bfsCalculateDepth(final Module module, final int depthNow)
-    {
-        int depth = depthNow + 1;
-
-        // Set depth on every child first
-        for (Module child : module.getChildEdges())
-        {
-            child.setDepth(Math.max(depth,child.getDepth()));
-            this.maxDepth = Math.max(this.maxDepth,child.getDepth());
-        }
-
-        // Dive down
-        for (Module child : module.getChildEdges())
-        {
-            bfsCalculateDepth(child,depth);
-        }
-    }
-
-    /**
-     * Using the provided dependencies, build the module graph
-     */
-    public void buildGraph() throws FileNotFoundException, IOException
-    {
-        normalizeDependencies();
+        this.setSelectionTerm("enable");
+        this.setNodeTerm("module");
         
-        // Connect edges
-        for (Module module : modules.values())
+        String java_version = System.getProperty("java.version");
+        if (java_version!=null)
         {
-            for (String parentName : module.getParentNames())
-            {
-                Module parent = get(parentName);
-
-                if (parent == null)
-                {
-                    if (Props.hasPropertyKey(parentName))
-                    {
-                        StartLog.debug("Module property not expandable (yet) [%s]",parentName);
-                    }
-                    else
-                    {
-                        StartLog.warn("Module not found [%s]",parentName);
-                    }
-                }
-                else
-                {
-                    module.addParentEdge(parent);
-                    parent.addChildEdge(module);
-                }
-            }
-
-            for (String optionalParentName : module.getOptionalParentNames())
-            {
-                Module optional = get(optionalParentName);
-                if (optional == null)
-                {
-                    StartLog.debug("Optional module not found [%s]",optionalParentName);
-                }
-                else if (optional.isEnabled())
-                {
-                    module.addParentEdge(optional);
-                    optional.addChildEdge(module);
-                }
-            }
-        }
-
-        // Verify there is no cyclic references
-        Stack<String> refs = new Stack<>();
-        for (Module module : modules.values())
-        {
-            refs.push(module.getName());
-            assertNoCycle(module,refs);
-            refs.pop();
-        }
-
-        // Calculate depth of all modules for sorting later
-        for (Module module : modules.values())
-        {
-            if (module.getParentEdges().isEmpty())
-            {
-                bfsCalculateDepth(module,0);
-            }
-        }
-    }
-
-    public void clearMissing()
-    {
-        missingModules.clear();
-    }
-    
-    public Integer count()
-    {
-        return modules.size();
+            args.setProperty("java.version",java_version,"<internal>",false);
+        }        
     }
 
     public void dump()
     {
         List<Module> ordered = new ArrayList<>();
-        ordered.addAll(modules.values());
+        ordered.addAll(getNodes());
         Collections.sort(ordered,new Module.NameComparator());
 
-        List<Module> active = resolveEnabled();
+        List<Module> active = getSelected();
 
         for (Module module : ordered)
         {
             boolean activated = active.contains(module);
-            boolean enabled = module.isEnabled();
-            boolean transitive = activated && !enabled;
+            boolean selected = module.isSelected();
+            boolean transitive = selected && module.matches(OnlyTransitivePredicate.INSTANCE);
 
-            char status = '-';
-            if (enabled)
+            String status = "[ ]";
+            if (transitive)
             {
-                status = '*';
+                status = "[t]";
             }
-            else if (transitive)
+            else if (selected)
             {
-                status = '+';
+                status = "[x]";
             }
 
             System.out.printf("%n %s Module: %s%n",status,module.getName());
             if (!module.getName().equals(module.getFilesystemRef()))
             {
-                System.out.printf("      Ref: %s%n",module.getFilesystemRef());
+                System.out.printf("        Ref: %s%n",module.getFilesystemRef());
             }
             for (String parent : module.getParentNames())
             {
-                System.out.printf("   Depend: %s%n",parent);
+                System.out.printf("     Depend: %s%n",parent);
             }
             for (String lib : module.getLibs())
             {
-                System.out.printf("      LIB: %s%n",lib);
+                System.out.printf("        LIB: %s%n",lib);
             }
             for (String xml : module.getXmls())
             {
-                System.out.printf("      XML: %s%n",xml);
+                System.out.printf("        XML: %s%n",xml);
             }
             if (StartLog.isDebugEnabled())
             {
-                System.out.printf("    depth: %d%n",module.getDepth());
+                System.out.printf("      depth: %d%n",module.getDepth());
             }
             if (activated)
             {
-                for (String source : module.getSources())
+                for (Selection selection : module.getSelections())
                 {
-                    System.out.printf("  Enabled: <via> %s%n",source);
-                }
-                if (transitive)
-                {
-                    System.out.printf("  Enabled: <via transitive reference>%n");
+                    System.out.printf("    Enabled: <via> %s%n",selection);
                 }
             }
             else
             {
-                System.out.printf("  Enabled: <not enabled in this configuration>%n");
+                System.out.printf("    Enabled: <not enabled in this configuration>%n");
             }
         }
     }
 
-    public void dumpEnabledTree()
-    {
-        List<Module> ordered = new ArrayList<>();
-        ordered.addAll(modules.values());
-        Collections.sort(ordered,new Module.DepthComparator());
-
-        List<Module> active = resolveEnabled();
-
-        for (Module module : ordered)
-        {
-            if (active.contains(module))
-            {
-                // Show module name
-                String indent = toIndent(module.getDepth());
-                System.out.printf("%s + Module: %s [%s]%n",indent,module.getName(),module.isEnabled()?"enabled":"transitive");
-            }
-        }
-    }
-
-    public void enable(String name) throws IOException
-    {
-        List<String> empty = Collections.emptyList();
-        enable(name,empty);
-    }
-    
-    public void enable(String name, List<String> sources) throws IOException
-    {
-        if (name.contains("*"))
-        {
-            // A regex!
-            Pattern pat = Pattern.compile(name);
-            List<Module> matching = new ArrayList<>();
-            do
-            {
-                matching.clear();
-                
-                // find matching entries that are not enabled
-                for (Map.Entry<String, Module> entry : modules.entrySet())
-                {
-                    if (pat.matcher(entry.getKey()).matches())
-                    {
-                        if (!entry.getValue().isEnabled())
-                        {
-                            matching.add(entry.getValue());
-                        }
-                    }
-                }
-                
-                // enable them
-                for (Module module : matching)
-                {
-                    enableModule(module,sources);
-                }
-            }
-            while (!matching.isEmpty());
-        }
-        else
-        {
-            Module module = modules.get(name);
-            if (module == null)
-            {
-                System.err.printf("WARNING: Cannot enable requested module [%s]: not a valid module name.%n",name);
-                return;
-            }
-            enableModule(module,sources);
-        }
-    }
-
-    private void enableModule(Module module, List<String> sources) throws IOException
-    {
-        String via = "<transitive>";
-
-        // Always add the sources
-        if (sources != null)
-        {
-            module.addSources(sources);
-            via = Main.join(sources, ", ");
-        }
-        
-        // If already enabled, nothing else to do
-        if (module.isEnabled())
-        {
-            StartLog.debug("Enabled module: %s (via %s)",module.getName(),via);
-            return;
-        }
-        
-        StartLog.debug("Enabling module: %s (via %s)",module.getName(),via);
-        module.setEnabled(true);
-        args.parseModule(module);
-        module.expandProperties(args.getProperties());
-        
-        // enable any parents that haven't been enabled (yet)
-        Set<String> parentNames = new HashSet<>();
-        parentNames.addAll(module.getParentNames());
-        for(String name: parentNames)
-        {
-            StartLog.debug("Enable parent '%s' of module: %s",name,module.getName());
-            Module parent = modules.get(name);
-            if (parent == null)
-            {
-                // parent module doesn't exist, yet
-                Path file = baseHome.getPath("modules/" + name + ".mod");
-                if (FS.canReadFile(file))
-                {
-                    parent = registerModule(file);
-                    parent.expandProperties(args.getProperties());
-                    updateParentReferencesTo(parent);
-                }
-                else
-                {
-                    if (!Props.hasPropertyKey(name))
-                    {
-                        StartLog.debug("Missing module definition: [ Mod: %s | File: %s ]",name,file);
-                        missingModules.add(name);
-                    }
-                }
-            }
-            if (parent != null)
-            {
-                enableModule(parent,null);
-            }
-        }
-    }
-    
-    private void findChildren(Module module, Set<Module> ret)
-    {
-        ret.add(module);
-        for (Module child : module.getChildEdges())
-        {
-            ret.add(child);
-        }
-    }
-
-    private void findParents(Module module, Map<String, Module> ret)
-    {
-        ret.put(module.getName(),module);
-        for (Module parent : module.getParentEdges())
-        {
-            ret.put(parent.getName(),parent);
-            findParents(parent,ret);
-        }
-    }
-
-    public Module get(String name)
-    {
-        return modules.get(name);
-    }
-
-    public int getMaxDepth()
-    {
-        return maxDepth;
-    }
-
-    public Set<Module> getModulesAtDepth(int depth)
-    {
-        Set<Module> ret = new HashSet<>();
-        for (Module module : modules.values())
-        {
-            if (module.getDepth() == depth)
-            {
-                ret.add(module);
-            }
-        }
-        return ret;
-    }
-
     @Override
-    public Iterator<Module> iterator()
+    public Module resolveNode(String name)
     {
-        return modules.values().iterator();
+        String expandedName = args.getProperties().expand(name);
+
+        if (Props.hasPropertyKey(expandedName))
+        {
+            StartLog.debug("Not yet able to expand property in: %s",name);
+            return null;
+        }
+
+        Path file = baseHome.getPath("modules/" + expandedName + ".mod");
+        if (FS.canReadFile(file))
+        {
+            Module parent = registerModule(file);
+            parent.expandProperties(args.getProperties());
+            updateParentReferencesTo(parent);
+            return parent;
+        }
+        else
+        {
+            if (!Props.hasPropertyKey(name))
+            {
+                StartLog.debug("Missing module definition: [ Mod: %s | File: %s ]",name,file);
+            }
+            return null;
+        }
+    }
+    
+    @Override
+    public void onNodeSelected(Module module)
+    {
+        StartLog.debug("on node selected: [%s] (%s.mod)",module.getName(),module.getFilesystemRef());
+        args.parseModule(module);
+        module.expandProperties(args.getProperties());
     }
 
     public List<String> normalizeLibs(List<Module> active)
@@ -455,30 +179,6 @@
         return xmls;
     }
 
-    public Module register(Module module)
-    {
-        modules.put(module.getName(),module);
-        return module;
-    }
-
-    public void registerParentsIfMissing(Module module) throws IOException
-    {
-        Set<String> parents = new HashSet<>(module.getParentNames());
-        for (String name : parents)
-        {
-            if (!modules.containsKey(name))
-            {
-                Path file = baseHome.getPath("modules/" + name + ".mod");
-                if (FS.canReadFile(file))
-                {
-                    Module parent = registerModule(file);
-                    updateParentReferencesTo(parent);
-                    registerParentsIfMissing(parent);
-                }
-            }
-        }
-    }
-    
     public void registerAll() throws IOException
     {
         for (Path path : baseHome.getPaths("modules/*.mod"))
@@ -486,140 +186,29 @@
             registerModule(path);
         }
     }
-    
-    // load missing post-expanded dependent modules
-    private void normalizeDependencies() throws FileNotFoundException, IOException
-    {
-        Set<String> expandedModules = new HashSet<>();
-        boolean done = false;
-        while (!done)
-        {
-            done = true;
-            Set<String> missingParents = new HashSet<>();
 
-            for (Module m : modules.values())
-            {
-                for (String parent : m.getParentNames())
-                {
-                    String expanded = args.getProperties().expand(parent);
-                    if (modules.containsKey(expanded) || missingModules.contains(parent) || expandedModules.contains(parent))
-                    {
-                        continue; // found. skip it.
-                    }
-                    done = false;
-                    StartLog.debug("Missing parent module %s == %s for %s",parent,expanded,m);
-                    missingParents.add(parent);
-                }
-            }
-
-            for (String missingParent : missingParents)
-            {
-                String expanded = args.getProperties().expand(missingParent);
-                Path file = baseHome.getPath("modules/" + expanded + ".mod");
-                if (FS.canReadFile(file))
-                {
-                    Module module = registerModule(file);
-                    updateParentReferencesTo(module);
-                    if (!expanded.equals(missingParent))
-                    {
-                        expandedModules.add(missingParent);
-                    }
-                }
-                else
-                {
-                    if (Props.hasPropertyKey(expanded))
-                    {
-                        StartLog.debug("Module property not expandable (yet) [%s]",expanded);
-                        expandedModules.add(missingParent);
-                    }
-                    else
-                    {
-                        StartLog.debug("Missing module definition: %s expanded to %s",missingParent,expanded);
-                        missingModules.add(missingParent);
-                    }
-                }
-            }
-        }
-    }
-
-    private Module registerModule(Path file) throws FileNotFoundException, IOException
+    private Module registerModule(Path file)
     {
         if (!FS.canReadFile(file))
         {
-            throw new IOException("Cannot read file: " + file);
+            throw new GraphException("Cannot read file: " + file);
         }
-        StartLog.debug("Registering Module: %s",baseHome.toShortForm(file));
-        Module module = new Module(baseHome,file);
-        return register(module);
-    }
-
-    public Set<String> resolveChildModulesOf(String moduleName)
-    {
-        Set<Module> ret = new HashSet<>();
-        Module module = get(moduleName);
-        findChildren(module,ret);
-        return asNameSet(ret);
+        String shortName = baseHome.toShortForm(file);
+        try
+        {
+            StartLog.debug("Registering Module: %s",shortName);
+            Module module = new Module(baseHome,file);
+            return register(module);
+        }
+        catch (Throwable t)
+        {
+            throw new GraphException("Unable to register module: " + shortName,t);
+        }
     }
 
     /**
-     * Resolve the execution order of the enabled modules, and all dependant modules, based on depth first transitive reduction.
-     * 
-     * @return the list of active modules (plus dependant modules), in execution order.
-     */
-    public List<Module> resolveEnabled()
-    {
-        Map<String, Module> active = new HashMap<String, Module>();
-
-        for (Module module : modules.values())
-        {
-            if (module.isEnabled())
-            {
-                findParents(module,active);
-            }
-        }
-
-        /*
-         * check against the missing modules
-         * 
-         * Ex: npn should match anything under npn/
-         */
-        for (String missing : missingModules)
-        {
-            for (String activeModule : active.keySet())
-            {
-                if (missing.startsWith(activeModule))
-                {
-                    StartLog.warn("** Unable to continue, required dependency missing. [%s]",missing);
-                    StartLog.warn("** As configured, Jetty is unable to start due to a missing enabled module dependency.");
-                    StartLog.warn("** This may be due to a transitive dependency akin to spdy on npn, which resolves based on the JDK in use.");
-                    throw new UsageException(UsageException.ERR_BAD_ARG, "Missing referenced dependency: " + missing);
-                }
-            }
-        }
-
-        List<Module> ordered = new ArrayList<>();
-        ordered.addAll(active.values());
-        Collections.sort(ordered,new Module.DepthComparator());
-        return ordered;
-    }
-
-    public Set<String> resolveParentModulesOf(String moduleName)
-    {
-        Map<String, Module> ret = new HashMap<>();
-        Module module = get(moduleName);
-        findParents(module,ret);
-        return ret.keySet();
-    }
-
-    private String toIndent(int depth)
-    {
-        char indent[] = new char[depth * 2];
-        Arrays.fill(indent,' ');
-        return new String(indent);
-    }
-
-    /**
-     * Modules can have a different logical name than to their filesystem reference. This updates existing references to the filesystem form to use the logical
+     * Modules can have a different logical name than to their filesystem reference. This updates existing references to
+     * the filesystem form to use the logical
      * name form.
      * 
      * @param module
@@ -633,9 +222,9 @@
             return;
         }
 
-        for (Module m : modules.values())
+        for (Module m : getNodes())
         {
-            Set<String> resolvedParents = new HashSet<>();
+            List<String> resolvedParents = new ArrayList<>();
             for (String parent : m.getParentNames())
             {
                 if (parent.equals(module.getFilesystemRef()))
@@ -658,10 +247,10 @@
     {
         StringBuilder str = new StringBuilder();
         str.append("Modules[");
-        str.append("count=").append(modules.size());
+        str.append("count=").append(count());
         str.append(",<");
         boolean delim = false;
-        for (String name : modules.keySet())
+        for (String name : getNodeNames())
         {
             if (delim)
             {
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Props.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Props.java
index 9d4a1d6..27471a4 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Props.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Props.java
@@ -42,6 +42,8 @@
  */
 public final class Props implements Iterable<Prop>
 {
+    private static final Pattern __propertyPattern = Pattern.compile("(?<=[^$]|^)\\$\\{([^}]*)\\}");
+    
     public static class Prop
     {
         public String key;
@@ -129,6 +131,7 @@
      * If arg is not a property, ignore it.
      * @param arg the argument to parse for a potential property
      * @param source the source for this argument (to track origin of property from)
+     * @return true if the property was added, false if the property wasn't added
      */
     public boolean addPossibleProperty(String arg, String source)
     {
@@ -199,8 +202,7 @@
             return str;
         }
 
-        Pattern pat = Pattern.compile("(?<=[^$]|^)(\\$\\{[^}]*\\})");
-        Matcher mat = pat.matcher(str);
+        Matcher mat = __propertyPattern.matcher(str);
         StringBuilder expanded = new StringBuilder();
         int offset = 0;
         String property;
@@ -208,7 +210,7 @@
 
         while (mat.find(offset))
         {
-            property = cleanReference(mat.group(1));
+            property = mat.group(1);
 
             // Loop detection
             if (seenStack.contains(property))
@@ -226,13 +228,13 @@
             }
 
             // find property name
-            expanded.append(str.subSequence(offset,mat.start(1)));
+            expanded.append(str.subSequence(offset,mat.start()));
             // get property value
             value = getString(property);
             if (value == null)
             {
                 StartLog.trace("Unable to expand: %s",property);
-                expanded.append(mat.group(1));
+                expanded.append(mat.group());
             }
             else
             {
@@ -243,7 +245,7 @@
                 expanded.append(value);
             }
             // update offset
-            offset = mat.end(1);
+            offset = mat.end();
         }
 
         // leftover
@@ -318,7 +320,7 @@
 
     public static boolean hasPropertyKey(String name)
     {
-        return Pattern.compile("(?<=[^$]|^)(\\$\\{[^}]*\\})").matcher(name).find();
+        return __propertyPattern.matcher(name).find();
     }
 
     @Override
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/README.TXT b/jetty-start/src/main/java/org/eclipse/jetty/start/README.TXT
deleted file mode 100644
index fc569bc..0000000
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/README.TXT
+++ /dev/null
@@ -1,48 +0,0 @@
-Jetty start
------------
-
-The run directory is either the top-level of a distribution
-or jetty-distribution/target/distribution directory when built from
-source.
-
-Jetty start.jar provides a cross platform replacement for startup scripts.
-It makes use of executable JAR that builds the classpath and then executes
-jetty.
-
-To run with the demo:
-
-  java -jar start.jar --enable=demo
-  java -jar start.jar
-
-To run with the default modules:
-
-  java -jar start.jar
-
-The default options may be specified in the start.ini file, or if
-that is not present, they are defined in the start.config file that
-is within the start.jar.
-
-To run with specific configuration file(s)
-
-  java -jar start.jar etc/jetty.xml
-
-To see the available options
-
-  java -jar start.jar --help
-
-To run with JSP support (if available)
-
-  java -jar start.jar --module=jsp
-
-To run with JMX support
-
-  java -jar start.jar --module=jmx
-
-To run with JSP & JMX support
-
-    java -jar start.jar --module=jsp,jmx
-
-Note that JSP requires the jasper jars to be within $JETTY/lib/jsp  These 
-are currently not distributed with the eclipse release and must be
-obtained from a jetty-hightide release from codehaus.
-
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
index 7445fa6..589a29f 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
@@ -18,11 +18,12 @@
 
 package org.eclipse.jetty.start;
 
-import static org.eclipse.jetty.start.UsageException.*;
-
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -31,6 +32,7 @@
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
+import java.util.Properties;
 import java.util.Set;
 import java.util.StringTokenizer;
 
@@ -39,6 +41,8 @@
 import org.eclipse.jetty.start.config.ConfigSources;
 import org.eclipse.jetty.start.config.DirConfigSource;
 
+import static org.eclipse.jetty.start.UsageException.ERR_BAD_ARG;
+
 /**
  * The Arguments required to start Jetty.
  */
@@ -48,40 +52,87 @@
 
     static
     {
+        // Use command line versions
         String ver = System.getProperty("jetty.version",null);
+        String tag = System.getProperty("jetty.tag.version","master");
 
+        // Use META-INF/MANIFEST.MF versions
         if (ver == null)
         {
             Package pkg = StartArgs.class.getPackage();
             if ((pkg != null) && "Eclipse.org - Jetty".equals(pkg.getImplementationVendor()) && (pkg.getImplementationVersion() != null))
             {
                 ver = pkg.getImplementationVersion();
+                if (tag == null)
+                {
+                    tag = "jetty-" + ver;
+                }
             }
         }
 
+        // Use jetty-version.properties values
         if (ver == null)
         {
-            ver = "TEST";
+            URL url = Thread.currentThread().getContextClassLoader().getResource("jetty-version.properties");
+            if (url != null)
+            {
+                try (InputStream in = url.openStream())
+                {
+                    Properties props = new Properties();
+                    props.load(in);
+                    ver = props.getProperty("jetty.version");
+                }
+                catch (IOException ignore)
+                {
+                    StartLog.debug(ignore);
+                }
+            }
+        }
+
+        // Default values
+        if (ver == null)
+        {
+            ver = "0.0";
+            if (tag == null)
+            {
+                tag = "master";
+            }
+        }
+
+        // Set Tag Defaults
+        if (tag == null || tag.contains("-SNAPSHOT"))
+        {
+            tag = "master";
         }
 
         VERSION = ver;
         System.setProperty("jetty.version",VERSION);
+        System.setProperty("jetty.tag.version",tag);
     }
 
     private static final String SERVER_MAIN = "org.eclipse.jetty.xml.XmlConfiguration";
 
     /** List of enabled modules */
     private Set<String> modules = new HashSet<>();
+
+    /** List of modules to skip [files] section validation */
+    private Set<String> skipFileValidationModules = new HashSet<>();
+
     /** Map of enabled modules to the source of where that activation occurred */
     private Map<String, List<String>> sources = new HashMap<>();
+
     /** Map of properties to where that property was declared */
     private Map<String, String> propertySource = new HashMap<>();
+
     /** List of all active [files] sections from enabled modules */
     private List<FileArg> files = new ArrayList<>();
+
     /** List of all active [lib] sections from enabled modules */
     private Classpath classpath;
+
     /** List of all active [xml] sections from enabled modules */
     private List<Path> xmls = new ArrayList<>();
+
     /** JVM arguments, found via commmand line and in all active [exec] sections from enabled modules */
     private List<String> jvmArgs = new ArrayList<>();
 
@@ -90,7 +141,7 @@
 
     /** List of all property references found directly on command line or start.ini */
     private List<String> propertyFileRefs = new ArrayList<>();
-    
+
     /** List of all property files */
     private List<Path> propertyFiles = new ArrayList<>();
 
@@ -112,7 +163,12 @@
     private Modules allModules;
     /** Should the server be run? */
     private boolean run = true;
+
+    /** Download related args */
     private boolean download = false;
+    private boolean licenseCheckRequired = false;
+    private boolean testingMode = false;
+
     private boolean help = false;
     private boolean stopCommand = false;
     private boolean listModules = false;
@@ -122,8 +178,8 @@
     private boolean dryRun = false;
 
     private boolean exec = false;
+    private String exec_properties;
     private boolean approveAllLicenses = false;
-    private boolean testingMode = false;
 
     public StartArgs()
     {
@@ -132,7 +188,13 @@
 
     private void addFile(Module module, String uriLocation)
     {
-        FileArg arg = new FileArg(module, uriLocation);
+        if (module.isSkipFilesValidation())
+        {
+            StartLog.debug("Not validating %s [files] for %s",module,uriLocation);
+            return;
+        }
+
+        FileArg arg = new FileArg(module,properties.expand(uriLocation));
         if (!files.contains(arg))
         {
             files.add(arg);
@@ -157,7 +219,7 @@
             xmls.add(xmlfile);
         }
     }
-    
+
     private void addUniquePropertyFile(String propertyFileRef, Path propertyFile) throws IOException
     {
         if (!FS.canReadFile(propertyFile))
@@ -211,9 +273,10 @@
         System.out.println("Jetty Environment:");
         System.out.println("-----------------");
         dumpProperty("jetty.version");
+        dumpProperty("jetty.tag.version");
         dumpProperty("jetty.home");
         dumpProperty("jetty.base");
-        
+
         // Jetty Configuration Environment
         System.out.println();
         System.out.println("Config Search Order:");
@@ -231,7 +294,7 @@
             }
             System.out.println();
         }
-        
+
         // Jetty Se
         System.out.println();
     }
@@ -344,8 +407,9 @@
     }
 
     /**
-     * Ensure that the System Properties are set (if defined as a System property, or start.config property, or start.ini property)
-     * 
+     * Ensure that the System Properties are set (if defined as a System property, or start.config property, or
+     * start.ini property)
+     *
      * @param key
      *            the key to be sure of
      */
@@ -371,9 +435,11 @@
 
     /**
      * Expand any command line added <code>--lib</code> lib references.
-     * 
+     *
      * @param baseHome
+     *            the base home in use
      * @throws IOException
+     *             if unable to expand the libraries
      */
     public void expandLibs(BaseHome baseHome) throws IOException
     {
@@ -383,10 +449,10 @@
             StartLog.debug("rawlibref = " + rawlibref);
             String libref = properties.expand(rawlibref);
             StartLog.debug("expanded = " + libref);
-            
+
             // perform path escaping (needed by windows)
             libref = libref.replaceAll("\\\\([^\\\\])","\\\\\\\\$1");
-            
+
             for (Path libpath : baseHome.getPaths(libref))
             {
                 classpath.addComponent(libpath.toFile());
@@ -396,10 +462,13 @@
 
     /**
      * Build up the Classpath and XML file references based on enabled Module list.
-     * 
+     *
      * @param baseHome
+     *            the base home in use
      * @param activeModules
+     *            the active (selected) modules
      * @throws IOException
+     *             if unable to expand the modules
      */
     public void expandModules(BaseHome baseHome, List<Module> activeModules) throws IOException
     {
@@ -429,6 +498,7 @@
             for (String xmlRef : module.getXmls())
             {
                 // Straight Reference
+                xmlRef = properties.expand(xmlRef);
                 Path xmlfile = baseHome.getPath(xmlRef);
                 addUniqueXmlFile(xmlRef,xmlfile);
             }
@@ -481,6 +551,11 @@
     {
         CommandLineBuilder cmd = new CommandLineBuilder();
 
+        // Special Stop/Shutdown properties
+        ensureSystemPropertySet("STOP.PORT");
+        ensureSystemPropertySet("STOP.KEY");
+        ensureSystemPropertySet("STOP.WAIT");
+
         if (addJavaInit)
         {
             cmd.addRawArg(CommandLineBuilder.findJavaBin());
@@ -506,33 +581,36 @@
             cmd.addRawArg(getMainClassname());
         }
 
-        // Special Stop/Shutdown properties
-        ensureSystemPropertySet("STOP.PORT");
-        ensureSystemPropertySet("STOP.KEY");
-        ensureSystemPropertySet("STOP.WAIT");
-
+       
         // pass properties as args or as a file
-        if (dryRun || isExec())
+        if (dryRun && exec_properties==null)
         {
             for (Prop p : properties)
-                cmd.addRawArg(CommandLineBuilder.quote(p.key)+"="+CommandLineBuilder.quote(p.value));
+                cmd.addRawArg(CommandLineBuilder.quote(p.key) + "=" + CommandLineBuilder.quote(p.value));
         }
         else if (properties.size() > 0)
         {
-            File prop_file = File.createTempFile("start",".properties");
-            prop_file.deleteOnExit();
-            try (FileOutputStream out = new FileOutputStream(prop_file))
+            Path prop_path;
+            if (exec_properties==null)
+            {
+                prop_path=Files.createTempFile("start_", ".properties");
+                prop_path.toFile().deleteOnExit();
+            }
+            else
+                prop_path=new File(exec_properties).toPath();
+                
+            try (OutputStream out = Files.newOutputStream(prop_path))
             {
                 properties.store(out,"start.jar properties");
             }
-            cmd.addRawArg(prop_file.getAbsolutePath());
+            cmd.addRawArg(prop_path.toAbsolutePath().toString());
         }
 
         for (Path xml : xmls)
         {
             cmd.addRawArg(xml.toAbsolutePath().toString());
         }
-        
+
         for (Path propertyFile : propertyFiles)
         {
             cmd.addRawArg(propertyFile.toAbsolutePath().toString());
@@ -547,6 +625,46 @@
         return System.getProperty("main.class",mainclass);
     }
 
+    public Path getMavenLocalRepoDir()
+    {
+        // Try property first
+        String localRepo = getProperties().getString("maven.local.repo");
+
+        if (Utils.isBlank(localRepo))
+        {
+            // Try jetty specific env variable
+            localRepo = System.getenv("JETTY_MAVEN_LOCAL_REPO");
+        }
+
+        if (Utils.isBlank(localRepo))
+        {
+            // Try generic env variable
+            localRepo = System.getenv("MAVEN_LOCAL_REPO");
+        }
+
+        // TODO: load & use $HOME/.m2/settings.xml ?
+        // TODO: possibly use Eclipse Aether to manage it ?
+        // TODO: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=449511
+
+        // Still blank? then its not specified
+        if (Utils.isBlank(localRepo))
+        {
+            return null;
+        }
+
+        Path localRepoDir = new File(localRepo).toPath();
+        localRepoDir = localRepoDir.normalize().toAbsolutePath();
+        if (Files.exists(localRepoDir) && Files.isDirectory(localRepoDir))
+        {
+            return localRepoDir;
+        }
+
+        StartLog.warn("Not a valid maven local repository directory: %s",localRepoDir);
+
+        // Not a valid repository directory, skip it
+        return null;
+    }
+
     public String getModuleGraphFilename()
     {
         return moduleGraphFilename;
@@ -557,6 +675,11 @@
         return properties;
     }
 
+    public Set<String> getSkipFileValidationModules()
+    {
+        return skipFileValidationModules;
+    }
+
     public List<String> getSources(String module)
     {
         return sources.get(module);
@@ -606,7 +729,12 @@
     {
         return exec;
     }
-    
+
+    public boolean isLicenseCheckRequired()
+    {
+        return licenseCheckRequired;
+    }
+
     public boolean isNormalMainClass()
     {
         return SERVER_MAIN.equals(getMainClassname());
@@ -641,7 +769,7 @@
     {
         return stopCommand;
     }
-    
+
     public boolean isTestingModeEnabled()
     {
         return testingMode;
@@ -672,10 +800,13 @@
 
     /**
      * Parse a single line of argument.
-     * 
-     * @param rawarg the raw argument to parse
-     * @param source the origin of this line of argument
-     * @param replaceProps true if properties in this parse replace previous ones, false to not replace.
+     *
+     * @param rawarg
+     *            the raw argument to parse
+     * @param source
+     *            the origin of this line of argument
+     * @param replaceProps
+     *            true if properties in this parse replace previous ones, false to not replace.
      */
     private void parse(final String rawarg, String source, boolean replaceProps)
     {
@@ -683,7 +814,7 @@
         {
             return;
         }
-        
+
         StartLog.debug("parse(\"%s\", \"%s\", %b)",rawarg,source,replaceProps);
 
         final String arg = rawarg.trim();
@@ -710,7 +841,7 @@
             // valid, but handled in StartLog instead
             return;
         }
-        
+
         if ("--testing-mode".equals(arg))
         {
             System.setProperty("org.eclipse.jetty.start.testing","true");
@@ -743,6 +874,7 @@
         {
             run = false;
             download = true;
+            licenseCheckRequired = true;
             return;
         }
 
@@ -773,6 +905,15 @@
             exec = true;
             return;
         }
+        
+        // Assign a fixed name to the property file for exec
+        if (arg.startsWith("--exec-properties="))
+        {
+            exec_properties=Props.getValue(arg);
+            if (!exec_properties.endsWith(".properties"))
+                throw new UsageException(ERR_BAD_ARG,"--exec-properties filename must have .properties suffix: %s",exec_properties);
+            return;
+        }
 
         // Enable forked execution of Jetty server
         if ("--approve-all-licenses".equals(arg))
@@ -812,6 +953,7 @@
             addToStartdIni.addAll(moduleNames);
             run = false;
             download = true;
+            licenseCheckRequired = true;
             return;
         }
 
@@ -822,6 +964,7 @@
             addToStartIni.addAll(moduleNames);
             run = false;
             download = true;
+            licenseCheckRequired = true;
             return;
         }
 
@@ -833,6 +976,17 @@
             return;
         }
 
+        // Skip [files] validation on a module
+        if (arg.startsWith("--skip-file-validation="))
+        {
+            List<String> moduleNames = Props.getValues(arg);
+            for (String moduleName : moduleNames)
+            {
+                skipFileValidationModules.add(moduleName);
+            }
+            return;
+        }
+
         // Create graphviz output of module graph
         if (arg.startsWith("--write-module-graph="))
         {
@@ -914,7 +1068,7 @@
             }
             return;
         }
-        
+
         if (FS.isPropertyFile(arg))
         {
             // only add non-duplicates
@@ -922,7 +1076,7 @@
             {
                 propertyFileRefs.add(arg);
             }
-                return;
+            return;
         }
 
         // Anything else is unrecognized
@@ -946,9 +1100,9 @@
 
     public void parseModule(Module module)
     {
-        if(module.hasDefaultConfig()) 
+        if (module.hasDefaultConfig())
         {
-            for(String line: module.getDefaultConfig())
+            for (String line : module.getDefaultConfig())
             {
                 parse(line,module.getFilesystemRef(),false);
             }
@@ -969,7 +1123,7 @@
             addUniqueXmlFile(xmlRef,xmlfile);
         }
     }
-    
+
     public void resolvePropertyFiles(BaseHome baseHome) throws IOException
     {
         // Find and Expand property files
@@ -990,7 +1144,7 @@
         this.allModules = allModules;
     }
 
-    private void setProperty(String key, String value, String source, boolean replaceProp)
+    public void setProperty(String key, String value, String source, boolean replaceProp)
     {
         // Special / Prevent override from start.ini's
         if (key.equals("jetty.home"))
@@ -1006,18 +1160,25 @@
             return;
         }
 
-        // Normal
-        if (replaceProp)
+        if (replaceProp || (!properties.containsKey(key)))
         {
-            // always override
             properties.setProperty(key,value,source);
-        }
-        else
-        {
-            // only set if unset
-            if (!properties.containsKey(key))
+            if(key.equals("java.version"))
             {
-                properties.setProperty(key,value,source);
+                try
+                {
+                    JavaVersion ver = JavaVersion.parse(value);
+                    properties.setProperty("java.version",ver.getVersion(),source);
+                    properties.setProperty("java.version.platform",Integer.toString(ver.getPlatform()),source);
+                    properties.setProperty("java.version.major",Integer.toString(ver.getMajor()),source);
+                    properties.setProperty("java.version.minor",Integer.toString(ver.getMinor()),source);
+                    properties.setProperty("java.version.micro",Integer.toString(ver.getMicro()),source);
+                    properties.setProperty("java.version.update",Integer.toString(ver.getUpdate()),source);
+                }
+                catch (Throwable x)
+                {
+                    throw new UsageException(UsageException.ERR_BAD_ARG, x.getMessage());
+                }
             }
         }
     }
@@ -1042,5 +1203,4 @@
         builder.append("]");
         return builder.toString();
     }
-
 }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/StartIni.java b/jetty-start/src/main/java/org/eclipse/jetty/start/StartIni.java
index f2e7e82..e309e4e 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/StartIni.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/StartIni.java
@@ -64,7 +64,14 @@
     @Override
     public void init()
     {
-        basedir = getFile().getParent().toAbsolutePath();
+        try
+        {
+            basedir = getFile().getParent().toRealPath();
+        }
+        catch (IOException e)
+        {
+            basedir = getFile().getParent().normalize().toAbsolutePath();
+        }
     }
 
     public Path getBaseDir()
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/UsageException.java b/jetty-start/src/main/java/org/eclipse/jetty/start/UsageException.java
index 7c7eaf3..34f01d9 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/UsageException.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/UsageException.java
@@ -27,8 +27,10 @@
     public static final int ERR_LOGGING = -1;
     public static final int ERR_INVOKE_MAIN = -2;
     public static final int ERR_NOT_STOPPED = -4;
-    public static final int ERR_UNKNOWN = -5;
-    public static final int ERR_BAD_ARG = -6;
+    public static final int ERR_BAD_ARG = -5;
+    public static final int ERR_BAD_GRAPH = -6;
+    public static final int ERR_BAD_STOP_PROPS = -7;
+    public static final int ERR_UNKNOWN = -9;
     private int exitCode;
 
     public UsageException(int exitCode, String format, Object... objs)
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Utils.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Utils.java
new file mode 100644
index 0000000..c3f933f
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Utils.java
@@ -0,0 +1,122 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import java.util.Collection;
+
+public final class Utils
+{
+    public static String join(Object[] arr, String delim)
+    {
+        if (arr == null)
+        {
+            return "";
+        }
+
+        return join(arr,0,arr.length,delim);
+    }
+
+    public static String join(Object[] arr, int start, int end, String delim)
+    {
+        if (arr == null)
+        {
+            return "";
+        }
+        StringBuilder str = new StringBuilder();
+        for (int i = start; i < end; i++)
+        {
+            if (i > start)
+            {
+                str.append(delim);
+            }
+            str.append(arr[i]);
+        }
+        return str.toString();
+    }
+
+    public static String join(Collection<?> objs, String delim)
+    {
+        if (objs == null)
+        {
+            return "";
+        }
+        StringBuilder str = new StringBuilder();
+        boolean needDelim = false;
+        for (Object obj : objs)
+        {
+            if (needDelim)
+            {
+                str.append(delim);
+            }
+            str.append(obj);
+            needDelim = true;
+        }
+        return str.toString();
+    }
+
+    /**
+     * Is String null, empty, or consisting of only whitespace.
+     * 
+     * @param value
+     *            the value to test
+     * @return true if null, empty, or consisting of only whitespace
+     */
+    public static boolean isBlank(String value)
+    {
+        if (value == null)
+        {
+            return true;
+        }
+        int len = value.length();
+        for (int i = 0; i < len; i++)
+        {
+            int c = value.codePointAt(i);
+            if (!Character.isWhitespace(c))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Is String valid and has something other than whitespace
+     * 
+     * @param value
+     *            the value to test
+     * @return true if String has something other than whitespace
+     */
+    public static boolean isNotBlank(String value)
+    {
+        if (value == null)
+        {
+            return false;
+        }
+        int len = value.length();
+        for (int i = 0; i < len; i++)
+        {
+            int c = value.codePointAt(i);
+            if (!Character.isWhitespace(c))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Version.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Version.java
index 9c3e882..51ce9f4 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Version.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Version.java
@@ -19,113 +19,248 @@
 package org.eclipse.jetty.start;
 
 /**
- * Utility class for parsing and comparing version strings. JDK 1.1 compatible.
- * 
+ * Utility class for parsing and comparing version strings.
+ * <p>
+ * http://www.oracle.com/technetwork/java/javase/namechange-140185.html
  */
-
-public class Version
+public class Version implements Comparable<Version>
 {
+    
+    /**
+     * Original String version
+     */
+    private String string = null;
+    
+    /**
+     * Short String version
+     */
+    private String shortString = null;
+    
+    /**
+     * The major version for java is always "1" (per
+     * <a href="http://www.oracle.com/technetwork/java/javase/namechange-140185.html">legacy versioning history</a>)
+     */
+    private int legacyMajor = 0;
+    /**
+     * The true major version is the second value ("1.5" == "Java 5", "1.8" = "Java 8", etc..)
+     */
+    private int major = -1;
+    /**
+     * The revision of the version.
+     * <p>
+     * This value is always "0" (also per <a
+     * href="http://www.oracle.com/technetwork/java/javase/namechange-140185.html">legacy versioning history</a>)
+     */
+    private int revision = -1;
+    /**
+     * The update (where bug fixes are placed)
+     */
+    private int update = -1;
+    
+    /**
+     * Update strings may be zero padded!
+     */
+    private String updateString = null;
+    
+    /**
+     * Extra versioning information present on the version string, but not relevant for version comparison reason.
+     * (eg: with "1.8.0_45-internal", the suffix would be "-internal")
+     */
+    private String suffix = "";
 
-    int _version = 0;
-    int _revision = 0;
-    int _subrevision = 0;
-    String _suffix = "";
-
-    public Version()
+    private static enum ParseState
     {
+        LEGACY,
+        MAJOR,
+        REVISION,
+        UPDATE;
     }
 
-    public Version(String version_string)
+    public Version(String versionString)
     {
-        parse(version_string);
+        parse(versionString);
     }
 
-    // java.lang.Comparable is Java 1.2! Cannot use it
+    @Override
     /**
      * Compares with other version. Does not take extension into account, as there is no reliable way to order them.
      * 
+     * @param other the other version to compare this to 
      * @return -1 if this is older version that other, 0 if its same version, 1 if it's newer version than other
      */
-    public int compare(Version other)
+    public int compareTo(Version other)
     {
         if (other == null)
         {
             throw new NullPointerException("other version is null");
         }
-        if (this._version < other._version)
+        if (this.legacyMajor < other.legacyMajor)
         {
             return -1;
         }
-        if (this._version > other._version)
+        if (this.legacyMajor > other.legacyMajor)
         {
             return 1;
         }
-        if (this._revision < other._revision)
+        if (this.major < other.major)
         {
             return -1;
         }
-        if (this._revision > other._revision)
+        if (this.major > other.major)
         {
             return 1;
         }
-        if (this._subrevision < other._subrevision)
+        if (this.revision < other.revision)
         {
             return -1;
         }
-        if (this._subrevision > other._subrevision)
+        if (this.revision > other.revision)
+        {
+            return 1;
+        }
+        if (this.update < other.update)
+        {
+            return -1;
+        }
+        if (this.update > other.update)
         {
             return 1;
         }
         return 0;
     }
 
-    /**
-     * Check whether this verion is in range of versions specified
-     */
-    public boolean isInRange(Version low, Version high)
+    public int getLegacyMajor()
     {
-        return ((compare(low) >= 0) && (compare(high) <= 0));
+        return legacyMajor;
+    }
+
+    public int getMajor()
+    {
+        return major;
+    }
+
+    public int getRevision()
+    {
+        return revision;
+    }
+
+    public int getUpdate()
+    {
+        return update;
+    }
+
+    public String getSuffix()
+    {
+        return suffix;
+    }
+
+    public boolean isNewerThan(Version other)
+    {
+        return compareTo(other) == 1;
+    }
+
+    public boolean isNewerThanOrEqualTo(Version other)
+    {
+        int comp = compareTo(other);
+        return (comp == 0) || (comp == 1);
+    }
+
+    public boolean isOlderThan(Version other)
+    {
+        return compareTo(other) == -1;
+    }
+
+    public boolean isOlderThanOrEqualTo(Version other)
+    {
+        int comp = compareTo(other);
+        return (comp == 0) || (comp == -1);
     }
 
     /**
-     * parses version string in the form version[.revision[.subrevision[extension]]] into this instance.
+     * Check whether this version is in range of versions specified
+     * 
+     * @param low
+     *            the low part of the range
+     * @param high
+     *            the high part of the range
+     * @return true if this version is within the provided range
      */
-    public void parse(String version_string)
+    public boolean isInRange(Version low, Version high)
     {
-        _version = 0;
-        _revision = 0;
-        _subrevision = 0;
-        _suffix = "";
-        int pos = 0;
-        int startpos = 0;
-        int endpos = version_string.length();
-        while ((pos < endpos) && Character.isDigit(version_string.charAt(pos)))
+        return ((compareTo(low) >= 0) && (compareTo(high) <= 0));
+    }
+
+    /**
+     * parses version string in the form legacy[.major[.revision[_update[-suffix]]]] into this instance.
+     * 
+     * @param versionStr
+     *            the version string
+     */
+    private void parse(String versionStr)
+    {
+        string = versionStr;
+        legacyMajor = 0;
+        major = -1;
+        revision = -1;
+        update = -1;
+        suffix = "";
+
+        ParseState state = ParseState.LEGACY;
+        int offset = 0;
+        int len = versionStr.length();
+        int val = 0;
+        while (offset < len)
         {
-            pos++;
-        }
-        _version = Integer.parseInt(version_string.substring(startpos,pos));
-        if ((pos < endpos) && (version_string.charAt(pos) == '.'))
-        {
-            startpos = ++pos;
-            while ((pos < endpos) && Character.isDigit(version_string.charAt(pos)))
+            char c = versionStr.charAt(offset);
+            if (c=='-')
+                shortString=versionStr.substring(0,offset);
+            boolean isSeparator = !Character.isLetterOrDigit(c);
+            if (isSeparator)
             {
-                pos++;
+                val = 0;
             }
-            _revision = Integer.parseInt(version_string.substring(startpos,pos));
-        }
-        if ((pos < endpos) && (version_string.charAt(pos) == '.'))
-        {
-            startpos = ++pos;
-            while ((pos < endpos) && Character.isDigit(version_string.charAt(pos)))
+            else if (Character.isDigit(c))
             {
-                pos++;
+                val = (val * 10) + (c - '0');
             }
-            _subrevision = Integer.parseInt(version_string.substring(startpos,pos));
+            else if (Character.isLetter(c))
+            {
+                suffix = versionStr.substring(offset);
+                break;
+            }
+
+            switch (state)
+            {
+                case LEGACY:
+                    if (isSeparator)
+                        state = ParseState.MAJOR;
+                    else
+                        legacyMajor = val;
+                    break;
+                case MAJOR:
+                    if (isSeparator)
+                        state = ParseState.REVISION;
+                    else
+                        major = val;
+                    break;
+                case REVISION:
+                    if (isSeparator)
+                        state = ParseState.UPDATE;
+                    else
+                        revision = val;
+                    break;
+                case UPDATE:
+                    if (!isSeparator)
+                    {
+                        update = val;
+                    }
+                    break;
+            }
+
+            offset++;
         }
-        if (pos < endpos)
-        {
-            _suffix = version_string.substring(pos);
-        }
+        if (shortString==null)
+            shortString=versionStr;
     }
 
     /**
@@ -134,13 +269,15 @@
     @Override
     public String toString()
     {
-        StringBuffer sb = new StringBuffer(10);
-        sb.append(_version);
-        sb.append('.');
-        sb.append(_revision);
-        sb.append('.');
-        sb.append(_subrevision);
-        sb.append(_suffix);
-        return sb.toString();
+        return string;
+    }
+    
+    /**
+     * Return short string form (without suffix)
+     * @return string the short version string form
+     */
+    public String toShortString()
+    {
+        return shortString;
     }
 }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartDirBuilder.java b/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartDirBuilder.java
new file mode 100644
index 0000000..850eeec
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartDirBuilder.java
@@ -0,0 +1,106 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.builders;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+
+import org.eclipse.jetty.start.BaseBuilder;
+import org.eclipse.jetty.start.BaseHome;
+import org.eclipse.jetty.start.FS;
+import org.eclipse.jetty.start.Module;
+import org.eclipse.jetty.start.StartLog;
+import org.eclipse.jetty.start.graph.OnlyTransitivePredicate;
+
+/**
+ * Management of the <code>${jetty.base}/start.d/</code> based configuration.
+ * <p>
+ * Implementation of the <code>--add-to-startd=[name]</code> command line behavior
+ */
+public class StartDirBuilder implements BaseBuilder.Config
+{
+    private final BaseHome baseHome;
+    private final Path startDir;
+
+    public StartDirBuilder(BaseBuilder baseBuilder) throws IOException
+    {
+        this.baseHome = baseBuilder.getBaseHome();
+        this.startDir = baseHome.getBasePath("start.d");
+        FS.ensureDirectoryExists(startDir);
+    }
+
+    @Override
+    public boolean addModule(Module module) throws IOException
+    {
+        if (module.isDynamic())
+        {
+            if (module.hasIniTemplate())
+            {
+                // warn
+                StartLog.warn("%-15s not adding [ini-template] from dynamic module",module.getName());
+            }
+            return false;
+        }
+
+        String mode = "";
+        boolean isTransitive = module.matches(OnlyTransitivePredicate.INSTANCE);
+        if (isTransitive)
+        {
+            mode = "(transitively) ";
+        }
+
+        if (module.hasIniTemplate() || !isTransitive)
+        {
+            // Create start.d/{name}.ini
+            Path ini = startDir.resolve(module.getName() + ".ini");
+            StartLog.info("%-15s initialised %sin %s",module.getName(),mode,baseHome.toShortForm(ini));
+
+            try (BufferedWriter writer = Files.newBufferedWriter(ini,StandardCharsets.UTF_8,StandardOpenOption.CREATE,StandardOpenOption.TRUNCATE_EXISTING))
+            {
+                writeModuleSection(writer,module);
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    protected void writeModuleSection(BufferedWriter writer, Module module)
+    {
+        PrintWriter out = new PrintWriter(writer);
+
+        out.println("# --------------------------------------- ");
+        out.println("# Module: " + module.getName());
+        out.println("--module=" + module.getName());
+        out.println();
+
+        for (String line : module.getIniTemplate())
+        {
+            out.println(line);
+        }
+
+        out.println();
+        out.flush();
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartIniBuilder.java b/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartIniBuilder.java
new file mode 100644
index 0000000..c8ed50c
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartIniBuilder.java
@@ -0,0 +1,148 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.builders;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jetty.start.BaseBuilder;
+import org.eclipse.jetty.start.BaseHome;
+import org.eclipse.jetty.start.Module;
+import org.eclipse.jetty.start.Props;
+import org.eclipse.jetty.start.StartLog;
+import org.eclipse.jetty.start.graph.OnlyTransitivePredicate;
+
+/**
+ * Management of the <code>${jetty.base}/start.ini</code> based configuration.
+ * <p>
+ * Implementation of the <code>--add-to-start=[name]</code> command line behavior
+ */
+public class StartIniBuilder implements BaseBuilder.Config
+{
+    private final BaseHome baseHome;
+    private final Path startIni;
+
+    /* List of modules already present in start.ini */
+    private Set<String> modulesPresent = new HashSet<>();
+
+    /* List of properties (keys only) already present in start.ini */
+    private Set<String> propsPresent = new HashSet<>();
+
+    public StartIniBuilder(BaseBuilder baseBuilder) throws IOException
+    {
+        this.baseHome = baseBuilder.getBaseHome();
+        this.startIni = baseHome.getBasePath("start.ini");
+
+        if (Files.exists(startIni))
+        {
+            parseIni();
+        }
+    }
+
+    private void parseIni() throws IOException
+    {
+        try (BufferedReader reader = Files.newBufferedReader(startIni,StandardCharsets.UTF_8))
+        {
+            String line;
+            while ((line = reader.readLine()) != null)
+            {
+                line = line.trim();
+                if (line.startsWith("--module="))
+                {
+                    List<String> moduleNames = Props.getValues(line);
+                    this.modulesPresent.addAll(moduleNames);
+                }
+                else if (!line.startsWith("-") && line.contains("="))
+                {
+                    String key = line.substring(0,line.indexOf('='));
+                    this.propsPresent.add(key);
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean addModule(Module module) throws IOException
+    {
+        if (modulesPresent.contains(module.getName()))
+        {
+            StartLog.info("%-15s already initialised in %s",module.getName(),baseHome.toShortForm(startIni));
+            // skip, already present
+            return false;
+        }
+
+        if (module.isDynamic())
+        {
+            if (module.hasIniTemplate())
+            {
+                // warn
+                StartLog.warn("%-15s not adding [ini-template] from dynamic module",module.getName());
+            }
+            return false;
+        }
+
+        String mode = "";
+        boolean isTransitive = module.matches(OnlyTransitivePredicate.INSTANCE);
+        if (isTransitive)
+        {
+            mode = "(transitively) ";
+        }
+
+        if (module.hasIniTemplate() || !isTransitive)
+        {
+            StartLog.info("%-15s initialised %sin %s",module.getName(),mode,baseHome.toShortForm(startIni));
+
+            // Append to start.ini
+            try (BufferedWriter writer = Files.newBufferedWriter(startIni,StandardCharsets.UTF_8,StandardOpenOption.APPEND,StandardOpenOption.CREATE))
+            {
+                writeModuleSection(writer,module);
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    protected void writeModuleSection(BufferedWriter writer, Module module)
+    {
+        PrintWriter out = new PrintWriter(writer);
+
+        out.println("# --------------------------------------- ");
+        out.println("# Module: " + module.getName());
+        out.println("--module=" + module.getName());
+        out.println();
+
+        for (String line : module.getIniTemplate())
+        {
+            out.println(line);
+        }
+
+        out.println();
+        out.flush();
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/config/CommandLineConfigSource.java b/jetty-start/src/main/java/org/eclipse/jetty/start/config/CommandLineConfigSource.java
index 13ee34c..c581c41 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/config/CommandLineConfigSource.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/config/CommandLineConfigSource.java
@@ -32,6 +32,7 @@
 import org.eclipse.jetty.start.Props.Prop;
 import org.eclipse.jetty.start.RawArgs;
 import org.eclipse.jetty.start.UsageException;
+import org.eclipse.jetty.start.Utils;
 
 /**
  * Configuration Source representing the Command Line arguments.
@@ -69,14 +70,14 @@
     {
         // If a jetty property is defined, use it
         Prop prop = this.props.getProp(BaseHome.JETTY_BASE,false);
-        if (prop != null && !isEmpty(prop.value))
+        if (prop != null && !Utils.isBlank(prop.value))
         {
             return FS.toPath(prop.value);
         }
 
         // If a system property is defined, use it
         String val = System.getProperty(BaseHome.JETTY_BASE);
-        if (!isEmpty(val))
+        if (!Utils.isBlank(val))
         {
             return FS.toPath(val);
         }
@@ -91,14 +92,14 @@
     {
         // If a jetty property is defined, use it
         Prop prop = this.props.getProp(BaseHome.JETTY_HOME,false);
-        if (prop != null && !isEmpty(prop.value))
+        if (prop != null && !Utils.isBlank(prop.value))
         {
             return FS.toPath(prop.value);
         }
 
         // If a system property is defined, use it
         String val = System.getProperty(BaseHome.JETTY_HOME);
-        if (!isEmpty(val))
+        if (!Utils.isBlank(val))
         {
             return FS.toPath(val);
         }
@@ -130,24 +131,6 @@
         return home;
     }
 
-    private boolean isEmpty(String value)
-    {
-        if (value == null)
-        {
-            return true;
-        }
-        int len = value.length();
-        for (int i = 0; i < len; i++)
-        {
-            int c = value.codePointAt(i);
-            if (!Character.isWhitespace(c))
-            {
-                return false;
-            }
-        }
-        return true;
-    }
-
     @Override
     public boolean equals(Object obj)
     {
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/config/ConfigSources.java b/jetty-start/src/main/java/org/eclipse/jetty/start/config/ConfigSources.java
index 47a2d01..252e918 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/config/ConfigSources.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/config/ConfigSources.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.start.config;
 
-import static org.eclipse.jetty.start.UsageException.*;
+import static org.eclipse.jetty.start.UsageException.ERR_BAD_ARG;
 
 import java.io.IOException;
 import java.nio.file.Path;
@@ -31,13 +31,12 @@
 
 import org.eclipse.jetty.start.FS;
 import org.eclipse.jetty.start.Props;
-import org.eclipse.jetty.start.RawArgs;
 import org.eclipse.jetty.start.Props.Prop;
+import org.eclipse.jetty.start.RawArgs;
 import org.eclipse.jetty.start.UsageException;
 
 /**
  * Weighted List of ConfigSources.
- * <p>
  */
 public class ConfigSources implements Iterable<ConfigSource>
 {
@@ -74,7 +73,7 @@
             {
                 String ref = getValue(arg.getLine());
                 String dirName = props.expand(ref);
-                Path dir = FS.toPath(dirName);
+                Path dir = FS.toPath(dirName).normalize().toAbsolutePath();
                 DirConfigSource dirsource = new DirConfigSource(ref,dir,sourceWeight.incrementAndGet(),true);
                 add(dirsource);
             }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/config/DirConfigSource.java b/jetty-start/src/main/java/org/eclipse/jetty/start/config/DirConfigSource.java
index 93754b8..186544f 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/config/DirConfigSource.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/config/DirConfigSource.java
@@ -18,11 +18,12 @@
 
 package org.eclipse.jetty.start.config;
 
-import static org.eclipse.jetty.start.UsageException.*;
+import static org.eclipse.jetty.start.UsageException.ERR_BAD_ARG;
 
 import java.io.IOException;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
 import java.nio.file.PathMatcher;
 import java.util.ArrayList;
@@ -33,11 +34,11 @@
 import org.eclipse.jetty.start.NaturalSort;
 import org.eclipse.jetty.start.PathMatchers;
 import org.eclipse.jetty.start.Props;
-import org.eclipse.jetty.start.RawArgs;
-import org.eclipse.jetty.start.UsageException;
 import org.eclipse.jetty.start.Props.Prop;
+import org.eclipse.jetty.start.RawArgs;
 import org.eclipse.jetty.start.StartIni;
 import org.eclipse.jetty.start.StartLog;
+import org.eclipse.jetty.start.UsageException;
 
 /**
  * A Directory based {@link ConfigSource}.
@@ -100,12 +101,21 @@
 
         if (canHaveArgs)
         {
-            Path iniFile = dir.resolve("start.ini");
-            if (FS.canReadFile(iniFile))
+            Path iniFile = dir.resolve("start.ini").normalize().toAbsolutePath();
+            
+            try
             {
-                StartIni ini = new StartIni(iniFile);
-                args.addAll(ini.getLines(),iniFile);
-                parseAllArgs(ini.getLines(),iniFile.toString());
+                iniFile = iniFile.toRealPath();
+                if (FS.canReadFile(iniFile))
+                {
+                    StartIni ini = new StartIni(iniFile);
+                    args.addAll(ini.getLines(),iniFile);
+                    parseAllArgs(ini.getLines(),iniFile.toString());
+                }
+            }
+            catch (NoSuchFileException ignore)
+            {
+                // ignore
             }
 
             Path startDdir = dir.resolve("start.d");
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/fileinits/MavenLocalRepoFileInitializer.java b/jetty-start/src/main/java/org/eclipse/jetty/start/fileinits/MavenLocalRepoFileInitializer.java
new file mode 100644
index 0000000..6e6a800
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/fileinits/MavenLocalRepoFileInitializer.java
@@ -0,0 +1,194 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.fileinits;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.eclipse.jetty.start.BaseHome;
+import org.eclipse.jetty.start.FS;
+import org.eclipse.jetty.start.FileInitializer;
+import org.eclipse.jetty.start.StartLog;
+import org.eclipse.jetty.start.Utils;
+
+/**
+ * Attempt to download a <code>maven://</code> URI, by first attempting to find
+ * the resource in the maven repository system (starting with local, then
+ * central)
+ * <p>
+ * Valid URI Formats:
+ * <dl>
+ * <dt><code>maven://&lt;groupId&gt;/&lt;artifactId&gt;/&lt;version&gt;</code></dt>
+ * <dd>minimum requirement (type defaults to <code>jar</code>, with no
+ * classifier)</dd>
+ * <dt><code>maven://&lt;groupId&gt;/&lt;artifactId&gt;/&lt;version&gt;/&lt;type&gt;</code></dt>
+ * <dd>optional type requirement</dd>
+ * <dt>
+ * <code>maven://&lt;groupId&gt;/&lt;artifactId&gt;/&lt;version&gt;/&lt;type&gt;/&lt;classifier&gt;</code>
+ * </dt>
+ * <dd>optional type and classifier requirement</dd>
+ * </dl>
+ */
+public class MavenLocalRepoFileInitializer extends UriFileInitializer implements FileInitializer
+{
+    public static class Coordinates
+    {
+        public String groupId;
+        public String artifactId;
+        public String version;
+        public String type;
+        public String classifier;
+
+        public String toPath()
+        {
+            StringBuilder pathlike = new StringBuilder();
+            pathlike.append(groupId.replace('.','/'));
+            pathlike.append('/').append(artifactId);
+            pathlike.append('/').append(version);
+            pathlike.append('/').append(artifactId);
+            pathlike.append('-').append(version);
+            if (classifier != null)
+            {
+                pathlike.append('-').append(classifier);
+            }
+            pathlike.append('.').append(type);
+            return pathlike.toString();
+        }
+
+        public URI toCentralURI()
+        {
+            return URI.create("http://central.maven.org/maven2/" + toPath());
+        }
+    }
+
+    private Path localRepositoryDir;
+
+    public MavenLocalRepoFileInitializer(BaseHome baseHome)
+    {
+        this(baseHome,null);
+    }
+
+    public MavenLocalRepoFileInitializer(BaseHome baseHome, Path localRepoDir)
+    {
+        super(baseHome);
+        this.localRepositoryDir = localRepoDir;
+    }
+
+    @Override
+    public boolean init(URI uri, Path file, String fileRef) throws IOException
+    {
+        Coordinates coords = getCoordinates(uri);
+        if (coords == null)
+        {
+            // Skip, not a maven:// URI
+            return false;
+        }
+
+        if (isFilePresent(file, baseHome.getPath(fileRef)))
+        {
+            // All done
+            return true;
+        }
+
+        // If using local repository
+        if (this.localRepositoryDir != null)
+        {
+            // Grab copy from local repository (download if needed to local
+            // repository)
+            Path localRepoFile = getLocalRepoFile(coords);
+            StartLog.log("COPY","%s to %s",localRepoFile,baseHome.toShortForm(file));
+            Files.copy(localRepoFile,file);
+        }
+        else
+        {
+            // normal non-local repo version
+            download(coords.toCentralURI(),file);
+        }
+        return true;
+    }
+
+    private Path getLocalRepoFile(Coordinates coords) throws IOException
+    {
+        Path localFile = localRepositoryDir.resolve(coords.toPath());
+        if (FS.canReadFile(localFile))
+        {
+            return localFile;
+        }
+
+        // Download, if needed
+        download(coords.toCentralURI(),localFile);
+        return localFile;
+    }
+
+    public Coordinates getCoordinates(URI uri)
+    {
+        if (!"maven".equalsIgnoreCase(uri.getScheme()))
+        {
+            return null;
+        }
+
+        String ssp = uri.getSchemeSpecificPart();
+
+        if (ssp.startsWith("//"))
+        {
+            ssp = ssp.substring(2);
+        }
+
+        String parts[] = ssp.split("/");
+
+        if (StartLog.isDebugEnabled())
+        {
+            StartLog.debug("ssp = %s",ssp);
+            StartLog.debug("parts = %d",parts.length);
+            for (int i = 0; i < parts.length; i++)
+            {
+                StartLog.debug("  part[%2d]: [%s]",i,parts[i]);
+            }
+        }
+
+        if (parts.length < 3)
+        {
+            throw new RuntimeException("Not a valid maven:// uri - " + uri);
+        }
+
+        Coordinates coords = new Coordinates();
+        coords.groupId = parts[0];
+        coords.artifactId = parts[1];
+        coords.version = parts[2];
+        coords.type = "jar";
+        coords.classifier = null;
+
+        if (parts.length >= 4)
+        {
+            if (Utils.isNotBlank(parts[3]))
+            {
+                coords.type = parts[3];
+            }
+
+            if ((parts.length == 5) && (Utils.isNotBlank(parts[4])))
+            {
+                coords.classifier = parts[4];
+            }
+        }
+
+        return coords;
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/fileinits/TestFileInitializer.java b/jetty-start/src/main/java/org/eclipse/jetty/start/fileinits/TestFileInitializer.java
new file mode 100644
index 0000000..90f862e
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/fileinits/TestFileInitializer.java
@@ -0,0 +1,44 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.fileinits;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Path;
+
+import org.eclipse.jetty.start.FS;
+import org.eclipse.jetty.start.FileInitializer;
+import org.eclipse.jetty.start.StartLog;
+
+/**
+ * In a start testing scenario, it is often not important to actually download
+ * or initialize a file, this implementation is merely a no-op for the
+ * {@link FileInitializer}
+ */
+public class TestFileInitializer implements FileInitializer
+{
+    @Override
+    public boolean init(URI uri, Path file, String fileRef) throws IOException
+    {
+        FS.ensureDirectoryExists(file.getParent());
+
+        StartLog.log("TESTING MODE","Skipping download of " + uri);
+        return true;
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/fileinits/UriFileInitializer.java b/jetty-start/src/main/java/org/eclipse/jetty/start/fileinits/UriFileInitializer.java
new file mode 100644
index 0000000..3316dbe
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/fileinits/UriFileInitializer.java
@@ -0,0 +1,149 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.fileinits;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+
+import org.eclipse.jetty.start.BaseHome;
+import org.eclipse.jetty.start.FS;
+import org.eclipse.jetty.start.FileInitializer;
+import org.eclipse.jetty.start.StartLog;
+
+public class UriFileInitializer implements FileInitializer
+{
+    private final static String[] SUPPORTED_SCHEMES = { "http", "https" };
+    protected final BaseHome baseHome;
+    
+    public UriFileInitializer(BaseHome baseHome)
+    {
+        this.baseHome = baseHome;
+    }
+    
+    @Override
+    public boolean init(URI uri, Path file, String fileRef) throws IOException
+    {
+        if (!isSupportedScheme(uri))
+        {
+            // Not a supported scheme.
+            return false;
+        }
+
+        if(isFilePresent(file, baseHome.getPath(fileRef)))
+        {
+            // All done
+            return true;
+        }
+
+        download(uri,file);
+
+        return true;
+    }
+
+    protected void download(URI uri, Path file) throws IOException
+    {
+        StartLog.log("DOWNLOAD","%s to %s",uri,baseHome.toShortForm(file));
+
+        FS.ensureDirectoryExists(file.getParent());
+        
+        HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection();
+        http.setInstanceFollowRedirects(true);
+        http.setAllowUserInteraction(false);
+        
+        int status = http.getResponseCode();
+        
+        if(status != HttpURLConnection.HTTP_OK)
+        {
+            throw new IOException("URL GET Failure [" + status + "/" + http.getResponseMessage() + "] on " + uri);
+        }
+
+        byte[] buf = new byte[8192];
+        try (InputStream in = http.getInputStream(); OutputStream out = Files.newOutputStream(file,StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE))
+        {
+            while (true)
+            {
+                int len = in.read(buf);
+
+                if (len > 0)
+                {
+                    out.write(buf,0,len);
+                }
+                if (len < 0)
+                {
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Test if any of the Paths exist (as files)
+     * 
+     * @param paths
+     *            the list of paths to check
+     * @return true if the path exist (as a file), false if it doesn't exist
+     * @throws IOException
+     *             if the path points to a non-file, or is not readable.
+     */
+    protected boolean isFilePresent(Path... paths) throws IOException
+    {
+        for (Path file : paths)
+        {
+            if (Files.exists(file))
+            {
+                if (Files.isDirectory(file))
+                {
+                    throw new IOException("Directory in the way: " + file.toAbsolutePath());
+                }
+
+                if (!Files.isReadable(file))
+                {
+                    throw new IOException("File not readable: " + file.toAbsolutePath());
+                }
+
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private boolean isSupportedScheme(URI uri)
+    {
+        String scheme = uri.getScheme();
+        if (scheme == null)
+        {
+            return false;
+        }
+        for (String supported : SUPPORTED_SCHEMES)
+        {
+            if (supported.equalsIgnoreCase(scheme))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/AllPredicate.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/AllPredicate.java
new file mode 100644
index 0000000..6a91314
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/AllPredicate.java
@@ -0,0 +1,31 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+/**
+ * Match on everything.
+ */
+public class AllPredicate implements Predicate
+{
+    @Override
+    public boolean match(Node<?> node)
+    {
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/AndPredicate.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/AndPredicate.java
new file mode 100644
index 0000000..a60c3e3
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/AndPredicate.java
@@ -0,0 +1,46 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+/**
+ * Match on multiple predicates.
+ */
+public class AndPredicate implements Predicate
+{
+    private final Predicate predicates[];
+
+    public AndPredicate(Predicate... predicates)
+    {
+        this.predicates = predicates;
+    }
+
+    @Override
+    public boolean match(Node<?> node)
+    {
+        for (Predicate predicate : this.predicates)
+        {
+            if (!predicate.match(node))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/AnySelectionPredicate.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/AnySelectionPredicate.java
new file mode 100644
index 0000000..03fc77e
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/AnySelectionPredicate.java
@@ -0,0 +1,28 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+public class AnySelectionPredicate implements Predicate
+{
+    @Override
+    public boolean match(Node<?> input)
+    {
+        return !input.getSelections().isEmpty();
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/CriteriaPredicate.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/CriteriaPredicate.java
new file mode 100644
index 0000000..46db934
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/CriteriaPredicate.java
@@ -0,0 +1,45 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+/**
+ * Predicate against a specific {@link Selection#getCriteria()}
+ */
+public class CriteriaPredicate implements Predicate
+{
+    private final String criteria;
+
+    public CriteriaPredicate(String criteria)
+    {
+        this.criteria = criteria;
+    }
+
+    @Override
+    public boolean match(Node<?> node)
+    {
+        for (Selection selection : node.getSelections())
+        {
+            if (criteria.equalsIgnoreCase(selection.getCriteria()))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/CriteriaSetPredicate.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/CriteriaSetPredicate.java
new file mode 100644
index 0000000..15d9814
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/CriteriaSetPredicate.java
@@ -0,0 +1,71 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Should match against the provided set of {@link Selection#getCriteria()} values.
+ * <p>
+ * Incomplete set is considered to be no-match.
+ */
+public class CriteriaSetPredicate implements Predicate
+{
+    private final Set<String> criteriaSet;
+
+    public CriteriaSetPredicate(String... criterias)
+    {
+        this.criteriaSet = new HashSet<>();
+
+        for (String name : criterias)
+        {
+            this.criteriaSet.add(name);
+        }
+    }
+
+    @Override
+    public boolean match(Node<?> node)
+    {
+        Set<Selection> selections = node.getSelections();
+        if (selections == null)
+        {
+            // empty sources list
+            return false;
+        }
+
+        Set<String> actualCriterias = node.getSelectedCriteriaSet();
+
+        if (actualCriterias.size() != criteriaSet.size())
+        {
+            // non-equal sized set
+            return false;
+        }
+
+        for (String actualCriteria : actualCriterias)
+        {
+            if (!this.criteriaSet.contains(actualCriteria))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
\ No newline at end of file
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Graph.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Graph.java
new file mode 100644
index 0000000..fa889ce
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Graph.java
@@ -0,0 +1,503 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import org.eclipse.jetty.start.Props;
+import org.eclipse.jetty.start.StartLog;
+import org.eclipse.jetty.start.Utils;
+
+/**
+ * Basic Graph
+ * @param <T> the node type
+ */
+public abstract class Graph<T extends Node<T>> implements Iterable<T>
+{
+    private String selectionTerm = "select";
+    private String nodeTerm = "node";
+    private Map<String, T> nodes = new LinkedHashMap<>();
+    private int maxDepth = -1;
+
+    protected Set<String> asNameSet(Set<T> nodeSet)
+    {
+        Set<String> ret = new HashSet<>();
+        for (T node : nodeSet)
+        {
+            ret.add(node.getName());
+        }
+        return ret;
+    }
+
+    private void assertNoCycle(T node, Stack<String> refs)
+    {
+        for (T parent : node.getParentEdges())
+        {
+            if (refs.contains(parent.getName()))
+            {
+                // Cycle detected.
+                StringBuilder err = new StringBuilder();
+                err.append("A cyclic reference in the ");
+                err.append(this.getClass().getSimpleName());
+                err.append(" has been detected: ");
+                for (int i = 0; i < refs.size(); i++)
+                {
+                    if (i > 0)
+                    {
+                        err.append(" -> ");
+                    }
+                    err.append(refs.get(i));
+                }
+                err.append(" -> ").append(parent.getName());
+                throw new IllegalStateException(err.toString());
+            }
+
+            refs.push(parent.getName());
+            assertNoCycle(parent,refs);
+            refs.pop();
+        }
+    }
+
+    private void bfsCalculateDepth(final T node, final int depthNow)
+    {
+        int depth = depthNow + 1;
+
+        // Set depth on every child first
+        for (T child : node.getChildEdges())
+        {
+            child.setDepth(Math.max(depth,child.getDepth()));
+            this.maxDepth = Math.max(this.maxDepth,child.getDepth());
+        }
+
+        // Dive down
+        for (T child : node.getChildEdges())
+        {
+            bfsCalculateDepth(child,depth);
+        }
+    }
+
+    public void buildGraph() throws FileNotFoundException, IOException
+    {
+        // Connect edges
+        // Make a copy of nodes.values() as the list could be modified
+        List<T> nodeList = new ArrayList<>(nodes.values());
+        for (T node : nodeList)
+        {
+            for (String parentName : node.getParentNames())
+            {
+                T parent = get(parentName);
+
+                if (parent == null)
+                {
+                    parent = resolveNode(parentName);
+                }
+
+                if (parent == null)
+                {
+                    if (Props.hasPropertyKey(parentName))
+                    {
+                        StartLog.debug("Module property not expandable (yet) [%s]",parentName);
+                    }
+                    else
+                    {
+                        StartLog.warn("Module not found [%s]",parentName);
+                    }
+                }
+                else
+                {
+                    node.addParentEdge(parent);
+                    parent.addChildEdge(node);
+                }
+            }
+
+            for (String optionalParentName : node.getOptionalParentNames())
+            {
+                T optional = get(optionalParentName);
+                if (optional == null)
+                {
+                    StartLog.debug("Optional module not found [%s]",optionalParentName);
+                }
+                else if (optional.isSelected())
+                {
+                    node.addParentEdge(optional);
+                    optional.addChildEdge(node);
+                }
+            }
+        }
+
+        // Verify there is no cyclic references
+        Stack<String> refs = new Stack<>();
+        for (T module : nodes.values())
+        {
+            refs.push(module.getName());
+            assertNoCycle(module,refs);
+            refs.pop();
+        }
+
+        // Calculate depth of all modules for sorting later
+        for (T module : nodes.values())
+        {
+            if (module.getParentEdges().isEmpty())
+            {
+                bfsCalculateDepth(module,0);
+            }
+        }
+    }
+
+    public boolean containsNode(String name)
+    {
+        return nodes.containsKey(name);
+    }
+
+    public int count()
+    {
+        return nodes.size();
+    }
+
+    public void dumpSelectedTree()
+    {
+        List<T> ordered = new ArrayList<>();
+        ordered.addAll(nodes.values());
+        Collections.sort(ordered,new NodeDepthComparator());
+
+        List<T> active = getSelected();
+
+        for (T module : ordered)
+        {
+            if (active.contains(module))
+            {
+                // Show module name
+                String indent = toIndent(module.getDepth());
+                boolean transitive = module.matches(OnlyTransitivePredicate.INSTANCE);
+                System.out.printf("%s + %s: %s [%s]%n",indent,toCap(nodeTerm),module.getName(),transitive?"transitive":"selected");
+            }
+        }
+    }
+
+    public void dumpSelected()
+    {
+        List<T> ordered = new ArrayList<>();
+        ordered.addAll(nodes.values());
+        Collections.sort(ordered,new NodeDepthComparator());
+
+        List<T> active = getSelected();
+
+        for (T module : ordered)
+        {
+            if (active.contains(module))
+            {
+                // Show module name
+                boolean transitive = module.matches(OnlyTransitivePredicate.INSTANCE);
+                System.out.printf("  %3d) %-15s ",module.getDepth() + 1,module.getName());
+                if (transitive)
+                {
+                    System.out.println("<transitive> ");
+                }
+                else
+                {
+                    List<String> criterias = new ArrayList<>();
+                    for (Selection selection : module.getSelections())
+                    {
+                        if (selection.isExplicit())
+                        {
+                            criterias.add(selection.getCriteria());
+                        }
+                    }
+                    Collections.sort(criterias);
+                    System.out.println(Utils.join(criterias,", "));
+                }
+            }
+        }
+    }
+
+    protected void findChildren(T module, Set<T> ret)
+    {
+        ret.add(module);
+        for (T child : module.getChildEdges())
+        {
+            ret.add(child);
+        }
+    }
+
+    protected void findParents(T module, Map<String, T> ret)
+    {
+        ret.put(module.getName(),module);
+        for (T parent : module.getParentEdges())
+        {
+            ret.put(parent.getName(),parent);
+            findParents(parent,ret);
+        }
+    }
+
+    public T get(String name)
+    {
+        return nodes.get(name);
+    }
+
+    /**
+     * Get the list of Selected nodes.
+     * @return the list of selected nodes
+     */
+    public List<T> getSelected()
+    {
+        return getMatching(new AnySelectionPredicate());
+    }
+
+    /**
+     * Get the Nodes from the tree that match the provided predicate.
+     *
+     * @param predicate
+     *            the way to match nodes
+     * @return the list of matching nodes in execution order.
+     */
+    public List<T> getMatching(Predicate predicate)
+    {
+        List<T> selected = new ArrayList<T>();
+
+        for (T node : nodes.values())
+        {
+            if (predicate.match(node))
+            {
+                selected.add(node);
+            }
+        }
+
+        Collections.sort(selected,new NodeDepthComparator());
+        return selected;
+    }
+
+    public int getMaxDepth()
+    {
+        return maxDepth;
+    }
+
+    public Set<T> getModulesAtDepth(int depth)
+    {
+        Set<T> ret = new HashSet<>();
+        for (T node : nodes.values())
+        {
+            if (node.getDepth() == depth)
+            {
+                ret.add(node);
+            }
+        }
+        return ret;
+    }
+
+    public Collection<String> getNodeNames()
+    {
+        return nodes.keySet();
+    }
+
+    public Collection<T> getNodes()
+    {
+        return nodes.values();
+    }
+
+    public String getNodeTerm()
+    {
+        return nodeTerm;
+    }
+
+    public String getSelectionTerm()
+    {
+        return selectionTerm;
+    }
+
+    @Override
+    public Iterator<T> iterator()
+    {
+        return nodes.values().iterator();
+    }
+
+    public abstract void onNodeSelected(T node);
+
+    public T register(T node)
+    {
+        StartLog.debug("Registering Node: [%s] %s",node.getName(),node);
+        nodes.put(node.getName(),node);
+        return node;
+    }
+
+    public Set<String> resolveChildNodesOf(String nodeName)
+    {
+        Set<T> ret = new HashSet<>();
+        T module = get(nodeName);
+        findChildren(module,ret);
+        return asNameSet(ret);
+    }
+
+    /**
+     * Resolve a node just in time.
+     * <p>
+     * Useful for nodes that are virtual/transient in nature (such as the jsp/jstl/alpn modules)
+     * @param name the name of the node to resolve
+     * @return the node
+     */
+    public abstract T resolveNode(String name);
+
+    public Set<String> resolveParentModulesOf(String nodeName)
+    {
+        Map<String, T> ret = new HashMap<>();
+        T node = get(nodeName);
+        findParents(node,ret);
+        return ret.keySet();
+    }
+
+    public int selectNode(Predicate nodePredicate, Selection selection)
+    {
+        int count = 0;
+        List<T> matches = getMatching(nodePredicate);
+        if (matches.isEmpty())
+        {
+            StringBuilder err = new StringBuilder();
+            err.append("WARNING: Cannot ").append(selectionTerm);
+            err.append(" requested ").append(nodeTerm);
+            err.append("s.  ").append(nodePredicate);
+            err.append(" returned no matches.");
+            StartLog.warn(err.toString());
+            return count;
+        }
+
+        // select them
+        for (T node : matches)
+        {
+            count += selectNode(node,selection);
+        }
+
+        return count;
+    }
+
+    public int selectNode(String name, Selection selection)
+    {
+        int count = 0;
+        T node = get(name);
+        if (node == null)
+        {
+            StringBuilder err = new StringBuilder();
+            err.append("Cannot ").append(selectionTerm);
+            err.append(" requested ").append(nodeTerm);
+            err.append(" [").append(name).append("]: not a valid ");
+            err.append(nodeTerm).append(" name.");
+            StartLog.warn(err.toString());
+            return count;
+        }
+
+        count += selectNode(node,selection);
+
+        return count;
+    }
+
+    private int selectNode(T node, Selection selection)
+    {
+        int count = 0;
+
+        if (node.getSelections().contains(selection))
+        {
+            // Already enabled with this selection.
+            return count;
+        }
+
+        StartLog.debug("%s %s: %s (via %s)",toCap(selectionTerm),nodeTerm,node.getName(),selection);
+
+        boolean newlySelected = node.getSelections().isEmpty();
+
+        // Add self
+        node.addSelection(selection);
+        if (newlySelected)
+        {
+            onNodeSelected(node);
+        }
+        count++;
+
+        // Walk transitive
+        Selection transitive = selection.asTransitive();
+        List<String> parentNames = new ArrayList<>();
+        parentNames.addAll(node.getParentNames());
+
+        count += selectNodes(parentNames,transitive);
+
+        return count;
+    }
+
+    public int selectNodes(Collection<String> names, Selection selection)
+    {
+        StartLog.debug("%s [%s] (via %s)",toCap(selectionTerm),Utils.join(names,", "),selection);
+
+        int count = 0;
+
+        for (String name : names)
+        {
+            T node = get(name);
+            // Node doesn't exist yet (try to resolve it it just-in-time)
+            if (node == null)
+            {
+                StartLog.debug("resolving node [%s]",name);
+                node = resolveNode(name);
+            }
+            // Node still doesn't exist? this is now an invalid graph.
+            if (node == null)
+            {
+                throw new GraphException("Missing referenced dependency: " + name);
+            }
+
+            count += selectNode(node.getName(),selection);
+        }
+
+        return count;
+    }
+
+    public void setNodeTerm(String nodeTerm)
+    {
+        this.nodeTerm = nodeTerm;
+    }
+
+    public void setSelectionTerm(String selectionTerm)
+    {
+        this.selectionTerm = selectionTerm;
+    }
+
+    private String toCap(String str)
+    {
+        StringBuilder cap = new StringBuilder();
+        cap.append(Character.toUpperCase(str.charAt(0)));
+        cap.append(str.substring(1));
+        return cap.toString();
+    }
+
+    private String toIndent(int depth)
+    {
+        char indent[] = new char[depth * 2];
+        Arrays.fill(indent,' ');
+        return new String(indent);
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/GraphException.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/GraphException.java
new file mode 100644
index 0000000..e553b64
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/GraphException.java
@@ -0,0 +1,36 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+/**
+ * A non-recoverable graph exception
+ */
+@SuppressWarnings("serial")
+public class GraphException extends RuntimeException
+{
+    public GraphException(String message, Throwable cause)
+    {
+        super(message,cause);
+    }
+
+    public GraphException(String message)
+    {
+        super(message);
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/NamePredicate.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/NamePredicate.java
new file mode 100644
index 0000000..e601bda
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/NamePredicate.java
@@ -0,0 +1,35 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+public class NamePredicate implements Predicate
+{
+    private final String name;
+    
+    public NamePredicate(String name)
+    {
+        this.name = name;
+    }
+    
+    @Override
+    public boolean match(Node<?> input)
+    {
+        return input.getName().equalsIgnoreCase(this.name);
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Node.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Node.java
new file mode 100644
index 0000000..afe5218
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Node.java
@@ -0,0 +1,179 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Basic Graph Node
+ * @param <T> the node type
+ */
+public abstract class Node<T>
+{
+    /** The logical name of this Node */
+    private String logicalName;
+    /** The depth of the Node in the tree */
+    private int depth = 0;
+    /** The set of selections for how this node was selected */
+    private Set<Selection> selections = new LinkedHashSet<>();
+    /** Set of Nodes, by name, that this Node depends on */
+    private List<String> parentNames = new ArrayList<>();
+    /** Set of Nodes, by name, that this Node optionally depend on */
+    private List<String> optionalParentNames = new ArrayList<>();
+
+    /** The Edges to parent Nodes */
+    private Set<T> parentEdges = new LinkedHashSet<>();
+    /** The Edges to child Nodes */
+    private Set<T> childEdges = new LinkedHashSet<>();
+
+    public void addChildEdge(T child)
+    {
+        if (childEdges.contains(child))
+        {
+            // already present, skip
+            return;
+        }
+        this.childEdges.add(child);
+    }
+
+    public void addOptionalParentName(String name)
+    {
+        if (this.optionalParentNames.contains(name))
+        {
+            // skip, name already exists
+            return;
+        }
+        this.optionalParentNames.add(name);
+    }
+
+    public void addParentEdge(T parent)
+    {
+        if (parentEdges.contains(parent))
+        {
+            // already present, skip
+            return;
+        }
+        this.parentEdges.add(parent);
+    }
+
+    public void addParentName(String name)
+    {
+        if (this.parentNames.contains(name))
+        {
+            // skip, name already exists
+            return;
+        }
+        this.parentNames.add(name);
+    }
+
+    public void addSelection(Selection selection)
+    {
+        this.selections.add(selection);
+    }
+
+    public Set<T> getChildEdges()
+    {
+        return childEdges;
+    }
+
+    public int getDepth()
+    {
+        return depth;
+    }
+
+    @Deprecated
+    public String getLogicalName()
+    {
+        return logicalName;
+    }
+
+    public String getName()
+    {
+        return logicalName;
+    }
+
+    public List<String> getOptionalParentNames()
+    {
+        return optionalParentNames;
+    }
+
+    public Set<T> getParentEdges()
+    {
+        return parentEdges;
+    }
+
+    public List<String> getParentNames()
+    {
+        return parentNames;
+    }
+
+    public Set<Selection> getSelections()
+    {
+        return selections;
+    }
+
+    public Set<String> getSelectedCriteriaSet()
+    {
+        Set<String> criteriaSet = new HashSet<>();
+        for (Selection selection : selections)
+        {
+            criteriaSet.add(selection.getCriteria());
+        }
+        return criteriaSet;
+    }
+
+    public boolean isSelected()
+    {
+        return !selections.isEmpty();
+    }
+
+    public boolean matches(Predicate predicate)
+    {
+        return predicate.match(this);
+    }
+
+    public void setDepth(int depth)
+    {
+        this.depth = depth;
+    }
+
+    public void setName(String name)
+    {
+        this.logicalName = name;
+    }
+
+    public void setParentNames(List<String> parents)
+    {
+        this.parentNames.clear();
+        this.parentEdges.clear();
+        if (parents != null)
+        {
+            this.parentNames.addAll(parents);
+        }
+    }
+
+    public void setSelections(Set<Selection> selection)
+    {
+        this.selections = selection;
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/NodeDepthComparator.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/NodeDepthComparator.java
new file mode 100644
index 0000000..879ba9b
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/NodeDepthComparator.java
@@ -0,0 +1,43 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+import java.text.CollationKey;
+import java.text.Collator;
+import java.util.Comparator;
+
+public class NodeDepthComparator implements Comparator<Node<?>>
+{
+    private Collator collator = Collator.getInstance();
+
+    @Override
+    public int compare(Node<?> o1, Node<?> o2)
+    {
+        // order by depth first.
+        int diff = o1.getDepth() - o2.getDepth();
+        if (diff != 0)
+        {
+            return diff;
+        }
+        // then by name (not really needed, but makes for predictable test cases)
+        CollationKey k1 = collator.getCollationKey(o1.getName());
+        CollationKey k2 = collator.getCollationKey(o2.getName());
+        return k1.compareTo(k2);
+    }
+}
\ No newline at end of file
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/OnlyTransitivePredicate.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/OnlyTransitivePredicate.java
new file mode 100644
index 0000000..ab74438
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/OnlyTransitivePredicate.java
@@ -0,0 +1,41 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+/**
+ * Predicate for a node that has no explicitly set selections.
+ * (They are all transitive)
+ */
+public class OnlyTransitivePredicate implements Predicate
+{
+    public static final Predicate INSTANCE = new OnlyTransitivePredicate();
+    
+    @Override
+    public boolean match(Node<?> input)
+    {
+        for (Selection selection : input.getSelections())
+        {
+            if (selection.isExplicit())
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Predicate.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Predicate.java
new file mode 100644
index 0000000..c33ec23
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Predicate.java
@@ -0,0 +1,27 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+/**
+ * Matcher of Nodes
+ */
+public interface Predicate
+{
+    public boolean match(Node<?> input);
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/RegexNamePredicate.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/RegexNamePredicate.java
new file mode 100644
index 0000000..75cc72d
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/RegexNamePredicate.java
@@ -0,0 +1,40 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+import java.util.regex.Pattern;
+
+/**
+ * Match a node based on name
+ */
+public class RegexNamePredicate implements Predicate
+{
+    private final Pattern pat;
+
+    public RegexNamePredicate(String regex)
+    {
+        this.pat = Pattern.compile(regex);
+    }
+
+    @Override
+    public boolean match(Node<?> node)
+    {
+        return pat.matcher(node.getName()).matches();
+    }
+}
\ No newline at end of file
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Selection.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Selection.java
new file mode 100644
index 0000000..c24dbf3
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Selection.java
@@ -0,0 +1,129 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+/**
+ * Represents a selection criteria.
+ * <p>
+ * Each <code>Selection</code> can be used [0..n] times in the graph. The <code>Selection</code> must contain a unique
+ * 'criteria' description that how selection was determined.
+ */
+public class Selection
+{
+    private final boolean explicit;
+    private final String criteria;
+
+    public Selection(String criteria)
+    {
+        this(criteria,true);
+    }
+
+    /**
+     * The Selection criteria
+     * 
+     * @param criteria
+     *            the selection criteria
+     * @param explicit
+     *            true if explicitly selected, false if transitively selected.
+     */
+    public Selection(String criteria, boolean explicit)
+    {
+        this.criteria = criteria;
+        this.explicit = explicit;
+    }
+
+    public Selection asTransitive()
+    {
+        if (this.explicit)
+        {
+            return new Selection(criteria,false);
+        }
+        return this;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+        {
+            return true;
+        }
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+        Selection other = (Selection)obj;
+        if (explicit != other.explicit)
+        {
+            return false;
+        }
+        if (criteria == null)
+        {
+            if (other.criteria != null)
+            {
+                return false;
+            }
+        }
+        else if (!criteria.equals(other.criteria))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Get the criteria for this selection
+     * @return the criteria
+     */
+    public String getCriteria()
+    {
+        return criteria;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = (prime * result) + (explicit ? 1231 : 1237);
+        result = (prime * result) + ((criteria == null) ? 0 : criteria.hashCode());
+        return result;
+    }
+
+    public boolean isExplicit()
+    {
+        return explicit;
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        if (!explicit)
+        {
+            str.append("<transitive from> ");
+        }
+        str.append(criteria);
+        return str.toString();
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/UniqueCriteriaPredicate.java b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/UniqueCriteriaPredicate.java
new file mode 100644
index 0000000..2400eee
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/graph/UniqueCriteriaPredicate.java
@@ -0,0 +1,63 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+/**
+ * Match against a specific {@link Selection#getCriteria()}, where
+ * there are no other {@link Selection#isExplicit()} specified.
+ */
+public class UniqueCriteriaPredicate implements Predicate
+{
+    private final String criteria;
+
+    public UniqueCriteriaPredicate(String criteria)
+    {
+        this.criteria = criteria;
+    }
+
+    @Override
+    public boolean match(Node<?> node)
+    {
+        if (node.getSelections().isEmpty())
+        {
+            // Empty selection list (no uniqueness to it)
+            return false;
+        }
+        
+        // Assume no match
+        boolean ret = false;
+        
+        for (Selection selection : node.getSelections())
+        {
+            if (criteria.equalsIgnoreCase(selection.getCriteria()))
+            {
+                // Found a match
+                ret = true;
+                continue; // this criteria is always valid.
+            }
+            else if (selection.isExplicit())
+            {
+                // Automatic failure
+                return false;
+            }
+        }
+
+        return ret;
+    }
+}
\ No newline at end of file
diff --git a/jetty-start/src/main/resources/org/eclipse/jetty/start/base-home-warning.txt b/jetty-start/src/main/resources/org/eclipse/jetty/start/base-home-warning.txt
deleted file mode 100644
index a34bfdb..0000000
--- a/jetty-start/src/main/resources/org/eclipse/jetty/start/base-home-warning.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-============================================================================
-WARNING: Jetty is starting using LEGACY behavior. 
-
-A proper {jetty.base} should be configured with no changes being made to the {jetty.home} directory.
-
-Please see http://www.eclipse.org/jetty/documentation/current/startup.html
-
-A demo-base directory has been provided as an example of this setup.
-
-  $ cd demo-base
-  $ java -jar ../start.jar
-
-This warning may be disabled by setting the system property
-
-  -Dorg.eclipse.jetty.start.home.warning=false
-============================================================================
diff --git a/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt b/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt
index eacb16b..413160b 100644
--- a/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt
+++ b/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt
@@ -34,7 +34,12 @@
                    a sub process. This can be used when start.ini
                    contains -X or -D arguments, but creates an extra
                    JVM instance.
-
+                   
+  --exec-properties=<filename>
+                   Assign a fixed name to the file used to transfer 
+                   properties to the sub process. This allows the 
+                   generated properties file to be saved and reused.
+                   Without this option, a temporary file is used.
 
 Debug and Start Logging:
 ------------------------
@@ -67,7 +72,7 @@
                    Temporarily enable a module from the command line.
                    Note: this can also be used in the ${jetty.base}/start.ini
                    or ${jetty.base}/start.d/*.ini files.
-
+                   
   --add-to-start=<modulename>(,<modulename>)*
                    Enable a module by appending lines to the
                    ${jetty.base}/start.ini file.
@@ -94,10 +99,25 @@
                    See http://graphviz.org/ for details on how to post-process
                    this file into the output best suited for your needs.
                    
-  --create-files   Create any missing files that are required by initialised 
+  --create-files   Create any missing files that are required by initialized 
                    modules.  This may download a file from the network if the 
                    module provides a URL.
 
+  --skip-file-validation=<modulename>(,<modulename)*
+                   Disable the [files] section validation of content
+                   in the ${jetty.base} directory for a specific
+                   module.  Useful for modules that have downloadable
+                   content that is being overridden with alternatives
+                   in the ${jetty.base} directory.
+                   CAUTION: 
+                     This advanced option is for administrators that
+                     fully understand the configuration of their
+                     ${jetty.base} and are willing to forego some of the
+                     safety checks built into the jetty-start mechanism.
+                     
+  --approve-all-licenses
+                   Approve all license questions. Useful for enabling 
+                   modules from a script that does not require user interaction.
 
 Startup / Shutdown Command Line:
 --------------------------------
@@ -118,7 +138,7 @@
 
     STOP.KEY=[alphanumeric]
       The passphrase defined to stop the server.
-      Requried along with STOP.PORT if you want to use the --stop option above.
+      Required along with STOP.PORT if you want to use the --stop option above.
 
     STOP.WAIT=[number]
       The time (in seconds) to wait for confirmation that the running
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java
index aa4163f..cd110dc 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java
@@ -18,7 +18,10 @@
 

 package org.eclipse.jetty.start;

 

-import static org.hamcrest.Matchers.*;

+import static org.hamcrest.Matchers.containsInAnyOrder;

+import static org.hamcrest.Matchers.containsString;

+import static org.hamcrest.Matchers.notNullValue;

+import static org.hamcrest.Matchers.startsWith;

 

 import java.io.File;

 import java.io.IOException;

@@ -57,7 +60,7 @@
                 System.out.printf(" %s%n",path);

             }

         }

-        Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));

+        Assert.assertThat(message + ": " + Utils.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));

     }

     

     public static void assertPathList(BaseHome hb, String message, List<String> expected, List<Path> paths)

@@ -81,7 +84,7 @@
                 System.out.printf(" %s%n",path);

             }

         }

-        Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));

+        Assert.assertThat(message + ": " + Utils.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));

     }

 

     public static void assertFileList(BaseHome hb, String message, List<String> expected, List<File> files)

@@ -91,7 +94,7 @@
         {

             actual.add(hb.toShortForm(file));

         }

-        Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));

+        Assert.assertThat(message + ": " + Utils.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));

     }

 

     @Test

diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java
index 2db8a81..d788593 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java
@@ -18,7 +18,8 @@
 

 package org.eclipse.jetty.start;

 

-import static org.hamcrest.Matchers.*;

+import static org.hamcrest.Matchers.greaterThan;

+import static org.hamcrest.Matchers.greaterThanOrEqualTo;

 

 import java.io.File;

 import java.io.FileNotFoundException;

@@ -47,11 +48,12 @@
      *            the StartArgs that has been processed via {@link Main#processCommandLine(String[])}

      * @param filename

      *            the filename of the assertion values

-     * @throws IOException

+     * @throws FileNotFoundException if unable to find the configuration

+     * @throws IOException if unable to process the configuration

      */

     public static void assertConfiguration(BaseHome baseHome, StartArgs args, String filename) throws FileNotFoundException, IOException

     {

-        Path testResourcesDir = MavenTestingUtils.getTestResourcesDir().toPath().toAbsolutePath();

+        Path testResourcesDir = MavenTestingUtils.getTestResourcesDir().toPath().toRealPath();

         File file = MavenTestingUtils.getTestResourceFile(filename);

         TextFile textFile = new TextFile(file.toPath());

 

@@ -101,7 +103,8 @@
         {

             String name = prop.key;

             if ("jetty.home".equals(name) || "jetty.base".equals(name) ||

-                "user.dir".equals(name) || prop.origin.equals(Props.ORIGIN_SYSPROP))

+                "user.dir".equals(name) || prop.origin.equals(Props.ORIGIN_SYSPROP) ||

+                name.startsWith("java."))

             {

                 // strip these out from assertion, to make assertions easier.

                 continue;

diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/DistTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/DistTest.java
new file mode 100644
index 0000000..f69e1af
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/DistTest.java
@@ -0,0 +1,211 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import static org.eclipse.jetty.start.StartMatchers.fileExists;
+import static org.eclipse.jetty.start.StartMatchers.notPathExists;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Test various things with a semi-valid src/test/resources/dist-home/
+ */
+public class DistTest
+{
+    @Rule
+    public TestingDir testdir = new TestingDir();
+
+    @Rule
+    public SystemExitAsException exitrule = new SystemExitAsException();
+
+    private void execMain(List<String> cmds) throws Exception
+    {
+        int len = cmds.size();
+        String args[] = cmds.toArray(new String[len]);
+
+        Main main = new Main();
+        StartArgs startArgs = main.processCommandLine(args);
+        main.start(startArgs);
+    }
+
+    public List<String> getBaseCommandLine(Path basePath)
+    {
+        List<String> cmds = new ArrayList<String>();
+        cmds.add("-Djava.io.tmpdir=" + MavenTestingUtils.getTargetDir().getAbsolutePath());
+        cmds.add("-Djetty.home=" + MavenTestingUtils.getTestResourceDir("dist-home").getAbsolutePath());
+        cmds.add("-Djetty.base=" + basePath.normalize().toAbsolutePath().toString());
+        cmds.add("--testing-mode");
+
+        return cmds;
+    }
+
+    @Test
+    public void testLikeDistro_SetupHome() throws Exception
+    {
+        Path basePath = testdir.getEmptyPathDir();
+
+        List<String> cmds = getBaseCommandLine(basePath);
+
+        cmds.add("--add-to-start=deploy,websocket,ext,resources,jsp,jstl,http");
+
+        execMain(cmds);
+    }
+    
+    @Test
+    public void testAddJstl() throws Exception
+    {
+        Path basePath = testdir.getEmptyPathDir();
+
+        List<String> cmds = getBaseCommandLine(basePath);
+        cmds.add("--add-to-start=jstl");
+        execMain(cmds);
+        
+        Path startIni = basePath.resolve("start.ini");
+        assertThat("start.ini", startIni, fileExists());
+
+        List<String> startIniLines = new TextFile(startIni).getLines();
+        // Modules that should be present
+        assertThat("start.ini", startIniLines, hasItem("--module=jstl"));
+        assertThat("start.ini", startIniLines, hasItem("--module=server"));
+        
+        // Test for modules that should not be present.
+        // Namely modules that are transitive and without ini-template.
+        assertThat("start.ini", startIniLines, not(hasItem("--module=servlet")));
+        assertThat("start.ini", startIniLines, not(hasItem("--module=apache-jsp")));
+        assertThat("start.ini", startIniLines, not(hasItem("--module=apache-jstl")));
+        assertThat("start.ini", startIniLines, not(hasItem("--module=jndi")));
+        assertThat("start.ini", startIniLines, not(hasItem("--module=security")));
+        assertThat("start.ini", startIniLines, not(hasItem("--module=webapp")));
+        assertThat("start.ini", startIniLines, not(hasItem("--module=plus")));
+        assertThat("start.ini", startIniLines, not(hasItem("--module=annotations")));
+        assertThat("start.ini", startIniLines, not(hasItem("--module=jsp")));
+        
+    }
+    
+    /**
+     * Test for https://bugs.eclipse.org/452329
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testReAddServerModule() throws Exception
+    {
+        Path basePath = testdir.getEmptyPathDir();
+
+        List<String> cmds = getBaseCommandLine(basePath);
+        cmds.add("--add-to-startd=http");
+        execMain(cmds);
+        
+        Path httpIni = basePath.resolve("start.d/http.ini");
+        Path serverIni = basePath.resolve("start.d/server.ini");
+        
+        assertThat("start.d/http.ini", httpIni, fileExists());
+        assertThat("start.d/server.ini", serverIni, fileExists());
+        
+        // Delete server.ini
+        Files.deleteIfExists(serverIni);
+        
+        // Attempt to re-add via 'server' module reference
+        cmds = getBaseCommandLine(basePath);
+        cmds.add("--add-to-startd=server");
+        execMain(cmds);
+        
+        assertThat("start.d/server.ini", serverIni, fileExists());
+    }
+    
+    /**
+     * Test for https://bugs.eclipse.org/452329
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testReAddServerViaHttpModule() throws Exception
+    {
+        Path basePath = testdir.getEmptyPathDir();
+
+        List<String> cmds = getBaseCommandLine(basePath);
+        cmds.add("--add-to-startd=http");
+        execMain(cmds);
+        
+        Path httpIni = basePath.resolve("start.d/http.ini");
+        Path serverIni = basePath.resolve("start.d/server.ini");
+        
+        assertThat("start.d/http.ini", httpIni, fileExists());
+        assertThat("start.d/server.ini", serverIni, fileExists());
+        
+        // Delete server.ini
+        Files.deleteIfExists(serverIni);
+        
+        // Attempt to re-add via 'http' module reference
+        cmds = getBaseCommandLine(basePath);
+        cmds.add("--add-to-startd=http");
+        execMain(cmds);
+        
+        assertThat("start.d/server.ini", serverIni, fileExists());
+    }
+    
+    /**
+     * Test for https://bugs.eclipse.org/452329
+     * @throws Exception on test failure
+     */
+    @Test
+    public void testReAddHttpThenDeployViaStartD() throws Exception
+    {
+        Path basePath = testdir.getEmptyPathDir();
+
+        List<String> cmds = getBaseCommandLine(basePath);
+        cmds.add("--add-to-start=http");
+        execMain(cmds);
+        
+        Path startIni = basePath.resolve("start.ini");
+        assertThat("start.ini", startIni, fileExists());
+
+        // Now add 'deploy' module.
+        cmds = getBaseCommandLine(basePath);
+        cmds.add("--add-to-startd=deploy");
+        execMain(cmds);
+        
+        // The following files should not exist (as its already defined in /start.ini)
+        Path serverIni = basePath.resolve("start.d/server.ini");
+        assertThat("start.d/server.ini", serverIni, notPathExists());
+    }
+    
+    @Test
+    @Ignore("See https://bugs.eclipse.org/451973")
+    public void testLikeDistro_SetupDemoBase() throws Exception
+    {
+        Path basePath = testdir.getEmptyPathDir();
+
+        List<String> cmds = getBaseCommandLine(basePath);
+
+        cmds.add("--add-to-start=continuation,deploy,websocket,ext,resources,client,annotations,jndi,servlets");
+        cmds.add("--add-to-startd=jsp,jstl,http,https");
+
+        execMain(cmds);
+    }
+}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/FSTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/FSTest.java
index f8a9e03..b689700 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/FSTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/FSTest.java
@@ -50,6 +50,7 @@
     
     /**
      * Utility method used by other test cases
+     * @param expected the expected String paths to be converted (in place)
      */
     public static void toOsSeparators(List<String> expected)
     {
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/FileArgTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/FileArgTest.java
index da417cd..116d325 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/FileArgTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/FileArgTest.java
@@ -18,8 +18,9 @@
 
 package org.eclipse.jetty.start;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/IncludeJettyDirTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/IncludeJettyDirTest.java
index cbb3c7c..308207b 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/IncludeJettyDirTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/IncludeJettyDirTest.java
@@ -18,9 +18,12 @@
 
 package org.eclipse.jetty.start;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
 
 import java.io.File;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -67,13 +70,13 @@
     @Rule
     public TestingDir testdir = new TestingDir();
 
-    private MainResult runMain(File baseDir, File homeDir, String... cmdLineArgs) throws Exception
+    private MainResult runMain(Path baseDir, Path homeDir, String... cmdLineArgs) throws Exception
     {
         MainResult ret = new MainResult();
         ret.main = new Main();
         List<String> cmdLine = new ArrayList<>();
-        cmdLine.add("jetty.home=" + homeDir.getAbsolutePath());
-        cmdLine.add("jetty.base=" + baseDir.getAbsolutePath());
+        cmdLine.add("jetty.home=" + homeDir.toString());
+        cmdLine.add("jetty.base=" + baseDir.toString());
         // cmdLine.add("--debug");
         for (String arg : cmdLineArgs)
         {
@@ -87,15 +90,15 @@
     public void testNoExtras() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1");
+                "jetty.http.host=127.0.0.1");
 
         // Simple command line - no reference to include-jetty-dirs
         MainResult result = runMain(base,home);
@@ -105,66 +108,66 @@
         expectedSearchOrder.add("${jetty.home}");
         result.assertSearchOrder(expectedSearchOrder);
 
-        result.assertProperty("jetty.host","127.0.0.1");
+        result.assertProperty("jetty.http.host","127.0.0.1");
     }
 
     @Test
     public void testCommandLine_1Extra() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
-        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+        TestEnv.makeFile(common,"start.ini","jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1");
+                "jetty.http.host=127.0.0.1");
 
         // Simple command line reference to include-jetty-dir
         MainResult result = runMain(base,home,
         // direct reference via path
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "--include-jetty-dir=" + common.toString());
 
         List<String> expectedSearchOrder = new ArrayList<>();
         expectedSearchOrder.add("${jetty.base}");
-        expectedSearchOrder.add(common.getAbsolutePath());
+        expectedSearchOrder.add(common.toString());
         expectedSearchOrder.add("${jetty.home}");
         result.assertSearchOrder(expectedSearchOrder);
 
-        result.assertProperty("jetty.host","127.0.0.1");
-        result.assertProperty("jetty.port","8080"); // from 'common'
+        result.assertProperty("jetty.http.host","127.0.0.1");
+        result.assertProperty("jetty.http.port","8080"); // from 'common'
     }
 
     @Test
     public void testCommandLine_1Extra_FromSimpleProp() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
-        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+        TestEnv.makeFile(common,"start.ini","jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1");
+                "jetty.http.host=127.0.0.1");
 
         // Simple command line reference to include-jetty-dir via property (also on command line)
         MainResult result = runMain(base,home,
         // property
-                "my.common=" + common.getAbsolutePath(),
+                "my.common=" + common.toString(),
                 // reference via property
                 "--include-jetty-dir=${my.common}");
 
@@ -174,39 +177,39 @@
         expectedSearchOrder.add("${jetty.home}");
         result.assertSearchOrder(expectedSearchOrder);
 
-        result.assertProperty("jetty.host","127.0.0.1");
-        result.assertProperty("jetty.port","8080"); // from 'common'
+        result.assertProperty("jetty.http.host","127.0.0.1");
+        result.assertProperty("jetty.http.port","8080"); // from 'common'
     }
 
     @Test
     public void testCommandLine_1Extra_FromPropPrefix() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create opt
-        File opt = testdir.getFile("opt");
+        Path opt = testdir.getPathFile("opt");
         FS.ensureEmpty(opt);
 
         // Create common
-        File common = new File(opt,"common");
+        Path common = opt.resolve("common");
         FS.ensureEmpty(common);
-        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+        TestEnv.makeFile(common,"start.ini","jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1");
+                "jetty.http.host=127.0.0.1");
 
         String dirRef = "${my.opt}" + File.separator + "common";
 
         // Simple command line reference to include-jetty-dir via property (also on command line)
         MainResult result = runMain(base,home,
         // property to 'opt' dir
-                "my.opt=" + opt.getAbsolutePath(),
+                "my.opt=" + opt.toString(),
                 // reference via property prefix
                 "--include-jetty-dir=" + dirRef);
 
@@ -216,39 +219,39 @@
         expectedSearchOrder.add("${jetty.home}");
         result.assertSearchOrder(expectedSearchOrder);
 
-        result.assertProperty("jetty.host","127.0.0.1");
-        result.assertProperty("jetty.port","8080"); // from 'common'
+        result.assertProperty("jetty.http.host","127.0.0.1");
+        result.assertProperty("jetty.http.port","8080"); // from 'common'
     }
 
     @Test
     public void testCommandLine_1Extra_FromCompoundProp() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create opt
-        File opt = testdir.getFile("opt");
+        Path opt = testdir.getPathFile("opt");
         FS.ensureEmpty(opt);
 
         // Create common
-        File common = new File(opt,"common");
+        Path common = opt.resolve("common");
         FS.ensureEmpty(common);
-        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+        TestEnv.makeFile(common,"start.ini","jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1");
+                "jetty.http.host=127.0.0.1");
 
         String dirRef = "${my.opt}" + File.separator + "${my.dir}";
 
         // Simple command line reference to include-jetty-dir via property (also on command line)
         MainResult result = runMain(base,home,
         // property to 'opt' dir
-                "my.opt=" + opt.getAbsolutePath(),
+                "my.opt=" + opt.toString(),
                 // property to commmon dir name
                 "my.dir=common",
                 // reference via property prefix
@@ -260,148 +263,148 @@
         expectedSearchOrder.add("${jetty.home}");
         result.assertSearchOrder(expectedSearchOrder);
 
-        result.assertProperty("jetty.host","127.0.0.1");
-        result.assertProperty("jetty.port","8080"); // from 'common'
+        result.assertProperty("jetty.http.host","127.0.0.1");
+        result.assertProperty("jetty.http.port","8080"); // from 'common'
     }
 
     @Test
     public void testRefCommon() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
-        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+        TestEnv.makeFile(common,"start.ini","jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString());
 
         MainResult result = runMain(base,home);
 
         List<String> expectedSearchOrder = new ArrayList<>();
         expectedSearchOrder.add("${jetty.base}");
-        expectedSearchOrder.add(common.getAbsolutePath());
+        expectedSearchOrder.add(common.toString());
         expectedSearchOrder.add("${jetty.home}");
         result.assertSearchOrder(expectedSearchOrder);
 
-        result.assertProperty("jetty.host","127.0.0.1");
-        result.assertProperty("jetty.port","8080"); // from 'common'
+        result.assertProperty("jetty.http.host","127.0.0.1");
+        result.assertProperty("jetty.http.port","8080"); // from 'common'
     }
 
     @Test
     public void testRefCommonAndCorp() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
-        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+        TestEnv.makeFile(common,"start.ini","jetty.http.port=8080");
 
         // Create corp
-        File corp = testdir.getFile("corp");
+        Path corp = testdir.getPathFile("corp");
         FS.ensureEmpty(corp);
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath(), //
-                "--include-jetty-dir=" + corp.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString(), //
+                "--include-jetty-dir=" + corp.toString());
 
         MainResult result = runMain(base,home);
 
         List<String> expectedSearchOrder = new ArrayList<>();
         expectedSearchOrder.add("${jetty.base}");
-        expectedSearchOrder.add(common.getAbsolutePath());
-        expectedSearchOrder.add(corp.getAbsolutePath());
+        expectedSearchOrder.add(common.toString());
+        expectedSearchOrder.add(corp.toString());
         expectedSearchOrder.add("${jetty.home}");
         result.assertSearchOrder(expectedSearchOrder);
 
-        result.assertProperty("jetty.host","127.0.0.1");
-        result.assertProperty("jetty.port","8080"); // from 'common'
+        result.assertProperty("jetty.http.host","127.0.0.1");
+        result.assertProperty("jetty.http.port","8080"); // from 'common'
     }
 
     @Test
     public void testRefCommonRefCorp() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create corp
-        File corp = testdir.getFile("corp");
+        Path corp = testdir.getPathFile("corp");
         FS.ensureEmpty(corp);
-        TestEnv.makeFile(corp,"start.ini","jetty.port=9090");
+        TestEnv.makeFile(corp,"start.ini","jetty.http.port=9090");
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
         TestEnv.makeFile(common,"start.ini", //
-                "--include-jetty-dir=" + corp.getAbsolutePath(), //
-                "jetty.port=8080");
+                "--include-jetty-dir=" + corp.toString(), //
+                "jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString());
 
         MainResult result = runMain(base,home);
 
         List<String> expectedSearchOrder = new ArrayList<>();
         expectedSearchOrder.add("${jetty.base}");
-        expectedSearchOrder.add(common.getAbsolutePath());
-        expectedSearchOrder.add(corp.getAbsolutePath());
+        expectedSearchOrder.add(common.toString());
+        expectedSearchOrder.add(corp.toString());
         expectedSearchOrder.add("${jetty.home}");
         result.assertSearchOrder(expectedSearchOrder);
 
-        result.assertProperty("jetty.host","127.0.0.1");
-        result.assertProperty("jetty.port","8080"); // from 'common'
+        result.assertProperty("jetty.http.host","127.0.0.1");
+        result.assertProperty("jetty.http.port","8080"); // from 'common'
     }
 
     @Test
     public void testRefCommonRefCorp_FromSimpleProps() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create corp
-        File corp = testdir.getFile("corp");
+        Path corp = testdir.getPathFile("corp");
         FS.ensureEmpty(corp);
         TestEnv.makeFile(corp,"start.ini", //
-                "jetty.port=9090");
+                "jetty.http.port=9090");
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
         TestEnv.makeFile(common,"start.ini", //
-                "my.corp=" + corp.getAbsolutePath(), //
+                "my.corp=" + corp.toString(), //
                 "--include-jetty-dir=${my.corp}", //
-                "jetty.port=8080");
+                "jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "my.common=" + common.getAbsolutePath(), //
+                "jetty.http.host=127.0.0.1",//
+                "my.common=" + common.toString(), //
                 "--include-jetty-dir=${my.common}");
 
         MainResult result = runMain(base,home);
@@ -413,138 +416,138 @@
         expectedSearchOrder.add("${jetty.home}");
         result.assertSearchOrder(expectedSearchOrder);
 
-        result.assertProperty("jetty.host","127.0.0.1");
-        result.assertProperty("jetty.port","8080"); // from 'common'
+        result.assertProperty("jetty.http.host","127.0.0.1");
+        result.assertProperty("jetty.http.port","8080"); // from 'common'
     }
 
     @Test
     public void testRefCommonRefCorp_CmdLineRef() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create devops
-        File devops = testdir.getFile("devops");
+        Path devops = testdir.getPathFile("devops");
         FS.ensureEmpty(devops);
         TestEnv.makeFile(devops,"start.ini", //
                 "--module=logging", //
-                "jetty.port=2222");
+                "jetty.http.port=2222");
 
         // Create corp
-        File corp = testdir.getFile("corp");
+        Path corp = testdir.getPathFile("corp");
         FS.ensureEmpty(corp);
         TestEnv.makeFile(corp,"start.ini", //
-                "jetty.port=9090");
+                "jetty.http.port=9090");
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
         TestEnv.makeFile(common,"start.ini", //
-                "--include-jetty-dir=" + corp.getAbsolutePath(), //
-                "jetty.port=8080");
+                "--include-jetty-dir=" + corp.toString(), //
+                "jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString());
 
         MainResult result = runMain(base,home,
         // command line provided include-jetty-dir ref
-                "--include-jetty-dir=" + devops.getAbsolutePath());
+                "--include-jetty-dir=" + devops.toString());
 
         List<String> expectedSearchOrder = new ArrayList<>();
         expectedSearchOrder.add("${jetty.base}");
-        expectedSearchOrder.add(devops.getAbsolutePath());
-        expectedSearchOrder.add(common.getAbsolutePath());
-        expectedSearchOrder.add(corp.getAbsolutePath());
+        expectedSearchOrder.add(devops.toString());
+        expectedSearchOrder.add(common.toString());
+        expectedSearchOrder.add(corp.toString());
         expectedSearchOrder.add("${jetty.home}");
         result.assertSearchOrder(expectedSearchOrder);
 
-        result.assertProperty("jetty.host","127.0.0.1");
-        result.assertProperty("jetty.port","2222"); // from 'devops'
+        result.assertProperty("jetty.http.host","127.0.0.1");
+        result.assertProperty("jetty.http.port","2222"); // from 'devops'
     }
 
     @Test
     public void testRefCommonRefCorp_CmdLineProp() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create corp
-        File corp = testdir.getFile("corp");
+        Path corp = testdir.getPathFile("corp");
         FS.ensureEmpty(corp);
         TestEnv.makeFile(corp,"start.ini", //
-                "jetty.port=9090");
+                "jetty.http.port=9090");
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
         TestEnv.makeFile(common,"start.ini", //
-                "--include-jetty-dir=" + corp.getAbsolutePath(), //
-                "jetty.port=8080");
+                "--include-jetty-dir=" + corp.toString(), //
+                "jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString());
 
         MainResult result = runMain(base,home,
         // command line property should override all others
-                "jetty.port=7070");
+                "jetty.http.port=7070");
 
         List<String> expectedSearchOrder = new ArrayList<>();
         expectedSearchOrder.add("${jetty.base}");
-        expectedSearchOrder.add(common.getAbsolutePath());
-        expectedSearchOrder.add(corp.getAbsolutePath());
+        expectedSearchOrder.add(common.toString());
+        expectedSearchOrder.add(corp.toString());
         expectedSearchOrder.add("${jetty.home}");
         result.assertSearchOrder(expectedSearchOrder);
 
-        result.assertProperty("jetty.host","127.0.0.1");
-        result.assertProperty("jetty.port","7070"); // from command line
+        result.assertProperty("jetty.http.host","127.0.0.1");
+        result.assertProperty("jetty.http.port","7070"); // from command line
     }
 
     @Test
     public void testBadDoubleRef() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
 
         // Create corp
-        File corp = testdir.getFile("corp");
+        Path corp = testdir.getPathFile("corp");
         FS.ensureEmpty(corp);
         TestEnv.makeFile(corp,"start.ini",
         // standard property
-                "jetty.port=9090",
+                "jetty.http.port=9090",
                 // INTENTIONAL BAD Reference (duplicate)
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "--include-jetty-dir=" + common.toString());
 
         // Populate common
         TestEnv.makeFile(common,"start.ini",
         // standard property
-                "jetty.port=8080",
+                "jetty.http.port=8080",
                 // reference to corp
-                "--include-jetty-dir=" + corp.getAbsolutePath());
+                "--include-jetty-dir=" + corp.toString());
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString());
 
         try
         {
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/JarVersionTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/JarVersionTest.java
index 2ec06a1..376bad8 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/JarVersionTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/JarVersionTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.start;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
 
 import java.io.File;
 
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/LicenseTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/LicenseTest.java
deleted file mode 100644
index cad8505..0000000
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/LicenseTest.java
+++ /dev/null
@@ -1,181 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.start;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.jetty.toolchain.test.IO;
-import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
-import org.eclipse.jetty.toolchain.test.OS;
-import org.eclipse.jetty.toolchain.test.TestingDir;
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- * Test various license handling.
- */
-public class LicenseTest
-{
-    @Rule
-    public TestingDir testdir = new TestingDir();
-
-    @Rule
-    public SystemExitAsException exitrule = new SystemExitAsException();
-
-    private String assertFileExists(File basePath, String name) throws IOException
-    {
-        File file = new File(basePath, OS.separators(name));
-        FS.exists(file.toPath());
-        return IO.readToString(file);
-    }
-
-    private void execMain(List<String> cmds) throws Exception
-    {
-        int len = cmds.size();
-        String args[] = cmds.toArray(new String[len]);
-
-        System.err.printf("%n## Exec: %s%n", Main.join(cmds,", "));
-        Main main = new Main();
-        StartArgs startArgs = main.processCommandLine(args);
-        main.start(startArgs);
-    }
-
-    public List<String> getBaseCommandLine(File basePath)
-    {
-        List<String> cmds = new ArrayList<String>();
-        cmds.add("-Djava.io.tmpdir=" + MavenTestingUtils.getTargetDir().getAbsolutePath());
-        cmds.add("-Djetty.home=" + MavenTestingUtils.getTestResourceDir("dist-home").getAbsolutePath());
-        cmds.add("-Djetty.base=" + basePath.getAbsolutePath());
-        cmds.add("--testing-mode");
-
-        return cmds;
-    }
-
-    @Test
-    public void testAdd_NoLicensed() throws Exception
-    {
-        File basePath = testdir.getEmptyDir();
-
-        List<String> cmds = getBaseCommandLine(basePath);
-
-        cmds.add("--add-to-start=http,deploy");
-
-        execMain(cmds);
-    }
-
-    @Test
-    public void testAdd_CDI_Licensed() throws Exception
-    {
-        File basePath = testdir.getEmptyDir();
-
-        List<String> cmds = getBaseCommandLine(basePath);
-
-        cmds.add("-Dorg.eclipse.jetty.start.ack.license.cdi=true");
-        cmds.add("--add-to-start=cdi");
-
-        execMain(cmds);
-    }
-    
-    @Test
-    public void testAdd_SPDY_Licensed() throws Exception
-    {
-        File basePath = testdir.getEmptyDir();
-
-        List<String> cmds = getBaseCommandLine(basePath);
-
-        cmds.add("-Dorg.eclipse.jetty.start.ack.license.protonego-impl=true");
-        cmds.add("--add-to-start=spdy");
-
-        execMain(cmds);
-        
-        String contents = assertFileExists(basePath, "start.ini");
-        assertThat("Contents",contents,containsString("--module=spdy"+System.lineSeparator()));
-    }
-    
-    @Test
-    public void testAdd_HttpSpdy_Then_Deploy() throws Exception
-    {
-        File basePath = testdir.getEmptyDir();
-
-        List<String> cmds = getBaseCommandLine(basePath);
-
-        cmds.add("-Dorg.eclipse.jetty.start.ack.license.protonego-impl=true");
-        cmds.add("--add-to-start=http,spdy");
-
-        execMain(cmds);
-        
-        String contents = assertFileExists(basePath, "start.ini");
-        assertThat("Contents",contents,containsString("--module=http"+System.lineSeparator()));
-        assertThat("Contents",contents,containsString("--module=spdy"+System.lineSeparator()));
-        
-        // now request deploy (no license check should occur)
-        List<String> cmds2 = getBaseCommandLine(basePath);
-        cmds2.add("--add-to-start=deploy");
-        execMain(cmds2);
-
-        contents = assertFileExists(basePath, "start.ini");
-        assertThat("Contents",contents,containsString("--module=deploy"+System.lineSeparator()));
-        assertThat("Contents",contents,containsString("--module=spdy"+System.lineSeparator()));
-    }
-    
-    @Test
-    public void testCreate_SPDY_Licensed() throws Exception
-    {
-        File basePath = testdir.getEmptyDir();
-
-        List<String> cmds = getBaseCommandLine(basePath);
-
-        cmds.add("-Dorg.eclipse.jetty.start.ack.license.protonego-impl=true");
-        
-        StringReader startIni = new StringReader("--module=spdy\n");
-        try (FileWriter writer = new FileWriter(new File(basePath,"start.ini")))
-        {
-            IO.copy(startIni,writer);
-        }
-
-        execMain(cmds);
-    }
-
-    @Test
-    public void testCreate_CDI_Licensed() throws Exception
-    {
-        File basePath = testdir.getEmptyDir();
-
-        List<String> cmds = getBaseCommandLine(basePath);
-
-        cmds.add("-Dorg.eclipse.jetty.start.ack.license.cdi=true");
-        cmds.add("--create-files");
-
-        StringReader startIni = new StringReader("--module=cdi\n");
-        try (FileWriter writer = new FileWriter(new File(basePath,"start.ini")))
-        {
-            IO.copy(startIni,writer);
-        }
-
-        execMain(cmds);
-    }
-}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/LicensingTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/LicensingTest.java
new file mode 100644
index 0000000..5a25b39
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/LicensingTest.java
@@ -0,0 +1,182 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.toolchain.test.IO;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.OS;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Test various license handling.
+ */
+public class LicensingTest
+{
+    @Rule
+    public TestingDir testdir = new TestingDir();
+
+    @Rule
+    public SystemExitAsException exitrule = new SystemExitAsException();
+
+    private String assertFileExists(Path basePath, String name) throws IOException
+    {
+        Path file = basePath.resolve(OS.separators(name));
+        FS.exists(file);
+        return IO.readToString(file.toFile());
+    }
+
+    private void execMain(List<String> cmds) throws Exception
+    {
+        int len = cmds.size();
+        String args[] = cmds.toArray(new String[len]);
+
+        System.err.printf("%n## Exec: %s%n", Utils.join(cmds,", "));
+        Main main = new Main();
+        StartArgs startArgs = main.processCommandLine(args);
+        main.start(startArgs);
+    }
+
+    public List<String> getBaseCommandLine(Path basePath)
+    {
+        List<String> cmds = new ArrayList<String>();
+        cmds.add("-Djava.io.tmpdir=" + MavenTestingUtils.getTargetDir().getAbsolutePath());
+        cmds.add("-Djetty.home=" + MavenTestingUtils.getTestResourceDir("dist-home").getAbsolutePath());
+        cmds.add("-Djetty.base=" + basePath.toString());
+        cmds.add("--testing-mode");
+
+        return cmds;
+    }
+
+    @Test
+    public void testAdd_NoLicensed() throws Exception
+    {
+        Path basePath = testdir.getEmptyPathDir();
+
+        List<String> cmds = getBaseCommandLine(basePath);
+
+        cmds.add("--add-to-start=http,deploy");
+
+        execMain(cmds);
+    }
+
+    @Test
+    public void testAdd_CDI_Licensed() throws Exception
+    {
+        Path basePath = testdir.getEmptyPathDir();
+
+        List<String> cmds = getBaseCommandLine(basePath);
+
+        cmds.add("-Dorg.eclipse.jetty.start.ack.licenses=true");
+        cmds.add("--add-to-start=cdi");
+
+        execMain(cmds);
+    }
+    
+    @Test
+    public void testAdd_HTTP2_Licensed() throws Exception
+    {
+        Path basePath = testdir.getEmptyPathDir();
+
+        List<String> cmds = getBaseCommandLine(basePath);
+
+        cmds.add("-Dorg.eclipse.jetty.start.ack.licenses=true");
+        cmds.add("--add-to-start=http2");
+
+        execMain(cmds);
+        
+        String contents = assertFileExists(basePath, "start.ini");
+        assertThat("Contents",contents,containsString("--module=http2"+System.lineSeparator()));
+    }
+    
+    @Test
+    public void testAdd_Http_Http2_Then_Deploy() throws Exception
+    {
+        Path basePath = testdir.getEmptyPathDir();
+
+        List<String> cmds = getBaseCommandLine(basePath);
+
+        cmds.add("-Dorg.eclipse.jetty.start.ack.license.protonego-impl=true");
+        cmds.add("--add-to-start=http,http2");
+
+        execMain(cmds);
+        
+        String contents = assertFileExists(basePath, "start.ini");
+        assertThat("Contents",contents,containsString("--module=http"+System.lineSeparator()));
+        assertThat("Contents",contents,containsString("--module=http2"+System.lineSeparator()));
+        
+        // now request deploy (no license check should occur)
+        List<String> cmds2 = getBaseCommandLine(basePath);
+        cmds2.add("--add-to-start=deploy");
+        execMain(cmds2);
+
+        contents = assertFileExists(basePath, "start.ini");
+        assertThat("Contents",contents,containsString("--module=deploy"+System.lineSeparator()));
+    }
+    
+    @Test
+    public void testCreate_HTTP2_Licensed() throws Exception
+    {
+        Path basePath = testdir.getEmptyPathDir();
+
+        List<String> cmds = getBaseCommandLine(basePath);
+
+        cmds.add("-Dorg.eclipse.jetty.start.ack.licenses=true");
+        cmds.add("--dry-run");
+        
+        StringReader startIni = new StringReader("--module=http2\n");
+        try (BufferedWriter writer = Files.newBufferedWriter(basePath.resolve("start.ini")))
+        {
+            IO.copy(startIni,writer);
+        }
+
+        execMain(cmds);
+    }
+
+    @Test
+    public void testCreate_CDI_Licensed() throws Exception
+    {
+        Path basePath = testdir.getEmptyPathDir();
+
+        List<String> cmds = getBaseCommandLine(basePath);
+
+        cmds.add("-Dorg.eclipse.jetty.start.ack.licenses=true");
+        cmds.add("--create-files");
+
+        StringReader startIni = new StringReader("--module=cdi\n");
+        try (BufferedWriter writer = Files.newBufferedWriter(basePath.resolve("start.ini")))
+        {
+            IO.copy(startIni,writer);
+        }
+
+        execMain(cmds);
+    }
+}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java
index 9740af2..865711e 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java
@@ -50,10 +50,10 @@
     public void testBasicProcessing() throws Exception
     {
         List<String> cmdLineArgs = new ArrayList<>();
-        File testJettyHome = MavenTestingUtils.getTestResourceDir("usecases/home").getAbsoluteFile();
+        Path testJettyHome = MavenTestingUtils.getTestResourceDir("dist-home").toPath().toRealPath();
         cmdLineArgs.add("user.dir=" + testJettyHome);
         cmdLineArgs.add("jetty.home=" + testJettyHome);
-        cmdLineArgs.add("jetty.port=9090");
+        // cmdLineArgs.add("jetty.http.port=9090");
 
         Main main = new Main();
         StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
@@ -83,13 +83,13 @@
     }
 
     @Test
-    @Ignore("Just a bit noisy for general testing")
+    @Ignore("Too noisy for general testing")
     public void testListConfig() throws Exception
     {
         List<String> cmdLineArgs = new ArrayList<>();
-        File testJettyHome = MavenTestingUtils.getTestResourceDir("usecases/home");
+        File testJettyHome = MavenTestingUtils.getTestResourceDir("dist-home");
+        cmdLineArgs.add("user.dir=" + testJettyHome);
         cmdLineArgs.add("jetty.home=" + testJettyHome);
-        cmdLineArgs.add("jetty.port=9090");
         cmdLineArgs.add("--list-config");
         // cmdLineArgs.add("--debug");
 
@@ -111,9 +111,9 @@
     {
         List<String> cmdLineArgs = new ArrayList<>();
 
-        File homePath = MavenTestingUtils.getTestResourceDir("usecases/home").getAbsoluteFile();
-        cmdLineArgs.add("jetty.home=" + homePath);
-        cmdLineArgs.add("user.dir=" + homePath);
+        Path homePath = MavenTestingUtils.getTestResourceDir("dist-home").toPath().toRealPath();
+        cmdLineArgs.add("jetty.home=" + homePath.toString());
+        cmdLineArgs.add("user.dir=" + homePath.toString());
 
         // JVM args
         cmdLineArgs.add("--exec");
@@ -121,11 +121,8 @@
         cmdLineArgs.add("-Xmx1024m");
 
         // Arbitrary Libs
-        Path extraJar = MavenTestingUtils.getTestResourceFile("extra-libs/example.jar").toPath().normalize();
-        Path extraDir = MavenTestingUtils.getTestResourceDir("extra-resources").toPath().normalize();
-        
-        extraJar = extraJar.toAbsolutePath();
-        extraDir = extraDir.toAbsolutePath();
+        Path extraJar = MavenTestingUtils.getTestResourceFile("extra-libs/example.jar").toPath().toRealPath();
+        Path extraDir = MavenTestingUtils.getTestResourceDir("extra-resources").toPath().toRealPath();
         
         assertThat("Extra Jar exists: " + extraJar,Files.exists(extraJar),is(true));
         assertThat("Extra Dir exists: " + extraDir,Files.exists(extraDir),is(true));
@@ -148,36 +145,35 @@
         StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
         BaseHome baseHome = main.getBaseHome();
 
-        assertThat("jetty.home",baseHome.getHome(),is(homePath.getAbsolutePath()));
-        assertThat("jetty.base",baseHome.getBase(),is(homePath.getAbsolutePath()));
+        assertThat("jetty.home",baseHome.getHome(),is(homePath.toString()));
+        assertThat("jetty.base",baseHome.getBase(),is(homePath.toString()));
 
         ConfigurationAssert.assertConfiguration(baseHome,args,"assert-home-with-jvm.txt");
     }
     
     @Test
-    public void testWithSpdy() throws Exception
+    public void testWithHttp2() throws Exception
     {
         List<String> cmdLineArgs = new ArrayList<>();
 
-        File homePath = MavenTestingUtils.getTestResourceDir("usecases/home").getAbsoluteFile();
+        Path homePath = MavenTestingUtils.getTestResourceDir("dist-home").toPath().toRealPath();
         cmdLineArgs.add("jetty.home=" + homePath);
         cmdLineArgs.add("user.dir=" + homePath);
-        cmdLineArgs.add("java.version=1.7.0_60");
+        cmdLineArgs.add("java.version=1.8.0_31");
 
         // Modules
-        cmdLineArgs.add("--module=server");
         cmdLineArgs.add("--module=deploy");
-        cmdLineArgs.add("--module=spdy");
+        cmdLineArgs.add("--module=http2");
 
         Main main = new Main();
 
         StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
         BaseHome baseHome = main.getBaseHome();
 
-        assertThat("jetty.home",baseHome.getHome(),is(homePath.getAbsolutePath()));
-        assertThat("jetty.base",baseHome.getBase(),is(homePath.getAbsolutePath()));
+        assertThat("jetty.home",baseHome.getHome(),is(homePath.toString()));
+        assertThat("jetty.base",baseHome.getBase(),is(homePath.toString()));
 
-        ConfigurationAssert.assertConfiguration(baseHome,args,"assert-home-with-spdy.txt");
+        ConfigurationAssert.assertConfiguration(baseHome,args,"assert-home-with-http2.txt");
     }
 
     @Test
@@ -185,7 +181,7 @@
     {
         List<String> cmdLineArgs = new ArrayList<>();
 
-        File homePath = MavenTestingUtils.getTestResourceDir("jetty home with spaces").getAbsoluteFile();
+        Path homePath = MavenTestingUtils.getTestResourceDir("jetty home with spaces").toPath().toRealPath();
         cmdLineArgs.add("user.dir=" + homePath);
         cmdLineArgs.add("jetty.home=" + homePath);
 
@@ -193,8 +189,8 @@
         StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
         BaseHome baseHome = main.getBaseHome();
 
-        assertThat("jetty.home",baseHome.getHome(),is(homePath.getAbsolutePath()));
-        assertThat("jetty.base",baseHome.getBase(),is(homePath.getAbsolutePath()));
+        assertThat("jetty.home",baseHome.getHome(),is(homePath.toString()));
+        assertThat("jetty.base",baseHome.getBase(),is(homePath.toString()));
 
         ConfigurationAssert.assertConfiguration(baseHome,args,"assert-home-with-spaces.txt");
     }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java
index 6ab23ad..67751cb 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java
@@ -18,9 +18,8 @@
 
 package org.eclipse.jetty.start;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
-import java.io.File;
 import java.io.IOException;
 import java.nio.file.Path;
 
@@ -43,16 +42,16 @@
     public void testGenerate_NothingEnabled() throws IOException
     {
         // Test Env
-        File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
-        File baseDir = testdir.getEmptyDir();
+        Path homeDir = MavenTestingUtils.getTestResourcePathDir("dist-home");
+        Path baseDir = testdir.getEmptyPathDir();
         String cmdLine[] = new String[] {"jetty.version=TEST"};
         
         // Configuration
         CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
         ConfigSources config = new ConfigSources();
         config.add(cmdLineSource);
-        config.add(new JettyHomeConfigSource(homeDir.toPath()));
-        config.add(new JettyBaseConfigSource(baseDir.toPath()));
+        config.add(new JettyHomeConfigSource(homeDir));
+        config.add(new JettyBaseConfigSource(baseDir));
         
         // Initialize
         BaseHome basehome = new BaseHome(config);
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleTest.java
index 2d40564..8f65608 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleTest.java
@@ -18,10 +18,13 @@
 
 package org.eclipse.jetty.start;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.is;
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.Path;
 
 import org.eclipse.jetty.start.config.CommandLineConfigSource;
 import org.eclipse.jetty.start.config.ConfigSources;
@@ -42,28 +45,27 @@
     public void testLoadWebSocket() throws IOException
     {
         // Test Env
-        File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
-        File baseDir = testdir.getEmptyDir();
+        Path homeDir = MavenTestingUtils.getTestResourcePathDir("dist-home");
+        Path baseDir = testdir.getEmptyPathDir();
         String cmdLine[] = new String[] {"jetty.version=TEST"};
         
         // Configuration
         CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
         ConfigSources config = new ConfigSources();
         config.add(cmdLineSource);
-        config.add(new JettyHomeConfigSource(homeDir.toPath()));
-        config.add(new JettyBaseConfigSource(baseDir.toPath()));
+        config.add(new JettyHomeConfigSource(homeDir));
+        config.add(new JettyBaseConfigSource(baseDir));
         
         // Initialize
         BaseHome basehome = new BaseHome(config);
         
-        File file = MavenTestingUtils.getTestResourceFile("usecases/home/modules/websocket.mod");
+        File file = MavenTestingUtils.getTestResourceFile("dist-home/modules/websocket.mod");
         Module module = new Module(basehome,file.toPath());
         
         Assert.assertThat("Module Name",module.getName(),is("websocket"));
-        Assert.assertThat("Module Parents Size",module.getParentNames().size(),is(2));
-        Assert.assertThat("Module Parents",module.getParentNames(),containsInAnyOrder("annotations","server"));
-        Assert.assertThat("Module Xmls Size",module.getXmls().size(),is(1));
-        Assert.assertThat("Module Xmls",module.getXmls(),contains("etc/jetty-websockets.xml"));
+        Assert.assertThat("Module Parents Size",module.getParentNames().size(),is(1));
+        Assert.assertThat("Module Parents",module.getParentNames(),containsInAnyOrder("annotations"));
+        Assert.assertThat("Module Xmls Size",module.getXmls().size(),is(0));
         Assert.assertThat("Module Options Size",module.getLibs().size(),is(1));
         Assert.assertThat("Module Options",module.getLibs(),contains("lib/websocket/*.jar"));
     }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java
index fcdecae..9854602 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java
@@ -18,27 +18,34 @@
 
 package org.eclipse.jetty.start;
 
-import static org.hamcrest.Matchers.contains;
-
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 import org.eclipse.jetty.start.config.CommandLineConfigSource;
 import org.eclipse.jetty.start.config.ConfigSources;
 import org.eclipse.jetty.start.config.JettyBaseConfigSource;
 import org.eclipse.jetty.start.config.JettyHomeConfigSource;
+import org.eclipse.jetty.start.graph.CriteriaSetPredicate;
+import org.eclipse.jetty.start.graph.Predicate;
+import org.eclipse.jetty.start.graph.RegexNamePredicate;
+import org.eclipse.jetty.start.graph.Selection;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.toolchain.test.TestingDir;
-import org.junit.Assert;
+import org.hamcrest.Matchers;
 import org.junit.Rule;
 import org.junit.Test;
 
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
 public class ModulesTest
 {
-    private final static List<String> TEST_SOURCE = Collections.singletonList("<test>");
+    private final static String TEST_SOURCE = "<test>";
 
     @Rule
     public TestingDir testdir = new TestingDir();
@@ -47,20 +54,125 @@
     public void testLoadAllModules() throws IOException
     {
         // Test Env
-        File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
-        File baseDir = testdir.getEmptyDir();
-        String cmdLine[] = new String[] {"jetty.version=TEST"};
-        
+        File homeDir = MavenTestingUtils.getTestResourceDir("dist-home");
+        File baseDir = testdir.getEmptyPathDir().toFile();
+        String cmdLine[] = new String[] { "jetty.version=TEST" };
+
         // Configuration
         CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
         ConfigSources config = new ConfigSources();
         config.add(cmdLineSource);
         config.add(new JettyHomeConfigSource(homeDir.toPath()));
         config.add(new JettyBaseConfigSource(baseDir.toPath()));
-        
+
         // Initialize
         BaseHome basehome = new BaseHome(config);
-        
+
+        StartArgs args = new StartArgs();
+        args.parse(config);
+
+        // Test Modules
+        Modules modules = new Modules(basehome,args);
+        modules.registerAll();
+
+        // Check versions
+        assertThat("java.version.platform", args.getProperties().getString("java.version.platform"),anyOf(Matchers.equalTo("8"),Matchers.equalTo("9")));
+
+        List<String> moduleNames = new ArrayList<>();
+        for (Module mod : modules)
+        {
+            // skip alpn-boot in this test (as its behavior is jdk specific)
+            if (mod.getName().equals("alpn-boot"))
+            {
+                continue;
+            }
+            moduleNames.add(mod.getName());
+        }
+
+        List<String> expected = new ArrayList<>();
+        expected.add("alpn");
+        expected.add("annotations");
+        expected.add("apache-jsp");
+        expected.add("apache-jstl");
+        expected.add("cdi");
+        expected.add("client");
+        expected.add("continuation");
+        expected.add("debuglog");
+        expected.add("deploy");
+        expected.add("ext");
+        expected.add("fcgi");
+        expected.add("gzip");
+        expected.add("hawtio");
+        expected.add("home-base-warning");
+        expected.add("http");
+        expected.add("http2");
+        expected.add("http2c");
+        expected.add("https");
+        expected.add("ipaccess");
+        expected.add("jaas");
+        expected.add("jamon");
+        expected.add("jaspi");
+        expected.add("jminix");
+        expected.add("jmx");
+        expected.add("jmx-remote");
+        expected.add("jndi");
+        expected.add("jolokia");
+        expected.add("jsp");
+        expected.add("jstl");
+        expected.add("jvm");
+        expected.add("logging");
+        expected.add("lowresources");
+        expected.add("monitor");
+        expected.add("plus");
+        expected.add("proxy");
+        expected.add("quickstart");
+        expected.add("requestlog");
+        expected.add("resources");
+        expected.add("rewrite");
+        expected.add("security");
+        expected.add("server");
+        expected.add("servlet");
+        expected.add("servlets");
+        expected.add("setuid");
+        expected.add("spring");
+        expected.add("ssl");
+        expected.add("stats");
+        expected.add("webapp");
+        expected.add("websocket");
+        expected.add("infinispan");
+        expected.add("jdbc-sessions");
+        expected.add("nosql");
+
+        ConfigurationAssert.assertContainsUnordered("All Modules",expected,moduleNames);
+    }
+
+    /**
+     * Test loading of only shallow modules, not deep references.
+     * In other words. ${search-dir}/modules/*.mod should be the only
+     * valid references, but ${search-dir}/alt/foo/modules/*.mod should
+     * not be considered valid.
+     * @throws IOException on test failures
+     */
+    @Test
+    public void testLoadShallowModulesOnly() throws IOException
+    {
+        // Test Env
+        File homeDir = MavenTestingUtils.getTestResourceDir("jetty home with spaces");
+        // intentionally setup top level resources dir (as this would have many
+        // deep references)
+        File baseDir = MavenTestingUtils.getTestResourcesDir();
+        String cmdLine[] = new String[] { "jetty.version=TEST" };
+
+        // Configuration
+        CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
+        ConfigSources config = new ConfigSources();
+        config.add(cmdLineSource);
+        config.add(new JettyHomeConfigSource(homeDir.toPath()));
+        config.add(new JettyBaseConfigSource(baseDir.toPath()));
+
+        // Initialize
+        BaseHome basehome = new BaseHome(config);
+
         StartArgs args = new StartArgs();
         args.parse(config);
 
@@ -71,49 +183,12 @@
         List<String> moduleNames = new ArrayList<>();
         for (Module mod : modules)
         {
-            // skip npn-boot in this test (as its behavior is jdk specific)
-            if (mod.getName().equals("npn-boot"))
-            {
-                continue;
-            }
             moduleNames.add(mod.getName());
         }
 
         List<String> expected = new ArrayList<>();
-        expected.add("jmx");
-        expected.add("client");
-        expected.add("stats");
-        expected.add("spdy");
-        expected.add("deploy");
-        expected.add("debug");
-        expected.add("security");
-        expected.add("ext");
-        expected.add("websocket");
-        expected.add("rewrite");
-        expected.add("ipaccess");
-        expected.add("xinetd");
-        expected.add("proxy");
-        expected.add("webapp");
-        expected.add("jndi");
-        expected.add("lowresources");
-        expected.add("https");
-        expected.add("plus");
-        expected.add("requestlog");
-        expected.add("jsp");
-        // (only present if enabled) expected.add("jsp-impl");
-        expected.add("monitor");
-        expected.add("xml");
-        expected.add("ssl");
-        expected.add("protonego");
-        expected.add("servlet");
-        expected.add("jaas");
-        expected.add("http");
         expected.add("base");
-        expected.add("server");
-        expected.add("annotations");
-        expected.add("resources");
-        expected.add("logging"); 
-        
+
         ConfigurationAssert.assertContainsUnordered("All Modules",expected,moduleNames);
     }
 
@@ -121,50 +196,61 @@
     public void testEnableRegexSimple() throws IOException
     {
         // Test Env
-        File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
-        File baseDir = testdir.getEmptyDir();
-        String cmdLine[] = new String[] {"jetty.version=TEST", "java.version=1.7.0_60"};
-        
+        File homeDir = MavenTestingUtils.getTestResourceDir("dist-home");
+        File baseDir = testdir.getEmptyPathDir().toFile();
+        String cmdLine[] = new String[] { "jetty.version=TEST", "java.version=1.8.0_31" };
+
         // Configuration
         CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
         ConfigSources config = new ConfigSources();
         config.add(cmdLineSource);
         config.add(new JettyHomeConfigSource(homeDir.toPath()));
         config.add(new JettyBaseConfigSource(baseDir.toPath()));
-        
+
         // Initialize
         BaseHome basehome = new BaseHome(config);
-        
+
         StartArgs args = new StartArgs();
         args.parse(config);
 
         // Test Modules
         Modules modules = new Modules(basehome,args);
         modules.registerAll();
-        modules.enable("[sj]{1}.*",TEST_SOURCE);
+        Predicate sjPredicate = new RegexNamePredicate("[sj]{1}.*");
+        modules.selectNode(sjPredicate,new Selection(TEST_SOURCE));
         modules.buildGraph();
 
         List<String> expected = new ArrayList<>();
         expected.add("jmx");
         expected.add("stats");
-        expected.add("spdy");
         expected.add("security");
         expected.add("jndi");
         expected.add("jsp");
         expected.add("servlet");
+        expected.add("servlets");
         expected.add("jaas");
         expected.add("server");
+        expected.add("setuid");
+        expected.add("spring");
+        expected.add("jaspi");
+        expected.add("jminix");
+        expected.add("jolokia");
+        expected.add("jamon");
+        expected.add("jstl");
+        expected.add("jmx-remote");
+        expected.add("jvm");
         // transitive
-        expected.add("base");
         expected.add("ssl");
-        expected.add("protonego");
-        expected.add("protonego-boot");
-        expected.add("protonego-impl");
-        expected.add("xml");
-        expected.add("jsp-impl");
-        
+        expected.add("apache-jsp");
+        expected.add("apache-jstl");
+        expected.add("webapp");
+        expected.add("deploy");
+        expected.add("plus");
+        expected.add("annotations");
+        expected.add("jdbc-sessions");
+
         List<String> resolved = new ArrayList<>();
-        for (Module module : modules.resolveEnabled())
+        for (Module module : modules.getSelected())
         {
             resolved.add(module.getName());
         }
@@ -176,40 +262,38 @@
     public void testResolve_ServerHttp() throws IOException
     {
         // Test Env
-        File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
-        File baseDir = testdir.getEmptyDir();
-        String cmdLine[] = new String[] {"jetty.version=TEST"};
-        
+        File homeDir = MavenTestingUtils.getTestResourceDir("dist-home");
+        File baseDir = testdir.getEmptyPathDir().toFile();
+        String cmdLine[] = new String[] { "jetty.version=TEST" };
+
         // Configuration
         CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
         ConfigSources config = new ConfigSources();
         config.add(cmdLineSource);
         config.add(new JettyHomeConfigSource(homeDir.toPath()));
         config.add(new JettyBaseConfigSource(baseDir.toPath()));
-        
+
         // Initialize
         BaseHome basehome = new BaseHome(config);
-        
+
         StartArgs args = new StartArgs();
         args.parse(config);
 
         // Test Modules
-        Modules modules = new Modules(basehome, args);
+        Modules modules = new Modules(basehome,args);
         modules.registerAll();
 
         // Enable 2 modules
-        modules.enable("server",TEST_SOURCE);
-        modules.enable("http",TEST_SOURCE);
+        modules.selectNode("server",new Selection(TEST_SOURCE));
+        modules.selectNode("http",new Selection(TEST_SOURCE));
 
         modules.buildGraph();
 
         // Collect active module list
-        List<Module> active = modules.resolveEnabled();
+        List<Module> active = modules.getSelected();
 
         // Assert names are correct, and in the right order
         List<String> expectedNames = new ArrayList<>();
-        expectedNames.add("base");
-        expectedNames.add("xml");
         expectedNames.add("server");
         expectedNames.add("http");
 
@@ -219,21 +303,20 @@
             actualNames.add(actual.getName());
         }
 
-        Assert.assertThat("Resolved Names: " + actualNames,actualNames,contains(expectedNames.toArray()));
+        assertThat("Resolved Names: " + actualNames,actualNames,contains(expectedNames.toArray()));
 
         // Assert Library List
         List<String> expectedLibs = new ArrayList<>();
-        expectedLibs.add("lib/jetty-util-${jetty.version}.jar");
-        expectedLibs.add("lib/jetty-io-${jetty.version}.jar");
-        expectedLibs.add("lib/jetty-xml-${jetty.version}.jar");
         expectedLibs.add("lib/servlet-api-3.1.jar");
         expectedLibs.add("lib/jetty-schemas-3.1.jar");
         expectedLibs.add("lib/jetty-http-${jetty.version}.jar");
-        expectedLibs.add("lib/jetty-continuation-${jetty.version}.jar");
         expectedLibs.add("lib/jetty-server-${jetty.version}.jar");
+        expectedLibs.add("lib/jetty-xml-${jetty.version}.jar");
+        expectedLibs.add("lib/jetty-util-${jetty.version}.jar");
+        expectedLibs.add("lib/jetty-io-${jetty.version}.jar");
 
         List<String> actualLibs = modules.normalizeLibs(active);
-        Assert.assertThat("Resolved Libs: " + actualLibs,actualLibs,contains(expectedLibs.toArray()));
+        assertThat("Resolved Libs: " + actualLibs,actualLibs,contains(expectedLibs.toArray()));
 
         // Assert XML List
         List<String> expectedXmls = new ArrayList<>();
@@ -241,27 +324,27 @@
         expectedXmls.add("etc/jetty-http.xml");
 
         List<String> actualXmls = modules.normalizeXmls(active);
-        Assert.assertThat("Resolved XMLs: " + actualXmls,actualXmls,contains(expectedXmls.toArray()));
+        assertThat("Resolved XMLs: " + actualXmls,actualXmls,contains(expectedXmls.toArray()));
     }
 
     @Test
     public void testResolve_WebSocket() throws IOException
     {
         // Test Env
-        File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
-        File baseDir = testdir.getEmptyDir();
-        String cmdLine[] = new String[] {"jetty.version=TEST"};
-        
+        File homeDir = MavenTestingUtils.getTestResourceDir("dist-home");
+        File baseDir = testdir.getEmptyPathDir().toFile();
+        String cmdLine[] = new String[] { "jetty.version=TEST" };
+
         // Configuration
         CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
         ConfigSources config = new ConfigSources();
         config.add(cmdLineSource);
         config.add(new JettyHomeConfigSource(homeDir.toPath()));
         config.add(new JettyBaseConfigSource(baseDir.toPath()));
-        
+
         // Initialize
         BaseHome basehome = new BaseHome(config);
-        
+
         StartArgs args = new StartArgs();
         args.parse(config);
 
@@ -270,23 +353,23 @@
         modules.registerAll();
 
         // Enable 2 modules
-        modules.enable("websocket",TEST_SOURCE);
-        modules.enable("http",TEST_SOURCE);
+        modules.selectNode("websocket",new Selection(TEST_SOURCE));
+        modules.selectNode("http",new Selection(TEST_SOURCE));
 
         modules.buildGraph();
         // modules.dump();
 
         // Collect active module list
-        List<Module> active = modules.resolveEnabled();
+        List<Module> active = modules.getSelected();
 
         // Assert names are correct, and in the right order
         List<String> expectedNames = new ArrayList<>();
-        expectedNames.add("base");
-        expectedNames.add("xml");
         expectedNames.add("server");
         expectedNames.add("http");
         expectedNames.add("jndi");
         expectedNames.add("security");
+        expectedNames.add("servlet");
+        expectedNames.add("webapp");
         expectedNames.add("plus");
         expectedNames.add("annotations");
         expectedNames.add("websocket");
@@ -297,28 +380,29 @@
             actualNames.add(actual.getName());
         }
 
-        Assert.assertThat("Resolved Names: " + actualNames,actualNames,contains(expectedNames.toArray()));
+        assertThat("Resolved Names: " + actualNames,actualNames,contains(expectedNames.toArray()));
 
         // Assert Library List
         List<String> expectedLibs = new ArrayList<>();
-        expectedLibs.add("lib/jetty-util-${jetty.version}.jar");
-        expectedLibs.add("lib/jetty-io-${jetty.version}.jar");
-        expectedLibs.add("lib/jetty-xml-${jetty.version}.jar");
         expectedLibs.add("lib/servlet-api-3.1.jar");
         expectedLibs.add("lib/jetty-schemas-3.1.jar");
         expectedLibs.add("lib/jetty-http-${jetty.version}.jar");
-        expectedLibs.add("lib/jetty-continuation-${jetty.version}.jar");
         expectedLibs.add("lib/jetty-server-${jetty.version}.jar");
+        expectedLibs.add("lib/jetty-xml-${jetty.version}.jar");
+        expectedLibs.add("lib/jetty-util-${jetty.version}.jar");
+        expectedLibs.add("lib/jetty-io-${jetty.version}.jar");
         expectedLibs.add("lib/jetty-jndi-${jetty.version}.jar");
         expectedLibs.add("lib/jndi/*.jar");
         expectedLibs.add("lib/jetty-security-${jetty.version}.jar");
+        expectedLibs.add("lib/jetty-servlet-${jetty.version}.jar");
+        expectedLibs.add("lib/jetty-webapp-${jetty.version}.jar");
         expectedLibs.add("lib/jetty-plus-${jetty.version}.jar");
         expectedLibs.add("lib/jetty-annotations-${jetty.version}.jar");
         expectedLibs.add("lib/annotations/*.jar");
         expectedLibs.add("lib/websocket/*.jar");
 
         List<String> actualLibs = modules.normalizeLibs(active);
-        Assert.assertThat("Resolved Libs: " + actualLibs,actualLibs,contains(expectedLibs.toArray()));
+        assertThat("Resolved Libs: " + actualLibs,actualLibs,contains(expectedLibs.toArray()));
 
         // Assert XML List
         List<String> expectedXmls = new ArrayList<>();
@@ -326,9 +410,98 @@
         expectedXmls.add("etc/jetty-http.xml");
         expectedXmls.add("etc/jetty-plus.xml");
         expectedXmls.add("etc/jetty-annotations.xml");
-        expectedXmls.add("etc/jetty-websockets.xml");
 
         List<String> actualXmls = modules.normalizeXmls(active);
-        Assert.assertThat("Resolved XMLs: " + actualXmls,actualXmls,contains(expectedXmls.toArray()));
+        assertThat("Resolved XMLs: " + actualXmls,actualXmls,contains(expectedXmls.toArray()));
+    }
+
+    @Test
+    public void testResolve_Alt() throws IOException
+    {
+        // Test Env
+        File homeDir = MavenTestingUtils.getTestResourceDir("dist-home");
+        File baseDir = testdir.getEmptyPathDir().toFile();
+        String cmdLine[] = new String[] { "jetty.version=TEST" };
+
+        // Configuration
+        CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
+        ConfigSources config = new ConfigSources();
+        config.add(cmdLineSource);
+        config.add(new JettyHomeConfigSource(homeDir.toPath()));
+        config.add(new JettyBaseConfigSource(baseDir.toPath()));
+
+        // Initialize
+        BaseHome basehome = new BaseHome(config);
+
+        StartArgs args = new StartArgs();
+        args.parse(config);
+
+        // Test Modules
+        Modules modules = new Modules(basehome,args);
+        modules.registerAll();
+
+        // Enable test modules
+        modules.selectNode("http",new Selection(TEST_SOURCE));
+        modules.selectNode("annotations",new Selection(TEST_SOURCE));
+        modules.selectNode("deploy",new Selection(TEST_SOURCE));
+        // Enable alternate modules
+        String alt = "<alt>";
+        modules.selectNode("websocket",new Selection(alt));
+        modules.selectNode("jsp",new Selection(alt));
+
+        modules.buildGraph();
+        // modules.dump();
+
+        // Collect active module list
+        List<Module> active = modules.getSelected();
+
+        // Assert names are correct, and in the right order
+        List<String> expectedNames = new ArrayList<>();
+        expectedNames.add("apache-jsp");
+        expectedNames.add("server");
+        expectedNames.add("http");
+        expectedNames.add("jndi");
+        expectedNames.add("security");
+        expectedNames.add("servlet");
+        expectedNames.add("webapp");
+        expectedNames.add("deploy");
+        expectedNames.add("plus");
+        expectedNames.add("annotations");
+        expectedNames.add("jsp");
+        expectedNames.add("websocket");
+
+        List<String> actualNames = new ArrayList<>();
+        for (Module actual : active)
+        {
+            actualNames.add(actual.getName());
+        }
+
+        assertThat("Resolved Names: " + actualNames,actualNames,contains(expectedNames.toArray()));
+
+        // Now work with the 'alt' selected
+        List<String> expectedAlts = new ArrayList<>();
+        expectedAlts.add("apache-jsp");
+        expectedAlts.add("jsp");
+        expectedAlts.add("websocket");
+
+        for (String expectedAlt : expectedAlts)
+        {
+            Module altMod = modules.get(expectedAlt);
+            assertThat("Alt.mod[" + expectedAlt + "].selected",altMod.isSelected(),is(true));
+            Set<String> sources = altMod.getSelectedCriteriaSet();
+            assertThat("Alt.mod[" + expectedAlt + "].sources: [" + Utils.join(sources,", ") + "]",sources,contains(alt));
+        }
+
+        // Now collect the unique source list
+        List<Module> alts = modules.getMatching(new CriteriaSetPredicate(alt));
+
+        // Assert names are correct, and in the right order
+        actualNames = new ArrayList<>();
+        for (Module actual : alts)
+        {
+            actualNames.add(actual.getName());
+        }
+
+        assertThat("Resolved Alt (Sources) Names: " + actualNames,actualNames,contains(expectedAlts.toArray()));
     }
 }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/PathFinderTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/PathFinderTest.java
index 7d371f8..8ce0c55 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/PathFinderTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/PathFinderTest.java
@@ -28,19 +28,22 @@
 import java.util.List;
 
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.junit.Rule;
 import org.junit.Test;
 
 public class PathFinderTest
 {
+    @Rule
+    public TestingDir testdir = new TestingDir();
+
     @Test
     public void testFindInis() throws IOException
     {
         File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
         Path homePath = homeDir.toPath().toAbsolutePath();
-        File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base");
-        Path basePath = baseDir.toPath().toAbsolutePath();
-        
-        
+        Path basePath = testdir.getEmptyPathDir();
+
         PathFinder finder = new PathFinder();
         finder.setFileMatcher("glob:**/*.ini");
         finder.setBase(homePath);
@@ -56,15 +59,16 @@
         expected.add("${jetty.home}/start.ini");
         FSTest.toOsSeparators(expected);
 
-        BaseHome hb = new BaseHome(new String[] { "jetty.home=" + homePath.toString(),  "jetty.base=" + basePath.toString()});
+        BaseHome hb = new BaseHome(new String[] { "jetty.home=" + homePath.toString(), "jetty.base=" + basePath.toString() });
         BaseHomeTest.assertPathList(hb,"Files found",expected,finder);
     }
 
     @Test
     public void testFindMods() throws IOException
     {
-        File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
+        File homeDir = MavenTestingUtils.getTestResourceDir("dist-home");
         Path homePath = homeDir.toPath().toAbsolutePath();
+        Path basePath = testdir.getEmptyPathDir();
 
         List<String> expected = new ArrayList<>();
         File modulesDir = new File(homeDir,"modules");
@@ -85,7 +89,7 @@
         
         Files.walkFileTree(modulesPath,EnumSet.of(FileVisitOption.FOLLOW_LINKS),1,finder);
 
-        BaseHome hb = new BaseHome(new String[] { "jetty.home=" + homePath.toString() });
+        BaseHome hb = new BaseHome(new String[] { "jetty.home=" + homePath.toString(), "jetty.base=" + basePath.toString() });
         BaseHomeTest.assertPathList(hb,"Files found",expected,finder);
     }
 }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/PropertyPassingTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/PropertyPassingTest.java
index 06d321a..e6fe7ae 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/PropertyPassingTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/PropertyPassingTest.java
@@ -18,8 +18,7 @@
 
 package org.eclipse.jetty.start;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.*;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -89,7 +88,7 @@
 
     @Rule
     public TestingDir testingdir = new TestingDir();
-
+    
     @Test
     public void testAsJvmArg() throws IOException, InterruptedException
     {
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/PropsTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/PropsTest.java
index a5b4692..cabb60a 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/PropsTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/PropsTest.java
@@ -18,6 +18,9 @@
 
 package org.eclipse.jetty.start;
 
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 import org.eclipse.jetty.start.Props.Prop;
 import org.junit.Test;
 
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/RebuildTestResources.java b/jetty-start/src/test/java/org/eclipse/jetty/start/RebuildTestResources.java
deleted file mode 100644
index 1944c54..0000000
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/RebuildTestResources.java
+++ /dev/null
@@ -1,190 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.start;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.PathMatcher;
-import java.nio.file.StandardCopyOption;
-import java.util.regex.Pattern;
-
-import org.eclipse.jetty.toolchain.test.FS;
-import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
-
-/**
- * Utility class to rebuild the src/test/resources/dist-home from the active build tree.
- * <p>
- * Not really meant to be run with each build. Nor is it a good idea to attempt to do that (as this would introduce a dependency from jetty-start ->
- * jetty-distribution which is a circular dependency)
- */
-public class RebuildTestResources
-{
-    public static void main(String[] args)
-    {
-        File realDistHome = MavenTestingUtils.getProjectDir("../jetty-distribution/target/distribution");
-        File outputDir = MavenTestingUtils.getTestResourceDir("dist-home");
-        try
-        {
-            new RebuildTestResources(realDistHome,outputDir).rebuild();
-        }
-        catch (Throwable t)
-        {
-            t.printStackTrace();
-        }
-    }
-
-    private static interface FileCopier
-    {
-        public void copy(Path from, Path to) throws IOException;
-    }
-
-    private static class NormalFileCopier implements FileCopier
-    {
-        @Override
-        public void copy(Path from, Path to) throws IOException
-        {
-            Files.copy(from,to,StandardCopyOption.REPLACE_EXISTING);
-        }
-    }
-
-    private static class TouchFileCopier implements FileCopier
-    {
-        @Override
-        public void copy(Path from, Path to) throws IOException
-        {
-            Files.createFile(to);
-        }
-    }
-
-    private static interface Renamer
-    {
-        public String getName(Path path);
-    }
-
-    private static class NoRenamer implements Renamer
-    {
-        @Override
-        public String getName(Path path)
-        {
-            return path.getFileName().toString();
-        }
-    }
-
-    private static class RegexRenamer implements Renamer
-    {
-        private final Pattern pat;
-        private final String replacement;
-
-        public RegexRenamer(String regex, String replacement)
-        {
-            this.pat = Pattern.compile(regex);
-            this.replacement = replacement;
-        }
-
-        @Override
-        public String getName(Path path)
-        {
-            String origName = path.getFileName().toString();
-            return pat.matcher(origName).replaceAll(replacement);
-        }
-    }
-
-    private final Path destDir;
-    private final Path srcDir;
-
-    public RebuildTestResources(File realDistHome, File outputDir) throws IOException
-    {
-        this.srcDir = realDistHome.toPath().toRealPath();
-        this.destDir = outputDir.toPath();
-    }
-
-    private void copyLibs() throws IOException
-    {
-        System.out.println("Copying libs (lib dir) ...");
-        Path libsDir = destDir.resolve("lib");
-        FS.ensureDirExists(libsDir.toFile());
-
-        PathMatcher matcher = getPathMatcher("glob:**.jar");
-        Renamer renamer = new RegexRenamer("-9\\.[0-9.]*(v[0-9]*)?(-SNAPSHOT)?(RC[0-9])?(M[0-9])?","-TEST");
-        FileCopier copier = new TouchFileCopier();
-        copyDir(srcDir.resolve("lib"),libsDir,matcher,renamer,copier);
-    }
-
-    private void copyModules() throws IOException
-    {
-        System.out.println("Copying modules ...");
-        Path modulesDir = destDir.resolve("modules");
-        FS.ensureDirExists(modulesDir.toFile());
-
-        PathMatcher matcher = getPathMatcher("glob:**.mod");
-        Renamer renamer = new NoRenamer();
-        FileCopier copier = new NormalFileCopier();
-        copyDir(srcDir.resolve("modules"),modulesDir,matcher,renamer,copier);
-    }
-
-    private void copyXmls() throws IOException
-    {
-        System.out.println("Copying xmls (etc dir) ...");
-        Path xmlDir = destDir.resolve("etc");
-        FS.ensureDirExists(xmlDir.toFile());
-
-        PathMatcher matcher = getPathMatcher("glob:**.xml");
-        Renamer renamer = new NoRenamer();
-        FileCopier copier = new TouchFileCopier();
-        copyDir(srcDir.resolve("etc"),xmlDir,matcher,renamer,copier);
-    }
-
-    private void rebuild() throws IOException
-    {
-        copyModules();
-        copyLibs();
-        copyXmls();
-        System.out.println("Done");
-    }
-
-    private PathMatcher getPathMatcher(String pattern)
-    {
-        return FileSystems.getDefault().getPathMatcher(pattern);
-    }
-
-    private void copyDir(Path from, Path to, PathMatcher fileMatcher, Renamer renamer, FileCopier copier) throws IOException
-    {
-        Files.createDirectories(to);
-
-        for (Path path : Files.newDirectoryStream(from))
-        {
-            String name = renamer.getName(path);
-            Path dest = to.resolve(name);
-            if (Files.isDirectory(path))
-            {
-                copyDir(path,dest,fileMatcher,renamer,copier);
-            }
-            else
-            {
-                if (fileMatcher.matches(path))
-                {
-                    copier.copy(path,dest);
-                }
-            }
-        }
-    }
-}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/StartMatchers.java b/jetty-start/src/test/java/org/eclipse/jetty/start/StartMatchers.java
new file mode 100644
index 0000000..a7b1706
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/StartMatchers.java
@@ -0,0 +1,104 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+
+public final class StartMatchers
+{
+    public static Matcher<Path> pathExists()
+    {
+        return new BaseMatcher<Path>()
+        {
+            @Override
+            public boolean matches(Object item)
+            {
+                final Path path = (Path)item;
+                return Files.exists(path);
+            }
+
+            @Override
+            public void describeTo(Description description)
+            {
+                description.appendText("Path should exist");
+            }
+            
+            @Override
+            public void describeMismatch(Object item, Description description)
+            {
+                description.appendText("Path did not exist ").appendValue(item);
+            }
+        };
+    }
+    
+    public static Matcher<Path> notPathExists()
+    {
+        return new BaseMatcher<Path>()
+        {
+            @Override
+            public boolean matches(Object item)
+            {
+                final Path path = (Path)item;
+                return !Files.exists(path);
+            }
+
+            @Override
+            public void describeTo(Description description)
+            {
+                description.appendText("Path should not exist");
+            }
+            
+            @Override
+            public void describeMismatch(Object item, Description description)
+            {
+                description.appendText("Path exists ").appendValue(item);
+            }
+        };
+    }
+    
+    public static Matcher<Path> fileExists()
+    {
+        return new BaseMatcher<Path>()
+        {
+            @Override
+            public boolean matches(Object item)
+            {
+                final Path path = (Path)item;
+                return Files.exists(path) && Files.isRegularFile(path);
+            }
+
+            @Override
+            public void describeTo(Description description)
+            {
+                description.appendText("File should exist");
+            }
+            
+            @Override
+            public void describeMismatch(Object item, Description description)
+            {
+                description.appendText("File did not exist ").appendValue(item);
+            }
+        };
+    }
+}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/TestBadUseCases.java b/jetty-start/src/test/java/org/eclipse/jetty/start/TestBadUseCases.java
index c5aaacb..382939a 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/TestBadUseCases.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/TestBadUseCases.java
@@ -18,68 +18,78 @@
 
 package org.eclipse.jetty.start;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.jetty.start.util.RebuildTestResources;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import static org.hamcrest.Matchers.containsString;
 
 /**
  * Test bad configuration scenarios.
  */
+@RunWith(Parameterized.class)
 public class TestBadUseCases
 {
-    private void assertBadConfig(String homeName, String baseName, String expectedErrorMessage, String... cmdLineArgs) throws Exception
+    @Parameters(name = "{0}")
+    public static List<Object[]> getCases()
     {
-        File homeDir = MavenTestingUtils.getTestResourceDir("usecases/" + homeName);
-        File baseDir = MavenTestingUtils.getTestResourceDir("usecases/" + baseName);
+        List<Object[]> ret = new ArrayList<>();
+
+        ret.add(new Object[]{ "http2",
+                "Invalid Java version",
+                new String[]{"java.version=0.0.0_0"}});
+
+        ret.add(new Object[]{ "versioned-modules-too-new",
+                "Module [http3] specifies jetty version [10.0] which is newer than this version of jetty [" + RebuildTestResources.JETTY_VERSION + "]",
+                null});
+
+        return ret;
+    }
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Parameter(0)
+    public String caseName;
+
+    @Parameter(1)
+    public String expectedErrorMessage;
+
+    @Parameter(2)
+    public String[] commandLineArgs;
+
+    @Test
+    public void testBadConfig() throws Exception
+    {
+        File homeDir = MavenTestingUtils.getTestResourceDir("dist-home");
+        File baseDir = MavenTestingUtils.getTestResourceDir("usecases/" + caseName);
 
         Main main = new Main();
         List<String> cmdLine = new ArrayList<>();
         cmdLine.add("jetty.home=" + homeDir.getAbsolutePath());
         cmdLine.add("jetty.base=" + baseDir.getAbsolutePath());
         // cmdLine.add("--debug");
-        for (String arg : cmdLineArgs)
+
+        if (commandLineArgs != null)
         {
-            cmdLine.add(arg);
+            for (String arg : commandLineArgs)
+            {
+                cmdLine.add(arg);
+            }
         }
 
-        try
-        {
-            main.processCommandLine(cmdLine);
-            fail("Expected " + UsageException.class.getName());
-        }
-        catch (UsageException e)
-        {
-            assertThat("Usage error",e.getMessage(),containsString(expectedErrorMessage));
-        }
+        expectedException.expect(UsageException.class);
+        expectedException.expectMessage(containsString(expectedErrorMessage));
+        main.processCommandLine(cmdLine);
     }
-
-    @Test
-    public void testBadJspCommandLine() throws Exception
-    {
-        assertBadConfig("home","base.with.jsp.default",
-                "Missing referenced dependency: jsp-impl/bad-jsp","jsp-impl=bad");
-    }
-
-    @Test
-    public void testBadJspImplName() throws Exception
-    {
-        assertBadConfig("home","base.with.jsp.bad",
-                "Missing referenced dependency: jsp-impl/bogus-jsp");
-    }
-    
-    @Test
-    public void testWithSpdyBadNpnVersion() throws Exception
-    {
-        assertBadConfig("home","base.enable.spdy.bad.npn.version",
-                "Missing referenced dependency: protonego-impl/npn-1.7.0_01",
-                "java.version=1.7.0_01", "protonego=npn");
-    }
-
-
 }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/TestEnv.java b/jetty-start/src/test/java/org/eclipse/jetty/start/TestEnv.java
index 72d5097..9a104d3 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/TestEnv.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/TestEnv.java
@@ -18,10 +18,12 @@
 
 package org.eclipse.jetty.start;
 
+import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
 
 import org.eclipse.jetty.toolchain.test.FS;
 import org.eclipse.jetty.toolchain.test.IO;
@@ -30,18 +32,19 @@
 
 public class TestEnv
 {
-    public static void copyTestDir(String testResourceDir, File destDir) throws IOException
+    public static void copyTestDir(String testResourceDir, Path destDir) throws IOException
     {
         FS.ensureDirExists(destDir);
         File srcDir = MavenTestingUtils.getTestResourceDir(testResourceDir);
-        IO.copyDir(srcDir,destDir);
+        IO.copyDir(srcDir,destDir.toFile());
     }
 
-    public static void makeFile(File dir, String relFilePath, String... contents) throws IOException
+    public static void makeFile(Path dir, String relFilePath, String... contents) throws IOException
     {
-        File outputFile = new File(dir,OS.separators(relFilePath));
-        FS.ensureDirExists(outputFile.getParentFile());
-        try (FileWriter writer = new FileWriter(outputFile); PrintWriter out = new PrintWriter(writer))
+        Path outputFile = dir.resolve(OS.separators(relFilePath));
+        FS.ensureDirExists(outputFile.getParent());
+        try (BufferedWriter writer = Files.newBufferedWriter(outputFile);
+             PrintWriter out = new PrintWriter(writer))
         {
             for (String content : contents)
             {
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java b/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java
index 22cb6ff..daec40e 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java
@@ -18,118 +18,73 @@
 
 package org.eclipse.jetty.start;
 
-import java.io.File;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
 
 /**
  * Various Home + Base use cases
  */
+@RunWith(Parameterized.class)
 public class TestUseCases
 {
-    private void assertUseCase(String homeName, String baseName, String assertName, String... cmdLineArgs) throws Exception
+    @Parameters(name = "{0}")
+    public static List<Object[]> getCases()
     {
-        File homeDir = MavenTestingUtils.getTestResourceDir("usecases/" + homeName);
-        File baseDir = MavenTestingUtils.getTestResourceDir("usecases/" + baseName);
+        List<Object[]> ret = new ArrayList<>();
+
+        ret.add(new String[] {"barebones", null});
+        ret.add(new String[] {"include-jetty-dir-logging", null});
+        ret.add(new String[] {"jmx", null});
+        ret.add(new String[] {"logging", null});
+        ret.add(new String[] {"jsp", null});
+        ret.add(new String[] {"database", null});
+        ret.add(new String[] {"deep-ext", null});
+        ret.add(new String[] {"versioned-modules", null});
+        
+        // Ones with command lines
+        ret.add(new Object[] {"http2", new String[]{"java.version=1.8.0_31"}});
+        ret.add(new Object[] {"basic-properties", new String[]{"port=9090"}});
+        ret.add(new Object[] {"agent-properties", new String[]{"java.vm.specification.version=1.6"}});
+        
+        return ret;
+    }
+
+    @Parameter(0)
+    public String caseName;
+
+    @Parameter(1)
+    public String[] commandLineArgs;
+
+    @Test
+    public void testUseCase() throws Exception
+    {
+        Path homeDir = MavenTestingUtils.getTestResourceDir("dist-home").toPath().toRealPath();
+        Path baseDir = MavenTestingUtils.getTestResourceDir("usecases/" + caseName).toPath().toRealPath();
 
         Main main = new Main();
         List<String> cmdLine = new ArrayList<>();
-        cmdLine.add("jetty.home=" + homeDir.getAbsolutePath());
-        cmdLine.add("jetty.base=" + baseDir.getAbsolutePath());
+        cmdLine.add("jetty.home=" + homeDir.toString());
+        cmdLine.add("jetty.base=" + baseDir.toString());
         // cmdLine.add("--debug");
-        for (String arg : cmdLineArgs)
+
+        if (commandLineArgs != null)
         {
-            cmdLine.add(arg);
+            for (String arg : commandLineArgs)
+            {
+                cmdLine.add(arg);
+            }
         }
+
         StartArgs args = main.processCommandLine(cmdLine);
         BaseHome baseHome = main.getBaseHome();
-        ConfigurationAssert.assertConfiguration(baseHome,args,"usecases/" + assertName);
-    }
-
-    @Test
-    public void testBarebones() throws Exception
-    {
-        assertUseCase("home","base.barebones","assert-barebones.txt");
-    }
-
-    @Test
-    public void testJMX() throws Exception
-    {
-        assertUseCase("home","base.jmx","assert-jmx.txt");
-    }
-    
-    @Test
-    public void testWithLogging() throws Exception
-    {
-        assertUseCase("home","base.logging","assert-logging.txt");
-    }
-
-    @Test
-    public void testWithIncludeJettyDir_Logging() throws Exception
-    {
-        assertUseCase("home","base.with.include.jetty.dirs","assert-include-jetty-dir-logging.txt");
-    }
-
-    @Test
-    public void testWithJspDefault() throws Exception
-    {
-        assertUseCase("home","base.with.jsp.default","assert-jsp-apache.txt");
-    }
-
-    @Test
-    public void testWithJspApache() throws Exception
-    {
-        assertUseCase("home","base.with.jsp.apache","assert-jsp-apache.txt");
-    }
-    
-    @Test
-    public void testWithJspGlassfish() throws Exception
-    {
-        assertUseCase("home","base.with.jsp.glassfish","assert-jsp-glassfish.txt");
-    }
-
-    @Test
-    public void testWithJspGlassfishCmdLine() throws Exception
-    {
-        assertUseCase("home","base.with.jsp.default","assert-jsp-glassfish.txt","jsp-impl=glassfish");
-    }
-
-    @Test
-    public void testWithMissingNpnVersion() throws Exception
-    {
-        assertUseCase("home","base.missing.npn.version","assert-missing-npn-version.txt","java.version=1.7.0_01");
-    }
-    
-    @Test
-    public void testWithSpdy() throws Exception
-    {
-        assertUseCase("home","base.enable.spdy","assert-enable-spdy.txt","java.version=1.7.0_60");
-    }
-    
-    @Test
-    public void testWithDatabase() throws Exception
-    {
-        assertUseCase("home","base.with.db","assert-with-db.txt");
-    }
-
-    @Test
-    public void testWithDeepExt() throws Exception
-    {
-        assertUseCase("home","base.with.ext","assert-with.ext.txt");
-    }
-    
-    @Test
-    public void testWithPropsBasic() throws Exception
-    {
-        assertUseCase("home","base.props.basic","assert-props.basic.txt","port=9090");
-    }
-    
-    @Test
-    public void testWithPropsAgent() throws Exception
-    {
-        assertUseCase("home","base.props.agent","assert-props.agent.txt","java.vm.specification.version=1.6");
+        ConfigurationAssert.assertConfiguration(baseHome,args,"usecases/" + caseName + ".assert.txt");
     }
 }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/VersionTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/VersionTest.java
index 13df005..8c3856e 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/VersionTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/VersionTest.java
@@ -18,48 +18,110 @@
 
 package org.eclipse.jetty.start;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
 
 import org.junit.Test;
 
 public class VersionTest
 {
     @Test
-    public void testDefaultVersion()
+    public void testParse() 
     {
-        Version version = new Version();
-        assertEquals("Default version difference to 0.0.0",0,version.compare(new Version("0.0.0")));
+        assertParse("1.8.0_45",1,8,0,45);
+        assertParse("1.8.0_45-internal",1,8,0,45);
+        assertParse("1.8.0-debug",1,8,0,-1);
+    }
+    
+    private void assertParse(String verStr, int legacyMajor, int major, int revision, int update)
+    {
+        Version ver = new Version(verStr);
+        assertThat("Version [" + verStr + "].legacyMajor", ver.getLegacyMajor(), is(legacyMajor));
+        assertThat("Version [" + verStr + "].major", ver.getMajor(), is(major));
+        assertThat("Version [" + verStr + "].revision", ver.getRevision(), is(revision));
+        assertThat("Version [" + verStr + "].update", ver.getUpdate(), is(update));
+        
+        assertThat("Version [" + verStr + "].toString", ver.toString(), is(verStr));
     }
 
     @Test
+    public void testToShortString() 
+    {
+        assertToShortString("1.8","1.8");
+        assertToShortString("1.8.0","1.8.0");
+        assertToShortString("1.8.0_3","1.8.0_3");
+        assertToShortString("1.8.0_03","1.8.0_03");
+        assertToShortString("1.8.0_45","1.8.0_45");
+        assertToShortString("1.8.0_45-internal","1.8.0_45");
+        assertToShortString("1.8.0-debug","1.8.0");
+    }
+    
+    private void assertToShortString(String verStr, String expectedShortString)
+    {
+        Version ver = new Version(verStr);
+        assertThat("Version [" + verStr + "].toShortString", ver.toShortString(), is(expectedShortString));
+    }
+
+    @Test
+    public void testToString() 
+    {
+        assertToString("1.8");
+        assertToString("1.8.0");
+        assertToString("1.8.0_0");
+        assertToString("1.8.0_3");
+        assertToString("1.8.0_03");
+    }
+
+    private void assertToString(String verStr)
+    {
+        Version ver = new Version(verStr);
+        assertThat("Version [" + verStr + "].toString", ver.toString(), is(verStr));
+    }
+    
+    @Test
     public void testNewerVersion() {
         assertIsNewer("0.0.0", "0.0.1");
         assertIsNewer("0.1.0", "0.1.1");
         assertIsNewer("1.5.0", "1.6.0");
         // assertIsNewer("1.6.0_12", "1.6.0_16"); // JDK version spec?
     }
-
+    
     @Test
     public void testOlderVersion() {
         assertIsOlder("0.0.1", "0.0.0");
         assertIsOlder("0.1.1", "0.1.0");
         assertIsOlder("1.6.0", "1.5.0");
     }
-
+    
+    @Test
+    public void testOlderOrEqualTo()
+    {
+        assertThat("9.2 <= 9.2",new Version("9.2").isOlderThanOrEqualTo(new Version("9.2")),is(true));
+        assertThat("9.2 <= 9.3",new Version("9.2").isOlderThanOrEqualTo(new Version("9.3")),is(true));
+        assertThat("9.3 <= 9.2",new Version("9.3").isOlderThanOrEqualTo(new Version("9.2")),is(false));
+    }
+    
+    @Test
+    public void testNewerOrEqualTo()
+    {
+        assertThat("9.2 >= 9.2",new Version("9.2").isNewerThanOrEqualTo(new Version("9.2")),is(true));
+        assertThat("9.2 >= 9.3",new Version("9.2").isNewerThanOrEqualTo(new Version("9.3")),is(false));
+        assertThat("9.3 >= 9.2",new Version("9.3").isNewerThanOrEqualTo(new Version("9.2")),is(true));
+    }
+    
     private void assertIsOlder(String basever, String testver)
     {
         Version vbase = new Version(basever);
         Version vtest = new Version(testver);
-        assertTrue("Version [" + testver + "] should be older than [" + basever + "]",
-                vtest.compare(vbase) == -1);
+        assertTrue("Version [" + testver + "] should be older than [" + basever + "]", vtest.isOlderThan(vbase));
     }
 
     private void assertIsNewer(String basever, String testver)
     {
         Version vbase = new Version(basever);
         Version vtest = new Version(testver);
-        assertTrue("Version [" + testver + "] should be newer than [" + basever + "]",
-                vtest.compare(vbase) == 1);
+        assertTrue("Version [" + testver + "] should be newer than [" + basever + "]", vtest.isNewerThan(vbase));
     }
+    
+    
 }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/config/ConfigSourcesTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/config/ConfigSourcesTest.java
index 30aacb8..28772c8 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/config/ConfigSourcesTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/config/ConfigSourcesTest.java
@@ -18,10 +18,13 @@
 
 package org.eclipse.jetty.start.config;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -52,7 +55,7 @@
         ConfigurationAssert.assertOrdered("ConfigSources.id order",expectedList,actualList);
     }
 
-    private void assertDirOrder(ConfigSources sources, File... expectedDirOrder)
+    private void assertDirOrder(ConfigSources sources, Path... expectedDirOrder) throws IOException
     {
         List<String> actualList = new ArrayList<>();
         for (ConfigSource source : sources)
@@ -63,9 +66,9 @@
             }
         }
         List<String> expectedList = new ArrayList<>();
-        for (File path : expectedDirOrder)
+        for (Path path : expectedDirOrder)
         {
-            expectedList.add(path.getAbsolutePath());
+            expectedList.add(path.toRealPath().toString());
         }
         ConfigurationAssert.assertOrdered("ConfigSources.dir order",expectedList,actualList);
     }
@@ -81,22 +84,22 @@
     public void testOrder_BasicConfig() throws IOException
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1");
+                "jetty.http.host=127.0.0.1");
 
         ConfigSources sources = new ConfigSources();
 
         String[] cmdLine = new String[0];
         sources.add(new CommandLineConfigSource(cmdLine));
-        sources.add(new JettyBaseConfigSource(base.toPath()));
-        sources.add(new JettyHomeConfigSource(home.toPath()));
+        sources.add(new JettyBaseConfigSource(base));
+        sources.add(new JettyHomeConfigSource(home));
 
         assertIdOrder(sources,"<command-line>","${jetty.base}","${jetty.home}");
     }
@@ -105,49 +108,50 @@
     public void testOrder_With1ExtraConfig() throws IOException
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create common
-        File common = testdir.getFile("common");
-        FS.ensureEmpty(common);
+        Path common = testdir.getPathFile("common");
+        FS.ensureEmpty(common.toFile());
+        common = common.toRealPath();
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString());
 
         ConfigSources sources = new ConfigSources();
 
         String[] cmdLine = new String[0];
         sources.add(new CommandLineConfigSource(cmdLine));
-        sources.add(new JettyHomeConfigSource(home.toPath()));
-        sources.add(new JettyBaseConfigSource(base.toPath()));
+        sources.add(new JettyHomeConfigSource(home.toRealPath()));
+        sources.add(new JettyBaseConfigSource(base.toRealPath()));
 
-        assertIdOrder(sources,"<command-line>","${jetty.base}",common.getAbsolutePath(),"${jetty.home}");
+        assertIdOrder(sources,"<command-line>","${jetty.base}",common.toString(),"${jetty.home}");
     }
 
     @Test
     public void testCommandLine_1Extra_FromSimpleProp() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
-        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+        TestEnv.makeFile(common,"start.ini","jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1");
+                "jetty.http.host=127.0.0.1");
 
         ConfigSources sources = new ConfigSources();
 
@@ -155,44 +159,44 @@
 
         String[] cmdLine = new String[] {
                 // property
-                "my.common=" + common.getAbsolutePath(),
+                "my.common=" + common.toString(),
                 // reference via property
                 "--include-jetty-dir=${my.common}" };
         
         sources.add(new CommandLineConfigSource(cmdLine));
-        sources.add(new JettyHomeConfigSource(home.toPath()));
-        sources.add(new JettyBaseConfigSource(base.toPath()));
+        sources.add(new JettyHomeConfigSource(home));
+        sources.add(new JettyBaseConfigSource(base));
 
         assertIdOrder(sources,"<command-line>","${jetty.base}","${my.common}","${jetty.home}");
 
         assertDirOrder(sources,base,common,home);
 
-        assertProperty(sources,"jetty.host","127.0.0.1");
-        assertProperty(sources,"jetty.port","8080"); // from 'common'
+        assertProperty(sources,"jetty.http.host","127.0.0.1");
+        assertProperty(sources,"jetty.http.port","8080"); // from 'common'
     }
 
     @Test
     public void testCommandLine_1Extra_FromPropPrefix() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create opt
-        File opt = testdir.getFile("opt");
+        Path opt = testdir.getPathFile("opt");
         FS.ensureEmpty(opt);
 
         // Create common
-        File common = new File(opt,"common");
+        Path common = opt.resolve("common");
         FS.ensureEmpty(common);
-        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+        TestEnv.makeFile(common,"start.ini","jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1");
+                "jetty.http.host=127.0.0.1");
 
         String dirRef = "${my.opt}" + File.separator + "common";
 
@@ -201,44 +205,44 @@
         // Simple command line reference to include-jetty-dir via property (also on command line)
         String[] cmdLine = new String[] {
                 // property to 'opt' dir
-                "my.opt=" + opt.getAbsolutePath(),
+                "my.opt=" + opt.toString(),
                 // reference via property prefix
                 "--include-jetty-dir=" + dirRef };
         
         sources.add(new CommandLineConfigSource(cmdLine));
-        sources.add(new JettyHomeConfigSource(home.toPath()));
-        sources.add(new JettyBaseConfigSource(base.toPath()));
+        sources.add(new JettyHomeConfigSource(home));
+        sources.add(new JettyBaseConfigSource(base));
 
         assertIdOrder(sources,"<command-line>","${jetty.base}",dirRef,"${jetty.home}");
 
         assertDirOrder(sources,base,common,home);
 
-        assertProperty(sources,"jetty.host","127.0.0.1");
-        assertProperty(sources,"jetty.port","8080"); // from 'common'
+        assertProperty(sources,"jetty.http.host","127.0.0.1");
+        assertProperty(sources,"jetty.http.port","8080"); // from 'common'
     }
 
     @Test
     public void testCommandLine_1Extra_FromCompoundProp() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create opt
-        File opt = testdir.getFile("opt");
+        Path opt = testdir.getPathFile("opt");
         FS.ensureEmpty(opt);
 
         // Create common
-        File common = new File(opt,"common");
+        Path common = opt.resolve("common");
         FS.ensureEmpty(common);
-        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+        TestEnv.makeFile(common,"start.ini","jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1");
+                "jetty.http.host=127.0.0.1");
 
         String dirRef = "${my.opt}" + File.separator + "${my.dir}";
 
@@ -248,184 +252,184 @@
 
         String[] cmdLine = new String[] {
                 // property to 'opt' dir
-                "my.opt=" + opt.getAbsolutePath(),
+                "my.opt=" + opt.toString(),
                 // property to commmon dir name
                 "my.dir=common",
                 // reference via property prefix
                 "--include-jetty-dir=" + dirRef };
         
         sources.add(new CommandLineConfigSource(cmdLine));
-        sources.add(new JettyHomeConfigSource(home.toPath()));
-        sources.add(new JettyBaseConfigSource(base.toPath()));
+        sources.add(new JettyHomeConfigSource(home));
+        sources.add(new JettyBaseConfigSource(base));
 
         assertIdOrder(sources,"<command-line>","${jetty.base}",dirRef,"${jetty.home}");
 
         assertDirOrder(sources,base,common,home);
 
-        assertProperty(sources,"jetty.host","127.0.0.1");
-        assertProperty(sources,"jetty.port","8080"); // from 'common'
+        assertProperty(sources,"jetty.http.host","127.0.0.1");
+        assertProperty(sources,"jetty.http.port","8080"); // from 'common'
     }
     
     @Test
     public void testRefCommon() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
-        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+        TestEnv.makeFile(common,"start.ini","jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString());
 
         ConfigSources sources = new ConfigSources();
         
         String cmdLine[] = new String[0];
         sources.add(new CommandLineConfigSource(cmdLine));
-        sources.add(new JettyHomeConfigSource(home.toPath()));
-        sources.add(new JettyBaseConfigSource(base.toPath()));
+        sources.add(new JettyHomeConfigSource(home));
+        sources.add(new JettyBaseConfigSource(base));
 
-        assertIdOrder(sources,"<command-line>","${jetty.base}",common.getAbsolutePath(),"${jetty.home}");
+        assertIdOrder(sources,"<command-line>","${jetty.base}",common.toString(),"${jetty.home}");
 
         assertDirOrder(sources,base,common,home);
 
-        assertProperty(sources,"jetty.host","127.0.0.1");
-        assertProperty(sources,"jetty.port","8080"); // from 'common'
+        assertProperty(sources,"jetty.http.host","127.0.0.1");
+        assertProperty(sources,"jetty.http.port","8080"); // from 'common'
     }
 
     @Test
     public void testRefCommonAndCorp() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
-        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+        TestEnv.makeFile(common,"start.ini","jetty.http.port=8080");
 
         // Create corp
-        File corp = testdir.getFile("corp");
+        Path corp = testdir.getPathFile("corp");
         FS.ensureEmpty(corp);
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath(), //
-                "--include-jetty-dir=" + corp.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString(), //
+                "--include-jetty-dir=" + corp.toString());
 
         ConfigSources sources = new ConfigSources();
 
         String cmdLine[] = new String[0];
         sources.add(new CommandLineConfigSource(cmdLine));
-        sources.add(new JettyHomeConfigSource(home.toPath()));
-        sources.add(new JettyBaseConfigSource(base.toPath()));
+        sources.add(new JettyHomeConfigSource(home));
+        sources.add(new JettyBaseConfigSource(base));
 
         assertIdOrder(sources,"<command-line>","${jetty.base}",
-                common.getAbsolutePath(),
-                corp.getAbsolutePath(),
+                common.toString(),
+                corp.toString(),
                 "${jetty.home}");
 
         assertDirOrder(sources,base,common,corp,home);
 
-        assertProperty(sources,"jetty.host","127.0.0.1");
-        assertProperty(sources,"jetty.port","8080"); // from 'common'
+        assertProperty(sources,"jetty.http.host","127.0.0.1");
+        assertProperty(sources,"jetty.http.port","8080"); // from 'common'
     }
     
     @Test
     public void testRefCommonRefCorp() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create corp
-        File corp = testdir.getFile("corp");
+        Path corp = testdir.getPathFile("corp");
         FS.ensureEmpty(corp);
         TestEnv.makeFile(corp,"start.ini", //
-                "jetty.port=9090");
+                "jetty.http.port=9090");
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
         TestEnv.makeFile(common,"start.ini", //
-                "--include-jetty-dir=" + corp.getAbsolutePath(), //
-                "jetty.port=8080");
+                "--include-jetty-dir=" + corp.toString(), //
+                "jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString());
 
         ConfigSources sources = new ConfigSources();
 
         String cmdLine[] = new String[0];
         sources.add(new CommandLineConfigSource(cmdLine));
-        sources.add(new JettyHomeConfigSource(home.toPath()));
-        sources.add(new JettyBaseConfigSource(base.toPath()));
+        sources.add(new JettyHomeConfigSource(home));
+        sources.add(new JettyBaseConfigSource(base));
 
         assertIdOrder(sources,"<command-line>","${jetty.base}",
-                common.getAbsolutePath(),
-                corp.getAbsolutePath(),
+                common.toString(),
+                corp.toString(),
                 "${jetty.home}");
 
         assertDirOrder(sources,base,common,corp,home);
 
-        assertProperty(sources,"jetty.host","127.0.0.1");
-        assertProperty(sources,"jetty.port","8080"); // from 'common'
+        assertProperty(sources,"jetty.http.host","127.0.0.1");
+        assertProperty(sources,"jetty.http.port","8080"); // from 'common'
     }
     
     @Test
     public void testRefCommonRefCorp_FromSimpleProps() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create corp
-        File corp = testdir.getFile("corp");
+        Path corp = testdir.getPathFile("corp");
         FS.ensureEmpty(corp);
         TestEnv.makeFile(corp,"start.ini", //
-                "jetty.port=9090");
+                "jetty.http.port=9090");
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
         TestEnv.makeFile(common,"start.ini", //
-                "my.corp=" + corp.getAbsolutePath(), //
+                "my.corp=" + corp.toString(), //
                 "--include-jetty-dir=${my.corp}", //
-                "jetty.port=8080");
+                "jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "my.common="+common.getAbsolutePath(), //
+                "jetty.http.host=127.0.0.1",//
+                "my.common="+common.toString(), //
                 "--include-jetty-dir=${my.common}");
 
         ConfigSources sources = new ConfigSources();
 
         String cmdLine[] = new String[0];
         sources.add(new CommandLineConfigSource(cmdLine));
-        sources.add(new JettyHomeConfigSource(home.toPath()));
-        sources.add(new JettyBaseConfigSource(base.toPath()));
+        sources.add(new JettyHomeConfigSource(home));
+        sources.add(new JettyBaseConfigSource(base));
 
         assertIdOrder(sources,"<command-line>",
                 "${jetty.base}",
@@ -435,150 +439,150 @@
 
         assertDirOrder(sources,base,common,corp,home);
 
-        assertProperty(sources,"jetty.host","127.0.0.1");
-        assertProperty(sources,"jetty.port","8080"); // from 'common'
+        assertProperty(sources,"jetty.http.host","127.0.0.1");
+        assertProperty(sources,"jetty.http.port","8080"); // from 'common'
     }
     
     @Test
     public void testRefCommonRefCorp_CmdLineRef() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create devops
-        File devops = testdir.getFile("devops");
+        Path devops = testdir.getPathFile("devops");
         FS.ensureEmpty(devops);
         TestEnv.makeFile(devops,"start.ini", //
                 "--module=logging", //
-                "jetty.port=2222");
+                "jetty.http.port=2222");
 
         // Create corp
-        File corp = testdir.getFile("corp");
+        Path corp = testdir.getPathFile("corp");
         FS.ensureEmpty(corp);
         TestEnv.makeFile(corp,"start.ini", //
-                "jetty.port=9090");
+                "jetty.http.port=9090");
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
         TestEnv.makeFile(common,"start.ini", //
-                "--include-jetty-dir=" + corp.getAbsolutePath(), //
-                "jetty.port=8080");
+                "--include-jetty-dir=" + corp.toString(), //
+                "jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString());
 
         ConfigSources sources = new ConfigSources();
         
         String cmdLine[] = new String[]{
                 // command line provided include-jetty-dir ref
-                "--include-jetty-dir=" + devops.getAbsolutePath()};
+                "--include-jetty-dir=" + devops.toString()};
         sources.add(new CommandLineConfigSource(cmdLine));
-        sources.add(new JettyHomeConfigSource(home.toPath()));
-        sources.add(new JettyBaseConfigSource(base.toPath()));
+        sources.add(new JettyHomeConfigSource(home));
+        sources.add(new JettyBaseConfigSource(base));
 
         assertIdOrder(sources,"<command-line>",
                 "${jetty.base}",
-                devops.getAbsolutePath(),
-                common.getAbsolutePath(),
-                corp.getAbsolutePath(),
+                devops.toString(),
+                common.toString(),
+                corp.toString(),
                 "${jetty.home}");
 
         assertDirOrder(sources,base,devops,common,corp,home);
 
-        assertProperty(sources,"jetty.host","127.0.0.1");
-        assertProperty(sources,"jetty.port","2222"); // from 'common'
+        assertProperty(sources,"jetty.http.host","127.0.0.1");
+        assertProperty(sources,"jetty.http.port","2222"); // from 'common'
     }
     
     @Test
     public void testRefCommonRefCorp_CmdLineProp() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create corp
-        File corp = testdir.getFile("corp");
+        Path corp = testdir.getPathFile("corp");
         FS.ensureEmpty(corp);
         TestEnv.makeFile(corp,"start.ini", //
-                "jetty.port=9090");
+                "jetty.http.port=9090");
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
         TestEnv.makeFile(common,"start.ini", //
-                "--include-jetty-dir=" + corp.getAbsolutePath(), //
-                "jetty.port=8080");
+                "--include-jetty-dir=" + corp.toString(), //
+                "jetty.http.port=8080");
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString());
 
         ConfigSources sources = new ConfigSources();
         
         String cmdLine[] = new String[]{
              // command line property should override all others
-                "jetty.port=7070"
+                "jetty.http.port=7070"
         };
         sources.add(new CommandLineConfigSource(cmdLine));
-        sources.add(new JettyHomeConfigSource(home.toPath()));
-        sources.add(new JettyBaseConfigSource(base.toPath()));
+        sources.add(new JettyHomeConfigSource(home));
+        sources.add(new JettyBaseConfigSource(base));
 
         assertIdOrder(sources,"<command-line>","${jetty.base}",
-                common.getAbsolutePath(),
-                corp.getAbsolutePath(),
+                common.toString(),
+                corp.toString(),
                 "${jetty.home}");
 
         assertDirOrder(sources,base,common,corp,home);
 
-        assertProperty(sources,"jetty.host","127.0.0.1");
-        assertProperty(sources,"jetty.port","7070"); // from <command-line>
+        assertProperty(sources,"jetty.http.host","127.0.0.1");
+        assertProperty(sources,"jetty.http.port","7070"); // from <command-line>
     }
     
     @Test
     public void testBadDoubleRef() throws Exception
     {
         // Create home
-        File home = testdir.getFile("home");
+        Path home = testdir.getPathFile("home");
         FS.ensureEmpty(home);
-        TestEnv.copyTestDir("usecases/home",home);
+        TestEnv.copyTestDir("dist-home",home);
 
         // Create common
-        File common = testdir.getFile("common");
+        Path common = testdir.getPathFile("common");
         FS.ensureEmpty(common);
 
         // Create corp
-        File corp = testdir.getFile("corp");
+        Path corp = testdir.getPathFile("corp");
         FS.ensureEmpty(corp);
         TestEnv.makeFile(corp,"start.ini", 
                 // standard property
-                "jetty.port=9090",
+                "jetty.http.port=9090",
                 // INTENTIONAL BAD Reference (duplicate)
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "--include-jetty-dir=" + common.toString());
 
         // Populate common
         TestEnv.makeFile(common,"start.ini", 
                 // standard property
-                "jetty.port=8080",
+                "jetty.http.port=8080",
                 // reference to corp
-                "--include-jetty-dir=" + corp.getAbsolutePath());
+                "--include-jetty-dir=" + corp.toString());
 
         // Create base
-        File base = testdir.getFile("base");
+        Path base = testdir.getPathFile("base");
         FS.ensureEmpty(base);
         TestEnv.makeFile(base,"start.ini", //
-                "jetty.host=127.0.0.1",//
-                "--include-jetty-dir=" + common.getAbsolutePath());
+                "jetty.http.host=127.0.0.1",//
+                "--include-jetty-dir=" + common.toString());
 
         ConfigSources sources = new ConfigSources();
 
@@ -586,8 +590,8 @@
         {
             String cmdLine[] = new String[0];
             sources.add(new CommandLineConfigSource(cmdLine));
-            sources.add(new JettyHomeConfigSource(home.toPath()));
-            sources.add(new JettyBaseConfigSource(base.toPath()));
+            sources.add(new JettyHomeConfigSource(home));
+            sources.add(new JettyBaseConfigSource(base));
             
             Assert.fail("Should have thrown a UsageException");
         }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/fileinits/MavenLocalRepoFileInitializerTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/fileinits/MavenLocalRepoFileInitializerTest.java
new file mode 100644
index 0000000..cac7d14
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/fileinits/MavenLocalRepoFileInitializerTest.java
@@ -0,0 +1,154 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.fileinits;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Path;
+
+import org.eclipse.jetty.start.BaseHome;
+import org.eclipse.jetty.start.config.ConfigSources;
+import org.eclipse.jetty.start.config.JettyBaseConfigSource;
+import org.eclipse.jetty.start.config.JettyHomeConfigSource;
+import org.eclipse.jetty.start.fileinits.MavenLocalRepoFileInitializer.Coordinates;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class MavenLocalRepoFileInitializerTest
+{
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+    
+    @Rule
+    public TestingDir testdir = new TestingDir();
+    
+    private BaseHome baseHome;
+    
+    @Before
+    public void setupBaseHome() throws IOException
+    {
+        Path homeDir = testdir.getEmptyPathDir();
+        
+        ConfigSources config = new ConfigSources();
+        config.add(new JettyHomeConfigSource(homeDir));
+        config.add(new JettyBaseConfigSource(homeDir));
+
+        this.baseHome = new BaseHome(config);
+    }
+
+    @Test
+    public void testGetCoordinate_NotMaven()
+    {
+        MavenLocalRepoFileInitializer repo = new MavenLocalRepoFileInitializer(baseHome);
+        String ref = "http://www.eclipse.org/jetty";
+        Coordinates coords = repo.getCoordinates(URI.create(ref));
+        assertThat("Coords",coords,nullValue());
+    }
+
+    @Test
+    public void testGetCoordinate_InvalidMaven()
+    {
+        MavenLocalRepoFileInitializer repo = new MavenLocalRepoFileInitializer(baseHome);
+        String ref = "maven://www.eclipse.org/jetty";
+        expectedException.expect(RuntimeException.class);
+        expectedException.expectMessage(containsString("Not a valid maven:// uri"));
+        repo.getCoordinates(URI.create(ref));
+    }
+
+    @Test
+    public void testGetCoordinate_Normal()
+    {
+        MavenLocalRepoFileInitializer repo = new MavenLocalRepoFileInitializer(baseHome);
+        String ref = "maven://org.eclipse.jetty/jetty-start/9.3.x";
+        Coordinates coords = repo.getCoordinates(URI.create(ref));
+        assertThat("Coordinates",coords,notNullValue());
+
+        assertThat("coords.groupId",coords.groupId,is("org.eclipse.jetty"));
+        assertThat("coords.artifactId",coords.artifactId,is("jetty-start"));
+        assertThat("coords.version",coords.version,is("9.3.x"));
+        assertThat("coords.type",coords.type,is("jar"));
+        assertThat("coords.classifier",coords.classifier,nullValue());
+        
+        assertThat("coords.toCentralURI", coords.toCentralURI().toASCIIString(), 
+                is("http://central.maven.org/maven2/org/eclipse/jetty/jetty-start/9.3.x/jetty-start-9.3.x.jar"));
+    }
+
+    @Test
+    public void testGetCoordinate_Zip()
+    {
+        MavenLocalRepoFileInitializer repo = new MavenLocalRepoFileInitializer(baseHome);
+        String ref = "maven://org.eclipse.jetty/jetty-distribution/9.3.x/zip";
+        Coordinates coords = repo.getCoordinates(URI.create(ref));
+        assertThat("Coordinates",coords,notNullValue());
+
+        assertThat("coords.groupId",coords.groupId,is("org.eclipse.jetty"));
+        assertThat("coords.artifactId",coords.artifactId,is("jetty-distribution"));
+        assertThat("coords.version",coords.version,is("9.3.x"));
+        assertThat("coords.type",coords.type,is("zip"));
+        assertThat("coords.classifier",coords.classifier,nullValue());
+        
+        assertThat("coords.toCentralURI", coords.toCentralURI().toASCIIString(), 
+                is("http://central.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.3.x/jetty-distribution-9.3.x.zip"));
+    }
+
+    @Test
+    public void testGetCoordinate_TestJar()
+    {
+        MavenLocalRepoFileInitializer repo = new MavenLocalRepoFileInitializer(baseHome);
+        String ref = "maven://org.eclipse.jetty/jetty-http/9.3.x/jar/tests";
+        Coordinates coords = repo.getCoordinates(URI.create(ref));
+        assertThat("Coordinates",coords,notNullValue());
+
+        assertThat("coords.groupId",coords.groupId,is("org.eclipse.jetty"));
+        assertThat("coords.artifactId",coords.artifactId,is("jetty-http"));
+        assertThat("coords.version",coords.version,is("9.3.x"));
+        assertThat("coords.type",coords.type,is("jar"));
+        assertThat("coords.classifier",coords.classifier,is("tests"));
+        
+        assertThat("coords.toCentralURI", coords.toCentralURI().toASCIIString(), 
+                is("http://central.maven.org/maven2/org/eclipse/jetty/jetty-http/9.3.x/jetty-http-9.3.x-tests.jar"));
+    }
+    
+    @Test
+    public void testGetCoordinate_Test_UnspecifiedType()
+    {
+        MavenLocalRepoFileInitializer repo = new MavenLocalRepoFileInitializer(baseHome);
+        String ref = "maven://org.eclipse.jetty/jetty-http/9.3.x//tests";
+        Coordinates coords = repo.getCoordinates(URI.create(ref));
+        assertThat("Coordinates",coords,notNullValue());
+
+        assertThat("coords.groupId",coords.groupId,is("org.eclipse.jetty"));
+        assertThat("coords.artifactId",coords.artifactId,is("jetty-http"));
+        assertThat("coords.version",coords.version,is("9.3.x"));
+        assertThat("coords.type",coords.type,is("jar"));
+        assertThat("coords.classifier",coords.classifier,is("tests"));
+        
+        assertThat("coords.toCentralURI", coords.toCentralURI().toASCIIString(), 
+                is("http://central.maven.org/maven2/org/eclipse/jetty/jetty-http/9.3.x/jetty-http-9.3.x-tests.jar"));
+    }
+}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/graph/NodeTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/graph/NodeTest.java
new file mode 100644
index 0000000..0941f6b
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/graph/NodeTest.java
@@ -0,0 +1,75 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.graph;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+
+public class NodeTest
+{
+    private static class TestNode extends Node<TestNode>
+    {
+        public TestNode(String name)
+        {
+            setName(name);
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("TestNode[%s]",getName());
+        }
+    }
+
+    @Test
+    public void testNoNameMatch()
+    {
+        TestNode node = new TestNode("a");
+        Predicate predicate = new NamePredicate("b");
+        assertThat(node.toString(),node.matches(predicate),is(false));
+    }
+    
+    @Test
+    public void testNameMatch()
+    {
+        TestNode node = new TestNode("a");
+        Predicate predicate = new NamePredicate("a");
+        assertThat(node.toString(),node.matches(predicate),is(true));
+    }
+    
+    @Test
+    public void testAnySelectionMatch()
+    {
+        TestNode node = new TestNode("a");
+        node.addSelection(new Selection("test"));
+        Predicate predicate = new AnySelectionPredicate();
+        assertThat(node.toString(),node.matches(predicate),is(true));
+    }
+    
+    @Test
+    public void testAnySelectionNoMatch()
+    {
+        TestNode node = new TestNode("a");
+        // NOT Selected - node.addSelection(new Selection("test"));
+        Predicate predicate = new AnySelectionPredicate();
+        assertThat(node.toString(),node.matches(predicate),is(false));
+    }
+}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/util/CorrectMavenCentralRefs.java b/jetty-start/src/test/java/org/eclipse/jetty/start/util/CorrectMavenCentralRefs.java
new file mode 100644
index 0000000..ac5adc5
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/util/CorrectMavenCentralRefs.java
@@ -0,0 +1,256 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.util;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileVisitOption;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.start.PathFinder;
+import org.eclipse.jetty.start.PathMatchers;
+import org.eclipse.jetty.start.Utils;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+
+/**
+ * Simple utility to scan all of the mod files and correct references
+ * to maven central URL in [files] sections to the new maven:// syntax
+ */
+public class CorrectMavenCentralRefs
+{
+    public static void main(String[] args)
+    {
+        Path buildRoot = MavenTestingUtils.getProjectDir("..").toPath();
+        buildRoot = buildRoot.normalize().toAbsolutePath();
+
+        // Test to make sure we are in right directory
+        Path rootPomXml = buildRoot.resolve("pom.xml");
+        Path distPomXml = buildRoot.resolve("jetty-distribution/pom.xml");
+        if (!Files.exists(rootPomXml) || !Files.exists(distPomXml))
+        {
+            System.err.println("Not build root directory: " + buildRoot);
+            System.exit(-1);
+        }
+
+        try
+        {
+            new CorrectMavenCentralRefs().fix(buildRoot);
+        }
+        catch (Throwable t)
+        {
+            t.printStackTrace(System.err);
+        }
+    }
+
+    public void fix(Path buildRoot) throws IOException
+    {
+        // Find all of the *.mod files
+        PathFinder finder = new PathFinder();
+        finder.setFileMatcher("glob:**/*.mod");
+        finder.setBase(buildRoot);
+
+        // Matcher for target directories
+        PathMatcher targetMatcher = PathMatchers.getMatcher("glob:**/target/**");
+        PathMatcher testMatcher = PathMatchers.getMatcher("glob:**/test/**");
+
+        System.out.printf("Walking path: %s%n",buildRoot);
+        Set<FileVisitOption> options = Collections.emptySet();
+        Files.walkFileTree(buildRoot,options,30,finder);
+
+        System.out.printf("Found: %d hits%n",finder.getHits().size());
+        int count = 0;
+        for (Path path : finder.getHits())
+        {
+            if (Files.isDirectory(path))
+            {
+                // skip
+                continue;
+            }
+
+            if (targetMatcher.matches(path))
+            {
+                // skip
+                continue;
+            }
+
+            if (testMatcher.matches(path))
+            {
+                // skip
+                continue;
+            }
+
+            if (processModFile(path))
+            {
+                count++;
+            }
+        }
+
+        System.out.printf("Processed %,d modules",count);
+    }
+
+    private boolean processFileRefs(List<String> lines)
+    {
+        Pattern section = Pattern.compile("\\s*\\[([^]]*)\\]\\s*");
+        int filesStart = -1;
+        int filesEnd = -1;
+
+        // Find [files] section
+        String sectionId = null;
+        int lineCount = lines.size();
+        for (int i = 0; i < lineCount; i++)
+        {
+            String line = lines.get(i).trim();
+
+            Matcher sectionMatcher = section.matcher(line);
+
+            if (sectionMatcher.matches())
+            {
+                sectionId = sectionMatcher.group(1).trim().toUpperCase(Locale.ENGLISH);
+            }
+            else
+            {
+                if ("FILES".equals(sectionId))
+                {
+                    if (filesStart < 0)
+                    {
+                        filesStart = i;
+                    }
+                    filesEnd = i;
+                }
+            }
+        }
+
+        if (filesStart == (-1))
+        {
+            // no [files] section
+            return false;
+        }
+
+        // process lines, only in files section
+        int updated = 0;
+        for (int i = filesStart; i <= filesEnd; i++)
+        {
+            String line = lines.get(i);
+            String keyword = "maven.org/maven2/";
+            int idx = line.indexOf(keyword);
+            if (idx > 0)
+            {
+                int pipe = line.indexOf('|');
+                String rawpath = line.substring(idx + keyword.length(),pipe);
+                String destpath = line.substring(pipe + 1);
+
+                String parts[] = rawpath.split("/");
+                int rev = parts.length;
+                String filename = parts[--rev];
+
+                String type = "jar";
+                int ext = filename.lastIndexOf('.');
+                if (ext > 0)
+                {
+                    type = filename.substring(ext + 1);
+                }
+                String version = parts[--rev];
+                String artifactId = parts[--rev];
+                String groupId = Utils.join(parts,0,rev,".");
+
+                String classifier = filename.replaceFirst(artifactId + '-' + version,"");
+                classifier = classifier.replaceFirst('.' + type + '$',"");
+                if (Utils.isNotBlank(classifier) && (classifier.charAt(0) == '-'))
+                {
+                    classifier = classifier.substring(1);
+                }
+
+                StringBuilder murl = new StringBuilder();
+                murl.append("maven://");
+                murl.append(groupId).append('/');
+                murl.append(artifactId).append('/');
+                murl.append(version);
+                if (!"jar".equals(type) || Utils.isNotBlank(classifier))
+                {
+                    murl.append('/').append(type);
+                    if (Utils.isNotBlank(classifier))
+                    {
+                        murl.append('/').append(classifier);
+                    }
+                }
+
+                lines.set(i,murl.toString()+'|'+destpath);
+
+                updated++;
+            }
+        }
+
+        return (updated > 0);
+    }
+
+    private boolean processModFile(Path path) throws IOException
+    {
+        List<String> lines = readLines(path);
+        if (processFileRefs(lines))
+        {
+            // the lines are now dirty, save them.
+            System.out.printf("Updating: %s%n",path);
+            saveLines(path,lines);
+            return true;
+        }
+
+        // no update performed
+        return false;
+    }
+
+    private List<String> readLines(Path path) throws IOException
+    {
+        List<String> lines = new ArrayList<>();
+
+        try (BufferedReader reader = Files.newBufferedReader(path,StandardCharsets.UTF_8))
+        {
+            String line;
+            while ((line = reader.readLine()) != null)
+            {
+                lines.add(line);
+            }
+        }
+
+        return lines;
+    }
+
+    private void saveLines(Path path, List<String> lines) throws IOException
+    {
+        try (BufferedWriter writer = Files.newBufferedWriter(path,StandardCharsets.UTF_8,StandardOpenOption.TRUNCATE_EXISTING))
+        {
+            for (String line : lines)
+            {
+                writer.write(line);
+                writer.write(System.lineSeparator());
+            }
+        }
+    }
+}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/util/RebuildTestResources.java b/jetty-start/src/test/java/org/eclipse/jetty/start/util/RebuildTestResources.java
new file mode 100644
index 0000000..fceee96
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/util/RebuildTestResources.java
@@ -0,0 +1,197 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.StandardCopyOption;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+
+/**
+ * Utility class to rebuild the src/test/resources/dist-home from the active build tree.
+ * <p>
+ * Not really meant to be run with each build. Nor is it a good idea to attempt to do that (as this would introduce a dependency from jetty-start ->
+ * jetty-distribution which is a circular dependency)
+ */
+public class RebuildTestResources
+{
+    public static final String JETTY_VERSION = "9.3";
+    
+    public static void main(String[] args)
+    {
+        File realDistHome = MavenTestingUtils.getProjectDir("../jetty-distribution/target/distribution");
+        File outputDir = MavenTestingUtils.getTestResourceDir("dist-home");
+        try
+        {
+            new RebuildTestResources(realDistHome,outputDir).rebuild();
+        }
+        catch (Throwable t)
+        {
+            t.printStackTrace();
+        }
+    }
+
+    private static interface FileCopier
+    {
+        public void copy(Path from, Path to) throws IOException;
+    }
+
+    private static class NormalFileCopier implements FileCopier
+    {
+        @Override
+        public void copy(Path from, Path to) throws IOException
+        {
+            Files.copy(from,to,StandardCopyOption.REPLACE_EXISTING);
+        }
+    }
+
+    private static class TouchFileCopier implements FileCopier
+    {
+        @Override
+        public void copy(Path from, Path to) throws IOException
+        {
+            if(Files.exists(to)) 
+            {
+                // skip if it exists
+                return;
+            }
+            Files.createFile(to);
+        }
+    }
+
+    private static interface Renamer
+    {
+        public String getName(Path path);
+    }
+
+    private static class NoRenamer implements Renamer
+    {
+        @Override
+        public String getName(Path path)
+        {
+            return path.getFileName().toString();
+        }
+    }
+
+    private static class RegexRenamer implements Renamer
+    {
+        private final Pattern pat;
+        private final String replacement;
+
+        public RegexRenamer(String regex, String replacement)
+        {
+            this.pat = Pattern.compile(regex);
+            this.replacement = replacement;
+        }
+
+        @Override
+        public String getName(Path path)
+        {
+            String origName = path.getFileName().toString();
+            return pat.matcher(origName).replaceAll(replacement);
+        }
+    }
+
+    private final Path destDir;
+    private final Path srcDir;
+
+    public RebuildTestResources(File realDistHome, File outputDir) throws IOException
+    {
+        this.srcDir = realDistHome.toPath().toRealPath();
+        this.destDir = outputDir.toPath();
+    }
+
+    private void copyLibs() throws IOException
+    {
+        System.out.println("Copying libs (lib dir) ...");
+        Path libsDir = destDir.resolve("lib");
+        FS.ensureDirExists(libsDir.toFile());
+
+        PathMatcher matcher = getPathMatcher("glob:**.jar");
+        Renamer renamer = new RegexRenamer("-9\\.[0-9.]*(v[0-9-]*)?(-SNAPSHOT)?(RC[0-9])?(M[0-9])?","-" + JETTY_VERSION);
+        FileCopier copier = new TouchFileCopier();
+        copyDir(srcDir.resolve("lib"),libsDir,matcher,renamer,copier);
+    }
+
+    private void copyModules() throws IOException
+    {
+        System.out.println("Copying modules ...");
+        Path modulesDir = destDir.resolve("modules");
+        FS.ensureDirExists(modulesDir.toFile());
+
+        PathMatcher matcher = getPathMatcher("glob:**.mod");
+        Renamer renamer = new NoRenamer();
+        FileCopier copier = new NormalFileCopier();
+        copyDir(srcDir.resolve("modules"),modulesDir,matcher,renamer,copier);
+    }
+
+    private void copyXmls() throws IOException
+    {
+        System.out.println("Copying xmls (etc dir) ...");
+        Path xmlDir = destDir.resolve("etc");
+        FS.ensureDirExists(xmlDir.toFile());
+
+        PathMatcher matcher = getPathMatcher("glob:**.xml");
+        Renamer renamer = new NoRenamer();
+        FileCopier copier = new TouchFileCopier();
+        copyDir(srcDir.resolve("etc"),xmlDir,matcher,renamer,copier);
+    }
+
+    private void rebuild() throws IOException
+    {
+        copyModules();
+        copyLibs();
+        copyXmls();
+        System.out.println("Done");
+    }
+
+    private PathMatcher getPathMatcher(String pattern)
+    {
+        return FileSystems.getDefault().getPathMatcher(pattern);
+    }
+
+    private void copyDir(Path from, Path to, PathMatcher fileMatcher, Renamer renamer, FileCopier copier) throws IOException
+    {
+        Files.createDirectories(to);
+
+        for (Path path : Files.newDirectoryStream(from))
+        {
+            String name = renamer.getName(path);
+            Path dest = to.resolve(name);
+            if (Files.isDirectory(path))
+            {
+                copyDir(path,dest,fileMatcher,renamer,copier);
+            }
+            else
+            {
+                if (fileMatcher.matches(path))
+                {
+                    copier.copy(path,dest);
+                }
+            }
+        }
+    }
+}
diff --git a/jetty-start/src/test/resources/assert-home-with-http2.txt b/jetty-start/src/test/resources/assert-home-with-http2.txt
new file mode 100644
index 0000000..a8f4683
--- /dev/null
+++ b/jetty-start/src/test/resources/assert-home-with-http2.txt
@@ -0,0 +1,83 @@
+# The XMLs we expect (order is important)
+XML|${jetty.base}/etc/home-base-warning.xml
+XML|${jetty.base}/etc/jetty.xml
+XML|${jetty.base}/etc/jetty-http.xml
+XML|${jetty.base}/etc/jetty-ssl.xml
+XML|${jetty.base}/etc/jetty-ssl-context.xml
+XML|${jetty.base}/etc/jetty-alpn.xml
+XML|${jetty.base}/etc/jetty-deploy.xml
+XML|${jetty.base}/etc/jetty-http2.xml
+XML|${jetty.base}/etc/jetty-plus.xml
+XML|${jetty.base}/etc/jetty-annotations.xml
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.base}/lib/apache-jsp/org.eclipse.jetty.apache-jsp-9.3.jar
+LIB|${jetty.base}/lib/apache-jsp/org.eclipse.jetty.orbit.org.eclipse.jdt.core-3.8.2.v20130121.jar
+LIB|${jetty.base}/lib/apache-jsp/org.mortbay.jasper.apache-el-8.0.20.M0.jar
+LIB|${jetty.base}/lib/apache-jsp/org.mortbay.jasper.apache-jsp-8.0.20.M0.jar
+LIB|${jetty.base}/lib/apache-jstl/org.apache.taglibs.taglibs-standard-impl-1.2.1.jar
+LIB|${jetty.base}/lib/apache-jstl/org.apache.taglibs.taglibs-standard-spec-1.2.1.jar
+LIB|${jetty.base}/lib/jndi/javax.mail.glassfish-1.4.1.v201005082020.jar
+LIB|${jetty.base}/lib/jndi/javax.transaction-api-1.2.jar
+LIB|${jetty.base}/lib/annotations/asm-5.0.1.jar
+LIB|${jetty.base}/lib/annotations/asm-commons-5.0.1.jar
+LIB|${jetty.base}/lib/annotations/javax.annotation-api-1.2.jar
+LIB|${jetty.base}/lib/websocket/javax.websocket-api-1.0.jar
+LIB|${jetty.base}/lib/websocket/javax-websocket-client-impl-9.3.jar
+LIB|${jetty.base}/lib/websocket/javax-websocket-server-impl-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-api-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-client-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-common-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-server-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-servlet-9.3.jar
+LIB|${jetty.base}/lib/jetty-alpn-server-9.3.jar
+LIB|${jetty.base}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.base}/lib/http2/http2-common-9.3.jar
+LIB|${jetty.base}/lib/http2/http2-hpack-9.3.jar
+LIB|${jetty.base}/lib/http2/http2-server-9.3.jar
+LIB|${jetty.base}/lib/servlet-api-3.1.jar
+LIB|${jetty.base}/lib/jetty-annotations-9.3.jar
+LIB|${jetty.base}/lib/jetty-http-9.3.jar
+LIB|${jetty.base}/lib/jetty-io-9.3.jar
+LIB|${jetty.base}/lib/jetty-deploy-9.3.jar
+LIB|${jetty.base}/lib/jetty-plus-9.3.jar
+LIB|${jetty.base}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.base}/lib/jetty-security-9.3.jar
+LIB|${jetty.base}/lib/jetty-server-9.3.jar
+LIB|${jetty.base}/lib/jetty-servlet-9.3.jar
+LIB|${jetty.base}/lib/jetty-util-9.3.jar
+LIB|${jetty.base}/lib/jetty-webapp-9.3.jar
+LIB|${jetty.base}/lib/jetty-xml-9.3.jar
+LIB|${jetty.base}/lib/jetty-jndi-9.3.jar
+
+
+# The Properties we expect (order is irrelevant)
+# (these are the properties we actually set in the configuration)
+# PROP|java.version=1.8.0_31
+PROP|jetty.http.port=8080
+PROP|jetty.httpConfig.delayDispatchUntilContent=false
+PROP|jetty.server.dumpAfterStart=false
+PROP|jetty.server.dumpBeforeStop=false
+PROP|jetty.httpConfig.outputBufferSize=32768
+PROP|jetty.httpConfig.requestHeaderSize=8192
+PROP|jetty.httpConfig.responseHeaderSize=8192
+PROP|jetty.httpConfig.sendDateHeader=false
+PROP|jetty.httpConfig.sendServerVersion=true
+PROP|jetty.threadPool.maxThreads=200
+PROP|jetty.threadPool.minThreads=10
+PROP|jetty.threadPool.idleTimeout=60000
+
+# JVM Args
+JVM|-Xms1024m
+JVM|-Xmx1024m
+
+# Downloads
+DOWNLOAD|maven://org.mortbay.jetty.alpn/alpn-boot/8.1.3.v20150130|lib/alpn/alpn-boot-8.1.3.v20150130.jar
+DOWNLOAD|https://raw.githubusercontent.com/eclipse/jetty.project/master/jetty-server/src/test/config/etc/keystore?id=master|etc/keystore
+
+# Files
+FILE|lib/
+FILE|lib/ext/
+FILE|lib/alpn/
+FILE|resources/
+FILE|webapps/
diff --git a/jetty-start/src/test/resources/assert-home-with-jvm.txt b/jetty-start/src/test/resources/assert-home-with-jvm.txt
index 9774d97..871aa8d 100644
--- a/jetty-start/src/test/resources/assert-home-with-jvm.txt
+++ b/jetty-start/src/test/resources/assert-home-with-jvm.txt
@@ -1,44 +1,75 @@
 # The XMLs we expect (order is important)
-XML|${jetty.base}/etc/jetty-jmx.xml
+XML|${jetty.base}/etc/home-base-warning.xml
 XML|${jetty.base}/etc/jetty.xml
 XML|${jetty.base}/etc/jetty-http.xml
+XML|${jetty.base}/etc/jetty-deploy.xml
 XML|${jetty.base}/etc/jetty-plus.xml
 XML|${jetty.base}/etc/jetty-annotations.xml
-XML|${jetty.base}/etc/jetty-websockets.xml
+XML|${jetty.base}/etc/jetty-jmx.xml
 XML|${jetty.base}/etc/jetty-logging.xml
 
 # The LIBs we expect (order is irrelevant)
-LIB|${jetty.base}/lib/annotations/javax.annotation-api-1.2.jar
-LIB|${jetty.base}/lib/annotations/org.objectweb.asm-TEST.jar
-LIB|${jetty.base}/lib/jetty-annotations-TEST.jar
-LIB|${jetty.base}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.base}/lib/jetty-http-TEST.jar
-LIB|${jetty.base}/lib/jetty-io-TEST.jar
-LIB|${jetty.base}/lib/jetty-jmx-TEST.jar
-LIB|${jetty.base}/lib/jetty-jndi-TEST.jar
-LIB|${jetty.base}/lib/jetty-plus-TEST.jar
-LIB|${jetty.base}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.base}/lib/jetty-security-TEST.jar
-LIB|${jetty.base}/lib/jetty-server-TEST.jar
-LIB|${jetty.base}/lib/jetty-util-TEST.jar
-LIB|${jetty.base}/lib/jetty-xml-TEST.jar
-LIB|${jetty.base}/lib/jndi/javax.activation-1.1.jar
-LIB|${jetty.base}/lib/jndi/javax.transaction-api-1.2.jar
-LIB|${jetty.base}/lib/servlet-api-3.1.jar
-LIB|${jetty.base}/lib/websocket/javax.websocket-api-1.0.jar
-LIB|${jetty.base}/lib/websocket/javax-websocket-client-impl-TEST.jar
-LIB|${jetty.base}/lib/websocket/javax-websocket-server-impl-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-api-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-client-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-common-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-server-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-servlet-TEST.jar
 LIB|${maven-test-resources}/extra-resources
 LIB|${maven-test-resources}/extra-libs/example.jar
+LIB|${jetty.base}/lib/apache-jsp/org.eclipse.jetty.apache-jsp-9.3.jar
+LIB|${jetty.base}/lib/apache-jsp/org.eclipse.jetty.orbit.org.eclipse.jdt.core-3.8.2.v20130121.jar
+LIB|${jetty.base}/lib/apache-jsp/org.mortbay.jasper.apache-el-8.0.20.M0.jar
+LIB|${jetty.base}/lib/apache-jsp/org.mortbay.jasper.apache-jsp-8.0.20.M0.jar
+LIB|${jetty.base}/lib/apache-jstl/org.apache.taglibs.taglibs-standard-impl-1.2.1.jar
+LIB|${jetty.base}/lib/apache-jstl/org.apache.taglibs.taglibs-standard-spec-1.2.1.jar
+LIB|${jetty.base}/lib/servlet-api-3.1.jar
+LIB|${jetty.base}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.base}/lib/jndi/javax.mail.glassfish-1.4.1.v201005082020.jar
+LIB|${jetty.base}/lib/jndi/javax.transaction-api-1.2.jar
+LIB|${jetty.base}/lib/annotations/asm-5.0.1.jar
+LIB|${jetty.base}/lib/annotations/asm-commons-5.0.1.jar
+LIB|${jetty.base}/lib/annotations/javax.annotation-api-1.2.jar
+LIB|${jetty.base}/lib/websocket/javax.websocket-api-1.0.jar
+LIB|${jetty.base}/lib/websocket/javax-websocket-client-impl-9.3.jar
+LIB|${jetty.base}/lib/websocket/javax-websocket-server-impl-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-api-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-client-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-common-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-server-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-servlet-9.3.jar
+LIB|${jetty.base}/lib/jetty-annotations-9.3.jar
+LIB|${jetty.base}/lib/jetty-http-9.3.jar
+LIB|${jetty.base}/lib/jetty-io-9.3.jar
+LIB|${jetty.base}/lib/jetty-deploy-9.3.jar
+LIB|${jetty.base}/lib/jetty-plus-9.3.jar
+LIB|${jetty.base}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.base}/lib/jetty-security-9.3.jar
+LIB|${jetty.base}/lib/jetty-server-9.3.jar
+LIB|${jetty.base}/lib/jetty-servlet-9.3.jar
+LIB|${jetty.base}/lib/jetty-util-9.3.jar
+LIB|${jetty.base}/lib/jetty-webapp-9.3.jar
+LIB|${jetty.base}/lib/jetty-xml-9.3.jar
+LIB|${jetty.base}/lib/jetty-jndi-9.3.jar
+
 
 # The Properties we expect (order is irrelevant)
-# PROP|jetty.port=9090
+# (these are the properties we actually set in the configuration)
+# PROP|jetty.http.port=8080
+# (these are the ones set by default from jetty.home modules)
+PROP|jetty.http.port=8080
+PROP|jetty.httpConfig.delayDispatchUntilContent=false
+PROP|jetty.server.dumpAfterStart=false
+PROP|jetty.server.dumpBeforeStop=false
+PROP|jetty.httpConfig.outputBufferSize=32768
+PROP|jetty.httpConfig.requestHeaderSize=8192
+PROP|jetty.httpConfig.responseHeaderSize=8192
+PROP|jetty.httpConfig.sendDateHeader=false
+PROP|jetty.httpConfig.sendServerVersion=true
+PROP|jetty.threadPool.maxThreads=200
+PROP|jetty.threadPool.minThreads=10
+PROP|jetty.threadPool.idleTimeout=60000
 
 # JVM Args
 JVM|-Xms1024m
 JVM|-Xmx1024m
+
+# Files / Directories to create
+FILE|lib/
+FILE|lib/ext/
+FILE|resources/
+FILE|webapps/
diff --git a/jetty-start/src/test/resources/assert-home-with-spdy.txt b/jetty-start/src/test/resources/assert-home-with-spdy.txt
deleted file mode 100644
index aa5aea9..0000000
--- a/jetty-start/src/test/resources/assert-home-with-spdy.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.base}/etc/jetty-jmx.xml
-XML|${jetty.base}/etc/protonego-alpn.xml
-XML|${jetty.base}/etc/jetty.xml
-XML|${jetty.base}/etc/jetty-http.xml
-XML|${jetty.base}/etc/jetty-ssl.xml
-XML|${jetty.base}/etc/jetty-plus.xml
-XML|${jetty.base}/etc/jetty-spdy.xml
-XML|${jetty.base}/etc/jetty-annotations.xml
-XML|${jetty.base}/etc/jetty-deploy.xml
-XML|${jetty.base}/etc/jetty-websockets.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.base}/lib/annotations/javax.annotation-api-1.2.jar
-LIB|${jetty.base}/lib/annotations/org.objectweb.asm-TEST.jar
-LIB|${jetty.base}/lib/jetty-annotations-TEST.jar
-LIB|${jetty.base}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.base}/lib/jetty-http-TEST.jar
-LIB|${jetty.base}/lib/jetty-io-TEST.jar
-LIB|${jetty.base}/lib/jetty-deploy-TEST.jar
-LIB|${jetty.base}/lib/jetty-jmx-TEST.jar
-LIB|${jetty.base}/lib/jetty-jndi-TEST.jar
-LIB|${jetty.base}/lib/jetty-plus-TEST.jar
-LIB|${jetty.base}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.base}/lib/jetty-security-TEST.jar
-LIB|${jetty.base}/lib/jetty-server-TEST.jar
-LIB|${jetty.base}/lib/jetty-servlet-TEST.jar
-LIB|${jetty.base}/lib/jetty-util-TEST.jar
-LIB|${jetty.base}/lib/jetty-webapp-TEST.jar
-LIB|${jetty.base}/lib/jetty-xml-TEST.jar
-LIB|${jetty.base}/lib/spdy/spdy-client-TEST.jar
-LIB|${jetty.base}/lib/spdy/spdy-core-TEST.jar
-LIB|${jetty.base}/lib/spdy/spdy-http-common-TEST.jar
-LIB|${jetty.base}/lib/spdy/spdy-http-server-TEST.jar
-LIB|${jetty.base}/lib/spdy/spdy-server-TEST.jar
-LIB|${jetty.base}/lib/jndi/javax.activation-1.1.jar
-LIB|${jetty.base}/lib/jndi/javax.transaction-api-1.2.jar
-LIB|${jetty.base}/lib/servlet-api-3.1.jar
-LIB|${jetty.base}/lib/websocket/javax.websocket-api-1.0.jar
-LIB|${jetty.base}/lib/websocket/javax-websocket-client-impl-TEST.jar
-LIB|${jetty.base}/lib/websocket/javax-websocket-server-impl-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-api-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-client-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-common-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-server-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-servlet-TEST.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|java.version=1.7.0_60
-PROP|jetty.keymanager.password=OBF:1u2u1wml1z7s1z7a1wnl1u2g
-PROP|jetty.keystore=etc/keystore
-PROP|jetty.keystore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
-PROP|jetty.secure.port=8443
-PROP|jetty.truststore=etc/keystore
-PROP|jetty.truststore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
-PROP|protonego=alpn
-PROP|spdy.port=8443
-PROP|spdy.timeout=30000
-
-# JVM Args
-JVM|-Xms1024m
-JVM|-Xmx1024m
-
-# Downloads
-DOWNLOAD|http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-DOWNLOAD|http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/plain/jetty-server/src/main/config/etc/keystore|etc/keystore
-
-# Files
-FILE|lib/
-FILE|lib/alpn/
-
-
diff --git a/jetty-start/src/test/resources/assert-home.txt b/jetty-start/src/test/resources/assert-home.txt
index 5be8d46..70515e3 100644
--- a/jetty-start/src/test/resources/assert-home.txt
+++ b/jetty-start/src/test/resources/assert-home.txt
@@ -1,37 +1,64 @@
 # The XMLs we expect (order is important)
-XML|${jetty.base}/etc/jetty-jmx.xml
+XML|${jetty.base}/etc/home-base-warning.xml
 XML|${jetty.base}/etc/jetty.xml
 XML|${jetty.base}/etc/jetty-http.xml
+XML|${jetty.base}/etc/jetty-deploy.xml
 XML|${jetty.base}/etc/jetty-plus.xml
 XML|${jetty.base}/etc/jetty-annotations.xml
-XML|${jetty.base}/etc/jetty-websockets.xml
 
 # The LIBs we expect (order is irrelevant)
+LIB|${jetty.base}/lib/apache-jsp/org.eclipse.jetty.apache-jsp-9.3.jar
+LIB|${jetty.base}/lib/apache-jsp/org.eclipse.jetty.orbit.org.eclipse.jdt.core-3.8.2.v20130121.jar
+LIB|${jetty.base}/lib/apache-jsp/org.mortbay.jasper.apache-el-8.0.20.M0.jar
+LIB|${jetty.base}/lib/apache-jsp/org.mortbay.jasper.apache-jsp-8.0.20.M0.jar
+LIB|${jetty.base}/lib/apache-jstl/org.apache.taglibs.taglibs-standard-impl-1.2.1.jar
+LIB|${jetty.base}/lib/apache-jstl/org.apache.taglibs.taglibs-standard-spec-1.2.1.jar
+LIB|${jetty.base}/lib/annotations/asm-5.0.1.jar
+LIB|${jetty.base}/lib/annotations/asm-commons-5.0.1.jar
 LIB|${jetty.base}/lib/annotations/javax.annotation-api-1.2.jar
-LIB|${jetty.base}/lib/annotations/org.objectweb.asm-TEST.jar
-LIB|${jetty.base}/lib/jetty-annotations-TEST.jar
-LIB|${jetty.base}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.base}/lib/jetty-http-TEST.jar
-LIB|${jetty.base}/lib/jetty-io-TEST.jar
-LIB|${jetty.base}/lib/jetty-jmx-TEST.jar
-LIB|${jetty.base}/lib/jetty-jndi-TEST.jar
-LIB|${jetty.base}/lib/jetty-plus-TEST.jar
-LIB|${jetty.base}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.base}/lib/jetty-security-TEST.jar
-LIB|${jetty.base}/lib/jetty-server-TEST.jar
-LIB|${jetty.base}/lib/jetty-util-TEST.jar
-LIB|${jetty.base}/lib/jetty-xml-TEST.jar
-LIB|${jetty.base}/lib/jndi/javax.activation-1.1.jar
+LIB|${jetty.base}/lib/jetty-jndi-9.3.jar
+LIB|${jetty.base}/lib/jndi/javax.mail.glassfish-1.4.1.v201005082020.jar
 LIB|${jetty.base}/lib/jndi/javax.transaction-api-1.2.jar
+LIB|${jetty.base}/lib/jetty-annotations-9.3.jar
+LIB|${jetty.base}/lib/jetty-http-9.3.jar
+LIB|${jetty.base}/lib/jetty-io-9.3.jar
+LIB|${jetty.base}/lib/jetty-deploy-9.3.jar
+LIB|${jetty.base}/lib/jetty-plus-9.3.jar
+LIB|${jetty.base}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.base}/lib/jetty-security-9.3.jar
+LIB|${jetty.base}/lib/jetty-server-9.3.jar
+LIB|${jetty.base}/lib/jetty-servlet-9.3.jar
+LIB|${jetty.base}/lib/jetty-util-9.3.jar
+LIB|${jetty.base}/lib/jetty-webapp-9.3.jar
+LIB|${jetty.base}/lib/jetty-xml-9.3.jar
 LIB|${jetty.base}/lib/servlet-api-3.1.jar
 LIB|${jetty.base}/lib/websocket/javax.websocket-api-1.0.jar
-LIB|${jetty.base}/lib/websocket/javax-websocket-client-impl-TEST.jar
-LIB|${jetty.base}/lib/websocket/javax-websocket-server-impl-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-api-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-client-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-common-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-server-TEST.jar
-LIB|${jetty.base}/lib/websocket/websocket-servlet-TEST.jar
+LIB|${jetty.base}/lib/websocket/javax-websocket-client-impl-9.3.jar
+LIB|${jetty.base}/lib/websocket/javax-websocket-server-impl-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-api-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-client-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-common-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-server-9.3.jar
+LIB|${jetty.base}/lib/websocket/websocket-servlet-9.3.jar
 
 # The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
+# (these are the properties we actually set in the configuration)
+PROP|jetty.http.port=8080
+# (these are the ones set by default from jetty.home modules)
+PROP|jetty.httpConfig.delayDispatchUntilContent=false
+PROP|jetty.server.dumpAfterStart=false
+PROP|jetty.server.dumpBeforeStop=false
+PROP|jetty.httpConfig.outputBufferSize=32768
+PROP|jetty.httpConfig.requestHeaderSize=8192
+PROP|jetty.httpConfig.responseHeaderSize=8192
+PROP|jetty.httpConfig.sendDateHeader=false
+PROP|jetty.httpConfig.sendServerVersion=true
+PROP|jetty.threadPool.maxThreads=200
+PROP|jetty.threadPool.minThreads=10
+PROP|jetty.threadPool.idleTimeout=60000
+
+# Files
+FILE|lib/
+FILE|lib/ext/
+FILE|resources/
+FILE|webapps/
diff --git a/jetty-start/src/test/resources/dist-home/etc/protonego-alpn.xml b/jetty-start/src/test/resources/dist-home/etc/jetty-alpn.xml
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/etc/protonego-alpn.xml
rename to jetty-start/src/test/resources/dist-home/etc/jetty-alpn.xml
diff --git a/jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml b/jetty-start/src/test/resources/dist-home/etc/jetty-debuglog.xml
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml
rename to jetty-start/src/test/resources/dist-home/etc/jetty-debuglog.xml
diff --git a/jetty-start/src/test/resources/dist-home/etc/jetty-spdy.xml b/jetty-start/src/test/resources/dist-home/etc/jetty-gzip.xml
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/etc/jetty-spdy.xml
rename to jetty-start/src/test/resources/dist-home/etc/jetty-gzip.xml
diff --git a/jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml b/jetty-start/src/test/resources/dist-home/etc/jetty-http2.xml
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml
copy to jetty-start/src/test/resources/dist-home/etc/jetty-http2.xml
diff --git a/jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml b/jetty-start/src/test/resources/dist-home/etc/jetty-http2c.xml
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml
copy to jetty-start/src/test/resources/dist-home/etc/jetty-http2c.xml
diff --git a/jetty-start/src/test/resources/dist-home/etc/protonego-alpn.xml b/jetty-start/src/test/resources/dist-home/etc/jetty-infinispan.xml
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/etc/protonego-alpn.xml
copy to jetty-start/src/test/resources/dist-home/etc/jetty-infinispan.xml
diff --git a/jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml b/jetty-start/src/test/resources/dist-home/etc/jetty-jdbc-sessions.xml
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml
copy to jetty-start/src/test/resources/dist-home/etc/jetty-jdbc-sessions.xml
diff --git a/jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml b/jetty-start/src/test/resources/dist-home/etc/jetty-nosql.xml
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml
copy to jetty-start/src/test/resources/dist-home/etc/jetty-nosql.xml
diff --git a/jetty-start/src/test/resources/dist-home/etc/jetty-spdy-proxy.xml b/jetty-start/src/test/resources/dist-home/etc/jetty-spdy-proxy.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/etc/jetty-spdy-proxy.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml b/jetty-start/src/test/resources/dist-home/etc/jetty-ssl-context.xml
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/etc/jetty-debug.xml
copy to jetty-start/src/test/resources/dist-home/etc/jetty-ssl-context.xml
diff --git a/jetty-start/src/test/resources/dist-home/etc/jetty-xinetd.xml b/jetty-start/src/test/resources/dist-home/etc/jetty-xinetd.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/etc/jetty-xinetd.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/etc/protonego-npn.xml b/jetty-start/src/test/resources/dist-home/etc/protonego-npn.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/etc/protonego-npn.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.mortbay.jasper.apache-el-8.0.9.M3.jar b/jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.eclipse.jetty.apache-jsp-9.3.jar
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.mortbay.jasper.apache-el-8.0.9.M3.jar
rename to jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.eclipse.jetty.apache-jsp-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.eclipse.jetty.apache-jsp-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.mortbay.jasper.apache-el-8.0.20.M0.jar
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.eclipse.jetty.apache-jsp-TEST.jar
rename to jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.mortbay.jasper.apache-el-8.0.20.M0.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.eclipse.jetty.apache-jsp-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.mortbay.jasper.apache-jsp-8.0.20.M0.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.eclipse.jetty.apache-jsp-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.mortbay.jasper.apache-jsp-8.0.20.M0.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.mortbay.jasper.apache-jsp-8.0.9.M3.jar b/jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.mortbay.jasper.apache-jsp-8.0.9.M3.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/apache-jsp/org.mortbay.jasper.apache-jsp-8.0.9.M3.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/cdi-core-9.3.jar
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
rename to jetty-start/src/test/resources/dist-home/lib/cdi-core-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/cdi-servlet-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/cdi-servlet-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/cdi-websocket-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/cdi-websocket-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/fcgi/fcgi-client-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/fcgi/fcgi-client-9.3.jar
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/lib/fcgi/fcgi-client-TEST.jar
rename to jetty-start/src/test/resources/dist-home/lib/fcgi/fcgi-client-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/fcgi/fcgi-client-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/fcgi/fcgi-server-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/fcgi/fcgi-client-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/fcgi/fcgi-server-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/fcgi/fcgi-server-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/fcgi/fcgi-server-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/fcgi/fcgi-server-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jsp/jetty-jsp-jdt-2.3.3.jar b/jetty-start/src/test/resources/dist-home/lib/http2/http2-common-9.3.jar
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/lib/jsp/jetty-jsp-jdt-2.3.3.jar
rename to jetty-start/src/test/resources/dist-home/lib/http2/http2-common-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jsp/jetty-jsp-jdt-2.3.3.jar b/jetty-start/src/test/resources/dist-home/lib/http2/http2-hpack-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jsp/jetty-jsp-jdt-2.3.3.jar
copy to jetty-start/src/test/resources/dist-home/lib/http2/http2-hpack-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jsp/jetty-jsp-jdt-2.3.3.jar b/jetty-start/src/test/resources/dist-home/lib/http2/http2-server-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jsp/jetty-jsp-jdt-2.3.3.jar
copy to jetty-start/src/test/resources/dist-home/lib/http2/http2-server-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-alpn-server-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-alpn-server-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-alpn-server-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-alpn-server-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-alpn-server-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-annotations-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-annotations-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-annotations-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-annotations-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-annotations-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-client-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-client-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-client-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-client-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-client-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-alpn-client-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-continuation-9.3.jar
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/lib/jetty-alpn-client-TEST.jar
rename to jetty-start/src/test/resources/dist-home/lib/jetty-continuation-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-continuation-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-continuation-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-continuation-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-deploy-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-deploy-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-deploy-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-deploy-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-deploy-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-http-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-http-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-http-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-http-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-http-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-infinispan-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-infinispan-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-io-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-io-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-io-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-io-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-io-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-jaas-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-jaas-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-jaas-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-jaas-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-jaas-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-jaspi-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-jaspi-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-jaspi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-jaspi-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-jaspi-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-jmx-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-jmx-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-jmx-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-jmx-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-jmx-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-jndi-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-jndi-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-jndi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-jndi-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-jndi-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-nosql-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-nosql-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-plus-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-plus-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-plus-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-plus-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-plus-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-proxy-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-proxy-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-proxy-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-proxy-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-proxy-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-quickstart-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-quickstart-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-quickstart-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-quickstart-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-quickstart-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-rewrite-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-rewrite-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-rewrite-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-rewrite-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-rewrite-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-security-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-security-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-security-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-security-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-security-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-server-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-server-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-server-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-server-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-server-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-servlet-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-servlet-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-servlet-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-servlet-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-servlet-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-servlets-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-servlets-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-servlets-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-servlets-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-servlets-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-util-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-util-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-util-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-util-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-util-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-webapp-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-webapp-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-webapp-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-webapp-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-webapp-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-xml-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/jetty-cdi-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/jetty-xml-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/jetty-xml-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/jetty-xml-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jetty-xml-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jsp/javax.el-3.0.0.jar b/jetty-start/src/test/resources/dist-home/lib/jsp/javax.el-3.0.0.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jsp/javax.el-3.0.0.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jsp/javax.servlet.jsp-2.3.2.jar b/jetty-start/src/test/resources/dist-home/lib/jsp/javax.servlet.jsp-2.3.2.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jsp/javax.servlet.jsp-2.3.2.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jsp/javax.servlet.jsp-api-2.3.1.jar b/jetty-start/src/test/resources/dist-home/lib/jsp/javax.servlet.jsp-api-2.3.1.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jsp/javax.servlet.jsp-api-2.3.1.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jsp/javax.servlet.jsp.jstl-1.2.2.jar b/jetty-start/src/test/resources/dist-home/lib/jsp/javax.servlet.jsp.jstl-1.2.2.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jsp/javax.servlet.jsp.jstl-1.2.2.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar b/jetty-start/src/test/resources/dist-home/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/jsp/org.eclipse.jetty.orbit.javax.servlet.jsp.jstl-1.2.0.v201105211821.jar b/jetty-start/src/test/resources/dist-home/lib/jsp/org.eclipse.jetty.orbit.javax.servlet.jsp.jstl-1.2.0.v201105211821.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/jsp/org.eclipse.jetty.orbit.javax.servlet.jsp.jstl-1.2.0.v201105211821.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/monitor/jetty-monitor-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/monitor/jetty-monitor-9.3.jar
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/lib/monitor/jetty-monitor-TEST.jar
rename to jetty-start/src/test/resources/dist-home/lib/monitor/jetty-monitor-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/setuid/jetty-setuid-java-1.0.1.jar b/jetty-start/src/test/resources/dist-home/lib/setuid/jetty-setuid-java-1.0.3.jar
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/lib/setuid/jetty-setuid-java-1.0.1.jar
rename to jetty-start/src/test/resources/dist-home/lib/setuid/jetty-setuid-java-1.0.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-client-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-client-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-client-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-core-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-core-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-core-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-http-common-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-http-common-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-http-common-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-http-server-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-http-server-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-http-server-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-server-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-server-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/spdy/spdy-server-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/spring/jetty-spring-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/spring/jetty-spring-9.3.jar
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/lib/spring/jetty-spring-TEST.jar
rename to jetty-start/src/test/resources/dist-home/lib/spring/jetty-spring-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/websocket/javax-websocket-client-impl-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/websocket/javax-websocket-client-impl-9.3.jar
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/lib/websocket/javax-websocket-client-impl-TEST.jar
rename to jetty-start/src/test/resources/dist-home/lib/websocket/javax-websocket-client-impl-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/websocket/javax-websocket-client-impl-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/websocket/javax-websocket-server-impl-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/websocket/javax-websocket-client-impl-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/websocket/javax-websocket-server-impl-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/websocket/javax-websocket-server-impl-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/websocket/javax-websocket-server-impl-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/websocket/javax-websocket-server-impl-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-api-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-api-9.3.jar
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/lib/websocket/websocket-api-TEST.jar
rename to jetty-start/src/test/resources/dist-home/lib/websocket/websocket-api-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-api-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-client-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/websocket/websocket-api-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/websocket/websocket-client-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-client-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-client-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-client-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-api-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-common-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/websocket/websocket-api-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/websocket/websocket-common-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-common-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-common-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-common-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-api-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-server-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/websocket/websocket-api-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/websocket/websocket-server-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-server-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-server-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-server-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-api-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-servlet-9.3.jar
similarity index 100%
copy from jetty-start/src/test/resources/dist-home/lib/websocket/websocket-api-TEST.jar
copy to jetty-start/src/test/resources/dist-home/lib/websocket/websocket-servlet-9.3.jar
diff --git a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-servlet-TEST.jar b/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-servlet-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/dist-home/lib/websocket/websocket-servlet-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0.mod
new file mode 100644
index 0000000..cb91b4c
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+maven://org.mortbay.jetty.alpn/alpn-boot/8.1.0.v20141016|lib/alpn/alpn-boot-8.1.0.v20141016.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_05.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_05.mod
new file mode 100644
index 0000000..cb91b4c
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_05.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+maven://org.mortbay.jetty.alpn/alpn-boot/8.1.0.v20141016|lib/alpn/alpn-boot-8.1.0.v20141016.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_101.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_101.mod
new file mode 100644
index 0000000..8923d6a
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_101.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_102.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_102.mod
new file mode 100644
index 0000000..8923d6a
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_102.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_11.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_11.mod
new file mode 100644
index 0000000..cb91b4c
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_11.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+maven://org.mortbay.jetty.alpn/alpn-boot/8.1.0.v20141016|lib/alpn/alpn-boot-8.1.0.v20141016.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_111.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_111.mod
new file mode 100644
index 0000000..8923d6a
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_111.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_112.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_112.mod
new file mode 100644
index 0000000..cfdaff1
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_112.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.10.v20161026/alpn-boot-8.1.10.v20161026.jar|lib/alpn/alpn-boot-8.1.10.v20161026.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.10.v20161026.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_121.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_121.mod
new file mode 100644
index 0000000..4eb78ef
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_121.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.11.v20170118/alpn-boot-8.1.11.v20170118.jar|lib/alpn/alpn-boot-8.1.11.v20170118.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.11.v20170118.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_131.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_131.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_131.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_131.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_20.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_20.mod
new file mode 100644
index 0000000..cb91b4c
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_20.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+maven://org.mortbay.jetty.alpn/alpn-boot/8.1.0.v20141016|lib/alpn/alpn-boot-8.1.0.v20141016.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_25.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_25.mod
new file mode 100644
index 0000000..6d6d75e
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_25.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+maven://org.mortbay.jetty.alpn/alpn-boot/8.1.2.v20141202|lib/alpn/alpn-boot-8.1.2.v20141202.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.2.v20141202.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_31.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_31.mod
new file mode 100644
index 0000000..e52bd23
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_31.mod
@@ -0,0 +1,8 @@
+[name]
+alpn-boot
+
+[files]
+maven://org.mortbay.jetty.alpn/alpn-boot/8.1.3.v20150130|lib/alpn/alpn-boot-8.1.3.v20150130.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.3.v20150130.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_40.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_40.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_40.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_40.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_45.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_45.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_45.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_45.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_51.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_51.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_51.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_51.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_60.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_60.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_60.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_60.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_65.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_65.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_65.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_65.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_66.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_66.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_66.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_66.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_71.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_71.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_71.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_71.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_72.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_72.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_72.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_72.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_73.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_73.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_73.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_73.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_74.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_74.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_74.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_74.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_77.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_77.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_77.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_77.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_91.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_91.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_91.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_91.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_92.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_92.mod
similarity index 100%
rename from jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_92.mod
rename to jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_92.mod
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-8.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-8.mod
new file mode 100644
index 0000000..cc0befd
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-8.mod
@@ -0,0 +1,30 @@
+# ALPN is provided via a -Xbootclasspath that modifies the secure connections
+# in java to support the ALPN layer needed for HTTP/2.
+#
+# This modification has a tight dependency on specific recent updates of
+# Java 1.7 and Java 1.8 (Java versions prior to 1.7u40 are not supported).
+#
+# The alpn module will use an appropriate alpn-boot jar for your
+# specific version of Java.
+#
+# IMPORTANT: Versions of Java that exist after this module was created are
+#            not guaranteed to work with existing alpn-boot jars, and might
+#            need a new alpn-boot to be created / tested / deployed by the
+#            Jetty project in order to provide support for these future
+#            Java versions.
+#
+# All versions of the alpn-boot jar can be found at
+# http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/
+
+[depend]
+alpn-impl/alpn-${java.version}
+
+[files]
+lib/
+lib/alpn/
+
+[license]
+ALPN is a hosted at github under the GPL v2 with ClassPath Exception.
+ALPN replaces/modifies OpenJDK classes in the sun.security.ssl package.
+http://github.com/jetty-project/jetty-alpn
+http://openjdk.java.net/legal/gplv2+ce.html
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-9.mod b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-9.mod
new file mode 100644
index 0000000..731b744
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-9.mod
@@ -0,0 +1,5 @@
+# Provides support for ALPN based on JDK 9.
+
+[lib]
+lib/jetty-alpn-java-server-${jetty.version}.jar
+lib/alpn-api-*.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/alpn.mod b/jetty-start/src/test/resources/dist-home/modules/alpn.mod
new file mode 100644
index 0000000..a16dbf5
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/alpn.mod
@@ -0,0 +1,22 @@
+[depend]
+ssl
+alpn-impl/alpn-${java.version.platform}
+
+[lib]
+lib/jetty-alpn-client-${jetty.version}.jar
+lib/jetty-alpn-server-${jetty.version}.jar
+
+[xml]
+etc/jetty-alpn.xml
+
+[ini-template]
+## Overrides the order protocols are chosen by the server.
+## The default order is that specified by the order of the
+## modules declared in start.ini.
+# jetty.alpn.protocols=h2,http/1.1
+
+## Specifies what protocol to use when negotiation fails.
+# jetty.alpn.defaultProtocol=http/1.1
+
+## ALPN debug logging on System.err
+# jetty.alpn.debug=false
diff --git a/jetty-start/src/test/resources/dist-home/modules/apache-jsp.mod b/jetty-start/src/test/resources/dist-home/modules/apache-jsp.mod
new file mode 100644
index 0000000..5123670
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/apache-jsp.mod
@@ -0,0 +1,10 @@
+#
+# Apache JSP Module
+#
+
+[name]
+apache-jsp
+
+[lib]
+lib/apache-jsp/*.jar
+
diff --git a/jetty-start/src/test/resources/dist-home/modules/apache-jstl.mod b/jetty-start/src/test/resources/dist-home/modules/apache-jstl.mod
new file mode 100644
index 0000000..e4a9001
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/apache-jstl.mod
@@ -0,0 +1,9 @@
+#
+# Apache JSTL 
+#
+
+[name]
+apache-jstl
+
+[lib]
+lib/apache-jstl/*.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/cdi.mod b/jetty-start/src/test/resources/dist-home/modules/cdi.mod
index 68dea58..ebffb55 100644
--- a/jetty-start/src/test/resources/dist-home/modules/cdi.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/cdi.mod
@@ -1,5 +1,5 @@
 #
-# CDI / Weld Jetty module
+# [EXPERIMENTAL] CDI / Weld Jetty module
 #
 
 [depend]
@@ -10,12 +10,24 @@
 jsp
 
 [files]
-lib/weld/
-http://central.maven.org/maven2/org/jboss/weld/servlet/weld-servlet/2.2.5.Final/weld-servlet-2.2.5.Final.jar|lib/weld/weld-servlet-2.2.5.Final.jar
+lib/cdi/
+maven://javax.enterprise/cdi-api/1.2|lib/cdi/javax.enterprise.cdi-api-1.2.jar
+maven://javax.interceptor/javax.interceptor-api/1.2|lib/cdi/javax.interceptor-api-1.2.jar
+maven://javax.inject/javax.inject/1|lib/cdi/javax.inject-1.0.jar
+maven://org.jboss.weld.servlet/weld-servlet-core/2.2.9.Final|lib/cdi/weld-servlet-core-2.2.9.Final.jar
+maven://org.jboss.weld.environment/weld-environment-common/2.2.9.Final|lib/cdi/weld-environment-common-2.2.9.Final.jar
+maven://org.jboss.weld/weld-core-impl/2.2.9.Final|lib/cdi/weld-core-impl-2.2.9.Final.jar
+maven://org.jboss.classfilewriter/jboss-classfilewriter/1.0.5.Final|lib/cdi/jboss-classfilewriter-1.0.5.Final.jar
+maven://com.google.guava/guava/13.0.1|lib/cdi/com.google.guava.guava-13.0.1.jar
+maven://org.jboss.weld/weld-spi/2.2.SP3|lib/cdi/weld-spi-2.2.SP3.jar
+maven://org.jboss.weld/weld-api/2.2.SP3|lib/cdi/weld-api-2.2.SP3.jar
+maven://org.jboss.logging/jboss-logging/3.1.3.GA|lib/cdi/jboss-logging-3.1.3.GA.jar
+
 
 [lib]
-lib/weld/weld-servlet-2.2.5.Final.jar
-lib/jetty-cdi-${jetty.version}.jar
+lib/cdi/*.jar
+lib/cdi-core-${jetty.version}.jar
+lib/cdi-servlet-${jetty.version}.jar
 
 [xml]
 etc/jetty-cdi.xml
diff --git a/jetty-start/src/test/resources/dist-home/modules/debug.mod b/jetty-start/src/test/resources/dist-home/modules/debug.mod
deleted file mode 100644
index f740ea2..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/debug.mod
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Debug module
-#
-
-[depend]
-server
-
-[xml]
-etc/jetty-debug.xml
diff --git a/jetty-start/src/test/resources/dist-home/modules/debuglog.mod b/jetty-start/src/test/resources/dist-home/modules/debuglog.mod
new file mode 100644
index 0000000..ba8b60a
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/debuglog.mod
@@ -0,0 +1,25 @@
+#
+# Debug module
+#
+
+[depend]
+server
+
+[files]
+logs/
+
+[xml]
+etc/jetty-debuglog.xml
+
+[ini-template]
+## Logging directory (relative to $jetty.base)
+# jetty.debuglog.dir=logs
+
+## Whether to append to existing file
+# jetty.debuglog.append=false
+
+## How many days to retain old log files
+# jetty.debuglog.retainDays=90
+
+## Timezone of the log entries
+# jetty.debuglog.timezone=GMT
diff --git a/jetty-start/src/test/resources/dist-home/modules/deploy.mod b/jetty-start/src/test/resources/dist-home/modules/deploy.mod
index f16b3f2..f567a20 100644
--- a/jetty-start/src/test/resources/dist-home/modules/deploy.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/deploy.mod
@@ -15,7 +15,11 @@
 etc/jetty-deploy.xml
 
 [ini-template]
-## DeployManager configuration
-# Monitored Directory name (relative to jetty.base)
-# jetty.deploy.monitoredDirName=webapps
+# Monitored directory name (relative to $jetty.base)
+# jetty.deploy.monitoredDir=webapps
 
+# Monitored directory scan period (seconds)
+# jetty.deploy.scanInterval=1
+
+# Whether to extract *.war files
+# jetty.deploy.extractWars=true
diff --git a/jetty-start/src/test/resources/dist-home/modules/fcgi.mod b/jetty-start/src/test/resources/dist-home/modules/fcgi.mod
index e837b00..14152d5 100644
--- a/jetty-start/src/test/resources/dist-home/modules/fcgi.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/fcgi.mod
@@ -12,4 +12,4 @@
 
 [ini-template]
 ## For configuration of FastCGI contexts, see
-## TODO: documentation url here
+## https://www.eclipse.org/jetty/documentation/current/fastcgi.html
diff --git a/jetty-start/src/test/resources/dist-home/modules/gzip.mod b/jetty-start/src/test/resources/dist-home/modules/gzip.mod
new file mode 100644
index 0000000..1efc834
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/gzip.mod
@@ -0,0 +1,23 @@
+#
+# GZIP module
+# Applies GzipHandler to entire server
+#
+
+[depend]
+server
+
+[xml]
+etc/jetty-gzip.xml
+
+[ini-template]
+## Minimum content length after which gzip is enabled
+# jetty.gzip.minGzipSize=2048
+
+## Check whether a file with *.gz extension exists
+# jetty.gzip.checkGzExists=false
+
+## Gzip compression level (-1 for default)
+# jetty.gzip.compressionLevel=-1
+
+## User agents for which gzip is disabled
+# jetty.gzip.excludedUserAgent=.*MSIE.6\.0.*
diff --git a/jetty-start/src/test/resources/dist-home/modules/hawtio.mod b/jetty-start/src/test/resources/dist-home/modules/hawtio.mod
index 2dfb31b..f6d0d9d 100644
--- a/jetty-start/src/test/resources/dist-home/modules/hawtio.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/hawtio.mod
@@ -22,7 +22,7 @@
 http://www.apache.org/licenses/LICENSE-2.0.html
 
 [ini-template]
-
+## Hawt.io configuration
 -Dhawtio.authenticationEnabled=false
 -Dhawtio.dirname=/dirname
 -Dhawtio.config.dir=${jetty.base}/etc/hawtio
diff --git a/jetty-start/src/test/resources/dist-home/modules/http.mod b/jetty-start/src/test/resources/dist-home/modules/http.mod
index dc34bc3..01e9862 100644
--- a/jetty-start/src/test/resources/dist-home/modules/http.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/http.mod
@@ -11,17 +11,26 @@
 [ini-template]
 ### HTTP Connector Configuration
 
-## HTTP port to listen on
-jetty.port=8080
+## Connector host/address to bind to
+# jetty.http.host=0.0.0.0
 
-## HTTP idle timeout in milliseconds
-http.timeout=30000
+## Connector port to listen on
+# jetty.http.port=8080
 
-## HTTP Socket.soLingerTime in seconds. (-1 to disable)
-# http.soLingerTime=-1
+## Connector idle timeout in milliseconds
+# jetty.http.idleTimeout=30000
 
-## Parameters to control the number and priority of acceptors and selectors
-# http.selectors=1
-# http.acceptors=1
-# http.selectorPriorityDelta=0
-# http.acceptorPriorityDelta=0
+## Connector socket linger time in seconds (-1 to disable)
+# jetty.http.soLingerTime=-1
+
+## Number of acceptors (-1 picks default based on number of cores)
+# jetty.http.acceptors=-1
+
+## Number of selectors (-1 picks default based on number of cores)
+# jetty.http.selectors=-1
+
+## ServerSocketChannel backlog (0 picks platform default)
+# jetty.http.acceptorQueueSize=0
+
+## Thread priority delta to give to acceptor threads
+# jetty.http.acceptorPriorityDelta=0
diff --git a/jetty-start/src/test/resources/dist-home/modules/http2.mod b/jetty-start/src/test/resources/dist-home/modules/http2.mod
new file mode 100644
index 0000000..6b2b0cd
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/http2.mod
@@ -0,0 +1,20 @@
+#
+# HTTP2 Support Module
+#
+
+[depend]
+ssl
+alpn
+
+[lib]
+lib/http2/*.jar
+
+[xml]
+etc/jetty-http2.xml
+
+[ini-template]
+## Max number of concurrent streams per connection
+# jetty.http2.maxConcurrentStreams=1024
+
+## Initial stream receive window (client to server)
+# jetty.http2.initialStreamRecvWindow=65535
diff --git a/jetty-start/src/test/resources/dist-home/modules/http2c.mod b/jetty-start/src/test/resources/dist-home/modules/http2c.mod
new file mode 100644
index 0000000..3792194
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/http2c.mod
@@ -0,0 +1,22 @@
+#
+# HTTP2 Clear Text Support Module
+# This module adds support for HTTP/2 clear text to the
+# HTTP/1 clear text connector (defined in jetty-http.xml).
+# The resulting connector will accept both HTTP/1 and HTTP/2 connections.
+#
+
+[depend]
+http
+
+[lib]
+lib/http2/*.jar
+
+[xml]
+etc/jetty-http2c.xml
+
+[ini-template]
+## Max number of concurrent streams per connection
+# jetty.http2.maxConcurrentStreams=1024
+
+## Initial stream receive window (client to server)
+# jetty.http2.initialStreamRecvWindow=65535
diff --git a/jetty-start/src/test/resources/dist-home/modules/https.mod b/jetty-start/src/test/resources/dist-home/modules/https.mod
index bd1b718..092e0d7 100644
--- a/jetty-start/src/test/resources/dist-home/modules/https.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/https.mod
@@ -5,15 +5,9 @@
 [depend]
 ssl
 
+[optional]
+http2
+
 [xml]
 etc/jetty-https.xml
 
-[ini-template]
-## HTTPS Configuration
-# HTTP port to listen on
-https.port=8443
-# HTTPS idle timeout in milliseconds
-https.timeout=30000
-# HTTPS Socket.soLingerTime in seconds. (-1 to disable)
-# https.soLingerTime=-1
-
diff --git a/jetty-start/src/test/resources/dist-home/modules/infinispan.mod b/jetty-start/src/test/resources/dist-home/modules/infinispan.mod
new file mode 100644
index 0000000..afa39fc
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/infinispan.mod
@@ -0,0 +1,33 @@
+#
+# Jetty Infinispan module
+#
+
+[depend]
+annotations
+webapp
+
+[files]
+maven://org.infinispan/infinispan-core/7.1.1.Final|lib/infinispan/infinispan-core-7.1.1.Final.jar
+maven://org.infinispan/infinispan-commons/7.1.1.Final|lib/infinispan/infinispan-commons-7.1.1.Final.jar
+maven://org.jgroups/jgroups/3.6.1.Final|lib/infinispan/jgroups-3.6.1.Final.jar
+maven://org.jboss.marshalling/jboss-marshalling-osgi/1.4.4.Final|lib/infinispan/jboss-marshalling-osgi-1.4.4.Final.jar
+maven://org.jboss.logging/jboss-logging/3.1.2.GA|lib/infinispan/jboss-logging-3.1.2.GA.jar
+
+[lib]
+lib/jetty-infinispan-${jetty.version}.jar
+lib/infinispan/*.jar
+
+[xml]
+etc/jetty-infinispan.xml
+
+[license]
+Infinispan is an open source project hosted on Github and released under the Apache 2.0 license.
+http://infinispan.org/
+http://www.apache.org/licenses/LICENSE-2.0.html
+
+[ini-template]
+## Infinispan Session config
+
+## Unique identifier for this node in the cluster
+# jetty.infinispanSession.workerName=node1
+
diff --git a/jetty-start/src/test/resources/dist-home/modules/jaas.mod b/jetty-start/src/test/resources/dist-home/modules/jaas.mod
index 4932140..fee3f59 100644
--- a/jetty-start/src/test/resources/dist-home/modules/jaas.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/jaas.mod
@@ -12,5 +12,6 @@
 etc/jetty-jaas.xml
 
 [ini-template]
-## JAAS Configuration
-jaas.login.conf=etc/login.conf
+## The file location (relative to $jetty.base) for the
+## JAAS "java.security.auth.login.config" system property
+# jetty.jaas.login.conf=etc/login.conf
diff --git a/jetty-start/src/test/resources/dist-home/modules/jamon.mod b/jetty-start/src/test/resources/dist-home/modules/jamon.mod
index 2aeb2ad..2d1f144 100644
--- a/jetty-start/src/test/resources/dist-home/modules/jamon.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/jamon.mod
@@ -13,8 +13,8 @@
 
 [files]
 lib/jamon/
-http://central.maven.org/maven2/com/jamonapi/jamon/2.79/jamon-2.79.jar|lib/jamon/jamon-2.79.jar
-http://central.maven.org/maven2/com/jamonapi/jamon_war/2.79/jamon_war-2.79.war|lib/jamon/jamon.war
+maven://com.jamonapi/jamon/2.79|lib/jamon/jamon-2.79.jar
+maven://com.jamonapi/jamon_war/2.79/war|lib/jamon/jamon.war
 
 [lib]
 lib/jamon/**.jar
@@ -25,6 +25,7 @@
 http://jamonapi.sourceforge.net/JAMonLicense.html
 
 [ini-template]
+## Jamon Configuration
+# jamon.summaryLabels=demo
 jamon.summaryLabels=default, request.getStatus().contextpath.value.ms
-#jamon.summaryLabels=demo
 
diff --git a/jetty-start/src/test/resources/dist-home/modules/jdbc-sessions.mod b/jetty-start/src/test/resources/dist-home/modules/jdbc-sessions.mod
new file mode 100644
index 0000000..d77ff04
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/jdbc-sessions.mod
@@ -0,0 +1,27 @@
+#
+# Jetty JDBC Session module
+#
+
+[depend]
+annotations
+webapp
+
+[xml]
+etc/jetty-jdbc-sessions.xml
+
+
+[ini-template]
+## JDBC Session config
+
+## Unique identifier for this node in the cluster
+# jetty.jdbcSession.workerName=node1
+
+## The interval in seconds between sweeps of the scavenger
+# jetty.jdbcSession.scavenge=600
+
+## Uncomment either the datasource name or driverClass and connectionURL
+# jetty.jdbcSession.datasource=sessions
+# jetty.jdbcSession.driverClass=changeme
+# jetty.jdbcSession.connectionURL=changeme
+
+
diff --git a/jetty-start/src/test/resources/dist-home/modules/jminix.mod b/jetty-start/src/test/resources/dist-home/modules/jminix.mod
index b35d702..05788f0 100644
--- a/jetty-start/src/test/resources/dist-home/modules/jminix.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/jminix.mod
@@ -11,21 +11,21 @@
 
 [files]
 lib/jminix/
-http://central.maven.org/maven2/org/jminix/jminix/1.1.0/jminix-1.1.0.jar|lib/jminix/jminix-1.1.0.jar
+maven://org.jminix/jminix/1.1.0|lib/jminix/jminix-1.1.0.jar
 http://maven.restlet.com/org/restlet/org.restlet/1.1.5/org.restlet-1.1.5.jar|lib/jminix/org.restlet-1.1.5.jar
 http://maven.restlet.com/org/restlet/org.restlet.ext.velocity/1.1.5/org.restlet.ext.velocity-1.1.5.jar|lib/jminix/org.restlet.ext.velocity-1.1.5.jar
-http://central.maven.org/maven2/org/apache/velocity/velocity/1.5/velocity-1.5.jar|lib/jminix/velocity-1.5.jar
-http://central.maven.org/maven2/oro/oro/2.0.8/oro-2.0.8.jar|lib/jminix/oro-2.0.8.jar
+maven://org.apache.velocity/velocity/1.5|lib/jminix/velocity-1.5.jar
+maven://oro/oro/2.0.8|lib/jminix/oro-2.0.8.jar
 http://maven.restlet.com/com/noelios/restlet/com.noelios.restlet/1.1.5/com.noelios.restlet-1.1.5.jar|lib/jminix/com.noelios.restlet-1.1.5.jar
 http://maven.restlet.com/com/noelios/restlet/com.noelios.restlet.ext.servlet/1.1.5/com.noelios.restlet.ext.servlet-1.1.5.jar|lib/jminix/com.noelios.restlet.ext.servlet-1.1.5.jar
-http://central.maven.org/maven2/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar|lib/jminix/commons-logging-1.1.1.jar
-http://repo2.maven.org/maven2/net/sf/json-lib/json-lib/2.2.3/json-lib-2.2.3-jdk15.jar|lib/jminix/json-lib-2.2.3-jdk15.jar
-http://central.maven.org/maven2/commons-lang/commons-lang/2.4/commons-lang-2.4.jar|lib/jminix/commons-lang-2.4.jar
-http://central.maven.org/maven2/commons-beanutils/commons-beanutils/1.7.0/commons-beanutils-1.7.0.jar|lib/jminix/commons-beanutils-1.7.0.jar
-http://central.maven.org/maven2/commons-collections/commons-collections/3.2/commons-collections-3.2.jar|lib/jminix/commons-collections-3.2.jar
-http://central.maven.org/maven2/net/sf/ezmorph/ezmorph/1.0.6/ezmorph-1.0.6.jar|lib/jminix/ezmorph-1.0.6.jar
-http://central.maven.org/maven2/org/jgroups/jgroups/2.12.1.3.Final/jgroups-2.12.1.3.Final.jar|lib/jminix/jgroups-2.12.1.3.Final.jar
-http://central.maven.org/maven2/org/jasypt/jasypt/1.8/jasypt-1.8.jar|lib/jminix/jasypt-1.8.jar
+maven://commons-logging/commons-logging/1.1.1|lib/jminix/commons-logging-1.1.1.jar
+maven://net.sf.json-lib/json-lib/2.2.3/jar/jdk15|lib/jminix/json-lib-2.2.3-jdk15.jar
+maven://commons-lang/commons-lang/2.4|lib/jminix/commons-lang-2.4.jar
+maven://commons-beanutils/commons-beanutils/1.7.0|lib/jminix/commons-beanutils-1.7.0.jar
+maven://commons-collections/commons-collections/3.2|lib/jminix/commons-collections-3.2.jar
+maven://net.sf.ezmorph/ezmorph/1.0.6|lib/jminix/ezmorph-1.0.6.jar
+maven://org.jgroups/jgroups/2.12.1.3.Final|lib/jminix/jgroups-2.12.1.3.Final.jar
+maven://org.jasypt/jasypt/1.8|lib/jminix/jasypt-1.8.jar
 
 [lib]
 lib/jminix/**.jar
@@ -36,6 +36,6 @@
 http://www.apache.org/licenses/LICENSE-2.0
 
 [ini-template]
-# Jminix Configuration
+## Jminix Configuration
 jminix.port=8088
 
diff --git a/jetty-start/src/test/resources/dist-home/modules/jmx-remote.mod b/jetty-start/src/test/resources/dist-home/modules/jmx-remote.mod
index b6be74a..f8a5111 100644
--- a/jetty-start/src/test/resources/dist-home/modules/jmx-remote.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/jmx-remote.mod
@@ -9,10 +9,8 @@
 etc/jetty-jmx-remote.xml
 
 [ini-template]
-## JMX Configuration
-## Enable for an open port accessible by remote machines
-# jetty.jmxrmihost=localhost
-# jetty.jmxrmiport=1099
-## Strictly speaking you shouldn't need --exec to use this in most environments.
-## If this isn't working, make sure you enable --exec as well
-# -Dcom.sun.management.jmxremote
+## The host/address to bind RMI to
+# jetty.jmxremote.rmihost=localhost
+
+## The port RMI listens to
+# jetty.jmxremote.rmiport=1099
diff --git a/jetty-start/src/test/resources/dist-home/modules/jolokia.mod b/jetty-start/src/test/resources/dist-home/modules/jolokia.mod
index 876c2fc..da8ac8f 100644
--- a/jetty-start/src/test/resources/dist-home/modules/jolokia.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/jolokia.mod
@@ -11,7 +11,7 @@
 etc/jolokia.xml
 
 [files]
-http://repo1.maven.org/maven2/org/jolokia/jolokia-war/1.2.2/jolokia-war-1.2.2.war|lib/jolokia/jolokia.war
+maven://org.jolokia/jolokia-war/1.2.2/war|lib/jolokia/jolokia.war
 
 [license]
 Jolokia is released under the Apache License 2.0
diff --git a/jetty-start/src/test/resources/dist-home/modules/jsp-impl/apache-jsp.mod b/jetty-start/src/test/resources/dist-home/modules/jsp-impl/apache-jsp.mod
deleted file mode 100644
index aed547c..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/jsp-impl/apache-jsp.mod
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# Apache JSP Module
-#
-
-[name]
-jsp-impl
-
-[lib]
-lib/apache-jsp/*.jar
-
diff --git a/jetty-start/src/test/resources/dist-home/modules/jsp-impl/apache-jstl.mod b/jetty-start/src/test/resources/dist-home/modules/jsp-impl/apache-jstl.mod
deleted file mode 100644
index 804b191..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/jsp-impl/apache-jstl.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Apache JSTL 
-#
-[name]
-jstl-impl
-
-[lib]
-lib/apache-jstl/*.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/jsp-impl/glassfish-jsp.mod b/jetty-start/src/test/resources/dist-home/modules/jsp-impl/glassfish-jsp.mod
deleted file mode 100644
index 130d2b3..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/jsp-impl/glassfish-jsp.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Glassfish JSP Module
-#
-[name]
-jsp-impl
-
-[lib]
-lib/jsp/*.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/jsp-impl/glassfish-jstl.mod b/jetty-start/src/test/resources/dist-home/modules/jsp-impl/glassfish-jstl.mod
deleted file mode 100644
index 4b8e6f3..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/jsp-impl/glassfish-jstl.mod
+++ /dev/null
@@ -1,6 +0,0 @@
-#
-# Glassfish JSTL
-[name]
-jstl-impl
-
-# This file is empty as glassfish jstl is provided by glassfish jsp
diff --git a/jetty-start/src/test/resources/dist-home/modules/jsp.mod b/jetty-start/src/test/resources/dist-home/modules/jsp.mod
index bb31ca7..a16cc93 100644
--- a/jetty-start/src/test/resources/dist-home/modules/jsp.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/jsp.mod
@@ -5,17 +5,5 @@
 [depend]
 servlet
 annotations
-jsp-impl/${jsp-impl}-jsp
+apache-jsp
 
-[ini-template]
-# JSP Configuration
-
-# Select JSP implementation, choices are
-#   glassfish : The reference implementation 
-#               default in jetty <= 9.1
-#   apache    : The apache version 
-#               default jetty >= 9.2
-jsp-impl=apache
-
-# To use a non-jdk compiler for JSP compilation when using glassfish uncomment next line
-# -Dorg.apache.jasper.compiler.disablejsr199=true
diff --git a/jetty-start/src/test/resources/dist-home/modules/jstl.mod b/jetty-start/src/test/resources/dist-home/modules/jstl.mod
index cb06244..efc310a 100644
--- a/jetty-start/src/test/resources/dist-home/modules/jstl.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/jstl.mod
@@ -1,14 +1,8 @@
 #
-# Jetty JSP Module
+# Jetty JSTL Module
 #
 
 [depend]
 jsp
-jsp-impl/${jsp-impl}-jstl
+apache-jstl
 
-[ini-template]
-# JSTL Configuration
-# The glassfish jsp-impl includes JSTL by default and this module
-# is not required to activate it.
-# The apache jsp-impl does not include JSTL by default and this module
-# is required to put JSTL on the container classpath
diff --git a/jetty-start/src/test/resources/dist-home/modules/jvm.mod b/jetty-start/src/test/resources/dist-home/modules/jvm.mod
index 195521c..c1b6381 100644
--- a/jetty-start/src/test/resources/dist-home/modules/jvm.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/jvm.mod
@@ -20,4 +20,3 @@
 # -XX:+PrintTenuringDistribution
 # -XX:+PrintCommandLineFlags
 # -XX:+DisableExplicitGC
-# -Dorg.apache.jasper.compiler.disablejsr199=true
diff --git a/jetty-start/src/test/resources/dist-home/modules/logging.mod b/jetty-start/src/test/resources/dist-home/modules/logging.mod
index a39bfe4..4f30a88 100644
--- a/jetty-start/src/test/resources/dist-home/modules/logging.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/logging.mod
@@ -14,18 +14,23 @@
 
 [ini-template]
 ## Logging Configuration
-# Configure jetty logging for default internal behavior STDERR output
+## Configure jetty logging for default internal behavior STDERR output
 # -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
 
-# Configure jetty logging for slf4j
+## Configure jetty logging for slf4j
 # -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog
 
-# Configure jetty logging for java.util.logging
+## Configure jetty logging for java.util.logging
 # -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.JavaUtilLog
 
-# STDERR / STDOUT Logging
-# Number of days to retain logs
-# jetty.log.retain=90
-# Directory for logging output
-# Either a path relative to ${jetty.base} or an absolute path
-# jetty.logs=logs
+## Logging directory (relative to $jetty.base)
+# jetty.logging.dir=logs
+
+## Whether to append to existing file
+# jetty.logging.append=false
+
+## How many days to retain old log files
+# jetty.logging.retainDays=90
+
+## Timezone of the log timestamps
+# jetty.logging.timezone=GMT
diff --git a/jetty-start/src/test/resources/dist-home/modules/lowresources.mod b/jetty-start/src/test/resources/dist-home/modules/lowresources.mod
index 99112d5..2f765d9 100644
--- a/jetty-start/src/test/resources/dist-home/modules/lowresources.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/lowresources.mod
@@ -9,10 +9,20 @@
 etc/jetty-lowresources.xml
 
 [ini-template]
-## Low Resources Configuration
-# lowresources.period=1050
-# lowresources.lowResourcesIdleTimeout=200
-# lowresources.monitorThreads=true
-# lowresources.maxConnections=0
-# lowresources.maxMemory=0
-# lowresources.maxLowResourcesTime=5000
+## Scan period to look for low resources (in milliseconds)
+# jetty.lowresources.period=1000
+
+## The idle timeout to apply to low resources (in milliseconds)
+# jetty.lowresources.idleTimeout=1000
+
+## Whether to monitor ThreadPool threads for low resources
+# jetty.lowresources.monitorThreads=true
+
+## Max number of connections allowed before being in low resources mode
+# jetty.lowresources.maxConnections=0
+
+## Max memory allowed before being in low resources mode (in bytes)
+# jetty.lowresources.maxMemory=0
+
+## Max time a resource may stay in low resource mode before actions are taken (in milliseconds)
+# jetty.lowresources.maxLowResourcesTime=5000
diff --git a/jetty-start/src/test/resources/dist-home/modules/nosql.mod b/jetty-start/src/test/resources/dist-home/modules/nosql.mod
new file mode 100644
index 0000000..a5b5a9e
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/modules/nosql.mod
@@ -0,0 +1,31 @@
+#
+# Jetty NoSql module
+#
+
+[depend]
+webapp
+
+[files]
+maven://org.mongodb/mongo-java-driver/2.6.1|lib/nosql/mongo-java-driver-2.6.1.jar
+
+[lib]
+lib/jetty-nosql-${jetty.version}.jar
+lib/nosql/*.jar
+
+[xml]
+etc/jetty-nosql.xml
+
+[license]
+The java driver for the MongoDB document-based database system is hosted on GitHub and released under the Apache 2.0 license.
+http://www.mongodb.org/
+http://www.apache.org/licenses/LICENSE-2.0.html
+
+[ini-template]
+## MongoDB SessionIdManager config
+
+## Unique identifier for this node in the cluster
+# jetty.nosqlSession.workerName=node1
+
+## Interval in seconds between scavenging expired sessions
+# jetty.nosqlSession.scavenge=1800
+
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_40.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_40.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_40.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_45.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_45.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_45.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_51.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_51.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_51.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_55.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_55.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_55.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_60.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_60.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_60.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_65.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_65.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_65.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_67.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_67.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_67.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_71.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_71.mod
deleted file mode 100644
index e9b4e2a..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_71.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.2.v20141202/alpn-boot-7.1.2.v20141202.jar|lib/alpn/alpn-boot-7.1.2.v20141202.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.2.v20141202.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_72.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_72.mod
deleted file mode 100644
index e9b4e2a..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_72.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.2.v20141202/alpn-boot-7.1.2.v20141202.jar|lib/alpn/alpn-boot-7.1.2.v20141202.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.2.v20141202.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_75.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_75.mod
deleted file mode 100644
index ac315d6..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_75.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.3.v20150130/alpn-boot-7.1.3.v20150130.jar|lib/alpn/alpn-boot-7.1.3.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.3.v20150130.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_76.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_76.mod
deleted file mode 100644
index ac315d6..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_76.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.3.v20150130/alpn-boot-7.1.3.v20150130.jar|lib/alpn/alpn-boot-7.1.3.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.3.v20150130.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_79.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_79.mod
deleted file mode 100644
index ac315d6..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_79.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.3.v20150130/alpn-boot-7.1.3.v20150130.jar|lib/alpn/alpn-boot-7.1.3.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.3.v20150130.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_80.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_80.mod
deleted file mode 100644
index ac315d6..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.7.0_80.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.3.v20150130/alpn-boot-7.1.3.v20150130.jar|lib/alpn/alpn-boot-7.1.3.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.3.v20150130.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0.mod
deleted file mode 100644
index a81732c..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_05.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_05.mod
deleted file mode 100644
index a81732c..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_05.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_101.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_101.mod
deleted file mode 100644
index a7f656b..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_101.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_102.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_102.mod
deleted file mode 100644
index a7f656b..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_102.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_11.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_11.mod
deleted file mode 100644
index a81732c..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_11.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_111.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_111.mod
deleted file mode 100644
index a7f656b..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_111.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.9.v20160720/alpn-boot-8.1.9.v20160720.jar|lib/alpn/alpn-boot-8.1.9.v20160720.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.9.v20160720.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_112.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_112.mod
deleted file mode 100644
index a09db39..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_112.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.10.v20161026/alpn-boot-8.1.10.v20161026.jar|lib/alpn/alpn-boot-8.1.10.v20161026.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.10.v20161026.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_121.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_121.mod
deleted file mode 100644
index fdd3868..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_121.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.11.v20170118/alpn-boot-8.1.11.v20170118.jar|lib/alpn/alpn-boot-8.1.11.v20170118.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.11.v20170118.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_20.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_20.mod
deleted file mode 100644
index a81732c..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_20.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_25.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_25.mod
deleted file mode 100644
index 8d13261..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_25.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.2.v20141202/alpn-boot-8.1.2.v20141202.jar|lib/alpn/alpn-boot-8.1.2.v20141202.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.2.v20141202.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_31.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_31.mod
deleted file mode 100644
index 4114979..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn-1.8.0_31.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.3.v20150130/alpn-boot-8.1.3.v20150130.jar|lib/alpn/alpn-boot-8.1.3.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.3.v20150130.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn.mod
deleted file mode 100644
index d3e4118..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/alpn.mod
+++ /dev/null
@@ -1,42 +0,0 @@
-# ALPN is provided via a -Xbootclasspath that modifies the secure connections
-# in java to support the ALPN layer needed for SPDY (and eventually HTTP/2)
-#
-# This modification has a tight dependency on specific recent updates of
-# Java 1.7 and Java 1.8
-# (Java versions prior to 1.7u40 are not supported)
-#
-# The alpn protonego module will use an appropriate alpn-boot jar for your
-# specific version of Java.
-#
-# IMPORTANT: Versions of Java that exist after this module was created are
-#            not guaranteed to work with existing alpn-boot jars, and might
-#            need a new alpn-boot to be created / tested / deployed by the
-#            Jetty project in order to provide support for these future
-#            Java versions.
-#
-# All versions of alpn-boot can be found at
-# http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/
-
-[name]
-protonego-impl
-
-[depend]
-protonego-impl/alpn-${java.version}
-
-[lib]
-lib/jetty-alpn-client-${jetty.version}.jar
-lib/jetty-alpn-server-${jetty.version}.jar
-
-[xml]
-etc/protonego-alpn.xml
-
-[files]
-lib/
-lib/alpn/
-
-[license]
-ALPN is a hosted at github under the GPL v2 with ClassPath Exception.
-ALPN replaces/modifies OpenJDK classes in the java.sun.security.ssl package.
-http://github.com/jetty-project/jetty-alpn
-http://openjdk.java.net/legal/gplv2+ce.html
-
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_04.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_04.mod
deleted file mode 100644
index 007570b..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_04.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.0.v20120525/npn-boot-1.1.0.v20120525.jar|lib/npn/npn-boot-1.1.0.v20120525.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.0.v20120525.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_05.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_05.mod
deleted file mode 100644
index 007570b..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_05.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.0.v20120525/npn-boot-1.1.0.v20120525.jar|lib/npn/npn-boot-1.1.0.v20120525.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.0.v20120525.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_06.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_06.mod
deleted file mode 100644
index 868a7a7..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_06.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.1.v20121030/npn-boot-1.1.1.v20121030.jar|lib/npn/npn-boot-1.1.1.v20121030.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.1.v20121030.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_07.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_07.mod
deleted file mode 100644
index 868a7a7..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_07.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.1.v20121030/npn-boot-1.1.1.v20121030.jar|lib/npn/npn-boot-1.1.1.v20121030.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.1.v20121030.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_09.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_09.mod
deleted file mode 100644
index 20c1db2..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_09.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar|lib/npn/npn-boot-1.1.3.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.3.v20130313.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_10.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_10.mod
deleted file mode 100644
index 20c1db2..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_10.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar|lib/npn/npn-boot-1.1.3.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.3.v20130313.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_11.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_11.mod
deleted file mode 100644
index 20c1db2..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_11.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar|lib/npn/npn-boot-1.1.3.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.3.v20130313.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_13.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_13.mod
deleted file mode 100644
index 1645a52..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_13.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.4.v20130313/npn-boot-1.1.4.v20130313.jar|lib/npn/npn-boot-1.1.4.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.4.v20130313.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_15.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_15.mod
deleted file mode 100644
index 73bc090..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_15.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar|lib/npn/npn-boot-1.1.5.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_17.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_17.mod
deleted file mode 100644
index 73bc090..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_17.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar|lib/npn/npn-boot-1.1.5.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_21.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_21.mod
deleted file mode 100644
index 73bc090..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_21.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar|lib/npn/npn-boot-1.1.5.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_25.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_25.mod
deleted file mode 100644
index 73bc090..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_25.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar|lib/npn/npn-boot-1.1.5.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_40.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_40.mod
deleted file mode 100644
index 465e6f0..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_40.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar|lib/npn/npn-boot-1.1.6.v20130911.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_45.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_45.mod
deleted file mode 100644
index 465e6f0..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_45.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar|lib/npn/npn-boot-1.1.6.v20130911.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_51.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_51.mod
deleted file mode 100644
index 465e6f0..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_51.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar|lib/npn/npn-boot-1.1.6.v20130911.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_55.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_55.mod
deleted file mode 100644
index 5f8704d..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_55.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.8.v20141013/npn-boot-1.1.8.v20141013.jar|lib/npn/npn-boot-1.1.8.v20141013.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.8.v20141013.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_60.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_60.mod
deleted file mode 100644
index 5f8704d..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_60.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.8.v20141013/npn-boot-1.1.8.v20141013.jar|lib/npn/npn-boot-1.1.8.v20141013.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.8.v20141013.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_65.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_65.mod
deleted file mode 100644
index 5f8704d..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_65.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.8.v20141013/npn-boot-1.1.8.v20141013.jar|lib/npn/npn-boot-1.1.8.v20141013.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.8.v20141013.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_67.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_67.mod
deleted file mode 100644
index 5f8704d..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_67.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.8.v20141013/npn-boot-1.1.8.v20141013.jar|lib/npn/npn-boot-1.1.8.v20141013.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.8.v20141013.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_71.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_71.mod
deleted file mode 100644
index 851aca8..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_71.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.9.v20141016/npn-boot-1.1.9.v20141016.jar|lib/npn/npn-boot-1.1.9.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.9.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_72.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_72.mod
deleted file mode 100644
index 851aca8..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_72.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.9.v20141016/npn-boot-1.1.9.v20141016.jar|lib/npn/npn-boot-1.1.9.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.9.v20141016.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_75.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_75.mod
deleted file mode 100644
index a5fac11..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_75.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.10.v20150130/npn-boot-1.1.10.v20150130.jar|lib/npn/npn-boot-1.1.10.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.10.v20150130.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_76.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_76.mod
deleted file mode 100644
index a5fac11..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_76.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.10.v20150130/npn-boot-1.1.10.v20150130.jar|lib/npn/npn-boot-1.1.10.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.10.v20150130.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_79.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_79.mod
deleted file mode 100644
index a5fac11..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_79.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.10.v20150130/npn-boot-1.1.10.v20150130.jar|lib/npn/npn-boot-1.1.10.v20150130.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.10.v20150130.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_80.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_80.mod
deleted file mode 100644
index 2cce5fa..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn-1.7.0_80.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.11.v20150415/npn-boot-1.1.11.v20150415.jar|lib/npn/npn-boot-1.1.11.v20150415.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.11.v20150415.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn.mod b/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn.mod
deleted file mode 100644
index 1a2c71d..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego-impl/npn.mod
+++ /dev/null
@@ -1,37 +0,0 @@
-# NPN is provided via a -Xbootclasspath that modifies the secure connections
-# in java to support the NPN layer needed for SPDY.
-#
-# This modification has a tight dependency on specific updates of Java 1.7.
-# (No support for Java 8 exists for npn / npn-boot, use alpn instead)
-#
-# The npn module will use an appropriate npn-boot jar for your specific
-# version of Java.
-#
-# IMPORTANT: Versions of Java that exist after this module was created are
-#            not guaranteed to work with existing npn-boot jars, and might
-#            need a new npn-boot to be created / tested / deployed by the
-#            Jetty project in order to provide support for these future
-#            Java versions.
-#
-# All versions of npn-boot can be found at
-# http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/
-
-
-[name]
-protonego-impl
-
-[depend]
-protonego-impl/npn-${java.version}
-
-[xml]
-etc/protonego-npn.xml
-
-[files]
-lib/
-lib/npn/
-
-[license]
-NPN is a hosted at github under the GPL v2 with ClassPath Exception.
-NPN replaces/modifies OpenJDK classes in the java.sun.security.ssl package.
-http://github.com/jetty-project/jetty-npn
-http://openjdk.java.net/legal/gplv2+ce.html
diff --git a/jetty-start/src/test/resources/dist-home/modules/protonego.mod b/jetty-start/src/test/resources/dist-home/modules/protonego.mod
deleted file mode 100644
index fbf4d08..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/protonego.mod
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# Protocol Negotiatin Selection Module
-#
-
-[depend]
-protonego-impl/${protonego}
-
-[ini-template]
-# Protocol Negotiation Implementation Selection
-#  choices are:
-#    'npn'  : original implementation for SPDY (now deprecated)
-#    'alpn' : replacement for NPN, in use by current SPDY implementations
-#             and the future HTTP/2 spec
-#  Note: java 1.8+ are ALPN only.
-protonego=alpn
-
-# Configuration for NPN
-# npn.protocols=spdy/3,http/1.1
-# npn.defaultProtocol=http/1.1
-
-# Configuration for ALPN
-# alpn.protocols=h2-14,http/1.1
-# alpn.defaultProtocol=http/1.1
-
diff --git a/jetty-start/src/test/resources/dist-home/modules/proxy.mod b/jetty-start/src/test/resources/dist-home/modules/proxy.mod
index a879ae1..6b91f68 100644
--- a/jetty-start/src/test/resources/dist-home/modules/proxy.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/proxy.mod
@@ -14,9 +14,9 @@
 
 [ini-template]
 ## Proxy Configuration
-#jetty.proxy.servletClass=org.eclipse.jetty.proxy.ProxyServlet
-#jetty.proxy.servletMapping=/*
-#jetty.proxy.maxThreads=128
-#jetty.proxy.maxConnections=256
-#jetty.proxy.idleTimeout=30000
-#jetty.proxy.timeout=60000
+# jetty.proxy.servletClass=org.eclipse.jetty.proxy.ProxyServlet
+# jetty.proxy.servletMapping=/*
+# jetty.proxy.maxThreads=128
+# jetty.proxy.maxConnections=256
+# jetty.proxy.idleTimeout=30000
+# jetty.proxy.timeout=60000
diff --git a/jetty-start/src/test/resources/dist-home/modules/quickstart.mod b/jetty-start/src/test/resources/dist-home/modules/quickstart.mod
index 89db9fd..4e59dd0 100644
--- a/jetty-start/src/test/resources/dist-home/modules/quickstart.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/quickstart.mod
@@ -7,6 +7,5 @@
 plus
 annotations
 
-
 [lib]
 lib/jetty-quickstart-${jetty.version}.jar
diff --git a/jetty-start/src/test/resources/dist-home/modules/requestlog.mod b/jetty-start/src/test/resources/dist-home/modules/requestlog.mod
index f5e0614..e27b246 100644
--- a/jetty-start/src/test/resources/dist-home/modules/requestlog.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/requestlog.mod
@@ -12,19 +12,26 @@
 logs/
 
 [ini-template]
-## Request Log Configuration
-# Filename for Request Log output (relative to jetty.base)
-# requestlog.filename=/logs/yyyy_mm_dd.request.log
-# Date format for rollovered files (uses SimpleDateFormat syntax)
-# requestlog.filenameDateFormat=yyyy_MM_dd
-# How many days to retain the logs
-# requestlog.retain=90
-# If an existing log with the same name is found, just append to it
-# requestlog.append=true
-# Use the extended log output
-# requestlog.extended=true
-# Log http cookie information as well
-# requestlog.cookies=true
-# Set the log output timezone
-# requestlog.timezone=GMT
+## Logging directory (relative to $jetty.base)
+# jetty.requestlog.dir=logs
 
+## File path
+# jetty.requestlog.filePath=${jetty.requestlog.dir}/yyyy_mm_dd.request.log
+
+## Date format for rollovered files (uses SimpleDateFormat syntax)
+# jetty.requestlog.filenameDateFormat=yyyy_MM_dd
+
+## How many days to retain old log files
+# jetty.requestlog.retainDays=90
+
+## Whether to append to existing file
+# jetty.requestlog.append=true
+
+## Whether to use the extended log output
+# jetty.requestlog.extended=true
+
+## Whether to log http cookie information
+# jetty.requestlog.cookies=true
+
+## Timezone of the log entries
+# jetty.requestlog.timezone=GMT
diff --git a/jetty-start/src/test/resources/dist-home/modules/rewrite.mod b/jetty-start/src/test/resources/dist-home/modules/rewrite.mod
index d2e00c8..c8a1750 100644
--- a/jetty-start/src/test/resources/dist-home/modules/rewrite.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/rewrite.mod
@@ -10,3 +10,13 @@
 
 [xml]
 etc/jetty-rewrite.xml
+
+[ini-template]
+## Whether to rewrite the request URI
+# jetty.rewrite.rewriteRequestURI=true
+
+## Whether to rewrite the path info
+# jetty.rewrite.rewritePathInfo=false
+
+## Request attribute key under with the original path is stored
+# jetty.rewrite.originalPathAttribute=requestedPath
diff --git a/jetty-start/src/test/resources/dist-home/modules/server.mod b/jetty-start/src/test/resources/dist-home/modules/server.mod
index 3cdac35..6b5dbe9 100644
--- a/jetty-start/src/test/resources/dist-home/modules/server.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/server.mod
@@ -20,30 +20,53 @@
 etc/jetty.xml
 
 [ini-template]
-##
-## Server Threading Configuration
-##
-# minimum number of threads
-threads.min=10
-# maximum number of threads
-threads.max=200
-# thread idle timeout in milliseconds
-threads.timeout=60000
-# buffer size for output
-jetty.output.buffer.size=32768
-# request header buffer size
-jetty.request.header.size=8192
-# response header buffer size
-jetty.response.header.size=8192
-# should jetty send the server version header?
-jetty.send.server.version=true
-# should jetty send the date header?
-jetty.send.date.header=false
-# What host to listen on (leave commented to listen on all interfaces)
-#jetty.host=myhost.com
-# Dump the state of the Jetty server, components, and webapps after startup
-jetty.dump.start=false
-# Dump the state of the Jetty server, before stop
-jetty.dump.stop=false
+### ThreadPool configuration
+## Minimum number of threads
+# jetty.threadPool.minThreads=10
 
+## Maximum number of threads
+# jetty.threadPool.maxThreads=200
 
+## Thread idle timeout (in milliseconds)
+# jetty.threadPool.idleTimeout=60000
+
+### Common HTTP configuration
+## Scheme to use to build URIs for secure redirects
+# jetty.httpConfig.secureScheme=https
+
+## Port to use to build URIs for secure redirects
+# jetty.httpConfig.securePort=8443
+
+## Response content buffer size (in bytes)
+# jetty.httpConfig.outputBufferSize=32768
+
+## Max response content write length that is buffered (in bytes)
+# jetty.httpConfig.outputAggregationSize=8192
+
+## Max request headers size (in bytes)
+# jetty.httpConfig.requestHeaderSize=8192
+
+## Max response headers size (in bytes)
+# jetty.httpConfig.responseHeaderSize=8192
+
+## Whether to send the Server: header
+# jetty.httpConfig.sendServerVersion=true
+
+## Whether to send the Date: header
+# jetty.httpConfig.sendDateHeader=false
+
+## Max per-connection header cache size (in nodes)
+# jetty.httpConfig.headerCacheSize=512
+
+## Whether, for requests with content, delay dispatch until some content has arrived
+# jetty.httpConfig.delayDispatchUntilContent=true
+
+### Server configuration
+## Whether ctrl+c on the console gracefully stops the Jetty server
+# jetty.server.stopAtShutdown=true
+
+## Dump the state of the Jetty server, components, and webapps after startup
+# jetty.server.dumpAfterStart=false
+
+## Dump the state of the Jetty server, components, and webapps before shutdown
+# jetty.server.dumpBeforeStop=false
diff --git a/jetty-start/src/test/resources/dist-home/modules/servlets.mod b/jetty-start/src/test/resources/dist-home/modules/servlets.mod
index e8724b8..2e977c0 100644
--- a/jetty-start/src/test/resources/dist-home/modules/servlets.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/servlets.mod
@@ -2,6 +2,7 @@
 # Jetty Servlets Module
 #
 
+
 [depend]
 servlet
 
diff --git a/jetty-start/src/test/resources/dist-home/modules/setuid.mod b/jetty-start/src/test/resources/dist-home/modules/setuid.mod
index 64c9e23..41ef757 100644
--- a/jetty-start/src/test/resources/dist-home/modules/setuid.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/setuid.mod
@@ -6,14 +6,14 @@
 server
 
 [lib]
-lib/setuid/jetty-setuid-java-1.0.1.jar
+lib/setuid/jetty-setuid-java-1.0.3.jar
 
 [xml]
 etc/jetty-setuid.xml
 
 [ini-template]
 ## SetUID Configuration
-# jetty.startServerAsPrivileged=false
-# jetty.username=jetty
-# jetty.groupname=jetty
-# jetty.umask=002
+# jetty.setuid.startServerAsPrivileged=false
+# jetty.setuid.userName=jetty
+# jetty.setuid.groupName=jetty
+# jetty.setuid.umask=002
diff --git a/jetty-start/src/test/resources/dist-home/modules/spdy.mod b/jetty-start/src/test/resources/dist-home/modules/spdy.mod
deleted file mode 100644
index cf79dfa..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/spdy.mod
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# SPDY Support Module
-#
-
-[depend]
-ssl
-protonego
-
-[lib]
-lib/spdy/*.jar
-
-[xml]
-etc/jetty-ssl.xml
-etc/jetty-spdy.xml
-
-[ini-template]
-## SPDY Configuration
-
-# Port for SPDY connections
-spdy.port=8443
-
-# SPDY idle timeout in milliseconds
-spdy.timeout=30000
-
-# Initial Window Size for SPDY
-#spdy.initialWindowSize=65536
diff --git a/jetty-start/src/test/resources/dist-home/modules/spring.mod b/jetty-start/src/test/resources/dist-home/modules/spring.mod
index 444afb2..39b9b8d 100644
--- a/jetty-start/src/test/resources/dist-home/modules/spring.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/spring.mod
@@ -1,6 +1,7 @@
 #
 # Spring
 #
+
 [name]
 spring
 
diff --git a/jetty-start/src/test/resources/dist-home/modules/ssl.mod b/jetty-start/src/test/resources/dist-home/modules/ssl.mod
index a2fdfd1..5d829c3 100644
--- a/jetty-start/src/test/resources/dist-home/modules/ssl.mod
+++ b/jetty-start/src/test/resources/dist-home/modules/ssl.mod
@@ -2,39 +2,81 @@
 # SSL Keystore module
 #
 
+[name]
+ssl
+
 [depend]
 server
 
 [xml]
 etc/jetty-ssl.xml
+etc/jetty-ssl-context.xml
 
 [files]
 https://raw.githubusercontent.com/eclipse/jetty.project/master/jetty-server/src/test/config/etc/keystore?id=${jetty.tag.version}|etc/keystore
 
 [ini-template]
-### SSL Keystore Configuration
-# define the port to use for secure redirection
-jetty.secure.port=8443
+### TLS(SSL) Connector Configuration
 
-## Setup a demonstration keystore and truststore
-jetty.keystore=etc/keystore
-jetty.truststore=etc/keystore
+## Connector host/address to bind to
+# jetty.ssl.host=0.0.0.0
 
-## Set the demonstration passwords.
+## Connector port to listen on
+jetty.ssl.port=8443
+
+## Connector idle timeout in milliseconds
+# jetty.ssl.idleTimeout=30000
+
+## Connector socket linger time in seconds (-1 to disable)
+# jetty.ssl.soLingerTime=-1
+
+## Number of acceptors (-1 picks default based on number of cores)
+# jetty.ssl.acceptors=-1
+
+## Number of selectors (-1 picks default based on number of cores)
+# jetty.ssl.selectors=-1
+
+## ServerSocketChannel backlog (0 picks platform default)
+# jetty.ssl.acceptorQueueSize=0
+
+## Thread priority delta to give to acceptor threads
+# jetty.ssl.acceptorPriorityDelta=0
+
+## Whether request host names are checked to match any SNI names
+# jetty.ssl.sniHostCheck=true
+
+### SslContextFactory Configuration
 ## Note that OBF passwords are not secure, just protected from casual observation
 ## See http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html
-jetty.keystore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
-jetty.keymanager.password=OBF:1u2u1wml1z7s1z7a1wnl1u2g
-jetty.truststore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
 
-### Set the client auth behavior
-## Set to true if client certificate authentication is required
-# jetty.ssl.needClientAuth=true
-## Set to true if client certificate authentication is desired
-# jetty.ssl.wantClientAuth=true
+## Keystore file path (relative to $jetty.base)
+# jetty.sslContext.keyStorePath=etc/keystore
 
-## Parameters to control the number and priority of acceptors and selectors
-# ssl.selectors=1
-# ssl.acceptors=1
-# ssl.selectorPriorityDelta=0
-# ssl.acceptorPriorityDelta=0
+## Truststore file path (relative to $jetty.base)
+# jetty.sslContext.trustStorePath=etc/keystore
+
+## Keystore password
+# jetty.sslContext.keyStorePassword=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
+
+## Keystore type and provider
+# jetty.sslContext.keyStoreType=JKS
+# jetty.sslContext.keyStoreProvider=
+
+## KeyManager password
+# jetty.sslContext.keyManagerPassword=OBF:1u2u1wml1z7s1z7a1wnl1u2g
+
+## Truststore password
+# jetty.sslContext.trustStorePassword=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
+
+## Truststore type and provider
+# jetty.sslContext.trustStoreType=JKS
+# jetty.sslContext.trustStoreProvider=
+
+## whether client certificate authentication is required
+# jetty.sslContext.needClientAuth=false
+
+## Whether client certificate authentication is desired
+# jetty.sslContext.wantClientAuth=false
+
+## Whether cipher order is significant (since java 8 only)
+# jetty.sslContext.useCipherSuitesOrder=true
diff --git a/jetty-start/src/test/resources/dist-home/modules/xinetd.mod b/jetty-start/src/test/resources/dist-home/modules/xinetd.mod
deleted file mode 100644
index e53618e..0000000
--- a/jetty-start/src/test/resources/dist-home/modules/xinetd.mod
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# Xinetd module
-#
-
-[depend]
-server
-
-[xml]
-etc/jetty-xinetd.xml
-
-[ini-template]
-## Xinetd Configuration
-## See ${jetty.home}/etc/jetty-xinetd.xml for example service entry
-jetty.xinetd.idleTimeout=300000
-jetty.xinetd.acceptors=2
-jetty.xinetd.statsOn=false
-
diff --git a/jetty-start/src/test/resources/dist-home/start.ini b/jetty-start/src/test/resources/dist-home/start.ini
new file mode 100644
index 0000000..76987d9
--- /dev/null
+++ b/jetty-start/src/test/resources/dist-home/start.ini
@@ -0,0 +1,131 @@
+#===========================================================
+# Jetty Startup 
+#
+# Starting Jetty from this {jetty.home} is not recommended.
+#
+# A proper {jetty.base} directory should be configured, instead
+# of making changes to this {jetty.home} directory.
+#
+# See documentation about {jetty.base} at
+# http://www.eclipse.org/jetty/documentation/current/startup.html
+#
+# A demo-base directory has been provided as an example of
+# this sort of setup.
+#
+# $ cd demo-base
+# $ java -jar ../start.jar
+#
+#===========================================================
+
+# To disable the warning message, comment the following line
+--module=home-base-warning
+
+# --------------------------------------- 
+# Module: apache-jsp
+--module=apache-jsp
+
+# --------------------------------------- 
+# Module: apache-jstl
+--module=apache-jstl
+
+# --------------------------------------- 
+# Module: ext
+--module=ext
+
+# --------------------------------------- 
+# Module: resources
+--module=resources
+
+# --------------------------------------- 
+# Module: server
+--module=server
+##
+## Server Threading Configuration
+##
+# minimum number of threads
+jetty.threadPool.minThreads=10
+# maximum number of threads
+jetty.threadPool.maxThreads=200
+# thread idle timeout in milliseconds
+jetty.threadPool.idleTimeout=60000
+# buffer size for output
+jetty.httpConfig.outputBufferSize=32768
+# request header buffer size
+jetty.httpConfig.requestHeaderSize=8192
+# response header buffer size
+jetty.httpConfig.responseHeaderSize=8192
+# should jetty send the server version header?
+jetty.httpConfig.sendServerVersion=true
+# should jetty send the date header?
+jetty.httpConfig.sendDateHeader=false
+# What host to listen on (leave commented to listen on all interfaces)
+#jetty.http.host=myhost.com
+# Dump the state of the Jetty server, components, and webapps after startup
+jetty.server.dumpAfterStart=false
+# Dump the state of the Jetty server, before stop
+jetty.server.dumpBeforeStop=false
+# Enable delayed dispatch optimisation
+jetty.httpConfig.delayDispatchUntilContent=false
+
+# --------------------------------------- 
+# Module: http
+--module=http
+### HTTP Connector Configuration
+
+## HTTP port to listen on
+jetty.http.port=8080
+
+## HTTP idle timeout in milliseconds
+# jetty.http.idleTimeout=30000
+
+## HTTP Socket.soLingerTime in seconds. (-1 to disable)
+# jetty.http.soLingerTime=-1
+
+## Parameters to control the number and priority of acceptors and selectors
+# jetty.http.selectors=1
+# jetty.http.acceptors=1
+# jetty.http.acceptorPriorityDelta=0
+
+# --------------------------------------- 
+# Module: jndi
+--module=jndi
+
+# --------------------------------------- 
+# Module: security
+--module=security
+
+# --------------------------------------- 
+# Module: servlet
+--module=servlet
+
+# --------------------------------------- 
+# Module: webapp
+--module=webapp
+
+# --------------------------------------- 
+# Module: deploy
+--module=deploy
+## DeployManager configuration
+# Monitored Directory name (relative to jetty.base)
+# jetty.deploy.monitoredDir=webapps
+
+# --------------------------------------- 
+# Module: plus
+--module=plus
+
+# --------------------------------------- 
+# Module: annotations
+--module=annotations
+
+# --------------------------------------- 
+# Module: jsp
+--module=jsp
+
+# --------------------------------------- 
+# Module: websocket
+--module=websocket
+
+# --------------------------------------- 
+# Module: jstl
+--module=jstl
+
diff --git a/jetty-start/src/test/resources/jetty-version.properties b/jetty-start/src/test/resources/jetty-version.properties
new file mode 100644
index 0000000..3527756
--- /dev/null
+++ b/jetty-start/src/test/resources/jetty-version.properties
@@ -0,0 +1 @@
+jetty.version=9.3
diff --git a/jetty-start/src/test/resources/usecases/agent-properties.assert.txt b/jetty-start/src/test/resources/usecases/agent-properties.assert.txt
new file mode 100644
index 0000000..664fec8
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/agent-properties.assert.txt
@@ -0,0 +1,33 @@
+# The XMLs we expect (order is important)
+XML|${jetty.home}/etc/jetty.xml
+XML|${jetty.home}/etc/jetty-http.xml
+XML|${jetty.home}/etc/jetty-jmx.xml
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.home}/lib/jetty-http-9.3.jar
+LIB|${jetty.home}/lib/jetty-io-9.3.jar
+LIB|${jetty.home}/lib/jetty-jmx-9.3.jar
+LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.home}/lib/jetty-server-9.3.jar
+LIB|${jetty.home}/lib/jetty-util-9.3.jar
+LIB|${jetty.home}/lib/jetty-xml-9.3.jar
+LIB|${jetty.home}/lib/servlet-api-3.1.jar
+LIB|${jetty.base}/lib/agent-jdk-1.6.jar
+
+# The Properties we expect (order is irrelevant)
+# (this is the property we actually set in jetty.base)
+PROP|jetty.http.port=9090
+# PROP|java.vm.specification.version=1.6
+# (these are the ones set by default from jetty.home modules)
+# PROP|jetty.http.idleTimeout=30000
+# PROP|jetty.httpConfig.delayDispatchUntilContent=false
+# PROP|jetty.server.dumpAfterStart=false
+# PROP|jetty.server.dumpBeforeStop=false
+# PROP|jetty.httpConfig.outputBufferSize=32768
+# PROP|jetty.httpConfig.requestHeaderSize=8192
+# PROP|jetty.httpConfig.responseHeaderSize=8192
+# PROP|jetty.httpConfig.sendDateHeader=false
+# PROP|jetty.httpConfig.sendServerVersion=true
+# PROP|jetty.threadPool.maxThreads=200
+# PROP|jetty.threadPool.minThreads=10
+# PROP|jetty.threadPool.idleTimeout=60000
diff --git a/jetty-start/src/test/resources/usecases/base.props.agent/lib/agent-jdk-1.5.jar b/jetty-start/src/test/resources/usecases/agent-properties/lib/agent-jdk-1.5.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.props.agent/lib/agent-jdk-1.5.jar
rename to jetty-start/src/test/resources/usecases/agent-properties/lib/agent-jdk-1.5.jar
diff --git a/jetty-start/src/test/resources/usecases/base.props.agent/lib/agent-jdk-1.6.jar b/jetty-start/src/test/resources/usecases/agent-properties/lib/agent-jdk-1.6.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.props.agent/lib/agent-jdk-1.6.jar
rename to jetty-start/src/test/resources/usecases/agent-properties/lib/agent-jdk-1.6.jar
diff --git a/jetty-start/src/test/resources/usecases/base.props.agent/lib/agent-jdk-1.7.jar b/jetty-start/src/test/resources/usecases/agent-properties/lib/agent-jdk-1.7.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.props.agent/lib/agent-jdk-1.7.jar
rename to jetty-start/src/test/resources/usecases/agent-properties/lib/agent-jdk-1.7.jar
diff --git a/jetty-start/src/test/resources/usecases/base.props.agent/modules/agent.mod b/jetty-start/src/test/resources/usecases/agent-properties/modules/agent.mod
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.props.agent/modules/agent.mod
rename to jetty-start/src/test/resources/usecases/agent-properties/modules/agent.mod
diff --git a/jetty-start/src/test/resources/usecases/agent-properties/start.ini b/jetty-start/src/test/resources/usecases/agent-properties/start.ini
new file mode 100644
index 0000000..db061d8
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/agent-properties/start.ini
@@ -0,0 +1,4 @@
+
+--module=http,agent
+
+jetty.http.port=9090
diff --git a/jetty-start/src/test/resources/usecases/assert-barebones.txt b/jetty-start/src/test/resources/usecases/assert-barebones.txt
deleted file mode 100644
index 164f3f9..0000000
--- a/jetty-start/src/test/resources/usecases/assert-barebones.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/assert-enable-spdy-bad-npn-version.txt b/jetty-start/src/test/resources/usecases/assert-enable-spdy-bad-npn-version.txt
deleted file mode 100644
index 84c8867..0000000
--- a/jetty-start/src/test/resources/usecases/assert-enable-spdy-bad-npn-version.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-# The XMLs we expect (order is important)
-
-# The LIBs we expect (order is irrelevant)
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
-PROP|jetty.keystore=etc/keystore
-PROP|jetty.keystore.password=friendly
-PROP|jetty.keymanager.password=icecream
-PROP|jetty.truststore=etc/keystore
-PROP|jetty.truststore.password=sundae
-PROP|java.version=1.7.0_01
-
-# The Downloads
-
-# The Bootlib
diff --git a/jetty-start/src/test/resources/usecases/assert-enable-spdy.txt b/jetty-start/src/test/resources/usecases/assert-enable-spdy.txt
deleted file mode 100644
index a69eeb6..0000000
--- a/jetty-start/src/test/resources/usecases/assert-enable-spdy.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty-jmx.xml
-XML|${jetty.home}/etc/protonego-alpn.xml
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-XML|${jetty.home}/etc/jetty-ssl.xml
-XML|${jetty.home}/etc/jetty-spdy.xml
-
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-jmx-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-LIB|${jetty.home}/lib/spdy/spdy-client-TEST.jar
-LIB|${jetty.home}/lib/spdy/spdy-http-server-TEST.jar
-LIB|${jetty.home}/lib/spdy/spdy-http-common-TEST.jar
-LIB|${jetty.home}/lib/spdy/spdy-server-TEST.jar
-LIB|${jetty.home}/lib/spdy/spdy-core-TEST.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
-PROP|jetty.secure.port=8443
-PROP|jetty.keystore=etc/keystore
-PROP|jetty.keystore.password=friendly
-PROP|jetty.keymanager.password=icecream
-PROP|jetty.truststore=etc/keystore
-PROP|jetty.truststore.password=sundae
-PROP|java.version=1.7.0_60
-PROP|protonego=alpn
-PROP|spdy.port=8443
-PROP|spdy.timeout=30000
-
-# The Downloads
-DOWNLOAD|http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-DOWNLOAD|http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/plain/jetty-server/src/main/config/etc/keystore|etc/keystore
-
-# The Bootlib
-BOOTLIB|-Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-# The Files
-FILE|lib/
-FILE|lib/alpn/
-
diff --git a/jetty-start/src/test/resources/usecases/assert-include-jetty-dir-logging.txt b/jetty-start/src/test/resources/usecases/assert-include-jetty-dir-logging.txt
deleted file mode 100644
index 4ecd847..0000000
--- a/jetty-start/src/test/resources/usecases/assert-include-jetty-dir-logging.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty-jmx.xml
-XML|${maven-test-resources}/extra-jetty-dirs/logging/etc/jetty-logging.xml
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-jmx-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-LIB|${jetty.home}/resources
-LIB|${maven-test-resources}/extra-jetty-dirs/logging/lib/logging/logback.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
-
-# Files
-FILE|logs/
diff --git a/jetty-start/src/test/resources/usecases/assert-jmx.txt b/jetty-start/src/test/resources/usecases/assert-jmx.txt
deleted file mode 100644
index def27d7..0000000
--- a/jetty-start/src/test/resources/usecases/assert-jmx.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty-jmx.xml
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-jmx-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/assert-jsp-apache.txt b/jetty-start/src/test/resources/usecases/assert-jsp-apache.txt
deleted file mode 100644
index fe3d51c..0000000
--- a/jetty-start/src/test/resources/usecases/assert-jsp-apache.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-servlet-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-LIB|${jetty.home}/lib/apache-jsp/javax.servlet.jsp.javax.servlet.jsp-api-TEST.jar
-LIB|${jetty.home}/lib/apache-jsp/org.eclipse.jetty.apache-jsp-TEST.jar
-LIB|${jetty.home}/lib/apache-jsp/org.eclipse.jetty.orbit.org.eclipse.jdt.core-TEST.jar
-LIB|${jetty.home}/lib/apache-jsp/org.mortbay.jasper.apache-el-TEST.jar
-LIB|${jetty.home}/lib/apache-jsp/org.mortbay.jasper.apache-jsp-TEST.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
-PROP|jsp-impl=apache
-
-# Files / Directories to create
-# FILE|lib/
diff --git a/jetty-start/src/test/resources/usecases/assert-jsp-glassfish.txt b/jetty-start/src/test/resources/usecases/assert-jsp-glassfish.txt
deleted file mode 100644
index 9c17c56..0000000
--- a/jetty-start/src/test/resources/usecases/assert-jsp-glassfish.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-servlet-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-LIB|${jetty.home}/lib/jsp/javax.el-TEST.jar
-LIB|${jetty.home}/lib/jsp/javax.servlet.jsp.jstl-TEST.jar
-LIB|${jetty.home}/lib/jsp/javax.servlet.jsp-api-TEST.jar
-LIB|${jetty.home}/lib/jsp/javax.servlet.jsp-TEST.jar
-LIB|${jetty.home}/lib/jsp/jetty-jsp-jdt-TEST.jar
-LIB|${jetty.home}/lib/jsp/org.eclipse.jdt.core-TEST.jar
-LIB|${jetty.home}/lib/jsp/org.eclipse.jetty.orbit.javax.servlet.jsp.jstl-TEST.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
-PROP|jsp-impl=glassfish
-
-# Files / Directories to create
-# FILE|lib/
diff --git a/jetty-start/src/test/resources/usecases/assert-logging.txt b/jetty-start/src/test/resources/usecases/assert-logging.txt
deleted file mode 100644
index c83dc2c..0000000
--- a/jetty-start/src/test/resources/usecases/assert-logging.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-LIB|${jetty.base}/lib/logging/slf4j-api.jar
-LIB|${jetty.base}/lib/logging/jul-to-slf4j.jar
-LIB|${jetty.base}/lib/logging/logback-core.jar
-LIB|${jetty.base}/lib/logging/logback-classic.jar
-LIB|${jetty.base}/resources
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
-
-# Other File References
-FILE|logs/
-FILE|resources/
-
-# Downloads
-DOWNLOAD|http://central.maven.org/maven2/org/slf4j/slf4j-api/1.6.6/slf4j-api-1.6.6.jar|lib/logging/slf4j-api-1.6.6.jar
-DOWNLOAD|http://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.0.7/logback-core-1.0.7.jar|lib/logging/logback-core-1.0.7.jar
-DOWNLOAD|http://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.0.7/logback-classic-1.0.7.jar|lib/logging/logback-classic-1.0.7.jar
-DOWNLOAD|https://raw.githubusercontent.com/jetty-project/logging-modules/master/logback/logback.xml|resources/logback.xml
-DOWNLOAD|https://raw.githubusercontent.com/jetty-project/logging-modules/master/logback/jetty-logging.properties|resources/jetty-logging.properties
diff --git a/jetty-start/src/test/resources/usecases/assert-missing-npn-version.txt b/jetty-start/src/test/resources/usecases/assert-missing-npn-version.txt
deleted file mode 100644
index 884767a..0000000
--- a/jetty-start/src/test/resources/usecases/assert-missing-npn-version.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty-jmx.xml
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-jmx-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
-PROP|jetty.keystore=etc/keystore
-PROP|jetty.keystore.password=friendly
-PROP|jetty.keymanager.password=icecream
-PROP|jetty.truststore=etc/keystore
-PROP|jetty.truststore.password=sundae
-PROP|java.version=1.7.0_01
-
-# The Downloads
-
-# The Bootlib
diff --git a/jetty-start/src/test/resources/usecases/assert-props.agent.txt b/jetty-start/src/test/resources/usecases/assert-props.agent.txt
deleted file mode 100644
index 27108ab..0000000
--- a/jetty-start/src/test/resources/usecases/assert-props.agent.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty-jmx.xml
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-jmx-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-LIB|${jetty.base}/lib/agent-jdk-1.6.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
-PROP|java.vm.specification.version=1.6
diff --git a/jetty-start/src/test/resources/usecases/assert-props.basic.txt b/jetty-start/src/test/resources/usecases/assert-props.basic.txt
deleted file mode 100644
index e56177e..0000000
--- a/jetty-start/src/test/resources/usecases/assert-props.basic.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
-PROP|port=9090
diff --git a/jetty-start/src/test/resources/usecases/assert-with-db.txt b/jetty-start/src/test/resources/usecases/assert-with-db.txt
deleted file mode 100644
index 1651974..0000000
--- a/jetty-start/src/test/resources/usecases/assert-with-db.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-XML|${jetty.home}/etc/jetty-plus.xml
-XML|${jetty.home}/etc/jetty-deploy.xml
-XML|${jetty.base}/etc/jetty-db.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-LIB|${jetty.home}/lib/jetty-jndi-TEST.jar
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jndi/javax.transaction-api-1.2.jar
-LIB|${jetty.home}/lib/jetty-plus-TEST.jar
-LIB|${jetty.home}/lib/jetty-deploy-TEST.jar
-LIB|${jetty.home}/lib/jetty-security-TEST.jar
-LIB|${jetty.home}/lib/jndi/javax.activation-1.1.jar
-LIB|${jetty.home}/lib/jetty-webapp-TEST.jar
-LIB|${jetty.home}/lib/jetty-servlet-TEST.jar
-LIB|${jetty.base}/lib/db/mysql-driver.jar
-LIB|${jetty.base}/lib/db/bonecp.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
-PROP|mysql.user=frank
-PROP|mysql.pass=secret
\ No newline at end of file
diff --git a/jetty-start/src/test/resources/usecases/assert-with-module-persistence.txt b/jetty-start/src/test/resources/usecases/assert-with-module-persistence.txt
deleted file mode 100644
index 75fb89c..0000000
--- a/jetty-start/src/test/resources/usecases/assert-with-module-persistence.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-ssl.xml
-XML|${jetty.home}/etc/jetty-https.xml
-XML|${jetty.home}/etc/jetty-plus.xml
-XML|${jetty.home}/etc/jetty-annotations.xml
-XML|${jetty.home}/etc/jetty-websockets.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/annotations/javax.annotation-api-1.2.jar
-LIB|${jetty.home}/lib/annotations/org.objectweb.asm-TEST.jar
-LIB|${jetty.home}/lib/jetty-annotations-TEST.jar
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-jndi-TEST.jar
-LIB|${jetty.home}/lib/jetty-plus-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-security-TEST.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/jndi/javax.activation-1.1.jar
-LIB|${jetty.home}/lib/jndi/javax.transaction-api-1.2.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-LIB|${jetty.home}/lib/websocket/javax.websocket-api-1.0.jar
-LIB|${jetty.home}/lib/websocket/javax-websocket-client-impl-TEST.jar
-LIB|${jetty.home}/lib/websocket/javax-websocket-server-impl-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-api-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-client-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-common-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-server-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-servlet-TEST.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=12345
-PROP|jetty.keystore=etc/keystore
-PROP|jetty.keystore.password=friendly
-PROP|jetty.keymanager.password=icecream
-PROP|jetty.truststore=etc/keystore
-PROP|jetty.truststore.password=sundae
-
-# JVM Args
-# JVM|-Xms1024m
diff --git a/jetty-start/src/test/resources/usecases/assert-with.ext.txt b/jetty-start/src/test/resources/usecases/assert-with.ext.txt
deleted file mode 100644
index 7759a41..0000000
--- a/jetty-start/src/test/resources/usecases/assert-with.ext.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-# The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-
-# The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-LIB|${jetty.base}/lib/ext/agent.jar
-LIB|${jetty.base}/lib/ext/jdbc/mariadb-jdbc.jar
-LIB|${jetty.base}/lib/ext/logging/slf4j-api.jar
-LIB|${jetty.base}/lib/ext/logging/jul-to-slf4j.jar
-LIB|${jetty.base}/lib/ext/logging/logback-core.jar
-LIB|${jetty.base}/lib/ext/logging/logback-classic.jar
-
-# The Properties we expect (order is irrelevant)
-PROP|jetty.port=9090
-
-# Files / Directories to create
-FILE|lib/
-FILE|lib/ext/
diff --git a/jetty-start/src/test/resources/usecases/barebones.assert.txt b/jetty-start/src/test/resources/usecases/barebones.assert.txt
new file mode 100644
index 0000000..3314346
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/barebones.assert.txt
@@ -0,0 +1,29 @@
+# The XMLs we expect (order is important)
+XML|${jetty.home}/etc/jetty.xml
+XML|${jetty.home}/etc/jetty-http.xml
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.home}/lib/jetty-http-9.3.jar
+LIB|${jetty.home}/lib/jetty-io-9.3.jar
+LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.home}/lib/jetty-server-9.3.jar
+LIB|${jetty.home}/lib/jetty-util-9.3.jar
+LIB|${jetty.home}/lib/jetty-xml-9.3.jar
+LIB|${jetty.home}/lib/servlet-api-3.1.jar
+
+# The Properties we expect (order is irrelevant)
+# (this is the property we actually set in jetty.base)
+PROP|jetty.http.port=9090
+# (these are the ones set by default from jetty.home modules)
+# PROP|jetty.http.idleTimeout=30000
+# PROP|jetty.httpConfig.delayDispatchUntilContent=false
+# PROP|jetty.server.dumpAfterStart=false
+# PROP|jetty.server.dumpBeforeStop=false
+# PROP|jetty.httpConfig.outputBufferSize=32768
+# PROP|jetty.httpConfig.requestHeaderSize=8192
+# PROP|jetty.httpConfig.responseHeaderSize=8192
+# PROP|jetty.httpConfig.sendDateHeader=false
+# PROP|jetty.httpConfig.sendServerVersion=true
+# PROP|jetty.threadPool.maxThreads=200
+# PROP|jetty.threadPool.minThreads=10
+# PROP|jetty.threadPool.idleTimeout=60000
diff --git a/jetty-start/src/test/resources/usecases/barebones/start.ini b/jetty-start/src/test/resources/usecases/barebones/start.ini
new file mode 100644
index 0000000..a3d92b9
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/barebones/start.ini
@@ -0,0 +1,5 @@
+
+--module=server
+--module=http
+
+jetty.http.port=9090
diff --git a/jetty-start/src/test/resources/usecases/base.barebones/start.ini b/jetty-start/src/test/resources/usecases/base.barebones/start.ini
deleted file mode 100644
index 1c0e065..0000000
--- a/jetty-start/src/test/resources/usecases/base.barebones/start.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-
---module=server
---module=http
-
-jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/base.enable.spdy.bad.npn.version/start.ini b/jetty-start/src/test/resources/usecases/base.enable.spdy.bad.npn.version/start.ini
deleted file mode 100644
index 13fa508..0000000
--- a/jetty-start/src/test/resources/usecases/base.enable.spdy.bad.npn.version/start.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-
---module=server,http,jmx,spdy
-
-jetty.port=9090
-
-# Some SSL keystore configuration
-jetty.keystore=etc/keystore
-jetty.keystore.password=friendly
-jetty.keymanager.password=icecream
-jetty.truststore=etc/keystore
-jetty.truststore.password=sundae
-
diff --git a/jetty-start/src/test/resources/usecases/base.enable.spdy/start.ini b/jetty-start/src/test/resources/usecases/base.enable.spdy/start.ini
deleted file mode 100644
index 13fa508..0000000
--- a/jetty-start/src/test/resources/usecases/base.enable.spdy/start.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-
---module=server,http,jmx,spdy
-
-jetty.port=9090
-
-# Some SSL keystore configuration
-jetty.keystore=etc/keystore
-jetty.keystore.password=friendly
-jetty.keymanager.password=icecream
-jetty.truststore=etc/keystore
-jetty.truststore.password=sundae
-
diff --git a/jetty-start/src/test/resources/usecases/base.jmx/start.ini b/jetty-start/src/test/resources/usecases/base.jmx/start.ini
deleted file mode 100644
index d3950e6..0000000
--- a/jetty-start/src/test/resources/usecases/base.jmx/start.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-
---module=server,http,jmx
-
-jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/base.logging/start.ini b/jetty-start/src/test/resources/usecases/base.logging/start.ini
deleted file mode 100644
index e18ff3d..0000000
--- a/jetty-start/src/test/resources/usecases/base.logging/start.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-
---module=server
---module=http
---module=logging
---module=resources
-
-jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/base.missing.npn.version/start.ini b/jetty-start/src/test/resources/usecases/base.missing.npn.version/start.ini
deleted file mode 100644
index 2915961..0000000
--- a/jetty-start/src/test/resources/usecases/base.missing.npn.version/start.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-
---module=server,http,jmx
-
-jetty.port=9090
-
-# Some SSL keystore configuration
-jetty.keystore=etc/keystore
-jetty.keystore.password=friendly
-jetty.keymanager.password=icecream
-jetty.truststore=etc/keystore
-jetty.truststore.password=sundae
-
diff --git a/jetty-start/src/test/resources/usecases/base.props.agent/start.ini b/jetty-start/src/test/resources/usecases/base.props.agent/start.ini
deleted file mode 100644
index 198a07c..0000000
--- a/jetty-start/src/test/resources/usecases/base.props.agent/start.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-
---module=http,agent
-
-jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/base.props.basic/start.ini b/jetty-start/src/test/resources/usecases/base.props.basic/start.ini
deleted file mode 100644
index f80bdb2..0000000
--- a/jetty-start/src/test/resources/usecases/base.props.basic/start.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-
---module=server
---module=http
-
-jetty.port=${port}
diff --git a/jetty-start/src/test/resources/usecases/base.with.db/start.ini b/jetty-start/src/test/resources/usecases/base.with.db/start.ini
deleted file mode 100644
index d5513df..0000000
--- a/jetty-start/src/test/resources/usecases/base.with.db/start.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-
---module=http,db
-
-mysql.user=frank
-mysql.pass=secret
-
-jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/base.with.ext/start.ini b/jetty-start/src/test/resources/usecases/base.with.ext/start.ini
deleted file mode 100644
index c8fa930..0000000
--- a/jetty-start/src/test/resources/usecases/base.with.ext/start.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-
---module=server
---module=http
---module=ext
-
-jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/base.with.include.jetty.dirs/start.ini b/jetty-start/src/test/resources/usecases/base.with.include.jetty.dirs/start.ini
deleted file mode 100644
index 9e16be6..0000000
--- a/jetty-start/src/test/resources/usecases/base.with.include.jetty.dirs/start.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-
---include-jetty-dir=${start.basedir}/../../extra-jetty-dirs/logging
---module=server,http,jmx
-
-jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/base.with.jsp.apache/start.ini b/jetty-start/src/test/resources/usecases/base.with.jsp.apache/start.ini
deleted file mode 100644
index fcdea02..0000000
--- a/jetty-start/src/test/resources/usecases/base.with.jsp.apache/start.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-
---module=server
---module=http
---module=jsp
-jsp-impl=apache
-
-jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/base.with.jsp.bad/start.ini b/jetty-start/src/test/resources/usecases/base.with.jsp.bad/start.ini
deleted file mode 100644
index 96e495a..0000000
--- a/jetty-start/src/test/resources/usecases/base.with.jsp.bad/start.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-
---module=server
---module=http
---module=jsp
-jsp-impl=bogus
-
-jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/base.with.jsp.default/start.ini b/jetty-start/src/test/resources/usecases/base.with.jsp.default/start.ini
deleted file mode 100644
index bf58fa8..0000000
--- a/jetty-start/src/test/resources/usecases/base.with.jsp.default/start.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-
---module=server
---module=http
---module=jsp
-
-jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/base.with.jsp.glassfish/start.ini b/jetty-start/src/test/resources/usecases/base.with.jsp.glassfish/start.ini
deleted file mode 100644
index 7107ade..0000000
--- a/jetty-start/src/test/resources/usecases/base.with.jsp.glassfish/start.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-
---module=server
---module=http
---module=jsp
-jsp-impl=glassfish
-
-jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/basic-properties.assert.txt b/jetty-start/src/test/resources/usecases/basic-properties.assert.txt
new file mode 100644
index 0000000..ab45829
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/basic-properties.assert.txt
@@ -0,0 +1,30 @@
+# The XMLs we expect (order is important)
+XML|${jetty.home}/etc/jetty.xml
+XML|${jetty.home}/etc/jetty-http.xml
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.home}/lib/jetty-http-9.3.jar
+LIB|${jetty.home}/lib/jetty-io-9.3.jar
+LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.home}/lib/jetty-server-9.3.jar
+LIB|${jetty.home}/lib/jetty-util-9.3.jar
+LIB|${jetty.home}/lib/jetty-xml-9.3.jar
+LIB|${jetty.home}/lib/servlet-api-3.1.jar
+
+# The Properties we expect (order is irrelevant)
+# (this is the property we actually set in jetty.base)
+PROP|jetty.http.port=9090
+PROP|port=9090
+# (these are the ones set by default from jetty.home modules)
+# PROP|jetty.http.idleTimeout=30000
+# PROP|jetty.httpConfig.delayDispatchUntilContent=false
+# PROP|jetty.server.dumpAfterStart=false
+# PROP|jetty.server.dumpBeforeStop=false
+# PROP|jetty.httpConfig.outputBufferSize=32768
+# PROP|jetty.httpConfig.requestHeaderSize=8192
+# PROP|jetty.httpConfig.responseHeaderSize=8192
+# PROP|jetty.httpConfig.sendDateHeader=false
+# PROP|jetty.httpConfig.sendServerVersion=true
+# PROP|jetty.threadPool.maxThreads=200
+# PROP|jetty.threadPool.minThreads=10
+# PROP|jetty.threadPool.idleTimeout=60000
diff --git a/jetty-start/src/test/resources/usecases/basic-properties/start.ini b/jetty-start/src/test/resources/usecases/basic-properties/start.ini
new file mode 100644
index 0000000..eaef9da
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/basic-properties/start.ini
@@ -0,0 +1,5 @@
+
+--module=server
+--module=http
+
+jetty.http.port=${port}
diff --git a/jetty-start/src/test/resources/usecases/database.assert.txt b/jetty-start/src/test/resources/usecases/database.assert.txt
new file mode 100644
index 0000000..e8fd0ec
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/database.assert.txt
@@ -0,0 +1,47 @@
+# The XMLs we expect (order is important)
+XML|${jetty.home}/etc/jetty.xml
+XML|${jetty.home}/etc/jetty-http.xml
+XML|${jetty.home}/etc/jetty-deploy.xml
+XML|${jetty.home}/etc/jetty-plus.xml
+XML|${jetty.base}/etc/jetty-db.xml
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.home}/lib/jetty-http-9.3.jar
+LIB|${jetty.home}/lib/jetty-io-9.3.jar
+LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.home}/lib/jetty-server-9.3.jar
+LIB|${jetty.home}/lib/jetty-util-9.3.jar
+LIB|${jetty.home}/lib/jetty-xml-9.3.jar
+LIB|${jetty.home}/lib/servlet-api-3.1.jar
+LIB|${jetty.home}/lib/jetty-jndi-9.3.jar
+LIB|${jetty.home}/lib/jndi/javax.mail.glassfish-1.4.1.v201005082020.jar
+LIB|${jetty.home}/lib/jndi/javax.transaction-api-1.2.jar
+LIB|${jetty.home}/lib/jetty-plus-9.3.jar
+LIB|${jetty.home}/lib/jetty-deploy-9.3.jar
+LIB|${jetty.home}/lib/jetty-security-9.3.jar
+LIB|${jetty.home}/lib/jetty-webapp-9.3.jar
+LIB|${jetty.home}/lib/jetty-servlet-9.3.jar
+LIB|${jetty.base}/lib/db/mysql-driver.jar
+LIB|${jetty.base}/lib/db/bonecp.jar
+
+# The Properties we expect (order is irrelevant)
+# (this is the property we actually set in jetty.base)
+PROP|jetty.http.port=9090
+PROP|mysql.user=frank
+PROP|mysql.pass=secret
+# (these are the ones set by default from jetty.home modules)
+# PROP|jetty.http.idleTimeout=30000
+# PROP|jetty.httpConfig.delayDispatchUntilContent=false
+# PROP|jetty.server.dumpAfterStart=false
+# PROP|jetty.server.dumpBeforeStop=false
+# PROP|jetty.httpConfig.outputBufferSize=32768
+# PROP|jetty.httpConfig.requestHeaderSize=8192
+# PROP|jetty.httpConfig.responseHeaderSize=8192
+# PROP|jetty.httpConfig.sendDateHeader=false
+# PROP|jetty.httpConfig.sendServerVersion=true
+# PROP|jetty.threadPool.maxThreads=200
+# PROP|jetty.threadPool.minThreads=10
+# PROP|jetty.threadPool.idleTimeout=60000
+
+# Files / Directories to create
+FILE|webapps/
diff --git a/jetty-start/src/test/resources/usecases/base.with.db/etc/jetty-db.xml b/jetty-start/src/test/resources/usecases/database/etc/jetty-db.xml
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.with.db/etc/jetty-db.xml
rename to jetty-start/src/test/resources/usecases/database/etc/jetty-db.xml
diff --git a/jetty-start/src/test/resources/usecases/base.with.db/lib/db/bonecp.jar b/jetty-start/src/test/resources/usecases/database/lib/db/bonecp.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.with.db/lib/db/bonecp.jar
rename to jetty-start/src/test/resources/usecases/database/lib/db/bonecp.jar
diff --git a/jetty-start/src/test/resources/usecases/base.with.db/lib/db/mysql-driver.jar b/jetty-start/src/test/resources/usecases/database/lib/db/mysql-driver.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.with.db/lib/db/mysql-driver.jar
rename to jetty-start/src/test/resources/usecases/database/lib/db/mysql-driver.jar
diff --git a/jetty-start/src/test/resources/usecases/base.with.db/modules/db.mod b/jetty-start/src/test/resources/usecases/database/modules/db.mod
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.with.db/modules/db.mod
rename to jetty-start/src/test/resources/usecases/database/modules/db.mod
diff --git a/jetty-start/src/test/resources/usecases/database/start.ini b/jetty-start/src/test/resources/usecases/database/start.ini
new file mode 100644
index 0000000..a9a013b
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/database/start.ini
@@ -0,0 +1,7 @@
+
+--module=http,db
+
+mysql.user=frank
+mysql.pass=secret
+
+jetty.http.port=9090
diff --git a/jetty-start/src/test/resources/usecases/deep-ext.assert.txt b/jetty-start/src/test/resources/usecases/deep-ext.assert.txt
new file mode 100644
index 0000000..bb8684e
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/deep-ext.assert.txt
@@ -0,0 +1,39 @@
+# The XMLs we expect (order is important)
+XML|${jetty.home}/etc/jetty.xml
+XML|${jetty.home}/etc/jetty-http.xml
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.home}/lib/jetty-http-9.3.jar
+LIB|${jetty.home}/lib/jetty-io-9.3.jar
+LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.home}/lib/jetty-server-9.3.jar
+LIB|${jetty.home}/lib/jetty-util-9.3.jar
+LIB|${jetty.home}/lib/jetty-xml-9.3.jar
+LIB|${jetty.home}/lib/servlet-api-3.1.jar
+LIB|${jetty.base}/lib/ext/agent.jar
+LIB|${jetty.base}/lib/ext/jdbc/mariadb-jdbc.jar
+LIB|${jetty.base}/lib/ext/logging/slf4j-api.jar
+LIB|${jetty.base}/lib/ext/logging/jul-to-slf4j.jar
+LIB|${jetty.base}/lib/ext/logging/logback-core.jar
+LIB|${jetty.base}/lib/ext/logging/logback-classic.jar
+
+# The Properties we expect (order is irrelevant)
+# (this is the property we actually set in jetty.base)
+PROP|jetty.http.port=9090
+# (these are the ones set by default from jetty.home modules)
+# PROP|jetty.http.idleTimeout=30000
+# PROP|jetty.httpConfig.delayDispatchUntilContent=false
+# PROP|jetty.server.dumpAfterStart=false
+# PROP|jetty.server.dumpBeforeStop=false
+# PROP|jetty.httpConfig.outputBufferSize=32768
+# PROP|jetty.httpConfig.requestHeaderSize=8192
+# PROP|jetty.httpConfig.responseHeaderSize=8192
+# PROP|jetty.httpConfig.sendDateHeader=false
+# PROP|jetty.httpConfig.sendServerVersion=true
+# PROP|jetty.threadPool.maxThreads=200
+# PROP|jetty.threadPool.minThreads=10
+# PROP|jetty.threadPool.idleTimeout=60000
+
+# Files / Directories to create
+FILE|lib/
+FILE|lib/ext/
diff --git a/jetty-start/src/test/resources/usecases/base.with.ext/lib/ext/agent.jar b/jetty-start/src/test/resources/usecases/deep-ext/lib/ext/agent.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.with.ext/lib/ext/agent.jar
rename to jetty-start/src/test/resources/usecases/deep-ext/lib/ext/agent.jar
diff --git a/jetty-start/src/test/resources/usecases/base.with.ext/lib/ext/jdbc/mariadb-jdbc.jar b/jetty-start/src/test/resources/usecases/deep-ext/lib/ext/jdbc/mariadb-jdbc.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.with.ext/lib/ext/jdbc/mariadb-jdbc.jar
rename to jetty-start/src/test/resources/usecases/deep-ext/lib/ext/jdbc/mariadb-jdbc.jar
diff --git a/jetty-start/src/test/resources/usecases/base.with.ext/lib/ext/logging/jul-to-slf4j.jar b/jetty-start/src/test/resources/usecases/deep-ext/lib/ext/logging/jul-to-slf4j.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.with.ext/lib/ext/logging/jul-to-slf4j.jar
rename to jetty-start/src/test/resources/usecases/deep-ext/lib/ext/logging/jul-to-slf4j.jar
diff --git a/jetty-start/src/test/resources/usecases/base.with.ext/lib/ext/logging/logback-classic.jar b/jetty-start/src/test/resources/usecases/deep-ext/lib/ext/logging/logback-classic.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.with.ext/lib/ext/logging/logback-classic.jar
rename to jetty-start/src/test/resources/usecases/deep-ext/lib/ext/logging/logback-classic.jar
diff --git a/jetty-start/src/test/resources/usecases/base.with.ext/lib/ext/logging/logback-core.jar b/jetty-start/src/test/resources/usecases/deep-ext/lib/ext/logging/logback-core.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.with.ext/lib/ext/logging/logback-core.jar
rename to jetty-start/src/test/resources/usecases/deep-ext/lib/ext/logging/logback-core.jar
diff --git a/jetty-start/src/test/resources/usecases/base.with.ext/lib/ext/logging/slf4j-api.jar b/jetty-start/src/test/resources/usecases/deep-ext/lib/ext/logging/slf4j-api.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.with.ext/lib/ext/logging/slf4j-api.jar
rename to jetty-start/src/test/resources/usecases/deep-ext/lib/ext/logging/slf4j-api.jar
diff --git a/jetty-start/src/test/resources/usecases/base.with.ext/lib/jetty-util-alt.jar b/jetty-start/src/test/resources/usecases/deep-ext/lib/jetty-util-alt.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.with.ext/lib/jetty-util-alt.jar
rename to jetty-start/src/test/resources/usecases/deep-ext/lib/jetty-util-alt.jar
diff --git a/jetty-start/src/test/resources/usecases/deep-ext/start.ini b/jetty-start/src/test/resources/usecases/deep-ext/start.ini
new file mode 100644
index 0000000..2165a30
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/deep-ext/start.ini
@@ -0,0 +1,6 @@
+
+--module=server
+--module=http
+--module=ext
+
+jetty.http.port=9090
diff --git a/jetty-start/src/test/resources/usecases/home/etc/README.spnego b/jetty-start/src/test/resources/usecases/home/etc/README.spnego
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/README.spnego
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jdbcRealm.properties b/jetty-start/src/test/resources/usecases/home/etc/jdbcRealm.properties
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jdbcRealm.properties
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-annotations.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-annotations.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-annotations.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-contexts.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-contexts.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-contexts.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-debug.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-debug.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-debug.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-demo.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-demo.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-demo.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-deploy.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-deploy.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-deploy.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-http.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-http.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-http.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-https.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-https.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-https.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-ipaccess.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-ipaccess.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-ipaccess.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-jaas.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-jaas.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-jaas.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-jmx.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-jmx.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-jmx.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-logging.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-logging.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-logging.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-lowresources.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-lowresources.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-lowresources.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-monitor.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-monitor.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-monitor.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-plus.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-plus.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-plus.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-proxy.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-proxy.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-proxy.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-requestlog.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-requestlog.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-requestlog.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-rewrite.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-rewrite.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-rewrite.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-setuid.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-setuid.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-setuid.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-spdy-proxy.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-spdy-proxy.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-spdy-proxy.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-spdy.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-spdy.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-spdy.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-ssl.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-ssl.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-ssl.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-started.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-started.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-started.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-stats.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-stats.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-stats.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-testrealm.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-testrealm.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-testrealm.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-webapps.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-webapps.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-webapps.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-websockets.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-websockets.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-websockets.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty-xinetd.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty-xinetd.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty-xinetd.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty.conf b/jetty-start/src/test/resources/usecases/home/etc/jetty.conf
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty.conf
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/jetty.xml b/jetty-start/src/test/resources/usecases/home/etc/jetty.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/jetty.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/keystore b/jetty-start/src/test/resources/usecases/home/etc/keystore
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/keystore
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/krb5.ini b/jetty-start/src/test/resources/usecases/home/etc/krb5.ini
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/krb5.ini
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/protonego-alpn.xml b/jetty-start/src/test/resources/usecases/home/etc/protonego-alpn.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/protonego-alpn.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/protonego-npn.xml b/jetty-start/src/test/resources/usecases/home/etc/protonego-npn.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/protonego-npn.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/spnego.conf b/jetty-start/src/test/resources/usecases/home/etc/spnego.conf
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/spnego.conf
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/spnego.properties b/jetty-start/src/test/resources/usecases/home/etc/spnego.properties
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/spnego.properties
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/test-realm.xml b/jetty-start/src/test/resources/usecases/home/etc/test-realm.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/test-realm.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/etc/webdefault.xml b/jetty-start/src/test/resources/usecases/home/etc/webdefault.xml
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/etc/webdefault.xml
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/annotations/javax.annotation-api-1.2.jar b/jetty-start/src/test/resources/usecases/home/lib/annotations/javax.annotation-api-1.2.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/annotations/javax.annotation-api-1.2.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/annotations/org.objectweb.asm-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/annotations/org.objectweb.asm-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/annotations/org.objectweb.asm-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/javax.servlet.jsp.javax.servlet.jsp-api-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/javax.servlet.jsp.javax.servlet.jsp-api-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/javax.servlet.jsp.javax.servlet.jsp-api-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/org.eclipse.jetty.apache-jsp-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/org.eclipse.jetty.apache-jsp-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/org.eclipse.jetty.apache-jsp-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/org.eclipse.jetty.orbit.org.eclipse.jdt.core-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/org.eclipse.jetty.orbit.org.eclipse.jdt.core-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/org.eclipse.jetty.orbit.org.eclipse.jdt.core-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/org.mortbay.jasper.apache-el-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/org.mortbay.jasper.apache-el-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/org.mortbay.jasper.apache-el-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/org.mortbay.jasper.apache-jsp-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/org.mortbay.jasper.apache-jsp-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/apache-jsp/org.mortbay.jasper.apache-jsp-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/apache-jstl/org.apache.taglibs.taglibs-standard-impl-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/apache-jstl/org.apache.taglibs.taglibs-standard-impl-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/apache-jstl/org.apache.taglibs.taglibs-standard-impl-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/apache-jstl/org.apache.taglibs.taglibs-standard-spec-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/apache-jstl/org.apache.taglibs.taglibs-standard-spec-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/apache-jstl/org.apache.taglibs.taglibs-standard-spec-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/ext/.nodelete b/jetty-start/src/test/resources/usecases/home/lib/ext/.nodelete
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/ext/.nodelete
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-annotations-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-annotations-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-annotations-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-client-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-client-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-client-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-continuation-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-continuation-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-continuation-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-deploy-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-deploy-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-deploy-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-http-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-http-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-http-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-io-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-io-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-io-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-jaas-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-jaas-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-jaas-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-jmx-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-jmx-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-jmx-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-jndi-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-jndi-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-jndi-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-jsp-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-jsp-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-jsp-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-plus-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-plus-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-plus-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-proxy-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-proxy-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-proxy-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-rewrite-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-rewrite-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-rewrite-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-schemas-3.1.RC0.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-schemas-3.1.RC0.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-schemas-3.1.RC0.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-schemas-3.1.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-schemas-3.1.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-schemas-3.1.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-security-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-security-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-security-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-server-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-server-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-server-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-servlet-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-servlet-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-servlet-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-servlets-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-servlets-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-servlets-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-util-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-util-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-util-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-webapp-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-webapp-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-webapp-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jetty-xml-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jetty-xml-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jetty-xml-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jndi/javax.activation-1.1.jar b/jetty-start/src/test/resources/usecases/home/lib/jndi/javax.activation-1.1.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jndi/javax.activation-1.1.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jndi/javax.transaction-api-1.2.jar b/jetty-start/src/test/resources/usecases/home/lib/jndi/javax.transaction-api-1.2.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jndi/javax.transaction-api-1.2.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jsp/javax.el-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jsp/javax.el-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jsp/javax.el-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jsp/javax.servlet.jsp-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jsp/javax.servlet.jsp-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jsp/javax.servlet.jsp-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jsp/javax.servlet.jsp-api-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jsp/javax.servlet.jsp-api-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jsp/javax.servlet.jsp-api-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jsp/javax.servlet.jsp.jstl-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jsp/javax.servlet.jsp.jstl-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jsp/javax.servlet.jsp.jstl-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jsp/jetty-jsp-jdt-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jsp/jetty-jsp-jdt-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jsp/jetty-jsp-jdt-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jsp/org.eclipse.jdt.core-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jsp/org.eclipse.jdt.core-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jsp/org.eclipse.jdt.core-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/jsp/org.eclipse.jetty.orbit.javax.servlet.jsp.jstl-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/jsp/org.eclipse.jetty.orbit.javax.servlet.jsp.jstl-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/jsp/org.eclipse.jetty.orbit.javax.servlet.jsp.jstl-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/monitor/jetty-monitor-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/monitor/jetty-monitor-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/monitor/jetty-monitor-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/servlet-api-3.1.jar b/jetty-start/src/test/resources/usecases/home/lib/servlet-api-3.1.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/servlet-api-3.1.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/setuid/jetty-setuid-java-1.0.1.jar b/jetty-start/src/test/resources/usecases/home/lib/setuid/jetty-setuid-java-1.0.1.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/setuid/jetty-setuid-java-1.0.1.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/setuid/libsetuid-linux.so b/jetty-start/src/test/resources/usecases/home/lib/setuid/libsetuid-linux.so
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/setuid/libsetuid-linux.so
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/setuid/libsetuid-osx.so b/jetty-start/src/test/resources/usecases/home/lib/setuid/libsetuid-osx.so
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/setuid/libsetuid-osx.so
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-client-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-client-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-client-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-core-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-core-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-core-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-http-common-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-http-common-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-http-common-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-http-server-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-http-server-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-http-server-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-server-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-server-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/spdy/spdy-server-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/websocket/javax-websocket-client-impl-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/websocket/javax-websocket-client-impl-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/websocket/javax-websocket-client-impl-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/websocket/javax-websocket-server-impl-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/websocket/javax-websocket-server-impl-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/websocket/javax-websocket-server-impl-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/websocket/javax.websocket-api-1.0.jar b/jetty-start/src/test/resources/usecases/home/lib/websocket/javax.websocket-api-1.0.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/websocket/javax.websocket-api-1.0.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-api-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-api-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-api-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-client-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-client-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-client-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-common-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-common-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-common-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-server-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-server-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-server-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-servlet-TEST.jar b/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-servlet-TEST.jar
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/lib/websocket/websocket-servlet-TEST.jar
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/modules/annotations.mod b/jetty-start/src/test/resources/usecases/home/modules/annotations.mod
deleted file mode 100644
index 65e4654..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/annotations.mod
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# Jetty Annotation Scanning Module
-#
-
-[depend]
-# Annotations needs plus, and jndi features
-plus
-
-[lib]
-# Annotations needs jetty annotation jars
-lib/jetty-annotations-${jetty.version}.jar
-# Need annotation processing jars too
-lib/annotations/*.jar
-
-[xml]
-# Enable annotation scanning webapp configurations
-etc/jetty-annotations.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/base.mod b/jetty-start/src/test/resources/usecases/home/modules/base.mod
deleted file mode 100644
index ad8ea32..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/base.mod
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# Base Module
-#
-
-[optional]
-# JMX is optional, if it appears in the module tree then depend on it
-jmx
-
-[lib]
-lib/jetty-util-${jetty.version}.jar
-lib/jetty-io-${jetty.version}.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/client.mod b/jetty-start/src/test/resources/usecases/home/modules/client.mod
deleted file mode 100644
index 6788eac..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/client.mod
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-# Client Feature
-#
-
-[lib]
-# Client jars
-lib/jetty-client-${jetty.version}.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/debug.mod b/jetty-start/src/test/resources/usecases/home/modules/debug.mod
deleted file mode 100644
index f740ea2..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/debug.mod
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Debug module
-#
-
-[depend]
-server
-
-[xml]
-etc/jetty-debug.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/deploy.mod b/jetty-start/src/test/resources/usecases/home/modules/deploy.mod
deleted file mode 100644
index 94c0e40..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/deploy.mod
+++ /dev/null
@@ -1,14 +0,0 @@
-#
-# Deploy Feature
-#
-
-[depend]
-webapp
-
-[lib]
-# Deploy jars
-lib/jetty-deploy-${jetty.version}.jar
-
-[xml]
-# Deploy configuration
-etc/jetty-deploy.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/ext.mod b/jetty-start/src/test/resources/usecases/home/modules/ext.mod
deleted file mode 100644
index 66c0519..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/ext.mod
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# ext module
-#
-
-[lib]
-lib/ext/**.jar
-
-[files]
-lib/
-lib/ext/
diff --git a/jetty-start/src/test/resources/usecases/home/modules/http.mod b/jetty-start/src/test/resources/usecases/home/modules/http.mod
deleted file mode 100644
index 8515414..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/http.mod
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Jetty HTTP Server
-#
-
-[depend]
-server
-
-[xml]
-etc/jetty-http.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/https.mod b/jetty-start/src/test/resources/usecases/home/modules/https.mod
deleted file mode 100644
index 281c5db..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/https.mod
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# Jetty HTTP Server
-#
-
-[depend]
-server
-
-[xml]
-etc/jetty-ssl.xml
-etc/jetty-https.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/ipaccess.mod b/jetty-start/src/test/resources/usecases/home/modules/ipaccess.mod
deleted file mode 100644
index 956ea0f..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/ipaccess.mod
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# IPAccess module
-#
-
-[depend]
-server
-
-[xml]
-etc/jetty-ipaccess.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/jaas.mod b/jetty-start/src/test/resources/usecases/home/modules/jaas.mod
deleted file mode 100644
index 9fb04f7..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/jaas.mod
+++ /dev/null
@@ -1,14 +0,0 @@
-#
-# JAAS Feature
-#
-
-[depend]
-server
-
-[lib]
-# JAAS jars
-lib/jetty-jaas-${jetty.version}.jar
-
-[xml]
-# JAAS configuration
-etc/jetty-jaas.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/jmx.mod b/jetty-start/src/test/resources/usecases/home/modules/jmx.mod
deleted file mode 100644
index fd8740a..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/jmx.mod
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# JMX Feature
-#
-
-[lib]
-# JMX jars (as defined in start.config)
-lib/jetty-jmx-${jetty.version}.jar
-
-[xml]
-# JMX configuration
-etc/jetty-jmx.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/jndi.mod b/jetty-start/src/test/resources/usecases/home/modules/jndi.mod
deleted file mode 100644
index 33c077c..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/jndi.mod
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# JNDI Support
-#
-
-[depend]
-server
-
-[lib]
-lib/jetty-jndi-${jetty.version}.jar
-lib/jndi/*.jar
-
diff --git a/jetty-start/src/test/resources/usecases/home/modules/jsp-impl/apache-jsp.mod b/jetty-start/src/test/resources/usecases/home/modules/jsp-impl/apache-jsp.mod
deleted file mode 100644
index aed547c..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/jsp-impl/apache-jsp.mod
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# Apache JSP Module
-#
-
-[name]
-jsp-impl
-
-[lib]
-lib/apache-jsp/*.jar
-
diff --git a/jetty-start/src/test/resources/usecases/home/modules/jsp-impl/glassfish-jsp.mod b/jetty-start/src/test/resources/usecases/home/modules/jsp-impl/glassfish-jsp.mod
deleted file mode 100644
index 130d2b3..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/jsp-impl/glassfish-jsp.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Glassfish JSP Module
-#
-[name]
-jsp-impl
-
-[lib]
-lib/jsp/*.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/jsp.mod b/jetty-start/src/test/resources/usecases/home/modules/jsp.mod
deleted file mode 100644
index fa5b9fd..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/jsp.mod
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Jetty JSP Module
-#
-
-[depend]
-servlet
-jsp-impl/${jsp-impl}-jsp
-
-[ini-template]
-# JSP Configuration
-
-# Select JSP implementation, choices are
-#   glassfish : The reference implementation 
-#               default in jetty <= 9.1
-#   apache    : The apache version 
-#               default jetty >= 9.2
-jsp-impl=apache
-
-# To use a non-jdk compiler for JSP compilation when using glassfish uncomment next line
-# -Dorg.apache.jasper.compiler.disablejsr199=true
diff --git a/jetty-start/src/test/resources/usecases/home/modules/logging.mod b/jetty-start/src/test/resources/usecases/home/modules/logging.mod
deleted file mode 100644
index a39bfe4..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/logging.mod
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# Jetty std err/out logging
-#
-
-[xml]
-etc/jetty-logging.xml
-
-[files]
-logs/
-
-[lib]
-lib/logging/**.jar
-resources/
-
-[ini-template]
-## Logging Configuration
-# Configure jetty logging for default internal behavior STDERR output
-# -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
-
-# Configure jetty logging for slf4j
-# -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog
-
-# Configure jetty logging for java.util.logging
-# -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.JavaUtilLog
-
-# STDERR / STDOUT Logging
-# Number of days to retain logs
-# jetty.log.retain=90
-# Directory for logging output
-# Either a path relative to ${jetty.base} or an absolute path
-# jetty.logs=logs
diff --git a/jetty-start/src/test/resources/usecases/home/modules/lowresources.mod b/jetty-start/src/test/resources/usecases/home/modules/lowresources.mod
deleted file mode 100644
index 4ca96de..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/lowresources.mod
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Low Resources module
-#
-
-[depend]
-server
-
-[xml]
-etc/jetty-lowresources.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/monitor.mod b/jetty-start/src/test/resources/usecases/home/modules/monitor.mod
deleted file mode 100644
index 67f006d..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/monitor.mod
+++ /dev/null
@@ -1,13 +0,0 @@
-#
-# Jetty Monitor module
-#
-
-[depend]
-server
-client
-
-[lib]
-lib/jetty-monitor-${jetty.version}.jar
-
-[xml]
-etc/jetty-monitor.xml
\ No newline at end of file
diff --git a/jetty-start/src/test/resources/usecases/home/modules/plus.mod b/jetty-start/src/test/resources/usecases/home/modules/plus.mod
deleted file mode 100644
index b781f00..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/plus.mod
+++ /dev/null
@@ -1,15 +0,0 @@
-#
-# Jetty Proxy module
-#
-
-[depend]
-server
-security
-jndi
-
-[lib]
-lib/jetty-plus-${jetty.version}.jar
-
-[xml]
-# Plus requires configuration
-etc/jetty-plus.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_40.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_40.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_40.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_45.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_45.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_45.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_51.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_51.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_51.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_55.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_55.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_55.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_60.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_60.mod
deleted file mode 100644
index 54d3731..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.7.0_60.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.8.0.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.8.0.mod
deleted file mode 100644
index a81732c..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.8.0.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.8.0_05.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.8.0_05.mod
deleted file mode 100644
index a81732c..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn-1.8.0_05.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar
-
-[exec]
--Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn.mod
deleted file mode 100644
index 0e399f0..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/alpn.mod
+++ /dev/null
@@ -1,36 +0,0 @@
-# ALPN is provided via a -Xbootclasspath that modifies the secure connections
-# in java to support the ALPN layer needed for SPDY (and eventually HTTP/2)
-#
-# This modification has a tight dependency on specific recent updates of
-# Java 1.7 and Java 1.8
-# (Java versions prior to 1.7u40 are not supported)
-#
-# The alpn protonego module will use an appropriate alpn-boot jar for your
-# specific version of Java.
-#
-# IMPORTANT: Versions of Java that exist after this module was created are
-#            not guaranteed to work with existing alpn-boot jars, and might
-#            need a new alpn-boot to be created / tested / deployed by the
-#            Jetty project in order to provide support for these future
-#            Java versions.
-#
-# All versions of alpn-boot can be found at
-# http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/
-
-[name]
-protonego-impl
-
-[depend]
-protonego-impl/alpn-${java.version}
-
-[lib]
-lib/jetty-alpn-client-${jetty.version}.jar
-lib/jetty-alpn-server-${jetty.version}.jar
-
-[xml]
-etc/protonego-alpn.xml
-
-[files]
-lib/
-lib/alpn/
-
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_04.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_04.mod
deleted file mode 100644
index 007570b..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_04.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.0.v20120525/npn-boot-1.1.0.v20120525.jar|lib/npn/npn-boot-1.1.0.v20120525.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.0.v20120525.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_05.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_05.mod
deleted file mode 100644
index 007570b..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_05.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.0.v20120525/npn-boot-1.1.0.v20120525.jar|lib/npn/npn-boot-1.1.0.v20120525.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.0.v20120525.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_06.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_06.mod
deleted file mode 100644
index 868a7a7..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_06.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.1.v20121030/npn-boot-1.1.1.v20121030.jar|lib/npn/npn-boot-1.1.1.v20121030.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.1.v20121030.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_07.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_07.mod
deleted file mode 100644
index 868a7a7..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_07.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.1.v20121030/npn-boot-1.1.1.v20121030.jar|lib/npn/npn-boot-1.1.1.v20121030.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.1.v20121030.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_09.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_09.mod
deleted file mode 100644
index 20c1db2..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_09.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar|lib/npn/npn-boot-1.1.3.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.3.v20130313.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_10.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_10.mod
deleted file mode 100644
index 20c1db2..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_10.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar|lib/npn/npn-boot-1.1.3.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.3.v20130313.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_11.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_11.mod
deleted file mode 100644
index 20c1db2..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_11.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar|lib/npn/npn-boot-1.1.3.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.3.v20130313.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_13.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_13.mod
deleted file mode 100644
index 1645a52..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_13.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.4.v20130313/npn-boot-1.1.4.v20130313.jar|lib/npn/npn-boot-1.1.4.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.4.v20130313.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_15.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_15.mod
deleted file mode 100644
index 73bc090..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_15.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar|lib/npn/npn-boot-1.1.5.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_17.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_17.mod
deleted file mode 100644
index 73bc090..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_17.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar|lib/npn/npn-boot-1.1.5.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_21.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_21.mod
deleted file mode 100644
index 73bc090..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_21.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar|lib/npn/npn-boot-1.1.5.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_25.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_25.mod
deleted file mode 100644
index 73bc090..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_25.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar|lib/npn/npn-boot-1.1.5.v20130313.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_40.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_40.mod
deleted file mode 100644
index 465e6f0..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_40.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar|lib/npn/npn-boot-1.1.6.v20130911.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_45.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_45.mod
deleted file mode 100644
index 465e6f0..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_45.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar|lib/npn/npn-boot-1.1.6.v20130911.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_51.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_51.mod
deleted file mode 100644
index 465e6f0..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_51.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar|lib/npn/npn-boot-1.1.6.v20130911.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_55.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_55.mod
deleted file mode 100644
index 5f8704d..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_55.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.8.v20141013/npn-boot-1.1.8.v20141013.jar|lib/npn/npn-boot-1.1.8.v20141013.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.8.v20141013.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_60.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_60.mod
deleted file mode 100644
index 5f8704d..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn-1.7.0_60.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-[name]
-protonego-boot
-
-[files]
-http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.8.v20141013/npn-boot-1.1.8.v20141013.jar|lib/npn/npn-boot-1.1.8.v20141013.jar
-
-[exec]
--Xbootclasspath/p:lib/npn/npn-boot-1.1.8.v20141013.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn.mod
deleted file mode 100644
index 040aad1..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego-impl/npn.mod
+++ /dev/null
@@ -1,31 +0,0 @@
-# NPN is provided via a -Xbootclasspath that modifies the secure connections
-# in java to support the NPN layer needed for SPDY.
-#
-# This modification has a tight dependency on specific updates of Java 1.7.
-# (No support for Java 8 exists for npn / npn-boot, use alpn instead)
-#
-# The npn module will use an appropriate npn-boot jar for your specific
-# version of Java.
-#
-# IMPORTANT: Versions of Java that exist after this module was created are
-#            not guaranteed to work with existing npn-boot jars, and might
-#            need a new npn-boot to be created / tested / deployed by the
-#            Jetty project in order to provide support for these future
-#            Java versions.
-#
-# All versions of npn-boot can be found at
-# http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/
-
-
-[name]
-protonego-impl
-
-[depend]
-protonego-impl/npn-${java.version}
-
-[xml]
-etc/protonego-npn.xml
-
-[files]
-lib/
-lib/npn/
diff --git a/jetty-start/src/test/resources/usecases/home/modules/protonego.mod b/jetty-start/src/test/resources/usecases/home/modules/protonego.mod
deleted file mode 100644
index d7bba9f..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/protonego.mod
+++ /dev/null
@@ -1,15 +0,0 @@
-#
-# Protocol Negotiatin Selection Module
-#
-
-[depend]
-protonego-impl/${protonego}
-
-[ini-template]
-# Protocol Negotiation Implementation Selection
-#  choices are:
-#    'npn'  : original implementation for SPDY (now deprecated)
-#    'alpn' : replacement for NPN, in use by current SPDY implementations
-#             and the future HTTP/2 spec
-#  Note: java 1.8+ are ALPN only.
-protonego=alpn
diff --git a/jetty-start/src/test/resources/usecases/home/modules/proxy.mod b/jetty-start/src/test/resources/usecases/home/modules/proxy.mod
deleted file mode 100644
index 7873329..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/proxy.mod
+++ /dev/null
@@ -1,14 +0,0 @@
-#
-# Jetty Proxy module
-#
-
-[depend]
-server
-client
-
-[lib]
-lib/jetty-proxy-${jetty.version}.jar
-
-[xml]
-# Proxy requires configuration
-etc/jetty-proxy.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/requestlog.mod b/jetty-start/src/test/resources/usecases/home/modules/requestlog.mod
deleted file mode 100644
index 2b048db..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/requestlog.mod
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Request Log module
-#
-
-[depend]
-server
-
-[xml]
-etc/jetty-requestlog.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/resources.mod b/jetty-start/src/test/resources/usecases/home/modules/resources.mod
deleted file mode 100644
index 8647d81..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/resources.mod
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# Module to add resources directory to classpath
-#
-
-[lib]
-resources/
-
-[files]
-resources/
-
diff --git a/jetty-start/src/test/resources/usecases/home/modules/rewrite.mod b/jetty-start/src/test/resources/usecases/home/modules/rewrite.mod
deleted file mode 100644
index 85fe5f0..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/rewrite.mod
+++ /dev/null
@@ -1,13 +0,0 @@
-#
-# Jetty Rewrite module
-#
-
-[depend]
-server
-
-[lib]
-lib/jetty-rewrite-${jetty.version}.jar
-
-[xml]
-# Annotations needs annotations configuration
-etc/jetty-rewrite.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/security.mod b/jetty-start/src/test/resources/usecases/home/modules/security.mod
deleted file mode 100644
index ba31632..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/security.mod
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Jetty Security Module
-#
-
-[depend]
-server
-
-[lib]
-lib/jetty-security-${jetty.version}.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/server.mod b/jetty-start/src/test/resources/usecases/home/modules/server.mod
deleted file mode 100644
index b1e9f33..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/server.mod
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# Base server
-#
-
-[optional]
-ext
-
-[depend]
-base
-xml
-
-[lib]
-lib/servlet-api-3.1.jar
-lib/jetty-schemas-3.1.jar
-lib/jetty-http-${jetty.version}.jar
-lib/jetty-continuation-${jetty.version}.jar
-lib/jetty-server-${jetty.version}.jar
-
-[xml]
-# Annotations needs annotations configuration
-etc/jetty.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/servlet.mod b/jetty-start/src/test/resources/usecases/home/modules/servlet.mod
deleted file mode 100644
index fdb65c5..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/servlet.mod
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Jetty Servlet Module
-#
-
-[depend]
-server
-
-[lib]
-lib/jetty-servlet-${jetty.version}.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/spdy.mod b/jetty-start/src/test/resources/usecases/home/modules/spdy.mod
deleted file mode 100644
index cf79dfa..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/spdy.mod
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# SPDY Support Module
-#
-
-[depend]
-ssl
-protonego
-
-[lib]
-lib/spdy/*.jar
-
-[xml]
-etc/jetty-ssl.xml
-etc/jetty-spdy.xml
-
-[ini-template]
-## SPDY Configuration
-
-# Port for SPDY connections
-spdy.port=8443
-
-# SPDY idle timeout in milliseconds
-spdy.timeout=30000
-
-# Initial Window Size for SPDY
-#spdy.initialWindowSize=65536
diff --git a/jetty-start/src/test/resources/usecases/home/modules/ssl.mod b/jetty-start/src/test/resources/usecases/home/modules/ssl.mod
deleted file mode 100644
index 449f581..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/ssl.mod
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# SSL Keystore module
-#
-
-[depend]
-server
-
-[xml]
-etc/jetty-ssl.xml
-
-[files]
-http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/plain/jetty-server/src/main/config/etc/keystore|etc/keystore
-
-[ini-template]
-## SSL Keystore Configuration
-# define the port to use for secure redirection
-jetty.secure.port=8443
-
-# Setup a demonstration keystore and truststore
-jetty.keystore=etc/keystore
-jetty.truststore=etc/keystore
-
-# Set the demonstration passwords.
-# Note that OBF passwords are not secure, just protected from casual observation
-# See http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html
-jetty.keystore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
-jetty.keymanager.password=OBF:1u2u1wml1z7s1z7a1wnl1u2g
-jetty.truststore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
-
-# Set the client auth behavior
-# Set to true if client certificate authentication is required
-# jetty.ssl.needClientAuth=true
-# Set to true if client certificate authentication is desired
-# jetty.ssl.wantClientAuth=true
-
diff --git a/jetty-start/src/test/resources/usecases/home/modules/stats.mod b/jetty-start/src/test/resources/usecases/home/modules/stats.mod
deleted file mode 100644
index 0922469..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/stats.mod
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Stats module
-#
-
-[depend]
-server
-
-[xml]
-etc/jetty-stats.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/webapp.mod b/jetty-start/src/test/resources/usecases/home/modules/webapp.mod
deleted file mode 100644
index f62c554..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/webapp.mod
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Base server
-#
-
-[depend]
-servlet
-
-[lib]
-lib/jetty-webapp-${jetty.version}.jar
diff --git a/jetty-start/src/test/resources/usecases/home/modules/websocket.mod b/jetty-start/src/test/resources/usecases/home/modules/websocket.mod
deleted file mode 100644
index f45babd..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/websocket.mod
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# WebSocket Feature
-#
-
-# WebSocket needs Annotations feature
-[depend]
-server
-annotations
-
-# WebSocket needs websocket jars (as defined in start.config)
-[lib]
-lib/websocket/*.jar
-
-# WebSocket needs websocket configuration
-[xml]
-etc/jetty-websockets.xml
-
diff --git a/jetty-start/src/test/resources/usecases/home/modules/xinetd.mod b/jetty-start/src/test/resources/usecases/home/modules/xinetd.mod
deleted file mode 100644
index fdc1b3c..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/xinetd.mod
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Stats module
-#
-
-[depend]
-server
-
-[xml]
-etc/jetty-xinetd.xml
diff --git a/jetty-start/src/test/resources/usecases/home/modules/xml.mod b/jetty-start/src/test/resources/usecases/home/modules/xml.mod
deleted file mode 100644
index d53107a..0000000
--- a/jetty-start/src/test/resources/usecases/home/modules/xml.mod
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# Jetty XML Configuration
-#
-
-[depend]
-base
-
-[lib]
-lib/jetty-xml-${jetty.version}.jar
-
diff --git a/jetty-start/src/test/resources/usecases/home/resources/.nodelete b/jetty-start/src/test/resources/usecases/home/resources/.nodelete
deleted file mode 100644
index e69de29..0000000
--- a/jetty-start/src/test/resources/usecases/home/resources/.nodelete
+++ /dev/null
diff --git a/jetty-start/src/test/resources/usecases/home/start.ini b/jetty-start/src/test/resources/usecases/home/start.ini
deleted file mode 100644
index a65a9cb..0000000
--- a/jetty-start/src/test/resources/usecases/home/start.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-
---module=server,http,jmx,annotations,websocket
diff --git a/jetty-start/src/test/resources/usecases/http2.assert.txt b/jetty-start/src/test/resources/usecases/http2.assert.txt
new file mode 100644
index 0000000..7c21f5a
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/http2.assert.txt
@@ -0,0 +1,44 @@
+# The XMLs we expect (order is important)
+XML|${jetty.home}/etc/jetty.xml
+XML|${jetty.home}/etc/jetty-http.xml
+XML|${jetty.home}/etc/jetty-jmx.xml
+XML|${jetty.home}/etc/jetty-ssl.xml
+XML|${jetty.home}/etc/jetty-ssl-context.xml
+XML|${jetty.home}/etc/jetty-alpn.xml
+XML|${jetty.home}/etc/jetty-http2.xml
+
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.home}/lib/jetty-http-9.3.jar
+LIB|${jetty.home}/lib/jetty-io-9.3.jar
+LIB|${jetty.home}/lib/jetty-jmx-9.3.jar
+LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.home}/lib/jetty-server-9.3.jar
+LIB|${jetty.home}/lib/jetty-util-9.3.jar
+LIB|${jetty.home}/lib/jetty-xml-9.3.jar
+LIB|${jetty.home}/lib/servlet-api-3.1.jar
+LIB|${jetty.home}/lib/jetty-alpn-server-9.3.jar
+LIB|${jetty.home}/lib/http2/http2-common-9.3.jar
+LIB|${jetty.home}/lib/http2/http2-hpack-9.3.jar
+LIB|${jetty.home}/lib/http2/http2-server-9.3.jar
+
+# The Properties we expect (order is irrelevant)
+# (this is the property we actually set in jetty.base)
+PROP|jetty.http.port=9090
+#PROP|java.version=1.8.0_31
+PROP|jetty.sslContext.keyStorePath=etc/keystore
+PROP|jetty.sslContext.keyStorePassword=friendly
+PROP|jetty.sslContext.keyManagerPassword=icecream
+PROP|jetty.sslContext.trustStorePath=etc/keystore
+PROP|jetty.sslContext.trustStorePassword=sundae
+
+# The Downloads
+DOWNLOAD|maven://org.mortbay.jetty.alpn/alpn-boot/8.1.3.v20150130|lib/alpn/alpn-boot-8.1.3.v20150130.jar
+DOWNLOAD|https://raw.githubusercontent.com/eclipse/jetty.project/master/jetty-server/src/test/config/etc/keystore?id=master|etc/keystore
+
+# The Bootlib
+BOOTLIB|-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.3.v20150130.jar
+
+# The Files
+FILE|lib/
+FILE|lib/alpn/
diff --git a/jetty-start/src/test/resources/usecases/http2/start.ini b/jetty-start/src/test/resources/usecases/http2/start.ini
new file mode 100644
index 0000000..96a9c58
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/http2/start.ini
@@ -0,0 +1,12 @@
+
+--module=server,http,jmx,http2
+
+jetty.http.port=9090
+
+# Some SSL keystore configuration
+jetty.sslContext.keyStorePath=etc/keystore
+jetty.sslContext.keyStorePassword=friendly
+jetty.sslContext.keyManagerPassword=icecream
+jetty.sslContext.trustStorePath=etc/keystore
+jetty.sslContext.trustStorePassword=sundae
+
diff --git a/jetty-start/src/test/resources/usecases/include-jetty-dir-logging.assert.txt b/jetty-start/src/test/resources/usecases/include-jetty-dir-logging.assert.txt
new file mode 100644
index 0000000..60f10dd
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/include-jetty-dir-logging.assert.txt
@@ -0,0 +1,37 @@
+# The XMLs we expect (order is important)
+XML|${maven-test-resources}/extra-jetty-dirs/logging/etc/jetty-logging.xml
+XML|${jetty.home}/etc/jetty.xml
+XML|${jetty.home}/etc/jetty-http.xml
+XML|${jetty.home}/etc/jetty-jmx.xml
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.home}/lib/jetty-http-9.3.jar
+LIB|${jetty.home}/lib/jetty-io-9.3.jar
+LIB|${jetty.home}/lib/jetty-jmx-9.3.jar
+LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.home}/lib/jetty-server-9.3.jar
+LIB|${jetty.home}/lib/jetty-util-9.3.jar
+LIB|${jetty.home}/lib/jetty-xml-9.3.jar
+LIB|${jetty.home}/lib/servlet-api-3.1.jar
+LIB|${jetty.base}/resources
+LIB|${maven-test-resources}/extra-jetty-dirs/logging/lib/logging/logback.jar
+
+# The Properties we expect (order is irrelevant)
+# (this is the property we actually set in jetty.base)
+PROP|jetty.http.port=9090
+# (these are the ones set by default from jetty.home modules)
+# PROP|jetty.http.idleTimeout=30000
+# PROP|jetty.httpConfig.delayDispatchUntilContent=false
+# PROP|jetty.server.dumpAfterStart=false
+# PROP|jetty.server.dumpBeforeStop=false
+# PROP|jetty.httpConfig.outputBufferSize=32768
+# PROP|jetty.httpConfig.requestHeaderSize=8192
+# PROP|jetty.httpConfig.responseHeaderSize=8192
+# PROP|jetty.httpConfig.sendDateHeader=false
+# PROP|jetty.httpConfig.sendServerVersion=true
+# PROP|jetty.threadPool.maxThreads=200
+# PROP|jetty.threadPool.minThreads=10
+# PROP|jetty.threadPool.idleTimeout=60000
+
+# Files
+FILE|logs/
diff --git a/jetty-start/src/test/resources/usecases/home/etc/realm.properties b/jetty-start/src/test/resources/usecases/include-jetty-dir-logging/resources/some.properties
similarity index 100%
rename from jetty-start/src/test/resources/usecases/home/etc/realm.properties
rename to jetty-start/src/test/resources/usecases/include-jetty-dir-logging/resources/some.properties
diff --git a/jetty-start/src/test/resources/usecases/include-jetty-dir-logging/start.ini b/jetty-start/src/test/resources/usecases/include-jetty-dir-logging/start.ini
new file mode 100644
index 0000000..d44a453
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/include-jetty-dir-logging/start.ini
@@ -0,0 +1,6 @@
+
+--include-jetty-dir=${start.basedir}/../../extra-jetty-dirs/logging
+--module=server,http,jmx
+#--module=resources
+
+jetty.http.port=9090
diff --git a/jetty-start/src/test/resources/usecases/jmx.assert.txt b/jetty-start/src/test/resources/usecases/jmx.assert.txt
new file mode 100644
index 0000000..205c2ea
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/jmx.assert.txt
@@ -0,0 +1,31 @@
+# The XMLs we expect (order is important)
+XML|${jetty.home}/etc/jetty.xml
+XML|${jetty.home}/etc/jetty-http.xml
+XML|${jetty.home}/etc/jetty-jmx.xml
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.home}/lib/jetty-http-9.3.jar
+LIB|${jetty.home}/lib/jetty-io-9.3.jar
+LIB|${jetty.home}/lib/jetty-jmx-9.3.jar
+LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.home}/lib/jetty-server-9.3.jar
+LIB|${jetty.home}/lib/jetty-util-9.3.jar
+LIB|${jetty.home}/lib/jetty-xml-9.3.jar
+LIB|${jetty.home}/lib/servlet-api-3.1.jar
+
+# The Properties we expect (order is irrelevant)
+# (this is the property we actually set in jetty.base)
+PROP|jetty.http.port=9090
+# (these are the ones set by default from jetty.home modules)
+# PROP|jetty.http.idleTimeout=30000
+# PROP|jetty.httpConfig.delayDispatchUntilContent=false
+# PROP|jetty.server.dumpAfterStart=false
+# PROP|jetty.server.dumpBeforeStop=false
+# PROP|jetty.httpConfig.outputBufferSize=32768
+# PROP|jetty.httpConfig.requestHeaderSize=8192
+# PROP|jetty.httpConfig.responseHeaderSize=8192
+# PROP|jetty.httpConfig.sendDateHeader=false
+# PROP|jetty.httpConfig.sendServerVersion=true
+# PROP|jetty.threadPool.maxThreads=200
+# PROP|jetty.threadPool.minThreads=10
+# PROP|jetty.threadPool.idleTimeout=60000
diff --git a/jetty-start/src/test/resources/usecases/jmx/start.ini b/jetty-start/src/test/resources/usecases/jmx/start.ini
new file mode 100644
index 0000000..5339629
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/jmx/start.ini
@@ -0,0 +1,4 @@
+
+--module=server,http,jmx
+
+jetty.http.port=9090
diff --git a/jetty-start/src/test/resources/usecases/jsp.assert.txt b/jetty-start/src/test/resources/usecases/jsp.assert.txt
new file mode 100644
index 0000000..96bd0ca
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/jsp.assert.txt
@@ -0,0 +1,49 @@
+# The XMLs we expect (order is important)
+XML|${jetty.home}/etc/jetty.xml
+XML|${jetty.home}/etc/jetty-http.xml
+XML|${jetty.home}/etc/jetty-plus.xml
+XML|${jetty.home}/etc/jetty-annotations.xml
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.home}/lib/jetty-annotations-9.3.jar
+LIB|${jetty.home}/lib/annotations/asm-5.0.1.jar
+LIB|${jetty.home}/lib/annotations/asm-commons-5.0.1.jar
+LIB|${jetty.home}/lib/annotations/javax.annotation-api-1.2.jar
+LIB|${jetty.home}/lib/jetty-http-9.3.jar
+LIB|${jetty.home}/lib/jetty-io-9.3.jar
+LIB|${jetty.home}/lib/jetty-jndi-9.3.jar
+LIB|${jetty.home}/lib/jndi/javax.mail.glassfish-1.4.1.v201005082020.jar
+LIB|${jetty.home}/lib/jndi/javax.transaction-api-1.2.jar
+LIB|${jetty.home}/lib/jetty-plus-9.3.jar
+LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.home}/lib/jetty-security-9.3.jar
+LIB|${jetty.home}/lib/jetty-server-9.3.jar
+LIB|${jetty.home}/lib/jetty-servlet-9.3.jar
+LIB|${jetty.home}/lib/jetty-util-9.3.jar
+LIB|${jetty.home}/lib/jetty-webapp-9.3.jar
+LIB|${jetty.home}/lib/jetty-xml-9.3.jar
+LIB|${jetty.home}/lib/servlet-api-3.1.jar
+LIB|${jetty.home}/lib/apache-jsp/org.eclipse.jetty.apache-jsp-9.3.jar
+LIB|${jetty.home}/lib/apache-jsp/org.eclipse.jetty.orbit.org.eclipse.jdt.core-3.8.2.v20130121.jar
+LIB|${jetty.home}/lib/apache-jsp/org.mortbay.jasper.apache-el-8.0.20.M0.jar
+LIB|${jetty.home}/lib/apache-jsp/org.mortbay.jasper.apache-jsp-8.0.20.M0.jar
+
+# The Properties we expect (order is irrelevant)
+# (these are the properties we actually set in the configuration)
+PROP|jetty.http.port=9090
+# (these are the ones set by default from jetty.home modules)
+# PROP|jetty.http.idleTimeout=30000
+# PROP|jetty.httpConfig.delayDispatchUntilContent=false
+# PROP|jetty.server.dumpAfterStart=false
+# PROP|jetty.server.dumpBeforeStop=false
+# PROP|jetty.httpConfig.outputBufferSize=32768
+# PROP|jetty.httpConfig.requestHeaderSize=8192
+# PROP|jetty.httpConfig.responseHeaderSize=8192
+# PROP|jetty.httpConfig.sendDateHeader=false
+# PROP|jetty.httpConfig.sendServerVersion=true
+# PROP|jetty.threadPool.maxThreads=200
+# PROP|jetty.threadPool.minThreads=10
+# PROP|jetty.threadPool.idleTimeout=60000
+
+# Files / Directories to create
+# FILE|lib/
diff --git a/jetty-start/src/test/resources/usecases/jsp/start.ini b/jetty-start/src/test/resources/usecases/jsp/start.ini
new file mode 100644
index 0000000..f01871c
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/jsp/start.ini
@@ -0,0 +1,6 @@
+
+--module=server
+--module=http
+--module=jsp
+
+jetty.http.port=9090
diff --git a/jetty-start/src/test/resources/usecases/logging.assert.txt b/jetty-start/src/test/resources/usecases/logging.assert.txt
new file mode 100644
index 0000000..90780a9
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/logging.assert.txt
@@ -0,0 +1,45 @@
+# The XMLs we expect (order is important)
+XML|${jetty.home}/etc/jetty.xml
+XML|${jetty.home}/etc/jetty-http.xml
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.home}/lib/jetty-http-9.3.jar
+LIB|${jetty.home}/lib/jetty-io-9.3.jar
+LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.home}/lib/jetty-server-9.3.jar
+LIB|${jetty.home}/lib/jetty-util-9.3.jar
+LIB|${jetty.home}/lib/jetty-xml-9.3.jar
+LIB|${jetty.home}/lib/servlet-api-3.1.jar
+LIB|${jetty.base}/lib/logging/slf4j-api.jar
+LIB|${jetty.base}/lib/logging/jul-to-slf4j.jar
+LIB|${jetty.base}/lib/logging/logback-core.jar
+LIB|${jetty.base}/lib/logging/logback-classic.jar
+LIB|${jetty.base}/resources
+
+# The Properties we expect (order is irrelevant)
+# (this is the property we actually set in jetty.base)
+PROP|jetty.http.port=9090
+# (these are the ones set by default from jetty.home modules)
+# PROP|jetty.http.idleTimeout=30000
+# PROP|jetty.httpConfig.delayDispatchUntilContent=false
+# PROP|jetty.server.dumpAfterStart=false
+# PROP|jetty.server.dumpBeforeStop=false
+# PROP|jetty.httpConfig.outputBufferSize=32768
+# PROP|jetty.httpConfig.requestHeaderSize=8192
+# PROP|jetty.httpConfig.responseHeaderSize=8192
+# PROP|jetty.httpConfig.sendDateHeader=false
+# PROP|jetty.httpConfig.sendServerVersion=true
+# PROP|jetty.threadPool.maxThreads=200
+# PROP|jetty.threadPool.minThreads=10
+# PROP|jetty.threadPool.idleTimeout=60000
+
+# Other File References
+FILE|logs/
+FILE|resources/
+
+# Downloads
+DOWNLOAD|http://central.maven.org/maven2/org/slf4j/slf4j-api/1.6.6/slf4j-api-1.6.6.jar|lib/logging/slf4j-api-1.6.6.jar
+DOWNLOAD|http://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.0.7/logback-core-1.0.7.jar|lib/logging/logback-core-1.0.7.jar
+DOWNLOAD|http://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.0.7/logback-classic-1.0.7.jar|lib/logging/logback-classic-1.0.7.jar
+DOWNLOAD|https://raw.githubusercontent.com/jetty-project/logging-modules/master/logback/logback.xml|resources/logback.xml
+DOWNLOAD|https://raw.githubusercontent.com/jetty-project/logging-modules/master/logback/jetty-logging.properties|resources/jetty-logging.properties
diff --git a/jetty-start/src/test/resources/usecases/base.logging/lib/logging/jul-to-slf4j.jar b/jetty-start/src/test/resources/usecases/logging/lib/logging/jul-to-slf4j.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.logging/lib/logging/jul-to-slf4j.jar
rename to jetty-start/src/test/resources/usecases/logging/lib/logging/jul-to-slf4j.jar
diff --git a/jetty-start/src/test/resources/usecases/base.logging/lib/logging/logback-classic.jar b/jetty-start/src/test/resources/usecases/logging/lib/logging/logback-classic.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.logging/lib/logging/logback-classic.jar
rename to jetty-start/src/test/resources/usecases/logging/lib/logging/logback-classic.jar
diff --git a/jetty-start/src/test/resources/usecases/base.logging/lib/logging/logback-core.jar b/jetty-start/src/test/resources/usecases/logging/lib/logging/logback-core.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.logging/lib/logging/logback-core.jar
rename to jetty-start/src/test/resources/usecases/logging/lib/logging/logback-core.jar
diff --git a/jetty-start/src/test/resources/usecases/base.logging/lib/logging/slf4j-api.jar b/jetty-start/src/test/resources/usecases/logging/lib/logging/slf4j-api.jar
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.logging/lib/logging/slf4j-api.jar
rename to jetty-start/src/test/resources/usecases/logging/lib/logging/slf4j-api.jar
diff --git a/jetty-start/src/test/resources/usecases/base.logging/modules/logging.mod b/jetty-start/src/test/resources/usecases/logging/modules/logging.mod
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.logging/modules/logging.mod
rename to jetty-start/src/test/resources/usecases/logging/modules/logging.mod
diff --git a/jetty-start/src/test/resources/usecases/base.logging/resources/jetty-logging.properties b/jetty-start/src/test/resources/usecases/logging/resources/jetty-logging.properties
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.logging/resources/jetty-logging.properties
rename to jetty-start/src/test/resources/usecases/logging/resources/jetty-logging.properties
diff --git a/jetty-start/src/test/resources/usecases/base.logging/resources/logback.xml b/jetty-start/src/test/resources/usecases/logging/resources/logback.xml
similarity index 100%
rename from jetty-start/src/test/resources/usecases/base.logging/resources/logback.xml
rename to jetty-start/src/test/resources/usecases/logging/resources/logback.xml
diff --git a/jetty-start/src/test/resources/usecases/logging/start.ini b/jetty-start/src/test/resources/usecases/logging/start.ini
new file mode 100644
index 0000000..083675e
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/logging/start.ini
@@ -0,0 +1,7 @@
+
+--module=server
+--module=http
+--module=logging
+--module=resources
+
+jetty.http.port=9090
diff --git a/jetty-start/src/test/resources/usecases/versioned-modules-too-new/modules/http.mod b/jetty-start/src/test/resources/usecases/versioned-modules-too-new/modules/http.mod
new file mode 100644
index 0000000..cff8028
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/versioned-modules-too-new/modules/http.mod
@@ -0,0 +1,39 @@
+#
+# Jetty HTTP Connector
+#
+
+[version]
+9.3
+
+[depend]
+server
+
+[xml]
+etc/jetty-http.xml
+
+[ini-template]
+### HTTP Connector Configuration
+
+## Connector host/address to bind to
+# jetty.http.host=0.0.0.0
+
+## Connector port to listen on
+# jetty.http.port=80
+
+## Connector idle timeout in milliseconds
+# jetty.http.idleTimeout=30000
+
+## Connector socket linger time in seconds (-1 to disable)
+# jetty.http.soLingerTime=-1
+
+## Number of acceptors (-1 picks default based on number of cores)
+# jetty.http.acceptors=-1
+
+## Number of selectors (-1 picks default based on number of cores)
+# jetty.http.selectors=-1
+
+## ServerSocketChannel backlog (0 picks platform default)
+# jetty.http.acceptorQueueSize=0
+
+## Thread priority delta to give to acceptor threads
+# jetty.http.acceptorPriorityDelta=0
diff --git a/jetty-start/src/test/resources/usecases/versioned-modules-too-new/modules/http3.mod b/jetty-start/src/test/resources/usecases/versioned-modules-too-new/modules/http3.mod
new file mode 100644
index 0000000..34ec477
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/versioned-modules-too-new/modules/http3.mod
@@ -0,0 +1,10 @@
+#
+# Fake Jetty HTTP/3 Connector
+#
+
+[version]
+10.0
+
+[depend]
+server
+
diff --git a/jetty-start/src/test/resources/usecases/versioned-modules-too-new/start.ini b/jetty-start/src/test/resources/usecases/versioned-modules-too-new/start.ini
new file mode 100644
index 0000000..42b2db8
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/versioned-modules-too-new/start.ini
@@ -0,0 +1,6 @@
+
+--module=server
+--module=http
+--module=http3
+
+jetty.http.port=9090
diff --git a/jetty-start/src/test/resources/usecases/versioned-modules.assert.txt b/jetty-start/src/test/resources/usecases/versioned-modules.assert.txt
new file mode 100644
index 0000000..f1b8108
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/versioned-modules.assert.txt
@@ -0,0 +1,18 @@
+# The XMLs we expect (order is important)
+XML|${jetty.home}/etc/jetty.xml
+XML|${jetty.home}/etc/jetty-http.xml
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.home}/lib/jetty-http-9.3.jar
+LIB|${jetty.home}/lib/jetty-io-9.3.jar
+LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.home}/lib/jetty-server-9.3.jar
+LIB|${jetty.home}/lib/jetty-util-9.3.jar
+LIB|${jetty.home}/lib/jetty-xml-9.3.jar
+LIB|${jetty.home}/lib/servlet-api-3.1.jar
+
+# The Properties we expect (order is irrelevant)
+# (this is the property we actually set in jetty.base)
+PROP|jetty.http.port=9090
+PROP|from-module=old
+PROP|the-future=is-new
diff --git a/jetty-start/src/test/resources/usecases/versioned-modules/modules/http.mod b/jetty-start/src/test/resources/usecases/versioned-modules/modules/http.mod
new file mode 100644
index 0000000..cff8028
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/versioned-modules/modules/http.mod
@@ -0,0 +1,39 @@
+#
+# Jetty HTTP Connector
+#
+
+[version]
+9.3
+
+[depend]
+server
+
+[xml]
+etc/jetty-http.xml
+
+[ini-template]
+### HTTP Connector Configuration
+
+## Connector host/address to bind to
+# jetty.http.host=0.0.0.0
+
+## Connector port to listen on
+# jetty.http.port=80
+
+## Connector idle timeout in milliseconds
+# jetty.http.idleTimeout=30000
+
+## Connector socket linger time in seconds (-1 to disable)
+# jetty.http.soLingerTime=-1
+
+## Number of acceptors (-1 picks default based on number of cores)
+# jetty.http.acceptors=-1
+
+## Number of selectors (-1 picks default based on number of cores)
+# jetty.http.selectors=-1
+
+## ServerSocketChannel backlog (0 picks platform default)
+# jetty.http.acceptorQueueSize=0
+
+## Thread priority delta to give to acceptor threads
+# jetty.http.acceptorPriorityDelta=0
diff --git a/jetty-start/src/test/resources/usecases/versioned-modules/modules/new.mod b/jetty-start/src/test/resources/usecases/versioned-modules/modules/new.mod
new file mode 100644
index 0000000..ae1b359
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/versioned-modules/modules/new.mod
@@ -0,0 +1,10 @@
+#
+# New Module (testing module based defaults)
+#
+
+[version]
+9.3
+
+[ini]
+the-future=is-new
+
diff --git a/jetty-start/src/test/resources/usecases/versioned-modules/modules/old.mod b/jetty-start/src/test/resources/usecases/versioned-modules/modules/old.mod
new file mode 100644
index 0000000..ff27710
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/versioned-modules/modules/old.mod
@@ -0,0 +1,7 @@
+#
+# Old Module (backward compat test with 9.2 modules)
+#
+
+[defaults]
+from-module=old
+
diff --git a/jetty-start/src/test/resources/usecases/versioned-modules/start.ini b/jetty-start/src/test/resources/usecases/versioned-modules/start.ini
new file mode 100644
index 0000000..c306053
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/versioned-modules/start.ini
@@ -0,0 +1,7 @@
+
+--module=server
+--module=http
+--module=old
+--module=new
+
+jetty.http.port=9090
diff --git a/jetty-util-ajax/pom.xml b/jetty-util-ajax/pom.xml
index 5630d24..63fa203 100644
--- a/jetty-util-ajax/pom.xml
+++ b/jetty-util-ajax/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-util-ajax</artifactId>
@@ -15,54 +15,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",org.slf4j;version="[1.5,2.0)";resolution:=optional,org.slf4j.impl;version="[1.5,2.0)";resolution:=optional,*</Import-Package>
-              </instructions>
-            </configuration>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-<!--
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
--->
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSON.java b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSON.java
index b4c9f22..3b74e1f 100644
--- a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSON.java
+++ b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSON.java
@@ -39,51 +39,51 @@
 
 /**
  * JSON Parser and Generator.
- * <p />
+ * <p>
  * This class provides some static methods to convert POJOs to and from JSON
  * notation. The mapping from JSON to java is:
  *
  * <pre>
- *   object ==> Map
- *   array  ==> Object[]
- *   number ==> Double or Long
- *   string ==> String
- *   null   ==> null
- *   bool   ==> Boolean
+ *   object --&gt; Map
+ *   array  --&gt; Object[]
+ *   number --&gt; Double or Long
+ *   string --&gt; String
+ *   null   --&gt; null
+ *   bool   --&gt; 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!)
+ *   String --&gt; string
+ *   Number --&gt; number
+ *   Map    --&gt; object
+ *   List   --&gt; array
+ *   Array  --&gt; array
+ *   null   --&gt; null
+ *   Boolean--&gt; boolean
+ *   Object --&gt; 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 />
+ * <p>
  * The interface {@link JSON.Generator} may be implemented by classes that know
  * how to render themselves as JSON and the {@link #toString(Object)} method
  * will use {@link JSON.Generator#addJSON(Appendable)} to generate the JSON.
  * The class {@link JSON.Literal} may be used to hold pre-generated JSON object.
- * <p />
+ * <p>
  * The interface {@link JSON.Convertor} may be implemented to provide static
  * converters for objects that may be registered with
  * {@link #registerConvertor(Class, Convertor)}.
  * These converters are looked up by class, interface and super class by
  * {@link #getConvertor(Class)}.
- * <p />
+ * <p>
  * If a JSON object has a "class" field, then a java class for that name is
  * loaded and the method {@link #convertTo(Class,Map)} is used to find a
  * {@link JSON.Convertor} for that class.
- * <p />
+ * <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).
  */
@@ -188,6 +188,7 @@
      * @param in
      *            Reader containing JSON object or array.
      * @return A Map, Object array or primitive array parsed from the JSON.
+     * @throws IOException if unable to parse
      */
     public static Object parse(Reader in) throws IOException
     {
@@ -200,6 +201,7 @@
      * @param stripOuterComment
      *            If true, an outer comment around the JSON is ignored.
      * @return A Map, Object array or primitive array parsed from the JSON.
+     * @throws IOException if unable to parse
      */
     public static Object parse(Reader in, boolean stripOuterComment) throws IOException
     {
@@ -207,10 +209,11 @@
     }
 
     /**
-     * @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.
+     * @throws IOException if unable to parse
+     * @deprecated use {@link #parse(Reader)}
      */
     @Deprecated
     public static Object parse(InputStream in) throws IOException
@@ -219,12 +222,13 @@
     }
 
     /**
-     * @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.
+     * @throws IOException if unable to parse
+     * @deprecated use {@link #parse(Reader, boolean)}
      */
     @Deprecated
     public static Object parse(InputStream in, boolean stripOuterComment) throws IOException
diff --git a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java
index 18212a7..9fcc4ce 100644
--- a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java
+++ b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java
@@ -28,12 +28,8 @@
 
 import org.eclipse.jetty.util.ajax.JSON.Output;
 
-/* ------------------------------------------------------------ */
 /**
  * Convert an Object to JSON using reflection on getters methods.
- * 
- * 
- *
  */
 public class JSONObjectConvertor implements JSON.Convertor
 {
@@ -52,7 +48,7 @@
     
     /* ------------------------------------------------------------ */
     /**
-     * @param fromJSON
+     * @param fromJSON true to convert from JSON
      * @param excluded An array of field names to exclude from the conversion
      */
     public JSONObjectConvertor(boolean fromJSON,String[] excluded)
@@ -84,7 +80,7 @@
             {
                 Method m=methods[i];
                 if (!Modifier.isStatic(m.getModifiers()) &&  
-                        m.getParameterTypes().length==0 && 
+                        m.getParameterCount()==0 &&
                         m.getReturnType()!=null &&
                         m.getDeclaringClass()!=Object.class)
                 {
diff --git a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java
index c173b42..82e90f5 100644
--- a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java
+++ b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java
@@ -116,7 +116,7 @@
             if (!Modifier.isStatic(m.getModifiers()) && m.getDeclaringClass()!=Object.class)
             {
                 String name=m.getName();
-                switch(m.getParameterTypes().length)
+                switch(m.getParameterCount())
                 {
                     case 0:
                         
diff --git a/jetty-util/pom.xml b/jetty-util/pom.xml
index 02fae2a..9907c07 100644
--- a/jetty-util/pom.xml
+++ b/jetty-util/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-util</artifactId>
@@ -15,52 +15,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",org.slf4j;version="[1.6,2.0)";resolution:=optional,org.slf4j.impl;version="[1.6,2.0)";resolution:=optional,*</Import-Package>
-              </instructions>
-            </configuration>
-           </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-util/src/main/config/etc/jetty-logging.xml b/jetty-util/src/main/config/etc/jetty-logging.xml
index 52589ee..dc7fe9e 100644
--- a/jetty-util/src/main/config/etc/jetty-logging.xml
+++ b/jetty-util/src/main/config/etc/jetty-logging.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Configure stderr and stdout to a Jetty rollover log file        -->
@@ -13,10 +12,12 @@
     <New id="ServerLog" class="java.io.PrintStream">
       <Arg>
         <New class="org.eclipse.jetty.util.RolloverFileOutputStream">
-          <Arg><Property name="jetty.logs" default="./logs"/>/yyyy_mm_dd.stderrout.log</Arg>
-          <Arg type="boolean">false</Arg>
-          <Arg type="int">90</Arg>
-          <Arg><Call class="java.util.TimeZone" name="getTimeZone"><Arg>GMT</Arg></Call></Arg>
+          <Arg><Property name="jetty.logging.dir" deprecated="jetty.logs" default="./logs"/>/yyyy_mm_dd.stderrout.log</Arg>
+          <Arg type="boolean"><Property name="jetty.logging.append" default="false"/></Arg>
+          <Arg type="int"><Property name="jetty.logging.retainDays" default="90"/></Arg>
+          <Arg>
+              <Call class="java.util.TimeZone" name="getTimeZone"><Arg><Property name="jetty.logging.timezone" default="GMT"/></Arg></Call>
+          </Arg>
           <Get id="ServerLogName" name="datedFilename"/>
         </New>
       </Arg>
diff --git a/jetty-util/src/main/config/modules/logging.mod b/jetty-util/src/main/config/modules/logging.mod
index a39bfe4..4f30a88 100644
--- a/jetty-util/src/main/config/modules/logging.mod
+++ b/jetty-util/src/main/config/modules/logging.mod
@@ -14,18 +14,23 @@
 
 [ini-template]
 ## Logging Configuration
-# Configure jetty logging for default internal behavior STDERR output
+## Configure jetty logging for default internal behavior STDERR output
 # -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
 
-# Configure jetty logging for slf4j
+## Configure jetty logging for slf4j
 # -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog
 
-# Configure jetty logging for java.util.logging
+## Configure jetty logging for java.util.logging
 # -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.JavaUtilLog
 
-# STDERR / STDOUT Logging
-# Number of days to retain logs
-# jetty.log.retain=90
-# Directory for logging output
-# Either a path relative to ${jetty.base} or an absolute path
-# jetty.logs=logs
+## Logging directory (relative to $jetty.base)
+# jetty.logging.dir=logs
+
+## Whether to append to existing file
+# jetty.logging.append=false
+
+## How many days to retain old log files
+# jetty.logging.retainDays=90
+
+## Timezone of the log timestamps
+# jetty.logging.timezone=GMT
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/AbstractTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/AbstractTrie.java
index 1081753..caae197 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/AbstractTrie.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/AbstractTrie.java
@@ -22,12 +22,13 @@
 import java.nio.charset.StandardCharsets;
 
 
-/* ------------------------------------------------------------ */
-/** Abstract Trie implementation.
+/** 
+ * Abstract Trie implementation.
  * <p>Provides some common implementations, which may not be the most
  * efficient. For byte operations, the assumption is made that the charset
  * is ISO-8859-1</p>
- * @param <V>
+ * 
+ * @param <V> the type of object that the Trie holds
  */
 public abstract class AbstractTrie<V> implements Trie<V>
 {
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayQueue.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayQueue.java
index 2e05ccd..9ed8f46 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayQueue.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayQueue.java
@@ -25,11 +25,12 @@
 /* ------------------------------------------------------------ */
 /**
  * Queue backed by circular array.
- * <p/>
+ * <p>
  * This partial Queue implementation (also with {@link #remove()} for stack operation)
  * is backed by a growable circular array.
+ * </p>
  *
- * @param <E>
+ * @param <E> the type of object the queue holds
  */
 public class ArrayQueue<E> extends AbstractList<E> implements Queue<E>
 {
@@ -91,6 +92,15 @@
     }
 
     /* ------------------------------------------------------------ */
+    /**
+     * @return the next slot to be used
+     */
+    public int getNextSlotUnsafe()
+    {
+        return _nextSlot;
+    }
+    
+    /* ------------------------------------------------------------ */
     @Override
     public boolean add(E e)
     {
@@ -109,9 +119,9 @@
     }
 
     /* ------------------------------------------------------------ */
-    private boolean enqueue(E e)
+    protected boolean enqueue(E e)
     {
-        if (_size == _elements.length && !grow())
+        if (_size == _elements.length && !growUnsafe())
             return false;
 
         _size++;
@@ -192,7 +202,7 @@
     }
 
     /* ------------------------------------------------------------ */
-    private E dequeue()
+    protected E dequeue()
     {
         E e = at(_nextE);
         _elements[_nextE] = null;
@@ -246,6 +256,12 @@
     }
 
     /* ------------------------------------------------------------ */
+    public int sizeUnsafe()
+    {
+        return _size;
+    }
+
+    /* ------------------------------------------------------------ */
     @Override
     public E get(int index)
     {
@@ -339,7 +355,7 @@
             if (index < 0 || index > _size)
                 throw new IndexOutOfBoundsException("!(" + 0 + "<" + index + "<=" + _size + ")");
 
-            if (_size == _elements.length && !grow())
+            if (_size == _elements.length && !growUnsafe())
                 throw new IllegalStateException("Full");
 
             if (index == _size)
@@ -384,25 +400,33 @@
     }
 
     /* ------------------------------------------------------------ */
-    protected boolean grow()
+    protected void resizeUnsafe(int newCapacity)
     {
-        synchronized (_lock)
+        newCapacity = Math.max(newCapacity,_size);
+        Object[] elements = new Object[newCapacity];
+
+        if (_size>0)
         {
-            if (_growCapacity <= 0)
-                return false;
-
-            Object[] elements = new Object[_elements.length + _growCapacity];
-
-            int split = _elements.length - _nextE;
-            if (split > 0)
+            if (_nextSlot>_nextE)
+                System.arraycopy(_elements, _nextE, elements, 0, _size);
+            else
+            {
+                int split = _elements.length - _nextE;
                 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;
+            }
         }
+        _elements = elements;
+        _nextE = 0;
+        _nextSlot = _size;
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected boolean growUnsafe()
+    {
+        if (_growCapacity <= 0)
+            return false;
+        resizeUnsafe(_elements.length+_growCapacity);
+        return true;
     }
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java
index 22bcd29..852ecb2 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java
@@ -24,20 +24,22 @@
 import java.util.Set;
 
 
-/* ------------------------------------------------------------ */
 /** 
  * <p>A Ternary Trie String lookup data structure.</p>
- * This Trie is of a fixed size and cannot grow (which can be a good thing with regards to DOS when used as a cache).
  * <p>
- * The Trie is stored in 3 arrays:<dl>
+ * This Trie is of a fixed size and cannot grow (which can be a good thing with regards to DOS when used as a cache).
+ * </p>
+ * <p>
+ * The Trie is stored in 3 arrays:
+ * </p>
+ * <dl>
  * <dt>char[] _tree</dt><dd>This is semantically 2 dimensional array flattened into a 1 dimensional char array. The second dimension
  * is that every 4 sequential elements represents a row of: character; hi index; eq index; low index, used to build a
  * ternary trie of key strings.</dd>
- * <dt>String[] _key<dt><dd>An array of key values where each element matches a row in the _tree array. A non zero key element 
+ * <dt>String[] _key</dt><dd>An array of key values where each element matches a row in the _tree array. A non zero key element 
  * indicates that the _tree row is a complete key rather than an intermediate character of a longer key.</dd>
  * <dt>V[] _value</dt><dd>An array of values corresponding to the _key array</dd>
  * </dl>
- * </p>
  * <p>The lookup of a value will iterate through the _tree array matching characters. If the equal tree branch is followed,
  * then the _key array is looked up to see if this is a complete match.  If a match is found then the _value array is looked up
  * to return the matching value.
@@ -52,7 +54,7 @@
  * Trie is required external locks need to be applied.
  * </p>
  * 
- * @param <V>
+ * @param <V> the Entry type 
  */
 public class ArrayTernaryTrie<V> extends AbstractTrie<V>
 {
@@ -141,8 +143,8 @@
 
     /* ------------------------------------------------------------ */
     /** Copy Trie and change capacity by a factor
-     * @param trie
-     * @param factor
+     * @param trie the trie to copy from
+     * @param factor the factor to grow the capacity by
      */
     public ArrayTernaryTrie(ArrayTernaryTrie<V> trie, double factor)
     {
@@ -153,6 +155,16 @@
         _tree=Arrays.copyOf(trie._tree, capacity*ROW_SIZE);
         _key=Arrays.copyOf(trie._key, capacity);
     }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void clear()
+    {
+        _rows=0;
+        Arrays.fill(_value,null);
+        Arrays.fill(_tree,(char)0);
+        Arrays.fill(_key,null);
+    }
     
     /* ------------------------------------------------------------ */
     @Override
@@ -311,9 +323,11 @@
     private V getBest(int t,String s,int offset,int len)
     {
         int node=t;
-        loop: for(int i=0; i<len; i++)
+        int end=offset+len;
+        loop: while(offset<end)
         {
-            char c=s.charAt(offset+i);
+            char c=s.charAt(offset++);
+            len--;
             if(isCaseInsensitive() && c<128)
                 c=StringUtil.lowercases[c];
 
@@ -333,9 +347,9 @@
                     if (_key[t]!=null)
                     {
                         node=t;
-                        V best=getBest(t,s,offset+i+1,len-i-1);
-                        if (best!=null)
-                            return best;
+                        V better=getBest(t,s,offset,len);
+                        if (better!=null)
+                            return better;
                     }
                     break;
                 }
@@ -362,9 +376,11 @@
     private V getBest(int t,byte[] b, int offset, int len)
     {
         int node=t;
-        loop: for(int i=0; i<len; i++)
+        int end=offset+len;
+        loop: while(offset<end)
         {
-            byte c=(byte)(b[offset+i]&0x7f);
+            byte c=(byte)(b[offset++]&0x7f);
+            len--;
             if(isCaseInsensitive())
                 c=(byte)StringUtil.lowercases[c];
 
@@ -384,9 +400,9 @@
                     if (_key[t]!=null)
                     {
                         node=t;
-                        V best=getBest(t,b,offset+i+1,len-i-1);
-                        if (best!=null)
-                            return best;
+                        V better=getBest(t,b,offset,len);
+                        if (better!=null)
+                            return better;
                     }
                     break;
                 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTrie.java
index a801686..b4b82d4 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTrie.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTrie.java
@@ -20,6 +20,7 @@
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -44,7 +45,7 @@
  * and not mutated during that access.  If concurrent mutations of the
  * Trie is required external locks need to be applied.
  * </p>
- * @param <V>
+ * @param <V> the entry type
  */
 public class ArrayTrie<V> extends AbstractTrie<V>
 {
@@ -132,7 +133,16 @@
         _rowIndex=new char[capacity*32];
         _key=new String[capacity];
     }
-    
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void clear()
+    {
+        _rows=0;
+        Arrays.fill(_value,null);
+        Arrays.fill(_rowIndex,(char)0);
+        Arrays.fill(_key,null);
+    }
     
     /* ------------------------------------------------------------ */
     @Override
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayUtil.java
index bb5e358..0f5a07a 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayUtil.java
@@ -25,8 +25,8 @@
 import java.util.List;
 
 
-/* ------------------------------------------------------------ */
 /**
+ * Utility methods for Array manipulation
  */
 public class ArrayUtil
     implements Cloneable, Serializable
@@ -55,11 +55,31 @@
     }
 
     /* ------------------------------------------------------------ */
+    /** Add arrays
+     * @param array1 An array to add to (or null)
+     * @param array2 An array to add to (or null)
+     * @return new array with contents of both arrays, or null if both arrays are null
+     * @param <T> the array entry type
+     */
+    public static<T> T[] add(T[] array1, T[] array2)
+    {
+        if (array1==null || array1.length==0)
+            return array2;
+        if (array2==null || array2.length==0)
+            return array1;
+                    
+        T[] na = Arrays.copyOf(array1,array1.length+array2.length);
+        System.arraycopy(array2,0,na,array1.length,array2.length);
+        return na;
+    }
+    
+    /* ------------------------------------------------------------ */
     /** 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
+     * @param <T> the array entry type
      */
     public static<T> T[] addToArray(T[] array, T item, Class<?> type)
     {
@@ -86,6 +106,7 @@
      * @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
+     * @param <T> the array entry type
      */
     public static<T> T[] prependToArray(T item, T[] array, Class<?> type)
     {
@@ -113,6 +134,7 @@
     /**
      * @param array Any array of object
      * @return A new <i>modifiable</i> list initialised with the elements from <code>array</code>.
+     * @param <E> the array entry type
      */
     public static<E> List<E> asMutableList(E[] array)
     {	
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java b/jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java
index 23a9f3b..6ccdfb3 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java
@@ -22,6 +22,7 @@
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
 
 
 /** Fast B64 Encoder/Decoder as described in RFC 1421.
@@ -35,12 +36,12 @@
 {
     private static final char __pad='=';
     private 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','+','/'
-            };
+        {
+        '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','+','/'
+        };
 
     private static final byte[] __rfc1421nibbles;
     static
@@ -52,6 +53,25 @@
             __rfc1421nibbles[(byte)__rfc1421alphabet[b]]=b;
         __rfc1421nibbles[(byte)__pad]=0;
     }
+    
+    private static final char[] __rfc4648urlAlphabet=
+        {
+        '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','-','_'
+        };
+
+    private static final byte[] __rfc4648urlNibbles;
+    static
+    {
+        __rfc4648urlNibbles=new byte[256];
+        for (int i=0;i<256;i++)
+            __rfc4648urlNibbles[i]=-1;
+        for (byte b=0;b<64;b++)
+            __rfc4648urlNibbles[(byte)__rfc4648urlAlphabet[b]]=b;
+        __rfc4648urlNibbles[(byte)__pad]=0;
+    }
 
     private B64Code()
     {
@@ -430,6 +450,75 @@
         return;
     }
     
+    /* ------------------------------------------------------------ */
+    public static byte[] decodeRFC4648URL(String encoded)
+    {
+        if (encoded==null)
+            return null;
+
+        ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);        
+        decodeRFC4648URL(encoded, bout);
+        return bout.toByteArray();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Base 64 decode as described in RFC 4648 URL.
+     * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
+     * @param encoded String to decode.
+     * @param bout stream for decoded bytes
+     * @throws IllegalArgumentException if the input is not a valid
+     *         B64 encoding.
+     */
+    static public void decodeRFC4648URL (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=__rfc4648urlNibbles[c];
+            if (nibble<0)
+                throw new IllegalArgumentException("Not B64 encoded");
+
+            nibbles[s++]=__rfc4648urlNibbles[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
     {
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java b/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java
index 397848c..25e9443 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java
@@ -33,13 +33,16 @@
 
 /**
  * A BlockingQueue backed by a circular array capable or growing.
- * <p/>
+ * <p>
  * 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.
- * <p/>
+ * </p>
+ * <p>
  * Unlike {@link java.util.concurrent.ArrayBlockingQueue}, this class is able to grow and provides a blocking put call.
- * <p/>
+ * </p>
+ * <p>
  * The queue has both a capacity (the size of the array currently allocated) and a max capacity (the maximum size that may be allocated), which defaults to
  * {@link Integer#MAX_VALUE}.
+ * </p>
  * 
  * @param <E>
  *            The element type
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingCallback.java b/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingCallback.java
index f7a5ad3..43c9faa 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingCallback.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingCallback.java
@@ -26,23 +26,22 @@
 
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.thread.NonBlockingThread;
 
-/* ------------------------------------------------------------ */
 /**
  * An implementation of Callback that blocks until success or failure.
  */
-public class BlockingCallback implements Callback
+@Deprecated
+public class BlockingCallback implements Callback.NonBlocking
 {
     private static final Logger LOG = Log.getLogger(BlockingCallback.class);
-    
     private static Throwable SUCCEEDED = new ConstantThrowable("SUCCEEDED");
     
     private final CountDownLatch _latch = new CountDownLatch(1);
     private final AtomicReference<Throwable> _state = new AtomicReference<>();
-    
+
     public BlockingCallback()
-    {}
+    {
+    }
 
     @Override
     public void succeeded()
@@ -58,7 +57,8 @@
             _latch.countDown();
     }
 
-    /** Block until the Callback has succeeded or failed and 
+    /**
+     * Blocks until the Callback has succeeded or failed and
      * after the return leave in the state to allow reuse.
      * This is useful for code that wants to repeatable use a FutureCallback to convert
      * an asynchronous API to a blocking API. 
@@ -66,9 +66,6 @@
      */
     public void block() throws IOException
     {
-        if (NonBlockingThread.isNonBlockingThread())
-            LOG.warn("Blocking a NonBlockingThread: ",new Throwable());
-        
         try
         {
             _latch.await();
@@ -90,12 +87,10 @@
             _state.set(null);
         }
     }
-    
-    
+
     @Override
     public String toString()
     {
         return String.format("%s@%x{%s}",BlockingCallback.class.getSimpleName(),hashCode(),_state.get());
     }
-
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java
index 8a38165..efc3d58 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java
@@ -19,18 +19,24 @@
 package org.eclipse.jetty.util;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.RandomAccessFile;
+import java.lang.reflect.Field;
+import java.nio.Buffer;
 import java.nio.BufferOverflowException;
 import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileChannel.MapMode;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.StandardOpenOption;
 import java.util.Arrays;
 
+import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.resource.Resource;
 
 
@@ -42,10 +48,12 @@
  * The various ByteBuffer methods assume a mode and some of them will switch or enforce a mode:
  * Allocate and clear set fill mode; flip and compact switch modes; read and write assume fill 
  * and flush modes.    This duality can result in confusing code such as:
+ * </p>
  * <pre>
  *     buffer.clear();
  *     channel.write(buffer);
  * </pre>
+ * <p>
  * Which looks as if it should write no data, but in fact writes the buffer worth of garbage.
  * </p>
  * <p>
@@ -57,13 +65,14 @@
  * <p>
  * Thus this class provides alternate implementations of {@link #allocate(int)}, 
  * {@link #allocateDirect(int)} and {@link #clear(ByteBuffer)} that leave the buffer
- * in flush mode.   Thus the following tests will pass:<pre>
+ * in flush mode.   Thus the following tests will pass:
+ * </p>
+ * <pre>
  *     ByteBuffer buffer = BufferUtil.allocate(1024);
  *     assert(buffer.remaining()==0);
  *     BufferUtil.clear(buffer);
  *     assert(buffer.remaining()==0);
  * </pre>
- * </p>
  * <p>If the BufferUtil methods {@link #fill(ByteBuffer, byte[], int, int)}, 
  * {@link #append(ByteBuffer, byte[], int, int)} or {@link #put(ByteBuffer, ByteBuffer)} are used,
  * then the caller does not need to explicitly switch the buffer to fill mode.    
@@ -71,6 +80,7 @@
  * then they can use explicit calls of #flipToFill(ByteBuffer) and #flipToFlush(ByteBuffer, int)
  * to change modes.  Note because this convention attempts to avoid the copies of compact, the position
  * is not set to zero on each fill cycle and so its value must be remembered:
+ * </p>
  * <pre>
  *      int pos = BufferUtil.flipToFill(buffer);
  *      try
@@ -82,8 +92,9 @@
  *          flipToFlush(buffer, pos);
  *      }
  * </pre>
- * The flipToFill method will effectively clear the buffer if it is emtpy and will compact the buffer if there is no space.
- * 
+ * <p>
+ * The flipToFill method will effectively clear the buffer if it is empty and will compact the buffer if there is no space.
+ * </p>
  */
 public class BufferUtil
 {
@@ -312,8 +323,7 @@
             {
                 to.put(from);
                 put = remaining;
-                from.position(0);
-                from.limit(0);
+                from.position(from.limit());
             }
             else if (from.hasArray())
             {
@@ -355,7 +365,7 @@
      * @param b bytes to append
      * @param off offset into byte
      * @param len length to append
-     * @throws BufferOverflowException
+     * @throws BufferOverflowException if unable to append buffer due to space limits
      */
     public static void append(ByteBuffer to, byte[] b, int off, int len) throws BufferOverflowException
     {
@@ -392,6 +402,7 @@
     /** Appends a buffer to a buffer
      * @param to Buffer is flush mode
      * @param b buffer to append
+     * @return The position of the valid data before the flipped position.
      */
     public static int append(ByteBuffer to, ByteBuffer b)
     {
@@ -413,6 +424,7 @@
      * @param b bytes to fill
      * @param off offset into byte
      * @param len length to fill
+     * @return The position of the valid data before the flipped position.
      */
     public static int fill(ByteBuffer to, byte[] b, int off, int len)
     {
@@ -521,10 +533,13 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** Convert a partial buffer to an ISO-8859-1 String
-     * @param buffer  The buffer to convert in flush mode. The buffer is unchanged
+    /** Convert a partial buffer to a String.
+     * 
+     * @param buffer the buffer to convert 
+     * @param position The position in the buffer to start the string from
+     * @param length The length of the buffer
      * @param charset The {@link Charset} to use to convert the bytes
-     * @return The buffer as a string.
+     * @return  The buffer as a string.
      */
     public static String toString(ByteBuffer buffer, int position, int length, Charset charset)
     {
@@ -553,11 +568,34 @@
      */
     public static int toInt(ByteBuffer buffer)
     {
+        return toInt(buffer,buffer.position(),buffer.remaining());
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * 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 in flush mode. The position is not changed.
+     * @param position
+     *            the position in the buffer to start reading from
+     * @param length
+     *            the length of the buffer to use for conversion
+     * @return an int of the buffer bytes
+     */
+    public static int toInt(ByteBuffer buffer, int position, int length)
+    {
         int val = 0;
         boolean started = false;
         boolean minus = false;
 
-        for (int i = buffer.position(); i < buffer.limit(); i++)
+        int limit = position+length;
+        
+        if (length<=0)
+            throw new NumberFormatException(toString(buffer,position,length,StandardCharsets.UTF_8));
+        
+        for (int i = position; i < limit; i++)
         {
             byte b = buffer.get(i);
             if (b <= SPACE)
@@ -809,30 +847,14 @@
 
     public static ByteBuffer toBuffer(String s)
     {
-        return ByteBuffer.wrap(s.getBytes(StandardCharsets.ISO_8859_1));
-    }
-
-    public static ByteBuffer toDirectBuffer(String s)
-    {
-        byte[] bytes = s.getBytes(StandardCharsets.ISO_8859_1);
-        ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length);
-        buf.put(bytes);
-        buf.flip();
-        return buf;
+        return toBuffer(s, StandardCharsets.ISO_8859_1);
     }
 
     public static ByteBuffer toBuffer(String s, Charset charset)
     {
-        return ByteBuffer.wrap(s.getBytes(charset));
-    }
-
-    public static ByteBuffer toDirectBuffer(String s, Charset charset)
-    {
-        byte[] bytes = s.getBytes(charset);
-        ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length);
-        buf.put(bytes);
-        buf.flip();
-        return buf;
+        if (s == null)
+            return EMPTY_BUFFER;
+        return toBuffer(s.getBytes(charset));
     }
 
     /**
@@ -842,9 +864,11 @@
      *            the byte array to back buffer with.
      * @return ByteBuffer with provided byte array, in flush mode
      */
-    public static ByteBuffer toBuffer(byte array[])
+    public static ByteBuffer toBuffer(byte[] array)
     {
-        return ByteBuffer.wrap(array);
+        if (array == null)
+            return EMPTY_BUFFER;
+        return toBuffer(array, 0, array.length);
     }
 
     /**
@@ -860,17 +884,54 @@
      */
     public static ByteBuffer toBuffer(byte array[], int offset, int length)
     {
+        if (array == null)
+            return EMPTY_BUFFER;
         return ByteBuffer.wrap(array, offset, length);
     }
 
+    public static ByteBuffer toDirectBuffer(String s)
+    {
+        return toDirectBuffer(s, StandardCharsets.ISO_8859_1);
+    }
+
+    public static ByteBuffer toDirectBuffer(String s, Charset charset)
+    {
+        if (s == null)
+            return EMPTY_BUFFER;
+        byte[] bytes = s.getBytes(charset);
+        ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length);
+        buf.put(bytes);
+        buf.flip();
+        return buf;
+    }
+
     public static ByteBuffer toMappedBuffer(File file) throws IOException
     {
-        try (RandomAccessFile raf = new RandomAccessFile(file, "r"))
+        try (FileChannel channel = FileChannel.open(file.toPath(),StandardOpenOption.READ))
         {
-            return raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length());
+            return channel.map(MapMode.READ_ONLY, 0, file.length());
         }
     }
 
+    public static boolean isMappedBuffer(ByteBuffer buffer)
+    {
+        if (!(buffer instanceof MappedByteBuffer))
+            return false;
+        MappedByteBuffer mapped = (MappedByteBuffer) buffer;
+
+        try 
+        {
+            // Check if it really is a mapped buffer
+            mapped.isLoaded();
+            return true;
+        }
+        catch(UnsupportedOperationException e)
+        {
+            return false;
+        }
+    }
+    
+    
     public static ByteBuffer toBuffer(Resource resource,boolean direct) throws IOException
     {
         int len=(int)resource.length();
@@ -928,7 +989,6 @@
     
     /* ------------------------------------------------------------ */
     /** Convert Buffer to string ID independent of content
-     * @param buffer
      */
     private static void idString(ByteBuffer buffer, StringBuilder out) 
     {
@@ -949,7 +1009,7 @@
     
     /* ------------------------------------------------------------ */
     /** Convert Buffer to string ID independent of content
-     * @param buffer
+     * @param buffer the buffet to generate a string ID from
      * @return A string showing the buffer ID
      */
     public static String toIDString(ByteBuffer buffer)
@@ -962,7 +1022,7 @@
     
     /* ------------------------------------------------------------ */
     /** Convert Buffer to a detail debug string of pointers and content
-     * @param buffer
+     * @param buffer the buffer to generate a detail string from
      * @return A string showing the pointers and content of the buffer
      */
     public static String toDetailString(ByteBuffer buffer)
@@ -991,38 +1051,46 @@
 
     private static void appendDebugString(StringBuilder buf,ByteBuffer buffer)
     {
-        for (int i = 0; i < buffer.position(); i++)
+        try
         {
-            appendContentChar(buf,buffer.get(i));
-            if (i == 16 && buffer.position() > 32)
+            for (int i = 0; i < buffer.position(); i++)
             {
-                buf.append("...");
-                i = buffer.position() - 16;
+                appendContentChar(buf,buffer.get(i));
+                if (i == 16 && buffer.position() > 32)
+                {
+                    buf.append("...");
+                    i = buffer.position() - 16;
+                }
             }
+            buf.append("<<<");
+            for (int i = buffer.position(); i < buffer.limit(); i++)
+            {
+                appendContentChar(buf,buffer.get(i));
+                if (i == buffer.position() + 16 && buffer.limit() > buffer.position() + 32)
+                {
+                    buf.append("...");
+                    i = buffer.limit() - 16;
+                }
+            }
+            buf.append(">>>");
+            int limit = buffer.limit();
+            buffer.limit(buffer.capacity());
+            for (int i = limit; i < buffer.capacity(); i++)
+            {
+                appendContentChar(buf,buffer.get(i));
+                if (i == limit + 16 && buffer.capacity() > limit + 32)
+                {
+                    buf.append("...");
+                    i = buffer.capacity() - 16;
+                }
+            }
+            buffer.limit(limit);
         }
-        buf.append("<<<");
-        for (int i = buffer.position(); i < buffer.limit(); i++)
+        catch(Throwable x)
         {
-            appendContentChar(buf,buffer.get(i));
-            if (i == buffer.position() + 16 && buffer.limit() > buffer.position() + 32)
-            {
-                buf.append("...");
-                i = buffer.limit() - 16;
-            }
+            Log.getRootLogger().ignore(x);
+            buf.append("!!concurrent mod!!");
         }
-        buf.append(">>>");
-        int limit = buffer.limit();
-        buffer.limit(buffer.capacity());
-        for (int i = limit; i < buffer.capacity(); i++)
-        {
-            appendContentChar(buf,buffer.get(i));
-            if (i == limit + 16 && buffer.capacity() > limit + 32)
-            {
-                buf.append("...");
-                i = buffer.capacity() - 16;
-            }
-        }
-        buffer.limit(limit);
     }
 
     private static void appendContentChar(StringBuilder buf, byte b)
@@ -1040,6 +1108,43 @@
         else
             buf.append("\\x").append(TypeUtil.toHexString(b));
     }
+    
+    /* ------------------------------------------------------------ */
+    /** Convert buffer to a Hex Summary String.
+     * @param buffer the buffer to generate a hex byte summary from
+     * @return A string showing a summary of the content in hex
+     */
+    public static String toHexSummary(ByteBuffer buffer)
+    {
+        if (buffer == null)
+            return "null";
+        StringBuilder buf = new StringBuilder();
+        
+        buf.append("b[").append(buffer.remaining()).append("]=");
+        for (int i = buffer.position(); i < buffer.limit(); i++)
+        {
+            TypeUtil.toHex(buffer.get(i),buf);
+            if (i == buffer.position() + 24 && buffer.limit() > buffer.position() + 32)
+            {
+                buf.append("...");
+                i = buffer.limit() - 8;
+            }
+        }
+        return buf.toString();
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Convert buffer to a Hex String.
+     * @param buffer the buffer to generate a hex byte summary from
+     * @return A hex string
+     */
+    public static String toHexString(ByteBuffer buffer)
+    {
+        if (buffer == null)
+            return "null";
+        return TypeUtil.toHexString(toArray(buffer));
+    }
+
 
     private final static int[] decDivisors =
             {1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
@@ -1084,4 +1189,5 @@
 
 
 
+
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Callback.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Callback.java
index 9cd55d3..64e6263 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/Callback.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Callback.java
@@ -27,37 +27,88 @@
 public interface Callback
 {
     /**
+     * Instance of Adapter that can be used when the callback methods need an empty
+     * implementation without incurring in the cost of allocating a new Adapter object.
+     */
+    Callback NOOP = new Callback()
+    {
+    };
+
+    /**
      * <p>Callback invoked when the operation completes.</p>
      *
      * @see #failed(Throwable)
      */
-    public abstract void succeeded();
+    default void succeeded()
+    {
+    }
 
     /**
      * <p>Callback invoked when the operation fails.</p>
      * @param x the reason for the operation failure
      */
-    public void failed(Throwable x);
+    default void failed(Throwable x)
+    {
+    }
 
     /**
-     * <p>Empty implementation of {@link Callback}</p>
+     * @return True if the callback is known to never block the caller
      */
-    public static class Adapter implements Callback
+    default boolean isNonBlocking()
     {
-        /**
-         * Instance of Adapter that can be used when the callback methods need an empty
-         * implementation without incurring in the cost of allocating a new Adapter object.
-         */
-        public static final Adapter INSTANCE = new Adapter();
+        return false;
+    }
+
+    /**
+     * Callback interface that declares itself as non-blocking
+     */
+    interface NonBlocking extends Callback
+    {
+        @Override
+        default boolean isNonBlocking()
+        {
+            return true;
+        }
+    }
+
+    class Nested implements Callback
+    {
+        private final Callback callback;
+
+        public Nested(Callback callback)
+        {
+            this.callback = callback;
+        }
+
+        public Nested(Nested nested)
+        {
+            this.callback = nested.callback;
+        }
 
         @Override
         public void succeeded()
         {
+            callback.succeeded();
         }
 
         @Override
         public void failed(Throwable x)
         {
+            callback.failed(x);
         }
+
+        @Override
+        public boolean isNonBlocking()
+        {
+            return callback.isNonBlocking();
+        }
+    }
+
+    /**
+     * <p>Empty implementation of {@link Callback}</p>
+     */
+    @Deprecated
+    class Adapter implements Callback
+    {
     }
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/CompletableCallback.java b/jetty-util/src/main/java/org/eclipse/jetty/util/CompletableCallback.java
index 2693dff..b89f711 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/CompletableCallback.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/CompletableCallback.java
@@ -21,16 +21,18 @@
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
+ * <p>
  * A callback to be used by driver code that needs to know whether the callback has been
  * succeeded or failed (that is, completed) just after the asynchronous operation or not,
  * typically because further processing depends on the callback being completed.
  * The driver code competes with the asynchronous operation to complete the callback.
- * <p />
+ * </p>
+ * <p>
  * If the callback is already completed, the driver code continues the processing,
  * otherwise it suspends it. If it is suspended, the callback will be completed some time
  * later, and {@link #resume()} or {@link #abort(Throwable)} will be called to allow the
  * application to resume the processing.
- * <p />
+ * </p>
  * Typical usage:
  * <pre>
  * CompletableCallback callback = new CompletableCallback()
@@ -132,6 +134,7 @@
 
     /**
      * Callback method invoked when this callback is failed.
+     * @param failure the throwable reprsenting the callback failure
      */
     public abstract void abort(Throwable failure);
 
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ConcurrentArrayQueue.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ConcurrentArrayQueue.java
index 34c2734..9dd540b 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ConcurrentArrayQueue.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ConcurrentArrayQueue.java
@@ -25,6 +25,8 @@
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.atomic.AtomicIntegerArray;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.atomic.AtomicReferenceArray;
@@ -32,14 +34,16 @@
 /**
  * A concurrent, unbounded implementation of {@link Queue} that uses singly-linked array blocks
  * to store elements.
- * <p/>
+ * <p>
  * This class is a drop-in replacement for {@link ConcurrentLinkedQueue}, with similar performance
  * but producing less garbage because arrays are used to store elements rather than nodes.
- * <p/>
+ * </p>
+ * <p>
  * The algorithm used is a variation of the algorithm from Gidenstam, Sundell and Tsigas
  * (http://www.adm.hb.se/~AGD/Presentations/CacheAwareQueue_OPODIS.pdf).
+ * </p>
  *
- * @param <T>
+ * @param <T> the Array entry type
  */
 public class ConcurrentArrayQueue<T> extends AbstractQueue<T>
 {
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ConstantThrowable.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ConstantThrowable.java
index 53cce8f..2ef870a 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ConstantThrowable.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ConstantThrowable.java
@@ -22,22 +22,21 @@
  * A {@link Throwable} that may be used in static contexts. It uses Java 7
  * constructor that prevents setting stackTrace inside exception object.
  */
-public class ConstantThrowable extends Throwable {
-
-    private String name;
-
-    public ConstantThrowable() {
+public class ConstantThrowable extends Throwable
+{
+    public ConstantThrowable()
+    {
         this(null);
     }
 
-    public ConstantThrowable(String name) {
-        super(null, null, false, false);
-        this.name = name;
+    public ConstantThrowable(String name)
+    {
+        super(name, null, false, false);
     }
 
     @Override
-    public String toString() {
-        return name;
+    public String toString()
+    {
+        return String.valueOf(getMessage());
     }
-
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/CountingCallback.java b/jetty-util/src/main/java/org/eclipse/jetty/util/CountingCallback.java
index 7a9899e..f6538d7 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/CountingCallback.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/CountingCallback.java
@@ -38,14 +38,13 @@
  * }
  * </pre>
  */
-public class CountingCallback implements Callback
+public class CountingCallback extends Callback.Nested
 {
-    private final Callback callback;
     private final AtomicInteger count;
 
     public CountingCallback(Callback callback, int count)
     {
-        this.callback = callback;
+        super(callback);
         this.count = new AtomicInteger(count);
     }
 
@@ -64,7 +63,7 @@
             if (count.compareAndSet(current, current - 1))
             {
                 if (current == 1)
-                    callback.succeeded();
+                    super.succeeded();
                 return;
             }
         }
@@ -84,7 +83,7 @@
 
             if (count.compareAndSet(current, 0))
             {
-                callback.failed(failure);
+                super.failed(failure);
                 return;
             }
         }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/DateCache.java b/jetty-util/src/main/java/org/eclipse/jetty/util/DateCache.java
index 3ce5a06..ebc6f08 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/DateCache.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/DateCache.java
@@ -23,7 +23,6 @@
 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 second
@@ -37,9 +36,7 @@
  *
  * If consecutive calls are frequently very different, then this
  * may be a little slower than a normal DateFormat.
- *
  */
-
 public class DateCache
 {
     public static final String DEFAULT_FORMAT="EEE MMM dd HH:mm:ss zzz yyyy";
@@ -77,6 +74,7 @@
     /* ------------------------------------------------------------ */
     /** Constructor.
      * Make a DateCache that will use the given format
+     * @param format the format to use
      */
     public DateCache(String format)
     {
@@ -161,7 +159,7 @@
 
     /* ------------------------------------------------------------ */
     /** Format a date according to our stored formatter.
-     * @param inDate 
+     * @param inDate the Date
      * @return Formatted date
      */
     public String format(Date inDate)
@@ -187,7 +185,7 @@
     /** Format a date according to our stored formatter.
      * If it happens to be in the same second as the last formatNow
      * call, then the format is reused.
-     * @param inDate 
+     * @param inDate the date in milliseconds since unix epoch 
      * @return Formatted date
      */
     public String format(long inDate)
@@ -215,7 +213,7 @@
      * The passed time is expected to be close to the current time, so it is 
      * compared to the last value passed and if it is within the same second,
      * the format is reused.  Otherwise a new cached format is created.
-     * @param now 
+     * @param now the milliseconds since unix epoch 
      * @return Formatted date
      */
     public String formatNow(long now)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/DecoratedObjectFactory.java b/jetty-util/src/main/java/org/eclipse/jetty/util/DecoratedObjectFactory.java
new file mode 100644
index 0000000..084f1a9
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/DecoratedObjectFactory.java
@@ -0,0 +1,119 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.Iterator;
+import java.util.List;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * An ObjectFactory enhanced by {@link Decorator} instances.
+ * <p>
+ * Consistent single location for all Decorator behavior, with equal behavior in a ServletContext and also for a stand
+ * alone client.
+ * <p>
+ * Used by ServletContextHandler, WebAppContext, WebSocketServerFactory, and WebSocketClient.
+ * <p>
+ * Can be found in the ServletContext Attributes at the {@link #ATTR DecoratedObjectFactory.ATTR} key.
+ */
+public class DecoratedObjectFactory implements Iterable<Decorator>
+{
+    private static final Logger LOG = Log.getLogger(DecoratedObjectFactory.class);
+
+    /**
+     * ServletContext attribute for the active DecoratedObjectFactory
+     */
+    public static final String ATTR = DecoratedObjectFactory.class.getName();
+
+    private List<Decorator> decorators = new ArrayList<>();
+
+    public void addDecorator(Decorator decorator)
+    {
+        LOG.debug("Adding Decorator: {}", decorator);
+        this.decorators.add(decorator);
+    }
+
+    public void clear()
+    {
+        this.decorators.clear();
+    }
+
+    public <T> T createInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("Creating Instance: " + clazz);
+        }
+        T o = clazz.newInstance();
+        return decorate(o);
+    }
+
+    public <T> T decorate(T obj)
+    {
+        T f = obj;
+        // Decorate is always backwards
+        for (int i = decorators.size() - 1; i >= 0; i--)
+        {
+            f = decorators.get(i).decorate(f);
+        }
+        return f;
+    }
+
+    public void destroy(Object obj)
+    {
+        for (Decorator decorator : this.decorators)
+        {
+            decorator.destroy(obj);
+        }
+    }
+
+    public List<Decorator> getDecorators()
+    {
+        return Collections.unmodifiableList(decorators);
+    }
+
+    @Override
+    public Iterator<Decorator> iterator()
+    {
+        return this.decorators.iterator();
+    }
+
+    public void setDecorators(List<? extends Decorator> decorators)
+    {
+        this.decorators.clear();
+        if (decorators != null)
+        {
+            this.decorators.addAll(decorators);
+        }
+    }
+    
+    @Override
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        str.append(this.getClass().getName()).append("[decorators=");
+        str.append(Integer.toString(decorators.size()));
+        str.append("]");
+        return str.toString();
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Decorator.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Decorator.java
new file mode 100644
index 0000000..0396cee
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Decorator.java
@@ -0,0 +1,35 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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;
+
+/**
+ * Interface for 3rd party libraries to decorate recently created objects in Jetty.
+ * <p>
+ * Most common use is weld/CDI.
+ * <p>
+ * This was moved from org.eclipse.jetty.servlet.ServletContextHandler to allow
+ * client applications to also use Weld/CDI to decorate objects.  
+ * Such as websocket client (which has no servlet api requirement)
+ */
+public interface Decorator
+{
+    <T> T decorate(T o);
+
+    void destroy(Object o);
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/DeprecationWarning.java b/jetty-util/src/main/java/org/eclipse/jetty/util/DeprecationWarning.java
new file mode 100644
index 0000000..a7b0063
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/DeprecationWarning.java
@@ -0,0 +1,86 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class DeprecationWarning implements Decorator
+{
+    private static final Logger LOG = Log.getLogger(DeprecationWarning.class);
+
+    @Override
+    public <T> T decorate(T o)
+    {
+        if (o == null)
+        {
+            return null;
+        }
+
+        Class<?> clazz = o.getClass();
+
+        try
+        {
+            Deprecated depr = clazz.getAnnotation(Deprecated.class);
+            if (depr != null)
+            {
+                LOG.warn("Using @Deprecated Class {}",clazz.getName());
+            }
+        }
+        catch (Throwable t)
+        {
+            LOG.ignore(t);
+        }
+
+        verifyIndirectTypes(clazz.getSuperclass(),clazz,"Class");
+        for (Class<?> ifaceClazz : clazz.getInterfaces())
+        {
+            verifyIndirectTypes(ifaceClazz,clazz,"Interface");
+        }
+
+        return o;
+    }
+
+    private void verifyIndirectTypes(Class<?> superClazz, Class<?> clazz, String typeName)
+    {
+        try
+        {
+            // Report on super class deprecation too
+            while (superClazz != null && superClazz != Object.class)
+            {
+                Deprecated supDepr = superClazz.getAnnotation(Deprecated.class);
+                if (supDepr != null)
+                {
+                    LOG.warn("Using indirect @Deprecated {} {} - (seen from {})",typeName,superClazz.getName(),clazz);
+                }
+
+                superClazz = superClazz.getSuperclass();
+            }
+        }
+        catch (Throwable t)
+        {
+            LOG.ignore(t);
+        }
+    }
+
+    @Override
+    public void destroy(Object o)
+    {
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/HostMap.java b/jetty-util/src/main/java/org/eclipse/jetty/util/HostMap.java
index 900cf5f..cfb8c41 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/HostMap.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/HostMap.java
@@ -23,8 +23,8 @@
 import java.util.HashSet;
 import java.util.Map;
 
-/* ------------------------------------------------------------ */
 /**
+ * @param <TYPE> the element type
  */
 @SuppressWarnings("serial")
 public class HostMap<TYPE> extends HashMap<String, TYPE>
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/HostPort.java b/jetty-util/src/main/java/org/eclipse/jetty/util/HostPort.java
index 099b919..c1db363 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/HostPort.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/HostPort.java
@@ -30,11 +30,16 @@
 
     public HostPort(String authority) throws IllegalArgumentException
     {
-        if (authority==null || authority.length()==0)
+        if (authority==null)
             throw new IllegalArgumentException("No Authority");
         try
         {
-            if (authority.charAt(0)=='[')
+            if (authority.isEmpty())
+            {
+                _host=authority;
+                _port=0;
+            }
+            else if (authority.charAt(0)=='[')
             {
                 // ipv6reference
                 int close=authority.lastIndexOf(']');
@@ -78,7 +83,7 @@
                 {initCause(ex);}
             };
         }
-        if(_host.isEmpty())
+        if(_host==null)
             throw new IllegalArgumentException("Bad host");
         if(_port<0)
             throw new IllegalArgumentException("Bad port");
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/IO.java b/jetty-util/src/main/java/org/eclipse/jetty/util/IO.java
index 562c3ed..65bcf44 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/IO.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/IO.java
@@ -17,7 +17,9 @@
 //
 
 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;
@@ -30,7 +32,6 @@
 import java.io.StringWriter;
 import java.io.Writer;
 import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
 import java.nio.channels.GatheringByteChannel;
 import java.nio.charset.Charset;
 
@@ -42,10 +43,10 @@
  * Provides stream handling utilities in
  * singleton Threadpool implementation accessed by static members.
  */
-public class IO 
+public class IO
 {
     private static final Logger LOG = Log.getLogger(IO.class);
-    
+
     /* ------------------------------------------------------------------- */
     public final static String
         CRLF      = "\015\012";
@@ -79,9 +80,9 @@
             this.read=read;
             this.write=write;
         }
-        
+
         /* ------------------------------------------------------------ */
-        /* 
+        /*
          * @see java.lang.Runnable#run()
          */
         public void run()
@@ -108,46 +109,56 @@
             }
         }
     }
-    
+
     /* ------------------------------------------------------------------- */
     /** Copy Stream in to Stream out until EOF or exception.
+     * @param in the input stream to read from (until EOF)
+     * @param out the output stream to write to
+     * @throws IOException if unable to copy streams
      */
     public static void copy(InputStream in, OutputStream out)
          throws IOException
     {
         copy(in,out,-1);
     }
-    
+
     /* ------------------------------------------------------------------- */
     /** Copy Reader to Writer out until EOF or exception.
+     * @param in the read to read from (until EOF)
+     * @param out the writer to write to
+     * @throws IOException if unable to copy the streams
      */
     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.
+     * @param in the stream to read from
+     * @param out the stream to write to
+     * @param byteCount the number of bytes to copy
+     * @throws IOException if unable to copy the streams
      */
     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);
             }
@@ -162,19 +173,23 @@
                 out.write(buffer,0,len);
             }
         }
-    }  
-    
+    }
+
     /* ------------------------------------------------------------------- */
     /** Copy Reader to Writer for byteCount bytes or until EOF or exception.
+     * @param in the Reader to read from
+     * @param out the Writer to write to
+     * @param byteCount the number of bytes to copy
+     * @throws IOException if unable to copy streams
      */
     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)
@@ -182,11 +197,11 @@
                 if (byteCount<bufferSize)
                     len=in.read(buffer,0,(int)byteCount);
                 else
-                    len=in.read(buffer,0,bufferSize);                   
-                
+                    len=in.read(buffer,0,bufferSize);
+
                 if (len==-1)
                     break;
-                
+
                 byteCount -= len;
                 out.write(buffer,0,len);
             }
@@ -216,9 +231,9 @@
 
     /* ------------------------------------------------------------ */
     /** Copy files or directories
-     * @param from
-     * @param to
-     * @throws IOException
+     * @param from the file to copy
+     * @param to the destination to copy to
+     * @throws IOException if unable to copy
      */
     public static void copy(File from,File to) throws IOException
     {
@@ -238,7 +253,7 @@
         }
         else
             to.mkdirs();
-        
+
         File[] files = from.listFiles();
         if (files!=null)
         {
@@ -251,7 +266,7 @@
             }
         }
     }
-    
+
     /* ------------------------------------------------------------ */
     public static void copyFile(File from,File to) throws IOException
     {
@@ -261,18 +276,25 @@
             copy(in,out);
         }
     }
-    
+
     /* ------------------------------------------------------------ */
     /** Read input stream to string.
+     * @param in the stream to read from (until EOF)
+     * @return the String parsed from stream (default Charset)
+     * @throws IOException if unable to read the stream (or handle the charset)
      */
     public static String toString(InputStream in)
         throws IOException
     {
         return toString(in,(Charset)null);
     }
-    
+
     /* ------------------------------------------------------------ */
     /** Read input stream to string.
+     * @param in the stream to read from (until EOF)
+     * @param encoding the encoding to use (can be null to use default Charset)
+     * @return the String parsed from the stream
+     * @throws IOException if unable to read the stream (or handle the charset)
      */
     public static String toString(InputStream in,String encoding)
         throws IOException
@@ -281,6 +303,10 @@
     }
 
     /** Read input stream to string.
+     * @param in the stream to read from (until EOF)
+     * @param encoding the Charset to use (can be null to use default Charset)
+     * @return the String parsed from the stream
+     * @throws IOException if unable to read the stream (or handle the charset)
      */
     public static String toString(InputStream in, Charset encoding)
             throws IOException
@@ -294,6 +320,9 @@
 
     /* ------------------------------------------------------------ */
     /** Read input stream to string.
+     * @param in the reader to read from (until EOF)
+     * @return the String parsed from the reader
+     * @throws IOException if unable to read the stream (or handle the charset)
      */
     public static String toString(Reader in)
         throws IOException
@@ -307,7 +336,8 @@
     /* ------------------------------------------------------------ */
     /** Delete File.
      * This delete will recursively delete directories - BE CAREFULL
-     * @param file The file to be deleted.
+     * @param file The file (or directory) to be deleted.
+     * @return true if anything was deleted. (note: this does not mean that all content in a directory was deleted)
      */
     public static boolean delete(File file)
     {
@@ -322,7 +352,24 @@
         return file.delete();
     }
 
-    /* ------------------------------------------------------------ */
+    /**
+     * Closes an arbitrary closable, and logs exceptions at ignore level
+     *
+     * @param closeable the closeable to close
+     */
+    public static void close(Closeable closeable)
+    {
+        try
+        {
+            if (closeable != null)
+                closeable.close();
+        }
+        catch (IOException ignore)
+        {
+            LOG.ignore(ignore);
+        }
+    }
+
     /**
      * closes an input stream, and logs exceptions
      *
@@ -330,52 +377,39 @@
      */
     public static void close(InputStream is)
     {
-        try
-        {
-            if (is != null)
-                is.close();
-        }
-        catch (IOException e)
-        {
-            LOG.ignore(e);
-        }
+        close((Closeable)is);
+    }
+
+    /**
+     * closes an output stream, and logs exceptions
+     *
+     * @param os the output stream to close
+     */
+    public static void close(OutputStream os)
+    {
+        close((Closeable)os);
     }
 
     /**
      * 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);
-        }
+        close((Closeable)reader);
     }
 
     /**
      * 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);
-        }
+        close((Closeable)writer);
     }
-    
+
     /* ------------------------------------------------------------ */
     public static byte[] readBytes(InputStream in)
         throws IOException
@@ -388,15 +422,22 @@
     /* ------------------------------------------------------------ */
     /**
      * A gathering write utility wrapper.
-     * <p>This method wraps a gather write with a loop that handles the limitations of some operating systems that
-     * have a limit on the number of buffers written.  The method loops on the write until either all the content
-     * is written or no progress is made.
-     * @param out The GatheringgByteChannel to write to
-     * @param buffers The buffers to write
-     * @param offset The offset into the buffers array
-     * @param length The length in buffers to write
+     * <p>
+     * This method wraps a gather write with a loop that handles the limitations of some operating systems that have a
+     * limit on the number of buffers written. The method loops on the write until either all the content is written or
+     * no progress is made.
+     *
+     * @param out
+     *            The GatheringByteChannel to write to
+     * @param buffers
+     *            The buffers to write
+     * @param offset
+     *            The offset into the buffers array
+     * @param length
+     *            The length in buffers to write
      * @return The total bytes written
      * @throws IOException
+     *             if unable write to the GatheringByteChannel
      */
     public static long write(GatheringByteChannel out, ByteBuffer[] buffers, int offset, int length) throws IOException
     {
@@ -405,14 +446,14 @@
         {
             // Write as much as we can
             long wrote=out.write(buffers,offset,length);
-            
+
             // If we can't write any more, give up
             if (wrote==0)
                 break;
-            
+
             // count the total
             total+=wrote;
-            
+
             // Look for unwritten content
             for (int i=offset;i<buffers.length;i++)
             {
@@ -426,32 +467,12 @@
             }
             length=0;
         }
-        
+
         return total;
     }
-    
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * 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()
@@ -460,17 +481,17 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** 
+    /**
      * @return An outputstream to nowhere
      */
     public static InputStream getClosedStream()
     {
         return __closedStream;
     }
-    
+
     /* ------------------------------------------------------------ */
     /* ------------------------------------------------------------ */
-    private static class NullOS extends OutputStream                                    
+    private static class NullOS extends OutputStream
     {
         @Override
         public void close(){}
@@ -485,10 +506,10 @@
     }
     private static NullOS __nullStream = new NullOS();
 
-    
+
     /* ------------------------------------------------------------ */
     /* ------------------------------------------------------------ */
-    private static class ClosedIS extends InputStream                                    
+    private static class ClosedIS extends InputStream
     {
         @Override
         public int read() throws IOException
@@ -497,28 +518,28 @@
         }
     }
     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                                    
+    private static class NullWrite extends Writer
     {
         @Override
         public void close(){}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/IPAddressMap.java b/jetty-util/src/main/java/org/eclipse/jetty/util/IPAddressMap.java
index 060aafd..a6df219 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/IPAddressMap.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/IPAddressMap.java
@@ -32,14 +32,15 @@
  * 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
+ *     nnn  - an absolute value (0-255)
+ * mmm-nnn  - an inclusive range of absolute values, 
+ *            with following shorthand notations:
+ *              nnn- =&gt; nnn-255
+ *             -nnn  =&gt; 0-nnn
+ *             -     =&gt; 0-255
+ *          a,b,...  - a list of wildcard specifications
  * </pre>
+ * @param <TYPE> the Map Entry value type
  * @deprecated
  */
 @SuppressWarnings("serial")
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExclude.java b/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExclude.java
index 3f60b39..e8ca8e8 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExclude.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExclude.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.util;
 
 import java.util.Set;
+import java.util.function.Predicate;
 
 
 /** Utility class to maintain a set of inclusions and exclusions.
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExcludeSet.java b/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExcludeSet.java
index 6c3af70..ef7375f 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExcludeSet.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExcludeSet.java
@@ -19,7 +19,9 @@
 package org.eclipse.jetty.util;
 
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.Set;
+import java.util.function.Predicate;
 
 
 /** Utility class to maintain a set of inclusions and exclusions.
@@ -64,7 +66,7 @@
     }
     
     /**
-     * Construct an IncludeExclude
+     * Construct an IncludeExclude.
      * @param setClass The type of {@link Set} to using internally to hold patterns. Two instances will be created.
      * one for include patterns and one for exclude patters.  If the class is also a {@link Predicate},
      * then it is also used as the item test for the set, otherwise a {@link SetContainsPredicate} instance
@@ -113,6 +115,11 @@
      */
     public <SET extends Set<P>> IncludeExcludeSet(Set<P> includeSet, Predicate<T> includePredicate, Set<P> excludeSet, Predicate<T> excludePredicate)
     {
+        Objects.requireNonNull(includeSet,"Include Set");
+        Objects.requireNonNull(includePredicate,"Include Predicate");
+        Objects.requireNonNull(excludeSet,"Exclude Set");
+        Objects.requireNonNull(excludePredicate,"Exclude Predicate");
+        
         _includes = includeSet;
         _includePredicate = includePredicate;
         _excludes = excludeSet;
@@ -179,4 +186,9 @@
     {
         return String.format("%s@%x{i=%s,ip=%s,e=%s,ep=%s}",this.getClass().getSimpleName(),hashCode(),_includes,_includePredicate,_excludes,_excludePredicate);
     }
+
+    public boolean isEmpty()
+    {
+        return _includes.isEmpty() && _excludes.isEmpty();
+    }
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/InetAddressSet.java b/jetty-util/src/main/java/org/eclipse/jetty/util/InetAddressSet.java
index f9cd674..0c61687 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/InetAddressSet.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/InetAddressSet.java
@@ -24,6 +24,7 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Predicate;
 
 /**
  * A set of InetAddress patterns.
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/IntrospectionUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/IntrospectionUtil.java
index 2d503ff..5aeb9a2 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/IntrospectionUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/IntrospectionUtil.java
@@ -44,7 +44,7 @@
         if (!method.getName().startsWith("set"))
             return false;
         
-        if (method.getParameterTypes().length != 1)
+        if (method.getParameterCount() != 1)
             return false;
         
         return true;
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/IteratingCallback.java b/jetty-util/src/main/java/org/eclipse/jetty/util/IteratingCallback.java
index 1a43826..1171867 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/IteratingCallback.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/IteratingCallback.java
@@ -19,35 +19,40 @@
 package org.eclipse.jetty.util;
 
 import java.nio.channels.ClosedChannelException;
-import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.util.thread.Locker;
 
 /**
  * This specialized callback implements a pattern that allows
  * a large job to be broken into smaller tasks using iteration
  * rather than recursion.
- * <p/>
+ * <p>
  * A typical example is the write of a large content to a socket,
  * divided in chunks. Chunk C1 is written by thread T1, which
  * also invokes the callback, which writes chunk C2, which invokes
  * the callback again, which writes chunk C3, and so forth.
- * <p/>
+ * </p>
+ * <p>
  * The problem with the example is that if the callback thread
  * is the same that performs the I/O operation, then the process
  * is recursive and may result in a stack overflow.
  * To avoid the stack overflow, a thread dispatch must be performed,
  * causing context switching and cache misses, affecting performance.
- * <p/>
+ * </p>
+ * <p>
  * To avoid this issue, this callback uses an AtomicReference to
  * record whether success callback has been called during the processing
  * of a sub task, and if so then the processing iterates rather than
  * recurring.
- * <p/>
+ * </p>
+ * <p>
  * Subclasses must implement method {@link #process()} where the sub
  * task is executed and a suitable {@link IteratingCallback.Action} is
  * returned to this callback to indicate the overall progress of the job.
  * This callback is passed to the asynchronous execution of each sub
  * task and a call the {@link #succeeded()} on this callback represents
  * the completion of the sub task.
+ * </p>
  */
 public abstract class IteratingCallback implements Callback
 {
@@ -67,37 +72,32 @@
          * and set iterating to true.
          */
         PROCESSING,
-        
+
         /**
          * Waiting for a schedule callback
          */
         PENDING,
-        
+
         /**
          * Called by a schedule callback
          */
         CALLED,
-        
+
         /**
-         * The overall job has succeeded as indicated by a {@link Action#SUCCEEDED} return 
+         * The overall job has succeeded as indicated by a {@link Action#SUCCEEDED} return
          * from {@link IteratingCallback#process()}
          */
         SUCCEEDED,
-        
+
         /**
          * The overall job has failed as indicated by a call to {@link IteratingCallback#failed(Throwable)}
          */
         FAILED,
-        
+
         /**
          * This callback has been closed and cannot be reset.
-         */ 
-        CLOSED,
-        
-        /**
-         * State is locked while leaving processing state to check the iterate boolean
          */
-        LOCKED
+        CLOSED
     }
 
     /**
@@ -118,32 +118,34 @@
          * may have not yet been invoked.
          */
         SCHEDULED,
-        
+
         /**
          * Indicates that {@link #process()} has completed the overall job.
          */
         SUCCEEDED
     }
 
-    private final AtomicReference<State> _state;
+    private Locker _locker = new Locker();
+    private State _state;
     private boolean _iterate;
-    
-    
+
+
     protected IteratingCallback()
     {
-        _state = new AtomicReference<>(State.IDLE);
+        _state = State.IDLE;
     }
-    
+
     protected IteratingCallback(boolean needReset)
     {
-        _state = new AtomicReference<>(needReset ? State.SUCCEEDED : State.IDLE);
+        _state = needReset ? State.SUCCEEDED : State.IDLE;
     }
-    
+
     /**
      * Method called by {@link #iterate()} to process the sub task.
-     * <p/>
+     * <p>
      * Implementations must start the asynchronous execution of the sub task
      * (if any) and return an appropriate action:
+     * </p>
      * <ul>
      * <li>{@link Action#IDLE} when no sub tasks are available for execution
      * but the overall job is not completed yet</li>
@@ -152,17 +154,11 @@
      * <li>{@link Action#SUCCEEDED} when the overall job is completed</li>
      * </ul>
      *
-     * @throws Exception if the sub task processing throws
+     * @return the appropriate Action
+     *
+     * @throws Throwable if the sub task processing throws
      */
-    protected abstract Action process() throws Exception;
-
-    /**
-     * @deprecated Use {@link #onCompleteSuccess()} instead.
-     */
-    @Deprecated
-    protected void completed()
-    {
-    }
+    protected abstract Action process() throws Throwable;
 
     /**
      * Invoked when the overall task has completed successfully.
@@ -171,71 +167,70 @@
      */
     protected void onCompleteSuccess()
     {
-        completed();
     }
-    
+
     /**
      * Invoked when the overall task has completed with a failure.
+     * @param cause the throwable to indicate cause of failure
      *
      * @see #onCompleteSuccess()
      */
-    protected void onCompleteFailure(Throwable x)
+    protected void onCompleteFailure(Throwable cause)
     {
     }
 
     /**
      * This method must be invoked by applications to start the processing
-     * of sub tasks.  It can be called at any time by any thread, and it's 
+     * of sub tasks.  It can be called at any time by any thread, and it's
      * contract is that when called, then the {@link #process()} method will
-     * be called during or soon after, either by the calling thread or by 
+     * be called during or soon after, either by the calling thread or by
      * another thread.
      */
     public void iterate()
     {
+        boolean process=false;
+
         loop: while (true)
         {
-            State state=_state.get();
-            switch (state)
+            try (Locker.Lock lock = _locker.lock())
             {
-                case PENDING:
-                case CALLED:
-                    // process will be called when callback is handled
-                    break loop;
-                    
-                case IDLE:
-                    if (!_state.compareAndSet(state,State.PROCESSING))
-                        continue;
-                    processing();
-                    break loop;
-                    
-                case PROCESSING:
-                    if (!_state.compareAndSet(state,State.LOCKED))
-                        continue;
-                    // Tell the thread that is processing that it must iterate again
-                    _iterate=true;
-                    _state.set(State.PROCESSING);
-                    break loop;
-                    
-                case LOCKED:
-                    Thread.yield();
-                    continue loop;
+                switch (_state)
+                {
+                    case PENDING:
+                    case CALLED:
+                        // process will be called when callback is handled
+                        break loop;
 
-                case FAILED:
-                case SUCCEEDED:
-                    break loop;
+                    case IDLE:
+                        _state=State.PROCESSING;
+                        process=true;
+                        break loop;
 
-                case CLOSED:
-                default:
-                    throw new IllegalStateException("state="+state);
+                    case PROCESSING:
+                        _iterate=true;
+                        break loop;
+
+                    case FAILED:
+                    case SUCCEEDED:
+                        break loop;
+
+                    case CLOSED:
+                    default:
+                        throw new IllegalStateException(toString());
+                }
             }
         }
+        if (process)
+            processing();
     }
 
-    private void processing() 
+    private void processing()
     {
         // This should only ever be called when in processing state, however a failed or close call
         // may happen concurrently, so state is not assumed.
-        
+
+        boolean on_complete_success=false;
+
         // While we are processing
         processing: while (true)
         {
@@ -251,13 +246,10 @@
                 break processing;
             }
 
-            // loop until we have successfully acted on the action we have just received
-            acting: while(true)
+            // acted on the action we have just received
+            try(Locker.Lock lock = _locker.lock())
             {
-                // action handling needs to know the state
-                State state=_state.get();
-                
-                switch (state)
+                switch (_state)
                 {
                     case PROCESSING:
                     {
@@ -265,67 +257,56 @@
                         {
                             case IDLE:
                             {
-                                // lock the state
-                                if (!_state.compareAndSet(state,State.LOCKED))
-                                    continue acting;
-
                                 // Has iterate been called while we were processing?
                                 if (_iterate)
                                 {
                                     // yes, so skip idle and keep processing
                                     _iterate=false;
-                                    _state.set(State.PROCESSING);
+                                    _state=State.PROCESSING;
                                     continue processing;
                                 }
 
                                 // No, so we can go idle
-                                _state.set(State.IDLE);
+                                _state=State.IDLE;
                                 break processing;
                             }
-                            
+
                             case SCHEDULED:
                             {
-                                if (!_state.compareAndSet(state, State.PENDING))
-                                    continue acting;
                                 // we won the race against the callback, so the callback has to process and we can break processing
+                                _state=State.PENDING;
                                 break processing;
                             }
-                            
+
                             case SUCCEEDED:
                             {
-                                if (!_state.compareAndSet(state, State.LOCKED))
-                                    continue acting;
+                                // we lost the race against the callback,
                                 _iterate=false;
-                                _state.set(State.SUCCEEDED);
-                                onCompleteSuccess();
+                                _state=State.SUCCEEDED;
+                                on_complete_success=true;
                                 break processing;
                             }
 
                             default:
-                                throw new IllegalStateException("state="+state+" action="+action); 
+                                throw new IllegalStateException(String.format("%s[action=%s]", this, action));
                         }
                     }
-                    
+
                     case CALLED:
                     {
                         switch (action)
                         {
                             case SCHEDULED:
                             {
-                                if (!_state.compareAndSet(state, State.PROCESSING))
-                                    continue acting;
                                 // we lost the race, so we have to keep processing
+                                _state=State.PROCESSING;
                                 continue processing;
                             }
 
                             default:
-                                throw new IllegalStateException("state="+state+" action="+action); 
+                                throw new IllegalStateException(String.format("%s[action=%s]", this, action));
                         }
                     }
-                        
-                    case LOCKED:
-                        Thread.yield();
-                        continue acting;
 
                     case SUCCEEDED:
                     case FAILED:
@@ -335,12 +316,15 @@
                     case IDLE:
                     case PENDING:
                     default:
-                        throw new IllegalStateException("state="+state+" action="+action); 
+                        throw new IllegalStateException(String.format("%s[action=%s]", this, action));
                 }
             }
         }
+
+        if (on_complete_success)
+            onCompleteSuccess();
     }
-    
+
     /**
      * Invoked when the sub task succeeds.
      * Subclasses that override this method must always remember to call
@@ -349,41 +333,36 @@
     @Override
     public void succeeded()
     {
-        loop: while (true)
+        boolean process=false;
+        try(Locker.Lock lock = _locker.lock())
         {
-            State state = _state.get();
-            switch (state)
+            switch (_state)
             {
                 case PROCESSING:
                 {
-                    if (!_state.compareAndSet(state, State.CALLED))
-                        continue loop;
-                    break loop;
+                    _state=State.CALLED;
+                    break;
                 }
                 case PENDING:
                 {
-                    if (!_state.compareAndSet(state, State.PROCESSING))
-                        continue loop;
-                    processing();
-                    break loop;
+                    _state=State.PROCESSING;
+                    process=true;
+                    break;
                 }
                 case CLOSED:
                 case FAILED:
                 {
                     // Too late!
-                    break loop;
+                    break;
                 }
-                case LOCKED:
-                {
-                    Thread.yield();
-                    continue loop;
-                }       
                 default:
                 {
-                    throw new IllegalStateException("state="+state);
+                    throw new IllegalStateException(toString());
                 }
             }
         }
+        if (process)
+            processing();
     }
 
     /**
@@ -394,73 +373,58 @@
     @Override
     public void failed(Throwable x)
     {
-        loop: while (true)
+        boolean failure=false;
+        try(Locker.Lock lock = _locker.lock())
         {
-            State state = _state.get();
-            switch (state)
+            switch (_state)
             {
                 case SUCCEEDED:
                 case FAILED:
                 case IDLE:
                 case CLOSED:
                 case CALLED:
-                {
                     // too late!.
-                    break loop;
-                }
-                case LOCKED:
-                {
-                    Thread.yield();
-                    continue loop;
-                }  
-                case PENDING: 
-                case PROCESSING: 
-                {
-                    if (!_state.compareAndSet(state, State.FAILED))
-                        continue loop;
+                    break;
 
-                    onCompleteFailure(x);
-                    break loop;
+                case PENDING:
+                case PROCESSING:
+                {
+                    _state=State.FAILED;
+                    failure=true;
+                    break;
                 }
                 default:
-                    throw new IllegalStateException("state="+state);
+                    throw new IllegalStateException(toString());
             }
         }
+        if (failure)
+            onCompleteFailure(x);
     }
 
     public void close()
     {
-        loop: while (true)
+        boolean failure=false;
+        try(Locker.Lock lock = _locker.lock())
         {
-            State state = _state.get();
-            switch (state)
+            switch (_state)
             {
                 case IDLE:
                 case SUCCEEDED:
                 case FAILED:
-                {
-                    if (!_state.compareAndSet(state, State.CLOSED))
-                        continue loop;
-                    break loop;
-                }
+                    _state=State.CLOSED;
+                    break;
+
                 case CLOSED:
-                {
-                    break loop;
-                }
-                case LOCKED:
-                {
-                    Thread.yield();
-                    continue loop;
-                }    
+                    break;
+
                 default:
-                {
-                    if (!_state.compareAndSet(state, State.CLOSED))
-                        continue loop;
-                    onCompleteFailure(new ClosedChannelException());
-                    break loop;
-                }
+                    _state=State.CLOSED;
+                    failure=true;
             }
         }
+
+        if(failure)
+            onCompleteFailure(new ClosedChannelException());
     }
 
     /*
@@ -469,20 +433,29 @@
      */
     boolean isIdle()
     {
-        return _state.get() == State.IDLE;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            return _state == State.IDLE;
+        }
     }
 
     public boolean isClosed()
     {
-        return _state.get() == State.CLOSED;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            return _state == State.CLOSED;
+        }
     }
-    
+
     /**
      * @return whether this callback has failed
      */
     public boolean isFailed()
     {
-        return _state.get() == State.FAILED;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            return _state == State.FAILED;
+        }
     }
 
     /**
@@ -490,51 +463,42 @@
      */
     public boolean isSucceeded()
     {
-        return _state.get() == State.SUCCEEDED;
+        try(Locker.Lock lock = _locker.lock())
+        {
+            return _state == State.SUCCEEDED;
+        }
     }
 
     /**
      * Resets this callback.
-     * <p/>
+     * <p>
      * A callback can only be reset to IDLE from the
      * SUCCEEDED or FAILED states or if it is already IDLE.
+     * </p>
      *
      * @return true if the reset was successful
      */
     public boolean reset()
     {
-        while (true)
+        try(Locker.Lock lock = _locker.lock())
         {
-            State state=_state.get();
-            switch(state)
+            switch(_state)
             {
                 case IDLE:
                     return true;
-                    
+
                 case SUCCEEDED:
-                    if (!_state.compareAndSet(state, State.LOCKED))
-                        continue;
-                    _iterate=false;
-                    _state.set(State.IDLE);
-                    return true;
-                    
                 case FAILED:
-                    if (!_state.compareAndSet(state, State.LOCKED))
-                        continue;
                     _iterate=false;
-                    _state.set(State.IDLE);
+                    _state=State.IDLE;
                     return true;
 
-                case LOCKED:
-                    Thread.yield();
-                    continue;
-                    
                 default:
                     return false;
             }
         }
     }
-    
+
     @Override
     public String toString()
     {
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/IteratingNestedCallback.java b/jetty-util/src/main/java/org/eclipse/jetty/util/IteratingNestedCallback.java
index cfe488e..230552c 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/IteratingNestedCallback.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/IteratingNestedCallback.java
@@ -46,6 +46,12 @@
     {
         _callback=callback;
     }
+
+    @Override
+    public boolean isNonBlocking()
+    { 
+        return _callback.isNonBlocking();
+    }
     
     @Override
     protected void onCompleteSuccess()
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Jetty.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Jetty.java
index f391a54..e1c6779 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/Jetty.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Jetty.java
@@ -21,6 +21,8 @@
 public class Jetty
 {
     public static final String VERSION;
+    public static final String POWERED_BY;
+    public static final boolean STABLE;
 
     static
     {
@@ -30,10 +32,16 @@
                 pkg.getImplementationVersion() != null)
             VERSION = pkg.getImplementationVersion();
         else
-            VERSION = System.getProperty("jetty.version", "9.2.z-SNAPSHOT");
+            VERSION = System.getProperty("jetty.version", "9.3.z-SNAPSHOT");
+
+        POWERED_BY="<a href=\"http://eclipse.org/jetty\">Powered by Jetty:// "+VERSION+"</a>";
+
+        // Show warning when RC# or M# is in version string
+        STABLE = !VERSION.matches("^.*\\.(RC|M)[0-9]+$");
     }
 
     private Jetty()
     {
     }
+    
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/LazyList.java b/jetty-util/src/main/java/org/eclipse/jetty/util/LazyList.java
index 3229622..8cf4da0 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/LazyList.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/LazyList.java
@@ -27,21 +27,23 @@
 import java.util.List;
 import java.util.ListIterator;
 
-/* ------------------------------------------------------------ */
-/** Lazy List creation.
+/** 
+ * Lazy List creation.
+ * <p>
  * 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>
  * <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>
+ * </p>
+ * <strong>Usage</strong>
  * <pre>
  *   Object lazylist =null;
  *   while(loopCondition)
@@ -162,7 +164,9 @@
 
     /* ------------------------------------------------------------ */
     /** Ensure the capacity of the underlying list.
-     * 
+     * @param list the list to grow 
+     * @param initialSize the size to grow to
+     * @return the new List with new size
      */
     public static Object ensureSize(Object list, int initialSize)
     {
@@ -230,6 +234,7 @@
      * @param list A LazyList returned from LazyList.add(Object)
      * @return The List of added items, which may be an EMPTY_LIST
      * or a SingletonList.
+     * @param <E> the list entry type
      */
     public static<E> List<E> getList(Object list)
     {
@@ -245,6 +250,7 @@
      * empty list.
      * @return The List of added items, which may be null, an EMPTY_LIST
      * or a SingletonList.
+     * @param <E> the list entry type
      */
     @SuppressWarnings("unchecked")
     public static<E> List<E> getList(Object list, boolean nullForEmpty)
@@ -365,6 +371,7 @@
      * @param list  A LazyList returned from LazyList.add(Object) or null
      * @param i int index
      * @return the item from the list.
+     * @param <E> the list entry type
      */
     @SuppressWarnings("unchecked")
     public static <E> E get(Object list, int i)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java
index 42579c7..67ff10c 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java
@@ -69,10 +69,10 @@
     /* ------------------------------------------------------------ */
     /** Load a class.
      * 
-     * @param loadClass
-     * @param name
+     * @param loadClass a similar class, belong in the same classloader of the desired class to load
+     * @param name the name of the new class to load, using the same ClassLoader as the <code>loadClass</code> 
      * @return Class
-     * @throws ClassNotFoundException
+     * @throws ClassNotFoundException if not able to find the class
      */
     @SuppressWarnings("rawtypes")
     public static Class loadClass(Class loadClass,String name)
@@ -153,7 +153,9 @@
      * above the given classloader.
      * 
      * This is primarily used for jasper.
+     * @param loader the classloader to use
      * @return the system class path
+     * @throws Exception if unable to generate the classpath from the resource references
      */
     public static String getClassPath(ClassLoader loader) throws Exception
     {
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MemoryUtils.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MemoryUtils.java
index 556d591..cfc49d5 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/MemoryUtils.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MemoryUtils.java
@@ -22,8 +22,7 @@
 import java.security.PrivilegedAction;
 
 /**
- * {@link MemoryUtils} provides an abstraction over memory properties and operations.
- * <p />
+ * MemoryUtils provides an abstraction over memory properties and operations.
  */
 public class MemoryUtils
 {
@@ -67,5 +66,4 @@
     {
         return getCacheLineBytes() >> 3;
     }
-
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiException.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiException.java
index 299c8f3..877437e 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiException.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiException.java
@@ -86,7 +86,7 @@
      * 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 
+     * @exception Exception the Error or Exception if nested is 1, or the MultiException itself if nested is more than 1.
      */
     public void ifExceptionThrow()
         throws Exception
@@ -146,6 +146,7 @@
      * If this multi exception is empty then no action is taken. If it
      * contains a any exceptions then this
      * multi exception is thrown. 
+     * @throws MultiException the multiexception if there are nested exception
      */
     public void ifExceptionThrowMulti()
         throws MultiException
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java
index 0ef2038..dfca2fa 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java
@@ -28,6 +28,7 @@
 
 /** 
  * A multi valued Map.
+ * @param <V> the entry type for multimap values
  */
 @SuppressWarnings("serial")
 public class MultiMap<V> extends HashMap<String,List<V>>
@@ -283,7 +284,7 @@
      * Test for a specific single value in the map.
      * <p>
      * NOTE: This is a SLOW operation, and is actively discouraged.
-     * @param value
+     * @param value the value to search for
      * @return true if contains simple value
      */
     public boolean containsSimpleValue(V value)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java
index 421432c..09e505a 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java
@@ -30,14 +30,18 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 
 import javax.servlet.MultipartConfigElement;
-import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
 import javax.servlet.http.Part;
 
 import org.eclipse.jetty.util.log.Log;
@@ -54,13 +58,16 @@
 {
     private static final Logger LOG = Log.getLogger(MultiPartInputStreamParser.class);
     public static final MultipartConfigElement  __DEFAULT_MULTIPART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
+    public static final MultiMap<Part> EMPTY_MAP = new MultiMap(Collections.emptyMap());
     protected InputStream _in;
     protected MultipartConfigElement _config;
     protected String _contentType;
     protected MultiMap<Part> _parts;
+    protected Exception _err;
     protected File _tmpDir;
     protected File _contextTmpDir;
     protected boolean _deleteOnExit;
+    protected boolean _writeFilesWithFilenames;
 
 
 
@@ -83,6 +90,11 @@
             _filename = filename;
         }
 
+        @Override
+        public String toString()
+        {
+            return String.format("Part{n=%s,fn=%s,ct=%s,s=%d,t=%b,f=%s}",_name,_filename,_contentType,_size,_temporary,_file);
+        }
         protected void setContentType (String contentType)
         {
             _contentType = contentType;
@@ -92,9 +104,19 @@
         protected void open()
         throws IOException
         {
-            //Write to a buffer in memory until we discover we've exceed the
-            //MultipartConfig fileSizeThreshold
-            _out = _bout= new ByteArrayOutputStream2();
+            //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 (isWriteFilesWithFilenames() && _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()
@@ -125,7 +147,7 @@
 
             if (MultiPartInputStreamParser.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStreamParser.this._config.getFileSizeThreshold() && _file==null)
                 createFile();
-           
+
             _out.write(bytes, offset, length);
             _size += length;
         }
@@ -134,7 +156,7 @@
         throws IOException
         {
             _file = File.createTempFile("MultiPart", "", MultiPartInputStreamParser.this._tmpDir);
-            
+
             if (_deleteOnExit)
                 _file.deleteOnExit();
             FileOutputStream fos = new FileOutputStream(_file);
@@ -173,7 +195,7 @@
         {
             if (name == null)
                 return null;
-            return (String)_headers.getValue(name.toLowerCase(Locale.ENGLISH), 0);
+            return _headers.getValue(name.toLowerCase(Locale.ENGLISH), 0);
         }
 
         /**
@@ -209,8 +231,8 @@
            }
         }
 
-        
-        /** 
+
+        /**
          * @see javax.servlet.http.Part#getSubmittedFileName()
          */
         @Override
@@ -239,7 +261,7 @@
          */
         public long getSize()
         {
-            return _size;         
+            return _size;
         }
 
         /**
@@ -250,7 +272,7 @@
             if (_file == null)
             {
                 _temporary = false;
-                
+
                 //part data is only in the ByteArrayOutputStream and never been written to disk
                 _file = new File (_tmpDir, fileName);
 
@@ -272,10 +294,11 @@
             {
                 //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;
+
+                Path src = _file.toPath();
+                Path target = src.resolveSibling(fileName);
+                Files.move(src, target, StandardCopyOption.REPLACE_EXISTING);
+                _file = target.toFile();
             }
         }
 
@@ -287,13 +310,13 @@
         public void delete() throws IOException
         {
             if (_file != null && _file.exists())
-                _file.delete();     
+                _file.delete();
         }
-        
+
         /**
          * Only remove tmp files.
-         * 
-         * @throws IOException
+         *
+         * @throws IOException if unable to delete the file
          */
         public void cleanUp() throws IOException
         {
@@ -303,7 +326,8 @@
 
 
         /**
-         * Get the file, if any, the data has been written to.
+         * Get the file
+         * @return the file, if any, the data has been written to.
          */
         public File getFile ()
         {
@@ -332,19 +356,29 @@
      */
     public MultiPartInputStreamParser (InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
     {
+        _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());
+        
+        if (in instanceof ServletInputStream)
+        {
+            if (((ServletInputStream)in).isFinished())
+            {
+                _parts = EMPTY_MAP;
+                return;
+            }
+        }
         _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 the parts that were parsed
      */
     public Collection<Part> getParsedParts()
     {
@@ -352,7 +386,7 @@
             return Collections.emptyList();
 
         Collection<List<Part>> values = _parts.values();
-        List<Part> parts = new ArrayList<Part>();
+        List<Part> parts = new ArrayList<>();
         for (List<Part> o: values)
         {
             List<Part> asList = LazyList.getList(o, false);
@@ -363,8 +397,8 @@
 
     /**
      * Delete any tmp storage for parts, and clear out the parts list.
-     * 
-     * @throws MultiException
+     *
+     * @throws MultiException if unable to delete the parts
      */
     public void deleteParts ()
     throws MultiException
@@ -376,30 +410,33 @@
             try
             {
                 ((MultiPartInputStreamParser.MultiPart)p).cleanUp();
-            } 
+            }
             catch(Exception e)
-            {     
-                err.add(e); 
+            {
+                err.add(e);
             }
         }
         _parts.clear();
-        
+
         err.ifExceptionThrowMulti();
     }
 
-   
+
     /**
      * Parse, if necessary, the multipart data and return the list of Parts.
-     * 
-     * @throws IOException
-     * @throws ServletException
+     *
+     * @return the parts
+     * @throws IOException if unable to get the parts
      */
     public Collection<Part> getParts()
-    throws IOException, ServletException
+    throws IOException
     {
         parse();
+        throwIfError();
+
+        
         Collection<List<Part>> values = _parts.values();
-        List<Part> parts = new ArrayList<Part>();
+        List<Part> parts = new ArrayList<>();
         for (List<Part> o: values)
         {
             List<Part> asList = LazyList.getList(o, false);
@@ -411,262 +448,303 @@
 
     /**
      * Get the named Part.
-     * 
-     * @param name
-     * @throws IOException
-     * @throws ServletException
+     *
+     * @param name the part name
+     * @return the parts
+     * @throws IOException if unable to get the part
      */
     public Part getPart(String name)
-    throws IOException, ServletException
+    throws IOException
     {
         parse();
-        return (Part)_parts.getValue(name, 0);
+        throwIfError();   
+        return _parts.getValue(name, 0);
     }
 
+    /**
+     * Throws an exception if one has been latched.
+     * 
+     * @throws IOException the exception (if present)
+     */
+    protected void throwIfError ()
+    throws IOException
+    {
+        if (_err != null)
+        {
+            if (_err instanceof IOException)
+                throw (IOException)_err;
+            if (_err instanceof IllegalStateException)
+                throw (IllegalStateException)_err;
+            throw new IllegalStateException(_err);
+        }
+    }
 
     /**
      * 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)
+        if (_parts != null || _err != 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<Part>();
+        _parts = new MultiMap<>();
 
         //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)).trim());
-        }
-        
-        String boundary="--"+contentTypeBoundary;
-        byte[] byteBoundary=(boundary+"--").getBytes(StandardCharsets.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());
-        }
+            //sort out the location to which to write the files
 
-        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;
-                
-                //end of 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);
-                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;
-                    }
-                };
-            }
+            if (_config.getLocation() == null)
+                _tmpDir = _contextTmpDir;
+            else if ("".equals(_config.getLocation()))
+                _tmpDir = _contextTmpDir;
             else
-                partInput = _in;
+            {
+                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)).trim());
+            }
+
+            String boundary="--"+contentTypeBoundary;
+            String lastBoundary=boundary+"--";
+            byte[] byteBoundary=lastBoundary.getBytes(StandardCharsets.ISO_8859_1);
+
+            // Get first boundary
+            String line = null;
             try
             {
-                int state=-2;
-                int c;
-                boolean cr=false;
-                boolean lf=false;
+                line=((ReadLineInputStream)_in).readLine();
+            }
+            catch (IOException e)
+            {
+                LOG.warn("Badly formatted multipart request");
+                throw e;
+            }
 
-                // loop for all lines
+            if (line == null)
+                throw new IOException("Missing content for multipart request");
+
+            boolean badFormatLogged = false;
+            line=line.trim();
+            while (line != null && !line.equals(boundary) && !line.equals(lastBoundary))
+            {
+                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");
+
+            // Empty multipart.
+            if (line.equals(lastBoundary))
+                return;
+
+            // Read each part
+            boolean lastPart=false;
+
+            outer:while(!lastPart)
+            {
+                String contentDisposition=null;
+                String contentType=null;
+                String contentTransferEncoding=null;
+
+                MultiMap<String> headers = new MultiMap<>();
                 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()+")");
+                    line=((ReadLineInputStream)_in).readLine();
 
-                        state=-2;
-                        
-                        // look for CR and/or LF
-                        if(c==13||c==10)
+                    //No more input
+                    if(line==null)
+                        break outer;
+
+                    //end of 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);
+                    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
                         {
-                            if(c==13)
+                            int c = in.read();
+                            if (c >= 0 && c == '=')
                             {
-                                partInput.mark(1);
-                                int tmp=partInput.read();
-                                if (tmp!=10)
-                                    partInput.reset();
-                                else
-                                    state=tmp;
+                                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);
                             }
-                            break;
+                            return c;
                         }
-                        
-                        // Look for boundary
-                        if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
+                    };
+                }
+                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)
                         {
-                            b++;
+                            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);
+                            }
                         }
-                        else
+
+                        // 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))
                         {
-                            // 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);
 
@@ -674,67 +752,67 @@
                                 part.write(10);
 
                             cr=lf=false;
-                            if(b>0)
-                                part.write(byteBoundary,0,b);
-
+                            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))
-                    {
+
+                        // 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=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;
+                        cr=(c==13);
+                        lf=(c==10||state==10);
                         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();
                 }
             }
-            finally
+            if (lastPart)
             {
-
-                part.close();
+                while(line!=null)
+                    line=((ReadLineInputStream)_in).readLine();
             }
+            else
+                throw new IOException("Incomplete parts");
         }
-        if (!lastPart)
-            throw new IOException("Incomplete parts");
+        catch (Exception e)
+        {
+            _err = e;
+        }
     }
-    
+
     public void setDeleteOnExit(boolean deleteOnExit)
     {
         _deleteOnExit = deleteOnExit;
     }
 
+    public void setWriteFilesWithFilenames (boolean writeFilesWithFilenames)
+    {
+        _writeFilesWithFilenames = writeFilesWithFilenames;
+    }
+    
+    public boolean isWriteFilesWithFilenames ()
+    {
+        return _writeFilesWithFilenames;
+    }
 
     public boolean isDeleteOnExit()
     {
@@ -749,8 +827,8 @@
         String value = nameEqualsValue.substring(idx+1).trim();
         return QuotedStringTokenizer.unquoteOnly(value);
     }
-    
-    
+
+
     /* ------------------------------------------------------------ */
     private String filenameValue(String nameEqualsValue)
     {
@@ -778,7 +856,7 @@
             return QuotedStringTokenizer.unquoteOnly(value, true);
     }
 
-    
+
 
     private static class Base64InputStream extends InputStream
     {
@@ -787,7 +865,7 @@
         byte[] _buffer;
         int _pos;
 
-    
+
         public Base64InputStream(ReadLineInputStream rlis)
         {
             _in = rlis;
@@ -802,7 +880,7 @@
                 //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(); 
+                _line = _in.readLine();
                 if (_line==null)
                     return -1;  //nothing left
                 if (_line.startsWith("--"))
@@ -820,7 +898,7 @@
 
                 _pos=0;
             }
-            
+
             return _buffer[_pos++];
         }
     }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java
index 7f4a2eb..7baa99c 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java
@@ -24,7 +24,6 @@
 import java.nio.charset.StandardCharsets;
 
 
-/* ================================================================ */
 /** Handle a multipart MIME response.
  *
  * 
@@ -100,6 +99,8 @@
     
     /* ------------------------------------------------------------ */
     /** Start creation of the next Content.
+     * @param contentType the content type of the part
+     * @throws IOException if unable to write the part
      */
     public void startPart(String contentType)
          throws IOException
@@ -118,6 +119,9 @@
         
     /* ------------------------------------------------------------ */
     /** Start creation of the next Content.
+     * @param contentType the content type of the part
+     * @param headers the part headers
+     * @throws IOException if unable to write the part
      */
     public void startPart(String contentType, String[] headers)
          throws IOException
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartWriter.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartWriter.java
index 78df80f..766959f 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartWriter.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartWriter.java
@@ -87,6 +87,8 @@
     
     /* ------------------------------------------------------------ */
     /** Start creation of the next Content.
+     * @param contentType the content type
+     * @throws IOException if unable to write the part
      */
     public void startPart(String contentType)
          throws IOException
@@ -105,6 +107,7 @@
     
     /* ------------------------------------------------------------ */
     /** end creation of the next Content.
+     * @throws IOException if unable to write the part
      */
     public void endPart()
          throws IOException
@@ -116,6 +119,9 @@
         
     /* ------------------------------------------------------------ */
     /** Start creation of the next Content.
+     * @param contentType the content type of the part
+     * @param headers the part headers
+     * @throws IOException if unable to write the part
      */
     public void startPart(String contentType, String[] headers)
          throws IOException
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/PathWatcher.java b/jetty-util/src/main/java/org/eclipse/jetty/util/PathWatcher.java
new file mode 100644
index 0000000..00d313a
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/PathWatcher.java
@@ -0,0 +1,1456 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.nio.file.ClosedWatchServiceException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchEvent.Kind;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Watch a Path (and sub directories) for Path changes.
+ * <p>
+ * Suitable replacement for the old {@link Scanner} implementation.
+ * <p>
+ * Allows for configured Excludes and Includes using {@link FileSystem#getPathMatcher(String)} syntax.
+ * <p>
+ * Reports activity via registered {@link Listener}s
+ */
+public class PathWatcher extends AbstractLifeCycle implements Runnable
+{
+    public static class Config
+    {
+        public static final int UNLIMITED_DEPTH = -9999;
+        
+        private static final String PATTERN_SEP;
+
+        static
+        {
+            String sep = File.separator;
+            if (File.separatorChar == '\\')
+            {
+                sep = "\\\\";
+            }
+            PATTERN_SEP = sep;
+        }
+
+        protected final Path dir;
+        protected int recurseDepth = 0; // 0 means no sub-directories are scanned
+        protected List<PathMatcher> includes;
+        protected List<PathMatcher> excludes;
+        protected boolean excludeHidden = false;
+
+        public Config(Path path)
+        {
+            this.dir = path;
+            includes = new ArrayList<>();
+            excludes = new ArrayList<>();
+        }
+
+        /**
+         * Add an exclude PathMatcher
+         *
+         * @param matcher
+         *            the path matcher for this exclude
+         */
+        public void addExclude(PathMatcher matcher)
+        {
+            this.excludes.add(matcher);
+        }
+
+        /**
+         * Add an exclude PathMatcher.
+         * <p>
+         * Note: this pattern is FileSystem specific (so use "/" for Linux and OSX, and "\\" for Windows)
+         *
+         * @param syntaxAndPattern
+         *            the PathMatcher syntax and pattern to use
+         * @see FileSystem#getPathMatcher(String) for detail on syntax and pattern
+         */
+        public void addExclude(final String syntaxAndPattern)
+        {
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("Adding exclude: [{}]",syntaxAndPattern);
+            }
+            addExclude(dir.getFileSystem().getPathMatcher(syntaxAndPattern));
+        }
+
+        /**
+         * Add a <code>glob:</code> syntax pattern exclude reference in a directory relative, os neutral, pattern.
+         *
+         * <pre>
+         *    On Linux:
+         *    Config config = new Config(Path("/home/user/example"));
+         *    config.addExcludeGlobRelative("*.war") =&gt; "glob:/home/user/example/*.war"
+         * 
+         *    On Windows
+         *    Config config = new Config(Path("D:/code/examples"));
+         *    config.addExcludeGlobRelative("*.war") =&gt; "glob:D:\\code\\examples\\*.war"
+         *
+         * </pre>
+         *
+         * @param pattern
+         *            the pattern, in unixy format, relative to config.dir
+         */
+        public void addExcludeGlobRelative(String pattern)
+        {
+            addExclude(toGlobPattern(dir,pattern));
+        }
+
+        /**
+         * Exclude hidden files and hidden directories
+         */
+        public void addExcludeHidden()
+        {
+            if (!excludeHidden)
+            {
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("Adding hidden files and directories to exclusions");
+                }
+                excludeHidden = true;
+
+                addExclude("regex:^.*" + PATTERN_SEP + "\\..*$"); // ignore hidden files
+                addExclude("regex:^.*" + PATTERN_SEP + "\\..*" + PATTERN_SEP + ".*$"); // ignore files in hidden directories
+            }
+        }
+
+        /**
+         * Add multiple exclude PathMatchers
+         *
+         * @param syntaxAndPatterns
+         *            the list of PathMatcher syntax and patterns to use
+         * @see FileSystem#getPathMatcher(String) for detail on syntax and pattern
+         */
+        public void addExcludes(List<String> syntaxAndPatterns)
+        {
+            for (String syntaxAndPattern : syntaxAndPatterns)
+            {
+                addExclude(syntaxAndPattern);
+            }
+        }
+
+        /**
+         * Add an include PathMatcher
+         *
+         * @param matcher
+         *            the path matcher for this include
+         */
+        public void addInclude(PathMatcher matcher)
+        {
+            this.includes.add(matcher);
+        }
+
+        /**
+         * Add an include PathMatcher
+         *
+         * @param syntaxAndPattern
+         *            the PathMatcher syntax and pattern to use
+         * @see FileSystem#getPathMatcher(String) for detail on syntax and pattern
+         */
+        public void addInclude(String syntaxAndPattern)
+        {
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("Adding include: [{}]",syntaxAndPattern);
+            }
+            addInclude(dir.getFileSystem().getPathMatcher(syntaxAndPattern));
+        }
+
+        /**
+         * Add a <code>glob:</code> syntax pattern reference in a directory relative, os neutral, pattern.
+         *
+         * <pre>
+         *    On Linux:
+         *    Config config = new Config(Path("/home/user/example"));
+         *    config.addIncludeGlobRelative("*.war") =&gt; "glob:/home/user/example/*.war"
+         * 
+         *    On Windows
+         *    Config config = new Config(Path("D:/code/examples"));
+         *    config.addIncludeGlobRelative("*.war") =&gt; "glob:D:\\code\\examples\\*.war"
+         *
+         * </pre>
+         *
+         * @param pattern
+         *            the pattern, in unixy format, relative to config.dir
+         */
+        public void addIncludeGlobRelative(String pattern)
+        {
+            addInclude(toGlobPattern(dir,pattern));
+        }
+
+        /**
+         * Add multiple include PathMatchers
+         *
+         * @param syntaxAndPatterns
+         *            the list of PathMatcher syntax and patterns to use
+         * @see FileSystem#getPathMatcher(String) for detail on syntax and pattern
+         */
+        public void addIncludes(List<String> syntaxAndPatterns)
+        {
+            for (String syntaxAndPattern : syntaxAndPatterns)
+            {
+                addInclude(syntaxAndPattern);
+            }
+        }
+
+        /**
+         * Build a new config from a this configuration.
+         * <p>
+         * Useful for working with sub-directories that also need to be watched.
+         *
+         * @param dir
+         *            the directory to build new Config from (using this config as source of includes/excludes)
+         * @return the new Config
+         */
+        public Config asSubConfig(Path dir)
+        {
+            Config subconfig = new Config(dir);
+            subconfig.includes = this.includes;
+            subconfig.excludes = this.excludes;
+            if (dir == this.dir)
+                subconfig.recurseDepth = this.recurseDepth; // TODO shouldn't really do a subconfig for this
+            else
+            {
+                if (this.recurseDepth == UNLIMITED_DEPTH)
+                    subconfig.recurseDepth = UNLIMITED_DEPTH;
+                else
+                    subconfig.recurseDepth = this.recurseDepth - (dir.getNameCount() - this.dir.getNameCount());                
+            }
+            return subconfig;
+        }
+
+        public int getRecurseDepth()
+        {
+            return recurseDepth;
+        }
+        
+        public boolean isRecurseDepthUnlimited ()
+        {
+            return this.recurseDepth == UNLIMITED_DEPTH;
+        }
+        
+        public Path getPath ()
+        {
+            return this.dir;
+        }
+
+        private boolean hasMatch(Path path, List<PathMatcher> matchers)
+        {
+            for (PathMatcher matcher : matchers)
+            {
+                if (matcher.matches(path))
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public boolean isExcluded(Path dir) throws IOException
+        {
+            if (excludeHidden)
+            {
+                if (Files.isHidden(dir))
+                {
+                    if (NOISY_LOG.isDebugEnabled())
+                    {
+                        NOISY_LOG.debug("isExcluded [Hidden] on {}",dir);
+                    }
+                    return true;
+                }
+            }
+
+            if (excludes.isEmpty())
+            {
+                // no excludes == everything allowed
+                return false;
+            }
+
+            boolean matched = hasMatch(dir,excludes);
+            if (NOISY_LOG.isDebugEnabled())
+            {
+                NOISY_LOG.debug("isExcluded [{}] on {}",matched,dir);
+            }
+            return matched;
+        }
+
+        public boolean isIncluded(Path dir)
+        {
+            if (includes.isEmpty())
+            {
+                // no includes == everything allowed
+                if (NOISY_LOG.isDebugEnabled())
+                {
+                    NOISY_LOG.debug("isIncluded [All] on {}",dir);
+                }
+                return true;
+            }
+
+            boolean matched = hasMatch(dir,includes);
+            if (NOISY_LOG.isDebugEnabled())
+            {
+                NOISY_LOG.debug("isIncluded [{}] on {}",matched,dir);
+            }
+            return matched;
+        }
+
+        public boolean matches(Path path)
+        {
+            try
+            {
+                return !isExcluded(path) && isIncluded(path);
+            }
+            catch (IOException e)
+            {
+                LOG.warn("Unable to match path: " + path,e);
+                return false;
+            }
+        }
+
+        /**
+         * Set the recurse depth for the directory scanning.
+         * <p>
+         * -999 indicates arbitrarily deep recursion, 0 indicates no recursion, 1 is only one directory deep, and so on.
+         *
+         * @param depth
+         *            the number of directories deep to recurse
+         */
+        public void setRecurseDepth(int depth)
+        {
+            this.recurseDepth = depth;
+        }
+        
+   
+
+        /**
+         * Determine if the provided child directory should be recursed into based on the configured {@link #setRecurseDepth(int)}
+         *
+         * @param child
+         *            the child directory to test against
+         * @return true if recurse should occur, false otherwise
+         */
+        public boolean shouldRecurseDirectory(Path child)
+        {
+            if (!child.startsWith(dir))
+            {
+                // not part of parent? don't recurse
+                return false;
+            }
+
+            //If not limiting depth, should recurse all
+            if (isRecurseDepthUnlimited())
+                return true;
+            
+            //Depth limited, check it
+            int childDepth = dir.relativize(child).getNameCount();
+            return (childDepth <= recurseDepth);
+        }
+
+        private String toGlobPattern(Path path, String subPattern)
+        {
+            StringBuilder s = new StringBuilder();
+            s.append("glob:");
+
+            boolean needDelim = false;
+
+            // Add root (aka "C:\" for Windows)
+            Path root = path.getRoot();
+            if (root != null)
+            {
+                if (NOISY_LOG.isDebugEnabled())
+                {
+                    NOISY_LOG.debug("Path: {} -> Root: {}",path,root);
+                }
+                for (char c : root.toString().toCharArray())
+                {
+                    if (c == '\\')
+                    {
+                        s.append(PATTERN_SEP);
+                    }
+                    else
+                    {
+                        s.append(c);
+                    }
+                }
+            }
+            else
+            {
+                needDelim = true;
+            }
+
+            // Add the individual path segments
+            for (Path segment : path)
+            {
+                if (needDelim)
+                {
+                    s.append(PATTERN_SEP);
+                }
+                s.append(segment);
+                needDelim = true;
+            }
+
+            // Add the sub pattern (if specified)
+            if ((subPattern != null) && (subPattern.length() > 0))
+            {
+                if (needDelim)
+                {
+                    s.append(PATTERN_SEP);
+                }
+                for (char c : subPattern.toCharArray())
+                {
+                    if (c == '/')
+                    {
+                        s.append(PATTERN_SEP);
+                    }
+                    else
+                    {
+                        s.append(c);
+                    }
+                }
+            }
+
+            return s.toString();
+        }
+
+        @Override
+        public String toString()
+        {
+            StringBuilder s = new StringBuilder();
+            s.append(dir);
+            if (recurseDepth > 0)
+            {
+                s.append(" [depth=").append(recurseDepth).append("]");
+            }
+            return s.toString();
+        }
+    }
+    
+    public static class DepthLimitedFileVisitor extends SimpleFileVisitor<Path>
+    {
+        private Config base;
+        private PathWatcher watcher;
+        
+        public DepthLimitedFileVisitor (PathWatcher watcher, Config base)
+        {
+            this.base = base;
+            this.watcher = watcher;
+        }
+
+        /*
+         * 2 situations:
+         * 
+         * 1. a subtree exists at the time a dir to watch is added (eg watching /tmp/xxx and it contains aaa/)
+         *  - will start with /tmp/xxx for which we want to register with the poller
+         *  - want to visit each child
+         *     - if child is file, gen add event
+         *     - if child is dir, gen add event but ONLY register it if inside depth limit and ONLY continue visit of child if inside depth limit
+         * 2. a subtree is added inside a watched dir (eg watching /tmp/xxx, add aaa/ to xxx/)
+         *  - will start with /tmp/xxx/aaa 
+         *    - gen add event but ONLY register it if inside depth limit and ONLY continue visit of children if inside depth limit
+         *    
+         */
+        @Override
+        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
+        {
+            //In a directory:
+            // 1. the dir is the base directory
+            //   - register it with the poll mechanism
+            //   - generate pending add event (iff notifiable and matches patterns)
+            //   - continue the visit (sibling dirs, sibling files)
+            // 2. the dir is a subdir at some depth in the basedir's tree
+            //   - if the level of the subdir less or equal to base's limit
+            //     - register it wih the poll mechanism
+            //     - generate pending add event (iff notifiable and matches patterns)
+            //   - else stop visiting this dir
+
+            if (!base.isExcluded(dir))
+            {
+                if (base.isIncluded(dir))
+                {
+                    if (watcher.isNotifiable())
+                    {
+                        // Directory is specifically included in PathMatcher, then
+                        // it should be notified as such to interested listeners
+                        PathWatchEvent event = new PathWatchEvent(dir,PathWatchEventType.ADDED);
+                        if (LOG.isDebugEnabled())
+                        {
+                            LOG.debug("Pending {}",event);
+                        }
+                        watcher.addToPendingList(dir, event);
+                    }
+                }
+
+                //Register the dir with the watcher if it is:
+                // - the base dir and recursion is unlimited
+                // - the base dir and its depth is 0 (meaning we want to capture events from it, but not necessarily its children)
+                // - the base dir and we are recursing it and the depth is within the limit
+                // - a child dir and its depth is within the limits
+                if ((base.getPath().equals(dir) && (base.isRecurseDepthUnlimited() || base.getRecurseDepth() >= 0)) || base.shouldRecurseDirectory(dir))
+                    watcher.register(dir,base);
+            }
+
+            //Continue walking the tree of this dir if it is:
+            // - the base dir and recursion is unlimited
+            // - the base dir and we're not recursing in it
+            // - the base dir and we are recursing it and the depth is within the limit
+            // - a child dir and its depth is within the limits
+            if ((base.getPath().equals(dir)&& (base.isRecurseDepthUnlimited() || base.getRecurseDepth() >= 0)) || base.shouldRecurseDirectory(dir))
+                return FileVisitResult.CONTINUE;
+            else 
+                return FileVisitResult.SKIP_SUBTREE;   
+        }
+
+        @Override
+        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
+        {
+            // In a file:
+            //    - register with poll mechanism
+            //    - generate pending add event (iff notifiable and matches patterns)
+            
+            if (base.matches(file) && watcher.isNotifiable())
+            {
+                PathWatchEvent event = new PathWatchEvent(file,PathWatchEventType.ADDED);
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("Pending {}",event);
+                }
+                watcher.addToPendingList(file, event);
+            }
+
+            return FileVisitResult.CONTINUE;
+        }
+        
+    }
+
+    /**
+     * Listener for path change events
+     */
+    public static interface Listener extends EventListener
+    {
+        void onPathWatchEvent(PathWatchEvent event);
+    }
+    
+    /**
+     * EventListListener
+     *
+     * Listener that reports accumulated events in one shot
+     */
+    public static interface EventListListener extends EventListener
+    {
+        void onPathWatchEvents(List<PathWatchEvent> events);
+    }
+
+    /**
+     * PathWatchEvent
+     *
+     * Represents a file event. Reported to registered listeners.
+     */
+    public static class PathWatchEvent
+    {
+        private final Path path;
+        private final PathWatchEventType type;
+        private int count = 0;
+     
+        public PathWatchEvent(Path path, PathWatchEventType type)
+        {
+            this.path = path;
+            this.count = 1;
+            this.type = type;
+
+        }
+
+        public PathWatchEvent(Path path, WatchEvent<Path> event)
+        {
+            this.path = path;
+            this.count = event.count();
+            if (event.kind() == ENTRY_CREATE)
+            {
+                this.type = PathWatchEventType.ADDED;
+            }
+            else if (event.kind() == ENTRY_DELETE)
+            {
+                this.type = PathWatchEventType.DELETED;
+            }
+            else if (event.kind() == ENTRY_MODIFY)
+            {
+                this.type = PathWatchEventType.MODIFIED;
+            }
+            else
+            {
+                this.type = PathWatchEventType.UNKNOWN;
+            }
+        }
+
+        /** 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object obj)
+        {
+            if (this == obj)
+            {
+                return true;
+            }
+            if (obj == null)
+            {
+                return false;
+            }
+            if (getClass() != obj.getClass())
+            {
+                return false;
+            }
+            PathWatchEvent other = (PathWatchEvent)obj;
+            if (path == null)
+            {
+                if (other.path != null)
+                {
+                    return false;
+                }
+            }
+            else if (!path.equals(other.path))
+            {
+                return false;
+            }
+            if (type != other.type)
+            {
+                return false;
+            }
+            return true;
+        }
+
+        public Path getPath()
+        {
+            return path;
+        }
+
+        public PathWatchEventType getType()
+        {
+            return type;
+        }
+        
+        public void incrementCount(int num)
+        {
+            count += num;
+        }
+
+        public int getCount()
+        {
+            return count;
+        }
+        
+        /** 
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode()
+        {
+            final int prime = 31;
+            int result = 1;
+            result = (prime * result) + ((path == null)?0:path.hashCode());
+            result = (prime * result) + ((type == null)?0:type.hashCode());
+            return result;
+        }
+
+        /** 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString()
+        {
+            return String.format("PathWatchEvent[%s|%s]",type,path);
+        }
+    }
+    
+    
+    
+    /**
+     * PathPendingEvents
+     *
+     * For a given path, a list of events that are awaiting the
+     * quiet time. The list is in the order that the event were
+     * received from the WatchService
+     */
+    public static class PathPendingEvents
+    {
+        private Path _path;
+        private List<PathWatchEvent> _events;
+        private long _timestamp;
+        private long _lastFileSize = -1;
+
+        public PathPendingEvents (Path path)
+        {
+            _path = path;
+        }
+        
+        public PathPendingEvents (Path path, PathWatchEvent event)
+        {
+            this (path);
+            addEvent(event);
+        }
+        
+        public void addEvent (PathWatchEvent event)
+        {
+            long now = System.currentTimeMillis();
+            _timestamp = now;
+
+            if (_events == null)
+            {
+                _events = new ArrayList<PathWatchEvent>();
+                _events.add(event);
+            }
+            else
+            {
+                //Check if the same type of event is already present, in which case we
+                //can increment its counter. Otherwise, add it
+                PathWatchEvent existingType = null;
+                for (PathWatchEvent e:_events)
+                {
+                    if (e.getType() == event.getType())
+                    {
+                        existingType = e;
+                        break;
+                    }
+                }
+
+                if (existingType == null)
+                {
+                    _events.add(event);
+                }
+                else
+                {
+                    existingType.incrementCount(event.getCount());
+                }
+            }
+
+        }
+        
+        public List<PathWatchEvent> getEvents()
+        {
+            return _events;
+        }
+
+        public long getTimestamp()
+        {
+            return _timestamp;
+        }
+   
+        
+        /**
+         * Check to see if the file referenced by this Event is quiet.
+         * <p>
+         * Will validate the timestamp to see if it is expired, as well as if the file size hasn't changed within the quiet period.
+         * <p>
+         * Always updates timestamp to 'now' on use of this method.
+         * 
+         * @param now the time now 
+         *
+         * @param expiredDuration
+         *            the expired duration past the timestamp to be considered expired
+         * @param expiredUnit
+         *            the unit of time for the expired check
+         * @return true if expired, false if not
+         */
+        public boolean isQuiet(long now, long expiredDuration, TimeUnit expiredUnit)
+        {
+
+            long pastdue = _timestamp + expiredUnit.toMillis(expiredDuration);
+            _timestamp = now;
+
+            long fileSize = _path.toFile().length(); // File.length() returns 0 for non existant files
+            boolean fileSizeChanged = (_lastFileSize != fileSize);
+            _lastFileSize = fileSize;
+
+            if ((now > pastdue) && (!fileSizeChanged /*|| fileSize == 0*/))
+            {
+                // Quiet period timestamp has expired, and file size hasn't changed, or the file
+                // has been deleted.
+                // Consider this a quiet event now.
+                return true;
+            }
+
+            return false;
+        }
+
+    }
+
+    /**
+     * PathWatchEventType
+     *
+     * Type of an event
+     */
+    public static enum PathWatchEventType
+    {
+        ADDED, DELETED, MODIFIED, UNKNOWN;
+    }
+
+    private static final boolean IS_WINDOWS;
+
+    static
+    {
+        String os = System.getProperty("os.name");
+        if (os == null)
+        {
+            IS_WINDOWS = false;
+        }
+        else
+        {
+            String osl = os.toLowerCase(Locale.ENGLISH);
+            IS_WINDOWS = osl.contains("windows");
+        }
+    }
+
+    private static final Logger LOG = Log.getLogger(PathWatcher.class);
+    /**
+     * super noisy debug logging
+     */
+    private static final Logger NOISY_LOG = Log.getLogger(PathWatcher.class.getName() + ".Noisy");
+
+    @SuppressWarnings("unchecked")
+    protected static <T> WatchEvent<T> cast(WatchEvent<?> event)
+    {
+        return (WatchEvent<T>)event;
+    }
+
+    private static final WatchEvent.Kind<?> WATCH_EVENT_KINDS[] = { ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY };
+    
+    private  WatchService watchService;
+    private  WatchEvent.Modifier watchModifiers[];
+    private  boolean nativeWatchService;
+    
+    private Map<WatchKey, Config> keys = new HashMap<>();
+    private List<EventListener> listeners = new CopyOnWriteArrayList<>(); //a listener may modify the listener list directly or by stopping the PathWatcher
+    private List<Config> configs = new ArrayList<>();
+
+    /**
+     * Update Quiet Time - set to 1000 ms as default (a lower value in Windows is not supported)
+     */
+    private long updateQuietTimeDuration = 1000;
+    private TimeUnit updateQuietTimeUnit = TimeUnit.MILLISECONDS;
+    private Thread thread;
+    private boolean _notifyExistingOnStart = true;
+    private Map<Path, PathPendingEvents> pendingEvents = new LinkedHashMap<>();
+    
+    
+    
+    /**
+     * Construct new PathWatcher
+     */
+    public PathWatcher()
+    {
+    }
+    
+    /**
+     * Request watch on a the given path (either file or dir)
+     * using all Config defaults. In the case of a dir,
+     * the default is not to recurse into subdirs for watching.
+     * 
+     * @param file the path to watch
+     */
+    public void watch (final Path file)
+    {
+        //Make a config for the dir above it and
+        //include a match only for the given path
+        //using all defaults for the configuration
+        Path abs = file;
+        if (!abs.isAbsolute())
+        {
+            abs = file.toAbsolutePath();
+        }
+        
+        //Check we don't already have a config for the parent directory. 
+        //If we do, add in this filename.
+        Config config = null;
+        Path parent = abs.getParent();
+        for (Config c:configs)
+        {
+            if (c.getPath().equals(parent))
+            {
+                config = c;
+                break;
+            }
+        }
+        
+        //Make a new config
+        if (config == null)
+        {
+            config = new Config(abs.getParent());
+            // the include for the directory itself
+            config.addIncludeGlobRelative("");
+            //add the include for the file
+            config.addIncludeGlobRelative(file.getFileName().toString());
+            watch(config);
+        }
+        else
+            //add the include for the file
+            config.addIncludeGlobRelative(file.getFileName().toString());
+    }
+    
+    /**
+     * Request watch on a path with custom Config 
+     * provided.
+     * 
+     * @param config the configuration to watch
+     */
+    public void watch (final Config config)
+    {
+        //Add a custom config
+        configs.add(config);
+    }
+    
+    /**
+     * Register path in the config with the file watch service,
+     * walking the tree if it happens to be a directory.
+     * 
+     * @param baseDir the base directory configuration to watch
+     * @throws IOException if unable to walk the filesystem tree
+     */
+    protected void prepareConfig (final Config baseDir) throws IOException
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("Watching directory {}",baseDir);
+        }
+        Files.walkFileTree(baseDir.getPath(), new DepthLimitedFileVisitor(this, baseDir));
+    }
+
+    
+    
+    /**
+     * Add a listener for changes the watcher notices.
+     * 
+     * @param listener change listener
+     */
+    public void addListener(EventListener listener)
+    {
+        listeners.add(listener);
+    }
+
+    /**
+     * Append some info on the paths that we are watching.
+     * 
+     * @param s
+     */
+    private void appendConfigId(StringBuilder s)
+    {
+        List<Path> dirs = new ArrayList<>();
+
+        for (Config config : keys.values())
+        {
+            dirs.add(config.dir);
+        }
+
+        Collections.sort(dirs);
+
+        s.append("[");
+        if (dirs.size() > 0)
+        {
+            s.append(dirs.get(0));
+            if (dirs.size() > 1)
+            {
+                s.append(" (+").append(dirs.size() - 1).append(")");
+            }
+        }
+        else
+        {
+            s.append("<null>");
+        }
+        s.append("]");
+    }
+
+    /** 
+     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        //create a new watchservice
+        createWatchService();
+        
+        //ensure setting of quiet time is appropriate now we have a watcher
+        setUpdateQuietTime(getUpdateQuietTimeMillis(), TimeUnit.MILLISECONDS);
+
+        // Register all watched paths, walking dir hierarchies as needed, possibly generating
+        // fake add events if notifyExistingOnStart is true
+        for (Config c:configs)
+            prepareConfig(c);
+        
+        // Start Thread for watcher take/pollKeys loop
+        StringBuilder threadId = new StringBuilder();
+        threadId.append("PathWatcher-Thread");
+        appendConfigId(threadId);
+
+        thread = new Thread(this,threadId.toString());
+        thread.setDaemon(true);
+        thread.start();
+        super.doStart();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        if (watchService != null)
+            watchService.close(); //will invalidate registered watch keys, interrupt thread in take or poll
+        watchService = null;
+        thread = null;
+        keys.clear();
+        pendingEvents.clear();
+        super.doStop();
+    }
+    
+    
+    /**
+     * Remove all current configs and listeners.
+     */
+    public void reset ()
+    {
+        if (!isStopped())
+            throw new IllegalStateException("PathWatcher must be stopped before reset.");
+        
+        configs.clear();
+        listeners.clear();
+    }
+    
+    
+    /**
+     * Create a fresh WatchService and determine if it is a 
+     * native implementation or not.
+     * 
+     * @throws IOException
+     */
+    private void createWatchService () throws IOException
+    {
+        //create a watch service
+        this.watchService = FileSystems.getDefault().newWatchService();
+
+        WatchEvent.Modifier modifiers[] = null;
+        boolean nativeService = true;
+        // Try to determine native behavior
+        // See http://stackoverflow.com/questions/9588737/is-java-7-watchservice-slow-for-anyone-else
+        try
+        {
+            ClassLoader cl = Thread.currentThread().getContextClassLoader();
+            Class<?> pollingWatchServiceClass = Class.forName("sun.nio.fs.PollingWatchService",false,cl);
+            if (pollingWatchServiceClass.isAssignableFrom(this.watchService.getClass()))
+            {
+                nativeService = false;
+                LOG.info("Using Non-Native Java {}",pollingWatchServiceClass.getName());
+                Class<?> c = Class.forName("com.sun.nio.file.SensitivityWatchEventModifier");
+                Field f = c.getField("HIGH");
+                modifiers = new WatchEvent.Modifier[]
+                        {
+                         (WatchEvent.Modifier)f.get(c)
+                        };
+            }
+        }
+        catch (Throwable t)
+        {
+            // Unknown JVM environment, assuming native.
+            LOG.ignore(t);
+        }
+
+        this.watchModifiers = modifiers;
+        this.nativeWatchService = nativeService;
+    }
+    
+    /**
+     * Check to see if the watcher is in a state where it should generate
+     * watch events to the listeners. Used to determine if watcher should generate
+     * events for existing files and dirs on startup.
+     * 
+     * @return true if the watcher should generate events to the listeners.
+     */
+    protected boolean isNotifiable ()
+    {
+        return (isStarted() || (!isStarted() && isNotifyExistingOnStart()));
+    }
+
+    /**
+     * Get an iterator over the listeners.
+     * 
+     * @return iterator over the listeners.
+     */
+    public Iterator<EventListener> getListeners()
+    {
+        return listeners.iterator();
+    }
+
+    /**
+     * Change the quiet time.
+     * 
+     * @return the quiet time in millis
+     */
+    public long getUpdateQuietTimeMillis()
+    {
+        return TimeUnit.MILLISECONDS.convert(updateQuietTimeDuration,updateQuietTimeUnit);
+    }
+
+    /**
+     * Generate events to the listeners.
+     * 
+     * @param events the events captured
+     */
+    protected void notifyOnPathWatchEvents (List<PathWatchEvent> events)
+    {
+        if (events == null || events.isEmpty())
+            return;
+
+        for (EventListener listener : listeners)
+        {
+            if (listener instanceof EventListListener)
+            {
+                try
+                {
+                    ((EventListListener)listener).onPathWatchEvents(events);
+                }
+                catch (Throwable t)
+                {
+                    LOG.warn(t);
+                }
+            }
+            else
+            {
+                Listener l = (Listener)listener;
+                for (PathWatchEvent event:events)
+                {
+                    try
+                    {
+                        l.onPathWatchEvent(event);
+                    }
+                    catch (Throwable t)
+                    {
+                        LOG.warn(t);
+                    }
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Register a path (directory) with the WatchService.
+     * 
+     * @param dir the directory to register
+     * @param root the configuration root
+     * @throws IOException if unable to register the path with the watch service.
+     */
+    protected void register(Path dir, Config root) throws IOException
+    {
+       
+        LOG.debug("Registering watch on {}",dir);
+        if(watchModifiers != null) 
+        {
+            // Java Watcher
+            WatchKey key = dir.register(watchService,WATCH_EVENT_KINDS,watchModifiers);
+            keys.put(key,root.asSubConfig(dir));
+        } else 
+        {
+            // Native Watcher
+            WatchKey key = dir.register(watchService,WATCH_EVENT_KINDS);
+            keys.put(key,root.asSubConfig(dir));
+        }
+    }
+
+    
+    /**
+     * Delete a listener
+     * @param listener the listener to remove
+     * @return true if the listener existed and was removed
+     */
+    public boolean removeListener(Listener listener)
+    {
+        return listeners.remove(listener);
+    }
+
+    
+    /** 
+     * Forever loop.
+     * 
+     * Wait for the WatchService to report some filesystem events for the
+     * watched paths.
+     * 
+     * When an event for a path first occurs, it is subjected to a quiet time.
+     * Subsequent events that arrive for the same path during this quiet time are
+     * accumulated and the timer reset. Only when the quiet time has expired are
+     * the accumulated events sent. MODIFY events are handled slightly differently -
+     * multiple MODIFY events arriving within a quiet time are coalesced into a
+     * single MODIFY event. Both the accumulation of events and coalescing of MODIFY
+     * events reduce the number and frequency of event reporting for "noisy" files (ie
+     * those that are undergoing rapid change).
+     * 
+     * @see java.lang.Runnable#run()
+     */
+    @Override
+    public void run()
+    {
+
+        List<PathWatchEvent> notifiableEvents = new ArrayList<PathWatchEvent>();
+        
+        // Start the java.nio watching
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("Starting java.nio file watching with {}",watchService);
+        }
+
+        while (watchService != null  && thread == Thread.currentThread())
+        {
+            WatchKey key = null;
+
+            try
+            {     
+                //If no pending events, wait forever for new events
+                if (pendingEvents.isEmpty())
+                {
+                    if (NOISY_LOG.isDebugEnabled())
+                        NOISY_LOG.debug("Waiting for take()");
+                    key = watchService.take();
+                }
+                else
+                {
+                    //There are existing events that might be ready to go,
+                    //only wait as long as the quiet time for any new events
+                    if (NOISY_LOG.isDebugEnabled())
+                        NOISY_LOG.debug("Waiting for poll({}, {})",updateQuietTimeDuration,updateQuietTimeUnit);
+
+                    key = watchService.poll(updateQuietTimeDuration,updateQuietTimeUnit);
+                   
+                    //If no new events its safe to process the pendings
+                    if (key == null)
+                    {
+                        long now = System.currentTimeMillis();
+                        // no new event encountered.
+                        for (Path path : new HashSet<Path>(pendingEvents.keySet()))
+                        {
+                            PathPendingEvents pending = pendingEvents.get(path);
+                            if (pending.isQuiet(now, updateQuietTimeDuration,updateQuietTimeUnit))
+                            {
+                                //No fresh events received during quiet time for this path, 
+                                //so generate the events that were pent up
+                                for (PathWatchEvent p:pending.getEvents())
+                                {
+                                    notifiableEvents.add(p);
+                                }
+                                // remove from pending list
+                                pendingEvents.remove(path);
+                            }
+                        }
+                    }
+                }
+            }
+            catch (ClosedWatchServiceException e)
+            {
+                // Normal shutdown of watcher
+                return;
+            }
+            catch (InterruptedException e)
+            {
+                if (isRunning())
+                {
+                    LOG.warn(e);
+                }
+                else
+                {
+                    LOG.ignore(e);
+                }
+                return;
+            }
+
+            //If there was some new events to process
+            if (key != null)
+            {
+
+                Config config = keys.get(key);
+                if (config == null)
+                {
+                    if (LOG.isDebugEnabled())
+                    {
+                        LOG.debug("WatchKey not recognized: {}",key);
+                    }
+                    continue;
+                }
+
+                for (WatchEvent<?> event : key.pollEvents())
+                {
+                    @SuppressWarnings("unchecked")
+                    WatchEvent.Kind<Path> kind = (Kind<Path>)event.kind();
+                    WatchEvent<Path> ev = cast(event);
+                    Path name = ev.context();
+                    Path child = config.dir.resolve(name);
+
+                    if (kind == ENTRY_CREATE)
+                    {
+                        // handle special case for registering new directories
+                        // recursively
+                        if (Files.isDirectory(child,LinkOption.NOFOLLOW_LINKS))
+                        {
+                            try
+                            {
+                                prepareConfig(config.asSubConfig(child));
+                            }
+                            catch (IOException e)
+                            {
+                                LOG.warn(e);
+                            }
+                        }
+                        else if (config.matches(child))
+                        {
+                            addToPendingList(child, new PathWatchEvent(child,ev));
+                        }
+                    }
+                    else if (config.matches(child))
+                    {
+                        addToPendingList(child, new PathWatchEvent(child,ev));      
+                    }
+                }
+            }
+
+            //Send any notifications generated this pass
+            notifyOnPathWatchEvents(notifiableEvents);
+            notifiableEvents.clear();
+            
+            if (key != null && !key.reset())
+            {
+                keys.remove(key);
+                if (keys.isEmpty())
+                {
+                    return; // all done, no longer monitoring anything
+                }
+            }
+        }
+    }
+    
+    
+    /**
+     * Add an event reported by the WatchService to list of pending events
+     * that will be sent after their quiet time has expired.
+     * 
+     * @param path the path to add to the pending list
+     * @param event the pending event
+     */
+    public void addToPendingList (Path path, PathWatchEvent event)
+    {
+        PathPendingEvents pending = pendingEvents.get(path);
+        
+        //Are there already pending events for this path?
+        if (pending == null)
+        {
+            //No existing pending events, create pending list
+            pendingEvents.put(path,new PathPendingEvents(path, event));
+        }
+        else
+        {
+            //There are already some events pending for this path
+            pending.addEvent(event);
+        }
+    }
+    
+    
+    /**
+     * Whether or not to issue notifications for directories and files that
+     * already exist when the watcher starts.
+     * 
+     * @param notify true if existing paths should be notified or not
+     */
+    public void setNotifyExistingOnStart (boolean notify)
+    {
+        _notifyExistingOnStart = notify;
+    }
+    
+    public boolean isNotifyExistingOnStart ()
+    {
+        return _notifyExistingOnStart;
+    }
+
+    /**
+     * Set the quiet time.
+     * 
+     * @param duration the quiet time duration
+     * @param unit the quite time unit
+     */
+    public void setUpdateQuietTime(long duration, TimeUnit unit)
+    {
+        long desiredMillis = unit.toMillis(duration);
+        
+        if (watchService != null && !this.nativeWatchService && (desiredMillis < 5000))
+        {
+            LOG.warn("Quiet Time is too low for non-native WatchService [{}]: {} < 5000 ms (defaulting to 5000 ms)",watchService.getClass().getName(),desiredMillis);
+            this.updateQuietTimeDuration = 5000;
+            this.updateQuietTimeUnit = TimeUnit.MILLISECONDS;
+            return;
+        }
+
+        if (IS_WINDOWS && (desiredMillis < 1000))
+        {
+            LOG.warn("Quiet Time is too low for Microsoft Windows: {} < 1000 ms (defaulting to 1000 ms)",desiredMillis);
+            this.updateQuietTimeDuration = 1000;
+            this.updateQuietTimeUnit = TimeUnit.MILLISECONDS;
+            return;
+        }
+        
+        // All other OS and watch service combinations can use desired setting
+        this.updateQuietTimeDuration = duration;
+        this.updateQuietTimeUnit = unit;
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder s = new StringBuilder(this.getClass().getName());
+        appendConfigId(s);
+        return s.toString();
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/PatternMatcher.java b/jetty-util/src/main/java/org/eclipse/jetty/util/PatternMatcher.java
index 1b08f4b..8211976 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/PatternMatcher.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/PatternMatcher.java
@@ -53,7 +53,7 @@
      * @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
+     * @throws Exception if fundamental error in pattern matching
      */
     public void match (Pattern pattern, URI[] uris, boolean isNullInclusive)
     throws Exception
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Predicate.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Predicate.java
deleted file mode 100644
index 7dac2ed..0000000
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/Predicate.java
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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;
-
-/**
- * Temporary implementation of Java 8's <code>java.util.function.Predicate</code>
- * <p>
- * To be removed for Java 8 only versions of Jetty.
- * 
- * @param <ITEM> the item to test
- */
-public interface Predicate<ITEM>
-{
-    boolean test(ITEM item);
-}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Promise.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Promise.java
index 7c4c9e1..21b1f24 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/Promise.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Promise.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.util;
 
+import java.util.Objects;
+
 import org.eclipse.jetty.util.log.Log;
 
 /**
@@ -41,7 +43,6 @@
      * @param x the reason for the operation failure
      */
     public void failed(Throwable x);
-    
 
     /**
      * <p>Empty implementation of {@link Promise}</p>
@@ -62,4 +63,43 @@
         }
     }
 
+    public static class Wrapper<W> implements Promise<W>
+    {
+        private final Promise<W> promise;
+
+        public Wrapper(Promise<W> promise)
+        {
+            this.promise = Objects.requireNonNull(promise);
+        }
+
+        @Override
+        public void succeeded(W result)
+        {
+            promise.succeeded(result);
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            promise.failed(x);
+        }
+
+        public Promise<W> getPromise()
+        {
+            return promise;
+        }
+
+        public Promise<W> unwrap()
+        {
+            Promise<W> result = promise;
+            while (true)
+            {
+                if (result instanceof Wrapper)
+                    result = ((Wrapper<W>)result).unwrap();
+                else
+                    break;
+            }
+            return result;
+        }
+    }
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/QuotedStringTokenizer.java b/jetty-util/src/main/java/org/eclipse/jetty/util/QuotedStringTokenizer.java
index f24c90e..823a78c 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/QuotedStringTokenizer.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/QuotedStringTokenizer.java
@@ -472,6 +472,7 @@
     /* ------------------------------------------------------------ */
     /** Unquote a string.
      * @param s The string to unquote.
+     * @param lenient true if unquoting should be lenient to escaped content, leaving some alone, false if string unescaping
      * @return quoted string
      */
     public static String unquote(String s, boolean lenient)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/RegexSet.java b/jetty-util/src/main/java/org/eclipse/jetty/util/RegexSet.java
index d9af7ce..148f18b 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/RegexSet.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/RegexSet.java
@@ -23,6 +23,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
+import java.util.function.Predicate;
 import java.util.regex.Pattern;
 
 /**
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/RolloverFileOutputStream.java b/jetty-util/src/main/java/org/eclipse/jetty/util/RolloverFileOutputStream.java
index a4da918..98b3ef2 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/RolloverFileOutputStream.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/RolloverFileOutputStream.java
@@ -24,7 +24,8 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.text.SimpleDateFormat;
-import java.util.Calendar;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
 import java.util.Date;
 import java.util.Locale;
 import java.util.TimeZone;
@@ -53,7 +54,6 @@
     final static int ROLLOVER_FILE_RETAIN_DAYS = 31;
 
     private RollTask _rollTask;
-    private Calendar midnight;
     private SimpleDateFormat _fileBackupFormat;
     private SimpleDateFormat _fileDateFormat;
 
@@ -66,7 +66,7 @@
     /**
      * @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
+     * @throws IOException if unable to create output
      */
     public RolloverFileOutputStream(String filename)
         throws IOException
@@ -79,7 +79,7 @@
      * @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
+     * @throws IOException if unable to create output
      */
     public RolloverFileOutputStream(String filename, boolean append)
         throws IOException
@@ -93,7 +93,7 @@
      * 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
+     * @throws IOException if unable to create output
      */
     public RolloverFileOutputStream(String filename,
                                     boolean append,
@@ -109,7 +109,8 @@
      * 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
+     * @param zone the timezone for the output
+     * @throws IOException if unable to create output
      */
     public RolloverFileOutputStream(String filename,
                                     boolean append,
@@ -117,19 +118,19 @@
                                     TimeZone zone)
         throws IOException
     {
-
-         this(filename,append,retainDays,zone,null,null);
+         this(filename,append,retainDays,zone,null,null,ZonedDateTime.now(zone.toZoneId()));
     }
-     
+
     /* ------------------------------------------------------------ */
     /**
      * @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 zone the timezone for the output
      * @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
+     * @throws IOException if unable to create output
      */
     public RolloverFileOutputStream(String filename,
                                     boolean append,
@@ -139,19 +140,32 @@
                                     String backupFormat)
         throws IOException
     {
+        this(filename,append,retainDays,zone,dateFormat,backupFormat,ZonedDateTime.now(zone.toZoneId()));
+    }
+    
+    
+    RolloverFileOutputStream(String filename,
+        boolean append,
+        int retainDays,
+        TimeZone zone,
+        String dateFormat,
+        String backupFormat,
+        ZonedDateTime now)
+            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();
@@ -164,35 +178,41 @@
         _filename=filename;
         _append=append;
         _retainDays=retainDays;
-        setFile();
         
         synchronized(RolloverFileOutputStream.class)
         {
             if (__rollover==null)
                 __rollover=new Timer(RolloverFileOutputStream.class.getName(),true);
-            
-            _rollTask=new RollTask();
-
-            midnight = Calendar.getInstance();
-            midnight.setTimeZone(zone);
-            // set to midnight
-            midnight.set(Calendar.HOUR, 0);
-            midnight.set(Calendar.MINUTE, 0);
-            midnight.set(Calendar.SECOND, 0);
-            midnight.set(Calendar.MILLISECOND, 0);
-            
-            scheduleNextRollover();
+    
+            // Calculate Today's Midnight, based on Configured TimeZone (will be in past, even if by a few milliseconds)
+            setFile(now);        
+            // This will schedule the rollover event to the next midnight
+            scheduleNextRollover(now);
         }
     }
-    
-    private void scheduleNextRollover()
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get the "start of day" for the provided DateTime at the zone specified.
+     *
+     * @param now the date time to calculate from
+     * @return start of the day of the date provided
+     */
+    public static ZonedDateTime toMidnight(ZonedDateTime now)
     {
-        // Increment to next day.
-        // Using Calendar.add(DAY, 1) takes in account Daylights Savings
-        // differences, and still maintains the "midnight" settings for
-        // Hour, Minute, Second, Milliseconds
-        midnight.add(Calendar.DAY_OF_MONTH, 1);
-        __rollover.schedule(_rollTask,midnight.getTime());
+        return now.toLocalDate().atStartOfDay(now.getZone()).plus(1, ChronoUnit.DAYS);
+    }
+
+    /* ------------------------------------------------------------ */
+    private void scheduleNextRollover(ZonedDateTime now)
+    {
+        _rollTask = new RollTask();
+        // Get tomorrow's midnight based on Configured TimeZone
+        ZonedDateTime midnight = toMidnight(now);
+
+        // Schedule next rollover event to occur, based on local machine's Unix Epoch milliseconds
+        long delay = midnight.toInstant().toEpochMilli() - now.toInstant().toEpochMilli();
+        __rollover.schedule(_rollTask,delay);
     }
 
     /* ------------------------------------------------------------ */
@@ -216,7 +236,7 @@
     }
 
     /* ------------------------------------------------------------ */
-    private synchronized void setFile()
+    synchronized void setFile(ZonedDateTime now)
         throws IOException
     {
         // Check directory
@@ -227,8 +247,6 @@
         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);
@@ -236,7 +254,7 @@
         {
             file=new File(dir,
                           filename.substring(0,i)+
-                          _fileDateFormat.format(now)+
+                          _fileDateFormat.format(new Date(now.toInstant().toEpochMilli()))+
                           filename.substring(i+YYYY_MM_DD.length()));
         }
             
@@ -249,7 +267,7 @@
             // Yep
             _file=file;
             if (!_append && file.exists())
-                file.renameTo(new File(file.toString()+"."+_fileBackupFormat.format(now)));
+                file.renameTo(new File(file.toString()+"."+_fileBackupFormat.format(new Date(now.toInstant().toEpochMilli()))));
             OutputStream oldOut=out;
             out=new FileOutputStream(file.toString(),_append);
             if (oldOut!=null)
@@ -259,13 +277,12 @@
     }
 
     /* ------------------------------------------------------------ */
-    private void removeOldFiles()
+    void removeOldFiles(ZonedDateTime now)
     {
         if (_retainDays>0)
         {
-            Calendar now = Calendar.getInstance();
-            now.add(Calendar.DAY_OF_MONTH, (-1)*_retainDays);
-            long expired = now.getTimeInMillis();
+            // Establish expiration time, based on configured TimeZone
+            long expired = now.minus(_retainDays, ChronoUnit.DAYS).toInstant().toEpochMilli();
             
             File file= new File(_filename);
             File dir = new File(file.getParent());
@@ -295,19 +312,19 @@
     /* ------------------------------------------------------------ */
     @Override
     public void write (byte[] buf)
-            throws IOException
-     {
-            out.write (buf);
-     }
+        throws IOException
+    {
+        out.write (buf);
+    }
 
     /* ------------------------------------------------------------ */
     @Override
     public void write (byte[] buf, int off, int len)
-            throws IOException
-     {
-            out.write (buf, off, len);
-     }
-    
+        throws IOException
+    {
+        out.write (buf, off, len);
+    }
+
     /* ------------------------------------------------------------ */
     @Override
     public void close()
@@ -321,8 +338,11 @@
                 out=null;
                 _file=null;
             }
-
-            _rollTask.cancel(); 
+    
+            if (_rollTask != null)
+            {
+                _rollTask.cancel();
+            }
         }
     }
     
@@ -334,14 +354,18 @@
         {
             try
             {
-                RolloverFileOutputStream.this.setFile();
-                RolloverFileOutputStream.this.scheduleNextRollover();
-                RolloverFileOutputStream.this.removeOldFiles();
+                synchronized(RolloverFileOutputStream.class)
+                {
+                    ZonedDateTime now = ZonedDateTime.now(_fileDateFormat.getTimeZone().toZoneId());
+                    RolloverFileOutputStream.this.setFile(now);
+                    RolloverFileOutputStream.this.scheduleNextRollover(now);
+                    RolloverFileOutputStream.this.removeOldFiles(now);
+                }
             }
-            catch(IOException e)
+            catch(Throwable t)
             {
                 // Cannot log this exception to a LOG, as RolloverFOS can be used by logging
-                e.printStackTrace(System.err);
+                t.printStackTrace(System.err);
             }
         }
     }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java
index 7133fea..cae7cf4 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java
@@ -221,7 +221,7 @@
     /**
      * Apply a filter to files found in the scan directory.
      * Only files matching the filter will be reported as added/changed/removed.
-     * @param filter
+     * @param filter the filename filter to use
      */
     public void setFilenameFilter (FilenameFilter filter)
     {
@@ -257,7 +257,7 @@
     
     /* ------------------------------------------------------------ */
     /** Set if found directories should be reported.
-     * @param dirs
+     * @param dirs true to report directory changes as well
      */
     public void setReportDirs(boolean dirs)
     {
@@ -273,7 +273,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Add an added/removed/changed listener
-     * @param listener
+     * @param listener the listener to add
      */
     public synchronized void addListener (Listener listener)
     {
@@ -370,6 +370,7 @@
     }
 
     /**
+     * @param path tests if the path exists
      * @return true if the path exists in one of the scandirs
      */
     public boolean exists(String path)
@@ -595,7 +596,7 @@
 
     /**
      * Report a file addition to the registered FileAddedListeners
-     * @param filename
+     * @param filename the filename
      */
     private void reportAddition (String filename)
     {
@@ -622,7 +623,7 @@
 
     /**
      * Report a file removal to the FileRemovedListeners
-     * @param filename
+     * @param filename the filename
      */
     private void reportRemoval (String filename)
     {
@@ -649,7 +650,7 @@
 
     /**
      * Report a file change to the FileChangedListeners
-     * @param filename
+     * @param filename the filename
      */
     private void reportChange (String filename)
     {
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/SharedBlockingCallback.java b/jetty-util/src/main/java/org/eclipse/jetty/util/SharedBlockingCallback.java
index 4ff7020..d6a841f 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/SharedBlockingCallback.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/SharedBlockingCallback.java
@@ -29,20 +29,20 @@
 
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.thread.NonBlockingThread;
 
-
-/* ------------------------------------------------------------ */
-/** Provides a reusable BlockingCallback.
+/**
+ * Provides a reusable {@link Callback} that can block the thread
+ * while waiting to be completed.
+ * <p>
  * A typical usage pattern is:
  * <pre>
  * void someBlockingCall(Object... args) throws IOException
  * {
- *   try(Blocker blocker=sharedBlockingCallback.acquire())
- *   {
- *     someAsyncCall(args,blocker);
- *     blocker.block();
- *   }
+ *     try(Blocker blocker = sharedBlockingCallback.acquire())
+ *     {
+ *         someAsyncCall(args, blocker);
+ *         blocker.block();
+ *     }
  * }
  * </pre>
  */
@@ -50,33 +50,25 @@
 {
     static final Logger LOG = Log.getLogger(SharedBlockingCallback.class);
 
-    final ReentrantLock _lock = new ReentrantLock();
-    final Condition _idle = _lock.newCondition();
-    final Condition _complete = _lock.newCondition();
-
-
     private static Throwable IDLE = new ConstantThrowable("IDLE");
-
     private static Throwable SUCCEEDED = new ConstantThrowable("SUCCEEDED");
 
     private static Throwable FAILED = new ConstantThrowable("FAILED");
 
-    Blocker _blocker;
-    
-    public SharedBlockingCallback()
-    {
-        _blocker=new Blocker();
-    }
-    
+    private final ReentrantLock _lock = new ReentrantLock();
+    private final Condition _idle = _lock.newCondition();
+    private final Condition _complete = _lock.newCondition();
+    private Blocker _blocker = new Blocker();
+
     protected long getIdleTimeout()
     {
         return -1;
     }
-    
+
     public Blocker acquire() throws IOException
     {
-        _lock.lock();
         long idle = getIdleTimeout();
+        _lock.lock();
         try
         {
             while (_blocker._state != IDLE)
@@ -91,8 +83,9 @@
                     _idle.await();
             }
             _blocker._state = null;
+            return _blocker;
         }
-        catch (final InterruptedException e)
+        catch (InterruptedException x)
         {
             throw new InterruptedIOException();
         }
@@ -100,7 +93,6 @@
         {
             _lock.unlock();
         }
-        return _blocker;
     }
 
     protected void notComplete(Blocker blocker)
@@ -110,18 +102,21 @@
             LOG.debug(new Throwable());
     }
     
-    /* ------------------------------------------------------------ */
-    /** A Closeable Callback.
+    /**
+     * A Closeable Callback.
      * Uses the auto close mechanism to check block has been called OK.
+     * <p>Implements {@link Callback.NonBlocking} because calls to this
+     * callback do not blocak, rather they wakeup the thread that is blocked
+     * in {@link #block()}
      */
-    public class Blocker implements Callback, Closeable
+    public class Blocker implements Callback.NonBlocking, Closeable
     {
-        Throwable _state = IDLE;
+        private Throwable _state = IDLE;
         
         protected Blocker()
         {
         }
-
+        
         @Override
         public void succeeded()
         {
@@ -159,8 +154,15 @@
                         _state=cause;
                     _complete.signalAll();
                 }
-                else 
+                else if (_state instanceof BlockerTimeoutException)
+                {
+                    // Failure arrived late, block() already
+                    // modified the state, nothing more to do.
+                }
+                else
+                {
                     throw new IllegalStateException(_state);
+                }
             }
             finally
             {
@@ -177,25 +179,29 @@
          */
         public void block() throws IOException
         {
-            if (NonBlockingThread.isNonBlockingThread())
-                LOG.warn("Blocking a NonBlockingThread: ",new Throwable());
-            
-            _lock.lock();
             long idle = getIdleTimeout();
+            _lock.lock();
             try
             {
                 while (_state == null)
                 {
-                    if (idle>0 && (idle < Long.MAX_VALUE/2))
+                    if (idle > 0)
                     {
-                        // Wait a little bit longer than expected callback idle timeout
-                        if (!_complete.await(idle+idle/2,TimeUnit.MILLISECONDS))
-                            // The callback has not arrived in sufficient time.
-                            // We will synthesize a TimeoutException 
-                            _state=new BlockerTimeoutException();
+                        // Waiting here may compete with the idle timeout mechanism,
+                        // so here we wait a little bit longer to favor the normal
+                        // idle timeout mechanism that will call failed(Throwable).
+                        long excess = Math.min(idle / 2, 1000);
+                        if (!_complete.await(idle + excess, TimeUnit.MILLISECONDS))
+                        {
+                            // Method failed(Throwable) has not been called yet,
+                            // so we will synthesize a special TimeoutException.
+                            _state = new BlockerTimeoutException();
+                        }
                     }
                     else
+                    {
                         _complete.await();
+                    }
                 }
 
                 if (_state == SUCCEEDED)
@@ -224,12 +230,9 @@
         
         /**
          * Check the Callback has succeeded or failed and after the return leave in the state to allow reuse.
-         * 
-         * @throws IOException
-         *             if exception was caught during blocking, or callback was cancelled
          */
         @Override
-        public void close() throws IOException
+        public void close()
         {
             _lock.lock();
             try
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/SocketAddressResolver.java b/jetty-util/src/main/java/org/eclipse/jetty/util/SocketAddressResolver.java
index eaa0390..f0d1638 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/SocketAddressResolver.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/SocketAddressResolver.java
@@ -18,14 +18,19 @@
 
 package org.eclipse.jetty.util;
 
+import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
-import java.nio.channels.UnresolvedAddressException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.thread.Scheduler;
@@ -38,26 +43,31 @@
     /**
      * Resolves the given host and port, returning a {@link SocketAddress} through the given {@link Promise}
      * with the default timeout.
-     *
-     * @param host the host to resolve
+     *  @param host the host to resolve
      * @param port the port of the resulting socket address
      * @param promise the callback invoked when the resolution succeeds or fails
      */
-    public void resolve(String host, int port, Promise<SocketAddress> promise);
+    public void resolve(String host, int port, Promise<List<InetSocketAddress>> promise);
 
     /**
      * <p>Creates {@link SocketAddress} instances synchronously in the caller thread.</p>
      */
+    @ManagedObject("The synchronous address resolver")
     public static class Sync implements SocketAddressResolver
     {
         @Override
-        public void resolve(String host, int port, Promise<SocketAddress> promise)
+        public void resolve(String host, int port, Promise<List<InetSocketAddress>> promise)
         {
             try
             {
-                InetSocketAddress result = new InetSocketAddress(host, port);
-                if (result.isUnresolved())
-                    promise.failed(new UnresolvedAddressException());
+                InetAddress[] addresses = InetAddress.getAllByName(host);
+
+                List<InetSocketAddress> result = new ArrayList<>(addresses.length);
+                for (InetAddress address : addresses)
+                    result.add(new InetSocketAddress(address, port));
+
+                if (result.isEmpty())
+                    promise.failed(new UnknownHostException());
                 else
                     promise.succeeded(result);
             }
@@ -91,6 +101,7 @@
      * });
      * </pre>
      */
+    @ManagedObject("The asynchronous address resolver")
     public static class Async implements SocketAddressResolver
     {
         private static final Logger LOG = Log.getLogger(SocketAddressResolver.class);
@@ -124,65 +135,61 @@
             return scheduler;
         }
 
+        @ManagedAttribute(value = "The timeout, in milliseconds, to resolve an address", readonly = true)
         public long getTimeout()
         {
             return timeout;
         }
 
         @Override
-        public void resolve(final String host, final int port, final Promise<SocketAddress> promise)
+        public void resolve(final String host, final int port, final Promise<List<InetSocketAddress>> promise)
         {
-            executor.execute(new Runnable()
+            executor.execute(() ->
             {
-                @Override
-                public void run()
+                Scheduler.Task task = null;
+                final AtomicBoolean complete = new AtomicBoolean();
+                if (timeout > 0)
                 {
-                    Scheduler.Task task = null;
-                    final AtomicBoolean complete = new AtomicBoolean();
-                    if (timeout > 0)
+                    final Thread thread = Thread.currentThread();
+                    task = scheduler.schedule(() ->
                     {
-                        final Thread thread = Thread.currentThread();
-                        task = scheduler.schedule(new Runnable()
-                        {
-                            @Override
-                            public void run()
-                            {
-                                if (complete.compareAndSet(false, true))
-                                {
-                                    promise.failed(new TimeoutException());
-                                    thread.interrupt();
-                                }
-                            }
-                        }, timeout, TimeUnit.MILLISECONDS);
-                    }
-
-                    try
-                    {
-                        long start = System.nanoTime();
-                        InetSocketAddress result = new InetSocketAddress(host, port);
-                        long elapsed = System.nanoTime() - start;
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("Resolved {} in {} ms", host, TimeUnit.NANOSECONDS.toMillis(elapsed));
                         if (complete.compareAndSet(false, true))
                         {
-                            if (result.isUnresolved())
-                                promise.failed(new UnresolvedAddressException());
-                            else
-                                promise.succeeded(result);
+                            promise.failed(new TimeoutException("DNS timeout " + getTimeout() + " ms"));
+                            thread.interrupt();
                         }
-                    }
-                    catch (Throwable x)
+                    }, timeout, TimeUnit.MILLISECONDS);
+                }
+
+                try
+                {
+                    long start = System.nanoTime();
+                    InetAddress[] addresses = InetAddress.getAllByName(host);
+                    long elapsed = System.nanoTime() - start;
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Resolved {} in {} ms", host, TimeUnit.NANOSECONDS.toMillis(elapsed));
+
+                    List<InetSocketAddress> result = new ArrayList<>(addresses.length);
+                    for (InetAddress address : addresses)
+                        result.add(new InetSocketAddress(address, port));
+
+                    if (complete.compareAndSet(false, true))
                     {
-                        if (complete.compareAndSet(false, true))
-                            promise.failed(x);
+                        if (result.isEmpty())
+                            promise.failed(new UnknownHostException());
+                        else
+                            promise.succeeded(result);
                     }
-                    finally
-                    {
-                        if (task != null)
-                            task.cancel();
-                        // Reset the interrupted status before releasing the thread to the pool
-                        Thread.interrupted();
-                    }
+                }
+                catch (Throwable x)
+                {
+                    if (complete.compareAndSet(false, true))
+                        promise.failed(x);
+                }
+                finally
+                {
+                    if (task != null)
+                        task.cancel();
                 }
             });
         }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java
index d4d85d8..e0ffafa 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.util;
 
 import java.io.UnsupportedEncodingException;
-import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
@@ -50,44 +49,25 @@
     @Deprecated
     public static final String __LINE_SEPARATOR = System.lineSeparator();
        
-    public static final String __ISO_8859_1="ISO-8859-1";
-    public final static String __UTF8="UTF-8";
-    public final static String __UTF16="UTF-16";
-
-    /**
-     * @deprecated Use {@link StandardCharsets#UTF_8}
-     */
-    @Deprecated
-    public final static Charset __UTF8_CHARSET=StandardCharsets.UTF_8;
-    /**
-     * @deprecated Use {@link StandardCharsets#ISO_8859_1}
-     */
-    @Deprecated
-    public final static Charset __ISO_8859_1_CHARSET=StandardCharsets.ISO_8859_1;
-    /**
-     * @deprecated Use {@link StandardCharsets#UTF_16}
-     */
-    @Deprecated
-    public final static Charset __UTF16_CHARSET=StandardCharsets.UTF_16;
-    /**
-     * @deprecated Use {@link StandardCharsets#US_ASCII}
-     */
-    @Deprecated
-    public final static Charset __US_ASCII_CHARSET=StandardCharsets.US_ASCII;
+    public static final String __ISO_8859_1="iso-8859-1";
+    public final static String __UTF8="utf-8";
+    public final static String __UTF16="utf-16";
     
     static
     {
-        CHARSETS.put("UTF-8",__UTF8);
-        CHARSETS.put("UTF8",__UTF8);
-        CHARSETS.put("UTF-16",__UTF16);
-        CHARSETS.put("UTF16",__UTF16);
-        CHARSETS.put("ISO-8859-1",__ISO_8859_1);
-        CHARSETS.put("ISO_8859_1",__ISO_8859_1);
+        CHARSETS.put("utf-8",__UTF8);
+        CHARSETS.put("utf8",__UTF8);
+        CHARSETS.put("utf-16",__UTF16);
+        CHARSETS.put("utf16",__UTF16);
+        CHARSETS.put("iso-8859-1",__ISO_8859_1);
+        CHARSETS.put("iso_8859_1",__ISO_8859_1);
     }
     
     /* ------------------------------------------------------------ */
     /** Convert alternate charset names (eg utf8) to normalized
      * name (eg UTF-8).
+     * @param s the charset to normalize
+     * @return the normalized charset (or null if normalized version not found)
      */
     public static String normalizeCharset(String s)
     {
@@ -98,6 +78,10 @@
     /* ------------------------------------------------------------ */
     /** Convert alternate charset names (eg utf8) to normalized
      * name (eg UTF-8).
+     * @param s the charset to normalize
+     * @param offset the offset in the charset
+     * @param length the length of the charset in the input param
+     * @return the normalized charset (or null if not found)
      */
     public static String normalizeCharset(String s,int offset,int length)
     {
@@ -133,6 +117,9 @@
      */
     public static String asciiToLowerCase(String s)
     {
+        if (s == null)
+            return null;
+        
         char[] c = null;
         int i=s.length();
 
@@ -223,6 +210,9 @@
     /* ------------------------------------------------------------ */
     /**
      * returns the next index of a character from the chars string
+     * @param s the input string to search
+     * @param chars the chars to look for
+     * @return the index of the character in the input stream found.
      */
     public static int indexFrom(String s,String chars)
     {
@@ -235,6 +225,10 @@
     /* ------------------------------------------------------------ */
     /**
      * replace substrings within string.
+     * @param s the input string
+     * @param sub the string to look for
+     * @param with the string to replace with
+     * @return the now replaced string
      */
     public static String replace(String s, String sub, String with)
     {
@@ -255,14 +249,15 @@
         if (c<s.length())
             buf.append(s.substring(c,s.length()));
 
-        return buf.toString();
-        
+        return buf.toString();   
     }
 
-
     /* ------------------------------------------------------------ */
     /** Remove single or double quotes.
+     * @param s the input string
+     * @return the string with quotes removed
      */
+    @Deprecated
     public static String unquote(String s)
     {
         return QuotedStringTokenizer.unquote(s);
@@ -297,6 +292,9 @@
     /* ------------------------------------------------------------ */
     /**
      * append hex digit
+     * @param buf the buffer to append to
+     * @param b the byte to append
+     * @param base the base of the hex output (almost always 16).
      * 
      */
     public static void append(StringBuilder buf,byte b,int base)
@@ -313,6 +311,12 @@
     }
 
     /* ------------------------------------------------------------ */
+    /**
+     * Append 2 digits (zero padded) to the StringBuffer
+     * 
+     * @param buf the buffer to append to
+     * @param i the value to append
+     */
     public static void append2digits(StringBuffer buf,int i)
     {
         if (i<100)
@@ -323,6 +327,12 @@
     }
     
     /* ------------------------------------------------------------ */
+    /**
+     * Append 2 digits (zero padded) to the StringBuilder
+     * 
+     * @param buf the buffer to append to
+     * @param i the value to append
+     */
     public static void append2digits(StringBuilder buf,int i)
     {
         if (i<100)
@@ -572,6 +582,8 @@
      * http://en.wikipedia.org/wiki/Security_Identifier
      * 
      * S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn
+     * @param sidBytes the SID bytes to build from
+     * @return the string SID
      */
     public static String sidBytesToString(byte[] sidBytes)
     {
@@ -619,6 +631,8 @@
      * http://en.wikipedia.org/wiki/Security_Identifier
      * 
      * S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn
+     * @param sidString the string SID
+     * @return the binary SID
      */
     public static byte[] sidStringToBytes( String sidString )
     {
@@ -707,7 +721,7 @@
             return minus?(-val):val;
         throw new NumberFormatException(string);
     }
-
+    
     /**
      * Convert String to an long. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
      * 
@@ -1028,5 +1042,17 @@
         }
         return out.toString();
     }
+    
+    /* ------------------------------------------------------------ */
+    /** The String value of an Object
+     * <p>This method calls {@link String#valueOf(Object)} unless the object is null,
+     * in which case null is returned</p>
+     * @param object The object
+     * @return String value or null
+     */
+    public static String valueOf(Object object)
+    {
+        return object==null?null:String.valueOf(object);
+    }
 
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java
index 877efab..9189e3c 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java
@@ -20,8 +20,8 @@
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -34,13 +34,13 @@
  * <p>This Trie is stored in a Tree and is unlimited in capacity</p>
  * 
  * <p>This Trie is not Threadsafe and contains no mutual exclusion 
- * or deliberate memory barriers.  It is intended for an ArrayTrie to be
+ * or deliberate memory barriers.  It is intended for an TreeTrie to be
  * built by a single thread and then used concurrently by multiple threads
  * and not mutated during that access.  If concurrent mutations of the
  * Trie is required external locks need to be applied.
  * </p>
  * 
- * @param <V>
+ * @param <V> the entry type
  */
 public class TreeTrie<V> extends AbstractTrie<V>
 {
@@ -75,6 +75,15 @@
         _nextIndex = new TreeTrie[INDEX];
         this._c=c;
     }
+    
+    @Override
+    public void clear()
+    {
+        Arrays.fill(_nextIndex,null);
+        _nextOther.clear();
+        _key=null;
+        _value=null;
+    }
 
     @Override
     public boolean put(String s, V v)
@@ -224,9 +233,43 @@
     @Override
     public V getBest(String s, int offset, int len)
     {
-        // TODO inefficient
-        byte[] b=s.substring(offset,offset+len).getBytes(StandardCharsets.ISO_8859_1);
-        return getBest(b,0,b.length);
+        TreeTrie<V> t = this;
+        for(int i=0; i < len; i++)
+        {
+            byte c=(byte)(0xff&s.charAt(offset+i));
+            int index=c>=0&&c<0x7f?__lookup[c]:-1;
+            if (index>=0)
+            {
+                if (t._nextIndex[index] == null) 
+                    break;
+                t = t._nextIndex[index];
+            }
+            else
+            {
+                TreeTrie<V> n=null;
+                for (int j=t._nextOther.size();j-->0;)
+                {
+                    n=t._nextOther.get(j);
+                    if (n._c==c)
+                        break;
+                    n=null;
+                }
+                if (n==null)
+                    break;
+                t=n;
+            }
+            
+            // Is the next Trie is a match
+            if (t._key!=null)
+            {
+                // Recurse so we can remember this possibility
+                V best=t.getBest(s,offset+i+1,len-i-1);
+                if (best!=null)
+                    return best;
+                break;
+            }
+        }
+        return t._value;
     }
     
     @Override
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java
index dc70299..fece291 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java
@@ -24,7 +24,7 @@
 
 /* ------------------------------------------------------------ */
 /** A Trie String lookup data structure.
- * @param <V>
+ * @param <V> the Trie entry type
  */
 public interface Trie<V>
 {
@@ -49,6 +49,7 @@
     /* ------------------------------------------------------------ */
     /** Get and exact match from a String key
      * @param s The key
+     * @return the value for the string key
      */
     public V get(String s);
 
@@ -57,6 +58,7 @@
      * @param s The key
      * @param offset The offset within the string of the key
      * @param len the length of the key
+     * @return the value for the string / offset / length
      */
     public V get(String s,int offset,int len);
 
@@ -121,4 +123,7 @@
     /* ------------------------------------------------------------ */
     public boolean isCaseInsensitive();
 
+    /* ------------------------------------------------------------ */
+    public void clear();
+
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java
index 61b05eb..168d09d 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java
@@ -29,6 +29,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 import org.eclipse.jetty.util.annotation.Name;
 import org.eclipse.jetty.util.log.Log;
@@ -173,7 +174,9 @@
     /** Array to List.
      * <p>
      * Works like {@link Arrays#asList(Object...)}, but handles null arrays.
+     * @param a the array to convert to a list
      * @return a list backed by the array.
+     * @param <T> the array and list entry type
      */
     public static <T> List<T> asList(T[] a)
     {
@@ -362,6 +365,19 @@
      * @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( char c )
+    {
+        int d= ((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
+        if (d<0 || d>15)
+            throw new NumberFormatException("!hex "+c);
+        return d;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @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);
@@ -483,12 +499,19 @@
     public static Object call(Class<?> oClass, String methodName, Object obj, Object[] arg)
        throws InvocationTargetException, NoSuchMethodException
     {
+        Objects.requireNonNull(oClass,"Class cannot be null");
+        Objects.requireNonNull(methodName,"Method name cannot be null");
+        if (StringUtil.isBlank(methodName))
+        {
+            throw new IllegalArgumentException("Method name cannot be blank");
+        }
+        
         // Lets just try all methods for now
         for (Method method : oClass.getMethods())
         {
             if (!method.getName().equals(methodName))
                 continue;            
-            if (method.getParameterTypes().length != arg.length)
+            if (method.getParameterCount() != arg.length)
                 continue;
             if (Modifier.isStatic(method.getModifiers()) != (obj == null))
                 continue;
@@ -512,7 +535,7 @@
         {
             if (!method.getName().equals(methodName))
                 continue;            
-            if (method.getParameterTypes().length != arg.length+1)
+            if (method.getParameterCount() != arg.length+1)
                 continue;
             if (!method.getParameterTypes()[arg.length].isArray())
                 continue;
@@ -539,9 +562,17 @@
 
     public static Object construct(Class<?> klass, Object[] arguments) throws InvocationTargetException, NoSuchMethodException
     {
+        Objects.requireNonNull(klass,"Class cannot be null");
+        
         for (Constructor<?> constructor : klass.getConstructors())
         {
-            if (constructor.getParameterTypes().length != arguments.length)
+            if (arguments == null)
+            {
+                // null arguments in .newInstance() is allowed
+                if (constructor.getParameterCount() != 0)
+                    continue;
+            }
+            else if (constructor.getParameterCount() != arguments.length)
                 continue;
 
             try
@@ -558,20 +589,34 @@
     
     public static Object construct(Class<?> klass, Object[] arguments, Map<String, Object> namedArgMap) throws InvocationTargetException, NoSuchMethodException
     {
+        Objects.requireNonNull(klass,"Class cannot be null");
+        Objects.requireNonNull(namedArgMap,"Named Argument Map cannot be null");
+        
         for (Constructor<?> constructor : klass.getConstructors())
         {
-            if (constructor.getParameterTypes().length != arguments.length)
+            if (arguments == null)
+            {
+                // null arguments in .newInstance() is allowed
+                if (constructor.getParameterCount() != 0)
+                    continue;
+            }
+            else if (constructor.getParameterCount() != arguments.length)
                 continue;
 
             try
             {
                 Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
                 
-                // target has no annotations
-                if ( parameterAnnotations == null || parameterAnnotations.length == 0 )
+                if (arguments == null || arguments.length == 0)
                 {
                     if (LOG.isDebugEnabled())
-                        LOG.debug("Target has no parameter annotations");
+                        LOG.debug("Constructor has no arguments");
+                    return constructor.newInstance(arguments);
+                }
+                else if (parameterAnnotations == null || parameterAnnotations.length == 0)
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Constructor has no parameter annotations");
                     return constructor.newInstance(arguments);
                 }
                 else
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
index 9d0e068..3d3671e 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
@@ -18,39 +18,35 @@
 
 package org.eclipse.jetty.util;
 
+import java.net.URI;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 
+import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
 
-
-/* ------------------------------------------------------------ */
-/** URI Holder.
+/** 
+ * URI Utility methods.
+ * <p>
  * 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
+ * </p>
  * 
+ * @see UrlEncoded
  */
 public class URIUtil
     implements Cloneable
 {
+    private static final Logger LOG = Log.getLogger(URIUtil.class);
     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 Charset __CHARSET;
-
-    static
-    {
-        String charset = System.getProperty("org.eclipse.jetty.util.URI.charset");
-        __CHARSET = charset == null ? StandardCharsets.UTF_8 : Charset.forName(charset);
-    }
+    public static final Charset __CHARSET=StandardCharsets.UTF_8 ;
 
     private URIUtil()
     {}
@@ -67,10 +63,10 @@
         if (path==null || path.length()==0)
             return path;
         
-        StringBuilder buf = encodePath(null,path);
+        StringBuilder buf = encodePath(null,path,0);
         return buf==null?path:buf.toString();
     }
-        
+
     /* ------------------------------------------------------------ */
     /** Encode a URI path.
      * @param path The path the encode
@@ -79,11 +75,21 @@
      */
     public static StringBuilder encodePath(StringBuilder buf, String path)
     {
+        return encodePath(buf,path,0);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 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.
+     */
+    private static StringBuilder encodePath(StringBuilder buf, String path, int offset)
+    {
         byte[] bytes=null;
         if (buf==null)
         {
-        loop:
-            for (int i=0;i<path.length();i++)
+            loop: for (int i=offset;i<path.length();i++)
             {
                 char c=path.charAt(i);
                 switch(c)
@@ -92,11 +98,19 @@
                     case '?':
                     case ';':
                     case '#':
-                    case '\'':
                     case '"':
+                    case '\'':
                     case '<':
                     case '>':
                     case ' ':
+                    case '[':
+                    case '\\':
+                    case ']':
+                    case '^':
+                    case '`':
+                    case '{':
+                    case '|':
+                    case '}':
                         buf=new StringBuilder(path.length()*2);
                         break loop;
                     default:
@@ -106,100 +120,147 @@
                             buf=new StringBuilder(path.length()*2);
                             break loop;
                         }
-                       
                 }
             }
             if (buf==null)
                 return null;
         }
-        
-        synchronized(buf)
+
+        int i;
+
+        loop: for (i=offset;i<path.length();i++)
         {
-            if (bytes!=null)
+            char c=path.charAt(i);
+            switch(c)
             {
-                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;
+                case '[':
+                    buf.append("%5B");
+                    continue;
+                case '\\':
+                    buf.append("%5C");
+                    continue;
+                case ']':
+                    buf.append("%5D");
+                    continue;
+                case '^':
+                    buf.append("%5E");
+                    continue;
+                case '`':
+                    buf.append("%60");
+                    continue;
+                case '{':
+                    buf.append("%7B");
+                    continue;
+                case '|':
+                    buf.append("%7C");
+                    continue;
+                case '}':
+                    buf.append("%7D");
+                    continue;
+
+                default:
+                    if (c>127)
                     {
-                      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;
+                        bytes=path.getBytes(URIUtil.__CHARSET);
+                        break loop;
                     }
-                }
-                
+                    buf.append(c);
             }
-            else
+        }
+
+        if (bytes!=null)
+        {
+            for (;i<bytes.length;i++)
             {
-                for (int i=0;i<path.length();i++)
+                byte c=bytes[i];
+                switch(c)
                 {
-                    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;
-                    }
+                    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;
+                    case '[':
+                        buf.append("%5B");
+                        continue;
+                    case '\\':
+                        buf.append("%5C");
+                        continue;
+                    case ']':
+                        buf.append("%5D");
+                        continue;
+                    case '^':
+                        buf.append("%5E");
+                        continue;
+                    case '`':
+                        buf.append("%60");
+                        continue;
+                    case '{':
+                        buf.append("%7B");
+                        continue;
+                    case '|':
+                        buf.append("%7C");
+                        continue;
+                    case '}':
+                        buf.append("%7D");
+                        continue;
+                    default:
+                        if (c<0)
+                        {
+                            buf.append('%');
+                            TypeUtil.toHex(c,buf);
+                        }
+                        else
+                            buf.append((char)c);
                 }
             }
         }
@@ -220,33 +281,29 @@
     {
         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;
+                    break;
                 }
             }
             if (buf==null)
                 return null;
         }
-        
-        synchronized(buf)
+
+        for (int i=0;i<path.length();i++)
         {
-            for (int i=0;i<path.length();i++)
+            char c=path.charAt(i);
+            if (c=='%' || encode.indexOf(c)>=0)
             {
-                char c=path.charAt(i);
-                if (c=='%' || encode.indexOf(c)>=0)
-                {
-                    buf.append('%');
-                    StringUtil.append(buf,(byte)(0xff&c),16);
-                }
-                else
-                    buf.append(c);
+                buf.append('%');
+                StringUtil.append(buf,(byte)(0xff&c),16);
             }
+            else
+                buf.append(c);
         }
 
         return buf;
@@ -254,135 +311,175 @@
     
     /* ------------------------------------------------------------ */
     /* 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++)
+        return decodePath(path,0,path.length());
+    }
+
+    /* ------------------------------------------------------------ */
+    /* Decode a URI path and strip parameters of UTF-8 path
+     */
+    public static String decodePath(String path, int offset, int length)
+    {
+        try
+        {
+            Utf8StringBuilder builder=null;
+            int end=offset+length;
+            for (int i=offset;i<end;i++)
+            {
+                char c = path.charAt(i);
+                switch(c)
+                {
+                    case '%':
+                        if (builder==null)
+                        {
+                            builder=new Utf8StringBuilder(path.length());
+                            builder.append(path,offset,i-offset);
+                        }
+                        if ((i+2)<end)
+                        {
+                            char u=path.charAt(i+1);
+                            if (u=='u')
+                            {
+                                // TODO this is wrong. This is a codepoint not a char
+                                builder.append((char)(0xffff&TypeUtil.parseInt(path,i+2,4,16)));
+                                i+=5;
+                            }
+                            else
+                            {
+                                builder.append((byte)(0xff&(TypeUtil.convertHexDigit(u)*16+TypeUtil.convertHexDigit(path.charAt(i+2)))));
+                                i+=2;
+                            }
+                        }
+                        else
+                        {
+                            throw new IllegalArgumentException("Bad URI % encoding");
+                        }
+
+                        break;
+
+                    case ';':
+                        if (builder==null)
+                        {
+                            builder=new Utf8StringBuilder(path.length());
+                            builder.append(path,offset,i-offset);
+                        }
+                        
+                        while(++i<end)
+                        {
+                            if (path.charAt(i)=='/')
+                            {
+                                builder.append('/');
+                                break;
+                            }
+                        }
+                        
+                        break;
+
+                    default:
+                        if (builder!=null)
+                            builder.append(c);
+                        break;
+                }
+            }
+
+            if (builder!=null)
+                return builder.toString();
+            if (offset==0 && length==path.length())
+                return path;
+            return path.substring(offset,end);   
+        }
+        catch(NotUtf8Exception e)
+        {
+            LOG.warn(path.substring(offset,offset+length)+" "+e);
+            LOG.debug(e);
+            return decodeISO88591Path(path,offset,length);
+        }
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /* Decode a URI path and strip parameters of ISO-8859-1 path
+     */
+    private static String decodeISO88591Path(String path, int offset, int length)
+    {
+        StringBuilder builder=null;
+        int end=offset+length;
+        for (int i=offset;i<end;i++)
         {
             char c = path.charAt(i);
-
-            if (c=='%' && (i+2)<len)
+            switch(c)
             {
-                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;
+                case '%':
+                    if (builder==null)
+                    {
+                        builder=new StringBuilder(path.length());
+                        builder.append(path,offset,i-offset);
+                    }
+                    if ((i+2)<end)
+                    {
+                        char u=path.charAt(i+1);
+                        if (u=='u')
+                        {
+                            // TODO this is wrong. This is a codepoint not a char
+                            builder.append((char)(0xffff&TypeUtil.parseInt(path,i+2,4,16)));
+                            i+=5;
+                        }
+                        else
+                        {
+                            builder.append((byte)(0xff&(TypeUtil.convertHexDigit(u)*16+TypeUtil.convertHexDigit(path.charAt(i+2)))));
+                            i+=2;
+                        }
+                    }
+                    else
+                    {
+                        throw new IllegalArgumentException();
+                    }
+                    
+                    break;
+                    
+                case ';':
+                    if (builder==null)
+                    {
+                        builder=new StringBuilder(path.length());
+                        builder.append(path,offset,i-offset);
+                    }
+                    while(++i<end)
+                    {
+                        if (path.charAt(i)=='/')
+                        {
+                            builder.append('/');
+                            break;
+                        }
+                    }
+                    break;
+                    
+                    
+                default:
+                    if (builder!=null)
+                        builder.append(c);
+                    break;
             }
-            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)
-            {
-                String s=new String(bytes,0,b,__CHARSET);
-                s.getChars(0,s.length(),chars,n);
-                n+=s.length();
-                b=0;
-            }
-            
-            chars[n++]=c;
         }
 
-        if (chars==null)
+        if (builder!=null)
+            return builder.toString();
+        if (offset==0 && length==path.length())
             return path;
-
-        // if we have a remaining sequence of bytes
-        if (b>0)
-        {
-            String s=new String(bytes,0,b,__CHARSET);
-            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 new String(buf,offset,length,__CHARSET);
-        return new String(bytes,0,n,__CHARSET);
+        return path.substring(offset,end);        
     }
 
     
     /* ------------------------------------------------------------ */
-    /** Add two URI path segments.
+    /** Add two encoded 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)
+    public static String addEncodedPaths(String p1, String p2)
     {
         if (p1==null || p1.length()==0)
         {
@@ -427,10 +524,59 @@
 
         return buf.toString();
     }
-    
+
+    /* ------------------------------------------------------------ */
+    /** Add two Decoded URI path segments.
+     * Handles null and empty paths.  Path and query params (eg ?a=b or
+     * ;JSESSIONID=xxx) are not handled
+     * @param p1 URI path segment (should be decoded)
+     * @param p2 URI path segment (should be decoded)
+     * @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;
+        
+        boolean p1EndsWithSlash = p1.endsWith(SLASH);
+        boolean p2StartsWithSlash = p2.startsWith(SLASH);
+        
+        if (p1EndsWithSlash && p2StartsWithSlash)
+        {
+            if (p2.length()==1)
+                return p1;
+            if (p1.length()==1)
+                return p2;
+        }
+        
+        StringBuilder buf = new StringBuilder(p1.length()+p2.length()+2);
+        buf.append(p1);
+        
+        if (p1.endsWith(SLASH))
+        {
+            if (p2.startsWith(SLASH))
+                buf.setLength(buf.length()-1);
+        }
+        else
+        {
+            if (!p2.startsWith(SLASH))
+                buf.append(SLASH);
+        }
+        buf.append(p2);
+
+        return buf.toString();
+    }
     /* ------------------------------------------------------------ */
     /** Return the parent Path.
      * Treat a URI like a directory path and return the parent directory.
+     * @param p the path to return a parent reference to
+     * @return the parent path of the URI
      */
     public static String parentPath(String p)
     {
@@ -446,7 +592,7 @@
     /** 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 
+     * @param path the path to convert
      * @return path or null.
      */
     public static String canonicalPath(String path)
@@ -580,8 +726,8 @@
     /* ------------------------------------------------------------ */
     /** Convert a path to a compact form.
      * All instances of "//" and "///" etc. are factored out to single "/" 
-     * @param path 
-     * @return path
+     * @param path the path to compact 
+     * @return the compacted path 
      */
     public static String compactPath(String path)
     {
@@ -614,7 +760,7 @@
         if (state<2)
             return path;
         
-        StringBuffer buf = new StringBuffer(path.length());
+        StringBuilder buf = new StringBuilder(path.length());
         buf.append(path,0,i);
         
         loop2:
@@ -667,11 +813,11 @@
     /* ------------------------------------------------------------ */
     /**
      * Create a new URI from the arguments, handling IPv6 host encoding and default ports
-     * @param scheme
-     * @param server
-     * @param port
-     * @param path
-     * @param query
+     * @param scheme the URI scheme
+     * @param server the URI server
+     * @param port the URI port
+     * @param path the URI path
+     * @param query the URI query
      * @return A String URI
      */
     public static String newURI(String scheme,String server, int port,String path,String query)
@@ -686,9 +832,9 @@
     /* ------------------------------------------------------------ */
     /**
      * Create a new URI StringBuilder from the arguments, handling IPv6 host encoding and default ports
-     * @param scheme
-     * @param server
-     * @param port
+     * @param scheme the URI scheme
+     * @param server the URI server
+     * @param port the URI port
      * @return a StringBuilder containing URI prefix
      */
     public static StringBuilder newURIBuilder(String scheme,String server, int port)
@@ -700,11 +846,11 @@
 
     /* ------------------------------------------------------------ */
     /** 
-     * Append scheme, host and port URI prefix, handling IPv6 address encoding and default ports</p>
+     * Append scheme, host and port URI prefix, handling IPv6 address encoding and default ports
      * @param url StringBuilder to append to
-     * @param scheme
-     * @param server
-     * @param port
+     * @param scheme the URI scheme
+     * @param server the URI server
+     * @param port the URI port
      */
     public static void appendSchemeHostPort(StringBuilder url,String scheme,String server, int port)
     {
@@ -732,11 +878,11 @@
     
     /* ------------------------------------------------------------ */
     /** 
-     * Append scheme, host and port URI prefix, handling IPv6 address encoding and default ports</p>
+     * Append scheme, host and port URI prefix, handling IPv6 address encoding and default ports
      * @param url StringBuffer to append to
-     * @param scheme
-     * @param server
-     * @param port
+     * @param scheme the URI scheme
+     * @param server the URI server
+     * @param port the URI port
      */
     public static void appendSchemeHostPort(StringBuffer url,String scheme,String server, int port)
     {
@@ -792,7 +938,47 @@
         }
         return a==lenA && b==lenB;
     }
+
+    public static boolean equalsIgnoreEncodings(URI uriA, URI uriB)
+    {
+        if (uriA.equals(uriB))
+            return true;
+
+        if (uriA.getScheme()==null)
+        {
+            if (uriB.getScheme()!=null)
+                return false;
+        }
+        else if (!uriA.getScheme().equals(uriB.getScheme()))
+            return false;
+
+        if (uriA.getAuthority()==null)
+        {
+            if (uriB.getAuthority()!=null)
+                return false;
+        }
+        else if (!uriA.getAuthority().equals(uriB.getAuthority()))
+            return false;
+
+        return equalsIgnoreEncodings(uriA.getPath(),uriB.getPath());
+    }
+
+    /**
+     * @param uri A URI to add the path to
+     * @param path A decoded path element
+     * @return URI with path added.
+     */
+    public static URI addPath(URI uri, String path)
+    {
+        String base = uri.toASCIIString();
+        StringBuilder buf = new StringBuilder(base.length()+path.length()*3);
+        buf.append(base);
+        if (buf.charAt(base.length()-1)!='/')
+            buf.append('/');
+
+        int offset=path.charAt(0)=='/'?1:0;
+        encodePath(buf,path,offset);
+
+        return URI.create(buf.toString());
+    }
 }
-
-
-
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java b/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
index 9b8b3d4..3a9986e 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
@@ -29,26 +29,29 @@
 import java.util.List;
 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".
+/** 
+ * 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>
+ * </p>
+ * <b>Notes</b>
+ * <p>
  * 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>
  * <p>
  * The hashtable either contains String single values, vectors
  * of String or arrays of Strings.
+ * </p>
  * <p>
  * This class is only partially synchronised.  In particular, simple
  * get operations are not protected from concurrent updates.
+ * </p>
  *
  * @see java.net.URLEncoder
  */
@@ -87,23 +90,24 @@
     
     public UrlEncoded(String query)
     {
-        decodeTo(query,this,ENCODING,-1);
+        decodeTo(query,this,ENCODING);
     }
 
     /* ----------------------------------------------------------------- */
     public void decode(String query)
     {
-        decodeTo(query,this,ENCODING,-1);
+        decodeTo(query,this,ENCODING);
     }
     
     /* ----------------------------------------------------------------- */
     public void decode(String query,Charset charset)
     {
-        decodeTo(query,this,charset,-1);
+        decodeTo(query,this,charset);
     }
     
     /* -------------------------------------------------------------- */
-    /** Encode Hashtable with % encoding.
+    /** Encode MultiMap with % encoding for UTF8 sequences.
+     * @return the MultiMap as a string with % encoding
      */
     public String encode()
     {
@@ -111,7 +115,9 @@
     }
     
     /* -------------------------------------------------------------- */
-    /** Encode Hashtable with % encoding.
+    /** Encode MultiMap with % encoding for arbitrary Charset sequences.
+     * @param charset the charset to use for encoding
+     * @return the MultiMap as a string encoded with % encodings
      */
     public String encode(Charset charset)
     {
@@ -119,9 +125,12 @@
     }
     
     /* -------------------------------------------------------------- */
-    /** Encode Hashtable with % encoding.
+    /** 
+     * Encode MultiMap with % encoding.
+     * @param charset the charset to encode with
      * @param equalsForNullValue if True, then an '=' is always used, even
-     * for parameters without a value. e.g. "blah?a=&b=&c=".
+     * for parameters without a value. e.g. <code>"blah?a=&amp;b=&amp;c="</code>.
+     * @return the MultiMap as a string encoded with % encodings
      */
     public synchronized String encode(Charset charset, boolean equalsForNullValue)
     {
@@ -129,9 +138,12 @@
     }
     
     /* -------------------------------------------------------------- */
-    /** Encode Hashtable with % encoding.
+    /** Encode MultiMap with % encoding.
+     * @param map the map to encode
+     * @param charset the charset to use for encoding (uses default encoding if null)
      * @param equalsForNullValue if True, then an '=' is always used, even
-     * for parameters without a value. e.g. "blah?a=&b=&c=".
+     * for parameters without a value. e.g. <code>"blah?a=&amp;b=&amp;c="</code>.
+     * @return the MultiMap as a string encoded with % encodings. 
      */
     public static String encode(MultiMap<String> map, Charset charset, boolean equalsForNullValue)
     {
@@ -190,21 +202,31 @@
     /* -------------------------------------------------------------- */
     /** Decoded parameters to Map.
      * @param content the string containing the encoded parameters
+     * @param map the MultiMap to put parsed query parameters into
+     * @param charset the charset to use for decoding
      */
-    public static void decodeTo(String content, MultiMap<String> map, String charset, int maxKeys)
+    public static void decodeTo(String content, MultiMap<String> map, String charset)
     {
-        decodeTo(content,map,charset==null?null:Charset.forName(charset),maxKeys);
+        decodeTo(content,map,charset==null?null:Charset.forName(charset));
     }
     
     /* -------------------------------------------------------------- */
     /** Decoded parameters to Map.
      * @param content the string containing the encoded parameters
+     * @param map the MultiMap to put parsed query parameters into
+     * @param charset the charset to use for decoding
      */
-    public static void decodeTo(String content, MultiMap<String> map, Charset charset, int maxKeys)
+    public static void decodeTo(String content, MultiMap<String> map, Charset charset)
     {
         if (charset==null)
             charset=ENCODING;
 
+        if (charset==StandardCharsets.UTF_8)
+        {
+            decodeUtf8To(content,0,content.length(),map);
+            return;
+        }
+        
         synchronized(map)
         {
             String key = null;
@@ -232,8 +254,6 @@
                       }
                       key = null;
                       value=null;
-                      if (maxKeys>0 && map.size()>maxKeys)
-                          throw new IllegalStateException("Form too many keys");
                       break;
                   case '=':
                       if (key!=null)
@@ -271,13 +291,19 @@
     }
 
     /* -------------------------------------------------------------- */
+    public static void decodeUtf8To(String query, MultiMap<String> map)
+    {
+        decodeUtf8To(query,0,query.length(),map);
+    }
+    
+    /* -------------------------------------------------------------- */
     /** Decoded parameters to Map.
-     * @param raw the byte[] containing the encoded parameters
+     * @param query the string 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<String> map)
+    public static void decodeUtf8To(String query,int offset, int length, MultiMap<String> map)
     {
         Utf8StringBuilder buffer = new Utf8StringBuilder();
         synchronized(map)
@@ -288,89 +314,54 @@
             int end=offset+length;
             for (int i=offset;i<end;i++)
             {
-                byte b=raw[i];
-                try
+                char c=query.charAt(i);
+                switch (c)
                 {
-                    switch ((char)(0xff&b))
-                    {
-                        case '&':
-                            value = buffer.toReplacedString();
-                            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 '&':
+                        value = buffer.toReplacedString();
+                        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.toReplacedString();
-                            buffer.reset();
+                    case '=':
+                        if (key!=null)
+                        {
+                            buffer.append(c);
                             break;
+                        }
+                        key = buffer.toReplacedString();
+                        buffer.reset();
+                        break;
 
-                        case '+':
-                            buffer.append((byte)' ');
-                            break;
+                    case '+':
+                        buffer.append((byte)' ');
+                        break;
 
-                        case '%':
-                            if (i+2<end)
-                            {
-                                if ('u'==raw[i+1])
-                                {
-                                    i++;
-                                    if (i+4<end)
-                                    {
-                                        byte top=raw[++i];
-                                        byte hi=raw[++i];
-                                        byte lo=raw[++i];
-                                        byte bot=raw[++i];
-                                        buffer.getStringBuilder().append(Character.toChars((convertHexDigit(top)<<12) +(convertHexDigit(hi)<<8) + (convertHexDigit(lo)<<4) +convertHexDigit(bot)));
-                                    }
-                                    else
-                                    {
-                                        buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
-                                        i=end;
-                                    }
-                                }
-                                else
-                                {
-                                    byte hi=raw[++i];
-                                    byte lo=raw[++i];
-                                    buffer.append((byte)((convertHexDigit(hi)<<4) + convertHexDigit(lo)));
-                                }
-                            }
-                            else
-                            {
-                                buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
-                                i=end;
-                            }
-                            break;
-                            
-                        default:
-                            buffer.append(b);
-                            break;
-                    }
-                }
-                catch(NotUtf8Exception e)
-                {
-                    LOG.warn(e.toString());
-                    LOG.debug(e);
-                }
-                catch(NumberFormatException e)
-                {
-                    buffer.append(Utf8Appendable.REPLACEMENT_UTF8,0,3);
-                    LOG.warn(e.toString());
-                    LOG.debug(e);
+                    case '%':
+                        if (i+2<end)
+                        {
+                            char hi=query.charAt(++i);
+                            char lo=query.charAt(++i);
+                            buffer.append(decodeHexByte(hi,lo));
+                        }
+                        else
+                        {
+                            throw new Utf8Appendable.NotUtf8Exception("Incomplete % encoding");
+                        }
+                        break;
+                        
+                    default:
+                        buffer.append(c);
+                        break;
                 }
             }
             
@@ -388,10 +379,13 @@
     }
 
     /* -------------------------------------------------------------- */
-    /** Decoded parameters to Map.
+    /** Decoded parameters to MultiMap, using ISO8859-1 encodings.
+     * 
      * @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
+     * @param maxLength maximum length of form to read
+     * @param maxKeys maximum number of keys to read or -1 for no limit
+     * @throws IOException if unable to decode inputstream as ISO8859-1
      */
     public static void decode88591To(InputStream in, MultiMap<String> map, int maxLength, int maxKeys)
     throws IOException
@@ -442,26 +436,8 @@
                         
                     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)));
-                        }
+                        int code1=in.read();
+                        buffer.append(decodeHexChar(code0,code1));
                         break;
                      
                     default:
@@ -489,7 +465,9 @@
     /** 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
+     * @param maxLength maximum form length to decode
+     * @param maxKeys the maximum number of keys to read or -1 for no limit 
+     * @throws IOException if unable to decode input stream
      */
     public static void decodeUtf8To(InputStream in, MultiMap<String> map, int maxLength, int maxKeys)
     throws IOException
@@ -505,99 +483,51 @@
             int totalLength=0;
             while ((b=in.read())>=0)
             {
-                try
+                switch ((char) b)
                 {
-                    switch ((char) b)
-                    {
-                        case '&':
-                            value = buffer.toReplacedString();
-                            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 '&':
+                        value = buffer.toReplacedString();
+                        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 has too many keys");
+                        break;
 
-                        case '=':
-                            if (key!=null)
-                            {
-                                buffer.append((byte)b);
-                                break;
-                            }
-                            key = buffer.toReplacedString(); 
-                            buffer.reset();
-                            break;
-
-                        case '+':
-                            buffer.append((byte)' ');
-                            break;
-
-                        case '%':
-                            int code0=in.read();
-                            boolean decoded=false;
-                            if ('u'==code0)
-                            {
-                                code0=in.read(); // XXX: we have to read the next byte, otherwise code0 is always 'u'
-                                if (code0>=0)
-                                {
-                                    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)));
-                                                decoded=true;
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                            else if (code0>=0)
-                            {
-                                int code1=in.read();
-                                if (code1>=0)
-                                {
-                                    buffer.append((byte)((convertHexDigit(code0)<<4)+convertHexDigit(code1)));
-                                    decoded=true;
-                                }
-                            }
-                            
-                            if (!decoded)
-                                buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
-
-                            break;
-                          
-                        default:
+                    case '=':
+                        if (key!=null)
+                        {
                             buffer.append((byte)b);
                             break;
-                    }
-                }
-                catch(NotUtf8Exception e)
-                {
-                    LOG.warn(e.toString());
-                    LOG.debug(e);
-                }
-                catch(NumberFormatException e)
-                {
-                    buffer.append(Utf8Appendable.REPLACEMENT_UTF8,0,3);
-                    LOG.warn(e.toString());
-                    LOG.debug(e);
+                        }
+                        key = buffer.toReplacedString();
+                        buffer.reset();
+                        break;
+
+                    case '+':
+                        buffer.append((byte)' ');
+                        break;
+
+                    case '%':
+                        char code0= (char) in.read();
+                        char code1= (char) in.read();
+                        buffer.append(decodeHexByte(code0,code1));
+                        break;
+                      
+                    default:
+                        buffer.append((byte)b);
+                        break;
                 }
                 if (maxLength>=0 && (++totalLength > maxLength))
-                    throw new IllegalStateException("Form too large");
+                    throw new IllegalStateException("Form is too large");
             }
             
             if (key != null)
@@ -620,12 +550,18 @@
         StringWriter buf = new StringWriter(8192);
         IO.copy(input,buf,maxLength);
         
-        decodeTo(buf.getBuffer().toString(),map,StandardCharsets.UTF_16,maxKeys);
+        // TODO implement maxKeys
+        decodeTo(buf.getBuffer().toString(),map,StandardCharsets.UTF_16);
     }
 
     /* -------------------------------------------------------------- */
     /** Decoded parameters to Map.
      * @param in the stream containing the encoded parameters
+     * @param map the MultiMap to decode into
+     * @param charset the charset to use for decoding
+     * @param maxLength the maximum length of the form to decode
+     * @param maxKeys the maximum number of keys to decode
+     * @throws IOException if unable to decode input stream
      */
     public static void decodeTo(InputStream in, MultiMap<String> map, String charset, int maxLength, int maxKeys)
     throws IOException
@@ -650,6 +586,11 @@
     /* -------------------------------------------------------------- */
     /** Decoded parameters to Map.
      * @param in the stream containing the encoded parameters
+     * @param map the MultiMap to decode into
+     * @param charset the charset to use for decoding
+     * @param maxLength the maximum length of the form to decode
+     * @param maxKeys the maximum number of keys to decode
+     * @throws IOException if unable to decode input stream
      */
     public static void decodeTo(InputStream in, MultiMap<String> map, Charset charset, int maxLength, int maxKeys)
     throws IOException
@@ -708,7 +649,7 @@
                             key = null;
                             value=null;
                             if (maxKeys>0 && map.size()>maxKeys)
-                                throw new IllegalStateException("Form too many keys");
+                                throw new IllegalStateException("Form has too many keys");
                             break;
                         case '=':
                             if (key!=null)
@@ -725,27 +666,8 @@
                             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));
-                            }
+                            int code1=in.read();
+                            output.write(decodeHexChar(code0,code1));
                             break;
                         default:
                             output.write(c);
@@ -754,7 +676,7 @@
 
                     totalLength++;
                     if (maxLength>=0 && totalLength > maxLength)
-                        throw new IllegalStateException("Form too large");
+                        throw new IllegalStateException("Form is too large");
                 }
 
                 size=output.size();
@@ -769,11 +691,28 @@
             }
         }
     }
+
+    /* -------------------------------------------------------------- */
+    /** Decode String with % encoding.
+     * This method makes the assumption that the majority of calls
+     * will need no decoding.
+     * @param encoded the encoded string to decode
+     * @return the decoded string
+     */
+    public static String decodeString(String encoded)
+    {
+        return decodeString(encoded,0,encoded.length(),ENCODING);
+    }
     
     /* -------------------------------------------------------------- */
     /** Decode String with % encoding.
      * This method makes the assumption that the majority of calls
      * will need no decoding.
+     * @param encoded the encoded string to decode
+     * @param offset the offset in the encoded string to decode from
+     * @param length the length of characters in the encoded string to decode
+     * @param charset the charset to use for decoding
+     * @return the decoded string
      */
     public static String decodeString(String encoded,int offset,int length,Charset charset)
     {
@@ -814,42 +753,10 @@
                     
                     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 e)
-                        {
-                            LOG.warn(e.toString());
-                            LOG.debug(e);
-                            buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT);  
-                        }
+                        int o=offset+i+1;
+                        i+=2;
+                        byte b=(byte)TypeUtil.parseInt(encoded,o,2,16);
+                        buffer.append(b);
                     }
                     else
                     {
@@ -913,44 +820,15 @@
                         {   
                             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(Exception e)
-                                {   
-                                    LOG.warn(e.toString());
-                                    LOG.debug(e);
-                                    ba[n++] = (byte)'?';
-                                }
+                                int o=offset+i+1;
+                                i+=3;
+                                ba[n]=(byte)TypeUtil.parseInt(encoded,o,2,16);
+                                n++;
                             }
                             else
                             {
-                                    ba[n++] = (byte)'?';
-                                    i=length;
+                                ba[n++] = (byte)'?';
+                                i=length;
                             }
                         }
                         else if (c=='+')
@@ -986,12 +864,35 @@
 
             return buffer.toString();
         }
-
+    }
+    
+    private static char decodeHexChar(int hi, int lo)
+    {
+        try
+        {
+            return (char) ((convertHexDigit(hi) << 4) + convertHexDigit(lo));
+        }
+        catch(NumberFormatException e)
+        {
+            throw new IllegalArgumentException("Not valid encoding '%" + (char) hi + (char) lo + "'");
+        }
+    }
+    
+    private static byte decodeHexByte(char hi, char lo)
+    {
+        try
+        {
+            return (byte) ((convertHexDigit(hi) << 4) + convertHexDigit(lo));
+        }
+        catch(NumberFormatException e)
+        {
+            throw new IllegalArgumentException("Not valid encoding '%" + hi + lo + "'");
+        }
     }
     
     /* ------------------------------------------------------------ */
     /** Perform URL encoding.
-     * @param string 
+     * @param string the string to encode
      * @return encoded string.
      */
     public static String encodeString(String string)
@@ -1001,7 +902,8 @@
     
     /* ------------------------------------------------------------ */
     /** Perform URL encoding.
-     * @param string 
+     * @param string the string to encode
+     * @param charset the charset to use for encoding
      * @return encoded string.
      */
     public static String encodeString(String string,Charset charset)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java
index 0c8df25..bd99090 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java
@@ -37,7 +37,7 @@
  *
  * License information for Bjoern Hoehrmann's code:
  *
- * Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ * Copyright (c) 2008-2009 Bjoern Hoehrmann &lt;bjoern@hoehrmann.de&gt;
  * 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:
@@ -98,6 +98,58 @@
         _state = UTF8_ACCEPT;
     }
 
+    
+    private void checkCharAppend() throws IOException
+    {
+        if (_state != UTF8_ACCEPT)
+        {
+            _appendable.append(REPLACEMENT);
+            int state=_state;
+            _state=UTF8_ACCEPT;
+            throw new NotUtf8Exception("char appended in state "+state);
+        }
+    }
+    
+    public void append(char c)
+    {
+        try
+        {
+            checkCharAppend();
+            _appendable.append(c); 
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void append(String s)
+    {
+        try
+        {
+            checkCharAppend();
+            _appendable.append(s); 
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    public void append(String s,int offset,int length)
+    {
+        try
+        {
+            checkCharAppend();
+            _appendable.append(s,offset,offset+length); 
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    
     public void append(byte b)
     {
         try
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8LineParser.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8LineParser.java
index d8ebc48..137d095 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8LineParser.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8LineParser.java
@@ -20,6 +20,8 @@
 
 import java.nio.ByteBuffer;
 
+import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
+
 /**
  * Stateful parser for lines of UTF8 formatted text, looking for <code>"\n"</code> as a line termination character.
  * <p>
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/ManagedAttribute.java b/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/ManagedAttribute.java
index 7ea6fa2..821d37e 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/ManagedAttribute.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/ManagedAttribute.java
@@ -25,14 +25,13 @@
 import java.lang.annotation.Target;
 
 /**
- * The @ManagedAttribute annotation is used to indicate that a given method 
+ * The <code>&#064;ManagedAttribute</code> annotation is used to indicate that a given method 
  * exposes a JMX attribute. This annotation is placed always on the reader 
  * method of a given attribute. Unless it is marked as read-only in the 
  * configuration of the annotation a corresponding setter is looked for 
  * following normal naming conventions. For example if this annotation is 
  * on a method called getFoo() then a method called setFoo() would be looked 
  * for and if found wired automatically into the jmx attribute.
- *
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
@@ -42,7 +41,7 @@
     /**
      * Description of the Managed Attribute
      * 
-     * @returngit checkout
+     * @return value
      */
     String value() default "Not Specified";
     
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/ManagedObject.java b/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/ManagedObject.java
index b860419..42cc91b 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/ManagedObject.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/ManagedObject.java
@@ -25,12 +25,11 @@
 import java.lang.annotation.Target;
 
 /**
- * The @ManagedObject annotation is used on a class at the top level to 
+ * The <code>&#064;ManagedObject</code> annotation is used on a class at the top level to 
  * indicate that it should be exposed as an mbean. It has only one attribute 
  * to it which is used as the description of the MBean. Should multiple 
- * @ManagedObject annotations be found in the chain of influence then the 
+ * <code>&#064;ManagedObject</code> annotations be found in the chain of influence then the 
  * first description is used.
- *
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
@@ -39,6 +38,7 @@
 {
     /**
      * Description of the Managed Object
+     * @return value
      */
     String value() default "Not Specified";
   
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/ManagedOperation.java b/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/ManagedOperation.java
index 225110d..47e8738 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/ManagedOperation.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/ManagedOperation.java
@@ -25,9 +25,8 @@
 import java.lang.annotation.Target;
 
 /**
- * The @ManagedOperation annotation is used to indicate that a given method 
+ * The <code>&#064;ManagedOperation</code> annotation is used to indicate that a given method 
  * should be considered a JMX operation.
- *
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
@@ -36,6 +35,7 @@
 {
     /**
      * Description of the Managed Object
+     * @return value
      */
     String value() default "Not Specified";
     
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/Name.java b/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/Name.java
index c18e684..3aef223 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/Name.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/annotation/Name.java
@@ -39,11 +39,13 @@
 {
     /**
      * the name of the parameter
+     * @return the value
      */
     String value();
     
     /**
      * the description of the parameter
+     * @return the description
      */
     String description() default "";
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java
index beaf5d4..402a58c 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java
@@ -209,7 +209,8 @@
     private void setFailed(Throwable th)
     {
         _state = __FAILED;
-        LOG.warn(FAILED+" " + this+": "+th,th);
+        if (LOG.isDebugEnabled())
+            LOG.warn(FAILED+" " + this+": "+th,th);
         for (Listener listener : _listeners)
             listener.lifeCycleFailure(this,th);
     }
@@ -233,4 +234,23 @@
         @Override public void lifeCycleStopped(LifeCycle event) {}
         @Override public void lifeCycleStopping(LifeCycle event) {}
     }
+    
+    /**
+     * A LifeCycle Listener that will call stop if any failures are notified.
+     */
+    public static final LifeCycle.Listener STOP_ON_FAILURE = new AbstractLifeCycleListener()
+    {
+        @Override 
+        public void lifeCycleFailure(LifeCycle lifecycle, Throwable cause) 
+        {
+            try
+            {
+                lifecycle.stop();
+            }
+            catch(Exception e)
+            {
+                cause.addSuppressed(e);
+            }
+        }
+    };
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java
index 8799821..260e445 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java
@@ -20,6 +20,9 @@
 
 import java.util.Collection;
 
+/**
+ * A Container
+ */
 public interface Container
 {
     /* ------------------------------------------------------------ */
@@ -39,6 +42,7 @@
     /**
      * @param clazz the class of the beans
      * @return the list of beans of the given class (or subclass)
+     * @param <T> the Bean type
      * @see #getBeans()
      */
     public <T> Collection<T> getBeans(Class<T> clazz);
@@ -46,12 +50,14 @@
     /**
      * @param clazz the class of the bean
      * @return the first bean of a specific class (or subclass), or null if no such bean exist
+     * @param <T> the Bean type 
      */
     public <T> T getBean(Class<T> clazz);
 
     /**
      * Removes the given bean.
      * If the bean is-a {@link Listener}, then also do an implicit {@link #removeEventListener(Listener)}.
+     * @param o the bean to remove
      * @return whether the bean was removed
      */
     public boolean removeBean(Object o);
@@ -59,14 +65,14 @@
     /**
      * Add an event listener. 
      * @see Container#addBean(Object)
-     * @param listener
+     * @param listener the listener to add
      */
     public void addEventListener(Listener listener);
     
     /**
      * Remove an event listener. 
      * @see Container#removeBean(Object)
-     * @param listener
+     * @param listener the listener to remove
      */
     public void removeEventListener(Listener listener);
 
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java
index a6e4e4e..fdc0b9c 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java
@@ -31,23 +31,26 @@
 import org.eclipse.jetty.util.log.Logger;
 
 /**
- * An ContainerLifeCycle is an {@link LifeCycle} implementation for a collection of contained beans.
+ * A ContainerLifeCycle is an {@link LifeCycle} implementation for a collection of contained beans.
  * <p>
- * Beans can be added the ContainerLifeCycle 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.
+ * Beans can be added to the ContainerLifeCycle 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 its
+ * lifecycle must be managed externally.
  * <p>
- * When a {@link LifeCycle} bean is added without a managed state being specified the state is determined heuristically:
+ * When a {@link LifeCycle} bean is added without a managed state being specified the state is
+ * determined heuristically:
  * <ul>
- *   <li>If the added bean is running, it will be added as an unmanaged bean.
- *   <li>If the added bean is !running and the container is !running, it will be added as an AUTO bean (see below).
- *   <li>If the added bean is !running and the container is starting, it will be added as an managed bean and will be started (this handles the frequent case of 
- *   new beans added during calls to doStart).
- *   <li>If the added bean is !running and the container is started, it will be added as an unmanaged bean.
+ *   <li>If the added bean is running, it will be added as an unmanaged bean.</li>
+ *   <li>If the added bean is !running and the container is !running, it will be added as an AUTO bean (see below).</li>
+ *   <li>If the added bean is !running and the container is starting, it will be added as a managed bean
+ *   and will be started (this handles the frequent case of new beans added during calls to doStart).</li>
+ *   <li>If the added bean is !running and the container is started, it will be added as an unmanaged bean.</li>
  * </ul>
- * When the container is started, then all contained managed beans will also be started.  Any contained Auto beans 
- * will be check for their status and if already started will be switched unmanaged beans, else they will be 
- * started and switched to managed beans.  Beans added after a container is started are not started and their state needs to
- * be explicitly managed.
+ * When the container is started, then all contained managed beans will also be started.
+ * Any contained AUTO beans will be check for their status and if already started will be switched unmanaged beans,
+ * else they will be started and switched to managed beans.
+ * Beans added after a container is started are not started and their state needs to be explicitly managed.
  * <p>
  * When stopping the container, a contained bean will be stopped by this aggregate only if it
  * is started by this aggregate.
@@ -55,10 +58,11 @@
  * 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 ContainerLifeCycle} 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.
+ * If adding a bean that is shared between multiple {@link ContainerLifeCycle} 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>
- * This class also provides utility methods to dump deep structures of objects.  It the dump, the following symbols are used to indicate the type of contained object:
+ * This class also provides utility methods to dump deep structures of objects.
+ * In the dump, the following symbols are used to indicate the type of contained object:
  * <pre>
  * SomeContainerLifeCycleInstance
  *   +- contained POJO instance
@@ -67,22 +71,14 @@
  *   +? referenced AUTO object that could become MANAGED or UNMANAGED.
  * </pre>
  */
-
-/* ------------------------------------------------------------ */
-/**
- */
 @ManagedObject("Implementation of Container and LifeCycle")
 public class ContainerLifeCycle extends AbstractLifeCycle implements Container, Destroyable, Dumpable
 {
     private static final Logger LOG = Log.getLogger(ContainerLifeCycle.class);
     private final List<Bean> _beans = new CopyOnWriteArrayList<>();
     private final List<Container.Listener> _listeners = new CopyOnWriteArrayList<>();
-    private boolean _doStarted = false;
-
-
-    public ContainerLifeCycle()
-    {
-    }
+    private boolean _doStarted;
+    private boolean _destroyed;
 
     /**
      * Starts the managed lifecycle beans in the order they were added.
@@ -90,6 +86,9 @@
     @Override
     protected void doStart() throws Exception
     {
+        if (_destroyed)
+            throw new IllegalStateException("Destroyed container cannot be restarted");
+
         // indicate that we are started, so that addBean will start other beans added.
         _doStarted = true;
 
@@ -124,8 +123,8 @@
     /**
      * Starts the given lifecycle.
      *
-     * @param l
-     * @throws Exception
+     * @param l the lifecycle to start
+     * @throws Exception if unable to start lifecycle
      */
     protected void start(LifeCycle l) throws Exception
     {
@@ -135,8 +134,8 @@
     /**
      * Stops the given lifecycle.
      *
-     * @param l
-     * @throws Exception
+     * @param l the lifecycle to stop
+     * @throws Exception if unable to stop the lifecycle
      */
     protected void stop(LifeCycle l) throws Exception
     {
@@ -158,8 +157,7 @@
             if (b._managed==Managed.MANAGED && b._bean instanceof LifeCycle)
             {
                 LifeCycle l = (LifeCycle)b._bean;
-                if (l.isRunning())
-                    stop(l);
+                stop(l);
             }
         }
     }
@@ -170,6 +168,7 @@
     @Override
     public void destroy()
     {
+        _destroyed = true;
         List<Bean> reverse = new ArrayList<>(_beans);
         Collections.reverse(reverse);
         for (Bean b : reverse)
@@ -183,7 +182,6 @@
         _beans.clear();
     }
 
-
     /**
      * @param bean the bean to test
      * @return whether this aggregate contains the bean
@@ -247,7 +245,7 @@
 
     public boolean addBean(Object o, Managed managed)
     {
-        if (contains(o))
+        if (o==null || contains(o))
             return false;
 
         Bean new_bean = new Bean(o);
@@ -326,14 +324,13 @@
         return true;
     }
 
-    
-    /* ------------------------------------------------------------ */
-    /** Add a managed lifecycle.
+    /**
+     * Adds a managed lifecycle.
      * <p>This is a convenience method that uses addBean(lifecycle,true)
      * and then ensures that the added bean is started iff this container
      * is running.  Exception from nested calls to start are caught and 
      * wrapped as RuntimeExceptions
-     * @param lifecycle
+     * @param lifecycle the managed lifecycle to add
      */
     public void addManaged(LifeCycle lifecycle)
     {
@@ -742,8 +739,7 @@
         }
     }
 
-
-    enum Managed { POJO, MANAGED, UNMANAGED, AUTO };
+    enum Managed { POJO, MANAGED, UNMANAGED, AUTO }
 
     private static class Bean
     {
@@ -752,6 +748,8 @@
 
         private Bean(Object b)
         {
+            if (b==null)
+                throw new NullPointerException();
             _bean = b;
         }
 
@@ -777,6 +775,17 @@
                 addBean(newBean);
         }
     }
+    
+    public void updateBean(Object oldBean, final Object newBean, boolean managed)
+    {
+        if (newBean!=oldBean)
+        {
+            if (oldBean!=null)
+                removeBean(oldBean);
+            if (newBean!=null)
+                addBean(newBean,managed);
+        }
+    }
 
     public void updateBeans(Object[] oldBeans, final Object[] newBeans)
     {
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Destroyable.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Destroyable.java
index ad21b84..4e13478 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Destroyable.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Destroyable.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.util.component;
 
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.annotation.ManagedOperation;
 
 /**
  * <p>A Destroyable is an object which can be destroyed.</p>
@@ -25,7 +27,9 @@
  * 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>
  */
+@ManagedObject
 public interface Destroyable
 {
+    @ManagedOperation(value = "Destroys this component", impact = "ACTION")
     void destroy();
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/DumpableCollection.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/DumpableCollection.java
new file mode 100644
index 0000000..32a3c0b
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/DumpableCollection.java
@@ -0,0 +1,51 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.Collection;
+
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+
+public class DumpableCollection implements Dumpable
+{
+    private final String _name;
+    private final Collection<?> _collection;
+
+    public DumpableCollection(String name,Collection<?> collection)
+    {
+        _name=name;
+        _collection=collection;
+    }
+
+    @Override
+    public String dump()
+    {
+        return ContainerLifeCycle.dump(this);
+    }
+
+    @Override
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        out.append(_name).append("\n");
+        if (_collection!=null)
+            ContainerLifeCycle.dump(out,indent,_collection);
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/LifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/LifeCycle.java
index f840ee6..a6e0cb2 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/LifeCycle.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/LifeCycle.java
@@ -26,7 +26,7 @@
 /* ------------------------------------------------------------ */
 /**
  * The lifecycle interface for generic components.
- * <br />
+ * <br>
  * Classes implementing this interface have a defined life cycle
  * defined by the methods of this interface.
  *
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/AbstractLogger.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/AbstractLogger.java
index fe08224..718518d 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/AbstractLogger.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/AbstractLogger.java
@@ -18,6 +18,7 @@
 
 package org.eclipse.jetty.util.log;
 
+import java.util.Properties;
 
 /* ------------------------------------------------------------ */
 /** Abstract Logger.
@@ -25,6 +26,13 @@
  */
 public abstract class AbstractLogger implements Logger
 {
+    public static final int LEVEL_DEFAULT = -1;
+    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;
+    public static final int LEVEL_OFF = 10;
+        
     @Override
     public final Logger getLogger(String name)
     {
@@ -76,6 +84,137 @@
         return true;
     }
     
+    /**
+     * 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 lookupLoggingLevel(Properties props, final String name)
+    {
+        if ((props == null) || (props.isEmpty()) || name==null )
+            return LEVEL_DEFAULT;
+        
+        // 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 LEVEL_DEFAULT;
+    }
+
+
+    public static String getLoggingProperty(Properties props, String name, String property)
+    {
+        // 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 s = props.getProperty(nameSegment+"."+property);
+            if (s!=null)
+                return s;
+    
+            // Trim and try again.
+            int idx = nameSegment.lastIndexOf('.');
+            nameSegment = (idx >= 0)?nameSegment.substring(0,idx):null;
+        }
+    
+        return null;
+    }
+
+
+    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;
+        }
+        else if ("OFF".equalsIgnoreCase(levelStr))
+        {
+            return LEVEL_OFF;
+        }
+    
+        System.err.println("Unknown StdErrLog level [" + levelSegment + "]=[" + levelStr + "], expecting only [ALL, DEBUG, INFO, WARN, OFF] as values.");
+        return -1;
+    }
+
+
+    /**
+     * Condenses a classname by stripping down the package name to just the first character of each package name
+     * segment.Configured
+     *
+     * <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 void debug(String msg, long arg)
     {
         if (isDebugEnabled())
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/JavaUtilLog.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/JavaUtilLog.java
index 7b493c3..b911ad0 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/JavaUtilLog.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/JavaUtilLog.java
@@ -18,7 +18,14 @@
 
 package org.eclipse.jetty.util.log;
 
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+
+import org.eclipse.jetty.util.Loader;
 
 /**
  * <p>
@@ -26,27 +33,109 @@
  * </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">
+ * You can also set the logger level using <a href="http://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html">
  * standard java.util.logging configuration</a>.
  * </p>
+ * 
+ * Configuration Properties:
+ * <dl>
+ *   <dt>${name|hierarchy}.LEVEL=(ALL|DEBUG|INFO|WARN|OFF)</dt>
+ *   <dd>
+ *   Sets the level that the Logger should log at.<br>
+ *   Names can be a package name, or a fully qualified class name.<br>
+ *   Default: The default from the java.util.logging mechanism/configuration
+ *   <br>
+ *   <dt>org.eclipse.jetty.util.log.javautil.PROPERTIES=&lt;property-resource-name&gt;</dt>
+ *   <dd>If set, it is used as a classpath resource name to find a java.util.logging 
+ *   property file.
+ *   <br>
+ *   Default: null
+ *   </dd>
+ *   <dt>org.eclipse.jetty.util.log.javautil.SOURCE=(true|false)</dt>
+ *   <dd>Set the LogRecord source class and method for JavaUtilLog.<br>
+ *   Default: true
+ *   </dd>
+ *   <dt>org.eclipse.jetty.util.log.SOURCE=(true|false)</dt>
+ *   <dd>Set the LogRecord source class and method for all Loggers.<br>
+ *   Default: depends on Logger class
+ *   </dd>
+ * </dl>
  */
 public class JavaUtilLog extends AbstractLogger
 {
+    private final static String THIS_CLASS= JavaUtilLog.class.getName();
+    private final static boolean __source = 
+            Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.SOURCE",
+            Log.__props.getProperty("org.eclipse.jetty.util.log.javautil.SOURCE","true")));
+    
+    private static boolean _initialized=false;
+    
     private Level configuredLevel;
     private java.util.logging.Logger _logger;
 
     public JavaUtilLog()
     {
-        this("org.eclipse.jetty.util.log");
+        this("org.eclipse.jetty.util.log.javautil");
     }
 
     public JavaUtilLog(String name)
     {
-        _logger = java.util.logging.Logger.getLogger(name);
-        if (Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.DEBUG", "false")))
+        synchronized (JavaUtilLog.class)
         {
-            _logger.setLevel(Level.FINE);
+            if (!_initialized)
+            {
+                _initialized=true;
+
+                final String properties=Log.__props.getProperty("org.eclipse.jetty.util.log.javautil.PROPERTIES",null);
+                if (properties!=null)
+                {
+                    AccessController.doPrivileged(new PrivilegedAction<Object>()
+                    {
+                        public Object run()
+                        {
+                            try
+                            {
+                                URL props = Loader.getResource(JavaUtilLog.class,properties);
+                                if (props != null)
+                                    LogManager.getLogManager().readConfiguration(props.openStream());
+                            }
+                            catch(Throwable e)
+                            {
+                                System.err.println("[WARN] Error loading logging config: " + properties);
+                                e.printStackTrace(System.err);
+                            }
+                            
+                            return null;
+                        }
+                    });
+                }
+            }
         }
+        
+        _logger = java.util.logging.Logger.getLogger(name);
+        
+        switch(lookupLoggingLevel(Log.__props,name))
+        {
+            case LEVEL_ALL:
+                _logger.setLevel(Level.ALL);
+                break;
+            case LEVEL_DEBUG:
+                _logger.setLevel(Level.FINE);
+                break;
+            case LEVEL_INFO:
+                _logger.setLevel(Level.INFO);
+                break;
+            case LEVEL_WARN:
+                _logger.setLevel(Level.WARNING);
+                break;
+            case LEVEL_OFF:
+                _logger.setLevel(Level.OFF);
+                break;
+            case LEVEL_DEFAULT:
+            default:
+                break;
+        }
+        
         configuredLevel = _logger.getLevel();
     }
 
@@ -55,36 +144,63 @@
         return _logger.getName();
     }
 
+    protected void log(Level level,String msg,Throwable thrown)
+    {
+        LogRecord record = new LogRecord(level,msg);
+        if (thrown!=null)
+            record.setThrown(thrown);
+        record.setLoggerName(_logger.getName());
+        if (__source)
+        {
+            StackTraceElement[] stack = new Throwable().getStackTrace();
+            for (int i=0;i<stack.length;i++)
+            {
+                StackTraceElement e=stack[i];
+                if (!e.getClassName().equals(THIS_CLASS))
+                {
+                    record.setSourceClassName(e.getClassName());
+                    record.setSourceMethodName(e.getMethodName());
+                    break;
+                }
+            }
+        }
+        _logger.log(record);
+    }
+    
     public void warn(String msg, Object... args)
     {
         if (_logger.isLoggable(Level.WARNING))
-            _logger.log(Level.WARNING,format(msg,args));
+            log(Level.WARNING,format(msg,args),null);
     }
 
     public void warn(Throwable thrown)
     {
-        warn("", thrown);
+        if (_logger.isLoggable(Level.WARNING))
+            log(Level.WARNING,"",thrown);
     }
 
     public void warn(String msg, Throwable thrown)
     {
-        _logger.log(Level.WARNING, msg, thrown);
+        if (_logger.isLoggable(Level.WARNING))
+            log(Level.WARNING,msg,thrown);
     }
 
     public void info(String msg, Object... args)
     {
         if (_logger.isLoggable(Level.INFO))
-            _logger.log(Level.INFO, format(msg, args));
+            log(Level.INFO, format(msg, args),null);
     }
 
     public void info(Throwable thrown)
     {
-        info("", thrown);
+        if (_logger.isLoggable(Level.INFO))
+            log(Level.INFO, "",thrown);
     }
 
     public void info(String msg, Throwable thrown)
     {
-        _logger.log(Level.INFO, msg, thrown);
+        if (_logger.isLoggable(Level.INFO))
+            log(Level.INFO,msg,thrown);
     }
 
     public boolean isDebugEnabled()
@@ -108,23 +224,25 @@
     public void debug(String msg, Object... args)
     {
         if (_logger.isLoggable(Level.FINE))
-            _logger.log(Level.FINE,format(msg, args));
+            log(Level.FINE,format(msg, args),null);
     }
 
     public void debug(String msg, long arg)
     {
         if (_logger.isLoggable(Level.FINE))
-            _logger.log(Level.FINE,format(msg, arg));
+            log(Level.FINE,format(msg, arg),null);
     }
 
     public void debug(Throwable thrown)
     {
-        debug("", thrown);
+        if (_logger.isLoggable(Level.FINE))
+            log(Level.FINE,"",thrown);
     }
 
     public void debug(String msg, Throwable thrown)
     {
-        _logger.log(Level.FINE, msg, thrown);
+        if (_logger.isLoggable(Level.FINE))
+            log(Level.FINE,msg,thrown);
     }
 
     /**
@@ -137,10 +255,8 @@
 
     public void ignore(Throwable ignored)
     {
-        if (Log.isIgnored())
-        {
-            warn(Log.IGNORED, ignored);
-        }
+        if (_logger.isLoggable(Level.FINEST))
+            log(Level.FINEST,Log.IGNORED,ignored);
     }
 
     private String format(String msg, Object... args)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/JettyLogHandler.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/JettyLogHandler.java
new file mode 100644
index 0000000..6dc3888
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/JettyLogHandler.java
@@ -0,0 +1,198 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.net.URL;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.regex.Pattern;
+
+/**
+ * Redirect java.util.logging events to Jetty Log
+ */
+public class JettyLogHandler extends java.util.logging.Handler
+{
+    public static void config()
+    {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        URL url = cl.getResource("logging.properties");
+        if (url != null)
+        {
+            System.err.printf("Initializing java.util.logging from %s%n",url);
+            try (InputStream in = url.openStream())
+            {
+                LogManager.getLogManager().readConfiguration(in);
+            }
+            catch (IOException e)
+            {
+                e.printStackTrace(System.err);
+            }
+        } 
+        else 
+        {
+            System.err.printf("WARNING: java.util.logging failed to initialize: logging.properties not found%n");
+        }
+
+        System.setProperty("org.apache.commons.logging.Log","org.apache.commons.logging.impl.Jdk14Logger");
+    }
+
+    public JettyLogHandler()
+    {
+        if (Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.DEBUG","false")))
+        {
+            setLevel(Level.FINEST);
+        }
+
+        if (Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.IGNORED","false")))
+        {
+            setLevel(Level.ALL);
+        }
+        
+        System.err.printf("%s Initialized at level [%s]%n",this.getClass().getName(),getLevel().getName());
+    }
+
+    private synchronized String formatMessage(LogRecord record)
+    {
+        String msg = getMessage(record);
+
+        try
+        {
+            Object params[] = record.getParameters();
+            if ((params == null) || (params.length == 0))
+            {
+                return msg;
+            }
+
+            if (Pattern.compile("\\{\\d+\\}").matcher(msg).find())
+            {
+                return MessageFormat.format(msg,params);
+            }
+
+            return msg;
+        }
+        catch (Exception ex)
+        {
+            return msg;
+        }
+    }
+
+    private String getMessage(LogRecord record)
+    {
+        ResourceBundle bundle = record.getResourceBundle();
+        if (bundle != null)
+        {
+            try
+            {
+                return bundle.getString(record.getMessage());
+            }
+            catch (java.util.MissingResourceException ex)
+            {
+            }
+        }
+
+        return record.getMessage();
+    }
+
+    @Override
+    public void publish(LogRecord record)
+    {
+        org.eclipse.jetty.util.log.Logger JLOG = getJettyLogger(record.getLoggerName());
+
+        int level = record.getLevel().intValue();
+        if (level >= Level.OFF.intValue())
+        {
+            // nothing to log, skip it.
+            return;
+        }
+
+        Throwable cause = record.getThrown();
+        String msg = formatMessage(record);
+
+        if (level >= Level.WARNING.intValue())
+        {
+            // log at warn
+            if (cause != null)
+            {
+                JLOG.warn(msg,cause);
+            }
+            else
+            {
+                JLOG.warn(msg);
+            }
+            return;
+        }
+
+        if (level >= Level.INFO.intValue())
+        {
+            // log at info
+            if (cause != null)
+            {
+                JLOG.info(msg,cause);
+            }
+            else
+            {
+                JLOG.info(msg);
+            }
+            return;
+        }
+
+        if (level >= Level.FINEST.intValue())
+        {
+            // log at debug
+            if (cause != null)
+            {
+                JLOG.debug(msg,cause);
+            }
+            else
+            {
+                JLOG.debug(msg);
+            }
+            return;
+        }
+
+        if (level >= Level.ALL.intValue())
+        {
+            // only corresponds with ignore (in jetty speak)
+            JLOG.ignore(cause);
+            return;
+        }
+    }
+
+    private Logger getJettyLogger(String loggerName)
+    {
+        return org.eclipse.jetty.util.log.Log.getLogger(loggerName);
+    }
+
+    @Override
+    public void flush()
+    {
+        // ignore
+    }
+
+    @Override
+    public void close() throws SecurityException
+    {
+        // ignore
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java
index d8caed5..3c6b865 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java
@@ -55,7 +55,7 @@
 public class Log
 {
     public final static String EXCEPTION= "EXCEPTION ";
-    public final static String IGNORED= "IGNORED ";
+    public final static String IGNORED= "IGNORED EXCEPTION ";
 
     /**
      * Logging Configuration Properties
@@ -130,7 +130,7 @@
         });
     }
     
-    private static void loadProperties(String resourceName, Properties props)
+    static void loadProperties(String resourceName, Properties props)
     {
         URL testProps = Loader.getResource(Log.class,resourceName);
         if (testProps != null)
@@ -167,13 +167,18 @@
                 return;
             __initialized = true;
 
+            Boolean announce = Boolean.parseBoolean(__props.getProperty("org.eclipse.jetty.util.log.announce", "true"));
+
             try
             {
-                Class<?> log_class = Loader.loadClass(Log.class, __logClass);
-                if (LOG == null || !LOG.getClass().equals(log_class))
+                Class<?> log_class = __logClass==null?null:Loader.loadClass(Log.class, __logClass);
+                if (LOG == null || (log_class!=null && !LOG.getClass().equals(log_class)))
                 {
                     LOG = (Logger)log_class.newInstance();
-                    LOG.debug("Logging to {} via {}", LOG, log_class.getName());
+                    if(announce)
+                    {
+                        LOG.debug("Logging to {} via {}", LOG, log_class.getName());
+                    }
                 }
             }
             catch(Throwable e)
@@ -182,8 +187,10 @@
                 initStandardLogging(e);
             }
 
-            if (LOG!=null)
-                LOG.info(String.format("Logging initialized @%dms",Uptime.getUptime()));
+            if (announce && LOG!=null)
+            {
+                LOG.info(String.format("Logging initialized @%dms", Uptime.getUptime()));
+            }
         }
     }
 
@@ -199,7 +206,12 @@
         {
             log_class = StdErrLog.class;
             LOG = new StdErrLog();
-            LOG.debug("Logging to {} via {}", LOG, log_class.getName());
+
+            Boolean announce = Boolean.parseBoolean(__props.getProperty("org.eclipse.jetty.util.log.announce", "true"));
+            if(announce)
+            {
+                LOG.debug("Logging to {} via {}", LOG, log_class.getName());
+            }
         }
     }
     
@@ -209,9 +221,19 @@
         return LOG;
     }
 
+    /**
+     * Set the root logger.
+     * <p>
+     * Note that if any classes have statically obtained their logger instance prior to this call, their Logger will not
+     * be affected by this call.
+     * 
+     * @param log
+     *            the root logger implementation to set
+     */
     public static void setLog(Logger log)
     {
         Log.LOG = log;
+        __logClass=null;
     }
 
     /**
@@ -312,4 +334,9 @@
     {
         return Collections.unmodifiableMap(__loggers);
     }
+
+    public static Properties getProperties()
+    {
+        return __props;
+    }
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Logger.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Logger.java
index 304d8f9..6e53242 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Logger.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Logger.java
@@ -117,6 +117,7 @@
     /**
      * Ignore an exception.
      * <p>This should be used rather than an empty catch block.
+     * @param ignored the throwable to log as ignored
      */
     public void ignore(Throwable ignored); 
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/LoggerLog.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/LoggerLog.java
index d9bb036..2cb7ca3 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/LoggerLog.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/LoggerLog.java
@@ -206,7 +206,7 @@
     {
         if (Log.isIgnored())
         {
-            warn(Log.IGNORED, ignored);
+            debug(Log.IGNORED, ignored);
         }
     }
 
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Slf4jLog.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Slf4jLog.java
index fb9dfe4..12843af 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Slf4jLog.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Slf4jLog.java
@@ -127,7 +127,7 @@
     {
         if (Log.isIgnored())
         {
-            warn(Log.IGNORED, ignored);
+            debug(Log.IGNORED, ignored);
         }
     }
 
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/StacklessLogging.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/StacklessLogging.java
index e6db8ee..48b8c83 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/StacklessLogging.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/StacklessLogging.java
@@ -18,6 +18,9 @@
 
 package org.eclipse.jetty.util.log;
 
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * A try-with-resources compatible layer for {@link StdErrLog#setHideStacks(boolean) hiding stacktraces} within the scope of the <code>try</code> block when
  * logging with {@link StdErrLog} implementation.
@@ -35,35 +38,47 @@
  */
 public class StacklessLogging implements AutoCloseable
 {
-    private final Class<?> clazzes[];
+    private final Set<StdErrLog> squelched = new HashSet<>();
 
     public StacklessLogging(Class<?>... classesToSquelch)
     {
-        this.clazzes = classesToSquelch;
-        hideStacks(true);
+        for (Class<?> clazz : classesToSquelch)
+        {
+            Logger log = Log.getLogger(clazz); 
+            // only operate on loggers that are of type StdErrLog
+            if (log instanceof StdErrLog)
+            {
+                StdErrLog stdErrLog=((StdErrLog)log);
+                if (!stdErrLog.isHideStacks())
+                {
+                    stdErrLog.setHideStacks(true);
+                    squelched.add(stdErrLog);
+                }
+            }
+        }
+    }
+
+    public StacklessLogging(Logger... logs)
+    {
+        for (Logger log : logs)
+        {
+            // only operate on loggers that are of type StdErrLog
+            if (log instanceof StdErrLog)
+            {
+                StdErrLog stdErrLog=((StdErrLog)log);
+                if (!stdErrLog.isHideStacks())
+                {
+                    stdErrLog.setHideStacks(true);
+                    squelched.add(stdErrLog);
+                }
+            }
+        }
     }
 
     @Override
-    public void close() throws Exception
+    public void close()
     {
-        hideStacks(false);
-    }
-
-    private void hideStacks(boolean hide)
-    {
-        for (Class<?> clazz : clazzes)
-        {
-            Logger log = Log.getLogger(clazz);
-            if (log == null)
-            {
-                // not interested in classes without loggers
-                continue;
-            }
-            if (log instanceof StdErrLog)
-            {
-                // only operate on loggers that are of type StdErrLog
-                ((StdErrLog)log).setHideStacks(hide);
-            }
-        }
+        for (StdErrLog log : squelched)
+            log.setHideStacks(false);
     }
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java
index 05a85c6..4f955b6 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java
@@ -21,6 +21,7 @@
 import java.io.PrintStream;
 import java.security.AccessControlException;
 import java.util.Properties;
+import java.util.logging.Level;
 
 import org.eclipse.jetty.util.DateCache;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
@@ -37,10 +38,10 @@
  * <dl>
  *   <dt>${name|hierarchy}.LEVEL=(ALL|DEBUG|INFO|WARN|OFF)</dt>
  *   <dd>
- *   Sets the level that the Logger should log at.<br/>
- *   Names can be a package name, or a fully qualified class name.<br/>
- *   Default: INFO<br/>
- *   <br/>
+ *   Sets the level that the Logger should log at.<br>
+ *   Names can be a package name, or a fully qualified class name.<br>
+ *   Default: INFO<br>
+ *   <br>
  *   Examples:
  *   <dl>
  *   <dt>org.eclipse.jetty.LEVEL=WARN</dt>
@@ -56,35 +57,35 @@
  *   <dt>${name}.SOURCE=(true|false)</dt>
  *   <dd>
  *   Logger specific, attempt to print the java source file name and line number
- *   where the logging event originated from.<br/>
+ *   where the logging event originated from.<br>
  *   Name must be a fully qualified class name (package name hierarchy is not supported
- *   by this configurable)<br/>
- *   Warning: this is a slow operation and will have an impact on performance!<br/>
+ *   by this configurable)<br>
+ *   Warning: this is a slow operation and will have an impact on performance!<br>
  *   Default: false
  *   </dd>
  *   
  *   <dt>${name}.STACKS=(true|false)</dt>
  *   <dd>
- *   Logger specific, control the display of stacktraces.<br/>
+ *   Logger specific, control the display of stacktraces.<br>
  *   Name must be a fully qualified class name (package name hierarchy is not supported
- *   by this configurable)<br/>
+ *   by this configurable)<br>
  *   Default: true
  *   </dd>
  *   
  *   <dt>org.eclipse.jetty.util.log.stderr.SOURCE=(true|false)</dt>
  *   <dd>Special Global Configuration, attempt to print the java source file name and line number
- *   where the logging event originated from.<br/>
+ *   where the logging event originated from.<br>
  *   Default: false
  *   </dd>
  *   
  *   <dt>org.eclipse.jetty.util.log.stderr.LONG=(true|false)</dt>
  *   <dd>Special Global Configuration, when true, output logging events to STDERR using
- *   long form, fully qualified class names.  when false, use abbreviated package names<br/>
+ *   long form, fully qualified class names.  when false, use abbreviated package names<br>
  *   Default: false
  *   </dd>
  *   <dt>org.eclipse.jetty.util.log.stderr.ESCAPE=(true|false)</dt>
  *   <dd>Global Configuration, when true output logging events to STDERR are always
- *   escaped so that control characters are replaced with '?";  '\r' with '<' and '\n' replaced '|'<br/>
+ *   escaped so that control characters are replaced with '?";  '\r' with '&lt;' and '\n' replaced '|'<br>
  *   Default: true
  *   </dd>
  * </dl>
@@ -93,18 +94,17 @@
 public class StdErrLog extends AbstractLogger
 {
     private static final String EOL = System.getProperty("line.separator");
+    // Do not change output format lightly, people rely on this output format now.
+    private static int __tagpad = Integer.parseInt(Log.__props.getProperty("org.eclipse.jetty.util.log.StdErrLog.TAG_PAD","0"));
     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"));
     private final static boolean __escape = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.ESCAPE","true"));
-
+    
     static
     {
-        __props.putAll(Log.__props);
-
         String deprecatedProperties[] =
         { "DEBUG", "org.eclipse.jetty.util.log.DEBUG", "org.eclipse.jetty.util.log.stderr.DEBUG" };
 
@@ -127,11 +127,11 @@
         }
     }
 
-    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;
-    public static final int LEVEL_OFF = 10;
+    public static void setTagPad(int pad)
+    {
+        __tagpad=pad;
+    }
+    
 
     private int _level = LEVEL_INFO;
     // Level that this Logger was configured as (remembered in special case of .setDebugEnabled())
@@ -146,6 +146,20 @@
     private final String _abbrevname;
     private boolean _hideStacks = false;
 
+    
+    public static int getLoggingLevel(Properties props,String name)
+    {
+        int level = lookupLoggingLevel(props,name);
+        if (level==LEVEL_DEFAULT)
+        {
+            level = lookupLoggingLevel(props,"log");
+            if (level==LEVEL_DEFAULT)
+                level=LEVEL_INFO;
+        }
+        return level;
+    }
+    
+    
     /**
      * Obtain a StdErrLog reference for the specified class, a convenience method used most often during testing to allow for control over a specific logger.
      * <p>
@@ -185,7 +199,7 @@
      */
     public StdErrLog(String name)
     {
-        this(name,__props);
+        this(name,null);
     }
 
     /**
@@ -198,16 +212,16 @@
      */
     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;
+        if (props!=null && props!=Log.__props)
+            Log.__props.putAll(props);
+        _name = name == null?"":name;
+        _abbrevname = condensePackageString(this._name);
+        _level = getLoggingLevel(Log.__props,this._name);
+        _configuredLevel = _level;
 
         try
         {
-            String source = getLoggingProperty(props,_name,"SOURCE");
+            String source = getLoggingProperty(Log.__props,_name,"SOURCE");
             _source = source==null?__source:Boolean.parseBoolean(source);
         }
         catch (AccessControlException ace)
@@ -218,7 +232,7 @@
         try
         {
             // allow stacktrace display to be controlled by properties as well
-            String stacks = getLoggingProperty(props,_name,"STACKS");
+            String stacks = getLoggingProperty(Log.__props,_name,"STACKS");
             _hideStacks = stacks==null?false:!Boolean.parseBoolean(stacks);
         }
         catch (AccessControlException ignore)
@@ -227,137 +241,6 @@
         }        
     }
 
-    /**
-     * 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)
-    {
-        if ((props == null) || (props.isEmpty()))
-        {
-            // Default Logging Level
-            return getLevelId("log.LEVEL","INFO");
-        }
-        
-        // 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"));
-    }
-    
-    public static String getLoggingProperty(Properties props, String name, String property)
-    {
-        // 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 s = props.getProperty(nameSegment+"."+property);
-            if (s!=null)
-                return s;
-
-            // Trim and try again.
-            int idx = nameSegment.lastIndexOf('.');
-            nameSegment = (idx >= 0)?nameSegment.substring(0,idx):null;
-        }
-
-        return null;
-    }
-
-    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;
-        }
-        else if ("OFF".equalsIgnoreCase(levelStr))
-        {
-            return LEVEL_OFF;
-        }
-
-        System.err.println("Unknown StdErrLog level [" + levelSegment + "]=[" + levelStr + "], expecting only [ALL, DEBUG, INFO, WARN, OFF] 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;
@@ -589,16 +472,26 @@
             buffer.append(".00");
         }
         buffer.append(ms).append(tag);
-        if (_printLongNames)
+        
+        String name=_printLongNames?_name:_abbrevname;
+        String tname=Thread.currentThread().getName();
+
+        int p=__tagpad>0?(name.length()+tname.length()-__tagpad):0;
+
+        if (p<0)
         {
-            buffer.append(_name);
+            buffer
+            .append(name)
+            .append(':')
+            .append("                                                  ",0,-p)
+            .append(tname);
         }
-        else
+        else if (p==0)
         {
-            buffer.append(_abbrevname);
+            buffer.append(name).append(':').append(tname);
         }
         buffer.append(':');
-        buffer.append(Thread.currentThread().getName()).append(": ");
+        
         if (_source)
         {
             Throwable source = new Throwable();
@@ -628,6 +521,8 @@
                 break;
             }
         }
+
+        buffer.append(' ');
     }
 
     private void format(StringBuilder builder, String msg, Object... args)
@@ -778,12 +673,6 @@
         return s.toString();
     }
 
-    public static void setProperties(Properties props)
-    {
-        __props.clear();
-        __props.putAll(props);
-    }
-
     public void ignore(Throwable ignored)
     {
         if (_level <= LEVEL_ALL)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java
index d496cdd..868402f 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java
@@ -47,20 +47,22 @@
  * 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)
- * 
+ *
+ * @deprecated Use {@link PathResource}
  */
+@Deprecated
 public class FileResource extends Resource
 {
     private static final Logger LOG = Log.getLogger(FileResource.class);
 
     /* ------------------------------------------------------------ */
     private final File _file;
-    private final String _uri;
+    private final URI _uri;
     private final URI _alias;
     
     /* -------------------------------------------------------- */
     public FileResource(URL url)
-        throws IOException, URISyntaxException
+            throws IOException, URISyntaxException
     {
         File file;
         try
@@ -69,7 +71,7 @@
             file =new File(url.toURI());
             assertValidPath(file.toString());
         }
-        catch (URISyntaxException e) 
+        catch (URISyntaxException e)
         {
             throw e;
         }
@@ -82,9 +84,9 @@
             try
             {
                 // Assume that File.toURL produced unencoded chars. So try encoding them.
-                String file_url="file:"+URIUtil.encodePath(url.toString().substring(5));           
+                String file_url="file:"+URIUtil.encodePath(url.toString().substring(5));
                 URI uri = new URI(file_url);
-                if (uri.getAuthority()==null) 
+                if (uri.getAuthority()==null)
                     file = new File(uri);
                 else
                     file = new File("//"+uri.getAuthority()+URIUtil.decodePath(url.getFile()));
@@ -101,7 +103,7 @@
         
         _file=file;
         _uri=normalizeURI(_file,url.toURI());
-        _alias=checkFileAlias(_file);
+        _alias=checkFileAlias(_uri,_file);
     }
 
     /* -------------------------------------------------------- */
@@ -109,45 +111,109 @@
     {
         File file=new File(uri);
         _file=file;
-        URI file_uri=_file.toURI();
-        _uri=normalizeURI(_file,uri);
-        assertValidPath(file.toString());
+        try
+        {
+            URI file_uri = _file.toURI();
+            _uri = normalizeURI(_file, uri);
+            assertValidPath(file.toString());
 
-        // Is it a URI alias?
-        if (!URIUtil.equalsIgnoreEncodings(_uri,file_uri.toString()))
-            _alias=_file.toURI();
-        else
-            _alias=checkFileAlias(_file);
+            // Is it a URI alias?
+            if (!URIUtil.equalsIgnoreEncodings(_uri.toASCIIString(), file_uri.toString()))
+                _alias = _file.toURI();
+            else
+                _alias = checkFileAlias(_uri, _file);
+        }
+        catch (URISyntaxException e)
+        {
+            throw new InvalidPathException(_file.toString(), e.getMessage())
+            {
+                {
+                    initCause(e);
+                }
+            };
+        }
     }
 
     /* -------------------------------------------------------- */
-    FileResource(File file)
+    public FileResource(File file)
     {
         assertValidPath(file.toString());
         _file=file;
-        _uri=normalizeURI(_file,_file.toURI());
-        _alias=checkFileAlias(_file);
+        try
+        {
+            _uri = normalizeURI(_file, _file.toURI());
+        }
+        catch (URISyntaxException e)
+        {
+            throw new InvalidPathException(_file.toString(), e.getMessage())
+            {
+                {
+                    initCause(e);
+                }
+            };
+        }
+        _alias=checkFileAlias(_uri,_file);
     }
 
     /* -------------------------------------------------------- */
-    private static String normalizeURI(File file, URI uri)
+    public FileResource(File base, String childPath)
     {
+        String encoded = URIUtil.encodePath(childPath);
+
+        _file = new File(base, childPath);
+
+        // The encoded path should be a suffix of the resource (give or take a directory / )
+        URI uri;
+        try
+        {
+            if (base.isDirectory())
+            {
+                // treat all paths being added as relative
+                uri=new URI(URIUtil.addEncodedPaths(base.toURI().toASCIIString(),encoded));
+            }
+            else
+            {
+                uri=new URI(base.toURI().toASCIIString()+encoded);
+            }
+        }
+        catch (final URISyntaxException e)
+        {
+            throw new InvalidPathException(base.toString() + childPath, e.getMessage())
+            {
+                {
+                    initCause(e);
+                }
+            };
+        }
+
+        _uri=uri;
+        _alias=checkFileAlias(_uri,_file);
+    }
+
+    /* -------------------------------------------------------- */
+    private static URI normalizeURI(File file, URI uri) throws URISyntaxException {
         String u =uri.toASCIIString();
         if (file.isDirectory())
         {
             if(!u.endsWith("/"))
                 u+="/";
-        } 
+        }
         else if (file.exists() && u.endsWith("/"))
             u=u.substring(0,u.length()-1);
-        return u;
+        return new URI(u);
     }
 
     /* -------------------------------------------------------- */
-    private static URI checkFileAlias(File file)
+    private static URI checkFileAlias(final URI uri, final File file)
     {
         try
         {
+            if (!URIUtil.equalsIgnoreEncodings(uri,file.toURI()))
+            {
+                // Return alias pointing to Java File normalized URI
+                return new File(uri).getAbsoluteFile().toURI();
+            }
+
             String abs=file.getAbsolutePath();
             String can=file.getCanonicalPath();
 
@@ -158,7 +224,7 @@
 
                 URI alias=new File(can).toURI();
                 // Have to encode the path as File.toURI does not!
-                return new URI("file://"+URIUtil.encodePath(alias.getPath()));  
+                return new URI("file://"+URIUtil.encodePath(alias.getPath()));
             }
         }
         catch(Exception e)
@@ -182,38 +248,18 @@
     /* -------------------------------------------------------- */
     @Override
     public Resource addPath(String path)
-        throws IOException,MalformedURLException
+            throws IOException, MalformedURLException
     {
         assertValidPath(path);
         path = org.eclipse.jetty.util.URIUtil.canonicalPath(path);
 
         if (path==null)
-            throw new MalformedURLException();   
+            throw new MalformedURLException();
         
         if ("/".equals(path))
             return this;
-        
-        path=URIUtil.encodePath(path);
-        // The encoded path should be a suffix of the resource (give or take a directory / )
-        URI uri;
-        try
-        {
-            if (_file.isDirectory())
-            {
-                // treat all paths being added as relative
-                uri=new URI(URIUtil.addPaths(_uri,path));
-            }
-            else
-            {
-                uri=new URI(_uri+path);
-            }
-        }
-        catch(final URISyntaxException e)
-        {
-            throw new InvalidPathException(path, e.getMessage());
-        }
 
-        return new FileResource(uri);
+        return new FileResource(_file, path);
     }
 
     private void assertValidPath(String path)
@@ -259,7 +305,7 @@
     @Override
     public boolean isDirectory()
     {
-        return _file.exists() && _file.isDirectory() || _uri.endsWith("/");
+        return _file.exists() && _file.isDirectory() || _uri.toASCIIString().endsWith("/");
     }
 
     /* --------------------------------------------------------- */
@@ -317,7 +363,7 @@
      */
     @Override
     public boolean delete()
-        throws SecurityException
+            throws SecurityException
     {
         return _file.delete();
     }
@@ -328,7 +374,7 @@
      */
     @Override
     public boolean renameTo( Resource dest)
-        throws SecurityException
+            throws SecurityException
     {
         if( dest instanceof FileResource)
             return _file.renameTo( ((FileResource)dest)._file);
@@ -349,15 +395,15 @@
         for (int i=list.length;i-->0;)
         {
             if (new File(_file,list[i]).isDirectory() &&
-                !list[i].endsWith("/"))
+                    !list[i].endsWith("/"))
                 list[i]+="/";
         }
         return list;
     }
     
     /* ------------------------------------------------------------ */
-    /** 
-     * @param o
+    /**
+     * @param o the object to compare against this instance
      * @return <code>true</code> of the object <code>o</code> is a {@link FileResource} pointing to the same file as this resource. 
      */
     @Override
@@ -380,13 +426,13 @@
     @Override
     public int hashCode()
     {
-       return null == _file ? super.hashCode() : _file.hashCode();
+        return null == _file ? super.hashCode() : _file.hashCode();
     }
     
     /* ------------------------------------------------------------ */
     @Override
     public void copyTo(File destination)
-        throws IOException
+            throws IOException
     {
         if (isDirectory())
         {
@@ -416,7 +462,7 @@
     {
         try
         {
-            return new URL(_uri);
+            return _uri.toURL();
         }
         catch (MalformedURLException e)
         {
@@ -427,13 +473,12 @@
     @Override
     public URI getURI()
     {
-        return _file.toURI();
+        return _uri;
     }
 
     @Override
     public String toString()
     {
-        return _uri;
+        return _uri.toString();
     }
-
-}
+}
\ No newline at end of file
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java
index 0a197ae..62f3f4b 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java
@@ -55,13 +55,13 @@
     protected JarFileResource(URL url, boolean useCaches)
     {
         super(url, useCaches);
-    }
-   
+    }   
 
     /* ------------------------------------------------------------ */
     @Override
     public synchronized void close()
     {
+        _exists=false;
         _list=null;
         _entry=null;
         _file=null;
@@ -121,7 +121,7 @@
         _jarFile=null;
         _list=null;
         
-        int sep = _urlString.indexOf("!/");
+        int sep = _urlString.lastIndexOf("!/");
         _jarUrl=_urlString.substring(0,sep+2);
         _path=_urlString.substring(sep+2);
         if (_path.length()==0)
@@ -144,7 +144,6 @@
 
         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;}
@@ -179,40 +178,39 @@
                 }
                 catch(Exception e)
                 {
-                       LOG.ignore(e);
+                    LOG.ignore(e);
                 }
             }
 
             // Do we need to look more closely?
             if (jar_file!=null && _entry==null && !_directory)
             {
-                // OK - we have a JarFile, lets look at the entries for our path
-                Enumeration<JarEntry> e=jar_file.entries();
-                while(e.hasMoreElements())
+                // OK - we have a JarFile, lets look for the entry
+                JarEntry entry = jar_file.getJarEntry(_path);
+                if (entry == null) 
                 {
-                    JarEntry entry = e.nextElement();
-                    String name=entry.getName().replace('\\','/');
-                    
-                    // Do we have a match
-                    if (name.equals(_path))
+                    // the entry does not exist
+                    _exists = false;
+                } 
+                else if (entry.isDirectory()) 
+                {
+                    _directory = true;
+                    _entry = entry;
+                } 
+                else 
+                {
+                    // Let's confirm is a file
+                    JarEntry directory = jar_file.getJarEntry(_path + '/');
+                    if (directory != null) 
                     {
-                        _entry=entry;
-                        // Is the match a directory
-                        _directory=_path.endsWith("/");
-                        break;
-                    }
-                    else if (_path.endsWith("/"))
+                        _directory = true;
+                        _entry = directory;
+                    } 
+                    else 
                     {
-                        if (name.startsWith(_path))
-                        {
-                            _directory=true;
-                            break;
-                        }
-                    }
-                    else if (name.startsWith(_path) && name.length()>_path.length() && name.charAt(_path.length())=='/')
-                    {
-                        _directory=true;
-                        break;
+                        // OK is a file
+                      _directory = false;
+                      _entry = entry;
                     }
                 }
             }
@@ -228,7 +226,7 @@
                     LOG.ignore(ioe);
                 }
             }
-        }    
+        }
         
         _exists= ( _directory || _entry!=null);
         return _exists;
@@ -284,7 +282,7 @@
                 //So, do one retry to drop a connection and get a fresh JarFile
                 LOG.warn("Retrying list:"+e);
                 LOG.debug(e);
-                release();
+                close();
                 list = listEntries();
             }
 
@@ -324,7 +322,7 @@
         }
         
         Enumeration<JarEntry> e=jarFile.entries();
-        String dir=_urlString.substring(_urlString.indexOf("!/")+2);
+        String dir=_urlString.substring(_urlString.lastIndexOf("!/")+2);
         while(e.hasMoreElements())
         {
             JarEntry entry = e.nextElement();               
@@ -382,7 +380,7 @@
     /**
      * Take a Resource that possibly might use URLConnection caching
      * and turn it into one that doesn't.
-     * @param resource
+     * @param resource the JarFileResource to obtain without URLConnection caching. 
      * @return the non-caching resource
      */
     public static Resource getNonCachingResource (Resource resource)
@@ -394,35 +392,26 @@
         
         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
+     * @param resource the resource to test for
      * @return true if resource is contained in the named resource
-     * @throws MalformedURLException
+     * @throws MalformedURLException if unable to process is contained due to invalid URL format
      */
     @Override
     public boolean isContainedIn (Resource resource) 
     throws MalformedURLException
     {
         String string = _urlString;
-        int index = string.indexOf("!/");
+        int index = string.lastIndexOf("!/");
         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());     
+        return url.sameFile(resource.getURI().toURL());     
     }
 }
-
-
-
-
-
-
-
-
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java
index 6d2505a..14402f1 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java
@@ -26,6 +26,7 @@
 import java.io.OutputStream;
 import java.net.JarURLConnection;
 import java.net.URL;
+import java.net.URLConnection;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 import java.util.jar.Manifest;
@@ -156,9 +157,10 @@
       
         if (LOG.isDebugEnabled()) 
             LOG.debug("Extracting entry = "+subEntryName+" from jar "+jarFileURL);
-        
-        try (InputStream is = jarFileURL.openConnection().getInputStream();
-                JarInputStream jin = new JarInputStream(is))
+        URLConnection c = jarFileURL.openConnection();
+        c.setUseCaches(false);
+        try (InputStream is = c.getInputStream();
+             JarInputStream jin = new JarInputStream(is))
         {
             JarEntry entry;
             boolean shouldExtract;
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java
index b68e02c..968847a 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java
@@ -33,13 +33,15 @@
 import java.nio.file.InvalidPathException;
 import java.nio.file.LinkOption;
 import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
+import java.nio.file.Paths;
 import java.nio.file.StandardOpenOption;
 import java.nio.file.attribute.FileTime;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.jetty.util.IO;
 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;
 
@@ -49,23 +51,180 @@
 public class PathResource extends Resource
 {
     private static final Logger LOG = Log.getLogger(PathResource.class);
-
+    private final static LinkOption NO_FOLLOW_LINKS[] = new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
+    private final static LinkOption FOLLOW_LINKS[] = new LinkOption[] {};
+    
     private final Path path;
+    private final Path alias;
     private final URI uri;
-    private LinkOption linkOptions[] = new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
+    
+    private final Path checkAliasPath()
+    {
+        Path abs = path;
 
+        /* Catch situation where the Path class has already normalized
+         * the URI eg. input path "aa./foo.txt"
+         * from an #addPath(String) is normalized away during
+         * the creation of a Path object reference.
+         * If the URI is different then the Path.toUri() then
+         * we will just use the original URI to construct the
+         * alias reference Path.
+         */
+
+        if(!URIUtil.equalsIgnoreEncodings(uri,path.toUri()))
+        {
+            try
+            {
+                return Paths.get(uri).toRealPath(FOLLOW_LINKS);
+            }
+            catch (IOException ignored)
+            {
+                // If the toRealPath() call fails, then let
+                // the alias checking routines continue on
+                // to other techniques.
+                LOG.ignore(ignored);
+            }
+        }
+
+        if (!abs.isAbsolute())
+        {
+            abs = path.toAbsolutePath();
+        }
+
+        try
+        {
+            if (Files.isSymbolicLink(path))
+                return path.getParent().resolve(Files.readSymbolicLink(path));
+            if (Files.exists(path))
+            {
+                Path real = abs.toRealPath(FOLLOW_LINKS);
+                
+                /*
+                 * If the real path is not the same as the absolute path
+                 * then we know that the real path is the alias for the
+                 * provided path.
+                 *
+                 * For OS's that are case insensitive, this should
+                 * return the real (on-disk / case correct) version
+                 * of the path.
+                 *
+                 * We have to be careful on Windows and OSX.
+                 * 
+                 * Assume we have the following scenario
+                 *   Path a = new File("foo").toPath();
+                 *   Files.createFile(a);
+                 *   Path b = new File("FOO").toPath();
+                 * 
+                 * There now exists a file called "foo" on disk.
+                 * Using Windows or OSX, with a Path reference of
+                 * "FOO", "Foo", "fOO", etc.. means the following
+                 * 
+                 *                        |  OSX    |  Windows   |  Linux
+                 * -----------------------+---------+------------+---------
+                 * Files.exists(a)        |  True   |  True      |  True
+                 * Files.exists(b)        |  True   |  True      |  False
+                 * Files.isSameFile(a,b)  |  True   |  True      |  False
+                 * a.equals(b)            |  False  |  True      |  False
+                 * 
+                 * See the javadoc for Path.equals() for details about this FileSystem
+                 * behavior difference
+                 * 
+                 * We also cannot rely on a.compareTo(b) as this is roughly equivalent
+                 * in implementation to a.equals(b)
+                 */
+                
+                int absCount = abs.getNameCount();
+                int realCount = real.getNameCount();
+                if (absCount != realCount)
+                {
+                    // different number of segments
+                    return real;
+                }
+                
+                // compare each segment of path, backwards
+                for (int i = realCount-1; i >= 0; i--)
+                {
+                    if (!abs.getName(i).toString().equals(real.getName(i).toString()))
+                    {
+                        return real;
+                    }
+                }
+            }
+        }
+        catch (IOException e)
+        {
+            LOG.ignore(e);
+        }
+        catch (Exception e)
+        {
+            LOG.warn("bad alias ({} {}) for {}", e.getClass().getName(), e.getMessage(),path);
+        }
+        return null;
+    }
+
+    /**
+     * Construct a new PathResource from a File object.
+     * <p>
+     * An invocation of this convenience constructor of the form.
+     * </p>
+     * <pre>
+     * new PathResource(file);
+     * </pre>
+     * <p>
+     * behaves in exactly the same way as the expression
+     * </p>
+     * <pre>
+     * new PathResource(file.toPath());
+     * </pre>
+
+     * @param file the file to use
+     */
     public PathResource(File file)
     {
         this(file.toPath());
     }
 
+    /**
+     * Construct a new PathResource from a Path object.
+     *
+     * @param path the path to use
+     */
     public PathResource(Path path)
     {
-        this.path = path;
+        this.path = path.toAbsolutePath();
         assertValidPath(path);
         this.uri = this.path.toUri();
+        this.alias = checkAliasPath();
     }
 
+    /**
+     * Construct a new PathResource from a parent PathResource
+     * and child sub path
+     *
+     * @param parent the parent path resource
+     * @param childPath the child sub path
+     */
+    private PathResource(PathResource parent, String childPath) throws MalformedURLException
+    {
+        // Calculate the URI and the path separately, so that any aliasing done by
+        // FileSystem.getPath(path,childPath) is visiable as a difference to the URI
+        // obtained via URIUtil.addDecodedPath(uri,childPath)
+
+        this.path = parent.path.getFileSystem().getPath(parent.path.toString(), childPath);
+        if (isDirectory() &&!childPath.endsWith("/"))
+            childPath+="/";
+        this.uri = URIUtil.addPath(parent.uri,childPath);
+        this.alias = checkAliasPath();
+    }
+
+    /**
+     * Construct a new PathResource from a URI object.
+     * <p>
+     * Must be an absolute URI using the <code>file</code> scheme.
+     *
+     * @param uri the URI to build this PathResource from.
+     * @throws IOException if unable to construct the PathResource from the URI.
+     */
     public PathResource(URI uri) throws IOException
     {
         if (!uri.isAbsolute())
@@ -81,7 +240,7 @@
         Path path;
         try
         {
-            path = new File(uri).toPath();
+            path = Paths.get(uri);
         }
         catch (InvalidPathException e)
         {
@@ -97,23 +256,57 @@
             throw new IOException("Unable to build Path from: " + uri,e);
         }
 
-        this.path = path;
+        this.path = path.toAbsolutePath();
         this.uri = path.toUri();
+        this.alias = checkAliasPath();
     }
 
+    /**
+     * Create a new PathResource from a provided URL object.
+     * <p>
+     * An invocation of this convenience constructor of the form.
+     * </p>
+     * <pre>
+     * new PathResource(url);
+     * </pre>
+     * <p>
+     * behaves in exactly the same way as the expression
+     * </p>
+     * <pre>
+     * new PathResource(url.toURI());
+     * </pre>
+     *
+     * @param url the url to attempt to create PathResource from
+     * @throws IOException if URL doesn't point to a location that can be transformed to a PathResource
+     * @throws URISyntaxException if the provided URL was malformed
+     */
     public PathResource(URL url) throws IOException, URISyntaxException
     {
         this(url.toURI());
     }
 
     @Override
-    public Resource addPath(String apath) throws IOException, MalformedURLException
+    public Resource addPath(final String subpath) throws IOException, MalformedURLException
     {
-        return new PathResource(this.path.getFileSystem().getPath(path.toString(), apath));
+        String cpath = URIUtil.canonicalPath(subpath);
+
+        if ((cpath == null) || (cpath.length() == 0))
+            throw new MalformedURLException(subpath);
+
+        if ("/".equals(cpath))
+            return this;
+
+        // subpaths are always under PathResource
+        // compensate for input subpaths like "/subdir"
+        // where default resolve behavior would be
+        // to treat that like an absolute path
+
+        return new PathResource(this, subpath);
     }
 
     private void assertValidPath(Path path)
     {
+        // TODO merged from 9.2, check if necessary
         String str = path.toString();
         int idx = StringUtil.indexOfControlChars(str);
         if(idx >= 0)
@@ -175,7 +368,7 @@
     @Override
     public boolean exists()
     {
-        return Files.exists(path,linkOptions);
+        return Files.exists(path,NO_FOLLOW_LINKS);
     }
 
     @Override
@@ -184,14 +377,24 @@
         return path.toFile();
     }
 
-    public boolean getFollowLinks()
+    /**
+     * @return the {@link Path} of the resource
+     */
+    public Path getPath()
     {
-        return (linkOptions != null) && (linkOptions.length > 0) && (linkOptions[0] == LinkOption.NOFOLLOW_LINKS);
+        return path;
     }
 
     @Override
     public InputStream getInputStream() throws IOException
     {
+        /* Mimic behavior from old FileResource class and its
+         * usage of java.io.FileInputStream(File) which will trigger
+         * an IOException on construction if the path is a directory
+         */
+        if (Files.isDirectory(path))
+            throw new IOException(path + " is a directory");
+
         return Files.newInputStream(path,StandardOpenOption.READ);
     }
 
@@ -245,7 +448,7 @@
     @Override
     public boolean isDirectory()
     {
-        return Files.isDirectory(path,linkOptions);
+        return Files.isDirectory(path,FOLLOW_LINKS);
     }
 
     @Override
@@ -253,7 +456,7 @@
     {
         try
         {
-            FileTime ft = Files.getLastModifiedTime(path,linkOptions);
+            FileTime ft = Files.getLastModifiedTime(path,FOLLOW_LINKS);
             return ft.toMillis();
         }
         catch (IOException e)
@@ -278,24 +481,29 @@
     }
 
     @Override
+    public boolean isAlias()
+    {
+        return this.alias!=null;
+    }
+
+    /**
+     * The Alias as a Path.
+     * <p>
+     *     Note: this cannot return the alias as a DIFFERENT path in 100% of situations,
+     *     due to Java's internal Path/File normalization.
+     * </p>
+     *
+     * @return the alias as a path.
+     */
+    public Path getAliasPath()
+    {
+        return this.alias;
+    }
+
+    @Override
     public URI getAlias()
     {
-        if (Files.isSymbolicLink(path))
-        {
-            try
-            {
-                return path.toRealPath().toUri();
-            }
-            catch (IOException e)
-            {
-                LOG.debug(e);
-                return null;
-            }
-        }
-        else
-        {
-            return null;
-        }
+        return this.alias==null?null:this.alias.toUri();
     }
 
     @Override
@@ -337,8 +545,8 @@
             PathResource destRes = (PathResource)dest;
             try
             {
-                Path result = Files.move(path,destRes.path,StandardCopyOption.ATOMIC_MOVE);
-                return Files.exists(result,linkOptions);
+                Path result = Files.move(path,destRes.path);
+                return Files.exists(result,NO_FOLLOW_LINKS);
             }
             catch (IOException e)
             {
@@ -352,15 +560,22 @@
         }
     }
 
-    public void setFollowLinks(boolean followLinks)
+    @Override
+    public void copyTo(File destination) throws IOException
     {
-        if (followLinks)
+        if (isDirectory())
         {
-            linkOptions = new LinkOption[0];
+            IO.copyDir(this.path.toFile(),destination);
         }
         else
         {
-            linkOptions = new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
+            Files.copy(this.path,destination.toPath());
         }
     }
+
+    @Override
+    public String toString()
+    {
+        return this.uri.toASCIIString();
+    }
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java
index 80256ef..a00837e 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java
@@ -39,6 +39,7 @@
 import org.eclipse.jetty.util.Loader;
 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;
 
@@ -61,7 +62,7 @@
     /**
      * Change the default setting for url connection caches.
      * Subsequent URLConnections will use this default.
-     * @param useCaches
+     * @param useCaches true to enable URL connection caches, false otherwise.
      */
     public static void setDefaultUseCaches (boolean useCaches)
     {
@@ -113,8 +114,7 @@
         {
             try
             {
-                FileResource fileResource= new FileResource(url);
-                return fileResource;
+                return new PathResource(url);
             }
             catch(Exception e)
             {
@@ -176,13 +176,13 @@
                     // It's a file.
                     if (resource.startsWith("./"))
                         resource=resource.substring(2);
-                    
                     File file=new File(resource).getCanonicalFile();
-                    return new FileResource(file);
+                    return new PathResource(file);
                 }
-                catch(Exception e2)
+                catch(IOException e2)
                 {
-                    LOG.debug(Log.EXCEPTION,e2);
+                    // TODO throw the IOException instead
+                    e.addSuppressed(e2);
                     throw e;
                 }
             }
@@ -199,7 +199,7 @@
     /* ------------------------------------------------------------ */
     public static Resource newResource(File file)
     {
-        return new FileResource(file);
+        return new PathResource(file.toPath());
     }
 
     /* ------------------------------------------------------------ */
@@ -226,6 +226,7 @@
             }
             catch (IllegalArgumentException e)
             {
+                LOG.ignore(e);
                 // Catches scenario where a bad Windows path like "C:\dev" is
                 // improperly escaped, which various downstream classloaders
                 // tend to have a problem with
@@ -258,6 +259,8 @@
 
     /* ------------------------------------------------------------ */
     /** Find a classpath resource.
+     * @param resource the relative name of the resource
+     * @return Resource or null
      */
     public static Resource newClassPathResource(String resource)
     {
@@ -321,43 +324,52 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * Returns true if the respresened resource exists.
+     * @return true if the represented 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
+     * @return true if the represented 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
+     * Time resource was last modified.
+     * 
+     * @return the last modified time as milliseconds since unix epoch
      */
     public abstract long lastModified();
 
 
     /* ------------------------------------------------------------ */
     /**
-     * Return the length of the resource
+     * Length of the resource.
+     * 
+     * @return the length of the resource
      */
     public abstract long length();
     
 
     /* ------------------------------------------------------------ */
     /**
-     * Returns an URL representing the given resource
+     * URL representing the resource.
+     * 
+     * @return an URL representing the given resource
+     * @deprecated use {{@link #getURI()}.toURL() instead.
      */
-    // TODO: should deprecate this one and only use getURI()
+    @Deprecated
     public abstract URL getURL();
 
     /* ------------------------------------------------------------ */
     /**
-     * Returns an URI representing the given resource
+     * URI representing the resource.
+     * 
+     * @return an URI representing the given resource
      */
     public URI getURI()
     {
@@ -374,8 +386,11 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * Returns an File representing the given resource or NULL if this
+     * File representing the given resource.
+     * 
+     * @return an File representing the given resource or NULL if this
      * is not possible.
+     * @throws IOException if unable to get the resource due to permissions 
      */
     public abstract File getFile()
         throws IOException;
@@ -383,47 +398,60 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * Returns the name of the resource
+     * The name of the resource.
+     * 
+     * @return the name of the resource
      */
     public abstract String getName();
     
 
     /* ------------------------------------------------------------ */
     /**
-     * Returns an input stream to the resource
+     * Input stream to the resource
+     * 
+     * @return an input stream to the resource
+     * @throws IOException if unable to open the input stream
      */
     public abstract InputStream getInputStream()
-        throws java.io.IOException;
+        throws IOException;
     
     /* ------------------------------------------------------------ */
     /**
-     * Returns an readable bytechannel to the resource or null if one is not available.
+     * Readable ByteChannel for the resource.
+     * 
+     * @return an readable bytechannel to the resource or null if one is not available.
+     * @throws IOException if unable to open the readable bytechannel for the resource.
      */
     public abstract ReadableByteChannel getReadableByteChannel()
-        throws java.io.IOException;
+        throws IOException;
 
     /* ------------------------------------------------------------ */
     /**
      * Deletes the given resource
+     * @return true if resource was found and successfully deleted, false if resource didn't exist or was unable to
+     * be deleted.
+     * @throws SecurityException if unable to delete due to permissions 
      */
-    // TODO: can throw IOException
     public abstract boolean delete()
         throws SecurityException;
     
     /* ------------------------------------------------------------ */
     /**
      * Rename the given resource
+     * @param dest the destination name for the resource
+     * @return true if the resource was renamed, false if the resource didn't exist or was unable to be renamed.
+     * @throws SecurityException if unable to rename due to permissions
      */
-    // TODO: can throw IOException
-    public abstract boolean renameTo( Resource dest)
+    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.
+     * list of resource names contained in the given resource.
+     * 
+     * @return a list of resource names contained in the given resource.
+     * Note: The resource names are not URL encoded.
      */
-    // TODO: can throw IOException
     public abstract String[] list();
 
     /* ------------------------------------------------------------ */
@@ -431,6 +459,9 @@
      * Returns the resource contained inside the current resource with the
      * given name.
      * @param path The path segment to add, which is not encoded
+     * @return the Resource for the resolved path within this Resource.
+     * @throws IOException if unable to resolve the path
+     * @throws MalformedURLException if the resolution of the path fails because the input path parameter is malformed.
      */
     public abstract Resource addPath(String path)
         throws IOException,MalformedURLException;
@@ -458,24 +489,40 @@
 
     /* ------------------------------------------------------------ */
     /** 
-     * @deprecated
+     * @param uri the uri to encode
+     * @return null (this is deprecated)
+     * @deprecated use {@link URIUtil} or {@link UrlEncoded} instead
      */
+    @Deprecated
     public String encode(String uri)
     {
         return null;
     }
         
     /* ------------------------------------------------------------ */
+    // FIXME: this appears to not be used
+    @SuppressWarnings("javadoc")
     public Object getAssociate()
     {
         return _associate;
     }
 
     /* ------------------------------------------------------------ */
+    // FIXME: this appear to not be used
+    @SuppressWarnings("javadoc")
     public void setAssociate(Object o)
     {
         _associate=o;
     }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if this Resource is an alias to another real Resource
+     */
+    public boolean isAlias()
+    {
+        return getAlias()!=null;
+    }
     
     /* ------------------------------------------------------------ */
     /**
@@ -491,6 +538,7 @@
      * @param base The base URL
      * @param parent True if the parent directory should be included
      * @return String of HTML
+     * @throws IOException if unable to get the list of resources as HTML
      */
     public String getListHTML(String base,boolean parent)
         throws IOException
@@ -518,7 +566,7 @@
         if (parent)
         {
             buf.append("<TR><TD><A HREF=\"");
-            buf.append(URIUtil.addPaths(base,"../"));
+            buf.append(URIUtil.addEncodedPaths(base,"../"));
             buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
         }
         
@@ -531,7 +579,7 @@
             Resource item = addPath(ls[i]);
             
             buf.append("\n<TR><TD><A HREF=\"");
-            String path=URIUtil.addPaths(encodedBase,URIUtil.encodePath(ls[i]));
+            String path=URIUtil.addEncodedPaths(encodedBase,URIUtil.encodePath(ls[i]));
             
             buf.append(path);
             
@@ -618,9 +666,10 @@
     
     /* ------------------------------------------------------------ */
     /** 
-     * @param out 
+     * @param out the output stream to write to 
      * @param start First byte to write
      * @param count Bytes to write or -1 for all of them.
+     * @throws IOException if unable to copy the Resource to the output
      */
     public void writeTo(OutputStream out,long start,long count)
         throws IOException
@@ -636,11 +685,20 @@
     }    
     
     /* ------------------------------------------------------------ */
+    /**
+     * Copy the Resource to the new destination file.
+     * <p>
+     * Will not replace existing destination file.
+     * 
+     * @param destination the destination file to create
+     * @throws IOException if unable to copy the resource
+     */
     public void copyTo(File destination)
         throws IOException
     {
         if (destination.exists())
-            throw new IllegalArgumentException(destination+" exists");
+            throw new IllegalArgumentException(destination + " exists");
+        
         try (OutputStream out = new FileOutputStream(destination))
         {
             writeTo(out,0,-1);
@@ -648,8 +706,18 @@
     }
 
     /* ------------------------------------------------------------ */
+    /**
+     * Generate a weak ETag reference for this Resource.
+     * 
+     * @return the weak ETag reference for this resource.
+     */
     public String getWeakETag()
     {
+        return getWeakETag("");
+    }
+    
+    public String getWeakETag(String suffix)
+    {
         try
         {
             StringBuilder b = new StringBuilder(32);
@@ -663,6 +731,7 @@
             
             B64Code.encode(lastModified()^lhash,b);
             B64Code.encode(length()^lhash,b);
+            b.append(suffix);
             b.append('"');
             return b.toString();
         } 
@@ -704,7 +773,7 @@
     /** Generate a properly encoded URL from a {@link File} instance.
      * @param file Target file. 
      * @return URL of the target file.
-     * @throws MalformedURLException 
+     * @throws MalformedURLException if unable to convert File to URL
      */
     public static URL toURL(File file) throws MalformedURLException
     {
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java
index 4a5e89a..d46a085 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java
@@ -248,10 +248,10 @@
     
     /* ------------------------------------------------------------ */
     /**
-     * @param path
+     * @param path the path to look for
      * @return the resource(file) if found, returns a list of resource dirs if its a dir, else null.
-     * @throws IOException
-     * @throws MalformedURLException
+     * @throws IOException if unable to look for path
+     * @throws MalformedURLException if failed to look for path due to url issue
      */
     protected Object findResource(String path) throws IOException, MalformedURLException
     {        
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java
index 8042c2b..7839e55 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java
@@ -221,9 +221,11 @@
      * the url protocol. Eg JarURLConnection does not reuse inputstreams.
      * 
      * @param resetConnection if true the connection field is set to null
+     * @return the inputstream for this resource
+     * @throws IOException if unable to open the input stream
      */
     protected synchronized InputStream getInputStream(boolean resetConnection)
-        throws java.io.IOException
+        throws IOException
     {
         if (!checkConnection())
             throw new IOException( "Invalid resource");
@@ -301,7 +303,7 @@
 
         path = URIUtil.canonicalPath(path);
 
-        return newResource(URIUtil.addPaths(_url.toExternalForm(),URIUtil.encodePath(path)), _useCaches);
+        return newResource(URIUtil.addEncodedPaths(_url.toExternalForm(),URIUtil.encodePath(path)), _useCaches);
     }
 
     /* ------------------------------------------------------------ */
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateUtils.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateUtils.java
index cfa4d63..fb001fd 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateUtils.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateUtils.java
@@ -29,37 +29,27 @@
 public class CertificateUtils
 {
     /* ------------------------------------------------------------ */
-    public static KeyStore getKeyStore(InputStream storeStream, String storePath, String storeType, String storeProvider, String storePassword) throws Exception
+    public static KeyStore getKeyStore(Resource store, String storeType, String storeProvider, String storePassword) throws Exception
     {
         KeyStore keystore = null;
 
-        if (storeStream != null || storePath != null)
+        if (store != null)
         {
-            InputStream inStream = storeStream;
-            try
+            if (storeProvider != null)
             {
-                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());
+                keystore = KeyStore.getInstance(storeType, storeProvider);
             }
-            finally
+            else
             {
-                if (inStream != null)
-                {
-                    inStream.close();
-                }
+                keystore = KeyStore.getInstance(storeType);
+            }
+            
+            if (!store.exists())
+                throw new IllegalStateException("no valid keystore");
+            
+            try (InputStream inStream = store.getInputStream())
+            {
+                keystore.load(inStream, storePassword == null ? null : storePassword.toCharArray());
             }
         }
         
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateValidator.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateValidator.java
index 77f545d..5ab977c 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateValidator.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateValidator.java
@@ -72,8 +72,8 @@
     /**
      * creates an instance of the certificate validator 
      *
-     * @param trustStore 
-     * @param crls
+     * @param trustStore the truststore to use 
+     * @param crls the Certificate Revocation List to use 
      */
     public CertificateValidator(KeyStore trustStore, Collection<? extends CRL> crls)
     {
@@ -89,8 +89,8 @@
     /**
      * validates all aliases inside of a given keystore
      * 
-     * @param keyStore
-     * @throws CertificateException
+     * @param keyStore the keystore to validate
+     * @throws CertificateException if keystore error and unable to validate 
      */
     public void validate( KeyStore keyStore ) throws CertificateException
     {
@@ -116,10 +116,10 @@
     /**
      * validates a specific alias inside of the keystore being passed in
      * 
-     * @param keyStore
-     * @param keyAlias
+     * @param keyStore the keystore to validate
+     * @param keyAlias the keyalias in the keystore to valid with
      * @return the keyAlias if valid
-     * @throws CertificateException
+     * @throws CertificateException if keystore error and unable to validate
      */
     public String validate(KeyStore keyStore, String keyAlias) throws CertificateException
     {
@@ -146,9 +146,9 @@
     /**
      * validates a specific certificate inside of the keystore being passed in
      * 
-     * @param keyStore
-     * @param cert
-     * @throws CertificateException
+     * @param keyStore the keystore to validate against
+     * @param cert the certificate to validate
+     * @throws CertificateException if keystore error and unable to validate
      */
     public void validate(KeyStore keyStore, Certificate cert) throws CertificateException
     {
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java
index 7fadeab..9a6b8a2 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java
@@ -21,13 +21,10 @@
 import java.io.Serializable;
 import java.util.Arrays;
 
-/* ------------------------------------------------------------ */
 /**
  * Constraint
  * 
  * Describe an auth and/or data constraint.
- * 
- * 
  */
 public class Constraint implements Cloneable, Serializable
 {
@@ -93,10 +90,10 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * convenience Constructor.
+     * Convenience Constructor.
      * 
-     * @param name
-     * @param role
+     * @param name the name
+     * @param role the role
      */
     public Constraint(String name, String role)
     {
@@ -113,7 +110,7 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @param name
+     * @param name the name
      */
     public void setName(String name)
     {
@@ -172,7 +169,7 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @param role
+     * @param role the role
      * @return True if the constraint contains the role.
      */
     public boolean hasRole(String role)
@@ -212,7 +209,7 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @param c Data constrain indicator: 0=DC+NONE, 1=DC_INTEGRAL &
+     * @param c Data constrain indicator: 0=DC+NONE, 1=DC_INTEGRAL &amp;
      *                2=DC_CONFIDENTIAL
      */
     public void setDataConstraint(int c)
@@ -223,7 +220,7 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @return Data constrain indicator: 0=DC+NONE, 1=DC_INTEGRAL &
+     * @return Data constrain indicator: 0=DC+NONE, 1=DC_INTEGRAL &amp;
      *         2=DC_CONFIDENTIAL
      */
     public int getDataConstraint()
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/security/Password.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Password.java
index 81ff1b9..6f1a889 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/security/Password.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Password.java
@@ -246,15 +246,11 @@
         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("Usage - java " + Password.class.getName() + " [<user>] <password>");
             System.err.println("If the password is ?, the user will be prompted for the password");
             System.exit(1);
         }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509ExtendedKeyManager.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509ExtendedKeyManager.java
index 00ee96a..5e87c557 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509ExtendedKeyManager.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509ExtendedKeyManager.java
@@ -25,106 +25,123 @@
 
 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
+ * <p>An {@link X509ExtendedKeyManager} that select a key with desired alias,
+ * delegating other processing to a nested X509ExtendedKeyManager.</p>
+ * <p>Can be used both with server and client sockets.</p>
  */
 public class AliasedX509ExtendedKeyManager extends X509ExtendedKeyManager
 {
-    private String _keyAlias;
-    private X509KeyManager _keyManager;
+    private final String _alias;
+    private final X509ExtendedKeyManager _delegate;
 
-    /* ------------------------------------------------------------ */
-    /**
-     * 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
+    public AliasedX509ExtendedKeyManager(X509ExtendedKeyManager keyManager, String keyAlias)
     {
-        _keyAlias = keyAlias;
-        _keyManager = keyManager;
+        _alias = keyAlias;
+        _delegate = keyManager;
     }
 
-    /* ------------------------------------------------------------ */
-    /**
-     * @see javax.net.ssl.X509KeyManager#chooseClientAlias(java.lang.String[], java.security.Principal[], java.net.Socket)
-     */
+    public X509ExtendedKeyManager getDelegate()
+    {
+        return _delegate;
+    }
+
+    @Override
     public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket)
     {
-        return _keyAlias == null ? _keyManager.chooseClientAlias(keyType, issuers, socket) : _keyAlias;
+        if (_alias==null)
+            return _delegate.chooseClientAlias(keyType,issuers,socket);
+
+        for (String kt : keyType)
+        {
+            String[] aliases = _delegate.getClientAliases(kt,issuers);
+            if (aliases!=null)
+            {
+                for (String a:aliases)
+                    if (_alias.equals(a))
+                        return _alias;
+            }
+        }
+
+        return null;
     }
 
-    /* ------------------------------------------------------------ */
-    /**
-     * @see javax.net.ssl.X509KeyManager#chooseServerAlias(java.lang.String, java.security.Principal[], java.net.Socket)
-     */
+    @Override
     public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket)
     {
-        return _keyAlias == null ? _keyManager.chooseServerAlias(keyType, issuers, socket) : _keyAlias;
+        if (_alias==null)
+            return _delegate.chooseServerAlias(keyType,issuers,socket);
+
+        String[] aliases = _delegate.getServerAliases(keyType,issuers);
+        if (aliases!=null)
+        {
+            for (String a:aliases)
+                if (_alias.equals(a))
+                    return _alias;
+        }
+
+        return null;
     }
 
-    /* ------------------------------------------------------------ */
-    /**
-     * @see javax.net.ssl.X509KeyManager#getClientAliases(java.lang.String, java.security.Principal[])
-     */
+    @Override
     public String[] getClientAliases(String keyType, Principal[] issuers)
     {
-        return _keyManager.getClientAliases(keyType, issuers);
+        return _delegate.getClientAliases(keyType, issuers);
     }
 
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see javax.net.ssl.X509KeyManager#getServerAliases(java.lang.String, java.security.Principal[])
-     */
+    @Override
     public String[] getServerAliases(String keyType, Principal[] issuers)
     {
-        return _keyManager.getServerAliases(keyType, issuers);
+        return _delegate.getServerAliases(keyType, issuers);
     }
 
-    /* ------------------------------------------------------------ */
-    /**
-     * @see javax.net.ssl.X509KeyManager#getCertificateChain(java.lang.String)
-     */
+    @Override
     public X509Certificate[] getCertificateChain(String alias)
     {
-        return _keyManager.getCertificateChain(alias);
+        return _delegate.getCertificateChain(alias);
     }
 
-    /* ------------------------------------------------------------ */
-    /**
-     * @see javax.net.ssl.X509KeyManager#getPrivateKey(java.lang.String)
-     */
+    @Override
     public PrivateKey getPrivateKey(String alias)
     {
-        return _keyManager.getPrivateKey(alias);
+        return _delegate.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;
+        if (_alias==null)
+            return _delegate.chooseEngineServerAlias(keyType,issuers,engine);
+
+        String[] aliases = _delegate.getServerAliases(keyType,issuers);
+        if (aliases!=null)
+        {
+            for (String a:aliases)
+                if (_alias.equals(a))
+                    return _alias;
+        }
+
+        return null;
     }
 
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @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;
+        if (_alias==null)
+            return _delegate.chooseEngineClientAlias(keyType,issuers,engine);
+
+        for (String kt : keyType)
+        {
+            String[] aliases = _delegate.getClientAliases(kt,issuers);
+            if (aliases!=null)
+            {
+                for (String a:aliases)
+                    if (_alias.equals(a))
+                        return _alias;
+            }
+        }
+
+        return null;
     }
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509KeyManager.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509KeyManager.java
deleted file mode 100644
index 918a067..0000000
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509KeyManager.java
+++ /dev/null
@@ -1,107 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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);
-    }
-}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SniX509ExtendedKeyManager.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SniX509ExtendedKeyManager.java
new file mode 100644
index 0000000..2056d02
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SniX509ExtendedKeyManager.java
@@ -0,0 +1,157 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 java.util.Arrays;
+import java.util.Collection;
+
+import javax.net.ssl.SNIMatcher;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509ExtendedKeyManager;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * <p>A {@link X509ExtendedKeyManager} that selects a key with an alias
+ * retrieved from SNI information, delegating other processing to a nested X509ExtendedKeyManager.</p>
+ * <p>Can only be used on server side.</p>
+ */
+public class SniX509ExtendedKeyManager extends X509ExtendedKeyManager
+{
+    public static final String SNI_X509 = "org.eclipse.jetty.util.ssl.snix509";
+    private static final String NO_MATCHERS = "no_matchers";
+    private static final Logger LOG = Log.getLogger(SniX509ExtendedKeyManager.class);
+
+    private final X509ExtendedKeyManager _delegate;
+
+    public SniX509ExtendedKeyManager(X509ExtendedKeyManager keyManager)
+    {
+        _delegate = keyManager;
+    }
+
+    @Override
+    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket)
+    {
+        return _delegate.chooseClientAlias(keyType,issuers,socket);
+    }
+
+    @Override
+    public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine)
+    {
+        return _delegate.chooseEngineClientAlias(keyType,issuers,engine);
+    }
+
+    protected String chooseServerAlias(String keyType, Principal[] issuers, Collection<SNIMatcher> matchers, SSLSession session)
+    {
+        // Look for the aliases that are suitable for the keytype and issuers
+        String[] aliases = _delegate.getServerAliases(keyType,issuers);
+        if (aliases==null || aliases.length==0)
+            return null;
+
+        // Look for the SNI information.
+        String host=null;
+        X509 x509=null;
+        if (matchers!=null)
+        {
+            for (SNIMatcher m : matchers)
+            {
+                if (m instanceof SslContextFactory.AliasSNIMatcher)
+                {
+                    SslContextFactory.AliasSNIMatcher matcher = (SslContextFactory.AliasSNIMatcher)m;
+                    host=matcher.getHost();
+                    x509=matcher.getX509();
+                    break;
+                }
+            }
+        }
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("Matched {} with {} from {}",host,x509,Arrays.asList(aliases));
+
+        // Check if the SNI selected alias is allowable
+        if (x509!=null)
+        {
+            for (String a:aliases)
+            {
+                if (a.equals(x509.getAlias()))
+                {
+                    session.putValue(SNI_X509,x509);
+                    return a;
+                }
+            }
+            return null;
+        }
+        return NO_MATCHERS;
+    }
+
+    @Override
+    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket)
+    {
+        SSLSocket sslSocket = (SSLSocket)socket;
+
+        String alias = chooseServerAlias(keyType,issuers,sslSocket.getSSLParameters().getSNIMatchers(),sslSocket.getHandshakeSession());
+        if (alias==NO_MATCHERS)
+            alias=_delegate.chooseServerAlias(keyType,issuers,socket);
+        if (LOG.isDebugEnabled())
+            LOG.debug("Chose alias {}/{} on {}",alias,keyType,socket);
+        return alias;
+    }
+
+    @Override
+    public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine)
+    {
+        String alias = chooseServerAlias(keyType,issuers,engine.getSSLParameters().getSNIMatchers(),engine.getHandshakeSession());
+        if (alias==NO_MATCHERS)
+            alias=_delegate.chooseEngineServerAlias(keyType,issuers,engine);
+        if (LOG.isDebugEnabled())
+            LOG.debug("Chose alias {}/{} on {}",alias,keyType,engine);
+        return alias;
+    }
+
+    @Override
+    public X509Certificate[] getCertificateChain(String alias)
+    {
+        return _delegate.getCertificateChain(alias);
+    }
+
+    @Override
+    public String[] getClientAliases(String keyType, Principal[] issuers)
+    {
+        return _delegate.getClientAliases(keyType,issuers);
+    }
+
+    @Override
+    public PrivateKey getPrivateKey(String alias)
+    {
+        return _delegate.getPrivateKey(alias);
+    }
+
+    @Override
+    public String[] getServerAliases(String keyType, Principal[] issuers)
+    {
+        return _delegate.getServerAliases(keyType,issuers);
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java
index 8420b9f..0731a94 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java
@@ -19,13 +19,12 @@
 package org.eclipse.jetty.util.ssl;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
-import java.security.InvalidParameterException;
+import java.net.MalformedURLException;
 import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.security.Security;
 import java.security.cert.CRL;
@@ -35,19 +34,27 @@
 import java.security.cert.PKIXBuilderParameters;
 import java.security.cert.X509CertSelector;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import javax.net.ssl.CertPathTrustManagerParameters;
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIMatcher;
+import javax.net.ssl.SNIServerName;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLParameters;
@@ -55,15 +62,19 @@
 import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLServerSocketFactory;
 import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.StandardConstants;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509ExtendedKeyManager;
 import javax.net.ssl.X509TrustManager;
 
-import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.resource.Resource;
@@ -71,14 +82,13 @@
 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 class SslContextFactory extends AbstractLifeCycle implements Dumpable
 {
     public final static TrustManager[] TRUST_ALL_CERTS = new X509TrustManager[]{new X509TrustManager()
     {
@@ -96,15 +106,15 @@
         }
     }};
 
-    static final Logger LOG = Log.getLogger(SslContextFactory.class);
+    private static final Logger LOG = Log.getLogger(SslContextFactory.class);
 
     public static final String DEFAULT_KEYMANAGERFACTORY_ALGORITHM =
-        (Security.getProperty("ssl.KeyManagerFactory.algorithm") == null ?
-                KeyManagerFactory.getDefaultAlgorithm() : Security.getProperty("ssl.KeyManagerFactory.algorithm"));
+            (Security.getProperty("ssl.KeyManagerFactory.algorithm") == null ?
+                    KeyManagerFactory.getDefaultAlgorithm() : Security.getProperty("ssl.KeyManagerFactory.algorithm"));
 
     public static final String DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM =
-        (Security.getProperty("ssl.TrustManagerFactory.algorithm") == null ?
-                TrustManagerFactory.getDefaultAlgorithm() : Security.getProperty("ssl.TrustManagerFactory.algorithm"));
+            (Security.getProperty("ssl.TrustManagerFactory.algorithm") == null ?
+                    TrustManagerFactory.getDefaultAlgorithm() : Security.getProperty("ssl.TrustManagerFactory.algorithm"));
 
     /** String name of key password property. */
     public static final String KEYPASSWORD_PROPERTY = "org.eclipse.jetty.ssl.keypassword";
@@ -112,100 +122,52 @@
     /** 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<>();
-
-    /** Included protocols. */
     private final Set<String> _includeProtocols = new LinkedHashSet<>();
-
-    /** Excluded cipher suites. */
     private final Set<String> _excludeCipherSuites = new LinkedHashSet<>();
-    
-    /** Included cipher suites. */
-    private final Set<String> _includeCipherSuites = new LinkedHashSet<>();
-
-    /** Keystore path. */
-    private String _keyStorePath;
-    /** Keystore provider name */
+    private final List<String> _includeCipherSuites = new ArrayList<>();
+    private final Map<String, X509> _aliasX509 = new HashMap<>();
+    private final Map<String, X509> _certHosts = new HashMap<>();
+    private final Map<String, X509> _certWilds = new HashMap<>();
+    private String[] _selectedProtocols;
+    private boolean _useCipherSuitesOrder = true;
+    private Comparator<String> _cipherComparator;
+    private String[] _selectedCipherSuites;
+    private Resource _keyStoreResource;
     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 Resource _trustStoreResource;
     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;
-
-    /** Keystore password */
-    private transient Password _keyStorePassword;
-    /** Key manager password */
-    private transient Password _keyManagerPassword;
-    /** Truststore password */
-    private transient Password _trustStorePassword;
-
-    /** SSL provider name */
+    private Password _keyStorePassword;
+    private Password _keyManagerPassword;
+    private Password _trustStorePassword;
     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 KeyStore _setKeyStore;
+    private KeyStore _setTrustStore;
     private boolean _sessionCachingEnabled = true;
-    /** SSL session cache size */
-    private int _sslSessionCacheSize;
-    /** SSL session timeout */
-    private int _sslSessionTimeout;
-
-    /** SSL context */
-    private SSLContext _context;
-
-    /** EndpointIdentificationAlgorithm - when set to "HTTPS" hostname verification will be enabled */
+    private int _sslSessionCacheSize = -1;
+    private int _sslSessionTimeout = -1;
+    private SSLContext _setContext;
     private String _endpointIdentificationAlgorithm = null;
-
-    /** Whether to blindly trust certificates */
     private boolean _trustAll;
-
-    /** Whether TLS renegotiation is allowed */
     private boolean _renegotiationAllowed = true;
+    private int _renegotiationLimit = 5;
+    private Factory _factory;
 
     /**
      * Construct an instance of SslContextFactory
@@ -219,39 +181,61 @@
     /**
      * 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)
     {
-        setTrustAll(trustAll);
-        addExcludeProtocols("SSL", "SSLv2", "SSLv2Hello", "SSLv3");
+        this(trustAll, null);
     }
 
     /**
      * Construct an instance of SslContextFactory
+     *
      * @param keyStorePath default keystore location
      */
     public SslContextFactory(String keyStorePath)
     {
-        _keyStorePath = keyStorePath;
+        this(false, keyStorePath);
+    }
+
+    private SslContextFactory(boolean trustAll, String keyStorePath)
+    {
+        setTrustAll(trustAll);
+        addExcludeProtocols("SSL", "SSLv2", "SSLv2Hello", "SSLv3");
+        setExcludeCipherSuites("^.*_(MD5|SHA|SHA1)$");
+        if (keyStorePath != null)
+            setKeyStorePath(keyStorePath);
     }
 
     /**
-     * Create the SSLContext object and start the lifecycle
-     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
+     * Create the SSLContext object and starts the lifecycle
      */
     @Override
     protected void doStart() throws Exception
     {
-        if (_context == null)
+        super.doStart();
+        synchronized (this)
         {
-            if (_keyStore==null && _keyStoreInputStream == null && _keyStorePath == null &&
-                _trustStore==null && _trustStoreInputStream == null && _trustStorePath == null )
-            {
-                TrustManager[] trust_managers=null;
+            load();
+        }
+    }
 
-                if (_trustAll)
+    private void load() throws Exception
+    {
+        SSLContext context = _setContext;
+        KeyStore keyStore = _setKeyStore;
+        KeyStore trustStore = _setTrustStore;
+
+        if (context == null)
+        {
+            // Is this an empty factory?
+            if (keyStore == null && _keyStoreResource == null && trustStore == null && _trustStoreResource == null)
+            {
+                TrustManager[] trust_managers = null;
+
+                if (isTrustAll())
                 {
                     if (LOG.isDebugEnabled())
                         LOG.debug("No keystore or trust store configured.  ACCEPTING UNTRUSTED CERTIFICATES!!!!!");
@@ -259,86 +243,205 @@
                     trust_managers = TRUST_ALL_CERTS;
                 }
 
-                SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm);
-                SSLContext context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol, _sslProvider);
+                String algorithm = getSecureRandomAlgorithm();
+                SecureRandom secureRandom = algorithm == null ? null : SecureRandom.getInstance(algorithm);
+                context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol, _sslProvider);
                 context.init(null, trust_managers, secureRandom);
-                _context = context;
             }
             else
             {
-                // verify that keystore and truststore
-                // parameters are set up correctly
-                checkKeyStore();
+                if (keyStore == null)
+                    keyStore = loadKeyStore(_keyStoreResource);
+                if (trustStore == null)
+                    trustStore = loadTrustStore(_trustStoreResource);
 
-                KeyStore keyStore = loadKeyStore();
-                KeyStore trustStore = loadTrustStore();
+                Collection<? extends CRL> crls = loadCRL(getCrlPath());
 
-                Collection<? extends CRL> crls = loadCRL(_crlPath);
-
-                if (_validateCerts && keyStore != null)
+                // Look for X.509 certificates to create alias map
+                if (keyStore != null)
                 {
-                    if (_certAlias == null)
+                    for (String alias : Collections.list(keyStore.aliases()))
                     {
-                        List<String> aliases = Collections.list(keyStore.aliases());
-                        _certAlias = aliases.size() == 1 ? aliases.get(0) : null;
-                    }
+                        Certificate certificate = keyStore.getCertificate(alias);
+                        if (certificate != null && "X.509".equals(certificate.getType()))
+                        {
+                            X509Certificate x509C = (X509Certificate)certificate;
 
-                    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));
-                    }
+                            // Exclude certificates with special uses
+                            if (X509.isCertSign(x509C))
+                            {
+                                if (LOG.isDebugEnabled())
+                                    LOG.debug("Skipping " + x509C);
+                                continue;
+                            }
+                            X509 x509 = new X509(alias, x509C);
+                            _aliasX509.put(alias, x509);
 
-                    CertificateValidator validator = new CertificateValidator(trustStore, crls);
-                    validator.setMaxCertPathLength(_maxCertPathLength);
-                    validator.setEnableCRLDP(_enableCRLDP);
-                    validator.setEnableOCSP(_enableOCSP);
-                    validator.setOcspResponderURL(_ocspResponderURL);
-                    validator.validate(keyStore, cert);
+                            if (isValidateCerts())
+                            {
+                                CertificateValidator validator = new CertificateValidator(trustStore, crls);
+                                validator.setMaxCertPathLength(getMaxCertPathLength());
+                                validator.setEnableCRLDP(isEnableCRLDP());
+                                validator.setEnableOCSP(isEnableOCSP());
+                                validator.setOcspResponderURL(getOcspResponderURL());
+                                validator.validate(keyStore, x509C); // TODO what about truststore?
+                            }
+
+                            LOG.info("x509={} for {}", x509, this);
+
+                            for (String h : x509.getHosts())
+                                _certHosts.put(h, x509);
+                            for (String w : x509.getWilds())
+                                _certWilds.put(w, x509);
+                        }
+                    }
                 }
 
+                // Instantiate key and trust managers
                 KeyManager[] keyManagers = getKeyManagers(keyStore);
-                TrustManager[] trustManagers = getTrustManagers(trustStore,crls);
+                TrustManager[] trustManagers = getTrustManagers(trustStore, crls);
 
-                SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm);
-                SSLContext context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol, _sslProvider);
-                context.init(keyManagers,trustManagers,secureRandom);
-                _context = context;
-            }
-
-            SSLEngine engine = newSSLEngine();
-            if (LOG.isDebugEnabled())
-            {
-                LOG.debug("Enabled Protocols {} of {}",Arrays.asList(engine.getEnabledProtocols()),Arrays.asList(engine.getSupportedProtocols()));
-                LOG.debug("Enabled Ciphers   {} of {}",Arrays.asList(engine.getEnabledCipherSuites()),Arrays.asList(engine.getSupportedCipherSuites()));
+                // Initialize context
+                SecureRandom secureRandom = (_secureRandomAlgorithm == null) ? null : SecureRandom.getInstance(_secureRandomAlgorithm);
+                context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol, _sslProvider);
+                context.init(keyManagers, trustManagers, secureRandom);
             }
         }
-    }
 
+        // Initialize cache
+        SSLSessionContext serverContext = context.getServerSessionContext();
+        if (serverContext != null)
+        {
+            if (getSslSessionCacheSize() > -1)
+                serverContext.setSessionCacheSize(getSslSessionCacheSize());
+            if (getSslSessionTimeout() > -1)
+                serverContext.setSessionTimeout(getSslSessionTimeout());
+        }
+
+        // select the protocols and ciphers
+        SSLParameters enabled = context.getDefaultSSLParameters();
+        SSLParameters supported = context.getSupportedSSLParameters();
+        selectCipherSuites(enabled.getCipherSuites(), supported.getCipherSuites());
+        selectProtocols(enabled.getProtocols(), supported.getProtocols());
+
+        _factory = new Factory(keyStore, trustStore, context);
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("Selected Protocols {} of {}", Arrays.asList(_selectedProtocols), Arrays.asList(supported.getProtocols()));
+            LOG.debug("Selected Ciphers   {} of {}", Arrays.asList(_selectedCipherSuites), Arrays.asList(supported.getCipherSuites()));
+        }
+    }
+    
+    @Override
+    public String dump()
+    {
+        return ContainerLifeCycle.dump(this);
+    }
+    
+    @Override
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        out.append(String.valueOf(this)).append(" trustAll=").append(Boolean.toString(_trustAll)).append(System.lineSeparator());
+    
+        try
+        {
+            /* Use a pristine SSLEngine (not one from this SslContextFactory).
+             * This will allow for proper detection and identification
+             * of JRE/lib/security/java.security level disabled features
+             */
+            SSLEngine sslEngine = SSLContext.getDefault().createSSLEngine();
+    
+            List<Object> selections = new ArrayList<>();
+            
+            // protocols
+            selections.add(new SslSelectionDump("Protocol",
+                    sslEngine.getSupportedProtocols(),
+                    sslEngine.getEnabledProtocols(),
+                    getExcludeProtocols(),
+                    getIncludeProtocols()));
+            
+            // ciphers
+            selections.add(new SslSelectionDump("Cipher Suite",
+                    sslEngine.getSupportedCipherSuites(),
+                    sslEngine.getEnabledCipherSuites(),
+                    getExcludeCipherSuites(),
+                    getIncludeCipherSuites()));
+            
+            ContainerLifeCycle.dump(out, indent, selections);
+        }
+        catch (NoSuchAlgorithmException ignore)
+        {
+            LOG.ignore(ignore);
+        }
+    }
+    
     @Override
     protected void doStop() throws Exception
     {
-        _context = null;
+        synchronized (this)
+        {
+            unload();
+        }
         super.doStop();
     }
 
+    private void unload()
+    {
+        _factory = null;
+        _selectedProtocols = null;
+        _selectedCipherSuites = null;
+        _aliasX509.clear();
+        _certHosts.clear();
+        _certWilds.clear();
+    }
+
+    public String[] getSelectedProtocols()
+    {
+        return Arrays.copyOf(_selectedProtocols, _selectedProtocols.length);
+    }
+
+    public String[] getSelectedCipherSuites()
+    {
+        return Arrays.copyOf(_selectedCipherSuites, _selectedCipherSuites.length);
+    }
+
+    public Comparator<String> getCipherComparator()
+    {
+        return _cipherComparator;
+    }
+
+    public void setCipherComparator(Comparator<String> cipherComparator)
+    {
+        if (cipherComparator != null)
+            setUseCipherSuitesOrder(true);
+        _cipherComparator = cipherComparator;
+    }
+
+    public Set<String> getAliases()
+    {
+        return Collections.unmodifiableSet(_aliasX509.keySet());
+    }
+
+    public X509 getX509(String alias)
+    {
+        return _aliasX509.get(alias);
+    }
+
     /**
      * @return The array of protocol names to exclude from
      * {@link SSLEngine#setEnabledProtocols(String[])}
      */
     public String[] getExcludeProtocols()
     {
-        return _excludeProtocols.toArray(new String[_excludeProtocols.size()]);
+        return _excludeProtocols.toArray(new String[0]);
     }
 
     /**
-     * @param protocols
-     *            The array of protocol names to exclude from
-     *            {@link SSLEngine#setEnabledProtocols(String[])}
+     * @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));
     }
@@ -348,7 +451,6 @@
      */
     public void addExcludeProtocols(String... protocol)
     {
-        checkNotStarted();
         _excludeProtocols.addAll(Arrays.asList(protocol));
     }
 
@@ -358,17 +460,15 @@
      */
     public String[] getIncludeProtocols()
     {
-        return _includeProtocols.toArray(new String[_includeProtocols.size()]);
+        return _includeProtocols.toArray(new String[0]);
     }
 
     /**
-     * @param protocols
-     *            The array of protocol names to include in
-     *            {@link SSLEngine#setEnabledProtocols(String[])}
+     * @param protocols The array of protocol names to include in
+     *                  {@link SSLEngine#setEnabledProtocols(String[])}
      */
     public void setIncludeProtocols(String... protocols)
     {
-        checkNotStarted();
         _includeProtocols.clear();
         _includeProtocols.addAll(Arrays.asList(protocols));
     }
@@ -379,18 +479,17 @@
      */
     public String[] getExcludeCipherSuites()
     {
-        return _excludeCipherSuites.toArray(new String[_excludeCipherSuites.size()]);
+        return _excludeCipherSuites.toArray(new String[0]);
     }
 
     /**
      * You can either use the exact cipher suite name or a a regular expression.
-     * @param cipherSuites
-     *            The array of cipher suite names to exclude from
-     *            {@link SSLEngine#setEnabledCipherSuites(String[])}
+     *
+     * @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));
     }
@@ -400,7 +499,6 @@
      */
     public void addExcludeCipherSuites(String... cipher)
     {
-        checkNotStarted();
         _excludeCipherSuites.addAll(Arrays.asList(cipher));
     }
 
@@ -410,38 +508,52 @@
      */
     public String[] getIncludeCipherSuites()
     {
-        return _includeCipherSuites.toArray(new String[_includeCipherSuites.size()]);
+        return _includeCipherSuites.toArray(new String[0]);
     }
 
     /**
      * You can either use the exact cipher suite name or a a regular expression.
-     * @param cipherSuites
-     *            The array of cipher suite names to include in
-     *            {@link SSLEngine#setEnabledCipherSuites(String[])}
+     *
+     * @param cipherSuites The array of cipher suite names to include in
+     *                     {@link SSLEngine#setEnabledCipherSuites(String[])}
      */
     public void setIncludeCipherSuites(String... cipherSuites)
     {
-        checkNotStarted();
         _includeCipherSuites.clear();
         _includeCipherSuites.addAll(Arrays.asList(cipherSuites));
     }
 
+    public boolean isUseCipherSuitesOrder()
+    {
+        return _useCipherSuitesOrder;
+    }
+
+    public void setUseCipherSuitesOrder(boolean useCipherSuitesOrder)
+    {
+        _useCipherSuitesOrder = useCipherSuitesOrder;
+    }
+
     /**
      * @return The file or URL of the SSL Key store.
      */
     public String getKeyStorePath()
     {
-        return _keyStorePath;
+        return _keyStoreResource.toString();
     }
 
     /**
-     * @param keyStorePath
-     *            The file or URL of the SSL Key store.
+     * @param keyStorePath The file or URL of the SSL Key store.
      */
     public void setKeyStorePath(String keyStorePath)
     {
-        checkNotStarted();
-        _keyStorePath = keyStorePath;
+        try
+        {
+            _keyStoreResource = Resource.newResource(keyStorePath);
+        }
+        catch (MalformedURLException e)
+        {
+            throw new IllegalArgumentException(e);
+        }
     }
 
     /**
@@ -453,12 +565,10 @@
     }
 
     /**
-     * @param keyStoreProvider
-     *            The provider of the key store
+     * @param keyStoreProvider The provider of the key store
      */
     public void setKeyStoreProvider(String keyStoreProvider)
     {
-        checkNotStarted();
         _keyStoreProvider = keyStoreProvider;
     }
 
@@ -471,12 +581,10 @@
     }
 
     /**
-     * @param keyStoreType
-     *            The type of the key store (default "JKS")
+     * @param keyStoreType The type of the key store (default "JKS")
      */
     public void setKeyStoreType(String keyStoreType)
     {
-        checkNotStarted();
         _keyStoreType = keyStoreType;
     }
 
@@ -489,31 +597,32 @@
     }
 
     /**
-     * @param certAlias
-     *            Alias of SSL certificate for the connector
+     * Set the default certificate Alias.
+     * <p>This can be used if there are multiple non-SNI certificates
+     * to specify the certificate that should be used, or with SNI
+     * certificates to set a certificate to try if no others match
+     * </p>
+     *
+     * @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
+     * @param trustStorePath The file name or URL of the trust store location
      */
     public void setTrustStorePath(String trustStorePath)
     {
-        checkNotStarted();
-        _trustStorePath = trustStorePath;
+        try
+        {
+            _trustStoreResource = Resource.newResource(trustStorePath);
+        }
+        catch (MalformedURLException e)
+        {
+            throw new IllegalArgumentException(e);
+        }
     }
 
     /**
@@ -525,12 +634,10 @@
     }
 
     /**
-     * @param trustStoreProvider
-     *            The provider of the trust store
+     * @param trustStoreProvider The provider of the trust store
      */
     public void setTrustStoreProvider(String trustStoreProvider)
     {
-        checkNotStarted();
         _trustStoreProvider = trustStoreProvider;
     }
 
@@ -543,12 +650,10 @@
     }
 
     /**
-     * @param trustStoreType
-     *            The type of the trust store (default "JKS")
+     * @param trustStoreType The type of the trust store (default "JKS")
      */
     public void setTrustStoreType(String trustStoreType)
     {
-        checkNotStarted();
         _trustStoreType = trustStoreType;
     }
 
@@ -562,13 +667,11 @@
     }
 
     /**
-     * @param needClientAuth
-     *            True if SSL needs client authentication.
+     * @param needClientAuth True if SSL needs client authentication.
      * @see SSLEngine#getNeedClientAuth()
      */
     public void setNeedClientAuth(boolean needClientAuth)
     {
-        checkNotStarted();
         _needClientAuth = needClientAuth;
     }
 
@@ -582,13 +685,11 @@
     }
 
     /**
-     * @param wantClientAuth
-     *            True if SSL wants client authentication.
+     * @param wantClientAuth True if SSL wants client authentication.
      * @see SSLEngine#getWantClientAuth()
      */
     public void setWantClientAuth(boolean wantClientAuth)
     {
-        checkNotStarted();
         _wantClientAuth = wantClientAuth;
     }
 
@@ -601,12 +702,10 @@
     }
 
     /**
-     * @param validateCerts
-     *            true if SSL certificates have to be validated
+     * @param validateCerts true if SSL certificates have to be validated
      */
     public void setValidateCerts(boolean validateCerts)
     {
-        checkNotStarted();
         _validateCerts = validateCerts;
     }
 
@@ -619,44 +718,76 @@
     }
 
     /**
-     * @param validatePeerCerts
-     *            true if SSL certificates of the peer have to be validated
+     * @param validatePeerCerts true if SSL certificates of the peer have to be validated
      */
     public void setValidatePeerCerts(boolean validatePeerCerts)
     {
-        checkNotStarted();
         _validatePeerCerts = validatePeerCerts;
     }
 
-
     /**
-     * @param password
-     *            The password for the key store
+     * @param password The password for the key store.  If null is passed and
+     *                 a keystore is set, then
+     *                 the {@link Password#getPassword(String, String, String)} is used to
+     *                 obtain a password either from the {@value #PASSWORD_PROPERTY}
+     *                 System property or by prompting for manual entry.
      */
     public void setKeyStorePassword(String password)
     {
-        checkNotStarted();
-        _keyStorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null);
+        if (password == null)
+        {
+            if (_keyStoreResource != null)
+                _keyStorePassword = Password.getPassword(PASSWORD_PROPERTY, null, null);
+            else
+                _keyStorePassword = null;
+        }
+        else
+        {
+            _keyStorePassword = new Password(password);
+        }
     }
 
     /**
-     * @param password
-     *            The password (if any) for the specific key within the key store
+     * @param password The password (if any) for the specific key within the key store.
+     *                 If null is passed and the {@value #KEYPASSWORD_PROPERTY} system property is set,
+     *                 then the {@link Password#getPassword(String, String, String)} is used to
+     *                 obtain a password from the {@value #KEYPASSWORD_PROPERTY}  system property.
      */
     public void setKeyManagerPassword(String password)
     {
-        checkNotStarted();
-        _keyManagerPassword = Password.getPassword(KEYPASSWORD_PROPERTY,password,null);
+        if (password == null)
+        {
+            if (System.getProperty(KEYPASSWORD_PROPERTY) != null)
+                _keyManagerPassword = Password.getPassword(KEYPASSWORD_PROPERTY, null, null);
+            else
+                _keyManagerPassword = null;
+        }
+        else
+        {
+            _keyManagerPassword = new Password(password);
+        }
     }
 
     /**
-     * @param password
-     *            The password for the trust store
+     * @param password The password for the trust store. If null is passed and a truststore is set
+     *                 that is different from the keystore, then
+     *                 the {@link Password#getPassword(String, String, String)} is used to
+     *                 obtain a password either from the {@value #PASSWORD_PROPERTY}
+     *                 System property or by prompting for manual entry.
      */
     public void setTrustStorePassword(String password)
     {
-        checkNotStarted();
-        _trustStorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null);
+        if (password == null)
+        {
+            if (_trustStoreResource != null && !_trustStoreResource.equals(_keyStoreResource))
+                _trustStorePassword = Password.getPassword(PASSWORD_PROPERTY, null, null);
+            else
+                _trustStorePassword = null;
+        }
+        else
+        {
+            _trustStorePassword = new Password(password);
+        }
     }
 
     /**
@@ -669,13 +800,11 @@
     }
 
     /**
-     * @param provider
-     *            The SSL provider name, which if set is passed to
-     *            {@link SSLContext#getInstance(String, String)}
+     * @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;
     }
 
@@ -689,13 +818,11 @@
     }
 
     /**
-     * @param protocol
-     *            The SSL protocol (default "TLS") passed to
-     *            {@link SSLContext#getInstance(String, String)}
+     * @param protocol The SSL protocol (default "TLS") passed to
+     *                 {@link SSLContext#getInstance(String, String)}
      */
     public void setProtocol(String protocol)
     {
-        checkNotStarted();
         _sslProtocol = protocol;
     }
 
@@ -710,32 +837,46 @@
     }
 
     /**
-     * @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)}
+     * @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;
     }
 
     /**
+     * @deprecated use {@link #getKeyManagerFactoryAlgorithm()} instead
+     */
+    @Deprecated
+    public String getSslKeyManagerFactoryAlgorithm()
+    {
+        return getKeyManagerFactoryAlgorithm();
+    }
+
+    /**
+     * @deprecated use {@link #setKeyManagerFactoryAlgorithm(String)} instead
+     */
+    @Deprecated
+    public void setSslKeyManagerFactoryAlgorithm(String algorithm)
+    {
+        setKeyManagerFactoryAlgorithm(algorithm);
+    }
+
+    /**
      * @return The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
      */
-    public String getSslKeyManagerFactoryAlgorithm()
+    public String getKeyManagerFactoryAlgorithm()
     {
-        return (_keyManagerFactoryAlgorithm);
+        return _keyManagerFactoryAlgorithm;
     }
 
     /**
-     * @param algorithm
-     *            The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
+     * @param algorithm The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
      */
-    public void setSslKeyManagerFactoryAlgorithm(String algorithm)
+    public void setKeyManagerFactoryAlgorithm(String algorithm)
     {
-        checkNotStarted();
         _keyManagerFactoryAlgorithm = algorithm;
     }
 
@@ -744,7 +885,7 @@
      */
     public String getTrustManagerFactoryAlgorithm()
     {
-        return (_trustManagerFactoryAlgorithm);
+        return _trustManagerFactoryAlgorithm;
     }
 
     /**
@@ -761,18 +902,16 @@
     public void setTrustAll(boolean trustAll)
     {
         _trustAll = trustAll;
-        if(trustAll)
+        if (trustAll)
             setEndpointIdentificationAlgorithm(null);
     }
 
     /**
-     * @param algorithm
-     *            The algorithm name (default "SunX509") used by the {@link TrustManagerFactory}
-     *            Use the string "TrustAll" to install a trust manager that trusts all.
+     * @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;
     }
 
@@ -793,6 +932,25 @@
     }
 
     /**
+     * @return The number of renegotions allowed for this connection.  When the limit
+     * is 0 renegotiation will be denied. If the limit is less than 0 then no limit is applied. 
+     */
+    public int getRenegotiationLimit()
+    {
+        return _renegotiationLimit;
+    }
+
+    /**
+     * @param renegotiationLimit The number of renegotions allowed for this connection.  
+     * When the limit is 0 renegotiation will be denied. If the limit is less than 0 then no limit is applied.
+     * Default 5.
+     */
+    public void setRenegotiationLimit(int renegotiationLimit)
+    {
+        _renegotiationLimit = renegotiationLimit;
+    }
+    
+    /**
      * @return Path to file that contains Certificate Revocation List
      */
     public String getCrlPath()
@@ -801,12 +959,10 @@
     }
 
     /**
-     * @param crlPath
-     *            Path to file that contains Certificate Revocation List
+     * @param crlPath Path to file that contains Certificate Revocation List
      */
     public void setCrlPath(String crlPath)
     {
-        checkNotStarted();
         _crlPath = crlPath;
     }
 
@@ -820,13 +976,11 @@
     }
 
     /**
-     * @param maxCertPathLength
-     *            maximum number of intermediate certificates in
-     *            the certification path (-1 for unlimited)
+     * @param maxCertPathLength maximum number of intermediate certificates in
+     *                          the certification path (-1 for unlimited)
      */
     public void setMaxCertPathLength(int maxCertPathLength)
     {
-        checkNotStarted();
         _maxCertPathLength = maxCertPathLength;
     }
 
@@ -836,18 +990,28 @@
     public SSLContext getSslContext()
     {
         if (!isStarted())
-            throw new IllegalStateException(getState());
-        return _context;
+            return _setContext;
+
+        synchronized (this)
+        {
+            return _factory._context;
+        }
     }
 
     /**
-     * @param sslContext
-     *            Set a preconfigured SSLContext
+     * @param sslContext Set a preconfigured SSLContext
      */
     public void setSslContext(SSLContext sslContext)
     {
-        checkNotStarted();
-        _context = sslContext;
+        _setContext = sslContext;
+    }
+
+    /**
+     * @return the endpoint identification algorithm
+     */
+    public String getEndpointIdentificationAlgorithm()
+    {
+        return _endpointIdentificationAlgorithm;
     }
 
     /**
@@ -857,38 +1021,50 @@
      */
     public void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm)
     {
-        this._endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
+        _endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
     }
 
     /**
      * Override this method to provide alternate way to load a keystore.
      *
+     * @param resource the resource to load the keystore from
      * @return the key store instance
      * @throws Exception if the keystore cannot be loaded
      */
-    protected KeyStore loadKeyStore() throws Exception
+    protected KeyStore loadKeyStore(Resource resource) throws Exception
     {
-        return _keyStore != null ? _keyStore : CertificateUtils.getKeyStore(_keyStoreInputStream,
-                _keyStorePath, _keyStoreType, _keyStoreProvider,
-                _keyStorePassword==null? null: _keyStorePassword.toString());
+        String storePassword = _keyStorePassword == null ? null : _keyStorePassword.toString();
+        return CertificateUtils.getKeyStore(resource, getKeyStoreType(), getKeyStoreProvider(), storePassword);
     }
 
     /**
      * Override this method to provide alternate way to load a truststore.
      *
+     * @param resource the resource to load the truststore from
      * @return the key store instance
      * @throws Exception if the truststore cannot be loaded
      */
-    protected KeyStore loadTrustStore() throws Exception
+    protected KeyStore loadTrustStore(Resource resource) throws Exception
     {
-        return _trustStore != null ? _trustStore : CertificateUtils.getKeyStore(_trustStoreInputStream,
-                _trustStorePath, _trustStoreType,  _trustStoreProvider,
-                _trustStorePassword==null? null: _trustStorePassword.toString());
+        String type = getTrustStoreType();
+        String provider = getTrustStoreProvider();
+        String passwd = _trustStorePassword == null ? null : _trustStorePassword.toString();
+        if (resource == null || resource.equals(_keyStoreResource))
+        {
+            resource = _keyStoreResource;
+            if (type == null)
+                type = _keyStoreType;
+            if (provider == null)
+                provider = _keyStoreProvider;
+            if (passwd == null)
+                passwd = _keyStorePassword == null ? null : _keyStorePassword.toString();
+        }
+        return CertificateUtils.getKeyStore(resource, type, provider, passwd);
     }
 
     /**
      * Loads certificate revocation list (CRL) from a file.
-     *
+     * <p>
      * Required for integrations to be able to override the mechanism used to
      * load CRL in order to provide their own implementation.
      *
@@ -907,22 +1083,36 @@
 
         if (keyStore != null)
         {
-            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(_keyManagerFactoryAlgorithm);
-            keyManagerFactory.init(keyStore,_keyManagerPassword == null?(_keyStorePassword == null?null:_keyStorePassword.toString().toCharArray()):_keyManagerPassword.toString().toCharArray());
+            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(getKeyManagerFactoryAlgorithm());
+            keyManagerFactory.init(keyStore, _keyManagerPassword == null ? (_keyStorePassword == null ? null : _keyStorePassword.toString().toCharArray()) : _keyManagerPassword.toString().toCharArray());
             managers = keyManagerFactory.getKeyManagers();
 
-            if (_certAlias != null)
+            if (managers != null)
             {
-                for (int idx = 0; idx < managers.length; idx++)
+                String alias = getCertAlias();
+                if (alias != null)
                 {
-                    if (managers[idx] instanceof X509KeyManager)
+                    for (int idx = 0; idx < managers.length; idx++)
                     {
-                        managers[idx] = new AliasedX509ExtendedKeyManager(_certAlias,(X509KeyManager)managers[idx]);
+                        if (managers[idx] instanceof X509ExtendedKeyManager)
+                            managers[idx] = new AliasedX509ExtendedKeyManager((X509ExtendedKeyManager)managers[idx], alias);
+                    }
+                }
+
+                if (!_certHosts.isEmpty() || !_certWilds.isEmpty())
+                {
+                    for (int idx = 0; idx < managers.length; idx++)
+                    {
+                        if (managers[idx] instanceof X509ExtendedKeyManager)
+                            managers[idx] = new SniX509ExtendedKeyManager((X509ExtendedKeyManager)managers[idx]);
                     }
                 }
             }
         }
 
+        if (LOG.isDebugEnabled())
+            LOG.debug("managers={} for {}", managers, this);
+
         return managers;
     }
 
@@ -932,9 +1122,9 @@
         if (trustStore != null)
         {
             // Revocation checking is only supported for PKIX algorithm
-            if (_validatePeerCerts && _trustManagerFactoryAlgorithm.equalsIgnoreCase("PKIX"))
+            if (isValidatePeerCerts() && "PKIX".equalsIgnoreCase(getTrustManagerFactoryAlgorithm()))
             {
-                PKIXBuilderParameters pbParams = new PKIXBuilderParameters(trustStore,new X509CertSelector());
+                PKIXBuilderParameters pbParams = new PKIXBuilderParameters(trustStore, new X509CertSelector());
 
                 // Set maximum certification path length
                 pbParams.setMaxPathLength(_maxCertPathLength);
@@ -944,19 +1134,19 @@
 
                 if (crls != null && !crls.isEmpty())
                 {
-                    pbParams.addCertStore(CertStore.getInstance("Collection",new CollectionCertStoreParameters(crls)));
+                    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");
+                    System.setProperty("com.sun.security.enableCRLDP", "true");
                 }
 
                 if (_enableOCSP)
                 {
                     // Enable On-Line Certificate Status Protocol (OCSP) support
-                    Security.setProperty("ocsp.enable","true");
+                    Security.setProperty("ocsp.enable", "true");
 
                     if (_ocspResponderURL != null)
                     {
@@ -983,60 +1173,14 @@
     }
 
     /**
-     * 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;
-
-        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 enabledProtocols   Array of enabled protocols
      * @param supportedProtocols Array of supported protocols
-     * @return Array of protocols to enable
      */
-    public String[] selectProtocols(String[] enabledProtocols, String[] supportedProtocols)
+    public void selectProtocols(String[] enabledProtocols, String[] supportedProtocols)
     {
         Set<String> selected_protocols = new LinkedHashSet<>();
 
@@ -1045,30 +1189,36 @@
         {
             // Use only the supported included protocols
             for (String protocol : _includeProtocols)
-                if(Arrays.asList(supportedProtocols).contains(protocol))
+            {
+                if (Arrays.asList(supportedProtocols).contains(protocol))
                     selected_protocols.add(protocol);
+                else
+                    LOG.info("Protocol {} not supported in {}", protocol, Arrays.asList(supportedProtocols));
+            }
         }
         else
             selected_protocols.addAll(Arrays.asList(enabledProtocols));
 
-
         // Remove any excluded protocols
         selected_protocols.removeAll(_excludeProtocols);
 
-        return selected_protocols.toArray(new String[selected_protocols.size()]);
+        if (selected_protocols.isEmpty())
+            LOG.warn("No selected protocols from {}", Arrays.asList(supportedProtocols));
+
+        _selectedProtocols = selected_protocols.toArray(new String[0]);
     }
 
     /**
      * 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 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)
+    protected void selectCipherSuites(String[] enabledCipherSuites, String[] supportedCipherSuites)
     {
-        Set<String> selected_ciphers = new CopyOnWriteArraySet<>();
+        List<String> selected_ciphers = new ArrayList<>();
 
         // Set the starting ciphers - either from the included or enabled list
         if (_includeCipherSuites.isEmpty())
@@ -1078,33 +1228,52 @@
 
         removeExcludedCipherSuites(selected_ciphers);
 
-        return selected_ciphers.toArray(new String[selected_ciphers.size()]);
+        if (selected_ciphers.isEmpty())
+            LOG.warn("No supported ciphers from {}", Arrays.asList(supportedCipherSuites));
+
+        Comparator<String> comparator = getCipherComparator();
+        if (comparator != null)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Sorting selected ciphers with {}", comparator);
+            Collections.sort(selected_ciphers, comparator);
+        }
+
+        _selectedCipherSuites = selected_ciphers.toArray(new String[0]);
     }
 
-    protected void processIncludeCipherSuites(String[] supportedCipherSuites, Set<String> selected_ciphers)
+    protected void processIncludeCipherSuites(String[] supportedCipherSuites, List<String> selected_ciphers)
     {
         for (String cipherSuite : _includeCipherSuites)
         {
             Pattern p = Pattern.compile(cipherSuite);
+            boolean added = false;
             for (String supportedCipherSuite : supportedCipherSuites)
             {
                 Matcher m = p.matcher(supportedCipherSuite);
                 if (m.matches())
+                {
+                    added = true;
                     selected_ciphers.add(supportedCipherSuite);
+                }
+
             }
+            if (!added)
+                LOG.info("No Cipher matching '{}' is supported", cipherSuite);
         }
     }
 
-    protected void removeExcludedCipherSuites(Set<String> selected_ciphers)
+    protected void removeExcludedCipherSuites(List<String> selected_ciphers)
     {
         for (String excludeCipherSuite : _excludeCipherSuites)
         {
             Pattern excludeCipherPattern = Pattern.compile(excludeCipherSuite);
-            for (String selectedCipherSuite : selected_ciphers)
+            for (Iterator<String> i = selected_ciphers.iterator(); i.hasNext(); )
             {
+                String selectedCipherSuite = i.next();
                 Matcher m = excludeCipherPattern.matcher(selectedCipherSuite);
                 if (m.matches())
-                    selected_ciphers.remove(selectedCipherSuite);
+                    i.remove();
             }
         }
     }
@@ -1112,10 +1281,10 @@
     /**
      * Check if the lifecycle has been started and throw runtime exception
      */
-    protected void checkNotStarted()
+    private void checkIsStarted()
     {
-        if (isStarted())
-            throw new IllegalStateException("Cannot modify configuration when "+getState());
+        if (!isStarted())
+            throw new IllegalStateException("!STARTED: " + this);
     }
 
     /**
@@ -1126,12 +1295,13 @@
         return _enableCRLDP;
     }
 
-    /** Enables CRL Distribution Points Support
+    /**
+     * Enables CRL Distribution Points Support
+     *
      * @param enableCRLDP true - turn on, false - turns off
      */
     public void setEnableCRLDP(boolean enableCRLDP)
     {
-        checkNotStarted();
         _enableCRLDP = enableCRLDP;
     }
 
@@ -1143,12 +1313,13 @@
         return _enableOCSP;
     }
 
-    /** Enables On-Line Certificate Status Protocol support
+    /**
+     * Enables On-Line Certificate Status Protocol support
+     *
      * @param enableOCSP true - turn on, false - turn off
      */
     public void setEnableOCSP(boolean enableOCSP)
     {
-        checkNotStarted();
         _enableOCSP = enableOCSP;
     }
 
@@ -1160,84 +1331,115 @@
         return _ocspResponderURL;
     }
 
-    /** Set the location of the OCSP Responder.
+    /**
+     * 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.
+    /**
+     * Set the key store.
+     *
      * @param keyStore the key store to set
      */
     public void setKeyStore(KeyStore keyStore)
     {
-        checkNotStarted();
-        _keyStore = keyStore;
+        _setKeyStore = keyStore;
     }
 
-    /** Set the trust store.
-     * @param trustStore the trust store to set
-     */
-    public void setTrustStore(KeyStore trustStore)
+    public KeyStore getKeyStore()
     {
-        checkNotStarted();
-        _trustStore = trustStore;
-    }
+        if (!isStarted())
+            return _setKeyStore;
 
-    /** Set the key store resource.
-     * @param resource the key store resource to set
-     */
-    public void setKeyStoreResource(Resource resource)
-    {
-        checkNotStarted();
-        try
+        synchronized (this)
         {
-            _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 _factory._keyStore;
         }
     }
 
     /**
-    * @return true if SSL Session caching is enabled
-    */
+     * Set the trust store.
+     *
+     * @param trustStore the trust store to set
+     */
+    public void setTrustStore(KeyStore trustStore)
+    {
+        _setTrustStore = trustStore;
+    }
+
+    public KeyStore getTrustStore()
+    {
+        if (!isStarted())
+            return _setTrustStore;
+
+        synchronized (this)
+        {
+            return _factory._trustStore;
+        }
+    }
+
+    /**
+     * Set the key store resource.
+     *
+     * @param resource the key store resource to set
+     */
+    public void setKeyStoreResource(Resource resource)
+    {
+        _keyStoreResource = resource;
+    }
+
+    public Resource getKeyStoreResource()
+    {
+        return _keyStoreResource;
+    }
+
+    /**
+     * Set the trust store resource.
+     *
+     * @param resource the trust store resource to set
+     */
+    public void setTrustStoreResource(Resource resource)
+    {
+        _trustStoreResource = resource;
+    }
+
+    public Resource getTrustStoreResource()
+    {
+        return _trustStoreResource;
+    }
+
+    /**
+     * @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
-    */
+    /**
+     * Set the flag to enable SSL Session caching.
+     * If set to true, then the {@link SSLContext#createSSLEngine(String, int)} method is
+     * used to pass host and port information as a hint for session reuse.  Note that
+     * this is only a hint and session may not be reused. Moreover, the hint is typically
+     * only used on client side implementations and setting this to false does not
+     * stop a server from accepting an offered session ID to reuse.
+     *
+     * @param enableSessionCaching the value of the flag
+     */
     public void setSessionCachingEnabled(boolean enableSessionCaching)
     {
         _sessionCachingEnabled = enableSessionCaching;
     }
 
-    /** Get SSL session cache size.
+    /**
+     * Get SSL session cache size.
+     * Passed directly to {@link SSLSessionContext#setSessionCacheSize(int)}
+     *
      * @return SSL session cache size
      */
     public int getSslSessionCacheSize()
@@ -1245,15 +1447,22 @@
         return _sslSessionCacheSize;
     }
 
-    /** SEt SSL session cache size.
-     * @param sslSessionCacheSize SSL session cache size to set
+    /**
+     * Set SSL session cache size.
+     * <p>Set the max cache size to be set on {@link SSLSessionContext#setSessionCacheSize(int)}
+     * when this factory is started.</p>
+     *
+     * @param sslSessionCacheSize SSL session cache size to set. A value  of -1 (default) uses
+     *                            the JVM default, 0 means unlimited and positive number is a max size.
      */
     public void setSslSessionCacheSize(int sslSessionCacheSize)
     {
         _sslSessionCacheSize = sslSessionCacheSize;
     }
 
-    /** Get SSL session timeout.
+    /**
+     * Get SSL session timeout.
+     *
      * @return SSL session timeout
      */
     public int getSslSessionTimeout()
@@ -1261,52 +1470,42 @@
         return _sslSessionTimeout;
     }
 
-    /** Set SSL session timeout.
-     * @param sslSessionTimeout SSL session timeout to set
+    /**
+     * Set SSL session timeout.
+     * <p>Set the timeout in seconds to be set on {@link SSLSessionContext#setSessionTimeout(int)}
+     * when this factory is started.</p>
+     *
+     * @param sslSessionTimeout SSL session timeout to set in seconds. A value of -1 (default) uses
+     *                          the JVM default, 0 means unlimited and positive number is a timeout in seconds.
      */
     public void setSslSessionTimeout(int sslSessionTimeout)
     {
         _sslSessionTimeout = sslSessionTimeout;
     }
 
-
-    public SSLServerSocket newSslServerSocket(String host,int port,int backlog) throws IOException
+    public SSLServerSocket newSslServerSocket(String host, int port, int backlog) throws IOException
     {
-        SSLServerSocketFactory factory = _context.getServerSocketFactory();
+        checkIsStarted();
 
+        SSLContext context = getSslContext();
+        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()));
+                (SSLServerSocket)(host == null ?
+                        factory.createServerSocket(port, backlog) :
+                        factory.createServerSocket(port, backlog, InetAddress.getByName(host)));
+        socket.setSSLParameters(customize(socket.getSSLParameters()));
 
         return socket;
     }
 
     public SSLSocket newSslSocket() throws IOException
     {
-        SSLSocketFactory factory = _context.getSocketFactory();
+        checkIsStarted();
 
+        SSLContext context = getSslContext();
+        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()));
+        socket.setSSLParameters(customize(socket.getSSLParameters()));
 
         return socket;
     }
@@ -1314,7 +1513,7 @@
     /**
      * Factory method for "scratch" {@link SSLEngine}s, usually only used for retrieving configuration
      * information such as the application buffer size or the list of protocols/ciphers.
-     * <p />
+     * <p>
      * This method should not be used for creating {@link SSLEngine}s that are used in actual socket
      * communication.
      *
@@ -1322,10 +1521,12 @@
      */
     public SSLEngine newSSLEngine()
     {
-        if (!isRunning())
-            throw new IllegalStateException("!STARTED");
-        SSLEngine sslEngine=_context.createSSLEngine();
+        checkIsStarted();
+
+        SSLContext context = getSslContext();
+        SSLEngine sslEngine = context.createSSLEngine();
         customize(sslEngine);
+
         return sslEngine;
     }
 
@@ -1339,28 +1540,30 @@
      */
     public SSLEngine newSSLEngine(String host, int port)
     {
-        if (!isRunning())
-            throw new IllegalStateException("!STARTED");
-        SSLEngine sslEngine=isSessionCachingEnabled()
-            ? _context.createSSLEngine(host, port)
-            : _context.createSSLEngine();
+        checkIsStarted();
+
+        SSLContext context = getSslContext();
+        SSLEngine sslEngine = isSessionCachingEnabled() ?
+                context.createSSLEngine(host, port) :
+                context.createSSLEngine();
         customize(sslEngine);
+
         return sslEngine;
     }
 
     /**
      * Server-side only factory method for creating {@link SSLEngine}s.
-     * <p />
+     * <p>
      * If the given {@code address} is null, it is equivalent to {@link #newSSLEngine()}, otherwise
      * {@link #newSSLEngine(String, int)} is called.
-     * <p />
+     * <p>
      * If {@link #getNeedClientAuth()} is {@code true}, then the host name is passed to
      * {@link #newSSLEngine(String, int)}, possibly incurring in a reverse DNS lookup, which takes time
      * and may hang the selector (since this method is usually called by the selector thread).
-     * <p />
+     * <p>
      * Otherwise, the host address is passed to {@link #newSSLEngine(String, int)} without DNS lookup
      * penalties.
-     * <p />
+     * <p>
      * Clients that wish to create {@link SSLEngine} instances must use {@link #newSSLEngine(String, int)}.
      *
      * @param address the remote peer address
@@ -1376,41 +1579,70 @@
         return newSSLEngine(hostName, address.getPort());
     }
 
+    /**
+     * Customize an SslEngine instance with the configuration of this factory,
+     * by calling {@link #customize(SSLParameters)}
+     *
+     * @param sslEngine the SSLEngine to customize
+     */
     public void customize(SSLEngine sslEngine)
     {
-        SSLParameters sslParams = sslEngine.getSSLParameters();
-        sslParams.setEndpointIdentificationAlgorithm(_endpointIdentificationAlgorithm);
-        sslEngine.setSSLParameters(sslParams);
+        if (LOG.isDebugEnabled())
+            LOG.debug("Customize {}", sslEngine);
 
+        sslEngine.setSSLParameters(customize(sslEngine.getSSLParameters()));
+    }
+
+    /**
+     * Customize an SslParameters instance with the configuration of this factory.
+     *
+     * @param sslParams The parameters to customize
+     * @return The passed instance of sslParams (returned as a convenience)
+     */
+    public SSLParameters customize(SSLParameters sslParams)
+    {
+        sslParams.setEndpointIdentificationAlgorithm(getEndpointIdentificationAlgorithm());
+        sslParams.setUseCipherSuitesOrder(isUseCipherSuitesOrder());
+        if (!_certHosts.isEmpty() || !_certWilds.isEmpty())
+            sslParams.setSNIMatchers(Collections.singletonList(new AliasSNIMatcher()));
+        if (_selectedCipherSuites != null)
+            sslParams.setCipherSuites(_selectedCipherSuites);
+        if (_selectedProtocols != null)
+            sslParams.setProtocols(_selectedProtocols);
         if (getWantClientAuth())
-            sslEngine.setWantClientAuth(getWantClientAuth());
+            sslParams.setWantClientAuth(true);
         if (getNeedClientAuth())
-            sslEngine.setNeedClientAuth(getNeedClientAuth());
+            sslParams.setNeedClientAuth(true);
+        return sslParams;
+    }
 
-        sslEngine.setEnabledCipherSuites(selectCipherSuites(
-                sslEngine.getEnabledCipherSuites(),
-                sslEngine.getSupportedCipherSuites()));
-
-        sslEngine.setEnabledProtocols(selectProtocols(sslEngine.getEnabledProtocols(),sslEngine.getSupportedProtocols()));
+    public void reload(Consumer<SslContextFactory> consumer) throws Exception
+    {
+        synchronized (this)
+        {
+            consumer.accept(this);
+            unload();
+            load();
+        }
     }
 
     public static X509Certificate[] getCertChain(SSLSession sslSession)
     {
         try
         {
-            Certificate[] javaxCerts=sslSession.getPeerCertificates();
-            if (javaxCerts==null||javaxCerts.length==0)
+            Certificate[] javaxCerts = sslSession.getPeerCertificates();
+            if (javaxCerts == null || javaxCerts.length == 0)
                 return null;
 
-            int length=javaxCerts.length;
-            X509Certificate[] javaCerts=new X509Certificate[length];
+            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++)
+            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);
+                byte bytes[] = javaxCerts[i].getEncoded();
+                ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
+                javaCerts[i] = (X509Certificate)cf.generateCertificate(stream);
             }
 
             return javaCerts;
@@ -1421,7 +1653,7 @@
         }
         catch (Exception e)
         {
-            LOG.warn(Log.EXCEPTION,e);
+            LOG.warn(Log.EXCEPTION, e);
             return null;
         }
     }
@@ -1430,11 +1662,10 @@
      * 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:
-     *
+     * <p>
      * <pre>
      *                         Effective
      *     Cipher       Type    Key Bits
@@ -1485,7 +1716,89 @@
         return String.format("%s@%x(%s,%s)",
                 getClass().getSimpleName(),
                 hashCode(),
-                _keyStorePath,
-                _trustStorePath);
+                _keyStoreResource,
+                _trustStoreResource);
+    }
+
+    class Factory
+    {
+        private final KeyStore _keyStore;
+        private final KeyStore _trustStore;
+        private final SSLContext _context;
+
+        Factory(KeyStore keyStore, KeyStore trustStore, SSLContext context)
+        {
+            super();
+            _keyStore = keyStore;
+            _trustStore = trustStore;
+            _context = context;
+        }
+    }
+
+    class AliasSNIMatcher extends SNIMatcher
+    {
+        private String _host;
+        private X509 _x509;
+
+        AliasSNIMatcher()
+        {
+            super(StandardConstants.SNI_HOST_NAME);
+        }
+
+        @Override
+        public boolean matches(SNIServerName serverName)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("SNI matching for {}", serverName);
+
+            if (serverName instanceof SNIHostName)
+            {
+                String host = _host = ((SNIHostName)serverName).getAsciiName();
+                host = StringUtil.asciiToLowerCase(host);
+
+                // Try an exact match
+                _x509 = _certHosts.get(host);
+
+                // Else try an exact wild match
+                if (_x509 == null)
+                {
+                    _x509 = _certWilds.get(host);
+
+                    // Else try an 1 deep wild match
+                    if (_x509 == null)
+                    {
+                        int dot = host.indexOf('.');
+                        if (dot >= 0)
+                        {
+                            String domain = host.substring(dot + 1);
+                            _x509 = _certWilds.get(domain);
+                        }
+                    }
+                }
+
+                if (LOG.isDebugEnabled())
+                    LOG.debug("SNI matched {}->{}", host, _x509);
+            }
+            else
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("SNI no match for {}", serverName);
+            }
+
+            // Return true and allow the KeyManager to accept or reject when choosing a certificate.
+            // If we don't have a SNI host, or didn't see any certificate aliases,
+            // just say true as it will either somehow work or fail elsewhere.
+            return true;
+        }
+
+        public String getHost()
+        {
+            return _host;
+        }
+
+        public X509 getX509()
+        {
+            return _x509;
+        }
     }
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslSelectionDump.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslSelectionDump.java
new file mode 100644
index 0000000..5c798f6
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslSelectionDump.java
@@ -0,0 +1,174 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+
+public class SslSelectionDump extends ContainerLifeCycle implements Dumpable
+{
+    private static class CaptionedList extends ArrayList<String> implements Dumpable
+    {
+        private final String caption;
+        
+        public CaptionedList(String caption)
+        {
+            this.caption = caption;
+        }
+        
+        @Override
+        public String dump()
+        {
+            return ContainerLifeCycle.dump(SslSelectionDump.CaptionedList.this);
+        }
+        
+        @Override
+        public void dump(Appendable out, String indent) throws IOException
+        {
+            out.append(caption);
+            out.append(" (size=").append(Integer.toString(size())).append(")");
+            out.append(System.lineSeparator());
+            ContainerLifeCycle.dump(out, indent, this);
+        }
+    }
+    
+    private final String type;
+    private SslSelectionDump.CaptionedList enabled = new SslSelectionDump.CaptionedList("Enabled");
+    private SslSelectionDump.CaptionedList disabled = new SslSelectionDump.CaptionedList("Disabled");
+    
+    public SslSelectionDump(String type,
+                            String[] supportedByJVM,
+                            String[] enabledByJVM,
+                            String[] excludedByConfig,
+                            String[] includedByConfig)
+    {
+        this.type = type;
+        addBean(enabled);
+        addBean(disabled);
+        
+        List<String> jvmEnabled = Arrays.asList(enabledByJVM);
+        List<Pattern> excludedPatterns = Arrays.stream(excludedByConfig)
+                .map((entry) -> Pattern.compile(entry))
+                .collect(Collectors.toList());
+        List<Pattern> includedPatterns = Arrays.stream(includedByConfig)
+                .map((entry) -> Pattern.compile(entry))
+                .collect(Collectors.toList());
+        
+        Arrays.stream(supportedByJVM)
+                .sorted(Comparator.naturalOrder())
+                .forEach((entry) ->
+                {
+                    boolean isPresent = true;
+                    
+                    StringBuilder s = new StringBuilder();
+                    s.append(entry);
+                    if (!jvmEnabled.contains(entry))
+                    {
+                        if (isPresent)
+                        {
+                            s.append(" -");
+                            isPresent = false;
+                        }
+                        s.append(" JreDisabled:java.security");
+                    }
+                    
+                    for (Pattern pattern : excludedPatterns)
+                    {
+                        Matcher m = pattern.matcher(entry);
+                        if (m.matches())
+                        {
+                            if (isPresent)
+                            {
+                                s.append(" -");
+                                isPresent = false;
+                            }
+                            else
+                            {
+                                s.append(",");
+                            }
+                            s.append(" ConfigExcluded:'").append(pattern.pattern()).append('\'');
+                        }
+                    }
+                    
+                    if (!includedPatterns.isEmpty())
+                    {
+                        boolean isIncluded = false;
+                        for (Pattern pattern : includedPatterns)
+                        {
+                            Matcher m = pattern.matcher(entry);
+                            if (m.matches())
+                            {
+                                isIncluded = true;
+                                break;
+                            }
+                        }
+                        
+                        if (!isIncluded)
+                        {
+                            if (isPresent)
+                            {
+                                s.append(" -");
+                                isPresent = false;
+                            }
+                            else
+                            {
+                                s.append(",");
+                            }
+                            s.append(" ConfigIncluded:NotSpecified");
+                        }
+                    }
+                    
+                    if (isPresent)
+                    {
+                        enabled.add(s.toString());
+                    }
+                    else
+                    {
+                        disabled.add(s.toString());
+                    }
+                });
+    }
+    
+    @Override
+    public String dump()
+    {
+        return ContainerLifeCycle.dump(this);
+    }
+    
+    @Override
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        dumpBeans(out, indent);
+    }
+    
+    @Override
+    protected void dumpThis(Appendable out) throws IOException
+    {
+        out.append(type).append(" Selections").append(System.lineSeparator());
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/X509.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/X509.java
new file mode 100644
index 0000000..692d0c1
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/X509.java
@@ -0,0 +1,163 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+import javax.security.auth.x500.X500Principal;
+
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class X509
+{
+    private static final Logger LOG = Log.getLogger(X509.class);
+
+    /*
+     * @see {@link X509Certificate#getKeyUsage()}
+     */
+    private static final int KEY_USAGE__KEY_CERT_SIGN=5;
+
+    /*
+     *
+     * @see {@link X509Certificate#getSubjectAlternativeNames()}
+     */
+    private static final int SUBJECT_ALTERNATIVE_NAMES__DNS_NAME=2;
+
+    public static boolean isCertSign(X509Certificate x509)
+    {
+        boolean[] key_usage=x509.getKeyUsage();
+        return key_usage!=null && key_usage[KEY_USAGE__KEY_CERT_SIGN];
+    }
+
+    private final X509Certificate _x509;
+    private final String _alias;
+    private final List<String> _hosts=new ArrayList<>();
+    private final List<String> _wilds=new ArrayList<>();
+
+    public X509(String alias,X509Certificate x509) throws CertificateParsingException, InvalidNameException
+    {
+        _alias=alias;
+        _x509 = x509;
+
+        // Look for alternative name extensions
+        boolean named=false;
+        Collection<List<?>> altNames = x509.getSubjectAlternativeNames();
+        if (altNames!=null)
+        {
+            for (List<?> list : altNames)
+            {
+                if (((Number)list.get(0)).intValue() == SUBJECT_ALTERNATIVE_NAMES__DNS_NAME)
+                {
+                    String cn = list.get(1).toString();
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Certificate SAN alias={} CN={} in {}",alias,cn,this);
+                    if (cn!=null)
+                    {
+                        named=true;
+                        addName(cn);
+                    }
+                }
+            }
+        }
+
+        // If no names found, look up the CN from the subject
+        if (!named)
+        {
+            LdapName name=new LdapName(x509.getSubjectX500Principal().getName(X500Principal.RFC2253));
+            for (Rdn rdn : name.getRdns())
+            {
+                if (rdn.getType().equalsIgnoreCase("CN"))
+                {
+                    String cn = rdn.getValue().toString();
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Certificate CN alias={} CN={} in {}",alias,cn,this);
+                    if (cn!=null && cn.contains(".") && !cn.contains(" "))
+                        addName(cn);
+                }
+            }
+        }
+    }
+
+    protected void addName(String cn)
+    {
+        cn=StringUtil.asciiToLowerCase(cn);
+        if (cn.startsWith("*."))
+            _wilds.add(cn.substring(2));
+        else
+            _hosts.add(cn);
+    }
+
+    public String getAlias()
+    {
+        return _alias;
+    }
+
+    public X509Certificate getCertificate()
+    {
+        return _x509;
+    }
+
+    public Set<String> getHosts()
+    {
+        return new HashSet<>(_hosts);
+    }
+
+    public Set<String> getWilds()
+    {
+        return new HashSet<>(_wilds);
+    }
+
+    public boolean matches(String host)
+    {
+        host=StringUtil.asciiToLowerCase(host);
+        if (_hosts.contains(host) || _wilds.contains(host))
+            return true;
+
+        int dot = host.indexOf('.');
+        if (dot>=0)
+        {
+            String domain=host.substring(dot+1);
+            if (_wilds.contains(domain))
+                return true;
+        }
+        return false;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%x(%s,h=%s,w=%s)",
+                getClass().getSimpleName(),
+                hashCode(),
+                _alias,
+                _hosts,
+                _wilds);
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/statistic/CounterStatistic.java b/jetty-util/src/main/java/org/eclipse/jetty/util/statistic/CounterStatistic.java
index 69fba96..9c6b8c8 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/statistic/CounterStatistic.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/statistic/CounterStatistic.java
@@ -19,9 +19,8 @@
 package org.eclipse.jetty.util.statistic;
 
 import java.util.concurrent.atomic.AtomicLong;
-
-import org.eclipse.jetty.util.Atomics;
-
+import java.util.concurrent.atomic.LongAccumulator;
+import java.util.concurrent.atomic.LongAdder;
 
 /* ------------------------------------------------------------ */
 /** Statistics on a counter value.
@@ -33,53 +32,70 @@
  */
 public class CounterStatistic
 {
-    protected final AtomicLong _max = new AtomicLong();
-    protected final AtomicLong _curr = new AtomicLong();
-    protected final AtomicLong _total = new AtomicLong();
+    protected final LongAccumulator _max = new LongAccumulator(Math::max,0L);
+    protected final AtomicLong _current = new AtomicLong();
+    protected final LongAdder _total = new LongAdder();
 
     /* ------------------------------------------------------------ */
     public void reset()
     {
-        reset(0);
+        _total.reset();
+        _max.reset();
+        long current=_current.get();
+        _total.add(current);
+        _max.accumulate(current);
     }
 
     /* ------------------------------------------------------------ */
     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
+        _current.set(value);
+        _total.reset();
+        _max.reset();
+        if (value>0)
+        {
+            _total.add(value);
+            _max.accumulate(value);
+        }
     }
 
     /* ------------------------------------------------------------ */
     /**
      * @param delta the amount to add to the count
+     * @return the new value
      */
     public long add(final long delta)
     {
-        long value=_curr.addAndGet(delta);
+        long value=_current.addAndGet(delta);
         if (delta > 0)
         {
-            _total.addAndGet(delta);
-            Atomics.updateMax(_max,value);
+            _total.add(delta);
+            _max.accumulate(value);
         }
         return value;
     }
 
     /* ------------------------------------------------------------ */
     /**
+     * increment the value by one
+     * @return the new value, post increment
      */
     public long increment()
     {
-        return add(1);
+        long value=_current.incrementAndGet();
+        _total.increment();
+        _max.accumulate(value);
+        return value;
     }
 
     /* ------------------------------------------------------------ */
     /**
+     * decrement by 1
+     * @return the new value, post-decrement
      */
     public long decrement()
     {
-        return add(-1);
+        return _current.decrementAndGet();
     }
 
     /* ------------------------------------------------------------ */
@@ -97,7 +113,7 @@
      */
     public long getCurrent()
     {
-        return _curr.get();
+        return _current.get();
     }
 
     /* ------------------------------------------------------------ */
@@ -106,13 +122,13 @@
      */
     public long getTotal()
     {
-        return _total.get();
+        return _total.sum();
     }
 
     /* ------------------------------------------------------------ */
     @Override
     public String toString()
     {
-        return String.format("%s@%x{c=%d,m=%d,t=%d}",this.getClass().getSimpleName(),hashCode(),_curr.get(),_max.get(),_total.get());
+        return String.format("%s@%x{c=%d,m=%d,t=%d}",this.getClass().getSimpleName(),hashCode(),_current.get(),_max.get(),_total.sum());
     }
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/statistic/SampleStatistic.java b/jetty-util/src/main/java/org/eclipse/jetty/util/statistic/SampleStatistic.java
index 2ce9983..3d98476 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/statistic/SampleStatistic.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/statistic/SampleStatistic.java
@@ -19,41 +19,40 @@
 package org.eclipse.jetty.util.statistic;
 
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.LongAccumulator;
+import java.util.concurrent.atomic.LongAdder;
 
 import org.eclipse.jetty.util.Atomics;
 
 
-/* ------------------------------------------------------------ */
 /**
  * SampledStatistics
  * <p>
- * Provides max, total, mean, count, variance, and standard
- * deviation of continuous sequence of samples.
+ * 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
+ * Calculates estimates of mean, variance, and standard deviation characteristics of a sample using a non synchronized
+ * approximation of the on-line algorithm presented in <cite>Donald Knuth's Art of Computer Programming, Volume 2,
+ * Semi numerical Algorithms, 3rd edition, page 232, Boston: Addison-Wesley</cite>. that cites a 1962 paper by B.P. Welford that
+ * can be found by following <a href="http://www.jstor.org/pss/1266577">Note on a Method for Calculating Corrected Sums
+ * of Squares and Products</a>
  * <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
+ * This algorithm is also described in Wikipedia at <a href=
+ * "http://en.wikipedia.org/w/index.php?title=Algorithms_for_calculating_variance&amp;section=4#On-line_algorithm">
+ * Algorithms for calculating variance </a>
  */
 public class SampleStatistic
 {
-    protected final AtomicLong _max = new AtomicLong();
+    protected final LongAccumulator _max = new LongAccumulator(Math::max,0L);
     protected final AtomicLong _total = new AtomicLong();
     protected final AtomicLong _count = new AtomicLong();
-    protected final AtomicLong _totalVariance100 = new AtomicLong();
+    protected final LongAdder _totalVariance100 = new LongAdder();
 
     public void reset()
     {
-        _max.set(0);
+        _max.reset();
         _total.set(0);
         _count.set(0);
-        _totalVariance100.set(0);
+        _totalVariance100.reset();
     }
 
     public void set(final long sample)
@@ -65,10 +64,10 @@
         {
             long mean10 = total*10/count;
             long delta10 = sample*10 - mean10;
-            _totalVariance100.addAndGet(delta10*delta10);
+            _totalVariance100.add(delta10*delta10);
         }
 
-        Atomics.updateMax(_max, sample);
+        _max.accumulate(sample);
     }
 
     /**
@@ -96,7 +95,7 @@
 
     public double getVariance()
     {
-        final long variance100 = _totalVariance100.get();
+        final long variance100 = _totalVariance100.sum();
         final long count = _count.get();
 
         return count>1?((double)variance100)/100.0/(count-1):0.0;
@@ -111,6 +110,6 @@
     @Override
     public String toString()
     {
-        return String.format("%s@%x{c=%d,m=%d,t=%d,v100=%d}",this.getClass().getSimpleName(),hashCode(),_count.get(),_max.get(),_total.get(),_totalVariance100.get());
+        return String.format("%s@%x{c=%d,m=%d,t=%d,v100=%d}",this.getClass().getSimpleName(),hashCode(),_count.get(),_max.get(),_total.get(),_totalVariance100.sum());
     }
 }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutionStrategy.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutionStrategy.java
new file mode 100644
index 0000000..8f62f85
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutionStrategy.java
@@ -0,0 +1,145 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.lang.reflect.Constructor;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume;
+
+/**
+ * <p>An {@link ExecutionStrategy} executes {@link Runnable} tasks produced by a {@link Producer}.
+ * The strategy to execute the task may vary depending on the implementation; the task may be
+ * run in the calling thread, or in a new thread, etc.</p>
+ * <p>The strategy delegates the production of tasks to a {@link Producer}, and continues to
+ * execute tasks until the producer continues to produce them.</p>
+ */
+public interface ExecutionStrategy
+{
+    /**
+     * <p>Initiates (or resumes) the task production and execution.</p>
+     * <p>This method guarantees that the task is never run by the
+     * thread that called this method.</p>
+     *
+     * @see #execute()
+     */
+    public void dispatch();
+
+    /**
+     * <p>Initiates (or resumes) the task production and execution.</p>
+     * <p>The produced task may be run by the same thread that called
+     * this method.</p>
+     *
+     * @see #dispatch()
+     */
+    public void execute();
+
+    /**
+     * A task that can handle {@link RejectedExecutionException}
+     */
+    public interface Rejectable
+    {
+        public void reject();
+    }
+
+    /**
+     * <p>A producer of {@link Runnable} tasks to run.</p>
+     * <p>The {@link ExecutionStrategy} will repeatedly invoke {@link #produce()} until
+     * the producer returns null, indicating that it has nothing more to produce.</p>
+     * <p>When no more tasks can be produced, implementations should arrange for the
+     * {@link ExecutionStrategy} to be invoked again in case an external event resumes
+     * the tasks production.</p>
+     */
+    public interface Producer
+    {
+        /**
+         * <p>Produces a task to be executed.</p>
+         *
+         * @return a task to executed or null if there are no more tasks to execute
+         */
+        Runnable produce();
+    }
+    
+    
+    /**
+     * <p>A factory for {@link ExecutionStrategy}.</p>
+     */
+    public static interface Factory
+    {
+        /**
+         * <p>Creates a new {@link ExecutionStrategy}.</p>
+         *
+         * @param producer the execution strategy producer
+         * @param executor the execution strategy executor
+         * @return a new {@link ExecutionStrategy}
+         */
+        public ExecutionStrategy newExecutionStrategy(Producer producer, Executor executor);
+
+        /**
+         * @return the default {@link ExecutionStrategy}
+         */
+        public static Factory getDefault()
+        {
+            return DefaultExecutionStrategyFactory.INSTANCE;
+        }
+
+        /**
+         * @param producer the execution strategy producer
+         * @param executor the execution strategy executor
+         * @deprecated use {@code getDefault().newExecutionStrategy(Producer, Executor)} instead
+         */
+        @Deprecated
+        public static ExecutionStrategy instanceFor(Producer producer, Executor executor)
+        {
+            return getDefault().newExecutionStrategy(producer, executor);
+        }
+    }
+
+    public static class DefaultExecutionStrategyFactory implements Factory
+    {
+        private static final Logger LOG = Log.getLogger(Factory.class);
+        private static final Factory INSTANCE = new DefaultExecutionStrategyFactory();
+
+        @Override
+        public ExecutionStrategy newExecutionStrategy(Producer producer, Executor executor)
+        {
+            String strategy = System.getProperty(producer.getClass().getName() + ".ExecutionStrategy");
+            if (strategy != null)
+            {
+                try
+                {
+                    Class<? extends ExecutionStrategy> c = Loader.loadClass(producer.getClass(), strategy);
+                    Constructor<? extends ExecutionStrategy> m = c.getConstructor(Producer.class, Executor.class);
+                    LOG.info("Use {} for {}", c.getSimpleName(), producer.getClass().getName());
+                    return m.newInstance(producer, executor);
+                }
+                catch (Exception e)
+                {
+                    LOG.warn(e);
+                }
+            }
+
+            return new ExecuteProduceConsume(producer, executor);
+        }
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java
index 8585ccb..23033ca 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java
@@ -65,7 +65,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Wraps an {@link ThreadPoolExecutor}.
-     * Max pool size is 256, pool thread timeout after 60 seconds, and core pool size is 32 when queueSize >= 0.
+     * Max pool size is 256, pool thread timeout after 60 seconds, and core pool size is 32 when queueSize &gt;= 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.
      */
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/Locker.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/Locker.java
new file mode 100644
index 0000000..26f4599
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/Locker.java
@@ -0,0 +1,69 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Convenience Lock Wrapper.
+ * 
+ * <pre>
+ * try(Locker.Lock lock = locker.lock())
+ * {
+ *   // something 
+ * }
+ * </pre>
+ */
+public class Locker
+{
+    private final ReentrantLock _lock = new ReentrantLock();
+    private final Lock _unlock = new Lock();
+
+    public Locker()
+    {
+    }
+
+    public Lock lock()
+    {
+        if (_lock.isHeldByCurrentThread())
+            throw new IllegalStateException("Locker is not reentrant");
+        _lock.lock();
+        return _unlock;
+    }
+
+    public boolean isLocked()
+    {
+        return _lock.isLocked();
+    }
+
+    public class Lock implements AutoCloseable
+    {
+        @Override
+        public void close()
+        {
+            _lock.unlock();
+        }
+    }
+    
+    public Condition newCondition()
+    {
+        return _lock.newCondition();
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/NonBlockingThread.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/NonBlockingThread.java
deleted file mode 100644
index 5d0d5da..0000000
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/NonBlockingThread.java
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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;
-
-/**
- * Marker that wraps a Runnable, indicating that it is running in a thread that must not be blocked.
- * <p />
- * Client code can use the thread-local {@link #isNonBlockingThread()} to detect whether they are
- * in the context of a non-blocking thread, and perform different actions if that's the case.
- */
-public class NonBlockingThread implements Runnable
-{
-    private final static ThreadLocal<Boolean> __nonBlockingThread = new ThreadLocal<>();
-
-    /**
-     * @return whether the current thread is a thread that must not block.
-     */
-    public static boolean isNonBlockingThread()
-    {
-        return Boolean.TRUE.equals(__nonBlockingThread.get());
-    }
-
-    private final Runnable delegate;
-
-    public NonBlockingThread(Runnable delegate)
-    {
-        this.delegate = delegate;
-    }
-
-    @Override
-    public void run()
-    {
-        try
-        {
-            __nonBlockingThread.set(Boolean.TRUE);
-            delegate.run();
-        }
-        finally
-        {
-            __nonBlockingThread.set(Boolean.FALSE);
-        }
-    }
-}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
old mode 100644
new mode 100755
index e1edabd..349a94b
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
@@ -22,6 +22,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.RejectedExecutionException;
@@ -43,7 +44,7 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
 
-@ManagedObject("A thread pool with no max bound by default")
+@ManagedObject("A thread pool")
 public class QueuedThreadPool extends AbstractLifeCycle implements SizedThreadPool, Dumpable
 {
     private static final Logger LOG = Log.getLogger(QueuedThreadPool.class);
@@ -51,9 +52,10 @@
     private final AtomicInteger _threadsStarted = new AtomicInteger();
     private final AtomicInteger _threadsIdle = new AtomicInteger();
     private final AtomicLong _lastShrink = new AtomicLong();
-    private final ConcurrentHashSet<Thread> _threads=new ConcurrentHashSet<Thread>();
+    private final ConcurrentHashSet<Thread> _threads=new ConcurrentHashSet<>();
     private final Object _joinLock = new Object();
     private final BlockingQueue<Runnable> _jobs;
+    private final ThreadGroup _threadGroup;
     private String _name = "qtp" + hashCode();
     private int _idleTimeout;
     private int _maxThreads;
@@ -61,6 +63,7 @@
     private int _priority = Thread.NORM_PRIORITY;
     private boolean _daemon = false;
     private boolean _detailedDump = false;
+    private int _lowThreadsThreshold = 1;
 
     public QueuedThreadPool()
     {
@@ -84,6 +87,11 @@
 
     public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout, @Name("queue") BlockingQueue<Runnable> queue)
     {
+        this(maxThreads, minThreads, idleTimeout, queue, null);
+    }
+
+    public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout, @Name("queue") BlockingQueue<Runnable> queue, @Name("threadGroup") ThreadGroup threadGroup)
+    {
         setMinThreads(minThreads);
         setMaxThreads(maxThreads);
         setIdleTimeout(idleTimeout);
@@ -95,6 +103,7 @@
             queue=new BlockingArrayQueue<>(capacity, capacity);
         }
         _jobs=queue;
+        _threadGroup=threadGroup;
     }
 
     @Override
@@ -119,13 +128,7 @@
             jobs.clear();
 
         // Fill job Q with noop jobs to wakeup idle
-        Runnable noop = new Runnable()
-        {
-            @Override
-            public void run()
-            {
-            }
-        };
+        Runnable noop = () -> {};
         for (int i = _threadsStarted.get(); i-- > 0; )
             jobs.offer(noop);
 
@@ -159,7 +162,7 @@
         if (size > 0)
         {
             Thread.yield();
-            
+
             if (LOG.isDebugEnabled())
             {
                 for (Thread unstopped : _threads)
@@ -186,7 +189,10 @@
     }
 
     /**
-     * Delegated to the named or anonymous Pool.
+     * Thread Pool should use Daemon Threading.
+     *
+     * @param daemon true to enable delegation
+     * @see Thread#setDaemon(boolean)
      */
     public void setDaemon(boolean daemon)
     {
@@ -276,7 +282,7 @@
     }
 
     /**
-     * Set the maximum number of threads.
+     * Get the maximum number of threads.
      * Delegated to the named or anonymous Pool.
      *
      * @return maximum number of threads.
@@ -322,27 +328,29 @@
     {
         return _priority;
     }
-    
+
     /**
      * Get the size of the job queue.
-     * 
+     *
      * @return Number of jobs queued waiting for a thread
      */
-    @ManagedAttribute("Size of the job queue")
+    @ManagedAttribute("size of the job queue")
     public int getQueueSize()
     {
         return _jobs.size();
     }
 
     /**
-     * Delegated to the named or anonymous Pool.
+     * @return whether this thread pool is using daemon threads
+     * @see Thread#setDaemon(boolean)
      */
-    @ManagedAttribute("thead pool using a daemon thread")
+    @ManagedAttribute("thread pool uses daemon threads")
     public boolean isDaemon()
     {
         return _daemon;
     }
 
+    @ManagedAttribute("reports additional details in the dump")
     public boolean isDetailedDump()
     {
         return _detailedDump;
@@ -352,10 +360,23 @@
     {
         _detailedDump = detailedDump;
     }
-    
+
+    @ManagedAttribute("threshold at which the pool is low on threads")
+    public int getLowThreadsThreshold()
+    {
+        return _lowThreadsThreshold;
+    }
+
+    public void setLowThreadsThreshold(int lowThreadsThreshold)
+    {
+        _lowThreadsThreshold = lowThreadsThreshold;
+    }
+
     @Override
     public void execute(Runnable job)
     {
+        if (LOG.isDebugEnabled())
+            LOG.debug("queue {}",job);
         if (!isRunning() || !_jobs.offer(job))
         {
             LOG.warn("{} rejected {}", this, job);
@@ -386,42 +407,49 @@
     }
 
     /**
-     * @return The total number of threads currently in the pool
+     * @return the total number of threads currently in the pool
      */
     @Override
-    @ManagedAttribute("total number of threads currently in the pool")
+    @ManagedAttribute("number of threads in the pool")
     public int getThreads()
     {
         return _threadsStarted.get();
     }
 
     /**
-     * @return The number of idle threads in the pool
+     * @return the number of idle threads in the pool
      */
     @Override
-    @ManagedAttribute("total number of idle threads in the pool")
+    @ManagedAttribute("number of idle threads in the pool")
     public int getIdleThreads()
     {
         return _threadsIdle.get();
     }
 
     /**
-     * @return The number of busy threads in the pool
+     * @return the number of busy threads in the pool
      */
-    @ManagedAttribute("total number of busy threads in the pool")
+    @ManagedAttribute("number of busy threads in the pool")
     public int getBusyThreads()
     {
         return getThreads() - getIdleThreads();
     }
-    
+
     /**
-     * @return True if the pool is at maxThreads and there are not more idle threads than queued jobs
+     * <p>Returns whether this thread pool is low on threads.</p>
+     * <p>The current formula is:</p>
+     * <pre>
+     * maxThreads - threads + idleThreads - queueSize &lt;= lowThreadsThreshold
+     * </pre>
+     *
+     * @return whether the pool is low on threads
+     * @see #getLowThreadsThreshold()
      */
     @Override
-    @ManagedAttribute("True if the pools is at maxThreads and there are not idle threads than queued jobs")
+    @ManagedAttribute(value = "thread pool is low on threads", readonly = true)
     public boolean isLowOnThreads()
     {
-        return _threadsStarted.get() == _maxThreads && _jobs.size() >= _threadsIdle.get();
+        return getMaxThreads() - getThreads() + getIdleThreads() - getQueueSize() <= getLowThreadsThreshold();
     }
 
     private boolean startThreads(int threadsToStart)
@@ -459,11 +487,11 @@
 
     protected Thread newThread(Runnable runnable)
     {
-        return new Thread(runnable);
+        return new Thread(_threadGroup, runnable);
     }
 
     @Override
-    @ManagedOperation("dump thread state")
+    @ManagedOperation("dumps thread pool state")
     public String dump()
     {
         return ContainerLifeCycle.dump(this);
@@ -472,7 +500,7 @@
     @Override
     public void dump(Appendable out, String indent) throws IOException
     {
-        List<Object> dump = new ArrayList<>(getMaxThreads());
+        List<Object> threads = new ArrayList<>(getMaxThreads());
         for (final Thread thread : _threads)
         {
             final StackTraceElement[] trace = thread.getStackTrace();
@@ -489,7 +517,7 @@
 
             if (isDetailedDump())
             {
-                dump.add(new Dumpable()
+                threads.add(new Dumpable()
                 {
                     @Override
                     public void dump(Appendable out, String indent) throws IOException
@@ -512,12 +540,16 @@
             else
             {
                 int p=thread.getPriority();
-                dump.add(thread.getId() + " " + thread.getName() + " " + thread.getState() + " @ " + (trace.length > 0 ? trace[0] : "???") + (idle ? " IDLE" : "")+ (p==Thread.NORM_PRIORITY?"":(" prio="+p)));
+                threads.add(thread.getId() + " " + thread.getName() + " " + thread.getState() + " @ " + (trace.length > 0 ? trace[0] : "???") + (idle ? " IDLE" : "")+ (p==Thread.NORM_PRIORITY?"":(" prio="+p)));
             }
         }
 
+        List<Runnable> jobs = Collections.emptyList();
+        if (isDetailedDump())
+            jobs = new ArrayList<>(getQueue());
+
         ContainerLifeCycle.dumpObject(out, this);
-        ContainerLifeCycle.dump(out, indent, dump);
+        ContainerLifeCycle.dump(out, indent, threads, jobs);
     }
 
     @Override
@@ -552,7 +584,11 @@
                     // Job loop
                     while (job != null && isRunning())
                     {
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("run {}",job);
                         runJob(job);
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("ran {}",job);
                         if (Thread.interrupted())
                         {
                             ignore=true;
@@ -645,17 +681,19 @@
 
     /**
      * @param queue the job queue
+     * @deprecated pass the queue to the constructor instead
      */
+    @Deprecated
     public void setQueue(BlockingQueue<Runnable> queue)
     {
         throw new UnsupportedOperationException("Use constructor injection");
     }
 
     /**
-     * @param id The thread ID to interrupt.
+     * @param id the thread ID to interrupt.
      * @return true if the thread was found and interrupted.
      */
-    @ManagedOperation("interrupt a pool thread")
+    @ManagedOperation("interrupts a pool thread")
     public boolean interruptThread(@Name("id") long id)
     {
         for (Thread thread : _threads)
@@ -670,10 +708,10 @@
     }
 
     /**
-     * @param id The thread ID to interrupt.
-     * @return true if the thread was found and interrupted.
+     * @param id the thread ID to interrupt.
+     * @return the stack frames dump
      */
-    @ManagedOperation("dump a pool thread stack")
+    @ManagedOperation("dumps a pool thread stack")
     public String dumpThread(@Name("id") long id)
     {
         for (Thread thread : _threads)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java
old mode 100644
new mode 100755
index 86e7768..9402f95
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java
@@ -32,7 +32,7 @@
 
 /**
  * Implementation of {@link Scheduler} based on JDK's {@link ScheduledThreadPoolExecutor}.
- * <p />
+ * <p>
  * While use of {@link ScheduledThreadPoolExecutor} creates futures that will not be used,
  * it has the advantage of allowing to set a property to remove cancelled tasks from its
  * queue even if the task did not fire, which provides a huge benefit in the performance
@@ -43,6 +43,7 @@
     private final String name;
     private final boolean daemon;
     private final ClassLoader classloader;
+    private final ThreadGroup threadGroup;
     private volatile ScheduledThreadPoolExecutor scheduler;
     private volatile Thread thread;
 
@@ -58,9 +59,15 @@
     
     public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader threadFactoryClassLoader)
     {
+        this(name, daemon, threadFactoryClassLoader, null);
+    }
+
+    public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader threadFactoryClassLoader, ThreadGroup threadGroup)
+    {
         this.name = name == null ? "Scheduler-" + hashCode() : name;
         this.daemon = daemon;
-        this.classloader = threadFactoryClassLoader;
+        this.classloader = threadFactoryClassLoader == null ? Thread.currentThread().getContextClassLoader() : threadFactoryClassLoader;
+        this.threadGroup = threadGroup;
     }
 
     @Override
@@ -71,7 +78,7 @@
             @Override
             public Thread newThread(Runnable r)
             {
-                Thread thread = ScheduledExecutorScheduler.this.thread = new Thread(r, name);
+                Thread thread = ScheduledExecutorScheduler.this.thread = new Thread(threadGroup, r, name);
                 thread.setDaemon(daemon);
                 thread.setContextClassLoader(classloader);
                 return thread;
@@ -123,11 +130,11 @@
         }
     }
 
-    private class ScheduledFutureTask implements Task
+    private static class ScheduledFutureTask implements Task
     {
         private final ScheduledFuture<?> scheduledFuture;
 
-        public ScheduledFutureTask(ScheduledFuture<?> scheduledFuture)
+        ScheduledFutureTask(ScheduledFuture<?> scheduledFuture)
         {
             this.scheduledFuture = scheduledFuture;
         }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/SpinLock.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/SpinLock.java
deleted file mode 100644
index 293afaf..0000000
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/SpinLock.java
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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.atomic.AtomicReference;
-
-/**
- * <p>This spin lock is a lock designed to protect VERY short sections
- * of critical code.  Threads attempting to take the lock will spin
- * forever until the lock is available, thus it is important that
- * the code protected by this lock is extremely simple and non
- * blocking. The reason for this lock is that it prevents a thread
- * from giving up a CPU core when contending for the lock.</p>
- * <pre>
- * try(SpinLock.Lock lock = spinlock.lock())
- * {
- *   // something very quick and non blocking
- * }
- * </pre>
- * <p>Further analysis however, shows that spin locks behave really
- * bad under heavy contention and where the number of threads
- * exceeds the number of cores, which are common scenarios for a
- * server, so this class was removed from usage, preferring
- * standard locks instead.</p>
- * @deprecated Do not use it anymore, prefer normal locks
- */
-@Deprecated
-public class SpinLock
-{
-    private final AtomicReference<Thread> _lock = new AtomicReference<>(null);
-    private final Lock _unlock = new Lock();
-
-    public Lock lock()
-    {
-        Thread thread = Thread.currentThread();
-        while(true)
-        {
-            if (!_lock.compareAndSet(null,thread))
-            {
-                if (_lock.get()==thread)
-                    throw new IllegalStateException("SpinLock is not reentrant");
-                continue;
-            }
-            return _unlock;
-        }
-    }
-
-    public boolean isLocked()
-    {
-        return _lock.get()!=null;
-    }
-
-    public boolean isLockedThread()
-    {
-        return _lock.get()==Thread.currentThread();
-    }
-
-    public class Lock implements AutoCloseable
-    {
-        @Override
-        public void close()
-        {
-            _lock.set(null);
-        }
-    }
-}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ThreadClassLoaderScope.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ThreadClassLoaderScope.java
new file mode 100644
index 0000000..827bbea
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ThreadClassLoaderScope.java
@@ -0,0 +1,45 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.Closeable;
+
+public class ThreadClassLoaderScope implements Closeable
+{
+    private final ClassLoader old;
+    private final ClassLoader scopedClassLoader;
+
+    public ThreadClassLoaderScope(ClassLoader cl)
+    {
+        old = Thread.currentThread().getContextClassLoader();
+        scopedClassLoader = cl;
+        Thread.currentThread().setContextClassLoader(scopedClassLoader);
+    }
+
+    @Override
+    public void close()
+    {
+        Thread.currentThread().setContextClassLoader(old);
+    }
+
+    public ClassLoader getScopedClassLoader()
+    {
+        return scopedClassLoader;
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ThreadPool.java
index 050836b..5605579 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ThreadPool.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ThreadPool.java
@@ -22,6 +22,7 @@
 
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.component.LifeCycle;
 
 /* ------------------------------------------------------------ */
 /** ThreadPool.
@@ -36,6 +37,7 @@
     /* ------------------------------------------------------------ */
     /**
      * Blocks until the thread pool is {@link LifeCycle#stop stopped}.
+     * @throws InterruptedException if thread was interrupted
      */
     public void join() throws InterruptedException;
 
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/TimerScheduler.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/TimerScheduler.java
index d6e5f5e..958df5b 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/TimerScheduler.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/TimerScheduler.java
@@ -113,7 +113,7 @@
             }
             catch (Throwable x)
             {
-                LOG.debug("Exception while executing task " + _task, x);
+                LOG.warn("Exception while executing task " + _task, x);
             }
         }
 
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ExecuteProduceConsume.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ExecuteProduceConsume.java
new file mode 100644
index 0000000..be87daf
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ExecuteProduceConsume.java
@@ -0,0 +1,361 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.strategy;
+
+import java.io.Closeable;
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
+import org.eclipse.jetty.util.thread.Locker;
+import org.eclipse.jetty.util.thread.Locker.Lock;
+import org.eclipse.jetty.util.thread.ThreadPool;
+
+/**
+ * <p>A strategy where the thread that produces will always run the resulting task.</p>
+ * <p>The strategy may then dispatch another thread to continue production.</p>
+ * <p>The strategy is also known by the nickname 'eat what you kill', which comes from
+ * the hunting ethic that says a person should not kill anything he or she does not
+ * plan on eating. In this case, the phrase is used to mean that a thread should
+ * not produce a task that it does not intend to run. By making producers run the
+ * task that they have just produced avoids execution delays and avoids parallel slow
+ * down by running the task in the same core, with good chances of having a hot CPU
+ * cache. It also avoids the creation of a queue of produced tasks that the system
+ * does not yet have capacity to consume, which can save memory and exert back
+ * pressure on producers.</p>
+ */
+public class ExecuteProduceConsume extends ExecutingExecutionStrategy implements ExecutionStrategy, Runnable
+{
+    private static final Logger LOG = Log.getLogger(ExecuteProduceConsume.class);
+
+    private final Locker _locker = new Locker();
+    private final Runnable _runExecute = new RunExecute();
+    private final Producer _producer;
+    private final ThreadPool _threadPool;
+    private boolean _idle = true;
+    private boolean _execute;
+    private boolean _producing;
+    private boolean _pending;
+    private boolean _lowThreads;
+
+    public ExecuteProduceConsume(Producer producer, Executor executor)
+    {
+        super(executor);
+        this._producer = producer;
+        _threadPool = executor instanceof ThreadPool ? (ThreadPool)executor : null;
+    }
+
+    @Deprecated
+    public ExecuteProduceConsume(Producer producer, Executor executor, ExecutionStrategy lowResourceStrategy)
+    {
+        this(producer, executor);
+    }
+
+    @Override
+    public void execute()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} execute", this);
+
+        boolean produce = false;
+        try (Lock locked = _locker.lock())
+        {
+            // If we are idle and a thread is not producing
+            if (_idle)
+            {
+                if (_producing)
+                    throw new IllegalStateException();
+
+                // Then this thread will do the producing
+                produce = _producing = true;
+                // and we are no longer idle
+                _idle = false;
+            }
+            else
+            {
+                // Otherwise, lets tell the producing thread
+                // that it should call produce again before going idle
+                _execute = true;
+            }
+        }
+
+        if (produce)
+            produceConsume();
+    }
+
+    @Override
+    public void dispatch()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} spawning", this);
+        boolean dispatch = false;
+        try (Lock locked = _locker.lock())
+        {
+            if (_idle)
+                dispatch = true;
+            else
+                _execute = true;
+        }
+        if (dispatch)
+            execute(_runExecute);
+    }
+
+    @Override
+    public void run()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} run", this);
+        boolean produce = false;
+        try (Lock locked = _locker.lock())
+        {
+            _pending = false;
+            if (!_idle && !_producing)
+            {
+                produce = _producing = true;
+            }
+        }
+
+        if (produce)
+            produceConsume();
+    }
+
+    private void produceConsume()
+    {
+        if (_threadPool != null && _threadPool.isLowOnThreads())
+        {
+            // If we are low on threads we must not produce and consume
+            // in the same thread, but produce and execute to consume.
+            if (!produceExecuteConsume())
+                return;
+        }
+        executeProduceConsume();
+    }
+
+    public boolean isLowOnThreads()
+    {
+        return _lowThreads;
+    }
+
+    /**
+     * @return true if we are still producing
+     */
+    private boolean produceExecuteConsume()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} enter low threads mode", this);
+        _lowThreads = true;
+        try
+        {
+            boolean idle = false;
+            while (_threadPool.isLowOnThreads())
+            {
+                Runnable task = _producer.produce();
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} produced {}", _producer, task);
+
+                if (task == null)
+                {
+                    // No task, so we are now idle
+                    try (Lock locked = _locker.lock())
+                    {
+                        if (_execute)
+                        {
+                            _execute = false;
+                            _producing = true;
+                            _idle = false;
+                            continue;
+                        }
+
+                        _producing = false;
+                        idle = _idle = true;
+                        break;
+                    }
+                }
+
+                // Execute the task.
+                executeProduct(task);
+            }
+            return !idle;
+        }
+        finally
+        {
+            _lowThreads = false;
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} exit low threads mode", this);
+        }
+    }
+
+    /**
+     * <p>Only called when in {@link #isLowOnThreads() low threads mode}
+     * to execute the task produced by the producer.</p>
+     * <p>Because </p>
+     * <p>If the task implements {@link Rejectable}, then {@link Rejectable#reject()}
+     * is immediately called on the task object. If the task also implements
+     * {@link Closeable}, then {@link Closeable#close()} is called on the task object.</p>
+     * <p>If the task does not implement {@link Rejectable}, then it is
+     * {@link #execute(Runnable) executed}.</p>
+     *
+     * @param task the produced task to execute
+     */
+    protected void executeProduct(Runnable task)
+    {
+        if (task instanceof Rejectable)
+        {
+            try
+            {
+                ((Rejectable)task).reject();
+                if (task instanceof Closeable)
+                    ((Closeable)task).close();
+            }
+            catch (Throwable x)
+            {
+                LOG.debug(x);
+            }
+        }
+        else
+        {
+            execute(task);
+        }
+    }
+
+    private void executeProduceConsume()
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} produce enter", this);
+
+        while (true)
+        {
+            // If we got here, then we are the thread that is producing.
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} producing", this);
+
+            Runnable task = _producer.produce();
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} produced {}", this, task);
+
+            boolean dispatch = false;
+            try (Lock locked = _locker.lock())
+            {
+                // Finished producing
+                _producing = false;
+
+                // Did we produced a task?
+                if (task == null)
+                {
+                    // There is no task.
+                    // Could another one just have been queued with an execute?
+                    if (_execute)
+                    {
+                        _idle = false;
+                        _producing = true;
+                        _execute = false;
+                        continue;
+                    }
+
+                    // ... and no additional calls to execute, so we are idle
+                    _idle = true;
+                    break;
+                }
+
+                // We have a task, which we will run ourselves,
+                // so if we don't have another thread pending
+                if (!_pending)
+                {
+                    // dispatch one
+                    dispatch = _pending = true;
+                }
+
+                _execute = false;
+            }
+
+            // If we became pending
+            if (dispatch)
+            {
+                // Spawn a new thread to continue production by running the produce loop.
+                if (LOG.isDebugEnabled())
+                    LOG.debug("{} dispatch", this);
+                if (!execute(this))
+                    task = null;
+            }
+
+            // Run the task.
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} run {}", this, task);
+            if (task != null)
+                task.run();
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} ran {}", this, task);
+
+            // Once we have run the task, we can try producing again.
+            try (Lock locked = _locker.lock())
+            {
+                // Is another thread already producing or we are now idle?
+                if (_producing || _idle)
+                    break;
+                _producing = true;
+            }
+        }
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} produce exit", this);
+    }
+
+    public Boolean isIdle()
+    {
+        try (Lock locked = _locker.lock())
+        {
+            return _idle;
+        }
+    }
+
+    public String toString()
+    {
+        StringBuilder builder = new StringBuilder();
+        builder.append("EPC ");
+        try (Lock locked = _locker.lock())
+        {
+            builder.append(_idle ? "Idle/" : "");
+            builder.append(_producing ? "Prod/" : "");
+            builder.append(_pending ? "Pend/" : "");
+            builder.append(_execute ? "Exec/" : "");
+        }
+        builder.append(_producer);
+        return builder.toString();
+    }
+
+    private class RunExecute implements Runnable
+    {
+        @Override
+        public void run()
+        {
+            execute();
+        }
+    }
+
+    public static class Factory implements ExecutionStrategy.Factory
+    {
+        @Override
+        public ExecutionStrategy newExecutionStrategy(Producer producer, Executor executor)
+        {
+            return new ExecuteProduceConsume(producer, executor);
+        }
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ExecutingExecutionStrategy.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ExecutingExecutionStrategy.java
new file mode 100644
index 0000000..6396b74
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ExecutingExecutionStrategy.java
@@ -0,0 +1,71 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.strategy;
+
+import java.io.Closeable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
+
+/**
+ * <p>Base class for strategies that need to execute a task by submitting it to an {@link Executor}.</p>
+ * <p>If the submission to the {@code Executor} is rejected (via a {@link RejectedExecutionException}),
+ * the task is tested whether it implements {@link Closeable}; if it does, then {@link Closeable#close()}
+ * is called on the task object.</p>
+ */
+public abstract class ExecutingExecutionStrategy implements ExecutionStrategy
+{
+    private static final Logger LOG = Log.getLogger(ExecutingExecutionStrategy.class);
+
+    private final Executor _executor;
+
+    protected ExecutingExecutionStrategy(Executor executor)
+    {
+        _executor=executor;
+    }
+
+    protected boolean execute(Runnable task)
+    {
+        try
+        {
+            _executor.execute(task);
+            return true;
+        }
+        catch(RejectedExecutionException e)
+        {
+            // If we cannot execute, then close the task and keep producing.
+            LOG.debug(e);
+            LOG.warn("Rejected execution of {}",task);
+            try
+            {
+                if (task instanceof Closeable)
+                    ((Closeable)task).close();
+            }
+            catch (Exception x)
+            {
+                e.addSuppressed(x);
+                LOG.warn(e);
+            }
+        }
+        return false;
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceConsume.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceConsume.java
new file mode 100644
index 0000000..420f87b
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceConsume.java
@@ -0,0 +1,121 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.strategy;
+
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
+import org.eclipse.jetty.util.thread.Locker;
+
+/**
+ * <p>A strategy where the caller thread iterates over task production, submitting each
+ * task to an {@link Executor} for execution.</p>
+ */
+public class ProduceConsume implements ExecutionStrategy, Runnable
+{
+    private static final Logger LOG = Log.getLogger(ExecuteProduceConsume.class);
+
+    private final Locker _locker = new Locker();
+    private final Producer _producer;
+    private final Executor _executor;
+    private State _state = State.IDLE;
+
+    public ProduceConsume(Producer producer, Executor executor)
+    {
+        this._producer = producer;
+        this._executor = executor;
+    }
+
+    @Override
+    public void execute()
+    {
+        try (Locker.Lock lock = _locker.lock())
+        {
+            switch(_state)
+            {
+                case IDLE:
+                    _state= State.PRODUCE;
+                    break;
+
+                case PRODUCE:
+                case EXECUTE:
+                    _state= State.EXECUTE;
+                    return;
+            }
+        }
+
+        // Iterate until we are complete.
+        while (true)
+        {
+            // Produce a task.
+            Runnable task = _producer.produce();
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} produced {}", _producer, task);
+
+            if (task == null)
+            {
+                try (Locker.Lock lock = _locker.lock())
+                {
+                    switch(_state)
+                    {
+                        case IDLE:
+                            throw new IllegalStateException();
+                        case PRODUCE:
+                            _state= State.IDLE;
+                            return;
+                        case EXECUTE:
+                            _state= State.PRODUCE;
+                            continue;
+                    }
+                }
+            }
+
+            // Run the task.
+            task.run();
+        }
+    }
+
+    @Override
+    public void dispatch()
+    {
+        _executor.execute(this);
+    }
+
+    @Override
+    public void run()
+    {
+        execute();
+    }
+
+    public static class Factory implements ExecutionStrategy.Factory
+    {
+        @Override
+        public ExecutionStrategy newExecutionStrategy(Producer producer, Executor executor)
+        {
+            return new ProduceConsume(producer, executor);
+        }
+    }
+
+    private enum State
+    {
+        IDLE, PRODUCE, EXECUTE
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceExecuteConsume.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceExecuteConsume.java
new file mode 100644
index 0000000..8c0b9f1
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceExecuteConsume.java
@@ -0,0 +1,115 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.strategy;
+
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.ExecutionStrategy;
+import org.eclipse.jetty.util.thread.Locker;
+import org.eclipse.jetty.util.thread.Locker.Lock;
+
+/**
+ * <p>A strategy where the caller thread iterates over task production, submitting each
+ * task to an {@link Executor} for execution.</p>
+ */
+public class ProduceExecuteConsume extends ExecutingExecutionStrategy implements ExecutionStrategy
+{
+    private static final Logger LOG = Log.getLogger(ProduceExecuteConsume.class);
+
+    private final Locker _locker = new Locker();
+    private final Producer _producer;
+    private State _state = State.IDLE;
+
+    public ProduceExecuteConsume(Producer producer, Executor executor)
+    {
+        super(executor);
+        this._producer = producer;
+    }
+
+    @Override
+    public void execute()
+    {
+        try (Lock locked = _locker.lock())
+        {
+            switch(_state)
+            {
+                case IDLE:
+                    _state=State.PRODUCE;
+                    break;
+
+                case PRODUCE:
+                case EXECUTE:
+                    _state=State.EXECUTE;
+                    return;
+            }
+        }
+
+        // Produce until we no task is found.
+        while (true)
+        {
+            // Produce a task.
+            Runnable task = _producer.produce();
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} produced {}", _producer, task);
+
+            if (task == null)
+            {
+                try (Lock locked = _locker.lock())
+                {
+                    switch(_state)
+                    {
+                        case IDLE:
+                            throw new IllegalStateException();
+                        case PRODUCE:
+                            _state=State.IDLE;
+                            return;
+                        case EXECUTE:
+                            _state=State.PRODUCE;
+                            continue;
+                    }
+                }
+            }
+
+            // Execute the task.
+            execute(task);
+        }        
+    }
+
+    @Override
+    public void dispatch()
+    {
+        execute();
+    }
+
+    public static class Factory implements ExecutionStrategy.Factory
+    {
+        @Override
+        public ExecutionStrategy newExecutionStrategy(Producer producer, Executor executor)
+        {
+            return new ProduceExecuteConsume(producer, executor);
+        }
+    }
+
+    private enum State
+    {
+        IDLE, PRODUCE, EXECUTE
+    }
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java
index 0ff7501..7c7a038 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java
@@ -19,15 +19,13 @@
 package org.eclipse.jetty.util;
 
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
 import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.BufferOverflowException;
 import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.concurrent.ThreadLocalRandom;
 
@@ -37,6 +35,12 @@
 import org.junit.Ignore;
 import org.junit.Test;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
 public class BufferUtilTest
 {
     @Test
@@ -152,7 +156,7 @@
         assertEquals(2,from.remaining());
         assertEquals("1234567890",BufferUtil.toString(to));
     }
-    
+
 
 
     @Test
@@ -165,7 +169,7 @@
         assertEquals("123",BufferUtil.toString(to));
         BufferUtil.append(to,from.array(),3,2);
         assertEquals("12345",BufferUtil.toString(to));
-        
+
         try
         {
             BufferUtil.append(to,from.array(),0,5);
@@ -174,7 +178,7 @@
         catch(BufferOverflowException e)
         {}
     }
-    
+
 
     @Test
     public void testPutDirect() throws Exception
@@ -289,7 +293,7 @@
         int capacity = BufferUtil.TEMP_BUFFER_SIZE*2+1024;
         testWriteToWithBufferThatDoesNotExposeArray(capacity);
     }
-    
+
 
     @Test
     public void testEnsureCapacity() throws Exception
@@ -298,13 +302,13 @@
         assertTrue(b==BufferUtil.ensureCapacity(b, 0));
         assertTrue(b==BufferUtil.ensureCapacity(b, 10));
         assertTrue(b==BufferUtil.ensureCapacity(b, b.capacity()));
-        
+
 
         ByteBuffer b1 = BufferUtil.ensureCapacity(b, 64);
         assertTrue(b!=b1);
         assertEquals(64, b1.capacity());
         assertEquals("Goodbye Cruel World", BufferUtil.toString(b1));
-        
+
         b1.position(8);
         b1.limit(13);
         assertEquals("Cruel", BufferUtil.toString(b1));
@@ -321,9 +325,9 @@
         assertEquals(64, b3.capacity());
         assertEquals("Cruel", BufferUtil.toString(b3));
         assertEquals(0, b3.arrayOffset());
-        
+
     }
-    
+
 
     private void testWriteToWithBufferThatDoesNotExposeArray(int capacity) throws IOException
     {
@@ -335,4 +339,39 @@
         BufferUtil.writeTo(buffer.asReadOnlyBuffer(), out);
         assertThat("Bytes in out equal bytes in buffer", Arrays.equals(bytes, out.toByteArray()), is(true));
     }
+
+    @Test
+    public void testMappedFile() throws Exception
+    {
+        String data="Now is the time for all good men to come to the aid of the party";
+        File file = File.createTempFile("test",".txt");
+        file.deleteOnExit();
+        try(FileWriter out = new FileWriter(file);)
+        {
+            out.write(data);
+        }
+
+        ByteBuffer mapped = BufferUtil.toMappedBuffer(file);
+        assertEquals(data,BufferUtil.toString(mapped));
+        assertTrue(BufferUtil.isMappedBuffer(mapped));
+
+        ByteBuffer direct = BufferUtil.allocateDirect(data.length());
+        BufferUtil.clearToFill(direct);
+        direct.put(data.getBytes(StandardCharsets.ISO_8859_1));
+        BufferUtil.flipToFlush(direct, 0);
+        assertEquals(data,BufferUtil.toString(direct));
+        assertFalse(BufferUtil.isMappedBuffer(direct));
+
+        ByteBuffer slice = direct.slice();
+        assertEquals(data,BufferUtil.toString(slice));
+        assertFalse(BufferUtil.isMappedBuffer(slice));
+
+        ByteBuffer duplicate = direct.duplicate();
+        assertEquals(data,BufferUtil.toString(duplicate));
+        assertFalse(BufferUtil.isMappedBuffer(duplicate));
+
+        ByteBuffer readonly = direct.asReadOnlyBuffer();
+        assertEquals(data,BufferUtil.toString(readonly));
+        assertFalse(BufferUtil.isMappedBuffer(readonly));
+    }
 }
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/HostPortTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/HostPortTest.java
index 0f49ead..63a3a2d 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/HostPortTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/HostPortTest.java
@@ -37,6 +37,8 @@
     public static List<String[]> testCases()
     {
         String data[][] = new String[][] { 
+            {"","",null},
+            {":80","","80"},
             {"host","host",null},
             {"host:80","host","80"},
             {"10.10.10.1","10.10.10.1",null},
@@ -46,8 +48,6 @@
 
             {null,null,null},
             {"host:",null,null},
-            {"",null,null},
-            {":80",null,"80"},
             {"127.0.0.1:",null,null},
             {"[0::0::0::0::1]:",null,null},
             {"host:xxx",null,null},
@@ -76,16 +76,18 @@
         try
         {
             HostPort hostPort = new HostPort(_authority);
-            assertThat(hostPort.getHost(),is(_expectedHost));
+            assertThat(_authority,hostPort.getHost(),is(_expectedHost));
             
             if (_expectedPort==null)
-                assertThat(hostPort.getPort(),is(0));
+                assertThat(_authority,hostPort.getPort(),is(0));
             else
-                assertThat(hostPort.getPort(),is(Integer.valueOf(_expectedPort)));
+                assertThat(_authority,hostPort.getPort(),is(Integer.valueOf(_expectedPort)));
         }
         catch (Exception e)
         {
-            assertNull(_expectedHost);
+            if (_expectedHost!=null)
+                e.printStackTrace();
+            assertNull(_authority,_expectedHost);
         }
     }
 
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/IteratingCallbackTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/IteratingCallbackTest.java
index eb7d414..e936bc4 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/IteratingCallbackTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/IteratingCallbackTest.java
@@ -24,40 +24,40 @@
 
 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
 import org.eclipse.jetty.util.thread.Scheduler;
-import org.junit.AfterClass;
+import org.junit.After;
 import org.junit.Assert;
-import org.junit.BeforeClass;
+import org.junit.Before;
 import org.junit.Test;
 
 public class IteratingCallbackTest
 {
-    static Scheduler scheduler = new ScheduledExecutorScheduler();
-   
-    @BeforeClass
-    public static void beforeClass() throws Exception
+    private Scheduler scheduler;
+
+    @Before
+    public void prepare() throws Exception
     {
+        scheduler = new ScheduledExecutorScheduler();
         scheduler.start();
     }
-    
-    @AfterClass
-    public static void afterClass() throws Exception
+
+    @After
+    public void dispose() throws Exception
     {
         scheduler.stop();
     }
-    
+
     @Test
     public void testNonWaitingProcess() throws Exception
     {
-        
-        TestCB cb=new TestCB()
+        TestCB cb = new TestCB()
         {
-            int i=10;
-            
+            int i = 10;
+
             @Override
             protected Action process() throws Exception
             {
                 processed++;
-                if (i-->1)      
+                if (i-- > 1)
                 {
                     succeeded(); // fake a completed IO operation
                     return Action.SCHEDULED;
@@ -65,61 +65,59 @@
                 return Action.SUCCEEDED;
             }
         };
-        
+
         cb.iterate();
-        Assert.assertTrue(cb.waitForComplete()); 
-        Assert.assertEquals(10,cb.processed);
+        Assert.assertTrue(cb.waitForComplete());
+        Assert.assertEquals(10, cb.processed);
     }
 
-
-
     @Test
     public void testWaitingProcess() throws Exception
     {
-        TestCB cb=new TestCB()
+        TestCB cb = new TestCB()
         {
-            int i=4;
-            
+            int i = 4;
+
             @Override
             protected Action process() throws Exception
             {
                 processed++;
-                if (i-->1)      
+                if (i-- > 1)
                 {
-                    scheduler.schedule(successTask,50,TimeUnit.MILLISECONDS);
+                    scheduler.schedule(successTask, 50, TimeUnit.MILLISECONDS);
                     return Action.SCHEDULED;
                 }
                 return Action.SUCCEEDED;
             }
         };
-        
+
         cb.iterate();
-        
+
         Assert.assertTrue(cb.waitForComplete());
-         
-        Assert.assertEquals(4,cb.processed);
+
+        Assert.assertEquals(4, cb.processed);
     }
 
     @Test
-    public void testWaitingProcessSpuriousInterate() throws Exception
+    public void testWaitingProcessSpuriousIterate() throws Exception
     {
-        final TestCB cb=new TestCB()
+        final TestCB cb = new TestCB()
         {
-            int i=4;
-            
+            int i = 4;
+
             @Override
             protected Action process() throws Exception
             {
                 processed++;
-                if (i-->1)      
+                if (i-- > 1)
                 {
-                    scheduler.schedule(successTask,50,TimeUnit.MILLISECONDS);
+                    scheduler.schedule(successTask, 50, TimeUnit.MILLISECONDS);
                     return Action.SCHEDULED;
                 }
                 return Action.SUCCEEDED;
             }
         };
-        
+
         cb.iterate();
         scheduler.schedule(new Runnable()
         {
@@ -128,29 +126,29 @@
             {
                 cb.iterate();
                 if (!cb.isSucceeded())
-                    scheduler.schedule(this,50,TimeUnit.MILLISECONDS);
+                    scheduler.schedule(this, 50, TimeUnit.MILLISECONDS);
             }
-        },49,TimeUnit.MILLISECONDS);
-        
+        }, 49, TimeUnit.MILLISECONDS);
+
         Assert.assertTrue(cb.waitForComplete());
-         
-        Assert.assertEquals(4,cb.processed);
+
+        Assert.assertEquals(4, cb.processed);
     }
 
     @Test
     public void testNonWaitingProcessFailure() throws Exception
     {
-        TestCB cb=new TestCB()
+        TestCB cb = new TestCB()
         {
-            int i=10;
-            
+            int i = 10;
+
             @Override
             protected Action process() throws Exception
             {
                 processed++;
-                if (i-->1)      
+                if (i-- > 1)
                 {
-                    if (i>5)
+                    if (i > 5)
                         succeeded(); // fake a completed IO operation
                     else
                         failed(new Exception("testing"));
@@ -159,63 +157,63 @@
                 return Action.SUCCEEDED;
             }
         };
-        
+
         cb.iterate();
-        Assert.assertFalse(cb.waitForComplete()); 
-        Assert.assertEquals(5,cb.processed);
+        Assert.assertFalse(cb.waitForComplete());
+        Assert.assertEquals(5, cb.processed);
     }
 
     @Test
     public void testWaitingProcessFailure() throws Exception
     {
-        TestCB cb=new TestCB()
+        TestCB cb = new TestCB()
         {
-            int i=4;
-            
+            int i = 4;
+
             @Override
             protected Action process() throws Exception
             {
                 processed++;
-                if (i-->1)      
+                if (i-- > 1)
                 {
-                    scheduler.schedule(i>2?successTask:failTask,50,TimeUnit.MILLISECONDS);
+                    scheduler.schedule(i > 2 ? successTask : failTask, 50, TimeUnit.MILLISECONDS);
                     return Action.SCHEDULED;
                 }
                 return Action.SUCCEEDED;
             }
         };
-        
+
         cb.iterate();
-        
+
         Assert.assertFalse(cb.waitForComplete());
-        Assert.assertEquals(2,cb.processed);
+        Assert.assertEquals(2, cb.processed);
     }
-    
+
 
     @Test
     public void testIdleWaiting() throws Exception
     {
         final CountDownLatch idle = new CountDownLatch(1);
-        
-        TestCB cb=new TestCB()
+
+        TestCB cb = new TestCB()
         {
-            int i=5;
-            
+            int i = 5;
+
             @Override
             protected Action process()
             {
                 processed++;
-                
-                switch(i--)
+
+                switch (i--)
                 {
                     case 5:
                         succeeded();
                         return Action.SCHEDULED;
-                        
+
                     case 4:
-                        scheduler.schedule(successTask,5,TimeUnit.MILLISECONDS);
+                        scheduler.schedule(successTask, 5, TimeUnit.MILLISECONDS);
                         return Action.SCHEDULED;
-                        
+
                     case 3:
                         scheduler.schedule(new Runnable()
                         {
@@ -224,49 +222,99 @@
                             {
                                 idle.countDown();
                             }
-                        },5,TimeUnit.MILLISECONDS);
+                        }, 5, TimeUnit.MILLISECONDS);
                         return Action.IDLE;
 
                     case 2:
                         succeeded();
                         return Action.SCHEDULED;
-                        
+
                     case 1:
-                        scheduler.schedule(successTask,5,TimeUnit.MILLISECONDS);
+                        scheduler.schedule(successTask, 5, TimeUnit.MILLISECONDS);
                         return Action.SCHEDULED;
-                        
+
                     case 0:
                         return Action.SUCCEEDED;
-                        
-                    default: 
+
+                    default:
                         throw new IllegalStateException();
-                    
+
                 }
             }
         };
-        
+
         cb.iterate();
-        idle.await(10,TimeUnit.SECONDS);
+        idle.await(10, TimeUnit.SECONDS);
         Assert.assertTrue(cb.isIdle());
-        
+
         cb.iterate();
         Assert.assertTrue(cb.waitForComplete());
-        Assert.assertEquals(6,cb.processed);
+        Assert.assertEquals(6, cb.processed);
     }
-    
-    
-    
+
+    @Test
+    public void testCloseDuringProcessingReturningScheduled() throws Exception
+    {
+        testCloseDuringProcessing(IteratingCallback.Action.SCHEDULED);
+    }
+
+    @Test
+    public void testCloseDuringProcessingReturningSucceeded() throws Exception
+    {
+        testCloseDuringProcessing(IteratingCallback.Action.SUCCEEDED);
+    }
+
+    private void testCloseDuringProcessing(final IteratingCallback.Action action) throws Exception
+    {
+        final CountDownLatch failureLatch = new CountDownLatch(1);
+        IteratingCallback callback = new IteratingCallback()
+        {
+            @Override
+            protected Action process() throws Exception
+            {
+                close();
+                return action;
+            }
+
+            @Override
+            protected void onCompleteFailure(Throwable cause)
+            {
+                failureLatch.countDown();
+            }
+        };
+
+        callback.iterate();
+
+        Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS));
+    }
+
     private abstract static class TestCB extends IteratingCallback
     {
-        CountDownLatch completed = new CountDownLatch(1);
-        int processed=0;
+        protected Runnable successTask = new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                succeeded();
+            }
+        };
+        protected Runnable failTask = new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                failed(new Exception("testing failure"));
+            }
+        };
+        protected CountDownLatch completed = new CountDownLatch(1);
+        protected int processed = 0;
 
         @Override
         protected void onCompleteSuccess()
         {
             completed.countDown();
         }
-        
+
         @Override
         public void onCompleteFailure(Throwable x)
         {
@@ -275,26 +323,8 @@
 
         boolean waitForComplete() throws InterruptedException
         {
-            completed.await(10,TimeUnit.SECONDS);
+            completed.await(10, TimeUnit.SECONDS);
             return isSucceeded();
         }
-        
-        Runnable successTask = new Runnable()
-        {
-            @Override
-            public void run()
-            {
-                succeeded();
-            }
-        };
-        Runnable failTask = new Runnable()
-        {
-            @Override
-            public void run()
-            {
-                failed(new Exception("testing failure"));
-            }
-        };
     }
-
 }
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiMapTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiMapTest.java
index 8ca0792..4c8dae4 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiMapTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiMapTest.java
@@ -122,7 +122,7 @@
     }
 
     /**
-     * Tests {@link MultiMap#putValues(String, String...)}
+     * Tests {@link MultiMap#putValues(String, Object[])}
      */
     @Test
     public void testPutValues_StringArray()
@@ -138,7 +138,7 @@
     }
 
     /**
-     * Tests {@link MultiMap#putValues(String, String...)}
+     * Tests {@link MultiMap#putValues(String, List)}
      */
     @Test
     public void testPutValues_VarArgs()
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java
index 89df6bb..0c03d7e 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java
@@ -38,10 +38,13 @@
 import java.util.Collection;
 
 import javax.servlet.MultipartConfigElement;
+import javax.servlet.ReadListener;
 import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
 import javax.servlet.http.Part;
 
 import org.eclipse.jetty.util.MultiPartInputStreamParser.MultiPart;
+import org.hamcrest.Matchers;
 import org.junit.Test;
 
 /**
@@ -62,6 +65,7 @@
         _tmpDir.deleteOnExit();
     }
     
+    @Test
     public void testBadMultiPartRequest()
     throws Exception
     {
@@ -71,12 +75,12 @@
         "Content-Type: application/octet-stream\r\n\r\n"+
         "How now brown cow."+
         "\r\n--" + boundary + "-\r\n\r\n";
-        
+
         MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
         MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()), 
-                                                             "multipart/form-data, boundary="+boundary,
-                                                             config,
-                                                             _tmpDir);
+                                                                         "multipart/form-data, boundary="+boundary,
+                                                                         config,
+                                                                         _tmpDir);
         mpis.setDeleteOnExit(true);
         try
         {
@@ -89,7 +93,56 @@
         }
     }
 
+
+    @Test
+    public void testFinalBoundaryOnly()
+    throws Exception
+    {
+        String delimiter = "\r\n";
+        final String boundary = "MockMultiPartTestBoundary";
+
+
+        // Malformed multipart request body containing only an arbitrary string of text, followed by the final boundary marker, delimited by empty lines.
+        String str =
+                delimiter +
+                "Hello world" +
+                delimiter +        // Two delimiter markers, which make an empty line.
+                delimiter +
+                "--" + boundary + "--" + delimiter; 
+
+        MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+        MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
+                                                                         "multipart/form-data, boundary="+boundary,
+                                                                         config,
+                                                                         _tmpDir);
+        mpis.setDeleteOnExit(true);
+        Collection<Part> parts = mpis.getParts();
+        assertTrue(mpis.getParts().isEmpty());
+    }
+
     
+    
+     @Test
+     public void testEmpty()
+     throws Exception
+     {
+         String delimiter = "\r\n";
+         final String boundary = "MockMultiPartTestBoundary";
+
+         String str =
+                 delimiter +
+                 "--" + boundary + "--" + delimiter; 
+
+         MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+         MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
+                                                                          "multipart/form-data, boundary="+boundary,
+                                                                          config,
+                                                                          _tmpDir);
+         mpis.setDeleteOnExit(true);
+         assertTrue(mpis.getParts().isEmpty());
+     }
+
+    @Test
     public void testNoBoundaryRequest()
     throws Exception
     {
@@ -186,6 +239,50 @@
         }
     }
     
+    
+    @Test
+    public void testBodyAlreadyConsumed()
+    throws Exception
+    {
+        ServletInputStream is = new ServletInputStream() {
+
+            @Override
+            public boolean isFinished()
+            {
+                return true;
+            }
+
+            @Override
+            public boolean isReady()
+            {
+                return false;
+            }
+
+            @Override
+            public void setReadListener(ReadListener readListener)
+            {
+            }
+
+            @Override
+            public int read() throws IOException
+            {
+                return 0;
+            }
+            
+        };
+        
+        MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+        MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(is, 
+                                                                         _contentType,
+                                                                         config,
+                                                                         _tmpDir);
+        mpis.setDeleteOnExit(true);
+        Collection<Part> parts = mpis.getParts();
+        assertEquals(0, parts.size());
+    }
+    
+    
+    
     @Test
     public void testWhitespaceBodyWithCRLF()
             throws Exception
@@ -354,6 +451,42 @@
             assertTrue(e.getMessage().startsWith("Request exceeds maxRequestSize"));
         }
     }
+    
+    
+    @Test
+    public void testRequestTooBigThrowsErrorOnGetParts ()
+    throws Exception
+    {
+        MultipartConfigElement config = new MultipartConfigElement(_dirname, 60, 100, 50);
+        MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
+                                                            _contentType,
+                                                             config,
+                                                             _tmpDir);
+        mpis.setDeleteOnExit(true);
+        Collection<Part> parts = null;
+        
+        //cause parsing
+        try
+        {
+            parts = mpis.getParts();
+            fail("Request should have exceeded maxRequestSize");
+        }
+        catch (IllegalStateException e)
+        {
+            assertTrue(e.getMessage().startsWith("Request exceeds maxRequestSize"));
+        }
+        
+        //try again
+        try
+        {
+            parts = mpis.getParts();
+            fail("Request should have exceeded maxRequestSize");
+        }
+        catch (IllegalStateException e)
+        {
+            assertTrue(e.getMessage().startsWith("Request exceeds maxRequestSize"));
+        }
+    }
 
     @Test
     public void testFileTooBig()
@@ -376,6 +509,41 @@
             assertTrue(e.getMessage().startsWith("Multipart Mime part"));
         }
     }
+    
+    @Test
+    public void testFileTooBigThrowsErrorOnGetParts()
+    throws Exception
+    {
+        MultipartConfigElement config = new MultipartConfigElement(_dirname, 40, 1024, 30);
+        MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
+                                                            _contentType,
+                                                             config,
+                                                             _tmpDir);
+        mpis.setDeleteOnExit(true);
+        Collection<Part> parts = null;
+        try
+        {
+            parts = mpis.getParts(); //caused parsing
+            fail("stuff.txt should have been larger than maxFileSize");
+        }
+        catch (IllegalStateException e)
+        {
+            assertTrue(e.getMessage().startsWith("Multipart Mime part"));
+        }
+        
+        //test again after the parsing
+        try
+        {
+            parts = mpis.getParts(); //caused parsing
+            fail("stuff.txt should have been larger than maxFileSize");
+        }
+        catch (IllegalStateException e)
+        {
+            assertTrue(e.getMessage().startsWith("Multipart Mime part"));
+        }
+    }
+    
+    
 
     @Test
     public void testPartFileNotDeleted () throws Exception
@@ -557,7 +725,7 @@
 
     }
     
-    
+    @Test
     public void testCharsetEncoding () throws Exception
     {
         String contentType = "multipart/form-data; boundary=TheBoundary; charset=ISO-8859-1";
@@ -657,6 +825,38 @@
     }
 
     
+    @Test
+    public void testWriteFilesIfContentDispositionFilename ()
+    throws Exception
+    {
+        String s = "--AaB03x\r\n"+
+                "content-disposition: form-data; name=\"field1\"; filename=\"frooble.txt\"\r\n"+
+                "\r\n"+
+                "Joe Blow\r\n"+
+                "--AaB03x\r\n"+
+                "content-disposition: form-data; name=\"stuff\"\r\n"+
+                "Content-Type: text/plain\r\n"+
+                "\r\n"+"sss"+
+                "aaa"+"\r\n" +
+                "--AaB03x--\r\n";
+        //all default values for multipartconfig, ie file size threshold 0
+        MultipartConfigElement config = new MultipartConfigElement(_dirname);
+        MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(s.getBytes()),
+                                                                         _contentType,
+                                                                         config,
+                                                                         _tmpDir); 
+        mpis.setDeleteOnExit(true);
+        mpis.setWriteFilesWithFilenames(true);
+        Collection<Part> parts = mpis.getParts();
+        assertThat(parts.size(), is(2));
+        Part field1 = mpis.getPart("field1"); //has a filename, should be written to a file
+        File f = ((MultiPartInputStreamParser.MultiPart)field1).getFile();
+        assertThat(f,notNullValue()); // longer than 100 bytes, should already be a tmp file
+        
+        Part stuff = mpis.getPart("stuff");
+        f = ((MultiPartInputStreamParser.MultiPart)stuff).getFile(); //should only be in memory, no filename
+        assertThat(f, nullValue());
+    }
     
     
     private void testMulti(String filename) throws IOException, ServletException, InterruptedException
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherDemo.java b/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherDemo.java
new file mode 100644
index 0000000..bd9414a
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherDemo.java
@@ -0,0 +1,132 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.util.PathWatcher.PathWatchEvent;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class PathWatcherDemo implements PathWatcher.Listener
+{
+    private static final Logger LOG = Log.getLogger(PathWatcherDemo.class);
+
+    public static void main(String[] args)
+    {
+        List<Path> paths = new ArrayList<>();
+        for (String arg : args)
+        {
+            paths.add(new File(arg).toPath());
+        }
+
+        if (paths.isEmpty())
+        {
+            LOG.warn("No paths specified on command line");
+            System.exit(-1);
+        }
+
+        PathWatcherDemo demo = new PathWatcherDemo();
+        try
+        {
+            demo.run(paths);
+        }
+        catch (Throwable t)
+        {
+            LOG.warn(t);
+        }
+    }
+
+    public void run(List<Path> paths) throws Exception
+    {
+        PathWatcher watcher = new PathWatcher();
+        //watcher.addListener(new PathWatcherDemo());
+        watcher.addListener (new PathWatcher.EventListListener(){
+
+            @Override
+            public void onPathWatchEvents(List<PathWatchEvent> events)
+            {
+               if (events == null)
+                   LOG.warn("Null events received");
+               if (events.isEmpty())
+                   LOG.warn("Empty events received");
+               
+               LOG.info("Bulk notification received");
+               for (PathWatchEvent e:events)
+                   onPathWatchEvent(e);
+                
+            }
+            
+        });
+        
+        watcher.setNotifyExistingOnStart(false);
+
+        List<String> excludes = new ArrayList<>();
+        excludes.add("glob:*.bak"); // ignore backup files
+        excludes.add("regex:^.*/\\~[^/]*$"); // ignore scratch files
+
+        for (Path path : paths)
+        {
+            if (Files.isDirectory(path))
+            {
+                PathWatcher.Config config = new PathWatcher.Config(path);
+                config.addExcludeHidden();
+                config.addExcludes(excludes);
+                config.setRecurseDepth(4);
+                watcher.watch(config);
+            }
+            else
+            {
+                watcher.watch(path);
+            }
+        }
+        watcher.start();
+        
+        Thread.currentThread().join();
+    }
+
+    @Override
+    public void onPathWatchEvent(PathWatchEvent event)
+    {
+        StringBuilder msg = new StringBuilder();
+        msg.append("onPathWatchEvent: [");
+        msg.append(event.getType());
+        msg.append("] ");
+        msg.append(event.getPath());
+        msg.append(" (count=").append(event.getCount()).append(")");
+        if (Files.isRegularFile(event.getPath()))
+        {
+            try
+            {
+                String fsize = String.format(" (filesize=%,d)",Files.size(event.getPath()));
+                msg.append(fsize);
+            }
+            catch (IOException e)
+            {
+                LOG.warn("Unable to get filesize",e);
+            }
+        }
+        LOG.info("{}",msg.toString());
+    }
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherTest.java
new file mode 100644
index 0000000..5e05758
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherTest.java
@@ -0,0 +1,709 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.PathWatcher.PathWatchEventType.*;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.toolchain.test.OS;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.eclipse.jetty.util.PathWatcher.PathWatchEvent;
+import org.eclipse.jetty.util.PathWatcher.PathWatchEventType;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+
+@Ignore("Disabled due to behavioral differences in various FileSystems (hard to write a single testcase that works in all scenarios)")
+public class PathWatcherTest
+{
+    public static class PathWatchEventCapture implements PathWatcher.Listener
+    {
+        public final static String FINISH_TAG = "#finished#.tag";
+        private static final Logger LOG = Log.getLogger(PathWatcherTest.PathWatchEventCapture.class);
+        private final Path baseDir;
+
+        /**
+         * Map of relative paths seen, to their events seen (in order seen)
+         */
+        public Map<String, List<PathWatchEventType>> events = new HashMap<>();
+
+        public int latchCount = 1;
+        public CountDownLatch finishedLatch;
+        private PathWatchEventType triggerType;
+        private Path triggerPath;
+
+        public PathWatchEventCapture(Path baseDir)
+        {
+            this.baseDir = baseDir;
+        }
+        
+       public void reset()
+       {
+           finishedLatch = new CountDownLatch(latchCount);
+           events.clear();
+       }
+
+        @Override
+        public void onPathWatchEvent(PathWatchEvent event)
+        {
+            synchronized (events)
+            {
+                //if triggered by path
+                if (triggerPath != null)
+                {
+                   
+                    if (triggerPath.equals(event.getPath()) && (event.getType() == triggerType))
+                    {
+                        LOG.debug("Encountered finish trigger: {} on {}",event.getType(),event.getPath());
+                        finishedLatch.countDown();
+                    }
+                }
+                else if (finishedLatch != null)
+                {
+                    finishedLatch.countDown();
+                }
+                
+
+                Path relativePath = this.baseDir.relativize(event.getPath());
+                String key = relativePath.toString().replace(File.separatorChar,'/');
+
+                List<PathWatchEventType> types = this.events.get(key);
+                if (types == null)
+                {
+                    types = new ArrayList<>();
+                }
+                types.add(event.getType());
+                this.events.put(key,types);
+                LOG.debug("Captured Event: {} | {}",event.getType(),key);
+            }
+        }
+
+        /**
+         * Validate the events seen match expectations.
+         * <p>
+         * Note: order of events is only important when looking at a specific file or directory. Events for multiple
+         * files can overlap in ways that this assertion doesn't care about.
+         * 
+         * @param expectedEvents
+         *            the events expected
+         */
+        public void assertEvents(Map<String, PathWatchEventType[]> expectedEvents)
+        {
+            assertThat("Event match (file|diretory) count",this.events.size(),is(expectedEvents.size()));
+
+            for (Map.Entry<String, PathWatchEventType[]> entry : expectedEvents.entrySet())
+            {
+                String relativePath = entry.getKey();
+                PathWatchEventType[] expectedTypes = entry.getValue();
+                assertEvents(relativePath,expectedTypes);
+            }
+        }
+
+        /**
+         * Validate the events seen match expectations.
+         * <p>
+         * Note: order of events is only important when looking at a specific file or directory. Events for multiple
+         * files can overlap in ways that this assertion doesn't care about.
+         * 
+         * @param relativePath
+         *            the test relative path to look for
+         * 
+         * @param expectedEvents
+         *            the events expected
+         */
+        public void assertEvents(String relativePath, PathWatchEventType... expectedEvents)
+        {
+            synchronized (events)
+            {
+                List<PathWatchEventType> actualEvents = this.events.get(relativePath);
+                assertThat("Events for path [" + relativePath + "]",actualEvents,contains(expectedEvents));
+            }
+        }
+
+        /**
+         * Set the path and type that will trigger this capture to be finished
+         * 
+         * @param triggerPath
+         *            the trigger path we look for to know that the capture is complete
+         * @param triggerType
+         *            the trigger type we look for to know that the capture is complete
+         */
+        public void setFinishTrigger(Path triggerPath, PathWatchEventType triggerType)
+        {
+            this.triggerPath = triggerPath;
+            this.triggerType = triggerType;
+            this.latchCount = 1;
+            this.finishedLatch = new CountDownLatch(1);
+            LOG.debug("Setting finish trigger {} for path {}",triggerType,triggerPath);
+        }
+        
+        public void setFinishTrigger (int count)
+        {
+            latchCount = count;
+            finishedLatch = new CountDownLatch(latchCount);
+        }
+
+        /**
+         * Await the countdown latch on the finish trigger
+         * 
+         * @param pathWatcher
+         *            the watcher instance we are waiting on
+         * @throws IOException
+         *             if unable to create the finish tag file
+         * @throws InterruptedException
+         *             if unable to await the finish of the run
+         * @see #setFinishTrigger(Path, PathWatchEventType)
+         */
+        public void awaitFinish(PathWatcher pathWatcher) throws IOException, InterruptedException
+        {
+            //assertThat("Trigger Path must be set",triggerPath,notNullValue());
+            //assertThat("Trigger Type must be set",triggerType,notNullValue());
+            double multiplier = 25.0;
+            long awaitMillis = (long)((double)pathWatcher.getUpdateQuietTimeMillis() * multiplier);
+            LOG.debug("Waiting for finish ({} ms)",awaitMillis);
+            assertThat("Timed Out (" + awaitMillis + "ms) waiting for capture to finish",finishedLatch.await(awaitMillis,TimeUnit.MILLISECONDS),is(true));
+            LOG.debug("Finished capture");
+        }
+    }
+
+    private static void updateFile(Path path, String newContents) throws IOException
+    {
+        try (FileOutputStream out = new FileOutputStream(path.toFile()))
+        {
+            out.write(newContents.getBytes(StandardCharsets.UTF_8));
+            out.flush();
+            out.getChannel().force(true);
+            out.getFD().sync();
+        }
+    }
+
+    /**
+     * Update (optionally create) a file over time.
+     * <p>
+     * The file will be created in a slowed down fashion, over the time specified.
+     * 
+     * @param path
+     *            the file to update / create
+     * @param fileSize
+     *            the ultimate file size to create
+     * @param timeDuration
+     *            the time duration to take to create the file (approximate, not 100% accurate)
+     * @param timeUnit
+     *            the time unit to take to create the file
+     * @throws IOException
+     *             if unable to write file
+     * @throws InterruptedException
+     *             if sleep between writes was interrupted
+     */
+    private void updateFileOverTime(Path path, int fileSize, int timeDuration, TimeUnit timeUnit) throws IOException, InterruptedException
+    {
+        // how long to sleep between writes
+        int sleepMs = 100;
+
+        // how many millis to spend writing entire file size
+        long totalMs = timeUnit.toMillis(timeDuration);
+
+        // how many write chunks to write
+        int writeCount = (int)((int)totalMs / (int)sleepMs);
+
+        // average chunk buffer
+        int chunkBufLen = fileSize / writeCount;
+        byte chunkBuf[] = new byte[chunkBufLen];
+        Arrays.fill(chunkBuf,(byte)'x');
+
+        try (FileOutputStream out = new FileOutputStream(path.toFile()))
+        {
+            int left = fileSize;
+
+            while (left > 0)
+            {
+                int len = Math.min(left,chunkBufLen);
+                out.write(chunkBuf,0,len);
+                left -= chunkBufLen;
+                out.flush();
+                out.getChannel().force(true);
+                // Force file to actually write to disk.
+                // Skipping any sort of filesystem caching of the write
+                out.getFD().sync();
+                TimeUnit.MILLISECONDS.sleep(sleepMs);
+            }
+        }
+    }
+
+    /**
+     * Sleep longer than the quiet time.
+     * 
+     * @param pathWatcher
+     *            the path watcher to inspect for its quiet time
+     * @throws InterruptedException
+     *             if unable to sleep
+     */
+    private static void awaitQuietTime(PathWatcher pathWatcher) throws InterruptedException
+    {
+        double multiplier = 5.0;
+        if (OS.IS_WINDOWS)
+        {
+            // Microsoft Windows filesystem is too slow for a lower multiplier
+            multiplier = 6.0;
+        }
+        TimeUnit.MILLISECONDS.sleep((long)((double)pathWatcher.getUpdateQuietTimeMillis() * multiplier));
+    }
+
+    private static final int KB = 1024;
+    private static final int MB = KB * KB;
+
+    @Rule
+    public TestingDir testdir = new TestingDir();
+
+    @Test
+    public void testConfig_ShouldRecurse_0() throws IOException
+    {
+        Path dir = testdir.getEmptyPathDir();
+
+        // Create a few directories
+        Files.createDirectories(dir.resolve("a/b/c/d"));
+
+        PathWatcher.Config config = new PathWatcher.Config(dir);
+
+        config.setRecurseDepth(0);
+        assertThat("Config.recurse[0].shouldRecurse[./a/b]",config.shouldRecurseDirectory(dir.resolve("a/b")),is(false));
+        assertThat("Config.recurse[0].shouldRecurse[./a]",config.shouldRecurseDirectory(dir.resolve("a")),is(false));
+        assertThat("Config.recurse[0].shouldRecurse[./]",config.shouldRecurseDirectory(dir),is(false));
+    }
+
+    @Test
+    public void testConfig_ShouldRecurse_1() throws IOException
+    {
+        Path dir = testdir.getEmptyPathDir();
+
+        // Create a few directories
+        Files.createDirectories(dir.resolve("a/b/c/d"));
+
+        PathWatcher.Config config = new PathWatcher.Config(dir);
+
+        config.setRecurseDepth(1);
+        assertThat("Config.recurse[1].shouldRecurse[./a/b]",config.shouldRecurseDirectory(dir.resolve("a/b")),is(false));
+        assertThat("Config.recurse[1].shouldRecurse[./a]",config.shouldRecurseDirectory(dir.resolve("a")),is(true));
+        assertThat("Config.recurse[1].shouldRecurse[./]",config.shouldRecurseDirectory(dir),is(true));
+    }
+
+    @Test
+    public void testConfig_ShouldRecurse_2() throws IOException
+    {
+        Path dir = testdir.getEmptyPathDir();
+
+        // Create a few directories
+        Files.createDirectories(dir.resolve("a/b/c/d"));
+
+        PathWatcher.Config config = new PathWatcher.Config(dir);
+
+        config.setRecurseDepth(2);
+        assertThat("Config.recurse[1].shouldRecurse[./a/b/c]",config.shouldRecurseDirectory(dir.resolve("a/b/c")),is(false));
+        assertThat("Config.recurse[1].shouldRecurse[./a/b]",config.shouldRecurseDirectory(dir.resolve("a/b")),is(true));
+        assertThat("Config.recurse[1].shouldRecurse[./a]",config.shouldRecurseDirectory(dir.resolve("a")),is(true));
+        assertThat("Config.recurse[1].shouldRecurse[./]",config.shouldRecurseDirectory(dir),is(true));
+    }
+    
+    
+    @Test
+    public void testConfig_ShouldRecurse_3() throws IOException
+    {
+        Path dir = testdir.getEmptyPathDir();
+        
+        //Create some deep dirs
+        Files.createDirectories(dir.resolve("a/b/c/d/e/f/g"));
+        
+        PathWatcher.Config config = new PathWatcher.Config(dir);
+        config.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
+        assertThat("Config.recurse[1].shouldRecurse[./a/b/c/d/g]",config.shouldRecurseDirectory(dir.resolve("a/b/c/d/g")),is(true));
+        assertThat("Config.recurse[1].shouldRecurse[./a/b/c/d/f]",config.shouldRecurseDirectory(dir.resolve("a/b/c/d/f")),is(true));
+        assertThat("Config.recurse[1].shouldRecurse[./a/b/c/d/e]",config.shouldRecurseDirectory(dir.resolve("a/b/c/d/e")),is(true));
+        assertThat("Config.recurse[1].shouldRecurse[./a/b/c/d]",config.shouldRecurseDirectory(dir.resolve("a/b/c/d")),is(true));
+        assertThat("Config.recurse[1].shouldRecurse[./a/b/c]",config.shouldRecurseDirectory(dir.resolve("a/b/c")),is(true));
+        assertThat("Config.recurse[1].shouldRecurse[./a/b]",config.shouldRecurseDirectory(dir.resolve("a/b")),is(true));
+        assertThat("Config.recurse[1].shouldRecurse[./a]",config.shouldRecurseDirectory(dir.resolve("a")),is(true));
+        assertThat("Config.recurse[1].shouldRecurse[./]",config.shouldRecurseDirectory(dir),is(true));
+    }
+    
+    @Test
+    public void testRestart() throws Exception
+    {
+        Path dir = testdir.getEmptyPathDir();
+        Files.createDirectories(dir.resolve("b/c"));
+        Files.createFile(dir.resolve("a.txt"));
+        Files.createFile(dir.resolve("b.txt"));
+        
+        PathWatcher pathWatcher = new PathWatcher();
+        pathWatcher.setNotifyExistingOnStart(true);
+        pathWatcher.setUpdateQuietTime(500,TimeUnit.MILLISECONDS);
+        
+        // Add listener
+        PathWatchEventCapture capture = new PathWatchEventCapture(dir);
+        capture.setFinishTrigger(2);
+        pathWatcher.addListener(capture);
+
+      
+        PathWatcher.Config config = new PathWatcher.Config(dir);
+        config.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
+        config.addIncludeGlobRelative("*.txt");
+        pathWatcher.watch(config);
+        try
+        {
+            pathWatcher.start();
+
+            // Let quiet time do its thing
+            awaitQuietTime(pathWatcher);
+
+            Map<String, PathWatchEventType[]> expected = new HashMap<>();
+            
+         
+            expected.put("a.txt",new PathWatchEventType[] {ADDED});
+            expected.put("b.txt",new PathWatchEventType[] {ADDED});
+
+         
+            capture.assertEvents(expected);
+            
+            //stop it
+            pathWatcher.stop();
+            
+            capture.reset();
+            
+            Thread.currentThread().sleep(1000);
+            
+            pathWatcher.start();
+            
+            awaitQuietTime(pathWatcher);
+            
+            capture.assertEvents(expected);
+            
+        }
+        finally
+        {
+            pathWatcher.stop();
+        }
+    }
+
+    /**
+     * When starting up the PathWatcher, the events should occur
+     * indicating files that are of interest that already exist
+     * on the filesystem.
+     * 
+     * @throws Exception
+     *             on test failure
+     */
+    @Test
+    public void testStartupFindFiles() throws Exception
+    {
+        Path dir = testdir.getEmptyPathDir();
+
+        // Files we are interested in
+        Files.createFile(dir.resolve("foo.war"));
+        Files.createDirectories(dir.resolve("bar/WEB-INF"));
+        Files.createFile(dir.resolve("bar/WEB-INF/web.xml"));
+
+        // Files we don't care about
+        Files.createFile(dir.resolve("foo.war.backup"));
+        Files.createFile(dir.resolve(".hidden.war"));
+        Files.createDirectories(dir.resolve(".wat/WEB-INF"));
+        Files.createFile(dir.resolve(".wat/huh.war"));
+        Files.createFile(dir.resolve(".wat/WEB-INF/web.xml"));
+
+        PathWatcher pathWatcher = new PathWatcher();
+        pathWatcher.setUpdateQuietTime(300,TimeUnit.MILLISECONDS);
+
+        // Add listener
+        PathWatchEventCapture capture = new PathWatchEventCapture(dir);
+        pathWatcher.addListener(capture);
+
+        // Add test dir configuration
+        PathWatcher.Config baseDirConfig = new PathWatcher.Config(dir);
+        baseDirConfig.setRecurseDepth(2);
+        baseDirConfig.addExcludeHidden();
+        baseDirConfig.addIncludeGlobRelative("*.war");
+        baseDirConfig.addIncludeGlobRelative("*/WEB-INF/web.xml");
+        pathWatcher.watch(baseDirConfig);
+
+        try
+        {
+            pathWatcher.start();
+
+            // Let quiet time do its thing
+            awaitQuietTime(pathWatcher);
+
+            Map<String, PathWatchEventType[]> expected = new HashMap<>();
+
+            expected.put("bar/WEB-INF/web.xml",new PathWatchEventType[] { ADDED });
+            expected.put("foo.war",new PathWatchEventType[] { ADDED });
+
+            capture.assertEvents(expected);
+        }
+        finally
+        {
+            pathWatcher.stop();
+        }
+    }
+    
+    @Test
+    public void testGlobPattern () throws Exception
+    {
+        Path dir = testdir.getEmptyPathDir();
+
+        // Files we are interested in
+        Files.createFile(dir.resolve("a.txt"));
+        Files.createDirectories(dir.resolve("b/b.txt"));
+        Files.createDirectories(dir.resolve("c/d"));
+        Files.createFile(dir.resolve("c/d/d.txt"));
+        Files.createFile(dir.resolve(".foo.txt"));
+
+        // Files we don't care about
+        Files.createFile(dir.resolve("txt.foo"));
+        Files.createFile(dir.resolve("b/foo.xml"));
+    
+
+        PathWatcher pathWatcher = new PathWatcher();
+        pathWatcher.setUpdateQuietTime(300,TimeUnit.MILLISECONDS);
+
+        // Add listener
+        PathWatchEventCapture capture = new PathWatchEventCapture(dir);
+        capture.setFinishTrigger(3);
+        pathWatcher.addListener(capture);
+
+        // Add test dir configuration
+        PathWatcher.Config baseDirConfig = new PathWatcher.Config(dir);
+        baseDirConfig.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
+        baseDirConfig.addExcludeHidden();
+        baseDirConfig.addIncludeGlobRelative("**.txt");
+        pathWatcher.watch(baseDirConfig);
+
+        try
+        {
+            pathWatcher.start();
+
+            // Let quiet time do its thing
+            awaitQuietTime(pathWatcher);
+
+            Map<String, PathWatchEventType[]> expected = new HashMap<>();
+
+            expected.put("a.txt",new PathWatchEventType[] { ADDED });
+            expected.put("b/b.txt",new PathWatchEventType[] { ADDED });
+            expected.put("c/d/d.txt",new PathWatchEventType[] { ADDED });
+            capture.assertEvents(expected);
+        }
+        finally
+        {
+            pathWatcher.stop();
+        }
+    }
+
+    @Test
+    public void testDeployFiles_Update_Delete() throws Exception
+    {
+        Path dir = testdir.getEmptyPathDir();
+
+        // Files we are interested in
+        Files.createFile(dir.resolve("foo.war"));
+        Files.createDirectories(dir.resolve("bar/WEB-INF"));
+        Files.createFile(dir.resolve("bar/WEB-INF/web.xml"));
+
+        PathWatcher pathWatcher = new PathWatcher();
+        pathWatcher.setUpdateQuietTime(300,TimeUnit.MILLISECONDS);
+
+        // Add listener
+        PathWatchEventCapture capture = new PathWatchEventCapture(dir);
+        capture.setFinishTrigger(5);
+        pathWatcher.addListener(capture);
+
+        // Add test dir configuration
+        PathWatcher.Config baseDirConfig = new PathWatcher.Config(dir);
+        baseDirConfig.setRecurseDepth(2);
+        baseDirConfig.addExcludeHidden();
+        baseDirConfig.addIncludeGlobRelative("*.war");
+        baseDirConfig.addIncludeGlobRelative("*/WEB-INF/web.xml");
+        pathWatcher.watch(baseDirConfig);
+
+        try
+        {
+            pathWatcher.start();
+
+            // Pretend that startup occurred
+            awaitQuietTime(pathWatcher);
+
+            // Update web.xml
+            Path webFile = dir.resolve("bar/WEB-INF/web.xml");
+            //capture.setFinishTrigger(webFile,MODIFIED);
+            updateFile(webFile,"Hello Update");
+
+            // Delete war
+            Files.delete(dir.resolve("foo.war"));
+
+            // Add a another new war
+            Files.createFile(dir.resolve("bar.war"));
+
+            // Let capture complete
+            capture.awaitFinish(pathWatcher);
+
+            Map<String, PathWatchEventType[]> expected = new HashMap<>();
+
+            expected.put("bar/WEB-INF/web.xml",new PathWatchEventType[] { ADDED, MODIFIED });
+            expected.put("foo.war",new PathWatchEventType[] { ADDED, DELETED });
+            expected.put("bar.war",new PathWatchEventType[] { ADDED });
+
+            capture.assertEvents(expected);
+        }
+        finally
+        {
+            pathWatcher.stop();
+        }
+    }
+
+    @Test
+    public void testDeployFiles_NewWar() throws Exception
+    {
+        Path dir = testdir.getEmptyPathDir();
+
+        // Files we are interested in
+        Files.createFile(dir.resolve("foo.war"));
+        Files.createDirectories(dir.resolve("bar/WEB-INF"));
+        Files.createFile(dir.resolve("bar/WEB-INF/web.xml"));
+
+        PathWatcher pathWatcher = new PathWatcher();
+        pathWatcher.setUpdateQuietTime(300,TimeUnit.MILLISECONDS);
+
+        // Add listener
+        PathWatchEventCapture capture = new PathWatchEventCapture(dir);
+        pathWatcher.addListener(capture);
+
+        // Add test dir configuration
+        PathWatcher.Config baseDirConfig = new PathWatcher.Config(dir);
+        baseDirConfig.setRecurseDepth(2);
+        baseDirConfig.addExcludeHidden();
+        baseDirConfig.addIncludeGlobRelative("*.war");
+        baseDirConfig.addIncludeGlobRelative("*/WEB-INF/web.xml");
+        pathWatcher.watch(baseDirConfig);
+
+        try
+        {
+            pathWatcher.start();
+
+            // Pretend that startup occurred
+            awaitQuietTime(pathWatcher);
+
+            // New war added
+            Path warFile = dir.resolve("hello.war");
+            capture.setFinishTrigger(warFile,MODIFIED);
+            updateFile(warFile,"Hello Update");
+
+            // Let capture finish
+            capture.awaitFinish(pathWatcher);
+
+            Map<String, PathWatchEventType[]> expected = new HashMap<>();
+
+            expected.put("bar/WEB-INF/web.xml",new PathWatchEventType[] { ADDED });
+            expected.put("foo.war",new PathWatchEventType[] { ADDED });
+            expected.put("hello.war",new PathWatchEventType[] { ADDED, MODIFIED });
+
+            capture.assertEvents(expected);
+        }
+        finally
+        {
+            pathWatcher.stop();
+        }
+    }
+
+    /**
+     * Pretend to add a new war file that is large, and being copied into place
+     * using some sort of technique that is slow enough that it takes a while for
+     * the entire war file to exist in place.
+     * <p>
+     * This is to test the quiet time logic to ensure that only a single MODIFIED event occurs on this new war file
+     * 
+     * @throws Exception
+     *             on test failure
+     */
+    @Test
+    public void testDeployFiles_NewWar_LargeSlowCopy() throws Exception
+    {
+        Path dir = testdir.getEmptyPathDir();
+
+        // Files we are interested in
+        Files.createFile(dir.resolve("foo.war"));
+        Files.createDirectories(dir.resolve("bar/WEB-INF"));
+        Files.createFile(dir.resolve("bar/WEB-INF/web.xml"));
+
+        PathWatcher pathWatcher = new PathWatcher();
+        pathWatcher.setUpdateQuietTime(500,TimeUnit.MILLISECONDS);
+
+        // Add listener
+        PathWatchEventCapture capture = new PathWatchEventCapture(dir);
+        pathWatcher.addListener(capture);
+
+        // Add test dir configuration
+        PathWatcher.Config baseDirConfig = new PathWatcher.Config(dir);
+        baseDirConfig.setRecurseDepth(2);
+        baseDirConfig.addExcludeHidden();
+        baseDirConfig.addIncludeGlobRelative("*.war");
+        baseDirConfig.addIncludeGlobRelative("*/WEB-INF/web.xml");
+        pathWatcher.watch(baseDirConfig);
+
+        try
+        {
+            pathWatcher.start();
+
+            // Pretend that startup occurred
+            awaitQuietTime(pathWatcher);
+
+            // New war added (slowly)
+            Path warFile = dir.resolve("hello.war");
+            capture.setFinishTrigger(warFile,MODIFIED);
+            updateFileOverTime(warFile,50 * MB,3,TimeUnit.SECONDS);
+
+            // Let capture finish
+            capture.awaitFinish(pathWatcher);
+
+            Map<String, PathWatchEventType[]> expected = new HashMap<>();
+
+            expected.put("bar/WEB-INF/web.xml",new PathWatchEventType[] { ADDED });
+            expected.put("foo.war",new PathWatchEventType[] { ADDED });
+            expected.put("hello.war",new PathWatchEventType[] { ADDED, MODIFIED });
+
+            capture.assertEvents(expected);
+        }
+        finally
+        {
+            pathWatcher.stop();
+        }
+    }
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/QueueBenchmarkTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/QueueBenchmarkTest.java
index d8281007..131048d 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/QueueBenchmarkTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/QueueBenchmarkTest.java
@@ -40,20 +40,8 @@
 public class QueueBenchmarkTest
 {
     private static final Logger logger = Log.getLogger(QueueBenchmarkTest.class);
-    private static final Runnable ELEMENT = new Runnable()
-    {
-        @Override
-        public void run()
-        {
-        }
-    };
-    private static final Runnable END = new Runnable()
-    {
-        @Override
-        public void run()
-        {
-        }
-    };
+    private static final Runnable ELEMENT = () -> {};
+    private static final Runnable END = () -> {};
 
     @Stress("High CPU")
     @Test
@@ -67,10 +55,10 @@
         final int iterations = 16 * 1024 * 1024;
 
         final List<Queue<Runnable>> queues = new ArrayList<>();
-        queues.add(new ConcurrentArrayQueue<Runnable>()); // Jetty lock-free queue, allocating array blocks
-        queues.add(new ConcurrentLinkedQueue<Runnable>()); // JDK lock-free queue, allocating nodes
-        queues.add(new ArrayBlockingQueue<Runnable>(iterations * writers)); // JDK lock-based, circular array queue
-        queues.add(new BlockingArrayQueue<Runnable>(iterations * writers)); // Jetty lock-based, circular array queue
+        queues.add(new ConcurrentArrayQueue<>()); // Jetty lock-free queue, allocating array blocks
+        queues.add(new ConcurrentLinkedQueue<>()); // JDK lock-free queue, allocating nodes
+        queues.add(new ArrayBlockingQueue<>(iterations * writers)); // JDK lock-based, circular array queue
+        queues.add(new BlockingArrayQueue<>(iterations * writers)); // Jetty lock-based, circular array queue
 
         testQueues(readers, writers, iterations, queues, false);
     }
@@ -87,9 +75,9 @@
         final int iterations = 16 * 1024 * 1024;
 
         final List<Queue<Runnable>> queues = new ArrayList<>();
-        queues.add(new LinkedBlockingQueue<Runnable>());
-        queues.add(new ArrayBlockingQueue<Runnable>(iterations * writers));
-        queues.add(new BlockingArrayQueue<Runnable>(iterations * writers));
+        queues.add(new LinkedBlockingQueue<>());
+        queues.add(new ArrayBlockingQueue<>(iterations * writers));
+        queues.add(new BlockingArrayQueue<>(iterations * writers));
 
         testQueues(readers, writers, iterations, queues, true);
     }
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/RolloverFileOutputStreamTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/RolloverFileOutputStreamTest.java
new file mode 100644
index 0000000..cf3df17
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/RolloverFileOutputStreamTest.java
@@ -0,0 +1,305 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.io.File;
+import java.io.FileReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAccessor;
+import java.util.Arrays;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.resource.ResourceTest;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+
+public class RolloverFileOutputStreamTest
+{
+    private static ZoneId toZoneId(String timezoneId)
+    {
+        ZoneId zone = TimeZone.getTimeZone(timezoneId).toZoneId();
+        // System.out.printf(".toZoneId(\"%s\") = [id=%s,normalized=%s]%n", timezoneId, zone.getId(), zone.normalized());
+        return zone;
+    }
+    
+    private static ZonedDateTime toDateTime(String timendate, ZoneId zone)
+    {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd-hh:mm:ss.S a z")
+                .withZone(zone);
+        return ZonedDateTime.parse(timendate, formatter);
+    }
+    
+    private static String toString(TemporalAccessor date)
+    {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd-hh:mm:ss.S a z");
+        return formatter.format(date);
+    }
+    
+    private void assertSequence(ZonedDateTime midnight, Object[][] expected)
+    {
+        ZonedDateTime nextEvent = midnight;
+        
+        for (int i = 0; i < expected.length; i++)
+        {
+            long lastMs = nextEvent.toInstant().toEpochMilli();
+            nextEvent = nextEvent.toLocalDate().plus(1, ChronoUnit.DAYS).atStartOfDay(nextEvent.getZone());                
+            assertThat("Next Event", toString(nextEvent), is(expected[i][0]));
+            long duration = (nextEvent.toInstant().toEpochMilli() - lastMs);
+            assertThat("Duration to next event", duration, is((long) expected[i][1]));
+        }
+    }
+    
+    @Test
+    public void testMidnightRolloverCalc_PST_DST_Start()
+    {
+        ZoneId zone = toZoneId("PST");
+        ZonedDateTime initialDate = toDateTime("2016.03.10-01:23:45.0 PM PST", zone);
+        
+        ZonedDateTime midnight = RolloverFileOutputStream.toMidnight(initialDate);
+        assertThat("Midnight", toString(midnight), is("2016.03.11-12:00:00.0 AM PST"));
+        
+        Object expected[][] = {
+                {"2016.03.12-12:00:00.0 AM PST", 86_400_000L},
+                {"2016.03.13-12:00:00.0 AM PST", 86_400_000L},
+                {"2016.03.14-12:00:00.0 AM PDT", 82_800_000L}, // the short day
+                {"2016.03.15-12:00:00.0 AM PDT", 86_400_000L},
+                {"2016.03.16-12:00:00.0 AM PDT", 86_400_000L},
+        };
+    
+        assertSequence(midnight, expected);
+    }
+    
+    @Test
+    public void testMidnightRolloverCalc_PST_DST_End()
+    {
+        ZoneId zone = toZoneId("PST");
+        ZonedDateTime initialDate = toDateTime("2016.11.03-11:22:33.0 AM PDT", zone);
+    
+        ZonedDateTime midnight = RolloverFileOutputStream.toMidnight(initialDate);
+        assertThat("Midnight", toString(midnight), is("2016.11.04-12:00:00.0 AM PDT"));
+    
+        Object expected[][] = {
+                {"2016.11.05-12:00:00.0 AM PDT", 86_400_000L},
+                {"2016.11.06-12:00:00.0 AM PDT", 86_400_000L},
+                {"2016.11.07-12:00:00.0 AM PST", 90_000_000L}, // the long day
+                {"2016.11.08-12:00:00.0 AM PST", 86_400_000L},
+                {"2016.11.09-12:00:00.0 AM PST", 86_400_000L},
+        };
+    
+        assertSequence(midnight, expected);
+    }
+    
+    @Test
+    public void testMidnightRolloverCalc_Sydney_DST_Start()
+    {
+        ZoneId zone = toZoneId("Australia/Sydney");
+        ZonedDateTime initialDate = toDateTime("2016.09.31-01:23:45.0 PM AEST", zone);
+    
+        ZonedDateTime midnight = RolloverFileOutputStream.toMidnight(initialDate);
+        assertThat("Midnight", toString(midnight), is("2016.10.01-12:00:00.0 AM AEST"));
+    
+        Object expected[][] = {
+                {"2016.10.02-12:00:00.0 AM AEST", 86_400_000L},
+                {"2016.10.03-12:00:00.0 AM AEDT", 82_800_000L}, // the short day
+                {"2016.10.04-12:00:00.0 AM AEDT", 86_400_000L},
+                {"2016.10.05-12:00:00.0 AM AEDT", 86_400_000L},
+                {"2016.10.06-12:00:00.0 AM AEDT", 86_400_000L},
+        };
+        
+        assertSequence(midnight, expected);
+    }
+    
+    @Test
+    public void testMidnightRolloverCalc_Sydney_DST_End()
+    {
+        ZoneId zone = toZoneId("Australia/Sydney");
+        ZonedDateTime initialDate = toDateTime("2016.04.01-11:22:33.0 AM AEDT", zone);
+    
+        ZonedDateTime midnight = RolloverFileOutputStream.toMidnight(initialDate);
+        assertThat("Midnight", toString(midnight), is("2016.04.02-12:00:00.0 AM AEDT"));
+    
+        Object expected[][] = {
+                {"2016.04.03-12:00:00.0 AM AEDT", 86_400_000L},
+                {"2016.04.04-12:00:00.0 AM AEST", 90_000_000L}, // The long day
+                {"2016.04.05-12:00:00.0 AM AEST", 86_400_000L},
+                {"2016.04.06-12:00:00.0 AM AEST", 86_400_000L},
+                {"2016.04.07-12:00:00.0 AM AEST", 86_400_000L},
+        };
+    
+        assertSequence(midnight, expected);
+    }
+    
+    @Test
+    public void testFilehandling() throws Exception
+    {
+        File testDir = MavenTestingUtils.getTargetTestingDir(ResourceTest.class.getName());
+        Path testPath = testDir.toPath();
+        FS.ensureEmpty(testDir);
+
+        ZoneId zone = toZoneId("Australia/Sydney");
+        ZonedDateTime now = toDateTime("2016.04.10-08:30:12.3 AM AEDT", zone);
+        
+        File template = new File(testDir,"test-rofos-yyyy_mm_dd.log");
+
+        try (RolloverFileOutputStream rofos = 
+            new RolloverFileOutputStream(template.getAbsolutePath(),false,3,TimeZone.getTimeZone(zone),null,null,now))
+        {
+            rofos.write("TICK".getBytes());
+            rofos.flush();
+        }
+        
+        now = now.plus(5,ChronoUnit.MINUTES);
+        
+        try (RolloverFileOutputStream rofos = 
+            new RolloverFileOutputStream(template.getAbsolutePath(),false,3,TimeZone.getTimeZone(zone),null,null,now))
+        {
+            rofos.write("TOCK".getBytes());
+            rofos.flush();
+            String[] ls = testDir.list();
+            assertThat(ls.length,is(2));
+            String backup = null;
+            for (String n: ls)
+            {
+                if (!"test-rofos-2016_04_10.log".equals(n))
+                    backup = n;
+            }
+            
+            assertThat(Arrays.asList(ls),Matchers.containsInAnyOrder(backup,"test-rofos-2016_04_10.log"));
+            
+            Files.setLastModifiedTime(testPath.resolve(backup),FileTime.from(now.toInstant()));
+            Files.setLastModifiedTime(testPath.resolve("test-rofos-2016_04_10.log"),FileTime.from(now.toInstant()));
+
+            ZonedDateTime time = now.minus(1,ChronoUnit.DAYS);
+            for (int i=10;i-->5;)
+            {
+                String file = "test-rofos-2016_04_0"+i+".log";
+                Path path = testPath.resolve(file);
+                FS.touch(path);
+                Files.setLastModifiedTime(path,FileTime.from(time.toInstant()));
+                
+                if (i%2==0)
+                {
+                    file = "test-rofos-2016_04_0"+i+".log.083512300";
+                    path = testPath.resolve(file);
+                    FS.touch(path);
+                    Files.setLastModifiedTime(path,FileTime.from(time.toInstant()));
+                    time = time.minus(1,ChronoUnit.DAYS);
+                }
+
+                file = "unrelated-"+i;
+                path = testPath.resolve(file);
+                FS.touch(path);
+                Files.setLastModifiedTime(path,FileTime.from(time.toInstant()));
+                
+                time = time.minus(1,ChronoUnit.DAYS);
+            }
+
+            ls = testDir.list();
+            assertThat(ls.length,is(14));
+            assertThat(Arrays.asList(ls),Matchers.containsInAnyOrder(
+                "test-rofos-2016_04_05.log",
+                "test-rofos-2016_04_06.log",
+                "test-rofos-2016_04_07.log", 
+                "test-rofos-2016_04_08.log", 
+                "test-rofos-2016_04_09.log",
+                "test-rofos-2016_04_10.log",
+                "test-rofos-2016_04_06.log.083512300", 
+                "test-rofos-2016_04_08.log.083512300", 
+                "test-rofos-2016_04_10.log.083512300",
+                "unrelated-9",
+                "unrelated-8",
+                "unrelated-7",
+                "unrelated-6",
+                "unrelated-5"
+                ));
+
+            rofos.removeOldFiles(now);
+            ls = testDir.list();
+            assertThat(ls.length,is(10));
+            assertThat(Arrays.asList(ls),Matchers.containsInAnyOrder(
+                "test-rofos-2016_04_08.log", 
+                "test-rofos-2016_04_09.log",
+                "test-rofos-2016_04_10.log",
+                "test-rofos-2016_04_08.log.083512300", 
+                "test-rofos-2016_04_10.log.083512300",
+                "unrelated-9",
+                "unrelated-8",
+                "unrelated-7",
+                "unrelated-6",
+                "unrelated-5"));
+            
+
+            assertThat(IO.toString(new FileReader(new File(testDir,backup))),is("TICK"));
+            assertThat(IO.toString(new FileReader(new File(testDir,"test-rofos-2016_04_10.log"))),is("TOCK"));
+            
+        }
+    }
+
+    @Test
+    public void testRollover() throws Exception
+    {
+        File testDir = MavenTestingUtils.getTargetTestingDir(ResourceTest.class.getName());
+        FS.ensureEmpty(testDir);
+
+        ZoneId zone = toZoneId("Australia/Sydney");
+        ZonedDateTime now = toDateTime("2016.04.10-11:59:55.0 PM AEDT", zone);
+        
+        File template = new File(testDir,"test-rofos-yyyy_mm_dd.log");
+        
+        try (RolloverFileOutputStream rofos = 
+            new RolloverFileOutputStream(template.getAbsolutePath(),false,0,TimeZone.getTimeZone(zone),null,null,now))
+        {
+            rofos.write("BEFORE".getBytes());
+            rofos.flush();
+            String[] ls = testDir.list();
+            assertThat(ls.length,is(1));
+            assertThat(ls[0],is("test-rofos-2016_04_10.log"));
+
+            TimeUnit.SECONDS.sleep(10);
+            rofos.write("AFTER".getBytes());
+            ls = testDir.list();
+            assertThat(ls.length,is(2));
+            
+            for (String n : ls)
+            {
+                String content = IO.toString(new FileReader(new File(testDir,n)));
+                if ("test-rofos-2016_04_10.log".equals(n))
+                {
+                    assertThat(content,is("BEFORE"));
+                }
+                else
+                {
+                    assertThat(content,is("AFTER"));
+                }
+            }
+        }
+    }
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/ScannerTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/ScannerTest.java
index ecb09b4..4736215 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/ScannerTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/ScannerTest.java
@@ -50,8 +50,12 @@
     @BeforeClass
     public static void setUpBeforeClass() throws Exception
     {
-        _directory = MavenTestingUtils.getTargetTestingDir(ScannerTest.class.getSimpleName());
-        FS.ensureEmpty(_directory);
+        File testDir = MavenTestingUtils.getTargetTestingDir(ScannerTest.class.getSimpleName());
+        FS.ensureEmpty(testDir);
+        
+        // Use full path, pointing to a real directory (for FileSystems that are case-insensitive, like Windows and OSX to use)
+        // This is only needed for the various comparisons below to make sense.
+        _directory = testDir.toPath().toRealPath().toFile();
 
         _scanner = new Scanner();
         _scanner.addScanDir(_directory);
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/SharedBlockingCallbackTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/SharedBlockingCallbackTest.java
index aa24e61..77f7813 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/SharedBlockingCallbackTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/SharedBlockingCallbackTest.java
@@ -201,7 +201,7 @@
 
             blocker.succeeded();
             blocker.block();
-        };
+        }
         Assert.assertThat(System.currentTimeMillis()-start,Matchers.lessThan(600L)); 
         Assert.assertEquals(0,notComplete.get());     
     }
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java
index 0780a47..6db144f 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java
@@ -40,7 +40,6 @@
         // test basic encode/decode
         StringBuilder buf = new StringBuilder();
 
-
         buf.setLength(0);
         URIUtil.encodePath(buf,"/foo%23+;,:=/b a r/?info ");
         assertEquals("/foo%2523+%3B,:=/b%20a%20r/%3Finfo%20",buf.toString());
@@ -54,15 +53,30 @@
         buf.setLength(0);
         URIUtil.encodePath(buf,"/context/'list'/\"me\"/;<script>window.alert('xss');</script>");
         assertEquals("/context/%27list%27/%22me%22/%3B%3Cscript%3Ewindow.alert(%27xss%27)%3B%3C/script%3E", buf.toString());
+
+        buf.setLength(0);
+        URIUtil.encodePath(buf, "test\u00f6?\u00f6:\u00df");
+        assertEquals("test%C3%B6%3F%C3%B6:%C3%9F", buf.toString());
+
+        buf.setLength(0);
+        URIUtil.encodePath(buf, "test?\u00f6?\u00f6:\u00df");
+        assertEquals("test%3F%C3%B6%3F%C3%B6:%C3%9F", buf.toString());
     }
 
     /* ------------------------------------------------------------ */
     @Test
     public void testDecodePath()
     {
+        assertEquals("/foo/bar",URIUtil.decodePath("xx/foo/barxx",2,8));
+        assertEquals("/foo/bar",URIUtil.decodePath("/foo/bar"));
+        assertEquals("/f o/b r",URIUtil.decodePath("/f%20o/b%20r"));
+        assertEquals("/foo/bar",URIUtil.decodePath("/foo;ignore/bar;ignore"));
+        assertEquals("/fää/bar",URIUtil.decodePath("/f\u00e4\u00e4;ignore/bar;ignore"));
+        assertEquals("/f\u0629\u0629%23/bar",URIUtil.decodePath("/f%d8%a9%d8%a9%2523;ignore/bar;ignore"));
+        
         assertEquals("foo%23;,:=b a r",URIUtil.decodePath("foo%2523%3b%2c:%3db%20a%20r;rubbish"));
-        assertEquals("foo%23;,:=b a r=",URIUtil.decodePath("xxxfoo%2523%3b%2c:%3db%20a%20r%3Dxxx;rubbish".getBytes(),3,30));
-        assertEquals("fää%23;,:=b a r=",URIUtil.decodePath("fää%2523%3b%2c:%3db%20a%20r%3D"));
+        assertEquals("/foo/bar%23;,:=b a r=",URIUtil.decodePath("xxx/foo/bar%2523%3b%2c:%3db%20a%20r%3Dxxx;rubbish",3,35));
+        assertEquals("f\u00e4\u00e4%23;,:=b a r=", URIUtil.decodePath("fää%2523%3b%2c:%3db%20a%20r%3D"));
         assertEquals("f\u0629\u0629%23;,:=b a r",URIUtil.decodePath("f%d8%a9%d8%a9%2523%3b%2c:%3db%20a%20r"));
         
         // Test for null character (real world ugly test case)
@@ -73,7 +87,96 @@
 
     /* ------------------------------------------------------------ */
     @Test
-    public void testAddPaths()
+    public void testAddEncodedPaths()
+    {
+        assertEquals("null+null", URIUtil.addEncodedPaths(null,null),null);
+        assertEquals("null+", URIUtil.addEncodedPaths(null,""),"");
+        assertEquals("null+bbb", URIUtil.addEncodedPaths(null,"bbb"),"bbb");
+        assertEquals("null+/", URIUtil.addEncodedPaths(null,"/"),"/");
+        assertEquals("null+/bbb", URIUtil.addEncodedPaths(null,"/bbb"),"/bbb");
+
+        assertEquals("+null", URIUtil.addEncodedPaths("",null),"");
+        assertEquals("+", URIUtil.addEncodedPaths("",""),"");
+        assertEquals("+bbb", URIUtil.addEncodedPaths("","bbb"),"bbb");
+        assertEquals("+/", URIUtil.addEncodedPaths("","/"),"/");
+        assertEquals("+/bbb", URIUtil.addEncodedPaths("","/bbb"),"/bbb");
+
+        assertEquals("aaa+null", URIUtil.addEncodedPaths("aaa",null),"aaa");
+        assertEquals("aaa+", URIUtil.addEncodedPaths("aaa",""),"aaa");
+        assertEquals("aaa+bbb", URIUtil.addEncodedPaths("aaa","bbb"),"aaa/bbb");
+        assertEquals("aaa+/", URIUtil.addEncodedPaths("aaa","/"),"aaa/");
+        assertEquals("aaa+/bbb", URIUtil.addEncodedPaths("aaa","/bbb"),"aaa/bbb");
+
+        assertEquals("/+null", URIUtil.addEncodedPaths("/",null),"/");
+        assertEquals("/+", URIUtil.addEncodedPaths("/",""),"/");
+        assertEquals("/+bbb", URIUtil.addEncodedPaths("/","bbb"),"/bbb");
+        assertEquals("/+/", URIUtil.addEncodedPaths("/","/"),"/");
+        assertEquals("/+/bbb", URIUtil.addEncodedPaths("/","/bbb"),"/bbb");
+
+        assertEquals("aaa/+null", URIUtil.addEncodedPaths("aaa/",null),"aaa/");
+        assertEquals("aaa/+", URIUtil.addEncodedPaths("aaa/",""),"aaa/");
+        assertEquals("aaa/+bbb", URIUtil.addEncodedPaths("aaa/","bbb"),"aaa/bbb");
+        assertEquals("aaa/+/", URIUtil.addEncodedPaths("aaa/","/"),"aaa/");
+        assertEquals("aaa/+/bbb", URIUtil.addEncodedPaths("aaa/","/bbb"),"aaa/bbb");
+
+        assertEquals(";JS+null", URIUtil.addEncodedPaths(";JS",null),";JS");
+        assertEquals(";JS+", URIUtil.addEncodedPaths(";JS",""),";JS");
+        assertEquals(";JS+bbb", URIUtil.addEncodedPaths(";JS","bbb"),"bbb;JS");
+        assertEquals(";JS+/", URIUtil.addEncodedPaths(";JS","/"),"/;JS");
+        assertEquals(";JS+/bbb", URIUtil.addEncodedPaths(";JS","/bbb"),"/bbb;JS");
+
+        assertEquals("aaa;JS+null", URIUtil.addEncodedPaths("aaa;JS",null),"aaa;JS");
+        assertEquals("aaa;JS+", URIUtil.addEncodedPaths("aaa;JS",""),"aaa;JS");
+        assertEquals("aaa;JS+bbb", URIUtil.addEncodedPaths("aaa;JS","bbb"),"aaa/bbb;JS");
+        assertEquals("aaa;JS+/", URIUtil.addEncodedPaths("aaa;JS","/"),"aaa/;JS");
+        assertEquals("aaa;JS+/bbb", URIUtil.addEncodedPaths("aaa;JS","/bbb"),"aaa/bbb;JS");
+
+        assertEquals("aaa;JS+null", URIUtil.addEncodedPaths("aaa/;JS",null),"aaa/;JS");
+        assertEquals("aaa;JS+", URIUtil.addEncodedPaths("aaa/;JS",""),"aaa/;JS");
+        assertEquals("aaa;JS+bbb", URIUtil.addEncodedPaths("aaa/;JS","bbb"),"aaa/bbb;JS");
+        assertEquals("aaa;JS+/", URIUtil.addEncodedPaths("aaa/;JS","/"),"aaa/;JS");
+        assertEquals("aaa;JS+/bbb", URIUtil.addEncodedPaths("aaa/;JS","/bbb"),"aaa/bbb;JS");
+
+        assertEquals("?A=1+null", URIUtil.addEncodedPaths("?A=1",null),"?A=1");
+        assertEquals("?A=1+", URIUtil.addEncodedPaths("?A=1",""),"?A=1");
+        assertEquals("?A=1+bbb", URIUtil.addEncodedPaths("?A=1","bbb"),"bbb?A=1");
+        assertEquals("?A=1+/", URIUtil.addEncodedPaths("?A=1","/"),"/?A=1");
+        assertEquals("?A=1+/bbb", URIUtil.addEncodedPaths("?A=1","/bbb"),"/bbb?A=1");
+
+        assertEquals("aaa?A=1+null", URIUtil.addEncodedPaths("aaa?A=1",null),"aaa?A=1");
+        assertEquals("aaa?A=1+", URIUtil.addEncodedPaths("aaa?A=1",""),"aaa?A=1");
+        assertEquals("aaa?A=1+bbb", URIUtil.addEncodedPaths("aaa?A=1","bbb"),"aaa/bbb?A=1");
+        assertEquals("aaa?A=1+/", URIUtil.addEncodedPaths("aaa?A=1","/"),"aaa/?A=1");
+        assertEquals("aaa?A=1+/bbb", URIUtil.addEncodedPaths("aaa?A=1","/bbb"),"aaa/bbb?A=1");
+
+        assertEquals("aaa?A=1+null", URIUtil.addEncodedPaths("aaa/?A=1",null),"aaa/?A=1");
+        assertEquals("aaa?A=1+", URIUtil.addEncodedPaths("aaa/?A=1",""),"aaa/?A=1");
+        assertEquals("aaa?A=1+bbb", URIUtil.addEncodedPaths("aaa/?A=1","bbb"),"aaa/bbb?A=1");
+        assertEquals("aaa?A=1+/", URIUtil.addEncodedPaths("aaa/?A=1","/"),"aaa/?A=1");
+        assertEquals("aaa?A=1+/bbb", URIUtil.addEncodedPaths("aaa/?A=1","/bbb"),"aaa/bbb?A=1");
+
+        assertEquals(";JS?A=1+null", URIUtil.addEncodedPaths(";JS?A=1",null),";JS?A=1");
+        assertEquals(";JS?A=1+", URIUtil.addEncodedPaths(";JS?A=1",""),";JS?A=1");
+        assertEquals(";JS?A=1+bbb", URIUtil.addEncodedPaths(";JS?A=1","bbb"),"bbb;JS?A=1");
+        assertEquals(";JS?A=1+/", URIUtil.addEncodedPaths(";JS?A=1","/"),"/;JS?A=1");
+        assertEquals(";JS?A=1+/bbb", URIUtil.addEncodedPaths(";JS?A=1","/bbb"),"/bbb;JS?A=1");
+
+        assertEquals("aaa;JS?A=1+null", URIUtil.addEncodedPaths("aaa;JS?A=1",null),"aaa;JS?A=1");
+        assertEquals("aaa;JS?A=1+", URIUtil.addEncodedPaths("aaa;JS?A=1",""),"aaa;JS?A=1");
+        assertEquals("aaa;JS?A=1+bbb", URIUtil.addEncodedPaths("aaa;JS?A=1","bbb"),"aaa/bbb;JS?A=1");
+        assertEquals("aaa;JS?A=1+/", URIUtil.addEncodedPaths("aaa;JS?A=1","/"),"aaa/;JS?A=1");
+        assertEquals("aaa;JS?A=1+/bbb", URIUtil.addEncodedPaths("aaa;JS?A=1","/bbb"),"aaa/bbb;JS?A=1");
+
+        assertEquals("aaa;JS?A=1+null", URIUtil.addEncodedPaths("aaa/;JS?A=1",null),"aaa/;JS?A=1");
+        assertEquals("aaa;JS?A=1+", URIUtil.addEncodedPaths("aaa/;JS?A=1",""),"aaa/;JS?A=1");
+        assertEquals("aaa;JS?A=1+bbb", URIUtil.addEncodedPaths("aaa/;JS?A=1","bbb"),"aaa/bbb;JS?A=1");
+        assertEquals("aaa;JS?A=1+/", URIUtil.addEncodedPaths("aaa/;JS?A=1","/"),"aaa/;JS?A=1");
+        assertEquals("aaa;JS?A=1+/bbb", URIUtil.addEncodedPaths("aaa/;JS?A=1","/bbb"),"aaa/bbb;JS?A=1");
+
+    }
+    /* ------------------------------------------------------------ */
+    @Test
+    public void testAddDecodedPaths()
     {
         assertEquals("null+null", URIUtil.addPaths(null,null),null);
         assertEquals("null+", URIUtil.addPaths(null,""),"");
@@ -107,57 +210,39 @@
 
         assertEquals(";JS+null", URIUtil.addPaths(";JS",null),";JS");
         assertEquals(";JS+", URIUtil.addPaths(";JS",""),";JS");
-        assertEquals(";JS+bbb", URIUtil.addPaths(";JS","bbb"),"bbb;JS");
-        assertEquals(";JS+/", URIUtil.addPaths(";JS","/"),"/;JS");
-        assertEquals(";JS+/bbb", URIUtil.addPaths(";JS","/bbb"),"/bbb;JS");
+        assertEquals(";JS+bbb", URIUtil.addPaths(";JS","bbb"),";JS/bbb");
+        assertEquals(";JS+/", URIUtil.addPaths(";JS","/"),";JS/");
+        assertEquals(";JS+/bbb", URIUtil.addPaths(";JS","/bbb"),";JS/bbb");
 
         assertEquals("aaa;JS+null", URIUtil.addPaths("aaa;JS",null),"aaa;JS");
         assertEquals("aaa;JS+", URIUtil.addPaths("aaa;JS",""),"aaa;JS");
-        assertEquals("aaa;JS+bbb", URIUtil.addPaths("aaa;JS","bbb"),"aaa/bbb;JS");
-        assertEquals("aaa;JS+/", URIUtil.addPaths("aaa;JS","/"),"aaa/;JS");
-        assertEquals("aaa;JS+/bbb", URIUtil.addPaths("aaa;JS","/bbb"),"aaa/bbb;JS");
+        assertEquals("aaa;JS+bbb", URIUtil.addPaths("aaa;JS","bbb"),"aaa;JS/bbb");
+        assertEquals("aaa;JS+/", URIUtil.addPaths("aaa;JS","/"),"aaa;JS/");
+        assertEquals("aaa;JS+/bbb", URIUtil.addPaths("aaa;JS","/bbb"),"aaa;JS/bbb");
 
         assertEquals("aaa;JS+null", URIUtil.addPaths("aaa/;JS",null),"aaa/;JS");
         assertEquals("aaa;JS+", URIUtil.addPaths("aaa/;JS",""),"aaa/;JS");
-        assertEquals("aaa;JS+bbb", URIUtil.addPaths("aaa/;JS","bbb"),"aaa/bbb;JS");
-        assertEquals("aaa;JS+/", URIUtil.addPaths("aaa/;JS","/"),"aaa/;JS");
-        assertEquals("aaa;JS+/bbb", URIUtil.addPaths("aaa/;JS","/bbb"),"aaa/bbb;JS");
+        assertEquals("aaa;JS+bbb", URIUtil.addPaths("aaa/;JS","bbb"),"aaa/;JS/bbb");
+        assertEquals("aaa;JS+/", URIUtil.addPaths("aaa/;JS","/"),"aaa/;JS/");
+        assertEquals("aaa;JS+/bbb", URIUtil.addPaths("aaa/;JS","/bbb"),"aaa/;JS/bbb");
 
         assertEquals("?A=1+null", URIUtil.addPaths("?A=1",null),"?A=1");
         assertEquals("?A=1+", URIUtil.addPaths("?A=1",""),"?A=1");
-        assertEquals("?A=1+bbb", URIUtil.addPaths("?A=1","bbb"),"bbb?A=1");
-        assertEquals("?A=1+/", URIUtil.addPaths("?A=1","/"),"/?A=1");
-        assertEquals("?A=1+/bbb", URIUtil.addPaths("?A=1","/bbb"),"/bbb?A=1");
+        assertEquals("?A=1+bbb", URIUtil.addPaths("?A=1","bbb"),"?A=1/bbb");
+        assertEquals("?A=1+/", URIUtil.addPaths("?A=1","/"),"?A=1/");
+        assertEquals("?A=1+/bbb", URIUtil.addPaths("?A=1","/bbb"),"?A=1/bbb");
 
         assertEquals("aaa?A=1+null", URIUtil.addPaths("aaa?A=1",null),"aaa?A=1");
         assertEquals("aaa?A=1+", URIUtil.addPaths("aaa?A=1",""),"aaa?A=1");
-        assertEquals("aaa?A=1+bbb", URIUtil.addPaths("aaa?A=1","bbb"),"aaa/bbb?A=1");
-        assertEquals("aaa?A=1+/", URIUtil.addPaths("aaa?A=1","/"),"aaa/?A=1");
-        assertEquals("aaa?A=1+/bbb", URIUtil.addPaths("aaa?A=1","/bbb"),"aaa/bbb?A=1");
+        assertEquals("aaa?A=1+bbb", URIUtil.addPaths("aaa?A=1","bbb"),"aaa?A=1/bbb");
+        assertEquals("aaa?A=1+/", URIUtil.addPaths("aaa?A=1","/"),"aaa?A=1/");
+        assertEquals("aaa?A=1+/bbb", URIUtil.addPaths("aaa?A=1","/bbb"),"aaa?A=1/bbb");
 
         assertEquals("aaa?A=1+null", URIUtil.addPaths("aaa/?A=1",null),"aaa/?A=1");
         assertEquals("aaa?A=1+", URIUtil.addPaths("aaa/?A=1",""),"aaa/?A=1");
-        assertEquals("aaa?A=1+bbb", URIUtil.addPaths("aaa/?A=1","bbb"),"aaa/bbb?A=1");
-        assertEquals("aaa?A=1+/", URIUtil.addPaths("aaa/?A=1","/"),"aaa/?A=1");
-        assertEquals("aaa?A=1+/bbb", URIUtil.addPaths("aaa/?A=1","/bbb"),"aaa/bbb?A=1");
-
-        assertEquals(";JS?A=1+null", URIUtil.addPaths(";JS?A=1",null),";JS?A=1");
-        assertEquals(";JS?A=1+", URIUtil.addPaths(";JS?A=1",""),";JS?A=1");
-        assertEquals(";JS?A=1+bbb", URIUtil.addPaths(";JS?A=1","bbb"),"bbb;JS?A=1");
-        assertEquals(";JS?A=1+/", URIUtil.addPaths(";JS?A=1","/"),"/;JS?A=1");
-        assertEquals(";JS?A=1+/bbb", URIUtil.addPaths(";JS?A=1","/bbb"),"/bbb;JS?A=1");
-
-        assertEquals("aaa;JS?A=1+null", URIUtil.addPaths("aaa;JS?A=1",null),"aaa;JS?A=1");
-        assertEquals("aaa;JS?A=1+", URIUtil.addPaths("aaa;JS?A=1",""),"aaa;JS?A=1");
-        assertEquals("aaa;JS?A=1+bbb", URIUtil.addPaths("aaa;JS?A=1","bbb"),"aaa/bbb;JS?A=1");
-        assertEquals("aaa;JS?A=1+/", URIUtil.addPaths("aaa;JS?A=1","/"),"aaa/;JS?A=1");
-        assertEquals("aaa;JS?A=1+/bbb", URIUtil.addPaths("aaa;JS?A=1","/bbb"),"aaa/bbb;JS?A=1");
-
-        assertEquals("aaa;JS?A=1+null", URIUtil.addPaths("aaa/;JS?A=1",null),"aaa/;JS?A=1");
-        assertEquals("aaa;JS?A=1+", URIUtil.addPaths("aaa/;JS?A=1",""),"aaa/;JS?A=1");
-        assertEquals("aaa;JS?A=1+bbb", URIUtil.addPaths("aaa/;JS?A=1","bbb"),"aaa/bbb;JS?A=1");
-        assertEquals("aaa;JS?A=1+/", URIUtil.addPaths("aaa/;JS?A=1","/"),"aaa/;JS?A=1");
-        assertEquals("aaa;JS?A=1+/bbb", URIUtil.addPaths("aaa/;JS?A=1","/bbb"),"aaa/bbb;JS?A=1");
+        assertEquals("aaa?A=1+bbb", URIUtil.addPaths("aaa/?A=1","bbb"),"aaa/?A=1/bbb");
+        assertEquals("aaa?A=1+/", URIUtil.addPaths("aaa/?A=1","/"),"aaa/?A=1/");
+        assertEquals("aaa?A=1+/bbb", URIUtil.addPaths("aaa/?A=1","/bbb"),"aaa/?A=1/bbb");
 
     }
 
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java
index d9016a7..6cdaffe 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java
@@ -18,7 +18,9 @@
 
 package org.eclipse.jetty.util;
 
+import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 import java.io.ByteArrayInputStream;
@@ -26,17 +28,16 @@
 import java.nio.charset.StandardCharsets;
 
 import org.junit.Assert;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 
-/* ------------------------------------------------------------ */
-/** Util meta Tests.
- *
+/**
+ * URL Encoding / Decoding Tests
  */
 public class URLEncodedTest
 {
-
-    /* -------------------------------------------------------------- */
     static
     {
         /*
@@ -46,9 +47,10 @@
             System.setProperty("org.eclipse.jetty.util.UrlEncoding.charset", StringUtil.__ISO_8859_1);
          */
     }
+    
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
 
-
-    /* -------------------------------------------------------------- */
     @Test
     public void testUrlEncoded()
     {
@@ -120,82 +122,8 @@
         assertEquals("encoded param size",1, url_encoded.size());
         assertEquals("encoded encode","Name8=xx%2C++yy++%2Czz", url_encoded.encode());
         assertEquals("encoded get", url_encoded.getString("Name8"),"xx,  yy  ,zz");
-
-        url_encoded.clear();
-        url_encoded.decode("Name11=%u30EDxxVerdi+%C6+og+2zz", StandardCharsets.ISO_8859_1);
-        assertEquals("encoded param size",1, url_encoded.size());
-        assertEquals("encoded get", "?xxVerdi \u00c6 og 2zz",url_encoded.getString("Name11"));
-
-        url_encoded.clear();
-        url_encoded.decode("Name12=%u30EDxxVerdi+%2F+og+2zz", StandardCharsets.UTF_8);
-        assertEquals("encoded param size",1, url_encoded.size());
-        assertEquals("encoded get", url_encoded.getString("Name12"),"\u30edxxVerdi / og 2zz");
-
-        url_encoded.clear();
-        url_encoded.decode("Name14=%uXXXXa%GGb%+%c%+%d", StandardCharsets.ISO_8859_1);
-        assertEquals("encoded param size",1, url_encoded.size());
-        assertEquals("encoded get","?a?b?c?d", url_encoded.getString("Name14"));
-
-        url_encoded.clear();
-        url_encoded.decode("Name14=%uXXXX%GG%+%%+%", StandardCharsets.UTF_8);
-        assertEquals("encoded param size",1, url_encoded.size());
-        assertEquals("encoded get", "\ufffd\ufffd\ufffd\ufffd",url_encoded.getString("Name14"));
-
-        
-        /* Not every jvm supports this encoding */
-
-        if (java.nio.charset.Charset.isSupported("SJIS"))
-        {
-            url_encoded.clear();
-            url_encoded.decode("Name9=%u30ED%83e%83X%83g", Charset.forName("SJIS")); // "Test" in Japanese Katakana
-            assertEquals("encoded param size",1, url_encoded.size());
-            assertEquals("encoded get", "\u30ed\u30c6\u30b9\u30c8", url_encoded.getString("Name9"));   
-        }
-        else
-            assertTrue("Charset SJIS not supported by jvm", true);
     }
 
-
-    /* -------------------------------------------------------------- */
-    @Test
-    public void testBadEncoding()
-    {
-        UrlEncoded url_encoded = new UrlEncoded();
-        url_encoded.decode("Name15=xx%zzyy", StandardCharsets.UTF_8);
-        assertEquals("encoded param size",1, url_encoded.size());
-        assertEquals("encoded get", "xx\ufffdyy", url_encoded.getString("Name15"));
-
-        byte[] bad="Name=%FF%FF%FF".getBytes(StandardCharsets.UTF_8);
-        MultiMap<String> map = new MultiMap<String>();
-        UrlEncoded.decodeUtf8To(bad,0,bad.length,map);
-        assertEquals("encoded param size",1, map.size());
-        assertEquals("encoded get", "\ufffd\ufffd\ufffd", map.getString("Name"));
-        
-        url_encoded.clear();
-        url_encoded.decode("Name=%FF%FF%FF", StandardCharsets.UTF_8);
-        assertEquals("encoded param size",1, url_encoded.size());
-        assertEquals("encoded get", "\ufffd\ufffd\ufffd", url_encoded.getString("Name"));
-        
-        url_encoded.clear();
-        url_encoded.decode("Name=%EF%EF%EF", StandardCharsets.UTF_8);
-        assertEquals("encoded param size",1, url_encoded.size());
-        assertEquals("encoded get", "\ufffd\ufffd", url_encoded.getString("Name"));
-
-        assertEquals("x",UrlEncoded.decodeString("x",0,1,StandardCharsets.UTF_8));
-        assertEquals("x\ufffd",UrlEncoded.decodeString("x%",0,2,StandardCharsets.UTF_8));
-        assertEquals("x\ufffd",UrlEncoded.decodeString("x%2",0,3,StandardCharsets.UTF_8));
-        assertEquals("x ",UrlEncoded.decodeString("x%20",0,4,StandardCharsets.UTF_8));
-
-        assertEquals("xxx",UrlEncoded.decodeString("xxx",0,3,StandardCharsets.UTF_8));
-        assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%",0,4,StandardCharsets.UTF_8));
-        assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%u",0,5,StandardCharsets.UTF_8));
-        assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%u1",0,6,StandardCharsets.UTF_8));
-        assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%u12",0,7,StandardCharsets.UTF_8));
-        assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%u123",0,8,StandardCharsets.UTF_8));
-        assertEquals("xxx\u1234",UrlEncoded.decodeString("xxx%u1234",0,9,StandardCharsets.UTF_8));
-    }
-
-
     /* -------------------------------------------------------------- */
     @Test
     public void testUrlEncodedStream()
@@ -209,17 +137,18 @@
            {StringUtil.__UTF16,StringUtil.__UTF16,"%00%30"},
         };
 
+        // Note: "%30" -> decode -> "0"
 
         for (int i=0;i<charsets.length;i++)
         {
             ByteArrayInputStream in = new ByteArrayInputStream(("name\n=value+"+charsets[i][2]+"&name1=&name2&n\u00e3me3=value+3").getBytes(charsets[i][0]));
             MultiMap<String> m = new MultiMap<>();
-            UrlEncoded.decodeTo(in, m, charsets[i][1]==null?null:Charset.forName(charsets[i][1]), -1,-1);
+            UrlEncoded.decodeTo(in, m, charsets[i][1]==null?null:Charset.forName(charsets[i][1]),-1,-1);
             assertEquals(charsets[i][1]+" stream length",4,m.size());
-            assertEquals(charsets[i][1]+" stream name\\n","value 0",m.getString("name\n"));
-            assertEquals(charsets[i][1]+" stream name1","",m.getString("name1"));
-            assertEquals(charsets[i][1]+" stream name2","",m.getString("name2"));
-            assertEquals(charsets[i][1]+" stream n\u00e3me3","value 3",m.getString("n\u00e3me3"));
+            assertThat(charsets[i][1]+" stream name\\n",m.getString("name\n"),is("value 0"));
+            assertThat(charsets[i][1]+" stream name1",m.getString("name1"),is(""));
+            assertThat(charsets[i][1]+" stream name2",m.getString("name2"),is(""));
+            assertThat(charsets[i][1]+" stream n\u00e3me3",m.getString("n\u00e3me3"),is("value 3"));
         }
 
 
@@ -227,7 +156,7 @@
         {
             ByteArrayInputStream in2 = new ByteArrayInputStream("name=%83e%83X%83g".getBytes(StandardCharsets.ISO_8859_1));
             MultiMap<String> m2 = new MultiMap<>();
-            UrlEncoded.decodeTo(in2, m2, Charset.forName("Shift_JIS"), -1,-1);
+            UrlEncoded.decodeTo(in2, m2, Charset.forName("Shift_JIS"),-1,-1);
             assertEquals("stream length",1,m2.size());
             assertEquals("stream name","\u30c6\u30b9\u30c8",m2.getString("name"));
         }
@@ -270,21 +199,19 @@
         String expected = new String(TypeUtil.fromHexString(hex),"utf-8");
         Assert.assertEquals(expected,url_encoded.getString("text"));
     }
-
-    /* -------------------------------------------------------------- */
+    
     @Test
-    public void testNotUtf8() throws Exception
+    public void testUtf8_MultiByteCodePoint()
     {
-        String query="name=X%c0%afZ";
-
-        MultiMap<String> map = new MultiMap<>();
-        UrlEncoded.LOG.info("EXPECT 4 Not Valid UTF8 warnings...");
-        UrlEncoded.decodeUtf8To(query.getBytes(StandardCharsets.ISO_8859_1),0,query.length(),map);
-        assertEquals("X"+Utf8Appendable.REPLACEMENT+Utf8Appendable.REPLACEMENT+"Z",map.getValue("name",0));
-
-        map.clear();
-
-        UrlEncoded.decodeUtf8To(new ByteArrayInputStream(query.getBytes(StandardCharsets.ISO_8859_1)),map,100,2);
-        assertEquals("X"+Utf8Appendable.REPLACEMENT+Utf8Appendable.REPLACEMENT+"Z",map.getValue("name",0));
+        String input = "text=test%C3%A4";
+        UrlEncoded url_encoded = new UrlEncoded();
+        url_encoded.decode(input);
+    
+        // http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=00e4&mode=hex
+        // Should be "testä"
+        // "test" followed by a LATIN SMALL LETTER A WITH DIAERESIS
+    
+        String expected = "test\u00e4";
+        assertThat(url_encoded.getString("text"),is(expected));
     }
 }
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/UrlEncodedInvalidEncodingTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/UrlEncodedInvalidEncodingTest.java
new file mode 100644
index 0000000..2c9080f
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/UrlEncodedInvalidEncodingTest.java
@@ -0,0 +1,87 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 java.nio.charset.StandardCharsets.UTF_8;
+
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class UrlEncodedInvalidEncodingTest
+{
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+    
+    @Parameterized.Parameters(name = "{1} | {0}")
+    public static List<Object[]> data()
+    {
+        ArrayList<Object[]> data = new ArrayList<>();
+        
+        data.add(new Object[]{ "Name=xx%zzyy", UTF_8, IllegalArgumentException.class });
+        data.add(new Object[]{ "Name=%FF%FF%FF", UTF_8, Utf8Appendable.NotUtf8Exception.class });
+        data.add(new Object[]{ "Name=%EF%EF%EF", UTF_8, Utf8Appendable.NotUtf8Exception.class });
+        data.add(new Object[]{ "Name=%E%F%F", UTF_8, IllegalArgumentException.class });
+        data.add(new Object[]{ "Name=x%", UTF_8, Utf8Appendable.NotUtf8Exception.class });
+        data.add(new Object[]{ "Name=x%2", UTF_8, Utf8Appendable.NotUtf8Exception.class });
+        data.add(new Object[]{ "Name=xxx%", UTF_8, Utf8Appendable.NotUtf8Exception.class });
+        data.add(new Object[]{ "name=X%c0%afZ", UTF_8, Utf8Appendable.NotUtf8Exception.class });
+        return data;
+    }
+    
+    @Parameterized.Parameter(0)
+    public String inputString;
+    
+    @Parameterized.Parameter(1)
+    public Charset charset;
+    
+    @Parameterized.Parameter(2)
+    public Class<? extends Throwable> expectedThrowable;
+    
+    @Test
+    public void testDecode()
+    {
+        UrlEncoded url_encoded = new UrlEncoded();
+        expectedException.expect(expectedThrowable);
+        url_encoded.decode(inputString, charset);
+    }
+    
+    @Test
+    public void testDecodeUtf8ToMap()
+    {
+        MultiMap<String> map = new MultiMap<String>();
+        expectedException.expect(expectedThrowable);
+        UrlEncoded.decodeUtf8To(inputString,map);
+    }
+    
+    @Test
+    public void testDecodeTo()
+    {
+        MultiMap<String> map = new MultiMap<String>();
+        expectedException.expect(expectedThrowable);
+        UrlEncoded.decodeTo(inputString,map,charset);
+    }
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/UrlEncodedUtf8Test.java b/jetty-util/src/test/java/org/eclipse/jetty/util/UrlEncodedUtf8Test.java
index c0290a6..b60de68 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/UrlEncodedUtf8Test.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/UrlEncodedUtf8Test.java
@@ -40,7 +40,7 @@
         String test=new String(bytes,StandardCharsets.UTF_8);
         String expected = "c"+Utf8Appendable.REPLACEMENT;
 
-        fromByteArray(test,bytes,"ab",expected,false);
+        fromString(test,test,"ab",expected,false);
         fromInputStream(test,bytes,"ab",expected,false);
     }
 
@@ -51,7 +51,7 @@
         String test=new String(bytes,StandardCharsets.UTF_8);
         String expected = ""+Utf8Appendable.REPLACEMENT;
 
-        fromByteArray(test,bytes,"ab",expected,false);
+        fromString(test,test,"ab",expected,false);
         fromInputStream(test,bytes,"ab",expected,false);
         
     }
@@ -64,7 +64,7 @@
         String name = "e"+Utf8Appendable.REPLACEMENT;
         String value = "fg";
 
-        fromByteArray(test,bytes,name,value,false);
+        fromString(test,test,name,value,false);
         fromInputStream(test,bytes,name,value,false);
     }
     
@@ -76,60 +76,16 @@
         String name = "ef";
         String value = "g"+Utf8Appendable.REPLACEMENT;
 
-        fromByteArray(test,bytes,name,value,false);
+        fromString(test,test,name,value,false);
         fromInputStream(test,bytes,name,value,false);
-        
     }
 
-    @Test
-    public void testCorrectUnicode() throws Exception
-    {
-        String chars="a=%u0061";
-        byte[] bytes= chars.getBytes(StandardCharsets.UTF_8);
-        String test=new String(bytes,StandardCharsets.UTF_8);
-        String name = "a";
-        String value = "a";
-
-        fromByteArray(test,bytes,name,value,false);
-        fromInputStream(test,bytes,name,value,false);
-        
-    }
-    
-    @Test
-    public void testIncompleteUnicode() throws Exception
-    {
-        String chars="a=%u0";
-        byte[] bytes= chars.getBytes(StandardCharsets.UTF_8);
-        String test=new String(bytes,StandardCharsets.UTF_8);
-        String name = "a";
-        String value = ""+Utf8Appendable.REPLACEMENT;
-
-        fromByteArray(test,bytes,name,value,false);
-        fromInputStream(test,bytes,name,value,false);
-        
-    }
-    
-    @Test
-    public void testIncompletePercent() throws Exception
-    {
-        String chars="a=%A";
-        byte[] bytes= chars.getBytes(StandardCharsets.UTF_8);
-        String test=new String(bytes,StandardCharsets.UTF_8);
-        String name = "a";
-        String value = ""+Utf8Appendable.REPLACEMENT;
-
-        fromByteArray(test,bytes,name,value,false);
-        fromInputStream(test,bytes,name,value,false);
-        
-    }
-
-    static void fromByteArray(String test,byte[] b,String field,String expected,boolean thrown) throws Exception
+    static void fromString(String test,String s,String field,String expected,boolean thrown) throws Exception
     {
         MultiMap<String> values=new MultiMap<>();
         try
         {
-            //safeDecodeUtf8To(b, 0, b.length, values);
-            UrlEncoded.decodeUtf8To(b, 0, b.length, values);
+            UrlEncoded.decodeUtf8To(s, 0, s.length(), values);
             if (thrown)
                 Assert.fail();
             Assert.assertEquals(test, expected, values.getString(field));
@@ -148,8 +104,7 @@
         MultiMap<String> values=new MultiMap<>();
         try
         {
-            //safeDecodeUtf8To(is, values, 1000000, 10000000);
-            UrlEncoded.decodeUtf8To(is, values, 1000000, 10000000);
+            UrlEncoded.decodeUtf8To(is, values, 1000000,-1);
             if (thrown)
                 Assert.fail();
             Assert.assertEquals(test, expected, values.getString(field));
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/component/ContainerLifeCycleTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/component/ContainerLifeCycleTest.java
index 407270d..cdc610a 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/component/ContainerLifeCycleTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/component/ContainerLifeCycleTest.java
@@ -29,155 +29,108 @@
 import org.junit.Assert;
 import org.junit.Test;
 
-
 public class ContainerLifeCycleTest
 {
+    @Test
+    public void testStartStop() throws Exception
+    {
+        ContainerLifeCycle a0 = new ContainerLifeCycle();
+        TestContainerLifeCycle a1 = new TestContainerLifeCycle();
+        a0.addBean(a1);
 
+        a0.start();
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(0, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
+
+        a0.start();
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(0, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
+
+        a0.stop();
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(1, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
+
+        a0.start();
+        Assert.assertEquals(2, a1.started.get());
+        Assert.assertEquals(1, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
+
+        a0.stop();
+        Assert.assertEquals(2, a1.started.get());
+        Assert.assertEquals(2, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
+    }
 
     @Test
     public void testStartStopDestroy() throws Exception
     {
-        final AtomicInteger destroyed=new AtomicInteger();
-        final AtomicInteger started=new AtomicInteger();
-        final AtomicInteger stopped=new AtomicInteger();
+        ContainerLifeCycle a0 = new ContainerLifeCycle();
+        TestContainerLifeCycle a1 = new TestContainerLifeCycle();
 
-        ContainerLifeCycle a0=new ContainerLifeCycle();
-
-        ContainerLifeCycle a1=new ContainerLifeCycle()
-        {
-            @Override
-            protected void doStart() throws Exception
-            {
-                started.incrementAndGet();
-                super.doStart();
-            }
-
-            @Override
-            protected void doStop() throws Exception
-            {
-                stopped.incrementAndGet();
-                super.doStop();
-            }
-
-            @Override
-            public void destroy()
-            {
-                destroyed.incrementAndGet();
-                super.destroy();
-            }
-
-        };
-
+        a0.start();
+        Assert.assertEquals(0, a1.started.get());
+        Assert.assertEquals(0, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a0.addBean(a1);
-
-        a0.start();
-        Assert.assertEquals(1,started.get());
-        Assert.assertEquals(0,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
-
-        a0.start();
-        Assert.assertEquals(1,started.get());
-        Assert.assertEquals(0,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
-
-        a0.stop();
-        Assert.assertEquals(1,started.get());
-        Assert.assertEquals(1,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
-
-        a0.start();
-        Assert.assertEquals(2,started.get());
-        Assert.assertEquals(1,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
-
-        a0.stop();
-        Assert.assertEquals(2,started.get());
-        Assert.assertEquals(2,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
-
-        a0.destroy();
-
-        Assert.assertEquals(2,started.get());
-        Assert.assertEquals(2,stopped.get());
-        Assert.assertEquals(1,destroyed.get());
-
-        a0.start();
-        Assert.assertEquals(2,started.get());
-        Assert.assertEquals(2,stopped.get());
-        Assert.assertEquals(1,destroyed.get());
-
-        a0.addBean(a1);
-        Assert.assertEquals(2,started.get());
-        Assert.assertEquals(2,stopped.get());
-        Assert.assertEquals(1,destroyed.get());
+        Assert.assertEquals(0, a1.started.get());
+        Assert.assertEquals(0, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
         Assert.assertFalse(a0.isManaged(a1));
+
         a0.start();
-        Assert.assertEquals(2,started.get());
-        Assert.assertEquals(2,stopped.get());
-        Assert.assertEquals(1,destroyed.get());
+        Assert.assertEquals(0, a1.started.get());
+        Assert.assertEquals(0, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
+
         a1.start();
         a0.manage(a1);
-        Assert.assertEquals(3,started.get());
-        Assert.assertEquals(2,stopped.get());
-        Assert.assertEquals(1,destroyed.get());
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(0, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a0.removeBean(a1);
-        Assert.assertEquals(3,started.get());
-        Assert.assertEquals(3,stopped.get());
-        Assert.assertEquals(1,destroyed.get());
-        
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(1, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
+
         a0.stop();
         a0.destroy();
-        Assert.assertEquals(3,started.get());
-        Assert.assertEquals(3,stopped.get());
-        Assert.assertEquals(1,destroyed.get());
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(1, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a1.stop();
-        Assert.assertEquals(3,started.get());
-        Assert.assertEquals(3,stopped.get());
-        Assert.assertEquals(1,destroyed.get());
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(1, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a1.destroy();
-        Assert.assertEquals(3,started.get());
-        Assert.assertEquals(3,stopped.get());
-        Assert.assertEquals(2,destroyed.get());
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(1, a1.stopped.get());
+        Assert.assertEquals(1, a1.destroyed.get());
+    }
 
+    @Test(expected = IllegalStateException.class)
+    public void testIllegalToStartAfterDestroy() throws Exception
+    {
+        ContainerLifeCycle container = new ContainerLifeCycle();
+        container.start();
+        container.stop();
+        container.destroy();
+
+        // Should throw IllegalStateException.
+        container.start();
     }
 
     @Test
     public void testDisJoint() throws Exception
     {
-        final AtomicInteger destroyed=new AtomicInteger();
-        final AtomicInteger started=new AtomicInteger();
-        final AtomicInteger stopped=new AtomicInteger();
-
-        ContainerLifeCycle a0=new ContainerLifeCycle();
-
-        ContainerLifeCycle a1=new ContainerLifeCycle()
-        {
-            @Override
-            protected void doStart() throws Exception
-            {
-                started.incrementAndGet();
-                super.doStart();
-            }
-
-            @Override
-            protected void doStop() throws Exception
-            {
-                stopped.incrementAndGet();
-                super.doStop();
-            }
-
-            @Override
-            public void destroy()
-            {
-                destroyed.incrementAndGet();
-                super.destroy();
-            }
-
-        };
+        ContainerLifeCycle a0 = new ContainerLifeCycle();
+        TestContainerLifeCycle a1 = new TestContainerLifeCycle();
 
         // Start the a1 bean before adding, makes it auto disjoint
         a1.start();
@@ -187,203 +140,198 @@
         Assert.assertFalse(a0.isManaged(a1));
 
         a0.start();
-        Assert.assertEquals(1,started.get());
-        Assert.assertEquals(0,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(0, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a0.start();
-        Assert.assertEquals(1,started.get());
-        Assert.assertEquals(0,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(0, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a0.stop();
-        Assert.assertEquals(1,started.get());
-        Assert.assertEquals(0,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(0, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a1.stop();
-        Assert.assertEquals(1,started.get());
-        Assert.assertEquals(1,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(1, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a0.start();
-        Assert.assertEquals(1,started.get());
-        Assert.assertEquals(1,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(1, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a0.manage(a1);
         Assert.assertTrue(a0.isManaged(a1));
 
         a0.stop();
-        Assert.assertEquals(1,started.get());
-        Assert.assertEquals(1,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
-
+        Assert.assertEquals(1, a1.started.get());
+        Assert.assertEquals(1, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a0.start();
-        Assert.assertEquals(2,started.get());
-        Assert.assertEquals(1,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
+        Assert.assertEquals(2, a1.started.get());
+        Assert.assertEquals(1, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a0.stop();
-        Assert.assertEquals(2,started.get());
-        Assert.assertEquals(2,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
-
+        Assert.assertEquals(2, a1.started.get());
+        Assert.assertEquals(2, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a0.unmanage(a1);
         Assert.assertFalse(a0.isManaged(a1));
 
         a0.destroy();
-        Assert.assertEquals(2,started.get());
-        Assert.assertEquals(2,stopped.get());
-        Assert.assertEquals(0,destroyed.get());
+        Assert.assertEquals(2, a1.started.get());
+        Assert.assertEquals(2, a1.stopped.get());
+        Assert.assertEquals(0, a1.destroyed.get());
 
         a1.destroy();
-        Assert.assertEquals(2,started.get());
-        Assert.assertEquals(2,stopped.get());
-        Assert.assertEquals(1,destroyed.get());
-
+        Assert.assertEquals(2, a1.started.get());
+        Assert.assertEquals(2, a1.stopped.get());
+        Assert.assertEquals(1, a1.destroyed.get());
     }
 
     @Test
     public void testDumpable() throws Exception
     {
         ContainerLifeCycle a0 = new ContainerLifeCycle();
-        String dump=trim(a0.dump());
-        dump=check(dump,"org.eclipse.jetty.util.component.ContainerLifeCycl");
+        String dump = trim(a0.dump());
+        dump = check(dump, "org.eclipse.jetty.util.component.ContainerLifeCycl");
 
         ContainerLifeCycle aa0 = new ContainerLifeCycle();
         a0.addBean(aa0);
-        dump=trim(a0.dump());
-        dump=check(dump,"org.eclipse.jetty.util.component.ContainerLifeCycl");
-        dump=check(dump," +? org.eclipse.jetty.util.component.ContainerLife");
+        dump = trim(a0.dump());
+        dump = check(dump, "org.eclipse.jetty.util.component.ContainerLifeCycl");
+        dump = check(dump, " +? org.eclipse.jetty.util.component.ContainerLife");
 
         ContainerLifeCycle aa1 = new ContainerLifeCycle();
         a0.addBean(aa1);
-        dump=trim(a0.dump());
-        dump=check(dump,"org.eclipse.jetty.util.component.ContainerLifeCycl");
-        dump=check(dump," +? org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," +? org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump,"");
-        
+        dump = trim(a0.dump());
+        dump = check(dump, "org.eclipse.jetty.util.component.ContainerLifeCycl");
+        dump = check(dump, " +? org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " +? org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, "");
+
         ContainerLifeCycle aa2 = new ContainerLifeCycle();
-        a0.addBean(aa2,false);
-        dump=trim(a0.dump());
-        dump=check(dump,"org.eclipse.jetty.util.component.ContainerLifeCycl");
-        dump=check(dump," +? org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," +? org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," +~ org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump,"");
-        
+        a0.addBean(aa2, false);
+        dump = trim(a0.dump());
+        dump = check(dump, "org.eclipse.jetty.util.component.ContainerLifeCycl");
+        dump = check(dump, " +? org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " +? org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " +~ org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, "");
+
         aa1.start();
         a0.start();
-        dump=trim(a0.dump());
-        dump=check(dump,"org.eclipse.jetty.util.component.ContainerLifeCycl");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," +~ org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," +~ org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump,"");
-        
+        dump = trim(a0.dump());
+        dump = check(dump, "org.eclipse.jetty.util.component.ContainerLifeCycl");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " +~ org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " +~ org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, "");
+
         a0.manage(aa1);
         a0.removeBean(aa2);
-        dump=trim(a0.dump());
-        dump=check(dump,"org.eclipse.jetty.util.component.ContainerLifeCycl");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump,"");        
+        dump = trim(a0.dump());
+        dump = check(dump, "org.eclipse.jetty.util.component.ContainerLifeCycl");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, "");
 
         ContainerLifeCycle aaa0 = new ContainerLifeCycle();
         aa0.addBean(aaa0);
-        dump=trim(a0.dump());
-        dump=check(dump,"org.eclipse.jetty.util.component.ContainerLifeCycl");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," |   +~ org.eclipse.jetty.util.component.Container");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump,"");
+        dump = trim(a0.dump());
+        dump = check(dump, "org.eclipse.jetty.util.component.ContainerLifeCycl");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " |   +~ org.eclipse.jetty.util.component.Container");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, "");
 
         ContainerLifeCycle aa10 = new ContainerLifeCycle();
-        aa1.addBean(aa10,true);
-        dump=trim(a0.dump());
-        dump=check(dump,"org.eclipse.jetty.util.component.ContainerLifeCycl");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," |   +~ org.eclipse.jetty.util.component.Container");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump,"     += org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"");
+        aa1.addBean(aa10, true);
+        dump = trim(a0.dump());
+        dump = check(dump, "org.eclipse.jetty.util.component.ContainerLifeCycl");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " |   +~ org.eclipse.jetty.util.component.Container");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, "     += org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "");
 
         final ContainerLifeCycle a1 = new ContainerLifeCycle();
         final ContainerLifeCycle a2 = new ContainerLifeCycle();
         final ContainerLifeCycle a3 = new ContainerLifeCycle();
         final ContainerLifeCycle a4 = new ContainerLifeCycle();
 
-
         ContainerLifeCycle aa = new ContainerLifeCycle()
         {
             @Override
             public void dump(Appendable out, String indent) throws IOException
             {
                 out.append(this.toString()).append("\n");
-                dump(out,indent,TypeUtil.asList(new Object[]{a1,a2}),TypeUtil.asList(new Object[]{a3,a4}));
+                dump(out, indent, TypeUtil.asList(new Object[]{a1, a2}), TypeUtil.asList(new Object[]{a3, a4}));
             }
         };
-        a0.addBean(aa,true);
-        dump=trim(a0.dump());
-        dump=check(dump,"org.eclipse.jetty.util.component.ContainerLifeCycl");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," |   +~ org.eclipse.jetty.util.component.Container");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," |   += org.eclipse.jetty.util.component.Container");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump,"     +- org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"     +- org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"     +- org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"     +- org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"");
+        a0.addBean(aa, true);
+        dump = trim(a0.dump());
+        dump = check(dump, "org.eclipse.jetty.util.component.ContainerLifeCycl");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " |   +~ org.eclipse.jetty.util.component.Container");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " |   += org.eclipse.jetty.util.component.Container");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, "     +- org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "     +- org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "     +- org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "     +- org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "");
 
-        a2.addBean(aa0,true);
-        dump=trim(a0.dump());
-        dump=check(dump,"org.eclipse.jetty.util.component.ContainerLifeCycl");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," |   +~ org.eclipse.jetty.util.component.Container");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," |   += org.eclipse.jetty.util.component.Container");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump,"     +- org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"     +- org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"     |   += org.eclipse.jetty.util.component.Conta");
-        dump=check(dump,"     |       +~ org.eclipse.jetty.util.component.C");
-        dump=check(dump,"     +- org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"     +- org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"");
+        a2.addBean(aa0, true);
+        dump = trim(a0.dump());
+        dump = check(dump, "org.eclipse.jetty.util.component.ContainerLifeCycl");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " |   +~ org.eclipse.jetty.util.component.Container");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " |   += org.eclipse.jetty.util.component.Container");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, "     +- org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "     +- org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "     |   += org.eclipse.jetty.util.component.Conta");
+        dump = check(dump, "     |       +~ org.eclipse.jetty.util.component.C");
+        dump = check(dump, "     +- org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "     +- org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "");
 
         a2.unmanage(aa0);
-        dump=trim(a0.dump());
-        dump=check(dump,"org.eclipse.jetty.util.component.ContainerLifeCycl");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," |   +~ org.eclipse.jetty.util.component.Container");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," |   += org.eclipse.jetty.util.component.Container");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump,"     +- org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"     +- org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"     |   +~ org.eclipse.jetty.util.component.Conta");
-        dump=check(dump,"     +- org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"     +- org.eclipse.jetty.util.component.Container");
-        dump=check(dump,"");
+        dump = trim(a0.dump());
+        dump = check(dump, "org.eclipse.jetty.util.component.ContainerLifeCycl");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " |   +~ org.eclipse.jetty.util.component.Container");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " |   += org.eclipse.jetty.util.component.Container");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, "     +- org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "     +- org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "     |   +~ org.eclipse.jetty.util.component.Conta");
+        dump = check(dump, "     +- org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "     +- org.eclipse.jetty.util.component.Container");
+        dump = check(dump, "");
 
         a0.unmanage(aa);
-        dump=trim(a0.dump());
-        dump=check(dump,"org.eclipse.jetty.util.component.ContainerLifeCycl");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," |   +~ org.eclipse.jetty.util.component.Container");
-        dump=check(dump," += org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump," |   += org.eclipse.jetty.util.component.Container");
-        dump=check(dump," +~ org.eclipse.jetty.util.component.ContainerLife");
-        dump=check(dump,"");
-
+        dump = trim(a0.dump());
+        dump = check(dump, "org.eclipse.jetty.util.component.ContainerLifeCycl");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " |   +~ org.eclipse.jetty.util.component.Container");
+        dump = check(dump, " += org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, " |   += org.eclipse.jetty.util.component.Container");
+        dump = check(dump, " +~ org.eclipse.jetty.util.component.ContainerLife");
+        dump = check(dump, "");
     }
-    
+
     @Test
     public void listenerTest() throws Exception
     {
@@ -391,9 +339,9 @@
         final Queue<String> operation = new ConcurrentLinkedQueue<>();
         final Queue<Container> parent = new ConcurrentLinkedQueue<>();
         final Queue<Object> child = new ConcurrentLinkedQueue<>();
-        
-        Container.Listener listener= new Container.Listener()
-        { 
+
+        Container.Listener listener = new Container.Listener()
+        {
             @Override
             public void beanRemoved(Container p, Object c)
             {
@@ -402,7 +350,7 @@
                 parent.add(p);
                 child.add(c);
             }
-            
+
             @Override
             public void beanAdded(Container p, Object c)
             {
@@ -411,32 +359,51 @@
                 parent.add(p);
                 child.add(c);
             }
-            
-            public @Override String toString() {return "listener";}
+
+            public
+            @Override
+            String toString()
+            {
+                return "listener";
+            }
         };
-        
-        
-        ContainerLifeCycle c0 = new ContainerLifeCycle() { public @Override String toString() {return "c0";}};
-        ContainerLifeCycle c00 = new ContainerLifeCycle() { public @Override String toString() {return "c00";}};
+
+        ContainerLifeCycle c0 = new ContainerLifeCycle()
+        {
+            public
+            @Override
+            String toString()
+            {
+                return "c0";
+            }
+        };
+        ContainerLifeCycle c00 = new ContainerLifeCycle()
+        {
+            public
+            @Override
+            String toString()
+            {
+                return "c00";
+            }
+        };
         c0.addBean(c00);
-        String b000="b000";
+        String b000 = "b000";
         c00.addBean(b000);
-        
+
         c0.addBean(listener);
 
-        Assert.assertEquals("listener",handled.poll());
-        Assert.assertEquals("added",operation.poll());
-        Assert.assertEquals(c0,parent.poll());
-        Assert.assertEquals(c00,child.poll());
+        Assert.assertEquals("listener", handled.poll());
+        Assert.assertEquals("added", operation.poll());
+        Assert.assertEquals(c0, parent.poll());
+        Assert.assertEquals(c00, child.poll());
 
-        Assert.assertEquals("listener",handled.poll());
-        Assert.assertEquals("added",operation.poll());
-        Assert.assertEquals(c0,parent.poll());
-        Assert.assertEquals(listener,child.poll());
+        Assert.assertEquals("listener", handled.poll());
+        Assert.assertEquals("added", operation.poll());
+        Assert.assertEquals(c0, parent.poll());
+        Assert.assertEquals(listener, child.poll());
 
-        
-        Container.InheritedListener inherited= new Container.InheritedListener()
-        { 
+        Container.InheritedListener inherited = new Container.InheritedListener()
+        {
             @Override
             public void beanRemoved(Container p, Object c)
             {
@@ -445,7 +412,7 @@
                 parent.add(p);
                 child.add(c);
             }
-            
+
             @Override
             public void beanAdded(Container p, Object c)
             {
@@ -454,84 +421,122 @@
                 parent.add(p);
                 child.add(c);
             }
-            
-            public @Override String toString() {return "inherited";}
+
+            public
+            @Override
+            String toString()
+            {
+                return "inherited";
+            }
         };
 
         c0.addBean(inherited);
 
-        Assert.assertEquals("inherited",handled.poll());
-        Assert.assertEquals("added",operation.poll());
-        Assert.assertEquals(c0,parent.poll());
-        Assert.assertEquals(c00,child.poll());
+        Assert.assertEquals("inherited", handled.poll());
+        Assert.assertEquals("added", operation.poll());
+        Assert.assertEquals(c0, parent.poll());
+        Assert.assertEquals(c00, child.poll());
 
-        Assert.assertEquals("inherited",handled.poll());
-        Assert.assertEquals("added",operation.poll());
-        Assert.assertEquals(c0,parent.poll());
-        Assert.assertEquals(listener,child.poll());
+        Assert.assertEquals("inherited", handled.poll());
+        Assert.assertEquals("added", operation.poll());
+        Assert.assertEquals(c0, parent.poll());
+        Assert.assertEquals(listener, child.poll());
 
-        Assert.assertEquals("listener",handled.poll());
-        Assert.assertEquals("added",operation.poll());
-        Assert.assertEquals(c0,parent.poll());
-        Assert.assertEquals(inherited,child.poll());
+        Assert.assertEquals("listener", handled.poll());
+        Assert.assertEquals("added", operation.poll());
+        Assert.assertEquals(c0, parent.poll());
+        Assert.assertEquals(inherited, child.poll());
 
-        Assert.assertEquals("inherited",handled.poll());
-        Assert.assertEquals("added",operation.poll());
-        Assert.assertEquals(c0,parent.poll());
-        Assert.assertEquals(inherited,child.poll());
-        
+        Assert.assertEquals("inherited", handled.poll());
+        Assert.assertEquals("added", operation.poll());
+        Assert.assertEquals(c0, parent.poll());
+        Assert.assertEquals(inherited, child.poll());
+
         c0.start();
-        
-        Assert.assertEquals("inherited",handled.poll());
-        Assert.assertEquals("added",operation.poll());
-        Assert.assertEquals(c00,parent.poll());
-        Assert.assertEquals(b000,child.poll());
-        
-        Assert.assertEquals("inherited",handled.poll());
-        Assert.assertEquals("added",operation.poll());
-        Assert.assertEquals(c00,parent.poll());
-        Assert.assertEquals(inherited,child.poll());
-        
+
+        Assert.assertEquals("inherited", handled.poll());
+        Assert.assertEquals("added", operation.poll());
+        Assert.assertEquals(c00, parent.poll());
+        Assert.assertEquals(b000, child.poll());
+
+        Assert.assertEquals("inherited", handled.poll());
+        Assert.assertEquals("added", operation.poll());
+        Assert.assertEquals(c00, parent.poll());
+        Assert.assertEquals(inherited, child.poll());
+
         c0.removeBean(c00);
-        
-        Assert.assertEquals("inherited",handled.poll());
-        Assert.assertEquals("removed",operation.poll());
-        Assert.assertEquals(c00,parent.poll());
-        Assert.assertEquals(inherited,child.poll());
-        
-        Assert.assertEquals("inherited",handled.poll());
-        Assert.assertEquals("removed",operation.poll());
-        Assert.assertEquals(c00,parent.poll());
-        Assert.assertEquals(b000,child.poll());
 
-        Assert.assertEquals("listener",handled.poll());
-        Assert.assertEquals("removed",operation.poll());
-        Assert.assertEquals(c0,parent.poll());
-        Assert.assertEquals(c00,child.poll());
+        Assert.assertEquals("inherited", handled.poll());
+        Assert.assertEquals("removed", operation.poll());
+        Assert.assertEquals(c00, parent.poll());
+        Assert.assertEquals(inherited, child.poll());
 
-        Assert.assertEquals("inherited",handled.poll());
-        Assert.assertEquals("removed",operation.poll());
-        Assert.assertEquals(c0,parent.poll());
-        Assert.assertEquals(c00,child.poll());
-        
+        Assert.assertEquals("inherited", handled.poll());
+        Assert.assertEquals("removed", operation.poll());
+        Assert.assertEquals(c00, parent.poll());
+        Assert.assertEquals(b000, child.poll());
+
+        Assert.assertEquals("listener", handled.poll());
+        Assert.assertEquals("removed", operation.poll());
+        Assert.assertEquals(c0, parent.poll());
+        Assert.assertEquals(c00, child.poll());
+
+        Assert.assertEquals("inherited", handled.poll());
+        Assert.assertEquals("removed", operation.poll());
+        Assert.assertEquals(c0, parent.poll());
+        Assert.assertEquals(c00, child.poll());
     }
 
     private final class InheritedListenerLifeCycle extends AbstractLifeCycle implements Container.InheritedListener
     {
-        public @Override void beanRemoved(Container p, Object c){}
+        @Override
+        public void beanRemoved(Container p, Object c)
+        {
+        }
 
-        public @Override void beanAdded(Container p, Object c) {}
+        @Override
+        public void beanAdded(Container p, Object c)
+        {
+        }
 
-        public @Override String toString() {return "inherited";}
+        @Override
+        public String toString()
+        {
+            return "inherited";
+        }
     }
-    
+
     @Test
     public void testInheritedListener() throws Exception
     {
-        ContainerLifeCycle c0 = new ContainerLifeCycle() { public @Override String toString() {return "c0";}};
-        ContainerLifeCycle c00 = new ContainerLifeCycle() { public @Override String toString() {return "c00";}};
-        ContainerLifeCycle c01 = new ContainerLifeCycle() { public @Override String toString() {return "c01";}};
-        Container.InheritedListener inherited= new InheritedListenerLifeCycle();
+        ContainerLifeCycle c0 = new ContainerLifeCycle()
+        {
+            public
+            @Override
+            String toString()
+            {
+                return "c0";
+            }
+        };
+        ContainerLifeCycle c00 = new ContainerLifeCycle()
+        {
+            public
+            @Override
+            String toString()
+            {
+                return "c00";
+            }
+        };
+        ContainerLifeCycle c01 = new ContainerLifeCycle()
+        {
+            public
+            @Override
+            String toString()
+            {
+                return "c01";
+            }
+        };
+        Container.InheritedListener inherited = new InheritedListenerLifeCycle();
 
         c0.addBean(c00);
         c0.start();
@@ -548,34 +553,59 @@
 
     String trim(String s) throws IOException
     {
-        StringBuilder b=new StringBuilder();
-        BufferedReader reader=new BufferedReader(new StringReader(s));
+        StringBuilder b = new StringBuilder();
+        BufferedReader reader = new BufferedReader(new StringReader(s));
 
-        for (String line=reader.readLine();line!=null;line=reader.readLine())
+        for (String line = reader.readLine(); line != null; line = reader.readLine())
         {
-            if (line.length()>50)
-                line=line.substring(0,50);
+            if (line.length() > 50)
+                line = line.substring(0, 50);
             b.append(line).append('\n');
         }
 
         return b.toString();
     }
 
-    String check(String s,String x)
+    String check(String s, String x)
     {
-        String r=s;
+        String r = s;
         int nl = s.indexOf('\n');
-        if (nl>0)
+        if (nl > 0)
         {
-            r=s.substring(nl+1);
-            s=s.substring(0,nl);
+            r = s.substring(nl + 1);
+            s = s.substring(0, nl);
         }
 
-        Assert.assertEquals(x,s);
+        Assert.assertEquals(x, s);
 
         return r;
     }
 
+    private static class TestContainerLifeCycle extends ContainerLifeCycle
+    {
+        private final AtomicInteger destroyed = new AtomicInteger();
+        private final AtomicInteger started = new AtomicInteger();
+        private final AtomicInteger stopped = new AtomicInteger();
 
+        @Override
+        protected void doStart() throws Exception
+        {
+            started.incrementAndGet();
+            super.doStart();
+        }
 
+        @Override
+        protected void doStop() throws Exception
+        {
+            stopped.incrementAndGet();
+            super.doStop();
+        }
+
+        @Override
+        public void destroy()
+        {
+            destroyed.incrementAndGet();
+            super.destroy();
+        }
+    }
 }
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerNestedTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerNestedTest.java
index 4aa008a..2b6a741 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerNestedTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerNestedTest.java
@@ -18,21 +18,27 @@
 
 package org.eclipse.jetty.util.component;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
 
 import java.util.ArrayList;
 import java.util.List;
 
 import org.hamcrest.Matcher;
+import org.junit.Ignore;
 import org.junit.Test;
 
 /**
  * Testing for LifeCycleListener events on nested components
  * during runtime.
  */
+@Ignore
 public class LifeCycleListenerNestedTest
 {
+    // Set this true to use test-specific workaround.
+    private final boolean WORKAROUND = false;
+    
     public static class Foo extends ContainerLifeCycle
     {
         @Override
@@ -153,18 +159,18 @@
         @Override
         public void beanAdded(Container parent, Object child)
         {
-            if(child instanceof ContainerLifeCycle)
+            if(child instanceof LifeCycle)
             {
-                ((ContainerLifeCycle)child).addLifeCycleListener(this);
+                ((LifeCycle)child).addLifeCycleListener(this);
             }
         }
 
         @Override
         public void beanRemoved(Container parent, Object child)
         {
-            if(child instanceof ContainerLifeCycle)
+            if(child instanceof LifeCycle)
             {
-                ((ContainerLifeCycle)child).removeLifeCycleListener(this);
+                ((LifeCycle)child).removeLifeCycleListener(this);
             }
         }
     }
@@ -180,7 +186,8 @@
 
         CapturingListener listener = new CapturingListener();
         foo.addLifeCycleListener(listener);
-        foo.addEventListener(listener);
+        if(WORKAROUND)
+            foo.addEventListener(listener);
 
         try
         {
@@ -210,7 +217,8 @@
 
         CapturingListener listener = new CapturingListener();
         foo.addLifeCycleListener(listener);
-        foo.addEventListener(listener);
+        if(WORKAROUND)
+            foo.addEventListener(listener);
 
         Bar bara = new Bar("a");
         Bar barb = new Bar("b");
@@ -247,7 +255,8 @@
 
         CapturingListener listener = new CapturingListener();
         foo.addLifeCycleListener(listener);
-        foo.addEventListener(listener);
+        if(WORKAROUND)
+            foo.addEventListener(listener);
 
         try
         {
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerTest.java
index c494f3c..1d61bf2 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerTest.java
@@ -22,8 +22,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.junit.Test;
 
 
@@ -41,9 +40,8 @@
 
         lifecycle.setCause(cause);
 
-        try
+        try (StacklessLogging stackless = new StacklessLogging(AbstractLifeCycle.class))
         {
-            StdErrLog.getLogger(AbstractLifeCycle.class).setHideStacks(true);
             lifecycle.start();
             assertTrue(false);
         }
@@ -52,10 +50,6 @@
             assertEquals(cause,e);
             assertEquals(cause,listener.getCause());
         }
-        finally
-        {
-            StdErrLog.getLogger(AbstractLifeCycle.class).setHideStacks(false);
-        }
         lifecycle.setCause(null);
 
         lifecycle.start();
@@ -88,9 +82,8 @@
         lifecycle.start();
         lifecycle.setCause(cause);
 
-        try
+        try (StacklessLogging stackless = new StacklessLogging(AbstractLifeCycle.class))
         {
-            ((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
             lifecycle.stop();
             assertTrue(false);
         }
@@ -99,10 +92,6 @@
             assertEquals(cause,e);
             assertEquals(cause,listener.getCause());
         }
-        finally
-        {
-            ((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(false);
-        }
 
         lifecycle.setCause(null);
 
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java
index 82e38ce..ad18fc4 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java
@@ -39,6 +39,11 @@
  */
 public class StdErrLogTest
 {
+    static
+    {
+        StdErrLog.setTagPad(0);
+    }
+    
     @Before
     public void before()
     {
@@ -156,40 +161,42 @@
 
     /**
      * Test to make sure that using a Null parameter on parameterized messages does not result in a NPE
+     * @throws Exception failed test
      */
     @Test
-    public void testParameterizedMessage_NullValues() throws NullPointerException
+    public void testParameterizedMessage_NullValues() throws Exception
     {
         StdErrLog log = new StdErrLog(StdErrLogTest.class.getName(),new Properties());
         log.setLevel(StdErrLog.LEVEL_DEBUG);
-        log.setHideStacks(true);
+        try (StacklessLogging stackless = new StacklessLogging(log))
+        {
+            log.info("Testing info(msg,null,null) - {} {}","arg0","arg1");
+            log.info("Testing info(msg,null,null) - {} {}",null,null);
+            log.info("Testing info(msg,null,null) - {}",null,null);
+            log.info("Testing info(msg,null,null)",null,null);
+            log.info(null,"Testing","info(null,arg0,arg1)");
+            log.info(null,null,null);
 
-        log.info("Testing info(msg,null,null) - {} {}","arg0","arg1");
-        log.info("Testing info(msg,null,null) - {} {}",null,null);
-        log.info("Testing info(msg,null,null) - {}",null,null);
-        log.info("Testing info(msg,null,null)",null,null);
-        log.info(null,"Testing","info(null,arg0,arg1)");
-        log.info(null,null,null);
+            log.debug("Testing debug(msg,null,null) - {} {}","arg0","arg1");
+            log.debug("Testing debug(msg,null,null) - {} {}",null,null);
+            log.debug("Testing debug(msg,null,null) - {}",null,null);
+            log.debug("Testing debug(msg,null,null)",null,null);
+            log.debug(null,"Testing","debug(null,arg0,arg1)");
+            log.debug(null,null,null);
 
-        log.debug("Testing debug(msg,null,null) - {} {}","arg0","arg1");
-        log.debug("Testing debug(msg,null,null) - {} {}",null,null);
-        log.debug("Testing debug(msg,null,null) - {}",null,null);
-        log.debug("Testing debug(msg,null,null)",null,null);
-        log.debug(null,"Testing","debug(null,arg0,arg1)");
-        log.debug(null,null,null);
+            log.debug("Testing debug(msg,null)");
+            log.debug(null,new Throwable("Testing debug(null,thrw)").fillInStackTrace());
 
-        log.debug("Testing debug(msg,null)");
-        log.debug(null,new Throwable("Testing debug(null,thrw)").fillInStackTrace());
+            log.warn("Testing warn(msg,null,null) - {} {}","arg0","arg1");
+            log.warn("Testing warn(msg,null,null) - {} {}",null,null);
+            log.warn("Testing warn(msg,null,null) - {}",null,null);
+            log.warn("Testing warn(msg,null,null)",null,null);
+            log.warn(null,"Testing","warn(msg,arg0,arg1)");
+            log.warn(null,null,null);
 
-        log.warn("Testing warn(msg,null,null) - {} {}","arg0","arg1");
-        log.warn("Testing warn(msg,null,null) - {} {}",null,null);
-        log.warn("Testing warn(msg,null,null) - {}",null,null);
-        log.warn("Testing warn(msg,null,null)",null,null);
-        log.warn(null,"Testing","warn(msg,arg0,arg1)");
-        log.warn(null,null,null);
-
-        log.warn("Testing warn(msg,null)");
-        log.warn(null,new Throwable("Testing warn(msg,thrw)").fillInStackTrace());
+            log.warn("Testing warn(msg,null)");
+            log.warn(null,new Throwable("Testing warn(msg,thrw)").fillInStackTrace());
+        }
     }
 
     @Test
@@ -302,233 +309,247 @@
      * Tests StdErrLog.warn() methods with level filtering.
      * <p>
      * Should always see WARN level messages, regardless of set level.
+     * @throws UnsupportedEncodingException failed test
      */
     @Test
     public void testWarnFiltering() throws UnsupportedEncodingException
     {
         StdErrLog log = new StdErrLog(StdErrLogTest.class.getName(),new Properties());
-        log.setHideStacks(false);
+        try (StacklessLogging stackless = new StacklessLogging(log))
+        {
+            StdErrCapture output = new StdErrCapture(log);
 
-        StdErrCapture output = new StdErrCapture(log);
+            // Start with default level
+            log.warn("See Me");
 
-        // Start with default level
-        log.warn("See Me");
+            // Set to debug level
+            log.setLevel(StdErrLog.LEVEL_DEBUG);
+            log.warn("Hear Me");
 
-        // Set to debug level
-        log.setLevel(StdErrLog.LEVEL_DEBUG);
-        log.warn("Hear Me");
+            // Set to warn level
+            log.setLevel(StdErrLog.LEVEL_WARN);
+            log.warn("Cheer Me");
 
-        // Set to warn level
-        log.setLevel(StdErrLog.LEVEL_WARN);
-        log.warn("Cheer Me");
+            log.warn("<zoom>", new Throwable("out of focus"));
+            log.warn(new Throwable("scene lost"));
 
-        log.warn("<zoom>", new Throwable("out of focus"));
-        log.warn(new Throwable("scene lost"));
+            // Validate Output
+            // System.err.print(output);
+            output.assertContains("See Me");
+            output.assertContains("Hear Me");
+            output.assertContains("Cheer Me");
 
-        // Validate Output
-        // System.err.print(output);
-        output.assertContains("See Me");
-        output.assertContains("Hear Me");
-        output.assertContains("Cheer Me");
-
-        // Validate Stack Traces
-        output.assertContains(".StdErrLogTest:tname: <zoom>");
-        output.assertContains("java.lang.Throwable: out of focus");
-        output.assertContains("java.lang.Throwable: scene lost");
+            // Validate Stack Traces
+            output.assertContains(".StdErrLogTest:tname: <zoom>");
+            output.assertContains("java.lang.Throwable: out of focus");
+            output.assertContains("java.lang.Throwable: scene lost");
+        }
     }
 
     /**
      * Tests StdErrLog.info() methods with level filtering.
      * <p>
      * Should only see INFO level messages when level is set to {@link StdErrLog#LEVEL_INFO} and below.
+     * @throws Exception failed test
      */
     @Test
-    public void testInfoFiltering() throws UnsupportedEncodingException
+    public void testInfoFiltering() throws Exception
     {
         StdErrLog log = new StdErrLog(StdErrLogTest.class.getName(),new Properties());
-        log.setHideStacks(false);
+        try (StacklessLogging stackless = new StacklessLogging(log))
+        {
+            StdErrCapture output = new StdErrCapture(log);
 
-        StdErrCapture output = new StdErrCapture(log);
+            // Normal/Default behavior
+            log.info("I will not buy");
 
-        // Normal/Default behavior
-        log.info("I will not buy");
+            // Level Debug
+            log.setLevel(StdErrLog.LEVEL_DEBUG);
+            log.info("this record");
 
-        // Level Debug
-        log.setLevel(StdErrLog.LEVEL_DEBUG);
-        log.info("this record");
+            // Level All
+            log.setLevel(StdErrLog.LEVEL_ALL);
+            log.info("it is scratched.");
 
-        // Level All
-        log.setLevel(StdErrLog.LEVEL_ALL);
-        log.info("it is scratched.");
+            log.info("<zoom>", new Throwable("out of focus"));
+            log.info(new Throwable("scene lost"));
 
-        log.info("<zoom>", new Throwable("out of focus"));
-        log.info(new Throwable("scene lost"));
+            // Level Warn
+            log.setLevel(StdErrLog.LEVEL_WARN);
+            log.info("sorry?");
+            log.info("<spoken line>", new Throwable("on editing room floor"));
 
-        // Level Warn
-        log.setLevel(StdErrLog.LEVEL_WARN);
-        log.info("sorry?");
-        log.info("<spoken line>", new Throwable("on editing room floor"));
+            // Validate Output
+            output.assertContains("I will not buy");
+            output.assertContains("this record");
+            output.assertContains("it is scratched.");
+            output.assertNotContains("sorry?");
 
-        // Validate Output
-        output.assertContains("I will not buy");
-        output.assertContains("this record");
-        output.assertContains("it is scratched.");
-        output.assertNotContains("sorry?");
+            // Validate Stack Traces
+            output.assertNotContains("<spoken line>");
+            output.assertNotContains("on editing room floor");
 
-        // Validate Stack Traces
-        output.assertNotContains("<spoken line>");
-        output.assertNotContains("on editing room floor");
-
-        output.assertContains(".StdErrLogTest:tname: <zoom>");
-        output.assertContains("java.lang.Throwable: out of focus");
-        output.assertContains("java.lang.Throwable: scene lost");
+            output.assertContains(".StdErrLogTest:tname: <zoom>");
+            output.assertContains("java.lang.Throwable: out of focus");
+            output.assertContains("java.lang.Throwable: scene lost");
+        }
     }
 
     /**
      * Tests {@link StdErrLog#LEVEL_OFF} filtering.
+     * @throws Exception failed test
      */
     @Test
-    public void testOffFiltering() throws UnsupportedEncodingException
+    public void testOffFiltering() throws Exception
     {
         StdErrLog log = new StdErrLog(StdErrLogTest.class.getName(),new Properties());
-        log.setHideStacks(false);
-        log.setLevel(StdErrLog.LEVEL_OFF);
+        try (StacklessLogging stackless = new StacklessLogging(log))
+        {
+            log.setLevel(StdErrLog.LEVEL_OFF);
 
-        StdErrCapture output = new StdErrCapture(log);
+            StdErrCapture output = new StdErrCapture(log);
 
-        // Various logging events
-        log.debug("Squelch");
-        log.debug("Squelch", new RuntimeException("Squelch"));
-        log.info("Squelch");
-        log.info("Squelch", new IllegalStateException("Squelch"));
-        log.warn("Squelch");
-        log.warn("Squelch", new Exception("Squelch"));
-        log.ignore(new Throwable("Squelch"));
+            // Various logging events
+            log.debug("Squelch");
+            log.debug("Squelch", new RuntimeException("Squelch"));
+            log.info("Squelch");
+            log.info("Squelch", new IllegalStateException("Squelch"));
+            log.warn("Squelch");
+            log.warn("Squelch", new Exception("Squelch"));
+            log.ignore(new Throwable("Squelch"));
 
-        // Validate Output
-        output.assertNotContains("Squelch");
+            // Validate Output
+            output.assertNotContains("Squelch");
+        }
     }
 
     /**
      * Tests StdErrLog.debug() methods with level filtering.
      * <p>
      * Should only see DEBUG level messages when level is set to {@link StdErrLog#LEVEL_DEBUG} and below.
+     * @throws Exception failed test
      */
     @Test
-    public void testDebugFiltering() throws UnsupportedEncodingException
+    public void testDebugFiltering() throws Exception
     {
         StdErrLog log = new StdErrLog(StdErrLogTest.class.getName(),new Properties());
-        log.setHideStacks(true);
+        try(StacklessLogging stackless = new StacklessLogging(log))
+        {
+            StdErrCapture output = new StdErrCapture(log);
 
-        StdErrCapture output = new StdErrCapture(log);
+            // Normal/Default behavior
+            log.debug("Tobacconist");
+            log.debug("<spoken line>", new Throwable("on editing room floor"));
 
-        // Normal/Default behavior
-        log.debug("Tobacconist");
-        log.debug("<spoken line>", new Throwable("on editing room floor"));
+            // Level Debug
+            log.setLevel(StdErrLog.LEVEL_DEBUG);
+            log.debug("my hovercraft is");
 
-        // Level Debug
-        log.setLevel(StdErrLog.LEVEL_DEBUG);
-        log.debug("my hovercraft is");
+            log.debug("<zoom>", new Throwable("out of focus"));
+            log.debug(new Throwable("scene lost"));
 
-        log.debug("<zoom>", new Throwable("out of focus"));
-        log.debug(new Throwable("scene lost"));
+            // Level All
+            log.setLevel(StdErrLog.LEVEL_ALL);
+            log.debug("full of eels.");
 
-        // Level All
-        log.setLevel(StdErrLog.LEVEL_ALL);
-        log.debug("full of eels.");
+            // Level Warn
+            log.setLevel(StdErrLog.LEVEL_WARN);
+            log.debug("what?");
 
-        // Level Warn
-        log.setLevel(StdErrLog.LEVEL_WARN);
-        log.debug("what?");
+            // Validate Output
+            // System.err.print(output);
+            output.assertNotContains("Tobacconist");
+            output.assertContains("my hovercraft is");
+            output.assertContains("full of eels.");
+            output.assertNotContains("what?");
 
-        // Validate Output
-        // System.err.print(output);
-        output.assertNotContains("Tobacconist");
-        output.assertContains("my hovercraft is");
-        output.assertContains("full of eels.");
-        output.assertNotContains("what?");
+            // Validate Stack Traces
+            output.assertNotContains("<spoken line>");
+            output.assertNotContains("on editing room floor");
 
-        // Validate Stack Traces
-        output.assertNotContains("<spoken line>");
-        output.assertNotContains("on editing room floor");
-
-        output.assertContains(".StdErrLogTest:tname: <zoom>");
-        output.assertContains("java.lang.Throwable: out of focus");
-        output.assertContains("java.lang.Throwable: scene lost");
+            output.assertContains(".StdErrLogTest:tname: <zoom>");
+            output.assertContains("java.lang.Throwable: out of focus");
+            output.assertContains("java.lang.Throwable: scene lost");
+        }
     }
 
     /**
      * Tests StdErrLog with {@link Logger#ignore(Throwable)} use.
      * <p>
      * Should only see IGNORED level messages when level is set to {@link StdErrLog#LEVEL_ALL}.
+     * @throws Exception failed test
      */
     @Test
-    public void testIgnores() throws UnsupportedEncodingException
+    public void testIgnores() throws Exception
     {
         StdErrLog log = new StdErrLog(StdErrLogTest.class.getName(),new Properties());
-        log.setHideStacks(true);
+        try (StacklessLogging stackless = new StacklessLogging(log))
+        {
+            StdErrCapture output = new StdErrCapture(log);
 
-        StdErrCapture output = new StdErrCapture(log);
+            // Normal/Default behavior
+            log.ignore(new Throwable("IGNORE ME"));
 
-        // Normal/Default behavior
-        log.ignore(new Throwable("IGNORE ME"));
+            // Show Ignored
+            log.setLevel(StdErrLog.LEVEL_ALL);
+            log.ignore(new Throwable("Don't ignore me"));
 
-        // Show Ignored
-        log.setLevel(StdErrLog.LEVEL_ALL);
-        log.ignore(new Throwable("Don't ignore me"));
+            // Set to Debug level
+            log.setLevel(StdErrLog.LEVEL_DEBUG);
+            log.ignore(new Throwable("Debug me"));
 
-        // Set to Debug level
-        log.setLevel(StdErrLog.LEVEL_DEBUG);
-        log.ignore(new Throwable("Debug me"));
-
-        // Validate Output
-        // System.err.print(output);
-        output.assertNotContains("IGNORE ME");
-        output.assertContains("Don't ignore me");
-        output.assertNotContains("Debug me");
+            // Validate Output
+            // System.err.print(output);
+            output.assertNotContains("IGNORE ME");
+            output.assertContains("Don't ignore me");
+            output.assertNotContains("Debug me");
+        }
     }
 
     @Test
-    public void testIsDebugEnabled() {
+    public void testIsDebugEnabled() throws Exception
+    {
         StdErrLog log = new StdErrLog(StdErrLogTest.class.getName(),new Properties());
-        log.setHideStacks(true);
+        try (StacklessLogging stackless = new StacklessLogging(log))
+        {
+            log.setLevel(StdErrLog.LEVEL_ALL);
+            Assert.assertThat("log.level(all).isDebugEnabled", log.isDebugEnabled(), is(true));
 
-        log.setLevel(StdErrLog.LEVEL_ALL);
-        Assert.assertThat("log.level(all).isDebugEnabled", log.isDebugEnabled(), is(true));
+            log.setLevel(StdErrLog.LEVEL_DEBUG);
+            Assert.assertThat("log.level(debug).isDebugEnabled", log.isDebugEnabled(), is(true));
 
-        log.setLevel(StdErrLog.LEVEL_DEBUG);
-        Assert.assertThat("log.level(debug).isDebugEnabled", log.isDebugEnabled(), is(true));
+            log.setLevel(StdErrLog.LEVEL_INFO);
+            Assert.assertThat("log.level(info).isDebugEnabled", log.isDebugEnabled(), is(false));
 
-        log.setLevel(StdErrLog.LEVEL_INFO);
-        Assert.assertThat("log.level(info).isDebugEnabled", log.isDebugEnabled(), is(false));
+            log.setLevel(StdErrLog.LEVEL_WARN);
+            Assert.assertThat("log.level(warn).isDebugEnabled", log.isDebugEnabled(), is(false));
 
-        log.setLevel(StdErrLog.LEVEL_WARN);
-        Assert.assertThat("log.level(warn).isDebugEnabled", log.isDebugEnabled(), is(false));
-
-        log.setLevel(StdErrLog.LEVEL_OFF);
-        Assert.assertThat("log.level(off).isDebugEnabled", log.isDebugEnabled(), is(false));
+            log.setLevel(StdErrLog.LEVEL_OFF);
+            Assert.assertThat("log.level(off).isDebugEnabled", log.isDebugEnabled(), is(false));
+        }
     }
 
     @Test
     public void testSetGetLevel()
     {
         StdErrLog log = new StdErrLog(StdErrLogTest.class.getName(),new Properties());
-        log.setHideStacks(true);
+        try (StacklessLogging stackless = new StacklessLogging(log))
+        {
+            log.setLevel(StdErrLog.LEVEL_ALL);
+            Assert.assertThat("log.level(all).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_ALL));
 
-        log.setLevel(StdErrLog.LEVEL_ALL);
-        Assert.assertThat("log.level(all).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_ALL));
+            log.setLevel(StdErrLog.LEVEL_DEBUG);
+            Assert.assertThat("log.level(debug).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_DEBUG));
 
-        log.setLevel(StdErrLog.LEVEL_DEBUG);
-        Assert.assertThat("log.level(debug).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_DEBUG));
+            log.setLevel(StdErrLog.LEVEL_INFO);
+            Assert.assertThat("log.level(info).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_INFO));
 
-        log.setLevel(StdErrLog.LEVEL_INFO);
-        Assert.assertThat("log.level(info).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_INFO));
+            log.setLevel(StdErrLog.LEVEL_WARN);
+            Assert.assertThat("log.level(warn).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_WARN));
 
-        log.setLevel(StdErrLog.LEVEL_WARN);
-        Assert.assertThat("log.level(warn).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_WARN));
-
-        log.setLevel(StdErrLog.LEVEL_OFF);
-        Assert.assertThat("log.level(off).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_OFF));
+            log.setLevel(StdErrLog.LEVEL_OFF);
+            Assert.assertThat("log.level(off).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_OFF));
+        }
     }
 
     @Test
@@ -536,12 +557,13 @@
     {
         String baseName = "jetty";
         StdErrLog log = new StdErrLog(baseName,new Properties());
-        log.setHideStacks(true);
+        try (StacklessLogging stackless = new StacklessLogging(log))
+        {
+            Assert.assertThat("Logger.name", log.getName(), is("jetty"));
 
-        Assert.assertThat("Logger.name", log.getName(), is("jetty"));
-
-        Logger log2 = log.getLogger("child");
-        Assert.assertThat("Logger.child.name", log2.getName(), is("jetty.child"));
+            Logger log2 = log.getLogger("child");
+            Assert.assertThat("Logger.child.name", log2.getName(), is("jetty.child"));
+        }
     }
 
     @Test
@@ -549,12 +571,13 @@
     {
         String baseName = "jetty";
         StdErrLog log = new StdErrLog(baseName,new Properties());
-        log.setHideStacks(true);
+        try (StacklessLogging stackless = new StacklessLogging(log))
+        {
+            Assert.assertThat("Logger.name", log.getName(), is("jetty"));
 
-        Assert.assertThat("Logger.name", log.getName(), is("jetty"));
-
-        Logger log2 = log.getLogger("child.of.the.sixties");
-        Assert.assertThat("Logger.child.name", log2.getName(), is("jetty.child.of.the.sixties"));
+            Logger log2 = log.getLogger("child.of.the.sixties");
+            Assert.assertThat("Logger.child.name", log2.getName(), is("jetty.child.of.the.sixties"));
+        }
     }
 
     @Test
@@ -562,14 +585,15 @@
     {
         String baseName = "jetty";
         StdErrLog log = new StdErrLog(baseName,new Properties());
-        log.setHideStacks(true);
+        try (StacklessLogging stackless = new StacklessLogging(log))
+        {
+            Assert.assertThat("Logger.name", log.getName(), is("jetty"));
 
-        Assert.assertThat("Logger.name", log.getName(), is("jetty"));
-
-        // Pass null as child reference, should return parent logger
-        Logger log2 = log.getLogger((String)null);
-        Assert.assertThat("Logger.child.name", log2.getName(), is("jetty"));
-        Assert.assertSame("Should have returned same logger", log2, log);
+            // Pass null as child reference, should return parent logger
+            Logger log2 = log.getLogger((String)null);
+            Assert.assertThat("Logger.child.name", log2.getName(), is("jetty"));
+            Assert.assertSame("Should have returned same logger", log2, log);
+        }
     }
 
     @Test
@@ -577,14 +601,15 @@
     {
         String baseName = "jetty";
         StdErrLog log = new StdErrLog(baseName,new Properties());
-        log.setHideStacks(true);
+        try (StacklessLogging stackless = new StacklessLogging(log))
+        {
+            Assert.assertThat("Logger.name", log.getName(), is("jetty"));
 
-        Assert.assertThat("Logger.name", log.getName(), is("jetty"));
-
-        // Pass empty name as child reference, should return parent logger
-        Logger log2 = log.getLogger("");
-        Assert.assertThat("Logger.child.name", log2.getName(), is("jetty"));
-        Assert.assertSame("Should have returned same logger", log2, log);
+            // Pass empty name as child reference, should return parent logger
+            Logger log2 = log.getLogger("");
+            Assert.assertThat("Logger.child.name", log2.getName(), is("jetty"));
+            Assert.assertSame("Should have returned same logger", log2, log);
+        }
     }
 
     @Test
@@ -592,20 +617,21 @@
     {
         String baseName = "jetty";
         StdErrLog log = new StdErrLog(baseName,new Properties());
-        log.setHideStacks(true);
+        try (StacklessLogging stackless = new StacklessLogging(log))
+        {
+            Assert.assertThat("Logger.name", log.getName(), is("jetty"));
 
-        Assert.assertThat("Logger.name", log.getName(), is("jetty"));
-
-        // Pass empty name as child reference, should return parent logger
-        Logger log2 = log.getLogger("      ");
-        Assert.assertThat("Logger.child.name", log2.getName(), is("jetty"));
-        Assert.assertSame("Should have returned same logger", log2, log);
+            // Pass empty name as child reference, should return parent logger
+            Logger log2 = log.getLogger("      ");
+            Assert.assertThat("Logger.child.name", log2.getName(), is("jetty"));
+            Assert.assertSame("Should have returned same logger", log2, log);
+        }
     }
 
     @Test
     public void testGetChildLogger_NullParent()
     {
-        StdErrLog log = new StdErrLog(null,new Properties());
+        AbstractLogger log = new StdErrLog(null,new Properties());
 
         Assert.assertThat("Logger.name", log.getName(), is(""));
 
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/AbstractFSResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/AbstractFSResourceTest.java
deleted file mode 100644
index 5195d20..0000000
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/AbstractFSResourceTest.java
+++ /dev/null
@@ -1,622 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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.FileWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.net.URI;
-import java.net.URL;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.file.FileSystemException;
-import java.nio.file.Files;
-import java.nio.file.InvalidPathException;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.eclipse.jetty.toolchain.test.FS;
-import org.eclipse.jetty.toolchain.test.IO;
-import org.eclipse.jetty.toolchain.test.OS;
-import org.eclipse.jetty.toolchain.test.TestingDir;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.CollectionAssert;
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeNoException;
-
-public abstract class AbstractFSResourceTest
-{
-    @Rule
-    public TestingDir testdir = new TestingDir();
-
-    public abstract Resource newResource(URI uri) throws IOException;
-
-    public abstract Resource newResource(File file) throws IOException;
-
-    private URI createEmptyFile(String name) throws IOException
-    {
-        File file = testdir.getFile(name);
-        file.createNewFile();
-        return file.toURI();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testNonAbsoluteURI() throws Exception
-    {
-        newResource(new URI("path/to/resource"));
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testNotFileURI() throws Exception
-    {
-        newResource(new URI("http://www.eclipse.org/jetty/"));
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testBogusFilename() throws Exception
-    {
-        if (OS.IS_UNIX)
-        {
-            // A windows path is invalid under unix
-            newResource(new URI("file://Z:/:"));
-        }
-        else if (OS.IS_WINDOWS)
-        {
-            // "CON" is a reserved name under windows
-            newResource(new URI("file://CON"));
-        }
-        else
-        {
-            assumeFalse("Unknown OS type",false);
-        }   
-    }
-
-    @Test
-    public void testIsContainedIn() throws Exception
-    {
-        createEmptyFile("foo");
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource res = base.addPath("foo");
-            assertThat("is contained in",res.isContainedIn(base),is(false));
-        }
-    }
-
-    @Test
-    public void testAddPath() throws Exception
-    {
-        File dir = testdir.getDir();
-        File subdir = new File(dir,"sub");
-        FS.ensureDirExists(subdir);
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource sub = base.addPath("sub");
-            assertThat("sub/.isDirectory",sub.isDirectory(),is(true));
-            
-            Resource tmp = sub.addPath("/tmp");
-            assertThat("No root",tmp.exists(),is(false));
-        }
-    }
-    
-    @Test
-    public void testIsDirectory() throws Exception
-    {
-        File dir = testdir.getDir();
-        createEmptyFile("foo");
-
-        File subdir = new File(dir,"sub");
-        FS.ensureDirExists(subdir);
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource res = base.addPath("foo");
-            assertThat("foo.isDirectory",res.isDirectory(),is(false));
-
-            Resource sub = base.addPath("sub");
-            assertThat("sub/.isDirectory",sub.isDirectory(),is(true));
-        }
-    }
-
-    @Test
-    public void testLastModified() throws Exception
-    {
-        File file = testdir.getFile("foo");
-        file.createNewFile();
-
-        long expected = file.lastModified();
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource res = base.addPath("foo");
-            assertThat("foo.lastModified",res.lastModified(),is(expected));
-        }
-    }
-
-    @Test
-    public void testLastModified_NotExists() throws Exception
-    {
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource res = base.addPath("foo");
-            assertThat("foo.lastModified",res.lastModified(),is(0L));
-        }
-    }
-
-    @Test
-    public void testLength() throws Exception
-    {
-        File file = testdir.getFile("foo");
-        file.createNewFile();
-
-        try (StringReader reader = new StringReader("foo"); FileWriter writer = new FileWriter(file))
-        {
-            IO.copy(reader,writer);
-        }
-
-        long expected = file.length();
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource res = base.addPath("foo");
-            assertThat("foo.length",res.length(),is(expected));
-        }
-    }
-
-    @Test
-    public void testLength_NotExists() throws Exception
-    {
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource res = base.addPath("foo");
-            assertThat("foo.length",res.length(),is(0L));
-        }
-    }
-
-    @Test
-    public void testDelete() throws Exception
-    {
-        File file = testdir.getFile("foo");
-        file.createNewFile();
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            // Is it there?
-            Resource res = base.addPath("foo");
-            assertThat("foo.exists",res.exists(),is(true));
-            // delete it
-            assertThat("foo.delete",res.delete(),is(true));
-            // is it there?
-            assertThat("foo.exists",res.exists(),is(false));
-        }
-    }
-
-    @Test
-    public void testDelete_NotExists() throws Exception
-    {
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            // Is it there?
-            Resource res = base.addPath("foo");
-            assertThat("foo.exists",res.exists(),is(false));
-            // delete it
-            assertThat("foo.delete",res.delete(),is(false));
-            // is it there?
-            assertThat("foo.exists",res.exists(),is(false));
-        }
-    }
-
-    @Test
-    public void testName() throws Exception
-    {
-        String expected = testdir.getDir().getAbsolutePath();
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            assertThat("base.name",base.getName(),is(expected));
-        }
-    }
-
-    @Test
-    public void testInputStream() throws Exception
-    {
-        File file = testdir.getFile("foo");
-        file.createNewFile();
-
-        String content = "Foo is here";
-
-        try (StringReader reader = new StringReader(content); FileWriter writer = new FileWriter(file))
-        {
-            IO.copy(reader,writer);
-        }
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource foo = base.addPath("foo");
-            try (InputStream stream = foo.getInputStream(); InputStreamReader reader = new InputStreamReader(stream); StringWriter writer = new StringWriter())
-            {
-                IO.copy(reader,writer);
-                assertThat("Stream",writer.toString(),is(content));
-            }
-        }
-    }
-
-    @Test
-    public void testReadableByteChannel() throws Exception
-    {
-        File file = testdir.getFile("foo");
-        file.createNewFile();
-
-        String content = "Foo is here";
-
-        try (StringReader reader = new StringReader(content); FileWriter writer = new FileWriter(file))
-        {
-            IO.copy(reader,writer);
-        }
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource foo = base.addPath("foo");
-            try (ReadableByteChannel channel = foo.getReadableByteChannel())
-            {
-                ByteBuffer buf = ByteBuffer.allocate(256);
-                channel.read(buf);
-                buf.flip();
-                String actual = BufferUtil.toUTF8String(buf);
-                assertThat("ReadableByteChannel content",actual,is(content));
-            }
-        }
-    }
-    
-    @Test
-    public void testGetURI() throws Exception
-    {
-        File file = testdir.getFile("foo");
-        file.createNewFile();
-
-        URI expected = file.toURI();
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource foo = base.addPath("foo");
-            assertThat("getURI",foo.getURI(),is(expected));
-        }
-    }
-
-    @Test
-    public void testGetURL() throws Exception
-    {
-        File file = testdir.getFile("foo");
-        file.createNewFile();
-
-        URL expected = file.toURI().toURL();
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource foo = base.addPath("foo");
-            assertThat("getURL",foo.getURL(),is(expected));
-        }
-    }
-    
-    @Test
-    public void testList() throws Exception
-    {
-        File dir = testdir.getDir();
-        FS.touch(new File(dir, "foo"));
-        FS.touch(new File(dir, "bar"));
-        FS.ensureDirExists(new File(dir, "tick"));
-        FS.ensureDirExists(new File(dir, "tock"));
-        
-        List<String> expected = new ArrayList<>();
-        expected.add("foo");
-        expected.add("bar");
-        expected.add("tick/");
-        expected.add("tock/");
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            String list[] = base.list();
-            List<String> actual = Arrays.asList(list);
-            
-            CollectionAssert.assertContainsUnordered("Resource Directory Listing",
-                    expected,actual);
-        }
-    }
-    
-    @Test
-    public void testSymlink() throws Exception
-    {
-        File dir = testdir.getDir();
-        
-        Path foo = new File(dir, "foo").toPath();
-        Path bar = new File(dir, "bar").toPath();
-        
-        try
-        {
-            Files.createFile(foo);
-            Files.createSymbolicLink(bar,foo);
-        }
-        catch (UnsupportedOperationException | FileSystemException e)
-        {
-            // if unable to create symlink, no point testing the rest
-            // this is the path that Microsoft Windows takes.
-            assumeNoException(e);
-        }
-        
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource resFoo = base.addPath("foo");
-            Resource resBar = base.addPath("bar");
-            
-            // Access to the same resource, but via a symlink means that they are not equivalent
-            assertThat("foo.equals(bar)", resFoo.equals(resBar), is(false));
-            
-            assertThat("foo.alias", resFoo.getAlias(), nullValue());
-            assertThat("bar.alias", resBar.getAlias(), is(foo.toUri()));
-        }
-    }
-    
-    @Test
-    public void testSemicolon() throws Exception
-    {
-        File dir = testdir.getDir();
-        
-        try
-        {
-            // attempt to create file
-            Path foo = new File(dir, "foo;").toPath();
-            Files.createFile(foo);
-        }
-        catch (Exception e)
-        {
-            // if unable to create file, no point testing the rest
-            // this is the path that Microsoft Windows takes.
-            assumeNoException(e);
-        }
-
-        try (Resource base = newResource(testdir.getDir()))
-        {
-            Resource res = base.addPath("foo;");
-            assertThat("Alias: " + res,res.getAlias(),nullValue());
-        }
-    }
-
-    @Test
-    public void testExist_Normal() throws Exception
-    {
-        createEmptyFile("a.jsp");
-
-        URI ref = testdir.getDir().toURI().resolve("a.jsp");
-        try (Resource fileres = newResource(ref))
-        {
-            assertThat("Resource: " + fileres,fileres.exists(),is(true));
-        }
-    }
-
-    @Test
-    public void testSingleQuoteInFileName() throws Exception
-    {
-        createEmptyFile("foo's.txt");
-        createEmptyFile("f o's.txt");
-
-        URI refQuoted = testdir.getDir().toURI().resolve("foo's.txt");
-
-        try (Resource fileres = newResource(refQuoted))
-        {
-            assertThat("Exists: " + refQuoted,fileres.exists(),is(true));
-            assertThat("Alias: " + refQuoted,fileres.getAlias(),nullValue());
-        }
-
-        URI refEncoded = testdir.getDir().toURI().resolve("foo%27s.txt");
-
-        try (Resource fileres = newResource(refEncoded))
-        {
-            assertThat("Exists: " + refEncoded,fileres.exists(),is(true));
-            assertThat("Alias: " + refEncoded,fileres.getAlias(),nullValue());
-        }
-
-        URI refQuoteSpace = testdir.getDir().toURI().resolve("f%20o's.txt");
-
-        try (Resource fileres = newResource(refQuoteSpace))
-        {
-            assertThat("Exists: " + refQuoteSpace,fileres.exists(),is(true));
-            assertThat("Alias: " + refQuoteSpace,fileres.getAlias(),nullValue());
-        }
-
-        URI refEncodedSpace = testdir.getDir().toURI().resolve("f%20o%27s.txt");
-
-        try (Resource fileres = newResource(refEncodedSpace))
-        {
-            assertThat("Exists: " + refEncodedSpace,fileres.exists(),is(true));
-            assertThat("Alias: " + refEncodedSpace,fileres.getAlias(),nullValue());
-        }
-
-        URI refA = testdir.getDir().toURI().resolve("foo's.txt");
-        URI refB = testdir.getDir().toURI().resolve("foo%27s.txt");
-
-        StringBuilder msg = new StringBuilder();
-        msg.append("URI[a].equals(URI[b])").append(System.lineSeparator());
-        msg.append("URI[a] = ").append(refA).append(System.lineSeparator());
-        msg.append("URI[b] = ").append(refB);
-
-        // show that simple URI.equals() doesn't work
-        assertThat(msg.toString(),refA.equals(refB),is(false));
-
-        // now show that Resource.equals() does work
-        try (Resource a = newResource(refA); Resource b = newResource(refB);)
-        {
-            assertThat("A.equals(B)",a.equals(b),is(true));
-        }
-    }
-
-    @Test
-    public void testExist_BadNull() throws Exception
-    {
-        createEmptyFile("a.jsp");
-
-        try
-        {
-            // request with null at end
-            URI ref = testdir.getDir().toURI().resolve("a.jsp%00");
-            assertThat("Null URI",ref,notNullValue());
-
-            newResource(ref);
-            fail("Should have thrown " + InvalidPathException.class);
-        }
-        catch (InvalidPathException e)
-        {
-            // Expected path
-        }
-    }
-
-    @Test
-    public void testExist_BadNullX() throws Exception
-    {
-        createEmptyFile("a.jsp");
-
-        try
-        {
-            // request with null and x at end
-            URI ref = testdir.getDir().toURI().resolve("a.jsp%00x");
-            assertThat("NullX URI",ref,notNullValue());
-
-            newResource(ref);
-            fail("Should have thrown " + InvalidPathException.class);
-        }
-        catch (InvalidPathException e)
-        {
-            // Expected path
-        }
-    }
-    
-    @Test
-    public void testExist_BadControlChars_Encoded() throws Exception
-    {
-        createEmptyFile("a.jsp");
-
-        try
-        {
-            // request with control characters
-            URI ref = testdir.getDir().toURI().resolve("a.jsp%1F%10");
-            assertThat("ControlCharacters URI",ref,notNullValue());
-
-            Resource fileref = newResource(ref);
-            assertThat("File Resource should not exists", fileref.exists(), is(false));
-        }
-        catch (InvalidPathException e)
-        {
-            // Expected path
-        }
-    }
-
-    @Test
-    public void testExist_BadControlChars_Decoded() throws Exception
-    {
-        createEmptyFile("a.jsp");
-
-        try
-        {
-            // request with control characters
-            File badFile = new File(testdir.getDir(), "a.jsp\014\010");
-            newResource(badFile);
-            fail("Should have thrown " + InvalidPathException.class);
-        }
-        catch (InvalidPathException e)
-        {
-            // Expected path
-        }
-    }
-
-    @Test
-    public void testExist_AddPath_BadControlChars_Decoded() throws Exception
-    {
-        createEmptyFile("a.jsp");
-
-        try
-        {
-            // base resource
-            URI ref = testdir.getDir().toURI();
-            Resource base = newResource(ref);
-            assertThat("Base Resource URI",ref,notNullValue());
-
-            // add path with control characters (raw/decoded control characters)
-            // This MUST fail
-            base.addPath("/a.jsp\014\010");
-            fail("Should have thrown " + InvalidPathException.class);
-        }
-        catch (InvalidPathException e)
-        {
-            // Expected path
-        }
-    }
-
-    @Test
-    public void testExist_AddPath_BadControlChars_Encoded() throws Exception
-    {
-        createEmptyFile("a.jsp");
-
-        try
-        {
-            // base resource
-            URI ref = testdir.getDir().toURI();
-            Resource base = newResource(ref);
-            assertThat("Base Resource URI",ref,notNullValue());
-
-            // add path with control characters
-            Resource fileref = base.addPath("/a.jsp%14%10");
-            assertThat("File Resource should not exists", fileref.exists(), is(false));
-        }
-        catch (InvalidPathException e)
-        {
-            // Expected path
-        }
-    }
-
-    @Test
-    public void testUtf8Dir() throws Exception
-    {
-        File dir=new File(testdir.getDir(),"bãm");
-        dir.mkdir();
-        File file = new File(dir,"file.txt");
-        file.createNewFile();
-        
-        Resource base = newResource(dir);
-        Assert.assertNull(base.getAlias());
-        
-        Resource r = base.addPath("file.txt");
-        Assert.assertNull(r.getAlias());
-        
-    }
-}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ClassPathResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ClassPathResourceTest.java
new file mode 100644
index 0000000..292c366
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ClassPathResourceTest.java
@@ -0,0 +1,112 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.junit.Test;
+
+public class ClassPathResourceTest
+{
+    /**
+     * Test a class path resource for existence.
+     */
+    @Test
+    public void testClassPathResourceClassRelative()
+    {
+        final String classPathName="Resource.class";
+
+        try(Resource resource=Resource.newClassPathResource(classPathName);)
+        {
+            // A class path cannot be a directory
+            assertFalse("Class path cannot be a directory.",resource.isDirectory());
+
+            // A class path must exist
+            assertTrue("Class path resource does not exist.",resource.exists());
+        }
+    }
+
+    /**
+     * Test a class path resource for existence.
+     */
+    @Test
+    public void testClassPathResourceClassAbsolute()
+    {
+        final String classPathName="/org/eclipse/jetty/util/resource/Resource.class";
+
+        Resource resource=Resource.newClassPathResource(classPathName);
+
+        // A class path cannot be a directory
+        assertFalse("Class path cannot be a directory.",resource.isDirectory());
+
+        // A class path must exist
+        assertTrue("Class path resource does not exist.",resource.exists());
+    }
+
+    /**
+     * Test a class path resource for directories.
+     * @throws Exception failed test
+     */
+    @Test
+    public void testClassPathResourceDirectory() throws Exception
+    {
+        final String classPathName="/";
+
+        Resource resource=Resource.newClassPathResource(classPathName);
+
+        // A class path must be a directory
+        assertTrue("Class path must be a directory.",resource.isDirectory());
+
+        assertTrue("Class path returned file must be a directory.",resource.getFile().isDirectory());
+
+        // A class path must exist
+        assertTrue("Class path resource does not exist.",resource.exists());
+    }
+
+    /**
+     * Test a class path resource for a file.
+     * @throws Exception failed test
+     */
+    @Test
+    public void testClassPathResourceFile() throws Exception
+    {
+        final String fileName="resource.txt";
+        final String classPathName="/"+fileName;
+
+        // Will locate a resource in the class path
+        Resource resource=Resource.newClassPathResource(classPathName);
+
+        // A class path cannot be a directory
+        assertFalse("Class path must be a directory.",resource.isDirectory());
+
+        assertTrue(resource!=null);
+
+        File file=resource.getFile();
+
+        assertEquals("File name from class path is not equal.",fileName,file.getName());
+        assertTrue("File returned from class path should be a file.",file.isFile());
+
+        // A class path must exist
+        assertTrue("Class path resource does not exist.",resource.exists());
+    }
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileResourceTest.java
deleted file mode 100644
index deda259..0000000
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileResourceTest.java
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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.URI;
-
-public class FileResourceTest extends AbstractFSResourceTest
-{
-    @Override
-    public Resource newResource(URI uri) throws IOException
-    {
-        return new FileResource(uri);
-    }
-    
-    @Override
-    public Resource newResource(File file) throws IOException
-    {
-        return new FileResource(file);
-    }
-}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileSystemResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileSystemResourceTest.java
new file mode 100644
index 0000000..f1e3e16
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileSystemResourceTest.java
@@ -0,0 +1,1416 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeThat;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemException;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.IO;
+import org.eclipse.jetty.toolchain.test.OS;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.eclipse.jetty.util.BufferUtil;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class FileSystemResourceTest
+{
+    @Rule
+    public TestingDir testdir = new TestingDir();
+    
+    private final Class<? extends Resource> _class;
+
+    @SuppressWarnings("deprecation")
+    @Parameters(name="{0}")
+    public static Collection<Object[]> data()
+    {
+        List<Object[]> data = new ArrayList<>();
+        data.add(new Class<?>[]{FileResource.class});
+        data.add(new Class<?>[]{PathResource.class});
+        return data;
+    }
+    
+    public FileSystemResourceTest(Class<? extends Resource> test)
+    {
+        _class=test;
+    }
+    
+    public Resource newResource(URI uri) throws Exception
+    {
+        try
+        {
+            return _class.getConstructor(URI.class).newInstance(uri);
+        }
+        catch(InvocationTargetException e)
+        {
+            try
+            {
+                throw e.getTargetException();
+            }
+            catch(Exception|Error ex)
+            {
+                throw ex;
+            }
+            catch(Throwable th)
+            {
+                throw new Error(th);
+            }
+        }
+    }
+
+    public Resource newResource(File file) throws Exception
+    {
+        try
+        {
+            return _class.getConstructor(File.class).newInstance(file);
+        }
+        catch(InvocationTargetException e)
+        {
+            try
+            {
+                throw e.getTargetException();
+            }
+            catch(Exception|Error ex)
+            {
+                throw ex;
+            }
+            catch(Throwable th)
+            {
+                throw new Error(th);
+            }
+        }
+    }
+    
+    private Matcher<Resource> hasNoAlias()
+    {
+        return new BaseMatcher<Resource>()
+        {
+            @Override
+            public boolean matches(Object item)
+            {
+                final Resource res = (Resource)item;
+                return !res.isAlias();
+            }
+
+            @Override
+            public void describeTo(Description description)
+            {
+                description.appendText("getAlias should return null");
+            }
+            
+            @Override
+            public void describeMismatch(Object item, Description description)
+            {
+                description.appendText("was ").appendValue(((Resource)item).getAlias());
+            }
+        };
+    }
+    
+    private Matcher<Resource> isAliasFor(final Resource resource)
+    {
+        return new BaseMatcher<Resource>()
+        {
+            @Override
+            public boolean matches(Object item)
+            {
+                final Resource ritem = (Resource)item;
+                final URI alias = ritem.getAlias();
+                if (alias == null)
+                {
+                    return ritem == null;
+                }
+                else
+                {
+                    return alias.equals(resource.getURI());
+                }
+            }
+
+            @Override
+            public void describeTo(Description description)
+            {
+                description.appendText("getAlias should return ").appendValue(resource.getURI());
+            }
+            
+            @Override
+            public void describeMismatch(Object item, Description description)
+            {
+                description.appendText("was ").appendValue(((Resource)item).getAlias());
+            }
+        };
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNonAbsoluteURI() throws Exception
+    {
+        newResource(new URI("path/to/resource"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNotFileURI() throws Exception
+    {
+        newResource(new URI("http://www.eclipse.org/jetty/"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBogusFilename() throws Exception
+    {
+        if (OS.IS_UNIX)
+        {
+            // A windows path is invalid under unix
+            newResource(new URI("file://Z:/:"));
+        }
+        else if (OS.IS_WINDOWS)
+        {
+            // "CON" is a reserved name under windows
+            newResource(new URI("file://CON"));
+        }
+        else
+        {
+            assumeFalse("Unknown OS type",false);
+        }
+    }
+    
+    @Test
+    public void testAddPath() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        
+        Path subdir = dir.resolve("sub");
+        FS.ensureDirExists(subdir.toFile());
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource sub = base.addPath("sub");
+            assertThat("sub/.isDirectory",sub.isDirectory(),is(true));
+
+            Resource tmp = sub.addPath("/tmp");
+            assertThat("No root",tmp.exists(),is(false));
+        }
+    }
+
+    @Test
+    public void testAddRootPath() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Path subdir = dir.resolve("sub");
+        Files.createDirectories(subdir);
+
+        String readableRootDir = findRootDir(dir.getFileSystem());
+        assumeThat("Readable Root Dir found",readableRootDir,notNullValue());
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource sub = base.addPath("sub");
+            assertThat("sub",sub.isDirectory(),is(true));
+
+            try
+            {
+                Resource rrd = sub.addPath(readableRootDir);
+                // valid path for unix and OSX
+                assertThat("Readable Root Dir",rrd.exists(),is(false));
+            }
+            catch (MalformedURLException | InvalidPathException e)
+            {
+                // valid path on Windows
+            }
+        }
+    }
+
+    private String findRootDir(FileSystem fs) throws IOException
+    {
+        // look for a directory off of a root path
+        for (Path rootDir : fs.getRootDirectories())
+        {
+            try (DirectoryStream<Path> dir = Files.newDirectoryStream(rootDir))
+            {
+                for (Path entry : dir)
+                {
+                    if (Files.isDirectory(entry) && !Files.isHidden(entry) && !entry.getFileName().toString().contains("$"))
+                    {
+                        return entry.toAbsolutePath().toString();
+                    }
+                }
+            }
+            catch(Exception e)
+            {
+            }
+        }
+
+        return null;
+    }
+
+    @Test
+    public void testIsContainedIn() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        Path foo = dir.resolve("foo");
+        Files.createFile(foo);
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource res = base.addPath("foo");
+            assertThat("is contained in",res.isContainedIn(base),is(false));
+        }
+    }
+
+    @Test
+    public void testIsDirectory() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        Path foo = dir.resolve("foo");
+        Files.createFile(foo);
+
+        Path subdir = dir.resolve("sub");
+        Files.createDirectories(subdir);
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource res = base.addPath("foo");
+            assertThat("foo.isDirectory",res.isDirectory(),is(false));
+
+            Resource sub = base.addPath("sub");
+            assertThat("sub/.isDirectory",sub.isDirectory(),is(true));
+        }
+    }
+
+    @Test
+    public void testLastModified() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        File file = testdir.getPathFile("foo").toFile();
+        file.createNewFile();
+
+        long expected = file.lastModified();
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource res = base.addPath("foo");
+            assertThat("foo.lastModified",res.lastModified()/1000*1000, lessThanOrEqualTo(expected));
+        }
+    }
+
+    @Test
+    public void testLastModified_NotExists() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource res = base.addPath("foo");
+            assertThat("foo.lastModified",res.lastModified(),is(0L));
+        }
+    }
+
+    @Test
+    public void testLength() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        Path file = dir.resolve("foo");
+
+        try (StringReader reader = new StringReader("foo");
+             BufferedWriter writer = Files.newBufferedWriter(file))
+        {
+            IO.copy(reader,writer);
+        }
+
+        long expected = Files.size(file);
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource res = base.addPath("foo");
+            assertThat("foo.length",res.length(),is(expected));
+        }
+    }
+
+    @Test
+    public void testLength_NotExists() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource res = base.addPath("foo");
+            assertThat("foo.length",res.length(),is(0L));
+        }
+    }
+
+    @Test
+    public void testDelete() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        Path file = dir.resolve("foo");
+        Files.createFile(file);
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            // Is it there?
+            Resource res = base.addPath("foo");
+            assertThat("foo.exists",res.exists(),is(true));
+            // delete it
+            assertThat("foo.delete",res.delete(),is(true));
+            // is it there?
+            assertThat("foo.exists",res.exists(),is(false));
+        }
+    }
+
+    @Test
+    public void testDelete_NotExists() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        try (Resource base = newResource(dir.toFile()))
+        {
+            // Is it there?
+            Resource res = base.addPath("foo");
+            assertThat("foo.exists",res.exists(),is(false));
+            // delete it
+            assertThat("foo.delete",res.delete(),is(false));
+            // is it there?
+            assertThat("foo.exists",res.exists(),is(false));
+        }
+    }
+
+    @Test
+    public void testName() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        String expected = dir.toAbsolutePath().toString();
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            assertThat("base.name",base.getName(),is(expected));
+        }
+    }
+
+    @Test
+    public void testInputStream() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        Path file = dir.resolve("foo");
+        String content = "Foo is here";
+
+        try (StringReader reader = new StringReader(content);
+             BufferedWriter writer = Files.newBufferedWriter(file))
+        {
+            IO.copy(reader,writer);
+        }
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource foo = base.addPath("foo");
+            try (InputStream stream = foo.getInputStream(); InputStreamReader reader = new InputStreamReader(stream); StringWriter writer = new StringWriter())
+            {
+                IO.copy(reader,writer);
+                assertThat("Stream",writer.toString(),is(content));
+            }
+        }
+    }
+
+    @Test
+    public void testReadableByteChannel() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+
+        Path file = dir.resolve("foo");
+        String content = "Foo is here";
+
+        try (StringReader reader = new StringReader(content);
+             BufferedWriter writer = Files.newBufferedWriter(file))
+        {
+            IO.copy(reader,writer);
+        }
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource foo = base.addPath("foo");
+            try (ReadableByteChannel channel = foo.getReadableByteChannel())
+            {
+                ByteBuffer buf = ByteBuffer.allocate(256);
+                channel.read(buf);
+                buf.flip();
+                String actual = BufferUtil.toUTF8String(buf);
+                assertThat("ReadableByteChannel content",actual,is(content));
+            }
+        }
+    }
+    
+    @Test
+    public void testGetURI() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+
+        Path file = dir.resolve("foo");
+        Files.createFile(file);
+
+        URI expected = file.toUri();
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource foo = base.addPath("foo");
+            assertThat("getURI",foo.getURI(),is(expected));
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testGetURL() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+
+        Path file = dir.resolve("foo");
+        Files.createFile(file);
+
+        URL expected = file.toUri().toURL();
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource foo = base.addPath("foo");
+            assertThat("getURL",foo.getURL(),is(expected));
+        }
+    }
+    
+    @Test
+    public void testList() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        Files.createFile(dir.resolve("foo"));
+        Files.createFile(dir.resolve("bar"));
+        Files.createDirectories(dir.resolve("tick"));
+        Files.createDirectories(dir.resolve("tock"));
+        
+        List<String> expected = new ArrayList<>();
+        expected.add("foo");
+        expected.add("bar");
+        expected.add("tick/");
+        expected.add("tock/");
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            String list[] = base.list();
+            List<String> actual = Arrays.asList(list);
+            
+            assertEquals(expected.size(),actual.size());
+            for (String s : expected)
+                assertEquals(true,actual.contains(s));
+            
+        }
+    }
+    
+    @Test
+    public void testSymlink() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        
+        Path foo = dir.resolve("foo");
+        Path bar = dir.resolve("bar");
+        
+        try
+        {
+            Files.createFile(foo);
+            Files.createSymbolicLink(bar,foo);
+        }
+        catch (UnsupportedOperationException | FileSystemException e)
+        {
+            // if unable to create symlink, no point testing the rest
+            // this is the path that Microsoft Windows takes.
+            assumeNoException(e);
+        }
+        
+        try (Resource base = newResource(dir.toFile()))
+        {
+            if (OS.IS_WINDOWS && base instanceof FileResource)
+                // FileResource doesn't handle symlinks of Windows
+                return;
+                
+            Resource resFoo = base.addPath("foo");
+            Resource resBar = base.addPath("bar");
+            
+            assertThat("resFoo.uri", resFoo.getURI(), is(foo.toUri()));
+            
+            // Access to the same resource, but via a symlink means that they are not equivalent
+            assertThat("foo.equals(bar)", resFoo.equals(resBar), is(false));
+            
+            assertThat("resource.alias", resFoo, hasNoAlias());
+            assertThat("resource.uri.alias", newResource(resFoo.getURI()), hasNoAlias());
+            assertThat("resource.file.alias", newResource(resFoo.getFile()), hasNoAlias());
+            
+            assertThat("alias", resBar, isAliasFor(resFoo));
+            assertThat("uri.alias", newResource(resBar.getURI()), isAliasFor(resFoo));
+            assertThat("file.alias", newResource(resBar.getFile()), isAliasFor(resFoo));
+        }
+    }
+
+    @Test
+    public void testNonExistantSymlink() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        Path foo = dir.resolve("foo");
+        Path bar = dir.resolve("bar");
+        
+        try
+        {
+            Files.createSymbolicLink(bar,foo);
+        }
+        catch (UnsupportedOperationException | FileSystemException e)
+        {
+            // if unable to create symlink, no point testing the rest
+            // this is the path that Microsoft Windows takes.
+            assumeNoException(e);
+        }
+        
+        try (Resource base = newResource(dir.toFile()))
+        {
+            // FileResource does not pass this test!
+            assumeFalse(base instanceof FileResource);
+            
+            Resource resFoo = base.addPath("foo");
+            Resource resBar = base.addPath("bar");
+            
+            assertThat("resFoo.uri", resFoo.getURI(), is(foo.toUri()));
+            
+            // Access to the same resource, but via a symlink means that they are not equivalent
+            assertThat("foo.equals(bar)", resFoo.equals(resBar), is(false));
+            
+            assertThat("resource.alias", resFoo, hasNoAlias());
+            assertThat("resource.uri.alias", newResource(resFoo.getURI()), hasNoAlias());
+            assertThat("resource.file.alias", newResource(resFoo.getFile()), hasNoAlias());
+            
+            assertThat("alias", resBar, isAliasFor(resFoo));
+            assertThat("uri.alias", newResource(resBar.getURI()), isAliasFor(resFoo));
+            assertThat("file.alias", newResource(resBar.getFile()), isAliasFor(resFoo));
+        }
+    }
+
+    @Test
+    public void testCaseInsensitiveAlias() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        Path path = dir.resolve("file");
+        Files.createFile(path);
+        
+        try (Resource base = newResource(dir.toFile()))
+        {
+            // Reference to actual resource that exists
+            Resource resource = base.addPath("file");
+
+            assertThat("resource.alias", resource, hasNoAlias());
+            assertThat("resource.uri.alias", newResource(resource.getURI()), hasNoAlias());
+            assertThat("resource.file.alias", newResource(resource.getFile()), hasNoAlias());
+
+            // On some case insensitive file systems, lets see if an alternate
+            // case for the filename results in an alias reference
+            Resource alias = base.addPath("FILE");
+            if (alias.exists())
+            {
+                // If it exists, it must be an alias
+                assertThat("alias", alias, isAliasFor(resource));
+                assertThat("alias.uri", newResource(alias.getURI()), isAliasFor(resource));
+                assertThat("alias.file", newResource(alias.getFile()), isAliasFor(resource));
+            }
+        }
+    }
+
+    /**
+     * Test for Windows feature that exposes 8.3 filename references
+     * for long filenames.
+     * <p>
+     * See: http://support.microsoft.com/kb/142982
+     * @throws Exception failed test
+     */
+    @Test
+    public void testCase8dot3Alias() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        Path path = dir.resolve("TextFile.Long.txt");
+        Files.createFile(path);
+        
+        try (Resource base = newResource(dir.toFile()))
+        {
+            // Long filename
+            Resource resource = base.addPath("TextFile.Long.txt");
+
+            assertThat("resource.alias", resource, hasNoAlias());
+            assertThat("resource.uri.alias", newResource(resource.getURI()), hasNoAlias());
+            assertThat("resource.file.alias", newResource(resource.getFile()), hasNoAlias());
+
+            // On some versions of Windows, the long filename can be referenced
+            // via a short 8.3 equivalent filename.
+            Resource alias = base.addPath("TEXTFI~1.TXT");
+            if (alias.exists())
+            {
+                // If it exists, it must be an alias
+                assertThat("alias", alias, isAliasFor(resource));
+                assertThat("alias.uri", newResource(alias.getURI()), isAliasFor(resource));
+                assertThat("alias.file", newResource(alias.getFile()), isAliasFor(resource));
+            }
+        }
+    }
+
+    /**
+     * NTFS Alternative Data / File Streams.
+     * <p>
+     * See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa364404(v=vs.85).aspx
+     * @throws Exception failed test
+     */
+    @Test
+    public void testNTFSFileStreamAlias() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+
+        Path path = dir.resolve("testfile");
+        Files.createFile(path);
+        
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource resource = base.addPath("testfile");
+
+            assertThat("resource.alias", resource, hasNoAlias());
+            assertThat("resource.uri.alias", newResource(resource.getURI()), hasNoAlias());
+            assertThat("resource.file.alias", newResource(resource.getFile()), hasNoAlias());
+
+            try
+            {
+                // Attempt to reference same file, but via NTFS simple stream
+                Resource alias = base.addPath("testfile:stream");
+                if (alias.exists())
+                {
+                    // If it exists, it must be an alias
+                    assertThat("resource.alias",alias,isAliasFor(resource));
+                    assertThat("resource.uri.alias",newResource(alias.getURI()),isAliasFor(resource));
+                    assertThat("resource.file.alias",newResource(alias.getFile()),isAliasFor(resource));
+                }
+            }
+            catch (InvalidPathException e)
+            {
+                // NTFS filesystem streams are unsupported on some platforms.
+                assumeNoException(e);
+            }
+        }
+    }
+    
+    /**
+     * NTFS Alternative Data / File Streams.
+     * <p>
+     * See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa364404(v=vs.85).aspx
+     * @throws Exception failed test
+     */
+    @Test
+    public void testNTFSFileDataStreamAlias() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+
+        Path path = dir.resolve("testfile");
+        Files.createFile(path);
+        
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource resource = base.addPath("testfile");
+
+            assertThat("resource.alias", resource, hasNoAlias());
+            assertThat("resource.uri.alias", newResource(resource.getURI()), hasNoAlias());
+            assertThat("resource.file.alias", newResource(resource.getFile()), hasNoAlias());
+
+            try
+            {
+                // Attempt to reference same file, but via NTFS DATA stream
+                Resource alias = base.addPath("testfile::$DATA");
+                if (alias.exists())
+                {
+                    assumeThat(alias.getURI().getScheme(), is("http"));
+                    
+                    // If it exists, it must be an alias
+                    assertThat("resource.alias",alias,isAliasFor(resource));
+                    assertThat("resource.uri.alias",newResource(alias.getURI()),isAliasFor(resource));
+                    assertThat("resource.file.alias",newResource(alias.getFile()),isAliasFor(resource));
+                }
+            }
+            catch (InvalidPathException e)
+            {
+                // NTFS filesystem streams are unsupported on some platforms.
+                assumeNoException(e);
+            }
+        }
+    }
+    
+    /**
+     * NTFS Alternative Data / File Streams.
+     * <p>
+     * See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa364404(v=vs.85).aspx
+     * @throws Exception failed test
+     */
+    @Test
+    public void testNTFSFileEncodedDataStreamAlias() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+
+        Path path = dir.resolve("testfile");
+        Files.createFile(path);
+        
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource resource = base.addPath("testfile");
+
+            assertThat("resource.alias", resource, hasNoAlias());
+            assertThat("resource.uri.alias", newResource(resource.getURI()), hasNoAlias());
+            assertThat("resource.file.alias", newResource(resource.getFile()), hasNoAlias());
+
+            try
+            {
+                // Attempt to reference same file, but via NTFS DATA stream (encoded addPath version) 
+                Resource alias = base.addPath("testfile::%24DATA");
+                if (alias.exists())
+                {
+                    // If it exists, it must be an alias
+                    assertThat("resource.alias",alias,isAliasFor(resource));
+                    assertThat("resource.uri.alias",newResource(alias.getURI()),isAliasFor(resource));
+                    assertThat("resource.file.alias",newResource(alias.getFile()),isAliasFor(resource));
+                }
+            }
+            catch (InvalidPathException e)
+            {
+                // NTFS filesystem streams are unsupported on some platforms.
+                assumeNoException(e);
+            }
+        }
+    }
+    
+    @Test
+    public void testSemicolon() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        
+        try
+        {
+            // attempt to create file
+            Path foo = dir.resolve("foo;");
+            Files.createFile(foo);
+        }
+        catch (Exception e)
+        {
+            // if unable to create file, no point testing the rest.
+            // this is the path that Microsoft Windows takes.
+            assumeNoException(e);
+        }
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource res = base.addPath("foo;");
+            assertThat("Alias: " + res,res,hasNoAlias());
+        }
+    }
+    
+    @Test
+    public void testSingleQuote() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        try
+        {
+            // attempt to create file
+            Path foo = dir.resolve("foo' bar");
+            Files.createFile(foo);
+        }
+        catch (Exception e)
+        {
+            // if unable to create file, no point testing the rest.
+            // this is the path that Microsoft Windows takes.
+            assumeNoException(e);
+        }
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource res = base.addPath("foo' bar");
+            assertThat("Alias: " + res,res.getAlias(),nullValue());
+        }
+    }
+    
+    @Test
+    public void testSingleBackTick() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        try
+        {
+            // attempt to create file
+            Path foo = dir.resolve("foo` bar");
+            Files.createFile(foo);
+        }
+        catch (Exception e)
+        {
+            // if unable to create file, no point testing the rest.
+            // this is the path that Microsoft Windows takes.
+            assumeNoException(e);
+        }
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            // FileResource does not pass this test!
+            assumeFalse(base instanceof FileResource);
+
+            Resource res = base.addPath("foo` bar");
+            assertThat("Alias: " + res,res.getAlias(),nullValue());
+        }
+    }
+    
+    @Test
+    public void testBrackets() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        try
+        {
+            // attempt to create file
+            Path foo = dir.resolve("foo[1]");
+            Files.createFile(foo);
+        }
+        catch (Exception e)
+        {
+            // if unable to create file, no point testing the rest.
+            // this is the path that Microsoft Windows takes.
+            assumeNoException(e);
+        }
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            Resource res = base.addPath("foo[1]");
+            assertThat("Alias: " + res,res.getAlias(),nullValue());
+        }
+    }
+    
+    @Test
+    public void testBraces() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        try
+        {
+            // attempt to create file
+            Path foo = dir.resolve("foo.{bar}.txt");
+            Files.createFile(foo);
+        }
+        catch (Exception e)
+        {
+            // if unable to create file, no point testing the rest.
+            // this is the path that Microsoft Windows takes.
+            assumeNoException(e);
+        }
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            // FileResource does not pass this test!
+            assumeFalse(base instanceof FileResource);
+
+            Resource res = base.addPath("foo.{bar}.txt");
+            assertThat("Alias: " + res,res.getAlias(),nullValue());
+        }
+    }
+    
+    @Test
+    public void testCaret() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        try
+        {
+            // attempt to create file
+            Path foo = dir.resolve("foo^3.txt");
+            Files.createFile(foo);
+        }
+        catch (Exception e)
+        {
+            // if unable to create file, no point testing the rest.
+            // this is the path that Microsoft Windows takes.
+            assumeNoException(e);
+        }
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            // FileResource does not pass this test!
+            assumeFalse(base instanceof FileResource);
+
+            Resource res = base.addPath("foo^3.txt");
+            assertThat("Alias: " + res,res.getAlias(),nullValue());
+        }
+    }
+    
+    @Test
+    public void testPipe() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        try
+        {
+            // attempt to create file
+            Path foo = dir.resolve("foo|bar.txt");
+            Files.createFile(foo);
+        }
+        catch (Exception e)
+        {
+            // if unable to create file, no point testing the rest.
+            // this is the path that Microsoft Windows takes.
+            assumeNoException(e);
+        }
+
+        try (Resource base = newResource(dir.toFile()))
+        {
+            // FileResource does not pass this test!
+            assumeFalse(base instanceof FileResource);
+
+            Resource res = base.addPath("foo|bar.txt");
+            assertThat("Alias: " + res,res.getAlias(),nullValue());
+        }
+    }
+
+    /**
+     * The most basic access example
+     * @throws Exception failed test
+     */
+    @Test
+    public void testExist_Normal() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        Path path = dir.resolve("a.jsp");
+        Files.createFile(path);
+
+        URI ref = testdir.getPath().toUri().resolve("a.jsp");
+        try (Resource fileres = newResource(ref))
+        {
+            assertThat("Resource: " + fileres,fileres.exists(),is(true));
+        }
+    }
+
+    @Test
+    public void testSingleQuoteInFileName() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        Path fooA = dir.resolve("foo's.txt");
+        Path fooB = dir.resolve("f o's.txt");
+        
+        Files.createFile(fooA);
+        Files.createFile(fooB);
+        
+        URI refQuoted = dir.resolve("foo's.txt").toUri();
+
+        try (Resource fileres = newResource(refQuoted))
+        {
+            // This test does not pass on FileResource
+            assumeFalse(fileres instanceof FileResource);
+            
+            assertThat("Exists: " + refQuoted,fileres.exists(),is(true));
+            assertThat("Alias: " + refQuoted,fileres,hasNoAlias());
+        }
+
+        URI refEncoded = dir.toUri().resolve("foo%27s.txt");
+
+        try (Resource fileres = newResource(refEncoded))
+        {
+            assertThat("Exists: " + refEncoded,fileres.exists(),is(true));
+            assertThat("Alias: " + refEncoded,fileres,hasNoAlias());
+        }
+
+        URI refQuoteSpace = dir.toUri().resolve("f%20o's.txt");
+
+        try (Resource fileres = newResource(refQuoteSpace))
+        {
+            assertThat("Exists: " + refQuoteSpace,fileres.exists(),is(true));
+            assertThat("Alias: " + refQuoteSpace,fileres,hasNoAlias());
+        }
+
+        URI refEncodedSpace = dir.toUri().resolve("f%20o%27s.txt");
+
+        try (Resource fileres = newResource(refEncodedSpace))
+        {
+            assertThat("Exists: " + refEncodedSpace,fileres.exists(),is(true));
+            assertThat("Alias: " + refEncodedSpace,fileres,hasNoAlias());
+        }
+
+        URI refA = dir.toUri().resolve("foo's.txt");
+        URI refB = dir.toUri().resolve("foo%27s.txt");
+
+        StringBuilder msg = new StringBuilder();
+        msg.append("URI[a].equals(URI[b])").append(System.lineSeparator());
+        msg.append("URI[a] = ").append(refA).append(System.lineSeparator());
+        msg.append("URI[b] = ").append(refB);
+
+        // show that simple URI.equals() doesn't work
+        assertThat(msg.toString(),refA.equals(refB),is(false));
+
+        // now show that Resource.equals() does work
+        try (Resource a = newResource(refA); Resource b = newResource(refB);)
+        {
+            assertThat("A.equals(B)",a.equals(b),is(true));
+        }
+    }
+
+    @Test
+    public void testExist_BadURINull() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        Path path = dir.resolve("a.jsp");
+        Files.createFile(path);
+
+        try
+        {
+            // request with null at end
+            URI uri = testdir.getPath().toUri().resolve("a.jsp%00");
+            assertThat("Null URI",uri,notNullValue());
+
+            Resource r = newResource(uri);
+            
+            // if we have r, then it better not exist
+            assertFalse(r.exists());
+        }
+        catch (InvalidPathException e)
+        {
+            // Exception is acceptable
+        }
+    }
+
+    @Test
+    public void testExist_BadURINullX() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+        
+        Path path = dir.resolve("a.jsp");
+        Files.createFile(path);
+
+        try
+        {
+            // request with null and x at end
+            URI uri = testdir.getPath().toUri().resolve("a.jsp%00x");
+            assertThat("NullX URI",uri,notNullValue());
+
+            Resource r = newResource(uri);
+            
+            // if we have r, then it better not exist
+            assertFalse(r.exists());
+        }
+        catch (InvalidPathException e)
+        {
+            // Exception is acceptable
+        }
+    }
+
+    @Test
+    public void testAddPath_WindowsSlash() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+
+        Path basePath = dir.resolve("base");
+        FS.ensureDirExists(basePath);
+        Path dirPath = basePath.resolve("aa");
+        FS.ensureDirExists(dirPath);
+        Path filePath = dirPath.resolve("foo.txt");
+        Files.createFile(filePath);
+
+        try (Resource base = newResource(basePath.toFile()))
+        {
+            assertThat("Exists: " + basePath,base.exists(),is(true));
+            assertThat("Alias: " + basePath,base,hasNoAlias());
+
+            Resource r = base.addPath("aa\\/foo.txt");
+            assertThat("getURI()", r.getURI().toASCIIString(), containsString("aa%5C/foo.txt"));
+
+            if(OS.IS_WINDOWS)
+            {
+                assertThat("isAlias()", r.isAlias(), is(true));
+                assertThat("getAlias()", r.getAlias(), notNullValue());
+                assertThat("getAlias()", r.getAlias().toASCIIString(), containsString("aa/foo.txt"));
+                assertThat("Exists: " + r, r.exists(), is(true));
+            }
+            else
+            {
+                assertThat("isAlias()", r.isAlias(), is(false));
+                assertThat("Exists: " + r, r.exists(), is(false));
+            }
+        }
+        catch (InvalidPathException e)
+        {
+            // Exception is acceptable
+        }
+    }
+
+    @Test
+    public void testAddPath_WindowsExtensionLess() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+
+        Path basePath = dir.resolve("base");
+        FS.ensureDirExists(basePath);
+        Path dirPath = basePath.resolve("aa");
+        FS.ensureDirExists(dirPath);
+        Path filePath = dirPath.resolve("foo.txt");
+        Files.createFile(filePath);
+
+        try (Resource base = newResource(basePath.toFile()))
+        {
+            assertThat("Exists: " + basePath,base.exists(),is(true));
+            assertThat("Alias: " + basePath,base,hasNoAlias());
+
+            Resource r = base.addPath("aa./foo.txt");
+            assertThat("getURI()", r.getURI().toASCIIString(), containsString("aa./foo.txt"));
+
+            if(OS.IS_WINDOWS)
+            {
+                assertThat("isAlias()", r.isAlias(), is(true));
+                assertThat("getAlias()", r.getAlias(), notNullValue());
+                assertThat("getAlias()", r.getAlias().toASCIIString(), containsString("aa/foo.txt"));
+                assertThat("Exists: " + r, r.exists(), is(true));
+            }
+            else
+            {
+                assertThat("isAlias()", r.isAlias(), is(false));
+                assertThat("Exists: " + r, r.exists(), is(false));
+            }
+        }
+        catch (InvalidPathException e)
+        {
+            // Exception is acceptable
+        }
+    }
+
+
+    @Test
+    public void testAddInitialSlash() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+
+        Path basePath = dir.resolve("base");
+        FS.ensureDirExists(basePath);
+        Path filePath = basePath.resolve("foo.txt");
+        Files.createFile(filePath);
+
+        try (Resource base = newResource(basePath.toFile()))
+        {
+            assertThat("Exists: " + basePath,base.exists(),is(true));
+            assertThat("Alias: " + basePath,base,hasNoAlias());
+
+            Resource r = base.addPath("/foo.txt");
+            assertThat("getURI()", r.getURI().toASCIIString(), containsString("/foo.txt"));
+
+            assertThat("isAlias()", r.isAlias(), is(false));
+            assertThat("Exists: " + r, r.exists(), is(true));
+        }
+        catch (InvalidPathException e)
+        {
+            // Exception is acceptable
+        }
+    }
+
+    @Test
+    public void testAddInitialDoubleSlash() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+
+        Path basePath = dir.resolve("base");
+        FS.ensureDirExists(basePath);
+        Path filePath = basePath.resolve("foo.txt");
+        Files.createFile(filePath);
+
+        try (Resource base = newResource(basePath.toFile()))
+        {
+            assertThat("Exists: " + basePath,base.exists(),is(true));
+            assertThat("Alias: " + basePath,base,hasNoAlias());
+
+            Resource r = base.addPath("//foo.txt");
+            assertThat("getURI()", r.getURI().toASCIIString(), containsString("//foo.txt"));
+
+            assertThat("isAlias()", r.isAlias(), is(true));
+            assertThat("getAlias()", r.getAlias(), notNullValue());
+            assertThat("getAlias()", r.getAlias().toASCIIString(), containsString("/foo.txt"));
+            assertThat("Exists: " + r, r.exists(), is(true));
+
+        }
+        catch (InvalidPathException e)
+        {
+            // Exception is acceptable
+        }
+    }
+    @Test
+    public void testAddDoubleSlash() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+
+        Path basePath = dir.resolve("base");
+        FS.ensureDirExists(basePath);
+        Path dirPath = basePath.resolve("aa");
+        FS.ensureDirExists(dirPath);
+        Path filePath = dirPath.resolve("foo.txt");
+        Files.createFile(filePath);
+
+        try (Resource base = newResource(basePath.toFile()))
+        {
+            assertThat("Exists: " + basePath,base.exists(),is(true));
+            assertThat("Alias: " + basePath,base,hasNoAlias());
+
+            Resource r = base.addPath("aa//foo.txt");
+            assertThat("getURI()", r.getURI().toASCIIString(), containsString("aa//foo.txt"));
+
+            assertThat("isAlias()", r.isAlias(), is(true));
+            assertThat("getAlias()", r.getAlias(), notNullValue());
+            assertThat("getAlias()", r.getAlias().toASCIIString(), containsString("aa/foo.txt"));
+            assertThat("Exists: " + r, r.exists(), is(true));
+
+        }
+        catch (InvalidPathException e)
+        {
+            // Exception is acceptable
+        }
+    }
+
+
+    @Test
+    public void testEncoding() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Files.createDirectories(dir);
+
+        Path specials = dir.resolve("a file with,spe#ials");
+        Files.createFile(specials);
+        
+        try(Resource res = newResource(specials.toFile()))
+        {
+            assertThat("Specials URL", res.getURI().toASCIIString(), containsString("a%20file%20with,spe%23ials"));
+            assertThat("Specials Filename", res.getFile().toString(), containsString("a file with,spe#ials"));
+            
+            res.delete();
+            assertThat("File should have been deleted.",res.exists(),is(false));
+        }
+    }
+    
+    @Test
+    public void testUtf8Dir() throws Exception
+    {
+        Path dir = testdir.getPath().normalize().toRealPath();
+        Path utf8Dir = dir.resolve("bãm");
+        Files.createDirectories(utf8Dir);
+        
+        Path file = utf8Dir.resolve("file.txt");
+        Files.createFile(file);
+        
+        try (Resource base = newResource(utf8Dir.toFile()))
+        {
+            assertThat("Exists: " + utf8Dir,base.exists(),is(true));
+            assertThat("Alias: " + utf8Dir,base,hasNoAlias());
+
+            Resource r = base.addPath("file.txt");
+            assertThat("Exists: " + r,r.exists(),is(true));
+            assertThat("Alias: " + r,r,hasNoAlias());
+        }
+    }
+    
+    @Test
+    public void testUncPath() throws Exception
+    {
+        assumeTrue("Only windows supports UNC paths", OS.IS_WINDOWS);
+        assumeFalse("FileResource does not support this test", _class.equals(FileResource.class));
+        
+        try (Resource base = newResource(URI.create("file://127.0.0.1/path")))
+        {
+            Resource resource = base.addPath("WEB-INF/");
+            assertThat("getURI()", resource.getURI().toASCIIString(), containsString("path/WEB-INF/"));
+            assertThat("isAlias()", resource.isAlias(), is(true));
+            assertThat("getAlias()", resource.getAlias(), notNullValue());
+            assertThat("getAlias()", resource.getAlias().toASCIIString(), containsString("path/WEB-INF"));
+        }
+    }
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JarResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JarResourceTest.java
new file mode 100644
index 0000000..9515978
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JarResourceTest.java
@@ -0,0 +1,205 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.zip.ZipFile;
+
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.IO;
+import org.junit.Test;
+
+public class JarResourceTest
+{
+    private String testResURI = MavenTestingUtils.getTestResourcesDir().getAbsoluteFile().toURI().toASCIIString();
+
+    @Test
+    public void testJarFile()
+    throws Exception
+    {
+        String s = "jar:"+testResURI+"TestData/test.zip!/subdir/";
+        Resource r = Resource.newResource(s);
+
+        Set<String> entries = new HashSet<>(Arrays.asList(r.list()));
+        assertEquals(3,entries.size());
+        assertTrue(entries.contains("alphabet"));
+        assertTrue(entries.contains("numbers"));
+        assertTrue(entries.contains("subsubdir/"));
+
+        File extract = File.createTempFile("extract", null);
+        if (extract.exists())
+            extract.delete();
+        extract.mkdir();
+        extract.deleteOnExit();
+
+        r.copyTo(extract);
+
+        Resource e = Resource.newResource(extract.getAbsolutePath());
+
+        entries = new HashSet<>(Arrays.asList(e.list()));
+        assertEquals(3,entries.size());
+        assertTrue(entries.contains("alphabet"));
+        assertTrue(entries.contains("numbers"));
+        assertTrue(entries.contains("subsubdir/"));
+        IO.delete(extract);
+
+        s = "jar:"+testResURI+"TestData/test.zip!/subdir/subsubdir/";
+        r = Resource.newResource(s);
+
+        entries = new HashSet<>(Arrays.asList(r.list()));
+        assertEquals(2,entries.size());
+        assertTrue(entries.contains("alphabet"));
+        assertTrue(entries.contains("numbers"));
+
+        extract = File.createTempFile("extract", null);
+        if (extract.exists())
+            extract.delete();
+        extract.mkdir();
+        extract.deleteOnExit();
+
+        r.copyTo(extract);
+
+        e = Resource.newResource(extract.getAbsolutePath());
+
+        entries = new HashSet<>(Arrays.asList(e.list()));
+        assertEquals(2,entries.size());
+        assertTrue(entries.contains("alphabet"));
+        assertTrue(entries.contains("numbers"));
+        IO.delete(extract);
+
+    }
+
+    /* ------------------------------------------------------------ */
+    @Test
+    public void testJarFileGetAllResoures()
+    throws Exception
+    {
+        String s = "jar:"+testResURI+"TestData/test.zip!/subdir/";
+        Resource r = Resource.newResource(s);
+        Collection<Resource> deep=r.getAllResources();
+        
+        assertEquals(4, deep.size());
+    }
+    
+    @Test
+    public void testJarFileIsContainedIn ()
+    throws Exception
+    {
+        String s = "jar:"+testResURI+"TestData/test.zip!/subdir/";
+        Resource r = Resource.newResource(s);
+        Resource container = Resource.newResource(testResURI+"TestData/test.zip");
+
+        assertTrue(r instanceof JarFileResource);
+        JarFileResource jarFileResource = (JarFileResource)r;
+
+        assertTrue(jarFileResource.isContainedIn(container));
+
+        container = Resource.newResource(testResURI+"TestData");
+        assertFalse(jarFileResource.isContainedIn(container));
+    }
+
+    /* ------------------------------------------------------------ */
+    @Test
+    public void testJarFileLastModified ()
+    throws Exception
+    {
+        String s = "jar:"+testResURI+"TestData/test.zip!/subdir/numbers";
+
+        try(ZipFile zf = new ZipFile(MavenTestingUtils.getTestResourceFile("TestData/test.zip")))
+        {
+            long last = zf.getEntry("subdir/numbers").getTime();
+
+            Resource r = Resource.newResource(s);
+            assertEquals(last,r.lastModified());
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Test
+    public void testJarFileCopyToDirectoryTraversal () throws Exception
+    {
+        String s = "jar:"+testResURI+"TestData/extract.zip!/";
+        Resource r = Resource.newResource(s);
+
+        assertTrue(r instanceof JarResource);
+        JarResource jarResource = (JarResource)r;
+
+        File destParent = File.createTempFile("copyjar", null);
+        if (destParent.exists())
+            destParent.delete();
+        destParent.mkdir();
+        destParent.deleteOnExit();
+
+        File dest = new File(destParent.getCanonicalPath()+"/extract");
+        if(dest.exists())
+            dest.delete();
+        dest.mkdir();
+        dest.deleteOnExit();
+
+        jarResource.copyTo(dest);
+
+        // dest contains only the valid entry; dest.getParent() contains only the dest directory
+        assertEquals(1, dest.listFiles().length);
+        assertEquals(1, dest.getParentFile().listFiles().length);
+
+        FilenameFilter dotdotFilenameFilter = new FilenameFilter() {
+            @Override
+            public boolean accept(File directory, String name)
+            {
+                return name.equals("dotdot.txt");
+            }
+        };
+        assertEquals(0, dest.listFiles(dotdotFilenameFilter).length);
+        assertEquals(0, dest.getParentFile().listFiles(dotdotFilenameFilter).length);
+
+        FilenameFilter extractfileFilenameFilter = new FilenameFilter() {
+            @Override
+            public boolean accept(File directory, String name)
+            {
+                return name.equals("extract-filenotdir");
+            }
+        };
+        assertEquals(0, dest.listFiles(extractfileFilenameFilter).length);
+        assertEquals(0, dest.getParentFile().listFiles(extractfileFilenameFilter).length);
+
+        FilenameFilter currentDirectoryFilenameFilter = new FilenameFilter() {
+            @Override
+            public boolean accept(File directory, String name)
+            {
+                return name.equals("current.txt");
+            }
+        };
+        assertEquals(1, dest.listFiles(currentDirectoryFilenameFilter).length);
+        assertEquals(0, dest.getParentFile().listFiles(currentDirectoryFilenameFilter).length);
+
+        IO.delete(dest);
+        assertFalse(dest.exists());
+    }
+
+
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/PathResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/PathResourceTest.java
deleted file mode 100644
index f642556..0000000
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/PathResourceTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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.URI;
-
-import org.junit.Ignore;
-
-@Ignore // Ignore as PathResource not utilized in 9.2 and fixed in 9.3
-public class PathResourceTest extends AbstractFSResourceTest
-{
-    @Override
-    public Resource newResource(URI uri) throws IOException
-    {
-        return new PathResource(uri);
-    }
-
-    @Override
-    public Resource newResource(File file) throws IOException
-    {
-        return new PathResource(file);
-    }
-}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java
index 3fa9d25..3937d97 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java
@@ -18,39 +18,34 @@
 
 package org.eclipse.jetty.util.resource;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assume.assumeThat;
 
 import java.io.File;
-import java.io.FilePermission;
-import java.io.FilenameFilter;
 import java.io.InputStream;
 import java.net.URI;
 import java.net.URL;
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.zip.ZipFile;
 
+import org.eclipse.jetty.toolchain.test.FS;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.toolchain.test.OS;
 import org.eclipse.jetty.util.IO;
 import org.hamcrest.Matchers;
 import org.junit.Assert;
-import org.junit.BeforeClass;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
 
-
+@RunWith(Parameterized.class)
 public class ResourceTest
 {
-    public static String __userDir = System.getProperty("basedir", ".");
-    public static URL __userURL=null;
-    private static String __relDir="";
-    private static File tmpFile;
-
     private static final boolean DIR=true;
     private static final boolean EXISTS=true;
 
@@ -98,6 +93,24 @@
             this.dir=dir;
             resource=Resource.newResource(url);
         }
+        
+        Data(URI uri,boolean exists, boolean dir)
+                throws Exception
+        {
+            this.test=uri.toASCIIString();
+            this.exists=exists;
+            this.dir=dir;
+            resource=Resource.newResource(uri);
+        }
+        
+        Data(File file,boolean exists, boolean dir)
+                throws Exception
+        {
+            this.test=file.toString();
+            this.exists=exists;
+            this.dir=dir;
+            resource=Resource.newResource(file);
+        }
 
         Data(String url,boolean exists, boolean dir, String content)
             throws Exception
@@ -108,423 +121,169 @@
             this.content=content;
             resource=Resource.newResource(url);
         }
+        
+        @Override
+        public String toString()
+        {
+            return this.test;
+        }
     }
-
-    public static Data[] data;
-
-    /* ------------------------------------------------------------ */
-    @BeforeClass
-    public static void setUp()
-    throws Exception
+    
+    static class UseCases
     {
-        if (data!=null)
-            return;
-
-        File file = new File(__userDir);
-        file=new File(file.getCanonicalPath());
-        URI uri = file.toURI();
-        __userURL=uri.toURL();
+        final Collection<Data[]> data;
+        final File fileRef;
+        final URI uriRef;
+        final String relRef;
         
-        __userURL = MavenTestingUtils.getTestResourcesDir().toURI().toURL();
-        FilePermission perm = (FilePermission) __userURL.openConnection().getPermission();
-        __userDir = new File(perm.getName()).getCanonicalPath() + File.separatorChar;
-        __relDir = "src/test/resources/".replace('/', File.separatorChar);  
+        final Data[] baseCases;
         
-        //System.err.println("User Dir="+__userDir);
-        //System.err.println("Rel  Dir="+__relDir);
-        //System.err.println("User URL="+__userURL);
-
-        tmpFile=File.createTempFile("test",null).getCanonicalFile();
-        tmpFile.deleteOnExit();
-
-        data = new Data[50];
-        int i=0;
-
-        data[i++]=new Data(tmpFile.toString(),EXISTS,!DIR);
-
-        int rt=i;
-        data[i++]=new Data(__userURL,EXISTS,DIR);
-        data[i++]=new Data(__userDir,EXISTS,DIR);
-        data[i++]=new Data(__relDir,EXISTS,DIR);
-        data[i++]=new Data(__userURL+"resource.txt",EXISTS,!DIR);
-        data[i++]=new Data(__userDir+"resource.txt",EXISTS,!DIR);
-        data[i++]=new Data(__relDir+"resource.txt",EXISTS,!DIR);
-        data[i++]=new Data(__userURL+"NoName.txt",!EXISTS,!DIR);
-        data[i++]=new Data(__userDir+"NoName.txt",!EXISTS,!DIR);
-        data[i++]=new Data(__relDir+"NoName.txt",!EXISTS,!DIR);
-
-        data[i++]=new Data(data[rt],"resource.txt",EXISTS,!DIR);
-        data[i++]=new Data(data[rt],"/resource.txt",EXISTS,!DIR);
-        data[i++]=new Data(data[rt],"NoName.txt",!EXISTS,!DIR);
-        data[i++]=new Data(data[rt],"/NoName.txt",!EXISTS,!DIR);
-
-        int td=i;
-        data[i++]=new Data(data[rt],"TestData",EXISTS,DIR);
-        data[i++]=new Data(data[rt],"TestData/",EXISTS,DIR);
-        data[i++]=new Data(data[td],"alphabet.txt",EXISTS,!DIR,"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-
-        data[i++]=new Data("jar:file:/somejar.jar!/content/",!EXISTS,DIR);
-        data[i++]=new Data("jar:file:/somejar.jar!/",!EXISTS,DIR);
-
-        int tj=i;
-        data[i++]=new Data("jar:"+__userURL+"TestData/test.zip!/",EXISTS,DIR);
-        data[i++]=new Data(data[tj],"Unkown",!EXISTS,!DIR);
-        data[i++]=new Data(data[tj],"/Unkown/",!EXISTS,DIR);
-
-        data[i++]=new Data(data[tj],"subdir",EXISTS,DIR);
-        data[i++]=new Data(data[tj],"/subdir/",EXISTS,DIR);
-        data[i++]=new Data(data[tj],"alphabet",EXISTS,!DIR,
-                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-        data[i++]=new Data(data[tj],"/subdir/alphabet",EXISTS,!DIR,
-                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-
-        Resource base = Resource.newResource(__userDir);
-        Resource dir0 = base.addPath("TestData");
-        assertTrue(dir0.isDirectory());
-        assertTrue(dir0.toString().endsWith("/"));
-        assertTrue(dir0.getAlias()==null);
-        Resource dir1 = base.addPath("TestData/");
-        assertTrue(dir1.isDirectory());
-        assertTrue(dir1.toString().endsWith("/"));
-        assertTrue(dir1.getAlias()==null);
-
-
+        public UseCases(String ref) throws Exception {
+            this.data = new ArrayList<Data[]>();
+            // relative directory reference
+            this.relRef = OS.separators(ref);
+            // File object reference
+            this.fileRef = MavenTestingUtils.getProjectDir(relRef);
+            // URI reference
+            this.uriRef = fileRef.toURI();
+            
+            // create baseline cases
+            baseCases = new Data[] { 
+                new Data(relRef,EXISTS,DIR), 
+                new Data(uriRef,EXISTS,DIR), 
+                new Data(fileRef,EXISTS,DIR) 
+            };
+            
+            // add all baseline cases
+            for (Data bcase : baseCases)
+            {
+                addCase(bcase);
+            }
+        }
+        
+        public void addCase(Data ucase)
+        {
+            this.data.add(new Data[]{ ucase });
+        }
+        
+        public void addAllSimpleCases(String subpath, boolean exists, boolean dir) 
+            throws Exception
+        {
+            addCase(new Data(OS.separators(relRef + subpath), exists, dir));
+            addCase(new Data(uriRef.resolve(subpath).toURL(), exists, dir));
+            addCase(new Data(new File(fileRef,subpath),exists, dir));
+        }
+        
+        public Data addAllAddPathCases(String subpath, boolean exists, boolean dir) throws Exception
+        {
+            Data bdata = null;
+            
+            for (Data bcase : baseCases)
+            {
+                bdata = new Data(bcase, subpath, exists, dir);
+                addCase(bdata);
+            }
+            
+            return bdata;
+        }
     }
+    
 
-    /* ------------------------------------------------------------ */
+    @Parameters(name="{0}")
+    public static Collection<Data[]> data() throws Exception
+    {
+        UseCases cases = new UseCases("src/test/resources/");
+        
+        File testDir = MavenTestingUtils.getTargetTestingDir(ResourceTest.class.getName());
+        FS.ensureEmpty(testDir);
+        File tmpFile = File.createTempFile("test",null,testDir);
+        
+        cases.addCase(new Data(tmpFile.toString(),EXISTS,!DIR));
+        
+        // Some resource references.
+        cases.addAllSimpleCases("resource.txt",EXISTS,!DIR);
+        cases.addAllSimpleCases("NoName.txt",!EXISTS,!DIR);
+        
+        // Some addPath() forms
+        cases.addAllAddPathCases("resource.txt",EXISTS,!DIR);
+        cases.addAllAddPathCases("/resource.txt",EXISTS,!DIR);
+        cases.addAllAddPathCases("//resource.txt",EXISTS,!DIR);
+        cases.addAllAddPathCases("NoName.txt",!EXISTS,!DIR);
+        cases.addAllAddPathCases("/NoName.txt",!EXISTS,!DIR);
+        cases.addAllAddPathCases("//NoName.txt",!EXISTS,!DIR);
+
+        Data tdata1 = cases.addAllAddPathCases("TestData", EXISTS, DIR);
+        Data tdata2 = cases.addAllAddPathCases("TestData/", EXISTS, DIR);
+        
+        cases.addCase(new Data(tdata1, "alphabet.txt", EXISTS,!DIR,"ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+        cases.addCase(new Data(tdata2, "alphabet.txt", EXISTS,!DIR,"ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+
+        cases.addCase(new Data("jar:file:/somejar.jar!/content/",!EXISTS,DIR));
+        cases.addCase(new Data("jar:file:/somejar.jar!/",!EXISTS,DIR));
+
+        String urlRef = cases.uriRef.toASCIIString();
+        Data zdata = new Data("jar:"+urlRef +"TestData/test.zip!/",EXISTS,DIR);
+        cases.addCase(zdata);
+        cases.addCase(new Data(zdata,"Unkown",!EXISTS,!DIR));
+        cases.addCase(new Data(zdata,"/Unkown/",!EXISTS,DIR));
+
+        cases.addCase(new Data(zdata,"subdir",EXISTS,DIR));
+        cases.addCase(new Data(zdata,"/subdir/",EXISTS,DIR));
+        cases.addCase(new Data(zdata,"alphabet",EXISTS,!DIR,
+                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+        cases.addCase(new Data(zdata,"/subdir/alphabet",EXISTS,!DIR,
+                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+        
+        cases.addAllAddPathCases("/TestData/test/subdir/subsubdir/",EXISTS,DIR);
+        cases.addAllAddPathCases("//TestData/test/subdir/subsubdir/",EXISTS,DIR);
+        cases.addAllAddPathCases("/TestData//test/subdir/subsubdir/",EXISTS,DIR);
+        cases.addAllAddPathCases("/TestData/test//subdir/subsubdir/",EXISTS,DIR);
+        cases.addAllAddPathCases("/TestData/test/subdir//subsubdir/",EXISTS,DIR);
+        
+        cases.addAllAddPathCases("TestData/test/subdir/subsubdir/",EXISTS,DIR);
+        cases.addAllAddPathCases("TestData/test/subdir/subsubdir//",EXISTS,DIR);
+        cases.addAllAddPathCases("TestData/test/subdir//subsubdir/",EXISTS,DIR);
+        cases.addAllAddPathCases("TestData/test//subdir/subsubdir/",EXISTS,DIR);
+
+        cases.addAllAddPathCases("/TestData/../TestData/test/subdir/subsubdir/",EXISTS,DIR);
+
+        return cases.data;
+    }
+    
+    @Parameter(value=0)
+    public Data data;
+
     @Test
     public void testResourceExists()
     {
-        for (int i=0;i<data.length;i++)
-        {
-            if (data[i]==null)
-                continue;
-
-            assertEquals(""+i+":"+data[i].test,data[i].exists,data[i].resource.exists());
-        }
+        assertThat("Exists: " + data.resource.getName(), data.resource.exists(), equalTo(data.exists));
     }
 
-    /* ------------------------------------------------------------ */
     @Test
     public void testResourceDir()
     {
-        for (int i=0;i<data.length;i++)
-        {
-            if (data[i]==null)
-                continue;
-
-            assertEquals(""+i+":"+data[i].test,data[i].dir,data[i].resource.isDirectory());
-        }
+        assertThat("Is Directory: " + data.test, data.resource.isDirectory(),equalTo(data.dir));
     }
 
-    /* ------------------------------------------------------------ */
-    @Test
-    public void testResourceContent()
-        throws Exception
-    {
-        for (int i=0;i<data.length;i++)
-        {
-            if (data[i]==null || data[i].content==null)
-                continue;
-
-            InputStream in = data[i].resource.getInputStream();
-            String c=IO.toString(in);
-            assertTrue(""+i+":"+data[i].test,c.startsWith(data[i].content));
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    @Test
-    public void testEncoding() throws Exception
-    {
-        Resource r =Resource.newResource("/tmp/a file with,spe#ials/");
-        assertTrue(r.getURL().toString().indexOf("a%20file%20with,spe%23ials")>0);
-        assertTrue(r.getFile().toString().indexOf("a file with,spe#ials")>0);
-        r.delete();
-        assertFalse("File should have been deleted.",r.exists());
-    }
-
-    /* ------------------------------------------------------------ */
-    @Test
-    public void testJarFile()
-    throws Exception
-    {
-        String s = "jar:"+__userURL+"TestData/test.zip!/subdir/";
-        Resource r = Resource.newResource(s);
-
-        Set<String> entries = new HashSet<>(Arrays.asList(r.list()));
-        assertEquals(3,entries.size());
-        assertTrue(entries.contains("alphabet"));
-        assertTrue(entries.contains("numbers"));
-        assertTrue(entries.contains("subsubdir/"));
-
-        File extract = File.createTempFile("extract", null);
-        if (extract.exists())
-            extract.delete();
-        extract.mkdir();
-        extract.deleteOnExit();
-
-        r.copyTo(extract);
-
-        Resource e = Resource.newResource(extract.getAbsolutePath());
-
-        entries = new HashSet<>(Arrays.asList(e.list()));
-        assertEquals(3,entries.size());
-        assertTrue(entries.contains("alphabet"));
-        assertTrue(entries.contains("numbers"));
-        assertTrue(entries.contains("subsubdir/"));
-        IO.delete(extract);
-
-        s = "jar:"+__userURL+"TestData/test.zip!/subdir/subsubdir/";
-        r = Resource.newResource(s);
-
-        entries = new HashSet<>(Arrays.asList(r.list()));
-        assertEquals(2,entries.size());
-        assertTrue(entries.contains("alphabet"));
-        assertTrue(entries.contains("numbers"));
-
-        extract = File.createTempFile("extract", null);
-        if (extract.exists())
-            extract.delete();
-        extract.mkdir();
-        extract.deleteOnExit();
-
-        r.copyTo(extract);
-
-        e = Resource.newResource(extract.getAbsolutePath());
-
-        entries = new HashSet<>(Arrays.asList(e.list()));
-        assertEquals(2,entries.size());
-        assertTrue(entries.contains("alphabet"));
-        assertTrue(entries.contains("numbers"));
-        IO.delete(extract);
-
-    }
-
-    /* ------------------------------------------------------------ */
-    @Test
-    public void testJarFileGetAllResoures()
-    throws Exception
-    {
-        String s = "jar:"+__userURL+"TestData/test.zip!/subdir/";
-        Resource r = Resource.newResource(s);
-        Collection<Resource> deep=r.getAllResources();
-        
-        assertEquals(4, deep.size());
-    }
-    
-    @Test
-    public void testJarFileIsContainedIn ()
-    throws Exception
-    {
-        String s = "jar:"+__userURL+"TestData/test.zip!/subdir/";
-        Resource r = Resource.newResource(s);
-        Resource container = Resource.newResource(__userURL+"TestData/test.zip");
-
-        assertTrue(r instanceof JarFileResource);
-        JarFileResource jarFileResource = (JarFileResource)r;
-
-        assertTrue(jarFileResource.isContainedIn(container));
-
-        container = Resource.newResource(__userURL+"TestData");
-        assertFalse(jarFileResource.isContainedIn(container));
-    }
-
-    /* ------------------------------------------------------------ */
-    @Test
-    public void testJarFileLastModified ()
-    throws Exception
-    {
-        String s = "jar:"+__userURL+"TestData/test.zip!/subdir/numbers";
-
-        try(ZipFile zf = new ZipFile(MavenTestingUtils.getTestResourceFile("TestData/test.zip")))
-        {
-            long last = zf.getEntry("subdir/numbers").getTime();
-
-            Resource r = Resource.newResource(s);
-            assertEquals(last,r.lastModified());
-        }
-    }
-
-    /* ------------------------------------------------------------ */
     @Test
     public void testEncodeAddPath ()
     throws Exception
     {
-        Resource r;
-
-        r = Resource.newResource(__userURL+"TestData/").addPath("foo%/b r");
-        Assert.assertThat(r.getURI().toString(),Matchers.endsWith("/foo%25/b%20r"));
-        
-        r = Resource.newResource("jar:"+__userURL+"TestData/test.zip!/subdir/").addPath("foo%/b r");
-        Assert.assertThat(r.getURI().toString(),Matchers.endsWith("/foo%25/b%20r"));
-    }
-    
-    
-    /* ------------------------------------------------------------ */
-    @Test
-    public void testJarFileCopyToDirectoryTraversal () throws Exception
-    {
-        String s = "jar:"+__userURL+"TestData/extract.zip!/";
-        Resource r = Resource.newResource(s);
-
-        assertTrue(r instanceof JarResource);
-        JarResource jarResource = (JarResource)r;
-
-        File destParent = File.createTempFile("copyjar", null);
-        if (destParent.exists())
-            destParent.delete();
-        destParent.mkdir();
-        destParent.deleteOnExit();
-
-        File dest = new File(destParent.getCanonicalPath()+"/extract");
-        if(dest.exists())
-            dest.delete();
-        dest.mkdir();
-        dest.deleteOnExit();
-
-        jarResource.copyTo(dest);
-
-        // dest contains only the valid entry; dest.getParent() contains only the dest directory
-        assertEquals(1, dest.listFiles().length);
-        assertEquals(1, dest.getParentFile().listFiles().length);
-
-        FilenameFilter dotdotFilenameFilter = new FilenameFilter() {
-            @Override
-            public boolean accept(File directory, String name)
-            {
-                return name.equals("dotdot.txt");
-            }
-        };
-        assertEquals(0, dest.listFiles(dotdotFilenameFilter).length);
-        assertEquals(0, dest.getParentFile().listFiles(dotdotFilenameFilter).length);
-
-        FilenameFilter extractfileFilenameFilter = new FilenameFilter() {
-            @Override
-            public boolean accept(File directory, String name)
-            {
-                return name.equals("extract-filenotdir");
-            }
-        };
-        assertEquals(0, dest.listFiles(extractfileFilenameFilter).length);
-        assertEquals(0, dest.getParentFile().listFiles(extractfileFilenameFilter).length);
-
-        FilenameFilter currentDirectoryFilenameFilter = new FilenameFilter() {
-            @Override
-            public boolean accept(File directory, String name)
-            {
-                return name.equals("current.txt");
-            }
-        };
-        assertEquals(1, dest.listFiles(currentDirectoryFilenameFilter).length);
-        assertEquals(0, dest.getParentFile().listFiles(currentDirectoryFilenameFilter).length);
-
-        IO.delete(dest);
-        assertFalse(dest.exists());
-    }
-
-    /**
-     * Test a class path resource for existence.
-     */
-    @Test
-    public void testClassPathResourceClassRelative()
-    {
-        final String classPathName="Resource.class";
-
-        try(Resource resource=Resource.newClassPathResource(classPathName);)
+        if (data.dir)
         {
-            // A class path cannot be a directory
-            assertFalse("Class path cannot be a directory.",resource.isDirectory());
-
-            // A class path must exist
-            assertTrue("Class path resource does not exist.",resource.exists());
+            Resource r = data.resource.addPath("foo%/b r");
+            Assert.assertThat(r.getURI().toString(),Matchers.endsWith("/foo%25/b%20r"));
         }
     }
-
-    /**
-     * Test a class path resource for existence.
-     */
+    
     @Test
-    public void testClassPathResourceClassAbsolute()
+    public void testResourceContent()
+        throws Exception
     {
-        final String classPathName="/org/eclipse/jetty/util/resource/Resource.class";
-
-        Resource resource=Resource.newClassPathResource(classPathName);
-
-        // A class path cannot be a directory
-        assertFalse("Class path cannot be a directory.",resource.isDirectory());
-
-        // A class path must exist
-        assertTrue("Class path resource does not exist.",resource.exists());
-    }
-
-    /**
-     * Test a class path resource for directories.
-     */
-    @Test
-    public void testClassPathResourceDirectory() throws Exception
-    {
-        final String classPathName="/";
-
-        Resource resource=Resource.newClassPathResource(classPathName);
-
-        // A class path must be a directory
-        assertTrue("Class path must be a directory.",resource.isDirectory());
-
-        assertTrue("Class path returned file must be a directory.",resource.getFile().isDirectory());
-
-        // A class path must exist
-        assertTrue("Class path resource does not exist.",resource.exists());
-    }
-
-    /**
-     * Test a class path resource for a file.
-     */
-    @Test
-    public void testClassPathResourceFile() throws Exception
-    {
-        final String fileName="resource.txt";
-        final String classPathName="/"+fileName;
-
-        // Will locate a resource in the class path
-        Resource resource=Resource.newClassPathResource(classPathName);
-
-        // A class path cannot be a directory
-        assertFalse("Class path must be a directory.",resource.isDirectory());
-
-        assertTrue(resource!=null);
-
-        File file=resource.getFile();
-
-        assertEquals("File name from class path is not equal.",fileName,file.getName());
-        assertTrue("File returned from class path should be a file.",file.isFile());
-
-        // A class path must exist
-        assertTrue("Class path resource does not exist.",resource.exists());
-    }
-
-    @Test
-    public void testUncPathResourceFile() throws Exception
-    {
-        // This test is intended to run only on Windows platform
-        assumeTrue(OS.IS_WINDOWS);
-
-        String path = __userURL.toURI().getPath().replace('/','\\')+"resource.txt";
-        //System.err.println(path);
-
-        Resource resource = Resource.newResource(path, false);
-        //System.err.println(resource);
-        assertTrue(resource.exists());
-
-        /*
-
-        String uncPath = "\\\\127.0.0.1"+__userURL.toURI().getPath().replace('/','\\').replace(':','$')+"ResourceTest.java";
-        System.err.println(uncPath);
-
-        Resource uncResource = Resource.newResource(uncPath, false);
-        System.err.println(uncResource);
-        assertTrue(uncResource.exists());
-
-        */
+        assumeThat(data.content, notNullValue());
+        
+        InputStream in = data.resource.getInputStream();
+        String c = IO.toString(in);
+        assertThat("Content: " + data.test,c,startsWith(data.content));
     }
 }
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
index d6a2e99..2a9021f 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
@@ -18,9 +18,10 @@
 
 package org.eclipse.jetty.util.ssl;
 
+import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThan;
-import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
@@ -32,11 +33,10 @@
 import javax.net.ssl.SSLEngine;
 
 import org.eclipse.jetty.toolchain.test.JDK;
-import org.eclipse.jetty.toolchain.test.OS;
 import org.eclipse.jetty.util.component.AbstractLifeCycle;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.eclipse.jetty.util.resource.Resource;
+import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Before;
@@ -55,9 +55,19 @@
     }
 
     @Test
+    public void testSLOTH() throws Exception
+    {
+        cf.setKeyStorePassword("storepwd");
+        cf.setKeyManagerPassword("keypwd");
+
+        cf.start();
+        
+        cf.dump(System.out, "");
+    }
+    
+    @Test
     public void testNoTsFileKs() throws Exception
     {
-        String keystorePath = System.getProperty("basedir",".") + "/src/test/resources/keystore";
         cf.setKeyStorePassword("storepwd");
         cf.setKeyManagerPassword("keypwd");
 
@@ -104,6 +114,8 @@
         cf.setKeyStoreResource(keystoreResource);
         cf.setKeyStorePassword("storepwd");
         cf.setKeyManagerPassword("keypwd");
+        cf.setTrustStoreResource(keystoreResource);
+        cf.setTrustStorePassword(null);
 
         cf.start();
 
@@ -130,7 +142,6 @@
     @Test
     public void testResourceTsResourceKsWrongPW() throws Exception
     {
-        SslContextFactory.LOG.info("EXPECT SslContextFactory@????????(null,null): java.security.UnrecoverableKeyException: Cannot recover key...");
         Resource keystoreResource = Resource.newSystemResource("keystore");
         Resource truststoreResource = Resource.newSystemResource("keystore");
 
@@ -140,21 +151,20 @@
         cf.setKeyManagerPassword("wrong_keypwd");
         cf.setTrustStorePassword("storepwd");
 
-        try
+        try (StacklessLogging stackless = new StacklessLogging(AbstractLifeCycle.class))
         {
-            ((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
             cf.start();
             Assert.fail();
         }
         catch(java.security.UnrecoverableKeyException e)
         {
+            Assert.assertThat(e.toString(),Matchers.containsString("UnrecoverableKeyException"));
         }
     }
 
     @Test
     public void testResourceTsWrongPWResourceKs() throws Exception
     {
-        SslContextFactory.LOG.info("EXPECT SslContextFactory@????????(null,null): java.io.IOException: Keystore was tampered with ...");
         Resource keystoreResource = Resource.newSystemResource("keystore");
         Resource truststoreResource = Resource.newSystemResource("keystore");
 
@@ -164,35 +174,29 @@
         cf.setKeyManagerPassword("keypwd");
         cf.setTrustStorePassword("wrong_storepwd");
 
-        try
+        try (StacklessLogging stackless = new StacklessLogging(AbstractLifeCycle.class))
         {
-            ((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
             cf.start();
             Assert.fail();
         }
         catch(IOException e)
         {
+            Assert.assertThat(e.toString(),Matchers.containsString("java.io.IOException: Keystore was tampered with, or password was incorrect"));
         }
     }
 
     @Test
     public void testNoKeyConfig() throws Exception
     {
-        try
+        try (StacklessLogging stackless = new StacklessLogging(AbstractLifeCycle.class))
         {
-            SslContextFactory.LOG.info("EXPECT SslContextFactory@????????(null,/foo): java.lang.IllegalStateException: SSL doesn't have a valid keystore...");
-            ((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
             cf.setTrustStorePath("/foo");
             cf.start();
             Assert.fail();
         }
         catch (IllegalStateException e)
         {
-
-        }
-        catch (Exception e)
-        {
-            Assert.fail("Unexpected exception");
+            Assert.assertThat(e.toString(),Matchers.containsString("IllegalStateException: no valid keystore"));
         }
     }
 
@@ -205,46 +209,21 @@
         String[] enabledCipherSuites = sslEngine.getEnabledCipherSuites();
         assertThat("At least 1 cipherSuite is enabled", enabledCipherSuites.length, greaterThan(0));
         for (String enabledCipherSuite : enabledCipherSuites)
-            assertThat("CipherSuite does not contain RC4", enabledCipherSuite.contains("RC4"), is(false));
+            assertThat("CipherSuite does not contain RC4", enabledCipherSuite.contains("RC4"), equalTo(false));
     }
 
     @Test
     public void testSetIncludeCipherSuitesRegex() throws Exception
     {
-        // Test does not work on JDK 8+ (RC4 is disabled)
+        cf.setIncludeCipherSuites(".*ECDHE.*",".*WIBBLE.*");
         Assume.assumeFalse(JDK.IS_8);
         
-        cf.setIncludeCipherSuites(".*RC4.*");
         cf.start();
         SSLEngine sslEngine = cf.newSSLEngine();
         String[] enabledCipherSuites = sslEngine.getEnabledCipherSuites();
-        assertThat("At least 1 cipherSuite is enabled", enabledCipherSuites.length, greaterThan(0));
+        assertThat("At least 1 cipherSuite is enabled", enabledCipherSuites.length, greaterThan(1));
         for (String enabledCipherSuite : enabledCipherSuites)
-            assertThat("CipherSuite contains RC4", enabledCipherSuite.contains("RC4"), is(true));
-    }
-
-    @Test
-    public void testSetIncludeCipherSuitesPreservesOrder()
-    {
-        String[] supportedCipherSuites = new String[]{"cipher4", "cipher2", "cipher1", "cipher3"};
-        String[] includeCipherSuites = {"cipher1", "cipher3", "cipher4"};
-
-        cf.setIncludeCipherSuites(includeCipherSuites);
-        String[] selectedCipherSuites = cf.selectCipherSuites(null, supportedCipherSuites);
-
-        assertSelectedMatchesIncluded(includeCipherSuites, selectedCipherSuites);
-    }
-
-    @Test
-    public void testSetIncludeProtocolsPreservesOrder()
-    {
-        String[] supportedProtocol = new String[]{"cipher4", "cipher2", "cipher1", "cipher3"};
-        String[] includeProtocol = {"cipher1", "cipher3", "cipher4"};
-
-        cf.setIncludeProtocols(includeProtocol);
-        String[] selectedProtocol = cf.selectProtocols(null, supportedProtocol);
-
-        assertSelectedMatchesIncluded(includeProtocol, selectedProtocol);
+            assertThat("CipherSuite contains ECDHE", enabledCipherSuite.contains("ECDHE"), equalTo(true));
     }
 
     @Test
@@ -256,11 +235,44 @@
     	assertNotNull(cf.getIncludeCipherSuites());
     }
     
-    private void assertSelectedMatchesIncluded(String[] includeStrings, String[] selectedStrings)
+    @Test
+    public void testSNICertificates() throws Exception
     {
-        assertThat(includeStrings.length + " strings are selected", selectedStrings.length, is(includeStrings.length));
-        assertThat("order from includeStrings is preserved", selectedStrings[0], equalTo(includeStrings[0]));
-        assertThat("order from includeStrings is preserved", selectedStrings[1], equalTo(includeStrings[1]));
-        assertThat("order from includeStrings is preserved", selectedStrings[2], equalTo(includeStrings[2]));
+        Resource keystoreResource = Resource.newSystemResource("snikeystore");
+
+        cf.setKeyStoreResource(keystoreResource);
+        cf.setKeyStorePassword("storepwd");
+        cf.setKeyManagerPassword("keypwd");
+        
+        cf.start();
+        
+        assertThat(cf.getAliases(),containsInAnyOrder("jetty","other","san","wild"));
+        
+        assertThat(cf.getX509("jetty").getHosts(),containsInAnyOrder("jetty.eclipse.org"));
+        assertTrue(cf.getX509("jetty").getWilds().isEmpty());
+        assertTrue(cf.getX509("jetty").matches("JETTY.Eclipse.Org"));
+        assertFalse(cf.getX509("jetty").matches("m.jetty.eclipse.org"));
+        assertFalse(cf.getX509("jetty").matches("eclipse.org"));
+        
+        assertThat(cf.getX509("other").getHosts(),containsInAnyOrder("www.example.com"));
+        assertTrue(cf.getX509("other").getWilds().isEmpty());
+        assertTrue(cf.getX509("other").matches("www.example.com"));
+        assertFalse(cf.getX509("other").matches("eclipse.org"));
+        
+        assertThat(cf.getX509("san").getHosts(),containsInAnyOrder("www.san.com","m.san.com"));
+        assertTrue(cf.getX509("san").getWilds().isEmpty());
+        assertTrue(cf.getX509("san").matches("www.san.com"));
+        assertTrue(cf.getX509("san").matches("m.san.com"));
+        assertFalse(cf.getX509("san").matches("other.san.com"));
+        assertFalse(cf.getX509("san").matches("san.com"));
+        assertFalse(cf.getX509("san").matches("eclipse.org"));
+        
+        assertTrue(cf.getX509("wild").getHosts().isEmpty());
+        assertThat(cf.getX509("wild").getWilds(),containsInAnyOrder("domain.com"));
+        assertTrue(cf.getX509("wild").matches("domain.com"));
+        assertTrue(cf.getX509("wild").matches("www.domain.com"));
+        assertTrue(cf.getX509("wild").matches("other.domain.com"));
+        assertFalse(cf.getX509("wild").matches("foo.bar.domain.com"));
+        assertFalse(cf.getX509("wild").matches("other.com"));
     }
 }
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/statistic/CounterStatisticTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/statistic/CounterStatisticTest.java
new file mode 100644
index 0000000..a624f65
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/statistic/CounterStatisticTest.java
@@ -0,0 +1,157 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.junit.Assert.assertThat;
+
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import org.junit.Test;
+
+
+/* ------------------------------------------------------------ */
+public class CounterStatisticTest
+{
+
+    @Test
+    public void testCounter()
+        throws Exception
+    {
+        CounterStatistic count = new CounterStatistic();
+        
+        assertThat(count.getCurrent(),equalTo(0L));
+        assertThat(count.getMax(),equalTo(0L));
+        assertThat(count.getTotal(),equalTo(0L));
+        
+        count.increment();
+        count.increment();
+        count.decrement();
+        count.add(4);
+        count.add(-2);
+
+        assertThat(count.getCurrent(),equalTo(3L));
+        assertThat(count.getMax(),equalTo(5L));
+        assertThat(count.getTotal(),equalTo(6L));
+        
+        count.reset();
+        assertThat(count.getCurrent(),equalTo(3L));
+        assertThat(count.getMax(),equalTo(3L));
+        assertThat(count.getTotal(),equalTo(3L));
+
+        count.increment();
+        count.decrement();
+        count.add(-2);
+        count.decrement();
+        assertThat(count.getCurrent(),equalTo(0L));
+        assertThat(count.getMax(),equalTo(4L));
+        assertThat(count.getTotal(),equalTo(4L));
+     
+        count.decrement();
+        assertThat(count.getCurrent(),equalTo(-1L));
+        assertThat(count.getMax(),equalTo(4L));
+        assertThat(count.getTotal(),equalTo(4L));
+
+        count.increment();
+        assertThat(count.getCurrent(),equalTo(0L));
+        assertThat(count.getMax(),equalTo(4L));
+        assertThat(count.getTotal(),equalTo(5L));
+    }
+    
+
+    @Test
+    public void testCounterContended()
+        throws Exception
+    {
+        final CounterStatistic counter = new CounterStatistic();
+        final int N=100;
+        final int L=1000;
+        final Thread[] threads = new Thread[N];
+        final CyclicBarrier incBarrier = new CyclicBarrier(N);
+        final CountDownLatch decBarrier = new CountDownLatch(N/2);
+        
+        for (int i=N;i-->0;)
+        {
+            final int I = i;
+      
+                
+            threads[i]=(i>=N/2)
+            ?new Thread()
+            {
+                @Override
+                public void run()
+                {
+                    try
+                    {
+                        incBarrier.await();
+                        decBarrier.await();
+                    }
+                    catch (Exception e)
+                    {
+                        throw new RuntimeException(e);
+                    }
+                    Random random = new Random();
+                    for (int l=L;l-->0;)
+                    {
+                        counter.decrement();
+                        if (random.nextInt(5)==0)
+                            Thread.yield();
+                    }
+                }
+            }
+            :new Thread()
+            {
+                @Override
+                public void run()
+                {
+                    try
+                    {
+                        incBarrier.await();
+                    }
+                    catch (Exception e)
+                    {
+                        throw new RuntimeException(e);
+                    }
+                    Random random = new Random();
+                    for (int l=L;l-->0;)
+                    {
+                        counter.increment();
+                        if (l==L/2)
+                            decBarrier.countDown();
+                        if (random.nextInt(5)==0)
+                            Thread.yield();
+                    }
+                }
+            };
+            threads[i].start();
+        }
+        
+
+        for (int i=N;i-->0;)
+            threads[i].join();
+        
+        assertThat(counter.getCurrent(),equalTo(0L));
+        assertThat(counter.getTotal(),equalTo(N*L/2L));
+        assertThat(counter.getMax(),greaterThanOrEqualTo((N/2)*(L/2L)));
+    }
+   
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/LockerTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/LockerTest.java
new file mode 100644
index 0000000..29752e2
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/LockerTest.java
@@ -0,0 +1,141 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class LockerTest
+{
+    public LockerTest()
+    {
+    }
+
+    @Test
+    public void testLocked()
+    {
+        Locker lock = new Locker();
+        assertFalse(lock.isLocked());
+
+        try(Locker.Lock l = lock.lock())
+        {
+            assertTrue(lock.isLocked());
+        }
+        finally
+        {
+            assertFalse(lock.isLocked());
+        }
+
+        assertFalse(lock.isLocked());
+    }
+
+    @Test
+    public void testLockedException()
+    {
+        Locker lock = new Locker();
+        assertFalse(lock.isLocked());
+
+        try(Locker.Lock l = lock.lock())
+        {
+            assertTrue(lock.isLocked());
+            throw new Exception();
+        }
+        catch(Exception e)
+        {
+            assertFalse(lock.isLocked());
+        }
+        finally
+        {
+            assertFalse(lock.isLocked());
+        }
+
+        assertFalse(lock.isLocked());
+    }
+
+    @Test
+    public void testContend() throws Exception
+    {
+        final Locker lock = new Locker();
+
+        final CountDownLatch held0 = new CountDownLatch(1);
+        final CountDownLatch hold0 = new CountDownLatch(1);
+
+        Thread thread0 = new Thread()
+        {
+            @Override
+            public void run()
+            {
+                try(Locker.Lock l = lock.lock())
+                {
+                    held0.countDown();
+                    hold0.await();
+                }
+                catch (InterruptedException e)
+                {
+                    e.printStackTrace();
+                }
+            }
+        };
+        thread0.start();
+        held0.await();
+
+        assertTrue(lock.isLocked());
+
+
+        final CountDownLatch held1 = new CountDownLatch(1);
+        final CountDownLatch hold1 = new CountDownLatch(1);
+        Thread thread1 = new Thread()
+        {
+            @Override
+            public void run()
+            {
+                try(Locker.Lock l = lock.lock())
+                {
+                    held1.countDown();
+                    hold1.await();
+                }
+                catch (InterruptedException e)
+                {
+                    e.printStackTrace();
+                }
+            }
+        };
+        thread1.start();
+        // thread1 will be spinning here
+        assertFalse(held1.await(100, TimeUnit.MILLISECONDS));
+
+        // Let thread0 complete
+        hold0.countDown();
+        thread0.join();
+
+        // thread1 can progress
+        held1.await();
+
+        // let thread1 complete
+        hold1.countDown();
+        thread1.join();
+
+        assertFalse(lock.isLocked());
+    }
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java
index c79ac94..7cfe195 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java
@@ -18,23 +18,22 @@
 
 package org.eclipse.jetty.util.thread;
 
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.eclipse.jetty.toolchain.test.AdvancedRunner;
 import org.eclipse.jetty.toolchain.test.annotation.Slow;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.hamcrest.Matchers.greaterThanOrEqualTo;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
 @RunWith(AdvancedRunner.class)
 public class QueuedThreadPoolTest
 {
@@ -271,9 +270,8 @@
         tp.setMaxThreads(10);
         tp.setIdleTimeout(1000);
         tp.start();
-        try
+        try (StacklessLogging stackless = new StacklessLogging(QueuedThreadPool.class))
         {
-            ((StdErrLog)Log.getLogger(QueuedThreadPool.class)).setHideStacks(true);
             tp.execute(new Runnable(){ public void run () { throw new IllegalStateException(); } });
             tp.execute(new Runnable(){ public void run () { throw new Error(); } });
             tp.execute(new Runnable(){ public void run () { throw new RuntimeException(); } });
@@ -282,10 +280,6 @@
             Thread.sleep(100);
             assertThat(tp.getThreads(),greaterThanOrEqualTo(5));
         }
-        finally
-        {
-            ((StdErrLog)Log.getLogger(QueuedThreadPool.class)).setHideStacks(false);
-        }
     }
 
     @Test
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/SchedulerTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/SchedulerTest.java
index 7515bfc..5654c96 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/SchedulerTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/SchedulerTest.java
@@ -28,6 +28,7 @@
 
 import org.eclipse.jetty.toolchain.perf.PlatformMonitor;
 import org.eclipse.jetty.toolchain.test.annotation.Slow;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.eclipse.jetty.util.statistic.SampleStatistic;
 import org.hamcrest.Matchers;
 import org.junit.After;
@@ -171,31 +172,34 @@
     @Test
     public void testTaskThrowsException() throws Exception
     {
-        long delay = 500;
-        _scheduler.schedule(new Runnable()
+        try (StacklessLogging stackless = new StacklessLogging(TimerScheduler.class))
         {
-            @Override
-            public void run()
+            long delay = 500;
+            _scheduler.schedule(new Runnable()
             {
-                throw new RuntimeException();
-            }
-        }, delay, TimeUnit.MILLISECONDS);
+                @Override
+                public void run()
+                {
+                    throw new RuntimeException("Thrown by testTaskThrowsException");
+                }
+            }, delay, TimeUnit.MILLISECONDS);
 
-        TimeUnit.MILLISECONDS.sleep(2 * delay);
+            TimeUnit.MILLISECONDS.sleep(2 * delay);
 
-        // Check whether after a task throwing an exception, the scheduler is still working
+            // Check whether after a task throwing an exception, the scheduler is still working
 
-        final CountDownLatch latch = new CountDownLatch(1);
-        _scheduler.schedule(new Runnable()
-        {
-            @Override
-            public void run()
+            final CountDownLatch latch = new CountDownLatch(1);
+            _scheduler.schedule(new Runnable()
             {
-                latch.countDown();
-            }
-        }, delay, TimeUnit.MILLISECONDS);
+                @Override
+                public void run()
+                {
+                    latch.countDown();
+                }
+            }, delay, TimeUnit.MILLISECONDS);
 
-        Assert.assertTrue(latch.await(2 * delay, TimeUnit.MILLISECONDS));
+            Assert.assertTrue(latch.await(2 * delay, TimeUnit.MILLISECONDS));
+        }
     }
 
     @Test
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/SweeperTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/SweeperTest.java
index 9216821..7255458 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/SweeperTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/SweeperTest.java
@@ -21,6 +21,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -91,34 +92,37 @@
     @Test
     public void testSweepThrows() throws Exception
     {
-        long period = 500;
-        final CountDownLatch taskLatch = new CountDownLatch(2);
-        Sweeper sweeper = new Sweeper(scheduler, period)
+        try(StacklessLogging scope = new StacklessLogging(Sweeper.class))
         {
-            @Override
-            public void run()
+            long period = 500;
+            final CountDownLatch taskLatch = new CountDownLatch(2);
+            Sweeper sweeper = new Sweeper(scheduler, period)
             {
-                super.run();
-                taskLatch.countDown();
-            }
-        };
-        sweeper.start();
+                @Override
+                public void run()
+                {
+                    super.run();
+                    taskLatch.countDown();
+                }
+            };
+            sweeper.start();
 
-        final CountDownLatch sweepLatch = new CountDownLatch(2);
-        sweeper.offer(new Sweeper.Sweepable()
-        {
-            @Override
-            public boolean sweep()
+            final CountDownLatch sweepLatch = new CountDownLatch(2);
+            sweeper.offer(new Sweeper.Sweepable()
             {
-                sweepLatch.countDown();
-                throw new NullPointerException();
-            }
-        });
+                @Override
+                public boolean sweep()
+                {
+                    sweepLatch.countDown();
+                    throw new NullPointerException("Test exception!");
+                }
+            });
 
-        Assert.assertTrue(sweepLatch.await(4 * period, TimeUnit.MILLISECONDS));
-        Assert.assertTrue(taskLatch.await(4 * period, TimeUnit.MILLISECONDS));
-        Assert.assertEquals(1, sweeper.getSize());
+            Assert.assertTrue(sweepLatch.await(4 * period, TimeUnit.MILLISECONDS));
+            Assert.assertTrue(taskLatch.await(4 * period, TimeUnit.MILLISECONDS));
+            Assert.assertEquals(1, sweeper.getSize());
 
-        sweeper.stop();
+            sweeper.stop();
+        }
     }
 }
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/ThreadClassLoaderScopeTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/ThreadClassLoaderScopeTest.java
new file mode 100644
index 0000000..e1f4c78
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/ThreadClassLoaderScopeTest.java
@@ -0,0 +1,79 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import org.junit.Test;
+
+public class ThreadClassLoaderScopeTest
+{
+    private static class ClassLoaderFoo extends URLClassLoader
+    {
+        public ClassLoaderFoo()
+        {
+            super(new URL[0]);
+        }
+    }
+
+    private static class ClassLoaderBar extends URLClassLoader
+    {
+        public ClassLoaderBar()
+        {
+            super(new URL[0]);
+        }
+    }
+    
+    @Test
+    public void testNormal()
+    {
+        try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(new ClassLoaderFoo()))
+        {
+            assertThat("ClassLoader in scope",Thread.currentThread().getContextClassLoader(),instanceOf(ClassLoaderFoo.class));
+            assertThat("Scoped ClassLoader",scope.getScopedClassLoader(),instanceOf(ClassLoaderFoo.class));
+        }
+        assertThat("ClassLoader after scope",Thread.currentThread().getContextClassLoader(),not(instanceOf(ClassLoaderFoo.class)));
+    }
+
+    @Test
+    public void testWithException()
+    {
+        try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(new ClassLoaderBar()))
+        {
+            assertThat("ClassLoader in 'scope'",Thread.currentThread().getContextClassLoader(),instanceOf(ClassLoaderBar.class));
+            assertThat("Scoped ClassLoader",scope.getScopedClassLoader(),instanceOf(ClassLoaderBar.class));
+            try (ThreadClassLoaderScope inner = new ThreadClassLoaderScope(new ClassLoaderFoo()))
+            {
+                assertThat("ClassLoader in 'inner'",Thread.currentThread().getContextClassLoader(),instanceOf(ClassLoaderFoo.class));
+                assertThat("Scoped ClassLoader",scope.getScopedClassLoader(),instanceOf(ClassLoaderFoo.class));
+                throw new RuntimeException("Intention exception");
+            }
+        }
+        catch (Throwable ignore)
+        {
+            /* ignore */
+        }
+        assertThat("ClassLoader after 'scope'",Thread.currentThread().getContextClassLoader(),not(instanceOf(ClassLoaderBar.class)));
+    }
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/strategy/ExecuteProduceConsumeTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/strategy/ExecuteProduceConsumeTest.java
new file mode 100644
index 0000000..150b436
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/strategy/ExecuteProduceConsumeTest.java
@@ -0,0 +1,374 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.strategy;
+
+
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.eclipse.jetty.util.thread.ExecutionStrategy.Producer;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ExecuteProduceConsumeTest
+{
+    private static final Runnable NULLTASK = () -> {};
+
+    private final BlockingQueue<Runnable> _produce = new LinkedBlockingQueue<>();
+    private final Queue<Runnable> _executions = new LinkedBlockingQueue<>();
+    private ExecuteProduceConsume _ewyk;
+    private volatile Thread _producer;
+
+    @Before
+    public void before()
+    {
+        _executions.clear();
+
+        Producer producer = () ->
+        {
+            try
+            {
+                _producer=Thread.currentThread();
+                Runnable task= _produce.take();
+                if (task==NULLTASK)
+                    return null;
+                return task;
+            }
+            catch(InterruptedException e)
+            {
+                e.printStackTrace();
+                return null;
+            }
+            finally
+            {
+                _producer=null;
+            }
+        };
+
+        Executor executor = _executions::add;
+
+        _ewyk = new ExecuteProduceConsume(producer,executor);
+    }
+
+    @After
+    public void after()
+    {
+        // All done and checked
+        Assert.assertThat(_produce.size(), Matchers.equalTo(0));
+        Assert.assertThat(_executions.size(), Matchers.equalTo(0));
+    }
+
+    @Test
+    public void testIdle()
+    {
+        _produce.add(NULLTASK);
+        _ewyk.execute();
+    }
+
+    @Test
+    public void testProduceOneNonBlockingTask()
+    {
+        Task t0 = new Task();
+        _produce.add(t0);
+        _produce.add(NULLTASK);
+        _ewyk.execute();
+        Assert.assertThat(t0.hasRun(), Matchers.equalTo(true));
+        Assert.assertEquals(_ewyk,_executions.poll());
+    }
+
+    @Test
+    public void testProduceManyNonBlockingTask()
+    {
+        Task[] tasks = new Task[10];
+        for (int i=0;i<tasks.length;i++)
+        {
+            tasks[i]=new Task();
+            _produce.add(tasks[i]);
+        }
+        _produce.add(NULLTASK);
+        _ewyk.execute();
+
+        for (Task task : tasks)
+            Assert.assertThat(task.hasRun(), Matchers.equalTo(true));
+        Assert.assertEquals(_ewyk,_executions.poll());
+    }
+
+    @Test
+    public void testProduceOneBlockingTaskIdleByDispatch() throws Exception
+    {
+        final Task t0 = new Task(true);
+        Thread thread = new Thread()
+        {
+            @Override
+            public void run()
+            {
+                _produce.add(t0);
+                _produce.add(NULLTASK);
+                _ewyk.execute();
+            }
+        };
+        thread.start();
+
+        // wait for execute thread to block in
+        t0.awaitRun();
+        Assert.assertEquals(thread,t0.getThread());
+
+        // Should have dispatched only one helper
+        Assert.assertEquals(_ewyk,_executions.poll());
+        // which is make us idle
+        _ewyk.run();
+        Assert.assertThat(_ewyk.isIdle(), Matchers.equalTo(true));
+
+
+        // unblock task
+        t0.unblock();
+        // will run to completion because are already idle
+        thread.join();
+    }
+
+    @Test
+    public void testProduceOneBlockingTaskIdleByTask() throws Exception
+    {
+        final Task t0 = new Task(true);
+        Thread thread = new Thread()
+        {
+            @Override
+            public void run()
+            {
+                _produce.add(t0);
+                _produce.add(NULLTASK);
+                _ewyk.execute();
+            }
+        };
+        thread.start();
+
+        // wait for execute thread to block in
+        t0.awaitRun();
+
+        // Should have dispatched only one helper
+        Assert.assertEquals(_ewyk,_executions.poll());
+
+        // unblock task
+        t0.unblock();
+        // will run to completion because are become idle
+        thread.join();
+        Assert.assertThat(_ewyk.isIdle(), Matchers.equalTo(true));
+
+        // because we are idle, dispatched thread is noop
+        _ewyk.run();
+    }
+
+    @Test
+    public void testBlockedInProduce() throws Exception
+    {
+        final Task t0 = new Task(true);
+        Thread thread0 = new Thread()
+        {
+            @Override
+            public void run()
+            {
+                _produce.add(t0);
+                _ewyk.execute();
+            }
+        };
+        thread0.start();
+
+        // wait for execute thread to block in task
+        t0.awaitRun();
+        Assert.assertEquals(thread0,t0.getThread());
+
+        // Should have dispatched another helper
+        Assert.assertEquals(_ewyk,_executions.poll());
+
+        // dispatched thread will block in produce
+        Thread thread1 = new Thread(_ewyk);
+        thread1.start();
+
+        // Spin
+        while(_producer==null)
+            Thread.yield();
+
+        // thread1 is blocked in producing
+        Assert.assertEquals(thread1,_producer);
+
+        // because we are producing, any other dispatched threads are noops
+        _ewyk.run();
+
+        // ditto with execute
+        _ewyk.execute();
+
+        // Now if unblock the production by the dispatched thread
+        final Task t1 = new Task(true);
+        _produce.add(t1);
+
+        // task will be run by thread1
+        t1.awaitRun();
+        Assert.assertEquals(thread1,t1.getThread());
+
+        // and another thread will have been requested
+        Assert.assertEquals(_ewyk,_executions.poll());
+
+        // If we unblock t1, it will overtake t0 and try to produce again!
+        t1.unblock();
+
+        // Now thread1 is producing again
+        while(_producer==null)
+            Thread.yield();
+        Assert.assertEquals(thread1,_producer);
+
+        // If we unblock t0, it will decide it is not needed
+        t0.unblock();
+        thread0.join();
+
+        // If the requested extra thread turns up, it is also noop because we are producing
+        _ewyk.run();
+
+        // Give the idle job
+        _produce.add(NULLTASK);
+
+        // Which will eventually idle the producer
+        thread1.join();
+        Assert.assertEquals(null,_producer);
+    }
+
+    @Test
+    public void testExecuteWhileIdling() throws Exception
+    {
+        final Task t0 = new Task(true);
+        Thread thread0 = new Thread()
+        {
+            @Override
+            public void run()
+            {
+                _produce.add(t0);
+                _ewyk.execute();
+            }
+        };
+        thread0.start();
+
+        // wait for execute thread to block in task
+        t0.awaitRun();
+        Assert.assertEquals(thread0,t0.getThread());
+
+        // Should have dispatched another helper
+        Assert.assertEquals(_ewyk,_executions.poll());
+
+        // We will go idle when we next produce
+        _produce.add(NULLTASK);
+
+        // execute will return immediately because it did not yet see the idle.
+        _ewyk.execute();
+
+        // When we unblock t0, thread1 will see the idle,
+        t0.unblock();
+
+        // but because there was a pending execute it will try producing again
+        while(_producer==null)
+            Thread.yield();
+        Assert.assertEquals(thread0,_producer);
+
+        // and will see new tasks
+        final Task t1 = new Task(true);
+        _produce.add(t1);
+        t1.awaitRun();
+        Assert.assertThat(t1.getThread(), Matchers.equalTo(thread0));
+
+        // Should NOT have dispatched another helper, because the last is still pending
+        Assert.assertThat(_executions.size(), Matchers.equalTo(0));
+
+        // When the dispatched thread turns up, it will see the second idle
+        _produce.add(NULLTASK);
+        _ewyk.run();
+        Assert.assertThat(_ewyk.isIdle(), Matchers.equalTo(true));
+
+        // So that when t1 completes it does not produce again.
+        t1.unblock();
+        thread0.join();
+    }
+
+    private static class Task implements Runnable
+    {
+        private final CountDownLatch _block = new CountDownLatch(1);
+        private final CountDownLatch _run = new CountDownLatch(1);
+        private volatile Thread _thread;
+
+        public Task()
+        {
+            this(false);
+        }
+
+        public Task(boolean block)
+        {
+            if (!block)
+                _block.countDown();
+        }
+
+        @Override
+        public void run()
+        {
+            try
+            {
+                _thread=Thread.currentThread();
+                _run.countDown();
+                _block.await();
+            }
+            catch (InterruptedException e)
+            {
+                throw new IllegalStateException(e);
+            }
+            finally
+            {
+                _thread=null;
+            }
+        }
+
+        public boolean hasRun()
+        {
+            return _run.getCount()<=0;
+        }
+
+        public void awaitRun()
+        {
+            try
+            {
+                _run.await();
+            }
+            catch (InterruptedException e)
+            {
+                throw new IllegalStateException(e);
+            }
+        }
+
+        public void unblock()
+        {
+            _block.countDown();
+        }
+
+        public Thread getThread()
+        {
+            return _thread;
+        }
+    }
+}
diff --git a/jetty-util/src/test/resources/jetty-logging.properties b/jetty-util/src/test/resources/jetty-logging.properties
index 3a0ce97..76035ea 100644
--- a/jetty-util/src/test/resources/jetty-logging.properties
+++ b/jetty-util/src/test/resources/jetty-logging.properties
@@ -1,3 +1,5 @@
 # Setup default logging implementation for during testing
 org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
 #org.eclipse.jetty.util.LEVEL=DEBUG
+
+#org.eclipse.jetty.util.PathWatcher.Noisy.LEVEL=OFF
diff --git a/jetty-util/src/test/resources/snikeystore b/jetty-util/src/test/resources/snikeystore
new file mode 100644
index 0000000..3c69266
--- /dev/null
+++ b/jetty-util/src/test/resources/snikeystore
Binary files differ
diff --git a/jetty-webapp/pom.xml b/jetty-webapp/pom.xml
index 9d6fe80..55d25db 100644
--- a/jetty-webapp/pom.xml
+++ b/jetty-webapp/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-webapp</artifactId>
@@ -28,60 +28,20 @@
     </resources>
     <plugins>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Import-Package>javax.servlet.*;version="[2.6.0,3.2]",*</Import-Package>
-              </instructions>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>
-              ${project.build.outputDirectory}/META-INF/MANIFEST.MF
-            </manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
           <onlyAnalyze>org.eclipse.jetty.webapp.*</onlyAnalyze>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <forkCount>1</forkCount>
+          <reuseForks>false</reuseForks>
+        </configuration>
+      </plugin>
     </plugins>
   </build>
   <dependencies>
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/CachingWebAppClassLoader.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/CachingWebAppClassLoader.java
new file mode 100644
index 0000000..1d76686
--- /dev/null
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/CachingWebAppClassLoader.java
@@ -0,0 +1,121 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.webapp;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jetty.util.ConcurrentHashSet;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.annotation.ManagedOperation;
+
+
+/**
+ * A WebAppClassLoader that caches {@link #getResource(String)} results.
+ * Specifically this ClassLoader caches not found classes and resources,
+ * which can greatly increase performance for applications that search 
+ * for resources.
+ */
+@ManagedObject
+public class CachingWebAppClassLoader extends WebAppClassLoader
+{
+    private final ConcurrentHashSet<String> _notFound = new ConcurrentHashSet<>();
+    private final ConcurrentHashMap<String,URL> _cache = new ConcurrentHashMap<>();
+    
+    public CachingWebAppClassLoader(ClassLoader parent, Context context) throws IOException
+    {
+        super(parent,context);
+    }
+
+    public CachingWebAppClassLoader(Context context) throws IOException
+    {
+        super(context);
+    }
+
+    @Override
+    public URL getResource(String name)
+    {
+        if (_notFound.contains(name))
+            return null;
+        
+        URL url = _cache.get(name);
+        
+        if (name==null)
+        {
+            url = super.getResource(name);
+        
+            if (url==null)
+            {
+                _notFound.add(name);
+            }
+            else
+            {
+                _cache.putIfAbsent(name,url);
+            }
+        }
+        
+        return url;
+    }
+
+    @Override
+    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
+    {
+        if (_notFound.contains(name))
+            throw new ClassNotFoundException(name+": in notfound cache");
+        try
+        {
+            return super.loadClass(name,resolve);
+        }
+        catch (ClassNotFoundException nfe)
+        {
+            _notFound.add(name);
+            throw nfe; 
+        }
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException
+    {
+        if (_notFound.contains(name))
+            throw new ClassNotFoundException(name+": in notfound cache");
+        try
+        {
+            return super.findClass(name);
+        }
+        catch (ClassNotFoundException nfe)
+        {
+            _notFound.add(name);
+            throw nfe; 
+        }
+    }
+
+    @ManagedOperation
+    public void clearCache()
+    {
+        _cache.clear();
+        _notFound.clear();
+    }
+    
+    @Override
+    public String toString()
+    {
+        return "Caching["+super.toString()+"]";
+    }
+}
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java
index f603dc7..de9fad8 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java
@@ -19,33 +19,56 @@
 
 package org.eclipse.jetty.webapp;
 
+import java.util.AbstractList;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
-import java.util.StringTokenizer;
+import java.util.ListIterator;
 
+import org.eclipse.jetty.util.StringUtil;
 
 /* ------------------------------------------------------------ */
 /**
- * ClasspathPattern performs sequential pattern matching of a class name 
+ * Classpath classes list performs sequential pattern matching of a class name 
  * against an internal array of classpath pattern entries.
- * 
- * When an entry starts with '-' (minus), reverse matching is performed.
- * When an entry ends with '.' (period), prefix matching is performed.
- * 
+ * A class pattern is a string of one of the forms:<ul>
+ * <li>'org.package.SomeClass' will match a specific class
+ * <li>'org.package.' will match a specific package hierarchy
+ * <li>'-org.package.Classname' excludes a specific class
+ * <li>'-org.package.' excludes a specific package hierarchy
+ * <li>Nested classes must be specified with the '$' separator if they 
+ * are to be explicitly included or excluded (eg. org.example.MyClass$NestedClass).
+ * <li>Nested classes are matched by their containing class. (eg. -org.example.MyClass
+ * would exclude org.example.MyClass$AnyNestedClass)
+ * </ul>
  * When class is initialized from a classpath pattern string, entries 
  * in this string should be separated by ':' (semicolon) or ',' (comma).
  */
 
-public class ClasspathPattern
+public class ClasspathPattern extends AbstractList<String>
 {
     private static class Entry
     {
-        public String classpath = null;
-        public boolean result = false;
-        public boolean partial = false;      
+        public final String _pattern;
+        public final String _name;
+        public final boolean _inclusive;
+        public final boolean _package;     
+        
+        Entry(String pattern)
+        {
+            _pattern=pattern;
+            _inclusive = !pattern.startsWith("-");
+            _package = pattern.endsWith(".");
+            _name = _inclusive ? pattern : pattern.substring(1).trim();
+        }
+        
+        @Override
+        public String toString()
+        {
+            return _pattern;
+        }
     }
     
-    final private List<String> _patterns = new ArrayList<String>();
     final private List<Entry> _entries = new ArrayList<Entry>();
     
     /* ------------------------------------------------------------ */
@@ -56,160 +79,140 @@
     /* ------------------------------------------------------------ */
     public ClasspathPattern(String[] patterns)
     {
-        setPatterns(patterns);
+        setAll(patterns);
     }
     
     /* ------------------------------------------------------------ */
     public ClasspathPattern(String pattern)
     {
-        setPattern(pattern);
+        add(pattern);
     }
     
+    /* ------------------------------------------------------------ */
+    @Override
+    public String get(int index)
+    {
+        return _entries.get(index)._pattern;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String set(int index, String element)
+    {
+        Entry e = _entries.set(index,new Entry(element));
+        return e==null?null:e._pattern;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void add(int index, String element)
+    {
+        _entries.add(index,new Entry(element));
+    }
+
+    /* ------------------------------------------------------------ */
+    @Deprecated
+    public void addPattern(String element)
+    {
+        add(element);
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public String remove(int index)
+    {
+        Entry e = _entries.remove(index);
+        return e==null?null:e._pattern;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public boolean remove(String pattern)
+    {
+        for (int i=_entries.size();i-->0;)
+        {
+            if (pattern.equals(_entries.get(i)._pattern))
+            {
+                _entries.remove(i);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int size()
+    {
+        return _entries.size();
+    }
 
     /* ------------------------------------------------------------ */
     /**
      * Initialize the matcher by parsing each classpath pattern in an array
      * 
-     * @param patterns array of classpath patterns
+     * @param classes array of classpath patterns
      */
-    private void setPatterns(String[] patterns)
+    private void setAll(String[] classes)
     {
-        _patterns.clear();
         _entries.clear();
-        addPatterns(patterns);
+        addAll(classes);
     }
     
     /* ------------------------------------------------------------ */
     /**
-     * @param patterns array of classpath patterns
+     * @param classes array of classpath patterns
      */
-    private void addPatterns(String[] patterns)
+    private void addAll(String[] classes)
     {
-        if (patterns != null)
-        {
-            Entry entry = null; 
-            for (String pattern : patterns)
-            {
-                entry = createEntry(pattern);
-                if (entry != null) 
-                {
-                    _patterns.add(pattern);
-                    _entries.add(entry);
-                }
-            }
-        }
+        if (classes!=null)
+            addAll(Arrays.asList(classes));
     }
     
     /* ------------------------------------------------------------ */
     /**
-     * @param patterns array of classpath patterns
+     * @param classes array of classpath patterns
      */
-    private void prependPatterns(String[] patterns)
+    public void prepend(String[] classes)
     {
-        if (patterns != null)
+        if (classes != null)
         {
-            Entry entry = null;
             int i=0;
-            for (String pattern : patterns)
+            for (String c : classes)
             {
-                entry = createEntry(pattern);
-                if (entry != null) 
-                {
-                    _patterns.add(i,pattern);
-                    _entries.add(i,entry);
-                    i++;
-                }
+                add(i,c);
+                i++;
             }
         }
     }
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * Create an entry object containing information about 
-     * a single classpath pattern
-     * 
-     * @param pattern single classpath pattern
-     * @return corresponding Entry object
-     */
-    private Entry createEntry(String pattern)
-    {
-        Entry entry = null;
-        
-        if (pattern != null)
-        {
-            String item = pattern.trim();
-            if (item.length() > 0)
-            {
-                entry = new Entry();
-                entry.result = !item.startsWith("-");
-                entry.partial = item.endsWith(".");
-                entry.classpath = entry.result ? item : item.substring(1).trim();
-            }
-        }
-        return entry;
-    }
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * Initialize the matcher by parsing a classpath pattern string
-     * 
-     * @param pattern classpath pattern string
-     */
-    public void setPattern(String pattern)
-    {
-        _patterns.clear();
-        _entries.clear();
-        addPattern(pattern);
-    }
 
     /* ------------------------------------------------------------ */
-    /**
-     * Parse a classpath pattern string and appending the result
-     * to the existing configuration.
-     * 
-     * @param pattern classpath pattern string
-     */
-    public void addPattern(String pattern)
+    public void prependPattern(String pattern)
     {
-        ArrayList<String> patterns = new ArrayList<String>();
-        StringTokenizer entries = new StringTokenizer(pattern, ":,");
-        while (entries.hasMoreTokens())
-        {
-            patterns.add(entries.nextToken());
-        }
-        
-        addPatterns(patterns.toArray(new String[patterns.size()]));
-    }   
-    
-
-    /* ------------------------------------------------------------ */
-    public void prependPattern(String classOrPackage)
-    {
-        ArrayList<String> patterns = new ArrayList<String>();
-        StringTokenizer entries = new StringTokenizer(classOrPackage, ":,");
-        while (entries.hasMoreTokens())
-        {
-            patterns.add(entries.nextToken());
-        }
-        
-        prependPatterns(patterns.toArray(new String[patterns.size()]));
+        add(0,pattern);
     }
     
-    
     /* ------------------------------------------------------------ */
     /**
      * @return array of classpath patterns
      */
     public String[] getPatterns()
     {
-        String[] patterns = null;
-        
-        if (_patterns!=null && _patterns.size() > 0)
+        return toArray(new String[_entries.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return List of classes excluded class exclusions and package patterns
+     */
+    public List<String> getClasses()
+    {
+        List<String> list = new ArrayList<>();
+        for (Entry e:_entries)
         {
-            patterns = _patterns.toArray(new String[_patterns.size()]);
+            if (e._inclusive && !e._package)
+                list.add(e._name);
         }
-        
-        return patterns;
+        return list;
     }
     
     /* ------------------------------------------------------------ */
@@ -221,48 +224,65 @@
      */
     public boolean match(String name)
     {       
-        boolean result=false;
+        name = name.replace('/','.');
 
-        if (_entries != null)
+        for (Entry entry : _entries)
         {
-            name = name.replace('/','.');
-
-            int startIndex = 0;
-
-            while(startIndex < name.length() && name.charAt(startIndex) == '.') {
-                startIndex++;
-            }
-
-            int dollar = name.indexOf("$");
-
-            int endIndex =  dollar != -1 ? dollar : name.length();
-
-            for (Entry entry : _entries)
+            if (entry==null)
+                continue;
+            if (entry._package)
             {
-                if (entry != null)
-                {               
-                    if (entry.partial)
-                    {
-                        if (name.regionMatches(startIndex, entry.classpath, 0, entry.classpath.length()))
-                        {
-                            result = entry.result;
-                            break;
-                        }
-                    }
-                    else
-                    {
-                        int regionLength = endIndex-startIndex;
-                        if (regionLength == entry.classpath.length()
-                                && name.regionMatches(startIndex, entry.classpath, 0, regionLength))
-                        {
-                            result = entry.result;
-                            break;
-                        }
-                    }
+                if (name.startsWith(entry._name) || ".".equals(entry._pattern))
+                    return entry._inclusive;
+            }
+            else
+            {
+                if (name.equals(entry._name))
+                    return entry._inclusive;
+                
+                if (name.length()>entry._name.length() && '$'==name.charAt(entry._name.length()) && name.startsWith(entry._name))
+                    return entry._inclusive;
+            }
+        }
+        return false;
+    }
+
+    public void addAfter(String afterPattern,String... patterns)
+    {
+        if (patterns!=null && afterPattern!=null)
+        {
+            ListIterator<String> iter = listIterator();
+            while (iter.hasNext())
+            {
+                String cc=iter.next();
+                if (afterPattern.equals(cc))
+                {
+                    for (int i=0;i<patterns.length;i++)
+                        iter.add(patterns[i]);
+                    return;
                 }
             }
         }
-        return result;
+        throw new IllegalArgumentException("after '"+afterPattern+"' not found in "+this);
     }
 
+    public void addBefore(String beforePattern,String... patterns)
+    {
+        if (patterns!=null && beforePattern!=null)
+        {
+            ListIterator<String> iter = listIterator();
+            while (iter.hasNext())
+            {
+                String cc=iter.next();
+                if (beforePattern.equals(cc))
+                {
+                    iter.previous();
+                    for (int i=0;i<patterns.length;i++)
+                        iter.add(patterns[i]);
+                    return;
+                }
+            }
+        }
+        throw new IllegalArgumentException("before '"+beforePattern+"' not found in "+this);
+    }
 }
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configuration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configuration.java
index 12701bd..01895dc 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configuration.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configuration.java
@@ -41,7 +41,7 @@
      * <p>
      * Typically this step discovers configuration resources
      * @param context The context to configure
-     * @throws Exception
+     * @throws Exception if unable to pre configure
      */
     public void preConfigure (WebAppContext context) throws Exception;
     
@@ -52,7 +52,7 @@
      * Typically this step applies the discovered configuration resources to
      * either the {@link WebAppContext} or the associated {@link MetaData}.
      * @param context The context to configure
-     * @throws Exception
+     * @throws Exception if unable to configure
      */
     public void configure (WebAppContext context) throws Exception;
     
@@ -60,7 +60,7 @@
     /* ------------------------------------------------------------------------------- */
     /** Clear down after configuration.
      * @param context The context to configure
-     * @throws Exception
+     * @throws Exception if unable to post configure
      */
     public void postConfigure (WebAppContext context) throws Exception;
     
@@ -69,7 +69,7 @@
      * This method is called to undo all configuration done. This is
      * called to allow the context to work correctly over a stop/start cycle
      * @param context The context to configure
-     * @throws Exception
+     * @throws Exception if unable to deconfigure
      */
     public void deconfigure (WebAppContext context) throws Exception;
 
@@ -78,7 +78,7 @@
      * This method is called to destroy a webappcontext. It is typically called when a context 
      * is removed from a server handler hierarchy by the deployer.
      * @param context The context to configure
-     * @throws Exception
+     * @throws Exception if unable to destroy
      */
     public void destroy (WebAppContext context) throws Exception;
     
@@ -90,7 +90,7 @@
      * has previously been configured by this Configuration.
      * @param template The template context
      * @param context The context to configure
-     * @throws Exception
+     * @throws Exception if unable to clone
      */
     public void cloneConfigure (WebAppContext template, WebAppContext context) throws Exception;
     
@@ -128,14 +128,18 @@
          */
         public static ClassList serverDefault(Server server)
         {
-            ClassList cl=server.getBean(ClassList.class);
-            if (cl!=null)
-                return new ClassList(cl);
-            Object attr = server.getAttribute(ATTR);
-            if (attr instanceof ClassList)
-                return new ClassList((ClassList)attr);
-            if (attr instanceof String[])
-                return new ClassList((String[])attr);
+            ClassList cl=null;
+            if (server!=null)
+            {
+                cl= server.getBean(ClassList.class);
+                if (cl!=null)
+                    return new ClassList(cl);
+                Object attr = server.getAttribute(ATTR);
+                if (attr instanceof ClassList)
+                    return new ClassList((ClassList)attr);
+                if (attr instanceof String[])
+                    return new ClassList((String[])attr);
+            }
             return new ClassList();
         }
         
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentConfiguration.java
index 7245613..e747068 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentConfiguration.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentConfiguration.java
@@ -25,9 +25,7 @@
 
 /**
  * FragmentConfiguration
- * 
- * 
- * 
+ * <p>
  * Process web-fragments in jars
  */
 public class FragmentConfiguration extends AbstractConfiguration
@@ -55,8 +53,10 @@
     /* ------------------------------------------------------------------------------- */
     /**
      * Look for any web-fragment.xml fragments in META-INF of jars in WEB-INF/lib
+     * @param context the web app context to look in
+     * @param metaData the metadata to populate with fragments
      * 
-     * @throws Exception
+     * @throws Exception if unable to find web fragments
      */
     public void findWebFragments (final WebAppContext context, final MetaData metaData) throws Exception
     {
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/IterativeDescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/IterativeDescriptorProcessor.java
index aad4b66..d508990 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/IterativeDescriptorProcessor.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/IterativeDescriptorProcessor.java
@@ -27,8 +27,6 @@
 
 /**
  * IterativeDescriptorProcessor
- *
- *
  */
 public abstract class IterativeDescriptorProcessor implements DescriptorProcessor
 {
@@ -41,8 +39,8 @@
      * Register a method to be called back when visiting the node with the given name.
      * The method must exist on a subclass of this class, and must have the signature:
      * public void method (Descriptor descriptor, XmlParser.Node node)
-     * @param nodeName
-     * @param m
+     * @param nodeName the node name
+     * @param m the method name
      */
     public void registerVisitor(String nodeName, Method m)
     {
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JarScanner.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JarScanner.java
index 4d80060..7dfa160 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JarScanner.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JarScanner.java
@@ -48,7 +48,6 @@
 {
     private static final Logger LOG = Log.getLogger(JarScanner.class);
 
-
     public abstract void processEntry (URI jarUri, JarEntry entry);
     
     /**
@@ -73,10 +72,10 @@
      * Will iterate over the jar names, matching
      * all those starting with "aaa-" first, then "bbb-".
      *
-     * @param pattern
-     * @param uris
+     * @param pattern the pattern to use for jar matching
+     * @param uris the uris of the jars to scan
      * @param isNullInclusive if true, an empty pattern means all names match, if false, none match
-     * @throws Exception
+     * @throws Exception if unable to scan
      */
     public void scan (Pattern pattern, URI[] uris, boolean isNullInclusive)
     throws Exception
@@ -110,11 +109,11 @@
      * parent loader hierarchy. If false, it is only applied to the
      * classloader passed in.
      * 
-     * @param pattern
-     * @param loader
-     * @param isNullInclusive
-     * @param visitParent
-     * @throws Exception
+     * @param pattern the pattern to use for jar matching
+     * @param loader the class loader to look for jars in
+     * @param isNullInclusive if true, an empty pattern means all names match, if false, none match
+     * @param visitParent if true, visit parent classloaders too
+     * @throws Exception if unable to scan
      */
     public void scan (Pattern pattern, ClassLoader loader, boolean isNullInclusive, boolean visitParent)
     throws Exception
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java
index b4ef54d..c7ec9b5 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java
@@ -18,7 +18,10 @@
 
 package org.eclipse.jetty.webapp;
 
+import java.io.File;
+import java.io.IOException;
 import java.util.Map;
+import java.util.concurrent.Callable;
 
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -42,9 +45,10 @@
     /** The value of this property points to the WEB-INF directory of
      * the web-app currently installed.
      * it is passed as a property to the jetty-web.xml file */
+    @Deprecated
     public static final String PROPERTY_THIS_WEB_INF_URL = "this.web-inf.url";
-
-
+    public static final String PROPERTY_WEB_INF_URI = "web-inf.uri";
+    public static final String PROPERTY_WEB_INF = "web-inf";
     public static final String XML_CONFIGURATION = "org.eclipse.jetty.webapp.JettyWebXmlConfiguration";
     public static final String JETTY_WEB_XML = "jetty-web.xml";
 
@@ -77,39 +81,35 @@
                 jetty=web_inf.addPath("web-jetty.xml");
 
             if(jetty.exists())
-            {
-                // No server classes while configuring
-                String[] old_server_classes = context.getServerClasses();
+            {             
+                if(LOG.isDebugEnabled())
+                    LOG.debug("Configure: "+jetty);
+
+                Object xml_attr=context.getAttribute(XML_CONFIGURATION);
+                context.removeAttribute(XML_CONFIGURATION);
+                
+                final XmlConfiguration jetty_config = xml_attr instanceof XmlConfiguration
+                    ?(XmlConfiguration)xml_attr
+                    :new XmlConfiguration(jetty.getURI().toURL());
+                setupXmlConfiguration(jetty_config, web_inf);
+                
                 try
                 {
-                    context.setServerClasses(null);
-                    if(LOG.isDebugEnabled())
-                        LOG.debug("Configure: "+jetty);
-
-                    XmlConfiguration jetty_config = (XmlConfiguration)context.getAttribute(XML_CONFIGURATION);
-
-                    if (jetty_config==null)
+                    final XmlConfiguration config=jetty_config;
+                    context.runWithoutCheckingServerClasses(new Callable<Void>()
                     {
-                        jetty_config=new XmlConfiguration(jetty.getURL());
-                    }
-                    else
-                    {
-                        context.removeAttribute(XML_CONFIGURATION);
-                    }
-                    setupXmlConfiguration(jetty_config, web_inf);
-                    try
-                    {
-                        jetty_config.configure(context);
-                    }
-                    catch (ClassNotFoundException e)
-                    {
-                        LOG.warn("Unable to process jetty-web.xml", e);
-                    }
+                        @Override
+                        public Void call() throws Exception
+                        {
+                            config.configure(context);
+                            return null;
+                        }
+                    });
                 }
-                finally
+                catch(Exception e)
                 {
-                    if (old_server_classes != null)
-                        context.setServerClasses(old_server_classes);
+                    LOG.warn("Error applying {}",jetty);
+                    throw e;
                 }
             }
         }
@@ -121,10 +121,11 @@
      * @param jetty_config The configuration object.
      * @param web_inf the WEB-INF location
      */
-    private void setupXmlConfiguration(XmlConfiguration jetty_config, Resource web_inf)
+    private void setupXmlConfiguration(XmlConfiguration jetty_config, Resource web_inf) throws IOException
     {
         Map<String,String> props = jetty_config.getProperties();
-        // TODO - should this be an id rather than a property?
-        props.put(PROPERTY_THIS_WEB_INF_URL, String.valueOf(web_inf.getURL()));
+        props.put(PROPERTY_THIS_WEB_INF_URL, web_inf.getURI().toString());  
+        props.put(PROPERTY_WEB_INF_URI, web_inf.getURI().toString());
+        props.put(PROPERTY_WEB_INF, web_inf.toString());
     }
 }
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java
index d63015f..93efb31 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java
@@ -32,9 +32,6 @@
 import org.eclipse.jetty.util.resource.EmptyResource;
 import org.eclipse.jetty.util.resource.Resource;
 
-
-
-
 /**
  * MetaData
  *
@@ -64,10 +61,6 @@
     protected Ordering _ordering;//can be set to RelativeOrdering by web-default.xml, web.xml, web-override.xml
     protected boolean allowDuplicateFragmentNames = false;
 
-
-
-
-
     public static class OriginInfo
     {
         private final String name;
@@ -132,7 +125,7 @@
             if (descriptor!=null)
                 return descriptor.toString();
             if (annotation!=null)
-                return "@"+annotation.annotationType().getSimpleName()+" on "+annotated.getName();
+                return "@"+annotation.annotationType().getSimpleName()+"("+annotated.getName()+")";
             return origin.toString();
         }
     }
@@ -263,7 +256,7 @@
      *
      * @param jarResource the jar the fragment is contained in
      * @param xmlResource the resource representing the xml file
-     * @throws Exception
+     * @throws Exception if unable to add fragment
      */
     public void addFragment (Resource jarResource, Resource xmlResource)
     throws Exception
@@ -304,7 +297,7 @@
     /**
      * Annotations not associated with a WEB-INF/lib fragment jar.
      * These are from WEB-INF/classes or the ??container path??
-     * @param annotations
+     * @param annotations the list of discovered annotations to add
      */
     public void addDiscoveredAnnotations(List<DiscoveredAnnotation> annotations)
     {
@@ -324,7 +317,7 @@
      * This method is synchronized as it is anticipated that it may be called by many threads
      * during the annotation scanning phase.
      * 
-     * @param annotation
+     * @param annotation the discovered annotation
      */
     public synchronized void addDiscoveredAnnotation (DiscoveredAnnotation annotation)
     {
@@ -368,7 +361,9 @@
 
     /**
      * Resolve all servlet/filter/listener metadata from all sources: descriptors and annotations.
-     *
+     * 
+     * @param context the context to resolve servlets / filters / listeners metadata from 
+     * @throws Exception if unable to resolve metadata
      */
     public void resolve (WebAppContext context)
     throws Exception
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java
index 8e5a3ff..7ac3ff6 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java
@@ -19,14 +19,21 @@
 package org.eclipse.jetty.webapp;
 
 
+import java.io.File;
+import java.io.IOException;
+import java.net.JarURLConnection;
 import java.net.URI;
 import java.net.URL;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
 
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -35,12 +42,13 @@
 
 /**
  * MetaInfConfiguration
+ * <p>
  *
  * Scan META-INF of jars to find:
  * <ul>
- * <li>tlds
- * <li>web-fragment.xml
- * <li>resources
+ * <li>tlds</li>
+ * <li>web-fragment.xml</li>
+ * <li>resources</li>
  * </ul>
  * 
  * The jars which are scanned are:
@@ -91,10 +99,10 @@
      * cache the info discovered indexed by the jar in which it was discovered: this speeds
      * up subsequent context deployments.
      * 
-     * @param context
-     * @param jars
-     * @param useCaches
-     * @throws Exception
+     * @param context the context for the scan
+     * @param jars the jars resources to scan
+     * @param useCaches if true, cache the info discovered
+     * @throws Exception if unable to scan the jars
      */
     public void scanJars (final WebAppContext context, Collection<Resource> jars, boolean useCaches)
     throws Exception
@@ -140,10 +148,10 @@
     /**
      * Scan for META-INF/resources dir in the given jar.
      * 
-     * @param context
-     * @param target
-     * @param cache
-     * @throws Exception
+     * @param context the context for the scan
+     * @param target the target resource to scan for
+     * @param cache the resource cache
+     * @throws Exception if unable to scan for resources
      */
     public void scanForResources (WebAppContext context, Resource target, ConcurrentHashMap<Resource,Resource> cache)
     throws Exception
@@ -173,10 +181,14 @@
             {
                 //Resource represents a packed jar
                 URI uri = target.getURI();
-                resourcesDir = Resource.newResource("jar:"+uri+"!/META-INF/resources");
+                resourcesDir = Resource.newResource(uriJarPrefix(uri,"!/META-INF/resources"));
             }
+            
             if (!resourcesDir.exists() || !resourcesDir.isDirectory())
+            {
+                resourcesDir.close();
                 resourcesDir = EmptyResource.INSTANCE;
+            }
 
             if (cache != null)
             {               
@@ -188,7 +200,9 @@
             }
 
             if (resourcesDir == EmptyResource.INSTANCE)
+            {
                 return;
+            }
         }
 
         //add it to the meta inf resources for this context
@@ -199,16 +213,17 @@
             context.setAttribute(METAINF_RESOURCES, dirs);
         }
         if (LOG.isDebugEnabled()) LOG.debug(resourcesDir+" added to context");
+
         dirs.add(resourcesDir);
     }
     
     /**
      * Scan for META-INF/web-fragment.xml file in the given jar.
      * 
-     * @param context
-     * @param jar
-     * @param cache
-     * @throws Exception
+     * @param context the context for the scan
+     * @param jar the jar resource to scan for fragements in
+     * @param cache the resource cache
+     * @throws Exception if unable to scan for fragments
      */
     public void scanForFragment (WebAppContext context, Resource jar, ConcurrentHashMap<Resource,Resource> cache)
     throws Exception
@@ -237,10 +252,13 @@
             else
             {
                 URI uri = jar.getURI();
-                webFrag = Resource.newResource("jar:"+uri+"!/META-INF/web-fragment.xml");
+                webFrag = Resource.newResource(uriJarPrefix(uri,"!/META-INF/web-fragment.xml"));
             }
             if (!webFrag.exists() || webFrag.isDirectory())
+            {
+                webFrag.close();
                 webFrag = EmptyResource.INSTANCE;
+            }
             
             if (cache != null)
             {
@@ -270,10 +288,10 @@
     /**
      * Discover META-INF/*.tld files in the given jar
      * 
-     * @param context
-     * @param jar
-     * @param cache
-     * @throws Exception
+     * @param context the context for the scan
+     * @param jar the jar resources to scan tlds for
+     * @param cache the resource cache
+     * @throws Exception if unable to scan for tlds
      */
     public void scanForTlds (WebAppContext context, Resource jar, ConcurrentHashMap<Resource, Collection<URL>> cache)
     throws Exception
@@ -297,30 +315,17 @@
         else
         {
             //not using caches or not in the cache so find all tlds
-            Resource metaInfDir = null;
+            tlds = new HashSet<URL>();  
             if (jar.isDirectory())
             {
-                //TODO ??????
-                metaInfDir = jar.addPath("/META-INF/");
+                tlds.addAll(getTlds(jar.getFile()));
             }
             else
             {
                 URI uri = jar.getURI();
-                metaInfDir = Resource.newResource("jar:"+uri+"!/META-INF/");
+                tlds.addAll(getTlds(uri));
             }
 
-            //find any *.tld files inside META-INF or subdirs
-            tlds = new HashSet<URL>();      
-            Collection<Resource> resources = metaInfDir.getAllResources();
-            for (Resource t:resources)
-            {
-                String name = t.toString();
-                if (name.endsWith(".tld"))
-                {
-                    if (LOG.isDebugEnabled()) LOG.debug(t+" tld discovered");
-                    tlds.add(t.getURL());
-                }
-            }
             if (cache != null)
             {  
                 if (LOG.isDebugEnabled()) LOG.debug(jar+" tld cache updated");
@@ -333,13 +338,13 @@
                 return;
         }
 
-        Collection<URL> tld_resources=(Collection<URL>)context.getAttribute(METAINF_TLDS);
-        if (tld_resources == null)
+        Collection<URL> metaInfTlds = (Collection<URL>)context.getAttribute(METAINF_TLDS);
+        if (metaInfTlds == null)
         {
-            tld_resources = new HashSet<URL>();
-            context.setAttribute(METAINF_TLDS, tld_resources);
+            metaInfTlds = new HashSet<URL>();
+            context.setAttribute(METAINF_TLDS, metaInfTlds);
         }
-        tld_resources.addAll(tlds);  
+        metaInfTlds.addAll(tlds);  
         if (LOG.isDebugEnabled()) LOG.debug("tlds added to context");
     }
     
@@ -347,8 +352,83 @@
     @Override
     public void postConfigure(WebAppContext context) throws Exception
     {
-        context.setAttribute(METAINF_FRAGMENTS, null); 
         context.setAttribute(METAINF_RESOURCES, null);
+
+        context.setAttribute(METAINF_FRAGMENTS, null); 
+   
         context.setAttribute(METAINF_TLDS, null);
     }
+    
+    /**
+     * Find all .tld files in all subdirs of the given dir.
+     * 
+     * @param dir the directory to scan
+     * @return the list of tlds found
+     * @throws IOException if unable to scan the directory
+     */
+    public Collection<URL>  getTlds (File dir) throws IOException
+    {
+        if (dir == null || !dir.isDirectory())
+            return Collections.emptySet();
+        
+        HashSet<URL> tlds = new HashSet<URL>();
+        
+        File[] files = dir.listFiles();
+        if (files != null)
+        {
+            for (File f:files)
+            {
+                if (f.isDirectory())
+                    tlds.addAll(getTlds(f));
+                else
+                {
+                    String name = f.getCanonicalPath();
+                    if (name.contains("META-INF") && name.endsWith(".tld"))
+                        tlds.add(f.toURI().toURL());
+                }
+            }
+        }
+        return tlds;  
+    }
+    
+    /**
+     * Find all .tld files in the given jar.
+     * 
+     * @param uri the uri to jar file
+     * @return the collection of tlds as url references  
+     * @throws IOException if unable to scan the jar file
+     */
+    public Collection<URL> getTlds (URI uri) throws IOException
+    {
+        HashSet<URL> tlds = new HashSet<URL>();
+
+        String jarUri = uriJarPrefix(uri, "!/");
+        URL url = new URL(jarUri);
+        JarURLConnection jarConn = (JarURLConnection) url.openConnection();
+        jarConn.setUseCaches(Resource.getDefaultUseCaches());
+        JarFile jarFile = jarConn.getJarFile();
+        Enumeration<JarEntry> entries = jarFile.entries();
+        while (entries.hasMoreElements())
+        {
+            JarEntry e = entries.nextElement();
+            String name = e.getName();
+            if (name.startsWith("META-INF") && name.endsWith(".tld"))
+            {
+                tlds.add(new URL(jarUri + name));
+            }
+        }
+        if (!Resource.getDefaultUseCaches())
+            jarFile.close();
+        return tlds;
+    }
+
+    private String uriJarPrefix(URI uri, String suffix)
+    {
+        String uriString = uri.toString();
+        if (uriString.startsWith("jar:")) {
+            return uriString + suffix;
+        } else {
+            return "jar:" + uriString + suffix;
+        }
+    }
 }
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Ordering.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Ordering.java
index 6b217f8..cbe3aef 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Ordering.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Ordering.java
@@ -29,18 +29,14 @@
 
 
 /**
- * Ordering
- *
  * Ordering options for jars in WEB-INF lib.
  */
 public interface Ordering
 {  
-
     public List<Resource> order(List<Resource> fragments);
     public boolean isAbsolute ();
     public boolean hasOther();
 
-
     /**
      * AbsoluteOrdering
      *
@@ -150,6 +146,10 @@
         @Override
         public List<Resource> order(List<Resource> jars)
         {         
+            _beforeOthers.clear();
+            _afterOthers.clear();
+            _noOthers.clear();
+
             //for each jar, put it into the ordering according to the fragment ordering
             for (Resource jar:jars)
             {
@@ -161,17 +161,17 @@
                     {
                         case None:
                         {
-                            ((RelativeOrdering)_metaData.getOrdering()).addNoOthers(jar);
+                            addNoOthers(jar);
                             break;
                         }
                         case Before:
                         { 
-                            ((RelativeOrdering)_metaData.getOrdering()).addBeforeOthers(jar);
+                            addBeforeOthers(jar);
                             break;
                         }
                         case After:
                         {
-                            ((RelativeOrdering)_metaData.getOrdering()).addAfterOthers(jar);
+                            addAfterOthers(jar);
                             break;
                         }
                     } 
@@ -179,7 +179,7 @@
                 else
                 {
                     //jar fragment has no descriptor, but there is a relative ordering in place, so it must be part of the others
-                    ((RelativeOrdering)_metaData.getOrdering()).addNoOthers(jar);
+                    addNoOthers(jar);
                 }
             }            
                 
@@ -350,9 +350,10 @@
     
        /**
         * Is fragment with name a before fragment with name b?
-        * @param list
-        * @param fragNameA
-        * @param fragNameB
+        * 
+        * @param list the list of resources
+        * @param fragNameA the first fragment
+        * @param fragNameB the second fragment
         * @return true if fragment name A is before fragment name B 
         */
        protected boolean isBefore (List<Resource> list, String fragNameA, String fragNameB)
@@ -397,9 +398,10 @@
     
        /**
         * Is fragment name "a" after fragment name "b"?
-        * @param list
-        * @param fragNameA
-        * @param fragNameB
+        * 
+        * @param list the list of resources
+        * @param fragNameA the first fragment
+        * @param fragNameB the second fragment
         * @return true if fragment name A is after fragment name B
         */
        protected boolean isAfter(List<Resource> list, String fragNameA, String fragNameB)
@@ -441,9 +443,9 @@
         * Insert the resource matching the fragName into the list of resources
         * at the location indicated by index.
         * 
-        * @param list
-        * @param index
-        * @param fragName
+        * @param list the list of resources
+        * @param index the index to insert into
+        * @param fragName the fragment name to insert
         */
        protected void insert(List<Resource> list, int index, String fragName)
        {
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
index 87413fd..a02bfc8 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
@@ -33,12 +33,13 @@
 
 import javax.servlet.DispatcherType;
 import javax.servlet.MultipartConfigElement;
-import javax.servlet.ServletException;
 import javax.servlet.SessionTrackingMode;
 
+import org.eclipse.jetty.http.pathmap.ServletPathSpec;
 import org.eclipse.jetty.security.ConstraintAware;
 import org.eclipse.jetty.security.ConstraintMapping;
 import org.eclipse.jetty.security.authentication.FormAuthenticator;
+import org.eclipse.jetty.server.session.AbstractSessionManager;
 import org.eclipse.jetty.servlet.BaseHolder.Source;
 import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
 import org.eclipse.jetty.servlet.FilterHolder;
@@ -58,9 +59,9 @@
 import org.eclipse.jetty.xml.XmlParser.Node;
 
 /**
- * StandardDescriptorProcessor
- *
- * Process a web.xml, web-defaults.xml, web-overrides.xml, web-fragment.xml.
+ * StandardDescriptorProcessor.
+ * <p>
+ * Process the web.xml, web-defaults.xml, web-overrides.xml, and web-fragment.xml descriptors.
  */
 public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
 {
@@ -148,11 +149,6 @@
         _servletMappings.clear();
     }
 
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitContextParam (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         String name = node.getString("param-name", false, true);
@@ -196,13 +192,6 @@
 
     }
 
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitDisplayName(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         //Servlet Spec 3.0 p. 74 Ignore from web-fragments
@@ -213,12 +202,6 @@
         }
     }
 
-
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitServlet(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         String id = node.getAttribute("id");
@@ -621,13 +604,6 @@
         }
     }
 
-
-
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitServletMapping(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         //Servlet Spec 3.0, p74
@@ -669,19 +645,16 @@
         }
     }
 
-
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitSessionConfig(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         XmlParser.Node tNode = node.get("session-timeout");
         if (tNode != null)
         {
-            int timeout = Integer.parseInt(tNode.toString(false, true));
-            context.getSessionHandler().getSessionManager().setMaxInactiveInterval(timeout * 60);
+            java.math.BigDecimal asDecimal = new java.math.BigDecimal(tNode.toString(false, true));
+            if (asDecimal.compareTo(AbstractSessionManager.MAX_INACTIVE_MINUTES) > 0)
+                throw new IllegalStateException ("Max session-timeout in minutes is "+AbstractSessionManager.MAX_INACTIVE_MINUTES);
+            
+            context.getSessionHandler().getSessionManager().setMaxInactiveInterval(asDecimal.intValueExact() * 60);
         }
 
         //Servlet Spec 3.0
@@ -997,13 +970,6 @@
         }
     }
 
-
-
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitMimeMapping(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         String extension = node.getString("extension", false, true);
@@ -1046,11 +1012,6 @@
         }
     }
 
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitWelcomeFileList(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         switch (context.getMetaData().getOrigin("welcome-file-list"))
@@ -1095,11 +1056,6 @@
         }
     }
 
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitLocaleEncodingList(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         Iterator<XmlParser.Node> iter = node.iterator("locale-encoding-mapping");
@@ -1146,11 +1102,6 @@
         }
     }
 
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitErrorPage(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         String error = node.getString("error-code", false, true);
@@ -1165,6 +1116,8 @@
             code=Integer.valueOf(error);
 
         String location = node.getString("location", false, true);
+        if (!location.startsWith("/"))
+            throw new IllegalStateException("Missing leading '/' for location: " + location);
         ErrorPageErrorHandler handler = (ErrorPageErrorHandler)context.getErrorHandler();
         String originName = "error."+error;
         switch (context.getMetaData().getOrigin(originName))
@@ -1212,10 +1165,6 @@
 
     }
 
-    /**
-     * @param context
-     * @param node
-     */
     public void addWelcomeFiles(WebAppContext context, XmlParser.Node node)
     {
         Iterator<XmlParser.Node> iter = node.iterator("welcome-file");
@@ -1230,12 +1179,6 @@
         }
     }
 
-
-    /**
-     * @param servletName
-     * @param node
-     * @param context
-     */
     public ServletMapping addServletMapping (String servletName, XmlParser.Node node, WebAppContext context, Descriptor descriptor)
     {
         ServletMapping mapping = new ServletMapping();
@@ -1247,10 +1190,9 @@
         while (iter.hasNext())
         {
             String p = iter.next().toString(false, true);
-            p = normalizePattern(p);
+            p = ServletPathSpec.normalize(p);
             
-            //check if there is already a mapping for this path, and if there is && it is from a defaultdescriptor
-            //remove it in favour of the new one      
+            //check if there is already a mapping for this path
             ListIterator<ServletMapping> listItor = _servletMappings.listIterator();
             boolean found = false;
             while (listItor.hasNext() && !found)
@@ -1260,21 +1202,30 @@
                 {
                     for (String ps:sm.getPathSpecs())
                     {
-                        if (p.equals(ps) && sm.isDefault())
+                        //The same path has been mapped multiple times, either to a different servlet or the same servlet.
+                        //If its a different servlet, this is only valid to do if the old mapping was from a default descriptor.
+                        if (p.equals(ps) && (sm.isDefault() || servletName.equals(sm.getServletName())))
                         {
-                            if (LOG.isDebugEnabled()) LOG.debug("{} in mapping {} from defaults descriptor is overridden by ",ps,sm,servletName);
+                            if (sm.isDefault())
+                            {
+                                if (LOG.isDebugEnabled()) LOG.debug("{} in mapping {} from defaults descriptor is overridden by ",ps,sm,servletName);
+                            }
+                            else
+                                LOG.warn("Duplicate mapping from {} to {}", p, servletName);
+
                             //remove ps from the path specs on the existing mapping
                             //if the mapping now has no pathspecs, remove it
                             String[] updatedPaths = ArrayUtil.removeFromArray(sm.getPathSpecs(), ps);
+                            
                             if (updatedPaths == null || updatedPaths.length == 0)
-                            { 
-                                if (LOG.isDebugEnabled()) LOG.debug("Removed mapping {} from defaults descriptor",sm);
+                            {
+                                if (LOG.isDebugEnabled()) LOG.debug("Removed empty mapping {}",sm);
                                 listItor.remove();
                             }
                             else 
                             {
                                 sm.setPathSpecs(updatedPaths);
-                                if (LOG.isDebugEnabled()) LOG.debug("Removed path {} from mapping {} from defaults descriptor ", p,sm);
+                                if (LOG.isDebugEnabled()) LOG.debug("Removed path {} from mapping {}", p,sm);
                             }
                             found = true;
                             break;
@@ -1282,24 +1233,17 @@
                     }
                 }
             }
-            
+
             paths.add(p);
             context.getMetaData().setOrigin(servletName+".servlet.mapping."+p, descriptor);
         }
+
         mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
         if (LOG.isDebugEnabled()) LOG.debug("Added mapping {} ",mapping);
-        
-      
-      
         _servletMappings.add(mapping);
         return mapping;
     }
 
-    /**
-     * @param filterName
-     * @param node
-     * @param context
-     */
     public void addFilterMapping (String filterName, XmlParser.Node node, WebAppContext context, Descriptor descriptor)
     {
         FilterMapping mapping = new FilterMapping();
@@ -1310,7 +1254,7 @@
         while (iter.hasNext())
         {
             String p = iter.next().toString(false, true);
-            p = normalizePattern(p);
+            p = ServletPathSpec.normalize(p);
             paths.add(p);
             context.getMetaData().setOrigin(filterName+".filter.mapping."+p, descriptor);
         }
@@ -1340,12 +1284,6 @@
         _filterMappings.add(mapping);
     }
 
-
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitTagLib(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         //Additive across web.xml and web-fragment.xml
@@ -1367,11 +1305,6 @@
         config.addTaglibDescriptor(tl);
     }
 
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitJspConfig(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         //Additive across web.xml and web-fragment.xml
@@ -1405,7 +1338,7 @@
             while (iter2.hasNext())
             {
                 String url = iter2.next().toString(false, true);
-                url = normalizePattern(url);
+                url = ServletPathSpec.normalize(url);
                 paths.add( url);
                 jpg.addUrlPattern(url);
             }
@@ -1441,18 +1374,55 @@
         //add mappings to the jsp servlet from the property-group mappings
         if (paths.size() > 0)
         {
-            ServletMapping mapping = new ServletMapping();
-            mapping.setServletName("jsp");
-            mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
-            _servletMappings.add(mapping);
+            ServletMapping jspMapping = null;
+            for (ServletMapping m: _servletMappings)
+            {
+                if (m.getServletName().equals("jsp"))
+                {
+                    jspMapping = m;
+                    break;
+                }
+            }
+            if (jspMapping != null)
+            {
+                if (jspMapping.getPathSpecs() == null)
+                {
+                    //no paths in jsp servlet mapping, we will add all of ours
+                    if (LOG.isDebugEnabled()) LOG.debug("Adding all paths from jsp-config to jsp servlet mapping");
+                    jspMapping.setPathSpecs(paths.toArray(new String[paths.size()]));
+                }
+                else
+                {
+                    //check if each of our paths is already present in existing mapping
+                    ListIterator<String> piterator = paths.listIterator();
+                    while (piterator.hasNext())
+                    {
+                        String p = piterator.next();
+                        if (jspMapping.containsPathSpec(p))
+                            piterator.remove();
+                    }
+                    
+                    //any remaining paths, add to the jspMapping
+                    if (paths.size() > 0)
+                    {
+                        for (String p:jspMapping.getPathSpecs())
+                            paths.add(p);
+                        if (LOG.isDebugEnabled()) LOG.debug("Adding extra paths from jsp-config to jsp servlet mapping");
+                        jspMapping.setPathSpecs((String[])paths.toArray(new String[paths.size()]));
+                    }
+                }
+            }
+            else
+            {
+                //no mapping for jsp yet, make one
+                ServletMapping mapping = new ServletMapping();
+                mapping.setServletName("jsp");
+                mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
+                _servletMappings.add(mapping);
+            }
         }
     }
 
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitSecurityConstraint(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         Constraint scBase = new Constraint();
@@ -1508,7 +1478,7 @@
                 while (iter2.hasNext())
                 {
                     String url = iter2.next().toString(false, true);
-                    url = normalizePattern(url);
+                    url = ServletPathSpec.normalize(url);
                     //remember origin so we can process ServletRegistration.Dynamic.setServletSecurityElement() correctly
                     context.getMetaData().setOrigin("constraint.url."+url, descriptor);
                     
@@ -1562,12 +1532,6 @@
         }
     }
 
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     * @throws Exception
-     */
     public void visitLoginConfig(WebAppContext context, Descriptor descriptor, XmlParser.Node node) throws Exception
     {
         //ServletSpec 3.0 p74 says elements present 0/1 time if specified in web.xml take
@@ -1732,11 +1696,6 @@
         }
     }
 
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitSecurityRole(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         //ServletSpec 3.0, p74 elements with multiplicity >1 are additive when merged
@@ -1745,12 +1704,6 @@
         ((ConstraintAware)context.getSecurityHandler()).addRole(role);
     }
 
-
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitFilter(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         String name = node.getString("filter-name", false, true);
@@ -1882,11 +1835,6 @@
         }
     }
 
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitFilterMapping(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         //Servlet Spec 3.0, p74
@@ -1925,12 +1873,6 @@
         }
     }
 
-
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitListener(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         String className = node.getString("listener-class", false, true);
@@ -1972,11 +1914,6 @@
         }
     }
 
-    /**
-     * @param context
-     * @param descriptor
-     * @param node
-     */
     public void visitDistributable(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         // the element has no content, so its simple presence
@@ -1989,25 +1926,18 @@
     /**
      * Servlet spec 3.1. When present in web.xml, this means that http methods that are
      * not covered by security constraints should have access denied.
-     * 
+     * <p>
      * See section 13.8.4, pg 145
-     * @param context
-     * @param descriptor
-     * @param node
+     * 
+     * @param context the of the processing
+     * @param descriptor the descriptor
+     * @param node the xml node
      */
     public void visitDenyUncoveredHttpMethods(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
     {
         ((ConstraintAware)context.getSecurityHandler()).setDenyUncoveredHttpMethods(true);
     }
 
-    /**
-     * @param context
-     * @param clazz
-     * @return the new event listener
-     * @throws ServletException
-     * @throws InstantiationException
-     * @throws IllegalAccessException
-     */
     public EventListener newListenerInstance(WebAppContext context,Class<? extends EventListener> clazz) throws Exception
     {
         ListenerHolder h = context.getServletHandler().newListenerHolder(Source.DESCRIPTOR);
@@ -2017,16 +1947,4 @@
         return l;
 
     }
-
-    /**
-     * @param p
-     * @return the normalized pattern
-     */
-    public String normalizePattern(String p)
-    {
-        if (p != null && p.length() > 0 && !p.startsWith("/") && !p.startsWith("*")) return "/" + p;
-        return p;
-    }
-
-
 }
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java
index 7b67bb9..156de5d 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java
@@ -23,7 +23,6 @@
 import java.io.InputStream;
 import java.lang.instrument.ClassFileTransformer;
 import java.lang.instrument.IllegalClassFormatException;
-import java.lang.instrument.Instrumentation;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.security.CodeSource;
@@ -46,11 +45,12 @@
 import org.eclipse.jetty.util.resource.ResourceCollection;
 
 
-/* ------------------------------------------------------------ */
-/** ClassLoader for HttpContext.
+/** 
+ * ClassLoader for HttpContext.
+ * <p>
  * Specializes URLClassLoader with some utility and file mapping
  * methods.
- *
+ * <p>
  * This loader defaults to the 2.3 servlet spec behavior where non
  * system classes are loaded from the classpath in preference to the
  * parent loader.  Java2 compliant loading, where the parent loader
@@ -58,14 +58,18 @@
  * {@link org.eclipse.jetty.webapp.WebAppContext#setParentLoaderPriority(boolean)} 
  * method and influenced with {@link WebAppContext#isServerClass(String)} and 
  * {@link WebAppContext#isSystemClass(String)}.
- *
+ * <p>
  * If no parent class loader is provided, then the current thread 
  * context classloader will be used.  If that is null then the 
  * classloader that loaded this class is used as the parent.
- * 
  */
 public class WebAppClassLoader extends URLClassLoader
 {
+    static
+    {
+        registerAsParallelCapable();
+    }
+
     private static final Logger LOG = Log.getLogger(WebAppClassLoader.class);
 
     private final Context _context;
@@ -131,7 +135,10 @@
     }
     
     /* ------------------------------------------------------------ */
-    /** Constructor.
+    /** 
+     * Constructor.
+     * @param context the context for this classloader
+     * @throws IOException if unable to initialize from context
      */
     public WebAppClassLoader(Context context)
         throws IOException
@@ -140,7 +147,12 @@
     }
     
     /* ------------------------------------------------------------ */
-    /** Constructor.
+    /** 
+     * Constructor.
+     * 
+     * @param parent the parent classloader 
+     * @param context the context for this classloader
+     * @throws IOException if unable to initialize classloader
      */
     public WebAppClassLoader(ClassLoader parent, Context context)
         throws IOException
@@ -200,6 +212,7 @@
      * @param resource Comma or semicolon separated path of filenames or URLs
      * pointing to directories or jar files. Directories should end
      * with '/'.
+     * @throws IOException if unable to add classpath from resource
      */
     public void addClassPath(Resource resource)
         throws IOException
@@ -220,6 +233,7 @@
      * @param classPath Comma or semicolon separated path of filenames or URLs
      * pointing to directories or jar files. Directories should end
      * with '/'.
+     * @throws IOException if unable to add classpath
      */
     public void addClassPath(String classPath)
         throws IOException
@@ -243,11 +257,13 @@
                 File file= resource.getFile();
                 if (file != null)
                 {
-                    URL url= resource.getURL();
+                    URL url= resource.getURI().toURL();
                     addURL(url);
                 }
                 else if (resource.isDirectory())
-                    addURL(resource.getURL());
+                {
+                    addURL(resource.getURI().toURL());
+                }
                 else
                 {
                     if (LOG.isDebugEnabled())
@@ -283,6 +299,8 @@
                 try 
                 {
                     Resource fn=lib.addPath(files[f]);
+                    if(LOG.isDebugEnabled())
+                        LOG.debug("addJar - {}", fn);
                     String fnlc=fn.getName().toLowerCase(Locale.ENGLISH);
                     // don't check if this is a directory, see Bug 353165
                     if (isFileSupported(fnlc))
@@ -356,42 +374,49 @@
         String tmp = name;
         if (tmp != null && tmp.endsWith(".class"))
             tmp = tmp.substring(0, tmp.length()-6);
-      
+        
         boolean system_class=_context.isSystemClass(tmp);
         boolean server_class=_context.isServerClass(tmp);
         
+        if (LOG.isDebugEnabled())
+            LOG.debug("getResource({}) system={} server={} cl={}",name,system_class,server_class,this);
+        
         if (system_class && server_class)
             return null;
         
+        ClassLoader source=null;
+        
         if (_parent!=null &&(_context.isParentLoaderPriority() || system_class ) && !server_class)
         {
             tried_parent= true;
             
             if (_parent!=null)
-                url= _parent.getResource(name);
+            {
+                source=_parent;
+                url=_parent.getResource(name);
+            }
         }
 
         if (url == null)
         {
             url= this.findResource(name);
-
+            source=this;
             if (url == null && name.startsWith("/"))
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("HACK leading / off " + name);
                 url= this.findResource(name.substring(1));
-            }
         }
 
         if (url == null && !tried_parent && !server_class )
         {
             if (_parent!=null)
+            {
+                tried_parent=true;
+                source=_parent;
                 url= _parent.getResource(name);
+            }
         }
 
-        if (url != null)
-            if (LOG.isDebugEnabled())
-                LOG.debug("getResource("+name+")=" + url);
+        if (LOG.isDebugEnabled())
+            LOG.debug("gotResource({})=={} from={} tried_parent={}",name,url,source,tried_parent);
 
         return url;
     }
@@ -405,67 +430,86 @@
 
     /* ------------------------------------------------------------ */
     @Override
-    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
+    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
     {
-        Class<?> c= findLoadedClass(name);
-        ClassNotFoundException ex= null;
-        boolean tried_parent= false;
-        
-        boolean system_class=_context.isSystemClass(name);
-        boolean server_class=_context.isServerClass(name);
-        
-        if (system_class && server_class)
+        synchronized (getClassLoadingLock(name))
         {
-            return null;
-        }
-        
-        if (c == null && _parent!=null && (_context.isParentLoaderPriority() || system_class) && !server_class)
-        {
-            tried_parent= true;
-            try
+            Class<?> c= findLoadedClass(name);
+            ClassNotFoundException ex= null;
+            boolean tried_parent= false;
+
+            boolean system_class=_context.isSystemClass(name);
+            boolean server_class=_context.isServerClass(name);
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("loadClass({}) system={} server={} cl={}",name,system_class,server_class,this);
+            
+            ClassLoader source=null;
+            
+            if (system_class && server_class)
             {
+                return null;
+            }
+
+            if (c == null && _parent!=null && (_context.isParentLoaderPriority() || system_class) && !server_class)
+            {
+                tried_parent= true;
+                source=_parent;
+                try
+                {
+                    c= _parent.loadClass(name);
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("loaded " + c);
+                }
+                catch (ClassNotFoundException e)
+                {
+                    ex= e;
+                }
+            }
+
+            if (c == null)
+            {
+                try
+                {
+                    source=this;
+                    c= this.findClass(name);
+                }
+                catch (ClassNotFoundException e)
+                {
+                    ex= e;
+                }
+            }
+
+            if (c == null && _parent!=null && !tried_parent && !server_class )
+            {
+                tried_parent=true;
+                source=_parent;
                 c= _parent.loadClass(name);
+            }
+
+            if (c == null && ex!=null)
+            {
                 if (LOG.isDebugEnabled())
-                    LOG.debug("loaded " + c);
+                    LOG.debug("!loadedClass({}) from={} tried_parent={}",name,this,tried_parent);
+                throw ex;
             }
-            catch (ClassNotFoundException e)
-            {
-                ex= e;
-            }
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("loadedClass({})=={} from={} tried_parent={}",name,c,source,tried_parent);
+            
+            if (resolve)
+                resolveClass(c);
+
+            return c;
         }
-
-        if (c == null)
-        {
-            try
-            {
-                c= this.findClass(name);
-            }
-            catch (ClassNotFoundException e)
-            {
-                ex= e;
-            }
-        }
-
-        if (c == null && _parent!=null && !tried_parent && !server_class )
-            c= _parent.loadClass(name);
-
-        if (c == null && ex!=null)
-            throw ex;
-
-        if (resolve)
-            resolveClass(c);
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("loaded {} from {}",c,c==null?null:c.getClassLoader());
-        
-        return c;
     }
 
     /* ------------------------------------------------------------ */
     /**
-     * @see addTransformer
-     * @deprecated
+     * @param transformer the transformer to add
+     * @deprecated {@link #addTransformer(ClassFileTransformer)} instead
      */
+    @Deprecated
     public void addClassFileTransformer(ClassFileTransformer transformer)
     {
         _transformers.add(transformer);
@@ -473,27 +517,23 @@
     
     /* ------------------------------------------------------------ */
     /**
-     * @see removeTransformer
-     * @deprecated
+     * @param transformer the transformer to remove
+     * @return true if transformer was removed
+     * @deprecated use {@link #removeTransformer(ClassFileTransformer)} instead
      */
+    @Deprecated
     public boolean removeClassFileTransformer(ClassFileTransformer transformer)
     {
         return _transformers.remove(transformer);
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see addClassFileTransformer
-     */
     public void addTransformer(ClassFileTransformer transformer)
     {
         _transformers.add(transformer);
     }
     
     /* ------------------------------------------------------------ */
-    /**
-     * @see removeClassFileTransformer
-     */
     public boolean removeTransformer(ClassFileTransformer transformer)
     {
         return _transformers.remove(transformer);
@@ -520,7 +560,10 @@
             {
                 content = url.openStream();
                 byte[] bytes = IO.readBytes(content);
-                    
+
+                if (LOG.isDebugEnabled())
+                    LOG.debug("foundClass({}) url={} cl={}",name,url,this);
+                
                 for (ClassFileTransformer transformer : _transformers)
                 {
                     byte[] tmp = transformer.transform(this,name,null,null,bytes);
@@ -557,6 +600,13 @@
         return clazz;
     }
     
+    
+    @Override
+    public void close() throws IOException
+    {
+        super.close();
+    }
+
     /* ------------------------------------------------------------ */
     @Override
     public String toString()
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
index ebb52c0..500ca29 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
@@ -22,6 +22,7 @@
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.net.URLClassLoader;
 import java.security.PermissionCollection;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -33,6 +34,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.Callable;
 
 import javax.servlet.ServletContext;
 import javax.servlet.ServletRegistration.Dynamic;
@@ -47,6 +49,7 @@
 import org.eclipse.jetty.security.ConstraintMapping;
 import org.eclipse.jetty.security.ConstraintSecurityHandler;
 import org.eclipse.jetty.security.SecurityHandler;
+import org.eclipse.jetty.server.ClassLoaderDump;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.HandlerContainer;
 import org.eclipse.jetty.server.Server;
@@ -56,18 +59,21 @@
 import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.util.AttributesMap;
 import org.eclipse.jetty.util.Loader;
 import org.eclipse.jetty.util.MultiException;
 import org.eclipse.jetty.util.URIUtil;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.component.DumpableCollection;
 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.ResourceCollection;
 
-/* ------------------------------------------------------------ */
-/** Web Application Context Handler.
+/** 
+ * Web Application Context Handler.
+ * <p>
  * The WebAppContext handler is an extension of ContextHandler that
  * coordinates the construction and configuration of nested handlers:
  * {@link org.eclipse.jetty.security.ConstraintSecurityHandler}, {@link org.eclipse.jetty.server.session.SessionHandler}
@@ -76,8 +82,6 @@
  * the default being  {@link org.eclipse.jetty.webapp.WebXmlConfiguration} and
  * {@link org.eclipse.jetty.webapp.JettyWebXmlConfiguration}.
  *
- * @org.apache.xbean.XBean description="Creates a servlet web application at a given context from a resource base"
- *
  */
 @ManagedObject("Web Application ContextHandler")
 public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context
@@ -105,6 +109,8 @@
     // System classes are classes that cannot be replaced by
     // the web application, and they are *always* loaded via
     // system classloader.
+    // TODO This centrally managed list of features that are exposed/hidden needs to be replaced
+    // with a more automatic distributed mechanism
     public final static String[] __dftSystemClasses =
     {
         "java.",                            // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
@@ -118,16 +124,19 @@
         "org.eclipse.jetty.jaas.",          // webapp cannot change jaas classes
         "org.eclipse.jetty.websocket.",     // webapp cannot change / replace websocket classes
         "org.eclipse.jetty.util.log.",      // webapp should use server log
-        "org.eclipse.jetty.servlet.ServletContextHandler.Decorator", // for CDI / weld use
+        "org.eclipse.jetty.servlet.StatisticsServlet", // webapp cannot change stats servlet
         "org.eclipse.jetty.servlet.DefaultServlet", // webapp cannot change default servlets
         "org.eclipse.jetty.jsp.JettyJspServlet", //webapp cannot change jetty jsp servlet
-        "org.eclipse.jetty.servlets.AsyncGzipFilter" // special case for AsyncGzipFilter
+        "org.eclipse.jetty.servlets.PushCacheFilter", //must be loaded by container classpath
+        "org.eclipse.jetty.servlets.PushSessionCacheFilter" //must be loaded by container classpath
     } ;
 
     // Server classes are classes that are hidden from being
     // loaded by the web application using system classloader,
     // so if web application needs to load any of such classes,
     // it has to include them in its distribution.
+    // TODO This centrally managed list of features that are exposed/hidden needs to be replaced
+    // with a more automatic distributed mechanism
     public final static String[] __dftServerClasses =
     {
         "-org.eclipse.jetty.jmx.",          // don't hide jmx classes
@@ -136,13 +145,14 @@
         "-org.eclipse.jetty.jndi.",         // don't hide naming classes
         "-org.eclipse.jetty.jaas.",         // don't hide jaas classes
         "-org.eclipse.jetty.servlets.",     // don't hide jetty servlets
+        "-org.eclipse.jetty.servlet.StatisticsServlet", // don't hide stats servlet
         "-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
         "-org.eclipse.jetty.jsp.",          //don't hide jsp servlet
         "-org.eclipse.jetty.servlet.listener.", // don't hide useful listeners
         "-org.eclipse.jetty.websocket.",    // don't hide websocket classes from webapps (allow webapp to use ones from system classloader)
         "-org.eclipse.jetty.apache.",       // don't hide jetty apache impls
         "-org.eclipse.jetty.util.log.",     // don't hide server log 
-        "-org.eclipse.jetty.servlet.ServletContextHandler.Decorator", // don't hide CDI / weld interface  
+        "-org.eclipse.jetty.alpn.",         // don't hide ALPN
         "org.objectweb.asm.",               // hide asm used by jetty
         "org.eclipse.jdt.",                 // hide jdt used by jetty
         "org.eclipse.jetty."                // hide other jetty classes
@@ -178,6 +188,8 @@
     private boolean _configurationDiscovered=true;
     private boolean _allowDuplicateFragmentNames = false;
     private boolean _throwUnavailableOnStartupException = false;
+    private boolean _checkingServerClasses = true;
+    
 
 
 
@@ -242,11 +254,14 @@
     /* ------------------------------------------------------------ */
     /**
      * This constructor is used in the geronimo integration.
-     *
+     * 
+     * @param parent the parent handler 
+     * @param contextPath the context path
      * @param sessionHandler SessionHandler for this web app
      * @param securityHandler SecurityHandler for this web app
      * @param servletHandler ServletHandler for this web app
      * @param errorHandler ErrorHandler for this web app
+     * @param options the options ({@link ServletContextHandler#SESSIONS} and/or {@link ServletContextHandler#SECURITY}) 
      */
     public WebAppContext(HandlerContainer parent, String contextPath, SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler,int options) 
     {
@@ -280,12 +295,13 @@
 
 
     /* ------------------------------------------------------------ */
-    /** Set Resource Alias.
+    /** 
+     * Set Resource Alias.
      * Resource aliases map resource uri's within a context.
      * They may optionally be used by a handler when looking for
      * a resource.
-     * @param alias
-     * @param uri
+     * @param alias the alias for a resource
+     * @param uri the uri for the resource
      */
     public void setResourceAlias(String alias, String uri)
     {
@@ -345,13 +361,12 @@
     {
         super.setClassLoader(classLoader);
 
-//        if ( !(classLoader instanceof WebAppClassLoader) )
-//        {
-//            LOG.info("NOTE: detected a classloader which is not an instance of WebAppClassLoader being set on WebAppContext, some typical class and resource locations may be missing on: " + toString() );
-//        }
-
+        String name = getDisplayName();
+        if (name==null) 
+            name=getContextPath();
+        
         if (classLoader!=null && classLoader instanceof WebAppClassLoader && getDisplayName()!=null)
-            ((WebAppClassLoader)classLoader).setName(getDisplayName());
+            ((WebAppClassLoader)classLoader).setName(name);
     }
 
     /* ------------------------------------------------------------ */
@@ -415,11 +430,13 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** Pre configure the web application.
+    /** 
+     * Pre configure the web application.
      * <p>
      * The method is normally called from {@link #start()}. It performs
      * the discovery of the configurations to be applied to this context,
-     * specifically:<ul>
+     * specifically:
+     * <ul>
      * <li>Instantiate the {@link Configuration} instances with a call to {@link #loadConfigurations()}.
      * <li>Setup the default System classes by calling {@link #loadSystemClasses()}
      * <li>Setup the default Server classes by calling <code>loadServerClasses()</code>
@@ -427,7 +444,7 @@
      * <li>Calls the {@link Configuration#preConfigure(WebAppContext)} method of all
      * Configuration instances.
      * </ul>
-     * @throws Exception
+     * @throws Exception if unable to pre configure
      */
     public void preConfigure() throws Exception
     {
@@ -527,25 +544,6 @@
     protected void doStop() throws Exception
     {
         super.doStop();
-
-        try
-        {
-            for (int i=_configurations.size();i-->0;)
-                _configurations.get(i).deconfigure(this);
-
-            if (_metadata != null)
-                _metadata.clear();
-            _metadata=new MetaData();
-
-        }
-        finally
-        {
-            if (_ownClassLoader)
-                setClassLoader(null);
-
-            setAvailable(true);
-            _unavailableException=null;
-        }
     }
 
     /* ------------------------------------------------------------ */
@@ -683,7 +681,7 @@
         if (_serverClasses == null)
             loadServerClasses();
 
-        _serverClasses.addPattern(classOrPackage);
+        _serverClasses.add(classOrPackage);
     }
 
     /* ------------------------------------------------------------ */
@@ -733,7 +731,7 @@
         if (_systemClasses == null)
             loadSystemClasses();
 
-        _systemClasses.addPattern(classOrPackage);
+        _systemClasses.add(classOrPackage);
     }
 
 
@@ -754,11 +752,33 @@
 
         _systemClasses.prependPattern(classOrPackage);
     }
-
+    
+    /* ------------------------------------------------------------ */
+    /** Call a Callable for which all calls to {@link #isServerClass(String)}
+     * will return false.
+     * @param callable The callable to call.
+     * @throws Exception Any exception thrown by the Callable
+     */
+    public void runWithoutCheckingServerClasses(Callable<Void> callable) throws Exception
+    {
+        _checkingServerClasses=false;
+        try
+        {
+            callable.call();
+        }
+        finally
+        {
+            _checkingServerClasses=true;
+        }
+    }
+    
     /* ------------------------------------------------------------ */
     @Override
     public boolean isServerClass(String name)
     {
+        if (!_checkingServerClasses)
+            return false;
+        
         if (_serverClasses == null)
             loadServerClasses();
 
@@ -947,7 +967,22 @@
         }
         return super.toString();
     }
-
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        dumpBeans(out,indent,
+            Collections.singletonList(new ClassLoaderDump(getClassLoader())),
+            Collections.singletonList(new DumpableCollection("Systemclasses "+this,_systemClasses)),
+            Collections.singletonList(new DumpableCollection("Serverclasses "+this,_serverClasses)),
+            Collections.singletonList(new DumpableCollection("Configurations "+this,_configurations)),
+            Collections.singletonList(new DumpableCollection("Handler attributes "+this,((AttributesMap)getAttributes()).getAttributeEntrySet())),
+            Collections.singletonList(new DumpableCollection("Context attributes "+this,((Context)getServletContext()).getAttributeEntrySet())),
+            Collections.singletonList(new DumpableCollection("Initparams "+this,getInitParams().entrySet()))
+            );
+    }
+    
     /* ------------------------------------------------------------ */
     /**
      * @param configurations The configuration class names.  If setConfigurations is not called
@@ -1064,7 +1099,7 @@
     /* ------------------------------------------------------------ */
     /** Add EventListener
      * Convenience method that calls {@link #setEventListeners(EventListener[])}
-     * @param listener
+     * @param listener the listener to add
      */
     @Override
     public void addEventListener(EventListener listener)
@@ -1152,9 +1187,10 @@
      *
      * In certain circumstances you want may want to deny access of one webapp from another
      * when you may not fully trust the webapp.  Setting this white list will enable a
-     * check when a servlet called getContext(String), validating that the uriInPath
+     * check when a servlet called {@link org.eclipse.jetty.servlet.ServletContextHandler.Context#getContext(String)}, validating that the uriInPath
      * for the given webapp has been declaratively allows access to the context.
-     * @param contextWhiteList
+     * 
+     * @param contextWhiteList the whitelist of contexts for {@link org.eclipse.jetty.servlet.ServletContextHandler.Context#getContext(String)} 
      */
     public void setContextWhiteList(String[] contextWhiteList)
     {
@@ -1168,12 +1204,7 @@
      * Server classes/packages are classes used to implement the server and are hidden
      * from the context.  If the context needs to load these classes, it must have its
      * own copy of them in WEB-INF/lib or WEB-INF/classes.
-     * A class pattern is a string of one of the forms:<dl>
-     * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
-     * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
-     * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
-     * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
-     * </dl>
+     * A {@link ClasspathPattern} is used to match the server classes.
      * @param serverClasses The serverClasses to set.
      */
     public void setServerClasses(String[] serverClasses)
@@ -1188,12 +1219,7 @@
      * System classes/packages are classes provided by the JVM and that
      * cannot be replaced by classes of the same name from WEB-INF,
      * regardless of the value of {@link #setParentLoaderPriority(boolean)}.
-     * A class pattern is a string of one of the forms:<dl>
-     * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
-     * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
-     * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
-     * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
-     * </dl>
+     * A {@link ClasspathPattern} is used to match the system classes.
      * @param systemClasses The systemClasses to set.
      */
     public void setSystemClasses(String[] systemClasses)
@@ -1234,7 +1260,7 @@
      * webapp will be kept when the webapp stops. Otherwise,
      * it will be deleted.
      * 
-     * @param delete
+     * @param persist true to persist the temp directory on shutdown / exit of the webapp
      */
     public void setPersistTempDirectory(boolean persist)
     {
@@ -1242,7 +1268,7 @@
     }
     
     /**
-     * @return
+     * @return true if tmp directory will persist between startups of the webapp
      */
     public boolean isPersistTempDirectory()
     {
@@ -1341,6 +1367,37 @@
 
         startWebapp();
     }
+    
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void stopContext() throws Exception
+    {
+        stopWebapp();
+        try
+        {
+            for (int i=_configurations.size();i-->0;)
+                _configurations.get(i).deconfigure(this);
+
+            if (_metadata != null)
+                _metadata.clear();
+            _metadata=new MetaData();
+
+        }
+        finally
+        {
+            if (_ownClassLoader)
+            {
+                ClassLoader loader = getClassLoader();
+                if (loader != null && loader instanceof URLClassLoader)
+                    ((URLClassLoader)loader).close(); 
+                setClassLoader(null);
+            }
+
+            setAvailable(true);
+            _unavailableException=null;
+        }
+    }
 
     /* ------------------------------------------------------------ */
     protected void startWebapp()
@@ -1349,6 +1406,11 @@
         super.startContext();
     }
     
+    /* ------------------------------------------------------------ */
+    protected void stopWebapp() throws Exception
+    {
+        super.stopContext();
+    }
     /* ------------------------------------------------------------ */    
     @Override
     public Set<String> setServletSecurity(Dynamic registration, ServletSecurityElement servletSecurityElement)
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java
index c6b9bd1..7282d77 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java
@@ -192,15 +192,17 @@
         //if we're not persisting the temp dir contents delete it
         if (!context.isPersistTempDirectory())
         {
-            IO.delete(context.getTempDirectory());
+           IO.delete(context.getTempDirectory());
         }
         
         //if it wasn't explicitly configured by the user, then unset it
-        Boolean tmpdirConfigured = (Boolean)context.getAttribute(TEMPDIR_CONFIGURED);
+       Boolean tmpdirConfigured = (Boolean)context.getAttribute(TEMPDIR_CONFIGURED);
         if (tmpdirConfigured != null && !tmpdirConfigured) 
             context.setTempDirectory(null);
 
         //reset the base resource back to what it was before we did any unpacking of resources
+        if (context.getBaseResource() != null)
+            context.getBaseResource().close();
         context.setBaseResource(_preUnpackBaseResource);
     }
 
@@ -227,8 +229,8 @@
      * Get a temporary directory in which to unpack the war etc etc.
      * The algorithm for determining this is to check these alternatives
      * in the order shown:
-     *
-     * <p>A. Try to use an explicit directory specifically for this webapp:</p>
+     * <p>
+     * A. Try to use an explicit directory specifically for this webapp:
      * <ol>
      * <li>
      * Iff an explicit directory is set for this webapp, use it. Set delete on
@@ -236,19 +238,21 @@
      * </li>
      * <li>
      * Iff javax.servlet.context.tempdir context attribute is set for
-     * this webapp && exists && writeable, then use it. Set delete on exit depends on
+     * this webapp &amp;&amp; exists &amp;&amp; writeable, then use it. Set delete on exit depends on
      * value of persistTempDirectory.
      * </li>
      * </ol>
      *
-     * <p>B. Create a directory based on global settings. The new directory
-     * will be called "Jetty-"+host+"-"+port+"__"+context+"-"+virtualhost+"-"+randomdigits+".dir"
-     * </p>
+     * <p>
+     * B. Create a directory based on global settings. The new directory
+     * will be called <code>"Jetty-"+host+"-"+port+"__"+context+"-"+virtualhost+"-"+randomdigits+".dir"</code>
      * <p>
      * If the user has specified the context attribute org.eclipse.jetty.webapp.basetempdir, the
      * directory specified by this attribute will be the parent of the temp dir created. Otherwise,
-     * the parent dir is $(java.io.tmpdir). Set delete on exit depends on value of persistTempDirectory. 
-     * </p>
+     * the parent dir is <code>${java.io.tmpdir}</code>. Set delete on exit depends on value of persistTempDirectory.
+     *  
+     * @param context the context to resolve the temp directory from
+     * @throws Exception if unable to resolve the temp directory
      */
     public void resolveTempDirectory (WebAppContext context)
     throws Exception
@@ -402,7 +406,7 @@
                 throw new IllegalStateException("No resourceBase or war set for context");
 
             // Accept aliases for WAR files
-            if (web_app.getAlias() != null)
+            if (web_app.isAlias())
             {
                 LOG.debug(web_app + " anti-aliased to " + web_app.getAlias());
                 web_app = context.newResource(web_app.getAlias());
@@ -546,18 +550,18 @@
         }
     }
 
-
-
-
     /**
      * Create a canonical name for a webapp temp directory.
+     * <p>
      * The form of the name is:
-     *  <code>"jetty-"+host+"-"+port+"-"+resourceBase+"-_"+context+"-"+virtualhost+"-"+randomdigits+".dir"</code>
+     * 
+     * <pre>"jetty-"+host+"-"+port+"-"+resourceBase+"-_"+context+"-"+virtualhost+"-"+randomdigits+".dir"</pre>
      *
      *  host and port uniquely identify the server
      *  context and virtual host uniquely identify the webapp
      *  randomdigits ensure every tmp directory is unique
      *  
+     * @param context the context to get the canonical name from 
      * @return the canonical name for the webapp temp directory
      */
     public static String getCanonicalNameForWebAppTmpDir (WebAppContext context)
@@ -598,7 +602,6 @@
             }
         }
 
-
         //Resource  base
         try
         {
@@ -678,9 +681,9 @@
     /**
      * Look for jars that should be treated as if they are in WEB-INF/lib
      * 
-     * @param context
+     * @param context the context to find the jars in
      * @return the list of jar resources found within context
-     * @throws Exception
+     * @throws Exception if unable to find the jars
      */
     protected List<Resource> findJars (WebAppContext context)
     throws Exception
@@ -696,11 +699,11 @@
     }
     
     /**
-     *  Look for jars in WEB-INF/lib
+     * Look for jars in <code>WEB-INF/lib</code>
      *  
-     * @param context
-     * @return
-     * @throws Exception
+     * @param context the context to find the lib jars in
+     * @return the list of jars as {@link Resource}
+     * @throws Exception if unable to scan for lib jars
      */
     protected List<Resource> findWebInfLibJars(WebAppContext context)
     throws Exception
@@ -741,9 +744,9 @@
     /**
      * Get jars from WebAppContext.getExtraClasspath as resources
      * 
-     * @param context
-     * @return
-     * @throws Exception
+     * @param context the context to find extra classpath jars in
+     * @return the list of Resources with the extra classpath, or null if not found
+     * @throws Exception if unable to find the extra classpath jars
      */
     protected List<Resource>  findExtraClasspathJars(WebAppContext context)
     throws Exception
@@ -769,11 +772,11 @@
     }
     
     /**
-     * Get WEB-INF/classes dir
+     * Get <code>WEB-INF/classes</code> dir
      * 
-     * @param context
-     * @return
-     * @throws Exception
+     * @param context the context to look for the <code>WEB-INF/classes</code> directory
+     * @return the Resource for the <code>WEB-INF/classes</code> directory
+     * @throws Exception if unable to find the <code>WEB-INF/classes</code> directory
      */
     protected Resource findWebInfClassesDir (WebAppContext context)
     throws Exception
@@ -798,9 +801,9 @@
     /**
      * Get class dirs from WebAppContext.getExtraClasspath as resources
      * 
-     * @param context
-     * @return
-     * @throws Exception
+     * @param context the context to look for extra classpaths in
+     * @return the list of Resources to the extra classpath 
+     * @throws Exception if unable to find the extra classpaths
      */
     protected List<Resource>  findExtraClasspathDirs(WebAppContext context)
     throws Exception
diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/ClasspathPatternTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/ClasspathPatternTest.java
new file mode 100644
index 0000000..42593ff
--- /dev/null
+++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/ClasspathPatternTest.java
@@ -0,0 +1,118 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.webapp;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class ClasspathPatternTest
+{
+    private final ClasspathPattern pattern = new ClasspathPattern();
+    
+    @Before
+    public void before()
+    {
+        pattern.clear();
+        pattern.add("org.package.");
+        pattern.add("-org.excluded.");
+        pattern.add("org.example.FooBar");
+        pattern.add("-org.example.Excluded");
+        pattern.addAll(Arrays.asList(new String[]{"-org.example.Nested$Minus","org.example.Nested","org.example.Nested$Something"}));
+    }
+    
+    @Test
+    public void testClassMatch()
+    {
+        assertTrue(pattern.match("org.example.FooBar"));
+        assertTrue(pattern.match("org.example.Nested"));
+        
+        assertFalse(pattern.match("org.example.Unknown"));
+        assertFalse(pattern.match("org.example.FooBar.Unknown"));
+    }
+    
+    @Test
+    public void testPackageMatch()
+    {
+        assertTrue(pattern.match("org.package.Something"));
+        assertTrue(pattern.match("org.package.other.Something"));
+        
+        assertFalse(pattern.match("org.example.Unknown"));
+        assertFalse(pattern.match("org.example.FooBar.Unknown"));
+        assertFalse(pattern.match("org.example.FooBarElse"));
+    }
+
+    @Test
+    public void testExplicitNestedMatch()
+    {
+        assertTrue(pattern.match("org.example.Nested$Something"));
+        assertFalse(pattern.match("org.example.Nested$Minus"));
+        assertTrue(pattern.match("org.example.Nested$Other"));
+    }
+
+    @Test
+    public void testImplicitNestedMatch()
+    {
+        assertTrue(pattern.match("org.example.FooBar$Other"));
+        assertTrue(pattern.match("org.example.Nested$Other"));
+    }
+    
+    @Test
+    public void testAddBefore()
+    {
+        pattern.addBefore("-org.excluded.","org.excluded.ExceptionOne","org.excluded.ExceptionTwo");
+
+        assertTrue(pattern.match("org.excluded.ExceptionOne"));
+        assertTrue(pattern.match("org.excluded.ExceptionTwo"));
+        
+        assertFalse(pattern.match("org.example.Unknown"));
+    }
+    
+    @Test
+    public void testAddAfter()
+    {
+        pattern.addAfter("org.package.","org.excluded.ExceptionOne","org.excluded.ExceptionTwo");
+
+        assertTrue(pattern.match("org.excluded.ExceptionOne"));
+        assertTrue(pattern.match("org.excluded.ExceptionTwo"));
+        
+        assertFalse(pattern.match("org.example.Unknown"));
+    }
+
+    @Test
+    public void testDoubledNested()
+    {
+        assertTrue(pattern.match("org.example.Nested$Something$Else"));
+        
+        assertFalse(pattern.match("org.example.Nested$Minus$Else"));
+    }
+
+    @Test
+    public void testMatchAll()
+    {
+        pattern.clear();
+        pattern.add(".");
+        assertTrue(pattern.match("org.example.Anything"));
+        assertTrue(pattern.match("org.example.Anything$Else"));
+    }
+}
diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java
index 8900048..23aa83b 100644
--- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java
+++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java
@@ -18,6 +18,7 @@
 
 package org.eclipse.jetty.webapp;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -37,8 +38,6 @@
 
 /**
  * OrderingTest
- *
- *
  */
 public class OrderingTest
 {
@@ -111,9 +110,6 @@
             return _name;
         }
         
-        /**
-         * @see org.eclipse.jetty.util.resource.Resource#getURL()
-         */
         @Override
         public URL getURL()
         {
@@ -510,6 +506,22 @@
     }
 
     @Test
+    public void testOrderFragments() throws Exception 
+    {
+        final MetaData metadata = new MetaData();
+        final Resource jarResource = new TestResource("A");
+
+        metadata.setOrdering(new Ordering.RelativeOrdering(metadata));
+        metadata.addWebInfJar(jarResource);
+        metadata.orderFragments();
+        assertEquals(1, metadata.getOrderedWebInfJars().size());
+        metadata.orderFragments();
+        assertEquals(1, metadata.getOrderedWebInfJars().size());
+    }
+
+
+
+    @Test
     public void testCircular1 ()
     throws Exception
     {
diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java
index 656c2a1..9d15afd 100644
--- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java
+++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java
@@ -18,33 +18,45 @@
 
 package org.eclipse.jetty.webapp;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.eclipse.jetty.toolchain.test.ExtraMatchers.*; 
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
 
 import java.lang.instrument.ClassFileTransformer;
 import java.lang.instrument.IllegalClassFormatException;
+import java.net.URI;
 import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Path;
 import java.security.ProtectionDomain;
 import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.Iterator;
+import java.util.Collections;
 import java.util.List;
 
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.resource.PathResource;
 import org.eclipse.jetty.util.resource.Resource;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 public class WebAppClassLoaderTest
 {
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    private Path testWebappDir;
     private WebAppContext _context;
     private WebAppClassLoader _loader;
 
     @Before
     public void init() throws Exception
     {
-        Resource webapp = Resource.newResource("./src/test/webapp");
+        this.testWebappDir = MavenTestingUtils.getProjectDirPath("src/test/webapp");
+        Resource webapp = new PathResource(testWebappDir);
+        
+        System.err.printf("testWebappDir = %s%n", testWebappDir);
 
         _context = new WebAppContext();
         _context.setBaseResource(webapp);
@@ -55,16 +67,39 @@
         _loader.addClassPath(webapp.addPath("WEB-INF/classes"));
         _loader.setName("test");
     }
+    
+    public void assertCanLoadClass(String clazz) throws ClassNotFoundException
+    {
+        assertThat("Can Load Class ["+clazz+"]", _loader.loadClass(clazz), notNullValue());
+    }
+    
+    public void assertCanLoadResource(String res) throws ClassNotFoundException
+    {
+        assertThat("Can Load Resource ["+res+"]", _loader.getResource(res), notNullValue());
+    }
+    
+    public void assertCantLoadClass(String clazz)
+    {
+        try
+        {
+            assertThat("Can't Load Class ["+clazz+"]", _loader.loadClass(clazz), nullValue());
+        }
+        catch (ClassNotFoundException e)
+        {
+            // Valid path
+        }
+    }
 
     @Test
     public void testParentLoad() throws Exception
     {
         _context.setParentLoaderPriority(true);
-        assertTrue(canLoadClass("org.acme.webapp.ClassInJarA"));
-        assertTrue(canLoadClass("org.acme.webapp.ClassInJarB"));
-        assertTrue(canLoadClass("org.acme.other.ClassInClassesC"));
+        
+        assertCanLoadClass("org.acme.webapp.ClassInJarA");
+        assertCanLoadClass("org.acme.webapp.ClassInJarB");
+        assertCanLoadClass("org.acme.other.ClassInClassesC");
 
-        assertTrue(cantLoadClass("org.eclipse.jetty.webapp.Configuration"));
+        assertCantLoadClass("org.eclipse.jetty.webapp.Configuration");
 
         Class<?> clazzA = _loader.loadClass("org.acme.webapp.ClassInJarA");
         assertTrue(clazzA.getField("FROM_PARENT")!=null);
@@ -74,22 +109,15 @@
     public void testWebAppLoad() throws Exception
     {
         _context.setParentLoaderPriority(false);
-        assertTrue(canLoadClass("org.acme.webapp.ClassInJarA"));
-        assertTrue(canLoadClass("org.acme.webapp.ClassInJarB"));
-        assertTrue(canLoadClass("org.acme.other.ClassInClassesC"));
+        assertCanLoadClass("org.acme.webapp.ClassInJarA");
+        assertCanLoadClass("org.acme.webapp.ClassInJarB");
+        assertCanLoadClass("org.acme.other.ClassInClassesC");
 
-        assertTrue(cantLoadClass("org.eclipse.jetty.webapp.Configuration"));
+        assertCantLoadClass("org.eclipse.jetty.webapp.Configuration");
 
         Class<?> clazzA = _loader.loadClass("org.acme.webapp.ClassInJarA");
-        try
-        {
-            clazzA.getField("FROM_PARENT");
-            assertTrue(false);
-        }
-        catch(NoSuchFieldException e)
-        {
-            assertTrue(true);
-        }
+        expectedException.expect(NoSuchFieldException.class);
+        clazzA.getField("FROM_PARENT");
     }
     
     @Test
@@ -123,20 +151,20 @@
         });
         
         _context.setParentLoaderPriority(false);
-        assertTrue(canLoadClass("org.acme.webapp.ClassInJarA"));
-        assertTrue(canLoadClass("org.acme.webapp.ClassInJarB"));
-        assertTrue(canLoadClass("org.acme.other.ClassInClassesC"));
-        assertTrue(canLoadClass("java.lang.String"));
-        assertTrue(cantLoadClass("org.eclipse.jetty.webapp.Configuration"));
         
-        Iterator<Object> iter = results.iterator();
-        assertEquals(_loader,iter.next());
-        assertEquals("org.acme.webapp.ClassInJarA",iter.next());
-        assertEquals(_loader,iter.next());
-        assertEquals("org.acme.webapp.ClassInJarB",iter.next());
-        assertEquals(_loader,iter.next());
-        assertEquals("org.acme.other.ClassInClassesC",iter.next());
-        assertFalse(iter.hasNext());
+        assertCanLoadClass("org.acme.webapp.ClassInJarA");
+        assertCanLoadClass("org.acme.webapp.ClassInJarB");
+        assertCanLoadClass("org.acme.other.ClassInClassesC");
+        assertCanLoadClass("java.lang.String");
+        assertCantLoadClass("org.eclipse.jetty.webapp.Configuration");
+        
+        assertThat("Classname Results", results, contains(
+                _loader,
+                "org.acme.webapp.ClassInJarA",
+                _loader,
+                "org.acme.webapp.ClassInJarB",
+                _loader,
+                "org.acme.other.ClassInClassesC"));
     }
     
     @Test
@@ -151,8 +179,7 @@
             }
         });
         
-        
-        assertTrue(canLoadClass("org.acme.webapp.ClassInJarA"));
+        assertCanLoadClass("org.acme.webapp.ClassInJarA");
     }
 
     @Test
@@ -164,12 +191,12 @@
         System.arraycopy(oldSC,0,newSC,1,oldSC.length);
         _context.setServerClasses(newSC);
 
-        assertTrue(canLoadClass("org.acme.webapp.ClassInJarA"));
-        assertTrue(canLoadClass("org.acme.webapp.ClassInJarB"));
-        assertTrue(canLoadClass("org.acme.other.ClassInClassesC"));
+        assertCanLoadClass("org.acme.webapp.ClassInJarA");
+        assertCanLoadClass("org.acme.webapp.ClassInJarB");
+        assertCanLoadClass("org.acme.other.ClassInClassesC");
 
-        assertTrue(canLoadClass("org.eclipse.jetty.webapp.Configuration"));
-        assertTrue(cantLoadClass("org.eclipse.jetty.webapp.JarScanner"));
+        assertCanLoadClass("org.eclipse.jetty.webapp.Configuration");
+        assertCantLoadClass("org.eclipse.jetty.webapp.JarScanner");
     }
 
     @Test
@@ -187,11 +214,11 @@
         System.arraycopy(oldSysC,0,newSysC,1,oldSysC.length);
         _context.setSystemClasses(newSysC);
 
-        assertTrue(canLoadClass("org.acme.webapp.ClassInJarA"));
-        assertTrue(canLoadClass("org.acme.webapp.ClassInJarB"));
-        assertTrue(canLoadClass("org.acme.other.ClassInClassesC"));
-        assertTrue(cantLoadClass("org.eclipse.jetty.webapp.Configuration"));
-        assertTrue(cantLoadClass("org.eclipse.jetty.webapp.JarScanner"));
+        assertCanLoadClass("org.acme.webapp.ClassInJarA");
+        assertCanLoadClass("org.acme.webapp.ClassInJarB");
+        assertCanLoadClass("org.acme.other.ClassInClassesC");
+        assertCantLoadClass("org.eclipse.jetty.webapp.Configuration");
+        assertCantLoadClass("org.eclipse.jetty.webapp.JarScanner");
 
         oldSysC=_context.getSystemClasses();
         newSysC=new String[oldSysC.length+1];
@@ -199,7 +226,7 @@
         System.arraycopy(oldSysC,0,newSysC,1,oldSysC.length);
         _context.setSystemClasses(newSysC);
 
-        assertNotNull(_loader.getResource("org/acme/webapp/ClassInJarA.class"));
+        assertCanLoadResource("org/acme/webapp/ClassInJarA.class");
         _context.setSystemClasses(oldSysC);
 
         oldServC=_context.getServerClasses();
@@ -207,27 +234,54 @@
         newServC[0]="org.acme.webapp.ClassInJarA";
         System.arraycopy(oldServC,0,newServC,1,oldServC.length);
         _context.setServerClasses(newServC);
-        assertNotNull(_loader.getResource("org/acme/webapp/ClassInJarA.class"));
+        assertCanLoadResource("org/acme/webapp/ClassInJarA.class");
     }
 
     @Test
     public void testResources() throws Exception
     {
+        List<URL> expected = new ArrayList<>();
         List<URL> resources;
+        
+        // Expected Locations
+        URL webappWebInfLibAcme = new URI("jar:" + testWebappDir.resolve("WEB-INF/lib/acme.jar").toUri().toASCIIString() + "!/org/acme/resource.txt").toURL();
+        URL webappWebInfClasses = testWebappDir.resolve("WEB-INF/classes/org/acme/resource.txt").toUri().toURL();
+        // (from parent classloader)
+        URL targetTestClasses = this.getClass().getClassLoader().getResource("org/acme/resource.txt");
 
         _context.setParentLoaderPriority(false);
-        resources =toList( _loader.getResources("org/acme/resource.txt"));
-        assertEquals(3,resources.size());
-        assertEquals(0,resources.get(0).toString().indexOf("jar:file:"));
-        assertEquals(-1,resources.get(1).toString().indexOf("test-classes"));
-        assertEquals(0,resources.get(2).toString().indexOf("file:"));
+        dump(_context);
+        resources =Collections.list(_loader.getResources("org/acme/resource.txt"));
+        
+        expected.clear();
+        expected.add(webappWebInfLibAcme);
+        expected.add(webappWebInfClasses);
+        expected.add(targetTestClasses);
+        
+        assertThat("Resources Found (Parent Loader Priority == false)",resources,ordered(expected));
+        
+//        dump(resources);
+//        assertEquals(3,resources.size());
+//        assertEquals(0,resources.get(0).toString().indexOf("jar:file:"));
+//        assertEquals(-1,resources.get(1).toString().indexOf("test-classes"));
+//        assertEquals(0,resources.get(2).toString().indexOf("file:"));
 
         _context.setParentLoaderPriority(true);
-        resources =toList( _loader.getResources("org/acme/resource.txt"));
-        assertEquals(3,resources.size());
-        assertEquals(0,resources.get(0).toString().indexOf("file:"));
-        assertEquals(0,resources.get(1).toString().indexOf("jar:file:"));
-        assertEquals(-1,resources.get(2).toString().indexOf("test-classes"));
+        // dump(_context);
+        resources =Collections.list(_loader.getResources("org/acme/resource.txt"));
+        
+        expected.clear();
+        expected.add(targetTestClasses);
+        expected.add(webappWebInfLibAcme);
+        expected.add(webappWebInfClasses);
+        
+        assertThat("Resources Found (Parent Loader Priority == true)",resources,ordered(expected));
+        
+//        dump(resources);
+//        assertEquals(3,resources.size());
+//        assertEquals(0,resources.get(0).toString().indexOf("file:"));
+//        assertEquals(0,resources.get(1).toString().indexOf("jar:file:"));
+//        assertEquals(-1,resources.get(2).toString().indexOf("test-classes"));
 
         String[] oldServC=_context.getServerClasses();
         String[] newServC=new String[oldServC.length+1];
@@ -236,10 +290,19 @@
         _context.setServerClasses(newServC);
 
         _context.setParentLoaderPriority(true);
-        resources =toList( _loader.getResources("org/acme/resource.txt"));
-        assertEquals(2,resources.size());
-        assertEquals(0,resources.get(0).toString().indexOf("jar:file:"));
-        assertEquals(0,resources.get(1).toString().indexOf("file:"));
+        // dump(_context);
+        resources =Collections.list(_loader.getResources("org/acme/resource.txt"));
+        
+        expected.clear();
+        expected.add(webappWebInfLibAcme);
+        expected.add(webappWebInfClasses);
+        
+        assertThat("Resources Found (Parent Loader Priority == true) (with serverClasses filtering)",resources,ordered(expected));
+        
+//        dump(resources);
+//        assertEquals(2,resources.size());
+//        assertEquals(0,resources.get(0).toString().indexOf("jar:file:"));
+//        assertEquals(0,resources.get(1).toString().indexOf("file:"));
 
         _context.setServerClasses(oldServC);
         String[] oldSysC=_context.getSystemClasses();
@@ -249,33 +312,57 @@
         _context.setSystemClasses(newSysC);
 
         _context.setParentLoaderPriority(true);
-        resources =toList( _loader.getResources("org/acme/resource.txt"));
-        assertEquals(1,resources.size());
-        assertEquals(0,resources.get(0).toString().indexOf("file:"));
+        // dump(_context);
+        resources =Collections.list(_loader.getResources("org/acme/resource.txt"));
+        
+        expected.clear();
+        expected.add(targetTestClasses);
+        
+        assertThat("Resources Found (Parent Loader Priority == true) (with systemClasses filtering)",resources,ordered(expected));
+        
+//        dump(resources);
+//        assertEquals(1,resources.size());
+//        assertEquals(0,resources.get(0).toString().indexOf("file:"));
     }
 
-    private List<URL> toList(Enumeration<URL> e)
+    private void dump(WebAppContext wac)
     {
-        List<URL> list = new ArrayList<URL>();
-        while (e!=null && e.hasMoreElements())
-            list.add(e.nextElement());
-        return list;
+        System.err.println("--Dump WebAppContext - " + wac);
+        System.err.printf("  context.getClass().getClassLoader() = %s%n",wac.getClass().getClassLoader());
+        dumpClassLoaderHierarchy("  ",wac.getClass().getClassLoader());
+        System.err.printf("  context.getClassLoader() = %s%n",wac.getClassLoader());
+        dumpClassLoaderHierarchy("  ",wac.getClassLoader());
     }
 
-    private boolean canLoadClass(String clazz) throws ClassNotFoundException
+    private void dumpClassLoaderHierarchy(String indent, ClassLoader classLoader)
     {
-        return _loader.loadClass(clazz)!=null;
-    }
-
-    private boolean cantLoadClass(String clazz)
-    {
-        try
+        if (classLoader != null)
         {
-            return _loader.loadClass(clazz)==null;
+            if(classLoader instanceof URLClassLoader)
+            {
+                URLClassLoader urlCL = (URLClassLoader)classLoader;
+                URL urls[] = urlCL.getURLs();
+                for (URL url : urls)
+                {
+                    System.err.printf("%s url[] = %s%n",indent,url);
+                }
+            }
+            
+            ClassLoader parent = classLoader.getParent();
+            if (parent != null)
+            {
+                System.err.printf("%s .parent = %s%n",indent,parent);
+                dumpClassLoaderHierarchy(indent + "  ",parent);
+            }
         }
-        catch(ClassNotFoundException e)
+    }
+
+    private void dump(List<URL> resources)
+    {
+        System.err.println("--Dump--");
+        for(URL url: resources)
         {
-            return true;
+            System.err.printf(" \"%s\"%n",url);
         }
     }
 }
diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java
index be3c12f..be6e026 100644
--- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java
+++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java
@@ -18,29 +18,35 @@
 
 package org.eclipse.jetty.webapp;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 import javax.servlet.GenericServlet;
 import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 
-import junit.framework.Assert;
-
 import org.eclipse.jetty.server.LocalConnector;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
 import org.eclipse.jetty.server.handler.HandlerList;
+import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.util.resource.ResourceCollection;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
 import org.junit.Test;
 
 public class WebAppContextTest
@@ -51,7 +57,7 @@
         Server server = new Server();
         //test if no classnames set, its the defaults
         WebAppContext wac = new WebAppContext();
-        Assert.assertEquals(0,wac.getConfigurations().length);
+        assertEquals(0,wac.getConfigurations().length);
         String[] classNames = wac.getConfigurationClasses();
         assertNotNull(classNames);
 
@@ -123,7 +129,7 @@
     /**
      * tests that the servlet context white list works
      *
-     * @throws Exception
+     * @throws Exception on test failure
      */
     @Test
     public void testContextWhiteList() throws Exception
@@ -237,4 +243,80 @@
             this.getServletContext().getContext("/B/s");
         }
     }
+    
+    @Test
+    public void testServletContextListener() throws Exception
+    {
+        Server server = new Server();
+        ServletContextHandler context = new ServletContextHandler(
+                ServletContextHandler.SESSIONS);
+        context.setContextPath("/");
+        context.setResourceBase(System.getProperty("java.io.tmpdir"));
+        server.setHandler(context);
+
+        final List<String> history=new ArrayList<>();
+        
+        context.addEventListener(new ServletContextListener()
+        {
+            @Override
+            public void contextInitialized(ServletContextEvent servletContextEvent)
+            {
+                history.add("I1");
+            }
+
+            @Override
+            public void contextDestroyed(ServletContextEvent servletContextEvent)
+            {
+                history.add("D1");
+            }
+        });
+        context.addEventListener(new ServletContextListener()
+        {
+            @Override
+            public void contextInitialized(ServletContextEvent servletContextEvent)
+            {
+                history.add("I2");
+                throw new RuntimeException("Listener2 broken");
+            }
+
+            @Override
+            public void contextDestroyed(ServletContextEvent servletContextEvent)
+            {
+                history.add("D2");
+            }
+        });
+        context.addEventListener(new ServletContextListener()
+        {
+            @Override
+            public void contextInitialized(ServletContextEvent servletContextEvent)
+            {
+                history.add("I3");
+            }
+
+            @Override
+            public void contextDestroyed(ServletContextEvent servletContextEvent)
+            {
+                history.add("D3");
+            }
+        });
+        
+        try
+        {
+            server.start();
+        }
+        catch(Exception e)
+        {
+            // System.err.println(e);
+        }
+        finally
+        {
+            server.stop();
+        }
+          
+        // TODO should be either:
+        // Assert.assertThat(history,Matchers.contains("I1","I2","D1"));
+        // or
+        // Assert.assertThat(history,Matchers.contains("I1","I2","D3","D3","D1"));
+        Assert.assertThat(history,Matchers.contains("I1","I2","D3","D2","D1"));
+    }
 }
diff --git a/jetty-webapp/src/test/resources/jetty-logging.properties b/jetty-webapp/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..4c4c7f8
--- /dev/null
+++ b/jetty-webapp/src/test/resources/jetty-logging.properties
@@ -0,0 +1,6 @@
+# Setup default logging implementation for during testing
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+org.eclipse.jetty.LEVEL=INFO
+# org.eclipse.jetty.webapp.WebAppClassLoader.LEVEL=DEBUG
+# org.eclipse.jetty.util.LEVEL=DEBUG
+# org.eclipse.jetty.util.PathWatcher.Noisy.LEVEL=OFF
diff --git a/jetty-websocket/javax-websocket-client-impl/pom.xml b/jetty-websocket/javax-websocket-client-impl/pom.xml
index da2ee59..ea1ce40 100644
--- a/jetty-websocket/javax-websocket-client-impl/pom.xml
+++ b/jetty-websocket/javax-websocket-client-impl/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <groupId>org.eclipse.jetty.websocket</groupId>
     <artifactId>websocket-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/AbstractJsrRemote.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/AbstractJsrRemote.java
index adc9cb8..adb4445 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/AbstractJsrRemote.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/AbstractJsrRemote.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.concurrent.Future;
+
 import javax.websocket.EncodeException;
 import javax.websocket.Encoder;
 import javax.websocket.RemoteEndpoint;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java
index 3063c56..3a290b1 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java
@@ -20,16 +20,15 @@
 
 import java.io.IOException;
 import java.net.URI;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Future;
+
 import javax.websocket.ClientEndpoint;
 import javax.websocket.ClientEndpointConfig;
 import javax.websocket.DeploymentException;
@@ -39,18 +38,22 @@
 import javax.websocket.Session;
 import javax.websocket.WebSocketContainer;
 
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.DecoratedObjectFactory;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.ShutdownThread;
 import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
 import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
 import org.eclipse.jetty.websocket.client.WebSocketClient;
 import org.eclipse.jetty.websocket.client.io.UpgradeListener;
-import org.eclipse.jetty.websocket.common.SessionListener;
 import org.eclipse.jetty.websocket.common.WebSocketSession;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
 import org.eclipse.jetty.websocket.jsr356.client.AnnotatedClientEndpointMetadata;
 import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig;
@@ -66,46 +69,42 @@
  * <p>
  * This should be specific to a JVM if run in a standalone mode. or specific to a WebAppContext if running on the Jetty server.
  */
-public class ClientContainer extends ContainerLifeCycle implements WebSocketContainer, SessionListener
+public class ClientContainer extends ContainerLifeCycle implements WebSocketContainer, WebSocketContainerScope
 {
     private static final Logger LOG = Log.getLogger(ClientContainer.class);
-    
+
+    /** The delegated Container Scope */
+    private final WebSocketContainerScope scopeDelegate;
     /** Tracking all primitive decoders for the container */
     private final DecoderFactory decoderFactory;
     /** Tracking all primitive encoders for the container */
     private final EncoderFactory encoderFactory;
-
     /** Tracking for all declared Client endpoints */
     private final Map<Class<?>, EndpointMetadata> endpointClientMetadataCache;
-    /** Tracking for all open Sessions */
-    private Set<Session> openSessions = new CopyOnWriteArraySet<>();
     /** The jetty websocket client in use for this container */
     private WebSocketClient client;
 
     public ClientContainer()
     {
         // This constructor is used with Standalone JSR Client usage.
-        this(null);
+        this(new SimpleContainerScope(WebSocketPolicy.newClientPolicy()));
         client.setDaemon(true);
     }
-    
-    public ClientContainer(Executor executor)
+
+    public ClientContainer(WebSocketContainerScope scope)
     {
-        endpointClientMetadataCache = new ConcurrentHashMap<>();
-        decoderFactory = new DecoderFactory(PrimitiveDecoderMetadataSet.INSTANCE);
-        encoderFactory = new EncoderFactory(PrimitiveEncoderMetadataSet.INSTANCE);
-
-        EmptyClientEndpointConfig empty = new EmptyClientEndpointConfig();
-        decoderFactory.init(empty);
-        encoderFactory.init(empty);
-
         boolean trustAll = Boolean.getBoolean("org.eclipse.jetty.websocket.jsr356.ssl-trust-all");
-        
-        client = new WebSocketClient(new SslContextFactory(trustAll), executor);
+
+        this.scopeDelegate = scope;
+        client = new WebSocketClient(scope, new SslContextFactory(trustAll));
         client.setEventDriverFactory(new JsrEventDriverFactory(client.getPolicy()));
-        client.setSessionFactory(new JsrSessionFactory(this,this,client));
+        client.setSessionFactory(new JsrSessionFactory(this));
         addBean(client);
 
+        this.endpointClientMetadataCache = new ConcurrentHashMap<>();
+        this.decoderFactory = new DecoderFactory(this,PrimitiveDecoderMetadataSet.INSTANCE);
+        this.encoderFactory = new EncoderFactory(this,PrimitiveEncoderMetadataSet.INSTANCE);
+
         ShutdownThread.register(this);
     }
 
@@ -188,6 +187,17 @@
     }
 
     @Override
+    protected void doStart() throws Exception
+    {
+        super.doStart();
+        
+        // Initialize the default decoder / encoder factories
+        EmptyClientEndpointConfig empty = new EmptyClientEndpointConfig();
+        this.decoderFactory.init(empty);
+        this.encoderFactory.init(empty);
+    }
+
+    @Override
     protected void doStop() throws Exception
     {
         ShutdownThread.deregister(this);
@@ -195,6 +205,12 @@
         super.doStop();
     }
 
+    @Override
+    public ByteBufferPool getBufferPool()
+    {
+        return scopeDelegate.getBufferPool();
+    }
+
     public WebSocketClient getClient()
     {
         return client;
@@ -202,11 +218,9 @@
 
     public EndpointMetadata getClientEndpointMetadata(Class<?> endpoint, EndpointConfig config)
     {
-        EndpointMetadata metadata = null;
-
         synchronized (endpointClientMetadataCache)
         {
-            metadata = endpointClientMetadataCache.get(endpoint);
+            EndpointMetadata metadata = endpointClientMetadataCache.get(endpoint);
 
             if (metadata != null)
             {
@@ -236,7 +250,7 @@
                 err.append(endpoint.getName());
                 err.append("] does not extend @").append(ClientEndpoint.class.getName());
                 err.append(" or extend from ").append(Endpoint.class.getName());
-                throw new InvalidWebSocketException("Unable to identify as valid Endpoint: " + endpoint);
+                throw new InvalidWebSocketException(err.toString());
             }
 
             endpointClientMetadataCache.put(endpoint,metadata);
@@ -279,6 +293,12 @@
     }
 
     @Override
+    public Executor getExecutor()
+    {
+        return scopeDelegate.getExecutor();
+    }
+
+    @Override
     public Set<Extension> getInstalledExtensions()
     {
         Set<Extension> ret = new HashSet<>();
@@ -292,12 +312,31 @@
         return ret;
     }
 
+    @Override
+    public DecoratedObjectFactory getObjectFactory()
+    {
+        return scopeDelegate.getObjectFactory();
+    }
+
     /**
      * Used in {@link Session#getOpenSessions()}
+     * @return the set of open sessions
      */
     public Set<Session> getOpenSessions()
     {
-        return Collections.unmodifiableSet(this.openSessions);
+        return new HashSet<>(getBeans(Session.class));
+    }
+
+    @Override
+    public WebSocketPolicy getPolicy()
+    {
+        return scopeDelegate.getPolicy();
+    }
+
+    @Override
+    public SslContextFactory getSslContextFactory()
+    {
+        return scopeDelegate.getSslContextFactory();
     }
 
     private EndpointInstance newClientEndpointInstance(Class<?> endpointClass, ClientEndpointConfig config)
@@ -335,7 +374,7 @@
     {
         if (session instanceof Session)
         {
-            this.openSessions.remove((Session)session);
+            removeBean(session);
         }
         else
         {
@@ -349,7 +388,7 @@
     {
         if (session instanceof Session)
         {
-            this.openSessions.add((Session)session);
+            addManaged(session);
         }
         else
         {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/DecoderFactory.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/DecoderFactory.java
index 0a65784..e76b2f0 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/DecoderFactory.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/DecoderFactory.java
@@ -19,12 +19,16 @@
 package org.eclipse.jetty.websocket.jsr356;
 
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
+
 import javax.websocket.Decoder;
 import javax.websocket.EndpointConfig;
 
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketSessionScope;
 import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
 import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadataSet;
 
@@ -71,18 +75,26 @@
     private static final Logger LOG = Log.getLogger(DecoderFactory.class);
 
     private final DecoderMetadataSet metadatas;
+    private final WebSocketContainerScope containerScope;
     private DecoderFactory parentFactory;
     private Map<Class<?>, Wrapper> activeWrappers;
 
-    public DecoderFactory(DecoderMetadataSet metadatas)
+    public DecoderFactory(WebSocketContainerScope containerScope, DecoderMetadataSet metadatas)
     {
-        this.metadatas = metadatas;
-        this.activeWrappers = new ConcurrentHashMap<>();
+        this(containerScope,metadatas,null);
+    }
+    
+    public DecoderFactory(WebSocketSessionScope sessionScope, DecoderMetadataSet metadatas, DecoderFactory parentFactory)
+    {
+        this(sessionScope.getContainerScope(),metadatas,parentFactory);
     }
 
-    public DecoderFactory(DecoderMetadataSet metadatas, DecoderFactory parentFactory)
+    protected DecoderFactory(WebSocketContainerScope containerScope, DecoderMetadataSet metadatas, DecoderFactory parentFactory)
     {
-        this(metadatas);
+        Objects.requireNonNull(containerScope,"Container Scope cannot be null");
+        this.containerScope = containerScope;
+        this.metadatas = metadatas;
+        this.activeWrappers = new ConcurrentHashMap<>();
         this.parentFactory = parentFactory;
     }
 
@@ -102,6 +114,7 @@
         {
             LOG.debug("getMetadataFor({})",type);
         }
+        
         DecoderMetadata metadata = metadatas.getMetadataByType(type);
 
         if (metadata != null)
@@ -153,6 +166,12 @@
         {
             LOG.debug("init({})",config);
         }
+        
+        if(!containerScope.isRunning())
+        {
+            throw new RuntimeException(containerScope.getClass().getName() + " is not running yet");
+        }
+        
         // Instantiate all declared decoders
         for (DecoderMetadata metadata : metadatas)
         {
@@ -172,7 +191,7 @@
         Class<? extends Decoder> decoderClass = metadata.getCoderClass();
         try
         {
-            Decoder decoder = decoderClass.newInstance();
+            Decoder decoder = containerScope.getObjectFactory().createInstance(decoderClass);
             return new Wrapper(decoder,metadata);
         }
         catch (InstantiationException | IllegalAccessException e)
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/EncoderFactory.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/EncoderFactory.java
index 7e3707d..bcd021d 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/EncoderFactory.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/EncoderFactory.java
@@ -19,12 +19,16 @@
 package org.eclipse.jetty.websocket.jsr356;
 
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
+
 import javax.websocket.Encoder;
 import javax.websocket.EndpointConfig;
 
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketSessionScope;
 import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadata;
 import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadataSet;
 
@@ -64,18 +68,26 @@
     private static final Logger LOG = Log.getLogger(EncoderFactory.class);
 
     private final EncoderMetadataSet metadatas;
+    private final WebSocketContainerScope containerScope;
     private EncoderFactory parentFactory;
     private Map<Class<?>, Wrapper> activeWrappers;
 
-    public EncoderFactory(EncoderMetadataSet metadatas)
+    public EncoderFactory(WebSocketContainerScope containerScope, EncoderMetadataSet metadatas)
     {
-        this.metadatas = metadatas;
-        this.activeWrappers = new ConcurrentHashMap<>();
+        this(containerScope,metadatas,null);
     }
 
-    public EncoderFactory(EncoderMetadataSet metadatas, EncoderFactory parentFactory)
+    public EncoderFactory(WebSocketSessionScope sessionScope, EncoderMetadataSet metadatas, EncoderFactory parentFactory)
     {
-        this(metadatas);
+        this(sessionScope.getContainerScope(),metadatas,parentFactory);
+    }
+
+    protected EncoderFactory(WebSocketContainerScope containerScope, EncoderMetadataSet metadatas, EncoderFactory parentFactory)
+    {
+        Objects.requireNonNull(containerScope,"Container Scope cannot be null");
+        this.containerScope = containerScope;
+        this.metadatas = metadatas;
+        this.activeWrappers = new ConcurrentHashMap<>();
         this.parentFactory = parentFactory;
     }
 
@@ -166,7 +178,7 @@
         Class<? extends Encoder> encoderClass = metadata.getCoderClass();
         try
         {
-            Encoder encoder = encoderClass.newInstance();
+            Encoder encoder = containerScope.getObjectFactory().createInstance(encoderClass);
             return new Wrapper(encoder,metadata);
         }
         catch (InstantiationException | IllegalAccessException e)
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JettyClientContainerProvider.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JettyClientContainerProvider.java
index 397410f..f65e735 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JettyClientContainerProvider.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JettyClientContainerProvider.java
@@ -22,16 +22,24 @@
 import javax.websocket.WebSocketContainer;
 
 /**
- * Client {@link ContainerProvider} implementation
+ * Client {@link ContainerProvider} implementation.
+ * <p>
+ * Created by a {@link java.util.ServiceLoader} call in the
+ * {@link javax.websocket.ContainerProvider#getWebSocketContainer()} call.
  */
 public class JettyClientContainerProvider extends ContainerProvider
 {
+    /**
+     * Used by {@link ContainerProvider#getWebSocketContainer()} to get a new instance
+     * of the Client {@link WebSocketContainer}.
+     */
     @Override
     protected WebSocketContainer getContainer()
     {
         ClientContainer container = new ClientContainer();
         try
         {
+            // We need to start this container properly.
             container.start();
             return container;
         }
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrExtension.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrExtension.java
index 72f2f28..d5a2efa 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrExtension.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrExtension.java
@@ -58,6 +58,7 @@
 
     /**
      * A configured extension
+     * @param cfg the configuration for the extension 
      */
     public JsrExtension(ExtensionConfig cfg)
     {
@@ -73,6 +74,7 @@
 
     /**
      * A potential (unconfigured) extension
+     * @param name the name of the extension
      */
     public JsrExtension(String name)
     {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSession.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSession.java
index 480d539..bb9d388 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSession.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSession.java
@@ -21,7 +21,6 @@
 import java.io.IOException;
 import java.net.URI;
 import java.security.Principal;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -29,6 +28,8 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
+
 import javax.websocket.CloseReason;
 import javax.websocket.EndpointConfig;
 import javax.websocket.Extension;
@@ -41,9 +42,7 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.BatchMode;
-import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
 import org.eclipse.jetty.websocket.common.LogicalConnection;
-import org.eclipse.jetty.websocket.common.SessionListener;
 import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.events.EventDriver;
 import org.eclipse.jetty.websocket.jsr356.endpoints.AbstractJsrEventDriver;
@@ -73,9 +72,9 @@
     private JsrAsyncRemote asyncRemote;
     private JsrBasicRemote basicRemote;
 
-    public JsrSession(URI requestURI, EventDriver websocket, LogicalConnection connection, ClientContainer container, String id, SessionListener... sessionListeners)
+    public JsrSession(ClientContainer container, String id, URI requestURI, EventDriver websocket, LogicalConnection connection)
     {
-        super(requestURI, websocket, connection, sessionListeners);
+        super(container, requestURI, websocket, connection);
         if (!(websocket instanceof AbstractJsrEventDriver))
         {
             throw new IllegalArgumentException("Cannot use, not a JSR WebSocket: " + websocket);
@@ -85,8 +84,8 @@
         this.metadata = jsr.getMetadata();
         this.container = container;
         this.id = id;
-        this.decoderFactory = new DecoderFactory(metadata.getDecoders(),container.getDecoderFactory());
-        this.encoderFactory = new EncoderFactory(metadata.getEncoders(),container.getEncoderFactory());
+        this.decoderFactory = new DecoderFactory(this,metadata.getDecoders(),container.getDecoderFactory());
+        this.encoderFactory = new EncoderFactory(this,metadata.getEncoders(),container.getEncoderFactory());
         this.messageHandlerFactory = new MessageHandlerFactory();
         this.wrappers = new MessageHandlerWrapper[MessageType.values().length];
         this.messageHandlerSet = new HashSet<>();
@@ -241,13 +240,9 @@
     @Override
     public List<Extension> getNegotiatedExtensions()
     {
-        if (negotiatedExtensions == null)
+        if ((negotiatedExtensions == null) && getUpgradeResponse().getExtensions() != null)
         {
-            negotiatedExtensions = new ArrayList<Extension>();
-            for (ExtensionConfig cfg : getUpgradeResponse().getExtensions())
-            {
-                negotiatedExtensions.add(new JsrExtension(cfg));
-            }
+            negotiatedExtensions = getUpgradeResponse().getExtensions().stream().map(JsrExtension::new).collect(Collectors.toList());
         }
         return negotiatedExtensions;
     }
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSessionFactory.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSessionFactory.java
index cbd0276..d16f619 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSessionFactory.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSessionFactory.java
@@ -19,36 +19,32 @@
 package org.eclipse.jetty.websocket.jsr356;
 
 import java.net.URI;
-import java.util.concurrent.atomic.AtomicLong;
 
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.common.LogicalConnection;
 import org.eclipse.jetty.websocket.common.SessionFactory;
-import org.eclipse.jetty.websocket.common.SessionListener;
 import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.events.EventDriver;
 import org.eclipse.jetty.websocket.jsr356.endpoints.AbstractJsrEventDriver;
 
 public class JsrSessionFactory implements SessionFactory
 {
-    private AtomicLong idgen = new AtomicLong(0);
+    private static final Logger LOG = Log.getLogger(JsrSessionFactory.class);
     private final ClientContainer container;
-    private final SessionListener[] listeners;
 
-    public JsrSessionFactory(ClientContainer container, SessionListener... sessionListeners)
+    public JsrSessionFactory(ClientContainer container)
     {
+        if(LOG.isDebugEnabled()) {
+            LOG.debug("Container: {}", container);
+        }
         this.container = container;
-        this.listeners = sessionListeners;
     }
 
     @Override
     public WebSocketSession createSession(URI requestURI, EventDriver websocket, LogicalConnection connection)
     {
-        return new JsrSession(requestURI,websocket,connection,container,getNextId(),listeners);
-    }
-
-    public String getNextId()
-    {
-        return String.format("websocket-%d",idgen.incrementAndGet());
+        return new JsrSession(container,connection.getId(),requestURI,websocket,connection);
     }
 
     @Override
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrUpgradeListener.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrUpgradeListener.java
index cd90547..9c9e970 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrUpgradeListener.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrUpgradeListener.java
@@ -21,6 +21,7 @@
 import java.net.HttpCookie;
 import java.util.List;
 import java.util.Map;
+
 import javax.websocket.ClientEndpointConfig.Configurator;
 
 import org.eclipse.jetty.websocket.api.UpgradeRequest;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/MessageHandlerFactory.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/MessageHandlerFactory.java
index 3560a9c..a389f30 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/MessageHandlerFactory.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/MessageHandlerFactory.java
@@ -22,6 +22,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+
 import javax.websocket.MessageHandler;
 
 import org.eclipse.jetty.util.log.Log;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/MessageHandlerWrapper.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/MessageHandlerWrapper.java
index 69ce562..b7532a2 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/MessageHandlerWrapper.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/MessageHandlerWrapper.java
@@ -19,6 +19,8 @@
 package org.eclipse.jetty.websocket.jsr356;
 
 import javax.websocket.MessageHandler;
+import javax.websocket.MessageHandler.Partial;
+import javax.websocket.MessageHandler.Whole;
 
 import org.eclipse.jetty.websocket.jsr356.metadata.MessageHandlerMetadata;
 
@@ -71,7 +73,7 @@
     }
 
     /**
-     * Flag for a onMessage() method that wants MessageHandler.{@link Whole} with a Decoder that is based on {@link TextStream} or {@link BinaryStream}
+     * Flag for a onMessage() method that wants MessageHandler.{@link Whole} with a Decoder that is based on {@link javax.websocket.Decoder.TextStream} or {@link javax.websocket.Decoder.BinaryStream}
      * 
      * @return true for Streaming based Decoder, false for normal decoder for whole messages.
      */
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/AnnotatedEndpointMetadata.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/AnnotatedEndpointMetadata.java
index 20b9443..dbd241a 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/AnnotatedEndpointMetadata.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/AnnotatedEndpointMetadata.java
@@ -22,6 +22,10 @@
 import java.util.LinkedList;
 
 import javax.websocket.EndpointConfig;
+import javax.websocket.OnClose;
+import javax.websocket.OnError;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
 
 import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
 import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadataSet;
@@ -33,6 +37,8 @@
  * 
  * @param <T>
  *            the annotation this metadata is based off of
+ * @param <C>
+ *            the endpoint configuration this is based off of
  */
 public abstract class AnnotatedEndpointMetadata<T extends Annotation, C extends EndpointConfig> implements EndpointMetadata
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/AnnotatedEndpointScanner.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/AnnotatedEndpointScanner.java
index c9b539e..6977a3e 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/AnnotatedEndpointScanner.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/AnnotatedEndpointScanner.java
@@ -22,6 +22,7 @@
 import java.lang.reflect.Method;
 import java.util.LinkedList;
 import java.util.List;
+
 import javax.websocket.EndpointConfig;
 import javax.websocket.OnClose;
 import javax.websocket.OnError;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/IJsrMethod.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/IJsrMethod.java
index d23f705..6cdf617 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/IJsrMethod.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/IJsrMethod.java
@@ -21,6 +21,7 @@
 import java.lang.reflect.Method;
 
 import javax.websocket.Decoder;
+import javax.websocket.OnMessage;
 
 import org.eclipse.jetty.websocket.jsr356.MessageType;
 
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrCallable.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrCallable.java
index 4eaae52..689d406 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrCallable.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrCallable.java
@@ -57,6 +57,7 @@
 
     /**
      * Copy Constructor
+     * @param copy the JsrCallable to copy from 
      */
     public JsrCallable(JsrCallable copy)
     {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrEvents.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrEvents.java
index f4ff337..44b6b83 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrEvents.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrEvents.java
@@ -24,9 +24,14 @@
 import java.lang.annotation.Annotation;
 import java.nio.ByteBuffer;
 import java.util.Map;
+
 import javax.websocket.CloseReason;
 import javax.websocket.DecodeException;
 import javax.websocket.EndpointConfig;
+import javax.websocket.OnClose;
+import javax.websocket.OnError;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
 import javax.websocket.RemoteEndpoint;
 
 import org.eclipse.jetty.util.log.Log;
@@ -35,6 +40,8 @@
 
 /**
  * The live event methods found for a specific Annotated Endpoint
+ * @param <T> the annotation type
+ * @param <C> the endpoint config type
  */
 public class JsrEvents<T extends Annotation, C extends EndpointConfig>
 {
@@ -148,6 +155,7 @@
     {
         if (onError == null)
         {
+            LOG.warn("Unable to report throwable to websocket (no @OnError handler declared): " + websocket.getClass().getName(), cause);
             return;
         }
         onError.call(websocket,cause);
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdBinary.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdBinary.java
index b999236..7577200 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdBinary.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdBinary.java
@@ -21,6 +21,8 @@
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 
+import javax.websocket.OnMessage;
+
 import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
 import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
 
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdDecoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdDecoder.java
index 3611635..479db46 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdDecoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdDecoder.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.jsr356.annotations;
 
+import javax.websocket.OnMessage;
+
 import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
 import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
 import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdOnClose.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdOnClose.java
index df5a8de..2bc24b2 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdOnClose.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdOnClose.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.websocket.jsr356.annotations;
 
 import javax.websocket.CloseReason;
+import javax.websocket.OnClose;
 
 import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
 import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdOnError.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdOnError.java
index 8119d95..5da5ad2 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdOnError.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdOnError.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.jsr356.annotations;
 
+import javax.websocket.OnError;
+
 import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
 import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
 
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdOnOpen.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdOnOpen.java
index 1bdf4a8..eb74a8c 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdOnOpen.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdOnOpen.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.websocket.jsr356.annotations;
 
 import javax.websocket.EndpointConfig;
+import javax.websocket.OnOpen;
 
 import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
 import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdText.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdText.java
index 907a3ce..6b4fdab 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdText.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrParamIdText.java
@@ -20,6 +20,8 @@
 
 import java.io.Reader;
 
+import javax.websocket.OnMessage;
+
 import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
 import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
 
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnCloseCallable.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnCloseCallable.java
index 3fb4d59..53b7b48 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnCloseCallable.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnCloseCallable.java
@@ -22,6 +22,7 @@
 
 import javax.websocket.CloseReason;
 import javax.websocket.CloseReason.CloseCodes;
+import javax.websocket.OnClose;
 
 import org.eclipse.jetty.websocket.common.CloseInfo;
 import org.eclipse.jetty.websocket.jsr356.JsrSession;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnErrorCallable.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnErrorCallable.java
index 79e77dc..e654deb 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnErrorCallable.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnErrorCallable.java
@@ -21,6 +21,7 @@
 import java.lang.reflect.Method;
 
 import javax.websocket.Decoder;
+import javax.websocket.OnError;
 
 import org.eclipse.jetty.websocket.jsr356.JsrSession;
 
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageBinaryCallable.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageBinaryCallable.java
index 19dbc8d..bad2daf 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageBinaryCallable.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageBinaryCallable.java
@@ -23,6 +23,7 @@
 
 import javax.websocket.DecodeException;
 import javax.websocket.Decoder;
+import javax.websocket.OnMessage;
 
 import org.eclipse.jetty.websocket.jsr356.JsrSession;
 import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
@@ -45,6 +46,7 @@
 
     /**
      * Copy Constructor
+     * @param copy the callable to copy
      */
     public OnMessageBinaryCallable(OnMessageCallable copy)
     {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageBinaryStreamCallable.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageBinaryStreamCallable.java
index b830153..a2ec817 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageBinaryStreamCallable.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageBinaryStreamCallable.java
@@ -25,6 +25,7 @@
 
 import javax.websocket.DecodeException;
 import javax.websocket.Decoder;
+import javax.websocket.OnMessage;
 
 import org.eclipse.jetty.websocket.jsr356.JsrSession;
 import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
@@ -32,7 +33,7 @@
 /**
  * Callable for {@link OnMessage} annotated methods for {@link InputStream} based binary message objects
  * 
- * @see BinaryStream
+ * @see javax.websocket.Decoder.BinaryStream
  */
 public class OnMessageBinaryStreamCallable extends OnMessageCallable
 {
@@ -45,6 +46,7 @@
 
     /**
      * Copy Constructor
+     * @param copy the callable to copy from
      */
     public OnMessageBinaryStreamCallable(OnMessageCallable copy)
     {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessagePongCallable.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessagePongCallable.java
index 75b8352..af65f75 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessagePongCallable.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessagePongCallable.java
@@ -21,6 +21,9 @@
 import java.lang.reflect.Method;
 import java.nio.ByteBuffer;
 
+import javax.websocket.OnMessage;
+import javax.websocket.PongMessage;
+
 import org.eclipse.jetty.websocket.jsr356.JsrPongMessage;
 import org.eclipse.jetty.websocket.jsr356.JsrSession;
 import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
@@ -37,6 +40,7 @@
 
     /**
      * Copy Constructor
+     * @param copy the callable to copy from
      */
     public OnMessagePongCallable(OnMessageCallable copy)
     {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageTextCallable.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageTextCallable.java
index 0b838c7..a008bbe 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageTextCallable.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageTextCallable.java
@@ -18,10 +18,12 @@
 
 package org.eclipse.jetty.websocket.jsr356.annotations;
 
+import java.io.Reader;
 import java.lang.reflect.Method;
 
 import javax.websocket.DecodeException;
 import javax.websocket.Decoder;
+import javax.websocket.OnMessage;
 
 import org.eclipse.jetty.websocket.jsr356.JsrSession;
 import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
@@ -44,6 +46,7 @@
 
     /**
      * Copy Constructor
+     * @param copy the callable to copy from
      */
     public OnMessageTextCallable(OnMessageCallable copy)
     {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageTextStreamCallable.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageTextStreamCallable.java
index 608c816..58abc90 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageTextStreamCallable.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnMessageTextStreamCallable.java
@@ -24,6 +24,7 @@
 
 import javax.websocket.DecodeException;
 import javax.websocket.Decoder;
+import javax.websocket.OnMessage;
 
 import org.eclipse.jetty.websocket.jsr356.JsrSession;
 import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
@@ -31,7 +32,7 @@
 /**
  * Callable for {@link OnMessage} annotated methods for {@link Reader} based text message objects
  * 
- * @see TextStream
+ * @see javax.websocket.Decoder.TextStream
  */
 public class OnMessageTextStreamCallable extends OnMessageCallable
 {
@@ -44,6 +45,7 @@
 
     /**
      * Copy Constructor
+     * @param copy the callable to copy from
      */
     public OnMessageTextStreamCallable(OnMessageCallable copy)
     {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnOpenCallable.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnOpenCallable.java
index c895902..c0c7d74 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnOpenCallable.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/OnOpenCallable.java
@@ -21,6 +21,7 @@
 import java.lang.reflect.Method;
 
 import javax.websocket.EndpointConfig;
+import javax.websocket.OnOpen;
 
 import org.eclipse.jetty.websocket.jsr356.JsrSession;
 import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/BooleanDecoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/BooleanDecoder.java
index fe2ec87..a8ab7a3 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/BooleanDecoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/BooleanDecoder.java
@@ -23,7 +23,7 @@
 
 
 /**
- * Default implementation of the {@link Text} Message to {@link Boolean} decoder.
+ * Default implementation of the {@link javax.websocket.Decoder.Text} Message to {@link Boolean} decoder.
  * <p>
  * Note: delegates to {@link Boolean#parseBoolean(String)} and will only support "true" and "false" as boolean values.
  */
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/ByteDecoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/ByteDecoder.java
index 01083b2..95e6ada 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/ByteDecoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/ByteDecoder.java
@@ -23,7 +23,7 @@
 
 
 /**
- * Default implementation of the {@link Text} Message to {@link Byte} decoder
+ * Default implementation of the {@link javax.websocket.Decoder.Text} Message to {@link Byte} decoder
  */
 public class ByteDecoder extends AbstractDecoder implements Decoder.Text<Byte>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/CharacterDecoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/CharacterDecoder.java
index c9f4e60..790b3d5 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/CharacterDecoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/CharacterDecoder.java
@@ -23,7 +23,7 @@
 
 
 /**
- * Default implementation of the {@link Text} Message to {@link Character} decoder
+ * Default implementation of the {@link javax.websocket.Decoder.Text} Message to {@link Character} decoder
  */
 public class CharacterDecoder extends AbstractDecoder implements Decoder.Text<Character>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/DoubleDecoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/DoubleDecoder.java
index 7bdffe8..51ad297 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/DoubleDecoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/DoubleDecoder.java
@@ -23,7 +23,7 @@
 
 
 /**
- * Default implementation of the {@link Text} Message to {@link Double} to decoder
+ * Default implementation of the {@link javax.websocket.Decoder.Text} Message to {@link Double} to decoder
  */
 public class DoubleDecoder extends AbstractDecoder implements Decoder.Text<Double>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/IntegerDecoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/IntegerDecoder.java
index 59533a7..fe50c50 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/IntegerDecoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/IntegerDecoder.java
@@ -23,7 +23,7 @@
 
 
 /**
- * Default implementation of the {@link Text} Message to {@link Integer} decoder
+ * Default implementation of the {@link javax.websocket.Decoder.Text} Message to {@link Integer} decoder
  */
 public class IntegerDecoder extends AbstractDecoder implements Decoder.Text<Integer>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/ShortDecoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/ShortDecoder.java
index 469eca0..1c64841 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/ShortDecoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/ShortDecoder.java
@@ -23,7 +23,7 @@
 
 
 /**
- * Default implementation of the {@link Text} Message to {@link Short} decoder
+ * Default implementation of the {@link javax.websocket.Decoder.Text} Message to {@link Short} decoder
  */
 public class ShortDecoder extends AbstractDecoder implements Decoder.Text<Short>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/StringDecoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/StringDecoder.java
index d376e63..39db75f 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/StringDecoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/decoders/StringDecoder.java
@@ -23,7 +23,7 @@
 
 
 /**
- * Default implementation of the {@link Text} Message to {@link String} decoder
+ * Default implementation of the {@link javax.websocket.Decoder.Text} Message to {@link String} decoder
  */
 public class StringDecoder extends AbstractDecoder implements Decoder.Text<String>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/BooleanEncoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/BooleanEncoder.java
index ce5c63c..0847d35 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/BooleanEncoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/BooleanEncoder.java
@@ -22,7 +22,7 @@
 import javax.websocket.Encoder;
 
 /**
- * Default encoder for {@link Boolean} to {@link Text} Message encoder
+ * Default encoder for {@link Boolean} to {@link javax.websocket.Encoder.Text} Message encoder
  */
 public class BooleanEncoder extends AbstractEncoder implements Encoder.Text<Boolean>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/ByteEncoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/ByteEncoder.java
index 51a21b6..2bded61 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/ByteEncoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/ByteEncoder.java
@@ -22,7 +22,7 @@
 import javax.websocket.Encoder;
 
 /**
- * Default encoder for {@link Byte} to {@link Text} Message encoder
+ * Default encoder for {@link Byte} to {@link javax.websocket.Encoder.Text} Message encoder
  */
 public class ByteEncoder extends AbstractEncoder implements Encoder.Text<Byte>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/CharacterEncoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/CharacterEncoder.java
index b021f0e..c39dc12 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/CharacterEncoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/CharacterEncoder.java
@@ -22,7 +22,7 @@
 import javax.websocket.Encoder;
 
 /**
- * Default encoder for {@link Character} to {@link Text} Message encoder
+ * Default encoder for {@link Character} to {@link javax.websocket.Encoder.Text} Message encoder
  */
 public class CharacterEncoder extends AbstractEncoder implements Encoder.Text<Character>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/DoubleEncoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/DoubleEncoder.java
index d0684b8..5aaa411 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/DoubleEncoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/DoubleEncoder.java
@@ -22,7 +22,7 @@
 import javax.websocket.Encoder;
 
 /**
- * Default encoder for {@link Double} to {@link Text} Message encoder
+ * Default encoder for {@link Double} to {@link javax.websocket.Encoder.Text} Message encoder
  */
 public class DoubleEncoder extends AbstractEncoder implements Encoder.Text<Double>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/FloatEncoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/FloatEncoder.java
index 1df8844..1abad5d 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/FloatEncoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/FloatEncoder.java
@@ -21,7 +21,7 @@
 import javax.websocket.EncodeException;
 import javax.websocket.Encoder;
 /**
- * Default encoder for {@link Float} to {@link Text} Message encoder
+ * Default encoder for {@link Float} to {@link javax.websocket.Encoder.Text} Message encoder
  */
 public class FloatEncoder extends AbstractEncoder implements Encoder.Text<Float>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/IntegerEncoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/IntegerEncoder.java
index b9e1552..be976c4 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/IntegerEncoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/IntegerEncoder.java
@@ -22,7 +22,7 @@
 import javax.websocket.Encoder;
 
 /**
- * Default encoder for {@link Integer} to {@link Text} Message encoder
+ * Default encoder for {@link Integer} to {@link javax.websocket.Encoder.Text} Message encoder
  */
 public class IntegerEncoder extends AbstractEncoder implements Encoder.Text<Integer>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/LongEncoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/LongEncoder.java
index 56e13fc..58a3c36 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/LongEncoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/LongEncoder.java
@@ -22,7 +22,7 @@
 import javax.websocket.Encoder;
 
 /**
- * Default encoder for {@link Long} to {@link Text} Message encoder
+ * Default encoder for {@link Long} to {@link javax.websocket.Encoder.Text} Message encoder
  */
 public class LongEncoder extends AbstractEncoder implements Encoder.Text<Long>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/ShortEncoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/ShortEncoder.java
index b2aee06..7ca62be 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/ShortEncoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/ShortEncoder.java
@@ -22,7 +22,7 @@
 import javax.websocket.Encoder;
 
 /**
- * Default encoder for {@link Short} to {@link Text} Message encoder
+ * Default encoder for {@link Short} to {@link javax.websocket.Encoder.Text} Message encoder
  */
 public class ShortEncoder extends AbstractEncoder implements Encoder.Text<Short>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/StringEncoder.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/StringEncoder.java
index 77c42ae..148897c 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/StringEncoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/encoders/StringEncoder.java
@@ -22,7 +22,7 @@
 import javax.websocket.Encoder;
 
 /**
- * Default encoder for {@link String} to {@link Text} Message encoder
+ * Default encoder for {@link String} to {@link javax.websocket.Encoder.Text} Message encoder
  */
 public class StringEncoder extends AbstractEncoder implements Encoder.Text<String>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java
index 8c40990..906bee8 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java
@@ -26,6 +26,7 @@
 
 import javax.websocket.CloseReason;
 import javax.websocket.DecodeException;
+import javax.websocket.MessageHandler.Whole;
 
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.log.Log;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/messages/BinaryPartialOnMessage.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/messages/BinaryPartialOnMessage.java
index 67e4f13..4206548 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/messages/BinaryPartialOnMessage.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/messages/BinaryPartialOnMessage.java
@@ -21,6 +21,8 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 
+import javax.websocket.OnMessage;
+
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.websocket.common.message.MessageAppender;
 import org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/messages/TextPartialMessage.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/messages/TextPartialMessage.java
index 6a4fd5e..4eb3f91 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/messages/TextPartialMessage.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/messages/TextPartialMessage.java
@@ -24,8 +24,8 @@
 import javax.websocket.MessageHandler;
 import javax.websocket.MessageHandler.Partial;
 
-import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.websocket.common.message.MessageAppender;
+import org.eclipse.jetty.websocket.common.util.Utf8PartialBuilder;
 import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
 
 /**
@@ -36,24 +36,23 @@
     @SuppressWarnings("unused")
     private final MessageHandlerWrapper msgWrapper;
     private final MessageHandler.Partial<String> partialHandler;
+    private final Utf8PartialBuilder utf8Partial;
 
     @SuppressWarnings("unchecked")
     public TextPartialMessage(MessageHandlerWrapper wrapper)
     {
         this.msgWrapper = wrapper;
         this.partialHandler = (Partial<String>)wrapper.getHandler();
+        this.utf8Partial = new Utf8PartialBuilder();
     }
 
     @Override
     public void appendFrame(ByteBuffer payload, boolean isLast) throws IOException
     {
-        String msg = null;
+        String partialText = utf8Partial.toPartialString(payload);
 
         // No decoders for Partial messages per JSR-356 (PFD1 spec)
-        if (payload != null)
-            msg = BufferUtil.toUTF8String(payload.slice());
-
-        partialHandler.onMessage(msg, isLast);
+        partialHandler.onMessage(partialText,isLast);
     }
 
     @Override
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/messages/TextPartialOnMessage.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/messages/TextPartialOnMessage.java
index b8c4f1b..d5f01ff 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/messages/TextPartialOnMessage.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/messages/TextPartialOnMessage.java
@@ -21,8 +21,10 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 
-import org.eclipse.jetty.util.BufferUtil;
+import javax.websocket.OnMessage;
+
 import org.eclipse.jetty.websocket.common.message.MessageAppender;
+import org.eclipse.jetty.websocket.common.util.Utf8PartialBuilder;
 import org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver;
 
 /**
@@ -31,12 +33,14 @@
 public class TextPartialOnMessage implements MessageAppender
 {
     private final JsrAnnotatedEventDriver driver;
+    private final Utf8PartialBuilder utf8Partial;
     private boolean finished;
 
     public TextPartialOnMessage(JsrAnnotatedEventDriver driver)
     {
         this.driver = driver;
         this.finished = false;
+        this.utf8Partial = new Utf8PartialBuilder();
     }
 
     @Override
@@ -46,15 +50,9 @@
         {
             throw new IOException("Cannot append to finished buffer");
         }
-        if (payload == null)
-        {
-            driver.onPartialTextMessage("",isLast);
-        }
-        else
-        {
-            String text = BufferUtil.toUTF8String(payload);
-            driver.onPartialTextMessage(text,isLast);
-        }
+        
+        String text = utf8Partial.toPartialString(payload);
+        driver.onPartialTextMessage(text,isLast);
     }
 
     @Override
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/metadata/CoderMetadata.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/metadata/CoderMetadata.java
index 513a30c..84146d8 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/metadata/CoderMetadata.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/metadata/CoderMetadata.java
@@ -21,10 +21,10 @@
 import org.eclipse.jetty.websocket.jsr356.MessageType;
 
 /**
- * The immutable base metadata for a coder ({@link Decoder} or {@link Encoder}
+ * The immutable base metadata for a coder ({@link javax.websocket.Decoder} or {@link javax.websocket.Encoder}
  * 
  * @param <T>
- *            the specific type of coder ({@link Decoder} or {@link Encoder}
+ *            the specific type of coder ({@link javax.websocket.Decoder} or {@link javax.websocket.Encoder}
  */
 public abstract class CoderMetadata<T>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/metadata/CoderMetadataSet.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/metadata/CoderMetadataSet.java
index 5b5061d..c72bf77 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/metadata/CoderMetadataSet.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/metadata/CoderMetadataSet.java
@@ -24,13 +24,15 @@
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
+
 /**
  * An durable collection of {@link CoderMetadata}.
  * <p>
  * This is a write-only collection, and cannot be modified once initialized.
  * 
  * @param <T>
- *            The type of coder ({@link Decoder} or {@link Encoder}
+ *            The type of coder ({@link javax.websocket.Decoder} or {@link javax.websocket.Encoder}
  * @param <M>
  *            The metadata for the coder
  */
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/metadata/DuplicateCoderException.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/metadata/DuplicateCoderException.java
index 064944a..df19802 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/metadata/DuplicateCoderException.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/metadata/DuplicateCoderException.java
@@ -21,7 +21,7 @@
 import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
 
 /**
- * Thrown when a duplicate coder is encountered when attempting to identify a Endpoint's metadata ( {@link Decoder} or {@link Encoder})
+ * Thrown when a duplicate coder is encountered when attempting to identify a Endpoint's metadata ({@link javax.websocket.Decoder} or {@link javax.websocket.Encoder})
  */
 public class DuplicateCoderException extends InvalidWebSocketException
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/AnnotatedEndpointConfigTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/AnnotatedEndpointConfigTest.java
index 16e8949..91b7c4d 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/AnnotatedEndpointConfigTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/AnnotatedEndpointConfigTest.java
@@ -18,7 +18,10 @@
 
 package org.eclipse.jetty.websocket.jsr356;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
 
 import java.net.URI;
 import java.util.List;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/ConfiguratorTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/ConfiguratorTest.java
index 6cbc3ce..cff8659 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/ConfiguratorTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/ConfiguratorTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.jsr356;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.notNullValue;
 
 import java.net.URI;
 import java.util.List;
@@ -40,7 +40,7 @@
 import org.junit.Test;
 
 /**
- * Tests of {@link Configurator}
+ * Tests of {@link javax.websocket.ClientEndpointConfig.Configurator}
  */
 public class ConfiguratorTest
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/CookiesTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/CookiesTest.java
index 8bba54f..c757177 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/CookiesTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/CookiesTest.java
@@ -23,6 +23,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+
 import javax.websocket.ClientEndpointConfig;
 import javax.websocket.ContainerProvider;
 import javax.websocket.Endpoint;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderFactoryTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderFactoryTest.java
index 5f98249..f0f2657 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderFactoryTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderFactoryTest.java
@@ -25,6 +25,9 @@
 
 import javax.websocket.Decoder;
 
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.jsr356.decoders.ByteArrayDecoder;
 import org.eclipse.jetty.websocket.jsr356.decoders.ByteBufferDecoder;
 import org.eclipse.jetty.websocket.jsr356.decoders.DateDecoder;
@@ -56,9 +59,11 @@
     @Before
     public void initDecoderFactory()
     {
-        DecoderFactory primitivesFactory = new DecoderFactory(PrimitiveDecoderMetadataSet.INSTANCE);
+        WebSocketContainerScope containerScope = new SimpleContainerScope(WebSocketPolicy.newClientPolicy());
+        
+        DecoderFactory primitivesFactory = new DecoderFactory(containerScope,PrimitiveDecoderMetadataSet.INSTANCE);
         metadatas = new DecoderMetadataSet();
-        factory = new DecoderFactory(metadatas,primitivesFactory);
+        factory = new DecoderFactory(containerScope,metadatas,primitivesFactory);
     }
 
     @Test
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderManySmallTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderManySmallTest.java
index 84c09ef..c867b9a 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderManySmallTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderManySmallTest.java
@@ -42,13 +42,15 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
+@Ignore("Not working atm")
 public class DecoderReaderManySmallTest
 {
     public static class EventId
@@ -111,7 +113,7 @@
     private static class EventIdServer implements Runnable
     {
         private BlockheadServer server;
-        private ServerConnection sconnection;
+        private IBlockheadServerConnection sconnection;
         private CountDownLatch connectLatch = new CountDownLatch(1);
 
         public EventIdServer(BlockheadServer server)
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderTest.java
index 33dbfaa..e5f137b 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderTest.java
@@ -50,7 +50,7 @@
 import org.eclipse.jetty.websocket.common.frames.ContinuationFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -135,13 +135,14 @@
         }
 
         @OnMessage
-        public void onMessage(Quotes msg)
+        public synchronized void onMessage(Quotes msg)
         {
+            Integer h=hashCode();
             messageQueue.add(msg);
-            System.out.printf("Quotes from: %s%n",msg.author);
+            System.out.printf("%x: Quotes from: %s%n",h,msg.author);
             for (String quote : msg.quotes)
             {
-                System.out.printf(" - %s%n",quote);
+                System.out.printf("%x: - %s%n",h,quote);
             }
         }
 
@@ -154,7 +155,7 @@
     private static class QuoteServer implements Runnable
     {
         private BlockheadServer server;
-        private ServerConnection sconnection;
+        private IBlockheadServerConnection sconnection;
         private CountDownLatch connectLatch = new CountDownLatch(1);
 
         public QuoteServer(BlockheadServer server)
@@ -271,8 +272,8 @@
     }
 
     // TODO analyse and fix 
-    @Ignore
     @Test
+    @Ignore ("Quotes appear to be able to arrive in any order?")
     public void testTwoQuotes() throws Exception
     {
         QuotesSocket quoter = new QuotesSocket();
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EchoHandler.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EchoHandler.java
index f885f22..5d19ddd 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EchoHandler.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EchoHandler.java
@@ -31,6 +31,7 @@
     @Override
     public void configure(WebSocketServletFactory factory)
     {
+        factory.getPolicy().setMaxTextMessageSize(2 * 1024 * 1024);
         factory.setCreator(this);
     }
 
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderFactoryTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderFactoryTest.java
index 86ced9a..cf0ccc5 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderFactoryTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderFactoryTest.java
@@ -22,6 +22,9 @@
 
 import javax.websocket.Encoder;
 
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.jsr356.encoders.IntegerEncoder;
 import org.eclipse.jetty.websocket.jsr356.encoders.LongEncoder;
 import org.eclipse.jetty.websocket.jsr356.encoders.PrimitiveEncoderMetadataSet;
@@ -53,9 +56,11 @@
     @Before
     public void initEncoderFactory()
     {
-        EncoderFactory primitivesFactory = new EncoderFactory(PrimitiveEncoderMetadataSet.INSTANCE);
+        WebSocketContainerScope containerScope = new SimpleContainerScope(WebSocketPolicy.newClientPolicy());
+        
+        EncoderFactory primitivesFactory = new EncoderFactory(containerScope,PrimitiveEncoderMetadataSet.INSTANCE);
         metadatas = new EncoderMetadataSet();
-        factory = new EncoderFactory(metadatas,primitivesFactory);
+        factory = new EncoderFactory(containerScope,metadatas,primitivesFactory);
     }
 
     @Test
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderTest.java
index 17f3ba1..7fb33f0 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderTest.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.jsr356;
 
+import static org.hamcrest.Matchers.containsString;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
@@ -26,6 +28,7 @@
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+
 import javax.websocket.ClientEndpointConfig;
 import javax.websocket.ContainerProvider;
 import javax.websocket.EncodeException;
@@ -42,22 +45,20 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.containsString;
-
 public class EncoderTest
 {
     private static class EchoServer implements Runnable
     {
         private Thread thread;
         private BlockheadServer server;
-        private ServerConnection sconnection;
+        private IBlockheadServerConnection sconnection;
         private CountDownLatch connectLatch = new CountDownLatch(1);
 
         public EchoServer(BlockheadServer server)
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EndpointEchoClient.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EndpointEchoClient.java
index 35c28cf..bdb4a4e 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EndpointEchoClient.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EndpointEchoClient.java
@@ -18,7 +18,10 @@
 
 package org.eclipse.jetty.websocket.jsr356;
 
+import static org.hamcrest.Matchers.notNullValue;
+
 import java.io.IOException;
+
 import javax.websocket.CloseReason;
 import javax.websocket.Endpoint;
 import javax.websocket.EndpointConfig;
@@ -28,8 +31,6 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.junit.Assert;
 
-import static org.hamcrest.Matchers.notNullValue;
-
 /**
  * Basic Echo Client from extended Endpoint
  */
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EndpointEchoTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EndpointEchoTest.java
index 4200478..c484a07 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EndpointEchoTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EndpointEchoTest.java
@@ -18,8 +18,11 @@
 
 package org.eclipse.jetty.websocket.jsr356;
 
+import static org.hamcrest.Matchers.instanceOf;
+
 import java.net.URI;
 import java.util.concurrent.TimeUnit;
+
 import javax.websocket.ContainerProvider;
 import javax.websocket.Session;
 import javax.websocket.WebSocketContainer;
@@ -35,8 +38,6 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.instanceOf;
-
 public class EndpointEchoTest
 {
     private static final Logger LOG = Log.getLogger(EndpointEchoTest.class);
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JettyEchoSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JettyEchoSocket.java
index 6a1c2a0..480a355 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JettyEchoSocket.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JettyEchoSocket.java
@@ -38,6 +38,9 @@
     @Override
     public void onWebSocketBinary(byte[] payload, int offset, int len)
     {
+        if (isNotConnected())
+            return;
+
         try
         {
             RemoteEndpoint remote = getRemote();
@@ -60,6 +63,9 @@
     @Override
     public void onWebSocketText(String message)
     {
+        if (isNotConnected())
+            return;
+
         try
         {
             RemoteEndpoint remote = getRemote();
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JsrSessionTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JsrSessionTest.java
index 28066f9..6e3e236 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JsrSessionTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JsrSessionTest.java
@@ -18,14 +18,18 @@
 
 package org.eclipse.jetty.websocket.jsr356;
 
+import static org.hamcrest.Matchers.*;
+
 import java.net.URI;
 import java.nio.ByteBuffer;
+
 import javax.websocket.ClientEndpointConfig;
 import javax.websocket.DeploymentException;
 import javax.websocket.MessageHandler;
 
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.common.events.EventDriver;
+import org.eclipse.jetty.websocket.common.test.DummyConnection;
 import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig;
 import org.eclipse.jetty.websocket.jsr356.client.SimpleEndpointMetadata;
 import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
@@ -34,14 +38,11 @@
 import org.eclipse.jetty.websocket.jsr356.handlers.ByteBufferPartialHandler;
 import org.eclipse.jetty.websocket.jsr356.handlers.LongMessageHandler;
 import org.eclipse.jetty.websocket.jsr356.handlers.StringWholeHandler;
-import org.eclipse.jetty.websocket.jsr356.samples.DummyConnection;
 import org.eclipse.jetty.websocket.jsr356.samples.DummyEndpoint;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.instanceOf;
-
 public class JsrSessionTest
 {
     private ClientContainer container;
@@ -60,10 +61,10 @@
         // Executor executor = null;
 
         EndpointInstance ei = new EndpointInstance(websocket,config,metadata);
-
+        
         EventDriver driver = new JsrEndpointEventDriver(policy,ei);
         DummyConnection connection = new DummyConnection();
-        session = new JsrSession(requestURI,driver,connection,container,id);
+        session = new JsrSession(container,id,requestURI,driver,connection);
     }
 
     @Test
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/MessageHandlerFactoryTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/MessageHandlerFactoryTest.java
index 3cd5b72..7fef389 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/MessageHandlerFactoryTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/MessageHandlerFactoryTest.java
@@ -25,6 +25,9 @@
 
 import javax.websocket.DeploymentException;
 
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.jsr356.decoders.PrimitiveDecoderMetadataSet;
 import org.eclipse.jetty.websocket.jsr356.handlers.ByteArrayPartialHandler;
 import org.eclipse.jetty.websocket.jsr356.handlers.StringPartialHandler;
@@ -44,9 +47,11 @@
     @Before
     public void init() throws DeploymentException
     {
-        DecoderFactory primitivesFactory = new DecoderFactory(PrimitiveDecoderMetadataSet.INSTANCE);
+        WebSocketContainerScope containerScope = new SimpleContainerScope(WebSocketPolicy.newClientPolicy());
+        
+        DecoderFactory primitivesFactory = new DecoderFactory(containerScope,PrimitiveDecoderMetadataSet.INSTANCE);
         metadatas = new DecoderMetadataSet();
-        decoders = new DecoderFactory(metadatas,primitivesFactory);
+        decoders = new DecoderFactory(containerScope,metadatas,primitivesFactory);
         factory = new MessageHandlerFactory();
     }
 
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/MessageReceivingTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/MessageReceivingTest.java
index 0577778..f5445e4 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/MessageReceivingTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/MessageReceivingTest.java
@@ -69,6 +69,7 @@
     public static void startServer() throws Exception {
         server = new Server();
         ServerConnector connector = new ServerConnector(server);
+        connector.setPort(0);
         server.addConnector(connector);
 
         handler = new EchoHandler();
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/ClientAnnotatedEndpointScanner_InvalidSignaturesTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/ClientAnnotatedEndpointScanner_InvalidSignaturesTest.java
index 27f7d2a..c5addce 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/ClientAnnotatedEndpointScanner_InvalidSignaturesTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/ClientAnnotatedEndpointScanner_InvalidSignaturesTest.java
@@ -18,10 +18,13 @@
 
 package org.eclipse.jetty.websocket.jsr356.endpoints;
 
+import static org.hamcrest.Matchers.containsString;
+
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+
 import javax.websocket.ClientEndpoint;
 import javax.websocket.ClientEndpointConfig;
 import javax.websocket.DeploymentException;
@@ -48,8 +51,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static org.hamcrest.Matchers.containsString;
-
 /**
  * Test {@link AnnotatedEndpointScanner} against various simple, 1 method, {@link ClientEndpoint} annotated classes with invalid signatures.
  */
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/TrackingSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/TrackingSocket.java
index eb549eb..a211138 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/TrackingSocket.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/TrackingSocket.java
@@ -18,8 +18,13 @@
 
 package org.eclipse.jetty.websocket.jsr356.endpoints;
 
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+
 import javax.websocket.CloseReason;
 import javax.websocket.CloseReason.CloseCode;
 
@@ -28,10 +33,6 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.junit.Assert;
 
-import static org.hamcrest.Matchers.greaterThanOrEqualTo;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-
 /**
  * Abstract base socket used for tracking state and events within the socket for testing reasons.
  */
@@ -99,7 +100,7 @@
 
     public void assertWasOpened() throws InterruptedException
     {
-        Assert.assertThat("Was Opened",openLatch.await(500,TimeUnit.MILLISECONDS),is(true));
+        Assert.assertThat("Was Opened",openLatch.await(30000,TimeUnit.MILLISECONDS),is(true));
     }
 
     public void clear()
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidCloseIntSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidCloseIntSocket.java
index 7b6c03a..65baf94 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidCloseIntSocket.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidCloseIntSocket.java
@@ -28,6 +28,7 @@
 {
     /**
      * Invalid Close Method Declaration (parameter type int)
+     * @param statusCode the status code
      */
     @OnClose
     public void onClose(int statusCode)
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidErrorErrorSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidErrorErrorSocket.java
index 3a97db3..4f3e2e4 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidErrorErrorSocket.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidErrorErrorSocket.java
@@ -28,6 +28,7 @@
 {
     /**
      * Invalid Error Method Declaration (parameter type Error)
+     * @param error the error
      */
     @OnError
     public void onError(Error error)
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidErrorExceptionSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidErrorExceptionSocket.java
index 97629f1..ea1d64a 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidErrorExceptionSocket.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidErrorExceptionSocket.java
@@ -28,6 +28,7 @@
 {
     /**
      * Invalid Error Method Declaration (parameter type Exception)
+     * @param e the extension
      */
     @OnError
     public void onError(Exception e)
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidErrorIntSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidErrorIntSocket.java
index 40c1774..19214d8 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidErrorIntSocket.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidErrorIntSocket.java
@@ -28,6 +28,7 @@
 {
     /**
      * Invalid Error Method Declaration (parameter type int)
+     * @param errorCount the error count
      */
     @OnError
     public void onError(int errorCount)
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenCloseReasonSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenCloseReasonSocket.java
index b66cba8..369c703 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenCloseReasonSocket.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenCloseReasonSocket.java
@@ -29,6 +29,7 @@
 {
     /**
      * Invalid Open Method Declaration (parameter type CloseReason)
+     * @param reason the close reason
      */
     @OnOpen
     public void onOpen(CloseReason reason)
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenIntSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenIntSocket.java
index 6d3c09c..5786c6d 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenIntSocket.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenIntSocket.java
@@ -28,6 +28,7 @@
 {
     /**
      * Invalid Open Method Declaration (parameter type int)
+     * @param value the open value
      */
     @OnOpen
     public void onOpen(int value)
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenSessionIntSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenSessionIntSocket.java
index fbef324..3074a18 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenSessionIntSocket.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenSessionIntSocket.java
@@ -29,6 +29,8 @@
 {
     /**
      * Invalid Open Method Declaration (parameter of type int)
+     * @param session the session for the open
+     * @param count the open count
      */
     @OnOpen
     public void onOpen(Session session, int count)
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/misbehaving/MisbehavingClassTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/misbehaving/MisbehavingClassTest.java
index 19af1d9..b7bb3df 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/misbehaving/MisbehavingClassTest.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/misbehaving/MisbehavingClassTest.java
@@ -18,8 +18,9 @@
 
 package org.eclipse.jetty.websocket.jsr356.misbehaving;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
 
 import java.net.URI;
 import java.util.concurrent.TimeUnit;
@@ -32,8 +33,8 @@
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.util.log.StacklessLogging;
+import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.jsr356.EchoHandler;
-import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointEventDriver;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -89,7 +90,7 @@
         WebSocketContainer container = ContainerProvider.getWebSocketContainer();
         EndpointRuntimeOnOpen socket = new EndpointRuntimeOnOpen();
 
-        try (StacklessLogging logging = new StacklessLogging(EndpointRuntimeOnOpen.class,JsrEndpointEventDriver.class))
+        try (StacklessLogging logging = new StacklessLogging(EndpointRuntimeOnOpen.class, WebSocketSession.class))
         {
             // expecting ArrayIndexOutOfBoundsException during onOpen
             Session session = container.connectToServer(socket,serverUri);
@@ -110,7 +111,7 @@
         WebSocketContainer container = ContainerProvider.getWebSocketContainer();
         AnnotatedRuntimeOnOpen socket = new AnnotatedRuntimeOnOpen();
 
-        try (StacklessLogging logging = new StacklessLogging(AnnotatedRuntimeOnOpen.class))
+        try (StacklessLogging logging = new StacklessLogging(AnnotatedRuntimeOnOpen.class, WebSocketSession.class))
         {
             // expecting ArrayIndexOutOfBoundsException during onOpen
             Session session = container.connectToServer(socket,serverUri);
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/samples/DummyConnection.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/samples/DummyConnection.java
deleted file mode 100644
index 4dfe200..0000000
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/samples/DummyConnection.java
+++ /dev/null
@@ -1,156 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.jsr356.samples;
-
-import java.net.InetSocketAddress;
-import java.util.concurrent.Executor;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.websocket.api.BatchMode;
-import org.eclipse.jetty.websocket.api.SuspendToken;
-import org.eclipse.jetty.websocket.api.WebSocketPolicy;
-import org.eclipse.jetty.websocket.api.WriteCallback;
-import org.eclipse.jetty.websocket.api.extensions.Frame;
-import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
-import org.eclipse.jetty.websocket.common.LogicalConnection;
-import org.eclipse.jetty.websocket.common.WebSocketSession;
-import org.eclipse.jetty.websocket.common.io.IOState;
-
-public class DummyConnection implements LogicalConnection
-{
-    private IOState iostate;
-
-    public DummyConnection()
-    {
-        this.iostate = new IOState();
-    }
-
-    @Override
-    public void close()
-    {
-    }
-
-    @Override
-    public void close(int statusCode, String reason)
-    {
-    }
-
-    @Override
-    public void disconnect()
-    {
-    }
-
-    @Override
-    public ByteBufferPool getBufferPool()
-    {
-        return null;
-    }
-
-    @Override
-    public Executor getExecutor()
-    {
-        return null;
-    }
-
-    @Override
-    public long getIdleTimeout()
-    {
-        return 0;
-    }
-
-    @Override
-    public IOState getIOState()
-    {
-        return this.iostate;
-    }
-
-    @Override
-    public InetSocketAddress getLocalAddress()
-    {
-        return null;
-    }
-
-    @Override
-    public long getMaxIdleTimeout()
-    {
-        return 0;
-    }
-
-    @Override
-    public WebSocketPolicy getPolicy()
-    {
-        return null;
-    }
-
-    @Override
-    public InetSocketAddress getRemoteAddress()
-    {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public WebSocketSession getSession()
-    {
-        return null;
-    }
-
-    @Override
-    public boolean isOpen()
-    {
-        return false;
-    }
-
-    @Override
-    public boolean isReading()
-    {
-        return false;
-    }
-
-    @Override
-    public void outgoingFrame(Frame frame, WriteCallback callback, BatchMode batchMode)
-    {
-    }
-
-    @Override
-    public void resume()
-    {
-    }
-
-    @Override
-    public void setMaxIdleTimeout(long ms)
-    {
-    }
-
-    @Override
-    public void setNextIncomingFrames(IncomingFrames incoming)
-    {
-    }
-
-    @Override
-    public void setSession(WebSocketSession session)
-    {
-    }
-
-    @Override
-    public SuspendToken suspend()
-    {
-        return null;
-    }
-}
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/samples/ExtDecoder.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/samples/ExtDecoder.java
index 255b289..62394e3 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/samples/ExtDecoder.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/samples/ExtDecoder.java
@@ -22,6 +22,7 @@
 
 /**
  * Testing scenario of an extended Decoder interface
+ * @param <T> the decoder type
  */
 public interface ExtDecoder<T> extends Decoder.Text<T>
 {
diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/resources/jetty-logging.properties b/jetty-websocket/javax-websocket-client-impl/src/test/resources/jetty-logging.properties
index 6c5baae..3acadb6 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/test/resources/jetty-logging.properties
+++ b/jetty-websocket/javax-websocket-client-impl/src/test/resources/jetty-logging.properties
@@ -1,5 +1,6 @@
 org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
 org.eclipse.jetty.LEVEL=WARN
 
-# org.eclipse.jetty.websocket.LEVEL=WARN
+# org.eclipse.jetty.websocket.LEVEL=INFO
+# org.eclipse.jetty.websocket.LEVEL=ALL
 # org.eclipse.jetty.websocket.jsr356.LEVEL=DEBUG
diff --git a/jetty-websocket/javax-websocket-server-impl/pom.xml b/jetty-websocket/javax-websocket-server-impl/pom.xml
index 8a1998a..4166221 100644
--- a/jetty-websocket/javax-websocket-server-impl/pom.xml
+++ b/jetty-websocket/javax-websocket-server-impl/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <groupId>org.eclipse.jetty.websocket</groupId>
     <artifactId>websocket-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -70,23 +70,6 @@
               </execution>
           </executions>
       </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptorRefs>
-                <descriptorRef>config</descriptorRef>
-              </descriptorRefs>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
     </plugins>
   </build>
 </project>
diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointConfig.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointConfig.java
index b7ce2a0..123598c 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointConfig.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointConfig.java
@@ -32,6 +32,8 @@
 import javax.websocket.server.ServerEndpoint;
 import javax.websocket.server.ServerEndpointConfig;
 
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
+
 public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
 {
     private final Class<?> endpointClass;
@@ -44,12 +46,12 @@
     private Map<String, Object> userProperties;
     private List<Extension> extensions;
 
-    public AnnotatedServerEndpointConfig(Class<?> endpointClass, ServerEndpoint anno) throws DeploymentException
+    public AnnotatedServerEndpointConfig(WebSocketContainerScope containerScope, Class<?> endpointClass, ServerEndpoint anno) throws DeploymentException
     {
-        this(endpointClass,anno,null);
+        this(containerScope,endpointClass,anno,null);
     }
 
-    public AnnotatedServerEndpointConfig(Class<?> endpointClass, ServerEndpoint anno, ServerEndpointConfig baseConfig) throws DeploymentException
+    public AnnotatedServerEndpointConfig(WebSocketContainerScope containerScope, Class<?> endpointClass, ServerEndpoint anno, ServerEndpointConfig baseConfig) throws DeploymentException
     {
         ServerEndpointConfig.Configurator configr = null;
 
@@ -109,23 +111,25 @@
         {
             userProperties.putAll(baseConfig.getUserProperties());
         }
+        
+        ServerEndpointConfig.Configurator cfgr;
 
         if (anno.configurator() == ServerEndpointConfig.Configurator.class)
         {
             if (configr != null)
             {
-                this.configurator = configr;
+                cfgr = configr;
             }
             else
             {
-                this.configurator = BasicServerEndpointConfigurator.INSTANCE;
+                cfgr = new ContainerDefaultConfigurator();
             }
         }
         else
         {
             try
             {
-                this.configurator = anno.configurator().newInstance();
+                cfgr = anno.configurator().newInstance();
             }
             catch (InstantiationException | IllegalAccessException e)
             {
@@ -137,6 +141,9 @@
                 throw new DeploymentException(err.toString(),e);
             }
         }
+        
+        // Make sure all Configurators obtained are decorated
+        this.configurator = containerScope.getObjectFactory().decorate(cfgr);
     }
 
     @Override
diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointMetadata.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointMetadata.java
index 93c4164..d6f2568 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointMetadata.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointMetadata.java
@@ -25,6 +25,7 @@
 import javax.websocket.server.ServerEndpointConfig;
 
 import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointMetadata;
 import org.eclipse.jetty.websocket.jsr356.annotations.IJsrParamId;
 
@@ -33,7 +34,7 @@
     private final ServerEndpoint endpoint;
     private final AnnotatedServerEndpointConfig config;
 
-    protected AnnotatedServerEndpointMetadata(Class<?> websocket, ServerEndpointConfig baseConfig) throws DeploymentException
+    protected AnnotatedServerEndpointMetadata(WebSocketContainerScope containerScope, Class<?> websocket, ServerEndpointConfig baseConfig) throws DeploymentException
     {
         super(websocket);
 
@@ -44,9 +45,9 @@
         }
 
         this.endpoint = anno;
-        this.config = new AnnotatedServerEndpointConfig(websocket,anno,baseConfig);
+        this.config = new AnnotatedServerEndpointConfig(containerScope,websocket,anno,baseConfig);
         
-        getDecoders().addAll(anno.decoders());
+        getDecoders().addAll(anno.decoders());  
         getEncoders().addAll(anno.encoders());
     }
 
diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/BasicServerEndpointConfig.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/BasicServerEndpointConfig.java
index c526c4c..58674ea 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/BasicServerEndpointConfig.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/BasicServerEndpointConfig.java
@@ -28,6 +28,8 @@
 import javax.websocket.Extension;
 import javax.websocket.server.ServerEndpointConfig;
 
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
+
 public class BasicServerEndpointConfig implements ServerEndpointConfig
 {
     private final List<Class<? extends Decoder>> decoders;
@@ -39,7 +41,7 @@
     private final String path;
     private Map<String, Object> userProperties;
 
-    public BasicServerEndpointConfig(Class<?> endpointClass, String path)
+    public BasicServerEndpointConfig(WebSocketContainerScope containerScope, Class<?> endpointClass, String path)
     {
         this.endpointClass = endpointClass;
         this.path = path;
@@ -49,10 +51,10 @@
         this.subprotocols = new ArrayList<>();
         this.extensions = new ArrayList<>();
         this.userProperties = new HashMap<>();
-        this.configurator = BasicServerEndpointConfigurator.INSTANCE;
+        this.configurator = new ContainerDefaultConfigurator();
     }
 
-    public BasicServerEndpointConfig(ServerEndpointConfig copy)
+    public BasicServerEndpointConfig(WebSocketContainerScope containerScope, ServerEndpointConfig copy)
     {
         // immutable concepts
         this.endpointClass = copy.getEndpointClass();
@@ -62,15 +64,21 @@
         this.encoders = copy.getEncoders();
         this.subprotocols = copy.getSubprotocols();
         this.extensions = copy.getExtensions();
+
+        ServerEndpointConfig.Configurator cfgr;
+
         if (copy.getConfigurator() != null)
         {
-            this.configurator = copy.getConfigurator();
+            cfgr = copy.getConfigurator();
         }
         else
         {
-            this.configurator = BasicServerEndpointConfigurator.INSTANCE;
+            cfgr = new ContainerDefaultConfigurator();
         }
 
+        // Make sure all Configurators obtained are decorated
+        this.configurator = containerScope.getObjectFactory().decorate(cfgr);
+
         // mutable concepts
         this.userProperties = new HashMap<>(copy.getUserProperties());
     }
diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/BasicServerEndpointConfigurator.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/BasicServerEndpointConfigurator.java
deleted file mode 100644
index eb23a22..0000000
--- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/BasicServerEndpointConfigurator.java
+++ /dev/null
@@ -1,108 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.jsr356.server;
-
-import java.util.List;
-import javax.websocket.Extension;
-import javax.websocket.HandshakeResponse;
-import javax.websocket.server.HandshakeRequest;
-import javax.websocket.server.ServerEndpointConfig;
-
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.websocket.api.util.QuoteUtil;
-
-public class BasicServerEndpointConfigurator extends ServerEndpointConfig.Configurator
-{
-    private static final Logger LOG = Log.getLogger(BasicServerEndpointConfigurator.class);
-    private static final String NO_SUBPROTOCOL = "";
-    public static final ServerEndpointConfig.Configurator INSTANCE = new BasicServerEndpointConfigurator();
-
-    @Override
-    public boolean checkOrigin(String originHeaderValue)
-    {
-        return true;
-    }
-
-    @Override
-    public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException
-    {
-        if (LOG.isDebugEnabled())
-        {
-            LOG.debug(".getEndpointInstance({})",endpointClass);
-        }
-        try
-        {
-            return endpointClass.newInstance();
-        }
-        catch (IllegalAccessException e)
-        {
-            throw new InstantiationException(String.format("%s: %s",e.getClass().getName(),e.getMessage()));
-        }
-    }
-
-    @Override
-    public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested)
-    {
-        return requested;
-    }
-
-    @Override
-    public String getNegotiatedSubprotocol(List<String> supported, List<String> requested)
-    {
-        if ((requested == null) || (requested.size() == 0))
-        {
-            // nothing requested, don't return anything
-            return NO_SUBPROTOCOL;
-        }
-
-        // Nothing specifically called out as being supported by the endpoint
-        if ((supported == null) || (supported.isEmpty()))
-        {
-            // Just return the first hit in this case
-            LOG.warn("Client requested Subprotocols on endpoint with none supported: {}",QuoteUtil.join(requested,","));
-            return NO_SUBPROTOCOL;
-        }
-
-        // Return the first matching hit from the list of supported protocols.
-        for (String possible : requested)
-        {
-            if (possible == null)
-            {
-                // skip null
-                continue;
-            }
-
-            if (supported.contains(possible))
-            {
-                return possible;
-            }
-        }
-
-        LOG.warn("Client requested subprotocols {} do not match any endpoint supported subprotocols {}",QuoteUtil.join(requested,","),
-                QuoteUtil.join(supported,","));
-        return NO_SUBPROTOCOL;
-    }
-
-    @Override
-    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response)
-    {
-        /* do nothing */
-    }
-}
diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ContainerDefaultConfigurator.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ContainerDefaultConfigurator.java
new file mode 100644
index 0000000..b116f17
--- /dev/null
+++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ContainerDefaultConfigurator.java
@@ -0,0 +1,128 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.jsr356.server;
+
+import java.util.List;
+import java.util.ServiceLoader;
+
+import javax.websocket.Extension;
+import javax.websocket.HandshakeResponse;
+import javax.websocket.server.HandshakeRequest;
+import javax.websocket.server.ServerEndpointConfig;
+import javax.websocket.server.ServerEndpointConfig.Configurator;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.util.QuoteUtil;
+
+/**
+ * The "Container Default Configurator" per the JSR-356 spec.
+ * 
+ * @see ServiceLoader behavior of {@link javax.websocket.server.ServerEndpointConfig.Configurator}
+ */
+public final class ContainerDefaultConfigurator extends Configurator
+{
+    private static final Logger LOG = Log.getLogger(ContainerDefaultConfigurator.class);
+    private static final String NO_SUBPROTOCOL = "";
+    
+    /**
+     * Default Constructor required, as
+     * javax.websocket.server.ServerEndpointConfig$Configurator.fetchContainerDefaultConfigurator()
+     * will be the one that instantiates this class in most cases.
+     */
+    public ContainerDefaultConfigurator()
+    {
+        super();
+    }
+
+    @Override
+    public boolean checkOrigin(String originHeaderValue)
+    {
+        return true;
+    }
+
+    @Override
+    public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException
+    {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug(".getEndpointInstance({})",endpointClass);
+        }
+        
+        try
+        {
+            // Since this is started via a ServiceLoader, this class has no Scope or context
+            // that can be used to obtain a ObjectFactory from.
+            return endpointClass.newInstance();
+        }
+        catch (IllegalAccessException e)
+        {
+            throw new InstantiationException(String.format("%s: %s",e.getClass().getName(),e.getMessage()));
+        }
+    }
+
+    @Override
+    public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested)
+    {
+        return requested;
+    }
+
+    @Override
+    public String getNegotiatedSubprotocol(List<String> supported, List<String> requested)
+    {
+        if ((requested == null) || (requested.size() == 0))
+        {
+            // nothing requested, don't return anything
+            return NO_SUBPROTOCOL;
+        }
+
+        // Nothing specifically called out as being supported by the endpoint
+        if ((supported == null) || (supported.isEmpty()))
+        {
+            // Just return the first hit in this case
+            LOG.warn("Client requested Subprotocols on endpoint with none supported: {}",QuoteUtil.join(requested,","));
+            return NO_SUBPROTOCOL;
+        }
+
+        // Return the first matching hit from the list of supported protocols.
+        for (String possible : requested)
+        {
+            if (possible == null)
+            {
+                // skip null
+                continue;
+            }
+
+            if (supported.contains(possible))
+            {
+                return possible;
+            }
+        }
+
+        LOG.warn("Client requested subprotocols {} do not match any endpoint supported subprotocols {}",QuoteUtil.join(requested,","),
+                QuoteUtil.join(supported,","));
+        return NO_SUBPROTOCOL;
+    }
+
+    @Override
+    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response)
+    {
+        /* do nothing */
+    }
+}
diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/JsrCreator.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/JsrCreator.java
index cc665d5..057f563 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/JsrCreator.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/JsrCreator.java
@@ -35,9 +35,9 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.jsr356.JsrExtension;
 import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
-import org.eclipse.jetty.websocket.jsr356.server.pathmap.WebSocketPathSpec;
 import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
 import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
 import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
@@ -48,11 +48,13 @@
     public static final String PROP_LOCAL_ADDRESS = "javax.websocket.endpoint.localAddress";
     public static final String PROP_LOCALES = "javax.websocket.upgrade.locales";
     private static final Logger LOG = Log.getLogger(JsrCreator.class);
+    private final WebSocketContainerScope containerScope;
     private final ServerEndpointMetadata metadata;
     private final ExtensionFactory extensionFactory;
 
-    public JsrCreator(ServerEndpointMetadata metadata, ExtensionFactory extensionFactory)
+    public JsrCreator(WebSocketContainerScope containerScope, ServerEndpointMetadata metadata, ExtensionFactory extensionFactory)
     {
+        this.containerScope = containerScope;
         this.metadata = metadata;
         this.extensionFactory = extensionFactory;
     }
@@ -65,11 +67,11 @@
 
         // Get raw config, as defined when the endpoint was added to the container
         ServerEndpointConfig config = metadata.getConfig();
-
+        
         // Establish a copy of the config, so that the UserProperties are unique
         // per upgrade request.
-        config = new BasicServerEndpointConfig(config);
-
+        config = new BasicServerEndpointConfig(containerScope, config);
+        
         // Bug 444617 - Expose localAddress and remoteAddress for jsr modify handshake to use
         // This is being implemented as an optional set of userProperties so that
         // it is not JSR api breaking.  A few users on #jetty and a few from cometd
@@ -141,7 +143,7 @@
             UriTemplatePathSpec wspathSpec = (UriTemplatePathSpec)pathSpec;
             String requestPath = req.getRequestPath();
             // Wrap the config with the path spec information
-            config = new PathParamServerEndpointConfig(config,wspathSpec,requestPath);
+            config = new PathParamServerEndpointConfig(containerScope,config,wspathSpec,requestPath);
         }
 
         // [JSR] Step 5: Call modifyHandshake
@@ -152,6 +154,8 @@
             // [JSR] Step 6: create endpoint class
             Class<?> endpointClass = config.getEndpointClass();
             Object endpoint = config.getConfigurator().getEndpointInstance(endpointClass);
+            // Do not decorate here (let the Connection and Session start first)
+            // This will allow CDI to see Session for injection into Endpoint classes.
             return new EndpointInstance(endpoint,config,metadata);
         }
         catch (InstantiationException e)
diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/PathParamServerEndpointConfig.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/PathParamServerEndpointConfig.java
index d3775da..6124931 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/PathParamServerEndpointConfig.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/PathParamServerEndpointConfig.java
@@ -24,17 +24,19 @@
 import javax.websocket.server.ServerEndpointConfig;
 
 import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
+import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 
 /**
- * Wrapper for a {@link ServerEndpointConfig} where there PathParm information from the incoming request.
+ * Wrapper for a {@link ServerEndpointConfig} where there is PathParam information from the incoming request.
  */
 public class PathParamServerEndpointConfig extends BasicServerEndpointConfig implements ServerEndpointConfig
 {
     private final Map<String, String> pathParamMap;
 
-    public PathParamServerEndpointConfig(ServerEndpointConfig config, UriTemplatePathSpec pathSpec, String requestPath)
+    public PathParamServerEndpointConfig(WebSocketContainerScope containerScope, ServerEndpointConfig config, UriTemplatePathSpec pathSpec, String requestPath)
     {
-        super(config);
+        super(containerScope, config);
 
         Map<String, String> pathMap = pathSpec.getPathParams(requestPath);
         pathParamMap = new HashMap<>();
diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java
index 0052d64..a6a130a 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java
@@ -18,15 +18,21 @@
 
 package org.eclipse.jetty.websocket.jsr356.server;
 
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 import javax.websocket.DeploymentException;
 import javax.websocket.Endpoint;
+import javax.websocket.Session;
 import javax.websocket.server.ServerEndpoint;
 import javax.websocket.server.ServerEndpointConfig;
 
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
 import org.eclipse.jetty.websocket.jsr356.ClientContainer;
 import org.eclipse.jetty.websocket.jsr356.JsrSessionFactory;
@@ -34,23 +40,27 @@
 import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
 import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
 import org.eclipse.jetty.websocket.server.NativeWebSocketConfiguration;
+import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
 
 public class ServerContainer extends ClientContainer implements javax.websocket.server.ServerContainer
 {
     private static final Logger LOG = Log.getLogger(ServerContainer.class);
 
     private final NativeWebSocketConfiguration configuration;
+    private List<Class<?>> deferredEndpointClasses;
+    private List<ServerEndpointConfig> deferredEndpointConfigs;
 
-    public ServerContainer(NativeWebSocketConfiguration nativeWebSocketConfiguration, Executor executor)
+    public ServerContainer(NativeWebSocketConfiguration configuration, Executor executor)
     {
-        super(executor);
-        this.configuration = nativeWebSocketConfiguration;
+        super(configuration.getFactory());
+        this.configuration = configuration;
         EventDriverFactory eventDriverFactory = this.configuration.getFactory().getEventDriverFactory();
         eventDriverFactory.addImplementation(new JsrServerEndpointImpl());
         eventDriverFactory.addImplementation(new JsrServerExtendsEndpointImpl());
-        this.configuration.getFactory().addSessionFactory(new JsrSessionFactory(this,this));
+        this.configuration.getFactory().addSessionFactory(new JsrSessionFactory(this));
+        addBean(this.configuration);
     }
-    
+
     public EndpointInstance newClientEndpointInstance(Object endpoint, ServerEndpointConfig config, String path)
     {
         EndpointMetadata metadata = getClientEndpointMetadata(endpoint.getClass(),config);
@@ -63,7 +73,7 @@
             }
             else
             {
-                cec = new BasicServerEndpointConfig(endpoint.getClass(),path);
+                cec = new BasicServerEndpointConfig(this,endpoint.getClass(),path);
             }
         }
         return new EndpointInstance(endpoint,cec,metadata);
@@ -72,25 +82,73 @@
     @Override
     public void addEndpoint(Class<?> endpointClass) throws DeploymentException
     {
-        ServerEndpointMetadata metadata = getServerEndpointMetadata(endpointClass,null);
-        addEndpoint(metadata);
+        if (isStarted() || isStarting())
+        {
+            ServerEndpointMetadata metadata = getServerEndpointMetadata(endpointClass,null);
+            addEndpoint(metadata);
+        }
+        else
+        {
+            if (deferredEndpointClasses == null)
+            {
+                deferredEndpointClasses = new ArrayList<Class<?>>();
+            }
+            deferredEndpointClasses.add(endpointClass);
+        }
     }
 
-    public void addEndpoint(ServerEndpointMetadata metadata) throws DeploymentException
+    private void addEndpoint(ServerEndpointMetadata metadata) throws DeploymentException
     {
-        JsrCreator creator = new JsrCreator(metadata,this.configuration.getFactory().getExtensionFactory());
+        JsrCreator creator = new JsrCreator(this,metadata,this.configuration.getFactory().getExtensionFactory());
         this.configuration.addMapping("uri-template|" + metadata.getPath(), creator);
     }
 
     @Override
     public void addEndpoint(ServerEndpointConfig config) throws DeploymentException
     {
-        if (LOG.isDebugEnabled())
+        if (isStarted() || isStarting())
         {
-            LOG.debug("addEndpoint({}) path={} endpoint={}",config,config.getPath(),config.getEndpointClass());
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("addEndpoint({}) path={} endpoint={}",config,config.getPath(),config.getEndpointClass());
+            }
+            ServerEndpointMetadata metadata = getServerEndpointMetadata(config.getEndpointClass(),config);
+            addEndpoint(metadata);
         }
-        ServerEndpointMetadata metadata = getServerEndpointMetadata(config.getEndpointClass(),config);
-        addEndpoint(metadata);
+        else
+        {
+            if (deferredEndpointConfigs == null)
+            {
+                deferredEndpointConfigs = new ArrayList<ServerEndpointConfig>();
+            }
+            deferredEndpointConfigs.add(config);
+        }
+    }
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        // Proceed with Normal Startup
+        super.doStart();
+
+        // Process Deferred Endpoints
+        if (deferredEndpointClasses != null)
+        {
+            for (Class<?> endpointClass : deferredEndpointClasses)
+            {
+                addEndpoint(endpointClass);
+            }
+            deferredEndpointClasses.clear();
+        }
+
+        if (deferredEndpointConfigs != null)
+        {
+            for (ServerEndpointConfig config : deferredEndpointConfigs)
+            {
+                addEndpoint(config);
+            }
+            deferredEndpointConfigs.clear();
+        }
     }
 
     public ServerEndpointMetadata getServerEndpointMetadata(final Class<?> endpoint, final ServerEndpointConfig config) throws DeploymentException
@@ -101,7 +159,7 @@
         if (anno != null)
         {
             // Annotated takes precedence here
-            AnnotatedServerEndpointMetadata ametadata = new AnnotatedServerEndpointMetadata(endpoint,config);
+            AnnotatedServerEndpointMetadata ametadata = new AnnotatedServerEndpointMetadata(this,endpoint,config);
             AnnotatedEndpointScanner<ServerEndpoint, ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(ametadata);
             metadata = ametadata;
             scanner.scan();
@@ -149,6 +207,11 @@
     {
         return this.configuration.getPolicy().getMaxTextMessageSize();
     }
+    
+    public WebSocketServerFactory getWebSocketServerFactory()
+    {
+        return this.configuration.getFactory();
+    }
 
     @Override
     public void setAsyncSendTimeout(long ms)
@@ -183,4 +246,22 @@
         // incoming streaming buffer size
         this.configuration.getPolicy().setMaxTextMessageBufferSize(max);
     }
+
+    @Override
+    public void onSessionClosed(WebSocketSession session)
+    {
+        getWebSocketServerFactory().onSessionClosed(session);
+    }
+
+    @Override
+    public void onSessionOpened(WebSocketSession session)
+    {
+        getWebSocketServerFactory().onSessionOpened(session);
+    }
+
+    @Override
+    public Set<Session> getOpenSessions()
+    {
+        return new HashSet<>(getWebSocketServerFactory().getBeans(Session.class));
+    }
 }
diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/deploy/WebSocketServerContainerInitializer.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/deploy/WebSocketServerContainerInitializer.java
index dcfc92b..8f564b5 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/deploy/WebSocketServerContainerInitializer.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/deploy/WebSocketServerContainerInitializer.java
@@ -23,6 +23,8 @@
 
 import javax.servlet.ServletContainerInitializer;
 import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.HandlesTypes;
 import javax.websocket.DeploymentException;
@@ -50,6 +52,34 @@
     private static final Logger LOG = Log.getLogger(WebSocketServerContainerInitializer.class);
     
     /**
+     * DestroyListener
+     */
+    public static class ContextDestroyListener implements ServletContextListener
+    {
+        @Override
+        public void contextInitialized(ServletContextEvent sce)
+        {
+            //noop
+        }
+
+        @Override
+        public void contextDestroyed(ServletContextEvent sce)
+        {
+            //remove any ServerContainer beans
+            if (sce.getServletContext() instanceof ContextHandler.Context)
+            {
+                ContextHandler handler = ((ContextHandler.Context)sce.getServletContext()).getContextHandler();
+                ServerContainer bean = handler.getBean(ServerContainer.class);
+                if (bean != null)
+                    handler.removeBean(bean);
+            }
+            
+            //remove reference in attributes
+            sce.getServletContext().removeAttribute(javax.websocket.server.ServerContainer.class.getName());
+        }
+    }
+    
+    /**
      * Test a ServletContext for {@code init-param} or {@code attribute} at {@code keyName} for
      * true or false setting that determines if the specified feature is enabled (or not).
      *
@@ -62,38 +92,38 @@
     {
         // Try context parameters first
         String cp = context.getInitParameter(keyName);
-        
+    
         if(cp != null)
         {
             if (TypeUtil.isTrue(cp))
             {
                 return true;
             }
-            
+        
             if (TypeUtil.isFalse(cp))
             {
                 return false;
             }
-            
+        
             return defValue;
         }
-        
+    
         // Next, try attribute on context
         Object enable = context.getAttribute(keyName);
-        
+    
         if(enable != null)
         {
             if (TypeUtil.isTrue(enable))
             {
                 return true;
             }
-            
+        
             if (TypeUtil.isFalse(enable))
             {
                 return false;
             }
         }
-        
+    
         return defValue;
     }
     
@@ -118,7 +148,6 @@
             if (LOG.isDebugEnabled())
                 LOG.debug("Dynamic filter add to support JSR356/javax.websocket.server: {}", WebSocketUpgradeFilter.class.getName());
             WebSocketUpgradeFilter.configureContext(context);
-            NativeWebSocketServletContainerInitializer.getDefaultFrom(context.getServletContext());
         }
     
         return jettyContainer;
@@ -132,7 +161,7 @@
     {
         return configureContext(jettyContext);
     }
-    
+
     @Override
     public void onStartup(Set<Class<?>> c, ServletContext context) throws ServletException
     {
@@ -163,10 +192,9 @@
             
             // Create the Jetty ServerContainer implementation
             ServerContainer jettyContainer = configureContext(jettyContext);
-
-            // Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment
-            context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer);
-
+    
+            context.addListener(new ContextDestroyListener()); // make sure context is cleaned up when the context stops
+    
             if (c.isEmpty())
             {
                 if (LOG.isDebugEnabled())
diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/WebSocketPathSpec.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/WebSocketPathSpec.java
deleted file mode 100644
index e7a8f60..0000000
--- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/WebSocketPathSpec.java
+++ /dev/null
@@ -1,344 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.jsr356.server.pathmap;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.eclipse.jetty.util.TypeUtil;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.websocket.server.pathmap.PathSpecGroup;
-import org.eclipse.jetty.websocket.server.pathmap.RegexPathSpec;
-
-/**
- * PathSpec for WebSocket &#064;{@link ServerEndpoint} declarations with support for URI templates and &#064;{@link PathParam} annotations
- * 
- * @see javax.websocket spec (JSR-356) Section 3.1.1 URI Mapping
- * @see <a href="https://tools.ietf.org/html/rfc6570">URI Templates (Level 1)</a>
- */
-public class WebSocketPathSpec extends RegexPathSpec
-{
-    private static final Logger LOG = Log.getLogger(WebSocketPathSpec.class);
-    
-    private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{(.*)\\}");
-    /** Reserved Symbols in URI Template variable */
-    private static final String VARIABLE_RESERVED = ":/?#[]@" + // gen-delims
-                                                    "!$&'()*+,;="; // sub-delims
-    /** Allowed Symboles in a URI Template variable */
-    private static final String VARIABLE_SYMBOLS="-._";
-    private static final Set<String> FORBIDDEN_SEGMENTS;
-
-    static
-    {
-        FORBIDDEN_SEGMENTS = new HashSet<>();
-        FORBIDDEN_SEGMENTS.add("/./");
-        FORBIDDEN_SEGMENTS.add("/../");
-        FORBIDDEN_SEGMENTS.add("//");
-    }
-
-    private String variables[];
-
-    public WebSocketPathSpec(String pathParamSpec)
-    {
-        super();
-        Objects.requireNonNull(pathParamSpec,"Path Param Spec cannot be null");
-
-        if ("".equals(pathParamSpec) || "/".equals(pathParamSpec))
-        {
-            super.pathSpec = "/";
-            super.pattern = Pattern.compile("^/$");
-            super.pathDepth = 1;
-            this.specLength = 1;
-            this.variables = new String[0];
-            this.group = PathSpecGroup.EXACT;
-            return;
-        }
-
-        if (pathParamSpec.charAt(0) != '/')
-        {
-            // path specs must start with '/'
-            StringBuilder err = new StringBuilder();
-            err.append("Syntax Error: path spec \"");
-            err.append(pathParamSpec);
-            err.append("\" must start with '/'");
-            throw new IllegalArgumentException(err.toString());
-        }
-
-        for (String forbidden : FORBIDDEN_SEGMENTS)
-        {
-            if (pathParamSpec.contains(forbidden))
-            {
-                StringBuilder err = new StringBuilder();
-                err.append("Syntax Error: segment ");
-                err.append(forbidden);
-                err.append(" is forbidden in path spec: ");
-                err.append(pathParamSpec);
-                throw new IllegalArgumentException(err.toString());
-            }
-        }
-
-        this.pathSpec = pathParamSpec;
-
-        StringBuilder regex = new StringBuilder();
-        regex.append('^');
-
-        List<String> varNames = new ArrayList<>();
-        // split up into path segments (ignoring the first slash that will always be empty)
-        String segments[] = pathParamSpec.substring(1).split("/");
-        char segmentSignature[] = new char[segments.length];
-        this.pathDepth = segments.length;
-        for (int i = 0; i < segments.length; i++)
-        {
-            String segment = segments[i];
-            Matcher mat = VARIABLE_PATTERN.matcher(segment);
-
-            if (mat.matches())
-            {
-                // entire path segment is a variable.
-                String variable = mat.group(1);
-                if (varNames.contains(variable))
-                {
-                    // duplicate variable names
-                    StringBuilder err = new StringBuilder();
-                    err.append("Syntax Error: variable ");
-                    err.append(variable);
-                    err.append(" is duplicated in path spec: ");
-                    err.append(pathParamSpec);
-                    throw new IllegalArgumentException(err.toString());
-                }
-
-                assertIsValidVariableLiteral(variable);
-
-                segmentSignature[i] = 'v'; // variable
-                // valid variable name
-                varNames.add(variable);
-                // build regex
-                regex.append("/([^/]+)");
-            }
-            else if (mat.find(0))
-            {
-                // variable exists as partial segment
-                StringBuilder err = new StringBuilder();
-                err.append("Syntax Error: variable ");
-                err.append(mat.group());
-                err.append(" must exist as entire path segment: ");
-                err.append(pathParamSpec);
-                throw new IllegalArgumentException(err.toString());
-            }
-            else if ((segment.indexOf('{') >= 0) || (segment.indexOf('}') >= 0))
-            {
-                // variable is split with a path separator
-                StringBuilder err = new StringBuilder();
-                err.append("Syntax Error: invalid path segment /");
-                err.append(segment);
-                err.append("/ variable declaration incomplete: ");
-                err.append(pathParamSpec);
-                throw new IllegalArgumentException(err.toString());
-            }
-            else if (segment.indexOf('*') >= 0)
-            {
-                // glob segment
-                StringBuilder err = new StringBuilder();
-                err.append("Syntax Error: path segment /");
-                err.append(segment);
-                err.append("/ contains a wildcard symbol (not supported by javax.websocket): ");
-                err.append(pathParamSpec);
-                throw new IllegalArgumentException(err.toString());
-            }
-            else
-            {
-                // valid path segment
-                segmentSignature[i] = 'e'; // exact
-                // build regex
-                regex.append('/');
-                // escape regex special characters
-                for (char c : segment.toCharArray())
-                {
-                    if ((c == '.') || (c == '[') || (c == ']') || (c == '\\'))
-                    {
-                        regex.append('\\');
-                    }
-                    regex.append(c);
-                }
-            }
-        }
-        
-        // Handle trailing slash (which is not picked up during split)
-        if(pathParamSpec.charAt(pathParamSpec.length()-1) == '/')
-        {
-            regex.append('/');
-        }
-
-        regex.append('$');
-
-        this.pattern = Pattern.compile(regex.toString());
-
-        int varcount = varNames.size();
-        this.variables = varNames.toArray(new String[varcount]);
-
-        // Convert signature to group
-        String sig = String.valueOf(segmentSignature);
-
-        if (Pattern.matches("^e*$",sig))
-        {
-            this.group = PathSpecGroup.EXACT;
-        }
-        else if (Pattern.matches("^e*v+",sig))
-        {
-            this.group = PathSpecGroup.PREFIX_GLOB;
-        }
-        else if (Pattern.matches("^v+e+",sig))
-        {
-            this.group = PathSpecGroup.SUFFIX_GLOB;
-        }
-        else
-        {
-            this.group = PathSpecGroup.MIDDLE_GLOB;
-        }
-    }
-
-    /**
-     * Validate variable literal name, per RFC6570, Section 2.1 Literals
-     * @param variable
-     * @param pathParamSpec
-     */
-    private void assertIsValidVariableLiteral(String variable)
-    {
-        int len = variable.length();
-        
-        int i = 0;
-        int codepoint;
-        boolean valid = (len > 0); // must not be zero length
-        
-        while (valid && i < len)
-        {
-            codepoint = variable.codePointAt(i);
-            i += Character.charCount(codepoint);
-
-            // basic letters, digits, or symbols
-            if (isValidBasicLiteralCodepoint(codepoint))
-            {
-                continue;
-            }
-
-            // The ucschar and iprivate pieces
-            if (Character.isSupplementaryCodePoint(codepoint))
-            {
-                continue;
-            }
-
-            // pct-encoded
-            if (codepoint == '%')
-            {
-                if (i + 2 > len)
-                {
-                    // invalid percent encoding, missing extra 2 chars
-                    valid = false;
-                    continue;
-                }
-                codepoint = TypeUtil.convertHexDigit(variable.codePointAt(i++)) << 4;
-                codepoint |= TypeUtil.convertHexDigit(variable.codePointAt(i++));
-
-                // validate basic literal
-                if (isValidBasicLiteralCodepoint(codepoint))
-                {
-                    continue;
-                }
-            }
-            
-            valid = false;
-        }
-
-        if (!valid)
-        {
-            // invalid variable name
-            StringBuilder err = new StringBuilder();
-            err.append("Syntax Error: variable {");
-            err.append(variable);
-            err.append("} an invalid variable name: ");
-            err.append(pathSpec);
-            throw new IllegalArgumentException(err.toString());
-        }
-    }
-    
-    private boolean isValidBasicLiteralCodepoint(int codepoint)
-    {
-        // basic letters or digits
-        if((codepoint >= 'a' && codepoint <= 'z') ||
-           (codepoint >= 'A' && codepoint <= 'Z') ||
-           (codepoint >= '0' && codepoint <= '9'))
-        {
-            return true;
-        }
-        
-        // basic allowed symbols
-        if(VARIABLE_SYMBOLS.indexOf(codepoint) >= 0)
-        {
-            return true; // valid simple value
-        }
-        
-        // basic reserved symbols
-        if(VARIABLE_RESERVED.indexOf(codepoint) >= 0)
-        {
-            LOG.warn("Detected URI Template reserved symbol [{}] in path spec \"{}\"",(char)codepoint,pathSpec);
-            return false; // valid simple value
-        }
-
-        return false;
-    }
-
-    public Map<String, String> getPathParams(String path)
-    {
-        Matcher matcher = getMatcher(path);
-        if (matcher.matches())
-        {
-            if (group == PathSpecGroup.EXACT)
-            {
-                return Collections.emptyMap();
-            }
-            Map<String, String> ret = new HashMap<>();
-            int groupCount = matcher.groupCount();
-            for (int i = 1; i <= groupCount; i++)
-            {
-                ret.put(this.variables[i - 1],matcher.group(i));
-            }
-            return ret;
-        }
-        return null;
-    }
-
-    public int getVariableCount()
-    {
-        return variables.length;
-    }
-
-    public String[] getVariables()
-    {
-        return this.variables;
-    }
-}
diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/resources/META-INF/services/javax.websocket.server.ServerEndpointConfig$Configurator b/jetty-websocket/javax-websocket-server-impl/src/main/resources/META-INF/services/javax.websocket.server.ServerEndpointConfig$Configurator
index 57e3ef3..7f67aa0 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/main/resources/META-INF/services/javax.websocket.server.ServerEndpointConfig$Configurator
+++ b/jetty-websocket/javax-websocket-server-impl/src/main/resources/META-INF/services/javax.websocket.server.ServerEndpointConfig$Configurator
@@ -1 +1 @@
-org.eclipse.jetty.websocket.jsr356.server.BasicServerEndpointConfigurator
\ No newline at end of file
+org.eclipse.jetty.websocket.jsr356.server.ContainerDefaultConfigurator
\ No newline at end of file
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointTest.java
index 6198823..139e46e 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.jsr356.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
 
 import java.io.File;
 import java.net.URI;
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/BasicEndpointTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/BasicEndpointTest.java
index 3bde1de..b581c41 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/BasicEndpointTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/BasicEndpointTest.java
@@ -35,7 +35,7 @@
 import org.junit.Test;
 
 /**
- * Example of an {@link Endpoint} extended echo server added programmatically via the
+ * Example of an {@link javax.websocket.Endpoint} extended echo server added programmatically via the
  * {@link ServerContainer#addEndpoint(javax.websocket.server.ServerEndpointConfig)}
  */
 public class BasicEndpointTest
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/BinaryStreamTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/BinaryStreamTest.java
index 527c03a..a1f21e6 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/BinaryStreamTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/BinaryStreamTest.java
@@ -26,6 +26,7 @@
 import java.util.Random;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+
 import javax.websocket.ClientEndpoint;
 import javax.websocket.ContainerProvider;
 import javax.websocket.OnMessage;
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java
index a9667cd..7c4324c 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java
@@ -20,6 +20,7 @@
 
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -36,6 +37,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
 
 import javax.websocket.DecodeException;
 import javax.websocket.Decoder;
@@ -59,6 +61,7 @@
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
 import org.eclipse.jetty.websocket.common.test.HttpResponse;
+import org.eclipse.jetty.websocket.common.test.IBlockheadClient;
 import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -96,9 +99,19 @@
     public static class NoExtensionsSocket
     {
         @OnMessage
-        public String echo(String message)
+        public String echo(Session session, String message)
         {
-            return message;
+            List<Extension> negotiatedExtensions = session.getNegotiatedExtensions();
+            if (negotiatedExtensions == null)
+            {
+                return "negotiatedExtensions=null";
+            }
+            else
+            {
+                return "negotiatedExtensions=" + negotiatedExtensions.stream()
+                        .map((ext) -> ext.getName())
+                        .collect(Collectors.joining(",", "[", "]"));
+            }
         }
     }
 
@@ -399,7 +412,7 @@
     {
         URI uri = baseServerUri.resolve("/empty");
 
-        try (BlockheadClient client = new BlockheadClient(uri))
+        try (IBlockheadClient client = new BlockheadClient(uri))
         {
             client.addExtensions("identity");
             client.connect();
@@ -414,13 +427,18 @@
     {
         URI uri = baseServerUri.resolve("/no-extensions");
 
-        try (BlockheadClient client = new BlockheadClient(uri))
+        try (IBlockheadClient client = new BlockheadClient(uri))
         {
             client.addExtensions("identity");
             client.connect();
             client.sendStandardRequest();
-            HttpResponse response = client.readResponseHeader();
-            Assert.assertThat("response.extensions", response.getExtensionsHeader(), nullValue());
+            HttpResponse response = client.expectUpgradeResponse();
+            assertThat("response.extensions", response.getExtensionsHeader(), nullValue());
+    
+            client.write(new TextFrame().setPayload("NegoExts"));
+            EventQueue<WebSocketFrame> frames = client.readFrames(1, 1, TimeUnit.SECONDS);
+            WebSocketFrame frame = frames.poll();
+            assertThat("Frame Response", frame.getPayloadAsUTF8(), is("negotiatedExtensions=[]"));
         }
     }
 
@@ -429,7 +447,7 @@
     {
         URI uri = baseServerUri.resolve("/capture-request-headers");
 
-        try (BlockheadClient client = new BlockheadClient(uri))
+        try (IBlockheadClient client = new BlockheadClient(uri))
         {
             client.addHeader("X-Dummy: Bogus\r\n");
             client.connect();
@@ -449,7 +467,7 @@
         URI uri = baseServerUri.resolve("/unique-user-props");
 
         // First request
-        try (BlockheadClient client = new BlockheadClient(uri))
+        try (IBlockheadClient client = new BlockheadClient(uri))
         {
             client.connect();
             client.sendStandardRequest();
@@ -462,7 +480,7 @@
         }
         
         // Second request
-        try (BlockheadClient client = new BlockheadClient(uri))
+        try (IBlockheadClient client = new BlockheadClient(uri))
         {
             client.connect();
             client.sendStandardRequest();
@@ -486,7 +504,7 @@
         URI uri = baseServerUri.resolve("/addr");
 
         // First request
-        try (BlockheadClient client = new BlockheadClient(uri))
+        try (IBlockheadClient client = new BlockheadClient(uri))
         {
             client.connect();
             client.sendStandardRequest();
@@ -513,6 +531,7 @@
     
     /**
      * Test of Sec-WebSocket-Protocol, as seen in RFC-6455, 1 protocol
+     * @throws Exception on test failure
      */
     @Test
     public void testProtocol_Single() throws Exception
@@ -520,7 +539,7 @@
         URI uri = baseServerUri.resolve("/protocols");
         ProtocolsConfigurator.seenProtocols.set(null);
 
-        try (BlockheadClient client = new BlockheadClient(uri))
+        try (IBlockheadClient client = new BlockheadClient(uri))
         {
             client.addHeader("Sec-WebSocket-Protocol: echo\r\n");
             client.connect();
@@ -536,6 +555,7 @@
     
     /**
      * Test of Sec-WebSocket-Protocol, as seen in RFC-6455, 3 protocols
+     * @throws Exception on test failure
      */
     @Test
     public void testProtocol_Triple() throws Exception
@@ -543,7 +563,7 @@
         URI uri = baseServerUri.resolve("/protocols");
         ProtocolsConfigurator.seenProtocols.set(null);
 
-        try (BlockheadClient client = new BlockheadClient(uri))
+        try (IBlockheadClient client = new BlockheadClient(uri))
         {
             client.addHeader("Sec-WebSocket-Protocol: echo, chat, status\r\n");
             client.connect();
@@ -559,6 +579,7 @@
     
     /**
      * Test of Sec-WebSocket-Protocol, using all lowercase header
+     * @throws Exception on test failure
      */
     @Test
     public void testProtocol_LowercaseHeader() throws Exception
@@ -566,7 +587,7 @@
         URI uri = baseServerUri.resolve("/protocols");
         ProtocolsConfigurator.seenProtocols.set(null);
 
-        try (BlockheadClient client = new BlockheadClient(uri))
+        try (IBlockheadClient client = new BlockheadClient(uri))
         {
             client.addHeader("sec-websocket-protocol: echo, chat, status\r\n");
             client.connect();
@@ -582,6 +603,7 @@
     
     /**
      * Test of Sec-WebSocket-Protocol, using non-spec case header
+     * @throws Exception on test failure
      */
     @Test
     public void testProtocol_AltHeaderCase() throws Exception
@@ -589,7 +611,7 @@
         URI uri = baseServerUri.resolve("/protocols");
         ProtocolsConfigurator.seenProtocols.set(null);
 
-        try (BlockheadClient client = new BlockheadClient(uri))
+        try (IBlockheadClient client = new BlockheadClient(uri))
         {
             client.addHeader("Sec-Websocket-Protocol: echo, chat, status\r\n");
             client.connect();
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/DummyConnection.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/DummyConnection.java
deleted file mode 100644
index 020c80c..0000000
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/DummyConnection.java
+++ /dev/null
@@ -1,162 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.jsr356.server;
-
-import java.net.InetSocketAddress;
-import java.util.concurrent.Executor;
-
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.websocket.api.BatchMode;
-import org.eclipse.jetty.websocket.api.SuspendToken;
-import org.eclipse.jetty.websocket.api.WebSocketPolicy;
-import org.eclipse.jetty.websocket.api.WriteCallback;
-import org.eclipse.jetty.websocket.api.extensions.Frame;
-import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
-import org.eclipse.jetty.websocket.common.LogicalConnection;
-import org.eclipse.jetty.websocket.common.WebSocketSession;
-import org.eclipse.jetty.websocket.common.io.IOState;
-
-public class DummyConnection implements LogicalConnection
-{
-    private static final Logger LOG = Log.getLogger(DummyConnection.class);
-    private IOState iostate;
-
-    public DummyConnection()
-    {
-        this.iostate = new IOState();
-    }
-
-    @Override
-    public void close()
-    {
-    }
-
-    @Override
-    public void close(int statusCode, String reason)
-    {
-    }
-
-    @Override
-    public void disconnect()
-    {
-    }
-
-    @Override
-    public ByteBufferPool getBufferPool()
-    {
-        return null;
-    }
-
-    @Override
-    public Executor getExecutor()
-    {
-        return null;
-    }
-
-    @Override
-    public long getIdleTimeout()
-    {
-        return 0;
-    }
-
-    @Override
-    public IOState getIOState()
-    {
-        return this.iostate;
-    }
-
-    @Override
-    public InetSocketAddress getLocalAddress()
-    {
-        return null;
-    }
-
-    @Override
-    public long getMaxIdleTimeout()
-    {
-        return 0;
-    }
-
-    @Override
-    public WebSocketPolicy getPolicy()
-    {
-        return null;
-    }
-
-    @Override
-    public InetSocketAddress getRemoteAddress()
-    {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public WebSocketSession getSession()
-    {
-        return null;
-    }
-
-    @Override
-    public boolean isOpen()
-    {
-        return false;
-    }
-
-    @Override
-    public boolean isReading()
-    {
-        return false;
-    }
-
-    @Override
-    public void outgoingFrame(Frame frame, WriteCallback callback, BatchMode batchMode)
-    {
-        callback.writeSuccess();
-    }
-
-    @Override
-    public void resume()
-    {
-    }
-
-    @Override
-    public void setMaxIdleTimeout(long ms)
-    {
-    }
-
-    @Override
-    public void setNextIncomingFrames(IncomingFrames incoming)
-    {
-        if (LOG.isDebugEnabled())
-            LOG.debug("setNextIncomingFrames({})",incoming);
-    }
-
-    @Override
-    public void setSession(WebSocketSession session)
-    {
-    }
-
-    @Override
-    public SuspendToken suspend()
-    {
-        return null;
-    }
-}
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/DummyCreator.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/DummyCreator.java
deleted file mode 100644
index e8e65a6..0000000
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/DummyCreator.java
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.jsr356.server;
-
-import org.eclipse.jetty.websocket.server.MappedWebSocketCreator;
-import org.eclipse.jetty.websocket.server.pathmap.PathMappings;
-import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
-
-public class DummyCreator implements MappedWebSocketCreator
-{
-    @Override
-    public void addMapping(String spec, WebSocketCreator creator)
-    {
-        /* do nothing */
-    }
-
-    @Override
-    public void addMapping(org.eclipse.jetty.websocket.server.pathmap.PathSpec spec, WebSocketCreator creator)
-    {
-        /* do nothing */
-    }
-    
-    @Override
-    public void addMapping(org.eclipse.jetty.http.pathmap.PathSpec spec, WebSocketCreator creator)
-    {
-        /* do nothing */
-    }
-    
-    @Override
-    public PathMappings<WebSocketCreator> getMappings()
-    {
-        return null;
-    }
-
-    @Override
-    public WebSocketCreator getMapping(String spec)
-    {
-        return null;
-    }
-
-    @Override
-    public boolean removeMapping(String spec)
-    {
-        return false;
-    }
-}
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/IdleTimeoutTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/IdleTimeoutTest.java
index 6f0576c..befc7e1 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/IdleTimeoutTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/IdleTimeoutTest.java
@@ -18,6 +18,10 @@
 
 package org.eclipse.jetty.websocket.jsr356.server;
 
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
 import java.io.IOException;
 import java.net.URI;
 import java.util.Queue;
@@ -30,10 +34,12 @@
 import org.eclipse.jetty.toolchain.test.TestingDir;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.eclipse.jetty.webapp.WebAppContext;
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.client.WebSocketClient;
 import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
+import org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents;
 import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.IdleTimeoutContextListener;
 import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.OnOpenIdleTimeoutEndpoint;
 import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.OnOpenIdleTimeoutSocket;
@@ -42,10 +48,6 @@
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
 public class IdleTimeoutTest
 {
     private static final Logger LOG = Log.getLogger(IdleTimeoutTest.class);
@@ -127,8 +129,11 @@
     @Test
     public void testAnnotated() throws Exception
     {
-        URI uri = server.getServerBaseURI();
-        assertConnectionTimeout(uri.resolve("idle-onopen-socket"));
+        try(StacklessLogging stackless = new StacklessLogging(JsrEvents.class))
+        {
+            URI uri = server.getServerBaseURI();
+            assertConnectionTimeout(uri.resolve("idle-onopen-socket"));
+        }
     }
 
     @Test
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/JettyServerEndpointConfiguratorTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/JettyServerEndpointConfiguratorTest.java
index d003f99..226bfcf 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/JettyServerEndpointConfiguratorTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/JettyServerEndpointConfiguratorTest.java
@@ -48,6 +48,6 @@
 
         ServerEndpointConfig.Configurator configr = iter.next();
         assertThat("Configurator",configr,notNullValue());
-        assertThat("COnfigurator type",configr,instanceOf(BasicServerEndpointConfigurator.class));
+        assertThat("Configurator type",configr,instanceOf(ContainerDefaultConfigurator.class));
     }
 }
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/JsrBatchModeTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/JsrBatchModeTest.java
index ec80c8e..7d86059 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/JsrBatchModeTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/JsrBatchModeTest.java
@@ -22,6 +22,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
+
 import javax.websocket.ClientEndpointConfig;
 import javax.websocket.ContainerProvider;
 import javax.websocket.Endpoint;
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/MemoryUsageTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/MemoryUsageTest.java
index 73ad49b..f64f876 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/MemoryUsageTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/MemoryUsageTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.jsr356.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.lessThan;
 
 import java.lang.management.ManagementFactory;
 import java.lang.management.MemoryMXBean;
@@ -114,8 +114,8 @@
         long heapUsed = heapAfter.getUsed() - heapBefore.getUsed();
         long nonHeapUsed = nonHeapAfter.getUsed() - nonHeapBefore.getUsed();
 
-//        System.out.println("heapUsed = " + heapUsed);
-//        System.out.println("nonHeapUsed = " + nonHeapUsed);
+        System.out.println("heapUsed = " + heapUsed);
+        System.out.println("nonHeapUsed = " + nonHeapUsed);
 //        new CountDownLatch(1).await();
 
         // Assume no more than 25 KiB per session pair (client and server).
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnPartialTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnPartialTest.java
index 57cc728..673f0c4 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnPartialTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnPartialTest.java
@@ -18,9 +18,13 @@
 
 package org.eclipse.jetty.websocket.jsr356.server;
 
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
+
 import javax.websocket.server.ServerEndpoint;
 import javax.websocket.server.ServerEndpointConfig;
 
@@ -31,6 +35,9 @@
 import org.eclipse.jetty.websocket.common.events.EventDriverImpl;
 import org.eclipse.jetty.websocket.common.frames.ContinuationFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
+import org.eclipse.jetty.websocket.common.test.DummyConnection;
 import org.eclipse.jetty.websocket.jsr356.ClientContainer;
 import org.eclipse.jetty.websocket.jsr356.JsrSession;
 import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
@@ -41,9 +48,6 @@
 import org.junit.Test;
 import org.junit.rules.TestName;
 
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-
 public class OnPartialTest
 {
     @Rule
@@ -65,8 +69,11 @@
         Class<?> endpoint = websocket.getClass();
         ServerEndpoint anno = endpoint.getAnnotation(ServerEndpoint.class);
         Assert.assertThat("Endpoint: " + endpoint + " should be annotated with @ServerEndpoint",anno,notNullValue());
-        ServerEndpointConfig config = new BasicServerEndpointConfig(endpoint,"/");
-        AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(endpoint,config);
+        
+        WebSocketContainerScope containerScope = new SimpleContainerScope(policy);
+        
+        ServerEndpointConfig config = new BasicServerEndpointConfig(containerScope,endpoint,"/");
+        AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(containerScope,endpoint,config);
         AnnotatedEndpointScanner<ServerEndpoint, ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(metadata);
         scanner.scan();
         EndpointInstance ei = new EndpointInstance(websocket,config,metadata);
@@ -78,9 +85,12 @@
         URI requestURI = URI.create("ws://localhost/" + id);
         DummyConnection connection = new DummyConnection();
         ClientContainer container = new ClientContainer();
+        container.start();
+        
         @SuppressWarnings("resource")
-        JsrSession session = new JsrSession(requestURI,driver,connection,container,id);
+        JsrSession session = new JsrSession(container,id,requestURI,driver,connection);
         session.setPolicy(policy);
+        session.start();
         session.open();
         return driver;
     }
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/PingPongTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/PingPongTest.java
index 36fc17b..36e4940 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/PingPongTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/PingPongTest.java
@@ -18,7 +18,8 @@
 
 package org.eclipse.jetty.websocket.jsr356.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsString;
 
 import java.io.File;
 import java.net.URI;
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ServerAnnotatedEndpointScanner_GoodSignaturesTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ServerAnnotatedEndpointScanner_GoodSignaturesTest.java
index afa3dd4..7a712d6 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ServerAnnotatedEndpointScanner_GoodSignaturesTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ServerAnnotatedEndpointScanner_GoodSignaturesTest.java
@@ -34,6 +34,9 @@
 import javax.websocket.server.ServerEndpoint;
 import javax.websocket.server.ServerEndpointConfig;
 
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
 import org.eclipse.jetty.websocket.jsr356.annotations.JsrCallable;
 import org.eclipse.jetty.websocket.jsr356.server.samples.BasicBinaryMessageByteBufferSocket;
@@ -182,7 +185,8 @@
     @Test
     public void testScan_Basic() throws Exception
     {
-        AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(testcase.pojo,null);
+        WebSocketContainerScope container = new SimpleContainerScope(WebSocketPolicy.newClientPolicy());
+        AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(container,testcase.pojo,null);
         AnnotatedEndpointScanner<ServerEndpoint, ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(metadata);
         scanner.scan();
 
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ServerAnnotatedEndpointScanner_InvalidSignaturesTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ServerAnnotatedEndpointScanner_InvalidSignaturesTest.java
index a8e1714..71bc73e 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ServerAnnotatedEndpointScanner_InvalidSignaturesTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ServerAnnotatedEndpointScanner_InvalidSignaturesTest.java
@@ -18,10 +18,13 @@
 
 package org.eclipse.jetty.websocket.jsr356.server;
 
+import static org.hamcrest.Matchers.containsString;
+
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+
 import javax.websocket.DeploymentException;
 import javax.websocket.OnClose;
 import javax.websocket.OnError;
@@ -31,7 +34,10 @@
 
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
 import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidCloseIntSocket;
 import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidErrorErrorSocket;
@@ -46,8 +52,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static org.hamcrest.Matchers.containsString;
-
 /**
  * Test {@link AnnotatedEndpointScanner} against various simple, 1 method {@link ServerEndpoint} annotated classes with invalid signatures.
  */
@@ -93,7 +97,8 @@
     @Test
     public void testScan_InvalidSignature() throws DeploymentException
     {
-        AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(pojo,null);
+        WebSocketContainerScope container = new SimpleContainerScope(WebSocketPolicy.newClientPolicy());
+        AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(container,pojo,null);
         AnnotatedEndpointScanner<ServerEndpoint,ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(metadata);
 
         try
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTest.java
index 1455afb..2d68529 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.jsr356.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.net.URI;
 import java.util.ArrayList;
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTrackingTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTrackingTest.java
new file mode 100644
index 0000000..2fdac78
--- /dev/null
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTrackingTest.java
@@ -0,0 +1,193 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.jsr356.server;
+
+import java.net.URI;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.websocket.CloseReason;
+import javax.websocket.Endpoint;
+import javax.websocket.EndpointConfig;
+import javax.websocket.OnMessage;
+import javax.websocket.Session;
+import javax.websocket.server.ServerEndpoint;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.eclipse.jetty.websocket.common.WebSocketSession;
+import org.eclipse.jetty.websocket.jsr356.ClientContainer;
+import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
+import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SessionTrackingTest
+{
+    private Server server;
+    private ServerContainer serverContainer;
+    private WebSocketServerFactory wsServerFactory;
+    private URI serverURI;
+
+    @Before
+    public void startServer() throws Exception
+    {
+        QueuedThreadPool serverThreads = new QueuedThreadPool();
+        serverThreads.setName("server");
+        server = new Server(serverThreads);
+        ServerConnector serverConnector = new ServerConnector(server);
+        serverConnector.setPort(0);
+        server.addConnector(serverConnector);
+        ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
+        servletContextHandler.setContextPath("/");
+        server.setHandler(servletContextHandler);
+
+        serverContainer = WebSocketServerContainerInitializer.configureContext(servletContextHandler);
+        serverContainer.addEndpoint(EchoSocket.class);
+
+        wsServerFactory = serverContainer.getWebSocketServerFactory();
+
+        server.start();
+
+        serverURI = new URI("ws://localhost:" + serverConnector.getLocalPort());
+    }
+
+    @After
+    public void stopServer() throws Exception
+    {
+        if (server != null)
+            server.stop();
+    }
+
+    @Test
+    public void testAddRemoveSessions() throws Exception
+    {
+        // Create Client
+        ClientContainer clientContainer = new ClientContainer();
+        QueuedThreadPool clientThreads = new QueuedThreadPool();
+        clientThreads.setName("client");
+        clientContainer.getClient().setExecutor(clientThreads);
+        try
+        {
+            CountDownLatch openedLatch = new CountDownLatch(2);
+            CountDownLatch closedLatch = new CountDownLatch(2);
+            wsServerFactory.addSessionListener(new WebSocketSession.Listener()
+            {
+                @Override
+                public void onOpened(WebSocketSession session)
+                {
+                    openedLatch.countDown();
+                }
+
+                @Override
+                public void onClosed(WebSocketSession session)
+                {
+                    closedLatch.countDown();
+                }
+            });
+
+            clientContainer.start();
+
+            // Establish connections
+            ClientSocket cli1 = new ClientSocket();
+            clientContainer.connectToServer(cli1, serverURI.resolve("/test"));
+            cli1.waitForOpen(1, TimeUnit.SECONDS);
+
+            // Establish new connection
+            ClientSocket cli2 = new ClientSocket();
+            clientContainer.connectToServer(cli2, serverURI.resolve("/test"));
+            cli2.waitForOpen(1, TimeUnit.SECONDS);
+
+            openedLatch.await(5, TimeUnit.SECONDS);
+            assertServerOpenConnectionCount(2);
+
+            // Establish close both connections
+            cli1.session.close();
+            cli2.session.close();
+
+            cli1.waitForClose(1, TimeUnit.SECONDS);
+            cli2.waitForClose(1, TimeUnit.SECONDS);
+
+            closedLatch.await(5, TimeUnit.SECONDS);
+            assertServerOpenConnectionCount(0);
+        }
+        finally
+        {
+            clientContainer.stop();
+        }
+    }
+
+    private void assertServerOpenConnectionCount(int expectedCount)
+    {
+        Set<Session> sessions = serverContainer.getOpenSessions();
+        int openCount = 0;
+        for (Session session : sessions)
+        {
+            Assert.assertThat("Session.isopen: " + session, session.isOpen(), Matchers.is(true));
+            openCount++;
+        }
+        Assert.assertThat("Open Session Count", openCount, Matchers.is(expectedCount));
+    }
+
+    private static class ClientSocket extends Endpoint
+    {
+        private Session session;
+        private CountDownLatch openLatch = new CountDownLatch(1);
+        private CountDownLatch closeLatch = new CountDownLatch(1);
+
+        @Override
+        public void onOpen(Session session, EndpointConfig config)
+        {
+            this.session = session;
+            openLatch.countDown();
+        }
+
+        @Override
+        public void onClose(Session session, CloseReason closeReason)
+        {
+            closeLatch.countDown();
+        }
+
+        public void waitForOpen(long timeout, TimeUnit unit) throws InterruptedException
+        {
+            Assert.assertThat("ClientSocket opened", openLatch.await(timeout, unit), Matchers.is(true));
+        }
+
+        public void waitForClose(long timeout, TimeUnit unit) throws InterruptedException
+        {
+            Assert.assertThat("ClientSocket opened", closeLatch.await(timeout, unit), Matchers.is(true));
+        }
+    }
+
+    @ServerEndpoint("/test")
+    public static class EchoSocket
+    {
+        @OnMessage
+        public String echo(String msg)
+        {
+            return msg;
+        }
+    }
+}
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/StreamTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/StreamTest.java
index 44fcd5b..8b7ac25 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/StreamTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/StreamTest.java
@@ -18,6 +18,9 @@
 
 package org.eclipse.jetty.websocket.jsr356.server;
 
+import static org.hamcrest.Matchers.equalToIgnoringCase;
+import static org.hamcrest.Matchers.is;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -25,13 +28,9 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URI;
-import java.security.DigestOutputStream;
-import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import javax.websocket.ClientEndpoint;
 import javax.websocket.CloseReason;
@@ -60,7 +59,7 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
-import org.eclipse.jetty.websocket.common.util.Hex;
+import org.eclipse.jetty.websocket.common.util.Sha1Sum;
 import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -68,9 +67,6 @@
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.equalToIgnoringCase;
-import static org.hamcrest.Matchers.is;
-
 public class StreamTest
 {
     private static final Logger LOG = Log.getLogger(StreamTest.class);
@@ -176,33 +172,12 @@
         Assert.assertThat("Path should exist: " + file,file.exists(),is(true));
         Assert.assertThat("Path should not be a directory:" + file,file.isDirectory(),is(false));
 
-        String expectedSha1 = loadExpectedSha1Sum(sha1File);
-        String actualSha1 = calculateSha1Sum(file);
+        String expectedSha1 = Sha1Sum.loadSha1(sha1File);
+        String actualSha1 = Sha1Sum.calculate(file);
 
         Assert.assertThat("SHA1Sum of content: " + file,expectedSha1,equalToIgnoringCase(actualSha1));
     }
 
-    private String calculateSha1Sum(File file) throws IOException, NoSuchAlgorithmException
-    {
-        MessageDigest digest = MessageDigest.getInstance("SHA1");
-        try (FileInputStream fis = new FileInputStream(file);
-                NoOpOutputStream noop = new NoOpOutputStream();
-                DigestOutputStream digester = new DigestOutputStream(noop,digest))
-        {
-            IO.copy(fis,digester);
-            return Hex.asHex(digest.digest());
-        }
-    }
-
-    private String loadExpectedSha1Sum(File sha1File) throws IOException
-    {
-        String contents = IO.readToString(sha1File);
-        Pattern pat = Pattern.compile("^[0-9A-Fa-f]*");
-        Matcher mat = pat.matcher(contents);
-        Assert.assertTrue("Should have found HEX code in SHA1 file: " + sha1File,mat.find());
-        return mat.group();
-    }
-
     @ClientEndpoint
     public static class ClientSocket
     {
@@ -317,32 +292,4 @@
             t.printStackTrace(System.err);
         }
     }
-
-    private static class NoOpOutputStream extends OutputStream
-    {
-        @Override
-        public void write(byte[] b) throws IOException
-        {
-        }
-
-        @Override
-        public void write(byte[] b, int off, int len) throws IOException
-        {
-        }
-
-        @Override
-        public void flush() throws IOException
-        {
-        }
-
-        @Override
-        public void close() throws IOException
-        {
-        }
-
-        @Override
-        public void write(int b) throws IOException
-        {
-        }
-    }
 }
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/TextStreamTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/TextStreamTest.java
index d87a86b..1f940a9 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/TextStreamTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/TextStreamTest.java
@@ -25,6 +25,7 @@
 import java.util.Random;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+
 import javax.websocket.ClientEndpoint;
 import javax.websocket.ContainerProvider;
 import javax.websocket.OnMessage;
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/TrackingSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/TrackingSocket.java
index 01688f3..34aed2c 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/TrackingSocket.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/TrackingSocket.java
@@ -18,8 +18,13 @@
 
 package org.eclipse.jetty.websocket.jsr356.server;
 
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+
 import javax.websocket.CloseReason;
 import javax.websocket.CloseReason.CloseCode;
 
@@ -28,10 +33,6 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.junit.Assert;
 
-import static org.hamcrest.Matchers.greaterThanOrEqualTo;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-
 /**
  * Abstract base socket used for tracking state and events within the socket for testing reasons.
  */
@@ -99,7 +100,7 @@
 
     public void assertWasOpened() throws InterruptedException
     {
-        Assert.assertThat("Was Opened",openLatch.await(500,TimeUnit.MILLISECONDS),is(true));
+        Assert.assertThat("Was Opened",openLatch.await(30000,TimeUnit.MILLISECONDS),is(true));
     }
 
     public void clear()
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/WSServer.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/WSServer.java
index a5e7f2b..2dcac78 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/WSServer.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/WSServer.java
@@ -67,7 +67,7 @@
 
     public WSServer(TestingDir testdir, String contextName)
     {
-        this(testdir.getDir(),contextName);
+        this(testdir.getPath().toFile(),contextName);
     }
 
     public WSServer(File testdir, String contextName)
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserDebugTool.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserDebugTool.java
index 6682303..5363a0f 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserDebugTool.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserDebugTool.java
@@ -18,9 +18,16 @@
 
 package org.eclipse.jetty.websocket.jsr356.server.browser;
 
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Objects;
+
 import javax.servlet.ServletException;
 import javax.websocket.DeploymentException;
 
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.servlet.DefaultServlet;
@@ -28,6 +35,7 @@
 import org.eclipse.jetty.servlet.ServletHolder;
 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.websocket.jsr356.server.ServerContainer;
 import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
 
@@ -76,17 +84,27 @@
         server.join();
     }
 
-    private void setupServer(int port) throws DeploymentException, ServletException
+    private void setupServer(int port) throws DeploymentException, ServletException, URISyntaxException, MalformedURLException
     {
         server = new Server();
-        ServerConnector connector = new ServerConnector(server);
+        
+        HttpConfiguration httpConf = new HttpConfiguration();
+        httpConf.setSendServerVersion(true);
+        
+        ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConf));
         connector.setPort(port);
         server.addConnector(connector);
 
+        String resourcePath = "/jsr-browser-debug-tool/index.html";
+        URL urlStatics = JsrBrowserDebugTool.class.getResource(resourcePath);
+        Objects.requireNonNull(urlStatics,"Unable to find " + resourcePath + " in classpath");
+        String urlBase = urlStatics.toURI().toASCIIString().replaceFirst("/[^/]*$","/");
+
         ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
         context.setContextPath("/");
+        context.setBaseResource(Resource.newResource(urlBase));
+
         ServletHolder holder = context.addServlet(DefaultServlet.class,"/");
-        holder.setInitParameter("resourceBase","src/test/resources/jsr-browser-debug-tool");
         holder.setInitParameter("dirAllowed","true");
         server.setHandler(context);
 
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserSocket.java
index d8739e5..8c8056f 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserSocket.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserSocket.java
@@ -23,6 +23,8 @@
 import java.util.Calendar;
 import java.util.Locale;
 import java.util.Random;
+import java.util.Set;
+
 import javax.websocket.CloseReason;
 import javax.websocket.OnClose;
 import javax.websocket.OnMessage;
@@ -128,6 +130,14 @@
                     {
                         writeMessage("Client Sec-WebSocket-Extensions: " + this.requestedExtensions);
                     }
+
+                    Set<Session> openSessions = session.getOpenSessions();
+                    writeMessage("OpenSessions.size() = " + openSessions.size());
+                    int i = 0;
+                    for (Session open : openSessions)
+                    {
+                        writeMessage("  OpenSession[%d] = %s", i++, open);
+                    }
                     break;
                 }
                 case "many":
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/PathMappingsTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/PathMappingsTest.java
deleted file mode 100644
index 07c1d11..0000000
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/PathMappingsTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.jsr356.server.pathmap;
-
-import static org.hamcrest.Matchers.notNullValue;
-
-import org.eclipse.jetty.websocket.server.pathmap.PathMappings;
-import org.eclipse.jetty.websocket.server.pathmap.PathMappings.MappedResource;
-import org.eclipse.jetty.websocket.server.pathmap.ServletPathSpec;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class PathMappingsTest
-{
-    private void assertMatch(PathMappings<String> pathmap, String path, String expectedValue)
-    {
-        String msg = String.format(".getMatch(\"%s\")",path);
-        MappedResource<String> match = pathmap.getMatch(path);
-        Assert.assertThat(msg,match,notNullValue());
-        String actualMatch = match.getResource();
-        Assert.assertEquals(msg,expectedValue,actualMatch);
-    }
-
-    public void dumpMappings(PathMappings<String> p)
-    {
-        for (MappedResource<String> res : p)
-        {
-            System.out.printf("  %s%n",res);
-        }
-    }
-
-    /**
-     * Test the match order rules with a mixed Servlet and WebSocket path specs
-     * <p>
-     * <ul>
-     * <li>Exact match</li>
-     * <li>Longest prefix match</li>
-     * <li>Longest suffix match</li>
-     * </ul>
-     */
-    @Test
-    public void testMixedMatchOrder()
-    {
-        PathMappings<String> p = new PathMappings<>();
-
-        p.put(new ServletPathSpec("/"),"default");
-        p.put(new ServletPathSpec("/animal/bird/*"),"birds");
-        p.put(new ServletPathSpec("/animal/fish/*"),"fishes");
-        p.put(new ServletPathSpec("/animal/*"),"animals");
-        p.put(new WebSocketPathSpec("/animal/{type}/{name}/chat"),"animalChat");
-        p.put(new WebSocketPathSpec("/animal/{type}/{name}/cam"),"animalCam");
-        p.put(new WebSocketPathSpec("/entrance/cam"),"entranceCam");
-
-        // dumpMappings(p);
-
-        assertMatch(p,"/animal/bird/eagle","birds");
-        assertMatch(p,"/animal/fish/bass/sea","fishes");
-        assertMatch(p,"/animal/peccary/javalina/evolution","animals");
-        assertMatch(p,"/","default");
-        assertMatch(p,"/animal/bird/eagle/chat","animalChat");
-        assertMatch(p,"/animal/bird/penguin/chat","animalChat");
-        assertMatch(p,"/animal/fish/trout/cam","animalCam");
-        assertMatch(p,"/entrance/cam","entranceCam");
-    }
-
-    /**
-     * Test the match order rules imposed by the WebSocket API (JSR-356)
-     * <p>
-     * <ul>
-     * <li>Exact match</li>
-     * <li>Longest prefix match</li>
-     * <li>Longest suffix match</li>
-     * </ul>
-     */
-    @Test
-    public void testWebsocketMatchOrder()
-    {
-        PathMappings<String> p = new PathMappings<>();
-
-        p.put(new WebSocketPathSpec("/a/{var}/c"),"endpointA");
-        p.put(new WebSocketPathSpec("/a/b/c"),"endpointB");
-        p.put(new WebSocketPathSpec("/a/{var1}/{var2}"),"endpointC");
-        p.put(new WebSocketPathSpec("/{var1}/d"),"endpointD");
-        p.put(new WebSocketPathSpec("/b/{var2}"),"endpointE");
-
-        // dumpMappings(p);
-
-        assertMatch(p,"/a/b/c","endpointB");
-        assertMatch(p,"/a/d/c","endpointA");
-        assertMatch(p,"/a/x/y","endpointC");
-
-        assertMatch(p,"/b/d","endpointE");
-    }
-}
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/WebSocketPathSpecBadSpecsTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/WebSocketPathSpecBadSpecsTest.java
deleted file mode 100644
index a4e9f70..0000000
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/WebSocketPathSpecBadSpecsTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.jsr356.server.pathmap;
-
-import static org.junit.Assert.fail;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-/**
- * Tests for bad path specs on ServerEndpoint Path Param / URI Template
- */
-@RunWith(Parameterized.class)
-public class WebSocketPathSpecBadSpecsTest
-{
-    private static void bad(List<String[]> data, String str)
-    {
-        data.add(new String[]
-        { str });
-    }
-
-    @Parameters
-    public static Collection<String[]> data()
-    {
-        List<String[]> data = new ArrayList<>();
-        bad(data,"/a/b{var}"); // bad syntax - variable does not encompass whole path segment
-        bad(data,"a/{var}"); // bad syntax - no start slash
-        bad(data,"/a/{var/b}"); // path segment separator in variable name
-        bad(data,"/{var}/*"); // bad syntax - no globs allowed
-        bad(data,"/{var}.do"); // bad syntax - variable does not encompass whole path segment
-        bad(data,"/a/{var*}"); // use of glob character not allowed in variable name
-        bad(data,"/a/{}"); // bad syntax - no variable name
-        // MIGHT BE ALLOWED bad(data,"/a/{---}"); // no alpha in variable name
-        bad(data,"{var}"); // bad syntax - no start slash
-        bad(data,"/a/{my special variable}"); // bad syntax - space in variable name
-        bad(data,"/a/{var}/{var}"); // variable name duplicate
-        // MIGHT BE ALLOWED bad(data,"/a/{var}/{Var}/{vAR}"); // variable name duplicated (diff case)
-        bad(data,"/a/../../../{var}"); // path navigation not allowed
-        bad(data,"/a/./{var}"); // path navigation not allowed
-        bad(data,"/a//{var}"); // bad syntax - double path slash (no path segment)
-        return data;
-    }
-
-    private String pathSpec;
-
-    public WebSocketPathSpecBadSpecsTest(String pathSpec)
-    {
-        this.pathSpec = pathSpec;
-    }
-
-    @Test
-    public void testBadPathSpec()
-    {
-        try
-        {
-            new WebSocketPathSpec(this.pathSpec);
-            fail("Expected IllegalArgumentException for a bad PathParam pathspec on: " + pathSpec);
-        }
-        catch (IllegalArgumentException e)
-        {
-            // expected path
-            System.out.println(e.getMessage());
-        }
-    }
-}
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/WebSocketPathSpecTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/WebSocketPathSpecTest.java
deleted file mode 100644
index 6bd8b71..0000000
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/WebSocketPathSpecTest.java
+++ /dev/null
@@ -1,286 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.jsr356.server.pathmap;
-
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-import java.util.Map;
-
-import org.eclipse.jetty.websocket.server.pathmap.PathSpec;
-import org.eclipse.jetty.websocket.server.pathmap.PathSpecGroup;
-import org.junit.Test;
-
-/**
- * Tests for ServerEndpoint Path Param / URI Template Path Specs
- */
-public class WebSocketPathSpecTest
-{
-    private void assertDetectedVars(WebSocketPathSpec spec, String... expectedVars)
-    {
-        String prefix = String.format("Spec(\"%s\")",spec.getPathSpec());
-        assertEquals(prefix + ".variableCount",expectedVars.length,spec.getVariableCount());
-        assertEquals(prefix + ".variable.length",expectedVars.length,spec.getVariables().length);
-        for (int i = 0; i < expectedVars.length; i++)
-        {
-            assertEquals(String.format("%s.variable[%d]",prefix,i),expectedVars[i],spec.getVariables()[i]);
-        }
-    }
-
-    private void assertMatches(PathSpec spec, String path)
-    {
-        String msg = String.format("Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path);
-        assertThat(msg,spec.matches(path),is(true));
-    }
-
-    private void assertNotMatches(PathSpec spec, String path)
-    {
-        String msg = String.format("!Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path);
-        assertThat(msg,spec.matches(path),is(false));
-    }
-
-    @Test
-    public void testDefaultPathSpec()
-    {
-        WebSocketPathSpec spec = new WebSocketPathSpec("/");
-        assertEquals("Spec.pathSpec","/",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",1,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.EXACT,spec.getGroup());
-
-        assertEquals("Spec.variableCount",0,spec.getVariableCount());
-        assertEquals("Spec.variable.length",0,spec.getVariables().length);
-    }
-
-    @Test
-    public void testExactOnePathSpec()
-    {
-        WebSocketPathSpec spec = new WebSocketPathSpec("/a");
-        assertEquals("Spec.pathSpec","/a",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/a$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",1,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.EXACT,spec.getGroup());
-        
-        assertMatches(spec,"/a");
-        assertMatches(spec,"/a?type=other");
-        assertNotMatches(spec,"/a/b");
-        assertNotMatches(spec,"/a/");
-
-        assertEquals("Spec.variableCount",0,spec.getVariableCount());
-        assertEquals("Spec.variable.length",0,spec.getVariables().length);
-    }
-    
-    @Test
-    public void testExactPathSpec_TestWebapp()
-    {
-        WebSocketPathSpec spec = new WebSocketPathSpec("/javax.websocket/");
-        assertEquals("Spec.pathSpec","/javax.websocket/",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/javax\\.websocket/$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",1,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.EXACT,spec.getGroup());
-        
-        assertMatches(spec,"/javax.websocket/");
-        assertNotMatches(spec,"/javax.websocket");
-
-        assertEquals("Spec.variableCount",0,spec.getVariableCount());
-        assertEquals("Spec.variable.length",0,spec.getVariables().length);
-    }
-    
-    @Test
-    public void testExactTwoPathSpec()
-    {
-        WebSocketPathSpec spec = new WebSocketPathSpec("/a/b");
-        assertEquals("Spec.pathSpec","/a/b",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/a/b$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",2,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.EXACT,spec.getGroup());
-
-        assertEquals("Spec.variableCount",0,spec.getVariableCount());
-        assertEquals("Spec.variable.length",0,spec.getVariables().length);
-
-        assertMatches(spec,"/a/b");
-
-        assertNotMatches(spec,"/a/b/");
-        assertNotMatches(spec,"/a/");
-        assertNotMatches(spec,"/a/bb");
-    }
-
-    @Test
-    public void testMiddleVarPathSpec()
-    {
-        WebSocketPathSpec spec = new WebSocketPathSpec("/a/{var}/c");
-        assertEquals("Spec.pathSpec","/a/{var}/c",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/a/([^/]+)/c$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",3,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.MIDDLE_GLOB,spec.getGroup());
-
-        assertDetectedVars(spec,"var");
-
-        assertMatches(spec,"/a/b/c");
-        assertMatches(spec,"/a/zz/c");
-        assertMatches(spec,"/a/hello+world/c");
-        assertNotMatches(spec,"/a/bc");
-        assertNotMatches(spec,"/a/b/");
-        assertNotMatches(spec,"/a/b");
-
-        Map<String, String> mapped = spec.getPathParams("/a/b/c");
-        assertThat("Spec.pathParams",mapped,notNullValue());
-        assertThat("Spec.pathParams.size",mapped.size(),is(1));
-        assertEquals("Spec.pathParams[var]","b",mapped.get("var"));
-    }
-
-    @Test
-    public void testOneVarPathSpec()
-    {
-        WebSocketPathSpec spec = new WebSocketPathSpec("/a/{foo}");
-        assertEquals("Spec.pathSpec","/a/{foo}",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/a/([^/]+)$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",2,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.PREFIX_GLOB,spec.getGroup());
-
-        assertDetectedVars(spec,"foo");
-
-        assertMatches(spec,"/a/b");
-        assertNotMatches(spec,"/a/");
-        assertNotMatches(spec,"/a");
-
-        Map<String, String> mapped = spec.getPathParams("/a/b");
-        assertThat("Spec.pathParams",mapped,notNullValue());
-        assertThat("Spec.pathParams.size",mapped.size(),is(1));
-        assertEquals("Spec.pathParams[foo]","b",mapped.get("foo"));
-    }
-
-    @Test
-    public void testOneVarSuffixPathSpec()
-    {
-        WebSocketPathSpec spec = new WebSocketPathSpec("/{var}/b/c");
-        assertEquals("Spec.pathSpec","/{var}/b/c",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/([^/]+)/b/c$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",3,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.SUFFIX_GLOB,spec.getGroup());
-
-        assertDetectedVars(spec,"var");
-
-        assertMatches(spec,"/a/b/c");
-        assertMatches(spec,"/az/b/c");
-        assertMatches(spec,"/hello+world/b/c");
-        assertNotMatches(spec,"/a/bc");
-        assertNotMatches(spec,"/a/b/");
-        assertNotMatches(spec,"/a/b");
-
-        Map<String, String> mapped = spec.getPathParams("/a/b/c");
-        assertThat("Spec.pathParams",mapped,notNullValue());
-        assertThat("Spec.pathParams.size",mapped.size(),is(1));
-        assertEquals("Spec.pathParams[var]","a",mapped.get("var"));
-    }
-
-    @Test
-    public void testTwoVarComplexInnerPathSpec()
-    {
-        WebSocketPathSpec spec = new WebSocketPathSpec("/a/{var1}/c/{var2}/e");
-        assertEquals("Spec.pathSpec","/a/{var1}/c/{var2}/e",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/a/([^/]+)/c/([^/]+)/e$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",5,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.MIDDLE_GLOB,spec.getGroup());
-
-        assertDetectedVars(spec,"var1","var2");
-
-        assertMatches(spec,"/a/b/c/d/e");
-        assertNotMatches(spec,"/a/bc/d/e");
-        assertNotMatches(spec,"/a/b/d/e");
-        assertNotMatches(spec,"/a/b//d/e");
-
-        Map<String, String> mapped = spec.getPathParams("/a/b/c/d/e");
-        assertThat("Spec.pathParams",mapped,notNullValue());
-        assertThat("Spec.pathParams.size",mapped.size(),is(2));
-        assertEquals("Spec.pathParams[var1]","b",mapped.get("var1"));
-        assertEquals("Spec.pathParams[var2]","d",mapped.get("var2"));
-    }
-
-    @Test
-    public void testTwoVarComplexOuterPathSpec()
-    {
-        WebSocketPathSpec spec = new WebSocketPathSpec("/{var1}/b/{var2}/{var3}");
-        assertEquals("Spec.pathSpec","/{var1}/b/{var2}/{var3}",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/([^/]+)/b/([^/]+)/([^/]+)$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",4,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.MIDDLE_GLOB,spec.getGroup());
-
-        assertDetectedVars(spec,"var1","var2","var3");
-
-        assertMatches(spec,"/a/b/c/d");
-        assertNotMatches(spec,"/a/bc/d/e");
-        assertNotMatches(spec,"/a/c/d/e");
-        assertNotMatches(spec,"/a//d/e");
-
-        Map<String, String> mapped = spec.getPathParams("/a/b/c/d");
-        assertThat("Spec.pathParams",mapped,notNullValue());
-        assertThat("Spec.pathParams.size",mapped.size(),is(3));
-        assertEquals("Spec.pathParams[var1]","a",mapped.get("var1"));
-        assertEquals("Spec.pathParams[var2]","c",mapped.get("var2"));
-        assertEquals("Spec.pathParams[var3]","d",mapped.get("var3"));
-    }
-
-    @Test
-    public void testTwoVarPrefixPathSpec()
-    {
-        WebSocketPathSpec spec = new WebSocketPathSpec("/a/{var1}/{var2}");
-        assertEquals("Spec.pathSpec","/a/{var1}/{var2}",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/a/([^/]+)/([^/]+)$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",3,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.PREFIX_GLOB,spec.getGroup());
-
-        assertDetectedVars(spec,"var1","var2");
-
-        assertMatches(spec,"/a/b/c");
-        assertNotMatches(spec,"/a/bc");
-        assertNotMatches(spec,"/a/b/");
-        assertNotMatches(spec,"/a/b");
-
-        Map<String, String> mapped = spec.getPathParams("/a/b/c");
-        assertThat("Spec.pathParams",mapped,notNullValue());
-        assertThat("Spec.pathParams.size",mapped.size(),is(2));
-        assertEquals("Spec.pathParams[var1]","b",mapped.get("var1"));
-        assertEquals("Spec.pathParams[var2]","c",mapped.get("var2"));
-    }
-
-    @Test
-    public void testVarOnlyPathSpec()
-    {
-        WebSocketPathSpec spec = new WebSocketPathSpec("/{var1}");
-        assertEquals("Spec.pathSpec","/{var1}",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/([^/]+)$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",1,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.PREFIX_GLOB,spec.getGroup());
-
-        assertDetectedVars(spec,"var1");
-
-        assertMatches(spec,"/a");
-        assertNotMatches(spec,"/");
-        assertNotMatches(spec,"/a/b");
-        assertNotMatches(spec,"/a/b/c");
-
-        Map<String, String> mapped = spec.getPathParams("/a");
-        assertThat("Spec.pathParams",mapped,notNullValue());
-        assertThat("Spec.pathParams.size",mapped.size(),is(1));
-        assertEquals("Spec.pathParams[var1]","a",mapped.get("var1"));
-    }
-}
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidCloseIntSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidCloseIntSocket.java
index 1db5400..abe891c 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidCloseIntSocket.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidCloseIntSocket.java
@@ -28,6 +28,7 @@
 {
     /**
      * Invalid Close Method Declaration (parameter type int)
+     * @param statusCode the status code
      */
     @OnClose
     public void onClose(int statusCode)
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidErrorErrorSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidErrorErrorSocket.java
index 502c31e..db659a1 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidErrorErrorSocket.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidErrorErrorSocket.java
@@ -28,6 +28,7 @@
 {
     /**
      * Invalid Error Method Declaration (parameter type Error)
+     * @param error the error
      */
     @OnError
     public void onError(Error error)
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidErrorExceptionSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidErrorExceptionSocket.java
index f6d9732..74b632e 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidErrorExceptionSocket.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidErrorExceptionSocket.java
@@ -28,6 +28,7 @@
 {
     /**
      * Invalid Error Method Declaration (parameter type Exception)
+     * @param e the exception
      */
     @OnError
     public void onError(Exception e)
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidErrorIntSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidErrorIntSocket.java
index 653343f..4432d62 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidErrorIntSocket.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidErrorIntSocket.java
@@ -28,6 +28,7 @@
 {
     /**
      * Invalid Error Method Declaration (parameter type int)
+     * @param errorCount the error count
      */
     @OnError
     public void onError(int errorCount)
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidOpenCloseReasonSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidOpenCloseReasonSocket.java
index 55aed6d..221827d 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidOpenCloseReasonSocket.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidOpenCloseReasonSocket.java
@@ -29,6 +29,7 @@
 {
     /**
      * Invalid Open Method Declaration (parameter type CloseReason)
+     * @param reason the close reason
      */
     @OnOpen
     public void onOpen(CloseReason reason)
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidOpenIntSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidOpenIntSocket.java
index abc038b..65402b6 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidOpenIntSocket.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidOpenIntSocket.java
@@ -28,6 +28,7 @@
 {
     /**
      * Invalid Open Method Declaration (parameter type int)
+     * @param value the value
      */
     @OnOpen
     public void onOpen(int value)
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidOpenSessionIntSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidOpenSessionIntSocket.java
index 1f563ea..835ce76 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidOpenSessionIntSocket.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/InvalidOpenSessionIntSocket.java
@@ -29,6 +29,8 @@
 {
     /**
      * Invalid Open Method Declaration (parameter of type int)
+     * @param session the sesion
+     * @param count the count
      */
     @OnOpen
     public void onOpen(Session session, int count)
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/BasicEchoEndpointContextListener.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/BasicEchoEndpointContextListener.java
index 9177c29..d587153 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/BasicEchoEndpointContextListener.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/BasicEchoEndpointContextListener.java
@@ -27,7 +27,7 @@
 import org.eclipse.jetty.websocket.jsr356.server.samples.pong.PongMessageEndpoint;
 
 /**
- * Example of adding a server WebSocket (extending {@link Endpoint}) programmatically directly.
+ * Example of adding a server WebSocket (extending {@link javax.websocket.Endpoint}) programmatically directly.
  * <p>
  * NOTE: this shouldn't work as the endpoint has no path associated with it.
  */
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/BasicEchoSocketConfigContextListener.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/BasicEchoSocketConfigContextListener.java
index 4a629e0..255ec2c 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/BasicEchoSocketConfigContextListener.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/BasicEchoSocketConfigContextListener.java
@@ -25,7 +25,7 @@
 import javax.websocket.server.ServerEndpointConfig;
 
 /**
- * Example of adding a server socket (which extends {@link Endpoint}) programmatically via the {@link ServerContainer#addEndpoint(ServerEndpointConfig)}
+ * Example of adding a server socket (which extends {@link javax.websocket.Endpoint}) programmatically via the {@link ServerContainer#addEndpoint(ServerEndpointConfig)}
  */
 public class BasicEchoSocketConfigContextListener implements ServletContextListener
 {
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/LargeEchoDefaultSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/LargeEchoDefaultSocket.java
index 0ef99e7..f340f24 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/LargeEchoDefaultSocket.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/LargeEchoDefaultSocket.java
@@ -20,6 +20,7 @@
 
 import javax.websocket.OnMessage;
 import javax.websocket.Session;
+import javax.websocket.WebSocketContainer;
 import javax.websocket.server.ServerEndpoint;
 
 /**
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/idletimeout/IdleTimeoutContextListener.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/idletimeout/IdleTimeoutContextListener.java
index 15c9fe3..773340b 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/idletimeout/IdleTimeoutContextListener.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/idletimeout/IdleTimeoutContextListener.java
@@ -25,7 +25,7 @@
 import javax.websocket.server.ServerEndpointConfig;
 
 /**
- * Example of adding a server WebSocket (extending {@link Endpoint}) programmatically via config
+ * Example of adding a server WebSocket (extending {@link javax.websocket.Endpoint}) programmatically via config
  */
 public class IdleTimeoutContextListener implements ServletContextListener
 {
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/pong/PongSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/pong/PongSocket.java
index d7c1702..0c0052d 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/pong/PongSocket.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/pong/PongSocket.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.websocket.jsr356.server.samples.pong;
 
 import java.nio.charset.StandardCharsets;
+
 import javax.websocket.EndpointConfig;
 import javax.websocket.OnMessage;
 import javax.websocket.OnOpen;
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/resources/jetty-logging.properties b/jetty-websocket/javax-websocket-server-impl/src/test/resources/jetty-logging.properties
index c5a50f6..5474a69 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/resources/jetty-logging.properties
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/resources/jetty-logging.properties
@@ -6,6 +6,8 @@
 # org.eclipse.jetty.websocket.LEVEL=WARN
 # org.eclipse.jetty.websocket.common.io.LEVEL=DEBUG
 
+# org.eclipse.jetty.websocket.common.WebSocketSession.LEVEL=DEBUG
+# org.eclipse.jetty.websocket.jsr356.LEVEL=DEBUG
 ### Show state changes on BrowserDebugTool
 # -- LEAVE THIS AT DEBUG LEVEL --
 org.eclipse.jetty.websocket.jsr356.server.browser.LEVEL=DEBUG
diff --git a/jetty-websocket/pom.xml b/jetty-websocket/pom.xml
index dfaf7d2..895af68 100644
--- a/jetty-websocket/pom.xml
+++ b/jetty-websocket/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <artifactId>jetty-project</artifactId>
         <groupId>org.eclipse.jetty</groupId>
-        <version>9.2.22-SNAPSHOT</version>
+        <version>9.3.19-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
@@ -32,38 +32,11 @@
                     <onlyAnalyze>org.eclipse.jetty.websocket.*</onlyAnalyze>
                 </configuration>
             </plugin>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>manifest</goal>
-                        </goals>
-                        <configuration>
-                            <instructions>
-                                <Export-Package>${bundle-symbolic-name}.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package>
-                                <Import-Package>javax.servlet.*;version="[3.1,4.0)",org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
-                                <_nouses>true</_nouses>
-                            </instructions>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <configuration>
-                    <archive>
-                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-                    </archive>
-                </configuration>
-            </plugin>
+<!--
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>clirr-maven-plugin</artifactId>
-                <version>2.5</version>
+                <version>2.7</version>
                 <executions>
                     <execution>
                         <id>compare-api</id>
@@ -78,6 +51,7 @@
                     <comparisonVersion>9.1.0.v20131115</comparisonVersion>
                 </configuration>
             </plugin>
+-->
         </plugins>
     </build>
 </project>
diff --git a/jetty-websocket/websocket-api/pom.xml b/jetty-websocket/websocket-api/pom.xml
index af1b7d4..12e4628 100644
--- a/jetty-websocket/websocket-api/pom.xml
+++ b/jetty-websocket/websocket-api/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.websocket</groupId>
         <artifactId>websocket-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
+        <version>9.3.19-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/BatchMode.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/BatchMode.java
index 763b35f..ce69ef8 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/BatchMode.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/BatchMode.java
@@ -18,8 +18,10 @@
 
 package org.eclipse.jetty.websocket.api;
 
+import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
+
 /**
- * The possible batch modes when invoking {@link OutgoingFrames#outgoingFrame(Frame, WriteCallback, BatchMode)}.
+ * The possible batch modes when invoking {@link OutgoingFrames#outgoingFrame(org.eclipse.jetty.websocket.api.extensions.Frame, WriteCallback, BatchMode)}.
  */
 public enum BatchMode
 {
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/InvalidWebSocketException.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/InvalidWebSocketException.java
index e35c855..a487373 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/InvalidWebSocketException.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/InvalidWebSocketException.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.api;
 
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+
 /**
  * Indicating that the provided Class is not a valid WebSocket as defined by the API.
  * <p>
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/RemoteEndpoint.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/RemoteEndpoint.java
index 791a5dd..0987bf5 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/RemoteEndpoint.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/RemoteEndpoint.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.websocket.api;
 
 import java.io.IOException;
+import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
 import java.util.concurrent.Future;
 
@@ -31,11 +32,14 @@
      * 
      * @param data
      *            the message to be sent
+     * @throws IOException
+     *             if unable to send the bytes
      */
     void sendBytes(ByteBuffer data) throws IOException;
 
     /**
-     * Initiates the asynchronous transmission of a binary message. This method returns before the message is transmitted. Developers may use the returned
+     * Initiates the asynchronous transmission of a binary message. This method returns before the message is
+     * transmitted. Developers may use the returned
      * Future object to track progress of the transmission.
      * 
      * @param data
@@ -45,7 +49,8 @@
     Future<Void> sendBytesByFuture(ByteBuffer data);
 
     /**
-     * Initiates the asynchronous transmission of a binary message. This method returns before the message is transmitted. Developers may provide a callback to
+     * Initiates the asynchronous transmission of a binary message. This method returns before the message is
+     * transmitted. Developers may provide a callback to
      * be notified when the message has been transmitted or resulted in an error.
      * 
      * @param data
@@ -56,38 +61,54 @@
     void sendBytes(ByteBuffer data, WriteCallback callback);
 
     /**
-     * Send a binary message in pieces, blocking until all of the message has been transmitted. The runtime reads the message in order. Non-final pieces are
+     * Send a binary message in pieces, blocking until all of the message has been transmitted. The runtime reads the
+     * message in order. Non-final pieces are
      * sent with isLast set to false. The final piece must be sent with isLast set to true.
      * 
      * @param fragment
      *            the piece of the message being sent
+     * @param isLast
+     *            true if this is the last piece of the partial bytes
+     * @throws IOException
+     *             if unable to send the partial bytes
      */
     void sendPartialBytes(ByteBuffer fragment, boolean isLast) throws IOException;
 
     /**
-     * Send a text message in pieces, blocking until all of the message has been transmitted. The runtime reads the message in order. Non-final pieces are sent
+     * Send a text message in pieces, blocking until all of the message has been transmitted. The runtime reads the
+     * message in order. Non-final pieces are sent
      * with isLast set to false. The final piece must be sent with isLast set to true.
      * 
      * @param fragment
      *            the piece of the message being sent
+     * @param isLast
+     *            true if this is the last piece of the partial bytes
+     * @throws IOException
+     *             if unable to send the partial bytes
      */
     void sendPartialString(String fragment, boolean isLast) throws IOException;
 
     /**
-     * Send a Ping message containing the given application data to the remote endpoint. The corresponding Pong message may be picked up using the
+     * Send a Ping message containing the given application data to the remote endpoint. The corresponding Pong message
+     * may be picked up using the
      * MessageHandler.Pong handler.
      * 
      * @param applicationData
      *            the data to be carried in the ping request
+     * @throws IOException
+     *             if unable to send the ping
      */
     void sendPing(ByteBuffer applicationData) throws IOException;
 
     /**
-     * Allows the developer to send an unsolicited Pong message containing the given application data in order to serve as a unidirectional heartbeat for the
+     * Allows the developer to send an unsolicited Pong message containing the given application data in order to serve
+     * as a unidirectional heartbeat for the
      * session.
      * 
      * @param applicationData
      *            the application data to be carried in the pong response.
+     * @throws IOException
+     *             if unable to send the pong
      */
     void sendPong(ByteBuffer applicationData) throws IOException;
 
@@ -98,11 +119,14 @@
      * 
      * @param text
      *            the message to be sent
+     * @throws IOException
+     *             if unable to send the text message
      */
     void sendString(String text) throws IOException;
 
     /**
-     * Initiates the asynchronous transmission of a text message. This method may return before the message is transmitted. Developers may use the returned
+     * Initiates the asynchronous transmission of a text message. This method may return before the message is
+     * transmitted. Developers may use the returned
      * Future object to track progress of the transmission.
      * 
      * @param text
@@ -112,7 +136,8 @@
     Future<Void> sendStringByFuture(String text);
 
     /**
-     * Initiates the asynchronous transmission of a text message. This method may return before the message is transmitted. Developers may provide a callback to
+     * Initiates the asynchronous transmission of a text message. This method may return before the message is
+     * transmitted. Developers may provide a callback to
      * be notified when the message has been transmitted or resulted in an error.
      * 
      * @param text
@@ -136,10 +161,19 @@
      * @see #flush()
      */
     void setBatchMode(BatchMode mode);
+
+    /**
+     * Get the InetSocketAddress for the established connection.
+     *
+     * @return the InetSocketAddress for the established connection. (or null, if the connection is no longer established)
+     */
+    InetSocketAddress getInetSocketAddress();
     
     /**
      * Flushes messages that may have been batched by the implementation.
-     * @throws IOException if the flush fails
+     * 
+     * @throws IOException
+     *             if the flush fails
      * @see #getBatchMode()
      */
     void flush() throws IOException;
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java
index d0fff19..4d13f92 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java
@@ -22,9 +22,10 @@
 import java.io.IOException;
 import java.net.InetSocketAddress;
 
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
+
 /**
  * Session represents an active link of communications with a Remote WebSocket Endpoint.
- * <p>
  */
 public interface Session extends Closeable
 {
@@ -80,7 +81,11 @@
      * Once called, any read/write activity on the websocket from this point will be indeterminate.
      * <p>
      * Once the underlying connection has been determined to be closed, the various onClose() events (either
-     * {@link WebSocketListener#onWebSocketClose(int, String)} or {@link OnWebSocketClose}) will be called on your websocket.
+     * {@link WebSocketListener#onWebSocketClose(int, String)} or {@link OnWebSocketClose}) will be called on your
+     * websocket.
+     * 
+     * @throws IOException
+     *             if unable to disconnect
      * 
      * @see #close()
      * @see #close(CloseStatus)
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeResponse.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeResponse.java
index 84de036..afe07f2 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeResponse.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeResponse.java
@@ -136,13 +136,15 @@
     /**
      * Issue a forbidden upgrade response.
      * <p>
-     * This means that the websocket endpoint was valid, but the conditions to use a WebSocket resulted in a forbidden access.
+     * This means that the websocket endpoint was valid, but the conditions to use a WebSocket resulted in a forbidden
+     * access.
      * <p>
      * Use this when the origin or authentication is invalid.
      * 
      * @param message
      *            the short 1 line detail message about the forbidden response
      * @throws IOException
+     *             if unable to send the forbidden
      */
     public void sendForbidden(String message) throws IOException
     {
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketAdapter.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketAdapter.java
index 1e042b0..73ccc81 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketAdapter.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketAdapter.java
@@ -26,11 +26,11 @@
 public class WebSocketAdapter implements WebSocketListener
 {
     private volatile Session session;
+    private RemoteEndpoint remote;
 
     public RemoteEndpoint getRemote()
     {
-        Session sess = this.session;
-        return sess == null?null:session.getRemote();
+        return remote;
     }
 
     public Session getSession()
@@ -60,12 +60,14 @@
     public void onWebSocketClose(int statusCode, String reason)
     {
         this.session = null;
+        this.remote = null;
     }
 
     @Override
     public void onWebSocketConnect(Session sess)
     {
         this.session = sess;
+        this.remote = sess.getRemote();
     }
 
     @Override
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketConnectionListener.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketConnectionListener.java
new file mode 100644
index 0000000..6d7285f
--- /dev/null
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketConnectionListener.java
@@ -0,0 +1,61 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.api;
+
+/**
+ * Core WebSocket Connection Listener
+ */
+public interface WebSocketConnectionListener
+{
+    /**
+     * A Close Event was received.
+     * <p>
+     * The underlying Connection will be considered closed at this point.
+     * 
+     * @param statusCode
+     *            the close status code. (See {@link StatusCode})
+     * @param reason
+     *            the optional reason for the close.
+     */
+    void onWebSocketClose(int statusCode, String reason);
+
+    /**
+     * A WebSocket {@link Session} has connected successfully and is ready to be used.
+     * <p>
+     * Note: It is a good idea to track this session as a field in your object so that you can write messages back via the {@link RemoteEndpoint}
+     * 
+     * @param session
+     *            the websocket session.
+     */
+    void onWebSocketConnect(Session session);
+
+    /**
+     * A WebSocket exception has occurred.
+     * <p>
+     * This is a way for the internal implementation to notify of exceptions occured during the processing of websocket.
+     * <p>
+     * Usually this occurs from bad / malformed incoming packets. (example: bad UTF8 data, frames that are too big, violations of the spec)
+     * <p>
+     * This will result in the {@link Session} being closed by the implementing side.
+     * 
+     * @param cause
+     *            the error that occurred.
+     */
+    void onWebSocketError(Throwable cause);
+}
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketFrameListener.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketFrameListener.java
new file mode 100644
index 0000000..a282eb9
--- /dev/null
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketFrameListener.java
@@ -0,0 +1,34 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.api;
+
+import org.eclipse.jetty.websocket.api.extensions.Frame;
+
+/**
+ * WebSocket Frame Listener interface for incoming WebSocket frames.
+ */
+public interface WebSocketFrameListener extends WebSocketConnectionListener
+{
+    /**
+     * A WebSocket frame has been received.
+     * 
+     * @param frame the immutable frame received
+     */
+    void onWebSocketFrame(Frame frame);
+}
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketListener.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketListener.java
index 29e584d..b526675 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketListener.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketListener.java
@@ -19,9 +19,9 @@
 package org.eclipse.jetty.websocket.api;
 
 /**
- * Basic WebSocket Listener interface for incoming WebSocket events.
+ * Basic WebSocket Listener interface for incoming WebSocket message events.
  */
-public interface WebSocketListener
+public interface WebSocketListener extends WebSocketConnectionListener
 {
     /**
      * A WebSocket binary frame has been received.
@@ -36,45 +36,9 @@
     void onWebSocketBinary(byte payload[], int offset, int len);
 
     /**
-     * A Close Event was received.
-     * <p>
-     * The underlying Connection will be considered closed at this point.
-     * 
-     * @param statusCode
-     *            the close status code. (See {@link StatusCode})
-     * @param reason
-     *            the optional reason for the close.
-     */
-    void onWebSocketClose(int statusCode, String reason);
-
-    /**
-     * A WebSocket {@link Session} has connected successfully and is ready to be used.
-     * <p>
-     * Note: It is a good idea to track this session as a field in your object so that you can write messages back via the {@link RemoteEndpoint}
-     * 
-     * @param session
-     *            the websocket session.
-     */
-    void onWebSocketConnect(Session session);
-
-    /**
-     * A WebSocket exception has occurred.
-     * <p>
-     * This is a way for the internal implementation to notify of exceptions occured during the processing of websocket.
-     * <p>
-     * Usually this occurs from bad / malformed incoming packets. (example: bad UTF8 data, frames that are too big, violations of the spec)
-     * <p>
-     * This will result in the {@link Session} being closed by the implementing side.
-     * 
-     * @param error
-     *            the error that occurred.
-     */
-    void onWebSocketError(Throwable cause);
-
-    /**
      * A WebSocket Text frame was received.
      * 
-     * @param message
+     * @param message the message
      */
     void onWebSocketText(String message);
 }
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPartialListener.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPartialListener.java
new file mode 100644
index 0000000..88cb014
--- /dev/null
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPartialListener.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.api;
+
+import java.nio.ByteBuffer;
+
+/**
+ * WebSocket Partial Message Listener interface for incoming WebSocket TEXT/BINARY/CONTINUATION frames.
+ */
+public interface WebSocketPartialListener extends WebSocketConnectionListener
+{
+    /**
+     * A WebSocket BINARY (or associated CONTINUATION) frame has been received.
+     * <p>
+     * <b>Important Note</b>: The payload <code>ByteBuffer</code> cannot be modified, and the ByteBuffer object itself
+     * will be recycled on completion of this method call, make a copy of the data contained within if you want to
+     * retain it between calls.
+     * 
+     * @param payload
+     *            the binary message frame payload
+     * @param fin
+     *            true if this is the final frame, false otherwise
+     */
+    void onWebSocketPartialBinary(ByteBuffer payload, boolean fin);
+
+    /**
+     * A WebSocket TEXT (or associated CONTINUATION) frame has been received.
+     * 
+     * @param payload
+     *            the text message payload
+     *            <p>
+     *            Note that due to framing, there is a above average chance of any UTF8 sequences being split on the
+     *            border between two frames will result in either the previous frame, or the next frame having an
+     *            invalid UTF8 sequence, but the combined frames having a valid UTF8 sequence.
+     *            <p>
+     *            The String being provided here will not end in a split UTF8 sequence. Instead this partial sequence
+     *            will be held over until the next frame is received.
+     * @param fin
+     *            true if this is the final frame, false otherwise
+     */
+    void onWebSocketPartialText(String payload, boolean fin);
+}
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPingPongListener.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPingPongListener.java
new file mode 100644
index 0000000..9a109df
--- /dev/null
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPingPongListener.java
@@ -0,0 +1,43 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.api;
+
+import java.nio.ByteBuffer;
+
+/**
+ * WebSocket PING/PONG Listener interface for incoming WebSocket PING/PONG frames.
+ */
+public interface WebSocketPingPongListener extends WebSocketConnectionListener
+{
+    /**
+     * A WebSocket PING has been received.
+     * 
+     * @param payload
+     *            the ping payload
+     */
+    void onWebSocketPing(ByteBuffer payload);
+
+    /**
+     * A WebSocket PONG has been received.
+     * 
+     * @param payload
+     *            the pong payload
+     */
+    void onWebSocketPong(ByteBuffer payload);
+}
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketClose.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketClose.java
index 9df1e15..2679229 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketClose.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketClose.java
@@ -24,6 +24,8 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.eclipse.jetty.websocket.api.Session;
+
 /**
  * Annotation for tagging methods to receive connection close events.
  * <p>
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketConnect.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketConnect.java
index 846a083..06b81de 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketConnect.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketConnect.java
@@ -24,6 +24,8 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.eclipse.jetty.websocket.api.Session;
+
 /**
  * Annotation for tagging methods to receive connection open events.
  * <p>
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketError.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketError.java
index 11cc8e5..2134479 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketError.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketError.java
@@ -24,12 +24,13 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.eclipse.jetty.websocket.api.Session;
+
 /**
  * Annotation for receiving websocket errors (exceptions) that have occurred internally in the websocket implementation.
  * <p>
  * Acceptable method patterns.<br>
  * Note: <code>methodName</code> can be any name you want to use.
- * <p>
  * <ol>
  * <li><code>public void methodName({@link Throwable} error)</code></li>
  * <li><code>public void methodName({@link Session} session, {@link Throwable} error)</code></li>
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketFrame.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketFrame.java
index 9e794c7..cca2af7 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketFrame.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketFrame.java
@@ -24,14 +24,16 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.eclipse.jetty.websocket.api.Session;
+
 /**
  * (ADVANCED) Annotation for tagging methods to receive frame events.
  * <p>
  * Acceptable method patterns.<br>
  * Note: <code>methodName</code> can be any name you want to use.
  * <ol>
- * <li><code>public void methodName({@link Frame} frame)</code></li>
- * <li><code>public void methodName({@link Session} session, {@link Frame} frame)</code></li>
+ * <li><code>public void methodName({@link org.eclipse.jetty.websocket.api.extensions.Frame} frame)</code></li>
+ * <li><code>public void methodName({@link Session} session, {@link org.eclipse.jetty.websocket.api.extensions.Frame} frame)</code></li>
  * </ol>
  */
 @Documented
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.java
index 049a418..1b2eeb5 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.java
@@ -18,12 +18,15 @@
 
 package org.eclipse.jetty.websocket.api.annotations;
 
+import java.io.Reader;
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.eclipse.jetty.websocket.api.Session;
+
 /**
  * Annotation for tagging methods to receive Binary or Text Message events.
  * <p>
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/Extension.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/Extension.java
index 531073b..9fb6723 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/Extension.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/Extension.java
@@ -36,6 +36,7 @@
      * The <code>Sec-WebSocket-Extensions</code> name for this extension.
      * <p>
      * Also known as the <a href="https://tools.ietf.org/html/rfc6455#section-9.1"><code>extension-token</code> per Section 9.1. Negotiating Extensions</a>.
+     * @return the name of the extension
      */
     public String getName();
 
@@ -81,9 +82,4 @@
      *            the next outgoing extension
      */
     public void setNextOutgoingFrames(OutgoingFrames nextOutgoing);
-    
-    // TODO: Extension should indicate if it requires boundary of fragments to be preserved
-    
-    // TODO: Extension should indicate if it uses the Extension data field of frame for its own reasons.
-    
 }
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/ExtensionConfig.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/ExtensionConfig.java
index c40268f..1753ddf 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/ExtensionConfig.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/ExtensionConfig.java
@@ -128,6 +128,7 @@
 
     /**
      * Copy constructor
+     * @param copy the extension config to copy
      */
     public ExtensionConfig(ExtensionConfig copy)
     {
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/Frame.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/Frame.java
index efbb8f9..d6aa16e 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/Frame.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/Frame.java
@@ -103,8 +103,9 @@
      * Same as {@link #isFin()}
      * 
      * @return true if final frame.
+     * @deprecated use {@link #isFin()} instead
      */
-    // FIXME: remove
+    @Deprecated
     public boolean isLast();
 
     public boolean isMasked();
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/OutgoingFrames.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/OutgoingFrames.java
index c13dede..3a49ec8 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/OutgoingFrames.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/OutgoingFrames.java
@@ -28,10 +28,10 @@
 {
     /**
      * A frame, and optional callback, intended for the network layer.
-     * <p/>
+     * <p>
      * Note: the frame can undergo many transformations in the various
      * layers and extensions present in the implementation.
-     * <p/>
+     * <p>
      * If you are implementing a mutation, you are obliged to handle
      * the incoming WriteCallback appropriately.
      *
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/util/QuoteUtil.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/util/QuoteUtil.java
index b76be93..4fb7ad8 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/util/QuoteUtil.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/util/QuoteUtil.java
@@ -27,7 +27,6 @@
  * Provide some consistent Http header value and Extension configuration parameter quoting support.
  * <p>
  * While QuotedStringTokenizer exists in jetty-util, and works great with http header values, using it in websocket-api is undesired.
- * <p>
  * <ul>
  * <li>Using QuotedStringTokenizer would introduce a dependency to jetty-util that would need to be exposed via the WebAppContext classloader</li>
  * <li>ABNF defined extension parameter parsing requirements of RFC-6455 (WebSocket) ABNF, is slightly different than the ABNF parsing defined in RFC-2616
@@ -48,8 +47,6 @@
             QUOTE_DOUBLE
         }
 
-        private static final boolean DEBUG = false;
-
         private final String input;
         private final String delims;
         private StringBuilder token;
@@ -84,14 +81,6 @@
             }
         }
 
-        private void debug(String format, Object... args)
-        {
-            if (DEBUG)
-            {
-                System.out.printf(format,args);
-            }
-        }
-
         @Override
         public boolean hasNext()
         {
@@ -134,7 +123,7 @@
                     {
                         if (delims.indexOf(c) >= 0)
                         {
-                            debug("hasNext/t: %b [%s]%n",hasToken,token);
+                            // System.out.printf("hasNext/t: %b [%s]%n",hasToken,token);
                             return hasToken;
                         }
                         else if (c == '\'')
@@ -193,10 +182,9 @@
                         break;
                     }
                 }
-                debug("%s <%s> : [%s]%n",state,c,token);
+                // System.out.printf("%s <%s> : [%s]%n",state,c,token);
             }
-
-            debug("hasNext/e: %b [%s]%n",hasToken,token);
+            // System.out.printf("hasNext/e: %b [%s]%n",hasToken,token);
             return hasToken;
         }
 
diff --git a/jetty-websocket/websocket-api/src/test/java/org/eclipse/jetty/websocket/api/util/QuoteUtilTest.java b/jetty-websocket/websocket-api/src/test/java/org/eclipse/jetty/websocket/api/util/QuoteUtilTest.java
index 199b29a..1968974 100644
--- a/jetty-websocket/websocket-api/src/test/java/org/eclipse/jetty/websocket/api/util/QuoteUtilTest.java
+++ b/jetty-websocket/websocket-api/src/test/java/org/eclipse/jetty/websocket/api/util/QuoteUtilTest.java
@@ -18,8 +18,8 @@
 
 package org.eclipse.jetty.websocket.api.util;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
 
 import java.util.Iterator;
 import java.util.NoSuchElementException;
diff --git a/jetty-websocket/websocket-client/pom.xml b/jetty-websocket/websocket-client/pom.xml
index f8216a5..cf62503 100644
--- a/jetty-websocket/websocket-client/pom.xml
+++ b/jetty-websocket/websocket-client/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.websocket</groupId>
         <artifactId>websocket-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
+        <version>9.3.19-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
@@ -88,7 +88,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
-                <version>2.4.3</version>
+                <version>2.0</version>
                 <executions>
                     <execution>
                         <phase>package</phase>
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java
index 68a03e8..31a4f8c 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java
@@ -46,7 +46,6 @@
 public class ClientUpgradeRequest extends UpgradeRequest
 {
     private static final Logger LOG = Log.getLogger(ClientUpgradeRequest.class);
-    private static final int MAX_KEYS = -1; // maximum number of parameter keys to decode
     private static final Set<String> FORBIDDEN_HEADERS;
 
     static
@@ -249,7 +248,7 @@
         if (StringUtil.isNotBlank(query))
         {
             MultiMap<String> params = new MultiMap<String>();
-            UrlEncoded.decodeTo(uri.getQuery(),params,StandardCharsets.UTF_8,MAX_KEYS);
+            UrlEncoded.decodeTo(uri.getQuery(),params,StandardCharsets.UTF_8);
 
             for (String key : params.keySet())
             {
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeResponse.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeResponse.java
index 26c1cbf..679a2d7 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeResponse.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeResponse.java
@@ -21,11 +21,15 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.UpgradeResponse;
 import org.eclipse.jetty.websocket.common.io.http.HttpResponseHeaderParseListener;
 
 public class ClientUpgradeResponse extends UpgradeResponse implements HttpResponseHeaderParseListener
 {
+    private static final Logger LOG = Log.getLogger(ClientUpgradeResponse.class);
     private ByteBuffer remainingBuffer;
 
     public ClientUpgradeResponse()
@@ -47,6 +51,10 @@
     @Override
     public void setRemainingBuffer(ByteBuffer remainingBuffer)
     {
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("Saving remaining header: {}",BufferUtil.toDetailString(remainingBuffer));
+        }
         this.remainingBuffer = remainingBuffer;
     }
 }
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java
index 7cda0c0..bc992f9 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java
@@ -22,9 +22,8 @@
 import java.net.CookieStore;
 import java.net.SocketAddress;
 import java.net.URI;
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Locale;
 import java.util.Set;
 import java.util.concurrent.Executor;
@@ -33,6 +32,7 @@
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.MappedByteBufferPool;
 import org.eclipse.jetty.io.SelectorManager;
+import org.eclipse.jetty.util.DecoratedObjectFactory;
 import org.eclipse.jetty.util.HttpCookieStore;
 import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
@@ -45,7 +45,6 @@
 import org.eclipse.jetty.util.thread.ShutdownThread;
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
-import org.eclipse.jetty.websocket.api.extensions.Extension;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
 import org.eclipse.jetty.websocket.client.io.ConnectPromise;
@@ -54,21 +53,21 @@
 import org.eclipse.jetty.websocket.client.masks.Masker;
 import org.eclipse.jetty.websocket.client.masks.RandomMasker;
 import org.eclipse.jetty.websocket.common.SessionFactory;
-import org.eclipse.jetty.websocket.common.SessionListener;
 import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.WebSocketSessionFactory;
 import org.eclipse.jetty.websocket.common.events.EventDriver;
 import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
 import org.eclipse.jetty.websocket.common.extensions.WebSocketExtensionFactory;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 
 /**
  * WebSocketClient provides a means of establishing connections to remote websocket endpoints.
  */
-public class WebSocketClient extends ContainerLifeCycle implements SessionListener
+public class WebSocketClient extends ContainerLifeCycle implements WebSocketContainerScope
 {
     private static final Logger LOG = Log.getLogger(WebSocketClient.class);
 
-    private final WebSocketPolicy policy;
+    private final WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
     private final SslContextFactory sslContextFactory;
     private final WebSocketExtensionFactory extensionRegistry;
     private boolean daemon = false;
@@ -76,18 +75,20 @@
     private SessionFactory sessionFactory;
     private ByteBufferPool bufferPool;
     private Executor executor;
+    private DecoratedObjectFactory objectFactory;
     private Scheduler scheduler;
     private CookieStore cookieStore;
     private ConnectionManager connectionManager;
     private Masker masker;
     private SocketAddress bindAddress;
     private long connectTimeout = SelectorManager.DEFAULT_CONNECT_TIMEOUT;
+    private boolean dispatchIO = true;
 
     public WebSocketClient()
     {
-        this(null,null);
+        this((SslContextFactory)null,null);
     }
-
+    
     public WebSocketClient(Executor executor)
     {
         this(null,executor);
@@ -108,28 +109,37 @@
         this(sslContextFactory,executor,new MappedByteBufferPool());
     }
     
-    public WebSocketClient(SslContextFactory sslContextFactory, Executor executor, ByteBufferPool bufferPool)
+    public WebSocketClient(WebSocketContainerScope scope)
     {
-        this.executor = executor;
-        this.sslContextFactory = sslContextFactory;
-        this.policy = WebSocketPolicy.newClientPolicy();
-        this.bufferPool = bufferPool;
-        this.extensionRegistry = new WebSocketExtensionFactory(policy,bufferPool);
-        
-        // Bug #431459 - unregistering compression extensions till they are more stable
-        this.extensionRegistry.unregister("deflate-frame");
-        this.extensionRegistry.unregister("permessage-deflate");
-        this.extensionRegistry.unregister("x-webkit-deflate-frame");
-        
-        this.masker = new RandomMasker();
-        this.eventDriverFactory = new EventDriverFactory(policy);
-        this.sessionFactory = new WebSocketSessionFactory(this);
-        
-        addBean(this.executor);
-        addBean(this.sslContextFactory);
-        addBean(this.bufferPool);
+        this(scope.getSslContextFactory(), scope.getExecutor(), scope.getBufferPool(), scope.getObjectFactory());
+    }
+    
+    public WebSocketClient(WebSocketContainerScope scope, SslContextFactory sslContextFactory)
+    {
+        this(sslContextFactory, scope.getExecutor(), scope.getBufferPool(), scope.getObjectFactory());
     }
 
+    public WebSocketClient(SslContextFactory sslContextFactory, Executor executor, ByteBufferPool bufferPool)
+    {
+        this(sslContextFactory, executor, bufferPool, new DecoratedObjectFactory());
+    }
+
+    public WebSocketClient(SslContextFactory sslContextFactory, Executor executor, ByteBufferPool bufferPool, DecoratedObjectFactory objectFactory)
+    {
+        
+        this.sslContextFactory = sslContextFactory;
+        if(sslContextFactory!=null)
+            addBean(sslContextFactory);
+        setExecutor(executor);
+        setBufferPool(bufferPool);
+        
+        this.objectFactory = objectFactory;
+        this.extensionRegistry = new WebSocketExtensionFactory(this);
+        
+        this.masker = new RandomMasker();
+        this.eventDriverFactory = new EventDriverFactory(policy);   
+    }
+    
     public Future<Session> connect(Object websocket, URI toUri) throws IOException
     {
         ClientUpgradeRequest request = new ClientUpgradeRequest(toUri);
@@ -184,7 +194,7 @@
             LOG.debug("connect websocket {} to {}",websocket,toUri);
 
         // Grab Connection Manager
-        initialiseClient();
+        initializeClient();
         ConnectionManager manager = getConnectionManager();
 
         // Setup Driver for user provided websocket
@@ -229,32 +239,36 @@
         if (LOG.isDebugEnabled())
             LOG.debug("Starting {}",this);
 
-        if (sslContextFactory != null)
-        {
-            addBean(sslContextFactory);
-        }
-
         String name = WebSocketClient.class.getSimpleName() + "@" + hashCode();
 
         if (bufferPool == null)
         {
-            bufferPool = new MappedByteBufferPool();
+            setBufferPool(new MappedByteBufferPool());
         }
-        addBean(bufferPool);
 
         if (scheduler == null)
         {
             scheduler = new ScheduledExecutorScheduler(name + "-scheduler",daemon);
+            addBean(scheduler);
         }
-        addBean(scheduler);
 
         if (cookieStore == null)
         {
-            cookieStore = new HttpCookieStore.Empty();
+            setCookieStore(new HttpCookieStore.Empty());
+        }
+
+        if(this.sessionFactory == null)
+        {
+            setSessionFactory(new WebSocketSessionFactory(this));
+        }
+        
+        if(this.objectFactory == null)
+        {
+            this.objectFactory = new DecoratedObjectFactory();
         }
 
         super.doStart();
-
+        
         if (LOG.isDebugEnabled())
             LOG.debug("Started {}",this);
     }
@@ -264,24 +278,24 @@
     {
         if (LOG.isDebugEnabled())
             LOG.debug("Stopping {}",this);
+
         
         if (ShutdownThread.isRegistered(this))
         {
             ShutdownThread.deregister(this);
         }
 
-        if (cookieStore != null)
-        {
-            cookieStore.removeAll();
-            cookieStore = null;
-        }
-
         super.doStop();
-
+        
         if (LOG.isDebugEnabled())
             LOG.debug("Stopped {}",this);
     }
 
+    public boolean isDispatchIO()
+    {
+        return dispatchIO;
+    }
+
     /**
      * Return the number of milliseconds for a timeout of an attempted write operation.
      * 
@@ -387,9 +401,15 @@
         return this.policy.getMaxTextMessageSize();
     }
 
+    @Override
+    public DecoratedObjectFactory getObjectFactory()
+    {
+        return this.objectFactory;
+    }
+
     public Set<WebSocketSession> getOpenSessions()
     {
-        return new HashSet<>(getBeans(WebSocketSession.class));
+        return Collections.unmodifiableSet(new HashSet<>(getBeans(WebSocketSession.class)));
     }
 
     public WebSocketPolicy getPolicy()
@@ -401,7 +421,7 @@
     {
         return scheduler;
     }
-
+    
     public SessionFactory getSessionFactory()
     {
         return sessionFactory;
@@ -416,29 +436,7 @@
         return sslContextFactory;
     }
 
-    public List<Extension> initExtensions(List<ExtensionConfig> requested)
-    {
-        List<Extension> extensions = new ArrayList<Extension>();
-
-        for (ExtensionConfig cfg : requested)
-        {
-            Extension extension = extensionRegistry.newInstance(cfg);
-
-            if (extension == null)
-            {
-                continue;
-            }
-
-            if (LOG.isDebugEnabled())
-                LOG.debug("added {}",extension);
-            extensions.add(extension);
-        }
-        if (LOG.isDebugEnabled())
-            LOG.debug("extensions={}",extensions);
-        return extensions;
-    }
-
-    private synchronized void initialiseClient() throws IOException
+    private synchronized void initializeClient() throws IOException
     {
         if (!ShutdownThread.isRegistered(this))
         {
@@ -489,20 +487,32 @@
     {
         if (LOG.isDebugEnabled())
             LOG.debug("Session Opened: {}",session);
+        addManaged(session);
     }
-
+    
     public void setAsyncWriteTimeout(long ms)
     {
         this.policy.setAsyncWriteTimeout(ms);
     }
 
+    /**
+     * @param bindAddress the address to bind to
+     * @deprecated use {@link #setBindAddress(SocketAddress)} instead
+     */
+    @Deprecated
     public void setBindAdddress(SocketAddress bindAddress)
     {
+        setBindAddress(bindAddress);
+    }
+
+    public void setBindAddress(SocketAddress bindAddress)
+    {
         this.bindAddress = bindAddress;
     }
 
     public void setBufferPool(ByteBufferPool bufferPool)
     {
+        updateBean(this.bufferPool,bufferPool);
         this.bufferPool = bufferPool;
     }
 
@@ -531,6 +541,11 @@
         this.daemon = daemon;
     }
 
+    public void setDispatchIO(boolean dispatchIO)
+    {
+        this.dispatchIO = dispatchIO;
+    }
+
     public void setEventDriverFactory(EventDriverFactory factory)
     {
         this.eventDriverFactory = factory;
@@ -572,6 +587,7 @@
 
     public void setSessionFactory(SessionFactory sessionFactory)
     {
+        updateBean(this.sessionFactory,sessionFactory);
         this.sessionFactory = sessionFactory;
     }
 
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectPromise.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectPromise.java
index 21005b0..b5ee615 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectPromise.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectPromise.java
@@ -19,6 +19,8 @@
 package org.eclipse.jetty.websocket.client.io;
 
 import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
 import org.eclipse.jetty.websocket.client.ClientUpgradeResponse;
@@ -32,12 +34,14 @@
  */
 public abstract class ConnectPromise extends FuturePromise<Session> implements Runnable
 {
+    private static final Logger LOG = Log.getLogger(ConnectPromise.class);
     private final WebSocketClient client;
     private final EventDriver driver;
     private final ClientUpgradeRequest request;
     private final Masker masker;
     private UpgradeListener upgradeListener;
     private ClientUpgradeResponse response;
+    private WebSocketSession session;
 
     public ConnectPromise(WebSocketClient client, EventDriver driver, ClientUpgradeRequest request)
     {
@@ -97,11 +101,18 @@
         this.upgradeListener = upgradeListener;
     }
 
-    public void succeeded(WebSocketSession session)
+    public void succeeded()
     {
+        if(LOG.isDebugEnabled())
+            LOG.debug("{}.succeeded()",this.getClass().getSimpleName());
         session.setUpgradeRequest(request);
         session.setUpgradeResponse(response);
-        session.open();
+        // session.open();
         super.succeeded(session);
     }
+
+    public void setSession(WebSocketSession session)
+    {
+        this.session = session;
+    }
 }
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java
index 97e34f8..3332590 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java
@@ -23,20 +23,13 @@
 import java.net.SocketAddress;
 import java.net.URI;
 import java.nio.channels.SocketChannel;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.Locale;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
 
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.websocket.api.StatusCode;
-import org.eclipse.jetty.websocket.api.WebSocketException;
 import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
 import org.eclipse.jetty.websocket.client.WebSocketClient;
-import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.events.EventDriver;
 
 /**
@@ -104,20 +97,6 @@
         }
     }
 
-    private class VirtualConnect extends ConnectPromise
-    {
-        public VirtualConnect(WebSocketClient client, EventDriver driver, ClientUpgradeRequest request)
-        {
-            super(client,driver,request);
-        }
-
-        @Override
-        public void run()
-        {
-            failed(new WebSocketException("MUX Not yet supported"));
-        }
-    }
-
     private static final Logger LOG = Log.getLogger(ConnectionManager.class);
 
     public static InetSocketAddress toSocketAddress(URI uri)
@@ -151,7 +130,6 @@
         return new InetSocketAddress(uri.getHost(),port);
     }
 
-    private final Queue<WebSocketSession> sessions = new ConcurrentLinkedQueue<>();
     private final WebSocketClient client;
     private WebSocketClientSelectorManager selector;
 
@@ -160,41 +138,8 @@
         this.client = client;
     }
 
-    public void addSession(WebSocketSession session)
-    {
-        sessions.add(session);
-    }
-
-    private void shutdownAllConnections()
-    {
-        for (WebSocketSession session : sessions)
-        {
-            if (session.getConnection() != null)
-            {
-                try
-                {
-                    session.getConnection().close(
-                            StatusCode.SHUTDOWN,
-                            "Shutdown");
-                }
-                catch (Throwable t)
-                {
-                    LOG.debug("During Shutdown All Connections",t);
-                }
-            }
-        }
-    }
-
     public ConnectPromise connect(WebSocketClient client, EventDriver driver, ClientUpgradeRequest request)
     {
-        URI toUri = request.getRequestURI();
-        String hostname = toUri.getHost();
-
-        if (isVirtualConnectionPossibleTo(hostname))
-        {
-            return new VirtualConnect(client,driver,request);
-        }
-
         return new PhysicalConnect(client,driver,request);
     }
 
@@ -212,8 +157,6 @@
     @Override
     protected void doStop() throws Exception
     {
-        shutdownAllConnections();
-        sessions.clear();
         super.doStop();
         removeBean(selector);
     }
@@ -223,17 +166,6 @@
         return selector;
     }
 
-    public Collection<WebSocketSession> getSessions()
-    {
-        return Collections.unmodifiableCollection(sessions);
-    }
-
-    public boolean isVirtualConnectionPossibleTo(String hostname)
-    {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
     /**
      * Factory method for new WebSocketClientSelectorManager (used by other projects like cometd)
      * 
@@ -245,9 +177,4 @@
     {
         return new WebSocketClientSelectorManager(client);
     }
-
-    public void removeSession(WebSocketSession session)
-    {
-        sessions.remove(session);
-    }
 }
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeConnection.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeConnection.java
index 88c504e..396dfce 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeConnection.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeConnection.java
@@ -29,6 +29,7 @@
 
 import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.FutureCallback;
@@ -49,14 +50,18 @@
 import org.eclipse.jetty.websocket.common.io.http.HttpResponseHeaderParser.ParseException;
 
 /**
- * This is the initial connection handling that exists immediately after physical connection is established to destination server.
+ * This is the initial connection handling that exists immediately after physical connection is established to
+ * destination server.
  * <p>
- * Eventually, upon successful Upgrade request/response, this connection swaps itself out for the WebSocektClientConnection handler.
+ * Eventually, upon successful Upgrade request/response, this connection swaps itself out for the
+ * WebSocektClientConnection handler.
  */
-public class UpgradeConnection extends AbstractConnection
+public class UpgradeConnection extends AbstractConnection implements Connection.UpgradeFrom
 {
     public class SendUpgradeRequest extends FutureCallback implements Runnable
     {
+        private final Logger LOG = Log.getLogger(UpgradeConnection.SendUpgradeRequest.class);
+        
         @Override
         public void run()
         {
@@ -71,16 +76,20 @@
 
             String rawRequest = request.generate();
 
-            ByteBuffer buf = BufferUtil.toBuffer(rawRequest, StandardCharsets.UTF_8);
+            ByteBuffer buf = BufferUtil.toBuffer(rawRequest,StandardCharsets.UTF_8);
             getEndPoint().write(this,buf);
         }
 
         @Override
         public void succeeded()
         {
-            LOG.debug("Upgrade Request Write Success");
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("Upgrade Request Write Success");
+            }
             // Writing the request header is complete.
             super.succeeded();
+            state = State.RESPONSE;
             // start the interest in fill
             fillInterested();
         }
@@ -88,8 +97,12 @@
         @Override
         public void failed(Throwable cause)
         {
-            LOG.warn("Upgrade Request Write Failure", cause);
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("Upgrade Request Write Failure",cause);
+            }
             super.failed(cause);
+            state = State.FAILURE;
             // Fail the connect promise when a fundamental exception during connect occurs.
             connectPromise.failed(cause);
         }
@@ -98,11 +111,21 @@
     /** HTTP Response Code: 101 Switching Protocols */
     private static final int SWITCHING_PROTOCOLS = 101;
 
+    private enum State
+    {
+        REQUEST,
+        RESPONSE,
+        FAILURE,
+        UPGRADE
+    }
+
     private static final Logger LOG = Log.getLogger(UpgradeConnection.class);
     private final ByteBufferPool bufferPool;
     private final ConnectPromise connectPromise;
     private final HttpResponseHeaderParser parser;
+    private State state = State.REQUEST;
     private ClientUpgradeRequest request;
+    private ClientUpgradeResponse response;
 
     public UpgradeConnection(EndPoint endp, Executor executor, ConnectPromise connectPromise)
     {
@@ -121,12 +144,17 @@
         // We need to gently close first, to allow
         // SSL close alerts to be sent by Jetty
         if (LOG.isDebugEnabled())
+        {
             LOG.debug("Shutting down output {}",endPoint);
+        }
+        
         endPoint.shutdownOutput();
         if (!onlyOutput)
         {
             if (LOG.isDebugEnabled())
+            {
                 LOG.debug("Closing {}",endPoint);
+            }
             endPoint.close();
         }
     }
@@ -147,6 +175,12 @@
             handshakeListener.onHandshakeResponse(response);
         }
     }
+    
+    @Override
+    public ByteBuffer onUpgradeFrom()
+    {
+        return connectPromise.getResponse().getRemainingBuffer();
+    }
 
     @Override
     public void onFillable()
@@ -157,20 +191,25 @@
         }
         ByteBuffer buffer = bufferPool.acquire(getInputBufferSize(),false);
         BufferUtil.clear(buffer);
-        boolean readMore = false;
         try
         {
-            readMore = read(buffer);
+            read(buffer);
         }
         finally
         {
             bufferPool.release(buffer);
         }
 
-        if (readMore)
+        if (state == State.RESPONSE)
         {
+            // Continue Reading
             fillInterested();
         }
+        else if (state == State.UPGRADE)
+        {
+            // Stop Reading, upgrade the connection now
+            upgradeConnection(response);
+        }
     }
 
     @Override
@@ -179,27 +218,27 @@
         super.onOpen();
         getExecutor().execute(new SendUpgradeRequest());
     }
-    
+
     @Override
     public void onClose()
     {
         if (LOG.isDebugEnabled())
         {
-            LOG.warn("Closed connection {}",this);
+            LOG.debug("Closed connection {}",this);
         }
         super.onClose();
     }
-    
+
     @Override
     protected boolean onReadTimeout()
     {
         if (LOG.isDebugEnabled())
         {
-            LOG.warn("Timeout on connection {}",this);
+            LOG.debug("Timeout on connection {}",this);
         }
-        
+
         failUpgrade(new IOException("Timeout while performing WebSocket Upgrade"));
-        
+
         return super.onReadTimeout();
     }
 
@@ -208,9 +247,8 @@
      * 
      * @param buffer
      *            the buffer to fill into from the endpoint
-     * @return true if there is more to read, false if reading should stop
      */
-    private boolean read(ByteBuffer buffer)
+    private void read(ByteBuffer buffer)
     {
         EndPoint endPoint = getEndPoint();
         try
@@ -220,13 +258,14 @@
                 int filled = endPoint.fill(buffer);
                 if (filled == 0)
                 {
-                    return true;
+                    return;
                 }
                 else if (filled < 0)
                 {
                     LOG.warn("read - EOF Reached");
+                    state = State.FAILURE;
                     failUpgrade(new EOFException("Reading WebSocket Upgrade response"));
-                    return false;
+                    return;
                 }
                 else
                 {
@@ -234,34 +273,32 @@
                     {
                         LOG.debug("Filled {} bytes - {}",filled,BufferUtil.toDetailString(buffer));
                     }
-                    ClientUpgradeResponse resp = (ClientUpgradeResponse)parser.parse(buffer);
-                    if (resp != null)
+                    response = (ClientUpgradeResponse)parser.parse(buffer);
+                    if (response != null)
                     {
                         // Got a response!
-                        validateResponse(resp);
-                        notifyConnect(resp);
-                        upgradeConnection(resp);
-                        if (buffer.hasRemaining())
-                        {
-                            LOG.debug("Has remaining client bytebuffer of {}",buffer.remaining());
-                        }
-                        return false; // do no more reading
+                        validateResponse(response);
+                        notifyConnect(response);
+                        state = State.UPGRADE;
+                        return; // do no more reading
                     }
                 }
             }
         }
         catch (IOException | ParseException e)
         {
+            LOG.ignore(e);
+            state = State.FAILURE;
             UpgradeException ue = new UpgradeException(request.getRequestURI(),e);
             connectPromise.failed(ue);
             disconnect(false);
-            return false;
         }
         catch (UpgradeException e)
         {
+            LOG.ignore(e);
+            state = State.FAILURE;
             connectPromise.failed(e);
             disconnect(false);
-            return false;
         }
     }
 
@@ -269,7 +306,7 @@
     {
         EndPoint endp = getEndPoint();
         Executor executor = getExecutor();
-        
+
         EventDriver websocket = connectPromise.getDriver();
         WebSocketPolicy policy = websocket.getPolicy();
 
@@ -278,9 +315,10 @@
         SessionFactory sessionFactory = connectPromise.getClient().getSessionFactory();
         WebSocketSession session = sessionFactory.createSession(request.getRequestURI(),websocket,connection);
         session.setPolicy(policy);
+        session.setUpgradeRequest(request);
         session.setUpgradeResponse(response);
-
-        connection.setSession(session);
+        connection.addListener(session);
+        connectPromise.setSession(session);
 
         // Initialize / Negotiate Extensions
         ExtensionStack extensionStack = new ExtensionStack(connectPromise.getClient().getExtensionFactory());
@@ -297,13 +335,11 @@
         session.setOutgoingHandler(extensionStack);
         extensionStack.setNextOutgoing(connection);
 
-        session.addBean(extensionStack);
+        session.addManaged(extensionStack);
         connectPromise.getClient().addManaged(session);
 
         // Now swap out the connection
-        // TODO use endp.upgrade ???
-        endp.setConnection(connection);
-        connection.onOpen();
+        endp.upgrade(connection);
     }
 
     private void validateResponse(ClientUpgradeResponse response)
@@ -311,7 +347,9 @@
         // Validate Response Status Code
         if (response.getStatusCode() != SWITCHING_PROTOCOLS)
         {
-            throw new UpgradeException(request.getRequestURI(),response.getStatusCode(),"Didn't switch protocols");
+            // TODO: use jetty-http and org.eclipse.jetty.http.HttpStatus for more meaningful exception messages 
+            throw new UpgradeException(request.getRequestURI(),response.getStatusCode(),"Didn't switch protocols, expected status <" + SWITCHING_PROTOCOLS
+                    + ">, but got <" + response.getStatusCode() + ">");
         }
 
         // Validate Connection header
@@ -340,6 +378,7 @@
         {
             for (String extVal : extValues)
             {
+                // TODO use QuotedCSV ???
                 QuotedStringTokenizer tok = new QuotedStringTokenizer(extVal,",");
                 while (tok.hasMoreTokens())
                 {
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientConnection.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientConnection.java
index d5adf20..7c9ba50 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientConnection.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientConnection.java
@@ -19,13 +19,10 @@
 package org.eclipse.jetty.websocket.client.io;
 
 import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.BatchMode;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.WriteCallback;
@@ -33,7 +30,6 @@
 import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
 import org.eclipse.jetty.websocket.client.masks.Masker;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
-import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection;
 
 /**
@@ -41,7 +37,6 @@
  */
 public class WebSocketClientConnection extends AbstractWebSocketConnection
 {
-    private static final Logger LOG = Log.getLogger(WebSocketClientConnection.class);
     private final ConnectPromise connectPromise;
     private final Masker masker;
     private final AtomicBoolean opened = new AtomicBoolean(false);
@@ -67,32 +62,14 @@
     }
 
     @Override
-    public void onClose()
-    {
-        super.onClose();
-        ConnectionManager connectionManager = connectPromise.getClient().getConnectionManager();
-        connectionManager.removeSession(getSession());
-    }
-
-    @Override
     public void onOpen()
     {
+        super.onOpen();
         boolean beenOpened = opened.getAndSet(true);
         if (!beenOpened)
         {
-            WebSocketSession session = getSession();
-            ConnectionManager connectionManager = connectPromise.getClient().getConnectionManager();
-            connectionManager.addSession(session);
-            connectPromise.succeeded(session);
-
-            ByteBuffer extraBuf = connectPromise.getResponse().getRemainingBuffer();
-            if (extraBuf.hasRemaining())
-            {
-                LOG.debug("Parsing extra remaining buffer from UpgradeConnection");
-                getParser().parse(extraBuf);
-            }
+            connectPromise.succeeded();
         }
-        super.onOpen();
     }
 
     /**
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java
index 4d3d84f..33981d9 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java
@@ -22,11 +22,13 @@
 import java.nio.channels.SelectionKey;
 import java.nio.channels.SocketChannel;
 import java.util.concurrent.Executor;
+
 import javax.net.ssl.SSLEngine;
 
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ManagedSelector;
 import org.eclipse.jetty.io.SelectChannelEndPoint;
 import org.eclipse.jetty.io.SelectorManager;
 import org.eclipse.jetty.io.ssl.SslConnection;
@@ -84,6 +86,7 @@
                     SSLEngine engine = newSSLEngine(sslContextFactory,channel);
                     SslConnection sslConnection = new SslConnection(bufferPool,getExecutor(),endPoint,engine);
                     sslConnection.setRenegotiationAllowed(sslContextFactory.isRenegotiationAllowed());
+                    sslConnection.setRenegotiationLimit(sslContextFactory.getRenegotiationLimit());
                     EndPoint sslEndPoint = sslConnection.getDecryptedEndPoint();
 
                     Connection connection = newUpgradeConnection(channel,sslEndPoint,connectPromise);
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/package-info.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/package-info.java
index 30ff1a6..dde7792 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/package-info.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/package-info.java
@@ -20,14 +20,15 @@
  * Jetty WebSocket Client API
  * <p>
  * The core class is {@link org.eclipse.jetty.websocket.client.WebSocketClient}, which acts as a central configuration object (for example
- * for {@link org.eclipse.jetty.websocket.client.WebSocketClient#setConnectTimeout(int) connect timeouts}, {@link WebSocketClient#setCookieStore(CookieStore)
- * request cookie store}, etc.) and as a factory for WebSocket {@link org.eclipse.jetty.websocket.api.Session} objects.
+ * for {@link org.eclipse.jetty.websocket.client.WebSocketClient#setConnectTimeout(long)}, 
+ * {@link org.eclipse.jetty.websocket.client.WebSocketClient#setCookieStore(java.net.CookieStore)}, 
+ * etc.) and as a factory for WebSocket {@link org.eclipse.jetty.websocket.api.Session} objects.
  * <p>
  * The <a href="https://tools.ietf.org/html/rfc6455">WebSocket protocol</a> is based on a framing protocol built
  * around an upgraded HTTP connection.  It is primarily focused on the sending of messages (text or binary), with an
  * occasional control frame (close, ping, pong) that this implementation uses.  
- * <p />
- * {@link org.eclipse.jetty.websocket.client.WebSocketClient} holds a number of {@link org.eclipse.jetty.websocket.api.Session Sessions}, which in turn
+ * <p>
+ * {@link org.eclipse.jetty.websocket.client.WebSocketClient} holds a number of {@link org.eclipse.jetty.websocket.api.Session}, which in turn
  * is used to manage physical vs virtual connection handling (mux extension).
  */
 package org.eclipse.jetty.websocket.client;
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/BadNetworkTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/BadNetworkTest.java
index fb4d5fc..e21b11d 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/BadNetworkTest.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/BadNetworkTest.java
@@ -26,7 +26,7 @@
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.api.StatusCode;
 import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
 import org.junit.After;
 import org.junit.Before;
@@ -82,12 +82,12 @@
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(wsocket,wsUri);
 
-        ServerConnection ssocket = server.accept();
+        IBlockheadServerConnection ssocket = server.accept();
         ssocket.upgrade();
 
         // Validate that we are connected
-        future.get(500,TimeUnit.MILLISECONDS);
-        wsocket.waitForConnected(500,TimeUnit.MILLISECONDS);
+        future.get(30,TimeUnit.SECONDS);
+        wsocket.waitForConnected(30,TimeUnit.SECONDS);
 
         // Have client disconnect abruptly
         Session session = wsocket.getSession();
@@ -110,12 +110,12 @@
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(wsocket,wsUri);
 
-        ServerConnection ssocket = server.accept();
+        IBlockheadServerConnection ssocket = server.accept();
         ssocket.upgrade();
 
         // Validate that we are connected
-        future.get(500,TimeUnit.MILLISECONDS);
-        wsocket.waitForConnected(500,TimeUnit.MILLISECONDS);
+        future.get(30,TimeUnit.SECONDS);
+        wsocket.waitForConnected(30,TimeUnit.SECONDS);
 
         // Have server disconnect abruptly
         ssocket.disconnect();
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientCloseTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientCloseTest.java
index 113caaa..78d9681 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientCloseTest.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientCloseTest.java
@@ -18,10 +18,17 @@
 
 package org.eclipse.jetty.websocket.client;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
 
 import java.io.IOException;
 import java.lang.reflect.Field;
+import java.net.SocketTimeoutException;
 import java.nio.ByteBuffer;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.SocketChannel;
@@ -32,11 +39,12 @@
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.ManagedSelector;
 import org.eclipse.jetty.io.SelectChannelEndPoint;
-import org.eclipse.jetty.io.SelectorManager.ManagedSelector;
 import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.toolchain.test.TestTracker;
 import org.eclipse.jetty.util.BufferUtil;
@@ -58,12 +66,11 @@
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection;
 import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.common.test.RawFrameBuilder;
 import org.hamcrest.Matcher;
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -81,54 +88,38 @@
         public CountDownLatch closeLatch = new CountDownLatch(1);
         public AtomicInteger closeCount = new AtomicInteger(0);
         public CountDownLatch openLatch = new CountDownLatch(1);
+        public CountDownLatch errorLatch = new CountDownLatch(1);
 
         public EventQueue<String> messageQueue = new EventQueue<>();
-        public EventQueue<Throwable> errorQueue = new EventQueue<>();
+        public AtomicReference<Throwable> error = new AtomicReference<>();
 
         public void assertNoCloseEvent()
         {
-            Assert.assertThat("Client Close Event",closeLatch.getCount(),is(1L));
-            Assert.assertThat("Client Close Event Status Code ",closeCode,is(-1));
+            assertThat("Client Close Event",closeLatch.getCount(),is(1L));
+            assertThat("Client Close Event Status Code ",closeCode,is(-1));
         }
 
         public void assertReceivedCloseEvent(int clientTimeoutMs, Matcher<Integer> statusCodeMatcher, Matcher<String> reasonMatcher)
                 throws InterruptedException
         {
-            long maxTimeout = clientTimeoutMs * 2;
+            long maxTimeout = clientTimeoutMs * 4;
 
-            Assert.assertThat("Client Close Event Occurred",closeLatch.await(maxTimeout,TimeUnit.MILLISECONDS),is(true));
-            Assert.assertThat("Client Close Event Count",closeCount.get(),is(1));
-            Assert.assertThat("Client Close Event Status Code",closeCode,statusCodeMatcher);
+            assertThat("Client Close Event Occurred",closeLatch.await(maxTimeout,TimeUnit.MILLISECONDS),is(true));
+            assertThat("Client Close Event Count",closeCount.get(),is(1));
+            assertThat("Client Close Event Status Code",closeCode,statusCodeMatcher);
             if (reasonMatcher == null)
             {
-                Assert.assertThat("Client Close Event Reason",closeReason,nullValue());
+                assertThat("Client Close Event Reason",closeReason,nullValue());
             }
             else
             {
-                Assert.assertThat("Client Close Event Reason",closeReason,reasonMatcher);
-            }
-        }
-
-        public void assertReceivedError(Class<? extends Throwable> expectedThrownClass, Matcher<String> messageMatcher) throws TimeoutException,
-                InterruptedException
-        {
-            errorQueue.awaitEventCount(1,500,TimeUnit.MILLISECONDS);
-            Throwable actual = errorQueue.poll();
-            Assert.assertThat("Client Error Event",actual,instanceOf(expectedThrownClass));
-            if (messageMatcher == null)
-            {
-                Assert.assertThat("Client Error Event Message",actual.getMessage(),nullValue());
-            }
-            else
-            {
-                Assert.assertThat("Client Error Event Message",actual.getMessage(),messageMatcher);
+                assertThat("Client Close Event Reason",closeReason,reasonMatcher);
             }
         }
 
         public void clearQueues()
         {
             messageQueue.clear();
-            errorQueue.clear();
         }
 
         @Override
@@ -152,8 +143,9 @@
         @Override
         public void onWebSocketError(Throwable cause)
         {
-            LOG.debug("onWebSocketError",cause);
-            Assert.assertThat("Error capture",errorQueue.offer(cause),is(true));
+            LOG.warn("onWebSocketError",cause);
+            assertThat("Unique Error Event", error.compareAndSet(null, cause), is(true));
+            errorLatch.countDown();
         }
 
         @Override
@@ -166,15 +158,15 @@
         public EndPoint getEndPoint() throws Exception
         {
             Session session = getSession();
-            Assert.assertThat("Session type",session,instanceOf(WebSocketSession.class));
+            assertThat("Session type",session,instanceOf(WebSocketSession.class));
 
             WebSocketSession wssession = (WebSocketSession)session;
             Field fld = wssession.getClass().getDeclaredField("connection");
             fld.setAccessible(true);
-            Assert.assertThat("Field: connection",fld,notNullValue());
+            assertThat("Field: connection",fld,notNullValue());
 
             Object val = fld.get(wssession);
-            Assert.assertThat("Connection type",val,instanceOf(AbstractWebSocketConnection.class));
+            assertThat("Connection type",val,instanceOf(AbstractWebSocketConnection.class));
             @SuppressWarnings("resource")
             AbstractWebSocketConnection wsconn = (AbstractWebSocketConnection)val;
             return wsconn.getEndPoint();
@@ -187,13 +179,13 @@
     private BlockheadServer server;
     private WebSocketClient client;
 
-    private void confirmConnection(CloseTrackingSocket clientSocket, Future<Session> clientFuture, ServerConnection serverConn) throws Exception
+    private void confirmConnection(CloseTrackingSocket clientSocket, Future<Session> clientFuture, IBlockheadServerConnection serverConns) throws Exception
     {
         // Wait for client connect on via future
-        clientFuture.get(500,TimeUnit.MILLISECONDS);
+        clientFuture.get(30,TimeUnit.SECONDS);
 
         // Wait for client connect via client websocket
-        Assert.assertThat("Client WebSocket is Open",clientSocket.openLatch.await(500,TimeUnit.MILLISECONDS),is(true));
+        assertThat("Client WebSocket is Open",clientSocket.openLatch.await(30,TimeUnit.SECONDS),is(true));
 
         try
         {
@@ -202,28 +194,28 @@
             Future<Void> testFut = clientSocket.getRemote().sendStringByFuture(echoMsg);
 
             // Wait for send future
-            testFut.get(500,TimeUnit.MILLISECONDS);
+            testFut.get(30,TimeUnit.SECONDS);
 
             // Read Frame on server side
-            IncomingFramesCapture serverCapture = serverConn.readFrames(1,500,TimeUnit.MILLISECONDS);
+            IncomingFramesCapture serverCapture = serverConns.readFrames(1,30,TimeUnit.SECONDS);
             serverCapture.assertNoErrors();
             serverCapture.assertFrameCount(1);
             WebSocketFrame frame = serverCapture.getFrames().poll();
-            Assert.assertThat("Server received frame",frame.getOpCode(),is(OpCode.TEXT));
-            Assert.assertThat("Server received frame payload",frame.getPayloadAsUTF8(),is(echoMsg));
+            assertThat("Server received frame",frame.getOpCode(),is(OpCode.TEXT));
+            assertThat("Server received frame payload",frame.getPayloadAsUTF8(),is(echoMsg));
 
             // Server send echo reply
-            serverConn.write(new TextFrame().setPayload(echoMsg));
+            serverConns.write(new TextFrame().setPayload(echoMsg));
 
             // Wait for received echo
             clientSocket.messageQueue.awaitEventCount(1,1,TimeUnit.SECONDS);
 
             // Verify received message
             String recvMsg = clientSocket.messageQueue.poll();
-            Assert.assertThat("Received message",recvMsg,is(echoMsg));
+            assertThat("Received message",recvMsg,is(echoMsg));
 
             // Verify that there are no errors
-            Assert.assertThat("Error events",clientSocket.errorQueue,empty());
+            assertThat("Error events",clientSocket.error.get(),nullValue());
         }
         finally
         {
@@ -231,24 +223,24 @@
         }
     }
 
-    private void confirmServerReceivedCloseFrame(ServerConnection serverConn, int expectedCloseCode, Matcher<String> closeReasonMatcher) throws IOException,
+    private void confirmServerReceivedCloseFrame(IBlockheadServerConnection serverConn, int expectedCloseCode, Matcher<String> closeReasonMatcher) throws IOException,
             TimeoutException
     {
-        IncomingFramesCapture serverCapture = serverConn.readFrames(1,500,TimeUnit.MILLISECONDS);
+        IncomingFramesCapture serverCapture = serverConn.readFrames(1,30,TimeUnit.SECONDS);
         serverCapture.assertNoErrors();
         serverCapture.assertFrameCount(1);
         serverCapture.assertHasFrame(OpCode.CLOSE,1);
         WebSocketFrame frame = serverCapture.getFrames().poll();
-        Assert.assertThat("Server received close frame",frame.getOpCode(),is(OpCode.CLOSE));
+        assertThat("Server received close frame",frame.getOpCode(),is(OpCode.CLOSE));
         CloseInfo closeInfo = new CloseInfo(frame);
-        Assert.assertThat("Server received close code",closeInfo.getStatusCode(),is(expectedCloseCode));
+        assertThat("Server received close code",closeInfo.getStatusCode(),is(expectedCloseCode));
         if (closeReasonMatcher == null)
         {
-            Assert.assertThat("Server received close reason",closeInfo.getReason(),nullValue());
+            assertThat("Server received close reason",closeInfo.getReason(),nullValue());
         }
         else
         {
-            Assert.assertThat("Server received close reason",closeInfo.getReason(),closeReasonMatcher);
+            assertThat("Server received close reason",closeInfo.getReason(),closeReasonMatcher);
         }
     }
 
@@ -348,7 +340,7 @@
         Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
 
         // Server accepts connect
-        ServerConnection serverConn = server.accept();
+        IBlockheadServerConnection serverConn = server.accept();
         serverConn.upgrade();
 
         // client confirms connection via echo
@@ -374,12 +366,12 @@
 
         // Verify received messages
         String recvMsg = clientSocket.messageQueue.poll();
-        Assert.assertThat("Received message 1",recvMsg,is("Hello"));
+        assertThat("Received message 1",recvMsg,is("Hello"));
         recvMsg = clientSocket.messageQueue.poll();
-        Assert.assertThat("Received message 2",recvMsg,is("World"));
+        assertThat("Received message 2",recvMsg,is("World"));
 
         // Verify that there are no errors
-        Assert.assertThat("Error events",clientSocket.errorQueue,empty());
+        assertThat("Error events",clientSocket.error.get(),nullValue());
 
         // client close event on ws-endpoint
         clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.NORMAL),containsString("From Server"));
@@ -397,7 +389,7 @@
         Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
 
         // Server accepts connect
-        ServerConnection serverConn = server.accept();
+        IBlockheadServerConnection serverConn = server.accept();
         serverConn.upgrade();
 
         // client confirms connection via echo
@@ -408,7 +400,7 @@
         // when write is congested, client enqueue close frame
         // client initiate write, but write never completes
         EndPoint endp = clientSocket.getEndPoint();
-        Assert.assertThat("EndPoint is testable",endp,instanceOf(TestEndPoint.class));
+        assertThat("EndPoint is testable",endp,instanceOf(TestEndPoint.class));
         TestEndPoint testendp = (TestEndPoint)endp;
 
         char msg[] = new char[10240];
@@ -424,16 +416,11 @@
             writeCount++;
             writeSize += msg.length;
         }
-        LOG.debug("Wrote {} frames totalling {} bytes of payload before congestion kicked in",writeCount,writeSize);
+        LOG.info("Wrote {} frames totalling {} bytes of payload before congestion kicked in",writeCount,writeSize);
 
-        // Verify that there are no errors
-        Assert.assertThat("Error events",clientSocket.errorQueue,empty());
-
-        // client idle timeout triggers close event on client ws-endpoint
-        // client close event on ws-endpoint
-        clientSocket.assertReceivedCloseEvent(timeout,
-                anyOf(is(StatusCode.SHUTDOWN),is(StatusCode.ABNORMAL)),
-                anyOf(containsString("Timeout"),containsString("timeout"),containsString("Write")));
+        // Verify timeout error
+        assertThat("OnError Latch", clientSocket.errorLatch.await(2, TimeUnit.SECONDS), is(true));
+        assertThat("OnError", clientSocket.error.get(), instanceOf(SocketTimeoutException.class));
     }
 
     @Test
@@ -448,7 +435,7 @@
         Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
 
         // Server accepts connect
-        ServerConnection serverConn = server.accept();
+        IBlockheadServerConnection serverConn = server.accept();
         serverConn.upgrade();
 
         // client confirms connection via echo
@@ -471,7 +458,9 @@
             serverConn.write(bad);
 
             // client should have noticed the error
-            clientSocket.assertReceivedError(ProtocolException.class,containsString("Invalid control frame"));
+            assertThat("OnError Latch", clientSocket.errorLatch.await(2, TimeUnit.SECONDS), is(true));
+            assertThat("OnError", clientSocket.error.get(), instanceOf(ProtocolException.class));
+            assertThat("OnError", clientSocket.error.get().getMessage(), containsString("Invalid control frame"));
 
             // client parse invalid frame, notifies server of close (protocol error)
             confirmServerReceivedCloseFrame(serverConn,StatusCode.PROTOCOL,allOf(containsString("Invalid control frame"),containsString("length")));
@@ -496,7 +485,7 @@
         Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
 
         // Server accepts connect
-        ServerConnection serverConn = server.accept();
+        IBlockheadServerConnection serverConn = server.accept();
         serverConn.upgrade();
 
         // client confirms connection via echo
@@ -532,7 +521,7 @@
         Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
 
         // Server accepts connect
-        ServerConnection serverConn = server.accept();
+        IBlockheadServerConnection serverConn = server.accept();
         serverConn.upgrade();
 
         // client confirms connection via echo
@@ -552,7 +541,9 @@
         // server sits idle
 
         // client idle timeout triggers close event on client ws-endpoint
-        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.SHUTDOWN),containsString("Timeout"));
+        assertThat("OnError Latch", clientSocket.errorLatch.await(2, TimeUnit.SECONDS), is(true));
+        assertThat("OnError", clientSocket.error.get(), instanceOf(SocketTimeoutException.class));
+        assertThat("OnError", clientSocket.error.get().getMessage(), containsString("Timeout on Read"));
     }
 
     @Test
@@ -564,7 +555,7 @@
 
         int clientCount = 3;
         CloseTrackingSocket clientSockets[] = new CloseTrackingSocket[clientCount];
-        ServerConnection serverConns[] = new ServerConnection[clientCount];
+        IBlockheadServerConnection serverConns[] = new IBlockheadServerConnection[clientCount];
 
         // Connect Multiple Clients
         for (int i = 0; i < clientCount; i++)
@@ -610,7 +601,7 @@
         Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
 
         // Server accepts connect
-        ServerConnection serverConn = server.accept();
+        IBlockheadServerConnection serverConn = server.accept();
         serverConn.upgrade();
 
         // client confirms connection via echo
@@ -624,8 +615,9 @@
         // client write failure
         final String origCloseReason = "Normal Close";
         clientSocket.getSession().close(StatusCode.NORMAL,origCloseReason);
-
-        clientSocket.assertReceivedError(EofException.class,null);
+    
+        assertThat("OnError Latch", clientSocket.errorLatch.await(2, TimeUnit.SECONDS), is(true));
+        assertThat("OnError", clientSocket.error.get(), instanceOf(EofException.class));
 
         // client triggers close event on client ws-endpoint
         // assert - close code==1006 (abnormal)
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientConnectTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientConnectTest.java
index 39c4f7b..49c4d2f 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientConnectTest.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientConnectTest.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.websocket.client;
 
 import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
 
 import java.io.IOException;
 import java.net.ConnectException;
@@ -36,7 +37,7 @@
 import org.eclipse.jetty.websocket.api.UpgradeException;
 import org.eclipse.jetty.websocket.common.AcceptHash;
 import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
 import org.junit.After;
 import org.junit.Assert;
@@ -47,6 +48,7 @@
 /**
  * Various connect condition testing
  */
+@SuppressWarnings("Duplicates")
 public class ClientConnectTest
 {
     @Rule
@@ -111,6 +113,52 @@
     }
 
     @Test
+    public void testUpgradeRequest() throws Exception
+    {
+        JettyTrackingSocket wsocket = new JettyTrackingSocket();
+
+        URI wsUri = server.getWsUri();
+        Future<Session> future = client.connect(wsocket,wsUri);
+
+        IBlockheadServerConnection connection = server.accept();
+        connection.upgrade();
+
+        Session sess = future.get(30,TimeUnit.SECONDS);
+        
+        sess.close();
+        
+        assertThat("Connect.UpgradeRequest", wsocket.connectUpgradeRequest, notNullValue());
+        assertThat("Connect.UpgradeResponse", wsocket.connectUpgradeResponse, notNullValue());
+    }
+    
+    @Test
+    public void testUpgradeWithAuthorizationHeader() throws Exception
+    {
+        JettyTrackingSocket wsocket = new JettyTrackingSocket();
+        
+        URI wsUri = server.getWsUri();
+        ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
+        // actual value for this test is irrelevant, its important that this
+        // header actually be sent with a value (the value specified)
+        upgradeRequest.setHeader("Authorization", "Bogus SHA1");
+        Future<Session> future = client.connect(wsocket,wsUri,upgradeRequest);
+        
+        IBlockheadServerConnection connection = server.accept();
+        List<String> requestLines = connection.upgrade();
+    
+        Session sess = future.get(30,TimeUnit.SECONDS);
+        sess.close();
+
+        String authLine = requestLines.stream()
+                .filter((line) -> line.startsWith("Authorization:"))
+                .findFirst().get();
+        
+        assertThat("Request Container Authorization", authLine, is("Authorization: Bogus SHA1"));
+        assertThat("Connect.UpgradeRequest", wsocket.connectUpgradeRequest, notNullValue());
+        assertThat("Connect.UpgradeResponse", wsocket.connectUpgradeResponse, notNullValue());
+    }
+    
+    @Test
     public void testBadHandshake() throws Exception
     {
         JettyTrackingSocket wsocket = new JettyTrackingSocket();
@@ -118,7 +166,7 @@
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(wsocket,wsUri);
 
-        ServerConnection connection = server.accept();
+        IBlockheadServerConnection connection = server.accept();
         connection.readRequest();
         // no upgrade, just fail with a 404 error
         connection.respond("HTTP/1.1 404 NOT FOUND\r\n\r\n");
@@ -126,7 +174,7 @@
         // The attempt to get upgrade response future should throw error
         try
         {
-            future.get(500,TimeUnit.MILLISECONDS);
+            future.get(30,TimeUnit.SECONDS);
             Assert.fail("Expected ExecutionException -> UpgradeException");
         }
         catch (ExecutionException e)
@@ -147,7 +195,7 @@
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(wsocket,wsUri);
 
-        ServerConnection connection = server.accept();
+        IBlockheadServerConnection connection = server.accept();
         connection.readRequest();
         // Send OK to GET but not upgrade
         connection.respond("HTTP/1.1 200 OK\r\n\r\n");
@@ -155,7 +203,7 @@
         // The attempt to get upgrade response future should throw error
         try
         {
-            future.get(500,TimeUnit.MILLISECONDS);
+            future.get(30,TimeUnit.SECONDS);
             Assert.fail("Expected ExecutionException -> UpgradeException");
         }
         catch (ExecutionException e)
@@ -176,7 +224,7 @@
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(wsocket,wsUri);
 
-        ServerConnection connection = server.accept();
+        IBlockheadServerConnection connection = server.accept();
         List<String> requestLines = connection.readRequestLines();
         String key = connection.parseWebSocketKey(requestLines);
 
@@ -191,7 +239,7 @@
         // The attempt to get upgrade response future should throw error
         try
         {
-            future.get(500,TimeUnit.MILLISECONDS);
+            future.get(30,TimeUnit.SECONDS);
             Assert.fail("Expected ExecutionException -> UpgradeException");
         }
         catch (ExecutionException e)
@@ -212,7 +260,7 @@
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(wsocket,wsUri);
 
-        ServerConnection connection = server.accept();
+        IBlockheadServerConnection connection = server.accept();
         List<String> requestLines = connection.readRequestLines();
         String key = connection.parseWebSocketKey(requestLines);
 
@@ -227,7 +275,7 @@
         // The attempt to get upgrade response future should throw error
         try
         {
-            future.get(500,TimeUnit.MILLISECONDS);
+            future.get(30,TimeUnit.SECONDS);
             Assert.fail("Expected ExecutionException -> UpgradeException");
         }
         catch (ExecutionException e)
@@ -248,7 +296,7 @@
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(wsocket,wsUri);
 
-        ServerConnection connection = server.accept();
+        IBlockheadServerConnection connection = server.accept();
         List<String> requestLines = connection.readRequestLines();
         String key = connection.parseWebSocketKey(requestLines);
 
@@ -263,7 +311,7 @@
         // The attempt to get upgrade response future should throw error
         try
         {
-            future.get(500,TimeUnit.MILLISECONDS);
+            future.get(30,TimeUnit.SECONDS);
             Assert.fail("Expected ExecutionException -> UpgradeException");
         }
         catch (ExecutionException e)
@@ -284,7 +332,7 @@
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(wsocket,wsUri);
 
-        ServerConnection connection = server.accept();
+        IBlockheadServerConnection connection = server.accept();
         connection.readRequest();
         // Upgrade badly
         connection.respond("HTTP/1.1 101 Upgrade\r\n" + "Sec-WebSocket-Accept: rubbish\r\n" + "\r\n");
@@ -292,7 +340,7 @@
         // The attempt to get upgrade response future should throw error
         try
         {
-            future.get(500,TimeUnit.MILLISECONDS);
+            future.get(30,TimeUnit.SECONDS);
             Assert.fail("Expected ExecutionException -> UpgradeException");
         }
         catch (ExecutionException e)
@@ -318,7 +366,7 @@
 
         try
         {
-            future.get(500,TimeUnit.MILLISECONDS);
+            future.get(3,TimeUnit.SECONDS);
             Assert.fail("Should have Timed Out");
         }
         catch (ExecutionException e)
@@ -347,7 +395,7 @@
             Future<Session> future = client.connect(wsocket,wsUri);
 
             // The attempt to get upgrade response future should throw error
-            future.get(1000,TimeUnit.MILLISECONDS);
+            future.get(3,TimeUnit.SECONDS);
             Assert.fail("Expected ExecutionException -> ConnectException");
         }
         catch (ConnectException e)
@@ -378,7 +426,7 @@
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(wsocket,wsUri);
 
-        ServerConnection ssocket = server.accept();
+        IBlockheadServerConnection ssocket = server.accept();
         Assert.assertNotNull(ssocket);
         // Intentionally don't upgrade
         // ssocket.upgrade();
@@ -386,7 +434,7 @@
         // The attempt to get upgrade response future should throw error
         try
         {
-            future.get(500,TimeUnit.MILLISECONDS);
+            future.get(3,TimeUnit.SECONDS);
             Assert.fail("Expected ExecutionException -> TimeoutException");
         }
         catch (ExecutionException e)
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/CookieTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/CookieTest.java
index ad59b33..87ebd83 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/CookieTest.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/CookieTest.java
@@ -37,7 +37,7 @@
 import org.eclipse.jetty.websocket.api.util.QuoteUtil;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -119,7 +119,7 @@
         Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
 
         // Server accepts connect
-        ServerConnection serverConn = server.accept();
+        IBlockheadServerConnection serverConn = server.accept();
 
         // client confirms upgrade and receipt of frame
         String serverCookies = confirmClientUpgradeAndCookies(clientSocket,clientConnectFuture,serverConn);
@@ -144,7 +144,7 @@
         Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri(),request);
 
         // Server accepts connect
-        ServerConnection serverConn = server.accept();
+        IBlockheadServerConnection serverConn = server.accept();
 
         // client confirms upgrade and receipt of frame
         String serverCookies = confirmClientUpgradeAndCookies(clientSocket,clientConnectFuture,serverConn);
@@ -152,7 +152,7 @@
         Assert.assertThat("Cookies seen at server side",serverCookies,containsString("hello=\"world\""));
     }
 
-    private String confirmClientUpgradeAndCookies(CookieTrackingSocket clientSocket, Future<Session> clientConnectFuture, ServerConnection serverConn)
+    private String confirmClientUpgradeAndCookies(CookieTrackingSocket clientSocket, Future<Session> clientConnectFuture, IBlockheadServerConnection serverConn)
             throws Exception
     {
         // Server upgrades
@@ -169,7 +169,7 @@
         serverConn.close(StatusCode.NORMAL);
 
         // Confirm client connect on future
-        clientConnectFuture.get(500,TimeUnit.MILLISECONDS);
+        clientConnectFuture.get(30000,TimeUnit.MILLISECONDS);
 
         // Wait for client receipt of cookie frame via client websocket
         clientSocket.messageQueue.awaitEventCount(1,2,TimeUnit.SECONDS);
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/JettyTrackingSocket.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/JettyTrackingSocket.java
index cfbb18d..49de951 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/JettyTrackingSocket.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/JettyTrackingSocket.java
@@ -30,6 +30,8 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.UpgradeRequest;
+import org.eclipse.jetty.websocket.api.UpgradeResponse;
 import org.eclipse.jetty.websocket.api.WebSocketAdapter;
 import org.junit.Assert;
 
@@ -42,6 +44,8 @@
 
     public int closeCode = -1;
     public Exchanger<String> messageExchanger;
+    public UpgradeRequest connectUpgradeRequest;
+    public UpgradeResponse connectUpgradeResponse;
     public StringBuilder closeMessage = new StringBuilder();
     public CountDownLatch openLatch = new CountDownLatch(1);
     public CountDownLatch closeLatch = new CountDownLatch(1);
@@ -90,7 +94,7 @@
 
     public void assertWasOpened() throws InterruptedException
     {
-        Assert.assertThat("Was Opened",openLatch.await(500,TimeUnit.MILLISECONDS),is(true));
+        Assert.assertThat("Was Opened",openLatch.await(30,TimeUnit.SECONDS),is(true));
     }
 
     public void awaitMessage(int expectedMessageCount, TimeUnit timeoutUnit, int timeoutDuration) throws TimeoutException, InterruptedException
@@ -124,6 +128,8 @@
     public void onWebSocketConnect(Session session)
     {
         super.onWebSocketConnect(session);
+        connectUpgradeRequest = session.getUpgradeRequest();
+        connectUpgradeResponse = session.getUpgradeResponse();
         openLatch.countDown();
     }
 
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerReadThread.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerReadThread.java
index ad44266..5c1be8b 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerReadThread.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerReadThread.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.client;
 
+import static org.hamcrest.Matchers.*;
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.Queue;
@@ -32,24 +34,22 @@
 import org.eclipse.jetty.websocket.common.CloseInfo;
 import org.eclipse.jetty.websocket.common.OpCode;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.junit.Assert;
 
-import static org.hamcrest.Matchers.is;
-
 public class ServerReadThread extends Thread
 {
     private static final int BUFFER_SIZE = 8192;
     private static final Logger LOG = Log.getLogger(ServerReadThread.class);
-    private final ServerConnection conn;
+    private final IBlockheadServerConnection conn;
     private boolean active = true;
     private int slowness = -1; // disabled is default
     private final AtomicInteger frameCount = new AtomicInteger();
     private final CountDownLatch expectedMessageCount;
 
-    public ServerReadThread(ServerConnection conn, int expectedMessages)
+    public ServerReadThread(IBlockheadServerConnection sconnection, int expectedMessages)
     {
-        this.conn = conn;
+        this.conn = sconnection;
         this.expectedMessageCount = new CountDownLatch(expectedMessages);
     }
 
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java
index 227947a..f16d854 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java
@@ -25,17 +25,17 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 
 public class ServerWriteThread extends Thread
 {
     private static final Logger LOG = Log.getLogger(ServerWriteThread.class);
-    private final ServerConnection conn;
+    private final IBlockheadServerConnection conn;
     private int slowness = -1;
     private int messageCount = 100;
     private String message = "Hello";
 
-    public ServerWriteThread(ServerConnection conn)
+    public ServerWriteThread(IBlockheadServerConnection conn)
     {
         this.conn = conn;
     }
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SessionTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SessionTest.java
index bcc1fd5..4f17a31 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SessionTest.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SessionTest.java
@@ -18,7 +18,10 @@
 
 package org.eclipse.jetty.websocket.client;
 
+import static org.hamcrest.Matchers.*;
+
 import java.net.URI;
+import java.util.Collection;
 import java.util.Set;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -28,15 +31,12 @@
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-
 public class SessionTest
 {
     private BlockheadServer server;
@@ -70,10 +70,10 @@
             request.setSubProtocols("echo");
             Future<Session> future = client.connect(cliSock,wsUri,request);
 
-            final ServerConnection srvSock = server.accept();
+            final IBlockheadServerConnection srvSock = server.accept();
             srvSock.upgrade();
 
-            Session sess = future.get(500,TimeUnit.MILLISECONDS);
+            Session sess = future.get(30000,TimeUnit.MILLISECONDS);
             Assert.assertThat("Session",sess,notNullValue());
             Assert.assertThat("Session.open",sess.isOpen(),is(true));
             Assert.assertThat("Session.upgradeRequest",sess.getUpgradeRequest(),notNullValue());
@@ -82,26 +82,30 @@
             cliSock.assertWasOpened();
             cliSock.assertNotClosed();
 
-            Assert.assertThat("client.connectionManager.sessions.size",client.getConnectionManager().getSessions().size(),is(1));
+            Collection<WebSocketSession> sessions = client.getBeans(WebSocketSession.class);
+            Assert.assertThat("client.connectionManager.sessions.size",sessions.size(),is(1));
 
             RemoteEndpoint remote = cliSock.getSession().getRemote();
             remote.sendStringByFuture("Hello World!");
             if (remote.getBatchMode() == BatchMode.ON)
+            {
                 remote.flush();
-            srvSock.echoMessage(1,500,TimeUnit.MILLISECONDS);
+            }
+            srvSock.echoMessage(1,30000,TimeUnit.MILLISECONDS);
             // wait for response from server
-            cliSock.waitForMessage(500,TimeUnit.MILLISECONDS);
+            cliSock.waitForMessage(30000,TimeUnit.MILLISECONDS);
             
             Set<WebSocketSession> open = client.getOpenSessions();
-            Assert.assertThat("Open Sessions.size", open.size(), is(1));
+            Assert.assertThat("(Before Close) Open Sessions.size", open.size(), is(1));
 
             cliSock.assertMessage("Hello World!");
             cliSock.close();
             srvSock.close();
             
-            cliSock.waitForClose(500,TimeUnit.MILLISECONDS);
+            cliSock.waitForClose(30000,TimeUnit.MILLISECONDS);
             open = client.getOpenSessions();
-            Assert.assertThat("Open Sessions.size", open.size(), is(0));
+            // TODO this sometimes fails!
+            Assert.assertThat("(After Close) Open Sessions.size", open.size(), is(0));
         }
         finally
         {
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowClientTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowClientTest.java
index 0727fa6..966a5d1 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowClientTest.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowClientTest.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.client;
 
+import static org.hamcrest.Matchers.*;
+
 import java.net.URI;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -27,15 +29,13 @@
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.api.StatusCode;
 import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.is;
-
 public class SlowClientTest
 {
     @Rule
@@ -81,13 +81,13 @@
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(tsocket, wsUri);
 
-        ServerConnection sconnection = server.accept();
+        IBlockheadServerConnection sconnection = server.accept();
         sconnection.setSoTimeout(60000);
         sconnection.upgrade();
 
         // Confirm connected
-        future.get(500, TimeUnit.MILLISECONDS);
-        tsocket.waitForConnected(500, TimeUnit.MILLISECONDS);
+        future.get(30,TimeUnit.SECONDS);
+        tsocket.waitForConnected(30,TimeUnit.SECONDS);
 
         int messageCount = 10;
 
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java
index def4384..f2d76a2 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.client;
 
+import static org.hamcrest.Matchers.*;
+
 import java.net.URI;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -28,15 +30,13 @@
 import org.eclipse.jetty.websocket.api.StatusCode;
 import org.eclipse.jetty.websocket.client.masks.ZeroMasker;
 import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.is;
-
 public class SlowServerTest
 {
     @Rule
@@ -83,13 +83,13 @@
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(tsocket,wsUri);
 
-        ServerConnection sconnection = server.accept();
+        IBlockheadServerConnection sconnection = server.accept();
         sconnection.setSoTimeout(60000);
         sconnection.upgrade();
 
         // Confirm connected
-        future.get(500,TimeUnit.MILLISECONDS);
-        tsocket.waitForConnected(500,TimeUnit.MILLISECONDS);
+        future.get(30,TimeUnit.SECONDS);
+        tsocket.waitForConnected(30,TimeUnit.SECONDS);
 
         int messageCount = 10;
 
@@ -130,13 +130,13 @@
         URI wsUri = server.getWsUri();
         Future<Session> clientConnectFuture = client.connect(clientSocket,wsUri);
 
-        ServerConnection serverConn = server.accept();
+        IBlockheadServerConnection serverConn = server.accept();
         serverConn.setSoTimeout(60000);
         serverConn.upgrade();
 
         // Confirm connected
-        clientConnectFuture.get(500,TimeUnit.MILLISECONDS);
-        clientSocket.waitForConnected(500,TimeUnit.MILLISECONDS);
+        clientConnectFuture.get(30,TimeUnit.SECONDS);
+        clientSocket.waitForConnected(30,TimeUnit.SECONDS);
 
         // Have server write slowly.
         int messageCount = 1000;
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/TomcatServerQuirksTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/TomcatServerQuirksTest.java
index ea44e31..5ae8ade 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/TomcatServerQuirksTest.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/TomcatServerQuirksTest.java
@@ -28,7 +28,7 @@
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.api.WebSocketAdapter;
 import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -66,7 +66,7 @@
      * <li><a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=54067">Apache Tomcat Bug #54067</a></li>
      * </ul>
      * 
-     * @throws IOException
+     * @throws Exception on test failure
      */
     @Test
     public void testTomcat7_0_32_WithTransferEncoding() throws Exception
@@ -91,7 +91,7 @@
             client.connect(websocket,wsURI);
 
             // Accept incoming connection
-            ServerConnection socket = server.accept();
+            IBlockheadServerConnection socket = server.accept();
             socket.setSoTimeout(2000); // timeout
 
             // Issue upgrade
@@ -113,8 +113,7 @@
             serverFrame.put((byte)(payload.length & 0xFF)); // second length byte
             serverFrame.put(payload);
             BufferUtil.flipToFlush(serverFrame,0);
-            byte buf[] = BufferUtil.toArray(serverFrame);
-            socket.write(buf,0,buf.length);
+            socket.write(serverFrame);
             socket.flush();
 
             Assert.assertTrue(websocket.dataLatch.await(1000,TimeUnit.SECONDS));
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/WebSocketClientTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/WebSocketClientTest.java
index c6fc078..2a8e578 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/WebSocketClientTest.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/WebSocketClientTest.java
@@ -18,9 +18,12 @@
 
 package org.eclipse.jetty.websocket.client;
 
+import static org.hamcrest.Matchers.*;
+
 import java.net.InetSocketAddress;
 import java.net.URI;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Future;
@@ -32,21 +35,17 @@
 import org.eclipse.jetty.websocket.api.RemoteEndpoint;
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.api.UpgradeRequest;
+import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.io.FutureWriteCallback;
 import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.hamcrest.Matchers.greaterThan;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.hamcrest.Matchers.nullValue;
-
 @RunWith(AdvancedRunner.class)
 public class WebSocketClientTest
 {
@@ -106,10 +105,10 @@
             request.setSubProtocols("echo");
             Future<Session> future = client.connect(cliSock,wsUri,request);
 
-            final ServerConnection srvSock = server.accept();
+            final IBlockheadServerConnection srvSock = server.accept();
             srvSock.upgrade();
 
-            Session sess = future.get(500,TimeUnit.MILLISECONDS);
+            Session sess = future.get(30,TimeUnit.SECONDS);
             Assert.assertThat("Session",sess,notNullValue());
             Assert.assertThat("Session.open",sess.isOpen(),is(true));
             Assert.assertThat("Session.upgradeRequest",sess.getUpgradeRequest(),notNullValue());
@@ -118,15 +117,16 @@
             cliSock.assertWasOpened();
             cliSock.assertNotClosed();
 
-            Assert.assertThat("client.connectionManager.sessions.size",client.getConnectionManager().getSessions().size(),is(1));
+            Collection<WebSocketSession> sessions = client.getBeans(WebSocketSession.class);
+            Assert.assertThat("client.connectionManager.sessions.size",sessions.size(),is(1));
 
             RemoteEndpoint remote = cliSock.getSession().getRemote();
             remote.sendStringByFuture("Hello World!");
             if (remote.getBatchMode() == BatchMode.ON)
                 remote.flush();
-            srvSock.echoMessage(1,500,TimeUnit.MILLISECONDS);
+            srvSock.echoMessage(1,30,TimeUnit.SECONDS);
             // wait for response from server
-            cliSock.waitForMessage(500,TimeUnit.MILLISECONDS);
+            cliSock.waitForMessage(30,TimeUnit.SECONDS);
 
             cliSock.assertMessage("Hello World!");
         }
@@ -152,10 +152,10 @@
             request.setSubProtocols("echo");
             Future<Session> future = client.connect(cliSock,wsUri,request);
 
-            final ServerConnection srvSock = server.accept();
+            final IBlockheadServerConnection srvSock = server.accept();
             srvSock.upgrade();
 
-            Session sess = future.get(500,TimeUnit.MILLISECONDS);
+            Session sess = future.get(30,TimeUnit.SECONDS);
             Assert.assertThat("Session",sess,notNullValue());
             Assert.assertThat("Session.open",sess.isOpen(),is(true));
             Assert.assertThat("Session.upgradeRequest",sess.getUpgradeRequest(),notNullValue());
@@ -164,7 +164,8 @@
             cliSock.assertWasOpened();
             cliSock.assertNotClosed();
 
-            Assert.assertThat("client.connectionManager.sessions.size",client.getConnectionManager().getSessions().size(),is(1));
+            Collection<WebSocketSession> sessions = client.getBeans(WebSocketSession.class);
+            Assert.assertThat("client.connectionManager.sessions.size",sessions.size(),is(1));
 
             FutureWriteCallback callback = new FutureWriteCallback();
 
@@ -188,11 +189,11 @@
             Future<Session> future = client.connect(wsocket,server.getWsUri());
 
             // Server
-            final ServerConnection srvSock = server.accept();
+            final IBlockheadServerConnection srvSock = server.accept();
             srvSock.upgrade();
 
             // Validate connect
-            Session sess = future.get(500,TimeUnit.MILLISECONDS);
+            Session sess = future.get(30,TimeUnit.SECONDS);
             Assert.assertThat("Session",sess,notNullValue());
             Assert.assertThat("Session.open",sess.isOpen(),is(true));
             Assert.assertThat("Session.upgradeRequest",sess.getUpgradeRequest(),notNullValue());
@@ -202,7 +203,7 @@
             srvSock.write(new TextFrame().setPayload("Hello World"));
 
             // Verify connect
-            future.get(500,TimeUnit.MILLISECONDS);
+            future.get(30,TimeUnit.SECONDS);
             wsocket.assertWasOpened();
             wsocket.awaitMessage(1,TimeUnit.SECONDS,2);
 
@@ -226,10 +227,10 @@
             URI wsUri = server.getWsUri();
             Future<Session> future = fact.connect(wsocket,wsUri);
 
-            ServerConnection ssocket = server.accept();
+            IBlockheadServerConnection ssocket = server.accept();
             ssocket.upgrade();
 
-            future.get(500,TimeUnit.MILLISECONDS);
+            future.get(30,TimeUnit.SECONDS);
 
             Assert.assertTrue(wsocket.openLatch.await(1,TimeUnit.SECONDS));
 
@@ -266,10 +267,10 @@
             URI wsUri = server.getWsUri();
             Future<Session> future = factSmall.connect(wsocket,wsUri);
 
-            ServerConnection ssocket = server.accept();
+            IBlockheadServerConnection ssocket = server.accept();
             ssocket.upgrade();
 
-            future.get(500,TimeUnit.MILLISECONDS);
+            future.get(30,TimeUnit.SECONDS);
 
             Assert.assertTrue(wsocket.openLatch.await(1,TimeUnit.SECONDS));
 
@@ -304,12 +305,12 @@
             URI wsUri = server.getWsUri();
             Future<Session> future = client.connect(wsocket,wsUri);
 
-            ServerConnection ssocket = server.accept();
+            IBlockheadServerConnection ssocket = server.accept();
             ssocket.upgrade();
 
             wsocket.awaitConnect(1,TimeUnit.SECONDS);
 
-            Session sess = future.get(500,TimeUnit.MILLISECONDS);
+            Session sess = future.get(30,TimeUnit.SECONDS);
             Assert.assertThat("Session",sess,notNullValue());
             Assert.assertThat("Session.open",sess.isOpen(),is(true));
 
@@ -346,10 +347,10 @@
             URI wsUri = server.getWsUri().resolve("/test?snack=cashews&amount=handful&brand=off");
             Future<Session> future = fact.connect(wsocket,wsUri);
 
-            ServerConnection ssocket = server.accept();
+            IBlockheadServerConnection ssocket = server.accept();
             ssocket.upgrade();
 
-            future.get(500,TimeUnit.MILLISECONDS);
+            future.get(30,TimeUnit.SECONDS);
 
             Assert.assertTrue(wsocket.openLatch.await(1,TimeUnit.SECONDS));
 
diff --git a/jetty-websocket/websocket-common/pom.xml b/jetty-websocket/websocket-common/pom.xml
index e9090cc..131aec6 100644
--- a/jetty-websocket/websocket-common/pom.xml
+++ b/jetty-websocket/websocket-common/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <groupId>org.eclipse.jetty.websocket</groupId>
     <artifactId>websocket-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -71,7 +71,6 @@
           <execution>
             <id>artifact-jars</id>
             <goals>
-              <goal>jar</goal>
               <goal>test-jar</goal>
             </goals>
           </execution>
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/BlockingWriteCallback.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/BlockingWriteCallback.java
index ce608e7..b34f83f 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/BlockingWriteCallback.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/BlockingWriteCallback.java
@@ -24,9 +24,8 @@
 import org.eclipse.jetty.util.SharedBlockingCallback;
 import org.eclipse.jetty.websocket.api.WriteCallback;
 
-
-/* ------------------------------------------------------------ */
-/** extend a SharedlBlockingCallback to an websocket WriteCallback
+/**
+ * Extends a {@link SharedBlockingCallback} to a WebSocket {@link WriteCallback}
  */
 public class BlockingWriteCallback extends SharedBlockingCallback
 {
@@ -39,11 +38,11 @@
         return new WriteBlocker(acquire());
     }
     
-    public static class WriteBlocker implements WriteCallback, Callback, AutoCloseable
+    public static class WriteBlocker implements WriteCallback, Callback.NonBlocking, AutoCloseable
     {
-        Blocker blocker;
+        private final Blocker blocker;
         
-        WriteBlocker(Blocker blocker)
+        protected WriteBlocker(Blocker blocker)
         {
             this.blocker=blocker;
         }
@@ -73,7 +72,7 @@
         }
         
         @Override
-        public void close() throws IOException
+        public void close()
         {
             blocker.close();
         }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/ConnectionState.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/ConnectionState.java
index a3fcafa..220ccdf 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/ConnectionState.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/ConnectionState.java
@@ -18,6 +18,9 @@
 
 package org.eclipse.jetty.websocket.common;
 
+import org.eclipse.jetty.websocket.common.io.IOState;
+import org.eclipse.jetty.websocket.common.io.IOState.ConnectionStateListener;
+
 /**
  * Connection states as outlined in <a href="https://tools.ietf.org/html/rfc6455">RFC6455</a>.
  */
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/LogicalConnection.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/LogicalConnection.java
index 944f669..68dcb75 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/LogicalConnection.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/LogicalConnection.java
@@ -35,7 +35,7 @@
      * <p>
      * Basic usage: results in an non-blocking async write, then connection close.
      * 
-     * @see StatusCode
+     * @see org.eclipse.jetty.websocket.api.StatusCode
      * @see #close(int, String)
      */
     public void close();
@@ -49,7 +49,7 @@
      *            the status code
      * @param reason
      *            the (optional) reason. (can be null for no reason)
-     * @see StatusCode
+     * @see org.eclipse.jetty.websocket.api.StatusCode
      */
     public void close(int statusCode, String reason);
 
@@ -60,11 +60,13 @@
 
     /**
      * Get the ByteBufferPool in use by the connection
+     * @return the buffer pool
      */
     ByteBufferPool getBufferPool();
     
     /**
      * Get the Executor used by this connection.
+     * @return the executor
      */
     Executor getExecutor();
 
@@ -113,13 +115,6 @@
     InetSocketAddress getRemoteAddress();
 
     /**
-     * Get the Session for this connection
-     * 
-     * @return the Session for this connection
-     */
-    WebSocketSession getSession();
-
-    /**
      * Test if logical connection is still open
      * 
      *  @return true if connection is open
@@ -155,15 +150,14 @@
     void setNextIncomingFrames(IncomingFrames incoming);
 
     /**
-     * Set the session associated with this connection
-     * 
-     * @param session
-     *            the session
-     */
-    void setSession(WebSocketSession session);
-
-    /**
      * Suspend a the incoming read events on the connection.
+     * @return the suspend token
      */
     SuspendToken suspend();
+
+    /**
+     * Get Unique ID for the Connection
+     * @return the unique ID for the connection
+     */
+    public String getId();
 }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/OpCode.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/OpCode.java
index b76440e..db076da 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/OpCode.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/OpCode.java
@@ -99,7 +99,8 @@
                 return "CONTINUATION";
             case TEXT:
                 return "TEXT";
-            case BINARY: return "BINARY";
+            case BINARY:
+                return "BINARY";
             case CLOSE:
                 return "CLOSE";
             case PING:
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java
index ea1ba04..d952ed9 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java
@@ -248,8 +248,6 @@
         }
         try
         {
-            // TODO: create DebugBuffer
-
             // parse through all the frames in the buffer
             while (parseFrame(buffer))
             {
@@ -332,9 +330,9 @@
                                 policy.getBehavior(),
                                 OpCode.name(opcode),
                                 fin,
-                                (isRsv1InUse()?'1':'.'),
-                                (isRsv2InUse()?'1':'.'),
-                                (isRsv3InUse()?'1':'.'));
+                                (((b & 0x40) != 0)?'1':'.'),
+                                (((b & 0x20) != 0)?'1':'.'),
+                                (((b & 0x10) != 0)?'1':'.'));
 
                     // base framing flags
                     switch(opcode)
@@ -653,7 +651,7 @@
         builder.append(",c=").append(cursor);
         builder.append(",len=").append(payloadLength);
         builder.append(",f=").append(frame);
-        builder.append(",p=").append(policy);
+        // builder.append(",p=").append(policy);
         builder.append("]");
         return builder.toString();
     }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/RemoteEndpointFactory.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/RemoteEndpointFactory.java
new file mode 100644
index 0000000..13673c2
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/RemoteEndpointFactory.java
@@ -0,0 +1,28 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common;
+
+import org.eclipse.jetty.websocket.api.BatchMode;
+import org.eclipse.jetty.websocket.api.RemoteEndpoint;
+import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
+
+public interface RemoteEndpointFactory
+{
+    RemoteEndpoint newRemoteEndpoint(LogicalConnection connection, OutgoingFrames outgoingFrames, BatchMode batchMode);
+}
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/SessionListener.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/SessionListener.java
deleted file mode 100644
index 6c093ea..0000000
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/SessionListener.java
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.common;
-
-/**
- * Basic listener interface for Session open/close.
- * <p>
- * Used primarily for tracking open sessions.
- */
-public interface SessionListener
-{
-    public void onSessionOpened(WebSocketSession session);
-    
-    public void onSessionClosed(WebSocketSession session);
-}
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketFrame.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketFrame.java
index 39e4cfc..3d54428 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketFrame.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketFrame.java
@@ -96,7 +96,6 @@
 
     /**
      * Combined FIN + RSV1 + RSV2 + RSV3 + OpCode byte.
-     * <p>
      * 
      * <pre>
      *   1000_0000 (0x80) = fin
@@ -119,6 +118,7 @@
 
     /**
      * Construct form opcode
+     * @param opcode the opcode the frame is based on
      */
     protected WebSocketFrame(byte opcode)
     {
@@ -341,6 +341,7 @@
      * 
      * @param buf
      *            the bytebuffer to set
+     * @return the frame itself
      */
     public WebSocketFrame setPayload(ByteBuffer buf)
     {
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpoint.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpoint.java
index adbaba8..9e399e9 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpoint.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpoint.java
@@ -216,9 +216,15 @@
         }
     }
 
-
+    /**
+     * Get the InetSocketAddress for the established connection.
+     *
+     * @return the InetSocketAddress for the established connection. (or null, if the connection is no longer established)
+     */
     public InetSocketAddress getInetSocketAddress()
     {
+        if(connection == null)
+            return null;
         return connection.getRemoteAddress();
     }
 
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java
index feed366..992d376 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java
@@ -22,18 +22,24 @@
 import java.net.InetSocketAddress;
 import java.net.URI;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.ServiceLoader;
 import java.util.concurrent.Executor;
 
 import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.component.Dumpable;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.ThreadClassLoaderScope;
 import org.eclipse.jetty.websocket.api.BatchMode;
+import org.eclipse.jetty.websocket.api.CloseException;
 import org.eclipse.jetty.websocket.api.CloseStatus;
 import org.eclipse.jetty.websocket.api.RemoteEndpoint;
 import org.eclipse.jetty.websocket.api.Session;
@@ -51,57 +57,62 @@
 import org.eclipse.jetty.websocket.common.events.EventDriver;
 import org.eclipse.jetty.websocket.common.io.IOState;
 import org.eclipse.jetty.websocket.common.io.IOState.ConnectionStateListener;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketSessionScope;
 
 @ManagedObject("A Jetty WebSocket Session")
-public class WebSocketSession extends ContainerLifeCycle implements Session, IncomingFrames, ConnectionStateListener
+public class WebSocketSession extends ContainerLifeCycle implements Session, RemoteEndpointFactory, WebSocketSessionScope, IncomingFrames, Connection.Listener, ConnectionStateListener
 {
     private static final Logger LOG = Log.getLogger(WebSocketSession.class);
+    private static final Logger LOG_OPEN = Log.getLogger(WebSocketSession.class.getName() + "_OPEN");
+    private final WebSocketContainerScope containerScope;
     private final URI requestURI;
-    private final EventDriver websocket;
     private final LogicalConnection connection;
-    private final SessionListener[] sessionListeners;
+    private final EventDriver websocket;
     private final Executor executor;
     private ClassLoader classLoader;
     private ExtensionFactory extensionFactory;
+    private RemoteEndpointFactory remoteEndpointFactory;
     private String protocolVersion;
     private Map<String, String[]> parameterMap = new HashMap<>();
-    private WebSocketRemoteEndpoint remote;
+    private RemoteEndpoint remote;
     private IncomingFrames incomingHandler;
     private OutgoingFrames outgoingHandler;
     private WebSocketPolicy policy;
     private UpgradeRequest upgradeRequest;
     private UpgradeResponse upgradeResponse;
-    private BatchMode batchMode = BatchMode.AUTO;
 
-    public WebSocketSession(URI requestURI, EventDriver websocket, LogicalConnection connection, SessionListener... sessionListeners)
+    public WebSocketSession(WebSocketContainerScope containerScope, URI requestURI, EventDriver websocket, LogicalConnection connection)
     {
-        if (requestURI == null)
-        {
-            throw new RuntimeException("Request URI cannot be null");
-        }
+        Objects.requireNonNull(containerScope,"Container Scope cannot be null");
+        Objects.requireNonNull(requestURI,"Request URI cannot be null");
 
         this.classLoader = Thread.currentThread().getContextClassLoader();
+        this.containerScope = containerScope;
         this.requestURI = requestURI;
         this.websocket = websocket;
         this.connection = connection;
-        this.sessionListeners = sessionListeners;
         this.executor = connection.getExecutor();
         this.outgoingHandler = connection;
         this.incomingHandler = websocket;
         this.connection.getIOState().addListener(this);
+        this.policy = containerScope.getPolicy();
+
+        addBean(this.connection);
+        addBean(this.websocket);
     }
 
     @Override
     public void close()
     {
         /* This is assumed to always be a NORMAL closure, no reason phrase */
-        connection.close(StatusCode.NORMAL, null);
+        close(StatusCode.NORMAL, null);
     }
 
     @Override
     public void close(CloseStatus closeStatus)
     {
-        this.close(closeStatus.getCode(),closeStatus.getPhrase());
+        close(closeStatus.getCode(),closeStatus.getPhrase());
     }
 
     @Override
@@ -128,6 +139,41 @@
     }
 
     @Override
+    protected void doStart() throws Exception
+    {
+        if(LOG.isDebugEnabled())
+            LOG.debug("starting - {}",this);
+
+        Iterator<RemoteEndpointFactory> iter = ServiceLoader.load(RemoteEndpointFactory.class).iterator();
+        if (iter.hasNext())
+            remoteEndpointFactory = iter.next();
+
+        if (remoteEndpointFactory == null)
+            remoteEndpointFactory = this;
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("Using RemoteEndpointFactory: {}", remoteEndpointFactory);
+
+        super.doStart();
+    }
+
+    @Override
+    protected void doStop() throws Exception
+    {
+        if(LOG.isDebugEnabled())
+            LOG.debug("stopping - {}",this);
+        try
+        {
+            close(StatusCode.SHUTDOWN,"Shutdown");
+        }
+        catch (Throwable t)
+        {
+            LOG.debug("During Connection Shutdown",t);
+        }
+        super.doStop();
+    }
+
+    @Override
     public void dump(Appendable out, String indent) throws IOException
     {
         dumpThis(out);
@@ -186,7 +232,7 @@
     {
         return this.connection.getBufferPool();
     }
-    
+
     public ClassLoader getClassLoader()
     {
         return this.getClass().getClassLoader();
@@ -197,6 +243,12 @@
         return connection;
     }
 
+    @Override
+    public WebSocketContainerScope getContainerScope()
+    {
+        return this.containerScope;
+    }
+
     public ExtensionFactory getExtensionFactory()
     {
         return extensionFactory;
@@ -244,12 +296,16 @@
     @Override
     public RemoteEndpoint getRemote()
     {
-        if (connection.getIOState().isOutputAvailable())
+        if(LOG_OPEN.isDebugEnabled())
+            LOG_OPEN.debug("[{}] {}.getRemote()",policy.getBehavior(),this.getClass().getSimpleName());
+        ConnectionState state = connection.getIOState().getConnectionState();
+
+        if ((state == ConnectionState.OPEN) || (state == ConnectionState.CONNECTED))
         {
             return remote;
         }
 
-        throw new WebSocketException("RemoteEndpoint unavailable, outgoing connection not open");
+        throw new WebSocketException("RemoteEndpoint unavailable, current state [" + state + "], expecting [OPEN or CONNECTED]");
     }
 
     @Override
@@ -275,6 +331,13 @@
         return this.upgradeResponse;
     }
 
+
+    @Override
+    public WebSocketSession getWebSocketSession()
+    {
+        return this;
+    }
+
     @Override
     public int hashCode()
     {
@@ -303,10 +366,19 @@
     @Override
     public void incomingFrame(Frame frame)
     {
-        if (connection.getIOState().isInputAvailable())
+        ClassLoader old = Thread.currentThread().getContextClassLoader();
+        try
         {
-            // Forward Frames Through Extension List
-            incomingHandler.incomingFrame(frame);
+            Thread.currentThread().setContextClassLoader(classLoader);
+            if (connection.getIOState().isInputAvailable())
+            {
+                // Forward Frames Through Extension List
+                incomingHandler.incomingFrame(frame);
+            }
+        }
+        finally
+        {
+            Thread.currentThread().setContextClassLoader(old);
         }
     }
 
@@ -347,6 +419,19 @@
         incomingError(cause);
     }
 
+    @Override
+    public void onClosed(Connection connection)
+    {
+    }
+
+    @Override
+    public void onOpened(Connection connection)
+    {
+        if(LOG_OPEN.isDebugEnabled())
+            LOG_OPEN.debug("[{}] {}.onOpened()",policy.getBehavior(),this.getClass().getSimpleName());
+        open();
+    }
+
     @SuppressWarnings("incomplete-switch")
     @Override
     public void onConnectionStateChange(ConnectionState state)
@@ -354,68 +439,65 @@
         switch (state)
         {
             case CLOSED:
-                // notify session listeners
-                for (SessionListener listener : sessionListeners)
-                {
-                    try
-                    {
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("{}.onSessionClosed()",listener.getClass().getSimpleName());
-                        listener.onSessionClosed(this);
-                    }
-                    catch (Throwable t)
-                    {
-                        LOG.ignore(t);
-                    }
-                }
                 IOState ioState = this.connection.getIOState();
                 CloseInfo close = ioState.getCloseInfo();
                 // confirmed close of local endpoint
                 notifyClose(close.getStatusCode(),close.getReason());
-                break;
-            case OPEN:
-                // notify session listeners
-                for (SessionListener listener : sessionListeners)
+                try
                 {
-                    try
-                    {
-                        listener.onSessionOpened(this);
-                    }
-                    catch (Throwable t)
-                    {
-                        LOG.ignore(t);
-                    }
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("{}.onSessionClosed()",containerScope.getClass().getSimpleName());
+                    containerScope.onSessionClosed(this);
+                }
+                catch (Throwable t)
+                {
+                    LOG.ignore(t);
+                }
+                break;
+            case CONNECTED:
+                // notify session listeners
+                try
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("{}.onSessionOpened()",containerScope.getClass().getSimpleName());
+                    containerScope.onSessionOpened(this);
+                }
+                catch (Throwable t)
+                {
+                    LOG.ignore(t);
                 }
                 break;
         }
     }
 
+    public WebSocketRemoteEndpoint newRemoteEndpoint(LogicalConnection connection, OutgoingFrames outgoingFrames, BatchMode batchMode)
+    {
+        return new WebSocketRemoteEndpoint(connection,outgoingHandler,getBatchMode());
+    }
+
     /**
      * Open/Activate the session
      */
     public void open()
     {
+        if(LOG_OPEN.isDebugEnabled())
+            LOG_OPEN.debug("[{}] {}.open()",policy.getBehavior(),this.getClass().getSimpleName());
+
         if (remote != null)
         {
             // already opened
             return;
         }
-        
-        ClassLoader old = Thread.currentThread().getContextClassLoader();
-        try 
-        {
-            Thread.currentThread().setContextClassLoader(classLoader);
 
+        try(ThreadClassLoaderScope scope = new ThreadClassLoaderScope(classLoader))
+        {
             // Upgrade success
             connection.getIOState().onConnected();
-            
+
             // Connect remote
-            BatchMode endpointBatchMode = websocket.getBatchMode();
-            if (endpointBatchMode == null)
-            {
-                endpointBatchMode = this.getBatchMode();
-            }
-            remote = new WebSocketRemoteEndpoint(connection,outgoingHandler,endpointBatchMode);
+            remote = remoteEndpointFactory.newRemoteEndpoint(connection,outgoingHandler,getBatchMode());
+            if(LOG_OPEN.isDebugEnabled())
+                LOG_OPEN.debug("[{}] {}.open() remote={}",policy.getBehavior(),this.getClass().getSimpleName(),remote);
 
             // Open WebSocket
             websocket.openSession(this);
@@ -428,8 +510,14 @@
                 LOG.debug("open -> {}",dump());
             }
         }
+        catch (CloseException ce)
+        {
+            LOG.warn(ce);
+            close(ce.getStatusCode(),ce.getMessage());
+        }
         catch (Throwable t)
         {
+            LOG.warn(t);
             // Exception on end-user WS-Endpoint.
             // Fast-fail & close connection with reason.
             int statusCode = StatusCode.SERVER_ERROR;
@@ -437,13 +525,8 @@
             {
                 statusCode = StatusCode.POLICY_VIOLATION;
             }
-            
             close(statusCode,t.getMessage());
         }
-        finally
-        {
-            Thread.currentThread().setContextClassLoader(old);
-        }
     }
 
     public void setExtensionFactory(ExtensionFactory extensionFactory)
@@ -504,20 +587,11 @@
     }
 
     /**
-     * @return the batching mode default for RemoteEndpoint behavior
+     * @return the default (initial) value for the batching mode.
      */
     public BatchMode getBatchMode()
     {
-        return batchMode;
-    }
-    
-    /**
-     * Set the batch mode default for the RemoteEndpoint behavior.
-     * @param mode the batching mode.
-     */
-    public void setBatchMode(BatchMode mode)
-    {
-        this.batchMode = mode;
+        return BatchMode.AUTO;
     }
 
     @Override
@@ -535,4 +609,10 @@
         return builder.toString();
     }
 
+    public static interface Listener
+    {
+        void onOpened(WebSocketSession session);
+
+        void onClosed(WebSocketSession session);
+    }
 }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSessionFactory.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSessionFactory.java
index fa680ff..4dc5e13 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSessionFactory.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSessionFactory.java
@@ -23,18 +23,19 @@
 import org.eclipse.jetty.websocket.common.events.EventDriver;
 import org.eclipse.jetty.websocket.common.events.JettyAnnotatedEventDriver;
 import org.eclipse.jetty.websocket.common.events.JettyListenerEventDriver;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 
 /**
  * Default Session factory, creating WebSocketSession objects.
  */
 public class WebSocketSessionFactory implements SessionFactory
 {
-    private final SessionListener[] listeners;
+    private final WebSocketContainerScope containerScope;
 
-    public WebSocketSessionFactory(SessionListener... sessionListeners)
+    public WebSocketSessionFactory(WebSocketContainerScope containerScope)
     {
-        listeners = sessionListeners;
-    }
+        this.containerScope = containerScope;
+   }
 
     @Override
     public boolean supports(EventDriver websocket)
@@ -45,6 +46,6 @@
     @Override
     public WebSocketSession createSession(URI requestURI, EventDriver websocket, LogicalConnection connection)
     {
-        return new WebSocketSession(requestURI,websocket,connection,listeners);
+        return new WebSocketSession(containerScope, requestURI,websocket,connection);
     }
 }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java
index d274c90..935548a 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java
@@ -23,6 +23,7 @@
 
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
+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.websocket.api.BatchMode;
@@ -40,11 +41,11 @@
 /**
  * EventDriver is the main interface between the User's WebSocket POJO and the internal jetty implementation of WebSocket.
  */
-public abstract class AbstractEventDriver implements IncomingFrames, EventDriver
+public abstract class AbstractEventDriver extends AbstractLifeCycle implements IncomingFrames, EventDriver
 {
     private static final Logger LOG = Log.getLogger(AbstractEventDriver.class);
     protected final Logger TARGET_LOG;
-    protected final WebSocketPolicy policy;
+    protected WebSocketPolicy policy;
     protected final Object websocket;
     protected WebSocketSession session;
     protected MessageAppender activeMessage;
@@ -203,13 +204,13 @@
     {
         /* TODO: provide annotation in future */
     }
-    
+
     @Override
     public void onPing(ByteBuffer buffer)
     {
         /* TODO: provide annotation in future */
     }
-    
+
     @Override
     public BatchMode getBatchMode()
     {
@@ -220,8 +221,13 @@
     public void openSession(WebSocketSession session)
     {
         if (LOG.isDebugEnabled())
-            LOG.debug("openSession({})",session);
+        {
+            LOG.debug("openSession({})", session);
+            LOG.debug("objectFactory={}", session.getContainerScope().getObjectFactory());
+        }
         this.session = session;
+        this.session.getContainerScope().getObjectFactory().decorate(this.websocket);
+        
         try
         {
             this.onConnect();
@@ -245,6 +251,12 @@
         TARGET_LOG.warn("Unhandled Error (closing connection)",t);
         onError(t);
 
+        if (t instanceof CloseException)
+        {
+            terminateConnection(((CloseException)t).getStatusCode(),t.getClass().getSimpleName());
+            return;
+        }
+
         // Unhandled Error, close the connection.
         switch (policy.getBehavior())
         {
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/EventDriverFactory.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/EventDriverFactory.java
index fda50cf..e983ec9 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/EventDriverFactory.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/EventDriverFactory.java
@@ -24,7 +24,9 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
+import org.eclipse.jetty.websocket.api.WebSocketListener;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
 
 /**
  * Create EventDriver implementations.
@@ -99,7 +101,7 @@
      * Wrap the given WebSocket object instance in a suitable EventDriver
      * 
      * @param websocket
-     *            the websocket instance to wrap. Must either implement {@link WebSocketListener} or be annotated with {@link WebSocket &#064WebSocket}
+     *            the websocket instance to wrap. Must either implement {@link WebSocketListener} or be annotated with {@link WebSocket &#064;WebSocket}
      * @return appropriate EventDriver for this websocket instance.
      */
     public EventDriver wrap(Object websocket)
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/JettyListenerEventDriver.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/JettyListenerEventDriver.java
index 9cb20aa..49c438fb 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/JettyListenerEventDriver.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/JettyListenerEventDriver.java
@@ -25,12 +25,19 @@
 
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.WebSocketConnectionListener;
+import org.eclipse.jetty.websocket.api.WebSocketFrameListener;
 import org.eclipse.jetty.websocket.api.WebSocketListener;
+import org.eclipse.jetty.websocket.api.WebSocketPartialListener;
+import org.eclipse.jetty.websocket.api.WebSocketPingPongListener;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.Frame;
+import org.eclipse.jetty.websocket.api.extensions.Frame.Type;
 import org.eclipse.jetty.websocket.common.CloseInfo;
+import org.eclipse.jetty.websocket.common.frames.ReadOnlyDelegatedFrame;
 import org.eclipse.jetty.websocket.common.message.SimpleBinaryMessage;
 import org.eclipse.jetty.websocket.common.message.SimpleTextMessage;
+import org.eclipse.jetty.websocket.common.util.Utf8PartialBuilder;
 
 /**
  * Handler for {@link WebSocketListener} based User WebSocket implementations.
@@ -38,10 +45,11 @@
 public class JettyListenerEventDriver extends AbstractEventDriver
 {
     private static final Logger LOG = Log.getLogger(JettyListenerEventDriver.class);
-    private final WebSocketListener listener;
+    private final WebSocketConnectionListener listener;
+    private Utf8PartialBuilder utf8Partial;
     private boolean hasCloseBeenCalled = false;
 
-    public JettyListenerEventDriver(WebSocketPolicy policy, WebSocketListener listener)
+    public JettyListenerEventDriver(WebSocketPolicy policy, WebSocketConnectionListener listener)
     {
         super(policy,listener);
         this.listener = listener;
@@ -50,18 +58,29 @@
     @Override
     public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException
     {
-        if (activeMessage == null)
+        if (listener instanceof WebSocketListener)
         {
-            activeMessage = new SimpleBinaryMessage(this);
+            if (activeMessage == null)
+            {
+                activeMessage = new SimpleBinaryMessage(this);
+            }
+
+            appendMessage(buffer,fin);
         }
 
-        appendMessage(buffer,fin);
+        if (listener instanceof WebSocketPartialListener)
+        {
+            ((WebSocketPartialListener)listener).onWebSocketPartialBinary(buffer.slice().asReadOnlyBuffer(),fin);
+        }
     }
 
     @Override
     public void onBinaryMessage(byte[] data)
     {
-        listener.onWebSocketBinary(data,0,data.length);
+        if (listener instanceof WebSocketListener)
+        {
+            ((WebSocketListener)listener).onWebSocketBinary(data,0,data.length);
+        }
     }
 
     @Override
@@ -96,7 +115,22 @@
     @Override
     public void onFrame(Frame frame)
     {
-        /* ignore, not supported by WebSocketListener */
+        if (listener instanceof WebSocketFrameListener)
+        {
+            ((WebSocketFrameListener)listener).onWebSocketFrame(new ReadOnlyDelegatedFrame(frame));
+        }
+
+        if (listener instanceof WebSocketPingPongListener)
+        {
+            if (frame.getType() == Type.PING)
+            {
+                ((WebSocketPingPongListener)listener).onWebSocketPing(frame.getPayload().asReadOnlyBuffer());
+            }
+            else if (frame.getType() == Type.PONG)
+            {
+                ((WebSocketPingPongListener)listener).onWebSocketPong(frame.getPayload().asReadOnlyBuffer());
+            }
+        }
     }
 
     @Override
@@ -114,18 +148,46 @@
     @Override
     public void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException
     {
-        if (activeMessage == null)
+        if (listener instanceof WebSocketListener)
         {
-            activeMessage = new SimpleTextMessage(this);
+            if (activeMessage == null)
+            {
+                activeMessage = new SimpleTextMessage(this);
+            }
+
+            appendMessage(buffer,fin);
         }
 
-        appendMessage(buffer,fin);
+        if (listener instanceof WebSocketPartialListener)
+        {
+            if (utf8Partial == null)
+            {
+                utf8Partial = new Utf8PartialBuilder();
+            }
+            
+            String partial = utf8Partial.toPartialString(buffer);
+            
+            ((WebSocketPartialListener)listener).onWebSocketPartialText(partial,fin);
+            
+            if (fin)
+            {
+                partial = null;
+            }
+        }
     }
 
+    /**
+     * Whole Message event.
+     * 
+     * @param message the whole message
+     */
     @Override
     public void onTextMessage(String message)
     {
-        listener.onWebSocketText(message);
+        if (listener instanceof WebSocketListener)
+        {
+            ((WebSocketListener)listener).onWebSocketText(message);
+        }
     }
 
     @Override
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/JettyListenerImpl.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/JettyListenerImpl.java
index 48fd9d0..4815059 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/JettyListenerImpl.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/JettyListenerImpl.java
@@ -18,6 +18,7 @@
 
 package org.eclipse.jetty.websocket.common.events;
 
+import org.eclipse.jetty.websocket.api.WebSocketConnectionListener;
 import org.eclipse.jetty.websocket.api.WebSocketListener;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 
@@ -26,7 +27,7 @@
     @Override
     public EventDriver create(Object websocket, WebSocketPolicy policy)
     {
-        WebSocketListener listener = (WebSocketListener)websocket;
+        WebSocketConnectionListener listener = (WebSocketConnectionListener)websocket;
         return new JettyListenerEventDriver(policy,listener);
     }
 
@@ -39,6 +40,6 @@
     @Override
     public boolean supports(Object websocket)
     {
-        return (websocket instanceof WebSocketListener);
+        return (websocket instanceof WebSocketConnectionListener);
     }
 }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/annotated/AbstractMethodAnnotationScanner.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/annotated/AbstractMethodAnnotationScanner.java
index 14f24f8..80455c2 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/annotated/AbstractMethodAnnotationScanner.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/annotated/AbstractMethodAnnotationScanner.java
@@ -27,6 +27,7 @@
 
 /**
  * Basic scanner for Annotated Methods
+ * @param <T> The type of metadata
  */
 public abstract class AbstractMethodAnnotationScanner<T>
 {
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/annotated/CallableMethod.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/annotated/CallableMethod.java
index 87e6bca..7221641 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/annotated/CallableMethod.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/annotated/CallableMethod.java
@@ -55,7 +55,8 @@
 
         if (obj == null)
         {
-            LOG.warn("Cannot call {} on null object",this.method);
+            String err = String.format("Cannot call %s on null object", this.method);
+            LOG.warn(new RuntimeException(err));            
             return null;
         }
 
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/AbstractExtension.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/AbstractExtension.java
index 3f77f05..2053422 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/AbstractExtension.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/AbstractExtension.java
@@ -23,7 +23,9 @@
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.BatchMode;
@@ -35,9 +37,10 @@
 import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
 import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
 import org.eclipse.jetty.websocket.common.LogicalConnection;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 
 @ManagedObject("Abstract Extension")
-public abstract class AbstractExtension extends ContainerLifeCycle implements Extension
+public abstract class AbstractExtension extends AbstractLifeCycle implements Dumpable, Extension
 {
     private final Logger log;
     private WebSocketPolicy policy;
@@ -51,11 +54,15 @@
     {
         log = Log.getLogger(this.getClass());
     }
-
+    
     @Override
+    public String dump()
+    {
+        return ContainerLifeCycle.dump(this);
+    }
+
     public void dump(Appendable out, String indent) throws IOException
     {
-        super.dump(out, indent);
         // incoming
         dumpWithHeading(out, indent, "incoming", this.nextIncoming);
         dumpWithHeading(out, indent, "outgoing", this.nextOutgoing);
@@ -67,6 +74,12 @@
         out.append(heading).append(" : ");
         out.append(bean.toString());
     }
+    
+    public void init(WebSocketContainerScope container)
+    {
+        this.policy = container.getPolicy();
+        this.bufferPool = container.getBufferPool();
+    }
 
     public ByteBufferPool getBufferPool()
     {
@@ -183,7 +196,7 @@
     {
         this.connection = connection;
     }
-
+    
     @Override
     public void setNextIncomingFrames(IncomingFrames nextIncoming)
     {
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java
index 9e59055..92b6fa6 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java
@@ -19,16 +19,17 @@
 package org.eclipse.jetty.websocket.common.extensions;
 
 import java.io.IOException;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Queue;
 
-import org.eclipse.jetty.util.ConcurrentArrayQueue;
 import org.eclipse.jetty.util.IteratingCallback;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.LifeCycle;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.BatchMode;
@@ -51,7 +52,7 @@
 {
     private static final Logger LOG = Log.getLogger(ExtensionStack.class);
 
-    private final Queue<FrameEntry> entries = new ConcurrentArrayQueue<>();
+    private final Queue<FrameEntry> entries = new ArrayDeque<>();
     private final IteratingCallback flusher = new Flusher();
     private final ExtensionFactory factory;
     private List<Extension> extensions;
@@ -89,6 +90,11 @@
                 Extension ext = exts.next();
                 ext.setNextOutgoingFrames(nextOutgoing);
                 nextOutgoing = ext;
+                
+                if (ext instanceof LifeCycle)
+                {
+                    addBean(ext,true);
+                }
             }
 
             // Connect incomings
@@ -286,7 +292,7 @@
         FrameEntry entry = new FrameEntry(frame,callback,batchMode);
         if (LOG.isDebugEnabled())
             LOG.debug("Queuing {}",entry);
-        entries.offer(entry);
+        offerEntry(entry);
         flusher.iterate();
     }
 
@@ -311,12 +317,36 @@
         }
     }
 
+    private void offerEntry(FrameEntry entry)
+    {
+        synchronized (this)
+        {
+            entries.offer(entry);
+        }
+    }
+
+    private FrameEntry pollEntry()
+    {
+        synchronized (this)
+        {
+            return entries.poll();
+        }
+    }
+
+    private int getQueueSize()
+    {
+        synchronized (this)
+        {
+            return entries.size();
+        }
+    }
+
     @Override
     public String toString()
     {
         StringBuilder s = new StringBuilder();
         s.append("ExtensionStack[");
-        s.append("queueSize=").append(entries.size());
+        s.append("queueSize=").append(getQueueSize());
         s.append(",extensions=");
         if (extensions == null)
         {
@@ -377,7 +407,7 @@
         @Override
         protected Action process() throws Exception
         {
-            current = entries.poll();
+            current = pollEntry();
             if (current == null)
             {
                 if (LOG.isDebugEnabled())
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/FrameCaptureExtension.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/FrameCaptureExtension.java
new file mode 100644
index 0000000..ea96286
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/FrameCaptureExtension.java
@@ -0,0 +1,187 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.extensions;
+
+import static java.nio.file.StandardOpenOption.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Calendar;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.BatchMode;
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.api.WriteCallback;
+import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
+import org.eclipse.jetty.websocket.api.extensions.Frame;
+import org.eclipse.jetty.websocket.common.Generator;
+import org.eclipse.jetty.websocket.common.WebSocketFrame;
+
+public class FrameCaptureExtension extends AbstractExtension
+{
+    private static final Logger LOG = Log.getLogger(FrameCaptureExtension.class);
+
+    private static final int BUFSIZE = 32768;
+    private Generator generator;
+    private Path outputDir;
+    private String prefix = "frame";
+    private Path incomingFramesPath;
+    private Path outgoingFramesPath;
+    
+    private AtomicInteger incomingCount = new AtomicInteger(0);
+    private AtomicInteger outgoingCount = new AtomicInteger(0);
+
+    private SeekableByteChannel incomingChannel;
+    private SeekableByteChannel outgoingChannel;
+
+    @Override
+    public String getName()
+    {
+        return "@frame-capture";
+    }
+
+    @Override
+    public void incomingFrame(Frame frame)
+    {
+        saveFrame(frame,false);
+        try
+        {
+            nextIncomingFrame(frame);
+        }
+        catch (Throwable t)
+        {
+            IO.close(incomingChannel);
+            incomingChannel = null;
+            throw t;
+        }
+    }
+
+    @Override
+    public void outgoingFrame(Frame frame, WriteCallback callback, BatchMode batchMode)
+    {
+        saveFrame(frame,true);
+        try
+        {
+            nextOutgoingFrame(frame,callback,batchMode);
+        }
+        catch (Throwable t)
+        {
+            IO.close(outgoingChannel);
+            outgoingChannel = null;
+            throw t;
+        }
+    }
+
+    private void saveFrame(Frame frame, boolean outgoing)
+    {
+        if (outputDir == null || generator == null)
+        {
+            return;
+        }
+
+        @SuppressWarnings("resource")
+        SeekableByteChannel channel = (outgoing) ? outgoingChannel : incomingChannel;
+        
+        if (channel == null)
+        {
+            return;
+        }
+
+        ByteBuffer buf = getBufferPool().acquire(BUFSIZE,false);
+
+        try
+        {
+            WebSocketFrame f = WebSocketFrame.copy(frame);
+            f.setMasked(false);
+            generator.generateHeaderBytes(f,buf);
+            channel.write(buf);
+            if (frame.hasPayload())
+            {
+                channel.write(frame.getPayload().slice());
+            }
+            if (LOG.isDebugEnabled())
+                LOG.debug("Saved {} frame #{}",(outgoing) ? "outgoing" : "incoming",
+                        (outgoing) ? outgoingCount.incrementAndGet() : incomingCount.incrementAndGet());
+        }
+        catch (IOException e)
+        {
+            LOG.warn("Unable to save frame: " + frame,e);
+        }
+        finally
+        {
+            getBufferPool().release(buf);
+        }
+    }
+
+    @Override
+    public void setConfig(ExtensionConfig config)
+    {
+        super.setConfig(config);
+
+        String cfgOutputDir = config.getParameter("output-dir",null);
+        if (StringUtil.isNotBlank(cfgOutputDir))
+        {
+            Path path = new File(cfgOutputDir).toPath();
+            if (Files.isDirectory(path) && Files.exists(path) && Files.isWritable(path))
+            {
+                this.outputDir = path;
+            }
+            else
+            {
+                LOG.warn("Unable to configure {}: not a valid output directory",path.toAbsolutePath().toString());
+            }
+        }
+
+        String cfgPrefix = config.getParameter("prefix","frame");
+        if (StringUtil.isNotBlank(cfgPrefix))
+        {
+            this.prefix = cfgPrefix;
+        }
+
+        if (this.outputDir != null)
+        {
+            try
+            {
+                Path dir = this.outputDir.toRealPath();
+
+                // create a non-validating, read-only generator
+                String tstamp = String.format("%1$tY%1$tm%1$td-%1$tH%1$tM%1$tS",Calendar.getInstance());
+                incomingFramesPath = dir.resolve(String.format("%s-%s-incoming.dat",this.prefix,tstamp));
+                outgoingFramesPath = dir.resolve(String.format("%s-%s-outgoing.dat",this.prefix,tstamp));
+
+                incomingChannel = Files.newByteChannel(incomingFramesPath,CREATE,WRITE);
+                outgoingChannel = Files.newByteChannel(outgoingFramesPath,CREATE,WRITE);
+
+                this.generator = new Generator(WebSocketPolicy.newServerPolicy(),getBufferPool(),false,true);
+            }
+            catch (IOException e)
+            {
+                LOG.warn("Unable to create capture file(s)",e);
+            }
+        }
+    }
+}
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/FrameDebugExtension.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/FrameDebugExtension.java
deleted file mode 100644
index 4efc7c8..0000000
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/FrameDebugExtension.java
+++ /dev/null
@@ -1,142 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.common.extensions;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.SeekableByteChannel;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.eclipse.jetty.util.StringUtil;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.websocket.api.BatchMode;
-import org.eclipse.jetty.websocket.api.WriteCallback;
-import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
-import org.eclipse.jetty.websocket.api.extensions.Frame;
-import org.eclipse.jetty.websocket.common.Generator;
-
-public class FrameDebugExtension extends AbstractExtension
-{
-    private static final Logger LOG = Log.getLogger(FrameDebugExtension.class);
-
-    private static final int BUFSIZE = 32768;
-    private Generator generator;
-    private Path outputDir;
-    private String prefix = "frame";
-    private AtomicLong incomingId = new AtomicLong(0);
-    private AtomicLong outgoingId = new AtomicLong(0);
-
-    @Override
-    public String getName()
-    {
-        return "@frame-debug";
-    }
-
-    @Override
-    public void incomingFrame(Frame frame)
-    {
-        saveFrame(frame,false);
-        nextIncomingFrame(frame);
-    }
-
-    @Override
-    public void outgoingFrame(Frame frame, WriteCallback callback, BatchMode batchMode)
-    {
-        saveFrame(frame,true);
-        nextOutgoingFrame(frame,callback,batchMode);
-    }
-
-    private void saveFrame(Frame frame, boolean outgoing)
-    {
-        if (outputDir == null || generator == null)
-        {
-            return;
-        }
-
-        StringBuilder filename = new StringBuilder();
-        filename.append(prefix);
-        if (outgoing)
-        {
-            filename.append(String.format("-outgoing-%05d",outgoingId.getAndIncrement()));
-        }
-        else
-        {
-            filename.append(String.format("-incoming-%05d",incomingId.getAndIncrement()));
-        }
-        filename.append(".dat");
-
-        Path outputFile = outputDir.resolve(filename.toString());
-        ByteBuffer buf = getBufferPool().acquire(BUFSIZE,false);
-        try (SeekableByteChannel channel = Files.newByteChannel(outputFile,StandardOpenOption.CREATE,StandardOpenOption.WRITE))
-        {
-            generator.generateHeaderBytes(frame,buf);
-            channel.write(buf);
-            if (frame.hasPayload())
-            {
-                channel.write(frame.getPayload().slice());
-            }
-            LOG.debug("Saved raw frame: {}",outputFile.toString());
-        }
-        catch (IOException e)
-        {
-            LOG.warn("Unable to save frame: " + filename.toString(),e);
-        }
-        finally
-        {
-            getBufferPool().release(buf);
-        }
-    }
-
-    @Override
-    public void setConfig(ExtensionConfig config)
-    {
-        super.setConfig(config);
-
-        String cfgOutputDir = config.getParameter("output-dir",null);
-        if (StringUtil.isNotBlank(cfgOutputDir))
-        {
-            Path path = new File(cfgOutputDir).toPath();
-            if (Files.isDirectory(path) && Files.exists(path) && Files.isWritable(path))
-            {
-                this.outputDir = path;
-            }
-            else
-            {
-                LOG.warn("Unable to configure {}: not a valid output directory",path.toAbsolutePath().toString());
-            }
-        }
-
-        String cfgPrefix = config.getParameter("prefix","frame");
-        if (StringUtil.isNotBlank(cfgPrefix))
-        {
-            this.prefix = cfgPrefix;
-        }
-
-        if (this.outputDir != null)
-        {
-            // create a non-validating, read-only generator
-            this.generator = new Generator(getPolicy(),getBufferPool(),false,true);
-        }
-    }
-}
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/WebSocketExtensionFactory.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/WebSocketExtensionFactory.java
index ac3e42d..c50ceb1 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/WebSocketExtensionFactory.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/WebSocketExtensionFactory.java
@@ -18,24 +18,21 @@
 
 package org.eclipse.jetty.websocket.common.extensions;
 
-import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.websocket.api.WebSocketException;
-import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.Extension;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 
 public class WebSocketExtensionFactory extends ExtensionFactory
 {
-    private WebSocketPolicy policy;
-    private ByteBufferPool bufferPool;
+    private WebSocketContainerScope container;
 
-    public WebSocketExtensionFactory(WebSocketPolicy policy, ByteBufferPool bufferPool)
+    public WebSocketExtensionFactory(WebSocketContainerScope container)
     {
         super();
-        this.policy = policy;
-        this.bufferPool = bufferPool;
+        this.container = container;
     }
 
     @Override
@@ -60,12 +57,11 @@
 
         try
         {
-            Extension ext = extClass.newInstance();
+            Extension ext = container.getObjectFactory().createInstance(extClass);
             if (ext instanceof AbstractExtension)
             {
                 AbstractExtension aext = (AbstractExtension)ext;
-                aext.setPolicy(policy);
-                aext.setBufferPool(bufferPool);
+                aext.init(container);
                 aext.setConfig(config);
             }
             return ext;
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/CompressExtension.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/CompressExtension.java
index 2a8d368..3ed6848 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/CompressExtension.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/CompressExtension.java
@@ -20,6 +20,7 @@
 
 import java.io.ByteArrayOutputStream;
 import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
 import java.util.Queue;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.zip.DataFormatException;
@@ -28,7 +29,6 @@
 import java.util.zip.ZipException;
 
 import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.ConcurrentArrayQueue;
 import org.eclipse.jetty.util.IteratingCallback;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -72,30 +72,36 @@
     
     private final static boolean NOWRAP = true;
 
-    private final Queue<FrameEntry> entries = new ConcurrentArrayQueue<>();
+    private final Queue<FrameEntry> entries = new ArrayDeque<>();
     private final IteratingCallback flusher = new Flusher();
-    private final Deflater deflater;
-    private final Inflater inflater;
+    private Deflater deflaterImpl;
+    private Inflater inflaterImpl;
     protected AtomicInteger decompressCount = new AtomicInteger(0);
     private int tailDrop = TAIL_DROP_NEVER;
     private int rsvUse = RSV_USE_ALWAYS;
 
     protected CompressExtension()
     {
-        deflater = new Deflater(Deflater.DEFAULT_COMPRESSION,NOWRAP);
-        inflater = new Inflater(NOWRAP);
         tailDrop = getTailDropMode();
         rsvUse = getRsvUseMode();
     }
 
     public Deflater getDeflater()
     {
-        return deflater;
+        if (deflaterImpl == null)
+        {
+            deflaterImpl = new Deflater(Deflater.DEFAULT_COMPRESSION,NOWRAP);
+        }
+        return deflaterImpl;
     }
 
     public Inflater getInflater()
     {
-        return inflater;
+        if (inflaterImpl == null)
+        {
+            inflaterImpl = new Inflater(NOWRAP);
+        }
+        return inflaterImpl;
     }
 
     /**
@@ -154,30 +160,35 @@
             return;
         }
         byte[] output = new byte[DECOMPRESS_BUF_SIZE];
-
-        if (inflater.needsInput() && !supplyInput(inflater,buf))
+        
+        Inflater inflater = getInflater();
+        
+        while(buf.hasRemaining() && inflater.needsInput())
         {
-            LOG.debug("Needed input, but no buffer could supply input");
-            return;
-        }
-
-        int read = 0;
-        while ((read = inflater.inflate(output)) >= 0)
-        {
-            if (read == 0)
+            if (!supplyInput(inflater,buf))
             {
-                LOG.debug("Decompress: read 0 {}",toDetail(inflater));
-                break;
+                LOG.debug("Needed input, but no buffer could supply input");
+                return;
             }
-            else
+    
+            int read;
+            while ((read = inflater.inflate(output)) >= 0)
             {
-                // do something with output
-                if (LOG.isDebugEnabled())
+                if (read == 0)
                 {
-                    LOG.debug("Decompressed {} bytes: {}",read,toDetail(inflater));
+                    LOG.debug("Decompress: read 0 {}",toDetail(inflater));
+                    break;
                 }
-
-                accumulator.copyChunk(output,0,read);
+                else
+                {
+                    // do something with output
+                    if (LOG.isDebugEnabled())
+                    {
+                        LOG.debug("Decompressed {} bytes: {}",read,toDetail(inflater));
+                    }
+    
+                    accumulator.copyChunk(output,0,read);
+                }
             }
         }
 
@@ -203,10 +214,26 @@
         FrameEntry entry = new FrameEntry(frame,callback,batchMode);
         if (LOG.isDebugEnabled())
             LOG.debug("Queuing {}",entry);
-        entries.offer(entry);
+        offerEntry(entry);
         flusher.iterate();
     }
 
+    private void offerEntry(FrameEntry entry)
+    {
+        synchronized (this)
+        {
+            entries.offer(entry);
+        }
+    }
+
+    private FrameEntry pollEntry()
+    {
+        synchronized (this)
+        {
+            return entries.poll();
+        }
+    }
+
     protected void notifyCallbackSuccess(WriteCallback callback)
     {
         try
@@ -247,7 +274,7 @@
         }
 
         byte input[];
-        int inputOffset = 0;
+        int inputOffset;
         int len;
 
         if (buf.hasArray())
@@ -287,7 +314,7 @@
         }
 
         byte input[];
-        int inputOffset = 0;
+        int inputOffset;
         int len;
 
         if (buf.hasArray())
@@ -343,6 +370,16 @@
         }
         return true;
     }
+    
+    @Override
+    protected void doStop() throws Exception
+    {
+        if(deflaterImpl != null)
+            deflaterImpl.end();
+        if(inflaterImpl != null)
+            inflaterImpl.end();
+        super.doStop();
+    }
 
     @Override
     public String toString()
@@ -387,7 +424,7 @@
         {
             if (finished)
             {
-                current = entries.poll();
+                current = pollEntry();
                 LOG.debug("Processing {}",current);
                 if (current == null)
                     return Action.IDLE;
@@ -404,12 +441,13 @@
         {
             Frame frame = entry.frame;
             BatchMode batchMode = entry.batchMode;
-            if (OpCode.isControlFrame(frame.getOpCode()) || !frame.hasPayload())
+            if (OpCode.isControlFrame(frame.getOpCode()))
             {
+                // Do not deflate control frames
                 nextOutgoingFrame(frame,this,batchMode);
                 return;
             }
-
+            
             compress(entry,true);
         }
 
@@ -425,13 +463,15 @@
                 LOG.debug("Compressing {}: {} bytes in {} bytes chunk",entry,remaining,outputLength);
 
             boolean needsCompress = true;
+            
+            Deflater deflater = getDeflater();
 
             if (deflater.needsInput() && !supplyInput(deflater,data))
             {
                 // no input supplied
                 needsCompress = false;
             }
-
+            
             ByteArrayOutputStream out = new ByteArrayOutputStream();
 
             byte[] output = new byte[outputLength];
@@ -483,7 +523,8 @@
             }
             else if (fin)
             {
-                // Special case: 8.2.3.6.  Generating an Empty Fragment Manually
+                // Special case: 7.2.3.6.  Generating an Empty Fragment Manually
+                // https://tools.ietf.org/html/rfc7692#section-7.2.3.6
                 payload = ByteBuffer.wrap(new byte[] { 0x00 });
             }
 
@@ -520,7 +561,7 @@
         {
             // Fail all the frames in the queue.
             FrameEntry entry;
-            while ((entry = entries.poll()) != null)
+            while ((entry = pollEntry()) != null)
                 notifyCallbackFailure(entry.callback,x);
         }
 
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtension.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtension.java
index 47cc36c..ade66b6 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtension.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtension.java
@@ -32,8 +32,8 @@
 
 /**
  * Per Message Deflate Compression extension for WebSocket.
- * <p/>
- * Attempts to follow <a href="https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-12">draft-ietf-hybi-permessage-compression-12</a>
+ * <p>
+ * Attempts to follow <a href="https://tools.ietf.org/html/rfc7692">Compression Extensions for WebSocket</a>
  */
 public class PerMessageDeflateExtension extends CompressExtension
 {
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/fragment/FragmentExtension.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/fragment/FragmentExtension.java
index 4139e08..b297ccf 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/fragment/FragmentExtension.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/fragment/FragmentExtension.java
@@ -20,9 +20,9 @@
 
 
 import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
 import java.util.Queue;
 
-import org.eclipse.jetty.util.ConcurrentArrayQueue;
 import org.eclipse.jetty.util.IteratingCallback;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -41,7 +41,7 @@
 {
     private static final Logger LOG = Log.getLogger(FragmentExtension.class);
 
-    private final Queue<FrameEntry> entries = new ConcurrentArrayQueue<>();
+    private final Queue<FrameEntry> entries = new ArrayDeque<>();
     private final IteratingCallback flusher = new Flusher();
     private int maxLength;
 
@@ -71,7 +71,7 @@
         FrameEntry entry = new FrameEntry(frame, callback, batchMode);
         if (LOG.isDebugEnabled())
             LOG.debug("Queuing {}", entry);
-        entries.offer(entry);
+        offerEntry(entry);
         flusher.iterate();
     }
 
@@ -82,6 +82,22 @@
         maxLength = config.getParameter("maxLength", -1);
     }
 
+    private void offerEntry(FrameEntry entry)
+    {
+        synchronized (this)
+        {
+            entries.offer(entry);
+        }
+    }
+
+    private FrameEntry pollEntry()
+    {
+        synchronized (this)
+        {
+            return entries.poll();
+        }
+    }
+
     private static class FrameEntry
     {
         private final Frame frame;
@@ -112,7 +128,7 @@
         {
             if (finished)
             {
-                current = entries.poll();
+                current = pollEntry();
                 LOG.debug("Processing {}", current);
                 if (current == null)
                     return Action.IDLE;
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/BinaryFrame.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/BinaryFrame.java
index bda896d..1fb0f6a 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/BinaryFrame.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/BinaryFrame.java
@@ -51,6 +51,8 @@
     @Override
     public Type getType()
     {
+        if (getOpCode() == OpCode.CONTINUATION)
+            return Type.CONTINUATION;
         return Type.BINARY;
     }
 }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/ControlFrame.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/ControlFrame.java
index 0b64257..bfce1f4 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/ControlFrame.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/ControlFrame.java
@@ -21,6 +21,7 @@
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 
+import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.websocket.api.ProtocolException;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 
@@ -128,4 +129,14 @@
         }
         return super.setPayload(buf);
     }
+
+    @Override
+    public ByteBuffer getPayload()
+    {
+        if (super.getPayload() == null)
+        {
+            return BufferUtil.EMPTY_BUFFER;
+        }
+        return super.getPayload();
+    }
 }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/DataFrame.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/DataFrame.java
index 9668855..639579b 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/DataFrame.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/DataFrame.java
@@ -36,6 +36,7 @@
      * Construct new DataFrame based on headers of provided frame.
      * <p>
      * Useful for when working in extensions and a new frame needs to be created.
+     * @param basedOn the frame this one is based on
      */
     public DataFrame(Frame basedOn)
     {
@@ -46,6 +47,8 @@
      * Construct new DataFrame based on headers of provided frame, overriding for continuations if needed.
      * <p>
      * Useful for when working in extensions and a new frame needs to be created.
+     * @param basedOn the frame this one is based on
+     * @param continuation true if this is a continuation frame
      */
     public DataFrame(Frame basedOn, boolean continuation)
     {
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/ReadOnlyDelegatedFrame.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/ReadOnlyDelegatedFrame.java
new file mode 100644
index 0000000..18940ba
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/ReadOnlyDelegatedFrame.java
@@ -0,0 +1,112 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.frames;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.websocket.api.extensions.Frame;
+
+/**
+ * Immutable, Read-only, Frame implementation.
+ */
+public class ReadOnlyDelegatedFrame implements Frame
+{
+    private final Frame delegate;
+    
+    public ReadOnlyDelegatedFrame(Frame frame)
+    {
+        this.delegate = frame;
+    }
+
+    @Override
+    public byte[] getMask()
+    {
+        return delegate.getMask();
+    }
+
+    @Override
+    public byte getOpCode()
+    {
+        return delegate.getOpCode();
+    }
+
+    @Override
+    public ByteBuffer getPayload()
+    {
+        if(!delegate.hasPayload()) {
+            return null;
+        }
+        return delegate.getPayload().asReadOnlyBuffer();
+    }
+
+    @Override
+    public int getPayloadLength()
+    {
+        return delegate.getPayloadLength();
+    }
+
+    @Override
+    public Type getType()
+    {
+        return delegate.getType();
+    }
+
+    @Override
+    public boolean hasPayload()
+    {
+        return delegate.hasPayload();
+    }
+
+    @Override
+    public boolean isFin()
+    {
+        return delegate.isFin();
+    }
+
+    @Override
+    @Deprecated
+    public boolean isLast()
+    {
+        return delegate.isLast();
+    }
+
+    @Override
+    public boolean isMasked()
+    {
+        return delegate.isMasked();
+    }
+
+    @Override
+    public boolean isRsv1()
+    {
+        return delegate.isRsv1();
+    }
+
+    @Override
+    public boolean isRsv2()
+    {
+        return delegate.isRsv2();
+    }
+
+    @Override
+    public boolean isRsv3()
+    {
+        return delegate.isRsv3();
+    }
+}
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/TextFrame.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/TextFrame.java
index e1d6a6f..eaf7149 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/TextFrame.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/frames/TextFrame.java
@@ -34,6 +34,8 @@
     @Override
     public Type getType()
     {
+        if (getOpCode() == OpCode.CONTINUATION)
+            return Type.CONTINUATION;
         return Type.TEXT;
     }
 
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java
index b0538df..5753035 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java
@@ -32,6 +32,7 @@
 
 import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
@@ -52,14 +53,15 @@
 import org.eclipse.jetty.websocket.common.Generator;
 import org.eclipse.jetty.websocket.common.LogicalConnection;
 import org.eclipse.jetty.websocket.common.Parser;
-import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.io.IOState.ConnectionStateListener;
 
 /**
- * Provides the implementation of {@link LogicalConnection} within the framework of the new {@link Connection} framework of {@code jetty-io}.
+ * Provides the implementation of {@link LogicalConnection} within the framework of the new {@link org.eclipse.jetty.io.Connection} framework of {@code jetty-io}.
  */
-public abstract class AbstractWebSocketConnection extends AbstractConnection implements LogicalConnection, ConnectionStateListener, Dumpable
+public abstract class AbstractWebSocketConnection extends AbstractConnection implements LogicalConnection, Connection.UpgradeTo, ConnectionStateListener, Dumpable
 {
+    private final AtomicBoolean closed = new AtomicBoolean();
+
     private class Flusher extends FrameFlusher
     {
         private Flusher(ByteBufferPool bufferPool, Generator generator, EndPoint endpoint)
@@ -70,7 +72,7 @@
         @Override
         protected void onFailure(Throwable x)
         {
-            session.notifyError(x);
+            notifyError(x);
 
             if (ioState.wasAbnormalClose())
             {
@@ -156,8 +158,8 @@
 
         private void onLocalClose()
         {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Local Close Confirmed {}",close);
+            if (LOG_CLOSE.isDebugEnabled())
+                LOG_CLOSE.debug("Local Close Confirmed {}",close);
             if (close.isAbnormal())
             {
                 ioState.onAbnormalClose(close);
@@ -190,7 +192,7 @@
             return countOnFillableEvents.get();
         }
     }
-    
+
     private static enum ReadMode
     {
         PARSE,
@@ -199,6 +201,8 @@
     }
 
     private static final Logger LOG = Log.getLogger(AbstractWebSocketConnection.class);
+    private static final Logger LOG_OPEN = Log.getLogger(AbstractWebSocketConnection.class.getName() + "_OPEN");
+    private static final Logger LOG_CLOSE = Log.getLogger(AbstractWebSocketConnection.class.getName() + "_CLOSE");
 
     /**
      * Minimum size of a buffer is the determined to be what would be the maximum framing header size (not including payload)
@@ -212,17 +216,22 @@
     private final WebSocketPolicy policy;
     private final AtomicBoolean suspendToken;
     private final FrameFlusher flusher;
-    private WebSocketSession session;
+    private final String id;
     private List<ExtensionConfig> extensions;
     private boolean isFilling;
-    private ByteBuffer buffer;
+    private ByteBuffer prefillBuffer;
     private ReadMode readMode = ReadMode.PARSE;
     private IOState ioState;
     private Stats stats = new Stats();
 
     public AbstractWebSocketConnection(EndPoint endp, Executor executor, Scheduler scheduler, WebSocketPolicy policy, ByteBufferPool bufferPool)
     {
-        super(endp,executor,EXECUTE_ONFILLABLE); // TODO review if this is best. Specifically with MUX
+        super(endp,executor);
+        this.id = String.format("%s:%d->%s:%d",
+                endp.getLocalAddress().getAddress().getHostAddress(),
+                endp.getLocalAddress().getPort(),
+                endp.getRemoteAddress().getAddress().getHostAddress(),
+                endp.getRemoteAddress().getPort());
         this.policy = policy;
         this.bufferPool = bufferPool;
         this.generator = new Generator(policy,bufferPool);
@@ -249,8 +258,9 @@
     @Override
     public void close()
     {
-        CloseInfo close = new CloseInfo();
-        this.outgoingFrame(close.asFrame(),new OnCloseLocalCallback(close),BatchMode.OFF);
+        if (LOG_CLOSE.isDebugEnabled())
+            LOG_CLOSE.debug("close()");
+        close(new CloseInfo());
     }
 
     /**
@@ -258,7 +268,7 @@
      * <p>                    fillInterested();
 
      * This can result in a close handshake over the network, or a simple local abnormal close
-     * 
+     *
      * @param statusCode
      *            the WebSocket status code.
      * @param reason
@@ -268,33 +278,41 @@
     @Override
     public void close(int statusCode, String reason)
     {
-        if (LOG.isDebugEnabled())
-            LOG.debug("close({},{})",statusCode,reason);
-        CloseInfo close = new CloseInfo(statusCode,reason);
-        this.outgoingFrame(close.asFrame(),new OnCloseLocalCallback(close),BatchMode.OFF);
+        if (LOG_CLOSE.isDebugEnabled())
+            LOG_CLOSE.debug("close({},{})", statusCode, reason);
+        close(new CloseInfo(statusCode, reason));
+    }
+
+    private void close(CloseInfo closeInfo)
+    {
+        if (closed.compareAndSet(false, true))
+            outgoingFrame(closeInfo.asFrame(), new OnCloseLocalCallback(closeInfo), BatchMode.OFF);
     }
 
     @Override
     public void disconnect()
     {
+        if (LOG_CLOSE.isDebugEnabled())
+            LOG_CLOSE.debug("{} disconnect()",policy.getBehavior());
         disconnect(false);
     }
 
     private void disconnect(boolean onlyOutput)
     {
-        if (LOG.isDebugEnabled())
-            LOG.debug("{} disconnect({})",policy.getBehavior(),onlyOutput?"outputOnly":"both");
+        if (LOG_CLOSE.isDebugEnabled())
+            LOG_CLOSE.debug("{} disconnect({})",policy.getBehavior(),onlyOutput?"outputOnly":"both");
         // close FrameFlusher, we cannot write anymore at this point.
         flusher.close();
         EndPoint endPoint = getEndPoint();
         // We need to gently close first, to allow
         // SSL close alerts to be sent by Jetty
-        if (LOG.isDebugEnabled())
-            LOG.debug("Shutting down output {}",endPoint);
+        if (LOG_CLOSE.isDebugEnabled())
+            LOG_CLOSE.debug("Shutting down output {}",endPoint);
         endPoint.shutdownOutput();
         if (!onlyOutput)
         {
-            LOG.debug("Closing {}",endPoint);
+            if (LOG_CLOSE.isDebugEnabled())
+                LOG_CLOSE.debug("Closing {}",endPoint);
             endPoint.close();
         }
     }
@@ -329,7 +347,7 @@
      * Get the list of extensions in use.
      * <p>
      * This list is negotiated during the WebSocket Upgrade Request/Response handshake.
-     * 
+     *
      * @return the list of negotiated extensions in use.
      */
     public List<ExtensionConfig> getExtensions()
@@ -343,6 +361,12 @@
     }
 
     @Override
+    public String getId()
+    {
+        return id;
+    }
+
+    @Override
     public long getIdleTimeout()
     {
         return getEndPoint().getIdleTimeout();
@@ -382,12 +406,6 @@
         return scheduler;
     }
 
-    @Override
-    public WebSocketSession getSession()
-    {
-        return session;
-    }
-
     public Stats getStats()
     {
         return stats;
@@ -396,7 +414,7 @@
     @Override
     public boolean isOpen()
     {
-        return getIOState().isOpen() && getEndPoint().isOpen();
+        return !closed.get();
     }
 
     @Override
@@ -423,21 +441,31 @@
     @Override
     public void onConnectionStateChange(ConnectionState state)
     {
-        if (LOG.isDebugEnabled())
-            LOG.debug("{} Connection State Change: {}",policy.getBehavior(),state);
+        if (LOG_CLOSE.isDebugEnabled())
+            LOG_CLOSE.debug("{} Connection State Change: {}",policy.getBehavior(),state);
+
         switch (state)
         {
             case OPEN:
-                if (BufferUtil.isEmpty(buffer))
+                if (BufferUtil.hasContent(prefillBuffer))
                 {
                     if (LOG.isDebugEnabled())
-                        LOG.debug("fillInterested");
-                    fillInterested();
+                    {
+                        LOG.debug("Parsing Upgrade prefill buffer ({} remaining)",prefillBuffer.remaining());
+                    }
+                    parser.parse(prefillBuffer);
                 }
-                else
-                    onFillable();
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("OPEN: normal fillInterested");
+                }
+                // TODO: investigate what happens if a failure occurs during prefill, and an attempt to write close fails,
+                // should a fill interested occur? or just a quick disconnect?
+                fillInterested();
                 break;
             case CLOSED:
+                if (LOG_CLOSE.isDebugEnabled())
+                    LOG_CLOSE.debug("CLOSED - wasAbnormalClose: {}", ioState.wasAbnormalClose());
                 if (ioState.wasAbnormalClose())
                 {
                     // Fire out a close frame, indicating abnormal shutdown, then disconnect
@@ -451,6 +479,8 @@
                 }
                 break;
             case CLOSING:
+                if (LOG_CLOSE.isDebugEnabled())
+                    LOG_CLOSE.debug("CLOSING - wasRemoteCloseInitiated: {}", ioState.wasRemoteCloseInitiated());
                 // First occurrence of .onCloseLocal or .onCloseRemote use
                 if (ioState.wasRemoteCloseInitiated())
                 {
@@ -469,8 +499,9 @@
         if (LOG.isDebugEnabled())
             LOG.debug("{} onFillable()",policy.getBehavior());
         stats.countOnFillableEvents.incrementAndGet();
-        if (buffer==null)
-            buffer = bufferPool.acquire(getInputBufferSize(),true);
+
+        ByteBuffer buffer = bufferPool.acquire(getInputBufferSize(),true);
+
         try
         {
             isFilling = true;
@@ -478,7 +509,7 @@
             if(readMode == ReadMode.PARSE)
             {
                 readMode = readParse(buffer);
-            } 
+            }
             else
             {
                 readMode = readDiscard(buffer);
@@ -487,7 +518,6 @@
         finally
         {
             bufferPool.release(buffer);
-            buffer=null;
         }
 
         if ((readMode != ReadMode.EOF) && (suspendToken.get() == false))
@@ -500,8 +530,6 @@
         }
     }
 
-    
-
     @Override
     protected void onFillInterestedFailed(Throwable cause)
     {
@@ -510,14 +538,31 @@
         super.onFillInterestedFailed(cause);
     }
 
-    protected void prefill(ByteBuffer prefilled)
+    /**
+     * Extra bytes from the initial HTTP upgrade that need to
+     * be processed by the websocket parser before starting
+     * to read bytes from the connection
+     * @param prefilled the bytes of prefilled content encountered during upgrade
+     */
+    protected void setInitialBuffer(ByteBuffer prefilled)
     {
-        buffer=prefilled;
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("set Initial Buffer - {}",BufferUtil.toDetailString(prefilled));
+        }
+        prefillBuffer = prefilled;
     }
-    
+
+    private void notifyError(Throwable t)
+    {
+        getParser().getIncomingFramesHandler().incomingError(t);
+    }
+
     @Override
     public void onOpen()
     {
+        if(LOG_OPEN.isDebugEnabled())
+            LOG_OPEN.debug("[{}] {}.onOpened()",policy.getBehavior(),this.getClass().getSimpleName());
         super.onOpen();
         this.ioState.onOpened();
     }
@@ -530,11 +575,13 @@
     {
         IOState state = getIOState();
         ConnectionState cstate = state.getConnectionState();
-        if (LOG.isDebugEnabled())
-            LOG.debug("{} Read Timeout - {}",policy.getBehavior(),cstate);
+        if (LOG_CLOSE.isDebugEnabled())
+            LOG_CLOSE.debug("{} Read Timeout - {}",policy.getBehavior(),cstate);
 
         if (cstate == ConnectionState.CLOSED)
         {
+            if (LOG_CLOSE.isDebugEnabled())
+                LOG_CLOSE.debug("onReadTimeout - Connection Already CLOSED");
             // close already completed, extra timeouts not relevant
             // allow underlying connection and endpoint to disconnect on its own
             return true;
@@ -542,7 +589,7 @@
 
         try
         {
-            session.notifyError(new SocketTimeoutException("Timeout on Read"));
+            notifyError(new SocketTimeoutException("Timeout on Read"));
         }
         finally
         {
@@ -581,15 +628,14 @@
                 }
                 else if (filled < 0)
                 {
-                    LOG.debug("read - EOF Reached (remote: {})",getRemoteAddress());
+                    if (LOG_CLOSE.isDebugEnabled())
+                        LOG_CLOSE.debug("read - EOF Reached (remote: {})",getRemoteAddress());
                     return ReadMode.EOF;
                 }
                 else
                 {
-                    if (LOG.isDebugEnabled())
-                    {
-                        LOG.debug("Discarded {} bytes - {}",filled,BufferUtil.toDetailString(buffer));
-                    }
+                    if (LOG_CLOSE.isDebugEnabled())
+                        LOG_CLOSE.debug("Discarded {} bytes - {}",filled,BufferUtil.toDetailString(buffer));
                 }
             }
         }
@@ -604,33 +650,33 @@
             return ReadMode.DISCARD;
         }
     }
-    
+
     private ReadMode readParse(ByteBuffer buffer)
     {
         EndPoint endPoint = getEndPoint();
         try
         {
-            while (true) // TODO: should this honor the LogicalConnection.suspend() ?
+            // Process the content from the Endpoint next
+            while(true)  // TODO: should this honor the LogicalConnection.suspend() ?
             {
                 int filled = endPoint.fill(buffer);
-                if (filled == 0)
-                {
-                    return ReadMode.PARSE;
-                }
-                else if (filled < 0)
+                if (filled < 0)
                 {
                     LOG.debug("read - EOF Reached (remote: {})",getRemoteAddress());
                     ioState.onReadFailure(new EOFException("Remote Read EOF"));
                     return ReadMode.EOF;
                 }
-                else
+                else if (filled == 0)
                 {
-                    if (LOG.isDebugEnabled())
-                    {
-                        LOG.debug("Filled {} bytes - {}",filled,BufferUtil.toDetailString(buffer));
-                    }
-                    parser.parse(buffer);
+                    // Done reading, wait for next onFillable
+                    return ReadMode.PARSE;
                 }
+
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("Filled {} bytes - {}",filled,BufferUtil.toDetailString(buffer));
+                }
+                parser.parse(buffer);
             }
         }
         catch (IOException e)
@@ -667,7 +713,7 @@
      * Get the list of extensions in use.
      * <p>
      * This list is negotiated during the WebSocket Upgrade Request/Response handshake.
-     * 
+     *
      * @param extensions
      *            the list of negotiated extensions in use.
      */
@@ -693,12 +739,6 @@
     }
 
     @Override
-    public void setSession(WebSocketSession session)
-    {
-        this.session = session;
-    }
-
-    @Override
     public SuspendToken suspend()
     {
         suspendToken.set(true);
@@ -720,7 +760,54 @@
     @Override
     public String toString()
     {
-        return String.format("%s{f=%s,g=%s,p=%s}",super.toString(),flusher,generator,parser);
+        return String.format("%s@%X{endp=%s,ios=%s,f=%s,g=%s,p=%s}",getClass().getSimpleName(),hashCode(),getEndPoint(),ioState,flusher,generator,parser);
     }
 
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+
+        EndPoint endp = getEndPoint();
+        if(endp != null)
+        {
+            result = prime * result + endp.getLocalAddress().hashCode();
+            result = prime * result + endp.getRemoteAddress().hashCode();
+        }
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        AbstractWebSocketConnection other = (AbstractWebSocketConnection)obj;
+        EndPoint endp = getEndPoint();
+        EndPoint otherEndp = other.getEndPoint();
+        if (endp == null)
+        {
+            if (otherEndp != null)
+                return false;
+        }
+        else if (!endp.equals(otherEndp))
+            return false;
+        return true;
+    }
+
+    /**
+     * Extra bytes from the initial HTTP upgrade that need to
+     * be processed by the websocket parser before starting
+     * to read bytes from the connection
+     */
+    @Override
+    public void onUpgradeTo(ByteBuffer prefilled)
+    {
+        setInitialBuffer(prefilled);
+    }
 }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java
index 873e70d..ef28131 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java
@@ -20,14 +20,15 @@
 
 import java.io.EOFException;
 import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Deque;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.util.ArrayQueue;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.IteratingCallback;
 import org.eclipse.jetty.util.log.Log;
@@ -40,7 +41,7 @@
 import org.eclipse.jetty.websocket.common.frames.BinaryFrame;
 
 /**
- * Interface for working with bytes destined for {@link EndPoint#write(Callback, ByteBuffer...)}
+ * Interface for working with bytes destined for {@link EndPoint#write(org.eclipse.jetty.util.Callback, ByteBuffer...)}
  */
 public class FrameFlusher
 {
@@ -163,7 +164,7 @@
             {
                 while ((entries.size() <= maxGather) && !queue.isEmpty())
                 {
-                    FrameEntry entry = queue.remove(0);
+                    FrameEntry entry = queue.poll();
                     currentBatchMode = BatchMode.max(currentBatchMode,entry.batchMode);
 
                     // Force flush if we need to.
@@ -293,7 +294,7 @@
     private final Generator generator;
     private final int maxGather;
     private final Object lock = new Object();
-    private final ArrayQueue<FrameEntry> queue = new ArrayQueue<>(16,16,lock);
+    private final Deque<FrameEntry> queue = new ArrayDeque<>();
     private final Flusher flusher;
     private final AtomicBoolean closed = new AtomicBoolean();
     private volatile Throwable failure;
@@ -353,7 +354,7 @@
                 case OpCode.PING:
                 {
                     // Prepend PINGs so they are processed first.
-                    queue.add(0,entry);
+                    queue.offerFirst(entry);
                     break;
                 }
                 case OpCode.CLOSE:
@@ -362,12 +363,12 @@
                     // added after this close frame, but we will
                     // fail them later to keep it simple here.
                     closed.set(true);
-                    queue.add(entry);
+                    queue.offer(entry);
                     break;
                 }
                 default:
                 {
-                    queue.add(entry);
+                    queue.offer(entry);
                     break;
                 }
             }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FutureWriteCallback.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FutureWriteCallback.java
index d5b4b39..0cdd5c4 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FutureWriteCallback.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FutureWriteCallback.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.common.io;
 
+import java.util.concurrent.Future;
+
 import org.eclipse.jetty.util.FutureCallback;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java
index 7647631..4e69e60 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java
@@ -44,7 +44,7 @@
     /**
      * The source of a close handshake. (ie: who initiated it).
      */
-    private static enum CloseHandshakeSource
+    private enum CloseHandshakeSource
     {
         /** No close handshake initiated (yet) */
         NONE,
@@ -53,7 +53,7 @@
         /** Remote side initiated the close handshake */
         REMOTE,
         /** An abnormal close situation (disconnect, timeout, etc...) */
-        ABNORMAL;
+        ABNORMAL
     }
 
     public static interface ConnectionStateListener
@@ -65,17 +65,17 @@
     private ConnectionState state;
     private final List<ConnectionStateListener> listeners = new CopyOnWriteArrayList<>();
 
-    /** 
+    /**
      * Is input on websocket available (for reading frames).
      * Used to determine close handshake completion, and track half-close states
      */
     private boolean inputAvailable;
-    /** 
+    /**
      * Is output on websocket available (for writing frames).
      * Used to determine close handshake completion, and track half-closed states.
      */
     private boolean outputAvailable;
-    /** 
+    /**
      * Initiator of the close handshake.
      * Used to determine who initiated a close handshake for reply reasons.
      */
@@ -150,7 +150,7 @@
 
     public boolean isClosed()
     {
-        synchronized (state)
+        synchronized (this)
         {
             return (state == ConnectionState.CLOSED);
         }
@@ -163,7 +163,7 @@
 
     public boolean isOpen()
     {
-        return (getConnectionState() != ConnectionState.CLOSED);
+        return !isClosed();
     }
 
     public boolean isOutputAvailable()
@@ -189,6 +189,7 @@
      * A websocket connection has been disconnected for abnormal close reasons.
      * <p>
      * This is the low level disconnect of the socket. It could be the result of a normal close operation, from an IO error, or even from a timeout.
+     * @param close the close information
      */
     public void onAbnormalClose(CloseInfo close)
     {
@@ -220,65 +221,87 @@
 
     /**
      * A close handshake has been issued from the local endpoint
+     * @param closeInfo the close information
      */
-    public void onCloseLocal(CloseInfo close)
+    public void onCloseLocal(CloseInfo closeInfo)
+    {
+        boolean open = false;
+        synchronized (this)
+        {
+            ConnectionState initialState = this.state;
+            if (LOG.isDebugEnabled())
+                LOG.debug("onCloseLocal({}) : {}", closeInfo, initialState);
+            if (initialState == ConnectionState.CLOSED)
+            {
+                // already closed
+                if (LOG.isDebugEnabled())
+                    LOG.debug("already closed");
+                return;
+            }
+
+            if (initialState == ConnectionState.CONNECTED)
+            {
+                // fast close. a local close request from end-user onConnect/onOpen method
+                if (LOG.isDebugEnabled())
+                    LOG.debug("FastClose in CONNECTED detected");
+                open = true;
+            }
+        }
+
+        if (open)
+            openAndCloseLocal(closeInfo);
+        else
+            closeLocal(closeInfo);
+    }
+
+    private void openAndCloseLocal(CloseInfo closeInfo)
+    {
+        // Force the state open (to allow read/write to endpoint)
+        onOpened();
+        if (LOG.isDebugEnabled())
+            LOG.debug("FastClose continuing with Closure");
+        closeLocal(closeInfo);
+    }
+
+    private void closeLocal(CloseInfo closeInfo)
     {
         ConnectionState event = null;
         ConnectionState abnormalEvent = null;
-        ConnectionState initialState = this.state;
-        if (LOG.isDebugEnabled())
-            LOG.debug("onCloseLocal({}) : {}",close,initialState);
-        if (initialState == ConnectionState.CLOSED)
-        {
-            // already closed
-            LOG.debug("already closed");
-            return;
-        }
-
-        if (initialState == ConnectionState.CONNECTED)
-        {
-            // fast close. a local close request from end-user onConnect/onOpen method
-            LOG.debug("FastClose in CONNECTED detected");
-            // Force the state open (to allow read/write to endpoint)
-            onOpened();
-            if (LOG.isDebugEnabled())
-                LOG.debug("FastClose continuing with Closure");
-        }
-
         synchronized (this)
         {
-            closeInfo = close;
+            if (LOG.isDebugEnabled())
+                LOG.debug("onCloseLocal(), input={}, output={}", inputAvailable, outputAvailable);
 
-            boolean in = inputAvailable;
-            boolean out = outputAvailable;
+            this.closeInfo = closeInfo;
+
+            // Turn off further output.
+            outputAvailable = false;
+
             if (closeHandshakeSource == CloseHandshakeSource.NONE)
             {
                 closeHandshakeSource = CloseHandshakeSource.LOCAL;
             }
-            out = false;
-            outputAvailable = false;
 
-            LOG.debug("onCloseLocal(), input={}, output={}",in,out);
-
-            if (!in && !out)
+            if (!inputAvailable)
             {
-                LOG.debug("Close Handshake satisfied, disconnecting");
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Close Handshake satisfied, disconnecting");
                 cleanClose = true;
                 this.state = ConnectionState.CLOSED;
-                finalClose.compareAndSet(null,close);
+                finalClose.compareAndSet(null,closeInfo);
                 event = this.state;
             }
             else if (this.state == ConnectionState.OPEN)
             {
-                // We are now entering CLOSING (or half-closed)
+                // We are now entering CLOSING (or half-closed).
                 this.state = ConnectionState.CLOSING;
                 event = this.state;
-                
-                // if abnormal, we don't expect an answer.
-                if (close.isAbnormal())
+
+                // If abnormal, we don't expect an answer.
+                if (closeInfo.isAbnormal())
                 {
                     abnormalEvent = ConnectionState.CLOSED;
-                    finalClose.compareAndSet(null,close);
+                    finalClose.compareAndSet(null,closeInfo);
                     cleanClose = false;
                     outputAvailable = false;
                     inputAvailable = false;
@@ -291,8 +314,7 @@
         if (event != null)
         {
             notifyStateListeners(event);
-            
-            if(abnormalEvent != null) 
+            if (abnormalEvent != null)
             {
                 notifyStateListeners(abnormalEvent);
             }
@@ -301,11 +323,12 @@
 
     /**
      * A close handshake has been received from the remote endpoint
+     * @param closeInfo the close information
      */
-    public void onCloseRemote(CloseInfo close)
+    public void onCloseRemote(CloseInfo closeInfo)
     {
         if (LOG.isDebugEnabled())
-            LOG.debug("onCloseRemote({})",close);
+            LOG.debug("onCloseRemote({})", closeInfo);
         ConnectionState event = null;
         synchronized (this)
         {
@@ -315,26 +338,25 @@
                 return;
             }
 
-            closeInfo = close;
+            if (LOG.isDebugEnabled())
+                LOG.debug("onCloseRemote(), input={}, output={}", inputAvailable, outputAvailable);
 
-            boolean in = inputAvailable;
-            boolean out = outputAvailable;
+            this.closeInfo = closeInfo;
+
+            // turn off further input
+            inputAvailable = false;
+
             if (closeHandshakeSource == CloseHandshakeSource.NONE)
             {
                 closeHandshakeSource = CloseHandshakeSource.REMOTE;
             }
-            in = false;
-            inputAvailable = false;
 
-            if (LOG.isDebugEnabled())
-                LOG.debug("onCloseRemote(), input={}, output={}",in,out);
-
-            if (!in && !out)
+            if (!outputAvailable)
             {
                 LOG.debug("Close Handshake satisfied, disconnecting");
                 cleanClose = true;
                 state = ConnectionState.CLOSED;
-                finalClose.compareAndSet(null,close);
+                finalClose.compareAndSet(null,closeInfo);
                 event = this.state;
             }
             else if (this.state == ConnectionState.OPEN)
@@ -399,6 +421,9 @@
      */
     public void onOpened()
     {
+        if(LOG.isDebugEnabled())
+            LOG.debug(" onOpened()");
+
         ConnectionState event = null;
         synchronized (this)
         {
@@ -426,6 +451,7 @@
      * The local endpoint has reached a read failure.
      * <p>
      * This could be a normal result after a proper close handshake, or even a premature close due to a connection disconnect.
+     * @param t the read failure
      */
     public void onReadFailure(Throwable t)
     {
@@ -476,6 +502,7 @@
      * The local endpoint has reached a write failure.
      * <p>
      * A low level I/O failure, or even a jetty side EndPoint close (from idle timeout) are a few scenarios
+     * @param t the throwable that caused the write failure
      */
     public void onWriteFailure(Throwable t)
     {
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/WriteCallbackWrapper.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/WriteCallbackWrapper.java
deleted file mode 100644
index c88e42e..0000000
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/WriteCallbackWrapper.java
+++ /dev/null
@@ -1,58 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.common.io;
-
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.websocket.api.WriteCallback;
-
-/**
- * Wraps the exposed {@link WriteCallback} WebSocket API with a Jetty {@link Callback}.
- * <p>
- * We don't expose the jetty {@link Callback} object to the webapp, as that makes things complicated for the WebAppContext's Classloader.
- */
-public class WriteCallbackWrapper implements Callback
-{
-    public static Callback wrap(WriteCallback callback)
-    {
-        if (callback == null)
-        {
-            return null;
-        }
-        return new WriteCallbackWrapper(callback);
-    }
-
-    private final WriteCallback callback;
-
-    public WriteCallbackWrapper(WriteCallback callback)
-    {
-        this.callback = callback;
-    }
-
-    @Override
-    public void failed(Throwable x)
-    {
-        callback.writeFailed(x);
-    }
-
-    @Override
-    public void succeeded()
-    {
-        callback.writeSuccess();
-    }
-}
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/http/HttpResponseHeaderParser.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/http/HttpResponseHeaderParser.java
index 041a035..bdb9395 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/http/HttpResponseHeaderParser.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/http/HttpResponseHeaderParser.java
@@ -80,7 +80,9 @@
             {
                 if (parseHeader(line))
                 {
-                    // Finished parsing entire header
+                    // Now finished with parsing the entire response header
+                    // Save the remaining bytes for WebSocket to process.
+                    
                     ByteBuffer copy = ByteBuffer.allocate(buf.remaining());
                     BufferUtil.put(buf,copy);
                     BufferUtil.flipToFlush(copy,0);
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/payload/PayloadProcessor.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/payload/PayloadProcessor.java
index b2fdf51..35ec3ad 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/payload/PayloadProcessor.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/payload/PayloadProcessor.java
@@ -20,6 +20,7 @@
 
 import java.nio.ByteBuffer;
 
+import org.eclipse.jetty.websocket.api.BadPayloadException;
 import org.eclipse.jetty.websocket.api.extensions.Frame;
 
 /**
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/message/MessageInputStream.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/message/MessageInputStream.java
index 5d029d3..14fe3c4 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/message/MessageInputStream.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/message/MessageInputStream.java
@@ -32,7 +32,7 @@
 
 /**
  * Support class for reading a (single) WebSocket BINARY message via a InputStream.
- * <p/>
+ * <p>
  * An InputStream that can access a queue of ByteBuffer payloads, along with expected InputStream blocking behavior.
  */
 public class MessageInputStream extends InputStream implements MessageAppender
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/message/MessageReader.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/message/MessageReader.java
index 6d957df..fb17766 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/message/MessageReader.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/message/MessageReader.java
@@ -21,11 +21,12 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 
 /**
  * Support class for reading a (single) WebSocket TEXT message via a Reader.
- * <p/>
+ * <p>
  * In compliance to the WebSocket spec, this reader always uses the UTF8 {@link Charset}.
  */
 public class MessageReader extends InputStreamReader implements MessageAppender
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/message/MessageWriter.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/message/MessageWriter.java
index e0170b7..33e707f 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/message/MessageWriter.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/message/MessageWriter.java
@@ -36,7 +36,7 @@
 
 /**
  * Support for writing a single WebSocket TEXT message via a {@link Writer}
- * <p/>
+ * <p>
  * Note: Per WebSocket spec, all WebSocket TEXT messages must be encoded in UTF-8
  */
 public class MessageWriter extends Writer
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/package-info.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/package-info.java
index 545146f..8383edc 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/package-info.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/package-info.java
@@ -21,7 +21,7 @@
  * <p>
  * A core set of internal implementation classes for the Jetty WebSocket API.
  * <p>
- * Note: do not reference or use classes present in this package space in your code. <br />
+ * Note: do not reference or use classes present in this package space in your code. <br>
  * Restrict your usage to the Jetty WebSocket API classes, the Jetty WebSocket Client API,
  * or the Jetty WebSocket Servlet API.
  */
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/SimpleContainerScope.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/SimpleContainerScope.java
new file mode 100644
index 0000000..9728acc
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/SimpleContainerScope.java
@@ -0,0 +1,119 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.scopes;
+
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.util.DecoratedObjectFactory;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.common.WebSocketSession;
+
+public class SimpleContainerScope extends ContainerLifeCycle implements WebSocketContainerScope
+{
+    private final ByteBufferPool bufferPool;
+    private final DecoratedObjectFactory objectFactory;
+    private final WebSocketPolicy policy;
+    private Executor executor;
+    private SslContextFactory sslContextFactory;
+
+    public SimpleContainerScope(WebSocketPolicy policy)
+    {
+        this(policy,new MappedByteBufferPool(),new DecoratedObjectFactory());
+    }
+
+    public SimpleContainerScope(WebSocketPolicy policy, ByteBufferPool bufferPool)
+    {
+        this(policy,bufferPool,new DecoratedObjectFactory());
+    }
+
+    public SimpleContainerScope(WebSocketPolicy policy, ByteBufferPool bufferPool, DecoratedObjectFactory objectFactory)
+    {
+        this.policy = policy;
+        this.bufferPool = bufferPool;
+        this.objectFactory = objectFactory;
+
+        QueuedThreadPool threadPool = new QueuedThreadPool();
+        String name = "WebSocketSimpleContainer@" + hashCode();
+        threadPool.setName(name);
+        threadPool.setDaemon(true);
+        this.executor = threadPool;
+    }
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        super.doStart();
+    }
+
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+    }
+
+    @Override
+    public ByteBufferPool getBufferPool()
+    {
+        return this.bufferPool;
+    }
+
+    @Override
+    public Executor getExecutor()
+    {
+        return this.executor;
+    }
+
+    @Override
+    public DecoratedObjectFactory getObjectFactory()
+    {
+        return this.objectFactory;
+    }
+
+    @Override
+    public WebSocketPolicy getPolicy()
+    {
+        return this.policy;
+    }
+
+    @Override
+    public SslContextFactory getSslContextFactory()
+    {
+        return this.sslContextFactory;
+    }
+
+    public void setSslContextFactory(SslContextFactory sslContextFactory)
+    {
+        this.sslContextFactory = sslContextFactory;
+    }
+
+    @Override
+    public void onSessionOpened(WebSocketSession session)
+    {
+    }
+
+    @Override
+    public void onSessionClosed(WebSocketSession session)
+    {
+    }
+}
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketContainerScope.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketContainerScope.java
new file mode 100644
index 0000000..0983558
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketContainerScope.java
@@ -0,0 +1,89 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.scopes;
+
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.DecoratedObjectFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.common.WebSocketSession;
+
+/**
+ * Defined Scope for a WebSocketContainer.
+ */
+public interface WebSocketContainerScope
+{
+    /**
+     * The configured Container Buffer Pool.
+     * 
+     * @return the buffer pool (never null)
+     */
+    ByteBufferPool getBufferPool();
+
+    /**
+     * Executor in use by the container.
+     * 
+     * @return the Executor in use by the container.
+     */
+    Executor getExecutor();
+
+    /**
+     * Object Factory used to create objects.
+     * 
+     * @return Object Factory used to create instances of objects.
+     */
+    DecoratedObjectFactory getObjectFactory();
+
+    /**
+     * The policy the container is running on.
+     * 
+     * @return the websocket policy
+     */
+    WebSocketPolicy getPolicy();
+
+    /**
+     * The SslContextFactory in use by the container.
+     * 
+     * @return the SslContextFactory in use by the container (can be null if no SSL context is defined)
+     */
+    SslContextFactory getSslContextFactory();
+    
+    /**
+     * Test for if the container has been started.
+     *
+     * @return true if container is started and running
+     */
+    boolean isRunning();
+    
+    /**
+     * A Session has been opened
+     * 
+     * @param session the session that was opened
+     */
+    void onSessionOpened(WebSocketSession session);
+    
+    /**
+     * A Session has been closed
+     * 
+     * @param session the session that was closed
+     */
+    void onSessionClosed(WebSocketSession session);
+}
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketSessionScope.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketSessionScope.java
new file mode 100644
index 0000000..8a0be03
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketSessionScope.java
@@ -0,0 +1,39 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.scopes;
+
+import org.eclipse.jetty.websocket.common.WebSocketSession;
+
+/**
+ * Defined Scope for a WebSocketSession (active connection)
+ */
+public interface WebSocketSessionScope
+{
+    /**
+     * Active {@link WebSocketSession} associated with this scope.
+     * @return the websocket session
+     */
+    WebSocketSession getWebSocketSession();
+
+    /**
+     * The parent {@link WebSocketContainerScope} for this session scope.
+     * @return the websocket container scope
+     */
+    WebSocketContainerScope getContainerScope();
+}
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/TextUtil.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/TextUtil.java
index 18f5a9b..3d1a0f6 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/TextUtil.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/TextUtil.java
@@ -43,7 +43,6 @@
 
     /**
      * Smash a long string to fit within the max string length, by taking the middle section of the string and replacing them with an ellipsis "..."
-     * <p>
      * 
      * <pre>
      * Examples:
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/Utf8PartialBuilder.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/Utf8PartialBuilder.java
new file mode 100644
index 0000000..6f85fbf
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/Utf8PartialBuilder.java
@@ -0,0 +1,63 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.util;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.util.Utf8Appendable;
+import org.eclipse.jetty.util.Utf8StringBuilder;
+
+/**
+ * Similar in scope to the {@link Utf8StringBuilder}, but allowing partially constructed Strings without throwing
+ * Exceptions for incomplete UTF8 sequences.
+ * <p>
+ * A call to {@link #toPartialString(ByteBuffer)} will return the section of the String from the start to the last
+ * completed UTF8 sequence. Leaving incomplete sequences for a subsequent call to complete.
+ */
+public class Utf8PartialBuilder
+{
+    private final StringBuilder str;
+    private final Utf8Appendable utf8;
+
+    public Utf8PartialBuilder()
+    {
+        this.str = new StringBuilder();
+        this.utf8 = new Utf8Appendable(str)
+        {
+            @Override
+            public int length()
+            {
+                return str.length();
+            }
+        };
+    }
+
+    public String toPartialString(ByteBuffer buf)
+    {
+        if (buf == null)
+        {
+            // no change, return empty
+            return "";
+        }
+        utf8.append(buf);
+        String ret = str.toString();
+        str.setLength(0);
+        return ret;
+    }
+}
diff --git a/jetty-websocket/websocket-common/src/test/java/examples/ListenerFrameSocket.java b/jetty-websocket/websocket-common/src/test/java/examples/ListenerFrameSocket.java
new file mode 100644
index 0000000..cb287ff
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/test/java/examples/ListenerFrameSocket.java
@@ -0,0 +1,53 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package examples;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.WebSocketFrameListener;
+import org.eclipse.jetty.websocket.api.extensions.Frame;
+import org.eclipse.jetty.websocket.common.events.EventCapture;
+
+public class ListenerFrameSocket implements WebSocketFrameListener
+{
+    public EventCapture capture = new EventCapture();
+
+    @Override
+    public void onWebSocketClose(int statusCode, String reason)
+    {
+        capture.add("onWebSocketClose(%d, %s)",statusCode,capture.q(reason));
+    }
+
+    @Override
+    public void onWebSocketConnect(Session session)
+    {
+        capture.add("onWebSocketConnect(%s)",session);
+    }
+
+    @Override
+    public void onWebSocketError(Throwable cause)
+    {
+        capture.add("onWebSocketError((%s) %s)",cause.getClass().getSimpleName(),cause.getMessage());
+    }
+    
+    @Override
+    public void onWebSocketFrame(Frame frame)
+    {
+        capture.add("onWebSocketFrame(%s, %d, %b)", frame.getType(), frame.getPayload().remaining(), frame.isFin());
+    }
+}
diff --git a/jetty-websocket/websocket-common/src/test/java/examples/ListenerPartialSocket.java b/jetty-websocket/websocket-common/src/test/java/examples/ListenerPartialSocket.java
new file mode 100644
index 0000000..3afab11
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/test/java/examples/ListenerPartialSocket.java
@@ -0,0 +1,60 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package examples;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.WebSocketPartialListener;
+import org.eclipse.jetty.websocket.common.events.EventCapture;
+
+public class ListenerPartialSocket implements WebSocketPartialListener
+{
+    public EventCapture capture = new EventCapture();
+
+    @Override
+    public void onWebSocketClose(int statusCode, String reason)
+    {
+        capture.add("onWebSocketClose(%d, %s)",statusCode,capture.q(reason));
+    }
+
+    @Override
+    public void onWebSocketConnect(Session session)
+    {
+        capture.add("onWebSocketConnect(%s)",session);
+    }
+
+    @Override
+    public void onWebSocketError(Throwable cause)
+    {
+        capture.add("onWebSocketError((%s) %s)",cause.getClass().getSimpleName(),cause.getMessage());
+    }
+    
+    @Override
+    public void onWebSocketPartialText(String payload, boolean fin)
+    {
+        capture.add("onWebSocketPartialText('%s', %b)",payload,fin);
+    }
+    
+    @Override
+    public void onWebSocketPartialBinary(ByteBuffer payload, boolean fin)
+    {
+        capture.add("onWebSocketPartialBinary(%s [%d], %b)",payload,payload.remaining(),fin);
+    }
+}
diff --git a/jetty-websocket/websocket-common/src/test/java/examples/ListenerPingPongSocket.java b/jetty-websocket/websocket-common/src/test/java/examples/ListenerPingPongSocket.java
new file mode 100644
index 0000000..8d94854
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/test/java/examples/ListenerPingPongSocket.java
@@ -0,0 +1,60 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package examples;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.WebSocketPingPongListener;
+import org.eclipse.jetty.websocket.common.events.EventCapture;
+
+public class ListenerPingPongSocket implements WebSocketPingPongListener
+{
+    public EventCapture capture = new EventCapture();
+
+    @Override
+    public void onWebSocketClose(int statusCode, String reason)
+    {
+        capture.add("onWebSocketClose(%d, %s)",statusCode,capture.q(reason));
+    }
+
+    @Override
+    public void onWebSocketConnect(Session session)
+    {
+        capture.add("onWebSocketConnect(%s)",session);
+    }
+
+    @Override
+    public void onWebSocketError(Throwable cause)
+    {
+        capture.add("onWebSocketError((%s) %s)",cause.getClass().getSimpleName(),cause.getMessage());
+    }
+
+    @Override
+    public void onWebSocketPing(ByteBuffer payload)
+    {
+        capture.add("onWebSocketPing(%d)",payload.remaining());
+    }
+
+    @Override
+    public void onWebSocketPong(ByteBuffer payload)
+    {
+        capture.add("onWebSocketPong(%d)",payload.remaining());
+    }
+}
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/CloseInfoTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/CloseInfoTest.java
index d08fbb6..2966271 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/CloseInfoTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/CloseInfoTest.java
@@ -18,9 +18,15 @@
 
 package org.eclipse.jetty.websocket.common;
 
-import static org.eclipse.jetty.websocket.api.StatusCode.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.eclipse.jetty.websocket.api.StatusCode.FAILED_TLS_HANDSHAKE;
+import static org.eclipse.jetty.websocket.api.StatusCode.NORMAL;
+import static org.eclipse.jetty.websocket.api.StatusCode.NO_CLOSE;
+import static org.eclipse.jetty.websocket.api.StatusCode.NO_CODE;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 
 import java.nio.ByteBuffer;
 
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/GeneratorParserRoundtripTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/GeneratorParserRoundtripTest.java
index ef68bf2..b8d915a 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/GeneratorParserRoundtripTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/GeneratorParserRoundtripTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.common;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/GeneratorTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/GeneratorTest.java
index 2d87acc..46f7660 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/GeneratorTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/GeneratorTest.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.common;
 
+import static org.hamcrest.Matchers.is;
+
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 
@@ -39,8 +41,6 @@
 import org.junit.Assert;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.is;
-
 public class GeneratorTest
 {
     private static final Logger LOG = Log.getLogger(GeneratorTest.WindowHelper.class);
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketFrameTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketFrameTest.java
index 9ad0e00..04f2b98 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketFrameTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketFrameTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.common;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.nio.ByteBuffer;
 
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpointTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpointTest.java
index e15d138..854cb12 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpointTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpointTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.common;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase1_1.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase1_1.java
index 08ff34a..2e4d6f0 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase1_1.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase1_1.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.common.ab;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
@@ -27,6 +27,7 @@
 import org.eclipse.jetty.websocket.api.WebSocketBehavior;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.Frame;
+import org.eclipse.jetty.websocket.common.Generator;
 import org.eclipse.jetty.websocket.common.OpCode;
 import org.eclipse.jetty.websocket.common.Parser;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase1_2.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase1_2.java
index 8a54f0e..ba88367 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase1_2.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase1_2.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.common.ab;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.nio.ByteBuffer;
 
@@ -26,6 +26,7 @@
 import org.eclipse.jetty.websocket.api.WebSocketBehavior;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.Frame;
+import org.eclipse.jetty.websocket.common.Generator;
 import org.eclipse.jetty.websocket.common.OpCode;
 import org.eclipse.jetty.websocket.common.Parser;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase2.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase2.java
index f95a304..df12cd6 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase2.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase2.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.common.ab;
 
+import static org.hamcrest.Matchers.is;
+
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 
@@ -39,8 +41,6 @@
 import org.junit.Assert;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.is;
-
 public class TestABCase2
 {
     WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.CLIENT);
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadBinarySignatureSocket.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadBinarySignatureSocket.java
index 481bf6b..dc93aac 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadBinarySignatureSocket.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadBinarySignatureSocket.java
@@ -30,6 +30,11 @@
 {
     /**
      * Declaring a non-void return type
+     * @param session  the session
+     * @param buf the buffer
+     * @param offset the offset
+     * @param len the length
+     * @return the response boolean
      */
     @OnWebSocketMessage
     public boolean onBinary(Session session, byte buf[], int offset, int len)
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadDuplicateBinarySocket.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadDuplicateBinarySocket.java
index 6646f7c..0d4b822 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadDuplicateBinarySocket.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadDuplicateBinarySocket.java
@@ -31,6 +31,9 @@
 {
     /**
      * First method
+     * @param payload the payload
+     * @param offset the offset
+     * @param len the len
      */
     @OnWebSocketMessage
     public void binMe(byte[] payload, int offset, int len)
@@ -40,6 +43,7 @@
 
     /**
      * Second method
+     * @param stream the input stream
      */
     @OnWebSocketMessage
     public void streamMe(InputStream stream)
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadDuplicateFrameSocket.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadDuplicateFrameSocket.java
index 0141d6a..634eb55 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadDuplicateFrameSocket.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadDuplicateFrameSocket.java
@@ -27,6 +27,7 @@
 {
     /**
      * The get a frame
+     * @param frame the frame
      */
     @OnWebSocketFrame
     public void frameMe(Frame frame)
@@ -36,6 +37,7 @@
 
     /**
      * This is a duplicate frame type (should throw an exception attempting to use)
+     * @param frame the frame
      */
     @OnWebSocketFrame
     public void watchMe(Frame frame)
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadTextSignatureSocket.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadTextSignatureSocket.java
index f6dee72..42d7b67 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadTextSignatureSocket.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/BadTextSignatureSocket.java
@@ -30,6 +30,8 @@
 {
     /**
      * Declaring a static method
+     * @param session the session
+     * @param text the text message
      */
     @OnWebSocketMessage
     public static void onText(Session session, String text)
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/FrameSocket.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/FrameSocket.java
index c0d59f8..c06e128 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/FrameSocket.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/annotations/FrameSocket.java
@@ -27,6 +27,7 @@
 {
     /**
      * A frame
+     * @param frame the frame
      */
     @OnWebSocketFrame
     public void frameMe(Frame frame)
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventCapture.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventCapture.java
index 4ab60a0..d881a32 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventCapture.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventCapture.java
@@ -18,6 +18,10 @@
 
 package org.eclipse.jetty.websocket.common.events;
 
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.startsWith;
+
 import java.util.regex.Pattern;
 
 import org.eclipse.jetty.toolchain.test.EventQueue;
@@ -25,10 +29,6 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.junit.Assert;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.startsWith;
-
 @SuppressWarnings("serial")
 public class EventCapture extends EventQueue<String>
 {
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventDriverTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventDriverTest.java
index ae7cb3f..872bec2 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventDriverTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventDriverTest.java
@@ -28,9 +28,14 @@
 import org.eclipse.jetty.websocket.common.CloseInfo;
 import org.eclipse.jetty.websocket.common.frames.BinaryFrame;
 import org.eclipse.jetty.websocket.common.frames.PingFrame;
+import org.eclipse.jetty.websocket.common.frames.PongFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
+import org.eclipse.jetty.websocket.common.io.CloseableLocalWebSocketSession;
 import org.eclipse.jetty.websocket.common.io.LocalWebSocketSession;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestName;
@@ -41,6 +46,7 @@
 import examples.AnnotatedFramesSocket;
 import examples.AnnotatedTextSocket;
 import examples.ListenerBasicSocket;
+import examples.ListenerPingPongSocket;
 
 public class EventDriverTest
 {
@@ -49,6 +55,14 @@
 
     @Rule
     public LeakTrackingBufferPoolRule bufferPool = new LeakTrackingBufferPoolRule("Test");
+    
+    private WebSocketContainerScope container;
+    
+    @Before
+    public void initContainer()
+    {
+        this.container = new SimpleContainerScope(WebSocketPolicy.newClientPolicy());
+    }
 
     private Frame makeBinaryFrame(String content, boolean fin)
     {
@@ -56,12 +70,12 @@
     }
 
     @Test
-    public void testAdapter_ConnectClose() throws IOException
+    public void testAdapter_ConnectClose() throws Exception
     {
         AdapterConnectCloseSocket socket = new AdapterConnectCloseSocket();
         EventDriver driver = wrap(socket);
 
-        try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver,bufferPool))
+        try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
         {
             conn.open();
             driver.incomingFrame(new CloseInfo(StatusCode.NORMAL).asFrame());
@@ -73,12 +87,12 @@
     }
 
     @Test
-    public void testAnnotated_ByteArray() throws IOException
+    public void testAnnotated_ByteArray() throws Exception
     {
         AnnotatedBinaryArraySocket socket = new AnnotatedBinaryArraySocket();
         EventDriver driver = wrap(socket);
 
-        try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver,bufferPool))
+        try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
         {
             conn.open();
             driver.incomingFrame(makeBinaryFrame("Hello World",true));
@@ -92,12 +106,12 @@
     }
 
     @Test
-    public void testAnnotated_Error() throws IOException
+    public void testAnnotated_Error() throws Exception
     {
         AnnotatedTextSocket socket = new AnnotatedTextSocket();
         EventDriver driver = wrap(socket);
 
-        try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver,bufferPool))
+        try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
         {
             conn.open();
             driver.incomingError(new WebSocketException("oof"));
@@ -111,12 +125,12 @@
     }
 
     @Test
-    public void testAnnotated_Frames() throws IOException
+    public void testAnnotated_Frames() throws Exception
     {
         AnnotatedFramesSocket socket = new AnnotatedFramesSocket();
         EventDriver driver = wrap(socket);
 
-        try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver,bufferPool))
+        try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
         {
             conn.open();
             driver.incomingFrame(new PingFrame().setPayload("PING"));
@@ -140,7 +154,7 @@
         AnnotatedBinaryStreamSocket socket = new AnnotatedBinaryStreamSocket();
         EventDriver driver = wrap(socket);
 
-        try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver,bufferPool))
+        try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
         {
             conn.open();
             driver.incomingFrame(makeBinaryFrame("Hello World",true));
@@ -154,12 +168,12 @@
     }
 
     @Test
-    public void testListener_Text() throws Exception
+    public void testListenerBasic_Text() throws Exception
     {
         ListenerBasicSocket socket = new ListenerBasicSocket();
         EventDriver driver = wrap(socket);
 
-        try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver,bufferPool))
+        try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
         {
             conn.start();
             conn.open();
@@ -173,6 +187,50 @@
         }
     }
 
+    @Test
+    public void testListenerPingPong() throws Exception
+    {
+        ListenerPingPongSocket socket = new ListenerPingPongSocket();
+        EventDriver driver = wrap(socket);
+
+        try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
+        {
+            conn.start();
+            conn.open();
+            driver.incomingFrame(new PingFrame().setPayload("PING"));
+            driver.incomingFrame(new PongFrame().setPayload("PONG"));
+            driver.incomingFrame(new CloseInfo(StatusCode.NORMAL).asFrame());
+
+            socket.capture.assertEventCount(4);
+            socket.capture.pop().assertEventStartsWith("onWebSocketConnect");
+            socket.capture.pop().assertEventStartsWith("onWebSocketPing(");
+            socket.capture.pop().assertEventStartsWith("onWebSocketPong(");
+            socket.capture.pop().assertEventStartsWith("onWebSocketClose(1000,");
+        }
+    }
+
+    @Test
+    public void testListenerEmptyPingPong() throws Exception
+    {
+        ListenerPingPongSocket socket = new ListenerPingPongSocket();
+        EventDriver driver = wrap(socket);
+
+        try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
+        {
+            conn.start();
+            conn.open();
+            driver.incomingFrame(new PingFrame());
+            driver.incomingFrame(new PongFrame());
+            driver.incomingFrame(new CloseInfo(StatusCode.NORMAL).asFrame());
+
+            socket.capture.assertEventCount(4);
+            socket.capture.pop().assertEventStartsWith("onWebSocketConnect");
+            socket.capture.pop().assertEventStartsWith("onWebSocketPing(");
+            socket.capture.pop().assertEventStartsWith("onWebSocketPong(");
+            socket.capture.pop().assertEventStartsWith("onWebSocketClose(1000,");
+        }
+    }
+
     private EventDriver wrap(Object websocket)
     {
         WebSocketPolicy policy = WebSocketPolicy.newServerPolicy();
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStackTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStackTest.java
index 8596307..5ac1b74 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStackTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStackTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.common.extensions;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -28,8 +28,9 @@
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.Extension;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
-import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
 import org.eclipse.jetty.websocket.common.extensions.identity.IdentityExtension;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
 import org.junit.Assert;
 import org.junit.Rule;
@@ -56,7 +57,9 @@
     private ExtensionStack createExtensionStack()
     {
         WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
-        ExtensionFactory factory = new WebSocketExtensionFactory(policy,bufferPool);
+        WebSocketContainerScope container = new SimpleContainerScope(policy,bufferPool);
+        
+        WebSocketExtensionFactory factory = new WebSocketExtensionFactory(container);
         return new ExtensionStack(factory);
     }
 
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/ExtensionTool.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/ExtensionTool.java
index 6314007..c09dcab 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/ExtensionTool.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/ExtensionTool.java
@@ -18,7 +18,8 @@
 
 package org.eclipse.jetty.websocket.common.extensions;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
 
 import java.nio.ByteBuffer;
 import java.util.Collections;
@@ -33,6 +34,8 @@
 import org.eclipse.jetty.websocket.common.Parser;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.common.test.ByteBufferAssert;
 import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.common.test.UnitParser;
@@ -126,7 +129,9 @@
     public ExtensionTool(WebSocketPolicy policy, ByteBufferPool bufferPool)
     {
         this.policy = policy;
-        this.factory = new WebSocketExtensionFactory(policy,bufferPool);
+        WebSocketContainerScope container = new SimpleContainerScope(policy, bufferPool);
+        WebSocketExtensionFactory extFactory = new WebSocketExtensionFactory(container);
+        this.factory = extFactory;
     }
 
     public Tester newTester(String parameterizedExtension)
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/FragmentExtensionTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/FragmentExtensionTest.java
index 1bf622e..7e3898b 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/FragmentExtensionTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/FragmentExtensionTest.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.common.extensions;
 
+import static org.hamcrest.Matchers.is;
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
@@ -44,8 +46,6 @@
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.is;
-
 public class FragmentExtensionTest
 {
     @Rule
@@ -140,6 +140,7 @@
 
     /**
      * Verify that outgoing text frames are fragmented by the maxLength configuration.
+     * @throws IOException on test failure
      */
     @Test
     public void testOutgoingFramesByMaxLength() throws IOException
@@ -212,6 +213,7 @@
 
     /**
      * Verify that outgoing text frames are fragmented by default configuration
+     * @throws IOException on test failure
      */
     @Test
     public void testOutgoingFramesDefaultConfig() throws IOException
@@ -276,6 +278,7 @@
 
     /**
      * Outgoing PING (Control Frame) should pass through extension unmodified
+     * @throws IOException on test failure
      */
     @Test
     public void testOutgoingPing() throws IOException
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/IdentityExtensionTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/IdentityExtensionTest.java
index 7e1b021..fe685ce 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/IdentityExtensionTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/IdentityExtensionTest.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.common.extensions;
 
+import static org.hamcrest.Matchers.is;
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
@@ -36,8 +38,6 @@
 import org.junit.Assert;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.is;
-
 public class IdentityExtensionTest
 {
     /**
@@ -71,6 +71,7 @@
 
     /**
      * Verify that outgoing frames are unmodified
+     * @throws IOException on test failure
      */
     @Test
     public void testOutgoingFrames() throws IOException
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtensionTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtensionTest.java
index 832127c..3486a4b 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtensionTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtensionTest.java
@@ -18,6 +18,9 @@
 
 package org.eclipse.jetty.websocket.common.extensions.compress;
 
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.*;
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
@@ -25,6 +28,7 @@
 import java.util.List;
 
 import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.TypeUtil;
 import org.eclipse.jetty.websocket.api.BatchMode;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
@@ -44,83 +48,61 @@
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.is;
-
 /**
  * Client side behavioral tests for permessage-deflate extension.
- * <p/>
+ * <p>
  * See: http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-15
  */
 public class PerMessageDeflateExtensionTest extends AbstractExtensionTest
 {
     @Rule
     public LeakTrackingBufferPoolRule bufferPool = new LeakTrackingBufferPoolRule("Test");
-
-    /**
-     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-15.
-     * <p/>
-     * Section 8.2.3.4: Using a DEFLATE Block with BFINAL Set to 1
-     */
-    @Test
-    public void testDraft15_DeflateBlockWithBFinal1()
+    
+    private void assertEndsWithTail(String hexStr, boolean expectedResult)
     {
-        Tester tester = clientExtensions.newTester("permessage-deflate");
-
-        tester.assertNegotiated("permessage-deflate");
-
-        tester.parseIncomingHex(// 1 message
-                "0xc1 0x08", // header
-                "0xf3 0x48 0xcd 0xc9 0xc9 0x07 0x00 0x00" // example payload 
-        );
-
-        tester.assertHasFrames("Hello");
+        ByteBuffer buf = ByteBuffer.wrap(TypeUtil.fromHexString(hexStr));
+        assertThat("endsWithTail([" + hexStr + "])",CompressExtension.endsWithTail(buf),is(expectedResult));
+    }
+    
+    @Test
+    public void testEndsWithTailBytes()
+    {
+        assertEndsWithTail("11223344",false);
+        assertEndsWithTail("00",false);
+        assertEndsWithTail("0000",false);
+        assertEndsWithTail("FFFF0000",false);
+        assertEndsWithTail("880000FFFF",true);
+        assertEndsWithTail("0000FFFF",true);
     }
 
     /**
-     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-15.
-     * <p/>
-     * Section 8.2.3.3: Using a DEFLATE Block with No Compression
-     */
-    @Test
-    public void testDraft15_DeflateBlockWithNoCompression()
-    {
-        Tester tester = clientExtensions.newTester("permessage-deflate");
-
-        tester.assertNegotiated("permessage-deflate");
-
-        tester.parseIncomingHex(// 1 message / no compression
-                "0xc1 0x0b 0x00 0x05 0x00 0xfa 0xff 0x48 0x65 0x6c 0x6c 0x6f 0x00" // example frame
-        );
-
-        tester.assertHasFrames("Hello");
-    }
-
-    /**
-     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-15.
-     * <p/>
+     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-21.
+     * <p>
      * Section 8.2.3.1: A message compressed using 1 compressed DEFLATE block
      */
     @Test
-    public void testDraft15_Hello_UnCompressedBlock()
+    public void testDraft21_Hello_UnCompressedBlock()
     {
         Tester tester = clientExtensions.newTester("permessage-deflate");
 
         tester.assertNegotiated("permessage-deflate");
 
-        tester.parseIncomingHex(//basic, 1 block, compressed with 0 compression level (aka, uncompressed).
-                "0xc1 0x07 0xf2 0x48 0xcd 0xc9 0xc9 0x07 0x00" // example frame
+        tester.parseIncomingHex(
+                // basic, 1 block, compressed with 0 compression level (aka, uncompressed).
+                "0xc1 0x07",  // (HEADER added for this test)
+                "0xf2 0x48 0xcd 0xc9 0xc9 0x07 0x00" // example frame from RFC
         );
 
         tester.assertHasFrames("Hello");
     }
 
     /**
-     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-15.
-     * <p/>
+     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-21.
+     * <p>
      * Section 8.2.3.1: A message compressed using 1 compressed DEFLATE block (with fragmentation)
      */
     @Test
-    public void testDraft15_Hello_UnCompressedBlock_Fragmented()
+    public void testDraft21_Hello_UnCompressedBlock_Fragmented()
     {
         Tester tester = clientExtensions.newTester("permessage-deflate");
 
@@ -138,12 +120,12 @@
     }
 
     /**
-     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-15.
-     * <p/>
+     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-21.
+     * <p>
      * Section 8.2.3.2: Sharing LZ77 Sliding Window
      */
     @Test
-    public void testDraft15_SharingL77SlidingWindow_ContextTakeover()
+    public void testDraft21_SharingL77SlidingWindow_ContextTakeover()
     {
         Tester tester = clientExtensions.newTester("permessage-deflate");
 
@@ -161,12 +143,12 @@
     }
 
     /**
-     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-15.
-     * <p/>
+     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-21.
+     * <p>
      * Section 8.2.3.2: Sharing LZ77 Sliding Window
      */
     @Test
-    public void testDraft15_SharingL77SlidingWindow_NoContextTakeover()
+    public void testDraft21_SharingL77SlidingWindow_NoContextTakeover()
     {
         Tester tester = clientExtensions.newTester("permessage-deflate");
 
@@ -185,12 +167,51 @@
     }
 
     /**
-     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-15.
-     * <p/>
+     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-21.
+     * <p>
+     * Section 8.2.3.3: Using a DEFLATE Block with No Compression
+     */
+    @Test
+    public void testDraft21_DeflateBlockWithNoCompression()
+    {
+        Tester tester = clientExtensions.newTester("permessage-deflate");
+
+        tester.assertNegotiated("permessage-deflate");
+
+        tester.parseIncomingHex(// 1 message / no compression
+                "0xc1 0x0b 0x00 0x05 0x00 0xfa 0xff 0x48 0x65 0x6c 0x6c 0x6f 0x00" // example frame
+        );
+
+        tester.assertHasFrames("Hello");
+    }
+
+    /**
+     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-21.
+     * <p>
+     * Section 8.2.3.4: Using a DEFLATE Block with BFINAL Set to 1
+     */
+    @Test
+    public void testDraft21_DeflateBlockWithBFinal1()
+    {
+        Tester tester = clientExtensions.newTester("permessage-deflate");
+
+        tester.assertNegotiated("permessage-deflate");
+
+        tester.parseIncomingHex(// 1 message
+                "0xc1 0x08", // header
+                "0xf3 0x48 0xcd 0xc9 0xc9 0x07 0x00 0x00" // example payload 
+        );
+
+        tester.assertHasFrames("Hello");
+    }
+
+    /**
+     * Decode payload example as seen in draft-ietf-hybi-permessage-compression-21.
+     * <p>
      * Section 8.2.3.5: Two DEFLATE Blocks in 1 Message
      */
     @Test
-    public void testDraft15_TwoDeflateBlocksOneMessage()
+    public void testDraft21_TwoDeflateBlocksOneMessage()
     {
         Tester tester = clientExtensions.newTester("permessage-deflate");
 
@@ -298,6 +319,7 @@
 
     /**
      * Outgoing PING (Control Frame) should pass through extension unmodified
+     * @throws IOException on test failure
      */
     @Test
     public void testOutgoingPing() throws IOException
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/CloseableLocalWebSocketSession.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/CloseableLocalWebSocketSession.java
new file mode 100644
index 0000000..a57778d
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/CloseableLocalWebSocketSession.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.io;
+
+import org.eclipse.jetty.websocket.common.events.EventDriver;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
+import org.junit.rules.TestName;
+
+public class CloseableLocalWebSocketSession extends LocalWebSocketSession implements AutoCloseable
+{
+    public CloseableLocalWebSocketSession(WebSocketContainerScope containerScope, TestName testname, EventDriver driver)
+    {
+        super(containerScope, testname, driver);
+        // LifeCycle start
+        try
+        {
+            start();
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void close()
+    {
+        // WebSocketSession.close();
+        super.close();
+
+        // LifeCycle Stop
+        try
+        {
+            stop();
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java
index 22835ff..560cdbf 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java
@@ -35,7 +35,6 @@
 import org.eclipse.jetty.websocket.common.CloseInfo;
 import org.eclipse.jetty.websocket.common.ConnectionState;
 import org.eclipse.jetty.websocket.common.LogicalConnection;
-import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.io.IOState.ConnectionStateListener;
 import org.junit.rules.TestName;
 
@@ -109,6 +108,12 @@
     }
 
     @Override
+    public String getId()
+    {
+        return this.id;
+    }
+
+    @Override
     public long getIdleTimeout()
     {
         return 0;
@@ -150,12 +155,6 @@
     }
 
     @Override
-    public WebSocketSession getSession()
-    {
-        return null;
-    }
-
-    @Override
     public void incomingError(Throwable e)
     {
         incoming.incomingError(e);
@@ -236,11 +235,6 @@
     }
 
     @Override
-    public void setSession(WebSocketSession session)
-    {
-    }
-
-    @Override
     public SuspendToken suspend()
     {
         return null;
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketSession.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketSession.java
index b72a456..d9fffc4 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketSession.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketSession.java
@@ -20,9 +20,9 @@
 
 import java.net.URI;
 
-import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.events.EventDriver;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.common.test.OutgoingFramesCapture;
 import org.junit.rules.TestName;
 
@@ -31,9 +31,10 @@
     private String id;
     private OutgoingFramesCapture outgoingCapture;
 
-    public LocalWebSocketSession(TestName testname, EventDriver driver, ByteBufferPool bufferPool)
+    public LocalWebSocketSession(WebSocketContainerScope containerScope, TestName testname, EventDriver driver)
     {
-        super(URI.create("ws://localhost/LocalWebSocketSesssion/" + testname.getMethodName()),driver,new LocalWebSocketConnection(testname,bufferPool));
+        super(containerScope,URI.create("ws://localhost/LocalWebSocketSesssion/" + testname.getMethodName()),driver,
+                new LocalWebSocketConnection(testname,containerScope.getBufferPool()));
         this.id = testname.getMethodName();
         outgoingCapture = new OutgoingFramesCapture();
         setOutgoingHandler(outgoingCapture);
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/TrackingCallback.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/TrackingCallback.java
deleted file mode 100644
index 78e8492..0000000
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/TrackingCallback.java
+++ /dev/null
@@ -1,81 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.common.io;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.websocket.api.WriteCallback;
-
-/**
- * Tracking Callback for testing how the callbacks are used.
- */
-public class TrackingCallback implements Callback, WriteCallback
-{
-    private AtomicInteger called = new AtomicInteger();
-    private boolean success = false;
-    private Throwable failure = null;
-
-    @Override
-    public void failed(Throwable x)
-    {
-        this.called.incrementAndGet();
-        this.success = false;
-        this.failure = x;
-    }
-
-    @Override
-    public void succeeded()
-    {
-        this.called.incrementAndGet();
-        this.success = false;
-    }
-
-    public Throwable getFailure()
-    {
-        return failure;
-    }
-
-    public boolean isSuccess()
-    {
-        return success;
-    }
-
-    public boolean isCalled()
-    {
-        return called.get() >= 1;
-    }
-
-    public int getCallCount()
-    {
-        return called.get();
-    }
-
-    @Override
-    public void writeFailed(Throwable x)
-    {
-        failed(x);
-    }
-
-    @Override
-    public void writeSuccess()
-    {
-        succeeded();
-    }
-}
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageInputStreamTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageInputStreamTest.java
index a92dbda..2254ae2 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageInputStreamTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageInputStreamTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.common.message;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -49,7 +49,6 @@
         {
             // Append a single message (simple, short)
             ByteBuffer payload = BufferUtil.toBuffer("Hello World",StandardCharsets.UTF_8);
-            System.out.printf("payload = %s%n",BufferUtil.toDetailString(payload));
             boolean fin = true;
             stream.appendFrame(payload,fin);
 
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageOutputStreamTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageOutputStreamTest.java
index 334d635..f24dd48 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageOutputStreamTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageOutputStreamTest.java
@@ -33,6 +33,8 @@
 import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
 import org.eclipse.jetty.websocket.common.io.FramePipes;
 import org.eclipse.jetty.websocket.common.io.LocalWebSocketSession;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
 import org.junit.After;
 import org.junit.Assert;
@@ -59,13 +61,14 @@
     private LocalWebSocketSession session;
 
     @After
-    public void closeSession()
+    public void closeSession() throws Exception
     {
         session.close();
+        session.stop();
     }
 
     @Before
-    public void setupSession()
+    public void setupSession() throws Exception
     {
         policy = WebSocketPolicy.newServerPolicy();
         policy.setInputBufferSize(1024);
@@ -73,6 +76,9 @@
 
         // Event Driver factory
         EventDriverFactory factory = new EventDriverFactory(policy);
+        
+        // Container
+        WebSocketContainerScope containerScope = new SimpleContainerScope(policy,bufferPool);
 
         // local socket
         EventDriver driver = factory.wrap(new TrackingSocket("local"));
@@ -81,11 +87,13 @@
         socket = new TrackingSocket("remote");
         OutgoingFrames socketPipe = FramePipes.to(factory.wrap(socket));
 
-        session = new LocalWebSocketSession(testname,driver,bufferPool);
+        session = new LocalWebSocketSession(containerScope,testname,driver);
 
         session.setPolicy(policy);
         // talk to our remote socket
         session.setOutgoingHandler(socketPipe);
+        // start session
+        session.start();
         // open connection
         session.open();
     }
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageWriterTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageWriterTest.java
index e87a7b0..805b671 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageWriterTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageWriterTest.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.common.message;
 
+import static org.hamcrest.Matchers.is;
+
 import java.util.Arrays;
 
 import org.eclipse.jetty.toolchain.test.TestTracker;
@@ -29,6 +31,8 @@
 import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
 import org.eclipse.jetty.websocket.common.io.FramePipes;
 import org.eclipse.jetty.websocket.common.io.LocalWebSocketSession;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
 import org.junit.After;
 import org.junit.Assert;
@@ -37,8 +41,6 @@
 import org.junit.Test;
 import org.junit.rules.TestName;
 
-import static org.hamcrest.Matchers.is;
-
 public class MessageWriterTest
 {
     private static final Logger LOG = Log.getLogger(MessageWriterTest.class);
@@ -57,13 +59,14 @@
     private LocalWebSocketSession session;
 
     @After
-    public void closeSession()
+    public void closeSession() throws Exception
     {
         session.close();
+        session.stop();
     }
 
     @Before
-    public void setupSession()
+    public void setupSession() throws Exception
     {
         policy = WebSocketPolicy.newServerPolicy();
         policy.setInputBufferSize(1024);
@@ -72,6 +75,9 @@
         // Event Driver factory
         EventDriverFactory factory = new EventDriverFactory(policy);
 
+        // Container
+        WebSocketContainerScope containerScope = new SimpleContainerScope(policy,bufferPool);
+
         // local socket
         EventDriver driver = factory.wrap(new TrackingSocket("local"));
 
@@ -79,11 +85,13 @@
         socket = new TrackingSocket("remote");
         OutgoingFrames socketPipe = FramePipes.to(factory.wrap(socket));
 
-        session = new LocalWebSocketSession(testname,driver,bufferPool);
+        session = new LocalWebSocketSession(containerScope,testname,driver);
 
         session.setPolicy(policy);
         // talk to our remote socket
         session.setOutgoingHandler(socketPipe);
+        // start session
+        session.start();
         // open connection
         session.open();
     }
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/TrackingInputStreamSocket.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/TrackingInputStreamSocket.java
index 49b56c8..e6f490e 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/TrackingInputStreamSocket.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/TrackingInputStreamSocket.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.common.message;
 
+import static org.hamcrest.Matchers.is;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.concurrent.CountDownLatch;
@@ -33,8 +35,6 @@
 import org.eclipse.jetty.websocket.api.annotations.WebSocket;
 import org.junit.Assert;
 
-import static org.hamcrest.Matchers.is;
-
 @WebSocket
 public class TrackingInputStreamSocket
 {
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/TrackingSocket.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/TrackingSocket.java
index 43a31d6..b736c51 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/TrackingSocket.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/TrackingSocket.java
@@ -99,7 +99,7 @@
 
     public void assertWasOpened() throws InterruptedException
     {
-        Assert.assertThat("Was Opened",openLatch.await(500,TimeUnit.MILLISECONDS),is(true));
+        Assert.assertThat("Was Opened",openLatch.await(30,TimeUnit.SECONDS),is(true));
     }
 
     public void awaitMessage(int expectedMessageCount, TimeUnit timeoutUnit, int timeoutDuration) throws TimeoutException, InterruptedException
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/Utf8CharBufferTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/Utf8CharBufferTest.java
index 368a717..46948a3 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/Utf8CharBufferTest.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/Utf8CharBufferTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.common.message;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java
index b3e3802..35051cd 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java
@@ -18,12 +18,15 @@
 
 package org.eclipse.jetty.websocket.common.test;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
 
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.HttpURLConnection;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.Socket;
@@ -40,6 +43,8 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
+import javax.net.ssl.HttpsURLConnection;
+
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.MappedByteBufferPool;
 import org.eclipse.jetty.toolchain.test.EventQueue;
@@ -68,6 +73,7 @@
 import org.eclipse.jetty.websocket.common.io.IOState;
 import org.eclipse.jetty.websocket.common.io.IOState.ConnectionStateListener;
 import org.eclipse.jetty.websocket.common.io.http.HttpResponseHeaderParser;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
 import org.junit.Assert;
 
 /**
@@ -82,7 +88,7 @@
  * with regards to basic IO behavior, a write should work as expected, a read should work as expected, but <u>what</u> byte it sends or reads is not within its
  * scope.
  */
-public class BlockheadClient implements OutgoingFrames, ConnectionStateListener, AutoCloseable
+public class BlockheadClient implements OutgoingFrames, ConnectionStateListener, AutoCloseable, IBlockheadClient
 {
     private class FrameReadingThread extends Thread implements Runnable, IncomingFrames
     {
@@ -227,21 +233,33 @@
         this.generator = new Generator(policy,bufferPool);
         this.parser = new Parser(policy,bufferPool);
 
-        this.extensionFactory = new WebSocketExtensionFactory(policy,bufferPool);
+        this.extensionFactory = new WebSocketExtensionFactory(new SimpleContainerScope(policy,bufferPool));
         this.ioState = new IOState();
         this.ioState.addListener(this);
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#addExtensions(java.lang.String)
+     */
+    @Override
     public void addExtensions(String xtension)
     {
         this.extensions.add(xtension);
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#addHeader(java.lang.String)
+     */
+    @Override
     public void addHeader(String header)
     {
         this.headers.add(header);
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#awaitDisconnect(long, java.util.concurrent.TimeUnit)
+     */
+    @Override
     public boolean awaitDisconnect(long timeout, TimeUnit unit) throws InterruptedException
     {
         return disconnectedLatch.await(timeout,unit);
@@ -257,6 +275,9 @@
         extensions.clear();
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#close()
+     */
     @Override
     public void close()
     {
@@ -264,6 +285,10 @@
         close(-1,null);
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#close(int, java.lang.String)
+     */
+    @Override
     public void close(int statusCode, String message)
     {
         LOG.debug("close({},{})",statusCode,message);
@@ -279,6 +304,10 @@
         }
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#connect()
+     */
+    @Override
     public void connect() throws IOException
     {
         InetAddress destAddr = InetAddress.getByName(destHttpURI.getHost());
@@ -317,6 +346,10 @@
         }
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#expectServerDisconnect()
+     */
+    @Override
     public void expectServerDisconnect()
     {
         if (eof)
@@ -347,6 +380,10 @@
         }
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#expectUpgradeResponse()
+     */
+    @Override
     public HttpResponse expectUpgradeResponse() throws IOException
     {
         HttpResponse response = readResponseHeader();
@@ -460,6 +497,10 @@
         return ioState;
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#getProtocols()
+     */
+    @Override
     public String getProtocols()
     {
         return protocols;
@@ -591,6 +632,10 @@
         return frameReader.frames;
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#readResponseHeader()
+     */
+    @Override
     public HttpResponse readResponseHeader() throws IOException
     {
         HttpResponse response = new HttpResponse();
@@ -626,6 +671,10 @@
         return response;
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#sendStandardRequest()
+     */
+    @Override
     public void sendStandardRequest() throws IOException
     {
         StringBuilder req = generateUpgradeRequest();
@@ -670,11 +719,19 @@
         this.executor = executor;
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#setProtocols(java.lang.String)
+     */
+    @Override
     public void setProtocols(String protocols)
     {
         this.protocols = protocols;
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#setTimeout(int, java.util.concurrent.TimeUnit)
+     */
+    @Override
     public void setTimeout(int duration, TimeUnit unit)
     {
         this.timeout = (int)TimeUnit.MILLISECONDS.convert(duration,unit);
@@ -719,6 +776,10 @@
         LOG.info("Waking up from sleep");
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#write(org.eclipse.jetty.websocket.common.WebSocketFrame)
+     */
+    @Override
     public void write(WebSocketFrame frame) throws IOException
     {
         if (!ioState.isOpen())
@@ -738,12 +799,20 @@
         extensionStack.outgoingFrame(frame,null,BatchMode.OFF);
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#writeRaw(java.nio.ByteBuffer)
+     */
+    @Override
     public void writeRaw(ByteBuffer buf) throws IOException
     {
         LOG.debug("write(ByteBuffer) {}",BufferUtil.toDetailString(buf));
         BufferUtil.writeTo(buf,out);
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#writeRaw(java.nio.ByteBuffer, int)
+     */
+    @Override
     public void writeRaw(ByteBuffer buf, int numBytes) throws IOException
     {
         int len = Math.min(numBytes,buf.remaining());
@@ -752,12 +821,20 @@
         out.write(arr);
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#writeRaw(java.lang.String)
+     */
+    @Override
     public void writeRaw(String str) throws IOException
     {
         LOG.debug("write((String)[{}]){}{})",str.length(),'\n',str);
         out.write(str.getBytes(StandardCharsets.ISO_8859_1));
     }
 
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#writeRawSlowly(java.nio.ByteBuffer, int)
+     */
+    @Override
     public void writeRawSlowly(ByteBuffer buf, int segmentSize) throws IOException
     {
         while (buf.remaining() > 0)
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java
index 984c6a0..c29186d 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java
@@ -20,52 +20,15 @@
 
 import static org.hamcrest.Matchers.*;
 
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.net.SocketException;
 import java.net.URI;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
-import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.websocket.api.BatchMode;
-import org.eclipse.jetty.websocket.api.WebSocketPolicy;
-import org.eclipse.jetty.websocket.api.WriteCallback;
-import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
-import org.eclipse.jetty.websocket.api.extensions.Frame;
-import org.eclipse.jetty.websocket.api.extensions.Frame.Type;
-import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
-import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
-import org.eclipse.jetty.websocket.common.AcceptHash;
-import org.eclipse.jetty.websocket.common.CloseInfo;
-import org.eclipse.jetty.websocket.common.Generator;
-import org.eclipse.jetty.websocket.common.OpCode;
-import org.eclipse.jetty.websocket.common.Parser;
-import org.eclipse.jetty.websocket.common.WebSocketFrame;
-import org.eclipse.jetty.websocket.common.extensions.ExtensionStack;
-import org.eclipse.jetty.websocket.common.extensions.WebSocketExtensionFactory;
-import org.eclipse.jetty.websocket.common.frames.CloseFrame;
 import org.junit.Assert;
 
 /**
@@ -75,548 +38,16 @@
  */
 public class BlockheadServer
 {
-    public static class ServerConnection implements IncomingFrames, OutgoingFrames, Runnable
-    {
-        private final int BUFFER_SIZE = 8192;
-        private final Socket socket;
-        private final ByteBufferPool bufferPool;
-        private final WebSocketPolicy policy;
-        private final IncomingFramesCapture incomingFrames;
-        private final Parser parser;
-        private final Generator generator;
-        private final AtomicInteger parseCount;
-        private final WebSocketExtensionFactory extensionRegistry;
-        private final AtomicBoolean echoing = new AtomicBoolean(false);
-        private Thread echoThread;
-
-        /** Set to true to disable timeouts (for debugging reasons) */
-        private boolean debug = false;
-        private OutputStream out;
-        private InputStream in;
-
-        private Map<String, String> extraResponseHeaders = new HashMap<>();
-        private OutgoingFrames outgoing = this;
-
-        public ServerConnection(Socket socket)
-        {
-            this.socket = socket;
-            this.incomingFrames = new IncomingFramesCapture();
-            this.policy = WebSocketPolicy.newServerPolicy();
-            this.policy.setMaxBinaryMessageSize(100000);
-            this.policy.setMaxTextMessageSize(100000);
-            // This is a blockhead server connection, no point tracking leaks on this object.
-            this.bufferPool = new MappedByteBufferPool(BUFFER_SIZE);
-            this.parser = new Parser(policy,bufferPool);
-            this.parseCount = new AtomicInteger(0);
-            this.generator = new Generator(policy,bufferPool,false);
-            this.extensionRegistry = new WebSocketExtensionFactory(policy,bufferPool);
-        }
-
-        /**
-         * Add an extra header for the upgrade response (from the server). No extra work is done to ensure the key and value are sane for http.
-         */
-        public void addResponseHeader(String rawkey, String rawvalue)
-        {
-            extraResponseHeaders.put(rawkey,rawvalue);
-        }
-
-        public void close() throws IOException
-        {
-            write(new CloseFrame());
-            flush();
-        }
-
-        public void close(int statusCode) throws IOException
-        {
-            CloseInfo close = new CloseInfo(statusCode);
-            write(close.asFrame());
-            flush();
-        }
-
-        public void disconnect()
-        {
-            LOG.debug("disconnect");
-            IO.close(in);
-            IO.close(out);
-            if (socket != null)
-            {
-                try
-                {
-                    socket.close();
-                }
-                catch (IOException ignore)
-                {
-                    /* ignore */
-                }
-            }
-        }
-
-        public void echoMessage(int expectedFrames, int timeoutDuration, TimeUnit timeoutUnit) throws IOException, TimeoutException
-        {
-            LOG.debug("Echo Frames [expecting {}]",expectedFrames);
-            IncomingFramesCapture cap = readFrames(expectedFrames,timeoutDuration,timeoutUnit);
-            // now echo them back.
-            for (Frame frame : cap.getFrames())
-            {
-                write(WebSocketFrame.copy(frame).setMasked(false));
-            }
-        }
-
-        public void flush() throws IOException
-        {
-            getOutputStream().flush();
-        }
-
-        public ByteBufferPool getBufferPool()
-        {
-            return bufferPool;
-        }
-
-        public IncomingFramesCapture getIncomingFrames()
-        {
-            return incomingFrames;
-        }
-
-        public InputStream getInputStream() throws IOException
-        {
-            if (in == null)
-            {
-                in = socket.getInputStream();
-            }
-            return in;
-        }
-
-        private OutputStream getOutputStream() throws IOException
-        {
-            if (out == null)
-            {
-                out = socket.getOutputStream();
-            }
-            return out;
-        }
-
-        public Parser getParser()
-        {
-            return parser;
-        }
-
-        public WebSocketPolicy getPolicy()
-        {
-            return policy;
-        }
-
-        @Override
-        public void incomingError(Throwable e)
-        {
-            incomingFrames.incomingError(e);
-        }
-
-        @Override
-        public void incomingFrame(Frame frame)
-        {
-            LOG.debug("incoming({})",frame);
-            int count = parseCount.incrementAndGet();
-            if ((count % 10) == 0)
-            {
-                LOG.info("Server parsed {} frames",count);
-            }
-            incomingFrames.incomingFrame(WebSocketFrame.copy(frame));
-
-            if (frame.getOpCode() == OpCode.CLOSE)
-            {
-                CloseInfo close = new CloseInfo(frame);
-                LOG.debug("Close frame: {}",close);
-            }
-
-            Type type = frame.getType();
-            if (echoing.get() && (type.isData() || type.isContinuation()))
-            {
-                try
-                {
-                    write(WebSocketFrame.copy(frame).setMasked(false));
-                }
-                catch (IOException e)
-                {
-                    LOG.warn(e);
-                }
-            }
-        }
-
-        @Override
-        public void outgoingFrame(Frame frame, WriteCallback callback, BatchMode batchMode)
-        {
-            ByteBuffer headerBuf = generator.generateHeaderBytes(frame);
-            if (LOG.isDebugEnabled())
-            {
-                LOG.debug("writing out: {}",BufferUtil.toDetailString(headerBuf));
-            }
-
-            try
-            {
-                BufferUtil.writeTo(headerBuf,out);
-                if (frame.hasPayload())
-                    BufferUtil.writeTo(frame.getPayload(),out);
-                out.flush();
-                if (callback != null)
-                {
-                    callback.writeSuccess();
-                }
-
-                if (frame.getOpCode() == OpCode.CLOSE)
-                {
-                    disconnect();
-                }
-            }
-            catch (Throwable t)
-            {
-                if (callback != null)
-                {
-                    callback.writeFailed(t);
-                }
-            }
-        }
-
-        public List<ExtensionConfig> parseExtensions(List<String> requestLines)
-        {
-            List<ExtensionConfig> extensionConfigs = new ArrayList<>();
-            
-            List<String> hits = regexFind(requestLines, "^Sec-WebSocket-Extensions: (.*)$");
-
-            for (String econf : hits)
-            {
-                // found extensions
-                ExtensionConfig config = ExtensionConfig.parse(econf);
-                extensionConfigs.add(config);
-            }
-
-            return extensionConfigs;
-        }
-
-        public String parseWebSocketKey(List<String> requestLines)
-        {
-            List<String> hits = regexFind(requestLines,"^Sec-WebSocket-Key: (.*)$");
-            if (hits.size() <= 0)
-            {
-                return null;
-            }
-            
-            Assert.assertThat("Number of Sec-WebSocket-Key headers", hits.size(), is(1));
-            
-            String key = hits.get(0);
-            return key;
-        }
-
-        public int read(ByteBuffer buf) throws IOException
-        {
-            int len = 0;
-            while ((in.available() > 0) && (buf.remaining() > 0))
-            {
-                buf.put((byte)in.read());
-                len++;
-            }
-            return len;
-        }
-
-        public IncomingFramesCapture readFrames(int expectedCount, int timeoutDuration, TimeUnit timeoutUnit) throws IOException, TimeoutException
-        {
-            LOG.debug("Read: waiting for {} frame(s) from client",expectedCount);
-            int startCount = incomingFrames.size();
-
-            ByteBuffer buf = bufferPool.acquire(BUFFER_SIZE,false);
-            BufferUtil.clearToFill(buf);
-            try
-            {
-                long msDur = TimeUnit.MILLISECONDS.convert(timeoutDuration,timeoutUnit);
-                long now = System.currentTimeMillis();
-                long expireOn = now + msDur;
-                LOG.debug("Now: {} - expireOn: {} ({} ms)",now,expireOn,msDur);
-
-                int len = 0;
-                while (incomingFrames.size() < (startCount + expectedCount))
-                {
-                    BufferUtil.clearToFill(buf);
-                    len = read(buf);
-                    if (len > 0)
-                    {
-                        LOG.debug("Read {} bytes",len);
-                        BufferUtil.flipToFlush(buf,0);
-                        parser.parse(buf);
-                    }
-                    try
-                    {
-                        TimeUnit.MILLISECONDS.sleep(20);
-                    }
-                    catch (InterruptedException gnore)
-                    {
-                        /* ignore */
-                    }
-                    if (!debug && (System.currentTimeMillis() > expireOn))
-                    {
-                        incomingFrames.dump();
-                        throw new TimeoutException(String.format("Timeout reading all %d expected frames. (managed to only read %d frame(s))",expectedCount,
-                                incomingFrames.size()));
-                    }
-                }
-            }
-            finally
-            {
-                bufferPool.release(buf);
-            }
-
-            return incomingFrames;
-        }
-
-        public String readRequest() throws IOException
-        {
-            LOG.debug("Reading client request");
-            StringBuilder request = new StringBuilder();
-            BufferedReader in = new BufferedReader(new InputStreamReader(getInputStream()));
-            for (String line = in.readLine(); line != null; line = in.readLine())
-            {
-                if (line.length() == 0)
-                {
-                    break;
-                }
-                request.append(line).append("\r\n");
-                LOG.debug("read line: {}",line);
-            }
-
-            LOG.debug("Client Request:{}{}","\n",request);
-            return request.toString();
-        }
-
-        public List<String> readRequestLines() throws IOException
-        {
-            LOG.debug("Reading client request header");
-            List<String> lines = new ArrayList<>();
-
-            BufferedReader in = new BufferedReader(new InputStreamReader(getInputStream()));
-            for (String line = in.readLine(); line != null; line = in.readLine())
-            {
-                if (line.length() == 0)
-                {
-                    break;
-                }
-                lines.add(line);
-            }
-
-            return lines;
-        }
-
-        public List<String> regexFind(List<String> lines, String pattern)
-        {
-            List<String> hits = new ArrayList<>();
-
-            Pattern patKey = Pattern.compile(pattern,Pattern.CASE_INSENSITIVE);
-
-            Matcher mat;
-            for (String line : lines)
-            {
-                mat = patKey.matcher(line);
-                if (mat.matches())
-                {
-                    if (mat.groupCount() >= 1)
-                    {
-                        hits.add(mat.group(1));
-                    }
-                    else
-                    {
-                        hits.add(mat.group(0));
-                    }
-                }
-            }
-
-            return hits;
-        }
-
-        public void respond(String rawstr) throws IOException
-        {
-            LOG.debug("respond(){}{}","\n",rawstr);
-            getOutputStream().write(rawstr.getBytes());
-            flush();
-        }
-
-        @Override
-        public void run()
-        {
-            LOG.debug("Entering echo thread");
-
-            ByteBuffer buf = bufferPool.acquire(BUFFER_SIZE,false);
-            BufferUtil.clearToFill(buf);
-            long readBytes = 0;
-            try
-            {
-                while (echoing.get())
-                {
-                    BufferUtil.clearToFill(buf);
-                    long len = read(buf);
-                    if (len > 0)
-                    {
-                        readBytes += len;
-                        LOG.debug("Read {} bytes",len);
-                        BufferUtil.flipToFlush(buf,0);
-                        parser.parse(buf);
-                    }
-
-                    try
-                    {
-                        TimeUnit.MILLISECONDS.sleep(20);
-                    }
-                    catch (InterruptedException gnore)
-                    {
-                        /* ignore */
-                    }
-                }
-            }
-            catch (IOException e)
-            {
-                LOG.debug("Exception during echo loop",e);
-            }
-            finally
-            {
-                LOG.debug("Read {} bytes",readBytes);
-                bufferPool.release(buf);
-            }
-        }
-
-        public void setSoTimeout(int ms) throws SocketException
-        {
-            socket.setSoTimeout(ms);
-        }
-
-        public void startEcho()
-        {
-            if (echoThread != null)
-            {
-                throw new IllegalStateException("Echo thread already declared!");
-            }
-            echoThread = new Thread(this,"BlockheadServer/Echo");
-            echoing.set(true);
-            echoThread.start();
-        }
-
-        public void stopEcho()
-        {
-            echoing.set(false);
-        }
-
-        public List<String> upgrade() throws IOException
-        {
-            List<String> requestLines = readRequestLines();
-            List<ExtensionConfig> extensionConfigs = parseExtensions(requestLines);
-            String key = parseWebSocketKey(requestLines);
-
-            LOG.debug("Client Request Extensions: {}",extensionConfigs);
-            LOG.debug("Client Request Key: {}",key);
-
-            Assert.assertThat("Request: Sec-WebSocket-Key",key,notNullValue());
-
-            // collect extensions configured in response header
-            ExtensionStack extensionStack = new ExtensionStack(extensionRegistry);
-            extensionStack.negotiate(extensionConfigs);
-
-            // Start with default routing
-            extensionStack.setNextIncoming(this);
-            extensionStack.setNextOutgoing(this);
-
-            // Configure Parser / Generator
-            extensionStack.configure(parser);
-            extensionStack.configure(generator);
-
-            // Start Stack
-            try
-            {
-                extensionStack.start();
-            }
-            catch (Exception e)
-            {
-                throw new IOException("Unable to start Extension Stack");
-            }
-
-            // Configure Parser
-            parser.setIncomingFramesHandler(extensionStack);
-
-            // Setup Response
-            StringBuilder resp = new StringBuilder();
-            resp.append("HTTP/1.1 101 Upgrade\r\n");
-            resp.append("Connection: upgrade\r\n");
-            resp.append("Sec-WebSocket-Accept: ");
-            resp.append(AcceptHash.hashKey(key)).append("\r\n");
-            if (extensionStack.hasNegotiatedExtensions())
-            {
-                // Respond to used extensions
-                resp.append("Sec-WebSocket-Extensions: ");
-                boolean delim = false;
-                for (ExtensionConfig ext : extensionStack.getNegotiatedExtensions())
-                {
-                    if (delim)
-                    {
-                        resp.append(", ");
-                    }
-                    resp.append(ext.getParameterizedName());
-                    delim = true;
-                }
-                resp.append("\r\n");
-            }
-            if (extraResponseHeaders.size() > 0)
-            {
-                for (Map.Entry<String, String> xheader : extraResponseHeaders.entrySet())
-                {
-                    resp.append(xheader.getKey());
-                    resp.append(": ");
-                    resp.append(xheader.getValue());
-                    resp.append("\r\n");
-                }
-            }
-            resp.append("\r\n");
-
-            // Write Response
-            LOG.debug("Response: {}",resp.toString());
-            write(resp.toString().getBytes());
-            return requestLines;
-        }
-
-        private void write(byte[] bytes) throws IOException
-        {
-            getOutputStream().write(bytes);
-        }
-
-        public void write(byte[] buf, int offset, int length) throws IOException
-        {
-            getOutputStream().write(buf,offset,length);
-        }
-
-        public void write(Frame frame) throws IOException
-        {
-            LOG.debug("write(Frame->{}) to {}",frame,outgoing);
-            outgoing.outgoingFrame(frame,null,BatchMode.OFF);
-        }
-
-        public void write(int b) throws IOException
-        {
-            getOutputStream().write(b);
-        }
-
-        public void write(ByteBuffer buf) throws IOException
-        {
-            byte arr[] = BufferUtil.toArray(buf);
-            if ((arr != null) && (arr.length > 0))
-            {
-                getOutputStream().write(arr);
-            }
-        }
-    }
-
     private static final Logger LOG = Log.getLogger(BlockheadServer.class);
     private ServerSocket serverSocket;
     private URI wsUri;
 
-    public ServerConnection accept() throws IOException
+    public IBlockheadServerConnection accept() throws IOException
     {
         LOG.debug(".accept()");
         assertIsStarted();
         Socket socket = serverSocket.accept();
-        return new ServerConnection(socket);
+        return new BlockheadServerConnection(socket);
     }
 
     private void assertIsStarted()
@@ -633,42 +64,6 @@
         return wsUri;
     }
 
-    public void respondToClient(Socket connection, String serverResponse) throws IOException
-    {
-        InputStream in = null;
-        InputStreamReader isr = null;
-        BufferedReader buf = null;
-        OutputStream out = null;
-        try
-        {
-            in = connection.getInputStream();
-            isr = new InputStreamReader(in);
-            buf = new BufferedReader(isr);
-            String line;
-            while ((line = buf.readLine()) != null)
-            {
-                // System.err.println(line);
-                if (line.length() == 0)
-                {
-                    // Got the "\r\n" line.
-                    break;
-                }
-            }
-
-            // System.out.println("[Server-Out] " + serverResponse);
-            out = connection.getOutputStream();
-            out.write(serverResponse.getBytes());
-            out.flush();
-        }
-        finally
-        {
-            IO.close(buf);
-            IO.close(isr);
-            IO.close(in);
-            IO.close(out);
-        }
-    }
-
     public void start() throws IOException
     {
         InetAddress addr = InetAddress.getByName("localhost");
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServerConnection.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServerConnection.java
new file mode 100644
index 0000000..169fa07
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServerConnection.java
@@ -0,0 +1,614 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.test;
+
+import static org.hamcrest.Matchers.*;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.BatchMode;
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.api.WriteCallback;
+import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
+import org.eclipse.jetty.websocket.api.extensions.Frame;
+import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
+import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
+import org.eclipse.jetty.websocket.api.extensions.Frame.Type;
+import org.eclipse.jetty.websocket.common.AcceptHash;
+import org.eclipse.jetty.websocket.common.CloseInfo;
+import org.eclipse.jetty.websocket.common.Generator;
+import org.eclipse.jetty.websocket.common.OpCode;
+import org.eclipse.jetty.websocket.common.Parser;
+import org.eclipse.jetty.websocket.common.WebSocketFrame;
+import org.eclipse.jetty.websocket.common.extensions.ExtensionStack;
+import org.eclipse.jetty.websocket.common.extensions.WebSocketExtensionFactory;
+import org.eclipse.jetty.websocket.common.frames.CloseFrame;
+import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
+import org.junit.Assert;
+
+public class BlockheadServerConnection implements IncomingFrames, OutgoingFrames, Runnable, IBlockheadServerConnection
+{
+    private static final Logger LOG = Log.getLogger(BlockheadServerConnection.class);
+    
+    private final int BUFFER_SIZE = 8192;
+    private final Socket socket;
+    private final ByteBufferPool bufferPool;
+    private final WebSocketPolicy policy;
+    private final IncomingFramesCapture incomingFrames;
+    private final Parser parser;
+    private final Generator generator;
+    private final AtomicInteger parseCount;
+    private final WebSocketExtensionFactory extensionRegistry;
+    private final AtomicBoolean echoing = new AtomicBoolean(false);
+    private Thread echoThread;
+
+    /** Set to true to disable timeouts (for debugging reasons) */
+    private boolean debug = false;
+    private OutputStream out;
+    private InputStream in;
+
+    private Map<String, String> extraResponseHeaders = new HashMap<>();
+    private OutgoingFrames outgoing = this;
+
+    public BlockheadServerConnection(Socket socket)
+    {
+        this.socket = socket;
+        this.incomingFrames = new IncomingFramesCapture();
+        this.policy = WebSocketPolicy.newServerPolicy();
+        this.policy.setMaxBinaryMessageSize(100000);
+        this.policy.setMaxTextMessageSize(100000);
+        // This is a blockhead server connection, no point tracking leaks on this object.
+        this.bufferPool = new MappedByteBufferPool(BUFFER_SIZE);
+        this.parser = new Parser(policy,bufferPool);
+        this.parseCount = new AtomicInteger(0);
+        this.generator = new Generator(policy,bufferPool,false);
+        this.extensionRegistry = new WebSocketExtensionFactory(new SimpleContainerScope(policy,bufferPool));
+    }
+
+    /**
+     * Add an extra header for the upgrade response (from the server). No extra work is done to ensure the key and value are sane for http.
+     * @param rawkey the raw key
+     * @param rawvalue the raw value
+     */
+    public void addResponseHeader(String rawkey, String rawvalue)
+    {
+        extraResponseHeaders.put(rawkey,rawvalue);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection#close()
+     */
+    @Override
+    public void close() throws IOException
+    {
+        write(new CloseFrame());
+        flush();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection#close(int)
+     */
+    @Override
+    public void close(int statusCode) throws IOException
+    {
+        CloseInfo close = new CloseInfo(statusCode);
+        write(close.asFrame());
+        flush();
+    }
+
+    public void disconnect()
+    {
+        LOG.debug("disconnect");
+        IO.close(in);
+        IO.close(out);
+        if (socket != null)
+        {
+            try
+            {
+                socket.close();
+            }
+            catch (IOException ignore)
+            {
+                /* ignore */
+            }
+        }
+    }
+
+    public void echoMessage(int expectedFrames, int timeoutDuration, TimeUnit timeoutUnit) throws IOException, TimeoutException
+    {
+        LOG.debug("Echo Frames [expecting {}]",expectedFrames);
+        IncomingFramesCapture cap = readFrames(expectedFrames,timeoutDuration,timeoutUnit);
+        // now echo them back.
+        for (Frame frame : cap.getFrames())
+        {
+            write(WebSocketFrame.copy(frame).setMasked(false));
+        }
+    }
+
+    public void flush() throws IOException
+    {
+        getOutputStream().flush();
+    }
+
+    public ByteBufferPool getBufferPool()
+    {
+        return bufferPool;
+    }
+
+    public IncomingFramesCapture getIncomingFrames()
+    {
+        return incomingFrames;
+    }
+
+    public InputStream getInputStream() throws IOException
+    {
+        if (in == null)
+        {
+            in = socket.getInputStream();
+        }
+        return in;
+    }
+
+    private OutputStream getOutputStream() throws IOException
+    {
+        if (out == null)
+        {
+            out = socket.getOutputStream();
+        }
+        return out;
+    }
+
+    public Parser getParser()
+    {
+        return parser;
+    }
+
+    public WebSocketPolicy getPolicy()
+    {
+        return policy;
+    }
+
+    @Override
+    public void incomingError(Throwable e)
+    {
+        incomingFrames.incomingError(e);
+    }
+
+    @Override
+    public void incomingFrame(Frame frame)
+    {
+        LOG.debug("incoming({})",frame);
+        int count = parseCount.incrementAndGet();
+        if ((count % 10) == 0)
+        {
+            LOG.info("Server parsed {} frames",count);
+        }
+        incomingFrames.incomingFrame(WebSocketFrame.copy(frame));
+
+        if (frame.getOpCode() == OpCode.CLOSE)
+        {
+            CloseInfo close = new CloseInfo(frame);
+            LOG.debug("Close frame: {}",close);
+        }
+
+        Type type = frame.getType();
+        if (echoing.get() && (type.isData() || type.isContinuation()))
+        {
+            try
+            {
+                write(WebSocketFrame.copy(frame).setMasked(false));
+            }
+            catch (IOException e)
+            {
+                LOG.warn(e);
+            }
+        }
+    }
+
+    @Override
+    public void outgoingFrame(Frame frame, WriteCallback callback, BatchMode batchMode)
+    {
+        ByteBuffer headerBuf = generator.generateHeaderBytes(frame);
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("writing out: {}",BufferUtil.toDetailString(headerBuf));
+        }
+
+        try
+        {
+            BufferUtil.writeTo(headerBuf,out);
+            if (frame.hasPayload())
+                BufferUtil.writeTo(frame.getPayload(),out);
+            out.flush();
+            if (callback != null)
+            {
+                callback.writeSuccess();
+            }
+
+            if (frame.getOpCode() == OpCode.CLOSE)
+            {
+                disconnect();
+            }
+        }
+        catch (Throwable t)
+        {
+            if (callback != null)
+            {
+                callback.writeFailed(t);
+            }
+        }
+    }
+
+    public List<ExtensionConfig> parseExtensions(List<String> requestLines)
+    {
+        List<ExtensionConfig> extensionConfigs = new ArrayList<>();
+        
+        List<String> hits = regexFind(requestLines, "^Sec-WebSocket-Extensions: (.*)$");
+
+        for (String econf : hits)
+        {
+            // found extensions
+            ExtensionConfig config = ExtensionConfig.parse(econf);
+            extensionConfigs.add(config);
+        }
+
+        return extensionConfigs;
+    }
+
+    public String parseWebSocketKey(List<String> requestLines)
+    {
+        List<String> hits = regexFind(requestLines,"^Sec-WebSocket-Key: (.*)$");
+        if (hits.size() <= 0)
+        {
+            return null;
+        }
+        
+        Assert.assertThat("Number of Sec-WebSocket-Key headers", hits.size(), is(1));
+        
+        String key = hits.get(0);
+        return key;
+    }
+
+    public int read(ByteBuffer buf) throws IOException
+    {
+        int len = 0;
+        while ((in.available() > 0) && (buf.remaining() > 0))
+        {
+            buf.put((byte)in.read());
+            len++;
+        }
+        return len;
+    }
+
+    public IncomingFramesCapture readFrames(int expectedCount, int timeoutDuration, TimeUnit timeoutUnit) throws IOException, TimeoutException
+    {
+        LOG.debug("Read: waiting for {} frame(s) from client",expectedCount);
+        int startCount = incomingFrames.size();
+
+        ByteBuffer buf = bufferPool.acquire(BUFFER_SIZE,false);
+        BufferUtil.clearToFill(buf);
+        try
+        {
+            long msDur = TimeUnit.MILLISECONDS.convert(timeoutDuration,timeoutUnit);
+            long now = System.currentTimeMillis();
+            long expireOn = now + msDur;
+            LOG.debug("Now: {} - expireOn: {} ({} ms)",now,expireOn,msDur);
+
+            int len = 0;
+            while (incomingFrames.size() < (startCount + expectedCount))
+            {
+                BufferUtil.clearToFill(buf);
+                len = read(buf);
+                if (len > 0)
+                {
+                    LOG.debug("Read {} bytes",len);
+                    BufferUtil.flipToFlush(buf,0);
+                    parser.parse(buf);
+                }
+                try
+                {
+                    TimeUnit.MILLISECONDS.sleep(20);
+                }
+                catch (InterruptedException gnore)
+                {
+                    /* ignore */
+                }
+                if (!debug && (System.currentTimeMillis() > expireOn))
+                {
+                    incomingFrames.dump();
+                    throw new TimeoutException(String.format("Timeout reading all %d expected frames. (managed to only read %d frame(s))",expectedCount,
+                            incomingFrames.size()));
+                }
+            }
+        }
+        finally
+        {
+            bufferPool.release(buf);
+        }
+
+        return incomingFrames;
+    }
+
+    public String readRequest() throws IOException
+    {
+        LOG.debug("Reading client request");
+        StringBuilder request = new StringBuilder();
+        BufferedReader in = new BufferedReader(new InputStreamReader(getInputStream()));
+        for (String line = in.readLine(); line != null; line = in.readLine())
+        {
+            if (line.length() == 0)
+            {
+                break;
+            }
+            request.append(line).append("\r\n");
+            LOG.debug("read line: {}",line);
+        }
+
+        LOG.debug("Client Request:{}{}","\n",request);
+        return request.toString();
+    }
+
+    public List<String> readRequestLines() throws IOException
+    {
+        LOG.debug("Reading client request header");
+        List<String> lines = new ArrayList<>();
+
+        BufferedReader in = new BufferedReader(new InputStreamReader(getInputStream()));
+        for (String line = in.readLine(); line != null; line = in.readLine())
+        {
+            if (line.length() == 0)
+            {
+                break;
+            }
+            lines.add(line);
+        }
+
+        return lines;
+    }
+
+    public List<String> regexFind(List<String> lines, String pattern)
+    {
+        List<String> hits = new ArrayList<>();
+
+        Pattern patKey = Pattern.compile(pattern,Pattern.CASE_INSENSITIVE);
+
+        Matcher mat;
+        for (String line : lines)
+        {
+            mat = patKey.matcher(line);
+            if (mat.matches())
+            {
+                if (mat.groupCount() >= 1)
+                {
+                    hits.add(mat.group(1));
+                }
+                else
+                {
+                    hits.add(mat.group(0));
+                }
+            }
+        }
+
+        return hits;
+    }
+
+    public void respond(String rawstr) throws IOException
+    {
+        LOG.debug("respond(){}{}","\n",rawstr);
+        getOutputStream().write(rawstr.getBytes());
+        flush();
+    }
+
+    @Override
+    public void run()
+    {
+        LOG.debug("Entering echo thread");
+
+        ByteBuffer buf = bufferPool.acquire(BUFFER_SIZE,false);
+        BufferUtil.clearToFill(buf);
+        long readBytes = 0;
+        try
+        {
+            while (echoing.get())
+            {
+                BufferUtil.clearToFill(buf);
+                long len = read(buf);
+                if (len > 0)
+                {
+                    readBytes += len;
+                    LOG.debug("Read {} bytes",len);
+                    BufferUtil.flipToFlush(buf,0);
+                    parser.parse(buf);
+                }
+
+                try
+                {
+                    TimeUnit.MILLISECONDS.sleep(20);
+                }
+                catch (InterruptedException gnore)
+                {
+                    /* ignore */
+                }
+            }
+        }
+        catch (IOException e)
+        {
+            LOG.debug("Exception during echo loop",e);
+        }
+        finally
+        {
+            LOG.debug("Read {} bytes",readBytes);
+            bufferPool.release(buf);
+        }
+    }
+
+    public void setSoTimeout(int ms) throws SocketException
+    {
+        socket.setSoTimeout(ms);
+    }
+
+    public void startEcho()
+    {
+        if (echoThread != null)
+        {
+            throw new IllegalStateException("Echo thread already declared!");
+        }
+        echoThread = new Thread(this,"BlockheadServer/Echo");
+        echoing.set(true);
+        echoThread.start();
+    }
+
+    public void stopEcho()
+    {
+        echoing.set(false);
+    }
+
+    public List<String> upgrade() throws IOException
+    {
+        List<String> requestLines = readRequestLines();
+        List<ExtensionConfig> extensionConfigs = parseExtensions(requestLines);
+        String key = parseWebSocketKey(requestLines);
+
+        LOG.debug("Client Request Extensions: {}",extensionConfigs);
+        LOG.debug("Client Request Key: {}",key);
+
+        Assert.assertThat("Request: Sec-WebSocket-Key",key,notNullValue());
+
+        // collect extensions configured in response header
+        ExtensionStack extensionStack = new ExtensionStack(extensionRegistry);
+        extensionStack.negotiate(extensionConfigs);
+
+        // Start with default routing
+        extensionStack.setNextIncoming(this);
+        extensionStack.setNextOutgoing(this);
+
+        // Configure Parser / Generator
+        extensionStack.configure(parser);
+        extensionStack.configure(generator);
+
+        // Start Stack
+        try
+        {
+            extensionStack.start();
+        }
+        catch (Exception e)
+        {
+            throw new IOException("Unable to start Extension Stack");
+        }
+
+        // Configure Parser
+        parser.setIncomingFramesHandler(extensionStack);
+
+        // Setup Response
+        StringBuilder resp = new StringBuilder();
+        resp.append("HTTP/1.1 101 Upgrade\r\n");
+        resp.append("Connection: upgrade\r\n");
+        resp.append("Sec-WebSocket-Accept: ");
+        resp.append(AcceptHash.hashKey(key)).append("\r\n");
+        if (extensionStack.hasNegotiatedExtensions())
+        {
+            // Respond to used extensions
+            resp.append("Sec-WebSocket-Extensions: ");
+            boolean delim = false;
+            for (ExtensionConfig ext : extensionStack.getNegotiatedExtensions())
+            {
+                if (delim)
+                {
+                    resp.append(", ");
+                }
+                resp.append(ext.getParameterizedName());
+                delim = true;
+            }
+            resp.append("\r\n");
+        }
+        if (extraResponseHeaders.size() > 0)
+        {
+            for (Map.Entry<String, String> xheader : extraResponseHeaders.entrySet())
+            {
+                resp.append(xheader.getKey());
+                resp.append(": ");
+                resp.append(xheader.getValue());
+                resp.append("\r\n");
+            }
+        }
+        resp.append("\r\n");
+
+        // Write Response
+        LOG.debug("Response: {}",resp.toString());
+        write(resp.toString().getBytes());
+        return requestLines;
+    }
+
+    private void write(byte[] bytes) throws IOException
+    {
+        getOutputStream().write(bytes);
+    }
+
+    public void write(byte[] buf, int offset, int length) throws IOException
+    {
+        getOutputStream().write(buf,offset,length);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection#write(org.eclipse.jetty.websocket.api.extensions.Frame)
+     */
+    @Override
+    public void write(Frame frame) throws IOException
+    {
+        LOG.debug("write(Frame->{}) to {}",frame,outgoing);
+        outgoing.outgoingFrame(frame,null,BatchMode.OFF);
+    }
+
+    public void write(int b) throws IOException
+    {
+        getOutputStream().write(b);
+    }
+
+    public void write(ByteBuffer buf) throws IOException
+    {
+        byte arr[] = BufferUtil.toArray(buf);
+        if ((arr != null) && (arr.length > 0))
+        {
+            getOutputStream().write(arr);
+        }
+    }
+}
\ No newline at end of file
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/ByteBufferAssert.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/ByteBufferAssert.java
index 5650568..2a5dce7 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/ByteBufferAssert.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/ByteBufferAssert.java
@@ -18,8 +18,9 @@
 
 package org.eclipse.jetty.websocket.common.test;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
 
 import java.nio.ByteBuffer;
 
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/DummyConnection.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/DummyConnection.java
new file mode 100644
index 0000000..c6f845f
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/DummyConnection.java
@@ -0,0 +1,155 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.test;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.BatchMode;
+import org.eclipse.jetty.websocket.api.SuspendToken;
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.api.WriteCallback;
+import org.eclipse.jetty.websocket.api.extensions.Frame;
+import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
+import org.eclipse.jetty.websocket.common.LogicalConnection;
+import org.eclipse.jetty.websocket.common.io.IOState;
+
+public class DummyConnection implements LogicalConnection
+{
+    private static final Logger LOG = Log.getLogger(DummyConnection.class);
+    private IOState iostate;
+
+    public DummyConnection()
+    {
+        this.iostate = new IOState();
+    }
+
+    @Override
+    public void close()
+    {
+    }
+
+    @Override
+    public void close(int statusCode, String reason)
+    {
+    }
+
+    @Override
+    public void disconnect()
+    {
+    }
+
+    @Override
+    public ByteBufferPool getBufferPool()
+    {
+        return null;
+    }
+
+    @Override
+    public Executor getExecutor()
+    {
+        return null;
+    }
+
+    @Override
+    public String getId()
+    {
+        return "dummy";
+    }
+
+    @Override
+    public long getIdleTimeout()
+    {
+        return 0;
+    }
+
+    @Override
+    public IOState getIOState()
+    {
+        return this.iostate;
+    }
+
+    @Override
+    public InetSocketAddress getLocalAddress()
+    {
+        return null;
+    }
+
+    @Override
+    public long getMaxIdleTimeout()
+    {
+        return 0;
+    }
+
+    @Override
+    public WebSocketPolicy getPolicy()
+    {
+        return null;
+    }
+
+    @Override
+    public InetSocketAddress getRemoteAddress()
+    {
+        return null;
+    }
+
+    @Override
+    public boolean isOpen()
+    {
+        return false;
+    }
+
+    @Override
+    public boolean isReading()
+    {
+        return false;
+    }
+
+    @Override
+    public void outgoingFrame(Frame frame, WriteCallback callback, BatchMode batchMode)
+    {
+        callback.writeSuccess();
+    }
+
+    @Override
+    public void resume()
+    {
+    }
+
+    @Override
+    public void setMaxIdleTimeout(long ms)
+    {
+    }
+
+    @Override
+    public void setNextIncomingFrames(IncomingFrames incoming)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("setNextIncomingFrames({})",incoming);
+    }
+
+    @Override
+    public SuspendToken suspend()
+    {
+        return null;
+    }
+}
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/Fuzzer.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/Fuzzer.java
index b10115f..5579b53 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/Fuzzer.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/Fuzzer.java
@@ -18,7 +18,8 @@
 
 package org.eclipse.jetty.websocket.common.test;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
 
 import java.io.IOException;
 import java.net.SocketException;
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IBlockheadClient.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IBlockheadClient.java
new file mode 100644
index 0000000..cf952fe
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IBlockheadClient.java
@@ -0,0 +1,79 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.test;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.toolchain.test.EventQueue;
+import org.eclipse.jetty.websocket.common.WebSocketFrame;
+
+/**
+ * Interface for BlockheadClient.
+ */
+public interface IBlockheadClient extends AutoCloseable
+{
+    public void addExtensions(String xtension);
+
+    public void addHeader(String header);
+
+    public boolean awaitDisconnect(long timeout, TimeUnit unit) throws InterruptedException;
+
+    public void close();
+
+    public void close(int statusCode, String message);
+
+    public void connect() throws IOException;
+
+    public void disconnect();
+
+    public void expectServerDisconnect();
+
+    public HttpResponse expectUpgradeResponse() throws IOException;
+
+    public InetSocketAddress getLocalSocketAddress();
+
+    public String getProtocols();
+
+    public InetSocketAddress getRemoteSocketAddress();
+
+    public EventQueue<WebSocketFrame> readFrames(int expectedFrameCount, int timeoutDuration, TimeUnit timeoutUnit) throws Exception;
+
+    public HttpResponse readResponseHeader() throws IOException;
+
+    public void sendStandardRequest() throws IOException;
+
+    public void setConnectionValue(String connectionValue);
+
+    public void setProtocols(String protocols);
+
+    public void setTimeout(int duration, TimeUnit unit);
+
+    public void write(WebSocketFrame frame) throws IOException;
+
+    public void writeRaw(ByteBuffer buf) throws IOException;
+
+    public void writeRaw(ByteBuffer buf, int numBytes) throws IOException;
+
+    public void writeRaw(String str) throws IOException;
+
+    public void writeRawSlowly(ByteBuffer buf, int segmentSize) throws IOException;
+}
\ No newline at end of file
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IBlockheadServerConnection.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IBlockheadServerConnection.java
new file mode 100644
index 0000000..69de6d7
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IBlockheadServerConnection.java
@@ -0,0 +1,68 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.test;
+
+import java.io.IOException;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.websocket.api.extensions.Frame;
+import org.eclipse.jetty.websocket.common.Parser;
+
+public interface IBlockheadServerConnection
+{
+    public void close() throws IOException;
+
+    public void close(int statusCode) throws IOException;
+
+    public void write(Frame frame) throws IOException;
+
+    public List<String> upgrade() throws IOException;
+
+    public void disconnect();
+
+    public IncomingFramesCapture readFrames(int expectedCount, int timeoutDuration, TimeUnit timeoutUnit) throws IOException, TimeoutException;
+    public void write(ByteBuffer buf) throws IOException;
+    public List<String> readRequestLines() throws IOException;
+    public String parseWebSocketKey(List<String> requestLines);
+    public void respond(String rawstr) throws IOException;
+    public String readRequest() throws IOException;
+    public List<String> regexFind(List<String> lines, String pattern);
+    public void echoMessage(int expectedFrames, int timeoutDuration, TimeUnit timeoutUnit) throws IOException, TimeoutException;
+    public void setSoTimeout(int ms) throws SocketException;
+    public ByteBufferPool getBufferPool();
+    public int read(ByteBuffer buf) throws IOException;
+    public Parser getParser();
+    public IncomingFramesCapture getIncomingFrames();
+    public void flush() throws IOException;
+    public void write(int b) throws IOException;
+    public void startEcho();
+    public void stopEcho();
+    
+    /**
+     * Add an extra header for the upgrade response (from the server). No extra work is done to ensure the key and value are sane for http.
+     * @param rawkey the raw key
+     * @param rawvalue the raw value
+     */
+    public void addResponseHeader(String rawkey, String rawvalue);
+}
\ No newline at end of file
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IncomingFramesCapture.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IncomingFramesCapture.java
index dd782ba..403b9f4 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IncomingFramesCapture.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IncomingFramesCapture.java
@@ -18,7 +18,8 @@
 
 package org.eclipse.jetty.websocket.common.test;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
 
 import java.util.Queue;
 
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/LeakTrackingBufferPoolRule.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/LeakTrackingBufferPoolRule.java
index f6938d6..f5d381f 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/LeakTrackingBufferPoolRule.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/LeakTrackingBufferPoolRule.java
@@ -18,15 +18,15 @@
 
 package org.eclipse.jetty.websocket.common.test;
 
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
 import org.eclipse.jetty.io.LeakTrackingByteBufferPool;
 import org.eclipse.jetty.io.MappedByteBufferPool;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
 public class LeakTrackingBufferPoolRule extends LeakTrackingByteBufferPool implements TestRule
 {
     private final String id;
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/OutgoingFramesCapture.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/OutgoingFramesCapture.java
index 521ab6d..ba406f8 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/OutgoingFramesCapture.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/OutgoingFramesCapture.java
@@ -18,6 +18,9 @@
 
 package org.eclipse.jetty.websocket.common.test;
 
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
+
 import java.util.LinkedList;
 
 import org.eclipse.jetty.util.BufferUtil;
@@ -29,9 +32,6 @@
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.junit.Assert;
 
-import static org.hamcrest.Matchers.greaterThanOrEqualTo;
-import static org.hamcrest.Matchers.is;
-
 public class OutgoingFramesCapture implements OutgoingFrames
 {
     private LinkedList<WebSocketFrame> frames = new LinkedList<>();
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/OutgoingNetworkBytesCapture.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/OutgoingNetworkBytesCapture.java
index 081235b..756d734 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/OutgoingNetworkBytesCapture.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/OutgoingNetworkBytesCapture.java
@@ -18,6 +18,9 @@
 
 package org.eclipse.jetty.websocket.common.test;
 
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.lessThan;
+
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
@@ -32,9 +35,6 @@
 import org.eclipse.jetty.websocket.common.Generator;
 import org.junit.Assert;
 
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.lessThan;
-
 /**
  * Capture outgoing network bytes.
  */
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/RawFrameBuilder.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/RawFrameBuilder.java
index 620ea9c..07a7350 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/RawFrameBuilder.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/RawFrameBuilder.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.common.test;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.nio.ByteBuffer;
 
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitGenerator.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitGenerator.java
index 09f61cc..d8510fc 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitGenerator.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitGenerator.java
@@ -83,6 +83,8 @@
 
     /**
      * Generate a single giant buffer of all provided frames Not appropriate for production code, but useful for testing.
+     * @param frames the list of frames to generate from
+     * @return the bytebuffer representing all of the generated frames
      */
     public static ByteBuffer generate(List<WebSocketFrame> frames)
     {
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitParser.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitParser.java
index d44ab3e..11c7e7d 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitParser.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitParser.java
@@ -50,6 +50,7 @@
      * Parse a buffer, but do so in a quiet fashion, squelching stacktraces if encountered.
      * <p>
      * Use if you know the parse will cause an exception and just don't wnat to make the test console all noisy.
+     * @param buf the buffer to parse
      */
     public void parseQuietly(ByteBuffer buf)
     {
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/Sha1Sum.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/Sha1Sum.java
new file mode 100644
index 0000000..f2f5050
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/Sha1Sum.java
@@ -0,0 +1,110 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.toolchain.test.IO;
+import org.junit.Assert;
+
+/**
+ * Calculate the sha1sum for various content 
+ */
+public class Sha1Sum
+{
+    private static class NoOpOutputStream extends OutputStream
+    {
+        @Override
+        public void write(byte[] b) throws IOException
+        {
+        }
+
+        @Override
+        public void write(byte[] b, int off, int len) throws IOException
+        {
+        }
+
+        @Override
+        public void flush() throws IOException
+        {
+        }
+
+        @Override
+        public void close() throws IOException
+        {
+        }
+
+        @Override
+        public void write(int b) throws IOException
+        {
+        }
+    }
+
+    public static String calculate(File file) throws NoSuchAlgorithmException, IOException
+    {
+        return calculate(file.toPath());
+    }
+
+    public static String calculate(Path path) throws NoSuchAlgorithmException, IOException
+    {
+        MessageDigest digest = MessageDigest.getInstance("SHA1");
+        try (InputStream in = Files.newInputStream(path,StandardOpenOption.READ);
+                NoOpOutputStream noop = new NoOpOutputStream();
+                DigestOutputStream digester = new DigestOutputStream(noop,digest))
+        {
+            IO.copy(in,digester);
+            return Hex.asHex(digest.digest());
+        }
+    }
+
+    public static String calculate(byte[] buf) throws NoSuchAlgorithmException
+    {
+        MessageDigest digest = MessageDigest.getInstance("SHA1");
+        digest.update(buf);
+        return Hex.asHex(digest.digest());
+    }
+    
+    public static String calculate(byte[] buf, int offset, int len) throws NoSuchAlgorithmException
+    {
+        MessageDigest digest = MessageDigest.getInstance("SHA1");
+        digest.update(buf,offset,len);
+        return Hex.asHex(digest.digest());
+    }
+    
+    public static String loadSha1(File sha1File) throws IOException
+    {
+        String contents = IO.readToString(sha1File);
+        Pattern pat = Pattern.compile("^[0-9A-Fa-f]*");
+        Matcher mat = pat.matcher(contents);
+        Assert.assertTrue("Should have found HEX code in SHA1 file: " + sha1File,mat.find());
+        return mat.group();
+    }
+
+}
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/Utf8PartialBuilderTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/Utf8PartialBuilderTest.java
new file mode 100644
index 0000000..0d0ac00
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/Utf8PartialBuilderTest.java
@@ -0,0 +1,86 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common.util;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.jetty.util.BufferUtil;
+import org.junit.Test;
+
+/**
+ * Test partial UTF8 String sequence building.
+ */
+public class Utf8PartialBuilderTest
+{
+    private ByteBuffer toByteBuffer(String hexStr)
+    {
+        return ByteBuffer.wrap(Hex.asByteArray(hexStr));
+    }
+    
+    @Test
+    public void testPartial_UnsplitCodepoint()
+    {
+        Utf8PartialBuilder utf8 = new Utf8PartialBuilder();
+
+        String seq1 = "Hello-\uC2B5@\uC39F\uC3A4";
+        String seq2 = "\uC3BC\uC3A0\uC3A1-UTF-8!!";
+
+        String ret1 = utf8.toPartialString(BufferUtil.toBuffer(seq1,StandardCharsets.UTF_8));
+        String ret2 = utf8.toPartialString(BufferUtil.toBuffer(seq2,StandardCharsets.UTF_8));
+
+        assertThat("Seq1",ret1,is(seq1));
+        assertThat("Seq2",ret2,is(seq2));
+    }
+    
+    @Test
+    public void testPartial_SplitCodepoint()
+    {
+        Utf8PartialBuilder utf8 = new Utf8PartialBuilder();
+
+        String seq1 = "48656C6C6F2DEC8AB540EC8E9FEC8E";
+        String seq2 = "A4EC8EBCEC8EA0EC8EA12D5554462D382121";
+        
+        String ret1 = utf8.toPartialString(toByteBuffer(seq1));
+        String ret2 = utf8.toPartialString(toByteBuffer(seq2));
+
+        assertThat("Seq1",ret1,is("Hello-\uC2B5@\uC39F"));
+        assertThat("Seq2",ret2,is("\uC3A4\uC3BC\uC3A0\uC3A1-UTF-8!!"));
+    }
+    
+    @Test
+    public void testPartial_SplitCodepoint_WithNoBuf()
+    {
+        Utf8PartialBuilder utf8 = new Utf8PartialBuilder();
+
+        String seq1 = "48656C6C6F2DEC8AB540EC8E9FEC8E";
+        String seq2 = "A4EC8EBCEC8EA0EC8EA12D5554462D382121";
+        
+        String ret1 = utf8.toPartialString(toByteBuffer(seq1));
+        String ret2 = utf8.toPartialString(BufferUtil.EMPTY_BUFFER);
+        String ret3 = utf8.toPartialString(toByteBuffer(seq2));
+
+        assertThat("Seq1",ret1,is("Hello-\uC2B5@\uC39F"));
+        assertThat("Seq2",ret2,is(""));
+        assertThat("Seq3",ret3,is("\uC3A4\uC3BC\uC3A0\uC3A1-UTF-8!!"));
+    }
+}
diff --git a/jetty-websocket/websocket-server/pom.xml b/jetty-websocket/websocket-server/pom.xml
index 4c1a866..2b28838 100644
--- a/jetty-websocket/websocket-server/pom.xml
+++ b/jetty-websocket/websocket-server/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.websocket</groupId>
         <artifactId>websocket-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
+        <version>9.3.19-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
@@ -16,38 +16,17 @@
 
     <build>
         <plugins>
-          <plugin>
-          <groupId>org.apache.felix</groupId>
-          <artifactId>maven-bundle-plugin</artifactId>
-          <extensions>true</extensions>
-          <executions>
-            <execution>
-              <id>generate-manifest</id>
-              <goals>
-                <goal>manifest</goal>
-              </goals>
-              <configuration>
-                <instructions>
-                  <Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
-                  <Provide-Capability>osgi.serviceloader; osgi.serviceloader=org.eclipse.jetty.websocket.servlet.WebSocketServletFactory</Provide-Capability>
-                  <_nouses>true</_nouses>
-                </instructions>
-              </configuration>
-            </execution>
-          </executions>
-        </plugin>
             <plugin>
-              <groupId>org.apache.maven.plugins</groupId>
-              <artifactId>maven-jar-plugin</artifactId>
-              <executions>
-                <execution>
-                  <id>artifact-jars</id>
-                  <goals>
-                    <goal>jar</goal>
-                    <goal>test-jar</goal>
-                  </goals>
-                </execution>
-              </executions>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>test-jar</id>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
             </plugin>
         </plugins>
     </build>
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/HandshakeRFC6455.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/HandshakeRFC6455.java
index 259c082..af48a02 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/HandshakeRFC6455.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/HandshakeRFC6455.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.websocket.server;
 
 import java.io.IOException;
+
 import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/MappedWebSocketCreator.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/MappedWebSocketCreator.java
index 37d4f63..6b26423 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/MappedWebSocketCreator.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/MappedWebSocketCreator.java
@@ -18,7 +18,6 @@
 
 package org.eclipse.jetty.websocket.server;
 
-import org.eclipse.jetty.websocket.server.pathmap.PathMappings;
 import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
 
 /**
@@ -66,14 +65,6 @@
     void addMapping(org.eclipse.jetty.http.pathmap.PathSpec spec, WebSocketCreator creator);
     
     /**
-     * Get all of the PathMappings declared.
-     *
-     * @return the PathMappings
-     * @deprecated do not use, use {@link #getMapping(String)} instead.
-     */
-    @Deprecated
-    PathMappings<WebSocketCreator> getMappings();
-    
     /**
      * Returns the creator for the given path spec.
      *
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketConfiguration.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketConfiguration.java
index 6ea8d25..fe0b971 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketConfiguration.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketConfiguration.java
@@ -21,6 +21,8 @@
 import java.io.IOException;
 import java.util.Iterator;
 
+import javax.servlet.ServletContext;
+
 import org.eclipse.jetty.http.pathmap.MappedResource;
 import org.eclipse.jetty.http.pathmap.PathMappings;
 import org.eclipse.jetty.http.pathmap.PathSpec;
@@ -46,9 +48,9 @@
     private final WebSocketServerFactory factory;
     private final PathMappings<WebSocketCreator> mappings = new PathMappings<>();
     
-    public NativeWebSocketConfiguration()
+    public NativeWebSocketConfiguration(ServletContext context)
     {
-        this(new WebSocketServerFactory());
+        this(new WebSocketServerFactory(context));
     }
     
     public NativeWebSocketConfiguration(WebSocketServerFactory webSocketServerFactory)
@@ -60,7 +62,7 @@
     @Override
     public void doStop() throws Exception
     {
-        mappings.reset();
+        mappings.removeIf((mapped) -> !(mapped.getResource() instanceof PersistedWebSocketCreator));
         super.doStop();
     }
     
@@ -111,13 +113,23 @@
     
     /**
      * Manually add a WebSocket mapping.
+     * <p>
+     *     If mapping is added before this configuration is started, then it is persisted through
+     *     stop/start of this configuration's lifecycle.  Otherwise it will be removed when
+     *     this configuration is stopped.
+     * </p>
      *
      * @param pathSpec the pathspec to respond on
      * @param creator the websocket creator to activate on the provided mapping.
      */
     public void addMapping(PathSpec pathSpec, WebSocketCreator creator)
     {
-        mappings.put(pathSpec, creator);
+        WebSocketCreator wsCreator = creator;
+        if (!isRunning())
+        {
+            wsCreator = new PersistedWebSocketCreator(creator);
+        }
+        mappings.put(pathSpec, wsCreator);
     }
     
     /**
@@ -132,11 +144,11 @@
     {
         if (spec instanceof org.eclipse.jetty.websocket.server.pathmap.ServletPathSpec)
         {
-            addMapping(new ServletPathSpec(spec.getPathSpec()), creator);
+            addMapping(new ServletPathSpec(spec.getSpec()), creator);
         }
         else if (spec instanceof org.eclipse.jetty.websocket.server.pathmap.RegexPathSpec)
         {
-            addMapping(new RegexPathSpec(spec.getPathSpec()), creator);
+            addMapping(new RegexPathSpec(spec.getSpec()), creator);
         }
         else
         {
@@ -154,22 +166,19 @@
      */
     public void addMapping(PathSpec pathSpec, final Class<?> endpointClass)
     {
-        mappings.put(pathSpec, new WebSocketCreator()
+        mappings.put(pathSpec, (req, resp) ->
         {
-            @Override
-            public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
+            try
             {
-                try
-                {
-                    return endpointClass.newInstance();
-                }
-                catch (InstantiationException | IllegalAccessException e)
-                {
-                    throw new WebSocketException("Unable to create instance of " + endpointClass.getName(), e);
-                }
+                return endpointClass.newInstance();
+            }
+            catch (InstantiationException | IllegalAccessException e)
+            {
+                throw new WebSocketException("Unable to create instance of " + endpointClass.getName(), e);
             }
         });
     }
+    
 
     @Override
     public void addMapping(String rawspec, WebSocketCreator creator)
@@ -243,10 +252,25 @@
         addMapping(pathSpec, endpointClass);
     }
 
-    @Override
-    public org.eclipse.jetty.websocket.server.pathmap.PathMappings<WebSocketCreator> getMappings()
+    private class PersistedWebSocketCreator implements WebSocketCreator
     {
-        throw new IllegalStateException("Access to PathMappings cannot be supported. See alternative API in javadoc for "
-                + MappedWebSocketCreator.class.getName());
+        private final WebSocketCreator delegate;
+        
+        public PersistedWebSocketCreator(WebSocketCreator delegate)
+        {
+            this.delegate = delegate;
+        }
+        
+        @Override
+        public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
+        {
+            return delegate.createWebSocket(req, resp);
+        }
+    
+        @Override
+        public String toString()
+        {
+            return "Persisted[" + super.toString() + "]";
+        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketServletContainerInitializer.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketServletContainerInitializer.java
index a99e709..fca57ef 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketServletContainerInitializer.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketServletContainerInitializer.java
@@ -33,7 +33,7 @@
         NativeWebSocketConfiguration configuration = (NativeWebSocketConfiguration) context.getAttribute(KEY);
         if (configuration == null)
         {
-            configuration = new NativeWebSocketConfiguration();
+            configuration = new NativeWebSocketConfiguration(context);
             context.setAttribute(KEY, configuration);
         }
         return configuration;
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandler.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandler.java
index 7c638ba..0f9a70b 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandler.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandler.java
@@ -53,8 +53,9 @@
             factory.register(websocketPojo);
         }
     }
-
-    private final WebSocketServletFactory webSocketFactory;
+    
+    private final ByteBufferPool bufferPool;
+    private WebSocketServletFactory webSocketFactory;
 
     public WebSocketHandler()
     {
@@ -63,10 +64,7 @@
     
     public WebSocketHandler(ByteBufferPool bufferPool)
     {
-        WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
-        configurePolicy(policy);
-        webSocketFactory = new WebSocketServerFactory(policy, bufferPool);
-        addBean(webSocketFactory);
+        this.bufferPool = bufferPool;
     }
 
     public abstract void configure(WebSocketServletFactory factory);
@@ -79,12 +77,18 @@
     @Override
     protected void doStart() throws Exception
     {
+        WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
+        configurePolicy(policy);
+        webSocketFactory = new WebSocketServerFactory(policy, getServer().getThreadPool(), bufferPool);
+        addBean(webSocketFactory);
         configure(webSocketFactory);
         super.doStart();
     }
-
+    
     public WebSocketServletFactory getWebSocketFactory()
     {
+        if (!isRunning())
+            throw new IllegalStateException("Not Started yet");
         return webSocketFactory;
     }
 
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandshake.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandshake.java
index 7a9d023..74569ec 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandshake.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandshake.java
@@ -28,8 +28,9 @@
     /**
      * Formulate a WebSocket upgrade handshake response.
      * 
-     * @param request
-     * @param response
+     * @param request the request
+     * @param response the response
+     * @throws IOException if unable to handshake
      */
     public void doHandshakeResponse(ServletUpgradeRequest request, ServletUpgradeResponse response) throws IOException;
 }
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerConnection.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerConnection.java
index d0dea38..2618fce 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerConnection.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerConnection.java
@@ -19,14 +19,11 @@
 package org.eclipse.jetty.websocket.server;
 
 import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
 import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.thread.Scheduler;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
@@ -34,8 +31,6 @@
 
 public class WebSocketServerConnection extends AbstractWebSocketConnection implements Connection.UpgradeTo
 {
-    private final AtomicBoolean opened = new AtomicBoolean(false);
-
     public WebSocketServerConnection(EndPoint endp, Executor executor, Scheduler scheduler, WebSocketPolicy policy, ByteBufferPool bufferPool)
     {
         super(endp,executor,scheduler,policy,bufferPool);
@@ -44,7 +39,7 @@
             endp.setIdleTimeout(policy.getIdleTimeout());
         }
     }
-
+    
     @Override
     public InetSocketAddress getLocalAddress()
     {
@@ -56,23 +51,6 @@
     {
         return getEndPoint().getRemoteAddress();
     }
-
-    @Override
-    public void onUpgradeTo(ByteBuffer prefilled)
-    {
-        prefill(prefilled);
-    }
-
-    @Override
-    public void onOpen()
-    {
-        boolean beenOpened = opened.getAndSet(true);
-        if (!beenOpened)
-        {
-            getSession().open();
-        }
-        super.onOpen();
-    }
     
     @Override
     public void setNextIncomingFrames(IncomingFrames incoming)
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java
index 1133979..988fc66 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java
@@ -22,44 +22,55 @@
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
+import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnection;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.DecoratedObjectFactory;
 import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
 import org.eclipse.jetty.util.thread.Scheduler;
 import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
-import org.eclipse.jetty.websocket.api.StatusCode;
 import org.eclipse.jetty.websocket.api.WebSocketException;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
 import org.eclipse.jetty.websocket.api.util.QuoteUtil;
 import org.eclipse.jetty.websocket.common.LogicalConnection;
 import org.eclipse.jetty.websocket.common.SessionFactory;
-import org.eclipse.jetty.websocket.common.SessionListener;
 import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.WebSocketSessionFactory;
 import org.eclipse.jetty.websocket.common.events.EventDriver;
 import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
 import org.eclipse.jetty.websocket.common.extensions.ExtensionStack;
 import org.eclipse.jetty.websocket.common.extensions.WebSocketExtensionFactory;
+import org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection;
+import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
 import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
 import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
 import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
@@ -68,66 +79,86 @@
 /**
  * Factory to create WebSocket connections
  */
-public class WebSocketServerFactory extends ContainerLifeCycle implements WebSocketCreator, WebSocketServletFactory, SessionListener
+public class WebSocketServerFactory extends ContainerLifeCycle implements WebSocketCreator, WebSocketContainerScope, WebSocketServletFactory
 {
     private static final Logger LOG = Log.getLogger(WebSocketServerFactory.class);
-
+    
     private final ClassLoader contextClassloader;
     private final Map<Integer, WebSocketHandshake> handshakes = new HashMap<>();
-    /**
-     * Have the factory maintain 1 and only 1 scheduler. All connections share this scheduler.
-     */
+    // TODO: obtain shared (per server scheduler, somehow)
     private final Scheduler scheduler = new ScheduledExecutorScheduler();
+    private final List<WebSocketSession.Listener> listeners = new CopyOnWriteArrayList<>();
     private final String supportedVersions;
     private final WebSocketPolicy defaultPolicy;
     private final EventDriverFactory eventDriverFactory;
     private final ByteBufferPool bufferPool;
     private final WebSocketExtensionFactory extensionFactory;
-    private List<SessionFactory> sessionFactories;
-    private Set<WebSocketSession> openSessions = new CopyOnWriteArraySet<>();
+    private final ServletContext context; // can be null when this factory is used from WebSocketHandler
+    private final List<SessionFactory> sessionFactories = new ArrayList<>();
+    private final List<Class<?>> registeredSocketClasses = new ArrayList<>();
+    private Executor executor;
+    private DecoratedObjectFactory objectFactory;
     private WebSocketCreator creator;
-    private List<Class<?>> registeredSocketClasses;
-
-    public WebSocketServerFactory()
+    
+    public WebSocketServerFactory(ServletContext context)
     {
-        this(WebSocketPolicy.newServerPolicy(), new MappedByteBufferPool());
+        this(context, WebSocketPolicy.newServerPolicy(), new MappedByteBufferPool());
     }
-
-    public WebSocketServerFactory(WebSocketPolicy policy)
+    
+    public WebSocketServerFactory(ServletContext context, ByteBufferPool bufferPool)
     {
-        this(policy, new MappedByteBufferPool());
+        this(context, WebSocketPolicy.newServerPolicy(), bufferPool);
     }
-
-    public WebSocketServerFactory(ByteBufferPool bufferPool)
+    
+    /**
+     * Entry point for {@link org.eclipse.jetty.websocket.servlet.WebSocketServletFactory.Loader}
+     *
+     * @param context the servlet context
+     * @param policy the policy to use
+     */
+    public WebSocketServerFactory(ServletContext context, WebSocketPolicy policy)
     {
-        this(WebSocketPolicy.newServerPolicy(), bufferPool);
+        this(context, policy, new MappedByteBufferPool());
     }
-
-    public WebSocketServerFactory(WebSocketPolicy policy, ByteBufferPool bufferPool)
+    
+    public WebSocketServerFactory(ServletContext context, WebSocketPolicy policy, ByteBufferPool bufferPool)
     {
+        this(Objects.requireNonNull(context, ServletContext.class.getName()), policy, null, null, bufferPool);
+    }
+    
+    /**
+     * Protected entry point for {@link WebSocketHandler}
+     *
+     * @param policy the policy to use
+     * @param executor the executor to use
+     * @param bufferPool the buffer pool to use
+     */
+    protected WebSocketServerFactory(WebSocketPolicy policy, Executor executor, ByteBufferPool bufferPool)
+    {
+        this(null, policy, new DecoratedObjectFactory(), executor, bufferPool);
+    }
+    
+    private WebSocketServerFactory(ServletContext context, WebSocketPolicy policy, DecoratedObjectFactory objectFactory, Executor executor, ByteBufferPool bufferPool)
+    {
+        this.context = context;
+        this.objectFactory = objectFactory;
+        this.executor = executor;
+        
         handshakes.put(HandshakeRFC6455.VERSION, new HandshakeRFC6455());
-
+        
         addBean(scheduler);
         addBean(bufferPool);
         
         this.contextClassloader = Thread.currentThread().getContextClassLoader();
-
-        this.registeredSocketClasses = new ArrayList<>();
-
+        
         this.defaultPolicy = policy;
         this.eventDriverFactory = new EventDriverFactory(defaultPolicy);
         this.bufferPool = bufferPool;
-        this.extensionFactory = new WebSocketExtensionFactory(defaultPolicy, this.bufferPool);
+        this.extensionFactory = new WebSocketExtensionFactory(this);
         
-        // Bug #431459 - unregistering compression extensions till they are more stable
-        this.extensionFactory.unregister("deflate-frame");
-        this.extensionFactory.unregister("permessage-deflate");
-        this.extensionFactory.unregister("x-webkit-deflate-frame");
-        
-        this.sessionFactories = new ArrayList<>();
         this.sessionFactories.add(new WebSocketSessionFactory(this));
         this.creator = this;
-
+        
         // Create supportedVersions
         List<Integer> versions = new ArrayList<>();
         for (int v : handshakes.keySet())
@@ -146,13 +177,23 @@
         }
         supportedVersions = rv.toString();
     }
-
+    
+    public void addSessionListener(WebSocketSession.Listener listener)
+    {
+        listeners.add(listener);
+    }
+    
+    public void removeSessionListener(WebSocketSession.Listener listener)
+    {
+        listeners.remove(listener);
+    }
+    
     @Override
     public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response) throws IOException
     {
         return acceptWebSocket(getCreator(), request, response);
     }
-
+    
     @Override
     public boolean acceptWebSocket(WebSocketCreator creator, HttpServletRequest request, HttpServletResponse response) throws IOException
     {
@@ -160,26 +201,31 @@
         try
         {
             Thread.currentThread().setContextClassLoader(contextClassloader);
+            
+            // Create Servlet Specific Upgrade Request/Response objects
             ServletUpgradeRequest sockreq = new ServletUpgradeRequest(request);
             ServletUpgradeResponse sockresp = new ServletUpgradeResponse(response);
-
+            
             Object websocketPojo = creator.createWebSocket(sockreq, sockresp);
-
+            
             // Handle response forbidden (and similar paths)
             if (sockresp.isCommitted())
             {
                 return false;
             }
-
+            
             if (websocketPojo == null)
             {
                 // no creation, sorry
                 sockresp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Endpoint Creation Failed");
                 return false;
             }
-
+            
+            // Allow Decorators to do their thing
+            websocketPojo = getObjectFactory().decorate(websocketPojo);
+            
             // Get the original HTTPConnection
-            HttpConnection connection = (HttpConnection)request.getAttribute("org.eclipse.jetty.server.HttpConnection");
+            HttpConnection connection = (HttpConnection) request.getAttribute("org.eclipse.jetty.server.HttpConnection");
             
             // Send the upgrade
             EventDriver driver = eventDriverFactory.wrap(websocketPojo);
@@ -194,7 +240,7 @@
             Thread.currentThread().setContextClassLoader(old);
         }
     }
-
+    
     public void addSessionFactory(SessionFactory sessionFactory)
     {
         if (sessionFactories.contains(sessionFactory))
@@ -203,54 +249,14 @@
         }
         this.sessionFactories.add(sessionFactory);
     }
-
-    @Override
-    public void cleanup()
-    {
-        try
-        {
-            this.stop();
-        }
-        catch (Exception e)
-        {
-            LOG.warn(e);
-        }
-    }
-
-    protected void shutdownAllConnections()
-    {
-        for (WebSocketSession session : openSessions)
-        {
-            if (session.getConnection() != null)
-            {
-                try
-                {
-                    session.getConnection().close(
-                            StatusCode.SHUTDOWN,
-                            "Shutdown");
-                }
-                catch (Throwable t)
-                {
-                    LOG.debug("During Shutdown All Connections",t);
-                }
-            }
-        }
-        openSessions.clear();
-    }
-
-    @Override
-    public WebSocketServletFactory createFactory(WebSocketPolicy policy)
-    {
-        return new WebSocketServerFactory(policy, bufferPool);
-    }
-
+    
     private WebSocketSession createSession(URI requestURI, EventDriver websocket, LogicalConnection connection)
     {
         if (websocket == null)
         {
             throw new InvalidWebSocketException("Unable to create Session from null websocket");
         }
-
+        
         for (SessionFactory impl : sessionFactories)
         {
             if (impl.supports(websocket))
@@ -265,10 +271,10 @@
                 }
             }
         }
-
+        
         throw new InvalidWebSocketException("Unable to create Session: unrecognized internal EventDriver type: " + websocket.getClass().getName());
     }
-
+    
     /**
      * Default Creator logic
      */
@@ -279,64 +285,101 @@
         {
             throw new WebSocketException("No WebSockets have been registered with the factory.  Cannot use default implementation of WebSocketCreator.");
         }
-
+        
         if (registeredSocketClasses.size() > 1)
         {
             LOG.warn("You have registered more than 1 websocket object, and are using the default WebSocketCreator! Using first registered websocket.");
         }
-
+        
         Class<?> firstClass = registeredSocketClasses.get(0);
         try
         {
-            return firstClass.newInstance();
+            return objectFactory.createInstance(firstClass);
         }
         catch (InstantiationException | IllegalAccessException e)
         {
             throw new WebSocketException("Unable to create instance of " + firstClass, e);
         }
     }
-
+    
     @Override
-    protected void doStop() throws Exception
+    protected void doStart() throws Exception
     {
-        shutdownAllConnections();
-        super.doStop();
+        if(this.objectFactory == null && context != null)
+        {
+            this.objectFactory = (DecoratedObjectFactory) context.getAttribute(DecoratedObjectFactory.ATTR);
+            if (this.objectFactory == null)
+            {
+                throw new IllegalStateException("Unable to find required ServletContext attribute: " + DecoratedObjectFactory.ATTR);
+            }
+        }
+    
+        if(this.executor == null && context != null)
+        {
+            ContextHandler contextHandler = ContextHandler.getContextHandler(context);
+            this.executor = contextHandler.getServer().getThreadPool();
+        }
+        
+        Objects.requireNonNull(this.objectFactory, DecoratedObjectFactory.class.getName());
+        Objects.requireNonNull(this.executor, Executor.class.getName());
+        
+        super.doStart();
     }
-
+    
+    @Override
+    public ByteBufferPool getBufferPool()
+    {
+        return this.bufferPool;
+    }
+    
     @Override
     public WebSocketCreator getCreator()
     {
         return this.creator;
     }
-
+    
+    @Override
+    public Executor getExecutor()
+    {
+        return this.executor;
+    }
+    
+    public DecoratedObjectFactory getObjectFactory()
+    {
+        return objectFactory;
+    }
+    
     public EventDriverFactory getEventDriverFactory()
     {
         return eventDriverFactory;
     }
-
+    
     @Override
     public ExtensionFactory getExtensionFactory()
     {
         return extensionFactory;
     }
-
-    public Set<WebSocketSession> getOpenSessions()
+    
+    public Collection<WebSocketSession> getOpenSessions()
     {
-        return Collections.unmodifiableSet(this.openSessions);
+        return getBeans(WebSocketSession.class);
     }
-
+    
     @Override
     public WebSocketPolicy getPolicy()
     {
         return defaultPolicy;
     }
-
+    
     @Override
-    public void init() throws Exception
+    public SslContextFactory getSslContextFactory()
     {
-        start(); // start lifecycle
+        /* Not relevant for a Server, as this is defined in the
+         * Connector configuration
+         */
+        return null;
     }
-
+    
     @Override
     public boolean isUpgradeRequest(HttpServletRequest request, HttpServletResponse response)
     {
@@ -361,7 +404,7 @@
             // no "Connection: upgrade" header present.
             return false;
         }
-
+        
         // Test for "Upgrade" token
         boolean foundUpgradeToken = false;
         Iterator<String> iter = QuoteUtil.splitAt(connection, ",");
@@ -374,7 +417,7 @@
                 break;
             }
         }
-
+        
         if (!foundUpgradeToken)
         {
             return false;
@@ -385,68 +428,67 @@
             // not a "GET" request (not a websocket upgrade)
             return false;
         }
-
+        
         if (!"HTTP/1.1".equals(request.getProtocol()))
         {
             LOG.debug("Not a 'HTTP/1.1' request (was [" + request.getProtocol() + "])");
             return false;
         }
-
+        
         return true;
     }
-
-    @Override
-    public void onSessionClosed(WebSocketSession session)
-    {
-        this.openSessions.remove(session);
-    }
-
+    
     @Override
     public void onSessionOpened(WebSocketSession session)
     {
-        this.openSessions.add(session);
+        addManaged(session);
+        notifySessionListeners(listener -> listener.onOpened(session));
     }
-
-    protected String[] parseProtocols(String protocol)
+    
+    @Override
+    public void onSessionClosed(WebSocketSession session)
     {
-        if (protocol == null)
-        {
-            return new String[]{null};
-        }
-        protocol = protocol.trim();
-        if (protocol.length() == 0)
-        {
-            return new String[]{null};
-        }
-        String[] passed = protocol.split("\\s*,\\s*");
-        String[] protocols = new String[passed.length + 1];
-        System.arraycopy(passed, 0, protocols, 0, passed.length);
-        return protocols;
+        removeBean(session);
+        notifySessionListeners(listener -> listener.onClosed(session));
     }
-
+    
+    private void notifySessionListeners(Consumer<WebSocketSession.Listener> consumer)
+    {
+        for (WebSocketSession.Listener listener : listeners)
+        {
+            try
+            {
+                consumer.accept(listener);
+            }
+            catch (Throwable x)
+            {
+                LOG.info("Exception while invoking listener " + listener, x);
+            }
+        }
+    }
+    
     @Override
     public void register(Class<?> websocketPojo)
     {
         registeredSocketClasses.add(websocketPojo);
     }
-
+    
     @Override
     public void setCreator(WebSocketCreator creator)
     {
         this.creator = creator;
     }
-
+    
     /**
      * Upgrade the request/response to a WebSocket Connection.
      * <p/>
      * This method will not normally return, but will instead throw a UpgradeConnectionException, to exit HTTP handling and initiate WebSocket handling of the
      * connection.
      *
-     * @param http     the raw http connection
-     * @param request  The request to upgrade
+     * @param http the raw http connection
+     * @param request The request to upgrade
      * @param response The response to upgrade
-     * @param driver   The websocket handler implementation to use
-     * @throws IOException
+     * @param driver The websocket handler implementation to use
      */
     private boolean upgrade(HttpConnection http, ServletUpgradeRequest request, ServletUpgradeResponse response, EventDriver driver) throws IOException
     {
@@ -458,14 +500,14 @@
         {
             throw new IllegalStateException("Not a 'HTTP/1.1' request");
         }
-
+        
         int version = request.getHeaderInt("Sec-WebSocket-Version");
         if (version < 0)
         {
             // Old pre-RFC version specifications (header not present in RFC-6455)
             version = request.getHeaderInt("Sec-WebSocket-Draft");
         }
-
+        
         WebSocketHandshake handshaker = handshakes.get(version);
         if (handshaker == null)
         {
@@ -490,14 +532,14 @@
             }
             warn.append(": [").append(supportedVersions).append("]");
             LOG.warn(warn.toString());
-
+            
             // Per RFC 6455 - 4.4 - Supporting Multiple Versions of WebSocket Protocol
             // Using the examples as outlined
             response.setHeader("Sec-WebSocket-Version", supportedVersions);
             response.sendError(HttpStatus.BAD_REQUEST_400, "Unsupported websocket version specification");
             return false;
         }
-
+        
         // Initialize / Negotiate Extensions
         ExtensionStack extensionStack = new ExtensionStack(getExtensionFactory());
         // The JSR allows for the extensions to be pre-negotiated, filtered, etc...
@@ -512,72 +554,82 @@
             // Use raw extension list from request
             extensionStack.negotiate(request.getExtensions());
         }
-
+        
         // Get original HTTP connection
         EndPoint endp = http.getEndPoint();
-        Executor executor = http.getConnector().getExecutor();
-        ByteBufferPool bufferPool = http.getConnector().getByteBufferPool();
+        Connector connector = http.getConnector();
+        Executor executor = connector.getExecutor();
+        ByteBufferPool bufferPool = connector.getByteBufferPool();
         
         // Setup websocket connection
-        WebSocketServerConnection wsConnection = new WebSocketServerConnection(endp, executor, scheduler, driver.getPolicy(), bufferPool);
-
+        AbstractWebSocketConnection wsConnection = new WebSocketServerConnection(endp, executor, scheduler, driver.getPolicy(), bufferPool);
+        
         extensionStack.setPolicy(driver.getPolicy());
         extensionStack.configure(wsConnection.getParser());
         extensionStack.configure(wsConnection.getGenerator());
-
+        
         if (LOG.isDebugEnabled())
         {
             LOG.debug("HttpConnection: {}", http);
             LOG.debug("WebSocketConnection: {}", wsConnection);
         }
-
+        
         // Setup Session
         WebSocketSession session = createSession(request.getRequestURI(), driver, wsConnection);
         session.setPolicy(driver.getPolicy());
         session.setUpgradeRequest(request);
-        // set true negotiated extension list back to response 
+        // set true negotiated extension list back to response
         response.setExtensions(extensionStack.getNegotiatedExtensions());
         session.setUpgradeResponse(response);
-        wsConnection.setSession(session);
-
+        wsConnection.addListener(session);
+        
         // Setup Incoming Routing
         wsConnection.setNextIncomingFrames(extensionStack);
         extensionStack.setNextIncoming(session);
-
+        
         // Setup Outgoing Routing
         session.setOutgoingHandler(extensionStack);
         extensionStack.setNextOutgoing(wsConnection);
-
+        
         // Start Components
-        try
+        session.addManaged(extensionStack);
+        this.addManaged(session);
+        
+        if (session.isFailed())
         {
-            session.start();
+            throw new IOException("Session failed to start");
         }
-        catch (Exception e)
-        {
-            throw new IOException("Unable to start Session", e);
-        }
-        try
-        {
-            extensionStack.start();
-        }
-        catch (Exception e)
-        {
-            throw new IOException("Unable to start Extension Stack", e);
-        }
-
+        
         // Tell jetty about the new upgraded connection
         request.setServletAttribute(HttpConnection.UPGRADE_CONNECTION_ATTRIBUTE, wsConnection);
-
+        
         if (LOG.isDebugEnabled())
             LOG.debug("Handshake Response: {}", handshaker);
-
+        
+        if (getSendServerVersion(connector))
+            response.setHeader("Server", HttpConfiguration.SERVER_VERSION);
+        
         // Process (version specific) handshake response
         handshaker.doHandshakeResponse(request, response);
-
+        
         if (LOG.isDebugEnabled())
             LOG.debug("Websocket upgrade {} {} {} {}", request.getRequestURI(), version, response.getAcceptedSubProtocol(), wsConnection);
-
+        
         return true;
     }
+    
+    private boolean getSendServerVersion(Connector connector)
+    {
+        ConnectionFactory connFactory = connector.getConnectionFactory(HttpVersion.HTTP_1_1.asString());
+        if (connFactory == null)
+            return false;
+        
+        if (connFactory instanceof HttpConnectionFactory)
+        {
+            HttpConfiguration httpConf = ((HttpConnectionFactory) connFactory).getHttpConfiguration();
+            if (httpConf != null)
+                return httpConf.getSendServerVersion();
+        }
+        return false;
+    }
 }
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilter.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilter.java
index 6a5c4bf..f5d0042 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilter.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilter.java
@@ -34,18 +34,15 @@
 
 import org.eclipse.jetty.http.pathmap.MappedResource;
 import org.eclipse.jetty.http.pathmap.PathSpec;
-import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
-import org.eclipse.jetty.util.component.AbstractLifeCycle;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.component.Dumpable;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
 import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
 
@@ -53,12 +50,12 @@
  * Inline Servlet Filter to capture WebSocket upgrade requests and perform path mappings to {@link WebSocketCreator} objects.
  */
 @ManagedObject("WebSocket Upgrade Filter")
-public class WebSocketUpgradeFilter extends AbstractLifeCycle implements Filter, MappedWebSocketCreator, Dumpable
+public class WebSocketUpgradeFilter implements Filter, MappedWebSocketCreator, Dumpable
 {
+    private static final Logger LOG = Log.getLogger(WebSocketUpgradeFilter.class);
     public static final String CONTEXT_ATTRIBUTE_KEY = "contextAttributeKey";
     public static final String CONFIG_ATTRIBUTE_KEY = "configAttributeKey";
-    private static final Logger LOG = Log.getLogger(WebSocketUpgradeFilter.class);
-    
+
     public static WebSocketUpgradeFilter configureContext(ServletContextHandler context) throws ServletException
     {
         // Prevent double configure
@@ -113,6 +110,7 @@
     }
     
     private NativeWebSocketConfiguration configuration;
+    private boolean localConfiguration = false;
     private boolean alreadySetToAttribute = false;
     
     public WebSocketUpgradeFilter()
@@ -120,9 +118,9 @@
         // do nothing
     }
     
-    public WebSocketUpgradeFilter(WebSocketPolicy policy, ByteBufferPool bufferPool)
+    public WebSocketUpgradeFilter(WebSocketServerFactory factory)
     {
-        this(new NativeWebSocketConfiguration(new WebSocketServerFactory(policy, bufferPool)));
+        this(new NativeWebSocketConfiguration(factory));
     }
     
     public WebSocketUpgradeFilter(NativeWebSocketConfiguration configuration)
@@ -136,6 +134,9 @@
         configuration.addMapping(spec, creator);
     }
     
+    /**
+     * @deprecated use new {@link #addMapping(org.eclipse.jetty.http.pathmap.PathSpec, WebSocketCreator)} instead
+     */
     @Deprecated
     @Override
     public void addMapping(org.eclipse.jetty.websocket.server.pathmap.PathSpec spec, WebSocketCreator creator)
@@ -161,7 +162,10 @@
         try
         {
             alreadySetToAttribute = false;
-            configuration.stop();
+            if(localConfiguration)
+            {
+                configuration.stop();
+            }
         }
         catch (Exception e)
         {
@@ -292,14 +296,6 @@
     }
     
     @Override
-    @Deprecated
-    public org.eclipse.jetty.websocket.server.pathmap.PathMappings<WebSocketCreator> getMappings()
-    {
-        throw new IllegalStateException("Access to PathMappings cannot be supported. See alternative API in javadoc for "
-                + MappedWebSocketCreator.class.getName());
-    }
-    
-    @Override
     public void init(FilterConfig config) throws ServletException
     {
         try
@@ -329,7 +325,11 @@
                 }
             }
             
-            this.configuration.start();
+            if(!this.configuration.isRunning())
+            {
+                localConfiguration = true;
+                this.configuration.start();
+            }
             
             String max = config.getInitParameter("maxIdleTime");
             if (max != null)
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeHandlerWrapper.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeHandlerWrapper.java
index 290b232..d204ed5 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeHandlerWrapper.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeHandlerWrapper.java
@@ -30,20 +30,21 @@
 import org.eclipse.jetty.io.MappedByteBufferPool;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.handler.HandlerWrapper;
+import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
 
 public class WebSocketUpgradeHandlerWrapper extends HandlerWrapper implements MappedWebSocketCreator
 {
     private NativeWebSocketConfiguration configuration;
 
-    public WebSocketUpgradeHandlerWrapper()
+    public WebSocketUpgradeHandlerWrapper(ServletContextHandler context)
     {
-        this(new MappedByteBufferPool());
+        this(context, new MappedByteBufferPool());
     }
     
-    public WebSocketUpgradeHandlerWrapper(ByteBufferPool bufferPool)
+    public WebSocketUpgradeHandlerWrapper(ServletContextHandler context, ByteBufferPool bufferPool)
     {
-        this.configuration = new NativeWebSocketConfiguration(new WebSocketServerFactory(bufferPool));
+        this.configuration = new NativeWebSocketConfiguration(new WebSocketServerFactory(context.getServletContext(), bufferPool));
     }
     
     @Override
@@ -79,13 +80,6 @@
     }
 
     @Override
-    public org.eclipse.jetty.websocket.server.pathmap.PathMappings<WebSocketCreator> getMappings()
-    {
-        throw new IllegalStateException("Access to PathMappings cannot be supported. See alternative API in javadoc for "
-                + MappedWebSocketCreator.class.getName());
-    }
-    
-    @Override
     public WebSocketCreator getMapping(String target)
     {
         return configuration.getMapping(target);
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/PathMappings.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/PathMappings.java
deleted file mode 100644
index d241190..0000000
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/PathMappings.java
+++ /dev/null
@@ -1,192 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.server.pathmap;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-import org.eclipse.jetty.util.annotation.ManagedAttribute;
-import org.eclipse.jetty.util.annotation.ManagedObject;
-import org.eclipse.jetty.util.component.ContainerLifeCycle;
-import org.eclipse.jetty.util.component.Dumpable;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.websocket.server.pathmap.PathMappings.MappedResource;
-
-/**
- * Path Mappings of PathSpec to Resource.
- * <p>
- * Sorted into search order upon entry into the Set
- * 
- * @param <E>
- * @deprecated use {@link org.eclipse.jetty.http.pathmap.PathMappings} instead
- */
-@Deprecated
-@ManagedObject("Path Mappings")
-public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
-{
-    @ManagedObject("Mapped Resource")
-    public static class MappedResource<E> implements Comparable<MappedResource<E>>
-    {
-        private final PathSpec pathSpec;
-        private final E resource;
-
-        public MappedResource(PathSpec pathSpec, E resource)
-        {
-            this.pathSpec = pathSpec;
-            this.resource = resource;
-        }
-
-        /**
-         * Comparison is based solely on the pathSpec
-         */
-        @Override
-        public int compareTo(MappedResource<E> other)
-        {
-            return this.pathSpec.compareTo(other.pathSpec);
-        }
-
-        @Override
-        public boolean equals(Object obj)
-        {
-            if (this == obj)
-            {
-                return true;
-            }
-            if (obj == null)
-            {
-                return false;
-            }
-            if (getClass() != obj.getClass())
-            {
-                return false;
-            }
-            MappedResource<?> other = (MappedResource<?>)obj;
-            if (pathSpec == null)
-            {
-                if (other.pathSpec != null)
-                {
-                    return false;
-                }
-            }
-            else if (!pathSpec.equals(other.pathSpec))
-            {
-                return false;
-            }
-            return true;
-        }
-
-        @ManagedAttribute(value = "path spec", readonly = true)
-        public PathSpec getPathSpec()
-        {
-            return pathSpec;
-        }
-
-        @ManagedAttribute(value = "resource", readonly = true)
-        public E getResource()
-        {
-            return resource;
-        }
-
-        @Override
-        public int hashCode()
-        {
-            final int prime = 31;
-            int result = 1;
-            result = (prime * result) + ((pathSpec == null)?0:pathSpec.hashCode());
-            return result;
-        }
-
-        @Override
-        public String toString()
-        {
-            return String.format("MappedResource[pathSpec=%s,resource=%s]",pathSpec,resource);
-        }
-    }
-
-    private static final Logger LOG = Log.getLogger(PathMappings.class);
-    private List<MappedResource<E>> mappings = new ArrayList<MappedResource<E>>();
-    private MappedResource<E> defaultResource = null;
-
-    @Override
-    public String dump()
-    {
-        return ContainerLifeCycle.dump(this);
-    }
-
-    @Override
-    public void dump(Appendable out, String indent) throws IOException
-    {
-        ContainerLifeCycle.dump(out,indent,mappings);
-    }
-
-    @ManagedAttribute(value = "mappings", readonly = true)
-    public List<MappedResource<E>> getMappings()
-    {
-        return mappings;
-    }
-    
-    public void reset()
-    {
-        mappings.clear();
-    }
-
-    public MappedResource<E> getMatch(String path)
-    {
-        int len = mappings.size();
-        for (int i = 0; i < len; i++)
-        {
-            MappedResource<E> mr = mappings.get(i);
-            if (mr.getPathSpec().matches(path))
-            {
-                return mr;
-            }
-        }
-        return defaultResource;
-    }
-
-    @Override
-    public Iterator<MappedResource<E>> iterator()
-    {
-        return mappings.iterator();
-    }
-
-    public void put(PathSpec pathSpec, E resource)
-    {
-        MappedResource<E> entry = new MappedResource<>(pathSpec,resource);
-        if (pathSpec.group == PathSpecGroup.DEFAULT)
-        {
-            defaultResource = entry;
-        }
-        // TODO: warning on replacement of existing mapping?
-        mappings.add(entry);
-        if (LOG.isDebugEnabled())
-            LOG.debug("Added {} to {}",entry,this);
-        Collections.sort(mappings);
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("%s[size=%d]",this.getClass().getSimpleName(),mappings.size());
-    }
-}
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/PathSpec.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/PathSpec.java
index cb22ada..026196a 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/PathSpec.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/PathSpec.java
@@ -19,149 +19,20 @@
 package org.eclipse.jetty.websocket.server.pathmap;
 
 /**
- * The base PathSpec, what all other path specs are based on
+ * @deprecated moved to jetty-http {@link org.eclipse.jetty.http.pathmap.PathSpec} (this facade will be removed in Jetty 9.4)
  */
-public abstract class PathSpec implements Comparable<PathSpec>
+@Deprecated
+public abstract class PathSpec
 {
-    protected String pathSpec;
-    protected PathSpecGroup group;
-    protected int pathDepth;
-    protected int specLength;
-
-    @Override
-    public int compareTo(PathSpec other)
+    private final String spec;
+    
+    protected PathSpec(String spec)
     {
-        // Grouping (increasing)
-        int diff = this.group.ordinal() - other.group.ordinal();
-        if (diff != 0)
-        {
-            return diff;
-        }
-
-        // Spec Length (decreasing)
-        diff = other.specLength - this.specLength;
-        if (diff != 0)
-        {
-            return diff;
-        }
-
-        // Path Spec Name (alphabetical)
-        return this.pathSpec.compareTo(other.pathSpec);
+        this.spec = spec;
     }
-
-    @Override
-    public boolean equals(Object obj)
+    
+    public String getSpec()
     {
-        if (this == obj)
-        {
-            return true;
-        }
-        if (obj == null)
-        {
-            return false;
-        }
-        if (getClass() != obj.getClass())
-        {
-            return false;
-        }
-        PathSpec other = (PathSpec)obj;
-        if (pathSpec == null)
-        {
-            if (other.pathSpec != null)
-            {
-                return false;
-            }
-        }
-        else if (!pathSpec.equals(other.pathSpec))
-        {
-            return false;
-        }
-        return true;
-    }
-
-    public PathSpecGroup getGroup()
-    {
-        return group;
-    }
-
-    /**
-     * Get the number of path elements that this path spec declares.
-     * <p>
-     * This is used to determine longest match logic.
-     * 
-     * @return the depth of the path segments that this spec declares
-     */
-    public int getPathDepth()
-    {
-        return pathDepth;
-    }
-
-    /**
-     * Return the portion of the path that is after the path spec.
-     * 
-     * @param path
-     *            the path to match against
-     * @return the path info portion of the string
-     */
-    public abstract String getPathInfo(String path);
-
-    /**
-     * Return the portion of the path that matches a path spec.
-     * 
-     * @param path
-     *            the path to match against
-     * @return the match, or null if no match at all
-     */
-    public abstract String getPathMatch(String path);
-
-    /**
-     * The as-provided path spec.
-     * 
-     * @return the as-provided path spec
-     */
-    public String getPathSpec()
-    {
-        return pathSpec;
-    }
-
-    /**
-     * Get the relative path.
-     * 
-     * @param base
-     *            the base the path is relative to
-     * @param path
-     *            the additional path
-     * @return the base plus path with pathSpec portion removed
-     */
-    public abstract String getRelativePath(String base, String path);
-
-    @Override
-    public int hashCode()
-    {
-        final int prime = 31;
-        int result = 1;
-        result = (prime * result) + ((pathSpec == null)?0:pathSpec.hashCode());
-        return result;
-    }
-
-    /**
-     * Test to see if the provided path matches this path spec
-     * 
-     * @param path
-     *            the path to test
-     * @return true if the path matches this path spec, false otherwise
-     */
-    public abstract boolean matches(String path);
-
-    @Override
-    public String toString()
-    {
-        StringBuilder str = new StringBuilder();
-        str.append(this.getClass().getSimpleName()).append("[\"");
-        str.append(pathSpec);
-        str.append("\",pathDepth=").append(pathDepth);
-        str.append(",group=").append(group);
-        str.append("]");
-        return str.toString();
+        return spec;
     }
 }
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/PathSpecGroup.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/PathSpecGroup.java
deleted file mode 100644
index c6e1e23..0000000
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/PathSpecGroup.java
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.server.pathmap;
-
-/**
- * Types of path spec groups.
- * <p>
- * This is used to facilitate proper pathspec search order.
- * <p>
- * Search Order: {@link PathSpecGroup#ordinal()} [increasin], {@link PathSpec#specLength} [decreasing], {@link PathSpec#pathSpec} [natural sort order]
- */
-public enum PathSpecGroup
-{
-    // NOTE: Order of enums determines order of Groups.
-
-    /**
-     * For exactly defined path specs, no glob.
-     */
-    EXACT,
-    /**
-     * For path specs that have a hardcoded prefix and suffix with wildcard glob in the middle.
-     * 
-     * <pre>
-     *   "^/downloads/[^/]*.zip$"  - regex spec
-     *   "/a/{var}/c"              - websocket spec
-     * </pre>
-     * 
-     * Note: there is no known servlet spec variant of this kind of path spec
-     */
-    MIDDLE_GLOB,
-    /**
-     * For path specs that have a hardcoded prefix and a trailing wildcard glob.
-     * <p>
-     * 
-     * <pre>
-     *   "/downloads/*"          - servlet spec
-     *   "/api/*"                - servlet spec
-     *   "^/rest/.*$"            - regex spec
-     *   "/bookings/{guest-id}"  - websocket spec
-     *   "/rewards/{vip-level}"  - websocket spec
-     * </pre>
-     */
-    PREFIX_GLOB,
-    /**
-     * For path specs that have a wildcard glob with a hardcoded suffix
-     * 
-     * <pre>
-     *   "*.do"        - servlet spec
-     *   "*.css"       - servlet spec
-     *   "^.*\.zip$"   - regex spec
-     * </pre>
-     * 
-     * Note: there is no known websocket spec variant of this kind of path spec
-     */
-    SUFFIX_GLOB,
-    /**
-     * The default spec for accessing the Root and/or Default behavior.
-     * 
-     * <pre>
-     *   "/"           - servlet spec   (Default Servlet)
-     *   "/"           - websocket spec (Root Context)
-     *   "^/$"         - regex spec     (Root Context)
-     * </pre>
-     */
-    DEFAULT;
-}
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/RegexPathSpec.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/RegexPathSpec.java
index 38e033a..6ac7141 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/RegexPathSpec.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/RegexPathSpec.java
@@ -18,159 +18,14 @@
 
 package org.eclipse.jetty.websocket.server.pathmap;
 
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
+/**
+ * @deprecated moved to jetty-http {@link org.eclipse.jetty.http.pathmap.RegexPathSpec} (this facade will be removed in Jetty 9.4) 
+ */
+@Deprecated
 public class RegexPathSpec extends PathSpec
 {
-    protected Pattern pattern;
-
-    protected RegexPathSpec()
-    {
-        super();
-    }
-
     public RegexPathSpec(String regex)
     {
-        super.pathSpec = regex;
-        boolean inGrouping = false;
-        this.pathDepth = 0;
-        this.specLength = pathSpec.length();
-        // build up a simple signature we can use to identify the grouping
-        StringBuilder signature = new StringBuilder();
-        for (char c : pathSpec.toCharArray())
-        {
-            switch (c)
-            {
-                case '[':
-                    inGrouping = true;
-                    break;
-                case ']':
-                    inGrouping = false;
-                    signature.append('g'); // glob
-                    break;
-                case '*':
-                    signature.append('g'); // glob
-                    break;
-                case '/':
-                    if (!inGrouping)
-                    {
-                        this.pathDepth++;
-                    }
-                    break;
-                default:
-                    if (!inGrouping)
-                    {
-                        if (Character.isLetterOrDigit(c))
-                        {
-                            signature.append('l'); // literal (exact)
-                        }
-                    }
-                    break;
-            }
-        }
-        this.pattern = Pattern.compile(pathSpec);
-
-        // Figure out the grouping based on the signature
-        String sig = signature.toString();
-
-        if (Pattern.matches("^l*$",sig))
-        {
-            this.group = PathSpecGroup.EXACT;
-        }
-        else if (Pattern.matches("^l*g+",sig))
-        {
-            this.group = PathSpecGroup.PREFIX_GLOB;
-        }
-        else if (Pattern.matches("^g+l+$",sig))
-        {
-            this.group = PathSpecGroup.SUFFIX_GLOB;
-        }
-        else
-        {
-            this.group = PathSpecGroup.MIDDLE_GLOB;
-        }
-    }
-
-    public Matcher getMatcher(String path)
-    {
-        return this.pattern.matcher(path);
-    }
-
-    @Override
-    public String getPathInfo(String path)
-    {
-        // Path Info only valid for PREFIX_GLOB types
-        if (group == PathSpecGroup.PREFIX_GLOB)
-        {
-            Matcher matcher = getMatcher(path);
-            if (matcher.matches())
-            {
-                if (matcher.groupCount() >= 1)
-                {
-                    String pathInfo = matcher.group(1);
-                    if ("".equals(pathInfo))
-                    {
-                        return "/";
-                    }
-                    else
-                    {
-                        return pathInfo;
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public String getPathMatch(String path)
-    {
-        Matcher matcher = getMatcher(path);
-        if (matcher.matches())
-        {
-            if (matcher.groupCount() >= 1)
-            {
-                int idx = matcher.start(1);
-                if (idx > 0)
-                {
-                    if (path.charAt(idx - 1) == '/')
-                    {
-                        idx--;
-                    }
-                    return path.substring(0,idx);
-                }
-            }
-            return path;
-        }
-        return null;
-    }
-
-    public Pattern getPattern()
-    {
-        return this.pattern;
-    }
-
-    @Override
-    public String getRelativePath(String base, String path)
-    {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public boolean matches(final String path)
-    {
-        int idx = path.indexOf('?');
-        if (idx >= 0)
-        {
-            // match only non-query part
-            return getMatcher(path.substring(0,idx)).matches();
-        }
-        else
-        {
-            // match entire path
-            return getMatcher(path).matches();
-        }
+        super(regex);
     }
 }
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/ServletPathSpec.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/ServletPathSpec.java
index 67e2c5c..7f78e68 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/ServletPathSpec.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/pathmap/ServletPathSpec.java
@@ -18,274 +18,14 @@
 
 package org.eclipse.jetty.websocket.server.pathmap;
 
-import org.eclipse.jetty.util.URIUtil;
-
+/**
+ * @deprecated moved to jetty-http {@link org.eclipse.jetty.http.pathmap.ServletPathSpec} (this facade will be removed in Jetty 9.4) 
+ */
+@Deprecated
 public class ServletPathSpec extends PathSpec
 {
-    public static final String PATH_SPEC_SEPARATORS = ":,";
-
-    /**
-     * Get multi-path spec splits.
-     * 
-     * @param servletPathSpec
-     *            the path spec that might contain multiple declared path specs
-     * @return the individual path specs found.
-     */
-    public static ServletPathSpec[] getMultiPathSpecs(String servletPathSpec)
-    {
-        String pathSpecs[] = servletPathSpec.split(PATH_SPEC_SEPARATORS);
-        int len = pathSpecs.length;
-        ServletPathSpec sps[] = new ServletPathSpec[len];
-        for (int i = 0; i < len; i++)
-        {
-            sps[i] = new ServletPathSpec(pathSpecs[i]);
-        }
-        return sps;
-    }
-
     public ServletPathSpec(String servletPathSpec)
     {
-        super();
-        assertValidServletPathSpec(servletPathSpec);
-
-        // The Path Spec for Default Servlet
-        if ((servletPathSpec == null) || (servletPathSpec.length() == 0) || "/".equals(servletPathSpec))
-        {
-            super.pathSpec = "/";
-            super.pathDepth = -1; // force this to be last in sort order
-            this.specLength = 1;
-            this.group = PathSpecGroup.DEFAULT;
-            return;
-        }
-
-        this.specLength = servletPathSpec.length();
-        super.pathDepth = 0;
-        char lastChar = servletPathSpec.charAt(specLength - 1);
-        // prefix based
-        if ((servletPathSpec.charAt(0) == '/') && (specLength > 1) && (lastChar == '*'))
-        {
-            this.group = PathSpecGroup.PREFIX_GLOB;
-        }
-        // suffix based
-        else if (servletPathSpec.charAt(0) == '*')
-        {
-            this.group = PathSpecGroup.SUFFIX_GLOB;
-        }
-        else
-        {
-            this.group = PathSpecGroup.EXACT;
-        }
-
-        for (int i = 0; i < specLength; i++)
-        {
-            int cp = servletPathSpec.codePointAt(i);
-            if (cp < 128)
-            {
-                char c = (char)cp;
-                switch (c)
-                {
-                    case '/':
-                        super.pathDepth++;
-                        break;
-                }
-            }
-        }
-
-        super.pathSpec = servletPathSpec;
-    }
-
-    private void assertValidServletPathSpec(String servletPathSpec)
-    {
-        if ((servletPathSpec == null) || servletPathSpec.equals(""))
-        {
-            return; // empty path spec
-        }
-
-        // Ensure we don't have path spec separators here in our single path spec.
-        for (char c : PATH_SPEC_SEPARATORS.toCharArray())
-        {
-            if (servletPathSpec.indexOf(c) >= 0)
-            {
-                throw new IllegalArgumentException("Servlet Spec 12.2 violation: encountered Path Spec Separator [" + PATH_SPEC_SEPARATORS
-                        + "] within specified path spec. did you forget to split this path spec up?");
-            }
-        }
-
-        int len = servletPathSpec.length();
-        // path spec must either start with '/' or '*.'
-        if (servletPathSpec.charAt(0) == '/')
-        {
-            // Prefix Based
-            if (len == 1)
-            {
-                return; // simple '/' path spec
-            }
-            int idx = servletPathSpec.indexOf('*');
-            if (idx < 0)
-            {
-                return; // no hit on glob '*'
-            }
-            // only allowed to have '*' at the end of the path spec
-            if (idx != (len - 1))
-            {
-                throw new IllegalArgumentException("Servlet Spec 12.2 violation: glob '*' can only exist at end of prefix based matches");
-            }
-        }
-        else if (servletPathSpec.startsWith("*."))
-        {
-            // Suffix Based
-            int idx = servletPathSpec.indexOf('/');
-            // cannot have path separator
-            if (idx >= 0)
-            {
-                throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have path separators");
-            }
-
-            idx = servletPathSpec.indexOf('*',2);
-            // only allowed to have 1 glob '*', at the start of the path spec
-            if (idx >= 1)
-            {
-                throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have multiple glob '*'");
-            }
-        }
-        else
-        {
-            throw new IllegalArgumentException("Servlet Spec 12.2 violation: path spec must start with \"/\" or \"*.\"");
-        }
-    }
-
-    @Override
-    public String getPathInfo(String path)
-    {
-        // Path Info only valid for PREFIX_GLOB types
-        if (group == PathSpecGroup.PREFIX_GLOB)
-        {
-            if (path.length() == (specLength - 2))
-            {
-                return null;
-            }
-            return path.substring(specLength - 2);
-        }
-
-        return null;
-    }
-
-    @Override
-    public String getPathMatch(String path)
-    {
-        switch (group)
-        {
-            case EXACT:
-                if (pathSpec.equals(path))
-                {
-                    return path;
-                }
-                else
-                {
-                    return null;
-                }
-            case PREFIX_GLOB:
-                if (isWildcardMatch(path))
-                {
-                    return path.substring(0,specLength - 2);
-                }
-                else
-                {
-                    return null;
-                }
-            case SUFFIX_GLOB:
-                if (path.regionMatches(path.length() - (specLength - 1),pathSpec,1,specLength - 1))
-                {
-                    return path;
-                }
-                else
-                {
-                    return null;
-                }
-            case DEFAULT:
-                return path;
-            default:
-                return null;
-        }
-    }
-
-    @Override
-    public String getRelativePath(String base, String path)
-    {
-        String info = getPathInfo(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;
-    }
-
-    private boolean isExactMatch(String path)
-    {
-        if (group == PathSpecGroup.EXACT)
-        {
-            if (pathSpec.equals(path))
-            {
-                return true;
-            }
-            return (path.charAt(path.length() - 1) == '/') && (path.equals(pathSpec + '/'));
-        }
-        return false;
-    }
-
-    private boolean isWildcardMatch(String path)
-    {
-        // For a spec of "/foo/*" match "/foo" , "/foo/..." but not "/foobar"
-        int cpl = specLength - 2;
-        if ((group == PathSpecGroup.PREFIX_GLOB) && (path.regionMatches(0,pathSpec,0,cpl)))
-        {
-            if ((path.length() == cpl) || ('/' == path.charAt(cpl)))
-            {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public boolean matches(String path)
-    {
-        switch (group)
-        {
-            case EXACT:
-                return isExactMatch(path);
-            case PREFIX_GLOB:
-                return isWildcardMatch(path);
-            case SUFFIX_GLOB:
-                return path.regionMatches((path.length() - specLength) + 1,pathSpec,1,specLength - 1);
-            case DEFAULT:
-                return true;
-            default:
-                return false;
-        }
+        super(servletPathSpec);
     }
 }
diff --git a/jetty-websocket/websocket-server/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.servlet.WebSocketServletFactory b/jetty-websocket/websocket-server/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.servlet.WebSocketServletFactory
deleted file mode 100644
index a5341c9..0000000
--- a/jetty-websocket/websocket-server/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.servlet.WebSocketServletFactory
+++ /dev/null
@@ -1 +0,0 @@
-org.eclipse.jetty.websocket.server.WebSocketServerFactory
\ No newline at end of file
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AnnotatedMaxMessageSizeTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AnnotatedMaxMessageSizeTest.java
index 1328ab6..5fb63df 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AnnotatedMaxMessageSizeTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AnnotatedMaxMessageSizeTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.io.IOException;
 import java.net.URI;
@@ -105,7 +105,7 @@
             client.write(new TextFrame().setPayload(msg));
 
             // Read frame (hopefully text frame)
-            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
             WebSocketFrame tf = frames.poll();
             Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
         }
@@ -133,7 +133,7 @@
             client.write(new TextFrame().setPayload(ByteBuffer.wrap(buf)));
 
             // Read frame (hopefully close frame saying its too large)
-            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
             WebSocketFrame tf = frames.poll();
             Assert.assertThat("Frame is close", tf.getOpCode(), is(OpCode.CLOSE));
             CloseInfo close = new CloseInfo(tf);
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ChromeTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ChromeTest.java
index 2343a7c..0a44f64 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ChromeTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ChromeTest.java
@@ -18,7 +18,8 @@
 
 package org.eclipse.jetty.websocket.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
 
 import java.util.concurrent.TimeUnit;
 
@@ -72,7 +73,7 @@
             client.write(new TextFrame().setPayload(msg));
 
             // Read frame (hopefully text frame)
-            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
             WebSocketFrame tf = frames.poll();
             Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
         }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DecoratorsLegacyTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DecoratorsLegacyTest.java
new file mode 100644
index 0000000..c3b65de
--- /dev/null
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DecoratorsLegacyTest.java
@@ -0,0 +1,181 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.server;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletContext;
+
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.toolchain.test.EventQueue;
+import org.eclipse.jetty.util.DecoratedObjectFactory;
+import org.eclipse.jetty.util.Decorator;
+import org.eclipse.jetty.websocket.api.WebSocketAdapter;
+import org.eclipse.jetty.websocket.common.WebSocketFrame;
+import org.eclipse.jetty.websocket.common.frames.TextFrame;
+import org.eclipse.jetty.websocket.common.test.BlockheadClient;
+import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
+import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
+import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
+import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
+import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class DecoratorsLegacyTest
+{
+    private static class DecoratorsSocket extends WebSocketAdapter
+    {
+        private final DecoratedObjectFactory objFactory;
+        
+        public DecoratorsSocket(DecoratedObjectFactory objFactory)
+        {
+            this.objFactory = objFactory;
+        }
+        
+        @Override
+        public void onWebSocketText(String message)
+        {
+            StringWriter str = new StringWriter();
+            PrintWriter out = new PrintWriter(str);
+            
+            if (objFactory != null)
+            {
+                out.printf("Object is a DecoratedObjectFactory%n");
+                List<Decorator> decorators = objFactory.getDecorators();
+                out.printf("Decorators.size = [%d]%n",decorators.size());
+                for (Decorator decorator : decorators)
+                {
+                    out.printf(" decorator[] = %s%n",decorator.getClass().getName());
+                }
+            }
+            else
+            {
+                out.printf("DecoratedObjectFactory is NULL%n");
+            }
+
+            getRemote().sendStringByFuture(str.toString());
+        }
+    }
+
+    private static class DecoratorsCreator implements WebSocketCreator
+    {
+        @Override
+        public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
+        {
+            ServletContext servletContext = req.getHttpServletRequest().getServletContext();
+            DecoratedObjectFactory objFactory = (DecoratedObjectFactory)servletContext.getAttribute(DecoratedObjectFactory.ATTR);
+            return new DecoratorsSocket(objFactory);
+        }
+    }
+
+    public static class DecoratorsRequestServlet extends WebSocketServlet
+    {
+        private static final long serialVersionUID = 1L;
+        private final WebSocketCreator creator;
+
+        public DecoratorsRequestServlet(WebSocketCreator creator)
+        {
+            this.creator = creator;
+        }
+
+        @Override
+        public void configure(WebSocketServletFactory factory)
+        {
+            factory.setCreator(this.creator);
+        }
+    }
+    
+    @SuppressWarnings("deprecation")
+    private static class DummyLegacyDecorator implements org.eclipse.jetty.servlet.ServletContextHandler.Decorator
+    {
+        @Override
+        public <T> T decorate(T o)
+        {
+            return o;
+        }
+
+        @Override
+        public void destroy(Object o)
+        {
+        }
+    }
+
+    private static SimpleServletServer server;
+    private static DecoratorsCreator decoratorsCreator;
+
+    @BeforeClass
+    public static void startServer() throws Exception
+    {
+        decoratorsCreator = new DecoratorsCreator();
+        server = new SimpleServletServer(new DecoratorsRequestServlet(decoratorsCreator))
+        {
+            @SuppressWarnings("deprecation")
+            @Override
+            protected void configureServletContextHandler(ServletContextHandler context)
+            {
+                context.getObjectFactory().clear();
+                // Add decorator in the legacy way
+                context.addDecorator(new DummyLegacyDecorator());
+            }
+        };
+        server.start();
+    }
+
+    @AfterClass
+    public static void stopServer()
+    {
+        server.stop();
+    }
+
+    @Test
+    public void testAccessRequestCookies() throws Exception
+    {
+        BlockheadClient client = new BlockheadClient(server.getServerUri());
+        client.setTimeout(1,TimeUnit.SECONDS);
+
+        try
+        {
+            client.connect();
+            client.sendStandardRequest();
+            client.expectUpgradeResponse();
+            
+            client.write(new TextFrame().setPayload("info"));
+
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
+            WebSocketFrame resp = frames.poll();
+            String textMsg = resp.getPayloadAsUTF8();
+            
+            assertThat("DecoratedObjectFactory", textMsg, containsString("Object is a DecoratedObjectFactory"));
+            assertThat("decorators.size", textMsg, containsString("Decorators.size = [1]"));
+            assertThat("decorator type", textMsg, containsString("decorator[] = " + DummyLegacyDecorator.class.getName()));
+        }
+        finally
+        {
+            client.close();
+        }
+    }
+}
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DecoratorsTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DecoratorsTest.java
new file mode 100644
index 0000000..3a353d7
--- /dev/null
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DecoratorsTest.java
@@ -0,0 +1,179 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.server;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletContext;
+
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.toolchain.test.EventQueue;
+import org.eclipse.jetty.util.DecoratedObjectFactory;
+import org.eclipse.jetty.util.Decorator;
+import org.eclipse.jetty.websocket.api.WebSocketAdapter;
+import org.eclipse.jetty.websocket.common.WebSocketFrame;
+import org.eclipse.jetty.websocket.common.frames.TextFrame;
+import org.eclipse.jetty.websocket.common.test.BlockheadClient;
+import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
+import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
+import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
+import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
+import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class DecoratorsTest
+{
+    private static class DecoratorsSocket extends WebSocketAdapter
+    {
+        private final DecoratedObjectFactory objFactory;
+        
+        public DecoratorsSocket(DecoratedObjectFactory objFactory)
+        {
+            this.objFactory = objFactory;
+        }
+        
+        @Override
+        public void onWebSocketText(String message)
+        {
+            StringWriter str = new StringWriter();
+            PrintWriter out = new PrintWriter(str);
+            
+            if (objFactory != null)
+            {
+                out.printf("Object is a DecoratedObjectFactory%n");
+                List<Decorator> decorators = objFactory.getDecorators();
+                out.printf("Decorators.size = [%d]%n",decorators.size());
+                for (Decorator decorator : decorators)
+                {
+                    out.printf(" decorator[] = %s%n",decorator.getClass().getName());
+                }
+            }
+            else
+            {
+                out.printf("DecoratedObjectFactory is NULL%n");
+            }
+
+            getRemote().sendStringByFuture(str.toString());
+        }
+    }
+
+    private static class DecoratorsCreator implements WebSocketCreator
+    {
+        @Override
+        public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
+        {
+            ServletContext servletContext = req.getHttpServletRequest().getServletContext();
+            DecoratedObjectFactory objFactory = (DecoratedObjectFactory)servletContext.getAttribute(DecoratedObjectFactory.ATTR);
+            return new DecoratorsSocket(objFactory);
+        }
+    }
+
+    public static class DecoratorsRequestServlet extends WebSocketServlet
+    {
+        private static final long serialVersionUID = 1L;
+        private final WebSocketCreator creator;
+
+        public DecoratorsRequestServlet(WebSocketCreator creator)
+        {
+            this.creator = creator;
+        }
+
+        @Override
+        public void configure(WebSocketServletFactory factory)
+        {
+            factory.setCreator(this.creator);
+        }
+    }
+    
+    private static class DummyUtilDecorator implements org.eclipse.jetty.util.Decorator
+    {
+        @Override
+        public <T> T decorate(T o)
+        {
+            return o;
+        }
+
+        @Override
+        public void destroy(Object o)
+        {
+        }
+    }
+
+    private static SimpleServletServer server;
+    private static DecoratorsCreator decoratorsCreator;
+
+    @BeforeClass
+    public static void startServer() throws Exception
+    {
+        decoratorsCreator = new DecoratorsCreator();
+        server = new SimpleServletServer(new DecoratorsRequestServlet(decoratorsCreator))
+        {
+            @Override
+            protected void configureServletContextHandler(ServletContextHandler context)
+            {
+                // Add decorator in the new util way
+                context.getObjectFactory().clear();
+                context.getObjectFactory().addDecorator(new DummyUtilDecorator());
+            }
+        };
+        server.start();
+    }
+
+    @AfterClass
+    public static void stopServer()
+    {
+        server.stop();
+    }
+
+    @Test
+    public void testAccessRequestCookies() throws Exception
+    {
+        BlockheadClient client = new BlockheadClient(server.getServerUri());
+        client.setTimeout(1,TimeUnit.SECONDS);
+
+        try
+        {
+            client.connect();
+            client.sendStandardRequest();
+            client.expectUpgradeResponse();
+            
+            client.write(new TextFrame().setPayload("info"));
+
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
+            WebSocketFrame resp = frames.poll();
+            String textMsg = resp.getPayloadAsUTF8();
+            
+            assertThat("DecoratedObjectFactory", textMsg, containsString("Object is a DecoratedObjectFactory"));
+            assertThat("decorators.size", textMsg, containsString("Decorators.size = [1]"));
+            assertThat("decorator type", textMsg, containsString("decorator[] = " + DummyUtilDecorator.class.getName()));
+        }
+        finally
+        {
+            client.close();
+        }
+    }
+}
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java
index 8039109..6b41b72 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java
@@ -26,6 +26,7 @@
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
+import org.eclipse.jetty.websocket.common.test.IBlockheadClient;
 import org.eclipse.jetty.websocket.server.examples.MyEchoServlet;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -52,7 +53,7 @@
     @Test
     public void testConnectionKeepAlive() throws Exception
     {
-        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
         {
             // Odd Connection Header value seen in Firefox
             client.setConnectionValue("keep-alive, Upgrade");
@@ -65,7 +66,7 @@
             client.write(new TextFrame().setPayload(msg));
 
             // Read frame (hopefully text frame)
-            EventQueue<WebSocketFrame> frames = client.readFrames(1, 500, TimeUnit.MILLISECONDS);
+            EventQueue<WebSocketFrame> frames = client.readFrames(1, 30, TimeUnit.SECONDS);
             WebSocketFrame tf = frames.poll();
             Assert.assertThat("Text Frame.status code", tf.getPayloadAsUTF8(), is(msg));
         }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FragmentExtensionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FragmentExtensionTest.java
index e09a467..3da69ab 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FragmentExtensionTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FragmentExtensionTest.java
@@ -18,7 +18,8 @@
 
 package org.eclipse.jetty.websocket.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
 
 import java.util.concurrent.TimeUnit;
 
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdentityExtensionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdentityExtensionTest.java
index bc6d1c7..57ddc79 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdentityExtensionTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdentityExtensionTest.java
@@ -18,7 +18,8 @@
 
 package org.eclipse.jetty.websocket.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
 
 import java.util.concurrent.TimeUnit;
 
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java
index 49d75a1..90871f4 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java
@@ -18,7 +18,8 @@
 
 package org.eclipse.jetty.websocket.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
 
 import java.util.concurrent.TimeUnit;
 
@@ -67,6 +68,7 @@
 
     /**
      * Test IdleTimeout on server.
+     * @throws Exception on test failure
      */
     @Test
     public void testIdleTimeout() throws Exception
@@ -92,7 +94,7 @@
             client.write(new TextFrame().setPayload("Hello"));
 
             // Expect server to have closed due to its own timeout
-            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
             WebSocketFrame frame = frames.poll();
             Assert.assertThat("frame opcode",frame.getOpCode(),is(OpCode.CLOSE));
             CloseInfo close = new CloseInfo(frame);
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/InfoContextAltAttributeListener.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/InfoContextAltAttributeListener.java
new file mode 100644
index 0000000..e874163
--- /dev/null
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/InfoContextAltAttributeListener.java
@@ -0,0 +1,51 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.server;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
+import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
+import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
+
+public class InfoContextAltAttributeListener implements WebSocketCreator, ServletContextListener
+{
+    private static final String ATTR = "alt.config";
+    
+    @Override
+    public void contextInitialized(ServletContextEvent sce)
+    {
+        NativeWebSocketConfiguration configuration = new NativeWebSocketConfiguration(sce.getServletContext());
+        configuration.getFactory().getPolicy().setMaxTextMessageSize(10 * 1024 * 1024);
+        configuration.addMapping("/info/*", this);
+        sce.getServletContext().setAttribute(ATTR, configuration);
+    }
+    
+    @Override
+    public void contextDestroyed(ServletContextEvent sce)
+    {
+    }
+    
+    @Override
+    public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
+    {
+        return new InfoSocket();
+    }
+}
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/InfoContextListener.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/InfoContextListener.java
index b303761..cbe8c51 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/InfoContextListener.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/InfoContextListener.java
@@ -30,7 +30,7 @@
     @Override
     public void contextInitialized(ServletContextEvent sce)
     {
-        NativeWebSocketConfiguration configuration = new NativeWebSocketConfiguration();
+        NativeWebSocketConfiguration configuration = new NativeWebSocketConfiguration(sce.getServletContext());
         configuration.getFactory().getPolicy().setMaxTextMessageSize(10 * 1024 * 1024);
         configuration.addMapping("/info/*", this);
         sce.getServletContext().setAttribute(NativeWebSocketConfiguration.class.getName(), configuration);
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ManyConnectionsCleanupTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ManyConnectionsCleanupTest.java
new file mode 100644
index 0000000..2492755
--- /dev/null
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ManyConnectionsCleanupTest.java
@@ -0,0 +1,369 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.server;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jetty.toolchain.test.EventQueue;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.StatusCode;
+import org.eclipse.jetty.websocket.api.WebSocketAdapter;
+import org.eclipse.jetty.websocket.common.CloseInfo;
+import org.eclipse.jetty.websocket.common.OpCode;
+import org.eclipse.jetty.websocket.common.WebSocketFrame;
+import org.eclipse.jetty.websocket.common.WebSocketSession;
+import org.eclipse.jetty.websocket.common.frames.TextFrame;
+import org.eclipse.jetty.websocket.common.test.BlockheadClient;
+import org.eclipse.jetty.websocket.common.test.IBlockheadClient;
+import org.eclipse.jetty.websocket.server.helper.RFCSocket;
+import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
+import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
+import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
+import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
+import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Tests various close scenarios that should result in Open Session cleanup
+ */
+@Ignore
+public class ManyConnectionsCleanupTest
+{
+    static class AbstractCloseSocket extends WebSocketAdapter
+    {
+        public CountDownLatch closeLatch = new CountDownLatch(1);
+        public String closeReason = null;
+        public int closeStatusCode = -1;
+        public List<Throwable> errors = new ArrayList<>();
+
+        @Override
+        public void onWebSocketClose(int statusCode, String reason)
+        {
+            LOG.debug("onWebSocketClose({}, {})",statusCode,reason);
+            this.closeStatusCode = statusCode;
+            this.closeReason = reason;
+            closeLatch.countDown();
+        }
+
+        @Override
+        public void onWebSocketError(Throwable cause)
+        {
+            errors.add(cause);
+        }
+    }
+
+    @SuppressWarnings("serial")
+    public static class CloseServlet extends WebSocketServlet implements WebSocketCreator
+    {
+        private WebSocketServerFactory serverFactory;
+        private AtomicInteger calls = new AtomicInteger(0);
+
+        @Override
+        public void configure(WebSocketServletFactory factory)
+        {
+            factory.setCreator(this);
+            if (factory instanceof WebSocketServerFactory)
+            {
+                this.serverFactory = (WebSocketServerFactory)factory;
+            }
+        }
+
+        @Override
+        public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
+        {
+            if (req.hasSubProtocol("fastclose"))
+            {
+                closeSocket = new FastCloseSocket(calls);
+                return closeSocket;
+            }
+
+            if (req.hasSubProtocol("fastfail"))
+            {
+                closeSocket = new FastFailSocket(calls);
+                return closeSocket;
+            }
+
+            if (req.hasSubProtocol("container"))
+            {
+                closeSocket = new ContainerSocket(serverFactory,calls);
+                return closeSocket;
+            }
+            return new RFCSocket();
+        }
+    }
+
+    /**
+     * On Message, return container information
+     */
+    public static class ContainerSocket extends AbstractCloseSocket
+    {
+        private static final Logger LOG = Log.getLogger(ManyConnectionsCleanupTest.ContainerSocket.class);
+        private final WebSocketServerFactory container;
+        private final AtomicInteger calls;
+        private Session session;
+
+        public ContainerSocket(WebSocketServerFactory container, AtomicInteger calls)
+        {
+            this.container = container;
+            this.calls = calls;
+        }
+
+        @Override
+        public void onWebSocketText(String message)
+        {
+            LOG.debug("onWebSocketText({})",message);
+            calls.incrementAndGet();
+            if (message.equalsIgnoreCase("openSessions"))
+            {
+                Collection<WebSocketSession> sessions = container.getOpenSessions();
+
+                StringBuilder ret = new StringBuilder();
+                ret.append("openSessions.size=").append(sessions.size()).append('\n');
+                int idx = 0;
+                for (WebSocketSession sess : sessions)
+                {
+                    ret.append('[').append(idx++).append("] ").append(sess.toString()).append('\n');
+                }
+                session.getRemote().sendStringByFuture(ret.toString());
+                session.close(StatusCode.NORMAL,"ContainerSocket");
+            } else if(message.equalsIgnoreCase("calls"))
+            {
+                session.getRemote().sendStringByFuture(String.format("calls=%,d",calls.get()));
+            }
+        }
+
+        @Override
+        public void onWebSocketConnect(Session sess)
+        {
+            LOG.debug("onWebSocketConnect({})",sess);
+            this.session = sess;
+        }
+    }
+
+    /**
+     * On Connect, close socket
+     */
+    public static class FastCloseSocket extends AbstractCloseSocket
+    {
+        private static final Logger LOG = Log.getLogger(ManyConnectionsCleanupTest.FastCloseSocket.class);
+        private final AtomicInteger calls;
+
+        public FastCloseSocket(AtomicInteger calls)
+        {
+            this.calls = calls;
+        }
+
+        @Override
+        public void onWebSocketConnect(Session sess)
+        {
+            LOG.debug("onWebSocketConnect({})",sess);
+            calls.incrementAndGet();
+            sess.close(StatusCode.NORMAL,"FastCloseServer");
+        }
+    }
+
+    /**
+     * On Connect, throw unhandled exception
+     */
+    public static class FastFailSocket extends AbstractCloseSocket
+    {
+        private static final Logger LOG = Log.getLogger(ManyConnectionsCleanupTest.FastFailSocket.class);
+        private final AtomicInteger calls;
+
+        public FastFailSocket(AtomicInteger calls)
+        {
+            this.calls = calls;
+        }
+
+        @Override
+        public void onWebSocketConnect(Session sess)
+        {
+            LOG.debug("onWebSocketConnect({})",sess);
+            calls.incrementAndGet();
+            // Test failure due to unhandled exception
+            // this should trigger a fast-fail closure during open/connect
+            throw new RuntimeException("Intentional FastFail");
+        }
+    }
+
+    private static final Logger LOG = Log.getLogger(ManyConnectionsCleanupTest.class);
+
+    private static SimpleServletServer server;
+    private static AbstractCloseSocket closeSocket;
+
+    @BeforeClass
+    public static void startServer() throws Exception
+    {
+        server = new SimpleServletServer(new CloseServlet());
+        server.start();
+    }
+
+    @AfterClass
+    public static void stopServer()
+    {
+        server.stop();
+    }
+
+    /**
+     * Test session open session cleanup (bug #474936)
+     * 
+     * @throws Exception
+     *             on test failure
+     */
+    @Test
+    public void testOpenSessionCleanup() throws Exception
+    {
+        int iterationCount = 100;
+        
+        StdErrLog.getLogger(FastFailSocket.class).setLevel(StdErrLog.LEVEL_OFF);
+        
+        StdErrLog sessLog = StdErrLog.getLogger(WebSocketSession.class);
+        int oldLevel = sessLog.getLevel();
+        sessLog.setLevel(StdErrLog.LEVEL_OFF);
+        
+        for (int requests = 0; requests < iterationCount; requests++)
+        {
+            fastFail();
+            fastClose();
+            dropConnection();
+        }
+        
+        sessLog.setLevel(oldLevel);
+
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
+        {
+            client.setProtocols("container");
+            client.setTimeout(1,TimeUnit.SECONDS);
+            client.connect();
+            client.sendStandardRequest();
+            client.expectUpgradeResponse();
+            
+            client.write(new TextFrame().setPayload("calls"));
+            client.write(new TextFrame().setPayload("openSessions"));
+
+            EventQueue<WebSocketFrame> frames = client.readFrames(3,6,TimeUnit.SECONDS);
+            WebSocketFrame frame;
+            String resp;
+            
+            frame = frames.poll();
+            assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.TEXT));
+            resp = frame.getPayloadAsUTF8();
+            assertThat("Should only have 1 open session",resp,containsString("calls=" + ((iterationCount * 2) + 1)));
+
+            frame = frames.poll();
+            assertThat("frames[1].opcode",frame.getOpCode(),is(OpCode.TEXT));
+            resp = frame.getPayloadAsUTF8();
+            assertThat("Should only have 1 open session",resp,containsString("openSessions.size=1\n"));
+
+            frame = frames.poll();
+            assertThat("frames[2].opcode",frame.getOpCode(),is(OpCode.CLOSE));
+            CloseInfo close = new CloseInfo(frame);
+            assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.NORMAL));
+            client.write(close.asFrame()); // respond with close
+
+            // ensure server socket got close event
+            assertThat("Open Sessions Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
+            assertThat("Open Sessions.statusCode",closeSocket.closeStatusCode,is(StatusCode.NORMAL));
+            assertThat("Open Sessions.errors",closeSocket.errors.size(),is(0));
+        }
+    }
+
+    private void fastClose() throws Exception
+    {
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
+        {
+            client.setProtocols("fastclose");
+            client.setTimeout(1,TimeUnit.SECONDS);
+            try (StacklessLogging scope = new StacklessLogging(WebSocketSession.class))
+            {
+                client.connect();
+                client.sendStandardRequest();
+                client.expectUpgradeResponse();
+                
+                client.readFrames(1,1,TimeUnit.SECONDS);
+
+                CloseInfo close = new CloseInfo(StatusCode.NORMAL,"Normal");
+                assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.NORMAL));
+
+                // Notify server of close handshake
+                client.write(close.asFrame()); // respond with close
+
+                // ensure server socket got close event
+                assertThat("Fast Close Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
+                assertThat("Fast Close.statusCode",closeSocket.closeStatusCode,is(StatusCode.NORMAL));
+            }
+        }
+    }
+
+    private void fastFail() throws Exception
+    {
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
+        {
+            client.setProtocols("fastfail");
+            client.setTimeout(1,TimeUnit.SECONDS);
+            try (StacklessLogging scope = new StacklessLogging(WebSocketSession.class))
+            {
+                client.connect();
+                client.sendStandardRequest();
+                client.expectUpgradeResponse();
+                
+                // client.readFrames(1,2,TimeUnit.SECONDS);
+
+                CloseInfo close = new CloseInfo(StatusCode.NORMAL,"Normal");
+                client.write(close.asFrame()); // respond with close
+
+                // ensure server socket got close event
+                assertThat("Fast Fail Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
+                assertThat("Fast Fail.statusCode",closeSocket.closeStatusCode,is(StatusCode.SERVER_ERROR));
+                assertThat("Fast Fail.errors",closeSocket.errors.size(),is(1));
+            }
+        }
+    }
+    
+    private void dropConnection() throws Exception
+    {
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
+        {
+            client.setProtocols("container");
+            client.setTimeout(1,TimeUnit.SECONDS);
+            try (StacklessLogging scope = new StacklessLogging(WebSocketSession.class))
+            {
+                client.connect();
+                client.sendStandardRequest();
+                client.expectUpgradeResponse();
+                client.disconnect();
+            }
+        }
+    }
+}
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/PerMessageDeflateExtensionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/PerMessageDeflateExtensionTest.java
index 66e7171..403b97b 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/PerMessageDeflateExtensionTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/PerMessageDeflateExtensionTest.java
@@ -18,41 +18,99 @@
 
 package org.eclipse.jetty.websocket.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
 
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
-import org.eclipse.jetty.toolchain.test.EventQueue;
-import org.eclipse.jetty.websocket.common.WebSocketFrame;
-import org.eclipse.jetty.websocket.common.frames.TextFrame;
-import org.eclipse.jetty.websocket.common.test.BlockheadClient;
-import org.eclipse.jetty.websocket.common.test.HttpResponse;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
+import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
+import org.eclipse.jetty.websocket.common.util.Sha1Sum;
+import org.eclipse.jetty.websocket.server.helper.CaptureSocket;
 import org.eclipse.jetty.websocket.server.helper.EchoServlet;
-import org.junit.AfterClass;
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Assume;
-import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class PerMessageDeflateExtensionTest
 {
-    private static SimpleServletServer server;
-
-    @BeforeClass
-    public static void startServer() throws Exception
+    private static enum TestCaseMessageSize
     {
-        server = new SimpleServletServer(new EchoServlet());
-        server.start();
+        TINY(10),
+        SMALL(1024),
+        MEDIUM(10*1024),
+        LARGE(100*1024),
+        HUGE(1024*1024);
+
+        private int size;
+
+        private TestCaseMessageSize(int size)
+        {
+            this.size = size;
+        }
     }
 
-    @AfterClass
-    public static void stopServer()
+    @Parameters(name = "{0} ({3}) (Input Buffer Size: {4} bytes)")
+    public static List<Object[]> modes()
+    {
+        List<Object[]> modes = new ArrayList<>();
+
+        for(TestCaseMessageSize size: TestCaseMessageSize.values())
+        {
+            modes.add(new Object[] { "Normal HTTP/WS", false, "ws", size, -1 });
+            modes.add(new Object[] { "Encrypted HTTPS/WSS", true, "wss", size, -1 });
+            int altInputBufSize = 15*1024;
+            modes.add(new Object[] { "Normal HTTP/WS", false, "ws", size, altInputBufSize });
+            modes.add(new Object[] { "Encrypted HTTPS/WSS", true, "wss", size, altInputBufSize });
+        }
+
+        return modes;
+    }
+
+    @Rule
+    public LeakTrackingBufferPoolRule bufferPool = new LeakTrackingBufferPoolRule("Test");
+
+    private SimpleServletServer server;
+    private String scheme;
+    private int msgSize;
+    private int inputBufferSize;
+
+    public PerMessageDeflateExtensionTest(String mode, boolean sslMode, String scheme, TestCaseMessageSize msgSize, int bufferSize) throws Exception
+    {
+        server = new SimpleServletServer(new EchoServlet());
+        server.enableSsl(sslMode);
+        server.start();
+
+        this.scheme = scheme;
+        this.msgSize = msgSize.size;
+        this.inputBufferSize = bufferSize;
+    }
+
+    @After
+    public void stopServer()
     {
         server.stop();
     }
 
     /**
      * Default configuration for permessage-deflate
+     * @throws Exception on test failure
      */
     @Test
     public void testPerMessageDeflateDefault() throws Exception
@@ -60,42 +118,84 @@
         Assume.assumeTrue("Server has permessage-deflate registered",
                 server.getWebSocketServletFactory().getExtensionFactory().isAvailable("permessage-deflate"));
 
-        BlockheadClient client = new BlockheadClient(server.getServerUri());
-        client.clearExtensions();
-        client.addExtensions("permessage-deflate");
-        client.setProtocols("echo");
+        Assert.assertThat("server scheme",server.getServerUri().getScheme(),is(scheme));
+
+        int binBufferSize = (int) (msgSize * 1.5);
+
+        WebSocketPolicy serverPolicy = server.getWebSocketServletFactory().getPolicy();
+
+        // Ensure binBufferSize is sane (not smaller then other buffers)
+        binBufferSize = Math.max(binBufferSize,serverPolicy.getMaxBinaryMessageSize());
+        binBufferSize = Math.max(binBufferSize,serverPolicy.getMaxBinaryMessageBufferSize());
+        binBufferSize = Math.max(binBufferSize,this.inputBufferSize);
+
+        serverPolicy.setMaxBinaryMessageSize(binBufferSize);
+        serverPolicy.setMaxBinaryMessageBufferSize(binBufferSize);
+
+        WebSocketClient client = new WebSocketClient(server.getSslContextFactory(),null,bufferPool);
+        WebSocketPolicy clientPolicy = client.getPolicy();
+        clientPolicy.setMaxBinaryMessageSize(binBufferSize);
+        clientPolicy.setMaxBinaryMessageBufferSize(binBufferSize);
+        if (inputBufferSize > 0)
+        {
+            clientPolicy.setInputBufferSize(inputBufferSize);
+        }
 
         try
         {
+            client.start();
             // Make sure the read times out if there are problems with the implementation
-            client.setTimeout(1,TimeUnit.SECONDS);
-            client.connect();
-            client.sendStandardRequest();
-            HttpResponse resp = client.expectUpgradeResponse();
+            client.setMaxIdleTimeout(TimeUnit.SECONDS.toMillis(15));
 
-            Assert.assertThat("Response",resp.getExtensionsHeader(),containsString("permessage-deflate"));
+            CaptureSocket clientSocket = new CaptureSocket();
+            ClientUpgradeRequest request = new ClientUpgradeRequest();
+            request.addExtensions("permessage-deflate");
+            request.setSubProtocols("echo");
 
-            String msg = "Hello";
+            Future<Session> fut = client.connect(clientSocket,server.getServerUri(),request);
+
+            // Wait for connect
+            Session session = fut.get(30,TimeUnit.SECONDS);
+
+            assertThat("Response.extensions",getNegotiatedExtensionList(session),containsString("permessage-deflate"));
+
+            // Create message
+            byte msg[] = new byte[msgSize];
+            Random rand = new Random();
+            rand.setSeed(8080);
+            rand.nextBytes(msg);
+
+            // Calculate sha1
+            String sha1 = Sha1Sum.calculate(msg);
 
             // Client sends first message
-            client.write(new TextFrame().setPayload(msg));
+            session.getRemote().sendBytes(ByteBuffer.wrap(msg));
 
-            EventQueue<WebSocketFrame> frames = client.readFrames(1,1000,TimeUnit.MILLISECONDS);
-            WebSocketFrame frame = frames.poll();
-            Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString()));
-
-            // Client sends second message
-            client.clearCaptured();
-            msg = "There";
-            client.write(new TextFrame().setPayload(msg));
-
-            frames = client.readFrames(1,1,TimeUnit.SECONDS);
-            frame = frames.poll();
-            Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString()));
+            clientSocket.messages.awaitEventCount(1,5,TimeUnit.SECONDS);
+            String echoMsg = clientSocket.messages.poll();
+            Assert.assertThat("Echo'd Message",echoMsg,is("binary[sha1="+sha1+"]"));
         }
         finally
         {
-            client.close();
+            client.stop();
         }
     }
+
+    private String getNegotiatedExtensionList(Session session)
+    {
+        StringBuilder actual = new StringBuilder();
+        actual.append('[');
+
+        boolean delim = false;
+        for (ExtensionConfig ext : session.getUpgradeResponse().getExtensions())
+        {
+            if (delim)
+                actual.append(", ");
+            actual.append(ext.getName());
+            delim = true;
+        }
+        actual.append(']');
+
+        return actual.toString();
+    }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java
index 4250a1d..662dcfa 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java
@@ -18,7 +18,9 @@
 
 package org.eclipse.jetty.websocket.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
 
 import java.net.HttpCookie;
 import java.util.List;
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/SimpleServletServer.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/SimpleServletServer.java
index 9d48612..e572857 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/SimpleServletServer.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/SimpleServletServer.java
@@ -22,6 +22,7 @@
 
 import javax.servlet.http.HttpServlet;
 
+import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.SecureRequestCustomizer;
@@ -100,7 +101,7 @@
             https_config.addCustomizer(new SecureRequestCustomizer());
 
             // SSL Connector
-            connector = new ServerConnector(server,new SslConnectionFactory(sslContextFactory,"http/1.1"),new HttpConnectionFactory(https_config));
+            connector = new ServerConnector(server,new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()),new HttpConnectionFactory(https_config));
             connector.setPort(0);
         }
         else
@@ -113,6 +114,7 @@
 
         ServletContextHandler context = new ServletContextHandler();
         context.setContextPath("/");
+        configureServletContextHandler(context);
         server.setHandler(context);
 
         // Serve capture servlet
@@ -137,6 +139,10 @@
         }
     }
 
+    protected void configureServletContextHandler(ServletContextHandler context)
+    {
+    }
+
     public void stop()
     {
         try
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/TooFastClientTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/TooFastClientTest.java
index f805c21..8337ddc 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/TooFastClientTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/TooFastClientTest.java
@@ -18,12 +18,15 @@
 
 package org.eclipse.jetty.websocket.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.io.LeakTrackingByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
 import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
@@ -36,8 +39,8 @@
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
+import org.junit.Ignore;
 
 /**
  * Test simulating a client that talks too quickly.
@@ -45,7 +48,6 @@
  * There is a class of client that will send the GET+Upgrade Request along with a few websocket frames in a single
  * network packet. This test attempts to perform this behavior as close as possible.
  */
-@Ignore
 public class TooFastClientTest
 {
     private static SimpleServletServer server;
@@ -64,7 +66,8 @@
     }
 
     @Test
-    public void testUpgradeWithWebkitDeflateExtension() throws Exception
+    @Ignore("RELEASE")
+    public void testUpgradeWithSmallFrames() throws Exception
     {
         BlockheadClient client = new BlockheadClient(server.getServerUri());
         try
@@ -83,11 +86,20 @@
             // Add text frames
             Generator generator = new Generator(WebSocketPolicy.newClientPolicy(),
                     new LeakTrackingBufferPoolRule("Generator"));
+            
             String msg1 = "Echo 1";
             String msg2 = "This is also an echooooo!";
             
-            generator.generateWholeFrame(new TextFrame().setPayload(msg1),initialPacket);
-            generator.generateWholeFrame(new TextFrame().setPayload(msg2),initialPacket);
+            TextFrame frame1 = new TextFrame().setPayload(msg1);
+            TextFrame frame2 = new TextFrame().setPayload(msg2);
+            
+            // Need to set frame mask (as these are client frames)
+            byte mask[] = new byte[] { 0x11, 0x22, 0x33, 0x44 };
+            frame1.setMask(mask);
+            frame2.setMask(mask);
+
+            generator.generateWholeFrame(frame1,initialPacket);
+            generator.generateWholeFrame(frame2,initialPacket);
 
             // Write packet to network
             BufferUtil.flipToFlush(initialPacket,0);
@@ -108,4 +120,64 @@
             client.close();
         }
     }
+    
+    /**
+     * Test where were a client sends a HTTP Upgrade to websocket AND enough websocket frame(s)
+     * to completely overfill the {@link org.eclipse.jetty.io.AbstractConnection#getInputBufferSize()}
+     * to test a situation where the WebSocket connection opens with prefill that exceeds 
+     * the normal input buffer sizes.
+     * @throws Exception on test failure
+     */
+    @Test
+    @Ignore("RELEASE")
+    public void testUpgradeWithLargeFrame() throws Exception
+    {
+        BlockheadClient client = new BlockheadClient(server.getServerUri());
+        try
+        {
+            client.connect();
+
+            // Create ByteBuffer representing the initial opening network packet from the client
+            ByteBuffer initialPacket = ByteBuffer.allocate(100 * 1024);
+            BufferUtil.clearToFill(initialPacket);
+
+            // Add upgrade request to packet
+            StringBuilder upgradeRequest = client.generateUpgradeRequest();
+            ByteBuffer upgradeBuffer = BufferUtil.toBuffer(upgradeRequest.toString(),StandardCharsets.UTF_8);
+            initialPacket.put(upgradeBuffer);
+
+            // Add text frames
+            Generator generator = new Generator(WebSocketPolicy.newClientPolicy(),
+                    new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()));
+
+            byte bigMsgBytes[] = new byte[64*1024];
+            Arrays.fill(bigMsgBytes,(byte)'x');
+            String bigMsg = new String(bigMsgBytes, StandardCharsets.UTF_8);
+            
+            // Need to set frame mask (as these are client frames)
+            byte mask[] = new byte[] { 0x11, 0x22, 0x33, 0x44 };
+            TextFrame frame = new TextFrame().setPayload(bigMsg);
+            frame.setMask(mask);
+            generator.generateWholeFrame(frame,initialPacket);
+
+            // Write packet to network
+            BufferUtil.flipToFlush(initialPacket,0);
+            client.writeRaw(initialPacket);
+
+            // Expect upgrade
+            client.expectUpgradeResponse();
+
+            // Read frames (hopefully text frames)
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
+
+            WebSocketFrame tf = frames.poll();
+            Assert.assertThat("Text Frame/msg1",tf.getPayloadAsUTF8(),is(bigMsg));
+        }
+        finally
+        {
+            client.close();
+        }
+    }
+    
+    
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WSServer.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WSServer.java
index 5057f31..02a7fe4 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WSServer.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WSServer.java
@@ -18,12 +18,15 @@
 
 package org.eclipse.jetty.websocket.server;
 
+import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
 
 import java.io.File;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 
 import org.eclipse.jetty.annotations.AnnotationConfiguration;
@@ -35,6 +38,7 @@
 import org.eclipse.jetty.server.handler.HandlerCollection;
 import org.eclipse.jetty.toolchain.test.FS;
 import org.eclipse.jetty.toolchain.test.IO;
+import org.eclipse.jetty.toolchain.test.JAR;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.toolchain.test.OS;
 import org.eclipse.jetty.toolchain.test.TestingDir;
@@ -47,7 +51,6 @@
 import org.eclipse.jetty.webapp.WebAppContext;
 import org.eclipse.jetty.webapp.WebInfConfiguration;
 import org.eclipse.jetty.webapp.WebXmlConfiguration;
-import org.junit.Assert;
 
 /**
  * Utility to build out exploded directory WebApps, in the /target/tests/ directory, for testing out servers that use javax.websocket endpoints.
@@ -67,7 +70,7 @@
 
     public WSServer(TestingDir testdir, String contextName)
     {
-        this(testdir.getDir(),contextName);
+        this(testdir.getPath().toFile(),contextName);
     }
 
     public WSServer(File testdir, String contextName)
@@ -82,7 +85,7 @@
         ClassLoader cl = Thread.currentThread().getContextClassLoader();
         String endpointPath = clazz.getName().replace('.','/') + ".class";
         URL classUrl = cl.getResource(endpointPath);
-        Assert.assertThat("Class URL for: " + clazz,classUrl,notNullValue());
+        assertThat("Class URL for: " + clazz,classUrl,notNullValue());
         File destFile = new File(classesDir,OS.separators(endpointPath));
         FS.ensureDirExists(destFile.getParentFile());
         File srcFile = new File(classUrl.toURI());
@@ -93,7 +96,31 @@
     {
         copyClass(endpointClass);
     }
-
+    
+    public void copyLib(Class<?> clazz, String jarFileName) throws URISyntaxException, IOException
+    {
+        webinf = new File(contextDir,"WEB-INF");
+        FS.ensureDirExists(webinf);
+        File libDir = new File(webinf,"lib");
+        FS.ensureDirExists(libDir);
+        File jarFile = new File(libDir, jarFileName);
+        
+        URL codeSourceURL = clazz.getProtectionDomain().getCodeSource().getLocation();
+        assertThat("Class CodeSource URL is file scheme", codeSourceURL.getProtocol(), is("file"));
+    
+        File sourceCodeSourceFile = new File(codeSourceURL.toURI());
+        if (sourceCodeSourceFile.isDirectory())
+        {
+            LOG.info("Creating " + jarFile + " from " + sourceCodeSourceFile);
+            JAR.create(sourceCodeSourceFile, jarFile);
+        }
+        else
+        {
+            LOG.info("Copying " + sourceCodeSourceFile + " to " + jarFile);
+            IO.copy(sourceCodeSourceFile, jarFile);
+        }
+    }
+    
     public void copyWebInf(String testResourceName) throws IOException
     {
         webinf = new File(contextDir,"WEB-INF");
@@ -173,7 +200,7 @@
         contexts = new ContextHandlerCollection();
         handlers.addHandler(contexts);
         server.setHandler(handlers);
-
+        
         server.start();
 
         String host = connector.getHost();
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java
index 50033c0..752b2f8 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java
@@ -18,9 +18,13 @@
 
 package org.eclipse.jetty.websocket.server;
 
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
-import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -35,9 +39,9 @@
 import org.eclipse.jetty.websocket.common.OpCode;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.WebSocketSession;
-import org.eclipse.jetty.websocket.common.events.AbstractEventDriver;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
+import org.eclipse.jetty.websocket.common.test.IBlockheadClient;
 import org.eclipse.jetty.websocket.server.helper.RFCSocket;
 import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
 import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
@@ -48,10 +52,6 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
 /**
  * Tests various close scenarios
  */
@@ -139,7 +139,7 @@
             LOG.debug("onWebSocketText({})",message);
             if (message.equalsIgnoreCase("openSessions"))
             {
-                Set<WebSocketSession> sessions = container.getOpenSessions();
+                Collection<WebSocketSession> sessions = container.getOpenSessions();
 
                 StringBuilder ret = new StringBuilder();
                 ret.append("openSessions.size=").append(sessions.size()).append('\n');
@@ -220,7 +220,7 @@
     @Test
     public void testFastClose() throws Exception
     {
-        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
         {
             client.setProtocols("fastclose");
             client.setTimeout(5,TimeUnit.SECONDS);
@@ -253,11 +253,11 @@
     @Test
     public void testFastFail() throws Exception
     {
-        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
         {
             client.setProtocols("fastfail");
             client.setTimeout(5,TimeUnit.SECONDS);
-            try (StacklessLogging scope = new StacklessLogging(AbstractEventDriver.class))
+            try (StacklessLogging scope = new StacklessLogging(FastFailSocket.class, WebSocketSession.class))
             {
                 client.connect();
                 client.sendStandardRequest();
@@ -292,7 +292,7 @@
         fastClose();
         dropConnection();
 
-        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
         {
             client.setProtocols("container");
             client.setTimeout(1,TimeUnit.SECONDS);
@@ -326,7 +326,7 @@
 
     private void fastClose() throws Exception
     {
-        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
         {
             client.setProtocols("fastclose");
             client.setTimeout(1,TimeUnit.SECONDS);
@@ -353,7 +353,7 @@
 
     private void fastFail() throws Exception
     {
-        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
         {
             client.setProtocols("fastfail");
             client.setTimeout(1,TimeUnit.SECONDS);
@@ -378,7 +378,7 @@
     
     private void dropConnection() throws Exception
     {
-        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
         {
             client.setProtocols("container");
             client.setTimeout(1,TimeUnit.SECONDS);
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketInvalidVersionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketInvalidVersionTest.java
index 6c9a497..8594df8 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketInvalidVersionTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketInvalidVersionTest.java
@@ -18,7 +18,8 @@
 
 package org.eclipse.jetty.websocket.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
 
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
 import org.eclipse.jetty.websocket.common.test.HttpResponse;
@@ -47,6 +48,7 @@
 
     /**
      * Test the requirement of responding with an http 400 when using a Sec-WebSocket-Version that is unsupported.
+     * @throws Exception on test failure
      */
     @Test
     public void testRequestVersion29() throws Exception
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java
index e4cb8a9..bb89a7e 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.server;
 
+import static org.hamcrest.Matchers.is;
+
 import java.net.URI;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -37,10 +39,10 @@
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.is;
-
 public class WebSocketOverSSLTest
 {
+    public static final int CONNECT_TIMEOUT = 15000;
+    public static final int FUTURE_TIMEOUT_SEC = 30;
     @Rule
     public TestTracker tracker = new TestTracker();
     
@@ -65,6 +67,7 @@
 
     /**
      * Test the requirement of issuing socket and receiving echo response
+     * @throws Exception on test failure
      */
     @Test
     public void testEcho() throws Exception
@@ -81,7 +84,7 @@
             Future<Session> fut = client.connect(clientSocket,requestUri);
 
             // wait for connect
-            Session session = fut.get(3,TimeUnit.SECONDS);
+            Session session = fut.get(FUTURE_TIMEOUT_SEC,TimeUnit.SECONDS);
 
             // Generate text frame
             String msg = "this is an echo ... cho ... ho ... o";
@@ -91,7 +94,7 @@
                 remote.flush();
 
             // Read frame (hopefully text frame)
-            clientSocket.messages.awaitEventCount(1,500,TimeUnit.MILLISECONDS);
+            clientSocket.messages.awaitEventCount(1,30,TimeUnit.SECONDS);
             EventQueue<String> captured = clientSocket.messages;
             Assert.assertThat("Text Message",captured.poll(),is(msg));
 
@@ -106,6 +109,7 @@
 
     /**
      * Test that server session reports as secure
+     * @throws Exception on test failure
      */
     @Test
     public void testServerSessionIsSecure() throws Exception
@@ -114,7 +118,7 @@
         WebSocketClient client = new WebSocketClient(server.getSslContextFactory(),null,bufferPool);
         try
         {
-            client.setConnectTimeout(3000);
+            client.setConnectTimeout(CONNECT_TIMEOUT);
             client.start();
 
             CaptureSocket clientSocket = new CaptureSocket();
@@ -123,7 +127,7 @@
             Future<Session> fut = client.connect(clientSocket,requestUri);
 
             // wait for connect
-            Session session = fut.get(5,TimeUnit.SECONDS);
+            Session session = fut.get(FUTURE_TIMEOUT_SEC,TimeUnit.SECONDS);
 
             // Generate text frame
             RemoteEndpoint remote = session.getRemote();
@@ -132,7 +136,7 @@
                 remote.flush();
 
             // Read frame (hopefully text frame)
-            clientSocket.messages.awaitEventCount(1,500,TimeUnit.MILLISECONDS);
+            clientSocket.messages.awaitEventCount(1,30,TimeUnit.SECONDS);
             EventQueue<String> captured = clientSocket.messages;
             Assert.assertThat("Server.session.isSecure",captured.poll(),is("session.isSecure=true"));
 
@@ -147,6 +151,7 @@
 
     /**
      * Test that server session.upgradeRequest.requestURI reports correctly
+     * @throws Exception on test failure
      */
     @Test
     public void testServerSessionRequestURI() throws Exception
@@ -155,7 +160,7 @@
         WebSocketClient client = new WebSocketClient(server.getSslContextFactory(),null,bufferPool);
         try
         {
-            client.setConnectTimeout(3000);
+            client.setConnectTimeout(CONNECT_TIMEOUT);
             client.start();
 
             CaptureSocket clientSocket = new CaptureSocket();
@@ -164,7 +169,7 @@
             Future<Session> fut = client.connect(clientSocket,requestUri);
 
             // wait for connect
-            Session session = fut.get(5,TimeUnit.SECONDS);
+            Session session = fut.get(FUTURE_TIMEOUT_SEC,TimeUnit.SECONDS);
 
             // Generate text frame
             RemoteEndpoint remote = session.getRemote();
@@ -173,7 +178,7 @@
                 remote.flush();
 
             // Read frame (hopefully text frame)
-            clientSocket.messages.awaitEventCount(1,500,TimeUnit.MILLISECONDS);
+            clientSocket.messages.awaitEventCount(1,30,TimeUnit.SECONDS);
             EventQueue<String> captured = clientSocket.messages;
             String expected = String.format("session.upgradeRequest.requestURI=%s",requestUri.toASCIIString());
             Assert.assertThat("session.upgradeRequest.requestURI",captured.poll(),is(expected));
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java
index ee1bd69..34f2f8c 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.net.URI;
 import java.util.concurrent.TimeUnit;
@@ -28,6 +28,7 @@
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
+import org.eclipse.jetty.websocket.common.test.IBlockheadClient;
 import org.eclipse.jetty.websocket.server.helper.SessionServlet;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -36,7 +37,7 @@
 import org.junit.runner.RunWith;
 
 /**
- * Testing various aspects of the server side support for WebSocket {@link Session}
+ * Testing various aspects of the server side support for WebSocket {@link org.eclipse.jetty.websocket.api.Session}
  */
 @RunWith(AdvancedRunner.class)
 public class WebSocketServerSessionTest
@@ -60,7 +61,7 @@
     public void testDisconnect() throws Exception
     {
         URI uri = server.getServerUri().resolve("/test/disconnect");
-        try (BlockheadClient client = new BlockheadClient(uri))
+        try (IBlockheadClient client = new BlockheadClient(uri))
         {
             client.connect();
             client.sendStandardRequest();
@@ -76,7 +77,7 @@
     public void testUpgradeRequestResponse() throws Exception
     {
         URI uri = server.getServerUri().resolve("/test?snack=cashews&amount=handful&brand=off");
-        try (BlockheadClient client = new BlockheadClient(uri))
+        try (IBlockheadClient client = new BlockheadClient(uri))
         {
             client.connect();
             client.sendStandardRequest();
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java
index a66901d..8b7c8e4 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java
@@ -18,7 +18,7 @@
 
 package org.eclipse.jetty.websocket.server;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
@@ -45,6 +45,7 @@
 import org.eclipse.jetty.websocket.common.test.UnitGenerator;
 import org.eclipse.jetty.websocket.common.util.Hex;
 import org.eclipse.jetty.websocket.server.helper.RFCServlet;
+import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.BeforeClass;
@@ -73,6 +74,12 @@
         server.stop();
     }
 
+    /**
+     * @param clazz the class to enable
+     * @param enabled true to enable the stack traces (or not)
+     * @deprecated use {@link StacklessLogging} in a try-with-resources block instead
+     */
+    @Deprecated
     private void enableStacks(Class<?> clazz, boolean enabled)
     {
         StdErrLog log = StdErrLog.getLogger(clazz);
@@ -81,6 +88,7 @@
 
     /**
      * Test that aggregation of binary frames into a single message occurs
+     * @throws Exception on test failure
      */
     @Test
     public void testBinaryAggregate() throws Exception
@@ -116,7 +124,7 @@
             client.write(bin); // write buf3 (fin=true)
 
             // Read frame echo'd back (hopefully a single binary frame)
-            EventQueue<WebSocketFrame> frames = client.readFrames(1,1000,TimeUnit.MILLISECONDS);
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
             Frame binmsg = frames.poll();
             int expectedSize = buf1.length + buf2.length + buf3.length;
             Assert.assertThat("BinaryFrame.payloadLength",binmsg.getPayloadLength(),is(expectedSize));
@@ -166,6 +174,7 @@
 
     /**
      * Test the requirement of issuing socket and receiving echo response
+     * @throws Exception on test failure
      */
     @Test
     public void testEcho() throws Exception
@@ -182,7 +191,7 @@
             client.write(new TextFrame().setPayload(msg));
 
             // Read frame (hopefully text frame)
-            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
             WebSocketFrame tf = frames.poll();
             Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
         }
@@ -195,15 +204,13 @@
     /**
      * Test the requirement of responding with server terminated close code 1011 when there is an unhandled (internal server error) being produced by the
      * WebSocket POJO.
+     * @throws Exception on test failure
      */
     @Test
     public void testInternalError() throws Exception
     {
-        // Disable Long Stacks from EventDriver (we know this test will throw an exception)
-        enableStacks(EventDriver.class,false);
-
-        BlockheadClient client = new BlockheadClient(server.getServerUri());
-        try
+        try (BlockheadClient client = new BlockheadClient(server.getServerUri());
+             StacklessLogging stackless=new StacklessLogging(EventDriver.class))
         {
             client.connect();
             client.sendStandardRequest();
@@ -215,24 +222,19 @@
                 client.write(new TextFrame().setPayload("CRASH"));
 
                 // Read frame (hopefully close frame)
-                EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+                EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
                 Frame cf = frames.poll();
                 CloseInfo close = new CloseInfo(cf);
                 Assert.assertThat("Close Frame.status code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
             }
         }
-        finally
-        {
-            // Reenable Long Stacks from EventDriver
-            enableStacks(EventDriver.class,true);
-            client.close();
-        }
     }
 
     /**
      * Test http://tools.ietf.org/html/rfc6455#section-4.1 where server side upgrade handling is supposed to be case insensitive.
      * <p>
      * This test will simulate a client requesting upgrade with all lowercase headers.
+     * @throws Exception on test failure
      */
     @Test
     public void testLowercaseUpgrade() throws Exception
@@ -261,7 +263,7 @@
             client.write(new TextFrame().setPayload(msg));
 
             // Read frame (hopefully text frame)
-            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
             WebSocketFrame tf = frames.poll();
             Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
         }
@@ -274,13 +276,10 @@
     @Test
     public void testTextNotUTF8() throws Exception
     {
-        // Disable Long Stacks from Parser (we know this test will throw an exception)
-        enableStacks(Parser.class,false);
-
-        BlockheadClient client = new BlockheadClient(server.getServerUri());
-        client.setProtocols("other");
-        try
+        try (StacklessLogging stackless=new StacklessLogging(Parser.class);
+             BlockheadClient client = new BlockheadClient(server.getServerUri()))
         {
+            client.setProtocols("other");
             client.connect();
             client.sendStandardRequest();
             client.expectUpgradeResponse();
@@ -294,24 +293,19 @@
             client.writeRaw(bbHeader);
             client.writeRaw(txt.getPayload());
 
-            EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
             WebSocketFrame frame = frames.poll();
             Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
             CloseInfo close = new CloseInfo(frame);
             Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.BAD_PAYLOAD));
         }
-        finally
-        {
-            // Reenable Long Stacks from Parser
-            enableStacks(Parser.class,true);
-            client.close();
-        }
     }
 
     /**
      * Test http://tools.ietf.org/html/rfc6455#section-4.1 where server side upgrade handling is supposed to be case insensitive.
      * <p>
      * This test will simulate a client requesting upgrade with all uppercase headers.
+     * @throws Exception on test failure
      */
     @Test
     public void testUppercaseUpgrade() throws Exception
@@ -340,7 +334,7 @@
             client.write(new TextFrame().setPayload(msg));
 
             // Read frame (hopefully text frame)
-            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
             WebSocketFrame tf = frames.poll();
             Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
         }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilterTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilterTest.java
index 4976cb5..da5c1ba 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilterTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilterTest.java
@@ -18,17 +18,23 @@
 
 package org.eclipse.jetty.websocket.server;
 
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
 import java.io.File;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.servlet.DispatcherType;
 
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
@@ -43,10 +49,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.junit.Assert.assertThat;
-
 @RunWith(Parameterized.class)
 public class WebSocketUpgradeFilterTest
 {
@@ -55,6 +57,13 @@
         Server newServer() throws Exception;
     }
     
+    private static AtomicInteger uniqTestDirId = new AtomicInteger(0);
+    
+    private static File getNewTestDir()
+    {
+        return MavenTestingUtils.getTargetTestingDir("WSUF-webxml-" + uniqTestDirId.getAndIncrement());
+    }
+    
     @Parameterized.Parameters(name = "{0}")
     public static List<Object[]> data()
     {
@@ -162,17 +171,47 @@
                 server.setHandler(context);
                 context.addFilter(WebSocketUpgradeFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
                 
-                NativeWebSocketConfiguration configuration = new NativeWebSocketConfiguration();
+                NativeWebSocketConfiguration configuration = new NativeWebSocketConfiguration(context.getServletContext());
                 configuration.getFactory().getPolicy().setMaxTextMessageSize(10 * 1024 * 1024);
                 configuration.addMapping("/info/*", infoCreator);
-                context.getServletContext().setAttribute(NativeWebSocketConfiguration.class.getName(), configuration);
+                context.setAttribute(NativeWebSocketConfiguration.class.getName(), configuration);
                 
                 server.start();
                 
                 return server;
             }
         }});
-        
+    
+        // Embedded WSUF, added as filter, apply app-ws configuration via wsuf constructor
+    
+        cases.add(new Object[]{"wsuf/addFilter/WSUF Constructor configure", new ServerProvider()
+        {
+            @Override
+            public Server newServer() throws Exception
+            {
+                Server server = new Server();
+                ServerConnector connector = new ServerConnector(server);
+                connector.setPort(0);
+                server.addConnector(connector);
+            
+                ServletContextHandler context = new ServletContextHandler();
+                context.setContextPath("/");
+                server.setHandler(context);
+            
+                NativeWebSocketConfiguration configuration = new NativeWebSocketConfiguration(context.getServletContext());
+                configuration.getFactory().getPolicy().setMaxTextMessageSize(10 * 1024 * 1024);
+                configuration.addMapping("/info/*", infoCreator);
+                context.addBean(configuration, true);
+            
+                FilterHolder wsufHolder = new FilterHolder(new WebSocketUpgradeFilter(configuration));
+                context.addFilter(wsufHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
+            
+                server.start();
+            
+                return server;
+            }
+        }});
+
         // Embedded WSUF, added as filter, apply app-ws configuration via ServletContextListener
         
         cases.add(new Object[]{"wsuf.configureContext/ServletContextListener configure", new ServerProvider()
@@ -204,13 +243,13 @@
             @Override
             public Server newServer() throws Exception
             {
-                File testDir = MavenTestingUtils.getTargetTestingDir("WSUF-webxml");
+                File testDir = getNewTestDir();
                 
                 WSServer server = new WSServer(testDir, "/");
-                
+    
+                server.copyWebInf("wsuf-config-via-listener.xml");
                 server.copyClass(InfoSocket.class);
                 server.copyClass(InfoContextAttributeListener.class);
-                server.copyWebInf("wsuf-config-via-listener.xml");
                 server.start();
                 
                 WebAppContext webapp = server.createWebAppContext();
@@ -220,6 +259,33 @@
             }
         }});
     
+        // WSUF from web.xml, SCI active, apply app-ws configuration via ServletContextListener with WEB-INF/lib/jetty-http.jar
+    
+        cases.add(new Object[]{"wsuf/WebAppContext/web.xml/ServletContextListener/jetty-http.jar", new ServerProvider()
+        {
+            @Override
+            public Server newServer() throws Exception
+            {
+                File testDir = getNewTestDir();
+            
+                WSServer server = new WSServer(testDir, "/");
+            
+                server.copyWebInf("wsuf-config-via-listener.xml");
+                server.copyClass(InfoSocket.class);
+                server.copyClass(InfoContextAttributeListener.class);
+                // Add a jetty-http.jar to ensure that the classloader constraints
+                // and the WebAppClassloader setup is sane and correct
+                // The odd version string is present to capture bad regex behavior in Jetty
+                server.copyLib(org.eclipse.jetty.http.pathmap.PathSpec.class, "jetty-http-9.99.999.jar");
+                server.start();
+            
+                WebAppContext webapp = server.createWebAppContext();
+                server.deployWebapp(webapp);
+            
+                return server.getServer();
+            }
+        }});
+
         // WSUF from web.xml, SCI active, apply app-ws configuration via Servlet.init
     
         cases.add(new Object[]{"wsuf/WebAppContext/web.xml/Servlet.init", new ServerProvider()
@@ -227,13 +293,36 @@
             @Override
             public Server newServer() throws Exception
             {
-                File testDir = MavenTestingUtils.getTargetTestingDir("WSUF-webxml");
+                File testDir = getNewTestDir();
             
                 WSServer server = new WSServer(testDir, "/");
-            
+    
+                server.copyWebInf("wsuf-config-via-servlet-init.xml");
                 server.copyClass(InfoSocket.class);
                 server.copyClass(InfoServlet.class);
-                server.copyWebInf("wsuf-config-via-servlet-init.xml");
+                server.start();
+            
+                WebAppContext webapp = server.createWebAppContext();
+                server.deployWebapp(webapp);
+            
+                return server.getServer();
+            }
+        }});
+        
+        // xml based, wsuf, on alternate url-pattern and config attribute location
+
+        cases.add(new Object[]{"wsuf/WebAppContext/web.xml/ServletContextListener/alt-config", new ServerProvider()
+        {
+            @Override
+            public Server newServer() throws Exception
+            {
+                File testDir = getNewTestDir();
+            
+                WSServer server = new WSServer(testDir, "/");
+    
+                server.copyWebInf("wsuf-alt-config-via-listener.xml");
+                server.copyClass(InfoSocket.class);
+                server.copyClass(InfoContextAltAttributeListener.class);
                 server.start();
             
                 WebAppContext webapp = server.createWebAppContext();
@@ -267,7 +356,7 @@
     }
     
     @Test
-    public void testConfiguration() throws Exception
+    public void testNormalConfiguration() throws Exception
     {
         URI destUri = serverUri.resolve("/info/");
         
@@ -287,4 +376,45 @@
             assertThat("payload", payload, containsString("session.maxTextMessageSize=" + (10 * 1024 * 1024)));
         }
     }
+    
+    @Test
+    public void testStopStartOfHandler() throws Exception
+    {
+        URI destUri = serverUri.resolve("/info/");
+        
+        try (BlockheadClient client = new BlockheadClient(destUri))
+        {
+            client.connect();
+            client.sendStandardRequest();
+            client.expectUpgradeResponse();
+            
+            client.write(new TextFrame().setPayload("hello 1"));
+            
+            EventQueue<WebSocketFrame> frames = client.readFrames(1, 1000, TimeUnit.MILLISECONDS);
+            String payload = frames.poll().getPayloadAsUTF8();
+            
+            // If we can connect and send a text message, we know that the endpoint was
+            // added properly, and the response will help us verify the policy configuration too
+            assertThat("payload", payload, containsString("session.maxTextMessageSize=" + (10 * 1024 * 1024)));
+        }
+        
+        server.getHandler().stop();
+        server.getHandler().start();
+        
+        try (BlockheadClient client = new BlockheadClient(destUri))
+        {
+            client.connect();
+            client.sendStandardRequest();
+            client.expectUpgradeResponse();
+        
+            client.write(new TextFrame().setPayload("hello 2"));
+        
+            EventQueue<WebSocketFrame> frames = client.readFrames(1, 1000, TimeUnit.MILLISECONDS);
+            String payload = frames.poll().getPayloadAsUTF8();
+        
+            // If we can connect and send a text message, we know that the endpoint was
+            // added properly, and the response will help us verify the policy configuration too
+            assertThat("payload", payload, containsString("session.maxTextMessageSize=" + (10 * 1024 * 1024)));
+        }
+    }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java
index e514f8d..5dea3e3 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java
@@ -24,6 +24,7 @@
 
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.eclipse.jetty.util.log.StdErrLog;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.common.Generator;
@@ -175,6 +176,8 @@
     public TestName testname = new TestName();
 
     /**
+     * @param clazz the class to enable
+     * @param enabled true to enable the stack traces (or not)
      * @deprecated use {@link StacklessLogging} in a try-with-resources block instead
      */
     @Deprecated
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java
index 3ccf51d..3fc99bf 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java
@@ -36,6 +36,7 @@
 {
     /**
      * Echo 0 byte TEXT message
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_1_1() throws Exception
@@ -59,6 +60,7 @@
 
     /**
      * Echo 125 byte TEXT message (uses small 7-bit payload length)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_1_2() throws Exception
@@ -86,6 +88,7 @@
 
     /**
      * Echo 126 byte TEXT message (uses medium 2 byte payload length)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_1_3() throws Exception
@@ -113,6 +116,7 @@
 
     /**
      * Echo 127 byte TEXT message (uses medium 2 byte payload length)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_1_4() throws Exception
@@ -140,6 +144,7 @@
 
     /**
      * Echo 128 byte TEXT message (uses medium 2 byte payload length)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_1_5() throws Exception
@@ -167,6 +172,7 @@
 
     /**
      * Echo 65535 byte TEXT message (uses medium 2 byte payload length)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_1_6() throws Exception
@@ -194,6 +200,7 @@
 
     /**
      * Echo 65536 byte TEXT message (uses large 8 byte payload length)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_1_7() throws Exception
@@ -225,6 +232,7 @@
      * Only send 1 TEXT frame from client, but in small segments (flushed after each).
      * <p>
      * This is done to test the parsing together of the frame on the server side.
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_1_8() throws Exception
@@ -254,6 +262,7 @@
 
     /**
      * Echo 0 byte BINARY message
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_2_1() throws Exception
@@ -277,6 +286,7 @@
 
     /**
      * Echo 125 byte BINARY message (uses small 7-bit payload length)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_2_2() throws Exception
@@ -304,6 +314,7 @@
 
     /**
      * Echo 126 byte BINARY message (uses medium 2 byte payload length)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_2_3() throws Exception
@@ -331,6 +342,7 @@
 
     /**
      * Echo 127 byte BINARY message (uses medium 2 byte payload length)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_2_4() throws Exception
@@ -358,6 +370,7 @@
 
     /**
      * Echo 128 byte BINARY message (uses medium 2 byte payload length)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_2_5() throws Exception
@@ -385,6 +398,7 @@
 
     /**
      * Echo 65535 byte BINARY message (uses medium 2 byte payload length)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_2_6() throws Exception
@@ -412,6 +426,7 @@
 
     /**
      * Echo 65536 byte BINARY message (uses large 8 byte payload length)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_2_7() throws Exception
@@ -443,6 +458,7 @@
      * Only send 1 BINARY frame from client, but in small segments (flushed after each).
      * <p>
      * This is done to test the parsing together of the frame on the server side.
+     * @throws Exception on test failure
      */
     @Test
     public void testCase1_2_8() throws Exception
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase2.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase2.java
index b771839..2415457 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase2.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase2.java
@@ -42,6 +42,7 @@
 {
     /**
      * Ping without payload
+     * @throws Exception on test failure
      */
     @Test
     public void testCase2_1() throws Exception
@@ -61,6 +62,7 @@
 
     /**
      * 10 pings
+     * @throws Exception on test failure
      */
     @Test
     public void testCase2_10() throws Exception
@@ -95,6 +97,7 @@
 
     /**
      * 10 pings, sent slowly
+     * @throws Exception on test failure
      */
     @Test
     public void testCase2_11() throws Exception
@@ -130,6 +133,7 @@
 
     /**
      * Ping with small text payload
+     * @throws Exception on test failure
      */
     @Test
     public void testCase2_2() throws Exception
@@ -155,6 +159,7 @@
 
     /**
      * Ping with small binary (non-utf8) payload
+     * @throws Exception on test failure
      */
     @Test
     public void testCase2_3() throws Exception
@@ -180,6 +185,7 @@
 
     /**
      * Ping with 125 byte binary payload
+     * @throws Exception on test failure
      */
     @Test
     public void testCase2_4() throws Exception
@@ -206,6 +212,7 @@
 
     /**
      * Ping with 126 byte binary payload
+     * @throws Exception on test failure
      */
     @Test
     public void testCase2_5() throws Exception
@@ -236,6 +243,7 @@
 
     /**
      * Ping with 125 byte binary payload (slow send)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase2_6() throws Exception
@@ -263,6 +271,7 @@
 
     /**
      * Unsolicited pong frame without payload
+     * @throws Exception on test failure
      */
     @Test
     public void testCase2_7() throws Exception
@@ -285,6 +294,7 @@
 
     /**
      * Unsolicited pong frame with basic payload
+     * @throws Exception on test failure
      */
     @Test
     public void testCase2_8() throws Exception
@@ -307,6 +317,7 @@
 
     /**
      * Unsolicited pong frame, then ping with basic payload
+     * @throws Exception on test failure
      */
     @Test
     public void testCase2_9() throws Exception
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java
index 017142f..00f93eb 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java
@@ -43,6 +43,7 @@
 {
     /**
      * Send small text frame, with RSV1 == true, with no extensions defined.
+     * @throws Exception on test failure
      */
     @Test
     public void testCase3_1() throws Exception
@@ -62,6 +63,7 @@
 
     /**
      * Send small text frame, send again with RSV2 == true, then ping, with no extensions defined.
+     * @throws Exception on test failure
      */
     @Test
     public void testCase3_2() throws Exception
@@ -86,6 +88,7 @@
 
     /**
      * Send small text frame, send again with (RSV1 & RSV2), then ping, with no extensions defined.
+     * @throws Exception on test failure
      */
     @Test
     public void testCase3_3() throws Exception
@@ -110,6 +113,7 @@
 
     /**
      * Send small text frame, send again with (RSV3), then ping, with no extensions defined.
+     * @throws Exception on test failure
      */
     @Test
     public void testCase3_4() throws Exception
@@ -135,6 +139,7 @@
 
     /**
      * Send binary frame with (RSV3 & RSV1), with no extensions defined.
+     * @throws Exception on test failure
      */
     @Test
     public void testCase3_5() throws Exception
@@ -159,6 +164,7 @@
 
     /**
      * Send ping frame with (RSV3 & RSV2), with no extensions defined.
+     * @throws Exception on test failure
      */
     @Test
     public void testCase3_6() throws Exception
@@ -183,6 +189,7 @@
 
     /**
      * Send close frame with (RSV3 & RSV2 & RSV1), with no extensions defined.
+     * @throws Exception on test failure
      */
     @Test
     public void testCase3_7() throws Exception
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java
index 16f6772..731df15 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java
@@ -43,6 +43,7 @@
 {
     /**
      * Send opcode 3 (reserved)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase4_1_1() throws Exception
@@ -64,6 +65,7 @@
 
     /**
      * Send opcode 4 (reserved), with payload
+     * @throws Exception on test failure
      */
     @Test
     public void testCase4_1_2() throws Exception
@@ -88,6 +90,7 @@
 
     /**
      * Send small text, then frame with opcode 5 (reserved), then ping
+     * @throws Exception on test failure
      */
     @Test
     public void testCase4_1_3() throws Exception
@@ -112,6 +115,7 @@
 
     /**
      * Send small text, then frame with opcode 6 (reserved) w/payload, then ping
+     * @throws Exception on test failure
      */
     @Test
     public void testCase4_1_4() throws Exception
@@ -138,6 +142,7 @@
 
     /**
      * Send small text, then frame with opcode 7 (reserved) w/payload, then ping
+     * @throws Exception on test failure
      */
     @Test
     public void testCase4_1_5() throws Exception
@@ -164,6 +169,7 @@
 
     /**
      * Send opcode 11 (reserved)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase4_2_1() throws Exception
@@ -185,6 +191,7 @@
 
     /**
      * Send opcode 12 (reserved)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase4_2_2() throws Exception
@@ -208,6 +215,7 @@
 
     /**
      * Send small text, then frame with opcode 13 (reserved), then ping
+     * @throws Exception on test failure
      */
     @Test
     public void testCase4_2_3() throws Exception
@@ -232,6 +240,7 @@
 
     /**
      * Send small text, then frame with opcode 14 (reserved), then ping
+     * @throws Exception on test failure
      */
     @Test
     public void testCase4_2_4() throws Exception
@@ -258,6 +267,7 @@
 
     /**
      * Send small text, then frame with opcode 15 (reserved), then ping
+     * @throws Exception on test failure
      */
     @Test
     public void testCase4_2_5() throws Exception
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java
index f74106b..c433d67 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java
@@ -45,6 +45,7 @@
 {
     /**
      * Send ping fragmented in 2 packets
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_1() throws Exception
@@ -68,6 +69,7 @@
 
     /**
      * Send continuation+fin, then text+fin (framewise)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_10() throws Exception
@@ -91,6 +93,7 @@
 
     /**
      * Send continuation+fin, then text+fin (slowly)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_11() throws Exception
@@ -115,6 +118,7 @@
 
     /**
      * Send continuation+!fin, then text+fin
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_12() throws Exception
@@ -138,6 +142,7 @@
 
     /**
      * Send continuation+!fin, then text+fin (framewise)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_13() throws Exception
@@ -161,6 +166,7 @@
 
     /**
      * Send continuation+!fin, then text+fin (slowly)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_14() throws Exception
@@ -185,6 +191,7 @@
 
     /**
      * Send text fragmented properly in 2 frames, then continuation!fin, then text unfragmented.
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_15() throws Exception
@@ -211,6 +218,7 @@
 
     /**
      * (continuation!fin, text!fin, continuation+fin) * 2
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_16() throws Exception
@@ -238,6 +246,7 @@
 
     /**
      * (continuation+fin, text!fin, continuation+fin) * 2
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_17() throws Exception
@@ -265,6 +274,7 @@
 
     /**
      * text message fragmented in 2 frames, both frames as opcode=TEXT
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_18() throws Exception
@@ -288,6 +298,7 @@
 
     /**
      * send text message fragmented in 5 frames, with 2 pings and wait between.
+     * @throws Exception on test failure
      */
     @Test
     @Slow
@@ -335,6 +346,7 @@
 
     /**
      * Send pong fragmented in 2 packets
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_2() throws Exception
@@ -358,6 +370,7 @@
 
     /**
      * send text message fragmented in 5 frames, with 2 pings and wait between. (framewise)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_20() throws Exception
@@ -399,6 +412,7 @@
 
     /**
      * send text message fragmented in 5 frames, with 2 pings and wait between. (framewise)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_20_slow() throws Exception
@@ -441,6 +455,7 @@
 
     /**
      * Send text fragmented in 2 packets
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_3() throws Exception
@@ -465,6 +480,7 @@
 
     /**
      * Send text fragmented in 2 packets (framewise)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_4() throws Exception
@@ -489,6 +505,7 @@
 
     /**
      * Send text fragmented in 2 packets (slowly)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_5() throws Exception
@@ -514,6 +531,7 @@
 
     /**
      * Send text fragmented in 2 packets, with ping between them
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_6() throws Exception
@@ -540,6 +558,7 @@
 
     /**
      * Send text fragmented in 2 packets, with ping between them (framewise)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_7() throws Exception
@@ -566,6 +585,7 @@
 
     /**
      * Send text fragmented in 2 packets, with ping between them (slowly)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_8() throws Exception
@@ -593,6 +613,7 @@
 
     /**
      * Send continuation+fin, then text+fin
+     * @throws Exception on test failure
      */
     @Test
     public void testCase5_9() throws Exception
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java
index ace51f6..48ff152 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java
@@ -49,6 +49,8 @@
 {
     /**
      * Split a message byte array into a series of fragments (frames + continuations) of 1 byte message contents each.
+     * @param frames the frames
+     * @param msg the message
      */
     protected void fragmentText(List<WebSocketFrame> frames, byte msg[])
     {
@@ -78,6 +80,7 @@
 
     /**
      * text message, 1 frame, 0 length
+     * @throws Exception on test failure
      */
     @Test
     public void testCase6_1_1() throws Exception
@@ -101,6 +104,7 @@
 
     /**
      * text message, 0 length, 3 fragments
+     * @throws Exception on test failure
      */
     @Test
     public void testCase6_1_2() throws Exception
@@ -126,6 +130,7 @@
 
     /**
      * text message, small length, 3 fragments (only middle frame has payload)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase6_1_3() throws Exception
@@ -151,6 +156,7 @@
 
     /**
      * valid utf8 text message, 2 fragments (on UTF8 code point boundary)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase6_2_2() throws Exception
@@ -185,6 +191,7 @@
 
     /**
      * valid utf8 text message, many fragments (1 byte each)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase6_2_3() throws Exception
@@ -211,6 +218,7 @@
 
     /**
      * valid utf8 text message, many fragments (1 byte each)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase6_2_4() throws Exception
@@ -236,6 +244,7 @@
 
     /**
      * invalid utf8 text message, many fragments (1 byte each)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase6_3_2() throws Exception
@@ -264,6 +273,7 @@
      * fragment #1 and fragment #3 are both valid in themselves.
      * <p>
      * fragment #2 contains the invalid utf8 code point.
+     * @throws Exception on test failure
      */
     @Test
     @Slow
@@ -299,6 +309,7 @@
      * fragment #2 finishes the UTF8 code point but it is invalid
      * <p>
      * fragment #3 contains the remainder of the message.
+     * @throws Exception on test failure
      */
     @Test
     @Slow
@@ -326,6 +337,7 @@
 
     /**
      * invalid text message, 1 frame/fragment (slowly, and split within code points)
+     * @throws Exception on test failure
      */
     @Test
     @Slow
@@ -378,6 +390,7 @@
 
     /**
      * invalid text message, 1 frame/fragment (slowly, and split within code points)
+     * @throws Exception on test failure
      */
     @Test
     @Slow
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java
index 14e4568..c7b37d0 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java
@@ -52,6 +52,7 @@
 
     /**
      * Basic message then close frame, normal behavior
+     * @throws Exception on test failure
      */
     @Test
     public void testCase7_1_1() throws Exception
@@ -75,6 +76,7 @@
 
     /**
      * Close frame, then another close frame (send frame ignored)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase7_1_2() throws Exception
@@ -98,6 +100,7 @@
 
     /**
      * Close frame, then ping frame (no pong received)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase7_1_3() throws Exception
@@ -121,6 +124,7 @@
 
     /**
      * Close frame, then ping frame (no pong received)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase7_1_4() throws Exception
@@ -144,6 +148,7 @@
 
     /**
      * Text fin=false, close, then continuation fin=true
+     * @throws Exception on test failure
      */
     @Test
     public void testCase7_1_5() throws Exception
@@ -168,6 +173,7 @@
 
     /**
      * 256k msg, then close, then ping
+     * @throws Exception on test failure
      */
     @Test
     public void testCase7_1_6() throws Exception
@@ -197,6 +203,7 @@
 
     /**
      * close with no payload (payload length 0)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase7_3_1() throws Exception
@@ -219,6 +226,7 @@
 
     /**
      * close with invalid payload (payload length 1)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase7_3_2() throws Exception
@@ -244,6 +252,7 @@
 
     /**
      * close with valid payload (payload length 2)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase7_3_3() throws Exception
@@ -266,6 +275,7 @@
 
     /**
      * close with valid payload (with reason)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase7_3_4() throws Exception
@@ -288,6 +298,7 @@
 
     /**
      * close with valid payload (with 123 byte reason)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase7_3_5() throws Exception
@@ -314,6 +325,7 @@
 
     /**
      * close with invalid UTF8 in payload
+     * @throws Exception on test failure
      */
     @Test
     public void testCase7_5_1() throws Exception
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java
index 454d363..a914953 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java
@@ -83,6 +83,7 @@
 
     /**
      * just the close code, no reason
+     * @throws Exception on test failure
      */
     @Test
     public void testBadStatusCode() throws Exception
@@ -110,6 +111,7 @@
 
     /**
      * the bad close code, with reason
+     * @throws Exception on test failure
      */
     @Test
     public void testBadStatusCodeWithReason() throws Exception
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java
index 74c479f..7cb50f2 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java
@@ -78,6 +78,7 @@
 
     /**
      * just the close code, no reason
+     * @throws Exception on test failure
      */
     @Test
     public void testStatusCode() throws Exception
@@ -105,6 +106,7 @@
 
     /**
      * the good close code, with reason
+     * @throws Exception on test failure
      */
     @Test
     public void testStatusCodeWithReason() throws Exception
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase9.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase9.java
index a20c399..75221aa 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase9.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase9.java
@@ -131,6 +131,7 @@
 
     /**
      * Echo 64KB text message (1 frame)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase9_1_1() throws Exception
@@ -158,6 +159,7 @@
 
     /**
      * Echo 256KB text message (1 frame)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase9_1_2() throws Exception
@@ -185,6 +187,7 @@
 
     /**
      * Echo 1MB text message (1 frame)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase9_1_3() throws Exception
@@ -212,6 +215,7 @@
 
     /**
      * Echo 4MB text message (1 frame)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase9_1_4() throws Exception
@@ -239,6 +243,7 @@
 
     /**
      * Echo 8MB text message (1 frame)
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -267,6 +272,7 @@
 
     /**
      * Echo 16MB text message (1 frame)
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -295,6 +301,7 @@
 
     /**
      * Echo 64KB binary message (1 frame)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase9_2_1() throws Exception
@@ -321,6 +328,7 @@
 
     /**
      * Echo 256KB binary message (1 frame)
+     * @throws Exception on test failure
      */
     @Test
     public void testCase9_2_2() throws Exception
@@ -348,6 +356,7 @@
 
     /**
      * Echo 1MB binary message (1 frame)
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -376,6 +385,7 @@
 
     /**
      * Echo 4MB binary message (1 frame)
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -404,6 +414,7 @@
 
     /**
      * Echo 8MB binary message (1 frame)
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -432,6 +443,7 @@
 
     /**
      * Echo 16MB binary message (1 frame)
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -460,6 +472,7 @@
 
     /**
      * Send 4MB text message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -470,6 +483,7 @@
 
     /**
      * Send 4MB text message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -480,6 +494,7 @@
 
     /**
      * Send 4MB text message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -490,6 +505,7 @@
 
     /**
      * Send 4MB text message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -500,6 +516,7 @@
 
     /**
      * Send 4MB text message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -510,6 +527,7 @@
 
     /**
      * Send 4MB text message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -520,6 +538,7 @@
 
     /**
      * Send 4MB text message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -530,6 +549,7 @@
 
     /**
      * Send 4MB text message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -540,6 +560,7 @@
 
     /**
      * Send 4MB text message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -550,6 +571,7 @@
 
     /**
      * Send 4MB binary message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -560,6 +582,7 @@
 
     /**
      * Send 4MB binary message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -570,6 +593,7 @@
 
     /**
      * Send 4MB binary message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -580,6 +604,7 @@
 
     /**
      * Send 4MB binary message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -590,6 +615,7 @@
 
     /**
      * Send 4MB binary message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -600,6 +626,7 @@
 
     /**
      * Send 4MB binary message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -610,6 +637,7 @@
 
     /**
      * Send 4MB binary message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -620,6 +648,7 @@
 
     /**
      * Send 4MB binary message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -630,6 +659,7 @@
 
     /**
      * Send 4MB binary message in multiple frames.
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -640,6 +670,7 @@
 
     /**
      * Send 1MB text message in 1 frame, but slowly
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -650,6 +681,7 @@
 
     /**
      * Send 1MB text message in 1 frame, but slowly
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -660,6 +692,7 @@
 
     /**
      * Send 1MB text message in 1 frame, but slowly
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -670,6 +703,7 @@
 
     /**
      * Send 1MB text message in 1 frame, but slowly
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -680,6 +714,7 @@
 
     /**
      * Send 1MB text message in 1 frame, but slowly
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -690,6 +725,7 @@
 
     /**
      * Send 1MB text message in 1 frame, but slowly
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -700,6 +736,7 @@
 
     /**
      * Send 1MB binary message in 1 frame, but slowly
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -710,6 +747,7 @@
 
     /**
      * Send 1MB binary message in 1 frame, but slowly
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -720,6 +758,7 @@
 
     /**
      * Send 1MB binary message in 1 frame, but slowly
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -730,6 +769,7 @@
 
     /**
      * Send 1MB binary message in 1 frame, but slowly
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -740,6 +780,7 @@
 
     /**
      * Send 1MB binary message in 1 frame, but slowly
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
@@ -750,6 +791,7 @@
 
     /**
      * Send 1MB binary message in 1 frame, but slowly
+     * @throws Exception on test failure
      */
     @Test
     @Stress("High I/O use")
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java
index 3b42ca4..7bc3682 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java
@@ -27,8 +27,7 @@
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
-import org.eclipse.jetty.websocket.common.extensions.FrameDebugExtension;
-import org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension;
+import org.eclipse.jetty.websocket.common.extensions.FrameCaptureExtension;
 import org.eclipse.jetty.websocket.server.WebSocketHandler;
 import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
 import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
@@ -93,7 +92,7 @@
         // manually negotiate extensions
         List<ExtensionConfig> negotiated = new ArrayList<>();
         // adding frame debug
-        negotiated.add(new ExtensionConfig("@frame-debug; output-dir=target"));
+        negotiated.add(new ExtensionConfig("@frame-capture; output-dir=target"));
         for (ExtensionConfig config : req.getExtensions())
         {
             if (config.getName().equals("permessage-deflate"))
@@ -134,7 +133,7 @@
                 LOG.debug("Configuring WebSocketServerFactory ...");
 
                 // Registering Frame Debug
-                factory.getExtensionFactory().register("@frame-debug",FrameDebugExtension.class);
+                factory.getExtensionFactory().register("@frame-capture",FrameCaptureExtension.class);
 
                 // Setup the desired Socket to use for all incoming upgrade requests
                 factory.setCreator(BrowserDebugTool.this);
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/CaptureSocket.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/CaptureSocket.java
index 519c1d5..c961696 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/CaptureSocket.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/CaptureSocket.java
@@ -18,12 +18,14 @@
 
 package org.eclipse.jetty.websocket.server.helper;
 
+import java.security.NoSuchAlgorithmException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.api.WebSocketAdapter;
+import org.eclipse.jetty.websocket.common.util.Sha1Sum;
 
 public class CaptureSocket extends WebSocketAdapter
 {
@@ -58,4 +60,18 @@
         // System.out.printf("Received Message \"%s\" [size %d]%n", message, message.length());
         messages.add(message);
     }
+    
+    @Override
+    public void onWebSocketBinary(byte[] payload, int offset, int len)
+    {
+        try
+        {
+            messages.add("binary[sha1="+Sha1Sum.calculate(payload,offset,len)+"]");
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            messages.add("ERROR: Unable to caclulate Binary SHA1: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/SafariD00.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/SafariD00.java
index bc4793a..195f5f2 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/SafariD00.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/SafariD00.java
@@ -18,13 +18,15 @@
 
 package org.eclipse.jetty.websocket.server.helper;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
 import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.SocketAddress;
@@ -54,7 +56,7 @@
      * Open the Socket to the destination endpoint and
      *
      * @return the open java Socket.
-     * @throws IOException
+     * @throws IOException on test failure
      */
     public Socket connect() throws IOException
     {
@@ -75,7 +77,7 @@
     /**
      * Issue an Http websocket (Draft-0) upgrade request using the Safari particulars.
      *
-     * @throws UnsupportedEncodingException
+     * @throws IOException on test failure
      */
     public void issueHandshake() throws IOException
     {
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/AnnotatedRuntimeOnConnectSocket.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/AnnotatedRuntimeOnConnectSocket.java
index 3a498a7..ae757e4 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/AnnotatedRuntimeOnConnectSocket.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/AnnotatedRuntimeOnConnectSocket.java
@@ -39,11 +39,7 @@
     public void onWebSocketConnect(Session sess)
     {
         // Intentional runtime exception.
-        int[] arr = new int[5];
-        for (int i = 0; i < 10; i++)
-        {
-            arr[i] = 222;
-        }
+        throw new RuntimeException("Intentional Exception from onWebSocketConnect");
     }
 
     @OnWebSocketClose
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/ListenerRuntimeOnConnectSocket.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/ListenerRuntimeOnConnectSocket.java
index 664e333..354a61c 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/ListenerRuntimeOnConnectSocket.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/ListenerRuntimeOnConnectSocket.java
@@ -36,12 +36,7 @@
     {
         super.onWebSocketConnect(sess);
 
-        // Intentional runtime exception.
-        int[] arr = new int[5];
-        for (int i = 0; i < 10; i++)
-        {
-            arr[i] = 222;
-        }
+        throw new RuntimeException("Intentional Exception from onWebSocketConnect");
     }
 
     @Override
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/MisbehavingClassTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/MisbehavingClassTest.java
index 7d85877..a89358c 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/MisbehavingClassTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/MisbehavingClassTest.java
@@ -18,9 +18,9 @@
 
 package org.eclipse.jetty.websocket.server.misbehaving;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jetty.toolchain.test.EventQueue;
@@ -29,13 +29,18 @@
 import org.eclipse.jetty.websocket.common.CloseInfo;
 import org.eclipse.jetty.websocket.common.OpCode;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
-import org.eclipse.jetty.websocket.common.events.AbstractEventDriver;
+import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
+import org.eclipse.jetty.websocket.common.test.IBlockheadClient;
 import org.eclipse.jetty.websocket.server.SimpleServletServer;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
 /**
  * Testing badly behaving Socket class implementations to get the best
  * error messages and state out of the websocket implementation.
@@ -62,72 +67,70 @@
     @Test
     public void testListenerRuntimeOnConnect() throws Exception
     {
-        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri());
+             StacklessLogging scope = new StacklessLogging(ListenerRuntimeOnConnectSocket.class, WebSocketSession.class))
         {
             client.setProtocols("listener-runtime-connect");
             client.setTimeout(1,TimeUnit.SECONDS);
-            try (StacklessLogging scope = new StacklessLogging(AbstractEventDriver.class))
-            {
-                ListenerRuntimeOnConnectSocket socket = badSocketsServlet.listenerRuntimeConnect;
-                socket.reset();
 
-                client.connect();
-                client.sendStandardRequest();
-                client.expectUpgradeResponse();
+            ListenerRuntimeOnConnectSocket socket = badSocketsServlet.listenerRuntimeConnect;
+            socket.reset();
 
-                EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
-                WebSocketFrame frame = frames.poll();
-                assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
-                CloseInfo close = new CloseInfo(frame);
-                assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
+            client.connect();
+            client.sendStandardRequest();
+            client.expectUpgradeResponse();
 
-                client.write(close.asFrame()); // respond with close
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
+            WebSocketFrame frame = frames.poll();
+            assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
+            CloseInfo close = new CloseInfo(frame);
+            assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
 
-                // ensure server socket got close event
-                assertThat("Close Latch",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
-                assertThat("closeStatusCode",socket.closeStatusCode,is(StatusCode.SERVER_ERROR));
+            client.write(close.asFrame()); // respond with close
 
-                // Validate errors
-                assertThat("socket.onErrors",socket.errors.size(),is(1));
-                Throwable cause = socket.errors.pop();
-                assertThat("Error type",cause,instanceOf(ArrayIndexOutOfBoundsException.class));
-            }
+            // ensure server socket got close event
+            assertThat("Close Latch",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
+            assertThat("closeStatusCode",socket.closeStatusCode,is(StatusCode.SERVER_ERROR));
+
+            // Validate errors
+            assertThat("socket.onErrors",socket.errors.size(),is(1));
+            Throwable cause = socket.errors.pop();
+            assertThat("Error type",cause,instanceOf(RuntimeException.class));
         }
     }
     
     @Test
     public void testAnnotatedRuntimeOnConnect() throws Exception
     {
-        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
+        try (IBlockheadClient client = new BlockheadClient(server.getServerUri());
+             StacklessLogging scope = new StacklessLogging(AnnotatedRuntimeOnConnectSocket.class, WebSocketSession.class))
         {
             client.setProtocols("annotated-runtime-connect");
             client.setTimeout(1,TimeUnit.SECONDS);
-            try (StacklessLogging scope = new StacklessLogging(AbstractEventDriver.class))
-            {
-                AnnotatedRuntimeOnConnectSocket socket = badSocketsServlet.annotatedRuntimeConnect;
-                socket.reset();
 
-                client.connect();
-                client.sendStandardRequest();
-                client.expectUpgradeResponse();
+            AnnotatedRuntimeOnConnectSocket socket = badSocketsServlet.annotatedRuntimeConnect;
+            socket.reset();
 
-                EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
-                WebSocketFrame frame = frames.poll();
-                assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
-                CloseInfo close = new CloseInfo(frame);
-                assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
+            client.connect();
+            client.sendStandardRequest();
+            client.expectUpgradeResponse();
 
-                client.write(close.asFrame()); // respond with close
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
+            WebSocketFrame frame = frames.poll();
+            assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
+            CloseInfo close = new CloseInfo(frame);
+            assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
 
-                // ensure server socket got close event
-                assertThat("Close Latch",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
-                assertThat("closeStatusCode",socket.closeStatusCode,is(StatusCode.SERVER_ERROR));
+            client.write(close.asFrame()); // respond with close
 
-                // Validate errors
-                assertThat("socket.onErrors",socket.errors.size(),is(1));
-                Throwable cause = socket.errors.pop();
-                assertThat("Error type",cause,instanceOf(ArrayIndexOutOfBoundsException.class));
-            }
+            // ensure server socket got close event
+            assertThat("Close Latch",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
+            assertThat("closeStatusCode",socket.closeStatusCode,is(StatusCode.SERVER_ERROR));
+
+            // Validate errors
+            assertThat("socket.onErrors",socket.errors.size(),is(1));
+            Throwable cause = socket.errors.pop();
+            assertThat("Error type",cause,instanceOf(RuntimeException.class));
         }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/pathmap/PathMappingsBenchmarkTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/pathmap/PathMappingsBenchmarkTest.java
deleted file mode 100644
index 148856a..0000000
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/pathmap/PathMappingsBenchmarkTest.java
+++ /dev/null
@@ -1,226 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.server.pathmap;
-
-import java.util.concurrent.CyclicBarrier;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.http.PathMap;
-import org.eclipse.jetty.toolchain.test.AdvancedRunner;
-import org.eclipse.jetty.toolchain.test.annotation.Stress;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AdvancedRunner.class)
-public class PathMappingsBenchmarkTest
-{
-    public static abstract class AbstractPathMapThread extends Thread
-    {
-        private int iterations;
-        private CyclicBarrier barrier;
-        @SuppressWarnings("unused")
-        private long success;
-        @SuppressWarnings("unused")
-        private long error;
-
-        public AbstractPathMapThread(int iterations, CyclicBarrier barrier)
-        {
-            this.iterations = iterations;
-            this.barrier = barrier;
-        }
-
-        public abstract String getMatchedResource(String path);
-
-        @Override
-        public void run()
-        {
-            int llen = LOOKUPS.length;
-            String path;
-            String expectedResource;
-            String matchedResource;
-            await(barrier);
-            for (int iter = 0; iter < iterations; iter++)
-            {
-                for (int li = 0; li < llen; li++)
-                {
-                    path = LOOKUPS[li][0];
-                    expectedResource = LOOKUPS[li][1];
-                    matchedResource = getMatchedResource(path);
-                    if (matchedResource.equals(expectedResource))
-                    {
-                        success++;
-                    }
-                    else
-                    {
-                        error++;
-                    }
-                }
-            }
-            await(barrier);
-        }
-    }
-
-    public static class PathMapMatchThread extends AbstractPathMapThread
-    {
-        private PathMap<String> pathmap;
-
-        public PathMapMatchThread(PathMap<String> pathmap, int iters, CyclicBarrier barrier)
-        {
-            super(iters,barrier);
-            this.pathmap = pathmap;
-        }
-
-        @Override
-        public String getMatchedResource(String path)
-        {
-            return pathmap.getMatch(path).getValue();
-        }
-    }
-
-    public static class PathMatchThread extends AbstractPathMapThread
-    {
-        private PathMappings<String> pathmap;
-
-        public PathMatchThread(PathMappings<String> pathmap, int iters, CyclicBarrier barrier)
-        {
-            super(iters,barrier);
-            this.pathmap = pathmap;
-        }
-
-        @Override
-        public String getMatchedResource(String path)
-        {
-            return pathmap.getMatch(path).getResource();
-        }
-    }
-
-    private static final Logger LOG = Log.getLogger(PathMappingsBenchmarkTest.class);
-    private static final String[][] LOOKUPS;
-    private int runs = 20;
-    private int threads = 200;
-    private int iters = 10000;
-
-    static
-    {
-        LOOKUPS = new String[][]
-        {
-        // @formatter:off
-         { "/abs/path", "path" },
-         { "/abs/path/longer","longpath" },
-         { "/abs/path/foo","default" },
-         { "/main.css","default" },
-         { "/downloads/script.gz","gzipped" },
-         { "/downloads/distribution.tar.gz","tarball" },
-         { "/downloads/readme.txt","default" },
-         { "/downloads/logs.tgz","default" },
-         { "/animal/horse/mustang","animals" },
-         { "/animal/bird/eagle/bald","birds" },
-         { "/animal/fish/shark/hammerhead","fishes" },
-         { "/animal/insect/ladybug","animals" },
-        // @formatter:on
-        };
-    }
-
-    private static void await(CyclicBarrier barrier)
-    {
-        try
-        {
-            barrier.await();
-        }
-        catch (Exception x)
-        {
-            throw new RuntimeException(x);
-        }
-    }
-
-    @Stress("High CPU")
-    @Test
-    public void testServletPathMap()
-    {
-        // Setup (old) PathMap
-
-        PathMap<String> p = new PathMap<>();
-
-        p.put("/abs/path","path");
-        p.put("/abs/path/longer","longpath");
-        p.put("/animal/bird/*","birds");
-        p.put("/animal/fish/*","fishes");
-        p.put("/animal/*","animals");
-        p.put("*.tar.gz","tarball");
-        p.put("*.gz","gzipped");
-        p.put("/","default");
-
-        final CyclicBarrier barrier = new CyclicBarrier(threads + 1);
-
-        for (int r = 0; r < runs; r++)
-        {
-            for (int t = 0; t < threads; t++)
-            {
-                PathMapMatchThread thread = new PathMapMatchThread(p,iters,barrier);
-                thread.start();
-            }
-            await(barrier);
-            long begin = System.nanoTime();
-            await(barrier);
-            long end = System.nanoTime();
-            long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin);
-            int totalMatches = threads * iters * LOOKUPS.length;
-            LOG.info("jetty-http/PathMap (Servlet only) threads:{}/iters:{}/total-matches:{} => {} ms",threads,iters,totalMatches,elapsed);
-        }
-    }
-
-    @Stress("High CPU")
-    @Test
-    public void testServletPathMappings()
-    {
-        // Setup (new) PathMappings
-
-        PathMappings<String> p = new PathMappings<>();
-
-        p.put(new ServletPathSpec("/abs/path"),"path");
-        p.put(new ServletPathSpec("/abs/path/longer"),"longpath");
-        p.put(new ServletPathSpec("/animal/bird/*"),"birds");
-        p.put(new ServletPathSpec("/animal/fish/*"),"fishes");
-        p.put(new ServletPathSpec("/animal/*"),"animals");
-        p.put(new ServletPathSpec("*.tar.gz"),"tarball");
-        p.put(new ServletPathSpec("*.gz"),"gzipped");
-        p.put(new ServletPathSpec("/"),"default");
-
-        final CyclicBarrier barrier = new CyclicBarrier(threads + 1);
-
-        for (int r = 0; r < runs; r++)
-        {
-            for (int t = 0; t < threads; t++)
-            {
-                PathMatchThread thread = new PathMatchThread(p,iters,barrier);
-                thread.start();
-            }
-            await(barrier);
-            long begin = System.nanoTime();
-            await(barrier);
-            long end = System.nanoTime();
-            long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin);
-            int totalMatches = threads * iters * LOOKUPS.length;
-            LOG.info("jetty-websocket/PathMappings (Servlet only) threads:{}/iters:{}/total-matches:{} => {} ms",threads,iters,totalMatches,elapsed);
-
-        }
-    }
-}
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/pathmap/PathMappingsTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/pathmap/PathMappingsTest.java
deleted file mode 100644
index e7c0c6d..0000000
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/pathmap/PathMappingsTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.server.pathmap;
-
-import static org.hamcrest.Matchers.notNullValue;
-
-import org.eclipse.jetty.websocket.server.pathmap.PathMappings.MappedResource;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class PathMappingsTest
-{
-    private void assertMatch(PathMappings<String> pathmap, String path, String expectedValue)
-    {
-        String msg = String.format(".getMatch(\"%s\")",path);
-        MappedResource<String> match = pathmap.getMatch(path);
-        Assert.assertThat(msg,match,notNullValue());
-        String actualMatch = match.getResource();
-        Assert.assertEquals(msg,expectedValue,actualMatch);
-    }
-
-    public void dumpMappings(PathMappings<String> p)
-    {
-        for (MappedResource<String> res : p)
-        {
-            System.out.printf("  %s%n",res);
-        }
-    }
-
-    /**
-     * Test the match order rules with a mixed Servlet and WebSocket path specs
-     * <p>
-     * <ul>
-     * <li>Exact match</li>
-     * <li>Longest prefix match</li>
-     * <li>Longest suffix match</li>
-     * </ul>
-     */
-    @Test
-    public void testMixedMatchOrder()
-    {
-        PathMappings<String> p = new PathMappings<>();
-
-        p.put(new ServletPathSpec("/"),"default");
-        p.put(new ServletPathSpec("/animal/bird/*"),"birds");
-        p.put(new ServletPathSpec("/animal/fish/*"),"fishes");
-        p.put(new ServletPathSpec("/animal/*"),"animals");
-        p.put(new RegexPathSpec("^/animal/.*/chat$"),"animalChat");
-        p.put(new RegexPathSpec("^/animal/.*/cam$"),"animalCam");
-        p.put(new RegexPathSpec("^/entrance/cam$"),"entranceCam");
-
-        // dumpMappings(p);
-
-        assertMatch(p,"/animal/bird/eagle","birds");
-        assertMatch(p,"/animal/fish/bass/sea","fishes");
-        assertMatch(p,"/animal/peccary/javalina/evolution","animals");
-        assertMatch(p,"/","default");
-        assertMatch(p,"/animal/bird/eagle/chat","animalChat");
-        assertMatch(p,"/animal/bird/penguin/chat","animalChat");
-        assertMatch(p,"/animal/fish/trout/cam","animalCam");
-        assertMatch(p,"/entrance/cam","entranceCam");
-    }
-
-    /**
-     * Test the match order rules imposed by the Servlet API.
-     * <p>
-     * <ul>
-     * <li>Exact match</li>
-     * <li>Longest prefix match</li>
-     * <li>Longest suffix match</li>
-     * <li>default</li>
-     * </ul>
-     */
-    @Test
-    public void testServletMatchOrder()
-    {
-        PathMappings<String> p = new PathMappings<>();
-
-        p.put(new ServletPathSpec("/abs/path"),"path");
-        p.put(new ServletPathSpec("/abs/path/longer"),"longpath");
-        p.put(new ServletPathSpec("/animal/bird/*"),"birds");
-        p.put(new ServletPathSpec("/animal/fish/*"),"fishes");
-        p.put(new ServletPathSpec("/animal/*"),"animals");
-        p.put(new ServletPathSpec("*.tar.gz"),"tarball");
-        p.put(new ServletPathSpec("*.gz"),"gzipped");
-        p.put(new ServletPathSpec("/"),"default");
-
-        // dumpMappings(p);
-
-        assertMatch(p,"/abs/path","path");
-        assertMatch(p,"/abs/path/longer","longpath");
-        assertMatch(p,"/abs/path/foo","default");
-        assertMatch(p,"/main.css","default");
-        assertMatch(p,"/downloads/script.gz","gzipped");
-        assertMatch(p,"/downloads/distribution.tar.gz","tarball");
-        assertMatch(p,"/downloads/readme.txt","default");
-        assertMatch(p,"/downloads/logs.tgz","default");
-        assertMatch(p,"/animal/horse/mustang","animals");
-        assertMatch(p,"/animal/bird/eagle/bald","birds");
-        assertMatch(p,"/animal/fish/shark/hammerhead","fishes");
-        assertMatch(p,"/animal/insect/ladybug","animals");
-    }
-}
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/pathmap/RegexPathSpecTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/pathmap/RegexPathSpecTest.java
deleted file mode 100644
index bf47895..0000000
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/pathmap/RegexPathSpecTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.server.pathmap;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-import org.junit.Test;
-
-public class RegexPathSpecTest
-{
-    public static void assertMatches(PathSpec spec, String path)
-    {
-        String msg = String.format("Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path);
-        assertThat(msg,spec.matches(path),is(true));
-    }
-
-    public static void assertNotMatches(PathSpec spec, String path)
-    {
-        String msg = String.format("!Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path);
-        assertThat(msg,spec.matches(path),is(false));
-    }
-
-    @Test
-    public void testExactSpec()
-    {
-        RegexPathSpec spec = new RegexPathSpec("^/a$");
-        assertEquals("Spec.pathSpec","^/a$",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/a$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",1,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.EXACT,spec.group);
-
-        assertMatches(spec,"/a");
-
-        assertNotMatches(spec,"/aa");
-        assertNotMatches(spec,"/a/");
-    }
-
-    @Test
-    public void testMiddleSpec()
-    {
-        RegexPathSpec spec = new RegexPathSpec("^/rest/([^/]*)/list$");
-        assertEquals("Spec.pathSpec","^/rest/([^/]*)/list$",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/rest/([^/]*)/list$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",3,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.MIDDLE_GLOB,spec.group);
-
-        assertMatches(spec,"/rest/api/list");
-        assertMatches(spec,"/rest/1.0/list");
-        assertMatches(spec,"/rest/2.0/list");
-        assertMatches(spec,"/rest/accounts/list");
-
-        assertNotMatches(spec,"/a");
-        assertNotMatches(spec,"/aa");
-        assertNotMatches(spec,"/aa/bb");
-        assertNotMatches(spec,"/rest/admin/delete");
-        assertNotMatches(spec,"/rest/list");
-    }
-
-    @Test
-    public void testMiddleSpecNoGrouping()
-    {
-        RegexPathSpec spec = new RegexPathSpec("^/rest/[^/]+/list$");
-        assertEquals("Spec.pathSpec","^/rest/[^/]+/list$",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/rest/[^/]+/list$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",3,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.MIDDLE_GLOB,spec.group);
-
-        assertMatches(spec,"/rest/api/list");
-        assertMatches(spec,"/rest/1.0/list");
-        assertMatches(spec,"/rest/2.0/list");
-        assertMatches(spec,"/rest/accounts/list");
-
-        assertNotMatches(spec,"/a");
-        assertNotMatches(spec,"/aa");
-        assertNotMatches(spec,"/aa/bb");
-        assertNotMatches(spec,"/rest/admin/delete");
-        assertNotMatches(spec,"/rest/list");
-    }
-
-    @Test
-    public void testPrefixSpec()
-    {
-        RegexPathSpec spec = new RegexPathSpec("^/a/(.*)$");
-        assertEquals("Spec.pathSpec","^/a/(.*)$",spec.getPathSpec());
-        assertEquals("Spec.pattern","^/a/(.*)$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",2,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.PREFIX_GLOB,spec.group);
-
-        assertMatches(spec,"/a/");
-        assertMatches(spec,"/a/b");
-        assertMatches(spec,"/a/b/c/d/e");
-
-        assertNotMatches(spec,"/a");
-        assertNotMatches(spec,"/aa");
-        assertNotMatches(spec,"/aa/bb");
-    }
-
-    @Test
-    public void testSuffixSpec()
-    {
-        RegexPathSpec spec = new RegexPathSpec("^(.*).do$");
-        assertEquals("Spec.pathSpec","^(.*).do$",spec.getPathSpec());
-        assertEquals("Spec.pattern","^(.*).do$",spec.getPattern().pattern());
-        assertEquals("Spec.pathDepth",0,spec.getPathDepth());
-        assertEquals("Spec.group",PathSpecGroup.SUFFIX_GLOB,spec.group);
-
-        assertMatches(spec,"/a.do");
-        assertMatches(spec,"/a/b/c.do");
-        assertMatches(spec,"/abcde.do");
-        assertMatches(spec,"/abc/efg.do");
-
-        assertNotMatches(spec,"/a");
-        assertNotMatches(spec,"/aa");
-        assertNotMatches(spec,"/aa/bb");
-        assertNotMatches(spec,"/aa/bb.do/more");
-    }
-}
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/pathmap/ServletPathSpecTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/pathmap/ServletPathSpecTest.java
deleted file mode 100644
index 24d516f..0000000
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/pathmap/ServletPathSpecTest.java
+++ /dev/null
@@ -1,188 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.server.pathmap;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
-import org.junit.Test;
-
-public class ServletPathSpecTest
-{
-    private void assertBadServletPathSpec(String pathSpec)
-    {
-        try
-        {
-            new ServletPathSpec(pathSpec);
-            fail("Expected IllegalArgumentException for a bad servlet pathspec on: " + pathSpec);
-        }
-        catch (IllegalArgumentException e)
-        {
-            // expected path
-            System.out.println(e);
-        }
-    }
-
-    private void assertMatches(ServletPathSpec spec, String path)
-    {
-        String msg = String.format("Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path);
-        assertThat(msg,spec.matches(path),is(true));
-    }
-
-    private void assertNotMatches(ServletPathSpec spec, String path)
-    {
-        String msg = String.format("!Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path);
-        assertThat(msg,spec.matches(path),is(false));
-    }
-
-    @Test
-    public void testBadServletPathSpecA()
-    {
-        assertBadServletPathSpec("foo");
-    }
-
-    @Test
-    public void testBadServletPathSpecB()
-    {
-        assertBadServletPathSpec("/foo/*.do");
-    }
-
-    @Test
-    public void testBadServletPathSpecC()
-    {
-        assertBadServletPathSpec("foo/*.do");
-    }
-
-    @Test
-    public void testBadServletPathSpecD()
-    {
-        assertBadServletPathSpec("foo/*.*do");
-    }
-
-    @Test
-    public void testBadServletPathSpecE()
-    {
-        assertBadServletPathSpec("*do");
-    }
-
-    @Test
-    public void testDefaultPathSpec()
-    {
-        ServletPathSpec spec = new ServletPathSpec("/");
-        assertEquals("Spec.pathSpec","/",spec.getPathSpec());
-        assertEquals("Spec.pathDepth",-1,spec.getPathDepth());
-    }
-
-    @Test
-    public void testEmptyPathSpec()
-    {
-        ServletPathSpec spec = new ServletPathSpec("");
-        assertEquals("Spec.pathSpec","/",spec.getPathSpec());
-        assertEquals("Spec.pathDepth",-1,spec.getPathDepth());
-    }
-
-    @Test
-    public void testExactPathSpec()
-    {
-        ServletPathSpec spec = new ServletPathSpec("/abs/path");
-        assertEquals("Spec.pathSpec","/abs/path",spec.getPathSpec());
-        assertEquals("Spec.pathDepth",2,spec.getPathDepth());
-
-        assertMatches(spec,"/abs/path");
-        assertMatches(spec,"/abs/path/");
-
-        assertNotMatches(spec,"/abs/path/more");
-        assertNotMatches(spec,"/foo");
-        assertNotMatches(spec,"/foo/abs/path");
-        assertNotMatches(spec,"/foo/abs/path/");
-    }
-
-    @Test
-    public void testGetPathInfo()
-    {
-        assertEquals("pathInfo exact",null,new ServletPathSpec("/Foo/bar").getPathInfo("/Foo/bar"));
-        assertEquals("pathInfo prefix","/bar",new ServletPathSpec("/Foo/*").getPathInfo("/Foo/bar"));
-        assertEquals("pathInfo prefix","/*",new ServletPathSpec("/Foo/*").getPathInfo("/Foo/*"));
-        assertEquals("pathInfo prefix","/",new ServletPathSpec("/Foo/*").getPathInfo("/Foo/"));
-        assertEquals("pathInfo prefix",null,new ServletPathSpec("/Foo/*").getPathInfo("/Foo"));
-        assertEquals("pathInfo suffix",null,new ServletPathSpec("*.ext").getPathInfo("/Foo/bar.ext"));
-        assertEquals("pathInfo default",null,new ServletPathSpec("/").getPathInfo("/Foo/bar.ext"));
-
-        assertEquals("pathInfo default","/xxx/zzz",new ServletPathSpec("/*").getPathInfo("/xxx/zzz"));
-    }
-
-    @Test
-    public void testNullPathSpec()
-    {
-        ServletPathSpec spec = new ServletPathSpec(null);
-        assertEquals("Spec.pathSpec","/",spec.getPathSpec());
-        assertEquals("Spec.pathDepth",-1,spec.getPathDepth());
-    }
-
-    @Test
-    public void testPathMatch()
-    {
-        assertEquals("pathMatch exact","/Foo/bar",new ServletPathSpec("/Foo/bar").getPathMatch("/Foo/bar"));
-        assertEquals("pathMatch prefix","/Foo",new ServletPathSpec("/Foo/*").getPathMatch("/Foo/bar"));
-        assertEquals("pathMatch prefix","/Foo",new ServletPathSpec("/Foo/*").getPathMatch("/Foo/"));
-        assertEquals("pathMatch prefix","/Foo",new ServletPathSpec("/Foo/*").getPathMatch("/Foo"));
-        assertEquals("pathMatch suffix","/Foo/bar.ext",new ServletPathSpec("*.ext").getPathMatch("/Foo/bar.ext"));
-        assertEquals("pathMatch default","/Foo/bar.ext",new ServletPathSpec("/").getPathMatch("/Foo/bar.ext"));
-
-        assertEquals("pathMatch default","",new ServletPathSpec("/*").getPathMatch("/xxx/zzz"));
-    }
-
-    @Test
-    public void testPrefixPathSpec()
-    {
-        ServletPathSpec spec = new ServletPathSpec("/downloads/*");
-        assertEquals("Spec.pathSpec","/downloads/*",spec.getPathSpec());
-        assertEquals("Spec.pathDepth",2,spec.getPathDepth());
-
-        assertMatches(spec,"/downloads/logo.jpg");
-        assertMatches(spec,"/downloads/distribution.tar.gz");
-        assertMatches(spec,"/downloads/distribution.tgz");
-        assertMatches(spec,"/downloads/distribution.zip");
-
-        assertMatches(spec,"/downloads");
-
-        assertEquals("Spec.pathInfo","/",spec.getPathInfo("/downloads/"));
-        assertEquals("Spec.pathInfo","/distribution.zip",spec.getPathInfo("/downloads/distribution.zip"));
-        assertEquals("Spec.pathInfo","/dist/9.0/distribution.tar.gz",spec.getPathInfo("/downloads/dist/9.0/distribution.tar.gz"));
-    }
-
-    @Test
-    public void testSuffixPathSpec()
-    {
-        ServletPathSpec spec = new ServletPathSpec("*.gz");
-        assertEquals("Spec.pathSpec","*.gz",spec.getPathSpec());
-        assertEquals("Spec.pathDepth",0,spec.getPathDepth());
-
-        assertMatches(spec,"/downloads/distribution.tar.gz");
-        assertMatches(spec,"/downloads/jetty.log.gz");
-
-        assertNotMatches(spec,"/downloads/distribution.zip");
-        assertNotMatches(spec,"/downloads/distribution.tgz");
-        assertNotMatches(spec,"/abs/path");
-
-        assertEquals("Spec.pathInfo",null,spec.getPathInfo("/downloads/distribution.tar.gz"));
-    }
-}
diff --git a/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties b/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties
index 9165916..924d000 100644
--- a/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties
+++ b/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties
@@ -12,6 +12,11 @@
 # org.eclipse.jetty.websocket.server.blockhead.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.server.helper.LEVEL=DEBUG
 
+# org.eclipse.jetty.websocket.client.io.ConnectPromise.LEVEL=DEBUG
+# org.eclipse.jetty.websocket.common.WebSocketSession_OPEN.LEVEL=DEBUG
+# org.eclipse.jetty.websocket.common.io.IOState.LEVEL=DEBUG
+# org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection_OPEN.LEVEL=DEBUG
+
 ### Show state changes on BrowserDebugTool
 # -- LEAVE THIS AT DEBUG LEVEL --
 org.eclipse.jetty.websocket.server.browser.LEVEL=DEBUG
diff --git a/jetty-websocket/websocket-server/src/test/resources/wsuf-alt-config-via-listener.xml b/jetty-websocket/websocket-server/src/test/resources/wsuf-alt-config-via-listener.xml
new file mode 100644
index 0000000..4321a5c
--- /dev/null
+++ b/jetty-websocket/websocket-server/src/test/resources/wsuf-alt-config-via-listener.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app
+  xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
+		 http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+  version="3.1">
+
+  <listener>
+    <listener-class>org.eclipse.jetty.websocket.server.InfoContextAltAttributeListener</listener-class>
+  </listener>
+
+  <filter>
+    <filter-name>wsuf-alt</filter-name>
+    <filter-class>org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter</filter-class>
+    <init-param>
+      <param-name>configAttributeKey</param-name>
+      <param-value>alt.config</param-value>
+    </init-param>
+  </filter>
+
+  <filter-mapping>
+    <filter-name>wsuf-alt</filter-name>
+    <url-pattern>/info/*</url-pattern>
+  </filter-mapping>
+</web-app>
diff --git a/jetty-websocket/websocket-servlet/pom.xml b/jetty-websocket/websocket-servlet/pom.xml
index 58fe04e..a4a6757 100644
--- a/jetty-websocket/websocket-servlet/pom.xml
+++ b/jetty-websocket/websocket-servlet/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.websocket</groupId>
         <artifactId>websocket-parent</artifactId>
-        <version>9.2.22-SNAPSHOT</version>
+        <version>9.3.19-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
@@ -15,23 +15,6 @@
     </properties>
     <build>
       <plugins>
-           <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>artifact-jar</id>
-                        <goals>
-                            <goal>jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-                <configuration>
-                    <archive>
-                        <manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
-                    </archive>
-                </configuration>
-            </plugin>
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
@@ -40,9 +23,7 @@
                     <instructions>
                         <Bundle-Description>Websocket Servlet Interface</Bundle-Description>
                         <Bundle-Classpath />
-                        <_nouses>true</_nouses>
                         <DynamicImport-Package>org.eclipse.jetty.websocket.server.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}",org.eclipse.jetty.websocket.server.pathmap.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}"</DynamicImport-Package>
-                        <Require-Capability>osgi.serviceloader; filter:="(osgi.serviceloader=org.eclipse.jetty.websocket.servlet.WebSocketServletFactory)";cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)"</Require-Capability>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java
index 79e7d70..986b6de 100644
--- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java
+++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java
@@ -31,6 +31,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
@@ -107,7 +108,7 @@
 
     /**
      * Return the underlying HttpServletRequest that existed at Upgrade time.
-     * <p/>
+     * <p>
      * Note: many features of the HttpServletRequest are invalid when upgraded,
      * especially ones that deal with body content, streams, readers, and responses.
      *
@@ -150,7 +151,7 @@
 
     /**
      * Return a {@link InetSocketAddress} for the local socket.
-     * <p/>
+     * <p>
      * Warning: this can cause a DNS lookup
      *
      * @return the local socket address
@@ -181,6 +182,7 @@
     }
 
     /**
+     * @return the principal
      * @deprecated use {@link #getUserPrincipal()} instead
      */
     @Deprecated
@@ -229,7 +231,7 @@
 
     /**
      * Return a {@link InetSocketAddress} for the remote socket.
-     * <p/>
+     * <p>
      * Warning: this can cause a DNS lookup
      *
      * @return the remote socket address
@@ -251,7 +253,7 @@
 
     /**
      * Return the HttpSession if it exists.
-     * <p/>
+     * <p>
      * Note: this is equivalent to {@link HttpServletRequest#getSession(boolean)}
      * and will not create a new HttpSession.
      */
diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java
index 122268e..a6c5021 100644
--- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java
+++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
+
 import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.websocket.api.UpgradeResponse;
diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketCreator.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketCreator.java
index ed9dc1b..45d6035 100644
--- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketCreator.java
+++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketCreator.java
@@ -24,7 +24,7 @@
  * Should you desire filtering of the WebSocket object creation due to criteria such as origin or sub-protocol, then you will be required to implement a custom
  * WebSocketCreator implementation.
  * <p>
- * This has been moved from the WebSocketServlet to a standalone class managed by the WebSocketServerFactory due to need of WebSocket {@link Extension}s that
+ * This has been moved from the WebSocketServlet to a standalone class managed by the WebSocketServerFactory due to need of WebSocket {@link org.eclipse.jetty.websocket.api.extensions.Extension}s that
  * require the ability to create new websockets (such as the mux extension)
  */
 public interface WebSocketCreator
diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java
index f4b71d6..b53031f 100644
--- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java
+++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java
@@ -20,6 +20,7 @@
 
 import java.io.IOException;
 
+import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -27,6 +28,7 @@
 
 import org.eclipse.jetty.websocket.api.WebSocketBehavior;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
 
 /**
  * Abstract Servlet used to bridge the Servlet API to the WebSocket API.
@@ -88,7 +90,14 @@
     @Override
     public void destroy()
     {
-        factory.cleanup();
+        try
+        {
+            factory.stop();
+        }
+        catch (Exception ignore)
+        {
+            // ignore;
+        }
     }
 
     /**
@@ -124,14 +133,15 @@
             {
                 policy.setInputBufferSize(Integer.parseInt(max));
             }
-
-            factory = WebSocketServletFactory.Loader.create(policy);
+    
+            ServletContext ctx = getServletContext();
+            factory = WebSocketServletFactory.Loader.load(ctx, policy);
 
             configure(factory);
-
-            factory.init();
             
-            getServletContext().setAttribute(WebSocketServletFactory.class.getName(),factory);
+            factory.start();
+            
+            ctx.setAttribute(WebSocketServletFactory.class.getName(),factory);
         }
         catch (Exception x)
         {
diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.java
index 7a73101..28cc4a6 100644
--- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.java
+++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.java
@@ -19,13 +19,15 @@
 package org.eclipse.jetty.websocket.servlet;
 
 import java.io.IOException;
-import java.util.Iterator;
-import java.util.ServiceLoader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 
+import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
 
 /**
@@ -33,77 +35,60 @@
  */
 public interface WebSocketServletFactory
 {
-    public static class Loader
+    class Loader
     {
-        private static WebSocketServletFactory INSTANCE;
-
-        public static WebSocketServletFactory create(WebSocketPolicy policy) throws ClassNotFoundException, InstantiationException, IllegalAccessException
+        final static String DEFAULT_IMPL = "org.eclipse.jetty.websocket.server.WebSocketServerFactory";
+        
+        public static WebSocketServletFactory load(ServletContext ctx, WebSocketPolicy policy)
         {
-            return load(policy).createFactory(policy);
-        }
-
-        public static WebSocketServletFactory load(WebSocketPolicy policy) throws ClassNotFoundException, InstantiationException, IllegalAccessException
-        {
-            if (INSTANCE != null)
+            try
             {
-                return INSTANCE;
+                Class<? extends WebSocketServletFactory> wsClazz =
+                        (Class<? extends WebSocketServletFactory>) Class.forName(DEFAULT_IMPL,true,Thread.currentThread().getContextClassLoader());
+                Constructor<? extends WebSocketServletFactory> ctor = wsClazz.getDeclaredConstructor(new Class<?>[]{ServletContext.class, WebSocketPolicy.class});
+                return ctor.newInstance(ctx, policy);
             }
-            WebSocketServletFactory baseFactory;
-            Iterator<WebSocketServletFactory> factories = ServiceLoader.load(WebSocketServletFactory.class).iterator();
-
-            if (factories.hasNext())
+            catch (ClassNotFoundException e)
             {
-                baseFactory = factories.next();
+                throw new RuntimeException("Unable to load " + DEFAULT_IMPL, e);
             }
-            else
+            catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e)
             {
-                // Load the default class if ServiceLoader mechanism isn't valid in this environment. (such as OSGi)
-                ClassLoader loader = Thread.currentThread().getContextClassLoader();
-                @SuppressWarnings("unchecked")
-                Class<WebSocketServletFactory> wssf = (Class<WebSocketServletFactory>)loader
-                        .loadClass("org.eclipse.jetty.websocket.server.WebSocketServerFactory");
-                baseFactory = wssf.newInstance();
+                throw new RuntimeException("Unable to instantiate " + DEFAULT_IMPL, e);
             }
-
-            INSTANCE = baseFactory;
-            return INSTANCE;
         }
     }
-
-    public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response) throws IOException;
-
-    public boolean acceptWebSocket(WebSocketCreator creator, HttpServletRequest request, HttpServletResponse response) throws IOException;
-
-    public void cleanup();
-
-    public WebSocketServletFactory createFactory(WebSocketPolicy policy);
-
-    public abstract WebSocketCreator getCreator();
-
-    public abstract ExtensionFactory getExtensionFactory();
-
+    
+    boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response) throws IOException;
+    
+    boolean acceptWebSocket(WebSocketCreator creator, HttpServletRequest request, HttpServletResponse response) throws IOException;
+    
+    void start() throws Exception;
+    void stop() throws Exception;
+    
+    WebSocketCreator getCreator();
+    
+    ExtensionFactory getExtensionFactory();
+    
     /**
      * Get the base policy in use for WebSockets.
      * <p>
      * Note: individual WebSocket implementations can override some of the values in here by using the {@link WebSocket &#064;WebSocket} annotation.
-     * 
+     *
      * @return the base policy
      */
-    public WebSocketPolicy getPolicy();
-
-    public void init() throws Exception;
-
-    public boolean isUpgradeRequest(HttpServletRequest request, HttpServletResponse response);
-
+    WebSocketPolicy getPolicy();
+    
+    boolean isUpgradeRequest(HttpServletRequest request, HttpServletResponse response);
+    
     /**
      * Register a websocket class pojo with the default {@link WebSocketCreator}.
      * <p>
      * Note: only required if using the default {@link WebSocketCreator} provided by this factory.
-     * 
-     * @param websocketPojo
-     *            the class to instantiate for each incoming websocket upgrade request.
+     *
+     * @param websocketPojo the class to instantiate for each incoming websocket upgrade request.
      */
-    public void register(Class<?> websocketPojo);
-
-    public abstract void setCreator(WebSocketCreator creator);
+    void register(Class<?> websocketPojo);
+    
+    void setCreator(WebSocketCreator creator);
 }
diff --git a/jetty-xml/pom.xml b/jetty-xml/pom.xml
index 6080ed9..a6386bb 100644
--- a/jetty-xml/pom.xml
+++ b/jetty-xml/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-xml</artifactId>
@@ -15,30 +15,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <executions>
-          <execution>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <!--
-        Required for OSGI
-        -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlAppendable.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlAppendable.java
index eaad494..4578cf4 100644
--- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlAppendable.java
+++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlAppendable.java
@@ -52,7 +52,7 @@
     
     public XmlAppendable(Appendable out, int indent) throws IOException
     {
-        this(out,indent,"UTF-8");
+        this(out,indent,"utf-8");
     }
     
     public XmlAppendable(Appendable out, int indent, String encoding) throws IOException
diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java
index 1af1394..11b9e07 100644
--- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java
+++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java
@@ -35,7 +35,6 @@
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -51,12 +50,12 @@
 import org.eclipse.jetty.util.ArrayQueue;
 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.component.LifeCycle;
 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.xml.XmlParser.Node;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 
@@ -80,16 +79,12 @@
 public class XmlConfiguration
 {
     private static final Logger LOG = Log.getLogger(XmlConfiguration.class);
-
     private static final Class<?>[] __primitives =
             {Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE};
-
     private static final Class<?>[] __boxedPrimitives =
             {Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Void.class};
-
     private static final Class<?>[] __supportedCollections =
-            {ArrayList.class, ArrayQueue.class, HashSet.class, Queue.class, List.class, Set.class, Collection.class,};
-
+            {ArrayList.class, ArrayQueue.class, HashSet.class, Queue.class, List.class, Set.class, Collection.class};
     private static final Iterable<ConfigurationProcessorFactory> __factoryLoader = ServiceLoader.load(ConfigurationProcessorFactory.class);
     private static final XmlParser __parser = initParser();
     private static XmlParser initParser()
@@ -98,6 +93,7 @@
         URL config60 = Loader.getResource(XmlConfiguration.class, "org/eclipse/jetty/xml/configure_6_0.dtd");
         URL config76 = Loader.getResource(XmlConfiguration.class,"org/eclipse/jetty/xml/configure_7_6.dtd");
         URL config90 = Loader.getResource(XmlConfiguration.class,"org/eclipse/jetty/xml/configure_9_0.dtd");
+        URL config93 = Loader.getResource(XmlConfiguration.class,"org/eclipse/jetty/xml/configure_9_3.dtd");
         parser.redirectEntity("configure.dtd",config90);
         parser.redirectEntity("configure_1_0.dtd",config60);
         parser.redirectEntity("configure_1_1.dtd",config60);
@@ -106,13 +102,14 @@
         parser.redirectEntity("configure_6_0.dtd",config60);
         parser.redirectEntity("configure_7_6.dtd",config76);
         parser.redirectEntity("configure_9_0.dtd",config90);
+        parser.redirectEntity("configure_9_3.dtd",config93);
 
-        parser.redirectEntity("http://jetty.mortbay.org/configure.dtd",config90);
-        parser.redirectEntity("http://jetty.eclipse.org/configure.dtd",config90);
-        parser.redirectEntity("http://www.eclipse.org/jetty/configure.dtd",config90);
+        parser.redirectEntity("http://jetty.mortbay.org/configure.dtd",config93);
+        parser.redirectEntity("http://jetty.eclipse.org/configure.dtd",config93);
+        parser.redirectEntity("http://www.eclipse.org/jetty/configure.dtd",config93);
 
-        parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN",config90);
-        parser.redirectEntity("-//Jetty//Configure//EN",config90);
+        parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN",config93);
+        parser.redirectEntity("-//Jetty//Configure//EN",config93);
 
         return parser;
     }
@@ -150,7 +147,7 @@
      */
     public XmlConfiguration(String configuration) throws SAXException, IOException
     {
-        configuration = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE Configure PUBLIC \"-//Jetty//Configure//EN\" \"http://eclipse.org/jetty/configure.dtd\">"
+        configuration = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE Configure PUBLIC \"-//Jetty//Configure//EN\" \"http://eclipse.org/jetty/configure.dtd\">"
                 + configuration;
         InputSource source = new InputSource(new StringReader(configuration));
         synchronized (__parser)
@@ -272,9 +269,11 @@
     public void initializeDefaults(Object object)
     {
     }
+    
 
     private static class JettyXmlConfiguration implements ConfigurationProcessor
     {
+        
         private String _url;
         XmlParser.Node _root;
         XmlConfiguration _configuration;
@@ -295,6 +294,9 @@
                 String loaders = (oClass.getClassLoader()==obj.getClass().getClassLoader())?"":"Object Class and type Class are from different loaders.";
                 throw new IllegalArgumentException("Object of class '"+obj.getClass().getCanonicalName()+"' is not of type '" + oClass.getCanonicalName()+"'. "+loaders+" in "+_url);
             }
+            String id=_root.getAttribute("id");
+            if (id!=null)
+                _configuration.getIdMap().put(id,obj);
             configure(obj,_root,0);
             return obj;
         }
@@ -346,11 +348,13 @@
                 }
                 catch (NoSuchMethodException x)
                 {
-                    throw new IllegalStateException("No suitable constructor on " + oClass, x);
+                    throw new IllegalStateException(String.format("No constructor %s(%s,%s) in %s",oClass,arguments,namedArgMap,_url));
                 }
             }
+            if (id!=null)
+                _configuration.getIdMap().put(id,obj);
+                
             _configuration.initializeDefaults(obj);
-
             configure(obj, _root, index);
             return obj;
         }
@@ -375,10 +379,6 @@
          */
         public void configure(Object obj, XmlParser.Node cfg, int i) throws Exception
         {
-            String id = cfg.getAttribute("id");
-            if (id != null)
-                _configuration.getIdMap().put(id,obj);
-
             // Object already constructed so skip any arguments
             for (; i < cfg.size(); i++)
             {
@@ -425,12 +425,21 @@
                         case "Array":
                             newArray(obj, node);
                             break;
+                        case "Map":
+                            newMap(obj,node);
+                            break;
                         case "Ref":
                             refObj(obj, node);
                             break;
                         case "Property":
                             propertyObj(node);
                             break;
+                        case "SystemProperty":
+                            systemPropertyObj(node);
+                            break;
+                        case "Env":
+                            envObj(node);
+                            break;
                         default:
                             throw new IllegalStateException("Unknown tag: " + tag + " in " + _url);
                     }
@@ -630,7 +639,7 @@
             Map<Object, Object> map = (Map<Object, Object>)obj;
 
             String name = node.getAttribute("name");
-            Object value = value(obj,node);
+            Object value = value(obj, node);
             map.put(name,value);
             if (LOG.isDebugEnabled())
                 LOG.debug("XML " + obj + ".put(" + name + "," + value + ")");
@@ -638,7 +647,7 @@
 
         /*
          * Call a get method. Any object returned from the call is passed to the configure method to consume the remaining elements. @param obj @param node
-         *
+         * If class attribute is given and the name is "class", then the class instance itself is returned.
          * @return @exception Exception
          */
         private Object get(Object obj, XmlParser.Node node) throws Exception
@@ -656,9 +665,17 @@
 
             try
             {
-                // try calling a getXxx method.
-                Method method = oClass.getMethod("get" + name.substring(0,1).toUpperCase(Locale.ENGLISH) + name.substring(1),(java.lang.Class[])null);
-                obj = method.invoke(obj,(java.lang.Object[])null);
+                // Handle getClass explicitly
+                if ("class".equalsIgnoreCase(name))
+                    obj=oClass;
+                else
+                {
+                    // try calling a getXxx method.
+                    Method method = oClass.getMethod("get" + name.substring(0,1).toUpperCase(Locale.ENGLISH) + name.substring(1),(java.lang.Class[])null);
+                    obj = method.invoke(obj,(java.lang.Object[])null);
+                }
+                if (id!=null)
+                    _configuration.getIdMap().put(id,obj);
                 configure(obj,node,0);
             }
             catch (NoSuchMethodException nsme)
@@ -674,8 +691,6 @@
                     throw nsme;
                 }
             }
-            if (id != null)
-                _configuration.getIdMap().put(id,obj);
             return obj;
         }
 
@@ -688,50 +703,37 @@
          */
         private Object call(Object obj, XmlParser.Node node) throws Exception
         {
-            String id = node.getAttribute("id");
-            Class<?> oClass = nodeClass(node);
-            if (oClass != null)
-                obj = null;
-            else if (obj != null)
-                oClass = obj.getClass();
-            if (oClass == null)
-                throw new IllegalArgumentException(node.toString());
+            AttrOrElementNode aoeNode=new AttrOrElementNode(obj,node,"Id","Name","Class","Arg");
+            String id = aoeNode.getString("Id");
+            String name = aoeNode.getString("Name");
+            String clazz = aoeNode.getString("Class");
+            List<Object> args = aoeNode.getList("Arg");
             
-            int size = 0;
-            int argIndex = node.size();
-            for (int i = 0; i < node.size(); i++)
+            
+            Class<?> oClass;
+            if (clazz!=null)
             {
-                Object o = node.get(i);
-                if (o instanceof String)
-                    continue;
-                if (!((XmlParser.Node)o).getTag().equals("Arg"))
-                {
-                    argIndex = i;
-                    break;
-                }
-                size++;
+                // static call
+                oClass=Loader.loadClass(XmlConfiguration.class,clazz);
+                obj=null;
             }
-
-            Object[] arg = new Object[size];
-            for (int i = 0, j = 0; j < size; i++)
+            else if (obj!=null)
             {
-                Object o = node.get(i);
-                if (o instanceof String)
-                    continue;
-                arg[j++] = value(obj,(XmlParser.Node)o);
+                oClass = obj.getClass();
             }
-
-            String method = node.getAttribute("name");
+            else
+                throw new IllegalArgumentException(node.toString());
+           
             if (LOG.isDebugEnabled())
-                LOG.debug("XML call " + method);
+                LOG.debug("XML call " + name);
 
             try
             {
-                Object n= TypeUtil.call(oClass,method,obj,arg);
+                Object nobj= TypeUtil.call(oClass,name,obj,args.toArray(new Object[args.size()]));
                 if (id != null)
-                    _configuration.getIdMap().put(id,n);
-                configure(n,node,argIndex);
-                return n;
+                    _configuration.getIdMap().put(id,nobj);
+                configure(nobj,node,aoeNode.getNext());
+                return nobj;
             }
             catch (NoSuchMethodException e)
             {
@@ -751,59 +753,44 @@
          */
         private Object newObj(Object obj, XmlParser.Node node) throws Exception
         {
-            Class<?> oClass = nodeClass(node);
-            int argIndex = node.size();
-            
-            Map<String, Object> namedArgMap = new HashMap<>();
-            List<Object> arguments = new LinkedList<>();
-            XmlParser.Node child;
-
-            // Find the <Arg> elements
-            for (int i = 0; i < node.size(); i++)
-            {
-                Object o = node.get(i);
-                if (o instanceof String)
-                {
-                    // Skip raw String nodes
-                    continue;
-                }
-                
-                child = (XmlParser.Node)o;
-                if(child.getTag().equals("Arg"))
-                {
-                    String namedAttribute = child.getAttribute("name");
-                    Object value=value(obj,child);
-                    if (namedAttribute != null)
-                    {
-                        // named arguments
-                        namedArgMap.put(namedAttribute,value);
-                    }
-                    // raw arguments
-                    arguments.add(value);
-                } else {
-                    // The first non <Arg> child is the start of 
-                    // elements that configure the class, such as
-                    // <Set> and <Call> nodes
-                    argIndex = i;
-                    break;
-                }
-            }
+            AttrOrElementNode aoeNode=new AttrOrElementNode(obj,node,"Id","Class","Arg");
+            String id = aoeNode.getString("Id");
+            String clazz = aoeNode.getString("Class");
+            List<XmlParser.Node> argNodes = aoeNode.getNodes("Arg");
 
             if (LOG.isDebugEnabled())
-                LOG.debug("XML new " + oClass);
+                LOG.debug("XML new " + clazz);
+            
+            Class<?> oClass = Loader.loadClass(XmlConfiguration.class,clazz);
+            
+            // Find the <Arg> elements
+            Map<String, Object> namedArgMap = new HashMap<>();
+            List<Object> arguments = new LinkedList<>();
+            for (XmlParser.Node child : argNodes)
+            {
+                String namedAttribute = child.getAttribute("name");
+                Object value=value(obj,child);
+                if (namedAttribute != null)
+                {
+                    // named arguments
+                    namedArgMap.put(namedAttribute,value);
+                }
+                // raw arguments
+                arguments.add(value);
+            }
 
-            Object n;
+            Object nobj;
             try
             {
                 if (namedArgMap.size() > 0)
                 {
                    LOG.debug("using named mapping");
-                   n = TypeUtil.construct(oClass, arguments.toArray(), namedArgMap);
+                   nobj = TypeUtil.construct(oClass, arguments.toArray(), namedArgMap);
                 }
                 else
                 {
                     LOG.debug("using normal mapping");
-                    n = TypeUtil.construct(oClass, arguments.toArray());
+                    nobj = TypeUtil.construct(oClass, arguments.toArray());
                 }
             }
             catch (NoSuchMethodException e)
@@ -811,9 +798,12 @@
                 throw new IllegalStateException("No suitable constructor: " + node + " on " + obj);
             }
 
-            _configuration.initializeDefaults(n);
-            configure(n,node,argIndex);
-            return n;
+            if (id != null)
+                _configuration.getIdMap().put(id, nobj);
+            
+            _configuration.initializeDefaults(nobj);
+            configure(nobj,node,aoeNode.getNext());
+            return nobj;
         }
 
         /*
@@ -838,10 +828,13 @@
          */
         private Object newArray(Object obj, XmlParser.Node node) throws Exception
         {
+            AttrOrElementNode aoeNode=new AttrOrElementNode(obj,node,"Id","Type","Item");
+            String id = aoeNode.getString("Id");
+            String type = aoeNode.getString("Type");
+            List<XmlParser.Node> items = aoeNode.getNodes("Item");
+            
             // Get the type
             Class<?> aClass = java.lang.Object.class;
-            String type = node.getAttribute("type");
-            final String id = node.getAttribute("id");
             if (type != null)
             {
                 aClass = TypeUtil.fromName(type);
@@ -864,12 +857,11 @@
                     }
                 }
             }
-
+            
             Object al = null;
 
-            for (Object nodeObject : node)
+            for (XmlParser.Node item : items)
             {
-                XmlParser.Node item = (Node)nodeObject;
                 String nid = item.getAttribute("id");
                 Object v = value(obj,item);
                 al = LazyList.add(al,(v == null && aClass.isPrimitive())?0:v);
@@ -888,17 +880,16 @@
          */
         private Object newMap(Object obj, XmlParser.Node node) throws Exception
         {
-            String id = node.getAttribute("id");
+            AttrOrElementNode aoeNode=new AttrOrElementNode(node,"Id","Entry");
+            String id = aoeNode.getString("Id");
+            List<XmlParser.Node> entries = aoeNode.getNodes("Entry");
 
             Map<Object, Object> map = new HashMap<>();
             if (id != null)
-                _configuration.getIdMap().put(id,map);
+                _configuration.getIdMap().put(id, map);
 
-            for (Object o : node)
+            for (XmlParser.Node entry : entries)
             {
-                if (o instanceof String)
-                    continue;
-                XmlParser.Node entry = (XmlParser.Node)o;
                 if (!entry.getTag().equals("Entry"))
                     throw new IllegalStateException("Not an Entry");
 
@@ -945,20 +936,143 @@
          */
         private Object propertyObj(XmlParser.Node node) throws Exception
         {
-            String id = node.getAttribute("id");
-            String name = node.getAttribute("name");
-            String defaultValue = node.getAttribute("default");
-            Object prop;
-            Map<String,String> property_map=_configuration.getProperties();
-            if (property_map != null && property_map.containsKey(name))
-                prop = property_map.get(name);
-            else
-                prop = defaultValue;
+            AttrOrElementNode aoeNode=new AttrOrElementNode(node,"Id","Name","Deprecated","Default");
+            String id = aoeNode.getString("Id");
+            String name = aoeNode.getString("Name",true);
+            List<Object> deprecated = aoeNode.getList("Deprecated");
+            String dftValue = aoeNode.getString("Default");
+
+            // Look for a value
+            Map<String,String> properties = _configuration.getProperties();
+            String value = properties.get(name);
+            
+            // Look for a deprecated name value
+
+            String alternate=null;
+            if (!deprecated.isEmpty())
+            {
+                for (Object d : deprecated)
+                { 
+                    String v = properties.get(StringUtil.valueOf(d));
+                    if (v!=null)
+                    {
+                        if (value==null)
+                            LOG.warn("Property '{}' is deprecated, use '{}' instead", d, name);
+                        else
+                            LOG.warn("Property '{}' is deprecated, value from '{}' used", d, name);
+                    }
+                    if (alternate==null)
+                        alternate=v;;
+                }
+            }
+
+            // use alternate from deprecated
+            if (value==null)
+                value=alternate;
+            
+            // use default value
+            if (value==null)
+                value=dftValue;
+
+            // Set value if ID set
             if (id != null)
-                _configuration.getIdMap().put(id,prop);
-            if (prop != null)
-                configure(prop,node,0);
-            return prop;
+                _configuration.getIdMap().put(id, value);
+            return value;
+        }
+
+        /*
+         * Get a SystemProperty.
+         *
+         * @param node
+         * @return
+         * @exception Exception
+         */
+        private Object systemPropertyObj(XmlParser.Node node) throws Exception
+        {
+            AttrOrElementNode aoeNode=new AttrOrElementNode(node,"Id","Name","Deprecated","Default");
+            String id = aoeNode.getString("Id");
+            String name = aoeNode.getString("Name",true);
+            List<Object> deprecated = aoeNode.getList("Deprecated");
+            String dftValue = aoeNode.getString("Default");
+
+            // Look for a value
+            String value = System.getProperty(name);
+            
+            // Look for a deprecated name value
+            String alternate=null;
+            if (!deprecated.isEmpty())
+            {
+                for (Object d : deprecated)
+                { 
+                    String v = System.getProperty(StringUtil.valueOf(d));
+                    if (v!=null)
+                    {
+                        if (value==null)
+                            LOG.warn("SystemProperty '{}' is deprecated, use '{}' instead", d, name);
+                        else
+                            LOG.warn("SystemProperty '{}' is deprecated, value from '{}' used", d, name);
+                    }
+                    if (alternate==null)
+                        alternate=v;;
+                }
+            }
+
+            // use alternate from deprecated
+            if (value==null)
+                value=alternate;
+            
+            // use default value
+            if (value==null)
+                value=dftValue;
+
+            // Set value if ID set
+            if (id != null)
+                _configuration.getIdMap().put(id, value);
+
+            return value;
+        }
+        
+        /*
+         * Get a Environment Property.
+         *
+         * @param node
+         * @return
+         * @exception Exception
+         */
+        private Object envObj(XmlParser.Node node) throws Exception
+        {
+            AttrOrElementNode aoeNode=new AttrOrElementNode(node,"Id","Name","Deprecated","Default");
+            String id = aoeNode.getString("Id");
+            String name = aoeNode.getString("Name",true);
+            List<Object> deprecated = aoeNode.getList("Deprecated");
+            String dftValue = aoeNode.getString("Default");
+
+            // Look for a value
+            String value = System.getenv(name);
+            
+            // Look for a deprecated name value
+            if (value==null && !deprecated.isEmpty())
+            {
+                for (Object d : deprecated)
+                {
+                    value = System.getenv(StringUtil.valueOf(d));
+                    if (value!=null)
+                    {
+                        LOG.warn("Property '{}' is deprecated, use '{}' instead", d, name);
+                        break;
+                    }
+                }
+            }
+            
+            // use default value
+            if (value==null)
+                value=dftValue;
+
+            // Set value if ID set
+            if (id != null)
+                _configuration.getIdMap().put(id, value);
+
+            return value;
         }
 
         /*
@@ -1132,22 +1246,175 @@
             if ("Property".equals(tag))
                 return propertyObj(node);
             if ("SystemProperty".equals(tag))
-            {
-                String name = node.getAttribute("name");
-                String defaultValue = node.getAttribute("default");
-                return System.getProperty(name,defaultValue);
-            }
+                return systemPropertyObj(node);
             if ("Env".equals(tag))
-            {
-                String name = node.getAttribute("name");
-                String defaultValue = node.getAttribute("default");
-                String value=System.getenv(name);
-                return value==null?defaultValue:value;
-            }
+                return envObj(node);
 
             LOG.warn("Unknown value tag: " + node,new Throwable());
             return null;
         }
+        
+
+        private class AttrOrElementNode
+        {
+            final Object _obj;
+            final XmlParser.Node _node;
+            final Set<String> _elements = new HashSet<>();
+            final int _next;
+
+            AttrOrElementNode(XmlParser.Node node,String... elements )
+            {
+                this(null,node,elements);
+            }
+            
+            AttrOrElementNode(Object obj, XmlParser.Node node,String... elements )
+            {
+                _obj=obj;
+                _node=node;
+                for (String e:elements)
+                    _elements.add(e);
+                
+                int next=0;
+                for (Object o: _node)
+                {
+                    if (o instanceof String)
+                    {
+                        if (((String)o).trim().length()==0)
+                        {
+                            next++;
+                            continue;
+                        }
+                        break;
+                    }
+                    
+                    if (!(o instanceof XmlParser.Node))
+                        break;
+                    
+                    XmlParser.Node n = (XmlParser.Node)o;
+                    if (!_elements.contains(n.getTag()))
+                        break;
+                    
+                    next++;
+                }
+                _next=next;
+            }
+
+            public int getNext()
+            {
+                return _next;
+            }
+
+            public String getString(String elementName) throws Exception
+            {
+                return StringUtil.valueOf(get(elementName,false));
+            }
+            
+            public String getString(String elementName, boolean manditory) throws Exception
+            {
+                return StringUtil.valueOf(get(elementName,manditory));
+            }
+            
+            public Object get(String elementName, boolean manditory) throws Exception
+            {
+                String attrName=StringUtil.asciiToLowerCase(elementName);
+                String attr = _node.getAttribute(attrName);
+                Object value=attr;
+                
+                for (int i=0;i<_next;i++)
+                {
+                    Object o = _node.get(i);
+                    if (!(o instanceof XmlParser.Node))
+                        continue;
+                    XmlParser.Node n = (XmlParser.Node)o;
+                    if (elementName.equals(n.getTag()))
+                    {
+                        if (attr!=null)
+                            throw new IllegalStateException("Cannot have attr '"+attrName+"' and element '"+elementName+"'");
+
+                        value=value(_obj,n);
+                        break;
+                    }
+                }
+                
+                if (manditory && value==null)
+                    throw new IllegalStateException("Must have attr '"+attrName+"' or element '"+elementName+"'");
+                
+                return value;
+            }
+
+            public List<Object> getList(String elementName) throws Exception
+            {
+                return getList(elementName,false);
+            }
+            
+            public List<Object> getList(String elementName, boolean manditory) throws Exception
+            {
+                String attrName=StringUtil.asciiToLowerCase(elementName);
+                final List<Object> values=new ArrayList<>();
+                
+                String attr = _node.getAttribute(attrName);
+                if (attr!=null)
+                    values.addAll(StringUtil.csvSplit(null,attr,0,attr.length()));
+
+
+                for (int i=0;i<_next;i++)
+                {
+                    Object o = _node.get(i);
+                    if (!(o instanceof XmlParser.Node))
+                        continue;
+                    XmlParser.Node n = (XmlParser.Node)o;
+                    
+                    if (elementName.equals(n.getTag()))
+                    {
+                        if (attr!=null)
+                            throw new IllegalStateException("Cannot have attr '"+attrName+"' and element '"+elementName+"'");
+
+                        values.add(value(_obj,n));
+                    }
+                }
+                
+                if (manditory && values.isEmpty())
+                    throw new IllegalStateException("Must have attr '"+attrName+"' or element '"+elementName+"'");
+
+                return values;
+            }
+            
+            public List<XmlParser.Node> getNodes(String elementName) throws Exception
+            {
+                String attrName=StringUtil.asciiToLowerCase(elementName);
+                final List<XmlParser.Node> values=new ArrayList<>();
+                
+                String attr = _node.getAttribute(attrName);
+                if (attr!=null)
+                {
+                    for (String a : StringUtil.csvSplit(null,attr,0,attr.length()))
+                    {
+                        // create a fake node
+                        XmlParser.Node n = new XmlParser.Node(null,elementName,null);
+                        n.add(a);
+                        values.add(n);
+                    }
+                }
+
+                for (int i=0;i<_next;i++)
+                {
+                    Object o = _node.get(i);
+                    if (!(o instanceof XmlParser.Node))
+                        continue;
+                    XmlParser.Node n = (XmlParser.Node)o;
+                    
+                    if (elementName.equals(n.getTag()))
+                    {
+                        if (attr!=null)
+                            throw new IllegalStateException("Cannot have attr '"+attrName+"' and element '"+elementName+"'");
+
+                        values.add(n);
+                    }
+                }
+
+                return values;
+            }
+        }
     }
 
     /**
@@ -1177,7 +1444,6 @@
             {
                 try
                 {
-
                     Properties properties = null;
 
                     // Look for properties from start.jar
@@ -1199,14 +1465,9 @@
                     // If no start.config properties, use clean slate
                     if (properties == null)
                     {
-                        properties = new Properties();
                         // Add System Properties
-                        Enumeration<?> ensysprop = System.getProperties().propertyNames();
-                        while (ensysprop.hasMoreElements())
-                        {
-                            String name = (String)ensysprop.nextElement();
-                            properties.put(name,System.getProperty(name));
-                        }
+                        properties = new Properties();
+                        properties.putAll(System.getProperties());
                     }
 
                     // For all arguments, load properties
@@ -1228,7 +1489,7 @@
                     {
                         if (!args[i].toLowerCase(Locale.ENGLISH).endsWith(".properties") && (args[i].indexOf('=')<0))
                         {
-                            XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(args[i]).getURL());
+                            XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(args[i]).getURI().toURL());
                             if (last != null)
                                 configuration.getIdMap().putAll(last.getIdMap());
                             if (properties.size() > 0)
diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlParser.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlParser.java
index 05642f4..87c734f 100644
--- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlParser.java
+++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlParser.java
@@ -50,11 +50,9 @@
 /**
  * XML Parser wrapper. This class wraps any standard JAXP1.1 parser with convieniant error and
  * entity handlers and a mini dom-like document tree.
- * <P>
+ * <p>
  * By default, the parser is created as a validating parser only if xerces is present. This can be
  * configured by setting the "org.eclipse.jetty.xml.XmlParser.Validating" system property.
- *
- *
  */
 public class XmlParser
 {
@@ -82,9 +80,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * Constructor.
-     */
     public XmlParser(boolean validating)
     {
         setValidating(validating);
@@ -139,10 +134,6 @@
     }
     
     /* ------------------------------------------------------------ */
-    /**
-     * @param name
-     * @param entity
-     */
     public synchronized void redirectEntity(String name, URL entity)
     {
         if (entity != null)
@@ -218,6 +209,10 @@
     /* ------------------------------------------------------------ */
     /**
      * Parse String URL.
+     * @param url the url to the xml to parse
+     * @return the root node of the xml
+     * @throws IOException if unable to load the xml
+     * @throws SAXException if unable to parse the xml
      */
     public synchronized Node parse(String url) throws IOException, SAXException
     {
@@ -229,6 +224,10 @@
     /* ------------------------------------------------------------ */
     /**
      * Parse File.
+     * @param file the file to the xml to parse 
+     * @return the root node of the xml
+     * @throws IOException if unable to load the xml
+     * @throws SAXException if unable to parse the xml
      */
     public synchronized Node parse(File file) throws IOException, SAXException
     {
@@ -240,6 +239,10 @@
     /* ------------------------------------------------------------ */
     /**
      * Parse InputStream.
+     * @param in the input stream of the xml to parse
+     * @return the root node of the xml
+     * @throws IOException if unable to load the xml
+     * @throws SAXException if unable to parse the xml
      */
     public synchronized Node parse(InputStream in) throws IOException, SAXException
     {
@@ -557,6 +560,7 @@
         /* ------------------------------------------------------------ */
         /**
          * Get an array of element attributes.
+         * @return the attributes
          */
         public Attribute[] getAttributes()
         {
@@ -566,7 +570,8 @@
         /* ------------------------------------------------------------ */
         /**
          * Get an element attribute.
-         *
+         * 
+         * @param name the name of the attribute 
          * @return attribute or null.
          */
         public String getAttribute(String name)
@@ -577,7 +582,9 @@
         /* ------------------------------------------------------------ */
         /**
          * Get an element attribute.
-         *
+         * 
+         * @param name the name of the element 
+         * @param dft the default value
          * @return attribute or null.
          */
         public String getAttribute(String name, String dft)
@@ -618,7 +625,7 @@
         /**
          * Get the first child node with the tag.
          *
-         * @param tag
+         * @param tag the name of the tag
          * @return Node or null.
          */
         public Node get(String tag)
@@ -702,6 +709,7 @@
          * Convert to a string.
          *
          * @param tag If false, only _content is shown.
+         * @return the string value
          */
         public synchronized String toString(boolean tag)
         {
@@ -715,6 +723,8 @@
          * Convert to a string.
          *
          * @param tag If false, only _content is shown.
+         * @param trim true to trim the content
+         * @return the trimmed content
          */
         public synchronized String toString(boolean tag, boolean trim)
         {
diff --git a/jetty-xml/src/main/resources/org/eclipse/jetty/xml/configure_9_0.dtd b/jetty-xml/src/main/resources/org/eclipse/jetty/xml/configure_9_0.dtd
index 542bc8c..7d6045b 100644
--- a/jetty-xml/src/main/resources/org/eclipse/jetty/xml/configure_9_0.dtd
+++ b/jetty-xml/src/main/resources/org/eclipse/jetty/xml/configure_9_0.dtd
@@ -274,15 +274,6 @@
 This element allows arbitrary properties to be retrieved by name.
 The name attribute specifies the property name and the optional
 default argument provides a default value.
-
-A Property element can contain a sequence of elements such as Set, Put, Call, etc.
-which act on the retrieved object:
-
- <Property name="Server">
-   <Call id="jdbcIdMgr" name="getAttribute">
-     <Arg>jdbcIdMgr</Arg>
-   </Call>
- </Property>
 -->
 <!ELEMENT Property (%CONFIG;)* >
 <!ATTLIST Property %NAMEATTR; %DEFAULTATTR; %IDATTR; >
diff --git a/jetty-xml/src/main/resources/org/eclipse/jetty/xml/configure_9_3.dtd b/jetty-xml/src/main/resources/org/eclipse/jetty/xml/configure_9_3.dtd
new file mode 100644
index 0000000..b4f7c1d
--- /dev/null
+++ b/jetty-xml/src/main/resources/org/eclipse/jetty/xml/configure_9_3.dtd
@@ -0,0 +1,323 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!--
+This is the document type descriptor for the
+org.eclipse.jetty.xml.XmlConfiguration class.  It allows a java object to be
+configured by with a sequence of Set, Put and Call elements.  These tags are
+mapped to methods on the object to be configured as follows:
+
+  <Set  name="Test">value</Set>              ==  obj.setTest("value");
+  <Put  name="Test">value</Put>              ==  obj.put("Test","value");
+  <Call name="test"><Arg>value</Arg></Call>  ==  obj.test("value");
+
+Values themselves may be configured objects that are created with the
+<New> tag or returned from a <Call> tag.
+
+Values are matched to arguments on a best effort approach, but types
+my be specified if a match is not achieved.
+-->
+
+<!ENTITY % CONFIG "Set|Get|Put|Call|New|Ref|Array|Map|Property">
+<!ENTITY % VALUE "#PCDATA|Get|Call|New|Ref|Array|Map|SystemProperty|Env|Property">
+
+<!ENTITY % TYPEATTR "type CDATA #IMPLIED " > <!-- String|Character|Short|Byte|Integer|Long|Boolean|Float|Double|char|short|byte|int|long|boolean|float|double|URL|InetAddress|InetAddrPort| #classname -->
+<!ENTITY % IMPLIEDCLASSATTR "class CDATA #IMPLIED" >
+<!ENTITY % CLASSATTR "class CDATA #REQUIRED" >
+<!ENTITY % NAMEATTR "name CDATA #REQUIRED" >
+<!ENTITY % IMPLIEDNAMEATTR "name CDATA #IMPLIED" >
+<!ENTITY % DEPRECATEDATTR "deprecated CDATA #IMPLIED" >
+<!ENTITY % DEFAULTATTR "default CDATA #IMPLIED" >
+<!ENTITY % IDATTR "id ID #IMPLIED" >
+<!ENTITY % ARGATTR "arg CDATA #IMPLIED" >
+<!ENTITY % ITEMATTR "item CDATA #IMPLIED" >
+<!ENTITY % REFATTR "refid CDATA #IMPLIED" >
+<!ENTITY % REQUIREDIDATTR "id ID #REQUIRED" >
+
+
+<!--
+Configure Element.
+This is the root element that specifies the class of object that
+can be configured:
+
+    <Configure class="com.acme.MyClass"> ... </Configure>
+-->
+<!ELEMENT Configure (Arg*,(%CONFIG;)*) >
+<!ATTLIST Configure %IMPLIEDCLASSATTR; %IDATTR; >
+
+
+<!--
+Set Element.
+This element maps to a call to a setter method or field on the current object.
+The name and optional type attributes are used to select the setter
+method. If the name given is xxx, then a setXxx method is used, or
+the xxx field is used of setXxx cannot be found.
+A Set element can contain value text and/or the value objects returned
+by other elements such as Call, New, SystemProperty, etc.
+If no value type is specified, then white
+space is trimmed out of the value. If it contains multiple value
+elements they are added as strings before being converted to any
+specified type.
+
+A Set with a class attribute is treated as a static set method invocation.
+-->
+<!ELEMENT Set (%VALUE;)* >
+<!ATTLIST Set %NAMEATTR; %TYPEATTR; %IMPLIEDCLASSATTR; >
+
+
+<!--
+Get Element.
+This element maps to a call to a getter method or field on the current object.
+The name attribute is used to select the get method.
+If the name given is xxx, then a getXxx method is used, or
+the xxx field is used if getXxx cannot be found.
+A Get element can contain other elements such as Set, Put, Call, etc.
+which act on the object returned by the get call.
+
+A Get with a class attribute is treated as a static get method or field.
+-->
+<!ELEMENT Get (%CONFIG;)* >
+<!ATTLIST Get %NAMEATTR; %IMPLIEDCLASSATTR; %IDATTR; >
+
+
+<!--
+Put Element.
+This element maps to a call to a put method on the current object,
+which must implement the Map interface. The name attribute is used
+as the put key and the optional type attribute can force the type
+of the value.
+
+A Put element can contain value text and/or value elements such as Call,
+New, SystemProperty, etc. If no value type is specified, then white
+space is trimmed out of the value. If it contains multiple value
+elements they are added as strings before being converted to any
+specified type.
+-->
+<!ELEMENT Put (%VALUE;)* >
+<!ATTLIST Put %NAMEATTR; %TYPEATTR; >
+
+
+<!--
+Id Element.
+This element is the equivalent of the id attribute.
+-->
+<!ELEMENT Id (%VALUE;)* >
+
+
+<!--
+Name element.
+This element is the equivalent of the name attribute.
+-->
+<!ELEMENT Name (%VALUE;)* >
+
+
+<!--
+Deprecated element.
+This element is the equivalent of the deprecated attribute.
+-->
+<!ELEMENT Deprecated (%VALUE;)* >
+
+
+<!--
+Default element.
+This element is the equivalent of the default attribute.
+-->
+<!ELEMENT Default (%VALUE;)* >
+
+
+<!--
+Class element.
+This element is the equivalent of the class attribute.
+-->
+<!ELEMENT Class (%VALUE;)* >
+
+
+<!--
+Type element.
+This element is the equivalent of the type attribute.
+-->
+<!ELEMENT Type (%VALUE;)* >
+
+
+<!--
+Call Element.
+This element maps to an arbitrary call to a method on the current object,
+The name attribute and Arg elements are used to select the method.
+
+A Call element can contain a sequence of Arg elements followed by
+a sequence of other elements such as Set, Put, Call, etc. which act on any object
+returned by the original call:
+
+ <Call id="o2" name="test">
+   <Arg>value1</Arg>
+   <Set name="Test">Value2</Set>
+ </Call>
+
+This is equivalent to:
+
+ Object o2 = o1.test("value1");
+ o2.setTest("value2");
+
+A Call with a class attribute is treated as a static call.
+-->
+<!ELEMENT Call (Id?,Name?,Class?,Arg*,(%CONFIG;)*) >
+<!ATTLIST Call %ARGATTR; %IMPLIEDNAMEATTR; %IMPLIEDCLASSATTR; %IDATTR; >
+
+
+<!--
+Arg Element.
+This element defines a positional or optional named argument for the
+Call and New elements. The optional type attribute can force the type
+of the value.
+
+An Arg element can contain value text and/or value elements such as Call,
+New, SystemProperty, etc. If no value type is specified, then white
+space is trimmed out of the value. If it contains multiple value
+elements they are added as strings before being converted to any
+specified type.
+-->
+<!ELEMENT Arg (%VALUE;)* >
+<!ATTLIST Arg %TYPEATTR; %IMPLIEDNAMEATTR; >
+
+
+<!--
+New Element.
+This element allows the creation of a new object as part of a
+value for elements such as Set, Put, Arg, etc. The class attribute
+determines the type of the new object and the contained Arg elements
+are used to select the constructor for the new object.
+
+A New element can contain a sequence of Arg elements followed by
+a sequence of elements such as Set, Put, Call, etc. elements
+which act on the new object:
+
+ <New id="o" class="com.acme.MyClass">
+   <Arg>value1</Arg>
+   <Set name="test">Value2</Set>
+ </New>
+
+This is equivalent to:
+
+ Object o = new com.acme.MyClass("value1");
+ o.setTest("Value2");
+-->
+<!ELEMENT New (Id?,Name?,Class?,Arg*,(%CONFIG;)*) >
+<!ATTLIST New %IDATTR; %IMPLIEDCLASSATTR; %ARGATTR; >
+
+
+<!--
+Ref Element.
+This element allows a previously created object to be referenced by id.  The
+attribute refid is used to specify the id of another object (the attribute id can
+also be used, but it's use is deprecated).
+A Ref element can contain a sequence of elements such as Set, Put, Call, etc.
+which act on the referenced object.
+
+ <Ref refid="myobject">
+   <Set name="Test">Value2</Set>
+ </New>
+-->
+<!ELEMENT Ref (%CONFIG;)* >
+<!ATTLIST Ref %IDATTR; %REFATTR;>
+
+
+<!--
+Array Element.
+This element allows the creation of a new array as part of a
+value of elements such as Set, Put, Arg, etc. The type attribute determines
+the type of the new array and the contained Item elements
+are used for each element of the array:
+
+ <Array type="java.lang.String">
+   <Item>value0</Item>
+   <Item><New class="java.lang.String"><Arg>value1</Arg></New></Item>
+ </Array>
+
+This is equivalent to:
+ String[] a = new String[] { "value0", new String("value1") };
+-->
+<!ELEMENT Array (Id?,Type?,Item*) >
+<!ATTLIST Array %IDATTR;%TYPEATTR;%ITEMATTR; >
+
+
+<!--
+Map Element.
+This element allows the creation of a new map as part of a
+value of elements such as Set, Put, Arg, etc. The type attribute determines
+the type of the new array and the contained Item elements
+are used for each element of the array:
+
+ <Map>
+   <Entry>
+     <Item>keyName</Item>
+     <Item><New class="java.lang.String"><Arg>value1</Arg></New></Item>
+   </Entry>
+ </Map>
+
+This is equivalent to:
+ Map m = new HashMap();
+ m.put("keyName", new String("value1"));
+-->
+<!ELEMENT Map (Id?,Entry*) >
+<!ATTLIST Map %IDATTR; >
+<!ELEMENT Entry (Item,Item) >
+
+
+<!--
+Item Element.
+This element defines an entry for the Array or Map Entry elements.
+The optional type attribute can force the type of the value.
+
+An Item element can contain value text and/or the value object of
+elements such as Call, New, SystemProperty, etc. If no value type
+is specified, then white space is trimmed out of the value.
+If it contains multiple value elements they are added as strings
+before being converted to any specified type.
+-->
+<!ELEMENT Item (%VALUE;)* >
+<!ATTLIST Item %TYPEATTR; %IDATTR; >
+
+
+<!--
+System Property Element.
+This element allows JVM System properties to be retrieved as
+part of the value of elements such as Set, Put, Arg, etc.
+The name attribute specifies the property name and the optional
+default argument provides a default value.
+
+ <SystemProperty name="Test" default="value" />
+
+This is equivalent to:
+
+ System.getProperty("Test","value");
+-->
+<!ELEMENT SystemProperty (Id?,Name?,Deprecated*,Default?) >
+<!ATTLIST SystemProperty %IMPLIEDNAMEATTR; %DEFAULTATTR; %DEPRECATEDATTR; %IDATTR; >
+
+
+<!--
+Environment variable Element.
+This element allows OS Environment variables to be retrieved as
+part of the value of elements such as Set, Put, Arg, etc.
+The name attribute specifies the env variable name and the optional
+default argument provides a default value.
+
+ <Env name="Test" default="value" />
+
+This is equivalent to:
+
+ String v=System.getEnv("Test");
+ if (v==null) v="value";
+
+-->
+<!ELEMENT Env (Id?,Name?,Deprecated*,Default?) >
+<!ATTLIST Env %IMPLIEDNAMEATTR; %DEFAULTATTR; %DEPRECATEDATTR; %IDATTR; >
+
+
+<!--
+Property Element.
+This element allows arbitrary properties to be retrieved by name.
+The name attribute specifies the property name and the optional
+default argument provides a default value.
+-->
+<!ELEMENT Property (Id?,Name?,Deprecated*,Default?) >
+<!ATTLIST Property %IMPLIEDNAMEATTR; %DEFAULTATTR; %DEPRECATEDATTR; %IDATTR; >
diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlAppendableTest.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlAppendableTest.java
index 3fee2d3..a7e578a 100644
--- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlAppendableTest.java
+++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlAppendableTest.java
@@ -49,7 +49,7 @@
         out.closeTag();
 
         String expected = "" +
-                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                 "<test>\n" +
                 "  <tag/>\n" +
                 "  <tag name=\"attr value\" noval=\"\" quotes=\"&apos;&quot;\"/>\n" +
diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java
index 96536dd..b1349f3 100644
--- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java
+++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java
@@ -18,6 +18,18 @@
 
 package org.eclipse.jetty.xml;
 
+import java.io.ByteArrayInputStream;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.CoreMatchers.nullValue;
@@ -25,20 +37,9 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
-import java.io.ByteArrayInputStream;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
 public class XmlConfigurationTest
 {
-    protected String _configure="org/eclipse/jetty/xml/configure.xml";
+    protected String[] _configure=new String [] {"org/eclipse/jetty/xml/configureWithAttr.xml","org/eclipse/jetty/xml/configureWithElements.xml"};
 
     private static final String STRING_ARRAY_XML = "<Array type=\"String\"><Item type=\"String\">String1</Item><Item type=\"String\">String2</Item></Array>";
     private static final String INT_ARRAY_XML = "<Array type=\"int\"><Item type=\"int\">1</Item><Item type=\"int\">2</Item></Array>";
@@ -54,173 +55,197 @@
     @Test
     public void testPassedObject() throws Exception
     {
-        TestConfiguration.VALUE=77;
-        Map<String,String> properties = new HashMap<>();
-        properties.put("whatever", "xxx");
+        for (String configure : _configure)
+        {
+            Map<String,String> properties = new HashMap<>();
+            properties.put("whatever", "xxx");
+            TestConfiguration.VALUE=77;
+            URL url = XmlConfigurationTest.class.getClassLoader().getResource(configure);
+            XmlConfiguration configuration = new XmlConfiguration(url);
+            TestConfiguration tc = new TestConfiguration("tc");
+            configuration.getProperties().putAll(properties);
+            configuration.configure(tc);
 
-        URL url = XmlConfigurationTest.class.getClassLoader().getResource(_configure);
-        XmlConfiguration configuration = new XmlConfiguration(url);
-        TestConfiguration tc = new TestConfiguration("tc");
-        configuration.getProperties().putAll(properties);
-        configuration.configure(tc);
+            assertEquals("Set String","SetValue",tc.testObject);
+            assertEquals("Set Type",2,tc.testInt);
 
-        assertEquals("Set String","SetValue",tc.testObject);
-        assertEquals("Set Type",2,tc.testInt);
+            assertEquals(18080, tc.propValue);
 
-        assertEquals(18080, tc.propValue);
+            assertEquals("Put","PutValue",tc.get("Test"));
+            assertEquals("Put dft","2",tc.get("TestDft"));
+            assertEquals("Put type",2,tc.get("TestInt"));
 
-        assertEquals("Put","PutValue",tc.get("Test"));
-        assertEquals("Put dft","2",tc.get("TestDft"));
-        assertEquals("Put type",2,tc.get("TestInt"));
+            assertEquals("Trim","PutValue",tc.get("Trim"));
+            assertEquals("Null",null,tc.get("Null"));
+            assertEquals("NullTrim",null,tc.get("NullTrim"));
 
-        assertEquals("Trim","PutValue",tc.get("Trim"));
-        assertEquals("Null",null,tc.get("Null"));
-        assertEquals("NullTrim",null,tc.get("NullTrim"));
+            assertEquals("ObjectTrim",1.2345,tc.get("ObjectTrim"));
+            assertEquals("Objects","-1String",tc.get("Objects"));
+            assertEquals( "ObjectsTrim", "-1String",tc.get("ObjectsTrim"));
+            assertEquals( "String", "\n    PutValue\n  ",tc.get("String"));
+            assertEquals( "NullString", "",tc.get("NullString"));
+            assertEquals( "WhiteSpace", "\n  ",tc.get("WhiteSpace"));
+            assertEquals( "ObjectString", "\n    1.2345\n  ",tc.get("ObjectString"));
+            assertEquals( "ObjectsString", "-1String",tc.get("ObjectsString"));
+            assertEquals( "ObjectsWhiteString", "-1\n  String",tc.get("ObjectsWhiteString"));
 
-        assertEquals("ObjectTrim",1.2345,tc.get("ObjectTrim"));
-        assertEquals("Objects","-1String",tc.get("Objects"));
-        assertEquals( "ObjectsTrim", "-1String",tc.get("ObjectsTrim"));
-        assertEquals( "String", "\n    PutValue\n  ",tc.get("String"));
-        assertEquals( "NullString", "",tc.get("NullString"));
-        assertEquals( "WhiteSpace", "\n  ",tc.get("WhiteSpace"));
-        assertEquals( "ObjectString", "\n    1.2345\n  ",tc.get("ObjectString"));
-        assertEquals( "ObjectsString", "-1String",tc.get("ObjectsString"));
-        assertEquals( "ObjectsWhiteString", "-1\n  String",tc.get("ObjectsWhiteString"));
+            assertEquals( "SystemProperty", System.getProperty("user.dir")+"/stuff",tc.get("SystemProperty"));
+            assertEquals( "Env", System.getenv("HOME"),tc.get("Env"));
 
-        assertEquals( "SystemProperty", System.getProperty("user.dir")+"/stuff",tc.get("SystemProperty"));
-        assertEquals( "Env", System.getenv("HOME"),tc.get("Env"));
-
-        assertEquals( "Property", "xxx", tc.get("Property"));
+            assertEquals( "Property", "xxx", tc.get("Property"));
 
 
-        assertEquals( "Called", "Yes",tc.get("Called"));
+            assertEquals( "Called", "Yes",tc.get("Called"));
 
-        assertTrue(TestConfiguration.called);
+            assertTrue(TestConfiguration.called);
 
-        assertEquals("oa[0]","Blah",tc.oa[0]);
-        assertEquals("oa[1]","1.2.3.4:5678",tc.oa[1]);
-        assertEquals("oa[2]",1.2345,tc.oa[2]);
-        assertEquals("oa[3]",null,tc.oa[3]);
+            assertEquals("oa[0]","Blah",tc.oa[0]);
+            assertEquals("oa[1]","1.2.3.4:5678",tc.oa[1]);
+            assertEquals("oa[2]",1.2345,tc.oa[2]);
+            assertEquals("oa[3]",null,tc.oa[3]);
 
-        assertEquals("ia[0]",1,tc.ia[0]);
-        assertEquals("ia[1]",2,tc.ia[1]);
-        assertEquals("ia[2]",3,tc.ia[2]);
-        assertEquals("ia[3]",0,tc.ia[3]);
+            assertEquals("ia[0]",1,tc.ia[0]);
+            assertEquals("ia[1]",2,tc.ia[1]);
+            assertEquals("ia[2]",3,tc.ia[2]);
+            assertEquals("ia[3]",0,tc.ia[3]);
 
-        TestConfiguration tc2=tc.nested;
-        assertTrue(tc2!=null);
-        assertEquals( "Called(bool)",true,tc2.get("Arg"));
+            TestConfiguration tc2=tc.nested;
+            assertTrue(tc2!=null);
+            assertEquals( "Called(bool)",true,tc2.get("Arg"));
 
-        assertEquals("nested config",null,tc.get("Arg"));
-        assertEquals("nested config",true,tc2.get("Arg"));
+            assertEquals("nested config",null,tc.get("Arg"));
+            assertEquals("nested config",true,tc2.get("Arg"));
 
-        assertEquals("nested config","Call1",tc2.testObject);
-        assertEquals("nested config",4,tc2.testInt);
-        assertEquals( "nested call", "http://www.eclipse.com/",tc2.url.toString());
+            assertEquals("nested config","Call1",tc2.testObject);
+            assertEquals("nested config",4,tc2.testInt);
+            assertEquals( "nested call", "http://www.eclipse.com/",tc2.url.toString());
 
-        assertEquals("static to field",tc.testField1,77);
-        assertEquals("field to field",tc.testField2,2);
-        assertEquals("literal to static",TestConfiguration.VALUE,42);
+            assertEquals("static to field",tc.testField1,77);
+            assertEquals("field to field",tc.testField2,2);
+            assertEquals("literal to static",TestConfiguration.VALUE,42);
+            
+            assertEquals("value0",((Map<String,String>)configuration.getIdMap().get("map")).get("key0"));
+            assertEquals("value1",((Map<String,String>)configuration.getIdMap().get("map")).get("key1"));
+        }
     }
 
     @Test
     public void testNewObject() throws Exception
     {
-        TestConfiguration.VALUE=71;
-        Map<String,String> properties = new HashMap<>();
-        properties.put("whatever", "xxx");
-
-        URL url = XmlConfigurationTest.class.getClassLoader().getResource(_configure);
-        final AtomicInteger count = new AtomicInteger(0);
-        XmlConfiguration configuration = new XmlConfiguration(url)
+        for (String configure : _configure)
         {
-            @Override
-            public void initializeDefaults(Object object)
+            TestConfiguration.VALUE=71;
+            Map<String,String> properties = new HashMap<>();
+            properties.put("whatever", "xxx");
+            
+            URL url = XmlConfigurationTest.class.getClassLoader().getResource(configure);
+            final AtomicInteger count = new AtomicInteger(0);
+            XmlConfiguration configuration = new XmlConfiguration(url)
             {
-                if (object instanceof TestConfiguration)
+                @Override
+                public void initializeDefaults(Object object)
                 {
-                    count.incrementAndGet();
-                    ((TestConfiguration)object).setNested(null);
-                    ((TestConfiguration)object).setTestString("NEW DEFAULT");
+                    if (object instanceof TestConfiguration)
+                    {
+                        count.incrementAndGet();
+                        ((TestConfiguration)object).setNested(null);
+                        ((TestConfiguration)object).setTestString("NEW DEFAULT");
+                    }
                 }
-            }
-        };
-        configuration.getProperties().putAll(properties);
-        TestConfiguration tc = (TestConfiguration)configuration.configure();
+            };
+            configuration.getProperties().putAll(properties);
+            TestConfiguration tc = (TestConfiguration)configuration.configure();
 
-        assertEquals(3,count.get());
-        
-        assertEquals("NEW DEFAULT",tc.getTestString());
-        assertEquals("nested",tc.getNested().getTestString());
-        assertEquals("NEW DEFAULT",tc.getNested().getNested().getTestString());
-        
-        assertEquals("Set String","SetValue",tc.testObject);
-        assertEquals("Set Type",2,tc.testInt);
+            assertEquals(3,count.get());
 
-        assertEquals(18080, tc.propValue);
+            assertEquals("NEW DEFAULT",tc.getTestString());
+            assertEquals("nested",tc.getNested().getTestString());
+            assertEquals("NEW DEFAULT",tc.getNested().getNested().getTestString());
 
-        assertEquals("Put","PutValue",tc.get("Test"));
-        assertEquals("Put dft","2",tc.get("TestDft"));
-        assertEquals("Put type",2,tc.get("TestInt"));
+            assertEquals("Set String","SetValue",tc.testObject);
+            assertEquals("Set Type",2,tc.testInt);
 
-        assertEquals("Trim","PutValue",tc.get("Trim"));
-        assertEquals("Null",null,tc.get("Null"));
-        assertEquals("NullTrim",null,tc.get("NullTrim"));
+            assertEquals(18080, tc.propValue);
 
-        assertEquals("ObjectTrim",1.2345,tc.get("ObjectTrim"));
-        assertEquals("Objects","-1String",tc.get("Objects"));
-        assertEquals( "ObjectsTrim", "-1String",tc.get("ObjectsTrim"));
-        assertEquals( "String", "\n    PutValue\n  ",tc.get("String"));
-        assertEquals( "NullString", "",tc.get("NullString"));
-        assertEquals( "WhiteSpace", "\n  ",tc.get("WhiteSpace"));
-        assertEquals( "ObjectString", "\n    1.2345\n  ",tc.get("ObjectString"));
-        assertEquals( "ObjectsString", "-1String",tc.get("ObjectsString"));
-        assertEquals( "ObjectsWhiteString", "-1\n  String",tc.get("ObjectsWhiteString"));
+            assertEquals("Put","PutValue",tc.get("Test"));
+            assertEquals("Put dft","2",tc.get("TestDft"));
+            assertEquals("Put type",2,tc.get("TestInt"));
 
-        assertEquals( "SystemProperty", System.getProperty("user.dir")+"/stuff",tc.get("SystemProperty"));
-        assertEquals( "Property", "xxx", tc.get("Property"));
+            assertEquals("Trim","PutValue",tc.get("Trim"));
+            assertEquals("Null",null,tc.get("Null"));
+            assertEquals("NullTrim",null,tc.get("NullTrim"));
+
+            assertEquals("ObjectTrim",1.2345,tc.get("ObjectTrim"));
+            assertEquals("Objects","-1String",tc.get("Objects"));
+            assertEquals( "ObjectsTrim", "-1String",tc.get("ObjectsTrim"));
+            assertEquals( "String", "\n    PutValue\n  ",tc.get("String"));
+            assertEquals( "NullString", "",tc.get("NullString"));
+            assertEquals( "WhiteSpace", "\n  ",tc.get("WhiteSpace"));
+            assertEquals( "ObjectString", "\n    1.2345\n  ",tc.get("ObjectString"));
+            assertEquals( "ObjectsString", "-1String",tc.get("ObjectsString"));
+            assertEquals( "ObjectsWhiteString", "-1\n  String",tc.get("ObjectsWhiteString"));
+
+            assertEquals( "SystemProperty", System.getProperty("user.dir")+"/stuff",tc.get("SystemProperty"));
+            assertEquals( "Property", "xxx", tc.get("Property"));
 
 
-        assertEquals( "Called", "Yes",tc.get("Called"));
+            assertEquals( "Called", "Yes",tc.get("Called"));
 
-        assertTrue(TestConfiguration.called);
+            assertTrue(TestConfiguration.called);
 
-        assertEquals("oa[0]","Blah",tc.oa[0]);
-        assertEquals("oa[1]","1.2.3.4:5678",tc.oa[1]);
-        assertEquals("oa[2]",1.2345,tc.oa[2]);
-        assertEquals("oa[3]",null,tc.oa[3]);
+            assertEquals("oa[0]","Blah",tc.oa[0]);
+            assertEquals("oa[1]","1.2.3.4:5678",tc.oa[1]);
+            assertEquals("oa[2]",1.2345,tc.oa[2]);
+            assertEquals("oa[3]",null,tc.oa[3]);
 
-        assertEquals("ia[0]",1,tc.ia[0]);
-        assertEquals("ia[1]",2,tc.ia[1]);
-        assertEquals("ia[2]",3,tc.ia[2]);
-        assertEquals("ia[3]",0,tc.ia[3]);
+            assertEquals("ia[0]",1,tc.ia[0]);
+            assertEquals("ia[1]",2,tc.ia[1]);
+            assertEquals("ia[2]",3,tc.ia[2]);
+            assertEquals("ia[3]",0,tc.ia[3]);
 
-        TestConfiguration tc2=tc.nested;
-        assertTrue(tc2!=null);
-        assertEquals( "Called(bool)",true,tc2.get("Arg"));
+            TestConfiguration tc2=tc.nested;
+            assertTrue(tc2!=null);
+            assertEquals( "Called(bool)",true,tc2.get("Arg"));
 
-        assertEquals("nested config",null,tc.get("Arg"));
-        assertEquals("nested config",true,tc2.get("Arg"));
+            assertEquals("nested config",null,tc.get("Arg"));
+            assertEquals("nested config",true,tc2.get("Arg"));
 
-        assertEquals("nested config","Call1",tc2.testObject);
-        assertEquals("nested config",4,tc2.testInt);
-        assertEquals( "nested call", "http://www.eclipse.com/",tc2.url.toString());
+            assertEquals("nested config","Call1",tc2.testObject);
+            assertEquals("nested config",4,tc2.testInt);
+            assertEquals( "nested call", "http://www.eclipse.com/",tc2.url.toString());
 
-        assertEquals("static to field",71,tc.testField1);
-        assertEquals("field to field",2,tc.testField2);
-        assertEquals("literal to static",42,TestConfiguration.VALUE);
+            assertEquals("static to field",71,tc.testField1);
+            assertEquals("field to field",2,tc.testField2);
+            assertEquals("literal to static",42,TestConfiguration.VALUE);
+        }
     }
 
 
     @Test
+    public void testGetClass() throws Exception
+    {
+        XmlConfiguration configuration =
+            new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"Test\"><Get name=\"class\"/></Set></Configure>");
+        TestConfiguration tc = new TestConfiguration();
+        configuration.configure(tc);
+        assertEquals(TestConfiguration.class,tc.testObject);
+        
+        configuration =
+            new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"Test\"><Get class=\"java.lang.String\" name=\"class\"><Get id=\"simple\" name=\"simpleName\"/></Get></Set></Configure>");
+        configuration.configure(tc);
+        assertEquals(String.class,tc.testObject);
+        assertEquals("String",configuration.getIdMap().get("simple"));
+    }
+    
+    @Test
     public void testStringConfiguration() throws Exception
     {
         XmlConfiguration configuration =
             new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"Test\">SetValue</Set><Set name=\"Test\" type=\"int\">2</Set></Configure>");
         TestConfiguration tc = new TestConfiguration();
         configuration.configure(tc);
-        assertEquals("Set String 3","SetValue",tc.testObject);
-        assertEquals("Set Type 3",2,tc.testInt);
+        assertEquals("Set String 3", "SetValue", tc.testObject);
+        assertEquals("Set Type 3", 2, tc.testInt);
     }
 
     @Test
@@ -233,7 +258,7 @@
         assertThat("tc.getList() returns null as it's not configured yet",tc.getList(),is(nullValue()));
         xmlConfiguration.configure(tc);
         assertThat("tc.getList() returns not null",tc.getList(),not(nullValue()));
-        assertThat("tc.getList() has two entries as specified in the xml",tc.getList().size(),is(2));
+        assertThat("tc.getList() has two entries as specified in the xml", tc.getList().size(), is(2));
     }
 
     @Test
@@ -248,7 +273,7 @@
         assertThat("tc.getList() returns null as it's not configured yet",tc.getList(),is(nullValue()));
         xmlConfiguration.configure(tc);
         assertThat("tc.getList() returns not null",tc.getList(),not(nullValue()));
-        assertThat("tc.getList() has two entries as specified in the xml",tc.getList().size(),is(2));
+        assertThat("tc.getList() has two entries as specified in the xml", tc.getList().size(), is(2));
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -270,7 +295,7 @@
         assertThat("tc.getList() returns null as it's not configured yet",tc.getSet(),is(nullValue()));
         xmlConfiguration.configure(tc);
         assertThat("tc.getList() returns not null",tc.getSet(),not(nullValue()));
-        assertThat("tc.getList() has two entries as specified in the xml",tc.getSet().size(),is(2));
+        assertThat("tc.getList() has two entries as specified in the xml", tc.getSet().size(), is(2));
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -290,7 +315,7 @@
         TestConfiguration tc = new TestConfiguration();
         assertThat("tc.getList() returns null as it's not configured yet",tc.getList(),is(nullValue()));
         xmlConfiguration.configure(tc);
-        assertThat("tc.getList() has two entries as specified in the xml",tc.getList().size(),is(2));
+        assertThat("tc.getList() has two entries as specified in the xml", tc.getList().size(), is(2));
     }
 
     @Test
@@ -301,7 +326,7 @@
         TestConfiguration tc = new TestConfiguration();
         assertThat("tc.getList() returns null as it's not configured yet",tc.getList(),is(nullValue()));
         xmlConfiguration.configure(tc);
-        assertThat("tc.getList() has two entries as specified in the xml",tc.getList().size(),is(2));
+        assertThat("tc.getList() has two entries as specified in the xml", tc.getList().size(), is(2));
     }
 
     @Test(expected=NoSuchMethodException.class)
@@ -310,7 +335,7 @@
         XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"LinkedList\">"
                 + INT_ARRAY_XML + "</Set></Configure>");
         TestConfiguration tc = new TestConfiguration();
-        assertThat("tc.getSet() returns null as it's not configured yet",tc.getList(),is(nullValue()));
+        assertThat("tc.getSet() returns null as it's not configured yet", tc.getList(), is(nullValue()));
         xmlConfiguration.configure(tc);
     }
 
@@ -320,9 +345,9 @@
         XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"ArrayList\">"
                 + INT_ARRAY_XML + "</Set></Configure>");
         TestConfiguration tc = new TestConfiguration();
-        assertThat("tc.getSet() returns null as it's not configured yet",tc.getList(),is(nullValue()));
+        assertThat("tc.getSet() returns null as it's not configured yet", tc.getList(), is(nullValue()));
         xmlConfiguration.configure(tc);
-        assertThat("tc.getSet() has two entries as specified in the xml",tc.getList().size(),is(2));
+        assertThat("tc.getSet() has two entries as specified in the xml", tc.getList().size(), is(2));
     }
 
     @Test
@@ -331,9 +356,9 @@
         XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"Set\">"
                 + STRING_ARRAY_XML + "</Set></Configure>");
         TestConfiguration tc = new TestConfiguration();
-        assertThat("tc.getSet() returns null as it's not configured yet",tc.getSet(),is(nullValue()));
+        assertThat("tc.getSet() returns null as it's not configured yet", tc.getSet(), is(nullValue()));
         xmlConfiguration.configure(tc);
-        assertThat("tc.getSet() has two entries as specified in the xml",tc.getSet().size(),is(2));
+        assertThat("tc.getSet() has two entries as specified in the xml", tc.getSet().size(), is(2));
     }
 
     @Test
@@ -342,9 +367,9 @@
         XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"Set\">"
                 + INT_ARRAY_XML + "</Set></Configure>");
         TestConfiguration tc = new TestConfiguration();
-        assertThat("tc.getSet() returns null as it's not configured yet",tc.getSet(),is(nullValue()));
+        assertThat("tc.getSet() returns null as it's not configured yet", tc.getSet(), is(nullValue()));
         xmlConfiguration.configure(tc);
-        assertThat("tc.getSet() has two entries as specified in the xml",tc.getSet().size(),is(2));
+        assertThat("tc.getSet() has two entries as specified in the xml", tc.getSet().size(), is(2));
     }
 
     @Test
@@ -590,7 +615,7 @@
         Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird());
         Assert.assertEquals("nested first parameter not wired correctly","arg1", atc.getNested().getFirst());
         Assert.assertEquals("nested second parameter not wired correctly","arg2", atc.getNested().getSecond());
-        Assert.assertEquals("nested third parameter not wired correctly","arg3", atc.getNested().getThird());
+        Assert.assertEquals("nested third parameter not wired correctly", "arg3", atc.getNested().getThird());
     }
 
     @Test
@@ -618,7 +643,7 @@
         Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird());
         Assert.assertEquals("nested first parameter not wired correctly","arg1", atc.getNested().getFirst());
         Assert.assertEquals("nested second parameter not wired correctly","arg2", atc.getNested().getSecond());
-        Assert.assertEquals("nested third parameter not wired correctly","arg3", atc.getNested().getThird());
+        Assert.assertEquals("nested third parameter not wired correctly", "arg3", atc.getNested().getThird());
     }
 
     @Test
@@ -645,7 +670,7 @@
         Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird());
         Assert.assertEquals("nested first parameter not wired correctly","arg1", atc.getNested().getFirst());
         Assert.assertEquals("nested second parameter not wired correctly","arg2", atc.getNested().getSecond());
-        Assert.assertEquals("nested third parameter not wired correctly","arg3", atc.getNested().getThird());
+        Assert.assertEquals("nested third parameter not wired correctly", "arg3", atc.getNested().getThird());
     }
 
     public static class NativeHolder
@@ -779,4 +804,117 @@
             
         }
     }
+
+    @Test
+    public void testWithMultiplePropertyNamesWithNoPropertyThenDefaultIsChosen() throws Exception
+    {
+        // No properties
+        String defolt = "baz";
+        XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
+                "<Configure class=\"org.eclipse.jetty.xml.DefaultTestConfiguration\">" +
+                "  <Set name=\"first\"><Property name=\"wibble\" deprecated=\"foo,bar\" default=\"" + defolt + "\"/></Set>  " +
+                "</Configure>");
+        DefaultTestConfiguration config = (DefaultTestConfiguration)xmlConfiguration.configure();
+        assertEquals(defolt, config.getFirst());
+    }
+
+    @Test
+    public void testWithMultiplePropertyNamesWithFirstPropertyThenFirstIsChosen() throws Exception
+    {
+        String name = "foo";
+        String value = "foo";
+        XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
+                "<Configure class=\"org.eclipse.jetty.xml.DefaultTestConfiguration\">" +
+                "  <Set name=\"first\"><Property name=\"" + name + "\" deprecated=\"other,bar\" default=\"baz\"/></Set>  " +
+                "</Configure>");
+        xmlConfiguration.getProperties().put(name, value);
+        DefaultTestConfiguration config = (DefaultTestConfiguration)xmlConfiguration.configure();
+        assertEquals(value, config.getFirst());
+    }
+
+    @Test
+    public void testWithMultiplePropertyNamesWithSecondPropertyThenSecondIsChosen() throws Exception
+    {
+        String name = "bar";
+        String value = "bar";
+        XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
+                "<Configure class=\"org.eclipse.jetty.xml.DefaultTestConfiguration\">" +
+                "  <Set name=\"first\"><Property name=\"foo\" deprecated=\"" + name + "\" default=\"baz\"/></Set>  " +
+                "</Configure>");
+        xmlConfiguration.getProperties().put(name, value);
+        DefaultTestConfiguration config = (DefaultTestConfiguration)xmlConfiguration.configure();
+        assertEquals(value, config.getFirst());
+    }
+
+    @Test
+    public void testWithMultiplePropertyNamesWithDeprecatedThenThirdIsChosen() throws Exception
+    {
+        String name = "bar";
+        String value = "bar";
+        XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
+                "<Configure class=\"org.eclipse.jetty.xml.DefaultTestConfiguration\">" +
+                "  <Set name=\"first\"><Property name=\"foo\" deprecated=\"other," + name + "\" default=\"baz\"/></Set>  " +
+                "</Configure>");
+        xmlConfiguration.getProperties().put(name, value);
+        DefaultTestConfiguration config = (DefaultTestConfiguration)xmlConfiguration.configure();
+        assertEquals(value, config.getFirst());
+    }
+
+    @Test
+    public void testWithMultiplePropertyNameElementsWithDeprecatedThenThirdIsChosen() throws Exception
+    {
+        String name = "bar";
+        String value = "bar";
+        XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
+                "<Configure class=\"org.eclipse.jetty.xml.DefaultTestConfiguration\">" +
+                "  <Set name=\"first\">" +
+                "  <Property>  " +
+                "    <Name>foo</Name>" +
+                "    <Deprecated>foo</Deprecated>" +
+                "    <Deprecated>"+name+"</Deprecated>" +
+                "    <Default>baz</Default>" +
+                "  </Property>  " +
+                "  </Set>  " +
+                "</Configure>");
+        xmlConfiguration.getProperties().put(name, value);
+        DefaultTestConfiguration config = (DefaultTestConfiguration)xmlConfiguration.configure();
+        assertEquals(value, config.getFirst());
+    }
+
+    @Test
+    public void testPropertyNotFoundWithPropertyInDefaultValue() throws Exception
+    {
+        String name = "bar";
+        String value = "bar";
+        String defaultValue = "_<Property name=\"bar\"/>_<Property name=\"bar\"/>_";
+        String expectedValue = "_" + value + "_" + value + "_";
+        XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
+                "<Configure class=\"org.eclipse.jetty.xml.DefaultTestConfiguration\">" +
+                "  <Set name=\"first\">" +
+                "    <Property>" +
+                "      <Name>not_found</Name>" +
+                "      <Default>" + defaultValue + "</Default>" +
+                "    </Property>" +
+                "  </Set>  " +
+                "</Configure>");
+        xmlConfiguration.getProperties().put(name, value);
+        DefaultTestConfiguration config = (DefaultTestConfiguration)xmlConfiguration.configure();
+        assertEquals(expectedValue, config.getFirst());
+    }
+
+    @Test
+    public void testPropertyNotFoundWithPropertyInDefaultValueNotFoundWithDefault() throws Exception
+    {
+        String value = "bar";
+        XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
+                "<Configure class=\"org.eclipse.jetty.xml.DefaultTestConfiguration\">" +
+                "  <Set name=\"first\">" +
+                "    <Property name=\"not_found\">" +
+                "      <Default><Property name=\"also_not_found\" default=\"" + value + "\"/></Default>" +
+                "    </Property>" +
+                "  </Set>  " +
+                "</Configure>");
+        DefaultTestConfiguration config = (DefaultTestConfiguration)xmlConfiguration.configure();
+        assertEquals(value, config.getFirst());
+    }
 }
diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlParserTest.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlParserTest.java
index 8880e5b..eef1edd 100644
--- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlParserTest.java
+++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlParserTest.java
@@ -31,24 +31,12 @@
     {
         XmlParser parser = new XmlParser();
 
-        URL configURL = XmlConfiguration.class.getClassLoader().getResource("org/eclipse/jetty/xml/configure_6_0.dtd");
+        URL configURL = XmlConfiguration.class.getClassLoader().getResource("org/eclipse/jetty/xml/configure_9_3.dtd");
         parser.redirectEntity("configure.dtd", configURL);
-        parser.redirectEntity("configure_1_3.dtd", configURL);
         parser.redirectEntity("http://jetty.eclipse.org/configure.dtd", configURL);
         parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN", configURL);
-        parser.redirectEntity("http://jetty.eclipse.org/configure_1_3.dtd", configURL);
-        parser.redirectEntity("-//Mort Bay Consulting//DTD Configure 1.3//EN", configURL);
-        parser.redirectEntity("configure_1_2.dtd", configURL);
-        parser.redirectEntity("http://jetty.eclipse.org/configure_1_2.dtd", configURL);
-        parser.redirectEntity("-//Mort Bay Consulting//DTD Configure 1.2//EN", configURL);
-        parser.redirectEntity("configure_1_1.dtd", configURL);
-        parser.redirectEntity("http://jetty.eclipse.org/configure_1_1.dtd", configURL);
-        parser.redirectEntity("-//Mort Bay Consulting//DTD Configure 1.1//EN", configURL);
-        parser.redirectEntity("configure_1_0.dtd", configURL);
-        parser.redirectEntity("http://jetty.eclipse.org/configure_1_0.dtd", configURL);
-        parser.redirectEntity("-//Mort Bay Consulting//DTD Configure 1.0//EN", configURL);
 
-        URL url = XmlParserTest.class.getClassLoader().getResource("org/eclipse/jetty/xml/configure.xml");
+        URL url = XmlParserTest.class.getClassLoader().getResource("org/eclipse/jetty/xml/configureWithAttr.xml");
         XmlParser.Node testDoc = parser.parse(url.toString());
         String testDocStr = testDoc.toString().trim();
 
diff --git a/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configure.xml b/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configure.xml
deleted file mode 100644
index 46c8571..0000000
--- a/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configure.xml
+++ /dev/null
@@ -1,145 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
-
-<Configure class="org.eclipse.jetty.xml.TestConfiguration">
-  <Arg name="name">name</Arg>
-
-  <Set name="Test">SetValue</Set>
-  <Set name="Test" type="int"><Property name="does.not.exist" default="2"/></Set>
-  
-  <Set name="PropertyTest"><Property name="anIntegerNoActualPropDefined" default="18080"/></Set>
-
-  <Put name="Test">PutValue</Put>
-  <Put name="TestDft">2</Put>
-  <Put name="TestInt" type="int">2</Put>
-
-  <Put name="Trim">
-    PutValue
-  </Put>
-
-  <Put name="Null"></Put>
-
-  <Put name="NullTrim">
-  </Put>
-
-  <Put name="Object"><New class="java.lang.Double">
-      <Arg>1.2345</Arg>
-  </New></Put>
- 
-  <Put name="ObjectTrim">
-    <New class="java.lang.Double">
-      <Arg>1.2345</Arg>
-    </New>
-  </Put>
-
-  <Put name="Objects"><New class="java.lang.Integer">
-      <Arg>-1</Arg>
-  </New><New class="java.lang.String">
-      <Arg>String</Arg>
-  </New></Put>
-
-
-  <Put name="ObjectsTrim">
-    <New class="java.lang.Integer">
-      <Arg>-1</Arg>
-  </New><New class="java.lang.String">
-      <Arg>String</Arg>
-  </New></Put>
-
-
-  <Put name="String" type="String">
-    PutValue
-  </Put>
-
-  <Put name="NullString" type="String"></Put>
-
-  <Put name="WhiteSpace" type="String">
-  </Put>
-
-  <Put name="ObjectString" type="String">
-    <New class="java.lang.Double">
-      <Arg>1.2345</Arg>
-    </New>
-  </Put>
-
-  <Put name="ObjectsString" type="String"><New class="java.lang.Integer">
-      <Arg>-1</Arg>
-  </New><New class="java.lang.String">
-      <Arg>String</Arg>
-  </New></Put>
-
-
-  <Put name="ObjectsWhiteString">
-    <New class="java.lang.Integer">
-      <Arg>-1</Arg>
-  </New>
-  <New class="java.lang.String">
-      <Arg>String</Arg>
-  </New></Put>
-
-  <Put name="SystemProperty" ><SystemProperty name="user.dir"/>/stuff</Put>
-  <Put name="Property"><Property name="whatever" default="xxx"/></Put>
-  <Put name="SomethingElse"><SystemProperty name="floople" default="xxx"/></Put>
-  <Put name="Boolean" type="Boolean">True</Put>
-  <Put name="Float" type="Float">2.3</Put>
-  <Put name="Env"><Env name="HOME"/></Put>
-
-  <Set name="nested">
-    <New class="org.eclipse.jetty.xml.TestConfiguration">
-      <Set name="testString">nested</Set>
-      <Set name="nested">
-        <New class="org.eclipse.jetty.xml.TestConfiguration">
-        </New>
-      </Set>
-    </New>
-  </Set>
-
-  <Call name="call">
-  </Call>
-
-  <Call name="call">
-    <Arg type="boolean">false</Arg>
-  </Call>
-
-  <Call name="call">
-    <Arg type="boolean">true</Arg>
-    <Put name="nested">put</Put>
-    <Set name="Test">Call1</Set>
-    <Set name="Test" type="int">4</Set>
-    <Call name="call">
-      <Arg type="URL">http://www.eclipse.com/</Arg>
-      <Arg type="boolean">false</Arg>
-    </Call>
-  </Call>
-
-  <Get name="String">
-     <Call name="toString"/>
-  </Get>
-
-  <Call name="callStatic" class="org.eclipse.jetty.xml.TestConfiguration"/>
-
-  <Call name="call">
-    <Arg><Array type="java.lang.Object">
-      <Item>Blah</Item>
-      <Item type="String">1.2.3.4:5678</Item>
-      <Item><New class="java.lang.Double"><Arg>1.2345</Arg></New></Item>
-      <Item></Item>
-    </Array></Arg>
-  </Call>
-
-  <Call name="call">
-    <Arg><Array type="int">
-      <Item type="int">1</Item>
-      <Item type="int">2</Item>
-      <Item type="int">3</Item>
-      <Item></Item>
-    </Array></Arg>
-  </Call>
-
-  <Set name="testField1"><Get class="org.eclipse.jetty.xml.TestConfiguration" name="VALUE"/></Set>
-  <Set name="testField2"><Get name="testInt"/></Set>
-  <Set name="VALUE" type="int">42</Set>
-
-</Configure>
-
-
diff --git a/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configureWithAttr.xml b/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configureWithAttr.xml
new file mode 100644
index 0000000..8377987
--- /dev/null
+++ b/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configureWithAttr.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure class="org.eclipse.jetty.xml.TestConfiguration">
+  <Arg name="name">name</Arg>
+
+  <Set name="Test">SetValue</Set>
+  <Set name="Test" type="int"><Property name="does.not.exist" default="2"/></Set>
+  
+  <Set name="PropertyTest"><Property name="anIntegerNoActualPropDefined" default="18080"/></Set>
+
+  <Put name="Test">PutValue</Put>
+  <Put name="TestDft">2</Put>
+  <Put name="TestInt" type="int">2</Put>
+
+  <Put name="Trim">
+    PutValue
+  </Put>
+
+  <Put name="Null"></Put>
+
+  <Put name="NullTrim">
+  </Put>
+
+  <Put name="Object"><New class="java.lang.Double" arg="1.2345"/></Put>
+ 
+  <Put name="ObjectTrim">
+    <New class="java.lang.Double" arg="1.2345"/>
+  </Put>
+
+  <Put name="Objects"><New class="java.lang.Integer" arg="-1"/><New class="java.lang.String" arg="String"/></Put>
+
+  <Put name="ObjectsTrim">
+    <New class="java.lang.Integer" arg="-1">
+  </New><New class="java.lang.String" arg="String">
+  </New></Put>
+
+  <Put name="String" type="String">
+    PutValue
+  </Put>
+
+  <Put name="NullString" type="String"></Put>
+
+  <Put name="WhiteSpace" type="String">
+  </Put>
+
+  <Put name="ObjectString" type="String">
+    <New class="java.lang.Double">
+      <Arg>1.2345</Arg>
+    </New>
+  </Put>
+
+  <Put name="ObjectsString" type="String"><New class="java.lang.Integer" arg="-1"/><New class="java.lang.String" arg="String"/></Put>
+
+  <Put name="ObjectsWhiteString">
+    <New class="java.lang.Integer" arg="-1">
+  </New>
+  <New class="java.lang.String" arg="String">
+  </New></Put>
+
+  <Put name="SystemProperty" ><SystemProperty name="user.dir"/>/stuff</Put>
+  <Put name="Property"><Property name="whatever" default="xxx"/></Put>
+  <Put name="SomethingElse"><SystemProperty name="floople" default="xxx"/></Put>
+  <Put name="Boolean" type="Boolean">True</Put>
+  <Put name="Float" type="Float">2.3</Put>
+  <Put name="Env"><Env name="HOME"/></Put>
+
+  <Set name="nested">
+    <New class="org.eclipse.jetty.xml.TestConfiguration">
+      <Set name="testString">nested</Set>
+      <Set name="nested">
+        <New class="org.eclipse.jetty.xml.TestConfiguration">
+        </New>
+      </Set>
+    </New>
+  </Set>
+
+  <Call name="call">
+  </Call>
+
+  <Call name="call">
+    <Arg type="boolean">false</Arg>
+  </Call>
+
+  <Call name="call">
+    <Arg type="boolean">true</Arg>
+    <Put name="nested">put</Put>
+    <Set name="Test">Call1</Set>
+    <Set name="Test" type="int">4</Set>
+    <Call name="call">
+      <Arg type="URL">http://www.eclipse.com/</Arg>
+      <Arg type="boolean">false</Arg>
+    </Call>
+  </Call>
+
+  <Get name="String">
+     <Call name="toString"/>
+  </Get>
+
+  <Call name="callStatic" class="org.eclipse.jetty.xml.TestConfiguration"/>
+
+  <Call name="call">
+    <Arg><Array type="java.lang.Object">
+      <Item>Blah</Item>
+      <Item type="String">1.2.3.4:5678</Item>
+      <Item><New class="java.lang.Double"><Arg>1.2345</Arg></New></Item>
+      <Item></Item>
+    </Array></Arg>
+  </Call>
+
+  <Call name="call">
+    <Arg><Array type="int">
+      <Item type="int">1</Item>
+      <Item type="int">2</Item>
+      <Item type="int">3</Item>
+      <Item></Item>
+    </Array></Arg>
+  </Call>
+
+  <Set name="testField1"><Get class="org.eclipse.jetty.xml.TestConfiguration" name="VALUE"/></Set>
+  <Set name="testField2"><Get name="testInt"/></Set>
+  <Set name="VALUE" type="int">42</Set>
+
+  <Map id="map">
+    <Entry>
+      <Item id="key0">key0</Item>
+      <Item id="value0">value0</Item>
+    </Entry>
+    <Entry>
+      <Item id="key1">key1</Item>
+      <Item id="value1">value1</Item>
+    </Entry>
+  </Map>
+
+</Configure>
+
+
diff --git a/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configureWithElements.xml b/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configureWithElements.xml
new file mode 100644
index 0000000..e4fa88d
--- /dev/null
+++ b/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configureWithElements.xml
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure class="org.eclipse.jetty.xml.TestConfiguration">
+  <Arg name="name">name</Arg>
+
+  <Set name="Test">SetValue</Set>
+  <Set name="Test" type="int"><Property><Name>does.not.exist</Name><Default>2</Default></Property></Set>
+  
+  <Set name="PropertyTest"><Property><Name>anIntegerNoActualPropDefined</Name><Default>18080</Default></Property></Set>
+
+  <Put name="Test">PutValue</Put>
+  <Put name="TestDft">2</Put>
+  <Put name="TestInt" type="int">2</Put>
+
+  <Put name="Trim">
+    PutValue
+  </Put>
+
+  <Put name="Null"></Put>
+
+  <Put name="NullTrim">
+  </Put>
+
+  <Put name="Object"><New class="java.lang.Double">
+      <Arg>1.2345</Arg>
+  </New></Put>
+ 
+  <Put name="ObjectTrim">
+    <New class="java.lang.Double">
+      <Arg>1.2345</Arg>
+    </New>
+  </Put>
+
+  <Put name="Objects"><New class="java.lang.Integer">
+      <Arg>-1</Arg>
+  </New><New class="java.lang.String">
+      <Arg>String</Arg>
+  </New></Put>
+
+
+  <Put name="ObjectsTrim">
+    <New class="java.lang.Integer">
+      <Arg>-1</Arg>
+  </New><New class="java.lang.String">
+      <Arg>String</Arg>
+  </New></Put>
+
+
+  <Put name="String" type="String">
+    PutValue
+  </Put>
+
+  <Put name="NullString" type="String"></Put>
+
+  <Put name="WhiteSpace" type="String">
+  </Put>
+
+  <Put name="ObjectString" type="String">
+    <New class="java.lang.Double">
+      <Arg>1.2345</Arg>
+    </New>
+  </Put>
+
+  <Put name="ObjectsString" type="String"><New class="java.lang.Integer">
+      <Arg>-1</Arg>
+  </New><New>
+      <Class>java.lang.String</Class>
+      <Arg>String</Arg>
+  </New></Put>
+
+
+  <Put name="ObjectsWhiteString">
+    <New>
+      <Class>java.lang.Integer</Class>
+      <Arg>-1</Arg>
+  </New>
+  <New>
+      <Class>java.lang.String</Class>
+      <Arg>String</Arg>
+  </New></Put>
+
+  <Put name="SystemProperty" ><SystemProperty><Name>user.dir</Name></SystemProperty>/stuff</Put>
+  <Put name="Property"><Property><Name>whatever</Name><Default>xxx</Default></Property></Put>
+  <Put name="SomethingElse"><SystemProperty name="floople" default="xxx"/></Put>
+  <Put name="Boolean" type="Boolean">True</Put>
+  <Put name="Float" type="Float">2.3</Put>
+  <Put name="Env"><Env name="HOME"/></Put>
+
+  <Set name="nested">
+    <New>
+      <Class>org.eclipse.jetty.xml.TestConfiguration</Class>
+      <Set name="testString">nested</Set>
+      <Set name="nested">
+        <New class="org.eclipse.jetty.xml.TestConfiguration">
+        </New>
+      </Set>
+    </New>
+  </Set>
+
+  <Call name="call">
+  </Call>
+
+  <Call name="call">
+    <Arg type="boolean">false</Arg>
+  </Call>
+
+  <Call name="call">
+    <Arg type="boolean">true</Arg>
+    <Put name="nested">put</Put>
+    <Set name="Test">Call1</Set>
+    <Set name="Test" type="int">4</Set>
+    <Call>
+      <Name>call</Name>
+      <Arg type="URL">http://www.eclipse.com/</Arg>
+      <Arg type="boolean">false</Arg>
+    </Call>
+  </Call>
+
+  <Get name="String">
+     <Call name="toString"/>
+  </Get>
+
+  <Call>
+    <Name>callStatic</Name>
+    <Class>org.eclipse.jetty.xml.TestConfiguration</Class>
+  </Call>
+
+  <Call>
+    <Name>call</Name>
+    <Arg><Array type="java.lang.Object">
+      <Item>Blah</Item>
+      <Item type="String">1.2.3.4:5678</Item>
+      <Item><New class="java.lang.Double"><Arg>1.2345</Arg></New></Item>
+      <Item></Item>
+    </Array></Arg>
+  </Call>
+
+  <Call>
+    <Name>call</Name>
+    <Arg><Array>
+      <Id>array</Id>
+      <Type>int</Type>
+      <Item type="int">1</Item>
+      <Item type="int">2</Item>
+      <Item type="int">3</Item>
+      <Item></Item>
+    </Array></Arg>
+  </Call>
+
+  <Set name="testField1"><Get class="org.eclipse.jetty.xml.TestConfiguration" name="VALUE"/></Set>
+  <Set name="testField2"><Get name="testInt"/></Set>
+  <Set name="VALUE" type="int">42</Set>
+  
+  <Map>
+    <Id>map</Id>
+    <Entry>
+      <Item id="key0">key0</Item>
+      <Item id="value0">value0</Item>
+    </Entry>
+    <Entry>
+      <Item id="key1">key1</Item>
+      <Item id="value1">value1</Item>
+    </Entry>
+  </Map>
+
+</Configure>
+
+
diff --git a/jetty-xml/src/test/resources/org/eclipse/jetty/xml/mortbay.xml b/jetty-xml/src/test/resources/org/eclipse/jetty/xml/mortbay.xml
index e1c1808..c8e20ce 100644
--- a/jetty-xml/src/test/resources/org/eclipse/jetty/xml/mortbay.xml
+++ b/jetty-xml/src/test/resources/org/eclipse/jetty/xml/mortbay.xml
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure_9_3.dtd">
 <Configure class="java.lang.Object">
 </Configure>
diff --git a/logos/jetty-avatar-128dpi.png b/logos/jetty-avatar-128dpi.png
new file mode 100644
index 0000000..9ab7eca
--- /dev/null
+++ b/logos/jetty-avatar-128dpi.png
Binary files differ
diff --git a/logos/jetty-avatar-256.png b/logos/jetty-avatar-256.png
new file mode 100644
index 0000000..d1a8f71
--- /dev/null
+++ b/logos/jetty-avatar-256.png
Binary files differ
diff --git a/logos/jetty-avatar-64.png b/logos/jetty-avatar-64.png
new file mode 100644
index 0000000..498e45a
--- /dev/null
+++ b/logos/jetty-avatar-64.png
Binary files differ
diff --git a/logos/jetty-avatar.png b/logos/jetty-avatar.png
new file mode 100644
index 0000000..5ab4abc
--- /dev/null
+++ b/logos/jetty-avatar.png
Binary files differ
diff --git a/logos/jetty-avatar.svg b/logos/jetty-avatar.svg
new file mode 100644
index 0000000..f667369
--- /dev/null
+++ b/logos/jetty-avatar.svg
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="400"
+   height="400"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.48.5 r10040"
+   version="1.0"
+   sodipodi:docname="jetty-avatar.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="jetty-avatar.png"
+   inkscape:export-xdpi="28.799999"
+   inkscape:export-ydpi="28.799999">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+    <inkscape:perspective
+       id="perspective2390"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <filter
+       inkscape:collect="always"
+       id="filter3853"
+       x="-0.15826963"
+       width="1.3165393"
+       y="-0.20584013"
+       height="1.4116803">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="26.292865"
+         id="feGaussianBlur3855" />
+    </filter>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#525252"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.87428571"
+     inkscape:cx="171.56863"
+     inkscape:cy="200"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer2"
+     showgrid="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1026"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     showborder="true"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Jetty"
+     style="display:inline"
+     transform="translate(-1.845606,-221.31978)">
+    <g
+       id="g3857"
+       transform="matrix(0.88669095,0,0,0.88669095,25.158515,48.38446)">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3004"
+         d="m 84.37204,268.03851 a 12.50125,12.50125 0 0 0 -12.1875,9.6875 l -10.3125,44.75 a 12.50125,12.50125 0 0 0 0.71875,7.75 12.50125,12.50125 0 0 0 -4.0625,6.6875 L 18.68454,509.44476 c -0.93999,0.0793 -1.75306,0.16127 -3.21875,0.21875 a 12.50125,12.50125 0 0 0 -12,12.1875 l -0.96875,39.9375 a 12.50125,12.50125 0 0 0 12.53125,12.8125 l 67.40625,-0.25 a 12.50125,12.50125 0 0 0 0.625,0 c 8.74629,-0.48292 17.66461,-3.77402 24.21875,-10.15625 6.55414,-6.38223 10.64721,-14.90394 13.6875,-25.25 a 12.50125,12.50125 0 0 0 0.1875,-0.6875 l 5.625,-24.3125 3.75,0 -4.0625,8.6875 a 12.50125,12.50125 0 0 0 11.34375,17.78125 l 42.15625,0 a 12.50125,12.50125 0 0 0 11.34375,-7.21875 l 115.34375,-247.375 a 12.50125,12.50125 0 0 0 -11.3125,-17.78125 l -42.1875,0 a 12.50125,12.50125 0 0 0 -11.3125,7.21875 l -24.59375,52.75 a 12.50125,12.50125 0 0 0 -4.375,-0.78125 l -43.03125,0 10.15625,-43.875 a 12.50125,12.50125 0 0 0 -12.1875,-15.3125 l -83.4375,0 z m 262.15625,0 a 12.50125,12.50125 0 0 0 -11.3125,7.21875 l -115.375,247.375 a 12.50125,12.50125 0 0 0 11.34375,17.78125 l 42.15625,0 a 12.50125,12.50125 0 0 0 11.34375,-7.21875 l 115.34375,-247.375 a 12.50125,12.50125 0 0 0 -11.3125,-17.78125 l -42.1875,0 z m -196.75,146.28125 27.21875,0 -5.84375,12.53125 -24.28125,0 2.90625,-12.53125 z"
+         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:25;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3853);enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
+      <g
+         id="g3058">
+        <g
+           id="g3032"
+           transform="translate(-33.169935,0)">
+          <path
+             id="path2996"
+             d="M 106.32205,339.73892 65.825288,515.14967 c -2.406515,7.05911 -6.072322,6.68223 -14.254473,7.0031 l -0.962606,39.94815 67.382421,-0.24066 c 13.0754,-0.72195 21.22428,-8.13437 26.59883,-26.42388 l 45.18031,-195.69746 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:28.19471741;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path2994"
+             d="m 119.98951,280.53864 -10.33393,44.76119 83.44772,0 10.33393,-44.76119 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:28.19471741;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3020"
+             d="M 106.32205,339.73892 65.825288,515.14967 c -2.406515,7.05911 -6.072322,6.68223 -14.254473,7.0031 l -0.962606,39.94815 67.382421,-0.24066 c 13.0754,-0.72195 21.22428,-8.13437 26.59883,-26.42388 l 45.18031,-195.69746 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3018"
+             d="m 119.98951,280.53864 -10.33393,44.76119 83.44772,0 10.33393,-44.76119 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none"
+             inkscape:connector-curvature="0" />
+        </g>
+        <g
+           id="g3046"
+           transform="translate(-287.0915,0)">
+          <path
+             id="path2988"
+             d="m 542.7018,280.53864 -115.3597,247.38976 42.1718,0 115.3597,-247.38976 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:28.19471741;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3193"
+             d="m 636.0746,280.53864 -115.3597,247.38976 42.1718,0 115.3597,-247.38976 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:28.19471741;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3012"
+             d="m 542.7018,280.53864 -115.3597,247.38976 42.1718,0 115.3597,-247.38976 z"
+             style="fill:#fc592e;fill-opacity:1;fill-rule:evenodd;stroke:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3316"
+             d="m 636.0746,280.53864 -115.3597,247.38976 42.1718,0 115.3597,-247.38976 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none"
+             inkscape:connector-curvature="0" />
+        </g>
+        <g
+           id="g3052"
+           transform="translate(-239.05228,0)">
+          <path
+             id="path2992"
+             d="m 370.6225,339.73892 -14.3342,62.08809 83.7467,0 14.3342,-62.08809 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:28.19471741;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path2990"
+             d="m 347.6212,439.36864 -14.3342,62.08809 83.7466,0 14.3342,-62.08809 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:28.19471741;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3016"
+             d="m 370.6225,339.73892 -14.3342,62.08809 83.7467,0 14.3342,-62.08809 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none"
+             inkscape:connector-curvature="0" />
+          <path
+             id="path3014"
+             d="m 347.6212,439.36864 -14.3342,62.08809 83.7466,0 14.3342,-62.08809 z"
+             style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none"
+             inkscape:connector-curvature="0" />
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/logos/jetty-logo-200.png b/logos/jetty-logo-200.png
new file mode 100644
index 0000000..d579fff
--- /dev/null
+++ b/logos/jetty-logo-200.png
Binary files differ
diff --git a/logos/jetty-logo-shadow-400x114.png b/logos/jetty-logo-shadow-400x114.png
new file mode 100644
index 0000000..f3cb29b
--- /dev/null
+++ b/logos/jetty-logo-shadow-400x114.png
Binary files differ
diff --git a/logos/jetty-logo-shadow.png b/logos/jetty-logo-shadow.png
new file mode 100644
index 0000000..ad09b2f
--- /dev/null
+++ b/logos/jetty-logo-shadow.png
Binary files differ
diff --git a/logos/jetty-logo-shadow.svg b/logos/jetty-logo-shadow.svg
new file mode 100644
index 0000000..be9b835
--- /dev/null
+++ b/logos/jetty-logo-shadow.svg
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="1400"
+   height="400"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="jetty-logo-shadow.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="jetty-logo-shadow.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+    <inkscape:perspective
+       id="perspective2390"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <filter
+       inkscape:collect="always"
+       id="filter3295"
+       x="-0.036928206"
+       width="1.0738564"
+       y="-0.15990376"
+       height="1.3198075">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="20.425209"
+         id="feGaussianBlur3297" />
+    </filter>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.61821336"
+     inkscape:cx="638.23248"
+     inkscape:cy="439.84756"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1679"
+     inkscape:window-height="1001"
+     inkscape:window-x="1"
+     inkscape:window-y="48"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     showborder="true" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer1"
+     inkscape:label="Drop Shadow">
+    <path
+       style="fill:#121212;fill-opacity:0.76888889000000005;fill-rule:evenodd;stroke:none;stroke-width:25;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline;filter:url(#filter3295)"
+       d="M 133.14892,74.210753 C 127.32606,74.207483 122.27201,78.224803 120.96142,83.898253 L 110.61767,128.6795 C 110.03629,131.2768 110.29904,133.99187 111.36767,136.4295 C 109.33278,138.16652 107.90884,140.51055 107.30517,143.117 L 67.46142,315.617 C 66.521262,315.6963 65.709017,315.77825 64.24267,315.83575 C 57.642403,316.0821 52.37259,321.42036 52.21142,328.02325 L 51.27392,367.992 C 51.198431,371.36099 52.486258,374.61778 54.845444,377.02403 C 57.20463,379.43028 60.435344,380.78218 63.80517,380.77325 L 131.18017,380.5545 C 131.39916,380.54984 131.61798,380.53942 131.83642,380.52325 C 140.58271,380.04033 149.50103,376.74923 156.05517,370.367 C 162.60931,363.98478 166.70238,355.49431 169.74267,345.14825 C 169.81228,344.91058 169.8748,344.67089 169.93017,344.4295 L 185.05517,278.83575 C 185.38711,284.40834 186.31145,289.70084 188.05517,294.71075 C 190.72151,302.37149 195.75549,309.41129 202.83642,313.867 C 209.91735,318.32271 218.43366,320.14825 227.64892,320.14825 L 398.49267,320.14825 C 404.30384,320.13699 409.34096,316.12283 410.64892,310.46075 L 418.64892,275.8045 C 419.50468,272.09658 418.62495,268.20076 416.25888,265.22037 C 413.89281,262.23997 410.29806,260.49955 406.49267,260.492 L 294.14892,260.492 L 295.27392,255.64825 L 413.36767,255.64825 C 419.17884,255.63699 424.21596,251.62283 425.52392,245.96075 C 425.994,243.92461 428.7761,237.60241 431.96142,227.0545 C 434.57148,218.41152 437.29104,206.4887 439.49267,190.1795 L 450.58642,190.1795 L 436.61767,250.58575 C 433.83028,262.65929 433.25054,278.51751 440.52392,293.367 C 447.7973,308.21649 464.78839,320.14825 488.77392,320.14825 L 587.55517,320.14825 C 593.36634,320.13699 598.40346,316.12283 599.71142,310.46075 L 607.71142,275.8045 C 608.56718,272.09658 607.68745,268.20076 605.32138,265.22037 C 602.95531,262.23997 599.36056,260.49955 595.55517,260.492 C 566.44463,260.49201 549.53568,259.6817 543.21142,258.867 C 543.42812,257.44728 543.42232,257.233 544.14892,254.08575 L 558.89892,190.1795 L 611.43017,190.1795 L 617.55517,190.1795 L 625.30517,190.1795 L 611.33642,250.58575 C 608.54904,262.65928 607.96929,278.51751 615.24267,293.367 C 622.51605,308.21649 639.50714,320.14825 663.49267,320.14825 L 762.27392,320.14825 C 768.08509,320.13699 773.12221,316.12283 774.43017,310.46075 L 776.71142,300.5545 C 784.37997,311.40324 799.92403,320.14825 823.74267,320.14825 L 876.71142,320.14825 C 870.84247,320.97 863.04125,321.617 852.30517,321.617 L 768.93017,321.617 C 763.10731,321.61373 758.05326,325.63105 756.74267,331.3045 L 749.30517,363.5545 C 748.44941,367.26243 749.32914,371.15825 751.69521,374.13864 C 754.06128,377.11903 757.65603,378.85945 761.46142,378.867 L 892.93017,378.867 C 923.24425,378.867 944.16687,379.27677 961.27392,374.33575 C 978.38097,369.39473 990.66904,355.65737 994.99267,336.9295 L 1038.4302,148.742 C 1039.2872,145.02873 1038.4036,141.12735 1036.0304,138.14563 C 1033.6572,135.16391 1030.0535,133.42752 1026.2427,133.4295 L 943.83642,133.4295 C 938.01356,133.42623 932.95951,137.44355 931.64892,143.117 L 904.55517,260.492 L 882.11767,260.492 L 907.93017,148.742 C 908.78715,145.02874 907.9036,141.12736 905.5304,138.14565 C 903.15719,135.16393 899.55354,133.42754 895.74267,133.4295 L 812.36767,133.4295 C 810.12122,133.43967 807.91903,134.05499 805.99267,135.21075 C 804.06631,134.05499 801.86412,133.43967 799.61767,133.4295 L 746.71142,133.4295 L 751.30517,113.58575 C 752.16093,109.87782 751.2812,105.98201 748.91513,103.00161 C 746.54906,100.02122 742.95431,98.280803 739.14892,98.273253 L 656.46142,98.273253 C 650.63856,98.269983 645.58451,102.2873 644.27392,107.96075 L 638.39892,133.4295 L 624.89892,133.4295 L 618.74267,133.4295 L 571.99267,133.4295 L 576.58642,113.58575 C 577.44218,109.87782 576.56245,105.98201 574.19638,103.00161 C 571.83031,100.02122 568.23556,98.280803 564.43017,98.273253 L 481.74267,98.273253 C 475.91981,98.269983 470.86576,102.2873 469.55517,107.96075 L 463.68017,133.4295 L 444.02392,133.4295 C 438.20106,133.42623 433.14701,137.44355 431.83642,143.117 L 430.02392,151.0545 C 421.62416,141.56409 407.22229,133.4295 387.68017,133.4295 L 252.46142,133.4295 C 237.21293,133.42952 222.49219,141.92039 213.46142,155.77325 L 215.08642,148.742 C 215.69431,146.1414 215.45338,143.41448 214.39892,140.96075 C 216.4166,139.22739 217.8288,136.89563 218.43017,134.3045 L 228.77392,89.523253 C 229.6309,85.809993 228.74735,81.908613 226.37415,78.926903 C 224.00094,75.945183 220.39729,74.208793 216.58642,74.210753 L 133.14892,74.210753 z M 1230.6802,74.210753 C 1225.8279,74.217643 1221.418,77.031713 1219.3676,81.429503 L 1121.7426,290.742 L 1131.5239,248.367 C 1132.3797,244.65907 1131.4999,240.76324 1129.1338,237.78285 C 1126.7678,234.80245 1123.173,233.06204 1119.3676,233.0545 L 1035.6176,233.0545 C 1029.7948,233.05126 1024.7408,237.06857 1023.4302,242.742 L 1009.0864,304.83575 C 1008.2294,308.54901 1009.113,312.45039 1011.4862,315.43211 C 1013.8594,318.41382 1017.463,320.15022 1021.2739,320.14825 L 1105.0239,320.14825 C 1106.1113,320.14302 1107.1933,319.99595 1108.2426,319.71075 L 1103.9926,328.83575 C 1102.1877,332.70937 1102.4865,337.2366 1104.7849,340.83938 C 1107.0834,344.44215 1111.0629,346.62121 1115.3364,346.617 L 1157.4926,346.617 C 1162.3561,346.62224 1166.7813,343.80626 1168.8364,339.39825 L 1284.1802,91.992003 C 1285.9828,88.123403 1285.6872,83.602493 1283.3963,80.001533 C 1281.1053,76.400563 1277.1356,74.217203 1272.8676,74.210753 L 1230.6802,74.210753 z M 1324.0552,74.210753 C 1319.2029,74.217643 1314.793,77.031713 1312.7426,81.429503 L 1197.3676,328.83575 C 1195.5627,332.70937 1195.8615,337.2366 1198.1599,340.83938 C 1200.4584,344.44215 1204.4379,346.62121 1208.7114,346.617 L 1250.8676,346.617 C 1255.7311,346.62224 1260.1563,343.80626 1262.2114,339.39825 L 1377.5552,91.992003 C 1379.3578,88.123403 1379.0622,83.602493 1376.7713,80.001533 C 1374.4803,76.400563 1370.5106,74.217203 1366.2426,74.210753 L 1324.0552,74.210753 z M 1058.6176,133.4295 C 1052.7948,133.42628 1047.7408,137.44358 1046.4302,143.117 L 1032.0864,205.21075 C 1031.2294,208.92401 1032.113,212.82539 1034.4862,215.80711 C 1036.8594,218.78882 1040.463,220.52522 1044.2739,220.52325 L 1128.0239,220.52325 C 1133.8561,220.52318 1138.9133,216.49037 1140.2114,210.8045 L 1154.5239,148.742 C 1155.3797,145.03407 1154.4999,141.13824 1152.1338,138.15784 C 1149.7678,135.17745 1146.173,133.43703 1142.3676,133.4295 L 1058.6176,133.4295 z M 321.14892,190.1795 C 324.49626,190.1795 326.64455,190.50681 327.77392,190.77325 C 327.79548,191.54482 327.76376,193.22107 327.61767,195.492 L 310.96142,195.492 C 311.68961,194.18337 312.37493,193.13181 312.93017,192.5545 C 314.73432,190.67863 315.72887,190.1795 321.14892,190.1795 z M 733.61767,190.1795 L 789.30517,190.1795 L 772.99267,260.8045 C 772.10059,260.60116 771.18888,260.49636 770.27392,260.492 C 741.16337,260.492 724.25443,259.6817 717.93017,258.867 C 718.14687,257.44728 718.14107,257.233 718.86767,254.08575 L 733.61767,190.1795 z"
+       id="path3181"
+       sodipodi:nodetypes="cccccccccscccssccssccccsccccsscccsccccsccccccccsccccccccccsccsccsccccccsccccccsccccccccsccccccccccccccsccccccsccccsccccsccccscccccsccccscccccsccccsccccccscccccccc" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Jetty"
+     style="display:inline"
+     transform="translate(-1.845606,-221.31978)">
+    <path
+       id="path3193"
+       style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:25;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 468.5751,304.59386 L 460.4501,339.75011 L 430.85635,339.75011 L 423.54385,371.50011 L 453.1376,371.50011 L 435.6376,447.21886 C 431.11514,466.80783 434.69559,501.46886 475.60635,501.46886 L 574.3876,501.46886 L 582.3876,466.81261 C 513.82035,466.81262 513.81207,466.80768 518.8251,445.09386 L 535.8251,371.50011 L 598.2626,371.50011 L 604.3876,371.50011 L 627.85635,371.50011 L 610.35635,447.21886 C 605.83388,466.80782 609.41434,501.46886 650.3251,501.46886 L 749.10635,501.46886 L 757.10635,466.81261 C 688.53909,466.81261 688.53082,466.80768 693.54385,445.09386 L 710.54385,371.50011 L 779.10635,371.50011 L 786.4501,339.75011 L 717.85635,339.75011 L 725.98135,304.59386 L 643.29385,304.59386 L 635.16885,339.75011 L 611.73135,339.75011 L 605.5751,339.75011 L 543.1376,339.75011 L 551.2626,304.59386 L 468.5751,304.59386 z M 106.32205,339.73892 L 65.825288,515.14967 C 63.418773,522.20878 59.752966,521.8319 51.570815,522.15277 L 50.608209,562.10092 L 117.99063,561.86026 C 131.06603,561.13831 139.21491,553.72589 144.58946,535.43638 L 189.76977,339.73892 L 106.32205,339.73892 z M 119.98951,280.53864 L 109.65558,325.29983 L 193.1033,325.29983 L 203.43723,280.53864 L 119.98951,280.53864 z M 239.29385,339.75011 C 224.85477,339.75012 208.26802,352.04925 204.6376,371.50011 L 188.6376,436.96886 C 179.27284,477.53211 184.20976,501.46886 214.48135,501.46886 L 385.3251,501.46886 L 393.3251,466.81261 L 265.29385,466.81261 L 272.16885,436.96886 L 400.2001,436.96886 C 402.2537,428.07372 410.80305,415.14706 415.3251,371.50011 C 416.6053,365.955 407.76262,339.75011 374.5126,339.75011 L 239.29385,339.75011 z M 307.98135,371.50011 C 325.75108,371.50011 330.91518,377.05789 325.2001,401.81261 L 280.29385,401.81261 C 285.80848,377.9261 293.45487,371.5001 307.98135,371.50011 z M 799.21238,339.73892 L 769.87735,466.80292 C 769.87735,466.80292 761.87689,501.45673 810.59962,501.45673 L 893.34691,501.45673 C 889.21672,519.34655 887.23544,527.9284 839.14197,527.9284 L 755.76542,527.9284 L 748.32055,560.1757 L 879.79056,560.1757 C 940.41876,560.1757 962.20636,560.1757 969.65123,527.9284 L 1013.0982,339.73892 L 930.6824,339.73892 L 901.34737,466.80292 L 853.2539,466.80292 L 882.58893,339.73892 L 799.21238,339.73892 z M 1045.4591,339.73892 L 1031.1249,401.82701 L 1114.8716,401.82701 L 1129.2058,339.73892 L 1045.4591,339.73892 z M 1022.4578,439.36864 L 1008.1236,501.45673 L 1091.8702,501.45673 L 1106.2044,439.36864 L 1022.4578,439.36864 z M 1217.5384,280.53864 L 1102.1787,527.9284 L 1144.3505,527.9284 L 1259.7102,280.53864 L 1217.5384,280.53864 z M 1310.9112,280.53864 L 1195.5515,527.9284 L 1237.7233,527.9284 L 1353.083,280.53864 L 1310.9112,280.53864 z" />
+    <path
+       d="M 468.5751,304.59386 L 460.4501,339.75011 L 430.85635,339.75011 L 423.54385,371.50011 L 453.1376,371.50011 L 435.6376,447.21886 C 431.11514,466.80783 434.69559,501.46886 475.60635,501.46886 L 574.3876,501.46886 L 582.3876,466.81261 C 513.82035,466.81262 513.81207,466.80768 518.8251,445.09386 L 535.8251,371.50011 L 598.2626,371.50011 L 604.3876,371.50011 L 627.85635,371.50011 L 610.35635,447.21886 C 605.83388,466.80782 609.41434,501.46886 650.3251,501.46886 L 749.10635,501.46886 L 757.10635,466.81261 C 688.53909,466.81261 688.53082,466.80768 693.54385,445.09386 L 710.54385,371.50011 L 779.10635,371.50011 L 786.4501,339.75011 L 717.85635,339.75011 L 725.98135,304.59386 L 643.29385,304.59386 L 635.16885,339.75011 L 611.73135,339.75011 L 605.5751,339.75011 L 543.1376,339.75011 L 551.2626,304.59386 L 468.5751,304.59386 z M 106.32205,339.73892 L 65.825288,515.14967 C 63.418773,522.20878 59.752966,521.8319 51.570815,522.15277 L 50.608209,562.10092 L 117.99063,561.86026 C 131.06603,561.13831 139.21491,553.72589 144.58946,535.43638 L 189.76977,339.73892 L 106.32205,339.73892 z M 119.98951,280.53864 L 109.65558,325.29983 L 193.1033,325.29983 L 203.43723,280.53864 L 119.98951,280.53864 z M 239.29385,339.75011 C 224.85477,339.75012 208.26802,352.04925 204.6376,371.50011 L 188.6376,436.96886 C 179.27284,477.53211 184.20976,501.46886 214.48135,501.46886 L 385.3251,501.46886 L 393.3251,466.81261 L 265.29385,466.81261 L 272.16885,436.96886 L 400.2001,436.96886 C 402.2537,428.07372 410.80305,415.14706 415.3251,371.50011 C 416.6053,365.955 407.76262,339.75011 374.5126,339.75011 L 239.29385,339.75011 z M 307.98135,371.50011 C 325.75108,371.50011 330.91518,377.05789 325.2001,401.81261 L 280.29385,401.81261 C 285.80848,377.9261 293.45487,371.5001 307.98135,371.50011 z M 799.21238,339.73892 L 769.87735,466.80292 C 769.87735,466.80292 761.87689,501.45673 810.59962,501.45673 L 893.34691,501.45673 C 889.21672,519.34655 887.23544,527.9284 839.14197,527.9284 L 755.76542,527.9284 L 748.32055,560.1757 L 879.79056,560.1757 C 940.41876,560.1757 962.20636,560.1757 969.65123,527.9284 L 1013.0982,339.73892 L 930.6824,339.73892 L 901.34737,466.80292 L 853.2539,466.80292 L 882.58893,339.73892 L 799.21238,339.73892 z M 1045.4591,339.73892 L 1031.1249,401.82701 L 1114.8716,401.82701 L 1129.2058,339.73892 L 1045.4591,339.73892 z M 1022.4578,439.36864 L 1008.1236,501.45673 L 1091.8702,501.45673 L 1106.2044,439.36864 L 1022.4578,439.36864 z M 1217.5384,280.53864 L 1102.1787,527.9284 L 1144.3505,527.9284 L 1259.7102,280.53864 L 1217.5384,280.53864 z M 1310.9112,280.53864 L 1195.5515,527.9284 L 1237.7233,527.9284 L 1353.083,280.53864 L 1310.9112,280.53864 z"
+       style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:30;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3316" />
+  </g>
+</svg>
diff --git a/logos/jetty-logo.png b/logos/jetty-logo.png
new file mode 100644
index 0000000..b66f4d9
--- /dev/null
+++ b/logos/jetty-logo.png
Binary files differ
diff --git a/logos/jetty-logo.svg b/logos/jetty-logo.svg
new file mode 100644
index 0000000..97ef14f
--- /dev/null
+++ b/logos/jetty-logo.svg
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="1400"
+   height="400"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.48.3.1 r9886"
+   version="1.0"
+   sodipodi:docname="jetty-logo.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="jetty-logo-200.png"
+   inkscape:export-xdpi="12.86"
+   inkscape:export-ydpi="12.86">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+    <inkscape:perspective
+       id="perspective2390"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       sodipodi:type="inkscape:persp3d" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.87428571"
+     inkscape:cx="700"
+     inkscape:cy="200"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer2"
+     showgrid="false"
+     inkscape:window-width="1679"
+     inkscape:window-height="1001"
+     inkscape:window-x="1"
+     inkscape:window-y="48"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     showborder="true"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Jetty"
+     style="display:inline"
+     transform="translate(-1.845606,-221.31978)">
+    <path
+       id="path3193"
+       style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:25;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 468.5751,304.59386 L 460.4501,339.75011 L 430.85635,339.75011 L 423.54385,371.50011 L 453.1376,371.50011 L 435.6376,447.21886 C 431.11514,466.80783 434.69559,501.46886 475.60635,501.46886 L 574.3876,501.46886 L 582.3876,466.81261 C 513.82035,466.81262 513.81207,466.80768 518.8251,445.09386 L 535.8251,371.50011 L 598.2626,371.50011 L 604.3876,371.50011 L 627.85635,371.50011 L 610.35635,447.21886 C 605.83388,466.80782 609.41434,501.46886 650.3251,501.46886 L 749.10635,501.46886 L 757.10635,466.81261 C 688.53909,466.81261 688.53082,466.80768 693.54385,445.09386 L 710.54385,371.50011 L 779.10635,371.50011 L 786.4501,339.75011 L 717.85635,339.75011 L 725.98135,304.59386 L 643.29385,304.59386 L 635.16885,339.75011 L 611.73135,339.75011 L 605.5751,339.75011 L 543.1376,339.75011 L 551.2626,304.59386 L 468.5751,304.59386 z M 106.32205,339.73892 L 65.825288,515.14967 C 63.418773,522.20878 59.752966,521.8319 51.570815,522.15277 L 50.608209,562.10092 L 117.99063,561.86026 C 131.06603,561.13831 139.21491,553.72589 144.58946,535.43638 L 189.76977,339.73892 L 106.32205,339.73892 z M 119.98951,280.53864 L 109.65558,325.29983 L 193.1033,325.29983 L 203.43723,280.53864 L 119.98951,280.53864 z M 239.29385,339.75011 C 224.85477,339.75012 208.26802,352.04925 204.6376,371.50011 L 188.6376,436.96886 C 179.27284,477.53211 184.20976,501.46886 214.48135,501.46886 L 385.3251,501.46886 L 393.3251,466.81261 L 265.29385,466.81261 L 272.16885,436.96886 L 400.2001,436.96886 C 402.2537,428.07372 410.80305,415.14706 415.3251,371.50011 C 416.6053,365.955 407.76262,339.75011 374.5126,339.75011 L 239.29385,339.75011 z M 307.98135,371.50011 C 325.75108,371.50011 330.91518,377.05789 325.2001,401.81261 L 280.29385,401.81261 C 285.80848,377.9261 293.45487,371.5001 307.98135,371.50011 z M 799.21238,339.73892 L 769.87735,466.80292 C 769.87735,466.80292 761.87689,501.45673 810.59962,501.45673 L 893.34691,501.45673 C 889.21672,519.34655 887.23544,527.9284 839.14197,527.9284 L 755.76542,527.9284 L 748.32055,560.1757 L 879.79056,560.1757 C 940.41876,560.1757 962.20636,560.1757 969.65123,527.9284 L 1013.0982,339.73892 L 930.6824,339.73892 L 901.34737,466.80292 L 853.2539,466.80292 L 882.58893,339.73892 L 799.21238,339.73892 z M 1045.4591,339.73892 L 1031.1249,401.82701 L 1114.8716,401.82701 L 1129.2058,339.73892 L 1045.4591,339.73892 z M 1022.4578,439.36864 L 1008.1236,501.45673 L 1091.8702,501.45673 L 1106.2044,439.36864 L 1022.4578,439.36864 z M 1217.5384,280.53864 L 1102.1787,527.9284 L 1144.3505,527.9284 L 1259.7102,280.53864 L 1217.5384,280.53864 z M 1310.9112,280.53864 L 1195.5515,527.9284 L 1237.7233,527.9284 L 1353.083,280.53864 L 1310.9112,280.53864 z" />
+    <path
+       d="M 468.5751,304.59386 L 460.4501,339.75011 L 430.85635,339.75011 L 423.54385,371.50011 L 453.1376,371.50011 L 435.6376,447.21886 C 431.11514,466.80783 434.69559,501.46886 475.60635,501.46886 L 574.3876,501.46886 L 582.3876,466.81261 C 513.82035,466.81262 513.81207,466.80768 518.8251,445.09386 L 535.8251,371.50011 L 598.2626,371.50011 L 604.3876,371.50011 L 627.85635,371.50011 L 610.35635,447.21886 C 605.83388,466.80782 609.41434,501.46886 650.3251,501.46886 L 749.10635,501.46886 L 757.10635,466.81261 C 688.53909,466.81261 688.53082,466.80768 693.54385,445.09386 L 710.54385,371.50011 L 779.10635,371.50011 L 786.4501,339.75011 L 717.85635,339.75011 L 725.98135,304.59386 L 643.29385,304.59386 L 635.16885,339.75011 L 611.73135,339.75011 L 605.5751,339.75011 L 543.1376,339.75011 L 551.2626,304.59386 L 468.5751,304.59386 z M 106.32205,339.73892 L 65.825288,515.14967 C 63.418773,522.20878 59.752966,521.8319 51.570815,522.15277 L 50.608209,562.10092 L 117.99063,561.86026 C 131.06603,561.13831 139.21491,553.72589 144.58946,535.43638 L 189.76977,339.73892 L 106.32205,339.73892 z M 119.98951,280.53864 L 109.65558,325.29983 L 193.1033,325.29983 L 203.43723,280.53864 L 119.98951,280.53864 z M 239.29385,339.75011 C 224.85477,339.75012 208.26802,352.04925 204.6376,371.50011 L 188.6376,436.96886 C 179.27284,477.53211 184.20976,501.46886 214.48135,501.46886 L 385.3251,501.46886 L 393.3251,466.81261 L 265.29385,466.81261 L 272.16885,436.96886 L 400.2001,436.96886 C 402.2537,428.07372 410.80305,415.14706 415.3251,371.50011 C 416.6053,365.955 407.76262,339.75011 374.5126,339.75011 L 239.29385,339.75011 z M 307.98135,371.50011 C 325.75108,371.50011 330.91518,377.05789 325.2001,401.81261 L 280.29385,401.81261 C 285.80848,377.9261 293.45487,371.5001 307.98135,371.50011 z M 799.21238,339.73892 L 769.87735,466.80292 C 769.87735,466.80292 761.87689,501.45673 810.59962,501.45673 L 893.34691,501.45673 C 889.21672,519.34655 887.23544,527.9284 839.14197,527.9284 L 755.76542,527.9284 L 748.32055,560.1757 L 879.79056,560.1757 C 940.41876,560.1757 962.20636,560.1757 969.65123,527.9284 L 1013.0982,339.73892 L 930.6824,339.73892 L 901.34737,466.80292 L 853.2539,466.80292 L 882.58893,339.73892 L 799.21238,339.73892 z M 1045.4591,339.73892 L 1031.1249,401.82701 L 1114.8716,401.82701 L 1129.2058,339.73892 L 1045.4591,339.73892 z M 1022.4578,439.36864 L 1008.1236,501.45673 L 1091.8702,501.45673 L 1106.2044,439.36864 L 1022.4578,439.36864 z M 1217.5384,280.53864 L 1102.1787,527.9284 L 1144.3505,527.9284 L 1259.7102,280.53864 L 1217.5384,280.53864 z M 1310.9112,280.53864 L 1195.5515,527.9284 L 1237.7233,527.9284 L 1353.083,280.53864 L 1310.9112,280.53864 z"
+       style="fill:#fc390e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:30;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3316" />
+  </g>
+</svg>
diff --git a/pom.xml b/pom.xml
index 651d358..512ee9e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,41 +1,49 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-parent</artifactId>
-    <version>23</version>
+    <version>25</version>
   </parent>
+
+  <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-project</artifactId>
-  <version>9.2.22-SNAPSHOT</version>
+  <version>9.3.19-SNAPSHOT</version>
   <name>Jetty :: Project</name>
   <url>http://www.eclipse.org/jetty</url>
   <packaging>pom</packaging>
+
   <properties>
     <jetty.url>http://www.eclipse.org/jetty</jetty.url>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <build-support-version>1.1</build-support-version>
+    <build-support-version>1.4</build-support-version>
     <slf4j-version>1.6.6</slf4j-version>
     <jetty-test-policy-version>1.2</jetty-test-policy-version>
-    <npn.api.version>1.1.1.v20141010</npn.api.version>
     <alpn.api.version>1.1.3.v20160715</alpn.api.version>
+    <jsp.version>8.0.33</jsp.version>
     <!-- default values are unsupported, but required to be defined for reactor sanity reasons -->
-    <npn.version>undefined</npn.version>
     <alpn.version>undefined</alpn.version>
   </properties>
+
   <scm>
     <connection>scm:git:https://github.com/eclipse/jetty.project.git</connection>
     <developerConnection>scm:git:git@github.com:eclipse/jetty.project.git</developerConnection>
     <url>https://github.com/eclipse/jetty.project</url>
-    <tag>HEAD</tag>
+    <tag>jetty-9.3.13.M0</tag>
   </scm>
+
   <build>
     <defaultGoal>install</defaultGoal>
     <plugins>
       <plugin>
         <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.3</version>
         <configuration>
-          <source>1.7</source>
-          <target>1.7</target>
+          <source>1.8</source>
+          <target>1.8</target>
+          <testSource>1.8</testSource>
+          <testTarget>1.8</testTarget>
           <verbose>false</verbose>
         </configuration>
       </plugin>
@@ -135,8 +143,8 @@
                   <message>[ERROR] OLD MAVEN [${maven.version}] in use, Jetty ${project.version} requires Maven 3.0.0 or newer</message>
                 </requireMavenVersion>
                 <requireJavaVersion>
-                  <version>[1.7.0-40,)</version>
-                  <message>[ERROR] OLD JDK [${java.version}] in use. Jetty ${project.version} requires JDK 1.7.0_40 or newer</message>
+                  <version>[1.8.0,)</version>
+                  <message>[ERROR] OLD JDK [${java.version}] in use. Jetty ${project.version} requires JDK 1.8.0 or newer</message>
                 </requireJavaVersion>
                 <versionTxtRule implementation="org.eclipse.jetty.toolchain.enforcer.rules.VersionTxtRule" />
                 <versionOsgiRule implementation="org.eclipse.jetty.toolchain.enforcer.rules.RequireOsgiCompatibleVersionRule" />
@@ -174,7 +182,7 @@
       <plugin>
         <groupId>org.jacoco</groupId>
         <artifactId>jacoco-maven-plugin</artifactId>
-        <version>0.7.7.201606060606</version>
+        <version>0.7.8</version>
         <configuration>
           <excludes>
             <!-- build tools -->
@@ -239,7 +247,7 @@
           </execution>
         </executions>
         <configuration>
-          <targetJdk>1.7</targetJdk>
+          <targetJdk>1.8</targetJdk>
           <rulesets>
             <ruleset>jetty/pmd_logging_ruleset.xml</ruleset>
           </rulesets>
@@ -276,6 +284,8 @@
             <exclude>jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/DefaultPolicyLoader.java</exclude>
             <exclude>jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/PolicyFileScanner.java</exclude>
             <exclude>jetty-ant/**</exclude>
+            <exclude>jetty-infinispan/**</exclude>
+            <exclude>tests/test-sessions/test-infinispan-sessions/**</exclude>
           </excludes>
         </configuration>
         <executions>
@@ -288,13 +298,47 @@
           </execution>
         </executions>
       </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>manifest</goal>
+            </goals>
+           </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+         <archive>
+            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+         </archive>
+        </configuration>
+     </plugin>
     </plugins>
+
     <pluginManagement>
       <plugins>
         <plugin>
-          <groupId>org.eclipse.jetty.toolchain</groupId>
-          <artifactId>jetty-version-maven-plugin</artifactId>
-          <version>2.3</version>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-assembly-plugin</artifactId>
+          <version>3.0.0</version>
+          <dependencies>
+            <dependency>
+              <groupId>org.eclipse.jetty.toolchain</groupId>
+              <artifactId>jetty-assembly-descriptors</artifactId>
+              <version>1.0</version>
+            </dependency>
+          </dependencies>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-dependency-plugin</artifactId>
+          <version>3.0.0</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
@@ -307,6 +351,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-jar-plugin</artifactId>
+          <version>3.0.2</version>
           <configuration>
             <archive>
               <manifestEntries>
@@ -319,86 +364,21 @@
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-surefire-plugin</artifactId>
-          <version>2.17</version>
-          <configuration>
-            <argLine>@{argLine} -showversion -Xmx1g -Xms1g -XX:+PrintGCDetails</argLine>
-            <failIfNoTests>false</failIfNoTests>
-            <forkCount>1</forkCount>
-            <systemProperties>
-              <!--
-              <property>
-                <name>org.eclipse.jetty.io.AbstractBuffer.boundsChecking</name>
-                <value>true</value>
-              </property>
-              -->
-              <property>
-                <name>java.io.tmpdir</name>
-                <value>${project.build.directory}</value>
-              </property>
-            </systemProperties>
-          </configuration>
-        </plugin>
-        <plugin>
-          <groupId>org.apache.felix</groupId>
-          <artifactId>maven-bundle-plugin</artifactId>
-          <extensions>true</extensions>
-          <configuration>
-            <instructions>
-              <Bundle-SymbolicName>${bundle-symbolic-name}</Bundle-SymbolicName>
-              <Bundle-RequiredExecutionEnvironment>JavaSE-1.7</Bundle-RequiredExecutionEnvironment>
-              <Bundle-DocURL>${jetty.url}</Bundle-DocURL>
-              <Bundle-Vendor>Eclipse Jetty Project</Bundle-Vendor>
-              <Bundle-Classpath>.</Bundle-Classpath>
-              <Export-Package>${bundle-symbolic-name}.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package>
-              <Bundle-Copyright>Copyright (c) 2008-2017 Mort Bay Consulting Pty. Ltd.</Bundle-Copyright>
-              <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>
-            </instructions>
-          </configuration>
-        </plugin>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-assembly-plugin</artifactId>
-          <dependencies>
-            <dependency>
-              <groupId>org.eclipse.jetty.toolchain</groupId>
-              <artifactId>jetty-assembly-descriptors</artifactId>
-              <version>1.0</version>
-            </dependency>
-          </dependencies>
-        </plugin>
-        <plugin>
-          <groupId>org.codehaus.mojo</groupId>
-          <artifactId>findbugs-maven-plugin</artifactId>
-          <configuration>
-            <findbugsXmlOutput>true</findbugsXmlOutput>
-            <xmlOutput>true</xmlOutput>
-            <effort>Max</effort>
-            <onlyAnalyze>org.eclipse.jetty.*</onlyAnalyze>
-          </configuration>
-        </plugin>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-dependency-plugin</artifactId>
-          <version>2.10</version>
-        </plugin>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-jxr-plugin</artifactId>
-        </plugin>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-javadoc-plugin</artifactId>
+          <version>2.10.4</version>
           <configuration>
+            <charset>UTF-8</charset>
+            <docencoding>UTF-8</docencoding>
+            <encoding>UTF-8</encoding>
             <docfilessubdirs>true</docfilessubdirs>
             <detectLinks>false</detectLinks>
             <detectJavaApiLink>true</detectJavaApiLink>
             <excludePackageNames>com.acme.*;org.slf4j.*;org.mortbay.*</excludePackageNames>
             <links>
-              <link>http://docs.oracle.com/javase/7/docs/api/</link>
+              <link>http://docs.oracle.com/javase/8/docs/api/</link>
               <link>http://docs.oracle.com/javaee/7/api/</link>
+              <link>http://junit.org/junit4/javadoc/latest/</link>
               <link>http://download.eclipse.org/jetty/stable-9/apidocs/</link>
-              <link>http://junit.sourceforge.net/javadoc/</link>
             </links>
             <tags>
               <tag>
@@ -406,6 +386,56 @@
                 <placement>X</placement>
                 <head />
               </tag>
+              <tag>
+                <name>phase</name>
+                <placement>t</placement>
+                <head>Phase:</head>
+              </tag>
+              <tag>
+                <name>goal</name>
+                <placement>t</placement>
+                <head>Goal:</head>
+              </tag>
+              <tag>
+                <name>description</name>
+                <placement>a</placement>
+                <head>Description:</head>
+              </tag>
+              <tag>
+                <name>parameter</name>
+                <placement>f</placement>
+                <head>Parameter:</head>
+              </tag>
+              <tag>
+                <name>required</name>
+                <placement>f</placement>
+                <head>Required:</head>
+              </tag>
+              <tag>
+                <name>readonly</name>
+                <placement>f</placement>
+                <head>Read-Only:</head>
+              </tag>
+              <tag>
+                <name>execute</name>
+                <placement>X</placement>
+                <head />
+              </tag>
+              <tag>
+                <name>requiresDependencyResolution</name>
+                <placement>X</placement>
+                <head />
+              </tag>
+              <tag>
+                <name>requiresProject</name>
+                <placement>X</placement>
+                <head />
+              </tag>
+              <tag>
+                <name>threadSafe</name>
+                <placement>X</placement>
+                <head />
+              </tag>
             </tags>
             <header>
               <![CDATA[
@@ -425,23 +455,86 @@
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-jxr-plugin</artifactId>
+          <version>2.5</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-pmd-plugin</artifactId>
-          <version>2.7.1</version>
+          <version>3.7</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-plugin</artifactId>
+          <version>2.19.1</version>
+          <configuration>
+            <argLine>@{argLine} -Dfile.encoding=UTF-8 -Duser.language=en -Duser.region=US -showversion -Xmx1g -Xms1g -XX:+PrintGCDetails</argLine>
+            <failIfNoTests>false</failIfNoTests>
+            <forkCount>1</forkCount>
+            <systemProperties>
+              <property>
+                <name>java.io.tmpdir</name>
+                <value>${project.build.directory}</value>
+              </property>
+            </systemProperties>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-war-plugin</artifactId>
+          <version>3.0.0</version>
+        </plugin>
+        <plugin>
+          <groupId>org.eclipse.jetty.toolchain</groupId>
+          <artifactId>jetty-version-maven-plugin</artifactId>
+          <version>2.4</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.felix</groupId>
+          <artifactId>maven-bundle-plugin</artifactId>
+          <version>3.0.1</version>
+          <extensions>true</extensions>
+          <configuration>
+            <supportedProjectTypes>
+              <supportedProjectType>jar</supportedProjectType>
+              <supportedProjectType>maven-plugin</supportedProjectType>
+            </supportedProjectTypes>
+            <instructions>
+              <Bundle-SymbolicName>${bundle-symbolic-name}</Bundle-SymbolicName>
+              <Bundle-Description>Jetty module for ${project.name}</Bundle-Description>
+              <Bundle-RequiredExecutionEnvironment>JavaSE-1.8</Bundle-RequiredExecutionEnvironment>
+              <Bundle-DocURL>${jetty.url}</Bundle-DocURL>
+              <Bundle-Vendor>Eclipse Jetty Project</Bundle-Vendor>
+              <Bundle-Classpath>.</Bundle-Classpath>
+              <Export-Package>${bundle-symbolic-name}.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package>
+              <Bundle-Copyright>Copyright (c) 2008-2017 Mort Bay Consulting Pty. Ltd.</Bundle-Copyright>
+              <Import-Package>javax.servlet*;version="[2.6.0,3.2)",javax.transaction*;version="[1.1,1.3)",org.eclipse.jetty*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",*</Import-Package>
+            </instructions>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>findbugs-maven-plugin</artifactId>
+          <configuration>
+            <findbugsXmlOutput>true</findbugsXmlOutput>
+            <xmlOutput>true</xmlOutput>
+            <effort>Max</effort>
+            <onlyAnalyze>org.eclipse.jetty.*</onlyAnalyze>
+          </configuration>
         </plugin>
       </plugins>
     </pluginManagement>
   </build>
+
   <reporting>
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jxr-plugin</artifactId>
-        <version>2.1</version>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-javadoc-plugin</artifactId>
-        <version>2.8</version>
         <configuration>
           <maxmemory>512m</maxmemory>
           <docfilessubdirs>true</docfilessubdirs>
@@ -452,7 +545,6 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-pmd-plugin</artifactId>
-        <version>2.7.1</version>
         <configuration>
           <targetJdk>1.7</targetJdk>
           <rulesets>
@@ -463,38 +555,27 @@
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
-        <version>2.5.2</version>
       </plugin>
     </plugins>
   </reporting>
-  <repositories>
-    <repository>
-      <snapshots>
-        <enabled>true</enabled>
-      </snapshots>
-      <id>sonatype-snapshots</id>
-      <name>Sonatype Jetty Snapshots</name>
-      <url>https://oss.sonatype.org/content/groups/jetty</url>
-    </repository>
-  </repositories>
+
   <modules>
     <module>jetty-ant</module>
     <module>jetty-util</module>
     <module>jetty-jmx</module>
     <module>jetty-io</module>
     <module>jetty-http</module>
+    <module>jetty-http2</module>
     <module>jetty-continuation</module>
     <module>jetty-server</module>
     <module>jetty-xml</module>
     <module>jetty-security</module>
     <module>jetty-servlet</module>
     <module>jetty-webapp</module>
-    <module>jetty-spdy</module>
     <module>jetty-fcgi</module>
     <module>jetty-websocket</module>
     <module>jetty-servlets</module>
     <module>jetty-util-ajax</module>
-    <module>jetty-jsp</module>
     <module>apache-jsp</module>
     <module>apache-jstl</module>
     <module>jetty-maven-plugin</module>
@@ -512,6 +593,8 @@
     <module>jetty-jaspi</module>
     <module>jetty-rewrite</module>
     <module>jetty-nosql</module>
+    <module>jetty-infinispan</module>
+    <module>jetty-gcloud</module>
     <module>tests</module>
     <module>examples</module>
     <module>jetty-quickstart</module>
@@ -521,12 +604,13 @@
     <module>jetty-http-spi</module>
     <module>jetty-osgi</module>
     <module>jetty-alpn</module>
-
-    <!-- modules that need fixed and added back, or simply dropped and not maintained
-    <module>jetty-rhttp</module>
-    -->
+    <module>jetty-bom</module>
+    <module>jetty-documentation</module>
+    <!-- modules that need fixed and added back, or simply dropped and not maintained -->
+    <!--<module>jetty-rhttp</module>-->
     <!--<module>jetty-overlay-deployer</module>-->
   </modules>
+
   <dependencyManagement>
     <dependencies>
       <dependency>
@@ -565,113 +649,35 @@
       <dependency>
         <groupId>org.eclipse.jetty.toolchain</groupId>
         <artifactId>jetty-schemas</artifactId>
-        <version>3.1.M0</version>
-      </dependency>
-
-      <dependency>
-        <groupId>javax.servlet.jsp</groupId>
-        <artifactId>javax.servlet.jsp-api</artifactId>
-        <version>2.3.1</version>
-      </dependency>
-
-      <dependency>
-        <groupId>org.glassfish.web</groupId>
-        <artifactId>javax.servlet.jsp</artifactId>
-        <version>2.3.2</version>
-      </dependency>
-
-      <dependency>
-        <groupId>org.eclipse.jetty.toolchain</groupId>
-        <artifactId>jetty-jsp-jdt</artifactId>
-        <version>2.3.3</version>
-      </dependency>
-
-
-      <dependency>
-        <groupId>javax.el</groupId>
-        <artifactId>javax.el-api</artifactId>
-        <version>3.0.0</version>
-      </dependency>
-
-      <dependency>
-        <groupId>org.glassfish</groupId>
-        <artifactId>javax.el</artifactId>
-        <version>3.0.0</version>
+        <version>3.1</version>
       </dependency>
 
       <dependency>
         <groupId>org.mortbay.jasper</groupId>
         <artifactId>apache-jsp</artifactId>
-        <version>8.0.33</version>
+        <version>${jsp.version}</version>
       </dependency>
 
       <dependency>
-        <groupId>org.eclipse.jetty.orbit</groupId>
-        <artifactId>org.eclipse.jdt.core</artifactId>
-       <version>3.8.2.v20130121</version>
-       <exclusions>
-          <exclusion>
-            <groupId>org.eclipse.jetty.orbit</groupId>
-            <artifactId>javax.servlet</artifactId>
-         </exclusion>
-        </exclusions>
+        <groupId>org.eclipse.jdt.core.compiler</groupId>
+        <artifactId>ecj</artifactId>
+       <version>4.4.2</version>
       </dependency>
 
-    <!-- JSTL Impl -->
-    <dependency>
-       <groupId>org.glassfish.web</groupId>
-       <artifactId>javax.servlet.jsp.jstl</artifactId>
-       <version>1.2.2</version>
-      <exclusions>
-        <exclusion>
-          <groupId>javax.servlet.jsp.jstl</groupId>
-          <artifactId>jstl-api</artifactId>
-        </exclusion>
-        <exclusion>
-          <groupId>javax.servlet</groupId>
-          <artifactId>servlet-api</artifactId>
-        </exclusion>
-        <exclusion>
-          <groupId>javax.servlet.jsp</groupId>
-          <artifactId>jsp-api</artifactId>
-        </exclusion>
-        <exclusion>
-          <groupId>javax.el</groupId>
-          <artifactId>el-api</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-
-    <dependency>
-      <groupId>org.apache.taglibs</groupId>
-      <artifactId>taglibs-standard-impl</artifactId>
-      <version>1.2.1</version>
-    </dependency>
+      <!-- JSTL Impl -->
+      <dependency>
+        <groupId>org.apache.taglibs</groupId>
+        <artifactId>taglibs-standard-impl</artifactId>
+        <version>1.2.5</version>
+      </dependency>
 
      <!-- JSTL API -->
       <dependency>
-        <groupId>org.eclipse.jetty.orbit</groupId>
-        <artifactId>javax.servlet.jsp.jstl</artifactId>
-        <version>1.2.0.v201105211821</version>
-        <exclusions>
-          <exclusion>
-            <groupId>org.eclipse.jetty.orbit</groupId>
-            <artifactId>javax.servlet</artifactId>
-          </exclusion>
-          <exclusion>
-            <groupId>org.eclipse.jetty.orbit</groupId>
-            <artifactId>javax.servlet.jsp</artifactId>
-          </exclusion>
-        </exclusions>
-      </dependency>
-
-      <dependency>
         <groupId>org.apache.taglibs</groupId>
         <artifactId>taglibs-standard-spec</artifactId>
-        <version>1.2.1</version>
+        <version>1.2.5</version>
       </dependency>
 
-
       <dependency>
         <groupId>org.eclipse.jetty.orbit</groupId>
         <artifactId>javax.activation</artifactId>
@@ -692,7 +698,6 @@
         <scope>provided</scope>
       </dependency>
 
-
       <!-- Old Deps -->
       <dependency>
         <groupId>org.apache.maven</groupId>
@@ -702,7 +707,7 @@
       <dependency>
         <groupId>org.eclipse.jetty.toolchain</groupId>
         <artifactId>jetty-test-helper</artifactId>
-        <version>3.0</version>
+        <version>4.0</version>
       </dependency>
       <dependency>
         <groupId>org.eclipse.jetty.toolchain</groupId>
@@ -739,17 +744,6 @@
         <artifactId>hamcrest-library</artifactId>
         <version>1.3</version>
       </dependency>
-      <dependency>
-        <groupId>org.mockito</groupId>
-        <artifactId>mockito-core</artifactId>
-        <version>1.9.5</version>
-        <exclusions>
-          <exclusion>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-          </exclusion>
-        </exclusions>
-      </dependency>
     </dependencies>
   </dependencyManagement>
   <!--
@@ -763,10 +757,50 @@
    -->
   <profiles>
     <profile>
+      <id>config</id>
+      <activation>
+        <file>
+          <exists>src/main/config</exists>
+        </file>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-assembly-plugin</artifactId>
+            <executions>
+              <execution>
+                <phase>package</phase>
+                <goals>
+                  <goal>single</goal>
+                </goals>
+                <configuration>
+                  <descriptorRefs>
+                    <descriptorRef>config</descriptorRef>
+                  </descriptorRefs>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
       <id>eclipse-release</id>
       <modules>
         <module>aggregates/jetty-all</module>
       </modules>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-compiler-plugin</artifactId>
+            <configuration>
+              <source>1.8</source>
+              <target>1.8</target>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
     </profile>
     <profile>
       <id>ci</id>
@@ -787,6 +821,7 @@
                 <phase>generate-resources</phase>
                 <goals>
                   <goal>update-version-text</goal>
+                  <goal>tag</goal>
                 </goals>
                 <configuration>
                   <refreshTags>true</refreshTags>
@@ -838,40 +873,17 @@
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-javadoc-plugin</artifactId>
-            <configuration>
-              <excludePackageNames>com.acme</excludePackageNames>
-              <links>
-                <link>http://docs.oracle.com/javase/7/docs/api/</link>
-                <link>http://docs.oracle.com/javaee/6/api</link>
-                <link>http://junit.sourceforge.net/javadoc/</link>
-              </links>
-              <tags>
-                <tag>
-                  <name>org.apache.xbean.XBean</name>
-                  <placement>X</placement>
-                  <head />
-                </tag>
-              </tags>
-              <header>
-                <![CDATA[
-                    <script type="text/javascript">
-                      var _gaq = _gaq || [];
-                      _gaq.push(['_setAccount', 'UA-1149868-7']);
-                      _gaq.push(['_trackPageview']);
-                      (function() {
-                        var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
-                        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
-                        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
-                      })();
-                   </script>
-                ]]>
-              </header>
-            </configuration>
           </plugin>
         </plugins>
       </build>
     </profile>
     <profile>
+      <id>compact3</id>
+      <modules>
+        <module>aggregates/jetty-all-compact3</module>
+      </modules>
+    </profile>
+    <profile>
       <id>api-change</id>
       <build>
         <plugins>
@@ -897,175 +909,6 @@
       </build>
     </profile>
     <profile>
-      <id>7u40</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_40</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.6.v20130911</npn.version>
-        <alpn.version>7.1.0.v20141016</alpn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u45</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_45</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.6.v20130911</npn.version>
-        <alpn.version>7.1.0.v20141016</alpn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u51</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_51</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.6.v20130911</npn.version>
-        <alpn.version>7.1.0.v20141016</alpn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u55</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_55</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.8.v20141013</npn.version>
-        <alpn.version>7.1.0.v20141016</alpn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u60</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_60</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.8.v20141013</npn.version>
-        <alpn.version>7.1.0.v20141016</alpn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u65</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_65</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.8.v20141013</npn.version>
-        <alpn.version>7.1.0.v20141016</alpn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u67</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_67</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.8.v20141013</npn.version>
-        <alpn.version>7.1.0.v20141016</alpn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u71</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_71</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.9.v20141016</npn.version>
-        <alpn.version>7.1.2.v20141202</alpn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u72</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_72</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.9.v20141016</npn.version>
-        <alpn.version>7.1.2.v20141202</alpn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u75</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_75</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.10.v20150130</npn.version>
-        <alpn.version>7.1.3.v20150130</alpn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u76</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_76</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.10.v20150130</npn.version>
-        <alpn.version>7.1.3.v20150130</alpn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u79</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_79</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.10.v20150130</npn.version>
-        <alpn.version>7.1.3.v20150130</alpn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u80</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_80</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.11.v20150415</npn.version>
-        <alpn.version>7.1.3.v20150130</alpn.version>
-      </properties>
-    </profile>
-    <profile>
       <id>8u00</id>
       <activation>
         <property>
@@ -1352,6 +1195,18 @@
       <properties>
         <alpn.version>8.1.11.v20170118</alpn.version>
       </properties>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-javadoc-plugin</artifactId>
+            <configuration>
+              <!-- Required for Java 8u121 or later -->
+              <additionalparam>&#45;&#45;allow-script-in-comments</additionalparam>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
     </profile>
     <profile>
       <id>8u131</id>
diff --git a/scripts/looptest.sh b/scripts/looptest.sh
new file mode 100755
index 0000000..1bd1778
--- /dev/null
+++ b/scripts/looptest.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set
+
+export PATH=$M3/bin:$PATH
+
+mvn clean install -DskipTests
+
+cd $MODULE
+
+let count=0
+
+while mvn $OPTS test -Dtest=$TESTNAME 2>&1 > target/lastbuild.log
+do
+    now=`date`
+    echo "Test Run $count - $now" 
+    let count=$count+1
+done
diff --git a/scripts/release-jetty.sh b/scripts/release-jetty.sh
new file mode 100755
index 0000000..7cd0cfe
--- /dev/null
+++ b/scripts/release-jetty.sh
@@ -0,0 +1,188 @@
+#!/bin/bash
+
+echo ""
+echo "-----------------------------------------------"
+echo "  Verify Environment"
+
+requiredExecutable() {
+    hash $1 2>/dev/null 
+    if [ $? != 0 ] ; then
+        echo "ERROR: $1 not found.  Install $1"
+        exit -1
+    fi
+}
+
+requiredExecutable "git"
+requiredExecutable "xmllint"
+requiredExecutable "sed"
+requiredExecutable "gpg"
+requiredExecutable "egrep"
+requiredExecutable "mvn"
+
+proceedyn() {
+    while true; do
+        read -p "$1 " yn
+        case ${yn:-$2} in
+            [Yy]* ) return 0;;
+            [Nn]* ) return 1;;
+            * ) echo "Please answer yes or no.";;
+        esac
+    done
+}
+
+echo ""
+echo "-----------------------------------------------"
+echo "  Collect Information About Release"
+
+function gitFindRemoteByUrl() {
+    URL="$1"
+    for GREMOTE in $(git remote); do
+        git ls-remote --get-url $GREMOTE | grep "$URL" 2>&1 > /dev/null
+        if [ $? -eq 0 ] ; then
+            echo $GREMOTE
+        fi
+    done
+    return 0
+}
+
+GIT_REMOTE_URL="github.com:eclipse/jetty.project.git"
+GIT_REMOTE_ID=$(gitFindRemoteByUrl "$GIT_REMOTE_URL")
+GIT_BRANCH_ID=$(git symbolic-ref -q --short HEAD || git describe --tags --exact-match)
+
+if [ -z "$GIT_REMOTE_ID" ] ; then
+    echo "ERROR: Unable to determine git remote id for $GIT_REMOTE_URL"
+    echo "Are you running this build from a properly cloned git local repository?"
+    exit -1
+fi
+
+# Ensure that git user is in their gpg key list
+GIT_USER_EMAIL=`git config --get user.email`
+
+#gpg -q --list-keys "$GIT_USER_EMAIL" 2>&1 > /dev/null
+#if [ $? != 0 ] ; then
+#    echo "ERROR: git user.email of $GIT_USER_EMAIL is not present in your gpg --list-keys"
+#    echo "Go ahead and make one $ gpg --gen-key"
+#    exit -1
+#fi
+
+VER_CURRENT=`sed -e "s/xmlns/ignore/" pom.xml | xmllint --xpath "/project/version/text()" -`
+echo "Current pom.xml Version: ${VER_CURRENT}"
+read -e -p "Release Version  ? " VER_RELEASE
+read -e -p "Next Dev Version ? " VER_NEXT
+# VER_RELEASE=9.3.5.v20151012
+# VER_NEXT=9.3.6-SNAPSHOT
+TAG_NAME="jetty-$VER_RELEASE"
+
+# Ensure tag doesn't exist (yet)
+git rev-parse --quiet --verify "$TAG_NAME" 2>&1 > /dev/null
+if [ $? -eq 0 ] ; then
+    echo ""
+    echo "ERROR: Git Tag $TAG_NAME already exists"
+    echo ""
+    git show -s "$TAG_NAME"
+    exit -1
+fi
+
+ALT_DEPLOY_DIR=$HOME/.m2/alt-deploy
+if [ ! -d "$ALT_DEPLOY_DIR" ] ; then
+    mkdir -p "$ALT_DEPLOY_DIR"
+fi
+
+# DEPLOY_OPTS="-Dmaven.test.failure.ignore=true"
+DEPLOY_OPTS="-Dtest=None"
+# DEPLOY_OPTS="$DEPLOY_OPTS -DaltDeploymentRepository=intarget::default::file://$ALT_DEPLOY_DIR/"
+
+echo ""
+echo "-----------------------------------------------"
+echo "  Release Plan Review"
+echo ""
+echo "Git Remote ID    : $GIT_REMOTE_ID"
+echo "Git Branch ID    : $GIT_BRANCH_ID"
+echo "Git user.email   : $GIT_USER_EMAIL"
+echo "Current Version  : $VER_CURRENT"
+echo "Release Version  : $VER_RELEASE"
+echo "Next Dev Version : $VER_NEXT"
+echo "Tag name         : $TAG_NAME"
+echo "Maven Deploy Opts: $DEPLOY_OPTS"
+
+reportMavenTestFailures() {
+    failFiles=$(egrep -lr --include="*.txt" -E "^Tests .* FAILURE" .)
+    oldIFS="$IFS"
+    IFS='
+'
+    IFS=${IFS:0:1}
+    failarray=( $failFiles )
+    IFS="$oldIFS"
+
+    for index in ${!failarray[@]}; do
+        echo ${failarray[index]}
+        cat ${failarray[index]}
+    done
+
+    if [ ${#failarray[@]} -gt 0 ] ; then
+        echo "There are ${#failarray[@]} Test Cases with failures"
+    else
+        echo "There are no testcases with failures"
+    fi
+}
+
+echo ""
+if proceedyn "Are you sure you want to release using above? (y/N)" n; then
+    echo ""
+    if proceedyn "Update VERSION.txt for $VER_RELEASE? (Y/n)" y; then
+        mvn -N -Pupdate-version generate-resources
+        cp VERSION.txt VERSION.txt.backup
+        cat VERSION.txt.backup | sed -e "s/$VER_CURRENT/$VER_RELEASE/" > VERSION.txt
+        rm VERSION.txt.backup
+        echo "VERIFY the following files (in a different console window) before continuing."
+        echo "   VERSION.txt - top section"
+        echo "   target/vers-tag.txt - for the tag commit message"
+    fi
+
+    # This is equivalent to 'mvn release:prepare'
+    if proceedyn "Update project.versions for $VER_RELEASE? (Y/n)" y; then
+        mvn org.codehaus.mojo:versions-maven-plugin:2.1:set \
+            -DoldVersion="$VER_CURRENT" \
+            -DnewVersion="$VER_RELEASE"
+    fi
+    if proceedyn "Commit $VER_RELEASE updates? (Y/n)" y; then
+        git commit -a -m "Updating to version $VER_RELEASE"
+    fi
+    if proceedyn "Create Tag $TAG_NAME? (Y/n)" y; then
+        echo "TODO: Sign tags with GIT_USER_EMAIL=$GIT_USER_EMAIL"
+        echo "Using target/vers-tag.txt as tag text"
+        git tag --file=target/vers-tag.txt $TAG_NAME
+    fi
+
+    # This is equivalent to 'mvn release:perform'
+    if proceedyn "Build/Deploy from tag $TAG_NAME? (Y/n)" y; then
+        git checkout $TAG_NAME
+        mvn clean package source:jar javadoc:jar gpg:sign deploy \
+            -Peclipse-release $DEPLOY_OPTS
+        reportMavenTestFailures
+        git checkout $GIT_BRANCH_ID
+    fi
+    if proceedyn "Update working directory for $VER_NEXT? (Y/n)" y; then
+        echo "Update VERSION.txt for $VER_NEXT"
+        cp VERSION.txt VERSION.txt.backup
+        echo "jetty-$VER_NEXT" > VERSION.txt
+        echo "" >> VERSION.txt
+        cat VERSION.txt.backup >> VERSION.txt
+        echo "Update project.versions for $VER_NEXT"
+        mvn org.codehaus.mojo:versions-maven-plugin:2.1:set \
+            -DoldVersion="$VER_RELEASE" \
+            -DnewVersion="$VER_NEXT"
+        echo "Commit $VER_NEXT"
+        if proceedyn "Commit updates in working directory for $VER_NEXT? (Y/n)" y; then
+            git commit -a -m "Updating to version $VER_NEXT"
+        fi
+    fi
+    if proceedyn "Push git commits to remote $GIT_REMOTE_ID? (Y/n)" y; then
+        git push $GIT_REMOTE_ID $GIT_BRANCH_ID
+        git push $GIT_REMOTE_ID --tags
+    fi
+else
+    echo "Not performing release"
+fi
+
+
diff --git a/tests/pom.xml b/tests/pom.xml
index b7330cc..9c0510a 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -1,27 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <groupId>org.eclipse.jetty.tests</groupId>
@@ -32,6 +15,13 @@
   <build>
     <plugins>
       <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <configuration>
+          <additionalparam>-Xdoclint:none</additionalparam>
+        </configuration>
+      </plugin>
+      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
         <configuration>
@@ -49,5 +39,6 @@
     <module>test-integration</module>
     <module>test-quickstart</module>
     <module>test-jmx</module>
+    <module>test-http-client-transport</module>
   </modules>
 </project>
diff --git a/tests/test-cdi/cdi-client/pom.xml b/tests/test-cdi/cdi-client/pom.xml
deleted file mode 100644
index 3419c3b..0000000
--- a/tests/test-cdi/cdi-client/pom.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <parent>
-    <groupId>org.eclipse.jetty.tests</groupId>
-    <artifactId>test-cdi-parent</artifactId>
-    <version>9.2.4-SNAPSHOT</version>
-  </parent>
-  <modelVersion>4.0.0</modelVersion>
-  <artifactId>cdi-client</artifactId>
-  <packaging>jar</packaging>
-  <name>Jetty Tests :: CDI :: Clients</name>
-  <url>http://www.eclipse.org/jetty</url>
-  <properties>
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <bundle-symbolic-name>${project.groupId}.cdi.clients</bundle-symbolic-name>
-  </properties>
-  <dependencies>
-  </dependencies>
-</project>
diff --git a/tests/test-cdi/cdi-webapp-it/pom.xml b/tests/test-cdi/cdi-webapp-it/pom.xml
deleted file mode 100644
index ac3baf2..0000000
--- a/tests/test-cdi/cdi-webapp-it/pom.xml
+++ /dev/null
@@ -1,215 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <parent>
-    <groupId>org.eclipse.jetty.tests</groupId>
-    <artifactId>test-cdi-parent</artifactId>
-    <version>9.2.4-SNAPSHOT</version>
-  </parent>
-  <modelVersion>4.0.0</modelVersion>
-  <artifactId>cdi-webapp-it</artifactId>
-  <packaging>jar</packaging>
-  <name>Jetty Tests :: CDI :: WebApp Integration Tests</name>
-  <url>http://www.eclipse.org/jetty</url>
-  <properties>
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-    <bundle-symbolic-name>${project.groupId}.cdi.webapp.it</bundle-symbolic-name>
-    <scripts-dir>${project.basedir}/src/test/scripts</scripts-dir>
-    <test-base-dir>${project.build.directory}/test-base</test-base-dir>
-    <test-home-dir>${project.build.directory}/test-home</test-home-dir>
-  </properties>
-  <dependencies>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-distribution</artifactId>
-      <version>${project.version}</version>
-      <type>zip</type>
-      <scope>runtime</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.tests</groupId>
-      <artifactId>cdi-webapp</artifactId>
-      <version>${project.version}</version>
-      <type>war</type>
-      <scope>runtime</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty.toolchain</groupId>
-      <artifactId>jetty-test-helper</artifactId>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-dependency-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>copy-apps-for-testing</id>
-            <phase>process-test-resources</phase>
-            <goals>
-              <goal>copy-dependencies</goal>
-            </goals>
-            <configuration>
-              <includeArtifactIds>cdi-webapp</includeArtifactIds>
-              <includeScope>runtime</includeScope>
-              <includeTypes>war</includeTypes>
-              <overwriteSnapshots>true</overwriteSnapshots>
-              <overwriteReleases>true</overwriteReleases>
-              <stripVersion>true</stripVersion>
-              <outputDirectory>${test-base-dir}/webapps</outputDirectory>
-            </configuration>
-          </execution>
-          <execution>
-            <id>unpack-jetty-distro</id>
-            <phase>process-test-resources</phase>
-            <goals>
-              <goal>unpack-dependencies</goal>
-            </goals>
-            <configuration>
-              <includeArtifactIds>jetty-distribution</includeArtifactIds>
-              <includeScope>runtime</includeScope>
-              <includeTypes>zip</includeTypes>
-              <outputAbsoluteArtifactFilename>true</outputAbsoluteArtifactFilename>
-              <outputDirectory>${test-home-dir}</outputDirectory>
-              <overWriteSnapshots>true</overWriteSnapshots>
-              <overWriteIfNewer>true</overWriteIfNewer>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-failsafe-plugin</artifactId>
-        <version>2.17</version>
-        <executions>
-          <execution>
-            <goals>
-              <goal>integration-test</goal>
-              <goal>verify</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-antrun-plugin</artifactId>
-        <version>1.7</version>
-        <executions>
-          <execution>
-            <id>start-jetty</id>
-            <phase>pre-integration-test</phase>
-            <goals>
-              <goal>run</goal>
-            </goals>
-            <configuration>
-              <target>
-                <property name="jetty.home" location="${test-home-dir}/jetty-distribution-${project.version}"/>
-                <property name="jetty.base" location="${test-base-dir}"/>
-                <echo>Integration Test : Setup Jetty</echo>
-                <exec executable="${run.command}"
-                      dir="${scripts-dir}"
-                      spawn="false">
-                  <arg value="${run.command.xtra}"/>
-                  <arg value="${setup.script}"/>
-                  <arg file="${java.home}"/>
-                  <arg file="${jetty.home}"/>
-                  <arg file="${jetty.base}"/>
-                </exec>
-
-                <echo>Integration Test : Starting Jetty ...</echo>
-                <exec executable="${run.command}"
-                      dir="${scripts-dir}"
-                      spawn="true">
-                  <arg value="${run.command.xtra}"/>
-                  <arg value="${start.script}"/>
-                  <arg file="${java.home}"/>
-                  <arg file="${jetty.home}"/>
-                  <arg file="${jetty.base}"/>
-                </exec>
-                <waitfor maxwait="5" maxwaitunit="second"
-                  checkevery="100" checkeveryunit="millisecond">
-                  <http url="http://localhost:58080/cdi-webapp/"/>
-                </waitfor>
-                <echo>Integration Test : Jetty is now available</echo>
-              </target>
-            </configuration>
-          </execution>
-          <execution>
-            <id>stop-jetty</id>
-            <phase>post-integration-test</phase>
-            <goals>
-              <goal>run</goal>
-            </goals>
-            <configuration>
-              <target>
-                <property name="jetty.home" location="${test-home-dir}/jetty-distribution-${project.version}"/>
-                <property name="jetty.base" location="${test-base-dir}"/>
-                <echo>Integration Test : Stop Jetty</echo>
-                <exec executable="${run.command}"
-                      dir="${scripts-dir}"
-                      spawn="false">
-                  <arg value="${run.command.xtra}"/>
-                  <arg value="${stop.script}"/>
-                  <arg file="${java.home}"/>
-                  <arg file="${jetty.home}"/>
-                  <arg file="${jetty.base}"/>
-                </exec>
-              </target>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-  <profiles>
-    <profile>
-      <id>it-windows</id>
-      <activation>
-        <os>
-          <family>Windows</family>
-        </os>
-      </activation>
-      <properties>
-        <run.command>cmd</run.command>
-        <run.command.xtra>/c</run.command.xtra>
-        <start.script>start-jetty.bat</start.script>
-        <stop.script>stop-jetty.bat</stop.script>
-      </properties>
-    </profile>
-    <profile>
-      <id>it-unix</id>
-      <activation>
-        <os>
-          <family>unix</family>
-        </os>
-      </activation>
-      <properties>
-        <run.command>sh</run.command>
-        <run.command.xtra>--</run.command.xtra>
-        <setup.script>setup-jetty.sh</setup.script>
-        <start.script>start-jetty.sh</start.script>
-        <stop.script>stop-jetty.sh</stop.script>
-      </properties>
-    </profile>
-  </profiles>
-</project>
diff --git a/tests/test-cdi/cdi-webapp-it/src/test/java/org/eclipse/jetty/tests/HelloIT.java b/tests/test-cdi/cdi-webapp-it/src/test/java/org/eclipse/jetty/tests/HelloIT.java
deleted file mode 100644
index c1aaa4f..0000000
--- a/tests/test-cdi/cdi-webapp-it/src/test/java/org/eclipse/jetty/tests/HelloIT.java
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.tests;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.net.URI;
-
-import org.eclipse.jetty.toolchain.test.SimpleRequest;
-import org.junit.Test;
-
-/**
- * Basic tests for a simple @WebServlet with no CDI
- */
-public class HelloIT
-{
-    @Test
-    public void testBasic() throws Exception
-    {
-        URI serverURI = new URI("http://localhost:58080/cdi-webapp/");
-        SimpleRequest req = new SimpleRequest(serverURI);
-        assertThat(req.getString("hello"),is("Hello World"));
-    }
-}
diff --git a/tests/test-cdi/cdi-webapp-it/src/test/java/org/eclipse/jetty/tests/ServerInfoIT.java b/tests/test-cdi/cdi-webapp-it/src/test/java/org/eclipse/jetty/tests/ServerInfoIT.java
deleted file mode 100644
index 3b1c6c3..0000000
--- a/tests/test-cdi/cdi-webapp-it/src/test/java/org/eclipse/jetty/tests/ServerInfoIT.java
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.tests;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.net.URI;
-
-import org.eclipse.jetty.toolchain.test.SimpleRequest;
-import org.junit.Test;
-
-public class ServerInfoIT
-{
-    @Test
-    public void testGET() throws Exception {
-        URI serverURI = new URI("http://localhost:58080/cdi-webapp/");
-        SimpleRequest req = new SimpleRequest(serverURI);
-        assertThat(req.getString("serverinfo"),is("Hello World"));
-    }
-}
diff --git a/tests/test-cdi/cdi-webapp-it/src/test/scripts/setup-jetty.sh b/tests/test-cdi/cdi-webapp-it/src/test/scripts/setup-jetty.sh
deleted file mode 100755
index c1c2ad6..0000000
--- a/tests/test-cdi/cdi-webapp-it/src/test/scripts/setup-jetty.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-
-JAVA_HOME=$1
-JETTY_HOME=$2
-JETTY_BASE=$3
-
-echo \${java.home}  : $JAVA_HOME
-echo \${jetty.home} : $JETTY_HOME
-echo \${jetty.base} : $JETTY_BASE
-
-cd "$JETTY_BASE"
-
-"$JAVA_HOME/bin/java" -jar "$JETTY_HOME/start.jar" \
-    --approve-all-licenses \
-    --add-to-start=deploy,http,annotations,cdi,logging
-
-"$JAVA_HOME/bin/java" -jar "$JETTY_HOME/start.jar" \
-    --version
-
diff --git a/tests/test-cdi/cdi-webapp-it/src/test/scripts/start-jetty.sh b/tests/test-cdi/cdi-webapp-it/src/test/scripts/start-jetty.sh
deleted file mode 100755
index 1d0f9eb..0000000
--- a/tests/test-cdi/cdi-webapp-it/src/test/scripts/start-jetty.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-
-JAVA_HOME=$1
-JETTY_HOME=$2
-JETTY_BASE=$3
-
-echo \${java.home}  : $JAVA_HOME
-echo \${jetty.home} : $JETTY_HOME
-echo \${jetty.base} : $JETTY_BASE
-
-cd "$JETTY_BASE"
-
-"$JAVA_HOME/bin/java" -jar "$JETTY_HOME/start.jar" \
-    jetty.port=58080 \
-    STOP.PORT=58181 STOP.KEY=it
-
-
diff --git a/tests/test-cdi/cdi-webapp/pom.xml b/tests/test-cdi/cdi-webapp/pom.xml
deleted file mode 100644
index 117fe10..0000000
--- a/tests/test-cdi/cdi-webapp/pom.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  // ========================================================================
-  // Copyright (c) Webtide LLC
-  //
-  // All rights reserved. This program and the accompanying materials
-  // are made available under the terms of the Eclipse Public License v1.0
-  // and Apache License v2.0 which accompanies this distribution.
-  //
-  // The Eclipse Public License is available at
-  // http://www.eclipse.org/legal/epl-v10.html
-  //
-  // The Apache License v2.0 is available at
-  // http://www.apache.org/licenses/LICENSE-2.0.txt
-  //
-  // You may elect to redistribute this code under either of these licenses.
-  // ========================================================================
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <parent>
-    <groupId>org.eclipse.jetty.tests</groupId>
-    <artifactId>test-cdi-parent</artifactId>
-    <version>9.2.4-SNAPSHOT</version>
-  </parent>
-  <modelVersion>4.0.0</modelVersion>
-  <artifactId>cdi-webapp</artifactId>
-  <packaging>war</packaging>
-  <name>Jetty Tests :: CDI :: WebApp</name>
-  <url>http://www.eclipse.org/jetty</url>
-  <properties>
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <bundle-symbolic-name>${project.groupId}.cdi.webapp</bundle-symbolic-name>
-  </properties>
-  <dependencies>
-    <dependency>
-      <groupId>javax.servlet</groupId>
-      <artifactId>javax.servlet-api</artifactId>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>javax.enterprise</groupId>
-      <artifactId>cdi-api</artifactId>
-      <version>1.1</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>javax.websocket</groupId>
-      <artifactId>javax.websocket-api</artifactId>
-      <scope>provided</scope>
-    </dependency>
-  </dependencies>
-  <build>
-    <finalName>cdi-webapp</finalName>
-  </build>
-</project>
diff --git a/tests/test-cdi/pom.xml b/tests/test-cdi/pom.xml
deleted file mode 100644
index 307d8e1..0000000
--- a/tests/test-cdi/pom.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <parent>
-    <groupId>org.eclipse.jetty.tests</groupId>
-    <artifactId>tests-parent</artifactId>
-    <version>9.2.4-SNAPSHOT</version>
-  </parent>
-  <modelVersion>4.0.0</modelVersion>
-  <artifactId>test-cdi-parent</artifactId>
-  <packaging>pom</packaging>
-  <name>Jetty Tests :: CDI Parent</name>
-  <url>http://www.eclipse.org/jetty</url>
-  <modules>
-    <module>cdi-webapp</module>
-    <module>cdi-webapp-it</module>
-    <module>cdi-client</module>
-  </modules>
-</project>
diff --git a/tests/test-continuation/pom.xml b/tests/test-continuation/pom.xml
index 8f7ad5b..47bc6e7 100644
--- a/tests/test-continuation/pom.xml
+++ b/tests/test-continuation/pom.xml
@@ -1,26 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
@@ -28,6 +11,9 @@
   <packaging>jar</packaging>
   <name>Test :: Continuation</name>
   <description>Asynchronous API</description>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.continuation</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
diff --git a/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationBase.java b/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationBase.java
deleted file mode 100644
index 378b344..0000000
--- a/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationBase.java
+++ /dev/null
@@ -1,509 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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 static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.startsWith;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.Socket;
-import java.nio.charset.StandardCharsets;
-import java.util.Timer;
-import java.util.TimerTask;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-
-public abstract class ContinuationBase
-{
-    protected SuspendServlet _servlet=new SuspendServlet();
-    protected int _port;
-
-    protected void doNormal(String type) throws Exception
-    {
-        String response=process(null,null);
-        assertContains(type,response);
-        assertContains("NORMAL",response);
-        assertNotContains("history: onTimeout",response);
-        assertNotContains("history: onComplete",response);
-    }
-
-    protected void doSleep() throws Exception
-    {
-        String response=process("sleep=200",null);
-        assertContains("SLEPT",response);
-        assertNotContains("history: onTimeout",response);
-        assertNotContains("history: onComplete",response);
-    }
-
-    protected void doSuspend() throws Exception
-    {
-        String response=process("suspend=200",null);
-        assertContains("TIMEOUT",response);
-        assertContains("history: onTimeout",response);
-        assertContains("history: onComplete",response);
-    }
-
-    protected void doSuspendWaitResume() throws Exception
-    {
-        String response=process("suspend=200&resume=10",null);
-        assertContains("RESUMED",response);
-        assertNotContains("history: onTimeout",response);
-        assertContains("history: onComplete",response);
-    }
-
-    protected void doSuspendResume() throws Exception
-    {
-        String response=process("suspend=200&resume=0",null);
-        assertContains("RESUMED",response);
-        assertNotContains("history: onTimeout",response);
-        assertContains("history: onComplete",response);
-    }
-
-    protected void doSuspendWaitComplete() throws Exception
-    {
-        String response=process("suspend=200&complete=50",null);
-        assertContains("COMPLETED",response);
-        assertContains("history: initial",response);
-        assertNotContains("history: onTimeout",response);
-        assertContains("history: onComplete",response);
-        assertNotContains("history: !initial",response);
-    }
-
-    protected void doSuspendComplete() throws Exception
-    {
-        String response=process("suspend=200&complete=0",null);
-        assertContains("COMPLETED",response);
-        assertContains("history: initial",response);
-        assertNotContains("history: onTimeout",response);
-        assertContains("history: onComplete",response);
-        assertNotContains("history: !initial",response);
-    }
-
-    protected void doSuspendWaitResumeSuspendWaitResume() throws Exception
-    {
-        String response=process("suspend=1000&resume=10&suspend2=1000&resume2=10",null);
-        assertEquals(2,count(response,"history: suspend"));
-        assertEquals(2,count(response,"history: resume"));
-        assertEquals(0,count(response,"history: onTimeout"));
-        assertEquals(1,count(response,"history: onComplete"));
-        assertContains("RESUMED",response);
-    }
-
-    protected void doSuspendWaitResumeSuspendComplete() throws Exception
-    {
-        String response=process("suspend=1000&resume=10&suspend2=1000&complete2=10",null);
-        assertEquals(2,count(response,"history: suspend"));
-        assertEquals(1,count(response,"history: resume"));
-        assertEquals(0,count(response,"history: onTimeout"));
-        assertEquals(1,count(response,"history: onComplete"));
-        assertContains("COMPLETED",response);
-    }
-
-    protected void doSuspendWaitResumeSuspend() throws Exception
-    {
-        String response=process("suspend=1000&resume=10&suspend2=10",null);
-        assertEquals(2,count(response,"history: suspend"));
-        assertEquals(1,count(response,"history: resume"));
-        assertEquals(1,count(response,"history: onTimeout"));
-        assertEquals(1,count(response,"history: onComplete"));
-        assertContains("TIMEOUT",response);
-    }
-
-    protected void doSuspendTimeoutSuspendResume() throws Exception
-    {
-        String response=process("suspend=10&suspend2=1000&resume2=10",null);
-        assertEquals(2,count(response,"history: suspend"));
-        assertEquals(1,count(response,"history: resume"));
-        assertEquals(1,count(response,"history: onTimeout"));
-        assertEquals(1,count(response,"history: onComplete"));
-        assertContains("RESUMED",response);
-    }
-
-    protected void doSuspendTimeoutSuspendComplete() throws Exception
-    {
-        String response=process("suspend=10&suspend2=1000&complete2=10",null);
-        assertEquals(2,count(response,"history: suspend"));
-        assertEquals(0,count(response,"history: resume"));
-        assertEquals(1,count(response,"history: onTimeout"));
-        assertEquals(1,count(response,"history: onComplete"));
-        assertContains("COMPLETED",response);
-    }
-
-    protected void doSuspendTimeoutSuspend() throws Exception
-    {
-        String response=process("suspend=10&suspend2=10",null);
-        assertEquals(2,count(response,"history: suspend"));
-        assertEquals(0,count(response,"history: resume"));
-        assertEquals(2,count(response,"history: onTimeout"));
-        assertEquals(1,count(response,"history: onComplete"));
-        assertContains("TIMEOUT",response);
-    }
-
-    protected void doSuspendThrowResume() throws Exception
-    {
-        String response=process("suspend=200&resume=10&undispatch=true",null);
-        assertContains("RESUMED",response);
-        assertNotContains("history: onTimeout",response);
-        assertContains("history: onComplete",response);
-    }
-
-    protected void doSuspendResumeThrow() throws Exception
-    {
-        String response=process("suspend=200&resume=0&undispatch=true",null);
-        assertContains("RESUMED",response);
-        assertNotContains("history: onTimeout",response);
-        assertContains("history: onComplete",response);
-    }
-
-    protected void doSuspendThrowComplete() throws Exception
-    {
-        String response=process("suspend=200&complete=10&undispatch=true",null);
-        assertContains("COMPLETED",response);
-        assertNotContains("history: onTimeout",response);
-        assertContains("history: onComplete",response);
-    }
-
-    protected void doSuspendCompleteThrow() throws Exception
-    {
-        String response=process("suspend=200&complete=0&undispatch=true",null);
-        assertContains("COMPLETED",response);
-        assertNotContains("history: onTimeout",response);
-        assertContains("history: onComplete",response);
-    }
-
-
-    private int count(String responses,String substring)
-    {
-        int count=0;
-        int i=responses.indexOf(substring);
-        while (i>=0)
-        {
-            count++;
-            i=responses.indexOf(substring,i+substring.length());
-        }
-
-        return count;
-    }
-
-    protected void assertContains(String content,String response)
-    {
-        assertThat(response,startsWith("HTTP/1.1 200 OK"));
-        assertThat(response,containsString(content));
-    }
-
-    protected void assertNotContains(String content,String response)
-    {
-        assertThat(response,startsWith("HTTP/1.1 200 OK"));
-        assertThat(response,not(containsString(content)));
-    }
-
-    public synchronized String process(String query,String content) throws Exception
-    {
-        String request = "GET /";
-
-        if (query!=null)
-            request+="?"+query;
-        request+=" HTTP/1.1\r\n"+
-        "Host: localhost\r\n"+
-        "Connection: close\r\n";
-        if (content==null)
-            request+="\r\n";
-        else
-        {
-            request+="Content-Length: "+content.length()+"\r\n";
-            request+="\r\n" + content;
-        }
-
-        int port=_port;
-        String response=null;
-        try (Socket socket = new Socket("localhost",port);)
-        {
-            socket.setSoTimeout(10000);
-            socket.getOutputStream().write(request.getBytes(StandardCharsets.UTF_8));
-            socket.getOutputStream().flush();
-
-            response = toString(socket.getInputStream());
-        }
-        catch(Exception e)
-        {
-            System.err.println("failed on port "+port);
-            e.printStackTrace();
-            throw e;
-        }
-        return response;
-    }
-
-
-    protected abstract String toString(InputStream in) throws IOException;
-
-
-    private static class SuspendServlet extends HttpServlet
-    {
-        private Timer _timer=new Timer();
-
-        public SuspendServlet()
-        {}
-
-        /* ------------------------------------------------------------ */
-        @Override
-        protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
-        {
-            final Continuation continuation = ContinuationSupport.getContinuation(request);
-
-            response.addHeader("history",continuation.getClass().toString());
-
-            int read_before=0;
-            long sleep_for=-1;
-            long suspend_for=-1;
-            long suspend2_for=-1;
-            long resume_after=-1;
-            long resume2_after=-1;
-            long complete_after=-1;
-            long complete2_after=-1;
-            boolean undispatch=false;
-
-            if (request.getParameter("read")!=null)
-                read_before=Integer.parseInt(request.getParameter("read"));
-            if (request.getParameter("sleep")!=null)
-                sleep_for=Integer.parseInt(request.getParameter("sleep"));
-            if (request.getParameter("suspend")!=null)
-                suspend_for=Integer.parseInt(request.getParameter("suspend"));
-            if (request.getParameter("suspend2")!=null)
-                suspend2_for=Integer.parseInt(request.getParameter("suspend2"));
-            if (request.getParameter("resume")!=null)
-                resume_after=Integer.parseInt(request.getParameter("resume"));
-            if (request.getParameter("resume2")!=null)
-                resume2_after=Integer.parseInt(request.getParameter("resume2"));
-            if (request.getParameter("complete")!=null)
-                complete_after=Integer.parseInt(request.getParameter("complete"));
-            if (request.getParameter("complete2")!=null)
-                complete2_after=Integer.parseInt(request.getParameter("complete2"));
-            if (request.getParameter("undispatch")!=null)
-                undispatch=Boolean.parseBoolean(request.getParameter("undispatch"));
-
-            if (continuation.isInitial())
-            {
-                response.addHeader("history","initial");
-                if (read_before>0)
-                {
-                    byte[] buf=new byte[read_before];
-                    request.getInputStream().read(buf);
-                }
-                else if (read_before<0)
-                {
-                    InputStream in = request.getInputStream();
-                    int b=in.read();
-                    while(b!=-1)
-                        b=in.read();
-                }
-
-                if (suspend_for>=0)
-                {
-                    if (suspend_for>0)
-                        continuation.setTimeout(suspend_for);
-                    continuation.addContinuationListener(__listener);
-                    response.addHeader("history","suspend");
-                    continuation.suspend(response);
-
-                    if (complete_after>0)
-                    {
-                        TimerTask complete = new TimerTask()
-                        {
-                            @Override
-                            public void run()
-                            {
-                                try
-                                {
-                                    response.setStatus(200);
-                                    response.getOutputStream().println("COMPLETED\n");
-                                    continuation.complete();
-                                }
-                                catch(Exception e)
-                                {
-                                    e.printStackTrace();
-                                }
-                            }
-                        };
-                        synchronized (_timer)
-                        {
-                            _timer.schedule(complete,complete_after);
-                        }
-                    }
-                    else if (complete_after==0)
-                    {
-                        response.setStatus(200);
-                        response.getOutputStream().println("COMPLETED\n");
-                        continuation.complete();
-                    }
-                    else if (resume_after>0)
-                    {
-                        TimerTask resume = new TimerTask()
-                        {
-                            @Override
-                            public void run()
-                            {
-                                ((HttpServletResponse)continuation.getServletResponse()).addHeader("history","resume");
-                                continuation.resume();
-                            }
-                        };
-                        synchronized (_timer)
-                        {
-                            _timer.schedule(resume,resume_after);
-                        }
-                    }
-                    else if (resume_after==0)
-                    {
-                        ((HttpServletResponse)continuation.getServletResponse()).addHeader("history","resume");
-                        continuation.resume();
-                    }
-
-                    if (undispatch)
-                        continuation.undispatch();
-                }
-                else if (sleep_for>=0)
-                {
-                    try
-                    {
-                        Thread.sleep(sleep_for);
-                    }
-                    catch (InterruptedException e)
-                    {
-                        e.printStackTrace();
-                    }
-                    response.setStatus(200);
-                    response.getOutputStream().println("SLEPT\n");
-                }
-                else
-                {
-                    response.setStatus(200);
-                    response.getOutputStream().println("NORMAL\n");
-                }
-            }
-            else
-            {
-                response.addHeader("history","!initial");
-                if (suspend2_for>=0 && request.getAttribute("2nd")==null)
-                {
-                    request.setAttribute("2nd","cycle");
-
-                    if (suspend2_for>0)
-                        continuation.setTimeout(suspend2_for);
-                    // continuation.addContinuationListener(__listener);
-                    response.addHeader("history","suspend");
-                    continuation.suspend(response);
-
-                    if (complete2_after>0)
-                    {
-                        TimerTask complete = new TimerTask()
-                        {
-                            @Override
-                            public void run()
-                            {
-                                try
-                                {
-                                    response.setStatus(200);
-                                    response.getOutputStream().println("COMPLETED\n");
-                                    continuation.complete();
-                                }
-                                catch(Exception e)
-                                {
-                                    e.printStackTrace();
-                                }
-                            }
-                        };
-                        synchronized (_timer)
-                        {
-                            _timer.schedule(complete,complete2_after);
-                        }
-                    }
-                    else if (complete2_after==0)
-                    {
-                        response.setStatus(200);
-                        response.getOutputStream().println("COMPLETED\n");
-                        continuation.complete();
-                    }
-                    else if (resume2_after>0)
-                    {
-                        TimerTask resume = new TimerTask()
-                        {
-                            @Override
-                            public void run()
-                            {
-                                response.addHeader("history","resume");
-                                continuation.resume();
-                            }
-                        };
-                        synchronized (_timer)
-                        {
-                            _timer.schedule(resume,resume2_after);
-                        }
-                    }
-                    else if (resume2_after==0)
-                    {
-                        response.addHeader("history","resume");
-                        continuation.resume();
-                    }
-                    if (undispatch)
-                        continuation.undispatch();
-                    return;
-                }
-                else if (continuation.isExpired())
-                {
-                    response.setStatus(200);
-                    response.getOutputStream().println("TIMEOUT\n");
-                }
-                else if (continuation.isResumed())
-                {
-                    response.setStatus(200);
-                    response.getOutputStream().println("RESUMED\n");
-                }
-                else
-                {
-                    response.setStatus(200);
-                    response.getOutputStream().println("unknown???\n");
-                }
-            }
-        }
-    }
-
-
-    private static ContinuationListener __listener = new ContinuationListener()
-    {
-        @Override
-        public void onComplete(Continuation continuation)
-        {
-            ((HttpServletResponse)continuation.getServletResponse()).addHeader("history","onComplete");
-        }
-
-        @Override
-        public void onTimeout(Continuation continuation)
-        {
-            ((HttpServletResponse)continuation.getServletResponse()).addHeader("history","onTimeout");
-        }
-
-    };
-}
diff --git a/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationTest.java b/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationTest.java
deleted file mode 100644
index 7bb0bfa..0000000
--- a/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationTest.java
+++ /dev/null
@@ -1,203 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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 java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.RequestLog;
-import org.eclipse.jetty.server.Response;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.handler.RequestLogHandler;
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.component.AbstractLifeCycle;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-
-
-public class ContinuationTest extends ContinuationBase
-{
-    protected Server _server = new Server();
-    protected ServletHandler _servletHandler;
-    protected ServerConnector _connector;
-    FilterHolder _filter;
-    protected List<String> _log = new ArrayList<String>();
-
-    @Before
-    public void setUp() throws Exception
-    {
-        _connector = new ServerConnector(_server);
-        _server.setConnectors(new Connector[]{ _connector });
-
-        _log.clear();
-        RequestLogHandler requestLogHandler = new RequestLogHandler();
-        requestLogHandler.setRequestLog(new Log());
-        _server.setHandler(requestLogHandler);
-        
-        ServletContextHandler servletContext = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS);
-        requestLogHandler.setHandler(servletContext);
-        
-        _servletHandler=servletContext.getServletHandler();
-        ServletHolder holder=new ServletHolder(_servlet);
-        holder.setAsyncSupported(true);
-        _servletHandler.addServletWithMapping(holder,"/");
-        
-        _server.start();
-        _port=_connector.getLocalPort();
-    }
-
-    @After
-    public void tearDown() throws Exception
-    {
-        Assert.assertEquals(1,_log.size());
-        Assert.assertTrue(_log.get(0).startsWith("200 "));
-        Assert.assertTrue(_log.get(0).endsWith(" /"));
-        _server.stop();
-    }
-    
-    @Test
-    public void testContinuation() throws Exception
-    {
-        doNormal("Servlet3Continuation");
-    }
-
-    @Test
-    public void testSleep() throws Exception
-    {
-        doSleep();
-    }
-
-    @Test
-    public void testSuspend() throws Exception
-    {
-        doSuspend();
-    }
-
-    @Test
-    public void testSuspendWaitResume() throws Exception
-    {
-        doSuspendWaitResume();
-    }
-
-    @Test
-    public void testSuspendResume() throws Exception
-    {
-        doSuspendResume();
-    }
-
-    @Test
-    public void testSuspendWaitComplete() throws Exception
-    {
-        doSuspendWaitComplete();
-    }
-
-    @Test
-    public void testSuspendComplete() throws Exception
-    {
-        doSuspendComplete();
-    }
-
-    @Test
-    public void testSuspendWaitResumeSuspendWaitResume() throws Exception
-    {
-        doSuspendWaitResumeSuspendWaitResume();
-    }
-
-    @Test
-    public void testSuspendWaitResumeSuspendComplete() throws Exception
-    {
-        doSuspendWaitResumeSuspendComplete();
-    }
-
-    @Test
-    public void testSuspendWaitResumeSuspend() throws Exception
-    {
-        doSuspendWaitResumeSuspend();
-    }
-
-    @Test
-    public void testSuspendTimeoutSuspendResume() throws Exception
-    {
-        doSuspendTimeoutSuspendResume();
-    }
-
-    @Test
-    public void testSuspendTimeoutSuspendComplete() throws Exception
-    {
-        doSuspendTimeoutSuspendComplete();
-    }
-
-    @Test
-    public void testSuspendTimeoutSuspend() throws Exception
-    {
-        doSuspendTimeoutSuspend();
-    }
-
-    @Test
-    public void testSuspendThrowResume() throws Exception
-    {
-        doSuspendThrowResume();
-    }
-
-    @Test
-    public void testSuspendResumeThrow() throws Exception
-    {
-        doSuspendResumeThrow();
-    }
-
-    @Test
-    public void testSuspendThrowComplete() throws Exception
-    {
-        doSuspendThrowComplete();
-    }
-
-    @Test
-    public void testSuspendCompleteThrow() throws Exception
-    {
-        doSuspendCompleteThrow();
-    }
-    
-    @Override
-    protected String toString(InputStream in) throws IOException
-    {
-        return IO.toString(in);
-    }
-    
-    class Log extends AbstractLifeCycle implements RequestLog
-    {
-        @Override
-        public void log(Request request, Response response)
-        {
-            _log.add(response.getStatus()+" "+response.getContentCount()+" "+request.getRequestURI());
-        }
-        
-    }
-}
diff --git a/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationsTest.java b/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationsTest.java
new file mode 100644
index 0000000..33b1936
--- /dev/null
+++ b/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationsTest.java
@@ -0,0 +1,660 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+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.RequestLog;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.RequestLogHandler;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ContinuationsTest
+{
+    @SuppressWarnings("serial")
+    public static class SneakyList extends ArrayList<String> {
+        @Override
+        public boolean add(String e)
+        {
+            //System.err.printf("add(%s)%n",e);
+            return super.add(e);
+        }
+    };
+    
+    @Parameters(name="{0}")
+    public static List<Object[]> data()
+    {
+        List<Object[]> setup = new ArrayList<>();
+        
+        // Servlet3 / AsyncContext Setup
+        {
+            String description = "Servlet 3 Setup";
+            Class<? extends Continuation> expectedImplClass = Servlet3Continuation.class;
+            List<String> log = new ArrayList<>();
+            RequestLogHandler servlet3Setup = new RequestLogHandler();
+            servlet3Setup.setRequestLog(new Log(log));
+        
+            ServletContextHandler servletContext = new ServletContextHandler();
+            servlet3Setup.setHandler(servletContext);
+        
+            ServletHandler servletHandler=servletContext.getServletHandler();
+            List<String> history = new SneakyList();
+            Listener listener = new Listener(history);
+            ServletHolder holder=new ServletHolder(new SuspendServlet(history, listener));
+            holder.setAsyncSupported(true);
+            servletHandler.addServletWithMapping(holder, "/");
+            setup.add(new Object[]{description,servlet3Setup,history,listener,expectedImplClass,log});
+        }
+        
+        // Faux Continuations Setup
+        {
+            String description = "Faux Setup";
+            Class<? extends Continuation> expectedImplClass = FauxContinuation.class;
+            
+            // no log for this setup
+            List<String> log = null;
+            
+            ServletContextHandler fauxSetup = new ServletContextHandler();
+            ServletHandler servletHandler=fauxSetup.getServletHandler();
+            List<String> history = new SneakyList();
+            Listener listener = new Listener(history);
+            ServletHolder holder=new ServletHolder(new SuspendServlet(history, listener));
+            servletHandler.addServletWithMapping(holder,"/");
+    
+            FilterHolder filter= servletHandler.addFilterWithMapping(ContinuationFilter.class,"/*",null);
+            filter.setInitParameter("debug","true");
+            filter.setInitParameter("faux","true");
+            setup.add(new Object[]{description,fauxSetup,history,listener,expectedImplClass,log});
+        }
+        
+        return setup;
+    }
+    
+    @Parameter(0)
+    public String setupDescription;
+    
+    @Parameter(1)
+    public Handler setupHandler;
+    
+    @Parameter(2)
+    public List<String> history;
+    
+    @Parameter(3)
+    public Listener listener;
+    
+    @Parameter(4)
+    public Class<? extends Continuation> expectedImplClass;
+    
+    @Parameter(5)
+    public List<String> log;
+
+    @Test
+    public void testNormal() throws Exception
+    {
+        String response = process(null, null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("NORMAL"));
+        assertThat(history, hasItem(expectedImplClass.getName()));
+        assertThat(history, not(hasItem("onTimeout")));
+        assertThat(history, not(hasItem("onComplete")));
+    }
+
+    @Test
+    public void testSleep() throws Exception
+    {
+        String response = process("sleep=200", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("SLEPT"));
+        assertThat(history, not(hasItem("onTimeout")));
+        assertThat(history, not(hasItem("onComplete")));
+    }
+
+    @Test
+    public void testSuspend() throws Exception
+    {
+        String response = process("suspend=200", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("TIMEOUT"));
+        assertThat(history, hasItem("onTimeout"));
+        assertThat(history, hasItem("onComplete"));
+    }
+
+    @Test
+    public void testSuspendWaitResume() throws Exception
+    {
+        String response = process("suspend=200&resume=10", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("RESUMED"));
+        assertThat(history, not(hasItem("onTimeout")));
+        assertThat(history, hasItem("onComplete"));
+    }
+
+    @Test
+    public void testSuspendResume() throws Exception
+    {
+        String response = process("suspend=200&resume=0", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("RESUMED"));
+        assertThat(history, not(hasItem("onTimeout")));
+        assertThat(history, hasItem("onComplete"));
+    }
+
+    @Test
+    public void testSuspendWaitComplete() throws Exception
+    {
+        String response = process("suspend=200&complete=50", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("COMPLETED"));
+        assertThat(history, hasItem("initial"));
+        assertThat(history, not(hasItem("!initial")));
+        assertThat(history, not(hasItem("onTimeout")));
+        assertThat(history, hasItem("onComplete"));
+    }
+
+    @Test
+    public void testSuspendComplete() throws Exception
+    {
+        String response = process("suspend=200&complete=0", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("COMPLETED"));
+        assertThat(history, hasItem("initial"));
+        assertThat(history, not(hasItem("!initial")));
+        assertThat(history, not(hasItem("onTimeout")));
+        assertThat(history, hasItem("onComplete"));
+    }
+
+    @Test
+    public void testSuspendWaitResumeSuspendWaitResume() throws Exception
+    {
+        String response = process("suspend=1000&resume=10&suspend2=1000&resume2=10", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("RESUMED"));
+        assertEquals(2, count(history, "suspend"));
+        assertEquals(2, count(history, "resume"));
+        assertEquals(0, count(history, "onTimeout"));
+        assertEquals(1, count(history, "onComplete"));
+    }
+
+    @Test
+    public void testSuspendWaitResumeSuspendComplete() throws Exception
+    {
+        String response = process("suspend=1000&resume=10&suspend2=1000&complete2=10", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("COMPLETED"));
+        assertEquals(2, count(history, "suspend"));
+        assertEquals(1, count(history, "resume"));
+        assertEquals(0, count(history, "onTimeout"));
+        assertEquals(1, count(history, "onComplete"));
+    }
+
+    @Test
+    public void testSuspendWaitResumeSuspend() throws Exception
+    {
+        String response = process("suspend=1000&resume=10&suspend2=10", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("TIMEOUT"));
+        assertEquals(2, count(history, "suspend"));
+        assertEquals(1, count(history, "resume"));
+        assertEquals(1, count(history, "onTimeout"));
+        assertEquals(1, count(history, "onComplete"));
+    }
+
+    @Test
+    public void testSuspendTimeoutSuspendResume() throws Exception
+    {
+        String response = process("suspend=10&suspend2=1000&resume2=10", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("RESUMED"));
+        assertEquals(2, count(history, "suspend"));
+        assertEquals(1, count(history, "resume"));
+        assertEquals(1, count(history, "onTimeout"));
+        assertEquals(1, count(history, "onComplete"));
+    }
+
+    @Test
+    public void testSuspendTimeoutSuspendComplete() throws Exception
+    {
+        String response = process("suspend=10&suspend2=1000&complete2=10", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("COMPLETED"));
+        assertEquals(2, count(history, "suspend"));
+        assertEquals(0, count(history, "resume"));
+        assertEquals(1, count(history, "onTimeout"));
+        assertEquals(1, count(history, "onComplete"));
+    }
+
+    @Test
+    public void testSuspendTimeoutSuspend() throws Exception
+    {
+        String response = process("suspend=10&suspend2=10", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("TIMEOUT"));
+        assertEquals(2, count(history, "suspend"));
+        assertEquals(0, count(history, "resume"));
+        assertEquals(2, count(history, "onTimeout"));
+        assertEquals(1, count(history, "onComplete"));
+    }
+
+    @Test
+    public void testSuspendThrowResume() throws Exception
+    {
+        String response = process("suspend=200&resume=10&undispatch=true", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("RESUMED"));
+        assertThat(history, not(hasItem("onTimeout")));
+        assertThat(history, hasItem("onComplete"));
+    }
+
+    @Test
+    public void testSuspendResumeThrow() throws Exception
+    {
+        String response = process("suspend=200&resume=0&undispatch=true", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("RESUMED"));
+        assertThat(history, not(hasItem("onTimeout")));
+        assertThat(history, hasItem("onComplete"));
+    }
+
+    @Test
+    public void testSuspendThrowComplete() throws Exception
+    {
+        String response = process("suspend=200&complete=10&undispatch=true", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("COMPLETED"));
+        assertThat(history, not(hasItem("onTimeout")));
+        assertThat(history, hasItem("onComplete"));
+    }
+
+    @Test
+    public void testSuspendCompleteThrow() throws Exception
+    {
+        String response = process("suspend=200&complete=0&undispatch=true", null);
+        assertThat(response, startsWith("HTTP/1.1 200 OK"));
+        assertThat(response, containsString("COMPLETED"));
+        assertThat(history, not(hasItem("onTimeout")));
+        assertThat(history, hasItem("onComplete"));
+    }
+
+    private long count(List<String> history, String value)
+    {
+        return history.stream()
+                .filter(value::equals)
+                .count();
+    }
+
+    private String process(String query, String content) throws Exception
+    {
+        Server server = new Server();
+        
+        try 
+        {
+            ServerConnector connector = new ServerConnector(server);
+            server.addConnector(connector);
+            if(log != null) 
+            {
+                log.clear();
+            }
+            history.clear();
+            server.setHandler(this.setupHandler);
+        
+            server.start();
+            int port=connector.getLocalPort();
+            
+            StringBuilder request = new StringBuilder("GET /");
+    
+            if (query != null)
+                request.append("?").append(query);
+    
+            request.append(" HTTP/1.1\r\n")
+                    .append("Host: localhost\r\n")
+                    .append("Connection: close\r\n");
+    
+            if (content == null)
+            {
+                request.append("\r\n");
+            }
+            else
+            {
+                request.append("Content-Length: ").append(content.length()).append("\r\n");
+                request.append("\r\n").append(content);
+            }
+    
+            try (Socket socket = new Socket("localhost", port))
+            {
+                socket.setSoTimeout(10000);
+                socket.getOutputStream().write(request.toString().getBytes(StandardCharsets.UTF_8));
+                socket.getOutputStream().flush();
+                return toString(socket.getInputStream());
+            }
+        } 
+        finally 
+        {
+            server.stop();
+            
+            if (log != null)
+            {
+                assertThat("Log.size", log.size(),is(1));
+                String entry = log.get(0);
+                assertThat("Log entry", entry, startsWith("200 "));
+                assertThat("Log entry", entry, endsWith(" /"));
+            }
+        }
+    }
+
+    protected String toString(InputStream in) throws IOException
+    {
+        return IO.toString(in);
+    }
+
+    @SuppressWarnings("serial")
+    private static class SuspendServlet extends HttpServlet
+    {
+        private final Timer _timer = new Timer();
+        private final List<String> history;
+        private final ContinuationListener listener;
+
+        public SuspendServlet(List<String> history, ContinuationListener listener)
+        {
+            this.history = history;
+            this.listener = listener;
+        }
+
+        @Override
+        protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
+        {
+            final Continuation continuation = ContinuationSupport.getContinuation(request);
+
+            history.add(continuation.getClass().getName());
+
+            int read_before = 0;
+            long sleep_for = -1;
+            long suspend_for = -1;
+            long suspend2_for = -1;
+            long resume_after = -1;
+            long resume2_after = -1;
+            long complete_after = -1;
+            long complete2_after = -1;
+            boolean undispatch = false;
+
+            if (request.getParameter("read") != null)
+                read_before = Integer.parseInt(request.getParameter("read"));
+            if (request.getParameter("sleep") != null)
+                sleep_for = Integer.parseInt(request.getParameter("sleep"));
+            if (request.getParameter("suspend") != null)
+                suspend_for = Integer.parseInt(request.getParameter("suspend"));
+            if (request.getParameter("suspend2") != null)
+                suspend2_for = Integer.parseInt(request.getParameter("suspend2"));
+            if (request.getParameter("resume") != null)
+                resume_after = Integer.parseInt(request.getParameter("resume"));
+            if (request.getParameter("resume2") != null)
+                resume2_after = Integer.parseInt(request.getParameter("resume2"));
+            if (request.getParameter("complete") != null)
+                complete_after = Integer.parseInt(request.getParameter("complete"));
+            if (request.getParameter("complete2") != null)
+                complete2_after = Integer.parseInt(request.getParameter("complete2"));
+            if (request.getParameter("undispatch") != null)
+                undispatch = Boolean.parseBoolean(request.getParameter("undispatch"));
+
+            if (continuation.isInitial())
+            {
+                history.add("initial");
+                if (read_before > 0)
+                {
+                    byte[] buf = new byte[read_before];
+                    request.getInputStream().read(buf);
+                }
+                else if (read_before < 0)
+                {
+                    InputStream in = request.getInputStream();
+                    int b = in.read();
+                    while (b != -1)
+                        b = in.read();
+                }
+
+                if (suspend_for >= 0)
+                {
+                    if (suspend_for > 0)
+                        continuation.setTimeout(suspend_for);
+                    continuation.addContinuationListener(listener);
+                    history.add("suspend");
+                    continuation.suspend(response);
+
+                    if (complete_after > 0)
+                    {
+                        TimerTask complete = new TimerTask()
+                        {
+                            @Override
+                            public void run()
+                            {
+                                try
+                                {
+                                    response.setStatus(200);
+                                    response.getOutputStream().println("COMPLETED");
+                                    continuation.complete();
+                                }
+                                catch (Exception e)
+                                {
+                                    e.printStackTrace();
+                                }
+                            }
+                        };
+                        _timer.schedule(complete, complete_after);
+                    }
+                    else if (complete_after == 0)
+                    {
+                        response.setStatus(200);
+                        response.getOutputStream().println("COMPLETED");
+                        continuation.complete();
+                    }
+                    else if (resume_after > 0)
+                    {
+                        TimerTask resume = new TimerTask()
+                        {
+                            @Override
+                            public void run()
+                            {
+                                history.add("resume");
+                                continuation.resume();
+                            }
+                        };
+                        _timer.schedule(resume, resume_after);
+                    }
+                    else if (resume_after == 0)
+                    {
+                        history.add("resume");
+                        continuation.resume();
+                    }
+
+                    if (undispatch)
+                    {
+                        continuation.undispatch();
+                    }
+                }
+                else if (sleep_for >= 0)
+                {
+                    try
+                    {
+                        Thread.sleep(sleep_for);
+                    }
+                    catch (InterruptedException e)
+                    {
+                        e.printStackTrace();
+                    }
+                    response.setStatus(200);
+                    response.getOutputStream().println("SLEPT");
+                }
+                else
+                {
+                    response.setStatus(200);
+                    response.getOutputStream().println("NORMAL");
+                }
+            }
+            else
+            {
+                history.add("!initial");
+                if (suspend2_for >= 0 && request.getAttribute("2nd") == null)
+                {
+                    request.setAttribute("2nd", "cycle");
+
+                    if (suspend2_for > 0)
+                        continuation.setTimeout(suspend2_for);
+
+                    history.add("suspend");
+                    continuation.suspend(response);
+
+                    if (complete2_after > 0)
+                    {
+                        TimerTask complete = new TimerTask()
+                        {
+                            @Override
+                            public void run()
+                            {
+                                try
+                                {
+                                    response.setStatus(200);
+                                    response.getOutputStream().println("COMPLETED");
+                                    continuation.complete();
+                                }
+                                catch (Exception e)
+                                {
+                                    e.printStackTrace();
+                                }
+                            }
+                        };
+                        _timer.schedule(complete, complete2_after);
+                    }
+                    else if (complete2_after == 0)
+                    {
+                        response.setStatus(200);
+                        response.getOutputStream().println("COMPLETED");
+                        continuation.complete();
+                    }
+                    else if (resume2_after > 0)
+                    {
+                        TimerTask resume = new TimerTask()
+                        {
+                            @Override
+                            public void run()
+                            {
+                                history.add("resume");
+                                continuation.resume();
+                            }
+                        };
+                        _timer.schedule(resume, resume2_after);
+                    }
+                    else if (resume2_after == 0)
+                    {
+                        history.add("resume");
+                        continuation.resume();
+                    }
+
+                    if (undispatch)
+                    {
+                        continuation.undispatch();
+                    }
+                }
+                else if (continuation.isExpired())
+                {
+                    response.setStatus(200);
+                    response.getOutputStream().println("TIMEOUT");
+                }
+                else if (continuation.isResumed())
+                {
+                    response.setStatus(200);
+                    response.getOutputStream().println("RESUMED");
+                }
+                else
+                {
+                    response.setStatus(200);
+                    response.getOutputStream().println("UNKNOWN");
+                }
+            }
+        }
+    }
+
+    private static class Listener implements ContinuationListener
+    {
+        private final List<String> history;
+
+        public Listener(List<String> history)
+        {
+            this.history = history;
+        }
+
+        @Override
+        public void onComplete(Continuation continuation)
+        {
+            history.add("onComplete");
+        }
+
+        @Override
+        public void onTimeout(Continuation continuation)
+        {
+            history.add("onTimeout");
+        }
+    }
+    
+    public static class Log extends AbstractLifeCycle implements RequestLog
+    {
+        private final List<String> log;
+        
+        public Log(List<String> log) 
+        {
+            this.log = log;
+        }
+        
+        @Override
+        public void log(Request request, Response response)
+        {
+            int status = response.getCommittedMetaData().getStatus();
+            long written = response.getHttpChannel().getBytesWritten();
+            log.add(status+" "+written+" "+request.getRequestURI());
+        }
+    }
+}
diff --git a/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/FauxContinuationTest.java b/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/FauxContinuationTest.java
deleted file mode 100644
index 4b2eee9..0000000
--- a/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/FauxContinuationTest.java
+++ /dev/null
@@ -1,156 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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 java.io.InputStream;
-
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.util.IO;
-
-
-public class FauxContinuationTest extends ContinuationBase
-{
-    protected Server _server = new Server();
-    protected ServletHandler _servletHandler;
-    protected ServerConnector _connector;
-    FilterHolder _filter;
-
-    protected void setUp() throws Exception
-    {
-        _connector = new ServerConnector(_server);
-        _server.setConnectors(new Connector[]{ _connector });
-        ServletContextHandler servletContext = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS);
-        _server.setHandler(servletContext);
-        _servletHandler=servletContext.getServletHandler();
-        ServletHolder holder=new ServletHolder(_servlet);
-        _servletHandler.addServletWithMapping(holder,"/");
-        
-        _filter=_servletHandler.addFilterWithMapping(ContinuationFilter.class,"/*",null);
-        _filter.setInitParameter("debug","true");
-        _filter.setInitParameter("faux","true");
-        _server.start();
-        _port=_connector.getLocalPort();
-        
-    }
-
-    protected void tearDown() throws Exception
-    {
-        _server.stop();
-    }
-
-    public void testContinuation() throws Exception
-    {
-        doNormal("FauxContinuation");
-    }
-    
-    public void testSleep() throws Exception
-    {
-        doSleep();
-    }
-
-    public void testSuspend() throws Exception
-    {
-        doSuspend();
-    }
-
-    public void testSuspendWaitResume() throws Exception
-    {
-        doSuspendWaitResume();
-    }
-
-    public void testSuspendResume() throws Exception
-    {
-        doSuspendResume();
-    }
-
-    public void testSuspendWaitComplete() throws Exception
-    {
-        doSuspendWaitComplete();
-    }
-
-    public void testSuspendComplete() throws Exception
-    {
-        doSuspendComplete();
-    }
-
-    public void testSuspendWaitResumeSuspendWaitResume() throws Exception
-    {
-        doSuspendWaitResumeSuspendWaitResume();
-    }
-    
-    public void testSuspendWaitResumeSuspendComplete() throws Exception
-    {
-        doSuspendWaitResumeSuspendComplete();
-    }
-
-    public void testSuspendWaitResumeSuspend() throws Exception
-    {
-        doSuspendWaitResumeSuspend();
-    }
-
-    public void testSuspendTimeoutSuspendResume() throws Exception
-    {
-        doSuspendTimeoutSuspendResume();
-    }
-
-    public void testSuspendTimeoutSuspendComplete() throws Exception
-    {
-        doSuspendTimeoutSuspendComplete();
-    }
-
-    public void testSuspendTimeoutSuspend() throws Exception
-    {
-        doSuspendTimeoutSuspend();
-    }
-
-    public void testSuspendThrowResume() throws Exception
-    {
-        doSuspendThrowResume();
-    }
-
-    public void testSuspendResumeThrow() throws Exception
-    {
-        doSuspendResumeThrow();
-    }
-
-    public void testSuspendThrowComplete() throws Exception
-    {
-        doSuspendThrowComplete();
-    }
-
-    public void testSuspendCompleteThrow() throws Exception
-    {
-        doSuspendCompleteThrow();
-    }
-    
-    
-    
-    protected String toString(InputStream in) throws IOException
-    {
-        return IO.toString(in);
-    }
-    
-}
diff --git a/tests/test-continuation/src/test/resources/jetty-logging.properties b/tests/test-continuation/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..d1e0e0c
--- /dev/null
+++ b/tests/test-continuation/src/test/resources/jetty-logging.properties
@@ -0,0 +1,3 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+org.eclipse.jetty.LEVEL=WARN
+# org.eclipse.jetty.server.LEVEL=DEBUG
diff --git a/tests/test-http-client-transport/pom.xml b/tests/test-http-client-transport/pom.xml
new file mode 100644
index 0000000..358b714
--- /dev/null
+++ b/tests/test-http-client-transport/pom.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <groupId>org.eclipse.jetty.tests</groupId>
+        <artifactId>tests-parent</artifactId>
+        <version>9.3.19-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>test-http-client-transport</artifactId>
+    <packaging>jar</packaging>
+    <name>Test :: HTTP Client Transports</name>
+
+    <properties>
+        <bundle-symbolic-name>${project.groupId}.client.http</bundle-symbolic-name>
+    </properties>
+
+    <profiles>
+        <profile>
+            <id>jdk8</id>
+            <activation>
+                <jdk>[1.8,1.9)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-dependency-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>copy</id>
+                                <phase>generate-resources</phase>
+                                <goals>
+                                    <goal>copy</goal>
+                                </goals>
+                                <configuration>
+                                    <artifactItems>
+                                        <artifactItem>
+                                            <groupId>org.mortbay.jetty.alpn</groupId>
+                                            <artifactId>alpn-boot</artifactId>
+                                            <version>${alpn.version}</version>
+                                            <type>jar</type>
+                                            <overWrite>false</overWrite>
+                                            <outputDirectory>${project.build.directory}/alpn</outputDirectory>
+                                        </artifactItem>
+                                    </artifactItems>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <argLine>-Xbootclasspath/p:${project.build.directory}/alpn/alpn-boot-${alpn.version}.jar
+                            </argLine>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>jdk9</id>
+            <activation>
+                <jdk>[1.9,)</jdk>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>org.eclipse.jetty</groupId>
+                    <artifactId>jetty-alpn-java-client</artifactId>
+                    <version>${project.version}</version>
+                    <scope>test</scope>
+                </dependency>
+                <dependency>
+                    <groupId>org.eclipse.jetty</groupId>
+                    <artifactId>jetty-alpn-java-server</artifactId>
+                    <version>${project.version}</version>
+                    <scope>test</scope>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <!-- DO NOT DEPLOY (or Release) -->
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-servlet</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.http2</groupId>
+            <artifactId>http2-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-alpn-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.http2</groupId>
+            <artifactId>http2-http-client-transport</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.fcgi</groupId>
+            <artifactId>fcgi-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.toolchain</groupId>
+            <artifactId>jetty-test-helper</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java
new file mode 100644
index 0000000..911b38f
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java
@@ -0,0 +1,274 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServlet;
+
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpClientTransport;
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
+import org.eclipse.jetty.fcgi.server.ServerFCGIConnectionFactory;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.SocketAddressResolver;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public abstract class AbstractTest
+{
+    @Parameterized.Parameters(name = "transport: {0}")
+    public static Object[] parameters() throws Exception
+    {
+        return Transport.values();
+    }
+
+    @Rule
+    public final TestTracker tracker = new TestTracker();
+
+    protected final HttpConfiguration httpConfig = new HttpConfiguration();
+    protected final Transport transport;
+    protected SslContextFactory sslContextFactory;
+    protected Server server;
+    protected ServerConnector connector;
+    protected ServletContextHandler context;
+    protected String servletPath = "/servlet";
+    protected HttpClient client;
+
+    public AbstractTest(Transport transport)
+    {
+        Assume.assumeNotNull(transport);
+        this.transport = transport;
+    }
+
+    public void start(Handler handler) throws Exception
+    {
+        startServer(handler);
+        startClient();
+    }
+
+    public void start(HttpServlet servlet) throws Exception
+    {
+        startServer(servlet);
+        startClient();
+    }
+
+    protected void startServer(HttpServlet servlet) throws Exception
+    {
+        context = new ServletContextHandler();
+        context.setContextPath("/");
+        ServletHolder holder = new ServletHolder(servlet);
+        holder.setAsyncSupported(true);
+        context.addServlet(holder, servletPath);
+        startServer(context);
+    }
+
+    protected void startServer(Handler handler) throws Exception
+    {
+        sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
+        sslContextFactory.setTrustStorePassword("storepwd");
+        sslContextFactory.setUseCipherSuitesOrder(true);
+        sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
+        QueuedThreadPool serverThreads = new QueuedThreadPool();
+        serverThreads.setName("server");
+        server = new Server(serverThreads);
+        connector = newServerConnector(server);
+        server.addConnector(connector);
+        server.setHandler(handler);
+        server.start();
+    }
+
+    protected ServerConnector newServerConnector(Server server)
+    {
+        return new ServerConnector(server, provideServerConnectionFactory(transport));
+    }
+
+    protected void startClient() throws Exception
+    {
+        QueuedThreadPool clientThreads = new QueuedThreadPool();
+        clientThreads.setName("client");
+        client = newHttpClient(provideClientTransport(transport), sslContextFactory);
+        client.setExecutor(clientThreads);
+        client.setSocketAddressResolver(new SocketAddressResolver.Sync());
+        client.start();
+    }
+
+    protected ConnectionFactory[] provideServerConnectionFactory(Transport transport)
+    {
+        List<ConnectionFactory> result = new ArrayList<>();
+        switch (transport)
+        {
+            case HTTP:
+            {
+                result.add(new HttpConnectionFactory(httpConfig));
+                break;
+            }
+            case HTTPS:
+            {
+                httpConfig.addCustomizer(new SecureRequestCustomizer());
+                HttpConnectionFactory http = new HttpConnectionFactory(httpConfig);
+                SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, http.getProtocol());
+                result.add(ssl);
+                result.add(http);
+                break;
+            }
+            case H2C:
+            {
+                result.add(new HTTP2CServerConnectionFactory(httpConfig));
+                break;
+            }
+            case H2:
+            {
+                httpConfig.addCustomizer(new SecureRequestCustomizer());
+                HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpConfig);
+                ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory("h2");
+                SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
+                result.add(ssl);
+                result.add(alpn);
+                result.add(h2);
+                break;
+            }
+            case FCGI:
+            {
+                result.add(new ServerFCGIConnectionFactory(httpConfig));
+                break;
+            }
+            default:
+            {
+                throw new IllegalArgumentException();
+            }
+        }
+        return result.toArray(new ConnectionFactory[result.size()]);
+    }
+
+    protected HttpClientTransport provideClientTransport(Transport transport)
+    {
+        switch (transport)
+        {
+            case HTTP:
+            case HTTPS:
+            {
+                return new HttpClientTransportOverHTTP(1);
+            }
+            case H2C:
+            case H2:
+            {
+                HTTP2Client http2Client = newHTTP2Client();
+                return new HttpClientTransportOverHTTP2(http2Client);
+            }
+            case FCGI:
+            {
+                return new HttpClientTransportOverFCGI(1, false, "");
+            }
+            default:
+            {
+                throw new IllegalArgumentException();
+            }
+        }
+    }
+
+    protected HttpClient newHttpClient(HttpClientTransport transport, SslContextFactory sslContextFactory)
+    {
+        return new HttpClient(transport, sslContextFactory);
+    }
+
+    protected HTTP2Client newHTTP2Client()
+    {
+        HTTP2Client http2Client = new HTTP2Client();
+        http2Client.setSelectors(1);
+        return http2Client;
+    }
+
+    protected String getScheme()
+    {
+        return isTransportSecure() ? "https" : "http";
+    }
+
+    protected String newURI()
+    {
+        return getScheme() + "://localhost:" + connector.getLocalPort();
+    }
+
+    protected boolean isTransportSecure()
+    {
+        switch (transport)
+        {
+            case HTTP:
+            case H2C:
+            case FCGI:
+                return false;
+            case HTTPS:
+            case H2:
+                return true;
+            default:
+                throw new IllegalArgumentException();
+        }
+    }
+
+    @After
+    public void stop() throws Exception
+    {
+        stopClient();
+        stopServer();
+    }
+
+    protected void stopClient() throws Exception
+    {
+        if (client != null)
+            client.stop();
+    }
+
+    protected void stopServer() throws Exception
+    {
+        if (server != null)
+            server.stop();
+    }
+
+    protected enum Transport
+    {
+        HTTP, HTTPS, H2C, H2, FCGI
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncIOServletTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncIOServletTest.java
new file mode 100644
index 0000000..34e2d39
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncIOServletTest.java
@@ -0,0 +1,1184 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.UncheckedIOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Deque;
+import java.util.Queue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Destination;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
+import org.eclipse.jetty.client.util.BufferingResponseListener;
+import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.client.util.InputStreamContentProvider;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http2.HTTP2Session;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.client.http.HttpConnectionOverHTTP2;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandler.Context;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class AsyncIOServletTest extends AbstractTest
+{
+    private static final ThreadLocal<RuntimeException> scope = new ThreadLocal<>();
+
+    public AsyncIOServletTest(Transport transport)
+    {
+        super(transport == Transport.FCGI ? null : transport);
+    }
+
+    @Override
+    protected void startServer(Handler handler) throws Exception
+    {
+        if (handler == context)
+        {
+            // Add this listener before the context is started, so it's durable.
+            context.addEventListener(new ContextHandler.ContextScopeListener()
+            {
+                @Override
+                public void enterScope(Context context, Request request, Object reason)
+                {
+                    checkScope();
+                    scope.set(new RuntimeException());
+                }
+
+                @Override
+                public void exitScope(Context context, Request request)
+                {
+                    assertScope();
+                    scope.set(null);
+                }
+            });
+        }
+        super.startServer(handler);
+    }
+
+    private void assertScope()
+    {
+        Assert.assertNotNull("Not in scope", scope.get());
+    }
+
+    private void checkScope()
+    {
+        RuntimeException callScope = scope.get();
+        if (callScope != null)
+            throw callScope;
+    }
+
+    protected void stopServer() throws Exception
+    {
+        super.stopServer();
+        checkScope();
+        scope.set(null);
+    }
+
+    private void sleep(long ms)
+    {
+        try
+        {
+            Thread.sleep(ms);
+        }
+        catch (InterruptedException e)
+        {
+            throw new UncheckedIOException(new InterruptedIOException());
+        }
+    }
+
+    @Test
+    public void testAsyncReadThrowsException() throws Exception
+    {
+        testAsyncReadThrows(new NullPointerException("explicitly_thrown_by_test"));
+    }
+
+    @Test
+    public void testAsyncReadThrowsError() throws Exception
+    {
+        testAsyncReadThrows(new Error("explicitly_thrown_by_test"));
+    }
+
+    private void testAsyncReadThrows(Throwable throwable) throws Exception
+    {
+        CountDownLatch latch = new CountDownLatch(1);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                assertScope();
+                AsyncContext asyncContext = request.startAsync(request, response);
+                request.getInputStream().setReadListener(new ReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        assertScope();
+                        if (throwable instanceof RuntimeException)
+                            throw (RuntimeException)throwable;
+                        if (throwable instanceof Error)
+                            throw (Error)throwable;
+                        throw new IOException(throwable);
+                    }
+
+                    @Override
+                    public void onAllDataRead() throws IOException
+                    {
+                        assertScope();
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        assertScope();
+                        Assert.assertThat("onError type", t, instanceOf(throwable.getClass()));
+                        Assert.assertThat("onError message", t.getMessage(), is(throwable.getMessage()));
+                        latch.countDown();
+                        response.setStatus(500);
+                        asyncContext.complete();
+                    }
+                });
+            }
+        });
+
+        ContentResponse response = client.newRequest(newURI())
+                .method(HttpMethod.POST)
+                .path(servletPath)
+                .content(new StringContentProvider("0123456789"))
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        assertTrue(latch.await(5, TimeUnit.SECONDS));
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR_500, response.getStatus());
+    }
+
+    @Test
+    public void testAsyncReadIdleTimeout() throws Exception
+    {
+        int status = 567;
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                assertScope();
+                AsyncContext asyncContext = request.startAsync(request, response);
+                asyncContext.setTimeout(0);
+                ServletInputStream inputStream = request.getInputStream();
+                inputStream.setReadListener(new ReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        assertScope();
+                        while (inputStream.isReady() && !inputStream.isFinished())
+                            inputStream.read();
+                    }
+
+                    @Override
+                    public void onAllDataRead() throws IOException
+                    {
+                        assertScope();
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        assertScope();
+                        response.setStatus(status);
+                        // Do not put Connection: close header here, the test
+                        // verifies that the server closes no matter what.
+                        asyncContext.complete();
+                    }
+                });
+            }
+        });
+        connector.setIdleTimeout(1000);
+        CountDownLatch closeLatch = new CountDownLatch(1);
+        connector.addBean(new Connection.Listener()
+        {
+            @Override
+            public void onOpened(Connection connection)
+            {
+            }
+
+            @Override
+            public void onClosed(Connection connection)
+            {
+                closeLatch.countDown();
+            }
+        });
+
+        String data = "0123456789";
+        DeferredContentProvider content = new DeferredContentProvider();
+        content.offer(ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8)));
+        CountDownLatch responseLatch = new CountDownLatch(1);
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .method(HttpMethod.POST)
+                .path(servletPath)
+                .content(content)
+                .onResponseSuccess(r -> responseLatch.countDown())
+                .timeout(5, TimeUnit.SECONDS)
+                .send(result ->
+                {
+                    assertEquals(status, result.getResponse().getStatus());
+                    clientLatch.countDown();
+                });
+
+        assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
+        assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+        content.close();
+        assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testOnErrorThrows() throws Exception
+    {
+        AtomicInteger errors = new AtomicInteger();
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                assertScope();
+                if (request.getDispatcherType() == DispatcherType.ERROR)
+                {
+                    response.flushBuffer();
+                    return;
+                }
+
+                request.startAsync(request, response);
+                request.getInputStream().setReadListener(new ReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        assertScope();
+                        throw new NullPointerException("explicitly_thrown_by_test_1");
+                    }
+
+                    @Override
+                    public void onAllDataRead() throws IOException
+                    {
+                        assertScope();
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        assertScope();
+                        errors.incrementAndGet();
+                        throw new NullPointerException("explicitly_thrown_by_test_2")
+                        {{
+                            this.initCause(t);
+                        }};
+                    }
+                });
+            }
+        });
+
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            ContentResponse response = client.newRequest(newURI())
+                    .path(servletPath)
+                    .content(new StringContentProvider("0123456789"))
+                    .timeout(5, TimeUnit.SECONDS)
+                    .send();
+
+            assertEquals(HttpStatus.INTERNAL_SERVER_ERROR_500, response.getStatus());
+            Assert.assertEquals(1, errors.get());
+        }
+    }
+
+    @Test
+    public void testAsyncWriteThrowsException() throws Exception
+    {
+        testAsyncWriteThrows(new NullPointerException("explicitly_thrown_by_test"));
+    }
+
+    @Test
+    public void testAsyncWriteThrowsError() throws Exception
+    {
+        testAsyncWriteThrows(new Error("explicitly_thrown_by_test"));
+    }
+
+    private void testAsyncWriteThrows(Throwable throwable) throws Exception
+    {
+        CountDownLatch latch = new CountDownLatch(1);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                assertScope();
+                AsyncContext asyncContext = request.startAsync(request, response);
+                response.getOutputStream().setWriteListener(new WriteListener()
+                {
+                    @Override
+                    public void onWritePossible() throws IOException
+                    {
+                        assertScope();
+                        if (throwable instanceof RuntimeException)
+                            throw (RuntimeException)throwable;
+                        if (throwable instanceof Error)
+                            throw (Error)throwable;
+                        throw new IOException(throwable);
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        assertScope();
+                        latch.countDown();
+                        response.setStatus(500);
+                        asyncContext.complete();
+                        Assert.assertSame(throwable, t);
+                    }
+                });
+            }
+        });
+
+        ContentResponse response = client.newRequest(newURI())
+                .path(servletPath)
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        assertTrue(latch.await(5, TimeUnit.SECONDS));
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR_500, response.getStatus());
+    }
+
+    @Test
+    public void testAsyncWriteClosed() throws Exception
+    {
+        String text = "Now is the winter of our discontent. How Now Brown Cow. The quick brown fox jumped over the lazy dog.\n";
+        for (int i = 0; i < 10; i++)
+            text = text + text;
+        byte[] data = text.getBytes(StandardCharsets.UTF_8);
+
+        CountDownLatch errorLatch = new CountDownLatch(1);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                assertScope();
+                response.flushBuffer();
+
+                AsyncContext async = request.startAsync();
+                ServletOutputStream out = response.getOutputStream();
+                out.setWriteListener(new WriteListener()
+                {
+                    @Override
+                    public void onWritePossible() throws IOException
+                    {
+                        assertScope();
+
+                        // Wait for the failure to arrive to
+                        // the server while we are about to write.
+                        sleep(1000);
+
+                        out.write(data);
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        assertScope();
+                        async.complete();
+                        errorLatch.countDown();
+                    }
+                });
+            }
+        });
+
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .path(servletPath)
+                .onResponseHeaders(response ->
+                {
+                    if (response.getStatus() == HttpStatus.OK_200)
+                        response.abort(new IOException("explicitly_closed_by_test"));
+                })
+                .send(result ->
+                {
+                    if (result.isFailed())
+                        clientLatch.countDown();
+                });
+
+        assertTrue(errorLatch.await(5, TimeUnit.SECONDS));
+        assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testIsReadyAtEOF() throws Exception
+    {
+        String text = "TEST\n";
+        byte[] data = text.getBytes(StandardCharsets.UTF_8);
+
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                assertScope();
+                response.flushBuffer();
+
+                AsyncContext async = request.startAsync();
+                ServletInputStream input = request.getInputStream();
+                ServletOutputStream output = response.getOutputStream();
+
+                input.setReadListener(new ReadListener()
+                {
+                    transient int _i = 0;
+                    transient boolean _minusOne = false;
+                    transient boolean _finished = false;
+
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        assertScope();
+                        while (input.isReady() && !input.isFinished())
+                        {
+                            int b = input.read();
+                            if (b == -1)
+                                _minusOne = true;
+                            else if (data[_i++] != b)
+                                throw new IllegalStateException();
+                        }
+
+                        if (input.isFinished())
+                            _finished = true;
+                    }
+
+                    @Override
+                    public void onAllDataRead() throws IOException
+                    {
+                        assertScope();
+                        output.write(String.format("i=%d eof=%b finished=%b", _i, _minusOne, _finished).getBytes(StandardCharsets.UTF_8));
+                        async.complete();
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        assertScope();
+                        t.printStackTrace();
+                        async.complete();
+                    }
+                });
+            }
+        });
+
+        ContentResponse response = client.newRequest(newURI())
+                .method(HttpMethod.POST)
+                .path(servletPath)
+                .header(HttpHeader.CONNECTION, "close")
+                .content(new StringContentProvider(text))
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        String responseContent = response.getContentAsString();
+        assertThat(responseContent, containsString("i=" + data.length + " eof=true finished=true"));
+    }
+
+    @Test
+    public void testOnAllDataRead() throws Exception
+    {
+        String success = "SUCCESS";
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                assertScope();
+                response.flushBuffer();
+
+                AsyncContext async = request.startAsync();
+                async.setTimeout(5000);
+                ServletInputStream in = request.getInputStream();
+                ServletOutputStream out = response.getOutputStream();
+
+                in.setReadListener(new ReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        assertScope();
+                        try
+                        {
+                            sleep(1000);
+                            if (!in.isReady())
+                                throw new IllegalStateException();
+                            if (in.read() != 'X')
+                                throw new IllegalStateException();
+                            if (!in.isReady())
+                                throw new IllegalStateException();
+                            if (in.read() != -1)
+                                throw new IllegalStateException();
+                        }
+                        catch (IOException x)
+                        {
+                            throw new UncheckedIOException(x);
+                        }
+                    }
+
+                    @Override
+                    public void onAllDataRead() throws IOException
+                    {
+                        assertScope();
+                        out.write(success.getBytes(StandardCharsets.UTF_8));
+                        async.complete();
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        assertScope();
+                        t.printStackTrace();
+                        async.complete();
+                    }
+                });
+            }
+        });
+
+        byte[] data = "X".getBytes(StandardCharsets.UTF_8);
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        DeferredContentProvider content = new DeferredContentProvider()
+        {
+            @Override
+            public long getLength()
+            {
+                return data.length;
+            }
+        };
+        client.newRequest(newURI())
+                .method(HttpMethod.POST)
+                .path(servletPath)
+                .content(content)
+                .timeout(5, TimeUnit.SECONDS)
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        if (result.isSucceeded())
+                        {
+                            Response response = result.getResponse();
+                            String content = getContentAsString();
+                            if (response.getStatus() == HttpStatus.OK_200 && success.equals(content))
+                                clientLatch.countDown();
+                        }
+                    }
+                });
+
+        sleep(100);
+        content.offer(ByteBuffer.wrap(data));
+        content.close();
+
+        assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testOtherThreadOnAllDataRead() throws Exception
+    {
+        String success = "SUCCESS";
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                assertScope();
+                response.flushBuffer();
+
+                AsyncContext async = request.startAsync();
+                async.setTimeout(0);
+                ServletInputStream input = request.getInputStream();
+                ServletOutputStream output = response.getOutputStream();
+
+                if (request.getDispatcherType() == DispatcherType.ERROR)
+                    throw new IllegalStateException();
+
+                input.setReadListener(new ReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        assertScope();
+                        async.start(() ->
+                        {
+                            assertScope();
+                            try
+                            {
+                                sleep(1000);
+                                if (!input.isReady())
+                                    throw new IllegalStateException();
+                                if (input.read() != 'X')
+                                    throw new IllegalStateException();
+                                if (!input.isReady())
+                                    throw new IllegalStateException();
+                                if (input.read() != -1)
+                                    throw new IllegalStateException();
+                            }
+                            catch (IOException x)
+                            {
+                                throw new UncheckedIOException(x);
+                            }
+                        });
+                    }
+
+                    @Override
+                    public void onAllDataRead() throws IOException
+                    {
+                        output.write(success.getBytes(StandardCharsets.UTF_8));
+                        async.complete();
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        assertScope();
+                        t.printStackTrace();
+                        async.complete();
+                    }
+                });
+            }
+        });
+
+        byte[] data = "X".getBytes(StandardCharsets.UTF_8);
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        DeferredContentProvider content = new DeferredContentProvider();
+        client.newRequest(newURI())
+                .method(HttpMethod.POST)
+                .path(servletPath)
+                .content(content)
+                .timeout(5, TimeUnit.SECONDS)
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        if (result.isSucceeded())
+                        {
+                            Response response = result.getResponse();
+                            String content = getContentAsString();
+                            if (response.getStatus() == HttpStatus.OK_200 && success.equals(content))
+                                clientLatch.countDown();
+                        }
+                    }
+                });
+
+        sleep(100);
+        content.offer(ByteBuffer.wrap(data));
+        content.close();
+
+        assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testCompleteBeforeOnAllDataRead() throws Exception
+    {
+        String success = "SUCCESS";
+        AtomicBoolean allDataRead = new AtomicBoolean(false);
+
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                assertScope();
+                response.flushBuffer();
+
+                AsyncContext async = request.startAsync();
+                ServletInputStream input = request.getInputStream();
+                ServletOutputStream output = response.getOutputStream();
+
+                input.setReadListener(new ReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        assertScope();
+                        while (input.isReady())
+                        {
+                            int b = input.read();
+                            if (b < 0)
+                            {
+                                output.write(success.getBytes(StandardCharsets.UTF_8));
+                                async.complete();
+                                return;
+                            }
+                        }
+                    }
+
+                    @Override
+                    public void onAllDataRead() throws IOException
+                    {
+                        assertScope();
+                        output.write("FAILURE".getBytes(StandardCharsets.UTF_8));
+                        allDataRead.set(true);
+                        throw new IllegalStateException();
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        assertScope();
+                        t.printStackTrace();
+                    }
+                });
+            }
+        });
+
+        ContentResponse response = client.newRequest(newURI())
+                .method(HttpMethod.POST)
+                .path(servletPath)
+                .header(HttpHeader.CONNECTION, "close")
+                .content(new StringContentProvider("XYZ"))
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        assertThat(response.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
+        assertThat(response.getContentAsString(), Matchers.equalTo(success));
+    }
+
+    @Test
+    public void testEmptyAsyncRead() throws Exception
+    {
+        AtomicBoolean oda = new AtomicBoolean();
+        CountDownLatch latch = new CountDownLatch(1);
+
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                assertScope();
+                AsyncContext asyncContext = request.startAsync(request, response);
+                response.setStatus(200);
+                response.getOutputStream().close();
+                request.getInputStream().setReadListener(new ReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        assertScope();
+                        oda.set(true);
+                    }
+
+                    @Override
+                    public void onAllDataRead() throws IOException
+                    {
+                        assertScope();
+                        asyncContext.complete();
+                        latch.countDown();
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        assertScope();
+                        t.printStackTrace();
+                        asyncContext.complete();
+                    }
+                });
+            }
+        });
+
+        ContentResponse response = client.newRequest(newURI())
+                .path(servletPath)
+                .header(HttpHeader.CONNECTION, "close")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        assertThat(response.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
+        assertTrue(latch.await(5, TimeUnit.SECONDS));
+        // onDataAvailable must not be called.
+        Assert.assertFalse(oda.get());
+    }
+
+    @Test
+    public void testWriteFromOnDataAvailable() throws Exception
+    {
+        Queue<Throwable> errors = new ConcurrentLinkedQueue<>();
+        CountDownLatch writeLatch = new CountDownLatch(1);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                AsyncContext asyncContext = request.startAsync();
+                request.getInputStream().setReadListener(new ReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        ServletInputStream input = request.getInputStream();
+                        ServletOutputStream output = response.getOutputStream();
+                        while (input.isReady())
+                        {
+                            byte[] buffer = new byte[512];
+                            int read = input.read(buffer);
+                            if (read < 0)
+                            {
+                                asyncContext.complete();
+                                break;
+                            }
+                            if (output.isReady())
+                                output.write(buffer, 0, read);
+                            else
+                                Assert.fail();
+                        }
+                    }
+
+                    @Override
+                    public void onAllDataRead() throws IOException
+                    {
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        errors.offer(t);
+                    }
+                });
+                response.getOutputStream().setWriteListener(new WriteListener()
+                {
+                    @Override
+                    public void onWritePossible() throws IOException
+                    {
+                        writeLatch.countDown();
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        errors.offer(t);
+                    }
+                });
+            }
+        });
+
+        String content = "0123456789ABCDEF";
+        DeferredContentProvider contentProvider = new DeferredContentProvider();
+        contentProvider.offer(ByteBuffer.wrap(content.getBytes(StandardCharsets.UTF_8)));
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .method(HttpMethod.POST)
+                .path(servletPath)
+                .content(contentProvider)
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        if (result.isSucceeded())
+                        {
+                            Response response = result.getResponse();
+                            assertThat(response.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
+                            assertThat(getContentAsString(), Matchers.equalTo(content));
+                            assertThat(errors, Matchers.hasSize(0));
+                            clientLatch.countDown();
+                        }
+                    }
+                });
+
+        assertTrue(writeLatch.await(5, TimeUnit.SECONDS));
+
+        contentProvider.close();
+
+        assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testAsyncReadEarlyEOF() throws Exception
+    {
+        // SSLEngine receives the close alert from the client, and when
+        // the server passes the response to encrypt and write, SSLEngine
+        // only generates the close alert back, without encrypting the
+        // response, so we need to skip the transports over TLS.
+        Assume.assumeThat(transport, Matchers.not(Matchers.isOneOf(Transport.HTTPS, Transport.H2)));
+
+        String content = "jetty";
+        int responseCode = HttpStatus.NO_CONTENT_204;
+        CountDownLatch readLatch = new CountDownLatch(content.length());
+        CountDownLatch errorLatch = new CountDownLatch(1);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                AsyncContext asyncContext = request.startAsync();
+                ServletInputStream input = request.getInputStream();
+                input.setReadListener(new ReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        while (input.isReady() && !input.isFinished())
+                        {
+                            int read = input.read();
+                            // System.err.printf("%x%n", read);
+                            readLatch.countDown();
+                        }
+                    }
+
+                    @Override
+                    public void onAllDataRead() throws IOException
+                    {
+                    }
+
+                    @Override
+                    public void onError(Throwable x)
+                    {
+                        response.setStatus(responseCode);
+                        asyncContext.complete();
+                        errorLatch.countDown();
+                    }
+                });
+            }
+        });
+
+        CountDownLatch responseLatch = new CountDownLatch(1);
+        DeferredContentProvider contentProvider = new DeferredContentProvider();
+        contentProvider.offer(ByteBuffer.wrap(content.getBytes(StandardCharsets.UTF_8)));
+        org.eclipse.jetty.client.api.Request request = client.newRequest(newURI())
+                .method(HttpMethod.POST)
+                .path(servletPath)
+                .content(contentProvider)
+                .onResponseSuccess(response -> responseLatch.countDown());
+
+        Destination destination = client.getDestination(getScheme(), "localhost", connector.getLocalPort());
+        FuturePromise<org.eclipse.jetty.client.api.Connection> promise = new FuturePromise<>();
+        destination.newConnection(promise);
+        org.eclipse.jetty.client.api.Connection connection = promise.get(5, TimeUnit.SECONDS);
+        CountDownLatch clientLatch = new CountDownLatch(1);
+        connection.send(request, result ->
+        {
+            assertThat(result.getResponse().getStatus(), Matchers.equalTo(responseCode));
+            clientLatch.countDown();
+        });
+
+        assertTrue(readLatch.await(5, TimeUnit.SECONDS));
+
+        switch (transport)
+        {
+            case HTTP:
+            case HTTPS:
+                ((HttpConnectionOverHTTP)connection).getEndPoint().shutdownOutput();
+                break;
+            case H2C:
+            case H2:
+                // In case of HTTP/2, we not only send the request, but also the preface and
+                // SETTINGS frames. SETTINGS frame need to be replied, so we want to wait to
+                // write the reply before shutting output down, so that the test does not fail.
+                Thread.sleep(1000);
+                Session session = ((HttpConnectionOverHTTP2)connection).getSession();
+                ((HTTP2Session)session).getEndPoint().shutdownOutput();
+                break;
+            default:
+                Assert.fail();
+        }
+
+        // Wait for the response to arrive before finishing the request.
+        assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+        contentProvider.close();
+
+        assertTrue(errorLatch.await(5, TimeUnit.SECONDS));
+        assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testWriteListenerFromOtherThread() throws Exception
+    {
+        start(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                AsyncContext asyncContext = request.startAsync();
+                asyncContext.setTimeout(0);
+                request.getInputStream().setReadListener(new Listener(asyncContext));
+            }
+        });
+
+        int cores = 4;
+        int iterations = 10;
+        CountDownLatch latch = new CountDownLatch(cores * iterations);
+        Deque<Throwable> failures = new LinkedBlockingDeque<>();
+        for (int i = 0; i < cores; ++i)
+        {
+            client.getExecutor().execute(() ->
+            {
+                for (int j = 0; j < iterations; ++j)
+                {
+                    try
+                    {
+                        ContentResponse response = client.newRequest(newURI())
+                                .method(HttpMethod.POST)
+                                .path(servletPath)
+                                .content(new InputStreamContentProvider(new ByteArrayInputStream(new byte[16 * 1024])
+                                {
+                                    @Override
+                                    public int read(byte[] b, int off, int len)
+                                    {
+                                        sleep(5);
+                                        return super.read(b, off, Math.min(len, 4242));
+                                    }
+                                }))
+                                .send();
+                        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+                        latch.countDown();
+                    }
+                    catch (Throwable x)
+                    {
+                        failures.offer(x);
+                    }
+                }
+            });
+        }
+
+        Assert.assertTrue(latch.await(30, TimeUnit.SECONDS));
+        Assert.assertTrue(failures.isEmpty());
+    }
+
+    private class Listener implements ReadListener, WriteListener
+    {
+        private final Executor executor = Executors.newFixedThreadPool(32);
+        private final CompletableFuture<?> inputComplete = new CompletableFuture<>();
+        private final CompletableFuture<?> outputComplete = new CompletableFuture<>();
+        private final AtomicBoolean responseWritten = new AtomicBoolean();
+        private final AsyncContext asyncContext;
+        private final HttpServletResponse response;
+        private final ServletInputStream input;
+        private final ServletOutputStream output;
+
+        public Listener(AsyncContext asyncContext) throws IOException
+        {
+            this.asyncContext = asyncContext;
+            this.response = (HttpServletResponse)asyncContext.getResponse();
+            this.input = asyncContext.getRequest().getInputStream();
+            this.output = response.getOutputStream();
+            CompletableFuture.allOf(inputComplete, outputComplete)
+                    .whenComplete((ignoredResult, ignoredThrowable) -> asyncContext.complete());
+            // Dispatch setting the write listener to another thread.
+            executor.execute(() -> output.setWriteListener(this));
+        }
+
+        @Override
+        public void onDataAvailable() throws IOException
+        {
+            byte[] buffer = new byte[16 * 1024];
+            while (input.isReady())
+            {
+                if (input.read(buffer) < 0)
+                    return;
+            }
+        }
+
+        @Override
+        public void onAllDataRead() throws IOException
+        {
+            inputComplete.complete(null);
+        }
+
+        @Override
+        public void onWritePossible() throws IOException
+        {
+            // Dispatch OWP to another thread.
+            executor.execute(() ->
+            {
+                while (output.isReady())
+                {
+                    if (responseWritten.compareAndSet(false, true))
+                    {
+                        try
+                        {
+                            response.setStatus(HttpServletResponse.SC_OK);
+                            response.setContentType("text/plain;charset=utf-8");
+                            output.write("Hello world".getBytes());
+                        }
+                        catch (IOException x)
+                        {
+                            throw new UncheckedIOException(x);
+                        }
+                    }
+                    else
+                    {
+                        outputComplete.complete(null);
+                        return;
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void onError(Throwable t)
+        {
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            asyncContext.complete();
+        }
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncRequestContentTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncRequestContentTest.java
new file mode 100644
index 0000000..0904147
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncRequestContentTest.java
@@ -0,0 +1,191 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.client.util.InputStreamContentProvider;
+import org.eclipse.jetty.client.util.OutputStreamContentProvider;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class AsyncRequestContentTest extends AbstractTest
+{
+    public AsyncRequestContentTest(Transport transport)
+    {
+        super(transport);
+    }
+
+    @Test
+    public void testEmptyDeferredContent() throws Exception
+    {
+        start(new ConsumeInputHandler());
+
+        DeferredContentProvider contentProvider = new DeferredContentProvider();
+        CountDownLatch latch = new CountDownLatch(1);
+        client.POST(newURI())
+                .content(contentProvider)
+                .send(result ->
+                {
+                    if (result.isSucceeded() &&
+                            result.getResponse().getStatus() == HttpStatus.OK_200)
+                        latch.countDown();
+                });
+        contentProvider.close();
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testDeferredContent() throws Exception
+    {
+        start(new ConsumeInputHandler());
+
+        DeferredContentProvider contentProvider = new DeferredContentProvider();
+        CountDownLatch latch = new CountDownLatch(1);
+        client.POST(newURI())
+                .content(contentProvider)
+                .send(result ->
+                {
+                    if (result.isSucceeded() &&
+                            result.getResponse().getStatus() == HttpStatus.OK_200)
+                        latch.countDown();
+                });
+        contentProvider.offer(ByteBuffer.wrap(new byte[1]));
+        contentProvider.close();
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testEmptyInputStream() throws Exception
+    {
+        start(new ConsumeInputHandler());
+
+        InputStreamContentProvider contentProvider =
+                new InputStreamContentProvider(new ByteArrayInputStream(new byte[0]));
+        CountDownLatch latch = new CountDownLatch(1);
+        client.POST(newURI())
+                .content(contentProvider)
+                .send(result ->
+                {
+                    if (result.isSucceeded() &&
+                            result.getResponse().getStatus() == HttpStatus.OK_200)
+                        latch.countDown();
+                });
+        contentProvider.close();
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testInputStream() throws Exception
+    {
+        start(new ConsumeInputHandler());
+
+        InputStreamContentProvider contentProvider =
+                new InputStreamContentProvider(new ByteArrayInputStream(new byte[1]));
+        CountDownLatch latch = new CountDownLatch(1);
+        client.POST(newURI())
+                .content(contentProvider)
+                .send(result ->
+                {
+                    if (result.isSucceeded() &&
+                            result.getResponse().getStatus() == HttpStatus.OK_200)
+                        latch.countDown();
+                });
+        contentProvider.close();
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testEmptyOutputStream() throws Exception
+    {
+        start(new ConsumeInputHandler());
+
+        OutputStreamContentProvider contentProvider = new OutputStreamContentProvider();
+        CountDownLatch latch = new CountDownLatch(1);
+        client.POST(newURI())
+                .content(contentProvider)
+                .send(result ->
+                {
+                    if (result.isSucceeded() &&
+                            result.getResponse().getStatus() == HttpStatus.OK_200)
+                        latch.countDown();
+                });
+        contentProvider.close();
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testOutputStream() throws Exception
+    {
+        start(new ConsumeInputHandler());
+
+        OutputStreamContentProvider contentProvider = new OutputStreamContentProvider();
+        CountDownLatch latch = new CountDownLatch(1);
+        client.POST(newURI())
+                .content(contentProvider)
+                .send(result ->
+                {
+                    if (result.isSucceeded() &&
+                            result.getResponse().getStatus() == HttpStatus.OK_200)
+                        latch.countDown();
+                });
+        OutputStream output = contentProvider.getOutputStream();
+        output.write(new byte[1]);
+        output.flush();
+        contentProvider.close();
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    private static class ConsumeInputHandler extends AbstractHandler
+    {
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            ServletInputStream input = request.getInputStream();
+            while (true)
+            {
+                int read = input.read();
+                if (read < 0)
+                    break;
+            }
+            response.setStatus(HttpStatus.OK_200);
+        }
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ConnectionStatisticsTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ConnectionStatisticsTest.java
new file mode 100644
index 0000000..8a51b17
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ConnectionStatisticsTest.java
@@ -0,0 +1,101 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.util.BytesContentProvider;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.ConnectionStatistics;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.IO;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class ConnectionStatisticsTest extends AbstractTest
+{
+    public ConnectionStatisticsTest(Transport transport)
+    {
+        super(transport);
+    }
+
+    @Test
+    public void testConnectionStatistics() throws Exception
+    {
+        Assume.assumeThat(transport, Matchers.isOneOf(Transport.H2C, Transport.H2));
+
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+
+        ConnectionStatistics serverStats = new ConnectionStatistics();
+        connector.addBean(serverStats);
+        serverStats.start();
+
+        ConnectionStatistics clientStats = new ConnectionStatistics();
+        client.addBean(clientStats);
+        clientStats.start();
+
+        byte[] content = new byte[3072];
+        long contentLength = content.length;
+        ContentResponse response = client.newRequest(newURI())
+                .content(new BytesContentProvider(content))
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertThat(response.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
+
+        // The bytes have already arrived, but give time to
+        // the server to finish to run the response logic.
+        Thread.sleep(1000);
+
+        // Close all connections.
+        stop();
+
+        // Give some time to process the stop event.
+        Thread.sleep(1000);
+
+        Assert.assertThat(serverStats.getConnectionsMax(), Matchers.greaterThan(0L));
+        Assert.assertThat(serverStats.getReceivedBytes(), Matchers.greaterThan(contentLength));
+        Assert.assertThat(serverStats.getSentBytes(), Matchers.greaterThan(contentLength));
+        Assert.assertThat(serverStats.getReceivedMessages(), Matchers.greaterThan(0L));
+        Assert.assertThat(serverStats.getSentMessages(), Matchers.greaterThan(0L));
+
+        Assert.assertThat(clientStats.getConnectionsMax(), Matchers.greaterThan(0L));
+        Assert.assertThat(clientStats.getReceivedBytes(), Matchers.greaterThan(contentLength));
+        Assert.assertThat(clientStats.getSentBytes(), Matchers.greaterThan(contentLength));
+        Assert.assertThat(clientStats.getReceivedMessages(), Matchers.greaterThan(0L));
+        Assert.assertThat(clientStats.getSentMessages(), Matchers.greaterThan(0L));
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/EmptyServerHandler.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/EmptyServerHandler.java
new file mode 100644
index 0000000..eab1eed
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/EmptyServerHandler.java
@@ -0,0 +1,37 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+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.handler.AbstractHandler;
+
+public class EmptyServerHandler extends AbstractHandler
+{
+    @Override
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        baseRequest.setHandled(true);
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpChannelAssociationTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpChannelAssociationTest.java
new file mode 100644
index 0000000..c6add50
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpChannelAssociationTest.java
@@ -0,0 +1,207 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpClientTransport;
+import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.client.http.HttpChannelOverHTTP;
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
+import org.eclipse.jetty.fcgi.client.http.HttpChannelOverFCGI;
+import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
+import org.eclipse.jetty.fcgi.client.http.HttpConnectionOverFCGI;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.client.http.HttpChannelOverHTTP2;
+import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
+import org.eclipse.jetty.http2.client.http.HttpConnectionOverHTTP2;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HttpChannelAssociationTest extends AbstractTest
+{
+    public HttpChannelAssociationTest(Transport transport)
+    {
+        super(transport);
+    }
+
+    @Test
+    public void testAssociationFailedAbortsRequest() throws Exception
+    {
+        startServer(new EmptyServerHandler());
+
+        client = new HttpClient(newHttpClientTransport(transport, exchange -> false), sslContextFactory);
+        QueuedThreadPool clientThreads = new QueuedThreadPool();
+        clientThreads.setName("client");
+        client.setExecutor(clientThreads);
+        client.start();
+
+        CountDownLatch latch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .send(result ->
+                {
+                    if (result.isFailed())
+                        latch.countDown();
+                });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testIdleTimeoutJustBeforeAssociation() throws Exception
+    {
+        startServer(new EmptyServerHandler());
+
+        long idleTimeout = 1000;
+        client = new HttpClient(newHttpClientTransport(transport, exchange ->
+        {
+            // We idle timeout just before the association,
+            // we must be able to send the request successfully.
+            sleep(2 * idleTimeout);
+            return true;
+        }), sslContextFactory);
+        QueuedThreadPool clientThreads = new QueuedThreadPool();
+        clientThreads.setName("client");
+        client.setExecutor(clientThreads);
+        client.setIdleTimeout(idleTimeout);
+        client.start();
+
+        CountDownLatch latch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .send(result ->
+                {
+                    if (result.isSucceeded())
+                        latch.countDown();
+                });
+
+        Assert.assertTrue(latch.await(5 * idleTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    private HttpClientTransport newHttpClientTransport(Transport transport, Predicate<HttpExchange> code)
+    {
+        switch (transport)
+        {
+            case HTTP:
+            case HTTPS:
+            {
+                return new HttpClientTransportOverHTTP(1)
+                {
+                    @Override
+                    protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
+                    {
+                        return new HttpConnectionOverHTTP(endPoint, destination, promise)
+                        {
+                            @Override
+                            protected HttpChannelOverHTTP newHttpChannel()
+                            {
+                                return new HttpChannelOverHTTP(this)
+                                {
+                                    @Override
+                                    public boolean associate(HttpExchange exchange)
+                                    {
+                                        return code.test(exchange) && super.associate(exchange);
+                                    }
+                                };
+                            }
+                        };
+                    }
+                };
+            }
+            case H2C:
+            case H2:
+            {
+                HTTP2Client http2Client = new HTTP2Client();
+                http2Client.setSelectors(1);
+                return new HttpClientTransportOverHTTP2(http2Client)
+                {
+                    @Override
+                    protected HttpConnectionOverHTTP2 newHttpConnection(HttpDestination destination, Session session)
+                    {
+                        return new HttpConnectionOverHTTP2(destination, session)
+                        {
+                            @Override
+                            protected HttpChannelOverHTTP2 newHttpChannel(boolean push)
+                            {
+                                return new HttpChannelOverHTTP2(getHttpDestination(), this, getSession(), push)
+                                {
+                                    @Override
+                                    public boolean associate(HttpExchange exchange)
+                                    {
+                                        return code.test(exchange) && super.associate(exchange);
+                                    }
+                                };
+                            }
+                        };
+                    }
+                };
+            }
+            case FCGI:
+            {
+                return new HttpClientTransportOverFCGI(1, false, "")
+                {
+                    @Override
+                    protected HttpConnectionOverFCGI newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
+                    {
+                        return new HttpConnectionOverFCGI(endPoint, destination, promise, isMultiplexed())
+                        {
+                            @Override
+                            protected HttpChannelOverFCGI newHttpChannel(int id, org.eclipse.jetty.client.api.Request request)
+                            {
+                                return new HttpChannelOverFCGI(this, getFlusher(), id, request.getIdleTimeout())
+                                {
+                                    @Override
+                                    public boolean associate(HttpExchange exchange)
+                                    {
+                                        return code.test(exchange) && super.associate(exchange);
+                                    }
+                                };
+                            }
+                        };
+                    }
+                };
+            }
+            default:
+            {
+                throw new IllegalArgumentException();
+            }
+        }
+    }
+
+    private void sleep(long time)
+    {
+        try
+        {
+            Thread.sleep(time);
+        }
+        catch (InterruptedException x)
+        {
+            throw new RuntimeException(x);
+        }
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientConnectTimeoutTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientConnectTimeoutTest.java
new file mode 100644
index 0000000..0dcbad2
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientConnectTimeoutTest.java
@@ -0,0 +1,152 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jetty.client.api.Request;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Ignore;
+import org.junit.Test;
+
+// TODO: these tests seems to fail spuriously, figure out why.
+@Ignore
+public class HttpClientConnectTimeoutTest extends AbstractTest
+{
+    public HttpClientConnectTimeoutTest(Transport transport)
+    {
+        super(transport);
+    }
+
+    @Test
+    public void testConnectTimeout() throws Exception
+    {
+        final String host = "10.255.255.1";
+        final int port = 80;
+        int connectTimeout = 1000;
+        assumeConnectTimeout(host, port, connectTimeout);
+
+        start(new EmptyServerHandler());
+        client.stop();
+        client.setConnectTimeout(connectTimeout);
+        client.start();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Request request = client.newRequest(host, port);
+        request.send(result ->
+        {
+            if (result.isFailed())
+                latch.countDown();
+        });
+
+        Assert.assertTrue(latch.await(2 * connectTimeout, TimeUnit.MILLISECONDS));
+        Assert.assertNotNull(request.getAbortCause());
+    }
+
+    @Test
+    public void testConnectTimeoutIsCancelledByShorterRequestTimeout() throws Exception
+    {
+        String host = "10.255.255.1";
+        int port = 80;
+        int connectTimeout = 2000;
+        assumeConnectTimeout(host, port, connectTimeout);
+
+        start(new EmptyServerHandler());
+        client.stop();
+        client.setConnectTimeout(connectTimeout);
+        client.start();
+
+        final AtomicInteger completes = new AtomicInteger();
+        final CountDownLatch latch = new CountDownLatch(2);
+        Request request = client.newRequest(host, port);
+        request.timeout(connectTimeout / 2, TimeUnit.MILLISECONDS)
+                .send(result ->
+                {
+                    completes.incrementAndGet();
+                    latch.countDown();
+                });
+
+        Assert.assertFalse(latch.await(2 * connectTimeout, TimeUnit.MILLISECONDS));
+        Assert.assertEquals(1, completes.get());
+        Assert.assertNotNull(request.getAbortCause());
+    }
+
+    @Test
+    public void retryAfterConnectTimeout() throws Exception
+    {
+        final String host = "10.255.255.1";
+        final int port = 80;
+        int connectTimeout = 1000;
+        assumeConnectTimeout(host, port, connectTimeout);
+
+        start(new EmptyServerHandler());
+        client.stop();
+        client.setConnectTimeout(connectTimeout);
+        client.start();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Request request = client.newRequest(host, port);
+        request.send(result1 ->
+        {
+            if (result1.isFailed())
+            {
+                // Retry
+                client.newRequest(host, port).send(result2 ->
+                {
+                    if (result2.isFailed())
+                        latch.countDown();
+                });
+            }
+        });
+
+        Assert.assertTrue(latch.await(3 * connectTimeout, TimeUnit.MILLISECONDS));
+        Assert.assertNotNull(request.getAbortCause());
+    }
+
+    private void assumeConnectTimeout(String host, int port, int connectTimeout) throws IOException
+    {
+        try (Socket socket = new Socket())
+        {
+            // Try to connect to a private address in the 10.x.y.z range.
+            // These addresses are usually not routed, so an attempt to
+            // connect to them will hang the connection attempt, which is
+            // what we want to simulate in this test.
+            socket.connect(new InetSocketAddress(host, port), connectTimeout);
+            // Abort the test if we can connect.
+            Assume.assumeTrue(false);
+        }
+        catch (SocketTimeoutException x)
+        {
+            // Expected timeout during connect, continue the test.
+            Assume.assumeTrue(true);
+        }
+        catch (Throwable x)
+        {
+            // Abort if any other exception happens.
+            Assume.assumeTrue(false);
+        }
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientContinueTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientContinueTest.java
new file mode 100644
index 0000000..27a5947
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientContinueTest.java
@@ -0,0 +1,701 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.ContinueProtocolHandler;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.util.BufferingResponseListener;
+import org.eclipse.jetty.client.util.BytesContentProvider;
+import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpHeaderValue;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.annotation.Slow;
+import org.eclipse.jetty.util.IO;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class HttpClientContinueTest extends AbstractTest
+{
+    public HttpClientContinueTest(Transport transport)
+    {
+        // Skip FCGI for now.
+        super(transport == Transport.FCGI ? null : transport);
+    }
+
+    @Test
+    public void test_Expect100Continue_WithOneContent_Respond100Continue() throws Exception
+    {
+        test_Expect100Continue_Respond100Continue("data1".getBytes(StandardCharsets.UTF_8));
+    }
+
+    @Test
+    public void test_Expect100Continue_WithMultipleContents_Respond100Continue() throws Exception
+    {
+        test_Expect100Continue_Respond100Continue("data1".getBytes(StandardCharsets.UTF_8), "data2".getBytes(StandardCharsets.UTF_8), "data3".getBytes(StandardCharsets.UTF_8));
+    }
+
+    private void test_Expect100Continue_Respond100Continue(byte[]... contents) throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                // Send 100-Continue and copy the content back
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+
+        ContentResponse response = client.newRequest(newURI())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(new BytesContentProvider(contents))
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatus());
+
+        int index = 0;
+        byte[] responseContent = response.getContent();
+        for (byte[] content : contents)
+        {
+            for (byte b : content)
+            {
+                Assert.assertEquals(b, responseContent[index++]);
+            }
+        }
+    }
+
+    @Test
+    public void test_Expect100Continue_WithChunkedContent_Respond100Continue() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                // Send 100-Continue and copy the content back
+                ServletInputStream input = request.getInputStream();
+                // Make sure we chunk the response too
+                response.flushBuffer();
+                IO.copy(input, response.getOutputStream());
+            }
+        });
+
+        byte[] content1 = new byte[10240];
+        byte[] content2 = new byte[16384];
+        ContentResponse response = client.newRequest(newURI())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(new BytesContentProvider(content1, content2)
+                {
+                    @Override
+                    public long getLength()
+                    {
+                        return -1;
+                    }
+                })
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatus());
+
+        int index = 0;
+        byte[] responseContent = response.getContent();
+        for (byte b : content1)
+            Assert.assertEquals(b, responseContent[index++]);
+        for (byte b : content2)
+            Assert.assertEquals(b, responseContent[index++]);
+    }
+
+    @Test
+    public void test_Expect100Continue_WithContent_Respond417ExpectationFailed() throws Exception
+    {
+        test_Expect100Continue_WithContent_RespondError(417);
+    }
+
+    @Test
+    public void test_Expect100Continue_WithContent_Respond413RequestEntityTooLarge() throws Exception
+    {
+        test_Expect100Continue_WithContent_RespondError(413);
+    }
+
+    private void test_Expect100Continue_WithContent_RespondError(final int error) throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.sendError(error);
+            }
+        });
+
+        byte[] content1 = new byte[10240];
+        byte[] content2 = new byte[16384];
+        final CountDownLatch latch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(new BytesContentProvider(content1, content2))
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        Assert.assertTrue(result.isFailed());
+                        Assert.assertNotNull(result.getRequestFailure());
+                        Assert.assertNull(result.getResponseFailure());
+                        byte[] content = getContent();
+                        Assert.assertNotNull(content);
+                        Assert.assertTrue(content.length > 0);
+                        Assert.assertEquals(error, result.getResponse().getStatus());
+                        latch.countDown();
+                    }
+                });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void test_Expect100Continue_WithContent_WithRedirect() throws Exception
+    {
+        final String data = "success";
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                if (request.getRequestURI().endsWith("/done"))
+                {
+                    response.getOutputStream().print(data);
+                }
+                else
+                {
+                    // Send 100-Continue and consume the content
+                    IO.copy(request.getInputStream(), new ByteArrayOutputStream());
+                    // Send a redirect
+                    response.sendRedirect("/done");
+                }
+            }
+        });
+
+        byte[] content = new byte[10240];
+        final CountDownLatch latch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .method(HttpMethod.POST)
+                .path("/continue")
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(new BytesContentProvider(content))
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        Assert.assertFalse(result.isFailed());
+                        Assert.assertEquals(200, result.getResponse().getStatus());
+                        Assert.assertEquals(data, getContentAsString());
+                        latch.countDown();
+                    }
+                });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void test_Redirect_WithExpect100Continue_WithContent() throws Exception
+    {
+        // A request with Expect: 100-Continue cannot receive non-final responses like 3xx
+
+        final String data = "success";
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                if (request.getRequestURI().endsWith("/done"))
+                {
+                    // Send 100-Continue and consume the content
+                    IO.copy(request.getInputStream(), new ByteArrayOutputStream());
+                    response.getOutputStream().print(data);
+                }
+                else
+                {
+                    // Send a redirect
+                    response.sendRedirect("/done");
+                }
+            }
+        });
+
+        byte[] content = new byte[10240];
+        final CountDownLatch latch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .method(HttpMethod.POST)
+                .path("/redirect")
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(new BytesContentProvider(content))
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        Assert.assertTrue(result.isFailed());
+                        Assert.assertNotNull(result.getRequestFailure());
+                        Assert.assertNull(result.getResponseFailure());
+                        Assert.assertEquals(302, result.getResponse().getStatus());
+                        latch.countDown();
+                    }
+                });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Slow
+    @Test
+    public void test_Expect100Continue_WithContent_WithResponseFailure_Before100Continue() throws Exception
+    {
+        final long idleTimeout = 1000;
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                try
+                {
+                    TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
+                }
+                catch (InterruptedException x)
+                {
+                    throw new ServletException(x);
+                }
+            }
+        });
+
+        client.setIdleTimeout(idleTimeout);
+
+        byte[] content = new byte[1024];
+        final CountDownLatch latch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(new BytesContentProvider(content))
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        Assert.assertTrue(result.isFailed());
+                        Assert.assertNotNull(result.getRequestFailure());
+                        Assert.assertNotNull(result.getResponseFailure());
+                        latch.countDown();
+                    }
+                });
+
+        Assert.assertTrue(latch.await(3 * idleTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    @Slow
+    @Test
+    public void test_Expect100Continue_WithContent_WithResponseFailure_After100Continue() throws Exception
+    {
+        final long idleTimeout = 1000;
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                // Send 100-Continue and consume the content
+                IO.copy(request.getInputStream(), new ByteArrayOutputStream());
+                try
+                {
+                    TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
+                }
+                catch (InterruptedException x)
+                {
+                    throw new ServletException(x);
+                }
+            }
+        });
+
+        client.setIdleTimeout(idleTimeout);
+
+        byte[] content = new byte[1024];
+        final CountDownLatch latch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(new BytesContentProvider(content))
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        Assert.assertTrue(result.isFailed());
+                        Assert.assertNull(result.getRequestFailure());
+                        Assert.assertNotNull(result.getResponseFailure());
+                        latch.countDown();
+                    }
+                });
+
+        Assert.assertTrue(latch.await(3 * idleTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void test_Expect100Continue_WithContent_WithResponseFailure_During100Continue() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                // Send 100-Continue and consume the content
+                IO.copy(request.getInputStream(), new ByteArrayOutputStream());
+            }
+        });
+
+        client.getProtocolHandlers().clear();
+        client.getProtocolHandlers().put(new ContinueProtocolHandler()
+        {
+            @Override
+            public Response.Listener getResponseListener()
+            {
+                final Response.Listener listener = super.getResponseListener();
+                return new Response.Listener.Adapter()
+                {
+                    @Override
+                    public void onBegin(Response response)
+                    {
+                        response.abort(new Exception());
+                    }
+
+                    @Override
+                    public void onFailure(Response response, Throwable failure)
+                    {
+                        listener.onFailure(response, failure);
+                    }
+                };
+            }
+        });
+
+        byte[] content = new byte[1024];
+        final CountDownLatch latch = new CountDownLatch(1);
+        client.newRequest(newURI())
+        .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+        .content(new BytesContentProvider(content))
+        .send(new BufferingResponseListener()
+        {
+            @Override
+            public void onComplete(Result result)
+            {
+                Assert.assertTrue(result.isFailed());
+                Assert.assertNotNull(result.getRequestFailure());
+                Assert.assertNotNull(result.getResponseFailure());
+                latch.countDown();
+            }
+        });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Slow
+    @Test
+    public void test_Expect100Continue_WithDeferredContent_Respond100Continue() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                // Send 100-Continue and echo the content
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+
+        final byte[] chunk1 = new byte[]{0, 1, 2, 3};
+        final byte[] chunk2 = new byte[]{4, 5, 6, 7};
+        final byte[] data = new byte[chunk1.length + chunk2.length];
+        System.arraycopy(chunk1, 0, data, 0, chunk1.length);
+        System.arraycopy(chunk2, 0, data, chunk1.length, chunk2.length);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        DeferredContentProvider content = new DeferredContentProvider();
+        client.newRequest(newURI())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(content)
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        Assert.assertArrayEquals(data, getContent());
+                        latch.countDown();
+                    }
+                });
+
+        Thread.sleep(1000);
+
+        content.offer(ByteBuffer.wrap(chunk1));
+
+        Thread.sleep(1000);
+
+        content.offer(ByteBuffer.wrap(chunk2));
+        content.close();
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Slow
+    @Test
+    public void test_Expect100Continue_WithInitialAndDeferredContent_Respond100Continue() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                // Send 100-Continue and echo the content
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+
+        final byte[] chunk1 = new byte[]{0, 1, 2, 3};
+        final byte[] chunk2 = new byte[]{4, 5, 6, 7};
+        final byte[] data = new byte[chunk1.length + chunk2.length];
+        System.arraycopy(chunk1, 0, data, 0, chunk1.length);
+        System.arraycopy(chunk2, 0, data, chunk1.length, chunk2.length);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(chunk1));
+        client.newRequest(newURI())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(content)
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        Assert.assertArrayEquals(data, getContent());
+                        latch.countDown();
+                    }
+                });
+
+        Thread.sleep(1000);
+
+        content.offer(ByteBuffer.wrap(chunk2));
+        content.close();
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void test_Expect100Continue_WithConcurrentDeferredContent_Respond100Continue() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                // Send 100-Continue and echo the content
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+
+        final byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
+        final DeferredContentProvider content = new DeferredContentProvider();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .onRequestHeaders(request ->
+                {
+                    content.offer(ByteBuffer.wrap(data));
+                    content.close();
+                })
+                .content(content)
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        Assert.assertArrayEquals(data, getContent());
+                        latch.countDown();
+                    }
+                });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void test_Expect100Continue_WithInitialAndConcurrentDeferredContent_Respond100Continue() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                // Send 100-Continue and echo the content
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+
+        final byte[] chunk1 = new byte[]{0, 1, 2, 3};
+        final byte[] chunk2 = new byte[]{4, 5, 6};
+        final byte[] data = new byte[chunk1.length + chunk2.length];
+        System.arraycopy(chunk1, 0, data, 0, chunk1.length);
+        System.arraycopy(chunk2, 0, data, chunk1.length, chunk2.length);
+
+        final DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(chunk1));
+
+        client.getProtocolHandlers().put(new ContinueProtocolHandler()
+        {
+            @Override
+            public Response.Listener getResponseListener()
+            {
+                return new ContinueListener()
+                {
+                    @Override
+                    public void onHeaders(Response response)
+                    {
+                        super.onHeaders(response);
+                        content.offer(ByteBuffer.wrap(chunk2));
+                        content.close();
+                    }
+                };
+            }
+        });
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                .content(content)
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        Assert.assertArrayEquals(data, getContent());
+                        latch.countDown();
+                    }
+                });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void test_Expect100Continue_WithTwoResponsesInOneRead() throws Exception
+    {
+        Assume.assumeThat(transport, Matchers.isOneOf(Transport.HTTP, Transport.HTTPS));
+
+        // There is a chance that the server replies with the 100 Continue response
+        // and immediately after with the "normal" response, say a 200 OK.
+        // These may be read by the client in a single read, and must be handled correctly.
+
+        startClient();
+
+        try (ServerSocket server = new ServerSocket())
+        {
+            server.bind(new InetSocketAddress("localhost", 0));
+
+            final CountDownLatch latch = new CountDownLatch(1);
+            client.newRequest("localhost", server.getLocalPort())
+                    .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
+                    .content(new BytesContentProvider(new byte[]{0}))
+                    .send(result ->
+                    {
+                        Assert.assertTrue(result.toString(), result.isSucceeded());
+                        Assert.assertEquals(200, result.getResponse().getStatus());
+                        latch.countDown();
+                    });
+
+            try (Socket socket = server.accept())
+            {
+                // Read the request headers.
+                InputStream input = socket.getInputStream();
+                int crlfs = 0;
+                while (true)
+                {
+                    int read = input.read();
+                    if (read == '\r' || read == '\n')
+                        ++crlfs;
+                    else
+                        crlfs = 0;
+                    if (crlfs == 4)
+                        break;
+                }
+
+                OutputStream output = socket.getOutputStream();
+                String responses = "" +
+                        "HTTP/1.1 100 Continue\r\n" +
+                        "\r\n" +
+                        "HTTP/1.1 200 OK\r\n" +
+                        "Transfer-Encoding: chunked\r\n" +
+                        "\r\n" +
+                        "10\r\n" +
+                        "0123456789ABCDEF\r\n";
+                output.write(responses.getBytes(StandardCharsets.UTF_8));
+                output.flush();
+
+                Thread.sleep(1000);
+
+                String content = "" +
+                        "10\r\n" +
+                        "0123456789ABCDEF\r\n" +
+                        "0\r\n" +
+                        "\r\n";
+                output.write(content.getBytes(StandardCharsets.UTF_8));
+                output.flush();
+
+                Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+            }
+        }
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientIdleTimeoutTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientIdleTimeoutTest.java
new file mode 100644
index 0000000..052a4bc
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientIdleTimeoutTest.java
@@ -0,0 +1,152 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HttpClientIdleTimeoutTest extends AbstractTest
+{
+    private long idleTimeout = 1000;
+
+    public HttpClientIdleTimeoutTest(Transport transport)
+    {
+        super(transport);
+    }
+
+    @Test
+    public void testClientIdleTimeout() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                if (target.equals("/timeout"))
+                {
+                    AsyncContext asyncContext = request.startAsync();
+                    asyncContext.setTimeout(0);
+                }
+            }
+        });
+        client.stop();
+        client.setIdleTimeout(idleTimeout);
+        client.start();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .path("/timeout")
+                .send(result ->
+                {
+                    if (result.isFailed())
+                        latch.countDown();
+                });
+
+        Assert.assertTrue(latch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+
+        // Verify that after the timeout we can make another request.
+        ContentResponse response = client.newRequest(newURI()).send();
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    @Test
+    public void testRequestIdleTimeout() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                if (target.equals("/timeout"))
+                {
+                    AsyncContext asyncContext = request.startAsync();
+                    asyncContext.setTimeout(0);
+                }
+            }
+        });
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .path("/timeout")
+                .idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
+                .send(result ->
+                {
+                    if (result.isFailed())
+                        latch.countDown();
+                });
+
+        Assert.assertTrue(latch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+
+        // Verify that after the timeout we can make another request.
+        ContentResponse response = client.newRequest(newURI()).send();
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    @Test
+    public void testIdleClientIdleTimeout() throws Exception
+    {
+        start(new EmptyServerHandler());
+        client.stop();
+        client.setIdleTimeout(idleTimeout);
+        client.start();
+
+        // Make a first request to open a connection.
+        ContentResponse response = client.newRequest(newURI()).send();
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        // Let the connection idle timeout.
+        Thread.sleep(2 * idleTimeout);
+
+        // Verify that after the timeout we can make another request.
+        response = client.newRequest(newURI()).send();
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    @Test
+    public void testIdleServerIdleTimeout() throws Exception
+    {
+        start(new EmptyServerHandler());
+        connector.setIdleTimeout(idleTimeout);
+
+        ContentResponse response1 = client.newRequest(newURI()).send();
+        Assert.assertEquals(HttpStatus.OK_200, response1.getStatus());
+
+        // Let the server idle timeout.
+        Thread.sleep(2 * idleTimeout);
+
+        // Make sure we can make another request successfully.
+        ContentResponse response2 = client.newRequest(newURI()).send();
+        Assert.assertEquals(HttpStatus.OK_200, response2.getStatus());
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientLoadTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientLoadTest.java
new file mode 100644
index 0000000..99e65cb
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientLoadTest.java
@@ -0,0 +1,390 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.IntStream;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.ConnectionPool;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpClientTransport;
+import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.client.LeakTrackingConnectionPool;
+import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
+import org.eclipse.jetty.client.util.BytesContentProvider;
+import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
+import org.eclipse.jetty.fcgi.client.http.HttpDestinationOverFCGI;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.io.ArrayByteBufferPool;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.LeakTrackingByteBufferPool;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.LeakDetector;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Scheduler;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.junit.Assert.assertThat;
+
+public class HttpClientLoadTest extends AbstractTest
+{
+    private final Logger logger = Log.getLogger(HttpClientLoadTest.class);
+    private final AtomicLong requestCount = new AtomicLong();
+    private final AtomicLong connectionLeaks = new AtomicLong();
+
+    public HttpClientLoadTest(Transport transport)
+    {
+        super(transport);
+    }
+
+    @Override
+    protected ServerConnector newServerConnector(Server server)
+    {
+        int cores = Runtime.getRuntime().availableProcessors();
+        ByteBufferPool byteBufferPool = new ArrayByteBufferPool();
+        byteBufferPool = new LeakTrackingByteBufferPool(byteBufferPool);
+        return new ServerConnector(server, null, null, byteBufferPool,
+                1, Math.min(1, cores / 2), provideServerConnectionFactory(transport));
+    }
+
+    @Override
+    protected HttpClientTransport provideClientTransport(Transport transport)
+    {
+        switch (transport)
+        {
+            case HTTP:
+            case HTTPS:
+            {
+                return new HttpClientTransportOverHTTP(1)
+                {
+                    @Override
+                    public HttpDestination newHttpDestination(Origin origin)
+                    {
+                        return new HttpDestinationOverHTTP(getHttpClient(), origin)
+                        {
+                            @Override
+                            protected ConnectionPool newConnectionPool(HttpClient client)
+                            {
+                                return new LeakTrackingConnectionPool(this, client.getMaxConnectionsPerDestination(), this)
+                                {
+                                    @Override
+                                    protected void leaked(LeakDetector.LeakInfo leakInfo)
+                                    {
+                                        super.leaked(leakInfo);
+                                        connectionLeaks.incrementAndGet();
+                                    }
+                                };
+                            }
+                        };
+                    }
+                };
+            }
+            case FCGI:
+            {
+                return new HttpClientTransportOverFCGI(1, false, "")
+                {
+                    @Override
+                    public HttpDestination newHttpDestination(Origin origin)
+                    {
+                        return new HttpDestinationOverFCGI(getHttpClient(), origin)
+                        {
+                            @Override
+                            protected ConnectionPool newConnectionPool(HttpClient client)
+                            {
+                                return new LeakTrackingConnectionPool(this, client.getMaxConnectionsPerDestination(), this)
+                                {
+                                    @Override
+                                    protected void leaked(LeakDetector.LeakInfo leakInfo)
+                                    {
+                                        super.leaked(leakInfo);
+                                        connectionLeaks.incrementAndGet();
+                                    }
+                                };
+                            }
+                        };
+                    }
+                };
+            }
+            default:
+            {
+                return super.provideClientTransport(transport);
+            }
+        }
+    }
+
+    @Test
+    public void testIterative() throws Exception
+    {
+        start(new LoadHandler());
+
+        client.setByteBufferPool(new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()));
+        client.setMaxConnectionsPerDestination(32768);
+        client.setMaxRequestsQueuedPerDestination(1024 * 1024);
+
+        // At least 25k requests to warmup properly (use -XX:+PrintCompilation to verify JIT activity)
+        int runs = 1;
+        int iterations = 500;
+        for (int i = 0; i < runs; ++i)
+            run(iterations);
+
+        // Re-run after warmup
+        iterations = 5_000;
+        for (int i = 0; i < runs; ++i)
+            run(iterations);
+
+        System.gc();
+
+        ByteBufferPool byteBufferPool = connector.getByteBufferPool();
+        if (byteBufferPool instanceof LeakTrackingByteBufferPool)
+        {
+            LeakTrackingByteBufferPool serverBufferPool = (LeakTrackingByteBufferPool)byteBufferPool;
+            assertThat("Server BufferPool - leaked acquires", serverBufferPool.getLeakedAcquires(), Matchers.is(0L));
+            assertThat("Server BufferPool - leaked releases", serverBufferPool.getLeakedReleases(), Matchers.is(0L));
+            assertThat("Server BufferPool - unreleased", serverBufferPool.getLeakedResources(), Matchers.is(0L));
+        }
+
+        byteBufferPool = client.getByteBufferPool();
+        if (byteBufferPool instanceof LeakTrackingByteBufferPool)
+        {
+            LeakTrackingByteBufferPool clientBufferPool = (LeakTrackingByteBufferPool)byteBufferPool;
+            assertThat("Client BufferPool - leaked acquires", clientBufferPool.getLeakedAcquires(), Matchers.is(0L));
+            assertThat("Client BufferPool - leaked releases", clientBufferPool.getLeakedReleases(), Matchers.is(0L));
+            assertThat("Client BufferPool - unreleased", clientBufferPool.getLeakedResources(), Matchers.is(0L));
+        }
+
+        assertThat("Connection Leaks", connectionLeaks.get(), Matchers.is(0L));
+    }
+
+    @Test
+    public void testConcurrent() throws Exception
+    {
+        start(new LoadHandler());
+
+        client.setByteBufferPool(new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()));
+        client.setMaxConnectionsPerDestination(32768);
+        client.setMaxRequestsQueuedPerDestination(1024 * 1024);
+
+        int runs = 1;
+        int iterations = 256;
+        IntStream.range(0, 16).parallel().forEach(i ->
+                IntStream.range(0, runs).forEach(j ->
+                        run(iterations)));
+    }
+
+    private void run(int iterations)
+    {
+        CountDownLatch latch = new CountDownLatch(iterations);
+        List<String> failures = new ArrayList<>();
+
+        int factor = (logger.isDebugEnabled() ? 25 : 1) * 100;
+
+        // Dumps the state of the client if the test takes too long
+        final Thread testThread = Thread.currentThread();
+        Scheduler.Task task = client.getScheduler().schedule(() ->
+        {
+            logger.warn("Interrupting test, it is taking too long{}{}{}{}",
+                    System.lineSeparator(), server.dump(),
+                    System.lineSeparator(), client.dump());
+            testThread.interrupt();
+        }, iterations * factor, TimeUnit.MILLISECONDS);
+
+        long begin = System.nanoTime();
+        for (int i = 0; i < iterations; ++i)
+        {
+            test(latch, failures);
+//            test("http", "localhost", "GET", false, false, 64 * 1024, false, latch, failures);
+        }
+        Assert.assertTrue(await(latch, iterations, TimeUnit.SECONDS));
+        long end = System.nanoTime();
+        task.cancel();
+        long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin);
+        logger.info("{} requests in {} ms, {} req/s", iterations, elapsed, elapsed > 0 ? iterations * 1000 / elapsed : -1);
+
+        for (String failure : failures)
+            logger.info("FAILED: {}", failure);
+
+        Assert.assertTrue(failures.toString(), failures.isEmpty());
+    }
+
+    private void test(final CountDownLatch latch, final List<String> failures)
+    {
+        ThreadLocalRandom random = ThreadLocalRandom.current();
+        // Choose a random destination
+        String host = random.nextBoolean() ? "localhost" : "127.0.0.1";
+        // Choose a random method
+        HttpMethod method = random.nextBoolean() ? HttpMethod.GET : HttpMethod.POST;
+
+        boolean ssl = isTransportSecure();
+
+        // Choose randomly whether to close the connection on the client or on the server
+        boolean clientClose = false;
+        if (!ssl && random.nextInt(100) < 5)
+            clientClose = true;
+        boolean serverClose = false;
+        if (!ssl && random.nextInt(100) < 5)
+            serverClose = true;
+
+        int maxContentLength = 64 * 1024;
+        int contentLength = random.nextInt(maxContentLength) + 1;
+
+        test(getScheme(), host, method.asString(), clientClose, serverClose, contentLength, true, latch, failures);
+    }
+
+    private void test(String scheme, String host, String method, boolean clientClose, boolean serverClose, int contentLength, final boolean checkContentLength, final CountDownLatch latch, final List<String> failures)
+    {
+        long requestId = requestCount.incrementAndGet();
+        Request request = client.newRequest(host, connector.getLocalPort())
+                .scheme(scheme)
+                .path("/" + requestId)
+                .method(method);
+
+        if (clientClose)
+            request.header(HttpHeader.CONNECTION, "close");
+        else if (serverClose)
+            request.header("X-Close", "true");
+
+        switch (method)
+        {
+            case "GET":
+                request.header("X-Download", String.valueOf(contentLength));
+                break;
+            case "POST":
+                request.header("X-Upload", String.valueOf(contentLength));
+                request.content(new BytesContentProvider(new byte[contentLength]));
+                break;
+        }
+
+        final CountDownLatch requestLatch = new CountDownLatch(1);
+        request.send(new Response.Listener.Adapter()
+        {
+            private final AtomicInteger contentLength = new AtomicInteger();
+
+            @Override
+            public void onHeaders(Response response)
+            {
+                if (checkContentLength)
+                {
+                    String content = response.getHeaders().get("X-Content");
+                    if (content != null)
+                        contentLength.set(Integer.parseInt(content));
+                }
+            }
+
+            @Override
+            public void onContent(Response response, ByteBuffer content)
+            {
+                if (checkContentLength)
+                    contentLength.addAndGet(-content.remaining());
+            }
+
+            @Override
+            public void onComplete(Result result)
+            {
+                if (result.isFailed())
+                {
+                    result.getFailure().printStackTrace();
+                    failures.add("Result failed " + result);
+                }
+
+                if (checkContentLength && contentLength.get() != 0)
+                    failures.add("Content length mismatch " + contentLength);
+
+                requestLatch.countDown();
+                latch.countDown();
+            }
+        });
+        if (!await(requestLatch, 5, TimeUnit.SECONDS))
+        {
+            logger.warn("Request {} took too long{}{}{}{}", requestId,
+                    System.lineSeparator(), server.dump(),
+                    System.lineSeparator(), client.dump());
+        }
+    }
+
+    private boolean await(CountDownLatch latch, long time, TimeUnit unit)
+    {
+        try
+        {
+            return latch.await(time, unit);
+        }
+        catch (InterruptedException x)
+        {
+            throw new RuntimeException(x);
+        }
+    }
+
+    private class LoadHandler extends AbstractHandler
+    {
+        @Override
+        public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            String method = request.getMethod().toUpperCase(Locale.ENGLISH);
+            switch (method)
+            {
+                case "GET":
+                {
+                    int contentLength = request.getIntHeader("X-Download");
+                    if (contentLength > 0)
+                    {
+                        response.setHeader("X-Content", String.valueOf(contentLength));
+                        response.getOutputStream().write(new byte[contentLength]);
+                    }
+                    break;
+                }
+                case "POST":
+                {
+                    response.setHeader("X-Content", request.getHeader("X-Upload"));
+                    IO.copy(request.getInputStream(), response.getOutputStream());
+                    break;
+                }
+            }
+
+            if (Boolean.parseBoolean(request.getHeader("X-Close")))
+                response.setHeader("Connection", "close");
+
+            baseRequest.setHandled(true);
+        }
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientStreamTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientStreamTest.java
new file mode 100644
index 0000000..b1e3b91
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientStreamTest.java
@@ -0,0 +1,1194 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.util.BufferingResponseListener;
+import org.eclipse.jetty.client.util.BytesContentProvider;
+import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.client.util.InputStreamContentProvider;
+import org.eclipse.jetty.client.util.InputStreamResponseListener;
+import org.eclipse.jetty.client.util.OutputStreamContentProvider;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.annotation.Slow;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IO;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HttpClientStreamTest extends AbstractTest
+{
+    public HttpClientStreamTest(Transport transport)
+    {
+        super(transport);
+    }
+
+    @Test
+    public void testFileUpload() throws Exception
+    {
+        // Prepare a big file to upload
+        Path targetTestsDir = MavenTestingUtils.getTargetTestingDir().toPath();
+        Files.createDirectories(targetTestsDir);
+        Path upload = Paths.get(targetTestsDir.toString(), "http_client_upload.big");
+        try (OutputStream output = Files.newOutputStream(upload, StandardOpenOption.CREATE))
+        {
+            byte[] kb = new byte[1024];
+            for (int i = 0; i < 10 * 1024; ++i)
+                output.write(kb);
+        }
+
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.setStatus(200);
+                response.setContentLength(0);
+                response.flushBuffer();
+
+                InputStream in = request.getInputStream();
+                byte[] buffer = new byte[1024];
+                while (true)
+                {
+                    int read = in.read(buffer);
+                    if (read < 0)
+                        break;
+                }
+            }
+        });
+
+        final AtomicLong requestTime = new AtomicLong();
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .file(upload)
+                .onRequestSuccess(request -> requestTime.set(System.nanoTime()))
+                .timeout(30, TimeUnit.SECONDS)
+                .send();
+        long responseTime = System.nanoTime();
+
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertTrue(requestTime.get() <= responseTime);
+
+        // Give some time to the server to consume the request content
+        // This is just to avoid exception traces in the test output
+        Thread.sleep(1000);
+    }
+
+    @Test
+    public void testDownload() throws Exception
+    {
+        final byte[] data = new byte[128 * 1024];
+        byte value = 1;
+        Arrays.fill(data, value);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.getOutputStream().write(data);
+            }
+        });
+
+        InputStreamResponseListener listener = new InputStreamResponseListener();
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .send(listener);
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatus());
+
+        InputStream input = listener.getInputStream();
+        Assert.assertNotNull(input);
+
+        int length = 0;
+        while (input.read() == value)
+        {
+            if (length % 100 == 0)
+                Thread.sleep(1);
+            ++length;
+        }
+
+        Assert.assertEquals(data.length, length);
+
+        Result result = listener.await(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(result);
+        Assert.assertFalse(result.isFailed());
+        Assert.assertSame(response, result.getResponse());
+    }
+
+    @Test
+    public void testDownloadOfUTF8Content() throws Exception
+    {
+        final byte[] data = new byte[]{(byte)0xC3, (byte)0xA8}; // UTF-8 representation of &egrave;
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.getOutputStream().write(data);
+            }
+        });
+
+        InputStreamResponseListener listener = new InputStreamResponseListener();
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .send(listener);
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatus());
+
+        InputStream input = listener.getInputStream();
+        Assert.assertNotNull(input);
+
+        for (byte b : data)
+        {
+            int read = input.read();
+            Assert.assertTrue(read >= 0);
+            Assert.assertEquals(b & 0xFF, read);
+        }
+
+        Assert.assertEquals(-1, input.read());
+
+        Result result = listener.await(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(result);
+        Assert.assertFalse(result.isFailed());
+        Assert.assertSame(response, result.getResponse());
+    }
+
+    @Test
+    public void testDownloadWithFailure() throws Exception
+    {
+        final byte[] data = new byte[64 * 1024];
+        byte value = 1;
+        Arrays.fill(data, value);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                // Say we want to send this much...
+                response.setContentLength(2 * data.length);
+                // ...but write only half...
+                response.getOutputStream().write(data);
+                // ...then shutdown output
+                baseRequest.getHttpChannel().getEndPoint().shutdownOutput();
+            }
+        });
+
+        InputStreamResponseListener listener = new InputStreamResponseListener();
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .send(listener);
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatus());
+
+        InputStream input = listener.getInputStream();
+        Assert.assertNotNull(input);
+
+        int length = 0;
+        try
+        {
+            length = 0;
+            while (input.read() == value)
+            {
+                if (length % 100 == 0)
+                    Thread.sleep(1);
+                ++length;
+            }
+            Assert.fail();
+        }
+        catch (IOException x)
+        {
+            // Expected.
+        }
+
+        Assert.assertThat(length, Matchers.lessThanOrEqualTo(data.length));
+
+        Result result = listener.await(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(result);
+        Assert.assertTrue(result.isFailed());
+    }
+
+    @Test(expected = AsynchronousCloseException.class)
+    public void testInputStreamResponseListenerClosedBeforeReading() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+
+        InputStreamResponseListener listener = new InputStreamResponseListener();
+        InputStream stream = listener.getInputStream();
+        // Close the stream immediately.
+        stream.close();
+
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .content(new BytesContentProvider(new byte[]{0, 1, 2, 3}))
+                .send(listener);
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(200, response.getStatus());
+
+        stream.read(); // Throws
+    }
+
+    @Test(expected = AsynchronousCloseException.class)
+    public void testInputStreamResponseListenerClosedBeforeContent() throws Exception
+    {
+        AtomicReference<AsyncContext> contextRef = new AtomicReference<>();
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                contextRef.set(request.startAsync());
+                response.flushBuffer();
+            }
+        });
+
+        CountDownLatch latch = new CountDownLatch(1);
+        InputStreamResponseListener listener = new InputStreamResponseListener()
+        {
+            @Override
+            public void onContent(Response response, ByteBuffer content, Callback callback)
+            {
+                super.onContent(response, content, new Callback()
+                {
+                    @Override
+                    public void failed(Throwable x)
+                    {
+                        latch.countDown();
+                        callback.failed(x);
+                    }
+                });
+            }
+        };
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .send(listener);
+
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        InputStream input = listener.getInputStream();
+        input.close();
+
+        AsyncContext asyncContext = contextRef.get();
+        asyncContext.getResponse().getOutputStream().write(new byte[1024]);
+        asyncContext.complete();
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+
+        input.read(); // Throws
+    }
+
+    @Test
+    public void testInputStreamResponseListenerClosedWhileWaiting() throws Exception
+    {
+        byte[] chunk1 = new byte[]{0, 1};
+        byte[] chunk2 = new byte[]{2, 3};
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.setContentLength(chunk1.length + chunk2.length);
+                ServletOutputStream output = response.getOutputStream();
+                output.write(chunk1);
+                output.flush();
+                output.write(chunk2);
+            }
+        });
+
+        CountDownLatch failedLatch = new CountDownLatch(1);
+        CountDownLatch contentLatch = new CountDownLatch(1);
+        InputStreamResponseListener listener = new InputStreamResponseListener()
+        {
+            @Override
+            public void onContent(Response response, ByteBuffer content, Callback callback)
+            {
+                super.onContent(response, content, new Callback()
+                {
+                    @Override
+                    public void failed(Throwable x)
+                    {
+                        failedLatch.countDown();
+                        callback.failed(x);
+                    }
+                });
+                contentLatch.countDown();
+            }
+        };
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .send(listener);
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        // Wait until we get some content.
+        Assert.assertTrue(contentLatch.await(5, TimeUnit.SECONDS));
+
+        // Close the stream.
+        InputStream stream = listener.getInputStream();
+        stream.close();
+
+        // Make sure that the callback has been invoked.
+        Assert.assertTrue(failedLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testInputStreamResponseListenerFailedWhileWaiting() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                byte[] data = new byte[1024];
+                response.setContentLength(data.length);
+                ServletOutputStream output = response.getOutputStream();
+                output.write(data);
+            }
+        });
+
+        CountDownLatch failedLatch = new CountDownLatch(1);
+        CountDownLatch contentLatch = new CountDownLatch(1);
+        InputStreamResponseListener listener = new InputStreamResponseListener()
+        {
+            @Override
+            public void onContent(Response response, ByteBuffer content, Callback callback)
+            {
+                super.onContent(response, content, new Callback()
+                {
+                    @Override
+                    public void failed(Throwable x)
+                    {
+                        failedLatch.countDown();
+                        callback.failed(x);
+                    }
+                });
+                contentLatch.countDown();
+            }
+        };
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .send(listener);
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        // Wait until we get some content.
+        Assert.assertTrue(contentLatch.await(5, TimeUnit.SECONDS));
+
+        // Abort the response.
+        response.abort(new Exception());
+
+        // Make sure that the callback has been invoked.
+        Assert.assertTrue(failedLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testInputStreamResponseListenerFailedBeforeResponse() throws Exception
+    {
+        start(new EmptyServerHandler());
+        int port = connector.getLocalPort();
+        server.stop();
+
+        InputStreamResponseListener listener = new InputStreamResponseListener();
+        // Connect to the wrong port
+        client.newRequest("localhost", port)
+                .scheme(getScheme())
+                .send(listener);
+        Result result = listener.await(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(result);
+    }
+
+    @Test(expected = ExecutionException.class)
+    public void testInputStreamContentProviderThrowingWhileReading() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+
+        final byte[] data = new byte[]{0, 1, 2, 3};
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .content(new InputStreamContentProvider(new InputStream()
+                {
+                    private int index = 0;
+
+                    @Override
+                    public int read() throws IOException
+                    {
+                        // Will eventually throw ArrayIndexOutOfBounds
+                        return data[index++];
+                    }
+                }, data.length / 2))
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+    }
+
+    @Test(expected = AsynchronousCloseException.class)
+    public void testDownloadWithCloseBeforeContent() throws Exception
+    {
+        final byte[] data = new byte[128 * 1024];
+        byte value = 3;
+        Arrays.fill(data, value);
+        final CountDownLatch latch = new CountDownLatch(1);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.flushBuffer();
+
+                try
+                {
+                    Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+                }
+                catch (InterruptedException e)
+                {
+                    throw new InterruptedIOException();
+                }
+
+                response.getOutputStream().write(data);
+            }
+        });
+
+        InputStreamResponseListener listener = new InputStreamResponseListener();
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .send(listener);
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatus());
+
+        InputStream input = listener.getInputStream();
+        Assert.assertNotNull(input);
+        input.close();
+
+        latch.countDown();
+
+        input.read(); // Throws
+    }
+
+    @Test(expected = AsynchronousCloseException.class)
+    public void testDownloadWithCloseMiddleOfContent() throws Exception
+    {
+        final byte[] data1 = new byte[1024];
+        final byte[] data2 = new byte[1024];
+        final CountDownLatch latch = new CountDownLatch(1);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.getOutputStream().write(data1);
+                response.flushBuffer();
+
+                try
+                {
+                    Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+                }
+                catch (InterruptedException e)
+                {
+                    throw new InterruptedIOException();
+                }
+
+                response.getOutputStream().write(data2);
+            }
+        });
+
+        InputStreamResponseListener listener = new InputStreamResponseListener();
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .send(listener);
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatus());
+
+        InputStream input = listener.getInputStream();
+        Assert.assertNotNull(input);
+
+        for (byte datum1 : data1)
+            Assert.assertEquals(datum1, input.read());
+
+        input.close();
+
+        latch.countDown();
+
+        input.read(); // Throws
+    }
+
+    @Test
+    public void testDownloadWithCloseEndOfContent() throws Exception
+    {
+        final byte[] data = new byte[1024];
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.getOutputStream().write(data);
+                response.flushBuffer();
+            }
+        });
+
+        InputStreamResponseListener listener = new InputStreamResponseListener();
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .send(listener);
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatus());
+
+        InputStream input = listener.getInputStream();
+        Assert.assertNotNull(input);
+
+        for (byte datum : data)
+            Assert.assertEquals(datum, input.read());
+
+        // Read EOF
+        Assert.assertEquals(-1, input.read());
+
+        input.close();
+
+        // Must not throw
+        Assert.assertEquals(-1, input.read());
+    }
+
+    @Slow
+    @Test
+    public void testUploadWithDeferredContentProviderFromInputStream() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                IO.copy(request.getInputStream(), new ByteArrayOutputStream());
+            }
+        });
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        try (DeferredContentProvider content = new DeferredContentProvider())
+        {
+            client.newRequest("localhost", connector.getLocalPort())
+                    .scheme(getScheme())
+                    .content(content)
+                    .send(result ->
+                    {
+                        if (result.isSucceeded() && result.getResponse().getStatus() == 200)
+                            latch.countDown();
+                    });
+
+            // Make sure we provide the content *after* the request has been "sent".
+            Thread.sleep(1000);
+
+            try (ByteArrayInputStream input = new ByteArrayInputStream(new byte[1024]))
+            {
+                byte[] buffer = new byte[200];
+                int read;
+                while ((read = input.read(buffer)) >= 0)
+                    content.offer(ByteBuffer.wrap(buffer, 0, read));
+            }
+        }
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testUploadWithDeferredContentAvailableCallbacksNotifiedOnce() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                IO.copy(request.getInputStream(), new ByteArrayOutputStream());
+            }
+        });
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final AtomicInteger succeeds = new AtomicInteger();
+        try (DeferredContentProvider content = new DeferredContentProvider())
+        {
+            // Make the content immediately available.
+            content.offer(ByteBuffer.allocate(1024), new Callback()
+            {
+                @Override
+                public void succeeded()
+                {
+                    succeeds.incrementAndGet();
+                }
+            });
+
+            client.newRequest("localhost", connector.getLocalPort())
+                    .scheme(getScheme())
+                    .content(content)
+                    .send(result ->
+                    {
+                        if (result.isSucceeded() && result.getResponse().getStatus() == 200)
+                            latch.countDown();
+                    });
+        }
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        Assert.assertEquals(1, succeeds.get());
+    }
+
+    @Test
+    public void testUploadWithDeferredContentProviderRacingWithSend() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final byte[] data = new byte[512];
+        final DeferredContentProvider content = new DeferredContentProvider()
+        {
+            @Override
+            public void setListener(Listener listener)
+            {
+                super.setListener(listener);
+                // Simulate a concurrent call
+                offer(ByteBuffer.wrap(data));
+                close();
+            }
+        };
+
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .content(content)
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        if (result.isSucceeded() &&
+                                result.getResponse().getStatus() == 200 &&
+                                Arrays.equals(data, getContent()))
+                            latch.countDown();
+                    }
+                });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testUploadWithDeferredContentProviderRacingWithIterator() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final byte[] data = new byte[512];
+        final AtomicReference<DeferredContentProvider> contentRef = new AtomicReference<>();
+        final DeferredContentProvider content = new DeferredContentProvider()
+        {
+            @Override
+            public Iterator<ByteBuffer> iterator()
+            {
+                return new Iterator<ByteBuffer>()
+                {
+                    // Data for the deferred content iterator:
+                    // [0] => deferred
+                    // [1] => deferred
+                    // [2] => data
+                    private final byte[][] iteratorData = new byte[3][];
+                    private final AtomicInteger index = new AtomicInteger();
+
+                    {
+                        iteratorData[0] = null;
+                        iteratorData[1] = null;
+                        iteratorData[2] = data;
+                    }
+
+                    @Override
+                    public boolean hasNext()
+                    {
+                        return index.get() < iteratorData.length;
+                    }
+
+                    @Override
+                    public ByteBuffer next()
+                    {
+                        byte[] chunk = iteratorData[index.getAndIncrement()];
+                        ByteBuffer result = chunk == null ? null : ByteBuffer.wrap(chunk);
+                        if (index.get() < iteratorData.length)
+                        {
+                            contentRef.get().offer(result == null ? BufferUtil.EMPTY_BUFFER : result);
+                            contentRef.get().close();
+                        }
+                        return result;
+                    }
+
+                    @Override
+                    public void remove()
+                    {
+                    }
+                };
+            }
+        };
+        contentRef.set(content);
+
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .content(content)
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        if (result.isSucceeded() &&
+                                result.getResponse().getStatus() == 200 &&
+                                Arrays.equals(data, getContent()))
+                            latch.countDown();
+                    }
+                });
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testUploadWithOutputStream() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+
+        final byte[] data = new byte[512];
+        final CountDownLatch latch = new CountDownLatch(1);
+        OutputStreamContentProvider content = new OutputStreamContentProvider();
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .content(content)
+                .send(new BufferingResponseListener()
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        if (result.isSucceeded() &&
+                                result.getResponse().getStatus() == 200 &&
+                                Arrays.equals(data, getContent()))
+                            latch.countDown();
+                    }
+                });
+
+        // Make sure we provide the content *after* the request has been "sent".
+        Thread.sleep(1000);
+
+        try (OutputStream output = content.getOutputStream())
+        {
+            output.write(data);
+        }
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testBigUploadWithOutputStreamFromInputStream() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                IO.copy(request.getInputStream(), response.getOutputStream());
+            }
+        });
+
+        final byte[] data = new byte[16 * 1024 * 1024];
+        new Random().nextBytes(data);
+        final CountDownLatch latch = new CountDownLatch(1);
+        OutputStreamContentProvider content = new OutputStreamContentProvider();
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .content(content)
+                .send(new BufferingResponseListener(data.length)
+                {
+                    @Override
+                    public void onComplete(Result result)
+                    {
+                        Assert.assertTrue(result.isSucceeded());
+                        Assert.assertEquals(200, result.getResponse().getStatus());
+                        Assert.assertArrayEquals(data, getContent());
+                        latch.countDown();
+                    }
+                });
+
+        // Make sure we provide the content *after* the request has been "sent".
+        Thread.sleep(1000);
+
+        try (InputStream input = new ByteArrayInputStream(data); OutputStream output = content.getOutputStream())
+        {
+            byte[] buffer = new byte[1024];
+            while (true)
+            {
+                int read = input.read(buffer);
+                if (read < 0)
+                    break;
+                output.write(buffer, 0, read);
+            }
+        }
+
+        Assert.assertTrue(latch.await(30, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testUploadWithOutputStreamFailureToConnect() throws Exception
+    {
+        start(new EmptyServerHandler());
+
+        final byte[] data = new byte[512];
+        final CountDownLatch latch = new CountDownLatch(1);
+        OutputStreamContentProvider content = new OutputStreamContentProvider();
+        client.newRequest("0.0.0.1", connector.getLocalPort())
+                .scheme(getScheme())
+                .content(content)
+                .send(result ->
+                {
+                    if (result.isFailed())
+                        latch.countDown();
+                });
+
+        try (OutputStream output = content.getOutputStream())
+        {
+            output.write(data);
+            Assert.fail();
+        }
+        catch (IOException x)
+        {
+            // Expected
+        }
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testUploadWithDeferredContentProviderFailsMultipleOffers() throws Exception
+    {
+        start(new EmptyServerHandler());
+
+        final CountDownLatch failLatch = new CountDownLatch(2);
+        final Callback callback = new Callback()
+        {
+            @Override
+            public void failed(Throwable x)
+            {
+                failLatch.countDown();
+            }
+        };
+
+        final CountDownLatch completeLatch = new CountDownLatch(1);
+        final DeferredContentProvider content = new DeferredContentProvider();
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .content(content)
+                .onRequestBegin(request ->
+                {
+                    content.offer(ByteBuffer.wrap(new byte[256]), callback);
+                    content.offer(ByteBuffer.wrap(new byte[256]), callback);
+                    request.abort(new Exception("explicitly_thrown_by_test"));
+                })
+                .send(result ->
+                {
+                    if (result.isFailed())
+                        completeLatch.countDown();
+                });
+        Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(failLatch.await(5, TimeUnit.SECONDS));
+
+        // Make sure that adding more content results in the callback to be failed.
+        final CountDownLatch latch = new CountDownLatch(1);
+        content.offer(ByteBuffer.wrap(new byte[128]), new Callback()
+        {
+            @Override
+            public void failed(Throwable x)
+            {
+                latch.countDown();
+            }
+        });
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testUploadWithConnectFailureClosesStream() throws Exception
+    {
+        start(new EmptyServerHandler());
+
+        final CountDownLatch closeLatch = new CountDownLatch(1);
+        InputStream stream = new ByteArrayInputStream("test".getBytes(StandardCharsets.UTF_8))
+        {
+            @Override
+            public void close() throws IOException
+            {
+                super.close();
+                closeLatch.countDown();
+            }
+        };
+        InputStreamContentProvider content = new InputStreamContentProvider(stream);
+
+        final CountDownLatch completeLatch = new CountDownLatch(1);
+        client.newRequest("0.0.0.1", connector.getLocalPort())
+                .scheme(getScheme())
+                .content(content)
+                .send(result ->
+                {
+                    Assert.assertTrue(result.isFailed());
+                    completeLatch.countDown();
+                });
+
+        Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testUploadWithConcurrentServerCloseClosesStream() throws Exception
+    {
+        final CountDownLatch serverLatch = new CountDownLatch(1);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                AsyncContext asyncContext = request.startAsync();
+                asyncContext.setTimeout(0);
+                serverLatch.countDown();
+            }
+        });
+
+        final AtomicBoolean commit = new AtomicBoolean();
+        final CountDownLatch closeLatch = new CountDownLatch(1);
+        InputStream stream = new InputStream()
+        {
+            @Override
+            public int read() throws IOException
+            {
+                // This method will be called few times before
+                // the request is committed.
+                // We wait for the request to commit, and we
+                // wait for the request to reach the server,
+                // to be sure that the server endPoint has
+                // been created, before stopping the connector.
+
+                if (commit.get())
+                {
+                    try
+                    {
+                        Assert.assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
+                        connector.stop();
+                        return 0;
+                    }
+                    catch (Throwable x)
+                    {
+                        throw new IOException(x);
+                    }
+                }
+                else
+                {
+                    return connector.isStopped() ? -1 : 0;
+                }
+            }
+
+            @Override
+            public void close() throws IOException
+            {
+                super.close();
+                closeLatch.countDown();
+            }
+        };
+        InputStreamContentProvider provider = new InputStreamContentProvider(stream, 1);
+
+        final CountDownLatch completeLatch = new CountDownLatch(1);
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .content(provider)
+                .onRequestCommit(request -> commit.set(true))
+                .send(result ->
+                {
+                    Assert.assertTrue(result.isFailed());
+                    completeLatch.countDown();
+                });
+
+        Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testInputStreamResponseListenerBufferedRead() throws Exception
+    {
+        AtomicReference<AsyncContext> asyncContextRef = new AtomicReference<>();
+        CountDownLatch latch = new CountDownLatch(1);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                asyncContextRef.set(request.startAsync());
+                latch.countDown();
+            }
+        });
+
+        InputStreamResponseListener listener = new InputStreamResponseListener();
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .timeout(5, TimeUnit.SECONDS)
+                .send(listener);
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+
+        AsyncContext asyncContext = asyncContextRef.get();
+        Assert.assertNotNull(asyncContext);
+
+        Random random = new Random();
+
+        byte[] chunk = new byte[64];
+        random.nextBytes(chunk);
+        ServletOutputStream output = asyncContext.getResponse().getOutputStream();
+        output.write(chunk);
+        output.flush();
+
+        // Use a buffer larger than the data
+        // written to test that the read returns.
+        byte[] buffer = new byte[2 * chunk.length];
+        InputStream stream = listener.getInputStream();
+        int totalRead = 0;
+        while (totalRead < chunk.length)
+        {
+            int read = stream.read(buffer);
+            Assert.assertTrue(read > 0);
+            totalRead += read;
+        }
+
+        asyncContext.complete();
+
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testInputStreamResponseListenerWithRedirect() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                if (target.startsWith("/303"))
+                    response.sendRedirect("/200");
+            }
+        });
+
+        InputStreamResponseListener listener = new InputStreamResponseListener();
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .path("/303")
+                .followRedirects(true)
+                .send(listener);
+
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        Result result = listener.await(5, TimeUnit.SECONDS);
+        Assert.assertTrue(result.isSucceeded());
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java
new file mode 100644
index 0000000..8e218db
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java
@@ -0,0 +1,554 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.util.EnumSet;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.IntStream;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.util.BytesContentProvider;
+import org.eclipse.jetty.client.util.FutureResponseListener;
+import org.eclipse.jetty.client.util.InputStreamResponseListener;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http2.FlowControlStrategy;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class HttpClientTest extends AbstractTest
+{
+    public HttpClientTest(Transport transport)
+    {
+        super(transport);
+    }
+
+    @Test
+    public void testRequestWithoutResponseContent() throws Exception
+    {
+        final int status = HttpStatus.NO_CONTENT_204;
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.setStatus(status);
+            }
+        });
+
+        ContentResponse response = client.newRequest(newURI())
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(status, response.getStatus());
+        Assert.assertEquals(0, response.getContent().length);
+    }
+
+    @Test
+    public void testRequestWithSmallResponseContent() throws Exception
+    {
+        testRequestWithResponseContent(1024);
+    }
+
+    @Test
+    public void testRequestWithLargeResponseContent() throws Exception
+    {
+        testRequestWithResponseContent(1024 * 1024);
+    }
+
+    private void testRequestWithResponseContent(int length) throws Exception
+    {
+        final byte[] bytes = new byte[length];
+        new Random().nextBytes(bytes);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.setContentLength(length);
+                response.getOutputStream().write(bytes);
+            }
+        });
+
+        org.eclipse.jetty.client.api.Request request = client.newRequest(newURI());
+        FutureResponseListener listener = new FutureResponseListener(request, length);
+        request.timeout(10, TimeUnit.SECONDS).send(listener);
+        ContentResponse response = listener.get();
+
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertArrayEquals(bytes, response.getContent());
+    }
+
+    @Test
+    public void testRequestWithSmallResponseContentChunked() throws Exception
+    {
+        testRequestWithResponseContentChunked(512);
+    }
+
+    @Test
+    public void testRequestWithLargeResponseContentChunked() throws Exception
+    {
+        testRequestWithResponseContentChunked(512 * 512);
+    }
+
+    private void testRequestWithResponseContentChunked(int length) throws Exception
+    {
+        final byte[] chunk1 = new byte[length];
+        final byte[] chunk2 = new byte[length];
+        Random random = new Random();
+        random.nextBytes(chunk1);
+        random.nextBytes(chunk2);
+        byte[] bytes = new byte[chunk1.length + chunk2.length];
+        System.arraycopy(chunk1, 0, bytes, 0, chunk1.length);
+        System.arraycopy(chunk2, 0, bytes, chunk1.length, chunk2.length);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                ServletOutputStream output = response.getOutputStream();
+                output.write(chunk1);
+                output.flush();
+                output.write(chunk2);
+            }
+        });
+
+        org.eclipse.jetty.client.api.Request request = client.newRequest(newURI());
+        FutureResponseListener listener = new FutureResponseListener(request, 2 * length);
+        request.timeout(10, TimeUnit.SECONDS).send(listener);
+        ContentResponse response = listener.get();
+
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertArrayEquals(bytes, response.getContent());
+    }
+
+    @Test
+    public void testUploadZeroLengthWithoutResponseContent() throws Exception
+    {
+        testUploadWithoutResponseContent(0);
+    }
+
+    @Test
+    public void testUploadSmallWithoutResponseContent() throws Exception
+    {
+        testUploadWithoutResponseContent(1024);
+    }
+
+    @Test
+    public void testUploadLargeWithoutResponseContent() throws Exception
+    {
+        testUploadWithoutResponseContent(1024 * 1024);
+    }
+
+    private void testUploadWithoutResponseContent(int length) throws Exception
+    {
+        final byte[] bytes = new byte[length];
+        new Random().nextBytes(bytes);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                ServletInputStream input = request.getInputStream();
+                for (int i = 0; i < bytes.length; ++i)
+                    Assert.assertEquals(bytes[i] & 0xFF, input.read());
+                Assert.assertEquals(-1, input.read());
+            }
+        });
+
+        ContentResponse response = client.newRequest(newURI())
+                .method(HttpMethod.POST)
+                .content(new BytesContentProvider(bytes))
+                .timeout(15, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(0, response.getContent().length);
+    }
+
+    @Test
+    public void testClientManyWritesSlowServer() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+
+                long sleep = 1024;
+                long total = 0;
+                ServletInputStream input = request.getInputStream();
+                byte[] buffer = new byte[1024];
+                while (true)
+                {
+                    int read = input.read(buffer);
+                    if (read < 0)
+                        break;
+                    total += read;
+                    if (total >= sleep)
+                    {
+                        sleep(250);
+                        sleep += 256;
+                    }
+                }
+
+                response.getOutputStream().print(total);
+            }
+        });
+
+        int chunks = 256;
+        int chunkSize = 16;
+        byte[][] bytes = IntStream.range(0, chunks).mapToObj(x -> new byte[chunkSize]).toArray(byte[][]::new);
+        BytesContentProvider contentProvider = new BytesContentProvider("application/octet-stream", bytes);
+        ContentResponse response = client.newRequest(newURI())
+                .method(HttpMethod.POST)
+                .content(contentProvider)
+                .timeout(15, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+        Assert.assertEquals(chunks * chunkSize, Integer.parseInt(response.getContentAsString()));
+    }
+
+    @Test
+    public void testRequestAfterFailedRequest() throws Exception
+    {
+        int length = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.getOutputStream().write(new byte[length]);
+            }
+        });
+
+        // Make a request with a large enough response buffer.
+        org.eclipse.jetty.client.api.Request request = client.newRequest(newURI());
+        FutureResponseListener listener = new FutureResponseListener(request, length);
+        request.send(listener);
+        ContentResponse response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(response.getStatus(), 200);
+
+        // Make a request with a small response buffer, should fail.
+        try
+        {
+            request = client.newRequest(newURI());
+            listener = new FutureResponseListener(request, length / 10);
+            request.send(listener);
+            listener.get(5, TimeUnit.SECONDS);
+            Assert.fail();
+        }
+        catch (ExecutionException x)
+        {
+            Assert.assertThat(x.getMessage(),Matchers.containsString("Buffering capacity exceeded"));
+        }
+
+        // Verify that we can make another request.
+        request = client.newRequest(newURI());
+        listener = new FutureResponseListener(request, length);
+        request.send(listener);
+        response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(response.getStatus(), 200);
+    }
+
+    @Test(expected = ExecutionException.class)
+    public void testClientCannotValidateServerCertificate() throws Exception
+    {
+        // Only run this test for transports over TLS.
+        Assume.assumeTrue(EnumSet.of(Transport.HTTPS, Transport.H2).contains(transport));
+
+        startServer(new EmptyServerHandler());
+
+        // Use a default SslContextFactory, requests should fail because the server certificate is unknown.
+        client = newHttpClient(provideClientTransport(transport), new SslContextFactory());
+        QueuedThreadPool clientThreads = new QueuedThreadPool();
+        clientThreads.setName("client");
+        client.setExecutor(clientThreads);
+        client.start();
+
+        client.newRequest(newURI())
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+    }
+
+    @Test
+    public void testOPTIONS() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                Assert.assertTrue(HttpMethod.OPTIONS.is(request.getMethod()));
+                Assert.assertEquals("*", target);
+                Assert.assertEquals("*", request.getPathInfo());
+            }
+        });
+
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .method(HttpMethod.OPTIONS)
+                .path("*")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    @Test
+    public void testOPTIONSWithRelativeRedirect() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                if ("*".equals(target))
+                {
+                    // Be nasty and send a relative redirect.
+                    // Code 303 will change the method to GET.
+                    response.setStatus(HttpStatus.SEE_OTHER_303);
+                    response.setHeader("Location", "/");
+                }
+            }
+        });
+
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .method(HttpMethod.OPTIONS)
+                .path("*")
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    @Test
+    public void testDownloadWithInputStreamResponseListener() throws Exception
+    {
+        String content = "hello world";
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                response.getOutputStream().print(content);
+            }
+        });
+
+        CountDownLatch latch = new CountDownLatch(1);
+        InputStreamResponseListener listener = new InputStreamResponseListener();
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .onResponseSuccess(response -> latch.countDown())
+                .send(listener);
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertEquals(200, response.getStatus());
+
+        // Response cannot succeed until we read the content.
+        Assert.assertFalse(latch.await(500, TimeUnit.MILLISECONDS));
+
+        InputStream input = listener.getInputStream();
+        Assert.assertEquals(content, IO.toString(input));
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testConnectionListener() throws Exception
+    {
+        start(new EmptyServerHandler());
+
+        CountDownLatch openLatch = new CountDownLatch(1);
+        CountDownLatch closeLatch = new CountDownLatch(1);
+        client.addBean(new org.eclipse.jetty.io.Connection.Listener()
+        {
+            @Override
+            public void onOpened(org.eclipse.jetty.io.Connection connection)
+            {
+                openLatch.countDown();
+            }
+
+            @Override
+            public void onClosed(org.eclipse.jetty.io.Connection connection)
+            {
+                closeLatch.countDown();
+            }
+        });
+
+        long idleTimeout = 1000;
+        client.setIdleTimeout(idleTimeout);
+
+        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+        Assert.assertTrue(openLatch.await(1, TimeUnit.SECONDS));
+
+        Thread.sleep(2 * idleTimeout);
+        Assert.assertTrue(closeLatch.await(1, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testAsyncResponseContentBackPressure() throws Exception
+    {
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                // Large write to generate multiple DATA frames.
+                response.getOutputStream().write(new byte[256 * 1024]);
+            }
+        });
+
+        CountDownLatch completeLatch = new CountDownLatch(1);
+        AtomicInteger counter = new AtomicInteger();
+        AtomicReference<Callback> callbackRef = new AtomicReference<>();
+        AtomicReference<CountDownLatch> latchRef = new AtomicReference<>(new CountDownLatch(1));
+        client.newRequest("localhost", connector.getLocalPort())
+                .scheme(getScheme())
+                .onResponseContentAsync((response, content, callback) ->
+                {
+                    if (counter.incrementAndGet() == 1)
+                    {
+                        callbackRef.set(callback);
+                        latchRef.get().countDown();
+                    }
+                    else
+                    {
+                        callback.succeeded();
+                    }
+                })
+                .send(result -> completeLatch.countDown());
+
+        Assert.assertTrue(latchRef.get().await(5, TimeUnit.SECONDS));
+        // Wait some time to verify that back pressure is applied correctly.
+        Thread.sleep(1000);
+        Assert.assertEquals(1, counter.get());
+        callbackRef.get().succeeded();
+
+        Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testResponseWithContentCompleteListenerInvokedOnce() throws Exception
+    {
+        start(new EmptyServerHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                super.handle(target, baseRequest, request, response);
+                response.getWriter().write("Jetty");
+            }
+        });
+
+        AtomicInteger completes = new AtomicInteger();
+        client.newRequest(newURI())
+                .send(result -> completes.incrementAndGet());
+
+        sleep(1000);
+
+        Assert.assertEquals(1, completes.get());
+    }
+
+    @Test
+    public void testHEADResponds200() throws Exception
+    {
+        testHEAD(servletPath, HttpStatus.OK_200);
+    }
+
+    @Test
+    public void testHEADResponds404() throws Exception
+    {
+        testHEAD("/notMapped", HttpStatus.NOT_FOUND_404);
+    }
+
+    private void testHEAD(String path, int status) throws Exception
+    {
+        byte[] data = new byte[1024];
+        new Random().nextBytes(data);
+        start(new HttpServlet()
+        {
+            @Override
+            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+            {
+                response.getOutputStream().write(data);
+            }
+        });
+
+        ContentResponse response = client.newRequest(newURI())
+                .method(HttpMethod.HEAD)
+                .path(path)
+                .send();
+
+        Assert.assertEquals(status, response.getStatus());
+        Assert.assertEquals(0, response.getContent().length);
+    }
+
+    private void sleep(long time) throws IOException
+    {
+        try
+        {
+            Thread.sleep(time);
+        }
+        catch (InterruptedException x)
+        {
+            throw new InterruptedIOException();
+        }
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ServerTimeoutsTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ServerTimeoutsTest.java
new file mode 100644
index 0000000..528effa
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ServerTimeoutsTest.java
@@ -0,0 +1,732 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.client;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http2.server.AbstractHTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ServerTimeoutsTest extends AbstractTest
+{
+    public ServerTimeoutsTest(Transport transport)
+    {
+        // Skip FCGI for now, not much interested in its server-side behavior.
+        super(transport == Transport.FCGI ? null : transport);
+    }
+
+    private void setServerIdleTimeout(long idleTimeout)
+    {
+        AbstractHTTP2ServerConnectionFactory h2 = connector.getConnectionFactory(AbstractHTTP2ServerConnectionFactory.class);
+        if (h2 != null)
+            h2.setStreamIdleTimeout(idleTimeout);
+        else
+            connector.setIdleTimeout(idleTimeout);
+    }
+
+    @Test
+    public void testDelayedDispatchRequestWithDelayedFirstContentIdleTimeoutFires() throws Exception
+    {
+        httpConfig.setDelayDispatchUntilContent(true);
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                handlerLatch.countDown();
+            }
+        });
+        long idleTimeout = 2500;
+        setServerIdleTimeout(idleTimeout);
+
+        CountDownLatch resultLatch = new CountDownLatch(1);
+        client.POST(newURI())
+                .content(new DeferredContentProvider())
+                .send(result ->
+                {
+                    if (result.isFailed())
+                        resultLatch.countDown();
+                });
+
+        // We did not send the content, the request was not
+        // dispatched, the server should have idle timed out.
+        Assert.assertFalse(handlerLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+        Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testNoBlockingTimeoutBlockingReadIdleTimeoutFires() throws Exception
+    {
+        httpConfig.setBlockingTimeout(-1);
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new BlockingReadHandler(handlerLatch));
+        long idleTimeout = 2500;
+        setServerIdleTimeout(idleTimeout);
+
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            DeferredContentProvider contentProvider = new DeferredContentProvider(ByteBuffer.allocate(1));
+            CountDownLatch resultLatch = new CountDownLatch(1);
+            client.POST(newURI())
+                    .content(contentProvider)
+                    .send(result ->
+                    {
+                        if (result.getResponse().getStatus() == HttpStatus.INTERNAL_SERVER_ERROR_500)
+                            resultLatch.countDown();
+                    });
+
+            // Blocking read should timeout.
+            Assert.assertTrue(handlerLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+            // Complete the request.
+            contentProvider.close();
+            Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testBlockingTimeoutSmallerThanIdleTimeoutBlockingReadBlockingTimeoutFires() throws Exception
+    {
+        long blockingTimeout = 2500;
+        httpConfig.setBlockingTimeout(blockingTimeout);
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new BlockingReadHandler(handlerLatch));
+        long idleTimeout = 3 * blockingTimeout;
+        setServerIdleTimeout(idleTimeout);
+
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            DeferredContentProvider contentProvider = new DeferredContentProvider(ByteBuffer.allocate(1));
+            CountDownLatch resultLatch = new CountDownLatch(1);
+            client.POST(newURI())
+                    .content(contentProvider)
+                    .send(result ->
+                    {
+                        if (result.getResponse().getStatus() == HttpStatus.INTERNAL_SERVER_ERROR_500)
+                            resultLatch.countDown();
+                    });
+
+            // Blocking read should timeout.
+            Assert.assertTrue(handlerLatch.await(2 * blockingTimeout, TimeUnit.MILLISECONDS));
+            // Complete the request.
+            contentProvider.close();
+            Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testBlockingTimeoutLargerThanIdleTimeoutBlockingReadIdleTimeoutFires() throws Exception
+    {
+        long idleTimeout = 2500;
+        long blockingTimeout = 3 * idleTimeout;
+        httpConfig.setBlockingTimeout(blockingTimeout);
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new BlockingReadHandler(handlerLatch));
+        setServerIdleTimeout(idleTimeout);
+
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            DeferredContentProvider contentProvider = new DeferredContentProvider(ByteBuffer.allocate(1));
+            CountDownLatch resultLatch = new CountDownLatch(1);
+            client.POST(newURI())
+                    .content(contentProvider)
+                    .send(result ->
+                    {
+                        if (result.getResponse().getStatus() == HttpStatus.INTERNAL_SERVER_ERROR_500)
+                            resultLatch.countDown();
+                    });
+
+            // Blocking read should timeout.
+            Assert.assertTrue(handlerLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+            // Complete the request.
+            contentProvider.close();
+            Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testNoBlockingTimeoutBlockingWriteIdleTimeoutFires() throws Exception
+    {
+        httpConfig.setBlockingTimeout(-1);
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new BlockingWriteHandler(handlerLatch));
+        long idleTimeout = 2500;
+        setServerIdleTimeout(idleTimeout);
+
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            BlockingQueue<Callback> callbacks = new LinkedBlockingQueue<>();
+            CountDownLatch resultLatch = new CountDownLatch(1);
+            client.newRequest(newURI())
+                    .onResponseContentAsync((response, content, callback) ->
+                    {
+                        // Do not succeed the callback so the server will block writing.
+                        callbacks.offer(callback);
+                    })
+                    .send(result ->
+                    {
+                        if (result.isFailed())
+                            resultLatch.countDown();
+                    });
+
+            // Blocking write should timeout.
+            Assert.assertTrue(handlerLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+            // After the server stopped sending, consume on the client to read the early EOF.
+            while (true)
+            {
+                Callback callback = callbacks.poll(1, TimeUnit.SECONDS);
+                if (callback == null)
+                    break;
+                callback.succeeded();
+            }
+            Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testBlockingTimeoutSmallerThanIdleTimeoutBlockingWriteBlockingTimeoutFires() throws Exception
+    {
+        long blockingTimeout = 2500;
+        httpConfig.setBlockingTimeout(blockingTimeout);
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new BlockingWriteHandler(handlerLatch));
+        long idleTimeout = 3 * blockingTimeout;
+        setServerIdleTimeout(idleTimeout);
+
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            BlockingQueue<Callback> callbacks = new LinkedBlockingQueue<>();
+            CountDownLatch resultLatch = new CountDownLatch(1);
+            client.newRequest(newURI())
+                    .onResponseContentAsync((response, content, callback) ->
+                    {
+                        // Do not succeed the callback so the server will block writing.
+                        callbacks.offer(callback);
+                    })
+                    .send(result ->
+                    {
+                        if (result.isFailed())
+                            resultLatch.countDown();
+                    });
+
+            // Blocking write should timeout.
+            Assert.assertTrue(handlerLatch.await(2 * blockingTimeout, TimeUnit.MILLISECONDS));
+            // After the server stopped sending, consume on the client to read the early EOF.
+            while (true)
+            {
+                Callback callback = callbacks.poll(1, TimeUnit.SECONDS);
+                if (callback == null)
+                    break;
+                callback.succeeded();
+            }
+            Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testBlockingTimeoutLargerThanIdleTimeoutBlockingWriteIdleTimeoutFires() throws Exception
+    {
+        long idleTimeout = 2500;
+        long blockingTimeout = 3 * idleTimeout;
+        httpConfig.setBlockingTimeout(blockingTimeout);
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new BlockingWriteHandler(handlerLatch));
+        setServerIdleTimeout(idleTimeout);
+
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            BlockingQueue<Callback> callbacks = new LinkedBlockingQueue<>();
+            CountDownLatch resultLatch = new CountDownLatch(1);
+            client.newRequest(newURI())
+                    .onResponseContentAsync((response, content, callback) ->
+                    {
+                        // Do not succeed the callback so the server will block writing.
+                        callbacks.offer(callback);
+                    })
+                    .send(result ->
+                    {
+                        if (result.isFailed())
+                            resultLatch.countDown();
+                    });
+
+            // Blocking read should timeout.
+            Assert.assertTrue(handlerLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+            // After the server stopped sending, consume on the client to read the early EOF.
+            while (true)
+            {
+                Callback callback = callbacks.poll(1, TimeUnit.SECONDS);
+                if (callback == null)
+                    break;
+                callback.succeeded();
+            }
+            Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testBlockingTimeoutWithSlowRead() throws Exception
+    {
+        long idleTimeout = 2500;
+        long blockingTimeout = 2 * idleTimeout;
+        httpConfig.setBlockingTimeout(blockingTimeout);
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                try
+                {
+                    baseRequest.setHandled(true);
+                    ServletInputStream input = request.getInputStream();
+                    while (true)
+                    {
+                        int read = input.read();
+                        if (read < 0)
+                            break;
+                    }
+                }
+                catch (IOException x)
+                {
+                    handlerLatch.countDown();
+                    throw x;
+                }
+            }
+        });
+        setServerIdleTimeout(idleTimeout);
+
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            DeferredContentProvider contentProvider = new DeferredContentProvider();
+            CountDownLatch resultLatch = new CountDownLatch(1);
+            client.newRequest(newURI())
+                    .content(contentProvider)
+                    .send(result ->
+                    {
+                        // Result may fail to send the whole request body,
+                        // but the response has arrived successfully.
+                        if (result.getResponse().getStatus() == HttpStatus.INTERNAL_SERVER_ERROR_500)
+                            resultLatch.countDown();
+                    });
+
+            // The writes should be slow but not trigger the idle timeout.
+            long period = idleTimeout / 2;
+            long writes = 2 * (blockingTimeout / period);
+            for (long i = 0; i < writes; ++i)
+            {
+                contentProvider.offer(ByteBuffer.allocate(1));
+                Thread.sleep(period);
+            }
+            contentProvider.close();
+
+            // Blocking read should timeout.
+            Assert.assertTrue(handlerLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+            Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testAsyncReadIdleTimeoutFires() throws Exception
+    {
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                AsyncContext asyncContext = request.startAsync();
+                asyncContext.setTimeout(0);
+                ServletInputStream input = request.getInputStream();
+                input.setReadListener(new ReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        Assert.assertEquals(0, input.read());
+                        Assert.assertFalse(input.isReady());
+                    }
+
+                    @Override
+                    public void onAllDataRead() throws IOException
+                    {
+                    }
+
+                    @Override
+                    public void onError(Throwable failure)
+                    {
+                        if (failure instanceof TimeoutException)
+                        {
+                            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
+                            asyncContext.complete();
+                            handlerLatch.countDown();
+                        }
+                    }
+                });
+            }
+        });
+        long idleTimeout = 2500;
+        setServerIdleTimeout(idleTimeout);
+
+        DeferredContentProvider contentProvider = new DeferredContentProvider(ByteBuffer.allocate(1));
+        CountDownLatch resultLatch = new CountDownLatch(1);
+        client.POST(newURI())
+                .content(contentProvider)
+                .send(result ->
+                {
+                    if (result.getResponse().getStatus() == HttpStatus.INTERNAL_SERVER_ERROR_500)
+                        resultLatch.countDown();
+                });
+
+        // Async read should timeout.
+        Assert.assertTrue(handlerLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+        // Complete the request.
+        contentProvider.close();
+        Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testAsyncWriteIdleTimeoutFires() throws Exception
+    {
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                AsyncContext asyncContext = request.startAsync();
+                asyncContext.setTimeout(0);
+                ServletOutputStream output = response.getOutputStream();
+                output.setWriteListener(new WriteListener()
+                {
+                    @Override
+                    public void onWritePossible() throws IOException
+                    {
+                        output.write(new byte[64 * 1024 * 1024]);
+                    }
+
+                    @Override
+                    public void onError(Throwable failure)
+                    {
+                        if (failure instanceof TimeoutException)
+                        {
+                            asyncContext.complete();
+                            handlerLatch.countDown();
+                        }
+                    }
+                });
+            }
+        });
+        long idleTimeout = 2500;
+        setServerIdleTimeout(idleTimeout);
+
+        BlockingQueue<Callback> callbacks = new LinkedBlockingQueue<>();
+        CountDownLatch resultLatch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .onResponseContentAsync((response, content, callback) ->
+                {
+                    // Do not succeed the callback so the server will block writing.
+                    callbacks.offer(callback);
+                })
+                .send(result ->
+                {
+                    if (result.isFailed())
+                        resultLatch.countDown();
+                });
+
+        // Async write should timeout.
+        Assert.assertTrue(handlerLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+        // After the server stopped sending, consume on the client to read the early EOF.
+        while (true)
+        {
+            Callback callback = callbacks.poll(1, TimeUnit.SECONDS);
+            if (callback == null)
+                break;
+            callback.succeeded();
+        }
+        Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testBlockingReadWithMinimumDataRateBelowLimit() throws Exception
+    {
+        int bytesPerSecond = 20;
+        httpConfig.setMinRequestDataRate(bytesPerSecond);
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                try
+                {
+                    baseRequest.setHandled(true);
+                    ServletInputStream input = request.getInputStream();
+                    while (true)
+                    {
+                        int read = input.read();
+                        if (read < 0)
+                            break;
+                    }
+                }
+                catch (BadMessageException x)
+                {
+                    handlerLatch.countDown();
+                    throw x;
+                }
+            }
+        });
+
+        DeferredContentProvider contentProvider = new DeferredContentProvider();
+        CountDownLatch resultLatch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .content(contentProvider)
+                .send(result ->
+                {
+                    if (result.getResponse().getStatus() == HttpStatus.REQUEST_TIMEOUT_408)
+                        resultLatch.countDown();
+                });
+
+        for (int i = 0; i < 3; ++i)
+        {
+            contentProvider.offer(ByteBuffer.allocate(bytesPerSecond / 2));
+            Thread.sleep(2500);
+        }
+        contentProvider.close();
+
+        // Request should timeout.
+        Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testBlockingReadWithMinimumDataRateAboveLimit() throws Exception
+    {
+        int bytesPerSecond = 20;
+        httpConfig.setMinRequestDataRate(bytesPerSecond);
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                ServletInputStream input = request.getInputStream();
+                while (true)
+                {
+                    int read = input.read();
+                    if (read < 0)
+                        break;
+                }
+                handlerLatch.countDown();
+            }
+        });
+
+        DeferredContentProvider contentProvider = new DeferredContentProvider();
+        CountDownLatch resultLatch = new CountDownLatch(1);
+        client.newRequest(newURI())
+                .content(contentProvider)
+                .send(result ->
+                {
+                    if (result.getResponse().getStatus() == HttpStatus.OK_200)
+                        resultLatch.countDown();
+                });
+
+        for (int i = 0; i < 3; ++i)
+        {
+            contentProvider.offer(ByteBuffer.allocate(bytesPerSecond * 2));
+            Thread.sleep(2500);
+        }
+        contentProvider.close();
+
+        Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testBlockingReadHttpIdleTimeoutOverridesIdleTimeout() throws Exception
+    {
+        long httpIdleTimeout = 2500;
+        long idleTimeout = 3 * httpIdleTimeout;
+        httpConfig.setIdleTimeout(httpIdleTimeout);
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new BlockingReadHandler(handlerLatch));
+        setServerIdleTimeout(idleTimeout);
+
+        try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
+        {
+            DeferredContentProvider contentProvider = new DeferredContentProvider(ByteBuffer.allocate(1));
+            CountDownLatch resultLatch = new CountDownLatch(1);
+            client.POST(newURI())
+                    .content(contentProvider)
+                    .send(result ->
+                    {
+                        if (result.getResponse().getStatus() == HttpStatus.INTERNAL_SERVER_ERROR_500)
+                            resultLatch.countDown();
+                    });
+
+            // Blocking read should timeout.
+            Assert.assertTrue(handlerLatch.await(2 * httpIdleTimeout, TimeUnit.MILLISECONDS));
+            // Complete the request.
+            contentProvider.close();
+            Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    @Test
+    public void testAsyncReadHttpIdleTimeoutOverridesIdleTimeout() throws Exception
+    {
+        long httpIdleTimeout = 2500;
+        long idleTimeout = 3 * httpIdleTimeout;
+        httpConfig.setIdleTimeout(httpIdleTimeout);
+        CountDownLatch handlerLatch = new CountDownLatch(1);
+        start(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                baseRequest.setHandled(true);
+                AsyncContext asyncContext = request.startAsync();
+                asyncContext.setTimeout(0);
+                ServletInputStream input = request.getInputStream();
+                input.setReadListener(new ReadListener()
+                {
+                    @Override
+                    public void onDataAvailable() throws IOException
+                    {
+                        Assert.assertEquals(0, input.read());
+                        Assert.assertFalse(input.isReady());
+                    }
+
+                    @Override
+                    public void onAllDataRead() throws IOException
+                    {
+                    }
+
+                    @Override
+                    public void onError(Throwable failure)
+                    {
+                        if (failure instanceof TimeoutException)
+                        {
+                            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
+                            asyncContext.complete();
+                            handlerLatch.countDown();
+                        }
+                    }
+                });
+            }
+        });
+        setServerIdleTimeout(idleTimeout);
+
+        DeferredContentProvider contentProvider = new DeferredContentProvider(ByteBuffer.allocate(1));
+        CountDownLatch resultLatch = new CountDownLatch(1);
+        client.POST(newURI())
+                .content(contentProvider)
+                .send(result ->
+                {
+                    if (result.getResponse().getStatus() == HttpStatus.INTERNAL_SERVER_ERROR_500)
+                        resultLatch.countDown();
+                });
+
+        // Async read should timeout.
+        Assert.assertTrue(handlerLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
+        // Complete the request.
+        contentProvider.close();
+        Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    private static class BlockingReadHandler extends AbstractHandler
+    {
+        private final CountDownLatch handlerLatch;
+
+        public BlockingReadHandler(CountDownLatch handlerLatch)
+        {
+            this.handlerLatch = handlerLatch;
+        }
+
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            ServletInputStream input = request.getInputStream();
+            Assert.assertEquals(0, input.read());
+            try
+            {
+                input.read();
+            }
+            catch (IOException x)
+            {
+                handlerLatch.countDown();
+                throw x;
+            }
+        }
+    }
+
+    private static class BlockingWriteHandler extends AbstractHandler
+    {
+        private final CountDownLatch handlerLatch;
+
+        private BlockingWriteHandler(CountDownLatch handlerLatch)
+        {
+            this.handlerLatch = handlerLatch;
+        }
+
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            ServletOutputStream output = response.getOutputStream();
+            try
+            {
+                output.write(new byte[64 * 1024 * 1024]);
+            }
+            catch (IOException x)
+            {
+                handlerLatch.countDown();
+                throw x;
+            }
+        }
+    }
+}
diff --git a/tests/test-http-client-transport/src/test/resources/jetty-logging.properties b/tests/test-http-client-transport/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..607ee96
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/resources/jetty-logging.properties
@@ -0,0 +1,6 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.LEVEL=DEBUG
+#org.eclipse.jetty.client.LEVEL=DEBUG
+#org.eclipse.jetty.http2.LEVEL=DEBUG
+org.eclipse.jetty.http2.hpack.LEVEL=INFO
+#org.eclipse.jetty.http2.client.LEVEL=DEBUG
diff --git a/jetty-spdy/spdy-http-client-transport/src/test/resources/keystore.jks b/tests/test-http-client-transport/src/test/resources/keystore.jks
similarity index 100%
copy from jetty-spdy/spdy-http-client-transport/src/test/resources/keystore.jks
copy to tests/test-http-client-transport/src/test/resources/keystore.jks
Binary files differ
diff --git a/jetty-spdy/spdy-http-client-transport/src/test/resources/truststore.jks b/tests/test-http-client-transport/src/test/resources/truststore.jks
similarity index 100%
rename from jetty-spdy/spdy-http-client-transport/src/test/resources/truststore.jks
rename to tests/test-http-client-transport/src/test/resources/truststore.jks
Binary files differ
diff --git a/tests/test-integration/pom.xml b/tests/test-integration/pom.xml
index 377d36e..f4eac23 100644
--- a/tests/test-integration/pom.xml
+++ b/tests/test-integration/pom.xml
@@ -1,26 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>test-integration</artifactId>
@@ -31,6 +14,8 @@
     <test-wars-dir>${project.build.directory}/test-wars</test-wars-dir>
     <test-libs-dir>${project.build.directory}/test-libs</test-libs-dir>
     <test-dist-dir>${project.build.directory}/test-dist</test-dist-dir>
+    <bundle-symbolic-name>${project.groupId}.integrations</bundle-symbolic-name>
+
   </properties>
   <build>
     <plugins>
@@ -117,13 +102,20 @@
       <type>pom</type>
     </dependency>
     <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-http</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.eclipse.jetty.toolchain</groupId>
       <artifactId>jetty-test-helper</artifactId>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-jsp</artifactId>
+      <artifactId>apache-jsp</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
@@ -133,5 +125,17 @@
       <type>war</type>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.alpn</groupId>
+      <artifactId>alpn-api</artifactId>
+      <version>${alpn.api.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-alpn-server</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/DefaultHandlerTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/DefaultHandlerTest.java
index 6d90bbe..2504728 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/DefaultHandlerTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/DefaultHandlerTest.java
@@ -18,7 +18,8 @@
 
 package org.eclipse.jetty.test;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
@@ -26,7 +27,6 @@
 import java.net.Socket;
 import java.net.URL;
 import java.net.URLConnection;
-import java.util.List;
 
 import org.eclipse.jetty.http.HttpScheme;
 import org.eclipse.jetty.http.HttpStatus;
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/HttpInputIntegrationTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/HttpInputIntegrationTest.java
new file mode 100644
index 0000000..087ee5e
--- /dev/null
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/HttpInputIntegrationTest.java
@@ -0,0 +1,718 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.test;
+
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.net.ssl.SSLSocket;
+import javax.servlet.AsyncContext;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpChannelState;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.LocalConnector.LocalEndPoint;
+import org.eclipse.jetty.server.NetworkConnector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.hamcrest.Matchers;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class HttpInputIntegrationTest
+{
+    
+    enum Mode { BLOCKING, ASYNC_DISPATCHED, ASYNC_OTHER_DISPATCHED, ASYNC_OTHER_WAIT }
+    public final static String EOF = "__EOF__";
+    public final static String DELAY = "__DELAY__";
+    public final static String ABORT = "__ABORT__";
+    
+    private static Server __server;
+    private static HttpConfiguration __config;
+    private static HttpConfiguration __sslConfig;
+    private static SslContextFactory __sslContextFactory;
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        __config = new HttpConfiguration();
+        
+        __server = new Server();
+        LocalConnector local=new LocalConnector(__server,new HttpConnectionFactory(__config));
+        local.setIdleTimeout(4000);
+        __server.addConnector(local);
+        
+        ServerConnector http = new ServerConnector(__server,new HttpConnectionFactory(__config),new HTTP2CServerConnectionFactory(__config));
+        http.setIdleTimeout(4000);
+        __server.addConnector(http);
+        
+
+        // SSL Context Factory for HTTPS and HTTP/2
+        String jetty_distro = System.getProperty("jetty.distro","../../jetty-distribution/target/distribution");
+        __sslContextFactory = new SslContextFactory();
+        __sslContextFactory.setKeyStorePath(jetty_distro + "/../../../jetty-server/src/test/config/etc/keystore");
+        __sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
+        __sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
+
+        // HTTPS Configuration
+        __sslConfig = new HttpConfiguration(__config);
+        __sslConfig.addCustomizer(new SecureRequestCustomizer());
+
+        // HTTP/1 Connection Factory
+        HttpConnectionFactory h1=new HttpConnectionFactory(__sslConfig);
+        /* TODO
+        // HTTP/2 Connection Factory
+        HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(__sslConfig);
+        
+        NegotiatingServerConnectionFactory.checkProtocolNegotiationAvailable();
+        ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+        alpn.setDefaultProtocol(h1.getProtocol());
+        */
+        
+        // SSL Connection Factory
+        SslConnectionFactory ssl = new SslConnectionFactory(__sslContextFactory,h1.getProtocol() /*TODO alpn.getProtocol()*/);
+        
+        // HTTP/2 Connector
+        ServerConnector http2 = new ServerConnector(__server,ssl,/*TODO alpn,h2,*/ h1);
+        http2.setIdleTimeout(4000);
+        __server.addConnector(http2);
+        
+        
+        ServletContextHandler context = new ServletContextHandler(__server,"/ctx");
+        ServletHolder holder = new ServletHolder(new TestServlet());
+        holder.setAsyncSupported(true);
+        context.addServlet(holder,"/*");
+        
+        
+        
+        __server.start();
+    }
+    
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        __server.stop();
+    }
+    
+    interface TestClient
+    {        
+        /* ------------------------------------------------------------ */
+        /**
+         * @param uri The URI to test, typically /ctx/test?mode=THE_MODE
+         * @param delayMs the delay in MS to use.
+         * @param delayInFrame If null, send the request with no delays, if FALSE then send with delays between frames, if TRUE send with delays within frames
+         * @param contentLength The content length header to send.
+         * @param content The content to send, with each string to be converted to a chunk or a frame 
+         * @return The response received in HTTP/1 format
+         * @throws Exception
+         */
+        String send(String uri,int delayMs, Boolean delayInFrame, int contentLength, List<String> content) throws Exception; 
+    }
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data()
+    {
+        List<Object[]> tests = new ArrayList<>();
+        
+
+        // TODO other client types!
+        // test with the following clients/protocols:
+        //   + Local
+        //   + HTTP/1
+        //   + SSL + HTTP/1
+        //   + HTTP/2
+        //   + SSL + HTTP/2
+        //   + FASTCGI
+        for (Class<? extends TestClient> client : new Class[]{LocalClient.class,H1Client.class,H1SClient.class})
+        {
+
+            // test async actions that are run:
+            //   + By a thread in a container callback
+            //   + By another thread while a container callback is active
+            //   + By another thread while no container callback is active
+            for (Mode mode: Mode.values())
+            {
+
+                // test servlet dispatch with:
+                //   + Delayed dispatch on
+                //   + Delayed dispatch off
+                for (Boolean dispatch : new Boolean[]{false,true})
+                {
+                    // test send with 
+                    //   + No delays between frames
+                    //   + Delays between frames
+                    //   + Delays within frames!
+                    for (Boolean delayWithinFrame : new Boolean[]{null,false,true})
+                    {
+                        // test content 
+                        // + unknown length + EOF
+                        // + unknown length + content + EOF
+                        // + unknown length + content + content + EOF
+                        
+                        // + known length + EOF
+                        // + known length + content + EOF
+                        // + known length + content + content + EOF
+                        
+                        tests.add(new Object[]{tests.size(),client,mode,dispatch,delayWithinFrame,200,0,-1,new String[]{}});
+                        tests.add(new Object[]{tests.size(),client,mode,dispatch,delayWithinFrame,200,8,-1,new String[]{"content0"}});
+                        tests.add(new Object[]{tests.size(),client,mode,dispatch,delayWithinFrame,200,16,-1,new String[]{"content0","CONTENT1"}});
+                        
+                        tests.add(new Object[]{tests.size(),client,mode,dispatch,delayWithinFrame,200,0,0,new String[]{}});
+                        tests.add(new Object[]{tests.size(),client,mode,dispatch,delayWithinFrame,200,8,8,new String[]{"content0"}});
+                        tests.add(new Object[]{tests.size(),client,mode,dispatch,delayWithinFrame,200,16,16,new String[]{"content0","CONTENT1"}});
+                        
+                    }
+                }
+            }
+        }
+        return tests;
+    }
+    
+
+    final int _id;
+    final Class<? extends TestClient> _client;
+    final Mode _mode;
+    final Boolean _delay;
+    final int _status;
+    final int _read;
+    final int _length;
+    final List<String> _send;
+    
+    public HttpInputIntegrationTest(int id,Class<? extends TestClient> client, Mode mode,boolean dispatch,Boolean delay,int status,int read,int length,String... send)
+    {
+        _id=id;
+        _client=client;
+        _mode=mode;
+        __config.setDelayDispatchUntilContent(dispatch);
+        _delay=delay;
+        _status=status;
+        _read=read;
+        _length=length;
+        _send = Arrays.asList(send);
+    }
+    
+    
+    private static void runmode(Mode mode,final Request request, final Runnable test)
+    {
+        switch(mode)
+        {
+            case ASYNC_DISPATCHED:
+            {
+                test.run();
+                break;
+            }   
+            case ASYNC_OTHER_DISPATCHED:
+            {
+                final CountDownLatch latch = new CountDownLatch(1);
+                new Thread()
+                {
+                    @Override
+                    public void run()
+                    {
+                        try
+                        {
+                            test.run();
+                        }
+                        finally
+                        {
+                            latch.countDown();
+                        }
+                    }
+                }.start();
+                // prevent caller returning until other thread complete
+                try
+                {
+                    if (!latch.await(5,TimeUnit.SECONDS))
+                        Assert.fail();
+                }
+                catch(Exception e)
+                {
+                    Assert.fail();
+                }
+                break;
+            }   
+            case ASYNC_OTHER_WAIT:
+            {
+                final CountDownLatch latch = new CountDownLatch(1);
+                final HttpChannelState.State S=request.getHttpChannelState().getState();
+                new Thread()
+                {
+                    @Override
+                    public void run()
+                    {
+                        try
+                        {
+                            if (!latch.await(5,TimeUnit.SECONDS))
+                                Assert.fail();
+                            
+                            // Spin until state change
+                            HttpChannelState.State s=request.getHttpChannelState().getState();
+                            while(request.getHttpChannelState().getState()==S )
+                            {
+                                Thread.yield();
+                                s=request.getHttpChannelState().getState();
+                            }
+                            test.run();
+                        }
+                        catch (Exception e)
+                        {
+                            e.printStackTrace();
+                        }
+                    }
+                }.start();
+                // ensure other thread running before trying to return
+                latch.countDown();
+                break;
+            }
+                
+            default:
+                throw new IllegalStateException();
+        }
+        
+    }
+    
+    @Test
+    public void testOne() throws Exception
+    {
+        System.err.printf("[%d] TEST   c=%s, m=%s, delayDispatch=%b delayInFrame=%s content-length:%d expect=%d read=%d content:%s%n",_id,_client.getSimpleName(),_mode,__config.isDelayDispatchUntilContent(),_delay,_length,_status,_read,_send);
+
+        TestClient client=_client.newInstance();
+        String response = client.send("/ctx/test?mode="+_mode,50,_delay,_length,_send);
+        
+        int sum=0;
+        for (String s:_send)
+            for (char c : s.toCharArray())
+                sum+=c;
+        
+        assertThat(response,startsWith("HTTP"));
+        assertThat(response,Matchers.containsString(" "+_status+" "));
+        assertThat(response,Matchers.containsString("read="+_read));
+        assertThat(response,Matchers.containsString("sum="+sum));
+    }
+    
+    @Test
+    public void testStress() throws Exception
+    {
+        System.err.printf("[%d] STRESS c=%s, m=%s, delayDispatch=%b delayInFrame=%s content-length:%d expect=%d read=%d content:%s%n",_id,_client.getSimpleName(),_mode,__config.isDelayDispatchUntilContent(),_delay,_length,_status,_read,_send);
+
+        int sum=0;
+        for (String s:_send)
+            for (char c : s.toCharArray())
+                sum+=c;
+        final int summation=sum;
+        
+        
+        final int threads=10;
+        final int loops=10;
+        
+        final AtomicInteger count = new AtomicInteger(0);
+        Thread[] t = new Thread[threads];
+        
+        Runnable run = new Runnable()
+        {
+            @Override 
+            public void run()
+            {
+                try
+                {
+                    TestClient client=_client.newInstance();
+                    for (int j=0;j<loops;j++)
+                    {
+                        String response = client.send("/ctx/test?mode="+_mode,10,_delay,_length,_send);
+                        assertThat(response,startsWith("HTTP"));
+                        assertThat(response,Matchers.containsString(" "+_status+" "));
+                        assertThat(response,Matchers.containsString("read="+_read));
+                        assertThat(response,Matchers.containsString("sum="+summation));
+                        count.incrementAndGet();
+                    }
+                }
+                catch(Exception e)
+                {
+                    e.printStackTrace();
+                }
+            }
+        };
+        
+        for (int i=0;i<threads;i++)
+        {
+            t[i]=new Thread(run);
+            t[i].start();
+        }
+        for (int i=0;i<threads;i++)
+            t[i].join();
+        
+        assertThat(count.get(),Matchers.is(threads*loops));
+    }
+    
+    
+    public static class TestServlet extends HttpServlet
+    {
+        String expected ="content0CONTENT1";
+        
+        @Override
+        protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException
+        {
+            final Mode mode = Mode.valueOf(req.getParameter("mode"));
+            resp.setContentType("text/plain");
+                        
+            if (mode==Mode.BLOCKING)
+            {
+                try
+                {
+                    String content = IO.toString(req.getInputStream());
+                    resp.setStatus(200);
+                    resp.setContentType("text/plain");
+                    resp.getWriter().println("read="+content.length());
+                    int sum = 0;
+                    for (char c:content.toCharArray())
+                        sum+=c;
+                    resp.getWriter().println("sum="+sum);
+                }
+                catch(Exception e)
+                {
+                    e.printStackTrace();
+                    resp.setStatus(500);
+                    resp.getWriter().println("read="+e);
+                    resp.getWriter().println("sum=-1");
+                }
+            }
+            else
+            {
+                // we are asynchronous
+                final AsyncContext context = req.startAsync();
+                context.setTimeout(10000);
+                final ServletInputStream in = req.getInputStream();
+                final Request request = Request.getBaseRequest(req);
+                final AtomicInteger read = new AtomicInteger(0);
+                final AtomicInteger sum = new AtomicInteger(0);
+
+                runmode(mode,request,new Runnable()
+                {
+                    @Override
+                    public void run()
+                    {
+                        in.setReadListener(new ReadListener()
+                        {
+                            @Override
+                            public void onError(Throwable t)
+                            {
+                                t.printStackTrace();
+                                try
+                                {
+                                    resp.sendError(500);
+                                }
+                                catch (IOException e)
+                                {
+                                    e.printStackTrace();
+                                    throw new RuntimeException(e);
+                                }
+                                context.complete();
+                            }
+
+                            @Override
+                            public void onDataAvailable() throws IOException
+                            {
+                                runmode(mode,request,new Runnable()
+                                {
+                                    @Override
+                                    public void run()
+                                    {
+                                        while(in.isReady() && !in.isFinished())
+                                        {
+                                            try
+                                            {
+                                                int b = in.read();
+                                                if (b<0)
+                                                    return;
+                                                sum.addAndGet(b);
+                                                int i=read.getAndIncrement();
+                                                if (b!=expected.charAt(i))
+                                                {
+                                                    System.err.printf("XXX '%c'!='%c' at %d%n",expected.charAt(i),(char)b,i);
+                                                    System.err.println("    "+request.getHttpChannel());
+                                                    System.err.println("    "+request.getHttpChannel().getHttpTransport());
+                                                }
+                                                
+                                            }
+                                            catch (IOException e)
+                                            {
+                                                onError(e);
+                                            }
+                                        }
+                                    }
+                                });
+                            }
+
+                            @Override
+                            public void onAllDataRead() throws IOException
+                            {
+                                resp.setStatus(200);
+                                resp.setContentType("text/plain");
+                                resp.getWriter().println("read="+read.get());
+                                resp.getWriter().println("sum="+sum.get());
+                                context.complete();
+                            }
+                        });
+                    }
+                });
+            }
+        }
+    }
+    
+
+    public static class LocalClient implements TestClient
+    {
+        StringBuilder flushed = new StringBuilder();
+        @Override
+        public String send(String uri,int delayMs, Boolean delayInFrame,int contentLength, List<String> content) throws Exception
+        {
+            LocalConnector connector = __server.getBean(LocalConnector.class);
+
+            StringBuilder buffer = new StringBuilder();
+            buffer.append("GET ").append(uri).append(" HTTP/1.1\r\n");
+            buffer.append("Host: localhost\r\n");
+            buffer.append("Connection: close\r\n");
+
+            LocalEndPoint local = connector.executeRequest("");
+            
+            flush(local,buffer,delayMs,delayInFrame,true);
+
+            boolean chunked=contentLength<0;
+            if (chunked)
+                buffer.append("Transfer-Encoding: chunked\r\n");
+            else
+                buffer.append("Content-Length: ").append(contentLength).append("\r\n");
+
+            if (contentLength>0)
+                buffer.append("Content-Type: text/plain\r\n");
+            buffer.append("\r\n");
+
+            flush(local,buffer,delayMs,delayInFrame,false);
+
+            for (String c : content)
+            {
+                if (chunked)
+                {
+                    buffer.append(Integer.toHexString(c.length())).append("\r\n");
+                    flush(local,buffer,delayMs,delayInFrame,true);
+                }
+
+                buffer.append(c.substring(0,1));
+                flush(local,buffer,delayMs,delayInFrame,true);
+                buffer.append(c.substring(1));
+                if (chunked)
+                    buffer.append("\r\n"); 
+                flush(local,buffer,delayMs,delayInFrame,false);
+            }
+
+            if (chunked)
+            {
+                buffer.append("0");
+                flush(local,buffer,delayMs,delayInFrame,true);
+                buffer.append("\r\n\r\n");
+            }
+
+            flush(local,buffer);
+            local.waitUntilClosed();
+            return local.takeOutputString();
+        }
+          
+
+        private void flush(LocalEndPoint local, StringBuilder buffer,int delayMs, Boolean delayInFrame, boolean inFrame) throws Exception
+        {
+            // Flush now if we should delay
+            if (delayInFrame!=null && delayInFrame.equals(inFrame))
+            {
+                flush(local,buffer);
+                Thread.sleep(delayMs);
+            }
+        }
+        
+        private void flush(final LocalEndPoint local, StringBuilder buffer) throws Exception
+        {
+            final String flush=buffer.toString();
+            buffer.setLength(0);
+            flushed.append(flush);
+            local.addInputAndExecute(BufferUtil.toBuffer(flush));
+        }
+
+    }
+
+    public static class H1Client implements TestClient
+    {
+        NetworkConnector _connector;
+
+        public H1Client()
+        {
+            for (Connector c:__server.getConnectors())
+            {
+                if (c instanceof NetworkConnector && c.getDefaultConnectionFactory().getProtocol().equals(HttpVersion.HTTP_1_1.asString()))
+                {
+                    _connector=(NetworkConnector)c;
+                    break;
+                }
+            }
+        }
+
+        @Override
+        public String send(String uri, int delayMs, Boolean delayInFrame,int contentLength, List<String> content) throws Exception
+        {
+            int port=_connector.getLocalPort();
+            
+            try (Socket client = newSocket("localhost", port))
+            {
+                client.setSoTimeout(5000);
+                client.setTcpNoDelay(true);
+                client.setSoLinger(true,1);
+                OutputStream out = client.getOutputStream();
+
+                StringBuilder buffer = new StringBuilder();
+                buffer.append("GET ").append(uri).append(" HTTP/1.1\r\n");
+                buffer.append("Host: localhost:").append(port).append("\r\n");
+                buffer.append("Connection: close\r\n");
+
+                flush(out,buffer,delayMs,delayInFrame,true);
+
+                boolean chunked=contentLength<0;
+                if (chunked)
+                    buffer.append("Transfer-Encoding: chunked\r\n");
+                else
+                    buffer.append("Content-Length: ").append(contentLength).append("\r\n");
+                    
+                if (contentLength>0)
+                    buffer.append("Content-Type: text/plain\r\n");
+                buffer.append("\r\n");
+
+                flush(out,buffer,delayMs,delayInFrame,false);
+                
+                for (String c : content)
+                {
+                    if (chunked)
+                    {
+                        buffer.append(Integer.toHexString(c.length())).append("\r\n");
+                       flush(out,buffer,delayMs,delayInFrame,true);
+                    }
+                    
+                    buffer.append(c.substring(0,1));
+                    flush(out,buffer,delayMs,delayInFrame,true);
+                    buffer.append(c.substring(1));
+                    flush(out,buffer,delayMs,delayInFrame,false);
+                    if (chunked)
+                        buffer.append("\r\n"); 
+                }
+
+                if (chunked)
+                {
+                    buffer.append("0");
+                    flush(out,buffer,delayMs,delayInFrame,true);
+                    buffer.append("\r\n\r\n");
+                }
+                
+                flush(out,buffer);
+                
+                return IO.toString(client.getInputStream());
+            }
+            
+        }
+
+        private void flush(OutputStream out, StringBuilder buffer, int delayMs, Boolean delayInFrame, boolean inFrame) throws Exception
+        {
+            // Flush now if we should delay
+            if (delayInFrame!=null && delayInFrame.equals(inFrame))
+            {
+                flush(out,buffer);
+                Thread.sleep(delayMs);
+            }
+        }
+        
+        private void flush(OutputStream out, StringBuilder buffer) throws Exception
+        {
+            String flush=buffer.toString();
+            buffer.setLength(0);
+            out.write(flush.getBytes(StandardCharsets.ISO_8859_1));
+            out.flush();
+        }
+
+        public Socket newSocket(String host, int port) throws IOException
+        {
+            return new Socket(host, port);
+        }
+    }
+    
+    public static class H1SClient extends H1Client
+    {
+        public H1SClient()
+        {
+            for (Connector c:__server.getConnectors())
+            {
+                if (c instanceof NetworkConnector && c.getDefaultConnectionFactory().getProtocol().equals("SSL"))
+                {
+                    _connector=(NetworkConnector)c;
+                    break;
+                }
+            }
+        }
+
+        public Socket newSocket(String host, int port) throws IOException
+        {
+            SSLSocket socket = __sslContextFactory.newSslSocket();
+            socket.connect(new InetSocketAddress(Inet4Address.getByName(host),port));
+            return socket;
+        }
+    }
+
+    
+}
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/FakeJspServlet.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/FakeJspServlet.java
index 4a34073..1a86d13 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/FakeJspServlet.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/FakeJspServlet.java
@@ -20,7 +20,6 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.net.URISyntaxException;
 import java.net.URL;
 
 import javax.servlet.ServletException;
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithAliasesTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithAliasesTest.java
index 2a402b4..e5fc196 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithAliasesTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithAliasesTest.java
@@ -18,8 +18,6 @@
 
 package org.eclipse.jetty.test.jsp;
 
-import static org.hamcrest.Matchers.*;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -47,6 +45,10 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+
 /**
  * Test various paths for JSP resources that tickle various java.io.File bugs to get around the JspServlet matching, that then flows to the DefaultServlet to be
  * served as source files.
@@ -62,7 +64,7 @@
     public static Collection<String[]> data()
     {
         List<String[]> data = new ArrayList<String[]>();
-        
+
         double javaVersion = Double.parseDouble(System.getProperty("java.specification.version"));
 
         // @formatter:off
@@ -73,7 +75,7 @@
         data.add(new String[] { "false","/dump.jsp%00x/dump.jsp" });
         data.add(new String[] { "false","/dump.jsp%00/dump.jsp" });
 
-        if (javaVersion >= 1.7) 
+        if (javaVersion >= 1.7)
         {
             data.add(new String[] { "false","/dump.jsp%00x" });
             data.add(new String[] { "false","/dump.jsp%00x/" });
@@ -87,7 +89,7 @@
     @BeforeClass
     public static void startServer() throws Exception
     {
-        server = new Server(0);  
+        server = new Server(0);
 
         // Configure LoginService
         HashLoginService login = new HashLoginService();
@@ -109,14 +111,14 @@
 
         // add jsp
         ServletHolder jsp = new ServletHolder(new FakeJspServlet());
-        context.addServlet(jsp,"*.jsp");        
+        context.addServlet(jsp,"*.jsp");
         jsp.setInitParameter("classpath",context.getClassPath());
 
         // add context
         server.setHandler(context);
 
         server.start();
-        
+
         int port = ((NetworkConnector)server.getConnectors()[0]).getLocalPort();
 
         serverURI = new URI("http://localhost:" + port + "/");
@@ -143,7 +145,7 @@
         // make sure that jsp actually ran, and didn't just get passed onto
         // the default servlet to return the jsp source
         String body = getResponseBody(conn);
-        
+
         if (knownBypass && body.indexOf("<%@")>=0)
             LOG.info("Known bypass of mapping by "+path);
         else
@@ -164,7 +166,7 @@
 
         if (conn.getResponseCode()!=404)
             System.err.println(conn.getResponseMessage());
-        
+
         // Of other possible paths, only 404 Not Found is expected
         Assert.assertThat("Response Code",conn.getResponseCode(),is(404));
     }
@@ -173,13 +175,13 @@
     public void testGetReference() throws Exception
     {
         URI uri = serverURI.resolve(path);
-        
+
         HttpURLConnection conn = null;
         try
         {
             conn = (HttpURLConnection)uri.toURL().openConnection();
-            conn.setConnectTimeout(1000);
-            conn.setReadTimeout(1000);
+            conn.setConnectTimeout(5000);
+            conn.setReadTimeout(5000);
             assertResponse(conn);
         }
         finally
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithoutAliasesTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithoutAliasesTest.java
index 74d7f98..c9ff0d2 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithoutAliasesTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithoutAliasesTest.java
@@ -18,7 +18,9 @@
 
 package org.eclipse.jetty.test.jsp;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java
index 7362758..4b58346 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java
@@ -18,21 +18,25 @@
 
 package org.eclipse.jetty.test.rfcs;
 
-import static org.junit.Assert.*;
-import static org.junit.matchers.JUnitMatchers.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 import java.io.IOException;
 import java.net.Socket;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
-import java.util.Collections;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.TimeZone;
 
 import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpParser;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.test.support.StringUtil;
@@ -42,7 +46,7 @@
 import org.eclipse.jetty.toolchain.test.FS;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.toolchain.test.StringAssert;
-import org.eclipse.jetty.util.MultiPartInputStreamParser;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.hamcrest.Matchers;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -146,7 +150,7 @@
 
         // Test formatting
         fields.putDateField("Date",expected.getTime().getTime());
-        Assert.assertEquals("3.3.1 RFC 822 preferred","Sun, 06 Nov 1994 08:49:37 GMT",fields.getStringField("Date"));
+        Assert.assertEquals("3.3.1 RFC 822 preferred","Sun, 06 Nov 1994 08:49:37 GMT",fields.get("Date"));
     }
 
     /**
@@ -1159,12 +1163,12 @@
         req4.append("\n");
 
         HttpTester.Response response = http.request(req4);
-
+        
         String specId = "10.3 Redirection HTTP/1.1 w/content";
-        assertEquals(specId,HttpStatus.FOUND_302, response.getStatus());
-        assertEquals(specId,server.getScheme() + "://localhost/tests/R2.txt", response.get("Location"));
-        assertEquals(specId,"close", response.get("Connection"));
-        assertTrue(specId,response.get("Content-Length") == null);
+        assertThat(specId + " [status]",response.getStatus(),is(HttpStatus.FOUND_302));
+        assertThat(specId + " [location]",response.get("Location"),is(server.getScheme() + "://localhost/tests/R2.txt"));
+        assertThat(specId + " [connection]",response.get("Connection"),is("close"));
+        assertThat(specId + " [content-length]",response.get("Content-Length"), nullValue());
     }
 
     /**
@@ -1446,15 +1450,17 @@
     public void test14_23_IncompleteHostHeader() throws Exception
     {
         // HTTP/1.1 - Incomplete (empty) Host header
+        try (StacklessLogging stackless = new StacklessLogging(HttpParser.class))
+        {
+            StringBuffer req4 = new StringBuffer();
+            req4.append("GET /tests/R1.txt HTTP/1.1\n");
+            req4.append("Host:\n");
+            req4.append("Connection: close\n");
+            req4.append("\n");
 
-        StringBuffer req4 = new StringBuffer();
-        req4.append("GET /tests/R1.txt HTTP/1.1\n");
-        req4.append("Host:\n");
-        req4.append("Connection: close\n");
-        req4.append("\n");
-
-        HttpTester.Response response = http.request(req4);
-        assertEquals("14.23 HTTP/1.1 - Empty Host", HttpStatus.OK_200, response.getStatus());
+            HttpTester.Response response = http.request(req4);
+            assertEquals("14.23 HTTP/1.1 - Empty Host", HttpStatus.OK_200, response.getStatus());
+        }
     }
 
     /**
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java
index ec3b99e..5dfa546 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java
@@ -276,7 +276,7 @@
      */
     public JettyDistro(TestingDir testdir) throws IOException
     {
-        this.jettyHomeDir = testdir.getDir();
+        this.jettyHomeDir = testdir.getPath().toFile();
         copyBaseDistro();
     }
 
@@ -292,7 +292,7 @@
      */
     public JettyDistro(TestingDir testdir, String artifact) throws IOException
     {
-        this.jettyHomeDir = testdir.getDir();
+        this.jettyHomeDir = testdir.getPath().toFile();
         if (artifact != null)
         {
             this.artifactName = artifact;
@@ -525,7 +525,7 @@
         // Do a dry run first to get the exact command line for Jetty process
         commands.add("-jar");
         commands.add("start.jar");
-        commands.add("jetty.port=0");
+        commands.add("jetty.http.port=0");
         if (_debug)
         {
             commands.add("-D.DEBUG=true");
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java
index 67ac03e..f2d3439 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java
@@ -178,7 +178,6 @@
 
         // Find the active server port.
         this._serverPort = ((NetworkConnector)_server.getConnectors()[0]).getLocalPort();
-        System.err.println("Server Port="+_serverPort);
         Assert.assertTrue("Server Port is between 1 and 65535. Actually <" + _serverPort + ">",(1 <= this._serverPort) && (this._serverPort <= 65535));
     }
 
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpResponseTesterTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpResponseTesterTest.java
index 48fda46..c3a2da2 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpResponseTesterTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpResponseTesterTest.java
@@ -18,6 +18,10 @@
 
 package org.eclipse.jetty.test.support.rawhttp;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+
 import java.io.IOException;
 import java.util.List;
 
@@ -27,10 +31,6 @@
 import org.junit.Assert;
 import org.junit.Test;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.assertThat;
-
 public class HttpResponseTesterTest
 {
     @Test
@@ -104,21 +104,21 @@
         Assert.assertEquals("Responses.size",3,responses.size());
 
         HttpTester.Response resp1 = responses.get(0);
-        System.err.println(resp1.toString());
+        // System.err.println(resp1.toString());
         Assert.assertEquals(HttpStatus.OK_200, resp1.getStatus());
         Assert.assertEquals("text/plain", resp1.get("Content-Type"));
         Assert.assertTrue(resp1.getContent().contains("ABCDEFGHIJKLMNOPQRSTTUVWXYZ\n"));
         assertThat(resp1.get("Connection"),is(not("close")));
 
         HttpTester.Response resp2 = responses.get(1);
-        System.err.println(resp2.toString());
+        // System.err.println(resp2.toString());
         Assert.assertEquals(HttpStatus.OK_200, resp2.getStatus());
         Assert.assertEquals("text/plain", resp2.get("Content-Type"));
         Assert.assertTrue(resp2.getContent().contains("Host=Default\nResource=R1\n"));
         assertThat(resp2.get("Connection"),is(not("close")));
 
         HttpTester.Response resp3 = responses.get(2);
-        System.err.println(resp3.toString());
+        // System.err.println(resp3.toString());
         Assert.assertEquals(HttpStatus.OK_200, resp3.getStatus());
         Assert.assertEquals("text/plain", resp3.get("Content-Type"));
         Assert.assertTrue(resp3.getContent().contains("Host=Default\nResource=R2\n"));
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpsSocketImpl.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpsSocketImpl.java
index 824b4d2..98e43a9 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpsSocketImpl.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpsSocketImpl.java
@@ -29,8 +29,6 @@
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
 
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
diff --git a/tests/test-integration/src/test/resources/DefaultHandler.xml b/tests/test-integration/src/test/resources/DefaultHandler.xml
index 644e7f9..7920979 100644
--- a/tests/test-integration/src/test/resources/DefaultHandler.xml
+++ b/tests/test-integration/src/test/resources/DefaultHandler.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Configure the Jetty Server                                      -->
@@ -41,8 +41,8 @@
              <New id="defcontext" class="org.eclipse.jetty.server.handler.ContextHandler">
                <Set name="contextPath">/tests</Set>
                <Set name="ResourceBase"><Property name="test.docroot.base"/>/default</Set>
-		       <Set name="Handler"><New id="reshandler" class="org.eclipse.jetty.server.handler.ResourceHandler"/></Set>
-		       <Set name="DisplayName">default</Set>
+	       <Set name="Handler"><New id="reshandler" class="org.eclipse.jetty.server.handler.ResourceHandler"/></Set>
+	       <Set name="DisplayName">default</Set>
              </New>
            </Item>
          </Array>
diff --git a/tests/test-integration/src/test/resources/NIOHttp.xml b/tests/test-integration/src/test/resources/NIOHttp.xml
index c766d10..d58befb 100644
--- a/tests/test-integration/src/test/resources/NIOHttp.xml
+++ b/tests/test-integration/src/test/resources/NIOHttp.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
@@ -20,9 +20,8 @@
             </Item>
           </Array>
         </Arg>
-        <Set name="host"><Property name="jetty.host" /></Set>
-        <Set name="port">0</Set>
-        <Set name="idleTimeout"><Property name="http.timeout" default="30000"/></Set>
+        <Set name="host"><Property name="jetty.http.host" /></Set>
+        <Set name="idleTimeout"><Property name="jetty.http.idleTimeout" default="30000"/></Set>
       </New>
     </Arg>
   </Call>
diff --git a/tests/test-integration/src/test/resources/NIOHttps.xml b/tests/test-integration/src/test/resources/NIOHttps.xml
index 5e90fdb..5146752 100644
--- a/tests/test-integration/src/test/resources/NIOHttps.xml
+++ b/tests/test-integration/src/test/resources/NIOHttps.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
@@ -25,8 +25,7 @@
               </Item>
             </Array>
           </Arg>
-          <Set name="host"><Property name="jetty.host" /></Set>
-          <Set name="port">0</Set>
+          <Set name="host"><Property name="jetty.http.host" /></Set>
           <Set name="idleTimeout">30000</Set>
         </New>
     </Arg>
diff --git a/tests/test-integration/src/test/resources/RFC2616Base.xml b/tests/test-integration/src/test/resources/RFC2616Base.xml
index 2c300d1..9a1d4ae 100644
--- a/tests/test-integration/src/test/resources/RFC2616Base.xml
+++ b/tests/test-integration/src/test/resources/RFC2616Base.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Configure the Jetty Server                                      -->
@@ -102,12 +102,10 @@
     </Arg>
   </Call>
 
-
-
-    <!-- =========================================================== -->
-    <!-- extra options                                               -->
-    <!-- =========================================================== -->
-    <Set name="stopAtShutdown">true</Set>
-    <Set name="stopTimeout">1000</Set>
+  <!-- =========================================================== -->
+  <!-- extra options                                               -->
+  <!-- =========================================================== -->
+  <Set name="stopAtShutdown">true</Set>
+  <Set name="stopTimeout">1000</Set>
 
 </Configure>
diff --git a/tests/test-integration/src/test/resources/RFC2616_Filters.xml b/tests/test-integration/src/test/resources/RFC2616_Filters.xml
index a798bfd..73dbb38 100644
--- a/tests/test-integration/src/test/resources/RFC2616_Filters.xml
+++ b/tests/test-integration/src/test/resources/RFC2616_Filters.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
diff --git a/tests/test-integration/src/test/resources/RFC2616_Redirects.xml b/tests/test-integration/src/test/resources/RFC2616_Redirects.xml
index a3cc7a3..fca59dd 100644
--- a/tests/test-integration/src/test/resources/RFC2616_Redirects.xml
+++ b/tests/test-integration/src/test/resources/RFC2616_Redirects.xml
@@ -1,54 +1,52 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
-    <!-- =========================================================== -->
-    <!-- Configure Rewrite Handler                                   -->
-    <!-- =========================================================== -->
-    <Get id="oldhandler" name="handler"/>
+  <!-- =========================================================== -->
+  <!-- Configure Rewrite Handler                                   -->
+  <!-- =========================================================== -->
 
-    <Set name="handler">
+  <Call name="insertHandler">
+    <Arg>
      <New id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
-
-      <Set name="handler"><Ref refid="oldhandler"/></Set>
       <Set name="rewriteRequestURI">true</Set>
       <Set name="rewritePathInfo">false</Set>
       <Set name="originalPathAttribute">requestedPath</Set>
 
       <Set name="rules">
-          <Array type="org.eclipse.jetty.rewrite.handler.Rule">
+	  <Array type="org.eclipse.jetty.rewrite.handler.Rule">
 
-            <!-- add a response rule -->
-            <!--
-            <Item>
-              <New id="response" class="org.eclipse.jetty.rewrite.handler.ResponsePatternRule">
-                <Set name="pattern">/rewrite/session/</Set>
-                <Set name="code">401</Set>
-                <Set name="reason">Setting error code 401</Set>
-              </New>
-            </Item>
-             -->
+	    <!-- add a response rule -->
+	    <!--
+	    <Item>
+	      <New id="response" class="org.eclipse.jetty.rewrite.handler.ResponsePatternRule">
+		<Set name="pattern">/rewrite/session/</Set>
+		<Set name="code">401</Set>
+		<Set name="reason">Setting error code 401</Set>
+	      </New>
+	    </Item>
+	     -->
 
-            <!-- add a simple redirect -->
-            <!--
-            <Item>
-              <New id="redirect" class="org.eclipse.jetty.rewrite.handler.RedirectPatternRule">
-                <Set name="pattern">/redirect/*</Set>
-                <Set name="location">/tests/</Set>
-              </New>
-            </Item>
-             -->
+	    <!-- add a simple redirect -->
+	    <!--
+	    <Item>
+	      <New id="redirect" class="org.eclipse.jetty.rewrite.handler.RedirectPatternRule">
+		<Set name="pattern">/redirect/*</Set>
+		<Set name="location">/tests/</Set>
+	      </New>
+	    </Item>
+	     -->
 
-            <!-- add a regex rewrite redirect -->
-            <Item>
-              <New id="redirect" class="org.eclipse.jetty.rewrite.handler.RedirectRegexRule">
-                <Set name="regex">/redirect/(.*)</Set>
-                <Set name="replacement">/tests/$1</Set>
-              </New>
-            </Item>
-          </Array>
-        </Set>
+	    <!-- add a regex rewrite redirect -->
+	    <Item>
+	      <New id="redirect" class="org.eclipse.jetty.rewrite.handler.RedirectRegexRule">
+		<Set name="regex">/redirect/(.*)</Set>
+		<Set name="replacement">/tests/$1</Set>
+	      </New>
+	    </Item>
+	  </Array>
+	</Set>
       </New>
-    </Set>
-
+    </Arg>
+  </Call>
 </Configure>
diff --git a/tests/test-integration/src/test/resources/jetty-logging.properties b/tests/test-integration/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..d96a696
--- /dev/null
+++ b/tests/test-integration/src/test/resources/jetty-logging.properties
@@ -0,0 +1,2 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.LEVEL=DEBUG
diff --git a/tests/test-integration/src/test/resources/ssl.xml b/tests/test-integration/src/test/resources/ssl.xml
index 84de070..eecff79 100644
--- a/tests/test-integration/src/test/resources/ssl.xml
+++ b/tests/test-integration/src/test/resources/ssl.xml
@@ -1,9 +1,9 @@
 <Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
-  <Set name="KeyStorePath"><Property name="jetty.home" default="." />/<Property name="jetty.keystore" default="keystore"/></Set>
-  <Set name="KeyStorePassword"><Property name="jetty.keystore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
-  <Set name="KeyManagerPassword"><Property name="jetty.keymanager.password" default="OBF:1u2u1wml1z7s1z7a1wnl1u2g"/></Set>
-  <Set name="TrustStorePath"><Property name="jetty.home" default="." />/<Property name="jetty.truststore" default="keystore"/></Set>
-  <Set name="TrustStorePassword"><Property name="jetty.truststore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
+  <Set name="KeyStorePath"><Property name="jetty.home" default="." />/<Property name="jetty.sslContext.keyStorePath" default="keystore"/></Set>
+  <Set name="KeyStorePassword"><Property name="jetty.sslContext.keyStorePassword" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
+  <Set name="KeyManagerPassword"><Property name="jetty.sslContext.keyManagerPassword" default="OBF:1u2u1wml1z7s1z7a1wnl1u2g"/></Set>
+  <Set name="TrustStorePath"><Property name="jetty.home" default="." />/<Property name="jetty.sslContext.trustStorePath" default="keystore"/></Set>
+  <Set name="TrustStorePassword"><Property name="jetty.sslContext.trustStorePassword" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
   <Set name="EndpointIdentificationAlgorithm"></Set>
   <Set name="ExcludeCipherSuites">
     <Array type="String">
diff --git a/tests/test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml b/tests/test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml
index 74d2fe9..36e624f 100644
--- a/tests/test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml
+++ b/tests/test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml
@@ -1,8 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
-	<Set name="contextPath">/rfc2616-webapp</Set>
-	<Set name="war">
-		<Property name="test.webapps" default="." />/test-webapp-rfc2616.war
-	</Set>
+  <Set name="contextPath">/rfc2616-webapp</Set>
+  <Set name="war"><Property name="test.webapps" default="." />/test-webapp-rfc2616.war</Set>
+  <Set name="gzipHandler">
+    <New class="org.eclipse.jetty.server.handler.gzip.GzipHandler">
+      <Set name="minGzipSize">1024</Set>
+    </New>
+  </Set>
 </Configure>
diff --git a/tests/test-integration/src/test/resources/webdefault.xml b/tests/test-integration/src/test/resources/webdefault.xml
index d87a7e2..d54d76f 100644
--- a/tests/test-integration/src/test/resources/webdefault.xml
+++ b/tests/test-integration/src/test/resources/webdefault.xml
@@ -1,113 +1,142 @@
 <?xml version="1.0" encoding="UTF-8"?>
-
-<!-- ===================================================================== -->
-<!-- This file contains the default descriptor for web applications.       -->
-<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
-<!-- The intent of this descriptor is to include jetty specific or common  -->
-<!-- configuration for all webapps.   If a context has a webdefault.xml    -->
-<!-- descriptor, it is applied before the contexts own web.xml file        -->
-<!--                                                                       -->
-<!-- A context may be assigned a default descriptor by:                    -->
-<!--  + Calling WebApplicationContext.setDefaultsDescriptor                -->
-<!--  + Passed an arg to addWebApplications                                -->
-<!--                                                                       -->
-<!-- This file is used both as the resource within the jetty.jar (which is -->
-<!-- used as the default if no explicit defaults descriptor is set) and it -->
-<!-- is copied to the etc directory of the Jetty distro and explicitly     -->
-<!-- by the jetty.xml file.                                                -->
-<!--                                                                       -->
-<!-- ===================================================================== -->
 <web-app 
-   xmlns="http://java.sun.com/xml/ns/javaee" 
+   xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
-   metadata-complete="true"
-   version="2.5"> 
+   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+   metadata-complete="false"
+   version="3.1"> 
+
+  <!-- ===================================================================== -->
+  <!-- This file contains the default descriptor for web applications.       -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+  <!-- The intent of this descriptor is to include jetty specific or common  -->
+  <!-- configuration for all webapps.   If a context has a webdefault.xml    -->
+  <!-- descriptor, it is applied before the context's own web.xml file       -->
+  <!--                                                                       -->
+  <!-- A context may be assigned a default descriptor by calling             -->
+  <!-- WebAppContext.setDefaultsDescriptor(String).                          -->
+  <!--                                                                       -->
+  <!-- This file is present in the jetty-webapp.jar, and is used as the      -->
+  <!-- defaults descriptor if no other is explicitly set on a context.       -->
+  <!--                                                                       -->
+  <!-- A copy of this file is also placed into the $JETTY_HOME/etc dir of    -->
+  <!-- the  distribution, and is referenced by some of the other xml files,  -->
+  <!-- eg the jetty-deploy.xml file.                                         -->
+  <!-- ===================================================================== -->
 
   <description>
     Default web.xml file.  
     This file is applied to a Web application before it's own WEB_INF/web.xml file
   </description>
 
+  <!-- ==================================================================== -->
+  <!-- Removes static references to beans from javax.el.BeanELResolver to   -->
+  <!-- ensure webapp classloader can be released on undeploy                -->
+  <!-- ==================================================================== -->
+  <listener>
+   <listener-class>org.eclipse.jetty.servlet.listener.ELContextCleaner</listener-class>
+  </listener>
+  
+  <!-- ==================================================================== -->
+  <!-- Removes static cache of Methods from java.beans.Introspector to      -->
+  <!-- ensure webapp classloader can be released on undeploy                -->
+  <!-- ==================================================================== -->  
+  <listener>
+   <listener-class>org.eclipse.jetty.servlet.listener.IntrospectorCleaner</listener-class>
+  </listener>
+  
 
   <!-- ==================================================================== -->
   <!-- Context params to control Session Cookies                            -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
-  <!-- UNCOMMENT TO ACTIVATE
-  <context-param>
-    <param-name>org.eclipse.jetty.servlet.SessionDomain</param-name>
-    <param-value>127.0.0.1</param-value>
-  </context-param>
-
-  <context-param>
-    <param-name>org.eclipse.jetty.servlet.SessionPath</param-name>
-    <param-value>/</param-value>
-  </context-param>
-
-  <context-param>
-    <param-name>org.eclipse.jetty.servlet.MaxAge</param-name>
-    <param-value>-1</param-value>
-  </context-param>
+  <!--
+    UNCOMMENT TO ACTIVATE 
+    <context-param> 
+      <param-name>org.eclipse.jetty.servlet.SessionDomain</param-name> 
+      <param-value>127.0.0.1</param-value> 
+    </context-param> 
+    <context-param>
+      <param-name>org.eclipse.jetty.servlet.SessionPath</param-name>
+      <param-value>/</param-value>
+    </context-param>
+    <context-param>
+      <param-name>org.eclipse.jetty.servlet.MaxAge</param-name>
+      <param-value>-1</param-value>
+    </context-param>
   -->
 
-
   <!-- ==================================================================== -->
   <!-- The default servlet.                                                 -->
   <!-- This servlet, normally mapped to /, provides the handling for static -->
   <!-- content, OPTIONS and TRACE methods for the context.                  -->
   <!-- The following initParameters are supported:                          -->
-  <!--                                                                      -->
-  <!--   acceptRanges     If true, range requests and responses are         -->
-  <!--                    supported                                         -->
-  <!--                                                                      -->
-  <!--   dirAllowed       If true, directory listings are returned if no    -->
-  <!--                    welcome file is found. Else 403 Forbidden.        -->
-  <!--                                                                      -->
-  <!--   welcomeServlets  If true, attempt to dispatch to welcome files     -->
-  <!--                    that are servlets, if no matching static          -->
-  <!--                    resources can be found.                           -->
-  <!--                                                                      -->
-  <!--   redirectWelcome  If true, redirect welcome file requests           -->
-  <!--                    else use request dispatcher forwards              -->
-  <!--                                                                      -->
-  <!--   gzip             If set to true, then static content will be served--> 
-  <!--                    as gzip content encoded if a matching resource is -->
-  <!--                    found ending with ".gz"                           -->
-  <!--                                                                      -->
-  <!--   resoureBase      Can be set to replace the context resource base   -->
-  <!--                                                                      -->
-  <!--   relativeResourceBase                                               -->
-  <!--                    Set with a pathname relative to the base of the   -->
-  <!--                    servlet context root. Useful for only serving     -->
-  <!--                    static content from only specific subdirectories. -->
-  <!--                                                                      -->
-  <!--   useFileMappedBuffer                                                -->
-  <!--                    If set to true (the default), a  memory mapped    -->
-  <!--                    file buffer will be used to serve static content  -->
-  <!--                    when using an NIO connector. Setting this value   -->
-  <!--                    to false means that a direct buffer will be used  -->
-  <!--                    instead. If you are having trouble with Windows   -->
-  <!--                    file locking, set this to false.                  -->
-  <!--                                                                      -->
-  <!--  cacheControl      If set, all static content will have this value   -->
-  <!--                    set as the cache-control header.                  -->
-  <!--                                                                      -->
-  <!--  maxCacheSize      Maximum size of the static resource cache         -->
-  <!--                                                                      -->
-  <!--  maxCachedFileSize Maximum size of any single file in the cache      -->
-  <!--                                                                      -->
-  <!--  maxCachedFiles    Maximum number of files in the cache              -->
-  <!--                                                                      -->
-  <!--  cacheType         "nio", "bio" or "both" to determine the type(s)   -->
-  <!--                    of resource cache. A bio cached buffer may be used-->
-  <!--                    by nio but is not as efficient as a nio buffer.   -->
-  <!--                    An nio cached buffer may not be used by bio.      -->
-  <!--                                                                      -->
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+  <!--  
+ *  acceptRanges      If true, range requests and responses are
+ *                    supported
+ *
+ *  dirAllowed        If true, directory listings are returned if no
+ *                    welcome file is found. Else 403 Forbidden.
+ *
+ *  welcomeServlets   If true, attempt to dispatch to welcome files
+ *                    that are servlets, but only after no matching static
+ *                    resources could be found. If false, then a welcome
+ *                    file must exist on disk. If "exact", then exact
+ *                    servlet matches are supported without an existing file.
+ *                    Default is true.
+ *
+ *                    This must be false if you want directory listings,
+ *                    but have index.jsp in your welcome file list.
+ *
+ *  redirectWelcome   If true, welcome files are redirected rather than
+ *                    forwarded to.
+ *
+ *  gzip              If set to true, then static content will be served as
+ *                    gzip content encoded if a matching resource is
+ *                    found ending with ".gz"
+ *
+ *  resourceBase      Set to replace the context resource base
+ *
+ *  resourceCache     If set, this is a context attribute name, which the servlet
+ *                    will use to look for a shared ResourceCache instance.
+ *
+ *  relativeResourceBase
+ *                    Set with a pathname relative to the base of the
+ *                    servlet context root. Useful for only serving static content out
+ *                    of only specific subdirectories.
+ *
+ *  pathInfoOnly      If true, only the path info will be applied to the resourceBase
+ *
+ *  stylesheet        Set with the location of an optional stylesheet that will be used
+ *                    to decorate the directory listing html.
+ *
+ *  aliases           If True, aliases of resources are allowed (eg. symbolic
+ *                    links and caps variations). May bypass security constraints.
+ *                    
+ *  etags             If True, weak etags will be generated and handled.
+ *
+ *  maxCacheSize      The maximum total size of the cache or 0 for no cache.
+ *  maxCachedFileSize The maximum size of a file to cache
+ *  maxCachedFiles    The maximum number of files to cache
+ *
+ *  useFileMappedBuffer
+ *                    If set to true, it will use mapped file buffers to serve static content
+ *                    when using an NIO connector. Setting this value to false means that
+ *                    a direct buffer will be used instead of a mapped file buffer.
+ *                    This file sets the value to true.
+ *
+ *  cacheControl      If set, all static content will have this value set as the cache-control
+ *                    header.
+ *
+ -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <servlet>
     <servlet-name>default</servlet-name>
     <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
     <init-param>
+      <param-name>aliases</param-name>
+      <param-value>false</param-value>
+    </init-param>
+    <init-param>
       <param-name>acceptRanges</param-name>
       <param-value>true</param-value>
     </init-param>
@@ -117,8 +146,8 @@
     </init-param>
     <init-param>
       <param-name>welcomeServlets</param-name>
- 	    <param-value>false</param-value>
- 	  </init-param>
+      <param-value>false</param-value>
+    </init-param>
     <init-param>
       <param-name>redirectWelcome</param-name>
       <param-value>false</param-value>
@@ -129,24 +158,30 @@
     </init-param>
     <init-param>
       <param-name>maxCachedFileSize</param-name>
-      <param-value>10000000</param-value>
+      <param-value>200000000</param-value>
     </init-param>
     <init-param>
       <param-name>maxCachedFiles</param-name>
-      <param-value>1000</param-value>
-    </init-param>
-    <init-param>
-      <param-name>cacheType</param-name>
-      <param-value>both</param-value>
+      <param-value>2048</param-value>
     </init-param>
     <init-param>
       <param-name>gzip</param-name>
-      <param-value>true</param-value>
+      <param-value>false</param-value>
+    </init-param>
+    <init-param>
+      <param-name>etags</param-name>
+      <param-value>false</param-value>
     </init-param>
     <init-param>
       <param-name>useFileMappedBuffer</param-name>
       <param-value>true</param-value>
-    </init-param>  
+    </init-param>
+    <!--
+    <init-param>
+      <param-name>resourceCache</param-name>
+      <param-value>resourceCache</param-value>
+    </init-param>
+    -->
     <!--
     <init-param>
       <param-name>cacheControl</param-name>
@@ -154,20 +189,23 @@
     </init-param>
     -->
     <load-on-startup>0</load-on-startup>
-  </servlet> 
+  </servlet>
 
-  <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
-  
+  <servlet-mapping>
+    <servlet-name>default</servlet-name>
+    <url-pattern>/</url-pattern>
+  </servlet-mapping>
+
 
   <!-- ==================================================================== -->
   <!-- JSP Servlet                                                          -->
-  <!-- This is the jasper JSP servlet from the jakarta project              -->
+  <!-- This is the jasper JSP servlet.                                      -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <!-- The JSP page compiler and execution servlet, which is the mechanism  -->
-  <!-- used by Glassfish to support JSP pages.  Traditionally, this servlet -->
-  <!-- is mapped to URL patterh "*.jsp".  This servlet supports the         -->
-  <!-- following initialization parameters (default values are in square    -->
-  <!-- brackets):                                                           -->
+  <!-- used by the jsp container to support JSP pages.  Traditionally,      -->
+  <!-- this servlet is mapped to URL pattern "*.jsp".  This servlet         -->
+  <!-- supports the following initialization parameters (default values     -->
+  <!-- are in square brackets):                                             -->
   <!--                                                                      -->
   <!--   checkInterval       If development is false and reloading is true, -->
   <!--                       background compiles are enabled. checkInterval -->
@@ -175,7 +213,7 @@
   <!--                       if a JSP page needs to be recompiled. [300]    -->
   <!--                                                                      -->
   <!--   compiler            Which compiler Ant should use to compile JSP   -->
-  <!--                       pages.  See the Ant documenation for more      -->
+  <!--                       pages.  See the Ant documentation for more     -->
   <!--                       information. [javac]                           -->
   <!--                                                                      -->
   <!--   classdebuginfo      Should the class file be compiled with         -->
@@ -236,28 +274,29 @@
   <!--   xpoweredBy          Determines whether X-Powered-By response       -->
   <!--                       header is added by generated servlet  [false]  -->
   <!--                                                                      -->
-  <!-- If you wish to use Jikes to compile JSP pages:                       -->
-  <!--   Set the init parameter "compiler" to "jikes".  Define              -->
-  <!--   the property "-Dbuild.compiler.emacs=true" when starting Jetty     -->
-  <!--   to cause Jikes to emit error messages in a format compatible with  -->
-  <!--   Jasper.                                                            -->
-  <!--   If you get an error reporting that jikes can't use UTF-8 encoding, -->
-  <!--   try setting the init parameter "javaEncoding" to "ISO-8859-1".     -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <servlet id="jsp">
     <servlet-name>jsp</servlet-name>
-    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+    <servlet-class>org.eclipse.jetty.jsp.JettyJspServlet</servlet-class>
     <init-param>
-        <param-name>logVerbosityLevel</param-name>
-        <param-value>DEBUG</param-value>
+      <param-name>logVerbosityLevel</param-name>
+      <param-value>DEBUG</param-value>
     </init-param>
     <init-param>
-        <param-name>fork</param-name>
-        <param-value>false</param-value>
+      <param-name>fork</param-name>
+      <param-value>false</param-value>
     </init-param>
     <init-param>
-        <param-name>xpoweredBy</param-name>
-        <param-value>false</param-value>
+      <param-name>xpoweredBy</param-name>
+      <param-value>false</param-value>
+    </init-param>
+    <init-param>
+      <param-name>compilerTargetVM</param-name>
+      <param-value>1.7</param-value>
+    </init-param>
+    <init-param>
+      <param-name>compilerSourceVM</param-name>
+      <param-value>1.7</param-value>
     </init-param>
     <!--  
     <init-param>
@@ -268,62 +307,22 @@
     <load-on-startup>0</load-on-startup>
   </servlet>
 
-  <servlet-mapping> 
-    <servlet-name>jsp</servlet-name> 
-    <url-pattern>*.jsp</url-pattern> 
+  <servlet-mapping>
+    <servlet-name>jsp</servlet-name>
+    <url-pattern>*.jsp</url-pattern>
     <url-pattern>*.jspf</url-pattern>
     <url-pattern>*.jspx</url-pattern>
     <url-pattern>*.xsp</url-pattern>
-    <url-pattern>*.JSP</url-pattern> 
+    <url-pattern>*.JSP</url-pattern>
     <url-pattern>*.JSPF</url-pattern>
     <url-pattern>*.JSPX</url-pattern>
     <url-pattern>*.XSP</url-pattern>
   </servlet-mapping>
-  
+
+
   <!-- ==================================================================== -->
-  <!-- Dynamic Servlet Invoker.                                             -->
-  <!-- This servlet invokes anonymous servlets that have not been defined   -->
-  <!-- in the web.xml or by other means. The first element of the pathInfo  -->
-  <!-- of a request passed to the envoker is treated as a servlet name for  -->
-  <!-- an existing servlet, or as a class name of a new servlet.            -->
-  <!-- This servlet is normally mapped to /servlet/*                        -->
-  <!-- This servlet support the following initParams:                       -->
-  <!--                                                                      -->
-  <!--  nonContextServlets       If false, the invoker can only load        -->
-  <!--                           servlets from the contexts classloader.    -->
-  <!--                           This is false by default and setting this  -->
-  <!--                           to true may have security implications.    -->
-  <!--                                                                      -->
-  <!--  verbose                  If true, log dynamic loads                 -->
-  <!--                                                                      -->
-  <!--  *                        All other parameters are copied to the     -->
-  <!--                           each dynamic servlet as init parameters    -->
+  <!-- Default session configuration                                        -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
-  <!-- Uncomment for dynamic invocation
-  <servlet>
-    <servlet-name>invoker</servlet-name>
-    <servlet-class>org.eclipse.jetty.servlet.Invoker</servlet-class>
-    <init-param>
-      <param-name>verbose</param-name>
-      <param-value>false</param-value>
-    </init-param>
-    <init-param>
-      <param-name>nonContextServlets</param-name>
-      <param-value>false</param-value>
-    </init-param>
-    <init-param>
-      <param-name>dynamicParam</param-name>
-      <param-value>anyValue</param-value>
-    </init-param>
-    <load-on-startup>0</load-on-startup>
-  </servlet>
-
-  <servlet-mapping> <servlet-name>invoker</servlet-name> <url-pattern>/servlet/*</url-pattern> </servlet-mapping>
-  -->
-
-
-
-  <!-- ==================================================================== -->
   <session-config>
     <session-timeout>30</session-timeout>
   </session-config>
@@ -331,7 +330,7 @@
   <!-- ==================================================================== -->
   <!-- Default MIME mappings                                                -->
   <!-- The default MIME mappings are provided by the mime.properties        -->
-  <!-- resource in the org.eclipse.jetty.server.jar file.  Additional or modified  -->
+  <!-- resource in the jetty-http.jar file.  Additional or modified         -->
   <!-- mappings may be specified here                                       -->
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <!-- UNCOMMENT TO ACTIVATE
@@ -342,6 +341,8 @@
   -->
 
   <!-- ==================================================================== -->
+  <!-- Default welcome files                                                -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <welcome-file-list>
     <welcome-file>index.html</welcome-file>
     <welcome-file>index.htm</welcome-file>
@@ -349,48 +350,170 @@
   </welcome-file-list>
 
   <!-- ==================================================================== -->
+  <!-- Default locale encodings                                             -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <locale-encoding-mapping-list>
-    <locale-encoding-mapping><locale>ar</locale><encoding>ISO-8859-6</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>be</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>bg</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ca</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>cs</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>da</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>de</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>el</locale><encoding>ISO-8859-7</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>en</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>es</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>et</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>fi</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>fr</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>hr</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>hu</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>is</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>it</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>iw</locale><encoding>ISO-8859-8</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ja</locale><encoding>Shift_JIS</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ko</locale><encoding>EUC-KR</encoding></locale-encoding-mapping>     
-    <locale-encoding-mapping><locale>lt</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>lv</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>mk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>nl</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>no</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>pl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>pt</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ro</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>ru</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sh</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sk</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sq</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sr</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>sv</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>tr</locale><encoding>ISO-8859-9</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>uk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>zh</locale><encoding>GB2312</encoding></locale-encoding-mapping>
-    <locale-encoding-mapping><locale>zh_TW</locale><encoding>Big5</encoding></locale-encoding-mapping>   
+    <locale-encoding-mapping>
+      <locale>ar</locale>
+      <encoding>ISO-8859-6</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>be</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>bg</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ca</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>cs</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>da</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>de</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>el</locale>
+      <encoding>ISO-8859-7</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>en</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>es</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>et</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>fi</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>fr</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>hr</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>hu</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>is</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>it</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>iw</locale>
+      <encoding>ISO-8859-8</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ja</locale>
+      <encoding>Shift_JIS</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ko</locale>
+      <encoding>EUC-KR</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>lt</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>lv</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>mk</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>nl</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>no</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>pl</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>pt</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ro</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>ru</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sh</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sk</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sl</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sq</locale>
+      <encoding>ISO-8859-2</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sr</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>sv</locale>
+      <encoding>ISO-8859-1</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>tr</locale>
+      <encoding>ISO-8859-9</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>uk</locale>
+      <encoding>ISO-8859-5</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>zh</locale>
+      <encoding>GB2312</encoding>
+    </locale-encoding-mapping>
+    <locale-encoding-mapping>
+      <locale>zh_TW</locale>
+      <encoding>Big5</encoding>
+    </locale-encoding-mapping>
   </locale-encoding-mapping-list>
-  
+
+  <!-- ==================================================================== -->
+  <!-- Disable TRACE method with security constraint                        -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
   <security-constraint>
     <web-resource-collection>
       <web-resource-name>Disable TRACE</web-resource-name>
@@ -399,6 +522,13 @@
     </web-resource-collection>
     <auth-constraint/>
   </security-constraint>
-  
+  <security-constraint>
+    <web-resource-collection>
+      <web-resource-name>Enable everything but TRACE</web-resource-name>
+      <url-pattern>/</url-pattern>
+      <http-method-omission>TRACE</http-method-omission>
+    </web-resource-collection>
+  </security-constraint>
+
 </web-app>
 
diff --git a/tests/test-jmx/jmx-webapp-it/pom.xml b/tests/test-jmx/jmx-webapp-it/pom.xml
index 1c84ce2..94fbf3b 100644
--- a/tests/test-jmx/jmx-webapp-it/pom.xml
+++ b/tests/test-jmx/jmx-webapp-it/pom.xml
@@ -1,26 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-jmx-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jmx-webapp-it</artifactId>
@@ -31,17 +14,18 @@
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
     <bundle-symbolic-name>${project.groupId}.jmx.webapp.it</bundle-symbolic-name>
-    <scripts-dir>${project.basedir}/src/test/scripts</scripts-dir>
     <test-base-dir>${project.build.directory}/test-base</test-base-dir>
-    <test-home-dir>${project.build.directory}/test-home</test-home-dir>
   </properties>
   <dependencies>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-distribution</artifactId>
+      <artifactId>jetty-annotations</artifactId>
       <version>${project.version}</version>
-      <type>zip</type>
-      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-jmx</artifactId>
+      <version>${project.version}</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty.tests</groupId>
@@ -78,22 +62,6 @@
               <outputDirectory>${test-base-dir}/webapps</outputDirectory>
             </configuration>
           </execution>
-          <execution>
-            <id>unpack-jetty-distro</id>
-            <phase>process-test-resources</phase>
-            <goals>
-              <goal>unpack-dependencies</goal>
-            </goals>
-            <configuration>
-              <includeArtifactIds>jetty-distribution</includeArtifactIds>
-              <includeScope>runtime</includeScope>
-              <includeTypes>zip</includeTypes>
-              <outputAbsoluteArtifactFilename>true</outputAbsoluteArtifactFilename>
-              <outputDirectory>${test-home-dir}</outputDirectory>
-              <overWriteSnapshots>true</overWriteSnapshots>
-              <overWriteIfNewer>true</overWriteIfNewer>
-            </configuration>
-          </execution>
         </executions>
       </plugin>
       <plugin>
@@ -109,99 +77,6 @@
           </execution>
         </executions>
       </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-antrun-plugin</artifactId>
-        <version>1.7</version>
-        <executions>
-          <execution>
-            <id>start-jetty</id>
-            <phase>pre-integration-test</phase>
-            <goals>
-              <goal>run</goal>
-            </goals>
-            <configuration>
-              <target>
-                <property name="jetty.jmx.home" location="${test-home-dir}/jetty-distribution-${project.version}" />
-                <property name="jetty.jmx.base" location="${test-base-dir}" />
-                <echo>Integration Test : Setup Jetty</echo>
-                <exec executable="${run.command}" dir="${scripts-dir}" spawn="false">
-                  <arg value="${run.command.xtra}" />
-                  <arg value="${setup.script}" />
-                  <arg file="${java.home}" />
-                  <arg file="${jetty.jmx.home}" />
-                  <arg file="${jetty.jmx.base}" />
-                </exec>
-
-                <echo>Integration Test : Starting Jetty ...</echo>
-                <exec executable="${run.command}" dir="${scripts-dir}" spawn="true">
-                  <arg value="${run.command.xtra}" />
-                  <arg value="${start.script}" />
-                  <arg file="${java.home}" />
-                  <arg file="${jetty.jmx.home}" />
-                  <arg file="${jetty.jmx.base}" />
-                </exec>
-                <waitfor maxwait="5" maxwaitunit="second" checkevery="500" checkeveryunit="millisecond">
-                  <http url="http://localhost:58080/jmx-webapp/" />
-                </waitfor>
-                <echo>Integration Test : Jetty is now available</echo>
-              </target>
-            </configuration>
-          </execution>
-          <execution>
-            <id>stop-jetty</id>
-            <phase>post-integration-test</phase>
-            <goals>
-              <goal>run</goal>
-            </goals>
-            <configuration>
-              <target>
-                <property name="jetty.jmx.home" location="${test-home-dir}/jetty-distribution-${project.version}" />
-                <property name="jetty.jmx.base" location="${test-base-dir}" />
-                <echo>Integration Test : Stop Jetty</echo>
-                <exec executable="${run.command}" dir="${scripts-dir}" spawn="false">
-                  <arg value="${run.command.xtra}" />
-                  <arg value="${stop.script}" />
-                  <arg file="${java.home}" />
-                  <arg file="${jetty.jmx.home}" />
-                  <arg file="${jetty.jmx.base}" />
-                </exec>
-              </target>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
     </plugins>
   </build>
-  <profiles>
-    <profile>
-      <id>it-windows</id>
-      <activation>
-        <os>
-          <family>Windows</family>
-        </os>
-      </activation>
-      <properties>
-        <run.command>cmd</run.command>
-        <run.command.xtra>/c</run.command.xtra>
-        <start.script>start-jetty.bat</start.script>
-        <stop.script>stop-jetty.bat</stop.script>
-      </properties>
-    </profile>
-    <profile>
-      <id>it-unix</id>
-      <activation>
-        <os>
-          <family>unix</family>
-        </os>
-      </activation>
-      <properties>
-        <run.command>sh</run.command>
-        <run.command.xtra>--</run.command.xtra>
-        <setup.script>setup-jetty.sh</setup.script>
-        <start.script>start-jetty.sh</start.script>
-        <stop.script>stop-jetty.sh</stop.script>
-      </properties>
-    </profile>
-  </profiles>
 </project>
diff --git a/tests/test-jmx/jmx-webapp-it/src/test/java/org/eclipse/jetty/test/jmx/JmxIT.java b/tests/test-jmx/jmx-webapp-it/src/test/java/org/eclipse/jetty/test/jmx/JmxIT.java
index be44a78..3b9ff99 100644
--- a/tests/test-jmx/jmx-webapp-it/src/test/java/org/eclipse/jetty/test/jmx/JmxIT.java
+++ b/tests/test-jmx/jmx-webapp-it/src/test/java/org/eclipse/jetty/test/jmx/JmxIT.java
@@ -18,21 +18,32 @@
 
 package org.eclipse.jetty.test.jmx;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertThat;
 
-import java.io.IOException;
+import java.io.File;
+import java.io.InputStream;
+import java.lang.management.ManagementFactory;
+import java.net.HttpURLConnection;
+import java.net.URI;
 
-import javax.management.AttributeNotFoundException;
-import javax.management.InstanceNotFoundException;
-import javax.management.MBeanException;
 import javax.management.MBeanServerConnection;
 import javax.management.ObjectName;
-import javax.management.ReflectionException;
 import javax.management.remote.JMXConnector;
 import javax.management.remote.JMXConnectorFactory;
 import javax.management.remote.JMXServiceURL;
 
+import org.eclipse.jetty.jmx.ConnectorServer;
+import org.eclipse.jetty.jmx.MBeanContainer;
+import org.eclipse.jetty.server.NetworkConnector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.webapp.Configuration;
+import org.eclipse.jetty.webapp.WebAppContext;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -42,26 +53,78 @@
  */
 public class JmxIT
 {
-    private static JMXConnector jmxc;
-    private static MBeanServerConnection mbsc;
+    private static JMXConnector __jmxc;
+    private static MBeanServerConnection __mbsc;
+    private static Server __server;
+    private static int __port;
 
     @BeforeClass
-    public static void connectToMBeanServer() throws IOException
+    public static void connectToMBeanServer() throws Exception
     {
+        startJetty();
         JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://localhost:1099/jndi/rmi://localhost:1099/jmxrmi");
-        jmxc = JMXConnectorFactory.connect(url,null);
-        mbsc = jmxc.getMBeanServerConnection();
+        __jmxc = JMXConnectorFactory.connect(url,null);
+        __mbsc = __jmxc.getMBeanServerConnection();
     }
 
     @AfterClass
-    public static void disconnectFromMBeanServer() throws IOException
+    public static void disconnectFromMBeanServer() throws Exception
     {
-        jmxc.close();
+        stopJetty();
+        __jmxc.close();
+    }
+
+    public static void startJetty() throws Exception
+    {
+        File target = MavenTestingUtils.getTargetDir();
+        File jettyBase = new File (target, "test-base");
+        File webapps = new File (jettyBase, "webapps");
+        File war = new File (webapps, "jmx-webapp.war");
+
+        //create server instance
+        __server = new Server(0);
+         
+        //set up the webapp
+        WebAppContext context = new WebAppContext();
+        
+        context.setWar(war.getCanonicalPath());
+        context.setContextPath("/jmx-webapp");
+        
+        Configuration.ClassList classlist = Configuration.ClassList
+                .setServerDefault(__server);
+        classlist.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
+                "org.eclipse.jetty.annotations.AnnotationConfiguration");
+
+        context.setAttribute(
+                            "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
+                            ".*/javax.servlet-[^/]*\\.jar$|.*/servlet-api-[^/]*\\.jar$");
+        __server.setHandler(context);
+        
+        //set up jmx remote
+        MBeanContainer mbContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer());
+        __server.addBean(mbContainer);
+
+        JMXServiceURL serviceUrl = new JMXServiceURL("rmi", "localhost", 1099, "/jndi/rmi://localhost:1099/jmxrmi");
+        ConnectorServer jmxConnServer = new ConnectorServer(serviceUrl, "org.eclipse.jetty.jmx:name=rmiconnectorserver");
+        __server.addBean(jmxConnServer);
+
+        //start server
+        __server.start();
+        
+        //remember chosen port
+        __port = ((NetworkConnector)__server.getConnectors()[0]).getLocalPort();
+    }
+    
+    
+    public static void stopJetty () throws Exception
+    {
+        if (__server != null)
+            __server.stop();
     }
 
     private String getStringAttribute(ObjectName objName, String attrName) throws Exception
     {
-        Object val = mbsc.getAttribute(objName,attrName);
+        Object val = __mbsc.getAttribute(objName,attrName);
         assertThat(attrName,val,notNullValue());
         assertThat(attrName,val,instanceOf(String.class));
         return (String)val;
@@ -69,19 +132,31 @@
 
     private int getIntegerAttribute(ObjectName objName, String attrName) throws Exception
     {
-        Object val = mbsc.getAttribute(objName,attrName);
+        Object val = __mbsc.getAttribute(objName,attrName);
         assertThat(attrName,val,notNullValue());
         assertThat(attrName,val,instanceOf(Integer.class));
         return (Integer)val;
     }
 
     @Test
+    public void testBasic() throws Exception
+    {
+        URI serverURI = new URI("http://localhost:"+String.valueOf(__port)+"/jmx-webapp/");
+        HttpURLConnection http = (HttpURLConnection) serverURI.resolve("ping").toURL().openConnection();
+        assertThat("http response", http.getResponseCode(), is(200));
+        try(InputStream inputStream = http.getInputStream())
+        {
+            String resp = IO.toString(inputStream);
+            assertThat(resp,startsWith("Servlet Pong at "));
+        }
+    }
+    
+    @Test
     public void testObtainRunningServerVersion() throws Exception
     {
         ObjectName serverName = new ObjectName("org.eclipse.jetty.server:type=server,id=0");
         String version = getStringAttribute(serverName,"version");
-        System.err.println("Running version: " + version);
-        assertThat("Version",version,startsWith("9.2."));
+        assertThat("Version",version,startsWith("9.3."));
     }
 
     @Test
@@ -118,7 +193,7 @@
         // Get initial count
         int count = getIntegerAttribute(pingerName,"count");
         // Operations
-        Object val = mbsc.invoke(pingerName,"ping",null,null);
+        Object val = __mbsc.invoke(pingerName,"ping",null,null);
         assertThat("ping() return",val.toString(),startsWith("Pong"));
         // Attributes
         assertThat("count",getIntegerAttribute(pingerName,"count"),is(count+1));
@@ -135,7 +210,7 @@
         // Get initial count
         int count = getIntegerAttribute(echoerName,"count");
         // Operations
-        Object val = mbsc.invoke(echoerName,"echo",new Object[]{"Its Me"},new String[]{String.class.getName()});
+        Object val = __mbsc.invoke(echoerName,"echo",new Object[]{"Its Me"},new String[]{String.class.getName()});
         assertThat("echo() return",val.toString(),is("Its Me"));
         // Attributes
         assertThat("count",getIntegerAttribute(echoerName,"count"),is(count+1));
diff --git a/tests/test-jmx/jmx-webapp-it/src/test/java/org/eclipse/jetty/test/jmx/PingIT.java b/tests/test-jmx/jmx-webapp-it/src/test/java/org/eclipse/jetty/test/jmx/PingIT.java
deleted file mode 100644
index 676a582..0000000
--- a/tests/test-jmx/jmx-webapp-it/src/test/java/org/eclipse/jetty/test/jmx/PingIT.java
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.test.jmx;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.net.URI;
-
-import org.eclipse.jetty.toolchain.test.SimpleRequest;
-import org.junit.Test;
-
-/**
- * Basic tests for a simple Servlet with no JMX involved (yet)
- */
-public class PingIT
-{
-    @Test
-    public void testBasic() throws Exception
-    {
-        URI serverURI = new URI("http://localhost:58080/jmx-webapp/");
-        SimpleRequest req = new SimpleRequest(serverURI);
-        assertThat(req.getString("ping"),startsWith("Servlet Pong at "));
-    }
-}
diff --git a/tests/test-jmx/jmx-webapp-it/src/test/scripts/setup-jetty.sh b/tests/test-jmx/jmx-webapp-it/src/test/scripts/setup-jetty.sh
deleted file mode 100755
index 22beac1..0000000
--- a/tests/test-jmx/jmx-webapp-it/src/test/scripts/setup-jetty.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-
-JAVA_HOME=$1
-JETTY_HOME=$2
-JETTY_BASE=$3
-
-echo \${java.home}  : $JAVA_HOME
-echo \${jetty.home} : $JETTY_HOME
-echo \${jetty.base} : $JETTY_BASE
-
-cd "$JETTY_BASE"
-
-"$JAVA_HOME/bin/java" -jar "$JETTY_HOME/start.jar" \
-    --approve-all-licenses \
-    --add-to-start=deploy,http,annotations,jmx,jmx-remote,logging
-
-"$JAVA_HOME/bin/java" -jar "$JETTY_HOME/start.jar" \
-    --version
-
diff --git a/tests/test-jmx/jmx-webapp-it/src/test/scripts/start-jetty.sh b/tests/test-jmx/jmx-webapp-it/src/test/scripts/start-jetty.sh
deleted file mode 100755
index 1d0f9eb..0000000
--- a/tests/test-jmx/jmx-webapp-it/src/test/scripts/start-jetty.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-
-JAVA_HOME=$1
-JETTY_HOME=$2
-JETTY_BASE=$3
-
-echo \${java.home}  : $JAVA_HOME
-echo \${jetty.home} : $JETTY_HOME
-echo \${jetty.base} : $JETTY_BASE
-
-cd "$JETTY_BASE"
-
-"$JAVA_HOME/bin/java" -jar "$JETTY_HOME/start.jar" \
-    jetty.port=58080 \
-    STOP.PORT=58181 STOP.KEY=it
-
-
diff --git a/tests/test-jmx/jmx-webapp-it/src/test/scripts/stop-jetty.sh b/tests/test-jmx/jmx-webapp-it/src/test/scripts/stop-jetty.sh
deleted file mode 100755
index 32e4077..0000000
--- a/tests/test-jmx/jmx-webapp-it/src/test/scripts/stop-jetty.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-
-JAVA_HOME=$1
-JETTY_HOME=$2
-JETTY_BASE=$3
-
-cd "$JETTY_BASE"
-"$JAVA_HOME/bin/java" -jar "$JETTY_HOME/start.jar" \
- --stop STOP.PORT=58181 STOP.KEY=it
-
-
diff --git a/tests/test-jmx/jmx-webapp/pom.xml b/tests/test-jmx/jmx-webapp/pom.xml
index a5adc61..c5cd9bf 100644
--- a/tests/test-jmx/jmx-webapp/pom.xml
+++ b/tests/test-jmx/jmx-webapp/pom.xml
@@ -1,27 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-  // ========================================================================
-  // Copyright (c) Webtide LLC
-  //
-  // All rights reserved. This program and the accompanying materials
-  // are made available under the terms of the Eclipse Public License v1.0
-  // and Apache License v2.0 which accompanies this distribution.
-  //
-  // The Eclipse Public License is available at
-  // http://www.eclipse.org/legal/epl-v10.html
-  //
-  // The Apache License v2.0 is available at
-  // http://www.apache.org/licenses/LICENSE-2.0.txt
-  //
-  // You may elect to redistribute this code under either of these licenses.
-  // ========================================================================
--->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-jmx-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <artifactId>jmx-webapp</artifactId>
   <packaging>war</packaging>
diff --git a/tests/test-jmx/pom.xml b/tests/test-jmx/pom.xml
index 1644913..a45d7aa 100644
--- a/tests/test-jmx/pom.xml
+++ b/tests/test-jmx/pom.xml
@@ -1,26 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>test-jmx-parent</artifactId>
diff --git a/tests/test-loginservice/pom.xml b/tests/test-loginservice/pom.xml
index 78832dd..bf3a781 100644
--- a/tests/test-loginservice/pom.xml
+++ b/tests/test-loginservice/pom.xml
@@ -1,31 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <artifactId>test-loginservice</artifactId>
   <name>Jetty Tests :: Login Service</name>
   <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.loginservice</bundle-symbolic-name>
+  </properties>
  <dependencies>
 	<dependency>
 		<groupId>org.eclipse.jetty</groupId>
diff --git a/tests/test-loginservice/src/test/java/org/eclipse/jetty/DataSourceLoginServiceTest.java b/tests/test-loginservice/src/test/java/org/eclipse/jetty/DataSourceLoginServiceTest.java
index a484e34..b1e0450 100644
--- a/tests/test-loginservice/src/test/java/org/eclipse/jetty/DataSourceLoginServiceTest.java
+++ b/tests/test-loginservice/src/test/java/org/eclipse/jetty/DataSourceLoginServiceTest.java
@@ -19,8 +19,10 @@
 
 package org.eclipse.jetty;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 
 import java.io.File;
 import java.io.FileOutputStream;
diff --git a/tests/test-loginservice/src/test/java/org/eclipse/jetty/JdbcLoginServiceTest.java b/tests/test-loginservice/src/test/java/org/eclipse/jetty/JdbcLoginServiceTest.java
index 875bfb7..46aa879 100644
--- a/tests/test-loginservice/src/test/java/org/eclipse/jetty/JdbcLoginServiceTest.java
+++ b/tests/test-loginservice/src/test/java/org/eclipse/jetty/JdbcLoginServiceTest.java
@@ -20,8 +20,10 @@
 
 
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -158,6 +160,21 @@
              stopClient();
          }
      }
+     
+     @Test
+     public void testGetNonExistantUser () throws Exception
+     {
+         try
+         {
+             startClient("foo", "bar");
+             ContentResponse response = _client.GET(_baseUri.resolve("input.txt"));
+             assertEquals(HttpServletResponse.SC_UNAUTHORIZED,response.getStatus());
+         }
+         finally
+         {
+             stopClient();
+         }
+     }
 
      //Head requests to jetty-client are not working: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=394552
      @Ignore
@@ -202,12 +219,17 @@
      protected void startClient()
          throws Exception
      {
+         startClient("jetty", "jetty");
+     }
+     
+     protected void startClient(String user, String pwd) throws Exception
+     {
          _client = new HttpClient();
          QueuedThreadPool executor = new QueuedThreadPool();
          executor.setName(executor.getName() + "-client");
          _client.setExecutor(executor);
          AuthenticationStore authStore = _client.getAuthenticationStore();
-         authStore.addAuthentication(new BasicAuthentication(_baseUri, __realm, "jetty", "jetty"));
+         authStore.addAuthentication(new BasicAuthentication(_baseUri, __realm, user, pwd));
          _client.start();
      }
 
diff --git a/tests/test-quickstart/pom.xml b/tests/test-quickstart/pom.xml
index 52d4507..cf29d76 100644
--- a/tests/test-quickstart/pom.xml
+++ b/tests/test-quickstart/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
@@ -11,6 +11,9 @@
   <name>Test :: Jetty Quick Start</name>
   <description>Jetty Quick Start Test</description>
   <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.tests.quickstart</bundle-symbolic-name>
+  </properties>
   <dependencies>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
diff --git a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/AttributeNormalizerTest.java b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/AttributeNormalizerTest.java
new file mode 100644
index 0000000..bf49e17
--- /dev/null
+++ b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/AttributeNormalizerTest.java
@@ -0,0 +1,258 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.quickstart;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.OS;
+import org.eclipse.jetty.util.resource.Resource;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AttributeNormalizerTest
+{
+    @Parameterized.Parameters(name = "[{index}] {0} - {1}")
+    public static List<Object[]> data()
+    {
+        List<Object[]> data = new ArrayList<>();
+    
+        String arch = String.format("%s/%s", System.getProperty("os.name"), System.getProperty("os.arch"));
+        
+        String title;
+        Map<String, String> env;
+        
+        // ------
+        title = "Typical Setup";
+        
+        env = new HashMap<>();
+        env.put("jetty.home", asTargetPath(title,"jetty-distro"));
+        env.put("jetty.base", asTargetPath(title,"jetty-distro/demo.base"));
+        env.put("WAR", asTargetPath(title,"jetty-distro/demo.base/webapps/FOO"));
+        
+        data.add(new Object[]{arch, title, env});
+        
+        // ------
+        // This puts the jetty.home inside of the jetty.base
+        title = "Overlap Setup";
+        env = new HashMap<>();
+        env.put("jetty.home", asTargetPath(title,"app/dist"));
+        env.put("jetty.base", asTargetPath(title,"app"));
+        env.put("WAR", asTargetPath(title,"app/webapps/FOO"));
+        
+        data.add(new Object[]{arch, title, env});
+        
+        // ------
+        // This tests a path scenario often seen on various automatic deployments tooling
+        // such as Kubernetes, CircleCI, TravisCI, and Jenkins.
+        title = "Nasty Path Setup";
+        env = new HashMap<>();
+        env.put("jetty.home", asTargetPath(title,"app%2Fnasty/dist"));
+        env.put("jetty.base", asTargetPath(title,"app%2Fnasty/base"));
+        env.put("WAR", asTargetPath(title,"app%2Fnasty/base/webapps/FOO"));
+        
+        data.add(new Object[]{arch, title, env});
+        return data;
+    }
+    
+    private static final String asTargetPath(String title, String subpath)
+    {
+        Path rootPath = MavenTestingUtils.getTargetTestingPath(title);
+        FS.ensureDirExists(rootPath);
+        Path path = rootPath.resolve(OS.separators(subpath));
+        FS.ensureDirExists(path);
+        
+        return path.toString();
+    }
+    
+    private Map<String, String> oldValues = new HashMap<>();
+    private final String jettyHome;
+    private final String jettyBase;
+    private final String war;
+    private final String arch;
+    private final String title;
+    private final Map<String, String> env;
+    private final AttributeNormalizer normalizer;
+    
+    public AttributeNormalizerTest(String arch, String title, Map<String, String> env) throws IOException
+    {
+        this.arch = arch;
+        this.title = title;
+        this.env = env;
+        
+        // Remember old values
+        env.keySet().stream().forEach((key) ->
+        {
+            String old = System.getProperty(key);
+            oldValues.put(key, old);
+        });
+        
+        // Grab specific values of interest in general
+        jettyHome = env.get("jetty.home");
+        jettyBase = env.get("jetty.base");
+        war = env.get("WAR");
+        
+        // Set environment (skipping null and WAR)
+        env.entrySet().stream()
+                .filter((e) -> e.getValue() != null && !e.getKey().equalsIgnoreCase("WAR"))
+                .forEach((entry) -> System.setProperty(entry.getKey(), entry.getValue()));
+        
+        // Setup normalizer
+        Resource webresource = Resource.newResource(war);
+        this.normalizer = new AttributeNormalizer(webresource);
+    }
+    
+    @After
+    public void restoreEnv()
+    {
+        // Restore old values
+        oldValues.entrySet().stream().forEach((entry) ->
+                EnvUtils.restoreSystemProperty(entry.getKey(), entry.getValue())
+        );
+    }
+    
+    private void assertNormalize(Object o, String expected)
+    {
+        String result = normalizer.normalize(o);
+        assertThat("normalize((" + o.getClass().getSimpleName() + ") " + o.toString() + ")",
+                result, is(expected));
+    }
+    
+    private void assertExpandPath(String line, String expected)
+    {
+        String result = normalizer.expand(line);
+        
+        // Treat output as strings
+        assertThat("expand('" + line + "')", result, is(expected));
+    }
+    
+    private void assertExpandURI(String line, URI expected)
+    {
+        String result = normalizer.expand(line);
+        
+        URI resultURI = URI.create(result);
+        assertThat("expand('" + line + "')", resultURI.getScheme(), is(expected.getScheme()));
+        assertThat("expand('" + line + "')", resultURI.getPath(), is(expected.getPath()));
+    }
+    
+    @Test
+    public void testNormalizeWarAsString()
+    {
+        // Normalize WAR as String path
+        assertNormalize(war, war); // only URL, File, URI are supported
+    }
+    
+    @Test
+    public void testNormalizeJettyBaseAsFile()
+    {
+        // Normalize jetty.base as File path
+        assertNormalize(new File(jettyBase), "${jetty.base}");
+    }
+    
+    @Test
+    public void testNormalizeJettyHomeAsFile()
+    {
+        // Normalize jetty.home as File path
+        assertNormalize(new File(jettyHome), "${jetty.home}");
+    }
+    
+    @Test
+    public void testNormalizeJettyBaseAsURI()
+    {
+        // Normalize jetty.base as URI path
+        assertNormalize(new File(jettyBase).toURI(), "${jetty.base.uri}");
+    }
+    
+    @Test
+    public void testNormalizeJettyHomeAsURI()
+    {
+        // Normalize jetty.home as URI path
+        assertNormalize(new File(jettyHome).toURI(), "${jetty.home.uri}");
+    }
+    
+    @Test
+    public void testExpandJettyBase()
+    {
+        // Expand jetty.base
+        assertExpandPath("${jetty.base}", jettyBase);
+    }
+    
+    @Test
+    public void testExpandJettyHome()
+    {
+        // Expand jetty.home
+        assertExpandPath("${jetty.home}", jettyHome);
+    }
+    
+    @Test
+    public void testNormalizeWarAsURI()
+    {
+        // Normalize WAR as URI
+        URI testWarURI = new File(war).toURI();
+        assertNormalize(testWarURI, "${WAR.uri}");
+    }
+    
+    @Test
+    public void testNormalizeWarDeepAsFile()
+    {
+        // Normalize WAR deep path as File
+        File testWarDeep = new File(new File(war), OS.separators("deep/ref")).getAbsoluteFile();
+        assertNormalize(testWarDeep, "${WAR.path}" + OS.separators("/deep/ref"));
+    }
+    
+    @Test
+    public void testNormalizeWarDeepAsString()
+    {
+        // Normalize WAR deep path as String
+        File testWarDeep = new File(new File(war), OS.separators("deep/ref")).getAbsoluteFile();
+        assertNormalize(testWarDeep.toString(), testWarDeep.toString());
+    }
+    
+    @Test
+    public void testNormalizeWarDeepAsURI()
+    {
+        // Normalize WAR deep path as URI
+        File testWarDeep = new File(new File(war), OS.separators("deep/ref")).getAbsoluteFile();
+        assertNormalize(testWarDeep.toURI(), "${WAR.uri}/deep/ref");
+    }
+    
+    @Test
+    public void testExpandWarDeep()
+    {
+        // Expand WAR deep path
+        File testWarDeep = new File(new File(war), OS.separators("deep/ref"));
+        URI uri = URI.create("jar:" + testWarDeep.toURI().toASCIIString() + "!/other/file");
+        assertExpandURI("jar:${WAR.uri}/deep/ref!/other/file", uri);
+    }
+}
+
diff --git a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/EnvUtils.java b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/EnvUtils.java
new file mode 100644
index 0000000..418dea6
--- /dev/null
+++ b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/EnvUtils.java
@@ -0,0 +1,76 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.quickstart;
+
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * Common utility methods for quickstart tests
+ */
+public class EnvUtils
+{
+    /**
+     * As the declared paths in this testcase might be actual paths on the system
+     * running these tests, the expected paths should be cleaned up to represent
+     * the actual system paths.
+     * <p>
+     * Eg: on fedora /etc/init.d is a symlink to /etc/rc.d/init.d
+     */
+    public static String toSystemPath(String rawpath)
+    {
+        Path path = FileSystems.getDefault().getPath(rawpath);
+        if (Files.exists(path))
+        {
+            // It exists, resolve it to the real path
+            try
+            {
+                path = path.toRealPath();
+            }
+            catch (IOException e)
+            {
+                // something prevented us from resolving to real path, fallback to
+                // absolute path resolution (not as accurate)
+                path = path.toAbsolutePath();
+                e.printStackTrace();
+            }
+        }
+        else
+        {
+            // File doesn't exist, resolve to absolute path
+            // We can't rely on File.toCanonicalPath() here
+            path = path.toAbsolutePath();
+        }
+        return path.toString();
+    }
+    
+    public static void restoreSystemProperty(String key, String value)
+    {
+        if (value == null)
+        {
+            System.clearProperty(key);
+        }
+        else
+        {
+            System.setProperty(key, value);
+        }
+    }
+}
diff --git a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureJNDIWar.java b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureJNDIWar.java
index c8629dd..02d026c 100644
--- a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureJNDIWar.java
+++ b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureJNDIWar.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.quickstart;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jetty.server.Server;
diff --git a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureSpecWar.java b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureSpecWar.java
index 13dc303..e069a86 100644
--- a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureSpecWar.java
+++ b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureSpecWar.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.quickstart;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jetty.server.Server;
diff --git a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureStandardTestWar.java b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureStandardTestWar.java
index 630aaf6..12a4a38 100644
--- a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureStandardTestWar.java
+++ b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureStandardTestWar.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.quickstart;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jetty.server.Server;
diff --git a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartTest.java b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartTest.java
index b18b0f7..24c1385 100644
--- a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartTest.java
+++ b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartTest.java
@@ -160,7 +160,7 @@
         if (contextXml != null)
         {
             // System.err.println("Applying "+contextXml);
-            XmlConfiguration xmlConfiguration = new XmlConfiguration(contextXml.getURL());  
+            XmlConfiguration xmlConfiguration = new XmlConfiguration(contextXml.getURI().toURL());
             xmlConfiguration.configure(webapp);   
         }
         
diff --git a/tests/test-quickstart/src/test/resources/jetty-logging.properties b/tests/test-quickstart/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..aec748c
--- /dev/null
+++ b/tests/test-quickstart/src/test/resources/jetty-logging.properties
@@ -0,0 +1,3 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+org.eclipse.jetty.LEVEL=INFO
+# org.eclipse.jetty.quickstart.AttributeNormalizer.LEVEL=DEBUG
diff --git a/tests/test-quickstart/src/test/resources/test-jndi.xml b/tests/test-quickstart/src/test/resources/test-jndi.xml
index 14c0934..5f4ab4f 100644
--- a/tests/test-quickstart/src/test/resources/test-jndi.xml
+++ b/tests/test-quickstart/src/test/resources/test-jndi.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Configure the test-jndi webapp                                  -->
diff --git a/tests/test-quickstart/src/test/resources/test-spec.xml b/tests/test-quickstart/src/test/resources/test-spec.xml
index 99fc577..a136d28 100644
--- a/tests/test-quickstart/src/test/resources/test-spec.xml
+++ b/tests/test-quickstart/src/test/resources/test-spec.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
 
diff --git a/tests/test-quickstart/src/test/resources/test.xml b/tests/test-quickstart/src/test/resources/test.xml
index 41eba0c..8515656 100644
--- a/tests/test-quickstart/src/test/resources/test.xml
+++ b/tests/test-quickstart/src/test/resources/test.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"  encoding="ISO-8859-1"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- ==================================================================
 Configure and deploy the test web application in $(jetty.home)/webapps/test
diff --git a/tests/test-sessions/pom.xml b/tests/test-sessions/pom.xml
index b8fc379..f474eeb 100644
--- a/tests/test-sessions/pom.xml
+++ b/tests/test-sessions/pom.xml
@@ -1,27 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <artifactId>test-sessions-parent</artifactId>
   <name>Jetty Tests :: Sessions :: Parent</name>
@@ -33,7 +16,9 @@
     <module>test-sessions-common</module>
     <module>test-hash-sessions</module>
     <module>test-jdbc-sessions</module>
-    <!-- Requires mongodb server running -->
     <module>test-mongodb-sessions</module>
+    <module>test-infinispan-sessions</module>
+    <module>test-gcloud-sessions</module>
+    <module>test-gcloud-memcached-sessions</module>
   </modules>
 </project>
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/pom.xml b/tests/test-sessions/test-gcloud-memcached-sessions/pom.xml
new file mode 100644
index 0000000..5ff02c0
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/pom.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.eclipse.jetty.tests</groupId>
+    <artifactId>test-sessions-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+  <artifactId>test-gcloud-memcached-sessions</artifactId>
+  <name>Jetty Tests :: Sessions :: GCloud with Memcached</name>
+  <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.sessions.gcloud.memcached</bundle-symbolic-name>
+  </properties>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <!-- DO NOT DEPLOY (or Release) -->
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <skipTests>true</skipTests>
+          <test>GCloudMemcachedTestSuite</test>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+       <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-webapp</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+	<dependency>
+		<groupId>org.eclipse.jetty</groupId>
+		<artifactId>jetty-client</artifactId>
+		<version>${project.version}</version>
+	</dependency>
+	<dependency>
+		<groupId>org.eclipse.jetty.tests</groupId>
+		<artifactId>test-sessions-common</artifactId>
+		<version>${project.version}</version>
+	</dependency>
+	<dependency>
+		<groupId>org.eclipse.jetty.gcloud</groupId>
+            <artifactId>jetty-gcloud-memcached-session-manager</artifactId>
+            <version>${project.version}</version>
+         </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <version>1.7.9</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <profiles>
+      <profile>
+        <id>gcloud</id>
+        <activation>
+          <property>
+            <name>gcloud.enabled</name>
+            <value>true</value>
+          </property>
+        </activation>
+        <build>
+          <plugins>
+            <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-surefire-plugin</artifactId>
+              <configuration>
+                <skipTests>false</skipTests>
+                <systemPropertyVariables>
+                  <DATASTORE_DATASET>jetty9-work</DATASTORE_DATASET>
+                  <DATASTORE_HOST>http://localhost:8088</DATASTORE_HOST>
+                </systemPropertyVariables>
+              </configuration>
+            </plugin>
+          </plugins>
+        </build>
+      </profile>
+  </profiles>
+
+</project>
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ClientCrossContextSessionTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ClientCrossContextSessionTest.java
new file mode 100644
index 0000000..b64e706
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ClientCrossContextSessionTest.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractClientCrossContextSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.Test;
+
+/**
+ * ClientCrossContextSessionTest
+ *
+ *
+ */
+public class ClientCrossContextSessionTest extends AbstractClientCrossContextSessionTest
+{
+
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractClientCrossContextSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return new GCloudMemcachedTestServer(port);
+    }
+
+    @Test
+    @Override
+    public void testCrossContextDispatch() throws Exception
+    {
+        super.testCrossContextDispatch();
+    }
+
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ForwardedSessionTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ForwardedSessionTest.java
new file mode 100644
index 0000000..40dc7f3
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ForwardedSessionTest.java
@@ -0,0 +1,51 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractForwardedSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * ForwardedSessionTest
+ *
+ *
+ */
+public class ForwardedSessionTest extends AbstractForwardedSessionTest
+{
+
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractForwardedSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+       return new GCloudMemcachedTestServer(port);
+    }
+
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/GCloudMemcachedSessionTestSupport.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/GCloudMemcachedSessionTestSupport.java
new file mode 100644
index 0000000..fa4d44f
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/GCloudMemcachedSessionTestSupport.java
@@ -0,0 +1,187 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+
+import org.eclipse.jetty.gcloud.session.GCloudSessionManager;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.resource.JarResource;
+import org.eclipse.jetty.util.resource.Resource;
+
+import com.google.api.client.util.Strings;
+import com.google.cloud.datastore.Datastore;
+import com.google.cloud.datastore.DatastoreFactory;
+import com.google.cloud.datastore.DatastoreOptions;
+import com.google.cloud.datastore.Entity;
+import com.google.cloud.datastore.GqlQuery;
+import com.google.cloud.datastore.Key;
+import com.google.cloud.datastore.ProjectionEntity;
+import com.google.cloud.datastore.Query;
+import com.google.cloud.datastore.Query.ResultType;
+import com.google.cloud.datastore.QueryResults;
+import com.google.cloud.datastore.StructuredQuery;
+import com.google.cloud.datastore.testing.LocalDatastoreHelper;
+
+import net.rubyeye.xmemcached.MemcachedClient;
+import net.rubyeye.xmemcached.XMemcachedClientBuilder;
+
+/**
+ * GCloudMemcachedSessionTestSupport
+ *
+ *
+ */
+public class GCloudMemcachedSessionTestSupport
+{
+
+    /**
+     * MemcachedFlusher
+     *
+     *
+     */
+    public static class MemcachedFlusher 
+    {
+        protected XMemcachedClientBuilder _builder;
+        protected MemcachedClient _client;
+
+
+        public MemcachedFlusher() throws Exception
+        {        
+            _builder = new XMemcachedClientBuilder("localhost:11211");
+            _client = _builder.build();
+        }
+        
+        public void flush () throws Exception
+        {
+            _client.flushAllWithNoReply();
+        }
+    }
+    
+    
+    MemcachedFlusher _flusher;
+    LocalDatastoreHelper _helper = LocalDatastoreHelper.create(1.0);
+    Datastore _ds;
+    
+   
+    
+    public GCloudMemcachedSessionTestSupport ()
+    {
+        DatastoreOptions options = _helper.options();
+        _ds = options.service();
+    }
+
+    
+    public Datastore getDatastore()
+    {
+        return _ds;
+    }
+    
+    public void setUp()
+    throws Exception
+    {
+        _helper.start();
+        _flusher = new MemcachedFlusher();
+    }
+    
+    
+   
+    
+    public void tearDown()
+    throws Exception
+    {
+        _helper.stop();
+        _flusher.flush();
+    }
+
+    
+    public void listSessions () throws Exception
+    {
+        GqlQuery.Builder builder = Query.gqlQueryBuilder(ResultType.ENTITY, "select * from "+GCloudSessionManager.KIND);
+       
+        Query<Entity> query = builder.build();
+    
+        QueryResults<Entity> results = _ds.run(query);
+        assertNotNull(results);
+        System.err.println("SESSIONS::::::::");
+        while (results.hasNext())
+        {
+            
+            Entity e = results.next();
+            System.err.println(e.getString("clusterId")+" expires at "+e.getLong("expiry"));
+        }
+        System.err.println("END OF SESSIONS::::::::");
+    }
+    
+   
+    public void assertSessions(int count) throws Exception
+    {
+        Query<Key> query = Query.keyQueryBuilder().kind(GCloudSessionManager.KIND).build();
+        QueryResults<Key> results = _ds.run(query);
+        assertNotNull(results);
+        int actual = 0;
+        while (results.hasNext())
+        { 
+            results.next();
+            ++actual;
+        }       
+        assertEquals(count, actual);
+    }
+
+    public void deleteSessions () throws Exception
+    {
+
+        _flusher.flush();
+        Query<Key> query = Query.keyQueryBuilder().kind(GCloudSessionManager.KIND).build();
+        QueryResults<Key> results = _ds.run(query);
+
+        if (results != null)
+        {
+            List<Key> keys = new ArrayList<Key>();
+
+            while (results.hasNext())
+            { 
+                keys.add(results.next());
+            }
+
+            _ds.delete(keys.toArray(new Key[keys.size()]));
+
+        }
+
+        assertSessions(0);
+    }
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/GCloudMemcachedTestServer.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/GCloudMemcachedTestServer.java
new file mode 100644
index 0000000..94e0a2e
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/GCloudMemcachedTestServer.java
@@ -0,0 +1,97 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+
+import org.eclipse.jetty.gcloud.session.GCloudSessionIdManager;
+import org.eclipse.jetty.server.SessionIdManager;
+import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.SessionHandler;
+
+/**
+ * GCloudMemachedTestServer
+ *
+ *
+ */
+public class GCloudMemcachedTestServer extends AbstractTestServer
+{
+    static int __workers=0;
+    public static int STALE_INTERVAL_SEC = 1;
+ 
+    
+    
+    /**
+     * @param port
+     * @param maxInactivePeriod
+     * @param scavengePeriod
+     */
+    public GCloudMemcachedTestServer(int port, int maxInactivePeriod, int scavengePeriod)
+    {
+        super(port, maxInactivePeriod, scavengePeriod);
+    }
+
+    /**
+     * @param port
+     */
+    public GCloudMemcachedTestServer(int port)
+    {
+        super(port, 30,10);
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionIdManager(java.lang.Object)
+     */
+    @Override
+    public SessionIdManager newSessionIdManager(Object config)
+    {
+        GCloudSessionIdManager idManager = new GCloudSessionIdManager(getServer());
+        idManager.setWorkerName("w"+(__workers++));
+        return idManager;
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionManager()
+     */
+    @Override
+    public SessionManager newSessionManager()
+    {
+        GCloudMemcachedSessionManager sessionManager = new GCloudMemcachedSessionManager();
+        sessionManager.setSessionIdManager((GCloudSessionIdManager)_sessionIdManager);
+        sessionManager.setStaleIntervalSec(STALE_INTERVAL_SEC);
+        sessionManager.setScavengeIntervalSec(_scavengePeriod);
+        sessionManager.setDatastore(GCloudMemcachedTestSuite.__testSupport.getDatastore());
+        sessionManager.setExpirySec(0);
+        sessionManager.setHost("localhost");
+        sessionManager.setPort("11211");
+        return sessionManager;
+        
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionHandler(org.eclipse.jetty.server.SessionManager)
+     */
+    @Override
+    public SessionHandler newSessionHandler(SessionManager sessionManager)
+    {
+        return new SessionHandler(sessionManager);
+    }
+
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/GCloudMemcachedTestSuite.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/GCloudMemcachedTestSuite.java
new file mode 100644
index 0000000..3a05354
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/GCloudMemcachedTestSuite.java
@@ -0,0 +1,72 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * GCloudMemcachedTestSuite
+ *
+ * Sets up the gcloud emulator once before running all tests.
+ *
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+    ClientCrossContextSessionTest.class,
+    ForwardedSessionTest.class,
+    ImmortalSessionTest.class,
+    InvalidationSessionTest.class,
+    LastAccessTimeTest.class,
+    LocalSessionScavengingTest.class,
+    NewSessionTest.class,
+    OrphanedSessionTest.class,
+    ReentrantRequestSessionTest.class,
+    RemoveSessionTest.class,
+    SameNodeLoadTest.class,
+    ServerCrossContextSessionTest.class,
+    SessionExpiryTest.class,
+    SessionInvalidateAndCreateTest.class,
+    SessionMigrationTest.class,
+    SessionRenewTest.class,
+    SessionValueSavingTest.class,
+    StopSessionManagerPreserveSessionTest.class
+})
+
+
+public class GCloudMemcachedTestSuite
+{
+    public static GCloudMemcachedSessionTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setUp () throws Exception
+    {
+        __testSupport = new GCloudMemcachedSessionTestSupport();
+        __testSupport.setUp();
+    }
+    
+    @AfterClass
+    public static void tearDown () throws Exception
+    {
+        __testSupport.tearDown();
+    }
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ImmortalSessionTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ImmortalSessionTest.java
new file mode 100644
index 0000000..ae5b6fe
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ImmortalSessionTest.java
@@ -0,0 +1,59 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractImmortalSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * ImmortalSessionTest
+ *
+ *
+ */
+public class ImmortalSessionTest extends AbstractImmortalSessionTest
+{
+   
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractImmortalSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int maxInactiveMs, int scavengeMs)
+    {
+       return new GCloudMemcachedTestServer(port, port, scavengeMs);
+    }
+
+    @Test
+    @Override
+    public void testImmortalSession() throws Exception
+    {
+        super.testImmortalSession();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/InvalidationSessionTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/InvalidationSessionTest.java
new file mode 100644
index 0000000..da3b14c
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/InvalidationSessionTest.java
@@ -0,0 +1,70 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractInvalidationSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * InvalidationSessionTest
+ *
+ *
+ */
+public class InvalidationSessionTest extends AbstractInvalidationSessionTest
+{
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return new GCloudMemcachedTestServer(port);
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#pause()
+     */
+    @Override
+    public void pause()
+    {
+        //This test moves around a session between 2 nodes. After it is invalidated on the 1st node,
+        //it will still be in the memory of the 2nd node. We need to wait until after the stale time
+        //has expired on node2 for it to reload the session and discover it has been deleted.
+        try
+        {
+            Thread.currentThread().sleep((2*GCloudMemcachedTestServer.STALE_INTERVAL_SEC)*1000);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        
+    }
+
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/LastAccessTimeTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/LastAccessTimeTest.java
new file mode 100644
index 0000000..c208614
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/LastAccessTimeTest.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractLastAccessTimeTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * LastAccessTimeTest
+ *
+ *
+ */
+public class LastAccessTimeTest extends AbstractLastAccessTimeTest
+{
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractLastAccessTimeTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudMemcachedTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testLastAccessTime() throws Exception
+    {
+        super.testLastAccessTime();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/LocalSessionScavengingTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/LocalSessionScavengingTest.java
new file mode 100644
index 0000000..356565f
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/LocalSessionScavengingTest.java
@@ -0,0 +1,59 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractLocalSessionScavengingTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * LocalSessionScavengingTest
+ *
+ *
+ */
+public class LocalSessionScavengingTest extends AbstractLocalSessionScavengingTest
+{
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractLocalSessionScavengingTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudMemcachedTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testLocalSessionsScavenging() throws Exception
+    {
+        super.testLocalSessionsScavenging();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/NewSessionTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/NewSessionTest.java
new file mode 100644
index 0000000..c359055
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/NewSessionTest.java
@@ -0,0 +1,63 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import java.io.File;
+
+import org.eclipse.jetty.server.session.AbstractNewSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * NewSessionTest
+ *
+ *
+ */
+public class NewSessionTest extends AbstractNewSessionTest
+{
+
+    
+    @After
+    public void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractNewSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+       return new GCloudMemcachedTestServer(port, max, scavenge);
+    }
+
+    @Test
+    public void testNewSession() throws Exception
+    {
+        super.testNewSession();
+    }
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/OrphanedSessionTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/OrphanedSessionTest.java
new file mode 100644
index 0000000..ebc6469
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/OrphanedSessionTest.java
@@ -0,0 +1,63 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractOrphanedSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * OrphanedSessionTest
+ *
+ *
+ */
+public class OrphanedSessionTest extends AbstractOrphanedSessionTest
+{
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractOrphanedSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudMemcachedTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testOrphanedSession() throws Exception
+    {
+        super.testOrphanedSession();
+        GCloudMemcachedTestSuite.__testSupport.assertSessions(0);
+    }
+    
+    
+    
+
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ReentrantRequestSessionTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ReentrantRequestSessionTest.java
new file mode 100644
index 0000000..bac9cb9
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ReentrantRequestSessionTest.java
@@ -0,0 +1,60 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractReentrantRequestSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * ReentrantRequestSessionTest
+ *
+ *
+ */
+public class ReentrantRequestSessionTest extends AbstractReentrantRequestSessionTest
+{
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractReentrantRequestSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return  new GCloudMemcachedTestServer(port);
+    }
+
+    @Test
+    @Override
+    public void testReentrantRequestSession() throws Exception
+    {
+        super.testReentrantRequestSession();
+    }
+    
+    
+
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/RemoveSessionTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/RemoveSessionTest.java
new file mode 100644
index 0000000..64a49ff
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/RemoveSessionTest.java
@@ -0,0 +1,63 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractRemoveSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * RemoveSessionTest
+ *
+ *
+ */
+public class RemoveSessionTest extends AbstractRemoveSessionTest
+{
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractRemoveSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    { 
+        return new GCloudMemcachedTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testRemoveSession() throws Exception
+    {
+        super.testRemoveSession();
+    }
+    
+    
+
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SameNodeLoadTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SameNodeLoadTest.java
new file mode 100644
index 0000000..627be49
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SameNodeLoadTest.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractSameNodeLoadTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SameNodeLoadTest
+ *
+ *
+ */
+public class SameNodeLoadTest extends AbstractSameNodeLoadTest
+{
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSameNodeLoadTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return  new GCloudMemcachedTestServer(port);
+    }
+
+    @Test
+    @Override
+    public void testLoad() throws Exception
+    {
+        super.testLoad();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ServerCrossContextSessionTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ServerCrossContextSessionTest.java
new file mode 100644
index 0000000..d344f15
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/ServerCrossContextSessionTest.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractServerCrossContextSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * ServerCrossContextSessionTest
+ *
+ *
+ */
+public class ServerCrossContextSessionTest extends AbstractServerCrossContextSessionTest
+{
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractServerCrossContextSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return  new GCloudMemcachedTestServer(port); 
+    }
+
+    @Test
+    @Override
+    public void testCrossContextDispatch() throws Exception
+    {
+        super.testCrossContextDispatch();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionExpiryTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionExpiryTest.java
new file mode 100644
index 0000000..b31d3ff
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionExpiryTest.java
@@ -0,0 +1,89 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractSessionExpiryTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SessionExpiryTest
+ *
+ *
+ */
+public class SessionExpiryTest extends AbstractSessionExpiryTest
+{
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionExpiryTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudMemcachedTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testSessionNotExpired() throws Exception
+    {
+        super.testSessionNotExpired();
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionExpiryTest#testSessionExpiry()
+     */
+    @Test
+    @Override
+    public void testSessionExpiry() throws Exception
+    {
+        super.testSessionExpiry();
+        GCloudMemcachedTestSuite.__testSupport.assertSessions(0);
+    }
+
+    @Override
+    public void verifySessionCreated(TestHttpSessionListener listener, String sessionId)
+    {
+        super.verifySessionCreated(listener, sessionId);
+        try{ GCloudMemcachedTestSuite.__testSupport.listSessions(); GCloudMemcachedTestSuite.__testSupport.assertSessions(1);}catch(Exception e) {e.printStackTrace();} 
+    }
+
+    @Override
+    public void verifySessionDestroyed(TestHttpSessionListener listener, String sessionId)
+    {
+        super.verifySessionDestroyed(listener, sessionId);
+        try{ GCloudMemcachedTestSuite.__testSupport.listSessions(); GCloudMemcachedTestSuite.__testSupport.assertSessions(0);}catch(Exception e) {e.printStackTrace();}
+    }
+
+    
+    
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionInvalidateAndCreateTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionInvalidateAndCreateTest.java
new file mode 100644
index 0000000..5c6f0d9
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionInvalidateAndCreateTest.java
@@ -0,0 +1,59 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractSessionInvalidateAndCreateTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SessionInvalidateAndCreateTest
+ *
+ *
+ */
+public class SessionInvalidateAndCreateTest extends AbstractSessionInvalidateAndCreateTest
+{
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionInvalidateAndCreateTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudMemcachedTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testSessionScavenge() throws Exception
+    {
+        super.testSessionScavenge();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionMigrationTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionMigrationTest.java
new file mode 100644
index 0000000..502bc04
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionMigrationTest.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractSessionMigrationTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SessionMigrationTest
+ *
+ *
+ */
+public class SessionMigrationTest extends AbstractSessionMigrationTest
+{
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionMigrationTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return  new GCloudMemcachedTestServer(port);
+    }
+
+    
+    @Test
+    @Override
+    public void testSessionMigration() throws Exception
+    {
+        super.testSessionMigration();
+    }
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionRenewTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionRenewTest.java
new file mode 100644
index 0000000..6f26148
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionRenewTest.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractSessionRenewTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SessionRenewTest
+ *
+ *
+ */
+public class SessionRenewTest extends AbstractSessionRenewTest
+{
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionRenewTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudMemcachedTestServer(port,max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testSessionRenewal() throws Exception
+    {
+        super.testSessionRenewal();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionValueSavingTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionValueSavingTest.java
new file mode 100644
index 0000000..fb5ea7d
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/SessionValueSavingTest.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import org.eclipse.jetty.server.session.AbstractSessionValueSavingTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SessionValueSavingTest
+ *
+ *
+ */
+public class SessionValueSavingTest extends AbstractSessionValueSavingTest
+{
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionValueSavingTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudMemcachedTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testSessionValueSaving() throws Exception
+    {
+        super.testSessionValueSaving();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/StopSessionManagerPreserveSessionTest.java b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/StopSessionManagerPreserveSessionTest.java
new file mode 100644
index 0000000..2514684
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-memcached-sessions/src/test/java/org/eclipse/jetty/gcloud/memcached/session/StopSessionManagerPreserveSessionTest.java
@@ -0,0 +1,87 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.memcached.session;
+
+import static org.junit.Assert.fail;
+import org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * StopSessionManagerPreserveSessionTest
+ *
+ *
+ */
+public class StopSessionManagerPreserveSessionTest extends AbstractStopSessionManagerPreserveSessionTest
+{
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudMemcachedTestSuite.__testSupport.deleteSessions();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest#checkSessionPersisted(boolean)
+     */
+    @Override
+    public void checkSessionPersisted(boolean expected)
+    {
+        try
+        {
+            GCloudMemcachedTestSuite.__testSupport.assertSessions(1);
+        }
+        catch (Exception e)
+        {
+            fail(e.getMessage());
+        }
+
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return  new GCloudMemcachedTestServer(port);
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest#configureSessionManagement(org.eclipse.jetty.servlet.ServletContextHandler)
+     */
+    @Override
+    public void configureSessionManagement(ServletContextHandler context)
+    {
+        
+    }
+
+    @Test
+    @Override
+    public void testStopSessionManagerPreserveSession() throws Exception
+    {
+        super.testStopSessionManagerPreserveSession();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/pom.xml b/tests/test-sessions/test-gcloud-sessions/pom.xml
new file mode 100644
index 0000000..50f9fa8
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/pom.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.eclipse.jetty.tests</groupId>
+    <artifactId>test-sessions-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+  <artifactId>test-gcloud-sessions</artifactId>
+  <name>Jetty Tests :: Sessions :: GCloud</name>
+  <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.sessions.gcloud</bundle-symbolic-name>
+  </properties>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <!-- DO NOT DEPLOY (or Release) -->
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <skipTests>true</skipTests>
+          <test>GCloudTestSuite</test>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+       <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-webapp</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.tests</groupId>
+            <artifactId>test-sessions-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.gcloud</groupId>
+            <artifactId>jetty-gcloud-session-manager</artifactId>
+            <version>${project.version}</version>
+         </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <profiles>
+      <profile>
+        <id>gcloud</id>
+        <activation>
+          <property>
+            <name>gcloud.enabled</name>
+            <value>true</value>
+          </property>
+        </activation>
+        <build>
+          <plugins>
+            <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-surefire-plugin</artifactId>
+              <configuration>
+                <skipTests>false</skipTests>
+                <systemPropertyVariables>
+                  <DATASTORE_DATASET>jetty9-work</DATASTORE_DATASET>
+                  <DATASTORE_HOST>http://localhost:8088</DATASTORE_HOST>
+                </systemPropertyVariables>
+              </configuration>
+            </plugin>
+          </plugins>
+        </build>
+      </profile>
+  </profiles>
+
+</project>
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ClientCrossContextSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ClientCrossContextSessionTest.java
new file mode 100644
index 0000000..862cd60
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ClientCrossContextSessionTest.java
@@ -0,0 +1,59 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractClientCrossContextSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * ClientCrossContextSessionTest
+ *
+ *
+ */
+public class ClientCrossContextSessionTest extends AbstractClientCrossContextSessionTest
+{
+
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractClientCrossContextSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return new GCloudTestServer(port);
+    }
+
+    @Test
+    @Override
+    public void testCrossContextDispatch() throws Exception
+    {
+        super.testCrossContextDispatch();
+    }
+
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ForwardedSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ForwardedSessionTest.java
new file mode 100644
index 0000000..f0c52b0
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ForwardedSessionTest.java
@@ -0,0 +1,51 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractForwardedSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * ForwardedSessionTest
+ *
+ *
+ */
+public class ForwardedSessionTest extends AbstractForwardedSessionTest
+{
+
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractForwardedSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+       return new GCloudTestServer(port);
+    }
+
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTestSupport.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTestSupport.java
new file mode 100644
index 0000000..58501b3
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTestSupport.java
@@ -0,0 +1,130 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.cloud.datastore.Datastore;
+import com.google.cloud.datastore.DatastoreOptions;
+import com.google.cloud.datastore.Entity;
+import com.google.cloud.datastore.GqlQuery;
+import com.google.cloud.datastore.Key;
+import com.google.cloud.datastore.Query;
+import com.google.cloud.datastore.Query.ResultType;
+import com.google.cloud.datastore.QueryResults;
+import com.google.cloud.datastore.testing.LocalDatastoreHelper;
+
+/**
+ * GCloudSessionTestSupport
+ *
+ *
+ */
+public class GCloudSessionTestSupport
+{
+    LocalDatastoreHelper _helper = LocalDatastoreHelper.create(1.0);
+    Datastore _ds;
+
+
+
+    public GCloudSessionTestSupport ()
+    {
+        DatastoreOptions options = _helper.options();
+        _ds = options.service();
+    }
+
+
+
+    public void setUp()
+            throws Exception
+    {
+        _helper.start();
+    }
+    
+    
+    public Datastore getDatastore ()
+    {
+        return _ds;
+    }
+    
+    
+    public void tearDown()
+    throws Exception
+    {
+        _helper.stop();
+    }
+    
+   
+    public void listSessions () throws Exception
+    {
+
+        GqlQuery.Builder builder = Query.gqlQueryBuilder(ResultType.ENTITY, "select * from "+GCloudSessionManager.KIND);
+       
+        Query<Entity> query = builder.build();
+    
+        QueryResults<Entity> results = _ds.run(query);
+        assertNotNull(results);
+        System.err.println("SESSIONS::::::::");
+        while (results.hasNext())
+        {
+            
+            Entity e = results.next();
+            System.err.println(e.getString("clusterId")+" expires at "+e.getLong("expiry"));
+        }
+        System.err.println("END OF SESSIONS::::::::");
+    }
+    
+    public void assertSessions(int count) throws Exception
+    {        
+        Query<Key> query = Query.keyQueryBuilder().kind(GCloudSessionManager.KIND).build();
+        QueryResults<Key> results = _ds.run(query);
+        assertNotNull(results);
+        int actual = 0;
+        while (results.hasNext())
+        { 
+            results.next();
+            ++actual;
+        }       
+        assertEquals(count, actual);
+    }
+
+    public void deleteSessions () throws Exception
+    {
+        Query<Key> query = Query.keyQueryBuilder().kind(GCloudSessionManager.KIND).build();
+        QueryResults<Key> results = _ds.run(query);
+
+        if (results != null)
+        {
+            List<Key> keys = new ArrayList<Key>();
+
+            while (results.hasNext())
+            { 
+                keys.add(results.next());
+            }
+
+            _ds.delete(keys.toArray(new Key[keys.size()]));
+        }
+
+        assertSessions(0);
+    }
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java
new file mode 100644
index 0000000..16f62ad
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java
@@ -0,0 +1,94 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.SessionIdManager;
+import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.SessionHandler;
+
+
+
+/**
+ * GCloudTestServer
+ *
+ *
+ */
+public class GCloudTestServer extends AbstractTestServer
+{
+    static int __workers=0;
+    public static int STALE_INTERVAL_SEC = 1;
+
+ 
+
+    /**
+     * @param port
+     * @param maxInactivePeriod
+     * @param scavengePeriod
+     */
+    public GCloudTestServer(int port, int maxInactivePeriod, int scavengePeriod)
+    {
+        super(port, maxInactivePeriod, scavengePeriod);
+    }
+
+    /**
+     * @param port
+     */
+    public GCloudTestServer(int port)
+    {
+        super(port, 30,10);
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionIdManager(java.lang.Object)
+     */
+    @Override
+    public SessionIdManager newSessionIdManager(Object config)
+    {
+        GCloudSessionIdManager idManager = new GCloudSessionIdManager(getServer());
+        idManager.setWorkerName("w"+(__workers++));
+        return idManager;
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionManager()
+     */
+    @Override
+    public SessionManager newSessionManager()
+    {
+        GCloudSessionManager sessionManager = new GCloudSessionManager();
+        sessionManager.setSessionIdManager((GCloudSessionIdManager)_sessionIdManager);
+        sessionManager.setStaleIntervalSec(STALE_INTERVAL_SEC);
+        sessionManager.setScavengeIntervalSec(_scavengePeriod);
+        sessionManager.setDatastore(GCloudTestSuite.__testSupport.getDatastore());
+        return sessionManager;
+        
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionHandler(org.eclipse.jetty.server.SessionManager)
+     */
+    @Override
+    public SessionHandler newSessionHandler(SessionManager sessionManager)
+    {
+        return new SessionHandler(sessionManager);
+    }
+
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestSuite.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestSuite.java
new file mode 100644
index 0000000..8f3afc4
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestSuite.java
@@ -0,0 +1,78 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * GCloudTestSuite
+ *
+ * Sets up the gcloud emulator once before running all tests.
+ *
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+  ClientCrossContextSessionTest.class,
+  ForwardedSessionTest.class,
+  ImmortalSessionTest.class,
+  InvalidationSessionTest.class,
+  LastAccessTimeTest.class,
+    
+  LocalSessionScavengingTest.class,
+  
+  NewSessionTest.class,
+  OrphanedSessionTest.class,
+  ReentrantRequestSessionTest.class,
+  
+  RemoveSessionTest.class,
+  
+  SameNodeLoadTest.class,
+  ServerCrossContextSessionTest.class,
+  SessionExpiryTest.class,
+  
+  SessionInvalidateAndCreateTest.class,
+  SessionMigrationTest.class,
+  
+  SessionRenewTest.class,
+  SessionValueSavingTest.class,
+  StopSessionManagerPreserveSessionTest.class
+})
+
+
+public class GCloudTestSuite
+{
+    public static GCloudSessionTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setUp () throws Exception
+    {
+        __testSupport = new GCloudSessionTestSupport();
+        __testSupport.setUp();
+    }
+    
+    @AfterClass
+    public static void tearDown () throws Exception
+    {
+        __testSupport.tearDown();
+    }
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ImmortalSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ImmortalSessionTest.java
new file mode 100644
index 0000000..f1eabfc
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ImmortalSessionTest.java
@@ -0,0 +1,59 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractImmortalSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * ImmortalSessionTest
+ *
+ *
+ */
+public class ImmortalSessionTest extends AbstractImmortalSessionTest
+{
+   
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractImmortalSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int maxInactiveMs, int scavengeMs)
+    {
+       return new GCloudTestServer(port, port, scavengeMs);
+    }
+
+    @Test
+    @Override
+    public void testImmortalSession() throws Exception
+    {
+        super.testImmortalSession();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/InvalidationSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/InvalidationSessionTest.java
new file mode 100644
index 0000000..0ff4309
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/InvalidationSessionTest.java
@@ -0,0 +1,70 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractInvalidationSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * InvalidationSessionTest
+ *
+ *
+ */
+public class InvalidationSessionTest extends AbstractInvalidationSessionTest
+{
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return new GCloudTestServer(port);
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#pause()
+     */
+    @Override
+    public void pause()
+    {
+        //This test moves around a session between 2 nodes. After it is invalidated on the 1st node,
+        //it will still be in the memory of the 2nd node. We need to wait until after the stale time
+        //has expired on node2 for it to reload the session and discover it has been deleted.
+        try
+        {
+            Thread.currentThread().sleep((2*GCloudTestServer.STALE_INTERVAL_SEC)*1000);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        
+    }
+
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LastAccessTimeTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LastAccessTimeTest.java
new file mode 100644
index 0000000..08d1c00
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LastAccessTimeTest.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractLastAccessTimeTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * LastAccessTimeTest
+ *
+ *
+ */
+public class LastAccessTimeTest extends AbstractLastAccessTimeTest
+{
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractLastAccessTimeTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testLastAccessTime() throws Exception
+    {
+        super.testLastAccessTime();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LocalSessionScavengingTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LocalSessionScavengingTest.java
new file mode 100644
index 0000000..bb6c3a7
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LocalSessionScavengingTest.java
@@ -0,0 +1,59 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractLocalSessionScavengingTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * LocalSessionScavengingTest
+ *
+ *
+ */
+public class LocalSessionScavengingTest extends AbstractLocalSessionScavengingTest
+{
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractLocalSessionScavengingTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testLocalSessionsScavenging() throws Exception
+    {
+        super.testLocalSessionsScavenging();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/NewSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/NewSessionTest.java
new file mode 100644
index 0000000..5e097b1
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/NewSessionTest.java
@@ -0,0 +1,63 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import java.io.File;
+
+import org.eclipse.jetty.server.session.AbstractNewSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * NewSessionTest
+ *
+ *
+ */
+public class NewSessionTest extends AbstractNewSessionTest
+{
+
+    
+    @After
+    public void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractNewSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+       return new GCloudTestServer(port, max, scavenge);
+    }
+
+    @Test
+    public void testNewSession() throws Exception
+    {
+        super.testNewSession();
+    }
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/OrphanedSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/OrphanedSessionTest.java
new file mode 100644
index 0000000..1e1a4c8
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/OrphanedSessionTest.java
@@ -0,0 +1,63 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractOrphanedSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * OrphanedSessionTest
+ *
+ *
+ */
+public class OrphanedSessionTest extends AbstractOrphanedSessionTest
+{
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractOrphanedSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testOrphanedSession() throws Exception
+    {
+        super.testOrphanedSession();
+        GCloudTestSuite.__testSupport.assertSessions(0);
+    }
+    
+    
+    
+
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ReentrantRequestSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ReentrantRequestSessionTest.java
new file mode 100644
index 0000000..7cf70b7
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ReentrantRequestSessionTest.java
@@ -0,0 +1,60 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractReentrantRequestSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * ReentrantRequestSessionTest
+ *
+ *
+ */
+public class ReentrantRequestSessionTest extends AbstractReentrantRequestSessionTest
+{
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractReentrantRequestSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return  new GCloudTestServer(port);
+    }
+
+    @Test
+    @Override
+    public void testReentrantRequestSession() throws Exception
+    {
+        super.testReentrantRequestSession();
+    }
+    
+    
+
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/RemoveSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/RemoveSessionTest.java
new file mode 100644
index 0000000..1568c32
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/RemoveSessionTest.java
@@ -0,0 +1,63 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractRemoveSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * RemoveSessionTest
+ *
+ *
+ */
+public class RemoveSessionTest extends AbstractRemoveSessionTest
+{
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractRemoveSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    { 
+        return new GCloudTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testRemoveSession() throws Exception
+    {
+        super.testRemoveSession();
+    }
+    
+    
+
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SameNodeLoadTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SameNodeLoadTest.java
new file mode 100644
index 0000000..c7afab9
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SameNodeLoadTest.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractSameNodeLoadTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SameNodeLoadTest
+ *
+ *
+ */
+public class SameNodeLoadTest extends AbstractSameNodeLoadTest
+{
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSameNodeLoadTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return  new GCloudTestServer(port);
+    }
+
+    @Test
+    @Override
+    public void testLoad() throws Exception
+    {
+        super.testLoad();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ServerCrossContextSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ServerCrossContextSessionTest.java
new file mode 100644
index 0000000..5dcf0df
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ServerCrossContextSessionTest.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractServerCrossContextSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * ServerCrossContextSessionTest
+ *
+ *
+ */
+public class ServerCrossContextSessionTest extends AbstractServerCrossContextSessionTest
+{
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractServerCrossContextSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return  new GCloudTestServer(port); 
+    }
+
+    @Test
+    @Override
+    public void testCrossContextDispatch() throws Exception
+    {
+        super.testCrossContextDispatch();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionExpiryTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionExpiryTest.java
new file mode 100644
index 0000000..3ae1c87
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionExpiryTest.java
@@ -0,0 +1,89 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractSessionExpiryTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SessionExpiryTest
+ *
+ *
+ */
+public class SessionExpiryTest extends AbstractSessionExpiryTest
+{
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionExpiryTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testSessionNotExpired() throws Exception
+    {
+        super.testSessionNotExpired();
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionExpiryTest#testSessionExpiry()
+     */
+    @Test
+    @Override
+    public void testSessionExpiry() throws Exception
+    {
+        super.testSessionExpiry();
+        GCloudTestSuite.__testSupport.assertSessions(0);
+    }
+
+    @Override
+    public void verifySessionCreated(TestHttpSessionListener listener, String sessionId)
+    {
+        super.verifySessionCreated(listener, sessionId);
+        try{ GCloudTestSuite.__testSupport.listSessions(); GCloudTestSuite.__testSupport.assertSessions(1);}catch(Exception e) {e.printStackTrace();} 
+    }
+
+    @Override
+    public void verifySessionDestroyed(TestHttpSessionListener listener, String sessionId)
+    {
+        super.verifySessionDestroyed(listener, sessionId);
+        try{ GCloudTestSuite.__testSupport.listSessions(); GCloudTestSuite.__testSupport.assertSessions(0);}catch(Exception e) {e.printStackTrace();}
+    }
+
+    
+    
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionInvalidateAndCreateTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionInvalidateAndCreateTest.java
new file mode 100644
index 0000000..3f479da
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionInvalidateAndCreateTest.java
@@ -0,0 +1,59 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractSessionInvalidateAndCreateTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SessionInvalidateAndCreateTest
+ *
+ *
+ */
+public class SessionInvalidateAndCreateTest extends AbstractSessionInvalidateAndCreateTest
+{
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionInvalidateAndCreateTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testSessionScavenge() throws Exception
+    {
+        super.testSessionScavenge();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionMigrationTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionMigrationTest.java
new file mode 100644
index 0000000..bdf226a
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionMigrationTest.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractSessionMigrationTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SessionMigrationTest
+ *
+ *
+ */
+public class SessionMigrationTest extends AbstractSessionMigrationTest
+{
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionMigrationTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return  new GCloudTestServer(port);
+    }
+
+    
+    @Test
+    @Override
+    public void testSessionMigration() throws Exception
+    {
+        super.testSessionMigration();
+    }
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionRenewTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionRenewTest.java
new file mode 100644
index 0000000..802fc59
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionRenewTest.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractSessionRenewTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SessionRenewTest
+ *
+ *
+ */
+public class SessionRenewTest extends AbstractSessionRenewTest
+{
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionRenewTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudTestServer(port,max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testSessionRenewal() throws Exception
+    {
+        super.testSessionRenewal();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionValueSavingTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionValueSavingTest.java
new file mode 100644
index 0000000..f49449d
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionValueSavingTest.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import org.eclipse.jetty.server.session.AbstractSessionValueSavingTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SessionValueSavingTest
+ *
+ *
+ */
+public class SessionValueSavingTest extends AbstractSessionValueSavingTest
+{
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionValueSavingTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return  new GCloudTestServer(port, max, scavenge);
+    }
+
+    @Test
+    @Override
+    public void testSessionValueSaving() throws Exception
+    {
+        super.testSessionValueSaving();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/StopSessionManagerPreserveSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/StopSessionManagerPreserveSessionTest.java
new file mode 100644
index 0000000..86eecca
--- /dev/null
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/StopSessionManagerPreserveSessionTest.java
@@ -0,0 +1,87 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.gcloud.session;
+
+import static org.junit.Assert.fail;
+import org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * StopSessionManagerPreserveSessionTest
+ *
+ *
+ */
+public class StopSessionManagerPreserveSessionTest extends AbstractStopSessionManagerPreserveSessionTest
+{
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        GCloudTestSuite.__testSupport.deleteSessions();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest#checkSessionPersisted(boolean)
+     */
+    @Override
+    public void checkSessionPersisted(boolean expected)
+    {
+        try
+        {
+            GCloudTestSuite.__testSupport.assertSessions(1);
+        }
+        catch (Exception e)
+        {
+            fail(e.getMessage());
+        }
+
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return  new GCloudTestServer(port);
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest#configureSessionManagement(org.eclipse.jetty.servlet.ServletContextHandler)
+     */
+    @Override
+    public void configureSessionManagement(ServletContextHandler context)
+    {
+        
+    }
+
+    @Test
+    @Override
+    public void testStopSessionManagerPreserveSession() throws Exception
+    {
+        super.testStopSessionManagerPreserveSession();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-hash-sessions/pom.xml b/tests/test-sessions/test-hash-sessions/pom.xml
index 69a11d5..6bf08a0 100644
--- a/tests/test-sessions/test-hash-sessions/pom.xml
+++ b/tests/test-sessions/test-hash-sessions/pom.xml
@@ -1,31 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-sessions-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <artifactId>test-hash-sessions</artifactId>
   <name>Jetty Tests :: Sessions :: Hash</name>
   <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.sessions.hash</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
new file mode 100644
index 0000000..556a9ef
--- /dev/null
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
@@ -0,0 +1,91 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.File;
+
+import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.util.IO;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * ForwardedSessionTest
+ *
+ *
+ */
+public class ForwardedSessionTest extends AbstractForwardedSessionTest
+{ 
+   File tmpDir;
+    
+    @Before
+    public void before() throws Exception
+    {
+        tmpDir = File.createTempFile("hash-session-forward-test", null);
+        tmpDir.delete();
+        tmpDir.mkdirs();
+        tmpDir.deleteOnExit();
+    }
+    
+    @After 
+    public void after()
+    {
+        IO.delete(tmpDir);
+    }
+    
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return new HashTestServer(port)
+        {
+
+            @Override
+            public SessionManager newSessionManager()
+            {
+                HashSessionManager sessionManager = (HashSessionManager)super.newSessionManager();
+                sessionManager.setSavePeriod(2);
+                
+                try
+                {
+                    sessionManager.setStoreDirectory(tmpDir);
+                }
+                catch (Exception e)
+                {
+                    throw new IllegalStateException(e);
+                }
+                return sessionManager;
+            }
+
+        };
+    }
+
+    
+    
+    @Test
+    public void testSessionCreateInForward() throws Exception
+    {
+        super.testSessionCreateInForward();
+    }
+
+  
+
+
+}
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java
index 83a561d..3758653 100644
--- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java
@@ -38,7 +38,7 @@
     }
 
 
-    public SessionIdManager newSessionIdManager(String config)
+    public SessionIdManager newSessionIdManager(Object config)
     {
         return new HashSessionIdManager();
     }
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java
index 9cb5e5d..db0556f 100644
--- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java
@@ -38,8 +38,7 @@
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.junit.Test;
 
 
@@ -111,8 +110,6 @@
         int inactivePeriod = 200;
         int scavengePeriod = 3;
         int idlePeriod = 5;
-        ((StdErrLog)Log.getLogger(org.eclipse.jetty.server.session.HashedSession.class)).setHideStacks(true);
-        System.setProperty("org.eclipse.jetty.STACKS", "false");
         File storeDir = new File (System.getProperty("java.io.tmpdir"), "idle-test");
         storeDir.deleteOnExit();
 
@@ -124,8 +121,8 @@
         server1.start();
         int port1 = server1.getPort();
 
-        try
-        {
+            try (StacklessLogging stackless = new StacklessLogging(HashedSession.class))
+            {
             HttpClient client = new HttpClient();
             client.start();
             String url = "http://localhost:" + port1 + contextPath + servletMapping;
@@ -133,7 +130,7 @@
             //make a request to set up a session on the server
             ContentResponse response = client.GET(url + "?action=init");
             assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-            String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+            String sessionCookie = response.getHeaders().get("Set-Cookie");
             assertTrue(sessionCookie != null);
             // Mangle the cookie, replacing Path with $Path, etc.
             sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/LightLoadTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/LightLoadTest.java
deleted file mode 100644
index 0af7231..0000000
--- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/LightLoadTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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 org.junit.Test;
-
-/**
- * LightLoadTest
- */
-public class LightLoadTest extends AbstractLightLoadTest
-{
-
-    public AbstractTestServer createServer(int port)
-    {
-        return new HashTestServer(port);
-    }
-
-    @Test
-    public void testLightLoad() throws Exception
-    {
-        super.testLightLoad();
-    }
-
-}
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/RedirectSessionTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/RedirectSessionTest.java
new file mode 100644
index 0000000..97caca2
--- /dev/null
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/RedirectSessionTest.java
@@ -0,0 +1,114 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.junit.Test;
+
+
+/**
+ * RedirectSessionTest
+ *
+ * Test that creating a session and then doing a redirect preserves the session.
+ */
+public class RedirectSessionTest
+{
+   
+    
+    
+    @Test
+    public void testSessionRedirect() throws Exception
+    {
+        AbstractTestServer testServer = new HashTestServer(0);
+        ServletContextHandler testServletContextHandler = testServer.addContext("/context");
+        testServletContextHandler.addServlet(Servlet1.class, "/one");
+        testServletContextHandler.addServlet(Servlet2.class, "/two");
+
+       
+      
+
+        try
+        {
+            testServer.start();
+            int serverPort=testServer.getPort();
+            HttpClient client = new HttpClient();
+            client.setFollowRedirects(true); //ensure client handles redirects
+            client.start();
+            try
+            {
+                //make a request to the first servlet, which will redirect
+                ContentResponse response = client.GET("http://localhost:" + serverPort + "/context/one");
+                assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+            }
+            finally
+            {
+                client.stop();
+            }
+        }
+        finally
+        {
+            testServer.stop();
+        }
+        
+    }
+    
+
+    public static class Servlet1 extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {
+            //create a session
+            HttpSession session = request.getSession(true);
+            assertNotNull(session);
+            session.setAttribute("servlet1", "servlet1");
+            response.sendRedirect("/context/two");
+        }
+    }
+
+    public static class Servlet2 extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {
+          //the session should exist after the redirect
+            HttpSession sess = request.getSession(false);
+            assertNotNull(sess);
+            assertNotNull(sess.getAttribute("servlet1"));
+            assertEquals("servlet1", sess.getAttribute("servlet1"));
+            
+        }
+    }
+
+
+    
+}
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ScatterGunLoadTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ScatterGunLoadTest.java
new file mode 100644
index 0000000..0f1fb88
--- /dev/null
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ScatterGunLoadTest.java
@@ -0,0 +1,40 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.Test;
+
+/**
+ * ScatterGunLoadTest
+ */
+public class ScatterGunLoadTest extends AbstractScatterGunLoadTest
+{
+
+    public AbstractTestServer createServer(int port)
+    {
+        return new HashTestServer(port);
+    }
+
+    @Test
+    public void testLightLoad() throws Exception
+    {
+        super.testLightLoad();
+    }
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/pom.xml b/tests/test-sessions/test-infinispan-sessions/pom.xml
new file mode 100644
index 0000000..6849a67
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/pom.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.eclipse.jetty.tests</groupId>
+    <artifactId>test-sessions-parent</artifactId>
+    <version>9.3.19-SNAPSHOT</version>
+  </parent>
+  <artifactId>test-infinispan-sessions</artifactId>
+  <name>Jetty Tests :: Sessions :: Infinispan</name>
+  <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.sessions.infinispan</bundle-symbolic-name>
+  </properties>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <!-- DO NOT DEPLOY (or Release) -->
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <includes>
+              <include>org/eclipse/jetty/server/session/*.java</include>
+          </includes>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>unpack</id>
+            <phase>generate-test-resources</phase>
+            <goals>
+              <goal>unpack</goal>
+            </goals>
+            <configuration>
+              <artifactItems>
+                <artifactItem>
+                  <groupId>org.eclipse.jetty.toolchain</groupId>
+                  <artifactId>jetty-test-policy</artifactId>
+                  <version>${jetty-test-policy-version}</version>
+                  <type>jar</type>
+                  <overWrite>true</overWrite>
+                  <includes>**/*.keystore,**/*.pem</includes>
+                  <outputDirectory>${jetty.test.policy.loc}</outputDirectory>
+                </artifactItem>
+              </artifactItems>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+       <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-webapp</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.tests</groupId>
+            <artifactId>test-sessions-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-infinispan</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.eclipse.jetty</groupId>
+          <artifactId>jetty-jmx</artifactId>
+          <version>${project.version}</version>
+          <optional>true</optional>
+        </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.infinispan</groupId>
+      <artifactId>infinispan-client-hotrod</artifactId>
+      <version>7.1.1.Final</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <profiles>
+  <!-- to test hotrod, configure a cache called "remote-session-test" -->
+      <profile>
+        <id>remote</id>
+        <activation>
+          <property>
+            <name>hotrod.enabled</name>
+            <value>true</value>
+          </property>
+        </activation>
+        <build>
+          <plugins>
+            <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-surefire-plugin</artifactId>
+              <configuration>
+                <includes>
+                  <include>**/*.java</include>
+                </includes>
+              </configuration>
+            </plugin>
+          </plugins>
+        </build>
+      </profile>
+  </profiles>
+</project>
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java
new file mode 100644
index 0000000..95e2eb0
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java
@@ -0,0 +1,60 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class ClientCrossContextSessionTest extends AbstractClientCrossContextSessionTest
+{
+
+    public static InfinispanTestSupport __testSupport;
+
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new InfinispanTestSupport();
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+    
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        InfinispanTestSessionServer server = new InfinispanTestSessionServer(port, __testSupport.getCache());
+        return server;
+    }
+    
+    @Test
+    public void testCrossContextDispatch() throws Exception
+    {
+        super.testCrossContextDispatch();
+    }
+    
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
new file mode 100644
index 0000000..3c8ad0b
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
@@ -0,0 +1,66 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * ForwardedSessionTest
+ *
+ *
+ */
+public class ForwardedSessionTest extends AbstractForwardedSessionTest
+{
+
+    public static InfinispanTestSupport __testSupport;
+
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new InfinispanTestSupport();
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+    
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        InfinispanTestSessionServer server = new InfinispanTestSessionServer(port, __testSupport.getCache());
+        return server;
+    }
+
+    @Test
+    public void testSessionCreateInForward() throws Exception
+    {
+        super.testSessionCreateInForward();
+    }
+   
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java
new file mode 100644
index 0000000..5b7a1d3
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java
@@ -0,0 +1,71 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.infinispan.Cache;
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.manager.EmbeddedCacheManager;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * ImmortalSessionTest
+ *
+ *
+ */
+public class ImmortalSessionTest extends AbstractImmortalSessionTest
+{
+
+
+    public static InfinispanTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new InfinispanTestSupport();
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractImmortalSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int maxInactiveMs, int scavengeMs)
+    {
+        return new InfinispanTestSessionServer(port, maxInactiveMs, scavengeMs, __testSupport.getCache());
+    }
+
+    @Override
+    public void testImmortalSession() throws Exception
+    {
+        super.testImmortalSession();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java
new file mode 100644
index 0000000..432fa83
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java
@@ -0,0 +1,117 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.eclipse.jetty.server.SessionIdManager;
+import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.session.infinispan.InfinispanSessionIdManager;
+import org.eclipse.jetty.session.infinispan.InfinispanSessionManager;
+import org.infinispan.Cache;
+import org.infinispan.commons.api.BasicCache;
+import org.infinispan.commons.util.CloseableIteratorSet;
+
+public class InfinispanTestSessionServer extends AbstractTestServer
+{
+    static int __workers=0;
+    
+
+
+    
+    
+    public InfinispanTestSessionServer(int port, BasicCache config)
+    {
+        this(port, 30, 10, config);
+    }
+    
+  
+    
+    public InfinispanTestSessionServer(int port, int maxInactivePeriod, int scavengePeriod, BasicCache config)
+    {
+        super(port, maxInactivePeriod, scavengePeriod, config);
+    }
+    
+    
+
+    @Override
+    public SessionIdManager newSessionIdManager(Object config)
+    {
+        InfinispanSessionIdManager idManager = new InfinispanSessionIdManager(getServer());
+        idManager.setWorkerName("w"+(__workers++));
+        idManager.setCache((BasicCache)config);
+        return idManager;
+    }
+
+    @Override
+    public SessionManager newSessionManager()
+    {
+        InfinispanSessionManager sessionManager = new InfinispanSessionManager();
+        sessionManager.setSessionIdManager((InfinispanSessionIdManager)_sessionIdManager);
+        sessionManager.setCache(((InfinispanSessionIdManager)_sessionIdManager).getCache());
+        sessionManager.setStaleIntervalSec(1);
+        sessionManager.setScavengeInterval(_scavengePeriod);
+        
+        return sessionManager;
+    }
+
+    @Override
+    public SessionHandler newSessionHandler(SessionManager sessionManager)
+    {
+        return new SessionHandler(sessionManager);
+    }
+
+    public boolean exists (String id)
+    {
+        BasicCache cache = ((InfinispanSessionIdManager)_sessionIdManager).getCache();
+        if (cache != null)
+        {
+            return cache.containsKey(id);      
+        }
+        
+        return false;
+    }
+    
+    public Object get (String id)
+    {
+        BasicCache cache = ((InfinispanSessionIdManager)_sessionIdManager).getCache();
+        if (cache != null)
+        {
+            return cache.get(id);      
+        }
+        
+        return null;
+    }
+
+    public void dumpCache ()
+    {
+        BasicCache cache = ((InfinispanSessionIdManager)_sessionIdManager).getCache();
+        if (cache != null)
+        {
+            System.err.println(cache.getName()+" contains "+cache.size()+" entries");         
+        }
+    }
+
+    public void clearCache ()
+    { 
+        BasicCache cache = ((InfinispanSessionIdManager)_sessionIdManager).getCache();
+        if (cache != null)
+            cache.clear();
+    }
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java
new file mode 100644
index 0000000..c1dcbf2
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java
@@ -0,0 +1,114 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.File;
+
+import org.eclipse.jetty.util.IO;
+import org.infinispan.Cache;
+import org.infinispan.configuration.cache.Configuration;
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.configuration.global.GlobalConfigurationBuilder;
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.manager.EmbeddedCacheManager;
+
+/**
+ * InfinispanTestSupport
+ *
+ *
+ */
+public class InfinispanTestSupport
+{
+    public static final String DEFAULT_CACHE_NAME =  "session_test_cache";
+    public  Cache _cache;
+ 
+    public ConfigurationBuilder _builder;
+    private  File _tmpdir;
+    private boolean _useFileStore;
+    private String _name;
+    public static  EmbeddedCacheManager _manager;
+    
+    static
+    {
+        try
+        {
+            _manager = new DefaultCacheManager(new GlobalConfigurationBuilder().globalJmxStatistics().allowDuplicateDomains(true).build());
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+    
+    public InfinispanTestSupport ()
+    {
+        this (null);
+    }
+    
+    public InfinispanTestSupport(String cacheName)
+    {     
+        if (cacheName == null)
+            cacheName = DEFAULT_CACHE_NAME+System.currentTimeMillis();
+        
+        _name = cacheName;
+        _builder = new ConfigurationBuilder();
+    }
+    
+    public void setUseFileStore (boolean useFileStore)
+    {
+        _useFileStore = useFileStore;
+    }
+  
+    public Cache getCache ()
+    {
+        return _cache;
+    }
+    
+    public void setup () throws Exception
+    {
+       if (_useFileStore)
+       {      
+        _tmpdir = File.createTempFile("infini", "span");
+        _tmpdir.delete();
+        _tmpdir.mkdir();
+        System.err.println("Temp file: "+_tmpdir);
+        Configuration config = _builder.persistence().addSingleFileStore().location(_tmpdir.getAbsolutePath()).storeAsBinary().build();
+        _manager.defineConfiguration(_name, config);
+       }
+       else
+       {
+           _manager.defineConfiguration(_name, _builder.build());
+       }
+       _cache = _manager.getCache(_name);
+    }
+
+
+    public void teardown () throws Exception
+    {
+        _manager.removeCache(_name);
+        if (_useFileStore)
+        {
+            if (_tmpdir != null)
+            {
+                IO.delete(_tmpdir);
+            }
+        }
+    }
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java
new file mode 100644
index 0000000..1e69135
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java
@@ -0,0 +1,91 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * InvalidationSessionTest
+ *
+ *
+ */
+public class InvalidationSessionTest extends AbstractInvalidationSessionTest
+{
+
+    public static InfinispanTestSupport __testSupport;
+    public static long __staleSec = 3L;
+   
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+      __testSupport = new InfinispanTestSupport();
+      __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return new InfinispanTestSessionServer(port, __testSupport.getCache());
+    }
+
+    
+    
+    
+    @Override
+    public void testInvalidation() throws Exception
+    {
+        super.testInvalidation();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#pause()
+     */
+    @Override
+    public void pause()
+    {
+        //This test moves a session from node 1 to node 2, then invalidates the session back on node1. This
+        //should never happen with a decent load balancer.
+        //The infinispan session manager on node 2 will hold the session in local memory for a specific (configurable)
+        //amount of time. We've set the stale session time to 3 sec, so we need to pause for at least this long before making
+        //another request to node2
+        
+        //that the node will re-load the session from the database and discover that it has gone.
+        try
+        {
+            Thread.sleep(2 * __staleSec * 1000);
+        }
+        catch (InterruptedException e)
+        {
+        }
+    }
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java
new file mode 100644
index 0000000..48bdf33
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java
@@ -0,0 +1,65 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.File;
+
+import org.eclipse.jetty.util.IO;
+import org.infinispan.Cache;
+import org.infinispan.configuration.cache.Configuration;
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.manager.EmbeddedCacheManager;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class LastAccessTimeTest extends AbstractLastAccessTimeTest
+{ 
+   public static InfinispanTestSupport __testSupport;
+   
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+       __testSupport = new InfinispanTestSupport();
+       __testSupport.setUseFileStore(true);
+       __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+    }
+
+    @Override
+    public void testLastAccessTime() throws Exception
+    {
+        super.testLastAccessTime();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LocalSessionScavengingTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LocalSessionScavengingTest.java
new file mode 100644
index 0000000..4f8a2e1
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LocalSessionScavengingTest.java
@@ -0,0 +1,59 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * LocalSessionScavengingTest
+ *
+ *
+ */
+public class LocalSessionScavengingTest extends AbstractLocalSessionScavengingTest
+{
+
+    public static InfinispanTestSupport __testSupport;
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new InfinispanTestSupport();
+        __testSupport.setUseFileStore(true);
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractLocalSessionScavengingTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+    }
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java
new file mode 100644
index 0000000..5da00c2
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java
@@ -0,0 +1,66 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * NewSessionTest
+ *
+ *
+ */
+public class NewSessionTest extends AbstractNewSessionTest
+{
+
+   public static InfinispanTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new InfinispanTestSupport();
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractNewSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+    }
+
+  
+    @Override
+    public void testNewSession() throws Exception
+    {
+        super.testNewSession();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java
new file mode 100644
index 0000000..62d2606
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java
@@ -0,0 +1,64 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * ReentrantRequestSessionTest
+ *
+ *
+ */
+public class ReentrantRequestSessionTest extends AbstractReentrantRequestSessionTest
+{
+    public static InfinispanTestSupport __testSupport;
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new InfinispanTestSupport();
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractReentrantRequestSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+       return new InfinispanTestSessionServer(port, __testSupport.getCache());
+    }
+
+    @Override
+    public void testReentrantRequestSession() throws Exception
+    {
+        super.testReentrantRequestSession();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/RemoveSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/RemoveSessionTest.java
new file mode 100644
index 0000000..c399b30
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/RemoveSessionTest.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class RemoveSessionTest extends AbstractRemoveSessionTest
+{
+
+    public static InfinispanTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new InfinispanTestSupport();
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+       InfinispanTestSessionServer s = new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+       return s;
+    }
+
+    @Test
+    public void testRemoveSession() throws Exception
+    {
+        super.testRemoveSession();
+    }
+   
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SameNodeLoadTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SameNodeLoadTest.java
new file mode 100644
index 0000000..a7ce4be
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SameNodeLoadTest.java
@@ -0,0 +1,66 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * SameNodeLoadTest
+ *
+ *
+ */
+public class SameNodeLoadTest extends AbstractSameNodeLoadTest
+{
+
+    public static InfinispanTestSupport __testSupport;
+
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new InfinispanTestSupport();
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSameNodeLoadTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        InfinispanTestSessionServer server = new InfinispanTestSessionServer(port, __testSupport.getCache());
+        return server;
+    }
+
+    @Override
+    public void testLoad() throws Exception
+    {
+        super.testLoad();
+    }
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java
new file mode 100644
index 0000000..e8cdf80
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java
@@ -0,0 +1,70 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class SessionExpiryTest extends AbstractSessionExpiryTest
+{
+
+    public static InfinispanTestSupport __testSupport;
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new InfinispanTestSupport();
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+       InfinispanTestSessionServer server =  new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+       return server;
+    }
+
+    @Test
+    @Override
+    public void testSessionNotExpired() throws Exception
+    {
+        super.testSessionNotExpired();
+    }
+
+    @Test
+    @Override
+    public void testSessionExpiry() throws Exception
+    {
+       super.testSessionExpiry();
+    }
+    
+    @Override
+    public void verifySessionDestroyed (TestHttpSessionListener listener, String sessionId)
+    {
+        //noop - sessions that expired when the InfinispanSessionManager was not running are not reloaded and do not have their listeners called on them.
+    }
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java
new file mode 100644
index 0000000..51a219c
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java
@@ -0,0 +1,66 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * SessionInvalidateAndCreateTest
+ *
+ *
+ */
+public class SessionInvalidateAndCreateTest extends AbstractSessionInvalidateAndCreateTest
+{
+    public static InfinispanTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new InfinispanTestSupport();
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionInvalidateAndCreateTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+    }
+
+    @Override
+    public void testSessionScavenge() throws Exception
+    {
+        super.testSessionScavenge();
+    }
+
+    
+    
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionMigrationTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionMigrationTest.java
new file mode 100644
index 0000000..bc5d95d
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionMigrationTest.java
@@ -0,0 +1,64 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * SessionMigrationTest
+ *
+ *
+ */
+public class SessionMigrationTest extends AbstractSessionMigrationTest
+{
+
+    public static InfinispanTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new InfinispanTestSupport();
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionMigrationTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return new InfinispanTestSessionServer(port, __testSupport.getCache());
+    }
+
+    @Override
+    public void testSessionMigration() throws Exception
+    {
+        super.testSessionMigration();
+    }
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
new file mode 100644
index 0000000..ed19d55
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
@@ -0,0 +1,67 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SessionRenewTest
+ *
+ *
+ */
+public class SessionRenewTest extends AbstractSessionRenewTest
+{
+
+    public static InfinispanTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new InfinispanTestSupport();
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionRenewTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+    }
+
+    @Test
+    public void testSessionRenewal() throws Exception
+    {
+        super.testSessionRenewal();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteClientCrossContextSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteClientCrossContextSessionTest.java
new file mode 100644
index 0000000..9f625e2
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteClientCrossContextSessionTest.java
@@ -0,0 +1,69 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+
+import org.eclipse.jetty.server.session.AbstractClientCrossContextSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * RemoteClientCrossContextSessionTest
+ *
+ *
+ */
+public class RemoteClientCrossContextSessionTest extends AbstractClientCrossContextSessionTest
+{
+  public static RemoteInfinispanTestSupport __testSupport;
+
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+    
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        InfinispanTestSessionServer server = new InfinispanTestSessionServer(port, __testSupport.getCache());
+        return server;
+    }
+    
+    @Test
+    public void testCrossContextDispatch() throws Exception
+    {
+        super.testCrossContextDispatch();
+    }
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteForwardedSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteForwardedSessionTest.java
new file mode 100644
index 0000000..e5d2b85
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteForwardedSessionTest.java
@@ -0,0 +1,68 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import org.eclipse.jetty.server.session.AbstractForwardedSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * RemoteForwardedSessionTest
+ *
+ *
+ */
+public class RemoteForwardedSessionTest extends AbstractForwardedSessionTest
+{
+
+    public static RemoteInfinispanTestSupport __testSupport;
+
+
+
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+        __testSupport.setup();
+    }
+
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+
+
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        InfinispanTestSessionServer server = new InfinispanTestSessionServer(port, __testSupport.getCache());
+        return server;
+    }
+
+    @Test
+    public void testSessionCreateInForward() throws Exception
+    {
+        super.testSessionCreateInForward();
+    }
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteImmortalSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteImmortalSessionTest.java
new file mode 100644
index 0000000..66d7ece
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteImmortalSessionTest.java
@@ -0,0 +1,74 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import org.eclipse.jetty.server.session.AbstractImmortalSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.infinispan.Cache;
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.manager.EmbeddedCacheManager;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * ImmortalSessionTest
+ *
+ *
+ */
+public class RemoteImmortalSessionTest extends AbstractImmortalSessionTest
+{
+
+
+    public static RemoteInfinispanTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractImmortalSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int maxInactiveMs, int scavengeMs)
+    {
+        return new InfinispanTestSessionServer(port, maxInactiveMs, scavengeMs, __testSupport.getCache());
+    }
+
+    @Override
+    public void testImmortalSession() throws Exception
+    {
+        super.testImmortalSession();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInfinispanTestSupport.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInfinispanTestSupport.java
new file mode 100644
index 0000000..c2d1562
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInfinispanTestSupport.java
@@ -0,0 +1,88 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import java.io.File;
+
+import org.eclipse.jetty.util.IO;
+import org.infinispan.Cache;
+import org.infinispan.client.hotrod.RemoteCache;
+import org.infinispan.client.hotrod.RemoteCacheManager;
+import org.infinispan.configuration.cache.Configuration;
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.configuration.global.GlobalConfigurationBuilder;
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.manager.EmbeddedCacheManager;
+
+/**
+ * RemoteInfinispanTestSupport
+ *
+ *
+ */
+public class RemoteInfinispanTestSupport
+{
+    public static final String DEFAULT_CACHE_NAME =  "session_test_cache";
+    public  RemoteCache _cache;
+    private String _name;
+    public static  RemoteCacheManager _manager;
+    
+    static
+    {
+        try
+        {
+            _manager = new RemoteCacheManager();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+    
+    public RemoteInfinispanTestSupport ()
+    {
+        this (null);
+    }
+    
+    public RemoteInfinispanTestSupport(String cacheName)
+    {     
+        if (cacheName == null)
+            cacheName = DEFAULT_CACHE_NAME+System.currentTimeMillis();
+        
+        _name = cacheName;
+    }
+    
+ 
+  
+    public RemoteCache getCache ()
+    {
+        return _cache;
+    }
+    
+    public void setup () throws Exception
+    {
+       _cache = _manager.getCache(_name);
+    }
+
+
+    public void teardown () throws Exception
+    {
+        
+    }
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInvalidationSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInvalidationSessionTest.java
new file mode 100644
index 0000000..4e405d8
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInvalidationSessionTest.java
@@ -0,0 +1,93 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import org.eclipse.jetty.server.session.AbstractInvalidationSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * InvalidationSessionTest
+ *
+ *
+ */
+public class RemoteInvalidationSessionTest extends AbstractInvalidationSessionTest
+{
+
+    public static RemoteInfinispanTestSupport __testSupport;
+    public static long __staleSec = 3L;
+   
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+      __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+      __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return new InfinispanTestSessionServer(port, __testSupport.getCache());
+    }
+
+    
+    
+    
+    @Override
+    public void testInvalidation() throws Exception
+    {
+        super.testInvalidation();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#pause()
+     */
+    @Override
+    public void pause()
+    {
+        //This test moves a session from node 1 to node 2, then invalidates the session back on node1. This
+        //should never happen with a decent load balancer.
+        //The infinispan session manager on node 2 will hold the session in local memory for a specific (configurable)
+        //amount of time. We've set the stale session time to 3 sec, so we need to pause for at least this long before making
+        //another request to node2 so 
+        //that the node will re-load the session from the database and discover that it has gone.
+        try
+        {
+            Thread.sleep(2 * __staleSec * 1000);
+        }
+        catch (InterruptedException e)
+        {
+        }
+    }
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteLastAccessTimeTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteLastAccessTimeTest.java
new file mode 100644
index 0000000..6eb3c4c
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteLastAccessTimeTest.java
@@ -0,0 +1,67 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import java.io.File;
+
+import org.eclipse.jetty.server.session.AbstractLastAccessTimeTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.eclipse.jetty.util.IO;
+import org.infinispan.Cache;
+import org.infinispan.configuration.cache.Configuration;
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.manager.EmbeddedCacheManager;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class RemoteLastAccessTimeTest extends AbstractLastAccessTimeTest
+{ 
+   public static RemoteInfinispanTestSupport __testSupport;
+   
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+       __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+       __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+    }
+
+    @Override
+    public void testLastAccessTime() throws Exception
+    {
+        super.testLastAccessTime();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteLocalSessionScavengingTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteLocalSessionScavengingTest.java
new file mode 100644
index 0000000..82fa1b3
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteLocalSessionScavengingTest.java
@@ -0,0 +1,61 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import org.eclipse.jetty.server.session.AbstractLocalSessionScavengingTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * LocalSessionScavengingTest
+ *
+ *
+ */
+public class RemoteLocalSessionScavengingTest extends AbstractLocalSessionScavengingTest
+{
+
+    public static RemoteInfinispanTestSupport __testSupport;
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractLocalSessionScavengingTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+    }
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteNewSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteNewSessionTest.java
new file mode 100644
index 0000000..a2a9121
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteNewSessionTest.java
@@ -0,0 +1,69 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import org.eclipse.jetty.server.session.AbstractNewSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * NewSessionTest
+ *
+ *
+ */
+public class RemoteNewSessionTest extends AbstractNewSessionTest
+{
+
+   public static RemoteInfinispanTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractNewSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+    }
+
+  
+    @Override
+    public void testNewSession() throws Exception
+    {
+        super.testNewSession();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteReentrantRequestSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteReentrantRequestSessionTest.java
new file mode 100644
index 0000000..154a8e3
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteReentrantRequestSessionTest.java
@@ -0,0 +1,67 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import org.eclipse.jetty.server.session.AbstractReentrantRequestSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * ReentrantRequestSessionTest
+ *
+ *
+ */
+public class RemoteReentrantRequestSessionTest extends AbstractReentrantRequestSessionTest
+{
+    public static RemoteInfinispanTestSupport __testSupport;
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractReentrantRequestSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+       return new InfinispanTestSessionServer(port, __testSupport.getCache());
+    }
+
+    @Override
+    public void testReentrantRequestSession() throws Exception
+    {
+        super.testReentrantRequestSession();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteRemoveSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteRemoveSessionTest.java
new file mode 100644
index 0000000..c41b554
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteRemoveSessionTest.java
@@ -0,0 +1,61 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import org.eclipse.jetty.server.session.AbstractRemoveSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class RemoteRemoveSessionTest extends AbstractRemoveSessionTest
+{
+
+    public static RemoteInfinispanTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+       InfinispanTestSessionServer s = new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+       return s;
+    }
+
+    @Test
+    public void testRemoveSession() throws Exception
+    {
+        super.testRemoveSession();
+    }
+   
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSameNodeLoadTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSameNodeLoadTest.java
new file mode 100644
index 0000000..0a5b685
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSameNodeLoadTest.java
@@ -0,0 +1,69 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import org.eclipse.jetty.server.session.AbstractSameNodeLoadTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * SameNodeLoadTest
+ *
+ *
+ */
+public class RemoteSameNodeLoadTest extends AbstractSameNodeLoadTest
+{
+
+    public static RemoteInfinispanTestSupport __testSupport;
+
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+       __testSupport.teardown();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSameNodeLoadTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        InfinispanTestSessionServer server = new InfinispanTestSessionServer(port, __testSupport.getCache());
+        return server;
+    }
+
+    @Override
+    public void testLoad() throws Exception
+    {
+        super.testLoad();
+    }
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSessionExpiryTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSessionExpiryTest.java
new file mode 100644
index 0000000..4160a28
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSessionExpiryTest.java
@@ -0,0 +1,73 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import org.eclipse.jetty.server.session.AbstractSessionExpiryTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class RemoteSessionExpiryTest extends AbstractSessionExpiryTest
+{
+
+    public static RemoteInfinispanTestSupport __testSupport;
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+       InfinispanTestSessionServer server =  new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+       return server;
+    }
+
+    @Test
+    @Override
+    public void testSessionNotExpired() throws Exception
+    {
+        super.testSessionNotExpired();
+    }
+
+    @Test
+    @Override
+    public void testSessionExpiry() throws Exception
+    {
+       super.testSessionExpiry();
+    }
+    
+    @Override
+    public void verifySessionDestroyed (TestHttpSessionListener listener, String sessionId)
+    {
+        //noop - sessions that expired when the InfinispanSessionManager was not running are not reloaded and do not have their listeners called on them.
+    }
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSessionInvalidateAndCreateTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSessionInvalidateAndCreateTest.java
new file mode 100644
index 0000000..7493b9c
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSessionInvalidateAndCreateTest.java
@@ -0,0 +1,69 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import org.eclipse.jetty.server.session.AbstractSessionInvalidateAndCreateTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * SessionInvalidateAndCreateTest
+ *
+ *
+ */
+public class RemoteSessionInvalidateAndCreateTest extends AbstractSessionInvalidateAndCreateTest
+{
+    public static RemoteInfinispanTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionInvalidateAndCreateTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+    }
+
+    @Override
+    public void testSessionScavenge() throws Exception
+    {
+        super.testSessionScavenge();
+    }
+
+    
+    
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSessionMigrationTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSessionMigrationTest.java
new file mode 100644
index 0000000..0f6d277
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSessionMigrationTest.java
@@ -0,0 +1,68 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import org.eclipse.jetty.server.session.AbstractSessionMigrationTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * RemoteSessionMigrationTest
+ *
+ *
+ */
+public class RemoteSessionMigrationTest extends AbstractSessionMigrationTest
+{
+
+    public static RemoteInfinispanTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionMigrationTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return new InfinispanTestSessionServer(port, __testSupport.getCache());
+    }
+
+    @Override
+    public void testSessionMigration() throws Exception
+    {
+        super.testSessionMigration();
+    }
+   
+
+}
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSessionRenewTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSessionRenewTest.java
new file mode 100644
index 0000000..cfbd204
--- /dev/null
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteSessionRenewTest.java
@@ -0,0 +1,70 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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.remote;
+
+import org.eclipse.jetty.server.session.AbstractSessionRenewTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * SessionRenewTest
+ *
+ *
+ */
+public class RemoteSessionRenewTest extends AbstractSessionRenewTest
+{
+
+    public static RemoteInfinispanTestSupport __testSupport;
+    
+    
+    @BeforeClass
+    public static void setup () throws Exception
+    {
+        __testSupport = new RemoteInfinispanTestSupport("remote-session-test");
+        __testSupport.setup();
+    }
+    
+    @AfterClass
+    public static void teardown () throws Exception
+    {
+        __testSupport.teardown();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionRenewTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache());
+    }
+
+    @Test
+    public void testSessionRenewal() throws Exception
+    {
+        super.testSessionRenewal();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-jdbc-sessions/pom.xml b/tests/test-sessions/test-jdbc-sessions/pom.xml
index c92d2a0..fff9d81 100644
--- a/tests/test-sessions/test-jdbc-sessions/pom.xml
+++ b/tests/test-sessions/test-jdbc-sessions/pom.xml
@@ -1,31 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-sessions-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <artifactId>test-jdbc-sessions</artifactId>
   <name>Jetty Tests :: Sessions :: JDBC</name>
   <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.sessions.jdbc</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java
index 78eac91..39c87fc 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java
@@ -77,7 +77,7 @@
                 ContentResponse response = client.GET("http://localhost:" + port + "/mod/test?action=create");
                 
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
new file mode 100644
index 0000000..15f8350
--- /dev/null
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
@@ -0,0 +1,50 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.Test;
+
+/**
+ * ForwardedSessionTest
+ *
+ *
+ */
+public class ForwardedSessionTest extends AbstractForwardedSessionTest
+{
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractForwardedSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return new JdbcTestServer(port);
+    }
+
+    @Test
+    public void testSessionCreateInForward() throws Exception
+    {
+        super.testSessionCreateInForward();
+    }
+    
+    
+    
+
+}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java
index 4ada03c..790053f 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java
@@ -73,17 +73,17 @@
     static int __workers=0;
     
     /** 
-     * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionIdManager(String)
+     * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionIdManager(Object)
      */
     @Override
-    public  SessionIdManager newSessionIdManager(String config)
+    public  SessionIdManager newSessionIdManager(Object config)
     {
         synchronized(JdbcTestServer.class)
         {
             JDBCSessionIdManager idManager = new JDBCSessionIdManager(_server);
             idManager.setScavengeInterval(_scavengePeriod);
             idManager.setWorkerName("w"+(__workers++));
-            idManager.setDriverInfo(DRIVER_CLASS, (config==null?DEFAULT_CONNECTION_URL:config));
+            idManager.setDriverInfo(DRIVER_CLASS, (config==null?DEFAULT_CONNECTION_URL:(String)config));
             JDBCSessionIdManager.SessionIdTableSchema idTableSchema = new JDBCSessionIdManager.SessionIdTableSchema();
             idTableSchema.setTableName("mysessionids");
             idTableSchema.setIdColumn("myid");
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java
index f4d8846..857abed 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java
@@ -103,7 +103,7 @@
         ContentResponse response = request.send();
         assertEquals(HttpServletResponse.SC_OK, response.getStatus());
 
-        sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+        sessionCookie = response.getHeaders().get("Set-Cookie");
         assertTrue( sessionCookie != null );
         // Mangle the cookie, replacing Path with $Path, etc.
         sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java
index fc25736..8e4bb8f 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java
@@ -70,7 +70,7 @@
                 ContentResponse response = client.GET("http://localhost:" + port + "/mod/test?action=create");
                 
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java
index f7aee02..c40d34b 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java
@@ -34,8 +34,7 @@
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.toolchain.test.TestingDir;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.StdErrLog;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.webapp.WebAppContext;
 import org.junit.Rule;
@@ -52,12 +51,10 @@
     @Test
     public void testSessionReloadWithMissingClass() throws Exception
     {
-        ((StdErrLog)Log.getLogger(org.eclipse.jetty.server.session.JDBCSessionManager.class)).setHideStacks(true);
         Resource.setDefaultUseCaches(false);
         String contextPath = "/foo";
 
-        File unpackedWarDir = testdir.getDir();
-        testdir.ensureEmpty();
+        File unpackedWarDir = testdir.getEmptyPathDir().toFile();
 
         File webInfDir = new File (unpackedWarDir, "WEB-INF");
         webInfDir.mkdir();
@@ -94,7 +91,7 @@
         webApp.addServlet("Bar", "/bar");
         server1.start();
         int port1 = server1.getPort();
-        try
+        try (StacklessLogging stackless = new StacklessLogging(JDBCSessionManager.class))
         {
             HttpClient client = new HttpClient();
             client.start();
@@ -104,7 +101,7 @@
                 ContentResponse response = client.GET("http://localhost:" + port1 + contextPath +"/bar?action=set");
                 
                 assertEquals( HttpServletResponse.SC_OK, response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 String sessionId = (String)webApp.getServletContext().getAttribute("foo");
                 assertNotNull(sessionId);
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java
index d8d4542..5897ed3 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java
@@ -35,8 +35,8 @@
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
-import org.junit.Test;
 import org.junit.Ignore;
+import org.junit.Test;
 
 /**
  *  SaveIntervalTest
@@ -77,7 +77,7 @@
                 // Perform a request to create a session              
                 ContentResponse response = client.GET("http://localhost:" + port + "/mod/test?action=create");
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java
index 499f53d..0a87a74 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java
@@ -18,23 +18,10 @@
 
 package org.eclipse.jetty.server.session;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import java.sql.DriverManager;
 import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.List;
 
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSessionEvent;
-import javax.servlet.http.HttpSessionListener;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.log.StacklessLogging;
 import org.junit.After;
 import org.junit.Test;
 
@@ -47,22 +34,6 @@
 public class SessionExpiryTest extends AbstractSessionExpiryTest
 {
 
-    public class TestHttpSessionListener implements HttpSessionListener
-    {
-        public List<String> createdSessions = new ArrayList<String>();
-        public List<String> destroyedSessions = new ArrayList<String>();
-        
-        public void sessionDestroyed(HttpSessionEvent se)
-        {
-            destroyedSessions.add(se.getSession().getId());
-        }
-        
-        public void sessionCreated(HttpSessionEvent se)
-        {
-            createdSessions.add(se.getSession().getId());
-        }
-    };
-    
     /** 
      * @see org.eclipse.jetty.server.session.AbstractSessionExpiryTest#createServer(int, int, int)
      */
@@ -75,70 +46,12 @@
     @Test
     public void testSessionExpiry() throws Exception
     {
-     
-        
-        String contextPath = "";
-        String servletMapping = "/server";
-        int inactivePeriod = 2;
-        int scavengePeriod = 1;
-        AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod);
-        TestServlet servlet = new TestServlet();
-        ServletHolder holder = new ServletHolder(servlet);
-        ServletContextHandler context = server1.addContext(contextPath);
-        context.addServlet(holder, servletMapping);
-        TestHttpSessionListener listener = new TestHttpSessionListener();
-        
-        context.getSessionHandler().addEventListener(listener);
-        
-        server1.start();
-        int port1 = server1.getPort();
-
-        try
+        try(StacklessLogging stackless=new StacklessLogging(JDBCSessionManager.class))
         {
-            HttpClient client = new HttpClient();
-            client.start();
-            String url = "http://localhost:" + port1 + contextPath + servletMapping;
-
-            //make a request to set up a session on the server
-            ContentResponse response1 = client.GET(url + "?action=init");
-            assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
-            String sessionCookie = response1.getHeaders().getStringField("Set-Cookie");
-            assertTrue(sessionCookie != null);
-            // Mangle the cookie, replacing Path with $Path, etc.
-            sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
-            
-            String sessionId = extractSessionId(sessionCookie);     
-            
-            assertTrue(listener.createdSessions.contains(sessionId));
-            //now stop the server
-            server1.stop();
-
-            //and wait until the expiry time has passed
-            pause(inactivePeriod);
-
-            //restart the server
-            server1.start();
-            
-            port1 = server1.getPort();
-            url = "http://localhost:" + port1 + contextPath + servletMapping;
-
-            //make another request, the session should have expired
-            Request request = client.newRequest(url + "?action=test");
-            request.getHeaders().add("Cookie", sessionCookie);
-            ContentResponse response2 = request.send();
-            assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
-            
-            //and wait until the expiry time has passed
-            pause(inactivePeriod);
-            
-            assertTrue(listener.destroyedSessions.contains(sessionId));
+            super.testSessionExpiry();
         }
-        finally
-        {
-            server1.stop();
-        }     
     }
-  
+    
     
     
     
@@ -149,24 +62,7 @@
         super.testSessionNotExpired();
     }
 
-    
-    
-    public String extractSessionId (String sessionCookie)
-    {
-        if (sessionCookie == null)
-            return null;
-        sessionCookie = sessionCookie.trim();
-        int i = sessionCookie.indexOf(';');
-        if (i >= 0)
-            sessionCookie = sessionCookie.substring(0,i);
-        if (sessionCookie.startsWith("JSESSIONID"))
-            sessionCookie = sessionCookie.substring("JSESSIONID=".length());
-        i = sessionCookie.indexOf('.');
-        if (i >=0)
-            sessionCookie = sessionCookie.substring(0,i);
-        return sessionCookie;
-    }
-
+  
     
     
     @After
diff --git a/tests/test-sessions/test-mongodb-sessions/pom.xml b/tests/test-sessions/test-mongodb-sessions/pom.xml
index c47b724..13417de 100644
--- a/tests/test-sessions/test-mongodb-sessions/pom.xml
+++ b/tests/test-sessions/test-mongodb-sessions/pom.xml
@@ -1,31 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-sessions-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <artifactId>test-mongodb-sessions</artifactId>
   <name>Jetty Tests :: Sessions :: Mongo</name>
   <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.sessions.mongo</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/AttributeNameTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/AttributeNameTest.java
index f9b8064..afee62c 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/AttributeNameTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/AttributeNameTest.java
@@ -33,6 +33,7 @@
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.nosql.NoSqlSession;
 import org.eclipse.jetty.server.session.AbstractTestServer;
 import org.junit.Test;
@@ -94,7 +95,8 @@
                 assertEquals("a.b.c",sessionTestResponse[0]);
  
 
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get(HttpHeader.SET_COOKIE);
+                      
                 assertTrue(sessionCookie != null);
                 //Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=","$1\\$Path=");
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ClientCrossContextSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ClientCrossContextSessionTest.java
index 8825050..5ded11f 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ClientCrossContextSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ClientCrossContextSessionTest.java
@@ -20,7 +20,6 @@
 
 import org.eclipse.jetty.server.session.AbstractClientCrossContextSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class ClientCrossContextSessionTest extends AbstractClientCrossContextSessionTest
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ForwardedSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ForwardedSessionTest.java
new file mode 100644
index 0000000..6e2c053
--- /dev/null
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ForwardedSessionTest.java
@@ -0,0 +1,48 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.nosql.mongodb;
+
+import org.eclipse.jetty.server.session.AbstractForwardedSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.Test;
+
+/**
+ * ForwardedSessionTest
+ *
+ *
+ */
+public class ForwardedSessionTest extends AbstractForwardedSessionTest
+{
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractForwardedSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return new MongoTestServer(port);
+    }
+    
+    @Test
+    public void testSessionCreateInForward() throws Exception
+    {
+        super.testSessionCreateInForward();
+    }
+}
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/IdleSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/IdleSessionTest.java
new file mode 100644
index 0000000..65f22cd
--- /dev/null
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/IdleSessionTest.java
@@ -0,0 +1,233 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.nosql.mongodb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.nosql.NoSqlSession;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.Test;
+
+/**
+ * IdleSessionTest
+ * 
+ * Test that mongo sessions can be passivated if idle longer than a configurable
+ * interval (which should be shorter than the expiry interval!)
+ *
+ */
+public class IdleSessionTest
+{
+    public static TestServlet _servlet = new TestServlet();
+    
+
+    public MongoTestServer createServer(int port, int max, int scavenge)
+    {
+        MongoTestServer server =  new MongoTestServer(port,max,scavenge);
+
+        return server;
+    }
+    
+
+    public void pause (int sec)
+    {
+        try
+        {
+            Thread.sleep(sec * 1000L);
+        }
+        catch (InterruptedException e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    
+
+    @Test
+    public void testIdleSession() throws Exception
+    {
+        String contextPath = "";
+        String servletMapping = "/server";
+        int inactivePeriod = 20; //sessions expire after 20 seconds
+        int scavengePeriod = 1; //look for expired sessions every second
+        int idlePeriod = 3; //after 3 seconds of inactivity, idle to disk
+        
+        
+        MongoTestServer server1 = createServer(0, inactivePeriod, scavengePeriod);
+        ServletHolder holder = new ServletHolder(_servlet);
+        ServletContextHandler contextHandler = server1.addContext(contextPath);
+        ((MongoSessionManager)contextHandler.getSessionHandler().getSessionManager()).setIdlePeriod(idlePeriod);
+        contextHandler.addServlet(holder, servletMapping);
+        server1.start();
+        int port1 = server1.getPort();
+
+        try
+        {
+            HttpClient client = new HttpClient();
+            client.start();
+            String url = "http://localhost:" + port1 + contextPath + servletMapping;
+
+            //make a request to set up a session on the server
+            ContentResponse response = client.GET(url + "?action=init");
+            assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+            String sessionCookie = response.getHeaders().get("Set-Cookie");
+            assertTrue(sessionCookie != null);
+            // Mangle the cookie, replacing Path with $Path, etc.
+            sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+
+            //and wait until the session should be idled out
+            pause(idlePeriod * 2);
+            
+            //check that the session is idle
+            checkSessionIdle();
+
+            //make another request to de-idle the session
+            Request request = client.newRequest(url + "?action=test");
+            request.getHeaders().add("Cookie", sessionCookie);
+            ContentResponse response2 = request.send();
+            assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
+
+            //check session de-idled
+            checkSessionDeIdle();
+            checkValue(2);
+            
+            //wait again for the session to be idled
+            pause(idlePeriod * 2);
+
+            //check that it is
+            checkSessionIdle();
+            
+            //While idle, take some action to ensure that a deidle won't work, like
+            //deleting all sessions in mongo
+            assertTrue(server1.getServer().getSessionIdManager() instanceof MongoTestServer.TestMongoSessionIdManager);
+            ((MongoTestServer.TestMongoSessionIdManager)server1.getServer().getSessionIdManager()).deleteAll();
+            
+            //now make a request for which deidle should fail
+            request = client.newRequest(url + "?action=testfail");
+            request.getHeaders().add("Cookie", sessionCookie);
+            response2 = request.send();
+            assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
+            
+            //Test trying to de-idle an expired session (ie before the scavenger can get to it)
+            
+            //make a request to set up a session on the server
+            response = client.GET(url + "?action=init");
+            assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+            sessionCookie = response.getHeaders().get("Set-Cookie");
+            assertTrue(sessionCookie != null);
+            // Mangle the cookie, replacing Path with $Path, etc.
+            sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+
+            //and wait until the session should be idled out
+            pause(idlePeriod * 2);
+            
+            //stop the scavenger
+            ((MongoTestServer.TestMongoSessionIdManager)server1.getServer().getSessionIdManager()).cancelScavenge();
+
+            //check that the session is idle
+            checkSessionIdle();
+
+            //wait until the session should be expired
+            pause (inactivePeriod + (inactivePeriod/2));
+            
+            //make a request to try and deidle the session
+            //make another request to de-idle the session
+            request = client.newRequest(url + "?action=testfail");
+            request.getHeaders().add("Cookie", sessionCookie);
+            response2 = request.send();
+            assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
+        }
+        finally
+        {
+            server1.stop();
+        }
+    }
+    
+    public void checkSessionIdle ()
+    {
+        assertNotNull(_servlet);
+        assertNotNull((NoSqlSession)_servlet._session);
+        assertTrue(((NoSqlSession)_servlet._session).isIdle());    
+    }
+    
+    
+    public void checkSessionDeIdle ()
+    {
+        assertNotNull(_servlet);
+        assertNotNull((NoSqlSession)_servlet._session);
+        assertTrue(!((NoSqlSession)_servlet._session).isIdle());  
+        assertTrue(!((NoSqlSession)_servlet._session).isDeIdleFailed());
+    }
+
+    
+    public void checkValue (int value)
+    {
+        assertNotNull(_servlet);
+        assertEquals(value, ((Integer)_servlet._session.getAttribute("value")).intValue());
+    }
+    
+    public static class TestServlet extends HttpServlet
+    {
+        public String originalId = null;
+        
+        public HttpSession _session = null;
+
+        @Override
+        protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
+        {
+            String action = request.getParameter("action");
+            if ("init".equals(action))
+            {
+                HttpSession session = request.getSession(true);
+                session.setAttribute("value", new Integer(1));
+                originalId = session.getId();
+                assertTrue(!((NoSqlSession)session).isIdle());
+                _session = session;
+            }
+            else if ("test".equals(action))
+            {
+                HttpSession session = request.getSession(false);
+                assertTrue(session != null);
+                assertTrue(originalId.equals(session.getId()));
+                assertTrue(!((NoSqlSession)session).isIdle());
+                Integer v = (Integer)session.getAttribute("value");
+                assertNotNull(v);
+                session.setAttribute("value", new Integer(v.intValue()+1));
+            }
+            else if ("testfail".equals(action))
+            {
+                HttpSession session = request.getSession(false);
+                assertTrue(session == null);
+            }
+        }
+    }
+}
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ImmortalSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ImmortalSessionTest.java
new file mode 100644
index 0000000..b6b5cd8
--- /dev/null
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ImmortalSessionTest.java
@@ -0,0 +1,42 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.nosql.mongodb;
+
+import org.eclipse.jetty.server.session.AbstractImmortalSessionTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+
+/**
+ * ImmortalSessionTest
+ *
+ *
+ */
+public class ImmortalSessionTest extends AbstractImmortalSessionTest
+{
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractImmortalSessionTest#createServer(int, int, int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port, int maxInactiveMs, int scavengeMs)
+    {
+        return new MongoTestServer(port, maxInactiveMs, scavengeMs);
+    }
+
+}
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LightLoadTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LightLoadTest.java
deleted file mode 100644
index 3f2be75..0000000
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LightLoadTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.nosql.mongodb;
-
-import org.eclipse.jetty.server.session.AbstractLightLoadTest;
-import org.eclipse.jetty.server.session.AbstractTestServer;
-import org.junit.Ignore;
-import org.junit.Test;
-
-/**
- * LightLoadTest
- */
-public class LightLoadTest extends AbstractLightLoadTest
-{
-
-    public AbstractTestServer createServer(int port)
-    {
-        return new MongoTestServer(port);
-    }
-
-    @Test
-    public void testLightLoad() throws Exception
-    {
-        super.testLightLoad();
-    }
-
-}
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LocalSessionScavengingTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LocalSessionScavengingTest.java
index 3d1189d..6ec0b44 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LocalSessionScavengingTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LocalSessionScavengingTest.java
@@ -27,7 +27,9 @@
     @Override
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
-       return new MongoTestServer(port,max,scavenge);
+       MongoTestServer mserver=new MongoTestServer(port,max,scavenge);
+       ((MongoSessionIdManager)mserver.getServer().getSessionIdManager()).setScavengeBlockSize(0);
+       return mserver;
     }
 
     @Override
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java
index 419acfb..70af3fa 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java
@@ -18,15 +18,18 @@
 
 package org.eclipse.jetty.nosql.mongodb;
 
-import java.util.concurrent.TimeUnit;
+import java.net.UnknownHostException;
 
-import org.eclipse.jetty.nosql.mongodb.MongoSessionIdManager;
-import org.eclipse.jetty.nosql.mongodb.MongoSessionManager;
+import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.SessionIdManager;
 import org.eclipse.jetty.server.SessionManager;
 import org.eclipse.jetty.server.session.AbstractTestServer;
 import org.eclipse.jetty.server.session.SessionHandler;
 
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import com.mongodb.MongoException;
+
 
 /**
  * @version $Revision$ $Date$
@@ -36,6 +39,34 @@
     static int __workers=0;
     private boolean _saveAllAttributes = false; // false save dirty, true save all
     
+    
+    public static class TestMongoSessionIdManager extends MongoSessionIdManager 
+    {
+
+        public TestMongoSessionIdManager(Server server) throws UnknownHostException, MongoException
+        {
+            super(server);
+        }
+        
+        
+        public void deleteAll ()
+        {
+            
+            DBCursor checkSessions = _sessions.find();
+
+            for (DBObject session : checkSessions)
+            {
+                _sessions.remove(session);
+            }
+        }
+        
+        public void cancelScavenge ()
+        {
+            if (_scavengerTask != null)
+                _scavengerTask.cancel();
+        }
+    }
+    
     public MongoTestServer(int port)
     {
         super(port, 30, 10);
@@ -54,12 +85,11 @@
         _saveAllAttributes = saveAllAttributes;
     }
 
-    public SessionIdManager newSessionIdManager(String config)
+    public SessionIdManager newSessionIdManager(Object config)
     {
         try
         {
-            System.err.println("MongoTestServer:SessionIdManager scavenge: delay:"+ _scavengePeriod + " period:"+_scavengePeriod);
-            MongoSessionIdManager idManager = new MongoSessionIdManager(_server);
+            MongoSessionIdManager idManager = new TestMongoSessionIdManager(_server);
             idManager.setWorkerName("w"+(__workers++));
             idManager.setScavengePeriod(_scavengePeriod);                  
 
@@ -83,7 +113,7 @@
             throw new RuntimeException(e);
         }
         
-        manager.setSavePeriod(1);
+        manager.setSavePeriod(0);
         manager.setStalePeriod(0);
         manager.setSaveAllAttributes(_saveAllAttributes);
         //manager.setScavengePeriod((int)TimeUnit.SECONDS.toMillis(_scavengePeriod));
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/NewSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/NewSessionTest.java
index a080392..ef99ae2 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/NewSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/NewSessionTest.java
@@ -20,7 +20,6 @@
 
 import org.eclipse.jetty.server.session.AbstractNewSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
-import org.junit.Ignore;
 import org.junit.Test;
 
 /**
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/OrphanedSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/OrphanedSessionTest.java
index 7c2b619..eab0eca 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/OrphanedSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/OrphanedSessionTest.java
@@ -20,7 +20,6 @@
 
 import org.eclipse.jetty.server.session.AbstractOrphanedSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
-import org.junit.Ignore;
 import org.junit.Test;
 
 /**
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeInvalidSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeInvalidSessionTest.java
index 924dd17..6c36669 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeInvalidSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeInvalidSessionTest.java
@@ -19,8 +19,8 @@
 package org.eclipse.jetty.nosql.mongodb;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.net.UnknownHostException;
@@ -73,7 +73,7 @@
         MongoTestServer server = createServer(0, 1, 0);
         ServletContextHandler context = server.addContext(contextPath);
         context.addServlet(TestServlet.class, servletMapping);
-        
+
         MongoSessionManager sessionManager = (MongoSessionManager)context.getSessionHandler().getSessionManager();
         MongoSessionIdManager idManager = (MongoSessionIdManager)server.getServer().getSessionIdManager();
         idManager.setPurge(true);
@@ -94,7 +94,7 @@
                 //Create a session
                 ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
@@ -124,8 +124,83 @@
         }
 
     }
-    
-    
+
+
+    @Test
+    public void testPurgeInvalidSessionsWithLimit() throws Exception
+    {
+        String contextPath = "";
+        String servletMapping = "/server";
+        long purgeInvalidAge = 1000; //1 sec
+        int purgeLimit = 5; // only purge 5 sessions for each purge run
+
+        //ensure scavenging is turned off so the purger gets a chance to find the session
+        MongoTestServer server = createServer(0, 1, 0);
+        ServletContextHandler context = server.addContext(contextPath);
+        context.addServlet(TestServlet.class, servletMapping);
+
+        // disable purging so we can call it manually below
+        MongoSessionManager sessionManager = (MongoSessionManager)context.getSessionHandler().getSessionManager();
+        MongoSessionIdManager idManager = (MongoSessionIdManager)server.getServer().getSessionIdManager();
+        idManager.setPurge(false);
+        idManager.setPurgeLimit(purgeLimit);
+        idManager.setPurgeInvalidAge(purgeInvalidAge);
+        // don't purge valid sessions
+        idManager.setPurgeValidAge(0);
+
+
+        server.start();
+        int port=server.getPort();
+        try
+        {
+            // cleanup any previous sessions that are invalid so that we are starting fresh
+            idManager.purgeFully();
+            long sessionCountAtTestStart = sessionManager.getSessionStoreCount();
+
+            HttpClient client = new HttpClient();
+            client.start();
+            try
+            {
+                // create double the purge limit of sessions, and make them all invalid
+                for (int i = 0; i < purgeLimit * 2; i++)
+                {
+                    ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
+                    assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+                    String sessionCookie = response.getHeaders().get("Set-Cookie");
+                    assertTrue(sessionCookie != null);
+                    // Mangle the cookie, replacing Path with $Path, etc.
+                    sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+
+
+                    Request request = client.newRequest("http://localhost:" + port + contextPath + servletMapping + "?action=invalidate");
+                    request.header("Cookie", sessionCookie);
+                    response = request.send();
+                    assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+                }
+
+                // sleep for our invalid age period so that the purge below does something
+                Thread.sleep(purgeInvalidAge * 2);
+
+                // validate that we have the right number of sessions before we purge
+                assertEquals("Expected to find right number of sessions before purge", sessionCountAtTestStart + (purgeLimit * 2), sessionManager.getSessionStoreCount());
+
+                // run our purge we should still have items in the DB
+                idManager.purge();
+                assertEquals("Expected to find sessions remaining in db after purge run with limit set",
+                        sessionCountAtTestStart + purgeLimit, sessionManager.getSessionStoreCount());
+            }
+            finally
+            {
+                client.stop();
+            }
+        }
+        finally
+        {
+            server.stop();
+        }
+    }
+
+
     public static class TestServlet extends HttpServlet
     {
         DBCollection _sessions;
@@ -163,6 +238,8 @@
                 dbSession = _sessions.findOne(new BasicDBObject("id", id));       
                 assertNotNull(dbSession);
                 assertTrue(dbSession.containsField(MongoSessionManager.__INVALIDATED));
+                assertTrue(dbSession.containsField(MongoSessionManager.__VALID));
+                assertTrue(dbSession.get(MongoSessionManager.__VALID).equals(false));
             }
             else if ("test".equals(action))
             {
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeValidSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeValidSessionTest.java
index 05da3e0..de6d284 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeValidSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeValidSessionTest.java
@@ -19,8 +19,8 @@
 package org.eclipse.jetty.nosql.mongodb;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.net.UnknownHostException;
@@ -31,10 +31,10 @@
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
-
 import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.nosql.mongodb.MongoTestServer.TestMongoSessionIdManager;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.junit.Test;
 
@@ -97,7 +97,7 @@
                 //Create a session
                 ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
@@ -124,6 +124,75 @@
     }
 
 
+    @Test
+    public void testPurgeValidSessionWithPurgeLimitSet() throws Exception
+    {
+        String contextPath = "";
+        String servletMapping = "/server";
+        long purgeDelay = 1000; //1 sec
+        long purgeValidAge = 1000; // 1 sec
+        int purgeLimit = 5; // only purge 5 items in each purge run
+
+        //ensure scavenging is turned off so the purger gets a chance to find the session
+        MongoTestServer server = createServer(0, 1, 0);
+        ServletContextHandler context = server.addContext(contextPath);
+        context.addServlet(TestServlet.class, servletMapping);
+
+        MongoSessionManager sessionManager = (MongoSessionManager)context.getSessionHandler().getSessionManager();
+        MongoSessionIdManager idManager = (MongoSessionIdManager)server.getServer().getSessionIdManager();
+        // disable purging we will run it manually below
+        idManager.setPurge(false);
+        idManager.setPurgeLimit(purgeLimit);
+        idManager.setPurgeDelay(purgeDelay);
+        idManager.setPurgeValidAge(purgeValidAge); //purge valid sessions older than
+
+        server.start();
+        int port=server.getPort();
+
+        try
+        {
+            // start with no sessions
+            ((TestMongoSessionIdManager)idManager).deleteAll();
+
+            HttpClient client = new HttpClient();
+            client.start();
+            try
+            {
+                // create double our purgeLmit number of sessions
+                for (int i = 0; i < purgeLimit * 2; i++)
+                {
+                    ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
+                    assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+                    String sessionCookie = response.getHeaders().get("Set-Cookie");
+                    assertTrue(sessionCookie != null);
+                    // don't remember our cookies from call to call
+                    client.getCookieStore().removeAll();
+                }
+
+                // delay long enough for purgeValidAge to apply
+                Thread.sleep(2* purgeValidAge);
+
+                // validate that we have the right number of sessions before we purge
+                assertEquals("Expected to find right number of sessions before purge", purgeLimit * 2, sessionManager.getSessionStoreCount());
+
+                // run our purge
+                idManager.purge();
+
+                assertEquals("Expected to find sessions remaining in db after purge run with limit set",
+                        purgeLimit, sessionManager.getSessionStoreCount());
+            }
+            finally
+            {
+                client.stop();
+            }
+        }
+        finally
+        {
+            server.stop();
+        }
+
+    }
+
     public static class TestServlet extends HttpServlet
     {
         DBCollection _sessions;
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ReentrantRequestSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ReentrantRequestSessionTest.java
index 75b468f..75a9736 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ReentrantRequestSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ReentrantRequestSessionTest.java
@@ -20,7 +20,6 @@
 
 import org.eclipse.jetty.server.session.AbstractReentrantRequestSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
-import org.junit.Ignore;
 import org.junit.Test;
 
 /**
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/RemoveSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/RemoveSessionTest.java
index 58aff68..fa4cfac 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/RemoveSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/RemoveSessionTest.java
@@ -20,7 +20,6 @@
 
 import org.eclipse.jetty.server.session.AbstractRemoveSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class RemoveSessionTest extends AbstractRemoveSessionTest
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ScatterGunLoadTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ScatterGunLoadTest.java
new file mode 100644
index 0000000..144fe5c
--- /dev/null
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ScatterGunLoadTest.java
@@ -0,0 +1,42 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.nosql.mongodb;
+
+import org.eclipse.jetty.server.session.AbstractScatterGunLoadTest;
+import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.Test;
+
+/**
+ * ScatterGunLoadTest
+ */
+public class ScatterGunLoadTest extends AbstractScatterGunLoadTest
+{
+
+    public AbstractTestServer createServer(int port)
+    {
+        return new MongoTestServer(port);
+    }
+
+    @Test
+    public void testLightLoad() throws Exception
+    {
+        super.testLightLoad();
+    }
+
+}
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ServerCrossContextSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ServerCrossContextSessionTest.java
index b0465ab..dd7188d 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ServerCrossContextSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ServerCrossContextSessionTest.java
@@ -20,7 +20,6 @@
 
 import org.eclipse.jetty.server.session.AbstractServerCrossContextSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
-import org.junit.Ignore;
 import org.junit.Test;
 
 
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionDump.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionDump.java
index 59b61d7..36656fb 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionDump.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionDump.java
@@ -24,6 +24,7 @@
 import java.util.Date;
 import java.util.Enumeration;
 
+import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
@@ -178,7 +179,7 @@
     /* ------------------------------------------------------------ */
     private String getURI(HttpServletRequest request)
     {
-        String uri=(String)request.getAttribute("javax.servlet.forward.request_uri");
+        String uri=(String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
         if (uri==null)
             uri=request.getRequestURI();
         return uri;
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionExpiryTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionExpiryTest.java
index a33c097..cfa05a3 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionExpiryTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionExpiryTest.java
@@ -18,10 +18,34 @@
 
 package org.eclipse.jetty.nosql.mongodb;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.server.session.AbstractSessionExpiryTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.StringUtil;
 import org.junit.Test;
 
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBCollection;
+import com.mongodb.DBObject;
+
+
 public class SessionExpiryTest extends AbstractSessionExpiryTest
 {
 
@@ -42,4 +66,351 @@
     {
         super.testSessionExpiry();
     }
+    
+    @Test
+    public void testBigSessionExpiry() throws Exception
+    {
+        String contextPath = "";
+        String servletMapping = "/server";
+        int inactivePeriod = Integer.MAX_VALUE * 60; //integer overflow
+        int scavengePeriod = 1;
+        AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod);
+        ChangeTimeoutServlet servlet = new ChangeTimeoutServlet();
+        ServletHolder holder = new ServletHolder(servlet);
+        ServletContextHandler context = server1.addContext(contextPath);
+        context.addServlet(holder, servletMapping);
+        TestHttpSessionListener listener = new TestHttpSessionListener();
+        
+        context.getSessionHandler().addEventListener(listener);
+        
+        server1.start();
+        int port1 = server1.getPort();
+
+        try
+        {
+            HttpClient client = new HttpClient();
+            client.start();
+            String url = "http://localhost:" + port1 + contextPath + servletMapping;
+
+            //make a request to set up a session on the server
+            ContentResponse response1 = client.GET(url + "?action=init");
+            assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
+            String sessionCookie = response1.getHeaders().get("Set-Cookie");
+            assertTrue(sessionCookie != null);
+            // Mangle the cookie, replacing Path with $Path, etc.
+            sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+            
+            String sessionId = AbstractTestServer.extractSessionId(sessionCookie);     
+            
+            DBCollection sessions = ((MongoSessionIdManager)((MongoTestServer)server1).getServer().getSessionIdManager()).getSessions();
+            verifySessionCreated(listener,sessionId);
+            //verify that the session timeout is set in mongo
+            verifySessionTimeout(sessions, sessionId, inactivePeriod);
+            
+            //get the session expiry time from mongo
+            long expiry = getSessionExpiry(sessions, sessionId);
+            assertEquals(0, expiry);
+
+        }
+        finally
+        {
+            server1.stop();
+        } 
+    }
+    
+    @Test
+    public void changeSessionTimeout() throws Exception
+    {
+        String contextPath = "";
+        String servletMapping = "/server";
+        int inactivePeriod = 10;
+        int scavengePeriod = 1;
+        AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod);
+        ChangeTimeoutServlet servlet = new ChangeTimeoutServlet();
+        ServletHolder holder = new ServletHolder(servlet);
+        ServletContextHandler context = server1.addContext(contextPath);
+        context.addServlet(holder, servletMapping);
+        TestHttpSessionListener listener = new TestHttpSessionListener();
+        
+        context.getSessionHandler().addEventListener(listener);
+        
+        server1.start();
+        int port1 = server1.getPort();
+
+        try
+        {
+            HttpClient client = new HttpClient();
+            client.start();
+            String url = "http://localhost:" + port1 + contextPath + servletMapping;
+
+            //make a request to set up a session on the server
+            ContentResponse response1 = client.GET(url + "?action=init");
+            assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
+            String sessionCookie = response1.getHeaders().get("Set-Cookie");
+            assertTrue(sessionCookie != null);
+            // Mangle the cookie, replacing Path with $Path, etc.
+            sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+            
+            String sessionId = AbstractTestServer.extractSessionId(sessionCookie);     
+            
+            DBCollection sessions = ((MongoSessionIdManager)((MongoTestServer)server1).getServer().getSessionIdManager()).getSessions();
+            verifySessionCreated(listener,sessionId);
+            //verify that the session timeout is set in mongo
+            verifySessionTimeout(sessions, sessionId, inactivePeriod);
+            
+            //get the session expiry time from mongo
+            long expiry = getSessionExpiry(sessions, sessionId);
+           
+            //make another request to change the session timeout to a smaller value
+            inactivePeriod = 5;
+            Request request = client.newRequest(url + "?action=change&val="+inactivePeriod);
+            request.getHeaders().add("Cookie", sessionCookie);
+            ContentResponse response2 = request.send();
+            assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
+ 
+            
+            //check the timeout in mongo
+            verifySessionTimeout(sessions, sessionId, inactivePeriod);
+            //check the session expiry time has decreased from previous value
+            assertTrue(getSessionExpiry(sessions, sessionId)<expiry);
+            expiry = getSessionExpiry(sessions, sessionId);
+            
+            //increase the session timeout
+            inactivePeriod = 20;
+            request = client.newRequest(url + "?action=change&val="+inactivePeriod);
+            request.getHeaders().add("Cookie", sessionCookie);
+            response2 = request.send();
+            assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
+            //verify that the session timeout is set in mongo
+            verifySessionTimeout(sessions, sessionId, inactivePeriod);
+            assertTrue(getSessionAccessed(sessions, sessionId)+ (1000L*inactivePeriod) == getSessionExpiry(sessions, sessionId));            
+        }
+        finally
+        {
+            server1.stop();
+        }     
+    }
+    
+    
+    @Test
+    public void testChangeNewSessionTimeout () throws Exception
+    {
+        String contextPath = "";
+        String servletMapping = "/server";
+        int inactivePeriod = 10;
+        int scavengePeriod = 1;
+        AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod);
+        ImmediateChangeTimeoutServlet servlet = new ImmediateChangeTimeoutServlet();
+        ServletHolder holder = new ServletHolder(servlet);
+        ServletContextHandler context = server1.addContext(contextPath);
+        context.addServlet(holder, servletMapping);
+        TestHttpSessionListener listener = new TestHttpSessionListener();
+        
+        context.getSessionHandler().addEventListener(listener);
+        
+        server1.start();
+        int port1 = server1.getPort();
+
+        try
+        {
+            HttpClient client = new HttpClient();
+            client.start();
+            String url = "http://localhost:" + port1 + contextPath + servletMapping;
+
+            inactivePeriod = 5; //change from the sessionmanager configured default
+            
+            //make a request to set up a session on the server and change its inactive setting straight away
+            ContentResponse response1 = client.GET(url + "?action=init&val="+inactivePeriod);
+            assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
+            String sessionCookie = response1.getHeaders().get("Set-Cookie");
+            assertTrue(sessionCookie != null);
+            // Mangle the cookie, replacing Path with $Path, etc.
+            sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+            
+            String sessionId = AbstractTestServer.extractSessionId(sessionCookie);     
+            
+            DBCollection sessions = ((MongoSessionIdManager)((MongoTestServer)server1).getServer().getSessionIdManager()).getSessions();
+            verifySessionCreated(listener,sessionId);
+            //verify that the session timeout is the new value and not the default
+            verifySessionTimeout(sessions, sessionId, inactivePeriod);             
+        }
+        finally
+        {
+            server1.stop();
+        }     
+    }
+    
+    @Test
+    public void testRequestForSessionWithChangedTimeout () throws Exception
+    {
+      String contextPath = "";
+      String servletMapping = "/server";
+      int inactivePeriod = 5;
+      int scavengePeriod = 1;
+      AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod);
+      ChangeTimeoutServlet servlet = new ChangeTimeoutServlet();
+      ServletHolder holder = new ServletHolder(servlet);
+      ServletContextHandler context = server1.addContext(contextPath);
+      context.addServlet(holder, servletMapping);
+      TestHttpSessionListener listener = new TestHttpSessionListener();
+      
+      context.getSessionHandler().addEventListener(listener);
+      
+      server1.start();
+      int port1 = server1.getPort();
+
+      try
+      {
+          HttpClient client = new HttpClient();
+          client.start();
+          String url = "http://localhost:" + port1 + contextPath + servletMapping;
+
+          //make a request to set up a session on the server with the session manager's inactive timeout
+          ContentResponse response = client.GET(url + "?action=init");
+          assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+          String sessionCookie = response.getHeaders().get("Set-Cookie");
+          assertTrue(sessionCookie != null);
+          // Mangle the cookie, replacing Path with $Path, etc.
+          sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+             
+         
+          //make another request to change the session timeout to a larger value
+          int newInactivePeriod = 100;
+          Request request = client.newRequest(url + "?action=change&val="+newInactivePeriod);
+          request.getHeaders().add("Cookie", sessionCookie);
+          response = request.send();
+          assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+          
+          //stop and restart the session manager to ensure it needs to reload the session
+          context.stop();
+          context.start();
+
+          //wait until the session manager timeout has passed and re-request the session
+          //which should still be valid
+          Thread.currentThread().sleep(inactivePeriod*1000L);
+
+          request = client.newRequest(url + "?action=check");
+          request.getHeaders().add("Cookie", sessionCookie);
+          response = request.send();
+          assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+          String sessionCookie2 = response.getHeaders().get("Set-Cookie");
+          assertNull(sessionCookie2);
+          
+      }
+      finally
+      {
+          server1.stop();
+      }     
+    }
+    
+    
+    public void verifySessionTimeout (DBCollection sessions, String id, int sec) throws Exception
+    {
+        assertNotNull(sessions);
+        assertNotNull(id);
+        
+        DBObject o = sessions.findOne(new BasicDBObject(MongoSessionManager.__ID,id));
+        assertNotNull(o);
+        Integer maxIdle = (Integer)o.get(MongoSessionManager.__MAX_IDLE);
+        assertNotNull(maxIdle);
+        assertEquals(sec, maxIdle.intValue());
+    }
+    
+    public long getSessionExpiry (DBCollection sessions, String id) throws Exception
+    {
+        assertNotNull(sessions);
+        assertNotNull(id);
+        
+        DBObject o = sessions.findOne(new BasicDBObject(MongoSessionManager.__ID,id));
+        assertNotNull(o);
+        Long expiry = (Long)o.get(MongoSessionManager.__EXPIRY);
+        return (expiry == null? null : expiry.longValue());
+    }
+    
+    public int getSessionMaxInactiveInterval (DBCollection sessions, String id) throws Exception
+    {
+        assertNotNull(sessions);
+        assertNotNull(id);
+        
+        DBObject o = sessions.findOne(new BasicDBObject(MongoSessionManager.__ID,id));
+        assertNotNull(o);
+        Integer inactiveInterval = (Integer)o.get(MongoSessionManager.__MAX_IDLE);
+        return (inactiveInterval == null? null : inactiveInterval.intValue());
+    }
+    
+    public long getSessionAccessed (DBCollection sessions, String id) throws Exception
+    {
+        assertNotNull(sessions);
+        assertNotNull(id);
+        
+        DBObject o = sessions.findOne(new BasicDBObject(MongoSessionManager.__ID,id));
+        assertNotNull(o);
+        Long accessed = (Long)o.get(MongoSessionManager.__ACCESSED);
+        return (accessed == null? null : accessed.longValue());
+    }
+    
+    public void debugPrint (DBCollection sessions, String id) throws Exception
+    {
+        assertNotNull(sessions);
+        assertNotNull(id);
+        
+        DBObject o = sessions.findOne(new BasicDBObject(MongoSessionManager.__ID,id));
+        assertNotNull(o);
+        System.err.println(o);
+    }
+    
+    public static class ChangeTimeoutServlet extends HttpServlet
+    {
+
+        @Override
+        protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
+        {
+            String action = request.getParameter("action");
+            if ("init".equals(action))
+            {
+                HttpSession session = request.getSession(true);
+                session.setAttribute("test", "test");
+            }
+            else if ("change".equals(action))
+            {
+                String tmp = request.getParameter("val");
+                int val = (StringUtil.isBlank(tmp)?0:Integer.valueOf(tmp.trim()));
+                HttpSession session = request.getSession(false);
+                assertNotNull(session);
+                session.setMaxInactiveInterval(val);
+            }
+            else if ("check".equals(action))
+            {
+                HttpSession session = request.getSession(false);
+                assertNotNull(session);
+            }
+        }
+    }
+    
+    public static class ImmediateChangeTimeoutServlet extends HttpServlet
+    {
+
+        @Override
+        protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
+        {
+            String action = request.getParameter("action");
+            if ("init".equals(action))
+            {
+                HttpSession session = request.getSession(true);
+                assertNotNull(session);
+                String tmp = request.getParameter("val");
+                int val = (StringUtil.isBlank(tmp)?0:Integer.valueOf(tmp.trim()));
+                session.setMaxInactiveInterval(val);
+            }
+            else if ("change".equals(action))
+            {
+                String tmp = request.getParameter("val");
+                int val = (StringUtil.isBlank(tmp)?0:Integer.valueOf(tmp.trim()));
+                HttpSession session = request.getSession(false);
+                assertNotNull(session);
+                session.setMaxInactiveInterval(val);
+            }
+        }
+    }
+
 }
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionInvalidateAndCreateTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionInvalidateAndCreateTest.java
index 9f769ef..ad9df8a 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionInvalidateAndCreateTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionInvalidateAndCreateTest.java
@@ -20,7 +20,6 @@
 
 import org.eclipse.jetty.server.session.AbstractSessionInvalidateAndCreateTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class SessionInvalidateAndCreateTest extends AbstractSessionInvalidateAndCreateTest
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionSavingValueTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionSavingValueTest.java
index 9a88170..b9f9561 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionSavingValueTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionSavingValueTest.java
@@ -26,7 +26,6 @@
 import java.io.Serializable;
 import java.lang.management.ManagementFactory;
 import java.net.MalformedURLException;
-import java.util.concurrent.Future;
 
 import javax.management.remote.JMXServiceURL;
 import javax.servlet.ServletException;
@@ -117,7 +116,7 @@
 
                 sessionTestValue = sessionTestResponse;
 
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=","$1\\$Path=");
@@ -144,7 +143,7 @@
 
                     sessionTestValue = sessionTestResponse;
 
-                    String setCookie = response2.getHeaders().getStringField("Set-Cookie");
+                    String setCookie = response2.getHeaders().get("Set-Cookie");
                     if (setCookie != null)
                         sessionCookie = setCookie.replaceFirst("(\\W)(P|p)ath=","$1\\$Path=");
 
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerDeleteSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerDeleteSessionTest.java
index 4a11b52..0eb8c22 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerDeleteSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerDeleteSessionTest.java
@@ -86,7 +86,7 @@
                 //Create a session
                 ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-sessions-common/pom.xml b/tests/test-sessions/test-sessions-common/pom.xml
index 3f0a10d..1bd1834 100644
--- a/tests/test-sessions/test-sessions-common/pom.xml
+++ b/tests/test-sessions/test-sessions-common/pom.xml
@@ -1,31 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-sessions-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <artifactId>test-sessions-common</artifactId>
   <name>Jetty Tests :: Sessions :: Common</name>
   <url>http://www.eclipse.org/jetty</url>
+    <properties>
+    <bundle-symbolic-name>${project.groupId}.sessions.common</bundle-symbolic-name>
+  </properties>
   <build>
   </build>
   <dependencies>
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractClientCrossContextSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractClientCrossContextSessionTest.java
index 31cda7f..5f18094 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractClientCrossContextSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractClientCrossContextSessionTest.java
@@ -74,7 +74,7 @@
                 ContentResponse response = client.GET("http://localhost:" + port + contextA + servletMapping);
 
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractForwardedSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractForwardedSessionTest.java
new file mode 100644
index 0000000..fd71ebf
--- /dev/null
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractForwardedSessionTest.java
@@ -0,0 +1,183 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.junit.Test;
+
+
+/**
+ * AbstractForwardedSessionTest
+ *
+ * Test that creating a session inside a forward on the same context works, and that
+ * attributes set after the forward returns are preserved.
+ * 
+ * This test requires that the sessions will be persisted, as the server is stopped and
+ * then restarted in order to check that all the attributes were saved.
+ */
+public abstract class AbstractForwardedSessionTest
+{
+   
+
+    public abstract AbstractTestServer createServer(int port);
+    
+    
+    @Test
+    public void testSessionCreateInForward() throws Exception
+    {
+        AbstractTestServer testServer = createServer(0);
+        ServletContextHandler testServletContextHandler = testServer.addContext("/context");
+        testServletContextHandler.addServlet(Servlet1.class, "/one");
+        testServletContextHandler.addServlet(Servlet2.class, "/two");
+        testServletContextHandler.addServlet(Servlet3.class, "/three");
+        testServletContextHandler.addServlet(Servlet4.class, "/four");
+       
+      
+
+        try
+        {
+            testServer.start();
+            int serverPort=testServer.getPort();
+            HttpClient client = new HttpClient();
+            client.start();
+            try
+            {
+                //make a request to the first servlet, which will forward it to other servlets
+                ContentResponse response = client.GET("http://localhost:" + serverPort + "/context/one");
+                assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
+                assertTrue(sessionCookie != null);
+                // Mangle the cookie, replacing Path with $Path, etc.
+                sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+
+                //test that the session was created, and that it contains the attributes from servlet3 and servlet1
+                
+                //stop the server, to make sure any session persistence has happened
+                testServer.stop();
+                            
+                //restart
+                testServer.start();
+                serverPort = testServer.getPort();
+       
+                //Make a fresh request
+                Request request = client.newRequest("http://localhost:" + serverPort + "/context/four");
+                request.header("Cookie", sessionCookie);
+                response = request.send();
+                assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+                
+            }
+            finally
+            {
+                client.stop();
+            }
+        }
+        finally
+        {
+            testServer.stop();
+        }
+        
+    }
+    
+
+    public static class Servlet1 extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {
+            //Don't create a session, just forward to another session in the same context
+            assertNull(request.getSession(false));
+            
+            //The session will be created by the other servlet, so will exist as this dispatch returns
+            RequestDispatcher dispatcher = request.getServletContext().getRequestDispatcher("/two");
+            dispatcher.forward(request, response);
+   
+            HttpSession sess = request.getSession(false);
+            assertNotNull(sess);
+            sess.setAttribute("servlet1", "servlet1");
+        }
+    }
+
+    public static class Servlet2 extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {
+            //forward to yet another servlet to do the creation
+            assertNull(request.getSession(false));
+
+            RequestDispatcher dispatcher = request.getServletContext().getRequestDispatcher("/three");
+            dispatcher.forward(request, response);
+            
+            //the session should exist after the forward
+            HttpSession sess = request.getSession(false);
+            assertNotNull(sess);
+        }
+    }
+
+
+
+    public static class Servlet3 extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {    
+            //No session yet
+            assertNull(request.getSession(false));
+            
+            //Create it
+            HttpSession session = request.getSession();
+            assertNotNull(session);
+            
+            //Set an attribute on it
+            session.setAttribute("servlet3", "servlet3");
+        }
+    }
+    
+    
+    public static class Servlet4 extends HttpServlet
+    {
+        @Override
+        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+        {    
+            //Check that the session contains attributes set during and after the session forward
+            HttpSession session = request.getSession();
+            assertNotNull(session);
+            assertNotNull(session.getAttribute("servlet1"));
+            assertNotNull(session.getAttribute("servlet3"));
+        }
+    }
+    
+}
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java
index 5bcad6c..b7b4ef8 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java
@@ -64,7 +64,7 @@
                 int value = 42;
                 ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=set&value=" + value);
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java
index 6360cca..6fcd01d 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java
@@ -79,23 +79,26 @@
                     ContentResponse response1 = client.GET(urls[0] + "?action=init");
 
                     assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
-                    String sessionCookie = response1.getHeaders().getStringField("Set-Cookie");
+                    String sessionCookie = response1.getHeaders().get("Set-Cookie");
                     assertTrue(sessionCookie != null);
                     // Mangle the cookie, replacing Path with $Path, etc.
                     sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
-
+                    
+                    
                     // Be sure the session is also present in node2
 
                     Request request2 = client.newRequest(urls[1] + "?action=increment");
                     request2.header("Cookie", sessionCookie);
                     ContentResponse response2 = request2.send();
                     assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
+ 
 
                     // Invalidate on node1
                     Request request1 = client.newRequest(urls[0] + "?action=invalidate");
                     request1.header("Cookie", sessionCookie);
                     response1 = request1.send();
                     assertEquals(HttpServletResponse.SC_OK, response1.getStatus());
+           
 
                     pause();
 
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java
index e394619..8d926b7 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java
@@ -87,7 +87,7 @@
                     ContentResponse response1 = client.GET("http://localhost:" + port1 + contextPath + servletMapping + "?action=init");
                     assertEquals(HttpServletResponse.SC_OK, response1.getStatus());
                     assertEquals("test", response1.getContentAsString());
-                    String sessionCookie = response1.getHeaders().getStringField("Set-Cookie");
+                    String sessionCookie = response1.getHeaders().get("Set-Cookie");
                     assertTrue( sessionCookie != null );
                     // Mangle the cookie, replacing Path with $Path, etc.
                     sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
@@ -106,7 +106,7 @@
                         assertEquals(HttpServletResponse.SC_OK , response2.getStatus());
                         assertEquals("test", response2.getContentAsString());
 
-                        String setCookie = response2.getHeaders().getStringField("Set-Cookie");
+                        String setCookie = response2.getHeaders().get("Set-Cookie");
                         if (setCookie!=null)
                             sessionCookie = setCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
 
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLightLoadTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLightLoadTest.java
deleted file mode 100644
index fa6184f..0000000
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLightLoadTest.java
+++ /dev/null
@@ -1,234 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      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 org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Random;
-import java.util.concurrent.CyclicBarrier;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Request;
-import org.junit.Test;
-
-
-/**
- * AbstractLightLoadTest
- */
-public abstract class AbstractLightLoadTest
-{
-    protected boolean _stress = Boolean.getBoolean( "STRESS" );
-
-    public abstract AbstractTestServer createServer(int port);
-
-    @Test
-    public void testLightLoad()
-        throws Exception
-    {
-        if ( _stress )
-        {
-            String contextPath = "";
-            String servletMapping = "/server";
-            AbstractTestServer server1 = createServer( 0 );
-            server1.addContext( contextPath ).addServlet( TestServlet.class, servletMapping );
-
-            try
-            {
-                server1.start();
-                int port1 = server1.getPort();
-                AbstractTestServer server2 = createServer( 0 );
-                server2.addContext( contextPath ).addServlet( TestServlet.class, servletMapping );
-
-                try
-                {
-                    server2.start();
-                    int port2=server2.getPort();
-                    HttpClient client = new HttpClient();
-                    client.start();
-                    try
-                    {
-                        String[] urls = new String[2];
-                        urls[0] = "http://localhost:" + port1 + contextPath + servletMapping;
-                        urls[1] = "http://localhost:" + port2 + contextPath + servletMapping;
-
-                        ContentResponse response1 = client.GET(urls[0] + "?action=init");
-                        assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
-                        String sessionCookie = response1.getHeaders().getStringField( "Set-Cookie" );
-                        assertTrue(sessionCookie != null);
-                        // Mangle the cookie, replacing Path with $Path, etc.
-                        sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
-
-                        ExecutorService executor = Executors.newCachedThreadPool();
-                        int clientsCount = 50;
-                        CyclicBarrier barrier = new CyclicBarrier( clientsCount + 1 );
-                        int requestsCount = 100;
-                        Worker[] workers = new Worker[clientsCount];
-                        for ( int i = 0; i < clientsCount; ++i )
-                        {
-                            workers[i] = new Worker( barrier, requestsCount, sessionCookie, urls );
-                            workers[i].start();
-                            executor.execute( workers[i] );
-                        }
-                        // Wait for all workers to be ready
-                        barrier.await();
-                        long start = System.nanoTime();
-
-                        // Wait for all workers to be done
-                        barrier.await();
-                        long end = System.nanoTime();
-                        long elapsed = TimeUnit.NANOSECONDS.toMillis( end - start );
-                        System.out.println( "elapsed ms: " + elapsed );
-
-                        for ( Worker worker : workers )
-                            worker.stop();
-                        executor.shutdownNow();
-
-                        // Perform one request to get the result
-                        Request request = client.newRequest( urls[0] + "?action=result" );
-                        request.header("Cookie", sessionCookie);
-                        ContentResponse response2 = request.send();
-                        assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
-                        String response = response2.getContentAsString();
-                        System.out.println( "get = " + response );
-                        assertEquals(response.trim(), String.valueOf( clientsCount * requestsCount ) );
-                    }
-                    finally
-                    {
-                        client.stop();
-                    }
-                }
-                finally
-                {
-                    server2.stop();
-                }
-            }
-            finally
-            {
-                server1.stop();
-            }
-        }
-    }
-
-    public static class Worker
-        implements Runnable
-    {
-        private final HttpClient client;
-
-        private final CyclicBarrier barrier;
-
-        private final int requestsCount;
-
-        private final String sessionCookie;
-
-        private final String[] urls;
-
-
-        public Worker( CyclicBarrier barrier, int requestsCount, String sessionCookie, String[] urls )
-        {
-            this.client = new HttpClient();
-            this.barrier = barrier;
-            this.requestsCount = requestsCount;
-            this.sessionCookie = sessionCookie;
-            this.urls = urls;
-        }
-
-        public void start()
-            throws Exception
-        {
-            client.start();
-        }
-
-        public void stop()
-            throws Exception
-        {
-            client.stop();
-        }
-
-        public void run()
-        {
-            try
-            {
-                // Wait for all workers to be ready
-                barrier.await();
-
-                Random random = new Random( System.nanoTime() );
-
-                for ( int i = 0; i < requestsCount; ++i )
-                {
-                    int urlIndex = random.nextInt( urls.length );
-                    Request request = client.newRequest(urls[urlIndex] + "?action=increment");
-                    request.header("Cookie", sessionCookie);
-                    ContentResponse response = request.send();
-                    assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                }
-
-                // Wait for all workers to be done
-                barrier.await();
-            }
-            catch ( Exception x )
-            {
-                throw new RuntimeException( x );
-            }
-        }
-    }
-
-    public static class TestServlet
-        extends HttpServlet
-    {
-        @Override
-        protected void doGet( HttpServletRequest request, HttpServletResponse response )
-            throws ServletException, IOException
-        {
-            String action = request.getParameter( "action" );
-            if ( "init".equals( action ) )
-            {
-                HttpSession session = request.getSession( true );
-                session.setAttribute( "value", 0 );
-            }
-            else if ( "increment".equals( action ) )
-            {
-                // Without synchronization, because it is taken care by Jetty/Terracotta
-                HttpSession session = request.getSession( false );
-                int value = (Integer) session.getAttribute( "value" );
-                session.setAttribute( "value", value + 1 );
-            }
-            else if ( "result".equals( action ) )
-            {
-                HttpSession session = request.getSession( false );
-                int value = (Integer) session.getAttribute( "value" );
-                PrintWriter writer = response.getWriter();
-                writer.println( value );
-                writer.flush();
-            }
-        }
-    }
-}
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLocalSessionScavengingTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLocalSessionScavengingTest.java
index 261b1b4..812561f 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLocalSessionScavengingTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLocalSessionScavengingTest.java
@@ -59,8 +59,8 @@
     {
         String contextPath = "";
         String servletMapping = "/server";
-        int inactivePeriod = 1;
-        int scavengePeriod = 2;
+        int inactivePeriod = 3;
+        int scavengePeriod = 1;
         AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod);
         server1.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
 
@@ -86,7 +86,7 @@
                     // Create the session on node1
                     ContentResponse response1 = client.GET(urls[0] + "?action=init");
                     assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
-                    String sessionCookie = response1.getHeaders().getStringField("Set-Cookie");
+                    String sessionCookie = response1.getHeaders().get("Set-Cookie");
                     assertTrue(sessionCookie != null);
                     // Mangle the cookie, replacing Path with $Path, etc.
                     sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
@@ -97,9 +97,8 @@
                     ContentResponse response2 = request.send();
                     assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
 
-
                     // Wait for the scavenger to run on node1, waiting 2.5 times the scavenger period
-                    pause(scavengePeriod);
+                    pause(inactivePeriod+scavengePeriod);
 
                     // Check that node1 does not have any local session cached
                     request = client.newRequest(urls[0] + "?action=check");
@@ -107,11 +106,10 @@
                     response1 = request.send();
                     assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
 
-
                     // Wait for the scavenger to run on node2, waiting 2 times the scavenger period
                     // This ensures that the scavenger on node2 runs at least once.
-                    pause(scavengePeriod);
-
+                    pause(inactivePeriod+(3*scavengePeriod));
+                    
                     // Check that node2 does not have any local session cached
                     request = client.newRequest(urls[1] + "?action=check");
                     request.header("Cookie", sessionCookie);
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractNewSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractNewSessionTest.java
index ba40565..980c2ca 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractNewSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractNewSessionTest.java
@@ -74,7 +74,7 @@
             {
                 ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractOrphanedSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractOrphanedSessionTest.java
index 7fbe359..90e7d08 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractOrphanedSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractOrphanedSessionTest.java
@@ -46,6 +46,7 @@
     /**
      * If nodeA creates a session, and just afterwards crashes, it is the only node that knows about the session.
      * We want to test that the session data is gone after scavenging.
+     * @throws Exception on test failure
      */
     @Test
     public void testOrphanedSession() throws Exception
@@ -74,7 +75,7 @@
                     // Connect to server1 to create a session and get its session cookie
                     ContentResponse response1 = client.GET("http://localhost:" + port1 + contextPath + servletMapping + "?action=init");
                     assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
-                    String sessionCookie = response1.getHeaders().getStringField("Set-Cookie");
+                    String sessionCookie = response1.getHeaders().get("Set-Cookie");
                     assertTrue(sessionCookie != null);
                     // Mangle the cookie, replacing Path with $Path, etc.
                     sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractProxySerializationTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractProxySerializationTest.java
index 495c3a8..8db9b9a 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractProxySerializationTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractProxySerializationTest.java
@@ -101,7 +101,7 @@
             {
                 ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java
index af4eb01..37b8c0a 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java
@@ -50,7 +50,7 @@
         String contextPath = "";
         String servletMapping = "/server";
         int scavengePeriod = 3;
-        AbstractTestServer server = createServer(0, 1, scavengePeriod);
+        AbstractTestServer server = createServer(0, 10, scavengePeriod);
         ServletContextHandler context = server.addContext(contextPath);
         context.addServlet(TestServlet.class, servletMapping);
         TestEventListener testListener = new TestEventListener();
@@ -66,7 +66,7 @@
             {
                 ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
@@ -82,8 +82,7 @@
                 assertTrue(testListener.isDestroyed());
 
 
-                // The session is not there anymore, but we present an old cookie
-                // The server creates a new session, we must ensure we released all locks
+                // The session is not there anymore, even if we present an old cookie
                 request = client.newRequest("http://localhost:" + port + contextPath + servletMapping + "?action=check");
                 request.header("Cookie", sessionCookie);
                 response = request.send();
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSameNodeLoadTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSameNodeLoadTest.java
new file mode 100644
index 0000000..88cd4bb
--- /dev/null
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSameNodeLoadTest.java
@@ -0,0 +1,231 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Random;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.junit.Test;
+
+
+/**
+ * AbstractSameNodeLoadTest
+ * 
+ * This test performs multiple concurrent requests for the same session on the same node.
+ * 
+ */
+public abstract class AbstractSameNodeLoadTest
+{
+    protected boolean _stress = Boolean.getBoolean( "STRESS" );
+
+    public abstract AbstractTestServer createServer(int port);
+
+    @Test
+    public void testLoad() throws Exception
+    {
+        if ( _stress )
+        {
+            String contextPath = "";
+            String servletMapping = "/server";
+            AbstractTestServer server1 = createServer( 0 );
+            server1.addContext( contextPath ).addServlet( TestServlet.class, servletMapping );
+
+            try
+            {
+                server1.start();
+                int port1 = server1.getPort();
+
+                HttpClient client = new HttpClient();
+                client.start();
+                try
+                {
+                    String url = "http://localhost:" + port1 + contextPath + servletMapping;
+
+
+                    //create session via first server
+                    ContentResponse response1 = client.GET(url + "?action=init");
+                    assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
+                    String sessionCookie = response1.getHeaders().getStringField( "Set-Cookie" );
+                    assertTrue(sessionCookie != null);
+                    // Mangle the cookie, replacing Path with $Path, etc.
+                    sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+
+                    //simulate 10 clients making 100 requests each
+                    ExecutorService executor = Executors.newCachedThreadPool();
+                    int clientsCount = 10;
+                    CyclicBarrier barrier = new CyclicBarrier( clientsCount + 1 );
+                    int requestsCount = 100;
+                    Worker[] workers = new Worker[clientsCount];
+                    for ( int i = 0; i < clientsCount; ++i )
+                    {
+                        workers[i] = new Worker(barrier, client, requestsCount, sessionCookie, url);
+                        executor.execute( workers[i] );
+                    }
+                    // Wait for all workers to be ready
+                    barrier.await();
+                    long start = System.nanoTime();
+
+                    // Wait for all workers to be done
+                    barrier.await();
+                    long end = System.nanoTime();
+                    long elapsed = TimeUnit.NANOSECONDS.toMillis( end - start );
+                    System.out.println( "elapsed ms: " + elapsed );
+
+                    executor.shutdownNow();
+
+                    // Perform one request to get the result
+                    Request request = client.newRequest( url + "?action=result" );
+                    request.header("Cookie", sessionCookie);
+                    ContentResponse response2 = request.send();
+                    assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
+                    String response = response2.getContentAsString();
+                    System.out.println( "get = " + response );
+                    assertEquals(response.trim(), String.valueOf( clientsCount * requestsCount ) );
+                }
+                finally
+                {
+                    client.stop();
+                }
+            }
+            finally
+            {
+                server1.stop();
+            }
+        }
+    }
+
+    public static class Worker implements Runnable
+    {
+        public static int COUNT = 0;
+        
+        private final HttpClient client;
+
+        private final CyclicBarrier barrier;
+
+        private final int requestsCount;
+
+        private final String sessionCookie;
+
+        private final String url;
+        
+        private final String name;
+
+
+        public Worker(CyclicBarrier barrier, HttpClient client, int requestsCount, String sessionCookie, String url)
+        {
+            this.client = client;
+            this.barrier = barrier;
+            this.requestsCount = requestsCount;
+            this.sessionCookie = sessionCookie;
+            this.url = url;
+            this.name = ""+(COUNT++);
+        }
+
+
+        public void run()
+        {
+            try
+            {
+                // Wait for all workers to be ready
+                barrier.await();
+
+                Random random = new Random( System.nanoTime() );
+
+                for ( int i = 0; i < requestsCount; ++i )
+                {
+                    int pauseMsec = random.nextInt(1000);
+
+                    //wait a random number of milliseconds between requests up to 1 second
+                    if (pauseMsec > 0)
+                    {
+                        Thread.currentThread().sleep(pauseMsec);
+                    }
+                    Request request = client.newRequest(url + "?action=increment");
+                    request.header("Cookie", sessionCookie);
+                    ContentResponse response = request.send();
+                    assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+                }
+
+                // Wait for all workers to be done
+                barrier.await();
+            }
+            catch ( Exception x )
+            {
+                throw new RuntimeException( x );
+            }
+        }
+    }
+
+    public static class TestServlet
+        extends HttpServlet
+    {
+        @Override
+        protected void doGet( HttpServletRequest request, HttpServletResponse response )
+            throws ServletException, IOException
+        {
+            String action = request.getParameter( "action" );
+            if ( "init".equals( action ) )
+            {
+                HttpSession session = request.getSession( true );
+                session.setAttribute( "value", 0 );
+            }
+            else if ( "increment".equals( action ) )
+            {
+                HttpSession session = request.getSession( false );
+                assertNotNull(session);
+                synchronized(session)
+                {
+                    int value = (Integer) session.getAttribute( "value" );
+                    session.setAttribute( "value", value + 1 );
+                }
+            }
+            else if ( "result".equals( action ) )
+            {
+                HttpSession session = request.getSession( false );
+                assertNotNull(session);
+                Integer value = null;
+                synchronized (session)
+                {
+                    value = (Integer) session.getAttribute( "value" );
+                }
+                PrintWriter writer = response.getWriter();
+                writer.println( value );
+                writer.flush();
+            }
+        }
+    }
+}
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractScatterGunLoadTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractScatterGunLoadTest.java
new file mode 100644
index 0000000..acbe6e0
--- /dev/null
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractScatterGunLoadTest.java
@@ -0,0 +1,242 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Random;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.junit.Test;
+
+
+/**
+ * AbstractScatterGunLoadTest
+ * 
+ * This is an unrealistic test. It takes a scatter-gun approach to smearing a
+ * single session across 2 different nodes at once. 
+ * 
+ * In the real world, we must have a load balancer that uses sticky sessions
+ * to keep the session pinned to a particular node.
+ */
+public abstract class AbstractScatterGunLoadTest
+{
+    protected boolean _stress = Boolean.getBoolean( "STRESS" );
+
+    public abstract AbstractTestServer createServer(int port);
+
+    @Test
+    public void testLightLoad()
+        throws Exception
+    {
+        if ( _stress )
+        {
+            String contextPath = "";
+            String servletMapping = "/server";
+            AbstractTestServer server1 = createServer( 0 );
+            server1.addContext( contextPath ).addServlet( TestServlet.class, servletMapping );
+
+            try
+            {
+                server1.start();
+                int port1 = server1.getPort();
+                AbstractTestServer server2 = createServer( 0 );
+                server2.addContext( contextPath ).addServlet( TestServlet.class, servletMapping );
+
+                try
+                {
+                    server2.start();
+                    int port2=server2.getPort();
+                    HttpClient client = new HttpClient();
+                    client.start();
+                    try
+                    {
+                        String[] urls = new String[2];
+                        urls[0] = "http://localhost:" + port1 + contextPath + servletMapping;
+                        urls[1] = "http://localhost:" + port2 + contextPath + servletMapping;
+
+                        //create session via first server
+                        ContentResponse response1 = client.GET(urls[0] + "?action=init");
+                        assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
+                        String sessionCookie = response1.getHeaders().get( "Set-Cookie" );
+                        assertTrue(sessionCookie != null);
+                        // Mangle the cookie, replacing Path with $Path, etc.
+                        sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+
+                        //simulate 50 clients making 100 requests each
+                        ExecutorService executor = Executors.newCachedThreadPool();
+                        int clientsCount = 50;
+                        CyclicBarrier barrier = new CyclicBarrier( clientsCount + 1 );
+                        int requestsCount = 100;
+                        Worker[] workers = new Worker[clientsCount];
+                        for ( int i = 0; i < clientsCount; ++i )
+                        {
+                            workers[i] = new Worker( barrier, requestsCount, sessionCookie, urls );
+                            workers[i].start();
+                            executor.execute( workers[i] );
+                        }
+                        // Wait for all workers to be ready
+                        barrier.await();
+                        long start = System.nanoTime();
+
+                        // Wait for all workers to be done
+                        barrier.await();
+                        long end = System.nanoTime();
+                        long elapsed = TimeUnit.NANOSECONDS.toMillis( end - start );
+                        System.out.println( "elapsed ms: " + elapsed );
+
+                        for ( Worker worker : workers )
+                            worker.stop();
+                        executor.shutdownNow();
+
+                        // Perform one request to get the result
+                        Request request = client.newRequest( urls[0] + "?action=result" );
+                        request.header("Cookie", sessionCookie);
+                        ContentResponse response2 = request.send();
+                        assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
+                        String response = response2.getContentAsString();
+                        System.out.println( "get = " + response );
+                        assertEquals(response.trim(), String.valueOf( clientsCount * requestsCount ) );
+                    }
+                    finally
+                    {
+                        client.stop();
+                    }
+                }
+                finally
+                {
+                    server2.stop();
+                }
+            }
+            finally
+            {
+                server1.stop();
+            }
+        }
+    }
+
+    public static class Worker
+        implements Runnable
+    {
+        private final HttpClient client;
+
+        private final CyclicBarrier barrier;
+
+        private final int requestsCount;
+
+        private final String sessionCookie;
+
+        private final String[] urls;
+
+
+        public Worker( CyclicBarrier barrier, int requestsCount, String sessionCookie, String[] urls )
+        {
+            this.client = new HttpClient();
+            this.barrier = barrier;
+            this.requestsCount = requestsCount;
+            this.sessionCookie = sessionCookie;
+            this.urls = urls;
+        }
+
+        public void start()
+            throws Exception
+        {
+            client.start();
+        }
+
+        public void stop()
+            throws Exception
+        {
+            client.stop();
+        }
+
+        public void run()
+        {
+            try
+            {
+                // Wait for all workers to be ready
+                barrier.await();
+
+                Random random = new Random( System.nanoTime() );
+
+                for ( int i = 0; i < requestsCount; ++i )
+                {
+                    int urlIndex = random.nextInt( urls.length );
+                    Request request = client.newRequest(urls[urlIndex] + "?action=increment");
+                    request.header("Cookie", sessionCookie);
+                    ContentResponse response = request.send();
+                    assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+                }
+
+                // Wait for all workers to be done
+                barrier.await();
+            }
+            catch ( Exception x )
+            {
+                throw new RuntimeException( x );
+            }
+        }
+    }
+
+    public static class TestServlet
+        extends HttpServlet
+    {
+        @Override
+        protected void doGet( HttpServletRequest request, HttpServletResponse response )
+            throws ServletException, IOException
+        {
+            String action = request.getParameter( "action" );
+            if ( "init".equals( action ) )
+            {
+                HttpSession session = request.getSession( true );
+                session.setAttribute( "value", 0 );
+            }
+            else if ( "increment".equals( action ) )
+            {
+                // Without synchronization
+                HttpSession session = request.getSession( false );
+                int value = (Integer) session.getAttribute( "value" );
+                session.setAttribute( "value", value + 1 );
+            }
+            else if ( "result".equals( action ) )
+            {
+                HttpSession session = request.getSession( false );
+                int value = (Integer) session.getAttribute( "value" );
+                PrintWriter writer = response.getWriter();
+                writer.println( value );
+                writer.flush();
+            }
+        }
+    }
+}
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java
index 3e306ba5..e9a961c 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java
@@ -39,7 +39,7 @@
 import org.junit.Test;
 
 /**
- * AbstractNewSessionTest
+ * AbstractSessionCookieTest
  */
 public abstract class AbstractSessionCookieTest
 {
@@ -81,7 +81,7 @@
                 ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
 
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 //sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java
index 2b79391..e3e15b8 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java
@@ -22,16 +22,21 @@
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
 
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.junit.Test;
 
@@ -50,13 +55,30 @@
             e.printStackTrace();
         }
     }
+    
+    public class TestHttpSessionListener implements HttpSessionListener
+    {
+        public List<String> createdSessions = new ArrayList<String>();
+        public List<String> destroyedSessions = new ArrayList<String>();
+        
+        public void sessionDestroyed(HttpSessionEvent se)
+        {
+            destroyedSessions.add(se.getSession().getId());
+        }
+        
+        public void sessionCreated(HttpSessionEvent se)
+        {
+            createdSessions.add(se.getSession().getId());
+        }
+    };
+    
 
     @Test
     public void testSessionNotExpired() throws Exception
     {
         String contextPath = "";
         String servletMapping = "/server";
-        int inactivePeriod = 10;
+        int inactivePeriod = 100;
         int scavengePeriod = 10;
         AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod);
         TestServlet servlet = new TestServlet();
@@ -75,7 +97,7 @@
             //make a request to set up a session on the server
             ContentResponse response = client.GET(url + "?action=init");
             assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-            String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+            String sessionCookie = response.getHeaders().get("Set-Cookie");
             assertTrue(sessionCookie != null);
             // Mangle the cookie, replacing Path with $Path, etc.
             sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
@@ -101,18 +123,26 @@
             server1.stop();
         }
     }
+    
 
     @Test
     public void testSessionExpiry() throws Exception
     {
+     
+        
         String contextPath = "";
         String servletMapping = "/server";
         int inactivePeriod = 2;
-        int scavengePeriod = 10;
+        int scavengePeriod = 1;
         AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod);
         TestServlet servlet = new TestServlet();
         ServletHolder holder = new ServletHolder(servlet);
-        server1.addContext(contextPath).addServlet(holder, servletMapping);
+        ServletContextHandler context = server1.addContext(contextPath);
+        context.addServlet(holder, servletMapping);
+        TestHttpSessionListener listener = new TestHttpSessionListener();
+        
+        context.getSessionHandler().addEventListener(listener);
+        
         server1.start();
         int port1 = server1.getPort();
 
@@ -125,11 +155,15 @@
             //make a request to set up a session on the server
             ContentResponse response1 = client.GET(url + "?action=init");
             assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
-            String sessionCookie = response1.getHeaders().getStringField("Set-Cookie");
+            String sessionCookie = response1.getHeaders().get("Set-Cookie");
             assertTrue(sessionCookie != null);
             // Mangle the cookie, replacing Path with $Path, etc.
             sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
-
+            
+            String sessionId = AbstractTestServer.extractSessionId(sessionCookie);     
+            
+            verifySessionCreated(listener,sessionId);
+            
             //now stop the server
             server1.stop();
 
@@ -138,6 +172,7 @@
 
             //restart the server
             server1.start();
+            
             port1 = server1.getPort();
             url = "http://localhost:" + port1 + contextPath + servletMapping;
 
@@ -146,12 +181,27 @@
             request.getHeaders().add("Cookie", sessionCookie);
             ContentResponse response2 = request.send();
             assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
+            
+            //and wait until the expiry time has passed
+            pause(inactivePeriod);
+            
+            verifySessionDestroyed (listener, sessionId);
         }
         finally
         {
             server1.stop();
-        }
+        }     
     }
+    public void verifySessionCreated (TestHttpSessionListener listener, String sessionId)
+    {
+        assertTrue(listener.createdSessions.contains(sessionId));
+    }
+    public void verifySessionDestroyed (TestHttpSessionListener listener, String sessionId)
+    {
+        assertTrue (listener.destroyedSessions.contains(sessionId));
+    }
+
+
 
     public static class TestServlet extends HttpServlet
     {
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java
index 3340ac5..4d2ad11 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.server.session;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -42,7 +43,6 @@
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
-import org.junit.Assert;
 import org.junit.Test;
 
 /**
@@ -94,8 +94,8 @@
     {
         String contextPath = "";
         String servletMapping = "/server";
-        int inactivePeriod = 1;
-        int scavengePeriod = 2;
+        int inactivePeriod = 4;
+        int scavengePeriod = 1;
         AbstractTestServer server = createServer(0, inactivePeriod, scavengePeriod);
         ServletContextHandler context = server.addContext(contextPath);
         TestServlet servlet = new TestServlet();
@@ -119,20 +119,19 @@
                 // Create the session
                 ContentResponse response1 = client.GET(url + "?action=init");
                 assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
-                String sessionCookie = response1.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response1.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
 
-
                 // Make a request which will invalidate the existing session and create a new one
                 Request request2 = client.newRequest(url + "?action=test");
                 request2.header("Cookie", sessionCookie);
                 ContentResponse response2 = request2.send();
                 assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
 
-                // Wait for the scavenger to run, waiting 3 times the scavenger period
-                pause(scavengePeriod);
+                // Wait for the scavenger to run and the session to have expired
+                pause(inactivePeriod + scavengePeriod);
 
                 //test that the session created in the last test is scavenged:
                 //the HttpSessionListener should have been called when session1 was invalidated and session2 was scavenged
@@ -187,6 +186,18 @@
                     //invalidate existing session
                     session.invalidate();
 
+                    //now try to access the invalid session
+                    try
+                    {
+                        session.getAttribute("identity");
+                        fail("Session should be invalid");
+                    }
+                    catch (IllegalStateException e)
+                    {
+                        assertNotNull(e.getMessage());
+                        assertTrue(e.getMessage().contains("id"));
+                    }
+
                     //now make a new session
                     session = request.getSession(true);
                     session.setAttribute("identity", "session2");
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionMigrationTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionMigrationTest.java
index 3648045..b80cc29 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionMigrationTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionMigrationTest.java
@@ -72,7 +72,7 @@
                     Request request1 = client.POST("http://localhost:" + port1 + contextPath + servletMapping + "?action=set&value=" + value);
                     ContentResponse response1 = request1.send();
                     assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
-                    String sessionCookie = response1.getHeaders().getStringField("Set-Cookie");
+                    String sessionCookie = response1.getHeaders().get("Set-Cookie");
                     assertTrue(sessionCookie != null);
                     // Mangle the cookie, replacing Path with $Path, etc.
                     sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java
index fb237b6..e3cc08d 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java
@@ -70,7 +70,7 @@
             ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
             assertEquals(HttpServletResponse.SC_OK,response.getStatus());
 
-            String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+            String sessionCookie = response.getHeaders().get("Set-Cookie");
             assertTrue(sessionCookie != null);
             assertFalse(testListener.isCalled());
 
@@ -79,7 +79,7 @@
             request.header("Cookie", sessionCookie);
             ContentResponse renewResponse = request.send();
             assertEquals(HttpServletResponse.SC_OK,renewResponse.getStatus());
-            String renewSessionCookie = renewResponse.getHeaders().getStringField("Set-Cookie");
+            String renewSessionCookie = renewResponse.getHeaders().get("Set-Cookie");
             assertNotNull(renewSessionCookie);
             assertNotSame(sessionCookie, renewSessionCookie);
             assertTrue(testListener.isCalled());
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java
index d29dccb..164ee34 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java
@@ -37,7 +37,7 @@
 
 
 /**
- * AbstractLastAccessTimeTest
+ * AbstractSessionValueSavingTest
  */
 public abstract class AbstractSessionValueSavingTest
 {
@@ -72,7 +72,7 @@
 
                     sessionTestValue = Long.parseLong(response1.getContentAsString());
 
-                    String sessionCookie = response1.getHeaders().getStringField("Set-Cookie");
+                    String sessionCookie = response1.getHeaders().get("Set-Cookie");
                     assertTrue( sessionCookie != null );
                     // Mangle the cookie, replacing Path with $Path, etc.
                     sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
@@ -95,7 +95,7 @@
                         assertTrue(sessionTestValue < Long.parseLong(response2.getContentAsString()));
                         sessionTestValue = Long.parseLong(response2.getContentAsString());
 
-                        String setCookie = response1.getHeaders().getStringField("Set-Cookie");
+                        String setCookie = response1.getHeaders().get("Set-Cookie");
                         if (setCookie!=null)
                             sessionCookie = setCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
 
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractStopSessionManagerPreserveSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractStopSessionManagerPreserveSessionTest.java
index 9ca7291..7ef8ab8 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractStopSessionManagerPreserveSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractStopSessionManagerPreserveSessionTest.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.server.session;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
@@ -36,6 +35,11 @@
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.junit.Test;
 
+/**
+ * AbstractStopSessionManagerPreserveSessionTest
+ *
+ *
+ */
 public abstract class AbstractStopSessionManagerPreserveSessionTest
 {
     public String _id;
@@ -74,7 +78,7 @@
                 //Create a session
                 ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                String sessionCookie = response.getHeaders().get("Set-Cookie");
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java
index 205916f..91387fb 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java
@@ -34,17 +34,38 @@
  */
 public abstract class AbstractTestServer
 {
+    public static int DEFAULT_MAX_INACTIVE = 30;
+    public static int DEFAULT_SCAVENGE = 10;
+    
     protected final Server _server;
     protected final int _maxInactivePeriod;
     protected final int _scavengePeriod;
     protected final ContextHandlerCollection _contexts;
     protected SessionIdManager _sessionIdManager;
 
+  
+    
+    public static String extractSessionId (String sessionCookie)
+    {
+        if (sessionCookie == null)
+            return null;
+        sessionCookie = sessionCookie.trim();
+        int i = sessionCookie.indexOf(';');
+        if (i >= 0)
+            sessionCookie = sessionCookie.substring(0,i);
+        if (sessionCookie.startsWith("JSESSIONID"))
+            sessionCookie = sessionCookie.substring("JSESSIONID=".length());
+        i = sessionCookie.indexOf('.');
+        if (i >=0)
+            sessionCookie = sessionCookie.substring(0,i);
+        return sessionCookie;
+    }
+
     
     
     public AbstractTestServer(int port)
     {
-        this(port, 30, 10);
+        this(port, DEFAULT_MAX_INACTIVE, DEFAULT_SCAVENGE);
     }
 
     public AbstractTestServer(int port, int maxInactivePeriod, int scavengePeriod)
@@ -52,7 +73,7 @@
         this (port, maxInactivePeriod, scavengePeriod, null);
     }
     
-    public AbstractTestServer(int port, int maxInactivePeriod, int scavengePeriod, String sessionIdMgrConfig)
+    public AbstractTestServer(int port, int maxInactivePeriod, int scavengePeriod, Object sessionIdMgrConfig)
     {
         _server = new Server(port);
         _maxInactivePeriod = maxInactivePeriod;
@@ -64,7 +85,7 @@
     
     
 
-    public abstract SessionIdManager newSessionIdManager(String config);
+    public abstract SessionIdManager newSessionIdManager(Object config);
     public abstract SessionManager newSessionManager();
     public abstract SessionHandler newSessionHandler(SessionManager sessionManager);
 
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractWebAppObjectInSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractWebAppObjectInSessionTest.java
index 47b07af..60eb7be 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractWebAppObjectInSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractWebAppObjectInSessionTest.java
@@ -120,7 +120,7 @@
 
                     ContentResponse response = request.send();
                     assertEquals( HttpServletResponse.SC_OK, response.getStatus());
-                    String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
+                    String sessionCookie = response.getHeaders().get("Set-Cookie");
                     assertTrue(sessionCookie != null);
                     // Mangle the cookie, replacing Path with $Path, etc.
                     sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
diff --git a/tests/test-webapps/pom.xml b/tests/test-webapps/pom.xml
index b2a802e..9a7e163 100644
--- a/tests/test-webapps/pom.xml
+++ b/tests/test-webapps/pom.xml
@@ -1,27 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <artifactId>test-webapps-parent</artifactId>
@@ -37,7 +20,7 @@
           <!-- DO NOT DEPLOY (or Release) -->
           <skip>true</skip>
         </configuration>
-      </plugin>    
+      </plugin>
       <plugin>
         <groupId>org.jacoco</groupId>
         <artifactId>jacoco-maven-plugin</artifactId>
diff --git a/tests/test-webapps/test-dispatch-webapp/pom.xml b/tests/test-webapps/test-dispatch-webapp/pom.xml
index 4a1f2b6..5afe024 100644
--- a/tests/test-webapps/test-dispatch-webapp/pom.xml
+++ b/tests/test-webapps/test-dispatch-webapp/pom.xml
@@ -4,11 +4,14 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.2.2-SNAPSHOT</version>
+    <version>9.3.0-SNAPSHOT</version>
   </parent>
   <name>Jetty Tests :: Webapps :: Dispatch Webapp</name>
   <artifactId>test-dispatch-webapp</artifactId>
   <packaging>war</packaging>
+   <properties>
+    <bundle-symbolic-name>${project.groupId}.dispatch</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
diff --git a/tests/test-webapps/test-jaas-webapp/pom.xml b/tests/test-webapps/test-jaas-webapp/pom.xml
index 9d47988..ac45de0 100644
--- a/tests/test-webapps/test-jaas-webapp/pom.xml
+++ b/tests/test-webapps/test-jaas-webapp/pom.xml
@@ -4,11 +4,14 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <artifactId>test-jaas-webapp</artifactId>
   <name>Jetty Tests :: WebApp :: JAAS</name>
   <packaging>war</packaging>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.jaas</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
@@ -28,8 +31,8 @@
           <systemProperties>
               <!-- This is for convenience so that the src/etc/login.conf file can stay unmodified when copied to $jetty.home/etc directory -->
             <systemProperty>
-              <name>jetty.home</name>
-              <value>${basedir}/src/main/config</value>
+              <name>jetty.base</name>
+              <value>${basedir}/src/main/config/demo-base</value>
             </systemProperty>
               <!-- Mandatory. This system property tells JAAS where to find the login module configuration file -->
             <systemProperty>
@@ -47,6 +50,13 @@
             </securityHandler>
           </webAppConfig>
         </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>5.1.19</version>
+          </dependency>
+        </dependencies>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
diff --git a/tests/test-webapps/test-jaas-webapp/src/main/config/demo-base/webapps/test-jaas.xml b/tests/test-webapps/test-jaas-webapp/src/main/config/demo-base/webapps/test-jaas.xml
index f3b0a18..c56276b 100644
--- a/tests/test-webapps/test-jaas-webapp/src/main/config/demo-base/webapps/test-jaas.xml
+++ b/tests/test-webapps/test-jaas-webapp/src/main/config/demo-base/webapps/test-jaas.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Configure the test-jndi webapp                                  -->
diff --git a/tests/test-webapps/test-jaas-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/tests/test-webapps/test-jaas-webapp/src/main/webapp/WEB-INF/jetty-web.xml
index e7830c6..ecd5e30 100644
--- a/tests/test-webapps/test-jaas-webapp/src/main/webapp/WEB-INF/jetty-web.xml
+++ b/tests/test-webapps/test-jaas-webapp/src/main/webapp/WEB-INF/jetty-web.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
   <Get class="org.eclipse.jetty.util.log.Log" name="rootLogger">
diff --git a/tests/test-webapps/test-jaas-webapp/src/main/webapp/auth.html b/tests/test-webapps/test-jaas-webapp/src/main/webapp/auth.html
index 249f958..ab12241 100644
--- a/tests/test-webapps/test-jaas-webapp/src/main/webapp/auth.html
+++ b/tests/test-webapps/test-jaas-webapp/src/main/webapp/auth.html
@@ -1,7 +1,7 @@
 <HTML>
   <HEAD>
     <META http-equiv="Pragma" content="no-cache">
-    <META http-equiv="Cache-Control" content="no-cache,no-store">
+    <META http-equiv="Cache-Control" content="no-cache,no-store,must-revalidate">
     <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
   </HEAD>
 
diff --git a/tests/test-webapps/test-jetty-webapp/pom.xml b/tests/test-webapps/test-jetty-webapp/pom.xml
index c725c8c..fd731f7 100644
--- a/tests/test-webapps/test-jetty-webapp/pom.xml
+++ b/tests/test-webapps/test-jetty-webapp/pom.xml
@@ -1,26 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
@@ -29,6 +12,9 @@
   <name>Test :: Jetty Test Webapp</name>
   <url>http://www.eclipse.org/jetty</url>
   <packaging>war</packaging>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.tests.webapp</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
@@ -70,15 +56,6 @@
           </execution>
         </executions>
       </plugin>
-      <!-- also make this webapp an osgi bundle -->
-      <plugin>
-        <artifactId>maven-war-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
       <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
@@ -87,32 +64,29 @@
           <supportedProjectTypes>
             <supportedProjectType>war</supportedProjectType>
           </supportedProjectTypes>
+          <instructions>
+            <Import-Package>javax.servlet.jsp.*;version="[2.2.0,3.0)",org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",*</Import-Package>
+            <Export-Package>!com.acme*</Export-Package>
+            <!-- the test webapp is configured via a jetty xml file
+            in order to add the security handler. -->
+            <Web-ContextPath>/</Web-ContextPath>
+            <!-- in fact the '.' must not be there
+            but Felix-BND has a bug:
+            http://www.mail-archive.com/users@felix.apache.org/msg04730.html
+            https://issues.apache.org/jira/browse/FELIX-1571
+            -->
+            <Bundle-ClassPath>.,WEB-INF/classes</Bundle-ClassPath>
+          </instructions>
         </configuration>
-        <executions>
-          <execution>
-            <id>bundle-manifest</id>
-            <phase>process-classes</phase>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Bundle-SymbolicName>org.eclipse.jetty.test-jetty-webapp</Bundle-SymbolicName>
-                <Import-Package>javax.servlet.jsp.*;version="[2.2.0, 3.0)",javax.servlet.*;version="[2.6,3.2)",org.eclipse.jetty.*;version="9.1",*</Import-Package>
-                <Export-Package>!com.acme*</Export-Package>
-                <!-- the test webapp is configured via a jetty xml file
-                in order to add the security handler. -->
-                <Web-ContextPath>/</Web-ContextPath>
-                <!-- in fact the '.' must not be there
-                but Felix-BND has a bug:
-                http://www.mail-archive.com/users@felix.apache.org/msg04730.html
-                https://issues.apache.org/jira/browse/FELIX-1571
-                -->
-                <Bundle-ClassPath>.,WEB-INF/classes</Bundle-ClassPath>
-              </instructions>
-            </configuration>
-           </execution>
-        </executions>
+      </plugin>
+      <!-- also make this webapp an osgi bundle -->
+      <plugin>
+        <artifactId>maven-war-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+          </archive>
+        </configuration>
       </plugin>
       <plugin>
         <groupId>org.eclipse.jetty</groupId>
@@ -157,6 +131,14 @@
           </loginServices>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <!-- must deploy: required for jetty-distribution -->
+          <skip>false</skip>
+        </configuration>
+      </plugin>
     </plugins>
   </build>
   <dependencies>
@@ -199,6 +181,13 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlet</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.eclipse.jetty.toolchain</groupId>
       <artifactId>jetty-test-helper</artifactId>
       <scope>test</scope>
@@ -216,12 +205,6 @@
       <scope>provided</scope>
     </dependency>
     <dependency>
-      <groupId>org.eclipse.jetty.spdy</groupId>
-      <artifactId>spdy-http-server</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>jstl</artifactId>
       <version>1.2</version>
@@ -249,10 +232,14 @@
               <goals>
                 <goal>jspc</goal>
               </goals>
+             <!-- example configuration
               <configuration>
-                 <includes>**/*.foo</includes>
-                 <excludes>**/*.fff</excludes>
+                <includes>**/*.foo</includes>
+                <excludes>**/*.fff</excludes>
+                <sourceVersion>1.8</sourceVersion>
+                <targetVersion>1.8</targetVersion>
               </configuration>
+              -->
             </execution>
           </executions>
         </plugin>
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/assembly/embedded-jetty-web-for-webbundle.xml b/tests/test-webapps/test-jetty-webapp/src/main/assembly/embedded-jetty-web-for-webbundle.xml
index 5d223e3..4e0e63c 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/assembly/embedded-jetty-web-for-webbundle.xml
+++ b/tests/test-webapps/test-jetty-webapp/src/main/assembly/embedded-jetty-web-for-webbundle.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- ==================================================================
 Configure and deploy the test web application in $(jetty.home)/webapps/test
@@ -13,7 +13,6 @@
 
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
 
-
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
   <!-- Required minimal context configuration :                        -->
   <!--  + contextPath                                                  -->
@@ -52,7 +51,9 @@
     <Set name="loginService">
       <New class="org.eclipse.jetty.security.HashLoginService">
 	    <Set name="name">Test Realm</Set>
-	    <Set name="config"><Property name="this.web-inf.url"/>realm.properties</Set>
+	    <Set name="config">
+	       <Property name="web-inf.uri">/realm.properties</Property>      
+	    </Set>
             <!-- To enable reload of realm when properties change, uncomment the following lines -->
             <!-- changing refreshInterval (in seconds) as desired                                -->
             <!--
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/assembly/web-bundle.xml b/tests/test-webapps/test-jetty-webapp/src/main/assembly/web-bundle.xml
index c220f51..92951ef 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/assembly/web-bundle.xml
+++ b/tests/test-webapps/test-jetty-webapp/src/main/assembly/web-bundle.xml
@@ -20,6 +20,8 @@
       </excludes>
     </fileSet>
   </fileSets>
+<!-- Removed until PropertyUserStore supports packed realm.properties -->
+<!--
   <files>
     <file>
       <source>src/main/assembly/embedded-jetty-web-for-webbundle.xml</source>
@@ -32,4 +34,5 @@
       <destName>realm.properties</destName>
     </file>
   </files>
+-->
 </assembly>
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/demo-rewrite-rules.xml b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/demo-rewrite-rules.xml
index 35a8f87..df2c608 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/demo-rewrite-rules.xml
+++ b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/demo-rewrite-rules.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Configure the demos                                             -->
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/test-realm.xml b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/test-realm.xml
index d5c776b..f1b342b 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/test-realm.xml
+++ b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/test-realm.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
     <!-- =========================================================== -->
     <!-- Configure Authentication Login Service                      -->
@@ -12,7 +12,7 @@
       <Arg>
         <New class="org.eclipse.jetty.security.HashLoginService">
           <Set name="name">Test Realm</Set>
-          <Set name="config"><Property name="demo.realm" default="etc/realm.properties"/></Set>
+          <Set name="config"><Property name="jetty.demo.realm" default="etc/realm.properties"/></Set>
           <Set name="refreshInterval">0</Set>
         </New>
       </Arg>
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/start.ini b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/start.ini
index 9a840f4..5921b61 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/start.ini
+++ b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/start.ini
@@ -6,7 +6,7 @@
 
 # Enable security via jaas, and configure it
 --module=jaas
-jaas.login.conf=etc/login.conf
+jetty.jaas.login.conf=etc/login.conf
 
 # Enable rewrite examples
 --module=rewrite
@@ -19,5 +19,5 @@
 
 # Create and configure the test realm
 etc/test-realm.xml
-demo.realm=etc/realm.properties
+jetty.demo.realm=etc/realm.properties
 
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml
index 43047f9..c8545a4 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml
+++ b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- ==================================================================
 Configure and deploy the test web application in $(jetty.home)/webapps/test
@@ -11,7 +11,7 @@
 detected.
 ===================================================================== -->
 
-<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+<Configure id="testWebapp" class="org.eclipse.jetty.webapp.WebAppContext">
 
   <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
   <!-- Required minimal context configuration :                        -->
@@ -35,6 +35,21 @@
     <Arg type="Boolean">true</Arg>
   </Call>
   
+  <Set name="gzipHandler">
+    <New class="org.eclipse.jetty.server.handler.gzip.GzipHandler">
+      <Set name="minGzipSize">2048</Set>      
+    </New>
+  </Set>
+  
+  <!-- Set Caching Classloader that improves performance on resource searching webapps -->
+  <!--
+  <Set name="classLoader">
+    <New class="org.eclipse.jetty.webapp.CachingWebAppClassLoader">
+      <Arg><Ref refid="testWebapp"/></Arg>
+    </New>
+  </Set>
+  -->
+  
   <!-- Enable symlinks 
   <Call name="addAliasCheck">
     <Arg><New class="org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker"/></Arg>
@@ -95,18 +110,20 @@
   -->
 
   <!-- Add context specific logger
-  <Set name="handler">
-    <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler">
-      <Set name="requestLog">
-	<New id="RequestLogImpl" class="org.eclipse.jetty.server.NCSARequestLog">
-	  <Set name="filename"><Property name="jetty.logs" default="./logs"/>/test-yyyy_mm_dd.request.log</Set>
-	  <Set name="filenameDateFormat">yyyy_MM_dd</Set>
-	  <Set name="append">true</Set>
-	  <Set name="LogTimeZone">GMT</Set>
-	</New>
-      </Set>
-    </New>
-  </Set>
+  <Call name="insertHandler">
+    <Arg>
+      <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler">
+        <Set name="requestLog">
+          <New id="RequestLogImpl" class="org.eclipse.jetty.server.NCSARequestLog">
+            <Set name="filename"><Property name="jetty.logs" default="./logs"/>/test-yyyy_mm_dd.request.log</Set>
+            <Set name="filenameDateFormat">yyyy_MM_dd</Set>
+            <Set name="append">true</Set>
+            <Set name="LogTimeZone">GMT</Set>
+          </New>
+        </Set>
+      </New>
+    </Arg>
+  </Call>
   -->
 
 </Configure>
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/CookieDump.java b/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/CookieDump.java
index 5b65ca5..ca778d4 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/CookieDump.java
+++ b/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/CookieDump.java
@@ -22,6 +22,7 @@
 import java.io.PrintWriter;
 import java.util.concurrent.TimeUnit;
 
+import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletException;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServlet;
@@ -105,7 +106,7 @@
     /* ------------------------------------------------------------ */
     private String getURI(HttpServletRequest request)
     {
-        String uri=(String)request.getAttribute("javax.servlet.forward.request_uri");
+        String uri=(String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
         if (uri==null)
             uri=request.getRequestURI();
         return uri;
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/DispatchServlet.java b/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/DispatchServlet.java
index 69e1100..f163327 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/DispatchServlet.java
+++ b/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/DispatchServlet.java
@@ -68,8 +68,8 @@
 
         String info;
 
-        if (sreq.getAttribute("javax.servlet.include.servlet_path") != null)
-            info= (String)sreq.getAttribute("javax.servlet.include.path_info");
+        if (sreq.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH) != null)
+            info= (String)sreq.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
         else
             info= sreq.getPathInfo();
 
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/Dump.java b/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/Dump.java
index d815d04..3c21b6f 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/Dump.java
+++ b/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/Dump.java
@@ -39,6 +39,7 @@
 import javax.servlet.AsyncContext;
 import javax.servlet.AsyncEvent;
 import javax.servlet.AsyncListener;
+import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
@@ -118,7 +119,7 @@
             }
             catch(ServletException se)
             {
-                se.printStackTrace();
+                getServletContext().log(se.toString());
             }
         }
 
@@ -329,7 +330,7 @@
 
         // handle an error
         String error= request.getParameter("error");
-        if (error != null && error.length() > 0 && request.getAttribute("javax.servlet.error.status_code")==null)
+        if (error != null && error.length() > 0 && request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)==null)
         {
             response.getOutputStream().println("THIS SHOULD NOT BE SEEN!");
             response.sendError(Integer.parseInt(error));
@@ -874,7 +875,7 @@
     /* ------------------------------------------------------------ */
     private String getURI(HttpServletRequest request)
     {
-        String uri= (String)request.getAttribute("javax.servlet.forward.request_uri");
+        String uri= (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
         if (uri == null)
             uri= request.getRequestURI();
         return uri;
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/SessionDump.java b/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/SessionDump.java
index e9ef6ff..6f72dc1 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/SessionDump.java
+++ b/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/SessionDump.java
@@ -23,6 +23,7 @@
 import java.util.Date;
 import java.util.Enumeration;
 
+import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
@@ -36,6 +37,23 @@
 @SuppressWarnings("serial")
 public class SessionDump extends HttpServlet
 {
+    /** 
+      * Simple object attribute to test serialization
+      */
+    public class ObjectAttributeValue implements java.io.Serializable
+    {
+        long l;
+        
+        public ObjectAttributeValue(long l)
+        {
+            this.l = l;
+        }
+
+        public long getValue()
+        {
+            return l;
+        }
+    }
 
     int redirectCount=0;
     /* ------------------------------------------------------------ */
@@ -64,6 +82,7 @@
             {
                 session = request.getSession(true);
                 session.setAttribute("test","value");
+                session.setAttribute("obj", new ObjectAttributeValue(System.currentTimeMillis()));
             }
             else if (session!=null)
             {
@@ -175,7 +194,7 @@
     /* ------------------------------------------------------------ */
     private String getURI(HttpServletRequest request)
     {
-        String uri=(String)request.getAttribute("javax.servlet.forward.request_uri");
+        String uri=(String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
         if (uri==null)
             uri=request.getRequestURI();
         return uri;
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml
index 19fb028..c04f74c 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml
+++ b/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!--
 This is the jetty specific web application configuration file.  When starting
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml b/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml
index a2b3763..bf3f330 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml
+++ b/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml
@@ -10,7 +10,7 @@
   
   <context-param>
     <param-name>org.eclipse.jetty.server.context.ManagedAttributes</param-name>
-    <param-value>QoSFilter,TransparentProxy.ThreadPool,TransparentProxy.HttpClient</param-value>
+    <param-value>PushFilter,QoSFilter,TransparentProxy.ThreadPool,TransparentProxy.HttpClient</param-value>
   </context-param>
   
   <!-- Declare TestListener, which declares TestFilter -->
@@ -18,6 +18,15 @@
     <listener-class>com.acme.TestListener</listener-class>
   </listener>
 
+  <filter>
+    <filter-name>PushFilter</filter-name>
+    <filter-class>org.eclipse.jetty.servlets.PushCacheFilter</filter-class>
+    <async-supported>true</async-supported>
+  </filter>  
+  <filter-mapping>
+    <filter-name>PushFilter</filter-name>
+    <url-pattern>/*</url-pattern>
+  </filter-mapping>
 
   <filter>
     <filter-name>QoSFilter</filter-name>
@@ -52,46 +61,6 @@
     <url-pattern>/dump/*</url-pattern>
   </filter-mapping>
   
-
-  <filter>
-    <filter-name>GzipFilter</filter-name>
-    <filter-class>org.eclipse.jetty.servlets.AsyncGzipFilter</filter-class>
-    <async-supported>true</async-supported>
-    <init-param>
-      <param-name>bufferSize</param-name>
-      <param-value>8192</param-value>
-    </init-param>
-    <init-param>
-      <param-name>mimeTypes</param-name>
-      <param-value>text/plain,application/xml,text/html</param-value>
-    </init-param>
-    <init-param>
-      <param-name>minGzipSize</param-name>
-      <param-value>2048</param-value>
-    </init-param>
-    <init-param>
-      <param-name>userAgent</param-name>
-      <param-value>(?:Mozilla[^\(]*\(compatible;\s*+([^;]*);.*)|(?:.*?([^\s]+/[^\s]+).*)</param-value>
-    </init-param>
-    <init-param>
-      <param-name>cacheSize</param-name>
-      <param-value>1024</param-value>
-    </init-param>
-    <init-param>
-      <param-name>excludedAgents</param-name>
-      <param-value>MSIE 6.0</param-value>
-    </init-param>
-    <init-param>
-      <param-name>uncheckedPrintWriter</param-name>
-      <param-value>true</param-value>
-    </init-param>
-  </filter>
-  <filter-mapping>
-    <filter-name>GzipFilter</filter-name>
-    <url-pattern>/dump/gzip/*</url-pattern>
-    <url-pattern>*.txt</url-pattern>
-  </filter-mapping>
-  
   <servlet>
     <servlet-name>Login</servlet-name>
     <servlet-class>com.acme.LoginServlet</servlet-class>
diff --git a/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java b/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java
index e87a700..85b7f01 100644
--- a/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java
+++ b/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java
@@ -93,43 +93,7 @@
         httpConnector.setIdleTimeout(30000);
         server.addConnector(httpConnector);
 
-        /*
-        // SSL configurations
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.setKeyStorePath(jetty_root + "/jetty-server/src/main/config/etc/keystore");
-        sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
-        sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
-        sslContextFactory.setTrustStorePath(jetty_root + "/jetty-server/src/main/config/etc/keystore");
-        sslContextFactory.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
-        sslContextFactory.setExcludeCipherSuites(
-                "SSL_RSA_WITH_DES_CBC_SHA",
-                "SSL_DHE_RSA_WITH_DES_CBC_SHA",
-                "SSL_DHE_DSS_WITH_DES_CBC_SHA",
-                "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
-                "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
-                "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
-                "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
-        
-        
-        // Spdy Connector
-        SPDYServerConnectionFactory.checkNPNAvailable();
-        PushStrategy push = new ReferrerPushStrategy();
-        HTTPSPDYServerConnectionFactory spdy2 = new HTTPSPDYServerConnectionFactory(2,config,push);
-        spdy2.setInputBufferSize(8192);
-        spdy2.setInitialWindowSize(32768);
-        HTTPSPDYServerConnectionFactory spdy3 = new HTTPSPDYServerConnectionFactory(3,config,push);
-        spdy2.setInputBufferSize(8192);
-        NPNServerConnectionFactory npn = new NPNServerConnectionFactory(spdy3.getProtocol(),spdy2.getProtocol(),http.getProtocol());
-        npn.setDefaultProtocol(http.getProtocol());
-        npn.setInputBufferSize(1024); 
-        SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory,npn.getProtocol()); 
-        ServerConnector spdyConnector = new ServerConnector(server,ssl,npn,spdy3,spdy2,http);
-        spdyConnector.setPort(8443);
-        spdyConnector.setIdleTimeout(15000);
-        server.addConnector(spdyConnector);
-        
-        */
-        
+
         // Handlers
         HandlerCollection handlers = new HandlerCollection();
         ContextHandlerCollection contexts = new ContextHandlerCollection();
@@ -158,6 +122,7 @@
         server.setStopAtShutdown(true);
 
         WebAppContext webapp = new WebAppContext();
+        webapp.setContextPath("/test");
         webapp.setParentLoaderPriority(true);
         webapp.setResourceBase("./src/main/webapp");
         webapp.setAttribute("testAttribute","testValue");
diff --git a/tests/test-webapps/test-jndi-webapp/pom.xml b/tests/test-webapps/test-jndi-webapp/pom.xml
index 7684579..a2d964d 100644
--- a/tests/test-webapps/test-jndi-webapp/pom.xml
+++ b/tests/test-webapps/test-jndi-webapp/pom.xml
@@ -4,11 +4,14 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <artifactId>test-jndi-webapp</artifactId>
   <name>Jetty Tests :: WebApp :: JNDI</name>
   <packaging>war</packaging>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.jndi</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
@@ -78,7 +81,6 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-assembly-plugin</artifactId>
-        <version>2.2-beta-3</version>
         <executions>
           <execution>
             <phase>package</phase>
diff --git a/tests/test-webapps/test-jndi-webapp/src/main/templates/jetty-test-jndi-header.xml b/tests/test-webapps/test-jndi-webapp/src/main/templates/jetty-test-jndi-header.xml
index 4f4ded0..484551a 100644
--- a/tests/test-webapps/test-jndi-webapp/src/main/templates/jetty-test-jndi-header.xml
+++ b/tests/test-webapps/test-jndi-webapp/src/main/templates/jetty-test-jndi-header.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Configure the test-jndi webapp                                  -->
diff --git a/tests/test-webapps/test-jndi-webapp/src/main/templates/plugin-context-header.xml b/tests/test-webapps/test-jndi-webapp/src/main/templates/plugin-context-header.xml
index 3e877a5..bd97eb1 100644
--- a/tests/test-webapps/test-jndi-webapp/src/main/templates/plugin-context-header.xml
+++ b/tests/test-webapps/test-jndi-webapp/src/main/templates/plugin-context-header.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <!-- =============================================================== -->
 <!-- Configure the test-jndi webapp                                  -->
diff --git a/tests/test-webapps/test-jndi-webapp/src/main/webapp/WEB-INF/jetty-env.xml b/tests/test-webapps/test-jndi-webapp/src/main/webapp/WEB-INF/jetty-env.xml
index b7fa49f..7899c45 100644
--- a/tests/test-webapps/test-jndi-webapp/src/main/webapp/WEB-INF/jetty-env.xml
+++ b/tests/test-webapps/test-jndi-webapp/src/main/webapp/WEB-INF/jetty-env.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
 
diff --git a/tests/test-webapps/test-jndi-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/tests/test-webapps/test-jndi-webapp/src/main/webapp/WEB-INF/jetty-web.xml
index fd4831e..203f811 100644
--- a/tests/test-webapps/test-jndi-webapp/src/main/webapp/WEB-INF/jetty-web.xml
+++ b/tests/test-webapps/test-jndi-webapp/src/main/webapp/WEB-INF/jetty-web.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
   <Get class="org.eclipse.jetty.util.log.Log" name="rootLogger">
diff --git a/tests/test-webapps/test-mock-resources/pom.xml b/tests/test-webapps/test-mock-resources/pom.xml
index 1689bbf..128bbc7 100644
--- a/tests/test-webapps/test-mock-resources/pom.xml
+++ b/tests/test-webapps/test-mock-resources/pom.xml
@@ -3,11 +3,14 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <name>Jetty Tests :: WebApp :: Mock Resources</name>
   <artifactId>test-mock-resources</artifactId>
   <packaging>jar</packaging>
+  <properties>
+   <bundle-symbolic-name>${project.groupId}.mocks</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
@@ -22,7 +25,6 @@
         <extensions>true</extensions>
         <executions>
           <execution>
-            <id>generate-manifest</id>
             <goals>
               <goal>manifest</goal>
             </goals>
@@ -44,23 +46,6 @@
           </execution>
         </executions>
       </plugin>
-     <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>artifact-jar</id>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-        </executions>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
     </plugins>
   </build>
   <dependencies>
@@ -73,12 +58,6 @@
       <groupId>javax.servlet</groupId>
       <artifactId>javax.servlet-api</artifactId>
     </dependency>
-<!--
-    <dependency>
-      <groupId>javax.mail</groupId>
-      <artifactId>javax.mail-api</artifactId>
-    </dependency>
--->
     <dependency>
       <groupId>org.eclipse.jetty.orbit</groupId>
       <artifactId>javax.mail.glassfish</artifactId>
diff --git a/tests/test-webapps/test-mock-resources/src/main/java/com/acme/MockTransport.java b/tests/test-webapps/test-mock-resources/src/main/java/com/acme/MockTransport.java
index 1f0cfcc..24557b4 100644
--- a/tests/test-webapps/test-mock-resources/src/main/java/com/acme/MockTransport.java
+++ b/tests/test-webapps/test-mock-resources/src/main/java/com/acme/MockTransport.java
@@ -28,12 +28,9 @@
 
 /**
  * MockTransport
- *
- *
  */
 public class MockTransport extends Transport
 {
-
     /**
      * @param session
      * @param urlname
diff --git a/tests/test-webapps/test-proxy-webapp/pom.xml b/tests/test-webapps/test-proxy-webapp/pom.xml
index 0e1db43..5a55c6d 100644
--- a/tests/test-webapps/test-proxy-webapp/pom.xml
+++ b/tests/test-webapps/test-proxy-webapp/pom.xml
@@ -1,26 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
@@ -28,6 +11,9 @@
   <artifactId>test-proxy-webapp</artifactId>
   <name>Test :: Jetty Proxy Webapp</name>
   <packaging>war</packaging>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.proxy</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
@@ -38,6 +24,14 @@
           </archive>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <!-- must deploy: required for jetty-distribution -->
+          <skip>false</skip>
+        </configuration>
+      </plugin>
     </plugins>
   </build>
   <dependencies>
@@ -83,18 +77,30 @@
       <scope>provided</scope>
     </dependency>
     <dependency>
+      <groupId>org.eclipse.jetty.http2</groupId>
+      <artifactId>http2-server</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-alpn-server</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-annotations</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>javax.servlet.jsp</groupId>
       <artifactId>jsp-api</artifactId>
       <version>2.1</version>
       <scope>provided</scope>
     </dependency>
     <dependency>
-      <groupId>org.eclipse.jetty.spdy</groupId>
-      <artifactId>spdy-http-server</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>jstl</artifactId>
       <version>1.2</version>
diff --git a/tests/test-webapps/test-proxy-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/tests/test-webapps/test-proxy-webapp/src/main/webapp/WEB-INF/jetty-web.xml
index 030fd19..e2cb8ec 100644
--- a/tests/test-webapps/test-proxy-webapp/src/main/webapp/WEB-INF/jetty-web.xml
+++ b/tests/test-webapps/test-proxy-webapp/src/main/webapp/WEB-INF/jetty-web.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
   <Set name="contextPath">/proxy</Set>
diff --git a/tests/test-webapps/test-proxy-webapp/src/test/java/org/eclipse/jetty/TestTransparentProxyServer.java b/tests/test-webapps/test-proxy-webapp/src/test/java/org/eclipse/jetty/TestTransparentProxyServer.java
index 44f366d..4a061a6 100644
--- a/tests/test-webapps/test-proxy-webapp/src/test/java/org/eclipse/jetty/TestTransparentProxyServer.java
+++ b/tests/test-webapps/test-proxy-webapp/src/test/java/org/eclipse/jetty/TestTransparentProxyServer.java
@@ -20,6 +20,9 @@
 
 import java.lang.management.ManagementFactory;
 
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
 import org.eclipse.jetty.jmx.MBeanContainer;
 import org.eclipse.jetty.server.ForwardedRequestCustomizer;
 import org.eclipse.jetty.server.Handler;
@@ -32,11 +35,6 @@
 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
 import org.eclipse.jetty.server.handler.DefaultHandler;
 import org.eclipse.jetty.server.handler.HandlerCollection;
-import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.http.PushStrategy;
-import org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.StdErrLog;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -71,7 +69,6 @@
         HttpConfiguration config = new HttpConfiguration();
         config.setSecurePort(8443);
         config.addCustomizer(new ForwardedRequestCustomizer());
-        config.addCustomizer(new SecureRequestCustomizer());
         config.setSendDateHeader(true);
         config.setSendServerVersion(true);
         
@@ -99,24 +96,27 @@
                 "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
                 "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
                 "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
+        sslContextFactory.setCipherComparator(new HTTP2Cipher.CipherComparator());
         
+
+        // HTTPS Configuration
+        HttpConfiguration https_config = new HttpConfiguration(config);
+        https_config.addCustomizer(new SecureRequestCustomizer());
         
-        // Spdy Connector
-        SPDYServerConnectionFactory.checkProtocolNegotiationAvailable();
-        PushStrategy push = new ReferrerPushStrategy();
-        HTTPSPDYServerConnectionFactory spdy2 = new HTTPSPDYServerConnectionFactory(2,config,push);
-        spdy2.setInputBufferSize(8192);
-        spdy2.setInitialWindowSize(32768);
-        HTTPSPDYServerConnectionFactory spdy3 = new HTTPSPDYServerConnectionFactory(3,config,push);
-        spdy2.setInputBufferSize(8192);
-        NPNServerConnectionFactory npn = new NPNServerConnectionFactory(spdy3.getProtocol(),spdy2.getProtocol(),http.getProtocol());
-        npn.setDefaultProtocol(http.getProtocol());
-        npn.setInputBufferSize(1024); 
-        SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory,npn.getProtocol()); 
-        ServerConnector spdyConnector = new ServerConnector(server,ssl,npn,spdy3,spdy2,http);
-        spdyConnector.setPort(8443);
-        spdyConnector.setIdleTimeout(15000);
-        server.addConnector(spdyConnector);
+        // HTTP2 factory
+        HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(https_config);
+        ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+        alpn.setDefaultProtocol(h2.getProtocol());
+        
+        // SSL Factory
+        SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory,alpn.getProtocol());
+        
+        // HTTP2 Connector
+        ServerConnector http2Connector = 
+            new ServerConnector(server,ssl,alpn,h2,new HttpConnectionFactory(https_config));
+        http2Connector.setPort(8443);
+        http2Connector.setIdleTimeout(15000);
+        server.addConnector(http2Connector);
         
         // Handlers
         HandlerCollection handlers = new HandlerCollection();
diff --git a/tests/test-webapps/test-servlet-spec/pom.xml b/tests/test-webapps/test-servlet-spec/pom.xml
index 3414110..9db79b1 100644
--- a/tests/test-webapps/test-servlet-spec/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <artifactId>test-servlet-spec-parent</artifactId>
   <name>Jetty Tests :: Spec Test WebApp :: Parent</name>
diff --git a/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml b/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
index 01fa1b3..08280ce 100644
--- a/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
@@ -3,11 +3,14 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-servlet-spec-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <artifactId>test-container-initializer</artifactId>
   <packaging>jar</packaging>
   <name>Jetty Tests :: WebApp :: Servlet Spec :: ServletContainerInitializer Test Jar</name>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.sci</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
        <plugin>
@@ -19,14 +22,6 @@
        <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
-        <executions>
-            <execution>
-                <id>artifact-jar</id>
-                <goals>
-                    <goal>jar</goal>
-                </goals>
-            </execution>
-        </executions>
         <configuration>
             <archive>
                 <manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
@@ -37,15 +32,6 @@
           <groupId>org.apache.felix</groupId>
           <artifactId>maven-bundle-plugin</artifactId>
           <extensions>true</extensions>
-          <executions>
-              <execution>
-                  <id>bundle-manifest</id>
-                  <phase>process-classes</phase>
-                  <goals>
-                      <goal>manifest</goal>
-                  </goals>
-              </execution>
-          </executions>
           <configuration>
               <instructions>
                   <Bundle-SymbolicName>org.eclipse.jetty.tests.test-servlet-container-initializer;singleton:=true</Bundle-SymbolicName>
diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml b/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
index 49edf65..f989bb2 100644
--- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
@@ -4,11 +4,14 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-servlet-spec-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <name>Jetty Tests :: Webapps :: Spec Webapp</name>
   <artifactId>test-spec-webapp</artifactId>
   <packaging>war</packaging>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.spec</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
@@ -68,37 +71,26 @@
           <supportedProjectTypes>
             <supportedProjectType>war</supportedProjectType>
           </supportedProjectTypes>
+          <instructions>
+            <Bundle-SymbolicName>org.eclipse.jetty.tests.test-spec-webapp</Bundle-SymbolicName>
+            <Bundle-Description>Test Webapp for Servlet 3.1 Features</Bundle-Description>
+            <Import-Package>
+              javax.servlet.jsp.*;version="[2.2.0, 3.0)",
+              javax.transaction*;version="[1.1,1.3)",
+              javax.servlet*;version="[2.6,3.2)",
+              org.eclipse.jetty*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",
+              org.eclipse.jetty.webapp;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:="optional",
+              org.eclipse.jetty.plus.jndi;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:="optional",
+              com.acme;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}",
+              *
+            </Import-Package>
+            <_nouses />
+            <Export-Package>com.acme.test;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}";-noimport:=true</Export-Package>
+            <Web-ContextPath>/</Web-ContextPath>
+            <Bundle-ClassPath>.,WEB-INF/classes,WEB-INF/lib</Bundle-ClassPath>
+            <Jetty-ContextFilePath>/META-INF/plugin-context.xml</Jetty-ContextFilePath>
+          </instructions>
         </configuration>
-        <executions>
-          <execution>
-            <id>bundle-manifest</id>
-            <phase>process-classes</phase>
-            <goals>
-              <goal>manifest</goal>
-            </goals>
-            <configuration>
-              <instructions>
-                <Bundle-SymbolicName>org.eclipse.jetty.tests.test-spec-webapp</Bundle-SymbolicName>
-                <Bundle-Description>Test Webapp for Servlet 3.1 Features</Bundle-Description>
-                <Import-Package>
-                  javax.servlet.jsp.*;version="[2.2.0, 3.0)",
-                  javax.transaction.*;version="[1.1, 2.0)", 
-                  javax.servlet.*;version="3.0",
-                  javax.sql,
-                  org.eclipse.jetty.webapp;version="9.2",org.eclipse.jetty.plus.jndi;version="9.2",
-                  org.eclipse.jetty.security;version="9.2",
-                  com.acme;version="9.2",
-                  *
-                </Import-Package>
-                <Export-Package>com.acme.test;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package>
-                <Web-ContextPath>/</Web-ContextPath>
-                <Bundle-ClassPath>.,WEB-INF/classes,WEB-INF/lib</Bundle-ClassPath>
-                <Jetty-ContextFilePath>./META-INF/plugin-context.xml</Jetty-ContextFilePath>
-                <_nouses>true</_nouses>
-              </instructions>
-            </configuration>
-           </execution>
-        </executions>
       </plugin>
 
 
@@ -152,7 +144,6 @@
        <!-- plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-assembly-plugin</artifactId>
-          <version>2.2-beta-3</version>
           <executions>
             <execution>
               <phase>package</phase>
diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/TestListener.java b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/TestListener.java
index d8cd95d..c5c3129 100644
--- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/TestListener.java
+++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/TestListener.java
@@ -19,7 +19,6 @@
 package com.acme.test;
 import java.util.EventListener;
 
-import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
 import javax.servlet.ServletContextAttributeEvent;
 import javax.servlet.ServletContextAttributeListener;
@@ -112,8 +111,6 @@
 
     public void contextInitialized(ServletContextEvent sce)
     {
-        System.err.println("Calling TestListener.contextInitialized");
-        
         sce.getServletContext().setAttribute("com.acme.AnnotationTest.sclInjectTest", Boolean.valueOf(maxAmount != null));
         
         //Can't add a ServletContextListener from a ServletContextListener even if it is declared in web.xml
diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/templates/annotations-context-header.xml b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/templates/annotations-context-header.xml
index 50b5981..ff2f6a1 100644
--- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/templates/annotations-context-header.xml
+++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/templates/annotations-context-header.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 
 <!-- =============================================================== -->
diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/templates/plugin-context-header.xml b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/templates/plugin-context-header.xml
index 450edda..36ff548 100644
--- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/templates/plugin-context-header.xml
+++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/templates/plugin-context-header.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 
 <!-- =============================================================== -->
diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/WEB-INF/jetty-env.xml b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/WEB-INF/jetty-env.xml
index c15926b..909ac83 100644
--- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/WEB-INF/jetty-env.xml
+++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/WEB-INF/jetty-env.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
 
diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/WEB-INF/jetty-web.xml
index 62b92f9..e360f5c 100644
--- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/WEB-INF/jetty-web.xml
+++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/WEB-INF/jetty-web.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
   <Get class="org.eclipse.jetty.util.log.Log" name="rootLogger">
diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/index.html b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/index.html
index 84f4701..8fe9f11 100644
--- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/index.html
+++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/index.html
@@ -29,13 +29,21 @@
   <button type="submit">Test</button>
 </form>
 
-<h3>DeclaresRoles</h3>
+<h3>Test Static Content from Fragment </h3>
+<p>Click the link to test accessing static content from a fragment's META-INF/resources</p>
+<a href="fragmentA/index.html">Static resource from a fragment </a>
+
+<h3>Test Servlet from Fragment </h3>
+<p>Click the link to test accessing a servlet added from a fragment's web-fragment.xml</p>
+<a href="fragment">Servlet added by web-fragment.xml</a>
+
+<h3>Test DeclaresRoles</h3>
 <p>Login as user <code>admin</code> with password <code>admin</code> when prompted after clicking the button below to test @DeclareRoles annotation</p>
 <form action="role" method="post">
   <button type="submit">Test Role Annotations</button>
 </form>
 
-<h3>ServletSecurity</h3>
+<h3>Test Servlet Security</h3>
 <p>Login as user <code>admin</code> with password <code>admin</code> when prompted after clicking the button below to test @ServletSecurity annotation</p>
 <form action="sec/foo" method="post">
   <button type="submit">Test ServletSecurity Annotation</button>
diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/test/jetty-plugin-env.xml b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/test/jetty-plugin-env.xml
index 7741843..23c4b5b 100644
--- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/test/jetty-plugin-env.xml
+++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/test/jetty-plugin-env.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
 <Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
 
diff --git a/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml b/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
index 674dce3..1fbe820 100644
--- a/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
@@ -3,12 +3,15 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-servlet-spec-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <name>Jetty Tests :: WebApp :: Servlet Spec :: Fragment Jar</name>
   <groupId>org.eclipse.jetty.tests</groupId>
   <artifactId>test-web-fragment</artifactId>
   <packaging>jar</packaging>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.fragment</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
@@ -17,46 +20,6 @@
           <verbose>false</verbose>
         </configuration>
       </plugin>
-       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <executions>
-            <execution>
-                <id>artifact-jar</id>
-                <goals>
-                    <goal>jar</goal>
-                </goals>
-            </execution>
-        </executions>
-        <configuration>
-            <archive>
-                <manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
-            </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-          <groupId>org.apache.felix</groupId>
-          <artifactId>maven-bundle-plugin</artifactId>
-          <extensions>true</extensions>
-          <executions>
-              <execution>
-                  <id>bundle-manifest</id>
-                  <phase>process-classes</phase>
-                  <goals>
-                      <goal>manifest</goal>
-                  </goals>
-              </execution>
-          </executions>
-          <configuration>
-              <instructions>
-                  <Bundle-SymbolicName>org.eclipse.jetty.tests.test-web-fragment;singleton:=true</Bundle-SymbolicName>
-                  <Bundle-Description>A bundle containing web fragment for testing</Bundle-Description>
-                  <Export-Package>com.acme.fragment;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package>
-                  <_nouses>true</_nouses>
-              </instructions>
-          </configuration>
-      </plugin>
-
     </plugins>
   </build>
   <dependencies>
@@ -64,5 +27,12 @@
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
       </dependency>
+
+<!--
+    <dependency>
+      <groupId>org.eclipse.jetty.orbit</groupId>
+      <artifactId>javax.servlet</artifactId>
+    </dependency>
+-->
   </dependencies>
 </project>
diff --git a/tests/test-webapps/test-webapp-rfc2616/pom.xml b/tests/test-webapps/test-webapp-rfc2616/pom.xml
index c54675e..3eef3b8 100644
--- a/tests/test-webapps/test-webapp-rfc2616/pom.xml
+++ b/tests/test-webapps/test-webapp-rfc2616/pom.xml
@@ -1,32 +1,18 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-// ========================================================================
-// Copyright (c) Webtide LLC
-// 
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at 
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.apache.org/licenses/LICENSE-2.0.txt
-//
-// You may elect to redistribute this code under either of these licenses. 
-// ========================================================================
- -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.2.22-SNAPSHOT</version>
+    <version>9.3.19-SNAPSHOT</version>
   </parent>
   <artifactId>test-webapp-rfc2616</artifactId>
   <name>Jetty Tests :: WebApp :: RFC2616</name>
   <url>http://www.eclipse.org/jetty</url>
   <packaging>war</packaging>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.rfc2616</bundle-symbolic-name>
+  </properties>
   <build>
     <plugins>
       <plugin>
diff --git a/tests/test-webapps/test-webapp-rfc2616/src/main/webapp/WEB-INF/web.xml b/tests/test-webapps/test-webapp-rfc2616/src/main/webapp/WEB-INF/web.xml
index 1ea878d..0685977 100644
--- a/tests/test-webapps/test-webapp-rfc2616/src/main/webapp/WEB-INF/web.xml
+++ b/tests/test-webapps/test-webapp-rfc2616/src/main/webapp/WEB-INF/web.xml
@@ -1,25 +1,20 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
   <display-name>rfc2616-webapp</display-name>
+
+  <context-param>
+    <param-name>org.eclipse.jetty.handler.GzipHandler.minGzipSize</param-name>
+    <param-value>1024</param-value>
+  </context-param>
+  <context-param>
+    <param-name>org.eclipse.jetty.handler.GzipHandler.includePaths</param-name>
+    <param-value>/*</param-value>
+  </context-param>
+  
   <welcome-file-list>
     <welcome-file>index.html</welcome-file>
   </welcome-file-list>
-  <filter>
-    <filter-name>GZIPFilter</filter-name>
-    <filter-class>org.eclipse.jetty.servlets.GzipFilter</filter-class>
-    <init-param>
-      <param-name>minGzipSize</param-name>
-      <param-value>1024</param-value>
-    </init-param>
-    <init-param>
-      <param-name>bufferSize</param-name>
-      <param-value>16384</param-value>
-    </init-param>
-  </filter>
-  <filter-mapping>
-    <filter-name>GZIPFilter</filter-name>
-    <url-pattern>/*</url-pattern>
-  </filter-mapping>
+  
   <servlet>
     <servlet-name>HttpMethodsServlet</servlet-name>
     <servlet-class>org.eclipse.jetty.tests.webapp.HttpMethodsServlet</servlet-class>
@@ -28,4 +23,4 @@
     <servlet-name>HttpMethodsServlet</servlet-name>
     <url-pattern>/httpmethods</url-pattern>
   </servlet-mapping>
-</web-app>
\ No newline at end of file
+</web-app>